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

Commit 2e455c27 authored by Jiri Kosina's avatar Jiri Kosina
Browse files

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
parents 05f6d025 b2eafd72
Loading
Loading
Loading
Loading
+84 −0
Original line number Diff line number Diff line
@@ -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.
+15 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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/
+849 −0

File added.

Preview size limit exceeded, changes collapsed.

+124 −74
Original line number Diff line number Diff line
@@ -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,6 +161,17 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
	callback->usage_callback = usage_callback;
	callback->usage_id = usage_id;
	callback->priv = NULL;
	/*
	 * 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);

@@ -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;
	report = sensor_hub_report(report_id, hsdev->hdev,
				   HID_INPUT_REPORT);
	if (!report)
		return -EINVAL;

	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);
	data->pending.status = true;
		hsdev->pending.status = true;
		spin_unlock_irqrestore(&data->lock, flags);
	report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT);
	if (!report)
		goto err_free;

	}
	mutex_lock(&data->mutex);
	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) {
	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 *)data->pending.raw_data;
			ret_val = *(u8 *)hsdev->pending.raw_data;
			break;
		case 2:
		ret_val = *(u16 *)data->pending.raw_data;
			ret_val = *(u16 *)hsdev->pending.raw_data;
			break;
		case 4:
		ret_val = *(u32 *)data->pending.raw_data;
			ret_val = *(u32 *)hsdev->pending.raw_data;
			break;
		default:
			ret_val = 0;
		}
	kfree(data->pending.raw_data);

err_free:
	data->pending.status = false;
	mutex_unlock(&data->mutex);
		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);
Loading