From e0610a1ce4bbd2e7809f60c98a0e0b95bd9fe85b Mon Sep 17 00:00:00 2001 From: patacongo Date: Fri, 24 Aug 2012 22:16:09 +0000 Subject: [PATCH] Update STM32 USB OTG FS host driver -- the driver is now marginally functional git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5051 42af7a65-404d-4744-a932-0658087f49c3 --- ChangeLog | 4 + arch/arm/src/lpc17xx/lpc17_usbhost.c | 8 +- arch/arm/src/stm32/stm32_otgfshost.c | 64 +++++++++------ drivers/usbhost/usbhost_storage.c | 113 ++++++++++++++------------- include/nuttx/usb/usbhost.h | 8 +- 5 files changed, 120 insertions(+), 77 deletions(-) diff --git a/ChangeLog b/ChangeLog index 502c0e352c..993719b11d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3173,3 +3173,7 @@ * arch/arm/src/stm32/stm32_otgfshost.c: Renamed from stm32_usbhost.c. This is nearly code complete and, with any luck, will be available in NuttX-6.22. + * configs/*/defconfig: Update all defconfig files to remove syntax + that is incompatible with the mconf configuration tool. + * arch/arm/src/stm32/stm32_otgfshost.c: This driver now appears to be + functional (although more testing is necesary). \ No newline at end of file diff --git a/arch/arm/src/lpc17xx/lpc17_usbhost.c b/arch/arm/src/lpc17xx/lpc17_usbhost.c index 154b953b72..de880cac10 100644 --- a/arch/arm/src/lpc17xx/lpc17_usbhost.c +++ b/arch/arm/src/lpc17xx/lpc17_usbhost.c @@ -2194,7 +2194,13 @@ static int lpc17_ctrlout(FAR struct usbhost_driver_s *drvr, * * Returned Values: * On success, zero (OK) is returned. On a failure, a negated errno value is - * returned indicating the nature of the failure + * returned indicating the nature of the failure: + * + * EAGAIN - If devices NAKs the transfer (or NYET or other error where + * it may be appropriate to restart the entire transaction). + * EPERM - If the endpoint stalls + * EIO - On a TX or data toggle error + * EPIPE - Overrun errors * * Assumptions: * - Only a single class bound to a single device is supported. diff --git a/arch/arm/src/stm32/stm32_otgfshost.c b/arch/arm/src/stm32/stm32_otgfshost.c index 06d35a04ac..d84ba68842 100644 --- a/arch/arm/src/stm32/stm32_otgfshost.c +++ b/arch/arm/src/stm32/stm32_otgfshost.c @@ -201,6 +201,7 @@ struct stm32_chan_s uint8_t epno; /* Device endpoint number (0-127) */ uint8_t eptype; /* See OTGFS_EPTYPE_* definitions */ uint8_t pid; /* Data PID */ + uint8_t npackets; /* Number of packets (for data toggle) */ bool inuse; /* True: This channel is "in use" */ volatile bool indata1; /* IN data toggle. True: DATA01 (Bulk and INTR only) */ volatile bool outdata1; /* OUT data toggle. True: DATA01 */ @@ -1057,6 +1058,13 @@ static void stm32_transfer_start(FAR struct stm32_usbhost_s *priv, int chidx) } #endif + /* Save the number of packets in the transfer. We will need this in + * order to set the next data toggle correctly when the transfer + * completes. + */ + + chan->npackets = (uint8_t)npackets; + /* Setup the HCTSIZn register */ regval = ((uint32_t)chan->buflen << OTGFS_HCTSIZ_XFRSIZ_SHIFT) | @@ -1387,7 +1395,7 @@ static void stm32_gint_wrpacket(FAR struct stm32_usbhost_s *priv, * OK - Transfer completed successfully * EAGAIN - If devices NAKs the transfer or NYET occurs * EPERM - If the endpoint stalls - * EIO - On a TX or data error + * EIO - On a TX or data toggle error * EPIPE - Frame overrun * * EBUSY in the result field indicates that the transfer has not completed. @@ -1405,8 +1413,8 @@ static inline void stm32_gint_hcinisr(FAR struct stm32_usbhost_s *priv, * HCINTMSK register to get the set of enabled HC interrupts. */ - pending = stm32_getreg(STM32_OTGFS_HCINT(chidx)); - regval = stm32_getreg(STM32_OTGFS_HCINTMSK(chidx)); + pending = stm32_getreg(STM32_OTGFS_HCINT(chidx)); + regval = stm32_getreg(STM32_OTGFS_HCINTMSK(chidx)); /* AND the two to get the set of enabled, pending HC interrupts */ @@ -1588,11 +1596,18 @@ static inline void stm32_gint_hcinisr(FAR struct stm32_usbhost_s *priv, else if ((pending & OTGFS_HCINT_NAK) != 0) { + /* For a BULK tranfer, the hardware is capable of retrying + * automatically on a NAK. However, this is not always + * what we need to do. So we always halt the transfer and + * return control to high level logic in the even of a NAK. + */ + +#if 0 /* Halt the interrupt channel */ if (chan->eptype == OTGFS_EPTYPE_CTRL) { - /* Halt the channel -- the CHH interrrupt is expected next */ + /* Halt the channel -- the CHH interrrupt is expected next */ stm32_chan_halt(priv, chidx, CHREASON_NAK); } @@ -1611,7 +1626,11 @@ static inline void stm32_gint_hcinisr(FAR struct stm32_usbhost_s *priv, regval &= ~OTGFS_HCCHAR_CHDIS; stm32_putreg(STM32_OTGFS_HCCHAR(chidx), regval); } +#else + /* Halt all transfers on the NAK -- the CHH interrrupt is expected next */ + stm32_chan_halt(priv, chidx, CHREASON_NAK); +#endif /* Clear the NAK condition */ stm32_putreg(STM32_OTGFS_HCINT(chidx), OTGFS_HCINT_NAK); @@ -1634,7 +1653,7 @@ static inline void stm32_gint_hcinisr(FAR struct stm32_usbhost_s *priv, * OK - Transfer completed successfully * EAGAIN - If devices NAKs the transfer or NYET occurs * EPERM - If the endpoint stalls - * EIO - On a TX or data error + * EIO - On a TX or data toggle error * EPIPE - Frame overrun * * EBUSY in the result field indicates that the transfer has not completed. @@ -1652,8 +1671,8 @@ static inline void stm32_gint_hcoutisr(FAR struct stm32_usbhost_s *priv, * HCINTMSK register to get the set of enabled HC interrupts. */ - pending = stm32_getreg(STM32_OTGFS_HCINT(chidx)); - regval = stm32_getreg(STM32_OTGFS_HCINTMSK(chidx)); + pending = stm32_getreg(STM32_OTGFS_HCINT(chidx)); + regval = stm32_getreg(STM32_OTGFS_HCINTMSK(chidx)); /* AND the two to get the set of enabled, pending HC interrupts */ @@ -1798,11 +1817,14 @@ static inline void stm32_gint_hcoutisr(FAR struct stm32_usbhost_s *priv, regval = stm32_getreg(STM32_OTGFS_HCCHAR(chidx)); - /* Is it a bulk endpoint */ + /* Is it a bulk endpoint? Were an odd number of packets + * transferred? + */ - if ((regval & OTGFS_HCCHAR_EPTYP_MASK) == OTGFS_HCCHAR_EPTYP_BULK) + if ((regval & OTGFS_HCCHAR_EPTYP_MASK) == OTGFS_HCCHAR_EPTYP_BULK && + (chan->npackets & 1) != 0) { - /* Yes... toggle the data out PID */ + /* Yes to both... toggle the data out PID */ chan->outdata1 ^= true; } @@ -3405,7 +3427,13 @@ static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, * * Returned Values: * On success, zero (OK) is returned. On a failure, a negated errno value is - * returned indicating the nature of the failure + * returned indicating the nature of the failure: + * + * EAGAIN - If devices NAKs the transfer (or NYET or other error where + * it may be appropriate to restart the entire transaction). + * EPERM - If the endpoint stalls + * EIO - On a TX or data toggle error + * EPIPE - Overrun errors * * Assumptions: * - Only a single class bound to a single device is supported. @@ -3528,22 +3556,14 @@ static int stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, ret = stm32_chan_wait(priv, chan); - /* EGAIN indicates that the device NAKed the transfer and we need + /* EAGAIN indicates that the device NAKed the transfer and we need * do try again. Anything else (success or other errors) will * cause use to return */ - if (ret != -EAGAIN) + if (ret != OK) { - /* Output some debug info on an error */ - - if (ret < 0) - { - udbg("Transfer failed: %d\n", ret); - } - - /* Break out and return this result */ - + udbg("Transfer failed: %d\n", ret); break; } } diff --git a/drivers/usbhost/usbhost_storage.c b/drivers/usbhost/usbhost_storage.c index 2817c24462..853287371d 100644 --- a/drivers/usbhost/usbhost_storage.c +++ b/drivers/usbhost/usbhost_storage.c @@ -1,8 +1,8 @@ /**************************************************************************** * drivers/usbhost/usbhost_storage.c * - * Copyright (C) 2010-2011 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt + * Copyright (C) 2010-2012 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -123,7 +123,7 @@ struct usbhost_state_s struct usbhost_driver_s *drvr; /* The remainder of the fields are provide to the mass storage class */ - + char sdchar; /* Character identifying the /dev/sd[n] device */ volatile bool disconnected; /* TRUE: Device has been disconnected */ uint8_t ifno; /* Interface number */ @@ -217,7 +217,7 @@ static inline int usbhost_tfree(FAR struct usbhost_state_s *priv); static FAR struct usbmsc_cbw_s *usbhost_cbwalloc(FAR struct usbhost_state_s *priv); /* struct usbhost_registry_s methods */ - + static struct usbhost_class_s *usbhost_create(FAR struct usbhost_driver_s *drvr, FAR const struct usbhost_id_s *id); @@ -248,7 +248,7 @@ static int usbhost_ioctl(FAR struct inode *inode, int cmd, * Private Data ****************************************************************************/ -/* This structure provides the registry entry ID informatino that will be +/* This structure provides the registry entry ID informatino that will be * used to associate the USB host mass storage class to a connected USB * device. */ @@ -416,7 +416,7 @@ static inline void usbhost_freeclass(FAR struct usbhost_state_s *class) flags = irqsave(); class->class.flink = g_freelist; g_freelist = class; - irqrestore(flags); + irqrestore(flags); } #else static inline void usbhost_freeclass(FAR struct usbhost_state_s *class) @@ -700,16 +700,16 @@ static inline int usbhost_testunitready(FAR struct usbhost_state_s *priv) int result; /* Initialize a CBW (re-using the allocated transfer buffer) */ - + cbw = usbhost_cbwalloc(priv); if (!cbw) { udbg("ERROR: Failed to create CBW\n"); return -ENOMEM; } - + /* Construct and send the CBW */ - + usbhost_testunitreadycbw(cbw); result = DRVR_TRANSFER(priv->drvr, priv->bulkout, (uint8_t*)cbw, USBMSC_CBW_SIZEOF); @@ -733,7 +733,7 @@ static inline int usbhost_requestsense(FAR struct usbhost_state_s *priv) int result; /* Initialize a CBW (re-using the allocated transfer buffer) */ - + cbw = usbhost_cbwalloc(priv); if (!cbw) { @@ -742,7 +742,7 @@ static inline int usbhost_requestsense(FAR struct usbhost_state_s *priv) } /* Construct and send the CBW */ - + usbhost_requestsensecbw(cbw); result = DRVR_TRANSFER(priv->drvr, priv->bulkout, (uint8_t*)cbw, USBMSC_CBW_SIZEOF); @@ -775,7 +775,7 @@ static inline int usbhost_readcapacity(FAR struct usbhost_state_s *priv) int result; /* Initialize a CBW (re-using the allocated transfer buffer) */ - + cbw = usbhost_cbwalloc(priv); if (!cbw) { @@ -784,7 +784,7 @@ static inline int usbhost_readcapacity(FAR struct usbhost_state_s *priv) } /* Construct and send the CBW */ - + usbhost_readcapacitycbw(cbw); result = DRVR_TRANSFER(priv->drvr, priv->bulkout, (uint8_t*)cbw, USBMSC_CBW_SIZEOF); @@ -823,7 +823,7 @@ static inline int usbhost_inquiry(FAR struct usbhost_state_s *priv) int result; /* Initialize a CBW (re-using the allocated transfer buffer) */ - + cbw = usbhost_cbwalloc(priv); if (!cbw) { @@ -832,7 +832,7 @@ static inline int usbhost_inquiry(FAR struct usbhost_state_s *priv) } /* Construct and send the CBW */ - + usbhost_inquirycbw(cbw); result = DRVR_TRANSFER(priv->drvr, priv->bulkout, (uint8_t*)cbw, USBMSC_CBW_SIZEOF); @@ -885,7 +885,7 @@ static void usbhost_destroy(FAR void *arg) DEBUGASSERT(priv != NULL); uvdbg("crefs: %d\n", priv->crefs); - + /* Unregister the block driver */ usbhost_mkdevname(priv, devname); @@ -965,10 +965,10 @@ static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv, uint8_t found = 0; int ret; - DEBUGASSERT(priv != NULL && + DEBUGASSERT(priv != NULL && configdesc != NULL && desclen >= sizeof(struct usb_cfgdesc_s)); - + /* Verify that we were passed a configuration descriptor */ cfgdesc = (FAR struct usb_cfgdesc_s *)configdesc; @@ -1004,7 +1004,7 @@ static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv, case USB_DESC_TYPE_INTERFACE: { FAR struct usb_ifdesc_s *ifdesc = (FAR struct usb_ifdesc_s *)configdesc; - + uvdbg("Interface descriptor\n"); DEBUGASSERT(remaining >= USB_SIZEOF_IFDESC); @@ -1078,7 +1078,7 @@ static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv, found |= USBHOST_BINFOUND; /* Save the bulk IN endpoint information */ - + bindesc.addr = epdesc->addr & USB_EP_ADDR_NUMBER_MASK; bindesc.in = 1; bindesc.funcaddr = funcaddr; @@ -1108,7 +1108,7 @@ static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv, } /* Increment the address of the next descriptor */ - + configdesc += desc->len; remaining -= desc->len; } @@ -1116,7 +1116,7 @@ static inline int usbhost_cfgdesc(FAR struct usbhost_state_s *priv, /* Sanity checking... did we find all of things that we need? Hmmm.. I wonder.. * can we work read-only or write-only if only one bulk endpoint found? */ - + if (found != USBHOST_ALLFOUND) { ulldbg("ERROR: Found IF:%s BIN:%s BOUT:%s\n", @@ -1202,7 +1202,7 @@ static inline int usbhost_initvolume(FAR struct usbhost_state_s *priv) uvdbg("Test unit ready, retries=%d\n", retries); /* Send TESTUNITREADY to see the unit is ready */ - + ret = usbhost_testunitready(priv); if (ret == OK) { @@ -1585,7 +1585,7 @@ static FAR struct usbmsc_cbw_s *usbhost_cbwalloc(FAR struct usbhost_state_s *pri * Name: usbhost_create * * Description: - * This function implements the create() method of struct usbhost_registry_s. + * This function implements the create() method of struct usbhost_registry_s. * The create() method is a callback into the class implementation. It is * used to (1) create a new instance of the USB host class state and to (2) * bind a USB host driver "session" to the class instance. Use of this @@ -1644,9 +1644,9 @@ static FAR struct usbhost_class_s *usbhost_create(FAR struct usbhost_driver_s *d priv->drvr = drvr; /* NOTE: We do not yet know the geometry of the USB mass storage device */ - + /* Return the instance of the USB mass storage class */ - + return &priv->class; } } @@ -1701,7 +1701,7 @@ static int usbhost_connect(FAR struct usbhost_class_s *class, FAR struct usbhost_state_s *priv = (FAR struct usbhost_state_s *)class; int ret; - DEBUGASSERT(priv != NULL && + DEBUGASSERT(priv != NULL && configdesc != NULL && desclen >= sizeof(struct usb_cfgdesc_s)); @@ -1722,7 +1722,7 @@ static int usbhost_connect(FAR struct usbhost_class_s *class, udbg("usbhost_initvolume() failed: %d\n", ret); } } - + return ret; } @@ -1792,7 +1792,7 @@ static int usbhost_disconnected(struct usbhost_class_s *class) } } - irqrestore(flags); + irqrestore(flags); return OK; } @@ -1945,45 +1945,52 @@ static ssize_t usbhost_read(FAR struct inode *inode, unsigned char *buffer, ret = -ENOMEM; /* Initialize a CBW (re-using the allocated transfer buffer) */ - + cbw = usbhost_cbwalloc(priv); if (cbw) { - /* Assume some device failure */ + /* Loop in the event that EAGAIN is returned (mean that the + * transaction was NAKed and we should try again. + */ - ret = -ENODEV; - - /* Construct and send the CBW */ - - usbhost_readcbw(startsector, priv->blocksize, nsectors, cbw); - result = DRVR_TRANSFER(priv->drvr, priv->bulkout, - (uint8_t*)cbw, USBMSC_CBW_SIZEOF); - if (result == OK) + do { - /* Receive the user data */ + /* Assume some device failure */ - result = DRVR_TRANSFER(priv->drvr, priv->bulkin, - buffer, priv->blocksize * nsectors); + ret = -ENODEV; + + /* Construct and send the CBW */ + + usbhost_readcbw(startsector, priv->blocksize, nsectors, cbw); + result = DRVR_TRANSFER(priv->drvr, priv->bulkout, + (uint8_t*)cbw, USBMSC_CBW_SIZEOF); if (result == OK) { - /* Receive the CSW */ + /* Receive the user data */ result = DRVR_TRANSFER(priv->drvr, priv->bulkin, - priv->tbuffer, USBMSC_CSW_SIZEOF); + buffer, priv->blocksize * nsectors); if (result == OK) { - FAR struct usbmsc_csw_s *csw; + /* Receive the CSW */ - /* Check the CSW status */ - - csw = (FAR struct usbmsc_csw_s *)priv->tbuffer; - if (csw->status == 0) + result = DRVR_TRANSFER(priv->drvr, priv->bulkin, + priv->tbuffer, USBMSC_CSW_SIZEOF); + if (result == OK) { - ret = nsectors; + FAR struct usbmsc_csw_s *csw; + + /* Check the CSW status */ + + csw = (FAR struct usbmsc_csw_s *)priv->tbuffer; + if (csw->status == 0) + { + ret = nsectors; + } } } } - } + } while (result == -EAGAIN); } usbhost_givesem(&priv->exclsem); @@ -2037,7 +2044,7 @@ static ssize_t usbhost_write(FAR struct inode *inode, const unsigned char *buffe ret = -ENOMEM; /* Initialize a CBW (re-using the allocated transfer buffer) */ - + cbw = usbhost_cbwalloc(priv); if (cbw) { @@ -2046,7 +2053,7 @@ static ssize_t usbhost_write(FAR struct inode *inode, const unsigned char *buffe ret = -ENODEV; /* Construct and send the CBW */ - + usbhost_writecbw(startsector, priv->blocksize, nsectors, cbw); result = DRVR_TRANSFER(priv->drvr, priv->bulkout, (uint8_t*)cbw, USBMSC_CBW_SIZEOF); diff --git a/include/nuttx/usb/usbhost.h b/include/nuttx/usb/usbhost.h index b28db95511..acfe9a829f 100644 --- a/include/nuttx/usb/usbhost.h +++ b/include/nuttx/usb/usbhost.h @@ -448,7 +448,13 @@ * * Returned Values: * On success, zero (OK) is returned. On a failure, a negated errno value is - * returned indicating the nature of the failure + * returned indicating the nature of the failure: + * + * EAGAIN - If devices NAKs the transfer (or NYET or other error where + * it may be appropriate to restart the entire transaction). + * EPERM - If the endpoint stalls + * EIO - On a TX or data toggle error + * EPIPE - Overrun errors * * Assumptions: * This function will *not* be called from an interrupt handler.