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)
typedef struct FDCtrl FDCtrl;
typedef struct FDrive FDrive;
static FDrive *get_drv(FDCtrl *fdctrl, int unit);
typedef struct FloppyBus {
BusState bus;
@ -180,7 +182,7 @@ typedef enum FDiskFlags {
FDISK_DBL_SIDES = 0x01,
} FDiskFlags;
typedef struct FDrive {
struct FDrive {
FDCtrl *fdctrl;
BlockBackend *blk;
/* Drive status */
@ -201,7 +203,7 @@ typedef struct FDrive {
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);
@ -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 */
@ -1185,9 +1281,9 @@ static inline FDrive *drv3(FDCtrl *fdctrl)
}
#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 1: return drv1(fdctrl);
#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) */
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 */
static void fdctrl_connect_drives(FDCtrl *fdctrl, Error **errp)
{
unsigned int i;
FDrive *drive;
DeviceState *dev;
Error *local_err = NULL;
for (i = 0; i < MAX_FD; i++) {
drive = &fdctrl->drives[i];
drive->fdctrl = fdctrl;
if (drive->blk) {
if (blk_get_on_error(drive->blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
error_setg(errp, "fdc doesn't support drive option werror");
return;
}
if (blk_get_on_error(drive->blk, 1) != BLOCKDEV_ON_ERROR_REPORT) {
error_setg(errp, "fdc doesn't support drive option rerror");
return;
}
/* If the drive is not present, we skip creating the qdev device, but
* still have to initialise the controller. */
if (!fdctrl->drives[i].blk) {
fd_init(drive);
fd_revalidate(drive);
continue;
}
fd_init(drive);
if (drive->blk) {
blk_set_dev_ops(drive->blk, &fdctrl_block_ops, drive);
pick_drive_type(drive);
dev = qdev_create(&fdctrl->bus.bus, "floppy");
qdev_prop_set_uint32(dev, "unit", i);
object_property_set_bool(OBJECT(dev), true, "realized", &local_err);
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(&sun4m_fdc_info);
type_register_static(&floppy_bus_info);
type_register_static(&floppy_drive_info);
}
type_init(fdc_register_types)