diff options
author | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2010-12-20 20:59:12 +0000 |
---|---|---|
committer | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2010-12-20 20:59:12 +0000 |
commit | cd668b716373867f0eb5705819953b9fee5d7e47 (patch) | |
tree | 6b08df9ec6433af3fb3632fd8c305b6ac90cd4ef /nuttx/arch/arm | |
parent | f097b55fce492cbc2e232e5406f0421d59183cc9 (diff) | |
download | px4-nuttx-cd668b716373867f0eb5705819953b9fee5d7e47.tar.gz px4-nuttx-cd668b716373867f0eb5705819953b9fee5d7e47.tar.bz2 px4-nuttx-cd668b716373867f0eb5705819953b9fee5d7e47.zip |
Add a little more
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3201 42af7a65-404d-4744-a932-0658087f49c3
Diffstat (limited to 'nuttx/arch/arm')
-rwxr-xr-x | nuttx/arch/arm/src/lpc17xx/lpc17_ohciram.h | 23 | ||||
-rwxr-xr-x | nuttx/arch/arm/src/lpc17xx/lpc17_usbhost.c | 383 |
2 files changed, 365 insertions, 41 deletions
diff --git a/nuttx/arch/arm/src/lpc17xx/lpc17_ohciram.h b/nuttx/arch/arm/src/lpc17xx/lpc17_ohciram.h index 431a8d8d3..5607e35d6 100755 --- a/nuttx/arch/arm/src/lpc17xx/lpc17_ohciram.h +++ b/nuttx/arch/arm/src/lpc17xx/lpc17_ohciram.h @@ -144,7 +144,12 @@ #if CONFIG_USBHOST_TDBUFFERS > 0 && !defined(CONFIG_USBHOST_TDBUFSIZE)
# define CONFIG_USBHOST_TDBUFSIZE 128
#endif
-#define LPC17_TDBUFFER_SIZE (CONFIG_USBHOST_TDBUFFERS * CONFIG_USBHOST_TDBUFSIZE)
+
+#if (CONFIG_USBHOST_TDBUFSIZE & 3) != 0
+# error "TD buffer size must be an even number of 32-bit words"
+#endif
+
+#define LPC17_TDFREE_SIZE (CONFIG_USBHOST_TDBUFFERS * CONFIG_USBHOST_TDBUFSIZE)
/* Configurable size of an IO buffer. The number of IO buffers will be determined
* by what is left at the end of the BANK1 memory setup aside of OHCI RAM.
@@ -154,6 +159,10 @@ # define CONFIG_USBHOST_IOBUFSIZE 512
#endif
+#if (CONFIG_USBHOST_IOBUFSIZE & 3) != 0
+# error "IO buffer size must be an even number of 32-bit words"
+#endif
+
/* OHCI Memory Layout ***************************************************************/
/* Example:
* Hardware:
@@ -171,8 +180,8 @@ * Sizes of things
* CONFIG_USBHOST_NEDS 2
* LPC17_EDFREE_SIZE 48
- * LPC17_TDBUFFER_SIZE 128
- * LPC17_TDBUFFER_SIZE 512
+ * LPC17_TDFREE_SIZE 128
+ * LPC17_IOFREE_SIZE 512
*
* Memory Layout
* LPC17_OHCIRAM_END (0x20008000 + 16384) = 0x2000c000
@@ -186,8 +195,8 @@ * LPC17_TDTAIL_ADDR 0x2000bd10
* LPC17_EDCTRL_ADDR 0x2000bd20
* LPC17_EDFREE_BASE 0x2000bd30
- * LPC17_TDBUFFER_BASE 0x2000bd50
- * LPC17_IOBUFFER_BASE 0x2000bdd0
+ * LPC17_TDFREE_BASE 0x2000bd50
+ * LPC17_IOFREE_BASE 0x2000bdd0
* LPC17_IOBUFFERS (0x2000c000 + 0x2000bdd0) / 512 = 560/512 = 1
*
* Wasted memory: 560-512 = 48 bytes
@@ -198,8 +207,8 @@ #define LPC17_TDTAIL_ADDR (LPC17_TDHEAD_ADDR + LPC17_TD_SIZE)
#define LPC17_EDCTRL_ADDR (LPC17_TDTAIL_ADDR + LPC17_TD_SIZE)
#define LPC17_EDFREE_BASE (LPC17_EDCTRL_ADDR + LPC17_ED_SIZE)
-#define LPC17_TDBUFFER_BASE (LPC17_EDFREE_BASE + LPC17_EDFREE_SIZE)
-#define LPC17_IOBUFFER_BASE (LPC17_TDBUFFER_BASE + LPC17_TDBUFFER_SIZE)
+#define LPC17_TDFREE_BASE (LPC17_EDFREE_BASE + LPC17_EDFREE_SIZE)
+#define LPC17_IOFREE_BASE (LPC17_TDFREE_BASE + LPC17_TDFREE_SIZE)
/* Finally, use the remainder of the allocated OHCI for IO buffers */
diff --git a/nuttx/arch/arm/src/lpc17xx/lpc17_usbhost.c b/nuttx/arch/arm/src/lpc17xx/lpc17_usbhost.c index c2cb49f57..d2bc45dde 100755 --- a/nuttx/arch/arm/src/lpc17xx/lpc17_usbhost.c +++ b/nuttx/arch/arm/src/lpc17xx/lpc17_usbhost.c @@ -104,7 +104,7 @@ #define EDCTRL ((volatile struct lpc17_hced_s *)LPC17_EDCTRL_ADDR) #define EDFREE ((struct lpc17_hced_s *)LPC17_EDFREE_BASE) -#define TDBuffer ((volatile uint8_t *)(LPC17_TDBUFFER_BASE) +#define TDFREE ((uint8_t *)LPC17_TDFREE_BASE) /* Descriptors *****************************************************************/ @@ -141,8 +141,10 @@ struct lpc17_usbhost_s /* Driver status */ - uint8_t tdstatus; /* TD control status bits from last Writeback Done Head event */ - sem_t wdhsem; /* Semaphore used to wait for Writeback Done Head event */ + volatile uint8_t tdstatus; /* TD control status bits from last Writeback Done Head event */ + volatile bool connected; /* Connected to device */ + sem_t rhssem; /* Semaphore to wait Root Hub Status change */ + sem_t wdhsem; /* Semaphore used to wait for Writeback Done Head event */ }; /* Host Controller Communication Area */ @@ -176,7 +178,7 @@ struct lpc17_hced_s volatile uint32_t next; /* Physical address of next Endpoint descriptor */ }; -/* The following is used manage a list of free EDs */ +/* The following are used to manage lists of free EDs and TD buffers*/ struct lpc17_edlist_s { @@ -184,6 +186,12 @@ struct lpc17_edlist_s uint32_t pad[3]; /* To make the same size as struct lpc17_hced_s */ }; +struct lpc17_tdlist_s +{ + struct lpc17_tdlist_s *flink; /* Link to next TD buffer in the list */ + /* Variable length buffer data follows */ +}; + /******************************************************************************* * Private Function Prototypes *******************************************************************************/ @@ -217,6 +225,8 @@ static void lpc17_edfree(struct lpc17_usbhost_s *priv, struct lpc17_hced_s *ed); static void lpc17_enqueuetd(volatile struct lpc17_hced_s *ed, uint32_t dirpid, uint32_t toggle, volatile uint8_t *buffer, size_t buflen); +static int lpc17_ctrltd(struct lpc17_usbhost_s *priv, uint32_t dirpid, + uint8_t *buffer, size_t buflen); /* Class helper functions ******************************************************/ @@ -277,9 +287,10 @@ static struct lpc17_usbhost_s g_usbhost = .class = NULL, }; -/* This is a free list of EDs */ +/* This is a free list of EDs and TD buffers */ static struct lpc17_edlist_s *g_edfree; +static struct lpc17_tdlist_s *g_tdfree; /******************************************************************************* * Public Data @@ -418,7 +429,7 @@ static void lpc17_putreg(uint32_t val, uint32_t addr) * This is just a wrapper to handle the annoying behavior of semaphore * waits that return due to the receipt of a signal. * - ****************************************************************************/ + *******************************************************************************/ static void lpc17_takesem(sem_t *sem) { @@ -440,7 +451,7 @@ static void lpc17_takesem(sem_t *sem) * Description: * Get a (possibly unaligned) 16-bit little endian value. * - ****************************************************************************/ + *******************************************************************************/ static inline uint16_t lpc17_getle16(const uint8_t *val) { @@ -453,7 +464,7 @@ static inline uint16_t lpc17_getle16(const uint8_t *val) * Description: * Put a (possibly unaligned) 16-bit little endian value. * - ****************************************************************************/ + *******************************************************************************/ static void lpc17_putle16(uint8_t *dest, uint16_t val) { @@ -495,6 +506,39 @@ static void lpc17_edfree(struct lpc17_usbhost_s *priv, struct lpc17_hced_s *ed) } /******************************************************************************* + * Name: lpc17_tdalloc + * + * Description: + * Allocate an TD buffer from the free list + * + *******************************************************************************/ + +static uint8_t *lpc17_tdalloc(struct lpc17_usbhost_s *priv) +{ + uint8_t *ret = (uint8_t *)g_tdfree; + if (ret) + { + g_tdfree = ((struct lpc17_tdlist_s*)ret)->flink; + } + return ret; +} + +/******************************************************************************* + * Name: lpc17_tdfree + * + * Description: + * Return an TD buffer to the free list + * + *******************************************************************************/ + +static void lpc17_tdfree(struct lpc17_usbhost_s *priv, uint8_t *buffer) +{ + struct lpc17_tdlist_s *tdfree = (struct lpc17_tdlist_s *)buffer; + tdfree->flink = g_tdfree; + g_tdfree = tdfree; +} + +/******************************************************************************* * Name: lpc17_enqueuetd * * Description: @@ -519,14 +563,73 @@ static void lpc17_enqueuetd(volatile struct lpc17_hced_s *ed, uint32_t dirpid, ed->next = 0; } -/**************************************************************************** - * Name: lpc17_classbind +/******************************************************************************* + * Name: lpc17_configdesc + * + * Description: + * Process a IN or OUT request on the control endpoint. This function + * will enqueue the request and wait for it to complete. Only one transfer + * may be queued; Neither these methods nor the transfer() method can be + * called again until the control transfer functions returns. + * + * These are blocking methods; these functions will not return until the + * control transfer has completed. + * + *******************************************************************************/ + +static int lpc17_ctrltd(struct lpc17_usbhost_s *priv, uint32_t dirpid, + uint8_t *buffer, size_t buflen) +{ + uint32_t toggle; + uint32_t regval; + + if (dirpid == GTD_STATUS_DP_SETUP) + { + toggle = GTD_STATUS_T_DATA0; + } + else + { + toggle = GTD_STATUS_T_DATA1; + } + + /* Then enqueue the transfer */ + + lpc17_enqueuetd(EDCTRL, dirpid, toggle, buffer, buflen); + + lpc17_putreg(LPC17_EDCTRL_ADDR, LPC17_USBHOST_CTRLHEADED); + + regval = lpc17_getreg(LPC17_USBHOST_CMDST); + regval |= OHCI_CMDST_CLF; + lpc17_putreg(regval, LPC17_USBHOST_CMDST); + + regval = lpc17_getreg(LPC17_USBHOST_CTRL); + regval |= OHCI_CTRL_CLE; + lpc17_putreg(regval, LPC17_USBHOST_CTRL); + + /* Wait for the Writeback Done Head interrupt */ + + lpc17_takesem(&priv->wdhsem); + + /* Check the TDHEAD completion status bits */ + + if (priv->tdstatus == 0) + { + return OK; + } + else + { + return -EIO; + } +} + +/******************************************************************************* + * Name: lpc17_configdesc * * Description: * A configuration descriptor has been obtained from the device. Find the * ID information for the class that supports this device. * - ****************************************************************************/ + *******************************************************************************/ static inline int lpc17_configdesc(struct lpc17_usbhost_s *priv, const uint8_t *configdesc, @@ -591,14 +694,14 @@ lpc17_configdesc(struct lpc17_usbhost_s *priv, const uint8_t *configdesc, return -ENOENT; } -/**************************************************************************** +/******************************************************************************* * Name: lpc17_classbind * * Description: * A configuration descriptor has been obtained from the device. Try to * bind this configuration descriptor with a supported class. * - ****************************************************************************/ + *******************************************************************************/ static int lpc17_classbind(struct lpc17_usbhost_s *priv, const uint8_t *configdesc, int desclen) @@ -664,7 +767,114 @@ static int lpc17_usbinterrupt(int irq, FAR void *context) /* Read the device interrupt status register */ usbtrace(TRACE_INTENTRY(LPC17_TRACEINTID_USB), 0xbeef); -#warning "Not implemented" + uint32_t intstatus; + uint32_t intenable; + + /* Read Interrupt Status and mask out interrupts that are not enabled. */ + + intstatus = lpc17_getreg(LPC17_USBHOST_INTST); + intenable = lpc17_getreg(LPC17_USBHOST_INTEN); + intstatus &= intenable; + + if (intstatus != 0) + { + /* Root hub status change interrupt */ + + if ((intstatus & OHCI_INT_RHSC) != 0) + { + uint32_t rhportst1 = lpc17_getreg(LPC17_USBHOST_RHPORTST1); + ullvdbg("Root Hub Status Change, RHPORTST: %08x\n", portstatus); + + if ((rhportst1 & OHCI_RHPORTST_CSC) != 0) + { + uint32_t rhstatus = lpc17_getreg(LPC17_USBHOST_RHSTATUS); + ullvdbg("Connect Status Change, RHSTATUS: %08x\n", portstatus); + + /* If DRWE is set, Connect Status Change indicates a remote wake-up event */ + + if (rhstatus & OHCI_RHSTATUS_DRWE) + { + ullvdbg("DRWE: Remote wake-up\n"); + } + + /* Otherwise... Not a remote wake-up event */ + + else + { + /* Check if we are not connected */ + + if ((rhportst1 & OHCI_RHPORTST_CCS) != 0) + { + if (!priv->connected) + { + DEBUGASSERT(priv->rhssem.semcount <= 0); + priv->tdstatus = 0; + priv->connected = true; + lpc17_givesem(&priv->rhssem); + } + else + { + ulldbg("Spurious status change (connected)\n"); + } + } + + /* Check if we are now disconnected */ + + else if (priv->connected) + { + /* Yes.. disable interrupts */ + + lpc17_putreg(0, LPC17_USBHOST_INTEN); + priv->connected = false; + + /* Are we bound to a class instance? */ + + if (priv->class) + { + /* Yes.. Disconnect the class */ + + CLASS_DISCONNECTED(priv->class); + } + } + else + { + ulldbg("Spurious status change (disconnected)\n"); + } + } + + /* Clear the CSC interrupt */ + + lpc17_putreg(OHCI_RHPORTST_CSC, LPC17_USBHOST_RHPORTST1); + } + + /* Check for port reset status change */ + + if ((rhportst1 & OHCI_RHPORTST_PRSC) != 0) + { + /* Release the RH port from reset */ + + lpc17_putreg(OHCI_RHPORTST_PRSC, LPC17_USBHOST_RHPORTST1); + } + } + + /* Writeback Done Head interrupt */ + + if ((intstatus & OHCI_INT_WDH) != 0) + { + /* Get the condition code from the control word */ + + priv->tdstatus = (TDHEAD->ctrl & GTD_STATUS_CC_MASK) >> GTD_STATUS_CC_SHIFT; + + /* And wake up the thread waiting for the WDH event */ + + DEBUGASSERT(priv->wdhsem.semcount <= 0); + lpc17_givesem(&priv->wdhsem); + } + + /* Clear interrupt status register */ + + lpc17_putreg(intstatus, LPC17_USBHOST_INTST); + } usbtrace(TRACE_INTEXIT(LPC17_TRACEINTID_USB), 0); return OK; } @@ -673,7 +883,7 @@ static int lpc17_usbinterrupt(int irq, FAR void *context) * USB Host Controller Operations *******************************************************************************/ -/************************************************************************************ +/******************************************************************************* * Name: lpc17_enumerate * * Description: @@ -698,7 +908,7 @@ static int lpc17_usbinterrupt(int irq, FAR void *context) * Assumptions: * This function will *not* be called from an interrupt handler. * - ************************************************************************************/ + *******************************************************************************/ static int lpc17_enumerate(FAR struct usbhost_driver_s *drvr) { @@ -706,7 +916,7 @@ static int lpc17_enumerate(FAR struct usbhost_driver_s *drvr) return -ENOSYS; } -/************************************************************************************ +/******************************************************************************* * Name: lpc17_alloc * * Description: @@ -730,7 +940,7 @@ static int lpc17_enumerate(FAR struct usbhost_driver_s *drvr) * Assumptions: * This function will *not* be called from an interrupt handler. * - ************************************************************************************/ + *******************************************************************************/ static int lpc17_alloc(FAR struct usbhost_driver_s *drvr, FAR uint8_t **buffer, FAR size_t *maxlen) @@ -739,7 +949,7 @@ static int lpc17_enumerate(FAR struct usbhost_driver_s *drvr) return -ENOSYS; } -/************************************************************************************ +/******************************************************************************* * Name: lpc17_free * * Description: @@ -760,7 +970,7 @@ static int lpc17_enumerate(FAR struct usbhost_driver_s *drvr) * Assumptions: * This function will *not* be called from an interrupt handler. * - ************************************************************************************/ + *******************************************************************************/ static int lpc17_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) { @@ -768,12 +978,12 @@ static int lpc17_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) return -ENOSYS; } -/************************************************************************************ +/******************************************************************************* * Name: lpc17_ctrlin and lpc17_ctrlout * * Description: * Process a IN or OUT request on the control endpoint. These methods - * will enqueue the request and return immediately. Only one transfer may be + * will enqueue the request and wait for it to complete. Only one transfer may be * queued; Neither these methods nor the transfer() method can be called again * until the control transfer functions returns. * @@ -797,25 +1007,59 @@ static int lpc17_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) * Assumptions: * This function will *not* be called from an interrupt handler. * - ************************************************************************************/ + *******************************************************************************/ static int lpc17_ctrlin(FAR struct usbhost_driver_s *drvr, FAR const struct usb_ctrlreq_s *req, FAR uint8_t *buffer) { -# warning "Not Implemented" - return -ENOSYS; + struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)drvr; + uint16_t len; + int ret; + + len = lpc17_getle16(req->len); + ret = lpc17_ctrltd(priv, GTD_STATUS_DP_SETUP, (uint8_t*)req, USB_SIZEOF_CTRLREQ); + if (ret == OK) + { + if (len) + { + ret = lpc17_ctrltd(priv, GTD_STATUS_DP_IN, buffer, len); + } + + if (ret == OK) + { + ret = lpc17_ctrltd(priv, GTD_STATUS_DP_OUT, NULL, 0); + } + } + return ret; } static int lpc17_ctrlout(FAR struct usbhost_driver_s *drvr, FAR const struct usb_ctrlreq_s *req, FAR const uint8_t *buffer) { -# warning "Not Implemented" - return -ENOSYS; + struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)drvr; + uint16_t len; + int ret; + + len = lpc17_getle16(req->len); + ret = lpc17_ctrltd(priv, GTD_STATUS_DP_SETUP, (uint8_t*)req, USB_SIZEOF_CTRLREQ); + if (ret == OK) + { + if (len) + { + ret = lpc17_ctrltd(priv, GTD_STATUS_DP_OUT, (uint8_t*)buffer, len); + } + + if (ret == OK) + { + ret = lpc17_ctrltd(priv, GTD_STATUS_DP_IN, NULL, 0); + } + } + return ret; } -/************************************************************************************ +/******************************************************************************* * Name: lpc17_transfer * * Description: @@ -843,17 +1087,75 @@ static int lpc17_ctrlout(FAR struct usbhost_driver_s *drvr, * Assumptions: * This function will *not* be called from an interrupt handler. * - ************************************************************************************/ + *******************************************************************************/ static int lpc17_transfer(FAR struct usbhost_driver_s *drvr, - FAR struct usbhost_epdesc_s *ed, + FAR struct usbhost_epdesc_s *ep, FAR uint8_t *buffer, size_t buflen) { -# warning "Not Implemented" - return -ENOSYS; + struct lpc17_usbhost_s *priv = (struct lpc17_usbhost_s *)drvr; + struct lpc17_hced_s *ed; + uint32_t dirpid; + uint32_t regval; + int ret = -ENOMEM; + + /* Allocate an ED */ + + ed = lpc17_edalloc(priv); + if (ed) + { + /* Format the endpoint descriptor */ + + lpc17_edinit(ed); + ed->ctrl = (uint32_t)(ep->addr) << ED_CONTROL_EN_SHIFT | + (uint32_t)(ep->mxpacketsize) << ED_CONTROL_MPS_SHIFT; + + /* Get the direction of the endpoint */ + + if (ep->in) + { + ed->ctrl |= ED_CONTROL_D_IN; + dirpid = GTD_STATUS_DP_IN; + } + else + { + ed->ctrl |= ED_CONTROL_D_OUT; + dirpid = GTD_STATUS_DP_OUT; + } + + /* Then enqueue the transfer */ + + lpc17_enqueuetd(ed, dirpid, GTD_STATUS_T_TOGGLE, buffer, buflen); + + lpc17_putreg((uint32_t)ed, LPC17_USBHOST_BULKHEADED); + + regval = lpc17_getreg(LPC17_USBHOST_CMDST); + regval |= OHCI_CMDST_BLF; + lpc17_putreg(regval, LPC17_USBHOST_CMDST); + + regval = lpc17_getreg(LPC17_USBHOST_CTRL); + regval |= OHCI_CTRL_BLE; + lpc17_putreg(regval, LPC17_USBHOST_CTRL); + + /* Wait for the Writeback Done Head interrupt */ + + lpc17_takesem(&priv->wdhsem); + + /* Check the TDHEAD completion status bits */ + + if (priv->tdstatus == 0) + { + ret = OK; + } + else + { + ret = -EIO; + } + } + return ret; } -/************************************************************************************ +/******************************************************************************* * Name: lpc17_disconnect * * Description: @@ -873,7 +1175,7 @@ static int lpc17_transfer(FAR struct usbhost_driver_s *drvr, * Assumptions: * This function will *not* be called from an interrupt handler. * - ************************************************************************************/ + *******************************************************************************/ static void lpc17_disconnect(FAR struct usbhost_driver_s *drvr) { @@ -933,6 +1235,7 @@ void up_usbhostinitialize(void) { struct lpc17_usbhost_s *priv = &g_usbhost; uint32_t regval; + uint8_t *tdfree; irqstate_t flags; int i; @@ -940,6 +1243,7 @@ void up_usbhostinitialize(void) /* Initialize the state data structure */ + sem_init(&priv->rhssem, 0, 0); sem_init(&priv->wdhsem, 0, 0); /* Enable power by setting PCUSB in the PCONP register */ @@ -995,6 +1299,17 @@ void up_usbhostinitialize(void) lpc17_edfree(priv, &EDFREE[i]); } + /* Initialize user-configurable TD buffers */ + + tdfree = TDFREE; + for (i = 0; i < CONFIG_USBHOST_NEDS; i++) + { + /* Put the TD buffer in a free list */ + + lpc17_tdfree(priv, tdfree); + tdfree += CONFIG_USBHOST_TDBUFSIZE; + } + /* Wait 50MS then perform hardware reset */ up_mdelay(50); |