* arch/arm/src/samd5e5/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/mutex.h>
#include <nuttx/spinlock.h>
#include <nuttx/usb/usb.h>
#include <nuttx/usb/usbdev.h>
#include <nuttx/usb/usbdev_trace.h>
#include <nuttx/usb/usbhost.h>
#include <nuttx/usb/usbhost_devaddr.h>
#include <nuttx/usb/usbhost_trace.h>
#include <arch/board/board.h>
#include "arm_internal.h"
#include "chip.h"
#include "hardware/sam_pinmap.h"
#include "hardware/sam_fuses.h"
#include "sam_gclk.h"
#include "sam_port.h"
#include "sam_periphclks.h"
#include "sam_usb.h"
#include "sam_usbhost.h"
#if defined(CONFIG_SAMD5E5_USB)
* Pre-processor Definitions
****************************************************************************/
#ifndef CONFIG_USB_EP0_DEFSIZE
# define CONFIG_USB_EP0_DEFSIZE 8
#endif
#ifndef CONFIG_USBDEV_EP0_MAXSIZE
# define CONFIG_USBDEV_EP0_MAXSIZE 64
#endif
* enabled.
*/
#ifndef CONFIG_DEBUG_USB
# undef CONFIG_SAMD5E5_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_RETRY_COUNT 3
#define SAM_SETUP_DELAY SEC2TICK(5)
#define SAM_DATANAK_DELAY SEC2TICK(5)
#define USB_CTRL_DPKT_TIMEOUT (500)
#define USB_CTRL_STAT_TIMEOUT (50)
#ifndef CONFIG_SAM_DESCSIZE
# define CONFIG_SAM_DESCSIZE 128
#endif
#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
#define SAM_TRACEINTID_PENDING_PIPE 0x002c
#define SAM_TRACEINTID_PIPENO 0x002d
#ifdef CONFIG_ENDIAN_BIG
# define LSB 1
# define MSB 0
#else
# define LSB 0
# define MSB 1
#endif
* Private Type Definitions
****************************************************************************/
#ifdef CONFIG_USBDEV
enum sam_epstate_e
{
USB_EPSTATE_DISABLED = 0,
USB_EPSTATE_STALLED,
USB_EPSTATE_IDLE,
USB_EPSTATE_SENDING,
USB_EPSTATE_RXSTOPPED,
USB_EPSTATE_EP0DATAOUT,
USB_EPSTATE_EP0STATUSIN,
USB_EPSTATE_EP0ADDRESS
};
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;
uint8_t prevstate;
uint8_t devaddr;
uint8_t selfpowered:1;
uint16_t epavail;
aligned_data(4) struct sam_ep_s eplist[SAM_USB_NENDPOINTS];
aligned_data(4)
struct usbdev_epdesc_s ep_descriptors[SAM_USB_NENDPOINTS *
SAM_USB_NBANKS()];
* 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.
*/
aligned_data(4) uint8_t ep0out[SAM_EP0_MAXPACKET];
};
#endif
#ifdef CONFIG_USBHOST
enum sam_hoststate_e
{
USB_HOSTSTATE_SUSPENDED = 0,
USB_HOSTSTATE_POWERED,
USB_HOSTSTATE_DEFAULT,
USB_HOSTSTATE_ADDRESSED,
USB_HOSTSTATE_CONFIGURED
};
enum usb_h_pipe_state
{
USB_H_PIPE_S_FREE = 0x00,
USB_H_PIPE_S_CFG = 0x01,
USB_H_PIPE_S_IDLE = 0x02,
USB_H_PIPE_S_SETUP = 0x03,
USB_H_PIPE_S_DATI = 0x05,
USB_H_PIPE_S_DATO = 0x06,
USB_H_PIPE_S_ZLPI = 0x07,
USB_H_PIPE_S_ZLPO = 0x08,
USB_H_PIPE_S_STATI = 0x09,
USB_H_PIPE_S_STATO = 0x0a,
USB_H_PIPE_S_TAKEN = 0x10
};
enum usb_h_status
{
USB_H_OK = 0,
USB_H_BUSY = -4,
USB_H_DENIED = -17,
USB_H_TIMEOUT = -8,
USB_H_ABORT = -3,
USB_H_STALL = -25,
USB_H_RESET = -7,
USB_H_ERR_ARG = -13,
USB_H_ERR_UNSP_OP = -27,
USB_H_ERR_NO_RSC = -28,
USB_H_ERR_NOT_INIT = -20,
USB_H_ERR = -6
};
* state machine (for debug purposes only)
*/
enum sam_smstate_e
{
SMSTATE_DETACHED = 0,
SMSTATE_ATTACHED,
SMSTATE_ENUM,
SMSTATE_CLASS_BOUND,
};
enum sam_chreason_e
{
CHREASON_IDLE = 0,
CHREASON_FREED,
CHREASON_XFRC,
CHREASON_NAK,
CHREASON_NYET,
CHREASON_STALL,
CHREASON_TXERR,
CHREASON_DTERR,
CHREASON_FRMOR,
CHREASON_CANCELLED
};
*
* Timing in USB 2.0 spec.:
* - 9.2.6.1 : USB sets an upper limit of 5 seconds as the upper
* limit for any command to be processed.
* - 9.2.6.3 : if a device receives a SetAddress() request,
* the device must be able to complete processing
* of the request and be able to
* successfully complete the Status stage of the request within
* 50 ms.
* After successful completion of the Status stage, the device is
* allowed a SetAddress() recovery interval of 2 ms. At the end of
* this interval, the device must be able to accept Setup packets
* addressed to the new address.
* - 9.2.6.4 : For standard device requests that require no Data stage,
* must be able to complete the request and be able to successfully
* complete the Status stage of the request within 50 ms of receipt
* of the request. This limitation applies to requests to the
* device, interface, or endpoint. For standard device requests
* that require data stage transfer to the host, the
* device must be able to return the first data packet
* to the host within 500 ms of receipt of the request. For
* subsequent data packets, if any, the device must be able to
* return them within 500 ms of successful completion of the
* transmission of the previous packet. The device must then be
* able to successfully complete the status stage
* within 50 ms after returning the last data packet.
* For standard device requests that require a data stage transfer
* to the device, the 5-second limit applies.
* - 9.2.6.5 : Unless specifically exempted in the class document, all
* class-specific requests must meet the timing limitations for
* standard device requests.
*
* Conclusion:
* 1. Whole request with data: 5 seconds
* 2. Whole request without data: 50 ms
* 3. Data packets: 500 ms
*/
struct usb_h_ctrl_xfer
{
uint8_t *data;
uint8_t *setup;
uint16_t size;
uint16_t count;
int16_t req_timeout;
int16_t pkt_timeout;
uint16_t pkt_size;
uint8_t state;
int8_t status;
};
struct usb_h_bulk_int_iso_xfer
{
uint32_t size;
uint32_t count;
uint8_t *data;
uint16_t reserved[3];
uint8_t state;
int8_t status;
};
struct usb_h_high_bw_xfer
{
uint32_t size;
uint32_t count;
uint8_t *data;
uint16_t pkt_size[3];
uint8_t state;
int8_t status;
};
struct usb_h_xfer
{
union
{
uint16_t u16[9];
uint8_t u8[18];
} reserved;
uint8_t state;
int8_t status;
};
struct sam_pipe_s
{
struct usbhost_pipedesc_s *descb[2];
volatile uint8_t pipestate;
volatile uint8_t pipestatus;
volatile int8_t pipestatus_general;
volatile int8_t pipestate_general;
int16_t result;
uint32_t size;
uint32_t count;
uint8_t *data;
int16_t pkt_timeout;
uint8_t zlp:1;
uint8_t stalled:1;
uint8_t pending:1;
uint8_t halted:1;
uint8_t zlpsent:1;
uint8_t txbusy:1;
uint8_t rxactive:1;
bool inuse;
bool in;
uint8_t idx;
uint8_t epno;
uint8_t eptype;
uint8_t funcaddr;
uint8_t speed;
uint8_t interval;
uint16_t maxpacket;
sem_t waitsem;
volatile bool waiter;
#ifdef CONFIG_USBHOST_ASYNCH
usbhost_asynch_t callback;
void *arg;
#endif
#ifdef HPL_USB_HOST
uint16_t max_pkt_size;
uint8_t ep;
uint8_t type;
uint8_t toggle;
uint8_t bank : 2;
uint8_t high_bw_out : 1;
uint8_t dma : 1;
uint8_t periodic_start : 1;
union
{
struct usb_h_xfer general;
struct usb_h_ctrl_xfer ctrl;
struct usb_h_bulk_int_iso_xfer bii;
struct usb_h_high_bw_xfer hbw;
} x;
#endif
};
struct sam_usbhost_s
{
* structure so that it is possible to simply cast from struct usbhost_s
* to struct sam_usbhost_s.
*/
struct usbhost_driver_s drvr;
struct usbhost_roothubport_s rhport;
uint8_t hoststate;
uint8_t prevstate;
uint16_t epavail;
mutex_t lock;
bool connected;
bool change;
bool pscwait;
uint8_t smstate;
uint8_t irqset;
uint8_t xfrtype;
sem_t pscsem;
uint16_t pipes_unfreeze;
int8_t suspend_start;
int8_t resume_start;
int8_t n_ctrl_req_user;
int8_t n_sof_user;
uint8_t pipe_pool_size;
#ifdef CONFIG_USBHOST_HUB
volatile struct usbhost_hubport_s *hport;
#endif
struct usbhost_devaddr_s devgen;
aligned_data(4)
struct sam_pipe_s pipelist[SAM_USB_NENDPOINTS];
aligned_data(4)
struct usbhost_pipedesc_s pipe_descriptors[SAM_USB_NENDPOINTS *
SAM_USB_NBANKS()];
usbhost_ep_t ep0;
aligned_data(4) uint8_t ctrl_buffer[64];
};
#endif
* Private Function Prototypes
****************************************************************************/
#ifdef CONFIG_SAMD5E5_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);
#ifdef CONFIG_USBHOST
static void sam_dumppipe(struct sam_usbhost_s *priv, uint8_t epno);
#endif
#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)
#ifdef CONFIG_USBHOST
# define sam_dumppipe(priv, epno)
#endif
#endif
static inline void sam_modifyreg8(uint32_t clrbits,
uint32_t setbits,
uintptr_t regaddr);
static void sam_enableclks(void);
#ifdef CONFIG_USBDEV
static void sam_disableclks(void);
static void sam_suspend(struct sam_usbdev_s *priv);
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);
#ifdef CONFIG_USBDEV_DMA
static void *sam_ep_allocbuffer(struct usbdev_ep_s *ep, uint16_t nbytes);
static void sam_ep_freebuffer(struct usbdev_ep_s *ep, void *buf);
#endif
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_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);
#endif
#ifdef CONFIG_USBHOST
#undef CONFIG_SAM_USBHOST_PKTDUMP
#ifdef CONFIG_SAM_USBHOST_PKTDUMP
# define sam_pktdump(m,b,n) lib_dumpbuffer(m,b,n)
#else
# define sam_pktdump(m,b,n)
#endif
static void sam_reset_pipes(struct sam_usbhost_s *priv, bool warm_reset);
static void sam_pipe_reset(struct sam_usbhost_s *priv, uint8_t epno);
static void sam_pipeset_reset(struct sam_usbhost_s *priv, uint16_t epset);
static inline uint16_t sam_getle16(const uint8_t *val);
static int sam_pipe_alloc(struct sam_usbhost_s *priv);
static inline void sam_pipe_free(struct sam_usbhost_s *priv,
int idx);
static void sam_pipe_configure(struct sam_usbhost_s *priv, int idx);
static int sam_pipe_waitsetup(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe);
#ifdef CONFIG_USBHOST_ASYNCH
static int sam_pipe_asynchsetup(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
usbhost_asynch_t callback, void *arg);
#endif
static int sam_pipe_wait(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe);
static void sam_pipe_wakeup(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe);
static int sam_ctrlep_alloc(struct sam_usbhost_s *priv,
const struct usbhost_epdesc_s *epdesc,
usbhost_ep_t *ep);
static int sam_xfrep_alloc(struct sam_usbhost_s *priv,
const struct usbhost_epdesc_s *epdesc,
usbhost_ep_t *ep);
static void sam_transfer_terminate(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe, int result);
static void sam_transfer_abort(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe, int result);
static void sam_send_continue(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe);
static void sam_send_start(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe);
static ssize_t sam_out_transfer(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
uint8_t *buffer,
size_t buflen);
#ifdef CONFIG_USBHOST_ASYNCH
static void sam_out_next(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe);
static int sam_out_asynch(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
uint8_t *buffer, size_t buflen,
usbhost_asynch_t callback, void *arg);
#endif
static int sam_ctrl_sendsetup(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
const struct usb_ctrlreq_s *req);
static int sam_ctrl_senddata(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
uint8_t *buffer, unsigned int buflen);
static int sam_ctrl_recvdata(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
uint8_t *buffer, unsigned int buflen);
static int sam_in_setup(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe);
static int sam_out_setup(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe);
static void sam_recv_continue(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe);
static void sam_recv_restart(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe);
static void sam_recv_start(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe);
static ssize_t sam_in_transfer(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
uint8_t *buffer,
size_t buflen);
#ifdef CONFIG_USBHOST_ASYNCH
static void sam_in_next(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe);
static int sam_in_asynch(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
uint8_t *buffer, size_t buflen,
usbhost_asynch_t callback, void *arg);
#endif
static void sam_gint_connected(struct sam_usbhost_s *priv);
static void sam_gint_disconnected(struct sam_usbhost_s *priv);
static void sam_pipe_interrupt(struct sam_usbhost_s *priv, int idx);
static int sam_usbhost_interrupt(int irq, void *context, void *arg);
static int sam_wait(struct usbhost_connection_s *conn,
struct usbhost_hubport_s **hport);
static int sam_rh_enumerate(struct sam_usbhost_s *priv,
struct usbhost_connection_s *conn,
struct usbhost_hubport_s *hport);
static int sam_enumerate(struct usbhost_connection_s *conn,
struct usbhost_hubport_s *hport);
static int sam_ep0configure(struct usbhost_driver_s *drvr,
usbhost_ep_t ep0, uint8_t funcaddr,
uint8_t speed,
uint16_t maxpacketsize);
static int sam_epalloc(struct usbhost_driver_s *drvr,
const struct usbhost_epdesc_s *epdesc,
usbhost_ep_t *ep);
static int sam_epfree(struct usbhost_driver_s *drvr, usbhost_ep_t ep);
static int sam_alloc(struct usbhost_driver_s *drvr,
uint8_t **buffer, size_t *maxlen);
static int sam_free(struct usbhost_driver_s *drvr,
uint8_t *buffer);
static int sam_ioalloc(struct usbhost_driver_s *drvr,
uint8_t **buffer, size_t buflen);
static int sam_iofree(struct usbhost_driver_s *drvr,
uint8_t *buffer);
static int sam_ctrlin(struct usbhost_driver_s *drvr,
usbhost_ep_t ep0,
const struct usb_ctrlreq_s *req,
uint8_t *buffer);
static int sam_ctrlout(struct usbhost_driver_s *drvr,
usbhost_ep_t ep0,
const struct usb_ctrlreq_s *req,
const uint8_t *buffer);
static ssize_t sam_transfer(struct usbhost_driver_s *drvr,
usbhost_ep_t ep,
uint8_t *buffer, size_t buflen);
#ifdef CONFIG_USBHOST_ASYNCH
static int sam_asynch(struct usbhost_driver_s *drvr,
usbhost_ep_t ep,
uint8_t *buffer, size_t buflen,
usbhost_asynch_t callback, void *arg);
#endif
static int sam_cancel(struct usbhost_driver_s *drvr,
usbhost_ep_t ep);
#ifdef CONFIG_USBHOST_HUB
static int sam_connect(struct usbhost_driver_s *drvr,
struct usbhost_hubport_s *hport,
bool connected);
#endif
static void sam_disconnect(struct usbhost_driver_s *drvr,
struct usbhost_hubport_s *hport);
static void sam_hostreset(struct sam_usbhost_s *priv);
static void sam_add_sof_user(struct sam_usbhost_s *priv);
#endif
* Private Data
****************************************************************************/
#ifdef CONFIG_USBHOST
* USB host. All status information can be simply retained in a single global
* instance.
*/
static struct sam_usbhost_s g_usbhost =
{
.lock = NXMUTEX_INITIALIZER,
.pscsem = SEM_INITIALIZER(0),
};
static struct usbhost_connection_s g_usbconn =
{
.wait = sam_wait,
.enumerate = sam_enumerate,
};
#endif
#ifdef CONFIG_USBDEV
* 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
};
#endif
* 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(SAM_TRACEINTID_PENDING_PIPE),
TRACE_STR(SAM_TRACEINTID_PIPENO),
TRACE_STR_END
};
#endif
* Private Functions
****************************************************************************/
* Register Operations
****************************************************************************/
* Name: sam_printreg
*
* Description:
* Print the SAMD5E5 USB register access
*
****************************************************************************/
#ifdef CONFIG_SAMD5E5_USB_REGDEBUG
static void sam_printreg(uintptr_t regaddr, uint32_t regval, bool iswrite)
{
uinfo("%p%s0x%08x\n", regaddr, iswrite ? "<-" : "->", regval);
}
#endif
* Name: sam_checkreg
*
* Description:
* Check if it is time to output debug information for accesses
* to a SAMD5E5 USB registers.
*
****************************************************************************/
#ifdef CONFIG_SAMD5E5_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;
* to the same register 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_getreg32
*
* Description:
* Get the contents of an 32-bit SAMD5E5 USB register
*
****************************************************************************/
#ifdef CONFIG_SAMD5E5_USB_REGDEBUG
static uint32_t sam_getreg32(uintptr_t regaddr)
{
uint32_t regval = getreg32(regaddr);
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 SAMD5E5 USB register to a value
*
****************************************************************************/
#ifdef CONFIG_SAMD5E5_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 SAMD5E5 USB register
*
****************************************************************************/
#ifdef CONFIG_SAMD5E5_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 SAMD5E5 USB register to a value
*
****************************************************************************/
#ifdef CONFIG_SAMD5E5_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 SAMD5E5 USB register
*
****************************************************************************/
#ifdef CONFIG_SAMD5E5_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 SAMD5E5 USB register to a value
*
****************************************************************************/
#ifdef CONFIG_SAMD5E5_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_SAMD5E5_USB_REGDEBUG) && defined(CONFIG_DEBUG_USB)
#ifdef CONFIG_USBDEV
static void sam_dumpep(struct sam_usbdev_s *priv, uint8_t epno)
{
uinfo("Global Registers:\n");
uinfo(" CTRLB: 0x%04x\n", sam_getreg16(SAM_USBDEV_CTRLB));
uinfo(" FNUM: 0x%04x\n", sam_getreg16(SAM_USBDEV_FNUM));
uinfo(" DADD: 0x%02x\n", sam_getreg8(SAM_USBDEV_DADD));
uinfo(" INTENSET: 0x%04x\n", sam_getreg16(SAM_USBDEV_INTENSET));
uinfo(" STATUS: 0x%02x\n", sam_getreg8(SAM_USBDEV_STATUS));
uinfo(" INTFLAG: 0x%04x\n", sam_getreg16(SAM_USBDEV_INTFLAG));
uinfo(" EPCFG[%d]: 0x%02x\n", epno, sam_getreg8(SAM_USBDEV_EPCFG(epno)));
uinfo("EPSTATUS[%d]: 0x%02x\n", epno,
sam_getreg8(SAM_USBDEV_EPSTATUS(epno)));
}
#endif
#ifdef CONFIG_USBHOST
static void sam_dumppipe(struct sam_usbhost_s *priv, uint8_t epno)
{
uinfo("Global Host Registers:\n");
uinfo(" CTRLB: 0x%04x\n", sam_getreg16(SAM_USBHOST_CTRLB));
uinfo(" FNUM: 0x%04x\n", sam_getreg16(SAM_USBHOST_FNUM));
uinfo(" HSOFC: 0x%02x\n", sam_getreg8(SAM_USBHOST_HSOFC));
uinfo(" INTENSET: 0x%04x\n", sam_getreg16(SAM_USBHOST_INTENSET));
uinfo(" STATUS: 0x%02x\n", sam_getreg8(SAM_USBHOST_STATUS));
uinfo(" INTFLAG: 0x%04x\n", sam_getreg16(SAM_USBHOST_INTFLAG));
uinfo(" PIPECFG[%d]: 0x%02x\n", epno, sam_getreg8(SAM_USBHOST_PCFG(epno)));
uinfo(" PSTATUS[%d]: 0x%02x\n", epno,
sam_getreg8(SAM_USBHOST_PSTATUS(epno)));
}
#endif
#endif
* Name: sam_modifyreg8
*
* Description:
* Modify selected bits of an SAM register.
*
****************************************************************************/
static inline void sam_modifyreg8(uint32_t clrbits,
uint32_t setbits, uintptr_t regaddr)
{
sam_putreg8((((sam_getreg8(regaddr)) & (~clrbits)) | setbits), regaddr);
}
* Name: sam_enableclks
* Description:
* Enable USB clock
****************************************************************************/
static void sam_enableclks(void)
{
sam_gclk_chan_enable(GCLK_CHAN_USB, BOARD_USB_GCLKGEN, false);
sam_ahb_usb_enableperiph();
sam_apb_usb_enableperiph();
}
* 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))
;
}
}
#ifdef CONFIG_USBDEV
* Name: sam_disableclks
* Description:
* Disable USB clock
****************************************************************************/
static void sam_disableclks(void)
{
sam_gclk_chan_disable(GCLK_CHAN_USB);
sam_apb_usb_disableperiph();
}
* Request Helpers
****************************************************************************/
* 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;
uinfo("ENTRY\n");
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;
uinfo("addr=%p\n", 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 protocoll
*/
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];
uinfo("addr=%p\n", &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;
uinfo("addr=%p\n", 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: 0x%02x type: 0x%02x addr: 0x%02x attr: 0x%02x "
"maxpacketsize: 0x%02x%02x interval: 0x%02x\n",
desc->len, desc->type, desc->addr, desc->attr,
desc->mxpacketsize[1], desc->mxpacketsize[0],
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(0x7f, SAM_USBDEV_EPINTENCLR(epno));
sam_putreg8(0x7f, 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;
* 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);
}
* Endpoint operations
****************************************************************************/
* 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_zalloc(sizeof(struct sam_req_s));
if (!privreq)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_ALLOCFAIL), 0);
return NULL;
}
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);
}
* 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
* 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))
{
* queue?
*/
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;
}
* Device Controller Operations
****************************************************************************/
* 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 (!ep || !req)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#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;
}
* Suspend/Resume Helpers
****************************************************************************/
* Name: sam_suspend
****************************************************************************/
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);
}
}
* 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();
* particular board
*/
sam_usb_suspend((struct usbdev_s *)priv, true);
if (priv->driver)
{
CLASS_RESUME(priv->driver, &priv->usbdev);
}
}
}
* Initialization/Reset
****************************************************************************/
* 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;
priv->usbdev.dualspeed = 0;
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);
}
* Interrupt Level Processing
****************************************************************************/
* 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];
uinfo("addr=%p\n", &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)
{
uinfo("ENTRY address=0x%x\n", 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)
{
* recipients (except 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)
{
* except 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_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];
uinfo("addr=%p\n", &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
*
****************************************************************************/
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.
*/
}
if ((pending & USBDEV_INT_SOF) != 0)
{
sam_putreg16(USBDEV_INT_SOF, SAM_USBDEV_INTFLAG);
}
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;
priv->usbdev.dualspeed = 0;
}
return OK;
}
* Endpoint Helpers
****************************************************************************/
* 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(0x7f, SAM_USBDEV_EPINTENCLR(epno));
sam_putreg8(0x7f, 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_getreg16(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_putreg16((uint16_t)regval, SAM_USBDEV_CTRLB);
return OK;
}
* Initialization/Reset
****************************************************************************/
* 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;
sam_portconfig(PORT_USB_DP);
sam_portconfig(PORT_USB_DM);
* input,
*/
sam_enableclks();
sam_ctrla_write(USB_CTRLA_SWRST);
while (sam_getreg32(SAM_USB_SYNCBUSY) == USB_SYNCBUSY_SWRST);
sam_ctrla_write(USB_CTRLA_ENABLE | USB_CTRLA_RUNSTBY |
USB_CTRLA_MODE_DEVICE);
while (sam_getreg32(SAM_USB_SYNCBUSY) == USB_SYNCBUSY_ENABLE);
calib_transn = (getreg32(SAM_FUSES_USBTRANSN_ADDR) &
SAM_FUSES_USBTRANSN_MASK) >> SAM_FUSES_USBTRANSN_SHIFT;
if (calib_transn == 0 || calib_transn == 0x1f)
calib_transn = 0x9;
calib_transp = (getreg32(SAM_FUSES_USBTRANSP_ADDR) &
SAM_FUSES_USBTRANSP_ADDR) >> SAM_FUSES_USBTRANSP_SHIFT;
if (calib_transp == 0 || calib_transp == 0x1f)
calib_transp = 0x19;
calib_trim = (getreg32(SAM_FUSES_USBTRIM_ADDR) &
SAM_FUSES_USBTRIM_MASK) >> SAM_FUSES_USBTRIM_SHIFT;
if (calib_trim == 0 || calib_trim == 0x7)
calib_trim = 0x6;
padcalib = USB_PADCAL_TRANSP(calib_transp) |
USB_PADCAL_TRANSN(calib_transn) |
USB_PADCAL_TRIM(calib_trim);
sam_putreg16(padcalib, SAM_USB_PADCAL);
uinfo("PADCAL: 0x%x\n", padcalib);
* 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);
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;
uinfo("driver: 0x%x\n", driver);
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);
up_enable_irq(SAM_IRQ_USBSOF);
up_enable_irq(SAM_IRQ_USBTRCPT0);
up_enable_irq(SAM_IRQ_USBTRCPT1);
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;
priv->usbdev.dualspeed = 0;
}
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);
up_disable_irq(SAM_IRQ_USBSOF);
up_disable_irq(SAM_IRQ_USBTRCPT0);
up_disable_irq(SAM_IRQ_USBTRCPT1);
* 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;
}
void sam_usb_suspend(struct usbdev_s *dev, bool resume)
{
}
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;
uinfo("INIT\n");
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;
}
if (irq_attach(SAM_IRQ_USBSOF, sam_usb_interrupt, priv) != 0)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_IRQREGISTRATION),
(uint16_t)SAM_IRQ_USBSOF);
goto errout;
}
if (irq_attach(SAM_IRQ_USBTRCPT0, sam_usb_interrupt, priv) != 0)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_IRQREGISTRATION),
(uint16_t)SAM_IRQ_USBTRCPT0);
goto errout;
}
if (irq_attach(SAM_IRQ_USBTRCPT1, sam_usb_interrupt, priv) != 0)
{
usbtrace(TRACE_DEVERROR(SAM_TRACEERR_IRQREGISTRATION),
(uint16_t)SAM_IRQ_USBTRCPT1);
goto errout;
}
return;
errout:
arm_usbuninitialize();
}
#endif
#ifdef CONFIG_USBHOST
* Name: sam_getle16
*
* Description:
* Get a (possibly unaligned) 16-bit little endian value.
*
****************************************************************************/
static inline uint16_t sam_getle16(const uint8_t *val)
{
return (uint16_t)val[1] << 8 | (uint16_t)val[0];
}
* Name: sam_add_sof_user
*
* Description:
* Add one SOF IRQ user and enable SOF interrupt
*
****************************************************************************/
static void sam_add_sof_user(struct sam_usbhost_s *priv)
{
priv->n_sof_user++;
sam_putreg16(USBHOST_INT_HSOF, SAM_USBHOST_INTENSET);
}
* Name: sam_pipe_alloc
*
* Description:
* Allocate a pipe.
*
****************************************************************************/
static int sam_pipe_alloc(struct sam_usbhost_s *priv)
{
int idx;
for (idx = 0; idx < SAM_USB_NENDPOINTS; idx++)
{
if (!priv->pipelist[idx].inuse)
{
priv->pipelist[idx].inuse = true;
return idx;
}
}
return -EBUSY;
}
* Name: sam_pipe_free
*
* Description:
* Free a previoiusly allocated pipe.
*
****************************************************************************/
static void sam_pipe_free(struct sam_usbhost_s *priv, int idx)
{
struct sam_pipe_s *pipe = &priv->pipelist[idx];
uinfo("pipe%d\n", idx);
DEBUGASSERT((unsigned)idx < SAM_USB_NENDPOINTS);
sam_putreg8(0, SAM_USBHOST_PCFG(pipe->idx));
priv->pipelist[idx].inuse = false;
}
static const uint16_t psize_2_size[] =
{
8,
16,
32,
64,
128,
256,
512,
1024
};
* Name: sam_get_psize
*
* Description:
* Convert bank size of bytes to PIPCFG.PSIZE -> size Size of bytes
*
****************************************************************************/
int8_t sam_get_psize(uint16_t size)
{
uint8_t i;
for (i = 0; i < sizeof(psize_2_size) / sizeof(uint16_t); i++)
{
if (size <= psize_2_size[i])
return i;
}
return 7;
}
* Name: sam_pipe_configure
*
* Description:
* Configure or re-configure a host pipe. Host pipes are configured
* when pipe is allocated and EP0 (only) is re-configured with the
* max packet size or device address changes.
*
****************************************************************************/
static void sam_pipe_configure(struct sam_usbhost_s *priv, int idx)
{
struct sam_pipe_s *pipe = &priv->pipelist[idx];
sam_putreg8(0x3f, SAM_USBHOST_PINTFLAG(pipe->idx));
switch (pipe->eptype)
{
case USB_EP_ATTR_XFER_CONTROL:
case USB_EP_ATTR_XFER_BULK:
{
#ifdef HAVE_USBHOST_TRACE_VERBOSE
uint16_t intrace;
uint16_t outtrace;
if (pipe->eptype == USB_EP_ATTR_XFER_CONTROL)
{
intrace = SAM_VTRACE2_PIPECONF_CTRL_IN;
outtrace = SAM_VTRACE2_PIPECONF_CTRL_OUT;
}
else
{
intrace = SAM_VTRACE2_PIPECONF_BULK_IN;
outtrace = SAM_VTRACE2_PIPECONF_BULK_OUT;
}
if (pipe->in)
{
usbhost_vtrace2(intrace, idx, pipe->epno);
}
else
{
usbhost_vtrace2(outtrace, idx, pipe->epno);
}
#endif
}
break;
case USB_EP_ATTR_XFER_INT:
{
#ifdef HAVE_USBHOST_TRACE_VERBOSE
if (pipe->in)
{
usbhost_vtrace2(SAM_VTRACE2_PIPECONF_INTR_IN, idx, pipe->epno);
}
else
{
usbhost_vtrace2(SAM_VTRACE2_PIPECONF_INTR_OUT, idx, pipe->epno);
}
#endif
}
break;
case USB_EP_ATTR_XFER_ISOC:
{
#ifdef HAVE_USBHOST_TRACE_VERBOSE
if (pipe->in)
{
usbhost_vtrace2(SAM_VTRACE2_PIPECONF_ISOC_IN, idx, pipe->epno);
}
else
{
usbhost_vtrace2(SAM_VTRACE2_PIPECONF_ISOC_OUT, idx, pipe->epno);
}
#endif
}
break;
}
pipe->descb[0]->ctrlpipe = USBHOST_CTRLPIPE_PDADDR(pipe->funcaddr) |
USBHOST_CTRLPIPE_PEPNUM(pipe->epno & USB_EPNO_MASK);
pipe->descb[0]->pktsize = USBHOST_PKTSIZE_SIZE(
sam_get_psize(pipe->maxpacket));
sam_putreg8(USBHOST_PCFG_PTYPE(pipe->eptype + 1) |
(pipe->eptype == USB_EP_ATTR_XFER_CONTROL ?
USBHOST_PCFG_PTOKEN_SETUP :
(pipe->in ? USBHOST_PCFG_PTOKEN_IN :
USBHOST_PCFG_PTOKEN_OUT)),
SAM_USBHOST_PCFG(pipe->idx));
sam_putreg8(pipe->interval, SAM_USBHOST_BINTERVAL(pipe->idx));
pipe->pipestatus_general = 0;
sam_putreg8((USBHOST_PINTFLAG_TRFAIL |
USBHOST_PINTFLAG_PERR |
USBHOST_PINTFLAG_STALL), SAM_USBHOST_PINTFLAG(pipe->idx));
sam_putreg8((USBHOST_PINTFLAG_TRFAIL |
USBHOST_PINTFLAG_PERR |
USBHOST_PINTFLAG_STALL), SAM_USBHOST_PINTENSET(pipe->idx));
pipe->pipestate_general = USB_H_PIPE_S_IDLE;
uinfo("pipe%d pktsize=0x%x ctrl=0x%x status=0x%x\n", pipe->idx,
pipe->descb[0]->pktsize,
pipe->descb[0]->ctrlpipe,
pipe->descb[0]->statuspipe);
}
* Name: sam_pipe_waitsetup
*
* Description:
* Set the request for the transfer complete event well
* BEFORE enabling the transfer (as soon as we are
* absolutely committed to the to avoid transfer).
* We do this to minimize race conditions.
* This logic would have to be expanded
* if we want to have more than one packet in flight at a time!
*
* Assumptions:
* Called from a normal thread context BEFORE the transfer has been started.
*
****************************************************************************/
static int sam_pipe_waitsetup(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe)
{
irqstate_t flags = enter_critical_section();
int ret = -ENODEV;
DEBUGASSERT(priv != NULL && pipe != NULL);
if (priv->connected)
{
* to be informed when either (1) the device is disconnected,
* or (2) the transfer completed.
*/
pipe->waiter = true;
#ifdef CONFIG_USBHOST_ASYNCH
pipe->callback = NULL;
pipe->arg = NULL;
#endif
ret = OK;
}
leave_critical_section(flags);
return ret;
}
* Name: sam_pipe_asynchsetup
*
* Description:
* Set the request for the transfer complete event well BEFORE enabling the
* transfer.
* We do this to minimize race conditions.
* This logic would have to be expanded
* if we want to have more than one packet in flight at a time!
*
* Assumptions:
* Might be called from the level of an interrupt handler
*
****************************************************************************/
#ifdef CONFIG_USBHOST_ASYNCH
static int sam_pipe_asynchsetup(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
usbhost_asynch_t callback, void *arg)
{
irqstate_t flags = enter_critical_section();
int ret = -ENODEV;
DEBUGASSERT(priv != NULL && pipe != NULL);
if (priv->connected)
{
pipe->waiter = false;
pipe->callback = callback;
pipe->arg = arg;
ret = OK;
}
leave_critical_section(flags);
return ret;
}
#endif
* Name: sam_pipe_wait
*
* Description:
* Wait for a transfer on a pipe to complete.
*
* Assumptions:
* Called from a normal thread context
*
****************************************************************************/
static int sam_pipe_wait(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe)
{
irqstate_t flags;
int ret;
* the host global interrupt needs to be disabled. However, here we disable
* all interrupts to exploit that fact that interrupts will be re-enabled
* while we wait.
*/
flags = enter_critical_section();
* was set to EBUSY and 'waiter' was set to the pipe expecting the
* response before the transfer was started; 'waiter' will be nullified
* and 'result' will be set appropriately when the transfer is completed.
*/
do
{
* completed before we get here or the transfer may complete while we
* wait here.
*/
ret = nxsem_wait(&pipe->waitsem);
* awakened by a signal too.
*/
DEBUGASSERT(ret == OK || ret == -EINTR);
}
while (pipe->waiter);
ret = -(int)pipe->result;
leave_critical_section(flags);
return ret;
}
* Name: sam_pipe_wakeup
*
* Description:
* A pipe transfer has completed... wakeup any threads waiting for the
* transfer to complete.
*
* Assumptions:
* This function is called from the transfer complete interrupt handler for
* the pipe. Interrupts are disabled.
*
****************************************************************************/
static void sam_pipe_wakeup(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe)
{
if (pipe->result != EBUSY)
{
if (pipe->waiter)
{
#ifdef CONFIG_USBHOST_ASYNCH
DEBUGASSERT(pipe->callback == NULL);
#endif
usbhost_vtrace2(pipe->in ? SAM_VTRACE2_PIPEWAKEUP_IN :
SAM_VTRACE2_PIPEWAKEUP_OUT,
pipe->epno, pipe->result);
nxsem_post(&pipe->waitsem);
pipe->waiter = false;
}
#ifdef CONFIG_USBHOST_ASYNCH
* when the transfer completes?
*/
else if (pipe->callback)
{
if (pipe->in)
{
sam_in_next(priv, pipe);
}
else
{
sam_out_next(priv, pipe);
}
}
#endif
}
}
* Name: sam_ctrlep_alloc
*
* Description:
* Allocate a container and pipes for control pipe.
*
* Input Parameters:
* priv - The private USB host driver state.
* epdesc - Describes the endpoint to be allocated.
* ep - A memory location provided by the caller in which to receive the
* allocated endpoint descriptor.
*
* Returned Value:
* On success, zero (OK) is returned. On a failure, a negated errno value
* is returned indicating the nature of the failure
*
* Assumptions:
* This function will *not* be called from an interrupt handler.
*
****************************************************************************/
static int sam_ctrlep_alloc(struct sam_usbhost_s *priv,
const struct usbhost_epdesc_s *epdesc,
usbhost_ep_t *ep)
{
struct usbhost_hubport_s *hport;
struct sam_pipe_s *pipe;
int idx;
* a device is connected (because we need a valid low speed indication).
*/
DEBUGASSERT(epdesc->hport != NULL);
hport = epdesc->hport;
idx = sam_pipe_alloc(priv);
if (idx < 0)
{
usbhost_trace1(SAM_TRACE1_PIPEALLOC_FAIL, -idx);
uerr("ERROR: Failed to allocate a host pipe\n");
return -ENOMEM;
}
pipe = &priv->pipelist[idx];
pipe->epno = epdesc->addr & USB_EPNO_MASK;
pipe->in = false;
pipe->eptype = USB_EP_ATTR_XFER_CONTROL;
pipe->funcaddr = hport->funcaddr;
pipe->speed = hport->speed;
pipe->interval = 0;
pipe->maxpacket = SAM_EP0_MAXPACKET;
pipe->pipestate_general = USB_H_PIPE_S_CFG;
sam_pipe_configure(priv, idx);
*ep = (usbhost_ep_t)idx;
return OK;
}
* Name: sam_xfrep_alloc
*
* Description:
* Allocate and configure one unidirectional pipe.
*
* Input Parameters:
* priv - The private USB host driver state.
* epdesc - Describes the endpoint to be allocated.
* ep - A memory location provided by the caller in which to receive the
* allocated endpoint descriptor.
*
* Returned Value:
* On success, zero (OK) is returned. On a failure, a negated
* errno value is returned indicating the nature of the failure
*
* Assumptions:
* This function will *not* be called from an interrupt handler.
*
****************************************************************************/
static int sam_xfrep_alloc(struct sam_usbhost_s *priv,
const struct usbhost_epdesc_s *epdesc,
usbhost_ep_t *ep)
{
struct usbhost_hubport_s *hport;
struct sam_pipe_s *pipe;
int idx;
* is connected (because we need a valid low speed indication).
*/
DEBUGASSERT(epdesc->hport != NULL);
hport = epdesc->hport;
DEBUGASSERT(hport != NULL);
idx = sam_pipe_alloc(priv);
if (idx < 0)
{
usbhost_trace1(SAM_TRACE1_PIPEALLOC_FAIL, -idx);
uerr("ERROR: Failed to allocate a host pipe\n");
return -ENOMEM;
}
* Note: Here we depend on the fact that the endpoint point type is
* encoded in the same way in the endpoint descriptor as it is in the OTG
* HS hardware.
*/
pipe = &priv->pipelist[idx];
pipe->epno = epdesc->addr & USB_EPNO_MASK;
pipe->in = epdesc->in;
pipe->eptype = epdesc->xfrtype;
pipe->funcaddr = hport->funcaddr;
pipe->speed = hport->speed;
pipe->interval = epdesc->interval;
pipe->maxpacket = epdesc->mxpacketsize;
pipe->pipestate_general = pipe->in ? USB_H_PIPE_S_DATI : USB_H_PIPE_S_DATO;
sam_pipe_configure(priv, idx);
*ep = (usbhost_ep_t)idx;
return OK;
}
* Name: sam_transfer_terminate
*
* Description:
* Terminate a IN or OUT transfer due to an error (or because a zero-
* length OUT transfer occurred).
*
* Returned value:
* OK - Transfer successful
* -EAGAIN - If devices NAKs the transfer.
* -EPERM - If the endpoint stalls
* -BUSY - The transfer is not complete
* -EIO - Other, undecoded error
*
* Assumptions:
*
****************************************************************************/
static void sam_transfer_terminate(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
int result)
{
sam_pipe_wakeup(priv, pipe);
if (pipe->pipestate_general < USB_H_PIPE_S_SETUP ||
pipe->pipestate_general > USB_H_PIPE_S_STATO)
return;
if (pipe->eptype == USB_EP_ATTR_XFER_CONTROL)
{
if (priv->n_ctrl_req_user)
priv->n_ctrl_req_user--;
if (priv->n_sof_user)
priv->n_sof_user--;
}
pipe->pipestate_general = USB_H_PIPE_S_IDLE;
pipe->pipestatus_general = result;
if (priv->n_ctrl_req_user == 0 && priv->suspend_start < 0)
{
uint8_t i;
if (priv->n_ctrl_req_user)
{
priv->suspend_start = -1;
return;
}
priv->pipes_unfreeze = 0;
for (i = 0; i < SAM_USB_NENDPOINTS; i++)
{
if ((sam_getreg16(SAM_USBHOST_PSTATUS(i)) &
USBHOST_PSTATUS_PFREEZE) >> 4)
continue;
priv->pipes_unfreeze |= 1 << i;
sam_putreg8(USBHOST_PSTATUS_PFREEZE, SAM_USBHOST_PSTATUSSET(i));
}
sam_add_sof_user(priv);
priv->suspend_start = 3;
}
}
static void sam_transfer_abort(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
int code)
{
sam_putreg8(USBHOST_PSTATUS_PFREEZE, SAM_USBHOST_PSTATUSSET(pipe->idx));
if (pipe->in == 0)
pipe->count += (pipe->descb[0]->pktsize &
USBHOST_PKTSIZE_MPKTSIZE_MASK) >>
USBHOST_PKTSIZE_MPKTSIZE_SHIFT;
sam_putreg8((USBHOST_PINTFLAG_TRCPT0 |
USBHOST_PINTFLAG_TRCPT1),
SAM_USBHOST_PINTENCLR(pipe->idx));
sam_transfer_terminate(priv, pipe, code);
}
* Name: sam_send_continue
*
* Description:
* Continue the send operation started by sam_send_start().
*
* Assumptions:
*
****************************************************************************/
static void sam_send_continue(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe)
{
uint8_t * src;
uint32_t size;
uint32_t count;
uint32_t n_tx = 0;
uint32_t n_remain;
if (pipe->pipestate_general == USB_H_PIPE_S_STATO)
{
sam_transfer_terminate(priv, pipe, OK);
return;
}
else if (pipe->pipestate_general != USB_H_PIPE_S_DATO)
return;
if (pipe->eptype == USB_EP_ATTR_XFER_CONTROL)
pipe->pkt_timeout = USB_CTRL_DPKT_TIMEOUT;
n_tx = (pipe->descb[0]->pktsize & USBHOST_PKTSIZE_BCNT_MASK) >>
USBHOST_PKTSIZE_BCNT_SHIFT;
if (n_tx < pipe->maxpacket)
pipe->zlp = 0;
src = pipe->data;
size = pipe->size;
count = pipe->count;
if (n_tx)
{
count += n_tx;
pipe->count = count;
}
n_remain = size - count;
if (n_remain > 16320)
n_tx = 16320;
else
n_tx = n_remain;
if (pipe->eptype == USB_EP_ATTR_XFER_CONTROL &&
pipe->count >= pipe->size &&
!pipe->zlp)
{
pipe->pipestate = USB_H_PIPE_S_STATI;
pipe->pkt_timeout = USB_CTRL_STAT_TIMEOUT;
sam_putreg8(USBHOST_PSTATUS_DTGL, SAM_USBHOST_PSTATUSSET(pipe->idx));
sam_recv_restart(priv, pipe);
return;
}
if (count >= size && !pipe->zlp)
{
if (pipe->eptype != USB_EP_ATTR_XFER_CONTROL)
{
sam_transfer_terminate(priv, pipe, OK);
}
else
{
}
}
else
{
sam_putreg8((USBHOST_PINTFLAG_TRCPT0 |
USBHOST_PINTFLAG_TRCPT1),
SAM_USBHOST_PINTFLAG(pipe->idx));
sam_putreg8((USBHOST_PINTFLAG_TRCPT0 |
USBHOST_PINTFLAG_TRCPT1),
SAM_USBHOST_PINTENSET(pipe->idx));
pipe->descb[0]->addr = (uint32_t)&src[count];
pipe->descb[0]->pktsize &= ~(USBHOST_PKTSIZE_MPKTSIZE_MASK |
USBHOST_PKTSIZE_BCNT_MASK);
pipe->descb[0]->pktsize |= USBHOST_PKTSIZE_BCNT(n_remain);
sam_modifyreg8(USBHOST_PCFG_PTOKEN_MASK,
USBHOST_PCFG_PTOKEN_OUT,
SAM_USBHOST_PCFG(pipe->idx));
sam_putreg8(USBHOST_PSTATUS_BK0RDY,
SAM_USBHOST_PSTATUSSET(pipe->idx));
sam_putreg8(USBHOST_PSTATUS_PFREEZE,
SAM_USBHOST_PSTATUSCLR(pipe->idx));
}
}
* Name: sam_send_start
*
* Description:
* Start at transfer on the selected IN or OUT pipe.
*
****************************************************************************/
static void sam_send_start(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe)
{
usbhost_vtrace2(SAM_VTRACE2_STARTTRANSFER1, pipe->idx, pipe->size);
pipe->result = EBUSY;
pipe->count = 0;
pipe->descb[0]->ctrlpipe &= ~USBHOST_CTRLPIPE_PDADDR_MASK;
pipe->descb[0]->ctrlpipe |= USBHOST_CTRLPIPE_PDADDR(pipe->funcaddr);
if (pipe->size > 0)
{
sam_putreg8((USBHOST_PINTFLAG_TRCPT0 |
USBHOST_PINTFLAG_TRCPT1),
SAM_USBHOST_PINTFLAG(pipe->idx));
sam_putreg8((USBHOST_PINTFLAG_TRCPT0 |
USBHOST_PINTFLAG_TRCPT1),
SAM_USBHOST_PINTENSET(pipe->idx));
pipe->descb[0]->addr = (uint32_t)pipe->data;
pipe->descb[0]->pktsize &= ~(USBHOST_PKTSIZE_MPKTSIZE_MASK |
USBHOST_PKTSIZE_BCNT_MASK);
pipe->descb[0]->pktsize |= USBHOST_PKTSIZE_BCNT(pipe->size);
sam_modifyreg8(USBHOST_PCFG_PTOKEN_MASK,
USBHOST_PCFG_PTOKEN_OUT,
SAM_USBHOST_PCFG(pipe->idx));
sam_putreg8(USBHOST_PSTATUS_BK0RDY,
SAM_USBHOST_PSTATUSSET(pipe->idx));
sam_putreg8(USBHOST_PSTATUS_PFREEZE,
SAM_USBHOST_PSTATUSCLR(pipe->idx));
}
else
{
sam_putreg8((USBHOST_PINTFLAG_TRCPT0 |
USBHOST_PINTFLAG_TRCPT1),
SAM_USBHOST_PINTFLAG(pipe->idx));
sam_putreg8((USBHOST_PINTFLAG_TRCPT0 |
USBHOST_PINTFLAG_TRCPT1),
SAM_USBHOST_PINTENSET(pipe->idx));
sam_putreg8(USBHOST_PSTATUS_DTGL,
SAM_USBHOST_PSTATUSSET(pipe->idx));
pipe->descb[0]->addr = (uint32_t)priv->ctrl_buffer;
pipe->descb[0]->pktsize &= ~(USBHOST_PKTSIZE_MPKTSIZE_MASK |
USBHOST_PKTSIZE_BCNT_MASK);
pipe->descb[0]->pktsize |= USBHOST_PKTSIZE_AUTOZLP;
sam_modifyreg8(USBHOST_PCFG_PTOKEN_MASK,
USBHOST_PCFG_PTOKEN_OUT,
SAM_USBHOST_PCFG(pipe->idx));
sam_putreg8(USBHOST_PSTATUS_BK0RDY,
SAM_USBHOST_PSTATUSSET(pipe->idx));
sam_putreg8(USBHOST_PSTATUS_PFREEZE,
SAM_USBHOST_PSTATUSCLR(pipe->idx));
}
}
* Name: sam_out_transfer
*
* Description:
* Transfer the 'buflen' bytes in 'buffer' through an OUT pipe.
*
* Assumptions:
* This function is called only from the TRANSFER
* interface. The lock, for example,
* must be relinquished before waiting.
*
****************************************************************************/
static ssize_t sam_out_transfer(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
uint8_t *buffer, size_t buflen)
{
clock_t start;
clock_t elapsed;
size_t xfrlen;
ssize_t xfrd;
int ret;
* or a fatal error occurs (any error other than a simple NAK)
*/
start = clock_systime_ticks();
xfrd = 0;
while (buflen > 0)
{
* multiple OUT packets, but I just haven't figured out how to handle
* the case where a single OUT packet in the group is NAKed.
*/
xfrlen = MIN(pipe->maxpacket, buflen);
pipe->data = buffer;
pipe->size = xfrlen;
pipe->count = 0;
uinfo("pipe%d buffer:%p buflen:%d\n",
pipe->idx,
pipe->data,
pipe->size);
ret = sam_pipe_waitsetup(priv, pipe);
if (ret < 0)
{
usbhost_trace1(SAM_TRACE1_DEVDISCONN1, 0);
return (ssize_t)ret;
}
* on the direction and the endpoint type
*/
ret = sam_out_setup(priv, pipe);
if (ret < 0)
{
usbhost_trace1(SAM_TRACE1_OUTSETUP_FAIL1, -ret);
return (ssize_t)ret;
}
ret = sam_pipe_wait(priv, pipe);
if (ret < 0)
{
usbhost_trace1(SAM_TRACE1_TRANSFER_FAILED1, ret);
* no SNDFIFO empty or Rx FIFO not-empty event occurred, then we
* should be able to just flush the Rx and SNDFIFOs and try again.
* We can detect this latter case because then the transfer buffer
* pointer and buffer size will be unaltered.
*/
elapsed = clock_systime_ticks() - start;
if (ret != -EAGAIN ||
elapsed >= SAM_DATANAK_DELAY ||
pipe->count > 0)
{
usbhost_trace1(SAM_TRACE1_PIPEWAIT_FAIL, -ret);
return (ssize_t)ret;
}
* Then retry the transfer
* using the same buffer pointer and length.
*/
nxsig_usleep(20 * 1000);
}
else
{
buffer += xfrlen;
buflen -= xfrlen;
xfrd += pipe->count;
}
}
return xfrd;
}
* Name: sam_out_next
*
* Description:
* Initiate the next of a sequence of asynchronous transfers.
*
* Assumptions:
* This function is always called from an interrupt handler
*
****************************************************************************/
#ifdef CONFIG_USBHOST_ASYNCH
static void sam_out_next(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe)
{
usbhost_asynch_t callback;
void *arg;
ssize_t nbytes;
int result;
int ret;
* Did the last chunk transfer complete OK?
*/
result = -(int)pipe->result;
if (pipe->count < pipe->size && result == OK)
{
* endpoint type
*/
ret = sam_out_setup(priv, pipe);
if (ret >= 0)
{
return;
}
usbhost_trace1(SAM_TRACE1_OUTSETUP_FAIL2, -ret);
result = ret;
}
usbhost_vtrace1(SAM_VTRACE1_TRANSFER_COMPLETE, result);
callback = pipe->callback;
arg = pipe->arg;
nbytes = pipe->count;
pipe->callback = NULL;
pipe->arg = NULL;
pipe->count = 0;
if (result < 0)
{
nbytes = (ssize_t)result;
}
callback(arg, nbytes);
}
#endif
* Name: sam_out_asynch
*
* Description:
* Initiate the first of a sequence of asynchronous transfers.
*
* Assumptions:
* This function is called only from the ASYNCH.
* The lock, for example, must be relinquished before waiting.
*
****************************************************************************/
#ifdef CONFIG_USBHOST_ASYNCH
static int sam_out_asynch(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
uint8_t *buffer, size_t buflen,
usbhost_asynch_t callback, void *arg)
{
int ret;
* BEFORE starting the first transfer
*/
pipe->data = buffer;
pipe->size = buflen;
pipe->count = 0;
ret = sam_pipe_asynchsetup(priv, pipe, callback, arg);
if (ret < 0)
{
usbhost_trace1(SAM_TRACE1_ASYNCHSETUP_FAIL1, -ret);
return ret;
}
* direction and the endpoint type
*/
ret = sam_out_setup(priv, pipe);
if (ret < 0)
{
usbhost_trace1(SAM_TRACE1_OUTSETUP_FAIL3, -ret);
}
return ret;
}
#endif
* Name: sam_ctrl_sendsetup
*
* Description:
* Send an IN/OUT SETUP packet.
*
* Assumptions:
* This function is called only from the CTRLIN and CTRLOUT interfaces.
*
****************************************************************************/
static int sam_ctrl_sendsetup(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
const struct usb_ctrlreq_s *req)
{
clock_t start;
clock_t elapsed;
int ret;
int i;
start = clock_systime_ticks();
do
{
pipe->data = (uint8_t *)req;
pipe->size = USB_SIZEOF_CTRLREQ;
pipe->count = 0;
pipe->result = EBUSY;
uinfo("pipe%d buffer:%p buflen:%d\n",
pipe->idx,
pipe->data,
pipe->size);
sam_pktdump("sam_ctrl_sendsetup", pipe->data, pipe->size);
ret = sam_pipe_waitsetup(priv, pipe);
if (ret < 0)
{
usbhost_trace1(SAM_TRACE1_DEVDISCONN2, 0);
return ret;
}
pipe->pipestate_general = USB_H_PIPE_S_SETUP;
sam_add_sof_user(priv);
priv->n_ctrl_req_user++;
pipe->descb[0]->ctrlpipe &= ~USBHOST_CTRLPIPE_PDADDR_MASK;
pipe->descb[0]->ctrlpipe |= USBHOST_CTRLPIPE_PDADDR(pipe->funcaddr);
sam_putreg8(USBHOST_PINTFLAG_TXSTP, SAM_USBHOST_PINTFLAG(pipe->idx));
for (i = 0; i < USB_SIZEOF_CTRLREQ; i++)
priv->ctrl_buffer[i] = pipe->data[i];
pipe->descb[0]->addr = (uint32_t)pipe->data;
pipe->descb[0]->pktsize &= ~(USBHOST_PKTSIZE_MPKTSIZE_MASK |
USBHOST_PKTSIZE_BCNT_MASK);
pipe->descb[0]->pktsize |= USBHOST_PKTSIZE_BCNT(USB_SIZEOF_CTRLREQ);
pipe->descb[0]->ctrlpipe = USBHOST_CTRLPIPE_PDADDR(pipe->funcaddr) |
USBHOST_CTRLPIPE_PEPNUM(pipe->epno & USB_EPNO_MASK);
uinfo("pipe%d pktsize=0x%x ctrl=0x%x status=0x%x\n",
pipe->idx,
pipe->descb[0]->pktsize,
pipe->descb[0]->ctrlpipe,
pipe->descb[0]->statuspipe);
sam_modifyreg8(USBHOST_PCFG_PTOKEN_MASK,
USBHOST_PCFG_PTOKEN_SETUP,
SAM_USBHOST_PCFG(pipe->idx));
sam_putreg8(USBHOST_PSTATUS_DTGL, SAM_USBHOST_PSTATUSCLR(pipe->idx));
sam_putreg8(USBHOST_PSTATUS_BK0RDY, SAM_USBHOST_PSTATUSSET(pipe->idx));
sam_putreg8(USBHOST_PINTFLAG_TXSTP, SAM_USBHOST_PINTENSET(pipe->idx));
sam_putreg8(USBHOST_PSTATUS_PFREEZE,
SAM_USBHOST_PSTATUSCLR(pipe->idx));
ret = sam_pipe_wait(priv, pipe);
* means that the device NAKed the SETUP command and that we should
* try a few more times. NOTE: The USB spec says that a peripheral
* must always ACK a SETUP packet.
*/
if (ret != -EAGAIN)
{
if (ret < 0)
{
usbhost_trace1(SAM_TRACE1_TRANSFER_FAILED2, ret);
}
return ret;
}
elapsed = clock_systime_ticks() - start;
}
while (elapsed < SAM_SETUP_DELAY);
return -ETIMEDOUT;
}
* Name: sam_ctrl_senddata
*
* Description:
* Send data in the data phase of an OUT control transfer. Or send status
* in the status phase of an IN control transfer
*
* Assumptions:
* This function is called only from the CTRLOUT interface.
*
****************************************************************************/
static int sam_ctrl_senddata(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
uint8_t *buffer, unsigned int buflen)
{
int ret;
uinfo("pipe%d buffer:%p buflen:%d\n", pipe->idx, buffer, buflen);
pipe->pipestate_general = USB_H_PIPE_S_DATO;
pipe->in = false;
pipe->data = buffer;
pipe->size = buflen;
pipe->count = 0;
ret = sam_pipe_waitsetup(priv, pipe);
if (ret < 0)
{
usbhost_trace1(SAM_TRACE1_DEVDISCONN3, 0);
return ret;
}
sam_send_start(priv, pipe);
return sam_pipe_wait(priv, pipe);
}
* Name: sam_ctrl_recvdata
*
* Description:
* Receive data in the data phase of an IN control transfer.
* Or receive status in the status phase of
* an OUT control transfer.
*
* Assumptions:
* This function is called only from the CTRLIN interface.
*
****************************************************************************/
static int sam_ctrl_recvdata(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
uint8_t *buffer, unsigned int buflen)
{
int ret;
pipe->pipestate_general = USB_H_PIPE_S_DATI;
pipe->in = true;
pipe->data = buffer;
pipe->size = buflen;
pipe->count = 0;
ret = sam_pipe_waitsetup(priv, pipe);
if (ret < 0)
{
usbhost_trace1(SAM_TRACE1_DEVDISCONN4, 0);
return ret;
}
sam_recv_start(priv, pipe);
ret = sam_pipe_wait(priv, pipe);
uinfo("pipe%d buffer:%p buflen:%d ADDR=0x%x PKTSIZE=0x%x\n",
pipe->idx, buffer, buflen,
pipe->descb[0]->addr,
pipe->descb[0]->pktsize);
uinfo("EXTREG=0x%x STATUSBK=0x%x CTRLPIPE=0x%x STATUSPIPE=0x%x\n",
pipe->descb[0]->extreg,
pipe->descb[0]->stausbk,
pipe->descb[0]->ctrlpipe,
pipe->descb[0]->statuspipe);
sam_pktdump("sam_ctrl_recvdata", pipe->data, pipe->size);
return ret;
}
* Name: sam_in_setup
*
* Description:
* Initiate an IN transfer on an bulk, interrupt, or isochronous pipe.
*
****************************************************************************/
static int sam_in_setup(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe)
{
uinfo("pipe%d\n", pipe->idx);
switch (pipe->eptype)
{
default:
case USB_EP_ATTR_XFER_CONTROL:
{
* not currently supported
*/
return -ENOSYS;
}
case USB_EP_ATTR_XFER_ISOC:
{
usbhost_vtrace2(SAM_VTRACE2_ISOCIN, pipe->idx, pipe->size);
}
break;
case USB_EP_ATTR_XFER_BULK:
{
usbhost_vtrace2(SAM_VTRACE2_BULKIN, pipe->idx, pipe->size);
pipe->pipestate_general = pipe->in ?
USB_H_PIPE_S_DATI : USB_H_PIPE_S_DATO;
}
break;
case USB_EP_ATTR_XFER_INT:
{
usbhost_vtrace2(SAM_VTRACE2_INTRIN, pipe->idx, pipe->size);
}
break;
}
sam_recv_start(priv, pipe);
return OK;
}
* Name: sam_out_setup
*
* Description:
* Initiate an OUT transfer on an bulk, interrupt, or isochronous pipe.
*
****************************************************************************/
static int sam_out_setup(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe)
{
switch (pipe->eptype)
{
default:
case USB_EP_ATTR_XFER_CONTROL:
{
* not currently supported
*/
return -ENOSYS;
}
case USB_EP_ATTR_XFER_ISOC:
{
usbhost_vtrace2(SAM_VTRACE2_ISOCOUT,
pipe->idx, pipe->size);
}
break;
case USB_EP_ATTR_XFER_BULK:
{
usbhost_vtrace2(SAM_VTRACE2_BULKOUT,
pipe->idx, pipe->size);
pipe->pipestate_general = pipe->in ?
USB_H_PIPE_S_DATI : USB_H_PIPE_S_DATO;
}
break;
case USB_EP_ATTR_XFER_INT:
{
usbhost_vtrace2(SAM_VTRACE2_INTROUT,
pipe->idx, pipe->size);
}
break;
}
sam_send_start(priv, pipe);
return OK;
}
* Name: sam_recv_continue
*
* Description:
* Continue the receive operation started by sam_recv_start(). This
* function is called from the interrupt handler worker when an interrupt
* indicates that new, incoming data is available
*
* Assumptions:
*
****************************************************************************/
static void sam_recv_continue(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe)
{
uint8_t *src;
uint8_t *dst;
uint32_t size;
uint32_t count;
uint32_t i;
uint32_t n_rx = 0;
uint32_t n_remain;
bool shortpkt = false;
bool full = false;
if (pipe->pipestate_general == USB_H_PIPE_S_STATI)
{
sam_transfer_terminate(priv, pipe, OK);
return;
}
else if (pipe->pipestate_general != USB_H_PIPE_S_DATI)
return;
n_rx = (pipe->descb[0]->pktsize & USBHOST_PKTSIZE_BCNT_MASK) >>
USBHOST_PKTSIZE_BCNT_SHIFT;
if (n_rx < pipe->maxpacket)
shortpkt = true;
if (n_rx)
{
dst = pipe->data;
size = pipe->size;
count = pipe->count;
n_remain = size - count;
src = (uint8_t *)pipe->descb[0]->addr;
dst = &dst[count];
if (n_rx >= n_remain)
{
n_rx = n_remain;
full = true;
}
count += n_rx;
for (i = 0; i < n_rx; i++)
*dst++ = *src++;
pipe->count = count;
}
if (pipe->eptype == USB_EP_ATTR_XFER_CONTROL)
pipe->pkt_timeout = USB_CTRL_DPKT_TIMEOUT;
if (full || shortpkt)
{
if (pipe->eptype == USB_EP_ATTR_XFER_CONTROL)
{
pipe->pipestate = USB_H_PIPE_S_STATO;
pipe->pkt_timeout = USB_CTRL_STAT_TIMEOUT;
sam_putreg8(USBHOST_PSTATUS_DTGL,
SAM_USBHOST_PSTATUSSET(pipe->idx));
sam_send_start(priv, pipe);
}
else
sam_transfer_terminate(priv, pipe, OK);
}
else
{
sam_recv_restart(priv, pipe);
}
}
* Name: sam_recv_restart
*
* Description:
* Start/Re-start the transfer on the selected IN or OUT pipe
*
****************************************************************************/
static void sam_recv_restart(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe)
{
sam_putreg8((USBHOST_PINTFLAG_TRCPT0 |
USBHOST_PINTFLAG_TRCPT1), SAM_USBHOST_PINTFLAG(pipe->idx));
sam_putreg8((USBHOST_PINTFLAG_TRCPT0 |
USBHOST_PINTFLAG_TRCPT1), SAM_USBHOST_PINTENSET(pipe->idx));
if (pipe->eptype == USB_EP_ATTR_XFER_CONTROL)
{
pipe->descb[0]->addr = (uint32_t)pipe->data;
pipe->descb[0]->pktsize &= ~(USBHOST_PKTSIZE_MPKTSIZE_MASK |
USBHOST_PKTSIZE_BCNT_MASK);
pipe->descb[0]->pktsize |= USBHOST_PKTSIZE_MPKTSIZE(pipe->maxpacket);
}
else
{
uint32_t n_next = pipe->size - pipe->count;
pipe->descb[0]->addr = (uint32_t)&pipe->data[pipe->count];
if (n_next > 16384)
n_next = 16384;
pipe->descb[0]->pktsize &= ~(USBHOST_PKTSIZE_MPKTSIZE_MASK |
USBHOST_PKTSIZE_BCNT_MASK);
pipe->descb[0]->pktsize |= USBHOST_PKTSIZE_MPKTSIZE(n_next);
}
sam_modifyreg8(USBHOST_PCFG_PTOKEN_MASK,
USBHOST_PCFG_PTOKEN_IN,
SAM_USBHOST_PCFG(pipe->idx));
sam_putreg8(USBHOST_PSTATUS_BK0RDY, SAM_USBHOST_PSTATUSCLR(pipe->idx));
sam_putreg8(USBHOST_PSTATUS_PFREEZE, SAM_USBHOST_PSTATUSCLR(pipe->idx));
}
* Name: sam_recv_start
*
* Description:
* Start at transfer on the selected IN or OUT pipe.
*
* Assumptions:
*
****************************************************************************/
static void sam_recv_start(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe)
{
usbhost_vtrace2(SAM_VTRACE2_STARTTRANSFER2, pipe->idx, pipe->size);
pipe->result = EBUSY;
pipe->count = 0;
pipe->descb[0]->ctrlpipe &= ~USBHOST_CTRLPIPE_PDADDR_MASK;
pipe->descb[0]->ctrlpipe |= USBHOST_CTRLPIPE_PDADDR(pipe->funcaddr);
sam_recv_restart(priv, pipe);
}
* Name: sam_in_transfer
*
* Description:
* Transfer 'buflen' bytes into 'buffer' from an IN pipe.
*
* Assumptions:
* This function is called only from the TRANSFER.
* The lock, for example, must be relinquished before waiting.
*
****************************************************************************/
static ssize_t sam_in_transfer(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
uint8_t *buffer, size_t buflen)
{
clock_t start;
ssize_t xfrd;
int ret;
* or a fatal error occurs any error other than a simple NAK. NAK would
* simply indicate the end of the transfer (short-transfer).
*/
pipe->data = buffer;
pipe->size = buflen;
pipe->count = 0;
xfrd = 0;
start = clock_systime_ticks();
while (pipe->count < pipe->size)
{
ret = sam_pipe_waitsetup(priv, pipe);
if (ret < 0)
{
usbhost_trace1(SAM_TRACE1_DEVDISCONN5, 0);
return (ssize_t)ret;
}
* and the endpoint type
*/
ret = sam_in_setup(priv, pipe);
if (ret < 0)
{
usbhost_trace1(SAM_TRACE1_INSETUP_FAIL1, -ret);
return (ssize_t)ret;
}
ret = sam_pipe_wait(priv, pipe);
if (ret < 0)
{
* buffered so far (if any).
*/
if (ret == -EAGAIN)
{
if (xfrd > 0)
return xfrd;
else
{
useconds_t delay;
* Has the timeout elapsed?
* if not then try again.
*/
clock_t elapsed = clock_systime_ticks() - start;
if (elapsed >= SAM_DATANAK_DELAY)
{
* as a failure.
*/
return (ssize_t)ret;
}
if (pipe->eptype == USB_EP_ATTR_XFER_INT)
{
* polling rate is determined by the bInterval field
* of the endpoint descriptor (in units of frames
* which we treat as milliseconds here).
*/
if (pipe->interval > 0)
{
delay = (useconds_t)pipe->interval * 1000;
}
else
{
* range is 1-255 frames. Assume one frame.
*/
delay = 1000;
}
}
else
{
* endpoints do not have a polling interval. Rather,
* the should wait until data is received.
*
* REVISIT: For bulk endpoints this 1 msec delay is only
* intended to give the CPU a break from the bulk EP tight
* polling loop. But are there performance issues?
*/
delay = 1000;
}
* isochronous endpoints, this is necessary to assure the
* polling interval. It is used in other cases only to
* prevent the polling from consuming too much CPU bandwidth.
*
* Small delays could require more resolution than is provided
* by the system timer. For example, if the system timer
* resolution is 10MS, then nxsig_usleep(1000) will actually request
* a delay 20MS (due to both quantization and rounding).
*
* REVISIT: So which is better? To ignore tiny delays and
* hog the system bandwidth? Or to wait for an excessive
* amount and destroy system throughput?
*/
if (delay > CONFIG_USEC_PER_TICK)
{
nxsig_usleep(delay - CONFIG_USEC_PER_TICK);
}
}
}
else
{
usbhost_trace1(SAM_TRACE1_TRANSFER_FAILED3, -ret);
return (ssize_t)ret;
}
}
else
{
* running total. Then continue reading until we read 'buflen'
* bytes of data or until the devices NAKs (implying a short
* packet).
*/
xfrd += pipe->count;
}
}
return xfrd;
}
* Name: sam_in_next
*
* Description:
* Initiate the next of a sequence of asynchronous transfers.
*
* Assumptions:
* This function is always called from an interrupt handler
*
****************************************************************************/
#ifdef CONFIG_USBHOST_ASYNCH
static void sam_in_next(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe)
{
usbhost_asynch_t callback;
void *arg;
ssize_t nbytes;
int result;
int ret;
* Did the last chunk transfer complete OK?
*/
result = -(int)pipe->result;
if (pipe->count < pipe->size && result == OK)
{
* endpoint type
*/
ret = sam_in_setup(priv, pipe);
if (ret >= 0)
{
return;
}
usbhost_trace1(SAM_TRACE1_INSETUP_FAIL2, -ret);
result = ret;
}
usbhost_vtrace2(SAM_VTRACE2_XFRCOMPLETE,
(unsigned int)pipe->idx, pipe->size);
callback = pipe->callback;
arg = pipe->arg;
nbytes = pipe->count;
pipe->callback = NULL;
pipe->arg = NULL;
pipe->count = 0;
if (result < 0)
{
nbytes = (ssize_t)result;
}
callback(arg, nbytes);
}
#endif
* Name: sam_in_asynch
*
* Description:
* Initiate the first of a sequence of asynchronous transfers.
*
* Assumptions:
* This function is called only from the ASYNCH interface.
* The lock, for example,
* must be relinquished before waiting.
*
****************************************************************************/
#ifdef CONFIG_USBHOST_ASYNCH
static int sam_in_asynch(struct sam_usbhost_s *priv,
struct sam_pipe_s *pipe,
uint8_t *buffer, size_t buflen,
usbhost_asynch_t callback, void *arg)
{
int ret;
* BEFORE starting the first transfer
*/
pipe->data = buffer;
pipe->size = buflen;
pipe->count = 0;
ret = sam_pipe_asynchsetup(priv, pipe, callback, arg);
if (ret < 0)
{
usbhost_trace1(SAM_TRACE1_ASYNCHSETUP_FAIL2, -ret);
return ret;
}
ret = sam_in_setup(priv, pipe);
if (ret < 0)
{
usbhost_trace1(SAM_TRACE1_INSETUP_FAIL3, -ret);
}
return ret;
}
#endif
* Name: sam_gint_connected
*
* Description:
* Handle a connection event.
*
****************************************************************************/
static void sam_gint_connected(struct sam_usbhost_s *priv)
{
if (!priv->connected)
{
usbhost_vtrace1(SAM_VTRACE1_CONNECTED1, 0);
priv->connected = true;
priv->change = true;
DEBUGASSERT(priv->smstate == SMSTATE_DETACHED);
priv->smstate = SMSTATE_ATTACHED;
if (priv->pscwait)
{
nxsem_post(&priv->pscsem);
priv->pscwait = false;
}
}
}
* Name: sam_gint_disconnected
*
* Description:
* Handle a disconnection event.
*
****************************************************************************/
static void sam_gint_disconnected(struct sam_usbhost_s *priv)
{
if (priv->connected)
{
usbhost_vtrace1(SAM_VTRACE1_DISCONNECTED1, 0);
if (priv->rhport.hport.devclass)
{
CLASS_DISCONNECTED(priv->rhport.hport.devclass);
priv->rhport.hport.devclass = NULL;
}
priv->smstate = SMSTATE_DETACHED;
priv->connected = false;
priv->change = true;
sam_reset_pipes(priv, false);
priv->rhport.hport.speed = USB_SPEED_FULL;
priv->rhport.hport.funcaddr = 0;
* change in the connection state
*/
if (priv->pscwait)
{
nxsem_post(&priv->pscsem);
priv->pscwait = false;
}
}
}
* USB Host Controller Operations
****************************************************************************/
* Name: sam_wait
*
* Description:
* Wait for a device to be connected or disconnected to/from a hub port.
*
* Input Parameters:
* conn - The USB host connection instance obtained as a parameter
* from the call to the USB driver initialization logic.
* hport - The location to return the hub port descriptor that detected the
* connection related event.
*
* Returned Value:
* Zero (OK) is returned on success when a device is connected or
* disconnected. This function will not return until either (1) a device is
* connected or disconnect to/from any hub port or until (2) some failure
* occurs. On a failure, a negated errno value is returned indicating the
* nature of the failure
*
* Assumptions:
* - Called from a single thread so no mutual exclusion is required.
* - Never called from an interrupt handler.
*
****************************************************************************/
static int sam_wait(struct usbhost_connection_s *conn,
struct usbhost_hubport_s **hport)
{
struct sam_usbhost_s *priv = &g_usbhost;
struct usbhost_hubport_s *connport;
irqstate_t flags;
flags = enter_critical_section();
for (; ; )
{
* port?
*/
if (priv->change)
{
connport = &priv->rhport.hport;
connport->connected = priv->connected;
priv->change = false;
*hport = connport;
leave_critical_section(flags);
uinfo("RHport Connected: %s\n", connport->connected ?
"YES" : "NO");
return OK;
}
#ifdef CONFIG_USBHOST_HUB
if (priv->hport)
{
connport = (struct usbhost_hubport_s *)priv->hport;
priv->hport = NULL;
*hport = connport;
leave_critical_section(flags);
uinfo("Hub port Connected: %s\n", connport->connected ?
"YES" : "NO");
return OK;
}
#endif
priv->pscwait = true;
nxsem_wait_uninterruptible(&priv->pscsem);
}
}
* Name: sam_rh_enumerate
*
* Description:
* Enumerate the connected device. As part of this enumeration process,
* the driver will (1) get the device's configuration descriptor, (2)
* extract the class ID info from the configuration descriptor, (3) call
* usbhost_findclass() to find the class that supports this device, (4)
* call the create() method on the struct usbhost_registry_s interface
* to get a class instance, and finally (5) call the connect() method
* of the struct usbhost_class_s interface. After that, the class is in
* charge of the sequence of operations.
*
* Input Parameters:
* conn - The USB host connection instance obtained as a parameter from
* the call to the USB driver initialization logic.
* hport - The descriptor of the hub port that has the newly connected
* device.
*
* Returned Value:
* On success, zero (OK) is returned. On a failure,
* a negated errno value is returned
* indicating the nature of the failure
*
* Assumptions:
* This function will *not* be called from an interrupt handler.
*
****************************************************************************/
static int sam_rh_enumerate(struct sam_usbhost_s *priv,
struct usbhost_connection_s *conn,
struct usbhost_hubport_s *hport)
{
uint32_t regval;
int ret;
uinfo("ENTRY\n");
DEBUGASSERT(conn != NULL && hport != NULL && hport->port == 0);
* method first to be assured that a device is connected.
*/
while (!priv->connected)
{
usbhost_trace1(TRACE1_DEVDISCONN, 0);
return -ENODEV;
}
DEBUGASSERT(priv->smstate == SMSTATE_ATTACHED);
nxsig_usleep(100 * 1000);
sam_hostreset(priv);
regval = sam_getreg8(SAM_USBHOST_STATUS);
if ((regval & USBDEV_STATUS_SPEED_MASK) == USBDEV_STATUS_SPEED_LOW)
priv->rhport.hport.speed = USB_SPEED_LOW;
else
priv->rhport.hport.speed = USB_SPEED_FULL;
if (priv->pipelist[0].inuse == false)
{
struct usbhost_epdesc_s epdesc = {
.hport = hport,
.addr = 0
};
ret = sam_ctrlep_alloc(priv, &epdesc, &priv->ep0);
if (ret < 0)
uerr("ERROR: Failed to allocate a control endpoint: %d\n", ret);
}
else
ret = OK;
return ret;
}
static int sam_enumerate(struct usbhost_connection_s *conn,
struct usbhost_hubport_s *hport)
{
struct sam_usbhost_s *priv = &g_usbhost;
int ret;
uinfo("ENTRY\n");
DEBUGASSERT(hport);
* little more effort to get the device speed. If it is a connection
* on an external hub, then we already have that information.
*/
#ifdef CONFIG_USBHOST_HUB
if (ROOTHUB(hport))
#endif
{
ret = sam_rh_enumerate(priv, conn, hport);
if (ret < 0)
{
return ret;
}
}
uinfo("Enumerate the device\n");
priv->smstate = SMSTATE_ENUM;
ret = usbhost_enumerate(hport, &hport->devclass);
* or because the device class is not supported. In either case, we just
* need to perform the disconnection operation and make ready for a new
* enumeration.
*/
if (ret < 0)
{
uerr("ERROR: Enumeration failed: %d\n", ret);
sam_gint_disconnected(priv);
}
return ret;
}
* Name: sam_ep0configure
*
* Description:
* Configure endpoint 0. This method is normally used internally by the
* enumerate() method but is made available at the interface to support an
* external implementation of the enumeration logic.
*
* Input Parameters:
* drvr - The USB host driver instance obtained as a parameter
* from the call to the class create() method.
* ep0 - The (opaque) EP0 endpoint instance
* funcaddr - The USB address of the function containing the
* endpoint that EP0 controls.
* speed - The speed of the port USB_SPEED_LOW, _FULL, or _HIGH
* maxpacketsize - The maximum number of bytes that can be sent to or
* received from the endpoint in a single data packet
*
* Returned Value:
* On success, zero (OK) is returned. On a failure, a negated
* errno value isreturned indicating the nature of the failure
*
* Assumptions:
* This function will *not* be called from an interrupt handler.
*
****************************************************************************/
static int sam_ep0configure(struct usbhost_driver_s *drvr,
usbhost_ep_t ep0,
uint8_t funcaddr,
uint8_t speed,
uint16_t maxpacketsize)
{
struct sam_usbhost_s *priv = (struct sam_usbhost_s *)drvr;
struct sam_pipe_s *pipe;
uinfo("funcaddr=%d speed=%d maxpacketsize=%d\n",
funcaddr, speed, maxpacketsize);
DEBUGASSERT(drvr != NULL && funcaddr < 128 && maxpacketsize <= 64 &&
(unsigned int)ep0 < SAM_USB_NENDPOINTS);
* hardware and state structures
*/
nxmutex_lock(&priv->lock);
pipe = &priv->pipelist[(unsigned int)ep0];
pipe->funcaddr = funcaddr;
pipe->speed = speed;
pipe->maxpacket = maxpacketsize;
sam_pipe_configure(priv, pipe->idx);
nxmutex_unlock(&priv->lock);
return OK;
}
* Name: sam_epalloc
*
* Description:
* Allocate and configure one endpoint.
*
* Input Parameters:
* drvr - The USB host driver instance obtained as a
* parameter from the call to the class create() method.
* epdesc - Describes the endpoint to be allocated.
* ep - A memory location provided by the caller in which to receive the
* allocated endpoint descriptor.
*
* Returned Value:
* On success, zero (OK) is returned. On a failure, a negated errno value
* is returned indicating the nature of the failure
*
* Assumptions:
* This function will *not* be called from an interrupt handler.
*
****************************************************************************/
static int sam_epalloc(struct usbhost_driver_s *drvr,
const struct usbhost_epdesc_s *epdesc,
usbhost_ep_t *ep)
{
struct sam_usbhost_s *priv = (struct sam_usbhost_s *)drvr;
int ret;
uwarn("addr=%d in=%d xfrtype=%d interval=%d mxpacketsize=%d\n",
epdesc->addr, epdesc->in, epdesc->xfrtype,
epdesc->interval, epdesc->mxpacketsize);
* is connected (because we need a valid low speed indication).
*/
DEBUGASSERT(drvr != 0 && epdesc != NULL && ep != NULL);
* host hardware and state structures
*/
nxmutex_lock(&priv->lock);
* because the normal, "transfer" endpoints are unidirectional an require
* only a single pipe. Control endpoints, however, are bi-diretional
* and require two pipes, one for the IN and one for the OUT direction.
*/
if (epdesc->xfrtype == USB_EP_ATTR_XFER_CONTROL)
{
ret = sam_ctrlep_alloc(priv, epdesc, ep);
}
else
{
ret = sam_xfrep_alloc(priv, epdesc, ep);
}
nxmutex_unlock(&priv->lock);
return ret;
}
* Name: sam_epfree
*
* Description:
* Free and endpoint previously allocated by DRVR_EPALLOC.
*
* Input Parameters:
* drvr - The USB host driver instance obtained as a parameter from the
* call to the class create() method.
* ep - The endpoint to be freed.
*
* Returned Value:
* On success, zero (OK) is returned. On a failure, a negated errno value
* is returned indicating the nature of the failure
*
* Assumptions:
* This function will *not* be called from an interrupt handler.
*
****************************************************************************/
static int sam_epfree(struct usbhost_driver_s *drvr, usbhost_ep_t ep)
{
struct sam_usbhost_s *priv = (struct sam_usbhost_s *)drvr;
DEBUGASSERT(priv);
* USB host hardware and state structures
*/
nxmutex_lock(&priv->lock);
sam_pipe_free(priv, (intptr_t)ep);
nxmutex_unlock(&priv->lock);
return OK;
}
* Name: sam_alloc
*
* Description:
* Some hardware supports special memory in which request and descriptor
* data can be accessed more efficiently.
* This method provides a mechanism to allocate
* the request/descriptor memory. If the underlying hardware does
* not support such "special" memory, this functions
* may simply map to kmm_malloc.
* This interface was optimized under a particular assumption. It was
* assumed that the driver maintains a pool of small, pre-allocated buffers
* for descriptor traffic. NOTE that size is not an input, but an output:
* The size of the pre-allocated buffer is returned.
*
* Input Parameters:
* drvr - The USB host driver instance obtained as a parameter
* from the call to the class create() method.
* buffer - The address of a memory location provided by
* the caller in which to return the allocated buffer memory address.
* maxlen - The address of a memory location provided by
* the caller in which to return the maximum size of the allocated
* buffer memory.
*
* Returned Value:
* On success, zero (OK) is returned. On a failure, a negated errno value
* is returned indicating the nature of the failure
*
* Assumptions:
* - Called from a single thread so no mutual exclusion is required.
* - Never called from an interrupt handler.
*
****************************************************************************/
static int sam_alloc(struct usbhost_driver_s *drvr,
uint8_t **buffer, size_t *maxlen)
{
uint8_t *alloc;
uinfo("ENTRY\n");
DEBUGASSERT(drvr && buffer && maxlen);
alloc = kmm_malloc(CONFIG_SAM_DESCSIZE);
if (!alloc)
{
return -ENOMEM;
}
*buffer = alloc;
*maxlen = CONFIG_SAM_DESCSIZE;
return OK;
}
* Name: sam_free
*
* Description:
* Some hardware supports special memory in which request and descriptor
* data can be accessed more efficiently.
* This method provides a mechanism to free that
* request/descriptor memory. If the underlying hardware does not support
* such "special" memory, this functions may simply map to kmm_free().
*
* Input Parameters:
* drvr - The USB host driver instance obtained as a parameter
* from the call to the class create() method.
* buffer - The address of the allocated buffer memory to be freed.
*
* Returned Value:
* On success, zero (OK) is returned. On a failure, a negated errno value
* is returned indicating the nature of the failure
*
* Assumptions:
* - Never called from an interrupt handler.
*
****************************************************************************/
static int sam_free(struct usbhost_driver_s *drvr, uint8_t *buffer)
{
uinfo("ENTRY\n");
DEBUGASSERT(drvr && buffer);
kmm_free(buffer);
return OK;
}
* Name: sam_ioalloc
*
* Description:
* Some hardware supports special memory in which larger IO buffers can
* be accessed more efficiently. This method provides a mechanism to
* allocate the request/descriptor memory.
* If the underlying hardware does not support
* such "special" memory, this functions may simply map to kmm_malloc.
*
* This interface differs from DRVR_ALLOC in that the
* buffers are variable-sized.
*
* Input Parameters:
* drvr - The USB host driver instance obtained as a parameter from the
* call to the class create() method.
* buffer - The address of a memory location provided by the caller
* in which to return the allocated buffer memory address.
* buflen - The size of the buffer required.
*
* Returned Value:
* On success, zero (OK) is returned. On a failure, a negated errno
* value is returned indicating the nature of the failure
*
* Assumptions:
* This function will *not* be called from an interrupt handler.
*
****************************************************************************/
static int sam_ioalloc(struct usbhost_driver_s *drvr,
uint8_t **buffer, size_t buflen)
{
uint8_t *alloc;
uinfo("ENTRY\n");
DEBUGASSERT(drvr && buffer && buflen > 0);
alloc = kmm_malloc(buflen);
if (!alloc)
{
return -ENOMEM;
}
*buffer = alloc;
return OK;
}
* Name: sam_iofree
*
* Description:
* Some hardware supports special memory in which IO data can
* be accessed more efficiently.
* This method provides a mechanism to free that IO buffer
* memory. If the underlying hardware does not support such
*
* Input Parameters:
* drvr - The USB host driver instance obtained as a parameter from the
* call to the class create() method.
* buffer - The address of the allocated buffer memory to be freed.
*
* Returned Value:
* On success, zero (OK) is returned. On a failure, a negated errno
* value is returned indicating the nature of the failure
*
* Assumptions:
* This function will *not* be called from an interrupt handler.
*
****************************************************************************/
static int sam_iofree(struct usbhost_driver_s *drvr, uint8_t *buffer)
{
uinfo("ENTRY\n");
DEBUGASSERT(drvr && buffer);
kmm_free(buffer);
return OK;
}
* Name: sam_ctrlin and sam_ctrlout
*
* Description:
* Process a IN or OUT request on the control endpoint. These methods
* will enqueue the request and wait for it to complete.
* Only one transfer may be queued;
* Neither these methods nor the transfer() method can be called again
* until the control transfer functions returns.
*
* These are blocking methods; these functions will not return until the
* control transfer has completed.
*
* Input Parameters:
* drvr - The USB host driver instance obtained as a parameter from the
* call to the class create() method.
* ep0 - The control endpoint to send/receive the control request.
* req - Describes the request to be sent. This request must lie
* in memory created by DRVR_ALLOC.
* buffer - A buffer used for sending the request and for returning any
* responses. This buffer must be large enough to hold the length value
* in the request description.
* Buffer must have been allocated using DRVR_ALLOC.
*
* NOTE: On an IN transaction, req and buffer may refer to the same
* allocated memory.
*
* Returned Value:
* On success, zero (OK) is returned. On a failure, a negated errno
* value is returned indicating the nature of the failure
*
* Assumptions:
* - Called from a single thread so no mutual exclusion is required.
* - Never called from an interrupt handler.
*
****************************************************************************/
static int sam_ctrlin(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
const struct usb_ctrlreq_s *req,
uint8_t *buffer)
{
struct sam_usbhost_s *priv = (struct sam_usbhost_s *)drvr;
struct sam_pipe_s *pipe;
uint16_t buflen;
clock_t start;
clock_t elapsed;
int retries;
int ret;
DEBUGASSERT(priv != NULL && req != NULL &&
(unsigned int)ep0 < SAM_USB_NENDPOINTS);
usbhost_vtrace2(SAM_VTRACE2_CTRLIN, req->type, req->req);
pipe = &priv->pipelist[(unsigned int)ep0];
buflen = sam_getle16(req->len);
uinfo("type:0x%02x req:0x%02x value:0x%02x%02x index:0x%02x%02x len:%d\n",
req->type, req->req, req->value[1], req->value[0],
req->index[1], req->index[0], buflen);
* host hardware and state structures
*/
nxmutex_lock(&priv->lock);
for (retries = 0; retries < SAM_RETRY_COUNT; retries++)
{
ret = sam_ctrl_sendsetup(priv, pipe, req);
if (ret < 0)
{
usbhost_trace1(SAM_TRACE1_SENDSETUP_FAIL2, -ret);
continue;
}
start = clock_systime_ticks();
do
{
if (buflen > 0)
{
ret = sam_ctrl_recvdata(priv, pipe, buffer, buflen);
if (ret < 0)
{
usbhost_trace1(SAM_TRACE1_RECVDATA_FAIL, -ret);
}
}
if (ret == OK)
{
ret = sam_ctrl_senddata(priv, pipe, NULL, 0);
if (ret == OK)
{
nxmutex_unlock(&priv->lock);
return OK;
}
usbhost_trace1(SAM_TRACE1_SENDSTATUS_FAIL, ret < 0 ?
-ret : ret);
}
elapsed = clock_systime_ticks() - start;
}
while (elapsed < SAM_DATANAK_DELAY);
}
* and timeouts have been exhausted
*/
nxmutex_unlock(&priv->lock);
return -ETIMEDOUT;
}
static int sam_ctrlout(struct usbhost_driver_s *drvr, usbhost_ep_t ep0,
const struct usb_ctrlreq_s *req,
const uint8_t *buffer)
{
struct sam_usbhost_s *priv = (struct sam_usbhost_s *)drvr;
struct sam_pipe_s *pipe;
uint16_t buflen;
clock_t start;
clock_t elapsed;
int retries;
int ret;
DEBUGASSERT(priv != NULL && req != NULL &&
(unsigned int)ep0 < SAM_USB_NENDPOINTS);
usbhost_vtrace2(SAM_VTRACE2_CTRLOUT, req->type, req->req);
pipe = &priv->pipelist[(unsigned int)ep0];
buflen = sam_getle16(req->len);
uinfo("type:0x%02x req:0x%02x value:0x%02x%02x index:0x%02x%02x len:%d\n",
req->type, req->req, req->value[1], req->value[0],
req->index[1], req->index[0], buflen);
* USB host hardware and state structures
*/
nxmutex_lock(&priv->lock);
for (retries = 0; retries < SAM_RETRY_COUNT; retries++)
{
ret = sam_ctrl_sendsetup(priv, pipe, req);
if (ret < 0)
{
usbhost_trace1(SAM_TRACE1_SENDSETUP_FAIL1, -ret);
continue;
}
start = clock_systime_ticks();
do
{
if (buflen > 0)
{
ret = sam_ctrl_senddata(priv, pipe,
(uint8_t *)buffer, buflen);
if (ret < 0)
{
usbhost_trace1(SAM_TRACE1_SENDDATA_FAIL, -ret);
}
}
if (ret == OK)
{
ret = sam_ctrl_recvdata(priv, pipe, NULL, 0);
if (ret == OK)
{
nxmutex_unlock(&priv->lock);
return OK;
}
usbhost_trace1(SAM_TRACE1_RECVSTATUS_FAIL,
ret < 0 ? -ret : ret);
}
elapsed = clock_systime_ticks() - start;
}
while (elapsed < SAM_DATANAK_DELAY);
}
* and timeouts have been exhausted
*/
nxmutex_unlock(&priv->lock);
return -ETIMEDOUT;
}
* Name: sam_transfer
*
* Description:
* Process a request to handle a transfer descriptor. This method will
* enqueue the transfer request, blocking until the transfer completes.
* Only one transfer may be queued; Neither this method nor the ctrlin
* or ctrlout methods can be called again until this function returns.
*
* This is a blocking method; this functions will not return until the
* transfer has completed.
*
* Input Parameters:
* drvr - The USB host driver instance obtained as a parameter from the
* call to the class create() method.
* ep - The IN or OUT endpoint descriptor for the device endpoint on
* which to perform the transfer.
* buffer - A buffer containing the data to be sent (OUT endpoint) or
* received (IN endpoint). Buffer must have been allocated using
* DRVR_ALLOC.
* buflen - The length of the data to be sent or received.
*
* Returned Value:
* On success, a non-negative value is returned that indicates the number
* of bytes successfully transferred. On a failure, a negated errno value
* is returned that indicates the nature of the failure:
*
* EAGAIN - If devices NAKs the transfer (or NYET or other error where
* it may be appropriate to restart the entire transaction).
* EPERM - If the endpoint stalls
* EIO - On a TX or data toggle error
* EPIPE - Overrun errors
*
* Assumptions:
* - Called from a single thread so no mutual exclusion is required.
* - Never called from an interrupt handler.
*
****************************************************************************/
static ssize_t sam_transfer(struct usbhost_driver_s *drvr,
usbhost_ep_t ep,
uint8_t *buffer,
size_t buflen)
{
struct sam_usbhost_s *priv = (struct sam_usbhost_s *)drvr;
struct sam_pipe_s *pipe;
unsigned int idx = (unsigned int)ep;
ssize_t nbytes;
uwarn("pipe%d buffer:%p buflen:%d\n", idx, buffer, buflen);
DEBUGASSERT(priv && buffer && idx < SAM_USB_NENDPOINTS && buflen > 0);
pipe = &priv->pipelist[idx];
* USB host hardware and state structures
*/
nxmutex_lock(&priv->lock);
if (pipe->in)
{
nbytes = sam_in_transfer(priv, pipe, buffer, buflen);
}
else
{
nbytes = sam_out_transfer(priv, pipe, buffer, buflen);
}
nxmutex_unlock(&priv->lock);
return nbytes;
}
* Name: sam_asynch
*
* Description:
* Process a request to handle a transfer descriptor. This method will
* enqueue the transfer request and return immediately. When the transfer
* completes, the callback will be invoked with the provided transfer.
* This method is useful for receiving interrupt transfers which may come
* infrequently.
*
* Only one transfer may be queued; Neither this method nor the ctrlin or
* ctrlout methods can be called again until the transfer completes.
*
* Input Parameters:
* drvr - The USB host driver instance obtained as a parameter from the
* call to the class create() method.
* ep - The IN or OUT endpoint descriptor for the device endpoint on
* which to perform the transfer.
* buffer - A buffer containing the data to be sent (OUT endpoint)
* or received (IN endpoint). Buffer must have been allocated using
* DRVR_ALLOC
* buflen - The length of the data to be sent or received.
* callback - This function will be called when the transfer completes.
* arg - The arbitrary parameter that will be passed to the callback
* function when the transfer completes.
*
* Returned Value:
* On success, zero (OK) is returned. On a failure, a negated errno
* value is returned indicating the nature of the failure
*
* Assumptions:
* - Called from a single thread so no mutual exclusion is required.
* - Never called from an interrupt handler.
*
****************************************************************************/
#ifdef CONFIG_USBHOST_ASYNCH
static int sam_asynch(struct usbhost_driver_s *drvr, usbhost_ep_t ep,
uint8_t *buffer, size_t buflen,
usbhost_asynch_t callback, void *arg)
{
struct sam_usbhost_s *priv = (struct sam_usbhost_s *)drvr;
struct sam_pipe_s *pipe;
unsigned int idx = (unsigned int)ep;
int ret;
uinfo("idx: %d buflen: %d\n", (unsigned int)ep, buflen);
DEBUGASSERT(priv && buffer && idx < SAM_USB_NENDPOINTS && buflen > 0);
pipe = &priv->pipelist[idx];
* USB host hardware and state structures
*/
nxmutex_lock(&priv->lock);
if (pipe->in)
{
ret = sam_in_asynch(priv, pipe, buffer, buflen, callback, arg);
}
else
{
ret = sam_out_asynch(priv, pipe, buffer, buflen, callback, arg);
}
nxmutex_unlock(&priv->lock);
return ret;
}
#endif
* Name: sam_cancel
*
* Description:
* Cancel a pending transfer on an endpoint. Cancelled synchronous or
* asynchronous transfer will complete normally with the error -ESHUTDOWN.
*
* Input Parameters:
* drvr - The USB host driver instance obtained as a parameter from the
* call to the class create() method.
* ep - The IN or OUT endpoint descriptor for the device endpoint on
* which an asynchronous transfer should be transferred.
*
* Returned Value:
* On success, zero (OK) is returned. On a failure, a negated errno value
* is returned indicating the nature of the failure.
*
****************************************************************************/
static int sam_cancel(struct usbhost_driver_s *drvr, usbhost_ep_t ep)
{
struct sam_usbhost_s *priv = (struct sam_usbhost_s *)drvr;
struct sam_pipe_s *pipe;
unsigned int idx = (unsigned int)ep;
irqstate_t flags;
uinfo("idx: %u: %d\n", idx);
DEBUGASSERT(priv && idx < SAM_USB_NENDPOINTS);
pipe = &priv->pipelist[idx];
* asynchronous completion of the transfer being cancelled.
*/
flags = enter_critical_section();
sam_transfer_abort(priv, pipe, CHREASON_CANCELLED);
pipe->result = -ESHUTDOWN;
if (pipe->waiter)
{
#ifdef CONFIG_USBHOST_ASYNCH
DEBUGASSERT(pipe->callback == NULL);
#endif
nxsem_post(&pipe->waitsem);
pipe->waiter = false;
}
#ifdef CONFIG_USBHOST_ASYNCH
* completes?
*/
else if (pipe->callback)
{
usbhost_asynch_t callback;
void *arg;
callback = pipe->callback;
arg = pipe->arg;
pipe->callback = NULL;
pipe->arg = NULL;
pipe->count = 0;
callback(arg, -ESHUTDOWN);
}
#endif
leave_critical_section(flags);
return OK;
}
* Name: sam_connect
*
* Description:
* New connections may be detected by an attached hub. This method is the
* mechanism that is used by the hub class to introduce a new connection
* and port description to the system.
*
* Input Parameters:
* drvr - The USB host driver instance obtained as a parameter from the
* call to the class create() method.
* hport - The descriptor of the hub port that detected the connection
* related event
* connected - True: device connected; false: device disconnected
*
* Returned Value:
* On success, zero (OK) is returned. On a failure, a negated errno value
* is returned indicating the nature of the failure.
*
****************************************************************************/
#ifdef CONFIG_USBHOST_HUB
static int sam_connect(struct usbhost_driver_s *drvr,
struct usbhost_hubport_s *hport,
bool connected)
{
struct sam_usbhost_s *priv = (struct sam_usbhost_s *)drvr;
irqstate_t flags;
DEBUGASSERT(priv != NULL && hport != NULL);
hport->connected = connected;
uinfo("Hub port %d connected: %s\n",
hport->port, connected ? "YES" : "NO");
flags = enter_critical_section();
priv->hport = hport;
if (priv->pscwait)
{
priv->pscwait = false;
nxsem_post(&priv->pscsem);
}
leave_critical_section(flags);
return OK;
}
#endif
* Name: sam_disconnect
*
* Description:
* Called by the class when an error occurs and driver has been
* disconnected. The USB host driver should discard the handle to the
* class instance (it is stale) and not attempt any further interaction
* with the class driver instance (until a new instance is received from
* the create() method). The driver should not called the class
* disconnected() method.
*
* Input Parameters:
* drvr - The USB host driver instance obtained as a parameter from the
* call to the class create() method.
* hport - The port from which the device is being disconnected.
* Might be a port on a hub.
*
* Returned Value:
* None
*
* Assumptions:
* - Only a single class bound to a single device is supported.
* - Never called from an interrupt handler.
*
****************************************************************************/
static void sam_disconnect(struct usbhost_driver_s *drvr,
struct usbhost_hubport_s *hport)
{
DEBUGASSERT(hport != NULL);
hport->devclass = NULL;
}
* Pipe Helpers
****************************************************************************/
* Name: sam_reset_pipes
*
* Description:
* Reset pipes
* \param priv Pointer to driver instance
* \param[in] warm_reset Handles runtime USB reset
*
****************************************************************************/
static void sam_reset_pipes(struct sam_usbhost_s *priv, bool warm_reset)
{
struct sam_pipe_s *pipe;
uint8_t i;
for (i = 0; i < SAM_USB_NENDPOINTS; i++)
{
pipe = &priv->pipelist[i];
if (warm_reset)
{
if (pipe->pipestate_general == USB_H_PIPE_S_FREE)
continue;
sam_pipe_configure(priv, i);
if (pipe->eptype == USB_EP_ATTR_XFER_CONTROL)
{
pipe->pipestate_general = USB_H_PIPE_S_SETUP;
}
sam_transfer_terminate(priv, pipe, USB_H_RESET);
}
else
pipe->pipestate_general = USB_H_PIPE_S_FREE;
}
}
* Name: sam_pipe_reset
*
* Description:
* Reset and disable one pipe.
*
****************************************************************************/
static void sam_pipe_reset(struct sam_usbhost_s *priv, uint8_t epno)
{
struct sam_pipe_s *pipe = &priv->pipelist[epno];
uinfo("pipe%d\n", pipe->idx);
sam_putreg8(0x3f, SAM_USBHOST_PINTENCLR(epno));
sam_putreg8(0x3f, SAM_USBHOST_PINTFLAG(epno));
pipe->pipestate = USB_H_PIPE_S_FREE;
pipe->stalled = false;
pipe->pending = false;
pipe->halted = false;
pipe->zlpsent = false;
pipe->txbusy = false;
pipe->rxactive = false;
}
* Name: sam_pipeset_reset
*
* Description:
* Reset and disable a set of pipes.
*
****************************************************************************/
static void sam_pipeset_reset(struct sam_usbhost_s *priv, uint16_t epset)
{
uint32_t bit;
int epno;
uinfo("ENTRY\n");
for (epno = 0, bit = 1, epset &= SAM_EPSET_ALL;
epno < SAM_USB_NENDPOINTS && epset != 0;
epno++, bit <<= 1)
{
if ((epset & bit) != 0)
{
sam_pipe_reset(priv, epno);
epset &= ~bit;
}
}
}
* Name: sam_vbusdrive
*
* Description:
* Drive the Vbus +5V.
*
* Input Parameters:
* priv - USB host driver private data structure.
* state - True: Drive, False: Don't drive
*
* Returned Value:
* None.
*
****************************************************************************/
static void sam_vbusdrive(struct sam_usbhost_s *priv, bool state)
{
sam_usbhost_vbusdrive(0, state);
up_mdelay(200);
}
* Name: sam_pipe_interrupt
*
* Description:
* Handle the USB pipe interrupt
*
****************************************************************************/
static void sam_pipe_interrupt(struct sam_usbhost_s *priv, int idx)
{
struct sam_pipe_s *pipe;
uint8_t pipisr;
uint8_t pipimr;
DEBUGASSERT((unsigned)idx < SAM_USB_NENDPOINTS);
pipe = &priv->pipelist[idx];
pipisr = sam_getreg8(SAM_USBHOST_PINTFLAG(idx));
pipimr = sam_getreg8(SAM_USBHOST_PINTENSET(idx));
uinfo("pipe%d PINTFLAG:0x%x PINTENSET:0x%x\n", idx, pipisr, pipimr);
if (pipisr & USBHOST_PINTFLAG_STALL)
{
sam_putreg8(USBHOST_PINTFLAG_STALL, SAM_USBHOST_PINTFLAG(idx));
uwarn("STALL =0x%x pktsize=0x%x ctrlpipe=0x%x statuspipe=0x%x\n",
pipisr,
pipe->descb[0]->pktsize,
pipe->descb[0]->ctrlpipe,
pipe->descb[0]->statuspipe);
sam_transfer_abort(priv, pipe, USB_H_STALL);
return;
}
if (pipisr & USBHOST_PINTFLAG_PERR)
{
sam_putreg8(USBHOST_PINTFLAG_PERR, SAM_USBHOST_PINTFLAG(idx));
uwarn("PERR =0x%x pktsize=0x%x ctrlpipe=0x%x statuspipe=0x%x\n",
pipisr,
pipe->descb[0]->pktsize,
pipe->descb[0]->ctrlpipe,
pipe->descb[0]->statuspipe);
switch (pipe->descb[0]->statuspipe & (USBHOST_STATUSPIPE_DTGLER |
USBHOST_STATUSPIPE_TOUTER |
USBHOST_STATUSPIPE_DAPIDER))
{
case USBHOST_STATUSPIPE_DTGLER:
pipe->pipestatus_general = USB_H_ERR;
break;
case USBHOST_STATUSPIPE_TOUTER:
pipe->pipestatus_general = USB_H_TIMEOUT;
pipe->result = -ETIMEDOUT;
break;
case USBHOST_STATUSPIPE_PIDER:
case USBHOST_STATUSPIPE_DAPIDER:
default:
pipe->pipestatus_general = USB_H_ERR;
break;
}
pipe->descb[0]->statuspipe = 0;
sam_transfer_abort(priv, pipe, USB_H_ERR);
return;
}
if (pipisr & USBHOST_PINTFLAG_TRFAIL)
{
sam_putreg8(USBHOST_PINTFLAG_TRFAIL, SAM_USBHOST_PINTFLAG(idx));
uwarn("TRFAIL =0x%x pktsize=0x%x ctrlpipe=0x%x statuspipe=0x%x\n",
pipisr,
pipe->descb[0]->pktsize,
pipe->descb[0]->ctrlpipe,
pipe->descb[0]->statuspipe);
uint8_t status_bk = pipe->descb[0]->stausbk;
if (status_bk)
{
pipe->descb[0]->stausbk = 0;
if (pipe->eptype != USB_EP_ATTR_XFER_ISOC &&
status_bk == USBHOST_STATUSBK_ERRORFLOW)
{
}
else
{
pipe->pipestatus_general = USB_H_ERR;
sam_transfer_abort(priv, pipe, USB_H_ERR);
}
}
return;
}
if ((pipisr & pipimr & (USBHOST_PINTFLAG_TRCPT0 |
USBHOST_PINTFLAG_TRCPT1)) != 0)
{
sam_putreg8((USBHOST_PINTFLAG_TRCPT0 |
USBHOST_PINTFLAG_TRCPT1), SAM_USBHOST_PINTFLAG(idx));
if ((sam_getreg8(SAM_USBHOST_PCFG(idx)) & USBHOST_PCFG_PTOKEN_MASK) ==
USBHOST_PCFG_PTOKEN_IN)
{
uwarn("pipe%d IN TRCPT pktsize=0x%x\n",
idx,
pipe->descb[0]->pktsize);
if (idx > 0)
sam_recv_continue(priv, pipe);
pipe->result = 0;
sam_pipe_wakeup(priv, pipe);
}
else
{
uwarn("pipe%d OUT TRCPT pktsize=0x%x\n",
idx,
pipe->descb[0]->pktsize);
if (idx > 0)
sam_send_continue(priv, pipe);
pipe->result = 0;
sam_pipe_wakeup(priv, pipe);
}
return;
}
if (pipisr & pipimr & USBHOST_PINTFLAG_TXSTP)
{
sam_putreg8(USBHOST_PINTFLAG_TXSTP, SAM_USBHOST_PINTFLAG(idx));
sam_putreg8(USBHOST_PINTFLAG_TXSTP, SAM_USBHOST_PINTENCLR(idx));
sam_putreg8(USBHOST_PSTATUS_DTGL, SAM_USBHOST_PSTATUSSET(idx));
if (priv->ctrl_buffer[0] & 0x80)
{
uwarn("pipe%d IN TXSTP\n", idx);
pipe->pipestate = USB_H_PIPE_S_DATI;
pipe->result = 0;
sam_pipe_wakeup(priv, pipe);
}
else
{
uwarn("pipe%d OUT TXSTP\n", idx);
if (priv->ctrl_buffer[6] || priv->ctrl_buffer[7])
{
pipe->pipestate = USB_H_PIPE_S_DATO;
pipe->result = 0;
sam_pipe_wakeup(priv, pipe);
}
else
{
uwarn("pipe%d OUT TXSTP ZLP\n", idx);
pipe->pipestate = USB_H_PIPE_S_STATI;
pipe->result = 0;
sam_pipe_wakeup(priv, pipe);
}
}
return;
}
}
* Name: sam_usbhost_interrupt
*
* Description:
* Handle the USB interrupt.
* Host Mode
*
****************************************************************************/
static int sam_usbhost_interrupt(int irq, void *context, void *arg)
{
struct sam_usbhost_s *priv = (struct sam_usbhost_s *)arg;
uint16_t isr;
uint16_t imr;
uint16_t regval;
uint16_t pending;
uint16_t pendingpipe;
int i;
isr = sam_getreg16(SAM_USBHOST_INTFLAG);
imr = sam_getreg16(SAM_USBHOST_INTENSET);
pending = isr & imr;
pendingpipe = sam_getreg16(SAM_USBHOST_PINTSMRY);
if (pendingpipe)
{
usbhost_vtrace1(TRACE_INTDECODE(SAM_TRACEINTID_PENDING_PIPE),
pendingpipe);
for (i = 0; i < SAM_USB_NENDPOINTS; i++)
{
if ((pendingpipe & USBHOST_PINTSMRY_PIPEINT(i)))
{
usbhost_vtrace1(TRACE_INTDECODE(SAM_TRACEINTID_PIPENO),
(uint16_t)i);
sam_pipe_interrupt(priv, i);
}
}
}
else
{
if (isr & USBHOST_INT_HSOF)
sam_putreg16(USBHOST_INT_HSOF, SAM_USBHOST_INTFLAG);
if (isr & USBHOST_INT_RST)
{
sam_putreg16(USBHOST_INT_RST, SAM_USBHOST_INTFLAG);
sam_reset_pipes(priv, true);
return OK;
}
else if (pending & USBHOST_INT_DDISC)
{
sam_putreg16((USBHOST_INT_DDISC |
USBHOST_INT_DCONN), SAM_USBHOST_INTFLAG);
* Disable wakeup/resumes interrupts,
* in case of disconnection during suspend mode
*/
sam_putreg16((USBHOST_INT_DDISC |
USBHOST_INT_WAKEUP |
USBHOST_INT_DNRSM |
USBHOST_INT_UPRSM), SAM_USBHOST_INTENCLR);
regval = sam_getreg16(SAM_USBHOST_CTRLB);
regval &= ~USBHOST_CTRLB_BUSRESET;
sam_putreg16(regval, SAM_USBHOST_CTRLB);
sam_putreg16((USBHOST_INT_DCONN |
USBHOST_INT_WAKEUP |
USBHOST_INT_DNRSM |
USBHOST_INT_UPRSM), SAM_USBHOST_INTFLAG);
sam_putreg16((USBHOST_INT_DCONN |
USBHOST_INT_WAKEUP |
USBHOST_INT_DNRSM |
USBHOST_INT_UPRSM), SAM_USBHOST_INTENSET);
priv->suspend_start = 0;
priv->resume_start = 0;
sam_gint_disconnected(priv);
return OK;
}
else if (pending & USBHOST_INT_DCONN)
{
sam_putreg16 (USBHOST_INT_DCONN, SAM_USBHOST_INTENCLR);
sam_putreg16(USBHOST_INT_DDISC, SAM_USBHOST_INTFLAG);
sam_putreg16(USBHOST_INT_DDISC, SAM_USBHOST_INTENSET);
regval = sam_getreg16(SAM_USBHOST_CTRLB);
regval |= USBHOST_CTRLB_SOFE | USBHOST_CTRLB_BUSRESET;
sam_putreg16(regval, SAM_USBHOST_CTRLB);
priv->suspend_start = 0;
priv->resume_start = 0;
sam_gint_connected(priv);
return OK;
}
if ((isr & USBHOST_INT_WAKEUP) && (imr & USBHOST_INT_DCONN))
{
sam_putreg16(USBHOST_INT_WAKEUP, SAM_USBHOST_INTFLAG);
sam_vbusdrive(priv, true);
}
if (pending & (USBHOST_INT_WAKEUP |
USBHOST_INT_UPRSM |
USBHOST_INT_DNRSM))
{
sam_putreg16((USBHOST_INT_WAKEUP |
USBHOST_INT_UPRSM |
USBHOST_INT_DNRSM), SAM_USBHOST_INTFLAG);
sam_putreg16((USBHOST_INT_WAKEUP |
USBHOST_INT_UPRSM |
USBHOST_INT_DNRSM), SAM_USBHOST_INTENCLR);
sam_putreg16((USBHOST_INT_RST |
USBHOST_INT_DDISC), SAM_USBHOST_INTENSET);
regval = sam_getreg16(SAM_USBHOST_CTRLB);
regval |= USBHOST_CTRLB_RESUME | USBHOST_CTRLB_SOFE;
sam_putreg16(regval, SAM_USBHOST_CTRLB);
priv->resume_start = 50;
sam_add_sof_user(priv);
return OK;
}
}
sam_putreg16(isr, SAM_USBHOST_INTFLAG);
return OK;
}
* Initialization/Reset
****************************************************************************/
* Name: sam_hostreset
****************************************************************************/
static void sam_hostreset(struct sam_usbhost_s *priv)
{
uint16_t regval;
uinfo("ENTRY\n");
sam_enableclks();
priv->hoststate = USB_HOSTSTATE_DEFAULT;
regval = sam_getreg16(SAM_USBHOST_CTRLB);
regval &= ~USBHOST_CTRLB_BUSRESET;
sam_putreg16(regval, SAM_USBHOST_CTRLB);
regval = USBHOST_INT_HSOF | USBHOST_INT_RST | USBHOST_INT_WAKEUP |
USBHOST_INT_DNRSM | USBHOST_INT_UPRSM | USBHOST_INT_RAMACER |
USBHOST_INT_DCONN | USBHOST_INT_DDISC;
sam_putreg16(regval, SAM_USBHOST_INTFLAG);
regval = USBHOST_INT_WAKEUP | USBHOST_INT_DDISC;
sam_putreg16(regval, SAM_USBHOST_INTENSET);
sam_dumppipe(priv, EP0);
}
* Name: sam_host_initialize
*
* Description:
* Initialize/re-initialize hardware for host mode operation. At present,
* this function is called only from sam_hw_initialize(). But if OTG mode
* were supported, this function would also be called to swtich between
* host and device modes on a connector ID change interrupt.
*
* Input Parameters:
* priv -- USB host driver private data structure.
*
* Returned Value:
* None.
*
****************************************************************************/
static void sam_host_initialize(struct sam_usbhost_s *priv)
{
sam_vbusdrive(priv, true);
sam_putreg16(USBHOST_INT_DCONN |
USBHOST_INT_RST |
USBHOST_INT_WAKEUP, SAM_USBHOST_INTENSET);
}
* Name: sam_sw_initialize
*
* Description:
* One-time setup of the host driver state structure.
*
* Input Parameters:
* priv -- USB host driver private data structure.
*
* Returned Value:
* None.
*
****************************************************************************/
static inline void sam_sw_initialize(struct sam_usbhost_s *priv)
{
struct usbhost_driver_s *drvr;
struct usbhost_hubport_s *hport;
int epno;
drvr = &priv->drvr;
drvr->ep0configure = sam_ep0configure;
drvr->epalloc = sam_epalloc;
drvr->epfree = sam_epfree;
drvr->alloc = sam_alloc;
drvr->free = sam_free;
drvr->ioalloc = sam_ioalloc;
drvr->iofree = sam_iofree;
drvr->ctrlin = sam_ctrlin;
drvr->ctrlout = sam_ctrlout;
drvr->transfer = sam_transfer;
#ifdef CONFIG_USBHOST_ASYNCH
drvr->asynch = sam_asynch;
#endif
drvr->cancel = sam_cancel;
#ifdef CONFIG_USBHOST_HUB
drvr->connect = sam_connect;
#endif
drvr->disconnect = sam_disconnect;
hport = &priv->rhport.hport;
hport->drvr = drvr;
#ifdef CONFIG_USBHOST_HUB
hport->parent = NULL;
#endif
hport->ep0 = priv->ep0;
hport->speed = USB_SPEED_FULL;
usbhost_devaddr_initialize(&priv->devgen);
priv->rhport.pdevgen = &priv->devgen;
for (epno = 0; epno < SAM_USB_NENDPOINTS; epno++)
{
priv->pipelist[epno].idx = epno;
nxsem_init(&priv->pipelist[epno].waitsem, 0, 0);
sam_putreg8(USBHOST_PSTATUS_PFREEZE, SAM_USBHOST_PSTATUSSET(epno));
priv->pipelist[epno].descb[0] = &priv->pipe_descriptors[(epno << 1)];
priv->pipelist[epno].descb[1] = &priv->pipe_descriptors[(epno << 1) + 1];
}
sam_reset_pipes(priv, false);
priv->smstate = SMSTATE_DETACHED;
priv->connected = false;
priv->change = false;
priv->irqset = 0;
}
* Name: sam_hw_initialize
*
* Description:
* One-time setup of the host controller harware for normal operations.
*
* Input Parameters:
* priv -- USB host driver private data structure.
*
* Returned Value:
* Zero on success; a negated errno value on failure.
*
****************************************************************************/
static inline int sam_hw_initialize(struct sam_usbhost_s *priv)
{
uint16_t regval;
uint32_t padcalib;
uint8_t calib_transn;
uint8_t calib_transp;
uint8_t calib_trim;
sam_portconfig(PORT_USB_DP | PORT_PULL_NONE);
sam_portconfig(PORT_USB_DM | PORT_PULL_NONE);
* inputthe USB clock
*/
sam_enableclks();
sam_ctrla_write(USB_CTRLA_SWRST);
calib_transn = (getreg32(SAM_FUSES_USBTRANSN_ADDR) &
SAM_FUSES_USBTRANSN_MASK) >> SAM_FUSES_USBTRANSN_SHIFT;
if (calib_transn == 0 || calib_transn == 0x1f)
calib_transn = 0x9;
calib_transp = (getreg32(SAM_FUSES_USBTRANSP_ADDR) &
SAM_FUSES_USBTRANSP_ADDR) >> SAM_FUSES_USBTRANSP_SHIFT;
if (calib_transp == 0 || calib_transp == 0x1f)
calib_transp = 0x19;
calib_trim = (getreg32(SAM_FUSES_USBTRIM_ADDR) &
SAM_FUSES_USBTRIM_MASK) >> SAM_FUSES_USBTRIM_SHIFT;
if (calib_trim == 0 || calib_trim == 0x7)
calib_trim = 0x6;
padcalib = USB_PADCAL_TRANSP(calib_transp) |
USB_PADCAL_TRANSN(calib_transn) |
USB_PADCAL_TRIM(calib_trim);
sam_putreg16(padcalib, SAM_USB_PADCAL);
uinfo("PADCAL: 0x%x\n", padcalib);
sam_putreg8(USB_QOSCTRL_CQOS(3) | USB_QOSCTRL_DQOS(3), SAM_USB_QOSCTRL);
sam_ctrla_write(USB_CTRLA_ENABLE |
USB_CTRLA_RUNSTBY |
USB_CTRLA_MODE_HOST);
sam_pipeset_reset(priv, SAM_EPSET_ALL);
* DMA transfers could happen
*/
memset((uint8_t *)(&priv->pipe_descriptors[0]), 0,
sizeof(priv->pipe_descriptors));
sam_putreg32((uint32_t)&priv->pipe_descriptors, SAM_USB_DESCADD);
regval = USBHOST_CTRLB_SOFE |
USBHOST_CTRLB_SPDCONF_LF |
USBHOST_CTRLB_VBUSOK;
sam_putreg16(regval, SAM_USBHOST_CTRLB);
sam_putreg16(USBHOST_INT_HSOF | USBHOST_INT_RST | USBHOST_INT_WAKEUP |
USBHOST_INT_DNRSM | USBHOST_INT_UPRSM | USBHOST_INT_RAMACER |
USBHOST_INT_DCONN | USBHOST_INT_DDISC,
SAM_USBHOST_INTENCLR);
sam_host_initialize(priv);
return OK;
}
* Public Functions
****************************************************************************/
* Name: sam_usbhost_initialize
*
* Description:
* Initialize USB host device controller hardware.
*
* Input Parameters:
* controller -- If the device supports more than USB host controller, then
* this identifies which controller is being initialized. Normally, this
* is just zero.
*
* Returned Value:
* And instance of the USB host interface. The controlling task should
* use this interface to (1) call the wait() method to wait for a device
* to be connected, and (2) call the enumerate() method to bind the device
* to a class driver.
*
* Assumptions:
* - This function should called in the initialization sequence in order
* to initialize the USB device functionality.
* - Class drivers should be initialized prior to calling this function.
* Otherwise, there is a race condition if the device is already connected.
*
****************************************************************************/
struct usbhost_connection_s *sam_usbhost_initialize(int controller)
{
* pre-allocated as g_usbhost. However, in most code, the private data
* structure will be referenced using the 'priv' pointer (rather than the
* global data) in order to simplify any
* future support for multiple devices.
*/
struct sam_usbhost_s *priv = &g_usbhost;
DEBUGASSERT(controller == 0);
sam_sw_initialize(priv);
sam_hw_initialize(priv);
priv->suspend_start = 0;
priv->resume_start = 0;
priv->pipes_unfreeze = 0;
priv->n_ctrl_req_user = 0;
priv->n_sof_user = 0;
if (irq_attach(SAM_IRQ_USB, sam_usbhost_interrupt, priv) != 0)
usbhost_vtrace1(TRACE_DEVERROR(SAM_TRACEERR_IRQREGISTRATION),
(uint16_t)SAM_IRQ_USB);
if (irq_attach(SAM_IRQ_USBSOF, sam_usbhost_interrupt, priv) != 0)
usbhost_vtrace1(TRACE_DEVERROR(SAM_TRACEERR_IRQREGISTRATION),
(uint16_t)SAM_IRQ_USBSOF);
if (irq_attach(SAM_IRQ_USBTRCPT0, sam_usbhost_interrupt, priv) != 0)
usbhost_vtrace1(TRACE_DEVERROR(SAM_TRACEERR_IRQREGISTRATION),
(uint16_t)SAM_IRQ_USBTRCPT0);
if (irq_attach(SAM_IRQ_USBTRCPT1, sam_usbhost_interrupt, priv) != 0)
usbhost_vtrace1(TRACE_DEVERROR(SAM_TRACEERR_IRQREGISTRATION),
(uint16_t)SAM_IRQ_USBTRCPT1);
up_enable_irq(SAM_IRQ_USB);
up_enable_irq(SAM_IRQ_USBSOF);
up_enable_irq(SAM_IRQ_USBTRCPT0);
up_enable_irq(SAM_IRQ_USBTRCPT1);
return &g_usbconn;
}
#endif
#endif