linux/drivers/hid/hid-playstation.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  HID driver for Sony DualSense(TM) controller.
 *
 *  Copyright (c) 2020-2022 Sony Interactive Entertainment
 */

#include <linux/bits.h>
#include <linux/crc32.h>
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/idr.h>
#include <linux/input/mt.h>
#include <linux/leds.h>
#include <linux/led-class-multicolor.h>
#include <linux/module.h>

#include <asm/unaligned.h>

#include "hid-ids.h"

/* List of connected playstation devices. */
static DEFINE_MUTEX(ps_devices_lock);
static LIST_HEAD(ps_devices_list);

static DEFINE_IDA(ps_player_id_allocator);

#define HID_PLAYSTATION_VERSION_PATCH

enum PS_TYPE {};

/* Base class for playstation devices. */
struct ps_device {};

/* Calibration data for playstation motion sensors. */
struct ps_calibration_data {};

struct ps_led_info {};

/* Seed values for DualShock4 / DualSense CRC32 for different report types. */
#define PS_INPUT_CRC32_SEED
#define PS_OUTPUT_CRC32_SEED
#define PS_FEATURE_CRC32_SEED

#define DS_INPUT_REPORT_USB
#define DS_INPUT_REPORT_USB_SIZE
#define DS_INPUT_REPORT_BT
#define DS_INPUT_REPORT_BT_SIZE
#define DS_OUTPUT_REPORT_USB
#define DS_OUTPUT_REPORT_USB_SIZE
#define DS_OUTPUT_REPORT_BT
#define DS_OUTPUT_REPORT_BT_SIZE

#define DS_FEATURE_REPORT_CALIBRATION
#define DS_FEATURE_REPORT_CALIBRATION_SIZE
#define DS_FEATURE_REPORT_PAIRING_INFO
#define DS_FEATURE_REPORT_PAIRING_INFO_SIZE
#define DS_FEATURE_REPORT_FIRMWARE_INFO
#define DS_FEATURE_REPORT_FIRMWARE_INFO_SIZE

/* Button masks for DualSense input report. */
#define DS_BUTTONS0_HAT_SWITCH
#define DS_BUTTONS0_SQUARE
#define DS_BUTTONS0_CROSS
#define DS_BUTTONS0_CIRCLE
#define DS_BUTTONS0_TRIANGLE
#define DS_BUTTONS1_L1
#define DS_BUTTONS1_R1
#define DS_BUTTONS1_L2
#define DS_BUTTONS1_R2
#define DS_BUTTONS1_CREATE
#define DS_BUTTONS1_OPTIONS
#define DS_BUTTONS1_L3
#define DS_BUTTONS1_R3
#define DS_BUTTONS2_PS_HOME
#define DS_BUTTONS2_TOUCHPAD
#define DS_BUTTONS2_MIC_MUTE

/* Status field of DualSense input report. */
#define DS_STATUS_BATTERY_CAPACITY
#define DS_STATUS_CHARGING
#define DS_STATUS_CHARGING_SHIFT

/* Feature version from DualSense Firmware Info report. */
#define DS_FEATURE_VERSION(major, minor)

/*
 * 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

 /* Magic value required in tag field of Bluetooth output report. */
#define DS_OUTPUT_TAG
/* Flags for DualSense output report. */
#define DS_OUTPUT_VALID_FLAG0_COMPATIBLE_VIBRATION
#define DS_OUTPUT_VALID_FLAG0_HAPTICS_SELECT
#define DS_OUTPUT_VALID_FLAG1_MIC_MUTE_LED_CONTROL_ENABLE
#define DS_OUTPUT_VALID_FLAG1_POWER_SAVE_CONTROL_ENABLE
#define DS_OUTPUT_VALID_FLAG1_LIGHTBAR_CONTROL_ENABLE
#define DS_OUTPUT_VALID_FLAG1_RELEASE_LEDS
#define DS_OUTPUT_VALID_FLAG1_PLAYER_INDICATOR_CONTROL_ENABLE
#define DS_OUTPUT_VALID_FLAG2_LIGHTBAR_SETUP_CONTROL_ENABLE
#define DS_OUTPUT_VALID_FLAG2_COMPATIBLE_VIBRATION2
#define DS_OUTPUT_POWER_SAVE_CONTROL_MIC_MUTE
#define DS_OUTPUT_LIGHTBAR_SETUP_LIGHT_OUT

/* DualSense hardware limits */
#define DS_ACC_RES_PER_G
#define DS_ACC_RANGE
#define DS_GYRO_RES_PER_DEG_S
#define DS_GYRO_RANGE
#define DS_TOUCHPAD_WIDTH
#define DS_TOUCHPAD_HEIGHT

struct dualsense {};

struct dualsense_touch_point {} __packed;
static_assert();

/* Main DualSense input report excluding any BT/USB specific headers. */
struct dualsense_input_report {} __packed;
/* Common input report size shared equals the size of the USB report minus 1 byte for ReportID. */
static_assert();

/* Common data between DualSense BT/USB main output report. */
struct dualsense_output_report_common {} __packed;
static_assert();

struct dualsense_output_report_bt {} __packed;
static_assert();

struct dualsense_output_report_usb {} __packed;
static_assert();

/*
 * 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 {};

#define DS4_INPUT_REPORT_USB
#define DS4_INPUT_REPORT_USB_SIZE
#define DS4_INPUT_REPORT_BT_MINIMAL
#define DS4_INPUT_REPORT_BT_MINIMAL_SIZE
#define DS4_INPUT_REPORT_BT
#define DS4_INPUT_REPORT_BT_SIZE
#define DS4_OUTPUT_REPORT_USB
#define DS4_OUTPUT_REPORT_USB_SIZE
#define DS4_OUTPUT_REPORT_BT
#define DS4_OUTPUT_REPORT_BT_SIZE

#define DS4_FEATURE_REPORT_CALIBRATION
#define DS4_FEATURE_REPORT_CALIBRATION_SIZE
#define DS4_FEATURE_REPORT_CALIBRATION_BT
#define DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE
#define DS4_FEATURE_REPORT_FIRMWARE_INFO
#define DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE
#define DS4_FEATURE_REPORT_PAIRING_INFO
#define DS4_FEATURE_REPORT_PAIRING_INFO_SIZE

/*
 * 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

/* Status field of DualShock4 input report. */
#define DS4_STATUS0_BATTERY_CAPACITY
#define DS4_STATUS0_CABLE_STATE
/* Battery status within batery_status field. */
#define DS4_BATTERY_STATUS_FULL
/* Status1 bit2 contains dongle connection state:
 * 0 = connectd
 * 1 = disconnected
 */
#define DS4_STATUS1_DONGLE_STATE

/* 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
/* Default to 4ms poll interval, which is same as USB (not adjustable). */
#define DS4_BT_DEFAULT_POLL_INTERVAL_MS
#define DS4_OUTPUT_HWCTL_CRC32
#define DS4_OUTPUT_HWCTL_HID

/* Flags for DualShock4 output report. */
#define DS4_OUTPUT_VALID_FLAG0_MOTOR
#define DS4_OUTPUT_VALID_FLAG0_LED
#define DS4_OUTPUT_VALID_FLAG0_LED_BLINK

/* DualShock4 hardware limits */
#define DS4_ACC_RES_PER_G
#define DS4_ACC_RANGE
#define DS4_GYRO_RES_PER_DEG_S
#define DS4_GYRO_RANGE
#define DS4_LIGHTBAR_MAX_BLINK
#define DS4_TOUCHPAD_WIDTH
#define DS4_TOUCHPAD_HEIGHT

enum dualshock4_dongle_state {};

struct dualshock4 {};

struct dualshock4_touch_point {} __packed;
static_assert();

struct dualshock4_touch_report {} __packed;
static_assert();

/* Main DualShock4 input report excluding any BT/USB specific headers. */
struct dualshock4_input_report_common {} __packed;
static_assert();

struct dualshock4_input_report_usb {} __packed;
static_assert();

struct dualshock4_input_report_bt {} __packed;
static_assert();

/* Common data between Bluetooth and USB DualShock4 output reports. */
struct dualshock4_output_report_common {} __packed;

struct dualshock4_output_report_usb {} __packed;
static_assert();

struct dualshock4_output_report_bt {} __packed;
static_assert();

/*
 * 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 {};

/*
 * 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.
 */
static const int ps_gamepad_buttons[] =;

static const struct {} ps_gamepad_hat_mapping[] =;

static int dualshock4_get_calibration_data(struct dualshock4 *ds4);
static inline void dualsense_schedule_work(struct dualsense *ds);
static inline void dualshock4_schedule_work(struct dualshock4 *ds4);
static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue);
static void dualshock4_set_default_lightbar_colors(struct dualshock4 *ds4);

/*
 * 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.
 */
static int ps_devices_list_add(struct ps_device *dev)
{}

static int ps_devices_list_remove(struct ps_device *dev)
{}

static int ps_device_set_player_id(struct ps_device *dev)
{}

static void ps_device_release_player_id(struct ps_device *dev)
{}

static struct input_dev *ps_allocate_input_dev(struct hid_device *hdev, const char *name_suffix)
{}

static enum power_supply_property ps_power_supply_props[] =;

static int ps_battery_get_property(struct power_supply *psy,
		enum power_supply_property psp,
		union power_supply_propval *val)
{}

static int ps_device_register_battery(struct ps_device *dev)
{}

/* Compute crc32 of HID data and compare against expected CRC. */
static bool ps_check_crc32(uint8_t seed, uint8_t *data, size_t len, uint32_t report_crc)
{}

static struct input_dev *ps_gamepad_create(struct hid_device *hdev,
		int (*play_effect)(struct input_dev *, void *, struct ff_effect *))
{}

static int ps_get_report(struct hid_device *hdev, uint8_t report_id, uint8_t *buf, size_t size,
		bool check_crc)
{}

static int ps_led_register(struct ps_device *ps_dev, struct led_classdev *led,
		const struct ps_led_info *led_info)
{}

/* Register a DualSense/DualShock4 RGB lightbar represented by a multicolor LED. */
static int ps_lightbar_register(struct ps_device *ps_dev, struct led_classdev_mc *lightbar_mc_dev,
	int (*brightness_set)(struct led_classdev *, enum led_brightness))
{}

static struct input_dev *ps_sensors_create(struct hid_device *hdev, int accel_range, int accel_res,
		int gyro_range, int gyro_res)
{}

static struct input_dev *ps_touchpad_create(struct hid_device *hdev, int width, int height,
		unsigned int num_contacts)
{}

static ssize_t firmware_version_show(struct device *dev,
				struct device_attribute
				*attr, char *buf)
{}

static DEVICE_ATTR_RO(firmware_version);

static ssize_t hardware_version_show(struct device *dev,
				struct device_attribute
				*attr, char *buf)
{}

static DEVICE_ATTR_RO(hardware_version);

static struct attribute *ps_device_attrs[] =;
ATTRIBUTE_GROUPS();

static int dualsense_get_calibration_data(struct dualsense *ds)
{}


static int dualsense_get_firmware_info(struct dualsense *ds)
{}

static int dualsense_get_mac_address(struct dualsense *ds)
{}

static int dualsense_lightbar_set_brightness(struct led_classdev *cdev,
	enum led_brightness brightness)
{}

static enum led_brightness dualsense_player_led_get_brightness(struct led_classdev *led)
{}

static int dualsense_player_led_set_brightness(struct led_classdev *led, enum led_brightness value)
{}

static void dualsense_init_output_report(struct dualsense *ds, struct dualsense_output_report *rp,
		void *buf)
{}

static inline void dualsense_schedule_work(struct dualsense *ds)
{}

/*
 * Helper function to send DualSense output reports. Applies a CRC at the end of a report
 * for Bluetooth reports.
 */
static void dualsense_send_output_report(struct dualsense *ds,
		struct dualsense_output_report *report)
{}

static void dualsense_output_worker(struct work_struct *work)
{}

static int dualsense_parse_report(struct ps_device *ps_dev, struct hid_report *report,
		u8 *data, int size)
{}

static int dualsense_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect)
{}

static void dualsense_remove(struct ps_device *ps_dev)
{}

static int dualsense_reset_leds(struct dualsense *ds)
{}

static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue)
{}

static void dualsense_set_player_leds(struct dualsense *ds)
{}

static struct ps_device *dualsense_create(struct hid_device *hdev)
{}

static void dualshock4_dongle_calibration_work(struct work_struct *work)
{}

static int dualshock4_get_calibration_data(struct dualshock4 *ds4)
{}

static int dualshock4_get_firmware_info(struct dualshock4 *ds4)
{}

static int dualshock4_get_mac_address(struct dualshock4 *ds4)
{}

static enum led_brightness dualshock4_led_get_brightness(struct led_classdev *led)
{}

static int dualshock4_led_set_blink(struct led_classdev *led, unsigned long *delay_on,
		unsigned long *delay_off)
{}

static int dualshock4_led_set_brightness(struct led_classdev *led, enum led_brightness value)
{}

static void dualshock4_init_output_report(struct dualshock4 *ds4,
		struct dualshock4_output_report *rp, void *buf)
{}

static void dualshock4_output_worker(struct work_struct *work)
{}

static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report *report,
		u8 *data, int size)
{}

static int dualshock4_dongle_parse_report(struct ps_device *ps_dev, struct hid_report *report,
		u8 *data, int size)
{}

static int dualshock4_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect)
{}

static void dualshock4_remove(struct ps_device *ps_dev)
{}

static inline void dualshock4_schedule_work(struct dualshock4 *ds4)
{}

static void dualshock4_set_bt_poll_interval(struct dualshock4 *ds4, uint8_t interval)
{}

/* Set default lightbar color based on player. */
static void dualshock4_set_default_lightbar_colors(struct dualshock4 *ds4)
{}

static struct ps_device *dualshock4_create(struct hid_device *hdev)
{}

static int ps_raw_event(struct hid_device *hdev, struct hid_report *report,
		u8 *data, int size)
{}

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

static void ps_remove(struct hid_device *hdev)
{}

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

static struct hid_driver ps_driver =;

static int __init ps_init(void)
{}

static void __exit ps_exit(void)
{}

module_init();
module_exit(ps_exit);

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