Commit 2818ccb4 authored by Benjamin Tissoires's avatar Benjamin Tissoires
Browse files

Merge branch 'for-6.3/hid-sensor' into for-linus

Allow more custom IIO sensors through HID (Philipp Jungkamp)
parents 1f3a9573 f1f73651
Loading
Loading
Loading
Loading
+169 −73
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
 */

#include <linux/ctype.h>
#include <linux/dmi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -750,114 +751,209 @@ static void hid_sensor_custom_dev_if_remove(struct hid_sensor_custom

}

/* luid defined in FW (e.g. ISH).  Maybe used to identify sensor. */
static const char *const known_sensor_luid[] = { "020B000000000000" };
/*
 * Match a known custom sensor.
 * tag and luid is mandatory.
 */
struct hid_sensor_custom_match {
	const char *tag;
	const char *luid;
	const char *model;
	const char *manufacturer;
	bool check_dmi;
	struct dmi_system_id dmi;
};

static int get_luid_table_index(unsigned char *usage_str)
{
	int i;
/*
 * Custom sensor properties used for matching.
 */
struct hid_sensor_custom_properties {
	u16 serial_num[HID_CUSTOM_MAX_FEATURE_BYTES];
	u16 model[HID_CUSTOM_MAX_FEATURE_BYTES];
	u16 manufacturer[HID_CUSTOM_MAX_FEATURE_BYTES];
};

	for (i = 0; i < ARRAY_SIZE(known_sensor_luid); i++) {
		if (!strncmp(usage_str, known_sensor_luid[i],
			     strlen(known_sensor_luid[i])))
			return i;
static const struct hid_sensor_custom_match hid_sensor_custom_known_table[] = {
	/*
	 * Intel Integrated Sensor Hub (ISH)
	 */
	{	/* Intel ISH hinge */
		.tag = "INT",
		.luid = "020B000000000000",
		.manufacturer = "INTEL",
	},
	/*
	 * Lenovo Intelligent Sensing Solution (LISS)
	 */
	{	/* ambient light */
		.tag = "LISS",
		.luid = "0041010200000082",
		.model = "STK3X3X Sensor",
		.manufacturer = "Vendor 258",
		.check_dmi = true,
		.dmi.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
		}
	},
	{	/* human presence */
		.tag = "LISS",
		.luid = "0226000171AC0081",
		.model = "VL53L1_HOD Sensor",
		.manufacturer = "ST_MICRO",
		.check_dmi = true,
		.dmi.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
		}
	},
	{}
};

static bool hid_sensor_custom_prop_match_str(const u16 *prop, const char *match,
					     size_t count)
{
	while (count-- && *prop && *match) {
		if (*prop != (u16) *match)
			return false;
		prop++;
		match++;
	}

	return -ENODEV;
	return (count == -1) || *prop == (u16)*match;
}

static int get_known_custom_sensor_index(struct hid_sensor_hub_device *hsdev)
static int hid_sensor_custom_get_prop(struct hid_sensor_hub_device *hsdev,
				      u32 prop_usage_id, size_t prop_size,
				      u16 *prop)
{
	struct hid_sensor_hub_attribute_info sensor_manufacturer = { 0 };
	struct hid_sensor_hub_attribute_info sensor_luid_info = { 0 };
	int report_size;
	struct hid_sensor_hub_attribute_info prop_attr = { 0 };
	int ret;
	static u16 w_buf[HID_CUSTOM_MAX_FEATURE_BYTES];
	static char buf[HID_CUSTOM_MAX_FEATURE_BYTES];
	int i;

	memset(w_buf, 0, sizeof(w_buf));
	memset(buf, 0, sizeof(buf));
	memset(prop, 0, prop_size);

	/* get manufacturer info */
	ret = sensor_hub_input_get_attribute_info(hsdev,
			HID_FEATURE_REPORT, hsdev->usage,
			HID_USAGE_SENSOR_PROP_MANUFACTURER, &sensor_manufacturer);
	ret = sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT,
						  hsdev->usage, prop_usage_id,
						  &prop_attr);
	if (ret < 0)
		return ret;

	report_size =
		sensor_hub_get_feature(hsdev, sensor_manufacturer.report_id,
				       sensor_manufacturer.index, sizeof(w_buf),
				       w_buf);
	if (report_size <= 0) {
		hid_err(hsdev->hdev,
			"Failed to get sensor manufacturer info %d\n",
			report_size);
		return -ENODEV;
	ret = sensor_hub_get_feature(hsdev, prop_attr.report_id,
				     prop_attr.index, prop_size, prop);
	if (ret < 0) {
		hid_err(hsdev->hdev, "Failed to get sensor property %08x %d\n",
			prop_usage_id, ret);
		return ret;
	}

	/* convert from wide char to char */
	for (i = 0; i < ARRAY_SIZE(buf) - 1 && w_buf[i]; i++)
		buf[i] = (char)w_buf[i];
	return 0;
}

	/* ensure it's ISH sensor */
	if (strncmp(buf, "INTEL", strlen("INTEL")))
		return -ENODEV;
static bool
hid_sensor_custom_do_match(struct hid_sensor_hub_device *hsdev,
			   const struct hid_sensor_custom_match *match,
			   const struct hid_sensor_custom_properties *prop)
{
	struct dmi_system_id dmi[] = { match->dmi, { 0 } };

	if (!hid_sensor_custom_prop_match_str(prop->serial_num, "LUID:", 5) ||
	    !hid_sensor_custom_prop_match_str(prop->serial_num + 5, match->luid,
					      HID_CUSTOM_MAX_FEATURE_BYTES - 5))
		return false;

	if (match->model &&
	    !hid_sensor_custom_prop_match_str(prop->model, match->model,
					      HID_CUSTOM_MAX_FEATURE_BYTES))
		return false;

	if (match->manufacturer &&
	    !hid_sensor_custom_prop_match_str(prop->manufacturer, match->manufacturer,
					      HID_CUSTOM_MAX_FEATURE_BYTES))
		return false;

	if (match->check_dmi && !dmi_check_system(dmi))
		return false;

	return true;
}

	memset(w_buf, 0, sizeof(w_buf));
	memset(buf, 0, sizeof(buf));
static int
hid_sensor_custom_properties_get(struct hid_sensor_hub_device *hsdev,
				 struct hid_sensor_custom_properties *prop)
{
	int ret;

	/* get real usage id */
	ret = sensor_hub_input_get_attribute_info(hsdev,
			HID_FEATURE_REPORT, hsdev->usage,
			HID_USAGE_SENSOR_PROP_SERIAL_NUM, &sensor_luid_info);
	ret = hid_sensor_custom_get_prop(hsdev,
					 HID_USAGE_SENSOR_PROP_SERIAL_NUM,
					 HID_CUSTOM_MAX_FEATURE_BYTES,
					 prop->serial_num);
	if (ret < 0)
		return ret;

	report_size = sensor_hub_get_feature(hsdev, sensor_luid_info.report_id,
					     sensor_luid_info.index, sizeof(w_buf),
					     w_buf);
	if (report_size <= 0) {
		hid_err(hsdev->hdev, "Failed to get real usage info %d\n",
			report_size);
		return -ENODEV;
	}
	/*
	 * Ignore errors on the following model and manufacturer properties.
	 * Because these are optional, it is not an error if they are missing.
	 */

	/* convert from wide char to char */
	for (i = 0; i < ARRAY_SIZE(buf) - 1 && w_buf[i]; i++)
		buf[i] = (char)w_buf[i];
	hid_sensor_custom_get_prop(hsdev, HID_USAGE_SENSOR_PROP_MODEL,
				   HID_CUSTOM_MAX_FEATURE_BYTES,
				   prop->model);

	if (strlen(buf) != strlen(known_sensor_luid[0]) + 5) {
		hid_err(hsdev->hdev,
			"%s luid length not match %zu != (%zu + 5)\n", __func__,
			strlen(buf), strlen(known_sensor_luid[0]));
		return -ENODEV;
	hid_sensor_custom_get_prop(hsdev, HID_USAGE_SENSOR_PROP_MANUFACTURER,
				   HID_CUSTOM_MAX_FEATURE_BYTES,
				   prop->manufacturer);

	return 0;
}

	/* get table index with luid (not matching 'LUID: ' in luid) */
	return get_luid_table_index(&buf[5]);
static int
hid_sensor_custom_get_known(struct hid_sensor_hub_device *hsdev,
			    const struct hid_sensor_custom_match **known)
{
	int ret;
	const struct hid_sensor_custom_match *match =
		hid_sensor_custom_known_table;
	struct hid_sensor_custom_properties *prop;

	prop = kmalloc(sizeof(struct hid_sensor_custom_properties), GFP_KERNEL);
	if (!prop)
		return -ENOMEM;

	ret = hid_sensor_custom_properties_get(hsdev, prop);
	if (ret < 0)
		goto out;

	while (match->tag) {
		if (hid_sensor_custom_do_match(hsdev, match, prop)) {
			*known = match;
			ret = 0;
			goto out;
		}
		match++;
	}
	ret = -ENODATA;
out:
	kfree(prop);
	return ret;
}

static struct platform_device *
hid_sensor_register_platform_device(struct platform_device *pdev,
				    struct hid_sensor_hub_device *hsdev,
				    int index)
				    const struct hid_sensor_custom_match *match)
{
	char real_usage[HID_SENSOR_USAGE_LENGTH] = { 0 };
	char real_usage[HID_SENSOR_USAGE_LENGTH];
	struct platform_device *custom_pdev;
	const char *dev_name;
	char *c;

	/* copy real usage id */
	memcpy(real_usage, known_sensor_luid[index], 4);
	memcpy(real_usage, match->luid, 4);

	/* usage id are all lowcase */
	for (c = real_usage; *c != '\0'; c++)
		*c = tolower(*c);

	/* HID-SENSOR-INT-REAL_USAGE_ID */
	dev_name = kasprintf(GFP_KERNEL, "HID-SENSOR-INT-%s", real_usage);
	/* HID-SENSOR-TAG-REAL_USAGE_ID */
	dev_name = kasprintf(GFP_KERNEL, "HID-SENSOR-%s-%s",
			     match->tag, real_usage);
	if (!dev_name)
		return ERR_PTR(-ENOMEM);

@@ -873,7 +969,7 @@ static int hid_sensor_custom_probe(struct platform_device *pdev)
	struct hid_sensor_custom *sensor_inst;
	struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
	int ret;
	int index;
	const struct hid_sensor_custom_match *match;

	sensor_inst = devm_kzalloc(&pdev->dev, sizeof(*sensor_inst),
				   GFP_KERNEL);
@@ -888,10 +984,10 @@ static int hid_sensor_custom_probe(struct platform_device *pdev)
	mutex_init(&sensor_inst->mutex);
	platform_set_drvdata(pdev, sensor_inst);

	index = get_known_custom_sensor_index(hsdev);
	if (index >= 0 && index < ARRAY_SIZE(known_sensor_luid)) {
	ret = hid_sensor_custom_get_known(hsdev, &match);
	if (!ret) {
		sensor_inst->custom_pdev =
			hid_sensor_register_platform_device(pdev, hsdev, index);
			hid_sensor_register_platform_device(pdev, hsdev, match);

		ret = PTR_ERR_OR_ZERO(sensor_inst->custom_pdev);
		if (ret) {
+14 −13
Original line number Diff line number Diff line
@@ -86,6 +86,7 @@ static int als_read_raw(struct iio_dev *indio_dev,
			      long mask)
{
	struct als_state *als_state = iio_priv(indio_dev);
	struct hid_sensor_hub_device *hsdev = als_state->common_attributes.hsdev;
	int report_id = -1;
	u32 address;
	int ret_type;
@@ -110,11 +111,8 @@ static int als_read_raw(struct iio_dev *indio_dev,
			hid_sensor_power_state(&als_state->common_attributes,
						true);
			*val = sensor_hub_input_attr_get_raw_value(
					als_state->common_attributes.hsdev,
					HID_USAGE_SENSOR_ALS, address,
					report_id,
					SENSOR_HUB_SYNC,
					min < 0);
					hsdev, hsdev->usage, address, report_id,
					SENSOR_HUB_SYNC, min < 0);
			hid_sensor_power_state(&als_state->common_attributes,
						false);
		} else {
@@ -259,9 +257,7 @@ static int als_parse_report(struct platform_device *pdev,
	dev_dbg(&pdev->dev, "als %x:%x\n", st->als_illum.index,
			st->als_illum.report_id);

	st->scale_precision = hid_sensor_format_scale(
				HID_USAGE_SENSOR_ALS,
				&st->als_illum,
	st->scale_precision = hid_sensor_format_scale(usage_id, &st->als_illum,
				&st->scale_pre_decml, &st->scale_post_decml);

	return ret;
@@ -285,7 +281,8 @@ static int hid_als_probe(struct platform_device *pdev)
	als_state->common_attributes.hsdev = hsdev;
	als_state->common_attributes.pdev = pdev;

	ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_ALS,
	ret = hid_sensor_parse_common_attributes(hsdev,
					hsdev->usage,
					&als_state->common_attributes,
					als_sensitivity_addresses,
					ARRAY_SIZE(als_sensitivity_addresses));
@@ -303,7 +300,8 @@ static int hid_als_probe(struct platform_device *pdev)

	ret = als_parse_report(pdev, hsdev,
			       (struct iio_chan_spec *)indio_dev->channels,
			       HID_USAGE_SENSOR_ALS, als_state);
			       hsdev->usage,
			       als_state);
	if (ret) {
		dev_err(&pdev->dev, "failed to setup attributes\n");
		return ret;
@@ -333,8 +331,7 @@ static int hid_als_probe(struct platform_device *pdev)
	als_state->callbacks.send_event = als_proc_event;
	als_state->callbacks.capture_sample = als_capture_sample;
	als_state->callbacks.pdev = pdev;
	ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ALS,
					&als_state->callbacks);
	ret = sensor_hub_register_callback(hsdev, hsdev->usage, &als_state->callbacks);
	if (ret < 0) {
		dev_err(&pdev->dev, "callback reg failed\n");
		goto error_iio_unreg;
@@ -356,7 +353,7 @@ static int hid_als_remove(struct platform_device *pdev)
	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
	struct als_state *als_state = iio_priv(indio_dev);

	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ALS);
	sensor_hub_remove_callback(hsdev, hsdev->usage);
	iio_device_unregister(indio_dev);
	hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes);

@@ -368,6 +365,10 @@ static const struct platform_device_id hid_als_ids[] = {
		/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
		.name = "HID-SENSOR-200041",
	},
	{
		/* Format: HID-SENSOR-custom_sensor_tag-usage_id_in_hex_lowercase */
		.name = "HID-SENSOR-LISS-0041",
	},
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, hid_als_ids);
+23 −14
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ static int prox_read_raw(struct iio_dev *indio_dev,
			      long mask)
{
	struct prox_state *prox_state = iio_priv(indio_dev);
	struct hid_sensor_hub_device *hsdev;
	int report_id = -1;
	u32 address;
	int ret_type;
@@ -75,6 +76,7 @@ static int prox_read_raw(struct iio_dev *indio_dev,
			report_id = prox_state->prox_attr.report_id;
			min = prox_state->prox_attr.logical_minimum;
			address = HID_USAGE_SENSOR_HUMAN_PRESENCE;
			hsdev = prox_state->common_attributes.hsdev;
			break;
		default:
			report_id = -1;
@@ -84,11 +86,8 @@ static int prox_read_raw(struct iio_dev *indio_dev,
			hid_sensor_power_state(&prox_state->common_attributes,
						true);
			*val = sensor_hub_input_attr_get_raw_value(
				prox_state->common_attributes.hsdev,
				HID_USAGE_SENSOR_PROX, address,
				report_id,
				SENSOR_HUB_SYNC,
				min < 0);
				hsdev, hsdev->usage, address, report_id,
				SENSOR_HUB_SYNC, min < 0);
			hid_sensor_power_state(&prox_state->common_attributes,
						false);
		} else {
@@ -191,12 +190,18 @@ static int prox_capture_sample(struct hid_sensor_hub_device *hsdev,

	switch (usage_id) {
	case HID_USAGE_SENSOR_HUMAN_PRESENCE:
		switch (raw_len) {
		case 1:
			prox_state->human_presence = *(u8 *)raw_data;
			return 0;
		case 4:
			prox_state->human_presence = *(u32 *)raw_data;
		ret = 0;
		break;
			return 0;
		default:
			break;
		}
		break;
	}

	return ret;
}
@@ -244,7 +249,7 @@ static int hid_prox_probe(struct platform_device *pdev)
	prox_state->common_attributes.hsdev = hsdev;
	prox_state->common_attributes.pdev = pdev;

	ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_PROX,
	ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage,
					&prox_state->common_attributes,
					prox_sensitivity_addresses,
					ARRAY_SIZE(prox_sensitivity_addresses));
@@ -262,7 +267,7 @@ static int hid_prox_probe(struct platform_device *pdev)

	ret = prox_parse_report(pdev, hsdev,
				(struct iio_chan_spec *)indio_dev->channels,
				HID_USAGE_SENSOR_PROX, prox_state);
				hsdev->usage, prox_state);
	if (ret) {
		dev_err(&pdev->dev, "failed to setup attributes\n");
		return ret;
@@ -291,7 +296,7 @@ static int hid_prox_probe(struct platform_device *pdev)
	prox_state->callbacks.send_event = prox_proc_event;
	prox_state->callbacks.capture_sample = prox_capture_sample;
	prox_state->callbacks.pdev = pdev;
	ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_PROX,
	ret = sensor_hub_register_callback(hsdev, hsdev->usage,
					   &prox_state->callbacks);
	if (ret < 0) {
		dev_err(&pdev->dev, "callback reg failed\n");
@@ -314,7 +319,7 @@ static int hid_prox_remove(struct platform_device *pdev)
	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
	struct prox_state *prox_state = iio_priv(indio_dev);

	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PROX);
	sensor_hub_remove_callback(hsdev, hsdev->usage);
	iio_device_unregister(indio_dev);
	hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes);

@@ -326,6 +331,10 @@ static const struct platform_device_id hid_prox_ids[] = {
		/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
		.name = "HID-SENSOR-200011",
	},
	{
		/* Format: HID-SENSOR-tag-usage_id_in_hex_lowercase */
		.name = "HID-SENSOR-LISS-0226",
	},
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, hid_prox_ids);
+1 −0
Original line number Diff line number Diff line
@@ -132,6 +132,7 @@
#define HID_USAGE_SENSOR_PROP_FRIENDLY_NAME			0x200301
#define HID_USAGE_SENSOR_PROP_SERIAL_NUM			0x200307
#define HID_USAGE_SENSOR_PROP_MANUFACTURER			0x200305
#define HID_USAGE_SENSOR_PROP_MODEL				0x200306
#define HID_USAGE_SENSOR_PROP_REPORT_INTERVAL			0x20030E
#define HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS			0x20030F
#define HID_USAGE_SENSOR_PROP_SENSITIVITY_RANGE_PCT		0x200310