// SPDX-License-Identifier: GPL-2.0-or-later /* * HID driver for Sony DualSense(TM) controller. * * Copyright (c) 2020-2022 Sony Interactive Entertainment
*/
/* Status field of DualSense input report. */ #define DS_STATUS_BATTERY_CAPACITY GENMASK(3, 0) #define DS_STATUS_CHARGING GENMASK(7, 4) #define DS_STATUS_CHARGING_SHIFT 4
/* Feature version from DualSense Firmware Info report. */ #define DS_FEATURE_VERSION(major, minor) ((major & 0xff) << 8 | (minor & 0xff))
/* * Status of a DualSense touch point contact. * Contact IDs, with highest bit set are 'inactive' * and any associated data is then invalid.
*/ #define DS_TOUCH_POINT_INACTIVE BIT(7)
/* Magic value required in tag field of Bluetooth output report. */ #define DS_OUTPUT_TAG 0x10 /* Flags for DualSense output report. */ #define DS_OUTPUT_VALID_FLAG0_COMPATIBLE_VIBRATION BIT(0) #define DS_OUTPUT_VALID_FLAG0_HAPTICS_SELECT BIT(1) #define DS_OUTPUT_VALID_FLAG1_MIC_MUTE_LED_CONTROL_ENABLE BIT(0) #define DS_OUTPUT_VALID_FLAG1_POWER_SAVE_CONTROL_ENABLE BIT(1) #define DS_OUTPUT_VALID_FLAG1_LIGHTBAR_CONTROL_ENABLE BIT(2) #define DS_OUTPUT_VALID_FLAG1_RELEASE_LEDS BIT(3) #define DS_OUTPUT_VALID_FLAG1_PLAYER_INDICATOR_CONTROL_ENABLE BIT(4) #define DS_OUTPUT_VALID_FLAG2_LIGHTBAR_SETUP_CONTROL_ENABLE BIT(1) #define DS_OUTPUT_VALID_FLAG2_COMPATIBLE_VIBRATION2 BIT(2) #define DS_OUTPUT_POWER_SAVE_CONTROL_MIC_MUTE BIT(4) #define DS_OUTPUT_LIGHTBAR_SETUP_LIGHT_OUT BIT(1)
uint8_t reserved3[12];
uint8_t status;
uint8_t reserved4[10];
} __packed; /* Common input report size shared equals the size of the USB report minus 1 byte for ReportID. */
static_assert(sizeof(struct dualsense_input_report) == DS_INPUT_REPORT_USB_SIZE - 1);
/* Common data between DualSense BT/USB main output report. */ struct dualsense_output_report_common {
uint8_t valid_flag0;
uint8_t valid_flag1;
/* * The DualSense has a main output report used to control most features. It is * largely the same between Bluetooth and USB except for different headers and CRC. * This structure hide the differences between the two to simplify sending output reports.
*/ struct dualsense_output_report {
uint8_t *data; /* Start of data */
uint8_t len; /* Size of output report */
/* Points to Bluetooth data payload in case for a Bluetooth report else NULL. */ struct dualsense_output_report_bt *bt; /* Points to USB data payload in case for a USB report else NULL. */ struct dualsense_output_report_usb *usb; /* Points to common section of report, so past any headers. */ struct dualsense_output_report_common *common;
};
/* * Status of a DualShock4 touch point contact. * Contact IDs, with highest bit set are 'inactive' * and any associated data is then invalid.
*/ #define DS4_TOUCH_POINT_INACTIVE BIT(7)
/* Status field of DualShock4 input report. */ #define DS4_STATUS0_BATTERY_CAPACITY GENMASK(3, 0) #define DS4_STATUS0_CABLE_STATE BIT(4) /* Battery status within batery_status field. */ #define DS4_BATTERY_STATUS_FULL 11 /* Status1 bit2 contains dongle connection state: * 0 = connectd * 1 = disconnected
*/ #define DS4_STATUS1_DONGLE_STATE BIT(2)
/* The lower 6 bits of hw_control of the Bluetooth main output report * control the interval at which Dualshock 4 reports data: * 0x00 - 1ms * 0x01 - 1ms * 0x02 - 2ms * 0x3E - 62ms * 0x3F - disabled
*/ #define DS4_OUTPUT_HWCTL_BT_POLL_MASK 0x3F /* Default to 4ms poll interval, which is same as USB (not adjustable). */ #define DS4_BT_DEFAULT_POLL_INTERVAL_MS 4 #define DS4_OUTPUT_HWCTL_CRC32 0x40 #define DS4_OUTPUT_HWCTL_HID 0x80
/* Calibration data for accelerometer and gyroscope. */ struct ps_calibration_data accel_calib_data[3]; struct ps_calibration_data gyro_calib_data[3];
/* Only used on dongle to track state transitions. */ enum dualshock4_dongle_state dongle_state; /* Used during calibration. */ struct work_struct dongle_hotplug_worker;
/* Timestamp for sensor data */ bool sensor_timestamp_initialized;
uint32_t prev_sensor_timestamp;
uint32_t sensor_timestamp_us;
/* Bluetooth poll interval */ bool update_bt_poll_interval;
uint8_t bt_poll_interval;
/* Lightbar leds */ bool update_lightbar; bool update_lightbar_blink; bool lightbar_enabled; /* For use by global LED control. */
uint8_t lightbar_red;
uint8_t lightbar_green;
uint8_t lightbar_blue;
uint8_t lightbar_blink_on; /* In increments of 10ms. */
uint8_t lightbar_blink_off; /* In increments of 10ms. */ struct led_classdev lightbar_leds[4];
struct dualshock4_input_report_bt {
uint8_t report_id; /* 0x11 */
uint8_t reserved[2]; struct dualshock4_input_report_common common;
uint8_t num_touch_reports; struct dualshock4_touch_report touch_reports[4]; /* BT has 4 compared to 3 for USB */
uint8_t reserved2[2];
__le32 crc32;
} __packed;
static_assert(sizeof(struct dualshock4_input_report_bt) == DS4_INPUT_REPORT_BT_SIZE);
/* Common data between Bluetooth and USB DualShock4 output reports. */ struct dualshock4_output_report_common {
uint8_t valid_flag0;
uint8_t valid_flag1;
/* * The DualShock4 has a main output report used to control most features. It is * largely the same between Bluetooth and USB except for different headers and CRC. * This structure hide the differences between the two to simplify sending output reports.
*/ struct dualshock4_output_report {
uint8_t *data; /* Start of data */
uint8_t len; /* Size of output report */
/* Points to Bluetooth data payload in case for a Bluetooth report else NULL. */ struct dualshock4_output_report_bt *bt; /* Points to USB data payload in case for a USB report else NULL. */ struct dualshock4_output_report_usb *usb; /* Points to common section of report, so past any headers. */ struct dualshock4_output_report_common *common;
};
/* * Common gamepad buttons across DualShock 3 / 4 and DualSense. * Note: for device with a touchpad, touchpad button is not included * as it will be part of the touchpad device.
*/ staticconstint ps_gamepad_buttons[] = {
BTN_WEST, /* Square */
BTN_NORTH, /* Triangle */
BTN_EAST, /* Circle */
BTN_SOUTH, /* Cross */
BTN_TL, /* L1 */
BTN_TR, /* R1 */
BTN_TL2, /* L2 */
BTN_TR2, /* R2 */
BTN_SELECT, /* Create (PS5) / Share (PS4) */
BTN_START, /* Option */
BTN_THUMBL, /* L3 */
BTN_THUMBR, /* R3 */
BTN_MODE, /* PS Home */
};
/* * Add a new ps_device to ps_devices if it doesn't exist. * Return error on duplicate device, which can happen if the same * device is connected using both Bluetooth and USB.
*/ staticint ps_devices_list_add(struct ps_device *dev)
{ struct ps_device *entry;
mutex_lock(&ps_devices_lock);
list_for_each_entry(entry, &ps_devices_list, list) { if (!memcmp(entry->mac_address, dev->mac_address, sizeof(dev->mac_address))) {
hid_err(dev->hdev, "Duplicate device found for MAC address %pMR.\n",
dev->mac_address);
mutex_unlock(&ps_devices_lock); return -EEXIST;
}
}
ret = devm_led_classdev_register(&ps_dev->hdev->dev, led); if (ret) {
hid_err(ps_dev->hdev, "Failed to register LED %s: %d\n", led_info->name, ret); return ret;
}
return 0;
}
/* Register a DualSense/DualShock4 RGB lightbar represented by a multicolor LED. */ staticint ps_lightbar_register(struct ps_device *ps_dev, struct led_classdev_mc *lightbar_mc_dev, int (*brightness_set)(struct led_classdev *, enum led_brightness))
{ struct hid_device *hdev = ps_dev->hdev; struct mc_subled *mc_led_info; struct led_classdev *led_cdev; int ret;
ret = devm_led_classdev_multicolor_register(&hdev->dev, lightbar_mc_dev); if (ret < 0) {
hid_err(hdev, "Cannot register multicolor LED device\n"); return ret;
}
return 0;
}
staticstruct input_dev *ps_sensors_create(struct hid_device *hdev, int accel_range, intaccel_res, int gyro_range, int gyro_res)
{ struct input_dev *sensors; int ret;
sensors = ps_allocate_input_dev(hdev, "Motion Sensors"); if (IS_ERR(sensors)) return ERR_CAST(sensors);
staticint dualsense_get_calibration_data(struct dualsense *ds)
{ struct hid_device *hdev = ds->base.hdev; short gyro_pitch_bias, gyro_pitch_plus, gyro_pitch_minus; short gyro_yaw_bias, gyro_yaw_plus, gyro_yaw_minus; short gyro_roll_bias, gyro_roll_plus, gyro_roll_minus; short gyro_speed_plus, gyro_speed_minus; short acc_x_plus, acc_x_minus; short acc_y_plus, acc_y_minus; short acc_z_plus, acc_z_minus; int speed_2x; int range_2g; int ret = 0; int i;
uint8_t *buf;
buf = kzalloc(DS_FEATURE_REPORT_CALIBRATION_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM;
ret = ps_get_report(ds->base.hdev, DS_FEATURE_REPORT_CALIBRATION, buf,
DS_FEATURE_REPORT_CALIBRATION_SIZE, true); if (ret) {
hid_err(ds->base.hdev, "Failed to retrieve DualSense calibration info: %d\n", ret); goto err_free;
}
/* * Sanity check gyro calibration data. This is needed to prevent crashes * during report handling of virtual, clone or broken devices not implementing * calibration data properly.
*/ for (i = 0; i < ARRAY_SIZE(ds->gyro_calib_data); i++) { if (ds->gyro_calib_data[i].sens_denom == 0) {
hid_warn(hdev, "Invalid gyro calibration data for axis (%d), disabling calibration.",
ds->gyro_calib_data[i].abs_code);
ds->gyro_calib_data[i].bias = 0;
ds->gyro_calib_data[i].sens_numer = DS_GYRO_RANGE;
ds->gyro_calib_data[i].sens_denom = S16_MAX;
}
}
/* * Set accelerometer calibration and normalization parameters. * Data values will be normalized to 1/DS_ACC_RES_PER_G g.
*/
range_2g = acc_x_plus - acc_x_minus;
ds->accel_calib_data[0].abs_code = ABS_X;
ds->accel_calib_data[0].bias = acc_x_plus - range_2g / 2;
ds->accel_calib_data[0].sens_numer = 2*DS_ACC_RES_PER_G;
ds->accel_calib_data[0].sens_denom = range_2g;
/* * Sanity check accelerometer calibration data. This is needed to prevent crashes * during report handling of virtual, clone or broken devices not implementing calibration * data properly.
*/ for (i = 0; i < ARRAY_SIZE(ds->accel_calib_data); i++) { if (ds->accel_calib_data[i].sens_denom == 0) {
hid_warn(hdev, "Invalid accelerometer calibration data for axis (%d), disabling calibration.",
ds->accel_calib_data[i].abs_code);
ds->accel_calib_data[i].bias = 0;
ds->accel_calib_data[i].sens_numer = DS_ACC_RANGE;
ds->accel_calib_data[i].sens_denom = S16_MAX;
}
}
err_free:
kfree(buf); return ret;
}
staticint dualsense_get_firmware_info(struct dualsense *ds)
{
uint8_t *buf; int ret;
buf = kzalloc(DS_FEATURE_REPORT_FIRMWARE_INFO_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM;
ret = ps_get_report(ds->base.hdev, DS_FEATURE_REPORT_FIRMWARE_INFO, buf,
DS_FEATURE_REPORT_FIRMWARE_INFO_SIZE, true); if (ret) {
hid_err(ds->base.hdev, "Failed to retrieve DualSense firmware info: %d\n", ret); goto err_free;
}
/* Update version is some kind of feature version. It is distinct from * the firmware version as there can be many different variations of a * controller over time with the same physical shell, but with different * PCBs and other internal changes. The update version (internal name) is * used as a means to detect what features are available and change behavior. * Note: the version is different between DualSense and DualSense Edge.
*/
ds->update_version = get_unaligned_le16(&buf[44]);
err_free:
kfree(buf); return ret;
}
staticint dualsense_get_mac_address(struct dualsense *ds)
{
uint8_t *buf; int ret = 0;
buf = kzalloc(DS_FEATURE_REPORT_PAIRING_INFO_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM;
ret = ps_get_report(ds->base.hdev, DS_FEATURE_REPORT_PAIRING_INFO, buf,
DS_FEATURE_REPORT_PAIRING_INFO_SIZE, true); if (ret) {
hid_err(ds->base.hdev, "Failed to retrieve DualSense pairing info: %d\n", ret); goto err_free;
}
led_mc_calc_color_components(mc_cdev, brightness);
red = mc_cdev->subled_info[0].brightness;
green = mc_cdev->subled_info[1].brightness;
blue = mc_cdev->subled_info[2].brightness;
if (hdev->bus == BUS_BLUETOOTH) { struct dualsense_output_report_bt *bt = buf;
memset(bt, 0, sizeof(*bt));
bt->report_id = DS_OUTPUT_REPORT_BT;
bt->tag = DS_OUTPUT_TAG; /* Tag must be set. Exact meaning is unclear. */
/* * Highest 4-bit is a sequence number, which needs to be increased * every report. Lowest 4-bit is tag and can be zero for now.
*/
bt->seq_tag = (ds->output_seq << 4) | 0x0; if (++ds->output_seq == 16)
ds->output_seq = 0;
spin_lock_irqsave(&ds->base.lock, flags); if (ds->output_worker_initialized)
schedule_work(&ds->output_worker);
spin_unlock_irqrestore(&ds->base.lock, flags);
}
/* * Helper function to send DualSense output reports. Applies a CRC at the end of a report * for Bluetooth reports.
*/ staticvoid dualsense_send_output_report(struct dualsense *ds, struct dualsense_output_report *report)
{ struct hid_device *hdev = ds->base.hdev;
/* Bluetooth packets need to be signed with a CRC in the last 4 bytes. */ if (report->bt) {
uint32_t crc;
uint8_t seed = PS_OUTPUT_CRC32_SEED;
/* * The DualSense has an internal microphone, which can be muted through a mute button * on the device. The driver is expected to read the button state and program the device * to mute/unmute audio at the hardware level.
*/
btn_mic_state = !!(ds_report->buttons[2] & DS_BUTTONS2_MIC_MUTE); if (btn_mic_state && !ds->last_btn_mic_state) {
spin_lock_irqsave(&ps_dev->lock, flags);
ds->update_mic_mute = true;
ds->mic_muted = !ds->mic_muted; /* toggle */
spin_unlock_irqrestore(&ps_dev->lock, flags);
/* Schedule updating of microphone state at hardware level. */
dualsense_schedule_work(ds);
}
ds->last_btn_mic_state = btn_mic_state;
/* Parse and calibrate gyroscope data. */ for (i = 0; i < ARRAY_SIZE(ds_report->gyro); i++) { int raw_data = (short)le16_to_cpu(ds_report->gyro[i]); int calib_data = mult_frac(ds->gyro_calib_data[i].sens_numer,
raw_data, ds->gyro_calib_data[i].sens_denom);
/* Parse and calibrate accelerometer data. */ for (i = 0; i < ARRAY_SIZE(ds_report->accel); i++) { int raw_data = (short)le16_to_cpu(ds_report->accel[i]); int calib_data = mult_frac(ds->accel_calib_data[i].sens_numer,
raw_data - ds->accel_calib_data[i].bias,
ds->accel_calib_data[i].sens_denom);
buf = kzalloc(sizeof(struct dualsense_output_report_bt), GFP_KERNEL); if (!buf) return -ENOMEM;
dualsense_init_output_report(ds, &report, buf); /* * On Bluetooth the DualSense outputs an animation on the lightbar * during startup and maintains a color afterwards. We need to explicitly * reconfigure the lightbar before we can do any programming later on. * In USB the lightbar is not on by default, but redoing the setup there * doesn't hurt.
*/
report.common->valid_flag2 = DS_OUTPUT_VALID_FLAG2_LIGHTBAR_SETUP_CONTROL_ENABLE;
report.common->lightbar_setup = DS_OUTPUT_LIGHTBAR_SETUP_LIGHT_OUT; /* Fade light out. */
dualsense_send_output_report(ds, &report);
staticvoid dualsense_set_player_leds(struct dualsense *ds)
{ /* * The DualSense controller has a row of 5 LEDs used for player ids. * Behavior on the PlayStation 5 console is to center the player id * across the LEDs, so e.g. player 1 would be "--x--" with x being 'on'. * Follow a similar mapping here.
*/ staticconstint player_ids[5] = {
BIT(2),
BIT(3) | BIT(1),
BIT(4) | BIT(2) | BIT(0),
BIT(4) | BIT(3) | BIT(1) | BIT(0),
BIT(4) | BIT(3) | BIT(2) | BIT(1) | BIT(0)
};
ds = devm_kzalloc(&hdev->dev, sizeof(*ds), GFP_KERNEL); if (!ds) return ERR_PTR(-ENOMEM);
/* * Patch version to allow userspace to distinguish between * hid-generic vs hid-playstation axis and button mapping.
*/
hdev->version |= HID_PLAYSTATION_VERSION_PATCH;
ret = dualsense_get_mac_address(ds); if (ret) {
hid_err(hdev, "Failed to get MAC address from DualSense\n"); return ERR_PTR(ret);
}
snprintf(hdev->uniq, sizeof(hdev->uniq), "%pMR", ds->base.mac_address);
ret = dualsense_get_firmware_info(ds); if (ret) {
hid_err(hdev, "Failed to get firmware info from DualSense\n"); return ERR_PTR(ret);
}
/* Original DualSense firmware simulated classic controller rumble through * its new haptics hardware. It felt different from classic rumble users * were used to. Since then new firmwares were introduced to change behavior * and make this new 'v2' behavior default on PlayStation and other platforms. * The original DualSense requires a new enough firmware as bundled with PS5 * software released in 2021. DualSense edge supports it out of the box. * Both devices also support the old mode, but it is not really used.
*/ if (hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER) { /* Feature version 2.21 introduced new vibration method. */
ds->use_vibration_v2 = ds->update_version >= DS_FEATURE_VERSION(2, 21);
} elseif (hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER_2) {
ds->use_vibration_v2 = true;
}
ret = ps_devices_list_add(ps_dev); if (ret) return ERR_PTR(ret);
ret = dualsense_get_calibration_data(ds); if (ret) {
hid_err(hdev, "Failed to get calibration data from DualSense\n"); goto err;
}
ds->gamepad = ps_gamepad_create(hdev, dualsense_play_effect); if (IS_ERR(ds->gamepad)) {
ret = PTR_ERR(ds->gamepad); goto err;
} /* Use gamepad input device name as primary device name for e.g. LEDs */
ps_dev->input_dev_name = dev_name(&ds->gamepad->dev);
ds->sensors = ps_sensors_create(hdev, DS_ACC_RANGE, DS_ACC_RES_PER_G,
DS_GYRO_RANGE, DS_GYRO_RES_PER_DEG_S); if (IS_ERR(ds->sensors)) {
ret = PTR_ERR(ds->sensors); goto err;
}
ds->touchpad = ps_touchpad_create(hdev, DS_TOUCHPAD_WIDTH, DS_TOUCHPAD_HEIGHT, 2); if (IS_ERR(ds->touchpad)) {
ret = PTR_ERR(ds->touchpad); goto err;
}
ret = ps_device_register_battery(ps_dev); if (ret) goto err;
/* * The hardware may have control over the LEDs (e.g. in Bluetooth on startup). * Reset the LEDs (lightbar, mute, player leds), so we can control them * from software.
*/
ret = dualsense_reset_leds(ds); if (ret) goto err;
ret = ps_lightbar_register(ps_dev, &ds->lightbar, dualsense_lightbar_set_brightness); if (ret) goto err;
/* Set default lightbar color. */
dualsense_set_lightbar(ds, 0, 0, 128); /* blue */
for (i = 0; i < ARRAY_SIZE(player_leds_info); i++) { conststruct ps_led_info *led_info = &player_leds_info[i];
ret = ps_led_register(ps_dev, &ds->player_leds[i], led_info); if (ret < 0) goto err;
}
ret = ps_device_set_player_id(ps_dev); if (ret) {
hid_err(hdev, "Failed to assign player id for DualSense: %d\n", ret); goto err;
}
/* Set player LEDs to our player id. */
dualsense_set_player_leds(ds);
/* * Reporting hardware and firmware is important as there are frequent updates, which * can change behavior.
*/
hid_info(hdev, "Registered DualSense controller hw_version=0x%08x fw_version=0x%08x\n",
ds->base.hw_version, ds->base.fw_version);
ret = dualshock4_get_calibration_data(ds4); if (ret < 0) { /* This call is very unlikely to fail for the dongle. When it * fails we are probably in a very bad state, so mark the * dongle as disabled. We will re-enable the dongle if a new * DS4 hotplug is detect from sony_raw_event as any issues * are likely resolved then (the dongle is quite stupid).
*/
hid_err(ds4->base.hdev, "DualShock 4 USB dongle: calibration failed, disabling device\n");
dongle_state = DONGLE_DISABLED;
} else {
hid_info(ds4->base.hdev, "DualShock 4 USB dongle: calibration completed\n");
dongle_state = DONGLE_CONNECTED;
}
staticint dualshock4_get_calibration_data(struct dualshock4 *ds4)
{ struct hid_device *hdev = ds4->base.hdev; short gyro_pitch_bias, gyro_pitch_plus, gyro_pitch_minus; short gyro_yaw_bias, gyro_yaw_plus, gyro_yaw_minus; short gyro_roll_bias, gyro_roll_plus, gyro_roll_minus; short gyro_speed_plus, gyro_speed_minus; short acc_x_plus, acc_x_minus; short acc_y_plus, acc_y_minus; short acc_z_plus, acc_z_minus; int speed_2x; int range_2g; int ret = 0; int i;
uint8_t *buf;
if (ds4->base.hdev->bus == BUS_USB) { int retries;
buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_SIZE, GFP_KERNEL); if (!buf) {
ret = -ENOMEM; goto transfer_failed;
}
/* We should normally receive the feature report data we asked * for, but hidraw applications such as Steam can issue feature * reports as well. In particular for Dongle reconnects, Steam * and this function are competing resulting in often receiving * data for a different HID report, so retry a few times.
*/ for (retries = 0; retries < 3; retries++) {
ret = ps_get_report(hdev, DS4_FEATURE_REPORT_CALIBRATION, buf,
DS4_FEATURE_REPORT_CALIBRATION_SIZE, true); if (ret) { if (retries < 2) {
hid_warn(hdev, "Retrying DualShock 4 get calibration report (0x02) request\n"); continue;
}
hid_warn(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret);
ret = -EILSEQ;
kfree(buf); goto transfer_failed;
} else { break;
}
}
} else { /* Bluetooth */
buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE, GFP_KERNEL); if (!buf) {
ret = -ENOMEM; goto transfer_failed;
}
ret = ps_get_report(hdev, DS4_FEATURE_REPORT_CALIBRATION_BT, buf,
DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE, true);
if (ret) {
hid_warn(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret);
kfree(buf); goto transfer_failed;
}
}
transfer_failed: /* * Sanity check gyro calibration data. This is needed to prevent crashes * during report handling of virtual, clone or broken devices not implementing * calibration data properly.
*/ for (i = 0; i < ARRAY_SIZE(ds4->gyro_calib_data); i++) { if (ds4->gyro_calib_data[i].sens_denom == 0) {
ds4->gyro_calib_data[i].abs_code = ABS_RX + i;
hid_warn(hdev, "Invalid gyro calibration data for axis (%d), disabling calibration.",
ds4->gyro_calib_data[i].abs_code);
ds4->gyro_calib_data[i].bias = 0;
ds4->gyro_calib_data[i].sens_numer = DS4_GYRO_RANGE;
ds4->gyro_calib_data[i].sens_denom = S16_MAX;
}
}
/* * Sanity check accelerometer calibration data. This is needed to prevent crashes * during report handling of virtual, clone or broken devices not implementing calibration * data properly.
*/ for (i = 0; i < ARRAY_SIZE(ds4->accel_calib_data); i++) { if (ds4->accel_calib_data[i].sens_denom == 0) {
ds4->accel_calib_data[i].abs_code = ABS_X + i;
hid_warn(hdev, "Invalid accelerometer calibration data for axis (%d), disabling calibration.",
ds4->accel_calib_data[i].abs_code);
ds4->accel_calib_data[i].bias = 0;
ds4->accel_calib_data[i].sens_numer = DS4_ACC_RANGE;
ds4->accel_calib_data[i].sens_denom = S16_MAX;
}
}
return ret;
}
staticint dualshock4_get_firmware_info(struct dualshock4 *ds4)
{
uint8_t *buf; int ret;
buf = kzalloc(DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM;
/* Note USB and BT support the same feature report, but this report * lacks CRC support, so must be disabled in ps_get_report.
*/
ret = ps_get_report(ds4->base.hdev, DS4_FEATURE_REPORT_FIRMWARE_INFO, buf,
DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE, false); if (ret) {
hid_err(ds4->base.hdev, "Failed to retrieve DualShock4 firmware info: %d\n", ret); goto err_free;
}
staticint dualshock4_get_mac_address(struct dualshock4 *ds4)
{ struct hid_device *hdev = ds4->base.hdev;
uint8_t *buf; int ret = 0;
if (hdev->bus == BUS_USB) {
buf = kzalloc(DS4_FEATURE_REPORT_PAIRING_INFO_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM;
ret = ps_get_report(hdev, DS4_FEATURE_REPORT_PAIRING_INFO, buf,
DS4_FEATURE_REPORT_PAIRING_INFO_SIZE, false); if (ret) {
hid_err(hdev, "Failed to retrieve DualShock4 pairing info: %d\n", ret); goto err_free;
}
memcpy(ds4->base.mac_address, &buf[1], sizeof(ds4->base.mac_address));
} else { /* Rely on HIDP for Bluetooth */ if (strlen(hdev->uniq) != 17) return -EINVAL;
ret = sscanf(hdev->uniq, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
&ds4->base.mac_address[5], &ds4->base.mac_address[4],
&ds4->base.mac_address[3], &ds4->base.mac_address[2],
&ds4->base.mac_address[1], &ds4->base.mac_address[0]);
if (ret != sizeof(ds4->base.mac_address)) return -EINVAL;
led_index = led - ds4->lightbar_leds; switch (led_index) { case 0: return ds4->lightbar_red; case 1: return ds4->lightbar_green; case 2: return ds4->lightbar_blue; case 3: return ds4->lightbar_enabled;
}
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.