Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
[firefly-linux-kernel-4.4.55.git] / drivers / hid / hid-sensor-hub.c
index e54ce1097e2cc57f5049cf852016087a32534c47..c3f6f1e311ea0d98da6981669e292e085552a927 100644 (file)
 
 #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 @@ done_proc:
 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;
-
-       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;
+               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);
+               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);