nuttx/arch/arm/src/samd2l2/sam_usb.c
Xiang Xiao 2e54df0f35 Don't include assert.h from public header file
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2021-06-03 08:36:03 -07:00

4142 lines
120 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/****************************************************************************
* arch/arm/src/samd2l2/sam_usb.c
*
* Copyright (C) 2015 Filament - www.filament.com
* Copyright (C) 2015 Offcode Ltd. All rights reserved.
* Author: Janne Rosberg <janne.rosberg@offcode.fi>
*
* This driver is derived from the SAM34 UDP driver:
* Copyright (C) 2014 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.
*
****************************************************************************/
/****************************************************************************
* USB core features:
*
* - Compatible with the USB 2.1 specification
* - USB Embedded Host and Device mode
* - Supports full (12Mbit/s) and low (1.5Mbit/s) speed communication
* - Supports Link Power Management (LPM-L1) protocol
* - On-chip transceivers with built-in pull-ups and pull-downs
* - On-Chip USB serial resistors
* - 1kHz SOF clock available on external pin
*
* Device mode
* - Supports 8 IN endpoints and 8 OUT endpoints
* No endpoint size limitations
* Built-in DMA with multi-packet and dual bank for all endpoints
* Supports feedback endpoint
* Supports crystal less clock
*
* Host mode
* - Supports 8 physical pipes
* No pipe size limitations
* Supports multiplexed virtual pipe on one physical pipe to allow an
* unlimited USB tree
* Built-in DMA with multi-packet support and dual bank for all pipes
* Supports feedback endpoint
* Supports the USB 2.0 Phase-locked SOFs feature
*
****************************************************************************/
/****************************************************************************
* WIP NOTES:
*
* DS 38.1 (859)
* To maximize throughput, an endpoint can be configured for ping-pong
* operation.
* When this is done the input and output endpoint with the same address are
* used in the same direction. The CPU or DMA Controller can then read/write
* one data buffer while the USB module writes/reads from the other buffer.
* This gives double buffered communication.
*
****************************************************************************/
/****************************************************************************
* 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/irq.h>
#include <nuttx/usb/usb.h>
#include <nuttx/usb/usbdev.h>
#include <nuttx/usb/usbdev_trace.h>
#include <arch/board/board.h>
#include "arm_arch.h"
#include "arm_internal.h"
#include "sam_gclk.h"
#include "chip.h"
#include "sam_port.h"
#include "sam_pinmap.h"
#include "sam_usb.h"
#include "sam_fuses.h"
#include "sam_periphclks.h"
#if defined(CONFIG_USBHOST) && defined(CONFIG_SAMD2L2_USB)
# error USBHOST mode not yet implemented!
#endif
#if defined(CONFIG_USBDEV) && defined(CONFIG_SAMD2L2_USB)
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
#ifndef CONFIG_USBDEV_EP0_MAXSIZE
# define CONFIG_USBDEV_EP0_MAXSIZE 64
#endif
/* Extremely detailed register debug that you would normally never want
* enabled.
*/
#ifndef CONFIG_DEBUG_USB
# undef CONFIG_SAMD2L2_USB_REGDEBUG
#endif
/* Driver Definitions *******************************************************/
#define EP0 (0)
#define SAM_EPSET_ALL (0xff) /* All endpoints */
#define SAM_EPSET_NOTEP0 (0xfe) /* All endpoints except EP0 */
#define SAM_EP_BIT(ep) (1 << (ep))
#define SAM_EP0_MAXPACKET (CONFIG_USBDEV_EP0_MAXSIZE) /* EP0 Max. packet size */
#define SAM_MAX_MULTIPACKET_SIZE (0x3fff)
/* Request queue operations *************************************************/
#define sam_rqempty(q) ((q)->head == NULL)
#define sam_rqpeek(q) ((q)->head)
/* USB trace ****************************************************************/
/* Trace error codes */
#define SAM_TRACEERR_ALLOCFAIL 0x0001
#define SAM_TRACEERR_BADCLEARFEATURE 0x0002
#define SAM_TRACEERR_BADDEVGETSTATUS 0x0003
#define SAM_TRACEERR_BADEPGETSTATUS 0x0004
#define SAM_TRACEERR_BADEOBSTATE 0x0005
#define SAM_TRACEERR_BADEPNO 0x0006
#define SAM_TRACEERR_BADEPTYPE 0x0007
#define SAM_TRACEERR_BADGETCONFIG 0x0008
#define SAM_TRACEERR_BADGETSETDESC 0x0009
#define SAM_TRACEERR_BADGETSTATUS 0x000a
#define SAM_TRACEERR_BADSETADDRESS 0x000b
#define SAM_TRACEERR_BADSETCONFIG 0x000c
#define SAM_TRACEERR_BADSETFEATURE 0x000d
#define SAM_TRACEERR_BINDFAILED 0x000e
#define SAM_TRACEERR_DISPATCHSTALL 0x000f
#define SAM_TRACEERR_DRIVER 0x0010
#define SAM_TRACEERR_DRIVERREGISTERED 0x0011
#define SAM_TRACEERR_EP0SETUPOUTSIZE 0x0012
#define SAM_TRACEERR_EP0SETUPSTALLED 0x0013
#define SAM_TRACEERR_EPOUTNULLPACKET 0x0014
#define SAM_TRACEERR_EPRESERVE 0x0015
#define SAM_TRACEERR_INVALIDCTRLREQ 0x0016
#define SAM_TRACEERR_INVALIDPARMS 0x0017
#define SAM_TRACEERR_IRQREGISTRATION 0x0018
#define SAM_TRACEERR_NOTCONFIGURED 0x0019
#define SAM_TRACEERR_REQABORTED 0x001a
#define SAM_TRACEERR_RXDATABKERR 0x001b
#define SAM_TRACEERR_TXCOMPERR 0x001c
#define SAM_TRACEERR_UNSUPPEPTYPE 0x001d
/* Trace interrupt codes */
#define SAM_TRACEINTID_INTERRUPT 0x0001
#define SAM_TRACEINTID_PENDING 0x0002
#define SAM_TRACEINTID_PENDING_EP 0x0003
#define SAM_TRACEINTID_SUSPEND 0x0004
#define SAM_TRACEINTID_SOF 0x0005
#define SAM_TRACEINTID_EORST 0x0006
#define SAM_TRACEINTID_WAKEUP 0x0007
#define SAM_TRACEINTID_EORSM 0x0008
#define SAM_TRACEINTID_UPRSM 0x0009
#define SAM_TRACEINTID_RAMACER 0x000a
#define SAM_TRACEINTID_LPMNYET 0x000b
#define SAM_TRACEINTID_LPMSUSP 0x000c
#define SAM_TRACEINTID_EPNO 0x000d
#define SAM_TRACEINTID_EPINTFLAGS 0x000e
#define SAM_TRACEINTID_EPTRCPT0 0x000f
#define SAM_TRACEINTID_EPTRCPT1 0x0010
#define SAM_TRACEINTID_EPTRFAIL0 0x0011
#define SAM_TRACEINTID_EPTRFAIL1 0x0012
#define SAM_TRACEINTID_EPRXSTP 0x0013
#define SAM_TRACEINTID_EPSTALL0 0x0014
#define SAM_TRACEINTID_EPSTALL1 0x0015
#define SAM_TRACEINTID_EPINQEMPTY 0x0016
#define SAM_TRACEINTID_EPOUTQEMPTY 0x0017
#define SAM_TRACEINTID_EP0SETUPOUT 0x0018
#define SAM_TRACEINTID_EP0SETUPIN 0x0019
#define SAM_TRACEINTID_EP0SETUPSETADDRESS 0x001a
#define SAM_TRACEINTID_NOSTDREQ 0x001b
#define SAM_TRACEINTID_GETSTATUS 0x001c
#define SAM_TRACEINTID_DEVGETSTATUS 0x001d
#define SAM_TRACEINTID_IFGETSTATUS 0x001e
#define SAM_TRACEINTID_CLEARFEATURE 0x001f
#define SAM_TRACEINTID_SETFEATURE 0x0020
#define SAM_TRACEINTID_GETSETDESC 0x0021
#define SAM_TRACEINTID_GETCONFIG 0x0022
#define SAM_TRACEINTID_SETCONFIG 0x0023
#define SAM_TRACEINTID_GETSETIF 0x0024
#define SAM_TRACEINTID_SYNCHFRAME 0x0025
#define SAM_TRACEINTID_DISPATCH 0x0026
#define SAM_TRACEINTID_ADDRESSED 0x0027
#define SAM_TRACEINTID_EPCONF 0x0028
#define SAM_TRACEINTID_EPINTEN 0x0029
#define SAM_TRACEINTID_EP0WRSTATUS 0x002a
#define SAM_TRACEINTID_EPTRCPT0_LEN 0x002b
/* Ever-present MIN and MAX macros */
#ifndef MIN
# define MIN(a,b) (a < b ? a : b)
#endif
#ifndef MAX
# define MAX(a,b) (a > b ? a : b)
#endif
/* Byte ordering in host-based values */
#ifdef CONFIG_ENDIAN_BIG
# define LSB 1
# define MSB 0
#else
# define LSB 0
# define MSB 1
#endif
/****************************************************************************
* Private Type Definitions
****************************************************************************/
/* State of an endpoint */
enum sam_epstate_e
{
/* --- All Endpoints --- */
USB_EPSTATE_DISABLED = 0, /* Endpoint is disabled */
USB_EPSTATE_STALLED, /* Endpoint is stalled */
USB_EPSTATE_IDLE, /* Endpoint is idle (i.e. ready for
* transmission) */
USB_EPSTATE_SENDING, /* Endpoint is sending data */
USB_EPSTATE_RXSTOPPED, /* OUT endpoint is stopped waiting for a read
* request */
/* --- Endpoint 0 Only --- */
USB_EPSTATE_EP0DATAOUT, /* Endpoint 0 is receiving SETUP OUT data */
USB_EPSTATE_EP0STATUSIN, /* Endpoint 0 is sending SETUP status */
USB_EPSTATE_EP0ADDRESS /* Address change is pending completion of
* status */
};
/* The overall state of the device */
enum sam_devstate_e
{
USB_DEVSTATE_SUSPENDED = 0, /* The device is currently suspended */
USB_DEVSTATE_POWERED, /* Host is providing +5V through the USB cable */
USB_DEVSTATE_DEFAULT, /* Device has been reset */
USB_DEVSTATE_ADDRESSED, /* The device has been given an address on the bus */
USB_DEVSTATE_CONFIGURED /* A valid configuration has been selected. */
};
/* The result of EP0 SETUP processing */
enum sam_ep0setup_e
{
USB_EP0SETUP_SUCCESS = 0, /* The SETUP was handle without incident */
USB_EP0SETUP_DISPATCHED, /* The SETUP was forwarded to the class driver */
USB_EP0SETUP_ADDRESS, /* A new device address is pending */
USB_EP0SETUP_STALL /* An error occurred */
};
union wb_u
{
uint16_t w;
uint8_t b[2];
};
/* A container for a request so that the request may be retained in a list */
struct sam_req_s
{
struct usbdev_req_s req; /* Standard USB request */
struct sam_req_s *flink; /* Supports a singly linked list */
uint16_t inflight; /* Number of TX bytes tansmitting or
* number of RX bytes we are waiting */
};
/* The head of a queue of requests */
struct sam_rqhead_s
{
struct sam_req_s *head; /* Requests are added to the head of the list */
struct sam_req_s *tail; /* Requests are removed from the tail of the list */
};
/* This is the internal representation of an endpoint */
struct sam_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 sam_ep_s.
*/
struct usbdev_ep_s ep; /* Standard endpoint structure */
/* SAMD2L2-specific fields */
struct sam_usbdev_s *dev; /* Reference to private driver data */
struct sam_rqhead_s reqq; /* Read/write request queue */
struct sam_rqhead_s pendq; /* Write requests pending stall sent */
struct usbdev_epdesc_s *descb[2]; /* Pointers to this endpoints descriptors */
volatile uint8_t epstate; /* State of the endpoint (see enum sam_epstate_e) */
uint8_t stalled:1; /* true: Endpoint is stalled */
uint8_t pending:1; /* true: IN Endpoint stall is pending */
uint8_t halted:1; /* true: Endpoint feature halted */
uint8_t zlpsent:1; /* Zero length packet has been sent */
uint8_t txbusy:1; /* Write request queue is busy (recursion avoidance kludge) */
uint8_t rxactive:1; /* read request is active (for top of queue) */
};
struct sam_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 sam_usbdev_s.
*/
struct usbdev_s usbdev;
/* The bound device class driver */
struct usbdevclass_driver_s *driver;
/* USB-specific fields */
struct usb_ctrlreq_s ctrl; /* Last EP0 request */
uint8_t devstate; /* State of the device (see enum
* sam_devstate_e) */
uint8_t prevstate; /* Previous state of the device
* before SUSPEND */
uint8_t devaddr; /* Assigned device address */
uint8_t selfpowered:1; /* 1: Device is self powered */
uint16_t epavail; /* Bitset of available endpoints */
/* The endpoint list */
struct sam_ep_s eplist[SAM_USB_NENDPOINTS];
/* Endpoint descriptors 2 banks for each endpoint */
struct usbdev_epdesc_s ep_descriptors[SAM_USB_NENDPOINTS * 2];
/* EP0 data buffer. For data that is included in an EP0 SETUP OUT
* transaction. In this case, no request is in place from the class
* driver and the incoming data is caught in this buffer. The size
* of valid data in the buffer is given by ctrlreg.len[]. For the
* case of EP0 SETUP IN transaction, the normal request mechanism is
* used and the class driver provides the buffering.
*/
uint8_t ep0out[SAM_EP0_MAXPACKET];
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* Register operations ******************************************************/
#ifdef CONFIG_SAMD2L2_USB_REGDEBUG
static void sam_printreg(uintptr_t regaddr, uint32_t regval, bool iswrite);
static void sam_checkreg(uintptr_t regaddr, uint32_t regval, bool iswrite);
static uint32_t sam_getreg32(uintptr_t regaddr);
static void sam_putreg32(uint32_t regval, uintptr_t regaddr);
static uint32_t sam_getreg16(uintptr_t regaddr);
static void sam_putreg16(uint16_t regval, uintptr_t regaddr);
static uint32_t sam_getreg8(uintptr_t regaddr);
static void sam_putreg8(uint8_t regval, uintptr_t regaddr);
static void sam_dumpep(struct sam_usbdev_s *priv, uint8_t epno);
#else
static inline uint32_t sam_getreg32(uintptr_t regaddr);
static inline void sam_putreg32(uint32_t regval, uintptr_t regaddr);
static inline uint32_t sam_getreg16(uintptr_t regaddr);
static inline void sam_putreg16(uint16_t regval, uintptr_t regaddr);
static inline uint32_t sam_getreg8(uintptr_t regaddr);
static inline void sam_putreg8(uint8_t regval, uintptr_t regaddr);
# define sam_dumpep(priv,epno)
#endif
/* Suspend/Resume Helpers ***************************************************/
#if 0 /* Not used */
static void sam_suspend(struct sam_usbdev_s *priv);
#endif
static void sam_resume(struct sam_usbdev_s *priv);
/* Request Helpers **********************************************************/
static struct sam_req_s *
sam_req_dequeue(struct sam_rqhead_s *queue);
static void sam_req_enqueue(struct sam_rqhead_s *queue,
struct sam_req_s *req);
static void sam_req_complete(struct sam_ep_s *privep, int16_t result);
static void sam_req_wrsetup(struct sam_usbdev_s *priv,
struct sam_ep_s *privep, struct sam_req_s *privreq);
static int sam_req_write(struct sam_usbdev_s *priv,
struct sam_ep_s *privep);
static int sam_req_read(struct sam_usbdev_s *priv,
struct sam_ep_s *privep, uint16_t recvsize);
static void sam_req_cancel(struct sam_ep_s *privep, int16_t status);
/* Interrupt level processing ***********************************************/
static void sam_ep0_ctrlread(struct sam_usbdev_s *priv);
static void sam_ep0_wrstatus(struct sam_usbdev_s *priv,
const uint8_t *buffer, size_t buflen);
static void sam_ep0_dispatch(struct sam_usbdev_s *priv);
static void sam_setdevaddr(struct sam_usbdev_s *priv, uint8_t value);
static void sam_ep0_setup(struct sam_usbdev_s *priv);
static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno);
static int sam_usb_interrupt(int irq, void *context, void *arg);
/* Endpoint helpers *********************************************************/
static void sam_ep_reset(struct sam_usbdev_s *priv, uint8_t epno);
static void sam_epset_reset(struct sam_usbdev_s *priv, uint16_t epset);
static int sam_ep_stall(struct sam_ep_s *privep);
static int sam_ep_resume(struct sam_ep_s *privep);
static inline struct sam_ep_s *
sam_ep_reserve(struct sam_usbdev_s *priv, uint8_t epset);
static inline void
sam_ep_unreserve(struct sam_usbdev_s *priv,
struct sam_ep_s *privep);
static inline bool
sam_ep_reserved(struct sam_usbdev_s *priv, int epno);
static int sam_ep_configure_internal(struct sam_ep_s *privep,
const struct usb_epdesc_s *desc);
/* Endpoint operations ******************************************************/
static int sam_ep_configure(struct usbdev_ep_s *ep,
const struct usb_epdesc_s *desc, bool last);
static int sam_ep_disable(struct usbdev_ep_s *ep);
static struct usbdev_req_s *
sam_ep_allocreq(struct usbdev_ep_s *ep);
static void sam_ep_freereq(struct usbdev_ep_s *ep,
struct usbdev_req_s *);
static int sam_ep_submit(struct usbdev_ep_s *ep,
struct usbdev_req_s *req);
static int sam_ep_cancel(struct usbdev_ep_s *ep,
struct usbdev_req_s *req);
static int sam_ep_stallresume(struct usbdev_ep_s *ep, bool resume);
/* USB device controller operations *****************************************/
static struct usbdev_ep_s *
sam_allocep(struct usbdev_s *dev, uint8_t epno, bool in,
uint8_t eptype);
static void sam_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep);
static int sam_getframe(struct usbdev_s *dev);
static int sam_wakeup(struct usbdev_s *dev);
static int sam_selfpowered(struct usbdev_s *dev, bool selfpowered);
static int sam_pullup(struct usbdev_s *dev, bool enable);
/* Initialization/Reset *****************************************************/
static void sam_reset(struct sam_usbdev_s *priv);
static void sam_enableclks(void);
static void sam_disableclks(void);
static void sam_hw_setup(struct sam_usbdev_s *priv);
static void sam_sw_setup(struct sam_usbdev_s *priv);
static void sam_hw_shutdown(struct sam_usbdev_s *priv);
static void sam_sw_shutdown(struct sam_usbdev_s *priv);
/****************************************************************************
* 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 sam_usbdev_s g_usbd;
static const struct usbdev_epops_s g_epops =
{
.configure = sam_ep_configure,
.disable = sam_ep_disable,
.allocreq = sam_ep_allocreq,
.freereq = sam_ep_freereq,
#ifdef CONFIG_USBDEV_DMA
.allocbuffer = sam_ep_allocbuffer,
.freebuffer = sam_ep_freebuffer,
#endif
.submit = sam_ep_submit,
.cancel = sam_ep_cancel,
.stall = sam_ep_stallresume,
};
static const struct usbdev_ops_s g_devops =
{
.allocep = sam_allocep,
.freeep = sam_freeep,
.getframe = sam_getframe,
.wakeup = sam_wakeup,
.selfpowered = sam_selfpowered,
.pullup = sam_pullup,
};
/* This describes endpoint 0 */
static const struct usb_epdesc_s g_ep0desc =
{
.len = USB_SIZEOF_EPDESC,
.type = USB_DESC_TYPE_ENDPOINT,
.addr = EP0,
.attr = USB_EP_ATTR_XFER_CONTROL,
.mxpacketsize =
{
64, 0
},
.interval = 0
};
/* Device error strings that may be enabled for more desciptive USB trace
* output.
*/
#ifdef CONFIG_USBDEV_TRACE_STRINGS
const struct trace_msg_t g_usb_trace_strings_deverror[] =
{
TRACE_STR(SAM_TRACEERR_ALLOCFAIL),
TRACE_STR(SAM_TRACEERR_BADCLEARFEATURE),
TRACE_STR(SAM_TRACEERR_BADDEVGETSTATUS),
TRACE_STR(SAM_TRACEERR_BADEPGETSTATUS),
TRACE_STR(SAM_TRACEERR_BADEOBSTATE),
TRACE_STR(SAM_TRACEERR_BADEPNO),
TRACE_STR(SAM_TRACEERR_BADEPTYPE),
TRACE_STR(SAM_TRACEERR_BADGETCONFIG),
TRACE_STR(SAM_TRACEERR_BADGETSETDESC),
TRACE_STR(SAM_TRACEERR_BADGETSTATUS),
TRACE_STR(SAM_TRACEERR_BADSETADDRESS),
TRACE_STR(SAM_TRACEERR_BADSETCONFIG),
TRACE_STR(SAM_TRACEERR_BADSETFEATURE),
TRACE_STR(SAM_TRACEERR_BINDFAILED),
TRACE_STR(SAM_TRACEERR_DISPATCHSTALL),
TRACE_STR(SAM_TRACEERR_DRIVER),
TRACE_STR(SAM_TRACEERR_DRIVERREGISTERED),
TRACE_STR(SAM_TRACEERR_EP0SETUPOUTSIZE),
TRACE_STR(SAM_TRACEERR_EP0SETUPSTALLED),
TRACE_STR(SAM_TRACEERR_EPOUTNULLPACKET),
TRACE_STR(SAM_TRACEERR_EPRESERVE),
TRACE_STR(SAM_TRACEERR_INVALIDCTRLREQ),
TRACE_STR(SAM_TRACEERR_INVALIDPARMS),
TRACE_STR(SAM_TRACEERR_IRQREGISTRATION),
TRACE_STR(SAM_TRACEERR_NOTCONFIGURED),
TRACE_STR(SAM_TRACEERR_REQABORTED),
TRACE_STR(SAM_TRACEERR_RXDATABKERR),
TRACE_STR(SAM_TRACEERR_TXCOMPERR),
TRACE_STR(SAM_TRACEERR_UNSUPPEPTYPE),
TRACE_STR_END
};
#endif
/* Interrupt event strings that may be enabled for more desciptive USB trace
* output.
*/
#ifdef CONFIG_USBDEV_TRACE_STRINGS
const struct trace_msg_t g_usb_trace_strings_intdecode[] =
{
TRACE_STR(SAM_TRACEINTID_INTERRUPT),
TRACE_STR(SAM_TRACEINTID_PENDING),
TRACE_STR(SAM_TRACEINTID_PENDING_EP),
TRACE_STR(SAM_TRACEINTID_SUSPEND),
TRACE_STR(SAM_TRACEINTID_SOF),
TRACE_STR(SAM_TRACEINTID_EORST),
TRACE_STR(SAM_TRACEINTID_WAKEUP),
TRACE_STR(SAM_TRACEINTID_EORSM),
TRACE_STR(SAM_TRACEINTID_UPRSM),
TRACE_STR(SAM_TRACEINTID_RAMACER),
TRACE_STR(SAM_TRACEINTID_LPMNYET),
TRACE_STR(SAM_TRACEINTID_LPMSUSP),
TRACE_STR(SAM_TRACEINTID_EPNO),
TRACE_STR(SAM_TRACEINTID_EPINTFLAGS),
TRACE_STR(SAM_TRACEINTID_EPTRCPT0),
TRACE_STR(SAM_TRACEINTID_EPTRCPT1),
TRACE_STR(SAM_TRACEINTID_EPTRFAIL0),
TRACE_STR(SAM_TRACEINTID_EPTRFAIL1),
TRACE_STR(SAM_TRACEINTID_EPRXSTP),
TRACE_STR(SAM_TRACEINTID_EPSTALL0),
TRACE_STR(SAM_TRACEINTID_EPSTALL1),
TRACE_STR(SAM_TRACEINTID_EPINQEMPTY),
TRACE_STR(SAM_TRACEINTID_EPOUTQEMPTY),
TRACE_STR(SAM_TRACEINTID_EP0SETUPOUT),
TRACE_STR(SAM_TRACEINTID_EP0SETUPIN),
TRACE_STR(SAM_TRACEINTID_EP0SETUPSETADDRESS),
TRACE_STR(SAM_TRACEINTID_NOSTDREQ),
TRACE_STR(SAM_TRACEINTID_GETSTATUS),
TRACE_STR(SAM_TRACEINTID_DEVGETSTATUS),
TRACE_STR(SAM_TRACEINTID_IFGETSTATUS),
TRACE_STR(SAM_TRACEINTID_CLEARFEATURE),
TRACE_STR(SAM_TRACEINTID_SETFEATURE),
TRACE_STR(SAM_TRACEINTID_GETSETDESC),
TRACE_STR(SAM_TRACEINTID_GETCONFIG),
TRACE_STR(SAM_TRACEINTID_SETCONFIG),
TRACE_STR(SAM_TRACEINTID_GETSETIF),
TRACE_STR(SAM_TRACEINTID_SYNCHFRAME),
TRACE_STR(SAM_TRACEINTID_DISPATCH),
TRACE_STR(SAM_TRACEINTID_ADDRESSED),
TRACE_STR(SAM_TRACEINTID_EPCONF),
TRACE_STR(SAM_TRACEINTID_EPINTEN),
TRACE_STR(SAM_TRACEINTID_EP0WRSTATUS),
TRACE_STR(SAM_TRACEINTID_EPTRCPT0_LEN),
TRACE_STR_END
};
#endif
/****************************************************************************
* Private Private Functions
****************************************************************************/
/****************************************************************************
* Name: sam_printreg
*
* Description:
* Print the SAMD2L2 USB register access
*
****************************************************************************/
#ifdef CONFIG_SAMD2L2_USB_REGDEBUG
static void sam_printreg(uintptr_t regaddr, uint32_t regval, bool iswrite)
{
uinfo("%p%s%08x\n", regaddr, iswrite ? "<-" : "->", regval);
}
#endif
/****************************************************************************
* Name: sam_checkreg
*
* Description:
* Check if it is time to output debug information for accesses to a
* SAMD2L2 USB registers
*
****************************************************************************/
#ifdef CONFIG_SAMD2L2_USB_REGDEBUG
static void sam_checkreg(uintptr_t regaddr, uint32_t regval, bool iswrite)
{
static uintptr_t prevaddr = 0;
static uint32_t preval = 0;
static uint32_t count = 0;
static bool prevwrite = false;
/* Is this the same value that we read from/wrote to the same register
* last time? Are we polling the register? If so, suppress the output.
*/
if (regaddr == prevaddr && regval == preval && prevwrite == iswrite)
{
/* Yes.. Just increment the count */
count++;
}
else
{
/* No this is a new address or value or operation. Were there any
* duplicate accesses before this one?
*/
if (count > 0)
{
/* Yes.. Just one? */
if (count == 1)
{
/* Yes.. Just one */
sam_printreg(prevaddr, preval, prevwrite);
}
else
{
/* No.. More than one. */
uinfo("[repeats %d more times]\n", count);
}
}
/* Save the new address, value, count, and operation for next time */
prevaddr = regaddr;
preval = regval;
count = 0;
prevwrite = iswrite;
/* Show the new register access */
sam_printreg(regaddr, regval, iswrite);
}
}
#endif
/****************************************************************************
* Name: sam_getreg32
*
* Description:
* Get the contents of an 32-bit SAMD2L2 USB register
*
****************************************************************************/
#ifdef CONFIG_SAMD2L2_USB_REGDEBUG
static uint32_t sam_getreg32(uintptr_t regaddr)
{
/* Read the value from the register */
uint32_t regval = getreg32(regaddr);
/* Check if we need to print this value */
sam_checkreg(regaddr, regval, false);
return regval;
}
#else
static inline uint32_t sam_getreg32(uintptr_t regaddr)
{
return getreg32(regaddr);
}
#endif
/****************************************************************************
* Name: sam_putreg32
*
* Description:
* Set the contents of an 32-bit SAMD2L2 USB register to a value
*
****************************************************************************/
#ifdef CONFIG_SAMD2L2_USB_REGDEBUG
static void sam_putreg32(uint32_t regval, uintptr_t regaddr)
{
/* Check if we need to print this value */
sam_checkreg(regaddr, regval, true);
/* Write the value */
putreg32(regval, regaddr);
}
#else
static inline void sam_putreg32(uint32_t regval, uintptr_t regaddr)
{
putreg32(regval, regaddr);
}
#endif
/****************************************************************************
* Name: sam_getreg16
*
* Description:
* Get the contents of an 16-bit SAMD2L2 USB register
*
****************************************************************************/
#ifdef CONFIG_SAMD2L2_USB_REGDEBUG
static uint32_t sam_getreg16(uintptr_t regaddr)
{
/* Read the value from the register */
uint32_t regval = getreg16(regaddr);
/* Check if we need to print this value */
sam_checkreg(regaddr, regval, false);
return regval;
}
#else
static inline uint32_t sam_getreg16(uintptr_t regaddr)
{
return getreg16(regaddr);
}
#endif
/****************************************************************************
* Name: sam_putreg16
*
* Description:
* Set the contents of an 16-bit SAMD2L2 USB register to a value
*
****************************************************************************/
#ifdef CONFIG_SAMD2L2_USB_REGDEBUG
static void sam_putreg16(uint16_t regval, uintptr_t regaddr)
{
/* Check if we need to print this value */
sam_checkreg(regaddr, regval, true);
/* Write the value */
putreg16(regval, regaddr);
}
#else
static inline void sam_putreg16(uint16_t regval, uintptr_t regaddr)
{
putreg16(regval, regaddr);
}
#endif
/****************************************************************************
* Name: sam_getreg8
*
* Description:
* Get the contents of an 8-bit SAMD2L2 USB register
*
****************************************************************************/
#ifdef CONFIG_SAMD2L2_USB_REGDEBUG
static uint32_t sam_getreg8(uintptr_t regaddr)
{
/* Read the value from the register */
uint32_t regval = getreg8(regaddr);
/* Check if we need to print this value */
sam_checkreg(regaddr, regval, false);
return regval;
}
#else
static inline uint32_t sam_getreg8(uintptr_t regaddr)
{
return getreg8(regaddr);
}
#endif
/****************************************************************************
* Name: sam_putreg8
*
* Description:
* Set the contents of an 8-bit SAMD2L2 USB register to a value
*
****************************************************************************/
#ifdef CONFIG_SAMD2L2_USB_REGDEBUG
static void sam_putreg8(uint8_t regval, uintptr_t regaddr)
{
/* Check if we need to print this value */
sam_checkreg(regaddr, regval, true);
/* Write the value */
putreg8(regval, regaddr);
}
#else
static inline void sam_putreg8(uint8_t regval, uintptr_t regaddr)
{
putreg8(regval, regaddr);
}
#endif
/****************************************************************************
* Name: sam_dumpep
****************************************************************************/
#if defined(CONFIG_SAMD2L2_USB_REGDEBUG) && defined(CONFIG_DEBUG_USB)
static void sam_dumpep(struct sam_usbdev_s *priv, uint8_t epno)
{
/* Global Registers */
uinfo("Global Registers:\n");
uinfo(" CTRLB: %04x\n",
sam_getreg16(SAM_USBDEV_CTRLB));
uinfo(" FNUM: %04x\n",
sam_getreg16(SAM_USBDEV_FNUM));
uinfo(" DADD: %02x\n",
sam_getreg8(SAM_USBDEV_DADD));
uinfo(" INTEN: %04x\n",
sam_getreg16(SAM_USBDEV_INTENSET));
uinfo(" STATUS: %02x\n",
sam_getreg8(SAM_USBDEV_STATUS));
uinfo(" INTFLAG: %04x\n",
sam_getreg16(SAM_USBDEV_INTFLAG));
uinfo(" EPCFG[%d]: %02x\n",
epno, sam_getreg8(SAM_USBDEV_EPCFG(epno)));
uinfo("EPSTATUS[%d]: %02x\n",
epno, sam_getreg8(SAM_USBDEV_EPSTATUS(epno)));
}
#endif
/****************************************************************************
* Name: sam_req_dequeue
****************************************************************************/
static struct sam_req_s *sam_req_dequeue(struct sam_rqhead_s *queue)
{
struct sam_req_s *ret = queue->head;
if (ret)
{
queue->head = ret->flink;
if (!queue->head)
{
queue->tail = NULL;
}
ret->flink = NULL;
}
return ret;
}
/****************************************************************************
* Name: sam_req_enqueue
****************************************************************************/
static void sam_req_enqueue(struct sam_rqhead_s *queue,
struct sam_req_s *req)
{
req->flink = NULL;
if (!queue->head)
{
queue->head = req;
queue->tail = req;
}
else
{
queue->tail->flink = req;
queue->tail = req;
}
}
/****************************************************************************
* Name: sam_req_complete
****************************************************************************/
static void sam_req_complete(struct sam_ep_s *privep, int16_t result)
{
struct sam_req_s *privreq;
irqstate_t flags;
/* Remove the completed request at the head of the endpoint request list */
flags = enter_critical_section();
privreq = sam_req_dequeue(&privep->reqq);
leave_critical_section(flags);
if (privreq)
{
/* Save the result in the request structure */
privreq->req.result = result;
/* Callback to the request completion handler */
privreq->flink = NULL;
privreq->req.callback(&privep->ep, &privreq->req);
/* mark endpoint ready to next transmission */
privep->epstate = USB_EPSTATE_IDLE;
privep->zlpsent = false;
}
}
/****************************************************************************
* Name: sam_req_wrsetup
*
* Description:
* Process the next queued write request.
*
****************************************************************************/
static void sam_req_wrsetup(struct sam_usbdev_s *priv,
struct sam_ep_s *privep,
struct sam_req_s *privreq)
{
const uint8_t *buf;
uint8_t epno;
int nbytes;
uint32_t packetsize;
/* Get the unadorned endpoint number */
epno = USB_EPNO(privep->ep.eplog);
/* Get the number of bytes remaining to be sent. */
DEBUGASSERT(privreq->req.xfrd < privreq->req.len);
nbytes = privreq->req.len - privreq->req.xfrd;
/* Either send the maxpacketsize(multi) or all of the remaining data in
* the request.
*/
if (nbytes >= SAM_MAX_MULTIPACKET_SIZE)
{
nbytes = SAM_MAX_MULTIPACKET_SIZE;
}
/* This is the new number of bytes "in-flight" */
privreq->inflight = nbytes;
usbtrace(TRACE_WRITE(USB_EPNO(privep->ep.eplog)), nbytes);
/* The new buffer pointer is the start of the buffer plus the number of
* bytes successfully transferred plus the number of bytes previously
* "in-flight".
*/
buf = privreq->req.buf + privreq->req.xfrd;
/* setup TX transfer using ep configured maxpacket size */
priv->eplist[epno].descb[1]->addr = (uint32_t) buf;
packetsize = priv->eplist[epno].descb[1]->pktsize;
packetsize &= ~USBDEV_PKTSIZE_BCNT_MASK;
packetsize &= ~USBDEV_PKTSIZE_MPKTSIZE_MASK;
packetsize |= USBDEV_PKTSIZE_BCNT(nbytes);
/* also set automatic ZLP sending if requested on req */
if (privreq->req.flags & USBDEV_REQFLAGS_NULLPKT)
{
packetsize |= USBDEV_PKTSIZE_AUTOZLP;
}
priv->eplist[epno].descb[1]->pktsize = packetsize;
/* Indicate that we are in the sending state
* This indication will be need in interrupt processing (TRCPT1)
* in order to properly terminate the request.
*/
privep->epstate = USB_EPSTATE_SENDING;
/* Set BK1RDY to notify the USB hardware that TX data is ready on
* descriptor bank1. We will be notified that the descriptor has been
* transmitted by the USB device when TRCPT1 in the endpoint's EPINTFLAG
* register has been set.
*/
sam_putreg8(USBDEV_EPSTATUS_BK1RDY, SAM_USBDEV_EPSTATUSSET(epno));
}
/****************************************************************************
* Name: sam_req_write
*
* Description:
* Process the next queued write request. This function is called in one
* of three contexts:
* (1) When the endpoint is IDLE and a new write request is submitted
* (with interrupts disabled),
* (2) from TRCPT1 interrupt handling when the current Tx transfer
* completes
* (3) when resuming a stalled IN or control endpoint.
*
* Calling rules:
*
* The transfer state must IDLE
*
* When a request is queued, the request 'len' is the number of bytes
* to transfer and 'xfrd' and 'inflight' must be zero.
*
* When this function starts a transfer it will update the request
* 'inflight' field to indicate the size of the transfer.
*
* When the transfer completes, the 'inflight' field must hold the
* number of bytes that have completed the transfer. This function will
* update 'xfrd' with the new size of the transfer.
*
****************************************************************************/
static int sam_req_write(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
{
struct sam_req_s *privreq;
uint8_t epno;
int bytesleft;
/* Get the unadorned endpoint number */
epno = USB_EPNO(privep->ep.eplog);
/* We get here when an IN endpoint interrupt occurs. So now we know that
* there is no TX transfer in progress (epstate should be IDLE).
*/
DEBUGASSERT(privep->epstate == USB_EPSTATE_IDLE);
while (privep->epstate == USB_EPSTATE_IDLE)
{
/* Check the request from the head of the endpoint request queue */
privreq = sam_rqpeek(&privep->reqq);
if (!privreq)
{
/* There is no TX transfer in progress and no new pending TX
* requests to send.
*/
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPINQEMPTY), epno);
/* Was there a pending endpoint stall? */
if (privep->pending)
{
/* Yes... stall the endpoint now */
sam_ep_stall(privep);
}
return -ENOENT;
}
uinfo("epno=%d req=%p: len=%d xfrd=%d inflight=%d\n",
epno, privreq, privreq->req.len, privreq->req.xfrd,
privreq->inflight);
/* Handle any bytes in flight. */
privreq->req.xfrd += privreq->inflight;
privreq->inflight = 0;
/* Get the number of bytes left to be sent in the packet */
bytesleft = privreq->req.len - privreq->req.xfrd;
if (bytesleft > 0)
{
/* Perform the write operation. epstate will become SENDING. */
sam_req_wrsetup(priv, privep, privreq);
}
/* No data to send...
* This can happen on one of two ways:
* (1) The last packet sent was the final packet of a transfer.
* Or
* (2) called with a request packet that has len == 0
*
* len == 0 means that it is requested to send a zero length packet
* required by protocol
*/
else if ((privreq->req.len == 0) && !privep->zlpsent)
{
/* If we get here, we requested to send the zero length packet now.
*/
privep->epstate = USB_EPSTATE_SENDING;
privep->zlpsent = true;
privreq->inflight = 0;
usbtrace(TRACE_WRITE(epno), 0);
/* setup 0 length TX transfer */
priv->eplist[0].descb[1]->addr = (uint32_t) &priv->ep0out[0];
priv->eplist[0].descb[1]->pktsize &= ~USBDEV_PKTSIZE_BCNT_MASK;
priv->eplist[0].descb[1]->pktsize &= ~USBDEV_PKTSIZE_MPKTSIZE_MASK;
priv->eplist[0].descb[1]->pktsize |= USBDEV_PKTSIZE_BCNT(0);
sam_putreg8(USBDEV_EPSTATUS_BK1RDY, SAM_USBDEV_EPSTATUSSET(epno));
}
/* If all of the bytes were sent (including any final zero length
* packet) then we are finished with the request buffer and we can
* return the request buffer to the class driver. The state will
* remain IDLE only if nothing else was put in flight.
*
* Note that we will then loop to check to check the next queued
* write request.
*/
if (privep->epstate == USB_EPSTATE_IDLE)
{
/* Return the write request to the class driver. Set the txbusy
* bit to prevent being called recursively from any new submission
* generated by returning the write request.
*/
usbtrace(TRACE_COMPLETE(epno), privreq->req.xfrd);
DEBUGASSERT(privreq->req.len == privreq->req.xfrd);
privep->txbusy = true;
sam_req_complete(privep, OK);
privep->txbusy = false;
}
}
return OK;
}
/****************************************************************************
* Name: sam_req_read
*
* Description:
* Complete the last read request. full or partial.
* The USB core has transferred the data to user request buffer.
* return the completed read request to the class
* implementation, and try to start the next queued read request.
*
* REVISIT:
* This function is called in one of two contexts:
* The normal case is
* (1) When the endpoint is IDLE and a new read request is submitted
* (with interrupts disabled),
* (2) from interrupt handling when the current RX transfer completes.
* But there is also a special case
* (3) when the OUT endpoint is stopped because there are no
* available read requests.
*
* Calling rules:
*
* The transfer state must IDLE
*
* When a request is queued, the request 'len' is size of the request
* buffer. Any OUT request can be received that will fit in this
* buffer. 'xfrd' and 'inflight' in the request must be zero
* If sam_req_read() is called to start a new transfer, the recvsize
* parameter must be zero.
*
* When the transfer completes, the 'recvsize' is the number of bytes
* ready on req->buffer.
*
****************************************************************************/
static int sam_req_read(struct sam_usbdev_s *priv, struct sam_ep_s *privep,
uint16_t recvsize)
{
struct sam_req_s *privreq;
uint32_t packetsize;
int epno;
DEBUGASSERT(priv && privep && privep->epstate == USB_EPSTATE_IDLE);
/* Check the request from the head of the endpoint request queue */
epno = USB_EPNO(privep->ep.eplog);
do
{
/* Peek at the next read request in the request queue */
privreq = sam_rqpeek(&privep->reqq);
if (!privreq)
{
/* No read request to receive data */
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPOUTQEMPTY), epno);
/* When no read requests are pending no EP descriptors are set to
* ready. HW sends NAK to host if it tries to send something.
*/
privep->epstate = USB_EPSTATE_RXSTOPPED;
return -ENOENT;
}
uinfo("EP%d: req.len=%d xfrd=%d recvsize=%d\n",
epno, privreq->req.len, privreq->req.xfrd, recvsize);
/* Ignore any attempt to receive a zero length packet */
if (privreq->req.len == 0)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EPOUTNULLPACKET), 0);
sam_req_complete(privep, OK);
privreq = NULL;
}
/* complete read request with available data */
if ((privreq->inflight) && (recvsize != 0))
{
usbtrace(TRACE_READ(USB_EPNO(privep->ep.eplog)), recvsize);
/* Update the total number of bytes transferred */
privreq->req.xfrd += recvsize;
privreq->inflight = 0;
usbtrace(TRACE_COMPLETE(epno), privreq->req.xfrd);
sam_req_complete(privep, OK);
/* need to set recvsize to zero. When calling sam_req_complete()
* class driver could call submit() again and we have new request
* ready on next while() loop.
*/
privep->rxactive = false;
recvsize = 0;
privreq = NULL;
}
}
while (privreq == NULL);
DEBUGASSERT(recvsize == 0);
/* activate new read request from queue */
privep->rxactive = true;
privreq->req.xfrd = 0;
privreq->inflight = privreq->req.len;
priv->eplist[epno].descb[0]->addr = (uint32_t) privreq->req.buf;
packetsize = priv->eplist[epno].descb[0]->pktsize;
packetsize &= ~USBDEV_PKTSIZE_BCNT_MASK;
packetsize &= ~USBDEV_PKTSIZE_MPKTSIZE_MASK;
packetsize |= USBDEV_PKTSIZE_MPKTSIZE(privreq->inflight);
sam_putreg8(USBDEV_EPSTATUS_BK0RDY, SAM_USBDEV_EPSTATUSCLR(epno));
return OK;
}
/****************************************************************************
* Name: sam_req_cancel
****************************************************************************/
static void sam_req_cancel(struct sam_ep_s *privep, int16_t result)
{
/* Complete every queued request with the specified status */
while (!sam_rqempty(&privep->reqq))
{
usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)),
(sam_rqpeek(&privep->reqq))->req.xfrd);
sam_req_complete(privep, result);
}
}
/****************************************************************************
* Name: sam_ep_configure_internal
*
* Description:
* This is the internal implementation of the endpoint configuration logic
* and implements the endpoint configuration method of the usbdev_ep_s
* interface. As an internal interface, it will be used to configure
* endpoint 0 which is not available to the class implementation.
*
****************************************************************************/
static int sam_ep_configure_internal(struct sam_ep_s *privep,
const struct usb_epdesc_s *desc)
{
uint8_t epconf;
uint16_t maxpacket;
uint8_t epno;
uint8_t eptype;
uint8_t intflags;
uint32_t ephwsize;
bool dirin;
DEBUGASSERT(privep && privep->dev && desc);
uinfo("len: %02x type: %02x addr: %02x attr: %02x "
"maxpacketsize: %02x %02x interval: %02x\n",
desc->len, desc->type, desc->addr, desc->attr,
desc->mxpacketsize[0], desc->mxpacketsize[1],
desc->interval);
/* Decode the endpoint descriptor */
epno = USB_EPNO(desc->addr);
dirin = (desc->addr & USB_DIR_MASK) == USB_REQ_DIR_IN;
eptype = (desc->attr & USB_EP_ATTR_XFERTYPE_MASK);
maxpacket = GETUINT16(desc->mxpacketsize);
if (maxpacket <= 8)
{
ephwsize = USBDEV_PKTSIZE_SIZE_8B;
}
else if (maxpacket <= 16)
{
ephwsize = USBDEV_PKTSIZE_SIZE_16B;
}
else if (maxpacket <= 32)
{
ephwsize = USBDEV_PKTSIZE_SIZE_32B;
}
else if (maxpacket <= 64)
{
ephwsize = USBDEV_PKTSIZE_SIZE_64B;
}
else if ((maxpacket <= 128) && (eptype == USB_EP_ATTR_XFER_ISOC))
{
ephwsize = USBDEV_PKTSIZE_SIZE_128B;
}
else if ((maxpacket <= 256) && (eptype == USB_EP_ATTR_XFER_ISOC))
{
ephwsize = USBDEV_PKTSIZE_SIZE_256B;
}
else if ((maxpacket <= 512) && (eptype == USB_EP_ATTR_XFER_ISOC))
{
ephwsize = USBDEV_PKTSIZE_SIZE_512B;
}
else if ((maxpacket <= 1023) && (eptype == USB_EP_ATTR_XFER_ISOC))
{
ephwsize = USBDEV_PKTSIZE_SIZE_1023B;
}
else
{
return -EINVAL;
}
/* update endpoint descriptors to correct size */
privep->descb[0]->pktsize = ephwsize;
privep->descb[1]->pktsize = ephwsize;
/* Initialize the endpoint structure */
privep->ep.eplog = desc->addr; /* Includes direction */
privep->ep.maxpacket = maxpacket;
privep->epstate = USB_EPSTATE_IDLE;
/* get current config IN and OUT */
epconf = 0x00;
sam_putreg8(0x00, SAM_USBDEV_EPCFG(epno));
if (dirin)
{
/* Disable bank1 (IN) */
intflags = USBDEV_EPINT_TRCPT1 | USBDEV_EPINT_STALL1;
}
else
{
/* Disable bank0 (OUT) */
intflags = USBDEV_EPINT_TRCPT0 | USBDEV_EPINT_STALL0;
}
/* write back disabled config */
sam_putreg8(0x7e, SAM_USBDEV_EPINTENCLR(epno));
sam_putreg8(0x7e, SAM_USBDEV_EPINTFLAG(epno));
/* Re-configure and enable the endpoint */
switch (eptype)
{
case USB_EP_ATTR_XFER_CONTROL:
{
epconf = USBDEV_EPCCFG_EPTYPE0_CTRLOUT |
USBDEV_EPCCFG_EPTYPE1_CTRLIN;
/* Also enable IN interrupts */
intflags = USBDEV_EPINT_TRCPT0 | USBDEV_EPINT_STALL0;
intflags |= USBDEV_EPINT_TRCPT1 | USBDEV_EPINT_STALL1;
intflags |= USBDEV_EPINT_RXSTP;
sam_putreg8(USBDEV_EPSTATUS_BK0RDY, SAM_USBDEV_EPSTATUSSET(0));
sam_putreg8(USBDEV_EPSTATUS_BK1RDY, SAM_USBDEV_EPSTATUSCLR(0));
}
break;
#ifdef CONFIG_USBDEV_ISOCHRONOUS
case USB_EP_ATTR_XFER_ISOC:
if (dirin)
{
epconf |= USBDEV_EPCCFG_EPTYPE1_ISOCIN;
}
else
{
epconf |= USBDEV_EPCCFG_EPTYPE0_ISOCOUT;
}
break;
#endif
case USB_EP_ATTR_XFER_BULK:
if (dirin)
{
epconf |= USBDEV_EPCCFG_EPTYPE1_BULKIN;
}
else
{
epconf |= USBDEV_EPCCFG_EPTYPE0_BULKOUT;
}
break;
case USB_EP_ATTR_XFER_INT:
if (dirin)
{
epconf |= USBDEV_EPCCFG_EPTYPE1_INTIN;
}
else
{
epconf |= USBDEV_EPCCFG_EPTYPE0_INTOUT;
}
break;
default:
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADEPTYPE),
eptype >> USB_EP_ATTR_XFERTYPE_SHIFT);
return -EINVAL;
}
sam_putreg8(epconf, SAM_USBDEV_EPCFG(epno));
/* Enable endpoint interrupts */
sam_putreg8(intflags, SAM_USBDEV_EPINTENSET(epno));
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPCONF), epno << 8 | epconf);
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPINTEN), epno << 8 | intflags);
sam_dumpep(privep->dev, epno);
return OK;
}
/****************************************************************************
* Name: sam_ep_reserve
*
* Description:
* Find and un-reserved endpoint number and reserve it for the caller.
*
****************************************************************************/
static inline struct sam_ep_s *
sam_ep_reserve(struct sam_usbdev_s *priv, uint8_t epset)
{
struct sam_ep_s *privep = NULL;
irqstate_t flags;
int epndx = 0;
flags = enter_critical_section();
epset &= priv->epavail;
if (epset)
{
/* Select the lowest bit in the set of matching, available endpoints
* (skipping EP0)
*/
for (epndx = 1; epndx < SAM_USB_NENDPOINTS; epndx++)
{
uint8_t bit = SAM_EP_BIT(epndx);
if ((epset & bit) != 0)
{
/* Mark the endpoint no longer available */
priv->epavail &= ~bit;
/* And return the pointer to the standard endpoint structure */
privep = &priv->eplist[epndx];
break;
}
}
}
leave_critical_section(flags);
return privep;
}
/****************************************************************************
* Name: sam_ep_unreserve
*
* Description:
* The endpoint is no long in-used. It will be unreserved and can be
* re-used if needed.
*
****************************************************************************/
static inline void
sam_ep_unreserve(struct sam_usbdev_s *priv, struct sam_ep_s *privep)
{
irqstate_t flags = enter_critical_section();
priv->epavail |= SAM_EP_BIT(USB_EPNO(privep->ep.eplog));
leave_critical_section(flags);
}
/****************************************************************************
* Name: sam_ep_reserved
*
* Description:
* Check if the endpoint has already been allocated.
*
****************************************************************************/
static inline bool
sam_ep_reserved(struct sam_usbdev_s *priv, int epno)
{
return ((priv->epavail & SAM_EP_BIT(epno)) == 0);
}
/****************************************************************************
* Name: sam_ep_configure
*
* Description:
* This is the endpoint configuration method of the usbdev_ep_s interface.
*
****************************************************************************/
static int sam_ep_configure(struct usbdev_ep_s *ep,
const struct usb_epdesc_s *desc,
bool last)
{
struct sam_ep_s *privep = (struct sam_ep_s *)ep;
int ret;
/* Verify parameters. Endpoint 0 is not available at this interface */
#if defined(CONFIG_DEBUG_USB) || defined(CONFIG_USBDEV_TRACE)
uint8_t epno = USB_EPNO(desc->addr);
usbtrace(TRACE_EPCONFIGURE, (uint16_t)epno);
DEBUGASSERT(ep && desc && epno > 0 && epno < SAM_USB_NENDPOINTS);
DEBUGASSERT(epno == USB_EPNO(ep->eplog));
#endif
/* This logic is implemented in sam_ep_configure_internal */
ret = sam_ep_configure_internal(privep, desc);
/* If this was the last endpoint, then the class driver is fully
* configured.
*/
if (ret == OK && last)
{
struct sam_usbdev_s *priv = privep->dev;
/* Go to the configured state (we should have been in the addressed
* state)
*/
DEBUGASSERT(priv && priv->devstate == USB_DEVSTATE_ADDRESSED);
priv->devstate = USB_DEVSTATE_CONFIGURED;
}
return ret;
}
/****************************************************************************
* Name: sam_ep_disable
*
* Description:
* This is the disable() method of the USB device endpoint structure.
*
****************************************************************************/
static int sam_ep_disable(struct usbdev_ep_s *ep)
{
struct sam_ep_s *privep = (struct sam_ep_s *)ep;
struct sam_usbdev_s *priv;
irqstate_t flags;
uint8_t epno;
#ifdef CONFIG_DEBUG_USB
if (!ep)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0);
uerr("ERROR: ep=%p\n", ep);
return -EINVAL;
}
#endif
epno = USB_EPNO(ep->eplog);
usbtrace(TRACE_EPDISABLE, epno);
/* Reset the endpoint and cancel any ongoing activity */
flags = enter_critical_section();
priv = privep->dev;
sam_ep_reset(priv, epno);
/* Revert to the addressed-but-not-configured state */
sam_setdevaddr(priv, priv->devaddr);
leave_critical_section(flags);
return OK;
}
/****************************************************************************
* Name: sam_ep_allocreq
*
* Description:
* This is the allocreq() method of the USB device endpoint structure.
*
****************************************************************************/
static struct usbdev_req_s *sam_ep_allocreq(struct usbdev_ep_s *ep)
{
struct sam_req_s *privreq;
#ifdef CONFIG_DEBUG_USB
if (!ep)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0);
return NULL;
}
#endif
usbtrace(TRACE_EPALLOCREQ, USB_EPNO(ep->eplog));
privreq = (struct sam_req_s *)kmm_malloc(sizeof(struct sam_req_s));
if (!privreq)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_ALLOCFAIL), 0);
return NULL;
}
memset(privreq, 0, sizeof(struct sam_req_s));
return &privreq->req;
}
/****************************************************************************
* Name: sam_ep_freereq
*
* Description:
* This is the freereq() method of the USB device endpoint structure.
*
****************************************************************************/
static void sam_ep_freereq(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
{
struct sam_req_s *privreq = (struct sam_req_s *)req;
#ifdef CONFIG_DEBUG_USB
if (!ep || !req)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0);
return;
}
#endif
usbtrace(TRACE_EPFREEREQ, USB_EPNO(ep->eplog));
kmm_free(privreq);
}
#if 0 /* FIXME, not required, check... */
/****************************************************************************
* Name: sam_ep_allocbuffer
*
* Description:
* This is the allocbuffer() method of the USB device endpoint structure.
*
****************************************************************************/
#ifdef CONFIG_USBDEV_DMA
static void *sam_ep_allocbuffer(struct usbdev_ep_s *ep, uint16_t nbytes)
{
/* There is not special buffer allocation requirement */
return kumm_malloc(nbytes);
}
#endif
/****************************************************************************
* Name: sam_ep_freebuffer
*
* Description:
* This is the freebuffer() method of the USB device endpoint structure.
*
****************************************************************************/
#ifdef CONFIG_USBDEV_DMA
static void sam_ep_freebuffer(struct usbdev_ep_s *ep, void *buf)
{
/* There is not special buffer allocation requirement */
kumm_free(buf);
}
#endif
#endif
/****************************************************************************
* Name: sam_ep_submit
*
* Description:
* This is the submit() method of the USB device endpoint structure.
*
****************************************************************************/
static int sam_ep_submit(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
{
struct sam_req_s *privreq = (struct sam_req_s *)req;
struct sam_ep_s *privep = (struct sam_ep_s *)ep;
struct sam_usbdev_s *priv;
irqstate_t flags;
uint8_t epno;
int ret = OK;
#ifdef CONFIG_DEBUG_USB
if (!req || !req->callback || !req->buf || !ep)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0);
uerr("ERROR: req=%p callback=%p buf=%p ep=%p\n", req, req->callback,
req->buf, ep);
return -EINVAL;
}
#endif
usbtrace(TRACE_EPSUBMIT, USB_EPNO(ep->eplog));
priv = privep->dev;
#ifdef CONFIG_DEBUG_USB
if (!priv->driver)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_NOTCONFIGURED),
priv->usbdev.speed);
uerr("ERROR: driver=%p\n", priv->driver);
return -ESHUTDOWN;
}
#endif
/* Handle the request from the class driver */
epno = USB_EPNO(ep->eplog);
req->result = -EINPROGRESS;
req->xfrd = 0;
privreq->inflight = 0;
flags = enter_critical_section();
/* Handle IN (device-to-host) requests. NOTE: If the class device is
* using the bi-directional EP0, then we assume that they intend the EP0
* IN functionality (EP0 SETUP OUT data receipt does not use requests).
*/
if (USB_ISEPIN(ep->eplog) || epno == EP0)
{
/* Check if the endpoint is stalled (or there is a stall pending) */
if (privep->stalled || privep->pending)
{
/* Yes.. in this case, save the new they will get in a special
* "pending" they will get queue until the stall is cleared.
*/
uinfo("Pending stall clear\n");
sam_req_enqueue(&privep->pendq, privreq);
usbtrace(TRACE_INREQQUEUED(epno), req->len);
ret = OK;
}
else
{
/* Add the new request to the request queue for the IN endpoint */
sam_req_enqueue(&privep->reqq, privreq);
usbtrace(TRACE_INREQQUEUED(epno), req->len);
/* If the IN endpoint is IDLE and there is not write queue
* processing in progress, then transfer the data now.
*/
if (privep->epstate == USB_EPSTATE_IDLE && !privep->txbusy)
{
ret = sam_req_write(priv, privep);
}
}
}
/* Handle OUT (host-to-device) requests */
else
{
/* Add the new request to the request queue for the OUT endpoint */
sam_req_enqueue(&privep->reqq, privreq);
usbtrace(TRACE_OUTREQQUEUED(epno), req->len);
/* Check if we have stopped RX receipt due to lack of read
* requests. In that case we are not receiving anything from host.
* and HW sends NAK to host. see sam_req_read()
* so this "state" is actually not required (at least yet)
*/
if (privep->epstate == USB_EPSTATE_RXSTOPPED)
{
privep->epstate = USB_EPSTATE_IDLE;
}
/* start new read if no active yet */
if (!privep->rxactive)
{
ret = sam_req_read(priv, privep, 0);
}
}
leave_critical_section(flags);
return ret;
}
/****************************************************************************
* Name: sam_ep_cancel
****************************************************************************/
static int sam_ep_cancel(struct usbdev_ep_s *ep, struct usbdev_req_s *req)
{
struct sam_ep_s *privep = (struct sam_ep_s *)ep;
irqstate_t flags;
#ifdef CONFIG_DEBUG_USB
if (!ep || !req)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
usbtrace(TRACE_EPCANCEL, USB_EPNO(ep->eplog));
flags = enter_critical_section();
sam_req_cancel(privep, -EAGAIN);
leave_critical_section(flags);
return OK;
}
/****************************************************************************
* Name: sam_ep_stallresume
****************************************************************************/
static int sam_ep_stallresume(struct usbdev_ep_s *ep, bool resume)
{
struct sam_ep_s *privep;
uint8_t epno;
irqstate_t flags;
int ret;
#ifdef CONFIG_DEBUG_USB
if (!ep)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
/* Handle the resume condition */
privep = (struct sam_ep_s *)ep;
if (resume)
{
ret = sam_ep_resume(privep);
}
/* Handle the stall condition */
else
{
/* If this is an IN endpoint (and not EP0) and if there are queued
* write requests, then we cannot stall now. Perhaps this is a
* protocol stall. In that case, we will need to drain the write
* requests before sending the stall.
*/
flags = enter_critical_section();
epno = USB_EPNO(ep->eplog);
if (epno != 0 && USB_ISEPIN(ep->eplog))
{
/* Are there any unfinished write requests in the request queue? */
if (!sam_rqempty(&privep->reqq))
{
/* Just set a flag to indicate that the endpoint must be
* stalled on the next TRCPTx interrupt when the request
* queue becomes empty.
*/
privep->pending = true;
leave_critical_section(flags);
return OK;
}
}
/* Not an IN endpoint, endpoint 0, or no pending write requests.
* Stall the endpoint now.
*/
ret = sam_ep_stall(privep);
leave_critical_section(flags);
}
return ret;
}
/****************************************************************************
* Name: sam_allocep
*
* Description:
* This is the allocep() method of the USB device driver interface
*
****************************************************************************/
static struct usbdev_ep_s *sam_allocep(struct usbdev_s *dev, uint8_t epno,
bool in, uint8_t eptype)
{
struct sam_usbdev_s *priv = (struct sam_usbdev_s *)dev;
struct sam_ep_s *privep = NULL;
uint16_t epset = SAM_EPSET_NOTEP0;
usbtrace(TRACE_DEVALLOCEP, (uint16_t)epno);
#ifdef CONFIG_DEBUG_USB
if (!dev)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0);
return NULL;
}
#endif
/* 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. All of the other checks will still
* be performed.
*
* First, verify that the logical endpoint is in the range supported by
* by the hardware.
*/
if (epno >= SAM_USB_NENDPOINTS)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADEPNO), (uint16_t)epno);
return NULL;
}
/* 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 = SAM_EP_BIT(epno);
}
/* Check if the selected endpoint number is available */
privep = sam_ep_reserve(priv, epset);
if (!privep)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EPRESERVE), (uint16_t)epset);
return NULL;
}
return &privep->ep;
}
/****************************************************************************
* Name: sam_freeep
*
* Description:
* This is the freeep() method of the USB device driver interface
*
****************************************************************************/
static void sam_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep)
{
struct sam_usbdev_s *priv;
struct sam_ep_s *privep;
#ifdef CONFIG_DEBUG_USB
if (!dev || !ep)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0);
return;
}
#endif
priv = (struct sam_usbdev_s *)dev;
privep = (struct sam_ep_s *)ep;
usbtrace(TRACE_DEVFREEEP, (uint16_t)USB_EPNO(ep->eplog));
if (priv && privep)
{
/* Mark the endpoint as available */
sam_ep_unreserve(priv, privep);
}
}
/****************************************************************************
* Name: sam_getframe
*
* Description:
* This is the getframe() method of the USB device driver interface
*
****************************************************************************/
static int sam_getframe(struct usbdev_s *dev)
{
uint32_t regval;
uint16_t frameno;
#ifdef CONFIG_DEBUG_USB
if (!dev)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
/* Return the last frame number detected by the hardware */
regval = sam_getreg16(SAM_USBDEV_FNUM);
frameno = (regval & USBDEV_FNUM_MASK) >> USBDEV_FNUM_SHIFT;
usbtrace(TRACE_DEVGETFRAME, frameno);
return frameno;
}
/****************************************************************************
* Name: sam_wakeup
*
* Description:
* This is the wakeup() method of the USB device driver interface
*
****************************************************************************/
static int sam_wakeup(struct usbdev_s *dev)
{
struct sam_usbdev_s *priv = (struct sam_usbdev_s *)dev;
irqstate_t flags;
uint16_t regval;
usbtrace(TRACE_DEVWAKEUP, 0);
#ifdef CONFIG_DEBUG_USB
if (!dev)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
/* Resume normal operation */
flags = enter_critical_section();
sam_resume(priv);
/* Activate a remote wakeup. (aka upstream resume)
* Setting the Remote Wakeup bit in CTRLB.UPRSM starts the
* Remote Wake Up procedure.
*
* This will automatically be done by the controller after 5 ms of
* inactivity on the USB bus.
*
* When the controller sends the Upstream Resume INTFLAG.WAKEUP is set
* and INTFLAG.SUSPEND is cleared.
* The CTRLB.UPRSM is cleared at the end of the transmitting Upstream
* Resume.
*/
regval = sam_getreg16(SAM_USBDEV_CTRLB);
regval |= USBDEV_CTRLB_UPRSM;
sam_putreg16(regval, SAM_USBDEV_CTRLB);
leave_critical_section(flags);
return OK;
}
/****************************************************************************
* Name: sam_selfpowered
*
* Description:
* This is the selfpowered() method of the USB device driver interface
*
****************************************************************************/
static int sam_selfpowered(struct usbdev_s *dev, bool selfpowered)
{
struct sam_usbdev_s *priv = (struct sam_usbdev_s *)dev;
usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered);
#ifdef CONFIG_DEBUG_USB
if (!dev)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0);
return -ENODEV;
}
#endif
priv->selfpowered = selfpowered;
return OK;
}
/****************************************************************************
* Name: sam_suspend
****************************************************************************/
#if 0 /* Not used */
static void sam_suspend(struct sam_usbdev_s *priv)
{
/* Don't do anything if the device is already suspended */
if (priv->devstate != USB_DEVSTATE_SUSPENDED)
{
/* Notify the class driver of the suspend event */
if (priv->driver)
{
CLASS_SUSPEND(priv->driver, &priv->usbdev);
}
/* Switch to the Suspended state */
priv->prevstate = priv->devstate;
priv->devstate = USB_DEVSTATE_SUSPENDED;
/* Disable clocking to the USB peripheral */
sam_disableclks();
/* Let the board-specific logic know that we have entered the
* suspend state. This may trigger additional reduced power
* consumption measures.
*/
sam_usb_suspend((struct usbdev_s *)priv, false);
}
}
#endif
/****************************************************************************
* Name: sam_resume
****************************************************************************/
static void sam_resume(struct sam_usbdev_s *priv)
{
/* This function is called when either (1) a WAKEUP interrupt is received
* from the host PC, or (2) the class device implementation calls the
* wakeup() method.
*/
/* Don't do anything if the device was not suspended */
if (priv->devstate == USB_DEVSTATE_SUSPENDED)
{
/* Revert to the previous state */
priv->devstate = priv->prevstate;
/* Restore clocking to the USB peripheral */
sam_enableclks();
/* Restore full power -- whatever that means for this particular
* board
*/
sam_usb_suspend((struct usbdev_s *)priv, true);
/* Notify the class driver of the resume event */
if (priv->driver)
{
CLASS_RESUME(priv->driver, &priv->usbdev);
}
}
}
/****************************************************************************
* Name: sam_reset
****************************************************************************/
static void sam_reset(struct sam_usbdev_s *priv)
{
uint16_t regval;
uint8_t epno;
/* Make sure that clocking is enabled to the USB peripheral. */
sam_enableclks();
/* Tell the class driver that we are disconnected. The class driver
* should then accept any new configurations.
*/
CLASS_DISCONNECT(priv->driver, &priv->usbdev);
/* The device enters the Default state (un-addressed and un-configured) */
priv->devaddr = 0;
sam_setdevaddr(priv, 0);
priv->devstate = USB_DEVSTATE_DEFAULT;
/* Reset and disable all endpoints. Then re-configure EP0 */
sam_epset_reset(priv, SAM_EPSET_ALL);
sam_ep_configure_internal(&priv->eplist[EP0], &g_ep0desc);
/* set EP0 waiting for SETUP */
sam_ep0_ctrlread(priv);
/* Reset endpoint data structures */
for (epno = 0; epno < SAM_USB_NENDPOINTS; epno++)
{
struct sam_ep_s *privep = &priv->eplist[epno];
/* Cancel any queued requests. Since they are cancelled
* with status -ESHUTDOWN, then will not be requeued
* until the configuration is reset. NOTE: This should
* not be necessary... the CLASS_DISCONNECT above should
* result in the class implementation calling sam_ep_disable
* for each of its configured endpoints.
*/
sam_req_cancel(privep, -ESHUTDOWN);
/* Reset endpoint status */
privep->stalled = false;
privep->pending = false;
privep->halted = false;
privep->zlpsent = false;
privep->txbusy = false;
privep->rxactive = false;
}
/* Re-configure the USB controller in its initial, unconnected state */
priv->usbdev.speed = USB_SPEED_FULL;
/* Clear all pending interrupt status */
regval = USBDEV_INT_SUSPEND | USBDEV_INT_SOF | USBDEV_INT_EORST |
USBDEV_INT_WAKEUP | USBDEV_INT_EORSM | USBDEV_INT_UPRSM |
USBDEV_INT_RAMACER | USBDEV_INT_LPMNYET | USBDEV_INT_LPMSUSP;
sam_putreg16(regval, SAM_USBDEV_INTFLAG);
/* Enable normal operational interrupts
* endpoint 0 is enabled on sam_ep_configure_internal()
*/
regval = USBDEV_INT_SOF | USBDEV_INT_WAKEUP | USBDEV_INT_SUSPEND;
sam_putreg16(regval, SAM_USBDEV_INTENSET);
sam_dumpep(priv, EP0);
}
/****************************************************************************
* Name: sam_ep0_wrstatus
*
* Description:
* write ep0 status reply back to host
*
****************************************************************************/
static void sam_ep0_wrstatus(struct sam_usbdev_s *priv,
const uint8_t *buffer, size_t buflen)
{
uint32_t packetsize;
/* we need to make copy of data as source is in stack
* reusing the static ep0 setup buffer
*/
DEBUGASSERT(buflen < SAM_EP0_MAXPACKET);
memcpy(&priv->ep0out[0], buffer, buflen);
/* set read for next setup OUT */
sam_ep0_ctrlread(priv);
/* setup TX transfer */
priv->eplist[0].descb[1]->addr = (uint32_t) &priv->ep0out[0];
packetsize = priv->eplist[0].descb[1]->pktsize;
packetsize &= ~USBDEV_PKTSIZE_BCNT_MASK;
packetsize &= ~USBDEV_PKTSIZE_MPKTSIZE_MASK;
packetsize |= USBDEV_PKTSIZE_BCNT(buflen);
priv->eplist[0].descb[1]->pktsize = packetsize;
/* Set BK1RDY to notify the USB hardware that TX data is ready on
* descriptor bank1. We will be notified that the descriptor has been
* transmitted by the USB device when TRCPT1 in the endpoint's EPINTFLAG
* register has been set.
*/
sam_putreg8(USBDEV_EPSTATUS_BK1RDY, SAM_USBDEV_EPSTATUSSET(0));
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0WRSTATUS), buflen);
}
/****************************************************************************
* Name: sam_ep0_dispatch
****************************************************************************/
static void sam_ep0_dispatch(struct sam_usbdev_s *priv)
{
uint8_t *dataout;
size_t outlen;
int ret;
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_DISPATCH), 0);
if (priv && priv->driver)
{
/* Assume IN SETUP (or OUT SETUP with no data) */
dataout = NULL;
outlen = 0;
/* Was this an OUT SETUP command? */
if (USB_REQ_ISOUT(priv->ctrl.type))
{
uint16_t tmplen = GETUINT16(priv->ctrl.len);
if (tmplen > 0)
{
dataout = priv->ep0out;
outlen = tmplen;
}
}
/* Forward to the control request to the class driver implementation */
ret = CLASS_SETUP(priv->driver, &priv->usbdev, &priv->ctrl,
dataout, outlen);
if (ret < 0)
{
/* Stall on failure */
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_DISPATCHSTALL), 0);
sam_ep_stall(&priv->eplist[EP0]);
}
}
}
/****************************************************************************
* Name: sam_setdevaddr
*
* Description:
* This function is called after the completion of the STATUS phase to
* instantiate the device address that was received during the SETUP
* phase. This enters the ADDRESSED state from either the DEFAULT or the
* CONFIGURED states.
*
* If called with address == 0, then function will revert to the DEFAULT,
* un-configured and un-addressed state.
*
****************************************************************************/
static void sam_setdevaddr(struct sam_usbdev_s *priv, uint8_t address)
{
DEBUGASSERT(address <= 0x7f);
if (address)
{
/* Enable the address */
address |= USBDEV_DADD_ADDEN;
sam_putreg8(address, SAM_USBDEV_DADD);
/* Go to the addressed but not configured state */
priv->devstate = USB_DEVSTATE_ADDRESSED;
}
else
{
/* Set address to zero. clear ADDEN bit */
sam_putreg8(0x00, SAM_USBDEV_DADD);
/* Revert to the un-addressed, default state */
priv->devstate = USB_DEVSTATE_DEFAULT;
}
}
/****************************************************************************
* Name: sam_ep0_setup
*
* Description:
* This function is called after the receiving of the SETUP packet
* data is ready on usb_ctrlreq_s struct
*
****************************************************************************/
static void sam_ep0_setup(struct sam_usbdev_s *priv)
{
struct sam_ep_s *ep0 = &priv->eplist[EP0];
struct sam_ep_s *privep;
union wb_u value;
union wb_u index;
union wb_u len;
union wb_u response;
enum sam_ep0setup_e ep0result;
uint8_t epno;
int nbytes = 0; /* Assume zero-length packet */
int ret;
/* Terminate any pending requests */
sam_req_cancel(ep0, -EPROTO);
/* Assume NOT stalled; no TX in progress */
ep0->stalled = false;
ep0->pending = false;
ep0->epstate = USB_EPSTATE_IDLE;
/* And extract the little-endian 16-bit values to host order */
value.w = GETUINT16(priv->ctrl.value);
index.w = GETUINT16(priv->ctrl.index);
len.w = GETUINT16(priv->ctrl.len);
uinfo("SETUP: type=%02x req=%02x value=%04x index=%04x len=%04x\n",
priv->ctrl.type, priv->ctrl.req, value.w, index.w, len.w);
/* Dispatch any non-standard requests */
if ((priv->ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_NOSTDREQ), priv->ctrl.type);
/* Let the class implementation handle all non-standard requests */
sam_ep0_dispatch(priv);
return;
}
/* Handle standard request. Pick off the things of interest to the
* USB device controller driver; pass what is left to the class driver
*/
ep0result = USB_EP0SETUP_SUCCESS;
switch (priv->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(SAM_TRACEINTID_GETSTATUS), priv->ctrl.type);
if (len.w != 2 || (priv->ctrl.type & USB_REQ_DIR_IN) == 0 ||
index.b[MSB] != 0 || value.w != 0)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADEPGETSTATUS), 0);
ep0result = USB_EP0SETUP_STALL;
}
else
{
switch (priv->ctrl.type & USB_REQ_RECIPIENT_MASK)
{
case USB_REQ_RECIPIENT_ENDPOINT:
{
epno = USB_EPNO(index.b[LSB]);
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_GETSTATUS), epno);
if (epno >= SAM_USB_NENDPOINTS)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADEPGETSTATUS),
epno);
ep0result = USB_EP0SETUP_STALL;
}
else
{
privep = &priv->eplist[epno];
response.w = 0; /* Not stalled */
nbytes = 2; /* Response size: 2 bytes */
if (privep->stalled)
{
/* Endpoint stalled */
response.b[LSB] = 1; /* Stalled */
}
}
}
break;
case USB_REQ_RECIPIENT_DEVICE:
{
if (index.w == 0)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_DEVGETSTATUS),
0);
/* Features: Remote Wakeup=YES; selfpowered=? */
response.w = 0;
response.b[LSB] = (priv->selfpowered <<
USB_FEATURE_SELFPOWERED) |
(1 << USB_FEATURE_REMOTEWAKEUP);
nbytes = 2; /* Response size: 2 bytes */
}
else
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADDEVGETSTATUS),
0);
ep0result = USB_EP0SETUP_STALL;
}
}
break;
case USB_REQ_RECIPIENT_INTERFACE:
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_IFGETSTATUS), 0);
response.w = 0;
nbytes = 2; /* Response size: 2 bytes */
}
break;
default:
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADGETSTATUS), 0);
ep0result = USB_EP0SETUP_STALL;
}
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(SAM_TRACEINTID_CLEARFEATURE),
priv->ctrl.type);
if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) !=
USB_REQ_RECIPIENT_ENDPOINT)
{
/* Let the class implementation handle all recipients (except
* for the endpoint recipient)
*/
sam_ep0_dispatch(priv);
ep0result = USB_EP0SETUP_DISPATCHED;
}
else
{
/* Endpoint recipient */
epno = USB_EPNO(index.b[LSB]);
if (epno < SAM_USB_NENDPOINTS && index.b[MSB] == 0 &&
value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0)
{
privep = &priv->eplist[epno];
privep->halted = false;
ret = sam_ep_resume(privep);
if (ret < 0)
{
ep0result = USB_EP0SETUP_STALL;
}
}
else
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADCLEARFEATURE), 0);
ep0result = USB_EP0SETUP_STALL;
}
}
}
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(SAM_TRACEINTID_SETFEATURE),
priv->ctrl.type);
if (((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE) &&
value.w == USB_FEATURE_TESTMODE)
{
/* Special case recipient=device test mode */
uinfo("test mode: %d\n", index.w);
}
else if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) !=
USB_REQ_RECIPIENT_ENDPOINT)
{
/* The class driver handles all recipients except
* recipient=endpoint.
*/
sam_ep0_dispatch(priv);
ep0result = USB_EP0SETUP_DISPATCHED;
}
else
{
/* Handler recipient=endpoint */
epno = USB_EPNO(index.b[LSB]);
if (epno < SAM_USB_NENDPOINTS && index.b[MSB] == 0 &&
value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0)
{
privep = &priv->eplist[epno];
privep->halted = true;
ret = sam_ep_stall(privep);
if (ret < 0)
{
ep0result = USB_EP0SETUP_STALL;
}
}
else
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADSETFEATURE), 0);
ep0result = USB_EP0SETUP_STALL;
}
}
}
break;
case USB_REQ_SETADDRESS:
{
/* type: host-to-device; recipient = device
* value: device address
* index: 0
* len: 0; data = none
*/
if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) !=
USB_REQ_RECIPIENT_DEVICE ||
index.w != 0 || len.w != 0 || value.w > 127)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADSETADDRESS), 0);
ep0result = USB_EP0SETUP_STALL;
}
else
{
/* Note that setting of the device address will be deferred.
* A zero-length packet will be sent and the device address will
* be set when the zero-length packet transfer completes.
*/
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0SETUPSETADDRESS),
value.w);
priv->devaddr = value.w;
ep0result = USB_EP0SETUP_ADDRESS;
}
}
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(SAM_TRACEINTID_GETSETDESC),
priv->ctrl.type);
if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE)
{
/* The request seems valid... let the class implementation
* handle it.
*/
sam_ep0_dispatch(priv);
ep0result = USB_EP0SETUP_DISPATCHED;
}
else
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADGETSETDESC), 0);
ep0result = USB_EP0SETUP_STALL;
}
}
break;
case USB_REQ_GETCONFIGURATION:
/* type: device-to-host; recipient = device
* value: 0;
* index: 0;
* len: 1; data = configuration value
*/
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_GETCONFIG),
priv->ctrl.type);
if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE &&
value.w == 0 && index.w == 0 && len.w == 1)
{
/* The request seems valid... let the class implementation
* handle it.
*/
sam_ep0_dispatch(priv);
ep0result = USB_EP0SETUP_DISPATCHED;
}
else
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADGETCONFIG), 0);
ep0result = USB_EP0SETUP_STALL;
}
}
break;
case USB_REQ_SETCONFIGURATION:
/* type: host-to-device; recipient = device
* value: configuration value
* index: 0;
* len: 0; data = none
*/
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_SETCONFIG),
priv->ctrl.type);
if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE &&
index.w == 0 && len.w == 0)
{
/* The request seems valid... let the class implementation
* handle it. If the class implementation accepts it new
* configuration, it will call sam_ep_configure() to configure
* the endpoints.
*/
sam_ep0_dispatch(priv);
ep0result = USB_EP0SETUP_DISPATCHED;
}
else
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADSETCONFIG), 0);
ep0result = USB_EP0SETUP_STALL;
}
}
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
*/
{
/* Let the class implementation handle the request */
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_GETSETIF), priv->ctrl.type);
sam_ep0_dispatch(priv);
ep0result = USB_EP0SETUP_DISPATCHED;
}
break;
case USB_REQ_SYNCHFRAME:
/* type: device-to-host; recipient = endpoint
* value: 0
* index: endpoint;
* len: 2; data = frame number
*/
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_SYNCHFRAME), 0);
}
break;
default:
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDCTRLREQ),
priv->ctrl.req);
ep0result = USB_EP0SETUP_STALL;
}
break;
}
/* Restrict the data length to the length requested in the setup packet */
if (nbytes > len.w)
{
nbytes = len.w;
}
/* At this point, the request has been handled and there are three
* (or four) possible outcomes:
*
* 1a. ep0result == USB_EP0SETUP_SUCCESS
*
* The setup request was successfully handled above and a response
* packet must be sent (may be a zero length packet).
*
* 1b. ep0result == USB_EP0SETUP_ADDRESS
*
* A special case is the case where epstate=USB_EPSTATE_EP0ADDRESS.
* This means that the above processing generated an additional state
* where we need to wait until we complete the status phase before
* applying the new device address.
*
* 2. ep0result == USB_EP0SETUP_DISPATCHED;
*
* The request was forwarded to the class implementation. In case,
* EP0 IN data may have already been sent and the EP0 IN response
* has already been queued? Or perhaps the endpoint has already
* been stalled? This is all under the control of the class driver.
*
* NOTE that for the case of non-standard SETUP requested, those
* requests were forwarded to the class driver and we don't even get
* to this logic.
*
* 3. ep0result == USB_EP0SETUP_STALL;
*
* An error was detected in either the above logic or by the class
* implementation logic.
*/
switch (ep0result)
{
case USB_EP0SETUP_SUCCESS:
{
/* Send the response (might be a zero-length packet) */
ep0->epstate = USB_EPSTATE_EP0STATUSIN;
sam_ep0_wrstatus(priv, response.b, nbytes);
}
break;
case USB_EP0SETUP_ADDRESS:
{
/* Send the response (might be a zero-length packet) */
ep0->epstate = USB_EPSTATE_EP0ADDRESS;
sam_ep0_wrstatus(priv, response.b, nbytes);
}
break;
case USB_EP0SETUP_STALL:
{
/* Stall EP0 */
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EP0SETUPSTALLED),
priv->ctrl.req);
sam_ep_stall(&priv->eplist[EP0]);
}
break;
case USB_EP0SETUP_DISPATCHED:
default:
break;
}
}
/****************************************************************************
* Name: sam_ctrla_write
*
* Description:
* writes value to CTRLA register some bits needs write-synchronisation
*
****************************************************************************/
static void sam_ctrla_write(uint8_t value)
{
sam_putreg8(value, SAM_USB_CTRLA);
if (value & USB_CTRLA_SWRST)
{
/* Due to synchronization there is a delay from writing CTRLA.SWRST
* until the reset is complete. CTRLA.SWRST and SYNCBUSY.SWRST will
* both be cleared when the reset is complete.
*/
while ((sam_getreg8(SAM_USB_CTRLA) & USB_CTRLA_SWRST) &&
(sam_getreg8(SAM_USB_SYNCBUSY) & USB_SYNCBUSY_SWRST))
;
return;
}
if (value & USB_CTRLA_ENABLE)
{
/* Due to synchronization there is delay from writing CTRLA.ENABLE
* until the peripheral is enabled/disabled.
* SYNCBUSY.ENABLE will be cleared when the operation is complete.
*/
while ((sam_getreg8(SAM_USB_SYNCBUSY) & USB_SYNCBUSY_ENABLE))
;
}
}
/****************************************************************************
* Name: sam_ep_trcpt_interrupt
*
* Description:
* Transmit completed on Bank 0/1
* Normal:
* OUT data transmit has been completed bank=0
* Ping-Pong:
* TODO:
*
****************************************************************************/
static void sam_ep_trcpt_interrupt(struct sam_usbdev_s *priv,
struct sam_ep_s *privep,
uint32_t flags, int bank)
{
uint32_t eptype;
uint16_t pktsize;
uint8_t epno;
/* Get the endpoint type */
epno = USB_EPNO(privep->ep.eplog);
eptype = sam_getreg8(SAM_USBDEV_EPCFG(epno)) & USBDEV_EPCFG_EPTYPE0_MASK;
/* Are we receiving data for a read request? EP0 does not receive data
* using read requests.
*/
/* Get the size of the packet that we just received */
pktsize = privep->descb[bank]->pktsize & USBDEV_PKTSIZE_BCNT_MASK;
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRCPT0_LEN), (uint16_t)pktsize);
if (privep->epstate == USB_EPSTATE_IDLE && epno != 0)
{
/* continue processing the read request. */
sam_req_read(priv, privep, pktsize);
}
/* Did we just receive the data associated with an OUT SETUP command? */
else if (privep->epstate == USB_EPSTATE_EP0DATAOUT)
{
uint16_t len;
DEBUGASSERT(epno == EP0 && bank == 0);
/* Yes.. back to the IDLE state */
privep->epstate = USB_EPSTATE_IDLE;
/* Get the size that we expected to receive */
len = GETUINT16(priv->ctrl.len);
if (len == pktsize)
{
/* And handle the EP0 SETUP now. */
sam_ep0_setup(priv);
}
else
{
/* Then stall. */
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EP0SETUPOUTSIZE), pktsize);
sam_ep_stall(privep);
}
}
/* Check for a EP0 STATUS packet returned by the host at the end of a
* SETUP status phase
*/
else if ((eptype == USBDEV_EPCCFG_EPTYPE0_CTRLOUT) && pktsize == 0)
{
DEBUGASSERT(epno == EP0 && bank == 0);
}
/* Otherwise there is a problem. Complain an clear the interrupt */
else
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_RXDATABKERR), privep->epstate);
}
}
/****************************************************************************
* Name: sam_ep0_ctrlread
*
* Description:
* setup 8-byte read req for ep0-ctrl setup phase.
* data is received on priv->ep0out buffer. This is notified by
* endpoint TRCPT0 interrupt
*
****************************************************************************/
static void sam_ep0_ctrlread(struct sam_usbdev_s *priv)
{
priv->eplist[0].descb[0]->addr = (uint32_t) &priv->ep0out[0];
priv->eplist[0].descb[0]->pktsize = USBDEV_PKTSIZE_MPKTSIZE(8) |
USBDEV_PKTSIZE_SIZE_64B;
sam_putreg8(USBDEV_EPSTATUS_BK0RDY, SAM_USBDEV_EPSTATUSCLR(0));
}
/****************************************************************************
* Name: sam_ep_interrupt
*
* Description:
* Handle the USB endpoint interrupt
*
****************************************************************************/
static void sam_ep_interrupt(struct sam_usbdev_s *priv, int epno)
{
struct sam_ep_s *privep;
uint16_t flags;
DEBUGASSERT((unsigned)epno < SAM_USB_NENDPOINTS);
/* Get the endpoint structure */
privep = &priv->eplist[epno];
/* Get the endpoint irq */
flags = sam_getreg8(SAM_USBDEV_EPINTFLAG(epno));
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPINTFLAGS), flags);
/* TRCPT1: IN packet sent and acknowledged by the host */
if ((flags & USBDEV_EPINT_TRCPT1) != 0)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRCPT1), flags);
/* Clear the TRCPT1 interrupt */
sam_putreg8(USBDEV_EPINT_TRCPT1, SAM_USBDEV_EPINTFLAG(epno));
/* Sending state. This is the completion of a "normal" write request
* transfer. In this case, we need to resume request processing in
* order to send the next outgoing packet.
*/
if (privep->epstate == USB_EPSTATE_SENDING ||
privep->epstate == USB_EPSTATE_EP0STATUSIN)
{
/* Continue/resume processing the write requests */
privep->epstate = USB_EPSTATE_IDLE;
sam_req_write(priv, privep);
}
/* Setting of the device address is a special case. The address was
* obtained when a preceding SETADDRESS SETUP command was processed.
* But the address is not set until the final SETUP status phase
* completes. This interrupt indicates the completion of that status
* phase and now we set the address.
*/
else if (privep->epstate == USB_EPSTATE_EP0ADDRESS)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_ADDRESSED), priv->devaddr);
DEBUGASSERT(epno == EP0);
/* Set the device address */
privep->epstate = USB_EPSTATE_IDLE;
sam_setdevaddr(priv, priv->devaddr);
}
else
{
/* Unexpected TRCPT1 interrupt */
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_TXCOMPERR), privep->epstate);
}
}
/* OUT packet received */
if ((flags & USBDEV_EPINT_TRCPT0) != 0)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRCPT0), flags);
/* Clear the TRCPT0 interrupt. */
sam_putreg8(USBDEV_EPINT_TRCPT0, SAM_USBDEV_EPINTFLAG(epno));
sam_ep_trcpt_interrupt(priv, privep, flags, 0);
}
/* Endpoint stall */
if ((flags & USBDEV_EPINT_STALL0) != 0)
{
sam_putreg8(USBDEV_EPINT_STALL0, SAM_USBDEV_EPINTFLAG(epno));
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPSTALL0), flags);
/* If EP is not halted, clear STALL */
if (privep->epstate != USB_EPSTATE_STALLED)
{
sam_putreg8(USBDEV_EPSTATUS_STALLRQ0,
SAM_USBDEV_EPSTATUSCLR(epno));
}
}
if ((flags & USBDEV_EPINT_STALL1) != 0)
{
sam_putreg8(USBDEV_EPINT_STALL1, SAM_USBDEV_EPINTFLAG(epno));
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPSTALL1), flags);
/* If EP is not halted, clear STALL */
if (privep->epstate != USB_EPSTATE_STALLED)
{
sam_putreg8(USBDEV_EPSTATUS_STALLRQ1,
SAM_USBDEV_EPSTATUSCLR(epno));
}
}
/* Transmit FAIL! */
if ((flags & USBDEV_EPINT_TRFAIL0) != 0)
{
sam_putreg8(USBDEV_EPINT_TRFAIL0, SAM_USBDEV_EPINTFLAG(epno));
privep->descb[0]->stausbk &= ~USBDEV_STATUSBK_ERRORFLOW;
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRFAIL0), flags);
}
if ((flags & USBDEV_EPINT_TRFAIL1) != 0)
{
sam_putreg8(USBDEV_EPINT_TRFAIL1, SAM_USBDEV_EPINTFLAG(epno));
privep->descb[1]->stausbk &= ~USBDEV_STATUSBK_ERRORFLOW;
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRFAIL1), flags);
}
/* SETUP packet received */
if ((flags & USBDEV_EPINT_RXSTP) != 0)
{
uint16_t len;
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPRXSTP), flags);
/* If a write request transfer was pending, complete it. */
if (privep->epstate == USB_EPSTATE_SENDING)
{
sam_req_complete(privep, -EPROTO);
}
/* SETUP data is ready in the ep0out buffer. */
memcpy((uint8_t *)&priv->ctrl, (uint8_t *)&priv->ep0out[0],
USB_SIZEOF_CTRLREQ);
/* Check for a SETUP IN transaction with data. */
len = GETUINT16(priv->ctrl.len);
if (USB_REQ_ISOUT(priv->ctrl.type) && len > 0)
{
/* Yes.. then we have to wait for the OUT data phase to complete
* before processing the SETUP command.
*/
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0SETUPOUT),
priv->ctrl.req);
privep->epstate = USB_EPSTATE_EP0DATAOUT;
/* Clear the RXSTP indication. */
sam_putreg8(USBDEV_EPINT_RXSTP, SAM_USBDEV_EPINTFLAG(epno));
}
else
{
/* This is an SETUP IN command (or a SETUP IN with no data). */
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0SETUPIN), len);
privep->epstate = USB_EPSTATE_IDLE;
/* Clear the RXSTP indication. */
sam_putreg8(USBDEV_EPINT_RXSTP, SAM_USBDEV_EPINTFLAG(epno));
/* Handle the SETUP OUT command now */
sam_ep0_setup(priv);
}
/* ready for next setup data */
sam_ep0_ctrlread(priv);
}
}
/****************************************************************************
* Name: sam_usb_interrupt
*
* Description:
* Handle the USB interrupt.
* Device Mode only TODO: Host
*
****************************************************************************/
static int sam_usb_interrupt(int irq, void *context, void *arg)
{
struct sam_usbdev_s *priv = (struct sam_usbdev_s *)arg;
uint16_t isr;
uint16_t pending;
uint16_t regval;
uint16_t pendingep;
int i;
/* Get the set of pending device interrupts */
isr = sam_getreg16(SAM_USBDEV_INTFLAG);
regval = sam_getreg16(SAM_USBDEV_INTENSET);
pending = isr & regval;
/* Get the set of pending endpoint interrupts */
pendingep = sam_getreg16(SAM_USBDEV_EPINTSMRY);
/* Handle all pending USB interrupts */
/* Serve Endpoint Interrupts first */
if (pendingep)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_PENDING_EP), pendingep);
for (i = 0; i < SAM_USB_NENDPOINTS; i++)
{
if ((pendingep & USBDEV_EPINTSMRY_EPINT(i)))
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPNO), (uint16_t)i);
sam_ep_interrupt(priv, i);
}
}
}
/* Suspend, treated last */
if (pending == USBDEV_INT_SUSPEND)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_SUSPEND), pending);
/* Enable wakeup interrupts */
sam_putreg16(USBDEV_INT_SUSPEND, SAM_USBDEV_INTENCLR);
sam_putreg16(USBDEV_INT_UPRSM | USBDEV_INT_WAKEUP | USBDEV_INT_EORSM,
SAM_USBDEV_INTENSET);
/* Clear the pending suspend (and any wakeup) interrupts */
sam_putreg16(USBDEV_INT_SUSPEND | USBDEV_INT_WAKEUP,
SAM_USBDEV_INTFLAG);
/* Perform board-specific suspend operations.
*
* The USB device peripheral clocks can be switched off.
* Resume event is asynchronously detected. MCK and USBCK can be
* switched off in the Power Management controller and
* Other board-specific operations could also be performed.
*/
}
/* SOF interrupt */
else if ((pending & USBDEV_INT_SOF) != 0)
{
/* Clear the pending SOF interrupt */
sam_putreg16(SAM_TRACEINTID_SOF, SAM_USBDEV_INTFLAG);
/* TODO: do we need check frame errors FNUM.FNCERR */
}
/* Resume or wakeup. REVISIT: Treat the same? */
else if ((pending & (USBDEV_INT_WAKEUP | USBDEV_INT_EORSM)) != 0)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_WAKEUP), (uint16_t)pending);
sam_resume(priv);
/* Clear the pending wakeup, resume, (and any suspend) interrupts */
sam_putreg16(USBDEV_INT_WAKEUP | USBDEV_INT_EORSM |
USBDEV_INT_SUSPEND, SAM_USBDEV_INTFLAG);
/* Disable wakeup and endofresume Enable suspend interrupt */
sam_putreg16(USBDEV_INT_WAKEUP | USBDEV_INT_EORSM,
SAM_USBDEV_INTENCLR);
sam_putreg16(USBDEV_INT_SUSPEND, SAM_USBDEV_INTENSET);
}
/* End of Reset. Set by hardware when an End Of Reset has been
* detected by the USB controller.
*/
if ((pending & USBDEV_INT_EORST) != 0)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EORST), pending);
/* Clear the end-of-reset interrupt */
sam_putreg16(USBDEV_INT_EORST, SAM_USBDEV_INTFLAG);
/* Handle the reset */
sam_reset(priv);
/* REVISIT: Set the device speed Why here ?? */
priv->usbdev.speed = USB_SPEED_FULL;
}
#if 0
/* for DEBUG help: check for pending unhandled irq's */
isr = sam_getreg16(SAM_USBDEV_INTFLAG);
if (isr)
{
uwarn("WARNING: Unhandled:0x%X\n", isr);
}
pendingep = sam_getreg16(SAM_USBDEV_EPINTSMRY);
if (pendingep)
{
uwarn("WARNING: Unhandled_EP:0x%X\n", pendingep);
}
#endif
return OK;
}
void arm_usbuninitialize(void)
{
uinfo("arm_usbuninitialize()\n");
}
void arm_usbinitialize(void)
{
/* For now there is only one USB controller, but we will always refer to
* it using a pointer to make any future ports to multiple USB controllers
* easier.
*/
struct sam_usbdev_s *priv = &g_usbd;
usbtrace(TRACE_DEVINIT, 0);
/* Software initialization */
sam_sw_setup(priv);
/* Power up and initialize USB controller. Interrupt from the USB
* controller is initialized here, but will not be enabled at the AIC
* until the class driver is installed.
*/
sam_hw_setup(priv);
/* Attach USB controller interrupt handlers. The hardware will not be
* initialized and interrupts will not be enabled until the class device
* driver is bound. Getting the IRQs here only makes sure that we have
* them when we need them later.
*/
if (irq_attach(SAM_IRQ_USB, sam_usb_interrupt, priv) != 0)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_IRQREGISTRATION),
(uint16_t)SAM_IRQ_USB);
goto errout;
}
uinfo("OK\n");
return;
errout:
arm_usbuninitialize();
}
/****************************************************************************
* Name: sam_ep_reset
*
* Description:
* Reset and disable one endpoints.
*
****************************************************************************/
static void sam_ep_reset(struct sam_usbdev_s *priv, uint8_t epno)
{
struct sam_ep_s *privep = &priv->eplist[epno];
/* Disable endpoint interrupts */
sam_putreg8(0x7e, SAM_USBDEV_EPINTENCLR(epno));
sam_putreg8(0x7e, SAM_USBDEV_EPINTFLAG(epno));
/* Cancel any queued requests. Since they are cancelled with status
* -ESHUTDOWN, then will not be requeued until the configuration is reset.
* NOTE: This should not be necessary... the CLASS_DISCONNECT above
* should result in the class implementation calling sam_ep_disable
* for each of its configured endpoints.
*/
sam_req_cancel(privep, -ESHUTDOWN);
/* Reset endpoint status */
privep->epstate = USB_EPSTATE_DISABLED;
privep->stalled = false;
privep->pending = false;
privep->halted = false;
privep->zlpsent = false;
privep->txbusy = false;
privep->rxactive = false;
}
/****************************************************************************
* Name: sam_epset_reset
*
* Description:
* Reset and disable a set of endpoints.
*
****************************************************************************/
static void sam_epset_reset(struct sam_usbdev_s *priv, uint16_t epset)
{
uint32_t bit;
int epno;
/* Reset each endpoint in the set */
for (epno = 0, bit = 1, epset &= SAM_EPSET_ALL;
epno < SAM_USB_NENDPOINTS && epset != 0;
epno++, bit <<= 1)
{
/* Is this endpoint in the set? */
if ((epset & bit) != 0)
{
/* Yes.. reset and disable it */
sam_ep_reset(priv, epno);
epset &= ~bit;
}
}
}
/****************************************************************************
* Name: sam_ep_stall
****************************************************************************/
static int sam_ep_stall(struct sam_ep_s *privep)
{
irqstate_t flags;
uint8_t epno;
/* Check that endpoint is in Idle state */
DEBUGASSERT(/* privep->epstate == UDP_EPSTATE_IDLE && */ privep->dev);
/* Check that endpoint is enabled and not already in Halt state */
flags = enter_critical_section();
if ((privep->epstate != USB_EPSTATE_DISABLED) &&
(privep->epstate != USB_EPSTATE_STALLED))
{
epno = USB_EPNO(privep->ep.eplog);
usbtrace(TRACE_EPSTALL, epno);
/* If this is an IN endpoint (or endpoint 0), then cancel any
* write requests in progress.
*/
if (epno == 0 || USB_ISEPIN(privep->ep.eplog))
{
sam_req_cancel(privep, -EPERM);
}
/* Put endpoint into stalled state */
privep->epstate = USB_EPSTATE_STALLED;
privep->stalled = true;
privep->pending = false;
sam_putreg8(USBDEV_EPSTATUS_STALLRQ0 | USBDEV_EPSTATUS_STALLRQ1,
SAM_USBDEV_EPSTATUSSET(epno));
}
leave_critical_section(flags);
return OK;
}
/****************************************************************************
* Name: sam_ep_resume
****************************************************************************/
static int sam_ep_resume(struct sam_ep_s *privep)
{
struct sam_usbdev_s *priv;
struct sam_req_s *req;
irqstate_t flags;
uint8_t epno;
/* Check that endpoint is in Idle state */
DEBUGASSERT(/* privep->epstate == UDP_EPSTATE_IDLE && */ privep->dev);
flags = enter_critical_section();
/* Check if the endpoint is stalled */
if (privep->epstate == USB_EPSTATE_STALLED)
{
epno = USB_EPNO(privep->ep.eplog);
usbtrace(TRACE_EPRESUME, epno);
priv = (struct sam_usbdev_s *)privep->dev;
/* Return endpoint to Idle state */
privep->stalled = false;
privep->pending = false;
privep->epstate = USB_EPSTATE_IDLE;
/* Clear STALLRQx request and reset data toggle */
if (USB_ISEPIN(privep->ep.eplog))
{
sam_putreg8(USBDEV_EPSTATUS_STALLRQ1,
SAM_USBDEV_EPSTATUSCLR(epno));
sam_putreg8(USBDEV_EPSTATUS_DTGLIN,
SAM_USBDEV_EPSTATUSCLR(epno));
sam_putreg8(USBDEV_EPINT_STALL1,
SAM_USBDEV_EPINTFLAG(epno));
}
else
{
sam_putreg8(USBDEV_EPSTATUS_STALLRQ0,
SAM_USBDEV_EPSTATUSCLR(epno));
sam_putreg8(USBDEV_EPSTATUS_DTGLOUT,
SAM_USBDEV_EPSTATUSCLR(epno));
sam_putreg8(USBDEV_EPINT_STALL0,
SAM_USBDEV_EPINTFLAG(epno));
}
/* Copy any requests in the pending request queue to the working
* request queue.
*/
while ((req = sam_req_dequeue(&privep->pendq)) != NULL)
{
sam_req_enqueue(&privep->reqq, req);
}
/* Resuming any blocked data transfers on the endpoint */
if (epno == 0 || USB_ISEPIN(privep->ep.eplog))
{
/* IN endpoint (or EP0). Restart any queued write requests */
sam_req_write(priv, privep);
}
}
leave_critical_section(flags);
return OK;
}
/****************************************************************************
* Name: sam_pullup
*
* Description:
* This is the pullup() method of the USB device driver interface
*
****************************************************************************/
static int sam_pullup(struct usbdev_s *dev, bool enable)
{
struct sam_usbdev_s *priv = (struct sam_usbdev_s *)dev;
uint32_t regval;
usbtrace(TRACE_DEVPULLUP, (uint16_t)enable);
/* Enable/disable the USB pull-up resistor */
regval = sam_getreg8(SAM_USBDEV_CTRLB);
if (enable)
{
/* Connect the 1.5 KOhm integrated pull-up on USB */
regval &= ~USBDEV_CTRLB_DETACH;
}
else
{
/* Disconnect the 1.5 KOhm integrated pull-up on USB */
regval |= USBDEV_CTRLB_DETACH;
/* Device returns to the Powered state */
if (priv->devstate > USB_DEVSTATE_POWERED)
{
priv->devstate = USB_DEVSTATE_POWERED;
}
}
sam_putreg8((uint8_t)regval, SAM_USBDEV_CTRLB);
return OK;
}
/****************************************************************************
* Name: sam_enableclks
****************************************************************************/
static void sam_enableclks(void)
{
sam_usb_enableperiph();
#if defined(CONFIG_ARCH_FAMILY_SAMD20) || defined(CONFIG_ARCH_FAMILY_SAMD21)
uint16_t regval;
/* Enable GCLK specified by BOARD_USB_GCLKGEN */
regval = GCLK_CLKCTRL_ID_USB;
putreg16(regval, SAM_GCLK_CLKCTRL);
/* Wait for clock to become disabled */
while ((getreg16(SAM_GCLK_CLKCTRL) & GCLK_CLKCTRL_CLKEN) != 0);
/* Select the USB source clock generator */
regval |= (uint16_t)BOARD_USB_GCLKGEN << GCLK_CLKCTRL_GEN_SHIFT;
/* Write the new configuration */
putreg16(regval, SAM_GCLK_CLKCTRL);
regval |= GCLK_CLKCTRL_CLKEN;
putreg16(regval, SAM_GCLK_CLKCTRL);
while ((getreg16(SAM_GCLK_CLKCTRL) & GCLK_CLKCTRL_CLKEN) == 0);
#elif defined(CONFIG_ARCH_FAMILY_SAML21)
sam_gclk_chan_enable(GCLK_CHAN_USB, BOARD_USB_GCLKGEN);
#endif
}
/****************************************************************************
* Name: sam_disableclks
****************************************************************************/
static void sam_disableclks(void)
{
#if defined(CONFIG_ARCH_FAMILY_SAMD20) || defined(CONFIG_ARCH_FAMILY_SAMD21)
#elif defined(CONFIG_ARCH_FAMILY_SAML21)
sam_gclk_chan_disable(GCLK_CHAN_USB);
#endif
sam_usb_disableperiph();
}
/****************************************************************************
* Name: sam_hw_setup
****************************************************************************/
static void sam_hw_setup(struct sam_usbdev_s *priv)
{
int i;
uint16_t regval;
uint32_t padcalib;
uint8_t calib_transn;
uint8_t calib_transp;
uint8_t calib_trim;
/* To use the USB, the programmer must first configure the USB clock
* input,
*/
sam_enableclks();
/* full reset USB */
sam_ctrla_write(USB_CTRLA_SWRST);
/* Load USB factory calibration values from NVRAM */
calib_transn = getreg32(SYSCTRL_FUSES_USBTRANSN_ADDR) &
SYSCTRL_FUSES_USBTRANSN_MASK >>
SYSCTRL_FUSES_USBTRANSN_SHIFT;
calib_transp = getreg32(SYSCTRL_FUSES_USBTRANSP_ADDR) &
SYSCTRL_FUSES_USBTRANSP_MASK >>
SYSCTRL_FUSES_USBTRANSP_SHIFT;
calib_trim = getreg32(SYSCTRL_FUSES_USBTRIM_ADDR) &
SYSCTRL_FUSES_USBTRIM_MASK >>
SYSCTRL_FUSES_USBTRIM_SHIFT;
padcalib = USB_PADCAL_TRANSP(calib_transp) |
USB_PADCAL_TRANSN(calib_transn) |
USB_PADCAL_TRIM(calib_trim);
sam_putreg32(padcalib, SAM_USB_PADCAL);
/* set config
* NREPLY = Any transaction to endpoint 0 will be ignored except SETUP
* cleared by hardware when receiving a SETUP packet.
* DETACH = The internal device pull-ups are disabled
*/
regval = USBDEV_CTRLB_NREPLY | USBDEV_CTRLB_DETACH;
/* do we need config to set LOW_SPEED mode? */
regval |= USBDEV_CTRLB_SPDCONF_FULL;
sam_putreg16(regval, SAM_USBDEV_CTRLB);
/* Enable USB core */
#ifdef CONFIG_USBDEV
sam_ctrla_write(USB_CTRLA_ENABLE | USB_CTRLA_MODE_DEVICE);
#endif
#ifdef CONFIG_USBHOST
sam_ctrla_write(USB_CTRLA_ENABLE | USB_CTRLA_MODE_HOST);
#endif
/* Set up the USB DP/DM pins */
sam_configport(PORT_USB_DP);
sam_configport(PORT_USB_DM);
/* Reset and disable endpoints */
sam_epset_reset(priv, SAM_EPSET_ALL);
/* Initialize Endpoints */
for (i = 0; i < SAM_USB_NENDPOINTS; i++)
{
/* Reset endpoint configuration */
sam_putreg8(0, SAM_USBDEV_EPCFG(i));
}
/* Init descriptor base address */
sam_putreg32((uint32_t)&priv->ep_descriptors, SAM_USB_DESCADD);
/* clear all previous descriptor data so no accidental
* DMA transfers could happen.
*/
memset((uint8_t *)(&priv->ep_descriptors[0]), 0,
sizeof(priv->ep_descriptors));
/* Disable all interrupts */
sam_putreg16(USBDEV_INT_SUSPEND | USBDEV_INT_SOF | USBDEV_INT_EORST |
USBDEV_INT_WAKEUP | USBDEV_INT_EORSM | USBDEV_INT_UPRSM |
USBDEV_INT_RAMACER | USBDEV_INT_LPMNYET | USBDEV_INT_LPMSUSP,
SAM_USBDEV_INTENCLR);
sam_pullup(&priv->usbdev, false);
}
/****************************************************************************
* Name: sam_hw_shutdown
****************************************************************************/
static void sam_hw_shutdown(struct sam_usbdev_s *priv)
{
priv->usbdev.speed = USB_SPEED_UNKNOWN;
/* Disable all interrupts */
sam_putreg16(USBDEV_INT_SUSPEND | USBDEV_INT_SOF | USBDEV_INT_EORST |
USBDEV_INT_WAKEUP | USBDEV_INT_EORSM | USBDEV_INT_UPRSM |
USBDEV_INT_RAMACER | USBDEV_INT_LPMNYET | USBDEV_INT_LPMSUSP,
SAM_USBDEV_INTENCLR);
/* Clear all pending interrupt status */
sam_putreg16(USBDEV_INT_SUSPEND | USBDEV_INT_SOF | USBDEV_INT_EORST |
USBDEV_INT_WAKEUP | USBDEV_INT_EORSM | USBDEV_INT_UPRSM |
USBDEV_INT_RAMACER | USBDEV_INT_LPMNYET | USBDEV_INT_LPMSUSP,
SAM_USBDEV_INTFLAG);
/* Disconnect the device / disable the pull-up */
sam_pullup(&priv->usbdev, false);
/* Disable clocking to the UDP peripheral */
sam_disableclks();
}
/****************************************************************************
* Name: sam_sw_setup
****************************************************************************/
static void sam_sw_setup(struct sam_usbdev_s *priv)
{
int epno;
/* Initialize the device state structure. NOTE: many fields
* have the initial value of zero and, hence, are not explicitly
* initialized here.
*/
memset(priv, 0, sizeof(struct sam_usbdev_s));
priv->usbdev.ops = &g_devops;
priv->usbdev.ep0 = &priv->eplist[EP0].ep;
priv->epavail = SAM_EPSET_ALL & ~SAM_EP_BIT(EP0);
priv->devstate = USB_DEVSTATE_SUSPENDED;
priv->prevstate = USB_DEVSTATE_POWERED;
/* Initialize the endpoint list */
for (epno = 0; epno < SAM_USB_NENDPOINTS; epno++)
{
/* Set endpoint operations, reference to driver structure (not
* really necessary because there is only one controller), and
* the (physical) endpoint number which is just the index to the
* endpoint.
*/
priv->eplist[epno].ep.ops = &g_epops;
priv->eplist[epno].dev = priv;
priv->eplist[epno].ep.eplog = epno;
/* We will use a maxpacket size for supported for each endpoint */
priv->eplist[epno].ep.maxpacket = SAM_USB_MAXPACKETSIZE(epno);
/* set descriptor addresses */
priv->eplist[epno].descb[0] = &priv->ep_descriptors[(epno << 1)];
priv->eplist[epno].descb[1] = &priv->ep_descriptors[(epno << 1) + 1];
}
/* Select a smaller endpoint size for EP0 */
#if SAM_EP0_MAXPACKET < 64 /* SAM_USB_MAXPACKETSIZE(0)? */
priv->eplist[EP0].ep.maxpacket = SAM_EP0_MAXPACKET;
#endif
}
/****************************************************************************
* Name: sam_sw_shutdown
****************************************************************************/
static void sam_sw_shutdown(struct sam_usbdev_s *priv)
{
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* 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)
{
/* For now there is only one USB controller, but we will always refer to
* it using a pointer to make any future ports to multiple USB controllers
* easier.
*/
struct sam_usbdev_s *priv = &g_usbd;
int ret;
usbtrace(TRACE_DEVREGISTER, 0);
#ifdef CONFIG_DEBUG_USB
if (!driver || !driver->ops->bind || !driver->ops->unbind ||
!driver->ops->disconnect || !driver->ops->setup)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
if (priv->driver)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_DRIVER), 0);
return -EBUSY;
}
#endif
/* First hook up the driver */
priv->driver = driver;
/* Then bind the class driver */
ret = CLASS_BIND(driver, &priv->usbdev);
if (ret)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BINDFAILED), (uint16_t)-ret);
priv->driver = NULL;
}
else
{
/* Enable USB controller interrupts at the NVIC. */
sam_hw_setup(priv);
up_enable_irq(SAM_IRQ_USB);
/* Enable EORST irq */
sam_putreg16(USBDEV_INT_EORST, SAM_USBDEV_INTENSET);
/* Enable pull-up to connect the device. The host should enumerate us
* some time after this. The next thing we expect is the EORST
* interrupt.
*/
sam_pullup(&priv->usbdev, true);
priv->usbdev.speed = USB_SPEED_FULL;
}
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)
{
/* For now there is only one USB controller, but we will always refer to
* it using a pointer to make any future ports to multiple USB controllers
* easier.
*/
struct sam_usbdev_s *priv = &g_usbd;
irqstate_t flags;
usbtrace(TRACE_DEVUNREGISTER, 0);
#ifdef CONFIG_DEBUG_USB
if (driver != priv->driver)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
/* Reset the hardware and cancel all requests. All requests must be
* canceled while the class driver is still bound.
*/
flags = enter_critical_section();
/* Unbind the class driver */
CLASS_UNBIND(driver, &priv->usbdev);
/* Disable USB controller interrupts (but keep them attached) */
up_disable_irq(SAM_IRQ_USB);
/* Put the hardware in an inactive state. Then bring the hardware back up
* in the initial state. This is essentially the same state as we were
* in when arm_usbinitialize() was first called.
*/
sam_hw_shutdown(priv);
sam_sw_shutdown(priv);
sam_sw_setup(priv);
sam_hw_setup(priv);
/* Unhook the driver */
priv->driver = NULL;
leave_critical_section(flags);
return OK;
}
#endif /* CONFIG_USBDEV && CONFIG_SAMD2L2_USB */