* arch/avr/src/at90usb/at90usb_usbdev.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/spinlock.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 "avr_internal.h"
* Pre-processor Definitions
****************************************************************************/
* 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 "Unsupported CPU clock"
#endif
#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
#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
#define AVR_NENDPOINTS (7)
#define AVR_EP0 (0)
#define AVR_CTRLEP_SIZE (8)
#define AVR_ALL_EPS (0x7f)
#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)
#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)
#define avr_rqempty(ep) ((ep)->head == NULL)
#define avr_rqpeek(ep) ((ep)->head)
* Private Types
****************************************************************************/
struct avr_req_s
{
struct usbdev_req_s req;
struct avr_req_s *flink;
};
struct avr_ep_s
{
* structure so that it is possible to simply cast from struct usbdev_ep_s
* to struct avr_ep_s.
*/
struct usbdev_ep_s ep;
struct avr_req_s *head;
struct avr_req_s *tail;
struct avr_req_s *pending;
uint8_t stalled:1;
uint8_t epin:1;
};
struct avr_usbdev_s
{
* structure so that it is possible to simply cast from struct usbdev_s to
* struct avr_usbdev_s.
*/
struct usbdev_s usbdev;
struct usbdevclass_driver_s *driver;
uint8_t ep0buf[64];
uint8_t paddr;
uint8_t epavail;
uint8_t epinset;
uint8_t epoutset;
uint8_t stalled:1;
uint8_t selfpowered:1;
uint8_t paddrset:1;
uint8_t attached:1;
#ifdef CONFIG_USBDEV_SELFPOWERED
uint8_t wkupen:1;
#endif
volatile bool connected;
struct avr_ep_s eplist[AVR_NENDPOINTS];
};
* Private Function Prototypes
****************************************************************************/
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);
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_ep_nsend(FAR struct avr_ep_s *privep,
FAR struct avr_req_s *privreq);
static inline int avr_ep_nrecv(FAR struct avr_ep_s *privep,
FAR struct usbdev_req_s *req);
static int avr_ep_in_queue(FAR struct avr_ep_s *privep);
static int avr_ep_out_queue(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);
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);
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);
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
****************************************************************************/
* 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 (; ; )
{
* 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;
}
if (!(g_usbdev.connected))
{
return -ENODEV;
}
else if ((UECONX & (1 << STALLRQ)) != 0)
{
return -EAGAIN;
}
* assume here to be at a one millisecond rate.
*/
if ((UDINT & (1 << SOFI)) != 0)
{
* milliseconds
*/
UDINT &= ~(1 << SOFI);
if ((timeout--) > 0)
{
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;
* set when a new OUT data is received
*/
while (buflen)
{
* 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;
}
* wait for the selected endpoint to be ready for an TX transfer
*/
avr_txready();
while (buflen > 0)
{
if (UEBCX == AVR_CTRLEP_SIZE)
{
* following bank. TXINI must be cleared to acknowledge the
* interrupt.
*/
usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EP0FIFOFULL), regval);
regval = UEINTX;
regval &= ~(1 << TXINI);
UEINTX = regval;
regval &= ~(1 << FIFOCON);
UEINTX = regval;
break;
}
UEDATX = *ptr++;
buflen--;
}
* 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_ep_nsend
*
* Description:
* Perform a TX transfer for Endpoint N
*
****************************************************************************/
static inline int avr_ep_nsend(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;
DEBUGASSERT((UEINTX & (1 << RWAL)) != 0);
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;
UENUM = privep->ep.eplog;
ret = avr_fifoready(AVR_TIMEOUT_LONG);
if (ret != OK)
{
usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EP0FIFOFULL), regval);
return -EAGAIN;
}
* (including zero-length packets)
*/
do
{
* 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)
{
UEDATX = *buffer++;
len++;
}
* packets if dual buffering is enabled).
*
* Clear any pending TXINI interrupts
*/
UEINT &= ~(1 << privep->ep.eplog);
* length packet). TXINI must always be cleared BEFORE clearing
* FIFOCON.
*/
regval = UEINTX;
regval &= ~(1 << TXINI);
UEINTX = regval;
regval &= ~(1 << FIFOCON);
UEINTX = regval;
req->xfrd += len;
buffer += len;
buflen -= len;
usbtrace(TRACE_WRITE(privep->ep.eplog), privreq->req.xfrd);
* 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)
{
more = true;
}
else if (zlp)
{
* last transfer was an exact multiple of the packet size.
*/
if (len && (len & pktmask) == 0)
{
* the packet size. A ZLP is needed.
*/
more = true;
}
else
{
* finished with this request.
*/
more = false;
}
}
else
{
more = false;
}
* 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)))
{
* then handle the remainder of the transfer asynchronously in the
* TXINI interrupt handler.
*/
if (ret == -ETIME)
{
UEIENX |= (1 << TXINE);
}
* available.
*/
usbtrace(TRACE_DEVERROR(AVR_TRACEERR_INFIFO), regval);
return ret;
}
}
while (more);
return OK;
}
* Name: avr_ep_nrecv
*
* Description:
* Perform an RX transfer for Endpoint N
*
****************************************************************************/
static inline int avr_ep_nrecv(FAR struct avr_ep_s *privep,
FAR struct usbdev_req_s *req)
{
FAR uint8_t *buffer;
uint8_t regval;
int ret;
buffer = req->buf;
req->xfrd = 0;
ret = avr_fifoready(AVR_TIMEOUT_LONG);
if (ret != OK)
{
usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EP0FIFONOTREADY), ret);
return ret;
}
while (req->xfrd < req->len)
{
* receive FIFO
*/
if (((UEINTX & (1 << RWAL)) == 0))
{
* 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;
}
else
{
*buffer++ = UEDATX;
req->xfrd++;
}
}
* 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_ep_in_queue
*
* 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_ep_in_queue(FAR struct avr_ep_s *privep)
{
FAR struct avr_req_s *privreq;
int ret = OK;
usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_EPIN), 0);
if (privep->pending)
{
privreq = privep->pending;
}
else
{
privreq = avr_rqdequeue(privep);
privep->pending = privreq;
}
if (privreq)
{
ret = avr_ep_nsend(privep, privreq);
* finished within this interrupt. We just need to exit with the
* pending transfer in place.
*/
if (ret == OK || ret != -ETIME)
{
* 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_ep_out_queue
*
* 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_ep_out_queue(FAR struct avr_ep_s *privep)
{
FAR struct avr_req_s *privreq;
int ret = OK;
usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_EPOUT), 0);
privreq = avr_rqdequeue(privep);
if (privreq)
{
ret = avr_ep_nrecv(privep, &privreq->req);
* 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)
{
* the callback.
*/
bool stalled = privep->stalled;
if (privep->ep.eplog == AVR_EP0)
{
privep->stalled = g_usbdev.stalled;
}
privreq->req.result = result;
privreq->req.callback(&privep->ep, &privreq->req);
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)
{
if (privep->pending)
{
FAR struct avr_req_s *privreq = privep->pending;
privep->pending = NULL;
UENUM = privep->ep.eplog;
UEIENX &= ~(1 << TXINE);
usbtrace(TRACE_COMPLETE(privep->ep.eplog), privreq->req.xfrd);
avr_reqcomplete(privep, privreq, status);
}
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;
if (epno == AVR_EP0)
{
return &g_usbdev.eplist[0];
}
for (i = 1; i < AVR_NENDPOINTS; i++)
{
privep = &g_usbdev.eplist[i];
if (epno == privep->ep.eplog)
{
return privep;
}
}
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)
{
ret = CLASS_SETUP(g_usbdev.driver, &g_usbdev.usbdev, ctrl, NULL, 0);
}
if (ret < 0)
{
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;
UENUM = AVR_EP0;
UECONX |= (1 << EPEN);
UECFG1X = 0;
UECFG0X = AVR_EPTYPE_CTRL;
UECFG1X = (1 << ALLOC) | AVR_EPSIZE_8;
regval = UESTA0X;
if ((regval & (1 << CFGOK)) == 0)
{
usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EP0CFGBAD), regval);
return -EINVAL;
}
* 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;
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;
UEINT &= ~(1 << epno);
UENUM = epno;
UEIENX = 0;
UEINTX = 0;
UECFG1X &= ~(1 << ALLOC);
UECONX &= ~(1 << EPEN);
avr_cancelrequests(privep, status);
privep->stalled = false;
}
* Name: avr_usbreset
*
* Description:
* Reset Usb engine
*
****************************************************************************/
static void avr_usbreset(void)
{
uint8_t epno;
uint8_t regval;
USBCON &= ~((1 << VBUSTE) | (1 << IDTE));
UDIEN = 0;
USBINT = 0;
UDINT = 0;
g_usbdev.connected = false;
#ifdef CONFIG_USBDEV_SELFPOWERED
g_usbdev.wkupen = false;
#endif
for (epno = 0; epno < AVR_NENDPOINTS; epno++)
{
struct avr_ep_s *privep = &g_usbdev.eplist[epno];
avr_epreset(privep, -ESHUTDOWN);
}
* then accept any new configurations.
*/
if (g_usbdev.driver)
{
CLASS_DISCONNECT(g_usbdev.driver, &g_usbdev.usbdev);
}
PLLCSR = USB_PLL_PSC;
PLLCSR |= (1 << PLLE);
while ((PLLCSR & (1 << PLOCK)) == 0);
regval = USBCON;
USBCON = (regval & ~(1 << USBE));
USBCON = (regval | (1 << USBE));
#ifndef CONFIG_USB_DISABLE_PADREGULATOR
UHWCON |= (1 << UVREGE);
#endif
USBCON &= ~(1 << FRZCLK);
#ifdef CONFIG_USB_LOWSPEED
UDCON |= (1 << LSM);
#else
UDCON &= ~(1 << LSM);
#endif
avr_setaddress(0);
avr_ep0configure();
USBCON |= (1 << VBUSTE);
* 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);
UDIEN |= ((1 << SUSPE) | (1 << EORSTE));
}
* Name: avr_usbshutdown
*
* Description:
* Shutdown the USB interface and put the hardware in a known state
*
****************************************************************************/
void avr_usbshutdown(void)
{
if (g_usbdev.driver)
{
CLASS_DISCONNECT(g_usbdev.driver, &g_usbdev.usbdev);
}
UDCON |= (1 << DETACH);
USBCON &= ~((1 << VBUSTE) | (1 << IDTE));
UDIEN = 0;
USBINT = 0;
UDINT = 0;
g_usbdev.connected = false;
#ifdef CONFIG_USBDEV_REMOTEWAKEUP
g_usbdev.wkupen = false;
#endif
USBCON &= ~(1 << USBE);
PLLCSR = 0;
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);
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);
avr_cancelrequests(&g_usbdev.eplist[AVR_EP0], -EPROTO);
avr_cancelrequests(&g_usbdev.eplist[AVR_EP0], -EPROTO);
g_usbdev.eplist[AVR_EP0].stalled = false;
g_usbdev.eplist[AVR_EP0].stalled = false;
g_usbdev.stalled = false;
ptr = (uint8_t *)&ctrl;
for (i = 0; i < USB_SIZEOF_CTRLREQ; i++)
{
*ptr++ = UEDATX;
}
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);
if ((ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD)
{
avr_dispatchrequest(&ctrl);
}
else
{
* device controller driver; pass what is left to the class driver.
*/
switch (ctrl.req)
{
case USB_REQ_GETSTATUS:
{
* 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
{
if (privep->stalled)
{
g_usbdev.ep0buf[0] =
(1 << USB_FEATURE_ENDPOINTHALT);
}
else
{
g_usbdev.ep0buf[0] = 0;
}
g_usbdev.ep0buf[1] = 0;
avr_ep0send(g_usbdev.ep0buf, 2);
}
}
break;
case USB_REQ_RECIPIENT_DEVICE:
{
if (index == 0)
{
usbtrace(TRACE_INTDECODE(
AVR_TRACEINTID_DEVGETSTATUS), 0);
#ifdef CONFIG_USBDEV_SELFPOWERED
g_usbdev.ep0buf[0] =
(1 << USB_FEATURE_SELFPOWERED);
#else
g_usbdev.ep0buf[0] = 0;
#endif
#ifdef CONFIG_USBDEV_REMOTEWAKEUP
if (g_usbdev.wkupen)
{
status |= (1 << USB_FEATURE_REMOTEWAKEUP);
}
#endif
g_usbdev.ep0buf[1] = 0;
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:
{
* 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:
{
* 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:
{
* 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)
{
avr_setaddress(ctrl.value[0]);
}
else
{
usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADSETADDRESS), 0);
g_usbdev.stalled = true;
}
}
break;
case USB_REQ_GETDESCRIPTOR:
* and index index: 0 or language ID; len: descriptor len; data =
* descriptor.
*/
case USB_REQ_SETDESCRIPTOR:
{
* 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:
{
* 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:
{
* 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:
* interface; len: 1; data = alt interface
*/
case USB_REQ_SETINTERFACE:
{
* setting index: interface; len: 0; data = none
*/
usbtrace(TRACE_INTDECODE(AVR_TRACEINTID_GETSETIF), 0);
avr_dispatchrequest(&ctrl);
}
break;
case USB_REQ_SYNCHFRAME:
{
* 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)
{
if ((UEINT & (1 << AVR_EP0)) != 0)
{
UEINT &= ~(1 << AVR_EP0);
UENUM = AVR_EP0;
if ((UEINTX & (1 << RXSTPI)) != 0)
{
avr_ep0setup();
}
UEINTX &= ~(1 << RXSTPI);
}
}
* Name: avr_ep_ninterrupt
*
* Description:
* USB endpoint/pipe IN interrupt handler
*
****************************************************************************/
static inline void avr_ep_ninterrupt(void)
{
struct avr_ep_s *privep;
uint8_t ueint = UEINT & (g_usbdev.epoutset | g_usbdev.epinset);
uint8_t epno;
uint8_t mask;
for (epno = 1, mask = 2; epno < AVR_NENDPOINTS &&
ueint != 0; epno++, mask <<= 1)
{
if ((ueint & mask) != 0)
{
ueint &= ~mask;
UENUM = epno;
privep = &g_usbdev.eplist[epno];
if (privep->epin)
{
UEINTX &= ~(1 << TXINI);
if ((UEIENX & (1 << TXINE)) != 0)
{
UEINT &= ~(1 << epno);
avr_ep_in_queue(privep);
}
}
else
{
* interrupts enabled?
*/
if ((UEIENX & (1 << RXOUTE)) != 0)
{
UEINT &= ~(1 << epno);
avr_ep_out_queue(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);
avr_ep0interrupt();
avr_ep_ninterrupt();
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);
vbus = ((USBSTA & (1 << VBUS)) != 0);
if (!g_usbdev.connected && vbus)
{
avr_usbreset();
g_usbdev.connected = true;
}
else if (g_usbdev.connected && !vbus)
{
avr_cancelall(-ENODEV);
UDCON |= (1 << DETACH);
* This reduces the power consumption. Clear to enable the clock
* inputs.
*/
USBCON |= (1 << FRZCLK);
PLLCSR = 0;
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);
if (g_usbdev.driver)
{
CLASS_SUSPEND(g_usbdev.driver, &g_usbdev.usbdev);
}
UDIEN &= ~(1 << SUSPE);
UDIEN |= (1 << WAKEUPE);
* detection is still active).
*/
USBCON |= (1 << FRZCLK);
PLLCSR = 0;
}
* Name: avr_genwakeup
*
* Description:
* Resume from suspend mode.
*
****************************************************************************/
static void avr_genwakeup(void)
{
usbtrace(TRACE_INTENTRY(AVR_TRACEINTID_WAKEUP), UDIEN);
PLLCSR = USB_PLL_PSC;
PLLCSR |= (1 << PLLE);
while ((PLLCSR & (1 << PLOCK)) == 0);
USBCON &= ~(1 << FRZCLK);
UDIEN &= ~(1 << WAKEUPE);
UDIEN |= (1 << SUSPE);
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);
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);
avr_ep0configure();
g_usbdev.stalled = false;
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);
if ((USBINT & (1 << VBUSTI)) != 0 && (USBCON & (1 << VBUSTE)) != 0)
{
USBINT &= ~(1 << VBUSTI);
avr_genvbus();
}
if ((UDINT & (1 << SUSPI)) != 0 && (UDIEN & (1 << SUSPE)) != 0)
{
UDINT &= ~(1 << SUSPI);
avr_gensuspend();
}
if ((UDINT & (1 << WAKEUPI)) != 0 && (UDIEN & (1 << WAKEUPE)) != 0)
{
UDINT &= ~(1 << WAKEUPI);
avr_genwakeup();
}
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);
uecfg0x = 0;
uecfg1x = (1 << ALLOC);
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;
}
if (USB_ISEPIN(desc->addr))
{
DEBUGASSERT(privep->epin != 0);
uecfg0x |= AVR_DIR_IN;
ueienx = (1 << RXOUTE);
}
else
{
DEBUGASSERT(privep->epin == 0);
}
uecfg1x |= AVR_DOUBLE_BANK;
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;
}
UENUM = ep->eplog;
UECONX |= (1 << EPEN);
UECFG1X = 0;
UECFG0X = uecfg0x;
UECFG1X = uecfg1x;
regval = UESTA0X;
if ((regval & (1 << CFGOK)) == 0)
{
usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EPCFGBAD), regval);
return -EINVAL;
}
privep->ep.maxpacket = maxpacket;
privep->stalled = 0;
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();
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 = 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;
}
req->result = -EINPROGRESS;
req->xfrd = 0;
flags = enter_critical_section();
if (g_usbdev.stalled)
{
ret = -EBUSY;
}
else if (privreq->req.len == 0)
{
usbtrace(TRACE_DEVERROR(AVR_TRACEERR_EPNULLPACKET), 0);
ret = -EINVAL;
}
else
{
avr_rqenqueue(privep, privreq);
* For these, we may have to initiate the next transfer.
*/
if (privep->epin)
{
usbtrace(TRACE_INREQQUEUED(privep->ep.eplog), privreq->req.len);
* not and if the FIFO is available now, then start the next
* IN transfer.
*/
if (!privep->pending && avr_fifoready(AVR_TIMEOUT_NONE) == OK)
{
ret = avr_ep_in_queue(privep);
}
}
else
{
usbtrace(TRACE_OUTREQQUEUED(privep->ep.eplog), privreq->req.len);
* then go get it
*/
if (avr_fifoready(AVR_TIMEOUT_NONE) == OK)
{
ret = avr_ep_out_queue(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);
* 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;
flags = enter_critical_section();
if (resume)
{
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);
epno = USB_EPNO(epno);
if (epno > 0)
{
* requested 'logical' endpoint.
*/
#ifdef CONFIG_DEBUG_FEATURES
if (epno >= AVR_NENDPOINTS)
{
usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BADEPNO), (uint16_t)epno);
return NULL;
}
#endif
* remove all of the candidate endpoints from the bitset except for the
* the IN/OUT pair for this logical address.
*/
epset &= (1 << epno);
}
if (epset)
{
flags = enter_critical_section();
for (epndx = 1; epndx < AVR_NENDPOINTS; epndx++)
{
epmask = 1 << epndx;
if ((epset & epmask) != 0)
{
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;
g_usbdev.epavail &= ~epmask;
if (in)
{
g_usbdev.epinset |= epmask;
privep->epin = 1;
}
else
{
g_usbdev.epoutset |= epmask;
privep->epin = 0;
}
leave_critical_section(flags);
return &privep->ep;
}
}
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);
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)
{
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: avr_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 avr_usbinitialize(void)
{
usbtrace(TRACE_DEVINIT, 0);
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);
if (irq_attach(AT90USB_IRQ_USBGEN, avr_geninterrupt, NULL) != 0)
{
usbtrace(TRACE_DEVERROR(AVR_TRACEERR_IRQREGISTRATION),
AT90USB_IRQ_USBGEN);
goto errout;
}
if (irq_attach(AT90USB_IRQ_USBEP, avr_epinterrupt, NULL) != 0)
{
usbtrace(TRACE_DEVERROR(AVR_TRACEERR_IRQREGISTRATION),
AT90USB_IRQ_USBEP);
goto errout;
}
avr_usbshutdown();
UHWCON |= (1 << UIMOD);
* enables interrupts.
*/
avr_usbreset();
USBCON |= (1 << OTGPADE);
avr_pullup(&g_usbdev.usbdev, false);
return;
errout:
avr_usbuninitialize();
}
* Name: avr_usbuninitialize
****************************************************************************/
void avr_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);
}
flags = enter_critical_section();
avr_pullup(&g_usbdev.usbdev, false);
g_usbdev.usbdev.speed = USB_SPEED_UNKNOWN;
irq_detach(AT90USB_IRQ_USBGEN);
irq_detach(AT90USB_IRQ_USBEP);
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
g_usbdev.driver = driver;
ret = CLASS_BIND(driver, &g_usbdev.usbdev);
if (ret)
{
usbtrace(TRACE_DEVERROR(AVR_TRACEERR_BINDFAILED),
(uint16_t) - ret);
g_usbdev.driver = NULL;
}
else
{
* 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
CLASS_UNBIND(driver, &g_usbdev.usbdev);
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