HID: hid-sensor-hub: Extend API for async reads
[linux-block.git] / drivers / hid / hid-sensor-hub.c
index 6a58b6c723aa215408051e2b3c967205569a7b94..0a916236316413c074da93f0a6d42e58f07c7360 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;
        }
 
@@ -138,7 +119,8 @@ static struct hid_sensor_hub_callbacks *sensor_hub_get_callback(
 
        spin_lock(&pdata->dyn_callback_lock);
        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 <
@@ -177,7 +159,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;
@@ -257,47 +250,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;
 }
@@ -453,16 +453,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",
@@ -472,8 +462,21 @@ 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) {
+                       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,
@@ -570,6 +573,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) {
@@ -616,7 +620,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);
@@ -628,6 +633,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;
@@ -651,10 +658,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,
@@ -674,13 +688,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);