teensytp: initial implementation (derived from teensytas)
[megadrive.git] / teensytp / host / main.c
... / ...
CommitLineData
1/*
2 * TeensyTP, Team Player/4-Player Adaptor implementation for Teensy3
3 * host part
4 * Copyright (c) 2015 notaz
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 */
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <stdint.h>
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <fcntl.h>
34#include <sys/ioctl.h>
35#include <sys/select.h>
36#include <unistd.h>
37#include <dirent.h>
38#include <signal.h>
39#include <termios.h>
40#include <errno.h>
41#include <linux/usbdevice_fs.h>
42#include <linux/usb/ch9.h>
43#include <linux/input.h>
44#include "../pkts.h"
45
46#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
47
48struct teensy_dev {
49 int fd;
50 struct {
51 int ep_in;
52 int ep_out;
53 } ifaces[2];
54};
55
56/* return 1 if found, 0 if not, < 0 on error */
57static int find_device(struct teensy_dev *dev,
58 uint16_t vendor, uint16_t product)
59{
60 const char path_root[] = "/dev/bus/usb";
61 union {
62 struct usb_descriptor_header hdr;
63 struct usb_device_descriptor d;
64 struct usb_config_descriptor c;
65 struct usb_interface_descriptor i;
66 struct usb_endpoint_descriptor e;
67 char space[0x100]; /* enough? */
68 } desc;
69 char path_bus[256], path_dev[256];
70 struct dirent *ent, *ent_bus;
71 DIR *dir = NULL, *dir_bus = NULL;
72 int num, fd = -1;
73 int iface = -1;
74 int retval = -1;
75 int ret;
76
77 memset(dev, 0xff, sizeof(*dev));
78
79 dir = opendir(path_root);
80 if (dir == NULL) {
81 perror("opendir");
82 return -1;
83 }
84
85 for (ent = readdir(dir); ent != NULL; ent = readdir(dir)) {
86 /* should be a number like 000 */
87 if (sscanf(ent->d_name, "%03d", &num) != 1)
88 continue;
89
90 snprintf(path_bus, sizeof(path_bus), "%s/%s",
91 path_root, ent->d_name);
92
93 dir_bus = opendir(path_bus);
94 if (dir_bus == NULL)
95 continue;
96
97 ent_bus = readdir(dir_bus);
98 for (; ent_bus != NULL; ent_bus = readdir(dir_bus)) {
99 if (sscanf(ent->d_name, "%03d", &num) != 1)
100 continue;
101
102 snprintf(path_dev, sizeof(path_dev), "%s/%s/%s",
103 path_root, ent->d_name, ent_bus->d_name);
104
105 fd = open(path_dev, O_RDWR);
106 if (fd == -1)
107 continue;
108
109 ret = read(fd, &desc.d, sizeof(desc.d));
110 if (ret != sizeof(desc.d)) {
111 fprintf(stderr, "desc read: %d/%zd: ", ret, sizeof(desc.d));
112 perror("");
113 goto next;
114 }
115
116 if (desc.d.bDescriptorType != USB_DT_DEVICE) {
117 fprintf(stderr, "%s: bad DT: 0x%02x\n",
118 path_dev, desc.d.bDescriptorType);
119 goto next;
120 }
121
122 if (desc.d.idVendor == vendor && desc.d.idProduct == product)
123 goto found;
124
125next:
126 close(fd);
127 fd = -1;
128 }
129
130 closedir(dir_bus);
131 dir_bus = NULL;
132 }
133
134 /* not found */
135 retval = 0;
136 goto out;
137
138found:
139 if (desc.d.bNumConfigurations != 1) {
140 fprintf(stderr, "unexpected bNumConfigurations: %u\n",
141 desc.d.bNumConfigurations);
142 goto out;
143 }
144
145 /* walk through all descriptors */
146 while (1)
147 {
148 ret = read(fd, &desc.hdr, sizeof(desc.hdr));
149 if (ret == 0)
150 break;
151 if (ret != sizeof(desc.hdr)) {
152 fprintf(stderr, "desc.hdr read: %d/%zd: ", ret, sizeof(desc.hdr));
153 perror("");
154 break;
155 }
156
157 ret = (int)lseek(fd, -sizeof(desc.hdr), SEEK_CUR);
158 if (ret == -1) {
159 perror("lseek");
160 break;
161 }
162
163 ret = read(fd, &desc, desc.hdr.bLength);
164 if (ret != desc.hdr.bLength) {
165 fprintf(stderr, "desc read: %d/%u: ", ret, desc.hdr.bLength);
166 perror("");
167 break;
168 }
169
170 switch (desc.hdr.bDescriptorType) {
171 case USB_DT_CONFIG:
172 if (desc.c.bNumInterfaces != 2) {
173 fprintf(stderr, "unexpected bNumInterfaces: %u\n",
174 desc.c.bNumInterfaces);
175 goto out;
176 }
177 break;
178
179 case USB_DT_INTERFACE:
180 if (desc.i.bInterfaceClass != USB_CLASS_HID
181 || desc.i.bInterfaceSubClass != 0
182 || desc.i.bInterfaceProtocol != 0) {
183 fprintf(stderr, "unexpected interface %x:%x:%x\n",
184 desc.i.bInterfaceClass, desc.i.bInterfaceSubClass,
185 desc.i.bInterfaceProtocol);
186 goto out;
187 }
188 if (desc.i.bNumEndpoints != 2) {
189 fprintf(stderr, "unexpected bNumEndpoints: %u\n",
190 desc.i.bNumEndpoints);
191 goto out;
192 }
193 iface++;
194 break;
195
196 case USB_DT_ENDPOINT:
197 if (iface < 0 || iface >= ARRAY_SIZE(dev->ifaces)) {
198 fprintf(stderr, "bad iface: %d\n", iface);
199 goto out;
200 }
201 if (desc.e.wMaxPacketSize != 64 && desc.e.wMaxPacketSize != 32) {
202 fprintf(stderr, "iface %d, EP %02x: "
203 "unexpected wMaxPacketSize: %u\n",
204 iface, desc.e.bEndpointAddress, desc.e.wMaxPacketSize);
205 goto out;
206 }
207 if (desc.e.bEndpointAddress & 0x80)
208 dev->ifaces[iface].ep_in = desc.e.bEndpointAddress; // & 0x7F;
209 else
210 dev->ifaces[iface].ep_out = desc.e.bEndpointAddress;
211 break;
212
213 case 0x21:
214 /* ignore */
215 break;
216
217 default:
218 fprintf(stderr, "skipping desc 0x%02x\n",
219 desc.hdr.bDescriptorType);
220 break;
221 }
222 }
223
224 /* claim interfaces */
225 for (iface = 0; iface < ARRAY_SIZE(dev->ifaces); iface++) {
226 struct usbdevfs_ioctl usbio;
227
228 if (dev->ifaces[iface].ep_in == -1) {
229 fprintf(stderr, "missing ep_in, iface: %d\n", iface);
230 goto out;
231 }
232 if (dev->ifaces[iface].ep_out == -1) {
233 fprintf(stderr, "missing ep_out, iface: %d\n", iface);
234 goto out;
235 }
236
237 /* disconnect default driver */
238 memset(&usbio, 0, sizeof(usbio));
239 usbio.ifno = iface;
240 usbio.ioctl_code = USBDEVFS_DISCONNECT;
241 ret = ioctl(fd, USBDEVFS_IOCTL, &usbio);
242 if (ret != 0 && errno != ENODATA)
243 perror("USBDEVFS_DISCONNECT");
244
245 ret = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &iface);
246 if (ret != 0)
247 perror("USBDEVFS_CLAIMINTERFACE");
248 }
249
250 dev->fd = fd;
251 fd = -1;
252 retval = 1;
253
254out:
255 if (fd != -1)
256 close(fd);
257 if (dir_bus != NULL)
258 closedir(dir_bus);
259 if (dir != NULL)
260 closedir(dir);
261
262 return retval;
263}
264
265static int enable_echo(int enable)
266{
267 const char *portname = "/dev/tty";
268 struct termios tty;
269 int retval = -1;
270 int ret;
271 int fd;
272
273 memset(&tty, 0, sizeof(tty));
274
275 fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
276 if (fd < 0) {
277 fprintf(stderr, "open %s: ", portname);
278 perror("");
279 return 1;
280 }
281
282 ret = tcgetattr(fd, &tty);
283 if (ret != 0) {
284 perror("tcgetattr");
285 goto out;
286 }
287
288 // printf("lflag: 0%o\n", tty.c_lflag);
289 if (enable)
290 tty.c_lflag |= ECHO | ICANON;
291 else {
292 tty.c_lflag &= ~(ECHO | ICANON);
293 tty.c_cc[VMIN] = tty.c_cc[VTIME] = 0;
294 }
295
296 ret = tcsetattr(fd, TCSANOW, &tty);
297 if (ret != 0) {
298 perror("tcsetattr");
299 goto out;
300 }
301
302 retval = 0;
303out:
304 close(fd);
305
306 return retval;
307}
308
309static int g_exit;
310
311static void signal_handler(int sig)
312{
313 g_exit = 1;
314 signal(sig, SIG_DFL);
315}
316
317/* MXYZ SACB RLDU */
318enum mdbtn {
319 MDBTN_UP = (1 << 0),
320 MDBTN_DOWN = (1 << 1),
321 MDBTN_LEFT = (1 << 2),
322 MDBTN_RIGHT = (1 << 3),
323 MDBTN_A = (1 << 6),
324 MDBTN_B = (1 << 4),
325 MDBTN_C = (1 << 5),
326 MDBTN_START = (1 << 7),
327 MDBTN_X = (1 << 10),
328 MDBTN_Y = (1 << 9),
329 MDBTN_Z = (1 << 8),
330 MDBTN_MODE = (1 << 11),
331};
332
333#define BTN_JOY BTN_JOYSTICK
334
335static const uint32_t evdev_md_default_map[KEY_CNT] = {
336 [KEY_UP] = MDBTN_UP,
337 [KEY_DOWN] = MDBTN_DOWN,
338 [KEY_LEFT] = MDBTN_LEFT,
339 [KEY_RIGHT] = MDBTN_RIGHT,
340 [KEY_Z] = MDBTN_A,
341 [KEY_X] = MDBTN_B,
342 [KEY_C] = MDBTN_C,
343 [KEY_A] = MDBTN_X,
344 [KEY_S] = MDBTN_Y,
345 [KEY_D] = MDBTN_Z,
346 [KEY_F] = MDBTN_MODE,
347 [KEY_ENTER] = MDBTN_START,
348 // gamepad
349 [BTN_JOY + 0] = MDBTN_A,
350 [BTN_JOY + 1] = MDBTN_B,
351 [BTN_JOY + 2] = MDBTN_C,
352 [BTN_JOY + 3] = MDBTN_START,
353 // pandora
354 [KEY_HOME] = MDBTN_A,
355 [KEY_PAGEDOWN] = MDBTN_B,
356 [KEY_END] = MDBTN_C,
357 [KEY_LEFTALT] = MDBTN_START,
358};
359
360static struct player_state {
361 uint32_t kc_map[KEY_CNT];
362 uint32_t state;
363 int dirty;
364} players[4];
365
366static int do_evdev_input(int fd, unsigned int player_i)
367{
368 struct player_state *player;
369 struct input_event ev;
370 uint32_t mask_clear = 0;
371 uint32_t mask_set = 0;
372 uint32_t old_state;
373 int ret;
374
375 ret = read(fd, &ev, sizeof(ev));
376 if (ret != sizeof(ev)) {
377 fprintf(stderr, "evdev read %d/%zd: ", ret, sizeof(ev));
378 perror("");
379 return 0;
380 }
381
382 if (player_i >= ARRAY_SIZE(players)) {
383 fprintf(stderr, "bad player: %u\n", player_i);
384 return 0;
385 }
386 player = &players[player_i];
387 old_state = player->state;
388
389 if (ev.type == EV_ABS) {
390 if (ev.code == 0) {
391 mask_clear = MDBTN_LEFT | MDBTN_RIGHT;
392 if (ev.value < 10)
393 mask_set = MDBTN_LEFT;
394 else if (ev.value > 210)
395 mask_set = MDBTN_RIGHT;
396 }
397 else {
398 mask_clear = MDBTN_UP | MDBTN_DOWN;
399 if (ev.value < 10)
400 mask_set = MDBTN_UP;
401 else if (ev.value > 210)
402 mask_set = MDBTN_DOWN;
403 }
404 printf("abs, s %x %x\n", mask_clear, mask_set);
405 }
406 else if (ev.type == EV_KEY) {
407 if (ev.value != 0 && ev.value != 1)
408 return 0;
409
410 if ((uint32_t)ev.code >= ARRAY_SIZE(player->kc_map)) {
411 fprintf(stderr, "evdev read bad key: %u\n", ev.code);
412 return 0;
413 }
414
415 if (ev.value) // press?
416 mask_set = player->kc_map[ev.code];
417 else
418 mask_clear = player->kc_map[ev.code];
419 }
420
421 player->state &= ~mask_clear;
422 player->state |= mask_set;
423
424 player->dirty |= old_state != player->state;
425 return player->dirty;
426}
427
428static int open_evdev(const char *name)
429{
430 int evdev_support = 0;
431 int fd, ret;
432
433 fd = open(name, O_RDONLY);
434 if (fd == -1) {
435 fprintf(stderr, "open %s: ", name);
436 perror("");
437 return -1;
438 }
439 ret = ioctl(fd, EVIOCGBIT(0, sizeof(evdev_support)),
440 &evdev_support);
441 if (ret < 0)
442 perror("EVIOCGBIT");
443 if (!(evdev_support & (1 << EV_KEY))) {
444 fprintf(stderr, "%s doesn't have keys\n", name);
445 close(fd);
446 return -1;
447 }
448
449 return fd;
450}
451
452static void do_stdin_input(uint32_t *mode, int *changed)
453{
454 char c = 0;
455 int ret;
456
457 ret = read(STDIN_FILENO, &c, 1);
458 if (ret <= 0) {
459 perror("read stdin");
460 return;
461 }
462
463 switch (c) {
464 case '1':
465 printf("3btn mode\n");
466 *mode = OP_MODE_3BTN;
467 *changed = 1;
468 break;
469 case '2':
470 printf("6btn mode\n");
471 *mode = OP_MODE_6BTN;
472 *changed = 1;
473 break;
474 case '3':
475 printf("teamplayer mode\n");
476 *mode = OP_MODE_TEAMPLAYER;
477 *changed = 1;
478 break;
479 }
480}
481
482static int parse_binds(unsigned int player_i, const char *binds)
483{
484 return 0;
485}
486
487static int submit_urb(int fd, struct usbdevfs_urb *urb, int ep,
488 void *buf, size_t buf_size)
489{
490 memset(urb, 0, sizeof(*urb));
491 urb->type = USBDEVFS_URB_TYPE_INTERRUPT;
492 urb->endpoint = ep;
493 urb->buffer = buf;
494 urb->buffer_length = buf_size;
495
496 return ioctl(fd, USBDEVFS_SUBMITURB, urb);
497}
498
499enum my_urbs {
500 URB_DATA_IN,
501 URB_DATA_OUT,
502 URB_DBG_IN,
503 URB_CNT
504};
505
506static void usage(const char *argv0)
507{
508 fprintf(stderr, "usage:\n%s <-e player /dev/input/node>*\n"
509 " [-b <player> <keycode=mdbtn>[,keycode=mdbtn]]\n"
510 " [-m <mode>]\n\n"
511 " mdbtn: int 0-11: UDLR ABCS XYZM)\n"
512 " mode: int 0-2: 3btn 6btn teamplayer\n", argv0);
513 exit(1);
514}
515
516static void bad_arg(char *argv[], int a)
517{
518 fprintf(stderr, "bad arg %d: '%s'\n", a, argv[a]);
519 usage(argv[0]);
520}
521
522int main(int argc, char *argv[])
523{
524 struct teensy_dev dev;
525 struct usbdevfs_urb urb[URB_CNT];
526 struct usbdevfs_urb *reaped_urb;
527 uint32_t evdev_players[16];
528 int evdev_fds[16];
529 int evdev_fd_cnt = 0;
530 int wait_device = 0;
531 int pending_urbs = 0;
532 int had_input = 0;
533 fd_set rfds, wfds;
534 int mode_changed = 0;
535 char buf_dbg[64 + 1];
536 struct tp_pkt pkt_in;
537 struct tp_pkt pkt_out;
538 struct timeval *timeout = NULL;
539 struct timeval tout;
540 uint32_t mode = OP_MODE_3BTN;
541 uint32_t player;
542 int i, ret = -1;
543 int fd;
544
545 for (i = 0; i < ARRAY_SIZE(players); i++)
546 memcpy(&players[i].kc_map, evdev_md_default_map,
547 sizeof(players[i].kc_map));
548
549 for (i = 1; i < argc; i++) {
550 if (argv[i][0] == '-') {
551 switch (argv[i][1] | (argv[i][2] << 8)) {
552 case 'e':
553 if (argv[++i] == NULL)
554 bad_arg(argv, i);
555 player = strtoul(argv[i], NULL, 0);
556 if (player >= ARRAY_SIZE(players))
557 bad_arg(argv, i);
558
559 if (argv[++i] == NULL)
560 bad_arg(argv, i);
561 fd = open_evdev(argv[i]);
562 if (fd == -1)
563 bad_arg(argv, i);
564 if (evdev_fd_cnt >= ARRAY_SIZE(evdev_fds)) {
565 fprintf(stderr, "too many evdevs\n");
566 break;
567 }
568 evdev_players[evdev_fd_cnt] = player;
569 evdev_fds[evdev_fd_cnt] = fd;
570 evdev_fd_cnt++;
571 continue;
572 case 'b':
573 if (argv[++i] == NULL)
574 bad_arg(argv, i);
575 player = strtoul(argv[i], NULL, 0);
576 if (player >= ARRAY_SIZE(players))
577 bad_arg(argv, i);
578
579 if (argv[++i] == NULL)
580 bad_arg(argv, i);
581 ret = parse_binds(player, argv[i]);
582 if (ret != 0)
583 bad_arg(argv, i);
584 continue;
585 case 'm':
586 if (argv[++i] == NULL)
587 bad_arg(argv, i);
588 mode = strtoul(argv[i], NULL, 0);
589 mode_changed = 1;
590 continue;
591 }
592 }
593 bad_arg(argv, i);
594 }
595
596 if (evdev_fd_cnt == 0)
597 usage(argv[0]);
598
599 enable_echo(0);
600 signal(SIGINT, signal_handler);
601
602 dev.fd = -1;
603
604 while (!g_exit || (pending_urbs & (1 << URB_DATA_OUT)))
605 {
606 if (dev.fd == -1) {
607 ret = find_device(&dev, 0x16C0, 0x0486);
608 if (ret < 0)
609 break;
610
611 if (ret == 0) {
612 if (!wait_device) {
613 printf("waiting for device...\n");
614 wait_device = 1;
615 }
616 usleep(250000);
617 continue;
618 }
619
620 wait_device = 0;
621 pending_urbs = 0;
622
623 /* we wait first, then send commands, but if teensy
624 * is started already, it won't send anything back */
625 tout.tv_sec = 1;
626 tout.tv_usec = 0;
627 timeout = &tout;
628 }
629
630 if (!(pending_urbs & (1 << URB_DATA_IN))) {
631 memset(&pkt_in, 0, sizeof(pkt_in));
632 ret = submit_urb(dev.fd, &urb[URB_DATA_IN], dev.ifaces[0].ep_in,
633 &pkt_in, sizeof(pkt_in));
634 if (ret != 0) {
635 perror("USBDEVFS_SUBMITURB URB_DATA_IN");
636 break;
637 }
638
639 pending_urbs |= 1 << URB_DATA_IN;
640 }
641 if (!(pending_urbs & (1 << URB_DBG_IN))) {
642 ret = submit_urb(dev.fd, &urb[URB_DBG_IN], dev.ifaces[1].ep_in,
643 buf_dbg, sizeof(buf_dbg) - 1);
644 if (ret != 0) {
645 perror("USBDEVFS_SUBMITURB URB_DBG_IN");
646 break;
647 }
648
649 pending_urbs |= 1 << URB_DBG_IN;
650 }
651
652 FD_ZERO(&rfds);
653 FD_SET(STDIN_FILENO, &rfds);
654 for (i = 0; i < evdev_fd_cnt; i++)
655 FD_SET(evdev_fds[i], &rfds);
656
657 FD_ZERO(&wfds);
658 FD_SET(dev.fd, &wfds);
659
660 ret = select(dev.fd + 1, &rfds, &wfds, NULL, timeout);
661 if (ret < 0) {
662 perror("select");
663 break;
664 }
665 timeout = NULL;
666
667 /* something form stdin? */
668 if (FD_ISSET(STDIN_FILENO, &rfds))
669 do_stdin_input(&mode, &mode_changed);
670
671 /* something from input devices? */
672 had_input = 0;
673 for (i = 0; i < evdev_fd_cnt; i++) {
674 if (FD_ISSET(evdev_fds[i], &rfds)) {
675 do_evdev_input(evdev_fds[i], evdev_players[i]);
676 had_input = 1;
677 }
678 }
679 if (had_input) {
680 /* collect any other input changes before starting
681 * the slow USB transfer */
682 tout.tv_sec = tout.tv_usec = 0;
683 timeout = &tout;
684 continue;
685 }
686
687 /* something from USB? */
688 if (FD_ISSET(dev.fd, &wfds))
689 {
690 unsigned int which_urb;
691
692 reaped_urb = NULL;
693 ret = ioctl(dev.fd, USBDEVFS_REAPURB, &reaped_urb);
694 if (ret != 0) {
695 if (errno == ENODEV)
696 goto dev_close;
697 perror("USBDEVFS_REAPURB");
698 break;
699 }
700 which_urb = reaped_urb - urb;
701 if (which_urb < ARRAY_SIZE(urb))
702 pending_urbs &= ~(1 << which_urb);
703 else {
704 fprintf(stderr, "reaped unknown urb: %p #%u",
705 reaped_urb, which_urb);
706 }
707
708 if (reaped_urb != NULL && reaped_urb->status != 0) {
709 errno = -reaped_urb->status;
710 fprintf(stderr, "urb #%u: ", which_urb);
711 perror("");
712 if (reaped_urb->status == -EILSEQ) {
713 /* this is usually a sign of disconnect.. */
714 usleep(250000);
715 goto dev_close;
716 }
717 }
718 else if (reaped_urb == &urb[URB_DATA_IN])
719 {
720 /* some request from teensy */
721 printf("rx data?\n");
722 }
723 else if (reaped_urb == &urb[URB_DATA_OUT])
724 {
725 }
726 else if (reaped_urb == &urb[URB_DBG_IN])
727 {
728 /* debug text */
729 buf_dbg[reaped_urb->actual_length] = 0;
730 printf("%s", buf_dbg);
731
732 // continue receiving debug before sending out stuff
733 tout.tv_sec = 0;
734 tout.tv_usec = 1000;
735 timeout = &tout;
736 continue;
737 }
738 else {
739 fprintf(stderr, "reaped unknown urb? %p #%zu\n",
740 reaped_urb, reaped_urb - urb);
741 }
742 }
743
744 /* something to send? */
745 if (pending_urbs & (1 << URB_DATA_OUT))
746 // can't do that yet - out urb still busy
747 continue;
748
749 if (mode_changed) {
750 memset(&pkt_out, 0, sizeof(pkt_out));
751 pkt_out.type = PKT_UPD_MODE;
752 pkt_out.mode = mode;
753
754 ret = submit_urb(dev.fd, &urb[URB_DATA_OUT], dev.ifaces[0].ep_out,
755 &pkt_out, sizeof(pkt_out));
756 if (ret != 0) {
757 perror("USBDEVFS_SUBMITURB PKT_STREAM_ENABLE");
758 continue;
759 }
760 pending_urbs |= 1 << URB_DATA_OUT;
761 mode_changed = 0;
762 continue;
763 }
764
765 /* send buttons if there were any changes */
766 memset(&pkt_out, 0, sizeof(pkt_out));
767 for (i = 0; i < ARRAY_SIZE(players); i++) {
768 if (players[i].dirty)
769 pkt_out.changed_players |= 1 << i;
770 players[i].dirty = 0;
771
772 pkt_out.bnts[i] = players[i].state;
773 }
774 if (pkt_out.changed_players != 0) {
775 pkt_out.type = PKT_UPD_BTNS;
776
777 ret = submit_urb(dev.fd, &urb[URB_DATA_OUT], dev.ifaces[0].ep_out,
778 &pkt_out, sizeof(pkt_out));
779 if (ret != 0) {
780 perror("USBDEVFS_SUBMITURB PKT_FIXED_STATE");
781 break;
782 }
783 pending_urbs |= 1 << URB_DATA_OUT;
784 continue;
785 }
786
787 continue;
788
789dev_close:
790 close(dev.fd);
791 dev.fd = -1;
792 }
793
794 enable_echo(1);
795
796 if (dev.fd != -1) {
797 /* deal with pending URBs */
798 if (pending_urbs & (1 << URB_DATA_IN))
799 ioctl(dev.fd, USBDEVFS_DISCARDURB, &urb[URB_DATA_IN]);
800 if (pending_urbs & (1 << URB_DBG_IN))
801 ioctl(dev.fd, USBDEVFS_DISCARDURB, &urb[URB_DBG_IN]);
802 for (i = 0; i < URB_CNT; i++) {
803 if (pending_urbs & (1 << i)) {
804 ret = ioctl(dev.fd, USBDEVFS_REAPURB, &reaped_urb);
805 if (ret != 0)
806 perror("USBDEVFS_REAPURB");
807 }
808 }
809
810 close(dev.fd);
811 }
812
813 return ret;
814}
815
816// vim: ts=2:sw=2:expandtab