* arch/arm/src/samd2l2/sam_usb.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* 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/param.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/spinlock.h>
#include <nuttx/usb/usb.h>
#include <nuttx/usb/usbdev.h>
#include <nuttx/usb/usbdev_trace.h>
#include <arch/board/board.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
****************************************************************************/
#ifndef CONFIG_USBDEV_EP0_MAXSIZE
# define CONFIG_USBDEV_EP0_MAXSIZE 64
#endif
* enabled.
*/
#ifndef CONFIG_DEBUG_USB
# undef CONFIG_SAMD2L2_USB_REGDEBUG
#endif
#define EP0 (0)
#define SAM_EPSET_ALL (0xff)
#define SAM_EPSET_NOTEP0 (0xfe)
#define SAM_EP_BIT(ep) (1 << (ep))
#define SAM_EP0_MAXPACKET (CONFIG_USBDEV_EP0_MAXSIZE)
#define SAM_MAX_MULTIPACKET_SIZE (0x3fff)
#define sam_rqempty(q) ((q)->head == NULL)
#define sam_rqpeek(q) ((q)->head)
#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
#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
#ifdef CONFIG_ENDIAN_BIG
# define LSB 1
# define MSB 0
#else
# define LSB 0
# define MSB 1
#endif
* Private Type Definitions
****************************************************************************/
enum sam_epstate_e
{
USB_EPSTATE_DISABLED = 0,
USB_EPSTATE_STALLED,
USB_EPSTATE_IDLE,
* transmission) */
USB_EPSTATE_SENDING,
USB_EPSTATE_RXSTOPPED,
* request */
USB_EPSTATE_EP0DATAOUT,
USB_EPSTATE_EP0STATUSIN,
USB_EPSTATE_EP0ADDRESS
* status */
};
enum sam_devstate_e
{
USB_DEVSTATE_SUSPENDED = 0,
USB_DEVSTATE_POWERED,
USB_DEVSTATE_DEFAULT,
USB_DEVSTATE_ADDRESSED,
USB_DEVSTATE_CONFIGURED
};
enum sam_ep0setup_e
{
USB_EP0SETUP_SUCCESS = 0,
USB_EP0SETUP_DISPATCHED,
USB_EP0SETUP_ADDRESS,
USB_EP0SETUP_STALL
};
union wb_u
{
uint16_t w;
uint8_t b[2];
};
struct sam_req_s
{
struct usbdev_req_s req;
struct sam_req_s *flink;
uint16_t inflight;
* number of RX bytes we are waiting */
};
struct sam_rqhead_s
{
struct sam_req_s *head;
struct sam_req_s *tail;
};
struct sam_ep_s
{
* structure so that it is possible to simply cast from struct usbdev_ep_s
* to struct sam_ep_s.
*/
struct usbdev_ep_s ep;
struct sam_usbdev_s *dev;
struct sam_rqhead_s reqq;
struct sam_rqhead_s pendq;
struct usbdev_epdesc_s *descb[2];
volatile uint8_t epstate;
uint8_t stalled:1;
uint8_t pending:1;
uint8_t halted:1;
uint8_t zlpsent:1;
uint8_t txbusy:1;
uint8_t rxactive:1;
};
struct sam_usbdev_s
{
* structure so that it is possible to simply cast from struct usbdev_s
* to struct sam_usbdev_s.
*/
struct usbdev_s usbdev;
struct usbdevclass_driver_s *driver;
struct usb_ctrlreq_s ctrl;
uint8_t devstate;
* sam_devstate_e) */
uint8_t prevstate;
* before SUSPEND */
uint8_t devaddr;
uint8_t selfpowered:1;
uint16_t epavail;
struct sam_ep_s eplist[SAM_USB_NENDPOINTS];
struct usbdev_epdesc_s ep_descriptors[SAM_USB_NENDPOINTS * 2];
* 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
****************************************************************************/
#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 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 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
#if 0
static void sam_suspend(struct sam_usbdev_s *priv);
#endif
static void sam_resume(struct sam_usbdev_s *priv);
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);
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);
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 int sam_ep_configure_internal(struct sam_ep_s *privep,
const struct usb_epdesc_s *desc);
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);
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);
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
****************************************************************************/
* 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,
};
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
};
* 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
* 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;
* last time? Are we polling the register? If so, suppress the output.
*/
if (regaddr == prevaddr && regval == preval && prevwrite == iswrite)
{
count++;
}
else
{
* duplicate accesses before this one?
*/
if (count > 0)
{
if (count == 1)
{
sam_printreg(prevaddr, preval, prevwrite);
}
else
{
uinfo("[repeats %d more times]\n", count);
}
}
prevaddr = regaddr;
preval = regval;
count = 0;
prevwrite = iswrite;
sam_printreg(regaddr, regval, iswrite);
}
}
#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)
{
sam_checkreg(regaddr, regval, true);
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)
{
uint32_t regval = getreg16(regaddr);
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)
{
sam_checkreg(regaddr, regval, true);
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)
{
uint32_t regval = getreg8(regaddr);
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)
{
sam_checkreg(regaddr, regval, true);
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)
{
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;
flags = enter_critical_section();
privreq = sam_req_dequeue(&privep->reqq);
leave_critical_section(flags);
if (privreq)
{
privreq->req.result = result;
privreq->flink = NULL;
privreq->req.callback(&privep->ep, &privreq->req);
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;
epno = USB_EPNO(privep->ep.eplog);
DEBUGASSERT(privreq->req.xfrd < privreq->req.len);
nbytes = privreq->req.len - privreq->req.xfrd;
* the request.
*/
if (nbytes >= SAM_MAX_MULTIPACKET_SIZE)
{
nbytes = SAM_MAX_MULTIPACKET_SIZE;
}
privreq->inflight = nbytes;
usbtrace(TRACE_WRITE(USB_EPNO(privep->ep.eplog)), nbytes);
* bytes successfully transferred plus the number of bytes previously
* "in-flight".
*/
buf = privreq->req.buf + privreq->req.xfrd;
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);
if (privreq->req.flags & USBDEV_REQFLAGS_NULLPKT)
{
packetsize |= USBDEV_PKTSIZE_AUTOZLP;
}
priv->eplist[epno].descb[1]->pktsize = packetsize;
* This indication will be need in interrupt processing (TRCPT1)
* in order to properly terminate the request.
*/
privep->epstate = USB_EPSTATE_SENDING;
* 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;
epno = USB_EPNO(privep->ep.eplog);
* there is no TX transfer in progress (epstate should be IDLE).
*/
DEBUGASSERT(privep->epstate == USB_EPSTATE_IDLE);
while (privep->epstate == USB_EPSTATE_IDLE)
{
privreq = sam_rqpeek(&privep->reqq);
if (!privreq)
{
* requests to send.
*/
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPINQEMPTY), epno);
if (privep->pending)
{
sam_ep_stall(privep);
}
return -ENOENT;
}
uinfo("epno=%d req=%p: len=%zu xfrd=%zu inflight=%d\n",
epno, privreq, privreq->req.len, privreq->req.xfrd,
privreq->inflight);
privreq->req.xfrd += privreq->inflight;
privreq->inflight = 0;
bytesleft = privreq->req.len - privreq->req.xfrd;
if (bytesleft > 0)
{
sam_req_wrsetup(priv, privep, privreq);
}
* 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)
{
*/
privep->epstate = USB_EPSTATE_SENDING;
privep->zlpsent = true;
privreq->inflight = 0;
usbtrace(TRACE_WRITE(epno), 0);
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));
}
* 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)
{
* 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;
int epno;
DEBUGASSERT(priv && privep && privep->epstate == USB_EPSTATE_IDLE);
epno = USB_EPNO(privep->ep.eplog);
do
{
privreq = sam_rqpeek(&privep->reqq);
if (!privreq)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPOUTQEMPTY), epno);
* ready. HW sends NAK to host if it tries to send something.
*/
privep->epstate = USB_EPSTATE_RXSTOPPED;
return -ENOENT;
}
uinfo("EP%d: req.len=%zu xfrd=%zu recvsize=%d\n",
epno, privreq->req.len, privreq->req.xfrd, recvsize);
if (privreq->req.len == 0)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EPOUTNULLPACKET), 0);
sam_req_complete(privep, OK);
privreq = NULL;
}
if ((privreq->inflight) && (recvsize != 0))
{
usbtrace(TRACE_READ(USB_EPNO(privep->ep.eplog)), recvsize);
privreq->req.xfrd += recvsize;
privreq->inflight = 0;
usbtrace(TRACE_COMPLETE(epno), privreq->req.xfrd);
sam_req_complete(privep, OK);
* 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);
privep->rxactive = true;
privreq->req.xfrd = 0;
privreq->inflight = privreq->req.len;
priv->eplist[epno].descb[0]->addr = (uint32_t) privreq->req.buf;
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)
{
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);
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;
}
privep->descb[0]->pktsize = ephwsize;
privep->descb[1]->pktsize = ephwsize;
privep->ep.eplog = desc->addr;
privep->ep.maxpacket = maxpacket;
privep->epstate = USB_EPSTATE_IDLE;
epconf = 0x00;
sam_putreg8(0x00, SAM_USBDEV_EPCFG(epno));
if (dirin)
{
intflags = USBDEV_EPINT_TRCPT1 | USBDEV_EPINT_STALL1;
}
else
{
intflags = USBDEV_EPINT_TRCPT0 | USBDEV_EPINT_STALL0;
}
sam_putreg8(0x7e, SAM_USBDEV_EPINTENCLR(epno));
sam_putreg8(0x7e, SAM_USBDEV_EPINTFLAG(epno));
switch (eptype)
{
case USB_EP_ATTR_XFER_CONTROL:
{
epconf = USBDEV_EPCCFG_EPTYPE0_CTRLOUT |
USBDEV_EPCCFG_EPTYPE1_CTRLIN;
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));
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)
{
* (skipping EP0)
*/
for (epndx = 1; epndx < SAM_USB_NENDPOINTS; epndx++)
{
uint8_t bit = SAM_EP_BIT(epndx);
if ((epset & bit) != 0)
{
priv->epavail &= ~bit;
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_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;
#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
ret = sam_ep_configure_internal(privep, desc);
* configured.
*/
if (ret == OK && last)
{
struct sam_usbdev_s *priv = privep->dev;
* 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);
flags = enter_critical_section();
priv = privep->dev;
sam_ep_reset(priv, epno);
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 = 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
* 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)
{
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)
{
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
epno = USB_EPNO(ep->eplog);
req->result = -EINPROGRESS;
req->xfrd = 0;
privreq->inflight = 0;
flags = enter_critical_section();
* 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)
{
if (privep->stalled || privep->pending)
{
* "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
{
sam_req_enqueue(&privep->reqq, privreq);
usbtrace(TRACE_INREQQUEUED(epno), req->len);
* processing in progress, then transfer the data now.
*/
if (privep->epstate == USB_EPSTATE_IDLE && !privep->txbusy)
{
ret = sam_req_write(priv, privep);
}
}
}
else
{
sam_req_enqueue(&privep->reqq, privreq);
usbtrace(TRACE_OUTREQQUEUED(epno), req->len);
* 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;
}
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
privep = (struct sam_ep_s *)ep;
if (resume)
{
ret = sam_ep_resume(privep);
}
else
{
* 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))
{
if (!sam_rqempty(&privep->reqq))
{
* stalled on the next TRCPTx interrupt when the request
* queue becomes empty.
*/
privep->pending = true;
leave_critical_section(flags);
return OK;
}
}
* 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
epno = USB_EPNO(epno);
if (epno > 0)
{
* 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;
}
* 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);
}
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)
{
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
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
flags = enter_critical_section();
sam_resume(priv);
* 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
static void sam_suspend(struct sam_usbdev_s *priv)
{
if (priv->devstate != USB_DEVSTATE_SUSPENDED)
{
if (priv->driver)
{
CLASS_SUSPEND(priv->driver, &priv->usbdev);
}
priv->prevstate = priv->devstate;
priv->devstate = USB_DEVSTATE_SUSPENDED;
sam_disableclks();
* 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)
{
* from the host PC, or (2) the class device implementation calls the
* wakeup() method.
*/
if (priv->devstate == USB_DEVSTATE_SUSPENDED)
{
priv->devstate = priv->prevstate;
sam_enableclks();
* board
*/
sam_usb_suspend((struct usbdev_s *)priv, true);
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;
sam_enableclks();
* should then accept any new configurations.
*/
CLASS_DISCONNECT(priv->driver, &priv->usbdev);
priv->devaddr = 0;
sam_setdevaddr(priv, 0);
priv->devstate = USB_DEVSTATE_DEFAULT;
sam_epset_reset(priv, SAM_EPSET_ALL);
sam_ep_configure_internal(&priv->eplist[EP0], &g_ep0desc);
sam_ep0_ctrlread(priv);
for (epno = 0; epno < SAM_USB_NENDPOINTS; epno++)
{
struct sam_ep_s *privep = &priv->eplist[epno];
* 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);
privep->stalled = false;
privep->pending = false;
privep->halted = false;
privep->zlpsent = false;
privep->txbusy = false;
privep->rxactive = false;
}
priv->usbdev.speed = USB_SPEED_FULL;
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);
* 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;
* reusing the static ep0 setup buffer
*/
DEBUGASSERT(buflen < SAM_EP0_MAXPACKET);
memcpy(&priv->ep0out[0], buffer, buflen);
sam_ep0_ctrlread(priv);
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;
* 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)
{
dataout = NULL;
outlen = 0;
if (USB_REQ_ISOUT(priv->ctrl.type))
{
uint16_t tmplen = GETUINT16(priv->ctrl.len);
if (tmplen > 0)
{
dataout = priv->ep0out;
outlen = tmplen;
}
}
ret = CLASS_SETUP(priv->driver, &priv->usbdev, &priv->ctrl,
dataout, outlen);
if (ret < 0)
{
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)
{
address |= USBDEV_DADD_ADDEN;
sam_putreg8(address, SAM_USBDEV_DADD);
priv->devstate = USB_DEVSTATE_ADDRESSED;
}
else
{
sam_putreg8(0x00, SAM_USBDEV_DADD);
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;
int ret;
sam_req_cancel(ep0, -EPROTO);
ep0->stalled = false;
ep0->pending = false;
ep0->epstate = USB_EPSTATE_IDLE;
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);
if ((priv->ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_NOSTDREQ), priv->ctrl.type);
sam_ep0_dispatch(priv);
return;
}
* USB device controller driver; pass what is left to the class driver
*/
ep0result = USB_EP0SETUP_SUCCESS;
switch (priv->ctrl.req)
{
case USB_REQ_GETSTATUS:
{
* 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;
nbytes = 2;
if (privep->stalled)
{
response.b[LSB] = 1;
}
}
}
break;
case USB_REQ_RECIPIENT_DEVICE:
{
if (index.w == 0)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_DEVGETSTATUS),
0);
response.w = 0;
response.b[LSB] = (priv->selfpowered <<
USB_FEATURE_SELFPOWERED) |
(1 << USB_FEATURE_REMOTEWAKEUP);
nbytes = 2;
}
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;
}
break;
default:
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BADGETSTATUS), 0);
ep0result = USB_EP0SETUP_STALL;
}
break;
}
}
}
break;
case USB_REQ_CLEARFEATURE:
{
* 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)
{
* for the endpoint recipient)
*/
sam_ep0_dispatch(priv);
ep0result = USB_EP0SETUP_DISPATCHED;
}
else
{
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:
{
* 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)
{
uinfo("test mode: %d\n", index.w);
}
else if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) !=
USB_REQ_RECIPIENT_ENDPOINT)
{
* recipient=endpoint.
*/
sam_ep0_dispatch(priv);
ep0result = USB_EP0SETUP_DISPATCHED;
}
else
{
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:
{
* 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
{
* 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:
* value: descriptor type and index
* index: 0 or language ID;
* len: descriptor len; data = descriptor
*/
case USB_REQ_SETDESCRIPTOR:
* 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)
{
* 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:
* 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)
{
* 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:
* 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)
{
* 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:
* value: 0
* index: interface;
* len: 1; data = alt interface
*/
case USB_REQ_SETINTERFACE:
* value: alternate setting
* index: interface;
* len: 0; data = none
*/
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_GETSETIF), priv->ctrl.type);
sam_ep0_dispatch(priv);
ep0result = USB_EP0SETUP_DISPATCHED;
}
break;
case USB_REQ_SYNCHFRAME:
* 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;
}
if (nbytes > len.w)
{
nbytes = len.w;
}
* (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:
{
ep0->epstate = USB_EPSTATE_EP0STATUSIN;
sam_ep0_wrstatus(priv, response.b, nbytes);
}
break;
case USB_EP0SETUP_ADDRESS:
{
ep0->epstate = USB_EPSTATE_EP0ADDRESS;
sam_ep0_wrstatus(priv, response.b, nbytes);
}
break;
case USB_EP0SETUP_STALL:
{
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)
{
* 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)
{
* 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;
epno = USB_EPNO(privep->ep.eplog);
eptype = sam_getreg8(SAM_USBDEV_EPCFG(epno)) & USBDEV_EPCFG_EPTYPE0_MASK;
* using read requests.
*/
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)
{
sam_req_read(priv, privep, pktsize);
}
else if (privep->epstate == USB_EPSTATE_EP0DATAOUT)
{
uint16_t len;
DEBUGASSERT(epno == EP0 && bank == 0);
privep->epstate = USB_EPSTATE_IDLE;
len = GETUINT16(priv->ctrl.len);
if (len == pktsize)
{
sam_ep0_setup(priv);
}
else
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_EP0SETUPOUTSIZE), pktsize);
sam_ep_stall(privep);
}
}
* SETUP status phase
*/
else if ((eptype == USBDEV_EPCCFG_EPTYPE0_CTRLOUT) && pktsize == 0)
{
DEBUGASSERT(epno == EP0 && bank == 0);
}
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);
privep = &priv->eplist[epno];
flags = sam_getreg8(SAM_USBDEV_EPINTFLAG(epno));
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPINTFLAGS), flags);
if ((flags & USBDEV_EPINT_TRCPT1) != 0)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRCPT1), flags);
sam_putreg8(USBDEV_EPINT_TRCPT1, SAM_USBDEV_EPINTFLAG(epno));
* 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)
{
privep->epstate = USB_EPSTATE_IDLE;
sam_req_write(priv, privep);
}
* 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);
privep->epstate = USB_EPSTATE_IDLE;
sam_setdevaddr(priv, priv->devaddr);
}
else
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_TXCOMPERR), privep->epstate);
}
}
if ((flags & USBDEV_EPINT_TRCPT0) != 0)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPTRCPT0), flags);
sam_putreg8(USBDEV_EPINT_TRCPT0, SAM_USBDEV_EPINTFLAG(epno));
sam_ep_trcpt_interrupt(priv, privep, flags, 0);
}
if ((flags & USBDEV_EPINT_STALL0) != 0)
{
sam_putreg8(USBDEV_EPINT_STALL0, SAM_USBDEV_EPINTFLAG(epno));
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPSTALL0), flags);
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 (privep->epstate != USB_EPSTATE_STALLED)
{
sam_putreg8(USBDEV_EPSTATUS_STALLRQ1,
SAM_USBDEV_EPSTATUSCLR(epno));
}
}
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);
}
if ((flags & USBDEV_EPINT_RXSTP) != 0)
{
uint16_t len;
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EPRXSTP), flags);
if (privep->epstate == USB_EPSTATE_SENDING)
{
sam_req_complete(privep, -EPROTO);
}
memcpy((uint8_t *)&priv->ctrl, (uint8_t *)&priv->ep0out[0],
USB_SIZEOF_CTRLREQ);
len = GETUINT16(priv->ctrl.len);
if (USB_REQ_ISOUT(priv->ctrl.type) && len > 0)
{
* before processing the SETUP command.
*/
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0SETUPOUT),
priv->ctrl.req);
privep->epstate = USB_EPSTATE_EP0DATAOUT;
sam_putreg8(USBDEV_EPINT_RXSTP, SAM_USBDEV_EPINTFLAG(epno));
}
else
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EP0SETUPIN), len);
privep->epstate = USB_EPSTATE_IDLE;
sam_putreg8(USBDEV_EPINT_RXSTP, SAM_USBDEV_EPINTFLAG(epno));
sam_ep0_setup(priv);
}
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;
isr = sam_getreg16(SAM_USBDEV_INTFLAG);
regval = sam_getreg16(SAM_USBDEV_INTENSET);
pending = isr & regval;
pendingep = sam_getreg16(SAM_USBDEV_EPINTSMRY);
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);
}
}
}
if (pending == USBDEV_INT_SUSPEND)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_SUSPEND), pending);
sam_putreg16(USBDEV_INT_SUSPEND, SAM_USBDEV_INTENCLR);
sam_putreg16(USBDEV_INT_UPRSM | USBDEV_INT_WAKEUP | USBDEV_INT_EORSM,
SAM_USBDEV_INTENSET);
sam_putreg16(USBDEV_INT_SUSPEND | USBDEV_INT_WAKEUP,
SAM_USBDEV_INTFLAG);
*
* 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.
*/
}
else if ((pending & USBDEV_INT_SOF) != 0)
{
sam_putreg16(SAM_TRACEINTID_SOF, SAM_USBDEV_INTFLAG);
}
else if ((pending & (USBDEV_INT_WAKEUP | USBDEV_INT_EORSM)) != 0)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_WAKEUP), (uint16_t)pending);
sam_resume(priv);
sam_putreg16(USBDEV_INT_WAKEUP | USBDEV_INT_EORSM |
USBDEV_INT_SUSPEND, SAM_USBDEV_INTFLAG);
sam_putreg16(USBDEV_INT_WAKEUP | USBDEV_INT_EORSM,
SAM_USBDEV_INTENCLR);
sam_putreg16(USBDEV_INT_SUSPEND, SAM_USBDEV_INTENSET);
}
* detected by the USB controller.
*/
if ((pending & USBDEV_INT_EORST) != 0)
{
usbtrace(TRACE_INTDECODE(SAM_TRACEINTID_EORST), pending);
sam_putreg16(USBDEV_INT_EORST, SAM_USBDEV_INTFLAG);
sam_reset(priv);
priv->usbdev.speed = USB_SPEED_FULL;
}
#if 0
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)
{
* 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);
sam_sw_setup(priv);
* controller is initialized here, but will not be enabled at the AIC
* until the class driver is installed.
*/
sam_hw_setup(priv);
* 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];
sam_putreg8(0x7e, SAM_USBDEV_EPINTENCLR(epno));
sam_putreg8(0x7e, SAM_USBDEV_EPINTFLAG(epno));
* -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);
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;
for (epno = 0, bit = 1, epset &= SAM_EPSET_ALL;
epno < SAM_USB_NENDPOINTS && epset != 0;
epno++, bit <<= 1)
{
if ((epset & bit) != 0)
{
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;
DEBUGASSERT( privep->dev);
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);
* write requests in progress.
*/
if (epno == 0 || USB_ISEPIN(privep->ep.eplog))
{
sam_req_cancel(privep, -EPERM);
}
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;
DEBUGASSERT( privep->dev);
flags = enter_critical_section();
if (privep->epstate == USB_EPSTATE_STALLED)
{
epno = USB_EPNO(privep->ep.eplog);
usbtrace(TRACE_EPRESUME, epno);
priv = (struct sam_usbdev_s *)privep->dev;
privep->stalled = false;
privep->pending = false;
privep->epstate = USB_EPSTATE_IDLE;
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));
}
* request queue.
*/
while ((req = sam_req_dequeue(&privep->pendq)) != NULL)
{
sam_req_enqueue(&privep->reqq, req);
}
if (epno == 0 || USB_ISEPIN(privep->ep.eplog))
{
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);
regval = sam_getreg8(SAM_USBDEV_CTRLB);
if (enable)
{
regval &= ~USBDEV_CTRLB_DETACH;
}
else
{
regval |= USBDEV_CTRLB_DETACH;
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;
regval = GCLK_CLKCTRL_ID_USB;
putreg16(regval, SAM_GCLK_CLKCTRL);
while ((getreg16(SAM_GCLK_CLKCTRL) & GCLK_CLKCTRL_CLKEN) != 0);
regval |= (uint16_t)BOARD_USB_GCLKGEN << GCLK_CLKCTRL_GEN_SHIFT;
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;
* input,
*/
sam_enableclks();
sam_ctrla_write(USB_CTRLA_SWRST);
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);
* 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;
regval |= USBDEV_CTRLB_SPDCONF_FULL;
sam_putreg16(regval, SAM_USBDEV_CTRLB);
#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
sam_configport(PORT_USB_DP);
sam_configport(PORT_USB_DM);
sam_epset_reset(priv, SAM_EPSET_ALL);
for (i = 0; i < SAM_USB_NENDPOINTS; i++)
{
sam_putreg8(0, SAM_USBDEV_EPCFG(i));
}
sam_putreg32((uint32_t)&priv->ep_descriptors, SAM_USB_DESCADD);
* DMA transfers could happen.
*/
memset((uint8_t *)(&priv->ep_descriptors[0]), 0,
sizeof(priv->ep_descriptors));
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;
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_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);
sam_pullup(&priv->usbdev, false);
sam_disableclks();
}
* Name: sam_sw_setup
****************************************************************************/
static void sam_sw_setup(struct sam_usbdev_s *priv)
{
int epno;
* 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;
for (epno = 0; epno < SAM_USB_NENDPOINTS; epno++)
{
* 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;
priv->eplist[epno].ep.maxpacket = SAM_USB_MAXPACKETSIZE(epno);
priv->eplist[epno].descb[0] = &priv->ep_descriptors[(epno << 1)];
priv->eplist[epno].descb[1] = &priv->ep_descriptors[(epno << 1) + 1];
}
#if SAM_EP0_MAXPACKET < 64
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)
{
* 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
priv->driver = driver;
ret = CLASS_BIND(driver, &priv->usbdev);
if (ret)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_BINDFAILED), (uint16_t)-ret);
priv->driver = NULL;
}
else
{
sam_hw_setup(priv);
up_enable_irq(SAM_IRQ_USB);
sam_putreg16(USBDEV_INT_EORST, SAM_USBDEV_INTENSET);
* 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)
{
* 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
* canceled while the class driver is still bound.
*/
flags = enter_critical_section();
CLASS_UNBIND(driver, &priv->usbdev);
up_disable_irq(SAM_IRQ_USB);
* 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);
priv->driver = NULL;
leave_critical_section(flags);
return OK;
}
#endif