فهرست منبع

portable: fomu: get msc to enumerate

Signed-off-by: Sean Cross <sean@xobs.io>
Sean Cross 6 سال پیش
والد
کامیت
32bb68409e
1فایلهای تغییر یافته به همراه95 افزوده شده و 65 حذف شده
  1. 95 65
      src/portable/foosn/fomu/dcd_fomu.c

+ 95 - 65
src/portable/foosn/fomu/dcd_fomu.c

@@ -41,105 +41,130 @@ void mputln(const char *str);
 // SIE Command
 //--------------------------------------------------------------------+
 
-static uint8_t volatile out_buffer_length[16];
-static uint8_t volatile * out_buffer[16];
-static uint8_t volatile out_buffer_max[16];
+#define EP_SIZE 64
+
+static uint16_t volatile rx_buffer_length[16];
+static uint8_t volatile * rx_buffer[16];
+static uint16_t volatile rx_buffer_max[16];
 
 static volatile bool tx_in_progress;
 static volatile uint8_t tx_ep;
 static volatile uint16_t tx_len;
+static uint8_t volatile * tx_buffer;
+static volatile uint16_t tx_offset;
 
 //--------------------------------------------------------------------+
 // PIPE HELPER
 //--------------------------------------------------------------------+
 
 static void finish_tx(void) {
-    // // Don't allow requeueing -- only queue more data if the system is idle.
-    // if (!(usb_in_status_read() & 2)) {
-    //     return;
-    // }
-
-    // Don't send empty data
-    if (!tx_in_progress) {
-        return;
-    }
+  // Don't send empty data
+  if (!tx_in_progress) {
+      return;
+  }
 
+  tx_offset += EP_SIZE;
+  if (tx_offset >= tx_len) {
     tx_in_progress = 0;
+    tx_buffer = NULL;
     dcd_event_xfer_complete(0, tx_ep, tx_len, XFER_RESULT_SUCCESS, true);
     return;
+  }
+
+  // Send more data
+  uint8_t added_bytes;
+  for (added_bytes = 0; (added_bytes < EP_SIZE) && (added_bytes + tx_offset < tx_len); added_bytes++) {
+    usb_in_data_write(tx_buffer[added_bytes + tx_offset]);
+  }
+
+  // Updating the epno queues the data
+  usb_in_ctrl_write(tu_edpt_number(tx_ep) & 0xf);
+  return;
 }
 
 static void process_rx(bool in_isr) {
     // If there isn't any data in the FIFO, don't do anything.
-    if (!(usb_out_status_read() & 1))
-        return;
+    if (!(usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)))
+      return;
+
+    uint8_t out_ep = (usb_out_status_read() >> CSR_USB_OUT_STATUS_EPNO_OFFSET) & 0xf;
+
+    // If the destination buffer doesn't exist, don't drain the hardware
+    // fifo.  Note that this can cause deadlocks if the host is waiting
+    // on some other endpoint's data!
+    if (rx_buffer[out_ep] == NULL)
+      return;
 
-    uint8_t out_ep = (usb_out_status_read() >> 2) & 0xf;
     uint32_t total_read = 0;
-    uint32_t current_offset = out_buffer_length[out_ep];
-    while (usb_out_status_read() & 1) {
+    uint32_t current_offset = rx_buffer_length[out_ep];
+    while (usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)) {
       uint8_t c = usb_out_data_read();
       total_read++;
-      if (out_buffer_length[out_ep] < out_buffer_max[out_ep])
-        out_buffer[out_ep][current_offset++] = c;
+      if (rx_buffer_length[out_ep] < rx_buffer_max[out_ep])
+        rx_buffer[out_ep][current_offset++] = c;
     }
 
     // Strip off the CRC16
-    total_read -= 2;
-    out_buffer_length[out_ep] += total_read;
-    if (out_buffer_length[out_ep] > out_buffer_max[out_ep])
-      out_buffer_length[out_ep] = out_buffer_max[out_ep];
-
-    if (out_buffer_max[out_ep] == out_buffer_length[out_ep]) {
-      out_buffer[out_ep] = NULL;
-      dcd_event_xfer_complete(0, tu_edpt_addr(out_ep, TUSB_DIR_OUT), out_buffer_length[out_ep], XFER_RESULT_SUCCESS, in_isr);
+    rx_buffer_length[out_ep] += (total_read - 2);
+    if (rx_buffer_length[out_ep] > rx_buffer_max[out_ep])
+      rx_buffer_length[out_ep] = rx_buffer_max[out_ep];
+
+    if (rx_buffer_max[out_ep] == rx_buffer_length[out_ep]) {
+      rx_buffer[out_ep] = NULL;
+      uint16_t len = rx_buffer_length[out_ep];
+      dcd_event_xfer_complete(0, tu_edpt_addr(out_ep, TUSB_DIR_OUT), len, XFER_RESULT_SUCCESS, in_isr);
     }
 
-    // Acknowledge having received the data
-    usb_out_ctrl_write(2);
+    // Acknowledge having received the data, and re-enable data reception
+    usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET);
 }
 
 //--------------------------------------------------------------------+
 // CONTROLLER API
 //--------------------------------------------------------------------+
 
+static void dcd_reset(void)
+{
+  usb_address_write(0);
+
+  // Reset all three FIFO handlers
+  usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_RESET_OFFSET);
+  usb_in_ctrl_write(1 << CSR_USB_IN_CTRL_RESET_OFFSET);
+  usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_RESET_OFFSET);
+
+  // Accept incoming data by default.
+  usb_out_ctrl_write(CSR_USB_OUT_CTRL_ENABLE_OFFSET);
+
+  memset(rx_buffer, 0, sizeof(rx_buffer));
+  tx_in_progress = 0;
+  tx_len = 0;
+  tx_buffer = NULL;
+  tx_offset = 0;
+
+  dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+}
+
 // Initializes the USB peripheral for device mode and enables it.
 void dcd_init(uint8_t rhport)
 {
   (void) rhport;
 
   usb_pullup_out_write(0);
-  usb_address_write(0);
-  usb_out_ctrl_write(0);
 
   usb_setup_ev_enable_write(0);
   usb_in_ev_enable_write(0);
   usb_out_ev_enable_write(0);
 
-  // Reset the IN handler
-  usb_in_ctrl_write(0x20);
-
-  // Reset the SETUP handler
-  usb_setup_ctrl_write(0x04);
-
-  // Reset the OUT handler
-  usb_out_ctrl_write(0x04);
-
   // Enable all event handlers and clear their contents
   usb_setup_ev_pending_write(usb_setup_ev_pending_read());
   usb_in_ev_pending_write(usb_in_ev_pending_read());
   usb_out_ev_pending_write(usb_out_ev_pending_read());
-  usb_setup_ev_enable_write(3);
   usb_in_ev_enable_write(1);
   usb_out_ev_enable_write(1);
-
-  // Accept incoming data by default.
-  usb_out_ctrl_write(2);
+  usb_setup_ev_enable_write(3);
 
   // Turn on the external pullup
   usb_pullup_out_write(1);
-
-  dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, false);
 }
 
 // Enables or disables the USB device interrupt(s). May be used to
@@ -222,36 +247,45 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t
 {
   (void)rhport;
 
+  // These sorts of transfers are handled in hardware
+  if ((tu_edpt_number(ep_addr) == 0) && (total_bytes == 0) && (buffer == NULL)) {
+    dcd_event_xfer_complete(0, ep_addr, total_bytes, XFER_RESULT_SUCCESS, false);
+
+    // An IN packet is sent to acknowledge an OUT token.  Re-enable OUT after this.
+    if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN)
+      usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET);
+    return true;
+  }
+
   if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
-    // These sorts of transfers are handled in hardware
-    if ((tu_edpt_number(ep_addr) == 0) && (total_bytes == 0) && (buffer == 0)) {
-      dcd_event_xfer_complete(0, ep_addr, total_bytes, XFER_RESULT_SUCCESS, false);
-      return true;
-    }
     uint32_t offset;
+
     // Wait for the tx pipe to free up
     while (tx_in_progress)
       ;
     tx_in_progress = 1;
     tx_ep = ep_addr;
     tx_len = total_bytes;
+    tx_buffer = buffer;
+    tx_offset = 0;
 
-    for (offset = 0; offset < total_bytes; offset++) {
+    for (offset = 0; (offset < EP_SIZE) && (offset < total_bytes); offset++) {
         usb_in_data_write(buffer[offset]);
     }
     // Updating the epno queues the data
     usb_in_ctrl_write(tu_edpt_number(ep_addr) & 0xf);
-    last_tx_buffer = buffer;
-    last_tx_bytes = total_bytes;
   }
   else if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) {
+    TU_ASSERT(rx_buffer[tu_edpt_number(ep_addr)] == NULL);
 
-    // Wait for the rx pipe to free up
-    while (out_buffer[tu_edpt_number(ep_addr)])
-      ;
-    out_buffer_max[tu_edpt_number(ep_addr)] = total_bytes;
-    out_buffer[tu_edpt_number(ep_addr)] = buffer;
-    out_buffer_length[tu_edpt_number(ep_addr)] = 0;
+    rx_buffer_length[tu_edpt_number(ep_addr)] = 0;
+    rx_buffer_max[tu_edpt_number(ep_addr)] = total_bytes;
+    rx_buffer[tu_edpt_number(ep_addr)] = buffer;
+
+    // If there's data in the buffer already, we'll try draining it
+    // into the current fifo immediately.  Note that since this
+    // bit is set, an interrupt won't fire again, so there is
+    // no need for a lock here.
     process_rx(false);
   }
   return true;
@@ -273,11 +307,7 @@ void hal_dcd_isr(uint8_t rhport)
   // This event means a bus reset occurred.  Reset everything, and
   // abandon any further processing.
   if (setup_pending & 2) {
-    usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_RESET_OFFSET);
-    usb_in_ctrl_write(1 << CSR_USB_IN_CTRL_RESET_OFFSET);
-    usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_RESET_OFFSET);
-
-    dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true);
+    dcd_reset();
     return;
   }