]> exis.tech > repos - linux.git/blobdiff - drivers/hid/hid-sensor-hub.c
Merge tag 'hwmon-for-v7.2-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/groec...
[linux.git] / drivers / hid / hid-sensor-hub.c
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)