TinyUSB MSC Callback Problem With HID Devices

by Alex Johnson 46 views

Introduction

This article discusses a specific issue encountered when using TinyUSB's USB Host capabilities, specifically involving Mass Storage Class (MSC) and Human Interface Device (HID) devices. The problem manifests as the MSC transfer completion callbacks failing to trigger randomly when both MSC and HID devices are connected simultaneously. This leads to application hangs, preventing the normal completion of file operations. We'll delve into the specifics of the issue, the conditions under which it occurs, and how to potentially reproduce it. Understanding this problem is crucial for developers working with embedded systems that require both MSC and HID functionalities via TinyUSB.

The Core Problem: MSC Callbacks and HID Interference

The central issue revolves around the MSC host callbacks not being triggered when an HID device is also present. This is not a problem caused by the HID device actively sending data; the mere presence of a connected HID device seems to be enough to disrupt the MSC transfer completion process. This behavior has been observed on Windows 11 using the esp-idf v5.5.0 and the esp32p4 board. The provided debug logs offer valuable insights into the situation. The issue is likely within the msch_xfer_cb. The symptom is often a halt during file operations through the FatFS layer, where the application waits indefinitely for a callback signal (semaphore). This results in the application essentially freezing.

Technical Details and Code Snippets

The user provided specific code snippets and debug information to highlight the problem. Let's break down the critical parts:

msc_complete_cb Callback Function

The code defines a msc_complete_cb callback function. This function is supposed to handle the completion of MSC transfers. It checks the status of the transfer and, if successful, signals a semaphore (msc_completion_sem). If the transfer fails, it logs an error and also gives the semaphore. This semaphore is then used to synchronize with the MSC transfers.

bool msc_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const *cb_data)
{
  if (cb_data->csw != NULL)
  {
    if (cb_data->csw->status == 0)
    {
      if (xSemaphoreGive(msc_completion_sem) != pdTRUE)
      {
        ESP_LOGE("MSC", "Failed to give msc_completion_sem");
      }
    }
    else
    {
      ESP_LOGE("MSC", "Command failed with status: %d", cb_data->csw->status);
      xSemaphoreGive(msc_completion_sem); 
    }
  }
  return true;
}

Disk Read and Write Functions

The usb_disk_read and usb_disk_write functions utilize tuh_msc_read10 and tuh_msc_write10, respectively, to initiate MSC transfers. They then use the msc_complete_cb as the callback function and wait for the semaphore to be given, indicating the completion of the transfer. This is a common pattern for handling asynchronous USB operations.

static DRESULT usb_disk_read(BYTE pdrv, BYTE *buff, DWORD sector, UINT count)
{
  uint8_t dev_addr = pdrv;
  if (!tuh_msc_read10(dev_addr, 0, buff, sector, (uint16_t)count, msc_complete_cb, 0))
  {
    ESP_LOGE("USB_DISK", "Failed to start read operation");
    return RES_ERROR;
  }
  xSemaphoreTake(msc_completion_sem, portMAX_DELAY);
  return RES_OK;
}

static DRESULT usb_disk_write(BYTE pdrv, const BYTE *buff, DWORD sector, UINT count)
{
  uint8_t dev_addr = pdrv;
  if (!tuh_msc_write10(dev_addr, 0, buff, sector, (uint16_t)count, msc_complete_cb, 0))
  {
    ESP_LOGE("USB_DISK", "Failed to start write operation");
    return RES_ERROR;
  }
  xSemaphoreTake(msc_completion_sem, portMAX_DELAY);
  return RES_OK;
}

Debug Logs

The debug logs reveal a successful MSC transfer until the point where the HID device is introduced. The logs show multiple 'OK' messages, indicating successful data transfers. The issue arises when the msc_complete_cb is no longer triggered, and the program gets blocked. The user noted the logs are great. The log indicates that the callback is no longer being called. The logs provide a clear timeline of events and the point where the system becomes unresponsive. They confirm that the issue is not related to the semaphore itself but, rather, the failure of the tuh_msc_write10 callback to be triggered in the first place.

Reproduction and Mitigation

The issue can be replicated using an USB Hub, and it's essential to understand that the presence of the HID device alone is enough to cause the issue. No active HID events are necessary to trigger the problem. This is a crucial detail for anyone encountering similar problems. Unfortunately, the provided information does not include a direct workaround or a definitive solution. However, understanding the core issue is the first step toward finding a resolution. One potential area of investigation could be within the TinyUSB stack. You should check the interaction between the MSC and HID drivers when they are both present and when USB traffic is being handled. This could involve examining the timing of USB transactions, the prioritization of different endpoints, and any potential conflicts in resource allocation.

Potential Root Causes and Further Investigation

Several factors could contribute to this problem. They are as follows:

  • Interrupt Handling Conflicts: The HID and MSC drivers might be contending for the same interrupt resources, leading to missed or delayed callbacks.
  • Resource Contention: There might be a conflict in accessing shared resources like USB buffers or control pipes. The presence of the HID device might be interfering with the MSC's ability to access these resources.
  • Driver Interactions: Bugs within the TinyUSB driver implementation could be at fault. There may be a subtle interaction or a race condition between the MSC and HID drivers that causes the MSC callbacks to be missed under specific conditions. Further investigation into the TinyUSB source code might be necessary to pinpoint the exact location of the issue.
  • USB Hub Issues: Although not explicitly stated, USB hubs are known to sometimes introduce compatibility problems or timing issues. The USB hub could be contributing to the problem by introducing unexpected delays or interfering with USB traffic management.

Recommendations and Conclusion

Developers facing this problem should carefully analyze the interaction between the MSC and HID drivers in the TinyUSB stack. Investigating interrupt handling, resource allocation, and potential race conditions is recommended. Examining the debug logs and experimenting with different USB configurations (e.g., trying a different USB hub or connecting the devices directly to the host) could also help to pinpoint the root cause. This issue underscores the importance of thorough testing with various device combinations when developing embedded systems that use USB. A deeper understanding of USB communication protocols and TinyUSB's internal workings will be critical to overcoming this challenge.

In conclusion, the problem of the missing MSC host callbacks when an HID device is present in TinyUSB requires further investigation to identify the root cause and implement an effective solution. This article provides a comprehensive overview of the issue, the associated code snippets, and potential areas for investigation. More information, such as the exact cause and the solution, will need to be obtained to address this issue.

For more information on TinyUSB and USB host implementation, you can check the TinyUSB documentation.