Advertisement
kyong1989

Retrogame.c

Jul 31st, 2016
114
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 20.27 KB | None | 0 0
  1. /*
  2. ADAFRUIT RETROGAME UTILITY: remaps buttons on Raspberry Pi GPIO header
  3. to virtual USB keyboard presses. Great for classic game emulators!
  4. Retrogame is interrupt-driven and efficient (usually under 0.3% CPU use)
  5. and debounces inputs for glitch-free gaming.
  6.  
  7. Connect one side of button(s) to GND pin (there are several on the GPIO
  8. header, but see later notes) and the other side to GPIO pin of interest.
  9. Internal pullups are used; no resistors required. Avoid pins 8 and 10;
  10. these are configured as a serial port by default on most systems (this
  11. can be disabled but takes some doing). Pin configuration is currently
  12. set in global table; no config file yet. See later comments.
  13.  
  14. Must be run as root, i.e. 'sudo ./retrogame &' or configure init scripts
  15. to launch automatically at system startup.
  16.  
  17. Requires uinput kernel module. This is typically present on popular
  18. Raspberry Pi Linux distributions but not enabled on some older varieties.
  19. To enable, either type:
  20.  
  21. sudo modprobe uinput
  22.  
  23. Or, to make this persistent between reboots, add a line to /etc/modules:
  24.  
  25. uinput
  26.  
  27. Prior versions of this code, when being compiled for use with the Cupcade
  28. or PiGRRL projects, required CUPCADE to be #defined. This is no longer
  29. the case; instead a test is performed to see if a PiTFT is connected, and
  30. one of two I/O tables is automatically selected.
  31.  
  32. Written by Phil Burgess for Adafruit Industries, distributed under BSD
  33. License. Adafruit invests time and resources providing this open source
  34. code, please support Adafruit and open-source hardware by purchasing
  35. products from Adafruit!
  36.  
  37.  
  38. Copyright (c) 2013 Adafruit Industries.
  39. All rights reserved.
  40.  
  41. Redistribution and use in source and binary forms, with or without
  42. modification, are permitted provided that the following conditions are met:
  43.  
  44. - Redistributions of source code must retain the above copyright notice,
  45. this list of conditions and the following disclaimer.
  46. - Redistributions in binary form must reproduce the above copyright notice,
  47. this list of conditions and the following disclaimer in the documentation
  48. and/or other materials provided with the distribution.
  49.  
  50. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  51. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  52. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  53. ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  54. LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  55. CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  56. SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  57. INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  58. CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  59. ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  60. POSSIBILITY OF SUCH DAMAGE.
  61. */
  62.  
  63. #include <stdio.h>
  64. #include <stdlib.h>
  65. #include <string.h>
  66. #include <unistd.h>
  67. #include <fcntl.h>
  68. #include <poll.h>
  69. #include <signal.h>
  70. #include <sys/mman.h>
  71. #include <linux/input.h>
  72. #include <linux/uinput.h>
  73.  
  74.  
  75. // START HERE ------------------------------------------------------------
  76. // This table remaps GPIO inputs to keyboard values. In this initial
  77. // implementation there's a 1:1 relationship (can't attach multiple keys
  78. // to a button) and the list is fixed in code; there is no configuration
  79. // file. Buttons physically connect between GPIO pins and ground. There
  80. // are only a few GND pins on the GPIO header, so a breakout board is
  81. // often needed. If you require just a couple extra ground connections
  82. // and have unused GPIO pins, set the corresponding key value to GND to
  83. // create a spare ground point.
  84.  
  85. #define GND -1
  86. struct {
  87. int pin;
  88. int key;
  89. } *io, // In main() this pointer is set to one of the two tables below.
  90. ioTFT[] = {
  91. // This pin/key table is used if an Adafruit PiTFT display
  92. // is detected (e.g. Cupcade or PiGRRL).
  93. // Input Output (from /usr/include/linux/input.h)
  94. { 2, KEY_UP },
  95. { 3, KEY_DOWN },
  96. { 4, KEY_LEFT },
  97. { 17, KEY_RIGHT },
  98. { 27, KEY_B },
  99. { 22, KEY_A },
  100. { 23, KEY_X },
  101. { 24, KEY_Y },
  102. { 9, KEY_ENTER },
  103. { 10, KEY_RIGHTSHIFT },
  104. { 25, KEY_L },
  105. { 11, KEY_R },
  106. { 7, KEY_ESC },
  107. { 8, KEY_F1 },
  108. { -1, -1 } }, // END OF LIST, DO NOT CHANGE
  109. // MAME must be configured with 'z' & 'x' as buttons 1 & 2 -
  110. // this was required for the accompanying 'menu' utility to
  111. // work (catching crtl/alt w/ncurses gets totally NASTY).
  112. // Credit/start are likewise moved to 'r' & 'q,' reason being
  113. // to play nicer with certain emulators not liking numbers.
  114. // GPIO options are 'maxed out' with PiTFT + above table.
  115. // If additional buttons are desired, will need to disable
  116. // serial console and/or use P5 header. Or use keyboard.
  117. ioStandard[] = {
  118. // This pin/key table is used when the PiTFT isn't found
  119. // (using HDMI or composite instead), as with our original
  120. // retro gaming guide.
  121. // Input Output (from /usr/include/linux/input.h)
  122. { 2, KEY_UP },
  123. { 3, KEY_DOWN },
  124. { 4, KEY_LEFT },
  125. { 17, KEY_RIGHT },
  126. { 27, KEY_B },
  127. { 22, KEY_A },
  128. { 23, KEY_X },
  129. { 24, KEY_Y },
  130. { 9, KEY_ENTER },
  131. { 10, KEY_RIGHTSHIFT },
  132. { 25, KEY_L },
  133. { 11, KEY_R },
  134. { 7, KEY_ESC },
  135. { 8, KEY_F1 },
  136. { -1, -1 } }; // END OF LIST, DO NOT CHANGE
  137.  
  138. // A "Vulcan nerve pinch" (holding down a specific button combination
  139. // for a few seconds) issues an 'esc' keypress to MAME (which brings up
  140. // an exit menu or quits the current game). The button combo is
  141. // configured with a bitmask corresponding to elements in the above io[]
  142. // array. The default value here uses elements 6 and 7 (credit and start
  143. // in the Cupcade pinout). If you change this, make certain it's a combo
  144. // that's not likely to occur during actual gameplay (i.e. avoid using
  145. // joystick directions or hold-for-rapid-fire buttons).
  146. // Also key auto-repeat times are set here. This is for navigating the
  147. // game menu using the 'gamera' utility; MAME disregards key repeat
  148. // events (as it should).
  149. const unsigned long vulcanMask = (1L << 6) | (1L << 7);
  150. const int vulcanKey = KEY_ESC, // Keycode to send
  151. vulcanTime = 1500, // Pinch time in milliseconds
  152. repTime1 = 500, // Key hold time to begin repeat
  153. repTime2 = 100; // Time between key repetitions
  154.  
  155.  
  156. // A few globals ---------------------------------------------------------
  157.  
  158. char
  159. *progName, // Program name (for error reporting)
  160. sysfs_root[] = "/sys/class/gpio", // Location of Sysfs GPIO files
  161. running = 1; // Signal handler will set to 0 (exit)
  162. volatile unsigned int
  163. *gpio; // GPIO register table
  164. const int
  165. debounceTime = 20; // 20 ms for button debouncing
  166.  
  167.  
  168. // Some utility functions ------------------------------------------------
  169.  
  170. // Set one GPIO pin attribute through the Sysfs interface.
  171. int pinConfig(int pin, char *attr, char *value) {
  172. char filename[50];
  173. int fd, w, len = strlen(value);
  174. sprintf(filename, "%s/gpio%d/%s", sysfs_root, pin, attr);
  175. if((fd = open(filename, O_WRONLY)) < 0) return -1;
  176. w = write(fd, value, len);
  177. close(fd);
  178. return (w != len); // 0 = success
  179. }
  180.  
  181. // Un-export any Sysfs pins used; don't leave filesystem cruft. Also
  182. // restores any GND pins to inputs. Write errors are ignored as pins
  183. // may be in a partially-initialized state.
  184. void cleanup() {
  185. char buf[50];
  186. int fd, i;
  187. sprintf(buf, "%s/unexport", sysfs_root);
  188. if((fd = open(buf, O_WRONLY)) >= 0) {
  189. for(i=0; io[i].pin >= 0; i++) {
  190. // Restore GND items to inputs
  191. if(io[i].key == GND)
  192. pinConfig(io[i].pin, "direction", "in");
  193. // And un-export all items regardless
  194. sprintf(buf, "%d", io[i].pin);
  195. write(fd, buf, strlen(buf));
  196. }
  197. close(fd);
  198. }
  199. }
  200.  
  201. // Quick-n-dirty error reporter; print message, clean up and exit.
  202. void err(char *msg) {
  203. printf("%s: %s. Try 'sudo %s'.\n", progName, msg, progName);
  204. cleanup();
  205. exit(1);
  206. }
  207.  
  208. // Interrupt handler -- set global flag to abort main loop.
  209. void signalHandler(int n) {
  210. running = 0;
  211. }
  212.  
  213. // Detect Pi board type. Doesn't return super-granular details,
  214. // just the most basic distinction needed for GPIO compatibility:
  215. // 0: Pi 1 Model B revision 1
  216. // 1: Pi 1 Model B revision 2, Model A, Model B+, Model A+
  217. // 2: Pi 2 Model B
  218.  
  219. static int boardType(void) {
  220. FILE *fp;
  221. char buf[1024], *ptr;
  222. int n, board = 1; // Assume Pi1 Rev2 by default
  223.  
  224. // Relies on info in /proc/cmdline. If this becomes unreliable
  225. // in the future, alt code below uses /proc/cpuinfo if any better.
  226. #if 1
  227. if((fp = fopen("/proc/cmdline", "r"))) {
  228. while(fgets(buf, sizeof(buf), fp)) {
  229. if((ptr = strstr(buf, "mem_size=")) &&
  230. (sscanf(&ptr[9], "%x", &n) == 1) &&
  231. (n == 0x3F000000)) {
  232. board = 2; // Appears to be a Pi 2
  233. break;
  234. } else if((ptr = strstr(buf, "boardrev=")) &&
  235. (sscanf(&ptr[9], "%x", &n) == 1) &&
  236. ((n == 0x02) || (n == 0x03))) {
  237. board = 0; // Appears to be an early Pi
  238. break;
  239. }
  240. }
  241. fclose(fp);
  242. }
  243. #else
  244. char s[8];
  245. if((fp = fopen("/proc/cpuinfo", "r"))) {
  246. while(fgets(buf, sizeof(buf), fp)) {
  247. if((ptr = strstr(buf, "Hardware")) &&
  248. (sscanf(&ptr[8], " : %7s", s) == 1) &&
  249. (!strcmp(s, "BCM2709"))) {
  250. board = 2; // Appears to be a Pi 2
  251. break;
  252. } else if((ptr = strstr(buf, "Revision")) &&
  253. (sscanf(&ptr[8], " : %x", &n) == 1) &&
  254. ((n == 0x02) || (n == 0x03))) {
  255. board = 0; // Appears to be an early Pi
  256. break;
  257. }
  258. }
  259. fclose(fp);
  260. }
  261. #endif
  262.  
  263. return board;
  264. }
  265.  
  266. // Main stuff ------------------------------------------------------------
  267.  
  268. #define PI1_BCM2708_PERI_BASE 0x20000000
  269. #define PI1_GPIO_BASE (PI1_BCM2708_PERI_BASE + 0x200000)
  270. #define PI2_BCM2708_PERI_BASE 0x3F000000
  271. #define PI2_GPIO_BASE (PI2_BCM2708_PERI_BASE + 0x200000)
  272. #define BLOCK_SIZE (4*1024)
  273. #define GPPUD (0x94 / 4)
  274. #define GPPUDCLK0 (0x98 / 4)
  275.  
  276. int main(int argc, char *argv[]) {
  277.  
  278. // A few arrays here are declared with 32 elements, even though
  279. // values aren't needed for io[] members where the 'key' value is
  280. // GND. This simplifies the code a bit -- no need for mallocs and
  281. // tests to create these arrays -- but may waste a handful of
  282. // bytes for any declared GNDs.
  283. char buf[50], // For sundry filenames
  284. c, // Pin input value ('0'/'1')
  285. board; // 0=Pi1Rev1, 1=Pi1Rev2, 2=Pi2
  286. int fd, // For mmap, sysfs, uinput
  287. i, j, // Asst. counter
  288. bitmask, // Pullup enable bitmask
  289. timeout = -1, // poll() timeout
  290. intstate[32], // Last-read state
  291. extstate[32], // Debounced state
  292. lastKey = -1; // Last key down (for repeat)
  293. unsigned long bitMask, bit; // For Vulcan pinch detect
  294. volatile unsigned char shortWait; // Delay counter
  295. struct input_event keyEv, synEv; // uinput events
  296. struct pollfd p[32]; // GPIO file descriptors
  297.  
  298. progName = argv[0]; // For error reporting
  299. signal(SIGINT , signalHandler); // Trap basic signals (exit cleanly)
  300. signal(SIGKILL, signalHandler);
  301.  
  302. // Select io[] table for Cupcade (TFT) or 'normal' project.
  303. io = (access("/etc/modprobe.d/adafruit.conf", F_OK) ||
  304. access("/dev/fb1", F_OK)) ? ioStandard : ioTFT;
  305.  
  306. // If this is a "Revision 1" Pi board (no mounting holes),
  307. // remap certain pin numbers in the io[] array for compatibility.
  308. // This way the code doesn't need modification for old boards.
  309. board = boardType();
  310. if(board == 0) {
  311. for(i=0; io[i].pin >= 0; i++) {
  312. if( io[i].pin == 2) io[i].pin = 0;
  313. else if(io[i].pin == 3) io[i].pin = 1;
  314. else if(io[i].pin == 27) io[i].pin = 21;
  315. }
  316. }
  317.  
  318. // ----------------------------------------------------------------
  319. // Although Sysfs provides solid GPIO interrupt handling, there's
  320. // no interface to the internal pull-up resistors (this is by
  321. // design, being a hardware-dependent feature). It's necessary to
  322. // grapple with the GPIO configuration registers directly to enable
  323. // the pull-ups. Based on GPIO example code by Dom and Gert van
  324. // Loo on elinux.org
  325.  
  326. if((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0)
  327. err("Can't open /dev/mem");
  328. gpio = mmap( // Memory-mapped I/O
  329. NULL, // Any adddress will do
  330. BLOCK_SIZE, // Mapped block length
  331. PROT_READ|PROT_WRITE, // Enable read+write
  332. MAP_SHARED, // Shared with other processes
  333. fd, // File to map
  334. (board == 2) ?
  335. PI2_GPIO_BASE : // -> GPIO registers
  336. PI1_GPIO_BASE);
  337.  
  338. close(fd); // Not needed after mmap()
  339. if(gpio == MAP_FAILED) err("Can't mmap()");
  340. // Make combined bitmap of pullup-enabled pins:
  341. for(bitmask=i=0; io[i].pin >= 0; i++)
  342. if(io[i].key != GND) bitmask |= (1 << io[i].pin);
  343. gpio[GPPUD] = 2; // Enable pullup
  344. for(shortWait=150;--shortWait;); // Min 150 cycle wait
  345. gpio[GPPUDCLK0] = bitmask; // Set pullup mask
  346. for(shortWait=150;--shortWait;); // Wait again
  347. gpio[GPPUD] = 0; // Reset pullup registers
  348. gpio[GPPUDCLK0] = 0;
  349. (void)munmap((void *)gpio, BLOCK_SIZE); // Done with GPIO mmap()
  350.  
  351.  
  352. // ----------------------------------------------------------------
  353. // All other GPIO config is handled through the sysfs interface.
  354.  
  355. sprintf(buf, "%s/export", sysfs_root);
  356. if((fd = open(buf, O_WRONLY)) < 0) // Open Sysfs export file
  357. err("Can't open GPIO export file");
  358. for(i=j=0; io[i].pin >= 0; i++) { // For each pin of interest...
  359. sprintf(buf, "%d", io[i].pin);
  360. write(fd, buf, strlen(buf)); // Export pin
  361. pinConfig(io[i].pin, "active_low", "0"); // Don't invert
  362. if(io[i].key == GND) {
  363. // Set pin to output, value 0 (ground)
  364. if(pinConfig(io[i].pin, "direction", "out") ||
  365. pinConfig(io[i].pin, "value" , "0"))
  366. err("Pin config failed (GND)");
  367. } else {
  368. // Set pin to input, detect rise+fall events
  369. if(pinConfig(io[i].pin, "direction", "in") ||
  370. pinConfig(io[i].pin, "edge" , "both"))
  371. err("Pin config failed");
  372. // Get initial pin value
  373. sprintf(buf, "%s/gpio%d/value",
  374. sysfs_root, io[i].pin);
  375. // The p[] file descriptor array isn't necessarily
  376. // aligned with the io[] array. GND keys in the
  377. // latter are skipped, but p[] requires contiguous
  378. // entries for poll(). So the pins to monitor are
  379. // at the head of p[], and there may be unused
  380. // elements at the end for each GND. Same applies
  381. // to the intstate[] and extstate[] arrays.
  382. if((p[j].fd = open(buf, O_RDONLY)) < 0)
  383. err("Can't access pin value");
  384. intstate[j] = 0;
  385. if((read(p[j].fd, &c, 1) == 1) && (c == '0'))
  386. intstate[j] = 1;
  387. extstate[j] = intstate[j];
  388. p[j].events = POLLPRI; // Set up poll() events
  389. p[j].revents = 0;
  390. j++;
  391. }
  392. } // 'j' is now count of non-GND items in io[] table
  393. close(fd); // Done exporting
  394.  
  395.  
  396. // ----------------------------------------------------------------
  397. // Set up uinput
  398.  
  399. #if 1
  400. // Retrogame normally uses /dev/uinput for generating key events.
  401. // Cupcade requires this and it's the default. SDL2 (used by
  402. // some newer emulators) doesn't like it, wants /dev/input/event0
  403. // instead. Enable that code by changing to "#if 0" above.
  404. if((fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK)) < 0)
  405. err("Can't open /dev/uinput");
  406. if(ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0)
  407. err("Can't SET_EVBIT");
  408. for(i=0; io[i].pin >= 0; i++) {
  409. if(io[i].key != GND) {
  410. if(ioctl(fd, UI_SET_KEYBIT, io[i].key) < 0)
  411. err("Can't SET_KEYBIT");
  412. }
  413. }
  414. if(ioctl(fd, UI_SET_KEYBIT, vulcanKey) < 0) err("Can't SET_KEYBIT");
  415. struct uinput_user_dev uidev;
  416. memset(&uidev, 0, sizeof(uidev));
  417. snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "retrogame");
  418. uidev.id.bustype = BUS_USB;
  419. uidev.id.vendor = 0x1;
  420. uidev.id.product = 0x1;
  421. uidev.id.version = 1;
  422. if(write(fd, &uidev, sizeof(uidev)) < 0)
  423. err("write failed");
  424. if(ioctl(fd, UI_DEV_CREATE) < 0)
  425. err("DEV_CREATE failed");
  426. #else // SDL2 prefers this event methodology
  427. if((fd = open("/dev/input/event0", O_WRONLY | O_NONBLOCK)) < 0)
  428. err("Can't open /dev/input/event0");
  429. #endif
  430.  
  431. // Initialize input event structures
  432. memset(&keyEv, 0, sizeof(keyEv));
  433. keyEv.type = EV_KEY;
  434. memset(&synEv, 0, sizeof(synEv));
  435. synEv.type = EV_SYN;
  436. synEv.code = SYN_REPORT;
  437. synEv.value = 0;
  438.  
  439. // 'fd' is now open file descriptor for issuing uinput events
  440.  
  441.  
  442. // ----------------------------------------------------------------
  443. // Monitor GPIO file descriptors for button events. The poll()
  444. // function watches for GPIO IRQs in this case; it is NOT
  445. // continually polling the pins! Processor load is near zero.
  446.  
  447. while(running) { // Signal handler can set this to 0 to exit
  448. // Wait for IRQ on pin (or timeout for button debounce)
  449. if(poll(p, j, timeout) > 0) { // If IRQ...
  450. for(i=0; i<j; i++) { // Scan non-GND pins...
  451. if(p[i].revents) { // Event received?
  452. // Read current pin state, store
  453. // in internal state flag, but
  454. // don't issue to uinput yet --
  455. // must wait for debounce!
  456. lseek(p[i].fd, 0, SEEK_SET);
  457. read(p[i].fd, &c, 1);
  458. if(c == '0') intstate[i] = 1;
  459. else if(c == '1') intstate[i] = 0;
  460. p[i].revents = 0; // Clear flag
  461. }
  462. }
  463. timeout = debounceTime; // Set timeout for debounce
  464. c = 0; // Don't issue SYN event
  465. // Else timeout occurred
  466. } else if(timeout == debounceTime) { // Button debounce timeout
  467. // 'j' (number of non-GNDs) is re-counted as
  468. // it's easier than maintaining an additional
  469. // remapping table or a duplicate key[] list.
  470. bitMask = 0L; // Mask of buttons currently pressed
  471. bit = 1L;
  472. for(c=i=j=0; io[i].pin >= 0; i++, bit<<=1) {
  473. if(io[i].key != GND) {
  474. // Compare internal state against
  475. // previously-issued value. Send
  476. // keystrokes only for changed states.
  477. if(intstate[j] != extstate[j]) {
  478. extstate[j] = intstate[j];
  479. keyEv.code = io[i].key;
  480. keyEv.value = intstate[j];
  481. write(fd, &keyEv,
  482. sizeof(keyEv));
  483. c = 1; // Follow w/SYN event
  484. if(intstate[j]) { // Press?
  485. // Note pressed key
  486. // and set initial
  487. // repeat interval.
  488. lastKey = i;
  489. timeout = repTime1;
  490. } else { // Release?
  491. // Stop repeat and
  492. // return to normal
  493. // IRQ monitoring
  494. // (no timeout).
  495. lastKey = timeout = -1;
  496. }
  497. }
  498. j++;
  499. if(intstate[i]) bitMask |= bit;
  500. }
  501. }
  502.  
  503. // If the "Vulcan nerve pinch" buttons are pressed,
  504. // set long timeout -- if this time elapses without
  505. // a button state change, esc keypress will be sent.
  506. if((bitMask & vulcanMask) == vulcanMask)
  507. timeout = vulcanTime;
  508. } else if(timeout == vulcanTime) { // Vulcan timeout occurred
  509. // Send keycode (MAME exits or displays exit menu)
  510. keyEv.code = vulcanKey;
  511. for(i=1; i>= 0; i--) { // Press, release
  512. keyEv.value = i;
  513. write(fd, &keyEv, sizeof(keyEv));
  514. usleep(10000); // Be slow, else MAME flakes
  515. write(fd, &synEv, sizeof(synEv));
  516. usleep(10000);
  517. }
  518. timeout = -1; // Return to normal processing
  519. c = 0; // No add'l SYN required
  520. } else if(lastKey >= 0) { // Else key repeat timeout
  521. if(timeout == repTime1) timeout = repTime2;
  522. else if(timeout > 30) timeout -= 5; // Accelerate
  523. c = 1; // Follow w/SYN event
  524. keyEv.code = io[lastKey].key;
  525. keyEv.value = 2; // Key repeat event
  526. write(fd, &keyEv, sizeof(keyEv));
  527. }
  528. if(c) write(fd, &synEv, sizeof(synEv));
  529. }
  530.  
  531. // ----------------------------------------------------------------
  532. // Clean up
  533.  
  534. ioctl(fd, UI_DEV_DESTROY); // Destroy and
  535. close(fd); // close uinput
  536. cleanup(); // Un-export pins
  537.  
  538. puts("Done.");
  539.  
  540. return 0;
  541. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement