DevFdc.cpp revision 0a6d755bc1e433527089985f19fec65ee6c9291a
/* $Id$ */
/** @file
* VBox storage devices: Floppy disk controller
*/
/*
* Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
* --------------------------------------------------------------------
*
* This code is based on:
*
* QEMU Floppy disk emulator (Intel 82078)
*
* Copyright (c) 2003 Jocelyn Mayer
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DEV_FDC
#include "VBoxDD.h"
#include "vl_vbox.h"
#define MAX_FD 2
/********************************************************/
/* debug Floppy devices */
/* #define DEBUG_FLOPPY */
#ifndef VBOX
#ifdef DEBUG_FLOPPY
#endif
#else /* !VBOX */
# ifdef LOG_ENABLED
static void FLOPPY_DPRINTF (const char *fmt, ...)
{
if (LogIsEnabled ()) {
RTLogLogger (NULL, NULL, "floppy: %N", fmt, &args); /* %N - nested va_list * type formatting call. */
}
}
# else
# endif
#endif /* !VBOX */
#ifndef VBOX
#else /* VBOX */
# define FLOPPY_ERROR RTLogPrintf
#endif /* VBOX */
#ifdef VBOX
#endif /* VBOX */
/********************************************************/
/* Floppy drive emulation */
/* Will always be a fixed parameter for us */
#define FD_SECTOR_LEN 512
/* Floppy disk drive emulation */
typedef enum fdisk_type_t {
} fdisk_type_t;
typedef enum fdrive_type_t {
#ifdef VBOX
#endif
typedef uint8_t fdrive_flags_t;
typedef enum fdrive_rate_t {
/**
* The status for one drive.
*
* @implements PDMIBASE
* @implements PDMIBLOCKPORT
* @implements PDMIMOUNTNOTIFY
*/
typedef struct fdrive_t {
#ifndef VBOX
#else /* VBOX */
/** Pointer to the attached driver's base interface. */
/** Pointer to the attached driver's block interface. */
/** Pointer to the attached driver's block bios interface. */
/** Pointer to the attached driver's mount interface.
* This is NULL if the driver isn't a removable unit. */
/** The base interface. */
/** The block port interface. */
/** The mount notify interface. */
/** The LUN #. */
/** The LED for this LUN. */
#endif
/* Drive status */
/* Position */
/* Media */
} fdrive_t;
{
/* Drive */
#ifndef VBOX
#else /* VBOX */
if (fInit) {
/* Fixate the drive type at init time if possible. */
switch (enmType) {
case PDMBLOCKTYPE_FLOPPY_360:
case PDMBLOCKTYPE_FLOPPY_1_20:
break;
case PDMBLOCKTYPE_FLOPPY_720:
case PDMBLOCKTYPE_FLOPPY_1_44:
break;
default:
AssertFailed();
case PDMBLOCKTYPE_FLOPPY_2_88:
break;
break;
break;
}
} else {
}
} /* else: The BIOS (and others) get the drive type via the CMOS, so
don't change it after the VM has been constructed. */
#endif /* VBOX */
drv->perpendicular = 0;
/* Disk */
}
{
}
/* Returns current position, in sectors, for given drive */
{
}
/* Seek to a new position:
* returns 0 if already on right track
* returns 1 if track changed
* returns 2 if track is invalid
* returns 3 if sector is invalid
* returns 4 if seek is disabled
*/
int enable_seek)
{
int sector;
int ret;
FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n",
return 2;
}
FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n",
return 3;
}
ret = 0;
#if 0
if (!enable_seek) {
FLOPPY_ERROR("no implicit seek %d %02x %02x (max=%d %02x %02x)\n",
return 4;
}
#endif
ret = 1;
}
return ret;
}
/* Set drive back to track 0 */
{
FLOPPY_DPRINTF("recalibrate\n");
}
/* Recognize floppy formats */
typedef struct fd_format_t {
const char *str;
} fd_format_t;
/* Note: Low-density disks (160K/180K/320K/360K) use 250 Kbps data rate
* in 40-track drives, but 300 Kbps in high-capacity 80-track drives.
*/
static fd_format_t fd_formats[] = {
/* First entry is default format */
/* 1.44 MB 3"1/2 floppy disks */
/* 2.88 MB 3"1/2 floppy disks */
/* 720 kB 3"1/2 floppy disks */
/* 1.2 MB 5"1/4 floppy disks */
/* 720 kB 5"1/4 floppy disks */
/* 360 kB 5"1/4 floppy disks (newer 9-sector formats) */
/* 320 kB 5"1/4 floppy disks (old 8-sector formats) */
/* 1.2 MB and low density 3"1/2 floppy 'aliases' */
#ifdef VBOX /* For larger than real life floppy images (see DrvBlock.cpp). */
/* 15.6 MB fake floppy disk (just need something big). */
/* 63.5 MB fake floppy disk (just need something big). */
#endif
/* end */
};
/* Revalidate a disk drive after a disk change */
{
const fd_format_t *parse;
int i, first_match, match;
FLOPPY_DPRINTF("revalidate\n");
#ifndef VBOX
#else /* VBOX */
#endif /* VBOX */
FLOPPY_DPRINTF("User defined disk (%d %d %d)",
} else {
#ifndef VBOX
#else /* VBOX */
{
}
#endif /* VBOX */
match = -1;
first_match = -1;
for (i = 0;; i++) {
parse = &fd_formats[i];
break;
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;
}
#ifdef VBOX
#endif
}
if (nb_heads == 1) {
} else {
}
} else {
FLOPPY_DPRINTF("No disk in drive\n");
}
}
/********************************************************/
/* Intel 82078 floppy disk controller emulation */
#ifndef VBOX
#else /* VBOX: */
void *opaque,
unsigned nchan,
#endif /* VBOX */
static void fdctrl_result_timer(void *opaque);
enum {
FD_DIR_WRITE = 0,
FD_DIR_READ = 1,
FD_DIR_SCANE = 2,
FD_DIR_SCANL = 3,
FD_DIR_SCANH = 4,
FD_DIR_FORMAT = 5
};
enum {
};
enum {
FD_REG_SRA = 0x00,
FD_REG_SRB = 0x01,
FD_REG_DOR = 0x02,
FD_REG_TDR = 0x03,
FD_REG_MSR = 0x04,
FD_REG_DSR = 0x04,
FD_REG_FIFO = 0x05,
FD_REG_DIR = 0x07,
FD_REG_CCR = 0x07
};
enum {
FD_CMD_READ_TRACK = 0x02,
FD_CMD_SPECIFY = 0x03,
FD_CMD_SENSE_DRIVE_STATUS = 0x04,
FD_CMD_WRITE = 0x05,
FD_CMD_READ = 0x06,
FD_CMD_RECALIBRATE = 0x07,
FD_CMD_SENSE_INTERRUPT_STATUS = 0x08,
FD_CMD_WRITE_DELETED = 0x09,
FD_CMD_READ_ID = 0x0a,
FD_CMD_READ_DELETED = 0x0c,
FD_CMD_FORMAT_TRACK = 0x0d,
FD_CMD_DUMPREG = 0x0e,
FD_CMD_SEEK = 0x0f,
FD_CMD_VERSION = 0x10,
FD_CMD_SCAN_EQUAL = 0x11,
FD_CMD_PERPENDICULAR_MODE = 0x12,
FD_CMD_CONFIGURE = 0x13,
FD_CMD_LOCK = 0x14,
FD_CMD_VERIFY = 0x16,
FD_CMD_POWERDOWN_MODE = 0x17,
FD_CMD_PART_ID = 0x18,
FD_CMD_SCAN_LOW_OR_EQUAL = 0x19,
FD_CMD_SCAN_HIGH_OR_EQUAL = 0x1d,
FD_CMD_SAVE = 0x2e,
FD_CMD_OPTION = 0x33,
FD_CMD_RESTORE = 0x4e,
FD_CMD_RELATIVE_SEEK_OUT = 0x8f,
FD_CMD_FORMAT_AND_WRITE = 0xcd,
FD_CMD_RELATIVE_SEEK_IN = 0xcf
};
enum {
};
enum {
FD_SR0_EQPMT = 0x10,
FD_SR0_SEEK = 0x20,
FD_SR0_ABNTERM = 0x40,
FD_SR0_INVCMD = 0x80,
FD_SR0_RDYCHG = 0xc0
};
enum {
};
enum {
};
enum {
FD_SRA_DIR = 0x01,
FD_SRA_nWP = 0x02,
FD_SRA_nINDX = 0x04,
FD_SRA_HDSEL = 0x08,
FD_SRA_nTRK0 = 0x10,
FD_SRA_STEP = 0x20,
FD_SRA_nDRV2 = 0x40,
FD_SRA_INTPEND = 0x80
};
enum {
FD_SRB_MTR0 = 0x01,
FD_SRB_MTR1 = 0x02,
FD_SRB_WGATE = 0x04,
FD_SRB_RDATA = 0x08,
FD_SRB_WDATA = 0x10,
FD_SRB_DR0 = 0x20
};
enum {
#if MAX_FD == 4
FD_DOR_SELMASK = 0x03,
#else
FD_DOR_SELMASK = 0x01,
#endif
FD_DOR_nRESET = 0x04,
FD_DOR_DMAEN = 0x08,
FD_DOR_MOTEN0 = 0x10,
FD_DOR_MOTEN1 = 0x20,
FD_DOR_MOTEN2 = 0x40,
FD_DOR_MOTEN3 = 0x80
};
enum {
#if MAX_FD == 4
FD_TDR_BOOTSEL = 0x0c
#else
FD_TDR_BOOTSEL = 0x04
#endif
};
enum {
FD_DSR_DRATEMASK= 0x03,
FD_DSR_PWRDOWN = 0x40,
FD_DSR_SWRESET = 0x80
};
enum {
FD_MSR_DRV0BUSY = 0x01,
FD_MSR_DRV1BUSY = 0x02,
FD_MSR_DRV2BUSY = 0x04,
FD_MSR_DRV3BUSY = 0x08,
FD_MSR_CMDBUSY = 0x10,
FD_MSR_NONDMA = 0x20,
FD_MSR_DIO = 0x40,
FD_MSR_RQM = 0x80
};
enum {
FD_DIR_DSKCHG = 0x80
};
#ifdef VBOX
/**
* Floppy controller state.
*
* @implements PDMILEDPORTS
*/
#endif
struct fdctrl_t {
#ifndef VBOX
#endif
/* Controller's identification */
/* HW */
#ifndef VBOX
int irq;
int dma_chann;
#else
#endif
/* Controller state */
/* Command FIFO */
/* States kept only to be returned back */
/* Timers state */
/* precompensation */
/* Power down config (also with status regB access mode */
/* Floppy drives */
#ifdef VBOX
/** Pointer to device instance. */
/** Status LUN: The base interface. */
/** Status LUN: The Leds interface. */
/** Status LUN: The Partner of ILeds. */
#endif
};
{
switch (reg) {
case FD_REG_SRA:
break;
case FD_REG_SRB:
break;
case FD_REG_DOR:
break;
case FD_REG_TDR:
break;
case FD_REG_MSR:
break;
case FD_REG_FIFO:
break;
case FD_REG_DIR:
break;
default:
break;
}
return retval;
}
{
switch (reg) {
case FD_REG_DOR:
break;
case FD_REG_TDR:
break;
case FD_REG_DSR:
break;
case FD_REG_FIFO:
break;
case FD_REG_CCR:
break;
default:
break;
}
}
/* Change IRQ state */
{
return;
FLOPPY_DPRINTF("Reset interrupt\n");
#ifdef VBOX
#else
#endif
}
{
FLOPPY_DPRINTF("Raising interrupt...\n");
#ifdef VBOX
#else
#endif
}
if (status0 & FD_SR0_SEEK) {
/* A seek clears the disk change line (if a disk is inserted). */
}
fdctrl->reset_sensei = 0;
}
/* Reset controller */
{
int i;
FLOPPY_DPRINTF("reset controller\n");
/* Initialise controller */
#ifdef VBOX
#else
#endif
/* FIFO state */
fdctrl->data_state = 0;
for (i = 0; i < MAX_FD; i++)
if (do_irq) {
}
}
{
}
{
else
}
#if MAX_FD == 4
{
else
}
{
else
}
#endif
{
#if MAX_FD == 4
#endif
default: return NULL;
}
}
/* Status A register : 0x00 (read-only) */
{
return retval;
}
/* Status B register : 0x01 (read-only) */
{
return retval;
}
/* Digital output register : 0x02 */
{
/* Selected drive */
return retval;
}
{
/* Motors */
if (value & FD_DOR_MOTEN0)
else
if (value & FD_DOR_MOTEN1)
else
/* Drive */
if (value & 1)
else
/* Reset */
if (!(value & FD_DOR_nRESET)) {
FLOPPY_DPRINTF("controller enter RESET state\n");
}
} else {
FLOPPY_DPRINTF("controller out of RESET state\n");
}
}
/* Selected drive */
}
/* Tape drive register : 0x03 */
{
return retval;
}
{
/* Reset mode */
FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
return;
}
/* Disk boot selection indicator */
/* Tape indicators: never allow */
}
/* Main status register : 0x04 (read) */
{
return retval;
}
/* Data select rate register : 0x04 (write) */
{
/* Reset mode */
FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
return;
}
/* Reset: autoclear */
if (value & FD_DSR_SWRESET) {
}
if (value & FD_DSR_PWRDOWN) {
}
}
/* Configuration control register : 0x07 (write) */
{
/* Reset mode */
FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
return;
}
/* Only the rate selection bits used in AT mode, and we
* store those in the DSR.
*/
}
{
#ifdef VBOX
#else
int ret;
return 0;
if (ret) {
}
return ret;
#endif
}
/* Digital input register : 0x07 (read-only) */
{
#ifdef VBOX
/* The change line signal is reported by the currently selected
* drive. If the corresponding motor on bit is not set, the drive
* is *not* selected!
*/
#else
#if MAX_FD == 4
#endif
)
#endif
retval |= FD_DIR_DSKCHG;
if (retval != 0)
return retval;
}
/* FIFO state control */
{
}
/* Set FIFO status for the host to read */
{
if (do_irq)
}
/* Set an error: unimplemented/unknown command */
{
}
/* Seek to next sector */
{
FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d)\n",
/* XXX: cur_drv->sect >= cur_drv->last_sect should be an
error in fact */
} else {
return 0;
}
} else {
return 0;
}
FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
} else {
}
return 1;
}
/* Callback for transfer end (stop or abort) */
{
FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
FLOPPY_DPRINTF("ST0:%02x ST1:%02x ST2:%02x C:%02x H:%02x R:%02x N:%02x\n",
#ifdef VBOX
#else
#endif
}
}
/* Prepare a data transfer (either DMA or FIFO) */
{
int did_seek = 0;
FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n",
FLOPPY_DPRINTF("CMD:%02x SEL:%02x C:%02x H:%02x R:%02x N:%02x EOT:%02x GPL:%02x DTL:%02x\n",
case 2:
/* sect too big */
return;
case 3:
/* track too big */
return;
case 4:
/* No seek enabled */
return;
case 1:
did_seek = 1;
break;
default:
break;
}
/* Check the data rate. If the programmed data rate does not match
* the currently inserted medium, the operation has to fail.
*/
#ifdef VBOX
FLOPPY_DPRINTF("data rate mismatch (fdc=%d, media=%d)\n",
return;
}
#endif
/* Set the FIFO state */
else
if (did_seek)
else
} else {
int tmp;
}
int dma_mode;
/* DMA transfer are enabled. Check if DMA channel is well programmed */
#ifndef VBOX
#else
#endif
FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n",
/* No access is allowed until DMA transfer has completed */
/* Now, we just have to wait for the DMA controller to
* recall us...
*/
#ifndef VBOX
#else
#endif
return;
} else {
}
}
FLOPPY_DPRINTF("start non-DMA transfer\n");
if (direction != FD_DIR_WRITE)
/* IO based transfer: calculate len */
return;
}
/* Prepare a format data transfer (either DMA or FIFO) */
{
ks = 1;
FLOPPY_DPRINTF("Start format at %d %d %02x, %d sect, pat %02x (%d)\n",
case 2:
/* sect too big */
return;
case 3:
/* track too big */
return;
case 4:
/* No seek enabled */
return;
case 1:
break;
default:
break;
}
/* It's not clear what should happen if the data rate does not match. */
#if 0
/* Check the data rate. If the programmed data rate does not match
* the currently inserted medium, the operation has to fail.
*/
FLOPPY_DPRINTF("data rate mismatch (fdc=%d, media=%d)\n",
return;
}
#endif
/* Set the FIFO state */
int dma_mode;
/* DMA transfer are enabled. Check if DMA channel is well programmed */
#ifndef VBOX
#else
#endif
FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n",
/* No access is allowed until DMA transfer has completed */
/* Now, we just have to wait for the DMA controller to
* recall us...
*/
#ifndef VBOX
#else
#endif
return;
} else {
}
}
FLOPPY_DPRINTF("start non-DMA format\n");
/* IO based transfer: calculate len */
return;
}
/* Prepare a transfer of deleted data */
{
FLOPPY_ERROR("fdctrl_start_transfer_del() unimplemented\n");
/* We don't handle deleted data,
* so we don't return *ANYTHING*
*/
}
#ifdef VBOX
{
int rc;
if (RT_FAILURE(rc))
return rc;
}
{
int rc;
if (RT_FAILURE(rc))
return rc;
}
#endif
/* handlers for DMA transfers */
#ifdef VBOX
void *opaque,
unsigned nchan,
#else
#endif
{
#ifdef VBOX
int rc;
#else
#endif
FLOPPY_DPRINTF("Not in DMA transfer mode !\n");
return 0;
}
#ifndef VBOX
#else /* !VBOX */
#endif
{
else
goto transfer_error;
}
#ifdef VBOX
{
{
/* Handle readonly medium early, no need to do DMA, touch the
* LED or attempt any writes. A real floppy doesn't attempt
* to write to readonly media either. */
0x00);
goto transfer_error;
}
}
#endif
FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x "
/* READ & SCAN commands and realign to a sector for WRITE */
#ifdef VBOX
if (RT_FAILURE(rc))
#else
#endif
{
FLOPPY_DPRINTF("Floppy: error getting sector %d\n",
/* Sure, image size is too small... */
}
}
case FD_DIR_READ:
/* READ commands */
#ifdef VBOX
{
}
#else
#endif
/* cpu_physical_memory_write(addr + fdctrl->data_pos, */
/* fdctrl->fifo + rel_pos, len); */
break;
case FD_DIR_WRITE:
/* WRITE commands */
#ifdef VBOX
{
}
if (RT_FAILURE(rc))
#else
#endif
{
goto transfer_error;
}
break;
#ifdef VBOX
case FD_DIR_FORMAT:
/* FORMAT command */
{
int sct;
/* Fill the entire track with desired data pattern. */
FLOPPY_DPRINTF("formatting track: %d sectors, pattern %02x\n",
{
if (RT_FAILURE(rc))
{
goto transfer_error;
}
}
}
break;
#endif
default:
/* SCAN commands */
{
int ret;
#ifdef VBOX
#else
#endif
if (ret == 0) {
goto end_transfer;
}
status2 = 0x00;
goto end_transfer;
}
}
break;
}
if (rel_pos == 0) {
/* Seek to next sector */
break;
}
}
FLOPPY_DPRINTF("end transfer %d %d %d\n",
status0 |= FD_SR0_SEEK;
return len;
}
/* Data register : 0x05 */
{
unsigned pos;
#ifdef VBOX
int rc;
#endif
FLOPPY_ERROR("controller not ready for reading\n");
return 0;
}
pos %= FD_SECTOR_LEN;
if (pos == 0) {
FLOPPY_DPRINTF("error seeking to next sector %d\n",
return 0;
}
#ifdef VBOX
if (RT_FAILURE(rc))
#else
#endif
{
FLOPPY_DPRINTF("error getting sector %d\n",
/* Sure, image size is too small... */
}
}
}
/* Switch from transfer mode to status mode
* then from status mode to command mode
*/
} else {
}
}
return retval;
}
{
#ifdef VBOX
#endif
FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n",
case 2:
/* sect too big */
return;
case 3:
/* track too big */
return;
case 4:
/* No seek enabled */
return;
case 1:
break;
default:
break;
}
#ifdef VBOX
if (RT_FAILURE (rc)) {
} else {
ok = 1;
}
}
if (ok) {
#else
} else {
#endif
/* Last sector done */
else
} else {
/* More to do */
}
}
}
{
}
{
/* Drives position */
#if MAX_FD == 4
#else
#endif
/* timers */
}
{
/* Controller's version */
}
{
}
{
/* Drives position */
#if MAX_FD == 4
#endif
/* timers */
}
{
/* Drives position */
#if MAX_FD == 4
#else
#endif
/* timers */
}
{
/* XXX: should set main status register to busy */
#ifdef VBOX
#else
#endif
}
{
FLOPPY_DPRINTF("Format track %d at %d, %d sectors, filler %02x\n",
FLOPPY_DPRINTF("CMD:%02x SEL:%02x N:%02x SC:%02x GPL:%02x D:%02x\n",
/* Since we cannot actually format anything, we have to make sure that
* whatever new format the guest is trying to establish matches the
* existing format of the medium.
*/
else
{
}
}
{
else
/* No result back */
}
{
/* 1 Byte status back */
0x28;
}
{
/* No drive means no TRK0 signal. */
/* Raise Interrupt */
}
{
if(fdctrl->reset_sensei > 0) {
fdctrl->reset_sensei--;
} else {
commands, so we do this hack. It should be suppressed
ASAP */
/* Hack to preserve SR0 on equipment check failures (no drive). */
}
}
{
#ifdef VBOX
/* The seek command just sends step pulses to the drive and doesn't care if
* there's a medium inserted or if it's banging the head against the drive.
*/
/* Raise Interrupt */
#else
} else {
/* Raise Interrupt */
}
#endif
}
{
/* No result back */
}
{
/* No result back */
}
{
}
{
/* No result back */
}
{
/* Command parameters done */
} else {
}
/* ERROR */
}
}
{
} else {
}
/* Raise Interrupt */
}
{
} else {
}
/* Raise Interrupt */
}
static const struct {
const char* name;
int parameters;
int direction;
} handlers[] = {
{ FD_CMD_SENSE_INTERRUPT_STATUS, 0xff, "SENSE INTERRUPT STATUS", 0, fdctrl_handle_sense_interrupt_status },
{ FD_CMD_DRIVE_SPECIFICATION_COMMAND, 0xff, "DRIVE SPECIFICATION COMMAND", 5, fdctrl_handle_drive_specification_command },
{ FD_CMD_WRITE, 0x1f, "WRITE (BeOS)", 8, fdctrl_start_transfer, FD_DIR_WRITE }, /* not in specification ; BeOS 4.5 bug */
};
/* Associate command to an index in the 'handlers' array */
{
int pos;
/* Reset mode */
FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
return;
}
FLOPPY_ERROR("controller not ready for writing\n");
return;
}
/* Is it write command time ? */
/* FIFO data write */
pos %= FD_SECTOR_LEN;
#ifdef VBOX
#else
#endif
}
/* Switch from transfer mode to status mode
* then from status mode to command mode
*/
return;
}
/* Command */
}
/* We now have all parameters
* and will be able to treat the command
*/
return;
}
}
}
static void fdctrl_result_timer(void *opaque)
{
/* Pretend we are spinning.
* This is needed for Coherent, which uses READ ID to check for
* sector interleaving.
*/
}
/* READ_ID can't automatically succeed! */
#ifdef VBOX
FLOPPY_DPRINTF("read id when no disk in drive\n");
FLOPPY_DPRINTF("read id rate mismatch (fdc=%d, media=%d)\n",
FLOPPY_DPRINTF("read id past last track (%d >= %d)\n",
}
else
#endif
}
#ifdef VBOX
/* -=-=-=-=-=-=-=-=- Timer Callback -=-=-=-=-=-=-=-=- */
/**
* @callback_method_impl{FNTMTIMERDEV}
*/
{
}
/* -=-=-=-=-=-=-=-=- I/O Port Access Handlers -=-=-=-=-=-=-=-=- */
/**
* @callback_method_impl{FNIOMIOPORTOUT}
*/
static DECLCALLBACK(int) fdcIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
if (cb == 1)
else
return VINF_SUCCESS;
}
/**
* @callback_method_impl{FNIOMIOPORTOUT}
*/
static DECLCALLBACK(int) fdcIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
if (cb == 1)
{
return VINF_SUCCESS;
}
return VERR_IOM_IOPORT_UNUSED;
}
/* -=-=-=-=-=-=-=-=- Saved state -=-=-=-=-=-=-=-=- */
/**
* @callback_method_impl{FNSSMDEVSAVEEXEC}
*/
{
unsigned int i;
/* Save the FDC I/O registers... */
/* ...the status registers... */
/* ...the command FIFO... */
/* ...and miscellaneous internal FDC state. */
/* Save the number of drives and per-drive state. Note that the media
* states will be updated in fd_revalidate() and need not be saved.
*/
for (i = 0; i < pThis->num_floppies; ++i)
{
}
}
/**
* @callback_method_impl{FNSSMDEVLOADEXEC}
*/
static DECLCALLBACK(int) fdcLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
{
unsigned int i;
if (uVersion > FDC_SAVESTATE_CURRENT)
/* The old saved state was significantly different. However, we can get
* back most of the controller state and fix the rest by pretending the
* disk in the drive (if any) has been replaced. At any rate there should
* be no difficulty unless the state was saved during a floppy operation.
*/
if (uVersion == FDC_SAVESTATE_OLD)
{
/* First verify a few assumptions. */
("The size of FIFO in saved state doesn't match!\n"),
("The number of drives in old saved state doesn't match!\n"),
/* Now load the old state. */
/* Toss IRQ level, DMA channel, I/O base, and state. */
/* Translate dma_en. */
if (val8)
/* Translate bootsel. */
for (i = 0; i < 2; ++i)
{
}
}
else /* New state - straightforward. */
{
/* Load the FDC I/O registers... */
/* ...the status registers... */
/* ...the command FIFO, if the size matches... */
("The size of FIFO in saved state doesn't match!\n"),
/* ...and miscellaneous internal FDC state. */
/* Validate the number of drives. */
("The number of drives in saved state doesn't match!\n"),
/* Load the per-drive state. */
for (i = 0; i < pThis->num_floppies; ++i)
{
}
}
}
/* -=-=-=-=-=-=-=-=- Drive level interfaces -=-=-=-=-=-=-=-=- */
/**
* @interface_method_impl{PDMIMOUNTNOTIFY,pfnMountNotify}
*/
{
LogFlow(("fdMountNotify:\n"));
}
/**
* @interface_method_impl{PDMIMOUNTNOTIFY,pfnUnmountNotify}
*/
{
LogFlow(("fdUnmountNotify:\n"));
}
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
{
return NULL;
}
/* -=-=-=-=-=-=-=-=- Controller level interfaces -=-=-=-=-=-=-=-=- */
/**
* @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
*/
static DECLCALLBACK(int) fdcStatusQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
{
return VINF_SUCCESS;
}
return VERR_PDM_LUN_NOT_FOUND;
}
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
{
return NULL;
}
/**
* Configure a drive.
*
* @returns VBox status code.
* @param drv The drive in question.
* @param pDevIns The driver instance.
* @param fInit Set if we're at init time and can change the drive type.
*/
{
int rc;
/*
* Reset the LED just to be on the safe side.
*/
/*
* Try attach the block device and get the interfaces.
*/
rc = PDMDevHlpDriverAttach (pDevIns, drv->iLUN, &drv->IBase, &drv->pDrvBase, s_apszDesc[drv->iLUN]);
if (RT_SUCCESS (rc)) {
if (drv->pDrvBlockBios) {
} else {
}
} else {
}
} else {
}
} else {
switch (rc) {
case VERR_ACCESS_DENIED:
/* Error already cached by DrvHostBase */
break;
/* Legal on architectures without a floppy controller */
break;
default:
N_ ("The floppy controller cannot attach to the floppy drive"));
break;
}
}
if (RT_FAILURE (rc)) {
}
return rc;
}
/**
* @interface_method_impl{PDMDEVREG,pfnAttach}
*
* This is called when we change block driver for a floppy drive.
*/
{
int rc;
("The FDC device does not support hotplugging\n"),
/*
* Validate.
*/
if (iLUN >= 2) {
AssertMsgFailed (("Configuration error: cannot attach or detach any but the first two LUNs - iLUN=%u\n",
iLUN));
return VERR_PDM_DEVINS_NO_ATTACH;
}
/*
* Locate the drive and stuff.
*/
/* the usual paranoia */
("Configuration error: failed to configure drive %d, rc=%Rrc\n", rc));
if (RT_SUCCESS(rc)) {
fd_revalidate (drv);
}
return rc;
}
/**
* @interface_method_impl{PDMDEVREG,pfnDetach}
*
* The floppy drive has been temporarily 'unplugged'.
*/
{
switch (iLUN)
{
case 0:
case 1:
{
break;
}
default:
break;
}
}
/**
* @interface_method_impl{PDMDEVREG,pfnReset}
*
* I haven't check the specs on what's supposed to happen on reset, but we
* should get any 'FATAL: floppy recal:f07 ctrl not ready' when resetting
* at wrong time like we do if this was all void.
*/
{
unsigned i;
LogFlow (("fdcReset:\n"));
fdctrl_reset(pThis, 0);
}
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
{
int rc;
unsigned i, j;
int ii;
bool mem_mapped;
/*
* Validate configuration.
*/
/*
* Read the configuration.
*/
AssertMsgRCReturn(rc, ("Configuration error: Failed to read bool value MemMapped rc=%Rrc\n", rc), rc);
/*
* Initialize data.
*/
/* Fill 'command_to_handler' lookup table */
for (j = 0; j < sizeof(command_to_handler); j++)
command_to_handler[j] = ii;
{
}
/*
* Create the FDC timer.
*/
if (RT_FAILURE(rc))
return rc;
/*
* Register DMA channel.
*/
{
if (RT_FAILURE(rc))
return rc;
}
/*
* IO / MMIO.
*/
if (mem_mapped)
{
AssertMsgFailed(("Memory mapped floppy not support by now\n"));
return VERR_NOT_SUPPORTED;
#if 0
FLOPPY_ERROR("memory mapped floppy not supported by now !\n");
#endif
}
else
{
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
}
/*
* Register the saved state data unit.
*/
rc = PDMDevHlpSSMRegister(pDevIns, FDC_SAVESTATE_CURRENT, sizeof(*pThis), fdcSaveExec, fdcLoadExec);
if (RT_FAILURE(rc))
return rc;
/*
* Attach the status port (optional).
*/
if (RT_SUCCESS (rc))
else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
{
return rc;
}
/*
* Initialize drives.
*/
{
if ( RT_FAILURE(rc)
&& rc != VERR_PDM_NO_ATTACHED_DRIVER)
{
return rc;
}
}
fdctrl_reset(pThis, 0);
return VINF_SUCCESS;
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DeviceFloppyController =
{
/* u32Version */
/* szName */
"i82078",
/* szRCMod */
"",
/* szR0Mod */
"",
/* pszDescription */
"Floppy drive controller (Intel 82078)",
/* fFlags */
/* fClass */
/* cMaxInstances */
1,
/* cbInstance */
sizeof(fdctrl_t),
/* pfnConstruct */
/* pfnDestruct */
NULL,
/* pfnRelocate */
NULL,
/* pfnMemSetup */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
/* pfnDetach */
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
};
#endif /* VBOX */
/*
* Local Variables:
* mode: c
* c-file-style: "k&r"
* indent-tabs-mode: nil
* End:
*/