Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/*******************************************************************************
* arch/arm/src/lpc214x/lpc214x_usbdev.c
*
* Copyright (C) 2008 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*******************************************************************************/
/*******************************************************************************
* Included Files
*******************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/usb.h>
#include <nuttx/usbdev.h>
#include <nuttx/usbdev_trace.h>
#include <arch/irq.h>
#include <arch/board/board.h>
#include "up_arch.h"
#include "up_internal.h"
#include "lpc214x_usbdev.h"
#include "lpc214x_power.h"
/*******************************************************************************
* 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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/* 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
# 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 */
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#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)
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/* If this bit is set in the lpc214x_epread response, it means that the
* recevied 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 desciptors located at the
* beginning of USB RAM. Each pointer points to a DMA descriptor with
* assocated DMA buffer.
*/
#define USB_UDCA (uint32*)LPC214X_USBDEV_RAMBASE)
#define USB_USCASIZE (LPC214X_NPHYSENDPOINTS*sizeof(uint32))
/* 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))
#else
# define USB_DDESCSIZE (4*sizeof(uint32))
#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)
/* 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) /* Acutally 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;
ubyte epphy; /* Physical EP address */
ubyte eplog; /* Configured logical EP address */
ubyte stalled:1; /* Endpoint is halted */
ubyte halted:1; /* Endpoint feature halted */
ubyte txnullpkt:1; /* Null packet needed at end of transfer */
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
};
/* This represents a DMA descriptor */
#ifdef CONFIG_LPC214X_USBDEV_DMA
struct lpc214x_dmadesc_s
{
uint32 nextdesc; /* Address of the next DMA descripto in RAM */
uint32 config; /* Misc. bit encoded configuration information */
uint32 start; /* DMA start address */
uint32 status; /* Misc. bit encoded status inforamation */
#ifdef CONFIG_USBDEV_ISOCHRONOUS
uint32 size; /* Isochronous packet size address */
#endif
ubyte 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 */
ubyte devstatus; /* Last response to device status command */
ubyte ep0state; /* State of certain EP0 operations */
ubyte paddr; /* Address assigned by SETADDRESS */
ubyte stalled:1; /* 1: Protocol stalled */
ubyte selfpowered:1; /* 1: Device is self powered */
ubyte paddrset:1; /* 1: Peripheral addr has been set */
ubyte attached:1; /* 1: Host attached */
ubyte rxpending:1; /* 1: RX pending */
uint32 softprio; /* Bitset of high priority interrupts */
uint32 epavail; /* Bitset of available endpoints */
#ifdef CONFIG_LPC214X_USBDEV_FRAME_INTERRUPT
uint32 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 ********************************************************/
#if defined(CONFIG_LPC214X_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static uint32 lpc214x_getreg(uint32 addr);
static void lpc214x_putreg(uint32 val, uint32 addr);
#else
# define lpc214x_getreg(addr) getreg32(addr)
# define lpc214x_putreg(val,addr) putreg32(val,addr)
#endif
/* Command operations **********************************************************/
static uint32 lpc214x_usbcmd(uint16 cmd, ubyte data);
/* Request queue operations ****************************************************/
static FAR struct lpc214x_req_s *lpc214x_rqdequeue(FAR struct lpc214x_ep_s *privep);
static void lpc214x_rqenqueue(FAR struct lpc214x_ep_s *privep,
FAR struct lpc214x_req_s *req);
/* Low level data transfers and request operations *****************************/
static void lpc214x_epwrite(ubyte epphy, const ubyte *data, uint32 nbytes);
static int lpc214x_epread(ubyte epphy, ubyte *data, uint32 nbytes);
static inline void lpc214x_abortrequest(struct lpc214x_ep_s *privep,
struct lpc214x_req_s *privreq, sint16 result);
static void lpc214x_reqcomplete(struct lpc214x_ep_s *privep, sint16 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 eplog);
static void lpc214x_eprealize(struct lpc214x_ep_s *privep, boolean prio,
uint32 packetsize);
static ubyte lpc214x_epclrinterrupt(ubyte epphy);
static inline void lpc214x_ep0configure(struct lpc214x_usbdev_s *priv);
#ifdef CONFIG_LPC214X_USBDEV_DMA
static inline void lpc214x_dmareset(uint32 enable);
#endif
static void lpc214x_usbreset(struct lpc214x_usbdev_s *priv);
static void lpc214x_dispatchrequest(struct lpc214x_usbdev_s *priv,
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, FAR void *context);
#ifdef CONFIG_LPC214X_USBDEV_DMA
static int lpc214x_dmasetup(struct lpc214x_usbdev_s *priv, ubyte epphy,
uint32 epmaxsize, uint32 nbytes, uint32 *isocpacket,
boolean isochronous);
static void lpc214x_dmarestart(ubyte epphy, uint32 descndx);
static void lpc214x_dmadisable(ubyte epphy);
#endif /* CONFIG_LPC214X_USBDEV_DMA */
/* Endpoint operations *********************************************************/
static int lpc214x_epconfigure(FAR struct usbdev_ep_s *ep,
const struct usb_epdesc_s *desc);
static int lpc214x_epdisable(FAR struct usbdev_ep_s *ep);
static FAR struct usbdev_req_s *lpc214x_epallocreq(FAR struct usbdev_ep_s *ep);
static void lpc214x_epfreereq(FAR struct usbdev_ep_s *ep,
FAR struct usbdev_req_s *);
#ifdef CONFIG_LPC214X_USBDEV_DMA
static FAR void *lpc214x_epallocbuffer(FAR struct usbdev_ep_s *ep,
uint16 nbytes);
static void lpc214x_epfreebuffer(FAR struct usbdev_ep_s *ep, void *buf);
#endif
static int lpc214x_epsubmit(FAR struct usbdev_ep_s *ep,
struct usbdev_req_s *req);
static int lpc214x_epcancel(FAR struct usbdev_ep_s *ep,
struct usbdev_req_s *req);
static int lpc214x_epstall(FAR struct usbdev_ep_s *ep, boolean resume);
/* USB device controller operations ********************************************/
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
static FAR struct usbdev_ep_s *lcp214x_allocep(FAR struct usbdev_s *dev,
ubyte epno, boolean in, ubyte eptype);
static void lpc214x_freeep(FAR struct usbdev_s *dev, FAR 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, boolean selfpowered);
static int lpc214x_pullup(struct usbdev_s *dev, boolean 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_LPC214X_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
*
*******************************************************************************/
#if defined(CONFIG_LPC214X_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static uint32 lpc214x_getreg(uint32 addr)
{
static uint32 prevaddr = 0;
static uint32 preval = 0;
static uint32 count = 0;
/* Read the value from the register */
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
/* Is this the same value that we read from the same registe last time? Are
* we polling the register? If so, suppress some of the output.
*/
if (addr == prevaddr || val == preval)
{
if (count == 0xffffffff || ++count > 3)
{
if (count == 4)
{
lldbg("...\n");
}
return val;
}
}
/* No this is a new address or value */
else
{
/* Did we print "..." for the previous value? */
if (count > 3)
{
/* Yes.. then show how many times the value repeated */
lldbg("[repeats %d more times]\n", count-3);
}
/* Save the new address, value, and count */
prevaddr = addr;
preval = val;
count = 1;
}
/* Show the register value read */
lldbg("%08x->%08x\n", addr, val);
return val;
}
#endif
/*******************************************************************************
* Name: lpc214x_putreg
*
* Description:
* Set the contents of an LPC214x register to a value
*
*******************************************************************************/
#if defined(CONFIG_LPC214X_USBDEV_REGDEBUG) && defined(CONFIG_DEBUG)
static void lpc214x_putreg(uint32 val, uint32 addr)
{
/* Show the register value being written */
lldbg("%08x<-%08x\n", addr, val);
/* Write the value */
putreg32(val, addr);
}
#endif
/*******************************************************************************
* Name: lpc214x_usbcmd
*
* Description:
* Transmit commands to the USB engine
*
*******************************************************************************/
static uint32 lpc214x_usbcmd(uint16 cmd, ubyte data)
{
irqstate_t flags;
uint32 tmp = 0;
/* Disable interrupt and clear CDFULL and CCEMPTY interrupt status */
flags = irqsave();
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);
/* 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);
}
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;
}
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);
}
/* 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 */
irqrestore(flags);
return tmp;
}
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
/*******************************************************************************
* Name: lpc214x_rqdequeue
*
* Description:
* Remove a request from an endpoint request queue
*
*******************************************************************************/
static FAR struct lpc214x_req_s *lpc214x_rqdequeue(FAR struct lpc214x_ep_s *privep)
{
FAR 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(FAR struct lpc214x_ep_s *privep,
FAR 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(ubyte epphy, const ubyte *data, uint32 nbytes)
{
uint32 value;
boolean aligned = (((uint32)data & 3) == 0);
/* Set the write enable bit for this physical EP address */
lpc214x_putreg(((epphy << 1) & LPC214X_USBCTRL_EPMASK) | LPC214X_USBCTRL_WREN,
LPC214X_USBDEV_CTRL);
/* Set the transmit packet length (nbytes must be less than 2048) */
/* Transfer the packet data */
do
{
/* Zero length packets are a special case */
if (nbytes)
{
if (aligned)
{
value = *(uint32*)data;
}
else
{
value = (uint32)data[0] | ((uint32)data[1] << 8) |
((uint32)data[2] << 16) | ((uint32)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);
(void)lpc214x_usbcmd(CMD_USB_EP_SELECT | epphy, 0);
(void)lpc214x_usbcmd(CMD_USB_EP_VALIDATEBUFFER, 0);
}
/*******************************************************************************
* Name: lpc214x_epread
*
* Description:
* Endpoint read (OUT)
*
*******************************************************************************/
static int lpc214x_epread(ubyte epphy, ubyte *data, uint32 nbytes)
{
uint32 pktlen;
uint32 result;
uint32 value;
ubyte 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)data & 3) == 0)
{
aligned = 1;
}
else
{
aligned = 2;
}
}
/* Set the read enable bit for this physical EP address */
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);
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*)data = value;
data += 4;
}
else if (aligned == 2)
*data++ = (ubyte)value;
*data++ = (ubyte)(value >> 8);
*data++ = (ubyte)(value >> 16);
*data++ = (ubyte)(value >> 24);
}
}
/* Done */
(void)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 recevied 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,
sint16 result)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_REQABORTED), (uint16)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, sint16 result)
{
struct lpc214x_req_s *privreq;
int stalled = privep->stalled;
irqstate_t flags;
/* Remove the complete request at the head of the endpoint request list */
flags = irqsave();
irqrestore(flags);
if (privreq)
{
/* If endpoint 0, temporarily reflect the state of protocol stalled
* in the callback.
*/
privep->stalled = privep->dev->stalled;
}
/* Save the result in the request structure */
privreq->req.result = result;
/* Callback to the request completion handler */
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
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;
ubyte *buf;
int nbytes;
int bytesleft;
/* Check the request from the head of the endpoint request queue */
usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPINQEMPTY), 0);
uvdbg("len=%d xfrd=%d nullpkt=%d\n",
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... Unless the EP supports double buffering, only one packet may be sent
/* Get the number of bytes left to be sent in the packet */
bytesleft = privreq->req.len - privreq->req.xfrd;
/* Send the next packet if (1) there are more bytes to be sent, or
* (2) the last packet sent was exactly maxpacketsize (bytesleft == 0)
*/
usbtrace(TRACE_WRITE(privep->epphy), privreq->req.xfrd);
if (bytesleft > 0 || privep->txnullpkt)
{
/* Try to send maxpacketsize -- unless we don't have that many
* bytes to send.
*/
if (bytesleft > privep->ep.maxpacket)
{
nbytes = privep->ep.maxpacket;
privep->txnullpkt = 0;
}
else
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 (bytesleft <= 0 || !privep->txnullpkt)
{
usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd);
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
lpc214x_reqcomplete(privep, OK);
return OK;
}
}
return OK; /* Won't get here */
}
/*******************************************************************************
* 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;
ubyte *buf;
int nbytesread;
/* Check the request from the head of the endpoint request queue */
usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPOUTQEMPTY), 0);
uvdbg("len=%d xfrd=%d 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;
}
#warning REVISIT... Unless the EP supports double buffering, only one packet may be received
usbtrace(TRACE_READ(privep->epphy), privreq->req.xfrd);
for (;;)
{
/* Receive the next packet if (1) there are more bytes to be receive, or
* (2) the last packet was exactly maxpacketsize.
*/
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);
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
return ERROR;
}
/* If the receive buffer is full or if the last packet was not full
* then we are finished with the transfer.
*/
privreq->req.xfrd += nbytesread;
if (privreq->req.len < privreq->req.xfrd || nbytesread < privep->ep.maxpacket)
{
usbtrace(TRACE_COMPLETE(privep->epphy), privreq->req.xfrd);
lpc214x_reqcomplete(privep, OK);
return OK;
}
}
return OK; /* Won't get here */
}
/*******************************************************************************
* Name: lpc214x_cancelrequests
*
* Description:
* Cancel all pending requests for an endpoint
*
*******************************************************************************/
static void lpc214x_cancelrequests(struct lpc214x_ep_s *privep)
{
lpc214x_reqcomplete(privep, -ESHUTDOWN);
}
}
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
/*******************************************************************************
* 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 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->eplog)
{
/* Return endpoint found */
return privep;
}
}
/* Return endpoint not found */
return NULL;
}
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
/*******************************************************************************
* Name: lpc214x_eprealize
*
* Description:
* Enable or disable an endpoint
*
*******************************************************************************/
static void lpc214x_eprealize(struct lpc214x_ep_s *privep, boolean prio, uint32 packetsize)
{
struct lpc214x_usbdev_s *priv = privep->dev;
uint32 mask;
uint32 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 ubyte lpc214x_epclrinterrupt(ubyte 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);
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
}
/*******************************************************************************
* Name: lpc214x_ep0configure
*
* Description:
* Configure endpoint 0
*
*******************************************************************************/
static inline void lpc214x_ep0configure(struct lpc214x_usbdev_s *priv)
{
uint32 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 enable)
{
int i;
/* Disable All DMA interrupts */
lpc214x_putreg(0, LPC214X_USBDEV_DMAINTEN);
lpc214x_putreg(0xffffffff, LPC214X_USBDEV_EPDMADIS);
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
/* 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)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)
{
/* 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);
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
/* Periperhal address is needed */
priv->paddrset = 0;
/* Endpoints not yet configured */
lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 0);
/* EndPoint 0 initialization */
lpc214x_ep0configure(priv);
#ifdef CONFIG_LPC214X_USBDEV_DMA
/* Enable End_of_Transfer_Interrupt and System_Error_Interrupt USB DMA
* interrupts
*/
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,
{
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);
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_req_s *privreq = lpc214x_rqpeek(ep0);
ubyte 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 */
{
sint16 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, (ubyte*)&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);
uvdbg("type=%02x req=%02x value=%04x index=%04x len=%04x\n",
/* Dispatch any non-standard requests */
if ((ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD)
return;
}
/* Handle standard request. Pick off the things of interest to the
* USB device controller driver; pass what is left to the class driver
*/
{
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
{
{
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)
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
{
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)
else if (priv->paddrset != 0 && value == USB_FEATURE_ENDPOINTHALT && len == 0 &&
(privep = lpc214x_epfindbyaddr(priv, index)) != NULL)
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) &&
else if ((ctrl.type & USB_REQ_RECIPIENT_MASK) != USB_REQ_RECIPIENT_ENDPOINT)
else if (priv->paddrset != 0 && value == USB_FEATURE_ENDPOINTHALT && len == 0 &&
(privep = lpc214x_epfindbyaddr(priv, index)) != NULL)
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 &&
/* 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
*/
/* 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)
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 &&
{
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 &&
{
lpc214x_dispatchrequest(priv, &ctrl);
}
else
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADSETCONFIG), 0);
priv->stalled = 1;
}
}
break;
/* type: device-to-host; recipient = interface
* value: 0
* index: interface;
* len: 1; data = alt interface
*/
/* 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);
}
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDCTRLREQ), 0);
priv->stalled = 1;
}
break;
}
if (priv->stalled)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EP0SETUPSTALLED), priv->ep0state);
lpc214x_epstall(&ep0->ep, FALSE);
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 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;
/* 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);
lpc214x_epstall(&ep0->ep, FALSE);
}
return;
}
/*******************************************************************************
* 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)priv->paddr);
lpc214x_usbcmd(CMD_USB_DEV_CONFIG, 0);
priv->ep0state = LPC214X_EP0REQUEST;
case LPC214X_EP0REQUEST:
{
/* Process the next request action (if any) */
lpc214x_wrrequest(&priv->eplist[LPC214X_EP0_IN]);
}
break;
default:
priv->stalled = 1;
break;
}
if (priv->stalled)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_EP0INSTALLED), priv->ep0state);
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
ep0 = &priv->eplist[LPC214X_EP0_IN];
lpc214x_epstall(&ep0->ep, FALSE);
lpc214x_epstall(&ep0->ep, FALSE);
}
}
/*******************************************************************************
* Name: lpc214x_usbinterrupt
*
* Description:
* USB interrupt handler
*
*******************************************************************************/
static int lpc214x_usbinterrupt(int irq, FAR void *context)
{
struct lpc214x_usbdev_s *priv = &g_usbdev;
struct lpc214x_ep_s *privep ;
uint32 devintstatus; /* Sampled state of the device interrupt status register */
uint32 epintstatus; /* Sampled state of the endpoint interrupt status register */
#ifdef CONFIG_LPC214X_USBDEV_DMA
uint32 dmaintstatus; /* Sampled state of dma interrupt status register */
#endif
uint32 softprio; /* Current priority interrupt bitset */
uint32 pending; /* Pending subset of priority interrupt bitset */
ubyte 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
#if CONFIG_DEBUG
/* USB engine error interrupt */
if ((devintstatus & USBDEV_DEVINT_EPRINT))
{
ubyte errcode;
/* Clear the error interrupt */
lpc214x_putreg(USBDEV_DEVINT_EPRINT, LPC214X_USBDEV_DEVINTCLR);
/* And show what error occurred */
errcode = (ubyte)lpc214x_usbcmd(CMD_USB_DEV_READERRORSTATUS, 0) & 0x0f;
usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPRINT), (uint16)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)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 = (ubyte)lpc214x_usbcmd(CMD_USB_DEV_GETSTATUS, 0);
usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_DEVSTAT), (uint16)g_usbdev.devstatus);
/* Device connection status */
if (DEVSTATUS_CONNCHG(g_usbdev.devstatus))
{
usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_CONNECTCHG),
(uint16)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)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)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)g_usbdev.devstatus);
}
/* Device reset */
if (DEVSTATUS_RESET(g_usbdev.devstatus))
{
usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_DEVRESET),
(uint16)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);
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
/* 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 throught 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 result = lpc214x_epclrinterrupt(LPC214X_CTRLEP_OUT);
if (result & USBDEV_EPSETUPPACKET)
{
usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EP0SETUP), (uint16)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);
(void)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 */
(void)lpc214x_epclrinterrupt(epphy);
/* Get the endpoint sructure 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)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) */
}
else
{
/* OUT: host-to-device */
usbtrace(TRACE_INTDECODE(LPC214X_TRACEINTID_EPIN), (uint16)epphy);
/* Read host data into the current read request */
{
lpc214x_rdrequest(privep);
}
else
{
uvdbg("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 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);
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
/* 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 throught 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)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, ubyte epphy,
uint32 epmaxsize, uint32 nbytes, uint32 *isocpacket,
boolean isochronous);
{
struct lpc214x_dmadesc_s *dmadesc = priv;
uint32 reg;
#ifdef CONFIG_DEBUG
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);
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
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)&dmadesc->buffer;
dmadesc->status = 0;
#ifdef CONFIG_USBDEV_ISOCHRONOUS
dmadesc->size = (uint32)packet;
#endif
/* Enable DMA tranfer 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))
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
{
/* 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(ubyte epphy, uint32 descndx)
{
uint32 reg;
/* Clear DMA descriptor status */
USB_DmaDesc[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 reg = lpc214x_usbcmd(CMD_USB_EP_SELECT | epphy, 0);
if ((LPC214X_EPPHYIN(epphy) && (reg & 0x60) == 0) ||
(LPC214X_EPPHYIN(epphy) && (reg & 0x60) == 0x60))
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
{
/* 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(ubyte epphy)
{
EPDMADIS = 1 << epphy;
}
#endif /* CONFIG_LPC214X_USBDEV_DMA */
/*******************************************************************************
* Endpoint operations
*******************************************************************************/
/*******************************************************************************
* Name: lpc214x_epconfigure
*
* Description:
* Configure endpoint, making it usable
*
*******************************************************************************/
static int lpc214x_epconfigure(FAR struct usbdev_ep_s *ep,
FAR const struct usb_epdesc_s *desc)
{
FAR struct lpc214x_ep_s *privep = (FAR struct lpc214x_ep_s *)ep;
uint32 inten;
usbtrace(TRACE_EPCONFIGURE, privep->epphy);
/* Save the logical EP number (so that we can map logical to physical later) */
privep->eplog = desc->addr;
/* 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);
lpc214x_putreg(inten, LPC214X_USBDEV_EPINTEN);
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
#endif
return OK;
}
/*******************************************************************************
* Name: lpc214x_epdisable
*
* Description:
* The endpoint will no longer be used
*
*******************************************************************************/
static int lpc214x_epdisable(FAR struct usbdev_ep_s *ep)
{
FAR struct lpc214x_ep_s *privep = (FAR struct lpc214x_ep_s *)ep;
irqstate_t flags;
uint32 mask = (1 << privep->epphy);
uint32 reg;
#ifdef CONFIG_DEBUG
if (!ep)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0);
return -EINVAL;
}
#endif
usbtrace(TRACE_EPDISABLE, privep->epphy);
/* Cancel any ongoing activity */
flags = irqsave();
lpc214x_cancelrequests(privep);
/* Disable endpoint and interrupt */
reg = lpc214x_getreg(LPC214X_USBDEV_REEP);
lpc214x_putreg(reg, LPC214X_USBDEV_REEP);
lpc214x_putreg(mask, LPC214X_USBDEV_EPDMADIS);
reg = lpc214x_getreg(LPC214X_USBDEV_EPINTEN);
lpc214x_putreg(reg, LPC214X_USBDEV_EPINTEN);
irqrestore(flags);
return OK;
}
/*******************************************************************************
* Name: lpc214x_epallocreq
*
* Description:
* Allocate an I/O request
*
*******************************************************************************/
static FAR struct usbdev_req_s *lpc214x_epallocreq(FAR struct usbdev_ep_s *ep)
{
FAR struct lpc214x_req_s *privreq;
#ifdef CONFIG_DEBUG
if (!ep)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0);
return NULL;
}
#endif
usbtrace(TRACE_EPALLOCREQ, ((FAR struct lpc214x_ep_s *)ep)->epphy);
privreq = (FAR struct lpc214x_req_s *)malloc(sizeof(struct lpc214x_req_s));
if (!privreq)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_ALLOCFAIL), 0);
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
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(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
{
FAR struct lpc214x_req_s *privreq = (FAR struct lpc214x_req_s *)req;
#ifdef CONFIG_DEBUG
if (!ep || !req)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0);
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
return;
}
#endif
usbtrace(TRACE_EPFREEREQ, ((FAR struct lpc214x_ep_s *)ep)->epphy);
free(privreq);
}
/*******************************************************************************
* Name: lpc214x_epallocbuffer
*
* Description:
* Allocate an I/O buffer
*
*******************************************************************************/
#ifdef CONFIG_LPC214X_USBDEV_DMA
static FAR void *lpc214x_epallocbuffer(FAR struct usbdev_ep_s *ep, uint16 nbytes)
{
FAR struct lpc214x_ep_s *privep = (FAR 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]
}
#endif
/*******************************************************************************
* Name: lpc214x_epfreebuffer
*
* Description:
* Free an I/O buffer
*
*******************************************************************************/
#ifdef CONFIG_LPC214X_USBDEV_DMA
static void lpc214x_epfreebuffer(FAR struct usbdev_ep_s *ep, FAR void *buf)
{
FAR struct lpc214x_ep_s *privep = (FAR 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"
}
#endif
/*******************************************************************************
* Name: lpc214x_epsubmit
*
* Description:
* Submit an I/O request to the endpoint
*
*******************************************************************************/
static int lpc214x_epsubmit(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
{
FAR struct lpc214x_req_s *privreq = (FAR struct lpc214x_req_s *)req;
FAR struct lpc214x_ep_s *privep = (FAR struct lpc214x_ep_s *)ep;
FAR struct lpc214x_usbdev_s *priv;
irqstate_t flags;
int ret = OK;
#ifdef CONFIG_DEBUG
if (!req || !req->callback || !req->buf || !ep)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0);
uvdbg("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->xfrd = 0;
flags = irqsave();
/* If we are stalled, then drop all requests on the floor */
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);
ret = lpc214x_wrrequest(privep);
}
/* Handle OUT (host-to-device) requests */
/* Add the new request to the request queue for the OUT endpoint */
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;
}
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
}
irqrestore(flags);
return ret;
}
/*******************************************************************************
* Name: lpc214x_epcancel
*
* Description:
* Cancel an I/O request previously sent to an endpoint
*
*******************************************************************************/
static int lpc214x_epcancel(FAR struct usbdev_ep_s *ep, FAR struct usbdev_req_s *req)
{
FAR struct lpc214x_ep_s *privep = (FAR struct lpc214x_ep_s *)ep;
FAR struct lpc214x_usbdev_s *priv;
irqstate_t flags;
#ifdef CONFIG_DEBUG
if (!ep || !req)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0);
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
return -EINVAL;
}
#endif
usbtrace(TRACE_EPCANCEL, privep->epphy);
priv = privep->dev;
flags = irqsave();
lpc214x_cancelrequests(privep);
irqrestore(flags);
return OK;
}
/*******************************************************************************
* Name: lpc214x_epstall
*
* Description:
* Stall or resume and endpoint
*
*******************************************************************************/
static int lpc214x_epstall(FAR struct usbdev_ep_s *ep, boolean resume)
{
FAR struct lpc214x_ep_s *privep = (FAR struct lpc214x_ep_s *)ep;
usbtrace(resume ? TRACE_EPRESUME : TRACE_EPSTALL, privep->epphy);
lpc214x_usbcmd(CMD_USB_EP_SETSTATUS | privep->epphy, (resume ? 0 : USBDEV_EPSTALL));
return OK;
}
/*******************************************************************************
* Device operations
*******************************************************************************/
/*******************************************************************************
* Name: lcp214x_allocep
*
* Description:
* Allocate an endpoint matching the parameters.
*
* Input Parameters:
* epphy - 7-bit physical endpoint number (without direction bit). Zero means
* that any endpoint matching the other requirements will suffice.
* in - TRUE: IN (device-to-host) endpoint requested
* eptype - Endpoint type. One of {USB_EP_ATTR_XFER_ISOC, USB_EP_ATTR_XFER_BULK,
* USB_EP_ATTR_XFER_INT}
*
*******************************************************************************/
static FAR struct usbdev_ep_s *lcp214x_allocep(FAR struct usbdev_s *dev, ubyte epphy,
boolean in, ubyte eptype)
{
FAR struct lpc214x_usbdev_s *priv = (FAR struct lpc214x_usbdev_s *)dev;
uint32 epset = LPC214X_EPALLSET;
irqstate_t flags;
int epndx = 0;
/* epphy=0 means that any endpoint will do */
if (epphy > 0)
{
if (epphy >= LPC214X_NLOGENDPOINTS)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADEPNO), (uint16)epphy);
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
return NULL;
}
epset &= 3 << epphy;
}
/* 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 */
break;
case USB_EP_ATTR_XFER_CONTROL: /* Control endpoint -- not a valid choice */
default:
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_BADEPTYPE), (uint16)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 = irqsave();
epset &= priv->epavail;
if (epset)
{
/* Select the lowest bit in the set of matching, available endpoints */
for (epndx = 2; epndx < LPC214X_NPHYSENDPOINTS; epndx++)
{
uint32 bit = 1 << epndx;
{
/* Mark the endpoint no longer available */
irqrestore(flags);
/* And return the pointer to the standard endpoint structure */
return &priv->eplist[epndx].ep;
}
}
/* Shouldn't get here */
}
irqrestore(flags);
}
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_NOEP), (uint16)epphy);
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
return NULL;
}
/*******************************************************************************
* Name: lpc214x_freeep
*
* Description:
* Free the previously allocated endpoint
*
*******************************************************************************/
static void lpc214x_freeep(FAR struct usbdev_s *dev, FAR struct usbdev_ep_s *ep)
{
FAR struct lpc214x_usbdev_s *priv = (FAR struct lpc214x_usbdev_s *)dev;
FAR struct lpc214x_ep_s *privep = (FAR struct lpc214x_ep_s *)ep;
irqstate_t flags;
usbtrace(TRACE_DEVFREEEP, (uint16)privep->epphy);
if (priv && privep)
{
/* Mark the endpoint as available */
flags = irqsave();
priv->epavail |= (1 << privep->epphy);
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
irqrestore(flags);
}
}
/*******************************************************************************
* Name: lpc214x_getframe
*
* Description:
* Returns the current frame number
*
*******************************************************************************/
static int lpc214x_getframe(struct usbdev_s *dev)
{
#ifdef CONFIG_LPC214X_USBDEV_FRAME_INTERRUPT
FAR struct lpc214x_usbdev_s *priv = (FAR struct lpc214x_usbdev_s *)dev;
/* Return last valid value of SOF read by the interrupt handler */
usbtrace(TRACE_DEVGETFRAME, (uint16)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)
{
ubyte arg = USBDEV_DEVSTATUS_SUSPEND;
irqstate_t flags;
usbtrace(TRACE_DEVWAKEUP, (uint16)g_usbdev.devstatus);
flags = irqsave();
if (DEVSTATUS_CONNECT(g_usbdev.devstatus))
{
arg |= USBDEV_DEVSTATUS_CONNECT;
}
lpc214x_usbcmd(CMD_USB_DEV_SETSTATUS, arg);
irqrestore(flags);
return OK;
}
/*******************************************************************************
* Name: lpc214x_selfpowered
*
* Description:
* Sets/clears the device selfpowered feature
*
*******************************************************************************/
static int lpc214x_selfpowered(struct usbdev_s *dev, boolean selfpowered)
{
FAR struct lpc214x_usbdev_s *priv = (FAR struct lpc214x_usbdev_s *)dev;
usbtrace(TRACE_DEVSELFPOWERED, (uint16)selfpowered);
#ifdef CONFIG_DEBUG
if (!dev)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0);
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
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, boolean enable)
{
usbtrace(TRACE_DEVPULLUP, (uint16)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: up_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 up_usbinitialize(void)
{
struct lpc214x_usbdev_s *priv = &g_usbdev;
uint32 reg;
usbtrace(TRACE_DEVINIT, 0);
lpc214x_putreg(0, LPC214X_USBDEV_INTST);
memset(priv, 0, sizeof(struct lpc214x_usbdev_s));
priv->usbdev.ops = &g_devops;
priv->usbdev.ep0 = &priv->eplist[LPC214X_EP0_IN].ep;
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
/* Initialize the endpoint list */
for (i = 0; i < LPC214X_NPHYSENDPOINTS; i++)
{
uint32 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;
priv->eplist[i].epphy = 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);
lpc214x_putreg(reg, LPC214X_PCON_PCONP);
/* Attach USB controller interrupt handler */
if (irq_attach(LPC214X_USB_IRQ, lpc214x_usbinterrupt) != 0)
{
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_IRQREGISTRATION),
(uint16)LPC214X_USB_IRQ);
/* 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:
up_usbuninitialize();
}
/*******************************************************************************
*******************************************************************************/
{
struct lpc214x_usbdev_s *priv = &g_usbdev;
uint32 reg;
irqstate_t flags;
usbtrace(TRACE_DEVUNINIT, 0);
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_DRIVERREGISTERED), 0);
usbdev_unregister(priv->driver);
}
/* Disconnect device */
flags = irqsave();
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);
irqrestore(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);
if (!driver || !driver->ops->bind || !driver->ops->unbind ||
!driver->ops->disconnect || !driver->ops->setup)
usbtrace(TRACE_DEVERROR(LPC214X_TRACEERR_INVALIDPARMS), 0);
}
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)-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
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);