input-plugin: don't leak fd on error path.
[profile/ivi/speech-recognition.git] / src / plugins / input-handler / input-plugin.c
1 /*
2  * Copyright (c) 2012, 2013, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *   * Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *   * Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *   * Neither the name of Intel Corporation nor the names of its contributors
14  *     may be used to endorse or promote products derived from this software
15  *     without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <linux/input.h>
33 #include <libudev.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <locale.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <errno.h>
40
41 #include <pulse/mainloop.h>
42 #include <pulse/mainloop-api.h>
43
44 #include <murphy/common/debug.h>
45
46 #include "srs/daemon/plugin.h"
47 #include "srs/daemon/context.h"
48 #include "srs/daemon/recognizer.h"
49
50 #define PLUGIN_NAME    "input-handler"
51 #define PLUGIN_DESCR   "For activating/deactivating voice recognition"
52 #define PLUGIN_AUTHORS "Janos Kovacs <janos.kovacs@intel.com>"
53 #define PLUGIN_VERSION "0.0.1"
54
55
56 typedef struct context_s  context_t;
57 typedef struct input_s    input_t;
58
59 struct context_s {
60     srs_plugin_t *plugin;
61     struct udev *udev;
62     size_t nkbd;
63     input_t *kbds;
64     uint16_t key;
65     uint32_t state;
66 };
67
68 struct input_s {
69     const char *path;
70     const char *id;
71     int fd;
72     pa_io_event *paev;
73 };
74
75 static void input_event_cb(pa_mainloop_api *, pa_io_event *, int,
76                            pa_io_event_flags_t, void *);
77 static void scan_devices(context_t *);
78 static void handle_device(context_t *, struct udev_device *);
79 static int add_input(context_t *, const char *, size_t *, input_t **);
80 static int remove_input(context_t *, const char *, size_t *, input_t **);
81
82
83 static int create_input(srs_plugin_t *plugin)
84 {
85     context_t *ctx;
86     struct udev *udev;
87
88     mrp_log_info("creating input plugin");
89
90     if ((ctx = mrp_allocz(sizeof(context_t))) && (udev = udev_new())) {
91         ctx->plugin = plugin;
92         ctx->udev = udev;
93
94         plugin->plugin_data = ctx;
95
96         return TRUE;
97     }
98
99     return FALSE;
100 }
101
102
103 static int config_input(srs_plugin_t *plugin, srs_cfg_t *settings)
104 {
105     context_t *ctx = (context_t *)plugin->plugin_data;
106
107     MRP_UNUSED(settings);
108
109     mrp_log_info("configuring input plugin");
110
111     ctx->key = KEY_PAUSE;
112
113     return TRUE;
114 }
115
116
117 static int start_input(srs_plugin_t *plugin)
118 {
119     context_t *ctx = (context_t *)plugin->plugin_data;
120
121     mrp_log_info("starting input plugin");
122
123     if (ctx->key) {
124         scan_devices(ctx);
125     }
126
127     return TRUE;
128 }
129
130
131 static void stop_input(srs_plugin_t *plugin)
132 {
133     context_t *ctx = (context_t *)plugin->plugin_data;
134
135     mrp_log_info("stopping input plugin");
136
137     if (ctx) {
138         udev_unref(ctx->udev);
139
140         while (ctx->nkbd > 0)
141             remove_input(ctx, ctx->kbds->path, &ctx->nkbd, &ctx->kbds);
142         mrp_free(ctx->kbds);
143
144         ctx->nkbd = 0;
145         ctx->kbds = NULL;
146     }
147
148     return;
149 }
150
151
152 static void destroy_input(srs_plugin_t *plugin)
153 {
154     context_t *ctx = (context_t *)plugin->plugin_data;
155
156     mrp_log_info("destroying input plugin");
157
158     if (ctx) {
159         mrp_free(ctx);
160     }
161 }
162
163 static void input_event_cb(pa_mainloop_api *ea,
164                            pa_io_event *ioev,
165                            int fd,
166                            pa_io_event_flags_t events,
167                            void *userdata)
168 {
169     context_t *ctx = (void *)userdata;
170     srs_plugin_t *plugin;
171     srs_context_t *srs;
172     struct input_event inpev;
173     int rd;
174
175     MRP_UNUSED(ea);
176     MRP_UNUSED(ioev);
177     MRP_UNUSED(events);
178
179     if (ctx && (plugin = ctx->plugin) && (srs = plugin->srs)) {
180         for (;;) {
181             if ((rd = read(fd, &inpev, sizeof(inpev))) != sizeof(inpev)) {
182                 if (rd < 0 && errno == EINTR)
183                     continue;
184                 else {
185                     mrp_debug("input plugin: failed to read input event");
186                     return;
187                 }
188             }
189
190             if (inpev.type == EV_KEY && inpev.value == 1) {
191                 if (inpev.code == ctx->key) {
192                     if ((ctx->state ^= 1))
193                         srs_activate_srec(srs, "sphinx-speech");
194                     else
195                         srs_deactivate_srec(srs, "sphinx-speech");
196                 }
197             }
198
199             return;
200        } /* for ;; */
201     }
202 }
203
204
205 static void scan_devices(context_t *ctx)
206 {
207     struct udev *udev;
208     struct udev_enumerate *enm;
209     struct udev_list_entry *list, *entry;
210     struct udev_device *dev;
211     const char *syspath;
212
213     if (!ctx || !(udev = ctx->udev))
214         return;
215
216     enm = udev_enumerate_new(udev);
217
218     udev_enumerate_add_match_subsystem(enm, "input");
219     udev_enumerate_scan_devices(enm);
220     list = udev_enumerate_get_list_entry(enm);
221
222     udev_list_entry_foreach(entry, list) {
223         syspath = udev_list_entry_get_name(entry);
224         if ((dev = udev_device_new_from_syspath(udev, syspath))) {
225             handle_device(ctx, dev);
226             udev_device_unref(dev);
227         }
228     }
229
230     udev_enumerate_unref(enm);
231 }
232
233 static void handle_device(context_t *ctx, struct udev_device *dev)
234 {
235     const char *path;
236     const char *kbd;
237     const char *key;
238
239     if ((path = udev_device_get_property_value(dev, "DEVNAME"))) {
240         key = udev_device_get_property_value(dev, "ID_INPUT_KEY");
241         kbd = udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD");
242
243         if (key) {
244         }
245
246         if (kbd && ctx->key) {
247             add_input(ctx, path, &ctx->nkbd, &ctx->kbds);
248         }
249     }
250 }
251
252 static int add_input(context_t *ctx,
253                      const char *path,
254                      size_t *pninput,
255                      input_t **pinputs)
256 {
257     static pa_io_event_flags_t flags = PA_IO_EVENT_INPUT;
258
259     srs_plugin_t *plugin = ctx->plugin;
260     srs_context_t *srs = plugin->srs;
261     pa_mainloop_api *mlapi = srs->pa;
262     char id[512];
263     size_t idx;
264     input_t *inp;
265     size_t size;
266     int fd;
267
268     if (!mlapi || !path || (fd = open(path, O_RDONLY)) < 0)
269         return -1;
270
271     if (ioctl(fd, EVIOCGNAME(sizeof(id)), id) < 0) {
272         close(fd);
273         return -1;
274     }
275
276     idx = *pninput;
277     size = sizeof(input_t) * (idx + 1);
278
279     if (!(*pinputs = mrp_realloc(*pinputs, size))) {
280         close(fd);
281         *pninput = 0;
282         return -1;
283     }
284
285     *pninput = idx + 1;
286     inp = *pinputs + idx;
287
288     inp->path = mrp_strdup(path);
289     inp->id = mrp_strdup(id);
290     inp->fd = fd;
291     inp->paev = mlapi->io_new(mlapi, fd, flags, input_event_cb, ctx);
292
293     if (!inp->path || !inp->id || !inp->paev) {
294         mrp_free((void *)inp->path);
295         mrp_free((void *)inp->id);
296         if (inp->paev)
297             mlapi->io_free(inp->paev);
298         close(inp->fd);
299         *pninput = idx;
300         return -1;
301     }
302
303     mrp_log_info("input plugin: added event source '%s'", id);
304
305     return 0;
306 }
307
308 static int remove_input(context_t *ctx,
309                         const char *path,
310                         size_t *pninput,
311                         input_t **pinputs)
312 {
313     srs_plugin_t *plugin = ctx->plugin;
314     srs_context_t *srs = plugin->srs;
315     pa_mainloop_api *mlapi = srs->pa;
316     input_t *inp, *inputs;
317     size_t i, ninput, size;
318
319     if (!mlapi || !path)
320         return -1;
321
322     inputs = *pinputs;
323     ninput = *pninput;
324
325     for (i = 0;  i < ninput;  i++) {
326         inp = inputs + i;
327
328         if (!strcmp(path, inp->path)) {
329             if (inp->paev)
330                 mlapi->io_free(inp->paev);
331             if (inp->fd >= 0)
332                 close(inp->fd);
333             mrp_free((void *)inp->path);
334             mrp_free((void *)inp->id);
335
336             size = (ninput - (i + 1)) * sizeof(input_t);
337
338             if (size > 0)
339                 memmove(inp + 1, inp, size);
340
341             *pninput = ninput - 1;
342
343             return 0;
344         }
345     }
346
347     return -1;
348 }
349
350 SRS_DECLARE_PLUGIN(PLUGIN_NAME, PLUGIN_DESCR, PLUGIN_AUTHORS, PLUGIN_VERSION,
351                    create_input, config_input, start_input, stop_input,
352                    destroy_input)
353
354
355 /*
356  * Local Variables:
357  * c-basic-offset: 4
358  * indent-tabs-mode: nil
359  * End:
360  *
361  */