Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
[jlayton/linux.git] / drivers / hid / i2c-hid / i2c-hid.c
index 42eebd14de1f2e41ed932335e729bfb448b7f3f1..b50860db92f16c6b34fe71db03b58ce7c7900253 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/pm.h>
+#include <linux/pm_runtime.h>
 #include <linux/device.h>
 #include <linux/wait.h>
 #include <linux/err.h>
@@ -256,18 +257,27 @@ static int i2c_hid_get_report(struct i2c_client *client, u8 reportType,
        return 0;
 }
 
-static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,
-               u8 reportID, unsigned char *buf, size_t data_len)
+/**
+ * i2c_hid_set_or_send_report: forward an incoming report to the device
+ * @client: the i2c_client of the device
+ * @reportType: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT
+ * @reportID: the report ID
+ * @buf: the actual data to transfer, without the report ID
+ * @len: size of buf
+ * @use_data: true: use SET_REPORT HID command, false: send plain OUTPUT report
+ */
+static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType,
+               u8 reportID, unsigned char *buf, size_t data_len, bool use_data)
 {
        struct i2c_hid *ihid = i2c_get_clientdata(client);
        u8 *args = ihid->argsbuf;
-       const struct i2c_hid_cmd * hidcmd = &hid_set_report_cmd;
+       const struct i2c_hid_cmd *hidcmd;
        int ret;
        u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister);
        u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister);
        u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength);
 
-       /* hidraw already checked that data_len < HID_MAX_BUFFER_SIZE */
+       /* hid_hw_* already checked that data_len < HID_MAX_BUFFER_SIZE */
        u16 size =      2                       /* size */ +
                        (reportID ? 1 : 0)      /* reportID */ +
                        data_len                /* buf */;
@@ -278,6 +288,9 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,
 
        i2c_hid_dbg(ihid, "%s\n", __func__);
 
+       if (!use_data && maxOutputLength == 0)
+               return -ENOSYS;
+
        if (reportID >= 0x0F) {
                args[index++] = reportID;
                reportID = 0x0F;
@@ -287,9 +300,10 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,
         * use the data register for feature reports or if the device does not
         * support the output register
         */
-       if (reportType == 0x03 || maxOutputLength == 0) {
+       if (use_data) {
                args[index++] = dataRegister & 0xFF;
                args[index++] = dataRegister >> 8;
+               hidcmd = &hid_set_report_cmd;
        } else {
                args[index++] = outputRegister & 0xFF;
                args[index++] = outputRegister >> 8;
@@ -454,10 +468,18 @@ static void i2c_hid_init_reports(struct hid_device *hid)
                return;
        }
 
+       /*
+        * The device must be powered on while we fetch initial reports
+        * from it.
+        */
+       pm_runtime_get_sync(&client->dev);
+
        list_for_each_entry(report,
                &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
                i2c_hid_init_report(report, inbuf, ihid->bufsize);
 
+       pm_runtime_put(&client->dev);
+
        kfree(inbuf);
 }
 
@@ -550,7 +572,7 @@ static int i2c_hid_get_raw_report(struct hid_device *hid,
 }
 
 static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
-               size_t count, unsigned char report_type)
+               size_t count, unsigned char report_type, bool use_data)
 {
        struct i2c_client *client = hid->driver_data;
        int report_id = buf[0];
@@ -564,9 +586,9 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
                count--;
        }
 
-       ret = i2c_hid_set_report(client,
+       ret = i2c_hid_set_or_send_report(client,
                                report_type == HID_FEATURE_REPORT ? 0x03 : 0x02,
-                               report_id, buf, count);
+                               report_id, buf, count, use_data);
 
        if (report_id && ret >= 0)
                ret++; /* add report_id to the number of transfered bytes */
@@ -574,34 +596,27 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,
        return ret;
 }
 
-static void i2c_hid_request(struct hid_device *hid, struct hid_report *rep,
-               int reqtype)
+static int i2c_hid_output_report(struct hid_device *hid, __u8 *buf,
+               size_t count)
 {
-       struct i2c_client *client = hid->driver_data;
-       char *buf;
-       int ret;
-       int len = i2c_hid_get_report_length(rep) - 2;
-
-       buf = hid_alloc_report_buf(rep, GFP_KERNEL);
-       if (!buf)
-               return;
+       return i2c_hid_output_raw_report(hid, buf, count, HID_OUTPUT_REPORT,
+                       false);
+}
 
+static int i2c_hid_raw_request(struct hid_device *hid, unsigned char reportnum,
+                              __u8 *buf, size_t len, unsigned char rtype,
+                              int reqtype)
+{
        switch (reqtype) {
        case HID_REQ_GET_REPORT:
-               ret = i2c_hid_get_raw_report(hid, rep->id, buf, len, rep->type);
-               if (ret < 0)
-                       dev_err(&client->dev, "%s: unable to get report: %d\n",
-                               __func__, ret);
-               else
-                       hid_input_report(hid, rep->type, buf, ret, 0);
-               break;
+               return i2c_hid_get_raw_report(hid, reportnum, buf, len, rtype);
        case HID_REQ_SET_REPORT:
-               hid_output_report(rep, buf);
-               i2c_hid_output_raw_report(hid, buf, len, rep->type);
-               break;
+               if (buf[0] != reportnum)
+                       return -EINVAL;
+               return i2c_hid_output_raw_report(hid, buf, len, rtype, true);
+       default:
+               return -EIO;
        }
-
-       kfree(buf);
 }
 
 static int i2c_hid_parse(struct hid_device *hid)
@@ -703,8 +718,8 @@ static int i2c_hid_open(struct hid_device *hid)
 
        mutex_lock(&i2c_hid_open_mut);
        if (!hid->open++) {
-               ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
-               if (ret) {
+               ret = pm_runtime_get_sync(&client->dev);
+               if (ret < 0) {
                        hid->open--;
                        goto done;
                }
@@ -712,7 +727,7 @@ static int i2c_hid_open(struct hid_device *hid)
        }
 done:
        mutex_unlock(&i2c_hid_open_mut);
-       return ret;
+       return ret < 0 ? ret : 0;
 }
 
 static void i2c_hid_close(struct hid_device *hid)
@@ -729,7 +744,7 @@ static void i2c_hid_close(struct hid_device *hid)
                clear_bit(I2C_HID_STARTED, &ihid->flags);
 
                /* Save some power */
-               i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+               pm_runtime_put(&client->dev);
        }
        mutex_unlock(&i2c_hid_open_mut);
 }
@@ -738,19 +753,18 @@ static int i2c_hid_power(struct hid_device *hid, int lvl)
 {
        struct i2c_client *client = hid->driver_data;
        struct i2c_hid *ihid = i2c_get_clientdata(client);
-       int ret = 0;
 
        i2c_hid_dbg(ihid, "%s lvl:%d\n", __func__, lvl);
 
        switch (lvl) {
        case PM_HINT_FULLON:
-               ret = i2c_hid_set_power(client, I2C_HID_PWR_ON);
+               pm_runtime_get_sync(&client->dev);
                break;
        case PM_HINT_NORMAL:
-               ret = i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+               pm_runtime_put(&client->dev);
                break;
        }
-       return ret;
+       return 0;
 }
 
 static struct hid_ll_driver i2c_hid_ll_driver = {
@@ -760,7 +774,8 @@ static struct hid_ll_driver i2c_hid_ll_driver = {
        .open = i2c_hid_open,
        .close = i2c_hid_close,
        .power = i2c_hid_power,
-       .request = i2c_hid_request,
+       .output_report = i2c_hid_output_report,
+       .raw_request = i2c_hid_raw_request,
 };
 
 static int i2c_hid_init_irq(struct i2c_client *client)
@@ -973,13 +988,17 @@ static int i2c_hid_probe(struct i2c_client *client,
        if (ret < 0)
                goto err;
 
+       pm_runtime_get_noresume(&client->dev);
+       pm_runtime_set_active(&client->dev);
+       pm_runtime_enable(&client->dev);
+
        ret = i2c_hid_fetch_hid_descriptor(ihid);
        if (ret < 0)
-               goto err;
+               goto err_pm;
 
        ret = i2c_hid_init_irq(client);
        if (ret < 0)
-               goto err;
+               goto err_pm;
 
        hid = hid_allocate_device();
        if (IS_ERR(hid)) {
@@ -991,8 +1010,6 @@ static int i2c_hid_probe(struct i2c_client *client,
 
        hid->driver_data = client;
        hid->ll_driver = &i2c_hid_ll_driver;
-       hid->hid_get_raw_report = i2c_hid_get_raw_report;
-       hid->hid_output_raw_report = i2c_hid_output_raw_report;
        hid->dev.parent = &client->dev;
        ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev));
        hid->bus = BUS_I2C;
@@ -1010,6 +1027,7 @@ static int i2c_hid_probe(struct i2c_client *client,
                goto err_mem_free;
        }
 
+       pm_runtime_put(&client->dev);
        return 0;
 
 err_mem_free:
@@ -1018,6 +1036,10 @@ err_mem_free:
 err_irq:
        free_irq(client->irq, ihid);
 
+err_pm:
+       pm_runtime_put_noidle(&client->dev);
+       pm_runtime_disable(&client->dev);
+
 err:
        i2c_hid_free_buffers(ihid);
        kfree(ihid);
@@ -1029,6 +1051,11 @@ static int i2c_hid_remove(struct i2c_client *client)
        struct i2c_hid *ihid = i2c_get_clientdata(client);
        struct hid_device *hid;
 
+       pm_runtime_get_sync(&client->dev);
+       pm_runtime_disable(&client->dev);
+       pm_runtime_set_suspended(&client->dev);
+       pm_runtime_put_noidle(&client->dev);
+
        hid = ihid->hid;
        hid_destroy_device(hid);
 
@@ -1074,7 +1101,31 @@ static int i2c_hid_resume(struct device *dev)
 }
 #endif
 
-static SIMPLE_DEV_PM_OPS(i2c_hid_pm, i2c_hid_suspend, i2c_hid_resume);
+#ifdef CONFIG_PM_RUNTIME
+static int i2c_hid_runtime_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+
+       i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
+       disable_irq(client->irq);
+       return 0;
+}
+
+static int i2c_hid_runtime_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+
+       enable_irq(client->irq);
+       i2c_hid_set_power(client, I2C_HID_PWR_ON);
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops i2c_hid_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_suspend, i2c_hid_resume)
+       SET_RUNTIME_PM_OPS(i2c_hid_runtime_suspend, i2c_hid_runtime_resume,
+                          NULL)
+};
 
 static const struct i2c_device_id i2c_hid_id_table[] = {
        { "hid", 0 },