48517a3b2613b18181542d93758ec79d205480b7
[megadrive.git] / host / main.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdint.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7 #include <sys/ioctl.h>
8 #include <sys/select.h>
9 #include <unistd.h>
10 #include <dirent.h>
11 #include <errno.h>
12 #include <linux/usbdevice_fs.h>
13 #include <linux/usb/ch9.h>
14
15 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
16
17 struct teensy_dev {
18   int fd;
19   struct {
20     int ep_in;
21     int ep_out;
22   } ifaces[2];
23 };
24
25 /* return 1 if founf, 0 if not, < 0 on error */
26 static int find_device(struct teensy_dev *dev,
27   uint16_t vendor, uint16_t product)
28 {
29   const char path_root[] = "/dev/bus/usb";
30   union {
31     struct usb_descriptor_header hdr;
32     struct usb_device_descriptor d;
33     struct usb_config_descriptor c;
34     struct usb_interface_descriptor i;
35     struct usb_endpoint_descriptor e;
36     char space[0x100]; /* enough? */
37   } desc;
38   char path_bus[256], path_dev[256];
39   struct dirent *ent, *ent_bus;
40   DIR *dir = NULL, *dir_bus = NULL;
41   int num, fd = -1;
42   int iface = -1;
43   int retval = -1;
44   int ret;
45
46   memset(dev, 0xff, sizeof(*dev));
47
48   dir = opendir(path_root);
49   if (dir == NULL) {
50     perror("opendir");
51     return -1;
52   }
53
54   for (ent = readdir(dir); ent != NULL; ent = readdir(dir)) {
55     /* should be a number like 000 */
56     if (sscanf(ent->d_name, "%03d", &num) != 1)
57       continue;
58
59     snprintf(path_bus, sizeof(path_bus), "%s/%s",
60         path_root, ent->d_name);
61
62     dir_bus = opendir(path_bus);
63     if (dir_bus == NULL)
64       continue;
65
66     ent_bus = readdir(dir_bus);
67     for (; ent_bus != NULL; ent_bus = readdir(dir_bus)) {
68       if (sscanf(ent->d_name, "%03d", &num) != 1)
69         continue;
70
71       snprintf(path_dev, sizeof(path_dev), "%s/%s/%s",
72           path_root, ent->d_name, ent_bus->d_name);
73
74       fd = open(path_dev, O_RDWR);
75       if (fd == -1)
76         continue;
77
78       ret = read(fd, &desc.d, sizeof(desc.d));
79       if (ret != sizeof(desc.d)) {
80         fprintf(stderr, "desc read: %d/%zd: ", ret, sizeof(desc.d));
81         perror("");
82         goto next;
83       }
84
85       if (desc.d.bDescriptorType != USB_DT_DEVICE) {
86         fprintf(stderr, "%s: bad DT: 0x%02x\n",
87             path_dev, desc.d.bDescriptorType);
88         goto next;
89       }
90
91       if (desc.d.idVendor == vendor && desc.d.idProduct == product)
92         goto found;
93
94 next:
95       close(fd);
96       fd = -1;
97     }
98
99     closedir(dir_bus);
100     dir_bus = NULL;
101   }
102
103   /* not found */
104   retval = 0;
105   goto out;
106
107 found:
108   if (desc.d.bNumConfigurations != 1) {
109     fprintf(stderr, "unexpected bNumConfigurations: %u\n",
110         desc.d.bNumConfigurations);
111     goto out;
112   }
113
114   /* walk through all descriptors */
115   while (1)
116   {
117     ret = read(fd, &desc.hdr, sizeof(desc.hdr));
118     if (ret == 0)
119       break;
120     if (ret != sizeof(desc.hdr)) {
121       fprintf(stderr, "desc.hdr read: %d/%zd: ", ret, sizeof(desc.hdr));
122       perror("");
123       break;
124     }
125
126     ret = (int)lseek(fd, -sizeof(desc.hdr), SEEK_CUR);
127     if (ret == -1) {
128       perror("lseek");
129       break;
130     }
131
132     ret = read(fd, &desc, desc.hdr.bLength);
133     if (ret != desc.hdr.bLength) {
134       fprintf(stderr, "desc read: %d/%u: ", ret, desc.hdr.bLength);
135       perror("");
136       break;
137     }
138
139     switch (desc.hdr.bDescriptorType) {
140       case USB_DT_CONFIG:
141         if (desc.c.bNumInterfaces != 2) {
142           fprintf(stderr, "unexpected bNumInterfaces: %u\n",
143               desc.c.bNumInterfaces);
144           goto out;
145         }
146         break;
147
148       case USB_DT_INTERFACE:
149         if (desc.i.bInterfaceClass != USB_CLASS_HID
150             || desc.i.bInterfaceSubClass != 0
151             || desc.i.bInterfaceProtocol != 0) {
152           fprintf(stderr, "unexpected interface %x:%x:%x\n",
153             desc.i.bInterfaceClass, desc.i.bInterfaceSubClass,
154             desc.i.bInterfaceProtocol);
155           goto out;
156         }
157         if (desc.i.bNumEndpoints != 2) {
158           fprintf(stderr, "unexpected bNumEndpoints: %u\n",
159             desc.i.bNumEndpoints);
160           goto out;
161         }
162         iface++;
163         break;
164
165       case USB_DT_ENDPOINT:
166         if (iface < 0 || iface >= ARRAY_SIZE(dev->ifaces)) {
167           fprintf(stderr, "bad iface: %d\n", iface);
168           goto out;
169         }
170         if (desc.e.wMaxPacketSize != 64 && desc.e.wMaxPacketSize != 32) {
171           fprintf(stderr, "iface %d, EP %02x: "
172             "unexpected wMaxPacketSize: %u\n",
173             iface, desc.e.bEndpointAddress, desc.e.wMaxPacketSize);
174           goto out;
175         }
176         if (desc.e.bEndpointAddress & 0x80)
177           dev->ifaces[iface].ep_in = desc.e.bEndpointAddress; // & 0x7F;
178         else
179           dev->ifaces[iface].ep_out = desc.e.bEndpointAddress;
180         break;
181
182       case 0x21:
183         /* ignore */
184         break;
185
186       default:
187         fprintf(stderr, "skipping desc 0x%02x\n",
188           desc.hdr.bDescriptorType);
189         break;
190     }
191   }
192
193   /* claim interfaces */
194   for (iface = 0; iface < ARRAY_SIZE(dev->ifaces); iface++) {
195     struct usbdevfs_ioctl usbio;
196
197     if (dev->ifaces[iface].ep_in == -1) {
198       fprintf(stderr, "missing ep_in, iface: %d\n", iface);
199       goto out;
200     }
201     if (dev->ifaces[iface].ep_out == -1) {
202       fprintf(stderr, "missing ep_out, iface: %d\n", iface);
203       goto out;
204     }
205
206     /* disconnect default driver */
207     memset(&usbio, 0, sizeof(usbio));
208     usbio.ifno = iface;
209     usbio.ioctl_code = USBDEVFS_DISCONNECT;
210     ret = ioctl(fd, USBDEVFS_IOCTL, &usbio);
211     if (ret != 0 && errno != ENODATA)
212       perror("USBDEVFS_DISCONNECT");
213
214     ret = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &iface);
215     if (ret != 0)
216       perror("USBDEVFS_CLAIMINTERFACE");
217   }
218
219   dev->fd = fd;
220   fd = -1;
221   retval = 1;
222
223 out:
224   if (fd != -1)
225     close(fd);
226   if (dir_bus != NULL)
227     closedir(dir_bus);
228   if (dir != NULL)
229     closedir(dir);
230
231   return retval;
232 }
233
234 enum my_urbs {
235   URB_DATA_IN,
236   URB_DATA_OUT,
237   URB_DBG_IN,
238   URB_CNT
239 };
240
241 int main(int argc, char *argv[])
242 {
243   struct teensy_dev dev;
244   struct usbdevfs_urb urb[URB_CNT];
245   struct usbdevfs_urb *reaped_urb;
246   char buf_dbg[64 + 1], buf_in[64];
247   int wait_device = 0;
248   int dbg_in_sent = 0;
249   int data_in_sent = 0;
250   fd_set wfds;
251   int ret;
252
253   dev.fd = -1;
254
255   while (1)
256   {
257     if (dev.fd == -1) {
258       ret = find_device(&dev, 0x16C0, 0x0486);
259       if (ret < 0)
260         return ret;
261
262       if (ret == 0) {
263         if (!wait_device) {
264           printf("waiting for device..\n");
265           wait_device = 1;
266         }
267         usleep(250000);
268         continue;
269       }
270
271       wait_device = 0;
272       data_in_sent = 0;
273       dbg_in_sent = 0;
274     }
275
276     if (!data_in_sent) {
277       memset(&urb[URB_DATA_IN], 0, sizeof(urb[URB_DATA_IN]));
278       urb[URB_DATA_IN].type = USBDEVFS_URB_TYPE_INTERRUPT;
279       urb[URB_DATA_IN].endpoint = dev.ifaces[0].ep_in;
280       urb[URB_DATA_IN].buffer = buf_in;
281       urb[URB_DATA_IN].buffer_length = sizeof(buf_in);
282
283       ret = ioctl(dev.fd, USBDEVFS_SUBMITURB, &urb[URB_DATA_IN]);
284       if (ret != 0) {
285         perror("USBDEVFS_SUBMITURB URB_DATA_IN");
286         return 1;
287       }
288       data_in_sent = 1;
289     }
290     if (!dbg_in_sent) {
291       memset(&urb[URB_DBG_IN], 0, sizeof(urb[URB_DBG_IN]));
292       urb[URB_DBG_IN].type = USBDEVFS_URB_TYPE_INTERRUPT;
293       urb[URB_DBG_IN].endpoint = dev.ifaces[1].ep_in;
294       urb[URB_DBG_IN].buffer = buf_dbg;
295       urb[URB_DBG_IN].buffer_length = sizeof(buf_dbg) - 1;
296
297       ret = ioctl(dev.fd, USBDEVFS_SUBMITURB, &urb[URB_DBG_IN]);
298       if (ret != 0) {
299         perror("USBDEVFS_SUBMITURB URB_DBG_IN");
300         return 1;
301       }
302       dbg_in_sent = 1;
303     }
304
305     FD_ZERO(&wfds);
306     FD_SET(dev.fd, &wfds);
307
308     ret = select(dev.fd + 1, NULL, &wfds, NULL, NULL);
309     if (ret < 0) {
310       perror("select");
311       return 1;
312     }
313
314     if (FD_ISSET(dev.fd, &wfds)) {
315       reaped_urb = NULL;
316       ret = ioctl(dev.fd, USBDEVFS_REAPURB, &reaped_urb);
317       if (ret != 0) {
318         if (errno == ENODEV)
319           goto dev_close;
320         perror("USBDEVFS_REAPURB");
321         return 1;
322       }
323
324       if (reaped_urb != NULL && reaped_urb->status != 0) {
325         errno = -reaped_urb->status;
326         perror("urb status");
327         if (reaped_urb->status == -EILSEQ) {
328           /* this is usually a sign of disconnect.. */
329           usleep(250000);
330           goto dev_close;
331         }
332       }
333
334       if (reaped_urb == &urb[URB_DATA_IN]) {
335         printf("*data*\n");
336         data_in_sent = 0;
337       }
338       else if (reaped_urb == &urb[URB_DBG_IN]) {
339         /* debug text */
340         buf_dbg[reaped_urb->actual_length] = 0;
341         printf("%s", buf_dbg);
342         dbg_in_sent = 0;
343       }
344       else {
345         fprintf(stderr, "reaped unknown urb? %p\n", reaped_urb);
346       }
347     }
348     continue;
349
350 dev_close:
351     close(dev.fd);
352     dev.fd = -1;
353   }
354
355   return 0;
356 }
357
358 // vim: ts=2:sw=2:expandtab