Răsfoiți Sursa

usb: Double-buffered OUT pipes.

Keir Fraser 5 ani în urmă
părinte
comite
22d988f87c
7 a modificat fișierele cu 139 adăugiri și 79 ștergeri
  1. 14 4
      inc/stm32f10x_regs.h
  2. 1 0
      inc/util.h
  3. 26 22
      src/floppy.c
  4. 6 4
      src/usb/cdc_acm.c
  5. 1 1
      src/usb/config.c
  6. 0 3
      src/usb/defs.h
  7. 91 45
      src/usb/hw_f1.c

+ 14 - 4
inc/stm32f10x_regs.h

@@ -739,10 +739,20 @@ struct usb {
 };
 
 struct usb_bufd {
-    uint32_t addr_tx;  /* 00: Transmission buffer address */
-    uint32_t count_tx; /* 04: Transmission byte count */
-    uint32_t addr_rx;  /* 08: Reception buffer address */
-    uint32_t count_rx; /* 0C: Reception byte count */
+    union {
+        struct {
+            uint32_t addr_tx;  /* 00: Transmission buffer address */
+            uint32_t count_tx; /* 04: Transmission byte count */
+            uint32_t addr_rx;  /* 08: Reception buffer address */
+            uint32_t count_rx; /* 0C: Reception byte count */
+        };
+        struct {
+            uint32_t addr_0;  /* 00: Double buffer #0 address */
+            uint32_t count_0; /* 04: Double buffer #0 byte count */
+            uint32_t addr_1;  /* 08: Double buffer #1 address */
+            uint32_t count_1; /* 0C: Double buffer #1 byte count */
+        };
+    };
 };
 
 #define USB_EPR_CTR_RX       (1u<<15)

+ 1 - 0
inc/util.h

@@ -122,6 +122,7 @@ int ep_rx_ready(uint8_t ep);
 void usb_read(uint8_t ep, void *buf, uint32_t len);
 bool_t ep_tx_ready(uint8_t ep);
 void usb_write(uint8_t ep, const void *buf, uint32_t len);
+#define USB_FS_MPS 64 /* Full Speed Max Packet Size */
 
 /* Floppy */
 void floppy_init(void);

+ 26 - 22
src/floppy.c

@@ -100,8 +100,9 @@ static enum {
     ST_write_flux_drain,
 } floppy_state = ST_inactive;
 
-#define FLOPPY_EP 2
-#define FLOPPY_MPS 64
+/* USB Endpoints for CDC ACM communications. */
+#define EP_RX 2
+#define EP_TX 3
 
 static uint8_t u_buf[8192];
 static uint32_t u_cons, u_prod;
@@ -361,9 +362,9 @@ static void auto_off_arm(void)
 static void floppy_end_command(void *ack, unsigned int ack_len)
 {
     auto_off_arm();
-    usb_write(FLOPPY_EP, ack, ack_len);
+    usb_write(EP_TX, ack, ack_len);
     u_cons = u_prod = 0;
-    if (ack_len == FLOPPY_MPS) {
+    if (ack_len == USB_FS_MPS) {
         ASSERT(floppy_state == ST_command_wait);
         floppy_state = ST_zlp;
     }
@@ -382,7 +383,7 @@ static struct {
     bool_t write_finished;
     unsigned int packet_len;
     unsigned int nr_samples;
-    uint8_t packet[FLOPPY_MPS];
+    uint8_t packet[USB_FS_MPS];
 } rw;
 
 static struct {
@@ -522,11 +523,11 @@ static void floppy_read(void)
         rdata_encode_flux();
         avail = (uint32_t)(u_prod - u_cons);
 
-    } else if ((avail < FLOPPY_MPS)
+    } else if ((avail < USB_FS_MPS)
                && !rw.packet_ready
-               && ep_tx_ready(FLOPPY_EP)) {
+               && ep_tx_ready(EP_TX)) {
 
-        memset(rw.packet, 0, FLOPPY_MPS);
+        memset(rw.packet, 0, USB_FS_MPS);
         make_read_packet(avail);
         floppy_state = ST_command_wait;
         floppy_end_command(rw.packet, avail+1);
@@ -538,18 +539,18 @@ static void floppy_read(void)
     if (avail > sizeof(u_buf)) {
         /* Overflow */
         printk("OVERFLOW %u %u %u %u\n", u_cons, u_prod,
-               rw.packet_ready, ep_tx_ready(FLOPPY_EP));
+               rw.packet_ready, ep_tx_ready(EP_TX));
         floppy_flux_end();
         rw.status = ACK_FLUX_OVERFLOW;
         floppy_state = ST_read_flux_drain;
         u_cons = u_prod = 0;
     }
 
-    if (!rw.packet_ready && (avail >= FLOPPY_MPS))
-        make_read_packet(FLOPPY_MPS);
+    if (!rw.packet_ready && (avail >= USB_FS_MPS))
+        make_read_packet(USB_FS_MPS);
 
-    if (rw.packet_ready && ep_tx_ready(FLOPPY_EP)) {
-        usb_write(FLOPPY_EP, rw.packet, FLOPPY_MPS);
+    if (rw.packet_ready && ep_tx_ready(EP_TX)) {
+        usb_write(EP_TX, rw.packet, USB_FS_MPS);
         rw.packet_ready = FALSE;
     }
 }
@@ -627,10 +628,10 @@ static void wdata_decode_flux(void)
 
 static void floppy_process_write_packet(void)
 {
-    int len = ep_rx_ready(FLOPPY_EP);
+    int len = ep_rx_ready(EP_RX);
 
     if ((len >= 0) && !rw.packet_ready) {
-        usb_read(FLOPPY_EP, rw.packet, len);
+        usb_read(EP_RX, rw.packet, len);
         rw.packet_ready = TRUE;
         rw.packet_len = len;
     }
@@ -734,7 +735,7 @@ static void floppy_write_check_underflow(void)
 
         /* Underflow */
         printk("UNDERFLOW %u %u %u %u\n", u_cons, u_prod,
-               rw.packet_ready, ep_rx_ready(FLOPPY_EP));
+               rw.packet_ready, ep_rx_ready(EP_RX));
         floppy_flux_end();
         rw.status = ACK_FLUX_UNDERFLOW;
         floppy_state = ST_write_flux_drain;
@@ -780,7 +781,7 @@ static void floppy_write_drain(void)
     }
 
     /* Wait for space to write ACK packet. */
-    if (!ep_tx_ready(FLOPPY_EP))
+    if (!ep_tx_ready(EP_TX))
         return;
 
     /* ACK with Status byte. */
@@ -892,27 +893,30 @@ void floppy_process(void)
         floppy_motor(FALSE);
         drive_deselect();
         auto_off.armed = FALSE;
+        gpio_write_pin(gpioa, 0, LOW);
+        delay_ms(100); /* XXX */
+        gpio_write_pin(gpioa, 0, HIGH);
     }
 
     switch (floppy_state) {
 
     case ST_command_wait:
 
-        len = ep_rx_ready(FLOPPY_EP);
+        len = ep_rx_ready(EP_RX);
         if ((len >= 0) && (len < (sizeof(u_buf)-u_prod))) {
-            usb_read(FLOPPY_EP, &u_buf[u_prod], len);
+            usb_read(EP_RX, &u_buf[u_prod], len);
             u_prod += len;
         }
 
-        if ((u_prod >= 2) && (u_prod >= u_buf[1]) && ep_tx_ready(FLOPPY_EP)) {
+        if ((u_prod >= 2) && (u_prod >= u_buf[1]) && ep_tx_ready(EP_TX)) {
             process_command();
         }
 
         break;
 
     case ST_zlp:
-        if (ep_tx_ready(FLOPPY_EP)) {
-            usb_write(FLOPPY_EP, NULL, 0);
+        if (ep_tx_ready(EP_TX)) {
+            usb_write(EP_TX, NULL, 0);
             floppy_state = ST_command_wait;
         }
         break;

+ 6 - 4
src/usb/cdc_acm.c

@@ -92,10 +92,12 @@ bool_t cdc_acm_handle_class_request(void)
 
 bool_t cdc_acm_set_configuration(void)
 {
-    /* Set up endpoints. */
-    usb_configure_ep(0x81, 0,          0); /* Notification Element (D->H) */
-    usb_configure_ep(0x02, 0, USB_FS_MPS); /* Bulk Pipe (H->D) */
-    usb_configure_ep(0x82, 0, USB_FS_MPS); /* Bulk Pipe (D->H) */
+    /* Notification Element (D->H) */
+    usb_configure_ep(0x81, USB_EP_TYPE_INTERRUPT, 0);
+    /* Bulk Pipe (H->D) */
+    usb_configure_ep(0x02, USB_EP_TYPE_BULK, 2*USB_FS_MPS);
+    /* Bulk Pipe (D->H) */
+    usb_configure_ep(0x83, USB_EP_TYPE_BULK, USB_FS_MPS);
 
     floppy_configured();
 

+ 1 - 1
src/usb/config.c

@@ -90,7 +90,7 @@ const uint8_t config_descriptor[] __aligned(2) = {
 /* Data IN Endpoint descriptor */
     0x07, /* 0 bLength */
     DESC_ENDPOINT, /* 1 bDescriptorType */
-    0x82, /* 2 bEndpointAddress */
+    0x83, /* 2 bEndpointAddress */
     0x02, /* 3 bmAttributes */
     0x40, /* 4 wMaxPacketSize - Low byte */
     0x00, /* 5 wMaxPacketSize - High byte */

+ 0 - 3
src/usb/defs.h

@@ -9,9 +9,6 @@
  * See the file COPYING for more details, or visit <http://unlicense.org>.
  */
 
-/* Full Speed Max Packet Size */
-#define USB_FS_MPS 64
-
 /* bRequest: Standard Request Codes */
 #define GET_STATUS          0
 #define CLEAR_FEATURE       1

+ 91 - 45
src/usb/hw_f1.c

@@ -11,11 +11,15 @@
 
 static uint16_t buf_end;
 
-/* We track which endpoints have been marked CTR (Correct TRansfer).
- * On receive side, checking EPR_STAT_RX == NAK races update of the
- * Buffer Descriptor's COUNT_RX by the hardware. */
-static bool_t _ep_rx_ready[8];
-static bool_t _ep_tx_ready[8];
+static struct {
+    /* We track which endpoints have been marked CTR (Correct TRansfer). On 
+     * receive side, checking EPR_STAT_RX == NAK races update of the Buffer 
+     * Descriptor's COUNT_RX by the hardware. */
+    bool_t rx_ready;
+    bool_t tx_ready;
+    /* Is this a double-buffered BULK endpoint? */
+    bool_t dbl_buf;
+} eps[8];
 
 void usb_init(void)
 {
@@ -60,33 +64,60 @@ static void dump_ep(uint8_t ep)
 
 int ep_rx_ready(uint8_t ep)
 {
-    return _ep_rx_ready[ep] ? usb_bufd[ep].count_rx & 0x3ff : -1;
+    uint16_t count, epr;
+    volatile struct usb_bufd *bd;
+
+    if (!eps[ep].rx_ready)
+        return -1;
+
+    bd = &usb_bufd[ep];
+    epr = usb->epr[ep];
+
+    count = !(epr & USB_EPR_EP_KIND_DBL_BUF) ? bd->count_rx
+        : (epr & 0x0040) ? bd->count_1 : bd->count_0;
+
+    return count & 0x3ff;
 }
 
 bool_t ep_tx_ready(uint8_t ep)
 {
-    return _ep_tx_ready[ep];
+    return eps[ep].tx_ready;
 }
 
 void usb_read(uint8_t ep, void *buf, uint32_t len)
 {
-    unsigned int i, base = (uint16_t)usb_bufd[ep].addr_rx >> 1;
-    uint16_t epr, *p = buf;
+    unsigned int i, base;
+    uint16_t epr = usb->epr[ep], *p = buf;
+    volatile struct usb_bufd *bd = &usb_bufd[ep];
+
+    if (epr & USB_EPR_EP_KIND_DBL_BUF) {
+        base = (epr & 0x0040) ? bd->addr_1 : bd->addr_0;
+        /* If HW is pointing at same buffer as us, we have to process both 
+         * buffers, and should defer clearing rx_ready until we've done so. */
+        if ((epr ^ (epr>>8)) & 0x40)
+            eps[ep].rx_ready = FALSE;
+    } else {
+        base = bd->addr_rx;
+        eps[ep].rx_ready = FALSE;
+    }
+    base = (uint16_t)base >> 1;
 
     for (i = 0; i < len/2; i++)
         *p++ = usb_buf[base + i];
     if (len&1)
         *(uint8_t *)p = usb_buf[base + i];
 
-    /* Clear CTR_RX and set status NAK->VALID. */
-    epr = usb->epr[ep];
-    epr &= 0x370f;
-    epr |= 0x0080;
-    epr ^= USB_EPR_STAT_RX(USB_STAT_VALID);
+    if (epr & USB_EPR_EP_KIND_DBL_BUF) {
+        /* Toggle SW_BUF. Status remains VALID at all times. */
+        epr &= 0x070f; /* preserve rw & t fields */
+        epr |= 0x80c0; /* preserve rc_w0 fields, toggle SW_BUF */
+    } else {
+        /* Set status NAK->VALID. */
+        epr &= 0x370f; /* preserve rw & t fields (except STAT_RX) */
+        epr |= 0x8080; /* preserve rc_w0 fields */
+        epr ^= USB_EPR_STAT_RX(USB_STAT_VALID); /* modify STAT_RX */
+    }
     usb->epr[ep] = epr;
-
-    /* Await next CTR_RX notification. */
-    _ep_rx_ready[ep] = FALSE;
 }
 
 void usb_write(uint8_t ep, const void *buf, uint32_t len)
@@ -110,7 +141,7 @@ void usb_write(uint8_t ep, const void *buf, uint32_t len)
     usb->epr[ep] = epr;
 
     /* Await next CTR_TX notification. */
-    _ep_tx_ready[ep] = FALSE;
+    eps[ep].tx_ready = FALSE;
 }
 
 static void usb_write_ep0(void)
@@ -147,10 +178,12 @@ static void usb_stall(uint8_t ep)
 void usb_configure_ep(uint8_t ep, uint8_t type, uint32_t size)
 {
     uint16_t old_epr, new_epr;
-    bool_t in;
+    bool_t in, dbl_buf;
+    volatile struct usb_bufd *bd;
 
     in = !!(ep & 0x80);
     ep &= 0x7f;
+    bd = &usb_bufd[ep];
 
     old_epr = usb->epr[ep];
 
@@ -158,24 +191,40 @@ void usb_configure_ep(uint8_t ep, uint8_t type, uint32_t size)
      * Clears: CTR_RX and CTR_TX. */
     new_epr = USB_EPR_EP_TYPE(type) | USB_EPR_EA(ep);
 
+    dbl_buf = (size > USB_FS_MPS);
+    if (dbl_buf) {
+        ASSERT(type == USB_EP_TYPE_BULK);
+        ASSERT(size == 2*USB_FS_MPS);
+        new_epr |= USB_EPR_EP_KIND_DBL_BUF;
+    }
+
     if (in || (ep == 0)) {
-        usb_bufd[ep].addr_tx = buf_end;
+        bd->addr_tx = buf_end;
         buf_end += size;
-        usb_bufd[ep].count_tx = 0;
+        bd->count_tx = 0;
         /* TX: Clears data toggle and sets status to NAK. */
         new_epr |= (old_epr & 0x0070) ^ USB_EPR_STAT_TX(USB_STAT_NAK);
         /* IN Endpoint is immediately ready to transmit. */
-        _ep_tx_ready[ep] = TRUE;
+        eps[ep].tx_ready = TRUE;
     }
 
     if (!in) {
-        usb_bufd[ep].addr_rx = buf_end;
-        buf_end += size;
-        usb_bufd[ep].count_rx = 0x8400; /* USB_FS_MPS = 64 bytes */
+        if (dbl_buf) {
+            bd->addr_0 = buf_end;
+            bd->addr_1 = buf_end + size/2;
+            bd->count_0 = bd->count_1 = 0x8400; /* USB_FS_MPS = 64 bytes */
+            buf_end += size;
+            /* RX: Clears SW_BUF. */
+            new_epr |= old_epr & 0x0040;
+        } else {
+            bd->addr_rx = buf_end;
+            buf_end += size;
+            bd->count_rx = 0x8400; /* USB_FS_MPS = 64 bytes */
+        }
         /* RX: Clears data toggle and sets status to VALID. */
         new_epr |= (old_epr & 0x7000) ^ USB_EPR_STAT_RX(USB_STAT_VALID);
         /* OUT Endpoint must wait for a packet from the Host. */
-        _ep_rx_ready[ep] = FALSE;
+        eps[ep].rx_ready = FALSE;
     }
 
     usb->epr[ep] = new_epr;
@@ -186,9 +235,8 @@ static void handle_reset(void)
     /* Reinitialise floppy subsystem. */
     floppy_reset();
 
-    /* All Endpoints in invalid Tx/Rx state. */
-    memset(_ep_rx_ready, 0, sizeof(_ep_rx_ready));
-    memset(_ep_tx_ready, 0, sizeof(_ep_tx_ready));
+    /* Clear endpoint soft state. */
+    memset(eps, 0, sizeof(eps));
 
     /* Clear any in-progress Control Transfer. */
     ep0.data_len = -1;
@@ -202,24 +250,28 @@ static void handle_reset(void)
     usb->istr &= ~USB_ISTR_RESET;
 }
 
+static void clear_ctr(uint8_t ep, uint16_t ctr)
+{
+    uint16_t epr = usb->epr[ep];
+    epr &= 0x070f; /* preserve rw & t fields */
+    epr |= 0x8080; /* preserve rc_w0 fields */
+    epr &= ~ctr;   /* clear specified rc_w0 field */
+    usb->epr[ep] = epr;
+}
+
 static void handle_rx_transfer(uint8_t ep)
 {
-    uint16_t epr;
+    uint16_t epr = usb->epr[ep];
     bool_t ready;
 
-    /* Clear CTR_RX. */
-    epr = usb->epr[ep];
-    epr &= 0x070f;
-    epr |= 0x0080;
-    usb->epr[ep] = epr;
-    _ep_rx_ready[ep] = TRUE;
+    clear_ctr(ep, USB_EPR_CTR_RX);
+    eps[ep].rx_ready = TRUE;
 
     /* We only handle Control Transfers here (endpoint 0). */
     if (ep != 0)
         return;
 
     ready = FALSE;
-    epr = usb->epr[ep];
 
     if (epr & USB_EPR_SETUP) {
 
@@ -291,14 +343,8 @@ static void handle_rx_transfer(uint8_t ep)
 
 static void handle_tx_transfer(uint8_t ep)
 {
-    uint16_t epr;
-
-    /* Clear CTR_TX. */
-    epr = usb->epr[ep];
-    epr &= 0x070f;
-    epr |= 0x8000;
-    usb->epr[ep] = epr;
-    _ep_tx_ready[ep] = TRUE;
+    clear_ctr(ep, USB_EPR_CTR_TX);
+    eps[ep].tx_ready = TRUE;
 
     /* We only handle Control Transfers here (endpoint 0). */
     if (ep != 0)