]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
HID: sensor-hub: Add sensor_hub_input_attr_read_values() for multi-byte reads
authorSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Wed, 10 Jun 2026 08:29:09 +0000 (16:29 +0800)
committerJonathan Cameron <jic23@kernel.org>
Mon, 29 Jun 2026 22:24:46 +0000 (23:24 +0100)
sensor_hub_input_attr_get_raw_value() is limited to returning a single
32-bit value, which is insufficient for sensors that report data larger
than 32 bits, such as a quaternion with four s16 elements.

Add sensor_hub_input_attr_read_values() that accepts a caller-provided
buffer and accumulates incoming data until the buffer is full. The two
paths are distinguished in sensor_hub_raw_event() by pending.max_raw_size
being non-zero, preserving backward compatibility.

Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Co-developed-by: Zhang Lixu <lixu.zhang@intel.com>
Signed-off-by: Zhang Lixu <lixu.zhang@intel.com>
Acked-by: Jiri Kosina <jkosina@suse.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
Cc: <Stable@vger.kernel.org>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
drivers/hid/hid-sensor-hub.c
include/linux/hid-sensor-hub.h

index 90666ff629defa97590ca411090bf996861304fa..34f710c465b80a0c46cc207e3d99a07c5767f291 100644 (file)
@@ -286,6 +286,54 @@ done_proc:
 }
 EXPORT_SYMBOL_GPL(sensor_hub_get_feature);
 
+int sensor_hub_input_attr_read_values(struct hid_sensor_hub_device *hsdev,
+                                     u32 usage_id, u32 attr_usage_id,
+                                     u32 report_id,
+                                     enum sensor_hub_read_flags flag,
+                                     u32 buffer_size, u8 *buffer)
+{
+       struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
+       struct hid_report *report;
+       unsigned long flags;
+       long cycles;
+       int ret;
+
+       report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT);
+       if (!report)
+               return -EINVAL;
+
+       mutex_lock(hsdev->mutex_ptr);
+       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.max_raw_size = buffer_size;
+               hsdev->pending.raw_data = buffer;
+
+               spin_lock_irqsave(&data->lock, flags);
+               hsdev->pending.status = true;
+               spin_unlock_irqrestore(&data->lock, flags);
+       }
+       mutex_lock(&data->mutex);
+       hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT);
+       mutex_unlock(&data->mutex);
+       ret = 0;
+       if (flag == SENSOR_HUB_SYNC) {
+               cycles = wait_for_completion_interruptible_timeout(&hsdev->pending.ready,
+                                                                  HZ * 5);
+               if (cycles == 0)
+                       ret = -ETIMEDOUT;
+               else if (cycles < 0)
+                       ret = cycles;
+
+               hsdev->pending.status = false;
+       }
+       mutex_unlock(hsdev->mutex_ptr);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sensor_hub_input_attr_read_values);
 
 int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
                                        u32 usage_id,
@@ -478,6 +526,8 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
        struct hid_collection *collection = NULL;
        void *priv = NULL;
        struct hid_sensor_hub_device *hsdev = NULL;
+       u32 copy_size;
+       u32 avail;
 
        hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n",
                         report->id, size, report->type);
@@ -518,12 +568,27 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
                                              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 (hsdev->pending.max_raw_size) {
+                               if (hsdev->pending.index < hsdev->pending.max_raw_size) {
+                                       avail = hsdev->pending.max_raw_size - hsdev->pending.index;
+                                       copy_size = clamp(sz, 0U, avail);
+
+                                       memcpy(hsdev->pending.raw_data + hsdev->pending.index,
+                                              ptr, copy_size);
+                                       hsdev->pending.index += copy_size;
+                                       if (hsdev->pending.index >= hsdev->pending.max_raw_size) {
+                                               hsdev->pending.raw_size = hsdev->pending.index;
+                                               complete(&hsdev->pending.ready);
+                                       }
+                               }
+                       } else {
+                               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)
index e71056553108508c9ac51172f989a2d54872085b..ab5cc8db3fbb3b81071fe72f9ae3467975fe06ab 100644 (file)
@@ -43,6 +43,8 @@ struct hid_sensor_hub_attribute_info {
  * @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.
+ * @index:             Current write index into raw_data for multi-byte reads.
+ * @max_raw_size:      Total buffer size for multi-byte reads; 0 for single-value reads.
  */
 struct sensor_hub_pending {
        bool status;
@@ -51,6 +53,8 @@ struct sensor_hub_pending {
        u32 attr_usage_id;
        int raw_size;
        u8  *raw_data;
+       u32 index;
+       u32 max_raw_size;
 };
 
 /**
@@ -183,6 +187,27 @@ int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
                                        bool is_signed
 );
 
+/**
+ * sensor_hub_input_attr_read_values() - Synchronous multi-byte read request
+ * @hsdev:             Hub device instance.
+ * @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
+ * @buffer_size:       Size of the buffer in bytes
+ * @buffer:            Buffer to store the read data
+ *
+ * Issues a synchronous or asynchronous read request for an input attribute,
+ * accumulating data into the provided buffer until it is full.
+ * Return: 0 on success, -ETIMEDOUT if the device did not respond, or a
+ * negative error code.
+ */
+int sensor_hub_input_attr_read_values(struct hid_sensor_hub_device *hsdev,
+                                     u32 usage_id, u32 attr_usage_id,
+                                     u32 report_id,
+                                     enum sensor_hub_read_flags flag,
+                                     u32 buffer_size, u8 *buffer);
+
 /**
 * sensor_hub_set_feature() - Feature set request
 * @hsdev:      Hub device instance.