Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

UDD_Send() (uotghs_device.c) has an unbounded busy-wait on TXINI — permanent MCU hang if host doesn't set it (native USB CDC line-state related)

オープン
#153 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
52/100
issue の種類
バグ
明瞭さ
おおむね明確
活発さ
活発
技術スタック
c, cpp
領域
embedded-iot

調査の方向性

system/libsam/source/uotghs_device.c で UDD_Send、UDD_WaitIN、UDD_WaitOUT、UDD_WaitForINOrOUT を読み始め、続いて USBCore.cpp と CDC.cpp を通じてそれらの呼び出し元をたどります。提供された sketch とホスト設定でネイティブ CDC のハングを再現します。完了条件は、USB flag が欠落していても無限にスピンしなくなり、影響を受ける送信/待機パスが一貫して転送を報告または中止することです。

索引モデルが issue の本文から書いたものです。

説明

Type: Bug USB: CDC serial

Summary

UDD_Send() in system/libsam/source/uotghs_device.c contains an unbounded busy-wait loop with no timeout, waiting for the TXINI (Transmit IN complete) flag on the target endpoint. If the host does not set this flag (e.g. around a USB line-state change such as DTR/RTS toggling on the native CDC port), the MCU hangs permanently in this loop, with interrupts still enabled and no way to recover except a full reset/power cycle.

uint32_t UDD_Send(uint32_t ep, const void* data, uint32_t len)
{
    const uint8_t *ptr_src = data;
    uint8_t *ptr_dest = (uint8_t *) &udd_get_endpoint_fifo_access8(ep);
    uint32_t i;

    while( UOTGHS_DEVEPTISR_TXINI != (UOTGHS->UOTGHS_DEVEPTISR[ep] & UOTGHS_DEVEPTISR_TXINI )) {}
    ...

(https://github.com/arduino/ArduinoCore-sam/blob/master/system/libsam/source/uotghs_device.c)

This is called from USBD_Send() in USBCore.cpp, which is the underlying function used by every SerialUSB.write()/println() call (via Serial_::write in CDC.cpp), as well as HID reports.

Related but similarly unbounded waits exist elsewhere in the same file (UDD_WaitIN, UDD_WaitOUT, UDD_WaitForINOrOUT) — all are plain while(...); with no timeout or bail-out condition.

How I found this

I have an Arduino Due (genuine, Rev3) that reliably hangs during extended native-USB operation on specific Windows 11 hosts (newer Intel xHCI controllers — Tiger Lake/Alder Lake generation). Full investigation history, including USB traffic capture (Wireshark/USBPcap) and elimination of third-party software as a cause, is documented here: https://github.com/arduino/ArduinoCore-sam/issues/71#issuecomment-5382181174

Using an ST-Link V2 + OpenOCD + GDB attached via the DEBUG (SWD) header, I was able to halt the MCU while it was hung (native USB unresponsive, but the rest of loop() — an independent LED blink — still running, i.e. not a full lockup: DHCSR.S_LOCKUP was 0, core was in normal Thread mode with interrupts enabled).

Register/symbol dump at the hang:

pc = 0x00081b06   -> inside UDD_Send+18
lr = 0x00080e99   -> called from USBD_Send() at USBCore.cpp:204
xpsr = 0x01000000 -> Thread mode, not inside an ISR
primask/faultmask = 0 -> interrupts not disabled

info line *0x00080e99 resolved cleanly to USBCore.cpp:204 inside USBD_Send, confirming the call chain Serial_::write → USBD_Send → UDD_Send, stuck spinning on the TXINI wait with no way out.

Reliable(-ish) repro

A minimal sketch (no HID/Joystick needed, plain CDC is enough):

bool state;
uint32_t Tim;
uint32_t TimLog;

void setup() {
  pinMode(13, OUTPUT);
  delay(1000);
  SerialUSB.println(1);
}

void loop() {
  if (millis() - Tim >= 500) {
    Tim = millis();
    digitalWrite(13, state);
    state = !state;
  }
  if (millis() - TimLog >= 5000) {
    TimLog = millis();
    SerialUSB.println(millis());
  }
}
  • Left running for ~15-25 minutes on an affected Windows 11 host: hangs organically.
  • Faster/more reliable trigger: from the host, open the native CDC COM port via .NET System.IO.Ports.SerialPort without setting DtrEnable/RtsEnable to true before .Open() — this toggles the CDC line state in a way that reliably triggers the hang within seconds in my testing.
  • Does not reproduce on Linux or in Windows Safe Mode on the same physical hardware — consistent with the theory that it's a host-timing-dependent race on whether TXINI gets set before the firmware's next attempt to send.

Suggested direction for a fix

At minimum, every unbounded while(...) wait in uotghs_device.c (UDD_Send, UDD_WaitIN, UDD_WaitOUT, UDD_WaitForINOrOUT) should have a bounded timeout (e.g. based on millis() or a cycle counter) and return an error/abort the transfer instead of spinning forever when the expected hardware flag never arrives. I understand this changes the function signatures/call sites (currently void/unconditional uint32_t return used as byte count, not status) — happy to discuss/help test a patch if a maintainer can advise on the preferred approach (e.g. optional timeout parameter, or a global "USB write timeout" behavior similar to how other Serial implementations use setTimeout()).

Environment

  • Board: Arduino Due (genuine, Rev3, native USB port)
  • Core: Arduino SAM Boards (32-bit ARM Cortex-M3), package version installed via Boards Manager — 1.6.12
  • Debug setup: ST-Link V2 (clone) via the onboard 4-pin SWD DEBUG header, OpenOCD 0.12.0, arm-none-eabi-gdb 4.8.3 (bundled with Arduino IDE's SAM toolchain)
  • Host: Windows 11 (build 26100), Intel USB 3.10/3.20 xHCI controllers (PCI\VEN_8086&DEV_51ED, PCI\VEN_8086&DEV_461E)

Happy to provide more register dumps, the .elf/.map files, or the full Wireshark capture if useful.

主要言語
HTML
スター
91
フォーク
112
PR マージ指標
30日以内にマージされた PR はありません

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

arduino/ArduinoCore-sam のほかの issue

arduino/ArduinoCore-sam の issue をすべて見る

似ている issue

Embedded & IoT の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。