probe: split pioasm and setup code into variant files, and add OEN variant
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1c4ca75..f4b8a67 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -36,6 +36,7 @@
 target_compile_options(picoprobe PRIVATE -Wall)
 
 pico_generate_pio_header(picoprobe ${CMAKE_CURRENT_LIST_DIR}/src/probe.pio)
+pico_generate_pio_header(picoprobe ${CMAKE_CURRENT_LIST_DIR}/src/probe_oen.pio)
 
 target_include_directories(picoprobe PRIVATE src)
 
diff --git a/README.md b/README.md
index 606f874..c938a95 100644
--- a/README.md
+++ b/README.md
@@ -3,3 +3,9 @@
 
 # Documentation
 Picoprobe documentation can be found in the [Pico Getting Started Guide](https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf). See "Appendix A: Using Picoprobe".
+
+# TODO
+- TinyUSB's vendor interface is FIFO-based and not packet-based. Using raw tx/rx callbacks is preferable as this stops DAP command batches from being concatenated, which confused openOCD.
+- Instead of polling, move the DAP thread to an asynchronously started/stopped one-shot operation to reduce CPU wakeups
+- AutoBaud selection, as PIO is a capable frequency counter
+- Possibly include RTT support
diff --git a/src/probe.c b/src/probe.c
index de8832d..825320d 100644
--- a/src/probe.c
+++ b/src/probe.c
@@ -111,49 +111,13 @@
     while(pio_sm_get_pc(pio0, PROBE_SM) != probe.offset + probe_offset_out_idle);
 }
 
-void probe_gpio_init()
-{
-#if defined(PROBE_PIN_RESET)
-    // Target reset pin: pull up, input to emulate open drain pin
-    gpio_pull_up(PROBE_PIN_RESET);
-    // gpio_init will leave the pin cleared and set as input
-    gpio_init(PROBE_PIN_RESET);
-#endif
-    // Funcsel pins
-    pio_gpio_init(pio0, PROBE_PIN_SWCLK);
-    pio_gpio_init(pio0, PROBE_PIN_SWDIO);
-    // Make sure SWDIO has a pullup on it. Idle state is high
-    gpio_pull_up(PROBE_PIN_SWDIO);
-}
-
 void probe_init() {
     if (!probe.initted) {
         uint offset = pio_add_program(pio0, &probe_program);
         probe.offset = offset;
 
         pio_sm_config sm_config = probe_program_get_default_config(offset);
-
-        // Set SWCLK as a sideset pin
-        sm_config_set_sideset_pins(&sm_config, PROBE_PIN_SWCLK);
-
-        // Set SWDIO offset
-        sm_config_set_out_pins(&sm_config, PROBE_PIN_SWDIO, 1);
-        sm_config_set_set_pins(&sm_config, PROBE_PIN_SWDIO, 1);
-#ifdef PROBE_PIN_SWDI
-        sm_config_set_in_pins(&sm_config, PROBE_PIN_SWDI);
-#else
-        sm_config_set_in_pins(&sm_config, PROBE_PIN_SWDIO);
-#endif
-
-        // Set SWD and SWDIO pins as output to start. This will be set in the sm
-        pio_sm_set_consecutive_pindirs(pio0, PROBE_SM, PROBE_PIN_OFFSET, 2, true);
-
-        // shift output right, autopull off, autopull threshold
-        sm_config_set_out_shift(&sm_config, true, false, 0);
-        // shift input right as swd data is lsb first, autopush off
-        sm_config_set_in_shift(&sm_config, true, false, 0);
-
-        // Init SM with config
+        probe_sm_init(&sm_config);
         pio_sm_init(pio0, PROBE_SM, offset, &sm_config);
 
         // Set up divisor
diff --git a/src/probe.h b/src/probe.h
index d9d9e55..a76f5c9 100644
--- a/src/probe.h
+++ b/src/probe.h
@@ -26,6 +26,14 @@
 #ifndef PROBE_H_
 #define PROBE_H_
 
+#if defined(PROBE_IO_RAW) || defined(PROBE_IO_SWDI)
+#include "probe.pio.h"
+#endif
+
+#if defined(PROBE_IO_OEN)
+#include "probe_oen.pio.h"
+#endif
+
 void probe_set_swclk_freq(uint freq_khz);
 void probe_write_bits(uint bit_count, uint32_t data_byte);
 uint32_t probe_read_bits(uint bit_count);
@@ -33,7 +41,6 @@
 void probe_read_mode(void);
 void probe_write_mode(void);
 
-void probe_gpio_init(void);
 void probe_init(void);
 void probe_deinit(void);
 
diff --git a/src/probe.pio b/src/probe.pio
index e8e8683..934d353 100644
--- a/src/probe.pio
+++ b/src/probe.pio
@@ -49,3 +49,47 @@
     jmp x-- in_posedge_bitloop        side 0x0 ;
     push                                      ; Push to rx fifo when done
     jmp in_posedge                            ; Jump back to start
+
+; Implement probe_gpio_init() and probe_sm_init() methods here - set pins, offsets, sidesets etc
+% c-sdk {
+
+static inline void probe_gpio_init()
+{
+#if defined(PROBE_PIN_RESET)
+    // Target reset pin: pull up, input to emulate open drain pin
+    gpio_pull_up(PROBE_PIN_RESET);
+    // gpio_init will leave the pin cleared and set as input
+    gpio_init(PROBE_PIN_RESET);
+#endif
+    // Funcsel pins
+    pio_gpio_init(pio0, PROBE_PIN_SWCLK);
+    pio_gpio_init(pio0, PROBE_PIN_SWDIO);
+    // Make sure SWDIO has a pullup on it. Idle state is high
+    gpio_pull_up(PROBE_PIN_SWDIO);
+}
+
+static inline void probe_sm_init(pio_sm_config* sm_config) {
+
+    // Set SWCLK as a sideset pin
+    sm_config_set_sideset_pins(sm_config, PROBE_PIN_SWCLK);
+
+    // Set SWDIO offset
+    sm_config_set_out_pins(sm_config, PROBE_PIN_SWDIO, 1);
+    sm_config_set_set_pins(sm_config, PROBE_PIN_SWDIO, 1);
+#ifdef PROBE_IO_SWDI
+    sm_config_set_in_pins(sm_config, PROBE_PIN_SWDI);
+#else
+    sm_config_set_in_pins(sm_config, PROBE_PIN_SWDIO);
+#endif
+
+
+    // Set SWD and SWDIO pins as output to start. This will be set in the sm
+    pio_sm_set_consecutive_pindirs(pio0, PROBE_SM, PROBE_PIN_OFFSET, 2, true);
+
+    // shift output right, autopull off, autopull threshold
+    sm_config_set_out_shift(sm_config, true, false, 0);
+    // shift input right as swd data is lsb first, autopush off
+    sm_config_set_in_shift(sm_config, true, false, 0);
+}
+
+%}
diff --git a/src/probe_oen.pio b/src/probe_oen.pio
new file mode 100644
index 0000000..ad2dbb5
--- /dev/null
+++ b/src/probe_oen.pio
@@ -0,0 +1,73 @@
+; Output-enable active-low variant of the SWD probe
+
+.program probe
+.side_set 2 opt
+
+; SWDIO_OEN is pin 0, SWCLK pin 1, SWDIO (out) pin 2, SWDI (in) pin 3.
+; Pin 0 and 1 are sideset pins
+public out_negedge:
+  set pindirs, 0x1 side 0x0    ; OE_N 0, data high, clock 0
+public out_idle:
+  pull                         ; pull nbits - 1
+  mov x, osr
+  pull                         ; pull data
+public out_negedge_bitloop:
+  out pins, 1                  side 0x0
+  jmp x-- out_negedge_bitloop  side 0x2  ; OE_N 0, clock high
+  set pins, 1                  side 0x0  ; drive data high (idle bus state)
+  push                         ; Push to rx fifo just so processor knows when done
+  jmp out_negedge              ; Wait for next transaction
+
+public in_posedge:
+  set pindirs, 0x0            side 0x1 ; OE_N 1, data high, clock 0
+public in_idle:
+  pull
+  mov x, osr
+in_posedge_bitloop:
+  in pins, 1                  side 0x1 ; OE_N 1, clock 0
+  jmp x-- in_posedge_bitloop  side 0x3 ; OE_N 1, clock 1
+  push
+  jmp in_posedge
+
+
+; Implement probe_gpio_init() and probe_sm_init() methods here - set pins, offsets, sidesets etc
+% c-sdk {
+
+void probe_gpio_init()
+{
+#if defined(PROBE_PIN_RESET)
+    // Target reset pin: pull up, input to emulate open drain pin
+    gpio_pull_up(PROBE_PIN_RESET);
+    // gpio_init will leave the pin cleared and set as input
+    gpio_init(PROBE_PIN_RESET);
+#endif
+    // Funcsel pins
+    pio_gpio_init(pio0, PROBE_PIN_SWDIOEN);
+    pio_gpio_init(pio0, PROBE_PIN_SWCLK);
+    pio_gpio_init(pio0, PROBE_PIN_SWDIO);
+
+    // Make sure SWDIO has a pullup on it. Idle state is high
+    gpio_pull_up(PROBE_PIN_SWDIO);
+    gpio_pull_up(PROBE_PIN_SWDIOEN);
+}
+
+void probe_sm_init(pio_sm_config* sm_config) {
+
+    // Set SWDIOEN and SWCLK as sideset pins
+    sm_config_set_sideset_pins(&sm_config, PROBE_PIN_SWDIOEN);
+
+    // Set SWDIO offset
+    sm_config_set_out_pins(sm_config, PROBE_PIN_SWDIO, 1);
+    sm_config_set_set_pins(sm_config, PROBE_PIN_SWDIO, 1);
+    sm_config_set_in_pins(sm_config, PROBE_PIN_SWDI);
+
+    // Set SWDIOEN, SWD and SWDIO pins as output to start. This will be set in the sm
+    pio_sm_set_consecutive_pindirs(pio0, PROBE_SM, PROBE_PIN_OFFSET, 3, true);
+
+    // shift output right, autopull off, autopull threshold
+    sm_config_set_out_shift(sm_config, true, false, 0);
+    // shift input right as swd data is lsb first, autopush off
+    sm_config_set_in_shift(sm_config, true, false, 0);
+}
+
+%}
\ No newline at end of file