From dfcf0504eb78f2d88e72ed772d4789b5f9f97247 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Mon, 27 Feb 2012 14:19:15 +0000 Subject: [PATCH] parts: New vhci usb device bridge By Torbjorn Tyridal Signed-off-by: Michel Pollet --- examples/parts/vhci_usb.c | 340 ++++++++++++++++++++++++++++++++++++++ examples/parts/vhci_usb.h | 34 ++++ 2 files changed, 374 insertions(+) create mode 100644 examples/parts/vhci_usb.c create mode 100644 examples/parts/vhci_usb.h diff --git a/examples/parts/vhci_usb.c b/examples/parts/vhci_usb.c new file mode 100644 index 0000000..551309a --- /dev/null +++ b/examples/parts/vhci_usb.c @@ -0,0 +1,340 @@ +/* vim: set sts=4:sw=4:ts=4:noexpandtab + vhci_usb.c + + Copyright 2012 Torbjorn Tyridal + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +/* + this avrsim part is an usb connection between an usb AVR and the virtual + host controller interface vhci-usb - making your sim-avr connect as a real + usb device to the developer machine. + + You'll need vhci-usb and libusb_vhci to make it work. + http://sourceforge.net/projects/usb-vhci/ +*/ + +/* TODO iso endpoint support */ + +#include "vhci_usb.h" +#include "libusb_vhci.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "avr_usb.h" + +static void vhci_usb_attach_hook(struct avr_irq_t * irq, uint32_t value, void * param) +{ + struct vhci_usb_t * p = (struct vhci_usb_t*)param; + p->attached=!!value; + printf("avr attached: %d\n",p->attached); +} + +struct usbsetup { +uint8_t reqtype; uint8_t req; uint16_t wValue; uint16_t wIndex; uint16_t wLength; +}__attribute__((__packed__)); + +struct _ep { + uint8_t epnum; + uint8_t epsz; +}; + +char * setuprequests[] = {"GET_STATUS","CLEAR_FEAT","","SET_FEAT","","SET_ADDR","GET_DESCR","SET_DESCR","GET_CONF","SET_CONF"}; + +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) +{ + assert(reqtype&0x80); + int ret; + struct usbsetup buf = {reqtype,req,wValue,wIndex,wLength}; + struct avr_io_usb pkt = {ep->epnum,sizeof (struct usbsetup), (char*)&buf}; + + avr_ioctl(p->avr, AVR_IOCTL_USB_SETUP, &pkt); + + pkt.sz = wLength; + pkt.buf = data; + while ( wLength ) { + usleep(1000); + ret = avr_ioctl(p->avr, AVR_IOCTL_USB_READ, &pkt); + if (ret==AVR_IOCTL_USB_NAK) {printf(" NAK\n");usleep(50000); continue; } + if (ret==AVR_IOCTL_USB_STALL) {printf(" STALL\n"); return ret;} + assert(ret==0); + pkt.buf+=pkt.sz; + if (ep->epsz != pkt.sz) break; + wLength-=pkt.sz; + pkt.sz = wLength; + } + wLength=pkt.buf-data; + + usleep(1000); + pkt.sz=0; + while ( (ret = avr_ioctl(p->avr, AVR_IOCTL_USB_WRITE, &pkt)) == AVR_IOCTL_USB_NAK) { + usleep(50000); + } + assert(ret==0); + return wLength; +} + +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) +{ + assert((reqtype&0x80)==0); + int ret; + struct usbsetup buf = {reqtype,req,wValue,wIndex,wLength}; + struct avr_io_usb pkt = {ep->epnum,sizeof (struct usbsetup), (char*)&buf}; + + avr_ioctl(p->avr, AVR_IOCTL_USB_SETUP, &pkt); + usleep(10000); + + if (wLength>0) { + pkt.sz = (wLength>ep->epsz ? ep->epsz : wLength); + pkt.buf = data; + while ( ret = avr_ioctl(p->avr, AVR_IOCTL_USB_WRITE, &pkt)) { + if (ret==AVR_IOCTL_USB_NAK) {usleep(50000); continue; } + if (ret==AVR_IOCTL_USB_STALL) {printf(" STALL\n"); return ret;} + assert(ret==0); + if (pkt.sz != ep->epsz) break; + pkt.buf+=pkt.sz; + wLength-=pkt.sz; + pkt.sz = (wLength>ep->epsz ? ep->epsz : wLength); + } + } + + pkt.sz=0; + while ( (ret = avr_ioctl(p->avr, AVR_IOCTL_USB_READ, &pkt)) == AVR_IOCTL_USB_NAK) { + usleep(50000); + } + return ret; +} + + +static void handle_status_change(struct vhci_usb_t * p, struct usb_vhci_port_stat*prev, struct usb_vhci_port_stat*curr) +{ + if (~prev->status & USB_VHCI_PORT_STAT_POWER && curr->status & USB_VHCI_PORT_STAT_POWER) { + avr_ioctl(p->avr, AVR_IOCTL_USB_VBUS, (void*)1); + if (p->attached) { + if (usb_vhci_port_connect(p->fd, 1, USB_VHCI_DATA_RATE_FULL) <0) { + perror("port_connect"); + abort(); + } + } + } + if (prev->status & USB_VHCI_PORT_STAT_POWER && ~curr->status & USB_VHCI_PORT_STAT_POWER) + avr_ioctl(p->avr, AVR_IOCTL_USB_VBUS, 0); + + if (curr->change & USB_VHCI_PORT_STAT_C_RESET && + ~curr->status & USB_VHCI_PORT_STAT_RESET && + curr->status & USB_VHCI_PORT_STAT_ENABLE) { +// printf("END OF RESET\n"); + } + if (~prev->status & USB_VHCI_PORT_STAT_RESET && + curr->status & USB_VHCI_PORT_STAT_RESET) { + avr_ioctl(p->avr, AVR_IOCTL_USB_RESET, NULL); + usleep(50000); + if (curr->status & USB_VHCI_PORT_STAT_CONNECTION) { + if (usb_vhci_port_reset_done(p->fd,1,1)<0) { + perror("reset_done"); + abort(); + } + } + } + if (~prev->flags & USB_VHCI_PORT_STAT_FLAG_RESUMING && + curr->flags & USB_VHCI_PORT_STAT_FLAG_RESUMING) { + printf("port resuming\n"); + if (curr->status & USB_VHCI_PORT_STAT_CONNECTION) { + printf(" completing\n"); + if (usb_vhci_port_resumed(p->fd,1)<0) { + perror("resumed"); + abort(); + } + } + } + if (~prev->status & USB_VHCI_PORT_STAT_SUSPEND && + curr->status & USB_VHCI_PORT_STAT_SUSPEND) + printf("port suspedning\n"); + if (prev->status & USB_VHCI_PORT_STAT_ENABLE && + ~curr->status & USB_VHCI_PORT_STAT_ENABLE) + printf("port disabled\n"); + + *prev = *curr; +} + +static int get_ep0_size(struct vhci_usb_t * p) +{ + struct _ep ep0 = {0,8}; + uint8_t data[8]; + + int res = control_read(p, &ep0, 0x80, 6, 1<<8,0,8,data); + assert(res==8); + return data[7]; +} + +static void handle_ep0_control(struct vhci_usb_t * p, struct _ep * ep0, struct usb_vhci_urb * urb) +{ + int res; + if (urb->bmRequestType &0x80) { + res = control_read(p,ep0, + urb->bmRequestType, + urb->bRequest, + urb->wValue, + urb->wIndex, + urb->wLength, + urb->buffer); + if (res>=0) { + urb->buffer_actual=res; + res=0; + } + } + else + res = control_write(p,ep0, + urb->bmRequestType, + urb->bRequest, + urb->wValue, + urb->wIndex, + urb->wLength, + urb->buffer); + + if (res==AVR_IOCTL_USB_STALL) + urb->status = USB_VHCI_STATUS_STALL; + else + urb->status = USB_VHCI_STATUS_SUCCESS; +} + + +static void * vhci_usb_thread(void * param) +{ + struct vhci_usb_t * p = (struct vhci_usb_t*)param; + struct _ep ep0 = {0,0}; + struct usb_vhci_port_stat port_status; + int id,busnum;char*busid; + p->fd = usb_vhci_open(1, &id, &busnum, &busid); + + + if (p->fd<0) { + perror("open vhci failed"); + printf("driver loaded, and access bits ok?\n"); + abort(); + } + printf("Created virtual usb host with 1 port at %s (bus# %d)\n",busid,busnum); + memset(&port_status,0,sizeof port_status); + + bool avrattached=false; + + for(unsigned cycle=0;;cycle++) { + struct usb_vhci_work wrk; + + int res = usb_vhci_fetch_work(p->fd, &wrk); + + if (p->attached != avrattached) { + if (p->attached && port_status.status & USB_VHCI_PORT_STAT_POWER) { + if (usb_vhci_port_connect(p->fd, 1, USB_VHCI_DATA_RATE_FULL) <0) { + perror("port_connect"); + abort(); + } + } + if (!p->attached) { + ep0.epsz=0; + //disconnect + } + avrattached=p->attached; + } + + if ( res<0 ) { + if( errno == ETIMEDOUT || errno == EINTR || errno == ENODATA) continue; + perror("fetch work failed"); + abort(); + } + + switch(wrk.type) { + case USB_VHCI_WORK_TYPE_PORT_STAT: + handle_status_change(p, &port_status, &wrk.work.port_stat); + break; + case USB_VHCI_WORK_TYPE_PROCESS_URB: + if (!ep0.epsz) + ep0.epsz = get_ep0_size(p); + + wrk.work.urb.buffer=0; + wrk.work.urb.iso_packets=0; + if (wrk.work.urb.buffer_length) + wrk.work.urb.buffer = malloc(wrk.work.urb.buffer_length); + if (wrk.work.urb.packet_count) + wrk.work.urb.iso_packets = malloc(wrk.work.urb.packet_count * sizeof (struct usb_vhci_iso_packet)); + if (res) { + if (usb_vhci_fetch_data(p->fd, &wrk.work.urb)<0) { + if (errno != ECANCELED) perror("fetch_data"); + free(wrk.work.urb.buffer); + free(wrk.work.urb.iso_packets); + usb_vhci_giveback(p->fd, &wrk.work.urb); + break; + } + } + + if(usb_vhci_is_control(wrk.work.urb.type) && !(wrk.work.urb.epadr & 0x7f)) { + handle_ep0_control(p, &ep0, &wrk.work.urb); + + } else { + struct avr_io_usb pkt = {wrk.work.urb.epadr, wrk.work.urb.buffer_actual, wrk.work.urb.buffer}; + if (usb_vhci_is_out(wrk.work.urb.epadr)) + res=avr_ioctl(p->avr, AVR_IOCTL_USB_WRITE, &pkt); + else { + pkt.sz = wrk.work.urb.buffer_length; + res=avr_ioctl(p->avr, AVR_IOCTL_USB_READ, &pkt); + wrk.work.urb.buffer_actual=pkt.sz; + } + if (res==AVR_IOCTL_USB_STALL) + wrk.work.urb.status = USB_VHCI_STATUS_STALL; + else if (res==AVR_IOCTL_USB_NAK) + wrk.work.urb.status = USB_VHCI_STATUS_TIMEDOUT; + else + wrk.work.urb.status = USB_VHCI_STATUS_SUCCESS; + } + if (usb_vhci_giveback(p->fd, &wrk.work.urb)<0) + perror("giveback"); + free(wrk.work.urb.buffer); + free(wrk.work.urb.iso_packets); + break; + case USB_VHCI_WORK_TYPE_CANCEL_URB: + printf("cancel urb\n"); + break; + default: + printf("illegal work type\n"); + abort(); + } + + } +} + + +void vhci_usb_init(struct avr_t * avr, struct vhci_usb_t * p) +{ + p->avr = avr; + pthread_t thread; + + pthread_create(&thread, NULL, vhci_usb_thread, p); + +} + +void vhci_usb_connect(struct vhci_usb_t * p, char uart) +{ + avr_irq_t * t = avr_io_getirq(p->avr, AVR_IOCTL_USB_GETIRQ(), USB_IRQ_ATTACH); + avr_irq_register_notify(t, vhci_usb_attach_hook, p); +} + diff --git a/examples/parts/vhci_usb.h b/examples/parts/vhci_usb.h new file mode 100644 index 0000000..55d3012 --- /dev/null +++ b/examples/parts/vhci_usb.h @@ -0,0 +1,34 @@ +/* + vhci_usb.h + + Copyright 2012 Torbjorn Tyridal + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#include + +struct avr_t; +typedef struct vhci_usb_t { + struct avr_t * avr; + + bool attached; + int fd; +} vhci_usb_t; + + +void vhci_usb_connect(struct vhci_usb_t * p, char uart); +void vhci_usb_init(struct avr_t * avr, struct vhci_usb_t * p); -- 2.39.5