HID: sensor-hub: Add sensor_hub_input_attr_read_values() for multi-byte reads

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>
This commit is contained in:
Srinivas Pandruvada
2026-06-10 16:29:09 +08:00
committed by Jonathan Cameron
parent 3ee2128b6f
commit f784fcea45
2 changed files with 96 additions and 6 deletions

View File

@@ -286,6 +286,54 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
}
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)

View 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.