Commit 98c062e8 authored by Philipp Jungkamp's avatar Philipp Jungkamp Committed by Jiri Kosina

HID: hid-sensor-custom: Allow more custom iio sensors

The known LUID table for established/known custom HID sensors was
limited to sensors with "INTEL" as manufacturer. But some vendors such
as Lenovo also include fairly standard iio sensors (e.g. ambient light)
in their custom sensors.

Expand the known custom sensors table by a tag used for the platform
device name and match sensors based on the LUID as well as optionally
on model and manufacturer properties.
Signed-off-by: default avatarPhilipp Jungkamp <p.jungkamp@gmx.net>
Reviewed-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
Acked-by: default avatarSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent 2043f9a3
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
*/ */
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/dmi.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -750,114 +751,179 @@ static void hid_sensor_custom_dev_if_remove(struct hid_sensor_custom ...@@ -750,114 +751,179 @@ 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) /*
{ * Custom sensor properties used for matching.
int i; */
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];
};
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",
},
{}
};
for (i = 0; i < ARRAY_SIZE(known_sensor_luid); i++) { static bool hid_sensor_custom_prop_match_str(const u16 *prop, const char *match,
if (!strncmp(usage_str, known_sensor_luid[i], size_t count)
strlen(known_sensor_luid[i]))) {
return i; 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 prop_attr = { 0 };
struct hid_sensor_hub_attribute_info sensor_luid_info = { 0 };
int report_size;
int ret; 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(prop, 0, prop_size);
memset(buf, 0, sizeof(buf));
/* get manufacturer info */ ret = sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT,
ret = sensor_hub_input_get_attribute_info(hsdev, hsdev->usage, prop_usage_id,
HID_FEATURE_REPORT, hsdev->usage, &prop_attr);
HID_USAGE_SENSOR_PROP_MANUFACTURER, &sensor_manufacturer);
if (ret < 0) if (ret < 0)
return ret; return ret;
report_size = ret = sensor_hub_get_feature(hsdev, prop_attr.report_id,
sensor_hub_get_feature(hsdev, sensor_manufacturer.report_id, prop_attr.index, prop_size, prop);
sensor_manufacturer.index, sizeof(w_buf), if (ret < 0) {
w_buf); hid_err(hsdev->hdev, "Failed to get sensor property %08x %d\n",
if (report_size <= 0) { prop_usage_id, ret);
hid_err(hsdev->hdev, return ret;
"Failed to get sensor manufacturer info %d\n",
report_size);
return -ENODEV;
} }
/* convert from wide char to char */ return 0;
for (i = 0; i < ARRAY_SIZE(buf) - 1 && w_buf[i]; i++) }
buf[i] = (char)w_buf[i];
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;
/* ensure it's ISH sensor */ if (match->model &&
if (strncmp(buf, "INTEL", strlen("INTEL"))) !hid_sensor_custom_prop_match_str(prop->model, match->model,
return -ENODEV; HID_CUSTOM_MAX_FEATURE_BYTES))
return false;
memset(w_buf, 0, sizeof(w_buf)); if (match->manufacturer &&
memset(buf, 0, sizeof(buf)); !hid_sensor_custom_prop_match_str(prop->manufacturer, match->manufacturer,
HID_CUSTOM_MAX_FEATURE_BYTES))
return false;
/* get real usage id */ if (match->check_dmi && !dmi_check_system(dmi))
ret = sensor_hub_input_get_attribute_info(hsdev, return false;
HID_FEATURE_REPORT, hsdev->usage,
HID_USAGE_SENSOR_PROP_SERIAL_NUM, &sensor_luid_info); return true;
}
static int
hid_sensor_custom_properties_get(struct hid_sensor_hub_device *hsdev,
struct hid_sensor_custom_properties *prop)
{
int ret;
ret = hid_sensor_custom_get_prop(hsdev,
HID_USAGE_SENSOR_PROP_SERIAL_NUM,
HID_CUSTOM_MAX_FEATURE_BYTES,
prop->serial_num);
if (ret < 0) if (ret < 0)
return ret; return ret;
report_size = sensor_hub_get_feature(hsdev, sensor_luid_info.report_id, /*
sensor_luid_info.index, sizeof(w_buf), * Ignore errors on the following model and manufacturer properties.
w_buf); * Because these are optional, it is not an error if they are missing.
if (report_size <= 0) { */
hid_err(hsdev->hdev, "Failed to get real usage info %d\n",
report_size);
return -ENODEV;
}
/* convert from wide char to char */ hid_sensor_custom_get_prop(hsdev, HID_USAGE_SENSOR_PROP_MODEL,
for (i = 0; i < ARRAY_SIZE(buf) - 1 && w_buf[i]; i++) HID_CUSTOM_MAX_FEATURE_BYTES,
buf[i] = (char)w_buf[i]; prop->model);
if (strlen(buf) != strlen(known_sensor_luid[0]) + 5) { hid_sensor_custom_get_prop(hsdev, HID_USAGE_SENSOR_PROP_MANUFACTURER,
hid_err(hsdev->hdev, HID_CUSTOM_MAX_FEATURE_BYTES,
"%s luid length not match %zu != (%zu + 5)\n", __func__, prop->manufacturer);
strlen(buf), strlen(known_sensor_luid[0]));
return -ENODEV; return 0;
}
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;
ret = hid_sensor_custom_properties_get(hsdev, &prop);
if (ret < 0)
return ret;
while (match->tag) {
if (hid_sensor_custom_do_match(hsdev, match, &prop)) {
*known = match;
return 0;
}
match++;
} }
/* get table index with luid (not matching 'LUID: ' in luid) */ return -ENODATA;
return get_luid_table_index(&buf[5]);
} }
static struct platform_device * static struct platform_device *
hid_sensor_register_platform_device(struct platform_device *pdev, hid_sensor_register_platform_device(struct platform_device *pdev,
struct hid_sensor_hub_device *hsdev, 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; struct platform_device *custom_pdev;
const char *dev_name; const char *dev_name;
char *c; char *c;
/* copy real usage id */ memcpy(real_usage, match->luid, 4);
memcpy(real_usage, known_sensor_luid[index], 4);
/* usage id are all lowcase */ /* usage id are all lowcase */
for (c = real_usage; *c != '\0'; c++) for (c = real_usage; *c != '\0'; c++)
*c = tolower(*c); *c = tolower(*c);
/* HID-SENSOR-INT-REAL_USAGE_ID */ /* HID-SENSOR-TAG-REAL_USAGE_ID */
dev_name = kasprintf(GFP_KERNEL, "HID-SENSOR-INT-%s", real_usage); dev_name = kasprintf(GFP_KERNEL, "HID-SENSOR-%s-%s",
match->tag, real_usage);
if (!dev_name) if (!dev_name)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
...@@ -873,7 +939,7 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) ...@@ -873,7 +939,7 @@ static int hid_sensor_custom_probe(struct platform_device *pdev)
struct hid_sensor_custom *sensor_inst; struct hid_sensor_custom *sensor_inst;
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
int ret; int ret;
int index; const struct hid_sensor_custom_match *match;
sensor_inst = devm_kzalloc(&pdev->dev, sizeof(*sensor_inst), sensor_inst = devm_kzalloc(&pdev->dev, sizeof(*sensor_inst),
GFP_KERNEL); GFP_KERNEL);
...@@ -888,10 +954,10 @@ static int hid_sensor_custom_probe(struct platform_device *pdev) ...@@ -888,10 +954,10 @@ static int hid_sensor_custom_probe(struct platform_device *pdev)
mutex_init(&sensor_inst->mutex); mutex_init(&sensor_inst->mutex);
platform_set_drvdata(pdev, sensor_inst); platform_set_drvdata(pdev, sensor_inst);
index = get_known_custom_sensor_index(hsdev); ret = hid_sensor_custom_get_known(hsdev, &match);
if (index >= 0 && index < ARRAY_SIZE(known_sensor_luid)) { if (!ret) {
sensor_inst->custom_pdev = 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); ret = PTR_ERR_OR_ZERO(sensor_inst->custom_pdev);
if (ret) { if (ret) {
......
...@@ -132,6 +132,7 @@ ...@@ -132,6 +132,7 @@
#define HID_USAGE_SENSOR_PROP_FRIENDLY_NAME 0x200301 #define HID_USAGE_SENSOR_PROP_FRIENDLY_NAME 0x200301
#define HID_USAGE_SENSOR_PROP_SERIAL_NUM 0x200307 #define HID_USAGE_SENSOR_PROP_SERIAL_NUM 0x200307
#define HID_USAGE_SENSOR_PROP_MANUFACTURER 0x200305 #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_REPORT_INTERVAL 0x20030E
#define HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS 0x20030F #define HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS 0x20030F
#define HID_USAGE_SENSOR_PROP_SENSITIVITY_RANGE_PCT 0x200310 #define HID_USAGE_SENSOR_PROP_SENSITIVITY_RANGE_PCT 0x200310
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment