fdc: Add a floppy drive qdev

Floppy controllers automatically create two floppy drive devices in qdev
now. (They always created two drives, but managed them only internally.)

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Message-id: 1477386868-21826-3-git-send-email-kwolf@redhat.com
Signed-off-by: John Snow <jsnow@redhat.com>
This commit is contained in:
Kevin Wolf 2016-10-27 16:29:13 -04:00 committed by John Snow
parent 51e6e90e72
commit 394ea2cac4
1 changed files with 120 additions and 31 deletions

View File

@ -60,6 +60,8 @@
#define FLOPPY_BUS(obj) OBJECT_CHECK(FloppyBus, (obj), TYPE_FLOPPY_BUS) #define FLOPPY_BUS(obj) OBJECT_CHECK(FloppyBus, (obj), TYPE_FLOPPY_BUS)
typedef struct FDCtrl FDCtrl; typedef struct FDCtrl FDCtrl;
typedef struct FDrive FDrive;
static FDrive *get_drv(FDCtrl *fdctrl, int unit);
typedef struct FloppyBus { typedef struct FloppyBus {
BusState bus; BusState bus;
@ -180,7 +182,7 @@ typedef enum FDiskFlags {
FDISK_DBL_SIDES = 0x01, FDISK_DBL_SIDES = 0x01,
} FDiskFlags; } FDiskFlags;
typedef struct FDrive { struct FDrive {
FDCtrl *fdctrl; FDCtrl *fdctrl;
BlockBackend *blk; BlockBackend *blk;
/* Drive status */ /* Drive status */
@ -201,7 +203,7 @@ typedef struct FDrive {
uint8_t media_rate; /* Data rate of medium */ uint8_t media_rate; /* Data rate of medium */
bool media_validated; /* Have we validated the media? */ bool media_validated; /* Have we validated the media? */
} FDrive; };
static FloppyDriveType get_fallback_drive_type(FDrive *drv); static FloppyDriveType get_fallback_drive_type(FDrive *drv);
@ -466,6 +468,100 @@ static void fd_revalidate(FDrive *drv)
} }
} }
static void fd_change_cb(void *opaque, bool load)
{
FDrive *drive = opaque;
drive->media_changed = 1;
drive->media_validated = false;
fd_revalidate(drive);
}
static const BlockDevOps fd_block_ops = {
.change_media_cb = fd_change_cb,
};
#define TYPE_FLOPPY_DRIVE "floppy"
#define FLOPPY_DRIVE(obj) \
OBJECT_CHECK(FloppyDrive, (obj), TYPE_FLOPPY_DRIVE)
typedef struct FloppyDrive {
DeviceState qdev;
uint32_t unit;
} FloppyDrive;
static Property floppy_drive_properties[] = {
DEFINE_PROP_UINT32("unit", FloppyDrive, unit, -1),
DEFINE_PROP_END_OF_LIST(),
};
static int floppy_drive_init(DeviceState *qdev)
{
FloppyDrive *dev = FLOPPY_DRIVE(qdev);
FloppyBus *bus = FLOPPY_BUS(qdev->parent_bus);
FDrive *drive;
if (dev->unit == -1) {
for (dev->unit = 0; dev->unit < MAX_FD; dev->unit++) {
drive = get_drv(bus->fdc, dev->unit);
if (!drive->blk) {
break;
}
}
}
if (dev->unit >= MAX_FD) {
error_report("Can't create floppy unit %d, bus supports only %d units",
dev->unit, MAX_FD);
return -1;
}
/* TODO Check whether unit is in use */
drive = get_drv(bus->fdc, dev->unit);
if (drive->blk) {
if (blk_get_on_error(drive->blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
error_report("fdc doesn't support drive option werror");
return -1;
}
if (blk_get_on_error(drive->blk, 1) != BLOCKDEV_ON_ERROR_REPORT) {
error_report("fdc doesn't support drive option rerror");
return -1;
}
} else {
/* Anonymous BlockBackend for an empty drive */
drive->blk = blk_new();
}
fd_init(drive);
if (drive->blk) {
blk_set_dev_ops(drive->blk, &fd_block_ops, drive);
pick_drive_type(drive);
}
fd_revalidate(drive);
return 0;
}
static void floppy_drive_class_init(ObjectClass *klass, void *data)
{
DeviceClass *k = DEVICE_CLASS(klass);
k->init = floppy_drive_init;
set_bit(DEVICE_CATEGORY_STORAGE, k->categories);
k->bus_type = TYPE_FLOPPY_BUS;
k->props = floppy_drive_properties;
k->desc = "virtual floppy drive";
}
static const TypeInfo floppy_drive_info = {
.name = TYPE_FLOPPY_DRIVE,
.parent = TYPE_DEVICE,
.instance_size = sizeof(FloppyDrive),
.class_init = floppy_drive_class_init,
};
/********************************************************/ /********************************************************/
/* Intel 82078 floppy disk controller emulation */ /* Intel 82078 floppy disk controller emulation */
@ -1185,9 +1281,9 @@ static inline FDrive *drv3(FDCtrl *fdctrl)
} }
#endif #endif
static FDrive *get_cur_drv(FDCtrl *fdctrl) static FDrive *get_drv(FDCtrl *fdctrl, int unit)
{ {
switch (fdctrl->cur_drv) { switch (unit) {
case 0: return drv0(fdctrl); case 0: return drv0(fdctrl);
case 1: return drv1(fdctrl); case 1: return drv1(fdctrl);
#if MAX_FD == 4 #if MAX_FD == 4
@ -1198,6 +1294,11 @@ static FDrive *get_cur_drv(FDCtrl *fdctrl)
} }
} }
static FDrive *get_cur_drv(FDCtrl *fdctrl)
{
return get_drv(fdctrl, fdctrl->cur_drv);
}
/* Status A register : 0x00 (read-only) */ /* Status A register : 0x00 (read-only) */
static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl) static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl)
{ {
@ -2357,46 +2458,33 @@ static void fdctrl_result_timer(void *opaque)
} }
} }
static void fdctrl_change_cb(void *opaque, bool load)
{
FDrive *drive = opaque;
drive->media_changed = 1;
drive->media_validated = false;
fd_revalidate(drive);
}
static const BlockDevOps fdctrl_block_ops = {
.change_media_cb = fdctrl_change_cb,
};
/* Init functions */ /* Init functions */
static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp) static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp)
{ {
unsigned int i; unsigned int i;
FDrive *drive; FDrive *drive;
DeviceState *dev;
Error *local_err = NULL;
for (i = 0; i < MAX_FD; i++) { for (i = 0; i < MAX_FD; i++) {
drive = &fdctrl->drives[i]; drive = &fdctrl->drives[i];
drive->fdctrl = fdctrl; drive->fdctrl = fdctrl;
if (drive->blk) { /* If the drive is not present, we skip creating the qdev device, but
if (blk_get_on_error(drive->blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC) { * still have to initialise the controller. */
error_setg(errp, "fdc doesn't support drive option werror"); if (!fdctrl->drives[i].blk) {
return; fd_init(drive);
} fd_revalidate(drive);
if (blk_get_on_error(drive->blk, 1) != BLOCKDEV_ON_ERROR_REPORT) { continue;
error_setg(errp, "fdc doesn't support drive option rerror");
return;
}
} }
fd_init(drive); dev = qdev_create(&fdctrl->bus.bus, "floppy");
if (drive->blk) { qdev_prop_set_uint32(dev, "unit", i);
blk_set_dev_ops(drive->blk, &fdctrl_block_ops, drive); object_property_set_bool(OBJECT(dev), true, "realized", &local_err);
pick_drive_type(drive); if (local_err) {
error_propagate(errp, local_err);
return;
} }
fd_revalidate(drive);
} }
} }
@ -2774,6 +2862,7 @@ static void fdc_register_types(void)
type_register_static(&sysbus_fdc_info); type_register_static(&sysbus_fdc_info);
type_register_static(&sun4m_fdc_info); type_register_static(&sun4m_fdc_info);
type_register_static(&floppy_bus_info); type_register_static(&floppy_bus_info);
type_register_static(&floppy_drive_info);
} }
type_init(fdc_register_types) type_init(fdc_register_types)