2972 lines
81 KiB
C
2972 lines
81 KiB
C
/****************************************************************************
|
||
* arch/arm/src/at90usb/at90usb_usbdev.c
|
||
*
|
||
* Copyright (C) 2011-2013, 2016 Gregory Nutt. All rights reserved.
|
||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||
*
|
||
* 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 <nuttx/config.h>
|
||
|
||
#include <sys/types.h>
|
||
#include <stdint.h>
|
||
#include <stdbool.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <errno.h>
|
||
#include <debug.h>
|
||
|
||
#include <nuttx/irq.h>
|
||
#include <nuttx/arch.h>
|
||
#include <nuttx/kmalloc.h>
|
||
#include <nuttx/usb/usb.h>
|
||
#include <nuttx/usb/usbdev.h>
|
||
#include <nuttx/usb/usbdev_trace.h>
|
||
|
||
#include <avr/io.h>
|
||
|
||
#include <arch/irq.h>
|
||
#include <arch/board/board.h>
|
||
|
||
#include "chip.h"
|
||
#include "up_arch.h"
|
||
#include "up_internal.h"
|
||
|
||
/****************************************************************************
|
||
* Pre-processor 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, FAR void *arg);
|
||
|
||
/* 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, FAR void *arg);
|
||
|
||
/* 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_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_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, NULL, 0);
|
||
}
|
||
|
||
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)
|
||
{
|
||
FAR struct avr_ep_s *privep = &g_usbdev.eplist[AVR_EP0];
|
||
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;
|
||
}
|
||
|
||
/* Initialize the endpoint data structure. Mark EP0 as an IN endpoint so
|
||
* that the submit() logic will know that any enqueue packets are to be
|
||
* sent.
|
||
*/
|
||
|
||
memset(privep, 0, sizeof(struct avr_ep_s));
|
||
privep->ep.ops = &g_epops;
|
||
privep->ep.maxpacket = AVR_CTRLEP_SIZE;
|
||
privep->epin = 1;
|
||
|
||
/* 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);
|
||
|
||
uinfo("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)
|
||
{
|
||
uinfo("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, FAR void *arg)
|
||
{
|
||
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 <20>Resume Detection<6F> 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);
|
||
|
||
/* Notify the class driver of the suspend event */
|
||
|
||
if (g_usbdev.driver)
|
||
{
|
||
CLASS_SUSPEND(g_usbdev.driver, &g_usbdev.usbdev);
|
||
}
|
||
|
||
/* Disable suspend event interrupts; enable wakeup event interrupt */
|
||
|
||
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);
|
||
|
||
/* Notify the class driver of the resume event */
|
||
|
||
if (g_usbdev.driver)
|
||
{
|
||
CLASS_RESUME(g_usbdev.driver, &g_usbdev.usbdev);
|
||
}
|
||
}
|
||
|
||
/****************************************************************************
|
||
* 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, FAR void *arg)
|
||
{
|
||
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)
|
||
{
|
||
FAR struct avr_ep_s *privep = (FAR struct avr_ep_s *)ep;
|
||
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(ep->eplog != 0 && 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))
|
||
{
|
||
DEBUGASSERT(privep->epin != 0);
|
||
uecfg0x |= AVR_DIR_IN;
|
||
ueienx = (1 << RXOUTE);
|
||
}
|
||
else
|
||
{
|
||
DEBUGASSERT(privep->epin == 0);
|
||
}
|
||
|
||
/* Handle banking (this needs to be revisited... Always double bank?) */
|
||
|
||
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;
|
||
}
|
||
|
||
/* Save the new max packet size and reset endpoint status */
|
||
|
||
privep->ep.maxpacket = maxpacket;
|
||
privep->stalled = 0;
|
||
|
||
/* Enable interrupts as appropriate for this endpoint */
|
||
|
||
UEIENX |= ueienx;
|
||
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_FEATURES
|
||
if (!ep)
|
||
{
|
||
usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INVALIDPARMS), 0);
|
||
return -EINVAL;
|
||
}
|
||
#endif
|
||
usbtrace(TRACE_EPDISABLE, privep->ep.eplog);
|
||
|
||
flags = enter_critical_section();
|
||
|
||
/* Disable the endpoint */
|
||
|
||
avr_epreset(privep, -ESHUTDOWN);
|
||
g_usbdev.stalled = true;
|
||
|
||
leave_critical_section(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_FEATURES
|
||
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 *)kmm_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_FEATURES
|
||
if (!ep || !req)
|
||
{
|
||
usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INVALIDPARMS), 0);
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
usbtrace(TRACE_EPFREEREQ, ((FAR struct avr_ep_s *)ep)->ep.eplog);
|
||
kmm_free(privreq);
|
||
}
|
||
|
||
/****************************************************************************
|
||
* Name: avr_epallocbuffer
|
||
*
|
||
* Description:
|
||
* Allocate an I/O buffer
|
||
*
|
||
****************************************************************************/
|
||
|
||
#ifdef CONFIG_USBDEV_DMA
|
||
static void *avr_epallocbuffer(FAR struct usbdev_ep_s *ep, unsigned bytes)
|
||
{
|
||
usbtrace(TRACE_EPALLOCBUFFER, privep->ep.eplog);
|
||
|
||
#ifdef CONFIG_USBDEV_DMAMEMORY
|
||
return usbdev_dma_alloc(bytes);
|
||
#else
|
||
return kmm_malloc(bytes);
|
||
#endif
|
||
}
|
||
#endif
|
||
|
||
/****************************************************************************
|
||
* Name: avr_epfreebuffer
|
||
*
|
||
* Description:
|
||
* Free an I/O buffer
|
||
*
|
||
****************************************************************************/
|
||
|
||
#ifdef CONFIG_USBDEV_DMA
|
||
static void avr_epfreebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf)
|
||
{
|
||
usbtrace(TRACE_EPFREEBUFFER, privep->ep.eplog);
|
||
|
||
#ifdef CONFIG_USBDEV_DMAMEMORY
|
||
usbdev_dma_free(buf);
|
||
#else
|
||
kmm_free(buf);
|
||
#endif
|
||
}
|
||
#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_FEATURES
|
||
if (!req || !req->callback || !req->buf || !ep)
|
||
{
|
||
usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INVALIDPARMS), 0);
|
||
uinfo("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 = enter_critical_section();
|
||
|
||
/* 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);
|
||
}
|
||
}
|
||
}
|
||
|
||
leave_critical_section(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_FEATURES
|
||
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 = enter_critical_section();
|
||
avr_cancelrequests(privep, -ESHUTDOWN);
|
||
leave_critical_section(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 = enter_critical_section();
|
||
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;
|
||
}
|
||
leave_critical_section(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)
|
||
{
|
||
FAR struct avr_ep_s *privep;
|
||
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_FEATURES
|
||
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 = enter_critical_section();
|
||
|
||
/* 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)
|
||
{
|
||
/* Initialize the endpoint structure */
|
||
|
||
privep = &g_usbdev.eplist[epndx];
|
||
memset(privep, 0, sizeof(struct avr_ep_s));
|
||
|
||
privep->ep.ops = &g_epops;
|
||
privep->ep.eplog = epndx;
|
||
privep->ep.maxpacket = (epndx == 1) ? 256 : 64;
|
||
|
||
/* Mark the IN/OUT endpoint no longer available */
|
||
|
||
g_usbdev.epavail &= ~epmask;
|
||
if (in)
|
||
{
|
||
g_usbdev.epinset |= epmask;
|
||
privep->epin = 1;
|
||
}
|
||
else
|
||
{
|
||
g_usbdev.epoutset |= epmask;
|
||
privep->epin = 0;
|
||
}
|
||
|
||
/* And return the pointer to the standard endpoint structure */
|
||
|
||
leave_critical_section(flags);
|
||
return &privep->ep;
|
||
}
|
||
}
|
||
|
||
/* Shouldn't get here */
|
||
|
||
leave_critical_section(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 = enter_critical_section();
|
||
epmask = (1 << privep->ep.eplog);
|
||
g_usbdev.epavail |= epmask;
|
||
g_usbdev.epinset &= ~epmask;
|
||
g_usbdev.epoutset &= ~epmask;
|
||
leave_critical_section(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 = enter_critical_section();
|
||
avr_genwakeup();
|
||
leave_critical_section(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_FEATURES
|
||
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)
|
||
{
|
||
usbtrace(TRACE_DEVINIT, 0);
|
||
|
||
/* 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);
|
||
|
||
/* Attach USB controller general interrupt handler */
|
||
|
||
if (irq_attach(AT90USB_IRQ_USBGEN, avr_geninterrupt, NULL) != 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, NULL) != 0)
|
||
{
|
||
usbtrace(TRACE_DEVERROR(AVR_TRACEERR_IRQREGISTRATION), AT90USB_IRQ_USBEP);
|
||
goto errout;
|
||
}
|
||
|
||
/* Shutdown the USB interface to put it in a known initial state */
|
||
|
||
avr_usbshutdown();
|
||
|
||
/* Select USB device mode */
|
||
|
||
UHWCON |= (1 << UIMOD);
|
||
|
||
/* Reset the interface to force re-enumeration (the reset operation
|
||
* enables interrupts.
|
||
*/
|
||
|
||
avr_usbreset();
|
||
|
||
/* 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 = enter_critical_section();
|
||
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();
|
||
leave_critical_section(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_FEATURES
|
||
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_FEATURES
|
||
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 = enter_critical_section();
|
||
avr_genvbus();
|
||
leave_critical_section(flags);
|
||
}
|
||
#endif
|
||
|