Merge branch 'for-4.1/sensor-hub' into for-linus

Conflicts:
	drivers/iio/common/hid-sensors/hid-sensor-trigger.c
	include/linux/hid-sensor-hub.h
This commit is contained in:
Jiri Kosina 2015-04-13 23:43:34 +02:00
commit 2e455c27bd
17 changed files with 1153 additions and 109 deletions

View File

@ -138,3 +138,87 @@ accelerometer wants to poll X axis value, then it can call this function with
the usage id of X axis. HID sensors can provide events, so this is not necessary
to poll for any field. If there is some new sample, the core driver will call
registered callback function to process the sample.
----------
HID Custom and generic Sensors
HID Sensor specification defines two special sensor usage types. Since they
don't represent a standard sensor, it is not possible to define using Linux IIO
type interfaces.
The purpose of these sensors is to extend the functionality or provide a
way to obfuscate the data being communicated by a sensor. Without knowing the
mapping between the data and its encapsulated form, it is difficult for
an application/driver to determine what data is being communicated by the sensor.
This allows some differentiating use cases, where vendor can provide applications.
Some common use cases are debug other sensors or to provide some events like
keyboard attached/detached or lid open/close.
To allow application to utilize these sensors, here they are exported uses sysfs
attribute groups, attributes and misc device interface.
An example of this representation on sysfs:
/sys/devices/pci0000:00/INT33C2:00/i2c-0/i2c-INT33D1:00/0018:8086:09FA.0001/HID-SENSOR-2000e1.6.auto$ tree -R
.
????????? enable_sensor
????????? feature-0-200316
??????? ????????? feature-0-200316-maximum
??????? ????????? feature-0-200316-minimum
??????? ????????? feature-0-200316-name
??????? ????????? feature-0-200316-size
??????? ????????? feature-0-200316-unit-expo
??????? ????????? feature-0-200316-units
??????? ????????? feature-0-200316-value
????????? feature-1-200201
??????? ????????? feature-1-200201-maximum
??????? ????????? feature-1-200201-minimum
??????? ????????? feature-1-200201-name
??????? ????????? feature-1-200201-size
??????? ????????? feature-1-200201-unit-expo
??????? ????????? feature-1-200201-units
??????? ????????? feature-1-200201-value
????????? input-0-200201
??????? ????????? input-0-200201-maximum
??????? ????????? input-0-200201-minimum
??????? ????????? input-0-200201-name
??????? ????????? input-0-200201-size
??????? ????????? input-0-200201-unit-expo
??????? ????????? input-0-200201-units
??????? ????????? input-0-200201-value
????????? input-1-200202
??????? ????????? input-1-200202-maximum
??????? ????????? input-1-200202-minimum
??????? ????????? input-1-200202-name
??????? ????????? input-1-200202-size
??????? ????????? input-1-200202-unit-expo
??????? ????????? input-1-200202-units
??????? ????????? input-1-200202-value
Here there is a custom sensors with four fields, two feature and two inputs.
Each field is represented by a set of attributes. All fields except the "value"
are read only. The value field is a RW field.
Example
/sys/bus/platform/devices/HID-SENSOR-2000e1.6.auto/feature-0-200316$ grep -r . *
feature-0-200316-maximum:6
feature-0-200316-minimum:0
feature-0-200316-name:property-reporting-state
feature-0-200316-size:1
feature-0-200316-unit-expo:0
feature-0-200316-units:25
feature-0-200316-value:1
How to enable such sensor?
By default sensor can be power gated. To enable sysfs attribute "enable" can be
used.
$ echo 1 > enable_sensor
Once enabled and powered on, sensor can report value using HID reports.
These reports are pushed using misc device interface in a FIFO order.
/dev$ tree | grep HID-SENSOR-2000e1.6.auto
??????? ????????? 10:53 -> ../HID-SENSOR-2000e1.6.auto
????????? HID-SENSOR-2000e1.6.auto
Each reports can be of variable length preceded by a header. This header
consist of a 32 bit usage id, 64 bit time stamp and 32 bit length field of raw
data.

View File

@ -878,6 +878,21 @@ config HID_SENSOR_HUB
for events and handle data streams. Each sensor driver can format
data and present to user mode using input or IIO interface.
config HID_SENSOR_CUSTOM_SENSOR
tristate "HID Sensors hub custom sensor support"
depends on HID_SENSOR_HUB
default n
---help---
HID Sensor hub specification allows definition of some custom and
generic sensors. Unlike other HID sensors, they can't be exported
via Linux IIO because of custom fields. This is up to the manufacturer
to decide how to interpret these special sensor ids and process in
the user space. Currently some manufacturers are using these ids for
sensor calibration and debugging other sensors. Manufacturers
should't use these special custom sensor ids to export any of the
standard sensors.
Select this config option for custom/generic sensor support.
endmenu
endif # HID

View File

@ -100,6 +100,7 @@ obj-$(CONFIG_HID_WACOM) += wacom.o
obj-$(CONFIG_HID_WALTOP) += hid-waltop.o
obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o
obj-$(CONFIG_HID_SENSOR_HUB) += hid-sensor-hub.o
obj-$(CONFIG_HID_SENSOR_CUSTOM_SENSOR) += hid-sensor-custom.o
obj-$(CONFIG_USB_HID) += usbhid/
obj-$(CONFIG_USB_MOUSE) += usbhid/

View File

@ -0,0 +1,849 @@
/*
* hid-sensor-custom.c
* Copyright (c) 2015, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/kfifo.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/bsearch.h>
#include <linux/platform_device.h>
#include <linux/hid-sensor-hub.h>
#define HID_CUSTOM_NAME_LENGTH 64
#define HID_CUSTOM_MAX_CORE_ATTRS 10
#define HID_CUSTOM_TOTAL_ATTRS (HID_CUSTOM_MAX_CORE_ATTRS + 1)
#define HID_CUSTOM_FIFO_SIZE 4096
#define HID_CUSTOM_MAX_FEATURE_BYTES 64
struct hid_sensor_custom_field {
int report_id;
char group_name[HID_CUSTOM_NAME_LENGTH];
struct hid_sensor_hub_attribute_info attribute;
struct device_attribute sd_attrs[HID_CUSTOM_MAX_CORE_ATTRS];
char attr_name[HID_CUSTOM_TOTAL_ATTRS][HID_CUSTOM_NAME_LENGTH];
struct attribute *attrs[HID_CUSTOM_TOTAL_ATTRS];
struct attribute_group hid_custom_attribute_group;
};
struct hid_sensor_custom {
struct mutex mutex;
struct platform_device *pdev;
struct hid_sensor_hub_device *hsdev;
struct hid_sensor_hub_callbacks callbacks;
int sensor_field_count;
struct hid_sensor_custom_field *fields;
int input_field_count;
int input_report_size;
int input_report_recd_size;
bool input_skip_sample;
bool enable;
struct hid_sensor_custom_field *power_state;
struct hid_sensor_custom_field *report_state;
struct miscdevice custom_dev;
struct kfifo data_fifo;
unsigned long misc_opened;
wait_queue_head_t wait;
};
/* Header for each sample to user space via dev interface */
struct hid_sensor_sample {
u32 usage_id;
u64 timestamp;
u32 raw_len;
} __packed;
static struct attribute hid_custom_attrs[] = {
{.name = "name", .mode = S_IRUGO},
{.name = "units", .mode = S_IRUGO},
{.name = "unit-expo", .mode = S_IRUGO},
{.name = "minimum", .mode = S_IRUGO},
{.name = "maximum", .mode = S_IRUGO},
{.name = "size", .mode = S_IRUGO},
{.name = "value", .mode = S_IWUSR | S_IRUGO},
{.name = NULL}
};
static const struct hid_custom_usage_desc {
int usage_id;
char *desc;
} hid_custom_usage_desc_table[] = {
{0x200201, "event-sensor-state"},
{0x200202, "event-sensor-event"},
{0x200301, "property-friendly-name"},
{0x200302, "property-persistent-unique-id"},
{0x200303, "property-sensor-status"},
{0x200304, "property-min-report-interval"},
{0x200305, "property-sensor-manufacturer"},
{0x200306, "property-sensor-model"},
{0x200307, "property-sensor-serial-number"},
{0x200308, "property-sensor-description"},
{0x200309, "property-sensor-connection-type"},
{0x20030A, "property-sensor-device-path"},
{0x20030B, "property-hardware-revision"},
{0x20030C, "property-firmware-version"},
{0x20030D, "property-release-date"},
{0x20030E, "property-report-interval"},
{0x20030F, "property-change-sensitivity-absolute"},
{0x200310, "property-change-sensitivity-percent-range"},
{0x200311, "property-change-sensitivity-percent-relative"},
{0x200312, "property-accuracy"},
{0x200313, "property-resolution"},
{0x200314, "property-maximum"},
{0x200315, "property-minimum"},
{0x200316, "property-reporting-state"},
{0x200317, "property-sampling-rate"},
{0x200318, "property-response-curve"},
{0x200319, "property-power-state"},
{0x200540, "data-field-custom"},
{0x200541, "data-field-custom-usage"},
{0x200542, "data-field-custom-boolean-array"},
{0x200543, "data-field-custom-value"},
{0x200544, "data-field-custom-value_1"},
{0x200545, "data-field-custom-value_2"},
{0x200546, "data-field-custom-value_3"},
{0x200547, "data-field-custom-value_4"},
{0x200548, "data-field-custom-value_5"},
{0x200549, "data-field-custom-value_6"},
{0x20054A, "data-field-custom-value_7"},
{0x20054B, "data-field-custom-value_8"},
{0x20054C, "data-field-custom-value_9"},
{0x20054D, "data-field-custom-value_10"},
{0x20054E, "data-field-custom-value_11"},
{0x20054F, "data-field-custom-value_12"},
{0x200550, "data-field-custom-value_13"},
{0x200551, "data-field-custom-value_14"},
{0x200552, "data-field-custom-value_15"},
{0x200553, "data-field-custom-value_16"},
{0x200554, "data-field-custom-value_17"},
{0x200555, "data-field-custom-value_18"},
{0x200556, "data-field-custom-value_19"},
{0x200557, "data-field-custom-value_20"},
{0x200558, "data-field-custom-value_21"},
{0x200559, "data-field-custom-value_22"},
{0x20055A, "data-field-custom-value_23"},
{0x20055B, "data-field-custom-value_24"},
{0x20055C, "data-field-custom-value_25"},
{0x20055D, "data-field-custom-value_26"},
{0x20055E, "data-field-custom-value_27"},
{0x20055F, "data-field-custom-value_28"},
};
static int usage_id_cmp(const void *p1, const void *p2)
{
if (*(int *)p1 < *(int *)p2)
return -1;
if (*(int *)p1 > *(int *)p2)
return 1;
return 0;
}
static ssize_t enable_sensor_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
return sprintf(buf, "%d\n", sensor_inst->enable);
}
static int set_power_report_state(struct hid_sensor_custom *sensor_inst,
bool state)
{
int power_val = -1;
int report_val = -1;
u32 power_state_usage_id;
u32 report_state_usage_id;
int ret;
/*
* It is possible that the power/report state ids are not present.
* In this case this function will return success. But if the
* ids are present, then it will return error if set fails.
*/
if (state) {
power_state_usage_id =
HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM;
report_state_usage_id =
HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM;
} else {
power_state_usage_id =
HID_USAGE_SENSOR_PROP_POWER_STATE_D4_POWER_OFF_ENUM;
report_state_usage_id =
HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM;
}
if (sensor_inst->power_state)
power_val = hid_sensor_get_usage_index(sensor_inst->hsdev,
sensor_inst->power_state->attribute.report_id,
sensor_inst->power_state->attribute.index,
power_state_usage_id);
if (sensor_inst->report_state)
report_val = hid_sensor_get_usage_index(sensor_inst->hsdev,
sensor_inst->report_state->attribute.report_id,
sensor_inst->report_state->attribute.index,
report_state_usage_id);
if (power_val >= 0) {
power_val +=
sensor_inst->power_state->attribute.logical_minimum;
ret = sensor_hub_set_feature(sensor_inst->hsdev,
sensor_inst->power_state->attribute.report_id,
sensor_inst->power_state->attribute.index,
sizeof(power_val),
&power_val);
if (ret) {
hid_err(sensor_inst->hsdev->hdev,
"Set power state failed\n");
return ret;
}
}
if (report_val >= 0) {
report_val +=
sensor_inst->report_state->attribute.logical_minimum;
ret = sensor_hub_set_feature(sensor_inst->hsdev,
sensor_inst->report_state->attribute.report_id,
sensor_inst->report_state->attribute.index,
sizeof(report_val),
&report_val);
if (ret) {
hid_err(sensor_inst->hsdev->hdev,
"Set report state failed\n");
return ret;
}
}
return 0;
}
static ssize_t enable_sensor_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct platform_device *pdev = to_platform_device(dev);
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
int value;
int ret = -EINVAL;
if (kstrtoint(buf, 0, &value) != 0)
return -EINVAL;
mutex_lock(&sensor_inst->mutex);
if (value && !sensor_inst->enable) {
ret = sensor_hub_device_open(sensor_inst->hsdev);
if (ret)
goto unlock_state;
ret = set_power_report_state(sensor_inst, true);
if (ret) {
sensor_hub_device_close(sensor_inst->hsdev);
goto unlock_state;
}
sensor_inst->enable = true;
} else if (!value && sensor_inst->enable) {
ret = set_power_report_state(sensor_inst, false);
sensor_hub_device_close(sensor_inst->hsdev);
sensor_inst->enable = false;
}
unlock_state:
mutex_unlock(&sensor_inst->mutex);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR_RW(enable_sensor);
static struct attribute *enable_sensor_attrs[] = {
&dev_attr_enable_sensor.attr,
NULL,
};
static struct attribute_group enable_sensor_attr_group = {
.attrs = enable_sensor_attrs,
};
static ssize_t show_value(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct platform_device *pdev = to_platform_device(dev);
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
struct hid_sensor_hub_attribute_info *attribute;
int index, usage, field_index;
char name[HID_CUSTOM_NAME_LENGTH];
bool feature = false;
bool input = false;
int value = 0;
if (sscanf(attr->attr.name, "feature-%d-%x-%s", &index, &usage,
name) == 3) {
feature = true;
field_index = index + sensor_inst->input_field_count;
} else if (sscanf(attr->attr.name, "input-%d-%x-%s", &index, &usage,
name) == 3) {
input = true;
field_index = index;
} else
return -EINVAL;
if (!strncmp(name, "value", strlen("value"))) {
u32 report_id;
int ret;
attribute = &sensor_inst->fields[field_index].attribute;
report_id = attribute->report_id;
if (feature) {
u8 values[HID_CUSTOM_MAX_FEATURE_BYTES];
int len = 0;
u64 value = 0;
int i = 0;
ret = sensor_hub_get_feature(sensor_inst->hsdev,
report_id,
index,
sizeof(values), values);
if (ret < 0)
return ret;
while (i < ret) {
if (i + attribute->size > ret) {
len += snprintf(&buf[len],
PAGE_SIZE - len,
"%d ", values[i]);
break;
}
switch (attribute->size) {
case 2:
value = (u64) *(u16 *)&values[i];
i += attribute->size;
break;
case 4:
value = (u64) *(u32 *)&values[i];
i += attribute->size;
break;
case 8:
value = *(u64 *)&values[i];
i += attribute->size;
break;
default:
value = (u64) values[i];
++i;
break;
}
len += snprintf(&buf[len], PAGE_SIZE - len,
"%lld ", value);
}
len += snprintf(&buf[len], PAGE_SIZE - len, "\n");
return len;
} else if (input)
value = sensor_hub_input_attr_get_raw_value(
sensor_inst->hsdev,
sensor_inst->hsdev->usage,
usage, report_id,
SENSOR_HUB_SYNC);
} else if (!strncmp(name, "units", strlen("units")))
value = sensor_inst->fields[field_index].attribute.units;
else if (!strncmp(name, "unit-expo", strlen("unit-expo")))
value = sensor_inst->fields[field_index].attribute.unit_expo;
else if (!strncmp(name, "size", strlen("size")))
value = sensor_inst->fields[field_index].attribute.size;
else if (!strncmp(name, "minimum", strlen("minimum")))
value = sensor_inst->fields[field_index].attribute.
logical_minimum;
else if (!strncmp(name, "maximum", strlen("maximum")))
value = sensor_inst->fields[field_index].attribute.
logical_maximum;
else if (!strncmp(name, "name", strlen("name"))) {
struct hid_custom_usage_desc *usage_desc;
usage_desc = bsearch(&usage, hid_custom_usage_desc_table,
ARRAY_SIZE(hid_custom_usage_desc_table),
sizeof(struct hid_custom_usage_desc),
usage_id_cmp);
if (usage_desc)
return snprintf(buf, PAGE_SIZE, "%s\n",
usage_desc->desc);
else
return sprintf(buf, "not-specified\n");
} else
return -EINVAL;
return sprintf(buf, "%d\n", value);
}
static ssize_t store_value(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct platform_device *pdev = to_platform_device(dev);
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
int index, field_index, usage;
char name[HID_CUSTOM_NAME_LENGTH];
int value;
if (sscanf(attr->attr.name, "feature-%d-%x-%s", &index, &usage,
name) == 3) {
field_index = index + sensor_inst->input_field_count;
} else
return -EINVAL;
if (!strncmp(name, "value", strlen("value"))) {
u32 report_id;
int ret;
if (kstrtoint(buf, 0, &value) != 0)
return -EINVAL;
report_id = sensor_inst->fields[field_index].attribute.
report_id;
ret = sensor_hub_set_feature(sensor_inst->hsdev, report_id,
index, sizeof(value), &value);
} else
return -EINVAL;
return count;
}
static int hid_sensor_capture_sample(struct hid_sensor_hub_device *hsdev,
unsigned usage_id, size_t raw_len,
char *raw_data, void *priv)
{
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(priv);
struct hid_sensor_sample header;
/* If any error occurs in a sample, rest of the fields are ignored */
if (sensor_inst->input_skip_sample) {
hid_err(sensor_inst->hsdev->hdev, "Skipped remaining data\n");
return 0;
}
hid_dbg(sensor_inst->hsdev->hdev, "%s received %d of %d\n", __func__,
(int) (sensor_inst->input_report_recd_size + raw_len),
sensor_inst->input_report_size);
if (!test_bit(0, &sensor_inst->misc_opened))
return 0;
if (!sensor_inst->input_report_recd_size) {
int required_size = sizeof(struct hid_sensor_sample) +
sensor_inst->input_report_size;
header.usage_id = hsdev->usage;
header.raw_len = sensor_inst->input_report_size;
header.timestamp = ktime_get_real_ns();
if (kfifo_avail(&sensor_inst->data_fifo) >= required_size) {
kfifo_in(&sensor_inst->data_fifo,
(unsigned char *)&header,
sizeof(header));
} else
sensor_inst->input_skip_sample = true;
}
if (kfifo_avail(&sensor_inst->data_fifo) >= raw_len)
kfifo_in(&sensor_inst->data_fifo, (unsigned char *)raw_data,
raw_len);
sensor_inst->input_report_recd_size += raw_len;
return 0;
}
static int hid_sensor_send_event(struct hid_sensor_hub_device *hsdev,
unsigned usage_id, void *priv)
{
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(priv);
if (!test_bit(0, &sensor_inst->misc_opened))
return 0;
sensor_inst->input_report_recd_size = 0;
sensor_inst->input_skip_sample = false;
wake_up(&sensor_inst->wait);
return 0;
}
static int hid_sensor_custom_add_field(struct hid_sensor_custom *sensor_inst,
int index, int report_type,
struct hid_report *report,
struct hid_field *field)
{
struct hid_sensor_custom_field *sensor_field;
void *fields;
fields = krealloc(sensor_inst->fields,
(sensor_inst->sensor_field_count + 1) *
sizeof(struct hid_sensor_custom_field), GFP_KERNEL);
if (!fields) {
kfree(sensor_inst->fields);
return -ENOMEM;
}
sensor_inst->fields = fields;
sensor_field = &sensor_inst->fields[sensor_inst->sensor_field_count];
sensor_field->attribute.usage_id = sensor_inst->hsdev->usage;
if (field->logical)
sensor_field->attribute.attrib_id = field->logical;
else
sensor_field->attribute.attrib_id = field->usage[0].hid;
sensor_field->attribute.index = index;
sensor_field->attribute.report_id = report->id;
sensor_field->attribute.units = field->unit;
sensor_field->attribute.unit_expo = field->unit_exponent;
sensor_field->attribute.size = (field->report_size / 8);
sensor_field->attribute.logical_minimum = field->logical_minimum;
sensor_field->attribute.logical_maximum = field->logical_maximum;
if (report_type == HID_FEATURE_REPORT)
snprintf(sensor_field->group_name,
sizeof(sensor_field->group_name), "feature-%x-%x",
sensor_field->attribute.index,
sensor_field->attribute.attrib_id);
else if (report_type == HID_INPUT_REPORT) {
snprintf(sensor_field->group_name,
sizeof(sensor_field->group_name),
"input-%x-%x", sensor_field->attribute.index,
sensor_field->attribute.attrib_id);
sensor_inst->input_field_count++;
sensor_inst->input_report_size += (field->report_size *
field->report_count) / 8;
}
memset(&sensor_field->hid_custom_attribute_group, 0,
sizeof(struct attribute_group));
sensor_inst->sensor_field_count++;
return 0;
}
static int hid_sensor_custom_add_fields(struct hid_sensor_custom *sensor_inst,
struct hid_report_enum *report_enum,
int report_type)
{
int i;
int ret;
struct hid_report *report;
struct hid_field *field;
struct hid_sensor_hub_device *hsdev = sensor_inst->hsdev;
list_for_each_entry(report, &report_enum->report_list, list) {
for (i = 0; i < report->maxfield; ++i) {
field = report->field[i];
if (field->maxusage &&
((field->usage[0].collection_index >=
hsdev->start_collection_index) &&
(field->usage[0].collection_index <
hsdev->end_collection_index))) {
ret = hid_sensor_custom_add_field(sensor_inst,
i,
report_type,
report,
field);
if (ret)
return ret;
}
}
}
return 0;
}
static int hid_sensor_custom_add_attributes(struct hid_sensor_custom
*sensor_inst)
{
struct hid_sensor_hub_device *hsdev = sensor_inst->hsdev;
struct hid_device *hdev = hsdev->hdev;
int ret = -1;
int i, j;
for (j = 0; j < HID_REPORT_TYPES; ++j) {
if (j == HID_OUTPUT_REPORT)
continue;
ret = hid_sensor_custom_add_fields(sensor_inst,
&hdev->report_enum[j], j);
if (ret)
return ret;
}
/* Create sysfs attributes */
for (i = 0; i < sensor_inst->sensor_field_count; ++i) {
j = 0;
while (j < HID_CUSTOM_TOTAL_ATTRS &&
hid_custom_attrs[j].name) {
struct device_attribute *device_attr;
device_attr = &sensor_inst->fields[i].sd_attrs[j];
snprintf((char *)&sensor_inst->fields[i].attr_name[j],
HID_CUSTOM_NAME_LENGTH, "%s-%s",
sensor_inst->fields[i].group_name,
hid_custom_attrs[j].name);
sysfs_attr_init(&device_attr->attr);
device_attr->attr.name =
(char *)&sensor_inst->fields[i].attr_name[j];
device_attr->attr.mode = hid_custom_attrs[j].mode;
device_attr->show = show_value;
if (hid_custom_attrs[j].mode & S_IWUSR)
device_attr->store = store_value;
sensor_inst->fields[i].attrs[j] = &device_attr->attr;
++j;
}
sensor_inst->fields[i].attrs[j] = NULL;
sensor_inst->fields[i].hid_custom_attribute_group.attrs =
sensor_inst->fields[i].attrs;
sensor_inst->fields[i].hid_custom_attribute_group.name =
sensor_inst->fields[i].group_name;
ret = sysfs_create_group(&sensor_inst->pdev->dev.kobj,
&sensor_inst->fields[i].
hid_custom_attribute_group);
if (ret)
break;
/* For power or report field store indexes */
if (sensor_inst->fields[i].attribute.attrib_id ==
HID_USAGE_SENSOR_PROY_POWER_STATE)
sensor_inst->power_state = &sensor_inst->fields[i];
else if (sensor_inst->fields[i].attribute.attrib_id ==
HID_USAGE_SENSOR_PROP_REPORT_STATE)
sensor_inst->report_state = &sensor_inst->fields[i];
}
return ret;
}
static void hid_sensor_custom_remove_attributes(struct hid_sensor_custom *
sensor_inst)
{
int i;
for (i = 0; i < sensor_inst->sensor_field_count; ++i)
sysfs_remove_group(&sensor_inst->pdev->dev.kobj,
&sensor_inst->fields[i].
hid_custom_attribute_group);
kfree(sensor_inst->fields);
}
static ssize_t hid_sensor_custom_read(struct file *file, char __user *buf,
size_t count, loff_t *f_ps)
{
struct hid_sensor_custom *sensor_inst;
unsigned int copied;
int ret;
sensor_inst = container_of(file->private_data,
struct hid_sensor_custom, custom_dev);
if (count < sizeof(struct hid_sensor_sample))
return -EINVAL;
do {
if (kfifo_is_empty(&sensor_inst->data_fifo)) {
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
ret = wait_event_interruptible(sensor_inst->wait,
!kfifo_is_empty(&sensor_inst->data_fifo));
if (ret)
return ret;
}
ret = kfifo_to_user(&sensor_inst->data_fifo, buf, count,
&copied);
if (ret)
return ret;
} while (copied == 0);
return copied;
}
static int hid_sensor_custom_release(struct inode *inode, struct file *file)
{
struct hid_sensor_custom *sensor_inst;
sensor_inst = container_of(file->private_data,
struct hid_sensor_custom, custom_dev);
clear_bit(0, &sensor_inst->misc_opened);
return 0;
}
static int hid_sensor_custom_open(struct inode *inode, struct file *file)
{
struct hid_sensor_custom *sensor_inst;
sensor_inst = container_of(file->private_data,
struct hid_sensor_custom, custom_dev);
/* We essentially have single reader and writer */
if (test_and_set_bit(0, &sensor_inst->misc_opened))
return -EBUSY;
return nonseekable_open(inode, file);
}
static unsigned int hid_sensor_custom_poll(struct file *file,
struct poll_table_struct *wait)
{
struct hid_sensor_custom *sensor_inst;
unsigned int mask = 0;
sensor_inst = container_of(file->private_data,
struct hid_sensor_custom, custom_dev);
poll_wait(file, &sensor_inst->wait, wait);
if (!kfifo_is_empty(&sensor_inst->data_fifo))
mask = POLLIN | POLLRDNORM;
return mask;
}
static const struct file_operations hid_sensor_custom_fops = {
.open = hid_sensor_custom_open,
.read = hid_sensor_custom_read,
.release = hid_sensor_custom_release,
.poll = hid_sensor_custom_poll,
.llseek = noop_llseek,
};
static int hid_sensor_custom_dev_if_add(struct hid_sensor_custom *sensor_inst)
{
int ret;
ret = kfifo_alloc(&sensor_inst->data_fifo, HID_CUSTOM_FIFO_SIZE,
GFP_KERNEL);
if (ret)
return ret;
init_waitqueue_head(&sensor_inst->wait);
sensor_inst->custom_dev.minor = MISC_DYNAMIC_MINOR;
sensor_inst->custom_dev.name = dev_name(&sensor_inst->pdev->dev);
sensor_inst->custom_dev.fops = &hid_sensor_custom_fops,
ret = misc_register(&sensor_inst->custom_dev);
if (ret) {
kfifo_free(&sensor_inst->data_fifo);
return ret;
}
return 0;
}
static void hid_sensor_custom_dev_if_remove(struct hid_sensor_custom
*sensor_inst)
{
wake_up(&sensor_inst->wait);
misc_deregister(&sensor_inst->custom_dev);
kfifo_free(&sensor_inst->data_fifo);
}
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;
sensor_inst = devm_kzalloc(&pdev->dev, sizeof(*sensor_inst),
GFP_KERNEL);
if (!sensor_inst)
return -ENOMEM;
sensor_inst->callbacks.capture_sample = hid_sensor_capture_sample;
sensor_inst->callbacks.send_event = hid_sensor_send_event;
sensor_inst->callbacks.pdev = pdev;
sensor_inst->hsdev = hsdev;
sensor_inst->pdev = pdev;
mutex_init(&sensor_inst->mutex);
platform_set_drvdata(pdev, sensor_inst);
ret = sensor_hub_register_callback(hsdev, hsdev->usage,
&sensor_inst->callbacks);
if (ret < 0) {
dev_err(&pdev->dev, "callback reg failed\n");
return ret;
}
ret = sysfs_create_group(&sensor_inst->pdev->dev.kobj,
&enable_sensor_attr_group);
if (ret)
goto err_remove_callback;
ret = hid_sensor_custom_add_attributes(sensor_inst);
if (ret)
goto err_remove_group;
ret = hid_sensor_custom_dev_if_add(sensor_inst);
if (ret)
goto err_remove_attributes;
return 0;
err_remove_attributes:
hid_sensor_custom_remove_attributes(sensor_inst);
err_remove_group:
sysfs_remove_group(&sensor_inst->pdev->dev.kobj,
&enable_sensor_attr_group);
err_remove_callback:
sensor_hub_remove_callback(hsdev, hsdev->usage);
return ret;
}
static int hid_sensor_custom_remove(struct platform_device *pdev)
{
struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev);
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
hid_sensor_custom_dev_if_remove(sensor_inst);
hid_sensor_custom_remove_attributes(sensor_inst);
sysfs_remove_group(&sensor_inst->pdev->dev.kobj,
&enable_sensor_attr_group);
sensor_hub_remove_callback(hsdev, hsdev->usage);
return 0;
}
static struct platform_device_id hid_sensor_custom_ids[] = {
{
.name = "HID-SENSOR-2000e1",
},
{
.name = "HID-SENSOR-2000e2",
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, hid_sensor_custom_ids);
static struct platform_driver hid_sensor_custom_platform_driver = {
.id_table = hid_sensor_custom_ids,
.driver = {
.name = KBUILD_MODNAME,
},
.probe = hid_sensor_custom_probe,
.remove = hid_sensor_custom_remove,
};
module_platform_driver(hid_sensor_custom_platform_driver);
MODULE_DESCRIPTION("HID Sensor Custom and Generic sensor Driver");
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_LICENSE("GPL");

View File

@ -28,30 +28,11 @@
#define HID_SENSOR_HUB_ENUM_QUIRK 0x01
/**
* struct sensor_hub_pending - Synchronous read pending information
* @status: Pending status true/false.
* @ready: Completion synchronization data.
* @usage_id: Usage id for physical device, E.g. Gyro usage id.
* @attr_usage_id: Usage Id of a field, E.g. X-AXIS for a gyro.
* @raw_size: Response size for a read request.
* @raw_data: Place holder for received response.
*/
struct sensor_hub_pending {
bool status;
struct completion ready;
u32 usage_id;
u32 attr_usage_id;
int raw_size;
u8 *raw_data;
};
/**
* struct sensor_hub_data - Hold a instance data for a HID hub device
* @hsdev: Stored hid instance for current hub device.
* @mutex: Mutex to serialize synchronous request.
* @lock: Spin lock to protect pending request structure.
* @pending: Holds information of pending sync read request.
* @dyn_callback_list: Holds callback function
* @dyn_callback_lock: spin lock to protect callback list
* @hid_sensor_hub_client_devs: Stores all MFD cells for a hub instance.
@ -61,7 +42,6 @@ struct sensor_hub_pending {
struct sensor_hub_data {
struct mutex mutex;
spinlock_t lock;
struct sensor_hub_pending pending;
struct list_head dyn_callback_list;
spinlock_t dyn_callback_lock;
struct mfd_cell *hid_sensor_hub_client_devs;
@ -106,7 +86,8 @@ static int sensor_hub_get_physical_device_count(struct hid_device *hdev)
for (i = 0; i < hdev->maxcollection; ++i) {
struct hid_collection *collection = &hdev->collection[i];
if (collection->type == HID_COLLECTION_PHYSICAL)
if (collection->type == HID_COLLECTION_PHYSICAL ||
collection->type == HID_COLLECTION_APPLICATION)
++count;
}
@ -139,7 +120,8 @@ static struct hid_sensor_hub_callbacks *sensor_hub_get_callback(
spin_lock_irqsave(&pdata->dyn_callback_lock, flags);
list_for_each_entry(callback, &pdata->dyn_callback_list, list)
if (callback->usage_id == usage_id &&
if ((callback->usage_id == usage_id ||
callback->usage_id == HID_USAGE_SENSOR_COLLECTION) &&
(collection_index >=
callback->hsdev->start_collection_index) &&
(collection_index <
@ -179,7 +161,18 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
callback->usage_callback = usage_callback;
callback->usage_id = usage_id;
callback->priv = NULL;
list_add_tail(&callback->list, &pdata->dyn_callback_list);
/*
* If there is a handler registered for the collection type, then
* it will handle all reports for sensors in this collection. If
* there is also an individual sensor handler registration, then
* we want to make sure that the reports are directed to collection
* handler, as this may be a fusion sensor. So add collection handlers
* to the beginning of the list, so that they are matched first.
*/
if (usage_id == HID_USAGE_SENSOR_COLLECTION)
list_add(&callback->list, &pdata->dyn_callback_list);
else
list_add_tail(&callback->list, &pdata->dyn_callback_list);
spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);
return 0;
@ -208,10 +201,14 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
EXPORT_SYMBOL_GPL(sensor_hub_remove_callback);
int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
u32 field_index, s32 value)
u32 field_index, int buffer_size, void *buffer)
{
struct hid_report *report;
struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
__s32 *buf32 = buffer;
int i = 0;
int remaining_bytes;
__s32 value;
int ret = 0;
mutex_lock(&data->mutex);
@ -220,7 +217,21 @@ int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
ret = -EINVAL;
goto done_proc;
}
hid_set_field(report->field[field_index], 0, value);
remaining_bytes = do_div(buffer_size, sizeof(__s32));
if (buffer_size) {
for (i = 0; i < buffer_size; ++i) {
hid_set_field(report->field[field_index], i,
(__force __s32)cpu_to_le32(*buf32));
++buf32;
}
}
if (remaining_bytes) {
value = 0;
memcpy(&value, (u8 *)buf32, remaining_bytes);
hid_set_field(report->field[field_index], i,
(__force __s32)cpu_to_le32(value));
}
hid_hw_request(hsdev->hdev, report, HID_REQ_SET_REPORT);
hid_hw_wait(hsdev->hdev);
@ -232,10 +243,11 @@ int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
EXPORT_SYMBOL_GPL(sensor_hub_set_feature);
int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
u32 field_index, s32 *value)
u32 field_index, int buffer_size, void *buffer)
{
struct hid_report *report;
struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
int report_size;
int ret = 0;
mutex_lock(&data->mutex);
@ -247,7 +259,17 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
}
hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT);
hid_hw_wait(hsdev->hdev);
*value = report->field[field_index]->value[0];
/* calculate number of bytes required to read this field */
report_size = DIV_ROUND_UP(report->field[field_index]->report_size,
8) *
report->field[field_index]->report_count;
if (!report_size) {
ret = -EINVAL;
goto done_proc;
}
ret = min(report_size, buffer_size);
memcpy(buffer, report->field[field_index]->value, ret);
done_proc:
mutex_unlock(&data->mutex);
@ -259,47 +281,54 @@ EXPORT_SYMBOL_GPL(sensor_hub_get_feature);
int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
u32 usage_id,
u32 attr_usage_id, u32 report_id)
u32 attr_usage_id, u32 report_id,
enum sensor_hub_read_flags flag)
{
struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
unsigned long flags;
struct hid_report *report;
int ret_val = 0;
mutex_lock(&data->mutex);
memset(&data->pending, 0, sizeof(data->pending));
init_completion(&data->pending.ready);
data->pending.usage_id = usage_id;
data->pending.attr_usage_id = attr_usage_id;
data->pending.raw_size = 0;
spin_lock_irqsave(&data->lock, flags);
data->pending.status = true;
spin_unlock_irqrestore(&data->lock, flags);
report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT);
report = sensor_hub_report(report_id, hsdev->hdev,
HID_INPUT_REPORT);
if (!report)
goto err_free;
return -EINVAL;
hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT);
wait_for_completion_interruptible_timeout(&data->pending.ready, HZ*5);
switch (data->pending.raw_size) {
case 1:
ret_val = *(u8 *)data->pending.raw_data;
break;
case 2:
ret_val = *(u16 *)data->pending.raw_data;
break;
case 4:
ret_val = *(u32 *)data->pending.raw_data;
break;
default:
ret_val = 0;
mutex_lock(&hsdev->mutex);
if (flag == SENSOR_HUB_SYNC) {
memset(&hsdev->pending, 0, sizeof(hsdev->pending));
init_completion(&hsdev->pending.ready);
hsdev->pending.usage_id = usage_id;
hsdev->pending.attr_usage_id = attr_usage_id;
hsdev->pending.raw_size = 0;
spin_lock_irqsave(&data->lock, flags);
hsdev->pending.status = true;
spin_unlock_irqrestore(&data->lock, flags);
}
kfree(data->pending.raw_data);
err_free:
data->pending.status = false;
mutex_lock(&data->mutex);
hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT);
mutex_unlock(&data->mutex);
if (flag == SENSOR_HUB_SYNC) {
wait_for_completion_interruptible_timeout(
&hsdev->pending.ready, HZ*5);
switch (hsdev->pending.raw_size) {
case 1:
ret_val = *(u8 *)hsdev->pending.raw_data;
break;
case 2:
ret_val = *(u16 *)hsdev->pending.raw_data;
break;
case 4:
ret_val = *(u32 *)hsdev->pending.raw_data;
break;
default:
ret_val = 0;
}
kfree(hsdev->pending.raw_data);
hsdev->pending.status = false;
}
mutex_unlock(&hsdev->mutex);
return ret_val;
}
@ -455,16 +484,6 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
report->field[i]->report_count)/8);
sz = (report->field[i]->report_size *
report->field[i]->report_count)/8;
if (pdata->pending.status && pdata->pending.attr_usage_id ==
report->field[i]->usage->hid) {
hid_dbg(hdev, "data was pending ...\n");
pdata->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC);
if (pdata->pending.raw_data)
pdata->pending.raw_size = sz;
else
pdata->pending.raw_size = 0;
complete(&pdata->pending.ready);
}
collection = &hdev->collection[
report->field[i]->usage->collection_index];
hid_dbg(hdev, "collection->usage %x\n",
@ -474,8 +493,23 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
report->field[i]->physical,
report->field[i]->usage[0].collection_index,
&hsdev, &priv);
if (callback && callback->capture_sample) {
if (!callback) {
ptr += sz;
continue;
}
if (hsdev->pending.status && (hsdev->pending.attr_usage_id ==
report->field[i]->usage->hid ||
hsdev->pending.attr_usage_id ==
report->field[i]->logical)) {
hid_dbg(hdev, "data was pending ...\n");
hsdev->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC);
if (hsdev->pending.raw_data)
hsdev->pending.raw_size = sz;
else
hsdev->pending.raw_size = 0;
complete(&hsdev->pending.ready);
}
if (callback->capture_sample) {
if (report->field[i]->logical)
callback->capture_sample(hsdev,
report->field[i]->logical, sz, ptr,
@ -572,6 +606,7 @@ static int sensor_hub_probe(struct hid_device *hdev,
int dev_cnt;
struct hid_sensor_hub_device *hsdev;
struct hid_sensor_hub_device *last_hsdev = NULL;
struct hid_sensor_hub_device *collection_hsdev = NULL;
sd = devm_kzalloc(&hdev->dev, sizeof(*sd), GFP_KERNEL);
if (!sd) {
@ -618,7 +653,8 @@ static int sensor_hub_probe(struct hid_device *hdev,
for (i = 0; i < hdev->maxcollection; ++i) {
struct hid_collection *collection = &hdev->collection[i];
if (collection->type == HID_COLLECTION_PHYSICAL) {
if (collection->type == HID_COLLECTION_PHYSICAL ||
collection->type == HID_COLLECTION_APPLICATION) {
hsdev = devm_kzalloc(&hdev->dev, sizeof(*hsdev),
GFP_KERNEL);
@ -630,6 +666,8 @@ static int sensor_hub_probe(struct hid_device *hdev,
hsdev->hdev = hdev;
hsdev->vendor_id = hdev->vendor;
hsdev->product_id = hdev->product;
hsdev->usage = collection->usage;
mutex_init(&hsdev->mutex);
hsdev->start_collection_index = i;
if (last_hsdev)
last_hsdev->end_collection_index = i;
@ -653,10 +691,17 @@ static int sensor_hub_probe(struct hid_device *hdev,
hid_dbg(hdev, "Adding %s:%d\n", name,
hsdev->start_collection_index);
sd->hid_sensor_client_cnt++;
if (collection_hsdev)
collection_hsdev->end_collection_index = i;
if (collection->type == HID_COLLECTION_APPLICATION &&
collection->usage == HID_USAGE_SENSOR_COLLECTION)
collection_hsdev = hsdev;
}
}
if (last_hsdev)
last_hsdev->end_collection_index = i;
if (collection_hsdev)
collection_hsdev->end_collection_index = i;
ret = mfd_add_hotplug_devices(&hdev->dev,
sd->hid_sensor_hub_client_devs,
@ -676,13 +721,18 @@ static void sensor_hub_remove(struct hid_device *hdev)
{
struct sensor_hub_data *data = hid_get_drvdata(hdev);
unsigned long flags;
int i;
hid_dbg(hdev, " hardware removed\n");
hid_hw_close(hdev);
hid_hw_stop(hdev);
spin_lock_irqsave(&data->lock, flags);
if (data->pending.status)
complete(&data->pending.ready);
for (i = 0; i < data->hid_sensor_client_cnt; ++i) {
struct hid_sensor_hub_device *hsdev =
data->hid_sensor_hub_client_devs[i].platform_data;
if (hsdev->pending.status)
complete(&hsdev->pending.ready);
}
spin_unlock_irqrestore(&data->lock, flags);
mfd_remove_devices(&hdev->dev);
hid_set_drvdata(hdev, NULL);

View File

@ -123,7 +123,8 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev,
*val = sensor_hub_input_attr_get_raw_value(
accel_state->common_attributes.hsdev,
HID_USAGE_SENSOR_ACCEL_3D, address,
report_id);
report_id,
SENSOR_HUB_SYNC);
else {
*val = 0;
hid_sensor_power_state(&accel_state->common_attributes,

View File

@ -153,8 +153,8 @@ s32 hid_sensor_read_poll_value(struct hid_sensor_common *st)
int ret;
ret = sensor_hub_get_feature(st->hsdev,
st->poll.report_id,
st->poll.index, &value);
st->poll.report_id,
st->poll.index, sizeof(value), &value);
if (ret < 0 || value < 0) {
return -EINVAL;
@ -174,8 +174,8 @@ int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st,
int ret;
ret = sensor_hub_get_feature(st->hsdev,
st->poll.report_id,
st->poll.index, &value);
st->poll.report_id,
st->poll.index, sizeof(value), &value);
if (ret < 0 || value < 0) {
*val1 = *val2 = 0;
return -EINVAL;
@ -212,9 +212,8 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,
else
value = 0;
}
ret = sensor_hub_set_feature(st->hsdev,
st->poll.report_id,
st->poll.index, value);
ret = sensor_hub_set_feature(st->hsdev, st->poll.report_id,
st->poll.index, sizeof(value), &value);
if (ret < 0 || value < 0)
ret = -EINVAL;
@ -229,8 +228,9 @@ int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st,
int ret;
ret = sensor_hub_get_feature(st->hsdev,
st->sensitivity.report_id,
st->sensitivity.index, &value);
st->sensitivity.report_id,
st->sensitivity.index, sizeof(value),
&value);
if (ret < 0 || value < 0) {
*val1 = *val2 = 0;
return -EINVAL;
@ -253,9 +253,9 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st,
value = convert_to_vtf_format(st->sensitivity.size,
st->sensitivity.unit_expo,
val1, val2);
ret = sensor_hub_set_feature(st->hsdev,
st->sensitivity.report_id,
st->sensitivity.index, value);
ret = sensor_hub_set_feature(st->hsdev, st->sensitivity.report_id,
st->sensitivity.index, sizeof(value),
&value);
if (ret < 0 || value < 0)
ret = -EINVAL;

View File

@ -68,20 +68,21 @@ static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state)
if (state_val >= 0) {
state_val += st->power_state.logical_minimum;
sensor_hub_set_feature(st->hsdev, st->power_state.report_id,
st->power_state.index,
(s32)state_val);
st->power_state.index, sizeof(state_val),
&state_val);
}
if (report_val >= 0) {
report_val += st->report_state.logical_minimum;
sensor_hub_set_feature(st->hsdev, st->report_state.report_id,
st->report_state.index,
(s32)report_val);
st->report_state.index,
sizeof(report_val),
&report_val);
}
sensor_hub_get_feature(st->hsdev, st->power_state.report_id,
st->power_state.index,
&state_val);
st->power_state.index,
sizeof(state_val), &state_val);
if (state && poll_value)
msleep_interruptible(poll_value * 2);

View File

@ -123,7 +123,8 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev,
*val = sensor_hub_input_attr_get_raw_value(
gyro_state->common_attributes.hsdev,
HID_USAGE_SENSOR_GYRO_3D, address,
report_id);
report_id,
SENSOR_HUB_SYNC);
else {
*val = 0;
hid_sensor_power_state(&gyro_state->common_attributes,

View File

@ -101,7 +101,8 @@ static int als_read_raw(struct iio_dev *indio_dev,
*val = sensor_hub_input_attr_get_raw_value(
als_state->common_attributes.hsdev,
HID_USAGE_SENSOR_ALS, address,
report_id);
report_id,
SENSOR_HUB_SYNC);
hid_sensor_power_state(&als_state->common_attributes,
false);
} else {

View File

@ -96,7 +96,8 @@ static int prox_read_raw(struct iio_dev *indio_dev,
*val = sensor_hub_input_attr_get_raw_value(
prox_state->common_attributes.hsdev,
HID_USAGE_SENSOR_PROX, address,
report_id);
report_id,
SENSOR_HUB_SYNC);
hid_sensor_power_state(&prox_state->common_attributes,
false);
} else {

View File

@ -170,7 +170,8 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev,
*val = sensor_hub_input_attr_get_raw_value(
magn_state->common_attributes.hsdev,
HID_USAGE_SENSOR_COMPASS_3D, address,
report_id);
report_id,
SENSOR_HUB_SYNC);
else {
*val = 0;
hid_sensor_power_state(&magn_state->common_attributes,

View File

@ -124,7 +124,8 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev,
*val = sensor_hub_input_attr_get_raw_value(
incl_state->common_attributes.hsdev,
HID_USAGE_SENSOR_INCLINOMETER_3D, address,
report_id);
report_id,
SENSOR_HUB_SYNC);
else {
hid_sensor_power_state(&incl_state->common_attributes,
false);

View File

@ -100,7 +100,8 @@ static int press_read_raw(struct iio_dev *indio_dev,
*val = sensor_hub_input_attr_get_raw_value(
press_state->common_attributes.hsdev,
HID_USAGE_SENSOR_PRESSURE, address,
report_id);
report_id,
SENSOR_HUB_SYNC);
hid_sensor_power_state(&press_state->common_attributes,
false);
} else {

View File

@ -213,7 +213,7 @@ static int hid_rtc_read_time(struct device *dev, struct rtc_time *tm)
/* get a report with all values through requesting one value */
sensor_hub_input_attr_get_raw_value(time_state->common_attributes.hsdev,
HID_USAGE_SENSOR_TIME, hid_time_addresses[0],
time_state->info[0].report_id);
time_state->info[0].report_id, SENSOR_HUB_SYNC);
/* wait for all values (event) */
ret = wait_for_completion_killable_timeout(
&time_state->comp_last_time, HZ*6);

View File

@ -48,20 +48,44 @@ struct hid_sensor_hub_attribute_info {
s32 logical_maximum;
};
/**
* struct sensor_hub_pending - Synchronous read pending information
* @status: Pending status true/false.
* @ready: Completion synchronization data.
* @usage_id: Usage id for physical device, E.g. Gyro usage id.
* @attr_usage_id: Usage Id of a field, E.g. X-AXIS for a gyro.
* @raw_size: Response size for a read request.
* @raw_data: Place holder for received response.
*/
struct sensor_hub_pending {
bool status;
struct completion ready;
u32 usage_id;
u32 attr_usage_id;
int raw_size;
u8 *raw_data;
};
/**
* struct hid_sensor_hub_device - Stores the hub instance data
* @hdev: Stores the hid instance.
* @vendor_id: Vendor id of hub device.
* @product_id: Product id of hub device.
* @usage: Usage id for this hub device instance.
* @start_collection_index: Starting index for a phy type collection
* @end_collection_index: Last index for a phy type collection
* @mutex: synchronizing mutex.
* @pending: Holds information of pending sync read request.
*/
struct hid_sensor_hub_device {
struct hid_device *hdev;
u32 vendor_id;
u32 product_id;
u32 usage;
int start_collection_index;
int end_collection_index;
struct mutex mutex;
struct sensor_hub_pending pending;
};
/**
@ -152,40 +176,51 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
* @usage_id: Attribute usage id of parent physical device as per spec
* @attr_usage_id: Attribute usage id as per spec
* @report_id: Report id to look for
* @flag: Synchronous or asynchronous read
*
* Issues a synchronous read request for an input attribute. Returns
* data upto 32 bits. Since client can get events, so this call should
* not be used for data paths, this will impact performance.
* Issues a synchronous or asynchronous read request for an input attribute.
* Returns data upto 32 bits.
*/
enum sensor_hub_read_flags {
SENSOR_HUB_SYNC,
SENSOR_HUB_ASYNC,
};
int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
u32 usage_id,
u32 attr_usage_id, u32 report_id);
u32 usage_id,
u32 attr_usage_id, u32 report_id,
enum sensor_hub_read_flags flag
);
/**
* sensor_hub_set_feature() - Feature set request
* @hsdev: Hub device instance.
* @report_id: Report id to look for
* @field_index: Field index inside a report
* @value: Value to set
* @buffer_size: size of the buffer
* @buffer: buffer to use in the feature set
*
* Used to set a field in feature report. For example this can set polling
* interval, sensitivity, activate/deactivate state.
*/
int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
u32 field_index, s32 value);
u32 field_index, int buffer_size, void *buffer);
/**
* sensor_hub_get_feature() - Feature get request
* @hsdev: Hub device instance.
* @report_id: Report id to look for
* @field_index: Field index inside a report
* @value: Place holder for return value
* @buffer_size: size of the buffer
* @buffer: buffer to copy output
*
* Used to get a field in feature report. For example this can get polling
* interval, sensitivity, activate/deactivate state.
* interval, sensitivity, activate/deactivate state. On success it returns
* number of bytes copied to buffer. On failure, it returns value < 0.
*/
int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
u32 field_index, s32 *value);
u32 field_index, int buffer_size, void *buffer);
/* hid-sensor-attributes */

View File

@ -21,6 +21,8 @@
#define HID_MAX_PHY_DEVICES 0xFF
#define HID_USAGE_SENSOR_COLLECTION 0x200001
/* Accel 3D (200073) */
#define HID_USAGE_SENSOR_ACCEL_3D 0x200073
#define HID_USAGE_SENSOR_DATA_ACCELERATION 0x200452