Merge branches 'for-3.8/upstream-fixes', 'for-3.9/hid-sensor', 'for-3.9/hidraw' and...
authorJiri Kosina <jkosina@suse.cz>
Thu, 21 Feb 2013 09:42:39 +0000 (10:42 +0100)
committerJiri Kosina <jkosina@suse.cz>
Thu, 21 Feb 2013 09:42:39 +0000 (10:42 +0100)
Conflicts:
drivers/hid/i2c-hid/i2c-hid.c

drivers/hid/hidraw.c
drivers/hid/i2c-hid/i2c-hid.c
drivers/hid/uhid.c

index 413a731..f3bbbce 100644 (file)
@@ -581,6 +581,7 @@ int __init hidraw_init(void)
        if (result < 0)
                goto error_class;
 
+       printk(KERN_INFO "hidraw: raw HID events driver (C) Jiri Kosina\n");
 out:
        return result;
 
index e766b56..ec79302 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/kernel.h>
 #include <linux/hid.h>
 #include <linux/mutex.h>
+#include <linux/acpi.h>
 
 #include <linux/i2c/i2c-hid.h>
 
@@ -139,6 +140,8 @@ struct i2c_hid {
        unsigned long           flags;          /* device flags */
 
        wait_queue_head_t       wait;           /* For waiting the interrupt */
+
+       struct i2c_hid_platform_data pdata;
 };
 
 static int __i2c_hid_command(struct i2c_client *client,
@@ -821,6 +824,70 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
        return 0;
 }
 
+#ifdef CONFIG_ACPI
+static int i2c_hid_acpi_pdata(struct i2c_client *client,
+               struct i2c_hid_platform_data *pdata)
+{
+       static u8 i2c_hid_guid[] = {
+               0xF7, 0xF6, 0xDF, 0x3C, 0x67, 0x42, 0x55, 0x45,
+               0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE,
+       };
+       struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object params[4], *obj;
+       struct acpi_object_list input;
+       struct acpi_device *adev;
+       acpi_handle handle;
+
+       handle = ACPI_HANDLE(&client->dev);
+       if (!handle || acpi_bus_get_device(handle, &adev))
+               return -ENODEV;
+
+       input.count = ARRAY_SIZE(params);
+       input.pointer = params;
+
+       params[0].type = ACPI_TYPE_BUFFER;
+       params[0].buffer.length = sizeof(i2c_hid_guid);
+       params[0].buffer.pointer = i2c_hid_guid;
+       params[1].type = ACPI_TYPE_INTEGER;
+       params[1].integer.value = 1;
+       params[2].type = ACPI_TYPE_INTEGER;
+       params[2].integer.value = 1; /* HID function */
+       params[3].type = ACPI_TYPE_INTEGER;
+       params[3].integer.value = 0;
+
+       if (ACPI_FAILURE(acpi_evaluate_object(handle, "_DSM", &input, &buf))) {
+               dev_err(&client->dev, "device _DSM execution failed\n");
+               return -ENODEV;
+       }
+
+       obj = (union acpi_object *)buf.pointer;
+       if (obj->type != ACPI_TYPE_INTEGER) {
+               dev_err(&client->dev, "device _DSM returned invalid type: %d\n",
+                       obj->type);
+               kfree(buf.pointer);
+               return -EINVAL;
+       }
+
+       pdata->hid_descriptor_address = obj->integer.value;
+
+       kfree(buf.pointer);
+       return 0;
+}
+
+static const struct acpi_device_id i2c_hid_acpi_match[] = {
+       {"ACPI0C50", 0 },
+       {"PNP0C50", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, i2c_hid_acpi_match);
+#else
+static inline int i2c_hid_acpi_pdata(struct i2c_client *client,
+               struct i2c_hid_platform_data *pdata)
+{
+       return -ENODEV;
+}
+#endif
+
 static int i2c_hid_probe(struct i2c_client *client,
                         const struct i2c_device_id *dev_id)
 {
@@ -832,11 +899,6 @@ static int i2c_hid_probe(struct i2c_client *client,
 
        dbg_hid("HID probe called for i2c 0x%02x\n", client->addr);
 
-       if (!platform_data) {
-               dev_err(&client->dev, "HID register address not provided\n");
-               return -EINVAL;
-       }
-
        if (!client->irq) {
                dev_err(&client->dev,
                        "HID over i2c has not been provided an Int IRQ\n");
@@ -847,11 +909,22 @@ static int i2c_hid_probe(struct i2c_client *client,
        if (!ihid)
                return -ENOMEM;
 
+       if (!platform_data) {
+               ret = i2c_hid_acpi_pdata(client, &ihid->pdata);
+               if (ret) {
+                       dev_err(&client->dev,
+                               "HID register address not provided\n");
+                       goto err;
+               }
+       } else {
+               ihid->pdata = *platform_data;
+       }
+
        i2c_set_clientdata(client, ihid);
 
        ihid->client = client;
 
-       hidRegister = platform_data->hid_descriptor_address;
+       hidRegister = ihid->pdata.hid_descriptor_address;
        ihid->wHIDDescRegister = cpu_to_le16(hidRegister);
 
        init_waitqueue_head(&ihid->wait);
@@ -884,6 +957,7 @@ static int i2c_hid_probe(struct i2c_client *client,
        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_HANDLE_SET(&hid->dev, ACPI_HANDLE(&client->dev));
        hid->bus = BUS_I2C;
        hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);
        hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID);
@@ -975,6 +1049,7 @@ static struct i2c_driver i2c_hid_driver = {
                .name   = "i2c_hid",
                .owner  = THIS_MODULE,
                .pm     = &i2c_hid_pm,
+               .acpi_match_table = ACPI_PTR(i2c_hid_acpi_match),
        },
 
        .probe          = i2c_hid_probe,
index 714cd8c..fc307e0 100644 (file)
@@ -11,6 +11,7 @@
  */
 
 #include <linux/atomic.h>
+#include <linux/compat.h>
 #include <linux/device.h>
 #include <linux/fs.h>
 #include <linux/hid.h>
@@ -276,6 +277,94 @@ static struct hid_ll_driver uhid_hid_driver = {
        .parse = uhid_hid_parse,
 };
 
+#ifdef CONFIG_COMPAT
+
+/* Apparently we haven't stepped on these rakes enough times yet. */
+struct uhid_create_req_compat {
+       __u8 name[128];
+       __u8 phys[64];
+       __u8 uniq[64];
+
+       compat_uptr_t rd_data;
+       __u16 rd_size;
+
+       __u16 bus;
+       __u32 vendor;
+       __u32 product;
+       __u32 version;
+       __u32 country;
+} __attribute__((__packed__));
+
+static int uhid_event_from_user(const char __user *buffer, size_t len,
+                               struct uhid_event *event)
+{
+       if (is_compat_task()) {
+               u32 type;
+
+               if (get_user(type, buffer))
+                       return -EFAULT;
+
+               if (type == UHID_CREATE) {
+                       /*
+                        * This is our messed up request with compat pointer.
+                        * It is largish (more than 256 bytes) so we better
+                        * allocate it from the heap.
+                        */
+                       struct uhid_create_req_compat *compat;
+
+                       compat = kmalloc(sizeof(*compat), GFP_KERNEL);
+                       if (!compat)
+                               return -ENOMEM;
+
+                       buffer += sizeof(type);
+                       len -= sizeof(type);
+                       if (copy_from_user(compat, buffer,
+                                          min(len, sizeof(*compat)))) {
+                               kfree(compat);
+                               return -EFAULT;
+                       }
+
+                       /* Shuffle the data over to proper structure */
+                       event->type = type;
+
+                       memcpy(event->u.create.name, compat->name,
+                               sizeof(compat->name));
+                       memcpy(event->u.create.phys, compat->phys,
+                               sizeof(compat->phys));
+                       memcpy(event->u.create.uniq, compat->uniq,
+                               sizeof(compat->uniq));
+
+                       event->u.create.rd_data = compat_ptr(compat->rd_data);
+                       event->u.create.rd_size = compat->rd_size;
+
+                       event->u.create.bus = compat->bus;
+                       event->u.create.vendor = compat->vendor;
+                       event->u.create.product = compat->product;
+                       event->u.create.version = compat->version;
+                       event->u.create.country = compat->country;
+
+                       kfree(compat);
+                       return 0;
+               }
+               /* All others can be copied directly */
+       }
+
+       if (copy_from_user(event, buffer, min(len, sizeof(*event))))
+               return -EFAULT;
+
+       return 0;
+}
+#else
+static int uhid_event_from_user(const char __user *buffer, size_t len,
+                               struct uhid_event *event)
+{
+       if (copy_from_user(event, buffer, min(len, sizeof(*event))))
+               return -EFAULT;
+
+       return 0;
+}
+#endif
+
 static int uhid_dev_create(struct uhid_device *uhid,
                           const struct uhid_event *ev)
 {
@@ -498,10 +587,10 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer,
 
        memset(&uhid->input_buf, 0, sizeof(uhid->input_buf));
        len = min(count, sizeof(uhid->input_buf));
-       if (copy_from_user(&uhid->input_buf, buffer, len)) {
-               ret = -EFAULT;
+
+       ret = uhid_event_from_user(buffer, len, &uhid->input_buf);
+       if (ret)
                goto unlock;
-       }
 
        switch (uhid->input_buf.type) {
        case UHID_CREATE: