Răsfoiți Sursa

fomu: commit latest version

Signed-off-by: Sean Cross <sean@xobs.io>
Sean Cross 6 ani în urmă
părinte
comite
843136d0e4
1 a modificat fișierele cu 182 adăugiri și 118 ștergeri
  1. 182 118
      src/portable/foosn/fomu/dcd_fomu.c

+ 182 - 118
src/portable/foosn/fomu/dcd_fomu.c

@@ -48,19 +48,49 @@ uint8_t volatile * rx_buffer[16];
 uint16_t volatile rx_buffer_max[16];
 
 volatile uint8_t tx_ep;
-volatile uint16_t tx_len;
-uint8_t volatile * tx_buffer;
-volatile uint16_t tx_offset;
+volatile uint16_t tx_buffer_offset[16];
+uint8_t volatile * tx_buffer[16];
+volatile uint16_t tx_buffer_max[16];
 volatile uint8_t reset_count;
 
+__attribute__((used)) uint8_t volatile * last_tx_buffer;
+__attribute__((used)) volatile uint8_t last_tx_ep;
+
+uint8_t setup_packet_bfr[10];
+
 //--------------------------------------------------------------------+
 // PIPE HELPER
 //--------------------------------------------------------------------+
 
-static void finish_tx(void) {
-  // Ignore "ACK" packets where there was no data to send.
-  if (!tx_buffer) {
-      return;
+static bool advance_tx_ep(void) {
+  // Move on to the next transmit buffer in a round-robin manner
+  uint8_t prev_tx_ep = tx_ep;
+  for (tx_ep = (tx_ep + 1) & 0xf; tx_ep != prev_tx_ep; tx_ep = ((tx_ep + 1) & 0xf)) {
+    if (tx_buffer[tx_ep])
+      return true;
+  }
+  if (!tx_buffer[tx_ep])
+    return false;
+  return true;
+}
+
+static void tx_more_data(void) {
+  // Send more data
+  uint8_t added_bytes;
+  for (added_bytes = 0; (added_bytes < EP_SIZE) && (added_bytes + tx_buffer_offset[tx_ep] < tx_buffer_max[tx_ep]); added_bytes++) {
+    usb_in_data_write(tx_buffer[tx_ep][added_bytes + tx_buffer_offset[tx_ep]]);
+  }
+
+  // Updating the epno queues the data
+  usb_in_ctrl_write(tx_ep & 0xf);
+}
+
+static void process_tx(bool in_isr) {
+  // If the buffer is now empty, search for the next buffer to fill.
+  if (!tx_buffer[tx_ep]) {
+    if (advance_tx_ep())
+      tx_more_data();
+    return;
   }
 
   // If the system isn't idle, then something is very wrong.
@@ -68,62 +98,78 @@ static void finish_tx(void) {
   if (!(in_status & (1 << CSR_USB_IN_STATUS_IDLE_OFFSET)))
     fomu_error(__LINE__);
 
-  tx_offset += EP_SIZE;
-  if (tx_offset >= tx_len) {
-    dcd_event_xfer_complete(0, tu_edpt_addr(tx_ep, TUSB_DIR_IN), tx_len, XFER_RESULT_SUCCESS, true);
-    tx_buffer = NULL;
-    return;
-  }
+  tx_buffer_offset[tx_ep] += EP_SIZE;
 
-  // 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]);
+  if (tx_buffer_offset[tx_ep] >= tx_buffer_max[tx_ep]) {
+    last_tx_buffer = tx_buffer[tx_ep];
+    last_tx_ep = tx_ep;
+    tx_buffer[tx_ep] = NULL;
+
+    dcd_event_xfer_complete(0, tu_edpt_addr(tx_ep, TUSB_DIR_IN), tx_buffer_max[tx_ep], XFER_RESULT_SUCCESS, in_isr);
+    if (!advance_tx_ep())
+      return;
   }
 
-  // Updating the epno queues the data
-  usb_in_ctrl_write(tx_ep & 0xf);
+  tx_more_data();
   return;
 }
 
 static void process_rx(bool in_isr) {
-    // If the OUT handler is still waiting to send, don't do anything.
-    uint8_t out_status = usb_out_status_read();
-    if (!(out_status & (1 << CSR_USB_OUT_STATUS_IDLE_OFFSET)))
-      return;
+  // If the OUT handler is still waiting to send, don't do anything.
+  uint8_t out_status = usb_out_status_read();
+  if (!(out_status & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)))
+    fomu_error(__LINE__);
+    // return;
 
-    uint8_t rx_ep = (out_status >> CSR_USB_OUT_STATUS_EPNO_OFFSET) & 0xf;
+  uint8_t rx_ep = (out_status >> 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[rx_ep] == NULL)
-      return;
+  // 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[rx_ep] == NULL) {
+    fomu_error(__LINE__);
+    return;
+  }
+
+  uint32_t total_read = 0;
+  uint32_t current_offset = rx_buffer_offset[rx_ep];
+  if (current_offset > rx_buffer_max[rx_ep])
+    fomu_error(__LINE__);
+  while (usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)) {
+    uint8_t c = usb_out_data_read();
+    total_read++;
+    if ((rx_buffer_offset[rx_ep] + current_offset) < rx_buffer_max[rx_ep])
+      rx_buffer[rx_ep][current_offset++] = c;
+  }
+  if (total_read > 66)
+    fomu_error(__LINE__);
 
-    uint32_t total_read = 0;
-    uint32_t current_offset = rx_buffer_offset[rx_ep];
-    if (current_offset > rx_buffer_max[rx_ep])
+  // Strip off the CRC16
+  rx_buffer_offset[rx_ep] += (total_read - 2);
+  if (rx_buffer_offset[rx_ep] > rx_buffer_max[rx_ep])
+    rx_buffer_offset[rx_ep] = rx_buffer_max[rx_ep];
+
+  if (rx_buffer_max[rx_ep] == rx_buffer_offset[rx_ep]) {
+    if (rx_buffer[rx_ep] == NULL)
       fomu_error(__LINE__);
-    while (usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET)) {
-      uint8_t c = usb_out_data_read();
-      total_read++;
-      if ((rx_buffer_offset[rx_ep] + current_offset) < rx_buffer_max[rx_ep])
-        rx_buffer[rx_ep][current_offset++] = c;
-    }
 
-    // Strip off the CRC16
-    rx_buffer_offset[rx_ep] += (total_read - 2);
-    if (rx_buffer_offset[rx_ep] > rx_buffer_max[rx_ep])
-      rx_buffer_offset[rx_ep] = rx_buffer_max[rx_ep];
+    // Disable this endpoint (causing it to respond NAK) until we have
+    // a buffer to place the data into.
+    rx_buffer[rx_ep] = NULL;
+    uint16_t len = rx_buffer_offset[rx_ep];
 
-    if (rx_buffer_max[rx_ep] == rx_buffer_offset[rx_ep]) {
-      rx_buffer[rx_ep] = NULL;
-      uint16_t len = rx_buffer_offset[rx_ep];
-      dcd_event_xfer_complete(0, tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len, XFER_RESULT_SUCCESS, in_isr);
-    }
+    // uint16_t ep_en_mask = usb_out_enable_status_read();
+    // int i;
+    // for (i = 0; i < 16; i++) {
+    //   if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i])))
+    //     fomu_error(__LINE__);
+    // }
+    dcd_event_xfer_complete(0, tu_edpt_addr(rx_ep, TUSB_DIR_OUT), len, XFER_RESULT_SUCCESS, in_isr);
+    return;
+  }
 
-    // Acknowledge having received the data, and re-enable data reception
-    usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET);
+  // If there's more data, re-enable data reception
+  usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | rx_ep);
 }
 
 //--------------------------------------------------------------------+
@@ -147,18 +193,16 @@ static void dcd_reset(void)
   memset((void *)rx_buffer, 0, sizeof(rx_buffer));
   memset((void *)rx_buffer_max, 0, sizeof(rx_buffer_max));
   memset((void *)rx_buffer_offset, 0, sizeof(rx_buffer_offset));
-  tx_len = 0;
-  tx_buffer = NULL;
-  tx_offset = 0;
-  tx_ep = 0;
 
-  // Accept incoming data by default.
-  usb_out_ctrl_write(1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET);
+  memset((void *)tx_buffer, 0, sizeof(tx_buffer));
+  memset((void *)tx_buffer_max, 0, sizeof(tx_buffer_max));
+  memset((void *)tx_buffer_offset, 0, sizeof(tx_buffer_offset));
+  tx_ep = 0;
 
   // 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_pending_write(-1);
+  usb_in_ev_pending_write(-1);
+  usb_out_ev_pending_write(-1);
   usb_in_ev_enable_write(1);
   usb_out_ev_enable_write(1);
   usb_setup_ev_enable_write(3);
@@ -203,12 +247,15 @@ void dcd_int_disable(uint8_t rhport)
 // Called when the device is given a new bus address.
 void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
 {
-  (void)rhport;
-  // Set address and then acknowledge the SETUP packet
-  usb_address_write(dev_addr);
+  // Respond with ACK status first before changing device address
+  dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
 
-  // ACK the transfer (sets the address)
-  usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_HANDLED_OFFSET);
+  // Wait for the response packet to get sent
+  while (tx_buffer[0] != NULL)
+    ;
+
+  // Activate the new address
+  usb_address_write(dev_addr);
 }
 
 // Called when the device received SET_CONFIG request, you can leave this
@@ -243,24 +290,36 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
     rx_buffer[ep_num] = NULL;
   }
 
+  else if (ep_dir == TUSB_DIR_OUT) {
+    tx_buffer_offset[ep_num] = 0;
+    tx_buffer_max[ep_num] = 0;
+    tx_buffer[ep_num] = NULL;
+  }
+
   return true;
 }
 
 void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
 {
   (void) rhport;
+  if (tu_edpt_number(ep_addr) == 2)
+    fomu_error(__LINE__);
+
   if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT)
-    usb_out_stall_write((1 << CSR_USB_OUT_STALL_STALL_OFFSET) | tu_edpt_number(ep_addr));
-  else {
+    usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_STALL_OFFSET) | (1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | tu_edpt_number(ep_addr));
+  else
     usb_in_ctrl_write((1 << CSR_USB_IN_CTRL_STALL_OFFSET) | tu_edpt_number(ep_addr));
-  }
 }
 
 void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
 {
   (void) rhport;
-  if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT)
-    usb_out_stall_write((0 << CSR_USB_OUT_STALL_STALL_OFFSET) | tu_edpt_number(ep_addr));
+  if (tu_edpt_dir(ep_addr) == TUSB_DIR_OUT) {
+    uint8_t enable = 0;
+    if (rx_buffer[ep_addr])
+      enable = 1;
+    usb_out_ctrl_write((0 << CSR_USB_OUT_CTRL_STALL_OFFSET) | (enable << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | tu_edpt_number(ep_addr));
+  }
   // IN endpoints will get unstalled when more data is written.
 }
 
@@ -269,62 +328,64 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t t
   (void)rhport;
   uint8_t ep_num = tu_edpt_number(ep_addr);
   uint8_t ep_dir = tu_edpt_dir(ep_addr);
-  TU_ASSERT(tx_ep < 16);
-
-  // These sorts of transfers are handled in hardware automatically, so simply inform
-  // the core that the transfer was processed.
-  if ((ep_num == 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 (ep_dir == TUSB_DIR_IN) {
-    //   usb_out_ev_enable_write(0);
-    //   process_rx(false);
-    //   usb_out_ev_enable_write(1);
-    // }
-    return true;
+  TU_ASSERT(ep_num < 16);
+
+  // Give a nonzero buffer when we transmit 0 bytes, so that the
+  // system doesn't think the endpoint is idle.
+  if ((buffer == NULL) && (total_bytes == 0)) {
+    buffer = (uint8_t *)0xffffffff;
   }
 
-  TU_ASSERT(((uint32_t)buffer) >= 0x10000000);
-  TU_ASSERT(((uint32_t)buffer) <= 0x10020000);
+  TU_ASSERT(buffer != NULL);
 
   if (ep_dir == TUSB_DIR_IN) {
-    uint32_t offset;
-
     // Wait for the tx pipe to free up
     uint8_t previous_reset_count = reset_count;
-    while (!((tx_buffer == NULL)
-          && (usb_in_status_read() & (1 << CSR_USB_IN_STATUS_IDLE_OFFSET))
-          && !(usb_in_status_read() & (1 << CSR_USB_IN_STATUS_HAVE_OFFSET))))
+    // Continue until the buffer is empty, the system is idle, and the fifo is empty.
+    while (tx_buffer[ep_num] != NULL)
       ;
+
     // If a reset happens while we're waiting, abort the transfer
     if (previous_reset_count != reset_count)
       return true;
 
-    tx_ep = ep_num;
-    tx_len = total_bytes;
-    tx_offset = 0;
-    tx_buffer = buffer;
-
-    for (offset = 0; (offset < EP_SIZE) && (offset < total_bytes); offset++) {
-      usb_in_data_write(buffer[offset]);
+    dcd_int_disable(0);
+    TU_ASSERT(tx_buffer[ep_num] == NULL);
+    tx_buffer_offset[ep_num] = 0;
+    tx_buffer_max[ep_num] = total_bytes;
+    tx_buffer[ep_num] = buffer;
+
+    // If the current buffer is NULL, then that means the tx logic is idle.
+    // Update the tx_ep to point to our endpoint number and queue the data.
+    // Otherwise, let it be and it'll get picked up after the next transfer
+    // finishes.
+    if ((tx_buffer[tx_ep] == NULL) || (tx_ep == ep_num)) {
+      tx_ep = ep_num;
+      tx_more_data();
     }
-    // Updating the epno queues the data
-    usb_in_ctrl_write(ep_num & 0xf);
+    dcd_int_enable(0);
   }
   else if (ep_dir == TUSB_DIR_OUT) {
-    TU_ASSERT(rx_buffer[ep_num] == NULL);
+    while (rx_buffer[ep_num] != NULL)
+      ;
 
     rx_buffer_offset[ep_num] = 0;
     rx_buffer_max[ep_num] = total_bytes;
-    rx_buffer[ep_num] = buffer;
 
-    // If there's data in the buffer already, we'll try draining it
-    // into the current fifo immediately.
-    usb_out_ev_enable_write(0);
-    if (usb_out_status_read() & (1 << CSR_USB_OUT_STATUS_HAVE_OFFSET))
-      process_rx(false);
-    usb_out_ev_enable_write(1);
+    dcd_int_disable(0);
+    rx_buffer[ep_num] = buffer;
+    usb_out_ctrl_write((1 << CSR_USB_OUT_CTRL_ENABLE_OFFSET) | ep_num);
+
+    // uint16_t ep_en_mask = usb_out_enable_status_read();
+    // int i;
+    // for (i = 0; i < 16; i++) {
+    //   if ((!!(ep_en_mask & (1 << i))) ^ (!!(rx_buffer[i]))) {
+    //     if (rx_buffer[i] && usb_out_ev_pending_read() && (usb_out_status_read() & 0xf) == i)
+    //       continue;
+    //     fomu_error(__LINE__);
+    //   }
+    // }
+    dcd_int_enable(0);
   }
   return true;
 }
@@ -339,8 +400,6 @@ void hal_dcd_isr(uint8_t rhport)
   uint8_t in_pending      = usb_in_ev_pending_read();
   uint8_t out_pending     = usb_out_ev_pending_read();
   usb_setup_ev_pending_write(setup_pending);
-  usb_in_ev_pending_write(in_pending);
-  usb_out_ev_pending_write(out_pending);
 
   // This event means a bus reset occurred.  Reset everything, and
   // abandon any further processing.
@@ -352,7 +411,10 @@ void hal_dcd_isr(uint8_t rhport)
   // An "OUT" transaction just completed so we have new data.
   // (But only if we can accept the data)
   // if (out_pending) {
-  if (usb_out_ev_enable_read() && out_pending) {
+  if (out_pending) {
+    if (!usb_out_ev_enable_read())
+      fomu_error(__LINE__);
+    usb_out_ev_pending_write(out_pending);
     process_rx(true);
   }
 
@@ -364,35 +426,37 @@ void hal_dcd_isr(uint8_t rhport)
   // to begin with, then make a subsequent request.  If we don't handle
   // the IN packets first, then the second request will be truncated.
   if (in_pending) {
-    finish_tx();
+    if (!usb_in_ev_enable_read())
+      fomu_error(__LINE__);
+    usb_in_ev_pending_write(in_pending);
+    process_tx(true);
   } 
 
   // We got a SETUP packet.  Copy it to the setup buffer and clear
   // the "pending" bit.
   if (setup_pending & 1) {
     // Setup packets are always 8 bytes, plus two bytes of crc16.
-    uint8_t setup_packet[10];
     uint32_t setup_length = 0;
 
-    if (!(usb_setup_status_read() & 1))
+    if (!(usb_setup_status_read() & (1 << CSR_USB_SETUP_STATUS_HAVE_OFFSET)))
       fomu_error(__LINE__);
 
-    while (usb_setup_status_read() & 1) {
+    while (usb_setup_status_read() & (1 << CSR_USB_SETUP_STATUS_HAVE_OFFSET)) {
       uint8_t c = usb_setup_data_read();
-      if (setup_length < sizeof(setup_packet))
-        setup_packet[setup_length] = c;
+      if (setup_length < sizeof(setup_packet_bfr))
+        setup_packet_bfr[setup_length] = c;
       setup_length++;
     }
 
     // If we have 10 bytes, that's a full SETUP packet plus CRC16.
     // Otherwise, it was an RX error.
     if (setup_length == 10) {
-      dcd_event_setup_received(rhport, setup_packet, true);
+      dcd_event_setup_received(rhport, setup_packet_bfr, true);
       // Acknowledge the packet, so long as it isn't a SET_ADDRESS
       // packet.  If it is, leave it unacknowledged and we'll do this
       // in the `dcd_set_address` function instead.
-      if (!((setup_packet[0] == 0x00) && (setup_packet[1] == 0x05)))
-        usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_HANDLED_OFFSET);
+      // if (!((setup_packet_bfr[0] == 0x00) && (setup_packet_bfr[1] == 0x05)))
+      usb_setup_ctrl_write(1 << CSR_USB_SETUP_CTRL_ACK_OFFSET);
     }
     else {
       fomu_error(__LINE__);