summaryrefslogtreecommitdiffstats
path: root/drivers/hid/uhid.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-02-21 17:41:38 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-21 17:41:38 -0800
commit48a732dfaa77a4dfec803aa8f248373998704f76 (patch)
treeb8ea89d3f48bc82fcc1b14d8cd356241e7ef2d37 /drivers/hid/uhid.c
parent9afa3195b96da7d2320ec44d19fbfbded7a15571 (diff)
parent0d69a3c731e120b05b7da9fb976830475a3fbc01 (diff)
downloadlinux-48a732dfaa77a4dfec803aa8f248373998704f76.tar.bz2
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
Pull HID subsystem updates from Jiri Kosina: "HID subsystem and drivers update. Highlights: - new support of a group of Win7/Win8 multitouch devices, from Benjamin Tissoires - fix for compat interface brokenness in uhid, from Dmitry Torokhov - conversion of drivers to use hid_driver helper, by H Hartley Sweeten - HID over I2C transport received ACPI enumeration support, written by Mika Westerberg - there is an ongoing effort to make HID sensor hubs independent of USB transport. The first self-contained part of this work is provided here, done by Mika Westerberg - a few smaller fixes here and there, support for a couple new devices added" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (43 commits) HID: Correct Logitech order in hid-ids.h HID: LG4FF: Remove unnecessary deadzone code HID: LG: Prevent the Logitech Gaming Wheels deadzone HID: LG: Fix detection of Logitech Speed Force Wireless (WiiWheel) HID: LG: Add support for Logitech Momo Force (Red) Wheel HID: hidraw: print message when succesfully initialized HID: logitech: split accel, brake for Driving Force wheel HID: logitech: add report descriptor for Driving Force wheel HID: add ThingM blink(1) USB RGB LED support HID: uhid: make creating devices work on 64/32 systems HID: wiimote: fix nunchuck button parser HID: blacklist Velleman data acquisition boards HID: sensor-hub: don't limit the driver only to USB bus HID: sensor-hub: get rid of unused sensor_hub_grabbed_usages[] table HID: extend autodetect to handle I2C sensors as well HID: ntrig: use input_configured() callback to set the name HID: multitouch: do not use pointers towards hid-core HID: add missing GENERIC_HARDIRQ dependency HID: multitouch: make MT_CLS_ALWAYS_TRUE the new default class HID: multitouch: fix protocol for Elo panels ...
Diffstat (limited to 'drivers/hid/uhid.c')
-rw-r--r--drivers/hid/uhid.c95
1 files changed, 92 insertions, 3 deletions
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index 714cd8cc9579..fc307e0422af 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: