qemu/hw/block/fdc-isa.c
<<
>>
Prefs
   1/*
   2 * QEMU Floppy disk emulator (Intel 82078)
   3 *
   4 * Copyright (c) 2003, 2007 Jocelyn Mayer
   5 * Copyright (c) 2008 Hervé Poussineau
   6 *
   7 * Permission is hereby granted, free of charge, to any person obtaining a copy
   8 * of this software and associated documentation files (the "Software"), to deal
   9 * in the Software without restriction, including without limitation the rights
  10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 * copies of the Software, and to permit persons to whom the Software is
  12 * furnished to do so, subject to the following conditions:
  13 *
  14 * The above copyright notice and this permission notice shall be included in
  15 * all copies or substantial portions of the Software.
  16 *
  17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23 * THE SOFTWARE.
  24 */
  25/*
  26 * The controller is used in Sun4m systems in a slightly different
  27 * way. There are changes in DOR register and DMA is not available.
  28 */
  29
  30#include "qemu/osdep.h"
  31#include "hw/block/fdc.h"
  32#include "qapi/error.h"
  33#include "qemu/error-report.h"
  34#include "qemu/timer.h"
  35#include "hw/acpi/acpi_aml_interface.h"
  36#include "hw/irq.h"
  37#include "hw/isa/isa.h"
  38#include "hw/qdev-properties.h"
  39#include "hw/qdev-properties-system.h"
  40#include "migration/vmstate.h"
  41#include "hw/block/block.h"
  42#include "sysemu/block-backend.h"
  43#include "sysemu/blockdev.h"
  44#include "sysemu/sysemu.h"
  45#include "qemu/log.h"
  46#include "qemu/main-loop.h"
  47#include "qemu/module.h"
  48#include "trace.h"
  49#include "qom/object.h"
  50#include "fdc-internal.h"
  51
  52OBJECT_DECLARE_SIMPLE_TYPE(FDCtrlISABus, ISA_FDC)
  53
  54struct FDCtrlISABus {
  55    /*< private >*/
  56    ISADevice parent_obj;
  57    /*< public >*/
  58
  59    uint32_t iobase;
  60    uint32_t irq;
  61    uint32_t dma;
  62    struct FDCtrl state;
  63    int32_t bootindexA;
  64    int32_t bootindexB;
  65};
  66
  67static void fdctrl_external_reset_isa(DeviceState *d)
  68{
  69    FDCtrlISABus *isa = ISA_FDC(d);
  70    FDCtrl *s = &isa->state;
  71
  72    fdctrl_reset(s, 0);
  73}
  74
  75void isa_fdc_init_drives(ISADevice *fdc, DriveInfo **fds)
  76{
  77    fdctrl_init_drives(&ISA_FDC(fdc)->state.bus, fds);
  78}
  79
  80static const MemoryRegionPortio fdc_portio_list[] = {
  81    { 1, 5, 1, .read = fdctrl_read, .write = fdctrl_write },
  82    { 7, 1, 1, .read = fdctrl_read, .write = fdctrl_write },
  83    PORTIO_END_OF_LIST(),
  84};
  85
  86static void isabus_fdc_realize(DeviceState *dev, Error **errp)
  87{
  88    ISADevice *isadev = ISA_DEVICE(dev);
  89    ISABus *bus = isa_bus_from_device(isadev);
  90    FDCtrlISABus *isa = ISA_FDC(dev);
  91    FDCtrl *fdctrl = &isa->state;
  92    Error *err = NULL;
  93
  94    isa_register_portio_list(isadev, &fdctrl->portio_list,
  95                             isa->iobase, fdc_portio_list, fdctrl,
  96                             "fdc");
  97
  98    fdctrl->irq = isa_bus_get_irq(bus, isa->irq);
  99    fdctrl->dma_chann = isa->dma;
 100    if (fdctrl->dma_chann != -1) {
 101        IsaDmaClass *k;
 102        fdctrl->dma = isa_bus_get_dma(bus, isa->dma);
 103        if (!fdctrl->dma) {
 104            error_setg(errp, "ISA controller does not support DMA");
 105            return;
 106        }
 107        k = ISADMA_GET_CLASS(fdctrl->dma);
 108        k->register_channel(fdctrl->dma, fdctrl->dma_chann,
 109                            &fdctrl_transfer_handler, fdctrl);
 110    }
 111
 112    qdev_set_legacy_instance_id(dev, isa->iobase, 2);
 113
 114    fdctrl_realize_common(dev, fdctrl, &err);
 115    if (err != NULL) {
 116        error_propagate(errp, err);
 117        return;
 118    }
 119}
 120
 121FloppyDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i)
 122{
 123    FDCtrlISABus *isa = ISA_FDC(fdc);
 124
 125    return isa->state.drives[i].drive;
 126}
 127
 128static void isa_fdc_get_drive_max_chs(FloppyDriveType type, uint8_t *maxc,
 129                                      uint8_t *maxh, uint8_t *maxs)
 130{
 131    const FDFormat *fdf;
 132
 133    *maxc = *maxh = *maxs = 0;
 134    for (fdf = fd_formats; fdf->drive != FLOPPY_DRIVE_TYPE_NONE; fdf++) {
 135        if (fdf->drive != type) {
 136            continue;
 137        }
 138        if (*maxc < fdf->max_track) {
 139            *maxc = fdf->max_track;
 140        }
 141        if (*maxh < fdf->max_head) {
 142            *maxh = fdf->max_head;
 143        }
 144        if (*maxs < fdf->last_sect) {
 145            *maxs = fdf->last_sect;
 146        }
 147    }
 148    (*maxc)--;
 149}
 150
 151static Aml *build_fdinfo_aml(int idx, FloppyDriveType type)
 152{
 153    Aml *dev, *fdi;
 154    uint8_t maxc, maxh, maxs;
 155
 156    isa_fdc_get_drive_max_chs(type, &maxc, &maxh, &maxs);
 157
 158    dev = aml_device("FLP%c", 'A' + idx);
 159
 160    aml_append(dev, aml_name_decl("_ADR", aml_int(idx)));
 161
 162    fdi = aml_package(16);
 163    aml_append(fdi, aml_int(idx));  /* Drive Number */
 164    aml_append(fdi,
 165        aml_int(cmos_get_fd_drive_type(type)));  /* Device Type */
 166    /*
 167     * the values below are the limits of the drive, and are thus independent
 168     * of the inserted media
 169     */
 170    aml_append(fdi, aml_int(maxc));  /* Maximum Cylinder Number */
 171    aml_append(fdi, aml_int(maxs));  /* Maximum Sector Number */
 172    aml_append(fdi, aml_int(maxh));  /* Maximum Head Number */
 173    /*
 174     * SeaBIOS returns the below values for int 0x13 func 0x08 regardless of
 175     * the drive type, so shall we
 176     */
 177    aml_append(fdi, aml_int(0xAF));  /* disk_specify_1 */
 178    aml_append(fdi, aml_int(0x02));  /* disk_specify_2 */
 179    aml_append(fdi, aml_int(0x25));  /* disk_motor_wait */
 180    aml_append(fdi, aml_int(0x02));  /* disk_sector_siz */
 181    aml_append(fdi, aml_int(0x12));  /* disk_eot */
 182    aml_append(fdi, aml_int(0x1B));  /* disk_rw_gap */
 183    aml_append(fdi, aml_int(0xFF));  /* disk_dtl */
 184    aml_append(fdi, aml_int(0x6C));  /* disk_formt_gap */
 185    aml_append(fdi, aml_int(0xF6));  /* disk_fill */
 186    aml_append(fdi, aml_int(0x0F));  /* disk_head_sttl */
 187    aml_append(fdi, aml_int(0x08));  /* disk_motor_strt */
 188
 189    aml_append(dev, aml_name_decl("_FDI", fdi));
 190    return dev;
 191}
 192
 193int cmos_get_fd_drive_type(FloppyDriveType fd0)
 194{
 195    int val;
 196
 197    switch (fd0) {
 198    case FLOPPY_DRIVE_TYPE_144:
 199        /* 1.44 Mb 3"5 drive */
 200        val = 4;
 201        break;
 202    case FLOPPY_DRIVE_TYPE_288:
 203        /* 2.88 Mb 3"5 drive */
 204        val = 5;
 205        break;
 206    case FLOPPY_DRIVE_TYPE_120:
 207        /* 1.2 Mb 5"5 drive */
 208        val = 2;
 209        break;
 210    case FLOPPY_DRIVE_TYPE_NONE:
 211    default:
 212        val = 0;
 213        break;
 214    }
 215    return val;
 216}
 217
 218static void build_fdc_aml(AcpiDevAmlIf *adev, Aml *scope)
 219{
 220    FDCtrlISABus *isa = ISA_FDC(adev);
 221    Aml *dev;
 222    Aml *crs;
 223    int i;
 224
 225#define ACPI_FDE_MAX_FD 4
 226    uint32_t fde_buf[5] = {
 227        0, 0, 0, 0,     /* presence of floppy drives #0 - #3 */
 228        cpu_to_le32(2)  /* tape presence (2 == never present) */
 229    };
 230
 231    crs = aml_resource_template();
 232    aml_append(crs,
 233        aml_io(AML_DECODE16, isa->iobase + 2, isa->iobase + 2, 0x00, 0x04));
 234    aml_append(crs,
 235        aml_io(AML_DECODE16, isa->iobase + 7, isa->iobase + 7, 0x00, 0x01));
 236    aml_append(crs, aml_irq_no_flags(isa->irq));
 237    aml_append(crs,
 238        aml_dma(AML_COMPATIBILITY, AML_NOTBUSMASTER, AML_TRANSFER8, isa->dma));
 239
 240    dev = aml_device("FDC0");
 241    aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0700")));
 242    aml_append(dev, aml_name_decl("_CRS", crs));
 243
 244    for (i = 0; i < MIN(MAX_FD, ACPI_FDE_MAX_FD); i++) {
 245        FloppyDriveType type = isa_fdc_get_drive_type(ISA_DEVICE(adev), i);
 246
 247        if (type < FLOPPY_DRIVE_TYPE_NONE) {
 248            fde_buf[i] = cpu_to_le32(1);  /* drive present */
 249            aml_append(dev, build_fdinfo_aml(i, type));
 250        }
 251    }
 252    aml_append(dev, aml_name_decl("_FDE",
 253               aml_buffer(sizeof(fde_buf), (uint8_t *)fde_buf)));
 254
 255    aml_append(scope, dev);
 256}
 257
 258static const VMStateDescription vmstate_isa_fdc = {
 259    .name = "fdc",
 260    .version_id = 2,
 261    .minimum_version_id = 2,
 262    .fields = (VMStateField[]) {
 263        VMSTATE_STRUCT(state, FDCtrlISABus, 0, vmstate_fdc, FDCtrl),
 264        VMSTATE_END_OF_LIST()
 265    }
 266};
 267
 268static Property isa_fdc_properties[] = {
 269    DEFINE_PROP_UINT32("iobase", FDCtrlISABus, iobase, 0x3f0),
 270    DEFINE_PROP_UINT32("irq", FDCtrlISABus, irq, 6),
 271    DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2),
 272    DEFINE_PROP_SIGNED("fdtypeA", FDCtrlISABus, state.qdev_for_drives[0].type,
 273                        FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type,
 274                        FloppyDriveType),
 275    DEFINE_PROP_SIGNED("fdtypeB", FDCtrlISABus, state.qdev_for_drives[1].type,
 276                        FLOPPY_DRIVE_TYPE_AUTO, qdev_prop_fdc_drive_type,
 277                        FloppyDriveType),
 278    DEFINE_PROP_SIGNED("fallback", FDCtrlISABus, state.fallback,
 279                        FLOPPY_DRIVE_TYPE_288, qdev_prop_fdc_drive_type,
 280                        FloppyDriveType),
 281    DEFINE_PROP_END_OF_LIST(),
 282};
 283
 284static void isabus_fdc_class_init(ObjectClass *klass, void *data)
 285{
 286    DeviceClass *dc = DEVICE_CLASS(klass);
 287    AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass);
 288
 289    dc->desc = "virtual floppy controller";
 290    dc->realize = isabus_fdc_realize;
 291    dc->fw_name = "fdc";
 292    dc->reset = fdctrl_external_reset_isa;
 293    dc->vmsd = &vmstate_isa_fdc;
 294    adevc->build_dev_aml = build_fdc_aml;
 295    device_class_set_props(dc, isa_fdc_properties);
 296    set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
 297}
 298
 299static void isabus_fdc_instance_init(Object *obj)
 300{
 301    FDCtrlISABus *isa = ISA_FDC(obj);
 302
 303    device_add_bootindex_property(obj, &isa->bootindexA,
 304                                  "bootindexA", "/floppy@0",
 305                                  DEVICE(obj));
 306    device_add_bootindex_property(obj, &isa->bootindexB,
 307                                  "bootindexB", "/floppy@1",
 308                                  DEVICE(obj));
 309}
 310
 311static const TypeInfo isa_fdc_info = {
 312    .name          = TYPE_ISA_FDC,
 313    .parent        = TYPE_ISA_DEVICE,
 314    .instance_size = sizeof(FDCtrlISABus),
 315    .class_init    = isabus_fdc_class_init,
 316    .instance_init = isabus_fdc_instance_init,
 317    .interfaces = (InterfaceInfo[]) {
 318        { TYPE_ACPI_DEV_AML_IF },
 319        { },
 320    },
 321};
 322
 323static void isa_fdc_register_types(void)
 324{
 325    type_register_static(&isa_fdc_info);
 326}
 327
 328type_init(isa_fdc_register_types)
 329