diff options
author | Philip Langdale <philipl@overt.org> | 2008-10-16 22:31:42 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2008-10-27 22:03:42 -0400 |
commit | 2d56f3a32c0e62f99c043d2579840f9731fe5855 (patch) | |
tree | 3bf1539bbed43e5309dcfd634f202bb9ad0f11b2 /drivers/input/evdev.c | |
parent | 49fdf6785fd660e18a1eb4588928f47e9fa29a9a (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/input/evdev.c')
-rw-r--r-- | drivers/input/evdev.c | 197 |
1 files changed, 8 insertions, 189 deletions
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 | ||
24 | struct evdev { | 24 | struct 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 | |||
296 | struct input_event_compat { | ||
297 | struct compat_timeval time; | ||
298 | __u16 type; | ||
299 | __u16 code; | ||
300 | __s32 value; | ||
301 | }; | ||
302 | |||
303 | struct 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 | |||
316 | struct 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 | |||
346 | static inline size_t evdev_event_size(void) | ||
347 | { | ||
348 | return COMPAT_TEST ? | ||
349 | sizeof(struct input_event_compat) : sizeof(struct input_event); | ||
350 | } | ||
351 | |||
352 | static 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 | |||
376 | static 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 | |||
400 | static 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 | |||
438 | static inline size_t evdev_event_size(void) | ||
439 | { | ||
440 | return sizeof(struct input_event); | ||
441 | } | ||
442 | |||
443 | static 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 | |||
452 | static 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 | |||
461 | static 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 | |||
475 | static ssize_t evdev_write(struct file *file, const char __user *buffer, | 294 | static 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); |