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