Author Topic: Defective USB Host hanging up your target  (Read 4466 times)

0 Members and 1 Guest are viewing this topic.

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 4147
  • Country: gb
  • Doing electronics since the 1960s...
Defective USB Host hanging up your target
« on: February 02, 2023, 11:08:48 pm »
I think this happens via an interrupt flood.

In the other direction, I've often seen PCs getting hanged up via some defective USB device being plugged in.

Is there any way to guard against this?

In my project the USB (CDC & MSC) is implemented entirely under interrupt, so is vulnerable to this kind of thing. One could perhaps detect an excessive IRQ frequency but what then? Block the interrupt for ever (until a power cycle)?
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 3932
  • Country: us
Re: Defective USB Host hanging up your target
« Reply #1 on: February 02, 2023, 11:19:20 pm »
USB never generates interrupts.  "interrupts transfers" are a type of USB communication, but not related to host interrupts.
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 4147
  • Country: gb
  • Doing electronics since the 1960s...
Re: Defective USB Host hanging up your target
« Reply #2 on: February 03, 2023, 03:17:21 am »
The USB implementation in the 32F4 is wholly interrupt-driven.

A CDC ISR transfers 64 byte packets. A MSC ISR transfers 512 byte packets.

It perhaps could be run using polling, from an RTOS task, but that would be inefficient.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline bugnate

  • Regular Contributor
  • *
  • Posts: 58
  • Country: us
Re: Defective USB Host hanging up your target
« Reply #3 on: February 03, 2023, 03:54:19 am »
You are asking what can be done on the device side in the case of a defective host? IMO the clean thing would be to initiate a soft disconnect on your end (see SDIS bit). On the host side this appears as an unplug. The USB +5v will stay active but there might be implications for power otherwise (but probably not). From there I guess you could either try to re-connect or give up on the host.
 
The following users thanked this post: peter-h

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3251
  • Country: ca
Re: Defective USB Host hanging up your target
« Reply #4 on: February 03, 2023, 05:00:10 am »
I think this happens via an interrupt flood.

I don't think there's a mechanism which would let a host flood your MCU with interrupts.
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 4147
  • Country: gb
  • Doing electronics since the 1960s...
Re: Defective USB Host hanging up your target
« Reply #5 on: February 03, 2023, 01:50:10 pm »
The Host can poll it very fast.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3251
  • Country: ca
Re: Defective USB Host hanging up your target
« Reply #6 on: February 03, 2023, 02:33:40 pm »
The Host can poll it very fast.

Poll how? What tokens does it send to you?
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 4147
  • Country: gb
  • Doing electronics since the 1960s...
Re: Defective USB Host hanging up your target
« Reply #7 on: February 03, 2023, 04:06:05 pm »
I don't have a USB analyser so I can't tell you. But I recently saw a case where a PC (win7-64) has a VMWARE VM (winXP) running, and the computer "could not decide" whether the USB device should connect to the base OS or to the VM. The result was something weird, and it hung up the target.

However, plenty of cases over the years where e.g. a duff flash stick would make a PC run very slowly.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3251
  • Country: ca
Re: Defective USB Host hanging up your target
« Reply #8 on: February 03, 2023, 05:19:37 pm »
I don't have a USB analyser so I can't tell you. But I recently saw a case where a PC (win7-64) has a VMWARE VM (winXP) running, and the computer "could not decide" whether the USB device should connect to the base OS or to the VM. The result was something weird, and it hung up the target.

I guess if it starts enumerating the device over and over again and your enumeration code is slow but high priority, it could produce a lot of load on the CPU. More likely your code is not robust enough to handle broken enumerations.

It's impossible to tell without investigating, and without knowing you cannot really fix it.
 

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 3932
  • Country: us
Re: Defective USB Host hanging up your target
« Reply #9 on: February 03, 2023, 06:10:02 pm »
The USB implementation in the 32F4 is wholly interrupt-driven.

A CDC ISR transfers 64 byte packets. A MSC ISR transfers 512 byte packets.

It perhaps could be run using polling, from an RTOS task, but that would be inefficient.

Sorry, I was responding to the part about "In the other direction, I've often seen PCs getting hanged up via some defective USB device being plugged in" -- this mostly happens due to a buggy driver.  A USB device cannot flood the host with interrupts because it can't even create host interrupts.  This is the case at least for USB 2.0 and below.  I don't know how USB >= 3.0 works for superspeed devices.

Anyway, I doubt that a host is sending tokens dramatically faster than the standard says, but it is certainly possible.  But without some sort of analysis tool to actually see what is happening everything is just speculation.    I will say, the host may be doing something unusual such as rapidly enabling and disabling the device, but the chances that the bug is with your implementation is something like 99%.  The USB stacks in mainstream operating systems have been extremely well vetted and tested on literally billions and billions of devices.  That doesn't mean there aren't bugs in them -- especially ones that show up in unusual circumstances. But 99% of the time the problem is going to be with your under development device, not the host.
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 4147
  • Country: gb
  • Doing electronics since the 1960s...
Re: Defective USB Host hanging up your target
« Reply #10 on: February 03, 2023, 09:09:10 pm »
Can anyone suggest a way of bypassing this USB OUT function ("OUT" means to Host i.e. the opposite of the correct USB terminology) if no USB cable is plugged in?

This is a fairly common bit of code from the ST Cube USB CDC (VCP) library, with additions from me to make it thread-safe, and a timeout in case a USB Host (with a USB VCP application running) is not connected.

Code: [Select]

/**
  * @brief  CDC_Transmit_FS
  *         Data to send over USB IN endpoint are sent over CDC interface
  *         through this function. Note that "IN" in USB terminology means
  *         box -> PC.
   *
  * @param  Buf: Buffer of data to be sent
  * @param  Len: Number of data to be sent (in bytes)
  *  *
  * This function had a call rate limit of about 10kHz, and had a packet length limit
  * of about 800 bytes - until the flow control mod below. These limits still apply if
  * flow_control=false.
  *
  * This function is BLOCKING if flow_control=true. That means that if there is no Host
  * application receiving the data (e.g. Teraterm) it will block output.
  * The problem is that if no USB device is connected from startup, the while() loop
  * would block for ever, hence the timeout.
  *
  * Flow control is normally disabled for the debugging output functions.
  *
  * If called before USB thread starts, all output is dumped - for obvious reasons!
  *
  * Mutexed to enable multiple RTOS tasks to output to port 0.
  *
  */

#define USB_TX_TIMEOUT 100 // 100ms

uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len, bool flow_control)
{

if ( g_USB_started && (Len>0) )
{

uint32_t counter = 0;
bool timeout = false;

osMutexAcquire(g_CDC_transmit_mutex, osWaitForever);

USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassDataCDC;

// Wait on USB Host accepting the data.
if ( flow_control )
{
// Loop around until USB Host has picked up the last data
// This rarely holds things up - 20us typ. delay
while (hcdc->TxState != 0) // This gets changed by USB thread, hence the g_USB_started test
{
osDelay(1);
counter++;
if (counter>USB_TX_TIMEOUT)
{
timeout=true;
break;
}
}
}

if (!timeout)
{
// Output the data to USB
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len);
USBD_CDC_TransmitPacket(&hUsbDeviceFS);
}

osMutexRelease(g_CDC_transmit_mutex);

g_comms_act[0]=LED_COM_TC; // indicate data flow on LED 0

// If flow control is selected OFF, we do a crude wait to make CDC output work
// even with a slow Host. This actually needs to be only a ~100-200us wait, but
// is host dependent.
if ( !flow_control )
{
osDelay(2);
}

}

return USBD_OK;
}
« Last Edit: February 04, 2023, 10:40:44 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 4147
  • Country: gb
  • Doing electronics since the 1960s...
Re: Defective USB Host hanging up your target
« Reply #11 on: February 05, 2023, 06:17:01 pm »
Update, in case someone finds this one day, is here
https://community.st.com/s/question/0D53W0000272BM1SAM/32f417-can-one-detect-when-a-usb-host-is-connected?t=1675610861832

No good solution really. Sensing VBUS is the easy low hanging fruit, detecting if a USB cable is even connected.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3251
  • Country: ca
Re: Defective USB Host hanging up your target
« Reply #12 on: February 05, 2023, 06:39:20 pm »
Update, in case someone finds this one day, is here
https://community.st.com/s/question/0D53W0000272BM1SAM/32f417-can-one-detect-when-a-usb-host-is-connected?t=1675610861832

No good solution really. Sensing VBUS is the easy low hanging fruit, detecting if a USB cable is even connected.

You're not really interested in whether the cable is connected, but rather whether it is successfully enumerated by the OS. The best way would be to know if any files are open for your device, but without a custom driver this is not really possible.

In the USB library, or whatever you use, there must be a variable which indicates the USB state. Otherwise it wouldn't be able to enumerate. At the very least there must a variable which holds the device address given to your device by the host. You can try to use it.
 
The following users thanked this post: SiliconWizard

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 4147
  • Country: gb
  • Doing electronics since the 1960s...
Re: Defective USB Host hanging up your target
« Reply #13 on: February 05, 2023, 07:56:32 pm »
I agree, but it is not trivial.

Open files are undetectable on a FAT volume. Windoze sees a removable drive as a load of 512 byte blocks and it totally owns them, and uses them as it wishes. I have FatFS providing file-level visibility in this volume to internal applications, but it can't even tell me when windoze has finished writing a file! (There are hacks for this, involving snooping inside USB MSC packets).

But it is a fair point in that windoze will see both CDC and MSC profiles in this case and a connection to MSC may be more detectable than a connection to CDC (VCP). In particular I believe windoze does a 1 block read of a removable drive at 1Hz and that would be trivial to detect. But, MacOS or Unix??

Quote
In the USB library, or whatever you use, there must be a variable which indicates the USB state. Otherwise it wouldn't be able to enumerate. At the very least there must a variable which holds the device address given to your device by the host. You can try to use it.

I will use it if you can tell me where it is :) I am using the ST Cube MX library, from ~ 3 years ago. I just don't have the understanding.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3251
  • Country: ca
Re: Defective USB Host hanging up your target
« Reply #14 on: February 05, 2023, 09:08:52 pm »
I will use it if you can tell me where it is :) I am using the ST Cube MX library, from ~ 3 years ago. I just don't have the understanding.

Reminds me of "knowing where to tap" story:

http://jeffacubed.com/the-boilermaker-story-or-knowing-where-to-tap/

If you look at the source code, it must be easy to spot. Most likely they named the state variable using something with "state" in it.
 

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 3932
  • Country: us
Re: Defective USB Host hanging up your target
« Reply #15 on: February 05, 2023, 10:24:49 pm »
I agree, but it is not trivial.

Open files are undetectable on a FAT volume. Windoze sees a removable drive as a load of 512 byte blocks and it totally owns them, and uses them as it wishes. I have FatFS providing file-level visibility in this volume to internal applications, but it can't even tell me when windoze has finished writing a file!

Wait, what does the filesystem driver have to do with a CDC device?
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 4147
  • Country: gb
  • Doing electronics since the 1960s...
Re: Defective USB Host hanging up your target
« Reply #16 on: February 05, 2023, 10:46:46 pm »
One could determine whether a "connection to some kind of USB Host" exists, from the 1Hz removable storage device poll.

But yes it won't tell you whether anybody is running a VCP application on the relevant PC COM port.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline abyrvalg

  • Frequent Contributor
  • **
  • Posts: 837
  • Country: es
Re: Defective USB Host hanging up your target
« Reply #17 on: February 06, 2023, 10:35:43 am »
There are tons of discussions on the subject just a single Google request away. This one even mentions some interrupt-related bug in older ST code (among with solutions how to detect the CONFIGURED state and VCP open/close).
 

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 4147
  • Country: gb
  • Doing electronics since the 1960s...
Re: Defective USB Host hanging up your target
« Reply #18 on: February 06, 2023, 11:21:26 am »
Thanks.

There is some weird code there which might be working by luck, like this one which is not checking TxState (can work by luck because I found experimentally that USB can accept data at a very high speed, like 10k packets per second, and the TxState flag goes busy for only 20us at a time)

Code: [Select]
        USBD_CDC_SetTxBuffer(hUsbDevice_0, Buf, Len);

        // Busy wait if USB is busy or exit on success or disconnection happens
        while(1)
        {

            //Check if USB went offline while retrying
            if ((hUsbDevice_0->dev_state != USBD_STATE_CONFIGURED)
                        || (hUsbDevice_0->ep0_state == USBD_EP0_STATUS_IN))
            {
                result = USBD_FAIL;
                break;
            }

            // Try send
            result = USBD_CDC_TransmitPacket(hUsbDevice_0);

I also wonder why he does a set_buffer, then other checks, and then conditionally does a transmit. Surely those two lines should always go together, and only once you are actually outputting data.

The other thing is that they appear to be transmitting from a "foreground code" context, while the USB code all runs under interrupts, so interrupts need to be disabled around the buffer set and data output - like I do below

Code: [Select]
if ( g_USB_started && (Len>0) && (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9)==1) )
{

osMutexAcquire(g_CDC_transmit_mutex, osWaitForever);

USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassDataCDC;
uint32_t timeout=0;
bool error=false;

// Wait on USB Host accepting the data.
// A simple 1 sec timeout prevents a hang.
if ( flow_control )
{
// Loop around until USB Host has picked up the previous data
while (hcdc->TxState != 0) // This gets changed by USB interrupt
{
osDelay(2);
timeout++;
if (timeout>500)
{
hcdc->TxState=0;
error=true;
break;
}
}
}

if (!error)
{
// Output the data to USB.
// USB interrupts must be disabled around this.
__disable_irq();
USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len);
USBD_CDC_TransmitPacket(&hUsbDeviceFS);
__enable_irq();
}

osMutexRelease(g_CDC_transmit_mutex);

g_comms_act[0]=LED_COM_TC; // indicate data flow on LED 0

// If flow control is selected OFF, we do a crude wait to make CDC output work
// even with a slow Host.
// In the usage context (outputting debugs to Teraterm) this actually needs to be only
// a ~100-200us wait, but is host dependent. This gives us a 1-2ms delay between
// *blocks* (typically whole lines).
if ( !flow_control )
{
asm("nop"); // for breakpoints
osDelay(2);
}

}

I will look at the other code and see if I can extract some additional "USB active" qualifiers. However so much of this seems to be Host OS dependent.

I can see stuff like

Code: [Select]
USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef  *pdev)
{
  if(pdev->dev_state == USBD_STATE_CONFIGURED)
  {
    if(pdev->pClass->SOF != NULL)
    {
      pdev->pClass->SOF(pdev);
    }
  }
  return USBD_OK;
}

but can't see how to get to pdev from within CDC_Transmit_FS().

Somebody on the ST forum wrote that __disable_irq(); cannot be used within an RTOS task but I totally don't get why that should be. Obviously it will stop RTOS task switching. If you just want to stop RTOS task switching you use portENTER_CRITICAL().

After more digging I found that
Code: [Select]
/**
  * @brief  Opens an endpoint of the low level driver.
  * @param  pdev: Device handle
  * @param  ep_addr: Endpoint number
  * @param  ep_type: Endpoint type
  * @param  ep_mps: Endpoint max packet size
  * @retval USBD status
  */
USBD_StatusTypeDef USBD_LL_OpenEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t ep_type, uint16_t ep_mps)
{
gets called when a USB connection is made, and

Code: [Select]
/**
  * @brief  Closes an endpoint of the low level driver.
  * @param  pdev: Device handle
  * @param  ep_addr: Endpoint number
  * @retval USBD status
  */
USBD_StatusTypeDef USBD_LL_CloseEP(USBD_HandleTypeDef *pdev, uint8_t ep_addr)
{
TopLED(false);
  HAL_StatusTypeDef hal_status = HAL_OK;
  USBD_StatusTypeDef usb_status = USBD_OK;
 
  hal_status = HAL_PCD_EP_Close(pdev->pData, ep_addr);
 
  usb_status =  USBD_Get_USB_Status(hal_status);   
 
  return usb_status; 
}

gets called when it is broken, so these two functions could be used to toggle a flag.

Another pair of functions which does a similar thing but specifically for CDC is
CDC_Init_FS()
CDC_DeInit_FS()

These, in the original ST code, do a malloc() and free() which nicely crashes your target after a few days as windoze goes to sleep periodically - due to heap fragmentation :) This was replaced by a static buffer, which incidentally makes the 2nd function an empty one.
I am now using these to toggle a flag. No indication however if there is an application on the VCP COM port.
« Last Edit: February 06, 2023, 03:01:20 pm by peter-h »
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3251
  • Country: ca
Re: Defective USB Host hanging up your target
« Reply #19 on: February 06, 2023, 03:05:27 pm »
I will look at the other code and see if I can extract some additional "USB active" qualifiers. However so much of this seems to be Host OS dependent.

None of that is Host OS dependent. You can get an MCU, configure it as a host, connect your device to it, and you will not be able to distinguish this from Windows or Linux. There are some heuristics you can use to differentiate between host OSes. Like Windows will ask for string 0xEE while others won't. But none of this will tell you if there are any apps actually connected to your device.

I can see stuff like

Code: [Select]
USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef  *pdev)
{
  if(pdev->dev_state == USBD_STATE_CONFIGURED)
  {
    if(pdev->pClass->SOF != NULL)
    {
      pdev->pClass->SOF(pdev);
    }
  }
  return USBD_OK;
}

USBD_STATE_CONFIGURED is already a good indicator that your device has been enumerated by the host. Without custom driver on the host PC, you won't get much better than this.

SOF is a token which is sent by the USB host at the beginning of every frame (1 ms).
 
The following users thanked this post: peter-h

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 4147
  • Country: gb
  • Doing electronics since the 1960s...
Re: Defective USB Host hanging up your target
« Reply #20 on: February 06, 2023, 03:17:30 pm »
Quote
Without custom driver on the host PC, you won't get much better than this.

In that case it looks like
CDC_Init_FS()
CDC_DeInit_FS()
are already doing the job for me, if I set a boolean to true in the 1st one and to false in the 2nd one.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline Sauli

  • Contributor
  • Posts: 43
  • Country: fi
Re: Defective USB Host hanging up your target
« Reply #21 on: February 06, 2023, 03:21:54 pm »
If an application on the PC is expecting data from your device, you will get IN tokens to the endpoind you are using to send that data. The Cube USB stack may not directly tell you that info, but it is hidden somewhere in the USB peripheral registers. IN endpoint interrupt register is a likely candidate for that. Either it tells you that the peripheral sent a NAK in response to an IN token or that an IN token was received while the transmit FIFO was empty.

Or you can just have a timeout on transmit. If the transmit times out, nobody is listening.

Generally you have no hope sending anything unless the USB peripheral is in configured state. And by that I mean it is both configured and not suspended.  If it is suspended you could try remote wakeup first. That probably won't help though as the PC should not suspend a port that an application is waiting data from. Windows suspends a port if there is no application connected to that port.
 
The following users thanked this post: peter-h

Offline peter-hTopic starter

  • Super Contributor
  • ***
  • Posts: 4147
  • Country: gb
  • Doing electronics since the 1960s...
Re: Defective USB Host hanging up your target
« Reply #22 on: February 06, 2023, 04:14:39 pm »
I've done a 1 sec timeout on the TxState flag.

Monitoring a GPIO pin with a scope shows this hardly ever moves and if it does, it is for ~20us only.

I don't want to get into the USB registers. That's just too convoluted :)

BTW my box is a client, not a Host (OTG Master or whatever you call it). OTG is in theory supported by the Cube code but was deliberately not implemented because customers have unrealistic expectations regarding what client profiles it will support, and regarding how much power it can provide.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3251
  • Country: ca
Re: Defective USB Host hanging up your target
« Reply #23 on: February 06, 2023, 04:29:37 pm »
If an application on the PC is expecting data from your device, you will get IN tokens to the endpoind you are using to send that data.

If I remember correctly, usbser.sys receives data even if the COM port is not open by any of the apps.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8895
  • Country: fi
Re: Defective USB Host hanging up your target
« Reply #24 on: February 06, 2023, 04:46:23 pm »
Colossally simple, and exactly the same as for any other interrupt source: calculate maximum possible theoretical interrupt rate and do your processing in significantly less time than the interrupt period. If you have some operations that require longer time to finish and are triggered with some sort of command, just ignore the new command while things are unfinished.

Just limit the speed to USB 1.1. If the peripheral can only generate an interrupt for a whole packet, that is going to be a lot of time between interrupts. If you don't make it, improve your code until you make it. If still unable, start ignoring packets. Generic pattern which works:

Code: [Select]

volatile int working_on_it;
isr_peripheral() // high priority
{
    if(working_on_it)
        return; // ignore packet

    working_on_it = 1;
    NVIC->STIR = PROCESSING_IRQn;       
}

isr_processing() // low priority
{
    do_things_with_packet;
    working_on_it = 0;
}
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf