examples: write the host cdc console over RTT when that is the console

console_write() forwards what the attached CDC device sent back to the
board's console, and reached for board_uart_write() directly to stay
non-blocking. On a board whose console is the debug probe there is no UART:
board_uart_write() is a stub returning -1, so everything the device echoed was
dropped, and the do/while loop retried a write that could never make progress
and spun until more data arrived.

Send it through SEGGER_RTT_Write() on LOGGER_RTT builds -- already
non-blocking in the default NO_BLOCK_SKIP mode, which is what the UART path
wanted board_uart_write() for.

The retry is also bounded now. board_uart_write() is only documented to
return the number of bytes sent, and the stubs disagree on how they say "no
console here" -- lpc40 returns -1, apm32f0xx returns 0 -- so the loop cannot
decide from the return value alone when to stop. It gives up after 10 ms
without progress instead, which no draining console ever hits, and keeps the
negative return as a fast path so a stub UART does not cost that wait on
every read.

The RTT header include lives in board_api.h so any example can reach it; the
include path only exists when LOGGER=rtt defines LOGGER_RTT.

host/cdc_msc_hid now passes on ea4088_quickstart, whose probe (LPC-Link2) has
no VCOM: the CDC echo round-trips through a CH340 with TX-RX shorted.
diff --git a/examples/host/cdc_msc_hid/src/cdc_app.c b/examples/host/cdc_msc_hid/src/cdc_app.c
index e6c1907..b77f7b0 100644
--- a/examples/host/cdc_msc_hid/src/cdc_app.c
+++ b/examples/host/cdc_msc_hid/src/cdc_app.c
@@ -42,11 +42,24 @@
   return count;
 }
 
-static size_t console_write(const uint8_t *buf, size_t bufsize) {
+// Give up forwarding to the console after this long without progress. Not every board's
+// board_uart_write() reports "no console here" the same way, so the loop below cannot rely
+// on the return value alone to know when to stop.
+enum { CONSOLE_STALL_MS = 10 };
+
+// Returns bytes written, 0 if the console cannot take them right now, negative if this
+// board has no console at all.
+static int console_write(const uint8_t *buf, size_t bufsize) {
+#if defined(LOGGER_RTT)
+  // The console is the debug probe, not a UART: board_uart_write() is a stub on those
+  // boards. NO_BLOCK_SKIP writes all or nothing, so 0 means the probe has not drained the
+  // up-buffer yet.
+  return (int) SEGGER_RTT_Write(0, buf, (unsigned) bufsize);
+#else
   // Use board_uart_write directly for non-blocking behavior.
   // board_putchar -> sys_write has a blocking retry loop that causes UART RX overrun.
-  int wr = board_uart_write(buf, (int) bufsize);
-  return (wr > 0) ? (size_t) wr : 0;
+  return board_uart_write(buf, (int) bufsize);
+#endif
 }
 
 // forward from console to usbh
@@ -70,11 +83,23 @@
   uint8_t  buf[64];
   uint32_t count = tuh_cdc_read(idx, buf, sizeof(buf));
   uint32_t wr    = 0;
+  uint32_t progress_ms = tusb_time_millis_api();
 
   do {
-    // uart write is slow, while waiting forward uart -> usbh else uart rx can be overflow
+    // console write is slow, while waiting forward console -> usbh else its rx can overflow
     if (count) {
-      wr += console_write(buf + wr, count - wr);
+      const int written = console_write(buf + wr, count - wr);
+      if (written < 0) {
+        break; // no console on this board at all
+      }
+      if (written > 0) {
+        wr += (uint32_t) written;
+        progress_ms = tusb_time_millis_api();
+      } else if (tusb_time_millis_api() - progress_ms > CONSOLE_STALL_MS) {
+        // a TX FIFO or an RTT up-buffer is only ever transiently full: nothing draining it
+        // must not stall the host task, so drop the rest
+        break;
+      }
     }
     console_to_usbh(idx);
   } while (wr < count);
diff --git a/hw/bsp/board_api.h b/hw/bsp/board_api.h
index 73c0607..27316af 100644
--- a/hw/bsp/board_api.h
+++ b/hw/bsp/board_api.h
@@ -39,6 +39,10 @@
 
 #include "tusb.h"
 
+#if defined(LOGGER_RTT)
+#include "SEGGER_RTT.h"
+#endif
+
 #if CFG_TUSB_OS == OPT_OS_ZEPHYR
   #include <zephyr/kernel.h>
 #elif CFG_TUSB_OS == OPT_OS_FREERTOS