teensytp: initial implementation (derived from teensytas)
[megadrive.git] / teensytp / host / main.c
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
48 struct 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 */
57 static 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
125 next:
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
138 found:
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
254 out:
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
265 static 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;
303 out:
304   close(fd);
305
306   return retval;
307 }
308
309 static int g_exit;
310
311 static void signal_handler(int sig)
312 {
313   g_exit = 1;
314   signal(sig, SIG_DFL);
315 }
316
317 /* MXYZ SACB RLDU */
318 enum 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
335 static 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
360 static struct player_state {
361   uint32_t kc_map[KEY_CNT];
362   uint32_t state;
363   int dirty;
364 } players[4];
365
366 static 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
428 static 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
452 static 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
482 static int parse_binds(unsigned int player_i, const char *binds)
483 {
484   return 0;
485 }
486
487 static 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
499 enum my_urbs {
500   URB_DATA_IN,
501   URB_DATA_OUT,
502   URB_DBG_IN,
503   URB_CNT
504 };
505
506 static 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
516 static 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
522 int 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
789 dev_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