diff options
-rw-r--r-- | drivers/hid/hidraw.c | 1 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid.c | 87 | ||||
-rw-r--r-- | drivers/hid/uhid.c | 95 |
3 files changed, 174 insertions, 9 deletions
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 413a73187d3..f3bbbce8353 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -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; diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index e766b5614ef..ec7930217a6 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -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, diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 714cd8cc957..fc307e0422a 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -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: |