Commit 679955cf31d0c881080efe9d0b4b949732706ff9
authorMichel Pollet <buserror@gmail.com>
Mon, 27 Feb 2012 14:17:25 +0000 (14:17 +0000)
committerMichel Pollet <buserror@gmail.com>
Mon, 27 Feb 2012 14:17:25 +0000 (14:17 +0000)
Straight from Torbjorn Tyridal <ttyridal@gmail.com> with
just a bit of source code remangling

Signed-off-by: Michel Pollet <buserror@gmail.com>
2 files changed:
simavr/sim/avr_usb.c [new file with mode: 0644]
simavr/sim/avr_usb.h [new file with mode: 0644]

diff --git a/simavr/sim/avr_usb.c b/simavr/sim/avr_usb.c
new file mode 100644 (file)
index 0000000..4f75374
--- /dev/null
@@ -0,0 +1,800 @@
+/* vim: set sts=4:sw=4:ts=4:noexpandtab
+       avr_usb.c
+
+       Copyright 2012 Torbjorn Tyridal <ttyridal@gmail.com>
+
+       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 <http://www.gnu.org/licenses/>.
+ */
+
+/* TODO correct reset values */
+/* TODO generate sofi every 1ms (when connected) */
+/* TODO otg support? */
+/* TODO drop bitfields? */
+/* TODO thread safe ioctls */
+/* TODO dual-bank endpoint buffers */
+/* TODO actually pay attention to endpoint memory allocation ? buggy endpoint configuration doesn't matter in the simulator now. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "avr_usb.h"
+
+enum usb_regs
+{
+       usbcon = 0,
+       udcon = 8,
+       udint = 9,
+       udien = 10,
+       udaddr = 11,
+       udfnuml = 12,
+       udfnumh = 13,
+       udmfn = 14,
+//     _res=15,
+       ueintx = 16,
+       uenum = 17,
+       uerst = 18,
+       ueconx = 19,
+       uecfg0x = 20,
+       uecfg1x = 21,
+       uesta0x = 22,
+       uesta1x = 23,
+       ueienx = 24,
+       uedatx = 25,
+       uebclx = 26,
+//     _res2=27,
+       ueint = 28,
+       otgtcon = 29,
+};
+
+union _ueintx {
+       struct {
+               uint8_t txini :1;
+               uint8_t stalledi :1;
+               uint8_t rxouti :1;
+               uint8_t rxstpi :1;
+               uint8_t nakouti :1;
+               uint8_t rwal :1;
+               uint8_t nakini :1;
+               uint8_t fifocon :1;
+       };
+       uint8_t v;
+};
+
+struct _epstate {
+       union _ueintx ueintx;
+       uint8_t dummy1;
+       uint8_t dummy2;
+       union {
+               struct {
+                       uint8_t epen :1;
+                       uint8_t res :2;
+                       uint8_t rstdt :1;
+                       uint8_t stallrqc :1;
+                       uint8_t stallrq :1;
+               };
+               uint8_t v;
+       } ueconx;
+       union {
+               struct {
+                       uint8_t epdir :1;
+                       uint8_t res :5;
+                       uint8_t eptype :2;
+               };
+               uint8_t v;
+       } uecfg0x;
+       union {
+               struct {
+                       uint8_t res :1;
+                       uint8_t alloc :1;
+                       uint8_t epbk1 :2;
+                       uint8_t epsize :3;
+                       uint8_t res2 :1;
+               };
+               uint8_t v;
+       } uecfg1x;
+       union {
+               struct {
+                       uint8_t nbusybk :2;
+                       uint8_t dtseq :2;
+                       uint8_t res :1;
+                       uint8_t underfi :1;
+                       uint8_t overfi :1;
+                       uint8_t cfgok :1;
+               };
+               uint8_t v;
+       } uesta0x;
+       union {
+               struct {
+                       uint8_t curbk :2;
+                       uint8_t ctrldir :1;
+                       uint8_t res :5;
+               };
+               uint8_t v;
+       } uesta1x;
+       union {
+               struct {
+                       uint8_t txine :1;
+                       uint8_t stallede :1;
+                       uint8_t rxoute :1;
+                       uint8_t rxstpe :1;
+                       uint8_t nakoute :1;
+                       uint8_t res :1;
+                       uint8_t nakine :1;
+                       uint8_t flerre :1;
+               };
+               uint8_t v;
+       } ueienx;
+
+       struct {
+               uint8_t bytes[64];
+               uint8_t tail;
+       } bank[2];
+       uint8_t current_bank;
+       int setup_is_read;
+};
+
+struct usb_internal_state {
+       struct _epstate ep_state[5];
+       avr_int_vector_t com_vect;
+       avr_int_vector_t gen_vect;
+};
+
+const uint8_t num_endpoints = 5;//sizeof (struct usb_internal_state.ep_state) / sizeof (struct usb_internal_state.ep_state[0]);
+
+static uint8_t
+current_ep_to_cpu(
+               avr_usb_t * p)
+{
+       return p->io.avr->data[p->r_usbcon + uenum];
+}
+
+static struct _epstate *
+get_epstate(
+               avr_usb_t * p,
+               uint8_t ep)
+{
+       assert(ep < num_endpoints);
+       return &p->state->ep_state[ep];
+}
+
+
+enum epints {
+       txini = 0,
+       stalledi = 1,
+       rxouti = 2,
+       rxstpi = 3,
+       nakouti = 4,
+       nakini = 6,
+       overfi = 10,
+       underfi = 11,
+};
+
+static void
+raise_ep_interrupt(
+        struct avr_t * avr,
+        avr_usb_t * p,
+        uint8_t ep,
+        enum epints irq)
+{
+       struct _epstate * epstate = get_epstate(p, ep);
+       assert(ep < num_endpoints);
+       avr->data[p->r_usbcon + ueint] |= 1 << ep;
+       switch (irq) {
+               case txini:
+               case stalledi:
+               case rxouti:
+               case nakouti:
+               case nakini:
+                       epstate->ueintx.v |= 1 << irq;
+                       if (epstate->ueienx.v & (1 << irq))
+                               avr_raise_interrupt(avr, &p->state->com_vect);
+                       break;
+               case rxstpi:
+                       epstate->ueintx.v |= 1 << irq;
+                       if (epstate->ueienx.v & (1 << irq))
+                               avr_raise_interrupt(avr, &p->state->com_vect);
+                       break;
+               case overfi:
+                       epstate->uesta0x.overfi = 1;
+                       if (epstate->ueienx.flerre)
+                               avr_raise_interrupt(avr, &p->state->com_vect);
+                       break;
+               case underfi:
+                       epstate->uesta0x.underfi = 1;
+                       if (epstate->ueienx.flerre)
+                               avr_raise_interrupt(avr, &p->state->com_vect);
+                       break;
+               default:
+                       assert(0);
+       }
+}
+
+enum usbints {
+       suspi = 0, sofi = 2, eorsti = 3, wakeupi = 4, eorsmi = 5, uprsmi = 6
+};
+static void
+raise_usb_interrupt(
+               avr_usb_t * p,
+               enum usbints irq)
+{
+       uint8_t * Rudien = &p->io.avr->data[p->r_usbcon + udien];
+       uint8_t * Rudint = &p->io.avr->data[p->r_usbcon + udint];
+
+       switch (irq) {
+               case uprsmi:
+               case eorsmi:
+               case wakeupi:
+               case eorsti:
+               case sofi:
+               case suspi:
+                       *Rudint |= 1 << irq;
+                       if (*Rudien & (1 << irq))
+                               avr_raise_interrupt(p->io.avr, &p->state->gen_vect);
+                       break;
+               default:
+                       assert(0);
+       }
+
+}
+
+static void
+reset_endpoints(
+               struct avr_t * avr,
+               avr_usb_t * p)
+{
+       memset(&p->state->ep_state[1], 0,
+               sizeof p->state->ep_state - sizeof p->state->ep_state[0]);
+}
+
+static int
+ep_fifo_empty(
+               struct _epstate * epstate)
+{
+       return epstate->bank[epstate->current_bank].tail == 0;
+}
+
+static int
+ep_fifo_full(
+               struct _epstate * epstate)
+{
+       return epstate->bank[epstate->current_bank].tail >=
+                                       (8 << epstate->uecfg1x.epsize);
+}
+
+static uint8_t
+ep_fifo_size(
+               struct _epstate * epstate)
+{
+       assert(epstate->ueconx.epen);
+       return (8 << epstate->uecfg1x.epsize);
+}
+
+static uint8_t
+ep_fifo_count(
+               struct _epstate * epstate)
+{
+       return epstate->bank[epstate->current_bank].tail;
+}
+
+static int
+ep_fifo_cpu_readbyte(
+               struct _epstate * epstate)
+{
+       uint8_t i, j;
+       uint8_t v = epstate->bank[epstate->current_bank].bytes[0];
+
+       if (!epstate->ueconx.epen) {
+               printf("WARNING! Adding bytes to non configured endpoint\n");
+               return -1;
+       }
+
+       if (ep_fifo_empty(epstate))
+               return -2;
+
+       for (i = 0, j = ep_fifo_count(epstate) - 1; i < j; i++)
+               epstate->bank[epstate->current_bank].bytes[i] =
+                       epstate->bank[epstate->current_bank].bytes[i + 1];
+       epstate->bank[epstate->current_bank].tail--;
+       return v;
+}
+
+static int
+ep_fifo_cpu_writebyte(
+               struct _epstate * epstate,
+               uint8_t v)
+{
+       if (!epstate->ueconx.epen) {
+               printf("WARNING! Adding bytes to non configured endpoint\n");
+               return -1;
+       }
+       if (ep_fifo_full(epstate))
+               return -2;
+
+       epstate->bank[epstate->current_bank].bytes[epstate->bank[epstate->current_bank].tail++] = v;
+       return 0;
+}
+
+static int
+ep_fifo_usb_read(
+               struct _epstate * epstate,
+               uint8_t * buf)
+{
+       if (!epstate->ueconx.epen) {
+               printf("WARNING! Reading from non configured endpoint\n");
+               return -1;
+       }
+       if (epstate->ueintx.txini) {
+               return AVR_IOCTL_USB_NAK;
+       }
+       if (epstate->ueintx.fifocon && epstate->uecfg0x.eptype != 0) {
+               return AVR_IOCTL_USB_NAK;
+       }
+
+       int ret = epstate->bank[epstate->current_bank].tail;
+       memcpy(buf, epstate->bank[epstate->current_bank].bytes,
+               epstate->bank[epstate->current_bank].tail);
+       epstate->bank[epstate->current_bank].tail = 0;
+       return ret;
+}
+
+static int
+ep_fifo_usb_write(
+        struct _epstate * epstate,
+        uint8_t * buf,
+        uint8_t len)
+{
+       if (!epstate->ueconx.epen) {
+               printf("WARNING! Adding bytes to non configured endpoint\n");
+               return -1;
+       }
+
+       if (epstate->ueintx.rxouti) {
+               return AVR_IOCTL_USB_NAK;
+       }
+       if (epstate->ueintx.fifocon && epstate->uecfg0x.eptype != 0) {
+               return AVR_IOCTL_USB_NAK;
+       }
+
+       if (len > ep_fifo_size(epstate)) {
+               printf("EP OVERFI\n");
+               len = sizeof epstate->bank[epstate->current_bank].bytes;
+       }memcpy(epstate->bank[epstate->current_bank].bytes, buf, len);
+       epstate->bank[epstate->current_bank].tail = len;
+
+       return 0;
+}
+
+static uint8_t
+avr_usb_ep_read_bytecount(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        void * param)
+{
+       avr_usb_t * p = (avr_usb_t *) param;
+       return ep_fifo_count(get_epstate(p, current_ep_to_cpu(p)));
+}
+
+static void
+avr_usb_udaddr_write(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        uint8_t v,
+        void * param)
+{
+       if (v & 0x80)
+               printf("Activate address %d\n", v & 0x7f);
+       avr_core_watch_write(avr, addr, v);
+}
+
+static void
+avr_usb_udcon_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
+{
+       avr_usb_t * p = (avr_usb_t *)param;
+
+       if(avr->data[addr]&1 && !(v&1))
+               avr_raise_irq(p->io.irq + USB_IRQ_ATTACH, !(v&1));
+       avr_core_watch_write(avr, addr, v);
+}
+
+static void
+avr_usb_uenum_write(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        uint8_t v,
+        void * param)
+{
+       assert(v < num_endpoints);
+       avr_core_watch_write(avr, addr, v);
+}
+
+static uint8_t
+avr_usb_ep_read_ueintx(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        void * param)
+{
+       avr_usb_t * p = (avr_usb_t *) param;
+       uint8_t ep = current_ep_to_cpu(p);
+
+       if (p->state->ep_state[ep].uecfg0x.epdir)
+               p->state->ep_state[ep].ueintx.rwal = !ep_fifo_full(get_epstate(p, ep));
+       else
+               p->state->ep_state[ep].ueintx.rwal = !ep_fifo_empty(get_epstate(p, ep));
+
+       return p->state->ep_state[ep].ueintx.v;
+}
+
+static void
+avr_usb_ep_write_ueintx(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        uint8_t v,
+        void * param)
+{
+       avr_usb_t * p = (avr_usb_t *) param;
+       uint8_t ep = current_ep_to_cpu(p);
+
+       union _ueintx * newstate = (union _ueintx*) &v;
+       union _ueintx * curstate = &p->state->ep_state[ep].ueintx;
+
+       if (curstate->rxouti & !newstate->rxouti)
+               curstate->rxouti = 0;
+       if (curstate->txini & !newstate->txini)
+               curstate->txini = 0;
+       if (curstate->rxstpi & !newstate->rxstpi) {
+               curstate->txini = 1;
+               curstate->rxouti = 0;
+               curstate->rxstpi = 0;
+       }
+       if (curstate->fifocon & !newstate->fifocon)
+               curstate->fifocon = 0;
+       if (curstate->nakini & !newstate->nakini)
+               curstate->nakini = 0;
+       if (curstate->nakouti & !newstate->nakouti)
+               curstate->nakouti = 0;
+       if (curstate->stalledi & !newstate->stalledi)
+               curstate->stalledi = 0;
+       if (curstate->rwal & !newstate->rwal)
+               printf("pointless change of ueintx.rwal\n");
+
+       if ((curstate->v & 0xdf) == 0)
+               avr->data[p->r_usbcon + ueint] &= 0xff ^ (1 << ep); // mark ep0 interrupt
+}
+
+static uint8_t
+avr_usb_ep_read(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        void * param)
+{
+       avr_usb_t * p = (avr_usb_t *) param;
+       uint8_t laddr = addr - p->r_usbcon;
+       uint8_t v;
+       struct _epstate * epstate = get_epstate(p, current_ep_to_cpu(p));
+
+       switch(laddr) {
+               case ueconx:  v = epstate->ueconx.v; break;
+               case uecfg0x: v = epstate->uecfg0x.v; break;
+               case uecfg1x: v = epstate->uecfg1x.v; break;
+               case uesta0x: v = epstate->uesta0x.v; break;
+               case uesta1x: v = epstate->uesta1x.v; break;
+               case ueienx:  v = epstate->ueienx.v; break;
+               default:assert(0);
+       }
+       return v;
+}
+
+static void
+avr_usb_ep_write(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        uint8_t v,
+        void * param)
+{
+       avr_usb_t * p = (avr_usb_t *) param;
+       struct _epstate * epstate = get_epstate(p, current_ep_to_cpu(p));
+       uint8_t laddr = addr - p->r_usbcon;
+
+       switch (laddr) {
+               case ueconx:
+                       if (v & 1 << 4)
+                               epstate->ueconx.stallrq = 0;
+                       if (v & 1 << 5)
+                               epstate->ueconx.stallrq = 1;
+                       epstate->ueconx.epen = (v & 1) != 0;
+                       break;
+               case uecfg0x:
+                       epstate->uecfg0x.v = v;
+                       epstate->uesta0x.cfgok = 0;
+                       break;
+               case uecfg1x:
+                       epstate->uecfg1x.v = v;
+                       epstate->uesta0x.cfgok = epstate->uecfg1x.alloc;
+                       if (epstate->uecfg0x.eptype == 0)
+                               epstate->ueintx.txini = 1;
+                       else if (epstate->uecfg0x.epdir) {
+                               epstate->ueintx.txini = 1;
+                               epstate->ueintx.rwal = 1;
+                               epstate->ueintx.fifocon = 1;
+                       } else
+                               epstate->ueintx.rxouti = 0;
+                       avr_core_watch_write(avr, p->r_usbcon + uesta0x,
+                               epstate->uesta0x.v);
+                       break;
+               case uesta0x:
+                       v = (epstate->uesta0x.v & 0x9f) + (v & (0x60 & epstate->uesta0x.v));
+                       epstate->uesta0x.v = v;
+                       break;
+               case ueienx:
+                       epstate->ueienx.v = v;
+                       break;
+               default:
+                       assert(0);
+       }
+}
+
+static uint8_t
+avr_usb_ep_read_data(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        void * param)
+{
+       avr_usb_t * p = (avr_usb_t *) param;
+       int ret = ep_fifo_cpu_readbyte(get_epstate(p, current_ep_to_cpu(p)));
+
+       if (ret < 0) {
+               if (ret == -2)
+                       raise_ep_interrupt(avr, p, current_ep_to_cpu(p), underfi);
+               return 0;
+       } else
+               return (uint8_t) ret;
+}
+
+static void
+avr_usb_ep_write_data(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        uint8_t v,
+        void * param)
+{
+       avr_usb_t * p = (avr_usb_t *) param;
+       int ret = ep_fifo_cpu_writebyte(get_epstate(p, current_ep_to_cpu(p)), v);
+       if (ret == 0)
+               return;
+
+       if (ret == -2)
+               raise_ep_interrupt(avr, p, current_ep_to_cpu(p), overfi);
+}
+
+static void
+avr_usb_pll_write(
+        struct avr_t * avr,
+        avr_io_addr_t addr,
+        uint8_t v,
+        void * param)
+{
+       v |= (v >> 1) & 1;
+       avr_core_watch_write(avr, addr, v);
+}
+
+
+avr_cycle_count_t
+sof_generator(
+        struct avr_t * avr,
+        avr_cycle_count_t when,
+        void * param)
+{
+       avr_usb_t * p = (avr_usb_t *) param;
+       //stop sof generation if detached
+       if (avr->data[p->r_usbcon + udcon] & 1)
+               return 0;
+       else {
+               raise_usb_interrupt(p, sofi);
+               return when;
+       }
+}
+
+static int
+avr_usb_ioctl(
+               struct avr_io_t * io,
+               uint32_t ctl,
+               void * io_param)
+{
+       avr_usb_t * p = (avr_usb_t *) io;
+       struct avr_io_usb * d = (struct avr_io_usb*) io_param;
+       struct _epstate * epstate = 0;
+       int ret;
+       uint8_t ep;
+
+       switch (ctl) {
+               case AVR_IOCTL_USB_READ:
+                       ep = d->pipe & 0x7f;
+                       epstate = get_epstate(p, ep);
+
+                       if (epstate->ueconx.stallrq) {
+                               raise_ep_interrupt(io->avr, p, 0, stalledi);
+                               return AVR_IOCTL_USB_STALL;
+                       }
+                       if (ep && !epstate->uecfg0x.epdir)
+                               printf("Reading from IN endpoint from host??\n");
+
+                       ret = ep_fifo_usb_read(epstate, d->buf);
+                       if (ret < 0) {
+                               // is this correct? It makes the cdc example work.
+                               // Linux stops polling the data ep if we send naks,but
+                               // according to usb spec nak'ing should be ok.
+                               if (epstate->uecfg0x.eptype == 2) {
+                                       d->sz = 0;
+                                       return 0;
+                               } else
+                                       return ret;
+                       }
+                       d->sz = ret;
+                       ret = 0;
+                       epstate->ueintx.fifocon = 1;
+                       raise_ep_interrupt(io->avr, p, ep, txini);
+                       return ret;
+               case AVR_IOCTL_USB_WRITE:
+                       ep = d->pipe & 0x7f;
+                       epstate = get_epstate(p, ep);
+
+                       if (ep && epstate->uecfg0x.epdir)
+                               printf("Writing to IN endpoint from host??\n");
+
+                       if (epstate->ueconx.stallrq) {
+                               raise_ep_interrupt(io->avr, p, 0, stalledi);
+                               return AVR_IOCTL_USB_STALL;
+                       }
+
+                       ret = ep_fifo_usb_write(epstate, d->buf, d->sz);
+                       if (ret < 0)
+                               return ret;
+
+                       epstate->ueintx.fifocon = 1;
+                       raise_ep_interrupt(io->avr, p, ep, rxouti);
+                       return 0;
+               case AVR_IOCTL_USB_SETUP:
+                       ep = d->pipe & 0x7f;
+                       epstate = get_epstate(p, ep);
+
+                       epstate->ueconx.stallrq = 0;
+                       // teensy actually depends on this (fails to ack rxouti on usb
+                       // control read status stage) even if the datasheet clarely states
+                       // that one should do so.
+                       epstate->ueintx.rxouti = 0;
+
+                       ret = ep_fifo_usb_write(epstate, d->buf, d->sz);
+                       if (ret < 0)
+                               return ret;
+                       raise_ep_interrupt(io->avr, p, ep, rxstpi);
+
+                       return 0;
+               case AVR_IOCTL_USB_RESET:
+                       printf("__USB_RESET__\n");
+                       reset_endpoints(io->avr, p);
+                       raise_usb_interrupt(p, eorsti);
+                       if (0)
+                               avr_cycle_timer_register_usec(io->avr, 1000, sof_generator, p);
+                       return 0;
+               default:
+                       return -1;
+       }
+}
+
+void
+avr_usb_reset(
+               struct avr_io_t *io)
+{
+       avr_usb_t * p = (avr_usb_t *) io;
+       uint8_t i;
+
+       memset(p->state->ep_state, 0, sizeof p->state->ep_state);
+
+       for (i = 0; i < otgtcon; i++)
+               p->io.avr->data[p->r_usbcon + i] = 0;
+
+       p->io.avr->data[p->r_usbcon] = 0x20;
+       p->io.avr->data[p->r_usbcon + udcon] = 1;
+
+       printf("%s\n", __FUNCTION__);
+}
+
+static const char * irq_names[USB_IRQ_COUNT] = {
+       [USB_IRQ_ATTACH] = ">attach",
+};
+
+static void
+avr_usb_dealloc(
+               struct avr_io_t * port)
+{
+       avr_usb_t * p = (avr_usb_t *) port;
+       free(p->state);
+}
+
+static avr_io_t        _io = {
+       .kind = "usb",
+       .reset = avr_usb_reset,
+       .irq_names = irq_names,
+       .ioctl = avr_usb_ioctl,
+       .dealloc = avr_usb_dealloc,
+};
+
+static void
+register_io_ep_readwrite(
+               avr_t * avr,
+               avr_usb_t * p,
+               uint8_t laddr)
+{
+       avr_register_io_write(avr, p->r_usbcon + laddr, avr_usb_ep_write, p);
+       avr_register_io_read(avr, p->r_usbcon + laddr, avr_usb_ep_read, p);
+}
+
+static void
+register_vectors(
+               avr_t * avr,
+               avr_usb_t * p)
+{
+       // usb interrupts are multiplexed into just two vectors.
+       // we therefore need fake bits for enable & raise
+
+       // use usbe as fake enable bit
+       p->state->com_vect.enable = (avr_regbit_t)AVR_IO_REGBIT(p->r_usbcon, 7);
+       p->state->gen_vect.enable = (avr_regbit_t)AVR_IO_REGBIT(p->r_usbcon, 7);
+
+//     // use reserved/unused bits in usbsta as fake raised bits
+//     p->state->com_vect.raised = (avr_regbit_t)AVR_IO_REGBIT(p->r_usbcon+1,7);
+//     p->state->gen_vect.raised = (avr_regbit_t)AVR_IO_REGBIT(p->r_usbcon+1,6);
+
+       p->state->com_vect.vector = p->usb_com_vect;
+       p->state->gen_vect.vector = p->usb_gen_vect;
+
+       avr_register_vector(avr, &p->state->com_vect);
+       avr_register_vector(avr, &p->state->gen_vect);
+}
+
+void avr_usb_init(avr_t * avr, avr_usb_t * p)
+{
+       p->io = _io;
+
+       p->state = calloc(1, sizeof *p->state);
+
+       avr_register_io(avr, &p->io);
+       register_vectors(avr, p);
+       // allocate this module's IRQ
+       avr_io_setirqs(&p->io, AVR_IOCTL_USB_GETIRQ(), USB_IRQ_COUNT, NULL);
+
+       avr_register_io_write(avr, p->r_usbcon + udaddr, avr_usb_udaddr_write, p);
+       avr_register_io_write(avr, p->r_usbcon + udcon, avr_usb_udcon_write, p);
+       avr_register_io_write(avr, p->r_usbcon + uenum, avr_usb_uenum_write, p);
+
+       avr_register_io_read(avr, p->r_usbcon + uedatx, avr_usb_ep_read_data, p);
+       avr_register_io_write(avr, p->r_usbcon + uedatx, avr_usb_ep_write_data, p);
+       avr_register_io_read(avr, p->r_usbcon + uebclx, avr_usb_ep_read_bytecount, p); //ro
+
+       avr_register_io_read(avr, p->r_usbcon + ueintx, avr_usb_ep_read_ueintx, p);
+       avr_register_io_write(avr, p->r_usbcon + ueintx, avr_usb_ep_write_ueintx, p);
+
+       register_io_ep_readwrite(avr, p, ueconx);
+       register_io_ep_readwrite(avr, p, uecfg0x);
+       register_io_ep_readwrite(avr, p, uecfg1x);
+       register_io_ep_readwrite(avr, p, uesta0x);
+       register_io_ep_readwrite(avr, p, uesta1x);
+       register_io_ep_readwrite(avr, p, ueienx);
+
+       avr_register_io_write(avr, p->r_pllcsr, avr_usb_pll_write, p);
+}
+
diff --git a/simavr/sim/avr_usb.h b/simavr/sim/avr_usb.h
new file mode 100644 (file)
index 0000000..609ffb6
--- /dev/null
@@ -0,0 +1,66 @@
+/* vim: set sts=4:sw=4:ts=4:noexpandtab
+       avr_usb.h
+
+       Copyright 2012 Torbjorn Tyridal <ttyridal@gmail.com>
+
+       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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef AVR_USB_H_
+#define AVR_USB_H_
+
+#include "sim_avr.h"
+
+enum {
+       USB_IRQ_ATTACH = 0,
+       USB_IRQ_COUNT
+};
+
+// add port number to get the real IRQ
+#define AVR_IOCTL_USB_WRITE AVR_IOCTL_DEF('u','s','b','w')
+#define AVR_IOCTL_USB_READ AVR_IOCTL_DEF('u','s','b','r')
+#define AVR_IOCTL_USB_SETUP AVR_IOCTL_DEF('u','s','b','s')
+#define AVR_IOCTL_USB_RESET AVR_IOCTL_DEF('u','s','b','R')
+#define AVR_IOCTL_USB_VBUS AVR_IOCTL_DEF('u','s','b','V')
+#define AVR_IOCTL_USB_GETIRQ() AVR_IOCTL_DEF('u','s','b',' ')
+
+struct avr_io_usb {
+       uint8_t pipe;   //[in]
+       size_t  sz;             //[in/out]
+       uint8_t * buf;  //[in/out]
+};
+#define AVR_IOCTL_USB_NAK -2
+#define AVR_IOCTL_USB_STALL -3
+#define AVR_IOCTL_USB_OK 0
+
+typedef struct avr_usb_t {
+       avr_io_t        io;
+       char name;
+       avr_regbit_t    disabled;       // bit in the PRR
+       avr_regbit_t    usbrf;          // bit in the MCUSR
+       avr_io_addr_t   r_usbcon;       // every usb reg is an offset of this.
+       avr_io_addr_t   r_pllcsr;
+
+
+       uint8_t usb_com_vect;
+       uint8_t usb_gen_vect;
+
+       struct usb_internal_state * state;
+} avr_usb_t;
+
+void avr_usb_init(avr_t * avr, avr_usb_t * port);
+
+#endif /* AVR_USB_H_ */