mingw: make simavr compilable with MinGW
[simavr] / examples / parts / vhci_usb.c
1 /* vim: set sts=4:sw=4:ts=4:noexpandtab
2         vhci_usb.c
3
4         Copyright 2012 Torbjorn Tyridal <ttyridal@gmail.com>
5
6         This file is part of simavr.
7
8         simavr is free software: you can redistribute it and/or modify
9         it under the terms of the GNU General Public License as published by
10         the Free Software Foundation, either version 3 of the License, or
11         (at your option) any later version.
12
13         simavr is distributed in the hope that it will be useful,
14         but WITHOUT ANY WARRANTY; without even the implied warranty of
15         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16         GNU General Public License for more details.
17
18         You should have received a copy of the GNU General Public License
19         along with simavr.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 /*
23         this avrsim part is an usb connection between an usb AVR and the virtual
24         host controller interface vhci-usb - making your sim-avr connect as a real
25         usb device to the developer machine.
26
27         You'll need vhci-usb and libusb_vhci to make it work.
28         http://sourceforge.net/projects/usb-vhci/
29 */
30
31 /* TODO iso endpoint support */
32
33 #include "vhci_usb.h"
34 #include "libusb_vhci.h"
35
36 #include <pthread.h>
37 #include <string.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <errno.h>
41 #include <unistd.h>
42 #include <assert.h>
43
44 #include "avr_usb.h"
45
46 static void
47 vhci_usb_attach_hook(
48         struct avr_irq_t * irq,
49         uint32_t value,
50         void * param)
51 {
52         struct vhci_usb_t * p = (struct vhci_usb_t*) param;
53         p->attached = !!value;
54         printf("avr attached: %d\n", p->attached);
55 }
56
57 struct usbsetup {
58 uint8_t reqtype; uint8_t req; uint16_t wValue; uint16_t wIndex; uint16_t wLength;
59 }__attribute__((__packed__));
60
61 struct _ep {
62         uint8_t epnum;
63         uint8_t epsz;
64 };
65
66 char * setuprequests[] =
67         { "GET_STATUS", "CLEAR_FEAT", "", "SET_FEAT", "", "SET_ADDR", "GET_DESCR",
68                 "SET_DESCR", "GET_CONF", "SET_CONF" };
69
70 static int
71 control_read(
72         struct vhci_usb_t * p,
73         struct _ep * ep,
74         uint8_t reqtype,
75         uint8_t req,
76         uint16_t wValue,
77         uint16_t wIndex,
78         uint16_t wLength,
79         uint8_t * data)
80 {
81         assert(reqtype&0x80);
82         int ret;
83         struct usbsetup buf =
84                 { reqtype, req, wValue, wIndex, wLength };
85         struct avr_io_usb pkt =
86                 { ep->epnum, sizeof(struct usbsetup), (uint8_t*) &buf };
87
88         avr_ioctl(p->avr, AVR_IOCTL_USB_SETUP, &pkt);
89
90         pkt.sz = wLength;
91         pkt.buf = data;
92         while (wLength) {
93                 usleep(1000);
94                 ret = avr_ioctl(p->avr, AVR_IOCTL_USB_READ, &pkt);
95                 if (ret == AVR_IOCTL_USB_NAK) {
96                         printf(" NAK\n");
97                         usleep(50000);
98                         continue;
99                 }
100                 if (ret == AVR_IOCTL_USB_STALL) {
101                         printf(" STALL\n");
102                         return ret;
103                 }
104                 assert(ret==0);
105                 pkt.buf += pkt.sz;
106                 if (ep->epsz != pkt.sz)
107                         break;
108                 wLength -= pkt.sz;
109                 pkt.sz = wLength;
110         }
111         wLength = pkt.buf - data;
112
113         usleep(1000);
114         pkt.sz = 0;
115         while ((ret = avr_ioctl(p->avr, AVR_IOCTL_USB_WRITE, &pkt))
116                 == AVR_IOCTL_USB_NAK) {
117                 usleep(50000);
118         }
119         assert(ret==0);
120         return wLength;
121 }
122
123 static int
124 control_write(
125         struct vhci_usb_t * p,
126         struct _ep * ep,
127         uint8_t reqtype,
128         uint8_t req,
129         uint16_t wValue,
130         uint16_t wIndex,
131         uint16_t wLength,
132         uint8_t * data)
133 {
134         assert((reqtype&0x80)==0);
135         int ret;
136         struct usbsetup buf =
137                 { reqtype, req, wValue, wIndex, wLength };
138         struct avr_io_usb pkt =
139                 { ep->epnum, sizeof(struct usbsetup), (uint8_t*) &buf };
140
141         avr_ioctl(p->avr, AVR_IOCTL_USB_SETUP, &pkt);
142         usleep(10000);
143
144         if (wLength > 0) {
145                 pkt.sz = (wLength > ep->epsz ? ep->epsz : wLength);
146                 pkt.buf = data;
147                 while ((ret = avr_ioctl(p->avr, AVR_IOCTL_USB_WRITE, &pkt)) != 0) {
148                         if (ret == AVR_IOCTL_USB_NAK) {
149                                 usleep(50000);
150                                 continue;
151                         }
152                         if (ret == AVR_IOCTL_USB_STALL) {
153                                 printf(" STALL\n");
154                                 return ret;
155                         }
156                         assert(ret==0);
157                         if (pkt.sz != ep->epsz)
158                                 break;
159                         pkt.buf += pkt.sz;
160                         wLength -= pkt.sz;
161                         pkt.sz = (wLength > ep->epsz ? ep->epsz : wLength);
162                 }
163         }
164
165         pkt.sz = 0;
166         while ((ret = avr_ioctl(p->avr, AVR_IOCTL_USB_READ, &pkt))
167                 == AVR_IOCTL_USB_NAK) {
168                 usleep(50000);
169         }
170         return ret;
171 }
172
173 static void
174 handle_status_change(
175         struct vhci_usb_t * p,
176         struct usb_vhci_port_stat*prev,
177         struct usb_vhci_port_stat*curr)
178 {
179         if (~prev->status & USB_VHCI_PORT_STAT_POWER
180                 && curr->status & USB_VHCI_PORT_STAT_POWER) {
181                 avr_ioctl(p->avr, AVR_IOCTL_USB_VBUS, (void*) 1);
182                 if (p->attached) {
183                         if (usb_vhci_port_connect(p->fd, 1, USB_VHCI_DATA_RATE_FULL) < 0) {
184                                 perror("port_connect");
185                                 abort();
186                         }
187                 }
188         }
189         if (prev->status & USB_VHCI_PORT_STAT_POWER
190                 && ~curr->status & USB_VHCI_PORT_STAT_POWER)
191                 avr_ioctl(p->avr, AVR_IOCTL_USB_VBUS, 0);
192
193         if (curr->change & USB_VHCI_PORT_STAT_C_RESET
194                 && ~curr->status & USB_VHCI_PORT_STAT_RESET
195                 && curr->status & USB_VHCI_PORT_STAT_ENABLE) {
196 //         printf("END OF RESET\n");
197         }
198         if (~prev->status & USB_VHCI_PORT_STAT_RESET
199                 && curr->status & USB_VHCI_PORT_STAT_RESET) {
200                 avr_ioctl(p->avr, AVR_IOCTL_USB_RESET, NULL);
201                 usleep(50000);
202                 if (curr->status & USB_VHCI_PORT_STAT_CONNECTION) {
203                         if (usb_vhci_port_reset_done(p->fd, 1, 1) < 0) {
204                                 perror("reset_done");
205                                 abort();
206                         }
207                 }
208         }
209         if (~prev->flags & USB_VHCI_PORT_STAT_FLAG_RESUMING
210                 && curr->flags & USB_VHCI_PORT_STAT_FLAG_RESUMING) {
211                 printf("port resuming\n");
212                 if (curr->status & USB_VHCI_PORT_STAT_CONNECTION) {
213                         printf("  completing\n");
214                         if (usb_vhci_port_resumed(p->fd, 1) < 0) {
215                                 perror("resumed");
216                                 abort();
217                         }
218                 }
219         }
220         if (~prev->status & USB_VHCI_PORT_STAT_SUSPEND
221                 && curr->status & USB_VHCI_PORT_STAT_SUSPEND)
222                 printf("port suspedning\n");
223         if (prev->status & USB_VHCI_PORT_STAT_ENABLE
224                 && ~curr->status & USB_VHCI_PORT_STAT_ENABLE)
225                 printf("port disabled\n");
226
227         *prev = *curr;
228 }
229
230 static int
231 get_ep0_size(
232                 struct vhci_usb_t * p)
233 {
234         struct _ep ep0 =
235                 { 0, 8 };
236         uint8_t data[8];
237
238         int res = control_read(p, &ep0, 0x80, 6, 1 << 8, 0, 8, data);
239         assert(res==8);
240         return data[7];
241 }
242
243 static void
244 handle_ep0_control(
245         struct vhci_usb_t * p,
246         struct _ep * ep0,
247         struct usb_vhci_urb * urb)
248 {
249         int res;
250         if (urb->bmRequestType &0x80) {
251                 res = control_read(p,ep0,
252                                 urb->bmRequestType,
253                                 urb->bRequest,
254                                 urb->wValue,
255                                 urb->wIndex,
256                                 urb->wLength,
257                                 urb->buffer);
258                         if (res>=0) {
259                                 urb->buffer_actual=res;
260                                 res=0;
261                         }
262         }
263         else
264                 res = control_write(p,ep0,
265                         urb->bmRequestType,
266                         urb->bRequest,
267                         urb->wValue,
268                         urb->wIndex,
269                         urb->wLength,
270                         urb->buffer);
271
272         if (res==AVR_IOCTL_USB_STALL)
273                 urb->status = USB_VHCI_STATUS_STALL;
274         else
275                 urb->status = USB_VHCI_STATUS_SUCCESS;
276 }
277
278 static void *
279 vhci_usb_thread(
280                 void * param)
281 {
282         struct vhci_usb_t * p = (struct vhci_usb_t*) param;
283         struct _ep ep0 =
284                 { 0, 0 };
285         struct usb_vhci_port_stat port_status;
286         int id, busnum;
287         char*busid;
288         p->fd = usb_vhci_open(1, &id, &busnum, &busid);
289
290         if (p->fd < 0) {
291                 perror("open vhci failed");
292                 printf("driver loaded, and access bits ok?\n");
293                 abort();
294         }
295         printf("Created virtual usb host with 1 port at %s (bus# %d)\n", busid,
296                 busnum);
297         memset(&port_status, 0, sizeof port_status);
298
299         bool avrattached = false;
300
301         for (unsigned cycle = 0;; cycle++) {
302                 struct usb_vhci_work wrk;
303
304                 int res = usb_vhci_fetch_work(p->fd, &wrk);
305
306                 if (p->attached != avrattached) {
307                         if (p->attached && port_status.status & USB_VHCI_PORT_STAT_POWER) {
308                                 if (usb_vhci_port_connect(p->fd, 1, USB_VHCI_DATA_RATE_FULL)
309                                         < 0) {
310                                         perror("port_connect");
311                                         abort();
312                                 }
313                         }
314                         if (!p->attached) {
315                                 ep0.epsz = 0;
316                                 //disconnect
317                         }
318                         avrattached = p->attached;
319                 }
320
321                 if (res < 0) {
322                         if (errno == ETIMEDOUT || errno == EINTR || errno == ENODATA)
323                                 continue;
324                         perror("fetch work failed");
325                         abort();
326                 }
327
328                 switch (wrk.type) {
329                         case USB_VHCI_WORK_TYPE_PORT_STAT:
330                                 handle_status_change(p, &port_status, &wrk.work.port_stat);
331                                 break;
332                         case USB_VHCI_WORK_TYPE_PROCESS_URB:
333                                 if (!ep0.epsz)
334                                         ep0.epsz = get_ep0_size(p);
335
336                                 wrk.work.urb.buffer = 0;
337                                 wrk.work.urb.iso_packets = 0;
338                                 if (wrk.work.urb.buffer_length)
339                                         wrk.work.urb.buffer = malloc(wrk.work.urb.buffer_length);
340                                 if (wrk.work.urb.packet_count)
341                                         wrk.work.urb.iso_packets = malloc(
342                                                 wrk.work.urb.packet_count
343                                                         * sizeof(struct usb_vhci_iso_packet));
344                                 if (res) {
345                                         if (usb_vhci_fetch_data(p->fd, &wrk.work.urb) < 0) {
346                                                 if (errno != ECANCELED)
347                                                         perror("fetch_data");
348                                                 free(wrk.work.urb.buffer);
349                                                 free(wrk.work.urb.iso_packets);
350                                                 usb_vhci_giveback(p->fd, &wrk.work.urb);
351                                                 break;
352                                         }
353                                 }
354
355                                 if (usb_vhci_is_control(wrk.work.urb.type)
356                                         && !(wrk.work.urb.epadr & 0x7f)) {
357                                         handle_ep0_control(p, &ep0, &wrk.work.urb);
358
359                                 } else {
360                                         struct avr_io_usb pkt =
361                                                 { wrk.work.urb.epadr, wrk.work.urb.buffer_actual,
362                                                         wrk.work.urb.buffer };
363                                         if (usb_vhci_is_out(wrk.work.urb.epadr))
364                                                 res = avr_ioctl(p->avr, AVR_IOCTL_USB_WRITE, &pkt);
365                                         else {
366                                                 pkt.sz = wrk.work.urb.buffer_length;
367                                                 res = avr_ioctl(p->avr, AVR_IOCTL_USB_READ, &pkt);
368                                                 wrk.work.urb.buffer_actual = pkt.sz;
369                                         }
370                                         if (res == AVR_IOCTL_USB_STALL)
371                                                 wrk.work.urb.status = USB_VHCI_STATUS_STALL;
372                                         else if (res == AVR_IOCTL_USB_NAK)
373                                                 wrk.work.urb.status = USB_VHCI_STATUS_TIMEDOUT;
374                                         else
375                                                 wrk.work.urb.status = USB_VHCI_STATUS_SUCCESS;
376                                 }
377                                 if (usb_vhci_giveback(p->fd, &wrk.work.urb) < 0)
378                                         perror("giveback");
379                                 free(wrk.work.urb.buffer);
380                                 free(wrk.work.urb.iso_packets);
381                                 break;
382                         case USB_VHCI_WORK_TYPE_CANCEL_URB:
383                                 printf("cancel urb\n");
384                                 break;
385                         default:
386                                 printf("illegal work type\n");
387                                 abort();
388                 }
389
390         }
391 }
392
393 void
394 vhci_usb_init(
395                 struct avr_t * avr,
396                 struct vhci_usb_t * p)
397 {
398         p->avr = avr;
399         pthread_t thread;
400
401         pthread_create(&thread, NULL, vhci_usb_thread, p);
402
403 }
404
405 void
406 vhci_usb_connect(
407                 struct vhci_usb_t * p,
408                 char uart)
409 {
410         avr_irq_t * t = avr_io_getirq(p->avr, AVR_IOCTL_USB_GETIRQ(),
411                 USB_IRQ_ATTACH);
412         avr_irq_register_notify(t, vhci_usb_attach_hook, p);
413 }
414