From eae3888f7401c8085488eed1c4c8c843a4a41c8f Mon Sep 17 00:00:00 2001 From: patacongo Date: Mon, 20 Jun 2011 16:26:12 +0000 Subject: Add an AVR USB driver git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3726 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/arch/avr/src/at90usb/at90usb_usbdev.c | 2933 +++++++++++++++++++++++++++ 1 file changed, 2933 insertions(+) create mode 100644 nuttx/arch/avr/src/at90usb/at90usb_usbdev.c (limited to 'nuttx') diff --git a/nuttx/arch/avr/src/at90usb/at90usb_usbdev.c b/nuttx/arch/avr/src/at90usb/at90usb_usbdev.c new file mode 100644 index 000000000..1c10faba8 --- /dev/null +++ b/nuttx/arch/avr/src/at90usb/at90usb_usbdev.c @@ -0,0 +1,2933 @@ +/******************************************************************************* + * arch/arm/src/at90usb/at90usb_usbdev.c + * + * Copyright (C) 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + *******************************************************************************/ + +/******************************************************************************* + * Included Files + *******************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include "chip.h" +#include "up_arch.h" +#include "up_internal.h" + +/******************************************************************************* + * Definitions + *******************************************************************************/ + +/* Configuration ***************************************************************/ +/* PLL Settings are based on F_CPU frequency which is defined in the board.h file */ + +#if (BOARD_CPU_CLOCK == 8000000) +# define USB_PLL_PSC ((1 << PLLP1) | (1 << PLLP0)) +#elif (BOARD_CPU_CLOCK == 16000000) +# if defined(__AVR_AT90USB647__) +# define USB_PLL_PSC ((1 << PLLP2) | (1 << PLLP1)) +# else +# define USB_PLL_PSC ((1 << PLLP2) | (1 << PLLP0)) +# endif +#else +# error "Unsuppored CPU clock" +#endif + +/* Debug ***********************************************************************/ + +/* Trace error codes */ + +#define AVR_TRACEERR_ALLOCFAIL 0x0001 +#define AVR_TRACEERR_BADCLREPFEATURE 0x0002 +#define AVR_TRACEERR_BADCLRDEVFEATURE 0x0003 +#define AVR_TRACEERR_BADDEVGETSTATUS 0x0004 +#define AVR_TRACEERR_BADEPNO 0x0005 +#define AVR_TRACEERR_BADEPGETSTATUS 0x0006 +#define AVR_TRACEERR_BADGETCONFIG 0x0007 +#define AVR_TRACEERR_BADGETSETDESC 0x0008 +#define AVR_TRACEERR_BADGETSTATUS 0x0009 +#define AVR_TRACEERR_BADSETADDRESS 0x000a +#define AVR_TRACEERR_BADSETCONFIG 0x000b +#define AVR_TRACEERR_BADSETEPFEATURE 0x000c +#define AVR_TRACEERR_BADSETDEVFEATURE 0x000d +#define AVR_TRACEERR_BINDFAILED 0x000e +#define AVR_TRACEERR_DRIVER 0x000f +#define AVR_TRACEERR_DISPATCHSTALL 0x0010 +#define AVR_TRACEERR_DRIVERREGISTERED 0x0011 +#define AVR_TRACEERR_EPNULLPACKET 0x0012 +#define AVR_TRACEERR_XFERTYPE 0x0013 +#define AVR_TRACEERR_PKTSIZE 0x0014 +#define AVR_TRACEERR_EPCFGBAD 0x0015 +#define AVR_TRACEERR_EP0CFGBAD 0x0016 +#define AVR_TRACEERR_EP0SETUPSTALLED 0x0017 +#define AVR_TRACEERR_EP0RXOUTI 0x0018 +#define AVR_TRACEERR_EP0FIFOFULL 0x0019 +#define AVR_TRACEERR_EP0FIFONOTREADY 0x001a +#define AVR_TRACEERR_INFIFO 0x001b +#define AVR_TRACEERR_INVALIDCTRLREQ 0x001c +#define AVR_TRACEERR_INVALIDPARMS 0x001d +#define AVR_TRACEERR_IRQREGISTRATION 0x001e +#define AVR_TRACEERR_NOEP 0x001f +#define AVR_TRACEERR_NOTCONFIGURED 0x0020 + +/* Trace interrupt codes */ + +#define AVR_TRACEINTID_GENINT 0x0001 +#define AVR_TRACEINTID_EPINT 0x0002 +#define AVR_TRACEINTID_VBUS 0x0003 +#define AVR_TRACEINTID_SUSPEND 0x0004 +#define AVR_TRACEINTID_WAKEUP 0x0005 +#define AVR_TRACEINTID_EOR 0x0006 +#define AVR_TRACEINTID_CLEARFEATURE 0x0007 +#define AVR_TRACEINTID_DEVGETSTATUS 0x0008 +#define AVR_TRACEINTID_DISPATCH 0x0009 +#define AVR_TRACEINTID_EP0SETUP 0x000a +#define AVR_TRACEINTID_EPGETSTATUS 0x000b +#define AVR_TRACEINTID_EPIN 0x000c +#define AVR_TRACEINTID_EPOUT 0x000d +#define AVR_TRACEINTID_EP0SETUPSETADDRESS 0x000e +#define AVR_TRACEINTID_GETCONFIG 0x000f +#define AVR_TRACEINTID_GETSETDESC 0x0010 +#define AVR_TRACEINTID_GETSETIF 0x0011 +#define AVR_TRACEINTID_GETSTATUS 0x0012 +#define AVR_TRACEINTID_IFGETSTATUS 0x0013 +#define AVR_TRACEINTID_SETCONFIG 0x0014 +#define AVR_TRACEINTID_SETFEATURE 0x0015 +#define AVR_TRACEINTID_SYNCHFRAME 0x0016 + +/* Hardware interface **********************************************************/ + +/* Endpoints ******************************************************************/ + +/* Number of endpoints */ + +#define AVR_NENDPOINTS (7) /* ep0-6 */ + +/* Endpoint 0 is special... */ + +#define AVR_EP0 (0) +#define AVR_CTRLEP_SIZE (8) + +/* Bit encoded ep0-6 */ + +#define AVR_ALL_EPS (0x7f) + +/* Endpoint configuration definitions */ + +#define AVR_EPTYPE_CTRL (0 << EPTYPE0) +#define AVR_EPTYPE_ISOC (1 << EPTYPE0) +#define AVR_EPTYPE_BULK (2 << EPTYPE0) +#define AVR_EPTYPE_INTR (3 << EPTYPE0) + +#define AVR_DIR_OUT (0 << EPDIR) +#define AVR_DIR_IN (1 << EPDIR) + +#define AVR_SINGLE_BANK (0 << EPBK0) +#define AVR_DOUBLE_BANK (1 << EPBK0) + +#define AVR_EPSIZE_8 (0 << EPSIZE0) +#define AVR_EPSIZE_16 (1 << EPSIZE0) +#define AVR_EPSIZE_32 (2 << EPSIZE0) +#define AVR_EPSIZE_64 (3 << EPSIZE0) +#define AVR_EPSIZE_128 (4 << EPSIZE0) +#define AVR_EPSIZE_256 (5 << EPSIZE0) + +/* General endpoint defintions */ + +#define AVR_EP0 (0) +#define AVR_NENDPOINTS (7) +#define AVR_EPNO_MASK (3) + +#define AVR_TIMEOUT_LONG (100) +#define AVR_TIMEOUT_SHORT (32) +#define AVR_TIMEOUT_NONE (0) + +/* Request queue operations ****************************************************/ + +#define avr_rqempty(ep) ((ep)->head == NULL) +#define avr_rqpeek(ep) ((ep)->head) + +/******************************************************************************* + * Private Types + *******************************************************************************/ + +/* A container for a request so that the request may be retained in a list */ + +struct avr_req_s +{ + struct usbdev_req_s req; /* Standard USB request */ + struct avr_req_s *flink; /* Supports a singly linked list */ +}; + +/* This is the internal representation of an endpoint */ + +struct avr_ep_s +{ + /* Common endpoint fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbdev_ep_s + * to struct avr_ep_s. */ + + struct usbdev_ep_s ep; /* Standard endpoint structure */ + + /* AVR-specific fields */ + + struct avr_req_s *head; /* Request list for this endpoint */ + struct avr_req_s *tail; + struct avr_req_s *pending; /* Pending IN request */ + uint8_t stalled:1; /* 1: Endpoint is stalled */ + uint8_t epin:1; /* 1: IN endpoint */ +}; + +/* This structure retains the state of the USB device controller */ + +struct avr_usbdev_s +{ + /* Common device fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbdev_s to + * struct avr_usbdev_s. */ + + struct usbdev_s usbdev; + + /* The bound device class driver */ + + struct usbdevclass_driver_s *driver; + + /* AVR-specific fields */ + + uint8_t ep0buf[64]; /* buffer for EP0 short transfers */ + uint8_t paddr; /* Address assigned by SETADDRESS */ + uint8_t epavail; /* Bitset of available (unconfigured) endpoints */ + uint8_t epinset; /* The set of all configured IN endpoints */ + uint8_t epoutset; /* The set of all configured OUT endpoints */ + uint8_t stalled:1; /* 1: Protocol stalled */ + uint8_t selfpowered:1; /* 1: Device is self powered */ + uint8_t paddrset:1; /* 1: Peripheral addr has been set */ + uint8_t attached:1; /* 1: Host attached */ +#ifdef CONFIG_USBDEV_SELFPOWERED + uint8_t wkupen:1; /* 1: Wake-up enabled */ +#endif + volatile bool connected; /* Device is connected */ + + /* The endpoint list */ + + struct avr_ep_s eplist[AVR_NENDPOINTS]; +}; + +/******************************************************************************* + * Private Function Prototypes + *******************************************************************************/ + +/* Request queue operations ****************************************************/ + +static FAR struct avr_req_s *avr_rqdequeue(FAR struct avr_ep_s *privep); +static inline void avr_rqenqueue(FAR struct avr_ep_s *privep, + FAR struct avr_req_s *req); + +/* Low level data transfers and request operations *****************************/ + +static void avr_txready(void); +static int avr_fifoready(int timeout); +static void avr_ep0send(FAR const uint8_t *buffer, uint16_t buflen); +static inline int avr_epNsend(FAR struct avr_ep_s *privep, + FAR struct avr_req_s *privreq); +static inline int avr_epNrecv(FAR struct avr_ep_s *privep, + FAR struct usbdev_req_s *req); +static int avr_epINqueue(FAR struct avr_ep_s *privep); +static int avr_epOUTqueue(FAR struct avr_ep_s *privep); +static void avr_reqcomplete(FAR struct avr_ep_s *privep, FAR struct avr_req_s *privreq, + int result); +static void avr_cancelrequests(FAR struct avr_ep_s *privep, int status); +static void avr_cancelall(int status); + +/* Endpoint interrupt handling *************************************************/ + +static struct avr_ep_s *avr_epfindbyaddr(uint8_t epno); +static void avr_dispatchrequest(FAR const struct usb_ctrlreq_s *ctrl); +static int avr_ep0configure(void); +static void avr_setaddress(uint8_t address); +static void avr_ep0setup(void); +static int avr_epinterrupt(int irq, FAR void *context); + +/* General interrupt handling **************************************************/ + +static void avr_epreset(FAR struct avr_ep_s *privep, int status); +static void avr_usbreset(void); +static void avr_genvbus(void); +static inline void avr_gensuspend(void); +static void avr_genwakeup(void); +static inline void avr_geneor(void); +static int avr_geninterrupt(int irq, FAR void *context); + +/* USB device controller operations ********************************************/ + +static int avr_epconfigure(FAR struct usbdev_ep_s *ep, + const struct usb_epdesc_s *desc, bool last); +static int avr_epdisable(FAR struct usbdev_ep_s *ep); +static FAR struct usbdev_req_s *avr_epallocreq(FAR struct usbdev_ep_s *ep); +static void avr_epfreereq(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *); +#ifdef CONFIG_ARCH_USBDEV_DMA +static void *avr_epallocbuffer(FAR struct usbdev_ep_s *ep, unsigned bytes); +static void avr_epfreebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf); +#endif +static int avr_epsubmit(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req); +static int avr_epcancel(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *req); +static int avr_epstall(FAR struct usbdev_ep_s *ep, bool resume); + +static FAR struct usbdev_ep_s *avr_allocep(FAR struct usbdev_s *dev, + uint8_t epno, bool in, + uint8_t eptype); +static void avr_freeep(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep); +static int avr_getframe(struct usbdev_s *dev); +static int avr_wakeup(struct usbdev_s *dev); +static int avr_selfpowered(struct usbdev_s *dev, bool selfpowered); +static int avr_pullup(struct usbdev_s *dev, bool enable); + +/******************************************************************************* + * Private Data + *******************************************************************************/ + +/* Since there is only a single USB interface, all status information can be + * be simply retained in a single global instance. + */ + +static struct avr_usbdev_s g_usbdev; + +static const struct usbdev_epops_s g_epops = +{ + .configure = avr_epconfigure, + .disable = avr_epdisable, + .allocreq = avr_epallocreq, + .freereq = avr_epfreereq, +#ifdef CONFIG_ARCH_USBDEV_DMA + .allocbuffer = avr_epallocbuffer, + .freebuffer = avr_epfreebuffer, +#endif + .submit = avr_epsubmit, + .cancel = avr_epcancel, + .stall = avr_epstall, +}; + +static const struct usbdev_ops_s g_devops = +{ + .allocep = avr_allocep, + .freeep = avr_freeep, + .getframe = avr_getframe, + .wakeup = avr_wakeup, + .selfpowered = avr_selfpowered, + .pullup = avr_pullup, +}; + +/******************************************************************************* + * Public Data + *******************************************************************************/ + +/******************************************************************************* + * Private Functions + *******************************************************************************/ + +/******************************************************************************* + * Name: avr_rqdequeue + * + * Description: + * Remove a request from an endpoint request queue + * + *******************************************************************************/ + +static FAR struct avr_req_s *avr_rqdequeue(FAR struct avr_ep_s *privep) +{ + FAR struct avr_req_s *ret = privep->head; + + if (ret) + { + privep->head = ret->flink; + if (!privep->head) + { + privep->tail = NULL; + } + + ret->flink = NULL; + } + + return ret; +} + +/******************************************************************************* + * Name: avr_rqenqueue + * + * Description: + * Add a request from an endpoint request queue + * + *******************************************************************************/ + +static inline void avr_rqenqueue(FAR struct avr_ep_s *privep, + FAR struct avr_req_s *req) +{ + req->flink = NULL; + if (!privep->head) + { + privep->head = req; + privep->tail = req; + } + else + { + privep->tail->flink = req; + privep->tail = req; + } +} + +/******************************************************************************* + * Name: avr_txready + * + * Description: + * Wait for the selected endpoint to be ready for an IN (TX) transfer + * + *******************************************************************************/ + +static void avr_txready(void) +{ + int retries = 10000; + while (((UEINTX & (1 << TXINI)) == 0) && retries-- > 0); +} + +/******************************************************************************* + * Name: avr_fifoready + * + * Description: + * Wait for the selected endpoint FIFO to be ready + * + *******************************************************************************/ + +static int avr_fifoready(int timeout) +{ + UDINT &= ~(1 << SOFI); + + for (;;) + { + /* Check if the FIFO is ready by testing RWAL (read/write allowed). The + * meaning of this bigtdepends on the direction of the endpoint: For an + * OUT endpoint, the RWAL bit is set if the firmware can read data from + * the bank, and cleared by hardware when the bank is empty; For an IN + * endpoint, the RWAL bit is set if the firmware can write data to the + * bank, and cleared by hardware when the bank is full. + */ + + if ((UEINTX & (1 << RWAL)) != 0) + { + return OK; + } + + /* Check if we are still connected and not stalled */ + + if (!(g_usbdev.connected)) + { + return -ENODEV; + } + else if ((UECONX & (1 << STALLRQ)) != 0) + { + return -EAGAIN; + } + + /* Timeing is driven by the start of frame (SOF) interrupt which we + * assume here to be at a one millisecond rate. */ + + if ((UDINT & (1 << SOFI)) != 0) + { + /* Clear the SOF interrupt decrement the count of elapsed + * milliseconds */ + + UDINT &= ~(1 << SOFI); + + if ((timeout--) > 0) + { + /* The timeout has elapsed... return a failure */ + + return -ETIME; + } + } + } +} + +/******************************************************************************* + * Name: avr_ep0send + * + * Description: + * Schedule a short TX transfer for Endpoint 0 + * + * Assumptions: + * - Endpoint 0 is already selected. + * + *******************************************************************************/ + +static void avr_ep0send(FAR const uint8_t *buffer, uint16_t buflen) +{ + FAR const uint8_t *ptr = buffer; + uint8_t regval; + + /* Loop while there are more bytes to send and RXOUTI is clear. RXOUTI is + * set when a new OUT data is received + */ + + while (buflen) + { + /* Verify that RXOUTI is clear. RXOUTI is set when a new OUT data is + * received. In this case, we have not option but to abort the transfer. + */ + + regval = UEINTX; + if ((regval & (1 << RXOUTI)) != 0) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EP0RXOUTI), regval); + return; + } + + /* Okay... wait for the selected endpoint to be ready for an TX transfer */ + + avr_txready(); + + /* Now send as many bytes as possible */ + + while (buflen > 0) + { + /* Break out of the loop if the FIFO is full */ + + if (UEBCX == AVR_CTRLEP_SIZE) + { + /* Clearing FIFOCON frees the current bank and switches to the + * following bank. TXINI must be cleared to acknowledge the + * interrupt. + */ + + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EP0FIFOFULL), regval); + + /* TXINI must always be cleared BEFORE clearing FIFOCON */ + + regval = UEINTX; + regval &= ~(1 << TXINI); + UEINTX = regval; + regval &= ~(1 << FIFOCON); + UEINTX = regval; + break; + } + + /* Not full, transfer another byte */ + + UEDATX = *ptr++; + buflen--; + } + + /* Clearing FIFOCON frees the current bank and switches to the following + * bank. TXINI must be cleared to acknowledge the interrupt. TXINI must + * always be cleared BEFORE clearing FIFOCON. + */ + + regval = UEINTX; + regval &= ~(1 << TXINI); + UEINTX = regval; + regval &= ~(1 << FIFOCON); + UEINTX = regval; + } +} + +/******************************************************************************* + * Name: avr_epNsend + * + * Description: + * Perform a TX transfer for Endpoint N + * + *******************************************************************************/ + +static inline int avr_epNsend(FAR struct avr_ep_s *privep, + FAR struct avr_req_s *privreq) +{ + FAR struct usbdev_req_s *req; + FAR const uint8_t *buffer; + uint16_t buflen; + uint16_t len; + uint16_t pktmask; + uint8_t ret; + uint8_t more; + uint8_t regval; + bool zlp; + + /* Check if endpoint is ready for read/write operations */ + + DEBUGASSERT((UEINTX & (1 << RWAL)) != 0); + + /* Setup pointers and counts for this transfer */ + + req = &privreq->req; + buffer = &req->buf[req->xfrd]; + buflen = req->len - req->xfrd; + zlp = ((privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0); + pktmask = privep->ep.maxpacket - 1; + + /* Select the endpoint */ + + UENUM = privep->ep.eplog; + + /* This function should not be called if we are not ready to write! */ + + ret = avr_fifoready(AVR_TIMEOUT_LONG); + if (ret != OK) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EP0FIFOFULL), regval); + return -EAGAIN; + } + + /* Send the USB data. The outer loop handles for each packet of data + * (including zero-length packets) + */ + + do + { + /* Then loop, putting each outgoing byte into the transmit FIFO until + * either (1) all of the data has been sent (len == buflen) or + * (2) the transmit FIFO is full + */ + + len = 0; + while (len < buflen && (UEINTX & (1 << RWAL)) != 0) + { + /* Add another byte to the transmit FIFO */ + + UEDATX = *buffer++; + len++; + } + + /* We now have one complete packet in the transmit FIFO(or maybe two + * packets if dual buffering is enabled). + * + * Clear any pending TXINI interrupts + */ + + UEINT &= ~(1 << privep->ep.eplog); + + /* Clear TXINI and send what is in the transmit FIFO (could be a zero + * length packet). TXINI must always be cleared BEFORE clearing FIFOCON. + */ + + regval = UEINTX; + regval &= ~(1 << TXINI); + UEINTX = regval; + regval &= ~(1 << FIFOCON); + UEINTX = regval; + + /* Adjust the remaining number of bytes to transfer. */ + + req->xfrd += len; + buffer += len; + buflen -= len; + + usbtrace(TRACE_WRITE(privep->ep.eplog), privreq->req.xfrd); + + /* Check if we need to send a zero length packet (ZLP); We need to send + * a ZLP if the last packet sent was exactly equal to the packet length + * AND if the endpoint is configuration to send ZLPs. However, in dual + * buffer mode, we may have actually just sent two packets so the actual + * check is for a non-zero, transfer of a multiple of the packet + */ + + if (buflen > 0) + { + /* There is more data to be sent */ + + more = true; + } + else if (zlp) + { + /* All of the data has been sent. A ZLP might be needed if the last + * transfer was an exact multiple of the packet size. + */ + + if (len && (len & pktmask) == 0) + { + /* The last packet was not a ZLP and was an example multiple of + * the packet size. A ZLP is needed. + */ + + more = true; + } + else + { + /* The last packet was a ZLP or was a partial packet. We are + * finished with this request. + */ + + more = false; + } + } + else + { + /* No more data to be sent and a ZLP is not needed */ + + more = false; + } + + /* RWAL will be de-asserted when there is no more space in the transmit + * FIFO. We care only if we have more data (or a zero-length-packet) to + * send. Try a short inline wait to see if the FIFO becomes write ready. + * This saves handling an interrupt most of the time (really depends on + * how fast the host takes the data from the transmit FIFO). + */ + + if (more && (ret = avr_fifoready(AVR_TIMEOUT_SHORT))) + { + /* If the endpoint simply did not become ready within the timeout, + * then handle the remainder of the transfer asynchronously in the + * TXINI interrupt handler. */ + + if (ret == -ETIME) + { + /* Enable the endpoint IN interrupt ISR. */ + + UEIENX |= (1 << TXINE); + } + + /* A fatal error occurred while waiting for the IN FIFO to become + * available. + */ + + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INFIFO), regval); + return ret; + } + } + while (more); + return OK; +} + +/******************************************************************************* + * Name: avr_epNrecv + * + * Description: + * Perform an RX transfer for Endpoint N + * + *******************************************************************************/ + +static inline int avr_epNrecv(FAR struct avr_ep_s *privep, + FAR struct usbdev_req_s *req) +{ + FAR uint8_t *buffer; + uint8_t regval; + int ret; + + /* Setup pointers and counts for this transfer */ + + buffer = req->buf; + req->xfrd = 0; + + /* This function should not be called if we are not ready to read! */ + + ret = avr_fifoready(AVR_TIMEOUT_LONG); + if (ret != OK) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EP0FIFONOTREADY), ret); + return ret; + } + + /* Loop until the requested number of bytes have been read */ + + while (req->xfrd < req->len) + { + /* RWAL will be de-asserted when everything has been read from the + * receive FIFO */ + + if (((UEINTX & (1 << RWAL)) == 0)) + { + /* The FIFO is empty.. Acknowledge receipt of the packet. RXOUTI must + * always be cleared BEFORE clearing FIFOCON. + */ + + regval = UEINTX; + regval &= ~(1 << RXOUTI); + UEINTX = regval; + regval &= ~(1 << FIFOCON); + UEINTX = regval; + + /* Return success */ + + usbtrace(TRACE_READ(privep->ep.eplog), req->xfrd); + return OK; + } + else + { + /* Receive the next byte */ + + *buffer++ = UEDATX; + + /* Increment the number of bytes received and try again */ + + req->xfrd++; + } + } + + /* We get here if the request buffer is full. There could be more bytes + * pending in the FIFO? + * + * Finalize the OUT stream transfer. RXOUTI must always be cleared BEFORE + * clearing FIFOCON. + */ + + regval = UEINTX; + regval &= ~(1 << RXOUTI); + UEINTX = regval; + regval &= ~(1 << FIFOCON); + UEINTX = regval; + + usbtrace(TRACE_READ(privep->ep.eplog), req->xfrd); + return OK; +} + +/******************************************************************************* + * Name: avr_epINqueue + * + * Description: + * This is part of the IN endpoint interrupt handling logic. It is called + * from interrupt handling logic for an endpoint when the TXIN endpoint + * interrupt occurs. Thus function is also called from the requeust enqueuing + * logic BUT with interrupts disabled. + * + *******************************************************************************/ + +static int avr_epINqueue(FAR struct avr_ep_s *privep) +{ + FAR struct avr_req_s *privreq; + int ret = OK; + + usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_EPIN), 0); + + /* First, check if there is already pending IN transfer */ + + if (privep->pending) + { + /* Yes.. use this request to continue the transfer */ + + privreq = privep->pending; + } + else + { + /* No.. remove the next request from the queue of IN requests */ + + privreq = avr_rqdequeue(privep); + privep->pending = privreq; + } + + /* Is there an IN request */ + + if (privreq) + { + /* Yes.. perform the IN transfer */ + + ret = avr_epNsend(privep, privreq); + + /* The return value of -ETIME means that the transfer was not + * finished within this interrupt. We just need to exit with the + * pending transfer in place. + */ + + if (ret == OK || ret != -ETIME) + { + /* The transfer has completed, perhaps with an error. Return the request + * to the class driver. + */ + + usbtrace(TRACE_COMPLETE(privep->ep.eplog), privreq->req.xfrd); + privep->pending = NULL; + avr_reqcomplete(privep, privreq, ret); + } + } + return ret; +} + +/******************************************************************************* + * Name: avr_epOUTqueue + * + * Description: + * This is part of the OUT endpointeinterrupt handling logic. It is called + * from interrupt handling logic for an endpoint when the RXOUT endpoint + * interrupt occurs. + * + *******************************************************************************/ + +static int avr_epOUTqueue(FAR struct avr_ep_s *privep) +{ + FAR struct avr_req_s *privreq; + int ret = OK; + + usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_EPOUT), 0); + + /* Remove the next request from the queue of OUT requests */ + + privreq = avr_rqdequeue(privep); + + /* Is there an OUT request */ + + if (privreq) + { + /* Yes.. perform the OUT transfer */ + + ret = avr_epNrecv(privep, &privreq->req); + + /* The transfer has completed, perhaps with an error. Return the request + * to the class driver. + */ + + usbtrace(TRACE_COMPLETE(privep->ep.eplog), privreq->req.xfrd); + avr_reqcomplete(privep, privreq, ret); + } + return ret; +} + +/******************************************************************************* + * Name: avr_reqcomplete + * + * Description: + * Handle termination of the request at the head of the endpoint request queue. + * + *******************************************************************************/ + +static void avr_reqcomplete(FAR struct avr_ep_s *privep, FAR struct avr_req_s *privreq, + int result) +{ + /* If endpoint 0, temporarily reflect the state of protocol stalled in the + * callback. */ + + bool stalled = privep->stalled; + if (privep->ep.eplog == AVR_EP0) + { + privep->stalled = g_usbdev.stalled; + } + + /* Save the result in the request structure */ + + privreq->req.result = result; + + /* Callback to the request completion handler */ + + privreq->req.callback(&privep->ep, &privreq->req); + + /* Restore the stalled indication */ + + privep->stalled = stalled; +} + +/******************************************************************************* + * Name: avr_cancelrequests + * + * Description: + * Cancel all pending requests for an endpoint + * + *******************************************************************************/ + +static void avr_cancelrequests(FAR struct avr_ep_s *privep, int status) +{ + /* Is there a pending, active IN transfer? */ + + if (privep->pending) + { + /* Remove the pending request */ + + FAR struct avr_req_s *privreq = privep->pending; + privep->pending = NULL; + + /* Make sure that the endpoint IN interrupt is disabled. */ + + UENUM = privep->ep.eplog; + UEIENX &= ~(1 << TXINE); + + /* Complete the request with the provided status */ + + usbtrace(TRACE_COMPLETE(privep->ep.eplog), privreq->req.xfrd); + avr_reqcomplete(privep, privreq, status); + } + + /* Then complete any queue requests. None of these should be active. */ + + while (!avr_rqempty(privep)) + { + usbtrace(TRACE_COMPLETE(privep->ep.eplog), (avr_rqpeek(privep))->req.xfrd); + avr_reqcomplete(privep, avr_rqdequeue(privep), status); + } +} + +/******************************************************************************* + * Name: avr_cancelall + * + * Description: + * Cancel all pending requests for an endpoint + * + *******************************************************************************/ + +static void avr_cancelall(int status) +{ + struct avr_ep_s *privep; + int i; + + for (i = 1; i < AVR_NENDPOINTS; i++) + { + privep = &g_usbdev.eplist[i]; + if (privep) + { + avr_cancelrequests(privep, status); + } + } +} + +/******************************************************************************* + * Name: avr_epfindbyaddr + * + * Description: + * Find the physical endpoint structure corresponding to a logic endpoint + * address + * + *******************************************************************************/ + +static struct avr_ep_s *avr_epfindbyaddr(uint8_t epno) +{ + struct avr_ep_s *privep; + int i; + + /* Endpoint zero is a special case */ + + if (epno == AVR_EP0) + { + return &g_usbdev.eplist[0]; + } + + /* Handle the remaining */ + + for (i = 1; i < AVR_NENDPOINTS; i++) + { + privep = &g_usbdev.eplist[i]; + + /* Same logical endpoint number? (includes direction bit) */ + + if (epno == privep->ep.eplog) + { + /* Return endpoint found */ + + return privep; + } + } + + /* Return endpoint not found */ + + return NULL; +} + +/******************************************************************************* + * Name: avr_dispatchrequest + * + * Description: + * Provide unhandled setup actions to the class driver. This is logically part + * of the USB interrupt handler. + * + *******************************************************************************/ + +static void avr_dispatchrequest(FAR const struct usb_ctrlreq_s *ctrl) +{ + int ret = -EIO; + + usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_DISPATCH), 0); + if (g_usbdev.driver) + { + /* Forward to the control request to the class driver implementation */ + + ret = CLASS_SETUP(g_usbdev.driver, &g_usbdev.usbdev, ctrl); + } + + if (ret < 0) + { + /* Stall on failure */ + + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_DISPATCHSTALL), 0); + g_usbdev.stalled = true; + } +} + +/******************************************************************************* + * Name: avr_ep0configure + * + * Description: + * Reset Usb engine + * + *******************************************************************************/ + +static int avr_ep0configure(void) +{ + uint8_t regval; + + /* Configure endpoint 0 */ + + UENUM = AVR_EP0; + UECONX |= (1 << EPEN); + UECFG1X = 0; + UECFG0X = AVR_EPTYPE_CTRL; + UECFG1X = (1 << ALLOC) | AVR_EPSIZE_8; + + /* Check for configuration failure */ + + regval = UESTA0X; + if ((regval & (1 << CFGOK)) == 0) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EP0CFGBAD), regval); + return -EINVAL; + } + + /* Enable OUT interrupts */ + + UEIENX |= (1 << RXOUTE); + return OK; +} + +/******************************************************************************* + * Name: avr_epreset + * + * Description: + * Reset the specified endpoint + * + * Input Parameters: + * epno - The endpoint to be reset + * + *******************************************************************************/ + +static void avr_epreset(FAR struct avr_ep_s *privep, int status) +{ + uint8_t epno = privep->ep.eplog; + + /* Reset the endpoint hardware */ + + UEINT &= ~(1 << epno); + UENUM = epno; + UEIENX = 0; + UEINTX = 0; + UECFG1X &= ~(1 << ALLOC); + UECONX &= ~(1 << EPEN); + + /* Cancel all requests */ + + avr_cancelrequests(privep, status); + + /* Reset endpoint status */ + + privep->stalled = false; +} + +/******************************************************************************* + * Name: avr_usbreset + * + * Description: + * Reset Usb engine + * + *******************************************************************************/ + +static void avr_usbreset(void) +{ + uint8_t epno; + uint8_t regval; + + /* Disable all interrupts */ + + USBCON &= ~((1 << VBUSTE) | (1 << IDTE)); + UDIEN = 0; + + /* Clear all pending interrupts */ + + USBINT = 0; + UDINT = 0; + + /* Reset selected state variables */ + + g_usbdev.connected = false; +#ifdef CONFIG_USBDEV_SELFPOWERED + g_usbdev.wkupen = false; +#endif + + /* Reset endpoints */ + + for (epno = 0; epno < AVR_NENDPOINTS; epno++) + { + struct avr_ep_s *privep = &g_usbdev.eplist[epno]; + avr_epreset(privep, -ESHUTDOWN); + } + + /* Tell the class driver that we are disconnected. The class driver should + * then accept any new configurations. */ + + if (g_usbdev.driver) + { + CLASS_DISCONNECT(g_usbdev.driver, &g_usbdev.usbdev); + } + + /* Enable the PLL */ + + PLLCSR = USB_PLL_PSC; + PLLCSR |= (1 << PLLE); + while ((PLLCSR & (1 << PLOCK)) == 0); + + /* Reset the USB interface */ + + regval = USBCON; + USBCON = (regval & ~(1 << USBE)); + USBCON = (regval | (1 << USBE)); + +#ifndef CONFIG_USB_DISABLE_PADREGULATOR + /* Enable the USB pad regulator */ + + UHWCON |= (1 << UVREGE); +#endif + + /* Enable USB clock inputs */ + + USBCON &= ~(1 << FRZCLK); + + /* Select low or high speed */ + +#ifdef CONFIG_USB_LOWSPEED + UDCON |= (1 << LSM); +#else + UDCON &= ~(1 << LSM); +#endif + + /* Set USB address to 0 */ + + avr_setaddress(0); + + /* EndPoint 0 initialization */ + + (void)avr_ep0configure(); + + /* Enable VBUS interrupts */ + + USBCON |= (1 << VBUSTE); + + /* Attach the device to the USB bus. This announces the device's presence to + * any attached USB host, starting the enumeration process. If no host is + * present, attaching the device will allow for enumeration once a host is + * connected to the device. + */ + + UDCON &= ~(1 << DETACH); + + /* Enable Suspend and reset interrupts */ + + UDIEN |= ((1 << SUSPE) | (1 << EORSTE)); +} + +/******************************************************************************* + * Name: avr_usbshutdown + * + * Description: + * Shutdown the USB interface and put the hardare in a known state + * + *******************************************************************************/ + +void avr_usbshutdown(void) +{ + /* Inform the class driver of the disconnection */ + + if (g_usbdev.driver) + { + CLASS_DISCONNECT(g_usbdev.driver, &g_usbdev.usbdev); + } + + /* Detach the device from the USB bus, terminating communications */ + + UDCON |= (1 << DETACH); + + /* Disable all interrupts */ + + USBCON &= ~((1 << VBUSTE) | (1 << IDTE)); + UDIEN = 0; + + /* Clear all pending interrupts */ + + USBINT = 0; + UDINT = 0; + + g_usbdev.connected = false; +#ifdef CONFIG_USBDEV_REMOTEWAKEUP + g_usbdev.wkupen = false; +#endif + + /* Disable the USB interface */ + + USBCON &= ~(1 << USBE); + + /* Shut down the USB PLL */ + + PLLCSR = 0; + + /* Turn off the VBUS pad */ + + USBCON &= ~(1 << OTGPADE); +} + +/******************************************************************************* + * Name: avr_setaddress + * + * Description: + * Set the devices USB address + * + *******************************************************************************/ + +static inline void avr_setaddress(uint8_t address) +{ + uint8_t regval; + + g_usbdev.paddr = address; + g_usbdev.paddrset = (address != 0); + + UEINTX &= ~(1 << RXSTPI); + + /* TXINI must always be cleared BEFORE clearing FIFOCON. */ + + regval = UEINTX; + regval &= ~(1 << TXINI); + UEINTX = regval; + regval &= ~(1 << FIFOCON); + UEINTX = regval; + regval = UEINTX; + + avr_txready(); + UDADDR = ((1 << ADDEN) | address); +} + +/******************************************************************************* + * Name: avr_ep0setup + * + * Description: + * USB Ctrl EP Setup Event. This is logically part of the USB interrupt + * handler. This event occurs when a setup packet is receive on EP0 OUT. + * + *******************************************************************************/ + +static inline void avr_ep0setup(void) +{ + struct avr_ep_s *privep; + struct usb_ctrlreq_s ctrl; + uint8_t *ptr; + uint16_t value; + uint16_t index; + uint16_t len; + uint8_t i; + + usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_EP0SETUP), 0); + + /* Terminate any pending requests */ + + avr_cancelrequests(&g_usbdev.eplist[AVR_EP0], -EPROTO); + avr_cancelrequests(&g_usbdev.eplist[AVR_EP0], -EPROTO); + + /* Assume NOT stalled */ + + g_usbdev.eplist[AVR_EP0].stalled = false; + g_usbdev.eplist[AVR_EP0].stalled = false; + g_usbdev.stalled = false; + + /* Read EP0 setup data -- Read the setup data from the hardware. */ + + ptr = (uint8_t*)&ctrl; + for (i = 0; i < USB_SIZEOF_CTRLREQ; i++) + { + *ptr++ = UEDATX; + } + + /* Extract the little-endian 16-bit values to host order */ + + value = GETUINT16(ctrl.value); + index = GETUINT16(ctrl.index); + len = GETUINT16(ctrl.len); + + ullvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n", + ctrl.type, ctrl.req, value, index, len); + + /* Dispatch any non-standard requests */ + + if ((ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD) + { + avr_dispatchrequest(&ctrl); + } + else + { + /* Handle standard request. Pick off the things of interest to the USB + * device controller driver; pass what is left to the class driver. + */ + + switch (ctrl.req) + { + case USB_REQ_GETSTATUS: + { + /* type: device-to-host; recipient = device, interface, endpoint + * value: 0 index: zero interface endpoint len: 2; data = status + */ + + usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_GETSTATUS), 0); + if (!g_usbdev.paddrset || len != 2 || + (ctrl.type & USB_REQ_DIR_IN) == 0 || value != 0) + { + g_usbdev.stalled = true; + } + else + { + switch (ctrl.type & USB_REQ_RECIPIENT_MASK) + { + case USB_REQ_RECIPIENT_ENDPOINT: + { + usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_EPGETSTATUS), 0); + privep = avr_epfindbyaddr(index); + if (!privep) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADEPGETSTATUS), 0); + g_usbdev.stalled = true; + } + else + { + /* Return endpoint stalled status */ + + if (privep->stalled) + { + g_usbdev.ep0buf[0] = (1 << USB_FEATURE_ENDPOINTHALT); /* Stalled */ + } + else + { + g_usbdev.ep0buf[0] = 0; /* Not stalled */ + } + g_usbdev.ep0buf[1] = 0; + + /* And send the response */ + + avr_ep0send(g_usbdev.ep0buf, 2); + } + } + break; + + case USB_REQ_RECIPIENT_DEVICE: + { + if (index == 0) + { + usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_DEVGETSTATUS), 0); + + /* Features: Remote Wakeup=YES; selfpowered=? */ + /* Return self-powered status */ + +#ifdef CONFIG_USBDEV_SELFPOWERED + g_usbdev.ep0buf[0] = (1 << USB_FEATURE_SELFPOWERED); +#else + g_usbdev.ep0buf[0] = 0; +#endif + /* Return remote wake-up enabled status */ + +#ifdef CONFIG_USBDEV_REMOTEWAKEUP + if (g_usbdev.wkupen) + { + status |= (1 << USB_FEATURE_REMOTEWAKEUP); + } +#endif + g_usbdev.ep0buf[1] = 0; + + /* And send the response */ + + avr_ep0send(g_usbdev.ep0buf, 2); + } + else + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADDEVGETSTATUS), 0); + g_usbdev.stalled = true; + } + } + break; + + case USB_REQ_RECIPIENT_INTERFACE: + { + usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_IFGETSTATUS), 0); + g_usbdev.ep0buf[0] = 0; + g_usbdev.ep0buf[1] = 0; + + avr_ep0send(g_usbdev.ep0buf, 2); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADGETSTATUS), 0); + g_usbdev.stalled = true; + } + break; + } + } + } + break; + + case USB_REQ_CLEARFEATURE: + { + /* type: host-to-device; recipient = device, interface or endpoint + * value: feature selector index: zero interface endpoint; len: + * zero, data = none + */ + + usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_CLEARFEATURE), 0); + switch (ctrl.type & USB_REQ_RECIPIENT_MASK) + { + case USB_REQ_RECIPIENT_ENDPOINT: + if (g_usbdev.paddrset != 0 && + value == USB_FEATURE_ENDPOINTHALT && + len == 0 && + (privep = avr_epfindbyaddr(index)) != NULL) + { + avr_epstall(&privep->ep, false); + } + else + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADCLREPFEATURE), value); + g_usbdev.stalled = true; + } + break; + + case USB_REQ_RECIPIENT_DEVICE: +#ifdef CONFIG_USBDEV_SELFPOWERED + if (g_usbdev.paddrset != 0 && + value == USB_FEATURE_REMOTEWAKEUP && + len == 0) + { + g_usbdev.wkupen = 0; + } + else + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADCLRDEVFEATURE), value); + g_usbdev.stalled = true; + } + break; +#endif + + default: + avr_dispatchrequest(&ctrl); + break; + } + } + break; + + case USB_REQ_SETFEATURE: + { + /* type: host-to-device; recipient = device, interface, endpoint + * value: feature selector index: zero interface endpoint; len: 0; + * data = none + */ + + usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_SETFEATURE), 0); + switch (ctrl.type & USB_REQ_RECIPIENT_MASK) + { + case USB_REQ_RECIPIENT_ENDPOINT: + if (g_usbdev.paddrset != 0 && + value == USB_FEATURE_ENDPOINTHALT && + len == 0 && + (privep = avr_epfindbyaddr(index)) != NULL) + { + avr_epstall(&privep->ep, true); + } + else + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADSETEPFEATURE), value); + g_usbdev.stalled = true; + } + break; + + case USB_REQ_RECIPIENT_DEVICE: +#ifdef CONFIG_USBDEV_SELFPOWERED + if (value == USB_FEATURE_TESTMODE) + { + ullvdbg("test mode: %d\n", index); + } + else if (value == USB_FEATURE_REMOTEWAKEUP) + { + g_usbdev.wkupen = 1; + } + else + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADSETDEVFEATURE), value); + g_usbdev.stalled = true; + } + break; +#endif + + default: + avr_dispatchrequest(&ctrl); + break; + } + } + break; + + case USB_REQ_SETADDRESS: + { + /* type: host-to-device; recipient = device value: device address + * index: 0 len: 0; data = none + */ + + usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_EP0SETUPSETADDRESS), value); + if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && + index == 0 && + len == 0 && + value < 128) + { + /* Save the address */ + + avr_setaddress(ctrl.value[0]); + } + else + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADSETADDRESS), 0); + g_usbdev.stalled = true; + } + } + break; + + case USB_REQ_GETDESCRIPTOR: + /* type: device-to-host; recipient = device value: descriptor type + * and index index: 0 or language ID; len: descriptor len; data = + * descriptor. + */ + + case USB_REQ_SETDESCRIPTOR: + { + /* type: host-to-device; recipient = device value: descriptor type + * and index index: 0 or language ID; len: descriptor len; data = + * descriptor + */ + + usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_GETSETDESC), 0); + if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) + { + avr_dispatchrequest(&ctrl); + } + else + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADGETSETDESC), 0); + g_usbdev.stalled = true; + } + } + break; + + case USB_REQ_GETCONFIGURATION: + { + /* type: device-to-host; recipient = device value: 0; index: 0; len: + * 1; data = configuration value + */ + + usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_GETCONFIG), 0); + if (g_usbdev.paddrset && + (ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && + value == 0 && + index == 0 && + len == 1) + { + avr_dispatchrequest(&ctrl); + } + else + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADGETCONFIG), 0); + g_usbdev.stalled = true; + } + } + break; + + case USB_REQ_SETCONFIGURATION: + { + /* type: host-to-device; recipient = device value: configuration + * value index: 0; len: 0; data = none + */ + + usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_SETCONFIG), 0); + if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE && + index == 0 && + len == 0) + { + avr_dispatchrequest(&ctrl); + } + else + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADSETCONFIG), 0); + g_usbdev.stalled = true; + } + } + break; + + case USB_REQ_GETINTERFACE: + /* type: device-to-host; recipient = interface value: 0 index: + * interface; len: 1; data = alt interface + */ + + case USB_REQ_SETINTERFACE: + { + /* type: host-to-device; recipient = interface value: alternate + * setting index: interface; len: 0; data = none + */ + + usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_GETSETIF), 0); + avr_dispatchrequest(&ctrl); + } + break; + + case USB_REQ_SYNCHFRAME: + { + /* type: device-to-host; recipient = endpoint value: 0 index: + * endpoint; len: 2; data = frame number + */ + + usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_SYNCHFRAME), 0); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INVALIDCTRLREQ), 0); + g_usbdev.stalled = true; + } + break; + } + } + + if (g_usbdev.stalled) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EP0SETUPSTALLED), 0); + avr_epstall(&g_usbdev.eplist[AVR_EP0].ep, false); + avr_epstall(&g_usbdev.eplist[AVR_EP0].ep, false); + } + + if ((UEINTX & (1 << RXSTPI)) != 0) + { + UECONX |= (1 << STALLRQ); + UEINTX &= ~(1 << RXSTPI); + } +} + +/******************************************************************************* + * Name: avr_ep0interrupt + * + * Description: + * USB endpoint/pipe IN interrupt handler + * + *******************************************************************************/ + +static inline void avr_ep0interrupt(void) +{ + /* Check if the control endpoint endpoint is pending */ + + if ((UEINT & (1 << AVR_EP0)) != 0) + { + /* Clear the endpoint interrupt */ + + UEINT &= ~(1 << AVR_EP0); + + /* Select the control endpoint */ + + UENUM = AVR_EP0; + + /* Check if the control endpoint has received a setup packet */ + + if ((UEINTX & (1 << RXSTPI)) != 0) + { + /* It has... process the control packet */ + + avr_ep0setup(); + } + + /* Handshake the endpoint setup interrupt */ + + UEINTX &= ~(1 << RXSTPI); + } +} + +/******************************************************************************* + * Name: avr_epNinterrupt + * + * Description: + * USB endpoint/pipe IN interrupt handler + * + *******************************************************************************/ + +static inline void avr_epNinterrupt(void) +{ + struct avr_ep_s *privep; + uint8_t ueint = UEINT & (g_usbdev.epoutset | g_usbdev.epinset); + uint8_t epno; + uint8_t mask; + + /* Check if any endpoint interrupt is pending */ + + for (epno = 1, mask = 2; epno < AVR_NENDPOINTS && ueint != 0; epno++, mask <<= 1) + { + /* Is there an interrupt pending on this endpoint? */ + + if ((ueint & mask) != 0) + { + ueint &= ~mask; + + /* Select the endpoint */ + + UENUM = epno; + privep = &g_usbdev.eplist[epno]; + + /* Is this an IN or an OUT interrupt? */ + + if (privep->epin) + { + /* Clear the endpoint IN interrupt flag (TXINI) */ + + UEINTX &= ~(1 << TXINI); + + /* Are IN endpoint interrupts enabled? */ + + if ((UEIENX & (1 << TXINE)) != 0) + { + /* Clear the endpoint interrupt */ + + UEINT &= ~(1 << epno); + + /* Handle the IN request queue */ + + (void)avr_epINqueue(privep); + } + } + else + { + /* Is is an OUT endpoint interrupt. Are OUT endpoint + * interrupts enabled? + */ + + if ((UEIENX & (1 << RXOUTE)) != 0) + { + /* Clear the endpoint interrupt */ + + UEINT &= ~(1 << epno); + + /* Handle the OUT request queue */ + + (void)avr_epOUTqueue(privep); + } + } + } + } +} + +/******************************************************************************* + * Name: avr_epinterrupt + * + * Description: + * USB endpoint/pipe interrupt handler + * + *******************************************************************************/ + +static int avr_epinterrupt(int irq, FAR void *context) +{ + usbtrace(TRACE_INTENTRY(AVR_TRACEINTID_EPINT), irq); + + /* Handle control endpoint interrupts */ + + avr_ep0interrupt(); + + /* Handle opther endpoint interrupts (N=1,2,..6 */ + + avr_epNinterrupt(); + + usbtrace(TRACE_INTEXIT(AVR_TRACEINTID_EPINT), irq); + return OK; +} + +/******************************************************************************* + * Name: avr_genvbus + * + * Description: + * A change in VBUS has been detected. Check if the device has been + * connected to or disconnected from a host. + * + *******************************************************************************/ + +static void avr_genvbus(void) +{ + bool vbus; + + usbtrace(TRACE_INTENTRY(AVR_TRACEINTID_VBUS), USBSTA); + + /* How has the VSUS signal changed? */ + + vbus = ((USBSTA & (1 << VBUS)) != 0); + if (!g_usbdev.connected && vbus) + { + /* We were not connected, but now we are */ + + avr_usbreset(); + g_usbdev.connected = true; + } + else if (g_usbdev.connected && !vbus) + { + /* We were connected, but now we are not */ + /* Cancel all pending and queue requests */ + + avr_cancelall(-ENODEV); + + /* Detach the device from the USB bus, terminating communications */ + + UDCON |= (1 << DETACH); + + /* Disable the clock inputs (the ”Resume Detection” is still active). + * This reduces the power consumption. Clear to enable the clock inputs. */ + + USBCON |= (1 << FRZCLK); + + /* Shut down the USB PLL */ + + PLLCSR = 0; + + /* Disable the USB pad regulator */ + + UHWCON &= ~(1 << UVREGE); + g_usbdev.connected = false; + } +} + +/******************************************************************************* + * Name: avr_gensuspend + * + * Description: + * The USB controller has been put in suspend mode. + * + *******************************************************************************/ + +static inline void avr_gensuspend(void) +{ + usbtrace(TRACE_INTENTRY(AVR_TRACEINTID_SUSPEND), UDIEN); + + UDIEN &= ~(1 << SUSPE); + UDIEN |= (1 << WAKEUPE); + + /* Disable the clock inputs to reduce power consumption. (wakeup + * detection is still active). + */ + + USBCON |= (1 << FRZCLK); + + /* And shut down the USB PLL */ + + PLLCSR = 0; +} + +/******************************************************************************* + * Name: avr_genwakeup + * + * Description: + * Resume from suspend mode. + * + *******************************************************************************/ + +static void avr_genwakeup(void) +{ + usbtrace(TRACE_INTENTRY(AVR_TRACEINTID_WAKEUP), UDIEN); + + /* Re-enable the PLL */ + + PLLCSR = USB_PLL_PSC; + PLLCSR |= (1 << PLLE); + while ((PLLCSR & (1 << PLOCK)) == 0); + + /* Re-enable USB clock inputs */ + + USBCON &= ~(1 << FRZCLK); + UDIEN &= ~(1 << WAKEUPE); + UDIEN |= (1 << SUSPE); +} + +/******************************************************************************* + * Name: avr_geneor + * + * Description: + * Handle an end-of-reset interrupt + * + *******************************************************************************/ + +static inline void avr_geneor(void) +{ + uint8_t epno; + + usbtrace(TRACE_INTENTRY(AVR_TRACEINTID_EOR), UDIEN); + + UDIEN &= ~(1 << SUSPE); + UDIEN |= (1 << WAKEUPE); + + /* Reset all endpoints and reconfigure endpoint 0 */ + + UEINT = 0; + for (epno = 0; epno < AVR_NENDPOINTS; epno++) + { + struct avr_ep_s *privep = &g_usbdev.eplist[epno]; + avr_epreset(privep, -EAGAIN); + } + + usbtrace(TRACE_EPCONFIGURE, AVR_EP0); + + /* Configure endpoint 0 */ + + (void)avr_ep0configure(); + + /* Reset endpoint status */ + + g_usbdev.stalled = false; + + /* Enable the endpoint SETUP interrupt ISR for the control endpoint */ + + UEIENX |= (1 << RXSTPE); +} + +/******************************************************************************* + * Name: avr_geninterrupt + * + * Description: + * USB general interrupt handler + * + *******************************************************************************/ + +static int avr_geninterrupt(int irq, FAR void *context) +{ + usbtrace(TRACE_INTENTRY(AVR_TRACEINTID_GENINT), irq); + + /* Check for a change in VBUS state detected */ + + if ((USBINT & (1 << VBUSTI)) != 0 && (USBCON & (1 << VBUSTE)) != 0) + { + USBINT &= ~(1 << VBUSTI); + avr_genvbus(); + } + + /* Check for a suspend event */ + + if ((UDINT & (1 << SUSPI)) != 0 && (UDIEN & (1 << SUSPE)) != 0) + { + UDINT &= ~(1 << SUSPI); + avr_gensuspend(); + } + + /* Check for a wake-up event */ + + if ((UDINT & (1 << WAKEUPI)) != 0 && (UDIEN & (1 << WAKEUPE)) != 0) + { + UDINT &= ~(1 << WAKEUPI); + avr_genwakeup(); + } + + /* Check for an end-of-reset, speed identification interrupt */ + + if ((UDINT & (1 << EORSTI)) != 0 && (UDIEN & (1 << EORSTE)) != 0) + { + UDINT &= ~(1 << EORSTI); + avr_geneor(); + } + + usbtrace(TRACE_INTEXIT(AVR_TRACEINTID_GENINT), irq); + return OK; +} + +/******************************************************************************* + * Name: avr_epconfigure + * + * Description: + * Configure endpoint, making it usable + * + * Input Parameters: + * ep - the struct usbdev_ep_s instance obtained from allocep() + * desc - A struct usb_epdesc_s instance describing the endpoint + * last - true if this this last endpoint to be configured. Some hardware + * needs to take special action when all of the endpoints have been + * configured. + * + *******************************************************************************/ + +static int avr_epconfigure(FAR struct usbdev_ep_s *ep, + FAR const struct usb_epdesc_s *desc, bool last) +{ + uint16_t maxpacket = GETUINT16(desc->mxpacketsize); + uint8_t uecfg0x; + uint8_t uecfg1x; + uint8_t ueienx = 0; + uint8_t regval; + + usbtrace(TRACE_EPCONFIGURE, ep->eplog); + DEBUGASSERT(desc->addr == ep->eplog); + + /* Configure the endpoint */ + + uecfg0x = 0; + uecfg1x = (1 << ALLOC); + + /* Handle the endpoint type */ + + switch (desc->attr & USB_EP_ATTR_XFERTYPE_MASK) + { + case USB_EP_ATTR_XFER_CONTROL: + uecfg0x |= AVR_EPTYPE_CTRL; + break; + + case USB_EP_ATTR_XFER_ISOC: + uecfg0x |= AVR_EPTYPE_ISOC; + break; + + case USB_EP_ATTR_XFER_BULK: + uecfg0x |= AVR_EPTYPE_BULK; + break; + + case USB_EP_ATTR_XFER_INT: + uecfg0x |= AVR_EPTYPE_INTR; + break; + + default: + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_XFERTYPE), desc->attr); + return -EINVAL; + } + + /* Handle the endpoint direction */ + + if (USB_ISEPIN(desc->addr)) + { + uecfg0x |= AVR_DIR_IN; + ueienx = (1 << RXOUTE); + } + + /* Handle banking (this needs to be revisited... Always double bank?) */ + + if (ep->eplog != 0) + { + uecfg1x |= AVR_DOUBLE_BANK; + } + + /* Handle the maximum packet size */ + + switch (maxpacket) + { + case 8: + uecfg1x |= AVR_EPSIZE_8; + break; + case 16: + uecfg1x |= AVR_EPSIZE_16; + break; + case 32: + uecfg1x |= AVR_EPSIZE_32; + break; + case 64: + uecfg1x |= AVR_EPSIZE_64; + break; + case 128: + uecfg1x |= AVR_EPSIZE_128; + break; + case 256: + if (ep->eplog == 1) + { + uecfg1x |= AVR_EPSIZE_8; + break; + } + default: + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_PKTSIZE), maxpacket); + return -EINVAL; + } + + /* Instantiate the configuration */ + + UENUM = ep->eplog; + UECONX |= (1 << EPEN); + UECFG1X = 0; + UECFG0X = uecfg0x; + UECFG1X = uecfg1x; + + /* Check for configuration failure */ + + regval = UESTA0X; + if ((regval & (1 << CFGOK)) == 0) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EPCFGBAD), regval); + return -EINVAL; + } + + /* Reset endpoint status */ + + g_usbdev.stalled = false; + + /* Enable interrupts as appropriate for this endpoint */ + + UEIENX |= uecfg1x; + return OK; +} + +/******************************************************************************* + * Name: avr_epdisable + * + * Description: + * The endpoint will no longer be used + * + *******************************************************************************/ + +static int avr_epdisable(FAR struct usbdev_ep_s *ep) +{ + FAR struct avr_ep_s *privep = (FAR struct avr_ep_s *)ep; + irqstate_t flags; + +#ifdef CONFIG_DEBUG + if (!ep) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + usbtrace(TRACE_EPDISABLE, privep->ep.eplog); + + flags = irqsave(); + + /* Disable the endpoint */ + + avr_epreset(privep, -ESHUTDOWN); + g_usbdev.stalled = true; + + irqrestore(flags); + return OK; +} + +/******************************************************************************* + * Name: avr_epallocreq + * + * Description: + * Allocate an I/O request + * + *******************************************************************************/ + +static FAR struct usbdev_req_s *avr_epallocreq(FAR struct usbdev_ep_s *ep) +{ + FAR struct avr_req_s *privreq; + +#ifdef CONFIG_DEBUG + if (!ep) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INVALIDPARMS), 0); + return NULL; + } +#endif + usbtrace(TRACE_EPALLOCREQ, ((FAR struct avr_ep_s *)ep)->ep.eplog); + + privreq = (FAR struct avr_req_s *)malloc(sizeof(struct avr_req_s)); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_ALLOCFAIL), 0); + return NULL; + } + + memset(privreq, 0, sizeof(struct avr_req_s)); + return &privreq->req; +} + +/******************************************************************************* + * Name: avr_epfreereq + * + * Description: + * Free an I/O request + * + *******************************************************************************/ + +static void avr_epfreereq(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + FAR struct avr_req_s *privreq = (FAR struct avr_req_s *)req; + +#ifdef CONFIG_DEBUG + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INVALIDPARMS), 0); + return; + } +#endif + + usbtrace(TRACE_EPFREEREQ, ((FAR struct avr_ep_s *)ep)->ep.eplog); + free(privreq); +} + +/******************************************************************************* + * Name: avr_epallocbuffer + * + * Description: + * Allocate an I/O buffer + * + *******************************************************************************/ + +#ifdef CONFIG_ARCH_USBDEV_DMA +static void *avr_epallocbuffer(FAR struct usbdev_ep_s *ep, unsigned bytes) +{ + usbtrace(TRACE_EPALLOCBUFFER, privep->ep.eplog); +return malloc(bytes)} +#endif + +/******************************************************************************* + * Name: avr_epfreebuffer + * + * Description: + * Free an I/O buffer + * + *******************************************************************************/ + +#ifdef CONFIG_AVR_USBDEV_DMA +static void avr_epfreebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf) +{ + usbtrace(TRACE_EPFREEBUFFER, privep->ep.eplog); + free(buf); +} +#endif + +/******************************************************************************* + * Name: avr_epsubmit + * + * Description: + * Submit an I/O request to the endpoint + * + *******************************************************************************/ + +static int avr_epsubmit(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + FAR struct avr_req_s *privreq = (FAR struct avr_req_s *)req; + FAR struct avr_ep_s *privep = (FAR struct avr_ep_s *)ep; + irqstate_t flags; + int ret = OK; + +#ifdef CONFIG_DEBUG + if (!req || !req->callback || !req->buf || !ep) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INVALIDPARMS), 0); + ullvdbg("req=%p callback=%p buf=%p ep=%p\n", + req, req->callback, req->buf, ep); + return -EINVAL; + } +#endif + + usbtrace(TRACE_EPSUBMIT, privep->ep.eplog); + + if (!g_usbdev.driver || g_usbdev.usbdev.speed == USB_SPEED_UNKNOWN) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_NOTCONFIGURED), g_usbdev.usbdev.speed); + return -ESHUTDOWN; + } + + /* Handle the request from the class driver */ + + req->result = -EINPROGRESS; + req->xfrd = 0; + + /* Disable Interrupts */ + + flags = irqsave(); + + /* If we are stalled, then drop all requests on the floor */ + + if (g_usbdev.stalled) + { + ret = -EBUSY; + } + + /* Ignore any attempt to enqueue a zero length packet */ + + else if (privreq->req.len == 0) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EPNULLPACKET), 0); + ret = -EINVAL; + } + else + { + /* Add the new request to the request queue for the endpoint */ + + avr_rqenqueue(privep, privreq); + + /* Some special operations have to be performed for IN requests. For + * these, we may have to initiate the next transfer. + */ + + if (privep->epin) + { + /* It is an IN transfer */ + + usbtrace(TRACE_INREQQUEUED(privep->ep.eplog), privreq->req.len); + + /* Is there an IN transfer in progress (waiting for the FIFO)? If + * not and if the FIFO is available now, then start the next + * IN transfer. + */ + + if (!privep->pending && avr_fifoready(AVR_TIMEOUT_NONE) == OK) + { + /* No, then start the next IN transfer */ + + ret = avr_epINqueue(privep); + } + } + else + { + /* It is an OUT transfer */ + + usbtrace(TRACE_OUTREQQUEUED(privep->ep.eplog), privreq->req.len); + + /* If there is something avaible in the fifo now, then go get it */ + + if (avr_fifoready(AVR_TIMEOUT_NONE) == OK); + { + ret = avr_epOUTqueue(privep); + } + } + } + + irqrestore(flags); + return ret; +} + +/******************************************************************************* + * Name: avr_epcancel + * + * Description: + * Cancel an I/O request previously sent to an endpoint + * + *******************************************************************************/ + +static int avr_epcancel(FAR struct usbdev_ep_s *ep, + FAR struct usbdev_req_s *req) +{ + FAR struct avr_ep_s *privep = (FAR struct avr_ep_s *)ep; + irqstate_t flags; + +#ifdef CONFIG_DEBUG + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + usbtrace(TRACE_EPCANCEL, privep->ep.eplog); + + /* FIXME: if the request is the first, then we need to flush the EP otherwise + * just remove it from the list but ... all other implementations cancel all + * requests ... */ + + flags = irqsave(); + avr_cancelrequests(privep, -ESHUTDOWN); + irqrestore(flags); + return OK; +} + +/******************************************************************************* + * Name: avr_epstall + * + * Description: + * Stall or resume and endpoint + * + *******************************************************************************/ + +static int avr_epstall(FAR struct usbdev_ep_s *ep, bool resume) +{ + irqstate_t flags; + + /* STALL or RESUME the endpoint */ + + flags = irqsave(); + if (resume) + { + /* Clear stall and reset the data toggle */ + + UECONX |= (1 << STALLRQC); + UERST = (1 << ep->eplog); + UERST = 0; + UECONX |= (1 << RSTDT); + g_usbdev.stalled = false; + } + else + { + UECONX |= (1 << STALLRQ); + g_usbdev.stalled = true; + } + irqrestore(flags); + return OK; +} + +/******************************************************************************* + * Device operations + *******************************************************************************/ + +/******************************************************************************* + * Name: avr_allocep + * + * Description: + * Allocate an endpoint matching the parameters. + * + * Input Parameters: + * eplog - 7-bit logical endpoint number (direction bit ignored). Zero means + * that any endpoint matching the other requirements will suffice. The + * assigned endpoint can be found in the eplog field. + * in - true: IN (device-to-host) endpoint requested + * eptype - Endpoint type. One of {USB_EP_ATTR_XFER_ISOC, USB_EP_ATTR_XFER_BULK, + * USB_EP_ATTR_XFER_INT} + * + *******************************************************************************/ + +static FAR struct usbdev_ep_s *avr_allocep(FAR struct usbdev_s *dev, + uint8_t epno, bool in, + uint8_t eptype) +{ + irqstate_t flags; + uint8_t epset = g_usbdev.epavail; + uint8_t epmask; + uint8_t epndx = 0; + + usbtrace(TRACE_DEVALLOCEP, epno); + + /* Ignore any direction bits in the logical address */ + + epno = USB_EPNO(epno); + + /* A logical address of 0 means that any endpoint will do */ + + if (epno > 0) + { + /* Otherwise, we will return the endpoint structure only for the + * requested 'logical' endpoint. + */ + +#ifdef CONFIG_DEBUG + if (epno >= AVR_NENDPOINTS) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADEPNO), (uint16_t)epno); + return NULL; + } +#endif + + /* Convert the logical address to a physical OUT endpoint address and + * remove all of the candidate endpoints from the bitset except for the + * the IN/OUT pair for this logical address. */ + + epset &= (1 << epno); + } + + /* Are any endpoints available? */ + + if (epset) + { + /* Yes.. now see if any of the request endpoints are available */ + + flags = irqsave(); + + /* Select the lowest bit in the set of matching, available endpoints */ + + for (epndx = 1; epndx < AVR_NENDPOINTS; epndx++) + { + epmask = 1 << epndx; + if ((epset & epmask) != 0) + { + /* Mark the IN/OUT endpoint no longer available */ + + g_usbdev.epavail &= ~epmask; + if (in) + { + g_usbdev.epinset |= epmask; + } + else + { + g_usbdev.epoutset |= epmask; + } + + /* And return the pointer to the standard endpoint structure */ + + irqrestore(flags); + return &g_usbdev.eplist[epndx].ep; + } + } + + /* Shouldn't get here */ + + irqrestore(flags); + } + + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_NOEP), (uint16_t) epno); + return NULL; +} + +/******************************************************************************* + * Name: avr_freeep + * + * Description: + * Free the previously allocated endpoint + * + *******************************************************************************/ + +static void avr_freeep(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep) +{ + FAR struct avr_ep_s *privep = (FAR struct avr_ep_s *)ep; + irqstate_t flags; + uint8_t epmask; + + usbtrace(TRACE_DEVFREEEP, (uint16_t) privep->ep.eplog); + + /* Mark the endpoint as available */ + + flags = irqsave(); + epmask = (1 << privep->ep.eplog); + g_usbdev.epavail |= epmask; + g_usbdev.epinset &= ~epmask; + g_usbdev.epoutset &= ~epmask; + irqrestore(flags); +} + +/******************************************************************************* + * Name: avr_getframe + * + * Description: + * Returns the current frame number + * + *******************************************************************************/ + +static int avr_getframe(struct usbdev_s *dev) +{ + /* Return the last frame number detected by the hardware */ + + usbtrace(TRACE_DEVGETFRAME, 0); + return (int)UDFNUMH << 8 | (int)UDFNUML; +} + +/******************************************************************************* + * Name: avr_wakeup + * + * Description: + * Tries to wake up the host connected to this device + * + *******************************************************************************/ + +static int avr_wakeup(struct usbdev_s *dev) +{ + irqstate_t flags; + + usbtrace(TRACE_DEVWAKEUP, 0); + + flags = irqsave(); + avr_genwakeup(); + irqrestore(flags); + return OK; +} + +/******************************************************************************* + * Name: avr_selfpowered + * + * Description: + * Sets/clears the device selfpowered feature + * + *******************************************************************************/ + +static int avr_selfpowered(struct usbdev_s *dev, bool selfpowered) +{ + usbtrace(TRACE_DEVSELFPOWERED, (uint16_t) selfpowered); + +#ifdef CONFIG_DEBUG + if (!dev) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INVALIDPARMS), 0); + return -ENODEV; + } +#endif + + g_usbdev.selfpowered = selfpowered; + return OK; +} + +/******************************************************************************* + * Name: avr_pullup + * + * Description: + * Software-controlled connect to/disconnect from USB host + * + *******************************************************************************/ + +static int avr_pullup(struct usbdev_s *dev, bool enable) +{ + usbtrace(TRACE_DEVPULLUP, (uint16_t) enable); + return OK; +} + +/******************************************************************************* + * Public Functions + *******************************************************************************/ + +/******************************************************************************* + * Name: up_usbinitialize + * + * Description: + * Initialize USB hardware. + * + * Assumptions: + * - This function is called very early in the initialization sequence + * - PLL and GIO pin initialization is not performed here but should been in + * the low-level boot logic: PLL1 must be configured for operation at 48MHz + * and P0.23 and PO.31 in PINSEL1 must be configured for Vbus and USB connect + * LED. + * + *******************************************************************************/ + +void up_usbinitialize(void) +{ + int i; + + usbtrace(TRACE_DEVINIT, 0); + + /* Shutdown the USB interface to put it in a known initial state */ + + avr_usbshutdown(); + + /* Select USB device mode */ + + UHWCON |= (1 << UIMOD); + + /* Initialize the device state structure */ + + memset(&g_usbdev, 0, sizeof(struct avr_usbdev_s)); + g_usbdev.usbdev.ops = &g_devops; + g_usbdev.usbdev.ep0 = &g_usbdev.eplist[AVR_EP0].ep; + g_usbdev.epavail = AVR_ALL_EPS & ~(1 << AVR_EP0); + + /* Initialize the endpoint list */ + + for (i = 0; i < AVR_NENDPOINTS; i++) + { + /* Set endpoint operations, reference to driver structure (not really + * necessary because there is only one controller), and the physical + * endpoint number (which is just the index to the endpoint). + */ + + g_usbdev.eplist[i].ep.ops = &g_epops; + g_usbdev.eplist[i].ep.eplog = i; + + /* The maximum packet size may depend on the endpoint. Endpoint 1 has + * a larger maximum packet size than the other endpoints. + */ + + g_usbdev.eplist[i].ep.maxpacket = (i == 1) ? 256 : 64; + } + + /* Reset the interface to force re-enumeration */ + + avr_usbreset(); + + /* Attach USB controller general interrupt handler */ + + if (irq_attach(AT90USB_IRQ_USBGEN, avr_geninterrupt) != 0) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_IRQREGISTRATION), AT90USB_IRQ_USBGEN); + goto errout; + } + + /* Attach USB controller endpoint/pipe interrupt handler */ + + if (irq_attach(AT90USB_IRQ_USBEP, avr_epinterrupt) != 0) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_IRQREGISTRATION), AT90USB_IRQ_USBEP); + goto errout; + } + + /* Set the VBUS pad */ + + USBCON |= (1 << OTGPADE); + + /* Disconnect device */ + + avr_pullup(&g_usbdev.usbdev, false); + return; + +errout: + up_usbuninitialize(); +} + +/******************************************************************************* + * Name: up_usbuninitialize + *******************************************************************************/ + +void up_usbuninitialize(void) +{ + irqstate_t flags; + + usbtrace(TRACE_DEVUNINIT, 0); + + if (g_usbdev.driver) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_DRIVERREGISTERED), 0); + usbdev_unregister(g_usbdev.driver); + } + + /* Disconnect device */ + + flags = irqsave(); + avr_pullup(&g_usbdev.usbdev, false); + g_usbdev.usbdev.speed = USB_SPEED_UNKNOWN; + + /* Detach IRQs */ + + irq_detach(AT90USB_IRQ_USBGEN); + irq_detach(AT90USB_IRQ_USBEP); + + /* Shutdown the USB controller hardware */ + + avr_usbshutdown(); + irqrestore(flags); +} + +/******************************************************************************* + * Name: usbdev_register + * + * Description: + * Register a USB device class driver. The class driver's bind() method will be + * called to bind it to a USB device driver. + * + *******************************************************************************/ + +int usbdev_register(struct usbdevclass_driver_s *driver) +{ + int ret; + + usbtrace(TRACE_DEVREGISTER, 0); + +#ifdef CONFIG_DEBUG + if (!driver || !driver->ops->bind || !driver->ops->unbind || + !driver->ops->disconnect || !driver->ops->setup) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } + + if (g_usbdev.driver) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_DRIVER), 0); + return -EBUSY; + } +#endif + + /* First hook up the driver */ + + g_usbdev.driver = driver; + + /* Then bind the class driver */ + + ret = CLASS_BIND(driver, &g_usbdev.usbdev); + if (ret) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BINDFAILED), (uint16_t) - ret); + g_usbdev.driver = NULL; + } + else + { + /* FIXME: nothing seems to call DEV_CONNECT(), but we need to set the RS + * bit to enable the controller. It kind of makes sense to do this + * after the class has bound to us... GEN: This bug is really in the + * class driver. It should make the soft connect when it is ready to be + * enumerated. I have added that logic to the class drivers but left + * this logic here. */ + + avr_pullup(&g_usbdev.usbdev, true); + } + return ret; +} + +/******************************************************************************* + * Name: usbdev_unregister + * + * Description: + * Un-register usbdev class driver.If the USB device is connected to a USB host, + * it will first disconnect(). The driver is also requested to unbind() and clean + * up any device state, before this procedure finally returns. + * + *******************************************************************************/ + +int usbdev_unregister(struct usbdevclass_driver_s *driver) +{ + usbtrace(TRACE_DEVUNREGISTER, 0); + +#ifdef CONFIG_DEBUG + if (driver != g_usbdev.driver) + { + usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Unbind the class driver */ + + CLASS_UNBIND(driver, &g_usbdev.usbdev); + + /* Unhook the driver */ + + g_usbdev.driver = NULL; + return OK; +} + +/******************************************************************************* + * Name: avr_pollvbus + * + * Description: + * Sample VBUS to see if there are changes in our connection status. There + * is actually an interrupt to signal this case so it should not be necessary + * to poll our connection status. However, on certain "noisy" systems, VBUS + * may bounce and provide inaccurate information in the interrupt handler + * (especially if a relay is used to switch VBUS!). This poll is, then, + * simply a failsafe to assure that VBUS connection events are never missed. + * + *******************************************************************************/ + +#ifdef CONFIG_USB_NOISYVBUS + void avr_pollvbus(void) +{ + irqstate_t flags; + + flags = irqsave(); + avr_genvbus(); + irqrestore(flags); +} +#endif \ No newline at end of file -- cgit v1.2.3