]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'for-4.1/sensor-hub' into for-linus
authorJiri Kosina <jkosina@suse.cz>
Mon, 13 Apr 2015 21:43:34 +0000 (23:43 +0200)
committerJiri Kosina <jkosina@suse.cz>
Mon, 13 Apr 2015 21:43:34 +0000 (23:43 +0200)
Conflicts:
drivers/iio/common/hid-sensors/hid-sensor-trigger.c
include/linux/hid-sensor-hub.h

12 files changed:
1  2 
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-sensor-hub.c
drivers/iio/accel/hid-sensor-accel-3d.c
drivers/iio/common/hid-sensors/hid-sensor-trigger.c
drivers/iio/gyro/hid-sensor-gyro-3d.c
drivers/iio/light/hid-sensor-als.c
drivers/iio/light/hid-sensor-prox.c
drivers/iio/magnetometer/hid-sensor-magn-3d.c
drivers/iio/orientation/hid-sensor-incl-3d.c
drivers/iio/pressure/hid-sensor-press.c
include/linux/hid-sensor-hub.h

diff --combined drivers/hid/Kconfig
index 15901065727fbd1da90a84b8d4986e7e9dd693ff,7de0e9ed98f37d4ce57fa41ed48828a50d2890c7..15338afdf7f9ab71c84a15dc3143c9dda11fa832
@@@ -92,7 -92,7 +92,7 @@@ menu "Special HID drivers
        depends on HID
  
  config HID_A4TECH
 -      tristate "A4 tech mice" if EXPERT
 +      tristate "A4 tech mice"
        depends on HID
        default !EXPERT
        ---help---
@@@ -113,7 -113,7 +113,7 @@@ config HID_ACRUX_F
        game controllers.
  
  config HID_APPLE
 -      tristate "Apple {i,Power,Mac}Books" if EXPERT
 +      tristate "Apple {i,Power,Mac}Books"
        depends on HID
        default !EXPERT
        ---help---
@@@ -141,7 -141,7 +141,7 @@@ config HID_AUREA
        Support for Aureal Cy se W-01RN Remote Controller and other Aureal derived remotes.
  
  config HID_BELKIN
 -      tristate "Belkin Flip KVM and Wireless keyboard" if EXPERT
 +      tristate "Belkin Flip KVM and Wireless keyboard"
        depends on HID
        default !EXPERT
        ---help---
@@@ -158,14 -158,14 +158,14 @@@ config HID_BETOP_F
         - BETOP 2185 PC & BFM MODE
  
  config HID_CHERRY
 -      tristate "Cherry Cymotion keyboard" if EXPERT
 +      tristate "Cherry Cymotion keyboard"
        depends on HID
        default !EXPERT
        ---help---
        Support for Cherry Cymotion keyboard.
  
  config HID_CHICONY
 -      tristate "Chicony Tactical pad" if EXPERT
 +      tristate "Chicony Tactical pad"
        depends on HID
        default !EXPERT
        ---help---
@@@ -196,7 -196,7 +196,7 @@@ config HID_CP211
        customizable USB descriptor fields are exposed as sysfs attributes.
  
  config HID_CYPRESS
 -      tristate "Cypress mouse and barcode readers" if EXPERT
 +      tristate "Cypress mouse and barcode readers"
        depends on HID
        default !EXPERT
        ---help---
@@@ -245,7 -245,7 +245,7 @@@ config HID_EL
        different devices than those handled by CONFIG_TOUCHSCREEN_USB_ELO.
  
  config HID_EZKEY
 -      tristate "Ezkey BTC 8193 keyboard" if EXPERT
 +      tristate "Ezkey BTC 8193 keyboard"
        depends on HID
        default !EXPERT
        ---help---
@@@ -286,6 -286,12 +286,6 @@@ config HID_GT683
        Currently the following devices are know to be supported:
          - MSI GT683R
  
 -config HID_HUION
 -      tristate "Huion tablets"
 -      depends on USB_HID
 -      ---help---
 -      Support for Huion 580 tablet.
 -
  config HID_KEYTOUCH
        tristate "Keytouch HID devices"
        depends on HID
@@@ -306,9 -312,9 +306,9 @@@ config HID_KY
  
  config HID_UCLOGIC
        tristate "UC-Logic"
 -      depends on HID
 +      depends on USB_HID
        ---help---
 -      Support for UC-Logic tablets.
 +      Support for UC-Logic and Huion tablets.
  
  config HID_WALTOP
        tristate "Waltop"
@@@ -338,7 -344,7 +338,7 @@@ config HID_TWINHA
        Support for Twinhan IR remote control.
  
  config HID_KENSINGTON
 -      tristate "Kensington Slimblade Trackball" if EXPERT
 +      tristate "Kensington Slimblade Trackball"
        depends on HID
        default !EXPERT
        ---help---
@@@ -366,7 -372,7 +366,7 @@@ config HID_LENOV
        - ThinkPad Compact USB Keyboard with TrackPoint (supports Fn keys)
  
  config HID_LOGITECH
 -      tristate "Logitech devices" if EXPERT
 +      tristate "Logitech devices"
        depends on HID
        default !EXPERT
        ---help---
@@@ -455,14 -461,14 +455,14 @@@ config HID_MAGICMOUS
        Apple Wireless "Magic" Mouse and the Apple Wireless "Magic" Trackpad.
  
  config HID_MICROSOFT
 -      tristate "Microsoft non-fully HID-compliant devices" if EXPERT
 +      tristate "Microsoft non-fully HID-compliant devices"
        depends on HID
        default !EXPERT
        ---help---
        Support for Microsoft devices that are not fully compliant with HID standard.
  
  config HID_MONTEREY
 -      tristate "Monterey Genius KB29E keyboard" if EXPERT
 +      tristate "Monterey Genius KB29E keyboard"
        depends on HID
        default !EXPERT
        ---help---
@@@ -632,6 -638,7 +632,6 @@@ config HID_PICOLCD_CI
  
  config HID_PLANTRONICS
        tristate "Plantronics USB HID Driver"
 -      default !EXPERT
        depends on HID
        ---help---
        Provides HID support for Plantronics telephony devices.
@@@ -878,6 -885,21 +878,21 @@@ config HID_SENSOR_HU
          for events and handle data streams. Each sensor driver can format
          data and present to user mode using input or IIO interface.
  
+ config HID_SENSOR_CUSTOM_SENSOR
+       tristate "HID Sensors hub custom sensor support"
+       depends on HID_SENSOR_HUB
+       default n
+       ---help---
+         HID Sensor hub specification allows definition of some custom and
+         generic sensors. Unlike other HID sensors, they can't be exported
+         via Linux IIO because of custom fields. This is up to the manufacturer
+         to decide how to interpret these special sensor ids and process in
+         the user space. Currently some manufacturers are using these ids for
+         sensor calibration and debugging other sensors. Manufacturers
+         should't use these special custom sensor ids to export any of the
+         standard sensors.
+         Select this config option for custom/generic sensor support.
  endmenu
  
  endif # HID
diff --combined drivers/hid/Makefile
index 9c399fe394fa9cea422a450d24560e441c75d1c1,c90ce7b900b60e12351fce0210204814e0f24e0e..e4a21dfd7ef3011506c9e4e50eb483480e05c5cd
@@@ -41,6 -41,7 +41,6 @@@ obj-$(CONFIG_HID_GYRATION)    += hid-gyrat
  obj-$(CONFIG_HID_HOLTEK)      += hid-holtek-kbd.o
  obj-$(CONFIG_HID_HOLTEK)      += hid-holtek-mouse.o
  obj-$(CONFIG_HID_HOLTEK)      += hid-holtekff.o
 -obj-$(CONFIG_HID_HUION)               += hid-huion.o
  obj-$(CONFIG_HID_HYPERV_MOUSE)        += hid-hyperv.o
  obj-$(CONFIG_HID_ICADE)               += hid-icade.o
  obj-$(CONFIG_HID_KENSINGTON)  += hid-kensington.o
@@@ -100,6 -101,7 +100,7 @@@ obj-$(CONFIG_HID_WACOM)            += wacom.
  obj-$(CONFIG_HID_WALTOP)      += hid-waltop.o
  obj-$(CONFIG_HID_WIIMOTE)     += hid-wiimote.o
  obj-$(CONFIG_HID_SENSOR_HUB)  += hid-sensor-hub.o
+ obj-$(CONFIG_HID_SENSOR_CUSTOM_SENSOR)        += hid-sensor-custom.o
  
  obj-$(CONFIG_USB_HID)         += usbhid/
  obj-$(CONFIG_USB_MOUSE)               += usbhid/
index e54ce1097e2cc57f5049cf852016087a32534c47,ecdcb5ac91f92ae97386931a9e4a6361238544e2..c3f6f1e311ea0d98da6981669e292e085552a927
  
  #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 +42,6 @@@
  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 +86,8 @@@ static int sensor_hub_get_physical_devi
  
        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;
        }
  
@@@ -135,22 -116,21 +116,23 @@@ static struct hid_sensor_hub_callbacks 
  {
        struct hid_sensor_hub_callbacks_list *callback;
        struct sensor_hub_data *pdata = hid_get_drvdata(hdev);
 +      unsigned long flags;
  
 -      spin_lock(&pdata->dyn_callback_lock);
 +      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 <
                                callback->hsdev->end_collection_index)) {
                        *priv = callback->priv;
                        *hsdev = callback->hsdev;
 -                      spin_unlock(&pdata->dyn_callback_lock);
 +                      spin_unlock_irqrestore(&pdata->dyn_callback_lock,
 +                                             flags);
                        return callback->usage_callback;
                }
 -      spin_unlock(&pdata->dyn_callback_lock);
 +      spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);
  
        return NULL;
  }
@@@ -179,7 -159,18 +161,18 @@@ int sensor_hub_register_callback(struc
        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 -199,14 +201,14 @@@ int sensor_hub_remove_callback(struct h
  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);
                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 -241,11 +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);
        }
        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 -279,54 +281,54 @@@ EXPORT_SYMBOL_GPL(sensor_hub_get_featur
  
  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 -482,6 +484,6 @@@ static int sensor_hub_raw_event(struct 
                                        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",
                                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 -604,7 +606,7 @@@ static int sensor_hub_probe(struct hid_
        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) {
        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);
                        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;
                        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 -719,18 +721,18 @@@ static void sensor_hub_remove(struct hi
  {
        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);
index df6a593bd4bdbddd5fcd37e3dd3df231c0cfc1a2,0085c2faeaaa268fb671fb72b798ec99f0c5eab6..2b4fad6998c150b583ed20831b5e3d4c497490fe
@@@ -111,19 -111,27 +111,20 @@@ static int accel_3d_read_raw(struct iio
        int report_id = -1;
        u32 address;
        int ret_type;
 -      s32 poll_value;
  
        *val = 0;
        *val2 = 0;
        switch (mask) {
        case 0:
 -              poll_value = hid_sensor_read_poll_value(
 -                                      &accel_state->common_attributes);
 -              if (poll_value < 0)
 -                      return -EINVAL;
 -
                hid_sensor_power_state(&accel_state->common_attributes, true);
 -              msleep_interruptible(poll_value * 2);
                report_id = accel_state->accel[chan->scan_index].report_id;
                address = accel_3d_addresses[chan->scan_index];
                if (report_id >= 0)
                        *val = sensor_hub_input_attr_get_raw_value(
                                        accel_state->common_attributes.hsdev,
                                        HID_USAGE_SENSOR_ACCEL_3D, address,
-                                       report_id);
+                                       report_id,
+                                       SENSOR_HUB_SYNC);
                else {
                        *val = 0;
                        hid_sensor_power_state(&accel_state->common_attributes,
@@@ -412,7 -420,6 +413,7 @@@ static struct platform_driver hid_accel
        .id_table = hid_accel_3d_ids,
        .driver = {
                .name   = KBUILD_MODNAME,
 +              .pm     = &hid_sensor_pm_ops,
        },
        .probe          = hid_accel_3d_probe,
        .remove         = hid_accel_3d_remove,
index 2f1d535b94c45d08a1824f17081466bace1ea14a,910e82a7d06ef750166882256a1a13697693476a..610fc98f88efa4f05fd996505f45411756a50fb1
  #include <linux/interrupt.h>
  #include <linux/irq.h>
  #include <linux/slab.h>
 +#include <linux/delay.h>
  #include <linux/hid-sensor-hub.h>
  #include <linux/iio/iio.h>
  #include <linux/iio/trigger.h>
  #include <linux/iio/sysfs.h>
  #include "hid-sensor-trigger.h"
  
 -int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
 +static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state)
  {
        int state_val;
        int report_val;
 +      s32 poll_value = 0;
  
        if (state) {
                if (sensor_hub_device_open(st->hsdev))
@@@ -49,8 -47,6 +49,8 @@@
                        st->report_state.report_id,
                        st->report_state.index,
                        HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM);
 +
 +              poll_value = hid_sensor_read_poll_value(st);
        } else {
                if (!atomic_dec_and_test(&st->data_ready))
                        return 0;
        if (state_val >= 0) {
                state_val += st->power_state.logical_minimum;
                sensor_hub_set_feature(st->hsdev, st->power_state.report_id,
-                                       st->power_state.index,
-                                       (s32)state_val);
+                                      st->power_state.index, sizeof(state_val),
+                                      &state_val);
        }
  
        if (report_val >= 0) {
                report_val += st->report_state.logical_minimum;
                sensor_hub_set_feature(st->hsdev, st->report_state.report_id,
-                                       st->report_state.index,
-                                       (s32)report_val);
+                                      st->report_state.index,
+                                      sizeof(report_val),
+                                      &report_val);
        }
  
        sensor_hub_get_feature(st->hsdev, st->power_state.report_id,
-                                       st->power_state.index,
-                                       &state_val);
+                              st->power_state.index,
+                              sizeof(state_val), &state_val);
 +      if (state && poll_value)
 +              msleep_interruptible(poll_value * 2);
 +
        return 0;
  }
  EXPORT_SYMBOL(hid_sensor_power_state);
  
 +int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
 +{
 +#ifdef CONFIG_PM
 +      int ret;
 +
 +      if (state)
 +              ret = pm_runtime_get_sync(&st->pdev->dev);
 +      else {
 +              pm_runtime_mark_last_busy(&st->pdev->dev);
 +              ret = pm_runtime_put_autosuspend(&st->pdev->dev);
 +      }
 +      if (ret < 0) {
 +              if (state)
 +                      pm_runtime_put_noidle(&st->pdev->dev);
 +              return ret;
 +      }
 +
 +      return 0;
 +#else
 +      return _hid_sensor_power_state(st, state);
 +#endif
 +}
 +
  static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
                                                bool state)
  {
@@@ -155,21 -126,8 +156,21 @@@ int hid_sensor_setup_trigger(struct iio
        attrb->trigger = trig;
        indio_dev->trig = iio_trigger_get(trig);
  
 -      return ret;
 +      ret = pm_runtime_set_active(&indio_dev->dev);
 +      if (ret)
 +              goto error_unreg_trigger;
  
 +      iio_device_set_drvdata(indio_dev, attrb);
 +      pm_suspend_ignore_children(&attrb->pdev->dev, true);
 +      pm_runtime_enable(&attrb->pdev->dev);
 +      /* Default to 3 seconds, but can be changed from sysfs */
 +      pm_runtime_set_autosuspend_delay(&attrb->pdev->dev,
 +                                       3000);
 +      pm_runtime_use_autosuspend(&attrb->pdev->dev);
 +
 +      return ret;
 +error_unreg_trigger:
 +      iio_trigger_unregister(trig);
  error_free_trig:
        iio_trigger_free(trig);
  error_ret:
  }
  EXPORT_SYMBOL(hid_sensor_setup_trigger);
  
 +#ifdef CONFIG_PM
 +static int hid_sensor_suspend(struct device *dev)
 +{
 +      struct platform_device *pdev = to_platform_device(dev);
 +      struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 +      struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
 +
 +      return _hid_sensor_power_state(attrb, false);
 +}
 +
 +static int hid_sensor_resume(struct device *dev)
 +{
 +      struct platform_device *pdev = to_platform_device(dev);
 +      struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 +      struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
 +
 +      return _hid_sensor_power_state(attrb, true);
 +}
 +
 +#endif
 +
 +const struct dev_pm_ops hid_sensor_pm_ops = {
 +      SET_SYSTEM_SLEEP_PM_OPS(hid_sensor_suspend, hid_sensor_resume)
 +      SET_RUNTIME_PM_OPS(hid_sensor_suspend,
 +                         hid_sensor_resume, NULL)
 +};
 +EXPORT_SYMBOL(hid_sensor_pm_ops);
 +
  MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
  MODULE_DESCRIPTION("HID Sensor trigger processing");
  MODULE_LICENSE("GPL");
index a3c3e19de52758190889a559a77271c8d35410d5,bdcd105aba26f98703f65c2d23a1638c83a6e059..b5883b6f4e5044f2e9f594bc29e526861a8a575e
@@@ -111,19 -111,27 +111,20 @@@ static int gyro_3d_read_raw(struct iio_
        int report_id = -1;
        u32 address;
        int ret_type;
 -      s32 poll_value;
  
        *val = 0;
        *val2 = 0;
        switch (mask) {
        case 0:
 -              poll_value = hid_sensor_read_poll_value(
 -                                      &gyro_state->common_attributes);
 -              if (poll_value < 0)
 -                      return -EINVAL;
 -
                hid_sensor_power_state(&gyro_state->common_attributes, true);
 -              msleep_interruptible(poll_value * 2);
                report_id = gyro_state->gyro[chan->scan_index].report_id;
                address = gyro_3d_addresses[chan->scan_index];
                if (report_id >= 0)
                        *val = sensor_hub_input_attr_get_raw_value(
                                        gyro_state->common_attributes.hsdev,
                                        HID_USAGE_SENSOR_GYRO_3D, address,
-                                       report_id);
+                                       report_id,
+                                       SENSOR_HUB_SYNC);
                else {
                        *val = 0;
                        hid_sensor_power_state(&gyro_state->common_attributes,
@@@ -409,7 -417,6 +410,7 @@@ static struct platform_driver hid_gyro_
        .id_table = hid_gyro_3d_ids,
        .driver = {
                .name   = KBUILD_MODNAME,
 +              .pm     = &hid_sensor_pm_ops,
        },
        .probe          = hid_gyro_3d_probe,
        .remove         = hid_gyro_3d_remove,
index 948acfc38b8cb3c0f142c8f7350254dc36cd377c,321862da626b70cc08b751d131e0e5191cd4c708..1609ecdd01b07a73b9ad697159f9eb58ac47c5de
@@@ -80,6 -80,7 +80,6 @@@ static int als_read_raw(struct iio_dev 
        int report_id = -1;
        u32 address;
        int ret_type;
 -      s32 poll_value;
  
        *val = 0;
        *val2 = 0;
                        break;
                }
                if (report_id >= 0) {
 -                      poll_value = hid_sensor_read_poll_value(
 -                                              &als_state->common_attributes);
 -                      if (poll_value < 0)
 -                              return -EINVAL;
 -
                        hid_sensor_power_state(&als_state->common_attributes,
                                                true);
 -                      msleep_interruptible(poll_value * 2);
 -
                        *val = sensor_hub_input_attr_get_raw_value(
                                        als_state->common_attributes.hsdev,
                                        HID_USAGE_SENSOR_ALS, address,
-                                       report_id);
+                                       report_id,
+                                       SENSOR_HUB_SYNC);
                        hid_sensor_power_state(&als_state->common_attributes,
                                                false);
                } else {
@@@ -373,7 -382,6 +374,7 @@@ static struct platform_driver hid_als_p
        .id_table = hid_als_ids,
        .driver = {
                .name   = KBUILD_MODNAME,
 +              .pm     = &hid_sensor_pm_ops,
        },
        .probe          = hid_als_probe,
        .remove         = hid_als_remove,
index 3ecf79ed08ac89bf4f82eedbdb47813ed2997f53,db9c60e4727625f8d52117a2f367487d495face4..91ecc46ffeaa0b9e5ecf25d3575d8ab364e4abe8
@@@ -75,6 -75,7 +75,6 @@@ static int prox_read_raw(struct iio_de
        int report_id = -1;
        u32 address;
        int ret_type;
 -      s32 poll_value;
  
        *val = 0;
        *val2 = 0;
                        break;
                }
                if (report_id >= 0) {
 -                      poll_value = hid_sensor_read_poll_value(
 -                                      &prox_state->common_attributes);
 -                      if (poll_value < 0)
 -                              return -EINVAL;
 -
                        hid_sensor_power_state(&prox_state->common_attributes,
                                                true);
 -
 -                      msleep_interruptible(poll_value * 2);
 -
                        *val = sensor_hub_input_attr_get_raw_value(
                                prox_state->common_attributes.hsdev,
                                HID_USAGE_SENSOR_PROX, address,
-                               report_id);
+                               report_id,
+                               SENSOR_HUB_SYNC);
                        hid_sensor_power_state(&prox_state->common_attributes,
                                                false);
                } else {
@@@ -364,7 -374,6 +365,7 @@@ static struct platform_driver hid_prox_
        .id_table = hid_prox_ids,
        .driver = {
                .name   = KBUILD_MODNAME,
 +              .pm     = &hid_sensor_pm_ops,
        },
        .probe          = hid_prox_probe,
        .remove         = hid_prox_remove,
index d22993b4066a0d1d50b8000c234bc2154f5477ef,4d299f39ffd1294ccfae4c368f7b9d0239ed6b03..4f9c0be244518d96ddeba9f1a5cb46329859129e
@@@ -157,12 -157,20 +157,12 @@@ static int magn_3d_read_raw(struct iio_
        int report_id = -1;
        u32 address;
        int ret_type;
 -      s32 poll_value;
  
        *val = 0;
        *val2 = 0;
        switch (mask) {
        case 0:
 -              poll_value = hid_sensor_read_poll_value(
 -                                      &magn_state->common_attributes);
 -              if (poll_value < 0)
 -                              return -EINVAL;
 -
                hid_sensor_power_state(&magn_state->common_attributes, true);
 -              msleep_interruptible(poll_value * 2);
 -
                report_id =
                        magn_state->magn[chan->address].report_id;
                address = magn_3d_addresses[chan->address];
                        *val = sensor_hub_input_attr_get_raw_value(
                                magn_state->common_attributes.hsdev,
                                HID_USAGE_SENSOR_COMPASS_3D, address,
-                               report_id);
+                               report_id,
+                               SENSOR_HUB_SYNC);
                else {
                        *val = 0;
                        hid_sensor_power_state(&magn_state->common_attributes,
@@@ -522,7 -531,6 +523,7 @@@ static struct platform_driver hid_magn_
        .id_table = hid_magn_3d_ids,
        .driver = {
                .name   = KBUILD_MODNAME,
 +              .pm     = &hid_sensor_pm_ops,
        },
        .probe          = hid_magn_3d_probe,
        .remove         = hid_magn_3d_remove,
index 73854460bb2cff5c18622ca39fc45f1b5e42625f,45bed080b4a6785b058354b4913a4e2ea386f007..5930fa32a2ab19a4df3a42f6939b323d876d3c8e
@@@ -111,12 -111,20 +111,12 @@@ static int incl_3d_read_raw(struct iio_
        int report_id = -1;
        u32 address;
        int ret_type;
 -      s32 poll_value;
  
        *val = 0;
        *val2 = 0;
        switch (mask) {
        case IIO_CHAN_INFO_RAW:
 -              poll_value = hid_sensor_read_poll_value(
 -                                      &incl_state->common_attributes);
 -              if (poll_value < 0)
 -                      return -EINVAL;
 -
                hid_sensor_power_state(&incl_state->common_attributes, true);
 -              msleep_interruptible(poll_value * 2);
 -
                report_id =
                        incl_state->incl[chan->scan_index].report_id;
                address = incl_3d_addresses[chan->scan_index];
                        *val = sensor_hub_input_attr_get_raw_value(
                                incl_state->common_attributes.hsdev,
                                HID_USAGE_SENSOR_INCLINOMETER_3D, address,
-                               report_id);
+                               report_id,
+                               SENSOR_HUB_SYNC);
                else {
                        hid_sensor_power_state(&incl_state->common_attributes,
                                                false);
@@@ -429,7 -438,6 +430,7 @@@ static struct platform_driver hid_incl_
        .id_table = hid_incl_3d_ids,
        .driver = {
                .name   = KBUILD_MODNAME,
 +              .pm     = &hid_sensor_pm_ops,
        },
        .probe          = hid_incl_3d_probe,
        .remove         = hid_incl_3d_remove,
index 1af314926ebd1f94ae422394aa186af7a05557bd,ac150f0c8b0200897603df7dce15a9a1a2bae49f..7bb8d4c1f7df4922279dd328ddfc83fba1b113c4
@@@ -79,6 -79,7 +79,6 @@@ static int press_read_raw(struct iio_de
        int report_id = -1;
        u32 address;
        int ret_type;
 -      s32 poll_value;
  
        *val = 0;
        *val2 = 0;
                        break;
                }
                if (report_id >= 0) {
 -                      poll_value = hid_sensor_read_poll_value(
 -                                      &press_state->common_attributes);
 -                      if (poll_value < 0)
 -                              return -EINVAL;
                        hid_sensor_power_state(&press_state->common_attributes,
                                                true);
 -
 -                      msleep_interruptible(poll_value * 2);
 -
                        *val = sensor_hub_input_attr_get_raw_value(
                                press_state->common_attributes.hsdev,
                                HID_USAGE_SENSOR_PRESSURE, address,
-                               report_id);
+                               report_id,
+                               SENSOR_HUB_SYNC);
                        hid_sensor_power_state(&press_state->common_attributes,
                                                false);
                } else {
@@@ -374,7 -383,6 +375,7 @@@ static struct platform_driver hid_press
        .id_table = hid_press_ids,
        .driver = {
                .name   = KBUILD_MODNAME,
 +              .pm     = &hid_sensor_pm_ops,
        },
        .probe          = hid_press_probe,
        .remove         = hid_press_remove,
index 4173a8fdad9efd052870b8738547ac1fa1962526,4a2fdbabfcf1f554c77b7ed52c9ef5c02f2214bc..0408421d885f9433c61149e2769bdd3c08192522
@@@ -33,8 -33,6 +33,8 @@@
   * @units:            Measurment unit for this attribute.
   * @unit_expo:                Exponent used in the data.
   * @size:             Size in bytes for data size.
 + * @logical_minimum:  Logical minimum value for this attribute.
 + * @logical_maximum:  Logical maximum value for this attribute.
   */
  struct hid_sensor_hub_attribute_info {
        u32 usage_id;
        s32 logical_maximum;
  };
  
+ /**
+  * 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 hid_sensor_hub_device - Stores the hub instance data
   * @hdev:             Stores the hid instance.
   * @vendor_id:                Vendor id of hub device.
   * @product_id:               Product id of hub device.
+  * @usage:            Usage id for this hub device instance.
   * @start_collection_index: Starting index for a phy type collection
   * @end_collection_index: Last index for a phy type collection
+  * @mutex:            synchronizing mutex.
+  * @pending:          Holds information of pending sync read request.
   */
  struct hid_sensor_hub_device {
        struct hid_device *hdev;
        u32 vendor_id;
        u32 product_id;
+       u32 usage;
        int start_collection_index;
        int end_collection_index;
+       struct mutex mutex;
+       struct sensor_hub_pending pending;
  };
  
  /**
@@@ -147,45 -169,53 +171,56 @@@ int sensor_hub_input_get_attribute_info
                        struct hid_sensor_hub_attribute_info *info);
  
  /**
 -* sensor_hub_input_attr_get_raw_value() - Attribute read request
 +* sensor_hub_input_attr_get_raw_value() - Synchronous 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
  *
- * Issues a synchronous read request for an input attribute. Returns
- * data upto 32 bits. Since client can get events, so this call should
- * not be used for data paths, this will impact performance.
+ * Issues a synchronous or asynchronous read request for an input attribute.
+ * Returns data upto 32 bits.
  */
  
+ enum sensor_hub_read_flags {
+       SENSOR_HUB_SYNC,
+       SENSOR_HUB_ASYNC,
+ };
  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 usage_id,
+                                       u32 attr_usage_id, u32 report_id,
+                                       enum sensor_hub_read_flags flag
+ );
  /**
  * sensor_hub_set_feature() - Feature set request
 +* @hsdev:     Hub device instance.
  * @report_id: Report id to look for
  * @field_index:       Field index inside a report
- * @value:     Value to set
+ * @buffer_size: size of the buffer
+ * @buffer:    buffer to use in the feature set
  *
  * Used to set a field in feature report. For example this can set polling
  * interval, sensitivity, activate/deactivate state.
  */
  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);
  
  /**
  * sensor_hub_get_feature() - Feature get request
 +* @hsdev:     Hub device instance.
  * @report_id: Report id to look for
  * @field_index:       Field index inside a report
- * @value:     Place holder for return value
+ * @buffer_size:       size of the buffer
+ * @buffer:    buffer to copy output
  *
  * Used to get a field in feature report. For example this can get polling
- * interval, sensitivity, activate/deactivate state.
+ * interval, sensitivity, activate/deactivate state. On success it returns
+ * number of bytes copied to buffer. On failure, it returns value < 0.
  */
  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);
  
  /* hid-sensor-attributes */