BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file sys_evdev.c
3 * @author Ambroz Bizjak <ambrop7@gmail.com>
4 *
5 * @section LICENSE
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * @section DESCRIPTION
30 *
31 * Linux event device module.
32 *
33 * Synopsis: sys.evdev(string device)
34 * Description: reports input events from a Linux event device. Transitions up when an event is
35 * detected, and goes down waiting for the next event when sys.evdev::nextevent() is called.
36 * Variables:
37 * string type - symbolic event type (e.g. EV_KEY, EV_REL, EV_ABS), corresponding to
38 * (struct input_event).type, or "unknown"
39 * string value - event value (signed integer), equal to (struct input_event).value
40 * string code_numeric - numeric event code (unsigned integer), equal to
41 * (struct input_event).code
42 * string code - symbolic event code (e.g. KEY_ESC. KEY_1, KEY_2, BTN_LEFT), corrresponding
43 * to (struct input_event).code, or "unknown"
44 *
45 * Synopsis: sys.evdev::nextevent()
46 * Description: makes the evdev module transition down in order to report the next event.
47 */
48  
49 #include <stdlib.h>
50 #include <string.h>
51 #include <stdint.h>
52 #include <inttypes.h>
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include <fcntl.h>
56 #include <unistd.h>
57 #include <linux/input.h>
58  
59 #include <misc/nonblocking.h>
60 #include <misc/debug.h>
61  
62 #include <ncd/module_common.h>
63  
64 #include <generated/blog_channel_ncd_sys_evdev.h>
65  
66 #include "linux_input_names.h"
67  
68 struct instance {
69 NCDModuleInst *i;
70 int evdev_fd;
71 BFileDescriptor bfd;
72 int processing;
73 struct input_event event;
74 };
75  
76 static void instance_free (struct instance *o, int is_error);
77  
78 enum {STRING_VALUE, STRING_CODE_NUMERIC, STRING_CODE};
79  
80 static const char *strings[] = {
81 "value", "code_numeric", "code", NULL
82 };
83  
84 #define MAKE_LOOKUP_FUNC(_name_) \
85 static const char * evdev_##_name_##_to_str (uint16_t type) \
86 { \
87 if (type >= (sizeof(_name_##_names) / sizeof(_name_##_names[0])) || !_name_##_names[type]) { \
88 return "unknown"; \
89 } \
90 return _name_##_names[type]; \
91 }
92  
93 MAKE_LOOKUP_FUNC(type)
94 MAKE_LOOKUP_FUNC(syn)
95 MAKE_LOOKUP_FUNC(key)
96 MAKE_LOOKUP_FUNC(rel)
97 MAKE_LOOKUP_FUNC(abs)
98 MAKE_LOOKUP_FUNC(sw)
99 MAKE_LOOKUP_FUNC(msc)
100 MAKE_LOOKUP_FUNC(led)
101 MAKE_LOOKUP_FUNC(rep)
102 MAKE_LOOKUP_FUNC(snd)
103 MAKE_LOOKUP_FUNC(ffstatus)
104  
105 static void device_handler (struct instance *o, int events)
106 {
107 if (o->processing) {
108 ModuleLog(o->i, BLOG_ERROR, "device error");
109 instance_free(o, 1);
110 return;
111 }
112  
113 int res = read(o->evdev_fd, &o->event, sizeof(o->event));
114 if (res < 0) {
115 ModuleLog(o->i, BLOG_ERROR, "read failed");
116 instance_free(o, 1);
117 return;
118 }
119 if (res != sizeof(o->event)) {
120 ModuleLog(o->i, BLOG_ERROR, "read wrong");
121 instance_free(o, 1);
122 return;
123 }
124  
125 // stop reading
126 BReactor_SetFileDescriptorEvents(o->i->params->iparams->reactor, &o->bfd, 0);
127  
128 // set processing
129 o->processing = 1;
130  
131 // signal up
132 NCDModuleInst_Backend_Up(o->i);
133 }
134  
135 static void device_nextevent (struct instance *o)
136 {
137 ASSERT(o->processing)
138  
139 // start reading
140 BReactor_SetFileDescriptorEvents(o->i->params->iparams->reactor, &o->bfd, BREACTOR_READ);
141  
142 // set not processing
143 o->processing = 0;
144  
145 // signal down
146 NCDModuleInst_Backend_Down(o->i);
147 }
148  
149 static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
150 {
151 struct instance *o = vo;
152 o->i = i;
153  
154 // check arguments
155 NCDValRef device_arg;
156 if (!NCDVal_ListRead(params->args, 1, &device_arg)) {
157 ModuleLog(o->i, BLOG_ERROR, "wrong arity");
158 goto fail0;
159 }
160 if (!NCDVal_IsStringNoNulls(device_arg)) {
161 ModuleLog(o->i, BLOG_ERROR, "wrong type");
162 goto fail0;
163 }
164  
165 // null terminate device
166 NCDValNullTermString device_nts;
167 if (!NCDVal_StringNullTerminate(device_arg, &device_nts)) {
168 ModuleLog(i, BLOG_ERROR, "NCDVal_StringNullTerminate failed");
169 goto fail0;
170 }
171  
172 // open device
173 o->evdev_fd = open(device_nts.data, O_RDONLY);
174 NCDValNullTermString_Free(&device_nts);
175 if (o->evdev_fd < 0) {
176 ModuleLog(o->i, BLOG_ERROR, "open failed");
177 goto fail0;
178 }
179  
180 // set non-blocking
181 if (!badvpn_set_nonblocking(o->evdev_fd)) {
182 ModuleLog(o->i, BLOG_ERROR, "badvpn_set_nonblocking failed");
183 goto fail1;
184 }
185  
186 // init BFileDescriptor
187 BFileDescriptor_Init(&o->bfd, o->evdev_fd, (BFileDescriptor_handler)device_handler, o);
188 if (!BReactor_AddFileDescriptor(o->i->params->iparams->reactor, &o->bfd)) {
189 ModuleLog(o->i, BLOG_ERROR, "BReactor_AddFileDescriptor failed");
190 goto fail1;
191 }
192 BReactor_SetFileDescriptorEvents(o->i->params->iparams->reactor, &o->bfd, BREACTOR_READ);
193  
194 // set not processing
195 o->processing = 0;
196 return;
197  
198 fail1:
199 if (close(o->evdev_fd) < 0) {
200 ModuleLog(o->i, BLOG_ERROR, "close failed");
201 }
202 fail0:
203 NCDModuleInst_Backend_DeadError(i);
204 }
205  
206 void instance_free (struct instance *o, int is_error)
207 {
208 // free BFileDescriptor
209 BReactor_RemoveFileDescriptor(o->i->params->iparams->reactor, &o->bfd);
210  
211 // close device.
212 // Ignore close error which happens if the device is removed.
213 if (close(o->evdev_fd) < 0) {
214 ModuleLog(o->i, BLOG_ERROR, "close failed");
215 }
216  
217 if (is_error) {
218 NCDModuleInst_Backend_DeadError(o->i);
219 } else {
220 NCDModuleInst_Backend_Dead(o->i);
221 }
222 }
223  
224 static void func_die (void *vo)
225 {
226 struct instance *o = vo;
227 instance_free(o, 0);
228 }
229  
230 static int func_getvar2 (void *vo, NCD_string_id_t name, NCDValMem *mem, NCDValRef *out)
231 {
232 struct instance *o = vo;
233 ASSERT(o->processing)
234  
235 if (name == NCD_STRING_TYPE) {
236 *out = NCDVal_NewString(mem, evdev_type_to_str(o->event.type));
237 return 1;
238 }
239  
240 if (name == ModuleString(o->i, STRING_VALUE)) {
241 char str[50];
242 snprintf(str, sizeof(str), "%"PRIi32, o->event.value);
243 *out = NCDVal_NewString(mem, str);
244 return 1;
245 }
246  
247 if (name == ModuleString(o->i, STRING_CODE_NUMERIC)) {
248 *out = ncd_make_uintmax(mem, o->event.code);
249 return 1;
250 }
251  
252 if (name == ModuleString(o->i, STRING_CODE)) {
253 const char *str = "unknown";
254  
255 #define MAKE_CASE(_evname_, _name_) \
256 case _evname_: \
257 str = evdev_##_name_##_to_str(o->event.code); \
258 break;
259  
260 switch (o->event.type) {
261 #ifdef EV_KEY
262 MAKE_CASE(EV_KEY, key)
263 #endif
264 #ifdef EV_SYN
265 MAKE_CASE(EV_SYN, syn)
266 #endif
267 #ifdef EV_REL
268 MAKE_CASE(EV_REL, rel)
269 #endif
270 #ifdef EV_ABS
271 MAKE_CASE(EV_ABS, abs)
272 #endif
273 #ifdef EV_SW
274 MAKE_CASE(EV_SW, sw)
275 #endif
276 #ifdef EV_MSC
277 MAKE_CASE(EV_MSC, msc)
278 #endif
279 #ifdef EV_LED
280 MAKE_CASE(EV_LED, led)
281 #endif
282 #ifdef EV_REP
283 MAKE_CASE(EV_REP, rep)
284 #endif
285 #ifdef EV_SND
286 MAKE_CASE(EV_SND, snd)
287 #endif
288 #ifdef EV_FF_STATUS
289 MAKE_CASE(EV_FF_STATUS, ffstatus)
290 #endif
291 }
292  
293 *out = NCDVal_NewString(mem, str);
294 return 1;
295 }
296  
297 return 0;
298 }
299  
300 static void nextevent_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
301 {
302 // check arguments
303 if (!NCDVal_ListRead(params->args, 0)) {
304 ModuleLog(i, BLOG_ERROR, "wrong arity");
305 goto fail0;
306 }
307  
308 // get method object
309 struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user);
310  
311 // make sure we are currently reporting an event
312 if (!mo->processing) {
313 ModuleLog(i, BLOG_ERROR, "not reporting an event");
314 goto fail0;
315 }
316  
317 // signal up.
318 // Do it before finishing the event so our process does not advance any further if
319 // we would be killed the event provider going down.
320 NCDModuleInst_Backend_Up(i);
321  
322 // wait for next event
323 device_nextevent(mo);
324 return;
325  
326 fail0:
327 NCDModuleInst_Backend_DeadError(i);
328 }
329  
330 static struct NCDModule modules[] = {
331 {
332 .type = "sys.evdev",
333 .func_new2 = func_new,
334 .func_die = func_die,
335 .func_getvar2 = func_getvar2,
336 .alloc_size = sizeof(struct instance)
337 }, {
338 .type = "sys.evdev::nextevent",
339 .func_new2 = nextevent_func_new
340 }, {
341 .type = NULL
342 }
343 };
344  
345 const struct NCDModuleGroup ncdmodule_sys_evdev = {
346 .modules = modules,
347 .strings = strings
348 };