Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100755 299 lines (252 sloc) 9.001 kb
c5df392 Initial commit.
Florian authored
1 /*
2 * SNESDev - Simulates a virtual keyboard for two SNES controllers that are
3 * connected to the GPIO pins of the Raspberry Pi.
4 *
5 * (c) Copyright 2012 Florian Müller (petrockblog@flo-mueller.com)
6 *
7 * SNESDev homepage: https://github.com/petrockblog/SNESDev-RPi
8 *
9 * Permission to use, copy, modify and distribute SNESDev in both binary and
10 * source form, for non-commercial purposes, is hereby granted without fee,
11 * providing that this license information and copyright notice appear with
12 * all copies and any derived work.
13 *
14 * This software is provided 'as-is', without any express or implied
15 * warranty. In no event shall the authors be held liable for any damages
16 * arising from the use of this software.
17 *
18 * SNESDev is freeware for PERSONAL USE only. Commercial users should
19 * seek permission of the copyright holders first. Commercial use includes
20 * charging money for SNESDev or software derived from SNESDev.
21 *
22 * The copyright holders request that bug fixes and improvements to the code
23 * should be forwarded to them so everyone can benefit from the modifications
24 * in future versions.
25 *
26 * Raspberry Pi is a trademark of the Raspberry Pi Foundation.
27 */
28
29 #include <linux/input.h>
30 #include <linux/uinput.h>
31 #include <sys/time.h>
32 #include <fcntl.h>
33 #include <string.h>
34 #include <stdio.h>
5d3d9f5 Extended code for use with RetroPie GPIO adapter, added signal handling
petrockblog authored
35 #include <stdlib.h>
c5df392 Initial commit.
Florian authored
36 #include <unistd.h>
37 #include <bcm2835.h>
5d3d9f5 Extended code for use with RetroPie GPIO adapter, added signal handling
petrockblog authored
38 #include <signal.h>
6da8309 Added recognition of short/long button press.
petrockblog authored
39 #include <time.h>
c5df392 Initial commit.
Florian authored
40
41 #include "SNESpad.h"
42
43 /* time to wait after each cycle */
44 #define FRAMEWAIT 20
7a24d81 waterbury Changes for Super Nintendo Emulated System Project
authored
45 #define FRAMEWAITLONG 200
c5df392 Initial commit.
Florian authored
46
47 /* set the GPIO pins of the button and the LEDs. */
7a24d81 waterbury Changes for Super Nintendo Emulated System Project
authored
48 #define BUTTONPIN RPI_GPIO_P1_11
5d3d9f5 Extended code for use with RetroPie GPIO adapter, added signal handling
petrockblog authored
49
50 int uinp_fd;
51 int doRun, pollButton, pollPads;
7a24d81 waterbury Changes for Super Nintendo Emulated System Project
authored
52 time_t timePressed;
53 uint8_t lastBtnState;
5d3d9f5 Extended code for use with RetroPie GPIO adapter, added signal handling
petrockblog authored
54
55 /* Signal callback function */
56 void sig_handler(int signo) {
57 if ((signo == SIGINT) | (signo == SIGKILL) | (signo == SIGQUIT) | (signo == SIGABRT)) {
58 printf("Releasing SNESDev-Rpi device.\n");
59 pollButton = 0;
60 pollPads = 0;
61 /* Destroy the input device */
62 ioctl(uinp_fd, UI_DEV_DESTROY);
63 /* Close the UINPUT device */
64 close(uinp_fd);
65 doRun = 0;
66 }
67 }
c5df392 Initial commit.
Florian authored
68
69 /* Setup the uinput device */
70 int setup_uinput_device() {
71 int uinp_fd = open("/dev/uinput", O_WRONLY | O_NDELAY);
72 if (uinp_fd == 0) {
73 printf("Unable to open /dev/uinput\n");
74 return -1;
75 }
76
77 struct uinput_user_dev uinp;
78 memset(&uinp, 0, sizeof(uinp));
79 strncpy(uinp.name, "SNES-to-Keyboard Device", strlen("SNES-to-Keyboard Device"));
80 uinp.id.version = 4;
81 uinp.id.bustype = BUS_USB;
82 uinp.id.product = 1;
83 uinp.id.vendor = 1;
84
85 // Setup the uinput device
86 ioctl(uinp_fd, UI_SET_EVBIT, EV_KEY);
87 ioctl(uinp_fd, UI_SET_EVBIT, EV_REL);
88 int i = 0;
89 for (i = 0; i < 256; i++) {
90 ioctl(uinp_fd, UI_SET_KEYBIT, i);
91 }
92
93 /* Create input device into input sub-system */
94 write(uinp_fd, &uinp, sizeof(uinp));
95 if (ioctl(uinp_fd, UI_DEV_CREATE)) {
5d3d9f5 Extended code for use with RetroPie GPIO adapter, added signal handling
petrockblog authored
96 printf("[SNESDev-Rpi] Unable to create UINPUT device.");
c5df392 Initial commit.
Florian authored
97 return -1;
98 }
99
100 return uinp_fd;
101 }
102
103 /* sends a key event to the virtual device */
104 void send_key_event(int fd, unsigned int keycode, int keyvalue) {
105 struct input_event event;
106 gettimeofday(&event.time, NULL);
107
108 event.type = EV_KEY;
109 event.code = keycode;
110 event.value = keyvalue;
111
112 if (write(fd, &event, sizeof(event)) < 0) {
5d3d9f5 Extended code for use with RetroPie GPIO adapter, added signal handling
petrockblog authored
113 printf("[SNESDev-Rpi] Simulate key error\n");
c5df392 Initial commit.
Florian authored
114 }
115
116 event.type = EV_SYN;
117 event.code = SYN_REPORT;
118 event.value = 0;
119 write(fd, &event, sizeof(event));
120 if (write(fd, &event, sizeof(event)) < 0) {
5d3d9f5 Extended code for use with RetroPie GPIO adapter, added signal handling
petrockblog authored
121 printf("[SNESDev-Rpi] Simulate key error\n");
c5df392 Initial commit.
Florian authored
122 }
123 }
124
6da8309 Added recognition of short/long button press.
petrockblog authored
125 /* checks the state of the button and decides for a short or long button press */
7a24d81 waterbury Changes for Super Nintendo Emulated System Project
authored
126 void checkButton(int uinh)
127 {
c5df392 Initial commit.
Florian authored
128
129 // read the state of the button into a local variable
7a24d81 waterbury Changes for Super Nintendo Emulated System Project
authored
130 uint8_t buttonState = bcm2835_gpio_lev(BUTTONPIN);
c5df392 Initial commit.
Florian authored
131
7a24d81 waterbury Changes for Super Nintendo Emulated System Project
authored
132 if (buttonState==HIGH && lastBtnState==LOW)
133 {
134 timePressed=time(NULL);
135 }
136
137 else if (buttonState==LOW && lastBtnState==HIGH)
138 {
139 time_t curTime = time(NULL);
140 double dif = difftime(curTime,timePressed);
141
142 // Sending ESC
143 send_key_event(uinh, KEY_ESC,1);
144 usleep(100000);
145 send_key_event(uinh, KEY_ESC,0);
146 usleep(500000);
147
148 send_key_event(uinh, KEY_F4,1);
149 usleep(100000);
150 send_key_event(uinh, KEY_F4,0);
151 usleep(2000000);
152
153 /*send_key_event(uinh, KEY_B,1);
154 usleep(100000);
155 send_key_event(uinh, KEY_B,0);
156 usleep(1000000);*/
157
158
159 system("sudo /home/pi/SNES-Pi/MCP23017_CartReader/cartCheckAndEmulate.sh f &");
160 usleep(1000000); //sleep for 1 seconds
161
162 //system("shutdown -t 3 -h now");
163 }
164 lastBtnState=buttonState;
165
c5df392 Initial commit.
Florian authored
166 }
167
168 /* checks, if a button on the pad is pressed and sends an event according the button state. */
5d3d9f5 Extended code for use with RetroPie GPIO adapter, added signal handling
petrockblog authored
169 void processPadBtn(uint16_t buttons, uint16_t mask, uint16_t key, int uinh) {
c5df392 Initial commit.
Florian authored
170 if ( (buttons & mask) == mask ) {
171 send_key_event(uinh, key, 1);
172 } else {
173 send_key_event(uinh, key, 0);
174 }
175 }
176
177
178 int main(int argc, char *argv[]) {
179
5d3d9f5 Extended code for use with RetroPie GPIO adapter, added signal handling
petrockblog authored
180 buttonstates padButtons;
181 snespad pads;
182 pollButton = 1;
183 pollPads = 1;
184 doRun = 1;
185
186 // check command line arguments
187 if (argc > 1) {
188 // argv[1]==1 poll controllers only
189 // argv[1]==2 poll button only
190 // argv[1]==3 poll controllers and button
191 switch ( atoi(argv[argc-1]) ) {
192 case 1:
193 printf("[SNESDev-Rpi] Polling only controllers.\n");
194 pollButton = 0;
195 pollPads = 1;
196 break;
197 case 2:
198 printf("[SNESDev-Rpi] Polling button only.\n");
199 pollButton = 1;
200 pollPads = 0;
201 break;
202 case 3:
203 printf("[SNESDev-Rpi] Polling controllers and button.\n");
204 pollButton = 1;
205 pollPads = 1;
206 break;
207 default:
208 return -1;
209 }
210 } else {
211 printf("[SNESDev-Rpi] Polling controllers and button.\n");
212 }
c5df392 Initial commit.
Florian authored
213
214 if (!bcm2835_init())
215 return 1;
216
217 // initialize button and LEDs
7a24d81 waterbury Changes for Super Nintendo Emulated System Project
authored
218 bcm2835_gpio_fsel(BUTTONPIN, BCM2835_GPIO_FSEL_INPT);
c5df392 Initial commit.
Florian authored
219
220 /* initialize controller structures with GPIO pin assignments */
221
5d3d9f5 Extended code for use with RetroPie GPIO adapter, added signal handling
petrockblog authored
222 // pin out used in SNESDev article on blog
7a24d81 waterbury Changes for Super Nintendo Emulated System Project
authored
223 pads.clock = RPI_GPIO_P1_18;
224 pads.strobe = RPI_GPIO_P1_16;
225 pads.data1 = RPI_GPIO_P1_22;
226 pads.data2 = RPI_GPIO_P1_15;
227
228 // pin out used pi gamecon GPIO driver
229 /* pads.clock = RPI_GPIO_P1_19;
230 pads.strobe = RPI_GPIO_P1_23;
231 pads.data1 = RPI_GPIO_P1_07;
232 pads.data2 = RPI_GPIO_P1_05;
233 */
c5df392 Initial commit.
Florian authored
234
235 /* set GPIO pins as input or output pins */
8b460e5 Enhanced polling of controllers
petrockblog authored
236 initializePads( &pads );
c5df392 Initial commit.
Florian authored
237
238 /* intialize virtual input device */
239 if ((uinp_fd = setup_uinput_device()) < 0) {
5d3d9f5 Extended code for use with RetroPie GPIO adapter, added signal handling
petrockblog authored
240 printf("[SNESDev-Rpi] Unable to find uinput device\n");
c5df392 Initial commit.
Florian authored
241 return -1;
242 }
243
7a24d81 waterbury Changes for Super Nintendo Emulated System Project
authored
244 if (signal(SIGINT, sig_handler) == SIG_ERR)
245 printf("\n[SNESDev-Rpi] Cannot catch SIGINT\n");
5d3d9f5 Extended code for use with RetroPie GPIO adapter, added signal handling
petrockblog authored
246
c5df392 Initial commit.
Florian authored
247 /* enter the main loop */
5d3d9f5 Extended code for use with RetroPie GPIO adapter, added signal handling
petrockblog authored
248 while ( doRun ) {
249
250 if (pollButton) {
251 /* Check state of button. */
252 checkButton(uinp_fd);
253 }
254
255 if (pollPads) {
256 /* read states of the buttons */
257 updateButtons(&pads, &padButtons);
258
259 /* send an event (pressed or released) for each button */
260 /* key events for first controller */
261 processPadBtn(padButtons.buttons1, SNES_A, KEY_X, uinp_fd);
262 processPadBtn(padButtons.buttons1, SNES_B, KEY_Z, uinp_fd);
263 processPadBtn(padButtons.buttons1, SNES_X, KEY_S, uinp_fd);
264 processPadBtn(padButtons.buttons1, SNES_Y, KEY_A, uinp_fd);
265 processPadBtn(padButtons.buttons1, SNES_L, KEY_Q, uinp_fd);
266 processPadBtn(padButtons.buttons1, SNES_R, KEY_W, uinp_fd);
267 processPadBtn(padButtons.buttons1, SNES_SELECT,KEY_RIGHTSHIFT, uinp_fd);
268 processPadBtn(padButtons.buttons1, SNES_START, KEY_ENTER, uinp_fd);
269 processPadBtn(padButtons.buttons1, SNES_LEFT, KEY_LEFT, uinp_fd);
270 processPadBtn(padButtons.buttons1, SNES_RIGHT, KEY_RIGHT, uinp_fd);
271 processPadBtn(padButtons.buttons1, SNES_UP, KEY_UP, uinp_fd);
272 processPadBtn(padButtons.buttons1, SNES_DOWN, KEY_DOWN, uinp_fd);
273
274 // key events for second controller
275 processPadBtn(padButtons.buttons2, SNES_A, KEY_E, uinp_fd);
276 processPadBtn(padButtons.buttons2, SNES_B, KEY_R, uinp_fd);
277 processPadBtn(padButtons.buttons2, SNES_X, KEY_T, uinp_fd);
278 processPadBtn(padButtons.buttons2, SNES_Y, KEY_Y, uinp_fd);
279 processPadBtn(padButtons.buttons2, SNES_L, KEY_U, uinp_fd);
280 processPadBtn(padButtons.buttons2, SNES_R, KEY_I, uinp_fd);
281 processPadBtn(padButtons.buttons2, SNES_SELECT,KEY_O, uinp_fd);
282 processPadBtn(padButtons.buttons2, SNES_START, KEY_P, uinp_fd);
283 processPadBtn(padButtons.buttons2, SNES_LEFT, KEY_C, uinp_fd);
284 processPadBtn(padButtons.buttons2, SNES_RIGHT, KEY_B, uinp_fd);
285 processPadBtn(padButtons.buttons2, SNES_UP, KEY_F, uinp_fd);
286 processPadBtn(padButtons.buttons2, SNES_DOWN, KEY_V, uinp_fd);
287 }
c5df392 Initial commit.
Florian authored
288
289 /* wait for some time to keep the CPU load low */
bcd23e0 longer frame wait if only button is polled
petrockblog authored
290 if (pollButton && !pollPads) {
291 delay(FRAMEWAITLONG);
292 } else {
293 delay(FRAMEWAIT);
294 }
c5df392 Initial commit.
Florian authored
295 }
296
5d3d9f5 Extended code for use with RetroPie GPIO adapter, added signal handling
petrockblog authored
297 return 0;
c5df392 Initial commit.
Florian authored
298 }
Something went wrong with that request. Please try again.