From d4feb7205fd8ea1b63094ec00677de73ac5d5a15 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 29 Apr 2015 15:53:58 -0600 Subject: [PATCH] First cut, dirty conversion of EFM32, STM32 FS and HS host drivers to the new interfaces --- arch/arm/src/efm32/efm32_usbhost.c | 1433 +++++++++++++++++++------ arch/arm/src/lpc31xx/lpc31_ehci.c | 1 + arch/arm/src/stm32/stm32_otgfshost.c | 1473 +++++++++++++++++++------ arch/arm/src/stm32/stm32_otghshost.c | 1475 ++++++++++++++++++++------ 4 files changed, 3357 insertions(+), 1025 deletions(-) diff --git a/arch/arm/src/efm32/efm32_usbhost.c b/arch/arm/src/efm32/efm32_usbhost.c index decd4ed9cb..8d60d530fe 100644 --- a/arch/arm/src/efm32/efm32_usbhost.c +++ b/arch/arm/src/efm32/efm32_usbhost.c @@ -1,7 +1,7 @@ /******************************************************************************* * arch/arm/src/efm32/efm32_usbhost.c * - * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Copyright (C) 2014-2015 Gregory Nutt. All rights reserved. * Authors: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -54,6 +54,7 @@ #include #include #include +#include #include #include @@ -138,7 +139,6 @@ #define EFM32_MAX_TX_FIFOS 15 /* Max number of TX FIFOs */ #define EFM32_MAX_PKTCOUNT 256 /* Max packet count */ #define EFM32_RETRY_COUNT 3 /* Number of ctrl transfer retries */ -#define EFM32_DEF_DEVADDR 0 /* Default device address */ /* Delays **********************************************************************/ @@ -201,6 +201,8 @@ struct efm32_chan_s volatile uint8_t chreason; /* Channel halt reason. See enum efm32_chreason_e */ uint8_t epno; /* Device endpoint number (0-127) */ uint8_t eptype; /* See EFM32_USB_EPTYPE_* definitions */ + uint8_t funcaddr; /* Device function address */ + uint8_t speed; /* Device speed */ uint8_t pid; /* Data PID */ uint8_t npackets; /* Number of packets (for data toggle) */ bool inuse; /* True: This channel is "in use" */ @@ -212,6 +214,21 @@ struct efm32_chan_s volatile uint16_t buflen; /* Buffer length (remaining) */ volatile uint16_t inflight; /* Number of Tx bytes "in-flight" */ FAR uint8_t *buffer; /* Transfer buffer pointer */ +#ifdef CONFIG_USBHOST_ASYNCH + usbhost_asynch_t callback; /* Transfer complete callback */ + FAR void *arg; /* Argument that accompanies the callback */ +#endif +}; + +/* A channel represents on uni-directional endpoint. So, in the case of the + * bi-directional, control endpoint, there must be two channels to represent + * the endpoint. + */ + +struct efm32_ctrlinfo_s +{ + uint8_t inndx; /* EP0 IN control channel index */ + uint8_t outndx; /* EP0 OUT control channel index */ }; /* This structure retains the state of the USB host controller */ @@ -225,23 +242,26 @@ struct efm32_usbhost_s struct usbhost_driver_s drvr; - /* The bound device class driver */ + /* This is the hub port description understood by class drivers */ - struct usbhost_class_s *class; + struct usbhost_roothubport_s rhport; /* Overall driver status */ volatile uint8_t smstate; /* The state of the USB host state machine */ - uint8_t devaddr; /* Device address */ - uint8_t ep0in; /* EP0 IN control channel index */ - uint8_t ep0out; /* EP0 OUT control channel index */ - uint8_t ep0size; /* EP0 max packet size */ uint8_t chidx; /* ID of channel waiting for space in Tx FIFO */ - bool lowspeed; /* True: low speed device */ volatile bool connected; /* Connected to device */ - volatile bool eventwait; /* True: Thread is waiting for a port event */ + volatile bool change; /* Connection change */ + volatile bool pscwait; /* True: Thread is waiting for a port event */ sem_t exclsem; /* Support mutually exclusive access */ - sem_t eventsem; /* Semaphore to wait for a port event */ + sem_t pscsem; /* Semaphore to wait for a port event */ + struct efm32_ctrlinfo_s ep0; /* Root hub port EP0 description */ + +#ifdef CONFIG_USBHOST_HUB + /* Used to pass external hub port events */ + + volatile struct usbhost_hubport_s *hport; +#endif /* The state of each host channel */ @@ -292,10 +312,24 @@ static void efm32_chan_halt(FAR struct efm32_usbhost_s *priv, int chidx, enum efm32_chreason_e chreason); static int efm32_chan_waitsetup(FAR struct efm32_usbhost_s *priv, FAR struct efm32_chan_s *chan); +#ifdef CONFIG_USBHOST_ASYNCH +static int efm32_chan_asynchsetup(FAR struct efm32_usbhost_s *priv, + FAR struct efm32_chan_s *chan, + usbhost_asynch_t callback, FAR void *arg); +#endif static int efm32_chan_wait(FAR struct efm32_usbhost_s *priv, FAR struct efm32_chan_s *chan); static void efm32_chan_wakeup(FAR struct efm32_usbhost_s *priv, FAR struct efm32_chan_s *chan); +static int efm32_ctrlchan_alloc(FAR struct efm32_usbhost_s *priv, + uint8_t epno, uint8_t funcaddr, uint8_t speed, + FAR struct efm32_ctrlinfo_s *ctrlep); +static int efm32_ctrlep_alloc(FAR struct efm32_usbhost_s *priv, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep); +static int efm32_xfrep_alloc(FAR struct efm32_usbhost_s *priv, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep); /* Control/data transfer logic *************************************************/ @@ -304,15 +338,34 @@ static void efm32_transfer_start(FAR struct efm32_usbhost_s *priv, int chidx); static inline uint16_t efm32_getframe(void); #endif static int efm32_ctrl_sendsetup(FAR struct efm32_usbhost_s *priv, + FAR struct efm32_ctrlinfo_s *ep0, FAR const struct usb_ctrlreq_s *req); static int efm32_ctrl_senddata(FAR struct efm32_usbhost_s *priv, + FAR struct efm32_ctrlinfo_s *ep0, FAR uint8_t *buffer, unsigned int buflen); static int efm32_ctrl_recvdata(FAR struct efm32_usbhost_s *priv, + FAR struct efm32_ctrlinfo_s *ep0, FAR uint8_t *buffer, unsigned int buflen); +static int efm32_in_setup(FAR struct efm32_usbhost_s *priv, int chidx); static int efm32_in_transfer(FAR struct efm32_usbhost_s *priv, int chidx, FAR uint8_t *buffer, size_t buflen); +#ifdef CONFIG_USBHOST_ASYNCH +static void stm32_in_next(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan); +static int stm32_in_asynch(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg); +#endif +static int efm32_out_setup(FAR struct efm32_usbhost_s *priv, int chidx); static int efm32_out_transfer(FAR struct efm32_usbhost_s *priv, int chidx, FAR uint8_t *buffer, size_t buflen); +#ifdef CONFIG_USBHOST_ASYNCH +static void stm32_out_next(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan); +static int stm32_out_asynch(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg); +#endif /* Interrupt handling **********************************************************/ /* Lower level interrupt handlers */ @@ -353,13 +406,16 @@ static void efm32_txfe_enable(FAR struct efm32_usbhost_s *priv, int chidx); /* USB host controller operations **********************************************/ static int efm32_wait(FAR struct usbhost_connection_s *conn, - FAR const bool *connected); -static int efm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx); + FAR struct usbhost_hubport_s **hport); +static int efm32_rh_enumerate(FAR struct efm32_usbhost_s *priv, + FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport); +static int efm32_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport); -static int efm32_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr, +static int efm32_ep0configure(FAR struct usbhost_driver_s *drvr, + usbhost_ep_t ep0, uint8_t funcaddr, uint8_t speed, uint16_t maxpacketsize); -static int efm32_getdevinfo(FAR struct usbhost_driver_s *drvr, - FAR struct usbhost_devinfo_s *devinfo); static int efm32_epalloc(FAR struct usbhost_driver_s *drvr, FAR const FAR struct usbhost_epdesc_s *epdesc, FAR usbhost_ep_t *ep); @@ -370,14 +426,28 @@ static int efm32_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer); static int efm32_ioalloc(FAR struct usbhost_driver_s *drvr, FAR uint8_t **buffer, size_t buflen); static int efm32_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer); -static int efm32_ctrlin(FAR struct usbhost_driver_s *drvr, - FAR const struct usb_ctrlreq_s *req, +static int efm32_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + const struct usb_ctrlreq_s *req, FAR uint8_t *buffer); -static int efm32_ctrlout(FAR struct usbhost_driver_s *drvr, +static int efm32_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, FAR const struct usb_ctrlreq_s *req, FAR const uint8_t *buffer); +static int efm32_transfer_common(FAR struct efm32_usbhost_s *priv, + FAR struct efm32_ed_s *ed,FAR uint8_t *buffer, + size_t buflen); static int efm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, FAR uint8_t *buffer, size_t buflen); +#ifdef CONFIG_USBHOST_ASYNCH +static int efm32_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg); +static int efm32_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); +#endif +#ifdef CONFIG_USBHOST_HUB +static int efm32_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport, + bool connected); +#endif static void efm32_disconnect(FAR struct usbhost_driver_s *drvr); /* Initialization **************************************************************/ @@ -400,25 +470,7 @@ static inline int efm32_hw_initialize(FAR struct efm32_usbhost_s *priv); * instance. */ -static struct efm32_usbhost_s g_usbhost = -{ - .drvr = - { - .ep0configure = efm32_ep0configure, - .getdevinfo = efm32_getdevinfo, - .epalloc = efm32_epalloc, - .epfree = efm32_epfree, - .alloc = efm32_alloc, - .free = efm32_free, - .ioalloc = efm32_ioalloc, - .iofree = efm32_iofree, - .ctrlin = efm32_ctrlin, - .ctrlout = efm32_ctrlout, - .transfer = efm32_transfer, - .disconnect = efm32_disconnect, - }, - .class = NULL, -}; +static struct efm32_usbhost_s g_usbhost; /* This is the connection/enumeration interface */ @@ -692,6 +744,7 @@ static inline void efm32_chan_freeall(FAR struct efm32_usbhost_s *priv) static void efm32_chan_configure(FAR struct efm32_usbhost_s *priv, int chidx) { + FAR struct efm32_chan_s *chan = &priv->chan[chidx]; uint32_t regval; /* Clear any old pending interrupts for this host channel. */ @@ -702,7 +755,7 @@ static void efm32_chan_configure(FAR struct efm32_usbhost_s *priv, int chidx) regval = 0; - switch (priv->chan[chidx].eptype) + switch (chan->eptype) { case EFM32_USB_EPTYPE_CTRL: case EFM32_USB_EPTYPE_BULK: @@ -713,7 +766,7 @@ static void efm32_chan_configure(FAR struct efm32_usbhost_s *priv, int chidx) /* Determine the definitive trace ID to use below */ - if (priv->chan[chidx].eptype == EFM32_USB_EPTYPE_CTRL) + if (chan->eptype == EFM32_USB_EPTYPE_CTRL) { intrace = OTGFS_VTRACE2_CHANCONF_CTRL_IN; outtrace = OTGFS_VTRACE2_CHANCONF_CTRL_OUT; @@ -733,14 +786,14 @@ static void efm32_chan_configure(FAR struct efm32_usbhost_s *priv, int chidx) /* Additional setting for IN/OUT endpoints */ - if (priv->chan[chidx].in) + if (chan->in) { - usbhost_vtrace2(intrace, chidx, priv->chan[chidx].epno); + usbhost_vtrace2(intrace, chidx, chan->epno); regval |= USB_HC_INTMSK_BBLERRMSK; } else { - usbhost_vtrace2(outtrace, chidx, priv->chan[chidx].epno); + usbhost_vtrace2(outtrace, chidx, chan->epno); } } break; @@ -755,17 +808,17 @@ static void efm32_chan_configure(FAR struct efm32_usbhost_s *priv, int chidx) /* Additional setting for IN endpoints */ - if (priv->chan[chidx].in) + if (chan->in) { usbhost_vtrace2(OTGFS_VTRACE2_CHANCONF_INTR_IN, chidx, - priv->chan[chidx].epno); + chan->epno); regval |= USB_HC_INTMSK_BBLERRMSK; } #ifdef HAVE_USBHOST_TRACE_VERBOSE else { usbhost_vtrace2(OTGFS_VTRACE2_CHANCONF_INTR_OUT, chidx, - priv->chan[chidx].epno); + chan->epno); } #endif } @@ -780,17 +833,17 @@ static void efm32_chan_configure(FAR struct efm32_usbhost_s *priv, int chidx) /* Additional setting for IN endpoints */ - if (priv->chan[chidx].in) + if (chan->in) { usbhost_vtrace2(OTGFS_VTRACE2_CHANCONF_ISOC_IN, chidx, - priv->chan[chidx].epno); + chan->epno); regval |= (USB_HC_INTMSK_XACTERRMSK | USB_HC_INTMSK_BBLERRMSK); } #ifdef HAVE_USBHOST_TRACE_VERBOSE else { usbhost_vtrace2(OTGFS_VTRACE2_CHANCONF_ISOC_OUT, chidx, - priv->chan[chidx].epno); + chan->epno); } #endif } @@ -809,28 +862,28 @@ static void efm32_chan_configure(FAR struct efm32_usbhost_s *priv, int chidx) /* Program the HCCHAR register */ - regval = ((uint32_t)priv->chan[chidx].maxpacket << _USB_HC_CHAR_MPS_SHIFT) | - ((uint32_t)priv->chan[chidx].epno << _USB_HC_CHAR_EPNUM_SHIFT) | - ((uint32_t)priv->chan[chidx].eptype << _USB_HC_CHAR_EPTYPE_SHIFT) | - ((uint32_t)priv->devaddr << _USB_HC_CHAR_DEVADDR_SHIFT); + regval = ((uint32_t)chan->maxpacket << _USB_HC_CHAR_MPS_SHIFT) | + ((uint32_t)chan->epno << _USB_HC_CHAR_EPNUM_SHIFT) | + ((uint32_t)chan->eptype << _USB_HC_CHAR_EPTYPE_SHIFT) | + ((uint32_t)chan->funcaddr << _USB_HC_CHAR_DEVADDR_SHIFT); /* Special case settings for low speed devices */ - if (priv->lowspeed) + if (chan->speed == USB_SPEED_LOW) { regval |= USB_HC_CHAR_LSPDDEV; } /* Special case settings for IN endpoints */ - if (priv->chan[chidx].in) + if (chan->in) { regval |= USB_HC_CHAR_EPDIR_IN; } /* Special case settings for INTR endpoints */ - if (priv->chan[chidx].eptype == EFM32_USB_EPTYPE_INTR) + if (chan->eptype == EFM32_USB_EPTYPE_INTR) { regval |= USB_HC_CHAR_ODDFRM; } @@ -953,14 +1006,59 @@ static int efm32_chan_waitsetup(FAR struct efm32_usbhost_s *priv, * either (1) the device is disconnected, or (2) the transfer completed. */ - chan->waiter = true; - ret = OK; + chan->waiter = true; +#ifdef CONFIG_USBHOST_ASYNCH + chan->callback = NULL; + chan->arg = NULL; +#endif + ret = OK; } irqrestore(flags); return ret; } +/******************************************************************************* + * Name: efm32_chan_asynchsetup + * + * Description: + * Set the request for the transfer complete event well BEFORE enabling the + * transfer (as soon as we are absolutely committed to the to avoid transfer). + * We do this to minimize race conditions. This logic would have to be expanded + * if we want to have more than one packet in flight at a time! + * + * Assumptions: + * Might be called from the level of an interrupt handler + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int efm32_chan_asynchsetup(FAR struct efm32_usbhost_s *priv, + FAR struct efm32_chan_s *chan. + usbhost_asynch_t callback, FAR void *arg) +{ + irqstate_t flags = irqsave(); + int ret = -ENODEV; + + /* Is the device still connected? */ + + if (priv->connected) + { + /* Yes.. then set waiter to indicate that we expect to be informed when + * either (1) the device is disconnected, or (2) the transfer completed. + */ + + chan->waiter = false; + chan->callback = callback; + chan->arg = arg; + ret = OK; + } + + irqrestore(flags); + return ret; +} +#endif + /******************************************************************************* * Name: efm32_chan_wait * @@ -1032,19 +1130,246 @@ static int efm32_chan_wait(FAR struct efm32_usbhost_s *priv, static void efm32_chan_wakeup(FAR struct efm32_usbhost_s *priv, FAR struct efm32_chan_s *chan) { - /* Is the transfer complete? Is there a thread waiting for this transfer - * to complete? + /* Is the transfer complete? */ + + if (chan->result != EBUSY) + { + /* Is there a thread waiting for this transfer to complete? */ + + if (chan->waiter) + { +#ifdef CONFIG_USBHOST_ASYNCH + /* Yes.. there should not also be a callback scheduled */ + + DEBUGASSERT(chan->callback == NULL); +#endif + /* Wake'em up! */ + + usbhost_vtrace2(chan->in ? OTGFS_VTRACE2_CHANWAKEUP_IN : + OTGFS_VTRACE2_CHANWAKEUP_OUT, + chan->epno, chan->result); + + efm32_givesem(&chan->waitsem); + chan->waiter = false; + } + +#ifdef CONFIG_USBHOST_ASYNCH + /* No.. is an asynchronous callback expected when the transfer + * completes? + */ + + else if (chan->callback) + { + /* Handle continuation of IN/OUT pipes */ + + if (priv->in) + { + stm32_in_next(priv, chan); + } + else + { + stm32_out_next(priv, chan); + } + } +#endif + } +} + +/******************************************************************************* + * Name: efm32_ctrlchan_alloc + * + * Description: + * Allocate and configured channels for a control pipe. + * + *******************************************************************************/ + +static int efm32_ctrlchan_alloc(FAR struct efm32_usbhost_s *priv, + uint8_t epno, uint8_t funcaddr, uint8_t speed, + FAR struct efm32_ctrlinfo_s *ctrlep) +{ + FAR struct efm32_chan_s *chan; + int inndx; + int outndx; + + outndx = efm32_chan_alloc(priv); + if (outndx < 0) + { + return -ENOMEM; + } + + ctrlep->outndx = outndx; + chan = &priv->chan[outndx]; + chan->epno = epno; + chan->in = false; + chan->eptype = EFM32_USB_EPTYPE_CTRL; + chan->funcaddr = funcaddr; + chan->speed = speed; + chan->maxpacket = EFM32_EP0_DEF_PACKET_SIZE; + chan->indata1 = false; + chan->outdata1 = false; + + /* Configure control OUT channels */ + + efm32_chan_configure(priv, outndx); + + /* Allocate and initialize the control IN channel */ + + inndx = efm32_chan_alloc(priv); + if (inndx < 0) + { + efm32_chan_free(priv, outndx); + return -ENOMEM; + } + + ctrlep->inndx = inndx; + chan = &priv->chan[inndx]; + chan->epno = epno; + chan->in = true; + chan->eptype = EFM32_USB_EPTYPE_CTRL; + chan->funcaddr = funcaddr; + chan->speed = speed; + chan->maxpacket = EFM32_EP0_DEF_PACKET_SIZE; + chan->indata1 = false; + chan->outdata1 = false; + + /* Configure control IN channels */ + + efm32_chan_configure(priv, inndx); + return OK; +} + +/******************************************************************************* + * Name: efm32_ctrlep_alloc + * + * Description: + * Allocate a container and channels for control pipe. + * + * Input Parameters: + * priv - The private USB host driver state. + * epdesc - Describes the endpoint to be allocated. + * ep - A memory location provided by the caller in which to receive the + * allocated endpoint descriptor. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + *******************************************************************************/ + +static int efm32_ctrlep_alloc(FAR struct efm32_usbhost_s *priv, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep) +{ + FAR struct usbhost_hubport_s *hport; + FAR struct efm32_ctrlinfo_s *ctrlep; + int ret; + + /* Sanity check. NOTE that this method should only be called if a device is + * connected (because we need a valid low speed indication). */ - if (chan->result != EBUSY && chan->waiter) - { - usbhost_vtrace2(chan->in ? OTGFS_VTRACE2_CHANWAKEUP_IN : - OTGFS_VTRACE2_CHANWAKEUP_OUT, - chan->epno, chan->result); + DEBUGASSERT(epdesc->hport != NULL); + hport = epdesc->hport; - efm32_givesem(&chan->waitsem); - chan->waiter = false; + /* Allocate a container for the control endpoint */ + + ctrlep = (FAR struct efm32_ctrlinfo_s *)kmm_malloc(sizeof(struct efm32_ctrlinfo_s)); + if (ctrlep == NULL) + { + udbg("ERROR: Failed to allocate control endpoint container\n"); + return -ENOMEM; } + + /* Then allocate and configure the IN/OUT channnels */ + + ret = efm32_ctrlchan_alloc(priv, epdesc->addr & USB_EPNO_MASK, + hport->funcaddr, hport->speed, ctrlep); + if (ret < 0) + { + udbg("ERROR: efm32_ctrlchan_alloc failed: %d\n", ret); + kmm_free(ctrlep); + return ret; + } + + /* Return a pointer to the control pipe container as the pipe "handle" */ + + *ep = (usbhost_ep_t)ctrlep; + ret = OK; +} + +/************************************************************************************ + * Name: efm32_xfrep_alloc + * + * Description: + * Allocate and configure one unidirectional endpoint. + * + * Input Parameters: + * priv - The private USB host driver state. + * epdesc - Describes the endpoint to be allocated. + * ep - A memory location provided by the caller in which to receive the + * allocated endpoint descriptor. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ************************************************************************************/ + +static int efm32_xfrep_alloc(FAR struct efm32_usbhost_s *priv, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep) +{ + struct usbhost_hubport_s *hport; + FAR struct efm32_chan_s *chan; + int chidx; + int ret; + + /* Sanity check. NOTE that this method should only be called if a device is + * connected (because we need a valid low speed indication). + */ + + DEBUGASSERT(epdesc->hport != NULL); + hport = epdesc->hport; + + /* Allocate a host channel for the endpoint */ + + chidx = efm32_chan_alloc(priv); + if (chidx < 0) + { + udbg("ERROR: Failed to allocate a host channel\n"); + return -ENOMEM; + } + + /* Decode the endpoint descriptor to initialize the channel data structures. + * Note: Here we depend on the fact that the endpoint point type is + * encoded in the same way in the endpoint descriptor as it is in the OTG + * HS hardware. + */ + + chan = &priv->chan[chidx]; + chan->epno = epdesc->addr & USB_EPNO_MASK; + chan->in = epdesc->in; + chan->eptype = epdesc->xfrtype; + chan->funcaddr = hport->funcaddr; + chan->speed = hport->speed; + chan->maxpacket = epdesc->mxpacketsize; + chan->indata1 = false; + chan->outdata1 = false; + + /* Then configure the endpoint */ + + efm32_chan_configure(priv, chidx); + + /* Return the index to the allocated channel as the endpoint "handle" */ + + *ep = (usbhost_ep_t)chidx; + return OK; } /******************************************************************************* @@ -1262,6 +1587,7 @@ static inline uint16_t efm32_getframe(void) *******************************************************************************/ static int efm32_ctrl_sendsetup(FAR struct efm32_usbhost_s *priv, + FAR struct efm32_ctrlinfo_s *ep0, FAR const struct usb_ctrlreq_s *req) { FAR struct efm32_chan_s *chan; @@ -1271,7 +1597,7 @@ static int efm32_ctrl_sendsetup(FAR struct efm32_usbhost_s *priv, /* Loop while the device reports NAK (and a timeout is not exceeded */ - chan = &priv->chan[priv->ep0out]; + chan = &priv->chan[ep0->outndx]; start = clock_systimer(); do @@ -1293,7 +1619,7 @@ static int efm32_ctrl_sendsetup(FAR struct efm32_usbhost_s *priv, /* Start the transfer */ - efm32_transfer_start(priv, priv->ep0out); + efm32_transfer_start(priv, ep0->outndx); /* Wait for the transfer to complete */ @@ -1337,9 +1663,10 @@ static int efm32_ctrl_sendsetup(FAR struct efm32_usbhost_s *priv, *******************************************************************************/ static int efm32_ctrl_senddata(FAR struct efm32_usbhost_s *priv, + FAR struct efm32_ctrlinfo_s *ep0, FAR uint8_t *buffer, unsigned int buflen) { - FAR struct efm32_chan_s *chan = &priv->chan[priv->ep0out]; + FAR struct efm32_chan_s *chan = &priv->chan[ep0->outndx]; int ret; /* Save buffer information */ @@ -1371,7 +1698,7 @@ static int efm32_ctrl_senddata(FAR struct efm32_usbhost_s *priv, /* Start the transfer */ - efm32_transfer_start(priv, priv->ep0out); + efm32_transfer_start(priv, ep0->outndx); /* Wait for the transfer to complete and return the result */ @@ -1388,9 +1715,10 @@ static int efm32_ctrl_senddata(FAR struct efm32_usbhost_s *priv, *******************************************************************************/ static int efm32_ctrl_recvdata(FAR struct efm32_usbhost_s *priv, + FAR struct efm32_ctrlinfo_s *ep0, FAR uint8_t *buffer, unsigned int buflen) { - FAR struct efm32_chan_s *chan = &priv->chan[priv->ep0in]; + FAR struct efm32_chan_s *chan = &priv->chan[ep0->inndx]; int ret; /* Save buffer information */ @@ -1410,13 +1738,75 @@ static int efm32_ctrl_recvdata(FAR struct efm32_usbhost_s *priv, /* Start the transfer */ - efm32_transfer_start(priv, priv->ep0in); + efm32_transfer_start(priv, ep0->inndx); /* Wait for the transfer to complete and return the result */ return efm32_chan_wait(priv, chan); } +/******************************************************************************* + * Name: efm32_in_setup + * + * Description: + * Initiate an IN transfer on an bulk, interrupt, or isochronous pipe. + * + *******************************************************************************/ + +static int efm32_in_setup(FAR struct efm32_usbhost_s *priv, int chidx) +{ + FAR struct efm32_chan_s *chan; + int ret = OK; + + /* Set up for the transfer based on the direction and the endpoint type */ + + chan = &priv->chan[chidx]; + switch (chan->eptype) + { + default: + case EFM32_USB_EPTYPE_CTRL: /* Control */ + { + /* This kind of transfer on control endpoints other than EP0 are not + * currently supported + */ + + return -ENOSYS; + } + + case EFM32_USB_EPTYPE_ISOC: /* Isochronous */ + { + /* Set up the IN data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_ISOCIN, chidx, chan->buflen); + chan->pid = EFM32_USB_PID_DATA0; + } + break; + + case EFM32_USB_EPTYPE_BULK: /* Bulk */ + { + /* Setup the IN data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_BULKIN, chidx, chan->buflen); + chan->pid = chan->indata1 ? EFM32_USB_PID_DATA1 : EFM32_USB_PID_DATA0; + } + break; + + case EFM32_USB_EPTYPE_INTR: /* Interrupt */ + { + /* Setup the IN data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_INTRIN, chidx, chan->buflen); + chan->pid = chan->indata1 ? EFM32_USB_PID_DATA1 : EFM32_USB_PID_DATA0; + } + break; + } + + /* Start the transfer */ + + efm32_transfer_start(priv, chidx); + return OK; +} + /******************************************************************************* * Name: efm32_in_transfer * @@ -1449,56 +1839,19 @@ static int efm32_in_transfer(FAR struct efm32_usbhost_s *priv, int chidx, ret = efm32_chan_waitsetup(priv, chan); if (ret != OK) { - usbhost_trace1(USB_TRACE1_DEVDISCONN, 0); + usbhost_trace1(OTGFS_TRACE1_DEVDISCONN, 0); return ret; } /* Set up for the transfer based on the direction and the endpoint type */ - switch (chan->eptype) + ret = efm32_in_setup(priv, chidx); + if (ret < 0) { - default: - case EFM32_USB_EPTYPE_CTRL: /* Control */ - { - /* This kind of transfer on control endpoints other than EP0 are not - * currently supported - */ - - return -ENOSYS; - } - - case EFM32_USB_EPTYPE_ISOC: /* Isochronous */ - { - /* Set up the IN data PID */ - - usbhost_vtrace2(OTGFS_VTRACE2_ISOCIN, chidx, buflen); - chan->pid = EFM32_USB_PID_DATA0; - } - break; - - case EFM32_USB_EPTYPE_BULK: /* Bulk */ - { - /* Setup the IN data PID */ - - usbhost_vtrace2(OTGFS_VTRACE2_BULKIN, chidx, buflen); - chan->pid = chan->indata1 ? EFM32_USB_PID_DATA1 : EFM32_USB_PID_DATA0; - } - break; - - case EFM32_USB_EPTYPE_INTR: /* Interrupt */ - { - /* Setup the IN data PID */ - - usbhost_vtrace2(OTGFS_VTRACE2_INTRIN, chidx, buflen); - chan->pid = chan->indata1 ? EFM32_USB_PID_DATA1 : EFM32_USB_PID_DATA0; - } - break; + udbg("ERROR: efm32_in_setup failed: %d\n", ret); + return ret; } - /* Start the transfer */ - - efm32_transfer_start(priv, chidx); - /* Wait for the transfer to complete and get the result */ ret = efm32_chan_wait(priv, chan); @@ -1510,7 +1863,7 @@ static int efm32_in_transfer(FAR struct efm32_usbhost_s *priv, int chidx, if (ret != OK) { - usbhost_trace1(USB_TRACE1_TRNSFRFAILED,ret); + usbhost_trace1(OTGFS_TRACE1_TRNSFRFAILED,ret); /* Check for a special case: If (1) the transfer was NAKed and (2) * no Tx FIFO empty or Rx FIFO not-empty event occurred, then we @@ -1534,6 +1887,174 @@ static int efm32_in_transfer(FAR struct efm32_usbhost_s *priv, int chidx, return ret; } +/******************************************************************************* + * Name: stm32_in_next + * + * Description: + * Initiate the next of a sequence of asynchronous transfers. + * + * Assumptions: + * This function is always called from an interrupt handler + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static void stm32_in_next(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan) +{ + usbhost_asynch_t callback; + FAR void *arg; + int result; + int ret; + + /* Is the full transfer complete? Did the last chunk transfer complete OK?*/ + + result = chan->result; + if (chan->buflen > 0 && result == OK) + { + /* Yes.. Set up for the next transfer based on the direction and the + * endpoint type + */ + + ret = stm32_in_setup(priv, chidx); + if (ret >= 0) + { + return; + } + + udbg("ERROR: stm32_in_setup failed: %d\n", ret); + result = ret; + } + + /* The transfer is complete, with or without an error */ + + uvdbg("Transfer complete: %d\n", result); + + /* Extract the callback information */ + + callback = chan->callback; + arg = chan->arg; + chan->callback = NULL; + chan->arg = NULL; + + /* Then perform the callback */ + + callback(arg, chan->result); +} +#endif + +/******************************************************************************* + * Name: stm32_in_asynch + * + * Description: + * Initiate the first of a sequence of asynchronous transfers. + * + * Assumptions: + * This function is never called from an interrupt handler + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_in_asynch(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg) +{ + FAR struct stm32_chan_s *chan; + int ret; + + /* Set up for the transfer data and callback BEFORE starting the first transfer */ + + chan = &priv->chan[chidx]; + chan->buffer = buffer; + chan->buflen = buflen; + + ret = stm32_chan_asynchsetup(priv, chan, callback, arg); + if (ret < 0) + { + udbg("ERROR: stm32_chan_asynchsetup failed: %d\n", ret); + return ret; + } + + /* Set up for the transfer based on the direction and the endpoint type */ + + ret = stm32_in_setup(priv, chidx); + if (ret < 0) + { + udbg("ERROR: stm32_in_setup failed: %d\n", ret); + } + + /* And return with the transfer pending */ + + return ret; +} +#endif + +/******************************************************************************* + * Name: efm32_out_setup + * + * Description: + * Initiate an OUT transfer on an bulk, interrupt, or isochronous pipe. + * + *******************************************************************************/ + +static int efm32_out_setup(FAR struct efm32_usbhost_s *priv, int chidx) +{ + FAR struct efm32_chan_s *chan; + int ret; + + /* Set up for the transfer based on the direction and the endpoint type */ + + chan = &priv->chan[chidx]; + switch (chan->eptype) + { + default: + case EFM32_USB_EPTYPE_CTRL: /* Control */ + { + /* This kind of transfer on control endpoints other than EP0 are not + * currently supported + */ + + return -ENOSYS; + } + + case EFM32_USB_EPTYPE_ISOC: /* Isochronous */ + { + /* Set up the OUT data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_ISOCOUT, chidx, buflen); + chan->pid = EFM32_USB_PID_DATA0; + } + break; + + case EFM32_USB_EPTYPE_BULK: /* Bulk */ + { + /* Setup the OUT data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_BULKOUT, chidx, buflen); + chan->pid = chan->outdata1 ? EFM32_USB_PID_DATA1 : EFM32_USB_PID_DATA0; + } + break; + + case EFM32_USB_EPTYPE_INTR: /* Interrupt */ + { + /* Setup the OUT data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_INTROUT, chidx, buflen); + chan->pid = chan->outdata1 ? EFM32_USB_PID_DATA1 : EFM32_USB_PID_DATA0; + + /* Toggle the OUT data PID for the next transfer */ + + chan->outdata1 ^= true; + } + break; + } + + /* Start the transfer */ + + efm32_transfer_start(priv, chidx); + return OK; +} + /******************************************************************************* * Name: efm32_out_transfer * @@ -1555,8 +2076,8 @@ static int efm32_out_transfer(FAR struct efm32_usbhost_s *priv, int chidx, * or a fatal error occurs (any error other than a simple NAK) */ - chan = &priv->chan[chidx]; - start = clock_systimer(); + chan = &priv->chan[chidx]; + start = clock_systimer(); while (buflen > 0) { @@ -1574,61 +2095,20 @@ static int efm32_out_transfer(FAR struct efm32_usbhost_s *priv, int chidx, ret = efm32_chan_waitsetup(priv, chan); if (ret != OK) { - usbhost_trace1(USB_TRACE1_DEVDISCONN,0); + usbhost_trace1(OTGFS_TRACE1_DEVDISCONN,0); return ret; } /* Set up for the transfer based on the direction and the endpoint type */ - switch (chan->eptype) + ret = efm32_out_setup(priv, chidx); + if (ret < 0) { - default: - case EFM32_USB_EPTYPE_CTRL: /* Control */ - { - /* This kind of transfer on control endpoints other than EP0 are not - * currently supported - */ - - return -ENOSYS; - } - - case EFM32_USB_EPTYPE_ISOC: /* Isochronous */ - { - /* Set up the OUT data PID */ - - usbhost_vtrace2(OTGFS_VTRACE2_ISOCOUT, chidx, buflen); - chan->pid = EFM32_USB_PID_DATA0; - } - break; - - case EFM32_USB_EPTYPE_BULK: /* Bulk */ - { - /* Setup the OUT data PID */ - - usbhost_vtrace2(OTGFS_VTRACE2_BULKOUT, chidx, buflen); - chan->pid = chan->outdata1 ? EFM32_USB_PID_DATA1 : EFM32_USB_PID_DATA0; - } - break; - - case EFM32_USB_EPTYPE_INTR: /* Interrupt */ - { - /* Setup the OUT data PID */ - - usbhost_vtrace2(OTGFS_VTRACE2_INTROUT, chidx, buflen); - chan->pid = chan->outdata1 ? EFM32_USB_PID_DATA1 : EFM32_USB_PID_DATA0; - - /* Toggle the OUT data PID for the next transfer */ - - chan->outdata1 ^= true; - } - break; + udbg("ERROR: efm32_out_setup failed: %d\n", ret); + return ret; } - /* Start the transfer */ - - efm32_transfer_start(priv, chidx); - - /* Wait for the transfer to complete and get the result */ + /* Wait for the transfer to complete and get the result */ ret = efm32_chan_wait(priv, chan); @@ -1636,7 +2116,7 @@ static int efm32_out_transfer(FAR struct efm32_usbhost_s *priv, int chidx, if (ret != OK) { - usbhost_trace1(USB_TRACE1_TRNSFRFAILED,ret); + usbhost_trace1(OTGFS_TRACE1_TRNSFRFAILED,ret); /* Check for a special case: If (1) the transfer was NAKed and (2) * no Tx FIFO empty or Rx FIFO not-empty event occurred, then we @@ -1659,7 +2139,7 @@ static int efm32_out_transfer(FAR struct efm32_usbhost_s *priv, int chidx, * data in the FIFO when the NAK occurs? Does it discard it? */ - efm32_flush_txfifos(USB_GRSTCTL_TXFNUM_FALL); + efm32_flush_txfifos(OTGFS_GRSTCTL_TXFNUM_HALL); /* Get the device a little time to catch up. Then retry the transfer * using the same buffer pointer and length. @@ -1679,6 +2159,108 @@ static int efm32_out_transfer(FAR struct efm32_usbhost_s *priv, int chidx, return ret; } +/******************************************************************************* + * Name: stm32_out_next + * + * Description: + * Initiate the next of a sequence of asynchronous transfers. + * + * Assumptions: + * This function is always called from an interrupt handler + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static void stm32_out_next(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan) +{ + usbhost_asynch_t callback; + FAR void *arg; + int result; + int ret; + + /* Is the full transfer complete? Did the last chunk transfer complete OK?*/ + + result = chan->result; + if (chan->buflen > 0 && result == OK) + { + /* Yes.. Set up for the next transfer based on the direction and the + * endpoint type + */ + + ret = stm32_out_setup(priv, chidx); + if (ret >= 0) + { + return; + } + + udbg("ERROR: stm32_out_setup failed: %d\n", ret); + result = ret; + } + + /* The transfer is complete, with or without an error */ + + uvdbg("Transfer complete: %d\n", result); + + /* Extract the callback information */ + + callback = chan->callback; + arg = chan->arg; + chan->callback = NULL; + chan->arg = NULL; + + /* Then perform the callback */ + + callback(arg, chan->result); +} +#endif + +/******************************************************************************* + * Name: stm32_out_asynch + * + * Description: + * Initiate the first of a sequence of asynchronous transfers. + * + * Assumptions: + * This function is never called from an interrupt handler + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_out_asynch(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg) +{ + FAR struct stm32_chan_s *chan; + int ret; + + /* Set up for the transfer data and callback BEFORE starting the first transfer */ + + chan = &priv->chan[chidx]; + chan->buffer = buffer; + chan->buflen = buflen; + + ret = stm32_chan_asynchsetup(priv, chan, callback, arg); + if (ret < 0) + { + udbg("ERROR: stm32_chan_asynchsetup failed: %d\n", ret); + return ret; + } + + /* Set up for the transfer based on the direction and the endpoint type */ + + ret = stm32_out_setup(priv, chidx); + if (ret < 0) + { + udbg("ERROR: stm32_out_setup failed: %d\n", ret); + } + + /* And return with the transfer pending */ + + return ret; +} +#endif + /******************************************************************************* * Name: efm32_gint_wrpacket * @@ -2206,15 +2788,16 @@ static void efm32_gint_connected(FAR struct efm32_usbhost_s *priv) usbhost_vtrace1(OTGFS_VTRACE1_CONNECTED,0); priv->connected = true; + priv->change = true; DEBUGASSERT(priv->smstate == SMSTATE_DETACHED); /* Notify any waiters */ priv->smstate = SMSTATE_ATTACHED; - if (priv->eventwait) + if (priv->pscwait) { - efm32_givesem(&priv->eventsem); - priv->eventwait = false; + efm32_givesem(&priv->pscsem); + priv->pscwait = false; } } } @@ -2239,29 +2822,29 @@ static void efm32_gint_disconnected(FAR struct efm32_usbhost_s *priv) /* Are we bound to a class driver? */ - if (priv->class) + if ( priv->rhport.hport.devclass) { /* Yes.. Disconnect the class driver */ - CLASS_DISCONNECTED(priv->class); - priv->class = NULL; + CLASS_DISCONNECTED( priv->rhport.hport.devclass); + priv->rhport.hport.devclass = NULL; } - /* Re-Initilaize Host for new Enumeration */ + /* Re-Initialize Host for new Enumeration */ priv->smstate = SMSTATE_DETACHED; - priv->ep0size = EFM32_EP0_MAX_PACKET_SIZE; - priv->devaddr = EFM32_DEF_DEVADDR; priv->connected = false; - priv->lowspeed = false; + priv->change = true; efm32_chan_freeall(priv); + priv->rhport.hport.speed = USB_SPEED_FULL; + /* Notify any waiters that there is a change in the connection state */ - if (priv->eventwait) + if (priv->pscwait) { - efm32_givesem(&priv->eventsem); - priv->eventwait = false; + efm32_givesem(&priv->pscsem); + priv->pscwait = false; } } } @@ -3082,19 +3665,20 @@ static void efm32_txfe_enable(FAR struct efm32_usbhost_s *priv, int chidx) * Name: efm32_wait * * Description: - * Wait for a device to be connected or disconneced. + * Wait for a device to be connected or disconnected to/from a hub port. * * Input Parameters: * conn - The USB host connection instance obtained as a parameter from the call to * the USB driver initialization logic. - * connected - A pointer to a boolean value. TRUE: Wait for device to be - * connected; FALSE: wait for device to be disconnected + * hport - The location to return the hub port descriptor that detected the + * connection related event. * * Returned Values: - * Zero (OK) is returned when a device in connected. This function will not - * return until either (1) a device is connected or (2) some failure occurs. - * On a failure, a negated errno value is returned indicating the nature of - * the failure + * Zero (OK) is returned on success when a device in connected or + * disconnected. This function will not return until either (1) a device is + * connected or disconnect to/from any hub port or until (2) some failure + * occurs. On a failure, a negated errno value is returned indicating the + * nature of the failure * * Assumptions: * - Called from a single thread so no mutual exclusion is required. @@ -3115,13 +3699,14 @@ static int efm32_wait(FAR struct usbhost_connection_s *conn, { /* No... wait for the connection/disconnection */ - priv->eventwait = true; - efm32_takesem(&priv->eventsem); + priv->pscwait = true; + efm32_takesem(&priv->pscsem); } + priv->change = false; irqrestore(flags); - udbg("Connected:%s\n", priv->connected ? "YES" : "NO"); + uvdbg("Connected:%s\n", priv->connected ? "YES" : "NO"); return OK; } @@ -3134,34 +3719,33 @@ static int efm32_wait(FAR struct usbhost_connection_s *conn, * extract the class ID info from the configuration descriptor, (3) call * usbhost_findclass() to find the class that supports this device, (4) * call the create() method on the struct usbhost_registry_s interface - * to get a class instance, and finally (5) call the configdesc() method + * to get a class instance, and finally (5) call the connect() method * of the struct usbhost_class_s interface. After that, the class is in * charge of the sequence of operations. * * Input Parameters: - * conn - The USB host connection instance obtained as a parameter from the call to - * the USB driver initialization logic. - * rphndx - Root hub port index. 0-(n-1) corresponds to root hub port 1-n. + * conn - The USB host connection instance obtained as a parameter from + * the call to the USB driver initialization logic. + * hport - The descriptor of the hub port that has the newly connected + * device. * * Returned Values: * On success, zero (OK) is returned. On a failure, a negated errno value is * returned indicating the nature of the failure * * Assumptions: - * - Only a single class bound to a single device is supported. - * - Called from a single thread so no mutual exclusion is required. - * - Never called from an interrupt handler. + * This function will *not* be called from an interrupt handler. * *******************************************************************************/ -static int efm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) +static int efm32_rh_enumerate(FAR struct efm32_usbhost_s *priv, + FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport) { - FAR struct efm32_usbhost_s *priv = &g_usbhost; uint32_t regval; - int chidx; int ret; - DEBUGASSERT(priv && rhpndx == 0); + DEBUGASSERT(conn != NULL && hport != NULL && hport->port == 0); /* Are we connected to a device? The caller should have called the wait() * method first to be assured that a device is connected. @@ -3177,32 +3761,6 @@ static int efm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) DEBUGASSERT(priv->smstate == SMSTATE_ATTACHED); - /* Allocate and initialize the control OUT channel */ - - chidx = efm32_chan_alloc(priv); - DEBUGASSERT(chidx >= 0); - - priv->ep0out = chidx; - priv->chan[chidx].epno = 0; - priv->chan[chidx].in = false; - priv->chan[chidx].eptype = EFM32_USB_EPTYPE_CTRL; - priv->chan[chidx].maxpacket = EFM32_EP0_DEF_PACKET_SIZE; - priv->chan[chidx].indata1 = false; - priv->chan[chidx].outdata1 = false; - - /* Allocate and initialize the control IN channel */ - - chidx = efm32_chan_alloc(priv); - DEBUGASSERT(chidx >= 0); - - priv->ep0in = chidx; - priv->chan[chidx].epno = 0; - priv->chan[chidx].in = true; - priv->chan[chidx].eptype = EFM32_USB_EPTYPE_CTRL; - priv->chan[chidx].maxpacket = EFM32_EP0_DEF_PACKET_SIZE; - priv->chan[chidx].indata1 = false; - priv->chan[chidx].outdata1 = false; - /* USB 2.0 spec says at least 50ms delay before port reset. We wait 100ms. */ usleep(100*1000); @@ -3214,20 +3772,55 @@ static int efm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) /* Get the current device speed */ regval = efm32_getreg(EFM32_USB_HPRT); - priv->lowspeed = ((regval & _USB_HPRT_PRTSPD_MASK) == USB_HPRT_PRTSPD_LS); + if ((regval & _USB_HPRT_PRTSPD_MASK) == USB_HPRT_PRTSPD_LS) + { + priv->rhport.hport.speed = USB_SPEED_LOW. + } + else + { + priv->rhport.hport.speed = USB_SPEED_FULL; + } - /* Configure control channels */ + /* Allocate and initialize the root hub port EP0 channels */ - efm32_chan_configure(priv, priv->ep0out); - efm32_chan_configure(priv, priv->ep0in); + ret = efm32_ctrlchan_alloc(priv, 0, 0, priv->rhport.hport.speed, &priv->ep0); + if (ret < 0) + { + udbg("ERROR: Failed to allocate a control endpoint: %d\n", ret); + } - /* Let the common usbhost_enumerate do all of the real work. Note that the - * FunctionAddress (USB address) is hardcoded to one. - */ + return ret; +} + +static int efm32_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport) +{ + FAR struct efm32_usbhost_s *priv = &g_usbhost; + int ret; + + DEBUGASSERT(hport); + + /* If this is a connection on the root hub, then we need to go to + * little more effort to get the device speed. If it is a connection + * on an external hub, then we already have that information. + */ + +#ifdef CONFIG_USBHOST_HUB + if (ROOTHUB(hport)) +#endif + { + ret = efm32_rh_enumerate(priv, conn, hport); + if (ret < 0) + { + return ret; + } + } + + /* Then let the common usbhost_enumerate do the real enumeration. */ uvdbg("Enumerate the device\n"); priv->smstate = SMSTATE_ENUM; - ret = usbhost_enumerate(&g_usbhost.drvr, 1, &priv->class); + ret = usbhost_enumerate(hport, &hport->devclass); /* The enumeration may fail either because of some HCD interfaces failure * or because the device class is not supported. In either case, we just @@ -3239,6 +3832,7 @@ static int efm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) { /* Return to the disconnected state */ + udbg("ERROR: Enumeration failed: %d\n", ret); efm32_gint_disconnected(priv); } @@ -3256,8 +3850,10 @@ static int efm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) * Input Parameters: * drvr - The USB host driver instance obtained as a parameter from the call to * the class create() method. + * ep0 - The (opaque) EP0 endpoint instance * funcaddr - The USB address of the function containing the endpoint that EP0 * controls + * speed - The speed of the port USB_SPEED_LOW, _FULL, or _HIGH * maxpacketsize - The maximum number of bytes that can be sent to or * received from the endpoint in a single data packet * @@ -3270,67 +3866,43 @@ static int efm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) * ************************************************************************************/ -static int efm32_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr, +static int efm32_ep0configure(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + uint8_t funcaddr, uint8_t speed, uint16_t maxpacketsize) { FAR struct efm32_usbhost_s *priv = (FAR struct efm32_usbhost_s *)drvr; + FAR struct efm32_ctrlinfo_s *ep0info = (FAR struct efm32_ctrlinfo_s *)ep0; + FAR struct efm32_chan_s *chan; - DEBUGASSERT(drvr && funcaddr < 128 && maxpacketsize < 2048); + DEBUGASSERT(drvr != NULL && ep0info != NULL && && funcaddr < 128 && + maxpacketsize <= 64); /* We must have exclusive access to the USB host hardware and state structures */ efm32_takesem(&priv->exclsem); - /* Save the device address and EP0 max packet size */ - - priv->devaddr = funcaddr; - priv->ep0size = maxpacketsize; - /* Configure the EP0 OUT channel */ - priv->chan[priv->ep0out].maxpacket = maxpacketsize; - efm32_chan_configure(priv, priv->ep0out); + chan = priv->&riv->chan[ep0info->outndx]; + chan->funcaddr = funcaddr; + chan->speed = speed; + chan->maxpacket = maxpacketsize; + + efm32_chan_configure(priv, ep0info->outndx); /* Configure the EP0 IN channel */ - priv->chan[priv->ep0in].maxpacket = maxpacketsize; - efm32_chan_configure(priv, priv->ep0in); + chan = priv->&riv->chan[ep0info->inndx]; + chan->funcaddr = funcaddr; + chan->speed = speed; + chan->maxpacket = maxpacketsize; + + efm32_chan_configure(priv, ep0info->inndx); efm32_givesem(&priv->exclsem); return OK; } -/************************************************************************************ - * Name: efm32_getdevinfo - * - * Description: - * Get information about the connected device. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the call to - * the class create() method. - * devinfo - A pointer to memory provided by the caller in which to return the - * device information. - * - * Returned Values: - * On success, zero (OK) is returned. On a failure, a negated errno value is - * returned indicating the nature of the failure - * - * Assumptions: - * This function will *not* be called from an interrupt handler. - * - ************************************************************************************/ - -static int efm32_getdevinfo(FAR struct usbhost_driver_s *drvr, - FAR struct usbhost_devinfo_s *devinfo) -{ - FAR struct efm32_usbhost_s *priv = (FAR struct efm32_usbhost_s *)drvr; - - DEBUGASSERT(drvr && devinfo); - devinfo->speed = priv->lowspeed ? DEVINFO_SPEED_LOW : DEVINFO_SPEED_FULL; - return OK; -} - /************************************************************************************ * Name: efm32_epalloc * @@ -3353,59 +3925,42 @@ static int efm32_getdevinfo(FAR struct usbhost_driver_s *drvr, * ************************************************************************************/ +static int efm32_epalloc(FAR struct usbhost_driver_s *drvr, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep) +{ static int efm32_epalloc(FAR struct usbhost_driver_s *drvr, FAR const struct usbhost_epdesc_s *epdesc, FAR usbhost_ep_t *ep) { FAR struct efm32_usbhost_s *priv = (FAR struct efm32_usbhost_s *)drvr; - FAR struct efm32_chan_s *chan; - int chidx; int ret; /* Sanity check. NOTE that this method should only be called if a device is * connected (because we need a valid low speed indication). */ - DEBUGASSERT(priv && epdesc && ep && priv->connected); + DEBUGASSERT(drvr != 0 && epdesc != NULL && ep != NULL); /* We must have exclusive access to the USB host hardware and state structures */ efm32_takesem(&priv->exclsem); - /* Allocate a host channel for the endpoint */ - - chidx = efm32_chan_alloc(priv); - if (chidx < 0) - { - udbg("Failed to allocate a host channel\n"); - ret = -ENOMEM; - goto errout; - } - - /* Decode the endpoint descriptor to initialize the channel data structures. - * Note: Here we depend on the fact that the endpoint point type is - * encoded in the same way in the endpoint descriptor as it is in the OTG - * FS hardware. + /* Handler control pipes differently from other endpoint types. This is + * because the normal, "transfer" endpoints are unidirectional an require + * only a single channel. Control endpoints, however, are bi-diretional + * and require two channels, one for the IN and one for the OUT direction. */ - chan = &priv->chan[chidx]; - chan->epno = epdesc->addr & USB_EPNO_MASK; - chan->in = epdesc->in; - chan->eptype = epdesc->xfrtype; - chan->maxpacket = epdesc->mxpacketsize; - chan->indata1 = false; - chan->outdata1 = false; + if (epdesc->xfrtype == EFM32_USB_EPTYPE_CTRL) + { + ret = efm32_ctrlep_alloc(priv, epdesc, ep); + } + else + { + ret = efm32_xfrep_alloc(priv, epdesc, ep); + } - /* Then configure the endpoint */ - - efm32_chan_configure(priv, chidx); - - /* Return the index to the allocated channel as the endpoint "handle" */ - - *ep = (usbhost_ep_t)chidx; - ret = OK; - -errout: efm32_givesem(&priv->exclsem); return ret; } @@ -3432,18 +3987,37 @@ errout: static int efm32_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) { - struct efm32_usbhost_s *priv = (struct efm32_usbhost_s *)drvr; - int chidx = (int)ep; + FAR struct efm32_usbhost_s *priv = (FAR struct efm32_usbhost_s *)drvr; + FAR struct efm32_ctrlinfo_s *ctrlep; - DEBUGASSERT(priv && chidx < EFM32_MAX_TX_FIFOS); + DEBUGASSERT(priv); /* We must have exclusive access to the USB host hardware and state structures */ efm32_takesem(&priv->exclsem); - /* Halt the channel and mark the channel avaiable */ + /* A single channel is represent by an index in the range of 0 to EFM32_MAX_TX_FIFOS. + * Otherwise, the ep must be a pointer to an allocated control endpoint structure. + */ - efm32_chan_free(priv, chidx); + if ((uintptr_t)ep < EFM32_MAX_TX_FIFOS) + { + /* Halt the channel and mark the channel available */ + + efm32_chan_free(priv, (int)ep); + } + else + { + /* Halt both control channel and mark the channels available */ + + FAR struct efm32_ctrlinfo_s *ctrlep = ( FAR struct efm32_ctrlinfo_s *)ep; + efm32_chan_free(priv, ctrlep->inndx); + efm32_chan_free(priv, ctrlep->outndx); + + /* And free the control endpoint container */ + + kmm_free(ctrlep); + } efm32_givesem(&priv->exclsem); return OK; @@ -3630,11 +4204,12 @@ static int efm32_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) * Input Parameters: * drvr - The USB host driver instance obtained as a parameter from the call to * the class create() method. + * ep0 - The control endpoint to send/receive the control request. * req - Describes the request to be sent. This request must lie in memory * created by DRVR_ALLOC. * buffer - A buffer used for sending the request and for returning any * responses. This buffer must be large enough to hold the length value - * in the request description. buffer must have been allocated using DRVR_ALLOC + * in the request description. buffer must have been allocated using DRVR_ALLOC. * * NOTE: On an IN transaction, req and buffer may refer to the same allocated * memory. @@ -3644,13 +4219,12 @@ static int efm32_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) * returned indicating the nature of the failure * * Assumptions: - * - Only a single class bound to a single device is supported. * - Called from a single thread so no mutual exclusion is required. * - Never called from an interrupt handler. * *******************************************************************************/ -static int efm32_ctrlin(FAR struct usbhost_driver_s *drvr, +static int efm32_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, FAR const struct usb_ctrlreq_s *req, FAR uint8_t *buffer) { @@ -3681,7 +4255,7 @@ static int efm32_ctrlin(FAR struct usbhost_driver_s *drvr, { /* Send the SETUP request */ - ret = efm32_ctrl_sendsetup(priv, req); + ret = efm32_ctrl_sendsetup(priv, ep0, req); if (ret < 0) { usbhost_trace1(USB_TRACE1_SENDSETUP, -ret); @@ -3697,7 +4271,7 @@ static int efm32_ctrlin(FAR struct usbhost_driver_s *drvr, if (buflen > 0) { - ret = efm32_ctrl_recvdata(priv, buffer, buflen); + ret = efm32_ctrl_recvdata(priv, ep0, buffer, buflen); if (ret < 0) { usbhost_trace1(USB_TRACE1_RECVDATA, -ret); @@ -3708,8 +4282,8 @@ static int efm32_ctrlin(FAR struct usbhost_driver_s *drvr, if (ret == OK) { - priv->chan[priv->ep0out].outdata1 ^= true; - ret = efm32_ctrl_senddata(priv, NULL, 0); + priv->chan[ep0->outndx].outdata1 ^= true; + ret = efm32_ctrl_senddata(priv, ep0, NULL, 0); if (ret == OK) { /* All success transactions exit here */ @@ -3734,7 +4308,7 @@ static int efm32_ctrlin(FAR struct usbhost_driver_s *drvr, return -ETIMEDOUT; } -static int efm32_ctrlout(FAR struct usbhost_driver_s *drvr, +static int efm32_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, FAR const struct usb_ctrlreq_s *req, FAR const uint8_t *buffer) { @@ -3767,7 +4341,7 @@ static int efm32_ctrlout(FAR struct usbhost_driver_s *drvr, /* Send the SETUP request */ - ret = efm32_ctrl_sendsetup(priv, req); + ret = efm32_ctrl_sendsetup(priv, ep0, req); if (ret < 0) { usbhost_trace1(USB_TRACE1_SENDSETUP, -ret); @@ -3785,8 +4359,8 @@ static int efm32_ctrlout(FAR struct usbhost_driver_s *drvr, { /* Start DATA out transfer (only one DATA packet) */ - priv->chan[priv->ep0out].outdata1 = true; - ret = efm32_ctrl_senddata(priv, NULL, 0); + priv->chan[ep0->outndx].outdata1 = true; + ret = efm32_ctrl_senddata(priv, ep0, NULL, 0); if (ret < 0) { usbhost_trace1(USB_TRACE1_SENDDATA, -ret); @@ -3797,7 +4371,7 @@ static int efm32_ctrlout(FAR struct usbhost_driver_s *drvr, if (ret == OK) { - ret = efm32_ctrl_recvdata(priv, NULL, 0); + ret = efm32_ctrl_recvdata(priv, ep0, NULL, 0); if (ret == OK) { /* All success transactins exit here */ @@ -3827,9 +4401,9 @@ static int efm32_ctrlout(FAR struct usbhost_driver_s *drvr, * * Description: * Process a request to handle a transfer descriptor. This method will - * enqueue the transfer request and return immediately. Only one transfer may be - * queued; Neither this method nor the ctrlin or ctrlout methods can be called - * again until this function returns. + * enqueue the transfer request, blocking until the transfer completes. Only + * one transfer may be queued; Neither this method nor the ctrlin or + * ctrlout methods can be called again until this function returns. * * This is a blocking method; this functions will not return until the * transfer has completed. @@ -3854,7 +4428,6 @@ static int efm32_ctrlout(FAR struct usbhost_driver_s *drvr, * EPIPE - Overrun errors * * Assumptions: - * - Only a single class bound to a single device is supported. * - Called from a single thread so no mutual exclusion is required. * - Never called from an interrupt handler. * @@ -3890,6 +4463,151 @@ static int efm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, return ret; } +/******************************************************************************* + * Name: efm32_asynch + * + * Description: + * Process a request to handle a transfer descriptor. This method will + * enqueue the transfer request and return immediately. When the transfer + * completes, the the callback will be invoked with the provided transfer. + * This method is useful for receiving interrupt transfers which may come + * infrequently. + * + * Only one transfer may be queued; Neither this method nor the ctrlin or + * ctrlout methods can be called again until the transfer completes. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on which to + * perform the transfer. + * buffer - A buffer containing the data to be sent (OUT endpoint) or received + * (IN endpoint). buffer must have been allocated using DRVR_ALLOC + * buflen - The length of the data to be sent or received. + * callback - This function will be called when the transfer completes. + * arg - The arbitrary parameter that will be passed to the callback function + * when the transfer completes. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int efm32_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg) +{ + FAR struct efm32_usbhost_s *priv = (FAR struct efm32_usbhost_s *)drvr; + unsigned int chidx = (unsigned int)ep; + int ret; + + uvdbg("chidx: %d buflen: %d\n", (unsigned int)ep, buflen); + + DEBUGASSERT(priv && buffer && chidx < EFM32_MAX_TX_FIFOS && buflen > 0); + + /* We must have exclusive access to the USB host hardware and state structures */ + + efm32_takesem(&priv->exclsem); + + /* Handle IN and OUT transfer slightly differently */ + + if (priv->chan[chidx].in) + { + ret = efm32_in_asynch(priv, chidx, buffer, buflen, callback, arg); + } + else + { + ret = efm32_out_asynch(priv, chidx, buffer, buflen, callback, arg); + } + + efm32_givesem(&priv->exclsem); + return ret; +} +#endif /* CONFIG_USBHOST_ASYNCH */ + +/************************************************************************************ + * Name: efm32_cancel + * + * Description: + * Cancel a pending asynchronous transfer on an endpoint. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on which an + * asynchronous transfer should be transferred. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure. + * + ************************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int efm32_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) +{ +# error Missing logic + return -ENOSYS; + } +#endif /* CONFIG_USBHOST_ASYNCH */ + +/************************************************************************************ + * Name: efm32_connect + * + * Description: + * New connections may be detected by an attached hub. This method is the + * mechanism that is used by the hub class to introduce a new connection + * and port description to the system. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * hport - The descriptor of the hub port that detected the connection + * related event + * connected - True: device connected; false: device disconnected + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure. + * + ************************************************************************************/ + +#ifdef CONFIG_USBHOST_HUB +static int efm32_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport, + bool connected) +{ + FAR struct efm32_usbhost_s *priv = (FAR struct efm32_usbhost_s *)drvr; + irqstate_t flags; + + DEBUGASSERT(priv != NULL && hport != NULL); + + /* Set the connected/disconnected flag */ + + hport->connected = connected; + ullvdbg("Hub port %d connected: %s\n", hport->port, connected ? "YES" : "NO"); + + /* Report the connection event */ + + flags = irqsave(); + priv->hport = hport; + if (priv->pscwait) + { + priv->pscwait = false; + efm32_givesem(&priv->pscsem); + } + + irqrestore(flags); + return OK; +} +#endif + /******************************************************************************* * Name: efm32_disconnect * @@ -3915,10 +4633,10 @@ static int efm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, static void efm32_disconnect(FAR struct usbhost_driver_s *drvr) { - struct efm32_usbhost_s *priv = (struct efm32_usbhost_s *)drvr; + FAR struct efm32_usbhost_s *priv = (FAR struct efm32_usbhost_s *)drvr; DEBUGASSERT(priv); - priv->class = NULL; + priv->rhport.hport.devclass = NULL; } /******************************************************************************* @@ -4190,17 +4908,56 @@ static void efm32_host_initialize(FAR struct efm32_usbhost_s *priv) static inline void efm32_sw_initialize(FAR struct efm32_usbhost_s *priv) { + FAR struct usbhost_driver_s *drvr; + FAR struct usbhost_hubport_s *hport; int i; - /* Initialize the state data structure */ + /* Initialize the device operations */ - sem_init(&priv->eventsem, 0, 0); + drvr = &priv->drvr; + drvr->ep0configure = efm32_ep0configure; + drvr->epalloc = efm32_epalloc; + drvr->epfree = efm32_epfree; + drvr->alloc = efm32_alloc; + drvr->free = efm32_free; + drvr->ioalloc = efm32_ioalloc; + drvr->iofree = efm32_iofree; + drvr->ctrlin = efm32_ctrlin; + drvr->ctrlout = efm32_ctrlout; + drvr->transfer = efm32_transfer; +#ifdef CONFIG_USBHOST_ASYNCH + drvr->asynch = efm32_asynch; + drvr->cancel = efm32_cancel; +#endif +#ifdef CONFIG_USBHOST_HUB + drvr->connect = efm32_connect; +#endif + drvr->disconnect = efm32_disconnect; + + /* Initialize the public port representation */ + + hport = &priv->rhport.hport; + hport->drvr = drvr; +#ifdef CONFIG_USBHOST_HUB + hport->parent = NULL; +#endif + hport->ep0 = (usbhost_ep_t)&priv->ep0; + hport->speed = USB_SPEED_FULL; + + /* Initialize function address generation logic */ + + usbhost_devaddr_initialize(&priv->rhport); + + /* Initialize semaphores */ + + sem_init(&priv->pscsem, 0, 0); sem_init(&priv->exclsem, 0, 1); + /* Initialize the driver state data */ + priv->smstate = SMSTATE_DETACHED; - priv->ep0size = EFM32_EP0_MAX_PACKET_SIZE; - priv->devaddr = EFM32_DEF_DEVADDR; priv->connected = false; + priv->change = false; priv->lowspeed = false; /* Put all of the channels back in their initial, allocated state */ diff --git a/arch/arm/src/lpc31xx/lpc31_ehci.c b/arch/arm/src/lpc31xx/lpc31_ehci.c index 636d97ba2b..073fe6e76c 100644 --- a/arch/arm/src/lpc31xx/lpc31_ehci.c +++ b/arch/arm/src/lpc31xx/lpc31_ehci.c @@ -18,6 +18,7 @@ * 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 diff --git a/arch/arm/src/stm32/stm32_otgfshost.c b/arch/arm/src/stm32/stm32_otgfshost.c index e8b7523954..8747c01c2f 100644 --- a/arch/arm/src/stm32/stm32_otgfshost.c +++ b/arch/arm/src/stm32/stm32_otgfshost.c @@ -1,7 +1,7 @@ /******************************************************************************* * arch/arm/src/stm32/stm32_otgfshost.c * - * Copyright (C) 2012-2014 Gregory Nutt. All rights reserved. + * Copyright (C) 2012-2015 Gregory Nutt. All rights reserved. * Authors: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -54,6 +54,7 @@ #include #include #include +#include #include #include @@ -145,7 +146,6 @@ #define STM32_MAX_TX_FIFOS 15 /* Max number of TX FIFOs */ #define STM32_MAX_PKTCOUNT 256 /* Max packet count */ #define STM32_RETRY_COUNT 3 /* Number of ctrl transfer retries */ -#define STM32_DEF_DEVADDR 0 /* Default device address */ /* Delays **********************************************************************/ @@ -208,6 +208,8 @@ struct stm32_chan_s volatile uint8_t chreason; /* Channel halt reason. See enum stm32_chreason_e */ uint8_t epno; /* Device endpoint number (0-127) */ uint8_t eptype; /* See OTGFS_EPTYPE_* definitions */ + uint8_t funcaddr; /* Device function address */ + uint8_t speed; /* Device speed */ uint8_t pid; /* Data PID */ uint8_t npackets; /* Number of packets (for data toggle) */ bool inuse; /* True: This channel is "in use" */ @@ -219,6 +221,21 @@ struct stm32_chan_s volatile uint16_t buflen; /* Buffer length (remaining) */ volatile uint16_t inflight; /* Number of Tx bytes "in-flight" */ FAR uint8_t *buffer; /* Transfer buffer pointer */ +#ifdef CONFIG_USBHOST_ASYNCH + usbhost_asynch_t callback; /* Transfer complete callback */ + FAR void *arg; /* Argument that accompanies the callback */ +#endif +}; + +/* A channel represents on uni-directional endpoint. So, in the case of the + * bi-directional, control endpoint, there must be two channels to represent + * the endpoint. + */ + +struct stm32_ctrlinfo_s +{ + uint8_t inndx; /* EP0 IN control channel index */ + uint8_t outndx; /* EP0 OUT control channel index */ }; /* This structure retains the state of the USB host controller */ @@ -232,23 +249,26 @@ struct stm32_usbhost_s struct usbhost_driver_s drvr; - /* The bound device class driver */ + /* This is the hub port description understood by class drivers */ - struct usbhost_class_s *class; + struct usbhost_roothubport_s rhport; /* Overall driver status */ volatile uint8_t smstate; /* The state of the USB host state machine */ - uint8_t devaddr; /* Device address */ - uint8_t ep0in; /* EP0 IN control channel index */ - uint8_t ep0out; /* EP0 OUT control channel index */ - uint8_t ep0size; /* EP0 max packet size */ uint8_t chidx; /* ID of channel waiting for space in Tx FIFO */ - bool lowspeed; /* True: low speed device */ volatile bool connected; /* Connected to device */ - volatile bool eventwait; /* True: Thread is waiting for a port event */ + volatile bool change; /* Connection change */ + volatile bool pscwait; /* True: Thread is waiting for a port event */ sem_t exclsem; /* Support mutually exclusive access */ - sem_t eventsem; /* Semaphore to wait for a port event */ + sem_t pscsem; /* Semaphore to wait for a port event */ + struct stm32_ctrlinfo_s ep0; /* Root hub port EP0 description */ + +#ifdef CONFIG_USBHOST_HUB + /* Used to pass external hub port events */ + + volatile struct usbhost_hubport_s *hport; +#endif /* The state of each host channel */ @@ -299,10 +319,24 @@ static void stm32_chan_halt(FAR struct stm32_usbhost_s *priv, int chidx, enum stm32_chreason_e chreason); static int stm32_chan_waitsetup(FAR struct stm32_usbhost_s *priv, FAR struct stm32_chan_s *chan); +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_chan_asynchsetup(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan, + usbhost_asynch_t callback, FAR void *arg); +#endif static int stm32_chan_wait(FAR struct stm32_usbhost_s *priv, FAR struct stm32_chan_s *chan); static void stm32_chan_wakeup(FAR struct stm32_usbhost_s *priv, FAR struct stm32_chan_s *chan); +static int stm32_ctrlchan_alloc(FAR struct stm32_usbhost_s *priv, + uint8_t epno, uint8_t funcaddr, uint8_t speed, + FAR struct stm32_ctrlinfo_s *ctrlep); +static int stm32_ctrlep_alloc(FAR struct stm32_usbhost_s *priv, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep); +static int stm32_xfrep_alloc(FAR struct stm32_usbhost_s *priv, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep); /* Control/data transfer logic *************************************************/ @@ -311,15 +345,34 @@ static void stm32_transfer_start(FAR struct stm32_usbhost_s *priv, int chidx); static inline uint16_t stm32_getframe(void); #endif static int stm32_ctrl_sendsetup(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, FAR const struct usb_ctrlreq_s *req); static int stm32_ctrl_senddata(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, FAR uint8_t *buffer, unsigned int buflen); static int stm32_ctrl_recvdata(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, FAR uint8_t *buffer, unsigned int buflen); +static int stm32_in_setup(FAR struct stm32_usbhost_s *priv, int chidx); static int stm32_in_transfer(FAR struct stm32_usbhost_s *priv, int chidx, FAR uint8_t *buffer, size_t buflen); +#ifdef CONFIG_USBHOST_ASYNCH +static void stm32_in_next(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan); +static int stm32_in_asynch(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg); +#endif +static int stm32_out_setup(FAR struct stm32_usbhost_s *priv, int chidx); static int stm32_out_transfer(FAR struct stm32_usbhost_s *priv, int chidx, FAR uint8_t *buffer, size_t buflen); +#ifdef CONFIG_USBHOST_ASYNCH +static void stm32_out_next(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan); +static int stm32_out_asynch(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg); +#endif /* Interrupt handling **********************************************************/ /* Lower level interrupt handlers */ @@ -360,13 +413,16 @@ static void stm32_txfe_enable(FAR struct stm32_usbhost_s *priv, int chidx); /* USB host controller operations **********************************************/ static int stm32_wait(FAR struct usbhost_connection_s *conn, - FAR const bool *connected); -static int stm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx); + FAR struct usbhost_hubport_s **hport); +static int stm32_rh_enumerate(FAR struct stm32_usbhost_s *priv, + FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport); +static int stm32_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport); -static int stm32_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr, +static int stm32_ep0configure(FAR struct usbhost_driver_s *drvr, + usbhost_ep_t ep0, uint8_t funcaddr, uint8_t speed, uint16_t maxpacketsize); -static int stm32_getdevinfo(FAR struct usbhost_driver_s *drvr, - FAR struct usbhost_devinfo_s *devinfo); static int stm32_epalloc(FAR struct usbhost_driver_s *drvr, FAR const FAR struct usbhost_epdesc_s *epdesc, FAR usbhost_ep_t *ep); @@ -377,14 +433,28 @@ static int stm32_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer); static int stm32_ioalloc(FAR struct usbhost_driver_s *drvr, FAR uint8_t **buffer, size_t buflen); static int stm32_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer); -static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, - FAR const struct usb_ctrlreq_s *req, +static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + const struct usb_ctrlreq_s *req, FAR uint8_t *buffer); -static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, +static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, FAR const struct usb_ctrlreq_s *req, FAR const uint8_t *buffer); +static int stm32_transfer_common(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ed_s *ed,FAR uint8_t *buffer, + size_t buflen); static int stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, FAR uint8_t *buffer, size_t buflen); +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg); +static int stm32_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); +#endif +#ifdef CONFIG_USBHOST_HUB +static int stm32_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport, + bool connected); +#endif static void stm32_disconnect(FAR struct usbhost_driver_s *drvr); /* Initialization **************************************************************/ @@ -407,25 +477,7 @@ static inline int stm32_hw_initialize(FAR struct stm32_usbhost_s *priv); * instance. */ -static struct stm32_usbhost_s g_usbhost = -{ - .drvr = - { - .ep0configure = stm32_ep0configure, - .getdevinfo = stm32_getdevinfo, - .epalloc = stm32_epalloc, - .epfree = stm32_epfree, - .alloc = stm32_alloc, - .free = stm32_free, - .ioalloc = stm32_ioalloc, - .iofree = stm32_iofree, - .ctrlin = stm32_ctrlin, - .ctrlout = stm32_ctrlout, - .transfer = stm32_transfer, - .disconnect = stm32_disconnect, - }, - .class = NULL, -}; +static struct stm32_usbhost_s g_usbhost; /* This is the connection/enumeration interface */ @@ -699,6 +751,7 @@ static inline void stm32_chan_freeall(FAR struct stm32_usbhost_s *priv) static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx) { + FAR struct stm32_chan_s *chan = &priv->chan[chidx]; uint32_t regval; /* Clear any old pending interrupts for this host channel. */ @@ -709,7 +762,7 @@ static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx) regval = 0; - switch (priv->chan[chidx].eptype) + switch (chan->eptype) { case OTGFS_EPTYPE_CTRL: case OTGFS_EPTYPE_BULK: @@ -720,7 +773,7 @@ static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx) /* Determine the definitive trace ID to use below */ - if (priv->chan[chidx].eptype == OTGFS_EPTYPE_CTRL) + if (chan->eptype == OTGFS_EPTYPE_CTRL) { intrace = OTGFS_VTRACE2_CHANCONF_CTRL_IN; outtrace = OTGFS_VTRACE2_CHANCONF_CTRL_OUT; @@ -739,14 +792,14 @@ static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx) /* Additional setting for IN/OUT endpoints */ - if (priv->chan[chidx].in) + if (chan->in) { - usbhost_vtrace2(intrace, chidx, priv->chan[chidx].epno); + usbhost_vtrace2(intrace, chidx, chan->epno); regval |= OTGFS_HCINT_BBERR; } else { - usbhost_vtrace2(outtrace, chidx, priv->chan[chidx].epno); + usbhost_vtrace2(outtrace, chidx, chan->epno); regval |= OTGFS_HCINT_NYET; } } @@ -761,17 +814,17 @@ static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx) /* Additional setting for IN endpoints */ - if (priv->chan[chidx].in) + if (chan->in) { usbhost_vtrace2(OTGFS_VTRACE2_CHANCONF_INTR_IN, chidx, - priv->chan[chidx].epno); + chan->epno); regval |= OTGFS_HCINT_BBERR; } #ifdef HAVE_USBHOST_TRACE_VERBOSE else { usbhost_vtrace2(OTGFS_VTRACE2_CHANCONF_INTR_OUT, chidx, - priv->chan[chidx].epno); + chan->epno); } #endif } @@ -785,17 +838,17 @@ static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx) /* Additional setting for IN endpoints */ - if (priv->chan[chidx].in) + if (chan->in) { usbhost_vtrace2(OTGFS_VTRACE2_CHANCONF_ISOC_IN, chidx, - priv->chan[chidx].epno); + chan->epno); regval |= (OTGFS_HCINT_TXERR | OTGFS_HCINT_BBERR); } #ifdef HAVE_USBHOST_TRACE_VERBOSE else { usbhost_vtrace2(OTGFS_VTRACE2_CHANCONF_ISOC_OUT, chidx, - priv->chan[chidx].epno); + chan->epno); } #endif } @@ -814,28 +867,28 @@ static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx) /* Program the HCCHAR register */ - regval = ((uint32_t)priv->chan[chidx].maxpacket << OTGFS_HCCHAR_MPSIZ_SHIFT) | - ((uint32_t)priv->chan[chidx].epno << OTGFS_HCCHAR_EPNUM_SHIFT) | - ((uint32_t)priv->chan[chidx].eptype << OTGFS_HCCHAR_EPTYP_SHIFT) | - ((uint32_t)priv->devaddr << OTGFS_HCCHAR_DAD_SHIFT); + regval = ((uint32_t)chan->maxpacket << OTGFS_HCCHAR_MPSIZ_SHIFT) | + ((uint32_t)chan->epno << OTGFS_HCCHAR_EPNUM_SHIFT) | + ((uint32_t)chan->eptype << OTGFS_HCCHAR_EPTYP_SHIFT) | + ((uint32_t)chan->funcaddr << OTGFS_HCCHAR_DAD_SHIFT); /* Special case settings for low speed devices */ - if (priv->lowspeed) + if (chan->speed == USB_SPEED_LOW) { regval |= OTGFS_HCCHAR_LSDEV; } /* Special case settings for IN endpoints */ - if (priv->chan[chidx].in) + if (chan->in) { regval |= OTGFS_HCCHAR_EPDIR_IN; } /* Special case settings for INTR endpoints */ - if (priv->chan[chidx].eptype == OTGFS_EPTYPE_INTR) + if (chan->eptype == OTGFS_EPTYPE_INTR) { regval |= OTGFS_HCCHAR_ODDFRM; } @@ -958,14 +1011,59 @@ static int stm32_chan_waitsetup(FAR struct stm32_usbhost_s *priv, * either (1) the device is disconnected, or (2) the transfer completed. */ - chan->waiter = true; - ret = OK; + chan->waiter = true; +#ifdef CONFIG_USBHOST_ASYNCH + chan->callback = NULL; + chan->arg = NULL; +#endif + ret = OK; } irqrestore(flags); return ret; } +/******************************************************************************* + * Name: stm32_chan_asynchsetup + * + * Description: + * Set the request for the transfer complete event well BEFORE enabling the + * transfer (as soon as we are absolutely committed to the to avoid transfer). + * We do this to minimize race conditions. This logic would have to be expanded + * if we want to have more than one packet in flight at a time! + * + * Assumptions: + * Might be called from the level of an interrupt handler + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_chan_asynchsetup(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan. + usbhost_asynch_t callback, FAR void *arg) +{ + irqstate_t flags = irqsave(); + int ret = -ENODEV; + + /* Is the device still connected? */ + + if (priv->connected) + { + /* Yes.. then set waiter to indicate that we expect to be informed when + * either (1) the device is disconnected, or (2) the transfer completed. + */ + + chan->waiter = false; + chan->callback = callback; + chan->arg = arg; + ret = OK; + } + + irqrestore(flags); + return ret; +} +#endif + /******************************************************************************* * Name: stm32_chan_wait * @@ -1037,19 +1135,246 @@ static int stm32_chan_wait(FAR struct stm32_usbhost_s *priv, static void stm32_chan_wakeup(FAR struct stm32_usbhost_s *priv, FAR struct stm32_chan_s *chan) { - /* Is the transfer complete? Is there a thread waiting for this transfer - * to complete? + /* Is the transfer complete? */ + + if (chan->result != EBUSY) + { + /* Is there a thread waiting for this transfer to complete? */ + + if (chan->waiter) + { +#ifdef CONFIG_USBHOST_ASYNCH + /* Yes.. there should not also be a callback scheduled */ + + DEBUGASSERT(chan->callback == NULL); +#endif + /* Wake'em up! */ + + usbhost_vtrace2(chan->in ? OTGFS_VTRACE2_CHANWAKEUP_IN : + OTGFS_VTRACE2_CHANWAKEUP_OUT, + chan->epno, chan->result); + + stm32_givesem(&chan->waitsem); + chan->waiter = false; + } + +#ifdef CONFIG_USBHOST_ASYNCH + /* No.. is an asynchronous callback expected when the transfer + * completes? + */ + + else if (chan->callback) + { + /* Handle continuation of IN/OUT pipes */ + + if (priv->in) + { + stm32_in_next(priv, chan); + } + else + { + stm32_out_next(priv, chan); + } + } +#endif + } +} + +/******************************************************************************* + * Name: stm32_ctrlchan_alloc + * + * Description: + * Allocate and configured channels for a control pipe. + * + *******************************************************************************/ + +static int stm32_ctrlchan_alloc(FAR struct stm32_usbhost_s *priv, + uint8_t epno, uint8_t funcaddr, uint8_t speed, + FAR struct stm32_ctrlinfo_s *ctrlep) +{ + FAR struct stm32_chan_s *chan; + int inndx; + int outndx; + + outndx = stm32_chan_alloc(priv); + if (outndx < 0) + { + return -ENOMEM; + } + + ctrlep->outndx = outndx; + chan = &priv->chan[outndx]; + chan->epno = epno; + chan->in = false; + chan->eptype = OTGFS_EPTYPE_CTRL; + chan->funcaddr = funcaddr; + chan->speed = speed; + chan->maxpacket = STM32_EP0_DEF_PACKET_SIZE; + chan->indata1 = false; + chan->outdata1 = false; + + /* Configure control OUT channels */ + + stm32_chan_configure(priv, outndx); + + /* Allocate and initialize the control IN channel */ + + inndx = stm32_chan_alloc(priv); + if (inndx < 0) + { + stm32_chan_free(priv, outndx); + return -ENOMEM; + } + + ctrlep->inndx = inndx; + chan = &priv->chan[inndx]; + chan->epno = epno; + chan->in = true; + chan->eptype = OTGFS_EPTYPE_CTRL; + chan->funcaddr = funcaddr; + chan->speed = speed; + chan->maxpacket = STM32_EP0_DEF_PACKET_SIZE; + chan->indata1 = false; + chan->outdata1 = false; + + /* Configure control IN channels */ + + stm32_chan_configure(priv, inndx); + return OK; +} + +/******************************************************************************* + * Name: stm32_ctrlep_alloc + * + * Description: + * Allocate a container and channels for control pipe. + * + * Input Parameters: + * priv - The private USB host driver state. + * epdesc - Describes the endpoint to be allocated. + * ep - A memory location provided by the caller in which to receive the + * allocated endpoint descriptor. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + *******************************************************************************/ + +static int stm32_ctrlep_alloc(FAR struct stm32_usbhost_s *priv, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep) +{ + FAR struct usbhost_hubport_s *hport; + FAR struct stm32_ctrlinfo_s *ctrlep; + int ret; + + /* Sanity check. NOTE that this method should only be called if a device is + * connected (because we need a valid low speed indication). */ - if (chan->result != EBUSY && chan->waiter) - { - usbhost_vtrace2(chan->in ? OTGFS_VTRACE2_CHANWAKEUP_IN : - OTGFS_VTRACE2_CHANWAKEUP_OUT, - chan->epno, chan->result); + DEBUGASSERT(epdesc->hport != NULL); + hport = epdesc->hport; - stm32_givesem(&chan->waitsem); - chan->waiter = false; + /* Allocate a container for the control endpoint */ + + ctrlep = (FAR struct stm32_ctrlinfo_s *)kmm_malloc(sizeof(struct stm32_ctrlinfo_s)); + if (ctrlep == NULL) + { + udbg("ERROR: Failed to allocate control endpoint container\n"); + return -ENOMEM; } + + /* Then allocate and configure the IN/OUT channnels */ + + ret = stm32_ctrlchan_alloc(priv, epdesc->addr & USB_EPNO_MASK, + hport->funcaddr, hport->speed, ctrlep); + if (ret < 0) + { + udbg("ERROR: stm32_ctrlchan_alloc failed: %d\n", ret); + kmm_free(ctrlep); + return ret; + } + + /* Return a pointer to the control pipe container as the pipe "handle" */ + + *ep = (usbhost_ep_t)ctrlep; + ret = OK; +} + +/************************************************************************************ + * Name: stm32_xfrep_alloc + * + * Description: + * Allocate and configure one unidirectional endpoint. + * + * Input Parameters: + * priv - The private USB host driver state. + * epdesc - Describes the endpoint to be allocated. + * ep - A memory location provided by the caller in which to receive the + * allocated endpoint descriptor. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ************************************************************************************/ + +static int stm32_xfrep_alloc(FAR struct stm32_usbhost_s *priv, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep) +{ + struct usbhost_hubport_s *hport; + FAR struct stm32_chan_s *chan; + int chidx; + int ret; + + /* Sanity check. NOTE that this method should only be called if a device is + * connected (because we need a valid low speed indication). + */ + + DEBUGASSERT(epdesc->hport != NULL); + hport = epdesc->hport; + + /* Allocate a host channel for the endpoint */ + + chidx = stm32_chan_alloc(priv); + if (chidx < 0) + { + udbg("ERROR: Failed to allocate a host channel\n"); + return -ENOMEM; + } + + /* Decode the endpoint descriptor to initialize the channel data structures. + * Note: Here we depend on the fact that the endpoint point type is + * encoded in the same way in the endpoint descriptor as it is in the OTG + * HS hardware. + */ + + chan = &priv->chan[chidx]; + chan->epno = epdesc->addr & USB_EPNO_MASK; + chan->in = epdesc->in; + chan->eptype = epdesc->xfrtype; + chan->funcaddr = hport->funcaddr; + chan->speed = hport->speed; + chan->maxpacket = epdesc->mxpacketsize; + chan->indata1 = false; + chan->outdata1 = false; + + /* Then configure the endpoint */ + + stm32_chan_configure(priv, chidx); + + /* Return the index to the allocated channel as the endpoint "handle" */ + + *ep = (usbhost_ep_t)chidx; + return OK; } /******************************************************************************* @@ -1267,6 +1592,7 @@ static inline uint16_t stm32_getframe(void) *******************************************************************************/ static int stm32_ctrl_sendsetup(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, FAR const struct usb_ctrlreq_s *req) { FAR struct stm32_chan_s *chan; @@ -1276,7 +1602,7 @@ static int stm32_ctrl_sendsetup(FAR struct stm32_usbhost_s *priv, /* Loop while the device reports NAK (and a timeout is not exceeded */ - chan = &priv->chan[priv->ep0out]; + chan = &priv->chan[ep0->outndx]; start = clock_systimer(); do @@ -1298,7 +1624,7 @@ static int stm32_ctrl_sendsetup(FAR struct stm32_usbhost_s *priv, /* Start the transfer */ - stm32_transfer_start(priv, priv->ep0out); + stm32_transfer_start(priv, ep0->outndx); /* Wait for the transfer to complete */ @@ -1342,9 +1668,10 @@ static int stm32_ctrl_sendsetup(FAR struct stm32_usbhost_s *priv, *******************************************************************************/ static int stm32_ctrl_senddata(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, FAR uint8_t *buffer, unsigned int buflen) { - FAR struct stm32_chan_s *chan = &priv->chan[priv->ep0out]; + FAR struct stm32_chan_s *chan = &priv->chan[ep0->outndx]; int ret; /* Save buffer information */ @@ -1376,7 +1703,7 @@ static int stm32_ctrl_senddata(FAR struct stm32_usbhost_s *priv, /* Start the transfer */ - stm32_transfer_start(priv, priv->ep0out); + stm32_transfer_start(priv, ep0->outndx); /* Wait for the transfer to complete and return the result */ @@ -1393,9 +1720,10 @@ static int stm32_ctrl_senddata(FAR struct stm32_usbhost_s *priv, *******************************************************************************/ static int stm32_ctrl_recvdata(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, FAR uint8_t *buffer, unsigned int buflen) { - FAR struct stm32_chan_s *chan = &priv->chan[priv->ep0in]; + FAR struct stm32_chan_s *chan = &priv->chan[ep0->inndx]; int ret; /* Save buffer information */ @@ -1415,13 +1743,75 @@ static int stm32_ctrl_recvdata(FAR struct stm32_usbhost_s *priv, /* Start the transfer */ - stm32_transfer_start(priv, priv->ep0in); + stm32_transfer_start(priv, ep0->inndx); /* Wait for the transfer to complete and return the result */ return stm32_chan_wait(priv, chan); } +/******************************************************************************* + * Name: stm32_in_setup + * + * Description: + * Initiate an IN transfer on an bulk, interrupt, or isochronous pipe. + * + *******************************************************************************/ + +static int stm32_in_setup(FAR struct stm32_usbhost_s *priv, int chidx) +{ + FAR struct stm32_chan_s *chan; + int ret = OK; + + /* Set up for the transfer based on the direction and the endpoint type */ + + chan = &priv->chan[chidx]; + switch (chan->eptype) + { + default: + case OTGFS_EPTYPE_CTRL: /* Control */ + { + /* This kind of transfer on control endpoints other than EP0 are not + * currently supported + */ + + return -ENOSYS; + } + + case OTGFS_EPTYPE_ISOC: /* Isochronous */ + { + /* Set up the IN data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_ISOCIN, chidx, chan->buflen); + chan->pid = OTGFS_PID_DATA0; + } + break; + + case OTGFS_EPTYPE_BULK: /* Bulk */ + { + /* Setup the IN data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_BULKIN, chidx, chan->buflen); + chan->pid = chan->indata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; + } + break; + + case OTGFS_EPTYPE_INTR: /* Interrupt */ + { + /* Setup the IN data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_INTRIN, chidx, chan->buflen); + chan->pid = chan->indata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; + } + break; + } + + /* Start the transfer */ + + stm32_transfer_start(priv, chidx); + return OK; +} + /******************************************************************************* * Name: stm32_in_transfer * @@ -1460,50 +1850,13 @@ static int stm32_in_transfer(FAR struct stm32_usbhost_s *priv, int chidx, /* Set up for the transfer based on the direction and the endpoint type */ - switch (chan->eptype) + ret = stm32_in_setup(priv, chidx); + if (ret < 0) { - default: - case OTGFS_EPTYPE_CTRL: /* Control */ - { - /* This kind of transfer on control endpoints other than EP0 are not - * currently supported - */ - - return -ENOSYS; - } - - case OTGFS_EPTYPE_ISOC: /* Isochronous */ - { - /* Set up the IN data PID */ - - usbhost_vtrace2(OTGFS_VTRACE2_ISOCIN, chidx, buflen); - chan->pid = OTGFS_PID_DATA0; - } - break; - - case OTGFS_EPTYPE_BULK: /* Bulk */ - { - /* Setup the IN data PID */ - - usbhost_vtrace2(OTGFS_VTRACE2_BULKIN, chidx, buflen); - chan->pid = chan->indata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; - } - break; - - case OTGFS_EPTYPE_INTR: /* Interrupt */ - { - /* Setup the IN data PID */ - - usbhost_vtrace2(OTGFS_VTRACE2_INTRIN, chidx, buflen); - chan->pid = chan->indata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; - } - break; + udbg("ERROR: stm32_in_setup failed: %d\n", ret); + return ret; } - /* Start the transfer */ - - stm32_transfer_start(priv, chidx); - /* Wait for the transfer to complete and get the result */ ret = stm32_chan_wait(priv, chan); @@ -1539,6 +1892,174 @@ static int stm32_in_transfer(FAR struct stm32_usbhost_s *priv, int chidx, return ret; } +/******************************************************************************* + * Name: stm32_in_next + * + * Description: + * Initiate the next of a sequence of asynchronous transfers. + * + * Assumptions: + * This function is always called from an interrupt handler + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static void stm32_in_next(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan) +{ + usbhost_asynch_t callback; + FAR void *arg; + int result; + int ret; + + /* Is the full transfer complete? Did the last chunk transfer complete OK?*/ + + result = chan->result; + if (chan->buflen > 0 && result == OK) + { + /* Yes.. Set up for the next transfer based on the direction and the + * endpoint type + */ + + ret = stm32_in_setup(priv, chidx); + if (ret >= 0) + { + return; + } + + udbg("ERROR: stm32_in_setup failed: %d\n", ret); + result = ret; + } + + /* The transfer is complete, with or without an error */ + + uvdbg("Transfer complete: %d\n", result); + + /* Extract the callback information */ + + callback = chan->callback; + arg = chan->arg; + chan->callback = NULL; + chan->arg = NULL; + + /* Then perform the callback */ + + callback(arg, chan->result); +} +#endif + +/******************************************************************************* + * Name: stm32_in_asynch + * + * Description: + * Initiate the first of a sequence of asynchronous transfers. + * + * Assumptions: + * This function is never called from an interrupt handler + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_in_asynch(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg) +{ + FAR struct stm32_chan_s *chan; + int ret; + + /* Set up for the transfer data and callback BEFORE starting the first transfer */ + + chan = &priv->chan[chidx]; + chan->buffer = buffer; + chan->buflen = buflen; + + ret = stm32_chan_asynchsetup(priv, chan, callback, arg); + if (ret < 0) + { + udbg("ERROR: stm32_chan_asynchsetup failed: %d\n", ret); + return ret; + } + + /* Set up for the transfer based on the direction and the endpoint type */ + + ret = stm32_in_setup(priv, chidx); + if (ret < 0) + { + udbg("ERROR: stm32_in_setup failed: %d\n", ret); + } + + /* And return with the transfer pending */ + + return ret; +} +#endif + +/******************************************************************************* + * Name: stm32_out_setup + * + * Description: + * Initiate an OUT transfer on an bulk, interrupt, or isochronous pipe. + * + *******************************************************************************/ + +static int stm32_out_setup(FAR struct stm32_usbhost_s *priv, int chidx) +{ + FAR struct stm32_chan_s *chan; + int ret; + + /* Set up for the transfer based on the direction and the endpoint type */ + + chan = &priv->chan[chidx]; + switch (chan->eptype) + { + default: + case OTGFS_EPTYPE_CTRL: /* Control */ + { + /* This kind of transfer on control endpoints other than EP0 are not + * currently supported + */ + + return -ENOSYS; + } + + case OTGFS_EPTYPE_ISOC: /* Isochronous */ + { + /* Set up the OUT data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_ISOCOUT, chidx, buflen); + chan->pid = OTGFS_PID_DATA0; + } + break; + + case OTGFS_EPTYPE_BULK: /* Bulk */ + { + /* Setup the OUT data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_BULKOUT, chidx, buflen); + chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; + } + break; + + case OTGFS_EPTYPE_INTR: /* Interrupt */ + { + /* Setup the OUT data PID */ + + usbhost_vtrace2(OTGFS_VTRACE2_INTROUT, chidx, buflen); + chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; + + /* Toggle the OUT data PID for the next transfer */ + + chan->outdata1 ^= true; + } + break; + } + + /* Start the transfer */ + + stm32_transfer_start(priv, chidx); + return OK; +} + /******************************************************************************* * Name: stm32_out_transfer * @@ -1560,8 +2081,8 @@ static int stm32_out_transfer(FAR struct stm32_usbhost_s *priv, int chidx, * or a fatal error occurs (any error other than a simple NAK) */ - chan = &priv->chan[chidx]; - start = clock_systimer(); + chan = &priv->chan[chidx]; + start = clock_systimer(); while (buflen > 0) { @@ -1585,55 +2106,14 @@ static int stm32_out_transfer(FAR struct stm32_usbhost_s *priv, int chidx, /* Set up for the transfer based on the direction and the endpoint type */ - switch (chan->eptype) + ret = stm32_out_setup(priv, chidx); + if (ret < 0) { - default: - case OTGFS_EPTYPE_CTRL: /* Control */ - { - /* This kind of transfer on control endpoints other than EP0 are not - * currently supported - */ - - return -ENOSYS; - } - - case OTGFS_EPTYPE_ISOC: /* Isochronous */ - { - /* Set up the OUT data PID */ - - usbhost_vtrace2(OTGFS_VTRACE2_ISOCOUT, chidx, buflen); - chan->pid = OTGFS_PID_DATA0; - } - break; - - case OTGFS_EPTYPE_BULK: /* Bulk */ - { - /* Setup the OUT data PID */ - - usbhost_vtrace2(OTGFS_VTRACE2_BULKOUT, chidx, buflen); - chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; - } - break; - - case OTGFS_EPTYPE_INTR: /* Interrupt */ - { - /* Setup the OUT data PID */ - - usbhost_vtrace2(OTGFS_VTRACE2_INTROUT, chidx, buflen); - chan->pid = chan->outdata1 ? OTGFS_PID_DATA1 : OTGFS_PID_DATA0; - - /* Toggle the OUT data PID for the next transfer */ - - chan->outdata1 ^= true; - } - break; + udbg("ERROR: stm32_out_setup failed: %d\n", ret); + return ret; } - /* Start the transfer */ - - stm32_transfer_start(priv, chidx); - - /* Wait for the transfer to complete and get the result */ + /* Wait for the transfer to complete and get the result */ ret = stm32_chan_wait(priv, chan); @@ -1684,6 +2164,108 @@ static int stm32_out_transfer(FAR struct stm32_usbhost_s *priv, int chidx, return ret; } +/******************************************************************************* + * Name: stm32_out_next + * + * Description: + * Initiate the next of a sequence of asynchronous transfers. + * + * Assumptions: + * This function is always called from an interrupt handler + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static void stm32_out_next(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan) +{ + usbhost_asynch_t callback; + FAR void *arg; + int result; + int ret; + + /* Is the full transfer complete? Did the last chunk transfer complete OK?*/ + + result = chan->result; + if (chan->buflen > 0 && result == OK) + { + /* Yes.. Set up for the next transfer based on the direction and the + * endpoint type + */ + + ret = stm32_out_setup(priv, chidx); + if (ret >= 0) + { + return; + } + + udbg("ERROR: stm32_out_setup failed: %d\n", ret); + result = ret; + } + + /* The transfer is complete, with or without an error */ + + uvdbg("Transfer complete: %d\n", result); + + /* Extract the callback information */ + + callback = chan->callback; + arg = chan->arg; + chan->callback = NULL; + chan->arg = NULL; + + /* Then perform the callback */ + + callback(arg, chan->result); +} +#endif + +/******************************************************************************* + * Name: stm32_out_asynch + * + * Description: + * Initiate the first of a sequence of asynchronous transfers. + * + * Assumptions: + * This function is never called from an interrupt handler + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_out_asynch(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg) +{ + FAR struct stm32_chan_s *chan; + int ret; + + /* Set up for the transfer data and callback BEFORE starting the first transfer */ + + chan = &priv->chan[chidx]; + chan->buffer = buffer; + chan->buflen = buflen; + + ret = stm32_chan_asynchsetup(priv, chan, callback, arg); + if (ret < 0) + { + udbg("ERROR: stm32_chan_asynchsetup failed: %d\n", ret); + return ret; + } + + /* Set up for the transfer based on the direction and the endpoint type */ + + ret = stm32_out_setup(priv, chidx); + if (ret < 0) + { + udbg("ERROR: stm32_out_setup failed: %d\n", ret); + } + + /* And return with the transfer pending */ + + return ret; +} +#endif + /******************************************************************************* * Name: stm32_gint_wrpacket * @@ -2226,15 +2808,16 @@ static void stm32_gint_connected(FAR struct stm32_usbhost_s *priv) usbhost_vtrace1(OTGFS_VTRACE1_CONNECTED,0); priv->connected = true; + priv->change = true; DEBUGASSERT(priv->smstate == SMSTATE_DETACHED); /* Notify any waiters */ priv->smstate = SMSTATE_ATTACHED; - if (priv->eventwait) + if (priv->pscwait) { - stm32_givesem(&priv->eventsem); - priv->eventwait = false; + stm32_givesem(&priv->pscsem); + priv->pscwait = false; } } } @@ -2259,29 +2842,29 @@ static void stm32_gint_disconnected(FAR struct stm32_usbhost_s *priv) /* Are we bound to a class driver? */ - if (priv->class) + if ( priv->rhport.hport.devclass) { /* Yes.. Disconnect the class driver */ - CLASS_DISCONNECTED(priv->class); - priv->class = NULL; + CLASS_DISCONNECTED( priv->rhport.hport.devclass); + priv->rhport.hport.devclass = NULL; } - /* Re-Initilaize Host for new Enumeration */ + /* Re-Initialize Host for new Enumeration */ priv->smstate = SMSTATE_DETACHED; - priv->ep0size = STM32_EP0_MAX_PACKET_SIZE; - priv->devaddr = STM32_DEF_DEVADDR; priv->connected = false; - priv->lowspeed = false; + priv->change = true; stm32_chan_freeall(priv); + priv->rhport.hport.speed = USB_SPEED_FULL; + /* Notify any waiters that there is a change in the connection state */ - if (priv->eventwait) + if (priv->pscwait) { - stm32_givesem(&priv->eventsem); - priv->eventwait = false; + stm32_givesem(&priv->pscsem); + priv->pscwait = false; } } } @@ -2722,8 +3305,8 @@ static inline void stm32_gint_hprtisr(FAR struct stm32_usbhost_s *priv) if ((hcfg & OTGFS_HCFG_FSLSPCS_MASK) != OTGFS_HCFG_FSLSPCS_LS6MHz) { - usbhost_vtrace1(OTGFS_VTRACE1_GINT_HPRT_FSLSSW, 0); + /* Yes... configure for LS */ hcfg &= ~OTGFS_HCFG_FSLSPCS_MASK; @@ -3099,19 +3682,20 @@ static void stm32_txfe_enable(FAR struct stm32_usbhost_s *priv, int chidx) * Name: stm32_wait * * Description: - * Wait for a device to be connected or disconneced. + * Wait for a device to be connected or disconnected to/from a hub port. * * Input Parameters: * conn - The USB host connection instance obtained as a parameter from the call to * the USB driver initialization logic. - * connected - A pointer to a boolean value. TRUE: Wait for device to be - * connected; FALSE: wait for device to be disconnected + * hport - The location to return the hub port descriptor that detected the + * connection related event. * * Returned Values: - * Zero (OK) is returned when a device in connected. This function will not - * return until either (1) a device is connected or (2) some failure occurs. - * On a failure, a negated errno value is returned indicating the nature of - * the failure + * Zero (OK) is returned on success when a device in connected or + * disconnected. This function will not return until either (1) a device is + * connected or disconnect to/from any hub port or until (2) some failure + * occurs. On a failure, a negated errno value is returned indicating the + * nature of the failure * * Assumptions: * - Called from a single thread so no mutual exclusion is required. @@ -3120,26 +3704,62 @@ static void stm32_txfe_enable(FAR struct stm32_usbhost_s *priv, int chidx) *******************************************************************************/ static int stm32_wait(FAR struct usbhost_connection_s *conn, - FAR const bool *connected) + FAR struct usbhost_hubport_s **hport) { FAR struct stm32_usbhost_s *priv = &g_usbhost; + struct usbhost_hubport_s *connport; irqstate_t flags; - /* Are we already connected? */ + /* Loop until a change in connection state is detected */ flags = irqsave(); - while (priv->connected == *connected) + for (;;) { - /* No... wait for the connection/disconnection */ + /* Is there a change in the connection state of the single root hub + * port? + */ - priv->eventwait = true; - stm32_takesem(&priv->eventsem); + if (priv->change) + { + connport = &priv->rhport.hport; + + /* Yes. Remember the new state */ + + connport->connected = priv->connected; + priv->change = false; + + /* And return the root hub port */ + + *hport = connport; + irqrestore(flags); + + uvdbg("RHport Connected: %s\n", connport->connected ? "YES" : "NO"); + return OK; + } + +#ifdef CONFIG_USBHOST_HUB + /* Is a device connected to an external hub? */ + + if (priv->hport) + { + /* Yes.. return the external hub port */ + + connport = (struct usbhost_hubport_s *)priv->hport; + priv->hport = NULL; + + *hport = connport; + irqrestore(flags); + + uvdbg("Hub port Connected: %s\n", connport->connected ? "YES" : "NO"); + return OK; + } +#endif + + /* Wait for the next connection event */ + + priv->pscwait = true; + stm32_takesem(&priv->pscsem); } - - irqrestore(flags); - - udbg("Connected:%s\n", priv->connected ? "YES" : "NO"); - return OK; } /******************************************************************************* @@ -3151,34 +3771,33 @@ static int stm32_wait(FAR struct usbhost_connection_s *conn, * extract the class ID info from the configuration descriptor, (3) call * usbhost_findclass() to find the class that supports this device, (4) * call the create() method on the struct usbhost_registry_s interface - * to get a class instance, and finally (5) call the configdesc() method + * to get a class instance, and finally (5) call the connect() method * of the struct usbhost_class_s interface. After that, the class is in * charge of the sequence of operations. * * Input Parameters: - * conn - The USB host connection instance obtained as a parameter from the call to - * the USB driver initialization logic. - * rphndx - Root hub port index. 0-(n-1) corresponds to root hub port 1-n. + * conn - The USB host connection instance obtained as a parameter from + * the call to the USB driver initialization logic. + * hport - The descriptor of the hub port that has the newly connected + * device. * * Returned Values: * On success, zero (OK) is returned. On a failure, a negated errno value is * returned indicating the nature of the failure * * Assumptions: - * - Only a single class bound to a single device is supported. - * - Called from a single thread so no mutual exclusion is required. - * - Never called from an interrupt handler. + * This function will *not* be called from an interrupt handler. * *******************************************************************************/ -static int stm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) +static int stm32_rh_enumerate(FAR struct stm32_usbhost_s *priv, + FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport) { - FAR struct stm32_usbhost_s *priv = &g_usbhost; uint32_t regval; - int chidx; int ret; - DEBUGASSERT(priv && rhpndx == 0); + DEBUGASSERT(conn != NULL && hport != NULL && hport->port == 0); /* Are we connected to a device? The caller should have called the wait() * method first to be assured that a device is connected. @@ -3194,32 +3813,6 @@ static int stm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) DEBUGASSERT(priv->smstate == SMSTATE_ATTACHED); - /* Allocate and initialize the control OUT channel */ - - chidx = stm32_chan_alloc(priv); - DEBUGASSERT(chidx >= 0); - - priv->ep0out = chidx; - priv->chan[chidx].epno = 0; - priv->chan[chidx].in = false; - priv->chan[chidx].eptype = OTGFS_EPTYPE_CTRL; - priv->chan[chidx].maxpacket = STM32_EP0_DEF_PACKET_SIZE; - priv->chan[chidx].indata1 = false; - priv->chan[chidx].outdata1 = false; - - /* Allocate and initialize the control IN channel */ - - chidx = stm32_chan_alloc(priv); - DEBUGASSERT(chidx >= 0); - - priv->ep0in = chidx; - priv->chan[chidx].epno = 0; - priv->chan[chidx].in = true; - priv->chan[chidx].eptype = OTGFS_EPTYPE_CTRL; - priv->chan[chidx].maxpacket = STM32_EP0_DEF_PACKET_SIZE; - priv->chan[chidx].indata1 = false; - priv->chan[chidx].outdata1 = false; - /* USB 2.0 spec says at least 50ms delay before port reset. We wait 100ms. */ usleep(100*1000); @@ -3231,20 +3824,55 @@ static int stm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) /* Get the current device speed */ regval = stm32_getreg(STM32_OTGFS_HPRT); - priv->lowspeed = ((regval & OTGFS_HPRT_PSPD_MASK) == OTGFS_HPRT_PSPD_LS); + if ((regval & OTGFS_HPRT_PSPD_MASK) == OTGFS_HPRT_PSPD_LS) + { + priv->rhport.hport.speed = USB_SPEED_LOW. + } + else + { + priv->rhport.hport.speed = USB_SPEED_FULL; + } - /* Configure control channels */ + /* Allocate and initialize the root hub port EP0 channels */ - stm32_chan_configure(priv, priv->ep0out); - stm32_chan_configure(priv, priv->ep0in); + ret = stm32_ctrlchan_alloc(priv, 0, 0, priv->rhport.hport.speed, &priv->ep0); + if (ret < 0) + { + udbg("ERROR: Failed to allocate a control endpoint: %d\n", ret); + } - /* Let the common usbhost_enumerate do all of the real work. Note that the - * FunctionAddress (USB address) is hardcoded to one. - */ + return ret; +} + +static int stm32_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport) +{ + FAR struct stm32_usbhost_s *priv = &g_usbhost; + int ret; + + DEBUGASSERT(hport); + + /* If this is a connection on the root hub, then we need to go to + * little more effort to get the device speed. If it is a connection + * on an external hub, then we already have that information. + */ + +#ifdef CONFIG_USBHOST_HUB + if (ROOTHUB(hport)) +#endif + { + ret = stm32_rh_enumerate(priv, conn, hport); + if (ret < 0) + { + return ret; + } + } + + /* Then let the common usbhost_enumerate do the real enumeration. */ uvdbg("Enumerate the device\n"); priv->smstate = SMSTATE_ENUM; - ret = usbhost_enumerate(&g_usbhost.drvr, 1, &priv->class); + ret = usbhost_enumerate(hport, &hport->devclass); /* The enumeration may fail either because of some HCD interfaces failure * or because the device class is not supported. In either case, we just @@ -3256,6 +3884,7 @@ static int stm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) { /* Return to the disconnected state */ + udbg("ERROR: Enumeration failed: %d\n", ret); stm32_gint_disconnected(priv); } @@ -3273,8 +3902,10 @@ static int stm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) * Input Parameters: * drvr - The USB host driver instance obtained as a parameter from the call to * the class create() method. + * ep0 - The (opaque) EP0 endpoint instance * funcaddr - The USB address of the function containing the endpoint that EP0 * controls + * speed - The speed of the port USB_SPEED_LOW, _FULL, or _HIGH * maxpacketsize - The maximum number of bytes that can be sent to or * received from the endpoint in a single data packet * @@ -3287,67 +3918,43 @@ static int stm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) * ************************************************************************************/ -static int stm32_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr, +static int stm32_ep0configure(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + uint8_t funcaddr, uint8_t speed, uint16_t maxpacketsize) { FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + FAR struct stm32_ctrlinfo_s *ep0info = (FAR struct stm32_ctrlinfo_s *)ep0; + FAR struct stm32_chan_s *chan; - DEBUGASSERT(drvr && funcaddr < 128 && maxpacketsize < 2048); + DEBUGASSERT(drvr != NULL && ep0info != NULL && && funcaddr < 128 && + maxpacketsize <= 64); /* We must have exclusive access to the USB host hardware and state structures */ stm32_takesem(&priv->exclsem); - /* Save the device address and EP0 max packet size */ - - priv->devaddr = funcaddr; - priv->ep0size = maxpacketsize; - /* Configure the EP0 OUT channel */ - priv->chan[priv->ep0out].maxpacket = maxpacketsize; - stm32_chan_configure(priv, priv->ep0out); + chan = priv->&riv->chan[ep0info->outndx]; + chan->funcaddr = funcaddr; + chan->speed = speed; + chan->maxpacket = maxpacketsize; + + stm32_chan_configure(priv, ep0info->outndx); /* Configure the EP0 IN channel */ - priv->chan[priv->ep0in].maxpacket = maxpacketsize; - stm32_chan_configure(priv, priv->ep0in); + chan = priv->&riv->chan[ep0info->inndx]; + chan->funcaddr = funcaddr; + chan->speed = speed; + chan->maxpacket = maxpacketsize; + + stm32_chan_configure(priv, ep0info->inndx); stm32_givesem(&priv->exclsem); return OK; } -/************************************************************************************ - * Name: stm32_getdevinfo - * - * Description: - * Get information about the connected device. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the call to - * the class create() method. - * devinfo - A pointer to memory provided by the caller in which to return the - * device information. - * - * Returned Values: - * On success, zero (OK) is returned. On a failure, a negated errno value is - * returned indicating the nature of the failure - * - * Assumptions: - * This function will *not* be called from an interrupt handler. - * - ************************************************************************************/ - -static int stm32_getdevinfo(FAR struct usbhost_driver_s *drvr, - FAR struct usbhost_devinfo_s *devinfo) -{ - FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; - - DEBUGASSERT(drvr && devinfo); - devinfo->speed = priv->lowspeed ? DEVINFO_SPEED_LOW : DEVINFO_SPEED_FULL; - return OK; -} - /************************************************************************************ * Name: stm32_epalloc * @@ -3375,54 +3982,33 @@ static int stm32_epalloc(FAR struct usbhost_driver_s *drvr, FAR usbhost_ep_t *ep) { FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; - FAR struct stm32_chan_s *chan; - int chidx; int ret; /* Sanity check. NOTE that this method should only be called if a device is * connected (because we need a valid low speed indication). */ - DEBUGASSERT(priv && epdesc && ep && priv->connected); + DEBUGASSERT(drvr != 0 && epdesc != NULL && ep != NULL); /* We must have exclusive access to the USB host hardware and state structures */ stm32_takesem(&priv->exclsem); - /* Allocate a host channel for the endpoint */ - - chidx = stm32_chan_alloc(priv); - if (chidx < 0) - { - udbg("Failed to allocate a host channel\n"); - ret = -ENOMEM; - goto errout; - } - - /* Decode the endpoint descriptor to initialize the channel data structures. - * Note: Here we depend on the fact that the endpoint point type is - * encoded in the same way in the endpoint descriptor as it is in the OTG - * FS hardware. + /* Handler control pipes differently from other endpoint types. This is + * because the normal, "transfer" endpoints are unidirectional an require + * only a single channel. Control endpoints, however, are bi-diretional + * and require two channels, one for the IN and one for the OUT direction. */ - chan = &priv->chan[chidx]; - chan->epno = epdesc->addr & USB_EPNO_MASK; - chan->in = epdesc->in; - chan->eptype = epdesc->xfrtype; - chan->maxpacket = epdesc->mxpacketsize; - chan->indata1 = false; - chan->outdata1 = false; + if (epdesc->xfrtype == OTGFS_EPTYPE_CTRL) + { + ret = stm32_ctrlep_alloc(priv, epdesc, ep); + } + else + { + ret = stm32_xfrep_alloc(priv, epdesc, ep); + } - /* Then configure the endpoint */ - - stm32_chan_configure(priv, chidx); - - /* Return the index to the allocated channel as the endpoint "handle" */ - - *ep = (usbhost_ep_t)chidx; - ret = OK; - -errout: stm32_givesem(&priv->exclsem); return ret; } @@ -3449,18 +4035,37 @@ errout: static int stm32_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) { - struct stm32_usbhost_s *priv = (struct stm32_usbhost_s *)drvr; - int chidx = (int)ep; + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + FAR struct stm32_ctrlinfo_s *ctrlep; - DEBUGASSERT(priv && chidx < STM32_MAX_TX_FIFOS); + DEBUGASSERT(priv); /* We must have exclusive access to the USB host hardware and state structures */ stm32_takesem(&priv->exclsem); - /* Halt the channel and mark the channel avaiable */ + /* A single channel is represent by an index in the range of 0 to STM32_MAX_TX_FIFOS. + * Otherwise, the ep must be a pointer to an allocated control endpoint structure. + */ - stm32_chan_free(priv, chidx); + if ((uintptr_t)ep < STM32_MAX_TX_FIFOS) + { + /* Halt the channel and mark the channel available */ + + stm32_chan_free(priv, (int)ep); + } + else + { + /* Halt both control channel and mark the channels available */ + + FAR struct stm32_ctrlinfo_s *ctrlep = ( FAR struct stm32_ctrlinfo_s *)ep; + stm32_chan_free(priv, ctrlep->inndx); + stm32_chan_free(priv, ctrlep->outndx); + + /* And free the control endpoint container */ + + kmm_free(ctrlep); + } stm32_givesem(&priv->exclsem); return OK; @@ -3647,11 +4252,12 @@ static int stm32_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) * Input Parameters: * drvr - The USB host driver instance obtained as a parameter from the call to * the class create() method. + * ep0 - The control endpoint to send/receive the control request. * req - Describes the request to be sent. This request must lie in memory * created by DRVR_ALLOC. * buffer - A buffer used for sending the request and for returning any * responses. This buffer must be large enough to hold the length value - * in the request description. buffer must have been allocated using DRVR_ALLOC + * in the request description. buffer must have been allocated using DRVR_ALLOC. * * NOTE: On an IN transaction, req and buffer may refer to the same allocated * memory. @@ -3661,13 +4267,12 @@ static int stm32_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) * returned indicating the nature of the failure * * Assumptions: - * - Only a single class bound to a single device is supported. * - Called from a single thread so no mutual exclusion is required. * - Never called from an interrupt handler. * *******************************************************************************/ -static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, +static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, FAR const struct usb_ctrlreq_s *req, FAR uint8_t *buffer) { @@ -3698,7 +4303,7 @@ static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, { /* Send the SETUP request */ - ret = stm32_ctrl_sendsetup(priv, req); + ret = stm32_ctrl_sendsetup(priv, ep0, req); if (ret < 0) { usbhost_trace1(OTGFS_TRACE1_SENDSETUP, -ret); @@ -3714,7 +4319,7 @@ static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, if (buflen > 0) { - ret = stm32_ctrl_recvdata(priv, buffer, buflen); + ret = stm32_ctrl_recvdata(priv, ep0, buffer, buflen); if (ret < 0) { usbhost_trace1(OTGFS_TRACE1_RECVDATA, -ret); @@ -3725,8 +4330,8 @@ static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, if (ret == OK) { - priv->chan[priv->ep0out].outdata1 ^= true; - ret = stm32_ctrl_senddata(priv, NULL, 0); + priv->chan[ep0->outndx].outdata1 ^= true; + ret = stm32_ctrl_senddata(priv, ep0, NULL, 0); if (ret == OK) { /* All success transactions exit here */ @@ -3751,7 +4356,7 @@ static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, return -ETIMEDOUT; } -static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, +static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, FAR const struct usb_ctrlreq_s *req, FAR const uint8_t *buffer) { @@ -3784,7 +4389,7 @@ static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, /* Send the SETUP request */ - ret = stm32_ctrl_sendsetup(priv, req); + ret = stm32_ctrl_sendsetup(priv, ep0, req); if (ret < 0) { usbhost_trace1(OTGFS_TRACE1_SENDSETUP, -ret); @@ -3802,8 +4407,8 @@ static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, { /* Start DATA out transfer (only one DATA packet) */ - priv->chan[priv->ep0out].outdata1 = true; - ret = stm32_ctrl_senddata(priv, NULL, 0); + priv->chan[ep0->outndx].outdata1 = true; + ret = stm32_ctrl_senddata(priv, ep0, NULL, 0); if (ret < 0) { usbhost_trace1(OTGFS_TRACE1_SENDDATA, -ret); @@ -3814,7 +4419,7 @@ static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, if (ret == OK) { - ret = stm32_ctrl_recvdata(priv, NULL, 0); + ret = stm32_ctrl_recvdata(priv, ep0, NULL, 0); if (ret == OK) { /* All success transactins exit here */ @@ -3844,9 +4449,9 @@ static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, * * Description: * Process a request to handle a transfer descriptor. This method will - * enqueue the transfer request and return immediately. Only one transfer may be - * queued; Neither this method nor the ctrlin or ctrlout methods can be called - * again until this function returns. + * enqueue the transfer request, blocking until the transfer completes. Only + * one transfer may be queued; Neither this method nor the ctrlin or + * ctrlout methods can be called again until this function returns. * * This is a blocking method; this functions will not return until the * transfer has completed. @@ -3871,7 +4476,6 @@ static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, * EPIPE - Overrun errors * * Assumptions: - * - Only a single class bound to a single device is supported. * - Called from a single thread so no mutual exclusion is required. * - Never called from an interrupt handler. * @@ -3907,6 +4511,151 @@ static int stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, return ret; } +/******************************************************************************* + * Name: stm32_asynch + * + * Description: + * Process a request to handle a transfer descriptor. This method will + * enqueue the transfer request and return immediately. When the transfer + * completes, the the callback will be invoked with the provided transfer. + * This method is useful for receiving interrupt transfers which may come + * infrequently. + * + * Only one transfer may be queued; Neither this method nor the ctrlin or + * ctrlout methods can be called again until the transfer completes. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on which to + * perform the transfer. + * buffer - A buffer containing the data to be sent (OUT endpoint) or received + * (IN endpoint). buffer must have been allocated using DRVR_ALLOC + * buflen - The length of the data to be sent or received. + * callback - This function will be called when the transfer completes. + * arg - The arbitrary parameter that will be passed to the callback function + * when the transfer completes. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg) +{ + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + unsigned int chidx = (unsigned int)ep; + int ret; + + uvdbg("chidx: %d buflen: %d\n", (unsigned int)ep, buflen); + + DEBUGASSERT(priv && buffer && chidx < STM32_MAX_TX_FIFOS && buflen > 0); + + /* We must have exclusive access to the USB host hardware and state structures */ + + stm32_takesem(&priv->exclsem); + + /* Handle IN and OUT transfer slightly differently */ + + if (priv->chan[chidx].in) + { + ret = stm32_in_asynch(priv, chidx, buffer, buflen, callback, arg); + } + else + { + ret = stm32_out_asynch(priv, chidx, buffer, buflen, callback, arg); + } + + stm32_givesem(&priv->exclsem); + return ret; +} +#endif /* CONFIG_USBHOST_ASYNCH */ + +/************************************************************************************ + * Name: stm32_cancel + * + * Description: + * Cancel a pending asynchronous transfer on an endpoint. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on which an + * asynchronous transfer should be transferred. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure. + * + ************************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) +{ +# error Missing logic + return -ENOSYS; + } +#endif /* CONFIG_USBHOST_ASYNCH */ + +/************************************************************************************ + * Name: stm32_connect + * + * Description: + * New connections may be detected by an attached hub. This method is the + * mechanism that is used by the hub class to introduce a new connection + * and port description to the system. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * hport - The descriptor of the hub port that detected the connection + * related event + * connected - True: device connected; false: device disconnected + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure. + * + ************************************************************************************/ + +#ifdef CONFIG_USBHOST_HUB +static int stm32_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport, + bool connected) +{ + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + irqstate_t flags; + + DEBUGASSERT(priv != NULL && hport != NULL); + + /* Set the connected/disconnected flag */ + + hport->connected = connected; + ullvdbg("Hub port %d connected: %s\n", hport->port, connected ? "YES" : "NO"); + + /* Report the connection event */ + + flags = irqsave(); + priv->hport = hport; + if (priv->pscwait) + { + priv->pscwait = false; + stm32_givesem(&priv->pscsem); + } + + irqrestore(flags); + return OK; +} +#endif + /******************************************************************************* * Name: stm32_disconnect * @@ -3932,10 +4681,10 @@ static int stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, static void stm32_disconnect(FAR struct usbhost_driver_s *drvr) { - struct stm32_usbhost_s *priv = (struct stm32_usbhost_s *)drvr; + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; DEBUGASSERT(priv); - priv->class = NULL; + priv->rhport.hport.devclass = NULL; } /******************************************************************************* @@ -4205,18 +4954,56 @@ static void stm32_host_initialize(FAR struct stm32_usbhost_s *priv) static inline void stm32_sw_initialize(FAR struct stm32_usbhost_s *priv) { + FAR struct usbhost_driver_s *drvr; + FAR struct usbhost_hubport_s *hport; int i; - /* Initialize the state data structure */ + /* Initialize the device operations */ - sem_init(&priv->eventsem, 0, 0); + drvr = &priv->drvr; + drvr->ep0configure = stm32_ep0configure; + drvr->epalloc = stm32_epalloc; + drvr->epfree = stm32_epfree; + drvr->alloc = stm32_alloc; + drvr->free = stm32_free; + drvr->ioalloc = stm32_ioalloc; + drvr->iofree = stm32_iofree; + drvr->ctrlin = stm32_ctrlin; + drvr->ctrlout = stm32_ctrlout; + drvr->transfer = stm32_transfer; +#ifdef CONFIG_USBHOST_ASYNCH + drvr->asynch = stm32_asynch; + drvr->cancel = stm32_cancel; +#endif +#ifdef CONFIG_USBHOST_HUB + drvr->connect = stm32_connect; +#endif + drvr->disconnect = stm32_disconnect; + + /* Initialize the public port representation */ + + hport = &priv->rhport.hport; + hport->drvr = drvr; +#ifdef CONFIG_USBHOST_HUB + hport->parent = NULL; +#endif + hport->ep0 = (usbhost_ep_t)&priv->ep0; + hport->speed = USB_SPEED_FULL; + + /* Initialize function address generation logic */ + + usbhost_devaddr_initialize(&priv->rhport); + + /* Initialize semaphores */ + + sem_init(&priv->pscsem, 0, 0); sem_init(&priv->exclsem, 0, 1); + /* Initialize the driver state data */ + priv->smstate = SMSTATE_DETACHED; - priv->ep0size = STM32_EP0_MAX_PACKET_SIZE; - priv->devaddr = STM32_DEF_DEVADDR; priv->connected = false; - priv->lowspeed = false; + priv->change = false; /* Put all of the channels back in their initial, allocated state */ diff --git a/arch/arm/src/stm32/stm32_otghshost.c b/arch/arm/src/stm32/stm32_otghshost.c index 8410308dbb..145daf2de6 100644 --- a/arch/arm/src/stm32/stm32_otghshost.c +++ b/arch/arm/src/stm32/stm32_otghshost.c @@ -1,7 +1,7 @@ /******************************************************************************* * arch/arm/src/stm32/stm32_otghshost.c * - * Copyright (C) 2012-2014 Gregory Nutt. All rights reserved. + * Copyright (C) 2012-2015 Gregory Nutt. All rights reserved. * Authors: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -54,6 +54,7 @@ #include #include #include +#include #include #include @@ -145,7 +146,6 @@ #define STM32_MAX_TX_FIFOS 12 /* Max number of TX FIFOs */ #define STM32_MAX_PKTCOUNT 256 /* Max packet count */ #define STM32_RETRY_COUNT 3 /* Number of ctrl transfer retries */ -#define STM32_DEF_DEVADDR 0 /* Default device address */ /* Delays **********************************************************************/ @@ -208,6 +208,8 @@ struct stm32_chan_s volatile uint8_t chreason; /* Channel halt reason. See enum stm32_chreason_e */ uint8_t epno; /* Device endpoint number (0-127) */ uint8_t eptype; /* See OTGHS_EPTYPE_* definitions */ + uint8_t funcaddr; /* Device function address */ + uint8_t speed; /* Device speed */ uint8_t pid; /* Data PID */ uint8_t npackets; /* Number of packets (for data toggle) */ bool inuse; /* True: This channel is "in use" */ @@ -219,6 +221,21 @@ struct stm32_chan_s volatile uint16_t buflen; /* Buffer length (remaining) */ volatile uint16_t inflight; /* Number of Tx bytes "in-flight" */ FAR uint8_t *buffer; /* Transfer buffer pointer */ +#ifdef CONFIG_USBHOST_ASYNCH + usbhost_asynch_t callback; /* Transfer complete callback */ + FAR void *arg; /* Argument that accompanies the callback */ +#endif +}; + +/* A channel represents on uni-directional endpoint. So, in the case of the + * bi-directional, control endpoint, there must be two channels to represent + * the endpoint. + */ + +struct stm32_ctrlinfo_s +{ + uint8_t inndx; /* EP0 IN control channel index */ + uint8_t outndx; /* EP0 OUT control channel index */ }; /* This structure retains the state of the USB host controller */ @@ -232,23 +249,26 @@ struct stm32_usbhost_s struct usbhost_driver_s drvr; - /* The bound device class driver */ + /* This is the hub port description understood by class drivers */ - struct usbhost_class_s *class; + struct usbhost_roothubport_s rhport; /* Overall driver status */ volatile uint8_t smstate; /* The state of the USB host state machine */ - uint8_t devaddr; /* Device address */ - uint8_t ep0in; /* EP0 IN control channel index */ - uint8_t ep0out; /* EP0 OUT control channel index */ - uint8_t ep0size; /* EP0 max packet size */ uint8_t chidx; /* ID of channel waiting for space in Tx FIFO */ - bool lowspeed; /* True: low speed device */ volatile bool connected; /* Connected to device */ - volatile bool eventwait; /* True: Thread is waiting for a port event */ + volatile bool change; /* Connection change */ + volatile bool pscwait; /* True: Thread is waiting for a port event */ sem_t exclsem; /* Support mutually exclusive access */ - sem_t eventsem; /* Semaphore to wait for a port event */ + sem_t pscsem; /* Semaphore to wait for a port event */ + struct stm32_ctrlinfo_s ep0; /* Root hub port EP0 description */ + +#ifdef CONFIG_USBHOST_HUB + /* Used to pass external hub port events */ + + volatile struct usbhost_hubport_s *hport; +#endif /* The state of each host channel */ @@ -299,10 +319,24 @@ static void stm32_chan_halt(FAR struct stm32_usbhost_s *priv, int chidx, enum stm32_chreason_e chreason); static int stm32_chan_waitsetup(FAR struct stm32_usbhost_s *priv, FAR struct stm32_chan_s *chan); +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_chan_asynchsetup(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan, + usbhost_asynch_t callback, FAR void *arg); +#endif static int stm32_chan_wait(FAR struct stm32_usbhost_s *priv, FAR struct stm32_chan_s *chan); static void stm32_chan_wakeup(FAR struct stm32_usbhost_s *priv, FAR struct stm32_chan_s *chan); +static int stm32_ctrlchan_alloc(FAR struct stm32_usbhost_s *priv, + uint8_t epno, uint8_t funcaddr, uint8_t speed, + FAR struct stm32_ctrlinfo_s *ctrlep); +static int stm32_ctrlep_alloc(FAR struct stm32_usbhost_s *priv, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep); +static int stm32_xfrep_alloc(FAR struct stm32_usbhost_s *priv, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep); /* Control/data transfer logic *************************************************/ @@ -311,15 +345,34 @@ static void stm32_transfer_start(FAR struct stm32_usbhost_s *priv, int chidx); static inline uint16_t stm32_getframe(void); #endif static int stm32_ctrl_sendsetup(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, FAR const struct usb_ctrlreq_s *req); static int stm32_ctrl_senddata(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, FAR uint8_t *buffer, unsigned int buflen); static int stm32_ctrl_recvdata(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, FAR uint8_t *buffer, unsigned int buflen); +static int stm32_in_setup(FAR struct stm32_usbhost_s *priv, int chidx); static int stm32_in_transfer(FAR struct stm32_usbhost_s *priv, int chidx, FAR uint8_t *buffer, size_t buflen); +#ifdef CONFIG_USBHOST_ASYNCH +static void stm32_in_next(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan); +static int stm32_in_asynch(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg); +#endif +static int stm32_out_setup(FAR struct stm32_usbhost_s *priv, int chidx); static int stm32_out_transfer(FAR struct stm32_usbhost_s *priv, int chidx, FAR uint8_t *buffer, size_t buflen); +#ifdef CONFIG_USBHOST_ASYNCH +static void stm32_out_next(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan); +static int stm32_out_asynch(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg); +#endif /* Interrupt handling **********************************************************/ /* Lower level interrupt handlers */ @@ -360,13 +413,16 @@ static void stm32_txfe_enable(FAR struct stm32_usbhost_s *priv, int chidx); /* USB host controller operations **********************************************/ static int stm32_wait(FAR struct usbhost_connection_s *conn, - FAR const bool *connected); -static int stm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx); + FAR struct usbhost_hubport_s **hport); +static int stm32_rh_enumerate(FAR struct stm32_usbhost_s *priv, + FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport); +static int stm32_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport); -static int stm32_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr, +static int stm32_ep0configure(FAR struct usbhost_driver_s *drvr, + usbhost_ep_t ep0, uint8_t funcaddr, uint8_t speed, uint16_t maxpacketsize); -static int stm32_getdevinfo(FAR struct usbhost_driver_s *drvr, - FAR struct usbhost_devinfo_s *devinfo); static int stm32_epalloc(FAR struct usbhost_driver_s *drvr, FAR const FAR struct usbhost_epdesc_s *epdesc, FAR usbhost_ep_t *ep); @@ -377,14 +433,28 @@ static int stm32_free(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer); static int stm32_ioalloc(FAR struct usbhost_driver_s *drvr, FAR uint8_t **buffer, size_t buflen); static int stm32_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer); -static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, - FAR const struct usb_ctrlreq_s *req, +static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + const struct usb_ctrlreq_s *req, FAR uint8_t *buffer); -static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, +static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, FAR const struct usb_ctrlreq_s *req, FAR const uint8_t *buffer); +static int stm32_transfer_common(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ed_s *ed,FAR uint8_t *buffer, + size_t buflen); static int stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, FAR uint8_t *buffer, size_t buflen); +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg); +static int stm32_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep); +#endif +#ifdef CONFIG_USBHOST_HUB +static int stm32_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport, + bool connected); +#endif static void stm32_disconnect(FAR struct usbhost_driver_s *drvr); /* Initialization **************************************************************/ @@ -407,25 +477,7 @@ static inline int stm32_hw_initialize(FAR struct stm32_usbhost_s *priv); * instance. */ -static struct stm32_usbhost_s g_usbhost = -{ - .drvr = - { - .ep0configure = stm32_ep0configure, - .getdevinfo = stm32_getdevinfo, - .epalloc = stm32_epalloc, - .epfree = stm32_epfree, - .alloc = stm32_alloc, - .free = stm32_free, - .ioalloc = stm32_ioalloc, - .iofree = stm32_iofree, - .ctrlin = stm32_ctrlin, - .ctrlout = stm32_ctrlout, - .transfer = stm32_transfer, - .disconnect = stm32_disconnect, - }, - .class = NULL, -}; +static struct stm32_usbhost_s g_usbhost; /* This is the connection/enumeration interface */ @@ -699,6 +751,7 @@ static inline void stm32_chan_freeall(FAR struct stm32_usbhost_s *priv) static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx) { + FAR struct stm32_chan_s *chan = &priv->chan[chidx]; uint32_t regval; /* Clear any old pending interrupts for this host channel. */ @@ -709,7 +762,7 @@ static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx) regval = 0; - switch (priv->chan[chidx].eptype) + switch (chan->eptype) { case OTGHS_EPTYPE_CTRL: case OTGHS_EPTYPE_BULK: @@ -720,7 +773,7 @@ static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx) /* Determine the definitive trace ID to use below */ - if (priv->chan[chidx].eptype == OTGHS_EPTYPE_CTRL) + if (chan->eptype == OTGHS_EPTYPE_CTRL) { intrace = OTGHS_VTRACE2_CHANCONF_CTRL_IN; outtrace = OTGHS_VTRACE2_CHANCONF_CTRL_OUT; @@ -739,14 +792,14 @@ static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx) /* Additional setting for IN/OUT endpoints */ - if (priv->chan[chidx].in) + if (chan->in) { - usbhost_vtrace2(intrace, chidx, priv->chan[chidx].epno); + usbhost_vtrace2(intrace, chidx, chan->epno); regval |= OTGHS_HCINT_BBERR; } else { - usbhost_vtrace2(outtrace, chidx, priv->chan[chidx].epno); + usbhost_vtrace2(outtrace, chidx, chan->epno); regval |= OTGHS_HCINT_NYET; } } @@ -761,17 +814,17 @@ static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx) /* Additional setting for IN endpoints */ - if (priv->chan[chidx].in) + if (chan->in) { usbhost_vtrace2(OTGHS_VTRACE2_CHANCONF_INTR_IN, chidx, - priv->chan[chidx].epno); + chan->epno); regval |= OTGHS_HCINT_BBERR; } #ifdef HAVE_USBHOST_TRACE_VERBOSE else { usbhost_vtrace2(OTGHS_VTRACE2_CHANCONF_INTR_OUT, chidx, - priv->chan[chidx].epno); + chan->epno); } #endif } @@ -785,17 +838,17 @@ static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx) /* Additional setting for IN endpoints */ - if (priv->chan[chidx].in) + if (chan->in) { usbhost_vtrace2(OTGHS_VTRACE2_CHANCONF_ISOC_IN, chidx, - priv->chan[chidx].epno); + chan->epno); regval |= (OTGHS_HCINT_TXERR | OTGHS_HCINT_BBERR); } #ifdef HAVE_USBHOST_TRACE_VERBOSE else { usbhost_vtrace2(OTGHS_VTRACE2_CHANCONF_ISOC_OUT, chidx, - priv->chan[chidx].epno); + chan->epno); } #endif } @@ -814,28 +867,28 @@ static void stm32_chan_configure(FAR struct stm32_usbhost_s *priv, int chidx) /* Program the HCCHAR register */ - regval = ((uint32_t)priv->chan[chidx].maxpacket << OTGHS_HCCHAR_MPSIZ_SHIFT) | - ((uint32_t)priv->chan[chidx].epno << OTGHS_HCCHAR_EPNUM_SHIFT) | - ((uint32_t)priv->chan[chidx].eptype << OTGHS_HCCHAR_EPTYP_SHIFT) | - ((uint32_t)priv->devaddr << OTGHS_HCCHAR_DAD_SHIFT); + regval = ((uint32_t)chan->maxpacket << OTGHS_HCCHAR_MPSIZ_SHIFT) | + ((uint32_t)chan->epno << OTGHS_HCCHAR_EPNUM_SHIFT) | + ((uint32_t)chan->eptype << OTGHS_HCCHAR_EPTYP_SHIFT) | + ((uint32_t)chan->funcaddr << OTGHS_HCCHAR_DAD_SHIFT); /* Special case settings for low speed devices */ - if (priv->lowspeed) + if (chan->speed == USB_SPEED_LOW) { regval |= OTGHS_HCCHAR_LSDEV; } /* Special case settings for IN endpoints */ - if (priv->chan[chidx].in) + if (chan->in) { regval |= OTGHS_HCCHAR_EPDIR_IN; } /* Special case settings for INTR endpoints */ - if (priv->chan[chidx].eptype == OTGHS_EPTYPE_INTR) + if (chan->eptype == OTGHS_EPTYPE_INTR) { regval |= OTGHS_HCCHAR_ODDFRM; } @@ -958,14 +1011,59 @@ static int stm32_chan_waitsetup(FAR struct stm32_usbhost_s *priv, * either (1) the device is disconnected, or (2) the transfer completed. */ - chan->waiter = true; - ret = OK; + chan->waiter = true; +#ifdef CONFIG_USBHOST_ASYNCH + chan->callback = NULL; + chan->arg = NULL; +#endif + ret = OK; } irqrestore(flags); return ret; } +/******************************************************************************* + * Name: stm32_chan_asynchsetup + * + * Description: + * Set the request for the transfer complete event well BEFORE enabling the + * transfer (as soon as we are absolutely committed to the to avoid transfer). + * We do this to minimize race conditions. This logic would have to be expanded + * if we want to have more than one packet in flight at a time! + * + * Assumptions: + * Might be called from the level of an interrupt handler + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_chan_asynchsetup(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan. + usbhost_asynch_t callback, FAR void *arg) +{ + irqstate_t flags = irqsave(); + int ret = -ENODEV; + + /* Is the device still connected? */ + + if (priv->connected) + { + /* Yes.. then set waiter to indicate that we expect to be informed when + * either (1) the device is disconnected, or (2) the transfer completed. + */ + + chan->waiter = false; + chan->callback = callback; + chan->arg = arg; + ret = OK; + } + + irqrestore(flags); + return ret; +} +#endif + /******************************************************************************* * Name: stm32_chan_wait * @@ -1037,19 +1135,246 @@ static int stm32_chan_wait(FAR struct stm32_usbhost_s *priv, static void stm32_chan_wakeup(FAR struct stm32_usbhost_s *priv, FAR struct stm32_chan_s *chan) { - /* Is the transfer complete? Is there a thread waiting for this transfer - * to complete? + /* Is the transfer complete? */ + + if (chan->result != EBUSY) + { + /* Is there a thread waiting for this transfer to complete? */ + + if (chan->waiter) + { +#ifdef CONFIG_USBHOST_ASYNCH + /* Yes.. there should not also be a callback scheduled */ + + DEBUGASSERT(chan->callback == NULL); +#endif + /* Wake'em up! */ + + usbhost_vtrace2(chan->in ? OTGHS_VTRACE2_CHANWAKEUP_IN : + OTGHS_VTRACE2_CHANWAKEUP_OUT, + chan->epno, chan->result); + + stm32_givesem(&chan->waitsem); + chan->waiter = false; + } + +#ifdef CONFIG_USBHOST_ASYNCH + /* No.. is an asynchronous callback expected when the transfer + * completes? + */ + + else if (chan->callback) + { + /* Handle continuation of IN/OUT pipes */ + + if (priv->in) + { + stm32_in_next(priv, chan); + } + else + { + stm32_out_next(priv, chan); + } + } +#endif + } +} + +/******************************************************************************* + * Name: stm32_ctrlchan_alloc + * + * Description: + * Allocate and configured channels for a control pipe. + * + *******************************************************************************/ + +static int stm32_ctrlchan_alloc(FAR struct stm32_usbhost_s *priv, + uint8_t epno, uint8_t funcaddr, uint8_t speed, + FAR struct stm32_ctrlinfo_s *ctrlep) +{ + FAR struct stm32_chan_s *chan; + int inndx; + int outndx; + + outndx = stm32_chan_alloc(priv); + if (outndx < 0) + { + return -ENOMEM; + } + + ctrlep->outndx = outndx; + chan = &priv->chan[outndx]; + chan->epno = epno; + chan->in = false; + chan->eptype = OTGHS_EPTYPE_CTRL; + chan->funcaddr = funcaddr; + chan->speed = speed; + chan->maxpacket = STM32_EP0_DEF_PACKET_SIZE; + chan->indata1 = false; + chan->outdata1 = false; + + /* Configure control OUT channels */ + + stm32_chan_configure(priv, outndx); + + /* Allocate and initialize the control IN channel */ + + inndx = stm32_chan_alloc(priv); + if (inndx < 0) + { + stm32_chan_free(priv, outndx); + return -ENOMEM; + } + + ctrlep->inndx = inndx; + chan = &priv->chan[inndx]; + chan->epno = epno; + chan->in = true; + chan->eptype = OTGHS_EPTYPE_CTRL; + chan->funcaddr = funcaddr; + chan->speed = speed; + chan->maxpacket = STM32_EP0_DEF_PACKET_SIZE; + chan->indata1 = false; + chan->outdata1 = false; + + /* Configure control IN channels */ + + stm32_chan_configure(priv, inndx); + return OK; +} + +/******************************************************************************* + * Name: stm32_ctrlep_alloc + * + * Description: + * Allocate a container and channels for control pipe. + * + * Input Parameters: + * priv - The private USB host driver state. + * epdesc - Describes the endpoint to be allocated. + * ep - A memory location provided by the caller in which to receive the + * allocated endpoint descriptor. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + *******************************************************************************/ + +static int stm32_ctrlep_alloc(FAR struct stm32_usbhost_s *priv, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep) +{ + FAR struct usbhost_hubport_s *hport; + FAR struct stm32_ctrlinfo_s *ctrlep; + int ret; + + /* Sanity check. NOTE that this method should only be called if a device is + * connected (because we need a valid low speed indication). */ - if (chan->result != EBUSY && chan->waiter) - { - usbhost_vtrace2(chan->in ? OTGHS_VTRACE2_CHANWAKEUP_IN : - OTGHS_VTRACE2_CHANWAKEUP_OUT, - chan->epno, chan->result); + DEBUGASSERT(epdesc->hport != NULL); + hport = epdesc->hport; - stm32_givesem(&chan->waitsem); - chan->waiter = false; + /* Allocate a container for the control endpoint */ + + ctrlep = (FAR struct stm32_ctrlinfo_s *)kmm_malloc(sizeof(struct stm32_ctrlinfo_s)); + if (ctrlep == NULL) + { + udbg("ERROR: Failed to allocate control endpoint container\n"); + return -ENOMEM; } + + /* Then allocate and configure the IN/OUT channnels */ + + ret = stm32_ctrlchan_alloc(priv, epdesc->addr & USB_EPNO_MASK, + hport->funcaddr, hport->speed, ctrlep); + if (ret < 0) + { + udbg("ERROR: stm32_ctrlchan_alloc failed: %d\n", ret); + kmm_free(ctrlep); + return ret; + } + + /* Return a pointer to the control pipe container as the pipe "handle" */ + + *ep = (usbhost_ep_t)ctrlep; + ret = OK; +} + +/************************************************************************************ + * Name: stm32_xfrep_alloc + * + * Description: + * Allocate and configure one unidirectional endpoint. + * + * Input Parameters: + * priv - The private USB host driver state. + * epdesc - Describes the endpoint to be allocated. + * ep - A memory location provided by the caller in which to receive the + * allocated endpoint descriptor. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * This function will *not* be called from an interrupt handler. + * + ************************************************************************************/ + +static int stm32_xfrep_alloc(FAR struct stm32_usbhost_s *priv, + FAR const struct usbhost_epdesc_s *epdesc, + FAR usbhost_ep_t *ep) +{ + struct usbhost_hubport_s *hport; + FAR struct stm32_chan_s *chan; + int chidx; + int ret; + + /* Sanity check. NOTE that this method should only be called if a device is + * connected (because we need a valid low speed indication). + */ + + DEBUGASSERT(epdesc->hport != NULL); + hport = epdesc->hport; + + /* Allocate a host channel for the endpoint */ + + chidx = stm32_chan_alloc(priv); + if (chidx < 0) + { + udbg("ERROR: Failed to allocate a host channel\n"); + return -ENOMEM; + } + + /* Decode the endpoint descriptor to initialize the channel data structures. + * Note: Here we depend on the fact that the endpoint point type is + * encoded in the same way in the endpoint descriptor as it is in the OTG + * HS hardware. + */ + + chan = &priv->chan[chidx]; + chan->epno = epdesc->addr & USB_EPNO_MASK; + chan->in = epdesc->in; + chan->eptype = epdesc->xfrtype; + chan->funcaddr = hport->funcaddr; + chan->speed = hport->speed; + chan->maxpacket = epdesc->mxpacketsize; + chan->indata1 = false; + chan->outdata1 = false; + + /* Then configure the endpoint */ + + stm32_chan_configure(priv, chidx); + + /* Return the index to the allocated channel as the endpoint "handle" */ + + *ep = (usbhost_ep_t)chidx; + return OK; } /******************************************************************************* @@ -1267,6 +1592,7 @@ static inline uint16_t stm32_getframe(void) *******************************************************************************/ static int stm32_ctrl_sendsetup(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, FAR const struct usb_ctrlreq_s *req) { FAR struct stm32_chan_s *chan; @@ -1276,7 +1602,7 @@ static int stm32_ctrl_sendsetup(FAR struct stm32_usbhost_s *priv, /* Loop while the device reports NAK (and a timeout is not exceeded */ - chan = &priv->chan[priv->ep0out]; + chan = &priv->chan[ep0->outndx]; start = clock_systimer(); do @@ -1298,7 +1624,7 @@ static int stm32_ctrl_sendsetup(FAR struct stm32_usbhost_s *priv, /* Start the transfer */ - stm32_transfer_start(priv, priv->ep0out); + stm32_transfer_start(priv, ep0->outndx); /* Wait for the transfer to complete */ @@ -1342,9 +1668,10 @@ static int stm32_ctrl_sendsetup(FAR struct stm32_usbhost_s *priv, *******************************************************************************/ static int stm32_ctrl_senddata(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, FAR uint8_t *buffer, unsigned int buflen) { - FAR struct stm32_chan_s *chan = &priv->chan[priv->ep0out]; + FAR struct stm32_chan_s *chan = &priv->chan[ep0->outndx]; int ret; /* Save buffer information */ @@ -1376,7 +1703,7 @@ static int stm32_ctrl_senddata(FAR struct stm32_usbhost_s *priv, /* Start the transfer */ - stm32_transfer_start(priv, priv->ep0out); + stm32_transfer_start(priv, ep0->outndx); /* Wait for the transfer to complete and return the result */ @@ -1393,9 +1720,10 @@ static int stm32_ctrl_senddata(FAR struct stm32_usbhost_s *priv, *******************************************************************************/ static int stm32_ctrl_recvdata(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_ctrlinfo_s *ep0, FAR uint8_t *buffer, unsigned int buflen) { - FAR struct stm32_chan_s *chan = &priv->chan[priv->ep0in]; + FAR struct stm32_chan_s *chan = &priv->chan[ep0->inndx]; int ret; /* Save buffer information */ @@ -1415,13 +1743,75 @@ static int stm32_ctrl_recvdata(FAR struct stm32_usbhost_s *priv, /* Start the transfer */ - stm32_transfer_start(priv, priv->ep0in); + stm32_transfer_start(priv, ep0->inndx); /* Wait for the transfer to complete and return the result */ return stm32_chan_wait(priv, chan); } +/******************************************************************************* + * Name: stm32_in_setup + * + * Description: + * Initiate an IN transfer on an bulk, interrupt, or isochronous pipe. + * + *******************************************************************************/ + +static int stm32_in_setup(FAR struct stm32_usbhost_s *priv, int chidx) +{ + FAR struct stm32_chan_s *chan; + int ret = OK; + + /* Set up for the transfer based on the direction and the endpoint type */ + + chan = &priv->chan[chidx]; + switch (chan->eptype) + { + default: + case OTGHS_EPTYPE_CTRL: /* Control */ + { + /* This kind of transfer on control endpoints other than EP0 are not + * currently supported + */ + + return -ENOSYS; + } + + case OTGHS_EPTYPE_ISOC: /* Isochronous */ + { + /* Set up the IN data PID */ + + usbhost_vtrace2(OTGHS_VTRACE2_ISOCIN, chidx, chan->buflen); + chan->pid = OTGHS_PID_DATA0; + } + break; + + case OTGHS_EPTYPE_BULK: /* Bulk */ + { + /* Setup the IN data PID */ + + usbhost_vtrace2(OTGHS_VTRACE2_BULKIN, chidx, chan->buflen); + chan->pid = chan->indata1 ? OTGHS_PID_DATA1 : OTGHS_PID_DATA0; + } + break; + + case OTGHS_EPTYPE_INTR: /* Interrupt */ + { + /* Setup the IN data PID */ + + usbhost_vtrace2(OTGHS_VTRACE2_INTRIN, chidx, chan->buflen); + chan->pid = chan->indata1 ? OTGHS_PID_DATA1 : OTGHS_PID_DATA0; + } + break; + } + + /* Start the transfer */ + + stm32_transfer_start(priv, chidx); + return OK; +} + /******************************************************************************* * Name: stm32_in_transfer * @@ -1460,50 +1850,13 @@ static int stm32_in_transfer(FAR struct stm32_usbhost_s *priv, int chidx, /* Set up for the transfer based on the direction and the endpoint type */ - switch (chan->eptype) + ret = stm32_in_setup(priv, chidx); + if (ret < 0) { - default: - case OTGHS_EPTYPE_CTRL: /* Control */ - { - /* This kind of transfer on control endpoints other than EP0 are not - * currently supported - */ - - return -ENOSYS; - } - - case OTGHS_EPTYPE_ISOC: /* Isochronous */ - { - /* Set up the IN data PID */ - - usbhost_vtrace2(OTGHS_VTRACE2_ISOCIN, chidx, buflen); - chan->pid = OTGHS_PID_DATA0; - } - break; - - case OTGHS_EPTYPE_BULK: /* Bulk */ - { - /* Setup the IN data PID */ - - usbhost_vtrace2(OTGHS_VTRACE2_BULKIN, chidx, buflen); - chan->pid = chan->indata1 ? OTGHS_PID_DATA1 : OTGHS_PID_DATA0; - } - break; - - case OTGHS_EPTYPE_INTR: /* Interrupt */ - { - /* Setup the IN data PID */ - - usbhost_vtrace2(OTGHS_VTRACE2_INTRIN, chidx, buflen); - chan->pid = chan->indata1 ? OTGHS_PID_DATA1 : OTGHS_PID_DATA0; - } - break; + udbg("ERROR: stm32_in_setup failed: %d\n", ret); + return ret; } - /* Start the transfer */ - - stm32_transfer_start(priv, chidx); - /* Wait for the transfer to complete and get the result */ ret = stm32_chan_wait(priv, chan); @@ -1539,6 +1892,174 @@ static int stm32_in_transfer(FAR struct stm32_usbhost_s *priv, int chidx, return ret; } +/******************************************************************************* + * Name: stm32_in_next + * + * Description: + * Initiate the next of a sequence of asynchronous transfers. + * + * Assumptions: + * This function is always called from an interrupt handler + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static void stm32_in_next(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan) +{ + usbhost_asynch_t callback; + FAR void *arg; + int result; + int ret; + + /* Is the full transfer complete? Did the last chunk transfer complete OK?*/ + + result = chan->result; + if (chan->buflen > 0 && result == OK) + { + /* Yes.. Set up for the next transfer based on the direction and the + * endpoint type + */ + + ret = stm32_in_setup(priv, chidx); + if (ret >= 0) + { + return; + } + + udbg("ERROR: stm32_in_setup failed: %d\n", ret); + result = ret; + } + + /* The transfer is complete, with or without an error */ + + uvdbg("Transfer complete: %d\n", result); + + /* Extract the callback information */ + + callback = chan->callback; + arg = chan->arg; + chan->callback = NULL; + chan->arg = NULL; + + /* Then perform the callback */ + + callback(arg, chan->result); +} +#endif + +/******************************************************************************* + * Name: stm32_in_asynch + * + * Description: + * Initiate the first of a sequence of asynchronous transfers. + * + * Assumptions: + * This function is never called from an interrupt handler + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_in_asynch(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg) +{ + FAR struct stm32_chan_s *chan; + int ret; + + /* Set up for the transfer data and callback BEFORE starting the first transfer */ + + chan = &priv->chan[chidx]; + chan->buffer = buffer; + chan->buflen = buflen; + + ret = stm32_chan_asynchsetup(priv, chan, callback, arg); + if (ret < 0) + { + udbg("ERROR: stm32_chan_asynchsetup failed: %d\n", ret); + return ret; + } + + /* Set up for the transfer based on the direction and the endpoint type */ + + ret = stm32_in_setup(priv, chidx); + if (ret < 0) + { + udbg("ERROR: stm32_in_setup failed: %d\n", ret); + } + + /* And return with the transfer pending */ + + return ret; +} +#endif + +/******************************************************************************* + * Name: stm32_out_setup + * + * Description: + * Initiate an OUT transfer on an bulk, interrupt, or isochronous pipe. + * + *******************************************************************************/ + +static int stm32_out_setup(FAR struct stm32_usbhost_s *priv, int chidx) +{ + FAR struct stm32_chan_s *chan; + int ret; + + /* Set up for the transfer based on the direction and the endpoint type */ + + chan = &priv->chan[chidx]; + switch (chan->eptype) + { + default: + case OTGHS_EPTYPE_CTRL: /* Control */ + { + /* This kind of transfer on control endpoints other than EP0 are not + * currently supported + */ + + return -ENOSYS; + } + + case OTGHS_EPTYPE_ISOC: /* Isochronous */ + { + /* Set up the OUT data PID */ + + usbhost_vtrace2(OTGHS_VTRACE2_ISOCOUT, chidx, buflen); + chan->pid = OTGHS_PID_DATA0; + } + break; + + case OTGHS_EPTYPE_BULK: /* Bulk */ + { + /* Setup the OUT data PID */ + + usbhost_vtrace2(OTGHS_VTRACE2_BULKOUT, chidx, buflen); + chan->pid = chan->outdata1 ? OTGHS_PID_DATA1 : OTGHS_PID_DATA0; + } + break; + + case OTGHS_EPTYPE_INTR: /* Interrupt */ + { + /* Setup the OUT data PID */ + + usbhost_vtrace2(OTGHS_VTRACE2_INTROUT, chidx, buflen); + chan->pid = chan->outdata1 ? OTGHS_PID_DATA1 : OTGHS_PID_DATA0; + + /* Toggle the OUT data PID for the next transfer */ + + chan->outdata1 ^= true; + } + break; + } + + /* Start the transfer */ + + stm32_transfer_start(priv, chidx); + return OK; +} + /******************************************************************************* * Name: stm32_out_transfer * @@ -1560,8 +2081,8 @@ static int stm32_out_transfer(FAR struct stm32_usbhost_s *priv, int chidx, * or a fatal error occurs (any error other than a simple NAK) */ - chan = &priv->chan[chidx]; - start = clock_systimer(); + chan = &priv->chan[chidx]; + start = clock_systimer(); while (buflen > 0) { @@ -1585,55 +2106,14 @@ static int stm32_out_transfer(FAR struct stm32_usbhost_s *priv, int chidx, /* Set up for the transfer based on the direction and the endpoint type */ - switch (chan->eptype) + ret = stm32_out_setup(priv, chidx); + if (ret < 0) { - default: - case OTGHS_EPTYPE_CTRL: /* Control */ - { - /* This kind of transfer on control endpoints other than EP0 are not - * currently supported - */ - - return -ENOSYS; - } - - case OTGHS_EPTYPE_ISOC: /* Isochronous */ - { - /* Set up the OUT data PID */ - - usbhost_vtrace2(OTGHS_VTRACE2_ISOCOUT, chidx, buflen); - chan->pid = OTGHS_PID_DATA0; - } - break; - - case OTGHS_EPTYPE_BULK: /* Bulk */ - { - /* Setup the OUT data PID */ - - usbhost_vtrace2(OTGHS_VTRACE2_BULKOUT, chidx, buflen); - chan->pid = chan->outdata1 ? OTGHS_PID_DATA1 : OTGHS_PID_DATA0; - } - break; - - case OTGHS_EPTYPE_INTR: /* Interrupt */ - { - /* Setup the OUT data PID */ - - usbhost_vtrace2(OTGHS_VTRACE2_INTROUT, chidx, buflen); - chan->pid = chan->outdata1 ? OTGHS_PID_DATA1 : OTGHS_PID_DATA0; - - /* Toggle the OUT data PID for the next transfer */ - - chan->outdata1 ^= true; - } - break; + udbg("ERROR: stm32_out_setup failed: %d\n", ret); + return ret; } - /* Start the transfer */ - - stm32_transfer_start(priv, chidx); - - /* Wait for the transfer to complete and get the result */ + /* Wait for the transfer to complete and get the result */ ret = stm32_chan_wait(priv, chan); @@ -1684,6 +2164,108 @@ static int stm32_out_transfer(FAR struct stm32_usbhost_s *priv, int chidx, return ret; } +/******************************************************************************* + * Name: stm32_out_next + * + * Description: + * Initiate the next of a sequence of asynchronous transfers. + * + * Assumptions: + * This function is always called from an interrupt handler + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static void stm32_out_next(FAR struct stm32_usbhost_s *priv, + FAR struct stm32_chan_s *chan) +{ + usbhost_asynch_t callback; + FAR void *arg; + int result; + int ret; + + /* Is the full transfer complete? Did the last chunk transfer complete OK?*/ + + result = chan->result; + if (chan->buflen > 0 && result == OK) + { + /* Yes.. Set up for the next transfer based on the direction and the + * endpoint type + */ + + ret = stm32_out_setup(priv, chidx); + if (ret >= 0) + { + return; + } + + udbg("ERROR: stm32_out_setup failed: %d\n", ret); + result = ret; + } + + /* The transfer is complete, with or without an error */ + + uvdbg("Transfer complete: %d\n", result); + + /* Extract the callback information */ + + callback = chan->callback; + arg = chan->arg; + chan->callback = NULL; + chan->arg = NULL; + + /* Then perform the callback */ + + callback(arg, chan->result); +} +#endif + +/******************************************************************************* + * Name: stm32_out_asynch + * + * Description: + * Initiate the first of a sequence of asynchronous transfers. + * + * Assumptions: + * This function is never called from an interrupt handler + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_out_asynch(FAR struct stm32_usbhost_s *priv, int chidx, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg) +{ + FAR struct stm32_chan_s *chan; + int ret; + + /* Set up for the transfer data and callback BEFORE starting the first transfer */ + + chan = &priv->chan[chidx]; + chan->buffer = buffer; + chan->buflen = buflen; + + ret = stm32_chan_asynchsetup(priv, chan, callback, arg); + if (ret < 0) + { + udbg("ERROR: stm32_chan_asynchsetup failed: %d\n", ret); + return ret; + } + + /* Set up for the transfer based on the direction and the endpoint type */ + + ret = stm32_out_setup(priv, chidx); + if (ret < 0) + { + udbg("ERROR: stm32_out_setup failed: %d\n", ret); + } + + /* And return with the transfer pending */ + + return ret; +} +#endif + /******************************************************************************* * Name: stm32_gint_wrpacket * @@ -2226,15 +2808,16 @@ static void stm32_gint_connected(FAR struct stm32_usbhost_s *priv) usbhost_vtrace1(OTGHS_VTRACE1_CONNECTED,0); priv->connected = true; + priv->change = true; DEBUGASSERT(priv->smstate == SMSTATE_DETACHED); /* Notify any waiters */ priv->smstate = SMSTATE_ATTACHED; - if (priv->eventwait) + if (priv->pscwait) { - stm32_givesem(&priv->eventsem); - priv->eventwait = false; + stm32_givesem(&priv->pscsem); + priv->pscwait = false; } } } @@ -2259,29 +2842,29 @@ static void stm32_gint_disconnected(FAR struct stm32_usbhost_s *priv) /* Are we bound to a class driver? */ - if (priv->class) + if ( priv->rhport.hport.devclass) { /* Yes.. Disconnect the class driver */ - CLASS_DISCONNECTED(priv->class); - priv->class = NULL; + CLASS_DISCONNECTED( priv->rhport.hport.devclass); + priv->rhport.hport.devclass = NULL; } - /* Re-Initilaize Host for new Enumeration */ + /* Re-Initialize Host for new Enumeration */ priv->smstate = SMSTATE_DETACHED; - priv->ep0size = STM32_EP0_MAX_PACKET_SIZE; - priv->devaddr = STM32_DEF_DEVADDR; priv->connected = false; - priv->lowspeed = false; + priv->change = true; stm32_chan_freeall(priv); + priv->rhport.hport.speed = USB_SPEED_FULL; + /* Notify any waiters that there is a change in the connection state */ - if (priv->eventwait) + if (priv->pscwait) { - stm32_givesem(&priv->eventsem); - priv->eventwait = false; + stm32_givesem(&priv->pscsem); + priv->pscwait = false; } } } @@ -2722,8 +3305,8 @@ static inline void stm32_gint_hprtisr(FAR struct stm32_usbhost_s *priv) if ((hcfg & OTGHS_HCFG_FSLSPCS_MASK) != OTGHS_HCFG_FSLSPCS_LS6MHz) { - usbhost_vtrace1(OTGHS_VTRACE1_GINT_HPRT_FSLSSW, 0); + /* Yes... configure for LS */ hcfg &= ~OTGHS_HCFG_FSLSPCS_MASK; @@ -3099,19 +3682,20 @@ static void stm32_txfe_enable(FAR struct stm32_usbhost_s *priv, int chidx) * Name: stm32_wait * * Description: - * Wait for a device to be connected or disconneced. + * Wait for a device to be connected or disconnected to/from a hub port. * * Input Parameters: * conn - The USB host connection instance obtained as a parameter from the call to * the USB driver initialization logic. - * connected - A pointer to a boolean value. TRUE: Wait for device to be - * connected; FALSE: wait for device to be disconnected + * hport - The location to return the hub port descriptor that detected the + * connection related event. * * Returned Values: - * Zero (OK) is returned when a device in connected. This function will not - * return until either (1) a device is connected or (2) some failure occurs. - * On a failure, a negated errno value is returned indicating the nature of - * the failure + * Zero (OK) is returned on success when a device in connected or + * disconnected. This function will not return until either (1) a device is + * connected or disconnect to/from any hub port or until (2) some failure + * occurs. On a failure, a negated errno value is returned indicating the + * nature of the failure * * Assumptions: * - Called from a single thread so no mutual exclusion is required. @@ -3120,26 +3704,62 @@ static void stm32_txfe_enable(FAR struct stm32_usbhost_s *priv, int chidx) *******************************************************************************/ static int stm32_wait(FAR struct usbhost_connection_s *conn, - FAR const bool *connected) + FAR struct usbhost_hubport_s **hport) { FAR struct stm32_usbhost_s *priv = &g_usbhost; + struct usbhost_hubport_s *connport; irqstate_t flags; - /* Are we already connected? */ + /* Loop until a change in connection state is detected */ flags = irqsave(); - while (priv->connected == *connected) + for (;;) { - /* No... wait for the connection/disconnection */ + /* Is there a change in the connection state of the single root hub + * port? + */ - priv->eventwait = true; - stm32_takesem(&priv->eventsem); + if (priv->change) + { + connport = &priv->rhport.hport; + + /* Yes. Remember the new state */ + + connport->connected = priv->connected; + priv->change = false; + + /* And return the root hub port */ + + *hport = connport; + irqrestore(flags); + + uvdbg("RHport Connected: %s\n", connport->connected ? "YES" : "NO"); + return OK; + } + +#ifdef CONFIG_USBHOST_HUB + /* Is a device connected to an external hub? */ + + if (priv->hport) + { + /* Yes.. return the external hub port */ + + connport = (struct usbhost_hubport_s *)priv->hport; + priv->hport = NULL; + + *hport = connport; + irqrestore(flags); + + uvdbg("Hub port Connected: %s\n", connport->connected ? "YES" : "NO"); + return OK; + } +#endif + + /* Wait for the next connection event */ + + priv->pscwait = true; + stm32_takesem(&priv->pscsem); } - - irqrestore(flags); - - udbg("Connected:%s\n", priv->connected ? "YES" : "NO"); - return OK; } /******************************************************************************* @@ -3151,34 +3771,33 @@ static int stm32_wait(FAR struct usbhost_connection_s *conn, * extract the class ID info from the configuration descriptor, (3) call * usbhost_findclass() to find the class that supports this device, (4) * call the create() method on the struct usbhost_registry_s interface - * to get a class instance, and finally (5) call the configdesc() method + * to get a class instance, and finally (5) call the connect() method * of the struct usbhost_class_s interface. After that, the class is in * charge of the sequence of operations. * * Input Parameters: - * conn - The USB host connection instance obtained as a parameter from the call to - * the USB driver initialization logic. - * rphndx - Root hub port index. 0-(n-1) corresponds to root hub port 1-n. + * conn - The USB host connection instance obtained as a parameter from + * the call to the USB driver initialization logic. + * hport - The descriptor of the hub port that has the newly connected + * device. * * Returned Values: * On success, zero (OK) is returned. On a failure, a negated errno value is * returned indicating the nature of the failure * * Assumptions: - * - Only a single class bound to a single device is supported. - * - Called from a single thread so no mutual exclusion is required. - * - Never called from an interrupt handler. + * This function will *not* be called from an interrupt handler. * *******************************************************************************/ -static int stm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) +static int stm32_rh_enumerate(FAR struct stm32_usbhost_s *priv, + FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport) { - FAR struct stm32_usbhost_s *priv = &g_usbhost; uint32_t regval; - int chidx; int ret; - DEBUGASSERT(priv && rhpndx == 0); + DEBUGASSERT(conn != NULL && hport != NULL && hport->port == 0); /* Are we connected to a device? The caller should have called the wait() * method first to be assured that a device is connected. @@ -3194,32 +3813,6 @@ static int stm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) DEBUGASSERT(priv->smstate == SMSTATE_ATTACHED); - /* Allocate and initialize the control OUT channel */ - - chidx = stm32_chan_alloc(priv); - DEBUGASSERT(chidx >= 0); - - priv->ep0out = chidx; - priv->chan[chidx].epno = 0; - priv->chan[chidx].in = false; - priv->chan[chidx].eptype = OTGHS_EPTYPE_CTRL; - priv->chan[chidx].maxpacket = STM32_EP0_DEF_PACKET_SIZE; - priv->chan[chidx].indata1 = false; - priv->chan[chidx].outdata1 = false; - - /* Allocate and initialize the control IN channel */ - - chidx = stm32_chan_alloc(priv); - DEBUGASSERT(chidx >= 0); - - priv->ep0in = chidx; - priv->chan[chidx].epno = 0; - priv->chan[chidx].in = true; - priv->chan[chidx].eptype = OTGHS_EPTYPE_CTRL; - priv->chan[chidx].maxpacket = STM32_EP0_DEF_PACKET_SIZE; - priv->chan[chidx].indata1 = false; - priv->chan[chidx].outdata1 = false; - /* USB 2.0 spec says at least 50ms delay before port reset. We wait 100ms. */ usleep(100*1000); @@ -3231,20 +3824,55 @@ static int stm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) /* Get the current device speed */ regval = stm32_getreg(STM32_OTGHS_HPRT); - priv->lowspeed = ((regval & OTGHS_HPRT_PSPD_MASK) == OTGHS_HPRT_PSPD_LS); + if ((regval & OTGHS_HPRT_PSPD_MASK) == OTGHS_HPRT_PSPD_LS) + { + priv->rhport.hport.speed = USB_SPEED_LOW. + } + else + { + priv->rhport.hport.speed = USB_SPEED_FULL; + } - /* Configure control channels */ + /* Allocate and initialize the root hub port EP0 channels */ - stm32_chan_configure(priv, priv->ep0out); - stm32_chan_configure(priv, priv->ep0in); + ret = stm32_ctrlchan_alloc(priv, 0, 0, priv->rhport.hport.speed, &priv->ep0); + if (ret < 0) + { + udbg("ERROR: Failed to allocate a control endpoint: %d\n", ret); + } - /* Let the common usbhost_enumerate do all of the real work. Note that the - * FunctionAddress (USB address) is hardcoded to one. - */ + return ret; +} + +static int stm32_enumerate(FAR struct usbhost_connection_s *conn, + FAR struct usbhost_hubport_s *hport) +{ + FAR struct stm32_usbhost_s *priv = &g_usbhost; + int ret; + + DEBUGASSERT(hport); + + /* If this is a connection on the root hub, then we need to go to + * little more effort to get the device speed. If it is a connection + * on an external hub, then we already have that information. + */ + +#ifdef CONFIG_USBHOST_HUB + if (ROOTHUB(hport)) +#endif + { + ret = stm32_rh_enumerate(priv, conn, hport); + if (ret < 0) + { + return ret; + } + } + + /* Then let the common usbhost_enumerate do the real enumeration. */ uvdbg("Enumerate the device\n"); priv->smstate = SMSTATE_ENUM; - ret = usbhost_enumerate(&g_usbhost.drvr, 1, &priv->class); + ret = usbhost_enumerate(hport, &hport->devclass); /* The enumeration may fail either because of some HCD interfaces failure * or because the device class is not supported. In either case, we just @@ -3256,6 +3884,7 @@ static int stm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) { /* Return to the disconnected state */ + udbg("ERROR: Enumeration failed: %d\n", ret); stm32_gint_disconnected(priv); } @@ -3273,8 +3902,10 @@ static int stm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) * Input Parameters: * drvr - The USB host driver instance obtained as a parameter from the call to * the class create() method. + * ep0 - The (opaque) EP0 endpoint instance * funcaddr - The USB address of the function containing the endpoint that EP0 * controls + * speed - The speed of the port USB_SPEED_LOW, _FULL, or _HIGH * maxpacketsize - The maximum number of bytes that can be sent to or * received from the endpoint in a single data packet * @@ -3287,67 +3918,43 @@ static int stm32_enumerate(FAR struct usbhost_connection_s *conn, int rhpndx) * ************************************************************************************/ -static int stm32_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr, +static int stm32_ep0configure(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, + uint8_t funcaddr, uint8_t speed, uint16_t maxpacketsize) { FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + FAR struct stm32_ctrlinfo_s *ep0info = (FAR struct stm32_ctrlinfo_s *)ep0; + FAR struct stm32_chan_s *chan; - DEBUGASSERT(drvr && funcaddr < 128 && maxpacketsize < 2048); + DEBUGASSERT(drvr != NULL && ep0info != NULL && && funcaddr < 128 && + maxpacketsize <= 64); /* We must have exclusive access to the USB host hardware and state structures */ stm32_takesem(&priv->exclsem); - /* Save the device address and EP0 max packet size */ - - priv->devaddr = funcaddr; - priv->ep0size = maxpacketsize; - /* Configure the EP0 OUT channel */ - priv->chan[priv->ep0out].maxpacket = maxpacketsize; - stm32_chan_configure(priv, priv->ep0out); + chan = priv->&riv->chan[ep0info->outndx]; + chan->funcaddr = funcaddr; + chan->speed = speed; + chan->maxpacket = maxpacketsize; + + stm32_chan_configure(priv, ep0info->outndx); /* Configure the EP0 IN channel */ - priv->chan[priv->ep0in].maxpacket = maxpacketsize; - stm32_chan_configure(priv, priv->ep0in); + chan = priv->&riv->chan[ep0info->inndx]; + chan->funcaddr = funcaddr; + chan->speed = speed; + chan->maxpacket = maxpacketsize; + + stm32_chan_configure(priv, ep0info->inndx); stm32_givesem(&priv->exclsem); return OK; } -/************************************************************************************ - * Name: stm32_getdevinfo - * - * Description: - * Get information about the connected device. - * - * Input Parameters: - * drvr - The USB host driver instance obtained as a parameter from the call to - * the class create() method. - * devinfo - A pointer to memory provided by the caller in which to return the - * device information. - * - * Returned Values: - * On success, zero (OK) is returned. On a failure, a negated errno value is - * returned indicating the nature of the failure - * - * Assumptions: - * This function will *not* be called from an interrupt handler. - * - ************************************************************************************/ - -static int stm32_getdevinfo(FAR struct usbhost_driver_s *drvr, - FAR struct usbhost_devinfo_s *devinfo) -{ - FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; - - DEBUGASSERT(drvr && devinfo); - devinfo->speed = priv->lowspeed ? DEVINFO_SPEED_LOW : DEVINFO_SPEED_FULL; - return OK; -} - /************************************************************************************ * Name: stm32_epalloc * @@ -3375,54 +3982,33 @@ static int stm32_epalloc(FAR struct usbhost_driver_s *drvr, FAR usbhost_ep_t *ep) { FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; - FAR struct stm32_chan_s *chan; - int chidx; int ret; /* Sanity check. NOTE that this method should only be called if a device is * connected (because we need a valid low speed indication). */ - DEBUGASSERT(priv && epdesc && ep && priv->connected); + DEBUGASSERT(drvr != 0 && epdesc != NULL && ep != NULL); /* We must have exclusive access to the USB host hardware and state structures */ stm32_takesem(&priv->exclsem); - /* Allocate a host channel for the endpoint */ - - chidx = stm32_chan_alloc(priv); - if (chidx < 0) - { - udbg("Failed to allocate a host channel\n"); - ret = -ENOMEM; - goto errout; - } - - /* Decode the endpoint descriptor to initialize the channel data structures. - * Note: Here we depend on the fact that the endpoint point type is - * encoded in the same way in the endpoint descriptor as it is in the OTG - * HS hardware. + /* Handler control pipes differently from other endpoint types. This is + * because the normal, "transfer" endpoints are unidirectional an require + * only a single channel. Control endpoints, however, are bi-diretional + * and require two channels, one for the IN and one for the OUT direction. */ - chan = &priv->chan[chidx]; - chan->epno = epdesc->addr & USB_EPNO_MASK; - chan->in = epdesc->in; - chan->eptype = epdesc->xfrtype; - chan->maxpacket = epdesc->mxpacketsize; - chan->indata1 = false; - chan->outdata1 = false; + if (epdesc->xfrtype == OTGHS_EPTYPE_CTRL) + { + ret = stm32_ctrlep_alloc(priv, epdesc, ep); + } + else + { + ret = stm32_xfrep_alloc(priv, epdesc, ep); + } - /* Then configure the endpoint */ - - stm32_chan_configure(priv, chidx); - - /* Return the index to the allocated channel as the endpoint "handle" */ - - *ep = (usbhost_ep_t)chidx; - ret = OK; - -errout: stm32_givesem(&priv->exclsem); return ret; } @@ -3449,18 +4035,37 @@ errout: static int stm32_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) { - struct stm32_usbhost_s *priv = (struct stm32_usbhost_s *)drvr; - int chidx = (int)ep; + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + FAR struct stm32_ctrlinfo_s *ctrlep; - DEBUGASSERT(priv && chidx < STM32_MAX_TX_FIFOS); + DEBUGASSERT(priv); /* We must have exclusive access to the USB host hardware and state structures */ stm32_takesem(&priv->exclsem); - /* Halt the channel and mark the channel avaiable */ + /* A single channel is represent by an index in the range of 0 to STM32_MAX_TX_FIFOS. + * Otherwise, the ep must be a pointer to an allocated control endpoint structure. + */ - stm32_chan_free(priv, chidx); + if ((uintptr_t)ep < STM32_MAX_TX_FIFOS) + { + /* Halt the channel and mark the channel available */ + + stm32_chan_free(priv, (int)ep); + } + else + { + /* Halt both control channel and mark the channels available */ + + FAR struct stm32_ctrlinfo_s *ctrlep = ( FAR struct stm32_ctrlinfo_s *)ep; + stm32_chan_free(priv, ctrlep->inndx); + stm32_chan_free(priv, ctrlep->outndx); + + /* And free the control endpoint container */ + + kmm_free(ctrlep); + } stm32_givesem(&priv->exclsem); return OK; @@ -3647,11 +4252,12 @@ static int stm32_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) * Input Parameters: * drvr - The USB host driver instance obtained as a parameter from the call to * the class create() method. + * ep0 - The control endpoint to send/receive the control request. * req - Describes the request to be sent. This request must lie in memory * created by DRVR_ALLOC. * buffer - A buffer used for sending the request and for returning any * responses. This buffer must be large enough to hold the length value - * in the request description. buffer must have been allocated using DRVR_ALLOC + * in the request description. buffer must have been allocated using DRVR_ALLOC. * * NOTE: On an IN transaction, req and buffer may refer to the same allocated * memory. @@ -3661,13 +4267,12 @@ static int stm32_iofree(FAR struct usbhost_driver_s *drvr, FAR uint8_t *buffer) * returned indicating the nature of the failure * * Assumptions: - * - Only a single class bound to a single device is supported. * - Called from a single thread so no mutual exclusion is required. * - Never called from an interrupt handler. * *******************************************************************************/ -static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, +static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, FAR const struct usb_ctrlreq_s *req, FAR uint8_t *buffer) { @@ -3698,7 +4303,7 @@ static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, { /* Send the SETUP request */ - ret = stm32_ctrl_sendsetup(priv, req); + ret = stm32_ctrl_sendsetup(priv, ep0, req); if (ret < 0) { usbhost_trace1(OTGHS_TRACE1_SENDSETUP, -ret); @@ -3714,7 +4319,7 @@ static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, if (buflen > 0) { - ret = stm32_ctrl_recvdata(priv, buffer, buflen); + ret = stm32_ctrl_recvdata(priv, ep0, buffer, buflen); if (ret < 0) { usbhost_trace1(OTGHS_TRACE1_RECVDATA, -ret); @@ -3725,8 +4330,8 @@ static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, if (ret == OK) { - priv->chan[priv->ep0out].outdata1 ^= true; - ret = stm32_ctrl_senddata(priv, NULL, 0); + priv->chan[ep0->outndx].outdata1 ^= true; + ret = stm32_ctrl_senddata(priv, ep0, NULL, 0); if (ret == OK) { /* All success transactions exit here */ @@ -3751,7 +4356,7 @@ static int stm32_ctrlin(FAR struct usbhost_driver_s *drvr, return -ETIMEDOUT; } -static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, +static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep0, FAR const struct usb_ctrlreq_s *req, FAR const uint8_t *buffer) { @@ -3784,7 +4389,7 @@ static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, /* Send the SETUP request */ - ret = stm32_ctrl_sendsetup(priv, req); + ret = stm32_ctrl_sendsetup(priv, ep0, req); if (ret < 0) { usbhost_trace1(OTGHS_TRACE1_SENDSETUP, -ret); @@ -3802,8 +4407,8 @@ static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, { /* Start DATA out transfer (only one DATA packet) */ - priv->chan[priv->ep0out].outdata1 = true; - ret = stm32_ctrl_senddata(priv, NULL, 0); + priv->chan[ep0->outndx].outdata1 = true; + ret = stm32_ctrl_senddata(priv, ep0, NULL, 0); if (ret < 0) { usbhost_trace1(OTGHS_TRACE1_SENDDATA, -ret); @@ -3814,7 +4419,7 @@ static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, if (ret == OK) { - ret = stm32_ctrl_recvdata(priv, NULL, 0); + ret = stm32_ctrl_recvdata(priv, ep0, NULL, 0); if (ret == OK) { /* All success transactins exit here */ @@ -3844,9 +4449,9 @@ static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, * * Description: * Process a request to handle a transfer descriptor. This method will - * enqueue the transfer request and return immediately. Only one transfer may be - * queued; Neither this method nor the ctrlin or ctrlout methods can be called - * again until this function returns. + * enqueue the transfer request, blocking until the transfer completes. Only + * one transfer may be queued; Neither this method nor the ctrlin or + * ctrlout methods can be called again until this function returns. * * This is a blocking method; this functions will not return until the * transfer has completed. @@ -3871,7 +4476,6 @@ static int stm32_ctrlout(FAR struct usbhost_driver_s *drvr, * EPIPE - Overrun errors * * Assumptions: - * - Only a single class bound to a single device is supported. * - Called from a single thread so no mutual exclusion is required. * - Never called from an interrupt handler. * @@ -3907,6 +4511,151 @@ static int stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, return ret; } +/******************************************************************************* + * Name: stm32_asynch + * + * Description: + * Process a request to handle a transfer descriptor. This method will + * enqueue the transfer request and return immediately. When the transfer + * completes, the the callback will be invoked with the provided transfer. + * This method is useful for receiving interrupt transfers which may come + * infrequently. + * + * Only one transfer may be queued; Neither this method nor the ctrlin or + * ctrlout methods can be called again until the transfer completes. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on which to + * perform the transfer. + * buffer - A buffer containing the data to be sent (OUT endpoint) or received + * (IN endpoint). buffer must have been allocated using DRVR_ALLOC + * buflen - The length of the data to be sent or received. + * callback - This function will be called when the transfer completes. + * arg - The arbitrary parameter that will be passed to the callback function + * when the transfer completes. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure + * + * Assumptions: + * - Called from a single thread so no mutual exclusion is required. + * - Never called from an interrupt handler. + * + *******************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_asynch(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, + FAR uint8_t *buffer, size_t buflen, + usbhost_asynch_t callback, FAR void *arg) +{ + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + unsigned int chidx = (unsigned int)ep; + int ret; + + uvdbg("chidx: %d buflen: %d\n", (unsigned int)ep, buflen); + + DEBUGASSERT(priv && buffer && chidx < STM32_MAX_TX_FIFOS && buflen > 0); + + /* We must have exclusive access to the USB host hardware and state structures */ + + stm32_takesem(&priv->exclsem); + + /* Handle IN and OUT transfer slightly differently */ + + if (priv->chan[chidx].in) + { + ret = stm32_in_asynch(priv, chidx, buffer, buflen, callback, arg); + } + else + { + ret = stm32_out_asynch(priv, chidx, buffer, buflen, callback, arg); + } + + stm32_givesem(&priv->exclsem); + return ret; +} +#endif /* CONFIG_USBHOST_ASYNCH */ + +/************************************************************************************ + * Name: stm32_cancel + * + * Description: + * Cancel a pending asynchronous transfer on an endpoint. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * ep - The IN or OUT endpoint descriptor for the device endpoint on which an + * asynchronous transfer should be transferred. + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure. + * + ************************************************************************************/ + +#ifdef CONFIG_USBHOST_ASYNCH +static int stm32_cancel(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep) +{ +# error Missing logic + return -ENOSYS; + } +#endif /* CONFIG_USBHOST_ASYNCH */ + +/************************************************************************************ + * Name: stm32_connect + * + * Description: + * New connections may be detected by an attached hub. This method is the + * mechanism that is used by the hub class to introduce a new connection + * and port description to the system. + * + * Input Parameters: + * drvr - The USB host driver instance obtained as a parameter from the call to + * the class create() method. + * hport - The descriptor of the hub port that detected the connection + * related event + * connected - True: device connected; false: device disconnected + * + * Returned Values: + * On success, zero (OK) is returned. On a failure, a negated errno value is + * returned indicating the nature of the failure. + * + ************************************************************************************/ + +#ifdef CONFIG_USBHOST_HUB +static int stm32_connect(FAR struct usbhost_driver_s *drvr, + FAR struct usbhost_hubport_s *hport, + bool connected) +{ + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; + irqstate_t flags; + + DEBUGASSERT(priv != NULL && hport != NULL); + + /* Set the connected/disconnected flag */ + + hport->connected = connected; + ullvdbg("Hub port %d connected: %s\n", hport->port, connected ? "YES" : "NO"); + + /* Report the connection event */ + + flags = irqsave(); + priv->hport = hport; + if (priv->pscwait) + { + priv->pscwait = false; + stm32_givesem(&priv->pscsem); + } + + irqrestore(flags); + return OK; +} +#endif + /******************************************************************************* * Name: stm32_disconnect * @@ -3932,10 +4681,10 @@ static int stm32_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep, static void stm32_disconnect(FAR struct usbhost_driver_s *drvr) { - struct stm32_usbhost_s *priv = (struct stm32_usbhost_s *)drvr; + FAR struct stm32_usbhost_s *priv = (FAR struct stm32_usbhost_s *)drvr; DEBUGASSERT(priv); - priv->class = NULL; + priv->rhport.hport.devclass = NULL; } /******************************************************************************* @@ -4136,7 +4885,7 @@ static void stm32_host_initialize(FAR struct stm32_usbhost_s *priv) /* Reset the host port */ - stm32_portreset(priv); + stm32_portreset(priv); /* Clear the HS-/LS-only support bit in the HCFG register */ @@ -4205,18 +4954,56 @@ static void stm32_host_initialize(FAR struct stm32_usbhost_s *priv) static inline void stm32_sw_initialize(FAR struct stm32_usbhost_s *priv) { + FAR struct usbhost_driver_s *drvr; + FAR struct usbhost_hubport_s *hport; int i; - /* Initialize the state data structure */ + /* Initialize the device operations */ - sem_init(&priv->eventsem, 0, 0); + drvr = &priv->drvr; + drvr->ep0configure = stm32_ep0configure; + drvr->epalloc = stm32_epalloc; + drvr->epfree = stm32_epfree; + drvr->alloc = stm32_alloc; + drvr->free = stm32_free; + drvr->ioalloc = stm32_ioalloc; + drvr->iofree = stm32_iofree; + drvr->ctrlin = stm32_ctrlin; + drvr->ctrlout = stm32_ctrlout; + drvr->transfer = stm32_transfer; +#ifdef CONFIG_USBHOST_ASYNCH + drvr->asynch = stm32_asynch; + drvr->cancel = stm32_cancel; +#endif +#ifdef CONFIG_USBHOST_HUB + drvr->connect = stm32_connect; +#endif + drvr->disconnect = stm32_disconnect; + + /* Initialize the public port representation */ + + hport = &priv->rhport.hport; + hport->drvr = drvr; +#ifdef CONFIG_USBHOST_HUB + hport->parent = NULL; +#endif + hport->ep0 = (usbhost_ep_t)&priv->ep0; + hport->speed = USB_SPEED_FULL; + + /* Initialize function address generation logic */ + + usbhost_devaddr_initialize(&priv->rhport); + + /* Initialize semaphores */ + + sem_init(&priv->pscsem, 0, 0); sem_init(&priv->exclsem, 0, 1); + /* Initialize the driver state data */ + priv->smstate = SMSTATE_DETACHED; - priv->ep0size = STM32_EP0_MAX_PACKET_SIZE; - priv->devaddr = STM32_DEF_DEVADDR; priv->connected = false; - priv->lowspeed = false; + priv->change = false; /* Put all of the channels back in their initial, allocated state */