xref: /linux/drivers/input/input-compat.c (revision 9a44b828dd7c0be1d0d6d6cf178d9727ceb6da6e)
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 
15 int 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(&compat_event, buffer,
22 				   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(event, buffer, sizeof(struct input_event)))
33 			return -EFAULT;
34 	}
35 
36 	return 0;
37 }
38 
39 int 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(buffer, &compat_event,
52 				 sizeof(struct input_event_compat)))
53 			return -EFAULT;
54 
55 	} else {
56 		if (copy_to_user(buffer, event, sizeof(struct input_event)))
57 			return -EFAULT;
58 	}
59 
60 	return 0;
61 }
62 
63 int 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 		memset(effect, 0, sizeof(*effect));
80 
81 		if (copy_from_user(compat_effect, buffer,
82 				   sizeof(struct ff_effect_compat)))
83 			return -EFAULT;
84 
85 		if (compat_effect->type == FF_PERIODIC &&
86 		    compat_effect->u.periodic.waveform == FF_CUSTOM)
87 			effect->u.periodic.custom_data =
88 				compat_ptr(compat_effect->u.periodic.custom_data);
89 	} else {
90 		if (size != sizeof(struct ff_effect))
91 			return -EINVAL;
92 
93 		if (copy_from_user(effect, buffer, sizeof(struct ff_effect)))
94 			return -EFAULT;
95 	}
96 
97 	return 0;
98 }
99 
100 int input_bits_to_string(char *buf, int buf_size, unsigned long bits,
101 			 bool skip_empty)
102 {
103 	int len = 0;
104 
105 	if (in_compat_syscall()) {
106 		u32 dword = bits >> 32;
107 		if (dword || !skip_empty)
108 			len += snprintf(buf, buf_size, "%x ", dword);
109 
110 		dword = bits & 0xffffffffUL;
111 		if (dword || !skip_empty || len)
112 			len += snprintf(buf + len, max(buf_size - len, 0),
113 					"%x", dword);
114 	} else {
115 		if (bits || !skip_empty)
116 			len += snprintf(buf, buf_size, "%lx", bits);
117 	}
118 
119 	return len;
120 }
121 
122 #else
123 
124 int input_event_from_user(const char __user *buffer,
125 			 struct input_event *event)
126 {
127 	if (copy_from_user(event, buffer, sizeof(struct input_event)))
128 		return -EFAULT;
129 
130 	return 0;
131 }
132 
133 int input_event_to_user(char __user *buffer,
134 			const struct input_event *event)
135 {
136 	if (copy_to_user(buffer, event, sizeof(struct input_event)))
137 		return -EFAULT;
138 
139 	return 0;
140 }
141 
142 int input_ff_effect_from_user(const char __user *buffer, size_t size,
143 			      struct ff_effect *effect)
144 {
145 	if (size != sizeof(struct ff_effect))
146 		return -EINVAL;
147 
148 	if (copy_from_user(effect, buffer, sizeof(struct ff_effect)))
149 		return -EFAULT;
150 
151 	return 0;
152 }
153 
154 int input_bits_to_string(char *buf, int buf_size, unsigned long bits,
155 			 bool skip_empty)
156 {
157 	return bits || !skip_empty ?
158 		snprintf(buf, buf_size, "%lx", bits) : 0;
159 }
160 
161 #endif /* CONFIG_COMPAT */
162 
163 EXPORT_SYMBOL_GPL(input_event_from_user);
164 EXPORT_SYMBOL_GPL(input_event_to_user);
165 EXPORT_SYMBOL_GPL(input_ff_effect_from_user);
166