Skip to content
Snippets Groups Projects
stm32_usbdev.c 104 KiB
Newer Older
patacongo's avatar
patacongo committed
      if (epno >= STM32_NENDPOINTS)
        {
          usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPNO), (uint16)epno);
          return NULL;
        }

      /* Convert the logical address to a physical OUT endpoint address and
       * remove all of the candidate endpoints from the bitset except for the
       * the IN/OUT pair for this logical address.
       */

      epset = STM32_ENDP_BIT(epno);
    }

  /* Check if the selected endpoint number is available */

patacongo's avatar
patacongo committed
  privep = stm32_epreserve(priv, epset);
patacongo's avatar
patacongo committed
  if (!privep)
    {
      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPRESERVE), (uint16)epset);
      goto errout;
    }
  epno = USB_EPNO(privep->ep.eplog);

  /* Allocate a PMA buffer for this endpoint */

patacongo's avatar
patacongo committed
#warning "REVISIT: Should configure BULK EPs using double buffer feature"
patacongo's avatar
patacongo committed
  bufno = stm32_epallocpma(priv);
patacongo's avatar
patacongo committed
  if (bufno < 0)
    {
      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPBUFFER), 0);
      goto errout_with_ep;
    }
  privep->bufno = (ubyte)bufno;
  return &privep->ep;

errout_with_ep:
patacongo's avatar
patacongo committed
  stm32_epunreserve(priv, privep);
patacongo's avatar
patacongo committed
errout:
  return NULL;
}

/****************************************************************************
 * Name: stm32_freeep
 ****************************************************************************/

static void stm32_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep)
{
  struct stm32_usbdev_s *priv;
  struct stm32_ep_s *privep;

#ifdef CONFIG_DEBUG
  if (!dev || !ep)
    {
      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
patacongo's avatar
patacongo committed
    }
#endif
  priv   = (struct stm32_usbdev_s *)dev;
  privep = (struct stm32_ep_s *)ep;
  usbtrace(TRACE_DEVFREEEP, (uint16)USB_EPNO(ep->eplog));

  if (priv && privep)
    {
      /* Free the PMA buffer assigned to this endpoint */

patacongo's avatar
patacongo committed
      stm32_epfreepma(priv, privep);
patacongo's avatar
patacongo committed

      /* Mark the endpoint as available */

patacongo's avatar
patacongo committed
      stm32_epunreserve(priv, privep);
patacongo's avatar
patacongo committed
    }
}

/****************************************************************************
 * Name: stm32_getframe
 ****************************************************************************/

static int stm32_getframe(struct usbdev_s *dev)
{
  uint16 fnr;

#ifdef CONFIG_DEBUG
  if (!dev)
    {
      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
      return -EINVAL;
    }
#endif

  /* Return the last frame number detected by the hardware */

  fnr = stm32_getreg(STM32_USB_FNR);
  usbtrace(TRACE_DEVGETFRAME, fnr);
  return (fnr & USB_FNR_FN_MASK);
}

/****************************************************************************
 * Name: stm32_wakeup
 ****************************************************************************/

static int stm32_wakeup(struct usbdev_s *dev)
{
  struct stm32_usbdev_s *priv = (struct stm32_usbdev_s *)dev;
  irqstate_t flags;

  usbtrace(TRACE_DEVWAKEUP, 0);
#ifdef CONFIG_DEBUG
  if (!dev)
    {
      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
      return -EINVAL;
    }
#endif

  /* Start the resume sequence.  The actual resume steps will be driven
   * by the ESOF interrupt.
   */

  flags = irqsave();
  stm32_initresume(priv);
  priv->rsmstate = RSMSTATE_STARTED;

  /* Disable the SUSP interrupt (until we are fully resumed), disable
   * the WKUP interrupt (we are already waking up), and enable the
patacongo's avatar
patacongo committed
   * ESOF interrupt that will drive the resume operations.  Clear any
   * pending ESOF interrupt.
patacongo's avatar
patacongo committed
  stm32_setimask(priv, USB_CNTR_ESOFM, USB_CNTR_WKUPM|USB_CNTR_SUSPM);
  stm32_putreg(~USB_ISTR_ESOF, STM32_USB_ISTR);
patacongo's avatar
patacongo committed
  irqrestore(flags);
  return OK;
}

/****************************************************************************
 * Name: stm32_selfpowered
 ****************************************************************************/

static int stm32_selfpowered(struct usbdev_s *dev, boolean selfpowered)
{
  struct stm32_usbdev_s *priv = (struct stm32_usbdev_s *)dev;

  usbtrace(TRACE_DEVSELFPOWERED, (uint16)selfpowered);

#ifdef CONFIG_DEBUG
  if (!dev)
    {
      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
      return -ENODEV;
    }
#endif

  priv->selfpowered = selfpowered;
  return OK;
}

/****************************************************************************
patacongo's avatar
patacongo committed
 * Initialization/Reset
patacongo's avatar
patacongo committed
 ****************************************************************************/
patacongo's avatar
patacongo committed
/****************************************************************************
 * Name: stm32_reset
 ****************************************************************************/

static void stm32_reset(struct stm32_usbdev_s *priv)
{
  int epno;

  /* Put the USB controller in reset, disable all interrupts */
  stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR);
patacongo's avatar
patacongo committed

  /* Reset the device state structure */

  priv->devstate  = DEVSTATE_INIT;
  priv->rsmstate  = RSMSTATE_IDLE;
  priv->rxpending = FALSE;

  /* Reset endpoints */

  for (epno = 0; epno < STM32_NENDPOINTS; epno++)
    {
      struct stm32_ep_s *privep = &priv->eplist[epno];

      /* Cancel any queue requests */

      stm32_cancelrequests(privep);

      /* Reset endpoint status */

      privep->stalled   = FALSE;
      privep->halted    = FALSE;
      privep->txbusy    = FALSE;
      privep->txnullpkt = FALSE;
    }

  /* Re-configure the USB controller in its initial, unconnected state */

  stm32_hwreset(priv);
} 

/****************************************************************************
 * Name: stm32_hwreset
 ****************************************************************************/

static void stm32_hwreset(struct stm32_usbdev_s *priv)
{
  /* Put the USB controller into reset, clear all interrupt enables */
patacongo's avatar
patacongo committed

  stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR);
patacongo's avatar
patacongo committed

  /* Disable interrupts (and perhaps take the USB controller out of reset) */

patacongo's avatar
patacongo committed
  priv->imask = 0;
  stm32_putreg(priv->imask, STM32_USB_CNTR);

  /* Set the STM32 BTABLE address */

  stm32_putreg(STM32_BTABLE_ADDRESS & 0xfff8, STM32_USB_BTABLE);

  /* Initialize EP0 */

  stm32_seteptype(EP0, USB_EPR_EPTYPE_CONTROL);
  stm32_seteptxstatus(EP0, USB_EPR_STATTX_NAK);
  stm32_seteprxaddr(EP0, STM32_EP0_RXADDR);
patacongo's avatar
patacongo committed
  stm32_seteprxcount(EP0, STM32_EP0MAXPACKET);
patacongo's avatar
patacongo committed
  stm32_seteptxaddr(EP0, STM32_EP0_TXADDR);
  stm32_clrstatusout(EP0);
  stm32_seteprxstatus(EP0, USB_EPR_STATRX_VALID);

  /* Set the device to respond on default address */

  stm32_setdevaddr(priv, 0);

patacongo's avatar
patacongo committed
  /* Clear any pending interrupts */

  stm32_putreg(0, STM32_USB_ISTR);

  /* Enable interrupts at the USB controller */
patacongo's avatar
patacongo committed
  stm32_setimask(priv, STM32_CNTR_SETUP, (USB_CNTR_ALLINTS & ~STM32_CNTR_SETUP));
  stm32_dumpep(EP0);
patacongo's avatar
patacongo committed
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/
/****************************************************************************
 * Name: up_usbinitialize
 * Description:
 *   Initialize the USB driver
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

void up_usbinitialize(void) 
{
  /* For now there is only one USB controller, but we will always refer to
   * it using a pointer to make any future ports to multiple USB controllers
   * easier.
   */

patacongo's avatar
patacongo committed
  struct stm32_usbdev_s *priv = &g_usbdev;
  int epno;

  usbtrace(TRACE_DEVINIT, 0);
patacongo's avatar
patacongo committed
  stm32_checksetup();
  /* Power the USB controller, put the USB controller into reset, disable
   * all USB interrupts
   */
patacongo's avatar
patacongo committed

  stm32_putreg(USB_CNTR_FRES|USB_CNTR_PDWN, STM32_USB_CNTR);

patacongo's avatar
patacongo committed
  /* Disconnect the device / disable the pull-up.  We don't want the
   * host to enumerate us until the class driver is registered.
   */ 
patacongo's avatar
patacongo committed

  stm32_usbpullup(&priv->usbdev, FALSE);
  
patacongo's avatar
patacongo committed
  /* Initialize the device state structure.  NOTE: many fields
   * have the initial value of zero and, hence, are not explicitly
   * initialized here.
   */

  memset(priv, 0, sizeof(struct stm32_usbdev_s));
  priv->usbdev.ops   = &g_devops;
  priv->usbdev.ep0   = &priv->eplist[EP0].ep;
  priv->epavail      = STM32_ENDP_ALLSET & ~STM32_ENDP_BIT(EP0);
  priv->bufavail     = STM32_BUFFER_ALLSET & ~STM32_BUFFER_EP0;

  /* Initialize the endpoint list */

  for (epno = 0; epno < STM32_NENDPOINTS; epno++)
    {
      /* Set endpoint operations, reference to driver structure (not
       * really necessary because there is only one controller), and
       * the (physical) endpoint number which is just the index to the
       * endpoint.
       */

      priv->eplist[epno].ep.ops    = &g_epops;
      priv->eplist[epno].dev       = priv;
      priv->eplist[epno].ep.eplog  = epno;

      /* We will use a fixed maxpacket size for all endpoints (perhaps
       * ISOC endpoints could have larger maxpacket???).  A smaller
       * packet size can be selected when the endpoint is configured.
       */

      priv->eplist[epno].ep.maxpacket = STM32_MAXPACKET_SIZE;
    }

  /* Select a smaller endpoint size for EP0 */
patacongo's avatar
patacongo committed

#if STM32_EP0MAXPACKET < STM32_MAXPACKET_SIZE
  priv->eplist[EP0].ep.maxpacket = STM32_EP0MAXPACKET;
#endif

  /* Power up the USB controller, holding it in reset.  There is a delay of
   * about 1uS after applying power before the USB will behave predictably.
patacongo's avatar
patacongo committed
   * A 5MS delay is more than enough.  NOTE that we leave the USB controller
   * in the reset state; the hardware will not be initialized until the
   * class driver has been bound.
   */

  stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR);
patacongo's avatar
patacongo committed
  up_mdelay(5);
patacongo's avatar
patacongo committed
  /* Attach USB controller interrupt handlers.  The hardware will not be
   * initialized and interrupts will not be enabled until the class device
   * driver is bound.  Getting the IRQs here only makes sure that we have
   * them when we need them later.
patacongo's avatar
patacongo committed

  if (irq_attach(STM32_IRQ_USBHPCANTX, stm32_hpinterrupt) != 0)
    {
      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_IRQREGISTRATION),
               (uint16)STM32_IRQ_USBHPCANTX);
      goto errout;
    }

  if (irq_attach(STM32_IRQ_USBLPCANRX0, stm32_lpinterrupt) != 0)
patacongo's avatar
patacongo committed
    {
      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_IRQREGISTRATION),
patacongo's avatar
patacongo committed
               (uint16)STM32_IRQ_USBLPCANRX0);
patacongo's avatar
patacongo committed
      goto errout;
    }
  return;

errout:
  up_usbuninitialize();
} 

/****************************************************************************
 * Name: up_usbuninitialize
 * Description:
 *   Initialize the USB driver
 * Input Parameters:
 *   None
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

void up_usbuninitialize(void) 
{
  /* For now there is only one USB controller, but we will always refer to
   * it using a pointer to make any future ports to multiple USB controllers
   * easier.
   */

patacongo's avatar
patacongo committed
  struct stm32_usbdev_s *priv = &g_usbdev;
  uint16 regval;
  irqstate_t flags;

  usbtrace(TRACE_DEVUNINIT, 0);

  if (priv->driver)
    {
      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DRIVERREGISTERED), 0);
      usbdev_unregister(priv->driver);
    }

  flags = irqsave();
  priv->usbdev.speed = USB_SPEED_UNKNOWN;

  /* Disable and detach IRQs */

  up_disable_irq(STM32_IRQ_USBHPCANTX);
  up_disable_irq(STM32_IRQ_USBLPCANRX0);
  irq_detach(STM32_IRQ_USBHPCANTX);
patacongo's avatar
patacongo committed
  irq_detach(STM32_IRQ_USBLPCANRX0);
  /* Disable all interrupts and force the USB controller into reset */ 
patacongo's avatar
patacongo committed

  stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR);
  
  /* Clear any pending interrupts */ 
patacongo's avatar
patacongo committed
  stm32_putreg(0, STM32_USB_ISTR);
patacongo's avatar
patacongo committed
  
  /* Disconnect the device / disable the pull-up */ 

  stm32_usbpullup(&priv->usbdev, FALSE);
  
  /* Power down the USB controller */
patacongo's avatar
patacongo committed

  stm32_putreg(USB_CNTR_FRES|USB_CNTR_PDWN, STM32_USB_CNTR);
  irqrestore(flags);
}

/****************************************************************************
 * Name: usbdev_register
 *
 * Description:
 *   Register a USB device class driver. The class driver's bind() method will be
 *   called to bind it to a USB device driver.
 *
 ****************************************************************************/

int usbdev_register(struct usbdevclass_driver_s *driver)
{
  /* For now there is only one USB controller, but we will always refer to
   * it using a pointer to make any future ports to multiple USB controllers
   * easier.
   */

  struct stm32_usbdev_s *priv = &g_usbdev;
patacongo's avatar
patacongo committed
  int ret;

  usbtrace(TRACE_DEVREGISTER, 0);

#ifdef CONFIG_DEBUG
  if (!driver || !driver->ops->bind || !driver->ops->unbind ||
      !driver->ops->disconnect || !driver->ops->setup)
    {
      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
      return -EINVAL;
    }

  if (priv->driver)
    {
      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DRIVER), 0);
      return -EBUSY;
    }
#endif

  /* First hook up the driver */

   priv->driver = driver;
patacongo's avatar
patacongo committed

  /* Then bind the class driver */

  ret = CLASS_BIND(driver, &priv->usbdev);
patacongo's avatar
patacongo committed
  if (ret)
    {
      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BINDFAILED), (uint16)-ret);
patacongo's avatar
patacongo committed
      priv->driver = NULL;
patacongo's avatar
patacongo committed
      /* Setup the USB controller -- enabling interrupts at the USB controller */

      stm32_hwreset(priv);

      /* Enable USB controller interrupts at the NVIC */
patacongo's avatar
patacongo committed

      up_enable_irq(STM32_IRQ_USBHPCANTX);
patacongo's avatar
patacongo committed
      up_enable_irq(STM32_IRQ_USBLPCANRX0);
patacongo's avatar
patacongo committed

      /* Set the interrrupt priority */

      up_prioritize_irq(STM32_IRQ_USBHPCANTX, CONFIG_USB_PRI);
patacongo's avatar
patacongo committed
      up_prioritize_irq(STM32_IRQ_USBLPCANRX0, CONFIG_USB_PRI);
patacongo's avatar
patacongo committed
      /* Enable pull-up to connect the device.  The host should enumerate us
       * some time after this
       */
patacongo's avatar
patacongo committed

      stm32_usbpullup(&priv->usbdev, TRUE);
patacongo's avatar
patacongo committed
   }
  return ret;
}

/****************************************************************************
 * Name: usbdev_unregister
 *
 * Description:
 *   Un-register usbdev class driver.If the USB device is connected to a USB host,
 *   it will first disconnect().  The driver is also requested to unbind() and clean
 *   up any device state, before this procedure finally returns.
 *
 ****************************************************************************/

int usbdev_unregister(struct usbdevclass_driver_s *driver)
{
  /* For now there is only one USB controller, but we will always refer to
   * it using a pointer to make any future ports to multiple USB controllers
   * easier.
   */

  struct stm32_usbdev_s *priv = &g_usbdev;
patacongo's avatar
patacongo committed
  usbtrace(TRACE_DEVUNREGISTER, 0);

#ifdef CONFIG_DEBUG
  if (driver != priv->driver)
    {
      usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0);
      return -EINVAL;
    }
#endif

  /* Unbind the class driver */

  CLASS_UNBIND(driver, &priv->usbdev);
patacongo's avatar
patacongo committed

  /* Disable USB controller interrupts */

  up_disable_irq(STM32_IRQ_USBHPCANTX);
  up_disable_irq(STM32_IRQ_USBLPCANRX0);

  /* Unhook the driver */

  priv->driver = NULL;
patacongo's avatar
patacongo committed
  return OK;
}

#endif /* CONFIG_USBDEV && CONFIG_STM32_USB */