* arch/arm/src/lpc43xx/lpc43_usb0dev.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <nuttx/spinlock.h>
#include <nuttx/usb/usb.h>
#include <nuttx/usb/usbdev.h>
#include <nuttx/usb/usbdev_trace.h>
#include <arch/board/board.h>
#include "chip.h"
#include "arm_internal.h"
#include "lpc43_usb0dev.h"
#include "lpc43_creg.h"
#include "lpc43_ccu.h"
#include "lpc43_cgu.h"
#include "lpc43_rgu.h"
* Pre-processor Definitions
****************************************************************************/
#ifndef CONFIG_USBDEV_EP0_MAXSIZE
# define CONFIG_USBDEV_EP0_MAXSIZE 64
#endif
#ifndef CONFIG_USBDEV_MAXPOWER
# define CONFIG_USBDEV_MAXPOWER 100
#endif
* enabled.
*/
#undef CONFIG_LPC43_USBDEV_REGDEBUG
* Probably a bad idea...
* Unless there is some issue with sampling the SOF from hardware
* asynchronously.
*/
#ifdef CONFIG_LPC43_USBDEV_FRAME_INTERRUPT
# define USB_FRAME_INT USBDEV_USBINTR_SRE
#else
# define USB_FRAME_INT 0
#endif
#ifdef CONFIG_DEBUG_FEATURES
# define USB_ERROR_INT USBDEV_USBINTR_UEE
#else
# define USB_ERROR_INT 0
#endif
#define LPC43_TRACEERR_ALLOCFAIL 0x0001
#define LPC43_TRACEERR_BADCLEARFEATURE 0x0002
#define LPC43_TRACEERR_BADDEVGETSTATUS 0x0003
#define LPC43_TRACEERR_BADEPNO 0x0004
#define LPC43_TRACEERR_BADEPGETSTATUS 0x0005
#define LPC43_TRACEERR_BADEPTYPE 0x0006
#define LPC43_TRACEERR_BADGETCONFIG 0x0007
#define LPC43_TRACEERR_BADGETSETDESC 0x0008
#define LPC43_TRACEERR_BADGETSTATUS 0x0009
#define LPC43_TRACEERR_BADSETADDRESS 0x000a
#define LPC43_TRACEERR_BADSETCONFIG 0x000b
#define LPC43_TRACEERR_BADSETFEATURE 0x000c
#define LPC43_TRACEERR_BINDFAILED 0x000d
#define LPC43_TRACEERR_DISPATCHSTALL 0x000e
#define LPC43_TRACEERR_DRIVER 0x000f
#define LPC43_TRACEERR_DRIVERREGISTERED 0x0010
#define LPC43_TRACEERR_EP0SETUPSTALLED 0x0011
#define LPC43_TRACEERR_EPINNULLPACKET 0x0012
#define LPC43_TRACEERR_EPOUTNULLPACKET 0x0013
#define LPC43_TRACEERR_INVALIDCTRLREQ 0x0014
#define LPC43_TRACEERR_INVALIDPARMS 0x0015
#define LPC43_TRACEERR_IRQREGISTRATION 0x0016
#define LPC43_TRACEERR_NOEP 0x0017
#define LPC43_TRACEERR_NOTCONFIGURED 0x0018
#define LPC43_TRACEERR_REQABORTED 0x0019
#define LPC43_TRACEINTID_USB 0x0001
#define LPC43_TRACEINTID_CLEARFEATURE 0x0002
#define LPC43_TRACEINTID_DEVGETSTATUS 0x0003
#define LPC43_TRACEINTID_DEVRESET 0x0004
#define LPC43_TRACEINTID_DISPATCH 0x0005
#define LPC43_TRACEINTID_EP0COMPLETE 0x0006
#define LPC43_TRACEINTID_EP0NAK 0x0007
#define LPC43_TRACEINTID_EP0SETUP 0x0008
#define LPC43_TRACEINTID_EPGETSTATUS 0x0009
#define LPC43_TRACEINTID_EPIN 0x000a
#define LPC43_TRACEINTID_EPINQEMPTY 0x000b
#define LPC43_TRACEINTID_EP0INSETADDRESS 0x000c
#define LPC43_TRACEINTID_EPOUT 0x000d
#define LPC43_TRACEINTID_EPOUTQEMPTY 0x000e
#define LPC43_TRACEINTID_EP0SETUPSETADDRESS 0x000f
#define LPC43_TRACEINTID_FRAME 0x0010
#define LPC43_TRACEINTID_GETCONFIG 0x0011
#define LPC43_TRACEINTID_GETSETDESC 0x0012
#define LPC43_TRACEINTID_GETSETIF 0x0013
#define LPC43_TRACEINTID_GETSTATUS 0x0014
#define LPC43_TRACEINTID_IFGETSTATUS 0x0015
#define LPC43_TRACEINTID_SETCONFIG 0x0016
#define LPC43_TRACEINTID_SETFEATURE 0x0017
#define LPC43_TRACEINTID_SUSPENDED 0x0018
#define LPC43_TRACEINTID_RESUMED 0x0019
#define LPC43_TRACEINTID_SYNCHFRAME 0x001a
* byte aligned.
*/
struct lpc43_dtd_s
{
volatile uint32_t nextdesc;
volatile uint32_t config;
uint32_t buffer0;
uint32_t buffer1;
uint32_t buffer2;
uint32_t buffer3;
uint32_t buffer4;
uint32_t xfer_len;
};
#define DTD_NEXTDESC_INVALID (1 << 0)
#define DTD_CONFIG_LENGTH(n) ((n) << 16)
#define DTD_CONFIG_IOC (1 << 15)
#define DTD_CONFIG_MULT_VARIABLE (0 << 10)
#define DTD_CONFIG_MULT_NUM(n) ((n) << 10)
#define DTD_CONFIG_ACTIVE (1 << 7)
#define DTD_CONFIG_HALTED (1 << 6)
#define DTD_CONFIG_BUFFER_ERROR (1 << 5)
#define DTD_CONFIG_TRANSACTION_ERROR (1 << 3)
* byte boundary
*/
struct lpc43_dqh_s
{
uint32_t capability;
uint32_t currdesc;
struct lpc43_dtd_s overlay;
volatile uint32_t setup[2];
uint32_t gap[4];
};
#define DQH_CAPABILITY_MULT_VARIABLE (0 << 30)
#define DQH_CAPABILITY_MULT_NUM(n) ((n) << 30)
#define DQH_CAPABILITY_ZLT (1 << 29)
#define DQH_CAPABILITY_MAX_PACKET(n) ((n) << 16)
#define DQH_CAPABILITY_IOS (1 << 15)
#define LPC43_NLOGENDPOINTS (6)
#define LPC43_NPHYSENDPOINTS (12)
#define LPC43_EPPHYIN(epphy) (((epphy) & 1) != 0)
#define LPC43_EPPHYOUT(epphy) (((epphy) & 1) == 0)
#define LPC43_EPPHYIN2LOG(epphy) (((uint8_t)(epphy) >> 1) |USB_DIR_IN)
#define LPC43_EPPHYOUT2LOG(epphy) (((uint8_t)(epphy) >> 1) | USB_DIR_OUT)
#define LPC43_EP0_OUT (0)
#define LPC43_EP0_IN (1)
#define LPC43_EPALLSET (0xfff)
#define LPC43_EPOUTSET (0x555)
#define LPC43_EPINSET (0xaaa)
#define LPC43_EPCTRLSET (0x003)
#define LPC43_EPINTRSET (0xffc)
#define LPC43_EPBULKSET (0xffc)
#define LPC43_EPISOCSET (0xffc)
#define LPC43_EP0MAXPACKET (64)
#define LPC43_BULKMAXPACKET (512)
#define LPC43_INTRMAXPACKET (1024)
#define LPC43_ISOCMAXPACKET (512)
* COMPLETE registers
*/
#define LPC43_ENDPTSHIFT(epphy) (LPC43_EPPHYIN(epphy) ? (16 + ((epphy) >> 1)) : ((epphy) >> 1))
#define LPC43_ENDPTMASK(epphy) (1 << LPC43_ENDPTSHIFT(epphy))
#define LPC43_ENDPTMASK_ALL 0x003f003f
#define lpc43_rqempty(ep) ((ep)->head == NULL)
#define lpc43_rqpeek(ep) ((ep)->head)
* Private Types
****************************************************************************/
struct lpc43_req_s
{
struct usbdev_req_s req;
struct lpc43_req_s *flink;
};
struct lpc43_ep_s
{
* structure so that it is possible to simply cast from struct usbdev_ep_s
* to struct lpc43_ep_s.
*/
struct usbdev_ep_s ep;
struct lpc43_usbdev_s *dev;
struct lpc43_req_s *head;
struct lpc43_req_s *tail;
uint8_t epphy;
uint8_t stalled:1;
};
struct lpc43_usbdev_s
{
* structure so that it is possible to simply cast from struct usbdev_s
* to struct lpc43_usbdev_s.
*/
struct usbdev_s usbdev;
struct usbdevclass_driver_s *driver;
uint8_t ep0state;
uint8_t ep0buf[64];
uint8_t paddr;
uint8_t stalled:1;
uint8_t selfpowered:1;
uint8_t paddrset:1;
uint8_t attached:1;
uint8_t suspended:1;
uint32_t softprio;
uint32_t epavail;
#ifdef CONFIG_LPC43_USBDEV_FRAME_INTERRUPT
uint32_t sof;
#endif
uint16_t ep0buf_len;
struct usb_ctrlreq_s ep0ctrl;
struct lpc43_ep_s eplist[LPC43_NPHYSENDPOINTS];
};
#define EP0STATE_IDLE 0
#define EP0STATE_SETUP_OUT 1
#define EP0STATE_SETUP_IN 2
#define EP0STATE_SHORTREAD 3
#define EP0STATE_SHORTWRITE 4
#define EP0STATE_WAIT_NAK_OUT 5
#define EP0STATE_WAIT_NAK_IN 6
#define EP0STATE_WAIT_STATUS_OUT 7
#define EP0STATE_WAIT_STATUS_IN 8
#define EP0STATE_DATA_IN 9
#define EP0STATE_DATA_OUT 10
* Private Function Prototypes
****************************************************************************/
#ifdef CONFIG_LPC43_USBDEV_REGDEBUG
static uint32_t lpc43_getreg(uint32_t addr);
static void lpc43_putreg(uint32_t val, uint32_t addr);
#else
# define lpc43_getreg(addr) getreg32(addr)
# define lpc43_putreg(val,addr) putreg32(val,addr)
#endif
static inline void lpc43_clrbits(uint32_t mask, uint32_t addr);
static inline void lpc43_setbits(uint32_t mask, uint32_t addr);
static inline void lpc43_chgbits(uint32_t mask,
uint32_t val, uint32_t addr);
static struct
lpc43_req_s *lpc43_rqdequeue(struct lpc43_ep_s *privep);
static bool lpc43_rqenqueue(struct lpc43_ep_s *privep,
struct lpc43_req_s *req);
static inline void lpc43_writedtd(struct lpc43_dtd_s *dtd,
const uint8_t *data,
uint32_t nbytes);
static inline void lpc43_queuedtd(uint8_t epphy, struct lpc43_dtd_s *dtd);
static inline void lpc43_ep0xfer(uint8_t epphy, uint8_t *data,
uint32_t nbytes);
static void lpc43_readsetup(uint8_t epphy,
struct usb_ctrlreq_s *ctrl);
static inline void lpc43_set_address(struct lpc43_usbdev_s *priv,
uint16_t address);
static void lpc43_flushep(struct lpc43_ep_s *privep);
static int lpc43_progressep(struct lpc43_ep_s *privep);
static void lpc43_reqcomplete(struct lpc43_ep_s *privep,
struct lpc43_req_s *privreq, int16_t result);
static void lpc43_cancelrequests(struct lpc43_ep_s *privep,
int16_t status);
static struct lpc43_ep_s *lpc43_epfindbyaddr(struct lpc43_usbdev_s *priv,
uint16_t eplog);
static void lpc43_dispatchrequest(struct lpc43_usbdev_s *priv,
const struct usb_ctrlreq_s *ctrl);
static void lpc43_ep0configure(struct lpc43_usbdev_s *priv);
static void lpc43_usbreset(struct lpc43_usbdev_s *priv);
static inline void lpc43_ep0state(struct lpc43_usbdev_s *priv,
uint16_t state);
static void lpc43_ep0setup(struct lpc43_usbdev_s *priv);
static void lpc43_ep0complete(struct lpc43_usbdev_s *priv,
uint8_t epphy);
static void lpc43_ep0nak(struct lpc43_usbdev_s *priv, uint8_t epphy);
static bool lpc43_epcomplete(struct lpc43_usbdev_s *priv,
uint8_t epphy);
static int lpc43_usbinterrupt(int irq,
void *context, void *arg);
static int lpc43_epconfigure(struct usbdev_ep_s *ep,
const struct usb_epdesc_s *desc, bool last);
static int lpc43_epdisable(struct usbdev_ep_s *ep);
static struct usbdev_req_s *lpc43_epallocreq(struct usbdev_ep_s *ep);
static void lpc43_epfreereq(struct usbdev_ep_s *ep,
struct usbdev_req_s *);
#ifdef CONFIG_USBDEV_DMA
static void *lpc43_epallocbuffer(struct usbdev_ep_s *ep,
unsigned bytes);
static void lpc43_epfreebuffer(struct usbdev_ep_s *ep,
void *buf);
#endif
static int lpc43_epsubmit(struct usbdev_ep_s *ep,
struct usbdev_req_s *req);
static int lpc43_epcancel(struct usbdev_ep_s *ep,
struct usbdev_req_s *req);
static int lpc43_epstall(struct usbdev_ep_s *ep, bool resume);
static struct usbdev_ep_s *lpc43_allocep(struct usbdev_s *dev,
uint8_t epno, bool in, uint8_t eptype);
static void lpc43_freeep(struct usbdev_s *dev,
struct usbdev_ep_s *ep);
static int lpc43_getframe(struct usbdev_s *dev);
static int lpc43_wakeup(struct usbdev_s *dev);
static int lpc43_selfpowered(struct usbdev_s *dev,
bool selfpowered);
static int lpc43_pullup(struct usbdev_s *dev, bool enable);
* Private Data
****************************************************************************/
* be simply retained in a single global instance.
*/
static struct lpc43_usbdev_s g_usbdev;
static struct
lpc43_dqh_s aligned_data(2048) g_qh[LPC43_NPHYSENDPOINTS];
static struct
lpc43_dtd_s aligned_data(32) g_td[LPC43_NPHYSENDPOINTS];
static const struct usbdev_epops_s g_epops =
{
.configure = lpc43_epconfigure,
.disable = lpc43_epdisable,
.allocreq = lpc43_epallocreq,
.freereq = lpc43_epfreereq,
#ifdef CONFIG_USBDEV_DMA
.allocbuffer = lpc43_epallocbuffer,
.freebuffer = lpc43_epfreebuffer,
#endif
.submit = lpc43_epsubmit,
.cancel = lpc43_epcancel,
.stall = lpc43_epstall,
};
static const struct usbdev_ops_s g_devops =
{
.allocep = lpc43_allocep,
.freeep = lpc43_freeep,
.getframe = lpc43_getframe,
.wakeup = lpc43_wakeup,
.selfpowered = lpc43_selfpowered,
.pullup = lpc43_pullup,
};
* Public Data
****************************************************************************/
* Private Functions
****************************************************************************/
* Name: lpc43_getreg
*
* Description:
* Get the contents of an LPC433x register
*
****************************************************************************/
#ifdef CONFIG_LPC43_USBDEV_REGDEBUG
static uint32_t lpc43_getreg(uint32_t addr)
{
static uint32_t prevaddr = 0;
static uint32_t preval = 0;
static uint32_t count = 0;
uint32_t val = getreg32(addr);
* 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)
{
usbinfo("...\n");
}
return val;
}
}
else
{
if (count > 3)
{
usbinfo("[repeats %d more times]\n", count - 3);
}
prevaddr = addr;
preval = val;
count = 1;
}
usbinfo("%08x->%08x\n", addr, val);
return val;
}
#endif
* Name: lpc43_putreg
*
* Description:
* Set the contents of an LPC433x register to a value
*
****************************************************************************/
#ifdef CONFIG_LPC43_USBDEV_REGDEBUG
static void lpc43_putreg(uint32_t val, uint32_t addr)
{
usbinfo("%08x<-%08x\n", addr, val);
putreg32(val, addr);
}
#endif
* Name: lpc43_clrbits
*
* Description:
* Clear bits in a register
*
****************************************************************************/
static inline void lpc43_clrbits(uint32_t mask, uint32_t addr)
{
uint32_t reg = lpc43_getreg(addr);
reg &= ~mask;
lpc43_putreg(reg, addr);
}
* Name: lpc43_setbits
*
* Description:
* Set bits in a register
*
****************************************************************************/
static inline void lpc43_setbits(uint32_t mask, uint32_t addr)
{
uint32_t reg = lpc43_getreg(addr);
reg |= mask;
lpc43_putreg(reg, addr);
}
* Name: lpc43_chgbits
*
* Description:
* Change bits in a register
*
****************************************************************************/
static inline void lpc43_chgbits(uint32_t mask, uint32_t val, uint32_t addr)
{
uint32_t reg = lpc43_getreg(addr);
reg &= ~mask;
reg |= val;
lpc43_putreg(reg, addr);
}
* Name: lpc43_rqdequeue
*
* Description:
* Remove a request from an endpoint request queue
*
****************************************************************************/
static struct lpc43_req_s *lpc43_rqdequeue(struct lpc43_ep_s *privep)
{
struct lpc43_req_s *ret = privep->head;
if (ret)
{
privep->head = ret->flink;
if (!privep->head)
{
privep->tail = NULL;
}
ret->flink = NULL;
}
return ret;
}
* Name: lpc43_rqenqueue
*
* Description:
* Add a request from an endpoint request queue
*
****************************************************************************/
static bool lpc43_rqenqueue(struct lpc43_ep_s *privep,
struct lpc43_req_s *req)
{
bool is_empty = !privep->head;
req->flink = NULL;
if (is_empty)
{
privep->head = req;
privep->tail = req;
}
else
{
privep->tail->flink = req;
privep->tail = req;
}
return is_empty;
}
* Name: lpc43_writedtd
*
* Description:
* Initialise a DTD to transfer the data
*
****************************************************************************/
static inline void lpc43_writedtd(struct lpc43_dtd_s *dtd,
const uint8_t *data, uint32_t nbytes)
{
dtd->nextdesc = DTD_NEXTDESC_INVALID;
dtd->config = DTD_CONFIG_LENGTH(nbytes) |
DTD_CONFIG_IOC | DTD_CONFIG_ACTIVE;
dtd->buffer0 = ((uint32_t) data);
dtd->buffer1 = (((uint32_t) data) + 0x1000) & 0xfffff000;
dtd->buffer2 = (((uint32_t) data) + 0x2000) & 0xfffff000;
dtd->buffer3 = (((uint32_t) data) + 0x3000) & 0xfffff000;
dtd->buffer4 = (((uint32_t) data) + 0x4000) & 0xfffff000;
dtd->xfer_len = nbytes;
}
* Name: lpc43_queuedtd
*
* Description:
* Add the DTD to the device list
*
****************************************************************************/
static void lpc43_queuedtd(uint8_t epphy, struct lpc43_dtd_s *dtd)
{
g_qh[epphy].overlay.nextdesc = (uint32_t) dtd;
g_qh[epphy].overlay.config &= ~(DTD_CONFIG_ACTIVE | DTD_CONFIG_HALTED);
uint32_t bit = LPC43_ENDPTMASK(epphy);
lpc43_setbits (bit, LPC43_USBDEV_ENDPTPRIME);
while (lpc43_getreg (LPC43_USBDEV_ENDPTPRIME) & bit)
;
}
* Name: lpc43_ep0xfer
*
* Description:
* Schedule a short transfer for Endpoint 0 (IN or OUT)
*
****************************************************************************/
static inline void lpc43_ep0xfer(uint8_t epphy,
uint8_t *buf, uint32_t nbytes)
{
struct lpc43_dtd_s *dtd = &g_td[epphy];
lpc43_writedtd(dtd, buf, nbytes);
lpc43_queuedtd(epphy, dtd);
}
* Name: lpc43_readsetup
*
* Description:
* Read a Setup packet from the DTD.
*
****************************************************************************/
static void lpc43_readsetup(uint8_t epphy, struct usb_ctrlreq_s *ctrl)
{
struct lpc43_dqh_s *dqh = &g_qh[epphy];
int i;
do
{
lpc43_setbits(USBDEV_USBCMD_SUTW, LPC43_USBDEV_USBCMD);
for (i = 0; i < 8; i++)
{
((uint8_t *) ctrl)[i] = ((uint8_t *) dqh->setup)[i];
}
}
while (!(lpc43_getreg(LPC43_USBDEV_USBCMD) & USBDEV_USBCMD_SUTW));
lpc43_clrbits(USBDEV_USBCMD_SUTW, LPC43_USBDEV_USBCMD);
lpc43_putreg (LPC43_ENDPTMASK(LPC43_EP0_OUT), LPC43_USBDEV_ENDPTSETUPSTAT);
}
* Name: lpc43_set_address
*
* Description:
* Set the devices USB address
*
****************************************************************************/
static inline void lpc43_set_address(struct lpc43_usbdev_s *priv,
uint16_t address)
{
priv->paddr = address;
priv->paddrset = address != 0;
lpc43_chgbits(USBDEV_DEVICEADDR_MASK,
priv->paddr << USBDEV_DEVICEADDR_SHIFT,
LPC43_USBDEV_DEVICEADDR);
}
* Name: lpc43_flushep
*
* Description:
* Flush any primed descriptors from this ep
*
****************************************************************************/
static void lpc43_flushep(struct lpc43_ep_s *privep)
{
uint32_t mask = LPC43_ENDPTMASK(privep->epphy);
do
{
lpc43_putreg (mask, LPC43_USBDEV_ENDPTFLUSH);
while ((lpc43_getreg(LPC43_USBDEV_ENDPTFLUSH) & mask) != 0)
;
}
while ((lpc43_getreg(LPC43_USBDEV_ENDPTSTATUS) & mask) != 0);
}
* Name: lpc43_progressep
*
* Description:
* Progress the Endpoint by priming the first request into the queue head
*
****************************************************************************/
static int lpc43_progressep(struct lpc43_ep_s *privep)
{
struct lpc43_dtd_s *dtd = &g_td[privep->epphy];
struct lpc43_req_s *privreq;
privreq = lpc43_rqpeek(privep);
if (!privreq)
{
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EPINQEMPTY), 0);
return OK;
}
if (privreq->req.len == 0)
{
* the host to illicit thr response
*/
if (privep->epphy == LPC43_EP0_IN &&
privep->dev->ep0state == EP0STATE_SETUP_OUT)
{
lpc43_ep0state (privep->dev, EP0STATE_WAIT_NAK_IN);
}
else
{
if (LPC43_EPPHYIN(privep->epphy))
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_EPINNULLPACKET), 0);
}
else
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_EPOUTNULLPACKET), 0);
}
}
lpc43_reqcomplete(privep, lpc43_rqdequeue(privep), OK);
return OK;
}
if (privep->epphy == LPC43_EP0_IN)
{
lpc43_ep0state (privep->dev, EP0STATE_DATA_IN);
}
else if (privep->epphy == LPC43_EP0_OUT)
{
lpc43_ep0state (privep->dev, EP0STATE_DATA_OUT);
}
int bytesleft = privreq->req.len - privreq->req.xfrd;
if (LPC43_EPPHYIN(privep->epphy))
{
usbtrace(TRACE_WRITE(privep->epphy), privreq->req.xfrd);
}
else
{
usbtrace(TRACE_READ(privep->epphy), privreq->req.xfrd);
}
lpc43_writedtd (dtd, privreq->req.buf + privreq->req.xfrd, bytesleft);
lpc43_queuedtd(privep->epphy, dtd);
return OK;
}
* Name: lpc43_reqcomplete
*
* Description:
* Handle termination of the request at the head of the endpoint request
* queue.
*
****************************************************************************/
static void lpc43_reqcomplete(struct lpc43_ep_s *privep,
struct lpc43_req_s *privreq, int16_t result)
{
* in the callback.
*/
bool stalled = privep->stalled;
if (privep->epphy == LPC43_EP0_IN)
privep->stalled = privep->dev->stalled;
privreq->req.result = result;
privreq->req.callback(&privep->ep, &privreq->req);
privep->stalled = stalled;
}
* Name: lpc43_cancelrequests
*
* Description:
* Cancel all pending requests for an endpoint
*
****************************************************************************/
static void lpc43_cancelrequests(struct lpc43_ep_s *privep, int16_t status)
{
if (!lpc43_rqempty(privep))
lpc43_flushep(privep);
while (!lpc43_rqempty(privep))
{
* FIXME: only report the error status if the transfer hasn't completed
*/
usbtrace(TRACE_COMPLETE(privep->epphy),
(lpc43_rqpeek(privep))->req.xfrd);
lpc43_reqcomplete(privep, lpc43_rqdequeue(privep), status);
}
}
* Name: lpc43_epfindbyaddr
*
* Description:
* Find the physical endpoint structure corresponding to a logic endpoint
* address
*
****************************************************************************/
static struct lpc43_ep_s *lpc43_epfindbyaddr(struct lpc43_usbdev_s *priv,
uint16_t eplog)
{
struct lpc43_ep_s *privep;
int i;
if (USB_EPNO(eplog) == 0)
{
return &priv->eplist[0];
}
for (i = 1; i < LPC43_NPHYSENDPOINTS; i++)
{
privep = &priv->eplist[i];
if (eplog == privep->ep.eplog)
{
return privep;
}
}
return NULL;
}
* Name: lpc43_dispatchrequest
*
* Description:
* Provide unhandled setup actions to the class driver. This is logically
* part of the USB interrupt handler.
*
****************************************************************************/
static void lpc43_dispatchrequest(struct lpc43_usbdev_s *priv,
const struct usb_ctrlreq_s *ctrl)
{
int ret = -EIO;
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_DISPATCH), 0);
if (priv->driver)
{
ret = CLASS_SETUP(priv->driver,
&priv->usbdev,
ctrl,
priv->ep0buf,
priv->ep0buf_len);
}
if (ret < 0)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_DISPATCHSTALL), 0);
priv->stalled = true;
}
}
* Name: lpc43_ep0configure
*
* Description:
* Reset Usb engine
*
****************************************************************************/
static void lpc43_ep0configure(struct lpc43_usbdev_s *priv)
{
g_qh[LPC43_EP0_OUT].capability = (DQH_CAPABILITY_MAX_PACKET(
CONFIG_USBDEV_EP0_MAXSIZE) |
DQH_CAPABILITY_IOS | DQH_CAPABILITY_ZLT);
g_qh[LPC43_EP0_IN].capability = (DQH_CAPABILITY_MAX_PACKET(
CONFIG_USBDEV_EP0_MAXSIZE) |
DQH_CAPABILITY_IOS | DQH_CAPABILITY_ZLT);
g_qh[LPC43_EP0_OUT].currdesc = DTD_NEXTDESC_INVALID;
g_qh[LPC43_EP0_IN].currdesc = DTD_NEXTDESC_INVALID;
lpc43_setbits (USBDEV_ENDPTCTRL0_RXE | USBDEV_ENDPTCTRL0_TXE,
LPC43_USBDEV_ENDPTCTRL0);
}
* Name: lpc43_usbreset
*
* Description:
* Reset Usb engine
*
****************************************************************************/
static void lpc43_usbreset(struct lpc43_usbdev_s *priv)
{
int epphy;
lpc43_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE,
LPC43_USBDEV_ENDPTCTRL1);
lpc43_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE,
LPC43_USBDEV_ENDPTCTRL2);
lpc43_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE,
LPC43_USBDEV_ENDPTCTRL3);
lpc43_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE,
LPC43_USBDEV_ENDPTCTRL4);
lpc43_clrbits (USBDEV_ENDPTCTRL_RXE | USBDEV_ENDPTCTRL_TXE,
LPC43_USBDEV_ENDPTCTRL5);
lpc43_putreg (lpc43_getreg(LPC43_USBDEV_ENDPTNAK),
LPC43_USBDEV_ENDPTNAK);
lpc43_putreg (lpc43_getreg(LPC43_USBDEV_ENDPTSETUPSTAT),
LPC43_USBDEV_ENDPTSETUPSTAT);
lpc43_putreg (lpc43_getreg(LPC43_USBDEV_ENDPTCOMPLETE),
LPC43_USBDEV_ENDPTCOMPLETE);
* flush all DTDs
*/
while (lpc43_getreg (LPC43_USBDEV_ENDPTPRIME) != 0);
lpc43_putreg (LPC43_ENDPTMASK_ALL, LPC43_USBDEV_ENDPTFLUSH);
while (lpc43_getreg (LPC43_USBDEV_ENDPTFLUSH))
;
for (epphy = 0; epphy < LPC43_NPHYSENDPOINTS; epphy++)
{
struct lpc43_ep_s *privep = &priv->eplist[epphy];
lpc43_cancelrequests (privep, -ESHUTDOWN);
privep->stalled = false;
}
* driver should then accept any new configurations.
*/
if (priv->driver)
{
CLASS_DISCONNECT(priv->driver, &priv->usbdev);
}
lpc43_chgbits(USBDEV_USBCMD_ITC_MASK,
USBDEV_USBCMD_ITCIMME, LPC43_USBDEV_USBCMD);
memset ((void *) g_qh, 0, sizeof (g_qh));
memset ((void *) g_td, 0, sizeof (g_td));
lpc43_set_address (priv, 0);
lpc43_putreg ((uint32_t)g_qh, LPC43_USBDEV_ENDPOINTLIST);
lpc43_ep0configure(priv);
lpc43_putreg(USB_FRAME_INT | USB_ERROR_INT |
USBDEV_USBINTR_NAKE | USBDEV_USBINTR_SLE |
USBDEV_USBINTR_URE | USBDEV_USBINTR_PCE | USBDEV_USBINTR_UE,
LPC43_USBDEV_USBINTR);
}
* Name: lpc43_setstate
*
* Description:
* Sets the EP0 state and manages the NAK interrupts
*
****************************************************************************/
static inline void lpc43_ep0state(struct lpc43_usbdev_s *priv,
uint16_t state)
{
priv->ep0state = state;
switch (state)
{
case EP0STATE_WAIT_NAK_IN:
lpc43_putreg (LPC43_ENDPTMASK(LPC43_EP0_IN), LPC43_USBDEV_ENDPTNAKEN);
break;
case EP0STATE_WAIT_NAK_OUT:
lpc43_putreg (LPC43_ENDPTMASK(LPC43_EP0_OUT), LPC43_USBDEV_ENDPTNAKEN);
break;
default:
lpc43_putreg(0, LPC43_USBDEV_ENDPTNAKEN);
break;
}
}
* Name: lpc43_ep0setup
*
* Description:
* USB Ctrl EP Setup Event. This is logically part of the USB interrupt
* handler. This event occurs when a setup packet is receive on EP0 OUT.
*
****************************************************************************/
static inline void lpc43_ep0setup(struct lpc43_usbdev_s *priv)
{
struct lpc43_ep_s *privep;
struct usb_ctrlreq_s *ctrl;
uint16_t value;
uint16_t index;
uint16_t len;
ctrl = &priv->ep0ctrl;
* because of the setup packet.
*/
lpc43_cancelrequests(&priv->eplist[LPC43_EP0_OUT], -EPROTO);
lpc43_cancelrequests(&priv->eplist[LPC43_EP0_IN], -EPROTO);
priv->eplist[LPC43_EP0_OUT].stalled = false;
priv->eplist[LPC43_EP0_IN].stalled = false;
priv->stalled = false;
lpc43_readsetup(LPC43_EP0_OUT, ctrl);
value = GETUINT16(ctrl->value);
index = GETUINT16(ctrl->index);
len = GETUINT16(ctrl->len);
priv->ep0buf_len = len;
uinfo("type=%02x req=%02x value=%04x index=%04x len=%04x\n",
ctrl->type, ctrl->req, value, index, len);
if (ctrl->type & USB_REQ_DIR_IN)
{
lpc43_ep0state (priv, EP0STATE_SETUP_IN);
}
else
{
lpc43_ep0state (priv, EP0STATE_SETUP_OUT);
if (len > 0)
{
lpc43_ep0state(priv, EP0STATE_SHORTREAD);
lpc43_ep0xfer(LPC43_EP0_OUT, priv->ep0buf, len);
return;
}
}
if ((ctrl->type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD)
{
lpc43_dispatchrequest(priv, ctrl);
}
else
{
* device controller driver; pass what is left to the class driver.
*/
switch (ctrl->req)
{
case USB_REQ_GETSTATUS:
{
* value: 0
* index: zero interface endpoint
* len: 2; data = status
*/
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_GETSTATUS), 0);
if (!priv->paddrset || len != 2 ||
(ctrl->type & USB_REQ_DIR_IN) == 0 || value != 0)
{
priv->stalled = true;
}
else
{
switch (ctrl->type & USB_REQ_RECIPIENT_MASK)
{
case USB_REQ_RECIPIENT_ENDPOINT:
{
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EPGETSTATUS),
0);
privep = lpc43_epfindbyaddr(priv, index);
if (!privep)
{
usbtrace(
TRACE_DEVERROR(LPC43_TRACEERR_BADEPGETSTATUS),
0);
priv->stalled = true;
}
else
{
if (privep->stalled)
{
priv->ep0buf[0] = 1;
}
else
{
priv->ep0buf[0] = 0;
}
priv->ep0buf[1] = 0;
lpc43_ep0xfer (LPC43_EP0_IN, priv->ep0buf, 2);
lpc43_ep0state (priv, EP0STATE_SHORTWRITE);
}
}
break;
case USB_REQ_RECIPIENT_DEVICE:
{
if (index == 0)
{
usbtrace(
TRACE_INTDECODE(LPC43_TRACEINTID_DEVGETSTATUS),
0);
priv->ep0buf[0] = (priv->selfpowered <<
USB_FEATURE_SELFPOWERED) |
(1 << USB_FEATURE_REMOTEWAKEUP);
priv->ep0buf[1] = 0;
lpc43_ep0xfer(LPC43_EP0_IN, priv->ep0buf, 2);
lpc43_ep0state (priv, EP0STATE_SHORTWRITE);
}
else
{
usbtrace(
TRACE_DEVERROR(LPC43_TRACEERR_BADDEVGETSTATUS),
0);
priv->stalled = true;
}
}
break;
case USB_REQ_RECIPIENT_INTERFACE:
{
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_IFGETSTATUS),
0);
priv->ep0buf[0] = 0;
priv->ep0buf[1] = 0;
lpc43_ep0xfer(LPC43_EP0_IN, priv->ep0buf, 2);
lpc43_ep0state (priv, EP0STATE_SHORTWRITE);
}
break;
default:
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADGETSTATUS),
0);
priv->stalled = true;
}
break;
}
}
}
break;
case USB_REQ_CLEARFEATURE:
{
* value: feature selector
* index: zero interface endpoint;
* len: zero, data = none
*/
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_CLEARFEATURE), 0);
if ((ctrl->type & USB_REQ_RECIPIENT_MASK) !=
USB_REQ_RECIPIENT_ENDPOINT)
{
lpc43_dispatchrequest(priv, ctrl);
}
else if (priv->paddrset != 0 && value ==
USB_FEATURE_ENDPOINTHALT && len == 0 &&
(privep = lpc43_epfindbyaddr(priv, index)) != NULL)
{
lpc43_epstall(&privep->ep, true);
lpc43_ep0state (priv, EP0STATE_WAIT_NAK_IN);
}
else
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADCLEARFEATURE), 0);
priv->stalled = true;
}
}
break;
case USB_REQ_SETFEATURE:
{
* value: feature selector
* index: zero interface endpoint;
* len: 0; data = none
*/
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_SETFEATURE), 0);
if (((ctrl->type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE) &&
value == USB_FEATURE_TESTMODE)
{
uinfo("test mode: %d\n", index);
}
else if ((ctrl->type & USB_REQ_RECIPIENT_MASK) !=
USB_REQ_RECIPIENT_ENDPOINT)
{
lpc43_dispatchrequest(priv, ctrl);
}
else if (priv->paddrset != 0 && value ==
USB_FEATURE_ENDPOINTHALT && len == 0 &&
(privep = lpc43_epfindbyaddr(priv, index)) != NULL)
{
lpc43_epstall(&privep->ep, false);
lpc43_ep0state (priv, EP0STATE_WAIT_NAK_IN);
}
else
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADSETFEATURE), 0);
priv->stalled = true;
}
}
break;
case USB_REQ_SETADDRESS:
{
* value: device address
* index: 0
* len: 0; data = none
*/
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EP0SETUPSETADDRESS),
value);
if ((ctrl->type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE &&
index == 0 && len == 0 && value < 128)
{
* We cannot actually change to the next address until
* the completion of the status phase.
*/
priv->paddr = ctrl->value[0];
priv->paddrset = false;
lpc43_ep0state (priv, EP0STATE_WAIT_NAK_IN);
}
else
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADSETADDRESS), 0);
priv->stalled = true;
}
}
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(LPC43_TRACEINTID_GETSETDESC), 0);
if ((ctrl->type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE)
{
lpc43_dispatchrequest(priv, ctrl);
}
else
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADGETSETDESC), 0);
priv->stalled = true;
}
}
break;
case USB_REQ_GETCONFIGURATION:
* value: 0;
* index: 0;
* len: 1; data = configuration value
*/
{
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_GETCONFIG), 0);
if (priv->paddrset && (ctrl->type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE &&
value == 0 && index == 0 && len == 1)
{
lpc43_dispatchrequest(priv, ctrl);
}
else
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADGETCONFIG), 0);
priv->stalled = true;
}
}
break;
case USB_REQ_SETCONFIGURATION:
* value: configuration value
* index: 0;
* len: 0; data = none
*/
{
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_SETCONFIG), 0);
if ((ctrl->type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE &&
index == 0 && len == 0)
{
lpc43_dispatchrequest(priv, ctrl);
}
else
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADSETCONFIG), 0);
priv->stalled = true;
}
}
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(LPC43_TRACEINTID_GETSETIF), 0);
lpc43_dispatchrequest(priv, ctrl);
}
break;
case USB_REQ_SYNCHFRAME:
* value: 0
* index: endpoint;
* len: 2; data = frame number
*/
{
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_SYNCHFRAME), 0);
}
break;
default:
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDCTRLREQ), 0);
priv->stalled = true;
}
break;
}
}
if (priv->stalled)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_EP0SETUPSTALLED),
priv->ep0state);
lpc43_epstall(&priv->eplist[LPC43_EP0_IN].ep, false);
lpc43_epstall(&priv->eplist[LPC43_EP0_OUT].ep, false);
}
}
* Name: lpc43_ep0complete
*
* Description:
* Transfer complete handler for Endpoint 0
*
****************************************************************************/
static void lpc43_ep0complete(struct lpc43_usbdev_s *priv, uint8_t epphy)
{
struct lpc43_ep_s *privep = &priv->eplist[epphy];
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EP0COMPLETE),
(uint16_t)priv->ep0state);
switch (priv->ep0state)
{
case EP0STATE_DATA_IN:
if (lpc43_rqempty(privep))
{
return;
}
if (lpc43_epcomplete (priv, epphy))
{
lpc43_ep0state (priv, EP0STATE_WAIT_NAK_OUT);
}
break;
case EP0STATE_DATA_OUT:
if (lpc43_rqempty(privep))
{
return;
}
if (lpc43_epcomplete (priv, epphy))
{
lpc43_ep0state (priv, EP0STATE_WAIT_NAK_IN);
}
break;
case EP0STATE_SHORTREAD:
lpc43_dispatchrequest(priv, &priv->ep0ctrl);
lpc43_ep0state (priv, EP0STATE_WAIT_NAK_IN);
break;
case EP0STATE_SHORTWRITE:
lpc43_ep0state (priv, EP0STATE_WAIT_NAK_OUT);
break;
case EP0STATE_WAIT_STATUS_IN:
lpc43_ep0state (priv, EP0STATE_IDLE);
* now that the status phase has completed
*/
if (! priv->paddrset && priv->paddr != 0)
{
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EP0INSETADDRESS),
(uint16_t)priv->paddr);
lpc43_set_address (priv, priv->paddr);
}
break;
case EP0STATE_WAIT_STATUS_OUT:
lpc43_ep0state (priv, EP0STATE_IDLE);
break;
default:
#ifdef CONFIG_DEBUG_FEATURES
DEBUGASSERT(priv->ep0state != EP0STATE_DATA_IN &&
priv->ep0state != EP0STATE_DATA_OUT &&
priv->ep0state != EP0STATE_SHORTWRITE &&
priv->ep0state != EP0STATE_WAIT_STATUS_IN &&
priv->ep0state != EP0STATE_WAIT_STATUS_OUT);
#endif
priv->stalled = true;
break;
}
if (priv->stalled)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_EP0SETUPSTALLED),
priv->ep0state);
lpc43_epstall(&priv->eplist[LPC43_EP0_IN].ep, false);
lpc43_epstall(&priv->eplist[LPC43_EP0_OUT].ep, false);
}
}
* Name: lpc43_ep0nak
*
* Description:
* Handle a NAK interrupt on EP0
*
****************************************************************************/
static void lpc43_ep0nak(struct lpc43_usbdev_s *priv, uint8_t epphy)
{
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EP0NAK),
(uint16_t)priv->ep0state);
switch (priv->ep0state)
{
case EP0STATE_WAIT_NAK_IN:
lpc43_ep0xfer (LPC43_EP0_IN, NULL, 0);
lpc43_ep0state (priv, EP0STATE_WAIT_STATUS_IN);
break;
case EP0STATE_WAIT_NAK_OUT:
lpc43_ep0xfer (LPC43_EP0_OUT, NULL, 0);
lpc43_ep0state (priv, EP0STATE_WAIT_STATUS_OUT);
break;
default:
#ifdef CONFIG_DEBUG_FEATURES
DEBUGASSERT(priv->ep0state != EP0STATE_WAIT_NAK_IN &&
priv->ep0state != EP0STATE_WAIT_NAK_OUT);
#endif
priv->stalled = true;
break;
}
if (priv->stalled)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_EP0SETUPSTALLED),
priv->ep0state);
lpc43_epstall(&priv->eplist[LPC43_EP0_IN].ep, false);
lpc43_epstall(&priv->eplist[LPC43_EP0_OUT].ep, false);
}
}
* Name: lpc43_epcomplete
*
* Description:
* Transfer complete handler for Endpoints other than 0
* returns whether the request at the head has completed
*
****************************************************************************/
bool lpc43_epcomplete(struct lpc43_usbdev_s *priv, uint8_t epphy)
{
struct lpc43_ep_s *privep = &priv->eplist[epphy];
struct lpc43_req_s *privreq = privep->head;
struct lpc43_dtd_s *dtd = &g_td[epphy];
if (privreq == NULL)
{
if (LPC43_EPPHYOUT(privep->epphy))
{
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EPINQEMPTY), 0);
}
else
{
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EPOUTQEMPTY), 0);
}
return true;
}
int xfrd = dtd->xfer_len - (dtd->config >> 16);
privreq->req.xfrd += xfrd;
bool complete = true;
if (LPC43_EPPHYOUT(privep->epphy))
{
* received
*/
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EPIN), complete);
}
else
{
* terminate with a ZLP
*/
bool need_zlp = (xfrd == privep->ep.maxpacket) &&
((privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0);
complete = (privreq->req.xfrd >= privreq->req.len && !need_zlp);
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EPOUT), complete);
}
* queued requests
*/
if (complete)
{
privreq = lpc43_rqdequeue (privep);
}
if (!lpc43_rqempty(privep))
{
lpc43_progressep(privep);
}
* submit a new request
*/
if (complete)
{
usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd);
lpc43_reqcomplete(privep, privreq, OK);
}
return complete;
}
* Name: lpc43_usbinterrupt
*
* Description:
* USB interrupt handler
*
****************************************************************************/
static int lpc43_usbinterrupt(int irq, void *context, void *arg)
{
struct lpc43_usbdev_s *priv = &g_usbdev;
uint32_t disr;
uint32_t portsc1;
uint32_t n;
usbtrace(TRACE_INTENTRY(LPC43_TRACEINTID_USB), 0);
disr = lpc43_getreg(LPC43_USBDEV_USBSTS);
lpc43_putreg(disr, LPC43_USBDEV_USBSTS);
if (disr & USBDEV_USBSTS_URI)
{
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_DEVRESET), 0);
lpc43_usbreset(priv);
usbtrace(TRACE_INTEXIT(LPC43_TRACEINTID_USB), 0);
return OK;
}
* the SLI bit will be set to a one.
*/
if (!priv->suspended && (disr & USBDEV_USBSTS_SLI) != 0)
{
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_SUSPENDED), 0);
priv->suspended = 1;
if (priv->driver)
{
CLASS_SUSPEND(priv->driver, &priv->usbdev);
}
}
* state. This bit can also be cleared by software writing a one to it.
*/
else if (priv->suspended && (disr & USBDEV_USBSTS_SLI) == 0)
{
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_RESUMED), 0);
priv->suspended = 0;
if (priv->driver)
{
CLASS_RESUME(priv->driver, &priv->usbdev);
}
}
if (disr & USBDEV_USBSTS_PCI)
{
portsc1 = lpc43_getreg(LPC43_USBDEV_PORTSC1);
if (portsc1 & USBDEV_PRTSC1_HSP)
priv->usbdev.speed = USB_SPEED_HIGH;
else
priv->usbdev.speed = USB_SPEED_FULL;
if (portsc1 & USBDEV_PRTSC1_FPR)
{
* while the port is in SUSPEND state - presumambly this
* is where the host is resuming the device?
*
* - but do we need to "ack" the interrupt
*/
}
}
#ifdef CONFIG_LPC43_USBDEV_FRAME_INTERRUPT
if (disr & USBDEV_USBSTT_SRI)
{
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_FRAME), 0);
priv->sof = lpc43_getreg(LPC43_USBDEV_FRINDEX);
}
#endif
if (disr & USBDEV_USBSTS_UEI)
{
* it is set alongside USBINT if the DTD also had its IOC
* bit set.
*/
}
if (disr & USBDEV_USBSTS_UI)
{
uint32_t mask = lpc43_getreg (LPC43_USBDEV_ENDPTCOMPLETE);
if (mask)
{
lpc43_putreg (mask, LPC43_USBDEV_ENDPTNAK);
lpc43_putreg (mask, LPC43_USBDEV_ENDPTCOMPLETE);
if (mask & LPC43_ENDPTMASK(0))
{
lpc43_ep0complete(priv, 0);
}
if (mask & LPC43_ENDPTMASK(1))
{
lpc43_ep0complete(priv, 1);
}
for (n = 1; n < LPC43_NLOGENDPOINTS; n++)
{
if (mask & LPC43_ENDPTMASK((n << 1)))
{
lpc43_epcomplete (priv, (n << 1));
}
if (mask & LPC43_ENDPTMASK((n << 1)+1))
{
lpc43_epcomplete(priv, (n << 1)+1);
}
}
}
uint32_t setupstat = lpc43_getreg(LPC43_USBDEV_ENDPTSETUPSTAT);
if (setupstat)
{
* received
*/
lpc43_putreg(LPC43_ENDPTMASK(LPC43_EP0_IN) |
LPC43_ENDPTMASK(LPC43_EP0_OUT),
LPC43_USBDEV_ENDPTCOMPLETE);
if (setupstat & LPC43_ENDPTMASK(LPC43_EP0_OUT))
{
usbtrace(TRACE_INTDECODE(LPC43_TRACEINTID_EP0SETUP),
setupstat);
lpc43_ep0setup(priv);
}
}
}
if (disr & USBDEV_USBSTS_NAKI)
{
uint32_t pending = lpc43_getreg(LPC43_USBDEV_ENDPTNAK) &
lpc43_getreg(LPC43_USBDEV_ENDPTNAKEN);
if (pending)
{
if (pending & LPC43_ENDPTMASK(0))
{
lpc43_ep0nak(priv, 0);
}
if (pending & LPC43_ENDPTMASK(1))
{
lpc43_ep0nak(priv, 1);
}
}
lpc43_putreg(pending, LPC43_USBDEV_ENDPTNAK);
}
usbtrace(TRACE_INTEXIT(LPC43_TRACEINTID_USB), 0);
return OK;
}
* Endpoint operations
****************************************************************************/
* Name: lpc43_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 lpc43_epconfigure(struct usbdev_ep_s *ep,
const struct usb_epdesc_s *desc,
bool last)
{
struct lpc43_ep_s *privep = (struct lpc43_ep_s *)ep;
usbtrace(TRACE_EPCONFIGURE, privep->epphy);
DEBUGASSERT(desc->addr == ep->eplog);
uint16_t maxsize = GETUINT16(desc->mxpacketsize);
if ((desc->attr & USB_EP_ATTR_XFERTYPE_MASK) == USB_EP_ATTR_XFER_ISOC)
{
g_qh[privep->epphy].capability = (DQH_CAPABILITY_MAX_PACKET(maxsize) |
DQH_CAPABILITY_IOS |
DQH_CAPABILITY_ZLT);
}
else
{
g_qh[privep->epphy].capability = (DQH_CAPABILITY_MAX_PACKET(maxsize) |
DQH_CAPABILITY_ZLT);
}
if (LPC43_EPPHYIN(privep->epphy))
{
uint32_t cfg = USBDEV_ENDPTCTRL_TXR;
switch (desc->attr & USB_EP_ATTR_XFERTYPE_MASK)
{
case USB_EP_ATTR_XFER_CONTROL:
cfg |= USBDEV_ENDPTCTRL_TXT_CTRL;
break;
case USB_EP_ATTR_XFER_ISOC:
cfg |= USBDEV_ENDPTCTRL_TXT_ISOC;
break;
case USB_EP_ATTR_XFER_BULK:
cfg |= USBDEV_ENDPTCTRL_TXT_BULK;
break;
case USB_EP_ATTR_XFER_INT:
cfg |= USBDEV_ENDPTCTRL_TXT_INTR;
break;
}
lpc43_chgbits (0xffff0000,
cfg, LPC43_USBDEV_ENDPTCTRL(privep->epphy >> 1));
}
else
{
uint32_t cfg = USBDEV_ENDPTCTRL_RXR;
switch (desc->attr & USB_EP_ATTR_XFERTYPE_MASK)
{
case USB_EP_ATTR_XFER_CONTROL:
cfg |= USBDEV_ENDPTCTRL_RXT_CTRL;
break;
case USB_EP_ATTR_XFER_ISOC:
cfg |= USBDEV_ENDPTCTRL_RXT_ISOC;
break;
case USB_EP_ATTR_XFER_BULK:
cfg |= USBDEV_ENDPTCTRL_RXT_BULK;
break;
}
lpc43_chgbits (0x0000ffff, cfg,
LPC43_USBDEV_ENDPTCTRL(privep->epphy >> 1));
}
privep->stalled = false;
if (LPC43_EPPHYIN(privep->epphy))
{
lpc43_setbits (USBDEV_ENDPTCTRL_TXE,
LPC43_USBDEV_ENDPTCTRL(privep->epphy >> 1));
}
else
{
lpc43_setbits (USBDEV_ENDPTCTRL_RXE,
LPC43_USBDEV_ENDPTCTRL(privep->epphy >> 1));
}
return OK;
}
* Name: lpc43_epdisable
*
* Description:
* The endpoint will no longer be used
*
****************************************************************************/
static int lpc43_epdisable(struct usbdev_ep_s *ep)
{
struct lpc43_ep_s *privep = (struct lpc43_ep_s *)ep;
irqstate_t flags;
#ifdef CONFIG_DEBUG_FEATURES
if (!ep)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
usbtrace(TRACE_EPDISABLE, privep->epphy);
flags = enter_critical_section();
if (LPC43_EPPHYIN(privep->epphy))
{
lpc43_clrbits (USBDEV_ENDPTCTRL_TXE,
LPC43_USBDEV_ENDPTCTRL(privep->epphy >> 1));
}
else
{
lpc43_clrbits (USBDEV_ENDPTCTRL_RXE,
LPC43_USBDEV_ENDPTCTRL(privep->epphy >> 1));
}
privep->stalled = true;
lpc43_cancelrequests(privep, -ESHUTDOWN);
leave_critical_section(flags);
return OK;
}
* Name: lpc43_epallocreq
*
* Description:
* Allocate an I/O request
*
****************************************************************************/
static struct
usbdev_req_s *lpc43_epallocreq(struct usbdev_ep_s *ep)
{
struct lpc43_req_s *privreq;
#ifdef CONFIG_DEBUG_FEATURES
if (!ep)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDPARMS), 0);
return NULL;
}
#endif
usbtrace(TRACE_EPALLOCREQ, ((struct lpc43_ep_s *)ep)->epphy);
privreq = kmm_malloc(sizeof(struct lpc43_req_s));
if (!privreq)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_ALLOCFAIL), 0);
return NULL;
}
memset(privreq, 0, sizeof(struct lpc43_req_s));
return &privreq->req;
}
* Name: lpc43_epfreereq
*
* Description:
* Free an I/O request
*
****************************************************************************/
static void lpc43_epfreereq(struct usbdev_ep_s *ep,
struct usbdev_req_s *req)
{
struct lpc43_req_s *privreq = (struct lpc43_req_s *)req;
#ifdef CONFIG_DEBUG_FEATURES
if (!ep || !req)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDPARMS), 0);
return;
}
#endif
usbtrace(TRACE_EPFREEREQ, ((struct lpc43_ep_s *)ep)->epphy);
kmm_free(privreq);
}
* Name: lpc43_epallocbuffer
*
* Description:
* Allocate an I/O buffer
*
****************************************************************************/
#ifdef CONFIG_USBDEV_DMA
static void *lpc43_epallocbuffer(struct usbdev_ep_s *ep, unsigned bytes)
{
usbtrace(TRACE_EPALLOCBUFFER, privep->epphy);
#ifdef CONFIG_USBDEV_DMAMEMORY
return usbdev_dma_alloc(bytes);
#else
return kmm_malloc(bytes);
#endif
}
#endif
* Name: lpc43_epfreebuffer
*
* Description:
* Free an I/O buffer
*
****************************************************************************/
#ifdef CONFIG_USBDEV_DMA
static void lpc43_epfreebuffer(struct usbdev_ep_s *ep, void *buf)
{
usbtrace(TRACE_EPFREEBUFFER, privep->epphy);
#ifdef CONFIG_USBDEV_DMAMEMORY
usbdev_dma_free(buf);
#else
kmm_free(buf);
#endif
}
#endif
* Name: lpc43_epsubmit
*
* Description:
* Submit an I/O request to the endpoint
*
****************************************************************************/
static int lpc43_epsubmit(struct usbdev_ep_s *ep,
struct usbdev_req_s *req)
{
struct lpc43_req_s *privreq = (struct lpc43_req_s *)req;
struct lpc43_ep_s *privep = (struct lpc43_ep_s *)ep;
struct lpc43_usbdev_s *priv;
irqstate_t flags;
int ret = OK;
#ifdef CONFIG_DEBUG_FEATURES
if (!req || !req->callback || !req->buf || !ep)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDPARMS), 0);
uinfo("req=%p callback=%p buf=%p ep=%p\n",
req, req->callback, req->buf, ep);
return -EINVAL;
}
#endif
usbtrace(TRACE_EPSUBMIT, privep->epphy);
priv = privep->dev;
if (!priv->driver || priv->usbdev.speed == USB_SPEED_UNKNOWN)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_NOTCONFIGURED),
priv->usbdev.speed);
return -ESHUTDOWN;
}
req->result = -EINPROGRESS;
req->xfrd = 0;
flags = enter_critical_section();
if (privep->stalled)
{
ret = -EBUSY;
}
else
{
if (LPC43_EPPHYIN(privep->epphy))
{
usbtrace(TRACE_INREQQUEUED(privep->epphy), privreq->req.len);
}
else
{
usbtrace(TRACE_OUTREQQUEUED(privep->epphy), privreq->req.len);
}
if (lpc43_rqenqueue(privep, privreq))
{
lpc43_progressep(privep);
}
}
leave_critical_section(flags);
return ret;
}
* Name: lpc43_epcancel
*
* Description:
* Cancel an I/O request previously sent to an endpoint
*
****************************************************************************/
static int lpc43_epcancel(struct usbdev_ep_s *ep,
struct usbdev_req_s *req)
{
struct lpc43_ep_s *privep = (struct lpc43_ep_s *)ep;
irqstate_t flags;
#ifdef CONFIG_DEBUG_FEATURES
if (!ep || !req)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
usbtrace(TRACE_EPCANCEL, privep->epphy);
flags = enter_critical_section();
* otherwise just remove it from the list
*
* but ... all other implementations cancel all requests ...
*/
lpc43_cancelrequests(privep, -ESHUTDOWN);
leave_critical_section(flags);
return OK;
}
* Name: lpc43_epstall
*
* Description:
* Stall or resume and endpoint
*
****************************************************************************/
static int lpc43_epstall(struct usbdev_ep_s *ep, bool resume)
{
struct lpc43_ep_s *privep = (struct lpc43_ep_s *)ep;
irqstate_t flags;
flags = enter_critical_section();
usbtrace(resume ? TRACE_EPRESUME : TRACE_EPSTALL, privep->epphy);
uint32_t addr = LPC43_USBDEV_ENDPTCTRL(privep->epphy >> 1);
uint32_t ctrl_xs = LPC43_EPPHYIN(privep->epphy) ?
USBDEV_ENDPTCTRL_TXS : USBDEV_ENDPTCTRL_RXS;
uint32_t ctrl_xr = LPC43_EPPHYIN(privep->epphy) ?
USBDEV_ENDPTCTRL_TXR : USBDEV_ENDPTCTRL_RXR;
if (resume)
{
privep->stalled = false;
lpc43_chgbits (ctrl_xs | ctrl_xr, ctrl_xr, addr);
}
else
{
privep->stalled = true;
lpc43_setbits (ctrl_xs, addr);
}
leave_critical_section(flags);
return OK;
}
* Device operations
****************************************************************************/
* Name: lpc43_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 struct usbdev_ep_s *lpc43_allocep(struct usbdev_s *dev,
uint8_t eplog,
bool in, uint8_t eptype)
{
struct lpc43_usbdev_s *priv = (struct lpc43_usbdev_s *)dev;
uint32_t epset = LPC43_EPALLSET & ~LPC43_EPCTRLSET;
irqstate_t flags;
int epndx = 0;
usbtrace(TRACE_DEVALLOCEP, (uint16_t)eplog);
eplog = USB_EPNO(eplog);
if (eplog > 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 (eplog >= LPC43_NLOGENDPOINTS)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADEPNO), (uint16_t)eplog);
return NULL;
}
* remove all of the candidate endpoints from the bitset except for the
* the IN/OUT pair for this logical address.
*/
epset &= 3 << (eplog << 1);
}
if (in)
{
epset &= LPC43_EPINSET;
}
else
{
epset &= LPC43_EPOUTSET;
}
switch (eptype)
{
case USB_EP_ATTR_XFER_INT:
epset &= LPC43_EPINTRSET;
break;
case USB_EP_ATTR_XFER_BULK:
epset &= LPC43_EPBULKSET;
break;
case USB_EP_ATTR_XFER_ISOC:
epset &= LPC43_EPISOCSET;
break;
case USB_EP_ATTR_XFER_CONTROL:
default:
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BADEPTYPE), (uint16_t)eptype);
return NULL;
}
if (epset)
{
flags = enter_critical_section();
epset &= priv->epavail;
if (epset)
{
* available endpoints
*/
for (epndx = 2; epndx < LPC43_NPHYSENDPOINTS; epndx++)
{
uint32_t bit = 1 << epndx;
if ((epset & bit) != 0)
{
priv->epavail &= ~bit;
leave_critical_section(flags);
* structure
*/
return &priv->eplist[epndx].ep;
}
}
}
leave_critical_section(flags);
}
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_NOEP), (uint16_t)eplog);
return NULL;
}
* Name: lpc43_freeep
*
* Description:
* Free the previously allocated endpoint
*
****************************************************************************/
static void lpc43_freeep(struct usbdev_s *dev,
struct usbdev_ep_s *ep)
{
struct lpc43_usbdev_s *priv = (struct lpc43_usbdev_s *)dev;
struct lpc43_ep_s *privep = (struct lpc43_ep_s *)ep;
irqstate_t flags;
usbtrace(TRACE_DEVFREEEP, (uint16_t)privep->epphy);
if (priv && privep)
{
flags = enter_critical_section();
priv->epavail |= (1 << privep->epphy);
leave_critical_section(flags);
}
}
* Name: lpc43_getframe
*
* Description:
* Returns the current frame number
*
****************************************************************************/
static int lpc43_getframe(struct usbdev_s *dev)
{
#ifdef CONFIG_LPC43_USBDEV_FRAME_INTERRUPT
struct lpc43_usbdev_s *priv = (struct lpc43_usbdev_s *)dev;
usbtrace(TRACE_DEVGETFRAME, (uint16_t)priv->sof);
return priv->sof;
#else
usbtrace(TRACE_DEVGETFRAME, 0);
return (int)lpc43_getreg(LPC43_USBDEV_FRINDEX);
#endif
}
* Name: lpc43_wakeup
*
* Description:
* Tries to wake up the host connected to this device
*
****************************************************************************/
static int lpc43_wakeup(struct usbdev_s *dev)
{
irqstate_t flags;
usbtrace(TRACE_DEVWAKEUP, 0);
flags = enter_critical_section();
lpc43_setbits(USBDEV_PRTSC1_FPR, LPC43_USBDEV_PORTSC1);
leave_critical_section(flags);
return OK;
}
* Name: lpc43_selfpowered
*
* Description:
* Sets/clears the device selfpowered feature
*
****************************************************************************/
static int lpc43_selfpowered(struct usbdev_s *dev, bool selfpowered)
{
struct lpc43_usbdev_s *priv = (struct lpc43_usbdev_s *)dev;
usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered);
#ifdef CONFIG_DEBUG_FEATURES
if (!dev)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDPARMS), 0);
return -ENODEV;
}
#endif
priv->selfpowered = selfpowered;
return OK;
}
* Name: lpc43_pullup
*
* Description:
* Software-controlled connect to/disconnect from USB host
*
****************************************************************************/
static int lpc43_pullup(struct usbdev_s *dev, bool enable)
{
usbtrace(TRACE_DEVPULLUP, (uint16_t)enable);
irqstate_t flags = enter_critical_section();
if (enable)
{
lpc43_setbits (USBDEV_USBCMD_RS, LPC43_USBDEV_USBCMD);
#ifdef CONFIG_LPC43_USB0DEV_NOVBUS
lpc43_clrbits (USBOTG_OTGSC_VD, LPC43_USBOTG_OTGSC);
lpc43_setbits (USBOTG_OTGSC_VC, LPC43_USBOTG_OTGSC);
#endif
}
else
{
lpc43_clrbits (USBDEV_USBCMD_RS, LPC43_USBDEV_USBCMD);
}
leave_critical_section(flags);
return OK;
}
* Public Functions
****************************************************************************/
* Name: arm_usbinitialize
*
* Description:
* Initialize USB hardware.
*
* Assumptions:
* - This function is called very early in the initialization sequence
* - PLL initialization is not performed here but should been in
* the low-level boot logic: PLL0 must be configured for operation
* at 480MHz
*
****************************************************************************/
void arm_usbinitialize(void)
{
struct lpc43_usbdev_s *priv = &g_usbdev;
int i;
uint32_t regval;
irqstate_t flags;
flags = enter_critical_section();
memset(priv, 0, sizeof(struct lpc43_usbdev_s));
priv->usbdev.ops = &g_devops;
priv->usbdev.ep0 = &priv->eplist[LPC43_EP0_IN].ep;
priv->epavail = LPC43_EPALLSET & ~LPC43_EPCTRLSET;
for (i = 0; i < LPC43_NPHYSENDPOINTS; i++)
{
uint32_t bit = 1 << i;
* really necessary because there is only one controller), and
* the physical endpoint number (which is just the index to the
* endpoint).
*/
priv->eplist[i].ep.ops = &g_epops;
priv->eplist[i].dev = priv;
* to a logical endpoint address usable by the class driver.
*/
priv->eplist[i].epphy = i;
if (LPC43_EPPHYIN(i))
{
priv->eplist[i].ep.eplog = LPC43_EPPHYIN2LOG(i);
}
else
{
priv->eplist[i].ep.eplog = LPC43_EPPHYOUT2LOG(i);
}
if ((LPC43_EPCTRLSET & bit) != 0)
{
priv->eplist[i].ep.maxpacket = LPC43_EP0MAXPACKET;
}
else if ((LPC43_EPINTRSET & bit) != 0)
{
priv->eplist[i].ep.maxpacket = LPC43_INTRMAXPACKET;
}
else if ((LPC43_EPBULKSET & bit) != 0)
{
priv->eplist[i].ep.maxpacket = LPC43_BULKMAXPACKET;
}
else
{
priv->eplist[i].ep.maxpacket = LPC43_ISOCMAXPACKET;
}
}
lpc43_pll0usbconfig();
lpc43_pll0usbenable();
regval = getreg32(LPC43_BASE_USB0_CLK);
regval &= ~(BASE_USB0_CLK_CLKSEL_MASK | BASE_USB0_CLK_PD) ;
regval |= (BASE_USB0_CLKSEL_PLL0USB | BASE_USB0_CLK_AUTOBLOCK);
putreg32(regval, LPC43_BASE_USB0_CLK);
regval = getreg32(LPC43_CCU1_M4_USB0_CFG);
regval |= CCU_CLK_CFG_RUN;
putreg32(regval, LPC43_CCU1_M4_USB0_CFG);
lpc43_pullup(&priv->usbdev, false);
lpc43_setbits (USBDEV_USBCMD_RST, LPC43_USBDEV_USBCMD);
while (lpc43_getreg (LPC43_USBDEV_USBCMD) & USBDEV_USBCMD_RST)
;
regval = getreg32(LPC43_CREG0);
regval &= ~CREG0_USB0PHY;
putreg32(regval, LPC43_CREG0);
lpc43_putreg(0, LPC43_USBDEV_USBINTR);
lpc43_putreg (USBDEV_USBMODE_SDIS | USBDEV_USBMODE_SLOM |
USBDEV_USBMODE_CM_DEVICE,
LPC43_USBDEV_USBMODE);
irq_attach(LPC43M4_IRQ_USB0, lpc43_usbinterrupt, NULL);
up_enable_irq(LPC43M4_IRQ_USB0);
leave_critical_section(flags);
lpc43_usbreset(priv);
}
* Name: arm_usbuninitialize
****************************************************************************/
void arm_usbuninitialize(void)
{
struct lpc43_usbdev_s *priv = &g_usbdev;
irqstate_t flags;
usbtrace(TRACE_DEVUNINIT, 0);
if (priv->driver)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_DRIVERREGISTERED), 0);
usbdev_unregister(priv->driver);
}
flags = enter_critical_section();
lpc43_pullup(&priv->usbdev, false);
priv->usbdev.speed = USB_SPEED_UNKNOWN;
up_disable_irq(LPC43M4_IRQ_USB0);
irq_detach(LPC43M4_IRQ_USB0);
lpc43_setbits (USBDEV_USBCMD_RST, LPC43_USBDEV_USBCMD);
while (lpc43_getreg (LPC43_USBDEV_USBCMD) & USBDEV_USBCMD_RST)
;
lpc43_pll0usbdisable();
leave_critical_section(flags);
}
* Name: usbdev_register
*
* Description:
* Register a USB device class driver. The class driver's bind() method
* will be called to bind it to a USB device driver.
*
****************************************************************************/
int usbdev_register(struct usbdevclass_driver_s *driver)
{
int ret;
usbtrace(TRACE_DEVREGISTER, 0);
#ifdef CONFIG_DEBUG_FEATURES
if (!driver || !driver->ops->bind || !driver->ops->unbind ||
!driver->ops->disconnect || !driver->ops->setup)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
if (g_usbdev.driver)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_DRIVER), 0);
return -EBUSY;
}
#endif
g_usbdev.driver = driver;
ret = CLASS_BIND(driver, &g_usbdev.usbdev);
if (ret)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_BINDFAILED), (uint16_t)-ret);
g_usbdev.driver = NULL;
}
else
{
up_enable_irq(LPC43M4_IRQ_USB0);
* the RS bit to enable the controller. It kind of makes sense
* to do this after the class has bound to us...
* GEN: This bug is really in the class driver. It should make the
* soft connect when it is ready to be enumerated. I have added
* that logic to the class drivers but left this logic here.
*/
lpc43_pullup(&g_usbdev.usbdev, true);
}
return ret;
}
* Name: usbdev_unregister
*
* Description:
* Un-register usbdev class driver.If the USB device is connected to a USB
* host, it will first disconnect(). The driver is also requested to
* unbind() and clean up any device state, before this procedure finally
* returns.
*
****************************************************************************/
int usbdev_unregister(struct usbdevclass_driver_s *driver)
{
usbtrace(TRACE_DEVUNREGISTER, 0);
#ifdef CONFIG_DEBUG_FEATURES
if (driver != g_usbdev.driver)
{
usbtrace(TRACE_DEVERROR(LPC43_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
CLASS_UNBIND(driver, &g_usbdev.usbdev);
up_disable_irq(LPC43M4_IRQ_USB0);
g_usbdev.driver = NULL;
return OK;
}