1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * 32bit compatibility wrappers for the input subsystem.
4 *
5 * Very heavily based on evdev.c - Copyright (c) 1999-2002 Vojtech Pavlik
6 */
7
8#include <linux/export.h>
9#include <linux/sprintf.h>
10#include <linux/uaccess.h>
11#include "input-compat.h"
12
13#ifdef CONFIG_COMPAT
14
15int input_event_from_user(const char __user *buffer,
16 struct input_event *event)
17{
18 if (in_compat_syscall() && !COMPAT_USE_64BIT_TIME) {
19 struct input_event_compat compat_event;
20
21 if (copy_from_user(to: &compat_event, from: buffer,
22 n: sizeof(struct input_event_compat)))
23 return -EFAULT;
24
25 event->input_event_sec = compat_event.sec;
26 event->input_event_usec = compat_event.usec;
27 event->type = compat_event.type;
28 event->code = compat_event.code;
29 event->value = compat_event.value;
30
31 } else {
32 if (copy_from_user(to: event, from: buffer, n: sizeof(struct input_event)))
33 return -EFAULT;
34 }
35
36 return 0;
37}
38
39int input_event_to_user(char __user *buffer,
40 const struct input_event *event)
41{
42 if (in_compat_syscall() && !COMPAT_USE_64BIT_TIME) {
43 struct input_event_compat compat_event;
44
45 compat_event.sec = event->input_event_sec;
46 compat_event.usec = event->input_event_usec;
47 compat_event.type = event->type;
48 compat_event.code = event->code;
49 compat_event.value = event->value;
50
51 if (copy_to_user(to: buffer, from: &compat_event,
52 n: sizeof(struct input_event_compat)))
53 return -EFAULT;
54
55 } else {
56 if (copy_to_user(to: buffer, from: event, n: sizeof(struct input_event)))
57 return -EFAULT;
58 }
59
60 return 0;
61}
62
63int input_ff_effect_from_user(const char __user *buffer, size_t size,
64 struct ff_effect *effect)
65{
66 if (in_compat_syscall()) {
67 struct ff_effect_compat *compat_effect;
68
69 if (size != sizeof(struct ff_effect_compat))
70 return -EINVAL;
71
72 /*
73 * It so happens that the pointer which needs to be changed
74 * is the last field in the structure, so we can retrieve the
75 * whole thing and replace just the pointer.
76 */
77 compat_effect = (struct ff_effect_compat *)effect;
78
79 if (copy_from_user(to: compat_effect, from: buffer,
80 n: sizeof(struct ff_effect_compat)))
81 return -EFAULT;
82
83 if (compat_effect->type == FF_PERIODIC &&
84 compat_effect->u.periodic.waveform == FF_CUSTOM)
85 effect->u.periodic.custom_data =
86 compat_ptr(uptr: compat_effect->u.periodic.custom_data);
87 } else {
88 if (size != sizeof(struct ff_effect))
89 return -EINVAL;
90
91 if (copy_from_user(to: effect, from: buffer, n: sizeof(struct ff_effect)))
92 return -EFAULT;
93 }
94
95 return 0;
96}
97
98int input_bits_to_string(char *buf, int buf_size, unsigned long bits,
99 bool skip_empty)
100{
101 int len = 0;
102
103 if (in_compat_syscall()) {
104 u32 dword = bits >> 32;
105 if (dword || !skip_empty)
106 len += snprintf(buf, size: buf_size, fmt: "%x ", dword);
107
108 dword = bits & 0xffffffffUL;
109 if (dword || !skip_empty || len)
110 len += snprintf(buf: buf + len, max(buf_size - len, 0),
111 fmt: "%x", dword);
112 } else {
113 if (bits || !skip_empty)
114 len += snprintf(buf, size: buf_size, fmt: "%lx", bits);
115 }
116
117 return len;
118}
119
120#else
121
122int input_event_from_user(const char __user *buffer,
123 struct input_event *event)
124{
125 if (copy_from_user(event, buffer, sizeof(struct input_event)))
126 return -EFAULT;
127
128 return 0;
129}
130
131int input_event_to_user(char __user *buffer,
132 const struct input_event *event)
133{
134 if (copy_to_user(buffer, event, sizeof(struct input_event)))
135 return -EFAULT;
136
137 return 0;
138}
139
140int input_ff_effect_from_user(const char __user *buffer, size_t size,
141 struct ff_effect *effect)
142{
143 if (size != sizeof(struct ff_effect))
144 return -EINVAL;
145
146 if (copy_from_user(effect, buffer, sizeof(struct ff_effect)))
147 return -EFAULT;
148
149 return 0;
150}
151
152int input_bits_to_string(char *buf, int buf_size, unsigned long bits,
153 bool skip_empty)
154{
155 return bits || !skip_empty ?
156 snprintf(buf, buf_size, "%lx", bits) : 0;
157}
158
159#endif /* CONFIG_COMPAT */
160
161EXPORT_SYMBOL_GPL(input_event_from_user);
162EXPORT_SYMBOL_GPL(input_event_to_user);
163EXPORT_SYMBOL_GPL(input_ff_effect_from_user);
164