X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=qemu%2Fhw%2Fblock%2Ffdc.c;fp=qemu%2Fhw%2Fblock%2Ffdc.c;h=372227569e722804c09186cc6f57ee0c03be6cbe;hb=437fd90c0250dee670290f9b714253671a990160;hp=5e1b67ee4389c1e41697cc4bf736c2a9c07433c8;hpb=5bbd6fe9b8bab2a93e548c5a53b032d1939eec05;p=kvmfornfv.git 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(), };