add chipidea ISO enable option

Signed-off-by: HiFiPHile <admin@hifiphile.com>
diff --git a/examples/host/audio_host/README.md b/examples/host/audio_host/README.md
index 5c2d6ec..4024c4e 100644
--- a/examples/host/audio_host/README.md
+++ b/examples/host/audio_host/README.md
@@ -117,7 +117,7 @@
 ## Notes
 
 - `tuh_audio_descriptor_cb()` exposes the validated Audio Control descriptor block during enumeration. Applications that need raw entity controls must copy the required entity IDs or descriptor fields before the callback returns, then use `tuh_audio_control_xfer()` after the device mounts.
-- While a stream is running, the driver queues up to `CFG_TUH_XFER_QUEUE_DEPTH` isochronous transfers with separate buffers and refills on completion. The example selects depth 2 on supported EHCI configurations and depth 1 otherwise. Transfers follow the endpoint's `bInterval`. `tuh_audio_capture_cb()` / `tuh_audio_playback_cb()` only count completed transfers; `audio_app_task()` services the FIFOs independently from the main loop. `tuh_audio_event_cb()` reports asynchronous start/stop results and unrecoverable transfer failures. The example restarts a failed stream automatically 100 ms later.
+- `audio_app_task()` services the FIFOs independently from the main loop. `tuh_audio_event_cb()` reports asynchronous start/stop results and unrecoverable transfer failures. The example restarts a failed stream automatically 100 ms later.
 - Capture and playback streams running concurrently in the same Audio Control instance must use the same sample rate.
 - `tuh_audio_read()` / `tuh_audio_write()` are non-blocking FIFO operations: they return the number of whole frames actually read/queued. `tuh_audio_read_available()` reports captured frames ready to read; `tuh_audio_write_available()` reports free playback capacity. `tuh_audio_write()` only queues data; the playback transfer-completion chain sends it, or sends silence when the FIFO does not contain a complete polling interval without consuming the partial data.
 - Isochronous transfers require the host to poll `tuh_task()` continuously; the capture FIFO absorbs short scheduling gaps and overwrites the oldest frames when full.
diff --git a/examples/host/audio_host/src/tusb_config.h b/examples/host/audio_host/src/tusb_config.h
index 50b5f30..983db5d 100644
--- a/examples/host/audio_host/src/tusb_config.h
+++ b/examples/host/audio_host/src/tusb_config.h
@@ -95,9 +95,9 @@
 #define CFG_TUH_DEVICE_MAX (3 * CFG_TUH_HUB + 1)
 
 //------------- Audio Host Config -------------//
-// Use the HCD's queue capacity, resolved by tusb_option.h after this config.
-#ifndef CFG_TUH_XFER_QUEUE_DEPTH
-  #define CFG_TUH_XFER_QUEUE_DEPTH TUP_HCD_XFER_QUEUE_DEPTH
+// Enable ISO and two transfer buffers on ChipIdea; other HCDs keep their defaults.
+#ifndef CFG_TUH_CHIPIDEA_ISO_ENABLE
+  #define CFG_TUH_CHIPIDEA_ISO_ENABLE 1
 #endif
 #define CFG_TUH_AUDIO_MAX           1
 #define CFG_TUH_AUDIO_EPIN_BUFSIZE  256 // max capture transfer the application submits
diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c
index 45e6b1f..8bfcca6 100644
--- a/src/portable/ehci/ehci.c
+++ b/src/portable/ehci/ehci.c
@@ -61,8 +61,9 @@
  * Each submission covers one service interval: HS uses iTDs (up to 3
  * transactions), FS uses siTDs. Requests exceeding interval capacity fail.
  *
- * CFG_TUH_XFER_QUEUE_DEPTH defaults to 1; EHCI supports up to 2. Accepted
- * buffers/descriptors remain owned until FIFO terminal completion. QUEUED
+ * CFG_TUH_CHIPIDEA_ISO_ENABLE enables ISO and defaults CFG_TUH_XFER_QUEUE_DEPTH
+ * to 2 (maximum supported); otherwise depth defaults to 1. Accepted buffers
+ * and descriptors remain owned until FIFO terminal completion. QUEUED
  * reports spare capacity with zero length; it does not release the buffer.
  * Audio uses a buffer per slot, primes on QUEUED and refills on completion.
  * Public tuh_edpt_xfer() retains single-outstanding callback behavior.
@@ -108,7 +109,7 @@
  *
  * References: EHCI 1.0 ch. 3/4; USB 2.0 ch. 11; RT1064 RM Rev. 2 ch. 42.
  */
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
 // An iTD must not cross a 4 KiB boundary (EHCI chapter 3).
 typedef union TU_ATTR_ALIGNED(64) {
   ehci_itd_t itd;
@@ -139,12 +140,12 @@
 
 typedef union {
   ehci_qhd_t qhd;
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
   iso_ep_t iso;
 #endif
 } ehci_ep_t;
 
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
 typedef union TU_ATTR_ALIGNED(64) {
   ehci_qtd_t qtd[2];
   iso_td_t iso;
@@ -166,7 +167,7 @@
   }control[CFG_TUH_DEVICE_MAX+CFG_TUH_HUB+1];
 
   ehci_ep_t qhd_pool[QHD_MAX];
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
   ehci_td_pair_t qtd_pool[(QTD_MAX + 1) / 2];
   bool qhd_is_iso[QHD_MAX];
   bool qtd_is_iso[(QTD_MAX + 1) / 2];
@@ -174,7 +175,7 @@
   ehci_qtd_t qtd_pool[QTD_MAX] TU_ATTR_ALIGNED(32);
 #endif
 
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
   uint32_t iso_uframe;
   uint16_t iso_last_frindex;
   uint8_t iso_saved_itc;
@@ -259,7 +260,7 @@
 TU_ATTR_ALWAYS_INLINE static inline void list_remove(ehci_link_t* head, ehci_link_t* prev, ehci_qhd_t* qhd);
 static void list_remove_qhd_by_addr(ehci_link_t *list_head, uint8_t dev_addr, uint8_t ep_addr);
 
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
 static iso_ep_t* iso_ep_find(uint8_t daddr, uint8_t ep_addr);
 static bool iso_ep_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const* desc);
 static bool iso_ep_close(uint8_t rhport, iso_ep_t* ep);
@@ -396,7 +397,7 @@
     return;
   }
 
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
   for (size_t i = 0; i < QHD_MAX; i++) {
     if (ehci_data.qhd_is_iso[i] && ehci_data.qhd_pool[i].iso.daddr == daddr) {
       TU_ASSERT(iso_ep_close(rhport, &ehci_data.qhd_pool[i].iso), );
@@ -543,7 +544,7 @@
 
 bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) {
   if (ep_desc->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
     return iso_ep_open(rhport, dev_addr, ep_desc);
 #else
     return false;
@@ -594,7 +595,7 @@
 }
 
 bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
   iso_ep_t* iso = iso_ep_find(daddr, ep_addr);
   if (iso != NULL) {
     return iso_ep_close(rhport, iso);
@@ -640,7 +641,7 @@
 bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) {
   (void) rhport;
 
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
   iso_ep_t* iso = iso_ep_find(dev_addr, ep_addr);
   if (iso != NULL) {
     return iso_xfer(rhport, iso, buffer, buflen);
@@ -693,7 +694,7 @@
 bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
   (void) rhport;
 
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
   iso_ep_t* iso = iso_ep_find(dev_addr, ep_addr);
   if (iso != NULL) {
     return iso_abort(rhport, iso);
@@ -730,7 +731,7 @@
 
 bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
   (void) rhport;
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
   TU_VERIFY(iso_ep_find(daddr, ep_addr) == NULL); // ISO endpoints do not halt
 #endif
   ehci_qhd_t *qhd = qhd_get_from_addr(daddr, ep_addr);
@@ -742,7 +743,7 @@
   return true;
 }
 
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
 //--------------------------------------------------------------------+
 // Isochronous transfers: one service interval per HCD submission
 //--------------------------------------------------------------------+
@@ -1197,7 +1198,7 @@
   (void) rhport;
 
   for (uint32_t i = 0; i < QHD_MAX; i++) {
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
     if (ehci_data.qhd_is_iso[i]) {
       continue;
     }
@@ -1336,7 +1337,7 @@
     return;
   }
 
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
   if (int_status & regs->inten & EHCI_INT_MASK_NXP_SOF) {
     regs->status = EHCI_INT_MASK_NXP_SOF;
   }
@@ -1367,7 +1368,7 @@
     // visited must remain pending for the next interrupt.
     regs->status = usb_int;
   }
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
   // SOF and completion commonly arrive together. Scan ISO only once, keeping
   // interrupt work short enough for task context to replenish the next slot.
   if (usb_int || (int_status & regs->inten & EHCI_INT_MASK_NXP_SOF)) {
@@ -1472,7 +1473,7 @@
 // Find a free queue head
 TU_ATTR_ALWAYS_INLINE static inline ehci_qhd_t *qhd_find_free(void) {
   for (uint32_t i = 0; i < QHD_MAX; i++) {
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
     if (ehci_data.qhd_is_iso[i]) {
       continue;
     }
@@ -1499,7 +1500,7 @@
   ehci_qhd_t *result = NULL;
   usbh_spin_lock(false);
   for (uint32_t i = 0; i < QHD_MAX; i++) {
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
     if (ehci_data.qhd_is_iso[i]) {
       continue;
     }
@@ -1624,7 +1625,7 @@
 
 TU_ATTR_ALWAYS_INLINE static inline ehci_qtd_t *qtd_find_free(void) {
   for (uint32_t i = 0; i < QTD_MAX; i++) {
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE
     if (!ehci_data.qtd_is_iso[i / 2] && !ehci_data.qtd_pool[i / 2].qtd[i % 2].used) {
       return &ehci_data.qtd_pool[i / 2].qtd[i % 2];
     }
diff --git a/src/tusb_option.h b/src/tusb_option.h
index b2bc439..6ab52e6 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -782,9 +782,14 @@
   #define CFG_TUH_TASK_EVENTS_PER_RUN  16
 #endif
 
-// HCD queue capacity. Applications can select this after MCU/IP resolution.
+// ChipIdea ISO is opt-in and selects two outstanding transfers by default.
+#ifndef CFG_TUH_CHIPIDEA_ISO_ENABLE
+  #define CFG_TUH_CHIPIDEA_ISO_ENABLE 0
+#endif
+
+// HCD queue capacity, resolved after the MCU/IP and controller selection.
 #ifndef TUP_HCD_XFER_QUEUE_DEPTH
-  #if defined(TUP_USBIP_EHCI) && !CFG_TUH_MAX3421
+  #if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE && !CFG_TUH_MAX3421
     #define TUP_HCD_XFER_QUEUE_DEPTH 2
   #else
     #define TUP_HCD_XFER_QUEUE_DEPTH 1
@@ -794,7 +799,11 @@
 // Maximum outstanding packets per endpoint. HCDs with spare capacity notify
 // class drivers with XFER_RESULT_QUEUED; other HCDs keep one packet in flight.
 #ifndef CFG_TUH_XFER_QUEUE_DEPTH
-  #define CFG_TUH_XFER_QUEUE_DEPTH 1
+  #if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE && !CFG_TUH_MAX3421
+    #define CFG_TUH_XFER_QUEUE_DEPTH TUP_HCD_XFER_QUEUE_DEPTH
+  #else
+    #define CFG_TUH_XFER_QUEUE_DEPTH 1
+  #endif
 #endif
 
 #if CFG_TUH_XFER_QUEUE_DEPTH < 1 || CFG_TUH_XFER_QUEUE_DEPTH > TUP_HCD_XFER_QUEUE_DEPTH
diff --git a/test/unit-test/host/CMakeLists.txt b/test/unit-test/host/CMakeLists.txt
index 10b511b..b2544e0 100644
--- a/test/unit-test/host/CMakeLists.txt
+++ b/test/unit-test/host/CMakeLists.txt
@@ -23,16 +23,28 @@
   set_tests_properties(${name} PROPERTIES TIMEOUT 10 LABELS host)
 endfunction()
 
-foreach (variant IN ITEMS disabled depth1 depth2 depth4)
+foreach (variant IN ITEMS generic disabled max3421 depth1 depth2 depth4)
   set(name test_ehci_iso_${variant})
   add_host_test(${name} ${CMAKE_CURRENT_LIST_DIR}/ehci/test_iso.c)
-  if (variant STREQUAL "disabled")
-    target_compile_definitions(${name} PRIVATE TEST_EHCI_GENERIC)
-    set(depth 1)
-  else ()
-    string(REPLACE "depth" "" depth ${variant})
+  if (NOT variant STREQUAL "disabled")
+    target_compile_definitions(${name} PRIVATE CFG_TUH_CHIPIDEA_ISO_ENABLE=1)
   endif ()
-  target_compile_definitions(${name} PRIVATE CFG_TUH_XFER_QUEUE_DEPTH=${depth} TUP_HCD_XFER_QUEUE_DEPTH=4)
+  if (variant STREQUAL "generic")
+    target_compile_definitions(${name} PRIVATE TEST_EHCI_GENERIC)
+  elseif (variant STREQUAL "max3421")
+    target_compile_definitions(${name} PRIVATE CFG_TUH_MAX3421=1)
+  elseif (variant STREQUAL "depth1")
+    target_compile_definitions(${name} PRIVATE CFG_TUH_XFER_QUEUE_DEPTH=1)
+  elseif (variant STREQUAL "depth4")
+    target_compile_definitions(${name} PRIVATE CFG_TUH_XFER_QUEUE_DEPTH=4 TUP_HCD_XFER_QUEUE_DEPTH=4)
+  endif ()
+  # Exercise default depth selection without overrides in the other variants.
+  if (variant MATCHES "^depth")
+    string(REPLACE "depth" "" depth ${variant})
+  else ()
+    set(depth 1)
+  endif ()
+  target_compile_definitions(${name} PRIVATE TEST_EXPECTED_QUEUE_DEPTH=${depth})
   target_compile_options(${name} PRIVATE -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast)
   # EHCI hardware links are 32-bit; keep the static simulated DMA memory below 4 GiB.
   if (WIN32)
diff --git a/test/unit-test/host/README.md b/test/unit-test/host/README.md
index 93fe07b..8583b90 100644
--- a/test/unit-test/host/README.md
+++ b/test/unit-test/host/README.md
@@ -17,15 +17,15 @@
   FRINDEX. Covers native FS, split transactions, HS, descriptor reuse,
   completion ordering/errors, cancellation, reset debounce and frame wrap.
   Shared QH/qTD pool tests cover mixed interrupt/ISO allocation, exhaustion,
-  rollback and reuse. Builds generic EHCI without ISO and ChipIdea at queue
-  depths 1, 2 and 4. Static DMA fixtures
-  are linked below 4 GiB; hardware descriptor layouts are asserted separately
+  rollback and reuse. Builds ChipIdea with ISO disabled and enabled at queue
+  depths 1, 2 and 4, and checks that the ISO flag keeps depth 1 on generic EHCI
+  and MAX3421. Static DMA fixtures are linked below 4 GiB; hardware descriptor layouts are asserted separately
   from the native-pointer software tail.
 - **USBH/audio:** actual stack and audio drivers with a stub HCD. Covers
   buffer ownership, queue capacity, callback dispatch, failed submissions,
   abort/close, capture/playback refill, underrun silence, feedback and reset
   timing. Builds generic, EHCI and MAX3421 configurations at depths 1, 2 and 4.
 
-The 13 CTest cases retain assertions in release builds and have bounded
+The 15 CTest cases retain assertions in release builds and have bounded
 execution times. They do not emulate DMA/cache coherency or USB wire timing;
 those still require hardware tests.
diff --git a/test/unit-test/host/ehci/test_iso.c b/test/unit-test/host/ehci/test_iso.c
index 4685286..2b0b991 100644
--- a/test/unit-test/host/ehci/test_iso.c
+++ b/test/unit-test/host/ehci/test_iso.c
@@ -14,6 +14,7 @@
 #define TU_VERIFY_STATIC(condition, ...) _Static_assert(condition, __VA_ARGS__)
 #include "portable/ehci/ehci.c"
 
+_Static_assert(CFG_TUH_XFER_QUEUE_DEPTH == TEST_EXPECTED_QUEUE_DEPTH, "queue depth selection");
 _Static_assert(sizeof(ehci_link_t) == 4, "link ABI");
 _Static_assert(sizeof(ehci_qtd_t) == 32, "qTD ABI");
 _Static_assert(offsetof(ehci_qhd_t, qtd_overlay) == 16, "QH hardware prefix");
@@ -76,7 +77,7 @@
   assert(regs.portsc == before); // Attach must not reset the port before USBH debounces it.
 }
 
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE && !CFG_TUH_MAX3421
 #define TEST_ISO_STREAM_EP_COUNT 4
 
 static bool open_ep(uint8_t addr, uint8_t speed, uint16_t size, uint8_t interval) {
@@ -378,7 +379,7 @@
   assert(events == 1);
 }
 
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE && !CFG_TUH_MAX3421
 static void test_limits_and_late_completion(void) {
   reset(TUSB_SPEED_HIGH);
   assert(!open_ep(0x80, TUSB_SPEED_HIGH, 64, 1));
@@ -810,7 +811,7 @@
   assert((uintptr_t)&ehci_data <= UINT32_MAX && (uintptr_t)buffer <= UINT32_MAX);
   test_attach_debounce();
   test_qtd_retirement();
-#ifdef TUP_USBIP_CHIPIDEA_HS
+#if defined(TUP_USBIP_CHIPIDEA_HS) && CFG_TUH_CHIPIDEA_ISO_ENABLE && !CFG_TUH_MAX3421
   test_shared_pools();
   test_native_fs();
   test_split();
@@ -831,6 +832,15 @@
 #if CFG_TUH_XFER_QUEUE_DEPTH > 1
   test_queue();
 #endif
+#else
+  reset(TUSB_SPEED_HIGH);
+  tusb_desc_endpoint_t const iso_desc = {
+    .bLength = sizeof(iso_desc), .bDescriptorType = TUSB_DESC_ENDPOINT,
+    .bEndpointAddress = 0x81, .bmAttributes = {.xfer = TUSB_XFER_ISOCHRONOUS},
+    .wMaxPacketSize = 64, .bInterval = 1
+  };
+  assert(!hcd_edpt_open(0, 1, &iso_desc));
+  assert(regs.command_bm.int_threshold == 8);
 #endif
   puts("EHCI ISO regression tests passed");
   return 0;