summaryrefslogtreecommitdiffstats
path: root/qemu/hw/block/fdc.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/hw/block/fdc.c')
-rw-r--r--qemu/hw/block/fdc.c467
1 files changed, 338 insertions, 129 deletions
diff --git a/qemu/hw/block/fdc.c b/qemu/hw/block/fdc.c
index 5e1b67ee4..372227569 100644
--- a/qemu/hw/block/fdc.c
+++ b/qemu/hw/block/fdc.c
@@ -27,8 +27,10 @@
* way. There are changes in DOR register and DMA is not available.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/block/fdc.h"
+#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu/timer.h"
#include "hw/isa/isa.h"
@@ -40,14 +42,15 @@
/********************************************************/
/* debug Floppy devices */
-//#define DEBUG_FLOPPY
-#ifdef DEBUG_FLOPPY
+#define DEBUG_FLOPPY 0
+
#define FLOPPY_DPRINTF(fmt, ...) \
- do { printf("FLOPPY: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define FLOPPY_DPRINTF(fmt, ...)
-#endif
+ do { \
+ if (DEBUG_FLOPPY) { \
+ fprintf(stderr, "FLOPPY: " fmt , ## __VA_ARGS__); \
+ } \
+ } while (0)
/********************************************************/
/* Floppy drive emulation */
@@ -59,104 +62,82 @@ typedef enum FDriveRate {
FDRIVE_RATE_1M = 0x03, /* 1 Mbps */
} FDriveRate;
+typedef enum FDriveSize {
+ FDRIVE_SIZE_UNKNOWN,
+ FDRIVE_SIZE_350,
+ FDRIVE_SIZE_525,
+} FDriveSize;
+
typedef struct FDFormat {
- FDriveType drive;
+ FloppyDriveType drive;
uint8_t last_sect;
uint8_t max_track;
uint8_t max_head;
FDriveRate rate;
} FDFormat;
+/* In many cases, the total sector size of a format is enough to uniquely
+ * identify it. However, there are some total sector collisions between
+ * formats of different physical size, and these are noted below by
+ * highlighting the total sector size for entries with collisions. */
static const FDFormat fd_formats[] = {
/* First entry is default format */
/* 1.44 MB 3"1/2 floppy disks */
- { FDRIVE_DRV_144, 18, 80, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_144, 20, 80, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_144, 21, 80, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_144, 21, 82, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_144, 21, 83, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_144, 22, 80, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_144, 23, 80, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_144, 24, 80, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_144, 18, 80, 1, FDRIVE_RATE_500K, }, /* 3.5" 2880 */
+ { FLOPPY_DRIVE_TYPE_144, 20, 80, 1, FDRIVE_RATE_500K, }, /* 3.5" 3200 */
+ { FLOPPY_DRIVE_TYPE_144, 21, 80, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_144, 21, 82, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_144, 21, 83, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_144, 22, 80, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_144, 23, 80, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_144, 24, 80, 1, FDRIVE_RATE_500K, },
/* 2.88 MB 3"1/2 floppy disks */
- { FDRIVE_DRV_288, 36, 80, 1, FDRIVE_RATE_1M, },
- { FDRIVE_DRV_288, 39, 80, 1, FDRIVE_RATE_1M, },
- { FDRIVE_DRV_288, 40, 80, 1, FDRIVE_RATE_1M, },
- { FDRIVE_DRV_288, 44, 80, 1, FDRIVE_RATE_1M, },
- { FDRIVE_DRV_288, 48, 80, 1, FDRIVE_RATE_1M, },
+ { FLOPPY_DRIVE_TYPE_288, 36, 80, 1, FDRIVE_RATE_1M, },
+ { FLOPPY_DRIVE_TYPE_288, 39, 80, 1, FDRIVE_RATE_1M, },
+ { FLOPPY_DRIVE_TYPE_288, 40, 80, 1, FDRIVE_RATE_1M, },
+ { FLOPPY_DRIVE_TYPE_288, 44, 80, 1, FDRIVE_RATE_1M, },
+ { FLOPPY_DRIVE_TYPE_288, 48, 80, 1, FDRIVE_RATE_1M, },
/* 720 kB 3"1/2 floppy disks */
- { FDRIVE_DRV_144, 9, 80, 1, FDRIVE_RATE_250K, },
- { FDRIVE_DRV_144, 10, 80, 1, FDRIVE_RATE_250K, },
- { FDRIVE_DRV_144, 10, 82, 1, FDRIVE_RATE_250K, },
- { FDRIVE_DRV_144, 10, 83, 1, FDRIVE_RATE_250K, },
- { FDRIVE_DRV_144, 13, 80, 1, FDRIVE_RATE_250K, },
- { FDRIVE_DRV_144, 14, 80, 1, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_144, 9, 80, 1, FDRIVE_RATE_250K, }, /* 3.5" 1440 */
+ { FLOPPY_DRIVE_TYPE_144, 10, 80, 1, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_144, 10, 82, 1, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_144, 10, 83, 1, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_144, 13, 80, 1, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_144, 14, 80, 1, FDRIVE_RATE_250K, },
/* 1.2 MB 5"1/4 floppy disks */
- { FDRIVE_DRV_120, 15, 80, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_120, 18, 80, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_120, 18, 82, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_120, 18, 83, 1, FDRIVE_RATE_500K, },
- { FDRIVE_DRV_120, 20, 80, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_120, 15, 80, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_120, 18, 80, 1, FDRIVE_RATE_500K, }, /* 5.25" 2880 */
+ { FLOPPY_DRIVE_TYPE_120, 18, 82, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_120, 18, 83, 1, FDRIVE_RATE_500K, },
+ { FLOPPY_DRIVE_TYPE_120, 20, 80, 1, FDRIVE_RATE_500K, }, /* 5.25" 3200 */
/* 720 kB 5"1/4 floppy disks */
- { FDRIVE_DRV_120, 9, 80, 1, FDRIVE_RATE_250K, },
- { FDRIVE_DRV_120, 11, 80, 1, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_120, 9, 80, 1, FDRIVE_RATE_250K, }, /* 5.25" 1440 */
+ { FLOPPY_DRIVE_TYPE_120, 11, 80, 1, FDRIVE_RATE_250K, },
/* 360 kB 5"1/4 floppy disks */
- { FDRIVE_DRV_120, 9, 40, 1, FDRIVE_RATE_300K, },
- { FDRIVE_DRV_120, 9, 40, 0, FDRIVE_RATE_300K, },
- { FDRIVE_DRV_120, 10, 41, 1, FDRIVE_RATE_300K, },
- { FDRIVE_DRV_120, 10, 42, 1, FDRIVE_RATE_300K, },
+ { FLOPPY_DRIVE_TYPE_120, 9, 40, 1, FDRIVE_RATE_300K, }, /* 5.25" 720 */
+ { FLOPPY_DRIVE_TYPE_120, 9, 40, 0, FDRIVE_RATE_300K, },
+ { FLOPPY_DRIVE_TYPE_120, 10, 41, 1, FDRIVE_RATE_300K, },
+ { FLOPPY_DRIVE_TYPE_120, 10, 42, 1, FDRIVE_RATE_300K, },
/* 320 kB 5"1/4 floppy disks */
- { FDRIVE_DRV_120, 8, 40, 1, FDRIVE_RATE_250K, },
- { FDRIVE_DRV_120, 8, 40, 0, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_120, 8, 40, 1, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_120, 8, 40, 0, FDRIVE_RATE_250K, },
/* 360 kB must match 5"1/4 better than 3"1/2... */
- { FDRIVE_DRV_144, 9, 80, 0, FDRIVE_RATE_250K, },
+ { FLOPPY_DRIVE_TYPE_144, 9, 80, 0, FDRIVE_RATE_250K, }, /* 3.5" 720 */
/* end */
- { FDRIVE_DRV_NONE, -1, -1, 0, 0, },
+ { FLOPPY_DRIVE_TYPE_NONE, -1, -1, 0, 0, },
};
-static void pick_geometry(BlockBackend *blk, int *nb_heads,
- int *max_track, int *last_sect,
- FDriveType drive_in, FDriveType *drive,
- FDriveRate *rate)
+static FDriveSize drive_size(FloppyDriveType drive)
{
- const FDFormat *parse;
- uint64_t nb_sectors, size;
- int i, first_match, match;
-
- blk_get_geometry(blk, &nb_sectors);
- match = -1;
- first_match = -1;
- for (i = 0; ; i++) {
- parse = &fd_formats[i];
- if (parse->drive == FDRIVE_DRV_NONE) {
- break;
- }
- if (drive_in == parse->drive ||
- drive_in == FDRIVE_DRV_NONE) {
- size = (parse->max_head + 1) * parse->max_track *
- parse->last_sect;
- if (nb_sectors == size) {
- match = i;
- break;
- }
- if (first_match == -1) {
- first_match = i;
- }
- }
- }
- if (match == -1) {
- if (first_match == -1) {
- match = 1;
- } else {
- match = first_match;
- }
- parse = &fd_formats[match];
+ switch (drive) {
+ case FLOPPY_DRIVE_TYPE_120:
+ return FDRIVE_SIZE_525;
+ case FLOPPY_DRIVE_TYPE_144:
+ case FLOPPY_DRIVE_TYPE_288:
+ return FDRIVE_SIZE_350;
+ default:
+ return FDRIVE_SIZE_UNKNOWN;
}
- *nb_heads = parse->max_head + 1;
- *max_track = parse->max_track;
- *last_sect = parse->last_sect;
- *drive = parse->drive;
- *rate = parse->rate;
}
#define GET_CUR_DRV(fdctrl) ((fdctrl)->cur_drv)
@@ -178,13 +159,14 @@ typedef struct FDrive {
FDCtrl *fdctrl;
BlockBackend *blk;
/* Drive status */
- FDriveType drive;
+ FloppyDriveType drive; /* CMOS drive type */
uint8_t perpendicular; /* 2.88 MB access mode */
/* Position */
uint8_t head;
uint8_t track;
uint8_t sect;
/* Media */
+ FloppyDriveType disk; /* Current disk type */
FDiskFlags flags;
uint8_t last_sect; /* Nb sector per track */
uint8_t max_track; /* Nb of tracks */
@@ -192,16 +174,38 @@ typedef struct FDrive {
uint8_t ro; /* Is read-only */
uint8_t media_changed; /* Is media changed */
uint8_t media_rate; /* Data rate of medium */
+
+ bool media_validated; /* Have we validated the media? */
} FDrive;
+
+static FloppyDriveType get_fallback_drive_type(FDrive *drv);
+
+/* Hack: FD_SEEK is expected to work on empty drives. However, QEMU
+ * currently goes through some pains to keep seeks within the bounds
+ * established by last_sect and max_track. Correcting this is difficult,
+ * as refactoring FDC code tends to expose nasty bugs in the Linux kernel.
+ *
+ * For now: allow empty drives to have large bounds so we can seek around,
+ * with the understanding that when a diskette is inserted, the bounds will
+ * properly tighten to match the geometry of that inserted medium.
+ */
+static void fd_empty_seek_hack(FDrive *drv)
+{
+ drv->last_sect = 0xFF;
+ drv->max_track = 0xFF;
+}
+
static void fd_init(FDrive *drv)
{
/* Drive */
- drv->drive = FDRIVE_DRV_NONE;
drv->perpendicular = 0;
/* Disk */
+ drv->disk = FLOPPY_DRIVE_TYPE_NONE;
drv->last_sect = 0;
drv->max_track = 0;
+ drv->ro = true;
+ drv->media_changed = 1;
}
#define NUM_SIDES(drv) ((drv)->flags & FDISK_DBL_SIDES ? 2 : 1)
@@ -284,39 +288,149 @@ static void fd_recalibrate(FDrive *drv)
fd_seek(drv, 0, 0, 1, 1);
}
+/**
+ * Determine geometry based on inserted diskette.
+ * Will not operate on an empty drive.
+ *
+ * @return: 0 on success, -1 if the drive is empty.
+ */
+static int pick_geometry(FDrive *drv)
+{
+ BlockBackend *blk = drv->blk;
+ const FDFormat *parse;
+ uint64_t nb_sectors, size;
+ int i;
+ int match, size_match, type_match;
+ bool magic = drv->drive == FLOPPY_DRIVE_TYPE_AUTO;
+
+ /* We can only pick a geometry if we have a diskette. */
+ if (!drv->blk || !blk_is_inserted(drv->blk) ||
+ drv->drive == FLOPPY_DRIVE_TYPE_NONE)
+ {
+ return -1;
+ }
+
+ /* We need to determine the likely geometry of the inserted medium.
+ * In order of preference, we look for:
+ * (1) The same drive type and number of sectors,
+ * (2) The same diskette size and number of sectors,
+ * (3) The same drive type.
+ *
+ * In all cases, matches that occur higher in the drive table will take
+ * precedence over matches that occur later in the table.
+ */
+ blk_get_geometry(blk, &nb_sectors);
+ match = size_match = type_match = -1;
+ for (i = 0; ; i++) {
+ parse = &fd_formats[i];
+ if (parse->drive == FLOPPY_DRIVE_TYPE_NONE) {
+ break;
+ }
+ size = (parse->max_head + 1) * parse->max_track * parse->last_sect;
+ if (nb_sectors == size) {
+ if (magic || parse->drive == drv->drive) {
+ /* (1) perfect match -- nb_sectors and drive type */
+ goto out;
+ } else if (drive_size(parse->drive) == drive_size(drv->drive)) {
+ /* (2) size match -- nb_sectors and physical medium size */
+ match = (match == -1) ? i : match;
+ } else {
+ /* This is suspicious -- Did the user misconfigure? */
+ size_match = (size_match == -1) ? i : size_match;
+ }
+ } else if (type_match == -1) {
+ if ((parse->drive == drv->drive) ||
+ (magic && (parse->drive == get_fallback_drive_type(drv)))) {
+ /* (3) type match -- nb_sectors mismatch, but matches the type
+ * specified explicitly by the user, or matches the fallback
+ * default type when using the drive autodetect mechanism */
+ type_match = i;
+ }
+ }
+ }
+
+ /* No exact match found */
+ if (match == -1) {
+ if (size_match != -1) {
+ parse = &fd_formats[size_match];
+ FLOPPY_DPRINTF("User requested floppy drive type '%s', "
+ "but inserted medium appears to be a "
+ "%"PRId64" sector '%s' type\n",
+ FloppyDriveType_lookup[drv->drive],
+ nb_sectors,
+ FloppyDriveType_lookup[parse->drive]);
+ }
+ match = type_match;
+ }
+
+ /* No match of any kind found -- fd_format is misconfigured, abort. */
+ if (match == -1) {
+ error_setg(&error_abort, "No candidate geometries present in table "
+ " for floppy drive type '%s'",
+ FloppyDriveType_lookup[drv->drive]);
+ }
+
+ parse = &(fd_formats[match]);
+
+ out:
+ if (parse->max_head == 0) {
+ drv->flags &= ~FDISK_DBL_SIDES;
+ } else {
+ drv->flags |= FDISK_DBL_SIDES;
+ }
+ drv->max_track = parse->max_track;
+ drv->last_sect = parse->last_sect;
+ drv->disk = parse->drive;
+ drv->media_rate = parse->rate;
+ return 0;
+}
+
+static void pick_drive_type(FDrive *drv)
+{
+ if (drv->drive != FLOPPY_DRIVE_TYPE_AUTO) {
+ return;
+ }
+
+ if (pick_geometry(drv) == 0) {
+ drv->drive = drv->disk;
+ } else {
+ drv->drive = get_fallback_drive_type(drv);
+ }
+
+ g_assert(drv->drive != FLOPPY_DRIVE_TYPE_AUTO);
+}
+
/* Revalidate a disk drive after a disk change */
static void fd_revalidate(FDrive *drv)
{
- int nb_heads, max_track, last_sect, ro;
- FDriveType drive;
- FDriveRate rate;
+ int rc;
FLOPPY_DPRINTF("revalidate\n");
if (drv->blk != NULL) {
- ro = blk_is_read_only(drv->blk);
- pick_geometry(drv->blk, &nb_heads, &max_track,
- &last_sect, drv->drive, &drive, &rate);
+ drv->ro = blk_is_read_only(drv->blk);
if (!blk_is_inserted(drv->blk)) {
FLOPPY_DPRINTF("No disk in drive\n");
- } else {
- FLOPPY_DPRINTF("Floppy disk (%d h %d t %d s) %s\n", nb_heads,
- max_track, last_sect, ro ? "ro" : "rw");
- }
- if (nb_heads == 1) {
- drv->flags &= ~FDISK_DBL_SIDES;
- } else {
- drv->flags |= FDISK_DBL_SIDES;
+ drv->disk = FLOPPY_DRIVE_TYPE_NONE;
+ fd_empty_seek_hack(drv);
+ } else if (!drv->media_validated) {
+ rc = pick_geometry(drv);
+ if (rc) {
+ FLOPPY_DPRINTF("Could not validate floppy drive media");
+ } else {
+ drv->media_validated = true;
+ FLOPPY_DPRINTF("Floppy disk (%d h %d t %d s) %s\n",
+ (drv->flags & FDISK_DBL_SIDES) ? 2 : 1,
+ drv->max_track, drv->last_sect,
+ drv->ro ? "ro" : "rw");
+ }
}
- drv->max_track = max_track;
- drv->last_sect = last_sect;
- drv->ro = ro;
- drv->drive = drive;
- drv->media_rate = rate;
} else {
FLOPPY_DPRINTF("No drive connected\n");
drv->last_sect = 0;
drv->max_track = 0;
drv->flags &= ~FDISK_DBL_SIDES;
+ drv->drive = FLOPPY_DRIVE_TYPE_NONE;
+ drv->disk = FLOPPY_DRIVE_TYPE_NONE;
}
}
@@ -532,6 +646,7 @@ struct FDCtrl {
QEMUTimer *result_timer;
int dma_chann;
uint8_t phase;
+ IsaDma *dma;
/* Controller's identification */
uint8_t version;
/* HW */
@@ -566,11 +681,17 @@ struct FDCtrl {
FDrive drives[MAX_FD];
int reset_sensei;
uint32_t check_media_rate;
+ FloppyDriveType fallback; /* type=auto failure fallback */
/* Timers state */
uint8_t timer0;
uint8_t timer1;
};
+static FloppyDriveType get_fallback_drive_type(FDrive *drv)
+{
+ return drv->fdctrl->fallback;
+}
+
#define TYPE_SYSBUS_FDC "base-sysbus-fdc"
#define SYSBUS_FDC(obj) OBJECT_CHECK(FDCtrlSysBus, (obj), TYPE_SYSBUS_FDC)
@@ -1311,7 +1432,8 @@ static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0,
fdctrl->fifo[6] = FD_SECTOR_SC;
fdctrl->data_dir = FD_DIR_READ;
if (!(fdctrl->msr & FD_MSR_NONDMA)) {
- DMA_release_DREQ(fdctrl->dma_chann);
+ IsaDmaClass *k = ISADMA_GET_CLASS(fdctrl->dma);
+ k->release_DREQ(fdctrl->dma, fdctrl->dma_chann);
}
fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
fdctrl->msr &= ~FD_MSR_NONDMA;
@@ -1397,27 +1519,43 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction)
}
fdctrl->eot = fdctrl->fifo[6];
if (fdctrl->dor & FD_DOR_DMAEN) {
- int dma_mode;
+ IsaDmaTransferMode dma_mode;
+ IsaDmaClass *k = ISADMA_GET_CLASS(fdctrl->dma);
+ bool dma_mode_ok;
/* DMA transfer are enabled. Check if DMA channel is well programmed */
- dma_mode = DMA_get_channel_mode(fdctrl->dma_chann);
- dma_mode = (dma_mode >> 2) & 3;
+ dma_mode = k->get_transfer_mode(fdctrl->dma, fdctrl->dma_chann);
FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n",
dma_mode, direction,
(128 << fdctrl->fifo[5]) *
(cur_drv->last_sect - ks + 1), fdctrl->data_len);
- if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL ||
- direction == FD_DIR_SCANH) && dma_mode == 0) ||
- (direction == FD_DIR_WRITE && dma_mode == 2) ||
- (direction == FD_DIR_READ && dma_mode == 1) ||
- (direction == FD_DIR_VERIFY)) {
+ switch (direction) {
+ case FD_DIR_SCANE:
+ case FD_DIR_SCANL:
+ case FD_DIR_SCANH:
+ dma_mode_ok = (dma_mode == ISADMA_TRANSFER_VERIFY);
+ break;
+ case FD_DIR_WRITE:
+ dma_mode_ok = (dma_mode == ISADMA_TRANSFER_WRITE);
+ break;
+ case FD_DIR_READ:
+ dma_mode_ok = (dma_mode == ISADMA_TRANSFER_READ);
+ break;
+ case FD_DIR_VERIFY:
+ dma_mode_ok = true;
+ break;
+ default:
+ dma_mode_ok = false;
+ break;
+ }
+ if (dma_mode_ok) {
/* No access is allowed until DMA transfer has completed */
fdctrl->msr &= ~FD_MSR_RQM;
if (direction != FD_DIR_VERIFY) {
/* Now, we just have to wait for the DMA controller to
* recall us...
*/
- DMA_hold_DREQ(fdctrl->dma_chann);
- DMA_schedule(fdctrl->dma_chann);
+ k->hold_DREQ(fdctrl->dma, fdctrl->dma_chann);
+ k->schedule(fdctrl->dma);
} else {
/* Start transfer */
fdctrl_transfer_handler(fdctrl, fdctrl->dma_chann, 0,
@@ -1456,12 +1594,14 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
FDrive *cur_drv;
int len, start_pos, rel_pos;
uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00;
+ IsaDmaClass *k;
fdctrl = opaque;
if (fdctrl->msr & FD_MSR_RQM) {
FLOPPY_DPRINTF("Not in DMA transfer mode !\n");
return 0;
}
+ k = ISADMA_GET_CLASS(fdctrl->dma);
cur_drv = get_cur_drv(fdctrl);
if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL ||
fdctrl->data_dir == FD_DIR_SCANH)
@@ -1500,8 +1640,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
switch (fdctrl->data_dir) {
case FD_DIR_READ:
/* READ commands */
- DMA_write_memory (nchan, fdctrl->fifo + rel_pos,
- fdctrl->data_pos, len);
+ k->write_memory(fdctrl->dma, nchan, fdctrl->fifo + rel_pos,
+ fdctrl->data_pos, len);
break;
case FD_DIR_WRITE:
/* WRITE commands */
@@ -1515,8 +1655,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
goto transfer_error;
}
- DMA_read_memory (nchan, fdctrl->fifo + rel_pos,
- fdctrl->data_pos, len);
+ k->read_memory(fdctrl->dma, nchan, fdctrl->fifo + rel_pos,
+ fdctrl->data_pos, len);
if (blk_write(cur_drv->blk, fd_sector(cur_drv),
fdctrl->fifo, 1) < 0) {
FLOPPY_DPRINTF("error writing sector %d\n",
@@ -1533,7 +1673,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan,
{
uint8_t tmpbuf[FD_SECTOR_LEN];
int ret;
- DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len);
+ k->read_memory(fdctrl->dma, nchan, tmpbuf, fdctrl->data_pos,
+ len);
ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len);
if (ret == 0) {
status2 = FD_SR2_SEH;
@@ -1798,8 +1939,8 @@ static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction)
FDrive *cur_drv = get_cur_drv(fdctrl);
cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
- timer_mod(fdctrl->result_timer,
- qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (get_ticks_per_sec() / 50));
+ timer_mod(fdctrl->result_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ (NANOSECONDS_PER_SECOND / 50));
}
static void fdctrl_handle_format_track(FDCtrl *fdctrl, int direction)
@@ -2185,6 +2326,7 @@ static void fdctrl_change_cb(void *opaque, bool load)
FDrive *drive = opaque;
drive->media_changed = 1;
+ drive->media_validated = false;
fd_revalidate(drive);
}
@@ -2214,10 +2356,11 @@ static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp)
}
fd_init(drive);
- fdctrl_change_cb(drive, 0);
if (drive->blk) {
blk_set_dev_ops(drive->blk, &fdctrl_block_ops, drive);
+ pick_drive_type(drive);
}
+ fd_revalidate(drive);
}
}
@@ -2233,10 +2376,12 @@ ISADevice *fdctrl_init_isa(ISABus *bus, DriveInfo **fds)
dev = DEVICE(isadev);
if (fds[0]) {
- qdev_prop_set_drive_nofail(dev, "driveA", blk_by_legacy_dinfo(fds[0]));
+ qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fds[0]),
+ &error_fatal);
}
if (fds[1]) {
- qdev_prop_set_drive_nofail(dev, "driveB", blk_by_legacy_dinfo(fds[1]));
+ qdev_prop_set_drive(dev, "driveB", blk_by_legacy_dinfo(fds[1]),
+ &error_fatal);
}
qdev_init_nofail(dev);
@@ -2256,10 +2401,12 @@ void fdctrl_init_sysbus(qemu_irq irq, int dma_chann,
fdctrl = &sys->state;
fdctrl->dma_chann = dma_chann; /* FIXME */
if (fds[0]) {
- qdev_prop_set_drive_nofail(dev, "driveA", blk_by_legacy_dinfo(fds[0]));
+ qdev_prop_set_drive(dev, "driveA", blk_by_legacy_dinfo(fds[0]),
+ &error_fatal);
}
if (fds[1]) {
- qdev_prop_set_drive_nofail(dev, "driveB", blk_by_legacy_dinfo(fds[1]));
+ qdev_prop_set_drive(dev, "driveB", blk_by_legacy_dinfo(fds[1]),
+ &error_fatal);
}
qdev_init_nofail(dev);
sbd = SYS_BUS_DEVICE(dev);
@@ -2275,7 +2422,8 @@ void sun4m_fdctrl_init(qemu_irq irq, hwaddr io_base,
dev = qdev_create(NULL, "SUNW,fdtwo");
if (fds[0]) {
- qdev_prop_set_drive_nofail(dev, "drive", blk_by_legacy_dinfo(fds[0]));
+ qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(fds[0]),
+ &error_fatal);
}
qdev_init_nofail(dev);
sys = SYSBUS_FDC(dev);
@@ -2289,6 +2437,10 @@ static void fdctrl_realize_common(FDCtrl *fdctrl, Error **errp)
int i, j;
static int command_tables_inited = 0;
+ if (fdctrl->fallback == FLOPPY_DRIVE_TYPE_AUTO) {
+ error_setg(errp, "Cannot choose a fallback FDrive type of 'auto'");
+ }
+
/* Fill 'command_to_handler' lookup table */
if (!command_tables_inited) {
command_tables_inited = 1;
@@ -2312,7 +2464,11 @@ static void fdctrl_realize_common(FDCtrl *fdctrl, Error **errp)
fdctrl->num_floppies = MAX_FD;
if (fdctrl->dma_chann != -1) {
- DMA_register_channel(fdctrl->dma_chann, &fdctrl_transfer_handler, fdctrl);
+ IsaDmaClass *k;
+ assert(fdctrl->dma);
+ k = ISADMA_GET_CLASS(fdctrl->dma);
+ k->register_channel(fdctrl->dma, fdctrl->dma_chann,
+ &fdctrl_transfer_handler, fdctrl);
}
fdctrl_connect_drives(fdctrl, errp);
}
@@ -2335,6 +2491,10 @@ static void isabus_fdc_realize(DeviceState *dev, Error **errp)
isa_init_irq(isadev, &fdctrl->irq, isa->irq);
fdctrl->dma_chann = isa->dma;
+ if (fdctrl->dma_chann != -1) {
+ fdctrl->dma = isa_get_dma(isa_bus_from_device(isadev), isa->dma);
+ assert(fdctrl->dma);
+ }
qdev_set_legacy_instance_id(dev, isa->iobase, 2);
fdctrl_realize_common(fdctrl, &err);
@@ -2363,6 +2523,8 @@ static void sun4m_fdc_initfn(Object *obj)
FDCtrlSysBus *sys = SYSBUS_FDC(obj);
FDCtrl *fdctrl = &sys->state;
+ fdctrl->dma_chann = -1;
+
memory_region_init_io(&fdctrl->iomem, obj, &fdctrl_mem_strict_ops,
fdctrl, "fdctrl", 0x08);
sysbus_init_mmio(sbd, &fdctrl->iomem);
@@ -2389,13 +2551,36 @@ static void sysbus_fdc_common_realize(DeviceState *dev, Error **errp)
fdctrl_realize_common(fdctrl, errp);
}
-FDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i)
+FloppyDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i)
{
FDCtrlISABus *isa = ISA_FDC(fdc);
return isa->state.drives[i].drive;
}
+void isa_fdc_get_drive_max_chs(FloppyDriveType type,
+ uint8_t *maxc, uint8_t *maxh, uint8_t *maxs)
+{
+ const FDFormat *fdf;
+
+ *maxc = *maxh = *maxs = 0;
+ for (fdf = fd_formats; fdf->drive != FLOPPY_DRIVE_TYPE_NONE; fdf++) {
+ if (fdf->drive != type) {
+ continue;
+ }
+ if (*maxc < fdf->max_track) {
+ *maxc = fdf->max_track;
+ }
+ if (*maxh < fdf->max_head) {
+ *maxh = fdf->max_head;
+ }
+ if (*maxs < fdf->last_sect) {
+ *maxs = fdf->last_sect;
+ }
+ }
+ (*maxc)--;
+}
+
static const VMStateDescription vmstate_isa_fdc ={
.name = "fdc",
.version_id = 2,
@@ -2414,6 +2599,15 @@ static Property isa_fdc_properties[] = {
DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].blk),
DEFINE_PROP_BIT("check_media_rate", FDCtrlISABus, state.check_media_rate,
0, true),
+ DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlISABus, state.drives[0].drive,
+ FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type,
+ FloppyDriveType),
+ DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlISABus, state.drives[1].drive,
+ FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type,
+ FloppyDriveType),
+ DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback,
+ FLOPPY_DRIVE_TYPE_288, qdev_prop_fdc_drive_type,
+ FloppyDriveType),
DEFINE_PROP_END_OF_LIST(),
};
@@ -2462,6 +2656,15 @@ static const VMStateDescription vmstate_sysbus_fdc ={
static Property sysbus_fdc_properties[] = {
DEFINE_PROP_DRIVE("driveA", FDCtrlSysBus, state.drives[0].blk),
DEFINE_PROP_DRIVE("driveB", FDCtrlSysBus, state.drives[1].blk),
+ DEFINE_PROP_DEFAULT("fdtypeA", FDCtrlSysBus, state.drives[0].drive,
+ FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type,
+ FloppyDriveType),
+ DEFINE_PROP_DEFAULT("fdtypeB", FDCtrlSysBus, state.drives[1].drive,
+ FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type,
+ FloppyDriveType),
+ DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback,
+ FLOPPY_DRIVE_TYPE_144, qdev_prop_fdc_drive_type,
+ FloppyDriveType),
DEFINE_PROP_END_OF_LIST(),
};
@@ -2482,6 +2685,12 @@ static const TypeInfo sysbus_fdc_info = {
static Property sun4m_fdc_properties[] = {
DEFINE_PROP_DRIVE("drive", FDCtrlSysBus, state.drives[0].blk),
+ DEFINE_PROP_DEFAULT("fdtype", FDCtrlSysBus, state.drives[0].drive,
+ FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type,
+ FloppyDriveType),
+ DEFINE_PROP_DEFAULT("fallback", FDCtrlISABus, state.fallback,
+ FLOPPY_DRIVE_TYPE_144, qdev_prop_fdc_drive_type,
+ FloppyDriveType),
DEFINE_PROP_END_OF_LIST(),
};