swim.c (13324B)
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_supports_write_perm(drive->blk), 141 false, 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 if (!blkconf_blocksizes(&dev->conf, errp)) { 193 return; 194 } 195 196 if (dev->conf.logical_block_size != 512 || 197 dev->conf.physical_block_size != 512) 198 { 199 error_setg(errp, "Physical and logical block size must " 200 "be 512 for floppy"); 201 return; 202 } 203 204 /* 205 * rerror/werror aren't supported by fdc and therefore not even registered 206 * with qdev. So set the defaults manually before they are used in 207 * blkconf_apply_backend_options(). 208 */ 209 dev->conf.rerror = BLOCKDEV_ON_ERROR_AUTO; 210 dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO; 211 212 if (!blkconf_apply_backend_options(&dev->conf, 213 !blk_supports_write_perm(dev->conf.blk), 214 false, errp)) { 215 return; 216 } 217 218 /* 219 * 'enospc' is the default for -drive, 'report' is what blk_new() gives us 220 * for empty drives. 221 */ 222 if (blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_ENOSPC && 223 blk_get_on_error(dev->conf.blk, 0) != BLOCKDEV_ON_ERROR_REPORT) { 224 error_setg(errp, "fdc doesn't support drive option werror"); 225 return; 226 } 227 if (blk_get_on_error(dev->conf.blk, 1) != BLOCKDEV_ON_ERROR_REPORT) { 228 error_setg(errp, "fdc doesn't support drive option rerror"); 229 return; 230 } 231 232 drive->conf = &dev->conf; 233 drive->blk = dev->conf.blk; 234 drive->swimctrl = bus->ctrl; 235 236 blk_set_dev_ops(drive->blk, &swim_block_ops, drive); 237} 238 239static void swim_drive_class_init(ObjectClass *klass, void *data) 240{ 241 DeviceClass *k = DEVICE_CLASS(klass); 242 k->realize = swim_drive_realize; 243 set_bit(DEVICE_CATEGORY_STORAGE, k->categories); 244 k->bus_type = TYPE_SWIM_BUS; 245 device_class_set_props(k, swim_drive_properties); 246 k->desc = "virtual SWIM drive"; 247} 248 249static const TypeInfo swim_drive_info = { 250 .name = TYPE_SWIM_DRIVE, 251 .parent = TYPE_DEVICE, 252 .instance_size = sizeof(SWIMDrive), 253 .class_init = swim_drive_class_init, 254}; 255 256static const TypeInfo swim_bus_info = { 257 .name = TYPE_SWIM_BUS, 258 .parent = TYPE_BUS, 259 .instance_size = sizeof(SWIMBus), 260}; 261 262static void iwmctrl_write(void *opaque, hwaddr reg, uint64_t value, 263 unsigned size) 264{ 265 SWIMCtrl *swimctrl = opaque; 266 267 reg >>= REG_SHIFT; 268 269 swimctrl->regs[reg >> 1] = reg & 1; 270 271 if (swimctrl->regs[IWM_Q6] && 272 swimctrl->regs[IWM_Q7]) { 273 if (swimctrl->regs[IWM_MTR]) { 274 /* data register */ 275 swimctrl->iwm_data = value; 276 } else { 277 /* mode register */ 278 swimctrl->iwm_mode = value; 279 /* detect sequence to switch from IWM mode to SWIM mode */ 280 switch (swimctrl->iwm_switch) { 281 case 0: 282 if (value == 0x57) { 283 swimctrl->iwm_switch++; 284 } 285 break; 286 case 1: 287 if (value == 0x17) { 288 swimctrl->iwm_switch++; 289 } 290 break; 291 case 2: 292 if (value == 0x57) { 293 swimctrl->iwm_switch++; 294 } 295 break; 296 case 3: 297 if (value == 0x57) { 298 swimctrl->mode = SWIM_MODE_SWIM; 299 swimctrl->iwm_switch = 0; 300 } 301 break; 302 } 303 } 304 } 305} 306 307static uint64_t iwmctrl_read(void *opaque, hwaddr reg, unsigned size) 308{ 309 SWIMCtrl *swimctrl = opaque; 310 311 reg >>= REG_SHIFT; 312 313 swimctrl->regs[reg >> 1] = reg & 1; 314 315 return 0; 316} 317 318static void swimctrl_write(void *opaque, hwaddr reg, uint64_t value, 319 unsigned size) 320{ 321 SWIMCtrl *swimctrl = opaque; 322 323 if (swimctrl->mode == SWIM_MODE_IWM) { 324 iwmctrl_write(opaque, reg, value, size); 325 return; 326 } 327 328 reg >>= REG_SHIFT; 329 330 switch (reg) { 331 case SWIM_WRITE_PHASE: 332 swimctrl->swim_phase = value; 333 break; 334 case SWIM_WRITE_MODE0: 335 swimctrl->swim_mode &= ~value; 336 break; 337 case SWIM_WRITE_MODE1: 338 swimctrl->swim_mode |= value; 339 break; 340 case SWIM_WRITE_DATA: 341 case SWIM_WRITE_MARK: 342 case SWIM_WRITE_CRC: 343 case SWIM_WRITE_PARAMETER: 344 case SWIM_WRITE_SETUP: 345 break; 346 } 347} 348 349static uint64_t swimctrl_read(void *opaque, hwaddr reg, unsigned size) 350{ 351 SWIMCtrl *swimctrl = opaque; 352 uint32_t value = 0; 353 354 if (swimctrl->mode == SWIM_MODE_IWM) { 355 return iwmctrl_read(opaque, reg, size); 356 } 357 358 reg >>= REG_SHIFT; 359 360 switch (reg) { 361 case SWIM_READ_PHASE: 362 value = swimctrl->swim_phase; 363 break; 364 case SWIM_READ_HANDSHAKE: 365 if (swimctrl->swim_phase == SWIM_DRIVE_PRESENT) { 366 /* always answer "no drive present" */ 367 value = SWIM_SENSE; 368 } 369 break; 370 case SWIM_READ_DATA: 371 case SWIM_READ_MARK: 372 case SWIM_READ_ERROR: 373 case SWIM_READ_PARAMETER: 374 case SWIM_READ_SETUP: 375 case SWIM_READ_STATUS: 376 break; 377 } 378 379 return value; 380} 381 382static const MemoryRegionOps swimctrl_mem_ops = { 383 .write = swimctrl_write, 384 .read = swimctrl_read, 385 .endianness = DEVICE_NATIVE_ENDIAN, 386}; 387 388static void sysbus_swim_reset(DeviceState *d) 389{ 390 Swim *sys = SWIM(d); 391 SWIMCtrl *ctrl = &sys->ctrl; 392 int i; 393 394 ctrl->mode = 0; 395 ctrl->iwm_switch = 0; 396 for (i = 0; i < 8; i++) { 397 ctrl->regs[i] = 0; 398 } 399 ctrl->iwm_data = 0; 400 ctrl->iwm_mode = 0; 401 ctrl->swim_phase = 0; 402 ctrl->swim_mode = 0; 403 for (i = 0; i < SWIM_MAX_FD; i++) { 404 fd_recalibrate(&ctrl->drives[i]); 405 } 406} 407 408static void sysbus_swim_init(Object *obj) 409{ 410 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 411 Swim *sbs = SWIM(obj); 412 SWIMCtrl *swimctrl = &sbs->ctrl; 413 414 memory_region_init_io(&swimctrl->iomem, obj, &swimctrl_mem_ops, swimctrl, 415 "swim", 0x2000); 416 sysbus_init_mmio(sbd, &swimctrl->iomem); 417} 418 419static void sysbus_swim_realize(DeviceState *dev, Error **errp) 420{ 421 Swim *sys = SWIM(dev); 422 SWIMCtrl *swimctrl = &sys->ctrl; 423 424 qbus_init(&swimctrl->bus, sizeof(SWIMBus), TYPE_SWIM_BUS, dev, NULL); 425 swimctrl->bus.ctrl = swimctrl; 426} 427 428static const VMStateDescription vmstate_fdrive = { 429 .name = "fdrive", 430 .version_id = 1, 431 .minimum_version_id = 1, 432 .fields = (VMStateField[]) { 433 VMSTATE_END_OF_LIST() 434 }, 435}; 436 437static const VMStateDescription vmstate_swim = { 438 .name = "swim", 439 .version_id = 1, 440 .minimum_version_id = 1, 441 .fields = (VMStateField[]) { 442 VMSTATE_INT32(mode, SWIMCtrl), 443 /* IWM mode */ 444 VMSTATE_INT32(iwm_switch, SWIMCtrl), 445 VMSTATE_UINT16_ARRAY(regs, SWIMCtrl, 8), 446 VMSTATE_UINT8(iwm_data, SWIMCtrl), 447 VMSTATE_UINT8(iwm_mode, SWIMCtrl), 448 /* SWIM mode */ 449 VMSTATE_UINT8(swim_phase, SWIMCtrl), 450 VMSTATE_UINT8(swim_mode, SWIMCtrl), 451 /* Drives */ 452 VMSTATE_STRUCT_ARRAY(drives, SWIMCtrl, SWIM_MAX_FD, 1, 453 vmstate_fdrive, FDrive), 454 VMSTATE_END_OF_LIST() 455 }, 456}; 457 458static const VMStateDescription vmstate_sysbus_swim = { 459 .name = "SWIM", 460 .version_id = 1, 461 .fields = (VMStateField[]) { 462 VMSTATE_STRUCT(ctrl, Swim, 0, vmstate_swim, SWIMCtrl), 463 VMSTATE_END_OF_LIST() 464 } 465}; 466 467static void sysbus_swim_class_init(ObjectClass *oc, void *data) 468{ 469 DeviceClass *dc = DEVICE_CLASS(oc); 470 471 dc->realize = sysbus_swim_realize; 472 dc->reset = sysbus_swim_reset; 473 dc->vmsd = &vmstate_sysbus_swim; 474} 475 476static const TypeInfo sysbus_swim_info = { 477 .name = TYPE_SWIM, 478 .parent = TYPE_SYS_BUS_DEVICE, 479 .instance_size = sizeof(Swim), 480 .instance_init = sysbus_swim_init, 481 .class_init = sysbus_swim_class_init, 482}; 483 484static void swim_register_types(void) 485{ 486 type_register_static(&sysbus_swim_info); 487 type_register_static(&swim_bus_info); 488 type_register_static(&swim_drive_info); 489} 490 491type_init(swim_register_types)