From 679955cf31d0c881080efe9d0b4b949732706ff9 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Mon, 27 Feb 2012 14:17:25 +0000 Subject: [PATCH] simavr: Added usb slave emulation module Straight from Torbjorn Tyridal with just a bit of source code remangling Signed-off-by: Michel Pollet --- simavr/sim/avr_usb.c | 800 +++++++++++++++++++++++++++++++++++++++++++ simavr/sim/avr_usb.h | 66 ++++ 2 files changed, 866 insertions(+) create mode 100644 simavr/sim/avr_usb.c create mode 100644 simavr/sim/avr_usb.h diff --git a/simavr/sim/avr_usb.c b/simavr/sim/avr_usb.c new file mode 100644 index 0000000..4f75374 --- /dev/null +++ b/simavr/sim/avr_usb.c @@ -0,0 +1,800 @@ +/* vim: set sts=4:sw=4:ts=4:noexpandtab + avr_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 . + */ + +/* 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 +#include +#include +#include +#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 index 0000000..609ffb6 --- /dev/null +++ b/simavr/sim/avr_usb.h @@ -0,0 +1,66 @@ +/* vim: set sts=4:sw=4:ts=4:noexpandtab + avr_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 . + */ + +#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_ */ -- 2.39.5