qemu/hw/block/swim.c
<<
>>
Prefs
   1/*
   2 * QEMU Macintosh floppy disk controller emulator (SWIM)
   3 *
   4 * Copyright (c) 2014-2018 Laurent Vivier <laurent@vivier.eu>
   5 *
   6 * This work is licensed under the terms of the GNU GPL, version 2.  See
   7 * the COPYING file in the top-level directory.
   8 *
   9 * Only the basic support: it allows to switch from IWM (Integrated WOZ
  10 * Machine) mode to the SWIM mode and makes the linux driver happy.
  11 */
  12
  13#include "qemu/osdep.h"
  14#include "qemu/main-loop.h"
  15#include "qapi/error.h"
  16#include "sysemu/block-backend.h"
  17#include "hw/sysbus.h"
  18#include "migration/vmstate.h"
  19#include "hw/block/block.h"
  20#include "hw/block/swim.h"
  21#include "hw/qdev-properties.h"
  22
  23/* IWM registers */
  24
  25#define IWM_PH0L                0
  26#define IWM_PH0H                1
  27#define IWM_PH1L                2
  28#define IWM_PH1H                3
  29#define IWM_PH2L                4
  30#define IWM_PH2H                5
  31#define IWM_PH3L                6
  32#define IWM_PH3H                7
  33#define IWM_MTROFF              8
  34#define IWM_MTRON               9
  35#define IWM_INTDRIVE            10
  36#define IWM_EXTDRIVE            11
  37#define IWM_Q6L                 12
  38#define IWM_Q6H                 13
  39#define IWM_Q7L                 14
  40#define IWM_Q7H                 15
  41
  42/* SWIM registers */
  43
  44#define SWIM_WRITE_DATA         0
  45#define SWIM_WRITE_MARK         1
  46#define SWIM_WRITE_CRC          2
  47#define SWIM_WRITE_PARAMETER    3
  48#define SWIM_WRITE_PHASE        4
  49#define SWIM_WRITE_SETUP        5
  50#define SWIM_WRITE_MODE0        6
  51#define SWIM_WRITE_MODE1        7
  52
  53#define SWIM_READ_DATA          8
  54#define SWIM_READ_MARK          9
  55#define SWIM_READ_ERROR         10
  56#define SWIM_READ_PARAMETER     11
  57#define SWIM_READ_PHASE         12
  58#define SWIM_READ_SETUP         13
  59#define SWIM_READ_STATUS        14
  60#define SWIM_READ_HANDSHAKE     15
  61
  62#define REG_SHIFT               9
  63
  64#define SWIM_MODE_IWM  0
  65#define SWIM_MODE_SWIM 1
  66
  67/* bits in phase register */
  68
  69#define SWIM_SEEK_NEGATIVE   0x074
  70#define SWIM_STEP            0x071
  71#define SWIM_MOTOR_ON        0x072
  72#define SWIM_MOTOR_OFF       0x076
  73#define SWIM_INDEX           0x073
  74#define SWIM_EJECT           0x077
  75#define SWIM_SETMFM          0x171
  76#define SWIM_SETGCR          0x175
  77#define SWIM_RELAX           0x033
  78#define SWIM_LSTRB           0x008
  79#define SWIM_CA_MASK         0x077
  80
  81/* Select values for swim_select and swim_readbit */
  82
  83#define SWIM_READ_DATA_0     0x074
  84#define SWIM_TWOMEG_DRIVE    0x075
  85#define SWIM_SINGLE_SIDED    0x076
  86#define SWIM_DRIVE_PRESENT   0x077
  87#define SWIM_DISK_IN         0x170
  88#define SWIM_WRITE_PROT      0x171
  89#define SWIM_TRACK_ZERO      0x172
  90#define SWIM_TACHO           0x173
  91#define SWIM_READ_DATA_1     0x174
  92#define SWIM_MFM_MODE        0x175
  93#define SWIM_SEEK_COMPLETE   0x176
  94#define SWIM_ONEMEG_MEDIA    0x177
  95
  96/* Bits in handshake register */
  97
  98#define SWIM_MARK_BYTE       0x01
  99#define SWIM_CRC_ZERO        0x02
 100#define SWIM_RDDATA          0x04
 101#define SWIM_SENSE           0x08
 102#define SWIM_MOTEN           0x10
 103#define SWIM_ERROR           0x20
 104#define SWIM_DAT2BYTE        0x40
 105#define SWIM_DAT1BYTE        0x80
 106
 107/* bits in setup register */
 108
 109#define SWIM_S_INV_WDATA     0x01
 110#define SWIM_S_3_5_SELECT    0x02
 111#define SWIM_S_GCR           0x04
 112#define SWIM_S_FCLK_DIV2     0x08
 113#define SWIM_S_ERROR_CORR    0x10
 114#define SWIM_S_IBM_DRIVE     0x20
 115#define SWIM_S_GCR_WRITE     0x40
 116#define SWIM_S_TIMEOUT       0x80
 117
 118/* bits in mode register */
 119
 120#define SWIM_CLFIFO          0x01
 121#define SWIM_ENBL1           0x02
 122#define SWIM_ENBL2           0x04
 123#define SWIM_ACTION          0x08
 124#define SWIM_WRITE_MODE      0x10
 125#define SWIM_HEDSEL          0x20
 126#define SWIM_MOTON           0x80
 127
 128static void fd_recalibrate(FDrive *drive)
 129{
 130}
 131
 132static void swim_change_cb(void *opaque, bool load, Error **errp)
 133{
 134    FDrive *drive = opaque;
 135
 136    if (!load) {
 137        blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort);
 138    } else {
 139        if (!blkconf_apply_backend_options(drive->conf,
 140                                           blk_is_read_only(drive->blk), false,
 141                                           errp)) {
 142            return;
 143        }
 144    }
 145}
 146
 147static const BlockDevOps swim_block_ops = {
 148    .change_media_cb = swim_change_cb,
 149};
 150
 151static Property swim_drive_properties[] = {
 152    DEFINE_PROP_INT32("unit", SWIMDrive, unit, -1),
 153    DEFINE_BLOCK_PROPERTIES(SWIMDrive, conf),
 154    DEFINE_PROP_END_OF_LIST(),
 155};
 156
 157static void swim_drive_realize(DeviceState *qdev, Error **errp)
 158{
 159    SWIMDrive *dev = SWIM_DRIVE(qdev);
 160    SWIMBus *bus = SWIM_BUS(qdev->parent_bus);
 161    FDrive *drive;
 162    int ret;
 163
 164    if (dev->unit == -1) {
 165        for (dev->unit = 0; dev->unit < SWIM_MAX_FD; dev->unit++) {
 166            drive = &bus->ctrl->drives[dev->unit];
 167            if (!drive->blk) {
 168                break;
 169            }
 170        }
 171    }
 172
 173    if (dev->unit >= SWIM_MAX_FD) {
 174        error_setg(errp, "Can't create floppy unit %d, bus supports "
 175                   "only %d units", dev->unit, SWIM_MAX_FD);
 176        return;
 177    }
 178
 179    drive = &bus->ctrl->drives[dev->unit];
 180    if (drive->blk) {
 181        error_setg(errp, "Floppy unit %d is in use", dev->unit);
 182        return;
 183    }
 184
 185    if (!dev->conf.blk) {
 186        /* Anonymous BlockBackend for an empty drive */
 187        dev->conf.blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
 188        ret = blk_attach_dev(dev->conf.blk, qdev);
 189        assert(ret == 0);
 190    }
 191
 192    blkconf_blocksizes(&dev->conf);
 193    if (dev->conf.logical_block_size != 512 ||
 194        dev->conf.physical_block_size != 512)
 195    {
 196        error_setg(errp, "Physical and logical block size must "
 197                   "be 512 for floppy");
 198        return;
 199    }
 200
 201    /*
 202     * rerror/werror aren't supported by fdc and therefore not even registered
 203     * with qdev. So set the defaults manually before they are used in
 204     * blkconf_apply_backend_options().
 205     */
 206    dev->conf.rerror = BLOCKDEV_ON_ERROR_AUTO;
 207    dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO;
 208
 209    if (!blkconf_apply_backend_options(&dev->conf,
 210                                       blk_is_read_only(dev->conf.blk),
 211                                       false, errp)) {
 212        return;
 213    }
 214
 215    /*
 216     * 'enospc' is the default for -drive, 'report' is what blk_new() gives us
 217     * for empty drives.
 218     */
 219    if (blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC &&
 220        blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_REPORT) {
 221        error_setg(errp, "fdc doesn't support drive option werror");
 222        return;
 223    }
 224    if (blk_get_on_error(dev->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) {
 225        error_setg(errp, "fdc doesn't support drive option rerror");
 226        return;
 227    }
 228
 229    drive->conf = &dev->conf;
 230    drive->blk = dev->conf.blk;
 231    drive->swimctrl = bus->ctrl;
 232
 233    blk_set_dev_ops(drive->blk, &swim_block_ops, drive);
 234}
 235
 236static void swim_drive_class_init(ObjectClass *klass, void *data)
 237{
 238    DeviceClass *k = DEVICE_CLASS(klass);
 239    k->realize = swim_drive_realize;
 240    set_bit(DEVICE_CATEGORY_STORAGE, k->categories);
 241    k->bus_type = TYPE_SWIM_BUS;
 242    device_class_set_props(k, swim_drive_properties);
 243    k->desc = "virtual SWIM drive";
 244}
 245
 246static const TypeInfo swim_drive_info = {
 247    .name = TYPE_SWIM_DRIVE,
 248    .parent = TYPE_DEVICE,
 249    .instance_size = sizeof(SWIMDrive),
 250    .class_init = swim_drive_class_init,
 251};
 252
 253static const TypeInfo swim_bus_info = {
 254    .name = TYPE_SWIM_BUS,
 255    .parent = TYPE_BUS,
 256    .instance_size = sizeof(SWIMBus),
 257};
 258
 259static void iwmctrl_write(void *opaque, hwaddr reg, uint64_t value,
 260                          unsigned size)
 261{
 262    SWIMCtrl *swimctrl = opaque;
 263
 264    reg >>= REG_SHIFT;
 265
 266    swimctrl->regs[reg >> 1] = reg & 1;
 267
 268    if (swimctrl->regs[IWM_Q6] &&
 269        swimctrl->regs[IWM_Q7]) {
 270        if (swimctrl->regs[IWM_MTR]) {
 271            /* data register */
 272            swimctrl->iwm_data = value;
 273        } else {
 274            /* mode register */
 275            swimctrl->iwm_mode = value;
 276            /* detect sequence to switch from IWM mode to SWIM mode */
 277            switch (swimctrl->iwm_switch) {
 278            case 0:
 279                if (value == 0x57) {
 280                    swimctrl->iwm_switch++;
 281                }
 282                break;
 283            case 1:
 284                if (value == 0x17) {
 285                    swimctrl->iwm_switch++;
 286                }
 287                break;
 288            case 2:
 289                if (value == 0x57) {
 290                    swimctrl->iwm_switch++;
 291                }
 292                break;
 293            case 3:
 294                if (value == 0x57) {
 295                    swimctrl->mode = SWIM_MODE_SWIM;
 296                    swimctrl->iwm_switch = 0;
 297                }
 298                break;
 299            }
 300        }
 301    }
 302}
 303
 304static uint64_t iwmctrl_read(void *opaque, hwaddr reg, unsigned size)
 305{
 306    SWIMCtrl *swimctrl = opaque;
 307
 308    reg >>= REG_SHIFT;
 309
 310    swimctrl->regs[reg >> 1] = reg & 1;
 311
 312    return 0;
 313}
 314
 315static void swimctrl_write(void *opaque, hwaddr reg, uint64_t value,
 316                           unsigned size)
 317{
 318    SWIMCtrl *swimctrl = opaque;
 319
 320    if (swimctrl->mode == SWIM_MODE_IWM) {
 321        iwmctrl_write(opaque, reg, value, size);
 322        return;
 323    }
 324
 325    reg >>= REG_SHIFT;
 326
 327    switch (reg) {
 328    case SWIM_WRITE_PHASE:
 329        swimctrl->swim_phase = value;
 330        break;
 331    case SWIM_WRITE_MODE0:
 332        swimctrl->swim_mode &= ~value;
 333        break;
 334    case SWIM_WRITE_MODE1:
 335        swimctrl->swim_mode |= value;
 336        break;
 337    case SWIM_WRITE_DATA:
 338    case SWIM_WRITE_MARK:
 339    case SWIM_WRITE_CRC:
 340    case SWIM_WRITE_PARAMETER:
 341    case SWIM_WRITE_SETUP:
 342        break;
 343    }
 344}
 345
 346static uint64_t swimctrl_read(void *opaque, hwaddr reg, unsigned size)
 347{
 348    SWIMCtrl *swimctrl = opaque;
 349    uint32_t value = 0;
 350
 351    if (swimctrl->mode == SWIM_MODE_IWM) {
 352        return iwmctrl_read(opaque, reg, size);
 353    }
 354
 355    reg >>= REG_SHIFT;
 356
 357    switch (reg) {
 358    case SWIM_READ_PHASE:
 359        value = swimctrl->swim_phase;
 360        break;
 361    case SWIM_READ_HANDSHAKE:
 362        if (swimctrl->swim_phase == SWIM_DRIVE_PRESENT) {
 363            /* always answer "no drive present" */
 364            value = SWIM_SENSE;
 365        }
 366        break;
 367    case SWIM_READ_DATA:
 368    case SWIM_READ_MARK:
 369    case SWIM_READ_ERROR:
 370    case SWIM_READ_PARAMETER:
 371    case SWIM_READ_SETUP:
 372    case SWIM_READ_STATUS:
 373        break;
 374    }
 375
 376    return value;
 377}
 378
 379static const MemoryRegionOps swimctrl_mem_ops = {
 380    .write = swimctrl_write,
 381    .read = swimctrl_read,
 382    .endianness = DEVICE_NATIVE_ENDIAN,
 383};
 384
 385static void sysbus_swim_reset(DeviceState *d)
 386{
 387    SWIM *sys = SWIM(d);
 388    SWIMCtrl *ctrl = &sys->ctrl;
 389    int i;
 390
 391    ctrl->mode = 0;
 392    ctrl->iwm_switch = 0;
 393    for (i = 0; i < 8; i++) {
 394        ctrl->regs[i] = 0;
 395    }
 396    ctrl->iwm_data = 0;
 397    ctrl->iwm_mode = 0;
 398    ctrl->swim_phase = 0;
 399    ctrl->swim_mode = 0;
 400    for (i = 0; i < SWIM_MAX_FD; i++) {
 401        fd_recalibrate(&ctrl->drives[i]);
 402    }
 403}
 404
 405static void sysbus_swim_init(Object *obj)
 406{
 407    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 408    SWIM *sbs = SWIM(obj);
 409    SWIMCtrl *swimctrl = &sbs->ctrl;
 410
 411    memory_region_init_io(&swimctrl->iomem, obj, &swimctrl_mem_ops, swimctrl,
 412                          "swim", 0x2000);
 413    sysbus_init_mmio(sbd, &swimctrl->iomem);
 414}
 415
 416static void sysbus_swim_realize(DeviceState *dev, Error **errp)
 417{
 418    SWIM *sys = SWIM(dev);
 419    SWIMCtrl *swimctrl = &sys->ctrl;
 420
 421    qbus_create_inplace(&swimctrl->bus, sizeof(SWIMBus), TYPE_SWIM_BUS, dev,
 422                        NULL);
 423    swimctrl->bus.ctrl = swimctrl;
 424}
 425
 426static const VMStateDescription vmstate_fdrive = {
 427    .name = "fdrive",
 428    .version_id = 1,
 429    .minimum_version_id = 1,
 430    .fields = (VMStateField[]) {
 431        VMSTATE_END_OF_LIST()
 432    },
 433};
 434
 435static const VMStateDescription vmstate_swim = {
 436    .name = "swim",
 437    .version_id = 1,
 438    .minimum_version_id = 1,
 439    .fields = (VMStateField[]) {
 440        VMSTATE_INT32(mode, SWIMCtrl),
 441        /* IWM mode */
 442        VMSTATE_INT32(iwm_switch, SWIMCtrl),
 443        VMSTATE_UINT16_ARRAY(regs, SWIMCtrl, 8),
 444        VMSTATE_UINT8(iwm_data, SWIMCtrl),
 445        VMSTATE_UINT8(iwm_mode, SWIMCtrl),
 446        /* SWIM mode */
 447        VMSTATE_UINT8(swim_phase, SWIMCtrl),
 448        VMSTATE_UINT8(swim_mode, SWIMCtrl),
 449        /* Drives */
 450        VMSTATE_STRUCT_ARRAY(drives, SWIMCtrl, SWIM_MAX_FD, 1,
 451                             vmstate_fdrive, FDrive),
 452        VMSTATE_END_OF_LIST()
 453    },
 454};
 455
 456static const VMStateDescription vmstate_sysbus_swim = {
 457    .name = "SWIM",
 458    .version_id = 1,
 459    .fields = (VMStateField[]) {
 460        VMSTATE_STRUCT(ctrl, SWIM, 0, vmstate_swim, SWIMCtrl),
 461        VMSTATE_END_OF_LIST()
 462    }
 463};
 464
 465static void sysbus_swim_class_init(ObjectClass *oc, void *data)
 466{
 467    DeviceClass *dc = DEVICE_CLASS(oc);
 468
 469    dc->realize = sysbus_swim_realize;
 470    dc->reset = sysbus_swim_reset;
 471    dc->vmsd = &vmstate_sysbus_swim;
 472}
 473
 474static const TypeInfo sysbus_swim_info = {
 475    .name          = TYPE_SWIM,
 476    .parent        = TYPE_SYS_BUS_DEVICE,
 477    .instance_size = sizeof(SWIM),
 478    .instance_init = sysbus_swim_init,
 479    .class_init    = sysbus_swim_class_init,
 480};
 481
 482static void swim_register_types(void)
 483{
 484    type_register_static(&sysbus_swim_info);
 485    type_register_static(&swim_bus_info);
 486    type_register_static(&swim_drive_info);
 487}
 488
 489type_init(swim_register_types)
 490