· 10 years ago · Aug 30, 2016, 11:56 PM
1/*
2ADAFRUIT RETROGAME UTILITY: remaps buttons on Raspberry Pi GPIO header to
3virtual USB keyboard presses. Great for classic game emulators! Retrogame
4is interrupt-driven and efficient (typically < 0.3% CPU use, even w/heavy
5button-mashing) and debounces inputs for glitch-free gaming.
6
7Connect one side of button(s) to GND pin (there are several on the GPIO
8header, but see later notes) and the other side to GPIO pin of interest.
9Internal pullups are used; no resistors required. Avoid pins 8 and 10;
10these are configured as a serial port by default on most systems (this can
11be disabled via raspi-config).
12
13Pin configuration is currently set in global table; no config file yet.
14
15Must be run as root, i.e. 'sudo ./retrogame &' or edit /etc/rc.local to
16launch automatically at system startup.
17
18Early Raspberry Pi Linux distributions might not have the uinput kernel
19module installed by default. To enable this, add a line to /etc/modules:
20
21 uinput
22
23Written by Phil Burgess for Adafruit Industries, distributed under BSD
24License. Adafruit invests time and resources providing this open source
25code, please support Adafruit and open-source hardware by purchasing
26products from Adafruit!
27
28
29Copyright (c) 2013, 2016 Adafruit Industries.
30All rights reserved.
31
32Redistribution and use in source and binary forms, with or without
33modification, are permitted provided that the following conditions are met:
34
35- Redistributions of source code must retain the above copyright notice,
36 this list of conditions and the following disclaimer.
37- Redistributions in binary form must reproduce the above copyright notice,
38 this list of conditions and the following disclaimer in the documentation
39 and/or other materials provided with the distribution.
40
41THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
42AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
45LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
46CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
47SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
48INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
49CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
51POSSIBILITY OF SUCH DAMAGE.
52*/
53
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#include <unistd.h>
58#include <fcntl.h>
59#include <poll.h>
60#include <signal.h>
61#include <dirent.h>
62#include <signal.h>
63#include <dirent.h>
64#include <sys/mman.h>
65#include <sys/stat.h>
66#include <sys/signalfd.h>
67#include <sys/inotify.h>
68#include <linux/input.h>
69#include <linux/uinput.h>
70#include "keyTable.h"
71
72
73// START HERE --------------------------------------------------------------
74// This table remaps GPIO inputs to keyboard values. In this initial
75// implementation there's a 1:1 relationship (can't attach multiple keys to
76// a button) and the list is fixed in code; there is no configuration file.
77// Buttons physically connect between GPIO pins and ground. There are only
78// a few GND pins on the GPIO header, so a breakout board is often needed.
79// If you require just a couple extra ground connections and have unused
80// GPIO pins, set the corresponding key value to GND to create a spare.
81
82#define GND -1
83struct {
84 int pin;
85 int key;
86} *io, // In main() this pointer is set to one of the two tables below.
87 ioTFT[] = {
88 // This pin/key table is used if an Adafruit PiTFT display
89 // is detected using the 'classic' methodology (e.g. Cupcade
90 // or PiGRRL 1 -- NOT PiGRRL 2).
91 // Input Output (from /usr/include/linux/input.h)
92 { 25, KEY_LEFT }, // Joystick (4 pins)
93 { 9, KEY_RIGHT },
94 { 17, KEY_DOWN },
95 { 10, KEY_UP },
96 { 23, KEY_Z }, // A/Fire/jump/primary
97 { 18, KEY_X }, // B/Bomb/secondary
98 { 11, KEY_S }, // Credit
99 { 4, KEY_A }, // Start 1P
100 { 22, KEY_M },
101 { 8, KEY_ENTER },
102 { -1, -1 } }, // END OF LIST, DO NOT CHANGE
103 // MAME must be configured with 'z' & 'x' as buttons 1 & 2 -
104 // this was required for the accompanying 'menu' utility to
105 // work (catching crtl/alt w/ncurses gets totally NASTY).
106 // Credit/start are likewise moved to 'r' & 'q,' reason being
107 // to play nicer with certain emulators not liking numbers.
108 // GPIO options are 'maxed out' with PiTFT + above table.
109 // If additional buttons are desired, will need to disable
110 // serial console and/or use P5 header. Or use keyboard.
111 ioStandard[] = {
112 // This pin/key table is used when the PiTFT isn't found
113 // (using HDMI or composite instead), or with newer projects
114 // such as PiGRRL 2 that "fake out" HDMI and copy it to the
115 // PiTFT screen.
116#if 0
117 // From our original Pi gaming guide:
118 // Input Output (from /usr/include/linux/input.h)
119 { 25, KEY_LEFT }, // Joystick (4 pins)
120 { 9, KEY_RIGHT },
121 { 10, KEY_UP },
122 { 17, KEY_DOWN },
123 { 10, KEY_UP },
124 { 17, KEY_DOWN },
125 { 23, KEY_LEFTCTRL }, // A/Fire/jump/primary
126 { 18, KEY_LEFTALT }, // B/Bomb/secondary
127
128 // For credit/start/etc., use USB keyboard or add more buttons.
129#else
130 // For PiGRRL 2:
131 // Input Output (from /usr/include/linux/input.h)
132 { 25, KEY_LEFT }, // Joystick (4 pins)
133 { 9, KEY_RIGHT },
134 { 10, KEY_UP },
135 { 17 , KEY_DOWN },
136 { , KEY_LEFTCTRL }, // A/Fire/jump/primary/RED
137 { , KEY_LEFTALT }, // B/Bomb/secondary/YELLOW
138 { 23, KEY_Z }, // X/BLUE
139 { 18, KEY_X }, // Y/GREEN
140 { 22, KEY_SPACE }, // Select
141 { 8, KEY_ENTER }, // Start
142 { 4, KEY_A }, // L Shoulder
143 { 11, KEY_S }, // R Shoulder
144 { , KEY_ESC }, // Exit ROM PiTFT Button 1
145 { , KEY_1 }, // PiTFT Button 2
146 { , KEY_2 }, // PiTFT Button 3
147 { , KEY_3 }, // PiTFT Button 4
148#endif
149 { -1, -1 } }; // END OF LIST, DO NOT CHANGE
150
151// A "Vulcan nerve pinch" (holding down a specific button combination
152// for a few seconds) issues an 'esc' keypress to MAME (which brings up
153// an exit menu or quits the current game). The button combo is
154// configured with a bitmask corresponding to elements in the above io[]
155// array. The default value here uses GPIO pins 23 and 18 (credit and
156// start in the Cupcade pinout). If you change this, make certain it's
157// a combo that's not likely to occur during actual gameplay (i.e. avoid
158// using joystick directions or hold-for-rapid-fire buttons).
159// Also key auto-repeat times are set here. This is for navigating the
160// game menu using the 'gamera' utility; MAME disregards key repeat
161// events (as it should).
162const unsigned long vulcanMask = (1L << 23) | (1L << 18);
163const int vulcanKey = KEY_ESC, // Keycode to send
164 vulcanTime = 1500, // Pinch time in milliseconds
165 repTime1 = 500, // Key hold time to begin repeat
166 repTime2 = 100; // Time between key repetitions
167
168
169// Global variables and such -----------------------------------------------
170
171extern char
172 *__progname, // Program name (for error reporting)
173 *program_invocation_name; // Full name as invoked (path, etc.)
174char
175 sysfs_root[] = "/sys/class/gpio", // Location of Sysfs GPIO files
176 running = 1, // Signal handler will set to 0 (exit)
177 *cfgPath, // Directory containing config file
178 *cfgName = NULL, // Name (no path) of config
179 *cfgPathname, // Full path/name to config file
180 board; // 0=Pi1Rev1, 1=Pi1Rev2, 2=Pi2/Pi3
181volatile unsigned int
182 *gpio; // GPIO register table
183const int
184*gpio; // GPIO register table
185const int
186 debounceTime = 20; // 20 ms for button debouncing
187struct pollfd
188 p[35]; // File descriptors for poll()
189int
190 fileWatch, // inotify file descriptor
191 intstate[32], // Button last-read state
192 extstate[32], // Button debounced state
193 keyfd1 = -1, // /dev/uinput file descriptor
194 keyfd2 = -1, // /dev/input/eventX file descriptor
195 keyfd = -1; // = (keyfd2 >= 0) ? keyfd2 : keyfd1;
196
197#define PI1_BCM2708_PERI_BASE 0x20000000
198#define PI1_GPIO_BASE (PI1_BCM2708_PERI_BASE + 0x200000)
199#define PI2_BCM2708_PERI_BASE 0x3F000000
200#define PI2_GPIO_BASE (PI2_BCM2708_PERI_BASE + 0x200000)
201#define BLOCK_SIZE (4*1024)
202#define GPPUD (0x94 / 4)
203#define GPPUDCLK0 (0x98 / 4)
204
205
206// Some utility functions --------------------------------------------------
207
208// Detect Pi board type. Not detailed, just enough for GPIO compatibilty:
209// 0 = Pi 1 Model B revision 1
210// 1 = Pi 1 Model B revision 2, Model A, Model B+, Model A+
211// 2 = Pi 2 Model B or Pi 3
212static int boardType(void) {
213 FILE *fp;
214 char buf[1024], *ptr;
215 int n, board = 2; // Assume Pi1 Rev2 by default
216
217 // Relies on info in /proc/cmdline. If this becomes unreliable
218 // in the future, alt code below uses /proc/cpuinfo if any better.
219#if 1
220 if((fp = fopen("/proc/cmdline", "r"))) {
221 while(fgets(buf, sizeof(buf), fp)) {
222 if((ptr = strstr(buf, "mem_size=")) &&
223 (sscanf(&ptr[9], "%x", &n) == 1) &&
224 (n == 0x3F000000)) {
225 board = 2; // Appears to be a Pi 2
226 break;
227 } else if((ptr = strstr(buf, "boardrev=")) &&
228 (sscanf(&ptr[9], "%x", &n) == 1) &&
229 ((n == 0x02) || (n == 0x03))) {
230 board = 0; // Appears to be an early Pi
231 break;
232 }
233 }
234 fclose(fp);
235 }
236#else
237 char s[8];
238 if((fp = fopen("/proc/cpuinfo", "r"))) {
239 while(fgets(buf, sizeof(buf), fp)) {
240 if((ptr = strstr(buf, "Hardware")) &&
241 (sscanf(&ptr[8], " : %7s", s) == 1) &&
242 (!strcmp(s, "BCM2709"))) {
243 board = 2; // Appears to be a Pi 2
244 break;
245 board = 2; // Appears to be a Pi 2
246 break;
247 } else if((ptr = strstr(buf, "Revision")) &&
248 (sscanf(&ptr[8], " : %x", &n) == 1) &&
249 ((n == 0x02) || (n == 0x03))) {
250 board = 0; // Appears to be an early Pi
251 break;
252 }
253 }
254 fclose(fp);
255 }
256#endif
257
258 return board;
259}
260
261// Set one GPIO pin attribute through the Sysfs interface.
262static int pinSetup(int pin, char *attr, char *value) {
263 char filename[50];
264 int fd, w, len = strlen(value);
265 sprintf(filename, "%s/gpio%d/%s", sysfs_root, pin, attr);
266 if((fd = open(filename, O_WRONLY)) < 0) return -1;
267 w = write(fd, value, len);
268 close(fd);
269 return (w != len); // 0 = success
270}
271
272// Configure GPIO internal pull up/down/none
273static void pull(int bitmask, int state) {
274 volatile unsigned char shortWait;
275 gpio[GPPUD] = state; // 2=up, 1=down, 0=none
276 for(shortWait=150;--shortWait;); // Min 150 cycle wait
277 gpio[GPPUDCLK0] = bitmask; // Set pullup mask
278 for(shortWait=150;--shortWait;); // Wait again
279 gpio[GPPUD] = 0; // Reset pullup registers
280 gpio[GPPUDCLK0] = 0;
281}
282
283// Restore GPIO and uinput to startup state; un-export any Sysfs pins used,
284// don't leave any filesystem cruft; restore any GND pins to inputs and
285// disable previously-set pull-ups. Write errors are ignored as pins may be
286// in a partially-initialized state.
287static void unloadPinConfig() {
288 char buf[50];
289 int fd, i, j, bitmask;
290
291 // Close GPIO file descriptors
292 for(i=0; i<32; i++) {
293 if(p[i].fd >= 0) {
294 close(p[i].fd);
295 p[i].fd = -1;
296 intstate[i] = extstate[i] = 0;
297 }
298 p[i].events = p[i].revents = 0;
299 }
300
301 // Close uinput file descriptors
302 keyfd = -1;
303 if(keyfd2 >= 0) {
304 close(keyfd2);
305 keyfd2 = -1;
306 close(keyfd2);
307 keyfd2 = -1;
308 }
309 if(keyfd1 >= 0) {
310 ioctl(keyfd1, UI_DEV_DESTROY);
311 close(keyfd1);
312 keyfd1 = -1;
313 }
314
315 // Un-export pins
316 sprintf(buf, "%s/unexport", sysfs_root);
317 if((fd = open(buf, O_WRONLY)) >= 0) {
318 for(i=0; io[i].pin >= 0; i++) {
319 // Restore GND items to inputs
320 if(io[i].key == GND)
321 pinSetup(io[i].pin, "direction", "in");
322 // And un-export all items regardless
323 sprintf(buf, "%d", io[i].pin);
324 write(fd, buf, strlen(buf));
325 }
326 close(fd);
327 }
328
329 // Disable pullups
330 for(bitmask=i=0; (j=io[i].pin) >= 0; i++) // Bitmap of pullup pins
331 if(io[i].key != GND) bitmask |= (1 << j);
332 pull(bitmask, 0); // Disable pullups
333}
334
335// Quick-n-dirty error reporter; print message, clean up and exit.
336static void err(char *msg) {
337 printf("%s: %s. Try 'sudo %s'.\n", __progname, msg,
338 program_invocation_name);
339 unloadPinConfig();
340 exit(1);
341}
342
343// Filter function for scandir(), identifies possible device candidates for
344// simulated keypress events (distinct from actual USB keyboard(s)).
345static int filter1(const struct dirent *d) {
346 if(!strncmp(d->d_name, "input", 5)) { // Name usu. 'input' + #
347 // Read contents of 'name' file inside this subdirectory,
348 // if it matches the retrogame executable, that's probably
349 // the device we want...
350 char filename[100], line[100];
351 FILE *fp;
352 sprintf(filename, "/sys/devices/virtual/input/%s/name",
353 d->d_name);
354 memset(line, 0, sizeof(line));
355 if((fp = fopen(filename, "r"))) {
356 fgets(line, sizeof(line), fp);
357 fclose(fp);
358 }
359 if(!strncmp(line, __progname, strlen(__progname))) return 1;
360 }
361 return 0;
362}
363
364// A second scandir() filter, checks for filename of 'event' + #
365static int filter2(const struct dirent *d) {
366 return !strncmp(d->d_name, "event", 5);
367static int filter2(const struct dirent *d) {
368 return !strncmp(d->d_name, "event", 5);
369}
370
371#if 0
372// Search for name in dictionary, return assigned value (-1 = not found)
373static int dictSearch(char *str, dict *d) {
374 int i;
375 for(i=0; d[i].name && strcasecmp(str, d[i].name); i++);
376 return d[i].value;
377}
378#endif
379
380// Load pin/key configuration. Right now this uses the io[] table,
381// eventual plan is to have a configuration file. Not there yet.
382static void loadPinConfig() {
383
384 char c, buf[50];
385 int i, j, fd, bitmask;
386
387 // Make combined bitmap of pullup-enabled pins:
388 for(bitmask=i=0; (j=io[i].pin) >= 0; i++)
389 if(io[i].key != GND) bitmask |= (1 << j);
390 pull(bitmask, 2); // Enable pullups
391
392 // All other GPIO config is handled through the sysfs interface.
393
394 sprintf(buf, "%s/export", sysfs_root);
395 if((fd = open(buf, O_WRONLY)) < 0) // Open Sysfs export file
396 err("Can't open GPIO export file");
397 for(i=0; (j=io[i].pin) >= 0; i++) { // For each pin of interest...
398 sprintf(buf, "%d", j);
399 write(fd, buf, strlen(buf)); // Export pin
400 pinSetup(j, "active_low", "0"); // Don't invert
401 if(io[i].key == GND) {
402 // Set pin to output, value 0 (ground)
403 if(pinSetup(j, "direction", "out") ||
404 pinSetup(j, "value" , "0"))
405 err("Pin config failed (GND)");
406 } else {
407 // Set pin to input, detect rise+fall events
408 if(pinSetup(j, "direction", "in") ||
409 pinSetup(j, "edge" , "both"))
410 err("Pin config failed");
411 // Get initial pin value
412 sprintf(buf, "%s/gpio%d/value", sysfs_root, j);
413 if((p[j].fd = open(buf, O_RDONLY)) < 0)
414 err("Can't access pin value");
415 intstate[j] = 0;
416 if((read(p[j].fd, &c, 1) == 1) && (c == '0'))
417 intstate[j] = 1;
418 extstate[j] = intstate[j];
419 p[j].events = POLLPRI; // Set up poll() events
420 p[j].revents = 0;
421 }
422 }
423 close(fd); // Done w/Sysfs exporting
424
425 // Set up uinput
426
427 // Attempt to create uidev virtual keyboard
428
429 // Set up uinput
430
431 // Attempt to create uidev virtual keyboard
432 if((keyfd1 = open("/dev/uinput", O_WRONLY | O_NONBLOCK)) >= 0) {
433 (void)ioctl(keyfd1, UI_SET_EVBIT, EV_KEY);
434 for(i=0; io[i].pin >= 0; i++) {
435 if(io[i].key != GND)
436 (void)ioctl(keyfd1, UI_SET_KEYBIT, io[i].key);
437 }
438 (void)ioctl(keyfd1, UI_SET_KEYBIT, vulcanKey);
439 struct uinput_user_dev uidev;
440 memset(&uidev, 0, sizeof(uidev));
441 snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "retrogame");
442 uidev.id.bustype = BUS_USB;
443 uidev.id.vendor = 0x1;
444 uidev.id.product = 0x1;
445 uidev.id.version = 1;
446 if(write(keyfd1, &uidev, sizeof(uidev)) < 0)
447 err("write failed");
448 if(ioctl(keyfd1, UI_DEV_CREATE) < 0)
449 err("DEV_CREATE failed");
450 }
451
452 // SDL2 (used by some newer emulators) wants /dev/input/eventX
453 // instead -- BUT -- this only exists if there's a physical USB
454 // keyboard attached or if the above code has run and created a
455 // virtual keyboard. On older systems this method doesn't apply,
456 // events can be sent to the keyfd1 virtual keyboard above...so,
457 // this code looks for an eventX device and (if present) will use
458 // that as the destination for events, else fallback on keyfd1.
459
460 // The 'X' in eventX is a unique identifier (typically a numeric
461 // digit or two) for each input device, dynamically assigned as
462 // USB input devices are plugged in or disconnected (or when the
463 // above code runs, creating a virtual keyboard). As it's
464 // dynamically assigned, we can't rely on a fixed number -- it
465 // will vary if there's a keyboard connected at startup.
466
467 struct dirent **namelist;
468 int n;
469 char evName[100] = "";
470
471 if((n = scandir("/sys/devices/virtual/input",
472 &namelist, filter1, NULL)) > 0) {
473 // Got a list of device(s). In theory there should
474 // be only one that makes it through the filter (name
475 // matches retrogame)...if there's multiples, only
476 // the first is used. (namelist can then be freed)
477 char path[100];
478 sprintf(path, "/sys/devices/virtual/input/%s",
479 namelist[0]->d_name);
480 for(i=0; i<n; i++) free(namelist[i]);
481 free(namelist);
482 // Within the given device path should be a subpath with
483 // the name 'eventX' (X varies), again theoretically
484 // should be only one, first in list is used.
485 if((n = scandir(path, &namelist, filter2, NULL)) > 0) {
486 sprintf(evName, "/dev/input/%s",
487 namelist[0]->d_name);
488 for(i=0; i<n; i++) free(namelist[i]);
489 namelist[0]->d_name);
490 for(i=0; i<n; i++) free(namelist[i]);
491 free(namelist);
492 }
493 }
494
495 if(!evName[0]) { // Nothing found? Use fallback method...
496 // Kinda lazy skim for last item in /dev/input/event*
497 // This is NOT guaranteed to be retrogame, but if the
498 // above method fails for some reason, this may be
499 // adequate. If there's a USB keyboard attached at
500 // boot, it usually instantiates in /dev/input before
501 // retrogame, so even if it's then removed, the index
502 // assigned to retrogame stays put...thus the last
503 // index mmmmight be what we need.
504 char name[32];
505 struct stat st;
506 for(i=99; i>=0; i--) {
507 sprintf(name, "/dev/input/event%d", i);
508 if(!stat(name, &st)) break; // last valid device
509 }
510 strcpy(evName, (i >= 0) ? name : "/dev/input/event0");
511 }
512
513 keyfd2 = open(evName, O_WRONLY | O_NONBLOCK);
514 keyfd = (keyfd2 >= 0) ? keyfd2 : keyfd1;
515 // keyfd1 and 2 are global and are held open (as a destination for
516 // key events) until unloadPinConfig() is called.
517}
518
519// Handle signal events (i=32), config file change events (33) or
520// config directory contents change events (34).
521// CONFIGURATION FILES AREN'T YET IMPLEMENTED, but this is a vital
522// part of that, monitoring for config changes so new settings can
523// be loaded synamically without a kill/restart/whatev.
524static void pollHandler(int i) {
525
526 if(i == 32) { // Signal event
527 struct signalfd_siginfo info;
528 read(p[i].fd, &info, sizeof(info));
529 if(info.ssi_signo == SIGHUP) { // kill -1 = force reload
530 printf("SIGHUP\n");
531 // Reload config
532 unloadPinConfig();
533 loadPinConfig();
534 } else { // Other signal = abort program
535 running = 0;
536 }
537 } else { // Change in config file or directory contents
538 char evBuf[1000];
539 int evCount = 0, bufPos = 0,
540 bytesRead = read(p[i].fd, evBuf, sizeof(evBuf));
541 while(bufPos < bytesRead) {
542 struct inotify_event *ev =
543 (struct inotify_event *)&evBuf[bufPos];
544
545 printf("EVENT %d:\n", evCount++);
546 printf("\tinotify event mask: %08x\n", ev->mask);
547 printf("\tlen: %d\n", ev->len);
548 if(ev->len > 0)
549
550 printf("\tlen: %d\n", ev->len);
551 if(ev->len > 0)
552 printf("\tname: '%s'\n", ev->name);
553 if(ev->mask & IN_MODIFY) {
554 puts("\tConfig file changed (reloading)");
555 unloadPinConfig();
556 loadPinConfig();
557 } else if(ev->mask & IN_IGNORED) {
558 // Config file deleted -- stop watching it
559 puts("\tConfig file removed");
560 inotify_rm_watch(p[1].fd, fileWatch);
561 // Closing the descriptor turns out to be
562 // important, as removing the watch itself
563 // creates another IN_IGNORED event.
564 // Avoids turtles all the way down.
565 close(p[33].fd);
566 p[33].fd = -1;
567 p[33].events = 0;
568 // Pin config is NOT unloaded...
569 // keep using prior values for now.
570 } else if(ev->mask & IN_MOVED_FROM) {
571 // File moved/renamed from directory...
572 // check if it's the one we're monitoring.
573 puts("\tFile moved or renamed");
574 if(!strcmp(ev->name, cfgName)) {
575 // It's our file -- stop watching it
576 puts("\tEffectively removed");
577 inotify_rm_watch(p[33].fd, fileWatch);
578 close(p[33].fd);
579 p[33].fd = -1;
580 p[33].events = 0;
581 // Pin config is NOT unloaded...
582 // keep using prior values for now.
583 } else {
584 // Some other file -- disregard
585 puts("\tNot the file we're watching");
586 }
587 } else if(ev->mask & (IN_CREATE | IN_MOVED_TO)) {
588 // File moved/renamed to directory...
589 // check if it's the one we're monitoring for.
590 puts("\tNew file in directory...");
591 if(!strcmp(ev->name, cfgName)) {
592 // It's our file -- start watching it!
593 puts("\tFile created/moved-to!");
594 if(p[33].fd >= 0) { // Existing file?
595 inotify_rm_watch(
596 p[33].fd, fileWatch);
597 close(p[33].fd);
598 }
599 p[33].fd = inotify_init();
600 fileWatch = inotify_add_watch(
601 p[33].fd, cfgPathname,
602 IN_MODIFY | IN_IGNORED);
603 p[33].events = POLLIN;
604 unloadPinConfig();
605 loadPinConfig();
606 } else {
607 // Some other file -- disregard
608 puts("\tNot the config file.");
609 // Some other file -- disregard
610 puts("\tNot the config file.");
611 }
612 }
613
614 bufPos += sizeof(struct inotify_event) + ev->len;
615 }
616 }
617}
618
619
620// Init and main loop ------------------------------------------------------
621
622int main(int argc, char *argv[]) {
623
624 char c; // Pin input value ('0'/'1')
625 int fd, // For mmap, sysfs
626 i, j, // Asst. counter
627 timeout = -1, // poll() timeout
628 lastKey = -1; // Last key down (for repeat)
629 unsigned long bitMask; // For Vulcan pinch detect
630 struct input_event keyEv, synEv; // uinput events
631 sigset_t sigset; // Signal mask
632
633 // Locate configuration file (if any) and path ---------------------
634
635 if(argc > 1) { // First argument (if given) is config file name
636 char *ptr = strrchr(argv[1], '/'); // Full pathname given?
637 if(ptr) { // Pathname given; separate into path & name
638 if(!(cfgPathname = strdup(argv[1])))
639 err("malloc() fail");
640 int len = ptr - argv[1]; // Length of path component
641 if(!len) { // Root path?
642 cfgPath = "/";
643 cfgName = &cfgPathname[1];
644 } else {
645 if(!(cfgPath = (char *)malloc(len + 1)))
646 err("malloc() fail");
647 memcpy(cfgPath, argv[1], len);
648 cfgPath[len] = 0;
649 }
650 } else { // No path given; use /boot directory.
651 cfgPath = "/boot";
652 if(!(cfgPathname = (char *)malloc(
653 strlen(cfgPath) + strlen(argv[1]) + 2)))
654 err("malloc() fail");
655 sprintf(cfgPathname, "%s/%s", cfgPath, argv[1]);
656 }
657 } else {
658 // No argument passed -- config file is located in /boot,
659 // name is "[program name].cfg" (e.g. /boot/retrogame.cfg)
660 cfgPath = "/boot";
661 if(!(cfgPathname = (char *)malloc(
662 strlen(cfgPath) + strlen(__progname) + 6)))
663 err("malloc() fail");
664 sprintf(cfgPathname, "%s/%s.cfg", cfgPath, __progname);
665 }
666 if(!cfgName) cfgName = &cfgPathname[strlen(cfgPath) + 1];
667
668 // Catch signals, config file changes ------------------------------
669
670 // Catch signals, config file changes ------------------------------
671
672 // Clear all descriptors and GPIO state, init input event structures
673 memset(p, 0, sizeof(p));
674 for(i=0; i<35; i++) p[i].fd = -1;
675 memset(intstate, 0, sizeof(intstate));
676 memset(extstate, 0, sizeof(extstate));
677 memset(&keyEv , 0, sizeof(keyEv));
678 memset(&synEv , 0, sizeof(synEv));
679 keyEv.type = EV_KEY;
680 synEv.type = EV_SYN;
681 synEv.code = SYN_REPORT;
682
683 sigfillset(&sigset);
684 sigprocmask(SIG_BLOCK, &sigset, NULL);
685 // pollfd #32 catches signals, so GPIO cleanup on exit is possible
686 p[32].fd = signalfd(-1, &sigset, 0);
687 p[32].events = POLLIN;
688
689 // pollfd #33 and #34 will be used for detecting changes in the
690 // config file and its parent directory. Config files ARE NOT
691 // YET IMPLEMENTED, but I'm working toward this incrementally.
692 // This change detection will let you edit the config and have
693 // immediate feedback without needing to kill the process or
694 // reboot the system.
695 for(i=33; i<=34; i++) {
696 p[i].fd = inotify_init();
697 p[i].events = POLLIN;
698 }
699 fileWatch = inotify_add_watch(p[33].fd, cfgPathname,
700 IN_MODIFY | IN_IGNORED);
701 inotify_add_watch(p[34].fd, cfgPath,
702 IN_CREATE | IN_MOVED_FROM | IN_MOVED_TO);
703
704 // p[0-31] are related to GPIO states, and will be reconfigured
705 // each time the config file is loaded.
706
707 // GPIO startup ----------------------------------------------------
708
709 board = boardType();
710
711 // Although Sysfs provides solid GPIO interrupt handling, there's
712 // no interface to the internal pull-up resistors (this is by
713 // design, being a hardware-dependent feature). It's necessary to
714 // grapple with the GPIO configuration registers directly to enable
715 // the pull-ups. Based on GPIO example code by Dom and Gert van
716 // Loo on elinux.org
717 if((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0)
718 err("Can't open /dev/mem");
719 gpio = mmap( // Memory-mapped I/O
720 NULL, // Any adddress will do
721 BLOCK_SIZE, // Mapped block length
722 PROT_READ|PROT_WRITE, // Enable read+write
723 MAP_SHARED, // Shared with other processes
724 fd, // File to map
725 (board == 2) ?
726 PI2_GPIO_BASE : // -> GPIO registers
727 PI1_GPIO_BASE);
728 close(fd); // Not needed after mmap()
729
730 PI1_GPIO_BASE);
731 close(fd); // Not needed after mmap()
732 if(gpio == MAP_FAILED) err("Can't mmap()");
733
734 // THIS CODE WILL BE GOING AWAY OR MOVING WHEN CONFIG FILES DONE:
735
736 // Select io[] table for Cupcade (TFT) or 'normal' project.
737 io = (access("/etc/modprobe.d/adafruit.conf", F_OK) ||
738 access("/dev/fb1", F_OK)) ? ioStandard : ioTFT;
739
740 // If this is a "Revision 1" Pi board (no mounting holes),
741 // remap certain pin numbers in the io[] array for compatibility.
742 // This way the code doesn't need modification for old boards.
743 // Will be moved to loadPinConfig() with some changes.
744 if(board == 0) {
745 for(i=0; io[i].pin >= 0; i++) {
746 if( io[i].pin == 2) io[i].pin = 0;
747 else if(io[i].pin == 3) io[i].pin = 1;
748 else if(io[i].pin == 27) io[i].pin = 21;
749 }
750 }
751
752 loadPinConfig();
753
754 // Main loop -------------------------------------------------------
755
756 // Monitor GPIO file descriptors for button events. The poll()
757 // function watches for GPIO IRQs in this case; it is NOT
758 // continually polling the pins! Processor load is near zero.
759
760 while(running) { // Signal handler will set this to 0 to exit
761 // Wait for IRQ on pin (or timeout for button debounce)
762 if(poll(p, 35, timeout) > 0) { // If IRQ...
763 for(i=0; i<32; i++) { // For each GPIO bit...
764 if(p[i].revents) { // Event received?
765 timeout = debounceTime;
766 // Read current pin state, store in
767 // internal state flag, flag, but
768 // don't issue to uinput yet -- must
769 // wait for debounce!
770 lseek(p[i].fd, 0, SEEK_SET);
771 read(p[i].fd, &c, 1);
772 if(c == '0') intstate[i] = 1;
773 else if(c == '1') intstate[i] = 0;
774 p[i].revents = 0;
775 }
776 }
777 for(; i<35; i++) { // Check signals, etc.
778 if(p[i].revents) { // Event received?
779 pollHandler(i);
780 p[i].revents = 0;
781 }
782 }
783 c = 0; // Don't issue SYN event
784 // Else timeout occurred
785 } else if(timeout == debounceTime) { // Debounce timeout
786 bitMask = 0L; // Mask of buttons currently pressed
787 for(c=i=0; (j=io[i].pin) >= 0; i++) {
788 if(io[i].key != GND) {
789 // Compare internal state against
790
791 if(io[i].key != GND) {
792 // Compare internal state against
793 // previously-issued value. Send
794 // keys only for changed states.
795 if(intstate[j] != extstate[j]) {
796 extstate[j] = intstate[j];
797 keyEv.code = io[i].key;
798 keyEv.value = intstate[j];
799 write(keyfd, &keyEv,
800 sizeof(keyEv));
801 c = 1; // Follow w/SYN event
802 if(intstate[j]) { // Press?
803 // Note pressed key
804 // and set initial
805 // repeat interval.
806 lastKey = i;
807 timeout = repTime1;
808 } else { // Release?
809 // Stop repeat and
810 // return to normal
811 // IRQ monitoring
812 // (no timeout).
813 lastKey = timeout =
814 -1;
815 }
816 }
817 if(intstate[j]) bitMask |= (1 << j);
818 }
819 }
820
821 // If the "Vulcan nerve pinch" buttons are pressed,
822 // set long timeout -- if this time elapses without
823 // a button state change, esc keypress will be sent.
824 if((bitMask & vulcanMask) == vulcanMask)
825 timeout = vulcanTime;
826 } else if(timeout == vulcanTime) { // Vulcan key timeout
827 // Send keycode (MAME exits or displays exit menu)
828 keyEv.code = vulcanKey;
829 for(i=1; i>= 0; i--) { // Press, release
830 keyEv.value = i;
831 write(keyfd, &keyEv, sizeof(keyEv));
832 usleep(10000); // Be slow, else MAME flakes
833 write(keyfd, &synEv, sizeof(synEv));
834 usleep(10000);
835 }
836 timeout = -1; // Return to normal processing
837 c = 0; // No add'l SYN required
838 } else if(lastKey >= 0) { // Else key repeat timeout
839 if(timeout == repTime1) timeout = repTime2;
840 else if(timeout > 30) timeout -= 5; // Accelerate
841 c = 1; // Follow w/SYN event
842 keyEv.code = io[lastKey].key;
843 keyEv.value = 2; // Key repeat event
844 write(keyfd, &keyEv, sizeof(keyEv));
845 }
846 if(c) write(keyfd, &synEv, sizeof(synEv));
847 }
848
849 // Clean up --------------------------------------------------------
850
851 unloadPinConfig(); // Close uinput, un-export pins
852
853 puts("Done.");
854
855 return 0;
856}