Makefiles: Updated for build on BSD
[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 vhci_usb_attach_hook(struct avr_irq_t * irq, uint32_t value, void * param)
47 {
48         struct vhci_usb_t * p = (struct vhci_usb_t*)param;
49         p->attached=!!value;
50         printf("avr attached: %d\n",p->attached);
51 }
52
53 struct usbsetup {
54 uint8_t reqtype; uint8_t req; uint16_t wValue; uint16_t wIndex; uint16_t wLength;
55 }__attribute__((__packed__));
56
57 struct _ep {
58         uint8_t epnum;
59         uint8_t epsz;
60 };
61
62 char * setuprequests[] = {"GET_STATUS","CLEAR_FEAT","","SET_FEAT","","SET_ADDR","GET_DESCR","SET_DESCR","GET_CONF","SET_CONF"};
63
64 static int control_read(struct vhci_usb_t * p, struct _ep * ep, uint8_t reqtype, uint8_t req, uint16_t wValue, uint16_t wIndex, uint16_t wLength, uint8_t * data)
65 {
66         assert(reqtype&0x80);
67         int ret;
68         struct usbsetup buf = {reqtype,req,wValue,wIndex,wLength};
69         struct avr_io_usb pkt = {ep->epnum,sizeof (struct usbsetup), (char*)&buf};
70
71         avr_ioctl(p->avr, AVR_IOCTL_USB_SETUP, &pkt);
72
73         pkt.sz = wLength;
74         pkt.buf = data;
75         while ( wLength ) {
76                 usleep(1000);
77                 ret = avr_ioctl(p->avr, AVR_IOCTL_USB_READ, &pkt);
78                 if (ret==AVR_IOCTL_USB_NAK) {printf(" NAK\n");usleep(50000); continue; }
79                 if (ret==AVR_IOCTL_USB_STALL) {printf(" STALL\n"); return ret;}
80                 assert(ret==0);
81                 pkt.buf+=pkt.sz;
82                 if (ep->epsz != pkt.sz) break;
83                 wLength-=pkt.sz;
84                 pkt.sz = wLength;
85         }
86         wLength=pkt.buf-data;
87
88         usleep(1000);
89         pkt.sz=0;
90         while ( (ret = avr_ioctl(p->avr, AVR_IOCTL_USB_WRITE, &pkt)) == AVR_IOCTL_USB_NAK) {
91                 usleep(50000);
92         }
93         assert(ret==0);
94         return wLength;
95 }
96
97 static int control_write(struct vhci_usb_t * p, struct _ep * ep, uint8_t reqtype, uint8_t req, uint16_t wValue, uint16_t wIndex, uint16_t wLength, uint8_t * data)
98 {
99         assert((reqtype&0x80)==0);
100         int ret;
101         struct usbsetup buf = {reqtype,req,wValue,wIndex,wLength};
102         struct avr_io_usb pkt = {ep->epnum,sizeof (struct usbsetup), (char*)&buf};
103
104         avr_ioctl(p->avr, AVR_IOCTL_USB_SETUP, &pkt);
105         usleep(10000);
106
107         if (wLength>0) {
108                 pkt.sz = (wLength>ep->epsz ? ep->epsz : wLength);
109                 pkt.buf = data;
110                 while ( ret = avr_ioctl(p->avr, AVR_IOCTL_USB_WRITE, &pkt)) {
111                         if (ret==AVR_IOCTL_USB_NAK) {usleep(50000); continue; }
112                         if (ret==AVR_IOCTL_USB_STALL) {printf(" STALL\n"); return ret;}
113                         assert(ret==0);
114                         if (pkt.sz != ep->epsz) break;
115                         pkt.buf+=pkt.sz;
116                         wLength-=pkt.sz;
117                         pkt.sz = (wLength>ep->epsz ? ep->epsz : wLength);
118                 }
119         }
120
121         pkt.sz=0;
122         while ( (ret = avr_ioctl(p->avr, AVR_IOCTL_USB_READ, &pkt)) == AVR_IOCTL_USB_NAK) {
123                 usleep(50000);
124         }
125         return ret;
126 }
127
128
129 static void handle_status_change(struct vhci_usb_t * p, struct usb_vhci_port_stat*prev, struct usb_vhci_port_stat*curr)
130 {
131         if (~prev->status & USB_VHCI_PORT_STAT_POWER && curr->status & USB_VHCI_PORT_STAT_POWER) {
132                 avr_ioctl(p->avr, AVR_IOCTL_USB_VBUS, (void*)1);
133                 if (p->attached) {
134                         if (usb_vhci_port_connect(p->fd, 1, USB_VHCI_DATA_RATE_FULL) <0) {
135                                 perror("port_connect");
136                                 abort();
137                         }
138                 }
139         }
140         if (prev->status & USB_VHCI_PORT_STAT_POWER && ~curr->status & USB_VHCI_PORT_STAT_POWER)
141                 avr_ioctl(p->avr, AVR_IOCTL_USB_VBUS, 0);
142
143         if (curr->change & USB_VHCI_PORT_STAT_C_RESET &&
144                         ~curr->status & USB_VHCI_PORT_STAT_RESET &&
145                         curr->status & USB_VHCI_PORT_STAT_ENABLE) {
146 //         printf("END OF RESET\n");
147         }
148         if (~prev->status & USB_VHCI_PORT_STAT_RESET &&
149                         curr->status & USB_VHCI_PORT_STAT_RESET) {
150                 avr_ioctl(p->avr, AVR_IOCTL_USB_RESET, NULL);
151                 usleep(50000);
152                 if (curr->status & USB_VHCI_PORT_STAT_CONNECTION) {
153                         if (usb_vhci_port_reset_done(p->fd,1,1)<0) {
154                                 perror("reset_done");
155                                 abort();
156                         }
157                 }
158         }
159         if (~prev->flags & USB_VHCI_PORT_STAT_FLAG_RESUMING &&
160                         curr->flags & USB_VHCI_PORT_STAT_FLAG_RESUMING) {
161                 printf("port resuming\n");
162                 if (curr->status & USB_VHCI_PORT_STAT_CONNECTION) {
163                         printf("  completing\n");
164                         if (usb_vhci_port_resumed(p->fd,1)<0) {
165                                 perror("resumed");
166                                 abort();
167                         }
168                 }
169         }
170         if (~prev->status & USB_VHCI_PORT_STAT_SUSPEND &&
171                         curr->status & USB_VHCI_PORT_STAT_SUSPEND)
172                 printf("port suspedning\n");
173         if (prev->status & USB_VHCI_PORT_STAT_ENABLE &&
174                         ~curr->status & USB_VHCI_PORT_STAT_ENABLE)
175                 printf("port disabled\n");
176
177         *prev = *curr;
178 }
179
180 static int get_ep0_size(struct vhci_usb_t * p)
181 {
182         struct _ep ep0 = {0,8};
183         uint8_t data[8];
184
185         int res = control_read(p, &ep0, 0x80, 6, 1<<8,0,8,data);
186         assert(res==8);
187         return data[7];
188 }
189
190 static void handle_ep0_control(struct vhci_usb_t * p, struct _ep * ep0, struct usb_vhci_urb * urb)
191 {
192         int res;
193         if (urb->bmRequestType &0x80) {
194                 res = control_read(p,ep0,
195                                 urb->bmRequestType,
196                                 urb->bRequest,
197                                 urb->wValue,
198                                 urb->wIndex,
199                                 urb->wLength,
200                                 urb->buffer);
201                         if (res>=0) {
202                                 urb->buffer_actual=res;
203                                 res=0;
204                         }
205         }
206         else
207                 res = control_write(p,ep0,
208                         urb->bmRequestType,
209                         urb->bRequest,
210                         urb->wValue,
211                         urb->wIndex,
212                         urb->wLength,
213                         urb->buffer);
214
215         if (res==AVR_IOCTL_USB_STALL)
216                 urb->status = USB_VHCI_STATUS_STALL;
217         else
218                 urb->status = USB_VHCI_STATUS_SUCCESS;
219 }
220
221
222 static void * vhci_usb_thread(void * param)
223 {
224         struct vhci_usb_t * p = (struct vhci_usb_t*)param;
225         struct _ep ep0 = {0,0};
226         struct usb_vhci_port_stat port_status;
227         int id,busnum;char*busid;
228         p->fd = usb_vhci_open(1, &id, &busnum, &busid);
229
230
231         if (p->fd<0) {
232                 perror("open vhci failed");
233                 printf("driver loaded, and access bits ok?\n");
234                 abort();
235         }
236         printf("Created virtual usb host with 1 port at %s (bus# %d)\n",busid,busnum);
237         memset(&port_status,0,sizeof port_status);
238
239         bool avrattached=false;
240
241         for(unsigned cycle=0;;cycle++) {
242                 struct usb_vhci_work wrk;
243
244                 int res = usb_vhci_fetch_work(p->fd, &wrk);
245
246                 if (p->attached != avrattached) {
247                         if (p->attached && port_status.status & USB_VHCI_PORT_STAT_POWER) {
248                                 if (usb_vhci_port_connect(p->fd, 1, USB_VHCI_DATA_RATE_FULL) <0) {
249                                         perror("port_connect");
250                                         abort();
251                                 }
252                         }
253                         if (!p->attached) {
254                                 ep0.epsz=0;
255                                 //disconnect
256                         }
257                         avrattached=p->attached;
258                 }
259
260                 if ( res<0 ) {
261                         if( errno == ETIMEDOUT || errno == EINTR || errno == ENODATA) continue;
262                         perror("fetch work failed");
263                         abort();
264                 }
265
266                 switch(wrk.type) {
267                 case USB_VHCI_WORK_TYPE_PORT_STAT:
268                         handle_status_change(p, &port_status, &wrk.work.port_stat);
269                         break;
270                 case USB_VHCI_WORK_TYPE_PROCESS_URB:
271                         if (!ep0.epsz)
272                                 ep0.epsz = get_ep0_size(p);
273
274                         wrk.work.urb.buffer=0;
275                         wrk.work.urb.iso_packets=0;
276                         if (wrk.work.urb.buffer_length)
277                                 wrk.work.urb.buffer = malloc(wrk.work.urb.buffer_length);
278                         if (wrk.work.urb.packet_count)
279                                 wrk.work.urb.iso_packets = malloc(wrk.work.urb.packet_count * sizeof (struct usb_vhci_iso_packet));
280                         if (res) {
281                                 if (usb_vhci_fetch_data(p->fd, &wrk.work.urb)<0) {
282                                         if (errno != ECANCELED) perror("fetch_data");
283                                         free(wrk.work.urb.buffer);
284                                         free(wrk.work.urb.iso_packets);
285                                         usb_vhci_giveback(p->fd, &wrk.work.urb);
286                                         break;
287                                 }
288                         }
289
290                         if(usb_vhci_is_control(wrk.work.urb.type) && !(wrk.work.urb.epadr & 0x7f)) {
291                                 handle_ep0_control(p, &ep0, &wrk.work.urb);
292
293                         } else {
294                                         struct avr_io_usb pkt = {wrk.work.urb.epadr, wrk.work.urb.buffer_actual, wrk.work.urb.buffer};
295                                         if (usb_vhci_is_out(wrk.work.urb.epadr))
296                                                 res=avr_ioctl(p->avr, AVR_IOCTL_USB_WRITE, &pkt);
297                                 else {
298                                                 pkt.sz = wrk.work.urb.buffer_length;
299                                         res=avr_ioctl(p->avr, AVR_IOCTL_USB_READ, &pkt);
300                                                 wrk.work.urb.buffer_actual=pkt.sz;
301                                         }
302                                 if (res==AVR_IOCTL_USB_STALL)
303                                         wrk.work.urb.status = USB_VHCI_STATUS_STALL;
304                                 else if (res==AVR_IOCTL_USB_NAK)
305                                         wrk.work.urb.status = USB_VHCI_STATUS_TIMEDOUT;
306                                 else
307                                         wrk.work.urb.status = USB_VHCI_STATUS_SUCCESS;
308                         }
309                         if (usb_vhci_giveback(p->fd, &wrk.work.urb)<0)
310                                 perror("giveback");
311                         free(wrk.work.urb.buffer);
312                         free(wrk.work.urb.iso_packets);
313                         break;
314                 case USB_VHCI_WORK_TYPE_CANCEL_URB:
315                         printf("cancel urb\n");
316                         break;
317                 default:
318                         printf("illegal work type\n");
319                         abort();
320                 }
321
322         }
323 }
324
325
326 void vhci_usb_init(struct avr_t * avr, struct vhci_usb_t * p)
327 {
328         p->avr = avr;
329         pthread_t thread;
330
331         pthread_create(&thread, NULL, vhci_usb_thread, p);
332
333 }
334
335 void vhci_usb_connect(struct vhci_usb_t * p, char uart)
336 {
337         avr_irq_t * t = avr_io_getirq(p->avr, AVR_IOCTL_USB_GETIRQ(), USB_IRQ_ATTACH);
338         avr_irq_register_notify(t, vhci_usb_attach_hook, p);
339 }
340