From ecf2f910424b53fecc3ca82ae410f054518cc752 Mon Sep 17 00:00:00 2001
From: hathach <thach@tinyusb.org>
Date: Fri, 25 Aug 2023 16:36:28 +0700
Subject: [PATCH] use endpoint pool for more flexible multiple devices support

---
 src/portable/analog/max3421/hcd_max3421.c | 180 +++++++++++++++-------
 1 file changed, 122 insertions(+), 58 deletions(-)

diff --git a/src/portable/analog/max3421/hcd_max3421.c b/src/portable/analog/max3421/hcd_max3421.c
index 09845ffe4..b0325f240 100644
--- a/src/portable/analog/max3421/hcd_max3421.c
+++ b/src/portable/analog/max3421/hcd_max3421.c
@@ -165,22 +165,26 @@ enum {
 //--------------------------------------------------------------------+
 
 typedef struct {
-  uint16_t packet_size;
-
   struct TU_ATTR_PACKED {
+    uint8_t ep_dir        : 1;
     uint8_t is_iso        : 1;
     uint8_t data_toggle   : 1;
-    uint8_t xfer_queued   : 1;
+    uint8_t xfer_pending  : 1;
     uint8_t xfer_complete : 1;
   };
+  struct TU_ATTR_PACKED {
+    uint8_t daddr : 4;
+    uint8_t ep_num : 4;
+  };
 
+  uint16_t packet_size;
   uint16_t total_len;
   uint16_t xferred_len;
   uint8_t* buf;
-} hcd_ep_t;
+} max3421_ep_t;
 
 typedef struct {
-  atomic_bool busy; // busy transferring
+  atomic_flag busy; // busy transferring
 
   // cached register
   uint8_t sndbc;
@@ -192,15 +196,16 @@ typedef struct {
 
   volatile uint16_t frame_count;
 
-  hcd_ep_t ep[8][2];
+  max3421_ep_t ep0_addr0; // control endpoint for addr0
+  max3421_ep_t ep[CFG_TUH_MAX3421_ENDPOINT_TOTAL];
 
   OSAL_MUTEX_DEF(spi_mutexdef);
 #if OSAL_MUTEX_REQUIRED
   osal_mutex_t spi_mutex;
 #endif
-} max2341_data_t;
+} max3421_data_t;
 
-static max2341_data_t _hcd_data;
+static max3421_data_t _hcd_data;
 
 //--------------------------------------------------------------------+
 // API: SPI transfer with MAX3421E, must be implemented by application
@@ -211,7 +216,7 @@ bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const * tx_buf, size_t tx_
 void tuh_max3421e_int_api(uint8_t rhport, bool enabled);
 
 //--------------------------------------------------------------------+
-//
+// SPI Helper
 //--------------------------------------------------------------------+
 
 static void max3421_spi_lock(uint8_t rhport, bool in_isr) {
@@ -318,6 +323,44 @@ static inline void sndbc_write(uint8_t rhport, uint8_t data, bool in_isr) {
   reg_write(rhport, SNDBC_ADDR, data, in_isr);
 }
 
+//--------------------------------------------------------------------+
+// Endpoint helper
+//--------------------------------------------------------------------+
+
+static max3421_ep_t* find_ep_not_addr0(uint8_t daddr, uint8_t ep_num, uint8_t ep_dir) {
+  for(size_t i=0; i<CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
+    max3421_ep_t* ep = &_hcd_data.ep[i];
+    // for control endpoint, skip direction check
+    if ( daddr == ep->daddr && ep_num == ep->ep_num && (ep_dir == ep->ep_dir || ep_num == 0) ) {
+      return ep;
+    }
+  }
+
+  return NULL;
+}
+
+// daddr = 0 and ep_num = 0 means find a free (allocate) endpoint
+TU_ATTR_ALWAYS_INLINE static inline max3421_ep_t * allocate_ep(void) {
+  return find_ep_not_addr0(0, 0, 0);
+}
+
+// free all endpoints belong to device address
+static void free_ep(uint8_t daddr) {
+  for (size_t i=0; i<CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
+    max3421_ep_t* ep = &_hcd_data.ep[i];
+    if (ep->daddr == daddr) {
+      tu_memclr(ep, sizeof(max3421_ep_t));
+    }
+  }
+}
+
+TU_ATTR_ALWAYS_INLINE static inline max3421_ep_t * find_opened_ep(uint8_t daddr, uint8_t ep_num, uint8_t ep_dir) {
+  if (daddr == 0 && ep_num == 0) {
+    return &_hcd_data.ep0_addr0;
+  }else{
+    return find_ep_not_addr0(daddr, ep_num, ep_dir);
+  }
+}
 
 //--------------------------------------------------------------------+
 // Controller API
@@ -379,8 +422,8 @@ bool hcd_init(uint8_t rhport) {
 
   hcd_int_disable(rhport);
 
-  TU_LOG2_INT(sizeof(hcd_ep_t));
-  TU_LOG2_INT(sizeof(max2341_data_t));
+  TU_LOG2_INT(sizeof(max3421_ep_t));
+  TU_LOG2_INT(sizeof(max3421_data_t));
 
   tu_memclr(&_hcd_data, sizeof(_hcd_data));
 
@@ -478,22 +521,29 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) {
 //--------------------------------------------------------------------+
 
 // Open an endpoint
-bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) {
+bool hcd_edpt_open(uint8_t rhport, uint8_t daddr, tusb_desc_endpoint_t const * ep_desc) {
   (void) rhport;
-  (void) dev_addr;
+  (void) daddr;
 
   uint8_t ep_num = tu_edpt_number(ep_desc->bEndpointAddress);
   uint8_t ep_dir = tu_edpt_dir(ep_desc->bEndpointAddress);
 
-  hcd_ep_t * ep = &_hcd_data.ep[ep_num][ep_dir];
-
-  ep->packet_size = tu_edpt_packet_size(ep_desc);
-
-  if (ep_desc->bEndpointAddress == 0) {
-    _hcd_data.ep[0][1].packet_size = ep->packet_size;
+  max3421_ep_t * ep;
+  if (daddr == 0 && ep_num == 0) {
+    ep = &_hcd_data.ep0_addr0;
+  }else {
+    ep = allocate_ep();
+    TU_ASSERT(ep);
+    ep->daddr = daddr;
+    ep->ep_num = ep_num;
+    ep->ep_dir = ep_dir;
   }
 
-  ep->is_iso = (TUSB_XFER_ISOCHRONOUS == ep_desc->bmAttributes.xfer) ? 1 : 0;
+  if ( TUSB_XFER_ISOCHRONOUS == ep_desc->bmAttributes.xfer ) {
+    ep->is_iso = 1;
+  }
+
+  ep->packet_size = tu_edpt_packet_size(ep_desc);
 
   return true;
 }
@@ -505,50 +555,55 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buf
   uint8_t const ep_num = tu_edpt_number(ep_addr);
   uint8_t const ep_dir = tu_edpt_dir(ep_addr);
 
-  hcd_ep_t* ep = &_hcd_data.ep[ep_num][ep_dir];
+  max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir);
+  TU_ASSERT(ep);
 
   ep->buf = buffer;
   ep->total_len = buflen;
   ep->xferred_len = 0;
   ep->xfer_complete = 0;
-  ep->xfer_queued = 1;
+  ep->xfer_pending = 1;
 
-  peraddr_write(rhport, daddr, false);
+  // carry out transfer if not busy
+//  if ( atomic_flag_test_and_set(&_hcd_data.busy) )
+  {
+    peraddr_write(rhport, daddr, false);
 
-  uint8_t hctl = 0;
-  uint8_t hxfr = ep_num;
+    uint8_t hctl = 0;
+    uint8_t hxfr = ep_num;
 
-  if ( ep->is_iso ) {
-    hxfr |= HXFR_ISO;
-  } else if ( ep_num == 0 ) {
-    ep->data_toggle = 1;
-    if ( buffer == NULL || buflen == 0 ) {
-      // ZLP for ACK stage, use HS
-      hxfr |= HXFR_HS;
-      hxfr |= (ep_dir ? 0 : HXFR_OUT_NIN);
-      hxfr_write(rhport, hxfr, false);
-      return true;
+    if ( ep->is_iso ) {
+      hxfr |= HXFR_ISO;
+    } else if ( ep_num == 0 ) {
+      ep->data_toggle = 1;
+      if ( buffer == NULL || buflen == 0 ) {
+        // ZLP for ACK stage, use HS
+        hxfr |= HXFR_HS;
+        hxfr |= (ep_dir ? 0 : HXFR_OUT_NIN);
+        hxfr_write(rhport, hxfr, false);
+        return true;
+      }
     }
+
+    if ( 0 == ep_dir ) {
+      // Page 12: Programming BULK-OUT Transfers
+      TU_ASSERT(_hcd_data.hirq & HIRQ_SNDBAV_IRQ);
+
+      uint8_t const xact_len = (uint8_t) tu_min16(buflen, ep->packet_size);
+      fifo_write(rhport, SNDFIFO_ADDR, buffer, xact_len, false);
+      sndbc_write(rhport, xact_len, false);
+
+      hctl = (ep->data_toggle ? HCTL_SNDTOG1 : HCTL_SNDTOG0);
+      hxfr |= HXFR_OUT_NIN;
+    } else {
+      // Page 13: Programming BULK-IN Transfers
+      hctl = (ep->data_toggle ? HCTL_RCVTOG1 : HCTL_RCVTOG0);
+    }
+
+    reg_write(rhport, HCTL_ADDR, hctl, false);
+    hxfr_write(rhport, hxfr, false);
   }
 
-  if ( 0 == ep_dir ) {
-    // Page 12: Programming BULK-OUT Transfers
-    TU_ASSERT(_hcd_data.hirq & HIRQ_SNDBAV_IRQ);
-
-    uint8_t const xact_len = (uint8_t) tu_min16(buflen, ep->packet_size);
-    fifo_write(rhport, SNDFIFO_ADDR, buffer, xact_len, false);
-    sndbc_write(rhport, xact_len, false);
-
-    hctl = (ep->data_toggle ? HCTL_SNDTOG1 : HCTL_SNDTOG0);
-    hxfr |= HXFR_OUT_NIN;
-  } else {
-    // Page 13: Programming BULK-IN Transfers
-    hctl = (ep->data_toggle ? HCTL_RCVTOG1 : HCTL_RCVTOG0);
-  }
-
-  reg_write(rhport, HCTL_ADDR, hctl, false);
-  hxfr_write(rhport, hxfr, false);
-
   return true;
 }
 
@@ -566,7 +621,7 @@ bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
 bool hcd_setup_send(uint8_t rhport, uint8_t daddr, uint8_t const setup_packet[8]) {
   (void) rhport;
 
-  hcd_ep_t* ep = &_hcd_data.ep[0][0];
+  max3421_ep_t* ep = find_opened_ep(daddr, 0, 0);
   ep->total_len  = 8;
   ep->xferred_len = 0;
 
@@ -624,7 +679,7 @@ static void handle_xfer_done(uint8_t rhport) {
 
   if ( (hxfr_type & HXFR_SETUP) || (hxfr_type & HXFR_OUT_NIN) ) {
     // SETUP or OUT transfer
-    hcd_ep_t *ep = &_hcd_data.ep[ep_num][0];
+    max3421_ep_t *ep = find_opened_ep(_hcd_data.peraddr, ep_num, 0);
     uint8_t xact_len;
 
     if ( hxfr_type & HXFR_SETUP) {
@@ -644,10 +699,15 @@ static void handle_xfer_done(uint8_t rhport) {
       hcd_event_xfer_complete(_hcd_data.peraddr, ep_num, ep->xferred_len, xfer_result, true);
     }else {
       // more to transfer
+      xact_len = (uint8_t) tu_min16(ep->total_len - ep->xferred_len, ep->packet_size);
+      fifo_write(rhport, SNDFIFO_ADDR, ep->buf, xact_len, true);
+      sndbc_write(rhport, xact_len, true);
+
+      hxfr_write(rhport, _hcd_data.hxfr, true);
     }
   } else {
     // IN transfer: fifo data is already received in RCVDAV IRQ
-    hcd_ep_t *ep = &_hcd_data.ep[ep_num][1];
+    max3421_ep_t *ep = find_opened_ep(_hcd_data.peraddr, ep_num, 1);
 
     if ( hxfr_type & HXFR_HS ) {
       ep->xfer_complete = 1;
@@ -702,6 +762,10 @@ void hcd_int_handler(uint8_t rhport) {
     if (speed == TUSB_SPEED_INVALID) {
       hcd_event_device_remove(rhport, true);
     }else {
+      // FIXME multiple MAX3421 rootdevice address is not 1
+      uint8_t const daddr = 1;
+      free_ep(daddr);
+
       hcd_event_device_attach(rhport, true);
     }
   }
@@ -710,8 +774,8 @@ void hcd_int_handler(uint8_t rhport) {
   // not call this handler again. So we need to loop until all IRQ are cleared
   while ( hirq & (HIRQ_RCVDAV_IRQ | HIRQ_HXFRDN_IRQ) ) {
     if ( hirq & HIRQ_RCVDAV_IRQ ) {
-      uint8_t ep_num = _hcd_data.hxfr & HXFR_EPNUM_MASK;
-      hcd_ep_t *ep = &_hcd_data.ep[ep_num][1];
+      uint8_t const ep_num = _hcd_data.hxfr & HXFR_EPNUM_MASK;
+      max3421_ep_t *ep = find_opened_ep(_hcd_data.peraddr, ep_num, 1);
       uint8_t xact_len;
 
       // RCVDAV_IRQ can trigger 2 times (dual buffered)