Selaa lähdekoodia

floppy: Small read-track fixes:
1. Do not race with the Index ISR (disable IRQs for the critical bit)
2. Fix an incorrect buffer-ring mask (used the wrong mask)
3. When we empty the USB ring on overflow, set avail=0 too
4. Take care not to write past the end of the revs[] array.

Keir Fraser 5 vuotta sitten
vanhempi
commit
d8ab27a3e3
1 muutettua tiedostoa jossa 37 lisäystä ja 16 poistoa
  1. 37 16
      src/floppy.c

+ 37 - 16
src/floppy.c

@@ -356,19 +356,36 @@ static bool_t rdata_encode_flux(void)
     uint16_t cons, prod, prev = dma.prev_sample, curr, next;
     unsigned int nr_samples;
 
+    ASSERT(rw.rev < rw.nr_revs);
+
+    /* We don't want to race the Index IRQ handler. */
+    IRQ_global_disable();
+
     /* Find out where the DMA engine's producer index has got to. */
     prod = ARRAY_SIZE(dma.buf) - dma_rdata.cndtr;
     nr_samples = (prod - dma.cons) & buf_mask;
 
     if (rw.rev != index.count) {
-        unsigned int final_samples = U_MASK(index.read_prod - dma.cons);
+        /* We have just passed the index mark: Record information about 
+         * the just-completed revolution. */
+        unsigned int final_samples = (index.read_prod - dma.cons) & buf_mask;
+        if (final_samples > nr_samples) {
+            /* Only way this can happen is if the producer has overflowed 
+             * past the consumer since the Index IRQ. We will report it 
+             * back to the caller by setting an overflow on the USB ring. */
+            printk("DMA OVERFLOW\n");
+            u_prod = u_cons + sizeof(u_buf) + 1;
+            final_samples = nr_samples;
+        }
         read_info[rw.rev].time = sysclk_stk(index.duration);
         read_info[rw.rev].samples = rw.nr_samples + final_samples;
-        rw.rev++;
         nr_samples -= final_samples;
         rw.nr_samples = 0;
+        rw.rev++;
     }
 
+    IRQ_global_enable();
+
     rw.nr_samples += nr_samples;
 
     /* Process the flux timings into the raw bitcell buffer. */
@@ -474,18 +491,32 @@ static void floppy_read(void)
 
     if (floppy_state == ST_read_flux) {
 
-        if (index.count >= rw.nr_revs) {
+        rdata_encode_flux();
+        avail = (uint32_t)(u_prod - u_cons);
+
+        if (avail > sizeof(u_buf)) {
+
+            /* Overflow */
+            printk("OVERFLOW %u %u %u %u\n", u_cons, u_prod,
+                   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 = avail = 0;
 
-        rdata_encode_flux();
-        avail = (uint32_t)(u_prod - u_cons);
+        } else if (rw.rev >= rw.nr_revs) {
+
+            /* Read all requested revolutions. */
+            floppy_flux_end();
+            floppy_state = ST_read_flux_drain;
+
+        }
 
     } else if ((avail < USB_FS_MPS)
                && !rw.packet_ready
                && ep_tx_ready(EP_TX)) {
 
+        /* Final packet, including ACK byte (NUL). */
         memset(rw.packet, 0, USB_FS_MPS);
         make_read_packet(avail);
         floppy_state = ST_command_wait;
@@ -495,16 +526,6 @@ 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(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 >= USB_FS_MPS))
         make_read_packet(USB_FS_MPS);