USB CDC stack can hang on Freedom Board

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

USB CDC stack can hang on Freedom Board

1,546 Views
chriswestervelt
Contributor III

I have discovered that in certain situations, a variable that needs to be cleared to prevent an endless loop can be left in limbo.  It is part of this routine CDC1_App_Task and is transactionOngoing.  It gets cleared by CDC1_App_Callback.  If however the connection is broken while a transaction is in progress and the application has called CDC1_App_Task, the app can become hung.

byte CDC1_App_Task(byte *txBuf, size_t txBufSize)

{

  uint8_t i;

  /* device is Kinetis L2K */

  /* call the periodic task function */

  USB_Class_CDC_Periodic_Task();

  /* check whether enumeration is complete or not */

  if ((start_app==TRUE) && (start_transactions==TRUE)) {

    if (Tx1_NofElements()!=0) {

      i = 0;

      while(i<txBufSize && Tx1_Get(&txBuf[i])==ERR_OK) {

        i++;

      }

      transactionOngoing = TRUE;

      if (USB_Class_CDC_Interface_DIC_Send_Data(CONTROLLER_ID, txBuf, i)!=USB_OK) {

        transactionOngoing = FALSE;

        return ERR_FAULT;

      }

      while(transactionOngoing){} /* wait until transaction is finished */

#if 1 /* workaround for problem in USB stack v3.1.1: if last block is 8, 16, 32, 40, 48, ... bytes, it does not get out until the next transfer? */

      if ((i%8)==0) {

        /* workaround: sending a dummy block of zero bytes */

        transactionOngoing = TRUE;

        if (USB_Class_CDC_Interface_DIC_Send_Data(CONTROLLER_ID, txBuf, 0)!=USB_OK) {

          transactionOngoing = FALSE;

          return ERR_FAULT;

        }

        while(transactionOngoing){} /* wait until transaction is finished */

      }

#endif

    } /* if */

    return ERR_OK;

  } else {

    return ERR_BUSOFF; /* USB bus not available yet */

  }

}

Tags (2)
5 Replies

814 Views
BlackNight
NXP Employee
NXP Employee

Hi Chris,

yes, the actual implementation on GitHub (see Processor Expert Component *.PEupd Files on GitHub | MCU on Eclipse) fixes this. It is using a timout component which prevents that the application will hang. The code is as below:

/*

** ===================================================================

**     Method      :  CDC1_SendDataBlock (component FSL_USB_CDC_Device)

**

**     Description :

**         Sends a USB data block

**         This method is internal. It is used by Processor Expert only.

** ===================================================================

*/

byte CDC1_SendDataBlock(byte *data, word dataSize)

{

  TMOUT1_CounterHandle timeout;

  uint8_t res = ERR_OK;

  transactionOngoing = TRUE;

  if (USB_Class_CDC_Interface_DIC_Send_Data(CONTROLLER_ID, data, dataSize)!=USB_OK) {

    transactionOngoing = FALSE;

    return ERR_FAULT;

  }

  /* wait for transaction finish */

  timeout = TMOUT1_GetCounter(100/TMOUT1_TICK_PERIOD_MS); /* set up timeout counter */

  while(transactionOngoing) { /* wait until transaction is finished */

    CDC1_RunUsbEngine();

    if (TMOUT1_CounterExpired(timeout)) {

      res = ERR_FAILED;

      break;

    }

  }

  TMOUT1_LeaveCounter(timeout); /* return timeout counter */

  return res;

}

/*

** ===================================================================

**     Method      :  CDC1_App_Task (component FSL_USB_CDC_Device)

**     Description :

**         Application task to be called periodically from the main

**         task.

**     Parameters  :

**         NAME            - DESCRIPTION

**       * txBuf           - Pointer to temporary buffer used to

**                           transmit data over USB. Should be equal or

**                           greater than the endpoint buffer size. Data

**                           will be sent in an asynchronous way, so

**                           make sure the buffer is *not* on the stack.

**                           This buffer must be available until the

**                           next transmission.

**         txBufSize       - Size of the buffer in bytes

**     Returns     :

**         ---             - Error code, returns ERR_OK if USB

**                           enumeration has been finished, error code

**                           otherwise.

** ===================================================================

*/

byte CDC1_App_Task(byte *txBuf, size_t txBufSize)

{

  uint8_t i, res;

  /* device is Kinetis L2K */

  CDC1_RunUsbEngine();

  /* call the periodic task function */

  USB_Class_CDC_Periodic_Task();

  /* check whether enumeration is complete or not */

  if ((start_app==TRUE) && (start_transactions==TRUE)) {

    if (Tx1_NofElements()!=0) {

      i = 0;

      while(i<txBufSize && Tx1_Get(&txBuf[i])==ERR_OK) {

        i++;

      }

      res = CDC1_SendDataBlock(txBuf, i);

      if (res!=ERR_OK) {

        return res;

      }

#if 1 /* workaround for problem in USB stack v3.1.1: if last block is 8, 16, 32, 40, 48, ... bytes, it does not get out until the next transfer? */

      if ((i%8)==0) {

        /* workaround: sending a dummy block of zero bytes */

        res = CDC1_SendDataBlock(txBuf, 0);

        if (res!=ERR_OK) {

          return res;

        }

      }

#endif

    } /* if */

    return ERR_OK;

  } else {

    return ERR_BUSOFF; /* USB bus not available yet */

  }

}

How the timeout is used: see this project on GitHub:

mcuoneclipse/Examples/FRDM-KL25Z/Freedom_UsbCdc at master · ErichStyger/mcuoneclipse · GitHub

I hope this helps,

Erich

814 Views
chriswestervelt
Contributor III

I will check out upgrading and re-building from the PE components.  It looks like a good solution.  Mine was similar and worked.  This is better.

0 Kudos
Reply

814 Views
BlackNight
NXP Employee
NXP Employee

As a heads-up: you need to call TMOUT1_AddTick() say from a periodic interrupt (or from the FreeRTOS Idle hook if you are using FreeRTOS). This will increment the tick counter of the timeout module so it can properly timeout.

The Timeout module can have multiple available timeout handles, typically one per 'user'. And that way it works reentrant across multiple parallel usage.

0 Kudos
Reply

814 Views
chriswestervelt
Contributor III

Erich, how often or less should you call this and would it be safe to call from a timer tick ISR?

0 Kudos
Reply

814 Views
BlackNight
NXP Employee
NXP Employee

You need to call it at the rate (in milli-seconds) you specify in the Timeout component (default is 10 ms).

And it is safe to all TMOUT1_AddTick() from an interrupt.

0 Kudos
Reply