Merge branch 'fix_debug' into 'master'

Fix ARM CMSIS-DAP issues

See merge request projectmu/picoprobe!1
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c7df996..ffa907f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,6 +17,7 @@
         src/cdc_uart.c
         src/get_serial.c
         src/sw_dp_pio.c
+        src/tusb_edpt_handler.c
 )
 
 target_sources(picoprobe PRIVATE
diff --git a/src/main.c b/src/main.c
index ac09011..1b8838b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -39,6 +39,7 @@
 #include "cdc_uart.h"
 #include "get_serial.h"
 #include "led.h"
+#include "tusb_edpt_handler.h"
 #include "DAP.h"
 
 // UART0 for Picoprobe debug
@@ -53,7 +54,7 @@
 #define TUD_TASK_PRIO  (tskIDLE_PRIORITY + 2)
 #define DAP_TASK_PRIO  (tskIDLE_PRIORITY + 1)
 
-static TaskHandle_t dap_taskhandle, tud_taskhandle;
+TaskHandle_t dap_taskhandle, tud_taskhandle;
 
 void usb_thread(void *ptr)
 {
@@ -75,22 +76,6 @@
 #define tud_vendor_flush(x) ((void)0)
 #endif
 
-void dap_thread(void *ptr)
-{
-    uint32_t resp_len;
-    do {
-        if (tud_vendor_available()) {
-            tud_vendor_read(RxDataBuffer, sizeof(RxDataBuffer));
-            resp_len = DAP_ProcessCommand(RxDataBuffer, TxDataBuffer);
-            tud_vendor_write(TxDataBuffer, resp_len);
-            tud_vendor_flush();
-        } else {
-            // Trivial delay to save power
-            vTaskDelay(1);
-        }
-    } while (1);
-}
-
 int main(void) {
 
     board_init();
diff --git a/src/tusb_edpt_handler.c b/src/tusb_edpt_handler.c
new file mode 100644
index 0000000..a08318d
--- /dev/null
+++ b/src/tusb_edpt_handler.c
@@ -0,0 +1,218 @@
+/**
+ * Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "tusb_edpt_handler.h"
+#include "DAP.h"
+
+static uint8_t itf_num;
+static uint8_t _rhport;
+
+volatile uint32_t _resp_len;
+
+uint8_t _out_ep_addr;
+uint8_t _in_ep_addr;
+
+buffer_t USBRequestBuffer; 
+buffer_t USBResponseBuffer;
+
+void dap_edpt_init(void) {
+
+}
+    
+void dap_edpt_reset(uint8_t __unused rhport)
+{
+    itf_num = 0;
+}
+
+uint16_t dap_edpt_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
+{   
+          
+    TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == itf_desc->bInterfaceClass &&
+              PICOPROBE_INTERFACE_SUBCLASS == itf_desc->bInterfaceSubClass &&
+              PICOPROBE_INTERFACE_PROTOCOL == itf_desc->bInterfaceProtocol, 0);
+
+    //  Initialise circular buffer indices
+    USBResponseBuffer.packet_wr_idx = 0;
+    USBResponseBuffer.packet_rd_idx = 0;
+    USBRequestBuffer.packet_wr_idx = 0;
+    USBRequestBuffer.packet_rd_idx = 0;
+
+    // Initialse full/empty flags
+    USBResponseBuffer.wasFull = false;
+    USBResponseBuffer.wasEmpty = true;
+    USBRequestBuffer.wasFull = false;
+    USBRequestBuffer.wasEmpty = true;
+
+    uint16_t const drv_len = sizeof(tusb_desc_interface_t) + (itf_desc->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
+    TU_VERIFY(max_len >= drv_len, 0);
+    itf_num = itf_desc->bInterfaceNumber;
+
+    // Initialising the OUT endpoint
+
+    tusb_desc_endpoint_t *edpt_desc = (tusb_desc_endpoint_t *) (itf_desc + 1);
+    uint8_t ep_addr = edpt_desc->bEndpointAddress;
+
+    _out_ep_addr = ep_addr;
+
+    // The OUT endpoint requires a call to usbd_edpt_xfer to initialise the endpoint, giving tinyUSB a buffer to consume when a transfer occurs at the endpoint
+    usbd_edpt_open(rhport, edpt_desc);
+    usbd_edpt_xfer(rhport, ep_addr, &(USBRequestBuffer.data[USBRequestBuffer.packet_wr_idx][0]), DAP_PACKET_SIZE);
+
+    // Initiliasing the IN endpoint
+
+    edpt_desc++;
+    ep_addr = edpt_desc->bEndpointAddress;
+
+    _in_ep_addr = ep_addr;
+
+    // The IN endpoint doesn't need a transfer to initialise it, as this will be done by the main loop of dap_thread
+    usbd_edpt_open(rhport, edpt_desc);
+
+    return drv_len;
+
+}
+
+bool dap_edpt_control_xfer_cb(uint8_t __unused rhport, uint8_t stage, tusb_control_request_t const *request)
+{   
+    return false;
+}
+
+// Manage USBResponseBuffer (request) write and USBRequestBuffer (response) read indices
+bool dap_edpt_xfer_cb(uint8_t __unused rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{   
+    const uint8_t ep_dir = tu_edpt_dir(ep_addr);
+
+    if(ep_dir == TUSB_DIR_IN)
+    {   
+        if(xferred_bytes >= 0u && xferred_bytes <= DAP_PACKET_SIZE)
+        {   
+            USBResponseBuffer.packet_rd_idx = (USBResponseBuffer.packet_rd_idx + 1) % DAP_PACKET_COUNT;
+
+            // This checks that the buffer was not empty in DAP thread, which means the next buffer was not queued up for the in endpoint callback
+            // So, queue up the buffer at the new read index, since we expect read to catch up to write at this point.
+            // It is possible for the read index to be multiple spaces behind the write index (if the USB callbacks are lagging behind dap thread), 
+            // so we account for this by only setting wasEmpty to true if the next callback will empty the buffer
+            if(!USBResponseBuffer.wasEmpty)
+            {   
+                usbd_edpt_xfer(rhport, ep_addr, &(USBResponseBuffer.data[USBResponseBuffer.packet_rd_idx][0]), (uint16_t) _resp_len);
+                USBResponseBuffer.wasEmpty = ((USBResponseBuffer.packet_rd_idx + 1) % DAP_PACKET_COUNT == USBResponseBuffer.packet_wr_idx);
+            }        
+
+            //  Wake up DAP thread after processing the callback
+            vTaskResume(dap_taskhandle);
+            return true;
+        }
+        
+        return false;
+
+    } else if(ep_dir == TUSB_DIR_OUT)    {
+
+        if(xferred_bytes >= 0u && xferred_bytes <= DAP_PACKET_SIZE)
+        {   
+            // Only queue the next buffer in the out callback if the buffer is not full
+            // If full, we set the wasFull flag, which will be checked by dap thread                       
+            if(!buffer_full(&USBRequestBuffer))
+            {   
+                USBRequestBuffer.packet_wr_idx = (USBRequestBuffer.packet_wr_idx + 1) % DAP_PACKET_COUNT; 
+                usbd_edpt_xfer(rhport, ep_addr, &(USBRequestBuffer.data[USBRequestBuffer.packet_wr_idx][0]), DAP_PACKET_SIZE);
+                USBRequestBuffer.wasFull = false; 
+            }
+            else {
+                USBRequestBuffer.wasFull = true;
+            }
+
+            //  Wake up DAP thread after processing the callback
+            vTaskResume(dap_taskhandle);
+            return true;
+        }
+
+        return false;
+    }
+    else return false;
+}
+
+void dap_thread(void *ptr)
+{
+    uint8_t DAPRequestBuffer[DAP_PACKET_SIZE];
+    uint8_t DAPResponseBuffer[DAP_PACKET_SIZE];
+
+    do
+    {   
+        while(USBRequestBuffer.packet_rd_idx != USBRequestBuffer.packet_wr_idx)
+        {   
+            // Read a single packet from the USB buffer into the DAP Request buffer
+            memcpy(DAPRequestBuffer, &(USBRequestBuffer.data[USBRequestBuffer.packet_rd_idx]), DAP_PACKET_SIZE);
+            USBRequestBuffer.packet_rd_idx = (USBRequestBuffer.packet_rd_idx + 1) % DAP_PACKET_COUNT; 
+
+            // If the buffer was full in the out callback, we need to queue up another buffer for the endpoint to consume, now that we know there is space in the buffer.
+            if(USBRequestBuffer.wasFull)
+            {   
+                vTaskSuspendAll(); // Suspend the scheduler to safely update the write index
+                USBRequestBuffer.packet_wr_idx = (USBRequestBuffer.packet_wr_idx + 1) % DAP_PACKET_COUNT;
+                usbd_edpt_xfer(_rhport, _out_ep_addr, &(USBRequestBuffer.data[USBRequestBuffer.packet_wr_idx][0]), DAP_PACKET_SIZE);
+                USBRequestBuffer.wasFull = false;
+                xTaskResumeAll();
+            }
+
+            _resp_len = DAP_ProcessCommand(DAPRequestBuffer, DAPResponseBuffer);
+
+
+            //  Suspend the scheduler to avoid stale values/race conditions between threads
+            vTaskSuspendAll();
+
+            if(buffer_empty(&USBResponseBuffer))
+            {
+                memcpy(&(USBResponseBuffer.data[USBResponseBuffer.packet_wr_idx]), DAPResponseBuffer, (uint16_t) _resp_len); 
+                USBResponseBuffer.packet_wr_idx = (USBResponseBuffer.packet_wr_idx + 1) % DAP_PACKET_COUNT; 
+
+                usbd_edpt_xfer(_rhport, _in_ep_addr, &(USBResponseBuffer.data[USBResponseBuffer.packet_rd_idx][0]), (uint16_t) _resp_len);
+            } else {
+
+                memcpy(&(USBResponseBuffer.data[USBResponseBuffer.packet_wr_idx]), DAPResponseBuffer, (uint16_t) _resp_len); 
+                USBResponseBuffer.packet_wr_idx = (USBResponseBuffer.packet_wr_idx + 1) % DAP_PACKET_COUNT; 
+                
+                // The In callback needs to check this flag to know when to queue up the next buffer.
+                USBResponseBuffer.wasEmpty = false;
+            }
+            xTaskResumeAll();
+        }
+        
+        // Suspend DAP thread until it is awoken by a USB thread callback
+        vTaskSuspend(dap_taskhandle);
+
+    } while (1);
+    
+}
+
+usbd_class_driver_t const _dap_edpt_driver =
+{
+    .init = dap_edpt_init,
+    .reset = dap_edpt_reset,
+    .open = dap_edpt_open,
+    .control_xfer_cb = dap_edpt_control_xfer_cb,
+    .xfer_cb = dap_edpt_xfer_cb,
+    .sof = NULL,
+    #if CFG_TUSB_DEBUG >= 2
+        .name = "PICOPROBE ENDPOINT"
+    #endif
+};
+
+// Add the custom driver to the tinyUSB stack
+usbd_class_driver_t const *usbd_app_driver_get_cb(uint8_t *driver_count)
+{
+    *driver_count = 1;
+    return &_dap_edpt_driver;
+}
+
+bool buffer_full(buffer_t *buffer)
+{
+    return ((buffer->packet_wr_idx + 1) % DAP_PACKET_COUNT == buffer->packet_rd_idx);
+}
+
+bool buffer_empty(buffer_t *buffer)
+{
+    return (buffer->packet_wr_idx == buffer->packet_rd_idx);
+}
diff --git a/src/tusb_edpt_handler.h b/src/tusb_edpt_handler.h
new file mode 100644
index 0000000..46badf1
--- /dev/null
+++ b/src/tusb_edpt_handler.h
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef TUSB_EDPT_HANDLER_H
+#define TUSB_EDPT_HANDLER_H
+
+#include "tusb.h"
+
+#include "device/usbd_pvt.h"
+#include "DAP_config.h"
+
+#define PICOPROBE_INTERFACE_SUBCLASS 0x00
+#define PICOPROBE_INTERFACE_PROTOCOL 0x00
+
+typedef struct {
+    uint8_t data[DAP_PACKET_COUNT][DAP_PACKET_SIZE];  
+    volatile uint32_t packet_wr_idx;
+    volatile uint32_t packet_rd_idx;
+    volatile bool wasEmpty;
+    volatile bool wasFull;
+} buffer_t;
+
+extern TaskHandle_t dap_taskhandle, tud_taskhandle;
+
+/* Main DAP loop */
+void dap_thread(void *ptr);
+
+/* Endpoint Handling */
+void picoprobe_edpt_init(void);
+uint16_t picoprobe_edpt_open(uint8_t __unused rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len);
+bool picoprobe_edpt_control_xfer_cb(uint8_t __unused rhport, uint8_t stage,  tusb_control_request_t const *request);
+bool picoprobe_edpt_xfer_cb(uint8_t __unused rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+
+/* Helper Functions */
+bool buffer_full(buffer_t *buffer);
+bool buffer_empty(buffer_t *buffer);
+
+#endif
\ No newline at end of file
diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c
index cb778b3..5b0b54f 100644
--- a/src/usb_descriptors.c
+++ b/src/usb_descriptors.c
@@ -242,4 +242,4 @@
 uint8_t const * tud_descriptor_bos_cb(void)
 {
   return desc_bos;
-}
+}
\ No newline at end of file