All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Hervé Poussineau" <hpoussin@reactos.org>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] [PATCH] Fix floppy controller issues v2
Date: Tue, 12 Feb 2008 18:55:21 +0100	[thread overview]
Message-ID: <47B1DD89.90603@reactos.org> (raw)

[-- Attachment #1: Type: text/plain, Size: 557 bytes --]

Hi,

Attached patch fixes some issues in the floppy disk controller:
- Enhance reset support (external and software)
- Use MAX_FD constant when possible
- Support up to 4 drives if MAX_FD is set to 4
- Fix DOR register, which should be writable at any time
- Let MSR return 0x20 when non-DMA transfer is happening
- Don't assume caller wants to read whole track at once
- Add seek to next sector when in non-DMA mode
- Fix non-DMA write, which was stopping after only 1 byte

Credits to Stuart Brady to help me to debug some issues...

Hervé

[-- Attachment #2: fdc.patch --]
[-- Type: text/plain, Size: 10690 bytes --]

Index: fdc.c
===================================================================
RCS file: /sources/qemu/qemu/hw/fdc.c,v
retrieving revision 1.37
diff -u -r1.37 fdc.c
--- fdc.c	1 Jan 2008 17:06:38 -0000	1.37
+++ fdc.c	12 Feb 2008 17:52:03 -0000
@@ -306,10 +306,9 @@
     drv->drflags &= ~FDRIVE_MOTOR_ON;
 }
 
-/* Re-initialise a drives (motor off, repositioned) */
+/* Re-initialise a drive (repositioned) */
 static void fd_reset (fdrive_t *drv)
 {
-    fd_stop(drv);
     fd_recalibrate(drv);
 }
 
@@ -402,7 +401,7 @@
     /* Sun4m quirks? */
     int sun4m;
     /* Floppy drives */
-    fdrive_t drives[2];
+    fdrive_t drives[MAX_FD];
 };
 
 static uint32_t fdctrl_read (void *opaque, uint32_t reg)
@@ -622,20 +621,16 @@
     fdctrl->dma_chann = dma_chann;
     fdctrl->io_base = io_base;
     fdctrl->config = 0x60; /* Implicit seek, polling & FIFO enabled */
-    if (fdctrl->dma_chann != -1) {
-        fdctrl->dma_en = 1;
+    if (fdctrl->dma_chann != -1)
         DMA_register_channel(dma_chann, &fdctrl_transfer_handler, fdctrl);
-    } else {
-        fdctrl->dma_en = 0;
-    }
-    for (i = 0; i < 2; i++) {
+    for (i = 0; i < MAX_FD; i++) {
         fd_init(&fdctrl->drives[i], fds[i]);
     }
     fdctrl_reset(fdctrl, 0);
     fdctrl->state = FD_CTRL_ACTIVE;
     register_savevm("fdc", io_base, 1, fdc_save, fdc_load, fdctrl);
     qemu_register_reset(fdctrl_external_reset, fdctrl);
-    for (i = 0; i < 2; i++) {
+    for (i = 0; i < MAX_FD; i++) {
         fd_revalidate(&fdctrl->drives[i]);
     }
 
@@ -735,6 +730,7 @@
     fdctrl_reset_fifo(fdctrl);
     if (do_irq)
         fdctrl_raise_irq(fdctrl, 0xc0);
+    fdctrl->dma_en = (fdctrl->dma_chann != -1) ? 1 : 0;
 }
 
 static inline fdrive_t *drv0 (fdctrl_t *fdctrl)
@@ -744,12 +740,41 @@
 
 static inline fdrive_t *drv1 (fdctrl_t *fdctrl)
 {
-    return &fdctrl->drives[1 - fdctrl->bootsel];
+    if (fdctrl->bootsel < 1)
+        return &fdctrl->drives[1];
+    else
+        return &fdctrl->drives[0];
+}
+
+#if MAX_FD >= 4
+static inline fdrive_t *drv2 (fdctrl_t *fdctrl)
+{
+    if (fdctrl->bootsel < 2)
+        return &fdctrl->drives[2];
+    else
+        return &fdctrl->drives[1];
 }
 
+static inline fdrive_t *drv3 (fdctrl_t *fdctrl)
+{
+    if (fdctrl->bootsel < 3)
+        return &fdctrl->drives[3];
+    else
+        return &fdctrl->drives[2];
+}
+#endif
+
 static fdrive_t *get_cur_drv (fdctrl_t *fdctrl)
 {
-    return fdctrl->cur_drv == 0 ? drv0(fdctrl) : drv1(fdctrl);
+    switch (fdctrl->cur_drv) {
+        case 0: return drv0(fdctrl);
+        case 1: return drv1(fdctrl);
+#if MAX_FD >= 4
+        case 2: return drv2(fdctrl);
+        case 3: return drv3(fdctrl);
+#endif
+        default: return NULL;
+    }
 }
 
 /* Status B register : 0x01 (read-only) */
@@ -765,9 +790,15 @@
     uint32_t retval = 0;
 
     /* Drive motors state indicators */
-    if (drv0(fdctrl)->drflags & FDRIVE_MOTOR_ON)
-        retval |= 1 << 5;
+#if MAX_FD >= 4
+    if (drv3(fdctrl)->drflags & FDRIVE_MOTOR_ON)
+        retval |= 1 << 7;
+    if (drv2(fdctrl)->drflags & FDRIVE_MOTOR_ON)
+        retval |= 1 << 6;
+#endif
     if (drv1(fdctrl)->drflags & FDRIVE_MOTOR_ON)
+        retval |= 1 << 5;
+    if (drv0(fdctrl)->drflags & FDRIVE_MOTOR_ON)
         retval |= 1 << 4;
     /* DMA enable */
     retval |= fdctrl->dma_en << 3;
@@ -782,15 +813,18 @@
 
 static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value)
 {
-    /* Reset mode */
-    if (fdctrl->state & FD_CTRL_RESET) {
-        if (!(value & 0x04)) {
-            FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
-            return;
-        }
-    }
     FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value);
     /* Drive motors state indicators */
+#if MAX_FD >= 4
+    if (value & 0x80)
+        fd_start(drv3(fdctrl));
+    else
+        fd_stop(drv3(fdctrl));
+    if (value & 0x40)
+        fd_start(drv2(fdctrl));
+    else
+        fd_stop(drv2(fdctrl));
+#endif

     if (value & 0x20)
         fd_start(drv1(fdctrl));
     else
@@ -818,7 +852,11 @@
         }
     }
     /* Selected drive */
+#if MAX_FD >= 4
+    fdctrl->cur_drv = value & 3;
+#else
     fdctrl->cur_drv = value & 1;
+#endif
 }
 
 /* Tape drive register : 0x03 */
@@ -843,7 +881,11 @@
     }
     FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value);
     /* Disk boot selection indicator */
+#if MAX_FD >= 4
+    fdctrl->bootsel = (value >> 2) & 3;
+#else
     fdctrl->bootsel = (value >> 2) & 1;
+#endif
     /* Tape indicators: never allow */
 }
 
@@ -860,7 +902,10 @@
         if (fdctrl->data_dir == FD_DIR_READ)
             retval |= 0x40;
     }
-    /* Should handle 0x20 for SPECIFY command */
+    /* Non-DMA indicator */
+    if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA &&
+        !fdctrl->dma_en)
+        retval |= 0x20;
     /* Command busy indicator */
     if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA ||
         FD_STATE(fdctrl->data_state) == FD_STATE_STATUS)
@@ -956,6 +1001,40 @@
 #endif
 }
 
+/* Seek to next sector */
+static int fdctrl_seek_to_next_sect (fdctrl_t *fdctrl, fdrive_t *cur_drv)
+{
+    FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d)\n",
+                   cur_drv->head, cur_drv->track, cur_drv->sect,
+                   fd_sector(cur_drv));
+    /* XXX: cur_drv->sect >= cur_drv->last_sect should be an
+       error in fact */
+    if (cur_drv->sect >= cur_drv->last_sect ||
+        cur_drv->sect == fdctrl->eot) {
+        cur_drv->sect = 1;
+        if (FD_MULTI_TRACK(fdctrl->data_state)) {
+            if (cur_drv->head == 0 &&
+                (cur_drv->flags & FDISK_DBL_SIDES) != 0) {
+                cur_drv->head = 1;
+            } else {
+                cur_drv->head = 0;
+                cur_drv->track++;
+                if ((cur_drv->flags & FDISK_DBL_SIDES) == 0)
+                    return 0;
+            }
+        } else {
+            cur_drv->track++;
+            return 0;
+        }
+        FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
+                       cur_drv->head, cur_drv->track,
+                       cur_drv->sect, fd_sector(cur_drv));
+    } else {
+        cur_drv->sect++;
+    }
+    return 1;
+}
+
 /* Callback for transfer end (stop or abort) */
 static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0,
                                   uint8_t status1, uint8_t status2)
@@ -1042,9 +1121,9 @@
     } else {
         int tmp;
         fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]);
-        tmp = (cur_drv->last_sect - ks + 1);
+        tmp = (fdctrl->fifo[6] - ks + 1);
         if (fdctrl->fifo[0] & 0x80)
-            tmp += cur_drv->last_sect;
+            tmp += fdctrl->fifo[6];
         fdctrl->data_len *= tmp;
     }
     fdctrl->eot = fdctrl->fifo[6];
@@ -1178,35 +1257,8 @@
         rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
         if (rel_pos == 0) {
             /* Seek to next sector */
-            FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d) (%d)\n",
-                           cur_drv->head, cur_drv->track, cur_drv->sect,
-                           fd_sector(cur_drv),
-                           fdctrl->data_pos - len);
-            /* XXX: cur_drv->sect >= cur_drv->last_sect should be an
-               error in fact */
-            if (cur_drv->sect >= cur_drv->last_sect ||
-                cur_drv->sect == fdctrl->eot) {
-                cur_drv->sect = 1;
-                if (FD_MULTI_TRACK(fdctrl->data_state)) {
-                    if (cur_drv->head == 0 &&
-                        (cur_drv->flags & FDISK_DBL_SIDES) != 0) {
-                        cur_drv->head = 1;
-                    } else {
-                        cur_drv->head = 0;
-                        cur_drv->track++;
-                        if ((cur_drv->flags & FDISK_DBL_SIDES) == 0)
-                            break;
-                    }
-                } else {
-                    cur_drv->track++;
-                    break;
-                }
-                FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
-                               cur_drv->head, cur_drv->track,
-                               cur_drv->sect, fd_sector(cur_drv));
-            } else {
-                cur_drv->sect++;
-            }
+            if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv))
+                break;
         }
     }
  end_transfer:
@@ -1244,6 +1296,8 @@
     if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) {
         pos %= FD_SECTOR_LEN;
         if (pos == 0) {
+            if (fdctrl->data_pos != 0)
+                fdctrl_seek_to_next_sect(fdctrl, cur_drv);
             len = fdctrl->data_len - fdctrl->data_pos;
             if (len > FD_SECTOR_LEN)
                 len = FD_SECTOR_LEN;
@@ -1336,6 +1390,7 @@
 static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value)
 {
     fdrive_t *cur_drv;
+    int pos;
 
     cur_drv = get_cur_drv(fdctrl);
     /* Reset mode */
@@ -1351,16 +1406,26 @@
     /* Is it write command time ? */
     if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) {
         /* FIFO data write */
-        fdctrl->fifo[fdctrl->data_pos++] = value;
-        if (fdctrl->data_pos % FD_SECTOR_LEN == (FD_SECTOR_LEN - 1) ||
+        pos = fdctrl->data_pos;
+        pos %= FD_SECTOR_LEN;
+        fdctrl->fifo[pos] = value;
+        if (pos == FD_SECTOR_LEN - 1 ||
             fdctrl->data_pos == fdctrl->data_len) {
             bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1);
+            fdctrl_seek_to_next_sect(fdctrl, cur_drv);
+        }
+        if (++fdctrl->data_pos == fdctrl->data_len) {
+            fdctrl->data_pos = 0;
+            /* Switch from transfer mode to status mode
+             * then from status mode to command mode
+             */
+            if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) {
+                fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
+            } else {
+                fdctrl_reset_fifo(fdctrl);
+                fdctrl_reset_irq(fdctrl);
+            }
         }
-        /* Switch from transfer mode to status mode
-         * then from status mode to command mode
-         */
-        if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA)
-            fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
         return;
     }
     if (fdctrl->data_pos == 0) {


                 reply	other threads:[~2008-02-12 17:55 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=47B1DD89.90603@reactos.org \
    --to=hpoussin@reactos.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.