Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f0e3e17c authored by Roderick Colenbrander's avatar Roderick Colenbrander Committed by Kishor Krishna Bhat
Browse files

BACKPORT: HID: playstation: Report DualSense hardware and firmware version



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: default avatarRoderick Colenbrander <roderick.colenbrander@sony.com>
Reviewed-by: default avatarBarnabás Pőcze <pobrn@protonmail.com>
Signed-off-by: default avatarBenjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: default avatarFarid Chahla <farid.chahla@sony.com>
Signed-off-by: default avatarSiarhei Vishniakou <svv@google.com>
Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git


Git-commit: 0b25b55d34f554b43a679e7e1303beb973b63e27
Signed-off-by: default avatarKishor Krishna Bhat <kishkris@codeaurora.org>
parent 7c1667ec
Loading
Loading
Loading
Loading
+82 −0
Original line number Diff line number Diff line
@@ -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: