1 /*  RetroArch - A frontend for libretro.
2  *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
3  *  Copyright (C) 2011-2017 - Daniel De Matteis
4  *
5  *  RetroArch is free software: you can redistribute it and/or modify it under the terms
6  *  of the GNU General Public License as published by the Free Software Found-
7  *  ation, either version 3 of the License, or (at your option) any later version.
8  *
9  *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11  *  PURPOSE.  See the GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License along with RetroArch.
14  *  If not, see <http://www.gnu.org/licenses/>.
15  */
16 #include <stdint.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <string.h>
20 #include <limits.h>
21 #include <errno.h>
22 #include <stdio.h>
23 
24 #include <sys/types.h>
25 #include <sys/inotify.h>
26 #include <linux/joystick.h>
27 
28 #include <fcntl.h>
29 #include <sys/epoll.h>
30 
31 #include <compat/strl.h>
32 #include <string/stdstring.h>
33 
34 #include "../input_driver.h"
35 
36 #include "../../verbosity.h"
37 #include "../../tasks/tasks_internal.h"
38 
39 #define NUM_BUTTONS 32
40 #define NUM_AXES 32
41 
42 struct linuxraw_joypad
43 {
44    int fd;
45    uint32_t buttons;
46    int16_t axes[NUM_AXES];
47 
48    char *ident;
49 };
50 
51 /* TODO/FIXME - static globals */
52 static struct linuxraw_joypad linuxraw_pads[MAX_USERS];
53 static int linuxraw_epoll                              = 0;
54 static int linuxraw_inotify                            = 0;
55 static bool linuxraw_hotplug                           = false;
56 
linuxraw_poll_pad(struct linuxraw_joypad * pad)57 static void linuxraw_poll_pad(struct linuxraw_joypad *pad)
58 {
59    struct js_event event;
60 
61    while (read(pad->fd, &event, sizeof(event)) == (ssize_t)sizeof(event))
62    {
63       unsigned type = event.type & ~JS_EVENT_INIT;
64 
65       switch (type)
66       {
67          case JS_EVENT_BUTTON:
68             if (event.number < NUM_BUTTONS)
69             {
70                if (event.value)
71                   BIT32_SET(pad->buttons, event.number);
72                else
73                   BIT32_CLEAR(pad->buttons, event.number);
74             }
75             break;
76 
77          case JS_EVENT_AXIS:
78             if (event.number < NUM_AXES)
79                pad->axes[event.number] = event.value;
80             break;
81       }
82    }
83 }
84 
linuxraw_joypad_init_pad(const char * path,struct linuxraw_joypad * pad)85 static bool linuxraw_joypad_init_pad(const char *path,
86       struct linuxraw_joypad *pad)
87 {
88    /* Device can have just been created, but not made accessible (yet).
89       IN_ATTRIB will signal when permissions change. */
90    if (access(path, R_OK) < 0)
91       return false;
92    if (pad->fd >= 0)
93       return false;
94 
95    pad->fd     = open(path, O_RDONLY | O_NONBLOCK);
96    *pad->ident = '\0';
97 
98    if (pad->fd >= 0)
99    {
100       struct epoll_event event;
101 
102       ioctl(pad->fd,
103                JSIOCGNAME(input_config_get_device_name_size(0)), pad->ident);
104 
105       event.events             = EPOLLIN;
106       event.data.ptr           = pad;
107 
108       if (epoll_ctl(linuxraw_epoll, EPOLL_CTL_ADD, pad->fd, &event) >= 0)
109          return true;
110    }
111 
112    return false;
113 }
114 
linuxraw_joypad_name(unsigned pad)115 static const char *linuxraw_joypad_name(unsigned pad)
116 {
117    if (pad >= MAX_USERS || string_is_empty(linuxraw_pads[pad].ident))
118       return NULL;
119 
120    return linuxraw_pads[pad].ident;
121 }
122 
linuxraw_joypad_poll(void)123 static void linuxraw_joypad_poll(void)
124 {
125    int i, ret;
126    struct epoll_event events[MAX_USERS + 1];
127 
128 retry:
129    ret = epoll_wait(linuxraw_epoll, events, MAX_USERS + 1, 0);
130    if (ret < 0 && errno == EINTR)
131       goto retry;
132 
133    for (i = 0; i < ret; i++)
134    {
135       struct linuxraw_joypad *ptr = (struct linuxraw_joypad*)
136          events[i].data.ptr;
137 
138       if (ptr)
139          linuxraw_poll_pad(ptr);
140       else
141       {
142          /* handle plugged pad */
143          int j, rc;
144          size_t event_size  = sizeof(struct inotify_event) + NAME_MAX + 1;
145          uint8_t *event_buf = (uint8_t*)calloc(1, event_size);
146 
147          while ((rc = read(linuxraw_inotify, event_buf, event_size)) >= 0)
148          {
149             struct inotify_event *event = (struct inotify_event*)&event_buf[0];
150 
151             event_buf[rc-1] = '\0';
152 
153             /* Can read multiple events in one read() call. */
154 
155             for (j = 0; j < rc; j += event->len + sizeof(struct inotify_event))
156             {
157                unsigned idx;
158 
159                event = (struct inotify_event*)&event_buf[j];
160 
161                if (strstr(event->name, "js") != event->name)
162                   continue;
163 
164                idx = strtoul(event->name + 2, NULL, 0);
165                if (idx >= MAX_USERS)
166                   continue;
167 
168                if (event->mask & IN_DELETE)
169                {
170                   if (linuxraw_pads[idx].fd >= 0)
171                   {
172                      if (linuxraw_hotplug)
173                         input_autoconfigure_disconnect(idx,
174                               linuxraw_pads[idx].ident);
175 
176                      close(linuxraw_pads[idx].fd);
177                      linuxraw_pads[idx].buttons = 0;
178                      memset(linuxraw_pads[idx].axes, 0,
179                            sizeof(linuxraw_pads[idx].axes));
180                      linuxraw_pads[idx].fd = -1;
181                      *linuxraw_pads[idx].ident = '\0';
182 
183                      input_autoconfigure_connect(
184                            NULL,
185                            NULL,
186                            linuxraw_joypad_name(idx),
187                            idx,
188                            0,
189                            0);
190                   }
191                }
192                /* Sometimes, device will be created before
193                 * access to it is established. */
194                else if (event->mask & (IN_CREATE | IN_ATTRIB))
195                {
196                   char path[PATH_MAX_LENGTH];
197 
198                   path[0] = '\0';
199 
200                   snprintf(path, sizeof(path), "/dev/input/%s", event->name);
201 
202                   if (     !string_is_empty(linuxraw_pads[idx].ident)
203                         && linuxraw_joypad_init_pad(path, &linuxraw_pads[idx]))
204                      input_autoconfigure_connect(
205                            linuxraw_pads[idx].ident,
206                            NULL,
207                            linuxraw_joypad.ident,
208                            idx,
209                            0,
210                            0);
211                }
212             }
213          }
214 
215          free(event_buf);
216       }
217    }
218 }
219 
linuxraw_joypad_init(void * data)220 static void *linuxraw_joypad_init(void *data)
221 {
222    unsigned i;
223    int fd = epoll_create(32);
224 
225    if (fd < 0)
226       return NULL;
227 
228    linuxraw_epoll = fd;
229 
230    for (i = 0; i < MAX_USERS; i++)
231    {
232       char path[PATH_MAX_LENGTH];
233       struct linuxraw_joypad *pad = (struct linuxraw_joypad*)&linuxraw_pads[i];
234 
235       path[0]                     = '\0';
236 
237       pad->fd                     = -1;
238       pad->ident                  = input_config_get_device_name_ptr(i);
239 
240       snprintf(path, sizeof(path), "/dev/input/js%u", i);
241 
242       input_autoconfigure_connect(
243             pad->ident,
244             NULL,
245             "linuxraw",
246             i,
247             0,
248             0);
249 
250       if (linuxraw_joypad_init_pad(path, pad))
251          linuxraw_poll_pad(pad);
252    }
253 
254    linuxraw_inotify = inotify_init();
255 
256    if (linuxraw_inotify >= 0)
257    {
258       struct epoll_event event;
259 
260       fcntl(linuxraw_inotify, F_SETFL, fcntl(linuxraw_inotify, F_GETFL) | O_NONBLOCK);
261       inotify_add_watch(linuxraw_inotify, "/dev/input", IN_DELETE | IN_CREATE | IN_ATTRIB);
262 
263       event.events             = EPOLLIN;
264       event.data.ptr           = NULL;
265 
266       /* Shouldn't happen, but just check it. */
267       if (epoll_ctl(linuxraw_epoll, EPOLL_CTL_ADD, linuxraw_inotify, &event) < 0)
268       {
269          RARCH_ERR("Failed to add FD (%d) to epoll list (%s).\n",
270                linuxraw_inotify, strerror(errno));
271       }
272    }
273 
274    linuxraw_hotplug = true;
275 
276    return (void*)-1;
277 }
278 
linuxraw_joypad_destroy(void)279 static void linuxraw_joypad_destroy(void)
280 {
281    unsigned i;
282 
283    for (i = 0; i < MAX_USERS; i++)
284    {
285       if (linuxraw_pads[i].fd >= 0)
286          close(linuxraw_pads[i].fd);
287    }
288 
289    memset(linuxraw_pads, 0, sizeof(linuxraw_pads));
290 
291    for (i = 0; i < MAX_USERS; i++)
292       linuxraw_pads[i].fd = -1;
293 
294    if (linuxraw_inotify >= 0)
295       close(linuxraw_inotify);
296    linuxraw_inotify = -1;
297 
298    if (linuxraw_epoll >= 0)
299       close(linuxraw_epoll);
300    linuxraw_epoll = -1;
301 
302    linuxraw_hotplug = false;
303 }
304 
linuxraw_joypad_button(unsigned port,uint16_t joykey)305 static int16_t linuxraw_joypad_button(unsigned port, uint16_t joykey)
306 {
307    const struct linuxraw_joypad    *pad = (const struct linuxraw_joypad*)
308       &linuxraw_pads[port];
309    if (port >= DEFAULT_MAX_PADS)
310       return 0;
311    if (joykey < NUM_BUTTONS)
312       return (BIT32_GET(pad->buttons, joykey));
313    return 0;
314 }
315 
linuxraw_joypad_get_buttons(unsigned port,input_bits_t * state)316 static void linuxraw_joypad_get_buttons(unsigned port, input_bits_t *state)
317 {
318 	const struct linuxraw_joypad *pad = (const struct linuxraw_joypad*)
319       &linuxraw_pads[port];
320 
321 	if (pad)
322    {
323 		BITS_COPY16_PTR(state, pad->buttons);
324 	}
325    else
326 		BIT256_CLEAR_ALL_PTR(state);
327 }
328 
linuxraw_joypad_axis_state(const struct linuxraw_joypad * pad,unsigned port,uint32_t joyaxis)329 static int16_t linuxraw_joypad_axis_state(
330       const struct linuxraw_joypad *pad,
331       unsigned port, uint32_t joyaxis)
332 {
333    if (AXIS_NEG_GET(joyaxis) < NUM_AXES)
334    {
335       /* Kernel returns values in range [-0x7fff, 0x7fff]. */
336       int16_t val = pad->axes[AXIS_NEG_GET(joyaxis)];
337       if (val < 0)
338          return val;
339    }
340    else if (AXIS_POS_GET(joyaxis) < NUM_AXES)
341    {
342       int16_t val = pad->axes[AXIS_POS_GET(joyaxis)];
343       if (val > 0)
344          return val;
345    }
346    return 0;
347 }
348 
linuxraw_joypad_axis(unsigned port,uint32_t joyaxis)349 static int16_t linuxraw_joypad_axis(unsigned port, uint32_t joyaxis)
350 {
351    const struct linuxraw_joypad *pad = (const struct linuxraw_joypad*)
352       &linuxraw_pads[port];
353    return linuxraw_joypad_axis_state(pad, port, joyaxis);
354 }
355 
linuxraw_joypad_state(rarch_joypad_info_t * joypad_info,const struct retro_keybind * binds,unsigned port)356 static int16_t linuxraw_joypad_state(
357       rarch_joypad_info_t *joypad_info,
358       const struct retro_keybind *binds,
359       unsigned port)
360 {
361    unsigned i;
362    int16_t ret                          = 0;
363    uint16_t port_idx                    = joypad_info->joy_idx;
364    const struct linuxraw_joypad    *pad = (const struct linuxraw_joypad*)
365       &linuxraw_pads[port_idx];
366 
367    if (port_idx >= DEFAULT_MAX_PADS)
368       return 0;
369 
370    for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++)
371    {
372       /* Auto-binds are per joypad, not per user. */
373       const uint64_t joykey  = (binds[i].joykey != NO_BTN)
374          ? binds[i].joykey  : joypad_info->auto_binds[i].joykey;
375       const uint32_t joyaxis = (binds[i].joyaxis != AXIS_NONE)
376          ? binds[i].joyaxis : joypad_info->auto_binds[i].joyaxis;
377       if ((uint16_t)joykey != NO_BTN &&
378             (joykey < NUM_BUTTONS)   &&
379             (BIT32_GET(pad->buttons, joykey)))
380          ret |= ( 1 << i);
381       else if (joyaxis != AXIS_NONE &&
382             ((float)abs(linuxraw_joypad_axis_state(pad, port_idx, joyaxis))
383              / 0x8000) > joypad_info->axis_threshold)
384          ret |= (1 << i);
385    }
386 
387    return ret;
388 }
389 
linuxraw_joypad_query_pad(unsigned pad)390 static bool linuxraw_joypad_query_pad(unsigned pad)
391 {
392    return pad < MAX_USERS && linuxraw_pads[pad].fd >= 0;
393 }
394 
395 input_device_driver_t linuxraw_joypad = {
396    linuxraw_joypad_init,
397    linuxraw_joypad_query_pad,
398    linuxraw_joypad_destroy,
399    linuxraw_joypad_button,
400    linuxraw_joypad_state,
401    linuxraw_joypad_get_buttons,
402    linuxraw_joypad_axis,
403    linuxraw_joypad_poll,
404    NULL,
405    linuxraw_joypad_name,
406    "linuxraw",
407 };
408