From f0e3e17c9422d99da539a6afae5324a821d75aa3 Mon Sep 17 00:00:00 2001 From: Roderick Colenbrander Date: Tue, 13 Jul 2021 23:02:34 +0530 Subject: [PATCH] BACKPORT: HID: playstation: Report DualSense hardware and firmware version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Retrieve DualSense hardware and firmware information using a vendor specific feature report. Report the data through the sysfs and also report using hid_info as there can be signficant differences between versions. Bug: 167947264 CRs-fixed: 2971837 Change-Id: I9a8e338d1a9726510194a4d41911dda8bb6371fc Signed-off-by: Roderick Colenbrander Reviewed-by: Barnabás Pőcze Signed-off-by: Benjamin Tissoires Signed-off-by: Farid Chahla Signed-off-by: Siarhei Vishniakou Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git Git-commit: 0b25b55d34f554b43a679e7e1303beb973b63e27 Signed-off-by: Kishor Krishna Bhat --- drivers/hid/hid-playstation.c | 82 +++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 3566efae5023..1e53015aa977 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -32,6 +32,8 @@ struct ps_device { uint8_t battery_capacity; int battery_status; uint8_t mac_address[6]; /* Note: stored in little endian order. */ + int32_t hw_version; + int32_t fw_version; int (*parse_report)(struct ps_device *dev, struct hid_report *report, u8 *data, int size); }; @@ -63,6 +65,9 @@ struct ps_calibration_data { #define DS_FEATURE_REPORT_PAIRING_INFO 0x09 #define DS_FEATURE_REPORT_PAIRING_INFO_SIZE 20 +#define DS_FEATURE_REPORT_FIRMWARE_INFO 0x20 +#define DS_FEATURE_REPORT_FIRMWARE_INFO_SIZE 64 + /* Button masks for DualSense input report. */ #define DS_BUTTONS0_HAT_SWITCH GENMASK(3, 0) #define DS_BUTTONS0_SQUARE BIT(4) @@ -533,6 +538,40 @@ static struct input_dev *ps_touchpad_create(struct hid_device *hdev, int width, return touchpad; } +static ssize_t firmware_version_show(struct device *dev, + struct device_attribute + *attr, char *buf) +{ + struct hid_device *hdev = to_hid_device(dev); + struct ps_device *ps_dev = hid_get_drvdata(hdev); + + return scnprintf(buf, PAGE_SIZE, "0x%08x\n", ps_dev->fw_version); +} + +static DEVICE_ATTR_RO(firmware_version); + +static ssize_t hardware_version_show(struct device *dev, + struct device_attribute + *attr, char *buf) +{ + struct hid_device *hdev = to_hid_device(dev); + struct ps_device *ps_dev = hid_get_drvdata(hdev); + + return scnprintf(buf, PAGE_SIZE, "0x%08x\n", ps_dev->hw_version); +} + +static DEVICE_ATTR_RO(hardware_version); + +static struct attribute *ps_device_attributes[] = { + &dev_attr_firmware_version.attr, + &dev_attr_hardware_version.attr, + NULL +}; + +static const struct attribute_group ps_device_attribute_group = { + .attrs = ps_device_attributes, +}; + static int dualsense_get_calibration_data(struct dualsense *ds) { short gyro_pitch_bias, gyro_pitch_plus, gyro_pitch_minus; @@ -623,6 +662,30 @@ static int dualsense_get_calibration_data(struct dualsense *ds) return ret; } +static int 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); + if (ret) { + hid_err(ds->base.hdev, "Failed to retrieve DualSense firmware info: %d\n", ret); + goto err_free; + } + + ds->base.hw_version = get_unaligned_le32(&buf[24]); + ds->base.fw_version = get_unaligned_le32(&buf[28]); + +err_free: + kfree(buf); + return ret; +} + static int dualsense_get_mac_address(struct dualsense *ds) { uint8_t *buf; @@ -966,6 +1029,12 @@ static struct ps_device *dualsense_create(struct hid_device *hdev) } 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); + } + ret = ps_devices_list_add(ps_dev); if (ret) return ERR_PTR(ret); @@ -1000,6 +1069,13 @@ static struct ps_device *dualsense_create(struct hid_device *hdev) if (ret) goto err; + /* + * 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); + return &ds->base; err: @@ -1050,6 +1126,12 @@ static int ps_probe(struct hid_device *hdev, const struct hid_device_id *id) } } + ret = devm_device_add_group(&hdev->dev, &ps_device_attribute_group); + if (ret) { + hid_err(hdev, "Failed to register sysfs nodes.\n"); + goto err_close; + } + return ret; err_close: