aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorPhilip Langdale <philipl@overt.org>2008-10-16 22:31:42 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2008-10-27 22:03:42 -0400
commit2d56f3a32c0e62f99c043d2579840f9731fe5855 (patch)
tree3bf1539bbed43e5309dcfd634f202bb9ad0f11b2 /drivers
parent49fdf6785fd660e18a1eb4588928f47e9fa29a9a (diff)
Input: refactor evdev 32bit compat to be shareable with uinput
Currently, evdev has working 32bit compatibility and uinput does not. uinput needs the input_event code that evdev uses, so let's refactor it so it can be shared. [dtor@mail.ru: add fix for force feedback compat issues] Signed-off-by: Philip Langdale <philipl@overt.org> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/input/Makefile2
-rw-r--r--drivers/input/evdev.c197
-rw-r--r--drivers/input/input-compat.c135
-rw-r--r--drivers/input/input-compat.h94
-rw-r--r--drivers/input/misc/uinput.c172
5 files changed, 383 insertions, 217 deletions
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 98c4f9a77876..4c9c745a7020 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -5,7 +5,7 @@
5# Each configuration option enables a list of files. 5# Each configuration option enables a list of files.
6 6
7obj-$(CONFIG_INPUT) += input-core.o 7obj-$(CONFIG_INPUT) += input-core.o
8input-core-objs := input.o ff-core.o 8input-core-objs := input.o input-compat.o ff-core.o
9 9
10obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o 10obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
11obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o 11obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 3524bef62be6..377b2007377e 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -19,7 +19,7 @@
19#include <linux/input.h> 19#include <linux/input.h>
20#include <linux/major.h> 20#include <linux/major.h>
21#include <linux/device.h> 21#include <linux/device.h>
22#include <linux/compat.h> 22#include "input-compat.h"
23 23
24struct evdev { 24struct evdev {
25 int exist; 25 int exist;
@@ -291,187 +291,6 @@ static int evdev_open(struct inode *inode, struct file *file)
291 return error; 291 return error;
292} 292}
293 293
294#ifdef CONFIG_COMPAT
295
296struct input_event_compat {
297 struct compat_timeval time;
298 __u16 type;
299 __u16 code;
300 __s32 value;
301};
302
303struct ff_periodic_effect_compat {
304 __u16 waveform;
305 __u16 period;
306 __s16 magnitude;
307 __s16 offset;
308 __u16 phase;
309
310 struct ff_envelope envelope;
311
312 __u32 custom_len;
313 compat_uptr_t custom_data;
314};
315
316struct ff_effect_compat {
317 __u16 type;
318 __s16 id;
319 __u16 direction;
320 struct ff_trigger trigger;
321 struct ff_replay replay;
322
323 union {
324 struct ff_constant_effect constant;
325 struct ff_ramp_effect ramp;
326 struct ff_periodic_effect_compat periodic;
327 struct ff_condition_effect condition[2]; /* One for each axis */
328 struct ff_rumble_effect rumble;
329 } u;
330};
331
332/* Note to the author of this code: did it ever occur to
333 you why the ifdefs are needed? Think about it again. -AK */
334#ifdef CONFIG_X86_64
335# define COMPAT_TEST is_compat_task()
336#elif defined(CONFIG_IA64)
337# define COMPAT_TEST IS_IA32_PROCESS(task_pt_regs(current))
338#elif defined(CONFIG_S390)
339# define COMPAT_TEST test_thread_flag(TIF_31BIT)
340#elif defined(CONFIG_MIPS)
341# define COMPAT_TEST test_thread_flag(TIF_32BIT_ADDR)
342#else
343# define COMPAT_TEST test_thread_flag(TIF_32BIT)
344#endif
345
346static inline size_t evdev_event_size(void)
347{
348 return COMPAT_TEST ?
349 sizeof(struct input_event_compat) : sizeof(struct input_event);
350}
351
352static int evdev_event_from_user(const char __user *buffer,
353 struct input_event *event)
354{
355 if (COMPAT_TEST) {
356 struct input_event_compat compat_event;
357
358 if (copy_from_user(&compat_event, buffer,
359 sizeof(struct input_event_compat)))
360 return -EFAULT;
361
362 event->time.tv_sec = compat_event.time.tv_sec;
363 event->time.tv_usec = compat_event.time.tv_usec;
364 event->type = compat_event.type;
365 event->code = compat_event.code;
366 event->value = compat_event.value;
367
368 } else {
369 if (copy_from_user(event, buffer, sizeof(struct input_event)))
370 return -EFAULT;
371 }
372
373 return 0;
374}
375
376static int evdev_event_to_user(char __user *buffer,
377 const struct input_event *event)
378{
379 if (COMPAT_TEST) {
380 struct input_event_compat compat_event;
381
382 compat_event.time.tv_sec = event->time.tv_sec;
383 compat_event.time.tv_usec = event->time.tv_usec;
384 compat_event.type = event->type;
385 compat_event.code = event->code;
386 compat_event.value = event->value;
387
388 if (copy_to_user(buffer, &compat_event,
389 sizeof(struct input_event_compat)))
390 return -EFAULT;
391
392 } else {
393 if (copy_to_user(buffer, event, sizeof(struct input_event)))
394 return -EFAULT;
395 }
396
397 return 0;
398}
399
400static int evdev_ff_effect_from_user(const char __user *buffer, size_t size,
401 struct ff_effect *effect)
402{
403 if (COMPAT_TEST) {
404 struct ff_effect_compat *compat_effect;
405
406 if (size != sizeof(struct ff_effect_compat))
407 return -EINVAL;
408
409 /*
410 * It so happens that the pointer which needs to be changed
411 * is the last field in the structure, so we can copy the
412 * whole thing and replace just the pointer.
413 */
414
415 compat_effect = (struct ff_effect_compat *)effect;
416
417 if (copy_from_user(compat_effect, buffer,
418 sizeof(struct ff_effect_compat)))
419 return -EFAULT;
420
421 if (compat_effect->type == FF_PERIODIC &&
422 compat_effect->u.periodic.waveform == FF_CUSTOM)
423 effect->u.periodic.custom_data =
424 compat_ptr(compat_effect->u.periodic.custom_data);
425 } else {
426 if (size != sizeof(struct ff_effect))
427 return -EINVAL;
428
429 if (copy_from_user(effect, buffer, sizeof(struct ff_effect)))
430 return -EFAULT;
431 }
432
433 return 0;
434}
435
436#else
437
438static inline size_t evdev_event_size(void)
439{
440 return sizeof(struct input_event);
441}
442
443static int evdev_event_from_user(const char __user *buffer,
444 struct input_event *event)
445{
446 if (copy_from_user(event, buffer, sizeof(struct input_event)))
447 return -EFAULT;
448
449 return 0;
450}
451
452static int evdev_event_to_user(char __user *buffer,
453 const struct input_event *event)
454{
455 if (copy_to_user(buffer, event, sizeof(struct input_event)))
456 return -EFAULT;
457
458 return 0;
459}
460
461static int evdev_ff_effect_from_user(const char __user *buffer, size_t size,
462 struct ff_effect *effect)
463{
464 if (size != sizeof(struct ff_effect))
465 return -EINVAL;
466
467 if (copy_from_user(effect, buffer, sizeof(struct ff_effect)))
468 return -EFAULT;
469
470 return 0;
471}
472
473#endif /* CONFIG_COMPAT */
474
475static ssize_t evdev_write(struct file *file, const char __user *buffer, 294static ssize_t evdev_write(struct file *file, const char __user *buffer,
476 size_t count, loff_t *ppos) 295 size_t count, loff_t *ppos)
477{ 296{
@@ -491,14 +310,14 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
491 310
492 while (retval < count) { 311 while (retval < count) {
493 312
494 if (evdev_event_from_user(buffer + retval, &event)) { 313 if (input_event_from_user(buffer + retval, &event)) {
495 retval = -EFAULT; 314 retval = -EFAULT;
496 goto out; 315 goto out;
497 } 316 }
498 317
499 input_inject_event(&evdev->handle, 318 input_inject_event(&evdev->handle,
500 event.type, event.code, event.value); 319 event.type, event.code, event.value);
501 retval += evdev_event_size(); 320 retval += input_event_size();
502 } 321 }
503 322
504 out: 323 out:
@@ -532,7 +351,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
532 struct input_event event; 351 struct input_event event;
533 int retval; 352 int retval;
534 353
535 if (count < evdev_event_size()) 354 if (count < input_event_size())
536 return -EINVAL; 355 return -EINVAL;
537 356
538 if (client->head == client->tail && evdev->exist && 357 if (client->head == client->tail && evdev->exist &&
@@ -547,13 +366,13 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
547 if (!evdev->exist) 366 if (!evdev->exist)
548 return -ENODEV; 367 return -ENODEV;
549 368
550 while (retval + evdev_event_size() <= count && 369 while (retval + input_event_size() <= count &&
551 evdev_fetch_next_event(client, &event)) { 370 evdev_fetch_next_event(client, &event)) {
552 371
553 if (evdev_event_to_user(buffer + retval, &event)) 372 if (input_event_to_user(buffer + retval, &event))
554 return -EFAULT; 373 return -EFAULT;
555 374
556 retval += evdev_event_size(); 375 retval += input_event_size();
557 } 376 }
558 377
559 return retval; 378 return retval;
@@ -824,7 +643,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
824 643
825 if (_IOC_NR(cmd) == _IOC_NR(EVIOCSFF)) { 644 if (_IOC_NR(cmd) == _IOC_NR(EVIOCSFF)) {
826 645
827 if (evdev_ff_effect_from_user(p, _IOC_SIZE(cmd), &effect)) 646 if (input_ff_effect_from_user(p, _IOC_SIZE(cmd), &effect))
828 return -EFAULT; 647 return -EFAULT;
829 648
830 error = input_ff_upload(dev, &effect, file); 649 error = input_ff_upload(dev, &effect, file);
diff --git a/drivers/input/input-compat.c b/drivers/input/input-compat.c
new file mode 100644
index 000000000000..1accb89ae66f
--- /dev/null
+++ b/drivers/input/input-compat.c
@@ -0,0 +1,135 @@
1/*
2 * 32bit compatibility wrappers for the input subsystem.
3 *
4 * Very heavily based on evdev.c - Copyright (c) 1999-2002 Vojtech Pavlik
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published by
8 * the Free Software Foundation.
9 */
10
11#include <asm/uaccess.h>
12#include "input-compat.h"
13
14#ifdef CONFIG_COMPAT
15
16int input_event_from_user(const char __user *buffer,
17 struct input_event *event)
18{
19 if (INPUT_COMPAT_TEST) {
20 struct input_event_compat compat_event;
21
22 if (copy_from_user(&compat_event, buffer,
23 sizeof(struct input_event_compat)))
24 return -EFAULT;
25
26 event->time.tv_sec = compat_event.time.tv_sec;
27 event->time.tv_usec = compat_event.time.tv_usec;
28 event->type = compat_event.type;
29 event->code = compat_event.code;
30 event->value = compat_event.value;
31
32 } else {
33 if (copy_from_user(event, buffer, sizeof(struct input_event)))
34 return -EFAULT;
35 }
36
37 return 0;
38}
39
40int input_event_to_user(char __user *buffer,
41 const struct input_event *event)
42{
43 if (INPUT_COMPAT_TEST) {
44 struct input_event_compat compat_event;
45
46 compat_event.time.tv_sec = event->time.tv_sec;
47 compat_event.time.tv_usec = event->time.tv_usec;
48 compat_event.type = event->type;
49 compat_event.code = event->code;
50 compat_event.value = event->value;
51
52 if (copy_to_user(buffer, &compat_event,
53 sizeof(struct input_event_compat)))
54 return -EFAULT;
55
56 } else {
57 if (copy_to_user(buffer, event, sizeof(struct input_event)))
58 return -EFAULT;
59 }
60
61 return 0;
62}
63
64int input_ff_effect_from_user(const char __user *buffer, size_t size,
65 struct ff_effect *effect)
66{
67 if (INPUT_COMPAT_TEST) {
68 struct ff_effect_compat *compat_effect;
69
70 if (size != sizeof(struct ff_effect_compat))
71 return -EINVAL;
72
73 /*
74 * It so happens that the pointer which needs to be changed
75 * is the last field in the structure, so we can retrieve the
76 * whole thing and replace just the pointer.
77 */
78 compat_effect = (struct ff_effect_compat *)effect;
79
80 if (copy_from_user(compat_effect, buffer,
81 sizeof(struct ff_effect_compat)))
82 return -EFAULT;
83
84 if (compat_effect->type == FF_PERIODIC &&
85 compat_effect->u.periodic.waveform == FF_CUSTOM)
86 effect->u.periodic.custom_data =
87 compat_ptr(compat_effect->u.periodic.custom_data);
88 } else {
89 if (size != sizeof(struct ff_effect))
90 return -EINVAL;
91
92 if (copy_from_user(effect, buffer, sizeof(struct ff_effect)))
93 return -EFAULT;
94 }
95
96 return 0;
97}
98
99#else
100
101int input_event_from_user(const char __user *buffer,
102 struct input_event *event)
103{
104 if (copy_from_user(event, buffer, sizeof(struct input_event)))
105 return -EFAULT;
106
107 return 0;
108}
109
110int input_event_to_user(char __user *buffer,
111 const struct input_event *event)
112{
113 if (copy_to_user(buffer, event, sizeof(struct input_event)))
114 return -EFAULT;
115
116 return 0;
117}
118
119int input_ff_effect_from_user(const char __user *buffer, size_t size,
120 struct ff_effect *effect)
121{
122 if (size != sizeof(struct ff_effect))
123 return -EINVAL;
124
125 if (copy_from_user(effect, buffer, sizeof(struct ff_effect)))
126 return -EFAULT;
127
128 return 0;
129}
130
131#endif /* CONFIG_COMPAT */
132
133EXPORT_SYMBOL_GPL(input_event_from_user);
134EXPORT_SYMBOL_GPL(input_event_to_user);
135EXPORT_SYMBOL_GPL(input_ff_effect_from_user);
diff --git a/drivers/input/input-compat.h b/drivers/input/input-compat.h
new file mode 100644
index 000000000000..47cd9eaee66a
--- /dev/null
+++ b/drivers/input/input-compat.h
@@ -0,0 +1,94 @@
1#ifndef _INPUT_COMPAT_H
2#define _INPUT_COMPAT_H
3
4/*
5 * 32bit compatibility wrappers for the input subsystem.
6 *
7 * Very heavily based on evdev.c - Copyright (c) 1999-2002 Vojtech Pavlik
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License version 2 as published by
11 * the Free Software Foundation.
12 */
13
14#include <linux/compiler.h>
15#include <linux/compat.h>
16#include <linux/input.h>
17
18#ifdef CONFIG_COMPAT
19
20/* Note to the author of this code: did it ever occur to
21 you why the ifdefs are needed? Think about it again. -AK */
22#ifdef CONFIG_X86_64
23# define INPUT_COMPAT_TEST is_compat_task()
24#elif defined(CONFIG_IA64)
25# define INPUT_COMPAT_TEST IS_IA32_PROCESS(task_pt_regs(current))
26#elif defined(CONFIG_S390)
27# define INPUT_COMPAT_TEST test_thread_flag(TIF_31BIT)
28#elif defined(CONFIG_MIPS)
29# define INPUT_COMPAT_TEST test_thread_flag(TIF_32BIT_ADDR)
30#else
31# define INPUT_COMPAT_TEST test_thread_flag(TIF_32BIT)
32#endif
33
34struct input_event_compat {
35 struct compat_timeval time;
36 __u16 type;
37 __u16 code;
38 __s32 value;
39};
40
41struct ff_periodic_effect_compat {
42 __u16 waveform;
43 __u16 period;
44 __s16 magnitude;
45 __s16 offset;
46 __u16 phase;
47
48 struct ff_envelope envelope;
49
50 __u32 custom_len;
51 compat_uptr_t custom_data;
52};
53
54struct ff_effect_compat {
55 __u16 type;
56 __s16 id;
57 __u16 direction;
58 struct ff_trigger trigger;
59 struct ff_replay replay;
60
61 union {
62 struct ff_constant_effect constant;
63 struct ff_ramp_effect ramp;
64 struct ff_periodic_effect_compat periodic;
65 struct ff_condition_effect condition[2]; /* One for each axis */
66 struct ff_rumble_effect rumble;
67 } u;
68};
69
70static inline size_t input_event_size(void)
71{
72 return INPUT_COMPAT_TEST ?
73 sizeof(struct input_event_compat) : sizeof(struct input_event);
74}
75
76#else
77
78static inline size_t input_event_size(void)
79{
80 return sizeof(struct input_event);
81}
82
83#endif /* CONFIG_COMPAT */
84
85int input_event_from_user(const char __user *buffer,
86 struct input_event *event);
87
88int input_event_to_user(char __user *buffer,
89 const struct input_event *event);
90
91int input_ff_effect_from_user(const char __user *buffer, size_t size,
92 struct ff_effect *effect);
93
94#endif /* _INPUT_COMPAT_H */
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 223d56d5555b..46b7caeb2817 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -37,6 +37,7 @@
37#include <linux/fs.h> 37#include <linux/fs.h>
38#include <linux/miscdevice.h> 38#include <linux/miscdevice.h>
39#include <linux/uinput.h> 39#include <linux/uinput.h>
40#include "../input-compat.h"
40 41
41static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) 42static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
42{ 43{
@@ -78,6 +79,7 @@ static struct uinput_request* uinput_request_find(struct uinput_device *udev, in
78 /* Find an input request, by ID. Returns NULL if the ID isn't valid. */ 79 /* Find an input request, by ID. Returns NULL if the ID isn't valid. */
79 if (id >= UINPUT_NUM_REQUESTS || id < 0) 80 if (id >= UINPUT_NUM_REQUESTS || id < 0)
80 return NULL; 81 return NULL;
82
81 return udev->requests[id]; 83 return udev->requests[id];
82} 84}
83 85
@@ -127,6 +129,17 @@ static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *eff
127 struct uinput_request request; 129 struct uinput_request request;
128 int retval; 130 int retval;
129 131
132 /*
133 * uinput driver does not currently support periodic effects with
134 * custom waveform since it does not have a way to pass buffer of
135 * samples (custom_data) to userspace. If ever there is a device
136 * supporting custom waveforms we would need to define an additional
137 * ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out.
138 */
139 if (effect->type == FF_PERIODIC &&
140 effect->u.periodic.waveform == FF_CUSTOM)
141 return -EINVAL;
142
130 request.id = -1; 143 request.id = -1;
131 init_completion(&request.done); 144 init_completion(&request.done);
132 request.code = UI_FF_UPLOAD; 145 request.code = UI_FF_UPLOAD;
@@ -353,15 +366,15 @@ static inline ssize_t uinput_inject_event(struct uinput_device *udev, const char
353{ 366{
354 struct input_event ev; 367 struct input_event ev;
355 368
356 if (count != sizeof(struct input_event)) 369 if (count < input_event_size())
357 return -EINVAL; 370 return -EINVAL;
358 371
359 if (copy_from_user(&ev, buffer, sizeof(struct input_event))) 372 if (input_event_from_user(buffer, &ev))
360 return -EFAULT; 373 return -EFAULT;
361 374
362 input_event(udev->dev, ev.type, ev.code, ev.value); 375 input_event(udev->dev, ev.type, ev.code, ev.value);
363 376
364 return sizeof(struct input_event); 377 return input_event_size();
365} 378}
366 379
367static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) 380static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
@@ -407,13 +420,13 @@ static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count,
407 goto out; 420 goto out;
408 } 421 }
409 422
410 while (udev->head != udev->tail && retval + sizeof(struct input_event) <= count) { 423 while (udev->head != udev->tail && retval + input_event_size() <= count) {
411 if (copy_to_user(buffer + retval, &udev->buff[udev->tail], sizeof(struct input_event))) { 424 if (input_event_to_user(buffer + retval, &udev->buff[udev->tail])) {
412 retval = -EFAULT; 425 retval = -EFAULT;
413 goto out; 426 goto out;
414 } 427 }
415 udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE; 428 udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
416 retval += sizeof(struct input_event); 429 retval += input_event_size();
417 } 430 }
418 431
419 out: 432 out:
@@ -444,6 +457,93 @@ static int uinput_release(struct inode *inode, struct file *file)
444 return 0; 457 return 0;
445} 458}
446 459
460#ifdef CONFIG_COMPAT
461struct uinput_ff_upload_compat {
462 int request_id;
463 int retval;
464 struct ff_effect_compat effect;
465 struct ff_effect_compat old;
466};
467
468static int uinput_ff_upload_to_user(char __user *buffer,
469 const struct uinput_ff_upload *ff_up)
470{
471 if (INPUT_COMPAT_TEST) {
472 struct uinput_ff_upload_compat ff_up_compat;
473
474 ff_up_compat.request_id = ff_up->request_id;
475 ff_up_compat.retval = ff_up->retval;
476 /*
477 * It so happens that the pointer that gives us the trouble
478 * is the last field in the structure. Since we don't support
479 * custom waveforms in uinput anyway we can just copy the whole
480 * thing (to the compat size) and ignore the pointer.
481 */
482 memcpy(&ff_up_compat.effect, &ff_up->effect,
483 sizeof(struct ff_effect_compat));
484 memcpy(&ff_up_compat.old, &ff_up->old,
485 sizeof(struct ff_effect_compat));
486
487 if (copy_to_user(buffer, &ff_up_compat,
488 sizeof(struct uinput_ff_upload_compat)))
489 return -EFAULT;
490 } else {
491 if (copy_to_user(buffer, ff_up,
492 sizeof(struct uinput_ff_upload)))
493 return -EFAULT;
494 }
495
496 return 0;
497}
498
499static int uinput_ff_upload_from_user(const char __user *buffer,
500 struct uinput_ff_upload *ff_up)
501{
502 if (INPUT_COMPAT_TEST) {
503 struct uinput_ff_upload_compat ff_up_compat;
504
505 if (copy_from_user(&ff_up_compat, buffer,
506 sizeof(struct uinput_ff_upload_compat)))
507 return -EFAULT;
508
509 ff_up->request_id = ff_up_compat.request_id;
510 ff_up->retval = ff_up_compat.retval;
511 memcpy(&ff_up->effect, &ff_up_compat.effect,
512 sizeof(struct ff_effect_compat));
513 memcpy(&ff_up->old, &ff_up_compat.old,
514 sizeof(struct ff_effect_compat));
515
516 } else {
517 if (copy_from_user(ff_up, buffer,
518 sizeof(struct uinput_ff_upload)))
519 return -EFAULT;
520 }
521
522 return 0;
523}
524
525#else
526
527static int uinput_ff_upload_to_user(char __user *buffer,
528 const struct uinput_ff_upload *ff_up)
529{
530 if (copy_to_user(buffer, ff_up, sizeof(struct uinput_ff_upload)))
531 return -EFAULT;
532
533 return 0;
534}
535
536static int uinput_ff_upload_from_user(const char __user *buffer,
537 struct uinput_ff_upload *ff_up)
538{
539 if (copy_from_user(ff_up, buffer, sizeof(struct uinput_ff_upload)))
540 return -EFAULT;
541
542 return 0;
543}
544
545#endif
546
447#define uinput_set_bit(_arg, _bit, _max) \ 547#define uinput_set_bit(_arg, _bit, _max) \
448({ \ 548({ \
449 int __ret = 0; \ 549 int __ret = 0; \
@@ -455,19 +555,17 @@ static int uinput_release(struct inode *inode, struct file *file)
455 __ret; \ 555 __ret; \
456}) 556})
457 557
458static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 558static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
559 unsigned long arg, void __user *p)
459{ 560{
460 int retval; 561 int retval;
461 struct uinput_device *udev; 562 struct uinput_device *udev = file->private_data;
462 void __user *p = (void __user *)arg;
463 struct uinput_ff_upload ff_up; 563 struct uinput_ff_upload ff_up;
464 struct uinput_ff_erase ff_erase; 564 struct uinput_ff_erase ff_erase;
465 struct uinput_request *req; 565 struct uinput_request *req;
466 int length; 566 int length;
467 char *phys; 567 char *phys;
468 568
469 udev = file->private_data;
470
471 retval = mutex_lock_interruptible(&udev->mutex); 569 retval = mutex_lock_interruptible(&udev->mutex);
472 if (retval) 570 if (retval)
473 return retval; 571 return retval;
@@ -549,26 +647,24 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
549 break; 647 break;
550 648
551 case UI_BEGIN_FF_UPLOAD: 649 case UI_BEGIN_FF_UPLOAD:
552 if (copy_from_user(&ff_up, p, sizeof(ff_up))) { 650 retval = uinput_ff_upload_from_user(p, &ff_up);
553 retval = -EFAULT; 651 if (retval)
554 break; 652 break;
555 } 653
556 req = uinput_request_find(udev, ff_up.request_id); 654 req = uinput_request_find(udev, ff_up.request_id);
557 if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) { 655 if (!req || req->code != UI_FF_UPLOAD || !req->u.upload.effect) {
558 retval = -EINVAL; 656 retval = -EINVAL;
559 break; 657 break;
560 } 658 }
659
561 ff_up.retval = 0; 660 ff_up.retval = 0;
562 memcpy(&ff_up.effect, req->u.upload.effect, sizeof(struct ff_effect)); 661 ff_up.effect = *req->u.upload.effect;
563 if (req->u.upload.old) 662 if (req->u.upload.old)
564 memcpy(&ff_up.old, req->u.upload.old, sizeof(struct ff_effect)); 663 ff_up.old = *req->u.upload.old;
565 else 664 else
566 memset(&ff_up.old, 0, sizeof(struct ff_effect)); 665 memset(&ff_up.old, 0, sizeof(struct ff_effect));
567 666
568 if (copy_to_user(p, &ff_up, sizeof(ff_up))) { 667 retval = uinput_ff_upload_to_user(p, &ff_up);
569 retval = -EFAULT;
570 break;
571 }
572 break; 668 break;
573 669
574 case UI_BEGIN_FF_ERASE: 670 case UI_BEGIN_FF_ERASE:
@@ -576,29 +672,34 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
576 retval = -EFAULT; 672 retval = -EFAULT;
577 break; 673 break;
578 } 674 }
675
579 req = uinput_request_find(udev, ff_erase.request_id); 676 req = uinput_request_find(udev, ff_erase.request_id);
580 if (!(req && req->code == UI_FF_ERASE)) { 677 if (!req || req->code != UI_FF_ERASE) {
581 retval = -EINVAL; 678 retval = -EINVAL;
582 break; 679 break;
583 } 680 }
681
584 ff_erase.retval = 0; 682 ff_erase.retval = 0;
585 ff_erase.effect_id = req->u.effect_id; 683 ff_erase.effect_id = req->u.effect_id;
586 if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) { 684 if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
587 retval = -EFAULT; 685 retval = -EFAULT;
588 break; 686 break;
589 } 687 }
688
590 break; 689 break;
591 690
592 case UI_END_FF_UPLOAD: 691 case UI_END_FF_UPLOAD:
593 if (copy_from_user(&ff_up, p, sizeof(ff_up))) { 692 retval = uinput_ff_upload_from_user(p, &ff_up);
594 retval = -EFAULT; 693 if (retval)
595 break; 694 break;
596 } 695
597 req = uinput_request_find(udev, ff_up.request_id); 696 req = uinput_request_find(udev, ff_up.request_id);
598 if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) { 697 if (!req || req->code != UI_FF_UPLOAD ||
698 !req->u.upload.effect) {
599 retval = -EINVAL; 699 retval = -EINVAL;
600 break; 700 break;
601 } 701 }
702
602 req->retval = ff_up.retval; 703 req->retval = ff_up.retval;
603 uinput_request_done(udev, req); 704 uinput_request_done(udev, req);
604 break; 705 break;
@@ -608,11 +709,13 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
608 retval = -EFAULT; 709 retval = -EFAULT;
609 break; 710 break;
610 } 711 }
712
611 req = uinput_request_find(udev, ff_erase.request_id); 713 req = uinput_request_find(udev, ff_erase.request_id);
612 if (!(req && req->code == UI_FF_ERASE)) { 714 if (!req || req->code != UI_FF_ERASE) {
613 retval = -EINVAL; 715 retval = -EINVAL;
614 break; 716 break;
615 } 717 }
718
616 req->retval = ff_erase.retval; 719 req->retval = ff_erase.retval;
617 uinput_request_done(udev, req); 720 uinput_request_done(udev, req);
618 break; 721 break;
@@ -626,6 +729,18 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
626 return retval; 729 return retval;
627} 730}
628 731
732static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
733{
734 return uinput_ioctl_handler(file, cmd, arg, (void __user *)arg);
735}
736
737#ifdef CONFIG_COMPAT
738static long uinput_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
739{
740 return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg));
741}
742#endif
743
629static const struct file_operations uinput_fops = { 744static const struct file_operations uinput_fops = {
630 .owner = THIS_MODULE, 745 .owner = THIS_MODULE,
631 .open = uinput_open, 746 .open = uinput_open,
@@ -634,6 +749,9 @@ static const struct file_operations uinput_fops = {
634 .write = uinput_write, 749 .write = uinput_write,
635 .poll = uinput_poll, 750 .poll = uinput_poll,
636 .unlocked_ioctl = uinput_ioctl, 751 .unlocked_ioctl = uinput_ioctl,
752#ifdef CONFIG_COMPAT
753 .compat_ioctl = uinput_compat_ioctl,
754#endif
637}; 755};
638 756
639static struct miscdevice uinput_misc = { 757static struct miscdevice uinput_misc = {