| /**************************************************************************** |
| * arch/arm/src/lpc214x/lpc214x_usbdev.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * 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/usb/usb.h> |
| #include <nuttx/usb/usbdev.h> |
| #include <nuttx/usb/usbdev_trace.h> |
| |
| #include <nuttx/irq.h> |
| #include <arch/board/board.h> |
| |
| #include "chip.h" |
| #include "arm_internal.h" |
| #include "lpc214x_usbdev.h" |
| #include "lpc214x_pll.h" |
| #include "lpc214x_power.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* Configuration ************************************************************/ |
| |
| #ifndef CONFIG_USBDEV_EP0_MAXSIZE |
| # define CONFIG_USBDEV_EP0_MAXSIZE 64 |
| #endif |
| |
| #ifndef CONFIG_USBDEV_MAXPOWER |
| # define CONFIG_USBDEV_MAXPOWER 100 /* mA */ |
| #endif |
| |
| #define USB_SLOW_INT USBDEV_DEVINT_EPSLOW |
| #define USB_DEVSTATUS_INT USBDEV_DEVINT_DEVSTAT |
| |
| #ifdef CONFIG_LPC214X_USBDEV_EPFAST_INTERRUPT |
| # define USB_FAST_INT USBDEV_DEVINT_EPFAST |
| #else |
| # define USB_FAST_INT 0 |
| #endif |
| |
| /* Extremely detailed register debug that you would normally never want |
| * enabled. |
| */ |
| |
| #undef CONFIG_LPC214X_USBDEV_REGDEBUG |
| |
| /* Enable reading SOF from interrupt handler vs. simply reading on demand. |
| * Probably a bad idea... Unless there is some issue with sampling the SOF |
| * from hardware asynchronously. |
| */ |
| |
| #ifdef CONFIG_LPC214X_USBDEV_FRAME_INTERRUPT |
| # define USB_FRAME_INT USBDEV_DEVINT_FRAME |
| #else |
| # define USB_FRAME_INT 0 |
| #endif |
| |
| #ifdef CONFIG_DEBUG_FEATURES |
| # define USB_ERROR_INT USBDEV_DEVINT_EPRINT |
| #else |
| # define USB_ERROR_INT 0 |
| #endif |
| |
| /* Number of DMA descriptors */ |
| |
| #ifdef CONFIG_LPC214X_USBDEV_DMA |
| # error DMA SUPPORT NOT YET FULLY IMPLEMENTED |
| # ifndef CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS |
| # define CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS 8 |
| # elif CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS > 30 |
| # define CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS 30 |
| # endif |
| #endif |
| |
| /* Debug ********************************************************************/ |
| |
| /* Trace error codes */ |
| |
| #define LPC214X_TRACEERR_ALLOCFAIL 0x0001 |
| #define LPC214X_TRACEERR_BADCLEARFEATURE 0x0002 |
| #define LPC214X_TRACEERR_BADDEVGETSTATUS 0x0003 |
| #define LPC214X_TRACEERR_BADEPNO 0x0004 |
| #define LPC214X_TRACEERR_BADEPGETSTATUS 0x0005 |
| #define LPC214X_TRACEERR_BADEPTYPE 0x0006 |
| #define LPC214X_TRACEERR_BADGETCONFIG 0x0007 |
| #define LPC214X_TRACEERR_BADGETSETDESC 0x0008 |
| #define LPC214X_TRACEERR_BADGETSTATUS 0x0009 |
| #define LPC214X_TRACEERR_BADSETADDRESS 0x000a |
| #define LPC214X_TRACEERR_BADSETCONFIG 0x000b |
| #define LPC214X_TRACEERR_BADSETFEATURE 0x000c |
| #define LPC214X_TRACEERR_BINDFAILED 0x000d |
| #define LPC214X_TRACEERR_DISPATCHSTALL 0x000e |
| #define LPC214X_TRACEERR_DMABUSY 0x000f |
| #define LPC214X_TRACEERR_DRIVER 0x0010 |
| #define LPC214X_TRACEERR_DRIVERREGISTERED 0x0011 |
| #define LPC214X_TRACEERR_EP0INSTALLED 0x0012 |
| #define LPC214X_TRACEERR_EP0OUTSTALLED 0x0013 |
| #define LPC214X_TRACEERR_EP0SETUPSTALLED 0x0014 |
| #define LPC214X_TRACEERR_EPINNULLPACKET 0x0015 |
| #define LPC214X_TRACEERR_EPOUTNULLPACKET 0x0016 |
| #define LPC214X_TRACEERR_EPREAD 0x0017 |
| #define LPC214X_TRACEERR_INVALIDCMD 0x0018 |
| #define LPC214X_TRACEERR_INVALIDCTRLREQ 0x0019 |
| #define LPC214X_TRACEERR_INVALIDPARMS 0x001a |
| #define LPC214X_TRACEERR_IRQREGISTRATION 0x001b |
| #define LPC214X_TRACEERR_NODMADESC 0x001c |
| #define LPC214X_TRACEERR_NOEP 0x001d |
| #define LPC214X_TRACEERR_NOTCONFIGURED 0x001e |
| #define LPC214X_TRACEERR_REQABORTED 0x001f |
| |
| /* Trace interrupt codes */ |
| |
| #define LPC214X_TRACEINTID_USB 0x0001 |
| #define LPC214X_TRACEINTID_CLEARFEATURE 0x0002 |
| #define LPC214X_TRACEINTID_CONNECTCHG 0x0003 |
| #define LPC214X_TRACEINTID_CONNECTED 0x0004 |
| #define LPC214X_TRACEINTID_DEVGETSTATUS 0x0005 |
| #define LPC214X_TRACEINTID_DEVRESET 0x0006 |
| #define LPC214X_TRACEINTID_DEVSTAT 0x0007 |
| #define LPC214X_TRACEINTID_DISCONNECTED 0x0008 |
| #define LPC214X_TRACEINTID_DISPATCH 0x0009 |
| #define LPC214X_TRACEINTID_EP0IN 0x000a |
| #define LPC214X_TRACEINTID_EP0OUT 0x000b |
| #define LPC214X_TRACEINTID_EP0SETUP 0x000c |
| #define LPC214X_TRACEINTID_EPDMA 0x000d |
| #define LPC214X_TRACEINTID_EPFAST 0x000e |
| #define LPC214X_TRACEINTID_EPGETSTATUS 0x000f |
| #define LPC214X_TRACEINTID_EPIN 0x0010 |
| #define LPC214X_TRACEINTID_EPINQEMPTY 0x0011 |
| #define LPC214X_TRACEINTID_EP0INSETADDRESS 0x0012 |
| #define LPC214X_TRACEINTID_EPOUT 0x0013 |
| #define LPC214X_TRACEINTID_EPOUTQEMPTY 0x0014 |
| #define LPC214X_TRACEINTID_EP0SETUPSETADDRESS 0x0015 |
| #define LPC214X_TRACEINTID_EPRINT 0x0016 |
| #define LPC214X_TRACEINTID_EPSLOW 0x0017 |
| #define LPC214X_TRACEINTID_FRAME 0x0018 |
| #define LPC214X_TRACEINTID_GETCONFIG 0x0019 |
| #define LPC214X_TRACEINTID_GETSETDESC 0x001a |
| #define LPC214X_TRACEINTID_GETSETIF 0x001b |
| #define LPC214X_TRACEINTID_GETSTATUS 0x001c |
| #define LPC214X_TRACEINTID_IFGETSTATUS 0x001d |
| #define LPC214X_TRACEINTID_SETCONFIG 0x001e |
| #define LPC214X_TRACEINTID_SETFEATURE 0x001f |
| #define LPC214X_TRACEINTID_SUSPENDCHG 0x0020 |
| #define LPC214X_TRACEINTID_SYNCHFRAME 0x0021 |
| |
| /* Hardware interface *******************************************************/ |
| |
| /* Macros for testing the device status response */ |
| |
| #define DEVSTATUS_CONNECT(s) (((s)&USBDEV_DEVSTATUS_CONNECT)!=0) |
| #define DEVSTATUS_CONNCHG(s) (((s)&USBDEV_DEVSTATUS_CONNCHG)!=0) |
| #define DEVSTATUS_SUSPEND(s) (((s)&USBDEV_DEVSTATUS_SUSPEND)!=0) |
| #define DEVSTATUS_SUSPCHG(s) (((s)&USBDEV_DEVSTATUS_SUSPCHG)!=0) |
| #define DEVSTATUS_RESET(s) (((s)&USBDEV_DEVSTATUS_RESET)!=0) |
| |
| /* If this bit is set in the lpc214x_epread response, it means that the |
| * received packet was overwritten by a later setup packet (ep0 only). |
| */ |
| |
| #define LPC214X_READOVERRUN_BIT (0x80000000) |
| #define LPC214X_READOVERRUN(s) (((s) & LPC214X_READOVERRUN_BIT) != 0) |
| |
| /* USB RAM ***************************************************************** |
| * |
| * UBS_UDCA is is list of 32 pointers to DMA descriptors located at the |
| * beginning of USB RAM. Each pointer points to a DMA descriptor with |
| * associated DMA buffer. |
| */ |
| |
| #define USB_UDCA (uint32_t *)LPC214X_USBDEV_RAMBASE) |
| #define USB_USCASIZE (LPC214X_NPHYSENDPOINTS*sizeof(uint32_t)) |
| |
| /* Each descriptor must be aligned to a 128 address boundary */ |
| |
| #define USB_DDALIGNDOWN(a) ((a)&~0x7f) |
| #define USB_DDALIGNUP(a) USB_DDALIGNDOWN((a)+0x7f) |
| |
| #define USB_DDSIZE USB_DDALIGNDOWN((LPC214X_USBDEV_RAMSIZE-USB_USCASIZE)/CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS) |
| #define USB_DDESC ((struct lpc214x_dmadesc_s*)(LPC214X_USBDEV_RAMBASE+USB_USCASIZE)) |
| |
| #ifdef CONFIG_USBDEV_ISOCHRONOUS |
| # define USB_DDESCSIZE (5*sizeof(uint32_t)) |
| #else |
| # define USB_DDESCSIZE (4*sizeof(uint32_t)) |
| #endif |
| |
| /* Endpoints ****************************************************************/ |
| |
| /* Number of endpoints */ |
| |
| #define LPC214X_NLOGENDPOINTS (16) /* ep0-15 */ |
| #define LPC214X_NPHYSENDPOINTS (32) /* x2 for IN and OUT */ |
| |
| /* Odd physical endpoint numbers are IN; even are out */ |
| |
| #define LPC214X_EPPHYIN(epphy) (((epphy)&1)!=0) |
| #define LPC214X_EPPHYOUT(epphy) (((epphy)&1)==0) |
| |
| #define LPC214X_EPPHYIN2LOG(epphy) (((uint8_t)(epphy)>>1)|USB_DIR_IN) |
| #define LPC214X_EPPHYOUT2LOG(epphy) (((uint8_t)(epphy)>>1)|USB_DIR_OUT) |
| |
| /* Each endpoint has somewhat different characteristics */ |
| |
| #define LPC214X_EPALLSET (0xffffffff) /* All endpoints */ |
| #define LPC214X_EPOUTSET (0x55555555) /* Even phy endpoint numbers are OUT EPs */ |
| #define LPC214X_EPINSET (0xaaaaaaaa) /* Odd endpoint numbers are IN EPs */ |
| #define LPC214X_EPCTRLSET (0x00000003) /* EP0 IN/OUT are control endpoints */ |
| #define LPC214X_EPINTRSET (0x0c30c30c) /* Interrupt endpoints */ |
| #define LPC214X_EPBULKSET (0xf0c30c30) /* Bulk endpoints */ |
| #define LPC214X_EPISOCSET (0x030c30c0) /* Isochronous endpoints */ |
| #define LPC214X_EPDBLBUFFER (0xf3cf3cf0) /* Double buffered endpoints */ |
| |
| #define LPC214X_EP0MAXPACKET (64) /* EP0 max packet size (1-64) */ |
| #define LPC214X_BULKMAXPACKET (64) /* Bulk endpoint max packet (8/16/32/64) */ |
| #define LPC214X_INTRMAXPACKET (64) /* Interrupt endpoint max packet (1 to 64) */ |
| #define LPC214X_ISOCMAXPACKET (512) /* Actually 1..1023 */ |
| |
| /* EP0 status. EP0 transfers occur in a number of different contexts. A |
| * simple state machine is required to handle the various transfer complete |
| * interrupt responses. The following values are the various states: |
| */ |
| |
| /* INTERRUPT CAUSE */ |
| #define LPC214X_EP0REQUEST (0) /* Normal request handling */ |
| #define LPC214X_EP0STATUSIN (1) /* Status sent */ |
| #define LPC214X_EP0STATUSOUT (2) /* Status received */ |
| #define LPC214X_EP0SHORTWRITE (3) /* Short data sent with no request */ |
| #define LPC214X_EP0SHORTWRSENT (4) /* Short data write complete */ |
| #define LPC214X_EP0SETADDRESS (5) /* Set address received */ |
| #define LPC214X_EP0WRITEREQUEST (6) /* EP0 write request sent */ |
| |
| /* Request queue operations *************************************************/ |
| |
| #define lpc214x_rqempty(ep) ((ep)->head == NULL) |
| #define lpc214x_rqpeek(ep) ((ep)->head) |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| /* A container for a request so that the request make be retained in a list */ |
| |
| struct lpc214x_req_s |
| { |
| struct usbdev_req_s req; /* Standard USB request */ |
| struct lpc214x_req_s *flink; /* Supports a singly linked list */ |
| }; |
| |
| /* This is the internal representation of an endpoint */ |
| |
| struct lpc214x_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 lpc214x_ep_s. |
| */ |
| |
| struct usbdev_ep_s ep; /* Standard endpoint structure */ |
| |
| /* LPC214X-specific fields */ |
| |
| struct lpc214x_usbdev_s *dev; /* Reference to private driver data */ |
| struct lpc214x_req_s *head; /* Request list for this endpoint */ |
| struct lpc214x_req_s *tail; |
| uint8_t epphy; /* Physical EP address */ |
| uint8_t stalled:1; /* 1: Endpoint is stalled */ |
| uint8_t halted:1; /* 1: Endpoint feature halted */ |
| uint8_t txbusy:1; /* 1: TX endpoint FIFO full */ |
| uint8_t txnullpkt:1; /* Null packet needed at end of transfer */ |
| }; |
| |
| /* This represents a DMA descriptor */ |
| |
| #ifdef CONFIG_LPC214X_USBDEV_DMA |
| struct lpc214x_dmadesc_s |
| { |
| uint32_t nextdesc; /* Address of the next DMA descriptor in RAM */ |
| uint32_t config; /* Misc. bit encoded configuration information */ |
| uint32_t start; /* DMA start address */ |
| uint32_t status; /* Misc. bit encoded status information */ |
| #ifdef CONFIG_USBDEV_ISOCHRONOUS |
| uint32_t size; /* Isochronous packet size address */ |
| #endif |
| uint8_t buffer[USB_DDSIZE - USB_DDESCSIZE]; |
| }; |
| #endif |
| |
| /* This structure retains the state of the USB device controller */ |
| |
| struct lpc214x_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 structlpc214x_usbdev_s. |
| */ |
| |
| struct usbdev_s usbdev; |
| |
| /* The bound device class driver */ |
| |
| struct usbdevclass_driver_s *driver; |
| |
| /* LPC214X-specific fields */ |
| |
| uint8_t devstatus; /* Last response to device status command */ |
| uint8_t ep0state; /* State of certain EP0 operations */ |
| uint8_t paddr; /* Address assigned by SETADDRESS */ |
| 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 */ |
| uint32_t softprio; /* Bitset of high priority interrupts */ |
| uint32_t epavail; /* Bitset of available endpoints */ |
| #ifdef CONFIG_LPC214X_USBDEV_FRAME_INTERRUPT |
| uint32_t sof; /* Last start-of-frame */ |
| #endif |
| |
| /* Allocated DMA descriptor */ |
| |
| #ifdef CONFIG_LPC214X_USBDEV_DMA |
| struct lpc214x_dmadesc_s *dmadesc; |
| #endif |
| |
| /* The endpoint list */ |
| |
| struct lpc214x_ep_s eplist[LPC214X_NPHYSENDPOINTS]; |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| /* Register operations ******************************************************/ |
| |
| #ifdef CONFIG_LPC214X_USBDEV_REGDEBUG |
| static uint32_t lpc214x_getreg(uint32_t addr); |
| static void lpc214x_putreg(uint32_t val, uint32_t addr); |
| #else |
| # define lpc214x_getreg(addr) getreg32(addr) |
| # define lpc214x_putreg(val,addr) putreg32(val,addr) |
| #endif |
| |
| /* Command operations *******************************************************/ |
| |
| static uint32_t lpc214x_usbcmd(uint16_t cmd, uint8_t data); |
| |
| /* Request queue operations *************************************************/ |
| |
| static struct lpc214x_req_s *lpc214x_rqdequeue(struct lpc214x_ep_s *privep); |
| static void lpc214x_rqenqueue(struct lpc214x_ep_s *privep, |
| struct lpc214x_req_s *req); |
| |
| /* Low level data transfers and request operations **************************/ |
| |
| static void lpc214x_epwrite(uint8_t epphy, |
| const uint8_t *data, |
| uint32_t nbytes); |
| static int lpc214x_epread(uint8_t epphy, uint8_t *data, uint32_t nbytes); |
| static inline void lpc214x_abortrequest(struct lpc214x_ep_s *privep, |
| struct lpc214x_req_s *privreq, int16_t result); |
| static void lpc214x_reqcomplete(struct lpc214x_ep_s *privep, int16_t result); |
| static int lpc214x_wrrequest(struct lpc214x_ep_s *privep); |
| static int lpc214x_rdrequest(struct lpc214x_ep_s *privep); |
| static void lpc214x_cancelrequests(struct lpc214x_ep_s *privep); |
| |
| /* Interrupt handling *******************************************************/ |
| |
| static struct |
| lpc214x_ep_s *lpc214x_epfindbyaddr(struct lpc214x_usbdev_s *priv, |
| uint16_t eplog); |
| static void lpc214x_eprealize(struct lpc214x_ep_s *privep, bool prio, |
| uint32_t packetsize); |
| static uint8_t lpc214x_epclrinterrupt(uint8_t epphy); |
| static inline void lpc214x_ep0configure(struct lpc214x_usbdev_s *priv); |
| #ifdef CONFIG_LPC214X_USBDEV_DMA |
| static inline void lpc214x_dmareset(uint32_t enable); |
| #endif |
| static void lpc214x_usbreset(struct lpc214x_usbdev_s *priv); |
| static void lpc214x_dispatchrequest(struct lpc214x_usbdev_s *priv, |
| const struct usb_ctrlreq_s *ctrl); |
| static inline void lpc214x_ep0setup(struct lpc214x_usbdev_s *priv); |
| static inline |
| void lpc214x_ep0dataoutinterrupt(struct lpc214x_usbdev_s *priv); |
| static inline |
| void lpc214x_ep0dataininterrupt(struct lpc214x_usbdev_s *priv); |
| static int lpc214x_usbinterrupt(int irq, void *context, void *arg); |
| |
| #ifdef CONFIG_LPC214X_USBDEV_DMA |
| static int lpc214x_dmasetup(struct lpc214x_usbdev_s *priv, uint8_t epphy, |
| uint32_t epmaxsize, uint32_t nbytes, uint32_t *isocpacket, |
| bool isochronous); |
| static void lpc214x_dmarestart(uint8_t epphy, uint32_t descndx); |
| static void lpc214x_dmadisable(uint8_t epphy); |
| #endif /* CONFIG_LPC214X_USBDEV_DMA */ |
| |
| /* Endpoint operations ******************************************************/ |
| |
| static int lpc214x_epconfigure(struct usbdev_ep_s *ep, |
| const struct usb_epdesc_s *desc, bool last); |
| static int lpc214x_epdisable(struct usbdev_ep_s *ep); |
| static struct usbdev_req_s *lpc214x_epallocreq(struct usbdev_ep_s *ep); |
| static void lpc214x_epfreereq(struct usbdev_ep_s *ep, |
| struct usbdev_req_s *); |
| #ifdef CONFIG_USBDEV_DMA |
| static void *lpc214x_epallocbuffer(struct usbdev_ep_s *ep, |
| uint16_t nbytes); |
| static void lpc214x_epfreebuffer(struct usbdev_ep_s *ep, void *buf); |
| #endif |
| static int lpc214x_epsubmit(struct usbdev_ep_s *ep, |
| struct usbdev_req_s *req); |
| static int lpc214x_epcancel(struct usbdev_ep_s *ep, |
| struct usbdev_req_s *req); |
| static int lpc214x_epstall(struct usbdev_ep_s *ep, bool resume); |
| |
| /* USB device controller operations *****************************************/ |
| |
| static struct usbdev_ep_s *lcp214x_allocep(struct usbdev_s *dev, |
| uint8_t epno, bool in, uint8_t eptype); |
| static void lpc214x_freeep(struct usbdev_s *dev, |
| struct usbdev_ep_s *ep); |
| static int lpc214x_getframe(struct usbdev_s *dev); |
| static int lpc214x_wakeup(struct usbdev_s *dev); |
| static int lpc214x_selfpowered(struct usbdev_s *dev, bool selfpowered); |
| static int lpc214x_pullup(struct usbdev_s *dev, bool enable); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* Since there is only a single USB interface, all status information can be |
| * be simply retained in a single global instance. |
| */ |
| |
| static struct lpc214x_usbdev_s g_usbdev; |
| |
| static const struct usbdev_epops_s g_epops = |
| { |
| .configure = lpc214x_epconfigure, |
| .disable = lpc214x_epdisable, |
| .allocreq = lpc214x_epallocreq, |
| .freereq = lpc214x_epfreereq, |
| #ifdef CONFIG_USBDEV_DMA |
| .allocbuffer = lpc214x_epallocbuffer, |
| .freebuffer = lpc214x_epfreebuffer, |
| #endif |
| .submit = lpc214x_epsubmit, |
| .cancel = lpc214x_epcancel, |
| .stall = lpc214x_epstall, |
| }; |
| |
| static const struct usbdev_ops_s g_devops = |
| { |
| .allocep = lcp214x_allocep, |
| .freeep = lpc214x_freeep, |
| .getframe = lpc214x_getframe, |
| .wakeup = lpc214x_wakeup, |
| .selfpowered = lpc214x_selfpowered, |
| .pullup = lpc214x_pullup, |
| }; |
| |
| /**************************************************************************** |
| * Public Data |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: lpc214x_getreg |
| * |
| * Description: |
| * Get the contents of an LPC214x register |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_LPC214X_USBDEV_REGDEBUG |
| static uint32_t lpc214x_getreg(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 register 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) |
| { |
| uinfo("...\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 */ |
| |
| uinfo("[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 */ |
| |
| uinfo("%08x->%08x\n", addr, val); |
| return val; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: lpc214x_putreg |
| * |
| * Description: |
| * Set the contents of an LPC214x register to a value |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_LPC214X_USBDEV_REGDEBUG |
| static void lpc214x_putreg(uint32_t val, uint32_t addr) |
| { |
| /* Show the register value being written */ |
| |
| uinfo("%08x<-%08x\n", addr, val); |
| |
| /* Write the value */ |
| |
| putreg32(val, addr); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: lpc214x_usbcmd |
| * |
| * Description: |
| * Transmit commands to the USB engine |
| * |
| ****************************************************************************/ |
| |
| static uint32_t lpc214x_usbcmd(uint16_t cmd, uint8_t data) |
| { |
| irqstate_t flags; |
| uint32_t tmp = 0; |
| |
| /* Disable interrupt and clear CDFULL and CCEMPTY interrupt status */ |
| |
| flags = enter_critical_section(); |
| lpc214x_putreg(USBDEV_DEVINT_CDFULL | USBDEV_DEVINT_CCEMTY, |
| LPC214X_USBDEV_DEVINTCLR); |
| |
| /* Load command + WR in command code register */ |
| |
| lpc214x_putreg(((cmd & 0xff) << 16) + CMD_USB_CMDWR, |
| LPC214X_USBDEV_CMDCODE); |
| |
| /* Wait until the command register is empty |
| * (CCEMPTY != 0, command is accepted) |
| */ |
| |
| while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & |
| USBDEV_DEVINT_CCEMTY) == 0); |
| |
| /* Clear command register empty (CCEMPTY) interrupt */ |
| |
| lpc214x_putreg(USBDEV_DEVINT_CCEMTY, LPC214X_USBDEV_DEVINTCLR); |
| |
| /* Determine next phase of the command */ |
| |
| switch (cmd) |
| { |
| /* Write operations (1 byte of data) */ |
| |
| case CMD_USB_DEV_SETADDRESS: |
| case CMD_USB_DEV_CONFIG: |
| case CMD_USB_DEV_SETMODE: |
| case CMD_USB_DEV_SETSTATUS: |
| { |
| /* Send data + WR and wait for CCEMPTY */ |
| |
| lpc214x_putreg((data << 16) + CMD_USB_DATAWR, |
| LPC214X_USBDEV_CMDCODE); |
| while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & |
| USBDEV_DEVINT_CCEMTY) == 0); |
| } |
| break; |
| |
| /* 16 bit read operations */ |
| |
| case CMD_USB_DEV_READFRAMENO: |
| case CMD_USB_DEV_READTESTREG: |
| { |
| /* Send command code + RD and wait for CDFULL */ |
| |
| lpc214x_putreg((cmd << 16) + CMD_USB_DATARD, |
| LPC214X_USBDEV_CMDCODE); |
| while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & |
| USBDEV_DEVINT_CDFULL) == 0); |
| |
| /* Clear CDFULL and read LS data */ |
| |
| lpc214x_putreg(USBDEV_DEVINT_CDFULL, LPC214X_USBDEV_DEVINTCLR); |
| tmp = lpc214x_getreg(LPC214X_USBDEV_CMDDATA); |
| |
| /* Send command code + RD and wait for CDFULL */ |
| |
| lpc214x_putreg((cmd << 16) + CMD_USB_DATARD, |
| LPC214X_USBDEV_CMDCODE); |
| while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & |
| USBDEV_DEVINT_CDFULL) == 0); |
| |
| /* Read MS data */ |
| |
| tmp |= lpc214x_getreg(LPC214X_USBDEV_CMDDATA) << 8; |
| } |
| break; |
| |
| /* 8-bit read operations */ |
| |
| case CMD_USB_DEV_GETSTATUS: |
| case CMD_USB_DEV_GETERRORCODE: |
| case CMD_USB_DEV_READERRORSTATUS: |
| case CMD_USB_EP_CLRBUFFER: |
| { |
| /* Send command code + RD and wait for CDFULL */ |
| |
| lpc214x_putreg((cmd << 16) + CMD_USB_DATARD, |
| LPC214X_USBDEV_CMDCODE); |
| while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & |
| USBDEV_DEVINT_CDFULL) == 0); |
| |
| /* Read data */ |
| |
| tmp = lpc214x_getreg(LPC214X_USBDEV_CMDDATA); |
| } |
| break; |
| |
| /* No data transfer */ |
| |
| case CMD_USB_EP_VALIDATEBUFFER: |
| break; |
| |
| default: |
| switch (cmd & 0x1e0) |
| { |
| case CMD_USB_EP_SELECT: |
| case CMD_USB_EP_SELECTCLEAR: |
| { |
| /* Send command code + RD and wait for CDFULL */ |
| |
| lpc214x_putreg((cmd << 16) + CMD_USB_DATARD, |
| LPC214X_USBDEV_CMDCODE); |
| while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & |
| USBDEV_DEVINT_CDFULL) == 0); |
| |
| /* Read data */ |
| |
| tmp = lpc214x_getreg(LPC214X_USBDEV_CMDDATA); |
| } |
| break; |
| |
| case CMD_USB_EP_SETSTATUS: |
| { |
| /* Send data + RD and wait for CCEMPTY */ |
| |
| lpc214x_putreg((data << 16) + CMD_USB_DATAWR, |
| LPC214X_USBDEV_CMDCODE); |
| while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & |
| USBDEV_DEVINT_CCEMTY) == 0); |
| } |
| break; |
| |
| default: |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDCMD), 0); |
| break; |
| } |
| break; |
| } |
| |
| /* Restore the interrupt flags */ |
| |
| leave_critical_section(flags); |
| return tmp; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_rqdequeue |
| * |
| * Description: |
| * Remove a request from an endpoint request queue |
| * |
| ****************************************************************************/ |
| |
| static struct lpc214x_req_s *lpc214x_rqdequeue(struct lpc214x_ep_s *privep) |
| { |
| struct lpc214x_req_s *ret = privep->head; |
| |
| if (ret) |
| { |
| privep->head = ret->flink; |
| if (!privep->head) |
| { |
| privep->tail = NULL; |
| } |
| |
| ret->flink = NULL; |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_rqenqueue |
| * |
| * Description: |
| * Add a request from an endpoint request queue |
| * |
| ****************************************************************************/ |
| |
| static void lpc214x_rqenqueue(struct lpc214x_ep_s *privep, |
| struct lpc214x_req_s *req) |
| { |
| req->flink = NULL; |
| if (!privep->head) |
| { |
| privep->head = req; |
| privep->tail = req; |
| } |
| else |
| { |
| privep->tail->flink = req; |
| privep->tail = req; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_epwrite |
| * |
| * Description: |
| * Endpoint write (IN) |
| * |
| ****************************************************************************/ |
| |
| static void lpc214x_epwrite(uint8_t epphy, |
| const uint8_t *data, uint32_t nbytes) |
| { |
| uint32_t value; |
| bool aligned = (((uint32_t)data & 3) == 0); |
| |
| /* Set the write enable bit for this physical EP address. Bits 2-5 are |
| * the logical endpoint number (0-15) |
| */ |
| |
| lpc214x_putreg(((epphy << 1) & LPC214X_USBCTRL_EPMASK) | |
| LPC214X_USBCTRL_WREN, LPC214X_USBDEV_CTRL); |
| |
| /* Set the transmit packet length (nbytes must be less than 2048) */ |
| |
| lpc214x_putreg(nbytes, LPC214X_USBDEV_TXPLEN); |
| |
| /* Transfer the packet data */ |
| |
| do |
| { |
| /* Zero length packets are a special case */ |
| |
| if (nbytes) |
| { |
| if (aligned) |
| { |
| value = *(uint32_t *)data; |
| } |
| else |
| { |
| value = (uint32_t)data[0] | ((uint32_t)data[1] << 8) | |
| ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24); |
| } |
| |
| lpc214x_putreg(value, LPC214X_USBDEV_TXDATA); |
| data += 4; |
| } |
| else |
| { |
| /* Zero length packet */ |
| |
| lpc214x_putreg(0, LPC214X_USBDEV_TXDATA); |
| } |
| } |
| while ((lpc214x_getreg(LPC214X_USBDEV_CTRL) & LPC214X_USBCTRL_WREN) != 0); |
| |
| /* Done */ |
| |
| lpc214x_putreg(0, LPC214X_USBDEV_CTRL); |
| lpc214x_usbcmd(CMD_USB_EP_SELECT | epphy, 0); |
| lpc214x_usbcmd(CMD_USB_EP_VALIDATEBUFFER, 0); |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_epread |
| * |
| * Description: |
| * Endpoint read (OUT) |
| * |
| ****************************************************************************/ |
| |
| static int lpc214x_epread(uint8_t epphy, uint8_t *data, uint32_t nbytes) |
| { |
| uint32_t pktlen; |
| uint32_t result; |
| uint32_t value; |
| uint8_t aligned = 0; |
| |
| /* If data is NULL, then we are being asked to read but discard the data. |
| * For most cases, the resulting buffer will be aligned and we will be |
| * able to do faster 32-bit transfers. |
| */ |
| |
| if (data) |
| { |
| if (((uint32_t)data & 3) == 0) |
| { |
| aligned = 1; |
| } |
| else |
| { |
| aligned = 2; |
| } |
| } |
| |
| /* Set the read enable bit for this physical EP address. Bits 2-5 are |
| * the logical endpoint number (0-15). |
| */ |
| |
| lpc214x_putreg(((epphy << 1) & LPC214X_USBCTRL_EPMASK) | |
| LPC214X_USBCTRL_RDEN, LPC214X_USBDEV_CTRL); |
| |
| /* Wait for packet buffer ready for reading */ |
| |
| while ((lpc214x_getreg(LPC214X_USBDEV_RXPLEN) & |
| USBDEV_RXPLEN_PKTRDY) == 0); |
| |
| /* Get the number of bytes of data to be read */ |
| |
| pktlen = lpc214x_getreg(LPC214X_USBDEV_RXPLEN) & |
| USBDEV_RXPLEN_PKTLENGTH; |
| |
| /* Read data from input buffer while read data is valid (DV) */ |
| |
| while ((lpc214x_getreg(LPC214X_USBDEV_RXPLEN) & |
| USBDEV_RXPLEN_DV) != 0) |
| { |
| value = lpc214x_getreg(LPC214X_USBDEV_RXDATA); |
| if (aligned == 1) |
| { |
| *(uint32_t *)data = value; |
| data += 4; |
| } |
| else if (aligned == 2) |
| { |
| *data++ = (uint8_t)value; |
| *data++ = (uint8_t)(value >> 8); |
| *data++ = (uint8_t)(value >> 16); |
| *data++ = (uint8_t)(value >> 24); |
| } |
| } |
| |
| /* Done */ |
| |
| lpc214x_putreg(0, LPC214X_USBDEV_CTRL); |
| lpc214x_usbcmd(CMD_USB_EP_SELECT | epphy, 0); |
| result = lpc214x_usbcmd(CMD_USB_EP_CLRBUFFER, 0); |
| |
| /* The packet overrun bit in the clear buffer response is applicable |
| * only on EP0 transfers. If set it means that the received packet |
| * was overwritten by a later setup packet. |
| */ |
| |
| if (epphy == LPC214X_EP0_OUT && (result & CMD_USB_CLRBUFFER_PO) != 0) |
| { |
| /* Pass this information in bit 31 */ |
| |
| pktlen |= LPC214X_READOVERRUN_BIT; |
| } |
| |
| return pktlen; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_abortrequest |
| * |
| * Description: |
| * Discard a request |
| * |
| ****************************************************************************/ |
| |
| static inline void lpc214x_abortrequest(struct lpc214x_ep_s *privep, |
| struct lpc214x_req_s *privreq, |
| int16_t result) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_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: lpc214x_reqcomplete |
| * |
| * Description: |
| * Handle termination of the request at the head of the endpoint request |
| * queue. |
| * |
| ****************************************************************************/ |
| |
| static void lpc214x_reqcomplete(struct lpc214x_ep_s *privep, int16_t result) |
| { |
| struct lpc214x_req_s *privreq; |
| int stalled = privep->stalled; |
| irqstate_t flags; |
| |
| /* Remove the completed request at the head of the endpoint request list */ |
| |
| flags = enter_critical_section(); |
| privreq = lpc214x_rqdequeue(privep); |
| leave_critical_section(flags); |
| |
| if (privreq) |
| { |
| /* If endpoint 0, temporarily reflect the state of protocol stalled |
| * in the callback. |
| */ |
| |
| if (privep->epphy == LPC214X_EP0_IN) |
| { |
| privep->stalled = privep->dev->stalled; |
| } |
| |
| /* 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: lpc214x_wrrequest |
| * |
| * Description: |
| * Send from the next queued write request |
| * |
| ****************************************************************************/ |
| |
| static int lpc214x_wrrequest(struct lpc214x_ep_s *privep) |
| { |
| struct lpc214x_req_s *privreq; |
| uint8_t *buf; |
| int nbytes; |
| int bytesleft; |
| |
| /* Check the request from the head of the endpoint request queue */ |
| |
| privreq = lpc214x_rqpeek(privep); |
| if (!privreq) |
| { |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPINQEMPTY), 0); |
| return OK; |
| } |
| |
| uinfo("epphy=%d req=%p: len=%zu xfrd=%zu nullpkt=%d\n", |
| privep->epphy, privreq, privreq->req.len, privreq->req.xfrd, |
| privep->txnullpkt); |
| |
| /* Ignore any attempt to send a zero length packet on anything but EP0IN */ |
| |
| if (privreq->req.len == 0) |
| { |
| if (privep->epphy == LPC214X_EP0_IN) |
| { |
| lpc214x_epwrite(LPC214X_EP0_IN, NULL, 0); |
| } |
| else |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EPINNULLPACKET), 0); |
| } |
| |
| /* In any event, the request is complete */ |
| |
| lpc214x_reqcomplete(privep, OK); |
| return OK; |
| } |
| |
| /* Otherwise send the data in the packet (in the DMA on case, we |
| * may be resuming transfer already in progress. |
| */ |
| #warning REVISIT... If the EP supports double buffering, then we can do better |
| |
| /* 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) |
| { |
| /* Indicate that there is data in the TX FIFO. This will be cleared |
| * when the EPIN interrupt is received |
| */ |
| |
| privep->txbusy = 1; |
| |
| /* 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; |
| lpc214x_epwrite(privep->epphy, buf, nbytes); |
| |
| /* 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; |
| lpc214x_reqcomplete(privep, OK); |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_rdrequest |
| * |
| * Description: |
| * Receive to the next queued read request |
| * |
| ****************************************************************************/ |
| |
| static int lpc214x_rdrequest(struct lpc214x_ep_s *privep) |
| { |
| struct lpc214x_req_s *privreq; |
| uint8_t *buf; |
| int nbytesread; |
| |
| /* Check the request from the head of the endpoint request queue */ |
| |
| privreq = lpc214x_rqpeek(privep); |
| if (!privreq) |
| { |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPOUTQEMPTY), 0); |
| return OK; |
| } |
| |
| uinfo("len=%zu xfrd=%zu nullpkt=%d\n", |
| privreq->req.len, privreq->req.xfrd, privep->txnullpkt); |
| |
| /* Ignore any attempt to receive a zero length packet */ |
| |
| if (privreq->req.len == 0) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EPOUTNULLPACKET), 0); |
| lpc214x_reqcomplete(privep, OK); |
| return OK; |
| } |
| |
| usbtrace(TRACE_READ(privep->epphy), privreq->req.xfrd); |
| |
| /* Receive the next packet */ |
| |
| buf = privreq->req.buf + privreq->req.xfrd; |
| nbytesread = lpc214x_epread(privep->epphy, buf, privep->ep.maxpacket); |
| if (nbytesread < 0) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_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.xfrd >= privreq->req.len || |
| nbytesread < privep->ep.maxpacket) |
| { |
| usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd); |
| lpc214x_reqcomplete(privep, OK); |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_cancelrequests |
| * |
| * Description: |
| * Cancel all pending requests for an endpoint |
| * |
| ****************************************************************************/ |
| |
| static void lpc214x_cancelrequests(struct lpc214x_ep_s *privep) |
| { |
| while (!lpc214x_rqempty(privep)) |
| { |
| usbtrace(TRACE_COMPLETE(privep->epphy), |
| (lpc214x_rqpeek(privep))->req.xfrd); |
| lpc214x_reqcomplete(privep, -ESHUTDOWN); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_epfindbyaddr |
| * |
| * Description: |
| * Find the physical endpoint structure corresponding to a logic endpoint |
| * address |
| * |
| ****************************************************************************/ |
| |
| static struct |
| lpc214x_ep_s *lpc214x_epfindbyaddr(struct lpc214x_usbdev_s *priv, |
| uint16_t eplog) |
| { |
| struct lpc214x_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 < LPC214X_NPHYSENDPOINTS; 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: lpc214x_eprealize |
| * |
| * Description: |
| * Enable or disable an endpoint |
| * |
| ****************************************************************************/ |
| |
| static void lpc214x_eprealize(struct lpc214x_ep_s *privep, |
| bool prio, uint32_t packetsize) |
| { |
| struct lpc214x_usbdev_s *priv = privep->dev; |
| uint32_t mask; |
| uint32_t reg; |
| |
| /* Initialize endpoint software priority */ |
| |
| mask = 1 << privep->epphy; |
| if (prio) |
| { |
| priv->softprio = priv->softprio | mask; |
| } |
| else |
| { |
| priv->softprio = priv->softprio & ~mask; |
| } |
| |
| /* Clear realize interrupt bit */ |
| |
| lpc214x_putreg(USBDEV_DEVINT_EPRLZED, LPC214X_USBDEV_DEVINTCLR); |
| |
| /* Realize the endpoint */ |
| |
| reg = lpc214x_getreg(LPC214X_USBDEV_REEP); |
| reg |= (1 << privep->epphy); |
| lpc214x_putreg(reg, LPC214X_USBDEV_REEP); |
| |
| /* Set endpoint maximum packet size */ |
| |
| lpc214x_putreg(privep->epphy, LPC214X_USBDEV_EPIND); |
| lpc214x_putreg(packetsize, LPC214X_USBDEV_MAXPSIZE); |
| |
| /* Wait for Realize complete */ |
| |
| while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & |
| USBDEV_DEVINT_EPRLZED) == 0); |
| |
| /* Clear realize interrupt bit */ |
| |
| lpc214x_putreg(USBDEV_DEVINT_EPRLZED, LPC214X_USBDEV_DEVINTCLR); |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_epclrinterrupt |
| * |
| * Description: |
| * Clear the EP interrupt flag and return the current EP status |
| * |
| ****************************************************************************/ |
| |
| static uint8_t lpc214x_epclrinterrupt(uint8_t epphy) |
| { |
| /* Clear the endpoint interrupt */ |
| |
| lpc214x_putreg(1 << epphy, LPC214X_USBDEV_EPINTCLR); |
| |
| /* Wait for data in the command data register */ |
| |
| while ((lpc214x_getreg(LPC214X_USBDEV_DEVINTST) & |
| USBDEV_DEVINT_CDFULL) == 0); |
| |
| /* Return the value of the command data register */ |
| |
| return lpc214x_getreg(LPC214X_USBDEV_CMDDATA); |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_ep0configure |
| * |
| * Description: |
| * Configure endpoint 0 |
| * |
| ****************************************************************************/ |
| |
| static inline void lpc214x_ep0configure(struct lpc214x_usbdev_s *priv) |
| { |
| uint32_t inten; |
| |
| /* EndPoint 0 initialization */ |
| |
| lpc214x_eprealize(&priv->eplist[LPC214X_CTRLEP_OUT], |
| 0, CONFIG_USBDEV_EP0_MAXSIZE); |
| lpc214x_eprealize(&priv->eplist[LPC214X_CTRLEP_IN], |
| 1, CONFIG_USBDEV_EP0_MAXSIZE); |
| |
| /* Enable EP0 interrupts (not DMA) */ |
| |
| inten = lpc214x_getreg(LPC214X_USBDEV_EPINTEN); |
| inten |= 3; /* EP0 Rx and Tx */ |
| lpc214x_putreg(inten, LPC214X_USBDEV_EPINTEN); |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_dmareset |
| * |
| * Description: Reset USB DMA |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_LPC214X_USBDEV_DMA |
| static inline void lpc214x_dmareset(uint32_t enable) |
| { |
| int i; |
| |
| /* Disable All DMA interrupts */ |
| |
| lpc214x_putreg(0, LPC214X_USBDEV_DMAINTEN); |
| |
| /* DMA Disable */ |
| |
| lpc214x_putreg(0xffffffff, LPC214X_USBDEV_EPDMADIS); |
| |
| /* DMA Request clear */ |
| |
| putreq32(0xffffffff, LPC214X_USBDEV_DMARCLR); |
| |
| /* End of Transfer Interrupt Clear */ |
| |
| putreq32(0xffffffff, LPC214X_USBDEV_EOTINTCLR); |
| |
| /* New DD Request Interrupt Clear */ |
| |
| putreq32(0xffffffff, LPC214X_USBDEV_NDDRINTCLR); |
| |
| /* System Error Interrupt Clear */ |
| |
| putreq32(0xffffffff, LPC214X_USBDEV_SYSERRINTCLR); |
| |
| /* Nullify all pointers in the UDCA */ |
| |
| for (i = 0; i < LPC214X_NPHYSENDPOINTS; ++i) |
| { |
| USB_UDCA[i] = NULL; |
| } |
| |
| /* Set USB UDCA Head register */ |
| |
| lpc214x_putreg((uint32_t)USB_UDCA, LPC214X_USBDEV_UDCAH); |
| |
| /* Invalidate all DMA descriptors */ |
| |
| for (i = 0; i < CONFIG_LPC214X_USBDEV_NDMADESCRIPTORS; ++i) |
| { |
| memset(&USB_DDESC[i], 0, USB_DDESCSIZE); |
| } |
| |
| /* Enable DMA interrupts */ |
| |
| lpc214x_putreg(enable, LPC214X_USBDEV_DMAINTEN); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: lpc214x_usbreset |
| * |
| * Description: |
| * Reset Usb engine |
| * |
| ****************************************************************************/ |
| |
| static void lpc214x_usbreset(struct lpc214x_usbdev_s *priv) |
| { |
| int epphy; |
| |
| /* Disable all endpoint interrupts */ |
| |
| lpc214x_putreg(0, LPC214X_USBDEV_EPINTEN); |
| |
| /* Frame is Hp interrupt */ |
| |
| lpc214x_putreg(1, LPC214X_USBDEV_DEVINTPRI); |
| |
| /* Clear all pending interrupts */ |
| |
| lpc214x_putreg(0xffffffff, LPC214X_USBDEV_EPINTCLR); |
| lpc214x_putreg(0xffffffff, LPC214X_USBDEV_DEVINTCLR); |
| |
| /* Periperhal address is needed */ |
| |
| priv->paddrset = 0; |
| |
| /* Reset endpoints */ |
| |
| for (epphy = 0; epphy < LPC214X_NPHYSENDPOINTS; epphy++) |
| { |
| struct lpc214x_ep_s *privep = &priv->eplist[epphy]; |
| |
| lpc214x_cancelrequests(privep); |
| |
| /* Reset endpoint status */ |
| |
| privep->stalled = false; |
| } |
| |
| /* Tell the class driver that we are disconnected. The class |
| * driver should then accept any new configurations. |
| */ |
| |
| if (priv->driver) |
| { |
| CLASS_DISCONNECT(priv->driver, &priv->usbdev); |
| } |
| |
| /* Endpoints not yet configured */ |
| |
| lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 0); |
| |
| /* EndPoint 0 initialization */ |
| |
| lpc214x_ep0configure(priv); |
| |
| /* Enable End_of_Transfer_Interrupt and System_Error_Interrupt USB DMA |
| * interrupts |
| */ |
| |
| #ifdef CONFIG_LPC214X_USBDEV_DMA |
| lpc214x_dmareset(CONFIG_LPC214X_USBDEV_DMAINTMASK); |
| #endif |
| |
| /* Enable Device interrupts */ |
| |
| lpc214x_putreg(USB_SLOW_INT | USB_DEVSTATUS_INT | USB_FAST_INT | |
| USB_FRAME_INT | USB_ERROR_INT, |
| LPC214X_USBDEV_DEVINTEN); |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_dispatchrequest |
| * |
| * Description: |
| * Provide unhandled setup actions to the class driver. This is logically |
| * part of the USB interrupt handler. |
| * |
| ****************************************************************************/ |
| |
| static void lpc214x_dispatchrequest(struct lpc214x_usbdev_s *priv, |
| const struct usb_ctrlreq_s *ctrl) |
| { |
| int ret; |
| |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_DISPATCH), 0); |
| if (priv && priv->driver) |
| { |
| /* Forward to the control request to the class driver implementation */ |
| |
| ret = CLASS_SETUP(priv->driver, &priv->usbdev, ctrl, NULL, 0); |
| if (ret < 0) |
| { |
| /* Stall on failure */ |
| |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_DISPATCHSTALL), 0); |
| priv->stalled = 1; |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_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 lpc214x_ep0setup(struct lpc214x_usbdev_s *priv) |
| { |
| struct lpc214x_ep_s *ep0 = &priv->eplist[LPC214X_EP0_OUT]; |
| struct lpc214x_ep_s *privep; |
| struct lpc214x_req_s *privreq = lpc214x_rqpeek(ep0); |
| struct usb_ctrlreq_s ctrl; |
| uint16_t value; |
| uint16_t index; |
| uint16_t len; |
| uint8_t response[2]; |
| int ret; |
| |
| /* Starting a control request? */ |
| |
| if (priv->usbdev.speed == USB_SPEED_UNKNOWN) |
| { |
| priv->usbdev.speed = USB_SPEED_FULL; |
| lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 1); |
| } |
| |
| /* Terminate any pending requests */ |
| |
| while (!lpc214x_rqempty(ep0)) |
| { |
| int16_t result = OK; |
| if (privreq->req.xfrd != privreq->req.len) |
| { |
| result = -EPROTO; |
| } |
| |
| usbtrace(TRACE_COMPLETE(ep0->epphy), privreq->req.xfrd); |
| lpc214x_reqcomplete(ep0, result); |
| } |
| |
| /* Assume NOT stalled */ |
| |
| ep0->stalled = 0; |
| priv->stalled = 0; |
| |
| /* Read EP0 data */ |
| |
| ret = lpc214x_epread(LPC214X_EP0_OUT, |
| (uint8_t *)&ctrl, USB_SIZEOF_CTRLREQ); |
| if (ret <= 0) |
| { |
| return; |
| } |
| |
| /* And extract the little-endian 16-bit values to host order */ |
| |
| value = GETUINT16(ctrl.value); |
| index = GETUINT16(ctrl.index); |
| len = GETUINT16(ctrl.len); |
| |
| uinfo("type=%02x req=%02x value=%04x index=%04x len=%04x\n", |
| ctrl.type, ctrl.req, value, index, len); |
| |
| /* Dispatch any non-standard requests */ |
| |
| if ((ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD) |
| { |
| lpc214x_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 |
| */ |
| |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_GETSTATUS), 0); |
| if (!priv->paddrset || len != 2 || |
| (ctrl.type & USB_REQ_DIR_IN) == 0 || value != 0) |
| { |
| priv->stalled = 1; |
| } |
| else |
| { |
| switch (ctrl.type & USB_REQ_RECIPIENT_MASK) |
| { |
| case USB_REQ_RECIPIENT_ENDPOINT: |
| { |
| usbtrace(TRACE_INTDECODE( |
| LPC214X_TRACEINTID_EPGETSTATUS), 0); |
| privep = lpc214x_epfindbyaddr(priv, index); |
| if (!privep) |
| { |
| usbtrace(TRACE_DEVERROR( |
| LPC214X_TRACEERR_BADEPGETSTATUS), 0); |
| priv->stalled = 1; |
| } |
| else |
| { |
| if ((lpc214x_usbcmd(CMD_USB_EP_SELECT | |
| privep->epphy, 0) & |
| CMD_USB_EPSELECT_ST) != 0) |
| { |
| response[0] = 1; /* Stalled */ |
| } |
| else |
| { |
| response[0] = 0; /* Not stalled */ |
| } |
| |
| response[1] = 0; |
| lpc214x_epwrite(LPC214X_EP0_IN, response, 2); |
| priv->ep0state = LPC214X_EP0SHORTWRITE; |
| } |
| } |
| break; |
| |
| case USB_REQ_RECIPIENT_DEVICE: |
| { |
| if (index == 0) |
| { |
| usbtrace(TRACE_INTDECODE( |
| LPC214X_TRACEINTID_DEVGETSTATUS), 0); |
| |
| /* Features: Remote Wakeup=YES; selfpowered=? */ |
| |
| response[0] = (priv->selfpowered << |
| USB_FEATURE_SELFPOWERED) | |
| (1 << USB_FEATURE_REMOTEWAKEUP); |
| response[1] = 0; |
| lpc214x_epwrite(LPC214X_EP0_IN, response, 2); |
| priv->ep0state = LPC214X_EP0SHORTWRITE; |
| } |
| else |
| { |
| usbtrace(TRACE_DEVERROR( |
| LPC214X_TRACEERR_BADDEVGETSTATUS), 0); |
| priv->stalled = 1; |
| } |
| } |
| break; |
| |
| case USB_REQ_RECIPIENT_INTERFACE: |
| { |
| usbtrace(TRACE_INTDECODE( |
| LPC214X_TRACEINTID_IFGETSTATUS), 0); |
| response[0] = 0; |
| response[1] = 0; |
| lpc214x_epwrite(LPC214X_EP0_IN, response, 2); |
| priv->ep0state = LPC214X_EP0SHORTWRITE; |
| } |
| break; |
| |
| default: |
| { |
| usbtrace(TRACE_DEVERROR( |
| LPC214X_TRACEERR_BADGETSTATUS), 0); |
| 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 |
| */ |
| |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_CLEARFEATURE), 0); |
| if ((ctrl.type & USB_REQ_RECIPIENT_MASK) != |
| USB_REQ_RECIPIENT_ENDPOINT) |
| { |
| lpc214x_dispatchrequest(priv, &ctrl); |
| } |
| else if (priv->paddrset != 0 && value == |
| USB_FEATURE_ENDPOINTHALT && len == 0 && |
| (privep = lpc214x_epfindbyaddr(priv, index)) != NULL) |
| { |
| privep->halted = 0; |
| lpc214x_epstall(&privep->ep, true); |
| lpc214x_epwrite(LPC214X_EP0_IN, NULL, 0); |
| priv->ep0state = LPC214X_EP0STATUSIN; |
| } |
| else |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADCLEARFEATURE), 0); |
| 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 |
| */ |
| |
| usbtrace(TRACE_INTDECODE(LPC214X_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) |
| { |
| lpc214x_dispatchrequest(priv, &ctrl); |
| } |
| else if (priv->paddrset != 0 && value == USB_FEATURE_ENDPOINTHALT && |
| len == 0 && (privep = lpc214x_epfindbyaddr(priv, index)) != |
| NULL) |
| { |
| privep->halted = 1; |
| lpc214x_epwrite(LPC214X_EP0_IN, NULL, 0); |
| priv->ep0state = LPC214X_EP0STATUSIN; |
| } |
| else |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADSETFEATURE), 0); |
| priv->stalled = 1; |
| } |
| } |
| break; |
| |
| case USB_REQ_SETADDRESS: |
| { |
| /* type: host-to-device; recipient = device |
| * value: device address |
| * index: 0 |
| * len: 0; data = none |
| */ |
| |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EP0SETUPSETADDRESS), |
| value); |
| if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == |
| USB_REQ_RECIPIENT_DEVICE && |
| index == 0 && len == 0 && value < 128) |
| { |
| /* Save the address. We cannot actually change to the next |
| * address until the completion of the status phase. |
| */ |
| |
| priv->paddr = ctrl.value[0]; |
| |
| /* Note that if we send the SETADDRESS command twice, that will |
| * force the address change. Otherwise, the hardware will |
| * automatically set the address at the end of the status phase. |
| */ |
| |
| lpc214x_usbcmd(CMD_USB_DEV_SETADDRESS, |
| CMD_USB_SETADDRESS_DEVEN | priv->paddr); |
| |
| /* Send a NULL packet. The status phase completes when the null |
| * packet has been sent successfully. |
| */ |
| |
| lpc214x_epwrite(LPC214X_EP0_IN, NULL, 0); |
| priv->ep0state = LPC214X_EP0SETADDRESS; |
| } |
| else |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADSETADDRESS), 0); |
| priv->stalled = 1; |
| } |
| } |
| break; |
| |
| case USB_REQ_GETDESCRIPTOR: |
| /* type: device-to-host; recipient = device |
| * value: descriptor type and index |
| * index: 0 or language ID; |
| * len: descriptor len; data = descriptor |
| */ |
| |
| case USB_REQ_SETDESCRIPTOR: |
| /* type: host-to-device; recipient = device |
| * value: descriptor type and index |
| * index: 0 or language ID; |
| * len: descriptor len; data = descriptor |
| */ |
| |
| { |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_GETSETDESC), 0); |
| if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == USB_REQ_RECIPIENT_DEVICE) |
| { |
| lpc214x_dispatchrequest(priv, &ctrl); |
| } |
| else |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADGETSETDESC), 0); |
| priv->stalled = 1; |
| } |
| } |
| break; |
| |
| case USB_REQ_GETCONFIGURATION: |
| /* type: device-to-host; recipient = device |
| * value: 0; |
| * index: 0; |
| * len: 1; data = configuration value |
| */ |
| |
| { |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_GETCONFIG), 0); |
| if (priv->paddrset && |
| (ctrl.type & USB_REQ_RECIPIENT_MASK) == |
| USB_REQ_RECIPIENT_DEVICE && |
| value == 0 && index == 0 && len == 1) |
| { |
| lpc214x_dispatchrequest(priv, &ctrl); |
| } |
| else |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADGETCONFIG), 0); |
| priv->stalled = 1; |
| } |
| } |
| break; |
| |
| case USB_REQ_SETCONFIGURATION: |
| /* type: host-to-device; recipient = device |
| * value: configuration value |
| * index: 0; |
| * len: 0; data = none |
| */ |
| |
| { |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_SETCONFIG), 0); |
| if ((ctrl.type & USB_REQ_RECIPIENT_MASK) == |
| USB_REQ_RECIPIENT_DEVICE && |
| index == 0 && len == 0) |
| { |
| lpc214x_dispatchrequest(priv, &ctrl); |
| } |
| else |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADSETCONFIG), 0); |
| priv->stalled = 1; |
| } |
| } |
| break; |
| |
| case USB_REQ_GETINTERFACE: |
| /* type: device-to-host; recipient = interface |
| * value: 0 |
| * index: interface; |
| * len: 1; data = alt interface |
| */ |
| |
| case USB_REQ_SETINTERFACE: |
| /* type: host-to-device; recipient = interface |
| * value: alternate setting |
| * index: interface; |
| * len: 0; data = none |
| */ |
| |
| { |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_GETSETIF), 0); |
| lpc214x_dispatchrequest(priv, &ctrl); |
| } |
| break; |
| |
| case USB_REQ_SYNCHFRAME: |
| /* type: device-to-host; recipient = endpoint |
| * value: 0 |
| * index: endpoint; |
| * len: 2; data = frame number |
| */ |
| |
| { |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_SYNCHFRAME), 0); |
| } |
| break; |
| |
| default: |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDCTRLREQ), 0); |
| priv->stalled = 1; |
| } |
| break; |
| } |
| |
| if (priv->stalled) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EP0SETUPSTALLED), |
| priv->ep0state); |
| ep0 = &priv->eplist[LPC214X_EP0_OUT]; |
| lpc214x_epstall(&ep0->ep, false); |
| ep0 = &priv->eplist[LPC214X_EP0_IN]; |
| lpc214x_epstall(&ep0->ep, false); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_ep0dataoutinterrupt |
| * |
| * Description: |
| * USB Ctrl EP Data OUT Event. This is logically part of the USB interrupt |
| * handler. Each non-isochronous OUT endpoint gives an interrupt when they |
| * receive a packet without error. |
| * |
| ****************************************************************************/ |
| |
| static inline void lpc214x_ep0dataoutinterrupt(struct lpc214x_usbdev_s *priv) |
| { |
| struct lpc214x_ep_s *ep0; |
| uint32_t pktlen; |
| |
| /* Copy new setup packet into setup buffer */ |
| |
| switch (priv->ep0state) |
| { |
| case LPC214X_EP0SHORTWRITE: |
| { |
| priv->ep0state = LPC214X_EP0STATUSOUT; |
| pktlen = lpc214x_epread(LPC214X_EP0_OUT, |
| NULL, CONFIG_USBDEV_EP0_MAXSIZE); |
| if (LPC214X_READOVERRUN(pktlen)) |
| { |
| lpc214x_ep0setup(priv); |
| } |
| } |
| |
| break; |
| |
| case LPC214X_EP0SHORTWRSENT: |
| { |
| priv->ep0state = LPC214X_EP0REQUEST; |
| pktlen = lpc214x_epread(LPC214X_EP0_OUT, |
| NULL, CONFIG_USBDEV_EP0_MAXSIZE); |
| if (LPC214X_READOVERRUN(pktlen)) |
| { |
| lpc214x_ep0setup(priv); |
| } |
| } |
| |
| break; |
| |
| case LPC214X_EP0REQUEST: |
| { |
| /* Process the next request action (if any) */ |
| |
| lpc214x_rdrequest(&priv->eplist[LPC214X_EP0_OUT]); |
| } |
| break; |
| |
| default: |
| priv->stalled = 1; |
| break; |
| } |
| |
| if (priv->stalled) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EP0OUTSTALLED), |
| priv->ep0state); |
| ep0 = &priv->eplist[LPC214X_EP0_OUT]; |
| lpc214x_epstall(&ep0->ep, false); |
| ep0 = &priv->eplist[LPC214X_EP0_IN]; |
| lpc214x_epstall(&ep0->ep, false); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_ep0dataininterrupt |
| * |
| * Description: |
| * USB Ctrl EP Data IN Event. This is logically part of the USB interrupt |
| * handler. All non-isochronous IN endpoints give this interrupt when a |
| * packet is successfully transmitted (OR a NAK handshake is sent on the |
| * bus provided that the interrupt on NAK feature is enabled). |
| * |
| ****************************************************************************/ |
| |
| static inline void lpc214x_ep0dataininterrupt(struct lpc214x_usbdev_s *priv) |
| { |
| struct lpc214x_ep_s *ep0; |
| |
| switch (priv->ep0state) |
| { |
| case LPC214X_EP0STATUSOUT: |
| case LPC214X_EP0STATUSIN: |
| priv->ep0state = LPC214X_EP0REQUEST; |
| break; |
| |
| case LPC214X_EP0SHORTWRITE: |
| priv->ep0state = LPC214X_EP0SHORTWRSENT; |
| break; |
| |
| case LPC214X_EP0SETADDRESS: |
| { |
| /* If the address was set to a non-zero value, then thiscompletes the |
| * default phase, and begins the address phase |
| * (still not fully configured) |
| */ |
| |
| usbtrace(TRACE_INTDECODE( |
| LPC214X_TRACEINTID_EP0INSETADDRESS), |
| (uint16_t)priv->paddr); |
| lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 0); |
| if (priv->paddr) |
| { |
| priv->paddrset = 1; |
| priv->ep0state = LPC214X_EP0REQUEST; |
| } |
| } |
| break; |
| |
| case LPC214X_EP0REQUEST: |
| { |
| /* Process the next request action (if any) */ |
| |
| ep0 = &priv->eplist[LPC214X_EP0_IN]; |
| ep0->txbusy = 0; |
| lpc214x_wrrequest(ep0); |
| } |
| break; |
| |
| default: |
| priv->stalled = 1; |
| break; |
| } |
| |
| if (priv->stalled) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EP0INSTALLED), |
| priv->ep0state); |
| ep0 = &priv->eplist[LPC214X_EP0_OUT]; |
| lpc214x_epstall(&ep0->ep, false); |
| ep0 = &priv->eplist[LPC214X_EP0_IN]; |
| lpc214x_epstall(&ep0->ep, false); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_usbinterrupt |
| * |
| * Description: |
| * USB interrupt handler |
| * |
| ****************************************************************************/ |
| |
| static int lpc214x_usbinterrupt(int irq, |
| void *context, void *arg) |
| { |
| struct lpc214x_usbdev_s *priv = &g_usbdev; |
| struct lpc214x_ep_s *privep ; |
| |
| uint32_t devintstatus; /* Sampled state of the device interrupt status register */ |
| uint32_t epintstatus; /* Sampled state of the endpoint interrupt status register */ |
| #ifdef CONFIG_LPC214X_USBDEV_DMA |
| uint32_t dmaintstatus; /* Sampled state of dma interrupt status register */ |
| #endif |
| uint32_t softprio; /* Current priority interrupt bitset */ |
| uint32_t pending; /* Pending subset of priority interrupt bitset */ |
| uint8_t epphy; /* Physical endpoint number being processed */ |
| int i; |
| |
| usbtrace(TRACE_INTENTRY(LPC214X_TRACEINTID_USB), 0); |
| |
| /* Read the device interrupt status register */ |
| |
| devintstatus = lpc214x_getreg(LPC214X_USBDEV_DEVINTST); |
| |
| #ifdef CONFIG_LPC214X_USBDEV_DMA |
| /* Check for low priority and high priority (non-DMA) interrupts */ |
| |
| if ((lpc214x_getreg(LPC214X_USBDEV_INTST) & |
| (USBDEV_INTST_REQLP | USBDEV_INTST_REQHP)) != 0) |
| { |
| #endif |
| #ifdef CONFIG_LPC214X_USBDEV_EPFAST_INTERRUPT |
| /* Fast EP interrupt */ |
| |
| if ((devintstatus & USBDEV_DEVINT_EPFAST) != 0) |
| { |
| /* Clear Fast EP interrupt */ |
| |
| lpc214x_putreg(USBDEV_DEVINT_EPFAST, |
| LPC214X_USBDEV_DEVINTCLR); |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPFAST), 0); |
| |
| /* Do what? */ |
| } |
| |
| #endif |
| |
| #ifdef CONFIG_DEBUG_FEATURES |
| /* USB engine error interrupt */ |
| |
| if ((devintstatus & USBDEV_DEVINT_EPRINT)) |
| { |
| uint8_t errcode; |
| |
| /* Clear the error interrupt */ |
| |
| lpc214x_putreg(USBDEV_DEVINT_EPRINT, |
| LPC214X_USBDEV_DEVINTCLR); |
| |
| /* And show what error occurred */ |
| |
| errcode = (uint8_t)lpc214x_usbcmd( |
| CMD_USB_DEV_READERRORSTATUS, 0) & 0x0f; |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPRINT), |
| (uint16_t)errcode); |
| UNUSED(errcode); |
| } |
| #endif |
| |
| #ifdef CONFIG_LPC214X_USBDEV_FRAME_INTERRUPT |
| /* Frame interrupt */ |
| |
| if ((devintstatus & USBDEV_DEVINT_FRAME) != 0) |
| { |
| /* Clear the frame interrupt */ |
| |
| lpc214x_putreg(USBDEV_DEVINT_FRAME, LPC214X_USBDEV_DEVINTCLR); |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_FRAME), 0); |
| |
| /* Then read the start of frame value */ |
| |
| priv->sof = (uint16_t)lpc214x_usbcmd(CMD_USB_DEV_READFRAMENO, 0); |
| } |
| #endif |
| |
| /* Device Status interrupt */ |
| |
| if ((devintstatus & USBDEV_DEVINT_DEVSTAT) != 0) |
| { |
| /* Clear Device status interrupt */ |
| |
| lpc214x_putreg(USBDEV_DEVINT_DEVSTAT, LPC214X_USBDEV_DEVINTCLR); |
| |
| /* Get device status */ |
| |
| g_usbdev.devstatus = (uint8_t)lpc214x_usbcmd( |
| CMD_USB_DEV_GETSTATUS, 0); |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_DEVSTAT), |
| (uint16_t)g_usbdev.devstatus); |
| |
| /* Device connection status */ |
| |
| if (DEVSTATUS_CONNCHG(g_usbdev.devstatus)) |
| { |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_CONNECTCHG), |
| (uint16_t)g_usbdev.devstatus); |
| if (DEVSTATUS_CONNECT(g_usbdev.devstatus)) |
| { |
| /* Host is connected */ |
| |
| if (!priv->attached) |
| { |
| /* We have a transition from unattached to attached */ |
| |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_CONNECTED), |
| (uint16_t)g_usbdev.devstatus); |
| priv->usbdev.speed = USB_SPEED_UNKNOWN; |
| lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 0); |
| priv->attached = 1; |
| } |
| } |
| |
| /* Otherwise the host is not attached */ |
| |
| else if (priv->attached) |
| { |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_DISCONNECTED), |
| (uint16_t)g_usbdev.devstatus); |
| priv->usbdev.speed = USB_SPEED_UNKNOWN; |
| lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 0); |
| priv->attached = 0; |
| priv->paddrset = 0; |
| } |
| } |
| |
| /* Device suspend status */ |
| |
| if (DEVSTATUS_SUSPCHG(g_usbdev.devstatus)) |
| { |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_SUSPENDCHG), |
| (uint16_t)g_usbdev.devstatus); |
| |
| /* Inform the Class driver of the change */ |
| |
| if (priv->driver) |
| { |
| if (DEVSTATUS_SUSPEND(g_usbdev.devstatus)) |
| { |
| CLASS_SUSPEND(priv->driver, &priv->usbdev); |
| } |
| else |
| { |
| CLASS_RESUME(priv->driver, &priv->usbdev); |
| } |
| } |
| |
| /* TODO: Perform power management operations here. */ |
| } |
| |
| /* Device reset */ |
| |
| if (DEVSTATUS_RESET(g_usbdev.devstatus)) |
| { |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_DEVRESET), |
| (uint16_t)g_usbdev.devstatus); |
| lpc214x_usbreset(priv); |
| } |
| } |
| |
| /* Slow EP interrupt */ |
| |
| if ((devintstatus & USBDEV_DEVINT_EPSLOW) != 0) |
| { |
| /* Clear Slow EP interrupt */ |
| |
| lpc214x_putreg(USBDEV_DEVINT_EPSLOW, LPC214X_USBDEV_DEVINTCLR); |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPSLOW), 0); |
| |
| do |
| { |
| /* Read the endpoint interrupt status register */ |
| |
| epintstatus = lpc214x_getreg(LPC214X_USBDEV_EPINTST); |
| |
| /* Loop twice: Process software high priority interrupts |
| * on the first pass and low priority interrupts on the |
| * second. |
| */ |
| |
| softprio = priv->softprio; |
| for (i = 0; i < 2; i++, softprio = ~softprio) |
| { |
| /* On the first time through the loop, pending will be |
| * the bitset of high priority pending interrupts; on the |
| * second time through it will be the bitset of low |
| * priority interrupts. |
| */ |
| |
| pending = epintstatus & softprio; |
| |
| /* EP0 OUT interrupt indicated by bit0 == 1 */ |
| |
| if ((pending & 1) != 0) |
| { |
| /* Clear the endpoint interrupt */ |
| |
| uint32_t result = lpc214x_epclrinterrupt( |
| LPC214X_CTRLEP_OUT); |
| if (result & USBDEV_EPSETUPPACKET) |
| { |
| usbtrace(TRACE_INTDECODE( |
| LPC214X_TRACEINTID_EP0SETUP), |
| (uint16_t)result); |
| lpc214x_ep0setup(priv); |
| } |
| else |
| { |
| usbtrace(TRACE_INTDECODE( |
| LPC214X_TRACEINTID_EP0OUT), |
| priv->ep0state); |
| lpc214x_ep0dataoutinterrupt(priv); |
| } |
| break; |
| } |
| |
| /* EP0 IN interrupt indicated by bit1 == 1 */ |
| |
| if ((pending & 2) != 0) |
| { |
| /* Clear the endpoint interrupt */ |
| |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EP0IN), |
| priv->ep0state); |
| lpc214x_epclrinterrupt(LPC214X_CTRLEP_IN); |
| lpc214x_ep0dataininterrupt(priv); |
| } |
| |
| pending >>= 2; |
| |
| /* All other endpoints EP 1-31 */ |
| |
| for (epphy = 2; pending; epphy++, pending >>= 1) |
| { |
| /* Is the endpoint interrupt pending? */ |
| |
| if ((pending & 1) != 0) |
| { |
| /* Yes.. clear the endpoint interrupt */ |
| |
| lpc214x_epclrinterrupt(epphy); |
| |
| /* Get the endpoint structure corresponding to |
| * the physical endpoint number. |
| */ |
| |
| privep = &priv->eplist[epphy]; |
| |
| /* Check for complete on IN or OUT endpoint. |
| * Odd physical endpoint addresses are IN |
| * endpoints. |
| */ |
| |
| if ((epphy & 1) != 0) |
| { |
| /* IN: device-to-host */ |
| |
| usbtrace(TRACE_INTDECODE( |
| LPC214X_TRACEINTID_EPOUT), |
| (uint16_t)epphy); |
| if (priv->usbdev.speed == USB_SPEED_UNKNOWN) |
| { |
| priv->usbdev.speed = USB_SPEED_FULL; |
| lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 1); |
| } |
| |
| /* Write host data from the current write |
| * request (if any) |
| */ |
| |
| privep->txbusy = 0; |
| lpc214x_wrrequest(privep); |
| } |
| else |
| { |
| /* OUT: host-to-device */ |
| |
| usbtrace(TRACE_INTDECODE( |
| LPC214X_TRACEINTID_EPIN), |
| (uint16_t)epphy); |
| |
| /* Read host data into the current read |
| * request |
| */ |
| |
| if (!lpc214x_rqempty(privep)) |
| { |
| lpc214x_rdrequest(privep); |
| } |
| else |
| { |
| uinfo("Pending data on OUT endpoint\n"); |
| priv->rxpending = 1; |
| } |
| } |
| } |
| } |
| } |
| } |
| while (epintstatus); |
| } |
| #ifdef CONFIG_LPC214X_USBDEV_DMA |
| } |
| |
| /* Check for DMA interrupts */ |
| |
| if ((lpc214x_getreg(LPC214X_USBDEV_INTST) & |
| USBDEV_INTST_REQDMA) != 0) |
| { |
| /* First Software High priority and then low priority */ |
| |
| uint32_t tmp; |
| |
| /* Collect the DMA interrupt sources */ |
| |
| dmaintstatus = 0; |
| tmp = lpc214x_getreg(LPC214X_USBDEV_EOTINTST); |
| if (lpc214x_getreg(LPC214X_USBDEV_DMAINTEN) & 1) |
| { |
| dmaintstatus |= tmp; |
| } |
| |
| lpc214x_putreg(tmp, LPC214X_USBDEV_EOTINTCLR); |
| |
| tmp = lpc214x_getreg(LPC214X_USBDEV_NDDRINTST); |
| if (lpc214x_getreg(LPC214X_USBDEV_DMAINTEN) & 2) |
| { |
| dmaintstatus |= tmp; |
| } |
| |
| lpc214x_putreg(tmp, LPC214X_USBDEV_NDDRINTCLR); |
| |
| tmp = lpc214x_getreg(LPC214X_USBDEV_SYSERRINTST); |
| if (lpc214x_getreg(LPC214X_USBDEV_DMAINTEN) & 4) |
| { |
| dmaintstatus |= tmp; |
| } |
| |
| lpc214x_putreg(tmp, LPC214X_USBDEV_SYSERRINTCLR); |
| |
| /* Loop twice: Process software high priority interrupts on the |
| * first pass and low priority interrupts on the second. |
| */ |
| |
| softprio = priv->softprio; |
| for (i = 0; i < 2; i++, softprio = ~softprio) |
| { |
| /* On the first time through the loop, pending will be |
| * the bitset of high priority pending interrupts; on the |
| * second time through it will be the bitset of low |
| * priority interrupts. Note that EP0 IN and OUT are |
| * omitted. |
| */ |
| |
| pending = (dmaintstatus & softprio) >> 2; |
| for (epphy = 2; pending; epphy++, pending >>= 1) |
| { |
| if ((pending & 1) != 0) |
| { |
| usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPDMA), |
| (uint16_t)epphy); |
| #warning DO WHAT? |
| } |
| } |
| } |
| } |
| |
| #endif |
| usbtrace(TRACE_INTEXIT(LPC214X_TRACEINTID_USB), 0); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_dmasetup |
| * |
| * Description: |
| * Setup for DMA Transfer |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_LPC214X_USBDEV_DMA |
| static int lpc214x_dmasetup(struct lpc214x_usbdev_s *priv, uint8_t epphy, |
| uint32_t epmaxsize, uint32_t nbytes, |
| uint32_t *isocpacket, |
| bool isochronous); |
| { |
| struct lpc214x_dmadesc_s *dmadesc = priv; |
| uint32_t reg; |
| |
| #ifdef CONFIG_DEBUG_FEATURES |
| if (!priv || epphy < 2) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0); |
| return -EINVAL; |
| } |
| #endif |
| |
| /* Check if a DMA descriptor has been assigned. If not, than that |
| * indicates that we will have to do parallel I/O |
| */ |
| |
| if (!dmadesc) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_NODMADESC), 0); |
| return -EBUSY; |
| } |
| |
| /* Verify that the DMA descriptor is available */ |
| |
| if ((dmadesc->status & USB_DMADESC_STATUSMASK) == |
| USB_DMADESC_BEINGSERVICED) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_DMABUSY), 0); |
| return -EBUSY; /* Shouldn't happen */ |
| } |
| |
| /* Init DMA Descriptor */ |
| |
| dmadesc->nexdesc = 0; |
| dmadesc->config = USB_DMADESC_MODENORMAL | |
| ((epmaxsize << USB_DMADESC_PKTSIZESHIFT) & |
| USB_DMADESC_PKTSIZEMASK) | |
| ((nbytes << USB_DMADESC_BULENSHIFT) & |
| USB_DMADESC_BUFLENMASK); |
| |
| #ifdef CONFIG_USBDEV_ISOCHRONOUS |
| if (isochronous) |
| { |
| dmadesc->config |= USB_DMADESC_ISCOEP; |
| } |
| #endif |
| |
| dmadesc->start = (uint32_t)&dmadesc->buffer; |
| dmadesc->status = 0; |
| |
| #ifdef CONFIG_USBDEV_ISOCHRONOUS |
| dmadesc->size = (uint32_t)packet; |
| #endif |
| |
| /* Enable DMA transfer for this endpoint */ |
| |
| putreq32(1 << epphy, LPC214X_USBDEV_EPDMAEN); |
| |
| /* Check state of IN/OUT Ep buffer */ |
| |
| reg = lpc214x_usbcmd(CMD_USB_EP_SELECT | epphy, 0); |
| |
| if ((LPC214X_EPPHYIN(epphy) && (reg & 0x60) == 0) || |
| (LPC214X_EPPHYOUT(epphy) && (reg & 0x60) == 0x60)) |
| { |
| /* DMA should be "being serviced" */ |
| |
| if ((dmadesc->status & USB_DMADESC_STATUSMASK) != |
| USB_DMADESC_BEINGSERVICED) |
| { |
| /* Re-trigger the DMA Transfer */ |
| |
| putreq21(1 << epphy, LPC214X_USBDEV_DMARCLR); |
| putreq32(1 << epphy, LPC214X_USBDEV_EPDMAEN); |
| } |
| } |
| |
| return OK; |
| } |
| #endif /* CONFIG_LPC214X_USBDEV_DMA */ |
| |
| /**************************************************************************** |
| * Name: lpc214x_dmarestart |
| * |
| * Description: |
| * Restart DMA Transfer |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_LPC214X_USBDEV_DMA |
| static void lpc214x_dmarestart(uint8_t epphy, uint32_t descndx) |
| { |
| uint32_t reg; |
| |
| /* Clear DMA descriptor status */ |
| |
| g_usbddesc[descndx].status = 0; |
| |
| /* Enable DMA transfer on the endpoint */ |
| |
| lpc214x_putreg(1 << epph, LPC214X_USBDEV_EPDMAEN); |
| |
| /* Check the state of IN/OUT EP buffer */ |
| |
| uint32_t reg = lpc214x_usbcmd(CMD_USB_EP_SELECT | epphy, 0); |
| if ((LPC214X_EPPHYIN(epphy) && (reg & 0x60) == 0) || |
| (LPC214X_EPPHYIN(epphy) && (reg & 0x60) == 0x60)) |
| { |
| /* Re-trigger the DMA Transfer */ |
| |
| putreq21(1 << epphy, LPC214X_USBDEV_DMARCLR); |
| putreq32(1 << epphy, LPC214X_USBDEV_EPDMAEN); |
| } |
| } |
| #endif /* CONFIG_LPC214X_USBDEV_DMA */ |
| |
| /**************************************************************************** |
| * Name: lpc214x_dmadisable |
| * |
| * Description: |
| * Disable DMA transfer for the EP |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_LPC214X_USBDEV_DMA |
| static void lpc214x_dmadisable(uint8_t epphy) |
| { |
| EPDMADIS = 1 << epphy; |
| } |
| #endif /* CONFIG_LPC214X_USBDEV_DMA */ |
| |
| /**************************************************************************** |
| * Endpoint operations |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: lpc214x_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 lpc214x_epconfigure(struct usbdev_ep_s *ep, |
| const struct usb_epdesc_s *desc, |
| bool last) |
| { |
| struct lpc214x_ep_s *privep = (struct lpc214x_ep_s *)ep; |
| uint32_t inten; |
| |
| usbtrace(TRACE_EPCONFIGURE, privep->epphy); |
| DEBUGASSERT(desc->addr == ep->eplog); |
| |
| /* Realize the endpoint */ |
| |
| lpc214x_eprealize(privep, 1, GETUINT16(desc->mxpacketsize)); |
| |
| /* Enable and reset EP -- twice */ |
| |
| lpc214x_usbcmd(CMD_USB_EP_SETSTATUS | privep->epphy, 0); |
| lpc214x_usbcmd(CMD_USB_EP_SETSTATUS | privep->epphy, 0); |
| |
| #ifdef CONFIG_LPC214X_USBDEV_DMA |
| /* Enable DMA Ep interrupt (WO) */ |
| |
| lpc214x_putreg(1 << privep->epphy, LPC214X_USBDEV_EPDMAEN); |
| #else |
| /* Enable Ep interrupt (R/W) */ |
| |
| inten = lpc214x_getreg(LPC214X_USBDEV_EPINTEN); |
| inten |= (1 << privep->epphy); |
| lpc214x_putreg(inten, LPC214X_USBDEV_EPINTEN); |
| #endif |
| |
| /* If all of the endpoints have been configured, then tell the USB |
| * controller to enabled normal activity on all realized endpoints. |
| */ |
| |
| if (last) |
| { |
| lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 1); |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_epdisable |
| * |
| * Description: |
| * The endpoint will no longer be used |
| * |
| ****************************************************************************/ |
| |
| static int lpc214x_epdisable(struct usbdev_ep_s *ep) |
| { |
| struct lpc214x_ep_s *privep = (struct lpc214x_ep_s *)ep; |
| irqstate_t flags; |
| uint32_t mask = (1 << privep->epphy); |
| uint32_t reg; |
| |
| #ifdef CONFIG_DEBUG_FEATURES |
| if (!ep) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0); |
| return -EINVAL; |
| } |
| #endif |
| |
| usbtrace(TRACE_EPDISABLE, privep->epphy); |
| |
| /* Cancel any ongoing activity */ |
| |
| flags = enter_critical_section(); |
| lpc214x_cancelrequests(privep); |
| |
| /* Disable endpoint and interrupt */ |
| |
| reg = lpc214x_getreg(LPC214X_USBDEV_REEP); |
| reg &= ~mask; |
| lpc214x_putreg(reg, LPC214X_USBDEV_REEP); |
| |
| lpc214x_putreg(mask, LPC214X_USBDEV_EPDMADIS); |
| |
| reg = lpc214x_getreg(LPC214X_USBDEV_EPINTEN); |
| reg &= ~mask; |
| lpc214x_putreg(reg, LPC214X_USBDEV_EPINTEN); |
| |
| leave_critical_section(flags); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_epallocreq |
| * |
| * Description: |
| * Allocate an I/O request |
| * |
| ****************************************************************************/ |
| |
| static struct usbdev_req_s *lpc214x_epallocreq(struct usbdev_ep_s *ep) |
| { |
| struct lpc214x_req_s *privreq; |
| |
| #ifdef CONFIG_DEBUG_FEATURES |
| if (!ep) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0); |
| return NULL; |
| } |
| #endif |
| |
| usbtrace(TRACE_EPALLOCREQ, ((struct lpc214x_ep_s *)ep)->epphy); |
| |
| privreq = (struct lpc214x_req_s *) |
| kmm_malloc(sizeof(struct lpc214x_req_s)); |
| if (!privreq) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_ALLOCFAIL), 0); |
| return NULL; |
| } |
| |
| memset(privreq, 0, sizeof(struct lpc214x_req_s)); |
| return &privreq->req; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_epfreereq |
| * |
| * Description: |
| * Free an I/O request |
| * |
| ****************************************************************************/ |
| |
| static void lpc214x_epfreereq(struct usbdev_ep_s *ep, |
| struct usbdev_req_s *req) |
| { |
| struct lpc214x_req_s *privreq = (struct lpc214x_req_s *)req; |
| |
| #ifdef CONFIG_DEBUG_FEATURES |
| if (!ep || !req) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0); |
| return; |
| } |
| #endif |
| |
| usbtrace(TRACE_EPFREEREQ, ((struct lpc214x_ep_s *)ep)->epphy); |
| |
| kmm_free(privreq); |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_epallocbuffer |
| * |
| * Description: |
| * Allocate an I/O buffer |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_LPC214X_USBDEV_DMA |
| static void *lpc214x_epallocbuffer(struct usbdev_ep_s *ep, |
| uint16_t nbytes) |
| { |
| #ifdef CONFIG_USBDEV_DMA |
| |
| struct lpc214x_ep_s *privep = (struct lpc214x_ep_s *)ep; |
| int descndx; |
| |
| usbtrace(TRACE_EPALLOCBUFFER, privep->epphy); |
| |
| /* Find a free DMA description */ |
| |
| #error "LOGIC INCOMPLETE" |
| |
| /* Set UDCA to the allocated DMA descriptor for this endpoint */ |
| |
| USB_UDCA[privep->epphy] = &USB_DDESC[descndx]; |
| return &USB_DDESC[descndx] |
| |
| #elif defined(CONFIG_USBDEV_DMAMEMORY) |
| |
| usbtrace(TRACE_EPALLOCBUFFER, privep->epphy); |
| return usbdev_dma_alloc(bytes); |
| |
| #else |
| |
| usbtrace(TRACE_EPALLOCBUFFER, privep->epphy); |
| return kmm_malloc(bytes); |
| |
| #endif |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: lpc214x_epfreebuffer |
| * |
| * Description: |
| * Free an I/O buffer |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_USBDEV_DMA |
| |
| static void lpc214x_epfreebuffer(struct usbdev_ep_s *ep, void *buf) |
| { |
| #ifdef CONFIG_LPC214X_USBDEV_DMA |
| struct lpc214x_ep_s *privep = (struct lpc214x_ep_s *)ep; |
| |
| usbtrace(TRACE_EPFREEBUFFER, privep->epphy); |
| |
| /* Indicate that there is no DMA descriptor associated with this |
| * endpoint |
| */ |
| |
| USB_UDCA[privep->epphy] = NULL; |
| |
| /* Mark the DMA descriptor as free for re-allocation */ |
| |
| # error "LOGIC INCOMPLETE" |
| |
| #elif defined(CONFIG_USBDEV_DMAMEMORY) |
| |
| usbtrace(TRACE_EPFREEBUFFER, privep->epphy); |
| usbdev_dma_free(buf); |
| |
| #else |
| |
| usbtrace(TRACE_EPFREEBUFFER, privep->epphy); |
| kmm_free(buf); |
| |
| #endif |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: lpc214x_epsubmit |
| * |
| * Description: |
| * Submit an I/O request to the endpoint |
| * |
| ****************************************************************************/ |
| |
| static int lpc214x_epsubmit(struct usbdev_ep_s *ep, |
| struct usbdev_req_s *req) |
| { |
| struct lpc214x_req_s *privreq = (struct lpc214x_req_s *)req; |
| struct lpc214x_ep_s *privep = (struct lpc214x_ep_s *)ep; |
| struct lpc214x_usbdev_s *priv; |
| irqstate_t flags; |
| int ret = OK; |
| |
| #ifdef CONFIG_DEBUG_FEATURES |
| if (!req || !req->callback || !req->buf || !ep) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_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(LPC214X_TRACEERR_NOTCONFIGURED), |
| priv->usbdev.speed); |
| return -ESHUTDOWN; |
| } |
| |
| /* Handle the request from the class driver */ |
| |
| req->result = -EINPROGRESS; |
| req->xfrd = 0; |
| flags = enter_critical_section(); |
| |
| /* If we are stalled, then drop all requests on the floor */ |
| |
| if (privep->stalled) |
| { |
| lpc214x_abortrequest(privep, privreq, -EBUSY); |
| ret = -EBUSY; |
| } |
| |
| /* Handle IN (device-to-host) requests */ |
| |
| else if (LPC214X_EPPHYIN(privep->epphy)) |
| { |
| /* Add the new request to the request queue for the IN endpoint */ |
| |
| lpc214x_rqenqueue(privep, privreq); |
| usbtrace(TRACE_INREQQUEUED(privep->epphy), privreq->req.len); |
| |
| /* If the IN endpoint FIFO is available, then transfer the data now */ |
| |
| if (privep->txbusy == 0) |
| { |
| ret = lpc214x_wrrequest(privep); |
| } |
| } |
| |
| /* Handle OUT (host-to-device) requests */ |
| |
| else |
| { |
| /* Add the new request to the request queue for the OUT endpoint */ |
| |
| privep->txnullpkt = 0; |
| lpc214x_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 = lpc214x_rdrequest(privep); |
| priv->rxpending = 0; |
| } |
| } |
| |
| leave_critical_section(flags); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_epcancel |
| * |
| * Description: |
| * Cancel an I/O request previously sent to an endpoint |
| * |
| ****************************************************************************/ |
| |
| static int lpc214x_epcancel(struct usbdev_ep_s *ep, |
| struct usbdev_req_s *req) |
| { |
| struct lpc214x_ep_s *privep = (struct lpc214x_ep_s *)ep; |
| irqstate_t flags; |
| |
| #ifdef CONFIG_DEBUG_FEATURES |
| if (!ep || !req) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0); |
| return -EINVAL; |
| } |
| #endif |
| |
| usbtrace(TRACE_EPCANCEL, privep->epphy); |
| |
| flags = enter_critical_section(); |
| lpc214x_cancelrequests(privep); |
| leave_critical_section(flags); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_epstall |
| * |
| * Description: |
| * Stall or resume and endpoint |
| * |
| ****************************************************************************/ |
| |
| static int lpc214x_epstall(struct usbdev_ep_s *ep, bool resume) |
| { |
| struct lpc214x_ep_s *privep = (struct lpc214x_ep_s *)ep; |
| irqstate_t flags; |
| |
| /* STALL or RESUME the endpoint */ |
| |
| flags = enter_critical_section(); |
| usbtrace(resume ? TRACE_EPRESUME : TRACE_EPSTALL, privep->epphy); |
| lpc214x_usbcmd(CMD_USB_EP_SETSTATUS | privep->epphy, |
| (resume ? 0 : USBDEV_EPSTALL)); |
| |
| /* If the endpoint of was resumed, then restart any queue write requests */ |
| |
| if (resume) |
| { |
| lpc214x_wrrequest(privep); |
| } |
| |
| leave_critical_section(flags); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Device operations |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: lcp214x_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 *lcp214x_allocep(struct usbdev_s *dev, |
| uint8_t eplog, |
| bool in, uint8_t eptype) |
| { |
| struct lpc214x_usbdev_s *priv = (struct lpc214x_usbdev_s *)dev; |
| uint32_t epset = LPC214X_EPALLSET & ~LPC214X_EPCTRLSET; |
| irqstate_t flags; |
| int epndx = 0; |
| |
| usbtrace(TRACE_DEVALLOCEP, (uint16_t)eplog); |
| |
| /* Ignore any direction bits in the logical address */ |
| |
| eplog = USB_EPNO(eplog); |
| |
| /* A logical address of 0 means that any endpoint will do */ |
| |
| if (eplog > 0) |
| { |
| /* Otherwise, we will return the endpoint structure only for the |
| * requested 'logical' endpoint. All of the other checks will still |
| * be performed. |
| * |
| * First, verify that the logical endpoint is in the range supported by |
| * by the hardware. |
| */ |
| |
| if (eplog >= LPC214X_NLOGENDPOINTS) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADEPNO), |
| (uint16_t)eplog); |
| return NULL; |
| } |
| |
| /* Convert the logical address to a physical OUT endpoint address and |
| * remove all of the candidate endpoints from the bitset except for the |
| * the IN/OUT pair for this logical address. |
| */ |
| |
| epset &= 3 << (eplog << 1); |
| } |
| |
| /* Get the subset matching the requested direction */ |
| |
| if (in) |
| { |
| epset &= LPC214X_EPINSET; |
| } |
| else |
| { |
| epset &= LPC214X_EPOUTSET; |
| } |
| |
| /* Get the subset matching the requested type */ |
| |
| switch (eptype) |
| { |
| case USB_EP_ATTR_XFER_INT: /* Interrupt endpoint */ |
| epset &= LPC214X_EPINTRSET; |
| break; |
| |
| case USB_EP_ATTR_XFER_BULK: /* Bulk endpoint */ |
| epset &= LPC214X_EPBULKSET; |
| break; |
| |
| case USB_EP_ATTR_XFER_ISOC: /* Isochronous endpoint */ |
| epset &= LPC214X_EPISOCSET; |
| break; |
| |
| case USB_EP_ATTR_XFER_CONTROL: /* Control endpoint -- not a valid choice */ |
| default: |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADEPTYPE), (uint16_t)eptype); |
| return NULL; |
| } |
| |
| /* Is the resulting endpoint supported by the LPC214x? */ |
| |
| if (epset) |
| { |
| /* Yes.. now see if any of the request endpoints are available */ |
| |
| flags = enter_critical_section(); |
| epset &= priv->epavail; |
| if (epset) |
| { |
| /* Select the lowest bit in the set of matching, |
| * available endpoints |
| */ |
| |
| for (epndx = 2; epndx < LPC214X_NPHYSENDPOINTS; epndx++) |
| { |
| uint32_t bit = 1 << epndx; |
| if ((epset & bit) != 0) |
| { |
| /* Mark the IN/OUT endpoint no longer available */ |
| |
| priv->epavail &= ~(3 << (bit & ~1)); |
| leave_critical_section(flags); |
| |
| /* And return the pointer to the standard endpoint |
| * structure |
| */ |
| |
| return &priv->eplist[epndx].ep; |
| } |
| } |
| |
| /* Shouldn't get here */ |
| } |
| |
| leave_critical_section(flags); |
| } |
| |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_NOEP), (uint16_t)eplog); |
| return NULL; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_freeep |
| * |
| * Description: |
| * Free the previously allocated endpoint |
| * |
| ****************************************************************************/ |
| |
| static void lpc214x_freeep(struct usbdev_s *dev, |
| struct usbdev_ep_s *ep) |
| { |
| struct lpc214x_usbdev_s *priv = (struct lpc214x_usbdev_s *)dev; |
| struct lpc214x_ep_s *privep = (struct lpc214x_ep_s *)ep; |
| irqstate_t flags; |
| |
| usbtrace(TRACE_DEVFREEEP, (uint16_t)privep->epphy); |
| |
| if (priv && privep) |
| { |
| /* Mark the endpoint as available */ |
| |
| flags = enter_critical_section(); |
| priv->epavail |= (1 << privep->epphy); |
| leave_critical_section(flags); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_getframe |
| * |
| * Description: |
| * Returns the current frame number |
| * |
| ****************************************************************************/ |
| |
| static int lpc214x_getframe(struct usbdev_s *dev) |
| { |
| #ifdef CONFIG_LPC214X_USBDEV_FRAME_INTERRUPT |
| struct lpc214x_usbdev_s *priv = (struct lpc214x_usbdev_s *)dev; |
| |
| /* Return last valid value of SOF read by the interrupt handler */ |
| |
| usbtrace(TRACE_DEVGETFRAME, (uint16_t)priv->sof); |
| return priv->sof; |
| #else |
| /* Return the last frame number detected by the hardware */ |
| |
| usbtrace(TRACE_DEVGETFRAME, 0); |
| return (int)lpc214x_usbcmd(CMD_USB_DEV_READFRAMENO, 0); |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_wakeup |
| * |
| * Description: |
| * Tries to wake up the host connected to this device |
| * |
| ****************************************************************************/ |
| |
| static int lpc214x_wakeup(struct usbdev_s *dev) |
| { |
| uint8_t arg = USBDEV_DEVSTATUS_SUSPEND; |
| irqstate_t flags; |
| |
| usbtrace(TRACE_DEVWAKEUP, (uint16_t)g_usbdev.devstatus); |
| |
| flags = enter_critical_section(); |
| if (DEVSTATUS_CONNECT(g_usbdev.devstatus)) |
| { |
| arg |= USBDEV_DEVSTATUS_CONNECT; |
| } |
| |
| lpc214x_usbcmd(CMD_USB_DEV_SETSTATUS, arg); |
| leave_critical_section(flags); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_selfpowered |
| * |
| * Description: |
| * Sets/clears the device selfpowered feature |
| * |
| ****************************************************************************/ |
| |
| static int lpc214x_selfpowered(struct usbdev_s *dev, bool selfpowered) |
| { |
| struct lpc214x_usbdev_s *priv = (struct lpc214x_usbdev_s *)dev; |
| |
| usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered); |
| |
| #ifdef CONFIG_DEBUG_FEATURES |
| if (!dev) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0); |
| return -ENODEV; |
| } |
| #endif |
| |
| priv->selfpowered = selfpowered; |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc214x_pullup |
| * |
| * Description: |
| * Software-controlled connect to/disconnect from USB host |
| * |
| ****************************************************************************/ |
| |
| static int lpc214x_pullup(struct usbdev_s *dev, bool enable) |
| { |
| usbtrace(TRACE_DEVPULLUP, (uint16_t)enable); |
| |
| /* The USBDEV_DEVSTATUS_CONNECT bit in the CMD_USB_DEV_SETSTATUS command |
| * controls the LPC214x SoftConnect_N output pin that is used for |
| * SoftConnect. |
| */ |
| |
| lpc214x_usbcmd(CMD_USB_DEV_SETSTATUS, |
| (enable ? USBDEV_DEVSTATUS_CONNECT : 0)); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: arm_usbinitialize |
| * |
| * Description: |
| * Initialize USB hardware. |
| * |
| * Assumptions: |
| * - This function is called very early in the initialization sequence |
| * - PLL and GIO pin initialization is not performed here but should been |
| * in the low-level boot logic: PLL1 must be configured for operation |
| * at 48MHz and P0.23 and PO.31 in PINSEL1 must be configured for Vbus |
| * and USB connect LED. |
| * |
| ****************************************************************************/ |
| |
| void arm_usbinitialize(void) |
| { |
| struct lpc214x_usbdev_s *priv = &g_usbdev; |
| uint32_t reg; |
| int i; |
| |
| usbtrace(TRACE_DEVINIT, 0); |
| |
| /* Disable USB interrupts */ |
| |
| lpc214x_putreg(0, LPC214X_USBDEV_INTST); |
| |
| /* Initialize the device state structure */ |
| |
| memset(priv, 0, sizeof(struct lpc214x_usbdev_s)); |
| priv->usbdev.ops = &g_devops; |
| priv->usbdev.ep0 = &priv->eplist[LPC214X_EP0_IN].ep; |
| priv->epavail = LPC214X_EPALLSET; |
| |
| /* Initialize the endpoint list */ |
| |
| for (i = 0; i < LPC214X_NPHYSENDPOINTS; i++) |
| { |
| uint32_t bit = 1 << i; |
| |
| /* Set endpoint operations, reference to driver structure (not |
| * really necessary because there is only one controller), and |
| * the physical endpoint number (which is just the index to the |
| * endpoint). |
| */ |
| |
| priv->eplist[i].ep.ops = &g_epops; |
| priv->eplist[i].dev = priv; |
| |
| /* The index, i, is the physical endpoint address; Map this |
| * to a logical endpoint address usable by the class driver. |
| */ |
| |
| priv->eplist[i].epphy = i; |
| if (LPC214X_EPPHYIN(i)) |
| { |
| priv->eplist[i].ep.eplog = LPC214X_EPPHYIN2LOG(i); |
| } |
| else |
| { |
| priv->eplist[i].ep.eplog = LPC214X_EPPHYOUT2LOG(i); |
| } |
| |
| /* The maximum packet size may depend on the type of endpoint */ |
| |
| if ((LPC214X_EPCTRLSET & bit) != 0) |
| { |
| priv->eplist[i].ep.maxpacket = LPC214X_EP0MAXPACKET; |
| } |
| else if ((LPC214X_EPINTRSET & bit) != 0) |
| { |
| priv->eplist[i].ep.maxpacket = LPC214X_INTRMAXPACKET; |
| } |
| else if ((LPC214X_EPBULKSET & bit) != 0) |
| { |
| priv->eplist[i].ep.maxpacket = LPC214X_BULKMAXPACKET; |
| } |
| else /* if ((LPC214X_EPISOCSET & bit) != 0) */ |
| { |
| priv->eplist[i].ep.maxpacket = LPC214X_ISOCMAXPACKET; |
| } |
| } |
| |
| /* Turn on USB power and clocking */ |
| |
| reg = lpc214x_getreg(LPC214X_PCON_PCONP); |
| reg |= LPC214X_PCONP_PCUSB; |
| lpc214x_putreg(reg, LPC214X_PCON_PCONP); |
| |
| /* Attach USB controller interrupt handler */ |
| |
| if (irq_attach(LPC214X_USB_IRQ, lpc214x_usbinterrupt, NULL) != 0) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_IRQREGISTRATION), |
| (uint16_t)LPC214X_USB_IRQ); |
| goto errout; |
| } |
| |
| /* Enable USB inerrupts at the controller -- but do not disable |
| * the ARM interrupt until the device is bound to the class |
| * driver |
| */ |
| |
| lpc214x_putreg(USBDEV_INTST_ENUSBINTS, LPC214X_USBDEV_INTST); |
| |
| /* Disconnect device */ |
| |
| lpc214x_pullup(&priv->usbdev, false); |
| |
| /* Enable EP0 for OUT (host-to-device) */ |
| |
| lpc214x_usbcmd(CMD_USB_DEV_SETADDRESS, CMD_USB_SETADDRESS_DEVEN | 0); |
| lpc214x_usbcmd(CMD_USB_DEV_SETADDRESS, CMD_USB_SETADDRESS_DEVEN | 0); |
| |
| /* Reset/Re-initialize the USB hardware */ |
| |
| lpc214x_usbreset(priv); |
| |
| /* Init Device state structure */ |
| |
| priv->devstatus = lpc214x_usbcmd(CMD_USB_DEV_GETSTATUS, 0); |
| return; |
| |
| errout: |
| arm_usbuninitialize(); |
| } |
| |
| /**************************************************************************** |
| * Name: arm_usbuninitialize |
| ****************************************************************************/ |
| |
| void arm_usbuninitialize(void) |
| { |
| struct lpc214x_usbdev_s *priv = &g_usbdev; |
| uint32_t reg; |
| irqstate_t flags; |
| |
| usbtrace(TRACE_DEVUNINIT, 0); |
| |
| if (priv->driver) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_DRIVERREGISTERED), 0); |
| usbdev_unregister(priv->driver); |
| } |
| |
| /* Disconnect device */ |
| |
| flags = enter_critical_section(); |
| lpc214x_pullup(&priv->usbdev, false); |
| priv->usbdev.speed = USB_SPEED_UNKNOWN; |
| lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 0); |
| |
| /* Disable and detach IRQs */ |
| |
| up_disable_irq(LPC214X_USB_IRQ); |
| irq_detach(LPC214X_USB_IRQ); |
| |
| /* Turn off USB power and clocking */ |
| |
| reg = lpc214x_getreg(LPC214X_PCON_PCONP); |
| reg &= ~LPC214X_PCONP_PCUSB; |
| lpc214x_putreg(reg, LPC214X_PCON_PCONP); |
| 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(LPC214X_TRACEERR_INVALIDPARMS), 0); |
| return -EINVAL; |
| } |
| |
| if (g_usbdev.driver) |
| { |
| usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_DRIVER), 0); |
| return -EBUSY; |
| } |
| #endif |
| |
| /* First 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(LPC214X_TRACEERR_BINDFAILED), |
| (uint16_t) - ret); |
| g_usbdev.driver = NULL; |
| } |
| else |
| { |
| /* Enable USB controller interrupts */ |
| |
| up_enable_irq(LPC214X_USB_IRQ); |
| } |
| |
| 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(LPC214X_TRACEERR_INVALIDPARMS), 0); |
| return -EINVAL; |
| } |
| #endif |
| |
| /* Unbind the class driver */ |
| |
| CLASS_UNBIND(driver, &g_usbdev.usbdev); |
| |
| /* Disable USB controller interrupts */ |
| |
| up_disable_irq(LPC214X_USB_IRQ); |
| |
| /* Unhook the driver */ |
| |
| g_usbdev.driver = NULL; |
| return OK; |
| } |