nuttx/arch/arm/src/dm320/dm320_usbdev.c

2581 lines
74 KiB
C

/*******************************************************************************
* arch/arm/src/dm320/dm320_usbdev.c
*
* Copyright (C) 2008-2013 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*******************************************************************************/
/*******************************************************************************
* Included Files
*******************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/usb/usb.h>
#include <nuttx/usb/usbdev.h>
#include <nuttx/usb/usbdev_trace.h>
#include <arch/irq.h>
#include <arch/board/board.h>
#include "chip.h"
#include "up_arch.h"
#include "up_internal.h"
#include "dm320_usb.h"
/*******************************************************************************
* Pre-Processor Definitions
*******************************************************************************/
/* Configuration ***************************************************************/
#ifndef CONFIG_USBDEV_MAXPOWER
# define CONFIG_USBDEV_MAXPOWER 100 /* mA */
#endif
/* Verify the selected USB attach interrupt GIO line */
#if CONFIG_DM320_GIO_USBATTACH < 0 || CONFIG_DM320_GIO_USBATTACH > 15
# error "CONFIG_DM320_GIO_USBATTACH invalid"
#endif
/* Calculate the IRQ number associated with this GIO */
#define IRQ_USBATTACH (DM320_IRQ_EXT0+CONFIG_DM320_GIO_USBATTACH)
/* Vendor ID & Product ID of the USB device */
#ifndef CONFIG_DM320_VENDORID
# define CONFIG_DM320_VENDORID 0xd320
#endif
#ifndef CONFIG_DM320_PRODUCTID
# define CONFIG_DM320_PRODUCTID 0x3211
#endif
/* Extremely detailed register debug that you would normally never want
* enabled.
*/
#undef CONFIG_DM320_USBDEV_REGDEBUG
/* Debug ***********************************************************************/
/* Trace error codes */
#define DM320_TRACEERR_ALLOCFAIL 0x0001
#define DM320_TRACEERR_ATTACHIRQREG 0x0002
#define DM320_TRACEERR_BINDFAILED 0x0003
#define DM320_TRACEERR_COREIRQREG 0x0004
#define DM320_TRACEERR_DRIVER 0x0005
#define DM320_TRACEERR_DRIVERREGISTERED 0x0006
#define DM320_TRACEERR_EPREAD 0x0007
#define DM320_TRACEERR_EWRITE 0x0008
#define DM320_TRACEERR_INVALIDPARMS 0x0009
#define DM320_TRACEERR_NOEP 0x000a
#define DM320_TRACEERR_NOTCONFIGURED 0x000b
#define DM320_TRACEERR_NULLPACKET 0x000c
#define DM320_TRACEERR_NULLREQUEST 0x000d
#define DM320_TRACEERR_REQABORTED 0x000e
#define DM320_TRACEERR_STALLEDCLRFEATURE 0x000f
#define DM320_TRACEERR_STALLEDISPATCH 0x0010
#define DM320_TRACEERR_STALLEDGETST 0x0011
#define DM320_TRACEERR_STALLEDGETSTEP 0x0012
#define DM320_TRACEERR_STALLEDGETSTRECIP 0x0013
#define DM320_TRACEERR_STALLEDREQUEST 0x0014
#define DM320_TRACEERR_STALLEDSETFEATURE 0x0015
/* Trace interrupt codes */
#define DM320_TRACEINTID_ATTACHED 0x0001
#define DM320_TRACEINTID_ATTACH 0x0002
#define DM320_TRACEINTID_CLEARFEATURE 0x0003
#define DM320_TRACEINTID_CONNECTED 0x0004
#define DM320_TRACEINTID_CONTROL 0x0005
#define DM320_TRACEINTID_DETACHED 0x0006
#define DM320_TRACEINTID_DISCONNECTED 0x0007
#define DM320_TRACEINTID_DISPATCH 0x0008
#define DM320_TRACEINTID_GETENDPOINT 0x0009
#define DM320_TRACEINTID_GETIFDEV 0x000a
#define DM320_TRACEINTID_GETSETDESC 0x000b
#define DM320_TRACEINTID_GETSETIFCONFIG 0x000c
#define DM320_TRACEINTID_GETSTATUS 0x000d
#define DM320_TRACEINTID_RESET 0x000e
#define DM320_TRACEINTID_RESUME 0x000f
#define DM320_TRACEINTID_RXFIFO 0x0010
#define DM320_TRACEINTID_RXPKTRDY 0x0011
#define DM320_TRACEINTID_SESSRQ 0x0012
#define DM320_TRACEINTID_SETADDRESS 0x0013
#define DM320_TRACEINTID_SETFEATURE 0x0014
#define DM320_TRACEINTID_SOF 0x0015
#define DM320_TRACEINTID_SUSPEND 0x0016
#define DM320_TRACEINTID_SYNCHFRAME 0x0017
#define DM320_TRACEINTID_TESTMODE 0x0018
#define DM320_TRACEINTID_TXFIFO 0x0019
#define DM320_TRACEINTID_TXFIFOSETEND 0x001a
#define DM320_TRACEINTID_TXFIFOSTALL 0x001b
#define DM320_TRACEINTID_TXPKTRDY 0x001c
#define DM320_TRACEINTID_UNKNOWN 0x001d
#define DM320_TRACEINTID_USBCTLR 0x001d
#define DM320_TRACEINTID_VBUSERR 0x001f
/* Hardware interface **********************************************************/
/* The DM320 hardware supports 8 configurable endpoints EP1-4, IN and OUT
* (in addition to EP0 IN and OUT). This driver, however, does not exploit
* the full configuratability of the hardware at this time but, instead,
* supports the one interrupt IN, one bulk IN and one bulk OUT endpoint.
*/
/* Hardware dependent sizes and numbers */
#define DM320_EP0MAXPACKET 64 /* EP0 max packet size */
#define DM320_BULKMAXPACKET 64 /* Bulk endpoint max packet */
#define DM320_INTRMAXPACKET 64 /* Interrupt endpoint max packet */
#define DM320_NENDPOINTS 4 /* Includes EP0 */
/* Endpoint numbers */
#define DM320_EP0 0 /* Control endpoint */
#define DM320_EPBULKIN 1 /* Bulk EP for send to host */
#define DM320_EPBULKOUT 2 /* Bulk EP for recv to host */
#define DM320_EPINTRIN 3 /* Intr EP for host poll */
/* Request queue operations ****************************************************/
#define dm320_rqempty(ep) ((ep)->head == NULL)
#define dm320_rqpeek(ep) ((ep)->head)
/*******************************************************************************
* Private Types
*******************************************************************************/
/* A container for a request so that the request make be retained in a list */
struct dm320_req_s
{
struct usbdev_req_s req; /* Standard USB request */
struct dm320_req_s *flink; /* Supports a singly linked list */
};
/* This is the internal representation of an endpoint */
struct dm320_ep_s
{
/* Common endpoint fields. This must be the first thing defined in the
* structure so that it is possible to simply cast from struct usbdev_ep_s
* to struct dm320_ep_s.
*/
struct usbdev_ep_s ep; /* Standard endpoint structure */
/* DM320-specific fields */
struct dm320_usbdev_s *dev; /* Reference to private driver data */
struct dm320_req_s *head; /* Request list for this endpoint */
struct dm320_req_s *tail;
uint8_t epphy; /* Physical EP address/index */
uint8_t stalled:1; /* Endpoint is halted */
uint8_t in:1; /* Endpoint is IN only */
uint8_t halted:1; /* Endpoint feature halted */
uint8_t txnullpkt:1; /* Null packet needed at end of transfer */
};
/* This structure encapsulates the overall driver state */
struct dm320_usbdev_s
{
/* Common device fields. This must be the first thing defined in the
* structure so that it is possible to simply cast from struct usbdev_s
* to struct dm320_usbdev_s.
*/
struct usbdev_s usbdev;
/* The bound device class driver */
struct usbdevclass_driver_s *driver;
/* DM320-specific fields */
uint8_t stalled:1; /* 1: Protocol stalled */
uint8_t selfpowered:1; /* 1: Device is self powered */
uint8_t paddrset:1; /* 1: Peripheral addr has been set */
uint8_t attached:1; /* 1: Host attached */
uint8_t rxpending:1; /* 1: RX pending */
uint8_t paddr; /* Peripheral address */
/* The endpoint list */
struct dm320_ep_s eplist[DM320_NENDPOINTS];
};
/* For maintaining tables of endpoint info */
struct dm320_epinfo_s
{
uint8_t addr; /* Logical endpoint address */
uint8_t attr; /* Endpoint attributes */
uint8_t fifo; /* FIFO mx pkt size + dual buffer bits */
#ifdef CONFIG_USBDEV_HIGHSPEED
uint16_t maxpacket; /* Max packet size */
#else
uint8_t maxpacket; /* Max packet size */
#endif
};
/*******************************************************************************
* Private Function Prototypes
*******************************************************************************/
/* Register operations */
#if defined(CONFIG_DM320_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static uint32_t dm320_getreg8(uint32_t addr);
static uint32_t dm320_getreg16(uint32_t addr);
static uint32_t dm320_getreg32(uint32_t addr);
static void dm320_putreg8(uint8_t val, uint32_t addr);
static void dm320_putreg16(uint16_t val, uint32_t addr);
static void dm320_putreg32(uint32_t val, uint32_t addr);
#else
# define dm320_getreg8(addr) getreg8(addr)
# define dm320_getreg16(addr) getreg16(addr)
# define dm320_getreg32(addr) getreg32(addr)
# define dm320_putreg8(val,addr) putreg8(val,addr)
# define dm320_putreg16(val,addr) putreg16(val,addr)
# define dm320_putreg32(val,addr) putreg32(val,addr)
#endif
/* Request queue operations ****************************************************/
static FAR struct dm320_req_s *dm320_rqdequeue(FAR struct dm320_ep_s *privep);
static void dm320_rqenqueue(FAR struct dm320_ep_s *privep, FAR struct dm320_req_s *req);
/* Low level data transfers and request operations */
static int dm320_ep0write(uint8_t *buf, uint16_t nbytes);
static int dm320_epwrite(uint8_t epphy, uint8_t *buf, uint16_t nbytes);
static int dm320_epread(uint8_t epphy, uint8_t *buf, uint16_t nbytes);
static inline void dm320_abortrequest(struct dm320_ep_s *privep,
struct dm320_req_s *privreq, int16_t result);
static void dm320_reqcomplete(struct dm320_ep_s *privep, int16_t result);
static int dm320_wrrequest(struct dm320_ep_s *privep);
static int dm320_rdrequest(struct dm320_ep_s *privep);
static void dm320_cancelrequests(struct dm320_ep_s *privep);
/* Interrupt handling */
static struct dm320_ep_s *dm320_epfindbyaddr(struct dm320_usbdev_s *priv,
uint16_t eplog);
static void dm320_dispatchrequest(struct dm320_usbdev_s *priv,
const struct usb_ctrlreq_s *ctrl);
static inline void dm320_ep0setup(struct dm320_usbdev_s *priv);
static inline uint32_t dm320_highestpriinterrupt(int intstatus);
static int dm320_ctlrinterrupt(int irq, FAR void *context);
static int dm320_attachinterrupt(int irq, FAR void *context);
/* Initialization operations */
static void dm320_epreset(unsigned int index);
static inline void dm320_epinitialize(struct dm320_usbdev_s *priv);
static void dm320_ctrlinitialize(struct dm320_usbdev_s *priv);
/* Endpoint methods */
static int dm320_epconfigure(FAR struct usbdev_ep_s *ep,
const struct usb_epdesc_s *desc, bool last);
static int dm320_epdisable(FAR struct usbdev_ep_s *ep);
static FAR struct usbdev_req_s *dm320_epallocreq(FAR struct usbdev_ep_s *ep);
static void dm320_epfreereq(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req);
#ifdef CONFIG_USBDEV_DMA
static FAR void *dm320_epallocbuffer(FAR struct usbdev_ep_s *ep, uint16_t nbytes);
static void dm320_epfreebuffer(FAR struct usbdev_ep_s *ep, void *buf);
#endif
static int dm320_epsubmit(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *privreq);
static int dm320_epcancel(FAR struct usbdev_ep_s *ep, struct usbdev_req_s *privreq);
/* USB device controller methods */
static FAR struct usbdev_ep_s *dm320_allocep(FAR struct usbdev_s *dev,
uint8_t epno, bool in, uint8_t eptype);
static void dm320_freeep(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep);
static int dm320_getframe(struct usbdev_s *dev);
static int dm320_wakeup(struct usbdev_s *dev);
static int dm320_selfpowered(struct usbdev_s *dev, bool selfpowered);
static int dm320_pullup(struct usbdev_s *dev, bool enable);
/*******************************************************************************
* Private Data
*******************************************************************************/
/* Endpoint methods */
static const struct usbdev_epops_s g_epops =
{
.configure = dm320_epconfigure,
.disable = dm320_epdisable,
.allocreq = dm320_epallocreq,
.freereq = dm320_epfreereq,
#ifdef CONFIG_USBDEV_DMA
.allocbuffer = dm320_epallocbuffer,
.freebuffer = dm320_epfreebuffer,
#endif
.submit = dm320_epsubmit,
.cancel = dm320_epcancel,
};
/* USB controller device methods */
static const struct usbdev_ops_s g_devops =
{
.allocep = dm320_allocep,
.freeep = dm320_freeep,
.getframe = dm320_getframe,
.wakeup = dm320_wakeup,
.selfpowered = dm320_selfpowered,
#ifdef CONFIG_DM320_GIO_USBDPPULLUP
.pullup = dm320_pullup,
#endif
};
/* There is only one, single, pre-allocated instance of the driver structure */
static struct dm320_usbdev_s g_usbdev;
/* Summarizes information about all DM320 endpoints */
static const struct dm320_epinfo_s g_epinfo[DM320_NENDPOINTS] =
{
{
0, /* EP0 */
USB_EP_ATTR_XFER_CONTROL, /* Type: Control IN/OUT */
USB_TXFIFO2_SZ_64|USB_TXFIFO2_SINGLE_BUF, /* Bits for TX/RXFIFO2 */
DM320_EP0MAXPACKET /* Max packet size */
},
{
DM320_EPBULKIN | USB_DIR_IN, /* Logical endpoint number: 1 IN */
USB_EP_ATTR_XFER_BULK, /* Type: Bulk */
USB_TXFIFO2_SZ_64|USB_TXFIFO2_SINGLE_BUF, /* Bits for TX/RXFIFO2 */
DM320_BULKMAXPACKET, /* Max packet size */
},
{
DM320_EPBULKOUT | USB_DIR_OUT, /* Logical endpoint number: 2 OUT */
USB_EP_ATTR_XFER_BULK, /* Type: Bulk */
USB_TXFIFO2_SZ_64|USB_TXFIFO2_SINGLE_BUF, /* Bits for TX/RXFIFO2 */
DM320_BULKMAXPACKET /* Max packet size */
},
{
DM320_EPINTRIN| USB_DIR_IN, /* Logical endpoint number: 3 IN */
USB_EP_ATTR_XFER_INT, /* Type: Interrupt */
USB_TXFIFO2_SZ_64|USB_TXFIFO2_SINGLE_BUF, /* Bits for TX/RXFIFO2 */
DM320_INTRMAXPACKET /* Max packet size */
}
};
/*******************************************************************************
* Private Functions
*******************************************************************************/
/*******************************************************************************
* Name: dm320_getreg8
*
* Description:
* Get the contents of an DM320 8-bit register
*
*******************************************************************************/
#if defined(CONFIG_DM320_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static uint8_t dm320_getreg8(uint32_t addr)
{
static uint32_t prevaddr = 0;
static uint8_t preval = 0;
static uint32_t count = 0;
/* Read the value from the register */
uint8_t val = getreg8(addr);
/* Is this the same value that we read from the same registe last time? Are
* we polling the register? If so, suppress some of the output.
*/
if (addr == prevaddr || val == preval)
{
if (count == 0xffffffff || ++count > 3)
{
if (count == 4)
{
lldbg("...\n");
}
return val;
}
}
/* No this is a new address or value */
else
{
/* Did we print "..." for the previous value? */
if (count > 3)
{
/* Yes.. then show how many times the value repeated */
lldbg("[repeats %d more times]\n", count-3);
}
/* Save the new address, value, and count */
prevaddr = addr;
preval = val;
count = 1;
}
/* Show the register value read */
lldbg("%08x->%02x\n", addr, val);
return val;
}
#endif
/*******************************************************************************
* Name: dm320_getreg16
*
* Description:
* Get the contents of an DM320 16-bit register
*
*******************************************************************************/
#if defined(CONFIG_DM320_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static uint32_t dm320_getreg16(uint32_t addr)
{
static uint32_t prevaddr = 0;
static uint16_t preval = 0;
static uint32_t count = 0;
/* Read the value from the register */
uint16_t val = getreg16(addr);
/* Is this the same value that we read from the same registe last time? Are
* we polling the register? If so, suppress some of the output.
*/
if (addr == prevaddr || val == preval)
{
if (count == 0xffffffff || ++count > 3)
{
if (count == 4)
{
lldbg("...\n");
}
return val;
}
}
/* No this is a new address or value */
else
{
/* Did we print "..." for the previous value? */
if (count > 3)
{
/* Yes.. then show how many times the value repeated */
lldbg("[repeats %d more times]\n", count-3);
}
/* Save the new address, value, and count */
prevaddr = addr;
preval = val;
count = 1;
}
/* Show the register value read */
lldbg("%08x->%04x\n", addr, val);
return val;
}
#endif
/*******************************************************************************
* Name: dm320_getreg32
*
* Description:
* Get the contents of an DM320 32-bit register
*
*******************************************************************************/
#if defined(CONFIG_DM320_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static uint32_t dm320_getreg32(uint32_t addr)
{
static uint32_t prevaddr = 0;
static uint32_t preval = 0;
static uint32_t count = 0;
/* Read the value from the register */
uint32_t val = getreg32(addr);
/* Is this the same value that we read from the same registe last time? Are
* we polling the register? If so, suppress some of the output.
*/
if (addr == prevaddr || val == preval)
{
if (count == 0xffffffff || ++count > 3)
{
if (count == 4)
{
lldbg("...\n");
}
return val;
}
}
/* No this is a new address or value */
else
{
/* Did we print "..." for the previous value? */
if (count > 3)
{
/* Yes.. then show how many times the value repeated */
lldbg("[repeats %d more times]\n", count-3);
}
/* Save the new address, value, and count */
prevaddr = addr;
preval = val;
count = 1;
}
/* Show the register value read */
lldbg("%08x->%08x\n", addr, val);
return val;
}
#endif
/*******************************************************************************
* Name: dm320_putreg8
*
* Description:
* Set the contents of an DM320 8-bit register to a value
*
*******************************************************************************/
#if defined(CONFIG_DM320_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static void dm320_putreg8(uint8_t val, uint32_t addr)
{
/* Show the register value being written */
lldbg("%08x<-%02x\n", addr, val);
/* Write the value */
putreg8(val, addr);
}
#endif
/*******************************************************************************
* Name: dm320_putreg16
*
* Description:
* Set the contents of an DM320 16-bit register to a value
*
*******************************************************************************/
#if defined(CONFIG_DM320_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static void dm320_putreg16(uint16_t val, uint32_t addr)
{
/* Show the register value being written */
lldbg("%08x<-%04x\n", addr, val);
/* Write the value */
putreg16(val, addr);
}
#endif
/*******************************************************************************
* Name: dm320_putreg32
*
* Description:
* Set the contents of an DM320 32-bit register to a value
*
*******************************************************************************/
#if defined(CONFIG_DM320_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static void dm320_putreg32(uint32_t val, uint32_t addr)
{
/* Show the register value being written */
lldbg("%08x<-%08x\n", addr, val);
/* Write the value */
putreg32(val, addr);
}
#endif
/*******************************************************************************
* Name: dm320_rqdequeue
*
* Description:
* Remove a request from an endpoint request queue
*
*******************************************************************************/
static FAR struct dm320_req_s *dm320_rqdequeue(FAR struct dm320_ep_s *privep)
{
FAR struct dm320_req_s *ret = privep->head;
if (ret)
{
privep->head = ret->flink;
if (!privep->head)
{
privep->tail = NULL;
}
ret->flink = NULL;
}
return ret;
}
/*******************************************************************************
* Name: dm320_rqenqueue
*
* Description:
* Add a request from an endpoint request queue
*
*******************************************************************************/
static void dm320_rqenqueue(FAR struct dm320_ep_s *privep,
FAR struct dm320_req_s *req)
{
req->flink = NULL;
if (!privep->head)
{
privep->head = req;
privep->tail = req;
}
else
{
privep->tail->flink = req;
privep->tail = req;
}
}
/*******************************************************************************
* Name: dm320_ep0write
*
* Description:
* Control endpoint write (IN)
*
*******************************************************************************/
static int dm320_ep0write(uint8_t *buf, uint16_t nbytes)
{
uint8_t csr0 = USB_PERCSR0_TXPKTRDY; /* XMT packet ready bit */
uint16_t bytesleft;
uint16_t nwritten;
if ( nbytes <= DM320_EP0MAXPACKET)
{
bytesleft = nbytes;
csr0 |= USB_PERCSR0_DATAEND; /* Transaction end bit */
}
else
{
bytesleft = DM320_EP0MAXPACKET;
}
nwritten = bytesleft;
while (bytesleft > 0)
{
dm320_putreg8(*buf++, DM320_USB_FIFO0);
bytesleft--;
}
dm320_putreg8(csr0, DM320_USB_PERCSR0);
return nwritten;
}
/*******************************************************************************
* Name: dm320_epwrite
*
* Description:
* Endpoint write (IN)
*
*******************************************************************************/
static int dm320_epwrite(uint8_t epphy, uint8_t *buf, uint16_t nbytes)
{
volatile uint8_t *fifo;
uint16_t bytesleft;
int ret = ERROR;
if (/*epphy < USB_EP0_SELECT || */ epphy >= DM320_NENDPOINTS)
{
return ret;
}
dm320_putreg8(epphy, DM320_USB_INDEX);
if (epphy == USB_EP0_SELECT )
{
return dm320_ep0write(buf, nbytes);
}
bytesleft = DM320_BULKMAXPACKET;
if (bytesleft > nbytes)
{
bytesleft = nbytes;
}
ret = bytesleft;
fifo = (volatile uint8_t *)DM320_USB_FIFO0;
fifo = fifo + (epphy << 2);
if (dm320_getreg8(DM320_USB_PERTXCSR1) & USB_TXCSR1_FIFOEMP)
{
dm320_putreg8(dm320_getreg8(DM320_USB_PERTXCSR1) | USB_TXCSR1_TXPKTRDY, DM320_USB_PERTXCSR1);
while (dm320_getreg8(DM320_USB_PERTXCSR1) & USB_TXCSR1_TXPKTRDY);
dm320_putreg8((dm320_getreg8(DM320_USB_PERTXCSR1) | USB_TXCSR1_FLFIFO), DM320_USB_PERTXCSR1);
}
while (bytesleft > 0)
{
*fifo = *buf++;
bytesleft--;
}
dm320_putreg8(dm320_getreg8(DM320_USB_PERTXCSR1) | USB_TXCSR1_TXPKTRDY, DM320_USB_PERTXCSR1);
return ret;
}
/*******************************************************************************
* Name: dm320_epread
*
* Description:
* Endpoint read (OUT)
*
*******************************************************************************/
static int dm320_epread(uint8_t epphy, uint8_t *buf, uint16_t nbytes)
{
volatile uint8_t *fifo;
int bytesleft;
int ret = ERROR;
if (/*epphy < USB_EP0_SELECT || */ epphy >= DM320_NENDPOINTS)
{
return ret;
}
dm320_putreg8(epphy, DM320_USB_INDEX);
if (epphy == USB_EP0_SELECT)
{
bytesleft = dm320_getreg8(DM320_USB_COUNT0);
if (bytesleft > nbytes)
{
bytesleft = nbytes;
}
}
else
{
bytesleft = dm320_getreg8(DM320_USB_RXCOUNT2);
bytesleft = (bytesleft << 8) + dm320_getreg8(DM320_USB_RXCOUNT1);
if (bytesleft > nbytes)
{
bytesleft = nbytes;
}
}
ret = bytesleft;
fifo = (uint8_t*)DM320_USB_FIFO0;
fifo = fifo + (epphy << 2);
while (bytesleft > 0)
{
*buf++ = *fifo;
bytesleft--;
}
/* Clear RXPKTRDY bit in PER_RXCSR1 */
dm320_putreg8(dm320_getreg8(DM320_USB_PERRXCSR1) & ~(USB_PERRXCSR1_RXPKTRDY), DM320_USB_PERRXCSR1);
return ret;
}
/*******************************************************************************
* Name: dm320_abortrequest
*
* Description:
* Discard a request
*
*******************************************************************************/
static inline void dm320_abortrequest(struct dm320_ep_s *privep,
struct dm320_req_s *privreq,
int16_t result)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_REQABORTED), (uint16_t)privep->epphy);
/* Save the result in the request structure */
privreq->req.result = result;
/* Callback to the request completion handler */
privreq->req.callback(&privep->ep, &privreq->req);
}
/*******************************************************************************
* Name: dm320_reqcomplete
*
* Description:
* Handle termination of a request.
*
*******************************************************************************/
static void dm320_reqcomplete(struct dm320_ep_s *privep, int16_t result)
{
struct dm320_req_s *privreq;
int stalled = privep->stalled;
irqstate_t flags;
/* Remove the completed request at the head of the endpoint request list */
flags = irqsave();
privreq = dm320_rqdequeue(privep);
irqrestore(flags);
if (privreq)
{
/* If endpoint 0, temporarily reflect the state of protocol stalled
* in the callback.
*/
if (privep->epphy == 0)
{
if (privep->dev->stalled)
{
privep->stalled = 1;
}
}
/* Save the result in the request structure */
privreq->req.result = result;
/* Callback to the request completion handler */
privreq->flink = NULL;
privreq->req.callback(&privep->ep, &privreq->req);
/* Restore the stalled indication */
privep->stalled = stalled;
}
}
/*******************************************************************************
* Name: dm320_wrrequest
*
* Description:
* Send from the next queued write request
*
* Returned Value:
* 0:not finished; 1:completed; <0:error
*
*******************************************************************************/
static int dm320_wrrequest(struct dm320_ep_s *privep)
{
struct dm320_req_s *privreq;
uint8_t *buf;
int nbytes;
int bytesleft;
int nbyteswritten;
/* Check the request from the head of the endpoint request queue */
privreq = dm320_rqpeek(privep);
if (!privreq)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_NULLREQUEST), 0);
return OK;
}
/* Otherwise send the data in the packet (in the DMA on case, we
* may be resuming transfer already in progress.
*/
for (;;)
{
/* Get the number of bytes left to be sent in the packet */
bytesleft = privreq->req.len - privreq->req.xfrd;
/* Send the next packet if (1) there are more bytes to be sent, or
* (2) the last packet sent was exactly maxpacketsize (bytesleft == 0)
*/
usbtrace(TRACE_WRITE(privep->epphy), privreq->req.xfrd);
if (bytesleft > 0 || privep->txnullpkt)
{
/* Try to send maxpacketsize -- unless we don't have that many
* bytes to send.
*/
privep->txnullpkt = 0;
if (bytesleft > privep->ep.maxpacket)
{
nbytes = privep->ep.maxpacket;
}
else
{
nbytes = bytesleft;
if ((privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0)
{
privep->txnullpkt = (bytesleft == privep->ep.maxpacket);
}
}
/* Send the largest number of bytes that we can in this packet */
buf = privreq->req.buf + privreq->req.xfrd;
nbyteswritten = dm320_epwrite(privep->epphy, buf, nbytes);
if (nbyteswritten < 0 || nbyteswritten != nbytes)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_EWRITE), nbyteswritten);
return ERROR;
}
/* Update for the next time through the loop */
privreq->req.xfrd += nbytes;
}
/* If all of the bytes were sent (including any final null packet)
* then we are finished with the transfer
*/
if (privreq->req.xfrd >= privreq->req.len && !privep->txnullpkt)
{
usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd);
privep->txnullpkt = 0;
dm320_reqcomplete(privep, OK);
return OK;
}
}
return OK; /* Won't get here */
}
/*******************************************************************************
* Name: dm320_rdrequest
*
* Description:
* Receive to the next queued read request
*
*******************************************************************************/
static int dm320_rdrequest(struct dm320_ep_s *privep)
{
struct dm320_req_s *privreq;
uint8_t *buf;
int nbytesread;
/* Check the request from the head of the endpoint request queue */
privreq = dm320_rqpeek(privep);
if (!privreq)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_NULLREQUEST), 0);
return OK;
}
usbtrace(TRACE_READ(privep->epphy), privreq->req.xfrd);
/* Receive the next packet */
buf = privreq->req.buf + privreq->req.xfrd;
nbytesread = dm320_epread(privep->epphy, buf, privep->ep.maxpacket);
if (nbytesread < 0)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_EPREAD), nbytesread);
return ERROR;
}
/* If the receive buffer is full or if the last packet was not full
* then we are finished with the transfer.
*/
privreq->req.xfrd += nbytesread;
if (privreq->req.len < privreq->req.xfrd || nbytesread < privep->ep.maxpacket)
{
usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd);
dm320_reqcomplete(privep, OK);
}
return OK;
}
/*******************************************************************************
* Name: dm320_cancelrequests
*
* Description:
* Cancel all pending requests for an endpoint
*
*******************************************************************************/
static void dm320_cancelrequests(struct dm320_ep_s *privep)
{
while (!dm320_rqempty(privep))
{
usbtrace(TRACE_COMPLETE(privep->epphy),
(dm320_rqpeek(privep))->req.xfrd);
dm320_reqcomplete(privep, -ESHUTDOWN);
}
}
/*******************************************************************************
* Name: dm320_epfindbyaddr
*
* Description:
* Find the physical endpoint structure corresponding to a logic endpoint
* address
*
*******************************************************************************/
static struct dm320_ep_s *dm320_epfindbyaddr(struct dm320_usbdev_s *priv,
uint16_t eplog)
{
struct dm320_ep_s *privep;
int i;
/* Endpoint zero is a special case */
if (USB_EPNO(eplog) == 0)
{
return &priv->eplist[0];
}
/* Handle the remaining */
for (i = 1; i < DM320_NENDPOINTS; i++)
{
privep = &priv->eplist[i];
/* Same logical endpoint number? (includes direction bit) */
if (eplog == privep->ep.eplog)
{
/* Return endpoint found */
return privep;
}
}
/* Return endpoint not found */
return NULL;
}
/*******************************************************************************
* Name: dm320_dispatchrequest
*
* Description:
* Provide unhandled setup actions to the class driver
*
*******************************************************************************/
static void dm320_dispatchrequest(struct dm320_usbdev_s *priv,
const struct usb_ctrlreq_s *ctrl)
{
int ret;
usbtrace(TRACE_INTDECODE(DM320_TRACEINTID_DISPATCH), 0);
if (priv && priv->driver)
{
ret = CLASS_SETUP(priv->driver, &priv->usbdev, ctrl, NULL, 0);
if (ret < 0)
{
/* Stall on failure */
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_STALLEDISPATCH), ctrl->req);
priv->stalled = 1;
}
}
}
/*******************************************************************************
* Name: dm320_ep0setup
*
* Description:
* USB Ctrl EP Setup Event
*
*******************************************************************************/
static inline void dm320_ep0setup(struct dm320_usbdev_s *priv)
{
struct dm320_ep_s *ep0 = &priv->eplist[DM320_EP0];
struct dm320_req_s *privreq = dm320_rqpeek(ep0);
struct dm320_ep_s *privep;
struct usb_ctrlreq_s ctrl;
uint16_t index;
uint16_t value;
uint16_t len;
int ret;
/* Starting a control request? */
if (priv->usbdev.speed == USB_SPEED_UNKNOWN)
{
priv->usbdev.speed = USB_SPEED_FULL;
}
/* Terminate any pending requests */
while (!dm320_rqempty(ep0))
{
int16_t result = OK;
if (privreq->req.xfrd != privreq->req.len)
{
result = -EPROTO;
}
usbtrace(TRACE_COMPLETE(ep0->epphy), privreq->req.xfrd);
dm320_reqcomplete(ep0, result);
}
/* Assume NOT stalled */
ep0->stalled = 0;
priv->stalled = 0;
/* Read EP0 data */
ret = dm320_epread(USB_EP0_SELECT, (uint8_t*)&ctrl, USB_SIZEOF_CTRLREQ);
if (ret <= 0)
{
return;
}
index = GETUINT16(ctrl.index);
value = GETUINT16(ctrl.value);
len = GETUINT16(ctrl.len);
ullvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n",
ctrl.type, ctrl.req, value, index, len);
/* Dispatch any non-standard requests */
ep0->in = (ctrl.type & USB_DIR_IN) != 0;
if ((ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD)
{
dm320_putreg8(USB_PERCSR0_CLRRXRDY, DM320_USB_PERCSR0);
dm320_dispatchrequest(priv, &ctrl);
return;
}
/* Handle standard request. Pick off the things of interest to the
* USB device controller driver; pass what is left to the class driver
*/
switch (ctrl.req)
{
case USB_REQ_GETSTATUS:
{
/* type: device-to-host; recipient = device, interface, endpoint
* value: 0
* index: zero interface endpoint
* len: 2; data = status
*/
dm320_putreg8(USB_PERCSR0_CLRRXRDY | USB_PERCSR0_DATAEND, DM320_USB_PERCSR0);
usbtrace(TRACE_INTDECODE(DM320_TRACEINTID_GETSTATUS), 0);
if (len != 2 || (ctrl.type & USB_REQ_DIR_IN) == 0 || value != 0)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_STALLEDGETST), ctrl.req);
priv->stalled = 1;
}
else
{
switch (ctrl.type & USB_REQ_RECIPIENT_MASK)
{
case USB_REQ_RECIPIENT_ENDPOINT:
{
usbtrace(TRACE_INTDECODE(DM320_TRACEINTID_GETENDPOINT), 0);
privep = dm320_epfindbyaddr(priv, index);
if (!privep)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_STALLEDGETSTEP), ctrl.type);
priv->stalled = 1;
}
}
break;
case USB_REQ_RECIPIENT_DEVICE:
case USB_REQ_RECIPIENT_INTERFACE:
usbtrace(TRACE_INTDECODE(DM320_TRACEINTID_GETIFDEV), 0);
break;
default:
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_STALLEDGETSTRECIP), ctrl.type);
priv->stalled = 1;
}
break;
}
}
}
break;
case USB_REQ_CLEARFEATURE:
{
/* type: host-to device; recipient = device, interface or endpoint
* value: feature selector
* index: zero interface endpoint;
* len: zero, data = none
*/
dm320_putreg8(USB_PERCSR0_CLRRXRDY | USB_PERCSR0_DATAEND, DM320_USB_PERCSR0);
usbtrace(TRACE_INTDECODE(DM320_TRACEINTID_CLEARFEATURE), (uint16_t)ctrl.req);
if (ctrl.type != USB_REQ_RECIPIENT_ENDPOINT)
{
dm320_dispatchrequest(priv, &ctrl);
}
else if (value == USB_FEATURE_ENDPOINTHALT && len == 0 &&
(privep = dm320_epfindbyaddr(priv, index)) != NULL)
{
privep->halted = 0;
/* Restart the write queue */
(void)dm320_wrrequest(privep);
}
else
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_STALLEDCLRFEATURE), ctrl.type);
priv->stalled = 1;
}
}
break;
case USB_REQ_SETFEATURE:
{
/* type: host-to-device; recipient = device, interface, endpoint
* value: feature selector
* index: zero interface endpoint;
* len: 0; data = none
*/
dm320_putreg8(USB_PERCSR0_CLRRXRDY | USB_PERCSR0_DATAEND, DM320_USB_PERCSR0);
usbtrace(TRACE_INTDECODE(DM320_TRACEINTID_SETFEATURE), 0);
if (ctrl.type == USB_REQ_RECIPIENT_DEVICE && value == USB_FEATURE_TESTMODE)
{
usbtrace(TRACE_INTDECODE(DM320_TRACEINTID_TESTMODE), index);
}
else if (ctrl.type != USB_REQ_RECIPIENT_ENDPOINT)
{
dm320_dispatchrequest(priv, &ctrl);
}
else if (value == USB_FEATURE_ENDPOINTHALT && len == 0 &&
(privep = dm320_epfindbyaddr(priv, index)) != NULL)
{
privep->halted = 1;
}
else
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_STALLEDSETFEATURE), ctrl.type);
priv->stalled = 1;
}
}
break;
case USB_REQ_SETADDRESS:
{
/* type: host-to-device; recipient = device
* value: device address
* index: 0
* len: 0; data = none
*/
dm320_putreg8(USB_PERCSR0_CLRRXRDY|USB_PERCSR0_DATAEND, DM320_USB_PERCSR0);
usbtrace(TRACE_INTDECODE(DM320_TRACEINTID_SETADDRESS), 0);
priv->paddr = value & 0xff;
}
break;
case USB_REQ_GETDESCRIPTOR:
/* type: device-to-host; recipient = device
* value: descriptor type and index
* index: 0 or language ID;
* len: descriptor len; data = descriptor
*/
case USB_REQ_SETDESCRIPTOR:
/* type: host-to-device; recipient = device
* value: descriptor type and index
* index: 0 or language ID;
* len: descriptor len; data = descriptor
*/
{
dm320_putreg8(USB_PERCSR0_CLRRXRDY, DM320_USB_PERCSR0);
usbtrace(TRACE_INTDECODE(DM320_TRACEINTID_GETSETDESC), 0);
dm320_dispatchrequest(priv, &ctrl);
}
break;
case USB_REQ_GETCONFIGURATION:
/* type: device-to-host; recipient = device
* value: 0;
* index: 0;
* len: 1; data = configuration value
*/
case USB_REQ_SETCONFIGURATION:
/* type: host-to-device; recipient = device
* value: configuration value
* index: 0;
* len: 0; data = none
*/
case USB_REQ_GETINTERFACE:
/* type: device-to-host; recipient = interface
* value: 0
* index: interface;
* len: 1; data = alt interface
*/
case USB_REQ_SETINTERFACE:
/* type: host-to-device; recipient = interface
* value: alternate setting
* index: interface;
* len: 0; data = none
*/
{
dm320_putreg8(USB_PERCSR0_CLRRXRDY|USB_PERCSR0_DATAEND, DM320_USB_PERCSR0);
usbtrace(TRACE_INTDECODE(DM320_TRACEINTID_GETSETIFCONFIG), 0);
dm320_dispatchrequest(priv, &ctrl);
}
break;
case USB_REQ_SYNCHFRAME:
{
/* type: device-to-host; recipient = endpoint
* value: 0
* index: endpoint;
* len: 2; data = frame number
*/
dm320_putreg8(USB_PERCSR0_CLRRXRDY|USB_PERCSR0_SENDST, DM320_USB_PERCSR0);
usbtrace(TRACE_INTDECODE(DM320_TRACEINTID_SYNCHFRAME), 0);
}
break;
default:
{
dm320_putreg8(USB_PERCSR0_CLRRXRDY|USB_PERCSR0_SENDST, DM320_USB_PERCSR0);
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_STALLEDREQUEST), ctrl.req);
priv->stalled = 1;
}
break;
}
}
/*******************************************************************************
* Name: dm320_highestpriinterrupt
*
* Description:
* Part of the USB core controller interrupt handling logic
*
*******************************************************************************/
static inline uint32_t dm320_highestpriinterrupt(int intstatus)
{
if ((intstatus & USB_INT_CONNECTED) != 0)
return USB_INT_CONNECTED;
if ((intstatus & USB_INT_DISCONNECTED) != 0)
return USB_INT_DISCONNECTED;
if ((intstatus & USB_INT_RESET) != 0)
return USB_INT_RESET;
if ((intstatus & USB_INT_RESUME) != 0)
return USB_INT_RESUME;
if ((intstatus & USB_INT_SESSRQ) != 0)
return USB_INT_SESSRQ;
if ((intstatus & USB_INT_VBUSERR) != 0)
return USB_INT_VBUSERR;
if ((intstatus & USB_INT_SOF) != 0)
return USB_INT_SOF;
if ((intstatus & USB_INT_SUSPEND) != 0)
return USB_INT_SUSPEND;
if ((intstatus & USB_INT_CONTROL) != 0)
return USB_INT_CONTROL;
if ((intstatus & USB_INT_RXFIFO) != 0)
return USB_INT_RXFIFO;
if ((intstatus & USB_INT_TXFIFO) != 0)
return USB_INT_TXFIFO;
return USB_INT_NOINTERRUPT;
}
/*******************************************************************************
* Name: dm320_ctlrinterrupt
*
* Description:
* Handle USB controller core interrupts
*
*******************************************************************************/
static int dm320_ctlrinterrupt(int irq, FAR void *context)
{
struct dm320_usbdev_s *priv = &g_usbdev;
struct dm320_ep_s *privep ;
uint32_t intstatus;
uint32_t priorityint;
uint8_t csr0;
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_USBCTLR), 0);
/* Combine interretup registers into one interrupt status value */
intstatus = ((uint32_t)dm320_getreg8(DM320_USB_INTRTX1) << 12) |
(((uint32_t)dm320_getreg8(DM320_USB_INTRRX1) >> 1) << 8) |
(uint32_t)dm320_getreg8(DM320_USB_INTRUSB);
/* Then process each interrupt source, highest priority first */
do
{
priorityint = dm320_highestpriinterrupt(intstatus);
switch (priorityint)
{
case USB_INT_RESET:
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_RESET), 0);
priv->paddrset = 0;
break;
case USB_INT_SESSRQ:
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_SESSRQ), 0);
break;
case USB_INT_VBUSERR:
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_VBUSERR), 0);
break;
case USB_INT_CONNECTED:
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_CONNECTED), 0);
break;
case USB_INT_RESUME:
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_RESUME), 0);
break;
case USB_INT_CONTROL:
{
/* Select EP0 */
dm320_putreg8(USB_EP0_SELECT, DM320_USB_INDEX);
/* Check for a response complete interrupt */
csr0 = dm320_getreg8(DM320_USB_PERCSR0);
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_CONTROL), csr0);
if (csr0 == 0x00)
{
/* Check if we need to set the peripheral address */
if (!priv->paddrset && priv->paddr != 0)
{
dm320_putreg8(priv->paddr, DM320_USB_FADDR);
priv->paddrset = 1;
break;
}
}
if ((csr0 & USB_PERCSR0_RXPKTRDY) != 0)
{
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_RXPKTRDY), csr0);
(void)dm320_getreg8(DM320_USB_COUNT0);
dm320_ep0setup(priv);
}
else if ((csr0 & USB_PERCSR0_SENTST) != 0)
{
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_TXFIFOSTALL), csr0);
dm320_putreg8(0, DM320_USB_PERCSR0);
}
else if ((csr0 & USB_PERCSR0_SETEND) != 0)
{
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_TXFIFOSETEND), csr0);
dm320_putreg8(USB_PERCSR0_CLRSETEND, DM320_USB_PERCSR0);
}
else if ((csr0 & USB_PERCSR0_TXPKTRDY) != 0)
{
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_TXPKTRDY), csr0);
}
else
{
/* Now ignore these unknown interrupts */
dm320_putreg8(USB_PERCSR0_CLRRXRDY | USB_PERCSR0_DATAEND, DM320_USB_PERCSR0);
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_UNKNOWN), csr0);
}
}
break;
case USB_INT_RXFIFO:
{
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_RXFIFO), 0);
privep = &priv->eplist[DM320_EPBULKOUT];
if (!dm320_rqempty(privep))
{
dm320_rdrequest(privep);
}
else
{
ullvdbg("Pending data on OUT endpoint\n");
priv->rxpending = 1;
}
}
break;
case USB_INT_TXFIFO:
{
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_TXFIFO), 0);
#ifdef PIPE_STALL
dm320_putreg8(DM320_EPBULKIN, DM320_USB_INDEX);
if (dm320_getreg8(DM320_USB_PERTXCSR1) & USB_TXCSR1_SENTST)
{
dm320_putreg8(dm320_getreg8(DM320_USB_PERTXCSR1) & ~USB_TXCSR1_SENTST, DM320_USB_PERTXCSR1);
dm320_putreg8(dm320_getreg8(DM320_USB_PERTXCSR1) & ~USB_TXCSR1_SENDST, DM320_USB_PERTXCSR1);
}
#endif
if (priv->usbdev.speed == USB_SPEED_UNKNOWN)
{
priv->usbdev.speed = USB_SPEED_FULL;
}
privep = &priv->eplist[DM320_EPBULKIN];
if (!dm320_rqempty(privep))
{
(void)dm320_wrrequest(privep);
}
}
break;
case USB_INT_SOF:
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_SOF), 0);
break;
case USB_INT_DISCONNECTED:
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_DISCONNECTED), 0);
break;
case USB_INT_SUSPEND:
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_SUSPEND), 0);
break;
default:
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_UNKNOWN), 0);
break;
}
intstatus = intstatus & ~priorityint;
}
while (intstatus != USB_INT_NOINTERRUPT);
return OK;
}
/*******************************************************************************
* Name: dm320_attachinterrupt
*
* Description:
* Attach GIO interrtup handler
*
*******************************************************************************/
static int dm320_attachinterrupt(int irq, FAR void *context)
{
struct dm320_usbdev_s *priv = &g_usbdev;
uint16_t gio;
/* Check if the USB device was connected to or disconnected from a host */
gio = dm320_getreg16(DM320_GIO_BITSET0);
usbtrace(TRACE_INTENTRY(DM320_TRACEINTID_ATTACH), gio);
if ((gio & (1 << CONFIG_DM320_GIO_USBATTACH)) == 0)
{
/* The host is disconnected */
if (priv->attached)
{
/* We have detected a transition from attached to detached */
usbtrace(TRACE_INTDECODE(DM320_TRACEINTID_DETACHED), 0);
priv->usbdev.speed = USB_SPEED_UNKNOWN;
priv->attached = 0;
dm320_putreg16(dm320_getreg16(DM320_CLKC_LPCTL1) | 0x0010, DM320_CLKC_LPCTL1);
if ((dm320_getreg8(DM320_USB_PERTXCSR1) & USB_TXCSR1_FIFOEMP))
{
dm320_putreg8(USB_TXCSR1_FLFIFO, DM320_USB_PERTXCSR1);
up_mdelay(5);
}
}
}
else if (!priv->attached)
{
/* We have a transition from unattached to attached */
usbtrace(TRACE_INTDECODE(DM320_TRACEINTID_ATTACHED), 0);
priv->usbdev.speed = USB_SPEED_UNKNOWN;
dm320_ctrlinitialize(priv);
dm320_putreg16(dm320_getreg16(DM320_INTC_FISEL0) & 0x7f, DM320_INTC_FISEL0);
dm320_putreg16(dm320_getreg16(DM320_INTC_EINT0) | 0x80, DM320_INTC_EINT0);
priv->usbdev.speed = USB_SPEED_UNKNOWN;
priv->paddrset = 0;
priv->paddr = 0;
priv->attached = 1;
}
return OK;
}
/*******************************************************************************
* Name: dm320_epreset
*******************************************************************************/
static void dm320_epreset(unsigned int index)
{
dm320_putreg8(index, DM320_USB_INDEX);
dm320_putreg8(USB_PERCSR0_CLRSETEND | USB_PERCSR0_CLRRXRDY, DM320_USB_PERCSR0);
dm320_putreg8(USB_CSR2_FLFIFO, DM320_USB_CSR2);
dm320_putreg8(USB_CSR2_FLFIFO, DM320_USB_CSR2);
}
/*******************************************************************************
* Name: dm320_epinitialize
*
* Description:
* Initialize endpoints. This is logically a part of dm320_ctrlinitialize
*
*******************************************************************************/
static inline void dm320_epinitialize(struct dm320_usbdev_s *priv)
{
uint16_t offset; /* Full USB buffer offset */
uint8_t addrhi; /* MS bytes of ofset */
uint8_t addrlo; /* LS bytes of offset */
int i;
/* Initialize endpoint 0 */
dm320_putreg8(USB_EP0_SELECT, DM320_USB_INDEX);
dm320_putreg8(USB_PERCSR0_CLRSETEND|USB_PERCSR0_CLRRXRDY, DM320_USB_PERCSR0);
dm320_putreg8(USB_CSR2_FLFIFO, DM320_USB_CSR2);
dm320_putreg8(USB_CSR2_FLFIFO, DM320_USB_CSR2);
/* EP0 Fifo size/address (ofset == 0) */
dm320_putreg8(0x00, DM320_USB_TXFIFO1);
dm320_putreg8(0x00, DM320_USB_RXFIFO1);
dm320_putreg8(g_epinfo[0].fifo, DM320_USB_TXFIFO2);
dm320_putreg8(USB_TXFIFO2_SZ_64, DM320_USB_RXFIFO2);
/* EP0 max packet size */
dm320_putreg8(g_epinfo[0].maxpacket >> 3, DM320_USB_TXMAXP);
dm320_putreg8(g_epinfo[0].maxpacket >> 3, DM320_USB_RXMAXP);
/* Setup bulk-in, bulk-out, iso-in, iso-out, and intr endpoints using the
* g_epinfo[] array.
*/
offset = DM320_EP0MAXPACKET; /* move to next buffer position */
for (i = 1; i < DM320_NENDPOINTS; i++)
{
dm320_putreg8(g_epinfo[i].addr & 0x0f, DM320_USB_INDEX);
addrlo = (offset >> 8) & 0xff;
addrhi = (offset >= 2048) ? 1 : 0;
/* Configure IN endpoints (device-to-host) */
if (USB_EPIN(g_epinfo[i].addr))
{
/* Initialize TX endpoint */
dm320_putreg8(USB_TXCSR1_CLRDATTOG|USB_TXCSR1_FLFIFO|USB_TXCSR1_UNDERRUN,
DM320_USB_PERTXCSR1);
dm320_putreg8(USB_TXCSR1_FLFIFO, DM320_USB_PERTXCSR1);
dm320_putreg8(USB_TXCSR2_FRDATTOG|USB_TXCSR2_MODE_TX, DM320_USB_TXCSR2);
/* FIFO address, max packet size, dual/single buffered */
dm320_putreg8(addrlo, DM320_USB_TXFIFO1);
dm320_putreg8(addrhi|g_epinfo[i].fifo, DM320_USB_TXFIFO2);
/* TX endpoint max packet size */
dm320_putreg8(g_epinfo[i].maxpacket >> 3, DM320_USB_TXMAXP);
}
/* Configure OUT endpoints (host-to-device) */
else
{
/* Initialize RX endpoint */
dm320_putreg8(USB_PERRXCSR1_CLRDATTOG|USB_PERRXCSR1_FLFIFO,
DM320_USB_PERRXCSR1);
dm320_putreg8(USB_PERRXCSR1_FLFIFO, DM320_USB_PERRXCSR1);
dm320_putreg8(0x00, DM320_USB_PERRXCSR2);
/* FIFO address, max packet size, dual/single buffered */
dm320_putreg8(addrhi, DM320_USB_RXFIFO1);
dm320_putreg8(addrhi|g_epinfo[i].fifo | USB_RXFIF02_DPB, DM320_USB_RXFIFO2);
/* RX endpoint max packet size */
dm320_putreg8(g_epinfo[i].maxpacket >> 3, DM320_USB_RXMAXP);
}
offset += g_epinfo[i].maxpacket;
}
}
/*******************************************************************************
* Name: dm320_ctrlinitialize
*
* Description:
* Initialize the DM320 USB controller for peripheral mode operation .
*
*******************************************************************************/
static void dm320_ctrlinitialize(FAR struct dm320_usbdev_s *priv)
{
/* Setup the USB controller for operation as a periperhal *******************/
/* Enable USB clock */
dm320_putreg16(dm320_getreg16(DM320_CLKC_MOD2) | 0x0060, DM320_CLKC_MOD2);
/* Disable USB Power down mode */
dm320_putreg16(dm320_getreg16(DM320_CLKC_LPCTL1) & 0xFFEF, DM320_CLKC_LPCTL1);
/* Put USB controller in peripheral mode */
dm320_putreg32(0x00000000, DM320_AHB_USBCTL);
dm320_putreg8(USB_DEVCTL_SESSREQ, DM320_USB_DEVCTL);
/* Reset USB controller registers */
dm320_putreg8(0x00, DM320_USB_FADDR); /* Reset peripheral address register */
dm320_putreg8(0x00, DM320_USB_POWER); /* Reset power control register */
/* Initialize interrupts *****************************************************/
up_maskack_irq(DM320_IRQ_USB0); /* Clear USB controller interrupt */
up_maskack_irq(DM320_IRQ_USB1); /* Clear USB DMA interrupt flag */
dm320_getreg8(DM320_USB_INTRTX1); /* Clear TX interrupt */
dm320_getreg8(DM320_USB_INTRRX1); /* Clear RX interrupt */
dm320_getreg8(DM320_USB_INTRUSB); /* Clear control interrupt */
dm320_getreg8(DM320_USB_DMAINTR);
/* Enable USB interrupts */
dm320_putreg8((DM320_EPBULKIN << 1), DM320_USB_INTRRX1E);
dm320_putreg8((DM320_EPBULKOUT << 1) | USB_EP0, DM320_USB_INTRTX1E);
dm320_putreg8(USB_INT_RESET|USB_INT_RESUME|USB_INT_SUSPEND|USB_INT_SESSRQ|USB_INT_SOF,
DM320_USB_INTRUSBE);
/* Initialize endpoints ******************************************************/
dm320_epinitialize(priv);
/* Peripheral address has not yet been set */
priv->paddr = 0;
dm320_putreg8(0, DM320_USB_FADDR);
/* Finished -- set default endpoint as EP0*/
dm320_putreg8(USB_EP0_SELECT, DM320_USB_INDEX);
}
/*******************************************************************************
* Endpoint Methods
*******************************************************************************/
/*******************************************************************************
* Name: dm320_epconfigure
*
* Description:
* Configure endpoint, making it usable
*
* Input Parameters:
* ep - the struct usbdev_ep_s instance obtained from allocep()
* desc - A struct usb_epdesc_s instance describing the endpoint
* last - true if this this last endpoint to be configured. Some hardware
* needs to take special action when all of the endpoints have been
* configured.
*
*******************************************************************************/
static int dm320_epconfigure(FAR struct usbdev_ep_s *ep,
FAR const struct usb_epdesc_s *desc,
bool last)
{
FAR struct dm320_ep_s *privep = (FAR struct dm320_ep_s *)ep;
/* Retain what we need from the descriptor */
usbtrace(TRACE_EPCONFIGURE, privep->epphy);
DEBUGASSERT(desc->addr == ep->eplog);
return OK;
}
/*******************************************************************************
* Name: dm320_epdisable
*
* Description:
* The endpoint will no longer be used
*
*******************************************************************************/
static int dm320_epdisable(FAR struct usbdev_ep_s *ep)
{
FAR struct dm320_ep_s *privep = (FAR struct dm320_ep_s *)ep;
irqstate_t flags;
#ifdef CONFIG_DEBUG
if (!ep)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
usbtrace(TRACE_EPDISABLE, privep->epphy);
/* Cancel any ongoing activity and reset the endpoint */
flags = irqsave();
dm320_cancelrequests(privep);
dm320_epreset(privep->epphy);
irqrestore(flags);
return OK;
}
/*******************************************************************************
* Name: dm320_epallocreq
*
* Description:
* Allocate an I/O request
*
*******************************************************************************/
static FAR struct usbdev_req_s *dm320_epallocreq(FAR struct usbdev_ep_s *ep)
{
FAR struct dm320_req_s *privreq;
#ifdef CONFIG_DEBUG
if (!ep)
{
return NULL;
}
#endif
usbtrace(TRACE_EPALLOCREQ, ((FAR struct dm320_ep_s *)ep)->epphy);
privreq = (FAR struct dm320_req_s *)kmalloc(sizeof(struct dm320_req_s));
if (!privreq)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_ALLOCFAIL), 0);
return NULL;
}
memset(privreq, 0, sizeof(struct dm320_req_s));
return &privreq->req;
}
/*******************************************************************************
* Name: dm320_epfreereq
*
* Description:
* Free an I/O request
*
*******************************************************************************/
static void dm320_epfreereq(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
{
FAR struct dm320_req_s *privreq = (FAR struct dm320_req_s *)req;
#ifdef CONFIG_DEBUG
if (!ep || !req)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_INVALIDPARMS), 0);
return;
}
#endif
usbtrace(TRACE_EPFREEREQ, ((FAR struct dm320_ep_s *)ep)->epphy);
kfree(privreq);
}
/*******************************************************************************
* Name: dm320_epallocbuffer
*
* Description:
* Allocate an I/O buffer
*
*******************************************************************************/
#ifdef CONFIG_USBDEV_DMA
static void *dm320_epallocbuffer(FAR struct usbdev_ep_s *ep, unsigned bytes)
{
usbtrace(TRACE_EPALLOCBUFFER, privep->epphy);
#ifdef CONFIG_USBDEV_DMAMEMORY
return usbdev_dma_alloc(bytes);
#else
return kmalloc(bytes);
#endif
}
#endif
/*******************************************************************************
* Name: dm320_epfreebuffer
*
* Description:
* Free an I/O buffer
*
*******************************************************************************/
#ifdef CONFIG_USBDEV_DMA
static void dm320_epfreebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf)
{
usbtrace(TRACE_EPFREEBUFFER, privep->epphy);
#ifdef CONFIG_USBDEV_DMAMEMORY
usbdev_dma_free(buf);
#else
kfree(buf);
#endif
}
#endif
/*******************************************************************************
* Name: dm320_epsubmit
*
* Description:
* Submit an I/O request to the endpoint
*
*******************************************************************************/
static int dm320_epsubmit(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
{
FAR struct dm320_req_s *privreq = (FAR struct dm320_req_s *)req;
FAR struct dm320_ep_s *privep = (FAR struct dm320_ep_s *)ep;
FAR struct dm320_usbdev_s *priv;
irqstate_t flags;
int ret = OK;
#ifdef CONFIG_DEBUG
if (!req || !req->callback || !req->buf || !ep)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
usbtrace(TRACE_EPSUBMIT, privep->epphy);
priv = privep->dev;
if (!priv->driver || priv->usbdev.speed == USB_SPEED_UNKNOWN)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_NOTCONFIGURED), 0);
return -ESHUTDOWN;
}
req->result = -EINPROGRESS;
req->xfrd = 0;
flags = irqsave();
/* Check for NULL packet */
if (req->len == 0 && (privep->in || privep->epphy == 3))
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_NULLPACKET), 0);
dm320_putreg8(dm320_getreg8(DM320_USB_PERTXCSR1) | USB_TXCSR1_TXPKTRDY, DM320_USB_PERTXCSR1);
dm320_abortrequest(privep, privreq, OK);
}
/* If we are stalled, then drop all requests on the floor */
else if (privep->stalled)
{
dm320_abortrequest(privep, privreq, -EBUSY);
ret = -EBUSY;
}
/* Handle zero-length transfers on EP0 */
else if (privep->epphy == 0 && req->len == 0)
{
/* Nothing to transfer -- exit success, with zero bytes transferred */
usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd);
dm320_abortrequest(privep, privreq, OK);
}
/* Handle IN (device-to-host) requests */
else if ((privep->in) || privep->epphy == 3)
{
/* Add the new request to the request queue for the IN endpoint */
dm320_rqenqueue(privep, privreq);
usbtrace(TRACE_INREQQUEUED(privep->epphy), privreq->req.len);
ret = dm320_wrrequest(privep);
}
/* Handle OUT (host-to-device) requests */
else
{
/* Add the new request to the request queue for the OUT endpoint */
privep->txnullpkt = 0;
dm320_rqenqueue(privep, privreq);
usbtrace(TRACE_OUTREQQUEUED(privep->epphy), privreq->req.len);
/* This there a incoming data pending the availability of a request? */
if (priv->rxpending)
{
ret = dm320_rdrequest(privep);
priv->rxpending = 0;
}
}
irqrestore(flags);
return ret;
}
/*******************************************************************************
* Name: dm320_epcancel
*
* Description:
* Cancel an I/O request previously sent to an endpoint
*
*******************************************************************************/
static int dm320_epcancel(struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
{
FAR struct dm320_ep_s *privep = (FAR struct dm320_ep_s *)ep;
FAR struct dm320_usbdev_s *priv;
irqstate_t flags;
#ifdef CONFIG_DEBUG
if (!ep || !req)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
usbtrace(TRACE_EPCANCEL, privep->epphy);
priv = privep->dev;
flags = irqsave();
dm320_cancelrequests(privep);
irqrestore(flags);
return OK;
}
/*******************************************************************************
* Device Methods
*******************************************************************************/
/*******************************************************************************
* Name: dm320_allocep
*
* Description:
* Allocate an endpoint matching the parameters
*
* Input Parameters:
* eplog - 7-bit logical endpoint number (direction bit ignored). Zero means
* that any endpoint matching the other requirements will suffice. The
* assigned endpoint can be found in the eplog field.
* in - true: IN (device-to-host) endpoint requested
* eptype - Endpoint type. One of {USB_EP_ATTR_XFER_ISOC, USB_EP_ATTR_XFER_BULK,
* USB_EP_ATTR_XFER_INT}
*
*******************************************************************************/
static FAR struct usbdev_ep_s *dm320_allocep(FAR struct usbdev_s *dev, uint8_t eplog,
bool in, uint8_t eptype)
{
FAR struct dm320_usbdev_s *priv = (FAR struct dm320_usbdev_s *)dev;
int ndx;
usbtrace(TRACE_DEVALLOCEP, 0);
/* Ignore any direction bits in the logical address */
eplog = USB_EPNO(eplog);
/* Check all endpoints (except EP0) */
for (ndx = 1; ndx < DM320_NENDPOINTS; ndx++)
{
/* Does this match the endpoint number (if one was provided?) */
if (eplog != 0 && eplog != USB_EPNO(priv->eplist[ndx].ep.eplog))
{
continue;
}
/* Does the direction match */
if (in)
{
if (!USB_EPIN(g_epinfo[ndx].addr))
{
continue;
}
}
else
{
if (!USB_EPOUT(g_epinfo[ndx].addr))
{
continue;
}
}
/* Does the type match? */
if (g_epinfo[ndx].attr == eptype)
{
/* Success! */
return &priv->eplist[ndx].ep;
}
}
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_NOEP), 0);
return NULL;
}
/*******************************************************************************
* Name: dm320_freeep
*
* Description:
* Free the previously allocated endpoint
*
*******************************************************************************/
static void dm320_freeep(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep)
{
FAR struct dm320_ep_s *privep = (FAR struct dm320_ep_s *)ep;
usbtrace(TRACE_DEVFREEEP, (uint16_t)privep->epphy);
/* Nothing needs to be done */
}
/*******************************************************************************
* Name: dm320_getframe
*
* Description:
* Returns the current frame number
*
*******************************************************************************/
static int dm320_getframe(struct usbdev_s *dev)
{
irqstate_t flags;
int ret;
usbtrace(TRACE_DEVGETFRAME, 0);
#ifdef CONFIG_DEBUG
if (!dev)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_INVALIDPARMS), 0);
return -ENODEV;
}
#endif
/* Return the contents of the frame register. Interrupts must be disabled
* because the operation is not atomic.
*/
flags = irqsave();
ret = dm320_getreg8(DM320_USB_FRAME2) << 8;
ret |= dm320_getreg8(DM320_USB_FRAME1);
irqrestore(flags);
return ret;
}
/*******************************************************************************
* Name: dm320_wakeup
*
* Description:
* Tries to wake up the host connected to this device
*
*******************************************************************************/
static int dm320_wakeup(struct usbdev_s *dev)
{
irqstate_t flags;
usbtrace(TRACE_DEVWAKEUP, 0);
flags = irqsave();
dm320_putreg8(USB_POWER_RESUME, DM320_USB_POWER);
irqrestore(flags);
return OK;
}
/*******************************************************************************
* Name: dm320_selfpowered
*
* Description:
* Sets/clears the device selfpowered feature
*
*******************************************************************************/
static int dm320_selfpowered(struct usbdev_s *dev, bool selfpowered)
{
struct dm320_usbdev_s *priv = &g_usbdev;
usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered);
#ifdef CONFIG_DEBUG
if (!dev)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_INVALIDPARMS), 0);
return -ENODEV;
}
#endif
priv->selfpowered = selfpowered;
return OK;
}
/*******************************************************************************
* Name: dm320_pullup
*
* Description:
* Software-controlled connect to/disconnect from USB host
*
*******************************************************************************/
#ifdef CONFIG_DM320_GIO_USBDPPULLUP
static int dm320_pullup(struct usbdev_s *dev, bool enable)
{
irqstate_t flags;
usbtrace(TRACE_DEVPULLUP, (uint16_t)enable);
flags = irqsave();
if (enable)
{
GIO_SET_OUTPUT(CONFIG_DM320_GIO_USBDPPULLUP); /* Set D+ pullup */
}
else
{
GIO_CLEAR_OUTPUT(CONFIG_DM320_GIO_USBDPPULLUP); /* Clear D+ pullup */
}
irqrestore(flags);
return OK;
}
#endif
/*******************************************************************************
* Public Functions
*******************************************************************************/
/*******************************************************************************
* Name: up_usbinitialize
*
* Description:
* Initialize USB hardware
*
*******************************************************************************/
void up_usbinitialize(void)
{
struct dm320_usbdev_s *priv = &g_usbdev;
struct dm320_ep_s *privep;
#ifdef CONFIG_DEBUG_USB
uint16_t chiprev;
#endif
int i;
usbtrace(TRACE_DEVINIT, 0);
/* Initialize the device state structure */
memset(priv, 0, sizeof(struct dm320_usbdev_s));
priv->usbdev.ops = &g_devops;
#ifdef CONFIG_DEBUG_USB
chiprev = dm320_getreg16(DM320_BUSC_REVR);
ulldbg("DM320 revision : %d.%d\n", chiprev >> 4, chiprev & 0x0f);
#endif
/* Enable USB clock & GIO clock */
dm320_putreg16(dm320_getreg16(DM320_CLKC_MOD2) | 0x0060, DM320_CLKC_MOD2);
dm320_putreg16(dm320_getreg16(DM320_CLKC_DIV4) | (((4) - 1) << 8) | ((1) - 1), DM320_CLKC_DIV4);
/* Initialize D+ pullup control GIO */
GIO_OUTPUT(CONFIG_DM320_GIO_USBDPPULLUP);
GIO_SET_OUTPUT(CONFIG_DM320_GIO_USBDPPULLUP);
/* Initilialize USB attach GIO */
GIO_INTERRUPT(CONFIG_DM320_GIO_USBATTACH);
GIO_BOTHEDGES(CONFIG_DM320_GIO_USBATTACH);
dm320_putreg16(dm320_getreg16(DM320_GIO_CHAT0) | (1 << CONFIG_DM320_GIO_USBATTACH), DM320_GIO_CHAT0);
/* Attach host attach GIO interrupt */
if (irq_attach(IRQ_USBATTACH, dm320_attachinterrupt) != 0)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_ATTACHIRQREG), 0);
goto errout;
}
/* Attach USB controller core interrupt handler -- interrupts will be
* enabled when the driver is bound
*/
if (irq_attach(DM320_IRQ_USB1, dm320_ctlrinterrupt) != 0)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_COREIRQREG), 0);
goto errout;
}
/* Initialize the DM320 USB controller for peripheral mode operation. */
dm320_ctrlinitialize(priv);
/* Perform endpoint initialization */
for (i = 0; i < DM320_NENDPOINTS; i++)
{
/* Set up the standard stuff */
privep = &priv->eplist[i];
memset(privep, 0, sizeof(struct dm320_ep_s));
privep->ep.ops = &g_epops;
privep->dev = priv;
/* The index, i, is the physical endpoint address; Map this
* to a logical endpoint address usable by the class driver.
*/
privep->epphy = i;
privep->ep.eplog = g_epinfo[i].addr;
/* Setup the endpoint-specific stuff */
priv->eplist[i].ep.maxpacket = g_epinfo[i].maxpacket;
if (USB_EPIN(g_epinfo[i].addr))
{
priv->eplist[i].in = 1;
}
/* Reset the endpoint */
dm320_epreset(privep->epphy);
}
/* Expose only the standard EP0 */
priv->usbdev.ep0 = &priv->eplist[0].ep;
/* For 'B' device initiate session request protocol */
dm320_putreg8(USB_DEVCTL_SESSREQ, DM320_USB_DEVCTL);
return;
errout:
up_usbuninitialize();
}
/*******************************************************************************
* Name: up_usbuninitialize
*******************************************************************************/
void up_usbuninitialize(void)
{
struct dm320_usbdev_s *priv = &g_usbdev;
usbtrace(TRACE_DEVUNINIT, 0);
if (priv->driver)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_DRIVERREGISTERED), 0);
usbdev_unregister(priv->driver);
}
priv->usbdev.speed = USB_SPEED_UNKNOWN;
dm320_putreg16(dm320_getreg16(DM320_CLKC_LPCTL1) | 0x0010, DM320_CLKC_LPCTL1);
/* Disable and detach IRQs */
up_disable_irq(IRQ_USBATTACH);
up_disable_irq(DM320_IRQ_USB1);
irq_detach(IRQ_USBATTACH);
irq_detach(DM320_IRQ_USB1);
}
/************************************************************************************
* Name: usbdevclass_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(FAR struct usbdevclass_driver_s *driver)
{
int ret;
usbtrace(TRACE_DEVREGISTER, 0);
#ifdef CONFIG_DEBUG
if (!driver || (driver->speed != USB_SPEED_FULL) || !driver->ops->bind ||
!driver->ops->unbind || !driver->ops->setup)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
if (g_usbdev.driver)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_DRIVER), 0);
return -EBUSY;
}
#endif
/* Hook up the driver */
g_usbdev.driver = driver;
/* Then bind the class driver */
ret = CLASS_BIND(driver, &g_usbdev.usbdev);
if (ret)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_BINDFAILED), (uint16_t)-ret);
g_usbdev.driver = NULL;
return ret;
}
/* Enable host detection and ep0 RX/TX */
dm320_epreset(0);
dm320_putreg8(USB_EP0, DM320_USB_INTRTX1E);
dm320_putreg8(USB_INT_RESET|USB_INT_RESUME|USB_INT_SUSPEND|USB_INT_SESSRQ|USB_INT_SOF,
DM320_USB_INTRUSBE);
/* Enable interrupts */
up_enable_irq(IRQ_USBATTACH);
up_enable_irq(DM320_IRQ_USB1);
return OK;
}
/************************************************************************************
* 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(FAR struct usbdevclass_driver_s *driver)
{
usbtrace(TRACE_DEVUNREGISTER, 0);
#ifdef CONFIG_DEBUG
if (driver != g_usbdev.driver)
{
usbtrace(TRACE_DEVERROR(DM320_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
/* Unbind the class driver */
CLASS_UNBIND(driver, &g_usbdev.usbdev);
/* Disable IRQs */
up_disable_irq(IRQ_USBATTACH);
up_disable_irq(DM320_IRQ_USB1);
/* Unhook the driver */
g_usbdev.driver = NULL;
return OK;
}