]> git.proxmox.com Git - qemu.git/commitdiff
hw: move target-independent files to subdirectories
authorPaolo Bonzini <pbonzini@redhat.com>
Fri, 1 Mar 2013 12:59:19 +0000 (13:59 +0100)
committerPaolo Bonzini <pbonzini@redhat.com>
Mon, 8 Apr 2013 16:13:12 +0000 (18:13 +0200)
This patch tackles all files that are compiled once, moving
them to subdirectories of hw/.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
416 files changed:
default-configs/i386-softmmu.mak
default-configs/mips-softmmu.mak
default-configs/mips64-softmmu.mak
default-configs/mips64el-softmmu.mak
default-configs/mipsel-softmmu.mak
default-configs/ppc-softmmu.mak
default-configs/ppc64-softmmu.mak
default-configs/ppcemb-softmmu.mak
default-configs/x86_64-softmmu.mak
hw/Makefile.objs
hw/ac97.c [deleted file]
hw/acpi.c [deleted file]
hw/acpi/Makefile.objs
hw/acpi/core.c [new file with mode: 0644]
hw/acpi/ich9.c [new file with mode: 0644]
hw/acpi/piix4.c [new file with mode: 0644]
hw/acpi_ich9.c [deleted file]
hw/acpi_piix4.c [deleted file]
hw/adb.c [deleted file]
hw/adlib.c [deleted file]
hw/ads7846.c [deleted file]
hw/apm.c [deleted file]
hw/applesmc.c [deleted file]
hw/arm_l2x0.c [deleted file]
hw/arm_timer.c [deleted file]
hw/audio/Makefile.objs
hw/audio/ac97.c [new file with mode: 0644]
hw/audio/adlib.c [new file with mode: 0644]
hw/audio/cs4231a.c [new file with mode: 0644]
hw/audio/es1370.c [new file with mode: 0644]
hw/audio/fmopl.c [new file with mode: 0644]
hw/audio/gus.c [new file with mode: 0644]
hw/audio/gusemu_hal.c [new file with mode: 0644]
hw/audio/gusemu_mixer.c [new file with mode: 0644]
hw/audio/hda-codec.c [new file with mode: 0644]
hw/audio/intel-hda.c [new file with mode: 0644]
hw/audio/lm4549.c [new file with mode: 0644]
hw/audio/pcspk.c [new file with mode: 0644]
hw/audio/pl041.c [new file with mode: 0644]
hw/audio/pl041.hx [new file with mode: 0644]
hw/audio/sb16.c [new file with mode: 0644]
hw/audio/wm8750.c [new file with mode: 0644]
hw/block-common.c [deleted file]
hw/block/Makefile.objs
hw/block/block.c [new file with mode: 0644]
hw/block/cdrom.c [new file with mode: 0644]
hw/block/ecc.c [new file with mode: 0644]
hw/block/fdc.c [new file with mode: 0644]
hw/block/hd-geometry.c [new file with mode: 0644]
hw/block/m25p80.c [new file with mode: 0644]
hw/block/nand.c [new file with mode: 0644]
hw/block/pflash_cfi01.c [new file with mode: 0644]
hw/block/pflash_cfi02.c [new file with mode: 0644]
hw/block/xen_disk.c [new file with mode: 0644]
hw/bt-hci-csr.c [deleted file]
hw/bt-hci.c [deleted file]
hw/bt-hid.c [deleted file]
hw/bt-l2cap.c [deleted file]
hw/bt-sdp.c [deleted file]
hw/bt.c [deleted file]
hw/bt/Makefile.objs
hw/bt/core.c [new file with mode: 0644]
hw/bt/hci-csr.c [new file with mode: 0644]
hw/bt/hci.c [new file with mode: 0644]
hw/bt/hid.c [new file with mode: 0644]
hw/bt/l2cap.c [new file with mode: 0644]
hw/bt/sdp.c [new file with mode: 0644]
hw/cadence_gem.c [deleted file]
hw/cadence_ttc.c [deleted file]
hw/cadence_uart.c [deleted file]
hw/ccid-card-emulated.c [deleted file]
hw/ccid-card-passthru.c [deleted file]
hw/cdrom.c [deleted file]
hw/char/Makefile.objs
hw/char/cadence_uart.c [new file with mode: 0644]
hw/char/escc.c [new file with mode: 0644]
hw/char/ipack.c [new file with mode: 0644]
hw/char/ipoctal232.c [new file with mode: 0644]
hw/char/parallel.c [new file with mode: 0644]
hw/char/pl011.c [new file with mode: 0644]
hw/char/serial-isa.c [new file with mode: 0644]
hw/char/serial-pci.c [new file with mode: 0644]
hw/char/serial.c [new file with mode: 0644]
hw/char/tpci200.c [new file with mode: 0644]
hw/char/virtio-console.c [new file with mode: 0644]
hw/char/xen_console.c [new file with mode: 0644]
hw/char/xilinx_uartlite.c [new file with mode: 0644]
hw/cirrus_vga.c [deleted file]
hw/core/Makefile.objs
hw/core/empty_slot.c [new file with mode: 0644]
hw/core/irq.c [new file with mode: 0644]
hw/core/loader.c [new file with mode: 0644]
hw/core/null-machine.c [new file with mode: 0644]
hw/core/ptimer.c [new file with mode: 0644]
hw/core/qdev-addr.c [new file with mode: 0644]
hw/core/qdev-properties-system.c [new file with mode: 0644]
hw/core/qdev-properties.c [new file with mode: 0644]
hw/core/qdev.c [new file with mode: 0644]
hw/core/stream.c [new file with mode: 0644]
hw/core/sysbus.c [new file with mode: 0644]
hw/cs4231a.c [deleted file]
hw/cuda.c [deleted file]
hw/dec_pci.c [deleted file]
hw/display/Makefile.objs
hw/display/ads7846.c [new file with mode: 0644]
hw/display/cirrus_vga.c [new file with mode: 0644]
hw/display/g364fb.c [new file with mode: 0644]
hw/display/jazz_led.c [new file with mode: 0644]
hw/display/pl110.c [new file with mode: 0644]
hw/display/ssd0303.c [new file with mode: 0644]
hw/display/ssd0323.c [new file with mode: 0644]
hw/display/vga-isa-mm.c [new file with mode: 0644]
hw/display/vga-isa.c [new file with mode: 0644]
hw/display/vga-pci.c [new file with mode: 0644]
hw/display/vmware_vga.c [new file with mode: 0644]
hw/display/xenfb.c [new file with mode: 0644]
hw/dma.c [deleted file]
hw/dma/Makefile.objs
hw/dma/i82374.c [new file with mode: 0644]
hw/dma/i8257.c [new file with mode: 0644]
hw/dma/pl080.c [new file with mode: 0644]
hw/dma/pl330.c [new file with mode: 0644]
hw/dma/puv3_dma.c [new file with mode: 0644]
hw/dma/rc4030.c [new file with mode: 0644]
hw/dma/xilinx_axidma.c [new file with mode: 0644]
hw/dp8393x.c [deleted file]
hw/ds1225y.c [deleted file]
hw/ds1338.c [deleted file]
hw/e1000.c [deleted file]
hw/ecc.c [deleted file]
hw/eepro100.c [deleted file]
hw/eeprom93xx.c [deleted file]
hw/empty_slot.c [deleted file]
hw/es1370.c [deleted file]
hw/escc.c [deleted file]
hw/esp-pci.c [deleted file]
hw/esp.c [deleted file]
hw/fdc.c [deleted file]
hw/fmopl.c [deleted file]
hw/fw_cfg.c [deleted file]
hw/g364fb.c [deleted file]
hw/gpio/Makefile.objs
hw/gpio/max7310.c [new file with mode: 0644]
hw/gpio/pl061.c [new file with mode: 0644]
hw/gpio/puv3_gpio.c [new file with mode: 0644]
hw/grackle_pci.c [deleted file]
hw/gus.c [deleted file]
hw/gusemu_hal.c [deleted file]
hw/gusemu_mixer.c [deleted file]
hw/hd-geometry.c [deleted file]
hw/hda-audio.c [deleted file]
hw/heathrow_pic.c [deleted file]
hw/hid.c [deleted file]
hw/hpet.c [deleted file]
hw/i2c.c [deleted file]
hw/i2c/Makefile.objs
hw/i2c/core.c [new file with mode: 0644]
hw/i2c/pm_smbus.c [new file with mode: 0644]
hw/i2c/smbus.c [new file with mode: 0644]
hw/i2c/smbus_eeprom.c [new file with mode: 0644]
hw/i2c/smbus_ich9.c [new file with mode: 0644]
hw/i2c/versatile_i2c.c [new file with mode: 0644]
hw/i82374.c [deleted file]
hw/i82378.c [deleted file]
hw/i8254.c [deleted file]
hw/i8254_common.c [deleted file]
hw/i8259.c [deleted file]
hw/i8259_common.c [deleted file]
hw/i82801b11.c [deleted file]
hw/input/Makefile.objs
hw/input/adb.c [new file with mode: 0644]
hw/input/hid.c [new file with mode: 0644]
hw/input/lm832x.c [new file with mode: 0644]
hw/input/pckbd.c [new file with mode: 0644]
hw/input/pl050.c [new file with mode: 0644]
hw/input/ps2.c [new file with mode: 0644]
hw/input/stellaris_input.c [new file with mode: 0644]
hw/input/tsc2005.c [new file with mode: 0644]
hw/input/vmmouse.c [new file with mode: 0644]
hw/intc/Makefile.objs
hw/intc/heathrow_pic.c [new file with mode: 0644]
hw/intc/i8259.c [new file with mode: 0644]
hw/intc/i8259_common.c [new file with mode: 0644]
hw/intc/pl190.c [new file with mode: 0644]
hw/intc/puv3_intc.c [new file with mode: 0644]
hw/intc/xilinx_intc.c [new file with mode: 0644]
hw/intel-hda.c [deleted file]
hw/ioh3420.c [deleted file]
hw/ipack.c [deleted file]
hw/ipoctal232.c [deleted file]
hw/irq.c [deleted file]
hw/isa-bus.c [deleted file]
hw/isa/Makefile.objs
hw/isa/apm.c [new file with mode: 0644]
hw/isa/i82378.c [new file with mode: 0644]
hw/isa/isa-bus.c [new file with mode: 0644]
hw/isa/isa_mmio.c [new file with mode: 0644]
hw/isa/pc87312.c [new file with mode: 0644]
hw/isa/piix4.c [new file with mode: 0644]
hw/isa_mmio.c [deleted file]
hw/jazz_led.c [deleted file]
hw/lan9118.c [deleted file]
hw/lm4549.c [deleted file]
hw/lm832x.c [deleted file]
hw/loader.c [deleted file]
hw/lsi53c895a.c [deleted file]
hw/m25p80.c [deleted file]
hw/m48t59.c [deleted file]
hw/mac_dbdma.c [deleted file]
hw/mac_nvram.c [deleted file]
hw/macio.c [deleted file]
hw/max111x.c [deleted file]
hw/max7310.c [deleted file]
hw/megasas.c [deleted file]
hw/mipsnet.c [deleted file]
hw/misc/Makefile.objs
hw/misc/applesmc.c [new file with mode: 0644]
hw/misc/arm_l2x0.c [new file with mode: 0644]
hw/misc/macio/Makefile.objs [new file with mode: 0644]
hw/misc/macio/cuda.c [new file with mode: 0644]
hw/misc/macio/mac_dbdma.c [new file with mode: 0644]
hw/misc/macio/macio.c [new file with mode: 0644]
hw/misc/max111x.c [new file with mode: 0644]
hw/misc/puv3_pm.c [new file with mode: 0644]
hw/misc/tmp105.c [new file with mode: 0644]
hw/nand.c [deleted file]
hw/ne2000-isa.c [deleted file]
hw/ne2000.c [deleted file]
hw/net/Makefile.objs
hw/net/cadence_gem.c [new file with mode: 0644]
hw/net/dp8393x.c [new file with mode: 0644]
hw/net/e1000.c [new file with mode: 0644]
hw/net/eepro100.c [new file with mode: 0644]
hw/net/lan9118.c [new file with mode: 0644]
hw/net/mipsnet.c [new file with mode: 0644]
hw/net/ne2000-isa.c [new file with mode: 0644]
hw/net/ne2000.c [new file with mode: 0644]
hw/net/opencores_eth.c [new file with mode: 0644]
hw/net/pcnet-pci.c [new file with mode: 0644]
hw/net/pcnet.c [new file with mode: 0644]
hw/net/rtl8139.c [new file with mode: 0644]
hw/net/smc91c111.c [new file with mode: 0644]
hw/net/vmware_utils.h [new file with mode: 0644]
hw/net/vmxnet3.c [new file with mode: 0644]
hw/net/vmxnet3.h [new file with mode: 0644]
hw/net/vmxnet_debug.h [new file with mode: 0644]
hw/net/vmxnet_rx_pkt.c [new file with mode: 0644]
hw/net/vmxnet_rx_pkt.h [new file with mode: 0644]
hw/net/vmxnet_tx_pkt.c [new file with mode: 0644]
hw/net/vmxnet_tx_pkt.h [new file with mode: 0644]
hw/net/xen_nic.c [new file with mode: 0644]
hw/net/xgmac.c [new file with mode: 0644]
hw/net/xilinx_axienet.c [new file with mode: 0644]
hw/null-machine.c [deleted file]
hw/nvram/Makefile.objs
hw/nvram/ds1225y.c [new file with mode: 0644]
hw/nvram/eeprom93xx.c [new file with mode: 0644]
hw/nvram/fw_cfg.c [new file with mode: 0644]
hw/nvram/mac_nvram.c [new file with mode: 0644]
hw/opencores_eth.c [deleted file]
hw/pam.c [deleted file]
hw/parallel.c [deleted file]
hw/pc87312.c [deleted file]
hw/pci/Makefile.objs
hw/pci/bridge/Makefile.objs [new file with mode: 0644]
hw/pci/bridge/i82801b11.c [new file with mode: 0644]
hw/pci/bridge/ioh3420.c [new file with mode: 0644]
hw/pci/bridge/pci_bridge_dev.c [new file with mode: 0644]
hw/pci/bridge/xio3130_downstream.c [new file with mode: 0644]
hw/pci/bridge/xio3130_upstream.c [new file with mode: 0644]
hw/pci/host/Makefile.objs [new file with mode: 0644]
hw/pci/host/dec.c [new file with mode: 0644]
hw/pci/host/grackle.c [new file with mode: 0644]
hw/pci/host/pam.c [new file with mode: 0644]
hw/pci/host/ppce500.c [new file with mode: 0644]
hw/pci/host/prep.c [new file with mode: 0644]
hw/pci/host/uninorth.c [new file with mode: 0644]
hw/pci/host/versatile.c [new file with mode: 0644]
hw/pci_bridge_dev.c [deleted file]
hw/pckbd.c [deleted file]
hw/pcnet-pci.c [deleted file]
hw/pcnet.c [deleted file]
hw/pcspk.c [deleted file]
hw/pflash_cfi01.c [deleted file]
hw/pflash_cfi02.c [deleted file]
hw/piix4.c [deleted file]
hw/pl011.c [deleted file]
hw/pl022.c [deleted file]
hw/pl031.c [deleted file]
hw/pl041.c [deleted file]
hw/pl041.hx [deleted file]
hw/pl050.c [deleted file]
hw/pl061.c [deleted file]
hw/pl080.c [deleted file]
hw/pl110.c [deleted file]
hw/pl181.c [deleted file]
hw/pl190.c [deleted file]
hw/pl330.c [deleted file]
hw/pm_smbus.c [deleted file]
hw/ppce500_pci.c [deleted file]
hw/prep_pci.c [deleted file]
hw/ps2.c [deleted file]
hw/ptimer.c [deleted file]
hw/puv3_dma.c [deleted file]
hw/puv3_gpio.c [deleted file]
hw/puv3_intc.c [deleted file]
hw/puv3_ost.c [deleted file]
hw/puv3_pm.c [deleted file]
hw/qdev-addr.c [deleted file]
hw/qdev-properties-system.c [deleted file]
hw/qdev-properties.c [deleted file]
hw/qdev.c [deleted file]
hw/rc4030.c [deleted file]
hw/rtl8139.c [deleted file]
hw/sb16.c [deleted file]
hw/scsi-bus.c [deleted file]
hw/scsi-disk.c [deleted file]
hw/scsi-generic.c [deleted file]
hw/scsi/Makefile.objs
hw/scsi/esp-pci.c [new file with mode: 0644]
hw/scsi/esp.c [new file with mode: 0644]
hw/scsi/lsi53c895a.c [new file with mode: 0644]
hw/scsi/megasas.c [new file with mode: 0644]
hw/scsi/scsi-bus.c [new file with mode: 0644]
hw/scsi/scsi-disk.c [new file with mode: 0644]
hw/scsi/scsi-generic.c [new file with mode: 0644]
hw/sd.c [deleted file]
hw/sd/Makefile.objs
hw/sd/pl181.c [new file with mode: 0644]
hw/sd/sd.c [new file with mode: 0644]
hw/sd/sdhci.c [new file with mode: 0644]
hw/sd/ssi-sd.c [new file with mode: 0644]
hw/sdhci.c [deleted file]
hw/serial-isa.c [deleted file]
hw/serial-pci.c [deleted file]
hw/serial.c [deleted file]
hw/smbus.c [deleted file]
hw/smbus_eeprom.c [deleted file]
hw/smbus_ich9.c [deleted file]
hw/smc91c111.c [deleted file]
hw/ssd0303.c [deleted file]
hw/ssd0323.c [deleted file]
hw/ssi-sd.c [deleted file]
hw/ssi.c [deleted file]
hw/ssi/Makefile.objs
hw/ssi/pl022.c [new file with mode: 0644]
hw/ssi/ssi.c [new file with mode: 0644]
hw/stellaris_input.c [deleted file]
hw/stream.c [deleted file]
hw/sysbus.c [deleted file]
hw/timer/Makefile.objs
hw/timer/arm_timer.c [new file with mode: 0644]
hw/timer/cadence_ttc.c [new file with mode: 0644]
hw/timer/ds1338.c [new file with mode: 0644]
hw/timer/hpet.c [new file with mode: 0644]
hw/timer/i8254.c [new file with mode: 0644]
hw/timer/i8254_common.c [new file with mode: 0644]
hw/timer/m48t59.c [new file with mode: 0644]
hw/timer/pl031.c [new file with mode: 0644]
hw/timer/puv3_ost.c [new file with mode: 0644]
hw/timer/twl92230.c [new file with mode: 0644]
hw/timer/xilinx_timer.c [new file with mode: 0644]
hw/tmp105.c [deleted file]
hw/tpci200.c [deleted file]
hw/tsc2005.c [deleted file]
hw/twl92230.c [deleted file]
hw/unin_pci.c [deleted file]
hw/usb/Makefile.objs
hw/usb/ccid-card-emulated.c [new file with mode: 0644]
hw/usb/ccid-card-passthru.c [new file with mode: 0644]
hw/versatile_i2c.c [deleted file]
hw/versatile_pci.c [deleted file]
hw/vga-isa-mm.c [deleted file]
hw/vga-isa.c [deleted file]
hw/vga-pci.c [deleted file]
hw/virtio-bus.c [deleted file]
hw/virtio-console.c [deleted file]
hw/virtio-pci.c [deleted file]
hw/virtio-rng.c [deleted file]
hw/virtio/Makefile.objs
hw/virtio/virtio-bus.c [new file with mode: 0644]
hw/virtio/virtio-pci.c [new file with mode: 0644]
hw/virtio/virtio-rng.c [new file with mode: 0644]
hw/vmmouse.c [deleted file]
hw/vmware_utils.h [deleted file]
hw/vmware_vga.c [deleted file]
hw/vmxnet3.c [deleted file]
hw/vmxnet3.h [deleted file]
hw/vmxnet_debug.h [deleted file]
hw/vmxnet_rx_pkt.c [deleted file]
hw/vmxnet_rx_pkt.h [deleted file]
hw/vmxnet_tx_pkt.c [deleted file]
hw/vmxnet_tx_pkt.h [deleted file]
hw/watchdog.c [deleted file]
hw/watchdog/Makefile.objs
hw/watchdog/watchdog.c [new file with mode: 0644]
hw/watchdog/wdt_i6300esb.c [new file with mode: 0644]
hw/wdt_i6300esb.c [deleted file]
hw/wm8750.c [deleted file]
hw/xen/Makefile.objs
hw/xen/xen_backend.c [new file with mode: 0644]
hw/xen/xen_devconfig.c [new file with mode: 0644]
hw/xen_backend.c [deleted file]
hw/xen_console.c [deleted file]
hw/xen_devconfig.c [deleted file]
hw/xen_disk.c [deleted file]
hw/xen_nic.c [deleted file]
hw/xenfb.c [deleted file]
hw/xgmac.c [deleted file]
hw/xilinx_axidma.c [deleted file]
hw/xilinx_axienet.c [deleted file]
hw/xilinx_intc.c [deleted file]
hw/xilinx_timer.c [deleted file]
hw/xilinx_uartlite.c [deleted file]
hw/xio3130_downstream.c [deleted file]
hw/xio3130_upstream.c [deleted file]

index df9e126c1f240093d1a3ac1d5d05ca7828423986..a2658cda664685fdae5738228f2d10d07d95ff4c 100644 (file)
@@ -16,7 +16,7 @@ CONFIG_PCKBD=y
 CONFIG_FDC=y
 CONFIG_ACPI=y
 CONFIG_APM=y
-CONFIG_DMA=y
+CONFIG_I8257=y
 CONFIG_IDE_ISA=y
 CONFIG_IDE_PIIX=y
 CONFIG_NE2000_ISA=y
index 4f04a337324f89fe37c30323c9bc4d3da64a0f97..dff6fef8a5be464d046c41bbcfc54cb0786d0ee1 100644 (file)
@@ -18,7 +18,7 @@ CONFIG_PCKBD=y
 CONFIG_FDC=y
 CONFIG_ACPI=y
 CONFIG_APM=y
-CONFIG_DMA=y
+CONFIG_I8257=y
 CONFIG_PIIX4=y
 CONFIG_IDE_ISA=y
 CONFIG_IDE_PIIX=y
index a5b6c3c36a4fbff34bae61fb8af706d8b9393575..0968e5facafb8f4e5e3700599d2006753d265bd0 100644 (file)
@@ -18,7 +18,7 @@ CONFIG_PCKBD=y
 CONFIG_FDC=y
 CONFIG_ACPI=y
 CONFIG_APM=y
-CONFIG_DMA=y
+CONFIG_I8257=y
 CONFIG_PIIX4=y
 CONFIG_IDE_ISA=y
 CONFIG_IDE_PIIX=y
index a0e6de8e68db02da5c93c532c01d72a369520df8..6f115d4c42f2f901b10fa8eed8c6592bd6b8b83e 100644 (file)
@@ -18,7 +18,7 @@ CONFIG_PCKBD=y
 CONFIG_FDC=y
 CONFIG_ACPI=y
 CONFIG_APM=y
-CONFIG_DMA=y
+CONFIG_I8257=y
 CONFIG_PIIX4=y
 CONFIG_IDE_ISA=y
 CONFIG_IDE_PIIX=y
index 753dd76a218da6272e05ea5aa1be533df3a6d1d3..e391cf7ba3f3aeb492ea7c89b8b3ed82d538ff7d 100644 (file)
@@ -18,7 +18,7 @@ CONFIG_PCKBD=y
 CONFIG_FDC=y
 CONFIG_ACPI=y
 CONFIG_APM=y
-CONFIG_DMA=y
+CONFIG_I8257=y
 CONFIG_PIIX4=y
 CONFIG_IDE_ISA=y
 CONFIG_IDE_PIIX=y
index c209a8da65b17bbd04cc8f6fe80d25a8226e5500..cdf82b10f327903675c3accdde00f24c4c6bf897 100644 (file)
@@ -13,7 +13,7 @@ CONFIG_PARALLEL=y
 CONFIG_I8254=y
 CONFIG_PCKBD=y
 CONFIG_FDC=y
-CONFIG_DMA=y
+CONFIG_I8257=y
 CONFIG_I82374=y
 CONFIG_OPENPIC=y
 CONFIG_PREP_PCI=y
index 8d490bd72eea60c1f5137f6f7596d465c890a1c3..ee895e920537c5ac9213be0b4912acffc37d21f1 100644 (file)
@@ -13,7 +13,7 @@ CONFIG_PARALLEL=y
 CONFIG_I8254=y
 CONFIG_PCKBD=y
 CONFIG_FDC=y
-CONFIG_DMA=y
+CONFIG_I8257=y
 CONFIG_I82374=y
 CONFIG_OPENPIC=y
 CONFIG_PREP_PCI=y
index 7f13421d93f59fec373e78d47d09fcffdc3a86f9..806adfd3fe8c7065ff98aa0eebfaa6fbd2351f02 100644 (file)
@@ -12,7 +12,7 @@ CONFIG_SERIAL=y
 CONFIG_I8254=y
 CONFIG_PCKBD=y
 CONFIG_FDC=y
-CONFIG_DMA=y
+CONFIG_I8257=y
 CONFIG_OPENPIC=y
 CONFIG_PREP_PCI=y
 CONFIG_MACIO=y
index ab3cd5fc350be9267d05be9bf6d671d866255459..fe4b70ba8eedc4c938ac626d9db098c6e29574d9 100644 (file)
@@ -16,7 +16,7 @@ CONFIG_PCKBD=y
 CONFIG_FDC=y
 CONFIG_ACPI=y
 CONFIG_APM=y
-CONFIG_DMA=y
+CONFIG_I8257=y
 CONFIG_IDE_ISA=y
 CONFIG_IDE_PIIX=y
 CONFIG_NE2000_ISA=y
index 0a92ff9b96b1ce33073a412bb66ef9461406bfdd..1d28ce28d7d9bc42ac5fd744b07f7e66b109b775 100644 (file)
@@ -1,8 +1,3 @@
-# core qdev-related obj files, also used by *-user:
-common-obj-y += qdev.o qdev-properties.o
-# irq.o needed for qdev GPIO handling:
-common-obj-y += irq.o
-
 devices-dirs-$(CONFIG_REALLY_VIRTFS) += 9pfs/
 devices-dirs-$(CONFIG_ACPI) += acpi/
 devices-dirs-$(CONFIG_SOFTMMU) += audio/
@@ -35,198 +30,6 @@ common-obj-y += $(devices-dirs-y)
 obj-y += $(devices-dirs-y)
 
 ifeq ($(CONFIG_SOFTMMU),y)
-common-obj-y += loader.o
-common-obj-$(CONFIG_VIRTIO) += virtio-console.o
-common-obj-$(CONFIG_VIRTIO) += virtio-rng.o
-common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
-common-obj-$(CONFIG_VIRTIO) += virtio-bus.o
-common-obj-y += fw_cfg.o
-common-obj-$(CONFIG_PCI) += pci_bridge_dev.o
-common-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o
-common-obj-$(CONFIG_PCI) += i82801b11.o
-common-obj-y += watchdog.o
-common-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
-common-obj-$(CONFIG_ECC) += ecc.o
-common-obj-$(CONFIG_NAND) += nand.o
-common-obj-$(CONFIG_PFLASH_CFI01) += pflash_cfi01.o
-common-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o
-
-common-obj-$(CONFIG_M48T59) += m48t59.o
-common-obj-$(CONFIG_ESCC) += escc.o
-common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o
-
-common-obj-$(CONFIG_SERIAL) += serial.o serial-isa.o
-common-obj-$(CONFIG_SERIAL_PCI) += serial-pci.o
-common-obj-$(CONFIG_PARALLEL) += parallel.o
-common-obj-$(CONFIG_I8254) += i8254_common.o i8254.o
-common-obj-$(CONFIG_PCSPK) += pcspk.o
-common-obj-$(CONFIG_PCKBD) += pckbd.o
-common-obj-$(CONFIG_FDC) += fdc.o
-common-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o acpi_ich9.o smbus_ich9.o
-common-obj-$(CONFIG_APM) += pm_smbus.o apm.o
-common-obj-$(CONFIG_DMA) += dma.o
-common-obj-$(CONFIG_I82374) += i82374.o
-common-obj-$(CONFIG_HPET) += hpet.o
-common-obj-$(CONFIG_APPLESMC) += applesmc.o
-ifeq ($(CONFIG_USB_SMARTCARD),y)
-common-obj-y += ccid-card-passthru.o
-common-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
-endif
-common-obj-$(CONFIG_I8259) += i8259_common.o i8259.o
-common-obj-$(CONFIG_SDHCI) += sdhci.o
-common-obj-y += pam.o
-
-# PPC devices
-common-obj-$(CONFIG_PREP_PCI) += prep_pci.o
-common-obj-$(CONFIG_I82378) += i82378.o
-common-obj-$(CONFIG_PC87312) += pc87312.o
-# Mac shared devices
-common-obj-$(CONFIG_MACIO) += macio.o
-common-obj-$(CONFIG_CUDA) += cuda.o
-common-obj-$(CONFIG_ADB) += adb.o
-common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o
-common-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o
-# OldWorld PowerMac
-common-obj-$(CONFIG_HEATHROW_PIC) += heathrow_pic.o
-common-obj-$(CONFIG_GRACKLE_PCI) += grackle_pci.o
-# NewWorld PowerMac
-common-obj-$(CONFIG_UNIN_PCI) += unin_pci.o
-common-obj-$(CONFIG_DEC_PCI) += dec_pci.o
-# PowerPC E500 boards
-common-obj-$(CONFIG_PPCE500_PCI) += ppce500_pci.o
-
-# MIPS devices
-common-obj-$(CONFIG_PIIX4) += piix4.o
-common-obj-$(CONFIG_G364FB) += g364fb.o
-common-obj-$(CONFIG_JAZZ_LED) += jazz_led.o
-
-# Xilinx devices
-common-obj-$(CONFIG_XILINX) += xilinx_intc.o
-common-obj-$(CONFIG_XILINX) += xilinx_timer.o
-common-obj-$(CONFIG_XILINX) += xilinx_uartlite.o
-common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o
-common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
-common-obj-$(CONFIG_XILINX_AXI) += stream.o
-
-# PKUnity SoC devices
-common-obj-$(CONFIG_PUV3) += puv3_intc.o
-common-obj-$(CONFIG_PUV3) += puv3_ost.o
-common-obj-$(CONFIG_PUV3) += puv3_gpio.o
-common-obj-$(CONFIG_PUV3) += puv3_pm.o
-common-obj-$(CONFIG_PUV3) += puv3_dma.o
-
-# ARM devices
-common-obj-$(CONFIG_ARM_TIMER) += arm_timer.o
-common-obj-$(CONFIG_PL011) += pl011.o
-common-obj-$(CONFIG_PL022) += pl022.o
-common-obj-$(CONFIG_PL031) += pl031.o
-common-obj-$(CONFIG_PL041) += pl041.o lm4549.o
-common-obj-$(CONFIG_PL050) += pl050.o
-common-obj-$(CONFIG_PL061) += pl061.o
-common-obj-$(CONFIG_PL080) += pl080.o
-common-obj-$(CONFIG_PL110) += pl110.o
-common-obj-$(CONFIG_PL181) += pl181.o
-common-obj-$(CONFIG_PL190) += pl190.o
-common-obj-$(CONFIG_PL310) += arm_l2x0.o
-common-obj-$(CONFIG_PL330) += pl330.o
-common-obj-$(CONFIG_VERSATILE_PCI) += versatile_pci.o
-common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o
-common-obj-$(CONFIG_CADENCE) += cadence_uart.o
-common-obj-$(CONFIG_CADENCE) += cadence_ttc.o
-common-obj-$(CONFIG_CADENCE) += cadence_gem.o
-common-obj-$(CONFIG_XGMAC) += xgmac.o
-
-# PCI watchdog devices
-common-obj-$(CONFIG_PCI) += wdt_i6300esb.o
-
-# IndustryPack
-common-obj-$(CONFIG_IPACK) += tpci200.o ipoctal232.o ipack.o
-
-# PCI network cards
-common-obj-$(CONFIG_NE2000_PCI) += ne2000.o
-common-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o
-common-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o
-common-obj-$(CONFIG_PCNET_COMMON) += pcnet.o
-common-obj-$(CONFIG_E1000_PCI) += e1000.o
-common-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o
-common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet_tx_pkt.o vmxnet_rx_pkt.o
-common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet3.o
-
-common-obj-$(CONFIG_SMC91C111) += smc91c111.o
-common-obj-$(CONFIG_LAN9118) += lan9118.o
-common-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o
-common-obj-$(CONFIG_OPENCORES_ETH) += opencores_eth.o
-
-# SCSI layer
-common-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o
-common-obj-$(CONFIG_MEGASAS_SCSI_PCI) += megasas.o
-common-obj-$(CONFIG_ESP) += esp.o
-common-obj-$(CONFIG_ESP_PCI) += esp-pci.o
-
-common-obj-y += sysbus.o isa-bus.o
-common-obj-y += qdev-addr.o
-
-# VGA
-common-obj-$(CONFIG_VGA_PCI) += vga-pci.o
-common-obj-$(CONFIG_VGA_ISA) += vga-isa.o
-common-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o
-common-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o
-common-obj-$(CONFIG_VMMOUSE) += vmmouse.o
-common-obj-$(CONFIG_VGA_CIRRUS) += cirrus_vga.o
-
-common-obj-$(CONFIG_RC4030) += rc4030.o
-common-obj-$(CONFIG_DP8393X) += dp8393x.o
-common-obj-$(CONFIG_DS1225Y) += ds1225y.o
-common-obj-$(CONFIG_MIPSNET) += mipsnet.o
-
-common-obj-y += null-machine.o
-
-# Sound
-sound-obj-y =
-sound-obj-$(CONFIG_SB16) += sb16.o
-sound-obj-$(CONFIG_ES1370) += es1370.o
-sound-obj-$(CONFIG_AC97) += ac97.o
-sound-obj-$(CONFIG_ADLIB) += fmopl.o adlib.o
-sound-obj-$(CONFIG_GUS) += gus.o gusemu_hal.o gusemu_mixer.o
-sound-obj-$(CONFIG_CS4231A) += cs4231a.o
-sound-obj-$(CONFIG_HDA) += intel-hda.o hda-audio.o
-
-$(obj)/adlib.o $(obj)/fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0
-
-common-obj-$(CONFIG_SOUND) += $(sound-obj-y)
-
-common-obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/
-
-common-obj-$(CONFIG_PTIMER) += ptimer.o
-common-obj-$(CONFIG_MAX7310) += max7310.o
-common-obj-$(CONFIG_WM8750) += wm8750.o
-common-obj-$(CONFIG_TWL92230) += twl92230.o
-common-obj-$(CONFIG_TSC2005) += tsc2005.o
-common-obj-$(CONFIG_LM832X) += lm832x.o
-common-obj-$(CONFIG_TMP105) += tmp105.o
-common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o
-common-obj-$(CONFIG_SSD0303) += ssd0303.o
-common-obj-$(CONFIG_SSD0323) += ssd0323.o
-common-obj-$(CONFIG_ADS7846) += ads7846.o
-common-obj-$(CONFIG_MAX111X) += max111x.o
-common-obj-$(CONFIG_DS1338) += ds1338.o
-common-obj-y += i2c.o smbus.o smbus_eeprom.o
-common-obj-y += eeprom93xx.o
-common-obj-y += scsi-disk.o cdrom.o hd-geometry.o block-common.o
-common-obj-y += scsi-generic.o scsi-bus.o
-common-obj-y += hid.o
-common-obj-$(CONFIG_SSI) += ssi.o
-common-obj-$(CONFIG_SSI_M25P80) += m25p80.o
-common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
-common-obj-$(CONFIG_SD) += sd.o
-common-obj-y += bt.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o
-common-obj-y += bt-hci-csr.o
-common-obj-y += ps2.o
-common-obj-y += qdev-properties-system.o
-
-# xen backend driver support
-common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o
-common-obj-$(CONFIG_XEN_BACKEND) += xen_console.o xenfb.o xen_disk.o xen_nic.o
 
 # Per-target files
 # virtio has to be here due to weird dependency between PCI and virtio-net.
diff --git a/hw/ac97.c b/hw/ac97.c
deleted file mode 100644 (file)
index ab68ec6..0000000
--- a/hw/ac97.c
+++ /dev/null
@@ -1,1438 +0,0 @@
-/*
- * Copyright (C) 2006 InnoTek Systemberatung GmbH
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License as published by the Free Software Foundation,
- * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
- * distribution. VirtualBox OSE is distributed in the hope that it will
- * be useful, but WITHOUT ANY WARRANTY of any kind.
- *
- * If you received this file as part of a commercial VirtualBox
- * distribution, then only the terms of your commercial VirtualBox
- * license agreement apply instead of the previous paragraph.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "hw/hw.h"
-#include "hw/audio/audio.h"
-#include "audio/audio.h"
-#include "hw/pci/pci.h"
-#include "sysemu/dma.h"
-
-enum {
-    AC97_Reset                     = 0x00,
-    AC97_Master_Volume_Mute        = 0x02,
-    AC97_Headphone_Volume_Mute     = 0x04,
-    AC97_Master_Volume_Mono_Mute   = 0x06,
-    AC97_Master_Tone_RL            = 0x08,
-    AC97_PC_BEEP_Volume_Mute       = 0x0A,
-    AC97_Phone_Volume_Mute         = 0x0C,
-    AC97_Mic_Volume_Mute           = 0x0E,
-    AC97_Line_In_Volume_Mute       = 0x10,
-    AC97_CD_Volume_Mute            = 0x12,
-    AC97_Video_Volume_Mute         = 0x14,
-    AC97_Aux_Volume_Mute           = 0x16,
-    AC97_PCM_Out_Volume_Mute       = 0x18,
-    AC97_Record_Select             = 0x1A,
-    AC97_Record_Gain_Mute          = 0x1C,
-    AC97_Record_Gain_Mic_Mute      = 0x1E,
-    AC97_General_Purpose           = 0x20,
-    AC97_3D_Control                = 0x22,
-    AC97_AC_97_RESERVED            = 0x24,
-    AC97_Powerdown_Ctrl_Stat       = 0x26,
-    AC97_Extended_Audio_ID         = 0x28,
-    AC97_Extended_Audio_Ctrl_Stat  = 0x2A,
-    AC97_PCM_Front_DAC_Rate        = 0x2C,
-    AC97_PCM_Surround_DAC_Rate     = 0x2E,
-    AC97_PCM_LFE_DAC_Rate          = 0x30,
-    AC97_PCM_LR_ADC_Rate           = 0x32,
-    AC97_MIC_ADC_Rate              = 0x34,
-    AC97_6Ch_Vol_C_LFE_Mute        = 0x36,
-    AC97_6Ch_Vol_L_R_Surround_Mute = 0x38,
-    AC97_Vendor_Reserved           = 0x58,
-    AC97_Sigmatel_Analog           = 0x6c, /* We emulate a Sigmatel codec */
-    AC97_Sigmatel_Dac2Invert       = 0x6e, /* We emulate a Sigmatel codec */
-    AC97_Vendor_ID1                = 0x7c,
-    AC97_Vendor_ID2                = 0x7e
-};
-
-#define SOFT_VOLUME
-#define SR_FIFOE 16             /* rwc */
-#define SR_BCIS  8              /* rwc */
-#define SR_LVBCI 4              /* rwc */
-#define SR_CELV  2              /* ro */
-#define SR_DCH   1              /* ro */
-#define SR_VALID_MASK ((1 << 5) - 1)
-#define SR_WCLEAR_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI)
-#define SR_RO_MASK (SR_DCH | SR_CELV)
-#define SR_INT_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI)
-
-#define CR_IOCE  16             /* rw */
-#define CR_FEIE  8              /* rw */
-#define CR_LVBIE 4              /* rw */
-#define CR_RR    2              /* rw */
-#define CR_RPBM  1              /* rw */
-#define CR_VALID_MASK ((1 << 5) - 1)
-#define CR_DONT_CLEAR_MASK (CR_IOCE | CR_FEIE | CR_LVBIE)
-
-#define GC_WR    4              /* rw */
-#define GC_CR    2              /* rw */
-#define GC_VALID_MASK ((1 << 6) - 1)
-
-#define GS_MD3   (1<<17)        /* rw */
-#define GS_AD3   (1<<16)        /* rw */
-#define GS_RCS   (1<<15)        /* rwc */
-#define GS_B3S12 (1<<14)        /* ro */
-#define GS_B2S12 (1<<13)        /* ro */
-#define GS_B1S12 (1<<12)        /* ro */
-#define GS_S1R1  (1<<11)        /* rwc */
-#define GS_S0R1  (1<<10)        /* rwc */
-#define GS_S1CR  (1<<9)         /* ro */
-#define GS_S0CR  (1<<8)         /* ro */
-#define GS_MINT  (1<<7)         /* ro */
-#define GS_POINT (1<<6)         /* ro */
-#define GS_PIINT (1<<5)         /* ro */
-#define GS_RSRVD ((1<<4)|(1<<3))
-#define GS_MOINT (1<<2)         /* ro */
-#define GS_MIINT (1<<1)         /* ro */
-#define GS_GSCI  1              /* rwc */
-#define GS_RO_MASK (GS_B3S12|                   \
-                    GS_B2S12|                   \
-                    GS_B1S12|                   \
-                    GS_S1CR|                    \
-                    GS_S0CR|                    \
-                    GS_MINT|                    \
-                    GS_POINT|                   \
-                    GS_PIINT|                   \
-                    GS_RSRVD|                   \
-                    GS_MOINT|                   \
-                    GS_MIINT)
-#define GS_VALID_MASK ((1 << 18) - 1)
-#define GS_WCLEAR_MASK (GS_RCS|GS_S1R1|GS_S0R1|GS_GSCI)
-
-#define BD_IOC (1<<31)
-#define BD_BUP (1<<30)
-
-#define EACS_VRA 1
-#define EACS_VRM 8
-
-#define MUTE_SHIFT 15
-
-#define REC_MASK 7
-enum {
-    REC_MIC = 0,
-    REC_CD,
-    REC_VIDEO,
-    REC_AUX,
-    REC_LINE_IN,
-    REC_STEREO_MIX,
-    REC_MONO_MIX,
-    REC_PHONE
-};
-
-typedef struct BD {
-    uint32_t addr;
-    uint32_t ctl_len;
-} BD;
-
-typedef struct AC97BusMasterRegs {
-    uint32_t bdbar;             /* rw 0 */
-    uint8_t civ;                /* ro 0 */
-    uint8_t lvi;                /* rw 0 */
-    uint16_t sr;                /* rw 1 */
-    uint16_t picb;              /* ro 0 */
-    uint8_t piv;                /* ro 0 */
-    uint8_t cr;                 /* rw 0 */
-    unsigned int bd_valid;
-    BD bd;
-} AC97BusMasterRegs;
-
-typedef struct AC97LinkState {
-    PCIDevice dev;
-    QEMUSoundCard card;
-    uint32_t use_broken_id;
-    uint32_t glob_cnt;
-    uint32_t glob_sta;
-    uint32_t cas;
-    uint32_t last_samp;
-    AC97BusMasterRegs bm_regs[3];
-    uint8_t mixer_data[256];
-    SWVoiceIn *voice_pi;
-    SWVoiceOut *voice_po;
-    SWVoiceIn *voice_mc;
-    int invalid_freq[3];
-    uint8_t silence[128];
-    int bup_flag;
-    MemoryRegion io_nam;
-    MemoryRegion io_nabm;
-} AC97LinkState;
-
-enum {
-    BUP_SET = 1,
-    BUP_LAST = 2
-};
-
-#ifdef DEBUG_AC97
-#define dolog(...) AUD_log ("ac97", __VA_ARGS__)
-#else
-#define dolog(...)
-#endif
-
-#define MKREGS(prefix, start)                   \
-enum {                                          \
-    prefix ## _BDBAR = start,                   \
-    prefix ## _CIV = start + 4,                 \
-    prefix ## _LVI = start + 5,                 \
-    prefix ## _SR = start + 6,                  \
-    prefix ## _PICB = start + 8,                \
-    prefix ## _PIV = start + 10,                \
-    prefix ## _CR = start + 11                  \
-}
-
-enum {
-    PI_INDEX = 0,
-    PO_INDEX,
-    MC_INDEX,
-    LAST_INDEX
-};
-
-MKREGS (PI, PI_INDEX * 16);
-MKREGS (PO, PO_INDEX * 16);
-MKREGS (MC, MC_INDEX * 16);
-
-enum {
-    GLOB_CNT = 0x2c,
-    GLOB_STA = 0x30,
-    CAS      = 0x34
-};
-
-#define GET_BM(index) (((index) >> 4) & 3)
-
-static void po_callback (void *opaque, int free);
-static void pi_callback (void *opaque, int avail);
-static void mc_callback (void *opaque, int avail);
-
-static void warm_reset (AC97LinkState *s)
-{
-    (void) s;
-}
-
-static void cold_reset (AC97LinkState * s)
-{
-    (void) s;
-}
-
-static void fetch_bd (AC97LinkState *s, AC97BusMasterRegs *r)
-{
-    uint8_t b[8];
-
-    pci_dma_read (&s->dev, r->bdbar + r->civ * 8, b, 8);
-    r->bd_valid = 1;
-    r->bd.addr = le32_to_cpu (*(uint32_t *) &b[0]) & ~3;
-    r->bd.ctl_len = le32_to_cpu (*(uint32_t *) &b[4]);
-    r->picb = r->bd.ctl_len & 0xffff;
-    dolog ("bd %2d addr=%#x ctl=%#06x len=%#x(%d bytes)\n",
-           r->civ, r->bd.addr, r->bd.ctl_len >> 16,
-           r->bd.ctl_len & 0xffff,
-           (r->bd.ctl_len & 0xffff) << 1);
-}
-
-static void update_sr (AC97LinkState *s, AC97BusMasterRegs *r, uint32_t new_sr)
-{
-    int event = 0;
-    int level = 0;
-    uint32_t new_mask = new_sr & SR_INT_MASK;
-    uint32_t old_mask = r->sr & SR_INT_MASK;
-    uint32_t masks[] = {GS_PIINT, GS_POINT, GS_MINT};
-
-    if (new_mask ^ old_mask) {
-        /** @todo is IRQ deasserted when only one of status bits is cleared? */
-        if (!new_mask) {
-            event = 1;
-            level = 0;
-        }
-        else {
-            if ((new_mask & SR_LVBCI) && (r->cr & CR_LVBIE)) {
-                event = 1;
-                level = 1;
-            }
-            if ((new_mask & SR_BCIS) && (r->cr & CR_IOCE)) {
-                event = 1;
-                level = 1;
-            }
-        }
-    }
-
-    r->sr = new_sr;
-
-    dolog ("IOC%d LVB%d sr=%#x event=%d level=%d\n",
-           r->sr & SR_BCIS, r->sr & SR_LVBCI,
-           r->sr,
-           event, level);
-
-    if (!event)
-        return;
-
-    if (level) {
-        s->glob_sta |= masks[r - s->bm_regs];
-        dolog ("set irq level=1\n");
-        qemu_set_irq (s->dev.irq[0], 1);
-    }
-    else {
-        s->glob_sta &= ~masks[r - s->bm_regs];
-        dolog ("set irq level=0\n");
-        qemu_set_irq (s->dev.irq[0], 0);
-    }
-}
-
-static void voice_set_active (AC97LinkState *s, int bm_index, int on)
-{
-    switch (bm_index) {
-    case PI_INDEX:
-        AUD_set_active_in (s->voice_pi, on);
-        break;
-
-    case PO_INDEX:
-        AUD_set_active_out (s->voice_po, on);
-        break;
-
-    case MC_INDEX:
-        AUD_set_active_in (s->voice_mc, on);
-        break;
-
-    default:
-        AUD_log ("ac97", "invalid bm_index(%d) in voice_set_active", bm_index);
-        break;
-    }
-}
-
-static void reset_bm_regs (AC97LinkState *s, AC97BusMasterRegs *r)
-{
-    dolog ("reset_bm_regs\n");
-    r->bdbar = 0;
-    r->civ = 0;
-    r->lvi = 0;
-    /** todo do we need to do that? */
-    update_sr (s, r, SR_DCH);
-    r->picb = 0;
-    r->piv = 0;
-    r->cr = r->cr & CR_DONT_CLEAR_MASK;
-    r->bd_valid = 0;
-
-    voice_set_active (s, r - s->bm_regs, 0);
-    memset (s->silence, 0, sizeof (s->silence));
-}
-
-static void mixer_store (AC97LinkState *s, uint32_t i, uint16_t v)
-{
-    if (i + 2 > sizeof (s->mixer_data)) {
-        dolog ("mixer_store: index %d out of bounds %zd\n",
-               i, sizeof (s->mixer_data));
-        return;
-    }
-
-    s->mixer_data[i + 0] = v & 0xff;
-    s->mixer_data[i + 1] = v >> 8;
-}
-
-static uint16_t mixer_load (AC97LinkState *s, uint32_t i)
-{
-    uint16_t val = 0xffff;
-
-    if (i + 2 > sizeof (s->mixer_data)) {
-        dolog ("mixer_load: index %d out of bounds %zd\n",
-               i, sizeof (s->mixer_data));
-    }
-    else {
-        val = s->mixer_data[i + 0] | (s->mixer_data[i + 1] << 8);
-    }
-
-    return val;
-}
-
-static void open_voice (AC97LinkState *s, int index, int freq)
-{
-    struct audsettings as;
-
-    as.freq = freq;
-    as.nchannels = 2;
-    as.fmt = AUD_FMT_S16;
-    as.endianness = 0;
-
-    if (freq > 0) {
-        s->invalid_freq[index] = 0;
-        switch (index) {
-        case PI_INDEX:
-            s->voice_pi = AUD_open_in (
-                &s->card,
-                s->voice_pi,
-                "ac97.pi",
-                s,
-                pi_callback,
-                &as
-                );
-            break;
-
-        case PO_INDEX:
-            s->voice_po = AUD_open_out (
-                &s->card,
-                s->voice_po,
-                "ac97.po",
-                s,
-                po_callback,
-                &as
-                );
-            break;
-
-        case MC_INDEX:
-            s->voice_mc = AUD_open_in (
-                &s->card,
-                s->voice_mc,
-                "ac97.mc",
-                s,
-                mc_callback,
-                &as
-                );
-            break;
-        }
-    }
-    else {
-        s->invalid_freq[index] = freq;
-        switch (index) {
-        case PI_INDEX:
-            AUD_close_in (&s->card, s->voice_pi);
-            s->voice_pi = NULL;
-            break;
-
-        case PO_INDEX:
-            AUD_close_out (&s->card, s->voice_po);
-            s->voice_po = NULL;
-            break;
-
-        case MC_INDEX:
-            AUD_close_in (&s->card, s->voice_mc);
-            s->voice_mc = NULL;
-            break;
-        }
-    }
-}
-
-static void reset_voices (AC97LinkState *s, uint8_t active[LAST_INDEX])
-{
-    uint16_t freq;
-
-    freq = mixer_load (s, AC97_PCM_LR_ADC_Rate);
-    open_voice (s, PI_INDEX, freq);
-    AUD_set_active_in (s->voice_pi, active[PI_INDEX]);
-
-    freq = mixer_load (s, AC97_PCM_Front_DAC_Rate);
-    open_voice (s, PO_INDEX, freq);
-    AUD_set_active_out (s->voice_po, active[PO_INDEX]);
-
-    freq = mixer_load (s, AC97_MIC_ADC_Rate);
-    open_voice (s, MC_INDEX, freq);
-    AUD_set_active_in (s->voice_mc, active[MC_INDEX]);
-}
-
-static void get_volume (uint16_t vol, uint16_t mask, int inverse,
-                        int *mute, uint8_t *lvol, uint8_t *rvol)
-{
-    *mute = (vol >> MUTE_SHIFT) & 1;
-    *rvol = (255 * (vol & mask)) / mask;
-    *lvol = (255 * ((vol >> 8) & mask)) / mask;
-
-    if (inverse) {
-        *rvol = 255 - *rvol;
-        *lvol = 255 - *lvol;
-    }
-}
-
-static void update_combined_volume_out (AC97LinkState *s)
-{
-    uint8_t lvol, rvol, plvol, prvol;
-    int mute, pmute;
-
-    get_volume (mixer_load (s, AC97_Master_Volume_Mute), 0x3f, 1,
-                &mute, &lvol, &rvol);
-    get_volume (mixer_load (s, AC97_PCM_Out_Volume_Mute), 0x1f, 1,
-                &pmute, &plvol, &prvol);
-
-    mute = mute | pmute;
-    lvol = (lvol * plvol) / 255;
-    rvol = (rvol * prvol) / 255;
-
-    AUD_set_volume_out (s->voice_po, mute, lvol, rvol);
-}
-
-static void update_volume_in (AC97LinkState *s)
-{
-    uint8_t lvol, rvol;
-    int mute;
-
-    get_volume (mixer_load (s, AC97_Record_Gain_Mute), 0x0f, 0,
-                &mute, &lvol, &rvol);
-
-    AUD_set_volume_in (s->voice_pi, mute, lvol, rvol);
-}
-
-static void set_volume (AC97LinkState *s, int index, uint32_t val)
-{
-    switch (index) {
-    case AC97_Master_Volume_Mute:
-        val &= 0xbf3f;
-        mixer_store (s, index, val);
-        update_combined_volume_out (s);
-        break;
-    case AC97_PCM_Out_Volume_Mute:
-        val &= 0x9f1f;
-        mixer_store (s, index, val);
-        update_combined_volume_out (s);
-        break;
-    case AC97_Record_Gain_Mute:
-        val &= 0x8f0f;
-        mixer_store (s, index, val);
-        update_volume_in (s);
-        break;
-    }
-}
-
-static void record_select (AC97LinkState *s, uint32_t val)
-{
-    uint8_t rs = val & REC_MASK;
-    uint8_t ls = (val >> 8) & REC_MASK;
-    mixer_store (s, AC97_Record_Select, rs | (ls << 8));
-}
-
-static void mixer_reset (AC97LinkState *s)
-{
-    uint8_t active[LAST_INDEX];
-
-    dolog ("mixer_reset\n");
-    memset (s->mixer_data, 0, sizeof (s->mixer_data));
-    memset (active, 0, sizeof (active));
-    mixer_store (s, AC97_Reset                   , 0x0000); /* 6940 */
-    mixer_store (s, AC97_Headphone_Volume_Mute   , 0x0000);
-    mixer_store (s, AC97_Master_Volume_Mono_Mute , 0x0000);
-    mixer_store (s, AC97_Master_Tone_RL,           0x0000);
-    mixer_store (s, AC97_PC_BEEP_Volume_Mute     , 0x0000);
-    mixer_store (s, AC97_Phone_Volume_Mute       , 0x0000);
-    mixer_store (s, AC97_Mic_Volume_Mute         , 0x0000);
-    mixer_store (s, AC97_Line_In_Volume_Mute     , 0x0000);
-    mixer_store (s, AC97_CD_Volume_Mute          , 0x0000);
-    mixer_store (s, AC97_Video_Volume_Mute       , 0x0000);
-    mixer_store (s, AC97_Aux_Volume_Mute         , 0x0000);
-    mixer_store (s, AC97_Record_Gain_Mic_Mute    , 0x0000);
-    mixer_store (s, AC97_General_Purpose         , 0x0000);
-    mixer_store (s, AC97_3D_Control              , 0x0000);
-    mixer_store (s, AC97_Powerdown_Ctrl_Stat     , 0x000f);
-
-    /*
-     * Sigmatel 9700 (STAC9700)
-     */
-    mixer_store (s, AC97_Vendor_ID1              , 0x8384);
-    mixer_store (s, AC97_Vendor_ID2              , 0x7600); /* 7608 */
-
-    mixer_store (s, AC97_Extended_Audio_ID       , 0x0809);
-    mixer_store (s, AC97_Extended_Audio_Ctrl_Stat, 0x0009);
-    mixer_store (s, AC97_PCM_Front_DAC_Rate      , 0xbb80);
-    mixer_store (s, AC97_PCM_Surround_DAC_Rate   , 0xbb80);
-    mixer_store (s, AC97_PCM_LFE_DAC_Rate        , 0xbb80);
-    mixer_store (s, AC97_PCM_LR_ADC_Rate         , 0xbb80);
-    mixer_store (s, AC97_MIC_ADC_Rate            , 0xbb80);
-
-    record_select (s, 0);
-    set_volume (s, AC97_Master_Volume_Mute, 0x8000);
-    set_volume (s, AC97_PCM_Out_Volume_Mute, 0x8808);
-    set_volume (s, AC97_Record_Gain_Mute, 0x8808);
-
-    reset_voices (s, active);
-}
-
-/**
- * Native audio mixer
- * I/O Reads
- */
-static uint32_t nam_readb (void *opaque, uint32_t addr)
-{
-    AC97LinkState *s = opaque;
-    dolog ("U nam readb %#x\n", addr);
-    s->cas = 0;
-    return ~0U;
-}
-
-static uint32_t nam_readw (void *opaque, uint32_t addr)
-{
-    AC97LinkState *s = opaque;
-    uint32_t val = ~0U;
-    uint32_t index = addr;
-    s->cas = 0;
-    val = mixer_load (s, index);
-    return val;
-}
-
-static uint32_t nam_readl (void *opaque, uint32_t addr)
-{
-    AC97LinkState *s = opaque;
-    dolog ("U nam readl %#x\n", addr);
-    s->cas = 0;
-    return ~0U;
-}
-
-/**
- * Native audio mixer
- * I/O Writes
- */
-static void nam_writeb (void *opaque, uint32_t addr, uint32_t val)
-{
-    AC97LinkState *s = opaque;
-    dolog ("U nam writeb %#x <- %#x\n", addr, val);
-    s->cas = 0;
-}
-
-static void nam_writew (void *opaque, uint32_t addr, uint32_t val)
-{
-    AC97LinkState *s = opaque;
-    uint32_t index = addr;
-    s->cas = 0;
-    switch (index) {
-    case AC97_Reset:
-        mixer_reset (s);
-        break;
-    case AC97_Powerdown_Ctrl_Stat:
-        val &= ~0x800f;
-        val |= mixer_load (s, index) & 0xf;
-        mixer_store (s, index, val);
-        break;
-    case AC97_PCM_Out_Volume_Mute:
-    case AC97_Master_Volume_Mute:
-    case AC97_Record_Gain_Mute:
-        set_volume (s, index, val);
-        break;
-    case AC97_Record_Select:
-        record_select (s, val);
-        break;
-    case AC97_Vendor_ID1:
-    case AC97_Vendor_ID2:
-        dolog ("Attempt to write vendor ID to %#x\n", val);
-        break;
-    case AC97_Extended_Audio_ID:
-        dolog ("Attempt to write extended audio ID to %#x\n", val);
-        break;
-    case AC97_Extended_Audio_Ctrl_Stat:
-        if (!(val & EACS_VRA)) {
-            mixer_store (s, AC97_PCM_Front_DAC_Rate, 0xbb80);
-            mixer_store (s, AC97_PCM_LR_ADC_Rate,    0xbb80);
-            open_voice (s, PI_INDEX, 48000);
-            open_voice (s, PO_INDEX, 48000);
-        }
-        if (!(val & EACS_VRM)) {
-            mixer_store (s, AC97_MIC_ADC_Rate, 0xbb80);
-            open_voice (s, MC_INDEX, 48000);
-        }
-        dolog ("Setting extended audio control to %#x\n", val);
-        mixer_store (s, AC97_Extended_Audio_Ctrl_Stat, val);
-        break;
-    case AC97_PCM_Front_DAC_Rate:
-        if (mixer_load (s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) {
-            mixer_store (s, index, val);
-            dolog ("Set front DAC rate to %d\n", val);
-            open_voice (s, PO_INDEX, val);
-        }
-        else {
-            dolog ("Attempt to set front DAC rate to %d, "
-                   "but VRA is not set\n",
-                   val);
-        }
-        break;
-    case AC97_MIC_ADC_Rate:
-        if (mixer_load (s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRM) {
-            mixer_store (s, index, val);
-            dolog ("Set MIC ADC rate to %d\n", val);
-            open_voice (s, MC_INDEX, val);
-        }
-        else {
-            dolog ("Attempt to set MIC ADC rate to %d, "
-                   "but VRM is not set\n",
-                   val);
-        }
-        break;
-    case AC97_PCM_LR_ADC_Rate:
-        if (mixer_load (s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) {
-            mixer_store (s, index, val);
-            dolog ("Set front LR ADC rate to %d\n", val);
-            open_voice (s, PI_INDEX, val);
-        }
-        else {
-            dolog ("Attempt to set LR ADC rate to %d, but VRA is not set\n",
-                    val);
-        }
-        break;
-    case AC97_Headphone_Volume_Mute:
-    case AC97_Master_Volume_Mono_Mute:
-    case AC97_Master_Tone_RL:
-    case AC97_PC_BEEP_Volume_Mute:
-    case AC97_Phone_Volume_Mute:
-    case AC97_Mic_Volume_Mute:
-    case AC97_Line_In_Volume_Mute:
-    case AC97_CD_Volume_Mute:
-    case AC97_Video_Volume_Mute:
-    case AC97_Aux_Volume_Mute:
-    case AC97_Record_Gain_Mic_Mute:
-    case AC97_General_Purpose:
-    case AC97_3D_Control:
-    case AC97_Sigmatel_Analog:
-    case AC97_Sigmatel_Dac2Invert:
-        /* None of the features in these regs are emulated, so they are RO */
-        break;
-    default:
-        dolog ("U nam writew %#x <- %#x\n", addr, val);
-        mixer_store (s, index, val);
-        break;
-    }
-}
-
-static void nam_writel (void *opaque, uint32_t addr, uint32_t val)
-{
-    AC97LinkState *s = opaque;
-    dolog ("U nam writel %#x <- %#x\n", addr, val);
-    s->cas = 0;
-}
-
-/**
- * Native audio bus master
- * I/O Reads
- */
-static uint32_t nabm_readb (void *opaque, uint32_t addr)
-{
-    AC97LinkState *s = opaque;
-    AC97BusMasterRegs *r = NULL;
-    uint32_t index = addr;
-    uint32_t val = ~0U;
-
-    switch (index) {
-    case CAS:
-        dolog ("CAS %d\n", s->cas);
-        val = s->cas;
-        s->cas = 1;
-        break;
-    case PI_CIV:
-    case PO_CIV:
-    case MC_CIV:
-        r = &s->bm_regs[GET_BM (index)];
-        val = r->civ;
-        dolog ("CIV[%d] -> %#x\n", GET_BM (index), val);
-        break;
-    case PI_LVI:
-    case PO_LVI:
-    case MC_LVI:
-        r = &s->bm_regs[GET_BM (index)];
-        val = r->lvi;
-        dolog ("LVI[%d] -> %#x\n", GET_BM (index), val);
-        break;
-    case PI_PIV:
-    case PO_PIV:
-    case MC_PIV:
-        r = &s->bm_regs[GET_BM (index)];
-        val = r->piv;
-        dolog ("PIV[%d] -> %#x\n", GET_BM (index), val);
-        break;
-    case PI_CR:
-    case PO_CR:
-    case MC_CR:
-        r = &s->bm_regs[GET_BM (index)];
-        val = r->cr;
-        dolog ("CR[%d] -> %#x\n", GET_BM (index), val);
-        break;
-    case PI_SR:
-    case PO_SR:
-    case MC_SR:
-        r = &s->bm_regs[GET_BM (index)];
-        val = r->sr & 0xff;
-        dolog ("SRb[%d] -> %#x\n", GET_BM (index), val);
-        break;
-    default:
-        dolog ("U nabm readb %#x -> %#x\n", addr, val);
-        break;
-    }
-    return val;
-}
-
-static uint32_t nabm_readw (void *opaque, uint32_t addr)
-{
-    AC97LinkState *s = opaque;
-    AC97BusMasterRegs *r = NULL;
-    uint32_t index = addr;
-    uint32_t val = ~0U;
-
-    switch (index) {
-    case PI_SR:
-    case PO_SR:
-    case MC_SR:
-        r = &s->bm_regs[GET_BM (index)];
-        val = r->sr;
-        dolog ("SR[%d] -> %#x\n", GET_BM (index), val);
-        break;
-    case PI_PICB:
-    case PO_PICB:
-    case MC_PICB:
-        r = &s->bm_regs[GET_BM (index)];
-        val = r->picb;
-        dolog ("PICB[%d] -> %#x\n", GET_BM (index), val);
-        break;
-    default:
-        dolog ("U nabm readw %#x -> %#x\n", addr, val);
-        break;
-    }
-    return val;
-}
-
-static uint32_t nabm_readl (void *opaque, uint32_t addr)
-{
-    AC97LinkState *s = opaque;
-    AC97BusMasterRegs *r = NULL;
-    uint32_t index = addr;
-    uint32_t val = ~0U;
-
-    switch (index) {
-    case PI_BDBAR:
-    case PO_BDBAR:
-    case MC_BDBAR:
-        r = &s->bm_regs[GET_BM (index)];
-        val = r->bdbar;
-        dolog ("BMADDR[%d] -> %#x\n", GET_BM (index), val);
-        break;
-    case PI_CIV:
-    case PO_CIV:
-    case MC_CIV:
-        r = &s->bm_regs[GET_BM (index)];
-        val = r->civ | (r->lvi << 8) | (r->sr << 16);
-        dolog ("CIV LVI SR[%d] -> %#x, %#x, %#x\n", GET_BM (index),
-               r->civ, r->lvi, r->sr);
-        break;
-    case PI_PICB:
-    case PO_PICB:
-    case MC_PICB:
-        r = &s->bm_regs[GET_BM (index)];
-        val = r->picb | (r->piv << 16) | (r->cr << 24);
-        dolog ("PICB PIV CR[%d] -> %#x %#x %#x %#x\n", GET_BM (index),
-               val, r->picb, r->piv, r->cr);
-        break;
-    case GLOB_CNT:
-        val = s->glob_cnt;
-        dolog ("glob_cnt -> %#x\n", val);
-        break;
-    case GLOB_STA:
-        val = s->glob_sta | GS_S0CR;
-        dolog ("glob_sta -> %#x\n", val);
-        break;
-    default:
-        dolog ("U nabm readl %#x -> %#x\n", addr, val);
-        break;
-    }
-    return val;
-}
-
-/**
- * Native audio bus master
- * I/O Writes
- */
-static void nabm_writeb (void *opaque, uint32_t addr, uint32_t val)
-{
-    AC97LinkState *s = opaque;
-    AC97BusMasterRegs *r = NULL;
-    uint32_t index = addr;
-    switch (index) {
-    case PI_LVI:
-    case PO_LVI:
-    case MC_LVI:
-        r = &s->bm_regs[GET_BM (index)];
-        if ((r->cr & CR_RPBM) && (r->sr & SR_DCH)) {
-            r->sr &= ~(SR_DCH | SR_CELV);
-            r->civ = r->piv;
-            r->piv = (r->piv + 1) % 32;
-            fetch_bd (s, r);
-        }
-        r->lvi = val % 32;
-        dolog ("LVI[%d] <- %#x\n", GET_BM (index), val);
-        break;
-    case PI_CR:
-    case PO_CR:
-    case MC_CR:
-        r = &s->bm_regs[GET_BM (index)];
-        if (val & CR_RR) {
-            reset_bm_regs (s, r);
-        }
-        else {
-            r->cr = val & CR_VALID_MASK;
-            if (!(r->cr & CR_RPBM)) {
-                voice_set_active (s, r - s->bm_regs, 0);
-                r->sr |= SR_DCH;
-            }
-            else {
-                r->civ = r->piv;
-                r->piv = (r->piv + 1) % 32;
-                fetch_bd (s, r);
-                r->sr &= ~SR_DCH;
-                voice_set_active (s, r - s->bm_regs, 1);
-            }
-        }
-        dolog ("CR[%d] <- %#x (cr %#x)\n", GET_BM (index), val, r->cr);
-        break;
-    case PI_SR:
-    case PO_SR:
-    case MC_SR:
-        r = &s->bm_regs[GET_BM (index)];
-        r->sr |= val & ~(SR_RO_MASK | SR_WCLEAR_MASK);
-        update_sr (s, r, r->sr & ~(val & SR_WCLEAR_MASK));
-        dolog ("SR[%d] <- %#x (sr %#x)\n", GET_BM (index), val, r->sr);
-        break;
-    default:
-        dolog ("U nabm writeb %#x <- %#x\n", addr, val);
-        break;
-    }
-}
-
-static void nabm_writew (void *opaque, uint32_t addr, uint32_t val)
-{
-    AC97LinkState *s = opaque;
-    AC97BusMasterRegs *r = NULL;
-    uint32_t index = addr;
-    switch (index) {
-    case PI_SR:
-    case PO_SR:
-    case MC_SR:
-        r = &s->bm_regs[GET_BM (index)];
-        r->sr |= val & ~(SR_RO_MASK | SR_WCLEAR_MASK);
-        update_sr (s, r, r->sr & ~(val & SR_WCLEAR_MASK));
-        dolog ("SR[%d] <- %#x (sr %#x)\n", GET_BM (index), val, r->sr);
-        break;
-    default:
-        dolog ("U nabm writew %#x <- %#x\n", addr, val);
-        break;
-    }
-}
-
-static void nabm_writel (void *opaque, uint32_t addr, uint32_t val)
-{
-    AC97LinkState *s = opaque;
-    AC97BusMasterRegs *r = NULL;
-    uint32_t index = addr;
-    switch (index) {
-    case PI_BDBAR:
-    case PO_BDBAR:
-    case MC_BDBAR:
-        r = &s->bm_regs[GET_BM (index)];
-        r->bdbar = val & ~3;
-        dolog ("BDBAR[%d] <- %#x (bdbar %#x)\n",
-               GET_BM (index), val, r->bdbar);
-        break;
-    case GLOB_CNT:
-        if (val & GC_WR)
-            warm_reset (s);
-        if (val & GC_CR)
-            cold_reset (s);
-        if (!(val & (GC_WR | GC_CR)))
-            s->glob_cnt = val & GC_VALID_MASK;
-        dolog ("glob_cnt <- %#x (glob_cnt %#x)\n", val, s->glob_cnt);
-        break;
-    case GLOB_STA:
-        s->glob_sta &= ~(val & GS_WCLEAR_MASK);
-        s->glob_sta |= (val & ~(GS_WCLEAR_MASK | GS_RO_MASK)) & GS_VALID_MASK;
-        dolog ("glob_sta <- %#x (glob_sta %#x)\n", val, s->glob_sta);
-        break;
-    default:
-        dolog ("U nabm writel %#x <- %#x\n", addr, val);
-        break;
-    }
-}
-
-static int write_audio (AC97LinkState *s, AC97BusMasterRegs *r,
-                        int max, int *stop)
-{
-    uint8_t tmpbuf[4096];
-    uint32_t addr = r->bd.addr;
-    uint32_t temp = r->picb << 1;
-    uint32_t written = 0;
-    int to_copy = 0;
-    temp = audio_MIN (temp, max);
-
-    if (!temp) {
-        *stop = 1;
-        return 0;
-    }
-
-    while (temp) {
-        int copied;
-        to_copy = audio_MIN (temp, sizeof (tmpbuf));
-        pci_dma_read (&s->dev, addr, tmpbuf, to_copy);
-        copied = AUD_write (s->voice_po, tmpbuf, to_copy);
-        dolog ("write_audio max=%x to_copy=%x copied=%x\n",
-               max, to_copy, copied);
-        if (!copied) {
-            *stop = 1;
-            break;
-        }
-        temp -= copied;
-        addr += copied;
-        written += copied;
-    }
-
-    if (!temp) {
-        if (to_copy < 4) {
-            dolog ("whoops\n");
-            s->last_samp = 0;
-        }
-        else {
-            s->last_samp = *(uint32_t *) &tmpbuf[to_copy - 4];
-        }
-    }
-
-    r->bd.addr = addr;
-    return written;
-}
-
-static void write_bup (AC97LinkState *s, int elapsed)
-{
-    dolog ("write_bup\n");
-    if (!(s->bup_flag & BUP_SET)) {
-        if (s->bup_flag & BUP_LAST) {
-            int i;
-            uint8_t *p = s->silence;
-            for (i = 0; i < sizeof (s->silence) / 4; i++, p += 4) {
-                *(uint32_t *) p = s->last_samp;
-            }
-        }
-        else {
-            memset (s->silence, 0, sizeof (s->silence));
-        }
-        s->bup_flag |= BUP_SET;
-    }
-
-    while (elapsed) {
-        int temp = audio_MIN (elapsed, sizeof (s->silence));
-        while (temp) {
-            int copied = AUD_write (s->voice_po, s->silence, temp);
-            if (!copied)
-                return;
-            temp -= copied;
-            elapsed -= copied;
-        }
-    }
-}
-
-static int read_audio (AC97LinkState *s, AC97BusMasterRegs *r,
-                       int max, int *stop)
-{
-    uint8_t tmpbuf[4096];
-    uint32_t addr = r->bd.addr;
-    uint32_t temp = r->picb << 1;
-    uint32_t nread = 0;
-    int to_copy = 0;
-    SWVoiceIn *voice = (r - s->bm_regs) == MC_INDEX ? s->voice_mc : s->voice_pi;
-
-    temp = audio_MIN (temp, max);
-
-    if (!temp) {
-        *stop = 1;
-        return 0;
-    }
-
-    while (temp) {
-        int acquired;
-        to_copy = audio_MIN (temp, sizeof (tmpbuf));
-        acquired = AUD_read (voice, tmpbuf, to_copy);
-        if (!acquired) {
-            *stop = 1;
-            break;
-        }
-        pci_dma_write (&s->dev, addr, tmpbuf, acquired);
-        temp -= acquired;
-        addr += acquired;
-        nread += acquired;
-    }
-
-    r->bd.addr = addr;
-    return nread;
-}
-
-static void transfer_audio (AC97LinkState *s, int index, int elapsed)
-{
-    AC97BusMasterRegs *r = &s->bm_regs[index];
-    int stop = 0;
-
-    if (s->invalid_freq[index]) {
-        AUD_log ("ac97", "attempt to use voice %d with invalid frequency %d\n",
-                 index, s->invalid_freq[index]);
-        return;
-    }
-
-    if (r->sr & SR_DCH) {
-        if (r->cr & CR_RPBM) {
-            switch (index) {
-            case PO_INDEX:
-                write_bup (s, elapsed);
-                break;
-            }
-        }
-        return;
-    }
-
-    while ((elapsed >> 1) && !stop) {
-        int temp;
-
-        if (!r->bd_valid) {
-            dolog ("invalid bd\n");
-            fetch_bd (s, r);
-        }
-
-        if (!r->picb) {
-            dolog ("fresh bd %d is empty %#x %#x\n",
-                   r->civ, r->bd.addr, r->bd.ctl_len);
-            if (r->civ == r->lvi) {
-                r->sr |= SR_DCH; /* CELV? */
-                s->bup_flag = 0;
-                break;
-            }
-            r->sr &= ~SR_CELV;
-            r->civ = r->piv;
-            r->piv = (r->piv + 1) % 32;
-            fetch_bd (s, r);
-            return;
-        }
-
-        switch (index) {
-        case PO_INDEX:
-            temp = write_audio (s, r, elapsed, &stop);
-            elapsed -= temp;
-            r->picb -= (temp >> 1);
-            break;
-
-        case PI_INDEX:
-        case MC_INDEX:
-            temp = read_audio (s, r, elapsed, &stop);
-            elapsed -= temp;
-            r->picb -= (temp >> 1);
-            break;
-        }
-
-        if (!r->picb) {
-            uint32_t new_sr = r->sr & ~SR_CELV;
-
-            if (r->bd.ctl_len & BD_IOC) {
-                new_sr |= SR_BCIS;
-            }
-
-            if (r->civ == r->lvi) {
-                dolog ("Underrun civ (%d) == lvi (%d)\n", r->civ, r->lvi);
-
-                new_sr |= SR_LVBCI | SR_DCH | SR_CELV;
-                stop = 1;
-                s->bup_flag = (r->bd.ctl_len & BD_BUP) ? BUP_LAST : 0;
-            }
-            else {
-                r->civ = r->piv;
-                r->piv = (r->piv + 1) % 32;
-                fetch_bd (s, r);
-            }
-
-            update_sr (s, r, new_sr);
-        }
-    }
-}
-
-static void pi_callback (void *opaque, int avail)
-{
-    transfer_audio (opaque, PI_INDEX, avail);
-}
-
-static void mc_callback (void *opaque, int avail)
-{
-    transfer_audio (opaque, MC_INDEX, avail);
-}
-
-static void po_callback (void *opaque, int free)
-{
-    transfer_audio (opaque, PO_INDEX, free);
-}
-
-static const VMStateDescription vmstate_ac97_bm_regs = {
-    .name = "ac97_bm_regs",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT32 (bdbar, AC97BusMasterRegs),
-        VMSTATE_UINT8 (civ, AC97BusMasterRegs),
-        VMSTATE_UINT8 (lvi, AC97BusMasterRegs),
-        VMSTATE_UINT16 (sr, AC97BusMasterRegs),
-        VMSTATE_UINT16 (picb, AC97BusMasterRegs),
-        VMSTATE_UINT8 (piv, AC97BusMasterRegs),
-        VMSTATE_UINT8 (cr, AC97BusMasterRegs),
-        VMSTATE_UINT32 (bd_valid, AC97BusMasterRegs),
-        VMSTATE_UINT32 (bd.addr, AC97BusMasterRegs),
-        VMSTATE_UINT32 (bd.ctl_len, AC97BusMasterRegs),
-        VMSTATE_END_OF_LIST ()
-    }
-};
-
-static int ac97_post_load (void *opaque, int version_id)
-{
-    uint8_t active[LAST_INDEX];
-    AC97LinkState *s = opaque;
-
-    record_select (s, mixer_load (s, AC97_Record_Select));
-    set_volume (s, AC97_Master_Volume_Mute,
-                mixer_load (s, AC97_Master_Volume_Mute));
-    set_volume (s, AC97_PCM_Out_Volume_Mute,
-                mixer_load (s, AC97_PCM_Out_Volume_Mute));
-    set_volume (s, AC97_Record_Gain_Mute,
-                mixer_load (s, AC97_Record_Gain_Mute));
-
-    active[PI_INDEX] = !!(s->bm_regs[PI_INDEX].cr & CR_RPBM);
-    active[PO_INDEX] = !!(s->bm_regs[PO_INDEX].cr & CR_RPBM);
-    active[MC_INDEX] = !!(s->bm_regs[MC_INDEX].cr & CR_RPBM);
-    reset_voices (s, active);
-
-    s->bup_flag = 0;
-    s->last_samp = 0;
-    return 0;
-}
-
-static bool is_version_2 (void *opaque, int version_id)
-{
-    return version_id == 2;
-}
-
-static const VMStateDescription vmstate_ac97 = {
-    .name = "ac97",
-    .version_id = 3,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .post_load = ac97_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_PCI_DEVICE (dev, AC97LinkState),
-        VMSTATE_UINT32 (glob_cnt, AC97LinkState),
-        VMSTATE_UINT32 (glob_sta, AC97LinkState),
-        VMSTATE_UINT32 (cas, AC97LinkState),
-        VMSTATE_STRUCT_ARRAY (bm_regs, AC97LinkState, 3, 1,
-                              vmstate_ac97_bm_regs, AC97BusMasterRegs),
-        VMSTATE_BUFFER (mixer_data, AC97LinkState),
-        VMSTATE_UNUSED_TEST (is_version_2, 3),
-        VMSTATE_END_OF_LIST ()
-    }
-};
-
-static uint64_t nam_read(void *opaque, hwaddr addr, unsigned size)
-{
-    if ((addr / size) > 256) {
-        return -1;
-    }
-
-    switch (size) {
-    case 1:
-        return nam_readb(opaque, addr);
-    case 2:
-        return nam_readw(opaque, addr);
-    case 4:
-        return nam_readl(opaque, addr);
-    default:
-        return -1;
-    }
-}
-
-static void nam_write(void *opaque, hwaddr addr, uint64_t val,
-                      unsigned size)
-{
-    if ((addr / size) > 256) {
-        return;
-    }
-
-    switch (size) {
-    case 1:
-        nam_writeb(opaque, addr, val);
-        break;
-    case 2:
-        nam_writew(opaque, addr, val);
-        break;
-    case 4:
-        nam_writel(opaque, addr, val);
-        break;
-    }
-}
-
-static const MemoryRegionOps ac97_io_nam_ops = {
-    .read = nam_read,
-    .write = nam_write,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 4,
-    },
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static uint64_t nabm_read(void *opaque, hwaddr addr, unsigned size)
-{
-    if ((addr / size) > 64) {
-        return -1;
-    }
-
-    switch (size) {
-    case 1:
-        return nabm_readb(opaque, addr);
-    case 2:
-        return nabm_readw(opaque, addr);
-    case 4:
-        return nabm_readl(opaque, addr);
-    default:
-        return -1;
-    }
-}
-
-static void nabm_write(void *opaque, hwaddr addr, uint64_t val,
-                      unsigned size)
-{
-    if ((addr / size) > 64) {
-        return;
-    }
-
-    switch (size) {
-    case 1:
-        nabm_writeb(opaque, addr, val);
-        break;
-    case 2:
-        nabm_writew(opaque, addr, val);
-        break;
-    case 4:
-        nabm_writel(opaque, addr, val);
-        break;
-    }
-}
-
-
-static const MemoryRegionOps ac97_io_nabm_ops = {
-    .read = nabm_read,
-    .write = nabm_write,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 4,
-    },
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void ac97_on_reset (void *opaque)
-{
-    AC97LinkState *s = opaque;
-
-    reset_bm_regs (s, &s->bm_regs[0]);
-    reset_bm_regs (s, &s->bm_regs[1]);
-    reset_bm_regs (s, &s->bm_regs[2]);
-
-    /*
-     * Reset the mixer too. The Windows XP driver seems to rely on
-     * this. At least it wants to read the vendor id before it resets
-     * the codec manually.
-     */
-    mixer_reset (s);
-}
-
-static int ac97_initfn (PCIDevice *dev)
-{
-    AC97LinkState *s = DO_UPCAST (AC97LinkState, dev, dev);
-    uint8_t *c = s->dev.config;
-
-    /* TODO: no need to override */
-    c[PCI_COMMAND] = 0x00;      /* pcicmd pci command rw, ro */
-    c[PCI_COMMAND + 1] = 0x00;
-
-    /* TODO: */
-    c[PCI_STATUS] = PCI_STATUS_FAST_BACK;      /* pcists pci status rwc, ro */
-    c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_MEDIUM >> 8;
-
-    c[PCI_CLASS_PROG] = 0x00;      /* pi programming interface ro */
-
-    /* TODO set when bar is registered. no need to override. */
-    /* nabmar native audio mixer base address rw */
-    c[PCI_BASE_ADDRESS_0] = PCI_BASE_ADDRESS_SPACE_IO;
-    c[PCI_BASE_ADDRESS_0 + 1] = 0x00;
-    c[PCI_BASE_ADDRESS_0 + 2] = 0x00;
-    c[PCI_BASE_ADDRESS_0 + 3] = 0x00;
-
-    /* TODO set when bar is registered. no need to override. */
-      /* nabmbar native audio bus mastering base address rw */
-    c[PCI_BASE_ADDRESS_0 + 4] = PCI_BASE_ADDRESS_SPACE_IO;
-    c[PCI_BASE_ADDRESS_0 + 5] = 0x00;
-    c[PCI_BASE_ADDRESS_0 + 6] = 0x00;
-    c[PCI_BASE_ADDRESS_0 + 7] = 0x00;
-
-    if (s->use_broken_id) {
-        c[PCI_SUBSYSTEM_VENDOR_ID] = 0x86;
-        c[PCI_SUBSYSTEM_VENDOR_ID + 1] = 0x80;
-        c[PCI_SUBSYSTEM_ID] = 0x00;
-        c[PCI_SUBSYSTEM_ID + 1] = 0x00;
-    }
-
-    c[PCI_INTERRUPT_LINE] = 0x00;      /* intr_ln interrupt line rw */
-    c[PCI_INTERRUPT_PIN] = 0x01;      /* intr_pn interrupt pin ro */
-
-    memory_region_init_io (&s->io_nam, &ac97_io_nam_ops, s, "ac97-nam", 1024);
-    memory_region_init_io (&s->io_nabm, &ac97_io_nabm_ops, s, "ac97-nabm", 256);
-    pci_register_bar (&s->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_nam);
-    pci_register_bar (&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_nabm);
-    qemu_register_reset (ac97_on_reset, s);
-    AUD_register_card ("ac97", &s->card);
-    ac97_on_reset (s);
-    return 0;
-}
-
-static void ac97_exitfn (PCIDevice *dev)
-{
-    AC97LinkState *s = DO_UPCAST (AC97LinkState, dev, dev);
-
-    memory_region_destroy (&s->io_nam);
-    memory_region_destroy (&s->io_nabm);
-}
-
-int ac97_init (PCIBus *bus)
-{
-    pci_create_simple (bus, -1, "AC97");
-    return 0;
-}
-
-static Property ac97_properties[] = {
-    DEFINE_PROP_UINT32 ("use_broken_id", AC97LinkState, use_broken_id, 0),
-    DEFINE_PROP_END_OF_LIST (),
-};
-
-static void ac97_class_init (ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS (klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS (klass);
-
-    k->init = ac97_initfn;
-    k->exit = ac97_exitfn;
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->device_id = PCI_DEVICE_ID_INTEL_82801AA_5;
-    k->revision = 0x01;
-    k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO;
-    dc->desc = "Intel 82801AA AC97 Audio";
-    dc->vmsd = &vmstate_ac97;
-    dc->props = ac97_properties;
-}
-
-static const TypeInfo ac97_info = {
-    .name          = "AC97",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof (AC97LinkState),
-    .class_init    = ac97_class_init,
-};
-
-static void ac97_register_types (void)
-{
-    type_register_static (&ac97_info);
-}
-
-type_init (ac97_register_types)
diff --git a/hw/acpi.c b/hw/acpi.c
deleted file mode 100644 (file)
index 64b8718..0000000
--- a/hw/acpi.c
+++ /dev/null
@@ -1,614 +0,0 @@
-/*
- * ACPI implementation
- *
- * Copyright (c) 2006 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-#include "sysemu/sysemu.h"
-#include "hw/hw.h"
-#include "hw/i386/pc.h"
-#include "hw/acpi/acpi.h"
-#include "monitor/monitor.h"
-#include "qemu/config-file.h"
-#include "qapi/opts-visitor.h"
-#include "qapi/dealloc-visitor.h"
-#include "qapi-visit.h"
-
-struct acpi_table_header {
-    uint16_t _length;         /* our length, not actual part of the hdr */
-                              /* allows easier parsing for fw_cfg clients */
-    char sig[4];              /* ACPI signature (4 ASCII characters) */
-    uint32_t length;          /* Length of table, in bytes, including header */
-    uint8_t revision;         /* ACPI Specification minor version # */
-    uint8_t checksum;         /* To make sum of entire table == 0 */
-    char oem_id[6];           /* OEM identification */
-    char oem_table_id[8];     /* OEM table identification */
-    uint32_t oem_revision;    /* OEM revision number */
-    char asl_compiler_id[4];  /* ASL compiler vendor ID */
-    uint32_t asl_compiler_revision; /* ASL compiler revision number */
-} QEMU_PACKED;
-
-#define ACPI_TABLE_HDR_SIZE sizeof(struct acpi_table_header)
-#define ACPI_TABLE_PFX_SIZE sizeof(uint16_t)  /* size of the extra prefix */
-
-static const char unsigned dfl_hdr[ACPI_TABLE_HDR_SIZE - ACPI_TABLE_PFX_SIZE] =
-    "QEMU\0\0\0\0\1\0"       /* sig (4), len(4), revno (1), csum (1) */
-    "QEMUQEQEMUQEMU\1\0\0\0" /* OEM id (6), table (8), revno (4) */
-    "QEMU\1\0\0\0"           /* ASL compiler ID (4), version (4) */
-    ;
-
-char unsigned *acpi_tables;
-size_t acpi_tables_len;
-
-static QemuOptsList qemu_acpi_opts = {
-    .name = "acpi",
-    .implied_opt_name = "data",
-    .head = QTAILQ_HEAD_INITIALIZER(qemu_acpi_opts.head),
-    .desc = { { 0 } } /* validated with OptsVisitor */
-};
-
-static void acpi_register_config(void)
-{
-    qemu_add_opts(&qemu_acpi_opts);
-}
-
-machine_init(acpi_register_config);
-
-static int acpi_checksum(const uint8_t *data, int len)
-{
-    int sum, i;
-    sum = 0;
-    for (i = 0; i < len; i++) {
-        sum += data[i];
-    }
-    return (-sum) & 0xff;
-}
-
-
-/* Install a copy of the ACPI table specified in @blob.
- *
- * If @has_header is set, @blob starts with the System Description Table Header
- * structure. Otherwise, "dfl_hdr" is prepended. In any case, each header field
- * is optionally overwritten from @hdrs.
- *
- * It is valid to call this function with
- * (@blob == NULL && bloblen == 0 && !has_header).
- *
- * @hdrs->file and @hdrs->data are ignored.
- *
- * SIZE_MAX is considered "infinity" in this function.
- *
- * The number of tables that can be installed is not limited, but the 16-bit
- * counter at the beginning of "acpi_tables" wraps around after UINT16_MAX.
- */
-static void acpi_table_install(const char unsigned *blob, size_t bloblen,
-                               bool has_header,
-                               const struct AcpiTableOptions *hdrs,
-                               Error **errp)
-{
-    size_t body_start;
-    const char unsigned *hdr_src;
-    size_t body_size, acpi_payload_size;
-    struct acpi_table_header *ext_hdr;
-    unsigned changed_fields;
-
-    /* Calculate where the ACPI table body starts within the blob, plus where
-     * to copy the ACPI table header from.
-     */
-    if (has_header) {
-        /*   _length             | ACPI header in blob | blob body
-         *   ^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^
-         *   ACPI_TABLE_PFX_SIZE     sizeof dfl_hdr      body_size
-         *                           == body_start
-         *
-         *                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-         *                           acpi_payload_size == bloblen
-         */
-        body_start = sizeof dfl_hdr;
-
-        if (bloblen < body_start) {
-            error_setg(errp, "ACPI table claiming to have header is too "
-                       "short, available: %zu, expected: %zu", bloblen,
-                       body_start);
-            return;
-        }
-        hdr_src = blob;
-    } else {
-        /*   _length             | ACPI header in template | blob body
-         *   ^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^
-         *   ACPI_TABLE_PFX_SIZE       sizeof dfl_hdr        body_size
-         *                                                   == bloblen
-         *
-         *                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-         *                                  acpi_payload_size
-         */
-        body_start = 0;
-        hdr_src = dfl_hdr;
-    }
-    body_size = bloblen - body_start;
-    acpi_payload_size = sizeof dfl_hdr + body_size;
-
-    if (acpi_payload_size > UINT16_MAX) {
-        error_setg(errp, "ACPI table too big, requested: %zu, max: %u",
-                   acpi_payload_size, (unsigned)UINT16_MAX);
-        return;
-    }
-
-    /* We won't fail from here on. Initialize / extend the globals. */
-    if (acpi_tables == NULL) {
-        acpi_tables_len = sizeof(uint16_t);
-        acpi_tables = g_malloc0(acpi_tables_len);
-    }
-
-    acpi_tables = g_realloc(acpi_tables, acpi_tables_len +
-                                         ACPI_TABLE_PFX_SIZE +
-                                         sizeof dfl_hdr + body_size);
-
-    ext_hdr = (struct acpi_table_header *)(acpi_tables + acpi_tables_len);
-    acpi_tables_len += ACPI_TABLE_PFX_SIZE;
-
-    memcpy(acpi_tables + acpi_tables_len, hdr_src, sizeof dfl_hdr);
-    acpi_tables_len += sizeof dfl_hdr;
-
-    if (blob != NULL) {
-        memcpy(acpi_tables + acpi_tables_len, blob + body_start, body_size);
-        acpi_tables_len += body_size;
-    }
-
-    /* increase number of tables */
-    cpu_to_le16wu((uint16_t *)acpi_tables,
-                  le16_to_cpupu((uint16_t *)acpi_tables) + 1u);
-
-    /* Update the header fields. The strings need not be NUL-terminated. */
-    changed_fields = 0;
-    ext_hdr->_length = cpu_to_le16(acpi_payload_size);
-
-    if (hdrs->has_sig) {
-        strncpy(ext_hdr->sig, hdrs->sig, sizeof ext_hdr->sig);
-        ++changed_fields;
-    }
-
-    if (has_header && le32_to_cpu(ext_hdr->length) != acpi_payload_size) {
-        fprintf(stderr,
-                "warning: ACPI table has wrong length, header says "
-                "%" PRIu32 ", actual size %zu bytes\n",
-                le32_to_cpu(ext_hdr->length), acpi_payload_size);
-    }
-    ext_hdr->length = cpu_to_le32(acpi_payload_size);
-
-    if (hdrs->has_rev) {
-        ext_hdr->revision = hdrs->rev;
-        ++changed_fields;
-    }
-
-    ext_hdr->checksum = 0;
-
-    if (hdrs->has_oem_id) {
-        strncpy(ext_hdr->oem_id, hdrs->oem_id, sizeof ext_hdr->oem_id);
-        ++changed_fields;
-    }
-    if (hdrs->has_oem_table_id) {
-        strncpy(ext_hdr->oem_table_id, hdrs->oem_table_id,
-                sizeof ext_hdr->oem_table_id);
-        ++changed_fields;
-    }
-    if (hdrs->has_oem_rev) {
-        ext_hdr->oem_revision = cpu_to_le32(hdrs->oem_rev);
-        ++changed_fields;
-    }
-    if (hdrs->has_asl_compiler_id) {
-        strncpy(ext_hdr->asl_compiler_id, hdrs->asl_compiler_id,
-                sizeof ext_hdr->asl_compiler_id);
-        ++changed_fields;
-    }
-    if (hdrs->has_asl_compiler_rev) {
-        ext_hdr->asl_compiler_revision = cpu_to_le32(hdrs->asl_compiler_rev);
-        ++changed_fields;
-    }
-
-    if (!has_header && changed_fields == 0) {
-        fprintf(stderr, "warning: ACPI table: no headers are specified\n");
-    }
-
-    /* recalculate checksum */
-    ext_hdr->checksum = acpi_checksum((const char unsigned *)ext_hdr +
-                                      ACPI_TABLE_PFX_SIZE, acpi_payload_size);
-}
-
-void acpi_table_add(const QemuOpts *opts, Error **errp)
-{
-    AcpiTableOptions *hdrs = NULL;
-    Error *err = NULL;
-    char **pathnames = NULL;
-    char **cur;
-    size_t bloblen = 0;
-    char unsigned *blob = NULL;
-
-    {
-        OptsVisitor *ov;
-
-        ov = opts_visitor_new(opts);
-        visit_type_AcpiTableOptions(opts_get_visitor(ov), &hdrs, NULL, &err);
-        opts_visitor_cleanup(ov);
-    }
-
-    if (err) {
-        goto out;
-    }
-    if (hdrs->has_file == hdrs->has_data) {
-        error_setg(&err, "'-acpitable' requires one of 'data' or 'file'");
-        goto out;
-    }
-
-    pathnames = g_strsplit(hdrs->has_file ? hdrs->file : hdrs->data, ":", 0);
-    if (pathnames == NULL || pathnames[0] == NULL) {
-        error_setg(&err, "'-acpitable' requires at least one pathname");
-        goto out;
-    }
-
-    /* now read in the data files, reallocating buffer as needed */
-    for (cur = pathnames; *cur; ++cur) {
-        int fd = open(*cur, O_RDONLY | O_BINARY);
-
-        if (fd < 0) {
-            error_setg(&err, "can't open file %s: %s", *cur, strerror(errno));
-            goto out;
-        }
-
-        for (;;) {
-            char unsigned data[8192];
-            ssize_t r;
-
-            r = read(fd, data, sizeof data);
-            if (r == 0) {
-                break;
-            } else if (r > 0) {
-                blob = g_realloc(blob, bloblen + r);
-                memcpy(blob + bloblen, data, r);
-                bloblen += r;
-            } else if (errno != EINTR) {
-                error_setg(&err, "can't read file %s: %s",
-                           *cur, strerror(errno));
-                close(fd);
-                goto out;
-            }
-        }
-
-        close(fd);
-    }
-
-    acpi_table_install(blob, bloblen, hdrs->has_file, hdrs, &err);
-
-out:
-    g_free(blob);
-    g_strfreev(pathnames);
-
-    if (hdrs != NULL) {
-        QapiDeallocVisitor *dv;
-
-        dv = qapi_dealloc_visitor_new();
-        visit_type_AcpiTableOptions(qapi_dealloc_get_visitor(dv), &hdrs, NULL,
-                                    NULL);
-        qapi_dealloc_visitor_cleanup(dv);
-    }
-
-    error_propagate(errp, err);
-}
-
-static void acpi_notify_wakeup(Notifier *notifier, void *data)
-{
-    ACPIREGS *ar = container_of(notifier, ACPIREGS, wakeup);
-    WakeupReason *reason = data;
-
-    switch (*reason) {
-    case QEMU_WAKEUP_REASON_RTC:
-        ar->pm1.evt.sts |=
-            (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_RT_CLOCK_STATUS);
-        break;
-    case QEMU_WAKEUP_REASON_PMTIMER:
-        ar->pm1.evt.sts |=
-            (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_TIMER_STATUS);
-        break;
-    case QEMU_WAKEUP_REASON_OTHER:
-    default:
-        /* ACPI_BITMASK_WAKE_STATUS should be set on resume.
-           Pretend that resume was caused by power button */
-        ar->pm1.evt.sts |=
-            (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_POWER_BUTTON_STATUS);
-        break;
-    }
-}
-
-/* ACPI PM1a EVT */
-uint16_t acpi_pm1_evt_get_sts(ACPIREGS *ar)
-{
-    int64_t d = acpi_pm_tmr_get_clock();
-    if (d >= ar->tmr.overflow_time) {
-        ar->pm1.evt.sts |= ACPI_BITMASK_TIMER_STATUS;
-    }
-    return ar->pm1.evt.sts;
-}
-
-static void acpi_pm1_evt_write_sts(ACPIREGS *ar, uint16_t val)
-{
-    uint16_t pm1_sts = acpi_pm1_evt_get_sts(ar);
-    if (pm1_sts & val & ACPI_BITMASK_TIMER_STATUS) {
-        /* if TMRSTS is reset, then compute the new overflow time */
-        acpi_pm_tmr_calc_overflow_time(ar);
-    }
-    ar->pm1.evt.sts &= ~val;
-}
-
-static void acpi_pm1_evt_write_en(ACPIREGS *ar, uint16_t val)
-{
-    ar->pm1.evt.en = val;
-    qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC,
-                              val & ACPI_BITMASK_RT_CLOCK_ENABLE);
-    qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER,
-                              val & ACPI_BITMASK_TIMER_ENABLE);
-}
-
-void acpi_pm1_evt_power_down(ACPIREGS *ar)
-{
-    if (ar->pm1.evt.en & ACPI_BITMASK_POWER_BUTTON_ENABLE) {
-        ar->pm1.evt.sts |= ACPI_BITMASK_POWER_BUTTON_STATUS;
-        ar->tmr.update_sci(ar);
-    }
-}
-
-void acpi_pm1_evt_reset(ACPIREGS *ar)
-{
-    ar->pm1.evt.sts = 0;
-    ar->pm1.evt.en = 0;
-    qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC, 0);
-    qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER, 0);
-}
-
-static uint64_t acpi_pm_evt_read(void *opaque, hwaddr addr, unsigned width)
-{
-    ACPIREGS *ar = opaque;
-    switch (addr) {
-    case 0:
-        return acpi_pm1_evt_get_sts(ar);
-    case 2:
-        return ar->pm1.evt.en;
-    default:
-        return 0;
-    }
-}
-
-static void acpi_pm_evt_write(void *opaque, hwaddr addr, uint64_t val,
-                              unsigned width)
-{
-    ACPIREGS *ar = opaque;
-    switch (addr) {
-    case 0:
-        acpi_pm1_evt_write_sts(ar, val);
-        ar->pm1.evt.update_sci(ar);
-        break;
-    case 2:
-        acpi_pm1_evt_write_en(ar, val);
-        ar->pm1.evt.update_sci(ar);
-        break;
-    }
-}
-
-static const MemoryRegionOps acpi_pm_evt_ops = {
-    .read = acpi_pm_evt_read,
-    .write = acpi_pm_evt_write,
-    .valid.min_access_size = 2,
-    .valid.max_access_size = 2,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-void acpi_pm1_evt_init(ACPIREGS *ar, acpi_update_sci_fn update_sci,
-                       MemoryRegion *parent)
-{
-    ar->pm1.evt.update_sci = update_sci;
-    memory_region_init_io(&ar->pm1.evt.io, &acpi_pm_evt_ops, ar, "acpi-evt", 4);
-    memory_region_add_subregion(parent, 0, &ar->pm1.evt.io);
-}
-
-/* ACPI PM_TMR */
-void acpi_pm_tmr_update(ACPIREGS *ar, bool enable)
-{
-    int64_t expire_time;
-
-    /* schedule a timer interruption if needed */
-    if (enable) {
-        expire_time = muldiv64(ar->tmr.overflow_time, get_ticks_per_sec(),
-                               PM_TIMER_FREQUENCY);
-        qemu_mod_timer(ar->tmr.timer, expire_time);
-    } else {
-        qemu_del_timer(ar->tmr.timer);
-    }
-}
-
-void acpi_pm_tmr_calc_overflow_time(ACPIREGS *ar)
-{
-    int64_t d = acpi_pm_tmr_get_clock();
-    ar->tmr.overflow_time = (d + 0x800000LL) & ~0x7fffffLL;
-}
-
-static uint32_t acpi_pm_tmr_get(ACPIREGS *ar)
-{
-    uint32_t d = acpi_pm_tmr_get_clock();
-    return d & 0xffffff;
-}
-
-static void acpi_pm_tmr_timer(void *opaque)
-{
-    ACPIREGS *ar = opaque;
-    qemu_system_wakeup_request(QEMU_WAKEUP_REASON_PMTIMER);
-    ar->tmr.update_sci(ar);
-}
-
-static uint64_t acpi_pm_tmr_read(void *opaque, hwaddr addr, unsigned width)
-{
-    return acpi_pm_tmr_get(opaque);
-}
-
-static const MemoryRegionOps acpi_pm_tmr_ops = {
-    .read = acpi_pm_tmr_read,
-    .valid.min_access_size = 4,
-    .valid.max_access_size = 4,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-void acpi_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci,
-                      MemoryRegion *parent)
-{
-    ar->tmr.update_sci = update_sci;
-    ar->tmr.timer = qemu_new_timer_ns(vm_clock, acpi_pm_tmr_timer, ar);
-    memory_region_init_io(&ar->tmr.io, &acpi_pm_tmr_ops, ar, "acpi-tmr", 4);
-    memory_region_add_subregion(parent, 8, &ar->tmr.io);
-}
-
-void acpi_pm_tmr_reset(ACPIREGS *ar)
-{
-    ar->tmr.overflow_time = 0;
-    qemu_del_timer(ar->tmr.timer);
-}
-
-/* ACPI PM1aCNT */
-static void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val)
-{
-    ar->pm1.cnt.cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE);
-
-    if (val & ACPI_BITMASK_SLEEP_ENABLE) {
-        /* change suspend type */
-        uint16_t sus_typ = (val >> 10) & 7;
-        switch(sus_typ) {
-        case 0: /* soft power off */
-            qemu_system_shutdown_request();
-            break;
-        case 1:
-            qemu_system_suspend_request();
-            break;
-        default:
-            if (sus_typ == ar->pm1.cnt.s4_val) { /* S4 request */
-                monitor_protocol_event(QEVENT_SUSPEND_DISK, NULL);
-                qemu_system_shutdown_request();
-            }
-            break;
-        }
-    }
-}
-
-void acpi_pm1_cnt_update(ACPIREGS *ar,
-                         bool sci_enable, bool sci_disable)
-{
-    /* ACPI specs 3.0, 4.7.2.5 */
-    if (sci_enable) {
-        ar->pm1.cnt.cnt |= ACPI_BITMASK_SCI_ENABLE;
-    } else if (sci_disable) {
-        ar->pm1.cnt.cnt &= ~ACPI_BITMASK_SCI_ENABLE;
-    }
-}
-
-static uint64_t acpi_pm_cnt_read(void *opaque, hwaddr addr, unsigned width)
-{
-    ACPIREGS *ar = opaque;
-    return ar->pm1.cnt.cnt;
-}
-
-static void acpi_pm_cnt_write(void *opaque, hwaddr addr, uint64_t val,
-                              unsigned width)
-{
-    acpi_pm1_cnt_write(opaque, val);
-}
-
-static const MemoryRegionOps acpi_pm_cnt_ops = {
-    .read = acpi_pm_cnt_read,
-    .write = acpi_pm_cnt_write,
-    .valid.min_access_size = 2,
-    .valid.max_access_size = 2,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent, uint8_t s4_val)
-{
-    ar->pm1.cnt.s4_val = s4_val;
-    ar->wakeup.notify = acpi_notify_wakeup;
-    qemu_register_wakeup_notifier(&ar->wakeup);
-    memory_region_init_io(&ar->pm1.cnt.io, &acpi_pm_cnt_ops, ar, "acpi-cnt", 2);
-    memory_region_add_subregion(parent, 4, &ar->pm1.cnt.io);
-}
-
-void acpi_pm1_cnt_reset(ACPIREGS *ar)
-{
-    ar->pm1.cnt.cnt = 0;
-}
-
-/* ACPI GPE */
-void acpi_gpe_init(ACPIREGS *ar, uint8_t len)
-{
-    ar->gpe.len = len;
-    ar->gpe.sts = g_malloc0(len / 2);
-    ar->gpe.en = g_malloc0(len / 2);
-}
-
-void acpi_gpe_reset(ACPIREGS *ar)
-{
-    memset(ar->gpe.sts, 0, ar->gpe.len / 2);
-    memset(ar->gpe.en, 0, ar->gpe.len / 2);
-}
-
-static uint8_t *acpi_gpe_ioport_get_ptr(ACPIREGS *ar, uint32_t addr)
-{
-    uint8_t *cur = NULL;
-
-    if (addr < ar->gpe.len / 2) {
-        cur = ar->gpe.sts + addr;
-    } else if (addr < ar->gpe.len) {
-        cur = ar->gpe.en + addr - ar->gpe.len / 2;
-    } else {
-        abort();
-    }
-
-    return cur;
-}
-
-void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val)
-{
-    uint8_t *cur;
-
-    cur = acpi_gpe_ioport_get_ptr(ar, addr);
-    if (addr < ar->gpe.len / 2) {
-        /* GPE_STS */
-        *cur = (*cur) & ~val;
-    } else if (addr < ar->gpe.len) {
-        /* GPE_EN */
-        *cur = val;
-    } else {
-        abort();
-    }
-}
-
-uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr)
-{
-    uint8_t *cur;
-    uint32_t val;
-
-    cur = acpi_gpe_ioport_get_ptr(ar, addr);
-    val = 0;
-    if (cur != NULL) {
-        val = *cur;
-    }
-
-    return val;
-}
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a0b63b5626c2f8dc810557df20e7bde8f30739c4 100644 (file)
@@ -0,0 +1,2 @@
+common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o
+
diff --git a/hw/acpi/core.c b/hw/acpi/core.c
new file mode 100644 (file)
index 0000000..64b8718
--- /dev/null
@@ -0,0 +1,614 @@
+/*
+ * ACPI implementation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+#include "sysemu/sysemu.h"
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "hw/acpi/acpi.h"
+#include "monitor/monitor.h"
+#include "qemu/config-file.h"
+#include "qapi/opts-visitor.h"
+#include "qapi/dealloc-visitor.h"
+#include "qapi-visit.h"
+
+struct acpi_table_header {
+    uint16_t _length;         /* our length, not actual part of the hdr */
+                              /* allows easier parsing for fw_cfg clients */
+    char sig[4];              /* ACPI signature (4 ASCII characters) */
+    uint32_t length;          /* Length of table, in bytes, including header */
+    uint8_t revision;         /* ACPI Specification minor version # */
+    uint8_t checksum;         /* To make sum of entire table == 0 */
+    char oem_id[6];           /* OEM identification */
+    char oem_table_id[8];     /* OEM table identification */
+    uint32_t oem_revision;    /* OEM revision number */
+    char asl_compiler_id[4];  /* ASL compiler vendor ID */
+    uint32_t asl_compiler_revision; /* ASL compiler revision number */
+} QEMU_PACKED;
+
+#define ACPI_TABLE_HDR_SIZE sizeof(struct acpi_table_header)
+#define ACPI_TABLE_PFX_SIZE sizeof(uint16_t)  /* size of the extra prefix */
+
+static const char unsigned dfl_hdr[ACPI_TABLE_HDR_SIZE - ACPI_TABLE_PFX_SIZE] =
+    "QEMU\0\0\0\0\1\0"       /* sig (4), len(4), revno (1), csum (1) */
+    "QEMUQEQEMUQEMU\1\0\0\0" /* OEM id (6), table (8), revno (4) */
+    "QEMU\1\0\0\0"           /* ASL compiler ID (4), version (4) */
+    ;
+
+char unsigned *acpi_tables;
+size_t acpi_tables_len;
+
+static QemuOptsList qemu_acpi_opts = {
+    .name = "acpi",
+    .implied_opt_name = "data",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_acpi_opts.head),
+    .desc = { { 0 } } /* validated with OptsVisitor */
+};
+
+static void acpi_register_config(void)
+{
+    qemu_add_opts(&qemu_acpi_opts);
+}
+
+machine_init(acpi_register_config);
+
+static int acpi_checksum(const uint8_t *data, int len)
+{
+    int sum, i;
+    sum = 0;
+    for (i = 0; i < len; i++) {
+        sum += data[i];
+    }
+    return (-sum) & 0xff;
+}
+
+
+/* Install a copy of the ACPI table specified in @blob.
+ *
+ * If @has_header is set, @blob starts with the System Description Table Header
+ * structure. Otherwise, "dfl_hdr" is prepended. In any case, each header field
+ * is optionally overwritten from @hdrs.
+ *
+ * It is valid to call this function with
+ * (@blob == NULL && bloblen == 0 && !has_header).
+ *
+ * @hdrs->file and @hdrs->data are ignored.
+ *
+ * SIZE_MAX is considered "infinity" in this function.
+ *
+ * The number of tables that can be installed is not limited, but the 16-bit
+ * counter at the beginning of "acpi_tables" wraps around after UINT16_MAX.
+ */
+static void acpi_table_install(const char unsigned *blob, size_t bloblen,
+                               bool has_header,
+                               const struct AcpiTableOptions *hdrs,
+                               Error **errp)
+{
+    size_t body_start;
+    const char unsigned *hdr_src;
+    size_t body_size, acpi_payload_size;
+    struct acpi_table_header *ext_hdr;
+    unsigned changed_fields;
+
+    /* Calculate where the ACPI table body starts within the blob, plus where
+     * to copy the ACPI table header from.
+     */
+    if (has_header) {
+        /*   _length             | ACPI header in blob | blob body
+         *   ^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^
+         *   ACPI_TABLE_PFX_SIZE     sizeof dfl_hdr      body_size
+         *                           == body_start
+         *
+         *                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+         *                           acpi_payload_size == bloblen
+         */
+        body_start = sizeof dfl_hdr;
+
+        if (bloblen < body_start) {
+            error_setg(errp, "ACPI table claiming to have header is too "
+                       "short, available: %zu, expected: %zu", bloblen,
+                       body_start);
+            return;
+        }
+        hdr_src = blob;
+    } else {
+        /*   _length             | ACPI header in template | blob body
+         *   ^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^
+         *   ACPI_TABLE_PFX_SIZE       sizeof dfl_hdr        body_size
+         *                                                   == bloblen
+         *
+         *                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+         *                                  acpi_payload_size
+         */
+        body_start = 0;
+        hdr_src = dfl_hdr;
+    }
+    body_size = bloblen - body_start;
+    acpi_payload_size = sizeof dfl_hdr + body_size;
+
+    if (acpi_payload_size > UINT16_MAX) {
+        error_setg(errp, "ACPI table too big, requested: %zu, max: %u",
+                   acpi_payload_size, (unsigned)UINT16_MAX);
+        return;
+    }
+
+    /* We won't fail from here on. Initialize / extend the globals. */
+    if (acpi_tables == NULL) {
+        acpi_tables_len = sizeof(uint16_t);
+        acpi_tables = g_malloc0(acpi_tables_len);
+    }
+
+    acpi_tables = g_realloc(acpi_tables, acpi_tables_len +
+                                         ACPI_TABLE_PFX_SIZE +
+                                         sizeof dfl_hdr + body_size);
+
+    ext_hdr = (struct acpi_table_header *)(acpi_tables + acpi_tables_len);
+    acpi_tables_len += ACPI_TABLE_PFX_SIZE;
+
+    memcpy(acpi_tables + acpi_tables_len, hdr_src, sizeof dfl_hdr);
+    acpi_tables_len += sizeof dfl_hdr;
+
+    if (blob != NULL) {
+        memcpy(acpi_tables + acpi_tables_len, blob + body_start, body_size);
+        acpi_tables_len += body_size;
+    }
+
+    /* increase number of tables */
+    cpu_to_le16wu((uint16_t *)acpi_tables,
+                  le16_to_cpupu((uint16_t *)acpi_tables) + 1u);
+
+    /* Update the header fields. The strings need not be NUL-terminated. */
+    changed_fields = 0;
+    ext_hdr->_length = cpu_to_le16(acpi_payload_size);
+
+    if (hdrs->has_sig) {
+        strncpy(ext_hdr->sig, hdrs->sig, sizeof ext_hdr->sig);
+        ++changed_fields;
+    }
+
+    if (has_header && le32_to_cpu(ext_hdr->length) != acpi_payload_size) {
+        fprintf(stderr,
+                "warning: ACPI table has wrong length, header says "
+                "%" PRIu32 ", actual size %zu bytes\n",
+                le32_to_cpu(ext_hdr->length), acpi_payload_size);
+    }
+    ext_hdr->length = cpu_to_le32(acpi_payload_size);
+
+    if (hdrs->has_rev) {
+        ext_hdr->revision = hdrs->rev;
+        ++changed_fields;
+    }
+
+    ext_hdr->checksum = 0;
+
+    if (hdrs->has_oem_id) {
+        strncpy(ext_hdr->oem_id, hdrs->oem_id, sizeof ext_hdr->oem_id);
+        ++changed_fields;
+    }
+    if (hdrs->has_oem_table_id) {
+        strncpy(ext_hdr->oem_table_id, hdrs->oem_table_id,
+                sizeof ext_hdr->oem_table_id);
+        ++changed_fields;
+    }
+    if (hdrs->has_oem_rev) {
+        ext_hdr->oem_revision = cpu_to_le32(hdrs->oem_rev);
+        ++changed_fields;
+    }
+    if (hdrs->has_asl_compiler_id) {
+        strncpy(ext_hdr->asl_compiler_id, hdrs->asl_compiler_id,
+                sizeof ext_hdr->asl_compiler_id);
+        ++changed_fields;
+    }
+    if (hdrs->has_asl_compiler_rev) {
+        ext_hdr->asl_compiler_revision = cpu_to_le32(hdrs->asl_compiler_rev);
+        ++changed_fields;
+    }
+
+    if (!has_header && changed_fields == 0) {
+        fprintf(stderr, "warning: ACPI table: no headers are specified\n");
+    }
+
+    /* recalculate checksum */
+    ext_hdr->checksum = acpi_checksum((const char unsigned *)ext_hdr +
+                                      ACPI_TABLE_PFX_SIZE, acpi_payload_size);
+}
+
+void acpi_table_add(const QemuOpts *opts, Error **errp)
+{
+    AcpiTableOptions *hdrs = NULL;
+    Error *err = NULL;
+    char **pathnames = NULL;
+    char **cur;
+    size_t bloblen = 0;
+    char unsigned *blob = NULL;
+
+    {
+        OptsVisitor *ov;
+
+        ov = opts_visitor_new(opts);
+        visit_type_AcpiTableOptions(opts_get_visitor(ov), &hdrs, NULL, &err);
+        opts_visitor_cleanup(ov);
+    }
+
+    if (err) {
+        goto out;
+    }
+    if (hdrs->has_file == hdrs->has_data) {
+        error_setg(&err, "'-acpitable' requires one of 'data' or 'file'");
+        goto out;
+    }
+
+    pathnames = g_strsplit(hdrs->has_file ? hdrs->file : hdrs->data, ":", 0);
+    if (pathnames == NULL || pathnames[0] == NULL) {
+        error_setg(&err, "'-acpitable' requires at least one pathname");
+        goto out;
+    }
+
+    /* now read in the data files, reallocating buffer as needed */
+    for (cur = pathnames; *cur; ++cur) {
+        int fd = open(*cur, O_RDONLY | O_BINARY);
+
+        if (fd < 0) {
+            error_setg(&err, "can't open file %s: %s", *cur, strerror(errno));
+            goto out;
+        }
+
+        for (;;) {
+            char unsigned data[8192];
+            ssize_t r;
+
+            r = read(fd, data, sizeof data);
+            if (r == 0) {
+                break;
+            } else if (r > 0) {
+                blob = g_realloc(blob, bloblen + r);
+                memcpy(blob + bloblen, data, r);
+                bloblen += r;
+            } else if (errno != EINTR) {
+                error_setg(&err, "can't read file %s: %s",
+                           *cur, strerror(errno));
+                close(fd);
+                goto out;
+            }
+        }
+
+        close(fd);
+    }
+
+    acpi_table_install(blob, bloblen, hdrs->has_file, hdrs, &err);
+
+out:
+    g_free(blob);
+    g_strfreev(pathnames);
+
+    if (hdrs != NULL) {
+        QapiDeallocVisitor *dv;
+
+        dv = qapi_dealloc_visitor_new();
+        visit_type_AcpiTableOptions(qapi_dealloc_get_visitor(dv), &hdrs, NULL,
+                                    NULL);
+        qapi_dealloc_visitor_cleanup(dv);
+    }
+
+    error_propagate(errp, err);
+}
+
+static void acpi_notify_wakeup(Notifier *notifier, void *data)
+{
+    ACPIREGS *ar = container_of(notifier, ACPIREGS, wakeup);
+    WakeupReason *reason = data;
+
+    switch (*reason) {
+    case QEMU_WAKEUP_REASON_RTC:
+        ar->pm1.evt.sts |=
+            (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_RT_CLOCK_STATUS);
+        break;
+    case QEMU_WAKEUP_REASON_PMTIMER:
+        ar->pm1.evt.sts |=
+            (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_TIMER_STATUS);
+        break;
+    case QEMU_WAKEUP_REASON_OTHER:
+    default:
+        /* ACPI_BITMASK_WAKE_STATUS should be set on resume.
+           Pretend that resume was caused by power button */
+        ar->pm1.evt.sts |=
+            (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_POWER_BUTTON_STATUS);
+        break;
+    }
+}
+
+/* ACPI PM1a EVT */
+uint16_t acpi_pm1_evt_get_sts(ACPIREGS *ar)
+{
+    int64_t d = acpi_pm_tmr_get_clock();
+    if (d >= ar->tmr.overflow_time) {
+        ar->pm1.evt.sts |= ACPI_BITMASK_TIMER_STATUS;
+    }
+    return ar->pm1.evt.sts;
+}
+
+static void acpi_pm1_evt_write_sts(ACPIREGS *ar, uint16_t val)
+{
+    uint16_t pm1_sts = acpi_pm1_evt_get_sts(ar);
+    if (pm1_sts & val & ACPI_BITMASK_TIMER_STATUS) {
+        /* if TMRSTS is reset, then compute the new overflow time */
+        acpi_pm_tmr_calc_overflow_time(ar);
+    }
+    ar->pm1.evt.sts &= ~val;
+}
+
+static void acpi_pm1_evt_write_en(ACPIREGS *ar, uint16_t val)
+{
+    ar->pm1.evt.en = val;
+    qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC,
+                              val & ACPI_BITMASK_RT_CLOCK_ENABLE);
+    qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER,
+                              val & ACPI_BITMASK_TIMER_ENABLE);
+}
+
+void acpi_pm1_evt_power_down(ACPIREGS *ar)
+{
+    if (ar->pm1.evt.en & ACPI_BITMASK_POWER_BUTTON_ENABLE) {
+        ar->pm1.evt.sts |= ACPI_BITMASK_POWER_BUTTON_STATUS;
+        ar->tmr.update_sci(ar);
+    }
+}
+
+void acpi_pm1_evt_reset(ACPIREGS *ar)
+{
+    ar->pm1.evt.sts = 0;
+    ar->pm1.evt.en = 0;
+    qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC, 0);
+    qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER, 0);
+}
+
+static uint64_t acpi_pm_evt_read(void *opaque, hwaddr addr, unsigned width)
+{
+    ACPIREGS *ar = opaque;
+    switch (addr) {
+    case 0:
+        return acpi_pm1_evt_get_sts(ar);
+    case 2:
+        return ar->pm1.evt.en;
+    default:
+        return 0;
+    }
+}
+
+static void acpi_pm_evt_write(void *opaque, hwaddr addr, uint64_t val,
+                              unsigned width)
+{
+    ACPIREGS *ar = opaque;
+    switch (addr) {
+    case 0:
+        acpi_pm1_evt_write_sts(ar, val);
+        ar->pm1.evt.update_sci(ar);
+        break;
+    case 2:
+        acpi_pm1_evt_write_en(ar, val);
+        ar->pm1.evt.update_sci(ar);
+        break;
+    }
+}
+
+static const MemoryRegionOps acpi_pm_evt_ops = {
+    .read = acpi_pm_evt_read,
+    .write = acpi_pm_evt_write,
+    .valid.min_access_size = 2,
+    .valid.max_access_size = 2,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+void acpi_pm1_evt_init(ACPIREGS *ar, acpi_update_sci_fn update_sci,
+                       MemoryRegion *parent)
+{
+    ar->pm1.evt.update_sci = update_sci;
+    memory_region_init_io(&ar->pm1.evt.io, &acpi_pm_evt_ops, ar, "acpi-evt", 4);
+    memory_region_add_subregion(parent, 0, &ar->pm1.evt.io);
+}
+
+/* ACPI PM_TMR */
+void acpi_pm_tmr_update(ACPIREGS *ar, bool enable)
+{
+    int64_t expire_time;
+
+    /* schedule a timer interruption if needed */
+    if (enable) {
+        expire_time = muldiv64(ar->tmr.overflow_time, get_ticks_per_sec(),
+                               PM_TIMER_FREQUENCY);
+        qemu_mod_timer(ar->tmr.timer, expire_time);
+    } else {
+        qemu_del_timer(ar->tmr.timer);
+    }
+}
+
+void acpi_pm_tmr_calc_overflow_time(ACPIREGS *ar)
+{
+    int64_t d = acpi_pm_tmr_get_clock();
+    ar->tmr.overflow_time = (d + 0x800000LL) & ~0x7fffffLL;
+}
+
+static uint32_t acpi_pm_tmr_get(ACPIREGS *ar)
+{
+    uint32_t d = acpi_pm_tmr_get_clock();
+    return d & 0xffffff;
+}
+
+static void acpi_pm_tmr_timer(void *opaque)
+{
+    ACPIREGS *ar = opaque;
+    qemu_system_wakeup_request(QEMU_WAKEUP_REASON_PMTIMER);
+    ar->tmr.update_sci(ar);
+}
+
+static uint64_t acpi_pm_tmr_read(void *opaque, hwaddr addr, unsigned width)
+{
+    return acpi_pm_tmr_get(opaque);
+}
+
+static const MemoryRegionOps acpi_pm_tmr_ops = {
+    .read = acpi_pm_tmr_read,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+void acpi_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci,
+                      MemoryRegion *parent)
+{
+    ar->tmr.update_sci = update_sci;
+    ar->tmr.timer = qemu_new_timer_ns(vm_clock, acpi_pm_tmr_timer, ar);
+    memory_region_init_io(&ar->tmr.io, &acpi_pm_tmr_ops, ar, "acpi-tmr", 4);
+    memory_region_add_subregion(parent, 8, &ar->tmr.io);
+}
+
+void acpi_pm_tmr_reset(ACPIREGS *ar)
+{
+    ar->tmr.overflow_time = 0;
+    qemu_del_timer(ar->tmr.timer);
+}
+
+/* ACPI PM1aCNT */
+static void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val)
+{
+    ar->pm1.cnt.cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE);
+
+    if (val & ACPI_BITMASK_SLEEP_ENABLE) {
+        /* change suspend type */
+        uint16_t sus_typ = (val >> 10) & 7;
+        switch(sus_typ) {
+        case 0: /* soft power off */
+            qemu_system_shutdown_request();
+            break;
+        case 1:
+            qemu_system_suspend_request();
+            break;
+        default:
+            if (sus_typ == ar->pm1.cnt.s4_val) { /* S4 request */
+                monitor_protocol_event(QEVENT_SUSPEND_DISK, NULL);
+                qemu_system_shutdown_request();
+            }
+            break;
+        }
+    }
+}
+
+void acpi_pm1_cnt_update(ACPIREGS *ar,
+                         bool sci_enable, bool sci_disable)
+{
+    /* ACPI specs 3.0, 4.7.2.5 */
+    if (sci_enable) {
+        ar->pm1.cnt.cnt |= ACPI_BITMASK_SCI_ENABLE;
+    } else if (sci_disable) {
+        ar->pm1.cnt.cnt &= ~ACPI_BITMASK_SCI_ENABLE;
+    }
+}
+
+static uint64_t acpi_pm_cnt_read(void *opaque, hwaddr addr, unsigned width)
+{
+    ACPIREGS *ar = opaque;
+    return ar->pm1.cnt.cnt;
+}
+
+static void acpi_pm_cnt_write(void *opaque, hwaddr addr, uint64_t val,
+                              unsigned width)
+{
+    acpi_pm1_cnt_write(opaque, val);
+}
+
+static const MemoryRegionOps acpi_pm_cnt_ops = {
+    .read = acpi_pm_cnt_read,
+    .write = acpi_pm_cnt_write,
+    .valid.min_access_size = 2,
+    .valid.max_access_size = 2,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent, uint8_t s4_val)
+{
+    ar->pm1.cnt.s4_val = s4_val;
+    ar->wakeup.notify = acpi_notify_wakeup;
+    qemu_register_wakeup_notifier(&ar->wakeup);
+    memory_region_init_io(&ar->pm1.cnt.io, &acpi_pm_cnt_ops, ar, "acpi-cnt", 2);
+    memory_region_add_subregion(parent, 4, &ar->pm1.cnt.io);
+}
+
+void acpi_pm1_cnt_reset(ACPIREGS *ar)
+{
+    ar->pm1.cnt.cnt = 0;
+}
+
+/* ACPI GPE */
+void acpi_gpe_init(ACPIREGS *ar, uint8_t len)
+{
+    ar->gpe.len = len;
+    ar->gpe.sts = g_malloc0(len / 2);
+    ar->gpe.en = g_malloc0(len / 2);
+}
+
+void acpi_gpe_reset(ACPIREGS *ar)
+{
+    memset(ar->gpe.sts, 0, ar->gpe.len / 2);
+    memset(ar->gpe.en, 0, ar->gpe.len / 2);
+}
+
+static uint8_t *acpi_gpe_ioport_get_ptr(ACPIREGS *ar, uint32_t addr)
+{
+    uint8_t *cur = NULL;
+
+    if (addr < ar->gpe.len / 2) {
+        cur = ar->gpe.sts + addr;
+    } else if (addr < ar->gpe.len) {
+        cur = ar->gpe.en + addr - ar->gpe.len / 2;
+    } else {
+        abort();
+    }
+
+    return cur;
+}
+
+void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val)
+{
+    uint8_t *cur;
+
+    cur = acpi_gpe_ioport_get_ptr(ar, addr);
+    if (addr < ar->gpe.len / 2) {
+        /* GPE_STS */
+        *cur = (*cur) & ~val;
+    } else if (addr < ar->gpe.len) {
+        /* GPE_EN */
+        *cur = val;
+    } else {
+        abort();
+    }
+}
+
+uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr)
+{
+    uint8_t *cur;
+    uint32_t val;
+
+    cur = acpi_gpe_ioport_get_ptr(ar, addr);
+    val = 0;
+    if (cur != NULL) {
+        val = *cur;
+    }
+
+    return val;
+}
diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c
new file mode 100644 (file)
index 0000000..e663d29
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * ACPI implementation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ * Copyright (C) 2012 Jason Baron <jbaron@redhat.com>
+ *
+ * This is based on acpi.c.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "hw/pci/pci.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+#include "hw/acpi/acpi.h"
+#include "sysemu/kvm.h"
+#include "exec/address-spaces.h"
+
+#include "hw/i386/ich9.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define ICH9_DEBUG(fmt, ...) \
+do { printf("%s "fmt, __func__, ## __VA_ARGS__); } while (0)
+#else
+#define ICH9_DEBUG(fmt, ...)    do { } while (0)
+#endif
+
+static void pm_update_sci(ICH9LPCPMRegs *pm)
+{
+    int sci_level, pm1a_sts;
+
+    pm1a_sts = acpi_pm1_evt_get_sts(&pm->acpi_regs);
+
+    sci_level = (((pm1a_sts & pm->acpi_regs.pm1.evt.en) &
+                  (ACPI_BITMASK_RT_CLOCK_ENABLE |
+                   ACPI_BITMASK_POWER_BUTTON_ENABLE |
+                   ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
+                   ACPI_BITMASK_TIMER_ENABLE)) != 0);
+    qemu_set_irq(pm->irq, sci_level);
+
+    /* schedule a timer interruption if needed */
+    acpi_pm_tmr_update(&pm->acpi_regs,
+                       (pm->acpi_regs.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
+                       !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
+}
+
+static void ich9_pm_update_sci_fn(ACPIREGS *regs)
+{
+    ICH9LPCPMRegs *pm = container_of(regs, ICH9LPCPMRegs, acpi_regs);
+    pm_update_sci(pm);
+}
+
+static uint64_t ich9_gpe_readb(void *opaque, hwaddr addr, unsigned width)
+{
+    ICH9LPCPMRegs *pm = opaque;
+    return acpi_gpe_ioport_readb(&pm->acpi_regs, addr);
+}
+
+static void ich9_gpe_writeb(void *opaque, hwaddr addr, uint64_t val,
+                            unsigned width)
+{
+    ICH9LPCPMRegs *pm = opaque;
+    acpi_gpe_ioport_writeb(&pm->acpi_regs, addr, val);
+}
+
+static const MemoryRegionOps ich9_gpe_ops = {
+    .read = ich9_gpe_readb,
+    .write = ich9_gpe_writeb,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 1,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t ich9_smi_readl(void *opaque, hwaddr addr, unsigned width)
+{
+    ICH9LPCPMRegs *pm = opaque;
+    switch (addr) {
+    case 0:
+        return pm->smi_en;
+    case 4:
+        return pm->smi_sts;
+    default:
+        return 0;
+    }
+}
+
+static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val,
+                            unsigned width)
+{
+    ICH9LPCPMRegs *pm = opaque;
+    switch (addr) {
+    case 0:
+        pm->smi_en = val;
+        break;
+    }
+}
+
+static const MemoryRegionOps ich9_smi_ops = {
+    .read = ich9_smi_readl,
+    .write = ich9_smi_writel,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base)
+{
+    ICH9_DEBUG("to 0x%x\n", pm_io_base);
+
+    assert((pm_io_base & ICH9_PMIO_MASK) == 0);
+
+    pm->pm_io_base = pm_io_base;
+    memory_region_transaction_begin();
+    memory_region_set_enabled(&pm->io, pm->pm_io_base != 0);
+    memory_region_set_address(&pm->io, pm->pm_io_base);
+    memory_region_transaction_commit();
+}
+
+static int ich9_pm_post_load(void *opaque, int version_id)
+{
+    ICH9LPCPMRegs *pm = opaque;
+    uint32_t pm_io_base = pm->pm_io_base;
+    pm->pm_io_base = 0;
+    ich9_pm_iospace_update(pm, pm_io_base);
+    return 0;
+}
+
+#define VMSTATE_GPE_ARRAY(_field, _state)                            \
+ {                                                                   \
+     .name       = (stringify(_field)),                              \
+     .version_id = 0,                                                \
+     .num        = ICH9_PMIO_GPE0_LEN,                               \
+     .info       = &vmstate_info_uint8,                              \
+     .size       = sizeof(uint8_t),                                  \
+     .flags      = VMS_ARRAY | VMS_POINTER,                          \
+     .offset     = vmstate_offset_pointer(_state, _field, uint8_t),  \
+ }
+
+const VMStateDescription vmstate_ich9_pm = {
+    .name = "ich9_pm",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = ich9_pm_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT16(acpi_regs.pm1.evt.sts, ICH9LPCPMRegs),
+        VMSTATE_UINT16(acpi_regs.pm1.evt.en, ICH9LPCPMRegs),
+        VMSTATE_UINT16(acpi_regs.pm1.cnt.cnt, ICH9LPCPMRegs),
+        VMSTATE_TIMER(acpi_regs.tmr.timer, ICH9LPCPMRegs),
+        VMSTATE_INT64(acpi_regs.tmr.overflow_time, ICH9LPCPMRegs),
+        VMSTATE_GPE_ARRAY(acpi_regs.gpe.sts, ICH9LPCPMRegs),
+        VMSTATE_GPE_ARRAY(acpi_regs.gpe.en, ICH9LPCPMRegs),
+        VMSTATE_UINT32(smi_en, ICH9LPCPMRegs),
+        VMSTATE_UINT32(smi_sts, ICH9LPCPMRegs),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void pm_reset(void *opaque)
+{
+    ICH9LPCPMRegs *pm = opaque;
+    ich9_pm_iospace_update(pm, 0);
+
+    acpi_pm1_evt_reset(&pm->acpi_regs);
+    acpi_pm1_cnt_reset(&pm->acpi_regs);
+    acpi_pm_tmr_reset(&pm->acpi_regs);
+    acpi_gpe_reset(&pm->acpi_regs);
+
+    if (kvm_enabled()) {
+        /* Mark SMM as already inited to prevent SMM from running. KVM does not
+         * support SMM mode. */
+        pm->smi_en |= ICH9_PMIO_SMI_EN_APMC_EN;
+    }
+
+    pm_update_sci(pm);
+}
+
+static void pm_powerdown_req(Notifier *n, void *opaque)
+{
+    ICH9LPCPMRegs *pm = container_of(n, ICH9LPCPMRegs, powerdown_notifier);
+
+    acpi_pm1_evt_power_down(&pm->acpi_regs);
+}
+
+void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
+                  qemu_irq sci_irq, qemu_irq cmos_s3)
+{
+    memory_region_init(&pm->io, "ich9-pm", ICH9_PMIO_SIZE);
+    memory_region_set_enabled(&pm->io, false);
+    memory_region_add_subregion(pci_address_space_io(lpc_pci),
+                                0, &pm->io);
+
+    acpi_pm_tmr_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io);
+    acpi_pm1_evt_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io);
+    acpi_pm1_cnt_init(&pm->acpi_regs, &pm->io, 2);
+
+    acpi_gpe_init(&pm->acpi_regs, ICH9_PMIO_GPE0_LEN);
+    memory_region_init_io(&pm->io_gpe, &ich9_gpe_ops, pm, "apci-gpe0",
+                          ICH9_PMIO_GPE0_LEN);
+    memory_region_add_subregion(&pm->io, ICH9_PMIO_GPE0_STS, &pm->io_gpe);
+
+    memory_region_init_io(&pm->io_smi, &ich9_smi_ops, pm, "apci-smi",
+                          8);
+    memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
+
+    pm->irq = sci_irq;
+    qemu_register_reset(pm_reset, pm);
+    pm->powerdown_notifier.notify = pm_powerdown_req;
+    qemu_register_powerdown_notifier(&pm->powerdown_notifier);
+}
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
new file mode 100644 (file)
index 0000000..88386d7
--- /dev/null
@@ -0,0 +1,641 @@
+/*
+ * ACPI implementation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "hw/isa/apm.h"
+#include "hw/i2c/pm_smbus.h"
+#include "hw/pci/pci.h"
+#include "hw/acpi/acpi.h"
+#include "sysemu/sysemu.h"
+#include "qemu/range.h"
+#include "exec/ioport.h"
+#include "hw/nvram/fw_cfg.h"
+#include "exec/address-spaces.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+# define PIIX4_DPRINTF(format, ...)     printf(format, ## __VA_ARGS__)
+#else
+# define PIIX4_DPRINTF(format, ...)     do { } while (0)
+#endif
+
+#define GPE_BASE 0xafe0
+#define GPE_LEN 4
+
+#define PCI_HOTPLUG_ADDR 0xae00
+#define PCI_HOTPLUG_SIZE 0x000f
+#define PCI_UP_BASE 0xae00
+#define PCI_DOWN_BASE 0xae04
+#define PCI_EJ_BASE 0xae08
+#define PCI_RMV_BASE 0xae0c
+
+#define PIIX4_PCI_HOTPLUG_STATUS 2
+
+struct pci_status {
+    uint32_t up; /* deprecated, maintained for migration compatibility */
+    uint32_t down;
+};
+
+typedef struct PIIX4PMState {
+    PCIDevice dev;
+
+    MemoryRegion io;
+    MemoryRegion io_gpe;
+    MemoryRegion io_pci;
+    ACPIREGS ar;
+
+    APMState apm;
+
+    PMSMBus smb;
+    uint32_t smb_io_base;
+
+    qemu_irq irq;
+    qemu_irq smi_irq;
+    int kvm_enabled;
+    Notifier machine_ready;
+    Notifier powerdown_notifier;
+
+    /* for pci hotplug */
+    struct pci_status pci0_status;
+    uint32_t pci0_hotplug_enable;
+    uint32_t pci0_slot_device_present;
+
+    uint8_t disable_s3;
+    uint8_t disable_s4;
+    uint8_t s4_val;
+} PIIX4PMState;
+
+static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
+                                           PCIBus *bus, PIIX4PMState *s);
+
+#define ACPI_ENABLE 0xf1
+#define ACPI_DISABLE 0xf0
+
+static void pm_update_sci(PIIX4PMState *s)
+{
+    int sci_level, pmsts;
+
+    pmsts = acpi_pm1_evt_get_sts(&s->ar);
+    sci_level = (((pmsts & s->ar.pm1.evt.en) &
+                  (ACPI_BITMASK_RT_CLOCK_ENABLE |
+                   ACPI_BITMASK_POWER_BUTTON_ENABLE |
+                   ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
+                   ACPI_BITMASK_TIMER_ENABLE)) != 0) ||
+        (((s->ar.gpe.sts[0] & s->ar.gpe.en[0])
+          & PIIX4_PCI_HOTPLUG_STATUS) != 0);
+
+    qemu_set_irq(s->irq, sci_level);
+    /* schedule a timer interruption if needed */
+    acpi_pm_tmr_update(&s->ar, (s->ar.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
+                       !(pmsts & ACPI_BITMASK_TIMER_STATUS));
+}
+
+static void pm_tmr_timer(ACPIREGS *ar)
+{
+    PIIX4PMState *s = container_of(ar, PIIX4PMState, ar);
+    pm_update_sci(s);
+}
+
+static void apm_ctrl_changed(uint32_t val, void *arg)
+{
+    PIIX4PMState *s = arg;
+
+    /* ACPI specs 3.0, 4.7.2.5 */
+    acpi_pm1_cnt_update(&s->ar, val == ACPI_ENABLE, val == ACPI_DISABLE);
+
+    if (s->dev.config[0x5b] & (1 << 1)) {
+        if (s->smi_irq) {
+            qemu_irq_raise(s->smi_irq);
+        }
+    }
+}
+
+static void pm_io_space_update(PIIX4PMState *s)
+{
+    uint32_t pm_io_base;
+
+    pm_io_base = le32_to_cpu(*(uint32_t *)(s->dev.config + 0x40));
+    pm_io_base &= 0xffc0;
+
+    memory_region_transaction_begin();
+    memory_region_set_enabled(&s->io, s->dev.config[0x80] & 1);
+    memory_region_set_address(&s->io, pm_io_base);
+    memory_region_transaction_commit();
+}
+
+static void smbus_io_space_update(PIIX4PMState *s)
+{
+    s->smb_io_base = le32_to_cpu(*(uint32_t *)(s->dev.config + 0x90));
+    s->smb_io_base &= 0xffc0;
+
+    memory_region_transaction_begin();
+    memory_region_set_enabled(&s->smb.io, s->dev.config[0xd2] & 1);
+    memory_region_set_address(&s->smb.io, s->smb_io_base);
+    memory_region_transaction_commit();
+}
+
+static void pm_write_config(PCIDevice *d,
+                            uint32_t address, uint32_t val, int len)
+{
+    pci_default_write_config(d, address, val, len);
+    if (range_covers_byte(address, len, 0x80) ||
+        ranges_overlap(address, len, 0x40, 4)) {
+        pm_io_space_update((PIIX4PMState *)d);
+    }
+    if (range_covers_byte(address, len, 0xd2) ||
+        ranges_overlap(address, len, 0x90, 4)) {
+        smbus_io_space_update((PIIX4PMState *)d);
+    }
+}
+
+static void vmstate_pci_status_pre_save(void *opaque)
+{
+    struct pci_status *pci0_status = opaque;
+    PIIX4PMState *s = container_of(pci0_status, PIIX4PMState, pci0_status);
+
+    /* We no longer track up, so build a safe value for migrating
+     * to a version that still does... of course these might get lost
+     * by an old buggy implementation, but we try. */
+    pci0_status->up = s->pci0_slot_device_present & s->pci0_hotplug_enable;
+}
+
+static int vmstate_acpi_post_load(void *opaque, int version_id)
+{
+    PIIX4PMState *s = opaque;
+
+    pm_io_space_update(s);
+    return 0;
+}
+
+#define VMSTATE_GPE_ARRAY(_field, _state)                            \
+ {                                                                   \
+     .name       = (stringify(_field)),                              \
+     .version_id = 0,                                                \
+     .info       = &vmstate_info_uint16,                             \
+     .size       = sizeof(uint16_t),                                 \
+     .flags      = VMS_SINGLE | VMS_POINTER,                         \
+     .offset     = vmstate_offset_pointer(_state, _field, uint8_t),  \
+ }
+
+static const VMStateDescription vmstate_gpe = {
+    .name = "gpe",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_GPE_ARRAY(sts, ACPIGPE),
+        VMSTATE_GPE_ARRAY(en, ACPIGPE),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_pci_status = {
+    .name = "pci_status",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_save = vmstate_pci_status_pre_save,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT32(up, struct pci_status),
+        VMSTATE_UINT32(down, struct pci_status),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int acpi_load_old(QEMUFile *f, void *opaque, int version_id)
+{
+    PIIX4PMState *s = opaque;
+    int ret, i;
+    uint16_t temp;
+
+    ret = pci_device_load(&s->dev, f);
+    if (ret < 0) {
+        return ret;
+    }
+    qemu_get_be16s(f, &s->ar.pm1.evt.sts);
+    qemu_get_be16s(f, &s->ar.pm1.evt.en);
+    qemu_get_be16s(f, &s->ar.pm1.cnt.cnt);
+
+    ret = vmstate_load_state(f, &vmstate_apm, &s->apm, 1);
+    if (ret) {
+        return ret;
+    }
+
+    qemu_get_timer(f, s->ar.tmr.timer);
+    qemu_get_sbe64s(f, &s->ar.tmr.overflow_time);
+
+    qemu_get_be16s(f, (uint16_t *)s->ar.gpe.sts);
+    for (i = 0; i < 3; i++) {
+        qemu_get_be16s(f, &temp);
+    }
+
+    qemu_get_be16s(f, (uint16_t *)s->ar.gpe.en);
+    for (i = 0; i < 3; i++) {
+        qemu_get_be16s(f, &temp);
+    }
+
+    ret = vmstate_load_state(f, &vmstate_pci_status, &s->pci0_status, 1);
+    return ret;
+}
+
+/* qemu-kvm 1.2 uses version 3 but advertised as 2
+ * To support incoming qemu-kvm 1.2 migration, change version_id
+ * and minimum_version_id to 2 below (which breaks migration from
+ * qemu 1.2).
+ *
+ */
+static const VMStateDescription vmstate_acpi = {
+    .name = "piix4_pm",
+    .version_id = 3,
+    .minimum_version_id = 3,
+    .minimum_version_id_old = 1,
+    .load_state_old = acpi_load_old,
+    .post_load = vmstate_acpi_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_PCI_DEVICE(dev, PIIX4PMState),
+        VMSTATE_UINT16(ar.pm1.evt.sts, PIIX4PMState),
+        VMSTATE_UINT16(ar.pm1.evt.en, PIIX4PMState),
+        VMSTATE_UINT16(ar.pm1.cnt.cnt, PIIX4PMState),
+        VMSTATE_STRUCT(apm, PIIX4PMState, 0, vmstate_apm, APMState),
+        VMSTATE_TIMER(ar.tmr.timer, PIIX4PMState),
+        VMSTATE_INT64(ar.tmr.overflow_time, PIIX4PMState),
+        VMSTATE_STRUCT(ar.gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE),
+        VMSTATE_STRUCT(pci0_status, PIIX4PMState, 2, vmstate_pci_status,
+                       struct pci_status),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void acpi_piix_eject_slot(PIIX4PMState *s, unsigned slots)
+{
+    BusChild *kid, *next;
+    BusState *bus = qdev_get_parent_bus(&s->dev.qdev);
+    int slot = ffs(slots) - 1;
+    bool slot_free = true;
+
+    /* Mark request as complete */
+    s->pci0_status.down &= ~(1U << slot);
+
+    QTAILQ_FOREACH_SAFE(kid, &bus->children, sibling, next) {
+        DeviceState *qdev = kid->child;
+        PCIDevice *dev = PCI_DEVICE(qdev);
+        PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
+        if (PCI_SLOT(dev->devfn) == slot) {
+            if (pc->no_hotplug) {
+                slot_free = false;
+            } else {
+                qdev_free(qdev);
+            }
+        }
+    }
+    if (slot_free) {
+        s->pci0_slot_device_present &= ~(1U << slot);
+    }
+}
+
+static void piix4_update_hotplug(PIIX4PMState *s)
+{
+    PCIDevice *dev = &s->dev;
+    BusState *bus = qdev_get_parent_bus(&dev->qdev);
+    BusChild *kid, *next;
+
+    /* Execute any pending removes during reset */
+    while (s->pci0_status.down) {
+        acpi_piix_eject_slot(s, s->pci0_status.down);
+    }
+
+    s->pci0_hotplug_enable = ~0;
+    s->pci0_slot_device_present = 0;
+
+    QTAILQ_FOREACH_SAFE(kid, &bus->children, sibling, next) {
+        DeviceState *qdev = kid->child;
+        PCIDevice *pdev = PCI_DEVICE(qdev);
+        PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pdev);
+        int slot = PCI_SLOT(pdev->devfn);
+
+        if (pc->no_hotplug) {
+            s->pci0_hotplug_enable &= ~(1U << slot);
+        }
+
+        s->pci0_slot_device_present |= (1U << slot);
+    }
+}
+
+static void piix4_reset(void *opaque)
+{
+    PIIX4PMState *s = opaque;
+    uint8_t *pci_conf = s->dev.config;
+
+    pci_conf[0x58] = 0;
+    pci_conf[0x59] = 0;
+    pci_conf[0x5a] = 0;
+    pci_conf[0x5b] = 0;
+
+    pci_conf[0x40] = 0x01; /* PM io base read only bit */
+    pci_conf[0x80] = 0;
+
+    if (s->kvm_enabled) {
+        /* Mark SMM as already inited (until KVM supports SMM). */
+        pci_conf[0x5B] = 0x02;
+    }
+    piix4_update_hotplug(s);
+}
+
+static void piix4_pm_powerdown_req(Notifier *n, void *opaque)
+{
+    PIIX4PMState *s = container_of(n, PIIX4PMState, powerdown_notifier);
+
+    assert(s != NULL);
+    acpi_pm1_evt_power_down(&s->ar);
+}
+
+static void piix4_pm_machine_ready(Notifier *n, void *opaque)
+{
+    PIIX4PMState *s = container_of(n, PIIX4PMState, machine_ready);
+    uint8_t *pci_conf;
+
+    pci_conf = s->dev.config;
+    pci_conf[0x5f] = (isa_is_ioport_assigned(0x378) ? 0x80 : 0) | 0x10;
+    pci_conf[0x63] = 0x60;
+    pci_conf[0x67] = (isa_is_ioport_assigned(0x3f8) ? 0x08 : 0) |
+       (isa_is_ioport_assigned(0x2f8) ? 0x90 : 0);
+
+}
+
+static int piix4_pm_initfn(PCIDevice *dev)
+{
+    PIIX4PMState *s = DO_UPCAST(PIIX4PMState, dev, dev);
+    uint8_t *pci_conf;
+
+    pci_conf = s->dev.config;
+    pci_conf[0x06] = 0x80;
+    pci_conf[0x07] = 0x02;
+    pci_conf[0x09] = 0x00;
+    pci_conf[0x3d] = 0x01; // interrupt pin 1
+
+    /* APM */
+    apm_init(dev, &s->apm, apm_ctrl_changed, s);
+
+    if (s->kvm_enabled) {
+        /* Mark SMM as already inited to prevent SMM from running.  KVM does not
+         * support SMM mode. */
+        pci_conf[0x5B] = 0x02;
+    }
+
+    /* XXX: which specification is used ? The i82731AB has different
+       mappings */
+    pci_conf[0x90] = s->smb_io_base | 1;
+    pci_conf[0x91] = s->smb_io_base >> 8;
+    pci_conf[0xd2] = 0x09;
+    pm_smbus_init(&s->dev.qdev, &s->smb);
+    memory_region_set_enabled(&s->smb.io, pci_conf[0xd2] & 1);
+    memory_region_add_subregion(pci_address_space_io(dev),
+                                s->smb_io_base, &s->smb.io);
+
+    memory_region_init(&s->io, "piix4-pm", 64);
+    memory_region_set_enabled(&s->io, false);
+    memory_region_add_subregion(pci_address_space_io(dev),
+                                0, &s->io);
+
+    acpi_pm_tmr_init(&s->ar, pm_tmr_timer, &s->io);
+    acpi_pm1_evt_init(&s->ar, pm_tmr_timer, &s->io);
+    acpi_pm1_cnt_init(&s->ar, &s->io, s->s4_val);
+    acpi_gpe_init(&s->ar, GPE_LEN);
+
+    s->powerdown_notifier.notify = piix4_pm_powerdown_req;
+    qemu_register_powerdown_notifier(&s->powerdown_notifier);
+
+    s->machine_ready.notify = piix4_pm_machine_ready;
+    qemu_add_machine_init_done_notifier(&s->machine_ready);
+    qemu_register_reset(piix4_reset, s);
+
+    piix4_acpi_system_hot_add_init(pci_address_space_io(dev), dev->bus, s);
+
+    return 0;
+}
+
+i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
+                       qemu_irq sci_irq, qemu_irq smi_irq,
+                       int kvm_enabled, void *fw_cfg)
+{
+    PCIDevice *dev;
+    PIIX4PMState *s;
+
+    dev = pci_create(bus, devfn, "PIIX4_PM");
+    qdev_prop_set_uint32(&dev->qdev, "smb_io_base", smb_io_base);
+
+    s = DO_UPCAST(PIIX4PMState, dev, dev);
+    s->irq = sci_irq;
+    s->smi_irq = smi_irq;
+    s->kvm_enabled = kvm_enabled;
+
+    qdev_init_nofail(&dev->qdev);
+
+    if (fw_cfg) {
+        uint8_t suspend[6] = {128, 0, 0, 129, 128, 128};
+        suspend[3] = 1 | ((!s->disable_s3) << 7);
+        suspend[4] = s->s4_val | ((!s->disable_s4) << 7);
+
+        fw_cfg_add_file(fw_cfg, "etc/system-states", g_memdup(suspend, 6), 6);
+    }
+
+    return s->smb.smbus;
+}
+
+static Property piix4_pm_properties[] = {
+    DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0),
+    DEFINE_PROP_UINT8("disable_s3", PIIX4PMState, disable_s3, 0),
+    DEFINE_PROP_UINT8("disable_s4", PIIX4PMState, disable_s4, 0),
+    DEFINE_PROP_UINT8("s4_val", PIIX4PMState, s4_val, 2),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void piix4_pm_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->no_hotplug = 1;
+    k->init = piix4_pm_initfn;
+    k->config_write = pm_write_config;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_82371AB_3;
+    k->revision = 0x03;
+    k->class_id = PCI_CLASS_BRIDGE_OTHER;
+    dc->desc = "PM";
+    dc->no_user = 1;
+    dc->vmsd = &vmstate_acpi;
+    dc->props = piix4_pm_properties;
+}
+
+static const TypeInfo piix4_pm_info = {
+    .name          = "PIIX4_PM",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PIIX4PMState),
+    .class_init    = piix4_pm_class_init,
+};
+
+static void piix4_pm_register_types(void)
+{
+    type_register_static(&piix4_pm_info);
+}
+
+type_init(piix4_pm_register_types)
+
+static uint64_t gpe_readb(void *opaque, hwaddr addr, unsigned width)
+{
+    PIIX4PMState *s = opaque;
+    uint32_t val = acpi_gpe_ioport_readb(&s->ar, addr);
+
+    PIIX4_DPRINTF("gpe read %x == %x\n", addr, val);
+    return val;
+}
+
+static void gpe_writeb(void *opaque, hwaddr addr, uint64_t val,
+                       unsigned width)
+{
+    PIIX4PMState *s = opaque;
+
+    acpi_gpe_ioport_writeb(&s->ar, addr, val);
+    pm_update_sci(s);
+
+    PIIX4_DPRINTF("gpe write %x <== %d\n", addr, val);
+}
+
+static const MemoryRegionOps piix4_gpe_ops = {
+    .read = gpe_readb,
+    .write = gpe_writeb,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 1,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    PIIX4PMState *s = opaque;
+    uint32_t val = 0;
+
+    switch (addr) {
+    case PCI_UP_BASE - PCI_HOTPLUG_ADDR:
+        /* Manufacture an "up" value to cause a device check on any hotplug
+         * slot with a device.  Extra device checks are harmless. */
+        val = s->pci0_slot_device_present & s->pci0_hotplug_enable;
+        PIIX4_DPRINTF("pci_up_read %x\n", val);
+        break;
+    case PCI_DOWN_BASE - PCI_HOTPLUG_ADDR:
+        val = s->pci0_status.down;
+        PIIX4_DPRINTF("pci_down_read %x\n", val);
+        break;
+    case PCI_EJ_BASE - PCI_HOTPLUG_ADDR:
+        /* No feature defined yet */
+        PIIX4_DPRINTF("pci_features_read %x\n", val);
+        break;
+    case PCI_RMV_BASE - PCI_HOTPLUG_ADDR:
+        val = s->pci0_hotplug_enable;
+        break;
+    default:
+        break;
+    }
+
+    return val;
+}
+
+static void pci_write(void *opaque, hwaddr addr, uint64_t data,
+                      unsigned int size)
+{
+    switch (addr) {
+    case PCI_EJ_BASE - PCI_HOTPLUG_ADDR:
+        acpi_piix_eject_slot(opaque, (uint32_t)data);
+        PIIX4_DPRINTF("pciej write %" HWADDR_PRIx " <== % " PRIu64 "\n",
+                      addr, data);
+        break;
+    default:
+        break;
+    }
+}
+
+static const MemoryRegionOps piix4_pci_ops = {
+    .read = pci_read,
+    .write = pci_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
+                                PCIHotplugState state);
+
+static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
+                                           PCIBus *bus, PIIX4PMState *s)
+{
+    memory_region_init_io(&s->io_gpe, &piix4_gpe_ops, s, "apci-gpe0",
+                          GPE_LEN);
+    memory_region_add_subregion(parent, GPE_BASE, &s->io_gpe);
+
+    memory_region_init_io(&s->io_pci, &piix4_pci_ops, s, "apci-pci-hotplug",
+                          PCI_HOTPLUG_SIZE);
+    memory_region_add_subregion(parent, PCI_HOTPLUG_ADDR,
+                                &s->io_pci);
+    pci_bus_hotplug(bus, piix4_device_hotplug, &s->dev.qdev);
+}
+
+static void enable_device(PIIX4PMState *s, int slot)
+{
+    s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
+    s->pci0_slot_device_present |= (1U << slot);
+}
+
+static void disable_device(PIIX4PMState *s, int slot)
+{
+    s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
+    s->pci0_status.down |= (1U << slot);
+}
+
+static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
+                               PCIHotplugState state)
+{
+    int slot = PCI_SLOT(dev->devfn);
+    PIIX4PMState *s = DO_UPCAST(PIIX4PMState, dev,
+                                PCI_DEVICE(qdev));
+
+    /* Don't send event when device is enabled during qemu machine creation:
+     * it is present on boot, no hotplug event is necessary. We do send an
+     * event when the device is disabled later. */
+    if (state == PCI_COLDPLUG_ENABLED) {
+        s->pci0_slot_device_present |= (1U << slot);
+        return 0;
+    }
+
+    if (state == PCI_HOTPLUG_ENABLED) {
+        enable_device(s, slot);
+    } else {
+        disable_device(s, slot);
+    }
+
+    pm_update_sci(s);
+
+    return 0;
+}
diff --git a/hw/acpi_ich9.c b/hw/acpi_ich9.c
deleted file mode 100644 (file)
index e663d29..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * ACPI implementation
- *
- * Copyright (c) 2006 Fabrice Bellard
- * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
- *                    VA Linux Systems Japan K.K.
- * Copyright (C) 2012 Jason Baron <jbaron@redhat.com>
- *
- * This is based on acpi.c.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-#include "hw/hw.h"
-#include "hw/i386/pc.h"
-#include "hw/pci/pci.h"
-#include "qemu/timer.h"
-#include "sysemu/sysemu.h"
-#include "hw/acpi/acpi.h"
-#include "sysemu/kvm.h"
-#include "exec/address-spaces.h"
-
-#include "hw/i386/ich9.h"
-
-//#define DEBUG
-
-#ifdef DEBUG
-#define ICH9_DEBUG(fmt, ...) \
-do { printf("%s "fmt, __func__, ## __VA_ARGS__); } while (0)
-#else
-#define ICH9_DEBUG(fmt, ...)    do { } while (0)
-#endif
-
-static void pm_update_sci(ICH9LPCPMRegs *pm)
-{
-    int sci_level, pm1a_sts;
-
-    pm1a_sts = acpi_pm1_evt_get_sts(&pm->acpi_regs);
-
-    sci_level = (((pm1a_sts & pm->acpi_regs.pm1.evt.en) &
-                  (ACPI_BITMASK_RT_CLOCK_ENABLE |
-                   ACPI_BITMASK_POWER_BUTTON_ENABLE |
-                   ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
-                   ACPI_BITMASK_TIMER_ENABLE)) != 0);
-    qemu_set_irq(pm->irq, sci_level);
-
-    /* schedule a timer interruption if needed */
-    acpi_pm_tmr_update(&pm->acpi_regs,
-                       (pm->acpi_regs.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
-                       !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
-}
-
-static void ich9_pm_update_sci_fn(ACPIREGS *regs)
-{
-    ICH9LPCPMRegs *pm = container_of(regs, ICH9LPCPMRegs, acpi_regs);
-    pm_update_sci(pm);
-}
-
-static uint64_t ich9_gpe_readb(void *opaque, hwaddr addr, unsigned width)
-{
-    ICH9LPCPMRegs *pm = opaque;
-    return acpi_gpe_ioport_readb(&pm->acpi_regs, addr);
-}
-
-static void ich9_gpe_writeb(void *opaque, hwaddr addr, uint64_t val,
-                            unsigned width)
-{
-    ICH9LPCPMRegs *pm = opaque;
-    acpi_gpe_ioport_writeb(&pm->acpi_regs, addr, val);
-}
-
-static const MemoryRegionOps ich9_gpe_ops = {
-    .read = ich9_gpe_readb,
-    .write = ich9_gpe_writeb,
-    .valid.min_access_size = 1,
-    .valid.max_access_size = 4,
-    .impl.min_access_size = 1,
-    .impl.max_access_size = 1,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static uint64_t ich9_smi_readl(void *opaque, hwaddr addr, unsigned width)
-{
-    ICH9LPCPMRegs *pm = opaque;
-    switch (addr) {
-    case 0:
-        return pm->smi_en;
-    case 4:
-        return pm->smi_sts;
-    default:
-        return 0;
-    }
-}
-
-static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val,
-                            unsigned width)
-{
-    ICH9LPCPMRegs *pm = opaque;
-    switch (addr) {
-    case 0:
-        pm->smi_en = val;
-        break;
-    }
-}
-
-static const MemoryRegionOps ich9_smi_ops = {
-    .read = ich9_smi_readl,
-    .write = ich9_smi_writel,
-    .valid.min_access_size = 4,
-    .valid.max_access_size = 4,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-void ich9_pm_iospace_update(ICH9LPCPMRegs *pm, uint32_t pm_io_base)
-{
-    ICH9_DEBUG("to 0x%x\n", pm_io_base);
-
-    assert((pm_io_base & ICH9_PMIO_MASK) == 0);
-
-    pm->pm_io_base = pm_io_base;
-    memory_region_transaction_begin();
-    memory_region_set_enabled(&pm->io, pm->pm_io_base != 0);
-    memory_region_set_address(&pm->io, pm->pm_io_base);
-    memory_region_transaction_commit();
-}
-
-static int ich9_pm_post_load(void *opaque, int version_id)
-{
-    ICH9LPCPMRegs *pm = opaque;
-    uint32_t pm_io_base = pm->pm_io_base;
-    pm->pm_io_base = 0;
-    ich9_pm_iospace_update(pm, pm_io_base);
-    return 0;
-}
-
-#define VMSTATE_GPE_ARRAY(_field, _state)                            \
- {                                                                   \
-     .name       = (stringify(_field)),                              \
-     .version_id = 0,                                                \
-     .num        = ICH9_PMIO_GPE0_LEN,                               \
-     .info       = &vmstate_info_uint8,                              \
-     .size       = sizeof(uint8_t),                                  \
-     .flags      = VMS_ARRAY | VMS_POINTER,                          \
-     .offset     = vmstate_offset_pointer(_state, _field, uint8_t),  \
- }
-
-const VMStateDescription vmstate_ich9_pm = {
-    .name = "ich9_pm",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .post_load = ich9_pm_post_load,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT16(acpi_regs.pm1.evt.sts, ICH9LPCPMRegs),
-        VMSTATE_UINT16(acpi_regs.pm1.evt.en, ICH9LPCPMRegs),
-        VMSTATE_UINT16(acpi_regs.pm1.cnt.cnt, ICH9LPCPMRegs),
-        VMSTATE_TIMER(acpi_regs.tmr.timer, ICH9LPCPMRegs),
-        VMSTATE_INT64(acpi_regs.tmr.overflow_time, ICH9LPCPMRegs),
-        VMSTATE_GPE_ARRAY(acpi_regs.gpe.sts, ICH9LPCPMRegs),
-        VMSTATE_GPE_ARRAY(acpi_regs.gpe.en, ICH9LPCPMRegs),
-        VMSTATE_UINT32(smi_en, ICH9LPCPMRegs),
-        VMSTATE_UINT32(smi_sts, ICH9LPCPMRegs),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void pm_reset(void *opaque)
-{
-    ICH9LPCPMRegs *pm = opaque;
-    ich9_pm_iospace_update(pm, 0);
-
-    acpi_pm1_evt_reset(&pm->acpi_regs);
-    acpi_pm1_cnt_reset(&pm->acpi_regs);
-    acpi_pm_tmr_reset(&pm->acpi_regs);
-    acpi_gpe_reset(&pm->acpi_regs);
-
-    if (kvm_enabled()) {
-        /* Mark SMM as already inited to prevent SMM from running. KVM does not
-         * support SMM mode. */
-        pm->smi_en |= ICH9_PMIO_SMI_EN_APMC_EN;
-    }
-
-    pm_update_sci(pm);
-}
-
-static void pm_powerdown_req(Notifier *n, void *opaque)
-{
-    ICH9LPCPMRegs *pm = container_of(n, ICH9LPCPMRegs, powerdown_notifier);
-
-    acpi_pm1_evt_power_down(&pm->acpi_regs);
-}
-
-void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm,
-                  qemu_irq sci_irq, qemu_irq cmos_s3)
-{
-    memory_region_init(&pm->io, "ich9-pm", ICH9_PMIO_SIZE);
-    memory_region_set_enabled(&pm->io, false);
-    memory_region_add_subregion(pci_address_space_io(lpc_pci),
-                                0, &pm->io);
-
-    acpi_pm_tmr_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io);
-    acpi_pm1_evt_init(&pm->acpi_regs, ich9_pm_update_sci_fn, &pm->io);
-    acpi_pm1_cnt_init(&pm->acpi_regs, &pm->io, 2);
-
-    acpi_gpe_init(&pm->acpi_regs, ICH9_PMIO_GPE0_LEN);
-    memory_region_init_io(&pm->io_gpe, &ich9_gpe_ops, pm, "apci-gpe0",
-                          ICH9_PMIO_GPE0_LEN);
-    memory_region_add_subregion(&pm->io, ICH9_PMIO_GPE0_STS, &pm->io_gpe);
-
-    memory_region_init_io(&pm->io_smi, &ich9_smi_ops, pm, "apci-smi",
-                          8);
-    memory_region_add_subregion(&pm->io, ICH9_PMIO_SMI_EN, &pm->io_smi);
-
-    pm->irq = sci_irq;
-    qemu_register_reset(pm_reset, pm);
-    pm->powerdown_notifier.notify = pm_powerdown_req;
-    qemu_register_powerdown_notifier(&pm->powerdown_notifier);
-}
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
deleted file mode 100644 (file)
index 88386d7..0000000
+++ /dev/null
@@ -1,641 +0,0 @@
-/*
- * ACPI implementation
- *
- * Copyright (c) 2006 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-#include "hw/hw.h"
-#include "hw/i386/pc.h"
-#include "hw/isa/apm.h"
-#include "hw/i2c/pm_smbus.h"
-#include "hw/pci/pci.h"
-#include "hw/acpi/acpi.h"
-#include "sysemu/sysemu.h"
-#include "qemu/range.h"
-#include "exec/ioport.h"
-#include "hw/nvram/fw_cfg.h"
-#include "exec/address-spaces.h"
-
-//#define DEBUG
-
-#ifdef DEBUG
-# define PIIX4_DPRINTF(format, ...)     printf(format, ## __VA_ARGS__)
-#else
-# define PIIX4_DPRINTF(format, ...)     do { } while (0)
-#endif
-
-#define GPE_BASE 0xafe0
-#define GPE_LEN 4
-
-#define PCI_HOTPLUG_ADDR 0xae00
-#define PCI_HOTPLUG_SIZE 0x000f
-#define PCI_UP_BASE 0xae00
-#define PCI_DOWN_BASE 0xae04
-#define PCI_EJ_BASE 0xae08
-#define PCI_RMV_BASE 0xae0c
-
-#define PIIX4_PCI_HOTPLUG_STATUS 2
-
-struct pci_status {
-    uint32_t up; /* deprecated, maintained for migration compatibility */
-    uint32_t down;
-};
-
-typedef struct PIIX4PMState {
-    PCIDevice dev;
-
-    MemoryRegion io;
-    MemoryRegion io_gpe;
-    MemoryRegion io_pci;
-    ACPIREGS ar;
-
-    APMState apm;
-
-    PMSMBus smb;
-    uint32_t smb_io_base;
-
-    qemu_irq irq;
-    qemu_irq smi_irq;
-    int kvm_enabled;
-    Notifier machine_ready;
-    Notifier powerdown_notifier;
-
-    /* for pci hotplug */
-    struct pci_status pci0_status;
-    uint32_t pci0_hotplug_enable;
-    uint32_t pci0_slot_device_present;
-
-    uint8_t disable_s3;
-    uint8_t disable_s4;
-    uint8_t s4_val;
-} PIIX4PMState;
-
-static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
-                                           PCIBus *bus, PIIX4PMState *s);
-
-#define ACPI_ENABLE 0xf1
-#define ACPI_DISABLE 0xf0
-
-static void pm_update_sci(PIIX4PMState *s)
-{
-    int sci_level, pmsts;
-
-    pmsts = acpi_pm1_evt_get_sts(&s->ar);
-    sci_level = (((pmsts & s->ar.pm1.evt.en) &
-                  (ACPI_BITMASK_RT_CLOCK_ENABLE |
-                   ACPI_BITMASK_POWER_BUTTON_ENABLE |
-                   ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
-                   ACPI_BITMASK_TIMER_ENABLE)) != 0) ||
-        (((s->ar.gpe.sts[0] & s->ar.gpe.en[0])
-          & PIIX4_PCI_HOTPLUG_STATUS) != 0);
-
-    qemu_set_irq(s->irq, sci_level);
-    /* schedule a timer interruption if needed */
-    acpi_pm_tmr_update(&s->ar, (s->ar.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
-                       !(pmsts & ACPI_BITMASK_TIMER_STATUS));
-}
-
-static void pm_tmr_timer(ACPIREGS *ar)
-{
-    PIIX4PMState *s = container_of(ar, PIIX4PMState, ar);
-    pm_update_sci(s);
-}
-
-static void apm_ctrl_changed(uint32_t val, void *arg)
-{
-    PIIX4PMState *s = arg;
-
-    /* ACPI specs 3.0, 4.7.2.5 */
-    acpi_pm1_cnt_update(&s->ar, val == ACPI_ENABLE, val == ACPI_DISABLE);
-
-    if (s->dev.config[0x5b] & (1 << 1)) {
-        if (s->smi_irq) {
-            qemu_irq_raise(s->smi_irq);
-        }
-    }
-}
-
-static void pm_io_space_update(PIIX4PMState *s)
-{
-    uint32_t pm_io_base;
-
-    pm_io_base = le32_to_cpu(*(uint32_t *)(s->dev.config + 0x40));
-    pm_io_base &= 0xffc0;
-
-    memory_region_transaction_begin();
-    memory_region_set_enabled(&s->io, s->dev.config[0x80] & 1);
-    memory_region_set_address(&s->io, pm_io_base);
-    memory_region_transaction_commit();
-}
-
-static void smbus_io_space_update(PIIX4PMState *s)
-{
-    s->smb_io_base = le32_to_cpu(*(uint32_t *)(s->dev.config + 0x90));
-    s->smb_io_base &= 0xffc0;
-
-    memory_region_transaction_begin();
-    memory_region_set_enabled(&s->smb.io, s->dev.config[0xd2] & 1);
-    memory_region_set_address(&s->smb.io, s->smb_io_base);
-    memory_region_transaction_commit();
-}
-
-static void pm_write_config(PCIDevice *d,
-                            uint32_t address, uint32_t val, int len)
-{
-    pci_default_write_config(d, address, val, len);
-    if (range_covers_byte(address, len, 0x80) ||
-        ranges_overlap(address, len, 0x40, 4)) {
-        pm_io_space_update((PIIX4PMState *)d);
-    }
-    if (range_covers_byte(address, len, 0xd2) ||
-        ranges_overlap(address, len, 0x90, 4)) {
-        smbus_io_space_update((PIIX4PMState *)d);
-    }
-}
-
-static void vmstate_pci_status_pre_save(void *opaque)
-{
-    struct pci_status *pci0_status = opaque;
-    PIIX4PMState *s = container_of(pci0_status, PIIX4PMState, pci0_status);
-
-    /* We no longer track up, so build a safe value for migrating
-     * to a version that still does... of course these might get lost
-     * by an old buggy implementation, but we try. */
-    pci0_status->up = s->pci0_slot_device_present & s->pci0_hotplug_enable;
-}
-
-static int vmstate_acpi_post_load(void *opaque, int version_id)
-{
-    PIIX4PMState *s = opaque;
-
-    pm_io_space_update(s);
-    return 0;
-}
-
-#define VMSTATE_GPE_ARRAY(_field, _state)                            \
- {                                                                   \
-     .name       = (stringify(_field)),                              \
-     .version_id = 0,                                                \
-     .info       = &vmstate_info_uint16,                             \
-     .size       = sizeof(uint16_t),                                 \
-     .flags      = VMS_SINGLE | VMS_POINTER,                         \
-     .offset     = vmstate_offset_pointer(_state, _field, uint8_t),  \
- }
-
-static const VMStateDescription vmstate_gpe = {
-    .name = "gpe",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField []) {
-        VMSTATE_GPE_ARRAY(sts, ACPIGPE),
-        VMSTATE_GPE_ARRAY(en, ACPIGPE),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_pci_status = {
-    .name = "pci_status",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .pre_save = vmstate_pci_status_pre_save,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT32(up, struct pci_status),
-        VMSTATE_UINT32(down, struct pci_status),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int acpi_load_old(QEMUFile *f, void *opaque, int version_id)
-{
-    PIIX4PMState *s = opaque;
-    int ret, i;
-    uint16_t temp;
-
-    ret = pci_device_load(&s->dev, f);
-    if (ret < 0) {
-        return ret;
-    }
-    qemu_get_be16s(f, &s->ar.pm1.evt.sts);
-    qemu_get_be16s(f, &s->ar.pm1.evt.en);
-    qemu_get_be16s(f, &s->ar.pm1.cnt.cnt);
-
-    ret = vmstate_load_state(f, &vmstate_apm, &s->apm, 1);
-    if (ret) {
-        return ret;
-    }
-
-    qemu_get_timer(f, s->ar.tmr.timer);
-    qemu_get_sbe64s(f, &s->ar.tmr.overflow_time);
-
-    qemu_get_be16s(f, (uint16_t *)s->ar.gpe.sts);
-    for (i = 0; i < 3; i++) {
-        qemu_get_be16s(f, &temp);
-    }
-
-    qemu_get_be16s(f, (uint16_t *)s->ar.gpe.en);
-    for (i = 0; i < 3; i++) {
-        qemu_get_be16s(f, &temp);
-    }
-
-    ret = vmstate_load_state(f, &vmstate_pci_status, &s->pci0_status, 1);
-    return ret;
-}
-
-/* qemu-kvm 1.2 uses version 3 but advertised as 2
- * To support incoming qemu-kvm 1.2 migration, change version_id
- * and minimum_version_id to 2 below (which breaks migration from
- * qemu 1.2).
- *
- */
-static const VMStateDescription vmstate_acpi = {
-    .name = "piix4_pm",
-    .version_id = 3,
-    .minimum_version_id = 3,
-    .minimum_version_id_old = 1,
-    .load_state_old = acpi_load_old,
-    .post_load = vmstate_acpi_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_PCI_DEVICE(dev, PIIX4PMState),
-        VMSTATE_UINT16(ar.pm1.evt.sts, PIIX4PMState),
-        VMSTATE_UINT16(ar.pm1.evt.en, PIIX4PMState),
-        VMSTATE_UINT16(ar.pm1.cnt.cnt, PIIX4PMState),
-        VMSTATE_STRUCT(apm, PIIX4PMState, 0, vmstate_apm, APMState),
-        VMSTATE_TIMER(ar.tmr.timer, PIIX4PMState),
-        VMSTATE_INT64(ar.tmr.overflow_time, PIIX4PMState),
-        VMSTATE_STRUCT(ar.gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE),
-        VMSTATE_STRUCT(pci0_status, PIIX4PMState, 2, vmstate_pci_status,
-                       struct pci_status),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void acpi_piix_eject_slot(PIIX4PMState *s, unsigned slots)
-{
-    BusChild *kid, *next;
-    BusState *bus = qdev_get_parent_bus(&s->dev.qdev);
-    int slot = ffs(slots) - 1;
-    bool slot_free = true;
-
-    /* Mark request as complete */
-    s->pci0_status.down &= ~(1U << slot);
-
-    QTAILQ_FOREACH_SAFE(kid, &bus->children, sibling, next) {
-        DeviceState *qdev = kid->child;
-        PCIDevice *dev = PCI_DEVICE(qdev);
-        PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
-        if (PCI_SLOT(dev->devfn) == slot) {
-            if (pc->no_hotplug) {
-                slot_free = false;
-            } else {
-                qdev_free(qdev);
-            }
-        }
-    }
-    if (slot_free) {
-        s->pci0_slot_device_present &= ~(1U << slot);
-    }
-}
-
-static void piix4_update_hotplug(PIIX4PMState *s)
-{
-    PCIDevice *dev = &s->dev;
-    BusState *bus = qdev_get_parent_bus(&dev->qdev);
-    BusChild *kid, *next;
-
-    /* Execute any pending removes during reset */
-    while (s->pci0_status.down) {
-        acpi_piix_eject_slot(s, s->pci0_status.down);
-    }
-
-    s->pci0_hotplug_enable = ~0;
-    s->pci0_slot_device_present = 0;
-
-    QTAILQ_FOREACH_SAFE(kid, &bus->children, sibling, next) {
-        DeviceState *qdev = kid->child;
-        PCIDevice *pdev = PCI_DEVICE(qdev);
-        PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pdev);
-        int slot = PCI_SLOT(pdev->devfn);
-
-        if (pc->no_hotplug) {
-            s->pci0_hotplug_enable &= ~(1U << slot);
-        }
-
-        s->pci0_slot_device_present |= (1U << slot);
-    }
-}
-
-static void piix4_reset(void *opaque)
-{
-    PIIX4PMState *s = opaque;
-    uint8_t *pci_conf = s->dev.config;
-
-    pci_conf[0x58] = 0;
-    pci_conf[0x59] = 0;
-    pci_conf[0x5a] = 0;
-    pci_conf[0x5b] = 0;
-
-    pci_conf[0x40] = 0x01; /* PM io base read only bit */
-    pci_conf[0x80] = 0;
-
-    if (s->kvm_enabled) {
-        /* Mark SMM as already inited (until KVM supports SMM). */
-        pci_conf[0x5B] = 0x02;
-    }
-    piix4_update_hotplug(s);
-}
-
-static void piix4_pm_powerdown_req(Notifier *n, void *opaque)
-{
-    PIIX4PMState *s = container_of(n, PIIX4PMState, powerdown_notifier);
-
-    assert(s != NULL);
-    acpi_pm1_evt_power_down(&s->ar);
-}
-
-static void piix4_pm_machine_ready(Notifier *n, void *opaque)
-{
-    PIIX4PMState *s = container_of(n, PIIX4PMState, machine_ready);
-    uint8_t *pci_conf;
-
-    pci_conf = s->dev.config;
-    pci_conf[0x5f] = (isa_is_ioport_assigned(0x378) ? 0x80 : 0) | 0x10;
-    pci_conf[0x63] = 0x60;
-    pci_conf[0x67] = (isa_is_ioport_assigned(0x3f8) ? 0x08 : 0) |
-       (isa_is_ioport_assigned(0x2f8) ? 0x90 : 0);
-
-}
-
-static int piix4_pm_initfn(PCIDevice *dev)
-{
-    PIIX4PMState *s = DO_UPCAST(PIIX4PMState, dev, dev);
-    uint8_t *pci_conf;
-
-    pci_conf = s->dev.config;
-    pci_conf[0x06] = 0x80;
-    pci_conf[0x07] = 0x02;
-    pci_conf[0x09] = 0x00;
-    pci_conf[0x3d] = 0x01; // interrupt pin 1
-
-    /* APM */
-    apm_init(dev, &s->apm, apm_ctrl_changed, s);
-
-    if (s->kvm_enabled) {
-        /* Mark SMM as already inited to prevent SMM from running.  KVM does not
-         * support SMM mode. */
-        pci_conf[0x5B] = 0x02;
-    }
-
-    /* XXX: which specification is used ? The i82731AB has different
-       mappings */
-    pci_conf[0x90] = s->smb_io_base | 1;
-    pci_conf[0x91] = s->smb_io_base >> 8;
-    pci_conf[0xd2] = 0x09;
-    pm_smbus_init(&s->dev.qdev, &s->smb);
-    memory_region_set_enabled(&s->smb.io, pci_conf[0xd2] & 1);
-    memory_region_add_subregion(pci_address_space_io(dev),
-                                s->smb_io_base, &s->smb.io);
-
-    memory_region_init(&s->io, "piix4-pm", 64);
-    memory_region_set_enabled(&s->io, false);
-    memory_region_add_subregion(pci_address_space_io(dev),
-                                0, &s->io);
-
-    acpi_pm_tmr_init(&s->ar, pm_tmr_timer, &s->io);
-    acpi_pm1_evt_init(&s->ar, pm_tmr_timer, &s->io);
-    acpi_pm1_cnt_init(&s->ar, &s->io, s->s4_val);
-    acpi_gpe_init(&s->ar, GPE_LEN);
-
-    s->powerdown_notifier.notify = piix4_pm_powerdown_req;
-    qemu_register_powerdown_notifier(&s->powerdown_notifier);
-
-    s->machine_ready.notify = piix4_pm_machine_ready;
-    qemu_add_machine_init_done_notifier(&s->machine_ready);
-    qemu_register_reset(piix4_reset, s);
-
-    piix4_acpi_system_hot_add_init(pci_address_space_io(dev), dev->bus, s);
-
-    return 0;
-}
-
-i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
-                       qemu_irq sci_irq, qemu_irq smi_irq,
-                       int kvm_enabled, void *fw_cfg)
-{
-    PCIDevice *dev;
-    PIIX4PMState *s;
-
-    dev = pci_create(bus, devfn, "PIIX4_PM");
-    qdev_prop_set_uint32(&dev->qdev, "smb_io_base", smb_io_base);
-
-    s = DO_UPCAST(PIIX4PMState, dev, dev);
-    s->irq = sci_irq;
-    s->smi_irq = smi_irq;
-    s->kvm_enabled = kvm_enabled;
-
-    qdev_init_nofail(&dev->qdev);
-
-    if (fw_cfg) {
-        uint8_t suspend[6] = {128, 0, 0, 129, 128, 128};
-        suspend[3] = 1 | ((!s->disable_s3) << 7);
-        suspend[4] = s->s4_val | ((!s->disable_s4) << 7);
-
-        fw_cfg_add_file(fw_cfg, "etc/system-states", g_memdup(suspend, 6), 6);
-    }
-
-    return s->smb.smbus;
-}
-
-static Property piix4_pm_properties[] = {
-    DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0),
-    DEFINE_PROP_UINT8("disable_s3", PIIX4PMState, disable_s3, 0),
-    DEFINE_PROP_UINT8("disable_s4", PIIX4PMState, disable_s4, 0),
-    DEFINE_PROP_UINT8("s4_val", PIIX4PMState, s4_val, 2),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void piix4_pm_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->no_hotplug = 1;
-    k->init = piix4_pm_initfn;
-    k->config_write = pm_write_config;
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->device_id = PCI_DEVICE_ID_INTEL_82371AB_3;
-    k->revision = 0x03;
-    k->class_id = PCI_CLASS_BRIDGE_OTHER;
-    dc->desc = "PM";
-    dc->no_user = 1;
-    dc->vmsd = &vmstate_acpi;
-    dc->props = piix4_pm_properties;
-}
-
-static const TypeInfo piix4_pm_info = {
-    .name          = "PIIX4_PM",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PIIX4PMState),
-    .class_init    = piix4_pm_class_init,
-};
-
-static void piix4_pm_register_types(void)
-{
-    type_register_static(&piix4_pm_info);
-}
-
-type_init(piix4_pm_register_types)
-
-static uint64_t gpe_readb(void *opaque, hwaddr addr, unsigned width)
-{
-    PIIX4PMState *s = opaque;
-    uint32_t val = acpi_gpe_ioport_readb(&s->ar, addr);
-
-    PIIX4_DPRINTF("gpe read %x == %x\n", addr, val);
-    return val;
-}
-
-static void gpe_writeb(void *opaque, hwaddr addr, uint64_t val,
-                       unsigned width)
-{
-    PIIX4PMState *s = opaque;
-
-    acpi_gpe_ioport_writeb(&s->ar, addr, val);
-    pm_update_sci(s);
-
-    PIIX4_DPRINTF("gpe write %x <== %d\n", addr, val);
-}
-
-static const MemoryRegionOps piix4_gpe_ops = {
-    .read = gpe_readb,
-    .write = gpe_writeb,
-    .valid.min_access_size = 1,
-    .valid.max_access_size = 4,
-    .impl.min_access_size = 1,
-    .impl.max_access_size = 1,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size)
-{
-    PIIX4PMState *s = opaque;
-    uint32_t val = 0;
-
-    switch (addr) {
-    case PCI_UP_BASE - PCI_HOTPLUG_ADDR:
-        /* Manufacture an "up" value to cause a device check on any hotplug
-         * slot with a device.  Extra device checks are harmless. */
-        val = s->pci0_slot_device_present & s->pci0_hotplug_enable;
-        PIIX4_DPRINTF("pci_up_read %x\n", val);
-        break;
-    case PCI_DOWN_BASE - PCI_HOTPLUG_ADDR:
-        val = s->pci0_status.down;
-        PIIX4_DPRINTF("pci_down_read %x\n", val);
-        break;
-    case PCI_EJ_BASE - PCI_HOTPLUG_ADDR:
-        /* No feature defined yet */
-        PIIX4_DPRINTF("pci_features_read %x\n", val);
-        break;
-    case PCI_RMV_BASE - PCI_HOTPLUG_ADDR:
-        val = s->pci0_hotplug_enable;
-        break;
-    default:
-        break;
-    }
-
-    return val;
-}
-
-static void pci_write(void *opaque, hwaddr addr, uint64_t data,
-                      unsigned int size)
-{
-    switch (addr) {
-    case PCI_EJ_BASE - PCI_HOTPLUG_ADDR:
-        acpi_piix_eject_slot(opaque, (uint32_t)data);
-        PIIX4_DPRINTF("pciej write %" HWADDR_PRIx " <== % " PRIu64 "\n",
-                      addr, data);
-        break;
-    default:
-        break;
-    }
-}
-
-static const MemoryRegionOps piix4_pci_ops = {
-    .read = pci_read,
-    .write = pci_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .valid = {
-        .min_access_size = 4,
-        .max_access_size = 4,
-    },
-};
-
-static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
-                                PCIHotplugState state);
-
-static void piix4_acpi_system_hot_add_init(MemoryRegion *parent,
-                                           PCIBus *bus, PIIX4PMState *s)
-{
-    memory_region_init_io(&s->io_gpe, &piix4_gpe_ops, s, "apci-gpe0",
-                          GPE_LEN);
-    memory_region_add_subregion(parent, GPE_BASE, &s->io_gpe);
-
-    memory_region_init_io(&s->io_pci, &piix4_pci_ops, s, "apci-pci-hotplug",
-                          PCI_HOTPLUG_SIZE);
-    memory_region_add_subregion(parent, PCI_HOTPLUG_ADDR,
-                                &s->io_pci);
-    pci_bus_hotplug(bus, piix4_device_hotplug, &s->dev.qdev);
-}
-
-static void enable_device(PIIX4PMState *s, int slot)
-{
-    s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
-    s->pci0_slot_device_present |= (1U << slot);
-}
-
-static void disable_device(PIIX4PMState *s, int slot)
-{
-    s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS;
-    s->pci0_status.down |= (1U << slot);
-}
-
-static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev,
-                               PCIHotplugState state)
-{
-    int slot = PCI_SLOT(dev->devfn);
-    PIIX4PMState *s = DO_UPCAST(PIIX4PMState, dev,
-                                PCI_DEVICE(qdev));
-
-    /* Don't send event when device is enabled during qemu machine creation:
-     * it is present on boot, no hotplug event is necessary. We do send an
-     * event when the device is disabled later. */
-    if (state == PCI_COLDPLUG_ENABLED) {
-        s->pci0_slot_device_present |= (1U << slot);
-        return 0;
-    }
-
-    if (state == PCI_HOTPLUG_ENABLED) {
-        enable_device(s, slot);
-    } else {
-        disable_device(s, slot);
-    }
-
-    pm_update_sci(s);
-
-    return 0;
-}
diff --git a/hw/adb.c b/hw/adb.c
deleted file mode 100644 (file)
index a75d3fd..0000000
--- a/hw/adb.c
+++ /dev/null
@@ -1,581 +0,0 @@
-/*
- * QEMU ADB support
- *
- * Copyright (c) 2004 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/input/adb.h"
-#include "ui/console.h"
-
-/* debug ADB */
-//#define DEBUG_ADB
-
-#ifdef DEBUG_ADB
-#define ADB_DPRINTF(fmt, ...) \
-do { printf("ADB: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define ADB_DPRINTF(fmt, ...)
-#endif
-
-/* ADB commands */
-#define ADB_BUSRESET           0x00
-#define ADB_FLUSH               0x01
-#define ADB_WRITEREG           0x08
-#define ADB_READREG            0x0c
-
-/* ADB device commands */
-#define ADB_CMD_SELF_TEST              0xff
-#define ADB_CMD_CHANGE_ID              0xfe
-#define ADB_CMD_CHANGE_ID_AND_ACT      0xfd
-#define ADB_CMD_CHANGE_ID_AND_ENABLE   0x00
-
-/* ADB default device IDs (upper 4 bits of ADB command byte) */
-#define ADB_DEVID_DONGLE   1
-#define ADB_DEVID_KEYBOARD 2
-#define ADB_DEVID_MOUSE    3
-#define ADB_DEVID_TABLET   4
-#define ADB_DEVID_MODEM    5
-#define ADB_DEVID_MISC     7
-
-/* error codes */
-#define ADB_RET_NOTPRESENT (-2)
-
-static void adb_device_reset(ADBDevice *d)
-{
-    qdev_reset_all(DEVICE(d));
-}
-
-int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len)
-{
-    ADBDevice *d;
-    int devaddr, cmd, i;
-
-    cmd = buf[0] & 0xf;
-    if (cmd == ADB_BUSRESET) {
-        for(i = 0; i < s->nb_devices; i++) {
-            d = s->devices[i];
-            adb_device_reset(d);
-        }
-        return 0;
-    }
-    devaddr = buf[0] >> 4;
-    for(i = 0; i < s->nb_devices; i++) {
-        d = s->devices[i];
-        if (d->devaddr == devaddr) {
-            ADBDeviceClass *adc = ADB_DEVICE_GET_CLASS(d);
-            return adc->devreq(d, obuf, buf, len);
-        }
-    }
-    return ADB_RET_NOTPRESENT;
-}
-
-/* XXX: move that to cuda ? */
-int adb_poll(ADBBusState *s, uint8_t *obuf)
-{
-    ADBDevice *d;
-    int olen, i;
-    uint8_t buf[1];
-
-    olen = 0;
-    for(i = 0; i < s->nb_devices; i++) {
-        if (s->poll_index >= s->nb_devices)
-            s->poll_index = 0;
-        d = s->devices[s->poll_index];
-        buf[0] = ADB_READREG | (d->devaddr << 4);
-        olen = adb_request(s, obuf + 1, buf, 1);
-        /* if there is data, we poll again the same device */
-        if (olen > 0) {
-            obuf[0] = buf[0];
-            olen++;
-            break;
-        }
-        s->poll_index++;
-    }
-    return olen;
-}
-
-static const TypeInfo adb_bus_type_info = {
-    .name = TYPE_ADB_BUS,
-    .parent = TYPE_BUS,
-    .instance_size = sizeof(ADBBusState),
-};
-
-static void adb_device_realizefn(DeviceState *dev, Error **errp)
-{
-    ADBDevice *d = ADB_DEVICE(dev);
-    ADBBusState *bus = ADB_BUS(qdev_get_parent_bus(dev));
-
-    if (bus->nb_devices >= MAX_ADB_DEVICES) {
-        return;
-    }
-
-    bus->devices[bus->nb_devices++] = d;
-}
-
-static void adb_device_class_init(ObjectClass *oc, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(oc);
-
-    dc->realize = adb_device_realizefn;
-    dc->bus_type = TYPE_ADB_BUS;
-}
-
-static const TypeInfo adb_device_type_info = {
-    .name = TYPE_ADB_DEVICE,
-    .parent = TYPE_DEVICE,
-    .instance_size = sizeof(ADBDevice),
-    .abstract = true,
-    .class_init = adb_device_class_init,
-};
-
-/***************************************************************/
-/* Keyboard ADB device */
-
-#define ADB_KEYBOARD(obj) OBJECT_CHECK(KBDState, (obj), TYPE_ADB_KEYBOARD)
-
-typedef struct KBDState {
-    /*< private >*/
-    ADBDevice parent_obj;
-    /*< public >*/
-
-    uint8_t data[128];
-    int rptr, wptr, count;
-} KBDState;
-
-#define ADB_KEYBOARD_CLASS(class) \
-    OBJECT_CLASS_CHECK(ADBKeyboardClass, (class), TYPE_ADB_KEYBOARD)
-#define ADB_KEYBOARD_GET_CLASS(obj) \
-    OBJECT_GET_CLASS(ADBKeyboardClass, (obj), TYPE_ADB_KEYBOARD)
-
-typedef struct ADBKeyboardClass {
-    /*< private >*/
-    ADBDeviceClass parent_class;
-    /*< public >*/
-
-    DeviceRealize parent_realize;
-} ADBKeyboardClass;
-
-static const uint8_t pc_to_adb_keycode[256] = {
-  0, 53, 18, 19, 20, 21, 23, 22, 26, 28, 25, 29, 27, 24, 51, 48,
- 12, 13, 14, 15, 17, 16, 32, 34, 31, 35, 33, 30, 36, 54,  0,  1,
-  2,  3,  5,  4, 38, 40, 37, 41, 39, 50, 56, 42,  6,  7,  8,  9,
- 11, 45, 46, 43, 47, 44,123, 67, 58, 49, 57,122,120, 99,118, 96,
- 97, 98,100,101,109, 71,107, 89, 91, 92, 78, 86, 87, 88, 69, 83,
- 84, 85, 82, 65,  0,  0, 10,103,111,  0,  0,110, 81,  0,  0,  0,
-  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-  0,  0,  0, 94,  0, 93,  0,  0,  0,  0,  0,  0,104,102,  0,  0,
-  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 76,125,  0,  0,
-  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,105,  0,  0,  0,  0,  0,
-  0,  0,  0,  0,  0, 75,  0,  0,124,  0,  0,  0,  0,  0,  0,  0,
-  0,  0,  0,  0,  0,  0,  0,115, 62,116,  0, 59,  0, 60,  0,119,
- 61,121,114,117,  0,  0,  0,  0,  0,  0,  0, 55,126,  0,127,  0,
-  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-  0,  0,  0,  0,  0, 95,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-};
-
-static void adb_kbd_put_keycode(void *opaque, int keycode)
-{
-    KBDState *s = opaque;
-
-    if (s->count < sizeof(s->data)) {
-        s->data[s->wptr] = keycode;
-        if (++s->wptr == sizeof(s->data))
-            s->wptr = 0;
-        s->count++;
-    }
-}
-
-static int adb_kbd_poll(ADBDevice *d, uint8_t *obuf)
-{
-    static int ext_keycode;
-    KBDState *s = ADB_KEYBOARD(d);
-    int adb_keycode, keycode;
-    int olen;
-
-    olen = 0;
-    for(;;) {
-        if (s->count == 0)
-            break;
-        keycode = s->data[s->rptr];
-        if (++s->rptr == sizeof(s->data))
-            s->rptr = 0;
-        s->count--;
-
-        if (keycode == 0xe0) {
-            ext_keycode = 1;
-        } else {
-            if (ext_keycode)
-                adb_keycode =  pc_to_adb_keycode[keycode | 0x80];
-            else
-                adb_keycode =  pc_to_adb_keycode[keycode & 0x7f];
-            obuf[0] = adb_keycode | (keycode & 0x80);
-            /* NOTE: could put a second keycode if needed */
-            obuf[1] = 0xff;
-            olen = 2;
-            ext_keycode = 0;
-            break;
-        }
-    }
-    return olen;
-}
-
-static int adb_kbd_request(ADBDevice *d, uint8_t *obuf,
-                           const uint8_t *buf, int len)
-{
-    KBDState *s = ADB_KEYBOARD(d);
-    int cmd, reg, olen;
-
-    if ((buf[0] & 0x0f) == ADB_FLUSH) {
-        /* flush keyboard fifo */
-        s->wptr = s->rptr = s->count = 0;
-        return 0;
-    }
-
-    cmd = buf[0] & 0xc;
-    reg = buf[0] & 0x3;
-    olen = 0;
-    switch(cmd) {
-    case ADB_WRITEREG:
-        switch(reg) {
-        case 2:
-            /* LED status */
-            break;
-        case 3:
-            switch(buf[2]) {
-            case ADB_CMD_SELF_TEST:
-                break;
-            case ADB_CMD_CHANGE_ID:
-            case ADB_CMD_CHANGE_ID_AND_ACT:
-            case ADB_CMD_CHANGE_ID_AND_ENABLE:
-                d->devaddr = buf[1] & 0xf;
-                break;
-            default:
-                /* XXX: check this */
-                d->devaddr = buf[1] & 0xf;
-                d->handler = buf[2];
-                break;
-            }
-        }
-        break;
-    case ADB_READREG:
-        switch(reg) {
-        case 0:
-            olen = adb_kbd_poll(d, obuf);
-            break;
-        case 1:
-            break;
-        case 2:
-            obuf[0] = 0x00; /* XXX: check this */
-            obuf[1] = 0x07; /* led status */
-            olen = 2;
-            break;
-        case 3:
-            obuf[0] = d->handler;
-            obuf[1] = d->devaddr;
-            olen = 2;
-            break;
-        }
-        break;
-    }
-    return olen;
-}
-
-static const VMStateDescription vmstate_adb_kbd = {
-    .name = "adb_kbd",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_BUFFER(data, KBDState),
-        VMSTATE_INT32(rptr, KBDState),
-        VMSTATE_INT32(wptr, KBDState),
-        VMSTATE_INT32(count, KBDState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void adb_kbd_reset(DeviceState *dev)
-{
-    ADBDevice *d = ADB_DEVICE(dev);
-    KBDState *s = ADB_KEYBOARD(dev);
-
-    d->handler = 1;
-    d->devaddr = ADB_DEVID_KEYBOARD;
-    memset(s->data, 0, sizeof(s->data));
-    s->rptr = 0;
-    s->wptr = 0;
-    s->count = 0;
-}
-
-static void adb_kbd_realizefn(DeviceState *dev, Error **errp)
-{
-    ADBDevice *d = ADB_DEVICE(dev);
-    ADBKeyboardClass *akc = ADB_KEYBOARD_GET_CLASS(dev);
-
-    akc->parent_realize(dev, errp);
-
-    qemu_add_kbd_event_handler(adb_kbd_put_keycode, d);
-}
-
-static void adb_kbd_initfn(Object *obj)
-{
-    ADBDevice *d = ADB_DEVICE(obj);
-
-    d->devaddr = ADB_DEVID_KEYBOARD;
-}
-
-static void adb_kbd_class_init(ObjectClass *oc, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(oc);
-    ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc);
-    ADBKeyboardClass *akc = ADB_KEYBOARD_CLASS(oc);
-
-    akc->parent_realize = dc->realize;
-    dc->realize = adb_kbd_realizefn;
-
-    adc->devreq = adb_kbd_request;
-    dc->reset = adb_kbd_reset;
-    dc->vmsd = &vmstate_adb_kbd;
-}
-
-static const TypeInfo adb_kbd_type_info = {
-    .name = TYPE_ADB_KEYBOARD,
-    .parent = TYPE_ADB_DEVICE,
-    .instance_size = sizeof(KBDState),
-    .instance_init = adb_kbd_initfn,
-    .class_init = adb_kbd_class_init,
-    .class_size = sizeof(ADBKeyboardClass),
-};
-
-/***************************************************************/
-/* Mouse ADB device */
-
-#define ADB_MOUSE(obj) OBJECT_CHECK(MouseState, (obj), TYPE_ADB_MOUSE)
-
-typedef struct MouseState {
-    /*< public >*/
-    ADBDevice parent_obj;
-    /*< private >*/
-
-    int buttons_state, last_buttons_state;
-    int dx, dy, dz;
-} MouseState;
-
-#define ADB_MOUSE_CLASS(class) \
-    OBJECT_CLASS_CHECK(ADBMouseClass, (class), TYPE_ADB_MOUSE)
-#define ADB_MOUSE_GET_CLASS(obj) \
-    OBJECT_GET_CLASS(ADBMouseClass, (obj), TYPE_ADB_MOUSE)
-
-typedef struct ADBMouseClass {
-    /*< public >*/
-    ADBDeviceClass parent_class;
-    /*< private >*/
-
-    DeviceRealize parent_realize;
-} ADBMouseClass;
-
-static void adb_mouse_event(void *opaque,
-                            int dx1, int dy1, int dz1, int buttons_state)
-{
-    MouseState *s = opaque;
-
-    s->dx += dx1;
-    s->dy += dy1;
-    s->dz += dz1;
-    s->buttons_state = buttons_state;
-}
-
-
-static int adb_mouse_poll(ADBDevice *d, uint8_t *obuf)
-{
-    MouseState *s = ADB_MOUSE(d);
-    int dx, dy;
-
-    if (s->last_buttons_state == s->buttons_state &&
-        s->dx == 0 && s->dy == 0)
-        return 0;
-
-    dx = s->dx;
-    if (dx < -63)
-        dx = -63;
-    else if (dx > 63)
-        dx = 63;
-
-    dy = s->dy;
-    if (dy < -63)
-        dy = -63;
-    else if (dy > 63)
-        dy = 63;
-
-    s->dx -= dx;
-    s->dy -= dy;
-    s->last_buttons_state = s->buttons_state;
-
-    dx &= 0x7f;
-    dy &= 0x7f;
-
-    if (!(s->buttons_state & MOUSE_EVENT_LBUTTON))
-        dy |= 0x80;
-    if (!(s->buttons_state & MOUSE_EVENT_RBUTTON))
-        dx |= 0x80;
-
-    obuf[0] = dy;
-    obuf[1] = dx;
-    return 2;
-}
-
-static int adb_mouse_request(ADBDevice *d, uint8_t *obuf,
-                             const uint8_t *buf, int len)
-{
-    MouseState *s = ADB_MOUSE(d);
-    int cmd, reg, olen;
-
-    if ((buf[0] & 0x0f) == ADB_FLUSH) {
-        /* flush mouse fifo */
-        s->buttons_state = s->last_buttons_state;
-        s->dx = 0;
-        s->dy = 0;
-        s->dz = 0;
-        return 0;
-    }
-
-    cmd = buf[0] & 0xc;
-    reg = buf[0] & 0x3;
-    olen = 0;
-    switch(cmd) {
-    case ADB_WRITEREG:
-        ADB_DPRINTF("write reg %d val 0x%2.2x\n", reg, buf[1]);
-        switch(reg) {
-        case 2:
-            break;
-        case 3:
-            switch(buf[2]) {
-            case ADB_CMD_SELF_TEST:
-                break;
-            case ADB_CMD_CHANGE_ID:
-            case ADB_CMD_CHANGE_ID_AND_ACT:
-            case ADB_CMD_CHANGE_ID_AND_ENABLE:
-                d->devaddr = buf[1] & 0xf;
-                break;
-            default:
-                /* XXX: check this */
-                d->devaddr = buf[1] & 0xf;
-                break;
-            }
-        }
-        break;
-    case ADB_READREG:
-        switch(reg) {
-        case 0:
-            olen = adb_mouse_poll(d, obuf);
-            break;
-        case 1:
-            break;
-        case 3:
-            obuf[0] = d->handler;
-            obuf[1] = d->devaddr;
-            olen = 2;
-            break;
-        }
-        ADB_DPRINTF("read reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x\n", reg,
-                    obuf[0], obuf[1]);
-        break;
-    }
-    return olen;
-}
-
-static void adb_mouse_reset(DeviceState *dev)
-{
-    ADBDevice *d = ADB_DEVICE(dev);
-    MouseState *s = ADB_MOUSE(dev);
-
-    d->handler = 2;
-    d->devaddr = ADB_DEVID_MOUSE;
-    s->last_buttons_state = s->buttons_state = 0;
-    s->dx = s->dy = s->dz = 0;
-}
-
-static const VMStateDescription vmstate_adb_mouse = {
-    .name = "adb_mouse",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_INT32(buttons_state, MouseState),
-        VMSTATE_INT32(last_buttons_state, MouseState),
-        VMSTATE_INT32(dx, MouseState),
-        VMSTATE_INT32(dy, MouseState),
-        VMSTATE_INT32(dz, MouseState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void adb_mouse_realizefn(DeviceState *dev, Error **errp)
-{
-    MouseState *s = ADB_MOUSE(dev);
-    ADBMouseClass *amc = ADB_MOUSE_GET_CLASS(dev);
-
-    amc->parent_realize(dev, errp);
-
-    qemu_add_mouse_event_handler(adb_mouse_event, s, 0, "QEMU ADB Mouse");
-}
-
-static void adb_mouse_initfn(Object *obj)
-{
-    ADBDevice *d = ADB_DEVICE(obj);
-
-    d->devaddr = ADB_DEVID_MOUSE;
-}
-
-static void adb_mouse_class_init(ObjectClass *oc, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(oc);
-    ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc);
-    ADBMouseClass *amc = ADB_MOUSE_CLASS(oc);
-
-    amc->parent_realize = dc->realize;
-    dc->realize = adb_mouse_realizefn;
-
-    adc->devreq = adb_mouse_request;
-    dc->reset = adb_mouse_reset;
-    dc->vmsd = &vmstate_adb_mouse;
-}
-
-static const TypeInfo adb_mouse_type_info = {
-    .name = TYPE_ADB_MOUSE,
-    .parent = TYPE_ADB_DEVICE,
-    .instance_size = sizeof(MouseState),
-    .instance_init = adb_mouse_initfn,
-    .class_init = adb_mouse_class_init,
-    .class_size = sizeof(ADBMouseClass),
-};
-
-
-static void adb_register_types(void)
-{
-    type_register_static(&adb_bus_type_info);
-    type_register_static(&adb_device_type_info);
-    type_register_static(&adb_kbd_type_info);
-    type_register_static(&adb_mouse_type_info);
-}
-
-type_init(adb_register_types)
diff --git a/hw/adlib.c b/hw/adlib.c
deleted file mode 100644 (file)
index 133c0ff..0000000
+++ /dev/null
@@ -1,337 +0,0 @@
-/*
- * QEMU Proxy for OPL2/3 emulation by MAME team
- *
- * Copyright (c) 2004-2005 Vassili Karpov (malc)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/hw.h"
-#include "hw/audio/audio.h"
-#include "audio/audio.h"
-#include "hw/isa/isa.h"
-
-//#define DEBUG
-
-#define ADLIB_KILL_TIMERS 1
-
-#ifdef DEBUG
-#include "qemu/timer.h"
-#endif
-
-#define dolog(...) AUD_log ("adlib", __VA_ARGS__)
-#ifdef DEBUG
-#define ldebug(...) dolog (__VA_ARGS__)
-#else
-#define ldebug(...)
-#endif
-
-#ifdef HAS_YMF262
-#include "ymf262.h"
-void YMF262UpdateOneQEMU (int which, INT16 *dst, int length);
-#define SHIFT 2
-#else
-#include "hw/fmopl.h"
-#define SHIFT 1
-#endif
-
-#define IO_READ_PROTO(name) \
-    uint32_t name (void *opaque, uint32_t nport)
-#define IO_WRITE_PROTO(name) \
-    void name (void *opaque, uint32_t nport, uint32_t val)
-
-static struct {
-    int port;
-    int freq;
-} conf = {0x220, 44100};
-
-typedef struct {
-    QEMUSoundCard card;
-    int ticking[2];
-    int enabled;
-    int active;
-    int bufpos;
-#ifdef DEBUG
-    int64_t exp[2];
-#endif
-    int16_t *mixbuf;
-    uint64_t dexp[2];
-    SWVoiceOut *voice;
-    int left, pos, samples;
-    QEMUAudioTimeStamp ats;
-#ifndef HAS_YMF262
-    FM_OPL *opl;
-#endif
-} AdlibState;
-
-static AdlibState glob_adlib;
-
-static void adlib_stop_opl_timer (AdlibState *s, size_t n)
-{
-#ifdef HAS_YMF262
-    YMF262TimerOver (0, n);
-#else
-    OPLTimerOver (s->opl, n);
-#endif
-    s->ticking[n] = 0;
-}
-
-static void adlib_kill_timers (AdlibState *s)
-{
-    size_t i;
-
-    for (i = 0; i < 2; ++i) {
-        if (s->ticking[i]) {
-            uint64_t delta;
-
-            delta = AUD_get_elapsed_usec_out (s->voice, &s->ats);
-            ldebug (
-                "delta = %f dexp = %f expired => %d\n",
-                delta / 1000000.0,
-                s->dexp[i] / 1000000.0,
-                delta >= s->dexp[i]
-                );
-            if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) {
-                adlib_stop_opl_timer (s, i);
-                AUD_init_time_stamp_out (s->voice, &s->ats);
-            }
-        }
-    }
-}
-
-static IO_WRITE_PROTO (adlib_write)
-{
-    AdlibState *s = opaque;
-    int a = nport & 3;
-
-    s->active = 1;
-    AUD_set_active_out (s->voice, 1);
-
-    adlib_kill_timers (s);
-
-#ifdef HAS_YMF262
-    YMF262Write (0, a, val);
-#else
-    OPLWrite (s->opl, a, val);
-#endif
-}
-
-static IO_READ_PROTO (adlib_read)
-{
-    AdlibState *s = opaque;
-    uint8_t data;
-    int a = nport & 3;
-
-    adlib_kill_timers (s);
-
-#ifdef HAS_YMF262
-    data = YMF262Read (0, a);
-#else
-    data = OPLRead (s->opl, a);
-#endif
-    return data;
-}
-
-static void timer_handler (int c, double interval_Sec)
-{
-    AdlibState *s = &glob_adlib;
-    unsigned n = c & 1;
-#ifdef DEBUG
-    double interval;
-    int64_t exp;
-#endif
-
-    if (interval_Sec == 0.0) {
-        s->ticking[n] = 0;
-        return;
-    }
-
-    s->ticking[n] = 1;
-#ifdef DEBUG
-    interval = get_ticks_per_sec () * interval_Sec;
-    exp = qemu_get_clock_ns (vm_clock) + interval;
-    s->exp[n] = exp;
-#endif
-
-    s->dexp[n] = interval_Sec * 1000000.0;
-    AUD_init_time_stamp_out (s->voice, &s->ats);
-}
-
-static int write_audio (AdlibState *s, int samples)
-{
-    int net = 0;
-    int pos = s->pos;
-
-    while (samples) {
-        int nbytes, wbytes, wsampl;
-
-        nbytes = samples << SHIFT;
-        wbytes = AUD_write (
-            s->voice,
-            s->mixbuf + (pos << (SHIFT - 1)),
-            nbytes
-            );
-
-        if (wbytes) {
-            wsampl = wbytes >> SHIFT;
-
-            samples -= wsampl;
-            pos = (pos + wsampl) % s->samples;
-
-            net += wsampl;
-        }
-        else {
-            break;
-        }
-    }
-
-    return net;
-}
-
-static void adlib_callback (void *opaque, int free)
-{
-    AdlibState *s = opaque;
-    int samples, net = 0, to_play, written;
-
-    samples = free >> SHIFT;
-    if (!(s->active && s->enabled) || !samples) {
-        return;
-    }
-
-    to_play = audio_MIN (s->left, samples);
-    while (to_play) {
-        written = write_audio (s, to_play);
-
-        if (written) {
-            s->left -= written;
-            samples -= written;
-            to_play -= written;
-            s->pos = (s->pos + written) % s->samples;
-        }
-        else {
-            return;
-        }
-    }
-
-    samples = audio_MIN (samples, s->samples - s->pos);
-    if (!samples) {
-        return;
-    }
-
-#ifdef HAS_YMF262
-    YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
-#else
-    YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
-#endif
-
-    while (samples) {
-        written = write_audio (s, samples);
-
-        if (written) {
-            net += written;
-            samples -= written;
-            s->pos = (s->pos + written) % s->samples;
-        }
-        else {
-            s->left = samples;
-            return;
-        }
-    }
-}
-
-static void Adlib_fini (AdlibState *s)
-{
-#ifdef HAS_YMF262
-    YMF262Shutdown ();
-#else
-    if (s->opl) {
-        OPLDestroy (s->opl);
-        s->opl = NULL;
-    }
-#endif
-
-    if (s->mixbuf) {
-        g_free (s->mixbuf);
-    }
-
-    s->active = 0;
-    s->enabled = 0;
-    AUD_remove_card (&s->card);
-}
-
-int Adlib_init (ISABus *bus)
-{
-    AdlibState *s = &glob_adlib;
-    struct audsettings as;
-
-#ifdef HAS_YMF262
-    if (YMF262Init (1, 14318180, conf.freq)) {
-        dolog ("YMF262Init %d failed\n", conf.freq);
-        return -1;
-    }
-    else {
-        YMF262SetTimerHandler (0, timer_handler, 0);
-        s->enabled = 1;
-    }
-#else
-    s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq);
-    if (!s->opl) {
-        dolog ("OPLCreate %d failed\n", conf.freq);
-        return -1;
-    }
-    else {
-        OPLSetTimerHandler (s->opl, timer_handler, 0);
-        s->enabled = 1;
-    }
-#endif
-
-    as.freq = conf.freq;
-    as.nchannels = SHIFT;
-    as.fmt = AUD_FMT_S16;
-    as.endianness = AUDIO_HOST_ENDIANNESS;
-
-    AUD_register_card ("adlib", &s->card);
-
-    s->voice = AUD_open_out (
-        &s->card,
-        s->voice,
-        "adlib",
-        s,
-        adlib_callback,
-        &as
-        );
-    if (!s->voice) {
-        Adlib_fini (s);
-        return -1;
-    }
-
-    s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT;
-    s->mixbuf = g_malloc0 (s->samples << SHIFT);
-
-    register_ioport_read (0x388, 4, 1, adlib_read, s);
-    register_ioport_write (0x388, 4, 1, adlib_write, s);
-
-    register_ioport_read (conf.port, 4, 1, adlib_read, s);
-    register_ioport_write (conf.port, 4, 1, adlib_write, s);
-
-    register_ioport_read (conf.port + 8, 2, 1, adlib_read, s);
-    register_ioport_write (conf.port + 8, 2, 1, adlib_write, s);
-
-    return 0;
-}
diff --git a/hw/ads7846.c b/hw/ads7846.c
deleted file mode 100644 (file)
index 5da3dc5..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * TI ADS7846 / TSC2046 chip emulation.
- *
- * Copyright (c) 2006 Openedhand Ltd.
- * Written by Andrzej Zaborowski <balrog@zabor.org>
- *
- * This code is licensed under the GNU GPL v2.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "hw/ssi.h"
-#include "ui/console.h"
-
-typedef struct {
-    SSISlave ssidev;
-    qemu_irq interrupt;
-
-    int input[8];
-    int pressure;
-    int noise;
-
-    int cycle;
-    int output;
-} ADS7846State;
-
-/* Control-byte bitfields */
-#define CB_PD0         (1 << 0)
-#define CB_PD1         (1 << 1)
-#define CB_SER         (1 << 2)
-#define CB_MODE                (1 << 3)
-#define CB_A0          (1 << 4)
-#define CB_A1          (1 << 5)
-#define CB_A2          (1 << 6)
-#define CB_START       (1 << 7)
-
-#define X_AXIS_DMAX    3470
-#define X_AXIS_MIN     290
-#define Y_AXIS_DMAX    3450
-#define Y_AXIS_MIN     200
-
-#define ADS_VBAT       2000
-#define ADS_VAUX       2000
-#define ADS_TEMP0      2000
-#define ADS_TEMP1      3000
-#define ADS_XPOS(x, y) (X_AXIS_MIN + ((X_AXIS_DMAX * (x)) >> 15))
-#define ADS_YPOS(x, y) (Y_AXIS_MIN + ((Y_AXIS_DMAX * (y)) >> 15))
-#define ADS_Z1POS(x, y)        600
-#define ADS_Z2POS(x, y)        (600 + 6000 / ADS_XPOS(x, y))
-
-static void ads7846_int_update(ADS7846State *s)
-{
-    if (s->interrupt)
-        qemu_set_irq(s->interrupt, s->pressure == 0);
-}
-
-static uint32_t ads7846_transfer(SSISlave *dev, uint32_t value)
-{
-    ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, dev);
-
-    switch (s->cycle ++) {
-    case 0:
-        if (!(value & CB_START)) {
-            s->cycle = 0;
-            break;
-        }
-
-        s->output = s->input[(value >> 4) & 7];
-
-        /* Imitate the ADC noise, some drivers expect this.  */
-        s->noise = (s->noise + 3) & 7;
-        switch ((value >> 4) & 7) {
-        case 1: s->output += s->noise ^ 2; break;
-        case 3: s->output += s->noise ^ 0; break;
-        case 4: s->output += s->noise ^ 7; break;
-        case 5: s->output += s->noise ^ 5; break;
-        }
-
-        if (value & CB_MODE)
-            s->output >>= 4;   /* 8 bits instead of 12 */
-
-        break;
-    case 1:
-        s->cycle = 0;
-        break;
-    }
-    return s->output;
-}
-
-static void ads7846_ts_event(void *opaque,
-                int x, int y, int z, int buttons_state)
-{
-    ADS7846State *s = opaque;
-
-    if (buttons_state) {
-        x = 0x7fff - x;
-        s->input[1] = ADS_XPOS(x, y);
-        s->input[3] = ADS_Z1POS(x, y);
-        s->input[4] = ADS_Z2POS(x, y);
-        s->input[5] = ADS_YPOS(x, y);
-    }
-
-    if (s->pressure == !buttons_state) {
-        s->pressure = !!buttons_state;
-
-        ads7846_int_update(s);
-    }
-}
-
-static int ads7856_post_load(void *opaque, int version_id)
-{
-    ADS7846State *s = opaque;
-
-    s->pressure = 0;
-    ads7846_int_update(s);
-    return 0;
-}
-
-static const VMStateDescription vmstate_ads7846 = {
-    .name = "ads7846",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .post_load = ads7856_post_load,
-    .fields      = (VMStateField[]) {
-        VMSTATE_SSI_SLAVE(ssidev, ADS7846State),
-        VMSTATE_INT32_ARRAY(input, ADS7846State, 8),
-        VMSTATE_INT32(noise, ADS7846State),
-        VMSTATE_INT32(cycle, ADS7846State),
-        VMSTATE_INT32(output, ADS7846State),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int ads7846_init(SSISlave *dev)
-{
-    ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, dev);
-
-    qdev_init_gpio_out(&dev->qdev, &s->interrupt, 1);
-
-    s->input[0] = ADS_TEMP0;   /* TEMP0 */
-    s->input[2] = ADS_VBAT;    /* VBAT */
-    s->input[6] = ADS_VAUX;    /* VAUX */
-    s->input[7] = ADS_TEMP1;   /* TEMP1 */
-
-    /* We want absolute coordinates */
-    qemu_add_mouse_event_handler(ads7846_ts_event, s, 1,
-                    "QEMU ADS7846-driven Touchscreen");
-
-    ads7846_int_update(s);
-
-    vmstate_register(NULL, -1, &vmstate_ads7846, s);
-    return 0;
-}
-
-static void ads7846_class_init(ObjectClass *klass, void *data)
-{
-    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
-
-    k->init = ads7846_init;
-    k->transfer = ads7846_transfer;
-}
-
-static const TypeInfo ads7846_info = {
-    .name          = "ads7846",
-    .parent        = TYPE_SSI_SLAVE,
-    .instance_size = sizeof(ADS7846State),
-    .class_init    = ads7846_class_init,
-};
-
-static void ads7846_register_types(void)
-{
-    type_register_static(&ads7846_info);
-}
-
-type_init(ads7846_register_types)
diff --git a/hw/apm.c b/hw/apm.c
deleted file mode 100644 (file)
index 5f21d21..0000000
--- a/hw/apm.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * QEMU PC APM controller Emulation
- * This is split out from acpi.c
- *
- * Copyright (c) 2006 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "hw/isa/apm.h"
-#include "hw/hw.h"
-#include "hw/pci/pci.h"
-
-//#define DEBUG
-
-#ifdef DEBUG
-# define APM_DPRINTF(format, ...)       printf(format, ## __VA_ARGS__)
-#else
-# define APM_DPRINTF(format, ...)       do { } while (0)
-#endif
-
-/* fixed I/O location */
-#define APM_CNT_IOPORT  0xb2
-#define APM_STS_IOPORT  0xb3
-
-static void apm_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
-                              unsigned size)
-{
-    APMState *apm = opaque;
-    addr &= 1;
-    APM_DPRINTF("apm_ioport_writeb addr=0x%x val=0x%02x\n", addr, val);
-    if (addr == 0) {
-        apm->apmc = val;
-
-        if (apm->callback) {
-            (apm->callback)(val, apm->arg);
-        }
-    } else {
-        apm->apms = val;
-    }
-}
-
-static uint64_t apm_ioport_readb(void *opaque, hwaddr addr, unsigned size)
-{
-    APMState *apm = opaque;
-    uint32_t val;
-
-    addr &= 1;
-    if (addr == 0) {
-        val = apm->apmc;
-    } else {
-        val = apm->apms;
-    }
-    APM_DPRINTF("apm_ioport_readb addr=0x%x val=0x%02x\n", addr, val);
-    return val;
-}
-
-const VMStateDescription vmstate_apm = {
-    .name = "APM State",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT8(apmc, APMState),
-        VMSTATE_UINT8(apms, APMState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const MemoryRegionOps apm_ops = {
-    .read = apm_ioport_readb,
-    .write = apm_ioport_writeb,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-void apm_init(PCIDevice *dev, APMState *apm, apm_ctrl_changed_t callback,
-              void *arg)
-{
-    apm->callback = callback;
-    apm->arg = arg;
-
-    /* ioport 0xb2, 0xb3 */
-    memory_region_init_io(&apm->io, &apm_ops, apm, "apm-io", 2);
-    memory_region_add_subregion(pci_address_space_io(dev), APM_CNT_IOPORT,
-                                &apm->io);
-}
diff --git a/hw/applesmc.c b/hw/applesmc.c
deleted file mode 100644 (file)
index c29558b..0000000
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- *  Apple SMC controller
- *
- *  Copyright (c) 2007 Alexander Graf
- *
- *  Authors: Alexander Graf <agraf@suse.de>
- *           Susanne Graf <suse@csgraf.de>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- * *****************************************************************
- *
- * In all Intel-based Apple hardware there is an SMC chip to control the
- * backlight, fans and several other generic device parameters. It also
- * contains the magic keys used to dongle Mac OS X to the device.
- *
- * This driver was mostly created by looking at the Linux AppleSMC driver
- * implementation and does not support IRQ.
- *
- */
-
-#include "hw/hw.h"
-#include "hw/isa/isa.h"
-#include "ui/console.h"
-#include "qemu/timer.h"
-
-/* #define DEBUG_SMC */
-
-#define APPLESMC_DEFAULT_IOBASE        0x300
-/* data port used by Apple SMC */
-#define APPLESMC_DATA_PORT             0x0
-/* command/status port used by Apple SMC */
-#define APPLESMC_CMD_PORT              0x4
-#define APPLESMC_NR_PORTS              32
-#define APPLESMC_MAX_DATA_LENGTH       32
-
-#define APPLESMC_READ_CMD              0x10
-#define APPLESMC_WRITE_CMD             0x11
-#define APPLESMC_GET_KEY_BY_INDEX_CMD  0x12
-#define APPLESMC_GET_KEY_TYPE_CMD      0x13
-
-#ifdef DEBUG_SMC
-#define smc_debug(...) fprintf(stderr, "AppleSMC: " __VA_ARGS__)
-#else
-#define smc_debug(...) do { } while(0)
-#endif
-
-static char default_osk[64] = "This is a dummy key. Enter the real key "
-                              "using the -osk parameter";
-
-struct AppleSMCData {
-    uint8_t len;
-    const char *key;
-    const char *data;
-    QLIST_ENTRY(AppleSMCData) node;
-};
-
-struct AppleSMCStatus {
-    ISADevice dev;
-    uint32_t iobase;
-    uint8_t cmd;
-    uint8_t status;
-    uint8_t key[4];
-    uint8_t read_pos;
-    uint8_t data_len;
-    uint8_t data_pos;
-    uint8_t data[255];
-    uint8_t charactic[4];
-    char *osk;
-    QLIST_HEAD(, AppleSMCData) data_def;
-};
-
-static void applesmc_io_cmd_writeb(void *opaque, uint32_t addr, uint32_t val)
-{
-    struct AppleSMCStatus *s = opaque;
-
-    smc_debug("CMD Write B: %#x = %#x\n", addr, val);
-    switch(val) {
-        case APPLESMC_READ_CMD:
-            s->status = 0x0c;
-            break;
-    }
-    s->cmd = val;
-    s->read_pos = 0;
-    s->data_pos = 0;
-}
-
-static void applesmc_fill_data(struct AppleSMCStatus *s)
-{
-    struct AppleSMCData *d;
-
-    QLIST_FOREACH(d, &s->data_def, node) {
-        if (!memcmp(d->key, s->key, 4)) {
-            smc_debug("Key matched (%s Len=%d Data=%s)\n", d->key,
-                      d->len, d->data);
-            memcpy(s->data, d->data, d->len);
-            return;
-        }
-    }
-}
-
-static void applesmc_io_data_writeb(void *opaque, uint32_t addr, uint32_t val)
-{
-    struct AppleSMCStatus *s = opaque;
-
-    smc_debug("DATA Write B: %#x = %#x\n", addr, val);
-    switch(s->cmd) {
-        case APPLESMC_READ_CMD:
-            if(s->read_pos < 4) {
-                s->key[s->read_pos] = val;
-                s->status = 0x04;
-            } else if(s->read_pos == 4) {
-                s->data_len = val;
-                s->status = 0x05;
-                s->data_pos = 0;
-                smc_debug("Key = %c%c%c%c Len = %d\n", s->key[0],
-                          s->key[1], s->key[2], s->key[3], val);
-                applesmc_fill_data(s);
-            }
-            s->read_pos++;
-            break;
-    }
-}
-
-static uint32_t applesmc_io_data_readb(void *opaque, uint32_t addr1)
-{
-    struct AppleSMCStatus *s = opaque;
-    uint8_t retval = 0;
-
-    switch(s->cmd) {
-        case APPLESMC_READ_CMD:
-            if(s->data_pos < s->data_len) {
-                retval = s->data[s->data_pos];
-                smc_debug("READ_DATA[%d] = %#hhx\n", s->data_pos,
-                          retval);
-                s->data_pos++;
-                if(s->data_pos == s->data_len) {
-                    s->status = 0x00;
-                    smc_debug("EOF\n");
-                } else
-                    s->status = 0x05;
-            }
-    }
-    smc_debug("DATA Read b: %#x = %#x\n", addr1, retval);
-
-    return retval;
-}
-
-static uint32_t applesmc_io_cmd_readb(void *opaque, uint32_t addr1)
-{
-    struct AppleSMCStatus *s = opaque;
-
-    smc_debug("CMD Read B: %#x\n", addr1);
-    return s->status;
-}
-
-static void applesmc_add_key(struct AppleSMCStatus *s, const char *key,
-                             int len, const char *data)
-{
-    struct AppleSMCData *def;
-
-    def = g_malloc0(sizeof(struct AppleSMCData));
-    def->key = key;
-    def->len = len;
-    def->data = data;
-
-    QLIST_INSERT_HEAD(&s->data_def, def, node);
-}
-
-static void qdev_applesmc_isa_reset(DeviceState *dev)
-{
-    struct AppleSMCStatus *s = DO_UPCAST(struct AppleSMCStatus, dev.qdev, dev);
-    struct AppleSMCData *d, *next;
-
-    /* Remove existing entries */
-    QLIST_FOREACH_SAFE(d, &s->data_def, node, next) {
-        QLIST_REMOVE(d, node);
-    }
-
-    applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03");
-    applesmc_add_key(s, "OSK0", 32, s->osk);
-    applesmc_add_key(s, "OSK1", 32, s->osk + 32);
-    applesmc_add_key(s, "NATJ", 1, "\0");
-    applesmc_add_key(s, "MSSP", 1, "\0");
-    applesmc_add_key(s, "MSSD", 1, "\0x3");
-}
-
-static int applesmc_isa_init(ISADevice *dev)
-{
-    struct AppleSMCStatus *s = DO_UPCAST(struct AppleSMCStatus, dev, dev);
-
-    register_ioport_read(s->iobase + APPLESMC_DATA_PORT, 4, 1,
-                         applesmc_io_data_readb, s);
-    register_ioport_read(s->iobase + APPLESMC_CMD_PORT, 4, 1,
-                         applesmc_io_cmd_readb, s);
-    register_ioport_write(s->iobase + APPLESMC_DATA_PORT, 4, 1,
-                          applesmc_io_data_writeb, s);
-    register_ioport_write(s->iobase + APPLESMC_CMD_PORT, 4, 1,
-                          applesmc_io_cmd_writeb, s);
-
-    if (!s->osk || (strlen(s->osk) != 64)) {
-        fprintf(stderr, "WARNING: Using AppleSMC with invalid key\n");
-        s->osk = default_osk;
-    }
-
-    QLIST_INIT(&s->data_def);
-    qdev_applesmc_isa_reset(&dev->qdev);
-
-    return 0;
-}
-
-static Property applesmc_isa_properties[] = {
-    DEFINE_PROP_HEX32("iobase", struct AppleSMCStatus, iobase,
-                      APPLESMC_DEFAULT_IOBASE),
-    DEFINE_PROP_STRING("osk", struct AppleSMCStatus, osk),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void qdev_applesmc_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
-    ic->init = applesmc_isa_init;
-    dc->reset = qdev_applesmc_isa_reset;
-    dc->props = applesmc_isa_properties;
-}
-
-static const TypeInfo applesmc_isa_info = {
-    .name          = "isa-applesmc",
-    .parent        = TYPE_ISA_DEVICE,
-    .instance_size = sizeof(struct AppleSMCStatus),
-    .class_init    = qdev_applesmc_class_init,
-};
-
-static void applesmc_register_types(void)
-{
-    type_register_static(&applesmc_isa_info);
-}
-
-type_init(applesmc_register_types)
diff --git a/hw/arm_l2x0.c b/hw/arm_l2x0.c
deleted file mode 100644 (file)
index eb4427d..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * ARM dummy L210, L220, PL310 cache controller.
- *
- * Copyright (c) 2010-2012 Calxeda
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2 or any later version, as published by the Free Software
- * Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "hw/sysbus.h"
-
-/* L2C-310 r3p2 */
-#define CACHE_ID 0x410000c8
-
-typedef struct l2x0_state {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    uint32_t cache_type;
-    uint32_t ctrl;
-    uint32_t aux_ctrl;
-    uint32_t data_ctrl;
-    uint32_t tag_ctrl;
-    uint32_t filter_start;
-    uint32_t filter_end;
-} l2x0_state;
-
-static const VMStateDescription vmstate_l2x0 = {
-    .name = "l2x0",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32(ctrl, l2x0_state),
-        VMSTATE_UINT32(aux_ctrl, l2x0_state),
-        VMSTATE_UINT32(data_ctrl, l2x0_state),
-        VMSTATE_UINT32(tag_ctrl, l2x0_state),
-        VMSTATE_UINT32(filter_start, l2x0_state),
-        VMSTATE_UINT32(filter_end, l2x0_state),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-
-static uint64_t l2x0_priv_read(void *opaque, hwaddr offset,
-                               unsigned size)
-{
-    uint32_t cache_data;
-    l2x0_state *s = (l2x0_state *)opaque;
-    offset &= 0xfff;
-    if (offset >= 0x730 && offset < 0x800) {
-        return 0; /* cache ops complete */
-    }
-    switch (offset) {
-    case 0:
-        return CACHE_ID;
-    case 0x4:
-        /* aux_ctrl values affect cache_type values */
-        cache_data = (s->aux_ctrl & (7 << 17)) >> 15;
-        cache_data |= (s->aux_ctrl & (1 << 16)) >> 16;
-        return s->cache_type |= (cache_data << 18) | (cache_data << 6);
-    case 0x100:
-        return s->ctrl;
-    case 0x104:
-        return s->aux_ctrl;
-    case 0x108:
-        return s->tag_ctrl;
-    case 0x10C:
-        return s->data_ctrl;
-    case 0xC00:
-        return s->filter_start;
-    case 0xC04:
-        return s->filter_end;
-    case 0xF40:
-        return 0;
-    case 0xF60:
-        return 0;
-    case 0xF80:
-        return 0;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "l2x0_priv_read: Bad offset %x\n", (int)offset);
-        break;
-    }
-    return 0;
-}
-
-static void l2x0_priv_write(void *opaque, hwaddr offset,
-                            uint64_t value, unsigned size)
-{
-    l2x0_state *s = (l2x0_state *)opaque;
-    offset &= 0xfff;
-    if (offset >= 0x730 && offset < 0x800) {
-        /* ignore */
-        return;
-    }
-    switch (offset) {
-    case 0x100:
-        s->ctrl = value & 1;
-        break;
-    case 0x104:
-        s->aux_ctrl = value;
-        break;
-    case 0x108:
-        s->tag_ctrl = value;
-        break;
-    case 0x10C:
-        s->data_ctrl = value;
-        break;
-    case 0xC00:
-        s->filter_start = value;
-        break;
-    case 0xC04:
-        s->filter_end = value;
-        break;
-    case 0xF40:
-        return;
-    case 0xF60:
-        return;
-    case 0xF80:
-        return;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "l2x0_priv_write: Bad offset %x\n", (int)offset);
-        break;
-    }
-}
-
-static void l2x0_priv_reset(DeviceState *dev)
-{
-    l2x0_state *s = DO_UPCAST(l2x0_state, busdev.qdev, dev);
-
-    s->ctrl = 0;
-    s->aux_ctrl = 0x02020000;
-    s->tag_ctrl = 0;
-    s->data_ctrl = 0;
-    s->filter_start = 0;
-    s->filter_end = 0;
-}
-
-static const MemoryRegionOps l2x0_mem_ops = {
-    .read = l2x0_priv_read,
-    .write = l2x0_priv_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
- };
-
-static int l2x0_priv_init(SysBusDevice *dev)
-{
-    l2x0_state *s = FROM_SYSBUS(l2x0_state, dev);
-
-    memory_region_init_io(&s->iomem, &l2x0_mem_ops, s, "l2x0_cc", 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-    return 0;
-}
-
-static Property l2x0_properties[] = {
-    DEFINE_PROP_UINT32("cache-type", l2x0_state, cache_type, 0x1c100100),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void l2x0_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    k->init = l2x0_priv_init;
-    dc->vmsd = &vmstate_l2x0;
-    dc->no_user = 1;
-    dc->props = l2x0_properties;
-    dc->reset = l2x0_priv_reset;
-}
-
-static const TypeInfo l2x0_info = {
-    .name = "l2x0",
-    .parent = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(l2x0_state),
-    .class_init = l2x0_class_init,
-};
-
-static void l2x0_register_types(void)
-{
-    type_register_static(&l2x0_info);
-}
-
-type_init(l2x0_register_types)
diff --git a/hw/arm_timer.c b/hw/arm_timer.c
deleted file mode 100644 (file)
index 6449870..0000000
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- * ARM PrimeCell Timer modules.
- *
- * Copyright (c) 2005-2006 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the GPL.
- */
-
-#include "hw/sysbus.h"
-#include "qemu/timer.h"
-#include "qemu-common.h"
-#include "hw/qdev.h"
-#include "hw/ptimer.h"
-
-/* Common timer implementation.  */
-
-#define TIMER_CTRL_ONESHOT      (1 << 0)
-#define TIMER_CTRL_32BIT        (1 << 1)
-#define TIMER_CTRL_DIV1         (0 << 2)
-#define TIMER_CTRL_DIV16        (1 << 2)
-#define TIMER_CTRL_DIV256       (2 << 2)
-#define TIMER_CTRL_IE           (1 << 5)
-#define TIMER_CTRL_PERIODIC     (1 << 6)
-#define TIMER_CTRL_ENABLE       (1 << 7)
-
-typedef struct {
-    ptimer_state *timer;
-    uint32_t control;
-    uint32_t limit;
-    int freq;
-    int int_level;
-    qemu_irq irq;
-} arm_timer_state;
-
-/* Check all active timers, and schedule the next timer interrupt.  */
-
-static void arm_timer_update(arm_timer_state *s)
-{
-    /* Update interrupts.  */
-    if (s->int_level && (s->control & TIMER_CTRL_IE)) {
-        qemu_irq_raise(s->irq);
-    } else {
-        qemu_irq_lower(s->irq);
-    }
-}
-
-static uint32_t arm_timer_read(void *opaque, hwaddr offset)
-{
-    arm_timer_state *s = (arm_timer_state *)opaque;
-
-    switch (offset >> 2) {
-    case 0: /* TimerLoad */
-    case 6: /* TimerBGLoad */
-        return s->limit;
-    case 1: /* TimerValue */
-        return ptimer_get_count(s->timer);
-    case 2: /* TimerControl */
-        return s->control;
-    case 4: /* TimerRIS */
-        return s->int_level;
-    case 5: /* TimerMIS */
-        if ((s->control & TIMER_CTRL_IE) == 0)
-            return 0;
-        return s->int_level;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: Bad offset %x\n", __func__, (int)offset);
-        return 0;
-    }
-}
-
-/* Reset the timer limit after settings have changed.  */
-static void arm_timer_recalibrate(arm_timer_state *s, int reload)
-{
-    uint32_t limit;
-
-    if ((s->control & (TIMER_CTRL_PERIODIC | TIMER_CTRL_ONESHOT)) == 0) {
-        /* Free running.  */
-        if (s->control & TIMER_CTRL_32BIT)
-            limit = 0xffffffff;
-        else
-            limit = 0xffff;
-    } else {
-          /* Periodic.  */
-          limit = s->limit;
-    }
-    ptimer_set_limit(s->timer, limit, reload);
-}
-
-static void arm_timer_write(void *opaque, hwaddr offset,
-                            uint32_t value)
-{
-    arm_timer_state *s = (arm_timer_state *)opaque;
-    int freq;
-
-    switch (offset >> 2) {
-    case 0: /* TimerLoad */
-        s->limit = value;
-        arm_timer_recalibrate(s, 1);
-        break;
-    case 1: /* TimerValue */
-        /* ??? Linux seems to want to write to this readonly register.
-           Ignore it.  */
-        break;
-    case 2: /* TimerControl */
-        if (s->control & TIMER_CTRL_ENABLE) {
-            /* Pause the timer if it is running.  This may cause some
-               inaccuracy dure to rounding, but avoids a whole lot of other
-               messyness.  */
-            ptimer_stop(s->timer);
-        }
-        s->control = value;
-        freq = s->freq;
-        /* ??? Need to recalculate expiry time after changing divisor.  */
-        switch ((value >> 2) & 3) {
-        case 1: freq >>= 4; break;
-        case 2: freq >>= 8; break;
-        }
-        arm_timer_recalibrate(s, s->control & TIMER_CTRL_ENABLE);
-        ptimer_set_freq(s->timer, freq);
-        if (s->control & TIMER_CTRL_ENABLE) {
-            /* Restart the timer if still enabled.  */
-            ptimer_run(s->timer, (s->control & TIMER_CTRL_ONESHOT) != 0);
-        }
-        break;
-    case 3: /* TimerIntClr */
-        s->int_level = 0;
-        break;
-    case 6: /* TimerBGLoad */
-        s->limit = value;
-        arm_timer_recalibrate(s, 0);
-        break;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: Bad offset %x\n", __func__, (int)offset);
-    }
-    arm_timer_update(s);
-}
-
-static void arm_timer_tick(void *opaque)
-{
-    arm_timer_state *s = (arm_timer_state *)opaque;
-    s->int_level = 1;
-    arm_timer_update(s);
-}
-
-static const VMStateDescription vmstate_arm_timer = {
-    .name = "arm_timer",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT32(control, arm_timer_state),
-        VMSTATE_UINT32(limit, arm_timer_state),
-        VMSTATE_INT32(int_level, arm_timer_state),
-        VMSTATE_PTIMER(timer, arm_timer_state),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static arm_timer_state *arm_timer_init(uint32_t freq)
-{
-    arm_timer_state *s;
-    QEMUBH *bh;
-
-    s = (arm_timer_state *)g_malloc0(sizeof(arm_timer_state));
-    s->freq = freq;
-    s->control = TIMER_CTRL_IE;
-
-    bh = qemu_bh_new(arm_timer_tick, s);
-    s->timer = ptimer_init(bh);
-    vmstate_register(NULL, -1, &vmstate_arm_timer, s);
-    return s;
-}
-
-/* ARM PrimeCell SP804 dual timer module.
- * Docs at
- * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0271d/index.html
-*/
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    arm_timer_state *timer[2];
-    uint32_t freq0, freq1;
-    int level[2];
-    qemu_irq irq;
-} sp804_state;
-
-static const uint8_t sp804_ids[] = {
-    /* Timer ID */
-    0x04, 0x18, 0x14, 0,
-    /* PrimeCell ID */
-    0xd, 0xf0, 0x05, 0xb1
-};
-
-/* Merge the IRQs from the two component devices.  */
-static void sp804_set_irq(void *opaque, int irq, int level)
-{
-    sp804_state *s = (sp804_state *)opaque;
-
-    s->level[irq] = level;
-    qemu_set_irq(s->irq, s->level[0] || s->level[1]);
-}
-
-static uint64_t sp804_read(void *opaque, hwaddr offset,
-                           unsigned size)
-{
-    sp804_state *s = (sp804_state *)opaque;
-
-    if (offset < 0x20) {
-        return arm_timer_read(s->timer[0], offset);
-    }
-    if (offset < 0x40) {
-        return arm_timer_read(s->timer[1], offset - 0x20);
-    }
-
-    /* TimerPeriphID */
-    if (offset >= 0xfe0 && offset <= 0xffc) {
-        return sp804_ids[(offset - 0xfe0) >> 2];
-    }
-
-    switch (offset) {
-    /* Integration Test control registers, which we won't support */
-    case 0xf00: /* TimerITCR */
-    case 0xf04: /* TimerITOP (strictly write only but..) */
-        qemu_log_mask(LOG_UNIMP,
-                      "%s: integration test registers unimplemented\n",
-                      __func__);
-        return 0;
-    }
-
-    qemu_log_mask(LOG_GUEST_ERROR,
-                  "%s: Bad offset %x\n", __func__, (int)offset);
-    return 0;
-}
-
-static void sp804_write(void *opaque, hwaddr offset,
-                        uint64_t value, unsigned size)
-{
-    sp804_state *s = (sp804_state *)opaque;
-
-    if (offset < 0x20) {
-        arm_timer_write(s->timer[0], offset, value);
-        return;
-    }
-
-    if (offset < 0x40) {
-        arm_timer_write(s->timer[1], offset - 0x20, value);
-        return;
-    }
-
-    /* Technically we could be writing to the Test Registers, but not likely */
-    qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %x\n",
-                  __func__, (int)offset);
-}
-
-static const MemoryRegionOps sp804_ops = {
-    .read = sp804_read,
-    .write = sp804_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static const VMStateDescription vmstate_sp804 = {
-    .name = "sp804",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_INT32_ARRAY(level, sp804_state, 2),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int sp804_init(SysBusDevice *dev)
-{
-    sp804_state *s = FROM_SYSBUS(sp804_state, dev);
-    qemu_irq *qi;
-
-    qi = qemu_allocate_irqs(sp804_set_irq, s, 2);
-    sysbus_init_irq(dev, &s->irq);
-    s->timer[0] = arm_timer_init(s->freq0);
-    s->timer[1] = arm_timer_init(s->freq1);
-    s->timer[0]->irq = qi[0];
-    s->timer[1]->irq = qi[1];
-    memory_region_init_io(&s->iomem, &sp804_ops, s, "sp804", 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-    vmstate_register(&dev->qdev, -1, &vmstate_sp804, s);
-    return 0;
-}
-
-/* Integrator/CP timer module.  */
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    arm_timer_state *timer[3];
-} icp_pit_state;
-
-static uint64_t icp_pit_read(void *opaque, hwaddr offset,
-                             unsigned size)
-{
-    icp_pit_state *s = (icp_pit_state *)opaque;
-    int n;
-
-    /* ??? Don't know the PrimeCell ID for this device.  */
-    n = offset >> 8;
-    if (n > 2) {
-        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad timer %d\n", __func__, n);
-    }
-
-    return arm_timer_read(s->timer[n], offset & 0xff);
-}
-
-static void icp_pit_write(void *opaque, hwaddr offset,
-                          uint64_t value, unsigned size)
-{
-    icp_pit_state *s = (icp_pit_state *)opaque;
-    int n;
-
-    n = offset >> 8;
-    if (n > 2) {
-        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad timer %d\n", __func__, n);
-    }
-
-    arm_timer_write(s->timer[n], offset & 0xff, value);
-}
-
-static const MemoryRegionOps icp_pit_ops = {
-    .read = icp_pit_read,
-    .write = icp_pit_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int icp_pit_init(SysBusDevice *dev)
-{
-    icp_pit_state *s = FROM_SYSBUS(icp_pit_state, dev);
-
-    /* Timer 0 runs at the system clock speed (40MHz).  */
-    s->timer[0] = arm_timer_init(40000000);
-    /* The other two timers run at 1MHz.  */
-    s->timer[1] = arm_timer_init(1000000);
-    s->timer[2] = arm_timer_init(1000000);
-
-    sysbus_init_irq(dev, &s->timer[0]->irq);
-    sysbus_init_irq(dev, &s->timer[1]->irq);
-    sysbus_init_irq(dev, &s->timer[2]->irq);
-
-    memory_region_init_io(&s->iomem, &icp_pit_ops, s, "icp_pit", 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-    /* This device has no state to save/restore.  The component timers will
-       save themselves.  */
-    return 0;
-}
-
-static void icp_pit_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sdc->init = icp_pit_init;
-}
-
-static const TypeInfo icp_pit_info = {
-    .name          = "integrator_pit",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(icp_pit_state),
-    .class_init    = icp_pit_class_init,
-};
-
-static Property sp804_properties[] = {
-    DEFINE_PROP_UINT32("freq0", sp804_state, freq0, 1000000),
-    DEFINE_PROP_UINT32("freq1", sp804_state, freq1, 1000000),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void sp804_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-    DeviceClass *k = DEVICE_CLASS(klass);
-
-    sdc->init = sp804_init;
-    k->props = sp804_properties;
-}
-
-static const TypeInfo sp804_info = {
-    .name          = "sp804",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(sp804_state),
-    .class_init    = sp804_class_init,
-};
-
-static void arm_timer_register_types(void)
-{
-    type_register_static(&icp_pit_info);
-    type_register_static(&sp804_info);
-}
-
-type_init(arm_timer_register_types)
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c50c367da7298d58a27f2b9880b4f57ad4e1341e 100644 (file)
@@ -0,0 +1,16 @@
+# Sound
+sound-obj-y =
+sound-obj-$(CONFIG_SB16) += sb16.o
+sound-obj-$(CONFIG_ES1370) += es1370.o
+sound-obj-$(CONFIG_AC97) += ac97.o
+sound-obj-$(CONFIG_ADLIB) += fmopl.o adlib.o
+sound-obj-$(CONFIG_GUS) += gus.o gusemu_hal.o gusemu_mixer.o
+sound-obj-$(CONFIG_CS4231A) += cs4231a.o
+sound-obj-$(CONFIG_HDA) += intel-hda.o hda-codec.o
+
+common-obj-$(CONFIG_SOUND) += $(sound-obj-y)
+common-obj-$(CONFIG_PCSPK) += pcspk.o
+common-obj-$(CONFIG_WM8750) += wm8750.o
+common-obj-$(CONFIG_PL041) += pl041.o lm4549.o
+
+$(obj)/adlib.o $(obj)/fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0
diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c
new file mode 100644 (file)
index 0000000..ab68ec6
--- /dev/null
@@ -0,0 +1,1438 @@
+/*
+ * Copyright (C) 2006 InnoTek Systemberatung GmbH
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software Foundation,
+ * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
+ * distribution. VirtualBox OSE is distributed in the hope that it will
+ * be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * If you received this file as part of a commercial VirtualBox
+ * distribution, then only the terms of your commercial VirtualBox
+ * license agreement apply instead of the previous paragraph.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "hw/hw.h"
+#include "hw/audio/audio.h"
+#include "audio/audio.h"
+#include "hw/pci/pci.h"
+#include "sysemu/dma.h"
+
+enum {
+    AC97_Reset                     = 0x00,
+    AC97_Master_Volume_Mute        = 0x02,
+    AC97_Headphone_Volume_Mute     = 0x04,
+    AC97_Master_Volume_Mono_Mute   = 0x06,
+    AC97_Master_Tone_RL            = 0x08,
+    AC97_PC_BEEP_Volume_Mute       = 0x0A,
+    AC97_Phone_Volume_Mute         = 0x0C,
+    AC97_Mic_Volume_Mute           = 0x0E,
+    AC97_Line_In_Volume_Mute       = 0x10,
+    AC97_CD_Volume_Mute            = 0x12,
+    AC97_Video_Volume_Mute         = 0x14,
+    AC97_Aux_Volume_Mute           = 0x16,
+    AC97_PCM_Out_Volume_Mute       = 0x18,
+    AC97_Record_Select             = 0x1A,
+    AC97_Record_Gain_Mute          = 0x1C,
+    AC97_Record_Gain_Mic_Mute      = 0x1E,
+    AC97_General_Purpose           = 0x20,
+    AC97_3D_Control                = 0x22,
+    AC97_AC_97_RESERVED            = 0x24,
+    AC97_Powerdown_Ctrl_Stat       = 0x26,
+    AC97_Extended_Audio_ID         = 0x28,
+    AC97_Extended_Audio_Ctrl_Stat  = 0x2A,
+    AC97_PCM_Front_DAC_Rate        = 0x2C,
+    AC97_PCM_Surround_DAC_Rate     = 0x2E,
+    AC97_PCM_LFE_DAC_Rate          = 0x30,
+    AC97_PCM_LR_ADC_Rate           = 0x32,
+    AC97_MIC_ADC_Rate              = 0x34,
+    AC97_6Ch_Vol_C_LFE_Mute        = 0x36,
+    AC97_6Ch_Vol_L_R_Surround_Mute = 0x38,
+    AC97_Vendor_Reserved           = 0x58,
+    AC97_Sigmatel_Analog           = 0x6c, /* We emulate a Sigmatel codec */
+    AC97_Sigmatel_Dac2Invert       = 0x6e, /* We emulate a Sigmatel codec */
+    AC97_Vendor_ID1                = 0x7c,
+    AC97_Vendor_ID2                = 0x7e
+};
+
+#define SOFT_VOLUME
+#define SR_FIFOE 16             /* rwc */
+#define SR_BCIS  8              /* rwc */
+#define SR_LVBCI 4              /* rwc */
+#define SR_CELV  2              /* ro */
+#define SR_DCH   1              /* ro */
+#define SR_VALID_MASK ((1 << 5) - 1)
+#define SR_WCLEAR_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI)
+#define SR_RO_MASK (SR_DCH | SR_CELV)
+#define SR_INT_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI)
+
+#define CR_IOCE  16             /* rw */
+#define CR_FEIE  8              /* rw */
+#define CR_LVBIE 4              /* rw */
+#define CR_RR    2              /* rw */
+#define CR_RPBM  1              /* rw */
+#define CR_VALID_MASK ((1 << 5) - 1)
+#define CR_DONT_CLEAR_MASK (CR_IOCE | CR_FEIE | CR_LVBIE)
+
+#define GC_WR    4              /* rw */
+#define GC_CR    2              /* rw */
+#define GC_VALID_MASK ((1 << 6) - 1)
+
+#define GS_MD3   (1<<17)        /* rw */
+#define GS_AD3   (1<<16)        /* rw */
+#define GS_RCS   (1<<15)        /* rwc */
+#define GS_B3S12 (1<<14)        /* ro */
+#define GS_B2S12 (1<<13)        /* ro */
+#define GS_B1S12 (1<<12)        /* ro */
+#define GS_S1R1  (1<<11)        /* rwc */
+#define GS_S0R1  (1<<10)        /* rwc */
+#define GS_S1CR  (1<<9)         /* ro */
+#define GS_S0CR  (1<<8)         /* ro */
+#define GS_MINT  (1<<7)         /* ro */
+#define GS_POINT (1<<6)         /* ro */
+#define GS_PIINT (1<<5)         /* ro */
+#define GS_RSRVD ((1<<4)|(1<<3))
+#define GS_MOINT (1<<2)         /* ro */
+#define GS_MIINT (1<<1)         /* ro */
+#define GS_GSCI  1              /* rwc */
+#define GS_RO_MASK (GS_B3S12|                   \
+                    GS_B2S12|                   \
+                    GS_B1S12|                   \
+                    GS_S1CR|                    \
+                    GS_S0CR|                    \
+                    GS_MINT|                    \
+                    GS_POINT|                   \
+                    GS_PIINT|                   \
+                    GS_RSRVD|                   \
+                    GS_MOINT|                   \
+                    GS_MIINT)
+#define GS_VALID_MASK ((1 << 18) - 1)
+#define GS_WCLEAR_MASK (GS_RCS|GS_S1R1|GS_S0R1|GS_GSCI)
+
+#define BD_IOC (1<<31)
+#define BD_BUP (1<<30)
+
+#define EACS_VRA 1
+#define EACS_VRM 8
+
+#define MUTE_SHIFT 15
+
+#define REC_MASK 7
+enum {
+    REC_MIC = 0,
+    REC_CD,
+    REC_VIDEO,
+    REC_AUX,
+    REC_LINE_IN,
+    REC_STEREO_MIX,
+    REC_MONO_MIX,
+    REC_PHONE
+};
+
+typedef struct BD {
+    uint32_t addr;
+    uint32_t ctl_len;
+} BD;
+
+typedef struct AC97BusMasterRegs {
+    uint32_t bdbar;             /* rw 0 */
+    uint8_t civ;                /* ro 0 */
+    uint8_t lvi;                /* rw 0 */
+    uint16_t sr;                /* rw 1 */
+    uint16_t picb;              /* ro 0 */
+    uint8_t piv;                /* ro 0 */
+    uint8_t cr;                 /* rw 0 */
+    unsigned int bd_valid;
+    BD bd;
+} AC97BusMasterRegs;
+
+typedef struct AC97LinkState {
+    PCIDevice dev;
+    QEMUSoundCard card;
+    uint32_t use_broken_id;
+    uint32_t glob_cnt;
+    uint32_t glob_sta;
+    uint32_t cas;
+    uint32_t last_samp;
+    AC97BusMasterRegs bm_regs[3];
+    uint8_t mixer_data[256];
+    SWVoiceIn *voice_pi;
+    SWVoiceOut *voice_po;
+    SWVoiceIn *voice_mc;
+    int invalid_freq[3];
+    uint8_t silence[128];
+    int bup_flag;
+    MemoryRegion io_nam;
+    MemoryRegion io_nabm;
+} AC97LinkState;
+
+enum {
+    BUP_SET = 1,
+    BUP_LAST = 2
+};
+
+#ifdef DEBUG_AC97
+#define dolog(...) AUD_log ("ac97", __VA_ARGS__)
+#else
+#define dolog(...)
+#endif
+
+#define MKREGS(prefix, start)                   \
+enum {                                          \
+    prefix ## _BDBAR = start,                   \
+    prefix ## _CIV = start + 4,                 \
+    prefix ## _LVI = start + 5,                 \
+    prefix ## _SR = start + 6,                  \
+    prefix ## _PICB = start + 8,                \
+    prefix ## _PIV = start + 10,                \
+    prefix ## _CR = start + 11                  \
+}
+
+enum {
+    PI_INDEX = 0,
+    PO_INDEX,
+    MC_INDEX,
+    LAST_INDEX
+};
+
+MKREGS (PI, PI_INDEX * 16);
+MKREGS (PO, PO_INDEX * 16);
+MKREGS (MC, MC_INDEX * 16);
+
+enum {
+    GLOB_CNT = 0x2c,
+    GLOB_STA = 0x30,
+    CAS      = 0x34
+};
+
+#define GET_BM(index) (((index) >> 4) & 3)
+
+static void po_callback (void *opaque, int free);
+static void pi_callback (void *opaque, int avail);
+static void mc_callback (void *opaque, int avail);
+
+static void warm_reset (AC97LinkState *s)
+{
+    (void) s;
+}
+
+static void cold_reset (AC97LinkState * s)
+{
+    (void) s;
+}
+
+static void fetch_bd (AC97LinkState *s, AC97BusMasterRegs *r)
+{
+    uint8_t b[8];
+
+    pci_dma_read (&s->dev, r->bdbar + r->civ * 8, b, 8);
+    r->bd_valid = 1;
+    r->bd.addr = le32_to_cpu (*(uint32_t *) &b[0]) & ~3;
+    r->bd.ctl_len = le32_to_cpu (*(uint32_t *) &b[4]);
+    r->picb = r->bd.ctl_len & 0xffff;
+    dolog ("bd %2d addr=%#x ctl=%#06x len=%#x(%d bytes)\n",
+           r->civ, r->bd.addr, r->bd.ctl_len >> 16,
+           r->bd.ctl_len & 0xffff,
+           (r->bd.ctl_len & 0xffff) << 1);
+}
+
+static void update_sr (AC97LinkState *s, AC97BusMasterRegs *r, uint32_t new_sr)
+{
+    int event = 0;
+    int level = 0;
+    uint32_t new_mask = new_sr & SR_INT_MASK;
+    uint32_t old_mask = r->sr & SR_INT_MASK;
+    uint32_t masks[] = {GS_PIINT, GS_POINT, GS_MINT};
+
+    if (new_mask ^ old_mask) {
+        /** @todo is IRQ deasserted when only one of status bits is cleared? */
+        if (!new_mask) {
+            event = 1;
+            level = 0;
+        }
+        else {
+            if ((new_mask & SR_LVBCI) && (r->cr & CR_LVBIE)) {
+                event = 1;
+                level = 1;
+            }
+            if ((new_mask & SR_BCIS) && (r->cr & CR_IOCE)) {
+                event = 1;
+                level = 1;
+            }
+        }
+    }
+
+    r->sr = new_sr;
+
+    dolog ("IOC%d LVB%d sr=%#x event=%d level=%d\n",
+           r->sr & SR_BCIS, r->sr & SR_LVBCI,
+           r->sr,
+           event, level);
+
+    if (!event)
+        return;
+
+    if (level) {
+        s->glob_sta |= masks[r - s->bm_regs];
+        dolog ("set irq level=1\n");
+        qemu_set_irq (s->dev.irq[0], 1);
+    }
+    else {
+        s->glob_sta &= ~masks[r - s->bm_regs];
+        dolog ("set irq level=0\n");
+        qemu_set_irq (s->dev.irq[0], 0);
+    }
+}
+
+static void voice_set_active (AC97LinkState *s, int bm_index, int on)
+{
+    switch (bm_index) {
+    case PI_INDEX:
+        AUD_set_active_in (s->voice_pi, on);
+        break;
+
+    case PO_INDEX:
+        AUD_set_active_out (s->voice_po, on);
+        break;
+
+    case MC_INDEX:
+        AUD_set_active_in (s->voice_mc, on);
+        break;
+
+    default:
+        AUD_log ("ac97", "invalid bm_index(%d) in voice_set_active", bm_index);
+        break;
+    }
+}
+
+static void reset_bm_regs (AC97LinkState *s, AC97BusMasterRegs *r)
+{
+    dolog ("reset_bm_regs\n");
+    r->bdbar = 0;
+    r->civ = 0;
+    r->lvi = 0;
+    /** todo do we need to do that? */
+    update_sr (s, r, SR_DCH);
+    r->picb = 0;
+    r->piv = 0;
+    r->cr = r->cr & CR_DONT_CLEAR_MASK;
+    r->bd_valid = 0;
+
+    voice_set_active (s, r - s->bm_regs, 0);
+    memset (s->silence, 0, sizeof (s->silence));
+}
+
+static void mixer_store (AC97LinkState *s, uint32_t i, uint16_t v)
+{
+    if (i + 2 > sizeof (s->mixer_data)) {
+        dolog ("mixer_store: index %d out of bounds %zd\n",
+               i, sizeof (s->mixer_data));
+        return;
+    }
+
+    s->mixer_data[i + 0] = v & 0xff;
+    s->mixer_data[i + 1] = v >> 8;
+}
+
+static uint16_t mixer_load (AC97LinkState *s, uint32_t i)
+{
+    uint16_t val = 0xffff;
+
+    if (i + 2 > sizeof (s->mixer_data)) {
+        dolog ("mixer_load: index %d out of bounds %zd\n",
+               i, sizeof (s->mixer_data));
+    }
+    else {
+        val = s->mixer_data[i + 0] | (s->mixer_data[i + 1] << 8);
+    }
+
+    return val;
+}
+
+static void open_voice (AC97LinkState *s, int index, int freq)
+{
+    struct audsettings as;
+
+    as.freq = freq;
+    as.nchannels = 2;
+    as.fmt = AUD_FMT_S16;
+    as.endianness = 0;
+
+    if (freq > 0) {
+        s->invalid_freq[index] = 0;
+        switch (index) {
+        case PI_INDEX:
+            s->voice_pi = AUD_open_in (
+                &s->card,
+                s->voice_pi,
+                "ac97.pi",
+                s,
+                pi_callback,
+                &as
+                );
+            break;
+
+        case PO_INDEX:
+            s->voice_po = AUD_open_out (
+                &s->card,
+                s->voice_po,
+                "ac97.po",
+                s,
+                po_callback,
+                &as
+                );
+            break;
+
+        case MC_INDEX:
+            s->voice_mc = AUD_open_in (
+                &s->card,
+                s->voice_mc,
+                "ac97.mc",
+                s,
+                mc_callback,
+                &as
+                );
+            break;
+        }
+    }
+    else {
+        s->invalid_freq[index] = freq;
+        switch (index) {
+        case PI_INDEX:
+            AUD_close_in (&s->card, s->voice_pi);
+            s->voice_pi = NULL;
+            break;
+
+        case PO_INDEX:
+            AUD_close_out (&s->card, s->voice_po);
+            s->voice_po = NULL;
+            break;
+
+        case MC_INDEX:
+            AUD_close_in (&s->card, s->voice_mc);
+            s->voice_mc = NULL;
+            break;
+        }
+    }
+}
+
+static void reset_voices (AC97LinkState *s, uint8_t active[LAST_INDEX])
+{
+    uint16_t freq;
+
+    freq = mixer_load (s, AC97_PCM_LR_ADC_Rate);
+    open_voice (s, PI_INDEX, freq);
+    AUD_set_active_in (s->voice_pi, active[PI_INDEX]);
+
+    freq = mixer_load (s, AC97_PCM_Front_DAC_Rate);
+    open_voice (s, PO_INDEX, freq);
+    AUD_set_active_out (s->voice_po, active[PO_INDEX]);
+
+    freq = mixer_load (s, AC97_MIC_ADC_Rate);
+    open_voice (s, MC_INDEX, freq);
+    AUD_set_active_in (s->voice_mc, active[MC_INDEX]);
+}
+
+static void get_volume (uint16_t vol, uint16_t mask, int inverse,
+                        int *mute, uint8_t *lvol, uint8_t *rvol)
+{
+    *mute = (vol >> MUTE_SHIFT) & 1;
+    *rvol = (255 * (vol & mask)) / mask;
+    *lvol = (255 * ((vol >> 8) & mask)) / mask;
+
+    if (inverse) {
+        *rvol = 255 - *rvol;
+        *lvol = 255 - *lvol;
+    }
+}
+
+static void update_combined_volume_out (AC97LinkState *s)
+{
+    uint8_t lvol, rvol, plvol, prvol;
+    int mute, pmute;
+
+    get_volume (mixer_load (s, AC97_Master_Volume_Mute), 0x3f, 1,
+                &mute, &lvol, &rvol);
+    get_volume (mixer_load (s, AC97_PCM_Out_Volume_Mute), 0x1f, 1,
+                &pmute, &plvol, &prvol);
+
+    mute = mute | pmute;
+    lvol = (lvol * plvol) / 255;
+    rvol = (rvol * prvol) / 255;
+
+    AUD_set_volume_out (s->voice_po, mute, lvol, rvol);
+}
+
+static void update_volume_in (AC97LinkState *s)
+{
+    uint8_t lvol, rvol;
+    int mute;
+
+    get_volume (mixer_load (s, AC97_Record_Gain_Mute), 0x0f, 0,
+                &mute, &lvol, &rvol);
+
+    AUD_set_volume_in (s->voice_pi, mute, lvol, rvol);
+}
+
+static void set_volume (AC97LinkState *s, int index, uint32_t val)
+{
+    switch (index) {
+    case AC97_Master_Volume_Mute:
+        val &= 0xbf3f;
+        mixer_store (s, index, val);
+        update_combined_volume_out (s);
+        break;
+    case AC97_PCM_Out_Volume_Mute:
+        val &= 0x9f1f;
+        mixer_store (s, index, val);
+        update_combined_volume_out (s);
+        break;
+    case AC97_Record_Gain_Mute:
+        val &= 0x8f0f;
+        mixer_store (s, index, val);
+        update_volume_in (s);
+        break;
+    }
+}
+
+static void record_select (AC97LinkState *s, uint32_t val)
+{
+    uint8_t rs = val & REC_MASK;
+    uint8_t ls = (val >> 8) & REC_MASK;
+    mixer_store (s, AC97_Record_Select, rs | (ls << 8));
+}
+
+static void mixer_reset (AC97LinkState *s)
+{
+    uint8_t active[LAST_INDEX];
+
+    dolog ("mixer_reset\n");
+    memset (s->mixer_data, 0, sizeof (s->mixer_data));
+    memset (active, 0, sizeof (active));
+    mixer_store (s, AC97_Reset                   , 0x0000); /* 6940 */
+    mixer_store (s, AC97_Headphone_Volume_Mute   , 0x0000);
+    mixer_store (s, AC97_Master_Volume_Mono_Mute , 0x0000);
+    mixer_store (s, AC97_Master_Tone_RL,           0x0000);
+    mixer_store (s, AC97_PC_BEEP_Volume_Mute     , 0x0000);
+    mixer_store (s, AC97_Phone_Volume_Mute       , 0x0000);
+    mixer_store (s, AC97_Mic_Volume_Mute         , 0x0000);
+    mixer_store (s, AC97_Line_In_Volume_Mute     , 0x0000);
+    mixer_store (s, AC97_CD_Volume_Mute          , 0x0000);
+    mixer_store (s, AC97_Video_Volume_Mute       , 0x0000);
+    mixer_store (s, AC97_Aux_Volume_Mute         , 0x0000);
+    mixer_store (s, AC97_Record_Gain_Mic_Mute    , 0x0000);
+    mixer_store (s, AC97_General_Purpose         , 0x0000);
+    mixer_store (s, AC97_3D_Control              , 0x0000);
+    mixer_store (s, AC97_Powerdown_Ctrl_Stat     , 0x000f);
+
+    /*
+     * Sigmatel 9700 (STAC9700)
+     */
+    mixer_store (s, AC97_Vendor_ID1              , 0x8384);
+    mixer_store (s, AC97_Vendor_ID2              , 0x7600); /* 7608 */
+
+    mixer_store (s, AC97_Extended_Audio_ID       , 0x0809);
+    mixer_store (s, AC97_Extended_Audio_Ctrl_Stat, 0x0009);
+    mixer_store (s, AC97_PCM_Front_DAC_Rate      , 0xbb80);
+    mixer_store (s, AC97_PCM_Surround_DAC_Rate   , 0xbb80);
+    mixer_store (s, AC97_PCM_LFE_DAC_Rate        , 0xbb80);
+    mixer_store (s, AC97_PCM_LR_ADC_Rate         , 0xbb80);
+    mixer_store (s, AC97_MIC_ADC_Rate            , 0xbb80);
+
+    record_select (s, 0);
+    set_volume (s, AC97_Master_Volume_Mute, 0x8000);
+    set_volume (s, AC97_PCM_Out_Volume_Mute, 0x8808);
+    set_volume (s, AC97_Record_Gain_Mute, 0x8808);
+
+    reset_voices (s, active);
+}
+
+/**
+ * Native audio mixer
+ * I/O Reads
+ */
+static uint32_t nam_readb (void *opaque, uint32_t addr)
+{
+    AC97LinkState *s = opaque;
+    dolog ("U nam readb %#x\n", addr);
+    s->cas = 0;
+    return ~0U;
+}
+
+static uint32_t nam_readw (void *opaque, uint32_t addr)
+{
+    AC97LinkState *s = opaque;
+    uint32_t val = ~0U;
+    uint32_t index = addr;
+    s->cas = 0;
+    val = mixer_load (s, index);
+    return val;
+}
+
+static uint32_t nam_readl (void *opaque, uint32_t addr)
+{
+    AC97LinkState *s = opaque;
+    dolog ("U nam readl %#x\n", addr);
+    s->cas = 0;
+    return ~0U;
+}
+
+/**
+ * Native audio mixer
+ * I/O Writes
+ */
+static void nam_writeb (void *opaque, uint32_t addr, uint32_t val)
+{
+    AC97LinkState *s = opaque;
+    dolog ("U nam writeb %#x <- %#x\n", addr, val);
+    s->cas = 0;
+}
+
+static void nam_writew (void *opaque, uint32_t addr, uint32_t val)
+{
+    AC97LinkState *s = opaque;
+    uint32_t index = addr;
+    s->cas = 0;
+    switch (index) {
+    case AC97_Reset:
+        mixer_reset (s);
+        break;
+    case AC97_Powerdown_Ctrl_Stat:
+        val &= ~0x800f;
+        val |= mixer_load (s, index) & 0xf;
+        mixer_store (s, index, val);
+        break;
+    case AC97_PCM_Out_Volume_Mute:
+    case AC97_Master_Volume_Mute:
+    case AC97_Record_Gain_Mute:
+        set_volume (s, index, val);
+        break;
+    case AC97_Record_Select:
+        record_select (s, val);
+        break;
+    case AC97_Vendor_ID1:
+    case AC97_Vendor_ID2:
+        dolog ("Attempt to write vendor ID to %#x\n", val);
+        break;
+    case AC97_Extended_Audio_ID:
+        dolog ("Attempt to write extended audio ID to %#x\n", val);
+        break;
+    case AC97_Extended_Audio_Ctrl_Stat:
+        if (!(val & EACS_VRA)) {
+            mixer_store (s, AC97_PCM_Front_DAC_Rate, 0xbb80);
+            mixer_store (s, AC97_PCM_LR_ADC_Rate,    0xbb80);
+            open_voice (s, PI_INDEX, 48000);
+            open_voice (s, PO_INDEX, 48000);
+        }
+        if (!(val & EACS_VRM)) {
+            mixer_store (s, AC97_MIC_ADC_Rate, 0xbb80);
+            open_voice (s, MC_INDEX, 48000);
+        }
+        dolog ("Setting extended audio control to %#x\n", val);
+        mixer_store (s, AC97_Extended_Audio_Ctrl_Stat, val);
+        break;
+    case AC97_PCM_Front_DAC_Rate:
+        if (mixer_load (s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) {
+            mixer_store (s, index, val);
+            dolog ("Set front DAC rate to %d\n", val);
+            open_voice (s, PO_INDEX, val);
+        }
+        else {
+            dolog ("Attempt to set front DAC rate to %d, "
+                   "but VRA is not set\n",
+                   val);
+        }
+        break;
+    case AC97_MIC_ADC_Rate:
+        if (mixer_load (s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRM) {
+            mixer_store (s, index, val);
+            dolog ("Set MIC ADC rate to %d\n", val);
+            open_voice (s, MC_INDEX, val);
+        }
+        else {
+            dolog ("Attempt to set MIC ADC rate to %d, "
+                   "but VRM is not set\n",
+                   val);
+        }
+        break;
+    case AC97_PCM_LR_ADC_Rate:
+        if (mixer_load (s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) {
+            mixer_store (s, index, val);
+            dolog ("Set front LR ADC rate to %d\n", val);
+            open_voice (s, PI_INDEX, val);
+        }
+        else {
+            dolog ("Attempt to set LR ADC rate to %d, but VRA is not set\n",
+                    val);
+        }
+        break;
+    case AC97_Headphone_Volume_Mute:
+    case AC97_Master_Volume_Mono_Mute:
+    case AC97_Master_Tone_RL:
+    case AC97_PC_BEEP_Volume_Mute:
+    case AC97_Phone_Volume_Mute:
+    case AC97_Mic_Volume_Mute:
+    case AC97_Line_In_Volume_Mute:
+    case AC97_CD_Volume_Mute:
+    case AC97_Video_Volume_Mute:
+    case AC97_Aux_Volume_Mute:
+    case AC97_Record_Gain_Mic_Mute:
+    case AC97_General_Purpose:
+    case AC97_3D_Control:
+    case AC97_Sigmatel_Analog:
+    case AC97_Sigmatel_Dac2Invert:
+        /* None of the features in these regs are emulated, so they are RO */
+        break;
+    default:
+        dolog ("U nam writew %#x <- %#x\n", addr, val);
+        mixer_store (s, index, val);
+        break;
+    }
+}
+
+static void nam_writel (void *opaque, uint32_t addr, uint32_t val)
+{
+    AC97LinkState *s = opaque;
+    dolog ("U nam writel %#x <- %#x\n", addr, val);
+    s->cas = 0;
+}
+
+/**
+ * Native audio bus master
+ * I/O Reads
+ */
+static uint32_t nabm_readb (void *opaque, uint32_t addr)
+{
+    AC97LinkState *s = opaque;
+    AC97BusMasterRegs *r = NULL;
+    uint32_t index = addr;
+    uint32_t val = ~0U;
+
+    switch (index) {
+    case CAS:
+        dolog ("CAS %d\n", s->cas);
+        val = s->cas;
+        s->cas = 1;
+        break;
+    case PI_CIV:
+    case PO_CIV:
+    case MC_CIV:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->civ;
+        dolog ("CIV[%d] -> %#x\n", GET_BM (index), val);
+        break;
+    case PI_LVI:
+    case PO_LVI:
+    case MC_LVI:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->lvi;
+        dolog ("LVI[%d] -> %#x\n", GET_BM (index), val);
+        break;
+    case PI_PIV:
+    case PO_PIV:
+    case MC_PIV:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->piv;
+        dolog ("PIV[%d] -> %#x\n", GET_BM (index), val);
+        break;
+    case PI_CR:
+    case PO_CR:
+    case MC_CR:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->cr;
+        dolog ("CR[%d] -> %#x\n", GET_BM (index), val);
+        break;
+    case PI_SR:
+    case PO_SR:
+    case MC_SR:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->sr & 0xff;
+        dolog ("SRb[%d] -> %#x\n", GET_BM (index), val);
+        break;
+    default:
+        dolog ("U nabm readb %#x -> %#x\n", addr, val);
+        break;
+    }
+    return val;
+}
+
+static uint32_t nabm_readw (void *opaque, uint32_t addr)
+{
+    AC97LinkState *s = opaque;
+    AC97BusMasterRegs *r = NULL;
+    uint32_t index = addr;
+    uint32_t val = ~0U;
+
+    switch (index) {
+    case PI_SR:
+    case PO_SR:
+    case MC_SR:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->sr;
+        dolog ("SR[%d] -> %#x\n", GET_BM (index), val);
+        break;
+    case PI_PICB:
+    case PO_PICB:
+    case MC_PICB:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->picb;
+        dolog ("PICB[%d] -> %#x\n", GET_BM (index), val);
+        break;
+    default:
+        dolog ("U nabm readw %#x -> %#x\n", addr, val);
+        break;
+    }
+    return val;
+}
+
+static uint32_t nabm_readl (void *opaque, uint32_t addr)
+{
+    AC97LinkState *s = opaque;
+    AC97BusMasterRegs *r = NULL;
+    uint32_t index = addr;
+    uint32_t val = ~0U;
+
+    switch (index) {
+    case PI_BDBAR:
+    case PO_BDBAR:
+    case MC_BDBAR:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->bdbar;
+        dolog ("BMADDR[%d] -> %#x\n", GET_BM (index), val);
+        break;
+    case PI_CIV:
+    case PO_CIV:
+    case MC_CIV:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->civ | (r->lvi << 8) | (r->sr << 16);
+        dolog ("CIV LVI SR[%d] -> %#x, %#x, %#x\n", GET_BM (index),
+               r->civ, r->lvi, r->sr);
+        break;
+    case PI_PICB:
+    case PO_PICB:
+    case MC_PICB:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->picb | (r->piv << 16) | (r->cr << 24);
+        dolog ("PICB PIV CR[%d] -> %#x %#x %#x %#x\n", GET_BM (index),
+               val, r->picb, r->piv, r->cr);
+        break;
+    case GLOB_CNT:
+        val = s->glob_cnt;
+        dolog ("glob_cnt -> %#x\n", val);
+        break;
+    case GLOB_STA:
+        val = s->glob_sta | GS_S0CR;
+        dolog ("glob_sta -> %#x\n", val);
+        break;
+    default:
+        dolog ("U nabm readl %#x -> %#x\n", addr, val);
+        break;
+    }
+    return val;
+}
+
+/**
+ * Native audio bus master
+ * I/O Writes
+ */
+static void nabm_writeb (void *opaque, uint32_t addr, uint32_t val)
+{
+    AC97LinkState *s = opaque;
+    AC97BusMasterRegs *r = NULL;
+    uint32_t index = addr;
+    switch (index) {
+    case PI_LVI:
+    case PO_LVI:
+    case MC_LVI:
+        r = &s->bm_regs[GET_BM (index)];
+        if ((r->cr & CR_RPBM) && (r->sr & SR_DCH)) {
+            r->sr &= ~(SR_DCH | SR_CELV);
+            r->civ = r->piv;
+            r->piv = (r->piv + 1) % 32;
+            fetch_bd (s, r);
+        }
+        r->lvi = val % 32;
+        dolog ("LVI[%d] <- %#x\n", GET_BM (index), val);
+        break;
+    case PI_CR:
+    case PO_CR:
+    case MC_CR:
+        r = &s->bm_regs[GET_BM (index)];
+        if (val & CR_RR) {
+            reset_bm_regs (s, r);
+        }
+        else {
+            r->cr = val & CR_VALID_MASK;
+            if (!(r->cr & CR_RPBM)) {
+                voice_set_active (s, r - s->bm_regs, 0);
+                r->sr |= SR_DCH;
+            }
+            else {
+                r->civ = r->piv;
+                r->piv = (r->piv + 1) % 32;
+                fetch_bd (s, r);
+                r->sr &= ~SR_DCH;
+                voice_set_active (s, r - s->bm_regs, 1);
+            }
+        }
+        dolog ("CR[%d] <- %#x (cr %#x)\n", GET_BM (index), val, r->cr);
+        break;
+    case PI_SR:
+    case PO_SR:
+    case MC_SR:
+        r = &s->bm_regs[GET_BM (index)];
+        r->sr |= val & ~(SR_RO_MASK | SR_WCLEAR_MASK);
+        update_sr (s, r, r->sr & ~(val & SR_WCLEAR_MASK));
+        dolog ("SR[%d] <- %#x (sr %#x)\n", GET_BM (index), val, r->sr);
+        break;
+    default:
+        dolog ("U nabm writeb %#x <- %#x\n", addr, val);
+        break;
+    }
+}
+
+static void nabm_writew (void *opaque, uint32_t addr, uint32_t val)
+{
+    AC97LinkState *s = opaque;
+    AC97BusMasterRegs *r = NULL;
+    uint32_t index = addr;
+    switch (index) {
+    case PI_SR:
+    case PO_SR:
+    case MC_SR:
+        r = &s->bm_regs[GET_BM (index)];
+        r->sr |= val & ~(SR_RO_MASK | SR_WCLEAR_MASK);
+        update_sr (s, r, r->sr & ~(val & SR_WCLEAR_MASK));
+        dolog ("SR[%d] <- %#x (sr %#x)\n", GET_BM (index), val, r->sr);
+        break;
+    default:
+        dolog ("U nabm writew %#x <- %#x\n", addr, val);
+        break;
+    }
+}
+
+static void nabm_writel (void *opaque, uint32_t addr, uint32_t val)
+{
+    AC97LinkState *s = opaque;
+    AC97BusMasterRegs *r = NULL;
+    uint32_t index = addr;
+    switch (index) {
+    case PI_BDBAR:
+    case PO_BDBAR:
+    case MC_BDBAR:
+        r = &s->bm_regs[GET_BM (index)];
+        r->bdbar = val & ~3;
+        dolog ("BDBAR[%d] <- %#x (bdbar %#x)\n",
+               GET_BM (index), val, r->bdbar);
+        break;
+    case GLOB_CNT:
+        if (val & GC_WR)
+            warm_reset (s);
+        if (val & GC_CR)
+            cold_reset (s);
+        if (!(val & (GC_WR | GC_CR)))
+            s->glob_cnt = val & GC_VALID_MASK;
+        dolog ("glob_cnt <- %#x (glob_cnt %#x)\n", val, s->glob_cnt);
+        break;
+    case GLOB_STA:
+        s->glob_sta &= ~(val & GS_WCLEAR_MASK);
+        s->glob_sta |= (val & ~(GS_WCLEAR_MASK | GS_RO_MASK)) & GS_VALID_MASK;
+        dolog ("glob_sta <- %#x (glob_sta %#x)\n", val, s->glob_sta);
+        break;
+    default:
+        dolog ("U nabm writel %#x <- %#x\n", addr, val);
+        break;
+    }
+}
+
+static int write_audio (AC97LinkState *s, AC97BusMasterRegs *r,
+                        int max, int *stop)
+{
+    uint8_t tmpbuf[4096];
+    uint32_t addr = r->bd.addr;
+    uint32_t temp = r->picb << 1;
+    uint32_t written = 0;
+    int to_copy = 0;
+    temp = audio_MIN (temp, max);
+
+    if (!temp) {
+        *stop = 1;
+        return 0;
+    }
+
+    while (temp) {
+        int copied;
+        to_copy = audio_MIN (temp, sizeof (tmpbuf));
+        pci_dma_read (&s->dev, addr, tmpbuf, to_copy);
+        copied = AUD_write (s->voice_po, tmpbuf, to_copy);
+        dolog ("write_audio max=%x to_copy=%x copied=%x\n",
+               max, to_copy, copied);
+        if (!copied) {
+            *stop = 1;
+            break;
+        }
+        temp -= copied;
+        addr += copied;
+        written += copied;
+    }
+
+    if (!temp) {
+        if (to_copy < 4) {
+            dolog ("whoops\n");
+            s->last_samp = 0;
+        }
+        else {
+            s->last_samp = *(uint32_t *) &tmpbuf[to_copy - 4];
+        }
+    }
+
+    r->bd.addr = addr;
+    return written;
+}
+
+static void write_bup (AC97LinkState *s, int elapsed)
+{
+    dolog ("write_bup\n");
+    if (!(s->bup_flag & BUP_SET)) {
+        if (s->bup_flag & BUP_LAST) {
+            int i;
+            uint8_t *p = s->silence;
+            for (i = 0; i < sizeof (s->silence) / 4; i++, p += 4) {
+                *(uint32_t *) p = s->last_samp;
+            }
+        }
+        else {
+            memset (s->silence, 0, sizeof (s->silence));
+        }
+        s->bup_flag |= BUP_SET;
+    }
+
+    while (elapsed) {
+        int temp = audio_MIN (elapsed, sizeof (s->silence));
+        while (temp) {
+            int copied = AUD_write (s->voice_po, s->silence, temp);
+            if (!copied)
+                return;
+            temp -= copied;
+            elapsed -= copied;
+        }
+    }
+}
+
+static int read_audio (AC97LinkState *s, AC97BusMasterRegs *r,
+                       int max, int *stop)
+{
+    uint8_t tmpbuf[4096];
+    uint32_t addr = r->bd.addr;
+    uint32_t temp = r->picb << 1;
+    uint32_t nread = 0;
+    int to_copy = 0;
+    SWVoiceIn *voice = (r - s->bm_regs) == MC_INDEX ? s->voice_mc : s->voice_pi;
+
+    temp = audio_MIN (temp, max);
+
+    if (!temp) {
+        *stop = 1;
+        return 0;
+    }
+
+    while (temp) {
+        int acquired;
+        to_copy = audio_MIN (temp, sizeof (tmpbuf));
+        acquired = AUD_read (voice, tmpbuf, to_copy);
+        if (!acquired) {
+            *stop = 1;
+            break;
+        }
+        pci_dma_write (&s->dev, addr, tmpbuf, acquired);
+        temp -= acquired;
+        addr += acquired;
+        nread += acquired;
+    }
+
+    r->bd.addr = addr;
+    return nread;
+}
+
+static void transfer_audio (AC97LinkState *s, int index, int elapsed)
+{
+    AC97BusMasterRegs *r = &s->bm_regs[index];
+    int stop = 0;
+
+    if (s->invalid_freq[index]) {
+        AUD_log ("ac97", "attempt to use voice %d with invalid frequency %d\n",
+                 index, s->invalid_freq[index]);
+        return;
+    }
+
+    if (r->sr & SR_DCH) {
+        if (r->cr & CR_RPBM) {
+            switch (index) {
+            case PO_INDEX:
+                write_bup (s, elapsed);
+                break;
+            }
+        }
+        return;
+    }
+
+    while ((elapsed >> 1) && !stop) {
+        int temp;
+
+        if (!r->bd_valid) {
+            dolog ("invalid bd\n");
+            fetch_bd (s, r);
+        }
+
+        if (!r->picb) {
+            dolog ("fresh bd %d is empty %#x %#x\n",
+                   r->civ, r->bd.addr, r->bd.ctl_len);
+            if (r->civ == r->lvi) {
+                r->sr |= SR_DCH; /* CELV? */
+                s->bup_flag = 0;
+                break;
+            }
+            r->sr &= ~SR_CELV;
+            r->civ = r->piv;
+            r->piv = (r->piv + 1) % 32;
+            fetch_bd (s, r);
+            return;
+        }
+
+        switch (index) {
+        case PO_INDEX:
+            temp = write_audio (s, r, elapsed, &stop);
+            elapsed -= temp;
+            r->picb -= (temp >> 1);
+            break;
+
+        case PI_INDEX:
+        case MC_INDEX:
+            temp = read_audio (s, r, elapsed, &stop);
+            elapsed -= temp;
+            r->picb -= (temp >> 1);
+            break;
+        }
+
+        if (!r->picb) {
+            uint32_t new_sr = r->sr & ~SR_CELV;
+
+            if (r->bd.ctl_len & BD_IOC) {
+                new_sr |= SR_BCIS;
+            }
+
+            if (r->civ == r->lvi) {
+                dolog ("Underrun civ (%d) == lvi (%d)\n", r->civ, r->lvi);
+
+                new_sr |= SR_LVBCI | SR_DCH | SR_CELV;
+                stop = 1;
+                s->bup_flag = (r->bd.ctl_len & BD_BUP) ? BUP_LAST : 0;
+            }
+            else {
+                r->civ = r->piv;
+                r->piv = (r->piv + 1) % 32;
+                fetch_bd (s, r);
+            }
+
+            update_sr (s, r, new_sr);
+        }
+    }
+}
+
+static void pi_callback (void *opaque, int avail)
+{
+    transfer_audio (opaque, PI_INDEX, avail);
+}
+
+static void mc_callback (void *opaque, int avail)
+{
+    transfer_audio (opaque, MC_INDEX, avail);
+}
+
+static void po_callback (void *opaque, int free)
+{
+    transfer_audio (opaque, PO_INDEX, free);
+}
+
+static const VMStateDescription vmstate_ac97_bm_regs = {
+    .name = "ac97_bm_regs",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT32 (bdbar, AC97BusMasterRegs),
+        VMSTATE_UINT8 (civ, AC97BusMasterRegs),
+        VMSTATE_UINT8 (lvi, AC97BusMasterRegs),
+        VMSTATE_UINT16 (sr, AC97BusMasterRegs),
+        VMSTATE_UINT16 (picb, AC97BusMasterRegs),
+        VMSTATE_UINT8 (piv, AC97BusMasterRegs),
+        VMSTATE_UINT8 (cr, AC97BusMasterRegs),
+        VMSTATE_UINT32 (bd_valid, AC97BusMasterRegs),
+        VMSTATE_UINT32 (bd.addr, AC97BusMasterRegs),
+        VMSTATE_UINT32 (bd.ctl_len, AC97BusMasterRegs),
+        VMSTATE_END_OF_LIST ()
+    }
+};
+
+static int ac97_post_load (void *opaque, int version_id)
+{
+    uint8_t active[LAST_INDEX];
+    AC97LinkState *s = opaque;
+
+    record_select (s, mixer_load (s, AC97_Record_Select));
+    set_volume (s, AC97_Master_Volume_Mute,
+                mixer_load (s, AC97_Master_Volume_Mute));
+    set_volume (s, AC97_PCM_Out_Volume_Mute,
+                mixer_load (s, AC97_PCM_Out_Volume_Mute));
+    set_volume (s, AC97_Record_Gain_Mute,
+                mixer_load (s, AC97_Record_Gain_Mute));
+
+    active[PI_INDEX] = !!(s->bm_regs[PI_INDEX].cr & CR_RPBM);
+    active[PO_INDEX] = !!(s->bm_regs[PO_INDEX].cr & CR_RPBM);
+    active[MC_INDEX] = !!(s->bm_regs[MC_INDEX].cr & CR_RPBM);
+    reset_voices (s, active);
+
+    s->bup_flag = 0;
+    s->last_samp = 0;
+    return 0;
+}
+
+static bool is_version_2 (void *opaque, int version_id)
+{
+    return version_id == 2;
+}
+
+static const VMStateDescription vmstate_ac97 = {
+    .name = "ac97",
+    .version_id = 3,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .post_load = ac97_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_PCI_DEVICE (dev, AC97LinkState),
+        VMSTATE_UINT32 (glob_cnt, AC97LinkState),
+        VMSTATE_UINT32 (glob_sta, AC97LinkState),
+        VMSTATE_UINT32 (cas, AC97LinkState),
+        VMSTATE_STRUCT_ARRAY (bm_regs, AC97LinkState, 3, 1,
+                              vmstate_ac97_bm_regs, AC97BusMasterRegs),
+        VMSTATE_BUFFER (mixer_data, AC97LinkState),
+        VMSTATE_UNUSED_TEST (is_version_2, 3),
+        VMSTATE_END_OF_LIST ()
+    }
+};
+
+static uint64_t nam_read(void *opaque, hwaddr addr, unsigned size)
+{
+    if ((addr / size) > 256) {
+        return -1;
+    }
+
+    switch (size) {
+    case 1:
+        return nam_readb(opaque, addr);
+    case 2:
+        return nam_readw(opaque, addr);
+    case 4:
+        return nam_readl(opaque, addr);
+    default:
+        return -1;
+    }
+}
+
+static void nam_write(void *opaque, hwaddr addr, uint64_t val,
+                      unsigned size)
+{
+    if ((addr / size) > 256) {
+        return;
+    }
+
+    switch (size) {
+    case 1:
+        nam_writeb(opaque, addr, val);
+        break;
+    case 2:
+        nam_writew(opaque, addr, val);
+        break;
+    case 4:
+        nam_writel(opaque, addr, val);
+        break;
+    }
+}
+
+static const MemoryRegionOps ac97_io_nam_ops = {
+    .read = nam_read,
+    .write = nam_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t nabm_read(void *opaque, hwaddr addr, unsigned size)
+{
+    if ((addr / size) > 64) {
+        return -1;
+    }
+
+    switch (size) {
+    case 1:
+        return nabm_readb(opaque, addr);
+    case 2:
+        return nabm_readw(opaque, addr);
+    case 4:
+        return nabm_readl(opaque, addr);
+    default:
+        return -1;
+    }
+}
+
+static void nabm_write(void *opaque, hwaddr addr, uint64_t val,
+                      unsigned size)
+{
+    if ((addr / size) > 64) {
+        return;
+    }
+
+    switch (size) {
+    case 1:
+        nabm_writeb(opaque, addr, val);
+        break;
+    case 2:
+        nabm_writew(opaque, addr, val);
+        break;
+    case 4:
+        nabm_writel(opaque, addr, val);
+        break;
+    }
+}
+
+
+static const MemoryRegionOps ac97_io_nabm_ops = {
+    .read = nabm_read,
+    .write = nabm_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ac97_on_reset (void *opaque)
+{
+    AC97LinkState *s = opaque;
+
+    reset_bm_regs (s, &s->bm_regs[0]);
+    reset_bm_regs (s, &s->bm_regs[1]);
+    reset_bm_regs (s, &s->bm_regs[2]);
+
+    /*
+     * Reset the mixer too. The Windows XP driver seems to rely on
+     * this. At least it wants to read the vendor id before it resets
+     * the codec manually.
+     */
+    mixer_reset (s);
+}
+
+static int ac97_initfn (PCIDevice *dev)
+{
+    AC97LinkState *s = DO_UPCAST (AC97LinkState, dev, dev);
+    uint8_t *c = s->dev.config;
+
+    /* TODO: no need to override */
+    c[PCI_COMMAND] = 0x00;      /* pcicmd pci command rw, ro */
+    c[PCI_COMMAND + 1] = 0x00;
+
+    /* TODO: */
+    c[PCI_STATUS] = PCI_STATUS_FAST_BACK;      /* pcists pci status rwc, ro */
+    c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_MEDIUM >> 8;
+
+    c[PCI_CLASS_PROG] = 0x00;      /* pi programming interface ro */
+
+    /* TODO set when bar is registered. no need to override. */
+    /* nabmar native audio mixer base address rw */
+    c[PCI_BASE_ADDRESS_0] = PCI_BASE_ADDRESS_SPACE_IO;
+    c[PCI_BASE_ADDRESS_0 + 1] = 0x00;
+    c[PCI_BASE_ADDRESS_0 + 2] = 0x00;
+    c[PCI_BASE_ADDRESS_0 + 3] = 0x00;
+
+    /* TODO set when bar is registered. no need to override. */
+      /* nabmbar native audio bus mastering base address rw */
+    c[PCI_BASE_ADDRESS_0 + 4] = PCI_BASE_ADDRESS_SPACE_IO;
+    c[PCI_BASE_ADDRESS_0 + 5] = 0x00;
+    c[PCI_BASE_ADDRESS_0 + 6] = 0x00;
+    c[PCI_BASE_ADDRESS_0 + 7] = 0x00;
+
+    if (s->use_broken_id) {
+        c[PCI_SUBSYSTEM_VENDOR_ID] = 0x86;
+        c[PCI_SUBSYSTEM_VENDOR_ID + 1] = 0x80;
+        c[PCI_SUBSYSTEM_ID] = 0x00;
+        c[PCI_SUBSYSTEM_ID + 1] = 0x00;
+    }
+
+    c[PCI_INTERRUPT_LINE] = 0x00;      /* intr_ln interrupt line rw */
+    c[PCI_INTERRUPT_PIN] = 0x01;      /* intr_pn interrupt pin ro */
+
+    memory_region_init_io (&s->io_nam, &ac97_io_nam_ops, s, "ac97-nam", 1024);
+    memory_region_init_io (&s->io_nabm, &ac97_io_nabm_ops, s, "ac97-nabm", 256);
+    pci_register_bar (&s->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_nam);
+    pci_register_bar (&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_nabm);
+    qemu_register_reset (ac97_on_reset, s);
+    AUD_register_card ("ac97", &s->card);
+    ac97_on_reset (s);
+    return 0;
+}
+
+static void ac97_exitfn (PCIDevice *dev)
+{
+    AC97LinkState *s = DO_UPCAST (AC97LinkState, dev, dev);
+
+    memory_region_destroy (&s->io_nam);
+    memory_region_destroy (&s->io_nabm);
+}
+
+int ac97_init (PCIBus *bus)
+{
+    pci_create_simple (bus, -1, "AC97");
+    return 0;
+}
+
+static Property ac97_properties[] = {
+    DEFINE_PROP_UINT32 ("use_broken_id", AC97LinkState, use_broken_id, 0),
+    DEFINE_PROP_END_OF_LIST (),
+};
+
+static void ac97_class_init (ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS (klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS (klass);
+
+    k->init = ac97_initfn;
+    k->exit = ac97_exitfn;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_82801AA_5;
+    k->revision = 0x01;
+    k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO;
+    dc->desc = "Intel 82801AA AC97 Audio";
+    dc->vmsd = &vmstate_ac97;
+    dc->props = ac97_properties;
+}
+
+static const TypeInfo ac97_info = {
+    .name          = "AC97",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof (AC97LinkState),
+    .class_init    = ac97_class_init,
+};
+
+static void ac97_register_types (void)
+{
+    type_register_static (&ac97_info);
+}
+
+type_init (ac97_register_types)
diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c
new file mode 100644 (file)
index 0000000..133c0ff
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * QEMU Proxy for OPL2/3 emulation by MAME team
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/audio/audio.h"
+#include "audio/audio.h"
+#include "hw/isa/isa.h"
+
+//#define DEBUG
+
+#define ADLIB_KILL_TIMERS 1
+
+#ifdef DEBUG
+#include "qemu/timer.h"
+#endif
+
+#define dolog(...) AUD_log ("adlib", __VA_ARGS__)
+#ifdef DEBUG
+#define ldebug(...) dolog (__VA_ARGS__)
+#else
+#define ldebug(...)
+#endif
+
+#ifdef HAS_YMF262
+#include "ymf262.h"
+void YMF262UpdateOneQEMU (int which, INT16 *dst, int length);
+#define SHIFT 2
+#else
+#include "hw/fmopl.h"
+#define SHIFT 1
+#endif
+
+#define IO_READ_PROTO(name) \
+    uint32_t name (void *opaque, uint32_t nport)
+#define IO_WRITE_PROTO(name) \
+    void name (void *opaque, uint32_t nport, uint32_t val)
+
+static struct {
+    int port;
+    int freq;
+} conf = {0x220, 44100};
+
+typedef struct {
+    QEMUSoundCard card;
+    int ticking[2];
+    int enabled;
+    int active;
+    int bufpos;
+#ifdef DEBUG
+    int64_t exp[2];
+#endif
+    int16_t *mixbuf;
+    uint64_t dexp[2];
+    SWVoiceOut *voice;
+    int left, pos, samples;
+    QEMUAudioTimeStamp ats;
+#ifndef HAS_YMF262
+    FM_OPL *opl;
+#endif
+} AdlibState;
+
+static AdlibState glob_adlib;
+
+static void adlib_stop_opl_timer (AdlibState *s, size_t n)
+{
+#ifdef HAS_YMF262
+    YMF262TimerOver (0, n);
+#else
+    OPLTimerOver (s->opl, n);
+#endif
+    s->ticking[n] = 0;
+}
+
+static void adlib_kill_timers (AdlibState *s)
+{
+    size_t i;
+
+    for (i = 0; i < 2; ++i) {
+        if (s->ticking[i]) {
+            uint64_t delta;
+
+            delta = AUD_get_elapsed_usec_out (s->voice, &s->ats);
+            ldebug (
+                "delta = %f dexp = %f expired => %d\n",
+                delta / 1000000.0,
+                s->dexp[i] / 1000000.0,
+                delta >= s->dexp[i]
+                );
+            if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) {
+                adlib_stop_opl_timer (s, i);
+                AUD_init_time_stamp_out (s->voice, &s->ats);
+            }
+        }
+    }
+}
+
+static IO_WRITE_PROTO (adlib_write)
+{
+    AdlibState *s = opaque;
+    int a = nport & 3;
+
+    s->active = 1;
+    AUD_set_active_out (s->voice, 1);
+
+    adlib_kill_timers (s);
+
+#ifdef HAS_YMF262
+    YMF262Write (0, a, val);
+#else
+    OPLWrite (s->opl, a, val);
+#endif
+}
+
+static IO_READ_PROTO (adlib_read)
+{
+    AdlibState *s = opaque;
+    uint8_t data;
+    int a = nport & 3;
+
+    adlib_kill_timers (s);
+
+#ifdef HAS_YMF262
+    data = YMF262Read (0, a);
+#else
+    data = OPLRead (s->opl, a);
+#endif
+    return data;
+}
+
+static void timer_handler (int c, double interval_Sec)
+{
+    AdlibState *s = &glob_adlib;
+    unsigned n = c & 1;
+#ifdef DEBUG
+    double interval;
+    int64_t exp;
+#endif
+
+    if (interval_Sec == 0.0) {
+        s->ticking[n] = 0;
+        return;
+    }
+
+    s->ticking[n] = 1;
+#ifdef DEBUG
+    interval = get_ticks_per_sec () * interval_Sec;
+    exp = qemu_get_clock_ns (vm_clock) + interval;
+    s->exp[n] = exp;
+#endif
+
+    s->dexp[n] = interval_Sec * 1000000.0;
+    AUD_init_time_stamp_out (s->voice, &s->ats);
+}
+
+static int write_audio (AdlibState *s, int samples)
+{
+    int net = 0;
+    int pos = s->pos;
+
+    while (samples) {
+        int nbytes, wbytes, wsampl;
+
+        nbytes = samples << SHIFT;
+        wbytes = AUD_write (
+            s->voice,
+            s->mixbuf + (pos << (SHIFT - 1)),
+            nbytes
+            );
+
+        if (wbytes) {
+            wsampl = wbytes >> SHIFT;
+
+            samples -= wsampl;
+            pos = (pos + wsampl) % s->samples;
+
+            net += wsampl;
+        }
+        else {
+            break;
+        }
+    }
+
+    return net;
+}
+
+static void adlib_callback (void *opaque, int free)
+{
+    AdlibState *s = opaque;
+    int samples, net = 0, to_play, written;
+
+    samples = free >> SHIFT;
+    if (!(s->active && s->enabled) || !samples) {
+        return;
+    }
+
+    to_play = audio_MIN (s->left, samples);
+    while (to_play) {
+        written = write_audio (s, to_play);
+
+        if (written) {
+            s->left -= written;
+            samples -= written;
+            to_play -= written;
+            s->pos = (s->pos + written) % s->samples;
+        }
+        else {
+            return;
+        }
+    }
+
+    samples = audio_MIN (samples, s->samples - s->pos);
+    if (!samples) {
+        return;
+    }
+
+#ifdef HAS_YMF262
+    YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
+#else
+    YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
+#endif
+
+    while (samples) {
+        written = write_audio (s, samples);
+
+        if (written) {
+            net += written;
+            samples -= written;
+            s->pos = (s->pos + written) % s->samples;
+        }
+        else {
+            s->left = samples;
+            return;
+        }
+    }
+}
+
+static void Adlib_fini (AdlibState *s)
+{
+#ifdef HAS_YMF262
+    YMF262Shutdown ();
+#else
+    if (s->opl) {
+        OPLDestroy (s->opl);
+        s->opl = NULL;
+    }
+#endif
+
+    if (s->mixbuf) {
+        g_free (s->mixbuf);
+    }
+
+    s->active = 0;
+    s->enabled = 0;
+    AUD_remove_card (&s->card);
+}
+
+int Adlib_init (ISABus *bus)
+{
+    AdlibState *s = &glob_adlib;
+    struct audsettings as;
+
+#ifdef HAS_YMF262
+    if (YMF262Init (1, 14318180, conf.freq)) {
+        dolog ("YMF262Init %d failed\n", conf.freq);
+        return -1;
+    }
+    else {
+        YMF262SetTimerHandler (0, timer_handler, 0);
+        s->enabled = 1;
+    }
+#else
+    s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq);
+    if (!s->opl) {
+        dolog ("OPLCreate %d failed\n", conf.freq);
+        return -1;
+    }
+    else {
+        OPLSetTimerHandler (s->opl, timer_handler, 0);
+        s->enabled = 1;
+    }
+#endif
+
+    as.freq = conf.freq;
+    as.nchannels = SHIFT;
+    as.fmt = AUD_FMT_S16;
+    as.endianness = AUDIO_HOST_ENDIANNESS;
+
+    AUD_register_card ("adlib", &s->card);
+
+    s->voice = AUD_open_out (
+        &s->card,
+        s->voice,
+        "adlib",
+        s,
+        adlib_callback,
+        &as
+        );
+    if (!s->voice) {
+        Adlib_fini (s);
+        return -1;
+    }
+
+    s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT;
+    s->mixbuf = g_malloc0 (s->samples << SHIFT);
+
+    register_ioport_read (0x388, 4, 1, adlib_read, s);
+    register_ioport_write (0x388, 4, 1, adlib_write, s);
+
+    register_ioport_read (conf.port, 4, 1, adlib_read, s);
+    register_ioport_write (conf.port, 4, 1, adlib_write, s);
+
+    register_ioport_read (conf.port + 8, 2, 1, adlib_read, s);
+    register_ioport_write (conf.port + 8, 2, 1, adlib_write, s);
+
+    return 0;
+}
diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c
new file mode 100644 (file)
index 0000000..5711b62
--- /dev/null
@@ -0,0 +1,697 @@
+/*
+ * QEMU Crystal CS4231 audio chip emulation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/audio/audio.h"
+#include "audio/audio.h"
+#include "hw/isa/isa.h"
+#include "hw/qdev.h"
+#include "qemu/timer.h"
+
+/*
+  Missing features:
+  ADC
+  Loopback
+  Timer
+  ADPCM
+  More...
+*/
+
+/* #define DEBUG */
+/* #define DEBUG_XLAW */
+
+static struct {
+    int aci_counter;
+} conf = {1};
+
+#ifdef DEBUG
+#define dolog(...) AUD_log ("cs4231a", __VA_ARGS__)
+#else
+#define dolog(...)
+#endif
+
+#define lwarn(...) AUD_log ("cs4231a", "warning: " __VA_ARGS__)
+#define lerr(...) AUD_log ("cs4231a", "error: " __VA_ARGS__)
+
+#define CS_REGS 16
+#define CS_DREGS 32
+
+typedef struct CSState {
+    ISADevice dev;
+    QEMUSoundCard card;
+    MemoryRegion ioports;
+    qemu_irq pic;
+    uint32_t regs[CS_REGS];
+    uint8_t dregs[CS_DREGS];
+    uint32_t irq;
+    uint32_t dma;
+    uint32_t port;
+    int shift;
+    int dma_running;
+    int audio_free;
+    int transferred;
+    int aci_counter;
+    SWVoiceOut *voice;
+    int16_t *tab;
+} CSState;
+
+#define MODE2 (1 << 6)
+#define MCE (1 << 6)
+#define PMCE (1 << 4)
+#define CMCE (1 << 5)
+#define TE (1 << 6)
+#define PEN (1 << 0)
+#define INT (1 << 0)
+#define IEN (1 << 1)
+#define PPIO (1 << 6)
+#define PI (1 << 4)
+#define CI (1 << 5)
+#define TI (1 << 6)
+
+enum {
+    Index_Address,
+    Index_Data,
+    Status,
+    PIO_Data
+};
+
+enum {
+    Left_ADC_Input_Control,
+    Right_ADC_Input_Control,
+    Left_AUX1_Input_Control,
+    Right_AUX1_Input_Control,
+    Left_AUX2_Input_Control,
+    Right_AUX2_Input_Control,
+    Left_DAC_Output_Control,
+    Right_DAC_Output_Control,
+    FS_And_Playback_Data_Format,
+    Interface_Configuration,
+    Pin_Control,
+    Error_Status_And_Initialization,
+    MODE_And_ID,
+    Loopback_Control,
+    Playback_Upper_Base_Count,
+    Playback_Lower_Base_Count,
+    Alternate_Feature_Enable_I,
+    Alternate_Feature_Enable_II,
+    Left_Line_Input_Control,
+    Right_Line_Input_Control,
+    Timer_Low_Base,
+    Timer_High_Base,
+    RESERVED,
+    Alternate_Feature_Enable_III,
+    Alternate_Feature_Status,
+    Version_Chip_ID,
+    Mono_Input_And_Output_Control,
+    RESERVED_2,
+    Capture_Data_Format,
+    RESERVED_3,
+    Capture_Upper_Base_Count,
+    Capture_Lower_Base_Count
+};
+
+static int freqs[2][8] = {
+    { 8000, 16000, 27420, 32000,    -1,    -1, 48000, 9000 },
+    { 5510, 11025, 18900, 22050, 37800, 44100, 33075, 6620 }
+};
+
+/* Tables courtesy http://hazelware.luggle.com/tutorials/mulawcompression.html */
+static int16_t MuLawDecompressTable[256] =
+{
+     -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
+     -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
+     -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
+     -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
+      -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
+      -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
+      -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
+      -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
+      -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
+      -1372, -1308, -1244, -1180, -1116, -1052,  -988,  -924,
+       -876,  -844,  -812,  -780,  -748,  -716,  -684,  -652,
+       -620,  -588,  -556,  -524,  -492,  -460,  -428,  -396,
+       -372,  -356,  -340,  -324,  -308,  -292,  -276,  -260,
+       -244,  -228,  -212,  -196,  -180,  -164,  -148,  -132,
+       -120,  -112,  -104,   -96,   -88,   -80,   -72,   -64,
+        -56,   -48,   -40,   -32,   -24,   -16,    -8,     0,
+      32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
+      23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
+      15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
+      11900, 11388, 10876, 10364,  9852,  9340,  8828,  8316,
+       7932,  7676,  7420,  7164,  6908,  6652,  6396,  6140,
+       5884,  5628,  5372,  5116,  4860,  4604,  4348,  4092,
+       3900,  3772,  3644,  3516,  3388,  3260,  3132,  3004,
+       2876,  2748,  2620,  2492,  2364,  2236,  2108,  1980,
+       1884,  1820,  1756,  1692,  1628,  1564,  1500,  1436,
+       1372,  1308,  1244,  1180,  1116,  1052,   988,   924,
+        876,   844,   812,   780,   748,   716,   684,   652,
+        620,   588,   556,   524,   492,   460,   428,   396,
+        372,   356,   340,   324,   308,   292,   276,   260,
+        244,   228,   212,   196,   180,   164,   148,   132,
+        120,   112,   104,    96,    88,    80,    72,    64,
+         56,    48,    40,    32,    24,    16,     8,     0
+};
+
+static int16_t ALawDecompressTable[256] =
+{
+     -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
+     -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
+     -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
+     -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
+     -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
+     -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
+     -11008,-10496,-12032,-11520,-8960, -8448, -9984, -9472,
+     -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
+     -344,  -328,  -376,  -360,  -280,  -264,  -312,  -296,
+     -472,  -456,  -504,  -488,  -408,  -392,  -440,  -424,
+     -88,   -72,   -120,  -104,  -24,   -8,    -56,   -40,
+     -216,  -200,  -248,  -232,  -152,  -136,  -184,  -168,
+     -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
+     -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
+     -688,  -656,  -752,  -720,  -560,  -528,  -624,  -592,
+     -944,  -912,  -1008, -976,  -816,  -784,  -880,  -848,
+      5504,  5248,  6016,  5760,  4480,  4224,  4992,  4736,
+      7552,  7296,  8064,  7808,  6528,  6272,  7040,  6784,
+      2752,  2624,  3008,  2880,  2240,  2112,  2496,  2368,
+      3776,  3648,  4032,  3904,  3264,  3136,  3520,  3392,
+      22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
+      30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
+      11008, 10496, 12032, 11520, 8960,  8448,  9984,  9472,
+      15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
+      344,   328,   376,   360,   280,   264,   312,   296,
+      472,   456,   504,   488,   408,   392,   440,   424,
+      88,    72,   120,   104,    24,     8,    56,    40,
+      216,   200,   248,   232,   152,   136,   184,   168,
+      1376,  1312,  1504,  1440,  1120,  1056,  1248,  1184,
+      1888,  1824,  2016,  1952,  1632,  1568,  1760,  1696,
+      688,   656,   752,   720,   560,   528,   624,   592,
+      944,   912,  1008,   976,   816,   784,   880,   848
+};
+
+static void cs_reset (void *opaque)
+{
+    CSState *s = opaque;
+
+    s->regs[Index_Address] = 0x40;
+    s->regs[Index_Data]    = 0x00;
+    s->regs[Status]        = 0x00;
+    s->regs[PIO_Data]      = 0x00;
+
+    s->dregs[Left_ADC_Input_Control]          = 0x00;
+    s->dregs[Right_ADC_Input_Control]         = 0x00;
+    s->dregs[Left_AUX1_Input_Control]         = 0x88;
+    s->dregs[Right_AUX1_Input_Control]        = 0x88;
+    s->dregs[Left_AUX2_Input_Control]         = 0x88;
+    s->dregs[Right_AUX2_Input_Control]        = 0x88;
+    s->dregs[Left_DAC_Output_Control]         = 0x80;
+    s->dregs[Right_DAC_Output_Control]        = 0x80;
+    s->dregs[FS_And_Playback_Data_Format]     = 0x00;
+    s->dregs[Interface_Configuration]         = 0x08;
+    s->dregs[Pin_Control]                     = 0x00;
+    s->dregs[Error_Status_And_Initialization] = 0x00;
+    s->dregs[MODE_And_ID]                     = 0x8a;
+    s->dregs[Loopback_Control]                = 0x00;
+    s->dregs[Playback_Upper_Base_Count]       = 0x00;
+    s->dregs[Playback_Lower_Base_Count]       = 0x00;
+    s->dregs[Alternate_Feature_Enable_I]      = 0x00;
+    s->dregs[Alternate_Feature_Enable_II]     = 0x00;
+    s->dregs[Left_Line_Input_Control]         = 0x88;
+    s->dregs[Right_Line_Input_Control]        = 0x88;
+    s->dregs[Timer_Low_Base]                  = 0x00;
+    s->dregs[Timer_High_Base]                 = 0x00;
+    s->dregs[RESERVED]                        = 0x00;
+    s->dregs[Alternate_Feature_Enable_III]    = 0x00;
+    s->dregs[Alternate_Feature_Status]        = 0x00;
+    s->dregs[Version_Chip_ID]                 = 0xa0;
+    s->dregs[Mono_Input_And_Output_Control]   = 0xa0;
+    s->dregs[RESERVED_2]                      = 0x00;
+    s->dregs[Capture_Data_Format]             = 0x00;
+    s->dregs[RESERVED_3]                      = 0x00;
+    s->dregs[Capture_Upper_Base_Count]        = 0x00;
+    s->dregs[Capture_Lower_Base_Count]        = 0x00;
+}
+
+static void cs_audio_callback (void *opaque, int free)
+{
+    CSState *s = opaque;
+    s->audio_free = free;
+}
+
+static void cs_reset_voices (CSState *s, uint32_t val)
+{
+    int xtal;
+    struct audsettings as;
+
+#ifdef DEBUG_XLAW
+    if (val == 0 || val == 32)
+        val = (1 << 4) | (1 << 5);
+#endif
+
+    xtal = val & 1;
+    as.freq = freqs[xtal][(val >> 1) & 7];
+
+    if (as.freq == -1) {
+        lerr ("unsupported frequency (val=%#x)\n", val);
+        goto error;
+    }
+
+    as.nchannels = (val & (1 << 4)) ? 2 : 1;
+    as.endianness = 0;
+    s->tab = NULL;
+
+    switch ((val >> 5) & ((s->dregs[MODE_And_ID] & MODE2) ? 7 : 3)) {
+    case 0:
+        as.fmt = AUD_FMT_U8;
+        s->shift = as.nchannels == 2;
+        break;
+
+    case 1:
+        s->tab = MuLawDecompressTable;
+        goto x_law;
+    case 3:
+        s->tab = ALawDecompressTable;
+    x_law:
+        as.fmt = AUD_FMT_S16;
+        as.endianness = AUDIO_HOST_ENDIANNESS;
+        s->shift = as.nchannels == 2;
+        break;
+
+    case 6:
+        as.endianness = 1;
+    case 2:
+        as.fmt = AUD_FMT_S16;
+        s->shift = as.nchannels;
+        break;
+
+    case 7:
+    case 4:
+        lerr ("attempt to use reserved format value (%#x)\n", val);
+        goto error;
+
+    case 5:
+        lerr ("ADPCM 4 bit IMA compatible format is not supported\n");
+        goto error;
+    }
+
+    s->voice = AUD_open_out (
+        &s->card,
+        s->voice,
+        "cs4231a",
+        s,
+        cs_audio_callback,
+        &as
+        );
+
+    if (s->dregs[Interface_Configuration] & PEN) {
+        if (!s->dma_running) {
+            DMA_hold_DREQ (s->dma);
+            AUD_set_active_out (s->voice, 1);
+            s->transferred = 0;
+        }
+        s->dma_running = 1;
+    }
+    else {
+        if (s->dma_running) {
+            DMA_release_DREQ (s->dma);
+            AUD_set_active_out (s->voice, 0);
+        }
+        s->dma_running = 0;
+    }
+    return;
+
+ error:
+    if (s->dma_running) {
+        DMA_release_DREQ (s->dma);
+        AUD_set_active_out (s->voice, 0);
+    }
+}
+
+static uint64_t cs_read (void *opaque, hwaddr addr, unsigned size)
+{
+    CSState *s = opaque;
+    uint32_t saddr, iaddr, ret;
+
+    saddr = addr;
+    iaddr = ~0U;
+
+    switch (saddr) {
+    case Index_Address:
+        ret = s->regs[saddr] & ~0x80;
+        break;
+
+    case Index_Data:
+        if (!(s->dregs[MODE_And_ID] & MODE2))
+            iaddr = s->regs[Index_Address] & 0x0f;
+        else
+            iaddr = s->regs[Index_Address] & 0x1f;
+
+        ret = s->dregs[iaddr];
+        if (iaddr == Error_Status_And_Initialization) {
+            /* keep SEAL happy */
+            if (s->aci_counter) {
+                ret |= 1 << 5;
+                s->aci_counter -= 1;
+            }
+        }
+        break;
+
+    default:
+        ret = s->regs[saddr];
+        break;
+    }
+    dolog ("read %d:%d -> %d\n", saddr, iaddr, ret);
+    return ret;
+}
+
+static void cs_write (void *opaque, hwaddr addr,
+                      uint64_t val64, unsigned size)
+{
+    CSState *s = opaque;
+    uint32_t saddr, iaddr, val;
+
+    saddr = addr;
+    val = val64;
+
+    switch (saddr) {
+    case Index_Address:
+        if (!(s->regs[Index_Address] & MCE) && (val & MCE)
+            && (s->dregs[Interface_Configuration] & (3 << 3)))
+            s->aci_counter = conf.aci_counter;
+
+        s->regs[Index_Address] = val & ~(1 << 7);
+        break;
+
+    case Index_Data:
+        if (!(s->dregs[MODE_And_ID] & MODE2))
+            iaddr = s->regs[Index_Address] & 0x0f;
+        else
+            iaddr = s->regs[Index_Address] & 0x1f;
+
+        switch (iaddr) {
+        case RESERVED:
+        case RESERVED_2:
+        case RESERVED_3:
+            lwarn ("attempt to write %#x to reserved indirect register %d\n",
+                   val, iaddr);
+            break;
+
+        case FS_And_Playback_Data_Format:
+            if (s->regs[Index_Address] & MCE) {
+                cs_reset_voices (s, val);
+            }
+            else {
+                if (s->dregs[Alternate_Feature_Status] & PMCE) {
+                    val = (val & ~0x0f) | (s->dregs[iaddr] & 0x0f);
+                    cs_reset_voices (s, val);
+                }
+                else {
+                    lwarn ("[P]MCE(%#x, %#x) is not set, val=%#x\n",
+                           s->regs[Index_Address],
+                           s->dregs[Alternate_Feature_Status],
+                           val);
+                    break;
+                }
+            }
+            s->dregs[iaddr] = val;
+            break;
+
+        case Interface_Configuration:
+            val &= ~(1 << 5);   /* D5 is reserved */
+            s->dregs[iaddr] = val;
+            if (val & PPIO) {
+                lwarn ("PIO is not supported (%#x)\n", val);
+                break;
+            }
+            if (val & PEN) {
+                if (!s->dma_running) {
+                    cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]);
+                }
+            }
+            else {
+                if (s->dma_running) {
+                    DMA_release_DREQ (s->dma);
+                    AUD_set_active_out (s->voice, 0);
+                    s->dma_running = 0;
+                }
+            }
+            break;
+
+        case Error_Status_And_Initialization:
+            lwarn ("attempt to write to read only register %d\n", iaddr);
+            break;
+
+        case MODE_And_ID:
+            dolog ("val=%#x\n", val);
+            if (val & MODE2)
+                s->dregs[iaddr] |= MODE2;
+            else
+                s->dregs[iaddr] &= ~MODE2;
+            break;
+
+        case Alternate_Feature_Enable_I:
+            if (val & TE)
+                lerr ("timer is not yet supported\n");
+            s->dregs[iaddr] = val;
+            break;
+
+        case Alternate_Feature_Status:
+            if ((s->dregs[iaddr] & PI) && !(val & PI)) {
+                /* XXX: TI CI */
+                qemu_irq_lower (s->pic);
+                s->regs[Status] &= ~INT;
+            }
+            s->dregs[iaddr] = val;
+            break;
+
+        case Version_Chip_ID:
+            lwarn ("write to Version_Chip_ID register %#x\n", val);
+            s->dregs[iaddr] = val;
+            break;
+
+        default:
+            s->dregs[iaddr] = val;
+            break;
+        }
+        dolog ("written value %#x to indirect register %d\n", val, iaddr);
+        break;
+
+    case Status:
+        if (s->regs[Status] & INT) {
+            qemu_irq_lower (s->pic);
+        }
+        s->regs[Status] &= ~INT;
+        s->dregs[Alternate_Feature_Status] &= ~(PI | CI | TI);
+        break;
+
+    case PIO_Data:
+        lwarn ("attempt to write value %#x to PIO register\n", val);
+        break;
+    }
+}
+
+static int cs_write_audio (CSState *s, int nchan, int dma_pos,
+                           int dma_len, int len)
+{
+    int temp, net;
+    uint8_t tmpbuf[4096];
+
+    temp = len;
+    net = 0;
+
+    while (temp) {
+        int left = dma_len - dma_pos;
+        int copied;
+        size_t to_copy;
+
+        to_copy = audio_MIN (temp, left);
+        if (to_copy > sizeof (tmpbuf)) {
+            to_copy = sizeof (tmpbuf);
+        }
+
+        copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy);
+        if (s->tab) {
+            int i;
+            int16_t linbuf[4096];
+
+            for (i = 0; i < copied; ++i)
+                linbuf[i] = s->tab[tmpbuf[i]];
+            copied = AUD_write (s->voice, linbuf, copied << 1);
+            copied >>= 1;
+        }
+        else {
+            copied = AUD_write (s->voice, tmpbuf, copied);
+        }
+
+        temp -= copied;
+        dma_pos = (dma_pos + copied) % dma_len;
+        net += copied;
+
+        if (!copied) {
+            break;
+        }
+    }
+
+    return net;
+}
+
+static int cs_dma_read (void *opaque, int nchan, int dma_pos, int dma_len)
+{
+    CSState *s = opaque;
+    int copy, written;
+    int till = -1;
+
+    copy = s->voice ? (s->audio_free >> (s->tab != NULL)) : dma_len;
+
+    if (s->dregs[Pin_Control] & IEN) {
+        till = (s->dregs[Playback_Lower_Base_Count]
+            | (s->dregs[Playback_Upper_Base_Count] << 8)) << s->shift;
+        till -= s->transferred;
+        copy = audio_MIN (till, copy);
+    }
+
+    if ((copy <= 0) || (dma_len <= 0)) {
+        return dma_pos;
+    }
+
+    written = cs_write_audio (s, nchan, dma_pos, dma_len, copy);
+
+    dma_pos = (dma_pos + written) % dma_len;
+    s->audio_free -= (written << (s->tab != NULL));
+
+    if (written == till) {
+        s->regs[Status] |= INT;
+        s->dregs[Alternate_Feature_Status] |= PI;
+        s->transferred = 0;
+        qemu_irq_raise (s->pic);
+    }
+    else {
+        s->transferred += written;
+    }
+
+    return dma_pos;
+}
+
+static int cs4231a_pre_load (void *opaque)
+{
+    CSState *s = opaque;
+
+    if (s->dma_running) {
+        DMA_release_DREQ (s->dma);
+        AUD_set_active_out (s->voice, 0);
+    }
+    s->dma_running = 0;
+    return 0;
+}
+
+static int cs4231a_post_load (void *opaque, int version_id)
+{
+    CSState *s = opaque;
+
+    if (s->dma_running && (s->dregs[Interface_Configuration] & PEN)) {
+        s->dma_running = 0;
+        cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]);
+    }
+    return 0;
+}
+
+static const VMStateDescription vmstate_cs4231a = {
+    .name = "cs4231a",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_load = cs4231a_pre_load,
+    .post_load = cs4231a_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT32_ARRAY (regs, CSState, CS_REGS),
+        VMSTATE_BUFFER (dregs, CSState),
+        VMSTATE_INT32 (dma_running, CSState),
+        VMSTATE_INT32 (audio_free, CSState),
+        VMSTATE_INT32 (transferred, CSState),
+        VMSTATE_INT32 (aci_counter, CSState),
+        VMSTATE_END_OF_LIST ()
+    }
+};
+
+static const MemoryRegionOps cs_ioport_ops = {
+    .read = cs_read,
+    .write = cs_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    }
+};
+
+static int cs4231a_initfn (ISADevice *dev)
+{
+    CSState *s = DO_UPCAST (CSState, dev, dev);
+
+    isa_init_irq (dev, &s->pic, s->irq);
+
+    memory_region_init_io (&s->ioports, &cs_ioport_ops, s, "cs4231a", 4);
+    isa_register_ioport (dev, &s->ioports, s->port);
+
+    DMA_register_channel (s->dma, cs_dma_read, s);
+
+    qemu_register_reset (cs_reset, s);
+    cs_reset (s);
+
+    AUD_register_card ("cs4231a", &s->card);
+    return 0;
+}
+
+int cs4231a_init (ISABus *bus)
+{
+    isa_create_simple (bus, "cs4231a");
+    return 0;
+}
+
+static Property cs4231a_properties[] = {
+    DEFINE_PROP_HEX32  ("iobase",  CSState, port, 0x534),
+    DEFINE_PROP_UINT32 ("irq",     CSState, irq,  9),
+    DEFINE_PROP_UINT32 ("dma",     CSState, dma,  3),
+    DEFINE_PROP_END_OF_LIST (),
+};
+
+static void cs4231a_class_initfn (ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS (klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS (klass);
+    ic->init = cs4231a_initfn;
+    dc->desc = "Crystal Semiconductor CS4231A";
+    dc->vmsd = &vmstate_cs4231a;
+    dc->props = cs4231a_properties;
+}
+
+static const TypeInfo cs4231a_info = {
+    .name          = "cs4231a",
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof (CSState),
+    .class_init    = cs4231a_class_initfn,
+};
+
+static void cs4231a_register_types (void)
+{
+    type_register_static (&cs4231a_info);
+}
+
+type_init (cs4231a_register_types)
diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c
new file mode 100644 (file)
index 0000000..9fe5708
--- /dev/null
@@ -0,0 +1,1089 @@
+/*
+ * QEMU ES1370 emulation
+ *
+ * Copyright (c) 2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* #define DEBUG_ES1370 */
+/* #define VERBOSE_ES1370 */
+#define SILENT_ES1370
+
+#include "hw/hw.h"
+#include "hw/audio/audio.h"
+#include "audio/audio.h"
+#include "hw/pci/pci.h"
+#include "sysemu/dma.h"
+
+/* Missing stuff:
+   SCTRL_P[12](END|ST)INC
+   SCTRL_P1SCTRLD
+   SCTRL_P2DACSEN
+   CTRL_DAC_SYNC
+   MIDI
+   non looped mode
+   surely more
+*/
+
+/*
+  Following macros and samplerate array were copied verbatim from
+  Linux kernel 2.4.30: drivers/sound/es1370.c
+
+  Copyright (C) 1998-2001, 2003 Thomas Sailer (t.sailer@alumni.ethz.ch)
+*/
+
+/* Start blatant GPL violation */
+
+#define ES1370_REG_CONTROL        0x00
+#define ES1370_REG_STATUS         0x04
+#define ES1370_REG_UART_DATA      0x08
+#define ES1370_REG_UART_STATUS    0x09
+#define ES1370_REG_UART_CONTROL   0x09
+#define ES1370_REG_UART_TEST      0x0a
+#define ES1370_REG_MEMPAGE        0x0c
+#define ES1370_REG_CODEC          0x10
+#define ES1370_REG_SERIAL_CONTROL 0x20
+#define ES1370_REG_DAC1_SCOUNT    0x24
+#define ES1370_REG_DAC2_SCOUNT    0x28
+#define ES1370_REG_ADC_SCOUNT     0x2c
+
+#define ES1370_REG_DAC1_FRAMEADR    0xc30
+#define ES1370_REG_DAC1_FRAMECNT    0xc34
+#define ES1370_REG_DAC2_FRAMEADR    0xc38
+#define ES1370_REG_DAC2_FRAMECNT    0xc3c
+#define ES1370_REG_ADC_FRAMEADR     0xd30
+#define ES1370_REG_ADC_FRAMECNT     0xd34
+#define ES1370_REG_PHANTOM_FRAMEADR 0xd38
+#define ES1370_REG_PHANTOM_FRAMECNT 0xd3c
+
+static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 };
+
+#define DAC2_SRTODIV(x) (((1411200+(x)/2)/(x))-2)
+#define DAC2_DIVTOSR(x) (1411200/((x)+2))
+
+#define CTRL_ADC_STOP   0x80000000  /* 1 = ADC stopped */
+#define CTRL_XCTL1      0x40000000  /* electret mic bias */
+#define CTRL_OPEN       0x20000000  /* no function, can be read and written */
+#define CTRL_PCLKDIV    0x1fff0000  /* ADC/DAC2 clock divider */
+#define CTRL_SH_PCLKDIV 16
+#define CTRL_MSFMTSEL   0x00008000  /* MPEG serial data fmt: 0 = Sony, 1 = I2S */
+#define CTRL_M_SBB      0x00004000  /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */
+#define CTRL_WTSRSEL    0x00003000  /* DAC1 clock freq: 0=5512, 1=11025, 2=22050, 3=44100 */
+#define CTRL_SH_WTSRSEL 12
+#define CTRL_DAC_SYNC   0x00000800  /* 1 = DAC2 runs off DAC1 clock */
+#define CTRL_CCB_INTRM  0x00000400  /* 1 = CCB "voice" ints enabled */
+#define CTRL_M_CB       0x00000200  /* recording source: 0 = ADC, 1 = MPEG */
+#define CTRL_XCTL0      0x00000100  /* 0 = Line in, 1 = Line out */
+#define CTRL_BREQ       0x00000080  /* 1 = test mode (internal mem test) */
+#define CTRL_DAC1_EN    0x00000040  /* enable DAC1 */
+#define CTRL_DAC2_EN    0x00000020  /* enable DAC2 */
+#define CTRL_ADC_EN     0x00000010  /* enable ADC */
+#define CTRL_UART_EN    0x00000008  /* enable MIDI uart */
+#define CTRL_JYSTK_EN   0x00000004  /* enable Joystick port (presumably at address 0x200) */
+#define CTRL_CDC_EN     0x00000002  /* enable serial (CODEC) interface */
+#define CTRL_SERR_DIS   0x00000001  /* 1 = disable PCI SERR signal */
+
+#define STAT_INTR       0x80000000  /* wired or of all interrupt bits */
+#define STAT_CSTAT      0x00000400  /* 1 = codec busy or codec write in progress */
+#define STAT_CBUSY      0x00000200  /* 1 = codec busy */
+#define STAT_CWRIP      0x00000100  /* 1 = codec write in progress */
+#define STAT_VC         0x00000060  /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */
+#define STAT_SH_VC      5
+#define STAT_MCCB       0x00000010  /* CCB int pending */
+#define STAT_UART       0x00000008  /* UART int pending */
+#define STAT_DAC1       0x00000004  /* DAC1 int pending */
+#define STAT_DAC2       0x00000002  /* DAC2 int pending */
+#define STAT_ADC        0x00000001  /* ADC int pending */
+
+#define USTAT_RXINT     0x80        /* UART rx int pending */
+#define USTAT_TXINT     0x04        /* UART tx int pending */
+#define USTAT_TXRDY     0x02        /* UART tx ready */
+#define USTAT_RXRDY     0x01        /* UART rx ready */
+
+#define UCTRL_RXINTEN   0x80        /* 1 = enable RX ints */
+#define UCTRL_TXINTEN   0x60        /* TX int enable field mask */
+#define UCTRL_ENA_TXINT 0x20        /* enable TX int */
+#define UCTRL_CNTRL     0x03        /* control field */
+#define UCTRL_CNTRL_SWR 0x03        /* software reset command */
+
+#define SCTRL_P2ENDINC    0x00380000  /*  */
+#define SCTRL_SH_P2ENDINC 19
+#define SCTRL_P2STINC     0x00070000  /*  */
+#define SCTRL_SH_P2STINC  16
+#define SCTRL_R1LOOPSEL   0x00008000  /* 0 = loop mode */
+#define SCTRL_P2LOOPSEL   0x00004000  /* 0 = loop mode */
+#define SCTRL_P1LOOPSEL   0x00002000  /* 0 = loop mode */
+#define SCTRL_P2PAUSE     0x00001000  /* 1 = pause mode */
+#define SCTRL_P1PAUSE     0x00000800  /* 1 = pause mode */
+#define SCTRL_R1INTEN     0x00000400  /* enable interrupt */
+#define SCTRL_P2INTEN     0x00000200  /* enable interrupt */
+#define SCTRL_P1INTEN     0x00000100  /* enable interrupt */
+#define SCTRL_P1SCTRLD    0x00000080  /* reload sample count register for DAC1 */
+#define SCTRL_P2DACSEN    0x00000040  /* 1 = DAC2 play back last sample when disabled */
+#define SCTRL_R1SEB       0x00000020  /* 1 = 16bit */
+#define SCTRL_R1SMB       0x00000010  /* 1 = stereo */
+#define SCTRL_R1FMT       0x00000030  /* format mask */
+#define SCTRL_SH_R1FMT    4
+#define SCTRL_P2SEB       0x00000008  /* 1 = 16bit */
+#define SCTRL_P2SMB       0x00000004  /* 1 = stereo */
+#define SCTRL_P2FMT       0x0000000c  /* format mask */
+#define SCTRL_SH_P2FMT    2
+#define SCTRL_P1SEB       0x00000002  /* 1 = 16bit */
+#define SCTRL_P1SMB       0x00000001  /* 1 = stereo */
+#define SCTRL_P1FMT       0x00000003  /* format mask */
+#define SCTRL_SH_P1FMT    0
+
+/* End blatant GPL violation */
+
+#define NB_CHANNELS 3
+#define DAC1_CHANNEL 0
+#define DAC2_CHANNEL 1
+#define ADC_CHANNEL 2
+
+#define IO_READ_PROTO(n) \
+static uint32_t n (void *opaque, uint32_t addr)
+#define IO_WRITE_PROTO(n) \
+static void n (void *opaque, uint32_t addr, uint32_t val)
+
+static void es1370_dac1_callback (void *opaque, int free);
+static void es1370_dac2_callback (void *opaque, int free);
+static void es1370_adc_callback (void *opaque, int avail);
+
+#ifdef DEBUG_ES1370
+
+#define ldebug(...) AUD_log ("es1370", __VA_ARGS__)
+
+static void print_ctl (uint32_t val)
+{
+    char buf[1024];
+
+    buf[0] = '\0';
+#define a(n) if (val & CTRL_##n) strcat (buf, " "#n)
+    a (ADC_STOP);
+    a (XCTL1);
+    a (OPEN);
+    a (MSFMTSEL);
+    a (M_SBB);
+    a (DAC_SYNC);
+    a (CCB_INTRM);
+    a (M_CB);
+    a (XCTL0);
+    a (BREQ);
+    a (DAC1_EN);
+    a (DAC2_EN);
+    a (ADC_EN);
+    a (UART_EN);
+    a (JYSTK_EN);
+    a (CDC_EN);
+    a (SERR_DIS);
+#undef a
+    AUD_log ("es1370", "ctl - PCLKDIV %d(DAC2 freq %d), freq %d,%s\n",
+             (val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV,
+             DAC2_DIVTOSR ((val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV),
+             dac1_samplerate[(val & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL],
+             buf);
+}
+
+static void print_sctl (uint32_t val)
+{
+    static const char *fmt_names[] = {"8M", "8S", "16M", "16S"};
+    char buf[1024];
+
+    buf[0] = '\0';
+
+#define a(n) if (val & SCTRL_##n) strcat (buf, " "#n)
+#define b(n) if (!(val & SCTRL_##n)) strcat (buf, " "#n)
+    b (R1LOOPSEL);
+    b (P2LOOPSEL);
+    b (P1LOOPSEL);
+    a (P2PAUSE);
+    a (P1PAUSE);
+    a (R1INTEN);
+    a (P2INTEN);
+    a (P1INTEN);
+    a (P1SCTRLD);
+    a (P2DACSEN);
+    if (buf[0]) {
+        strcat (buf, "\n        ");
+    }
+    else {
+        buf[0] = ' ';
+        buf[1] = '\0';
+    }
+#undef b
+#undef a
+    AUD_log ("es1370",
+             "%s"
+             "p2_end_inc %d, p2_st_inc %d, r1_fmt %s, p2_fmt %s, p1_fmt %s\n",
+             buf,
+             (val & SCTRL_P2ENDINC) >> SCTRL_SH_P2ENDINC,
+             (val & SCTRL_P2STINC) >> SCTRL_SH_P2STINC,
+             fmt_names [(val >> SCTRL_SH_R1FMT) & 3],
+             fmt_names [(val >> SCTRL_SH_P2FMT) & 3],
+             fmt_names [(val >> SCTRL_SH_P1FMT) & 3]
+        );
+}
+#else
+#define ldebug(...)
+#define print_ctl(...)
+#define print_sctl(...)
+#endif
+
+#ifdef VERBOSE_ES1370
+#define dolog(...) AUD_log ("es1370", __VA_ARGS__)
+#else
+#define dolog(...)
+#endif
+
+#ifndef SILENT_ES1370
+#define lwarn(...) AUD_log ("es1370: warning", __VA_ARGS__)
+#else
+#define lwarn(...)
+#endif
+
+struct chan {
+    uint32_t shift;
+    uint32_t leftover;
+    uint32_t scount;
+    uint32_t frame_addr;
+    uint32_t frame_cnt;
+};
+
+typedef struct ES1370State {
+    PCIDevice dev;
+    QEMUSoundCard card;
+    MemoryRegion io;
+    struct chan chan[NB_CHANNELS];
+    SWVoiceOut *dac_voice[2];
+    SWVoiceIn *adc_voice;
+
+    uint32_t ctl;
+    uint32_t status;
+    uint32_t mempage;
+    uint32_t codec;
+    uint32_t sctl;
+} ES1370State;
+
+struct chan_bits {
+    uint32_t ctl_en;
+    uint32_t stat_int;
+    uint32_t sctl_pause;
+    uint32_t sctl_inten;
+    uint32_t sctl_fmt;
+    uint32_t sctl_sh_fmt;
+    uint32_t sctl_loopsel;
+    void (*calc_freq) (ES1370State *s, uint32_t ctl,
+                       uint32_t *old_freq, uint32_t *new_freq);
+};
+
+static void es1370_dac1_calc_freq (ES1370State *s, uint32_t ctl,
+                                   uint32_t *old_freq, uint32_t *new_freq);
+static void es1370_dac2_and_adc_calc_freq (ES1370State *s, uint32_t ctl,
+                                           uint32_t *old_freq,
+                                           uint32_t *new_freq);
+
+static const struct chan_bits es1370_chan_bits[] = {
+    {CTRL_DAC1_EN, STAT_DAC1, SCTRL_P1PAUSE, SCTRL_P1INTEN,
+     SCTRL_P1FMT, SCTRL_SH_P1FMT, SCTRL_P1LOOPSEL,
+     es1370_dac1_calc_freq},
+
+    {CTRL_DAC2_EN, STAT_DAC2, SCTRL_P2PAUSE, SCTRL_P2INTEN,
+     SCTRL_P2FMT, SCTRL_SH_P2FMT, SCTRL_P2LOOPSEL,
+     es1370_dac2_and_adc_calc_freq},
+
+    {CTRL_ADC_EN, STAT_ADC, 0, SCTRL_R1INTEN,
+     SCTRL_R1FMT, SCTRL_SH_R1FMT, SCTRL_R1LOOPSEL,
+     es1370_dac2_and_adc_calc_freq}
+};
+
+static void es1370_update_status (ES1370State *s, uint32_t new_status)
+{
+    uint32_t level = new_status & (STAT_DAC1 | STAT_DAC2 | STAT_ADC);
+
+    if (level) {
+        s->status = new_status | STAT_INTR;
+    }
+    else {
+        s->status = new_status & ~STAT_INTR;
+    }
+    qemu_set_irq (s->dev.irq[0], !!level);
+}
+
+static void es1370_reset (ES1370State *s)
+{
+    size_t i;
+
+    s->ctl = 1;
+    s->status = 0x60;
+    s->mempage = 0;
+    s->codec = 0;
+    s->sctl = 0;
+
+    for (i = 0; i < NB_CHANNELS; ++i) {
+        struct chan *d = &s->chan[i];
+        d->scount = 0;
+        d->leftover = 0;
+        if (i == ADC_CHANNEL) {
+            AUD_close_in (&s->card, s->adc_voice);
+            s->adc_voice = NULL;
+        }
+        else {
+            AUD_close_out (&s->card, s->dac_voice[i]);
+            s->dac_voice[i] = NULL;
+        }
+    }
+    qemu_irq_lower (s->dev.irq[0]);
+}
+
+static void es1370_maybe_lower_irq (ES1370State *s, uint32_t sctl)
+{
+    uint32_t new_status = s->status;
+
+    if (!(sctl & SCTRL_P1INTEN) && (s->sctl & SCTRL_P1INTEN)) {
+        new_status &= ~STAT_DAC1;
+    }
+
+    if (!(sctl & SCTRL_P2INTEN) && (s->sctl & SCTRL_P2INTEN)) {
+        new_status &= ~STAT_DAC2;
+    }
+
+    if (!(sctl & SCTRL_R1INTEN) && (s->sctl & SCTRL_R1INTEN)) {
+        new_status &= ~STAT_ADC;
+    }
+
+    if (new_status != s->status) {
+        es1370_update_status (s, new_status);
+    }
+}
+
+static void es1370_dac1_calc_freq (ES1370State *s, uint32_t ctl,
+                                   uint32_t *old_freq, uint32_t *new_freq)
+
+{
+    *old_freq = dac1_samplerate[(s->ctl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL];
+    *new_freq = dac1_samplerate[(ctl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL];
+}
+
+static void es1370_dac2_and_adc_calc_freq (ES1370State *s, uint32_t ctl,
+                                           uint32_t *old_freq,
+                                           uint32_t *new_freq)
+
+{
+    uint32_t old_pclkdiv, new_pclkdiv;
+
+    new_pclkdiv = (ctl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV;
+    old_pclkdiv = (s->ctl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV;
+    *new_freq = DAC2_DIVTOSR (new_pclkdiv);
+    *old_freq = DAC2_DIVTOSR (old_pclkdiv);
+}
+
+static void es1370_update_voices (ES1370State *s, uint32_t ctl, uint32_t sctl)
+{
+    size_t i;
+    uint32_t old_freq, new_freq, old_fmt, new_fmt;
+
+    for (i = 0; i < NB_CHANNELS; ++i) {
+        struct chan *d = &s->chan[i];
+        const struct chan_bits *b = &es1370_chan_bits[i];
+
+        new_fmt = (sctl & b->sctl_fmt) >> b->sctl_sh_fmt;
+        old_fmt = (s->sctl & b->sctl_fmt) >> b->sctl_sh_fmt;
+
+        b->calc_freq (s, ctl, &old_freq, &new_freq);
+
+        if ((old_fmt != new_fmt) || (old_freq != new_freq)) {
+            d->shift = (new_fmt & 1) + (new_fmt >> 1);
+            ldebug ("channel %zu, freq = %d, nchannels %d, fmt %d, shift %d\n",
+                    i,
+                    new_freq,
+                    1 << (new_fmt & 1),
+                    (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8,
+                    d->shift);
+            if (new_freq) {
+                struct audsettings as;
+
+                as.freq = new_freq;
+                as.nchannels = 1 << (new_fmt & 1);
+                as.fmt = (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8;
+                as.endianness = 0;
+
+                if (i == ADC_CHANNEL) {
+                    s->adc_voice =
+                        AUD_open_in (
+                            &s->card,
+                            s->adc_voice,
+                            "es1370.adc",
+                            s,
+                            es1370_adc_callback,
+                            &as
+                            );
+                }
+                else {
+                    s->dac_voice[i] =
+                        AUD_open_out (
+                            &s->card,
+                            s->dac_voice[i],
+                            i ? "es1370.dac2" : "es1370.dac1",
+                            s,
+                            i ? es1370_dac2_callback : es1370_dac1_callback,
+                            &as
+                            );
+                }
+            }
+        }
+
+        if (((ctl ^ s->ctl) & b->ctl_en)
+            || ((sctl ^ s->sctl) & b->sctl_pause)) {
+            int on = (ctl & b->ctl_en) && !(sctl & b->sctl_pause);
+
+            if (i == ADC_CHANNEL) {
+                AUD_set_active_in (s->adc_voice, on);
+            }
+            else {
+                AUD_set_active_out (s->dac_voice[i], on);
+            }
+        }
+    }
+
+    s->ctl = ctl;
+    s->sctl = sctl;
+}
+
+static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr)
+{
+    addr &= 0xff;
+    if (addr >= 0x30 && addr <= 0x3f)
+        addr |= s->mempage << 8;
+    return addr;
+}
+
+IO_WRITE_PROTO (es1370_writeb)
+{
+    ES1370State *s = opaque;
+    uint32_t shift, mask;
+
+    addr = es1370_fixup (s, addr);
+
+    switch (addr) {
+    case ES1370_REG_CONTROL:
+    case ES1370_REG_CONTROL + 1:
+    case ES1370_REG_CONTROL + 2:
+    case ES1370_REG_CONTROL + 3:
+        shift = (addr - ES1370_REG_CONTROL) << 3;
+        mask = 0xff << shift;
+        val = (s->ctl & ~mask) | ((val & 0xff) << shift);
+        es1370_update_voices (s, val, s->sctl);
+        print_ctl (val);
+        break;
+    case ES1370_REG_MEMPAGE:
+        s->mempage = val;
+        break;
+    case ES1370_REG_SERIAL_CONTROL:
+    case ES1370_REG_SERIAL_CONTROL + 1:
+    case ES1370_REG_SERIAL_CONTROL + 2:
+    case ES1370_REG_SERIAL_CONTROL + 3:
+        shift = (addr - ES1370_REG_SERIAL_CONTROL) << 3;
+        mask = 0xff << shift;
+        val = (s->sctl & ~mask) | ((val & 0xff) << shift);
+        es1370_maybe_lower_irq (s, val);
+        es1370_update_voices (s, s->ctl, val);
+        print_sctl (val);
+        break;
+    default:
+        lwarn ("writeb %#x <- %#x\n", addr, val);
+        break;
+    }
+}
+
+IO_WRITE_PROTO (es1370_writew)
+{
+    ES1370State *s = opaque;
+    addr = es1370_fixup (s, addr);
+    uint32_t shift, mask;
+    struct chan *d = &s->chan[0];
+
+    switch (addr) {
+    case ES1370_REG_CODEC:
+        dolog ("ignored codec write address %#x, data %#x\n",
+               (val >> 8) & 0xff, val & 0xff);
+        s->codec = val;
+        break;
+
+    case ES1370_REG_CONTROL:
+    case ES1370_REG_CONTROL + 2:
+        shift = (addr != ES1370_REG_CONTROL) << 4;
+        mask = 0xffff << shift;
+        val = (s->ctl & ~mask) | ((val & 0xffff) << shift);
+        es1370_update_voices (s, val, s->sctl);
+        print_ctl (val);
+        break;
+
+    case ES1370_REG_ADC_SCOUNT:
+        d++;
+    case ES1370_REG_DAC2_SCOUNT:
+        d++;
+    case ES1370_REG_DAC1_SCOUNT:
+        d->scount = (d->scount & ~0xffff) | (val & 0xffff);
+        break;
+
+    default:
+        lwarn ("writew %#x <- %#x\n", addr, val);
+        break;
+    }
+}
+
+IO_WRITE_PROTO (es1370_writel)
+{
+    ES1370State *s = opaque;
+    struct chan *d = &s->chan[0];
+
+    addr = es1370_fixup (s, addr);
+
+    switch (addr) {
+    case ES1370_REG_CONTROL:
+        es1370_update_voices (s, val, s->sctl);
+        print_ctl (val);
+        break;
+
+    case ES1370_REG_MEMPAGE:
+        s->mempage = val & 0xf;
+        break;
+
+    case ES1370_REG_SERIAL_CONTROL:
+        es1370_maybe_lower_irq (s, val);
+        es1370_update_voices (s, s->ctl, val);
+        print_sctl (val);
+        break;
+
+    case ES1370_REG_ADC_SCOUNT:
+        d++;
+    case ES1370_REG_DAC2_SCOUNT:
+        d++;
+    case ES1370_REG_DAC1_SCOUNT:
+        d->scount = (val & 0xffff) | (d->scount & ~0xffff);
+        ldebug ("chan %td CURR_SAMP_CT %d, SAMP_CT %d\n",
+                d - &s->chan[0], val >> 16, (val & 0xffff));
+        break;
+
+    case ES1370_REG_ADC_FRAMEADR:
+        d++;
+    case ES1370_REG_DAC2_FRAMEADR:
+        d++;
+    case ES1370_REG_DAC1_FRAMEADR:
+        d->frame_addr = val;
+        ldebug ("chan %td frame address %#x\n", d - &s->chan[0], val);
+        break;
+
+    case ES1370_REG_PHANTOM_FRAMECNT:
+        lwarn ("writing to phantom frame count %#x\n", val);
+        break;
+    case ES1370_REG_PHANTOM_FRAMEADR:
+        lwarn ("writing to phantom frame address %#x\n", val);
+        break;
+
+    case ES1370_REG_ADC_FRAMECNT:
+        d++;
+    case ES1370_REG_DAC2_FRAMECNT:
+        d++;
+    case ES1370_REG_DAC1_FRAMECNT:
+        d->frame_cnt = val;
+        d->leftover = 0;
+        ldebug ("chan %td frame count %d, buffer size %d\n",
+                d - &s->chan[0], val >> 16, val & 0xffff);
+        break;
+
+    default:
+        lwarn ("writel %#x <- %#x\n", addr, val);
+        break;
+    }
+}
+
+IO_READ_PROTO (es1370_readb)
+{
+    ES1370State *s = opaque;
+    uint32_t val;
+
+    addr = es1370_fixup (s, addr);
+
+    switch (addr) {
+    case 0x1b:                  /* Legacy */
+        lwarn ("Attempt to read from legacy register\n");
+        val = 5;
+        break;
+    case ES1370_REG_MEMPAGE:
+        val = s->mempage;
+        break;
+    case ES1370_REG_CONTROL + 0:
+    case ES1370_REG_CONTROL + 1:
+    case ES1370_REG_CONTROL + 2:
+    case ES1370_REG_CONTROL + 3:
+        val = s->ctl >> ((addr - ES1370_REG_CONTROL) << 3);
+        break;
+    case ES1370_REG_STATUS + 0:
+    case ES1370_REG_STATUS + 1:
+    case ES1370_REG_STATUS + 2:
+    case ES1370_REG_STATUS + 3:
+        val = s->status >> ((addr - ES1370_REG_STATUS) << 3);
+        break;
+    default:
+        val = ~0;
+        lwarn ("readb %#x -> %#x\n", addr, val);
+        break;
+    }
+    return val;
+}
+
+IO_READ_PROTO (es1370_readw)
+{
+    ES1370State *s = opaque;
+    struct chan *d = &s->chan[0];
+    uint32_t val;
+
+    addr = es1370_fixup (s, addr);
+
+    switch (addr) {
+    case ES1370_REG_ADC_SCOUNT + 2:
+        d++;
+    case ES1370_REG_DAC2_SCOUNT + 2:
+        d++;
+    case ES1370_REG_DAC1_SCOUNT + 2:
+        val = d->scount >> 16;
+        break;
+
+    case ES1370_REG_ADC_FRAMECNT:
+        d++;
+    case ES1370_REG_DAC2_FRAMECNT:
+        d++;
+    case ES1370_REG_DAC1_FRAMECNT:
+        val = d->frame_cnt & 0xffff;
+        break;
+
+    case ES1370_REG_ADC_FRAMECNT + 2:
+        d++;
+    case ES1370_REG_DAC2_FRAMECNT + 2:
+        d++;
+    case ES1370_REG_DAC1_FRAMECNT + 2:
+        val = d->frame_cnt >> 16;
+        break;
+
+    default:
+        val = ~0;
+        lwarn ("readw %#x -> %#x\n", addr, val);
+        break;
+    }
+
+    return val;
+}
+
+IO_READ_PROTO (es1370_readl)
+{
+    ES1370State *s = opaque;
+    uint32_t val;
+    struct chan *d = &s->chan[0];
+
+    addr = es1370_fixup (s, addr);
+
+    switch (addr) {
+    case ES1370_REG_CONTROL:
+        val = s->ctl;
+        break;
+    case ES1370_REG_STATUS:
+        val = s->status;
+        break;
+    case ES1370_REG_MEMPAGE:
+        val = s->mempage;
+        break;
+    case ES1370_REG_CODEC:
+        val = s->codec;
+        break;
+    case ES1370_REG_SERIAL_CONTROL:
+        val = s->sctl;
+        break;
+
+    case ES1370_REG_ADC_SCOUNT:
+        d++;
+    case ES1370_REG_DAC2_SCOUNT:
+        d++;
+    case ES1370_REG_DAC1_SCOUNT:
+        val = d->scount;
+#ifdef DEBUG_ES1370
+        {
+            uint32_t curr_count = d->scount >> 16;
+            uint32_t count = d->scount & 0xffff;
+
+            curr_count <<= d->shift;
+            count <<= d->shift;
+            dolog ("read scount curr %d, total %d\n", curr_count, count);
+        }
+#endif
+        break;
+
+    case ES1370_REG_ADC_FRAMECNT:
+        d++;
+    case ES1370_REG_DAC2_FRAMECNT:
+        d++;
+    case ES1370_REG_DAC1_FRAMECNT:
+        val = d->frame_cnt;
+#ifdef DEBUG_ES1370
+        {
+            uint32_t size = ((d->frame_cnt & 0xffff) + 1) << 2;
+            uint32_t curr = ((d->frame_cnt >> 16) + 1) << 2;
+            if (curr > size) {
+                dolog ("read framecnt curr %d, size %d %d\n", curr, size,
+                       curr > size);
+            }
+        }
+#endif
+        break;
+
+    case ES1370_REG_ADC_FRAMEADR:
+        d++;
+    case ES1370_REG_DAC2_FRAMEADR:
+        d++;
+    case ES1370_REG_DAC1_FRAMEADR:
+        val = d->frame_addr;
+        break;
+
+    case ES1370_REG_PHANTOM_FRAMECNT:
+        val = ~0U;
+        lwarn ("reading from phantom frame count\n");
+        break;
+    case ES1370_REG_PHANTOM_FRAMEADR:
+        val = ~0U;
+        lwarn ("reading from phantom frame address\n");
+        break;
+
+    default:
+        val = ~0U;
+        lwarn ("readl %#x -> %#x\n", addr, val);
+        break;
+    }
+    return val;
+}
+
+static void es1370_transfer_audio (ES1370State *s, struct chan *d, int loop_sel,
+                                   int max, int *irq)
+{
+    uint8_t tmpbuf[4096];
+    uint32_t addr = d->frame_addr;
+    int sc = d->scount & 0xffff;
+    int csc = d->scount >> 16;
+    int csc_bytes = (csc + 1) << d->shift;
+    int cnt = d->frame_cnt >> 16;
+    int size = d->frame_cnt & 0xffff;
+    int left = ((size - cnt + 1) << 2) + d->leftover;
+    int transferred = 0;
+    int temp = audio_MIN (max, audio_MIN (left, csc_bytes));
+    int index = d - &s->chan[0];
+
+    addr += (cnt << 2) + d->leftover;
+
+    if (index == ADC_CHANNEL) {
+        while (temp) {
+            int acquired, to_copy;
+
+            to_copy = audio_MIN ((size_t) temp, sizeof (tmpbuf));
+            acquired = AUD_read (s->adc_voice, tmpbuf, to_copy);
+            if (!acquired)
+                break;
+
+            pci_dma_write (&s->dev, addr, tmpbuf, acquired);
+
+            temp -= acquired;
+            addr += acquired;
+            transferred += acquired;
+        }
+    }
+    else {
+        SWVoiceOut *voice = s->dac_voice[index];
+
+        while (temp) {
+            int copied, to_copy;
+
+            to_copy = audio_MIN ((size_t) temp, sizeof (tmpbuf));
+            pci_dma_read (&s->dev, addr, tmpbuf, to_copy);
+            copied = AUD_write (voice, tmpbuf, to_copy);
+            if (!copied)
+                break;
+            temp -= copied;
+            addr += copied;
+            transferred += copied;
+        }
+    }
+
+    if (csc_bytes == transferred) {
+        *irq = 1;
+        d->scount = sc | (sc << 16);
+        ldebug ("sc = %d, rate = %f\n",
+                (sc + 1) << d->shift,
+                (sc + 1) / (double) 44100);
+    }
+    else {
+        *irq = 0;
+        d->scount = sc | (((csc_bytes - transferred - 1) >> d->shift) << 16);
+    }
+
+    cnt += (transferred + d->leftover) >> 2;
+
+    if (s->sctl & loop_sel) {
+        /* Bah, how stupid is that having a 0 represent true value?
+           i just spent few hours on this shit */
+        AUD_log ("es1370: warning", "non looping mode\n");
+    }
+    else {
+        d->frame_cnt = size;
+
+        if ((uint32_t) cnt <= d->frame_cnt)
+            d->frame_cnt |= cnt << 16;
+    }
+
+    d->leftover = (transferred + d->leftover) & 3;
+}
+
+static void es1370_run_channel (ES1370State *s, size_t chan, int free_or_avail)
+{
+    uint32_t new_status = s->status;
+    int max_bytes, irq;
+    struct chan *d = &s->chan[chan];
+    const struct chan_bits *b = &es1370_chan_bits[chan];
+
+    if (!(s->ctl & b->ctl_en) || (s->sctl & b->sctl_pause)) {
+        return;
+    }
+
+    max_bytes = free_or_avail;
+    max_bytes &= ~((1 << d->shift) - 1);
+    if (!max_bytes) {
+        return;
+    }
+
+    es1370_transfer_audio (s, d, b->sctl_loopsel, max_bytes, &irq);
+
+    if (irq) {
+        if (s->sctl & b->sctl_inten) {
+            new_status |= b->stat_int;
+        }
+    }
+
+    if (new_status != s->status) {
+        es1370_update_status (s, new_status);
+    }
+}
+
+static void es1370_dac1_callback (void *opaque, int free)
+{
+    ES1370State *s = opaque;
+
+    es1370_run_channel (s, DAC1_CHANNEL, free);
+}
+
+static void es1370_dac2_callback (void *opaque, int free)
+{
+    ES1370State *s = opaque;
+
+    es1370_run_channel (s, DAC2_CHANNEL, free);
+}
+
+static void es1370_adc_callback (void *opaque, int avail)
+{
+    ES1370State *s = opaque;
+
+    es1370_run_channel (s, ADC_CHANNEL, avail);
+}
+
+static uint64_t es1370_read(void *opaque, hwaddr addr,
+                            unsigned size)
+{
+    switch (size) {
+    case 1:
+        return es1370_readb(opaque, addr);
+    case 2:
+        return es1370_readw(opaque, addr);
+    case 4:
+        return es1370_readl(opaque, addr);
+    default:
+        return -1;
+    }
+}
+
+static void es1370_write(void *opaque, hwaddr addr, uint64_t val,
+                      unsigned size)
+{
+    switch (size) {
+    case 1:
+        es1370_writeb(opaque, addr, val);
+        break;
+    case 2:
+        es1370_writew(opaque, addr, val);
+        break;
+    case 4:
+        es1370_writel(opaque, addr, val);
+        break;
+    }
+}
+
+static const MemoryRegionOps es1370_io_ops = {
+    .read = es1370_read,
+    .write = es1370_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_es1370_channel = {
+    .name = "es1370_channel",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT32 (shift, struct chan),
+        VMSTATE_UINT32 (leftover, struct chan),
+        VMSTATE_UINT32 (scount, struct chan),
+        VMSTATE_UINT32 (frame_addr, struct chan),
+        VMSTATE_UINT32 (frame_cnt, struct chan),
+        VMSTATE_END_OF_LIST ()
+    }
+};
+
+static int es1370_post_load (void *opaque, int version_id)
+{
+    uint32_t ctl, sctl;
+    ES1370State *s = opaque;
+    size_t i;
+
+    for (i = 0; i < NB_CHANNELS; ++i) {
+        if (i == ADC_CHANNEL) {
+            if (s->adc_voice) {
+                AUD_close_in (&s->card, s->adc_voice);
+                s->adc_voice = NULL;
+            }
+        }
+        else {
+            if (s->dac_voice[i]) {
+                AUD_close_out (&s->card, s->dac_voice[i]);
+                s->dac_voice[i] = NULL;
+            }
+        }
+    }
+
+    ctl = s->ctl;
+    sctl = s->sctl;
+    s->ctl = 0;
+    s->sctl = 0;
+    es1370_update_voices (s, ctl, sctl);
+    return 0;
+}
+
+static const VMStateDescription vmstate_es1370 = {
+    .name = "es1370",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .post_load = es1370_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_PCI_DEVICE (dev, ES1370State),
+        VMSTATE_STRUCT_ARRAY (chan, ES1370State, NB_CHANNELS, 2,
+                              vmstate_es1370_channel, struct chan),
+        VMSTATE_UINT32 (ctl, ES1370State),
+        VMSTATE_UINT32 (status, ES1370State),
+        VMSTATE_UINT32 (mempage, ES1370State),
+        VMSTATE_UINT32 (codec, ES1370State),
+        VMSTATE_UINT32 (sctl, ES1370State),
+        VMSTATE_END_OF_LIST ()
+    }
+};
+
+static void es1370_on_reset (void *opaque)
+{
+    ES1370State *s = opaque;
+    es1370_reset (s);
+}
+
+static int es1370_initfn (PCIDevice *dev)
+{
+    ES1370State *s = DO_UPCAST (ES1370State, dev, dev);
+    uint8_t *c = s->dev.config;
+
+    c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_SLOW >> 8;
+
+#if 0
+    c[PCI_CAPABILITY_LIST] = 0xdc;
+    c[PCI_INTERRUPT_LINE] = 10;
+    c[0xdc] = 0x00;
+#endif
+
+    c[PCI_INTERRUPT_PIN] = 1;
+    c[PCI_MIN_GNT] = 0x0c;
+    c[PCI_MAX_LAT] = 0x80;
+
+    memory_region_init_io (&s->io, &es1370_io_ops, s, "es1370", 256);
+    pci_register_bar (&s->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io);
+    qemu_register_reset (es1370_on_reset, s);
+
+    AUD_register_card ("es1370", &s->card);
+    es1370_reset (s);
+    return 0;
+}
+
+static void es1370_exitfn (PCIDevice *dev)
+{
+    ES1370State *s = DO_UPCAST (ES1370State, dev, dev);
+
+    memory_region_destroy (&s->io);
+}
+
+int es1370_init (PCIBus *bus)
+{
+    pci_create_simple (bus, -1, "ES1370");
+    return 0;
+}
+
+static void es1370_class_init (ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS (klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS (klass);
+
+    k->init = es1370_initfn;
+    k->exit = es1370_exitfn;
+    k->vendor_id = PCI_VENDOR_ID_ENSONIQ;
+    k->device_id = PCI_DEVICE_ID_ENSONIQ_ES1370;
+    k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO;
+    k->subsystem_vendor_id = 0x4942;
+    k->subsystem_id = 0x4c4c;
+    dc->desc = "ENSONIQ AudioPCI ES1370";
+    dc->vmsd = &vmstate_es1370;
+}
+
+static const TypeInfo es1370_info = {
+    .name          = "ES1370",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof (ES1370State),
+    .class_init    = es1370_class_init,
+};
+
+static void es1370_register_types (void)
+{
+    type_register_static (&es1370_info);
+}
+
+type_init (es1370_register_types)
+
diff --git a/hw/audio/fmopl.c b/hw/audio/fmopl.c
new file mode 100644 (file)
index 0000000..e50ba6c
--- /dev/null
@@ -0,0 +1,1395 @@
+/*
+**
+** File: fmopl.c -- software implementation of FM sound generator
+**
+** Copyright (C) 1999,2000 Tatsuyuki Satoh , MultiArcadeMachineEmurator development
+**
+** Version 0.37a
+**
+*/
+
+/*
+       preliminary :
+       Problem :
+       note:
+*/
+
+/* This version of fmopl.c is a fork of the MAME one, relicensed under the LGPL.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define INLINE         static inline
+#define HAS_YM3812     1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <math.h>
+//#include "driver.h"          /* use M.A.M.E. */
+#include "hw/fmopl.h"
+
+#ifndef PI
+#define PI 3.14159265358979323846
+#endif
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+/* -------------------- for debug --------------------- */
+/* #define OPL_OUTPUT_LOG */
+#ifdef OPL_OUTPUT_LOG
+static FILE *opl_dbg_fp = NULL;
+static FM_OPL *opl_dbg_opl[16];
+static int opl_dbg_maxchip,opl_dbg_chip;
+#endif
+
+/* -------------------- preliminary define section --------------------- */
+/* attack/decay rate time rate */
+#define OPL_ARRATE     141280  /* RATE 4 =  2826.24ms @ 3.6MHz */
+#define OPL_DRRATE    1956000  /* RATE 4 = 39280.64ms @ 3.6MHz */
+
+#define DELTAT_MIXING_LEVEL (1) /* DELTA-T ADPCM MIXING LEVEL */
+
+#define FREQ_BITS 24                   /* frequency turn          */
+
+/* counter bits = 20 , octerve 7 */
+#define FREQ_RATE   (1<<(FREQ_BITS-20))
+#define TL_BITS    (FREQ_BITS+2)
+
+/* final output shift , limit minimum and maximum */
+#define OPL_OUTSB   (TL_BITS+3-16)             /* OPL output final shift 16bit */
+#define OPL_MAXOUT (0x7fff<<OPL_OUTSB)
+#define OPL_MINOUT (-0x8000<<OPL_OUTSB)
+
+/* -------------------- quality selection --------------------- */
+
+/* sinwave entries */
+/* used static memory = SIN_ENT * 4 (byte) */
+#define SIN_ENT 2048
+
+/* output level entries (envelope,sinwave) */
+/* envelope counter lower bits */
+#define ENV_BITS 16
+/* envelope output entries */
+#define EG_ENT   4096
+/* used dynamic memory = EG_ENT*4*4(byte)or EG_ENT*6*4(byte) */
+/* used static  memory = EG_ENT*4 (byte)                     */
+
+#define EG_OFF   ((2*EG_ENT)<<ENV_BITS)  /* OFF          */
+#define EG_DED   EG_OFF
+#define EG_DST   (EG_ENT<<ENV_BITS)      /* DECAY  START */
+#define EG_AED   EG_DST
+#define EG_AST   0                       /* ATTACK START */
+
+#define EG_STEP (96.0/EG_ENT) /* OPL is 0.1875 dB step  */
+
+/* LFO table entries */
+#define VIB_ENT 512
+#define VIB_SHIFT (32-9)
+#define AMS_ENT 512
+#define AMS_SHIFT (32-9)
+
+#define VIB_RATE 256
+
+/* -------------------- local defines , macros --------------------- */
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* envelope phase */
+#define ENV_MOD_RR  0x00
+#define ENV_MOD_DR  0x01
+#define ENV_MOD_AR  0x02
+
+/* -------------------- tables --------------------- */
+static const int slot_array[32]=
+{
+        0, 2, 4, 1, 3, 5,-1,-1,
+        6, 8,10, 7, 9,11,-1,-1,
+       12,14,16,13,15,17,-1,-1,
+       -1,-1,-1,-1,-1,-1,-1,-1
+};
+
+/* key scale level */
+/* table is 3dB/OCT , DV converts this in TL step at 6dB/OCT */
+#define DV (EG_STEP/2)
+static const UINT32 KSL_TABLE[8*16]=
+{
+       /* OCT 0 */
+        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+       /* OCT 1 */
+        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+        0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV,
+        1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV,
+       /* OCT 2 */
+        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+        0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV,
+        3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV,
+        4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV,
+       /* OCT 3 */
+        0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV,
+        3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV,
+        6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV,
+        7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV,
+       /* OCT 4 */
+        0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV,
+        6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV,
+        9.000/DV, 9.750/DV,10.125/DV,10.500/DV,
+       10.875/DV,11.250/DV,11.625/DV,12.000/DV,
+       /* OCT 5 */
+        0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV,
+        9.000/DV,10.125/DV,10.875/DV,11.625/DV,
+       12.000/DV,12.750/DV,13.125/DV,13.500/DV,
+       13.875/DV,14.250/DV,14.625/DV,15.000/DV,
+       /* OCT 6 */
+        0.000/DV, 6.000/DV, 9.000/DV,10.875/DV,
+       12.000/DV,13.125/DV,13.875/DV,14.625/DV,
+       15.000/DV,15.750/DV,16.125/DV,16.500/DV,
+       16.875/DV,17.250/DV,17.625/DV,18.000/DV,
+       /* OCT 7 */
+        0.000/DV, 9.000/DV,12.000/DV,13.875/DV,
+       15.000/DV,16.125/DV,16.875/DV,17.625/DV,
+       18.000/DV,18.750/DV,19.125/DV,19.500/DV,
+       19.875/DV,20.250/DV,20.625/DV,21.000/DV
+};
+#undef DV
+
+/* sustain lebel table (3db per step) */
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+#define SC(db) (db*((3/EG_STEP)*(1<<ENV_BITS)))+EG_DST
+static const INT32 SL_TABLE[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+#define TL_MAX (EG_ENT*2) /* limit(tl + ksr + envelope) + sinwave */
+/* TotalLevel : 48 24 12  6  3 1.5 0.75 (dB) */
+/* TL_TABLE[ 0      to TL_MAX          ] : plus  section */
+/* TL_TABLE[ TL_MAX to TL_MAX+TL_MAX-1 ] : minus section */
+static INT32 *TL_TABLE;
+
+/* pointers to TL_TABLE with sinwave output offset */
+static INT32 **SIN_TABLE;
+
+/* LFO table */
+static INT32 *AMS_TABLE;
+static INT32 *VIB_TABLE;
+
+/* envelope output curve table */
+/* attack + decay + OFF */
+static INT32 ENV_CURVE[2*EG_ENT+1];
+
+/* multiple table */
+#define ML 2
+static const UINT32 MUL_TABLE[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */
+   0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML,
+   8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML
+};
+#undef ML
+
+/* dummy attack / decay rate ( when rate == 0 ) */
+static INT32 RATE_0[16]=
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+/* -------------------- static state --------------------- */
+
+/* lock level of common table */
+static int num_lock = 0;
+
+/* work table */
+static void *cur_chip = NULL;  /* current chip point */
+/* currenct chip state */
+/* static OPLSAMPLE  *bufL,*bufR; */
+static OPL_CH *S_CH;
+static OPL_CH *E_CH;
+OPL_SLOT *SLOT7_1,*SLOT7_2,*SLOT8_1,*SLOT8_2;
+
+static INT32 outd[1];
+static INT32 ams;
+static INT32 vib;
+INT32  *ams_table;
+INT32  *vib_table;
+static INT32 amsIncr;
+static INT32 vibIncr;
+static INT32 feedback2;                /* connect for SLOT 2 */
+
+/* log output level */
+#define LOG_ERR  3      /* ERROR       */
+#define LOG_WAR  2      /* WARNING     */
+#define LOG_INF  1      /* INFORMATION */
+
+//#define LOG_LEVEL LOG_INF
+#define LOG_LEVEL      LOG_ERR
+
+//#define LOG(n,x) if( (n)>=LOG_LEVEL ) logerror x
+#define LOG(n,x)
+
+/* --------------------- subroutines  --------------------- */
+
+INLINE int Limit( int val, int max, int min ) {
+       if ( val > max )
+               val = max;
+       else if ( val < min )
+               val = min;
+
+       return val;
+}
+
+/* status set and IRQ handling */
+INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag)
+{
+       /* set status flag */
+       OPL->status |= flag;
+       if(!(OPL->status & 0x80))
+       {
+               if(OPL->status & OPL->statusmask)
+               {       /* IRQ on */
+                       OPL->status |= 0x80;
+                       /* callback user interrupt handler (IRQ is OFF to ON) */
+                       if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1);
+               }
+       }
+}
+
+/* status reset and IRQ handling */
+INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag)
+{
+       /* reset status flag */
+       OPL->status &=~flag;
+       if((OPL->status & 0x80))
+       {
+               if (!(OPL->status & OPL->statusmask) )
+               {
+                       OPL->status &= 0x7f;
+                       /* callback user interrupt handler (IRQ is ON to OFF) */
+                       if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0);
+               }
+       }
+}
+
+/* IRQ mask set */
+INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag)
+{
+       OPL->statusmask = flag;
+       /* IRQ handling check */
+       OPL_STATUS_SET(OPL,0);
+       OPL_STATUS_RESET(OPL,0);
+}
+
+/* ----- key on  ----- */
+INLINE void OPL_KEYON(OPL_SLOT *SLOT)
+{
+       /* sin wave restart */
+       SLOT->Cnt = 0;
+       /* set attack */
+       SLOT->evm = ENV_MOD_AR;
+       SLOT->evs = SLOT->evsa;
+       SLOT->evc = EG_AST;
+       SLOT->eve = EG_AED;
+}
+/* ----- key off ----- */
+INLINE void OPL_KEYOFF(OPL_SLOT *SLOT)
+{
+       if( SLOT->evm > ENV_MOD_RR)
+       {
+               /* set envelope counter from envleope output */
+               SLOT->evm = ENV_MOD_RR;
+               if( !(SLOT->evc&EG_DST) )
+                       //SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<<ENV_BITS) + EG_DST;
+                       SLOT->evc = EG_DST;
+               SLOT->eve = EG_DED;
+               SLOT->evs = SLOT->evsr;
+       }
+}
+
+/* ---------- calcrate Envelope Generator & Phase Generator ---------- */
+/* return : envelope output */
+INLINE UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT )
+{
+       /* calcrate envelope generator */
+       if( (SLOT->evc+=SLOT->evs) >= SLOT->eve )
+       {
+               switch( SLOT->evm ){
+               case ENV_MOD_AR: /* ATTACK -> DECAY1 */
+                       /* next DR */
+                       SLOT->evm = ENV_MOD_DR;
+                       SLOT->evc = EG_DST;
+                       SLOT->eve = SLOT->SL;
+                       SLOT->evs = SLOT->evsd;
+                       break;
+               case ENV_MOD_DR: /* DECAY -> SL or RR */
+                       SLOT->evc = SLOT->SL;
+                       SLOT->eve = EG_DED;
+                       if(SLOT->eg_typ)
+                       {
+                               SLOT->evs = 0;
+                       }
+                       else
+                       {
+                               SLOT->evm = ENV_MOD_RR;
+                               SLOT->evs = SLOT->evsr;
+                       }
+                       break;
+               case ENV_MOD_RR: /* RR -> OFF */
+                       SLOT->evc = EG_OFF;
+                       SLOT->eve = EG_OFF+1;
+                       SLOT->evs = 0;
+                       break;
+               }
+       }
+       /* calcrate envelope */
+       return SLOT->TLL+ENV_CURVE[SLOT->evc>>ENV_BITS]+(SLOT->ams ? ams : 0);
+}
+
+/* set algorithm connection */
+static void set_algorithm( OPL_CH *CH)
+{
+       INT32 *carrier = &outd[0];
+       CH->connect1 = CH->CON ? carrier : &feedback2;
+       CH->connect2 = carrier;
+}
+
+/* ---------- frequency counter for operater update ---------- */
+INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT)
+{
+       int ksr;
+
+       /* frequency step counter */
+       SLOT->Incr = CH->fc * SLOT->mul;
+       ksr = CH->kcode >> SLOT->KSR;
+
+       if( SLOT->ksr != ksr )
+       {
+               SLOT->ksr = ksr;
+               /* attack , decay rate recalcration */
+               SLOT->evsa = SLOT->AR[ksr];
+               SLOT->evsd = SLOT->DR[ksr];
+               SLOT->evsr = SLOT->RR[ksr];
+       }
+       SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+}
+
+/* set multi,am,vib,EG-TYP,KSR,mul */
+INLINE void set_mul(FM_OPL *OPL,int slot,int v)
+{
+       OPL_CH   *CH   = &OPL->P_CH[slot/2];
+       OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+       SLOT->mul    = MUL_TABLE[v&0x0f];
+       SLOT->KSR    = (v&0x10) ? 0 : 2;
+       SLOT->eg_typ = (v&0x20)>>5;
+       SLOT->vib    = (v&0x40);
+       SLOT->ams    = (v&0x80);
+       CALC_FCSLOT(CH,SLOT);
+}
+
+/* set ksl & tl */
+INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v)
+{
+       OPL_CH   *CH   = &OPL->P_CH[slot/2];
+       OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+       int ksl = v>>6; /* 0 / 1.5 / 3 / 6 db/OCT */
+
+       SLOT->ksl = ksl ? 3-ksl : 31;
+       SLOT->TL  = (v&0x3f)*(0.75/EG_STEP); /* 0.75db step */
+
+       if( !(OPL->mode&0x80) )
+       {       /* not CSM latch total level */
+               SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+       }
+}
+
+/* set attack rate & decay rate  */
+INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v)
+{
+       OPL_CH   *CH   = &OPL->P_CH[slot/2];
+       OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+       int ar = v>>4;
+       int dr = v&0x0f;
+
+       SLOT->AR = ar ? &OPL->AR_TABLE[ar<<2] : RATE_0;
+       SLOT->evsa = SLOT->AR[SLOT->ksr];
+       if( SLOT->evm == ENV_MOD_AR ) SLOT->evs = SLOT->evsa;
+
+       SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0;
+       SLOT->evsd = SLOT->DR[SLOT->ksr];
+       if( SLOT->evm == ENV_MOD_DR ) SLOT->evs = SLOT->evsd;
+}
+
+/* set sustain level & release rate */
+INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v)
+{
+       OPL_CH   *CH   = &OPL->P_CH[slot/2];
+       OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+       int sl = v>>4;
+       int rr = v & 0x0f;
+
+       SLOT->SL = SL_TABLE[sl];
+       if( SLOT->evm == ENV_MOD_DR ) SLOT->eve = SLOT->SL;
+       SLOT->RR = &OPL->DR_TABLE[rr<<2];
+       SLOT->evsr = SLOT->RR[SLOT->ksr];
+       if( SLOT->evm == ENV_MOD_RR ) SLOT->evs = SLOT->evsr;
+}
+
+/* operator output calcrator */
+#define OP_OUT(slot,env,con)   slot->wavetable[((slot->Cnt+con)/(0x1000000/SIN_ENT))&(SIN_ENT-1)][env]
+/* ---------- calcrate one of channel ---------- */
+INLINE void OPL_CALC_CH( OPL_CH *CH )
+{
+       UINT32 env_out;
+       OPL_SLOT *SLOT;
+
+       feedback2 = 0;
+       /* SLOT 1 */
+       SLOT = &CH->SLOT[SLOT1];
+       env_out=OPL_CALC_SLOT(SLOT);
+       if( env_out < EG_ENT-1 )
+       {
+               /* PG */
+               if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+               else          SLOT->Cnt += SLOT->Incr;
+               /* connectoion */
+               if(CH->FB)
+               {
+                       int feedback1 = (CH->op1_out[0]+CH->op1_out[1])>>CH->FB;
+                       CH->op1_out[1] = CH->op1_out[0];
+                       *CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT,env_out,feedback1);
+               }
+               else
+               {
+                       *CH->connect1 += OP_OUT(SLOT,env_out,0);
+               }
+       }else
+       {
+               CH->op1_out[1] = CH->op1_out[0];
+               CH->op1_out[0] = 0;
+       }
+       /* SLOT 2 */
+       SLOT = &CH->SLOT[SLOT2];
+       env_out=OPL_CALC_SLOT(SLOT);
+       if( env_out < EG_ENT-1 )
+       {
+               /* PG */
+               if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+               else          SLOT->Cnt += SLOT->Incr;
+               /* connectoion */
+               outd[0] += OP_OUT(SLOT,env_out, feedback2);
+       }
+}
+
+/* ---------- calcrate rhythm block ---------- */
+#define WHITE_NOISE_db 6.0
+INLINE void OPL_CALC_RH( OPL_CH *CH )
+{
+       UINT32 env_tam,env_sd,env_top,env_hh;
+       int whitenoise = (rand()&1)*(WHITE_NOISE_db/EG_STEP);
+       INT32 tone8;
+
+       OPL_SLOT *SLOT;
+       int env_out;
+
+       /* BD : same as FM serial mode and output level is large */
+       feedback2 = 0;
+       /* SLOT 1 */
+       SLOT = &CH[6].SLOT[SLOT1];
+       env_out=OPL_CALC_SLOT(SLOT);
+       if( env_out < EG_ENT-1 )
+       {
+               /* PG */
+               if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+               else          SLOT->Cnt += SLOT->Incr;
+               /* connectoion */
+               if(CH[6].FB)
+               {
+                       int feedback1 = (CH[6].op1_out[0]+CH[6].op1_out[1])>>CH[6].FB;
+                       CH[6].op1_out[1] = CH[6].op1_out[0];
+                       feedback2 = CH[6].op1_out[0] = OP_OUT(SLOT,env_out,feedback1);
+               }
+               else
+               {
+                       feedback2 = OP_OUT(SLOT,env_out,0);
+               }
+       }else
+       {
+               feedback2 = 0;
+               CH[6].op1_out[1] = CH[6].op1_out[0];
+               CH[6].op1_out[0] = 0;
+       }
+       /* SLOT 2 */
+       SLOT = &CH[6].SLOT[SLOT2];
+       env_out=OPL_CALC_SLOT(SLOT);
+       if( env_out < EG_ENT-1 )
+       {
+               /* PG */
+               if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
+               else          SLOT->Cnt += SLOT->Incr;
+               /* connectoion */
+               outd[0] += OP_OUT(SLOT,env_out, feedback2)*2;
+       }
+
+       // SD  (17) = mul14[fnum7] + white noise
+       // TAM (15) = mul15[fnum8]
+       // TOP (18) = fnum6(mul18[fnum8]+whitenoise)
+       // HH  (14) = fnum7(mul18[fnum8]+whitenoise) + white noise
+       env_sd =OPL_CALC_SLOT(SLOT7_2) + whitenoise;
+       env_tam=OPL_CALC_SLOT(SLOT8_1);
+       env_top=OPL_CALC_SLOT(SLOT8_2);
+       env_hh =OPL_CALC_SLOT(SLOT7_1) + whitenoise;
+
+       /* PG */
+       if(SLOT7_1->vib) SLOT7_1->Cnt += (2*SLOT7_1->Incr*vib/VIB_RATE);
+       else             SLOT7_1->Cnt += 2*SLOT7_1->Incr;
+       if(SLOT7_2->vib) SLOT7_2->Cnt += ((CH[7].fc*8)*vib/VIB_RATE);
+       else             SLOT7_2->Cnt += (CH[7].fc*8);
+       if(SLOT8_1->vib) SLOT8_1->Cnt += (SLOT8_1->Incr*vib/VIB_RATE);
+       else             SLOT8_1->Cnt += SLOT8_1->Incr;
+       if(SLOT8_2->vib) SLOT8_2->Cnt += ((CH[8].fc*48)*vib/VIB_RATE);
+       else             SLOT8_2->Cnt += (CH[8].fc*48);
+
+       tone8 = OP_OUT(SLOT8_2,whitenoise,0 );
+
+       /* SD */
+       if( env_sd < EG_ENT-1 )
+               outd[0] += OP_OUT(SLOT7_1,env_sd, 0)*8;
+       /* TAM */
+       if( env_tam < EG_ENT-1 )
+               outd[0] += OP_OUT(SLOT8_1,env_tam, 0)*2;
+       /* TOP-CY */
+       if( env_top < EG_ENT-1 )
+               outd[0] += OP_OUT(SLOT7_2,env_top,tone8)*2;
+       /* HH */
+       if( env_hh  < EG_ENT-1 )
+               outd[0] += OP_OUT(SLOT7_2,env_hh,tone8)*2;
+}
+
+/* ----------- initialize time tabls ----------- */
+static void init_timetables( FM_OPL *OPL , int ARRATE , int DRRATE )
+{
+       int i;
+       double rate;
+
+       /* make attack rate & decay rate tables */
+       for (i = 0;i < 4;i++) OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0;
+       for (i = 4;i <= 60;i++){
+               rate  = OPL->freqbase;                                          /* frequency rate */
+               if( i < 60 ) rate *= 1.0+(i&3)*0.25;            /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */
+               rate *= 1<<((i>>2)-1);                                          /* b2-5 : shift bit */
+               rate *= (double)(EG_ENT<<ENV_BITS);
+               OPL->AR_TABLE[i] = rate / ARRATE;
+               OPL->DR_TABLE[i] = rate / DRRATE;
+       }
+       for (i = 60; i < ARRAY_SIZE(OPL->AR_TABLE); i++)
+       {
+               OPL->AR_TABLE[i] = EG_AED-1;
+               OPL->DR_TABLE[i] = OPL->DR_TABLE[60];
+       }
+#if 0
+       for (i = 0;i < 64 ;i++){        /* make for overflow area */
+               LOG(LOG_WAR, ("rate %2d , ar %f ms , dr %f ms\n", i,
+                       ((double)(EG_ENT<<ENV_BITS) / OPL->AR_TABLE[i]) * (1000.0 / OPL->rate),
+                       ((double)(EG_ENT<<ENV_BITS) / OPL->DR_TABLE[i]) * (1000.0 / OPL->rate) ));
+       }
+#endif
+}
+
+/* ---------- generic table initialize ---------- */
+static int OPLOpenTable( void )
+{
+       int s,t;
+       double rate;
+       int i,j;
+       double pom;
+
+       /* allocate dynamic tables */
+       if( (TL_TABLE = malloc(TL_MAX*2*sizeof(INT32))) == NULL)
+               return 0;
+       if( (SIN_TABLE = malloc(SIN_ENT*4 *sizeof(INT32 *))) == NULL)
+       {
+               free(TL_TABLE);
+               return 0;
+       }
+       if( (AMS_TABLE = malloc(AMS_ENT*2 *sizeof(INT32))) == NULL)
+       {
+               free(TL_TABLE);
+               free(SIN_TABLE);
+               return 0;
+       }
+       if( (VIB_TABLE = malloc(VIB_ENT*2 *sizeof(INT32))) == NULL)
+       {
+               free(TL_TABLE);
+               free(SIN_TABLE);
+               free(AMS_TABLE);
+               return 0;
+       }
+       /* make total level table */
+       for (t = 0;t < EG_ENT-1 ;t++){
+               rate = ((1<<TL_BITS)-1)/pow(10,EG_STEP*t/20);   /* dB -> voltage */
+               TL_TABLE[       t] =  (int)rate;
+               TL_TABLE[TL_MAX+t] = -TL_TABLE[t];
+/*             LOG(LOG_INF,("TotalLevel(%3d) = %x\n",t,TL_TABLE[t]));*/
+       }
+       /* fill volume off area */
+       for ( t = EG_ENT-1; t < TL_MAX ;t++){
+               TL_TABLE[t] = TL_TABLE[TL_MAX+t] = 0;
+       }
+
+       /* make sinwave table (total level offet) */
+       /* degree 0 = degree 180                   = off */
+       SIN_TABLE[0] = SIN_TABLE[SIN_ENT/2]         = &TL_TABLE[EG_ENT-1];
+       for (s = 1;s <= SIN_ENT/4;s++){
+               pom = sin(2*PI*s/SIN_ENT); /* sin     */
+               pom = 20*log10(1/pom);     /* decibel */
+               j = pom / EG_STEP;         /* TL_TABLE steps */
+
+        /* degree 0   -  90    , degree 180 -  90 : plus section */
+               SIN_TABLE[          s] = SIN_TABLE[SIN_ENT/2-s] = &TL_TABLE[j];
+        /* degree 180 - 270    , degree 360 - 270 : minus section */
+               SIN_TABLE[SIN_ENT/2+s] = SIN_TABLE[SIN_ENT  -s] = &TL_TABLE[TL_MAX+j];
+/*             LOG(LOG_INF,("sin(%3d) = %f:%f db\n",s,pom,(double)j * EG_STEP));*/
+       }
+       for (s = 0;s < SIN_ENT;s++)
+       {
+               SIN_TABLE[SIN_ENT*1+s] = s<(SIN_ENT/2) ? SIN_TABLE[s] : &TL_TABLE[EG_ENT];
+               SIN_TABLE[SIN_ENT*2+s] = SIN_TABLE[s % (SIN_ENT/2)];
+               SIN_TABLE[SIN_ENT*3+s] = (s/(SIN_ENT/4))&1 ? &TL_TABLE[EG_ENT] : SIN_TABLE[SIN_ENT*2+s];
+       }
+
+       /* envelope counter -> envelope output table */
+       for (i=0; i<EG_ENT; i++)
+       {
+               /* ATTACK curve */
+               pom = pow( ((double)(EG_ENT-1-i)/EG_ENT) , 8 ) * EG_ENT;
+               /* if( pom >= EG_ENT ) pom = EG_ENT-1; */
+               ENV_CURVE[i] = (int)pom;
+               /* DECAY ,RELEASE curve */
+               ENV_CURVE[(EG_DST>>ENV_BITS)+i]= i;
+       }
+       /* off */
+       ENV_CURVE[EG_OFF>>ENV_BITS]= EG_ENT-1;
+       /* make LFO ams table */
+       for (i=0; i<AMS_ENT; i++)
+       {
+               pom = (1.0+sin(2*PI*i/AMS_ENT))/2; /* sin */
+               AMS_TABLE[i]         = (1.0/EG_STEP)*pom; /* 1dB   */
+               AMS_TABLE[AMS_ENT+i] = (4.8/EG_STEP)*pom; /* 4.8dB */
+       }
+       /* make LFO vibrate table */
+       for (i=0; i<VIB_ENT; i++)
+       {
+               /* 100cent = 1seminote = 6% ?? */
+               pom = (double)VIB_RATE*0.06*sin(2*PI*i/VIB_ENT); /* +-100sect step */
+               VIB_TABLE[i]         = VIB_RATE + (pom*0.07); /* +- 7cent */
+               VIB_TABLE[VIB_ENT+i] = VIB_RATE + (pom*0.14); /* +-14cent */
+               /* LOG(LOG_INF,("vib %d=%d\n",i,VIB_TABLE[VIB_ENT+i])); */
+       }
+       return 1;
+}
+
+
+static void OPLCloseTable( void )
+{
+       free(TL_TABLE);
+       free(SIN_TABLE);
+       free(AMS_TABLE);
+       free(VIB_TABLE);
+}
+
+/* CSM Key Control */
+INLINE void CSMKeyControll(OPL_CH *CH)
+{
+       OPL_SLOT *slot1 = &CH->SLOT[SLOT1];
+       OPL_SLOT *slot2 = &CH->SLOT[SLOT2];
+       /* all key off */
+       OPL_KEYOFF(slot1);
+       OPL_KEYOFF(slot2);
+       /* total level latch */
+       slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+       slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+       /* key on */
+       CH->op1_out[0] = CH->op1_out[1] = 0;
+       OPL_KEYON(slot1);
+       OPL_KEYON(slot2);
+}
+
+/* ---------- opl initialize ---------- */
+static void OPL_initialize(FM_OPL *OPL)
+{
+       int fn;
+
+       /* frequency base */
+       OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72  : 0;
+       /* Timer base time */
+       OPL->TimerBase = 1.0/((double)OPL->clock / 72.0 );
+       /* make time tables */
+       init_timetables( OPL , OPL_ARRATE , OPL_DRRATE );
+       /* make fnumber -> increment counter table */
+       for( fn=0 ; fn < 1024 ; fn++ )
+       {
+               OPL->FN_TABLE[fn] = OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2;
+       }
+       /* LFO freq.table */
+       OPL->amsIncr = OPL->rate ? (double)AMS_ENT*(1<<AMS_SHIFT) / OPL->rate * 3.7 * ((double)OPL->clock/3600000) : 0;
+       OPL->vibIncr = OPL->rate ? (double)VIB_ENT*(1<<VIB_SHIFT) / OPL->rate * 6.4 * ((double)OPL->clock/3600000) : 0;
+}
+
+/* ---------- write a OPL registers ---------- */
+static void OPLWriteReg(FM_OPL *OPL, int r, int v)
+{
+       OPL_CH *CH;
+       int slot;
+       int block_fnum;
+
+       switch(r&0xe0)
+       {
+       case 0x00: /* 00-1f:control */
+               switch(r&0x1f)
+               {
+               case 0x01:
+                       /* wave selector enable */
+                       if(OPL->type&OPL_TYPE_WAVESEL)
+                       {
+                               OPL->wavesel = v&0x20;
+                               if(!OPL->wavesel)
+                               {
+                                       /* preset compatible mode */
+                                       int c;
+                                       for(c=0;c<OPL->max_ch;c++)
+                                       {
+                                               OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0];
+                                               OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0];
+                                       }
+                               }
+                       }
+                       return;
+               case 0x02:      /* Timer 1 */
+                       OPL->T[0] = (256-v)*4;
+                       break;
+               case 0x03:      /* Timer 2 */
+                       OPL->T[1] = (256-v)*16;
+                       return;
+               case 0x04:      /* IRQ clear / mask and Timer enable */
+                       if(v&0x80)
+                       {       /* IRQ flag clear */
+                               OPL_STATUS_RESET(OPL,0x7f);
+                       }
+                       else
+                       {       /* set IRQ mask ,timer enable*/
+                               UINT8 st1 = v&1;
+                               UINT8 st2 = (v>>1)&1;
+                               /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */
+                               OPL_STATUS_RESET(OPL,v&0x78);
+                               OPL_STATUSMASK_SET(OPL,((~v)&0x78)|0x01);
+                               /* timer 2 */
+                               if(OPL->st[1] != st2)
+                               {
+                                       double interval = st2 ? (double)OPL->T[1]*OPL->TimerBase : 0.0;
+                                       OPL->st[1] = st2;
+                                       if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+1,interval);
+                               }
+                               /* timer 1 */
+                               if(OPL->st[0] != st1)
+                               {
+                                       double interval = st1 ? (double)OPL->T[0]*OPL->TimerBase : 0.0;
+                                       OPL->st[0] = st1;
+                                       if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+0,interval);
+                               }
+                       }
+                       return;
+#if BUILD_Y8950
+               case 0x06:              /* Key Board OUT */
+                       if(OPL->type&OPL_TYPE_KEYBOARD)
+                       {
+                               if(OPL->keyboardhandler_w)
+                                       OPL->keyboardhandler_w(OPL->keyboard_param,v);
+                               else
+                                       LOG(LOG_WAR,("OPL:write unmapped KEYBOARD port\n"));
+                       }
+                       return;
+               case 0x07:      /* DELTA-T control : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */
+                       if(OPL->type&OPL_TYPE_ADPCM)
+                               YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+                       return;
+               case 0x08:      /* MODE,DELTA-T : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */
+                       OPL->mode = v;
+                       v&=0x1f;        /* for DELTA-T unit */
+               case 0x09:              /* START ADD */
+               case 0x0a:
+               case 0x0b:              /* STOP ADD  */
+               case 0x0c:
+               case 0x0d:              /* PRESCALE   */
+               case 0x0e:
+               case 0x0f:              /* ADPCM data */
+               case 0x10:              /* DELTA-N    */
+               case 0x11:              /* DELTA-N    */
+               case 0x12:              /* EG-CTRL    */
+                       if(OPL->type&OPL_TYPE_ADPCM)
+                               YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+                       return;
+#if 0
+               case 0x15:              /* DAC data    */
+               case 0x16:
+               case 0x17:              /* SHIFT    */
+                       return;
+               case 0x18:              /* I/O CTRL (Direction) */
+                       if(OPL->type&OPL_TYPE_IO)
+                               OPL->portDirection = v&0x0f;
+                       return;
+               case 0x19:              /* I/O DATA */
+                       if(OPL->type&OPL_TYPE_IO)
+                       {
+                               OPL->portLatch = v;
+                               if(OPL->porthandler_w)
+                                       OPL->porthandler_w(OPL->port_param,v&OPL->portDirection);
+                       }
+                       return;
+               case 0x1a:              /* PCM data */
+                       return;
+#endif
+#endif
+               }
+               break;
+       case 0x20:      /* am,vib,ksr,eg type,mul */
+               slot = slot_array[r&0x1f];
+               if(slot == -1) return;
+               set_mul(OPL,slot,v);
+               return;
+       case 0x40:
+               slot = slot_array[r&0x1f];
+               if(slot == -1) return;
+               set_ksl_tl(OPL,slot,v);
+               return;
+       case 0x60:
+               slot = slot_array[r&0x1f];
+               if(slot == -1) return;
+               set_ar_dr(OPL,slot,v);
+               return;
+       case 0x80:
+               slot = slot_array[r&0x1f];
+               if(slot == -1) return;
+               set_sl_rr(OPL,slot,v);
+               return;
+       case 0xa0:
+               switch(r)
+               {
+               case 0xbd:
+                       /* amsep,vibdep,r,bd,sd,tom,tc,hh */
+                       {
+                       UINT8 rkey = OPL->rhythm^v;
+                       OPL->ams_table = &AMS_TABLE[v&0x80 ? AMS_ENT : 0];
+                       OPL->vib_table = &VIB_TABLE[v&0x40 ? VIB_ENT : 0];
+                       OPL->rhythm  = v&0x3f;
+                       if(OPL->rhythm&0x20)
+                       {
+#if 0
+                               usrintf_showmessage("OPL Rhythm mode select");
+#endif
+                               /* BD key on/off */
+                               if(rkey&0x10)
+                               {
+                                       if(v&0x10)
+                                       {
+                                               OPL->P_CH[6].op1_out[0] = OPL->P_CH[6].op1_out[1] = 0;
+                                               OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT1]);
+                                               OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT2]);
+                                       }
+                                       else
+                                       {
+                                               OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1]);
+                                               OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2]);
+                                       }
+                               }
+                               /* SD key on/off */
+                               if(rkey&0x08)
+                               {
+                                       if(v&0x08) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT2]);
+                                       else       OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2]);
+                               }/* TAM key on/off */
+                               if(rkey&0x04)
+                               {
+                                       if(v&0x04) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT1]);
+                                       else       OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1]);
+                               }
+                               /* TOP-CY key on/off */
+                               if(rkey&0x02)
+                               {
+                                       if(v&0x02) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT2]);
+                                       else       OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2]);
+                               }
+                               /* HH key on/off */
+                               if(rkey&0x01)
+                               {
+                                       if(v&0x01) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT1]);
+                                       else       OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1]);
+                               }
+                       }
+                       }
+                       return;
+               }
+               /* keyon,block,fnum */
+               if( (r&0x0f) > 8) return;
+               CH = &OPL->P_CH[r&0x0f];
+               if(!(r&0x10))
+               {       /* a0-a8 */
+                       block_fnum  = (CH->block_fnum&0x1f00) | v;
+               }
+               else
+               {       /* b0-b8 */
+                       int keyon = (v>>5)&1;
+                       block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);
+                       if(CH->keyon != keyon)
+                       {
+                               if( (CH->keyon=keyon) )
+                               {
+                                       CH->op1_out[0] = CH->op1_out[1] = 0;
+                                       OPL_KEYON(&CH->SLOT[SLOT1]);
+                                       OPL_KEYON(&CH->SLOT[SLOT2]);
+                               }
+                               else
+                               {
+                                       OPL_KEYOFF(&CH->SLOT[SLOT1]);
+                                       OPL_KEYOFF(&CH->SLOT[SLOT2]);
+                               }
+                       }
+               }
+               /* update */
+               if(CH->block_fnum != block_fnum)
+               {
+                       int blockRv = 7-(block_fnum>>10);
+                       int fnum   = block_fnum&0x3ff;
+                       CH->block_fnum = block_fnum;
+
+                       CH->ksl_base = KSL_TABLE[block_fnum>>6];
+                       CH->fc = OPL->FN_TABLE[fnum]>>blockRv;
+                       CH->kcode = CH->block_fnum>>9;
+                       if( (OPL->mode&0x40) && CH->block_fnum&0x100) CH->kcode |=1;
+                       CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+                       CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+               }
+               return;
+       case 0xc0:
+               /* FB,C */
+               if( (r&0x0f) > 8) return;
+               CH = &OPL->P_CH[r&0x0f];
+               {
+               int feedback = (v>>1)&7;
+               CH->FB   = feedback ? (8+1) - feedback : 0;
+               CH->CON = v&1;
+               set_algorithm(CH);
+               }
+               return;
+       case 0xe0: /* wave type */
+               slot = slot_array[r&0x1f];
+               if(slot == -1) return;
+               CH = &OPL->P_CH[slot/2];
+               if(OPL->wavesel)
+               {
+                       /* LOG(LOG_INF,("OPL SLOT %d wave select %d\n",slot,v&3)); */
+                       CH->SLOT[slot&1].wavetable = &SIN_TABLE[(v&0x03)*SIN_ENT];
+               }
+               return;
+       }
+}
+
+/* lock/unlock for common table */
+static int OPL_LockTable(void)
+{
+       num_lock++;
+       if(num_lock>1) return 0;
+       /* first time */
+       cur_chip = NULL;
+       /* allocate total level table (128kb space) */
+       if( !OPLOpenTable() )
+       {
+               num_lock--;
+               return -1;
+       }
+       return 0;
+}
+
+static void OPL_UnLockTable(void)
+{
+       if(num_lock) num_lock--;
+       if(num_lock) return;
+       /* last time */
+       cur_chip = NULL;
+       OPLCloseTable();
+}
+
+#if (BUILD_YM3812 || BUILD_YM3526)
+/*******************************************************************************/
+/*             YM3812 local section                                                   */
+/*******************************************************************************/
+
+/* ---------- update one of chip ----------- */
+void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
+{
+    int i;
+       int data;
+       OPLSAMPLE *buf = buffer;
+       UINT32 amsCnt  = OPL->amsCnt;
+       UINT32 vibCnt  = OPL->vibCnt;
+       UINT8 rhythm = OPL->rhythm&0x20;
+       OPL_CH *CH,*R_CH;
+
+       if( (void *)OPL != cur_chip ){
+               cur_chip = (void *)OPL;
+               /* channel pointers */
+               S_CH = OPL->P_CH;
+               E_CH = &S_CH[9];
+               /* rhythm slot */
+               SLOT7_1 = &S_CH[7].SLOT[SLOT1];
+               SLOT7_2 = &S_CH[7].SLOT[SLOT2];
+               SLOT8_1 = &S_CH[8].SLOT[SLOT1];
+               SLOT8_2 = &S_CH[8].SLOT[SLOT2];
+               /* LFO state */
+               amsIncr = OPL->amsIncr;
+               vibIncr = OPL->vibIncr;
+               ams_table = OPL->ams_table;
+               vib_table = OPL->vib_table;
+       }
+       R_CH = rhythm ? &S_CH[6] : E_CH;
+    for( i=0; i < length ; i++ )
+       {
+               /*            channel A         channel B         channel C      */
+               /* LFO */
+               ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT];
+               vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT];
+               outd[0] = 0;
+               /* FM part */
+               for(CH=S_CH ; CH < R_CH ; CH++)
+                       OPL_CALC_CH(CH);
+               /* Rythn part */
+               if(rhythm)
+                       OPL_CALC_RH(S_CH);
+               /* limit check */
+               data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT );
+               /* store to sound buffer */
+               buf[i] = data >> OPL_OUTSB;
+       }
+
+       OPL->amsCnt = amsCnt;
+       OPL->vibCnt = vibCnt;
+#ifdef OPL_OUTPUT_LOG
+       if(opl_dbg_fp)
+       {
+               for(opl_dbg_chip=0;opl_dbg_chip<opl_dbg_maxchip;opl_dbg_chip++)
+                       if( opl_dbg_opl[opl_dbg_chip] == OPL) break;
+               fprintf(opl_dbg_fp,"%c%c%c",0x20+opl_dbg_chip,length&0xff,length/256);
+       }
+#endif
+}
+#endif /* (BUILD_YM3812 || BUILD_YM3526) */
+
+#if BUILD_Y8950
+
+void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
+{
+    int i;
+       int data;
+       OPLSAMPLE *buf = buffer;
+       UINT32 amsCnt  = OPL->amsCnt;
+       UINT32 vibCnt  = OPL->vibCnt;
+       UINT8 rhythm = OPL->rhythm&0x20;
+       OPL_CH *CH,*R_CH;
+       YM_DELTAT *DELTAT = OPL->deltat;
+
+       /* setup DELTA-T unit */
+       YM_DELTAT_DECODE_PRESET(DELTAT);
+
+       if( (void *)OPL != cur_chip ){
+               cur_chip = (void *)OPL;
+               /* channel pointers */
+               S_CH = OPL->P_CH;
+               E_CH = &S_CH[9];
+               /* rhythm slot */
+               SLOT7_1 = &S_CH[7].SLOT[SLOT1];
+               SLOT7_2 = &S_CH[7].SLOT[SLOT2];
+               SLOT8_1 = &S_CH[8].SLOT[SLOT1];
+               SLOT8_2 = &S_CH[8].SLOT[SLOT2];
+               /* LFO state */
+               amsIncr = OPL->amsIncr;
+               vibIncr = OPL->vibIncr;
+               ams_table = OPL->ams_table;
+               vib_table = OPL->vib_table;
+       }
+       R_CH = rhythm ? &S_CH[6] : E_CH;
+    for( i=0; i < length ; i++ )
+       {
+               /*            channel A         channel B         channel C      */
+               /* LFO */
+               ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT];
+               vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT];
+               outd[0] = 0;
+               /* deltaT ADPCM */
+               if( DELTAT->portstate )
+                       YM_DELTAT_ADPCM_CALC(DELTAT);
+               /* FM part */
+               for(CH=S_CH ; CH < R_CH ; CH++)
+                       OPL_CALC_CH(CH);
+               /* Rythn part */
+               if(rhythm)
+                       OPL_CALC_RH(S_CH);
+               /* limit check */
+               data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT );
+               /* store to sound buffer */
+               buf[i] = data >> OPL_OUTSB;
+       }
+       OPL->amsCnt = amsCnt;
+       OPL->vibCnt = vibCnt;
+       /* deltaT START flag */
+       if( !DELTAT->portstate )
+               OPL->status &= 0xfe;
+}
+#endif
+
+/* ---------- reset one of chip ---------- */
+void OPLResetChip(FM_OPL *OPL)
+{
+       int c,s;
+       int i;
+
+       /* reset chip */
+       OPL->mode   = 0;        /* normal mode */
+       OPL_STATUS_RESET(OPL,0x7f);
+       /* reset with register write */
+       OPLWriteReg(OPL,0x01,0); /* wabesel disable */
+       OPLWriteReg(OPL,0x02,0); /* Timer1 */
+       OPLWriteReg(OPL,0x03,0); /* Timer2 */
+       OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */
+       for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0);
+       /* reset OPerator paramater */
+       for( c = 0 ; c < OPL->max_ch ; c++ )
+       {
+               OPL_CH *CH = &OPL->P_CH[c];
+               /* OPL->P_CH[c].PAN = OPN_CENTER; */
+               for(s = 0 ; s < 2 ; s++ )
+               {
+                       /* wave table */
+                       CH->SLOT[s].wavetable = &SIN_TABLE[0];
+                       /* CH->SLOT[s].evm = ENV_MOD_RR; */
+                       CH->SLOT[s].evc = EG_OFF;
+                       CH->SLOT[s].eve = EG_OFF+1;
+                       CH->SLOT[s].evs = 0;
+               }
+       }
+#if BUILD_Y8950
+       if(OPL->type&OPL_TYPE_ADPCM)
+       {
+               YM_DELTAT *DELTAT = OPL->deltat;
+
+               DELTAT->freqbase = OPL->freqbase;
+               DELTAT->output_pointer = outd;
+               DELTAT->portshift = 5;
+               DELTAT->output_range = DELTAT_MIXING_LEVEL<<TL_BITS;
+               YM_DELTAT_ADPCM_Reset(DELTAT,0);
+       }
+#endif
+}
+
+/* ----------  Create one of vietual YM3812 ----------       */
+/* 'rate'  is sampling rate and 'bufsiz' is the size of the  */
+FM_OPL *OPLCreate(int type, int clock, int rate)
+{
+       char *ptr;
+       FM_OPL *OPL;
+       int state_size;
+       int max_ch = 9; /* normaly 9 channels */
+
+       if( OPL_LockTable() ==-1) return NULL;
+       /* allocate OPL state space */
+       state_size  = sizeof(FM_OPL);
+       state_size += sizeof(OPL_CH)*max_ch;
+#if BUILD_Y8950
+       if(type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT);
+#endif
+       /* allocate memory block */
+       ptr = malloc(state_size);
+       if(ptr==NULL) return NULL;
+       /* clear */
+       memset(ptr,0,state_size);
+       OPL        = (FM_OPL *)ptr; ptr+=sizeof(FM_OPL);
+       OPL->P_CH  = (OPL_CH *)ptr; ptr+=sizeof(OPL_CH)*max_ch;
+#if BUILD_Y8950
+       if(type&OPL_TYPE_ADPCM) OPL->deltat = (YM_DELTAT *)ptr; ptr+=sizeof(YM_DELTAT);
+#endif
+       /* set channel state pointer */
+       OPL->type  = type;
+       OPL->clock = clock;
+       OPL->rate  = rate;
+       OPL->max_ch = max_ch;
+       /* init grobal tables */
+       OPL_initialize(OPL);
+       /* reset chip */
+       OPLResetChip(OPL);
+#ifdef OPL_OUTPUT_LOG
+       if(!opl_dbg_fp)
+       {
+               opl_dbg_fp = fopen("opllog.opl","wb");
+               opl_dbg_maxchip = 0;
+       }
+       if(opl_dbg_fp)
+       {
+               opl_dbg_opl[opl_dbg_maxchip] = OPL;
+               fprintf(opl_dbg_fp,"%c%c%c%c%c%c",0x00+opl_dbg_maxchip,
+                       type,
+                       clock&0xff,
+                       (clock/0x100)&0xff,
+                       (clock/0x10000)&0xff,
+                       (clock/0x1000000)&0xff);
+               opl_dbg_maxchip++;
+       }
+#endif
+       return OPL;
+}
+
+/* ----------  Destroy one of vietual YM3812 ----------       */
+void OPLDestroy(FM_OPL *OPL)
+{
+#ifdef OPL_OUTPUT_LOG
+       if(opl_dbg_fp)
+       {
+               fclose(opl_dbg_fp);
+               opl_dbg_fp = NULL;
+       }
+#endif
+       OPL_UnLockTable();
+       free(OPL);
+}
+
+/* ----------  Option handlers ----------       */
+
+void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset)
+{
+       OPL->TimerHandler   = TimerHandler;
+       OPL->TimerParam = channelOffset;
+}
+void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param)
+{
+       OPL->IRQHandler     = IRQHandler;
+       OPL->IRQParam = param;
+}
+void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param)
+{
+       OPL->UpdateHandler = UpdateHandler;
+       OPL->UpdateParam = param;
+}
+#if BUILD_Y8950
+void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param)
+{
+       OPL->porthandler_w = PortHandler_w;
+       OPL->porthandler_r = PortHandler_r;
+       OPL->port_param = param;
+}
+
+void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param)
+{
+       OPL->keyboardhandler_w = KeyboardHandler_w;
+       OPL->keyboardhandler_r = KeyboardHandler_r;
+       OPL->keyboard_param = param;
+}
+#endif
+/* ---------- YM3812 I/O interface ---------- */
+int OPLWrite(FM_OPL *OPL,int a,int v)
+{
+       if( !(a&1) )
+       {       /* address port */
+               OPL->address = v & 0xff;
+       }
+       else
+       {       /* data port */
+               if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+#ifdef OPL_OUTPUT_LOG
+       if(opl_dbg_fp)
+       {
+               for(opl_dbg_chip=0;opl_dbg_chip<opl_dbg_maxchip;opl_dbg_chip++)
+                       if( opl_dbg_opl[opl_dbg_chip] == OPL) break;
+               fprintf(opl_dbg_fp,"%c%c%c",0x10+opl_dbg_chip,OPL->address,v);
+       }
+#endif
+               OPLWriteReg(OPL,OPL->address,v);
+       }
+       return OPL->status>>7;
+}
+
+unsigned char OPLRead(FM_OPL *OPL,int a)
+{
+       if( !(a&1) )
+       {       /* status port */
+               return OPL->status & (OPL->statusmask|0x80);
+       }
+       /* data port */
+       switch(OPL->address)
+       {
+       case 0x05: /* KeyBoard IN */
+               if(OPL->type&OPL_TYPE_KEYBOARD)
+               {
+                       if(OPL->keyboardhandler_r)
+                               return OPL->keyboardhandler_r(OPL->keyboard_param);
+                       else {
+                               LOG(LOG_WAR,("OPL:read unmapped KEYBOARD port\n"));
+                       }
+               }
+               return 0;
+#if 0
+       case 0x0f: /* ADPCM-DATA  */
+               return 0;
+#endif
+       case 0x19: /* I/O DATA    */
+               if(OPL->type&OPL_TYPE_IO)
+               {
+                       if(OPL->porthandler_r)
+                               return OPL->porthandler_r(OPL->port_param);
+                       else {
+                               LOG(LOG_WAR,("OPL:read unmapped I/O port\n"));
+                       }
+               }
+               return 0;
+       case 0x1a: /* PCM-DATA    */
+               return 0;
+       }
+       return 0;
+}
+
+int OPLTimerOver(FM_OPL *OPL,int c)
+{
+       if( c )
+       {       /* Timer B */
+               OPL_STATUS_SET(OPL,0x20);
+       }
+       else
+       {       /* Timer A */
+               OPL_STATUS_SET(OPL,0x40);
+               /* CSM mode key,TL control */
+               if( OPL->mode & 0x80 )
+               {       /* CSM mode total level latch and auto key on */
+                       int ch;
+                       if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+                       for(ch=0;ch<9;ch++)
+                               CSMKeyControll( &OPL->P_CH[ch] );
+               }
+       }
+       /* reload timer */
+       if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+c,(double)OPL->T[c]*OPL->TimerBase);
+       return OPL->status>>7;
+}
diff --git a/hw/audio/gus.c b/hw/audio/gus.c
new file mode 100644 (file)
index 0000000..e44704b
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * QEMU Proxy for Gravis Ultrasound GF1 emulation by Tibor "TS" Schütz
+ *
+ * Copyright (c) 2002-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/audio/audio.h"
+#include "audio/audio.h"
+#include "hw/isa/isa.h"
+#include "hw/gusemu.h"
+#include "hw/gustate.h"
+
+#define dolog(...) AUD_log ("audio", __VA_ARGS__)
+#ifdef DEBUG
+#define ldebug(...) dolog (__VA_ARGS__)
+#else
+#define ldebug(...)
+#endif
+
+#ifdef HOST_WORDS_BIGENDIAN
+#define GUS_ENDIANNESS 1
+#else
+#define GUS_ENDIANNESS 0
+#endif
+
+#define IO_READ_PROTO(name) \
+    static uint32_t name (void *opaque, uint32_t nport)
+#define IO_WRITE_PROTO(name) \
+    static void name (void *opaque, uint32_t nport, uint32_t val)
+
+typedef struct GUSState {
+    ISADevice dev;
+    GUSEmuState emu;
+    QEMUSoundCard card;
+    uint32_t freq;
+    uint32_t port;
+    int pos, left, shift, irqs;
+    GUSsample *mixbuf;
+    uint8_t himem[1024 * 1024 + 32 + 4096];
+    int samples;
+    SWVoiceOut *voice;
+    int64_t last_ticks;
+    qemu_irq pic;
+} GUSState;
+
+IO_READ_PROTO (gus_readb)
+{
+    GUSState *s = opaque;
+
+    return gus_read (&s->emu, nport, 1);
+}
+
+IO_READ_PROTO (gus_readw)
+{
+    GUSState *s = opaque;
+
+    return gus_read (&s->emu, nport, 2);
+}
+
+IO_WRITE_PROTO (gus_writeb)
+{
+    GUSState *s = opaque;
+
+    gus_write (&s->emu, nport, 1, val);
+}
+
+IO_WRITE_PROTO (gus_writew)
+{
+    GUSState *s = opaque;
+
+    gus_write (&s->emu, nport, 2, val);
+}
+
+static int write_audio (GUSState *s, int samples)
+{
+    int net = 0;
+    int pos = s->pos;
+
+    while (samples) {
+        int nbytes, wbytes, wsampl;
+
+        nbytes = samples << s->shift;
+        wbytes = AUD_write (
+            s->voice,
+            s->mixbuf + (pos << (s->shift - 1)),
+            nbytes
+            );
+
+        if (wbytes) {
+            wsampl = wbytes >> s->shift;
+
+            samples -= wsampl;
+            pos = (pos + wsampl) % s->samples;
+
+            net += wsampl;
+        }
+        else {
+            break;
+        }
+    }
+
+    return net;
+}
+
+static void GUS_callback (void *opaque, int free)
+{
+    int samples, to_play, net = 0;
+    GUSState *s = opaque;
+
+    samples = free >> s->shift;
+    to_play = audio_MIN (samples, s->left);
+
+    while (to_play) {
+        int written = write_audio (s, to_play);
+
+        if (!written) {
+            goto reset;
+        }
+
+        s->left -= written;
+        to_play -= written;
+        samples -= written;
+        net += written;
+    }
+
+    samples = audio_MIN (samples, s->samples);
+    if (samples) {
+        gus_mixvoices (&s->emu, s->freq, samples, s->mixbuf);
+
+        while (samples) {
+            int written = write_audio (s, samples);
+            if (!written) {
+                break;
+            }
+            samples -= written;
+            net += written;
+        }
+    }
+    s->left = samples;
+
+ reset:
+    gus_irqgen (&s->emu, muldiv64 (net, 1000000, s->freq));
+}
+
+int GUS_irqrequest (GUSEmuState *emu, int hwirq, int n)
+{
+    GUSState *s = emu->opaque;
+    /* qemu_irq_lower (s->pic); */
+    qemu_irq_raise (s->pic);
+    s->irqs += n;
+    ldebug ("irqrequest %d %d %d\n", hwirq, n, s->irqs);
+    return n;
+}
+
+void GUS_irqclear (GUSEmuState *emu, int hwirq)
+{
+    GUSState *s = emu->opaque;
+    ldebug ("irqclear %d %d\n", hwirq, s->irqs);
+    qemu_irq_lower (s->pic);
+    s->irqs -= 1;
+#ifdef IRQ_STORM
+    if (s->irqs > 0) {
+        qemu_irq_raise (s->pic[hwirq]);
+    }
+#endif
+}
+
+void GUS_dmarequest (GUSEmuState *der)
+{
+    /* GUSState *s = (GUSState *) der; */
+    ldebug ("dma request %d\n", der->gusdma);
+    DMA_hold_DREQ (der->gusdma);
+}
+
+static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
+{
+    GUSState *s = opaque;
+    char tmpbuf[4096];
+    int pos = dma_pos, mode, left = dma_len - dma_pos;
+
+    ldebug ("read DMA %#x %d\n", dma_pos, dma_len);
+    mode = DMA_get_channel_mode (s->emu.gusdma);
+    while (left) {
+        int to_copy = audio_MIN ((size_t) left, sizeof (tmpbuf));
+        int copied;
+
+        ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos);
+        copied = DMA_read_memory (nchan, tmpbuf, pos, to_copy);
+        gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied);
+        left -= copied;
+        pos += copied;
+    }
+
+    if (0 == ((mode >> 4) & 1)) {
+        DMA_release_DREQ (s->emu.gusdma);
+    }
+    return dma_len;
+}
+
+static const VMStateDescription vmstate_gus = {
+    .name = "gus",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields      = (VMStateField []) {
+        VMSTATE_INT32 (pos, GUSState),
+        VMSTATE_INT32 (left, GUSState),
+        VMSTATE_INT32 (shift, GUSState),
+        VMSTATE_INT32 (irqs, GUSState),
+        VMSTATE_INT32 (samples, GUSState),
+        VMSTATE_INT64 (last_ticks, GUSState),
+        VMSTATE_BUFFER (himem, GUSState),
+        VMSTATE_END_OF_LIST ()
+    }
+};
+
+static const MemoryRegionPortio gus_portio_list1[] = {
+    {0x000,  1, 1, .write = gus_writeb },
+    {0x000,  1, 2, .write = gus_writew },
+    {0x006, 10, 1, .read = gus_readb, .write = gus_writeb },
+    {0x006, 10, 2, .read = gus_readw, .write = gus_writew },
+    {0x100,  8, 1, .read = gus_readb, .write = gus_writeb },
+    {0x100,  8, 2, .read = gus_readw, .write = gus_writew },
+    PORTIO_END_OF_LIST (),
+};
+
+static const MemoryRegionPortio gus_portio_list2[] = {
+    {0, 1, 1, .read = gus_readb },
+    {0, 1, 2, .read = gus_readw },
+    PORTIO_END_OF_LIST (),
+};
+
+static int gus_initfn (ISADevice *dev)
+{
+    GUSState *s = DO_UPCAST (GUSState, dev, dev);
+    struct audsettings as;
+
+    AUD_register_card ("gus", &s->card);
+
+    as.freq = s->freq;
+    as.nchannels = 2;
+    as.fmt = AUD_FMT_S16;
+    as.endianness = GUS_ENDIANNESS;
+
+    s->voice = AUD_open_out (
+        &s->card,
+        NULL,
+        "gus",
+        s,
+        GUS_callback,
+        &as
+        );
+
+    if (!s->voice) {
+        AUD_remove_card (&s->card);
+        return -1;
+    }
+
+    s->shift = 2;
+    s->samples = AUD_get_buffer_size_out (s->voice) >> s->shift;
+    s->mixbuf = g_malloc0 (s->samples << s->shift);
+
+    isa_register_portio_list (dev, s->port, gus_portio_list1, s, "gus");
+    isa_register_portio_list (dev, (s->port + 0x100) & 0xf00,
+                              gus_portio_list2, s, "gus");
+
+    DMA_register_channel (s->emu.gusdma, GUS_read_DMA, s);
+    s->emu.himemaddr = s->himem;
+    s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32;
+    s->emu.opaque = s;
+    isa_init_irq (dev, &s->pic, s->emu.gusirq);
+
+    AUD_set_active_out (s->voice, 1);
+
+    return 0;
+}
+
+int GUS_init (ISABus *bus)
+{
+    isa_create_simple (bus, "gus");
+    return 0;
+}
+
+static Property gus_properties[] = {
+    DEFINE_PROP_UINT32 ("freq",    GUSState, freq,        44100),
+    DEFINE_PROP_HEX32  ("iobase",  GUSState, port,        0x240),
+    DEFINE_PROP_UINT32 ("irq",     GUSState, emu.gusirq,  7),
+    DEFINE_PROP_UINT32 ("dma",     GUSState, emu.gusdma,  3),
+    DEFINE_PROP_END_OF_LIST (),
+};
+
+static void gus_class_initfn (ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS (klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS (klass);
+    ic->init = gus_initfn;
+    dc->desc = "Gravis Ultrasound GF1";
+    dc->vmsd = &vmstate_gus;
+    dc->props = gus_properties;
+}
+
+static const TypeInfo gus_info = {
+    .name          = "gus",
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof (GUSState),
+    .class_init    = gus_class_initfn,
+};
+
+static void gus_register_types (void)
+{
+    type_register_static (&gus_info);
+}
+
+type_init (gus_register_types)
diff --git a/hw/audio/gusemu_hal.c b/hw/audio/gusemu_hal.c
new file mode 100644 (file)
index 0000000..0eee617
--- /dev/null
@@ -0,0 +1,554 @@
+/*
+ * GUSEMU32 - bus interface part
+ *
+ * Copyright (C) 2000-2007 Tibor "TS" Schütz
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * TODO: check mixer: see 7.20 of sdk for panning pos (applies to all gus models?)?
+ */
+
+#include "hw/gustate.h"
+#include "hw/gusemu.h"
+
+#define GUSregb(position) (*            (gusptr+(position)))
+#define GUSregw(position) (*(GUSword *) (gusptr+(position)))
+#define GUSregd(position) (*(GUSdword *)(gusptr+(position)))
+
+/* size given in bytes */
+unsigned int gus_read(GUSEmuState * state, int port, int size)
+{
+    int             value_read = 0;
+
+    GUSbyte        *gusptr;
+    gusptr = state->gusdatapos;
+    GUSregd(portaccesses)++;
+
+    switch (port & 0xff0f)
+    {
+        /* MixerCtrlReg (read not supported on GUS classic) */
+        /* case 0x200: return GUSregb(MixerCtrlReg2x0); */
+    case 0x206:                          /* IRQstatReg / SB2x6IRQ */
+        /* adlib/sb bits set in port handlers */
+        /* timer/voice bits set in gus_irqgen() */
+        /* dma bit set in gus_dma_transferdata */
+        /* midi not implemented yet */
+        return GUSregb(IRQStatReg2x6);
+    /* case 0x308:                       */ /* AdLib388 */
+    case 0x208:
+        if (GUSregb(GUS45TimerCtrl) & 1)
+            return GUSregb(TimerStatus2x8);
+        return GUSregb(AdLibStatus2x8);  /* AdLibStatus */
+    case 0x309:                          /* AdLib389 */
+    case 0x209:
+        return GUSregb(AdLibData2x9);    /* AdLibData */
+    case 0x20A:
+        return GUSregb(AdLibCommand2xA); /* AdLib2x8_2xA */
+
+#if 0
+    case 0x20B:                          /* GUS hidden registers (read not supported on GUS classic) */
+        switch (GUSregb(RegCtrl_2xF) & 0x07)
+        {
+        case 0:                                 /* IRQ/DMA select */
+            if (GUSregb(MixerCtrlReg2x0) & 0x40)
+                return GUSregb(IRQ_2xB);        /* control register select bit */
+            else
+                return GUSregb(DMA_2xB);
+            /* case 1-5:                        */ /* general purpose emulation regs  */
+            /*  return ...                      */ /* + status reset reg (write only) */
+        case 6:
+            return GUSregb(Jumper_2xB);         /* Joystick/MIDI enable (JumperReg) */
+        default:;
+        }
+        break;
+#endif
+
+    case 0x20C:                          /* SB2xCd */
+        value_read = GUSregb(SB2xCd);
+        if (GUSregb(StatRead_2xF) & 0x20)
+            GUSregb(SB2xCd) ^= 0x80; /* toggle MSB on read */
+        return value_read;
+        /* case 0x20D:                   */ /* SB2xD is write only -> 2xE writes to it*/
+    case 0x20E:
+        if (GUSregb(RegCtrl_2xF) & 0x80) /* 2xE read IRQ enabled? */
+        {
+            GUSregb(StatRead_2xF) |= 0x80;
+            GUS_irqrequest(state, state->gusirq, 1);
+        }
+        return GUSregb(SB2xE);           /* SB2xE */
+    case 0x20F:                          /* StatRead_2xF */
+        /*set/clear fixed bits */
+        /*value_read = (GUSregb(StatRead_2xF) & 0xf9)|1; */ /*(LSB not set on GUS classic!)*/
+        value_read = (GUSregb(StatRead_2xF) & 0xf9);
+        if (GUSregb(MixerCtrlReg2x0) & 0x08)
+            value_read |= 2;    /* DMA/IRQ enabled flag */
+        return value_read;
+    /* case 0x300:                      */ /* MIDI (not implemented) */
+    /* case 0x301:                      */ /* MIDI (not implemented) */
+    case 0x302:
+        return GUSregb(VoiceSelReg3x2); /* VoiceSelReg */
+    case 0x303:
+        return GUSregb(FunkSelReg3x3);  /* FunkSelReg */
+    case 0x304:                         /* DataRegLoByte3x4 + DataRegWord3x4 */
+    case 0x305:                         /* DataRegHiByte3x5 */
+        switch (GUSregb(FunkSelReg3x3))
+        {
+    /* common functions */
+        case 0x41:                      /* DramDMAContrReg */
+            value_read = GUSregb(GUS41DMACtrl); /* &0xfb */
+            GUSregb(GUS41DMACtrl) &= 0xbb;
+            if (state->gusdma >= 4)
+                value_read |= 0x04;
+            if (GUSregb(IRQStatReg2x6) & 0x80)
+            {
+                value_read |= 0x40;
+                GUSregb(IRQStatReg2x6) &= 0x7f;
+                if (!GUSregb(IRQStatReg2x6))
+                    GUS_irqclear(state, state->gusirq);
+            }
+            return (GUSbyte) value_read;
+            /* DramDMAmemPosReg */
+            /* case 0x42: value_read=GUSregw(GUS42DMAStart); break;*/
+            /* 43h+44h write only */
+        case 0x45:
+            return GUSregb(GUS45TimerCtrl);         /* TimerCtrlReg */
+            /* 46h+47h write only */
+            /* 48h: samp freq - write only */
+        case 0x49:
+            return GUSregb(GUS49SampCtrl) & 0xbf;   /* SampCtrlReg */
+        /* case 4bh:                                */ /* joystick trim not supported */
+        /* case 0x4c: return GUSregb(GUS4cReset);   */ /* GUSreset: write only*/
+    /* voice specific functions */
+        case 0x80:
+        case 0x81:
+        case 0x82:
+        case 0x83:
+        case 0x84:
+        case 0x85:
+        case 0x86:
+        case 0x87:
+        case 0x88:
+        case 0x89:
+        case 0x8a:
+        case 0x8b:
+        case 0x8c:
+        case 0x8d:
+            {
+                int             offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f);
+                offset += ((int) GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Funktion*2 */
+                value_read = GUSregw(offset);
+            }
+            break;
+    /* voice unspecific functions */
+        case 0x8e:                                  /* NumVoice */
+            return GUSregb(NumVoices);
+        case 0x8f:                                  /* irqstatreg */
+            /* (pseudo IRQ-FIFO is processed during a gus_write(0x3X3,0x8f)) */
+            return GUSregb(SynVoiceIRQ8f);
+        default:
+            return 0xffff;
+        }
+        if (size == 1)
+        {
+            if ((port & 0xff0f) == 0x305)
+                value_read = value_read >> 8;
+            value_read &= 0xff;
+        }
+        return (GUSword) value_read;
+    /* case 0x306:                                  */ /* Mixer/Version info */
+        /*  return 0xff; */ /* Pre 3.6 boards, ICS mixer NOT present */
+    case 0x307:                                     /* DRAMaccess */
+        {
+            GUSbyte        *adr;
+            adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff);
+            return *adr;
+        }
+    default:;
+    }
+    return 0xffff;
+}
+
+void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
+{
+    GUSbyte        *gusptr;
+    gusptr = state->gusdatapos;
+    GUSregd(portaccesses)++;
+
+    switch (port & 0xff0f)
+    {
+    case 0x200:                 /* MixerCtrlReg */
+        GUSregb(MixerCtrlReg2x0) = (GUSbyte) data;
+        break;
+    case 0x206:                 /* IRQstatReg / SB2x6IRQ */
+        if (GUSregb(GUS45TimerCtrl) & 0x20) /* SB IRQ enabled? -> set 2x6IRQ bit */
+        {
+            GUSregb(TimerStatus2x8) |= 0x08;
+            GUSregb(IRQStatReg2x6) = 0x10;
+            GUS_irqrequest(state, state->gusirq, 1);
+        }
+        break;
+    case 0x308:                /* AdLib 388h */
+    case 0x208:                /* AdLibCommandReg */
+        GUSregb(AdLibCommand2xA) = (GUSbyte) data;
+        break;
+    case 0x309:                /* AdLib 389h */
+    case 0x209:                /* AdLibDataReg */
+        if ((GUSregb(AdLibCommand2xA) == 0x04) && (!(GUSregb(GUS45TimerCtrl) & 1))) /* GUS auto timer mode enabled? */
+        {
+            if (data & 0x80)
+                GUSregb(TimerStatus2x8) &= 0x1f; /* AdLib IRQ reset? -> clear maskable adl. timer int regs */
+            else
+                GUSregb(TimerDataReg2x9) = (GUSbyte) data;
+        }
+        else
+        {
+            GUSregb(AdLibData2x9) = (GUSbyte) data;
+            if (GUSregb(GUS45TimerCtrl) & 0x02)
+            {
+                GUSregb(TimerStatus2x8) |= 0x01;
+                GUSregb(IRQStatReg2x6) = 0x10;
+                GUS_irqrequest(state, state->gusirq, 1);
+            }
+        }
+        break;
+    case 0x20A:
+        GUSregb(AdLibStatus2x8) = (GUSbyte) data;
+        break;                 /* AdLibStatus2x8 */
+    case 0x20B:                /* GUS hidden registers */
+        switch (GUSregb(RegCtrl_2xF) & 0x7)
+        {
+        case 0:
+            if (GUSregb(MixerCtrlReg2x0) & 0x40)
+                GUSregb(IRQ_2xB) = (GUSbyte) data; /* control register select bit */
+            else
+                GUSregb(DMA_2xB) = (GUSbyte) data;
+            break;
+            /* case 1-4: general purpose emulation regs */
+        case 5:                                    /* clear stat reg 2xF */
+            GUSregb(StatRead_2xF) = 0; /* ToDo: is this identical with GUS classic? */
+            if (!GUSregb(IRQStatReg2x6))
+                GUS_irqclear(state, state->gusirq);
+            break;
+        case 6:                                    /* Jumper reg (Joystick/MIDI enable) */
+            GUSregb(Jumper_2xB) = (GUSbyte) data;
+            break;
+        default:;
+        }
+        break;
+    case 0x20C:                /* SB2xCd */
+        if (GUSregb(GUS45TimerCtrl) & 0x20)
+        {
+            GUSregb(TimerStatus2x8) |= 0x10; /* SB IRQ enabled? -> set 2xCIRQ bit */
+            GUSregb(IRQStatReg2x6) = 0x10;
+            GUS_irqrequest(state, state->gusirq, 1);
+        }
+    case 0x20D:                /* SB2xCd no IRQ */
+        GUSregb(SB2xCd) = (GUSbyte) data;
+        break;
+    case 0x20E:                /* SB2xE */
+        GUSregb(SB2xE) = (GUSbyte) data;
+        break;
+    case 0x20F:
+        GUSregb(RegCtrl_2xF) = (GUSbyte) data;
+        break;                 /* CtrlReg2xF */
+    case 0x302:                /* VoiceSelReg */
+        GUSregb(VoiceSelReg3x2) = (GUSbyte) data;
+        break;
+    case 0x303:                /* FunkSelReg */
+        GUSregb(FunkSelReg3x3) = (GUSbyte) data;
+        if ((GUSbyte) data == 0x8f) /* set irqstatreg, get voicereg and clear IRQ */
+        {
+            int             voice;
+            if (GUSregd(voicewavetableirq)) /* WavetableIRQ */
+            {
+                for (voice = 0; voice < 31; voice++)
+                {
+                    if (GUSregd(voicewavetableirq) & (1 << voice))
+                    {
+                        GUSregd(voicewavetableirq) ^= (1 << voice); /* clear IRQ bit */
+                        GUSregb(voice << 5) &= 0x7f; /* clear voice reg irq bit */
+                        if (!GUSregd(voicewavetableirq))
+                            GUSregb(IRQStatReg2x6) &= 0xdf;
+                        if (!GUSregb(IRQStatReg2x6))
+                            GUS_irqclear(state, state->gusirq);
+                        GUSregb(SynVoiceIRQ8f) = voice | 0x60; /* (bit==0 => IRQ wartend) */
+                        return;
+                    }
+                }
+            }
+            else if (GUSregd(voicevolrampirq)) /* VolRamp IRQ */
+            {
+                for (voice = 0; voice < 31; voice++)
+                {
+                    if (GUSregd(voicevolrampirq) & (1 << voice))
+                    {
+                        GUSregd(voicevolrampirq) ^= (1 << voice); /* clear IRQ bit */
+                        GUSregb((voice << 5) + VSRVolRampControl) &= 0x7f; /* clear voice volume reg irq bit */
+                        if (!GUSregd(voicevolrampirq))
+                            GUSregb(IRQStatReg2x6) &= 0xbf;
+                        if (!GUSregb(IRQStatReg2x6))
+                            GUS_irqclear(state, state->gusirq);
+                        GUSregb(SynVoiceIRQ8f) = voice | 0x80; /* (bit==0 => IRQ wartend) */
+                        return;
+                    }
+                }
+            }
+            GUSregb(SynVoiceIRQ8f) = 0xe8; /* kein IRQ wartet */
+        }
+        break;
+    case 0x304:
+    case 0x305:
+        {
+            GUSword         writedata = (GUSword) data;
+            GUSword         readmask = 0x0000;
+            if (size == 1)
+            {
+                readmask = 0xff00;
+                writedata &= 0xff;
+                if ((port & 0xff0f) == 0x305)
+                {
+                    writedata = (GUSword) (writedata << 8);
+                    readmask = 0x00ff;
+                }
+            }
+            switch (GUSregb(FunkSelReg3x3))
+            {
+                /* voice specific functions */
+            case 0x00:
+            case 0x01:
+            case 0x02:
+            case 0x03:
+            case 0x04:
+            case 0x05:
+            case 0x06:
+            case 0x07:
+            case 0x08:
+            case 0x09:
+            case 0x0a:
+            case 0x0b:
+            case 0x0c:
+            case 0x0d:
+                {
+                    int             offset;
+                    if (!(GUSregb(GUS4cReset) & 0x01))
+                        break;  /* reset flag active? */
+                    offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f);
+                    offset += (GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /*  = Voice*32 + Funktion*2 */
+                    GUSregw(offset) = (GUSword) ((GUSregw(offset) & readmask) | writedata);
+                }
+                break;
+                /* voice unspecific functions */
+            case 0x0e:         /* NumVoices */
+                GUSregb(NumVoices) = (GUSbyte) data;
+                break;
+            /* case 0x0f:      */ /* read only */
+                /* common functions */
+            case 0x41:         /* DramDMAContrReg */
+                GUSregb(GUS41DMACtrl) = (GUSbyte) data;
+                if (data & 0x01)
+                    GUS_dmarequest(state);
+                break;
+            case 0x42:         /* DramDMAmemPosReg */
+                GUSregw(GUS42DMAStart) = (GUSregw(GUS42DMAStart) & readmask) | writedata;
+                GUSregb(GUS50DMAHigh) &= 0xf; /* compatibility stuff... */
+                break;
+            case 0x43:         /* DRAMaddrLo */
+                GUSregd(GUSDRAMPOS24bit) =
+                    (GUSregd(GUSDRAMPOS24bit) & (readmask | 0xff0000)) | writedata;
+                break;
+            case 0x44:         /* DRAMaddrHi */
+                GUSregd(GUSDRAMPOS24bit) =
+                    (GUSregd(GUSDRAMPOS24bit) & 0xffff) | ((data & 0x0f) << 16);
+                break;
+            case 0x45:         /* TCtrlReg */
+                GUSregb(GUS45TimerCtrl) = (GUSbyte) data;
+                if (!(data & 0x20))
+                    GUSregb(TimerStatus2x8) &= 0xe7;    /* sb IRQ dis? -> clear 2x8/2xC sb IRQ flags */
+                if (!(data & 0x02))
+                    GUSregb(TimerStatus2x8) &= 0xfe;    /* adlib data IRQ dis? -> clear 2x8 adlib IRQ flag */
+                if (!(GUSregb(TimerStatus2x8) & 0x19))
+                    GUSregb(IRQStatReg2x6) &= 0xef;     /* 0xe6; $$clear IRQ if both IRQ bits are inactive or cleared */
+                /* catch up delayed timer IRQs: */
+                if ((GUSregw(TimerIRQs) > 1) && (GUSregb(TimerDataReg2x9) & 3))
+                {
+                    if (GUSregb(TimerDataReg2x9) & 1)   /* start timer 1 (80us decrement rate) */
+                    {
+                        if (!(GUSregb(TimerDataReg2x9) & 0x40))
+                            GUSregb(TimerStatus2x8) |= 0xc0;    /* maskable bits */
+                        if (data & 4) /* timer1 irq enable */
+                        {
+                            GUSregb(TimerStatus2x8) |= 4;       /* nonmaskable bit */
+                            GUSregb(IRQStatReg2x6) |= 4;        /* timer 1 irq pending */
+                        }
+                    }
+                    if (GUSregb(TimerDataReg2x9) & 2)   /* start timer 2 (320us decrement rate) */
+                    {
+                        if (!(GUSregb(TimerDataReg2x9) & 0x20))
+                            GUSregb(TimerStatus2x8) |= 0xa0;    /* maskable bits */
+                        if (data & 8) /* timer2 irq enable */
+                        {
+                            GUSregb(TimerStatus2x8) |= 2;       /* nonmaskable bit */
+                            GUSregb(IRQStatReg2x6) |= 8;        /* timer 2 irq pending */
+                        }
+                    }
+                    GUSregw(TimerIRQs)--;
+                    if (GUSregw(BusyTimerIRQs) > 1)
+                        GUSregw(BusyTimerIRQs)--;
+                    else
+                        GUSregw(BusyTimerIRQs) =
+                            GUS_irqrequest(state, state->gusirq, GUSregw(TimerIRQs));
+                }
+                else
+                    GUSregw(TimerIRQs) = 0;
+
+                if (!(data & 0x04))
+                {
+                    GUSregb(TimerStatus2x8) &= 0xfb; /* clear non-maskable timer1 bit */
+                    GUSregb(IRQStatReg2x6)  &= 0xfb;
+                }
+                if (!(data & 0x08))
+                {
+                    GUSregb(TimerStatus2x8) &= 0xfd; /* clear non-maskable timer2 bit */
+                    GUSregb(IRQStatReg2x6)  &= 0xf7;
+                }
+                if (!GUSregb(IRQStatReg2x6))
+                    GUS_irqclear(state, state->gusirq);
+                break;
+            case 0x46:          /* Counter1 */
+                GUSregb(GUS46Counter1) = (GUSbyte) data;
+                break;
+            case 0x47:          /* Counter2 */
+                GUSregb(GUS47Counter2) = (GUSbyte) data;
+                break;
+            /* case 0x48:       */ /* sampling freq reg not emulated (same as interwave) */
+            case 0x49:          /* SampCtrlReg */
+                GUSregb(GUS49SampCtrl) = (GUSbyte) data;
+                break;
+            /* case 0x4b:       */ /* joystick trim not emulated */
+            case 0x4c:          /* GUSreset */
+                GUSregb(GUS4cReset) = (GUSbyte) data;
+                if (!(GUSregb(GUS4cReset) & 1)) /* reset... */
+                {
+                    GUSregd(voicewavetableirq) = 0;
+                    GUSregd(voicevolrampirq) = 0;
+                    GUSregw(TimerIRQs) = 0;
+                    GUSregw(BusyTimerIRQs) = 0;
+                    GUSregb(NumVoices) = 0xcd;
+                    GUSregb(IRQStatReg2x6) = 0;
+                    GUSregb(TimerStatus2x8) = 0;
+                    GUSregb(AdLibData2x9) = 0;
+                    GUSregb(TimerDataReg2x9) = 0;
+                    GUSregb(GUS41DMACtrl) = 0;
+                    GUSregb(GUS45TimerCtrl) = 0;
+                    GUSregb(GUS49SampCtrl) = 0;
+                    GUSregb(GUS4cReset) &= 0xf9; /* clear IRQ and DAC enable bits */
+                    GUS_irqclear(state, state->gusirq);
+                }
+                /* IRQ enable bit checked elsewhere */
+                /* EnableDAC bit may be used by external callers */
+                break;
+            }
+        }
+        break;
+    case 0x307:                /* DRAMaccess */
+        {
+            GUSbyte        *adr;
+            adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff);
+            *adr = (GUSbyte) data;
+        }
+        break;
+    }
+}
+
+/* Attention when breaking up a single DMA transfer to multiple ones:
+ * it may lead to multiple terminal count interrupts and broken transfers:
+ *
+ * 1. Whenever you transfer a piece of data, the gusemu callback is invoked
+ * 2. The callback may generate a TC irq (if the register was set up to do so)
+ * 3. The irq may result in the program using the GUS to reprogram the GUS
+ *
+ * Some programs also decide to upload by just checking if TC occurs
+ * (via interrupt or a cleared GUS dma flag)
+ * and then start the next transfer, without checking DMA state
+ *
+ * Thus: Always make sure to set the TC flag correctly!
+ *
+ * Note that the genuine GUS had a granularity of 16 bytes/words for low/high DMA
+ * while later cards had atomic granularity provided by an additional GUS50DMAHigh register
+ * GUSemu also uses this register to support byte-granular transfers for better compatibility
+ * with emulators other than GUSemu32
+ */
+
+void gus_dma_transferdata(GUSEmuState * state, char *dma_addr, unsigned int count, int TC)
+{
+    /* this function gets called by the callback function as soon as a DMA transfer is about to start
+     * dma_addr is a translated address within accessible memory, not the physical one,
+     * count is (real dma count register)+1
+     * note that the amount of bytes transferred is fully determined by values in the DMA registers
+     * do not forget to update DMA states after transferring the entire block:
+     * DREQ cleared & TC asserted after the _whole_ transfer */
+
+    char           *srcaddr;
+    char           *destaddr;
+    char            msbmask = 0;
+    GUSbyte        *gusptr;
+    gusptr = state->gusdatapos;
+
+    srcaddr = dma_addr; /* system memory address */
+    {
+        int             offset = (GUSregw(GUS42DMAStart) << 4) + (GUSregb(GUS50DMAHigh) & 0xf);
+        if (state->gusdma >= 4)
+            offset = (offset & 0xc0000) + (2 * (offset & 0x1fff0)); /* 16 bit address translation */
+        destaddr = (char *) state->himemaddr + offset; /* wavetable RAM address */
+    }
+
+    GUSregw(GUS42DMAStart) += (GUSword)  (count >> 4);                           /* ToDo: add 16bit GUS page limit? */
+    GUSregb(GUS50DMAHigh)   = (GUSbyte) ((count + GUSregb(GUS50DMAHigh)) & 0xf); /* ToDo: add 16bit GUS page limit? */
+
+    if (GUSregb(GUS41DMACtrl) & 0x02)   /* direction, 0 := sysram->gusram */
+    {
+        char           *tmpaddr = destaddr;
+        destaddr = srcaddr;
+        srcaddr = tmpaddr;
+    }
+
+    if ((GUSregb(GUS41DMACtrl) & 0x80) && (!(GUSregb(GUS41DMACtrl) & 0x02)))
+        msbmask = (const char) 0x80;    /* invert MSB */
+    for (; count > 0; count--)
+    {
+        if (GUSregb(GUS41DMACtrl) & 0x40)
+            *(destaddr++) = *(srcaddr++);               /* 16 bit lobyte */
+        else
+            *(destaddr++) = (msbmask ^ (*(srcaddr++))); /* 8 bit */
+        if (state->gusdma >= 4)
+            *(destaddr++) = (msbmask ^ (*(srcaddr++))); /* 16 bit hibyte */
+    }
+
+    if (TC)
+    {
+        (GUSregb(GUS41DMACtrl)) &= 0xfe;        /* clear DMA request bit */
+        if (GUSregb(GUS41DMACtrl) & 0x20)       /* DMA terminal count IRQ */
+        {
+            GUSregb(IRQStatReg2x6) |= 0x80;
+            GUS_irqrequest(state, state->gusirq, 1);
+        }
+    }
+}
diff --git a/hw/audio/gusemu_mixer.c b/hw/audio/gusemu_mixer.c
new file mode 100644 (file)
index 0000000..816c58a
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * GUSEMU32 - mixing engine (similar to Interwave GF1 compatibility)
+ *
+ * Copyright (C) 2000-2007 Tibor "TS" Schütz
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/gusemu.h"
+#include "hw/gustate.h"
+
+#define GUSregb(position)  (*            (gusptr+(position)))
+#define GUSregw(position)  (*(GUSword *) (gusptr+(position)))
+#define GUSregd(position)  (*(GUSdword *)(gusptr+(position)))
+
+#define GUSvoice(position) (*(GUSword *)(voiceptr+(position)))
+
+/* samples are always 16bit stereo (4 bytes each, first right then left interleaved) */
+void gus_mixvoices(GUSEmuState * state, unsigned int playback_freq, unsigned int numsamples,
+                   GUSsample *bufferpos)
+{
+    /* note that byte registers are stored in the upper half of each voice register! */
+    GUSbyte        *gusptr;
+    int             Voice;
+    GUSword        *voiceptr;
+
+    unsigned int    count;
+    for (count = 0; count < numsamples * 2; count++)
+        *(bufferpos + count) = 0;       /* clear */
+
+    gusptr = state->gusdatapos;
+    voiceptr = (GUSword *) gusptr;
+    if (!(GUSregb(GUS4cReset) & 0x01))  /* reset flag active? */
+        return;
+
+    for (Voice = 0; Voice <= (GUSregb(NumVoices) & 31); Voice++)
+    {
+        if (GUSvoice(wVSRControl)        &  0x200)
+            GUSvoice(wVSRControl)        |= 0x100; /* voice stop request */
+        if (GUSvoice(wVSRVolRampControl) &  0x200)
+            GUSvoice(wVSRVolRampControl) |= 0x100; /* Volume ramp stop request */
+        if (!(GUSvoice(wVSRControl) & GUSvoice(wVSRVolRampControl) & 0x100)) /* neither voice nor volume calculation active - save some time here ;) */
+        {
+            unsigned int    sample;
+
+            unsigned int    LoopStart = (GUSvoice(wVSRLoopStartHi) << 16) | GUSvoice(wVSRLoopStartLo); /* 23.9 format */
+            unsigned int    LoopEnd   = (GUSvoice(wVSRLoopEndHi)   << 16) | GUSvoice(wVSRLoopEndLo);   /* 23.9 format */
+            unsigned int    CurrPos   = (GUSvoice(wVSRCurrPosHi)   << 16) | GUSvoice(wVSRCurrPosLo);   /* 23.9 format */
+            int             VoiceIncrement = ((((unsigned long) GUSvoice(wVSRFreq) * 44100) / playback_freq) * (14 >> 1)) /
+                                             ((GUSregb(NumVoices) & 31) + 1); /* 6.10 increment/frame to 23.9 increment/sample */
+
+            int             PanningPos = (GUSvoice(wVSRPanning) >> 8) & 0xf;
+
+            unsigned int    Volume32   = 32 * GUSvoice(wVSRCurrVol); /* 32 times larger than original gus for maintaining precision while ramping */
+            unsigned int    StartVol32 = (GUSvoice(wVSRVolRampStartVol) & 0xff00) * 32;
+            unsigned int    EndVol32   = (GUSvoice(wVSRVolRampEndVol)   & 0xff00) * 32;
+            int             VolumeIncrement32 = (32 * 16 * (GUSvoice(wVSRVolRampRate) & 0x3f00) >> 8) >> ((((GUSvoice(wVSRVolRampRate) & 0xc000) >> 8) >> 6) * 3); /* including 1/8/64/512 volume speed divisor */
+            VolumeIncrement32 = (((VolumeIncrement32 * 44100 / 2) / playback_freq) * 14) / ((GUSregb(NumVoices) & 31) + 1); /* adjust ramping speed to playback speed */
+
+            if (GUSvoice(wVSRControl) & 0x4000)
+                VoiceIncrement    = -VoiceIncrement;    /* reverse playback */
+            if (GUSvoice(wVSRVolRampControl) & 0x4000)
+                VolumeIncrement32 = -VolumeIncrement32; /* reverse ramping */
+
+            for (sample = 0; sample < numsamples; sample++)
+            {
+                int             sample1, sample2, Volume;
+                if (GUSvoice(wVSRControl) & 0x400)      /* 16bit */
+                {
+                    int offset = ((CurrPos >> 9) & 0xc0000) + (((CurrPos >> 9) & 0x1ffff) << 1);
+                    GUSchar *adr;
+                    adr = (GUSchar *) state->himemaddr + offset;
+                    sample1 = (*adr & 0xff) + (*(adr + 1) * 256);
+                    sample2 = (*(adr + 2) & 0xff) + (*(adr + 2 + 1) * 256);
+                }
+                else            /* 8bit */
+                {
+                    int offset = (CurrPos >> 9) & 0xfffff;
+                    GUSchar *adr;
+                    adr = (GUSchar *) state->himemaddr + offset;
+                    sample1 = (*adr) * 256;
+                    sample2 = (*(adr + 1)) * 256;
+                }
+
+                Volume = ((((Volume32 >> (4 + 5)) & 0xff) + 256) << (Volume32 >> ((4 + 8) + 5))) / 512; /* semi-logarithmic volume, +5 due to additional precision */
+                sample1 = (((sample1 * Volume) >> 16) * (512 - (CurrPos % 512))) / 512;
+                sample2 = (((sample2 * Volume) >> 16) * (CurrPos % 512)) / 512;
+                sample1 += sample2;
+
+                if (!(GUSvoice(wVSRVolRampControl) & 0x100))
+                {
+                    Volume32 += VolumeIncrement32;
+                    if ((GUSvoice(wVSRVolRampControl) & 0x4000) ? (Volume32 <= StartVol32) : (Volume32 >= EndVol32)) /* ramp up boundary cross */
+                    {
+                        if (GUSvoice(wVSRVolRampControl) & 0x2000)
+                            GUSvoice(wVSRVolRampControl) |= 0x8000;     /* volramp IRQ enabled? -> IRQ wait flag */
+                        if (GUSvoice(wVSRVolRampControl) & 0x800)       /* loop enabled */
+                        {
+                            if (GUSvoice(wVSRVolRampControl) & 0x1000)  /* bidir. loop */
+                            {
+                                GUSvoice(wVSRVolRampControl) ^= 0x4000; /* toggle dir */
+                                VolumeIncrement32 = -VolumeIncrement32;
+                            }
+                            else
+                                Volume32 = (GUSvoice(wVSRVolRampControl) & 0x4000) ? EndVol32 : StartVol32; /* unidir. loop ramp */
+                        }
+                        else
+                        {
+                            GUSvoice(wVSRVolRampControl) |= 0x100;
+                            Volume32 =
+                                (GUSvoice(wVSRVolRampControl) & 0x4000) ? StartVol32 : EndVol32;
+                        }
+                    }
+                }
+                if ((GUSvoice(wVSRVolRampControl) & 0xa000) == 0xa000)  /* volramp IRQ set and enabled? */
+                {
+                    GUSregd(voicevolrampirq) |= 1 << Voice;             /* set irq slot */
+                }
+                else
+                {
+                    GUSregd(voicevolrampirq) &= (~(1 << Voice));        /* clear irq slot */
+                    GUSvoice(wVSRVolRampControl) &= 0x7f00;
+                }
+
+                if (!(GUSvoice(wVSRControl) & 0x100))
+                {
+                    CurrPos += VoiceIncrement;
+                    if ((GUSvoice(wVSRControl) & 0x4000) ? (CurrPos <= LoopStart) : (CurrPos >= LoopEnd)) /* playback boundary cross */
+                    {
+                        if (GUSvoice(wVSRControl) & 0x2000)
+                            GUSvoice(wVSRControl) |= 0x8000;       /* voice IRQ enabled -> IRQ wait flag */
+                        if (GUSvoice(wVSRControl) & 0x800)         /* loop enabled */
+                        {
+                            if (GUSvoice(wVSRControl) & 0x1000)    /* pingpong loop */
+                            {
+                                GUSvoice(wVSRControl) ^= 0x4000;   /* toggle dir */
+                                VoiceIncrement = -VoiceIncrement;
+                            }
+                            else
+                                CurrPos = (GUSvoice(wVSRControl) & 0x4000) ? LoopEnd : LoopStart; /* unidir. loop */
+                        }
+                        else if (!(GUSvoice(wVSRVolRampControl) & 0x400))
+                            GUSvoice(wVSRControl) |= 0x100;        /* loop disabled, rollover check */
+                    }
+                }
+                if ((GUSvoice(wVSRControl) & 0xa000) == 0xa000)    /* wavetable IRQ set and enabled? */
+                {
+                    GUSregd(voicewavetableirq) |= 1 << Voice;      /* set irq slot */
+                }
+                else
+                {
+                    GUSregd(voicewavetableirq) &= (~(1 << Voice)); /* clear irq slot */
+                    GUSvoice(wVSRControl) &= 0x7f00;
+                }
+
+                /* mix samples into buffer */
+                *(bufferpos + 2 * sample)     += (GUSsample) ((sample1 * PanningPos) >> 4);        /* right */
+                *(bufferpos + 2 * sample + 1) += (GUSsample) ((sample1 * (15 - PanningPos)) >> 4); /* left */
+            }
+            /* write back voice and volume */
+            GUSvoice(wVSRCurrVol)   = Volume32 / 32;
+            GUSvoice(wVSRCurrPosHi) = CurrPos >> 16;
+            GUSvoice(wVSRCurrPosLo) = CurrPos & 0xffff;
+        }
+        voiceptr += 16; /* next voice */
+    }
+}
+
+void gus_irqgen(GUSEmuState * state, unsigned int elapsed_time)
+/* time given in microseconds */
+{
+    int             requestedIRQs = 0;
+    GUSbyte        *gusptr;
+    gusptr = state->gusdatapos;
+    if (GUSregb(TimerDataReg2x9) & 1) /* start timer 1 (80us decrement rate) */
+    {
+        unsigned int    timer1fraction = state->timer1fraction;
+        int             newtimerirqs;
+        newtimerirqs          = (elapsed_time + timer1fraction) / (80 * (256 - GUSregb(GUS46Counter1)));
+        state->timer1fraction = (elapsed_time + timer1fraction) % (80 * (256 - GUSregb(GUS46Counter1)));
+        if (newtimerirqs)
+        {
+            if (!(GUSregb(TimerDataReg2x9) & 0x40))
+                GUSregb(TimerStatus2x8) |= 0xc0; /* maskable bits */
+            if (GUSregb(GUS45TimerCtrl) & 4)     /* timer1 irq enable */
+            {
+                GUSregb(TimerStatus2x8) |= 4;    /* nonmaskable bit */
+                GUSregb(IRQStatReg2x6)  |= 4;    /* timer 1 irq pending */
+                GUSregw(TimerIRQs) += newtimerirqs;
+                requestedIRQs += newtimerirqs;
+            }
+        }
+    }
+    if (GUSregb(TimerDataReg2x9) & 2) /* start timer 2 (320us decrement rate) */
+    {
+        unsigned int timer2fraction = state->timer2fraction;
+        int             newtimerirqs;
+        newtimerirqs          = (elapsed_time + timer2fraction) / (320 * (256 - GUSregb(GUS47Counter2)));
+        state->timer2fraction = (elapsed_time + timer2fraction) % (320 * (256 - GUSregb(GUS47Counter2)));
+        if (newtimerirqs)
+        {
+            if (!(GUSregb(TimerDataReg2x9) & 0x20))
+                GUSregb(TimerStatus2x8) |= 0xa0; /* maskable bits */
+            if (GUSregb(GUS45TimerCtrl) & 8)     /* timer2 irq enable */
+            {
+                GUSregb(TimerStatus2x8) |= 2;    /* nonmaskable bit */
+                GUSregb(IRQStatReg2x6)  |= 8;    /* timer 2 irq pending */
+                GUSregw(TimerIRQs) += newtimerirqs;
+                requestedIRQs += newtimerirqs;
+            }
+        }
+    }
+    if (GUSregb(GUS4cReset) & 0x4) /* synth IRQ enable */
+    {
+        if (GUSregd(voicewavetableirq))
+            GUSregb(IRQStatReg2x6) |= 0x20;
+        if (GUSregd(voicevolrampirq))
+            GUSregb(IRQStatReg2x6) |= 0x40;
+    }
+    if ((!requestedIRQs) && GUSregb(IRQStatReg2x6))
+        requestedIRQs++;
+    if (GUSregb(IRQStatReg2x6))
+        GUSregw(BusyTimerIRQs) = GUS_irqrequest(state, state->gusirq, requestedIRQs);
+}
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
new file mode 100644 (file)
index 0000000..6bdd820
--- /dev/null
@@ -0,0 +1,1098 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * written by Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "hw/intel-hda.h"
+#include "hw/intel-hda-defs.h"
+#include "audio/audio.h"
+
+/* -------------------------------------------------------------------------- */
+
+typedef struct desc_param {
+    uint32_t id;
+    uint32_t val;
+} desc_param;
+
+typedef struct desc_node {
+    uint32_t nid;
+    const char *name;
+    const desc_param *params;
+    uint32_t nparams;
+    uint32_t config;
+    uint32_t pinctl;
+    uint32_t *conn;
+    uint32_t stindex;
+} desc_node;
+
+typedef struct desc_codec {
+    const char *name;
+    uint32_t iid;
+    const desc_node *nodes;
+    uint32_t nnodes;
+} desc_codec;
+
+static const desc_param* hda_codec_find_param(const desc_node *node, uint32_t id)
+{
+    int i;
+
+    for (i = 0; i < node->nparams; i++) {
+        if (node->params[i].id == id) {
+            return &node->params[i];
+        }
+    }
+    return NULL;
+}
+
+static const desc_node* hda_codec_find_node(const desc_codec *codec, uint32_t nid)
+{
+    int i;
+
+    for (i = 0; i < codec->nnodes; i++) {
+        if (codec->nodes[i].nid == nid) {
+            return &codec->nodes[i];
+        }
+    }
+    return NULL;
+}
+
+static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
+{
+    if (format & AC_FMT_TYPE_NON_PCM) {
+        return;
+    }
+
+    as->freq = (format & AC_FMT_BASE_44K) ? 44100 : 48000;
+
+    switch ((format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT) {
+    case 1: as->freq *= 2; break;
+    case 2: as->freq *= 3; break;
+    case 3: as->freq *= 4; break;
+    }
+
+    switch ((format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT) {
+    case 1: as->freq /= 2; break;
+    case 2: as->freq /= 3; break;
+    case 3: as->freq /= 4; break;
+    case 4: as->freq /= 5; break;
+    case 5: as->freq /= 6; break;
+    case 6: as->freq /= 7; break;
+    case 7: as->freq /= 8; break;
+    }
+
+    switch (format & AC_FMT_BITS_MASK) {
+    case AC_FMT_BITS_8:  as->fmt = AUD_FMT_S8;  break;
+    case AC_FMT_BITS_16: as->fmt = AUD_FMT_S16; break;
+    case AC_FMT_BITS_32: as->fmt = AUD_FMT_S32; break;
+    }
+
+    as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1;
+}
+
+/* -------------------------------------------------------------------------- */
+/*
+ * HDA codec descriptions
+ */
+
+/* some defines */
+
+#define QEMU_HDA_ID_VENDOR  0x1af4
+#define QEMU_HDA_PCM_FORMATS (AC_SUPPCM_BITS_16 |       \
+                              0x1fc /* 16 -> 96 kHz */)
+#define QEMU_HDA_AMP_NONE    (0)
+#define QEMU_HDA_AMP_STEPS   0x4a
+
+#ifdef CONFIG_MIXEMU
+# define QEMU_HDA_ID_OUTPUT  ((QEMU_HDA_ID_VENDOR << 16) | 0x12)
+# define QEMU_HDA_ID_DUPLEX  ((QEMU_HDA_ID_VENDOR << 16) | 0x22)
+# define QEMU_HDA_ID_MICRO   ((QEMU_HDA_ID_VENDOR << 16) | 0x32)
+# define QEMU_HDA_AMP_CAPS                                              \
+    (AC_AMPCAP_MUTE |                                                   \
+     (QEMU_HDA_AMP_STEPS << AC_AMPCAP_OFFSET_SHIFT)    |                \
+     (QEMU_HDA_AMP_STEPS << AC_AMPCAP_NUM_STEPS_SHIFT) |                \
+     (3                  << AC_AMPCAP_STEP_SIZE_SHIFT))
+#else
+# define QEMU_HDA_ID_OUTPUT  ((QEMU_HDA_ID_VENDOR << 16) | 0x11)
+# define QEMU_HDA_ID_DUPLEX  ((QEMU_HDA_ID_VENDOR << 16) | 0x21)
+# define QEMU_HDA_ID_MICRO   ((QEMU_HDA_ID_VENDOR << 16) | 0x31)
+# define QEMU_HDA_AMP_CAPS   QEMU_HDA_AMP_NONE
+#endif
+
+/* common: audio output widget */
+static const desc_param common_params_audio_dac[] = {
+    {
+        .id  = AC_PAR_AUDIO_WIDGET_CAP,
+        .val = ((AC_WID_AUD_OUT << AC_WCAP_TYPE_SHIFT) |
+                AC_WCAP_FORMAT_OVRD |
+                AC_WCAP_AMP_OVRD |
+                AC_WCAP_OUT_AMP |
+                AC_WCAP_STEREO),
+    },{
+        .id  = AC_PAR_PCM,
+        .val = QEMU_HDA_PCM_FORMATS,
+    },{
+        .id  = AC_PAR_STREAM,
+        .val = AC_SUPFMT_PCM,
+    },{
+        .id  = AC_PAR_AMP_IN_CAP,
+        .val = QEMU_HDA_AMP_NONE,
+    },{
+        .id  = AC_PAR_AMP_OUT_CAP,
+        .val = QEMU_HDA_AMP_CAPS,
+    },
+};
+
+/* common: audio input widget */
+static const desc_param common_params_audio_adc[] = {
+    {
+        .id  = AC_PAR_AUDIO_WIDGET_CAP,
+        .val = ((AC_WID_AUD_IN << AC_WCAP_TYPE_SHIFT) |
+                AC_WCAP_CONN_LIST |
+                AC_WCAP_FORMAT_OVRD |
+                AC_WCAP_AMP_OVRD |
+                AC_WCAP_IN_AMP |
+                AC_WCAP_STEREO),
+    },{
+        .id  = AC_PAR_CONNLIST_LEN,
+        .val = 1,
+    },{
+        .id  = AC_PAR_PCM,
+        .val = QEMU_HDA_PCM_FORMATS,
+    },{
+        .id  = AC_PAR_STREAM,
+        .val = AC_SUPFMT_PCM,
+    },{
+        .id  = AC_PAR_AMP_IN_CAP,
+        .val = QEMU_HDA_AMP_CAPS,
+    },{
+        .id  = AC_PAR_AMP_OUT_CAP,
+        .val = QEMU_HDA_AMP_NONE,
+    },
+};
+
+/* common: pin widget (line-out) */
+static const desc_param common_params_audio_lineout[] = {
+    {
+        .id  = AC_PAR_AUDIO_WIDGET_CAP,
+        .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) |
+                AC_WCAP_CONN_LIST |
+                AC_WCAP_STEREO),
+    },{
+        .id  = AC_PAR_PIN_CAP,
+        .val = AC_PINCAP_OUT,
+    },{
+        .id  = AC_PAR_CONNLIST_LEN,
+        .val = 1,
+    },{
+        .id  = AC_PAR_AMP_IN_CAP,
+        .val = QEMU_HDA_AMP_NONE,
+    },{
+        .id  = AC_PAR_AMP_OUT_CAP,
+        .val = QEMU_HDA_AMP_NONE,
+    },
+};
+
+/* common: pin widget (line-in) */
+static const desc_param common_params_audio_linein[] = {
+    {
+        .id  = AC_PAR_AUDIO_WIDGET_CAP,
+        .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) |
+                AC_WCAP_STEREO),
+    },{
+        .id  = AC_PAR_PIN_CAP,
+        .val = AC_PINCAP_IN,
+    },{
+        .id  = AC_PAR_AMP_IN_CAP,
+        .val = QEMU_HDA_AMP_NONE,
+    },{
+        .id  = AC_PAR_AMP_OUT_CAP,
+        .val = QEMU_HDA_AMP_NONE,
+    },
+};
+
+/* output: root node */
+static const desc_param output_params_root[] = {
+    {
+        .id  = AC_PAR_VENDOR_ID,
+        .val = QEMU_HDA_ID_OUTPUT,
+    },{
+        .id  = AC_PAR_SUBSYSTEM_ID,
+        .val = QEMU_HDA_ID_OUTPUT,
+    },{
+        .id  = AC_PAR_REV_ID,
+        .val = 0x00100101,
+    },{
+        .id  = AC_PAR_NODE_COUNT,
+        .val = 0x00010001,
+    },
+};
+
+/* output: audio function */
+static const desc_param output_params_audio_func[] = {
+    {
+        .id  = AC_PAR_FUNCTION_TYPE,
+        .val = AC_GRP_AUDIO_FUNCTION,
+    },{
+        .id  = AC_PAR_SUBSYSTEM_ID,
+        .val = QEMU_HDA_ID_OUTPUT,
+    },{
+        .id  = AC_PAR_NODE_COUNT,
+        .val = 0x00020002,
+    },{
+        .id  = AC_PAR_PCM,
+        .val = QEMU_HDA_PCM_FORMATS,
+    },{
+        .id  = AC_PAR_STREAM,
+        .val = AC_SUPFMT_PCM,
+    },{
+        .id  = AC_PAR_AMP_IN_CAP,
+        .val = QEMU_HDA_AMP_NONE,
+    },{
+        .id  = AC_PAR_AMP_OUT_CAP,
+        .val = QEMU_HDA_AMP_NONE,
+    },{
+        .id  = AC_PAR_GPIO_CAP,
+        .val = 0,
+    },{
+        .id  = AC_PAR_AUDIO_FG_CAP,
+        .val = 0x00000808,
+    },{
+        .id  = AC_PAR_POWER_STATE,
+        .val = 0,
+    },
+};
+
+/* output: nodes */
+static const desc_node output_nodes[] = {
+    {
+        .nid     = AC_NODE_ROOT,
+        .name    = "root",
+        .params  = output_params_root,
+        .nparams = ARRAY_SIZE(output_params_root),
+    },{
+        .nid     = 1,
+        .name    = "func",
+        .params  = output_params_audio_func,
+        .nparams = ARRAY_SIZE(output_params_audio_func),
+    },{
+        .nid     = 2,
+        .name    = "dac",
+        .params  = common_params_audio_dac,
+        .nparams = ARRAY_SIZE(common_params_audio_dac),
+        .stindex = 0,
+    },{
+        .nid     = 3,
+        .name    = "out",
+        .params  = common_params_audio_lineout,
+        .nparams = ARRAY_SIZE(common_params_audio_lineout),
+        .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
+                    (AC_JACK_LINE_OUT     << AC_DEFCFG_DEVICE_SHIFT)    |
+                    (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
+                    (AC_JACK_COLOR_GREEN  << AC_DEFCFG_COLOR_SHIFT)     |
+                    0x10),
+        .pinctl  = AC_PINCTL_OUT_EN,
+        .conn    = (uint32_t[]) { 2 },
+    }
+};
+
+/* output: codec */
+static const desc_codec output = {
+    .name   = "output",
+    .iid    = QEMU_HDA_ID_OUTPUT,
+    .nodes  = output_nodes,
+    .nnodes = ARRAY_SIZE(output_nodes),
+};
+
+/* duplex: root node */
+static const desc_param duplex_params_root[] = {
+    {
+        .id  = AC_PAR_VENDOR_ID,
+        .val = QEMU_HDA_ID_DUPLEX,
+    },{
+        .id  = AC_PAR_SUBSYSTEM_ID,
+        .val = QEMU_HDA_ID_DUPLEX,
+    },{
+        .id  = AC_PAR_REV_ID,
+        .val = 0x00100101,
+    },{
+        .id  = AC_PAR_NODE_COUNT,
+        .val = 0x00010001,
+    },
+};
+
+/* duplex: audio function */
+static const desc_param duplex_params_audio_func[] = {
+    {
+        .id  = AC_PAR_FUNCTION_TYPE,
+        .val = AC_GRP_AUDIO_FUNCTION,
+    },{
+        .id  = AC_PAR_SUBSYSTEM_ID,
+        .val = QEMU_HDA_ID_DUPLEX,
+    },{
+        .id  = AC_PAR_NODE_COUNT,
+        .val = 0x00020004,
+    },{
+        .id  = AC_PAR_PCM,
+        .val = QEMU_HDA_PCM_FORMATS,
+    },{
+        .id  = AC_PAR_STREAM,
+        .val = AC_SUPFMT_PCM,
+    },{
+        .id  = AC_PAR_AMP_IN_CAP,
+        .val = QEMU_HDA_AMP_NONE,
+    },{
+        .id  = AC_PAR_AMP_OUT_CAP,
+        .val = QEMU_HDA_AMP_NONE,
+    },{
+        .id  = AC_PAR_GPIO_CAP,
+        .val = 0,
+    },{
+        .id  = AC_PAR_AUDIO_FG_CAP,
+        .val = 0x00000808,
+    },{
+        .id  = AC_PAR_POWER_STATE,
+        .val = 0,
+    },
+};
+
+/* duplex: nodes */
+static const desc_node duplex_nodes[] = {
+    {
+        .nid     = AC_NODE_ROOT,
+        .name    = "root",
+        .params  = duplex_params_root,
+        .nparams = ARRAY_SIZE(duplex_params_root),
+    },{
+        .nid     = 1,
+        .name    = "func",
+        .params  = duplex_params_audio_func,
+        .nparams = ARRAY_SIZE(duplex_params_audio_func),
+    },{
+        .nid     = 2,
+        .name    = "dac",
+        .params  = common_params_audio_dac,
+        .nparams = ARRAY_SIZE(common_params_audio_dac),
+        .stindex = 0,
+    },{
+        .nid     = 3,
+        .name    = "out",
+        .params  = common_params_audio_lineout,
+        .nparams = ARRAY_SIZE(common_params_audio_lineout),
+        .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
+                    (AC_JACK_LINE_OUT     << AC_DEFCFG_DEVICE_SHIFT)    |
+                    (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
+                    (AC_JACK_COLOR_GREEN  << AC_DEFCFG_COLOR_SHIFT)     |
+                    0x10),
+        .pinctl  = AC_PINCTL_OUT_EN,
+        .conn    = (uint32_t[]) { 2 },
+    },{
+        .nid     = 4,
+        .name    = "adc",
+        .params  = common_params_audio_adc,
+        .nparams = ARRAY_SIZE(common_params_audio_adc),
+        .stindex = 1,
+        .conn    = (uint32_t[]) { 5 },
+    },{
+        .nid     = 5,
+        .name    = "in",
+        .params  = common_params_audio_linein,
+        .nparams = ARRAY_SIZE(common_params_audio_linein),
+        .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
+                    (AC_JACK_LINE_IN      << AC_DEFCFG_DEVICE_SHIFT)    |
+                    (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
+                    (AC_JACK_COLOR_RED    << AC_DEFCFG_COLOR_SHIFT)     |
+                    0x20),
+        .pinctl  = AC_PINCTL_IN_EN,
+    }
+};
+
+/* duplex: codec */
+static const desc_codec duplex = {
+    .name   = "duplex",
+    .iid    = QEMU_HDA_ID_DUPLEX,
+    .nodes  = duplex_nodes,
+    .nnodes = ARRAY_SIZE(duplex_nodes),
+};
+
+/* micro: root node */
+static const desc_param micro_params_root[] = {
+    {
+        .id  = AC_PAR_VENDOR_ID,
+        .val = QEMU_HDA_ID_MICRO,
+    },{
+        .id  = AC_PAR_SUBSYSTEM_ID,
+        .val = QEMU_HDA_ID_MICRO,
+    },{
+        .id  = AC_PAR_REV_ID,
+        .val = 0x00100101,
+    },{
+        .id  = AC_PAR_NODE_COUNT,
+        .val = 0x00010001,
+    },
+};
+
+/* micro: audio function */
+static const desc_param micro_params_audio_func[] = {
+    {
+        .id  = AC_PAR_FUNCTION_TYPE,
+        .val = AC_GRP_AUDIO_FUNCTION,
+    },{
+        .id  = AC_PAR_SUBSYSTEM_ID,
+        .val = QEMU_HDA_ID_MICRO,
+    },{
+        .id  = AC_PAR_NODE_COUNT,
+        .val = 0x00020004,
+    },{
+        .id  = AC_PAR_PCM,
+        .val = QEMU_HDA_PCM_FORMATS,
+    },{
+        .id  = AC_PAR_STREAM,
+        .val = AC_SUPFMT_PCM,
+    },{
+        .id  = AC_PAR_AMP_IN_CAP,
+        .val = QEMU_HDA_AMP_NONE,
+    },{
+        .id  = AC_PAR_AMP_OUT_CAP,
+        .val = QEMU_HDA_AMP_NONE,
+    },{
+        .id  = AC_PAR_GPIO_CAP,
+        .val = 0,
+    },{
+        .id  = AC_PAR_AUDIO_FG_CAP,
+        .val = 0x00000808,
+    },{
+        .id  = AC_PAR_POWER_STATE,
+        .val = 0,
+    },
+};
+
+/* micro: nodes */
+static const desc_node micro_nodes[] = {
+    {
+        .nid     = AC_NODE_ROOT,
+        .name    = "root",
+        .params  = micro_params_root,
+        .nparams = ARRAY_SIZE(micro_params_root),
+    },{
+        .nid     = 1,
+        .name    = "func",
+        .params  = micro_params_audio_func,
+        .nparams = ARRAY_SIZE(micro_params_audio_func),
+    },{
+        .nid     = 2,
+        .name    = "dac",
+        .params  = common_params_audio_dac,
+        .nparams = ARRAY_SIZE(common_params_audio_dac),
+        .stindex = 0,
+    },{
+        .nid     = 3,
+        .name    = "out",
+        .params  = common_params_audio_lineout,
+        .nparams = ARRAY_SIZE(common_params_audio_lineout),
+        .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
+                    (AC_JACK_SPEAKER      << AC_DEFCFG_DEVICE_SHIFT)    |
+                    (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
+                    (AC_JACK_COLOR_GREEN  << AC_DEFCFG_COLOR_SHIFT)     |
+                    0x10),
+        .pinctl  = AC_PINCTL_OUT_EN,
+        .conn    = (uint32_t[]) { 2 },
+    },{
+        .nid     = 4,
+        .name    = "adc",
+        .params  = common_params_audio_adc,
+        .nparams = ARRAY_SIZE(common_params_audio_adc),
+        .stindex = 1,
+        .conn    = (uint32_t[]) { 5 },
+    },{
+        .nid     = 5,
+        .name    = "in",
+        .params  = common_params_audio_linein,
+        .nparams = ARRAY_SIZE(common_params_audio_linein),
+        .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
+                    (AC_JACK_MIC_IN       << AC_DEFCFG_DEVICE_SHIFT)    |
+                    (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
+                    (AC_JACK_COLOR_RED    << AC_DEFCFG_COLOR_SHIFT)     |
+                    0x20),
+        .pinctl  = AC_PINCTL_IN_EN,
+    }
+};
+
+/* micro: codec */
+static const desc_codec micro = {
+    .name   = "micro",
+    .iid    = QEMU_HDA_ID_MICRO,
+    .nodes  = micro_nodes,
+    .nnodes = ARRAY_SIZE(micro_nodes),
+};
+
+/* -------------------------------------------------------------------------- */
+
+static const char *fmt2name[] = {
+    [ AUD_FMT_U8  ] = "PCM-U8",
+    [ AUD_FMT_S8  ] = "PCM-S8",
+    [ AUD_FMT_U16 ] = "PCM-U16",
+    [ AUD_FMT_S16 ] = "PCM-S16",
+    [ AUD_FMT_U32 ] = "PCM-U32",
+    [ AUD_FMT_S32 ] = "PCM-S32",
+};
+
+typedef struct HDAAudioState HDAAudioState;
+typedef struct HDAAudioStream HDAAudioStream;
+
+struct HDAAudioStream {
+    HDAAudioState *state;
+    const desc_node *node;
+    bool output, running;
+    uint32_t stream;
+    uint32_t channel;
+    uint32_t format;
+    uint32_t gain_left, gain_right;
+    bool mute_left, mute_right;
+    struct audsettings as;
+    union {
+        SWVoiceIn *in;
+        SWVoiceOut *out;
+    } voice;
+    uint8_t buf[HDA_BUFFER_SIZE];
+    uint32_t bpos;
+};
+
+struct HDAAudioState {
+    HDACodecDevice hda;
+    const char *name;
+
+    QEMUSoundCard card;
+    const desc_codec *desc;
+    HDAAudioStream st[4];
+    bool running_compat[16];
+    bool running_real[2 * 16];
+
+    /* properties */
+    uint32_t debug;
+};
+
+static void hda_audio_input_cb(void *opaque, int avail)
+{
+    HDAAudioStream *st = opaque;
+    int recv = 0;
+    int len;
+    bool rc;
+
+    while (avail - recv >= sizeof(st->buf)) {
+        if (st->bpos != sizeof(st->buf)) {
+            len = AUD_read(st->voice.in, st->buf + st->bpos,
+                           sizeof(st->buf) - st->bpos);
+            st->bpos += len;
+            recv += len;
+            if (st->bpos != sizeof(st->buf)) {
+                break;
+            }
+        }
+        rc = hda_codec_xfer(&st->state->hda, st->stream, false,
+                            st->buf, sizeof(st->buf));
+        if (!rc) {
+            break;
+        }
+        st->bpos = 0;
+    }
+}
+
+static void hda_audio_output_cb(void *opaque, int avail)
+{
+    HDAAudioStream *st = opaque;
+    int sent = 0;
+    int len;
+    bool rc;
+
+    while (avail - sent >= sizeof(st->buf)) {
+        if (st->bpos == sizeof(st->buf)) {
+            rc = hda_codec_xfer(&st->state->hda, st->stream, true,
+                                st->buf, sizeof(st->buf));
+            if (!rc) {
+                break;
+            }
+            st->bpos = 0;
+        }
+        len = AUD_write(st->voice.out, st->buf + st->bpos,
+                        sizeof(st->buf) - st->bpos);
+        st->bpos += len;
+        sent += len;
+        if (st->bpos != sizeof(st->buf)) {
+            break;
+        }
+    }
+}
+
+static void hda_audio_set_running(HDAAudioStream *st, bool running)
+{
+    if (st->node == NULL) {
+        return;
+    }
+    if (st->running == running) {
+        return;
+    }
+    st->running = running;
+    dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name,
+           st->running ? "on" : "off", st->stream);
+    if (st->output) {
+        AUD_set_active_out(st->voice.out, st->running);
+    } else {
+        AUD_set_active_in(st->voice.in, st->running);
+    }
+}
+
+static void hda_audio_set_amp(HDAAudioStream *st)
+{
+    bool muted;
+    uint32_t left, right;
+
+    if (st->node == NULL) {
+        return;
+    }
+
+    muted = st->mute_left && st->mute_right;
+    left  = st->mute_left  ? 0 : st->gain_left;
+    right = st->mute_right ? 0 : st->gain_right;
+
+    left = left * 255 / QEMU_HDA_AMP_STEPS;
+    right = right * 255 / QEMU_HDA_AMP_STEPS;
+
+    if (st->output) {
+        AUD_set_volume_out(st->voice.out, muted, left, right);
+    } else {
+        AUD_set_volume_in(st->voice.in, muted, left, right);
+    }
+}
+
+static void hda_audio_setup(HDAAudioStream *st)
+{
+    if (st->node == NULL) {
+        return;
+    }
+
+    dprint(st->state, 1, "%s: format: %d x %s @ %d Hz\n",
+           st->node->name, st->as.nchannels,
+           fmt2name[st->as.fmt], st->as.freq);
+
+    if (st->output) {
+        st->voice.out = AUD_open_out(&st->state->card, st->voice.out,
+                                     st->node->name, st,
+                                     hda_audio_output_cb, &st->as);
+    } else {
+        st->voice.in = AUD_open_in(&st->state->card, st->voice.in,
+                                   st->node->name, st,
+                                   hda_audio_input_cb, &st->as);
+    }
+}
+
+static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data)
+{
+    HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+    HDAAudioStream *st;
+    const desc_node *node = NULL;
+    const desc_param *param;
+    uint32_t verb, payload, response, count, shift;
+
+    if ((data & 0x70000) == 0x70000) {
+        /* 12/8 id/payload */
+        verb = (data >> 8) & 0xfff;
+        payload = data & 0x00ff;
+    } else {
+        /* 4/16 id/payload */
+        verb = (data >> 8) & 0xf00;
+        payload = data & 0xffff;
+    }
+
+    node = hda_codec_find_node(a->desc, nid);
+    if (node == NULL) {
+        goto fail;
+    }
+    dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n",
+           __FUNCTION__, nid, node->name, verb, payload);
+
+    switch (verb) {
+    /* all nodes */
+    case AC_VERB_PARAMETERS:
+        param = hda_codec_find_param(node, payload);
+        if (param == NULL) {
+            goto fail;
+        }
+        hda_codec_response(hda, true, param->val);
+        break;
+    case AC_VERB_GET_SUBSYSTEM_ID:
+        hda_codec_response(hda, true, a->desc->iid);
+        break;
+
+    /* all functions */
+    case AC_VERB_GET_CONNECT_LIST:
+        param = hda_codec_find_param(node, AC_PAR_CONNLIST_LEN);
+        count = param ? param->val : 0;
+        response = 0;
+        shift = 0;
+        while (payload < count && shift < 32) {
+            response |= node->conn[payload] << shift;
+            payload++;
+            shift += 8;
+        }
+        hda_codec_response(hda, true, response);
+        break;
+
+    /* pin widget */
+    case AC_VERB_GET_CONFIG_DEFAULT:
+        hda_codec_response(hda, true, node->config);
+        break;
+    case AC_VERB_GET_PIN_WIDGET_CONTROL:
+        hda_codec_response(hda, true, node->pinctl);
+        break;
+    case AC_VERB_SET_PIN_WIDGET_CONTROL:
+        if (node->pinctl != payload) {
+            dprint(a, 1, "unhandled pin control bit\n");
+        }
+        hda_codec_response(hda, true, 0);
+        break;
+
+    /* audio in/out widget */
+    case AC_VERB_SET_CHANNEL_STREAMID:
+        st = a->st + node->stindex;
+        if (st->node == NULL) {
+            goto fail;
+        }
+        hda_audio_set_running(st, false);
+        st->stream = (payload >> 4) & 0x0f;
+        st->channel = payload & 0x0f;
+        dprint(a, 2, "%s: stream %d, channel %d\n",
+               st->node->name, st->stream, st->channel);
+        hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]);
+        hda_codec_response(hda, true, 0);
+        break;
+    case AC_VERB_GET_CONV:
+        st = a->st + node->stindex;
+        if (st->node == NULL) {
+            goto fail;
+        }
+        response = st->stream << 4 | st->channel;
+        hda_codec_response(hda, true, response);
+        break;
+    case AC_VERB_SET_STREAM_FORMAT:
+        st = a->st + node->stindex;
+        if (st->node == NULL) {
+            goto fail;
+        }
+        st->format = payload;
+        hda_codec_parse_fmt(st->format, &st->as);
+        hda_audio_setup(st);
+        hda_codec_response(hda, true, 0);
+        break;
+    case AC_VERB_GET_STREAM_FORMAT:
+        st = a->st + node->stindex;
+        if (st->node == NULL) {
+            goto fail;
+        }
+        hda_codec_response(hda, true, st->format);
+        break;
+    case AC_VERB_GET_AMP_GAIN_MUTE:
+        st = a->st + node->stindex;
+        if (st->node == NULL) {
+            goto fail;
+        }
+        if (payload & AC_AMP_GET_LEFT) {
+            response = st->gain_left | (st->mute_left ? AC_AMP_MUTE : 0);
+        } else {
+            response = st->gain_right | (st->mute_right ? AC_AMP_MUTE : 0);
+        }
+        hda_codec_response(hda, true, response);
+        break;
+    case AC_VERB_SET_AMP_GAIN_MUTE:
+        st = a->st + node->stindex;
+        if (st->node == NULL) {
+            goto fail;
+        }
+        dprint(a, 1, "amp (%s): %s%s%s%s index %d  gain %3d %s\n",
+               st->node->name,
+               (payload & AC_AMP_SET_OUTPUT) ? "o" : "-",
+               (payload & AC_AMP_SET_INPUT)  ? "i" : "-",
+               (payload & AC_AMP_SET_LEFT)   ? "l" : "-",
+               (payload & AC_AMP_SET_RIGHT)  ? "r" : "-",
+               (payload & AC_AMP_SET_INDEX) >> AC_AMP_SET_INDEX_SHIFT,
+               (payload & AC_AMP_GAIN),
+               (payload & AC_AMP_MUTE) ? "muted" : "");
+        if (payload & AC_AMP_SET_LEFT) {
+            st->gain_left = payload & AC_AMP_GAIN;
+            st->mute_left = payload & AC_AMP_MUTE;
+        }
+        if (payload & AC_AMP_SET_RIGHT) {
+            st->gain_right = payload & AC_AMP_GAIN;
+            st->mute_right = payload & AC_AMP_MUTE;
+        }
+        hda_audio_set_amp(st);
+        hda_codec_response(hda, true, 0);
+        break;
+
+    /* not supported */
+    case AC_VERB_SET_POWER_STATE:
+    case AC_VERB_GET_POWER_STATE:
+    case AC_VERB_GET_SDI_SELECT:
+        hda_codec_response(hda, true, 0);
+        break;
+    default:
+        goto fail;
+    }
+    return;
+
+fail:
+    dprint(a, 1, "%s: not handled: nid %d (%s), verb 0x%x, payload 0x%x\n",
+           __FUNCTION__, nid, node ? node->name : "?", verb, payload);
+    hda_codec_response(hda, true, 0);
+}
+
+static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output)
+{
+    HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+    int s;
+
+    a->running_compat[stnr] = running;
+    a->running_real[output * 16 + stnr] = running;
+    for (s = 0; s < ARRAY_SIZE(a->st); s++) {
+        if (a->st[s].node == NULL) {
+            continue;
+        }
+        if (a->st[s].output != output) {
+            continue;
+        }
+        if (a->st[s].stream != stnr) {
+            continue;
+        }
+        hda_audio_set_running(&a->st[s], running);
+    }
+}
+
+static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc)
+{
+    HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+    HDAAudioStream *st;
+    const desc_node *node;
+    const desc_param *param;
+    uint32_t i, type;
+
+    a->desc = desc;
+    a->name = object_get_typename(OBJECT(a));
+    dprint(a, 1, "%s: cad %d\n", __FUNCTION__, a->hda.cad);
+
+    AUD_register_card("hda", &a->card);
+    for (i = 0; i < a->desc->nnodes; i++) {
+        node = a->desc->nodes + i;
+        param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP);
+        if (NULL == param)
+            continue;
+        type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+        switch (type) {
+        case AC_WID_AUD_OUT:
+        case AC_WID_AUD_IN:
+            assert(node->stindex < ARRAY_SIZE(a->st));
+            st = a->st + node->stindex;
+            st->state = a;
+            st->node = node;
+            if (type == AC_WID_AUD_OUT) {
+                /* unmute output by default */
+                st->gain_left = QEMU_HDA_AMP_STEPS;
+                st->gain_right = QEMU_HDA_AMP_STEPS;
+                st->bpos = sizeof(st->buf);
+                st->output = true;
+            } else {
+                st->output = false;
+            }
+            st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 |
+                (1 << AC_FMT_CHAN_SHIFT);
+            hda_codec_parse_fmt(st->format, &st->as);
+            hda_audio_setup(st);
+            break;
+        }
+    }
+    return 0;
+}
+
+static int hda_audio_exit(HDACodecDevice *hda)
+{
+    HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
+    HDAAudioStream *st;
+    int i;
+
+    dprint(a, 1, "%s\n", __FUNCTION__);
+    for (i = 0; i < ARRAY_SIZE(a->st); i++) {
+        st = a->st + i;
+        if (st->node == NULL) {
+            continue;
+        }
+        if (st->output) {
+            AUD_close_out(&a->card, st->voice.out);
+        } else {
+            AUD_close_in(&a->card, st->voice.in);
+        }
+    }
+    AUD_remove_card(&a->card);
+    return 0;
+}
+
+static int hda_audio_post_load(void *opaque, int version)
+{
+    HDAAudioState *a = opaque;
+    HDAAudioStream *st;
+    int i;
+
+    dprint(a, 1, "%s\n", __FUNCTION__);
+    if (version == 1) {
+        /* assume running_compat[] is for output streams */
+        for (i = 0; i < ARRAY_SIZE(a->running_compat); i++)
+            a->running_real[16 + i] = a->running_compat[i];
+    }
+
+    for (i = 0; i < ARRAY_SIZE(a->st); i++) {
+        st = a->st + i;
+        if (st->node == NULL)
+            continue;
+        hda_codec_parse_fmt(st->format, &st->as);
+        hda_audio_setup(st);
+        hda_audio_set_amp(st);
+        hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]);
+    }
+    return 0;
+}
+
+static const VMStateDescription vmstate_hda_audio_stream = {
+    .name = "hda-audio-stream",
+    .version_id = 1,
+    .fields = (VMStateField []) {
+        VMSTATE_UINT32(stream, HDAAudioStream),
+        VMSTATE_UINT32(channel, HDAAudioStream),
+        VMSTATE_UINT32(format, HDAAudioStream),
+        VMSTATE_UINT32(gain_left, HDAAudioStream),
+        VMSTATE_UINT32(gain_right, HDAAudioStream),
+        VMSTATE_BOOL(mute_left, HDAAudioStream),
+        VMSTATE_BOOL(mute_right, HDAAudioStream),
+        VMSTATE_UINT32(bpos, HDAAudioStream),
+        VMSTATE_BUFFER(buf, HDAAudioStream),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_hda_audio = {
+    .name = "hda-audio",
+    .version_id = 2,
+    .post_load = hda_audio_post_load,
+    .fields = (VMStateField []) {
+        VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0,
+                             vmstate_hda_audio_stream,
+                             HDAAudioStream),
+        VMSTATE_BOOL_ARRAY(running_compat, HDAAudioState, 16),
+        VMSTATE_BOOL_ARRAY_V(running_real, HDAAudioState, 2 * 16, 2),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property hda_audio_properties[] = {
+    DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static int hda_audio_init_output(HDACodecDevice *hda)
+{
+    return hda_audio_init(hda, &output);
+}
+
+static int hda_audio_init_duplex(HDACodecDevice *hda)
+{
+    return hda_audio_init(hda, &duplex);
+}
+
+static int hda_audio_init_micro(HDACodecDevice *hda)
+{
+    return hda_audio_init(hda, &micro);
+}
+
+static void hda_audio_output_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
+
+    k->init = hda_audio_init_output;
+    k->exit = hda_audio_exit;
+    k->command = hda_audio_command;
+    k->stream = hda_audio_stream;
+    dc->desc = "HDA Audio Codec, output-only (line-out)";
+    dc->vmsd = &vmstate_hda_audio;
+    dc->props = hda_audio_properties;
+}
+
+static const TypeInfo hda_audio_output_info = {
+    .name          = "hda-output",
+    .parent        = TYPE_HDA_CODEC_DEVICE,
+    .instance_size = sizeof(HDAAudioState),
+    .class_init    = hda_audio_output_class_init,
+};
+
+static void hda_audio_duplex_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
+
+    k->init = hda_audio_init_duplex;
+    k->exit = hda_audio_exit;
+    k->command = hda_audio_command;
+    k->stream = hda_audio_stream;
+    dc->desc = "HDA Audio Codec, duplex (line-out, line-in)";
+    dc->vmsd = &vmstate_hda_audio;
+    dc->props = hda_audio_properties;
+}
+
+static const TypeInfo hda_audio_duplex_info = {
+    .name          = "hda-duplex",
+    .parent        = TYPE_HDA_CODEC_DEVICE,
+    .instance_size = sizeof(HDAAudioState),
+    .class_init    = hda_audio_duplex_class_init,
+};
+
+static void hda_audio_micro_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
+
+    k->init = hda_audio_init_micro;
+    k->exit = hda_audio_exit;
+    k->command = hda_audio_command;
+    k->stream = hda_audio_stream;
+    dc->desc = "HDA Audio Codec, duplex (speaker, microphone)";
+    dc->vmsd = &vmstate_hda_audio;
+    dc->props = hda_audio_properties;
+}
+
+static const TypeInfo hda_audio_micro_info = {
+    .name          = "hda-micro",
+    .parent        = TYPE_HDA_CODEC_DEVICE,
+    .instance_size = sizeof(HDAAudioState),
+    .class_init    = hda_audio_micro_class_init,
+};
+
+static void hda_audio_register_types(void)
+{
+    type_register_static(&hda_audio_output_info);
+    type_register_static(&hda_audio_duplex_info);
+    type_register_static(&hda_audio_micro_info);
+}
+
+type_init(hda_audio_register_types)
diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c
new file mode 100644 (file)
index 0000000..68201cd
--- /dev/null
@@ -0,0 +1,1329 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * written by Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/msi.h"
+#include "qemu/timer.h"
+#include "hw/audio/audio.h"
+#include "hw/intel-hda.h"
+#include "hw/intel-hda-defs.h"
+#include "sysemu/dma.h"
+
+/* --------------------------------------------------------------------- */
+/* hda bus                                                               */
+
+static Property hda_props[] = {
+    DEFINE_PROP_UINT32("cad", HDACodecDevice, cad, -1),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static const TypeInfo hda_codec_bus_info = {
+    .name = TYPE_HDA_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(HDACodecBus),
+};
+
+void hda_codec_bus_init(DeviceState *dev, HDACodecBus *bus,
+                        hda_codec_response_func response,
+                        hda_codec_xfer_func xfer)
+{
+    qbus_create_inplace(&bus->qbus, TYPE_HDA_BUS, dev, NULL);
+    bus->response = response;
+    bus->xfer = xfer;
+}
+
+static int hda_codec_dev_init(DeviceState *qdev)
+{
+    HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, qdev->parent_bus);
+    HDACodecDevice *dev = DO_UPCAST(HDACodecDevice, qdev, qdev);
+    HDACodecDeviceClass *cdc = HDA_CODEC_DEVICE_GET_CLASS(dev);
+
+    if (dev->cad == -1) {
+        dev->cad = bus->next_cad;
+    }
+    if (dev->cad >= 15) {
+        return -1;
+    }
+    bus->next_cad = dev->cad + 1;
+    return cdc->init(dev);
+}
+
+static int hda_codec_dev_exit(DeviceState *qdev)
+{
+    HDACodecDevice *dev = DO_UPCAST(HDACodecDevice, qdev, qdev);
+    HDACodecDeviceClass *cdc = HDA_CODEC_DEVICE_GET_CLASS(dev);
+
+    if (cdc->exit) {
+        cdc->exit(dev);
+    }
+    return 0;
+}
+
+HDACodecDevice *hda_codec_find(HDACodecBus *bus, uint32_t cad)
+{
+    BusChild *kid;
+    HDACodecDevice *cdev;
+
+    QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
+        DeviceState *qdev = kid->child;
+        cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
+        if (cdev->cad == cad) {
+            return cdev;
+        }
+    }
+    return NULL;
+}
+
+void hda_codec_response(HDACodecDevice *dev, bool solicited, uint32_t response)
+{
+    HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus);
+    bus->response(dev, solicited, response);
+}
+
+bool hda_codec_xfer(HDACodecDevice *dev, uint32_t stnr, bool output,
+                    uint8_t *buf, uint32_t len)
+{
+    HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus);
+    return bus->xfer(dev, stnr, output, buf, len);
+}
+
+/* --------------------------------------------------------------------- */
+/* intel hda emulation                                                   */
+
+typedef struct IntelHDAStream IntelHDAStream;
+typedef struct IntelHDAState IntelHDAState;
+typedef struct IntelHDAReg IntelHDAReg;
+
+typedef struct bpl {
+    uint64_t addr;
+    uint32_t len;
+    uint32_t flags;
+} bpl;
+
+struct IntelHDAStream {
+    /* registers */
+    uint32_t ctl;
+    uint32_t lpib;
+    uint32_t cbl;
+    uint32_t lvi;
+    uint32_t fmt;
+    uint32_t bdlp_lbase;
+    uint32_t bdlp_ubase;
+
+    /* state */
+    bpl      *bpl;
+    uint32_t bentries;
+    uint32_t bsize, be, bp;
+};
+
+struct IntelHDAState {
+    PCIDevice pci;
+    const char *name;
+    HDACodecBus codecs;
+
+    /* registers */
+    uint32_t g_ctl;
+    uint32_t wake_en;
+    uint32_t state_sts;
+    uint32_t int_ctl;
+    uint32_t int_sts;
+    uint32_t wall_clk;
+
+    uint32_t corb_lbase;
+    uint32_t corb_ubase;
+    uint32_t corb_rp;
+    uint32_t corb_wp;
+    uint32_t corb_ctl;
+    uint32_t corb_sts;
+    uint32_t corb_size;
+
+    uint32_t rirb_lbase;
+    uint32_t rirb_ubase;
+    uint32_t rirb_wp;
+    uint32_t rirb_cnt;
+    uint32_t rirb_ctl;
+    uint32_t rirb_sts;
+    uint32_t rirb_size;
+
+    uint32_t dp_lbase;
+    uint32_t dp_ubase;
+
+    uint32_t icw;
+    uint32_t irr;
+    uint32_t ics;
+
+    /* streams */
+    IntelHDAStream st[8];
+
+    /* state */
+    MemoryRegion mmio;
+    uint32_t rirb_count;
+    int64_t wall_base_ns;
+
+    /* debug logging */
+    const IntelHDAReg *last_reg;
+    uint32_t last_val;
+    uint32_t last_write;
+    uint32_t last_sec;
+    uint32_t repeat_count;
+
+    /* properties */
+    uint32_t debug;
+    uint32_t msi;
+};
+
+struct IntelHDAReg {
+    const char *name;      /* register name */
+    uint32_t   size;       /* size in bytes */
+    uint32_t   reset;      /* reset value */
+    uint32_t   wmask;      /* write mask */
+    uint32_t   wclear;     /* write 1 to clear bits */
+    uint32_t   offset;     /* location in IntelHDAState */
+    uint32_t   shift;      /* byte access entries for dwords */
+    uint32_t   stream;
+    void       (*whandler)(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old);
+    void       (*rhandler)(IntelHDAState *d, const IntelHDAReg *reg);
+};
+
+static void intel_hda_reset(DeviceState *dev);
+
+/* --------------------------------------------------------------------- */
+
+static hwaddr intel_hda_addr(uint32_t lbase, uint32_t ubase)
+{
+    hwaddr addr;
+
+    addr = ((uint64_t)ubase << 32) | lbase;
+    return addr;
+}
+
+static void intel_hda_update_int_sts(IntelHDAState *d)
+{
+    uint32_t sts = 0;
+    uint32_t i;
+
+    /* update controller status */
+    if (d->rirb_sts & ICH6_RBSTS_IRQ) {
+        sts |= (1 << 30);
+    }
+    if (d->rirb_sts & ICH6_RBSTS_OVERRUN) {
+        sts |= (1 << 30);
+    }
+    if (d->state_sts & d->wake_en) {
+        sts |= (1 << 30);
+    }
+
+    /* update stream status */
+    for (i = 0; i < 8; i++) {
+        /* buffer completion interrupt */
+        if (d->st[i].ctl & (1 << 26)) {
+            sts |= (1 << i);
+        }
+    }
+
+    /* update global status */
+    if (sts & d->int_ctl) {
+        sts |= (1 << 31);
+    }
+
+    d->int_sts = sts;
+}
+
+static void intel_hda_update_irq(IntelHDAState *d)
+{
+    int msi = d->msi && msi_enabled(&d->pci);
+    int level;
+
+    intel_hda_update_int_sts(d);
+    if (d->int_sts & (1 << 31) && d->int_ctl & (1 << 31)) {
+        level = 1;
+    } else {
+        level = 0;
+    }
+    dprint(d, 2, "%s: level %d [%s]\n", __FUNCTION__,
+           level, msi ? "msi" : "intx");
+    if (msi) {
+        if (level) {
+            msi_notify(&d->pci, 0);
+        }
+    } else {
+        qemu_set_irq(d->pci.irq[0], level);
+    }
+}
+
+static int intel_hda_send_command(IntelHDAState *d, uint32_t verb)
+{
+    uint32_t cad, nid, data;
+    HDACodecDevice *codec;
+    HDACodecDeviceClass *cdc;
+
+    cad = (verb >> 28) & 0x0f;
+    if (verb & (1 << 27)) {
+        /* indirect node addressing, not specified in HDA 1.0 */
+        dprint(d, 1, "%s: indirect node addressing (guest bug?)\n", __FUNCTION__);
+        return -1;
+    }
+    nid = (verb >> 20) & 0x7f;
+    data = verb & 0xfffff;
+
+    codec = hda_codec_find(&d->codecs, cad);
+    if (codec == NULL) {
+        dprint(d, 1, "%s: addressed non-existing codec\n", __FUNCTION__);
+        return -1;
+    }
+    cdc = HDA_CODEC_DEVICE_GET_CLASS(codec);
+    cdc->command(codec, nid, data);
+    return 0;
+}
+
+static void intel_hda_corb_run(IntelHDAState *d)
+{
+    hwaddr addr;
+    uint32_t rp, verb;
+
+    if (d->ics & ICH6_IRS_BUSY) {
+        dprint(d, 2, "%s: [icw] verb 0x%08x\n", __FUNCTION__, d->icw);
+        intel_hda_send_command(d, d->icw);
+        return;
+    }
+
+    for (;;) {
+        if (!(d->corb_ctl & ICH6_CORBCTL_RUN)) {
+            dprint(d, 2, "%s: !run\n", __FUNCTION__);
+            return;
+        }
+        if ((d->corb_rp & 0xff) == d->corb_wp) {
+            dprint(d, 2, "%s: corb ring empty\n", __FUNCTION__);
+            return;
+        }
+        if (d->rirb_count == d->rirb_cnt) {
+            dprint(d, 2, "%s: rirb count reached\n", __FUNCTION__);
+            return;
+        }
+
+        rp = (d->corb_rp + 1) & 0xff;
+        addr = intel_hda_addr(d->corb_lbase, d->corb_ubase);
+        verb = ldl_le_pci_dma(&d->pci, addr + 4*rp);
+        d->corb_rp = rp;
+
+        dprint(d, 2, "%s: [rp 0x%x] verb 0x%08x\n", __FUNCTION__, rp, verb);
+        intel_hda_send_command(d, verb);
+    }
+}
+
+static void intel_hda_response(HDACodecDevice *dev, bool solicited, uint32_t response)
+{
+    HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus);
+    IntelHDAState *d = container_of(bus, IntelHDAState, codecs);
+    hwaddr addr;
+    uint32_t wp, ex;
+
+    if (d->ics & ICH6_IRS_BUSY) {
+        dprint(d, 2, "%s: [irr] response 0x%x, cad 0x%x\n",
+               __FUNCTION__, response, dev->cad);
+        d->irr = response;
+        d->ics &= ~(ICH6_IRS_BUSY | 0xf0);
+        d->ics |= (ICH6_IRS_VALID | (dev->cad << 4));
+        return;
+    }
+
+    if (!(d->rirb_ctl & ICH6_RBCTL_DMA_EN)) {
+        dprint(d, 1, "%s: rirb dma disabled, drop codec response\n", __FUNCTION__);
+        return;
+    }
+
+    ex = (solicited ? 0 : (1 << 4)) | dev->cad;
+    wp = (d->rirb_wp + 1) & 0xff;
+    addr = intel_hda_addr(d->rirb_lbase, d->rirb_ubase);
+    stl_le_pci_dma(&d->pci, addr + 8*wp, response);
+    stl_le_pci_dma(&d->pci, addr + 8*wp + 4, ex);
+    d->rirb_wp = wp;
+
+    dprint(d, 2, "%s: [wp 0x%x] response 0x%x, extra 0x%x\n",
+           __FUNCTION__, wp, response, ex);
+
+    d->rirb_count++;
+    if (d->rirb_count == d->rirb_cnt) {
+        dprint(d, 2, "%s: rirb count reached (%d)\n", __FUNCTION__, d->rirb_count);
+        if (d->rirb_ctl & ICH6_RBCTL_IRQ_EN) {
+            d->rirb_sts |= ICH6_RBSTS_IRQ;
+            intel_hda_update_irq(d);
+        }
+    } else if ((d->corb_rp & 0xff) == d->corb_wp) {
+        dprint(d, 2, "%s: corb ring empty (%d/%d)\n", __FUNCTION__,
+               d->rirb_count, d->rirb_cnt);
+        if (d->rirb_ctl & ICH6_RBCTL_IRQ_EN) {
+            d->rirb_sts |= ICH6_RBSTS_IRQ;
+            intel_hda_update_irq(d);
+        }
+    }
+}
+
+static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output,
+                           uint8_t *buf, uint32_t len)
+{
+    HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus);
+    IntelHDAState *d = container_of(bus, IntelHDAState, codecs);
+    hwaddr addr;
+    uint32_t s, copy, left;
+    IntelHDAStream *st;
+    bool irq = false;
+
+    st = output ? d->st + 4 : d->st;
+    for (s = 0; s < 4; s++) {
+        if (stnr == ((st[s].ctl >> 20) & 0x0f)) {
+            st = st + s;
+            break;
+        }
+    }
+    if (s == 4) {
+        return false;
+    }
+    if (st->bpl == NULL) {
+        return false;
+    }
+    if (st->ctl & (1 << 26)) {
+        /*
+         * Wait with the next DMA xfer until the guest
+         * has acked the buffer completion interrupt
+         */
+        return false;
+    }
+
+    left = len;
+    while (left > 0) {
+        copy = left;
+        if (copy > st->bsize - st->lpib)
+            copy = st->bsize - st->lpib;
+        if (copy > st->bpl[st->be].len - st->bp)
+            copy = st->bpl[st->be].len - st->bp;
+
+        dprint(d, 3, "dma: entry %d, pos %d/%d, copy %d\n",
+               st->be, st->bp, st->bpl[st->be].len, copy);
+
+        pci_dma_rw(&d->pci, st->bpl[st->be].addr + st->bp, buf, copy, !output);
+        st->lpib += copy;
+        st->bp += copy;
+        buf += copy;
+        left -= copy;
+
+        if (st->bpl[st->be].len == st->bp) {
+            /* bpl entry filled */
+            if (st->bpl[st->be].flags & 0x01) {
+                irq = true;
+            }
+            st->bp = 0;
+            st->be++;
+            if (st->be == st->bentries) {
+                /* bpl wrap around */
+                st->be = 0;
+                st->lpib = 0;
+            }
+        }
+    }
+    if (d->dp_lbase & 0x01) {
+        addr = intel_hda_addr(d->dp_lbase & ~0x01, d->dp_ubase);
+        stl_le_pci_dma(&d->pci, addr + 8*s, st->lpib);
+    }
+    dprint(d, 3, "dma: --\n");
+
+    if (irq) {
+        st->ctl |= (1 << 26); /* buffer completion interrupt */
+        intel_hda_update_irq(d);
+    }
+    return true;
+}
+
+static void intel_hda_parse_bdl(IntelHDAState *d, IntelHDAStream *st)
+{
+    hwaddr addr;
+    uint8_t buf[16];
+    uint32_t i;
+
+    addr = intel_hda_addr(st->bdlp_lbase, st->bdlp_ubase);
+    st->bentries = st->lvi +1;
+    g_free(st->bpl);
+    st->bpl = g_malloc(sizeof(bpl) * st->bentries);
+    for (i = 0; i < st->bentries; i++, addr += 16) {
+        pci_dma_read(&d->pci, addr, buf, 16);
+        st->bpl[i].addr  = le64_to_cpu(*(uint64_t *)buf);
+        st->bpl[i].len   = le32_to_cpu(*(uint32_t *)(buf + 8));
+        st->bpl[i].flags = le32_to_cpu(*(uint32_t *)(buf + 12));
+        dprint(d, 1, "bdl/%d: 0x%" PRIx64 " +0x%x, 0x%x\n",
+               i, st->bpl[i].addr, st->bpl[i].len, st->bpl[i].flags);
+    }
+
+    st->bsize = st->cbl;
+    st->lpib  = 0;
+    st->be    = 0;
+    st->bp    = 0;
+}
+
+static void intel_hda_notify_codecs(IntelHDAState *d, uint32_t stream, bool running, bool output)
+{
+    BusChild *kid;
+    HDACodecDevice *cdev;
+
+    QTAILQ_FOREACH(kid, &d->codecs.qbus.children, sibling) {
+        DeviceState *qdev = kid->child;
+        HDACodecDeviceClass *cdc;
+
+        cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
+        cdc = HDA_CODEC_DEVICE_GET_CLASS(cdev);
+        if (cdc->stream) {
+            cdc->stream(cdev, stream, running, output);
+        }
+    }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void intel_hda_set_g_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+    if ((d->g_ctl & ICH6_GCTL_RESET) == 0) {
+        intel_hda_reset(&d->pci.qdev);
+    }
+}
+
+static void intel_hda_set_wake_en(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+    intel_hda_update_irq(d);
+}
+
+static void intel_hda_set_state_sts(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+    intel_hda_update_irq(d);
+}
+
+static void intel_hda_set_int_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+    intel_hda_update_irq(d);
+}
+
+static void intel_hda_get_wall_clk(IntelHDAState *d, const IntelHDAReg *reg)
+{
+    int64_t ns;
+
+    ns = qemu_get_clock_ns(vm_clock) - d->wall_base_ns;
+    d->wall_clk = (uint32_t)(ns * 24 / 1000);  /* 24 MHz */
+}
+
+static void intel_hda_set_corb_wp(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+    intel_hda_corb_run(d);
+}
+
+static void intel_hda_set_corb_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+    intel_hda_corb_run(d);
+}
+
+static void intel_hda_set_rirb_wp(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+    if (d->rirb_wp & ICH6_RIRBWP_RST) {
+        d->rirb_wp = 0;
+    }
+}
+
+static void intel_hda_set_rirb_sts(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+    intel_hda_update_irq(d);
+
+    if ((old & ICH6_RBSTS_IRQ) && !(d->rirb_sts & ICH6_RBSTS_IRQ)) {
+        /* cleared ICH6_RBSTS_IRQ */
+        d->rirb_count = 0;
+        intel_hda_corb_run(d);
+    }
+}
+
+static void intel_hda_set_ics(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+    if (d->ics & ICH6_IRS_BUSY) {
+        intel_hda_corb_run(d);
+    }
+}
+
+static void intel_hda_set_st_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
+{
+    bool output = reg->stream >= 4;
+    IntelHDAStream *st = d->st + reg->stream;
+
+    if (st->ctl & 0x01) {
+        /* reset */
+        dprint(d, 1, "st #%d: reset\n", reg->stream);
+        st->ctl = 0;
+    }
+    if ((st->ctl & 0x02) != (old & 0x02)) {
+        uint32_t stnr = (st->ctl >> 20) & 0x0f;
+        /* run bit flipped */
+        if (st->ctl & 0x02) {
+            /* start */
+            dprint(d, 1, "st #%d: start %d (ring buf %d bytes)\n",
+                   reg->stream, stnr, st->cbl);
+            intel_hda_parse_bdl(d, st);
+            intel_hda_notify_codecs(d, stnr, true, output);
+        } else {
+            /* stop */
+            dprint(d, 1, "st #%d: stop %d\n", reg->stream, stnr);
+            intel_hda_notify_codecs(d, stnr, false, output);
+        }
+    }
+    intel_hda_update_irq(d);
+}
+
+/* --------------------------------------------------------------------- */
+
+#define ST_REG(_n, _o) (0x80 + (_n) * 0x20 + (_o))
+
+static const struct IntelHDAReg regtab[] = {
+    /* global */
+    [ ICH6_REG_GCAP ] = {
+        .name     = "GCAP",
+        .size     = 2,
+        .reset    = 0x4401,
+    },
+    [ ICH6_REG_VMIN ] = {
+        .name     = "VMIN",
+        .size     = 1,
+    },
+    [ ICH6_REG_VMAJ ] = {
+        .name     = "VMAJ",
+        .size     = 1,
+        .reset    = 1,
+    },
+    [ ICH6_REG_OUTPAY ] = {
+        .name     = "OUTPAY",
+        .size     = 2,
+        .reset    = 0x3c,
+    },
+    [ ICH6_REG_INPAY ] = {
+        .name     = "INPAY",
+        .size     = 2,
+        .reset    = 0x1d,
+    },
+    [ ICH6_REG_GCTL ] = {
+        .name     = "GCTL",
+        .size     = 4,
+        .wmask    = 0x0103,
+        .offset   = offsetof(IntelHDAState, g_ctl),
+        .whandler = intel_hda_set_g_ctl,
+    },
+    [ ICH6_REG_WAKEEN ] = {
+        .name     = "WAKEEN",
+        .size     = 2,
+        .wmask    = 0x7fff,
+        .offset   = offsetof(IntelHDAState, wake_en),
+        .whandler = intel_hda_set_wake_en,
+    },
+    [ ICH6_REG_STATESTS ] = {
+        .name     = "STATESTS",
+        .size     = 2,
+        .wmask    = 0x7fff,
+        .wclear   = 0x7fff,
+        .offset   = offsetof(IntelHDAState, state_sts),
+        .whandler = intel_hda_set_state_sts,
+    },
+
+    /* interrupts */
+    [ ICH6_REG_INTCTL ] = {
+        .name     = "INTCTL",
+        .size     = 4,
+        .wmask    = 0xc00000ff,
+        .offset   = offsetof(IntelHDAState, int_ctl),
+        .whandler = intel_hda_set_int_ctl,
+    },
+    [ ICH6_REG_INTSTS ] = {
+        .name     = "INTSTS",
+        .size     = 4,
+        .wmask    = 0xc00000ff,
+        .wclear   = 0xc00000ff,
+        .offset   = offsetof(IntelHDAState, int_sts),
+    },
+
+    /* misc */
+    [ ICH6_REG_WALLCLK ] = {
+        .name     = "WALLCLK",
+        .size     = 4,
+        .offset   = offsetof(IntelHDAState, wall_clk),
+        .rhandler = intel_hda_get_wall_clk,
+    },
+    [ ICH6_REG_WALLCLK + 0x2000 ] = {
+        .name     = "WALLCLK(alias)",
+        .size     = 4,
+        .offset   = offsetof(IntelHDAState, wall_clk),
+        .rhandler = intel_hda_get_wall_clk,
+    },
+
+    /* dma engine */
+    [ ICH6_REG_CORBLBASE ] = {
+        .name     = "CORBLBASE",
+        .size     = 4,
+        .wmask    = 0xffffff80,
+        .offset   = offsetof(IntelHDAState, corb_lbase),
+    },
+    [ ICH6_REG_CORBUBASE ] = {
+        .name     = "CORBUBASE",
+        .size     = 4,
+        .wmask    = 0xffffffff,
+        .offset   = offsetof(IntelHDAState, corb_ubase),
+    },
+    [ ICH6_REG_CORBWP ] = {
+        .name     = "CORBWP",
+        .size     = 2,
+        .wmask    = 0xff,
+        .offset   = offsetof(IntelHDAState, corb_wp),
+        .whandler = intel_hda_set_corb_wp,
+    },
+    [ ICH6_REG_CORBRP ] = {
+        .name     = "CORBRP",
+        .size     = 2,
+        .wmask    = 0x80ff,
+        .offset   = offsetof(IntelHDAState, corb_rp),
+    },
+    [ ICH6_REG_CORBCTL ] = {
+        .name     = "CORBCTL",
+        .size     = 1,
+        .wmask    = 0x03,
+        .offset   = offsetof(IntelHDAState, corb_ctl),
+        .whandler = intel_hda_set_corb_ctl,
+    },
+    [ ICH6_REG_CORBSTS ] = {
+        .name     = "CORBSTS",
+        .size     = 1,
+        .wmask    = 0x01,
+        .wclear   = 0x01,
+        .offset   = offsetof(IntelHDAState, corb_sts),
+    },
+    [ ICH6_REG_CORBSIZE ] = {
+        .name     = "CORBSIZE",
+        .size     = 1,
+        .reset    = 0x42,
+        .offset   = offsetof(IntelHDAState, corb_size),
+    },
+    [ ICH6_REG_RIRBLBASE ] = {
+        .name     = "RIRBLBASE",
+        .size     = 4,
+        .wmask    = 0xffffff80,
+        .offset   = offsetof(IntelHDAState, rirb_lbase),
+    },
+    [ ICH6_REG_RIRBUBASE ] = {
+        .name     = "RIRBUBASE",
+        .size     = 4,
+        .wmask    = 0xffffffff,
+        .offset   = offsetof(IntelHDAState, rirb_ubase),
+    },
+    [ ICH6_REG_RIRBWP ] = {
+        .name     = "RIRBWP",
+        .size     = 2,
+        .wmask    = 0x8000,
+        .offset   = offsetof(IntelHDAState, rirb_wp),
+        .whandler = intel_hda_set_rirb_wp,
+    },
+    [ ICH6_REG_RINTCNT ] = {
+        .name     = "RINTCNT",
+        .size     = 2,
+        .wmask    = 0xff,
+        .offset   = offsetof(IntelHDAState, rirb_cnt),
+    },
+    [ ICH6_REG_RIRBCTL ] = {
+        .name     = "RIRBCTL",
+        .size     = 1,
+        .wmask    = 0x07,
+        .offset   = offsetof(IntelHDAState, rirb_ctl),
+    },
+    [ ICH6_REG_RIRBSTS ] = {
+        .name     = "RIRBSTS",
+        .size     = 1,
+        .wmask    = 0x05,
+        .wclear   = 0x05,
+        .offset   = offsetof(IntelHDAState, rirb_sts),
+        .whandler = intel_hda_set_rirb_sts,
+    },
+    [ ICH6_REG_RIRBSIZE ] = {
+        .name     = "RIRBSIZE",
+        .size     = 1,
+        .reset    = 0x42,
+        .offset   = offsetof(IntelHDAState, rirb_size),
+    },
+
+    [ ICH6_REG_DPLBASE ] = {
+        .name     = "DPLBASE",
+        .size     = 4,
+        .wmask    = 0xffffff81,
+        .offset   = offsetof(IntelHDAState, dp_lbase),
+    },
+    [ ICH6_REG_DPUBASE ] = {
+        .name     = "DPUBASE",
+        .size     = 4,
+        .wmask    = 0xffffffff,
+        .offset   = offsetof(IntelHDAState, dp_ubase),
+    },
+
+    [ ICH6_REG_IC ] = {
+        .name     = "ICW",
+        .size     = 4,
+        .wmask    = 0xffffffff,
+        .offset   = offsetof(IntelHDAState, icw),
+    },
+    [ ICH6_REG_IR ] = {
+        .name     = "IRR",
+        .size     = 4,
+        .offset   = offsetof(IntelHDAState, irr),
+    },
+    [ ICH6_REG_IRS ] = {
+        .name     = "ICS",
+        .size     = 2,
+        .wmask    = 0x0003,
+        .wclear   = 0x0002,
+        .offset   = offsetof(IntelHDAState, ics),
+        .whandler = intel_hda_set_ics,
+    },
+
+#define HDA_STREAM(_t, _i)                                            \
+    [ ST_REG(_i, ICH6_REG_SD_CTL) ] = {                               \
+        .stream   = _i,                                               \
+        .name     = _t stringify(_i) " CTL",                          \
+        .size     = 4,                                                \
+        .wmask    = 0x1cff001f,                                       \
+        .offset   = offsetof(IntelHDAState, st[_i].ctl),              \
+        .whandler = intel_hda_set_st_ctl,                             \
+    },                                                                \
+    [ ST_REG(_i, ICH6_REG_SD_CTL) + 2] = {                            \
+        .stream   = _i,                                               \
+        .name     = _t stringify(_i) " CTL(stnr)",                    \
+        .size     = 1,                                                \
+        .shift    = 16,                                               \
+        .wmask    = 0x00ff0000,                                       \
+        .offset   = offsetof(IntelHDAState, st[_i].ctl),              \
+        .whandler = intel_hda_set_st_ctl,                             \
+    },                                                                \
+    [ ST_REG(_i, ICH6_REG_SD_STS)] = {                                \
+        .stream   = _i,                                               \
+        .name     = _t stringify(_i) " CTL(sts)",                     \
+        .size     = 1,                                                \
+        .shift    = 24,                                               \
+        .wmask    = 0x1c000000,                                       \
+        .wclear   = 0x1c000000,                                       \
+        .offset   = offsetof(IntelHDAState, st[_i].ctl),              \
+        .whandler = intel_hda_set_st_ctl,                             \
+    },                                                                \
+    [ ST_REG(_i, ICH6_REG_SD_LPIB) ] = {                              \
+        .stream   = _i,                                               \
+        .name     = _t stringify(_i) " LPIB",                         \
+        .size     = 4,                                                \
+        .offset   = offsetof(IntelHDAState, st[_i].lpib),             \
+    },                                                                \
+    [ ST_REG(_i, ICH6_REG_SD_LPIB) + 0x2000 ] = {                     \
+        .stream   = _i,                                               \
+        .name     = _t stringify(_i) " LPIB(alias)",                  \
+        .size     = 4,                                                \
+        .offset   = offsetof(IntelHDAState, st[_i].lpib),             \
+    },                                                                \
+    [ ST_REG(_i, ICH6_REG_SD_CBL) ] = {                               \
+        .stream   = _i,                                               \
+        .name     = _t stringify(_i) " CBL",                          \
+        .size     = 4,                                                \
+        .wmask    = 0xffffffff,                                       \
+        .offset   = offsetof(IntelHDAState, st[_i].cbl),              \
+    },                                                                \
+    [ ST_REG(_i, ICH6_REG_SD_LVI) ] = {                               \
+        .stream   = _i,                                               \
+        .name     = _t stringify(_i) " LVI",                          \
+        .size     = 2,                                                \
+        .wmask    = 0x00ff,                                           \
+        .offset   = offsetof(IntelHDAState, st[_i].lvi),              \
+    },                                                                \
+    [ ST_REG(_i, ICH6_REG_SD_FIFOSIZE) ] = {                          \
+        .stream   = _i,                                               \
+        .name     = _t stringify(_i) " FIFOS",                        \
+        .size     = 2,                                                \
+        .reset    = HDA_BUFFER_SIZE,                                  \
+    },                                                                \
+    [ ST_REG(_i, ICH6_REG_SD_FORMAT) ] = {                            \
+        .stream   = _i,                                               \
+        .name     = _t stringify(_i) " FMT",                          \
+        .size     = 2,                                                \
+        .wmask    = 0x7f7f,                                           \
+        .offset   = offsetof(IntelHDAState, st[_i].fmt),              \
+    },                                                                \
+    [ ST_REG(_i, ICH6_REG_SD_BDLPL) ] = {                             \
+        .stream   = _i,                                               \
+        .name     = _t stringify(_i) " BDLPL",                        \
+        .size     = 4,                                                \
+        .wmask    = 0xffffff80,                                       \
+        .offset   = offsetof(IntelHDAState, st[_i].bdlp_lbase),       \
+    },                                                                \
+    [ ST_REG(_i, ICH6_REG_SD_BDLPU) ] = {                             \
+        .stream   = _i,                                               \
+        .name     = _t stringify(_i) " BDLPU",                        \
+        .size     = 4,                                                \
+        .wmask    = 0xffffffff,                                       \
+        .offset   = offsetof(IntelHDAState, st[_i].bdlp_ubase),       \
+    },                                                                \
+
+    HDA_STREAM("IN", 0)
+    HDA_STREAM("IN", 1)
+    HDA_STREAM("IN", 2)
+    HDA_STREAM("IN", 3)
+
+    HDA_STREAM("OUT", 4)
+    HDA_STREAM("OUT", 5)
+    HDA_STREAM("OUT", 6)
+    HDA_STREAM("OUT", 7)
+
+};
+
+static const IntelHDAReg *intel_hda_reg_find(IntelHDAState *d, hwaddr addr)
+{
+    const IntelHDAReg *reg;
+
+    if (addr >= sizeof(regtab)/sizeof(regtab[0])) {
+        goto noreg;
+    }
+    reg = regtab+addr;
+    if (reg->name == NULL) {
+        goto noreg;
+    }
+    return reg;
+
+noreg:
+    dprint(d, 1, "unknown register, addr 0x%x\n", (int) addr);
+    return NULL;
+}
+
+static uint32_t *intel_hda_reg_addr(IntelHDAState *d, const IntelHDAReg *reg)
+{
+    uint8_t *addr = (void*)d;
+
+    addr += reg->offset;
+    return (uint32_t*)addr;
+}
+
+static void intel_hda_reg_write(IntelHDAState *d, const IntelHDAReg *reg, uint32_t val,
+                                uint32_t wmask)
+{
+    uint32_t *addr;
+    uint32_t old;
+
+    if (!reg) {
+        return;
+    }
+
+    if (d->debug) {
+        time_t now = time(NULL);
+        if (d->last_write && d->last_reg == reg && d->last_val == val) {
+            d->repeat_count++;
+            if (d->last_sec != now) {
+                dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
+                d->last_sec = now;
+                d->repeat_count = 0;
+            }
+        } else {
+            if (d->repeat_count) {
+                dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
+            }
+            dprint(d, 2, "write %-16s: 0x%x (%x)\n", reg->name, val, wmask);
+            d->last_write = 1;
+            d->last_reg   = reg;
+            d->last_val   = val;
+            d->last_sec   = now;
+            d->repeat_count = 0;
+        }
+    }
+    assert(reg->offset != 0);
+
+    addr = intel_hda_reg_addr(d, reg);
+    old = *addr;
+
+    if (reg->shift) {
+        val <<= reg->shift;
+        wmask <<= reg->shift;
+    }
+    wmask &= reg->wmask;
+    *addr &= ~wmask;
+    *addr |= wmask & val;
+    *addr &= ~(val & reg->wclear);
+
+    if (reg->whandler) {
+        reg->whandler(d, reg, old);
+    }
+}
+
+static uint32_t intel_hda_reg_read(IntelHDAState *d, const IntelHDAReg *reg,
+                                   uint32_t rmask)
+{
+    uint32_t *addr, ret;
+
+    if (!reg) {
+        return 0;
+    }
+
+    if (reg->rhandler) {
+        reg->rhandler(d, reg);
+    }
+
+    if (reg->offset == 0) {
+        /* constant read-only register */
+        ret = reg->reset;
+    } else {
+        addr = intel_hda_reg_addr(d, reg);
+        ret = *addr;
+        if (reg->shift) {
+            ret >>= reg->shift;
+        }
+        ret &= rmask;
+    }
+    if (d->debug) {
+        time_t now = time(NULL);
+        if (!d->last_write && d->last_reg == reg && d->last_val == ret) {
+            d->repeat_count++;
+            if (d->last_sec != now) {
+                dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
+                d->last_sec = now;
+                d->repeat_count = 0;
+            }
+        } else {
+            if (d->repeat_count) {
+                dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
+            }
+            dprint(d, 2, "read  %-16s: 0x%x (%x)\n", reg->name, ret, rmask);
+            d->last_write = 0;
+            d->last_reg   = reg;
+            d->last_val   = ret;
+            d->last_sec   = now;
+            d->repeat_count = 0;
+        }
+    }
+    return ret;
+}
+
+static void intel_hda_regs_reset(IntelHDAState *d)
+{
+    uint32_t *addr;
+    int i;
+
+    for (i = 0; i < sizeof(regtab)/sizeof(regtab[0]); i++) {
+        if (regtab[i].name == NULL) {
+            continue;
+        }
+        if (regtab[i].offset == 0) {
+            continue;
+        }
+        addr = intel_hda_reg_addr(d, regtab + i);
+        *addr = regtab[i].reset;
+    }
+}
+
+/* --------------------------------------------------------------------- */
+
+static void intel_hda_mmio_writeb(void *opaque, hwaddr addr, uint32_t val)
+{
+    IntelHDAState *d = opaque;
+    const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+    intel_hda_reg_write(d, reg, val, 0xff);
+}
+
+static void intel_hda_mmio_writew(void *opaque, hwaddr addr, uint32_t val)
+{
+    IntelHDAState *d = opaque;
+    const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+    intel_hda_reg_write(d, reg, val, 0xffff);
+}
+
+static void intel_hda_mmio_writel(void *opaque, hwaddr addr, uint32_t val)
+{
+    IntelHDAState *d = opaque;
+    const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+    intel_hda_reg_write(d, reg, val, 0xffffffff);
+}
+
+static uint32_t intel_hda_mmio_readb(void *opaque, hwaddr addr)
+{
+    IntelHDAState *d = opaque;
+    const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+    return intel_hda_reg_read(d, reg, 0xff);
+}
+
+static uint32_t intel_hda_mmio_readw(void *opaque, hwaddr addr)
+{
+    IntelHDAState *d = opaque;
+    const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+    return intel_hda_reg_read(d, reg, 0xffff);
+}
+
+static uint32_t intel_hda_mmio_readl(void *opaque, hwaddr addr)
+{
+    IntelHDAState *d = opaque;
+    const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
+
+    return intel_hda_reg_read(d, reg, 0xffffffff);
+}
+
+static const MemoryRegionOps intel_hda_mmio_ops = {
+    .old_mmio = {
+        .read = {
+            intel_hda_mmio_readb,
+            intel_hda_mmio_readw,
+            intel_hda_mmio_readl,
+        },
+        .write = {
+            intel_hda_mmio_writeb,
+            intel_hda_mmio_writew,
+            intel_hda_mmio_writel,
+        },
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/* --------------------------------------------------------------------- */
+
+static void intel_hda_reset(DeviceState *dev)
+{
+    BusChild *kid;
+    IntelHDAState *d = DO_UPCAST(IntelHDAState, pci.qdev, dev);
+    HDACodecDevice *cdev;
+
+    intel_hda_regs_reset(d);
+    d->wall_base_ns = qemu_get_clock_ns(vm_clock);
+
+    /* reset codecs */
+    QTAILQ_FOREACH(kid, &d->codecs.qbus.children, sibling) {
+        DeviceState *qdev = kid->child;
+        cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
+        device_reset(DEVICE(cdev));
+        d->state_sts |= (1 << cdev->cad);
+    }
+    intel_hda_update_irq(d);
+}
+
+static int intel_hda_init(PCIDevice *pci)
+{
+    IntelHDAState *d = DO_UPCAST(IntelHDAState, pci, pci);
+    uint8_t *conf = d->pci.config;
+
+    d->name = object_get_typename(OBJECT(d));
+
+    pci_config_set_interrupt_pin(conf, 1);
+
+    /* HDCTL off 0x40 bit 0 selects signaling mode (1-HDA, 0 - Ac97) 18.1.19 */
+    conf[0x40] = 0x01;
+
+    memory_region_init_io(&d->mmio, &intel_hda_mmio_ops, d,
+                          "intel-hda", 0x4000);
+    pci_register_bar(&d->pci, 0, 0, &d->mmio);
+    if (d->msi) {
+        msi_init(&d->pci, 0x50, 1, true, false);
+    }
+
+    hda_codec_bus_init(&d->pci.qdev, &d->codecs,
+                       intel_hda_response, intel_hda_xfer);
+
+    return 0;
+}
+
+static void intel_hda_exit(PCIDevice *pci)
+{
+    IntelHDAState *d = DO_UPCAST(IntelHDAState, pci, pci);
+
+    msi_uninit(&d->pci);
+    memory_region_destroy(&d->mmio);
+}
+
+static int intel_hda_post_load(void *opaque, int version)
+{
+    IntelHDAState* d = opaque;
+    int i;
+
+    dprint(d, 1, "%s\n", __FUNCTION__);
+    for (i = 0; i < ARRAY_SIZE(d->st); i++) {
+        if (d->st[i].ctl & 0x02) {
+            intel_hda_parse_bdl(d, &d->st[i]);
+        }
+    }
+    intel_hda_update_irq(d);
+    return 0;
+}
+
+static const VMStateDescription vmstate_intel_hda_stream = {
+    .name = "intel-hda-stream",
+    .version_id = 1,
+    .fields = (VMStateField []) {
+        VMSTATE_UINT32(ctl, IntelHDAStream),
+        VMSTATE_UINT32(lpib, IntelHDAStream),
+        VMSTATE_UINT32(cbl, IntelHDAStream),
+        VMSTATE_UINT32(lvi, IntelHDAStream),
+        VMSTATE_UINT32(fmt, IntelHDAStream),
+        VMSTATE_UINT32(bdlp_lbase, IntelHDAStream),
+        VMSTATE_UINT32(bdlp_ubase, IntelHDAStream),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_intel_hda = {
+    .name = "intel-hda",
+    .version_id = 1,
+    .post_load = intel_hda_post_load,
+    .fields = (VMStateField []) {
+        VMSTATE_PCI_DEVICE(pci, IntelHDAState),
+
+        /* registers */
+        VMSTATE_UINT32(g_ctl, IntelHDAState),
+        VMSTATE_UINT32(wake_en, IntelHDAState),
+        VMSTATE_UINT32(state_sts, IntelHDAState),
+        VMSTATE_UINT32(int_ctl, IntelHDAState),
+        VMSTATE_UINT32(int_sts, IntelHDAState),
+        VMSTATE_UINT32(wall_clk, IntelHDAState),
+        VMSTATE_UINT32(corb_lbase, IntelHDAState),
+        VMSTATE_UINT32(corb_ubase, IntelHDAState),
+        VMSTATE_UINT32(corb_rp, IntelHDAState),
+        VMSTATE_UINT32(corb_wp, IntelHDAState),
+        VMSTATE_UINT32(corb_ctl, IntelHDAState),
+        VMSTATE_UINT32(corb_sts, IntelHDAState),
+        VMSTATE_UINT32(corb_size, IntelHDAState),
+        VMSTATE_UINT32(rirb_lbase, IntelHDAState),
+        VMSTATE_UINT32(rirb_ubase, IntelHDAState),
+        VMSTATE_UINT32(rirb_wp, IntelHDAState),
+        VMSTATE_UINT32(rirb_cnt, IntelHDAState),
+        VMSTATE_UINT32(rirb_ctl, IntelHDAState),
+        VMSTATE_UINT32(rirb_sts, IntelHDAState),
+        VMSTATE_UINT32(rirb_size, IntelHDAState),
+        VMSTATE_UINT32(dp_lbase, IntelHDAState),
+        VMSTATE_UINT32(dp_ubase, IntelHDAState),
+        VMSTATE_UINT32(icw, IntelHDAState),
+        VMSTATE_UINT32(irr, IntelHDAState),
+        VMSTATE_UINT32(ics, IntelHDAState),
+        VMSTATE_STRUCT_ARRAY(st, IntelHDAState, 8, 0,
+                             vmstate_intel_hda_stream,
+                             IntelHDAStream),
+
+        /* additional state info */
+        VMSTATE_UINT32(rirb_count, IntelHDAState),
+        VMSTATE_INT64(wall_base_ns, IntelHDAState),
+
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property intel_hda_properties[] = {
+    DEFINE_PROP_UINT32("debug", IntelHDAState, debug, 0),
+    DEFINE_PROP_UINT32("msi", IntelHDAState, msi, 1),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void intel_hda_class_init_common(ObjectClass *klass)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = intel_hda_init;
+    k->exit = intel_hda_exit;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->class_id = PCI_CLASS_MULTIMEDIA_HD_AUDIO;
+    dc->reset = intel_hda_reset;
+    dc->vmsd = &vmstate_intel_hda;
+    dc->props = intel_hda_properties;
+}
+
+static void intel_hda_class_init_ich6(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    intel_hda_class_init_common(klass);
+    k->device_id = 0x2668;
+    k->revision = 1;
+    dc->desc = "Intel HD Audio Controller (ich6)";
+}
+
+static void intel_hda_class_init_ich9(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    intel_hda_class_init_common(klass);
+    k->device_id = 0x293e;
+    k->revision = 3;
+    dc->desc = "Intel HD Audio Controller (ich9)";
+}
+
+static const TypeInfo intel_hda_info_ich6 = {
+    .name          = "intel-hda",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(IntelHDAState),
+    .class_init    = intel_hda_class_init_ich6,
+};
+
+static const TypeInfo intel_hda_info_ich9 = {
+    .name          = "ich9-intel-hda",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(IntelHDAState),
+    .class_init    = intel_hda_class_init_ich9,
+};
+
+static void hda_codec_device_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *k = DEVICE_CLASS(klass);
+    k->init = hda_codec_dev_init;
+    k->exit = hda_codec_dev_exit;
+    k->bus_type = TYPE_HDA_BUS;
+    k->props = hda_props;
+}
+
+static const TypeInfo hda_codec_device_type_info = {
+    .name = TYPE_HDA_CODEC_DEVICE,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(HDACodecDevice),
+    .abstract = true,
+    .class_size = sizeof(HDACodecDeviceClass),
+    .class_init = hda_codec_device_class_init,
+};
+
+static void intel_hda_register_types(void)
+{
+    type_register_static(&hda_codec_bus_info);
+    type_register_static(&intel_hda_info_ich6);
+    type_register_static(&intel_hda_info_ich9);
+    type_register_static(&hda_codec_device_type_info);
+}
+
+type_init(intel_hda_register_types)
+
+/*
+ * create intel hda controller with codec attached to it,
+ * so '-soundhw hda' works.
+ */
+int intel_hda_and_codec_init(PCIBus *bus)
+{
+    PCIDevice *controller;
+    BusState *hdabus;
+    DeviceState *codec;
+
+    controller = pci_create_simple(bus, -1, "intel-hda");
+    hdabus = QLIST_FIRST(&controller->qdev.child_bus);
+    codec = qdev_create(hdabus, "hda-duplex");
+    qdev_init_nofail(codec);
+    return 0;
+}
+
diff --git a/hw/audio/lm4549.c b/hw/audio/lm4549.c
new file mode 100644 (file)
index 0000000..67335cb
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * LM4549 Audio Codec Interface
+ *
+ * Copyright (c) 2011
+ * Written by Mathieu Sonet - www.elasticsheep.com
+ *
+ * This code is licensed under the GPL.
+ *
+ * *****************************************************************
+ *
+ * This driver emulates the LM4549 codec.
+ *
+ * It supports only one playback voice and no record voice.
+ */
+
+#include "hw/hw.h"
+#include "audio/audio.h"
+#include "hw/lm4549.h"
+
+#if 0
+#define LM4549_DEBUG  1
+#endif
+
+#if 0
+#define LM4549_DUMP_DAC_INPUT 1
+#endif
+
+#ifdef LM4549_DEBUG
+#define DPRINTF(fmt, ...) \
+do { printf("lm4549: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#if defined(LM4549_DUMP_DAC_INPUT)
+#include <stdio.h>
+static FILE *fp_dac_input;
+#endif
+
+/* LM4549 register list */
+enum {
+    LM4549_Reset                    = 0x00,
+    LM4549_Master_Volume            = 0x02,
+    LM4549_Line_Out_Volume          = 0x04,
+    LM4549_Master_Volume_Mono       = 0x06,
+    LM4549_PC_Beep_Volume           = 0x0A,
+    LM4549_Phone_Volume             = 0x0C,
+    LM4549_Mic_Volume               = 0x0E,
+    LM4549_Line_In_Volume           = 0x10,
+    LM4549_CD_Volume                = 0x12,
+    LM4549_Video_Volume             = 0x14,
+    LM4549_Aux_Volume               = 0x16,
+    LM4549_PCM_Out_Volume           = 0x18,
+    LM4549_Record_Select            = 0x1A,
+    LM4549_Record_Gain              = 0x1C,
+    LM4549_General_Purpose          = 0x20,
+    LM4549_3D_Control               = 0x22,
+    LM4549_Powerdown_Ctrl_Stat      = 0x26,
+    LM4549_Ext_Audio_ID             = 0x28,
+    LM4549_Ext_Audio_Stat_Ctrl      = 0x2A,
+    LM4549_PCM_Front_DAC_Rate       = 0x2C,
+    LM4549_PCM_ADC_Rate             = 0x32,
+    LM4549_Vendor_ID1               = 0x7C,
+    LM4549_Vendor_ID2               = 0x7E
+};
+
+static void lm4549_reset(lm4549_state *s)
+{
+    uint16_t *regfile = s->regfile;
+
+    regfile[LM4549_Reset]               = 0x0d50;
+    regfile[LM4549_Master_Volume]       = 0x8008;
+    regfile[LM4549_Line_Out_Volume]     = 0x8000;
+    regfile[LM4549_Master_Volume_Mono]  = 0x8000;
+    regfile[LM4549_PC_Beep_Volume]      = 0x0000;
+    regfile[LM4549_Phone_Volume]        = 0x8008;
+    regfile[LM4549_Mic_Volume]          = 0x8008;
+    regfile[LM4549_Line_In_Volume]      = 0x8808;
+    regfile[LM4549_CD_Volume]           = 0x8808;
+    regfile[LM4549_Video_Volume]        = 0x8808;
+    regfile[LM4549_Aux_Volume]          = 0x8808;
+    regfile[LM4549_PCM_Out_Volume]      = 0x8808;
+    regfile[LM4549_Record_Select]       = 0x0000;
+    regfile[LM4549_Record_Gain]         = 0x8000;
+    regfile[LM4549_General_Purpose]     = 0x0000;
+    regfile[LM4549_3D_Control]          = 0x0101;
+    regfile[LM4549_Powerdown_Ctrl_Stat] = 0x000f;
+    regfile[LM4549_Ext_Audio_ID]        = 0x0001;
+    regfile[LM4549_Ext_Audio_Stat_Ctrl] = 0x0000;
+    regfile[LM4549_PCM_Front_DAC_Rate]  = 0xbb80;
+    regfile[LM4549_PCM_ADC_Rate]        = 0xbb80;
+    regfile[LM4549_Vendor_ID1]          = 0x4e53;
+    regfile[LM4549_Vendor_ID2]          = 0x4331;
+}
+
+static void lm4549_audio_transfer(lm4549_state *s)
+{
+    uint32_t written_bytes, written_samples;
+    uint32_t i;
+
+    /* Activate the voice */
+    AUD_set_active_out(s->voice, 1);
+    s->voice_is_active = 1;
+
+    /* Try to write the buffer content */
+    written_bytes = AUD_write(s->voice, s->buffer,
+                              s->buffer_level * sizeof(uint16_t));
+    written_samples = written_bytes >> 1;
+
+#if defined(LM4549_DUMP_DAC_INPUT)
+    fwrite(s->buffer, sizeof(uint8_t), written_bytes, fp_dac_input);
+#endif
+
+    s->buffer_level -= written_samples;
+
+    if (s->buffer_level > 0) {
+        /* Move the data back to the start of the buffer */
+        for (i = 0; i < s->buffer_level; i++) {
+            s->buffer[i] = s->buffer[i + written_samples];
+        }
+    }
+}
+
+static void lm4549_audio_out_callback(void *opaque, int free)
+{
+    lm4549_state *s = (lm4549_state *)opaque;
+    static uint32_t prev_buffer_level;
+
+#ifdef LM4549_DEBUG
+    int size = AUD_get_buffer_size_out(s->voice);
+    DPRINTF("audio_out_callback size = %i free = %i\n", size, free);
+#endif
+
+    /* Detect that no data are consumed
+       => disable the voice */
+    if (s->buffer_level == prev_buffer_level) {
+        AUD_set_active_out(s->voice, 0);
+        s->voice_is_active = 0;
+    }
+    prev_buffer_level = s->buffer_level;
+
+    /* Check if a buffer transfer is pending */
+    if (s->buffer_level == LM4549_BUFFER_SIZE) {
+        lm4549_audio_transfer(s);
+
+        /* Request more data */
+        if (s->data_req_cb != NULL) {
+            (s->data_req_cb)(s->opaque);
+        }
+    }
+}
+
+uint32_t lm4549_read(lm4549_state *s, hwaddr offset)
+{
+    uint16_t *regfile = s->regfile;
+    uint32_t value = 0;
+
+    /* Read the stored value */
+    assert(offset < 128);
+    value = regfile[offset];
+
+    DPRINTF("read [0x%02x] = 0x%04x\n", offset, value);
+
+    return value;
+}
+
+void lm4549_write(lm4549_state *s,
+                  hwaddr offset, uint32_t value)
+{
+    uint16_t *regfile = s->regfile;
+
+    assert(offset < 128);
+    DPRINTF("write [0x%02x] = 0x%04x\n", offset, value);
+
+    switch (offset) {
+    case LM4549_Reset:
+        lm4549_reset(s);
+        break;
+
+    case LM4549_PCM_Front_DAC_Rate:
+        regfile[LM4549_PCM_Front_DAC_Rate] = value;
+        DPRINTF("DAC rate change = %i\n", value);
+
+        /* Re-open a voice with the new sample rate */
+        struct audsettings as;
+        as.freq = value;
+        as.nchannels = 2;
+        as.fmt = AUD_FMT_S16;
+        as.endianness = 0;
+
+        s->voice = AUD_open_out(
+            &s->card,
+            s->voice,
+            "lm4549.out",
+            s,
+            lm4549_audio_out_callback,
+            &as
+        );
+        break;
+
+    case LM4549_Powerdown_Ctrl_Stat:
+        value &= ~0xf;
+        value |= regfile[LM4549_Powerdown_Ctrl_Stat] & 0xf;
+        regfile[LM4549_Powerdown_Ctrl_Stat] = value;
+        break;
+
+    case LM4549_Ext_Audio_ID:
+    case LM4549_Vendor_ID1:
+    case LM4549_Vendor_ID2:
+        DPRINTF("Write to read-only register 0x%x\n", (int)offset);
+        break;
+
+    default:
+        /* Store the new value */
+        regfile[offset] = value;
+        break;
+    }
+}
+
+uint32_t lm4549_write_samples(lm4549_state *s, uint32_t left, uint32_t right)
+{
+    /* The left and right samples are in 20-bit resolution.
+       The LM4549 has 18-bit resolution and only uses the bits [19:2].
+       This model supports 16-bit playback.
+    */
+
+    if (s->buffer_level > LM4549_BUFFER_SIZE - 2) {
+        DPRINTF("write_sample Buffer full\n");
+        return 0;
+    }
+
+    /* Store 16-bit samples in the buffer */
+    s->buffer[s->buffer_level++] = (left >> 4);
+    s->buffer[s->buffer_level++] = (right >> 4);
+
+    if (s->buffer_level == LM4549_BUFFER_SIZE) {
+        /* Trigger the transfer of the buffer to the audio host */
+        lm4549_audio_transfer(s);
+    }
+
+    return 1;
+}
+
+static int lm4549_post_load(void *opaque, int version_id)
+{
+    lm4549_state *s = (lm4549_state *)opaque;
+    uint16_t *regfile = s->regfile;
+
+    /* Re-open a voice with the current sample rate */
+    uint32_t freq = regfile[LM4549_PCM_Front_DAC_Rate];
+
+    DPRINTF("post_load freq = %i\n", freq);
+    DPRINTF("post_load voice_is_active = %i\n", s->voice_is_active);
+
+    struct audsettings as;
+    as.freq = freq;
+    as.nchannels = 2;
+    as.fmt = AUD_FMT_S16;
+    as.endianness = 0;
+
+    s->voice = AUD_open_out(
+        &s->card,
+        s->voice,
+        "lm4549.out",
+        s,
+        lm4549_audio_out_callback,
+        &as
+    );
+
+    /* Request data */
+    if (s->voice_is_active == 1) {
+        lm4549_audio_out_callback(s, AUD_get_buffer_size_out(s->voice));
+    }
+
+    return 0;
+}
+
+void lm4549_init(lm4549_state *s, lm4549_callback data_req_cb, void* opaque)
+{
+    struct audsettings as;
+
+    /* Store the callback and opaque pointer */
+    s->data_req_cb = data_req_cb;
+    s->opaque = opaque;
+
+    /* Init the registers */
+    lm4549_reset(s);
+
+    /* Register an audio card */
+    AUD_register_card("lm4549", &s->card);
+
+    /* Open a default voice */
+    as.freq = 48000;
+    as.nchannels = 2;
+    as.fmt = AUD_FMT_S16;
+    as.endianness = 0;
+
+    s->voice = AUD_open_out(
+        &s->card,
+        s->voice,
+        "lm4549.out",
+        s,
+        lm4549_audio_out_callback,
+        &as
+    );
+
+    AUD_set_volume_out(s->voice, 0, 255, 255);
+
+    s->voice_is_active = 0;
+
+    /* Reset the input buffer */
+    memset(s->buffer, 0x00, sizeof(s->buffer));
+    s->buffer_level = 0;
+
+#if defined(LM4549_DUMP_DAC_INPUT)
+    fp_dac_input = fopen("lm4549_dac_input.pcm", "wb");
+    if (!fp_dac_input) {
+        hw_error("Unable to open lm4549_dac_input.pcm for writing\n");
+    }
+#endif
+}
+
+const VMStateDescription vmstate_lm4549_state = {
+    .name = "lm4549_state",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = &lm4549_post_load,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32(voice_is_active, lm4549_state),
+        VMSTATE_UINT16_ARRAY(regfile, lm4549_state, 128),
+        VMSTATE_UINT16_ARRAY(buffer, lm4549_state, LM4549_BUFFER_SIZE),
+        VMSTATE_UINT32(buffer_level, lm4549_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c
new file mode 100644 (file)
index 0000000..34e0df7
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * QEMU PC speaker emulation
+ *
+ * Copyright (c) 2006 Joachim Henke
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "hw/isa/isa.h"
+#include "audio/audio.h"
+#include "qemu/timer.h"
+#include "hw/timer/i8254.h"
+#include "hw/audio/pcspk.h"
+
+#define PCSPK_BUF_LEN 1792
+#define PCSPK_SAMPLE_RATE 32000
+#define PCSPK_MAX_FREQ (PCSPK_SAMPLE_RATE >> 1)
+#define PCSPK_MIN_COUNT ((PIT_FREQ + PCSPK_MAX_FREQ - 1) / PCSPK_MAX_FREQ)
+
+typedef struct {
+    ISADevice dev;
+    MemoryRegion ioport;
+    uint32_t iobase;
+    uint8_t sample_buf[PCSPK_BUF_LEN];
+    QEMUSoundCard card;
+    SWVoiceOut *voice;
+    void *pit;
+    unsigned int pit_count;
+    unsigned int samples;
+    unsigned int play_pos;
+    int data_on;
+    int dummy_refresh_clock;
+} PCSpkState;
+
+static const char *s_spk = "pcspk";
+static PCSpkState *pcspk_state;
+
+static inline void generate_samples(PCSpkState *s)
+{
+    unsigned int i;
+
+    if (s->pit_count) {
+        const uint32_t m = PCSPK_SAMPLE_RATE * s->pit_count;
+        const uint32_t n = ((uint64_t)PIT_FREQ << 32) / m;
+
+        /* multiple of wavelength for gapless looping */
+        s->samples = (PCSPK_BUF_LEN * PIT_FREQ / m * m / (PIT_FREQ >> 1) + 1) >> 1;
+        for (i = 0; i < s->samples; ++i)
+            s->sample_buf[i] = (64 & (n * i >> 25)) - 32;
+    } else {
+        s->samples = PCSPK_BUF_LEN;
+        for (i = 0; i < PCSPK_BUF_LEN; ++i)
+            s->sample_buf[i] = 128; /* silence */
+    }
+}
+
+static void pcspk_callback(void *opaque, int free)
+{
+    PCSpkState *s = opaque;
+    PITChannelInfo ch;
+    unsigned int n;
+
+    pit_get_channel_info(s->pit, 2, &ch);
+
+    if (ch.mode != 3) {
+        return;
+    }
+
+    n = ch.initial_count;
+    /* avoid frequencies that are not reproducible with sample rate */
+    if (n < PCSPK_MIN_COUNT)
+        n = 0;
+
+    if (s->pit_count != n) {
+        s->pit_count = n;
+        s->play_pos = 0;
+        generate_samples(s);
+    }
+
+    while (free > 0) {
+        n = audio_MIN(s->samples - s->play_pos, (unsigned int)free);
+        n = AUD_write(s->voice, &s->sample_buf[s->play_pos], n);
+        if (!n)
+            break;
+        s->play_pos = (s->play_pos + n) % s->samples;
+        free -= n;
+    }
+}
+
+int pcspk_audio_init(ISABus *bus)
+{
+    PCSpkState *s = pcspk_state;
+    struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUD_FMT_U8, 0};
+
+    AUD_register_card(s_spk, &s->card);
+
+    s->voice = AUD_open_out(&s->card, s->voice, s_spk, s, pcspk_callback, &as);
+    if (!s->voice) {
+        AUD_log(s_spk, "Could not open voice\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static uint64_t pcspk_io_read(void *opaque, hwaddr addr,
+                              unsigned size)
+{
+    PCSpkState *s = opaque;
+    PITChannelInfo ch;
+
+    pit_get_channel_info(s->pit, 2, &ch);
+
+    s->dummy_refresh_clock ^= (1 << 4);
+
+    return ch.gate | (s->data_on << 1) | s->dummy_refresh_clock |
+       (ch.out << 5);
+}
+
+static void pcspk_io_write(void *opaque, hwaddr addr, uint64_t val,
+                           unsigned size)
+{
+    PCSpkState *s = opaque;
+    const int gate = val & 1;
+
+    s->data_on = (val >> 1) & 1;
+    pit_set_gate(s->pit, 2, gate);
+    if (s->voice) {
+        if (gate) /* restart */
+            s->play_pos = 0;
+        AUD_set_active_out(s->voice, gate & s->data_on);
+    }
+}
+
+static const MemoryRegionOps pcspk_io_ops = {
+    .read = pcspk_io_read,
+    .write = pcspk_io_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static int pcspk_initfn(ISADevice *dev)
+{
+    PCSpkState *s = DO_UPCAST(PCSpkState, dev, dev);
+
+    memory_region_init_io(&s->ioport, &pcspk_io_ops, s, "elcr", 1);
+    isa_register_ioport(dev, &s->ioport, s->iobase);
+
+    pcspk_state = s;
+
+    return 0;
+}
+
+static Property pcspk_properties[] = {
+    DEFINE_PROP_HEX32("iobase", PCSpkState, iobase,  -1),
+    DEFINE_PROP_PTR("pit", PCSpkState, pit),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pcspk_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+
+    ic->init = pcspk_initfn;
+    dc->no_user = 1;
+    dc->props = pcspk_properties;
+}
+
+static const TypeInfo pcspk_info = {
+    .name           = "isa-pcspk",
+    .parent         = TYPE_ISA_DEVICE,
+    .instance_size  = sizeof(PCSpkState),
+    .class_init     = pcspk_class_initfn,
+};
+
+static void pcspk_register(void)
+{
+    type_register_static(&pcspk_info);
+}
+type_init(pcspk_register)
diff --git a/hw/audio/pl041.c b/hw/audio/pl041.c
new file mode 100644 (file)
index 0000000..92dddc2
--- /dev/null
@@ -0,0 +1,647 @@
+/*
+ * Arm PrimeCell PL041 Advanced Audio Codec Interface
+ *
+ * Copyright (c) 2011
+ * Written by Mathieu Sonet - www.elasticsheep.com
+ *
+ * This code is licensed under the GPL.
+ *
+ * *****************************************************************
+ *
+ * This driver emulates the ARM AACI interface
+ * connected to a LM4549 codec.
+ *
+ * Limitations:
+ * - Supports only a playback on one channel (Versatile/Vexpress)
+ * - Supports only one TX FIFO in compact-mode or non-compact mode.
+ * - Supports playback of 12, 16, 18 and 20 bits samples.
+ * - Record is not supported.
+ * - The PL041 is hardwired to a LM4549 codec.
+ *
+ */
+
+#include "hw/sysbus.h"
+
+#include "hw/pl041.h"
+#include "hw/lm4549.h"
+
+#if 0
+#define PL041_DEBUG_LEVEL 1
+#endif
+
+#if defined(PL041_DEBUG_LEVEL) && (PL041_DEBUG_LEVEL >= 1)
+#define DBG_L1(fmt, ...) \
+do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DBG_L1(fmt, ...) \
+do { } while (0)
+#endif
+
+#if defined(PL041_DEBUG_LEVEL) && (PL041_DEBUG_LEVEL >= 2)
+#define DBG_L2(fmt, ...) \
+do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DBG_L2(fmt, ...) \
+do { } while (0)
+#endif
+
+
+#define MAX_FIFO_DEPTH      (1024)
+#define DEFAULT_FIFO_DEPTH  (8)
+
+#define SLOT1_RW    (1 << 19)
+
+/* This FIFO only stores 20-bit samples on 32-bit words.
+   So its level is independent of the selected mode */
+typedef struct {
+    uint32_t level;
+    uint32_t data[MAX_FIFO_DEPTH];
+} pl041_fifo;
+
+typedef struct {
+    pl041_fifo tx_fifo;
+    uint8_t tx_enabled;
+    uint8_t tx_compact_mode;
+    uint8_t tx_sample_size;
+
+    pl041_fifo rx_fifo;
+    uint8_t rx_enabled;
+    uint8_t rx_compact_mode;
+    uint8_t rx_sample_size;
+} pl041_channel;
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    qemu_irq irq;
+
+    uint32_t fifo_depth; /* FIFO depth in non-compact mode */
+
+    pl041_regfile regs;
+    pl041_channel fifo1;
+    lm4549_state codec;
+} pl041_state;
+
+
+static const unsigned char pl041_default_id[8] = {
+    0x41, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1
+};
+
+#if defined(PL041_DEBUG_LEVEL)
+#define REGISTER(name, offset) #name,
+static const char *pl041_regs_name[] = {
+    #include "pl041.hx"
+};
+#undef REGISTER
+#endif
+
+
+#if defined(PL041_DEBUG_LEVEL)
+static const char *get_reg_name(hwaddr offset)
+{
+    if (offset <= PL041_dr1_7) {
+        return pl041_regs_name[offset >> 2];
+    }
+
+    return "unknown";
+}
+#endif
+
+static uint8_t pl041_compute_periphid3(pl041_state *s)
+{
+    uint8_t id3 = 1; /* One channel */
+
+    /* Add the fifo depth information */
+    switch (s->fifo_depth) {
+    case 8:
+        id3 |= 0 << 3;
+        break;
+    case 32:
+        id3 |= 1 << 3;
+        break;
+    case 64:
+        id3 |= 2 << 3;
+        break;
+    case 128:
+        id3 |= 3 << 3;
+        break;
+    case 256:
+        id3 |= 4 << 3;
+        break;
+    case 512:
+        id3 |= 5 << 3;
+        break;
+    case 1024:
+        id3 |= 6 << 3;
+        break;
+    case 2048:
+        id3 |= 7 << 3;
+        break;
+    }
+
+    return id3;
+}
+
+static void pl041_reset(pl041_state *s)
+{
+    DBG_L1("pl041_reset\n");
+
+    memset(&s->regs, 0x00, sizeof(pl041_regfile));
+
+    s->regs.slfr = SL1TXEMPTY | SL2TXEMPTY | SL12TXEMPTY;
+    s->regs.sr1 = TXFE | RXFE | TXHE;
+    s->regs.isr1 = 0;
+
+    memset(&s->fifo1, 0x00, sizeof(s->fifo1));
+}
+
+
+static void pl041_fifo1_write(pl041_state *s, uint32_t value)
+{
+    pl041_channel *channel = &s->fifo1;
+    pl041_fifo *fifo = &s->fifo1.tx_fifo;
+
+    /* Push the value in the FIFO */
+    if (channel->tx_compact_mode == 0) {
+        /* Non-compact mode */
+
+        if (fifo->level < s->fifo_depth) {
+            /* Pad the value with 0 to obtain a 20-bit sample */
+            switch (channel->tx_sample_size) {
+            case 12:
+                value = (value << 8) & 0xFFFFF;
+                break;
+            case 16:
+                value = (value << 4) & 0xFFFFF;
+                break;
+            case 18:
+                value = (value << 2) & 0xFFFFF;
+                break;
+            case 20:
+            default:
+                break;
+            }
+
+            /* Store the sample in the FIFO */
+            fifo->data[fifo->level++] = value;
+        }
+#if defined(PL041_DEBUG_LEVEL)
+        else {
+            DBG_L1("fifo1 write: overrun\n");
+        }
+#endif
+    } else {
+        /* Compact mode */
+
+        if ((fifo->level + 2) < s->fifo_depth) {
+            uint32_t i = 0;
+            uint32_t sample = 0;
+
+            for (i = 0; i < 2; i++) {
+                sample = value & 0xFFFF;
+                value = value >> 16;
+
+                /* Pad each sample with 0 to obtain a 20-bit sample */
+                switch (channel->tx_sample_size) {
+                case 12:
+                    sample = sample << 8;
+                    break;
+                case 16:
+                default:
+                    sample = sample << 4;
+                    break;
+                }
+
+                /* Store the sample in the FIFO */
+                fifo->data[fifo->level++] = sample;
+            }
+        }
+#if defined(PL041_DEBUG_LEVEL)
+        else {
+            DBG_L1("fifo1 write: overrun\n");
+        }
+#endif
+    }
+
+    /* Update the status register */
+    if (fifo->level > 0) {
+        s->regs.sr1 &= ~(TXUNDERRUN | TXFE);
+    }
+
+    if (fifo->level >= (s->fifo_depth / 2)) {
+        s->regs.sr1 &= ~TXHE;
+    }
+
+    if (fifo->level >= s->fifo_depth) {
+        s->regs.sr1 |= TXFF;
+    }
+
+    DBG_L2("fifo1_push sr1 = 0x%08x\n", s->regs.sr1);
+}
+
+static void pl041_fifo1_transmit(pl041_state *s)
+{
+    pl041_channel *channel = &s->fifo1;
+    pl041_fifo *fifo = &s->fifo1.tx_fifo;
+    uint32_t slots = s->regs.txcr1 & TXSLOT_MASK;
+    uint32_t written_samples;
+
+    /* Check if FIFO1 transmit is enabled */
+    if ((channel->tx_enabled) && (slots & (TXSLOT3 | TXSLOT4))) {
+        if (fifo->level >= (s->fifo_depth / 2)) {
+            int i;
+
+            DBG_L1("Transfer FIFO level = %i\n", fifo->level);
+
+            /* Try to transfer the whole FIFO */
+            for (i = 0; i < (fifo->level / 2); i++) {
+                uint32_t left = fifo->data[i * 2];
+                uint32_t right = fifo->data[i * 2 + 1];
+
+                 /* Transmit two 20-bit samples to the codec */
+                if (lm4549_write_samples(&s->codec, left, right) == 0) {
+                    DBG_L1("Codec buffer full\n");
+                    break;
+                }
+            }
+
+            written_samples = i * 2;
+            if (written_samples > 0) {
+                /* Update the FIFO level */
+                fifo->level -= written_samples;
+
+                /* Move back the pending samples to the start of the FIFO */
+                for (i = 0; i < fifo->level; i++) {
+                    fifo->data[i] = fifo->data[written_samples + i];
+                }
+
+                /* Update the status register */
+                s->regs.sr1 &= ~TXFF;
+
+                if (fifo->level <= (s->fifo_depth / 2)) {
+                    s->regs.sr1 |= TXHE;
+                }
+
+                if (fifo->level == 0) {
+                    s->regs.sr1 |= TXFE | TXUNDERRUN;
+                    DBG_L1("Empty FIFO\n");
+                }
+            }
+        }
+    }
+}
+
+static void pl041_isr1_update(pl041_state *s)
+{
+    /* Update ISR1 */
+    if (s->regs.sr1 & TXUNDERRUN) {
+        s->regs.isr1 |= URINTR;
+    } else {
+        s->regs.isr1 &= ~URINTR;
+    }
+
+    if (s->regs.sr1 & TXHE) {
+        s->regs.isr1 |= TXINTR;
+    } else {
+        s->regs.isr1 &= ~TXINTR;
+    }
+
+    if (!(s->regs.sr1 & TXBUSY) && (s->regs.sr1 & TXFE)) {
+        s->regs.isr1 |= TXCINTR;
+    } else {
+        s->regs.isr1 &= ~TXCINTR;
+    }
+
+    /* Update the irq state */
+    qemu_set_irq(s->irq, ((s->regs.isr1 & s->regs.ie1) > 0) ? 1 : 0);
+    DBG_L2("Set interrupt sr1 = 0x%08x isr1 = 0x%08x masked = 0x%08x\n",
+           s->regs.sr1, s->regs.isr1, s->regs.isr1 & s->regs.ie1);
+}
+
+static void pl041_request_data(void *opaque)
+{
+    pl041_state *s = (pl041_state *)opaque;
+
+    /* Trigger pending transfers */
+    pl041_fifo1_transmit(s);
+    pl041_isr1_update(s);
+}
+
+static uint64_t pl041_read(void *opaque, hwaddr offset,
+                                unsigned size)
+{
+    pl041_state *s = (pl041_state *)opaque;
+    int value;
+
+    if ((offset >= PL041_periphid0) && (offset <= PL041_pcellid3)) {
+        if (offset == PL041_periphid3) {
+            value = pl041_compute_periphid3(s);
+        } else {
+            value = pl041_default_id[(offset - PL041_periphid0) >> 2];
+        }
+
+        DBG_L1("pl041_read [0x%08x] => 0x%08x\n", offset, value);
+        return value;
+    } else if (offset <= PL041_dr4_7) {
+        value = *((uint32_t *)&s->regs + (offset >> 2));
+    } else {
+        DBG_L1("pl041_read: Reserved offset %x\n", (int)offset);
+        return 0;
+    }
+
+    switch (offset) {
+    case PL041_allints:
+        value = s->regs.isr1 & 0x7F;
+        break;
+    }
+
+    DBG_L1("pl041_read [0x%08x] %s => 0x%08x\n", offset,
+           get_reg_name(offset), value);
+
+    return value;
+}
+
+static void pl041_write(void *opaque, hwaddr offset,
+                             uint64_t value, unsigned size)
+{
+    pl041_state *s = (pl041_state *)opaque;
+    uint16_t control, data;
+    uint32_t result;
+
+    DBG_L1("pl041_write [0x%08x] %s <= 0x%08x\n", offset,
+           get_reg_name(offset), (unsigned int)value);
+
+    /* Write the register */
+    if (offset <= PL041_dr4_7) {
+        *((uint32_t *)&s->regs + (offset >> 2)) = value;
+    } else {
+        DBG_L1("pl041_write: Reserved offset %x\n", (int)offset);
+        return;
+    }
+
+    /* Execute the actions */
+    switch (offset) {
+    case PL041_txcr1:
+    {
+        pl041_channel *channel = &s->fifo1;
+
+        uint32_t txen = s->regs.txcr1 & TXEN;
+        uint32_t tsize = (s->regs.txcr1 & TSIZE_MASK) >> TSIZE_MASK_BIT;
+        uint32_t compact_mode = (s->regs.txcr1 & TXCOMPACT) ? 1 : 0;
+#if defined(PL041_DEBUG_LEVEL)
+        uint32_t slots = (s->regs.txcr1 & TXSLOT_MASK) >> TXSLOT_MASK_BIT;
+        uint32_t txfen = (s->regs.txcr1 & TXFEN) > 0 ? 1 : 0;
+#endif
+
+        DBG_L1("=> txen = %i slots = 0x%01x tsize = %i compact = %i "
+               "txfen = %i\n", txen, slots,  tsize, compact_mode, txfen);
+
+        channel->tx_enabled = txen;
+        channel->tx_compact_mode = compact_mode;
+
+        switch (tsize) {
+        case 0:
+            channel->tx_sample_size = 16;
+            break;
+        case 1:
+            channel->tx_sample_size = 18;
+            break;
+        case 2:
+            channel->tx_sample_size = 20;
+            break;
+        case 3:
+            channel->tx_sample_size = 12;
+            break;
+        }
+
+        DBG_L1("TX enabled = %i\n", channel->tx_enabled);
+        DBG_L1("TX compact mode = %i\n", channel->tx_compact_mode);
+        DBG_L1("TX sample width = %i\n", channel->tx_sample_size);
+
+        /* Check if compact mode is allowed with selected tsize */
+        if (channel->tx_compact_mode == 1) {
+            if ((channel->tx_sample_size == 18) ||
+                (channel->tx_sample_size == 20)) {
+                channel->tx_compact_mode = 0;
+                DBG_L1("Compact mode not allowed with 18/20-bit sample size\n");
+            }
+        }
+
+        break;
+    }
+    case PL041_sl1tx:
+        s->regs.slfr &= ~SL1TXEMPTY;
+
+        control = (s->regs.sl1tx >> 12) & 0x7F;
+        data = (s->regs.sl2tx >> 4) & 0xFFFF;
+
+        if ((s->regs.sl1tx & SLOT1_RW) == 0) {
+            /* Write operation */
+            lm4549_write(&s->codec, control, data);
+        } else {
+            /* Read operation */
+            result = lm4549_read(&s->codec, control);
+
+            /* Store the returned value */
+            s->regs.sl1rx = s->regs.sl1tx & ~SLOT1_RW;
+            s->regs.sl2rx = result << 4;
+
+            s->regs.slfr &= ~(SL1RXBUSY | SL2RXBUSY);
+            s->regs.slfr |= SL1RXVALID | SL2RXVALID;
+        }
+        break;
+
+    case PL041_sl2tx:
+        s->regs.sl2tx = value;
+        s->regs.slfr &= ~SL2TXEMPTY;
+        break;
+
+    case PL041_intclr:
+        DBG_L1("=> Clear interrupt intclr = 0x%08x isr1 = 0x%08x\n",
+               s->regs.intclr, s->regs.isr1);
+
+        if (s->regs.intclr & TXUEC1) {
+            s->regs.sr1 &= ~TXUNDERRUN;
+        }
+        break;
+
+    case PL041_maincr:
+    {
+#if defined(PL041_DEBUG_LEVEL)
+        char debug[] = " AACIFE  SL1RXEN  SL1TXEN";
+        if (!(value & AACIFE)) {
+            debug[0] = '!';
+        }
+        if (!(value & SL1RXEN)) {
+            debug[8] = '!';
+        }
+        if (!(value & SL1TXEN)) {
+            debug[17] = '!';
+        }
+        DBG_L1("%s\n", debug);
+#endif
+
+        if ((s->regs.maincr & AACIFE) == 0) {
+            pl041_reset(s);
+        }
+        break;
+    }
+
+    case PL041_dr1_0:
+    case PL041_dr1_1:
+    case PL041_dr1_2:
+    case PL041_dr1_3:
+        pl041_fifo1_write(s, value);
+        break;
+    }
+
+    /* Transmit the FIFO content */
+    pl041_fifo1_transmit(s);
+
+    /* Update the ISR1 register */
+    pl041_isr1_update(s);
+}
+
+static void pl041_device_reset(DeviceState *d)
+{
+    pl041_state *s = DO_UPCAST(pl041_state, busdev.qdev, d);
+
+    pl041_reset(s);
+}
+
+static const MemoryRegionOps pl041_ops = {
+    .read = pl041_read,
+    .write = pl041_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int pl041_init(SysBusDevice *dev)
+{
+    pl041_state *s = FROM_SYSBUS(pl041_state, dev);
+
+    DBG_L1("pl041_init 0x%08x\n", (uint32_t)s);
+
+    /* Check the device properties */
+    switch (s->fifo_depth) {
+    case 8:
+    case 32:
+    case 64:
+    case 128:
+    case 256:
+    case 512:
+    case 1024:
+    case 2048:
+        break;
+    case 16:
+    default:
+        /* NC FIFO depth of 16 is not allowed because its id bits in
+           AACIPERIPHID3 overlap with the id for the default NC FIFO depth */
+        qemu_log_mask(LOG_UNIMP,
+                      "pl041: unsupported non-compact fifo depth [%i]\n",
+                      s->fifo_depth);
+        return -1;
+    }
+
+    /* Connect the device to the sysbus */
+    memory_region_init_io(&s->iomem, &pl041_ops, s, "pl041", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+
+    /* Init the codec */
+    lm4549_init(&s->codec, &pl041_request_data, (void *)s);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_pl041_regfile = {
+    .name = "pl041_regfile",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+#define REGISTER(name, offset) VMSTATE_UINT32(name, pl041_regfile),
+        #include "pl041.hx"
+#undef REGISTER
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_pl041_fifo = {
+    .name = "pl041_fifo",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32(level, pl041_fifo),
+        VMSTATE_UINT32_ARRAY(data, pl041_fifo, MAX_FIFO_DEPTH),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_pl041_channel = {
+    .name = "pl041_channel",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_STRUCT(tx_fifo, pl041_channel, 0,
+                       vmstate_pl041_fifo, pl041_fifo),
+        VMSTATE_UINT8(tx_enabled, pl041_channel),
+        VMSTATE_UINT8(tx_compact_mode, pl041_channel),
+        VMSTATE_UINT8(tx_sample_size, pl041_channel),
+        VMSTATE_STRUCT(rx_fifo, pl041_channel, 0,
+                       vmstate_pl041_fifo, pl041_fifo),
+        VMSTATE_UINT8(rx_enabled, pl041_channel),
+        VMSTATE_UINT8(rx_compact_mode, pl041_channel),
+        VMSTATE_UINT8(rx_sample_size, pl041_channel),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_pl041 = {
+    .name = "pl041",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(fifo_depth, pl041_state),
+        VMSTATE_STRUCT(regs, pl041_state, 0,
+                       vmstate_pl041_regfile, pl041_regfile),
+        VMSTATE_STRUCT(fifo1, pl041_state, 0,
+                       vmstate_pl041_channel, pl041_channel),
+        VMSTATE_STRUCT(codec, pl041_state, 0,
+                       vmstate_lm4549_state, lm4549_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property pl041_device_properties[] = {
+    /* Non-compact FIFO depth property */
+    DEFINE_PROP_UINT32("nc_fifo_depth", pl041_state, fifo_depth, DEFAULT_FIFO_DEPTH),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pl041_device_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = pl041_init;
+    dc->no_user = 1;
+    dc->reset = pl041_device_reset;
+    dc->vmsd = &vmstate_pl041;
+    dc->props = pl041_device_properties;
+}
+
+static const TypeInfo pl041_device_info = {
+    .name          = "pl041",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl041_state),
+    .class_init    = pl041_device_class_init,
+};
+
+static void pl041_register_types(void)
+{
+    type_register_static(&pl041_device_info);
+}
+
+type_init(pl041_register_types)
diff --git a/hw/audio/pl041.hx b/hw/audio/pl041.hx
new file mode 100644 (file)
index 0000000..dd7188c
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Arm PrimeCell PL041 Advanced Audio Codec Interface
+ *
+ * Copyright (c) 2011
+ * Written by Mathieu Sonet - www.elasticsheep.com
+ *
+ * This code is licensed under the GPL.
+ *
+ * *****************************************************************
+ */
+
+/* PL041 register file description */
+
+REGISTER( rxcr1,   0x00 )
+REGISTER( txcr1,   0x04 )
+REGISTER( sr1,     0x08 )
+REGISTER( isr1,    0x0C )
+REGISTER( ie1,     0x10 )
+REGISTER( rxcr2,   0x14 )
+REGISTER( txcr2,   0x18 )
+REGISTER( sr2,     0x1C )
+REGISTER( isr2,    0x20 )
+REGISTER( ie2,     0x24 )
+REGISTER( rxcr3,   0x28 )
+REGISTER( txcr3,   0x2C )
+REGISTER( sr3,     0x30 )
+REGISTER( isr3,    0x34 )
+REGISTER( ie3,     0x38 )
+REGISTER( rxcr4,   0x3C )
+REGISTER( txcr4,   0x40 )
+REGISTER( sr4,     0x44 )
+REGISTER( isr4,    0x48 )
+REGISTER( ie4,     0x4C )
+REGISTER( sl1rx,   0x50 )
+REGISTER( sl1tx,   0x54 )
+REGISTER( sl2rx,   0x58 )
+REGISTER( sl2tx,   0x5C )
+REGISTER( sl12rx,  0x60 )
+REGISTER( sl12tx,  0x64 )
+REGISTER( slfr,    0x68 )
+REGISTER( slistat, 0x6C )
+REGISTER( slien,   0x70 )
+REGISTER( intclr,  0x74 )
+REGISTER( maincr,  0x78 )
+REGISTER( reset,   0x7C )
+REGISTER( sync,    0x80 )
+REGISTER( allints, 0x84 )
+REGISTER( mainfr,  0x88 )
+REGISTER( unused,  0x8C )
+REGISTER( dr1_0,   0x90 )
+REGISTER( dr1_1,   0x94 )
+REGISTER( dr1_2,   0x98 )
+REGISTER( dr1_3,   0x9C )
+REGISTER( dr1_4,   0xA0 )
+REGISTER( dr1_5,   0xA4 )
+REGISTER( dr1_6,   0xA8 )
+REGISTER( dr1_7,   0xAC )
+REGISTER( dr2_0,   0xB0 )
+REGISTER( dr2_1,   0xB4 )
+REGISTER( dr2_2,   0xB8 )
+REGISTER( dr2_3,   0xBC )
+REGISTER( dr2_4,   0xC0 )
+REGISTER( dr2_5,   0xC4 )
+REGISTER( dr2_6,   0xC8 )
+REGISTER( dr2_7,   0xCC )
+REGISTER( dr3_0,   0xD0 )
+REGISTER( dr3_1,   0xD4 )
+REGISTER( dr3_2,   0xD8 )
+REGISTER( dr3_3,   0xDC )
+REGISTER( dr3_4,   0xE0 )
+REGISTER( dr3_5,   0xE4 )
+REGISTER( dr3_6,   0xE8 )
+REGISTER( dr3_7,   0xEC )
+REGISTER( dr4_0,   0xF0 )
+REGISTER( dr4_1,   0xF4 )
+REGISTER( dr4_2,   0xF8 )
+REGISTER( dr4_3,   0xFC )
+REGISTER( dr4_4,   0x100 )
+REGISTER( dr4_5,   0x104 )
+REGISTER( dr4_6,   0x108 )
+REGISTER( dr4_7,   0x10C )
diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c
new file mode 100644 (file)
index 0000000..783b6b4
--- /dev/null
@@ -0,0 +1,1424 @@
+/*
+ * QEMU Soundblaster 16 emulation
+ *
+ * Copyright (c) 2003-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/audio/audio.h"
+#include "audio/audio.h"
+#include "hw/isa/isa.h"
+#include "hw/qdev.h"
+#include "qemu/timer.h"
+#include "qemu/host-utils.h"
+
+#define dolog(...) AUD_log ("sb16", __VA_ARGS__)
+
+/* #define DEBUG */
+/* #define DEBUG_SB16_MOST */
+
+#ifdef DEBUG
+#define ldebug(...) dolog (__VA_ARGS__)
+#else
+#define ldebug(...)
+#endif
+
+#define IO_READ_PROTO(name)                             \
+    uint32_t name (void *opaque, uint32_t nport)
+#define IO_WRITE_PROTO(name)                                    \
+    void name (void *opaque, uint32_t nport, uint32_t val)
+
+static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
+
+typedef struct SB16State {
+    ISADevice dev;
+    QEMUSoundCard card;
+    qemu_irq pic;
+    uint32_t irq;
+    uint32_t dma;
+    uint32_t hdma;
+    uint32_t port;
+    uint32_t ver;
+
+    int in_index;
+    int out_data_len;
+    int fmt_stereo;
+    int fmt_signed;
+    int fmt_bits;
+    audfmt_e fmt;
+    int dma_auto;
+    int block_size;
+    int fifo;
+    int freq;
+    int time_const;
+    int speaker;
+    int needed_bytes;
+    int cmd;
+    int use_hdma;
+    int highspeed;
+    int can_write;
+
+    int v2x6;
+
+    uint8_t csp_param;
+    uint8_t csp_value;
+    uint8_t csp_mode;
+    uint8_t csp_regs[256];
+    uint8_t csp_index;
+    uint8_t csp_reg83[4];
+    int csp_reg83r;
+    int csp_reg83w;
+
+    uint8_t in2_data[10];
+    uint8_t out_data[50];
+    uint8_t test_reg;
+    uint8_t last_read_byte;
+    int nzero;
+
+    int left_till_irq;
+
+    int dma_running;
+    int bytes_per_second;
+    int align;
+    int audio_free;
+    SWVoiceOut *voice;
+
+    QEMUTimer *aux_ts;
+    /* mixer state */
+    int mixer_nreg;
+    uint8_t mixer_regs[256];
+} SB16State;
+
+static void SB_audio_callback (void *opaque, int free);
+
+static int magic_of_irq (int irq)
+{
+    switch (irq) {
+    case 5:
+        return 2;
+    case 7:
+        return 4;
+    case 9:
+        return 1;
+    case 10:
+        return 8;
+    default:
+        dolog ("bad irq %d\n", irq);
+        return 2;
+    }
+}
+
+static int irq_of_magic (int magic)
+{
+    switch (magic) {
+    case 1:
+        return 9;
+    case 2:
+        return 5;
+    case 4:
+        return 7;
+    case 8:
+        return 10;
+    default:
+        dolog ("bad irq magic %d\n", magic);
+        return -1;
+    }
+}
+
+#if 0
+static void log_dsp (SB16State *dsp)
+{
+    ldebug ("%s:%s:%d:%s:dmasize=%d:freq=%d:const=%d:speaker=%d\n",
+            dsp->fmt_stereo ? "Stereo" : "Mono",
+            dsp->fmt_signed ? "Signed" : "Unsigned",
+            dsp->fmt_bits,
+            dsp->dma_auto ? "Auto" : "Single",
+            dsp->block_size,
+            dsp->freq,
+            dsp->time_const,
+            dsp->speaker);
+}
+#endif
+
+static void speaker (SB16State *s, int on)
+{
+    s->speaker = on;
+    /* AUD_enable (s->voice, on); */
+}
+
+static void control (SB16State *s, int hold)
+{
+    int dma = s->use_hdma ? s->hdma : s->dma;
+    s->dma_running = hold;
+
+    ldebug ("hold %d high %d dma %d\n", hold, s->use_hdma, dma);
+
+    if (hold) {
+        DMA_hold_DREQ (dma);
+        AUD_set_active_out (s->voice, 1);
+    }
+    else {
+        DMA_release_DREQ (dma);
+        AUD_set_active_out (s->voice, 0);
+    }
+}
+
+static void aux_timer (void *opaque)
+{
+    SB16State *s = opaque;
+    s->can_write = 1;
+    qemu_irq_raise (s->pic);
+}
+
+#define DMA8_AUTO 1
+#define DMA8_HIGH 2
+
+static void continue_dma8 (SB16State *s)
+{
+    if (s->freq > 0) {
+        struct audsettings as;
+
+        s->audio_free = 0;
+
+        as.freq = s->freq;
+        as.nchannels = 1 << s->fmt_stereo;
+        as.fmt = s->fmt;
+        as.endianness = 0;
+
+        s->voice = AUD_open_out (
+            &s->card,
+            s->voice,
+            "sb16",
+            s,
+            SB_audio_callback,
+            &as
+            );
+    }
+
+    control (s, 1);
+}
+
+static void dma_cmd8 (SB16State *s, int mask, int dma_len)
+{
+    s->fmt = AUD_FMT_U8;
+    s->use_hdma = 0;
+    s->fmt_bits = 8;
+    s->fmt_signed = 0;
+    s->fmt_stereo = (s->mixer_regs[0x0e] & 2) != 0;
+    if (-1 == s->time_const) {
+        if (s->freq <= 0)
+            s->freq = 11025;
+    }
+    else {
+        int tmp = (256 - s->time_const);
+        s->freq = (1000000 + (tmp / 2)) / tmp;
+    }
+
+    if (dma_len != -1) {
+        s->block_size = dma_len << s->fmt_stereo;
+    }
+    else {
+        /* This is apparently the only way to make both Act1/PL
+           and SecondReality/FC work
+
+           Act1 sets block size via command 0x48 and it's an odd number
+           SR does the same with even number
+           Both use stereo, and Creatives own documentation states that
+           0x48 sets block size in bytes less one.. go figure */
+        s->block_size &= ~s->fmt_stereo;
+    }
+
+    s->freq >>= s->fmt_stereo;
+    s->left_till_irq = s->block_size;
+    s->bytes_per_second = (s->freq << s->fmt_stereo);
+    /* s->highspeed = (mask & DMA8_HIGH) != 0; */
+    s->dma_auto = (mask & DMA8_AUTO) != 0;
+    s->align = (1 << s->fmt_stereo) - 1;
+
+    if (s->block_size & s->align) {
+        dolog ("warning: misaligned block size %d, alignment %d\n",
+               s->block_size, s->align + 1);
+    }
+
+    ldebug ("freq %d, stereo %d, sign %d, bits %d, "
+            "dma %d, auto %d, fifo %d, high %d\n",
+            s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
+            s->block_size, s->dma_auto, s->fifo, s->highspeed);
+
+    continue_dma8 (s);
+    speaker (s, 1);
+}
+
+static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len)
+{
+    s->use_hdma = cmd < 0xc0;
+    s->fifo = (cmd >> 1) & 1;
+    s->dma_auto = (cmd >> 2) & 1;
+    s->fmt_signed = (d0 >> 4) & 1;
+    s->fmt_stereo = (d0 >> 5) & 1;
+
+    switch (cmd >> 4) {
+    case 11:
+        s->fmt_bits = 16;
+        break;
+
+    case 12:
+        s->fmt_bits = 8;
+        break;
+    }
+
+    if (-1 != s->time_const) {
+#if 1
+        int tmp = 256 - s->time_const;
+        s->freq = (1000000 + (tmp / 2)) / tmp;
+#else
+        /* s->freq = 1000000 / ((255 - s->time_const) << s->fmt_stereo); */
+        s->freq = 1000000 / ((255 - s->time_const));
+#endif
+        s->time_const = -1;
+    }
+
+    s->block_size = dma_len + 1;
+    s->block_size <<= (s->fmt_bits == 16);
+    if (!s->dma_auto) {
+        /* It is clear that for DOOM and auto-init this value
+           shouldn't take stereo into account, while Miles Sound Systems
+           setsound.exe with single transfer mode wouldn't work without it
+           wonders of SB16 yet again */
+        s->block_size <<= s->fmt_stereo;
+    }
+
+    ldebug ("freq %d, stereo %d, sign %d, bits %d, "
+            "dma %d, auto %d, fifo %d, high %d\n",
+            s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
+            s->block_size, s->dma_auto, s->fifo, s->highspeed);
+
+    if (16 == s->fmt_bits) {
+        if (s->fmt_signed) {
+            s->fmt = AUD_FMT_S16;
+        }
+        else {
+            s->fmt = AUD_FMT_U16;
+        }
+    }
+    else {
+        if (s->fmt_signed) {
+            s->fmt = AUD_FMT_S8;
+        }
+        else {
+            s->fmt = AUD_FMT_U8;
+        }
+    }
+
+    s->left_till_irq = s->block_size;
+
+    s->bytes_per_second = (s->freq << s->fmt_stereo) << (s->fmt_bits == 16);
+    s->highspeed = 0;
+    s->align = (1 << (s->fmt_stereo + (s->fmt_bits == 16))) - 1;
+    if (s->block_size & s->align) {
+        dolog ("warning: misaligned block size %d, alignment %d\n",
+               s->block_size, s->align + 1);
+    }
+
+    if (s->freq) {
+        struct audsettings as;
+
+        s->audio_free = 0;
+
+        as.freq = s->freq;
+        as.nchannels = 1 << s->fmt_stereo;
+        as.fmt = s->fmt;
+        as.endianness = 0;
+
+        s->voice = AUD_open_out (
+            &s->card,
+            s->voice,
+            "sb16",
+            s,
+            SB_audio_callback,
+            &as
+            );
+    }
+
+    control (s, 1);
+    speaker (s, 1);
+}
+
+static inline void dsp_out_data (SB16State *s, uint8_t val)
+{
+    ldebug ("outdata %#x\n", val);
+    if ((size_t) s->out_data_len < sizeof (s->out_data)) {
+        s->out_data[s->out_data_len++] = val;
+    }
+}
+
+static inline uint8_t dsp_get_data (SB16State *s)
+{
+    if (s->in_index) {
+        return s->in2_data[--s->in_index];
+    }
+    else {
+        dolog ("buffer underflow\n");
+        return 0;
+    }
+}
+
+static void command (SB16State *s, uint8_t cmd)
+{
+    ldebug ("command %#x\n", cmd);
+
+    if (cmd > 0xaf && cmd < 0xd0) {
+        if (cmd & 8) {
+            dolog ("ADC not yet supported (command %#x)\n", cmd);
+        }
+
+        switch (cmd >> 4) {
+        case 11:
+        case 12:
+            break;
+        default:
+            dolog ("%#x wrong bits\n", cmd);
+        }
+        s->needed_bytes = 3;
+    }
+    else {
+        s->needed_bytes = 0;
+
+        switch (cmd) {
+        case 0x03:
+            dsp_out_data (s, 0x10); /* s->csp_param); */
+            goto warn;
+
+        case 0x04:
+            s->needed_bytes = 1;
+            goto warn;
+
+        case 0x05:
+            s->needed_bytes = 2;
+            goto warn;
+
+        case 0x08:
+            /* __asm__ ("int3"); */
+            goto warn;
+
+        case 0x0e:
+            s->needed_bytes = 2;
+            goto warn;
+
+        case 0x09:
+            dsp_out_data (s, 0xf8);
+            goto warn;
+
+        case 0x0f:
+            s->needed_bytes = 1;
+            goto warn;
+
+        case 0x10:
+            s->needed_bytes = 1;
+            goto warn;
+
+        case 0x14:
+            s->needed_bytes = 2;
+            s->block_size = 0;
+            break;
+
+        case 0x1c:              /* Auto-Initialize DMA DAC, 8-bit */
+            dma_cmd8 (s, DMA8_AUTO, -1);
+            break;
+
+        case 0x20:              /* Direct ADC, Juice/PL */
+            dsp_out_data (s, 0xff);
+            goto warn;
+
+        case 0x35:
+            dolog ("0x35 - MIDI command not implemented\n");
+            break;
+
+        case 0x40:
+            s->freq = -1;
+            s->time_const = -1;
+            s->needed_bytes = 1;
+            break;
+
+        case 0x41:
+            s->freq = -1;
+            s->time_const = -1;
+            s->needed_bytes = 2;
+            break;
+
+        case 0x42:
+            s->freq = -1;
+            s->time_const = -1;
+            s->needed_bytes = 2;
+            goto warn;
+
+        case 0x45:
+            dsp_out_data (s, 0xaa);
+            goto warn;
+
+        case 0x47:                /* Continue Auto-Initialize DMA 16bit */
+            break;
+
+        case 0x48:
+            s->needed_bytes = 2;
+            break;
+
+        case 0x74:
+            s->needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */
+            dolog ("0x75 - DMA DAC, 4-bit ADPCM not implemented\n");
+            break;
+
+        case 0x75:              /* DMA DAC, 4-bit ADPCM Reference */
+            s->needed_bytes = 2;
+            dolog ("0x74 - DMA DAC, 4-bit ADPCM Reference not implemented\n");
+            break;
+
+        case 0x76:              /* DMA DAC, 2.6-bit ADPCM */
+            s->needed_bytes = 2;
+            dolog ("0x74 - DMA DAC, 2.6-bit ADPCM not implemented\n");
+            break;
+
+        case 0x77:              /* DMA DAC, 2.6-bit ADPCM Reference */
+            s->needed_bytes = 2;
+            dolog ("0x74 - DMA DAC, 2.6-bit ADPCM Reference not implemented\n");
+            break;
+
+        case 0x7d:
+            dolog ("0x7d - Autio-Initialize DMA DAC, 4-bit ADPCM Reference\n");
+            dolog ("not implemented\n");
+            break;
+
+        case 0x7f:
+            dolog (
+                "0x7d - Autio-Initialize DMA DAC, 2.6-bit ADPCM Reference\n"
+                );
+            dolog ("not implemented\n");
+            break;
+
+        case 0x80:
+            s->needed_bytes = 2;
+            break;
+
+        case 0x90:
+        case 0x91:
+            dma_cmd8 (s, ((cmd & 1) == 0) | DMA8_HIGH, -1);
+            break;
+
+        case 0xd0:              /* halt DMA operation. 8bit */
+            control (s, 0);
+            break;
+
+        case 0xd1:              /* speaker on */
+            speaker (s, 1);
+            break;
+
+        case 0xd3:              /* speaker off */
+            speaker (s, 0);
+            break;
+
+        case 0xd4:              /* continue DMA operation. 8bit */
+            /* KQ6 (or maybe Sierras audblst.drv in general) resets
+               the frequency between halt/continue */
+            continue_dma8 (s);
+            break;
+
+        case 0xd5:              /* halt DMA operation. 16bit */
+            control (s, 0);
+            break;
+
+        case 0xd6:              /* continue DMA operation. 16bit */
+            control (s, 1);
+            break;
+
+        case 0xd9:              /* exit auto-init DMA after this block. 16bit */
+            s->dma_auto = 0;
+            break;
+
+        case 0xda:              /* exit auto-init DMA after this block. 8bit */
+            s->dma_auto = 0;
+            break;
+
+        case 0xe0:              /* DSP identification */
+            s->needed_bytes = 1;
+            break;
+
+        case 0xe1:
+            dsp_out_data (s, s->ver & 0xff);
+            dsp_out_data (s, s->ver >> 8);
+            break;
+
+        case 0xe2:
+            s->needed_bytes = 1;
+            goto warn;
+
+        case 0xe3:
+            {
+                int i;
+                for (i = sizeof (e3) - 1; i >= 0; --i)
+                    dsp_out_data (s, e3[i]);
+            }
+            break;
+
+        case 0xe4:              /* write test reg */
+            s->needed_bytes = 1;
+            break;
+
+        case 0xe7:
+            dolog ("Attempt to probe for ESS (0xe7)?\n");
+            break;
+
+        case 0xe8:              /* read test reg */
+            dsp_out_data (s, s->test_reg);
+            break;
+
+        case 0xf2:
+        case 0xf3:
+            dsp_out_data (s, 0xaa);
+            s->mixer_regs[0x82] |= (cmd == 0xf2) ? 1 : 2;
+            qemu_irq_raise (s->pic);
+            break;
+
+        case 0xf9:
+            s->needed_bytes = 1;
+            goto warn;
+
+        case 0xfa:
+            dsp_out_data (s, 0);
+            goto warn;
+
+        case 0xfc:              /* FIXME */
+            dsp_out_data (s, 0);
+            goto warn;
+
+        default:
+            dolog ("Unrecognized command %#x\n", cmd);
+            break;
+        }
+    }
+
+    if (!s->needed_bytes) {
+        ldebug ("\n");
+    }
+
+ exit:
+    if (!s->needed_bytes) {
+        s->cmd = -1;
+    }
+    else {
+        s->cmd = cmd;
+    }
+    return;
+
+ warn:
+    dolog ("warning: command %#x,%d is not truly understood yet\n",
+           cmd, s->needed_bytes);
+    goto exit;
+
+}
+
+static uint16_t dsp_get_lohi (SB16State *s)
+{
+    uint8_t hi = dsp_get_data (s);
+    uint8_t lo = dsp_get_data (s);
+    return (hi << 8) | lo;
+}
+
+static uint16_t dsp_get_hilo (SB16State *s)
+{
+    uint8_t lo = dsp_get_data (s);
+    uint8_t hi = dsp_get_data (s);
+    return (hi << 8) | lo;
+}
+
+static void complete (SB16State *s)
+{
+    int d0, d1, d2;
+    ldebug ("complete command %#x, in_index %d, needed_bytes %d\n",
+            s->cmd, s->in_index, s->needed_bytes);
+
+    if (s->cmd > 0xaf && s->cmd < 0xd0) {
+        d2 = dsp_get_data (s);
+        d1 = dsp_get_data (s);
+        d0 = dsp_get_data (s);
+
+        if (s->cmd & 8) {
+            dolog ("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n",
+                   s->cmd, d0, d1, d2);
+        }
+        else {
+            ldebug ("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n",
+                    s->cmd, d0, d1, d2);
+            dma_cmd (s, s->cmd, d0, d1 + (d2 << 8));
+        }
+    }
+    else {
+        switch (s->cmd) {
+        case 0x04:
+            s->csp_mode = dsp_get_data (s);
+            s->csp_reg83r = 0;
+            s->csp_reg83w = 0;
+            ldebug ("CSP command 0x04: mode=%#x\n", s->csp_mode);
+            break;
+
+        case 0x05:
+            s->csp_param = dsp_get_data (s);
+            s->csp_value = dsp_get_data (s);
+            ldebug ("CSP command 0x05: param=%#x value=%#x\n",
+                    s->csp_param,
+                    s->csp_value);
+            break;
+
+        case 0x0e:
+            d0 = dsp_get_data (s);
+            d1 = dsp_get_data (s);
+            ldebug ("write CSP register %d <- %#x\n", d1, d0);
+            if (d1 == 0x83) {
+                ldebug ("0x83[%d] <- %#x\n", s->csp_reg83r, d0);
+                s->csp_reg83[s->csp_reg83r % 4] = d0;
+                s->csp_reg83r += 1;
+            }
+            else {
+                s->csp_regs[d1] = d0;
+            }
+            break;
+
+        case 0x0f:
+            d0 = dsp_get_data (s);
+            ldebug ("read CSP register %#x -> %#x, mode=%#x\n",
+                    d0, s->csp_regs[d0], s->csp_mode);
+            if (d0 == 0x83) {
+                ldebug ("0x83[%d] -> %#x\n",
+                        s->csp_reg83w,
+                        s->csp_reg83[s->csp_reg83w % 4]);
+                dsp_out_data (s, s->csp_reg83[s->csp_reg83w % 4]);
+                s->csp_reg83w += 1;
+            }
+            else {
+                dsp_out_data (s, s->csp_regs[d0]);
+            }
+            break;
+
+        case 0x10:
+            d0 = dsp_get_data (s);
+            dolog ("cmd 0x10 d0=%#x\n", d0);
+            break;
+
+        case 0x14:
+            dma_cmd8 (s, 0, dsp_get_lohi (s) + 1);
+            break;
+
+        case 0x40:
+            s->time_const = dsp_get_data (s);
+            ldebug ("set time const %d\n", s->time_const);
+            break;
+
+        case 0x42:              /* FT2 sets output freq with this, go figure */
+#if 0
+            dolog ("cmd 0x42 might not do what it think it should\n");
+#endif
+        case 0x41:
+            s->freq = dsp_get_hilo (s);
+            ldebug ("set freq %d\n", s->freq);
+            break;
+
+        case 0x48:
+            s->block_size = dsp_get_lohi (s) + 1;
+            ldebug ("set dma block len %d\n", s->block_size);
+            break;
+
+        case 0x74:
+        case 0x75:
+        case 0x76:
+        case 0x77:
+            /* ADPCM stuff, ignore */
+            break;
+
+        case 0x80:
+            {
+                int freq, samples, bytes;
+                int64_t ticks;
+
+                freq = s->freq > 0 ? s->freq : 11025;
+                samples = dsp_get_lohi (s) + 1;
+                bytes = samples << s->fmt_stereo << (s->fmt_bits == 16);
+                ticks = muldiv64 (bytes, get_ticks_per_sec (), freq);
+                if (ticks < get_ticks_per_sec () / 1024) {
+                    qemu_irq_raise (s->pic);
+                }
+                else {
+                    if (s->aux_ts) {
+                        qemu_mod_timer (
+                            s->aux_ts,
+                            qemu_get_clock_ns (vm_clock) + ticks
+                            );
+                    }
+                }
+                ldebug ("mix silence %d %d %" PRId64 "\n", samples, bytes, ticks);
+            }
+            break;
+
+        case 0xe0:
+            d0 = dsp_get_data (s);
+            s->out_data_len = 0;
+            ldebug ("E0 data = %#x\n", d0);
+            dsp_out_data (s, ~d0);
+            break;
+
+        case 0xe2:
+#ifdef DEBUG
+            d0 = dsp_get_data (s);
+            dolog ("E2 = %#x\n", d0);
+#endif
+            break;
+
+        case 0xe4:
+            s->test_reg = dsp_get_data (s);
+            break;
+
+        case 0xf9:
+            d0 = dsp_get_data (s);
+            ldebug ("command 0xf9 with %#x\n", d0);
+            switch (d0) {
+            case 0x0e:
+                dsp_out_data (s, 0xff);
+                break;
+
+            case 0x0f:
+                dsp_out_data (s, 0x07);
+                break;
+
+            case 0x37:
+                dsp_out_data (s, 0x38);
+                break;
+
+            default:
+                dsp_out_data (s, 0x00);
+                break;
+            }
+            break;
+
+        default:
+            dolog ("complete: unrecognized command %#x\n", s->cmd);
+            return;
+        }
+    }
+
+    ldebug ("\n");
+    s->cmd = -1;
+}
+
+static void legacy_reset (SB16State *s)
+{
+    struct audsettings as;
+
+    s->freq = 11025;
+    s->fmt_signed = 0;
+    s->fmt_bits = 8;
+    s->fmt_stereo = 0;
+
+    as.freq = s->freq;
+    as.nchannels = 1;
+    as.fmt = AUD_FMT_U8;
+    as.endianness = 0;
+
+    s->voice = AUD_open_out (
+        &s->card,
+        s->voice,
+        "sb16",
+        s,
+        SB_audio_callback,
+        &as
+        );
+
+    /* Not sure about that... */
+    /* AUD_set_active_out (s->voice, 1); */
+}
+
+static void reset (SB16State *s)
+{
+    qemu_irq_lower (s->pic);
+    if (s->dma_auto) {
+        qemu_irq_raise (s->pic);
+        qemu_irq_lower (s->pic);
+    }
+
+    s->mixer_regs[0x82] = 0;
+    s->dma_auto = 0;
+    s->in_index = 0;
+    s->out_data_len = 0;
+    s->left_till_irq = 0;
+    s->needed_bytes = 0;
+    s->block_size = -1;
+    s->nzero = 0;
+    s->highspeed = 0;
+    s->v2x6 = 0;
+    s->cmd = -1;
+
+    dsp_out_data (s, 0xaa);
+    speaker (s, 0);
+    control (s, 0);
+    legacy_reset (s);
+}
+
+static IO_WRITE_PROTO (dsp_write)
+{
+    SB16State *s = opaque;
+    int iport;
+
+    iport = nport - s->port;
+
+    ldebug ("write %#x <- %#x\n", nport, val);
+    switch (iport) {
+    case 0x06:
+        switch (val) {
+        case 0x00:
+            if (s->v2x6 == 1) {
+                reset (s);
+            }
+            s->v2x6 = 0;
+            break;
+
+        case 0x01:
+        case 0x03:              /* FreeBSD kludge */
+            s->v2x6 = 1;
+            break;
+
+        case 0xc6:
+            s->v2x6 = 0;        /* Prince of Persia, csp.sys, diagnose.exe */
+            break;
+
+        case 0xb8:              /* Panic */
+            reset (s);
+            break;
+
+        case 0x39:
+            dsp_out_data (s, 0x38);
+            reset (s);
+            s->v2x6 = 0x39;
+            break;
+
+        default:
+            s->v2x6 = val;
+            break;
+        }
+        break;
+
+    case 0x0c:                  /* write data or command | write status */
+/*         if (s->highspeed) */
+/*             break; */
+
+        if (0 == s->needed_bytes) {
+            command (s, val);
+#if 0
+            if (0 == s->needed_bytes) {
+                log_dsp (s);
+            }
+#endif
+        }
+        else {
+            if (s->in_index == sizeof (s->in2_data)) {
+                dolog ("in data overrun\n");
+            }
+            else {
+                s->in2_data[s->in_index++] = val;
+                if (s->in_index == s->needed_bytes) {
+                    s->needed_bytes = 0;
+                    complete (s);
+#if 0
+                    log_dsp (s);
+#endif
+                }
+            }
+        }
+        break;
+
+    default:
+        ldebug ("(nport=%#x, val=%#x)\n", nport, val);
+        break;
+    }
+}
+
+static IO_READ_PROTO (dsp_read)
+{
+    SB16State *s = opaque;
+    int iport, retval, ack = 0;
+
+    iport = nport - s->port;
+
+    switch (iport) {
+    case 0x06:                  /* reset */
+        retval = 0xff;
+        break;
+
+    case 0x0a:                  /* read data */
+        if (s->out_data_len) {
+            retval = s->out_data[--s->out_data_len];
+            s->last_read_byte = retval;
+        }
+        else {
+            if (s->cmd != -1) {
+                dolog ("empty output buffer for command %#x\n",
+                       s->cmd);
+            }
+            retval = s->last_read_byte;
+            /* goto error; */
+        }
+        break;
+
+    case 0x0c:                  /* 0 can write */
+        retval = s->can_write ? 0 : 0x80;
+        break;
+
+    case 0x0d:                  /* timer interrupt clear */
+        /* dolog ("timer interrupt clear\n"); */
+        retval = 0;
+        break;
+
+    case 0x0e:                  /* data available status | irq 8 ack */
+        retval = (!s->out_data_len || s->highspeed) ? 0 : 0x80;
+        if (s->mixer_regs[0x82] & 1) {
+            ack = 1;
+            s->mixer_regs[0x82] &= 1;
+            qemu_irq_lower (s->pic);
+        }
+        break;
+
+    case 0x0f:                  /* irq 16 ack */
+        retval = 0xff;
+        if (s->mixer_regs[0x82] & 2) {
+            ack = 1;
+            s->mixer_regs[0x82] &= 2;
+            qemu_irq_lower (s->pic);
+        }
+        break;
+
+    default:
+        goto error;
+    }
+
+    if (!ack) {
+        ldebug ("read %#x -> %#x\n", nport, retval);
+    }
+
+    return retval;
+
+ error:
+    dolog ("warning: dsp_read %#x error\n", nport);
+    return 0xff;
+}
+
+static void reset_mixer (SB16State *s)
+{
+    int i;
+
+    memset (s->mixer_regs, 0xff, 0x7f);
+    memset (s->mixer_regs + 0x83, 0xff, sizeof (s->mixer_regs) - 0x83);
+
+    s->mixer_regs[0x02] = 4;    /* master volume 3bits */
+    s->mixer_regs[0x06] = 4;    /* MIDI volume 3bits */
+    s->mixer_regs[0x08] = 0;    /* CD volume 3bits */
+    s->mixer_regs[0x0a] = 0;    /* voice volume 2bits */
+
+    /* d5=input filt, d3=lowpass filt, d1,d2=input source */
+    s->mixer_regs[0x0c] = 0;
+
+    /* d5=output filt, d1=stereo switch */
+    s->mixer_regs[0x0e] = 0;
+
+    /* voice volume L d5,d7, R d1,d3 */
+    s->mixer_regs[0x04] = (4 << 5) | (4 << 1);
+    /* master ... */
+    s->mixer_regs[0x22] = (4 << 5) | (4 << 1);
+    /* MIDI ... */
+    s->mixer_regs[0x26] = (4 << 5) | (4 << 1);
+
+    for (i = 0x30; i < 0x48; i++) {
+        s->mixer_regs[i] = 0x20;
+    }
+}
+
+static IO_WRITE_PROTO (mixer_write_indexb)
+{
+    SB16State *s = opaque;
+    (void) nport;
+    s->mixer_nreg = val;
+}
+
+static IO_WRITE_PROTO (mixer_write_datab)
+{
+    SB16State *s = opaque;
+
+    (void) nport;
+    ldebug ("mixer_write [%#x] <- %#x\n", s->mixer_nreg, val);
+
+    switch (s->mixer_nreg) {
+    case 0x00:
+        reset_mixer (s);
+        break;
+
+    case 0x80:
+        {
+            int irq = irq_of_magic (val);
+            ldebug ("setting irq to %d (val=%#x)\n", irq, val);
+            if (irq > 0) {
+                s->irq = irq;
+            }
+        }
+        break;
+
+    case 0x81:
+        {
+            int dma, hdma;
+
+            dma = ctz32 (val & 0xf);
+            hdma = ctz32 (val & 0xf0);
+            if (dma != s->dma || hdma != s->hdma) {
+                dolog (
+                    "attempt to change DMA "
+                    "8bit %d(%d), 16bit %d(%d) (val=%#x)\n",
+                    dma, s->dma, hdma, s->hdma, val);
+            }
+#if 0
+            s->dma = dma;
+            s->hdma = hdma;
+#endif
+        }
+        break;
+
+    case 0x82:
+        dolog ("attempt to write into IRQ status register (val=%#x)\n",
+               val);
+        return;
+
+    default:
+        if (s->mixer_nreg >= 0x80) {
+            ldebug ("attempt to write mixer[%#x] <- %#x\n", s->mixer_nreg, val);
+        }
+        break;
+    }
+
+    s->mixer_regs[s->mixer_nreg] = val;
+}
+
+static IO_WRITE_PROTO (mixer_write_indexw)
+{
+    mixer_write_indexb (opaque, nport, val & 0xff);
+    mixer_write_datab (opaque, nport, (val >> 8) & 0xff);
+}
+
+static IO_READ_PROTO (mixer_read)
+{
+    SB16State *s = opaque;
+
+    (void) nport;
+#ifndef DEBUG_SB16_MOST
+    if (s->mixer_nreg != 0x82) {
+        ldebug ("mixer_read[%#x] -> %#x\n",
+                s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
+    }
+#else
+    ldebug ("mixer_read[%#x] -> %#x\n",
+            s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
+#endif
+    return s->mixer_regs[s->mixer_nreg];
+}
+
+static int write_audio (SB16State *s, int nchan, int dma_pos,
+                        int dma_len, int len)
+{
+    int temp, net;
+    uint8_t tmpbuf[4096];
+
+    temp = len;
+    net = 0;
+
+    while (temp) {
+        int left = dma_len - dma_pos;
+        int copied;
+        size_t to_copy;
+
+        to_copy = audio_MIN (temp, left);
+        if (to_copy > sizeof (tmpbuf)) {
+            to_copy = sizeof (tmpbuf);
+        }
+
+        copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy);
+        copied = AUD_write (s->voice, tmpbuf, copied);
+
+        temp -= copied;
+        dma_pos = (dma_pos + copied) % dma_len;
+        net += copied;
+
+        if (!copied) {
+            break;
+        }
+    }
+
+    return net;
+}
+
+static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
+{
+    SB16State *s = opaque;
+    int till, copy, written, free;
+
+    if (s->block_size <= 0) {
+        dolog ("invalid block size=%d nchan=%d dma_pos=%d dma_len=%d\n",
+               s->block_size, nchan, dma_pos, dma_len);
+        return dma_pos;
+    }
+
+    if (s->left_till_irq < 0) {
+        s->left_till_irq = s->block_size;
+    }
+
+    if (s->voice) {
+        free = s->audio_free & ~s->align;
+        if ((free <= 0) || !dma_len) {
+            return dma_pos;
+        }
+    }
+    else {
+        free = dma_len;
+    }
+
+    copy = free;
+    till = s->left_till_irq;
+
+#ifdef DEBUG_SB16_MOST
+    dolog ("pos:%06d %d till:%d len:%d\n",
+           dma_pos, free, till, dma_len);
+#endif
+
+    if (till <= copy) {
+        if (0 == s->dma_auto) {
+            copy = till;
+        }
+    }
+
+    written = write_audio (s, nchan, dma_pos, dma_len, copy);
+    dma_pos = (dma_pos + written) % dma_len;
+    s->left_till_irq -= written;
+
+    if (s->left_till_irq <= 0) {
+        s->mixer_regs[0x82] |= (nchan & 4) ? 2 : 1;
+        qemu_irq_raise (s->pic);
+        if (0 == s->dma_auto) {
+            control (s, 0);
+            speaker (s, 0);
+        }
+    }
+
+#ifdef DEBUG_SB16_MOST
+    ldebug ("pos %5d free %5d size %5d till % 5d copy %5d written %5d size %5d\n",
+            dma_pos, free, dma_len, s->left_till_irq, copy, written,
+            s->block_size);
+#endif
+
+    while (s->left_till_irq <= 0) {
+        s->left_till_irq = s->block_size + s->left_till_irq;
+    }
+
+    return dma_pos;
+}
+
+static void SB_audio_callback (void *opaque, int free)
+{
+    SB16State *s = opaque;
+    s->audio_free = free;
+}
+
+static int sb16_post_load (void *opaque, int version_id)
+{
+    SB16State *s = opaque;
+
+    if (s->voice) {
+        AUD_close_out (&s->card, s->voice);
+        s->voice = NULL;
+    }
+
+    if (s->dma_running) {
+        if (s->freq) {
+            struct audsettings as;
+
+            s->audio_free = 0;
+
+            as.freq = s->freq;
+            as.nchannels = 1 << s->fmt_stereo;
+            as.fmt = s->fmt;
+            as.endianness = 0;
+
+            s->voice = AUD_open_out (
+                &s->card,
+                s->voice,
+                "sb16",
+                s,
+                SB_audio_callback,
+                &as
+                );
+        }
+
+        control (s, 1);
+        speaker (s, s->speaker);
+    }
+    return 0;
+}
+
+static const VMStateDescription vmstate_sb16 = {
+    .name = "sb16",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = sb16_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT32 (irq, SB16State),
+        VMSTATE_UINT32 (dma, SB16State),
+        VMSTATE_UINT32 (hdma, SB16State),
+        VMSTATE_UINT32 (port, SB16State),
+        VMSTATE_UINT32 (ver, SB16State),
+        VMSTATE_INT32 (in_index, SB16State),
+        VMSTATE_INT32 (out_data_len, SB16State),
+        VMSTATE_INT32 (fmt_stereo, SB16State),
+        VMSTATE_INT32 (fmt_signed, SB16State),
+        VMSTATE_INT32 (fmt_bits, SB16State),
+        VMSTATE_UINT32 (fmt, SB16State),
+        VMSTATE_INT32 (dma_auto, SB16State),
+        VMSTATE_INT32 (block_size, SB16State),
+        VMSTATE_INT32 (fifo, SB16State),
+        VMSTATE_INT32 (freq, SB16State),
+        VMSTATE_INT32 (time_const, SB16State),
+        VMSTATE_INT32 (speaker, SB16State),
+        VMSTATE_INT32 (needed_bytes, SB16State),
+        VMSTATE_INT32 (cmd, SB16State),
+        VMSTATE_INT32 (use_hdma, SB16State),
+        VMSTATE_INT32 (highspeed, SB16State),
+        VMSTATE_INT32 (can_write, SB16State),
+        VMSTATE_INT32 (v2x6, SB16State),
+
+        VMSTATE_UINT8 (csp_param, SB16State),
+        VMSTATE_UINT8 (csp_value, SB16State),
+        VMSTATE_UINT8 (csp_mode, SB16State),
+        VMSTATE_UINT8 (csp_param, SB16State),
+        VMSTATE_BUFFER (csp_regs, SB16State),
+        VMSTATE_UINT8 (csp_index, SB16State),
+        VMSTATE_BUFFER (csp_reg83, SB16State),
+        VMSTATE_INT32 (csp_reg83r, SB16State),
+        VMSTATE_INT32 (csp_reg83w, SB16State),
+
+        VMSTATE_BUFFER (in2_data, SB16State),
+        VMSTATE_BUFFER (out_data, SB16State),
+        VMSTATE_UINT8 (test_reg, SB16State),
+        VMSTATE_UINT8 (last_read_byte, SB16State),
+
+        VMSTATE_INT32 (nzero, SB16State),
+        VMSTATE_INT32 (left_till_irq, SB16State),
+        VMSTATE_INT32 (dma_running, SB16State),
+        VMSTATE_INT32 (bytes_per_second, SB16State),
+        VMSTATE_INT32 (align, SB16State),
+
+        VMSTATE_INT32 (mixer_nreg, SB16State),
+        VMSTATE_BUFFER (mixer_regs, SB16State),
+
+        VMSTATE_END_OF_LIST ()
+    }
+};
+
+static const MemoryRegionPortio sb16_ioport_list[] = {
+    {  4, 1, 1, .write = mixer_write_indexb },
+    {  4, 1, 2, .write = mixer_write_indexw },
+    {  5, 1, 1, .read = mixer_read, .write = mixer_write_datab },
+    {  6, 1, 1, .read = dsp_read, .write = dsp_write },
+    { 10, 1, 1, .read = dsp_read },
+    { 12, 1, 1, .write = dsp_write },
+    { 12, 4, 1, .read = dsp_read },
+    PORTIO_END_OF_LIST (),
+};
+
+
+static int sb16_initfn (ISADevice *dev)
+{
+    SB16State *s;
+
+    s = DO_UPCAST (SB16State, dev, dev);
+
+    s->cmd = -1;
+    isa_init_irq (dev, &s->pic, s->irq);
+
+    s->mixer_regs[0x80] = magic_of_irq (s->irq);
+    s->mixer_regs[0x81] = (1 << s->dma) | (1 << s->hdma);
+    s->mixer_regs[0x82] = 2 << 5;
+
+    s->csp_regs[5] = 1;
+    s->csp_regs[9] = 0xf8;
+
+    reset_mixer (s);
+    s->aux_ts = qemu_new_timer_ns (vm_clock, aux_timer, s);
+    if (!s->aux_ts) {
+        dolog ("warning: Could not create auxiliary timer\n");
+    }
+
+    isa_register_portio_list (dev, s->port, sb16_ioport_list, s, "sb16");
+
+    DMA_register_channel (s->hdma, SB_read_DMA, s);
+    DMA_register_channel (s->dma, SB_read_DMA, s);
+    s->can_write = 1;
+
+    AUD_register_card ("sb16", &s->card);
+    return 0;
+}
+
+int SB16_init (ISABus *bus)
+{
+    isa_create_simple (bus, "sb16");
+    return 0;
+}
+
+static Property sb16_properties[] = {
+    DEFINE_PROP_HEX32  ("version", SB16State, ver,  0x0405), /* 4.5 */
+    DEFINE_PROP_HEX32  ("iobase",  SB16State, port, 0x220),
+    DEFINE_PROP_UINT32 ("irq",     SB16State, irq,  5),
+    DEFINE_PROP_UINT32 ("dma",     SB16State, dma,  1),
+    DEFINE_PROP_UINT32 ("dma16",   SB16State, hdma, 5),
+    DEFINE_PROP_END_OF_LIST (),
+};
+
+static void sb16_class_initfn (ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS (klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS (klass);
+    ic->init = sb16_initfn;
+    dc->desc = "Creative Sound Blaster 16";
+    dc->vmsd = &vmstate_sb16;
+    dc->props = sb16_properties;
+}
+
+static const TypeInfo sb16_info = {
+    .name          = "sb16",
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof (SB16State),
+    .class_init    = sb16_class_initfn,
+};
+
+static void sb16_register_types (void)
+{
+    type_register_static (&sb16_info);
+}
+
+type_init (sb16_register_types)
diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c
new file mode 100644 (file)
index 0000000..6b5a349
--- /dev/null
@@ -0,0 +1,716 @@
+/*
+ * WM8750 audio CODEC.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This file is licensed under GNU GPL.
+ */
+
+#include "hw/hw.h"
+#include "hw/i2c/i2c.h"
+#include "audio/audio.h"
+
+#define IN_PORT_N      3
+#define OUT_PORT_N     3
+
+#define CODEC          "wm8750"
+
+typedef struct {
+    int adc;
+    int adc_hz;
+    int dac;
+    int dac_hz;
+} WMRate;
+
+typedef struct {
+    I2CSlave i2c;
+    uint8_t i2c_data[2];
+    int i2c_len;
+    QEMUSoundCard card;
+    SWVoiceIn *adc_voice[IN_PORT_N];
+    SWVoiceOut *dac_voice[OUT_PORT_N];
+    int enable;
+    void (*data_req)(void *, int, int);
+    void *opaque;
+    uint8_t data_in[4096];
+    uint8_t data_out[4096];
+    int idx_in, req_in;
+    int idx_out, req_out;
+
+    SWVoiceOut **out[2];
+    uint8_t outvol[7], outmute[2];
+    SWVoiceIn **in[2];
+    uint8_t invol[4], inmute[2];
+
+    uint8_t diff[2], pol, ds, monomix[2], alc, mute;
+    uint8_t path[4], mpath[2], power, format;
+    const WMRate *rate;
+    uint8_t rate_vmstate;
+    int adc_hz, dac_hz, ext_adc_hz, ext_dac_hz, master;
+} WM8750State;
+
+/* pow(10.0, -i / 20.0) * 255, i = 0..42 */
+static const uint8_t wm8750_vol_db_table[] = {
+    255, 227, 203, 181, 161, 143, 128, 114, 102, 90, 81, 72, 64, 57, 51, 45,
+    40, 36, 32, 29, 26, 23, 20, 18, 16, 14, 13, 11, 10, 9, 8, 7, 6, 6, 5, 5,
+    4, 4, 3, 3, 3, 2, 2
+};
+
+#define WM8750_OUTVOL_TRANSFORM(x)     wm8750_vol_db_table[(0x7f - x) / 3]
+#define WM8750_INVOL_TRANSFORM(x)      (x << 2)
+
+static inline void wm8750_in_load(WM8750State *s)
+{
+    if (s->idx_in + s->req_in <= sizeof(s->data_in))
+        return;
+    s->idx_in = audio_MAX(0, (int) sizeof(s->data_in) - s->req_in);
+    AUD_read(*s->in[0], s->data_in + s->idx_in,
+             sizeof(s->data_in) - s->idx_in);
+}
+
+static inline void wm8750_out_flush(WM8750State *s)
+{
+    int sent = 0;
+    while (sent < s->idx_out)
+        sent += AUD_write(*s->out[0], s->data_out + sent, s->idx_out - sent)
+                ?: s->idx_out;
+    s->idx_out = 0;
+}
+
+static void wm8750_audio_in_cb(void *opaque, int avail_b)
+{
+    WM8750State *s = (WM8750State *) opaque;
+    s->req_in = avail_b;
+    s->data_req(s->opaque, s->req_out >> 2, avail_b >> 2);
+}
+
+static void wm8750_audio_out_cb(void *opaque, int free_b)
+{
+    WM8750State *s = (WM8750State *) opaque;
+
+    if (s->idx_out >= free_b) {
+        s->idx_out = free_b;
+        s->req_out = 0;
+        wm8750_out_flush(s);
+    } else
+        s->req_out = free_b - s->idx_out;
+    s->data_req(s->opaque, s->req_out >> 2, s->req_in >> 2);
+}
+
+static const WMRate wm_rate_table[] = {
+    {  256, 48000,  256, 48000 },      /* SR: 00000 */
+    {  384, 48000,  384, 48000 },      /* SR: 00001 */
+    {  256, 48000, 1536,  8000 },      /* SR: 00010 */
+    {  384, 48000, 2304,  8000 },      /* SR: 00011 */
+    { 1536,  8000,  256, 48000 },      /* SR: 00100 */
+    { 2304,  8000,  384, 48000 },      /* SR: 00101 */
+    { 1536,  8000, 1536,  8000 },      /* SR: 00110 */
+    { 2304,  8000, 2304,  8000 },      /* SR: 00111 */
+    { 1024, 12000, 1024, 12000 },      /* SR: 01000 */
+    { 1526, 12000, 1536, 12000 },      /* SR: 01001 */
+    {  768, 16000,  768, 16000 },      /* SR: 01010 */
+    { 1152, 16000, 1152, 16000 },      /* SR: 01011 */
+    {  384, 32000,  384, 32000 },      /* SR: 01100 */
+    {  576, 32000,  576, 32000 },      /* SR: 01101 */
+    {  128, 96000,  128, 96000 },      /* SR: 01110 */
+    {  192, 96000,  192, 96000 },      /* SR: 01111 */
+    {  256, 44100,  256, 44100 },      /* SR: 10000 */
+    {  384, 44100,  384, 44100 },      /* SR: 10001 */
+    {  256, 44100, 1408,  8018 },      /* SR: 10010 */
+    {  384, 44100, 2112,  8018 },      /* SR: 10011 */
+    { 1408,  8018,  256, 44100 },      /* SR: 10100 */
+    { 2112,  8018,  384, 44100 },      /* SR: 10101 */
+    { 1408,  8018, 1408,  8018 },      /* SR: 10110 */
+    { 2112,  8018, 2112,  8018 },      /* SR: 10111 */
+    { 1024, 11025, 1024, 11025 },      /* SR: 11000 */
+    { 1536, 11025, 1536, 11025 },      /* SR: 11001 */
+    {  512, 22050,  512, 22050 },      /* SR: 11010 */
+    {  768, 22050,  768, 22050 },      /* SR: 11011 */
+    {  512, 24000,  512, 24000 },      /* SR: 11100 */
+    {  768, 24000,  768, 24000 },      /* SR: 11101 */
+    {  128, 88200,  128, 88200 },      /* SR: 11110 */
+    {  192, 88200,  192, 88200 },      /* SR: 11111 */
+};
+
+static void wm8750_vol_update(WM8750State *s)
+{
+    /* FIXME: multiply all volumes by s->invol[2], s->invol[3] */
+
+    AUD_set_volume_in(s->adc_voice[0], s->mute,
+                    s->inmute[0] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[0]),
+                    s->inmute[1] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[1]));
+    AUD_set_volume_in(s->adc_voice[1], s->mute,
+                    s->inmute[0] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[0]),
+                    s->inmute[1] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[1]));
+    AUD_set_volume_in(s->adc_voice[2], s->mute,
+                    s->inmute[0] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[0]),
+                    s->inmute[1] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[1]));
+
+    /* FIXME: multiply all volumes by s->outvol[0], s->outvol[1] */
+
+    /* Speaker: LOUT2VOL ROUT2VOL */
+    AUD_set_volume_out(s->dac_voice[0], s->mute,
+                    s->outmute[0] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[4]),
+                    s->outmute[1] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[5]));
+
+    /* Headphone: LOUT1VOL ROUT1VOL */
+    AUD_set_volume_out(s->dac_voice[1], s->mute,
+                    s->outmute[0] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[2]),
+                    s->outmute[1] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[3]));
+
+    /* MONOOUT: MONOVOL MONOVOL */
+    AUD_set_volume_out(s->dac_voice[2], s->mute,
+                    s->outmute[0] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[6]),
+                    s->outmute[1] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[6]));
+}
+
+static void wm8750_set_format(WM8750State *s)
+{
+    int i;
+    struct audsettings in_fmt;
+    struct audsettings out_fmt;
+
+    wm8750_out_flush(s);
+
+    if (s->in[0] && *s->in[0])
+        AUD_set_active_in(*s->in[0], 0);
+    if (s->out[0] && *s->out[0])
+        AUD_set_active_out(*s->out[0], 0);
+
+    for (i = 0; i < IN_PORT_N; i ++)
+        if (s->adc_voice[i]) {
+            AUD_close_in(&s->card, s->adc_voice[i]);
+            s->adc_voice[i] = NULL;
+        }
+    for (i = 0; i < OUT_PORT_N; i ++)
+        if (s->dac_voice[i]) {
+            AUD_close_out(&s->card, s->dac_voice[i]);
+            s->dac_voice[i] = NULL;
+        }
+
+    if (!s->enable)
+        return;
+
+    /* Setup input */
+    in_fmt.endianness = 0;
+    in_fmt.nchannels = 2;
+    in_fmt.freq = s->adc_hz;
+    in_fmt.fmt = AUD_FMT_S16;
+
+    s->adc_voice[0] = AUD_open_in(&s->card, s->adc_voice[0],
+                    CODEC ".input1", s, wm8750_audio_in_cb, &in_fmt);
+    s->adc_voice[1] = AUD_open_in(&s->card, s->adc_voice[1],
+                    CODEC ".input2", s, wm8750_audio_in_cb, &in_fmt);
+    s->adc_voice[2] = AUD_open_in(&s->card, s->adc_voice[2],
+                    CODEC ".input3", s, wm8750_audio_in_cb, &in_fmt);
+
+    /* Setup output */
+    out_fmt.endianness = 0;
+    out_fmt.nchannels = 2;
+    out_fmt.freq = s->dac_hz;
+    out_fmt.fmt = AUD_FMT_S16;
+
+    s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
+                    CODEC ".speaker", s, wm8750_audio_out_cb, &out_fmt);
+    s->dac_voice[1] = AUD_open_out(&s->card, s->dac_voice[1],
+                    CODEC ".headphone", s, wm8750_audio_out_cb, &out_fmt);
+    /* MONOMIX is also in stereo for simplicity */
+    s->dac_voice[2] = AUD_open_out(&s->card, s->dac_voice[2],
+                    CODEC ".monomix", s, wm8750_audio_out_cb, &out_fmt);
+    /* no sense emulating OUT3 which is a mix of other outputs */
+
+    wm8750_vol_update(s);
+
+    /* We should connect the left and right channels to their
+     * respective inputs/outputs but we have completely no need
+     * for mixing or combining paths to different ports, so we
+     * connect both channels to where the left channel is routed.  */
+    if (s->in[0] && *s->in[0])
+        AUD_set_active_in(*s->in[0], 1);
+    if (s->out[0] && *s->out[0])
+        AUD_set_active_out(*s->out[0], 1);
+}
+
+static void wm8750_clk_update(WM8750State *s, int ext)
+{
+    if (s->master || !s->ext_dac_hz)
+        s->dac_hz = s->rate->dac_hz;
+    else
+        s->dac_hz = s->ext_dac_hz;
+
+    if (s->master || !s->ext_adc_hz)
+        s->adc_hz = s->rate->adc_hz;
+    else
+        s->adc_hz = s->ext_adc_hz;
+
+    if (s->master || (!s->ext_dac_hz && !s->ext_adc_hz)) {
+        if (!ext)
+            wm8750_set_format(s);
+    } else {
+        if (ext)
+            wm8750_set_format(s);
+    }
+}
+
+static void wm8750_reset(I2CSlave *i2c)
+{
+    WM8750State *s = (WM8750State *) i2c;
+    s->rate = &wm_rate_table[0];
+    s->enable = 0;
+    wm8750_clk_update(s, 1);
+    s->diff[0] = 0;
+    s->diff[1] = 0;
+    s->ds = 0;
+    s->alc = 0;
+    s->in[0] = &s->adc_voice[0];
+    s->invol[0] = 0x17;
+    s->invol[1] = 0x17;
+    s->invol[2] = 0xc3;
+    s->invol[3] = 0xc3;
+    s->out[0] = &s->dac_voice[0];
+    s->outvol[0] = 0xff;
+    s->outvol[1] = 0xff;
+    s->outvol[2] = 0x79;
+    s->outvol[3] = 0x79;
+    s->outvol[4] = 0x79;
+    s->outvol[5] = 0x79;
+    s->outvol[6] = 0x79;
+    s->inmute[0] = 0;
+    s->inmute[1] = 0;
+    s->outmute[0] = 0;
+    s->outmute[1] = 0;
+    s->mute = 1;
+    s->path[0] = 0;
+    s->path[1] = 0;
+    s->path[2] = 0;
+    s->path[3] = 0;
+    s->mpath[0] = 0;
+    s->mpath[1] = 0;
+    s->format = 0x0a;
+    s->idx_in = sizeof(s->data_in);
+    s->req_in = 0;
+    s->idx_out = 0;
+    s->req_out = 0;
+    wm8750_vol_update(s);
+    s->i2c_len = 0;
+}
+
+static void wm8750_event(I2CSlave *i2c, enum i2c_event event)
+{
+    WM8750State *s = (WM8750State *) i2c;
+
+    switch (event) {
+    case I2C_START_SEND:
+        s->i2c_len = 0;
+        break;
+    case I2C_FINISH:
+#ifdef VERBOSE
+        if (s->i2c_len < 2)
+            printf("%s: message too short (%i bytes)\n",
+                            __FUNCTION__, s->i2c_len);
+#endif
+        break;
+    default:
+        break;
+    }
+}
+
+#define WM8750_LINVOL  0x00
+#define WM8750_RINVOL  0x01
+#define WM8750_LOUT1V  0x02
+#define WM8750_ROUT1V  0x03
+#define WM8750_ADCDAC  0x05
+#define WM8750_IFACE   0x07
+#define WM8750_SRATE   0x08
+#define WM8750_LDAC    0x0a
+#define WM8750_RDAC    0x0b
+#define WM8750_BASS    0x0c
+#define WM8750_TREBLE  0x0d
+#define WM8750_RESET   0x0f
+#define WM8750_3D      0x10
+#define WM8750_ALC1    0x11
+#define WM8750_ALC2    0x12
+#define WM8750_ALC3    0x13
+#define WM8750_NGATE   0x14
+#define WM8750_LADC    0x15
+#define WM8750_RADC    0x16
+#define WM8750_ADCTL1  0x17
+#define WM8750_ADCTL2  0x18
+#define WM8750_PWR1    0x19
+#define WM8750_PWR2    0x1a
+#define WM8750_ADCTL3  0x1b
+#define WM8750_ADCIN   0x1f
+#define WM8750_LADCIN  0x20
+#define WM8750_RADCIN  0x21
+#define WM8750_LOUTM1  0x22
+#define WM8750_LOUTM2  0x23
+#define WM8750_ROUTM1  0x24
+#define WM8750_ROUTM2  0x25
+#define WM8750_MOUTM1  0x26
+#define WM8750_MOUTM2  0x27
+#define WM8750_LOUT2V  0x28
+#define WM8750_ROUT2V  0x29
+#define WM8750_MOUTV   0x2a
+
+static int wm8750_tx(I2CSlave *i2c, uint8_t data)
+{
+    WM8750State *s = (WM8750State *) i2c;
+    uint8_t cmd;
+    uint16_t value;
+
+    if (s->i2c_len >= 2) {
+#ifdef VERBOSE
+        printf("%s: long message (%i bytes)\n", __func__, s->i2c_len);
+#endif
+        return 1;
+    }
+    s->i2c_data[s->i2c_len ++] = data;
+    if (s->i2c_len != 2)
+        return 0;
+
+    cmd = s->i2c_data[0] >> 1;
+    value = ((s->i2c_data[0] << 8) | s->i2c_data[1]) & 0x1ff;
+
+    switch (cmd) {
+    case WM8750_LADCIN:        /* ADC Signal Path Control (Left) */
+        s->diff[0] = (((value >> 6) & 3) == 3);        /* LINSEL */
+        if (s->diff[0])
+            s->in[0] = &s->adc_voice[0 + s->ds * 1];
+        else
+            s->in[0] = &s->adc_voice[((value >> 6) & 3) * 1 + 0];
+        break;
+
+    case WM8750_RADCIN:        /* ADC Signal Path Control (Right) */
+        s->diff[1] = (((value >> 6) & 3) == 3);        /* RINSEL */
+        if (s->diff[1])
+            s->in[1] = &s->adc_voice[0 + s->ds * 1];
+        else
+            s->in[1] = &s->adc_voice[((value >> 6) & 3) * 1 + 0];
+        break;
+
+    case WM8750_ADCIN: /* ADC Input Mode */
+        s->ds = (value >> 8) & 1;      /* DS */
+        if (s->diff[0])
+            s->in[0] = &s->adc_voice[0 + s->ds * 1];
+        if (s->diff[1])
+            s->in[1] = &s->adc_voice[0 + s->ds * 1];
+        s->monomix[0] = (value >> 6) & 3;      /* MONOMIX */
+        break;
+
+    case WM8750_ADCTL1:        /* Additional Control (1) */
+        s->monomix[1] = (value >> 1) & 1;      /* DMONOMIX */
+        break;
+
+    case WM8750_PWR1:  /* Power Management (1) */
+        s->enable = ((value >> 6) & 7) == 3;   /* VMIDSEL, VREF */
+        wm8750_set_format(s);
+        break;
+
+    case WM8750_LINVOL:        /* Left Channel PGA */
+        s->invol[0] = value & 0x3f;            /* LINVOL */
+        s->inmute[0] = (value >> 7) & 1;       /* LINMUTE */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_RINVOL:        /* Right Channel PGA */
+        s->invol[1] = value & 0x3f;            /* RINVOL */
+        s->inmute[1] = (value >> 7) & 1;       /* RINMUTE */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_ADCDAC:        /* ADC and DAC Control */
+        s->pol = (value >> 5) & 3;             /* ADCPOL */
+        s->mute = (value >> 3) & 1;            /* DACMU */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_ADCTL3:        /* Additional Control (3) */
+        break;
+
+    case WM8750_LADC:  /* Left ADC Digital Volume */
+        s->invol[2] = value & 0xff;            /* LADCVOL */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_RADC:  /* Right ADC Digital Volume */
+        s->invol[3] = value & 0xff;            /* RADCVOL */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_ALC1:  /* ALC Control (1) */
+        s->alc = (value >> 7) & 3;             /* ALCSEL */
+        break;
+
+    case WM8750_NGATE: /* Noise Gate Control */
+    case WM8750_3D:    /* 3D enhance */
+        break;
+
+    case WM8750_LDAC:  /* Left Channel Digital Volume */
+        s->outvol[0] = value & 0xff;           /* LDACVOL */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_RDAC:  /* Right Channel Digital Volume */
+        s->outvol[1] = value & 0xff;           /* RDACVOL */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_BASS:  /* Bass Control */
+        break;
+
+    case WM8750_LOUTM1:        /* Left Mixer Control (1) */
+        s->path[0] = (value >> 8) & 1;         /* LD2LO */
+        /* TODO: mute/unmute respective paths */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_LOUTM2:        /* Left Mixer Control (2) */
+        s->path[1] = (value >> 8) & 1;         /* RD2LO */
+        /* TODO: mute/unmute respective paths */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_ROUTM1:        /* Right Mixer Control (1) */
+        s->path[2] = (value >> 8) & 1;         /* LD2RO */
+        /* TODO: mute/unmute respective paths */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_ROUTM2:        /* Right Mixer Control (2) */
+        s->path[3] = (value >> 8) & 1;         /* RD2RO */
+        /* TODO: mute/unmute respective paths */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_MOUTM1:        /* Mono Mixer Control (1) */
+        s->mpath[0] = (value >> 8) & 1;                /* LD2MO */
+        /* TODO: mute/unmute respective paths */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_MOUTM2:        /* Mono Mixer Control (2) */
+        s->mpath[1] = (value >> 8) & 1;                /* RD2MO */
+        /* TODO: mute/unmute respective paths */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_LOUT1V:        /* LOUT1 Volume */
+        s->outvol[2] = value & 0x7f;           /* LOUT1VOL */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_LOUT2V:        /* LOUT2 Volume */
+        s->outvol[4] = value & 0x7f;           /* LOUT2VOL */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_ROUT1V:        /* ROUT1 Volume */
+        s->outvol[3] = value & 0x7f;           /* ROUT1VOL */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_ROUT2V:        /* ROUT2 Volume */
+        s->outvol[5] = value & 0x7f;           /* ROUT2VOL */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_MOUTV: /* MONOOUT Volume */
+        s->outvol[6] = value & 0x7f;           /* MONOOUTVOL */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_ADCTL2:        /* Additional Control (2) */
+        break;
+
+    case WM8750_PWR2:  /* Power Management (2) */
+        s->power = value & 0x7e;
+        /* TODO: mute/unmute respective paths */
+        wm8750_vol_update(s);
+        break;
+
+    case WM8750_IFACE: /* Digital Audio Interface Format */
+        s->format = value;
+        s->master = (value >> 6) & 1;                  /* MS */
+        wm8750_clk_update(s, s->master);
+        break;
+
+    case WM8750_SRATE: /* Clocking and Sample Rate Control */
+        s->rate = &wm_rate_table[(value >> 1) & 0x1f];
+        wm8750_clk_update(s, 0);
+        break;
+
+    case WM8750_RESET: /* Reset */
+        wm8750_reset(&s->i2c);
+        break;
+
+#ifdef VERBOSE
+    default:
+        printf("%s: unknown register %02x\n", __FUNCTION__, cmd);
+#endif
+    }
+
+    return 0;
+}
+
+static int wm8750_rx(I2CSlave *i2c)
+{
+    return 0x00;
+}
+
+static void wm8750_pre_save(void *opaque)
+{
+    WM8750State *s = opaque;
+
+    s->rate_vmstate = s->rate - wm_rate_table;
+}
+
+static int wm8750_post_load(void *opaque, int version_id)
+{
+    WM8750State *s = opaque;
+
+    s->rate = &wm_rate_table[s->rate_vmstate & 0x1f];
+    return 0;
+}
+
+static const VMStateDescription vmstate_wm8750 = {
+    .name = CODEC,
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .pre_save = wm8750_pre_save,
+    .post_load = wm8750_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT8_ARRAY(i2c_data, WM8750State, 2),
+        VMSTATE_INT32(i2c_len, WM8750State),
+        VMSTATE_INT32(enable, WM8750State),
+        VMSTATE_INT32(idx_in, WM8750State),
+        VMSTATE_INT32(req_in, WM8750State),
+        VMSTATE_INT32(idx_out, WM8750State),
+        VMSTATE_INT32(req_out, WM8750State),
+        VMSTATE_UINT8_ARRAY(outvol, WM8750State, 7),
+        VMSTATE_UINT8_ARRAY(outmute, WM8750State, 2),
+        VMSTATE_UINT8_ARRAY(invol, WM8750State, 4),
+        VMSTATE_UINT8_ARRAY(inmute, WM8750State, 2),
+        VMSTATE_UINT8_ARRAY(diff, WM8750State, 2),
+        VMSTATE_UINT8(pol, WM8750State),
+        VMSTATE_UINT8(ds, WM8750State),
+        VMSTATE_UINT8_ARRAY(monomix, WM8750State, 2),
+        VMSTATE_UINT8(alc, WM8750State),
+        VMSTATE_UINT8(mute, WM8750State),
+        VMSTATE_UINT8_ARRAY(path, WM8750State, 4),
+        VMSTATE_UINT8_ARRAY(mpath, WM8750State, 2),
+        VMSTATE_UINT8(format, WM8750State),
+        VMSTATE_UINT8(power, WM8750State),
+        VMSTATE_UINT8(rate_vmstate, WM8750State),
+        VMSTATE_I2C_SLAVE(i2c, WM8750State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int wm8750_init(I2CSlave *i2c)
+{
+    WM8750State *s = FROM_I2C_SLAVE(WM8750State, i2c);
+
+    AUD_register_card(CODEC, &s->card);
+    wm8750_reset(&s->i2c);
+
+    return 0;
+}
+
+#if 0
+static void wm8750_fini(I2CSlave *i2c)
+{
+    WM8750State *s = (WM8750State *) i2c;
+    wm8750_reset(&s->i2c);
+    AUD_remove_card(&s->card);
+    g_free(s);
+}
+#endif
+
+void wm8750_data_req_set(DeviceState *dev,
+                void (*data_req)(void *, int, int), void *opaque)
+{
+    WM8750State *s = FROM_I2C_SLAVE(WM8750State, I2C_SLAVE(dev));
+    s->data_req = data_req;
+    s->opaque = opaque;
+}
+
+void wm8750_dac_dat(void *opaque, uint32_t sample)
+{
+    WM8750State *s = (WM8750State *) opaque;
+
+    *(uint32_t *) &s->data_out[s->idx_out] = sample;
+    s->req_out -= 4;
+    s->idx_out += 4;
+    if (s->idx_out >= sizeof(s->data_out) || s->req_out <= 0)
+        wm8750_out_flush(s);
+}
+
+void *wm8750_dac_buffer(void *opaque, int samples)
+{
+    WM8750State *s = (WM8750State *) opaque;
+    /* XXX: Should check if there are <i>samples</i> free samples available */
+    void *ret = s->data_out + s->idx_out;
+
+    s->idx_out += samples << 2;
+    s->req_out -= samples << 2;
+    return ret;
+}
+
+void wm8750_dac_commit(void *opaque)
+{
+    WM8750State *s = (WM8750State *) opaque;
+
+    wm8750_out_flush(s);
+}
+
+uint32_t wm8750_adc_dat(void *opaque)
+{
+    WM8750State *s = (WM8750State *) opaque;
+    uint32_t *data;
+
+    if (s->idx_in >= sizeof(s->data_in))
+        wm8750_in_load(s);
+
+    data = (uint32_t *) &s->data_in[s->idx_in];
+    s->req_in -= 4;
+    s->idx_in += 4;
+    return *data;
+}
+
+void wm8750_set_bclk_in(void *opaque, int new_hz)
+{
+    WM8750State *s = (WM8750State *) opaque;
+
+    s->ext_adc_hz = new_hz;
+    s->ext_dac_hz = new_hz;
+    wm8750_clk_update(s, 1);
+}
+
+static void wm8750_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
+
+    sc->init = wm8750_init;
+    sc->event = wm8750_event;
+    sc->recv = wm8750_rx;
+    sc->send = wm8750_tx;
+    dc->vmsd = &vmstate_wm8750;
+}
+
+static const TypeInfo wm8750_info = {
+    .name          = "wm8750",
+    .parent        = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(WM8750State),
+    .class_init    = wm8750_class_init,
+};
+
+static void wm8750_register_types(void)
+{
+    type_register_static(&wm8750_info);
+}
+
+type_init(wm8750_register_types)
diff --git a/hw/block-common.c b/hw/block-common.c
deleted file mode 100644 (file)
index 33dd3f3..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Common code for block device models
- *
- * Copyright (C) 2012 Red Hat, Inc.
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or
- * later.  See the COPYING file in the top-level directory.
- */
-
-#include "sysemu/blockdev.h"
-#include "hw/block/block.h"
-#include "qemu/error-report.h"
-
-void blkconf_serial(BlockConf *conf, char **serial)
-{
-    DriveInfo *dinfo;
-
-    if (!*serial) {
-        /* try to fall back to value set with legacy -drive serial=... */
-        dinfo = drive_get_by_blockdev(conf->bs);
-        *serial = g_strdup(dinfo->serial);
-    }
-}
-
-int blkconf_geometry(BlockConf *conf, int *ptrans,
-                     unsigned cyls_max, unsigned heads_max, unsigned secs_max)
-{
-    DriveInfo *dinfo;
-
-    if (!conf->cyls && !conf->heads && !conf->secs) {
-        /* try to fall back to value set with legacy -drive cyls=... */
-        dinfo = drive_get_by_blockdev(conf->bs);
-        conf->cyls  = dinfo->cyls;
-        conf->heads = dinfo->heads;
-        conf->secs  = dinfo->secs;
-        if (ptrans) {
-            *ptrans = dinfo->trans;
-        }
-    }
-    if (!conf->cyls && !conf->heads && !conf->secs) {
-        hd_geometry_guess(conf->bs,
-                          &conf->cyls, &conf->heads, &conf->secs,
-                          ptrans);
-    } else if (ptrans && *ptrans == BIOS_ATA_TRANSLATION_AUTO) {
-        *ptrans = hd_bios_chs_auto_trans(conf->cyls, conf->heads, conf->secs);
-    }
-    if (conf->cyls || conf->heads || conf->secs) {
-        if (conf->cyls < 1 || conf->cyls > cyls_max) {
-            error_report("cyls must be between 1 and %u", cyls_max);
-            return -1;
-        }
-        if (conf->heads < 1 || conf->heads > heads_max) {
-            error_report("heads must be between 1 and %u", heads_max);
-            return -1;
-        }
-        if (conf->secs < 1 || conf->secs > secs_max) {
-            error_report("secs must be between 1 and %u", secs_max);
-            return -1;
-        }
-    }
-    return 0;
-}
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5fa51013860dceff9c0b4afee15e5cd6d7749a70 100644 (file)
@@ -0,0 +1,8 @@
+common-obj-y += block.o cdrom.o hd-geometry.o
+common-obj-$(CONFIG_FDC) += fdc.o
+common-obj-$(CONFIG_SSI_M25P80) += m25p80.o
+common-obj-$(CONFIG_NAND) += nand.o
+common-obj-$(CONFIG_PFLASH_CFI01) += pflash_cfi01.o
+common-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o
+common-obj-$(CONFIG_XEN_BACKEND) += xen_disk.o
+common-obj-$(CONFIG_ECC) += ecc.o
diff --git a/hw/block/block.c b/hw/block/block.c
new file mode 100644 (file)
index 0000000..33dd3f3
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Common code for block device models
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * later.  See the COPYING file in the top-level directory.
+ */
+
+#include "sysemu/blockdev.h"
+#include "hw/block/block.h"
+#include "qemu/error-report.h"
+
+void blkconf_serial(BlockConf *conf, char **serial)
+{
+    DriveInfo *dinfo;
+
+    if (!*serial) {
+        /* try to fall back to value set with legacy -drive serial=... */
+        dinfo = drive_get_by_blockdev(conf->bs);
+        *serial = g_strdup(dinfo->serial);
+    }
+}
+
+int blkconf_geometry(BlockConf *conf, int *ptrans,
+                     unsigned cyls_max, unsigned heads_max, unsigned secs_max)
+{
+    DriveInfo *dinfo;
+
+    if (!conf->cyls && !conf->heads && !conf->secs) {
+        /* try to fall back to value set with legacy -drive cyls=... */
+        dinfo = drive_get_by_blockdev(conf->bs);
+        conf->cyls  = dinfo->cyls;
+        conf->heads = dinfo->heads;
+        conf->secs  = dinfo->secs;
+        if (ptrans) {
+            *ptrans = dinfo->trans;
+        }
+    }
+    if (!conf->cyls && !conf->heads && !conf->secs) {
+        hd_geometry_guess(conf->bs,
+                          &conf->cyls, &conf->heads, &conf->secs,
+                          ptrans);
+    } else if (ptrans && *ptrans == BIOS_ATA_TRANSLATION_AUTO) {
+        *ptrans = hd_bios_chs_auto_trans(conf->cyls, conf->heads, conf->secs);
+    }
+    if (conf->cyls || conf->heads || conf->secs) {
+        if (conf->cyls < 1 || conf->cyls > cyls_max) {
+            error_report("cyls must be between 1 and %u", cyls_max);
+            return -1;
+        }
+        if (conf->heads < 1 || conf->heads > heads_max) {
+            error_report("heads must be between 1 and %u", heads_max);
+            return -1;
+        }
+        if (conf->secs < 1 || conf->secs > secs_max) {
+            error_report("secs must be between 1 and %u", secs_max);
+            return -1;
+        }
+    }
+    return 0;
+}
diff --git a/hw/block/cdrom.c b/hw/block/cdrom.c
new file mode 100644 (file)
index 0000000..38469fa
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * QEMU ATAPI CD-ROM Emulator
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* ??? Most of the ATAPI emulation is still in ide.c.  It should be moved
+   here.  */
+
+#include "qemu-common.h"
+#include "hw/scsi/scsi.h"
+
+static void lba_to_msf(uint8_t *buf, int lba)
+{
+    lba += 150;
+    buf[0] = (lba / 75) / 60;
+    buf[1] = (lba / 75) % 60;
+    buf[2] = lba % 75;
+}
+
+/* same toc as bochs. Return -1 if error or the toc length */
+/* XXX: check this */
+int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track)
+{
+    uint8_t *q;
+    int len;
+
+    if (start_track > 1 && start_track != 0xaa)
+        return -1;
+    q = buf + 2;
+    *q++ = 1; /* first session */
+    *q++ = 1; /* last session */
+    if (start_track <= 1) {
+        *q++ = 0; /* reserved */
+        *q++ = 0x14; /* ADR, control */
+        *q++ = 1;    /* track number */
+        *q++ = 0; /* reserved */
+        if (msf) {
+            *q++ = 0; /* reserved */
+            lba_to_msf(q, 0);
+            q += 3;
+        } else {
+            /* sector 0 */
+            cpu_to_be32wu((uint32_t *)q, 0);
+            q += 4;
+        }
+    }
+    /* lead out track */
+    *q++ = 0; /* reserved */
+    *q++ = 0x16; /* ADR, control */
+    *q++ = 0xaa; /* track number */
+    *q++ = 0; /* reserved */
+    if (msf) {
+        *q++ = 0; /* reserved */
+        lba_to_msf(q, nb_sectors);
+        q += 3;
+    } else {
+        cpu_to_be32wu((uint32_t *)q, nb_sectors);
+        q += 4;
+    }
+    len = q - buf;
+    cpu_to_be16wu((uint16_t *)buf, len - 2);
+    return len;
+}
+
+/* mostly same info as PearPc */
+int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num)
+{
+    uint8_t *q;
+    int len;
+
+    q = buf + 2;
+    *q++ = 1; /* first session */
+    *q++ = 1; /* last session */
+
+    *q++ = 1; /* session number */
+    *q++ = 0x14; /* data track */
+    *q++ = 0; /* track number */
+    *q++ = 0xa0; /* lead-in */
+    *q++ = 0; /* min */
+    *q++ = 0; /* sec */
+    *q++ = 0; /* frame */
+    *q++ = 0;
+    *q++ = 1; /* first track */
+    *q++ = 0x00; /* disk type */
+    *q++ = 0x00;
+
+    *q++ = 1; /* session number */
+    *q++ = 0x14; /* data track */
+    *q++ = 0; /* track number */
+    *q++ = 0xa1;
+    *q++ = 0; /* min */
+    *q++ = 0; /* sec */
+    *q++ = 0; /* frame */
+    *q++ = 0;
+    *q++ = 1; /* last track */
+    *q++ = 0x00;
+    *q++ = 0x00;
+
+    *q++ = 1; /* session number */
+    *q++ = 0x14; /* data track */
+    *q++ = 0; /* track number */
+    *q++ = 0xa2; /* lead-out */
+    *q++ = 0; /* min */
+    *q++ = 0; /* sec */
+    *q++ = 0; /* frame */
+    if (msf) {
+        *q++ = 0; /* reserved */
+        lba_to_msf(q, nb_sectors);
+        q += 3;
+    } else {
+        cpu_to_be32wu((uint32_t *)q, nb_sectors);
+        q += 4;
+    }
+
+    *q++ = 1; /* session number */
+    *q++ = 0x14; /* ADR, control */
+    *q++ = 0;    /* track number */
+    *q++ = 1;    /* point */
+    *q++ = 0; /* min */
+    *q++ = 0; /* sec */
+    *q++ = 0; /* frame */
+    if (msf) {
+        *q++ = 0;
+        lba_to_msf(q, 0);
+        q += 3;
+    } else {
+        *q++ = 0;
+        *q++ = 0;
+        *q++ = 0;
+        *q++ = 0;
+    }
+
+    len = q - buf;
+    cpu_to_be16wu((uint16_t *)buf, len - 2);
+    return len;
+}
diff --git a/hw/block/ecc.c b/hw/block/ecc.c
new file mode 100644 (file)
index 0000000..8c888cc
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Calculate Error-correcting Codes. Used by NAND Flash controllers
+ * (not by NAND chips).
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GNU GPL v2.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "hw/hw.h"
+#include "hw/block/flash.h"
+
+/*
+ * Pre-calculated 256-way 1 byte column parity.  Table borrowed from Linux.
+ */
+static const uint8_t nand_ecc_precalc_table[] = {
+    0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
+    0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
+    0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
+    0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+    0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
+    0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+    0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
+    0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+    0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
+    0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+    0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
+    0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+    0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
+    0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+    0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
+    0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+    0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
+    0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+    0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
+    0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+    0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
+    0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+    0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
+    0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+    0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
+    0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+    0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
+    0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+    0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
+    0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+    0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
+    0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
+};
+
+/* Update ECC parity count.  */
+uint8_t ecc_digest(ECCState *s, uint8_t sample)
+{
+    uint8_t idx = nand_ecc_precalc_table[sample];
+
+    s->cp ^= idx & 0x3f;
+    if (idx & 0x40) {
+        s->lp[0] ^= ~s->count;
+        s->lp[1] ^= s->count;
+    }
+    s->count ++;
+
+    return sample;
+}
+
+/* Reinitialise the counters.  */
+void ecc_reset(ECCState *s)
+{
+    s->lp[0] = 0x0000;
+    s->lp[1] = 0x0000;
+    s->cp = 0x00;
+    s->count = 0;
+}
+
+/* Save/restore */
+VMStateDescription vmstate_ecc_state = {
+    .name = "ecc-state",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields = (VMStateField []) {
+        VMSTATE_UINT8(cp, ECCState),
+        VMSTATE_UINT16_ARRAY(lp, ECCState, 2),
+        VMSTATE_UINT16(count, ECCState),
+        VMSTATE_END_OF_LIST(),
+    },
+};
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
new file mode 100644 (file)
index 0000000..1ed874f
--- /dev/null
@@ -0,0 +1,2284 @@
+/*
+ * QEMU Floppy disk emulator (Intel 82078)
+ *
+ * Copyright (c) 2003, 2007 Jocelyn Mayer
+ * Copyright (c) 2008 Hervé Poussineau
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/*
+ * The controller is used in Sun4m systems in a slightly different
+ * way. There are changes in DOR register and DMA is not available.
+ */
+
+#include "hw/hw.h"
+#include "hw/block/fdc.h"
+#include "qemu/error-report.h"
+#include "qemu/timer.h"
+#include "hw/isa/isa.h"
+#include "hw/sysbus.h"
+#include "hw/qdev-addr.h"
+#include "sysemu/blockdev.h"
+#include "sysemu/sysemu.h"
+#include "qemu/log.h"
+
+/********************************************************/
+/* debug Floppy devices */
+//#define DEBUG_FLOPPY
+
+#ifdef DEBUG_FLOPPY
+#define FLOPPY_DPRINTF(fmt, ...)                                \
+    do { printf("FLOPPY: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define FLOPPY_DPRINTF(fmt, ...)
+#endif
+
+/********************************************************/
+/* Floppy drive emulation                               */
+
+typedef enum FDriveRate {
+    FDRIVE_RATE_500K = 0x00,  /* 500 Kbps */
+    FDRIVE_RATE_300K = 0x01,  /* 300 Kbps */
+    FDRIVE_RATE_250K = 0x02,  /* 250 Kbps */
+    FDRIVE_RATE_1M   = 0x03,  /*   1 Mbps */
+} FDriveRate;
+
+typedef struct FDFormat {
+    FDriveType drive;
+    uint8_t last_sect;
+    uint8_t max_track;
+    uint8_t max_head;
+    FDriveRate rate;
+} FDFormat;
+
+static const FDFormat fd_formats[] = {
+    /* First entry is default format */
+    /* 1.44 MB 3"1/2 floppy disks */
+    { FDRIVE_DRV_144, 18, 80, 1, FDRIVE_RATE_500K, },
+    { FDRIVE_DRV_144, 20, 80, 1, FDRIVE_RATE_500K, },
+    { FDRIVE_DRV_144, 21, 80, 1, FDRIVE_RATE_500K, },
+    { FDRIVE_DRV_144, 21, 82, 1, FDRIVE_RATE_500K, },
+    { FDRIVE_DRV_144, 21, 83, 1, FDRIVE_RATE_500K, },
+    { FDRIVE_DRV_144, 22, 80, 1, FDRIVE_RATE_500K, },
+    { FDRIVE_DRV_144, 23, 80, 1, FDRIVE_RATE_500K, },
+    { FDRIVE_DRV_144, 24, 80, 1, FDRIVE_RATE_500K, },
+    /* 2.88 MB 3"1/2 floppy disks */
+    { FDRIVE_DRV_288, 36, 80, 1, FDRIVE_RATE_1M, },
+    { FDRIVE_DRV_288, 39, 80, 1, FDRIVE_RATE_1M, },
+    { FDRIVE_DRV_288, 40, 80, 1, FDRIVE_RATE_1M, },
+    { FDRIVE_DRV_288, 44, 80, 1, FDRIVE_RATE_1M, },
+    { FDRIVE_DRV_288, 48, 80, 1, FDRIVE_RATE_1M, },
+    /* 720 kB 3"1/2 floppy disks */
+    { FDRIVE_DRV_144,  9, 80, 1, FDRIVE_RATE_250K, },
+    { FDRIVE_DRV_144, 10, 80, 1, FDRIVE_RATE_250K, },
+    { FDRIVE_DRV_144, 10, 82, 1, FDRIVE_RATE_250K, },
+    { FDRIVE_DRV_144, 10, 83, 1, FDRIVE_RATE_250K, },
+    { FDRIVE_DRV_144, 13, 80, 1, FDRIVE_RATE_250K, },
+    { FDRIVE_DRV_144, 14, 80, 1, FDRIVE_RATE_250K, },
+    /* 1.2 MB 5"1/4 floppy disks */
+    { FDRIVE_DRV_120, 15, 80, 1, FDRIVE_RATE_500K, },
+    { FDRIVE_DRV_120, 18, 80, 1, FDRIVE_RATE_500K, },
+    { FDRIVE_DRV_120, 18, 82, 1, FDRIVE_RATE_500K, },
+    { FDRIVE_DRV_120, 18, 83, 1, FDRIVE_RATE_500K, },
+    { FDRIVE_DRV_120, 20, 80, 1, FDRIVE_RATE_500K, },
+    /* 720 kB 5"1/4 floppy disks */
+    { FDRIVE_DRV_120,  9, 80, 1, FDRIVE_RATE_250K, },
+    { FDRIVE_DRV_120, 11, 80, 1, FDRIVE_RATE_250K, },
+    /* 360 kB 5"1/4 floppy disks */
+    { FDRIVE_DRV_120,  9, 40, 1, FDRIVE_RATE_300K, },
+    { FDRIVE_DRV_120,  9, 40, 0, FDRIVE_RATE_300K, },
+    { FDRIVE_DRV_120, 10, 41, 1, FDRIVE_RATE_300K, },
+    { FDRIVE_DRV_120, 10, 42, 1, FDRIVE_RATE_300K, },
+    /* 320 kB 5"1/4 floppy disks */
+    { FDRIVE_DRV_120,  8, 40, 1, FDRIVE_RATE_250K, },
+    { FDRIVE_DRV_120,  8, 40, 0, FDRIVE_RATE_250K, },
+    /* 360 kB must match 5"1/4 better than 3"1/2... */
+    { FDRIVE_DRV_144,  9, 80, 0, FDRIVE_RATE_250K, },
+    /* end */
+    { FDRIVE_DRV_NONE, -1, -1, 0, 0, },
+};
+
+static void pick_geometry(BlockDriverState *bs, int *nb_heads,
+                          int *max_track, int *last_sect,
+                          FDriveType drive_in, FDriveType *drive,
+                          FDriveRate *rate)
+{
+    const FDFormat *parse;
+    uint64_t nb_sectors, size;
+    int i, first_match, match;
+
+    bdrv_get_geometry(bs, &nb_sectors);
+    match = -1;
+    first_match = -1;
+    for (i = 0; ; i++) {
+        parse = &fd_formats[i];
+        if (parse->drive == FDRIVE_DRV_NONE) {
+            break;
+        }
+        if (drive_in == parse->drive ||
+            drive_in == FDRIVE_DRV_NONE) {
+            size = (parse->max_head + 1) * parse->max_track *
+                parse->last_sect;
+            if (nb_sectors == size) {
+                match = i;
+                break;
+            }
+            if (first_match == -1) {
+                first_match = i;
+            }
+        }
+    }
+    if (match == -1) {
+        if (first_match == -1) {
+            match = 1;
+        } else {
+            match = first_match;
+        }
+        parse = &fd_formats[match];
+    }
+    *nb_heads = parse->max_head + 1;
+    *max_track = parse->max_track;
+    *last_sect = parse->last_sect;
+    *drive = parse->drive;
+    *rate = parse->rate;
+}
+
+#define GET_CUR_DRV(fdctrl) ((fdctrl)->cur_drv)
+#define SET_CUR_DRV(fdctrl, drive) ((fdctrl)->cur_drv = (drive))
+
+/* Will always be a fixed parameter for us */
+#define FD_SECTOR_LEN          512
+#define FD_SECTOR_SC           2   /* Sector size code */
+#define FD_RESET_SENSEI_COUNT  4   /* Number of sense interrupts on RESET */
+
+typedef struct FDCtrl FDCtrl;
+
+/* Floppy disk drive emulation */
+typedef enum FDiskFlags {
+    FDISK_DBL_SIDES  = 0x01,
+} FDiskFlags;
+
+typedef struct FDrive {
+    FDCtrl *fdctrl;
+    BlockDriverState *bs;
+    /* Drive status */
+    FDriveType drive;
+    uint8_t perpendicular;    /* 2.88 MB access mode    */
+    /* Position */
+    uint8_t head;
+    uint8_t track;
+    uint8_t sect;
+    /* Media */
+    FDiskFlags flags;
+    uint8_t last_sect;        /* Nb sector per track    */
+    uint8_t max_track;        /* Nb of tracks           */
+    uint16_t bps;             /* Bytes per sector       */
+    uint8_t ro;               /* Is read-only           */
+    uint8_t media_changed;    /* Is media changed       */
+    uint8_t media_rate;       /* Data rate of medium    */
+} FDrive;
+
+static void fd_init(FDrive *drv)
+{
+    /* Drive */
+    drv->drive = FDRIVE_DRV_NONE;
+    drv->perpendicular = 0;
+    /* Disk */
+    drv->last_sect = 0;
+    drv->max_track = 0;
+}
+
+#define NUM_SIDES(drv) ((drv)->flags & FDISK_DBL_SIDES ? 2 : 1)
+
+static int fd_sector_calc(uint8_t head, uint8_t track, uint8_t sect,
+                          uint8_t last_sect, uint8_t num_sides)
+{
+    return (((track * num_sides) + head) * last_sect) + sect - 1;
+}
+
+/* Returns current position, in sectors, for given drive */
+static int fd_sector(FDrive *drv)
+{
+    return fd_sector_calc(drv->head, drv->track, drv->sect, drv->last_sect,
+                          NUM_SIDES(drv));
+}
+
+/* Seek to a new position:
+ * returns 0 if already on right track
+ * returns 1 if track changed
+ * returns 2 if track is invalid
+ * returns 3 if sector is invalid
+ * returns 4 if seek is disabled
+ */
+static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect,
+                   int enable_seek)
+{
+    uint32_t sector;
+    int ret;
+
+    if (track > drv->max_track ||
+        (head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) {
+        FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n",
+                       head, track, sect, 1,
+                       (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1,
+                       drv->max_track, drv->last_sect);
+        return 2;
+    }
+    if (sect > drv->last_sect) {
+        FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n",
+                       head, track, sect, 1,
+                       (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1,
+                       drv->max_track, drv->last_sect);
+        return 3;
+    }
+    sector = fd_sector_calc(head, track, sect, drv->last_sect, NUM_SIDES(drv));
+    ret = 0;
+    if (sector != fd_sector(drv)) {
+#if 0
+        if (!enable_seek) {
+            FLOPPY_DPRINTF("error: no implicit seek %d %02x %02x"
+                           " (max=%d %02x %02x)\n",
+                           head, track, sect, 1, drv->max_track,
+                           drv->last_sect);
+            return 4;
+        }
+#endif
+        drv->head = head;
+        if (drv->track != track) {
+            if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
+                drv->media_changed = 0;
+            }
+            ret = 1;
+        }
+        drv->track = track;
+        drv->sect = sect;
+    }
+
+    if (drv->bs == NULL || !bdrv_is_inserted(drv->bs)) {
+        ret = 2;
+    }
+
+    return ret;
+}
+
+/* Set drive back to track 0 */
+static void fd_recalibrate(FDrive *drv)
+{
+    FLOPPY_DPRINTF("recalibrate\n");
+    fd_seek(drv, 0, 0, 1, 1);
+}
+
+/* Revalidate a disk drive after a disk change */
+static void fd_revalidate(FDrive *drv)
+{
+    int nb_heads, max_track, last_sect, ro;
+    FDriveType drive;
+    FDriveRate rate;
+
+    FLOPPY_DPRINTF("revalidate\n");
+    if (drv->bs != NULL) {
+        ro = bdrv_is_read_only(drv->bs);
+        pick_geometry(drv->bs, &nb_heads, &max_track,
+                      &last_sect, drv->drive, &drive, &rate);
+        if (!bdrv_is_inserted(drv->bs)) {
+            FLOPPY_DPRINTF("No disk in drive\n");
+        } else {
+            FLOPPY_DPRINTF("Floppy disk (%d h %d t %d s) %s\n", nb_heads,
+                           max_track, last_sect, ro ? "ro" : "rw");
+        }
+        if (nb_heads == 1) {
+            drv->flags &= ~FDISK_DBL_SIDES;
+        } else {
+            drv->flags |= FDISK_DBL_SIDES;
+        }
+        drv->max_track = max_track;
+        drv->last_sect = last_sect;
+        drv->ro = ro;
+        drv->drive = drive;
+        drv->media_rate = rate;
+    } else {
+        FLOPPY_DPRINTF("No drive connected\n");
+        drv->last_sect = 0;
+        drv->max_track = 0;
+        drv->flags &= ~FDISK_DBL_SIDES;
+    }
+}
+
+/********************************************************/
+/* Intel 82078 floppy disk controller emulation          */
+
+static void fdctrl_reset(FDCtrl *fdctrl, int do_irq);
+static void fdctrl_reset_fifo(FDCtrl *fdctrl);
+static int fdctrl_transfer_handler (void *opaque, int nchan,
+                                    int dma_pos, int dma_len);
+static void fdctrl_raise_irq(FDCtrl *fdctrl);
+static FDrive *get_cur_drv(FDCtrl *fdctrl);
+
+static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl);
+static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl);
+static uint32_t fdctrl_read_dor(FDCtrl *fdctrl);
+static void fdctrl_write_dor(FDCtrl *fdctrl, uint32_t value);
+static uint32_t fdctrl_read_tape(FDCtrl *fdctrl);
+static void fdctrl_write_tape(FDCtrl *fdctrl, uint32_t value);
+static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl);
+static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value);
+static uint32_t fdctrl_read_data(FDCtrl *fdctrl);
+static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value);
+static uint32_t fdctrl_read_dir(FDCtrl *fdctrl);
+static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value);
+
+enum {
+    FD_DIR_WRITE   = 0,
+    FD_DIR_READ    = 1,
+    FD_DIR_SCANE   = 2,
+    FD_DIR_SCANL   = 3,
+    FD_DIR_SCANH   = 4,
+    FD_DIR_VERIFY  = 5,
+};
+
+enum {
+    FD_STATE_MULTI  = 0x01,    /* multi track flag */
+    FD_STATE_FORMAT = 0x02,    /* format flag */
+};
+
+enum {
+    FD_REG_SRA = 0x00,
+    FD_REG_SRB = 0x01,
+    FD_REG_DOR = 0x02,
+    FD_REG_TDR = 0x03,
+    FD_REG_MSR = 0x04,
+    FD_REG_DSR = 0x04,
+    FD_REG_FIFO = 0x05,
+    FD_REG_DIR = 0x07,
+    FD_REG_CCR = 0x07,
+};
+
+enum {
+    FD_CMD_READ_TRACK = 0x02,
+    FD_CMD_SPECIFY = 0x03,
+    FD_CMD_SENSE_DRIVE_STATUS = 0x04,
+    FD_CMD_WRITE = 0x05,
+    FD_CMD_READ = 0x06,
+    FD_CMD_RECALIBRATE = 0x07,
+    FD_CMD_SENSE_INTERRUPT_STATUS = 0x08,
+    FD_CMD_WRITE_DELETED = 0x09,
+    FD_CMD_READ_ID = 0x0a,
+    FD_CMD_READ_DELETED = 0x0c,
+    FD_CMD_FORMAT_TRACK = 0x0d,
+    FD_CMD_DUMPREG = 0x0e,
+    FD_CMD_SEEK = 0x0f,
+    FD_CMD_VERSION = 0x10,
+    FD_CMD_SCAN_EQUAL = 0x11,
+    FD_CMD_PERPENDICULAR_MODE = 0x12,
+    FD_CMD_CONFIGURE = 0x13,
+    FD_CMD_LOCK = 0x14,
+    FD_CMD_VERIFY = 0x16,
+    FD_CMD_POWERDOWN_MODE = 0x17,
+    FD_CMD_PART_ID = 0x18,
+    FD_CMD_SCAN_LOW_OR_EQUAL = 0x19,
+    FD_CMD_SCAN_HIGH_OR_EQUAL = 0x1d,
+    FD_CMD_SAVE = 0x2e,
+    FD_CMD_OPTION = 0x33,
+    FD_CMD_RESTORE = 0x4e,
+    FD_CMD_DRIVE_SPECIFICATION_COMMAND = 0x8e,
+    FD_CMD_RELATIVE_SEEK_OUT = 0x8f,
+    FD_CMD_FORMAT_AND_WRITE = 0xcd,
+    FD_CMD_RELATIVE_SEEK_IN = 0xcf,
+};
+
+enum {
+    FD_CONFIG_PRETRK = 0xff, /* Pre-compensation set to track 0 */
+    FD_CONFIG_FIFOTHR = 0x0f, /* FIFO threshold set to 1 byte */
+    FD_CONFIG_POLL  = 0x10, /* Poll enabled */
+    FD_CONFIG_EFIFO = 0x20, /* FIFO disabled */
+    FD_CONFIG_EIS   = 0x40, /* No implied seeks */
+};
+
+enum {
+    FD_SR0_DS0      = 0x01,
+    FD_SR0_DS1      = 0x02,
+    FD_SR0_HEAD     = 0x04,
+    FD_SR0_EQPMT    = 0x10,
+    FD_SR0_SEEK     = 0x20,
+    FD_SR0_ABNTERM  = 0x40,
+    FD_SR0_INVCMD   = 0x80,
+    FD_SR0_RDYCHG   = 0xc0,
+};
+
+enum {
+    FD_SR1_MA       = 0x01, /* Missing address mark */
+    FD_SR1_NW       = 0x02, /* Not writable */
+    FD_SR1_EC       = 0x80, /* End of cylinder */
+};
+
+enum {
+    FD_SR2_SNS      = 0x04, /* Scan not satisfied */
+    FD_SR2_SEH      = 0x08, /* Scan equal hit */
+};
+
+enum {
+    FD_SRA_DIR      = 0x01,
+    FD_SRA_nWP      = 0x02,
+    FD_SRA_nINDX    = 0x04,
+    FD_SRA_HDSEL    = 0x08,
+    FD_SRA_nTRK0    = 0x10,
+    FD_SRA_STEP     = 0x20,
+    FD_SRA_nDRV2    = 0x40,
+    FD_SRA_INTPEND  = 0x80,
+};
+
+enum {
+    FD_SRB_MTR0     = 0x01,
+    FD_SRB_MTR1     = 0x02,
+    FD_SRB_WGATE    = 0x04,
+    FD_SRB_RDATA    = 0x08,
+    FD_SRB_WDATA    = 0x10,
+    FD_SRB_DR0      = 0x20,
+};
+
+enum {
+#if MAX_FD == 4
+    FD_DOR_SELMASK  = 0x03,
+#else
+    FD_DOR_SELMASK  = 0x01,
+#endif
+    FD_DOR_nRESET   = 0x04,
+    FD_DOR_DMAEN    = 0x08,
+    FD_DOR_MOTEN0   = 0x10,
+    FD_DOR_MOTEN1   = 0x20,
+    FD_DOR_MOTEN2   = 0x40,
+    FD_DOR_MOTEN3   = 0x80,
+};
+
+enum {
+#if MAX_FD == 4
+    FD_TDR_BOOTSEL  = 0x0c,
+#else
+    FD_TDR_BOOTSEL  = 0x04,
+#endif
+};
+
+enum {
+    FD_DSR_DRATEMASK= 0x03,
+    FD_DSR_PWRDOWN  = 0x40,
+    FD_DSR_SWRESET  = 0x80,
+};
+
+enum {
+    FD_MSR_DRV0BUSY = 0x01,
+    FD_MSR_DRV1BUSY = 0x02,
+    FD_MSR_DRV2BUSY = 0x04,
+    FD_MSR_DRV3BUSY = 0x08,
+    FD_MSR_CMDBUSY  = 0x10,
+    FD_MSR_NONDMA   = 0x20,
+    FD_MSR_DIO      = 0x40,
+    FD_MSR_RQM      = 0x80,
+};
+
+enum {
+    FD_DIR_DSKCHG   = 0x80,
+};
+
+#define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI)
+#define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT)
+
+struct FDCtrl {
+    MemoryRegion iomem;
+    qemu_irq irq;
+    /* Controller state */
+    QEMUTimer *result_timer;
+    int dma_chann;
+    /* Controller's identification */
+    uint8_t version;
+    /* HW */
+    uint8_t sra;
+    uint8_t srb;
+    uint8_t dor;
+    uint8_t dor_vmstate; /* only used as temp during vmstate */
+    uint8_t tdr;
+    uint8_t dsr;
+    uint8_t msr;
+    uint8_t cur_drv;
+    uint8_t status0;
+    uint8_t status1;
+    uint8_t status2;
+    /* Command FIFO */
+    uint8_t *fifo;
+    int32_t fifo_size;
+    uint32_t data_pos;
+    uint32_t data_len;
+    uint8_t data_state;
+    uint8_t data_dir;
+    uint8_t eot; /* last wanted sector */
+    /* States kept only to be returned back */
+    /* precompensation */
+    uint8_t precomp_trk;
+    uint8_t config;
+    uint8_t lock;
+    /* Power down config (also with status regB access mode */
+    uint8_t pwrd;
+    /* Floppy drives */
+    uint8_t num_floppies;
+    /* Sun4m quirks? */
+    int sun4m;
+    FDrive drives[MAX_FD];
+    int reset_sensei;
+    uint32_t check_media_rate;
+    /* Timers state */
+    uint8_t timer0;
+    uint8_t timer1;
+};
+
+typedef struct FDCtrlSysBus {
+    SysBusDevice busdev;
+    struct FDCtrl state;
+} FDCtrlSysBus;
+
+typedef struct FDCtrlISABus {
+    ISADevice busdev;
+    uint32_t iobase;
+    uint32_t irq;
+    uint32_t dma;
+    struct FDCtrl state;
+    int32_t bootindexA;
+    int32_t bootindexB;
+} FDCtrlISABus;
+
+static uint32_t fdctrl_read (void *opaque, uint32_t reg)
+{
+    FDCtrl *fdctrl = opaque;
+    uint32_t retval;
+
+    reg &= 7;
+    switch (reg) {
+    case FD_REG_SRA:
+        retval = fdctrl_read_statusA(fdctrl);
+        break;
+    case FD_REG_SRB:
+        retval = fdctrl_read_statusB(fdctrl);
+        break;
+    case FD_REG_DOR:
+        retval = fdctrl_read_dor(fdctrl);
+        break;
+    case FD_REG_TDR:
+        retval = fdctrl_read_tape(fdctrl);
+        break;
+    case FD_REG_MSR:
+        retval = fdctrl_read_main_status(fdctrl);
+        break;
+    case FD_REG_FIFO:
+        retval = fdctrl_read_data(fdctrl);
+        break;
+    case FD_REG_DIR:
+        retval = fdctrl_read_dir(fdctrl);
+        break;
+    default:
+        retval = (uint32_t)(-1);
+        break;
+    }
+    FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval);
+
+    return retval;
+}
+
+static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
+{
+    FDCtrl *fdctrl = opaque;
+
+    FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value);
+
+    reg &= 7;
+    switch (reg) {
+    case FD_REG_DOR:
+        fdctrl_write_dor(fdctrl, value);
+        break;
+    case FD_REG_TDR:
+        fdctrl_write_tape(fdctrl, value);
+        break;
+    case FD_REG_DSR:
+        fdctrl_write_rate(fdctrl, value);
+        break;
+    case FD_REG_FIFO:
+        fdctrl_write_data(fdctrl, value);
+        break;
+    case FD_REG_CCR:
+        fdctrl_write_ccr(fdctrl, value);
+        break;
+    default:
+        break;
+    }
+}
+
+static uint64_t fdctrl_read_mem (void *opaque, hwaddr reg,
+                                 unsigned ize)
+{
+    return fdctrl_read(opaque, (uint32_t)reg);
+}
+
+static void fdctrl_write_mem (void *opaque, hwaddr reg,
+                              uint64_t value, unsigned size)
+{
+    fdctrl_write(opaque, (uint32_t)reg, value);
+}
+
+static const MemoryRegionOps fdctrl_mem_ops = {
+    .read = fdctrl_read_mem,
+    .write = fdctrl_write_mem,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const MemoryRegionOps fdctrl_mem_strict_ops = {
+    .read = fdctrl_read_mem,
+    .write = fdctrl_write_mem,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static bool fdrive_media_changed_needed(void *opaque)
+{
+    FDrive *drive = opaque;
+
+    return (drive->bs != NULL && drive->media_changed != 1);
+}
+
+static const VMStateDescription vmstate_fdrive_media_changed = {
+    .name = "fdrive/media_changed",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(media_changed, FDrive),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static bool fdrive_media_rate_needed(void *opaque)
+{
+    FDrive *drive = opaque;
+
+    return drive->fdctrl->check_media_rate;
+}
+
+static const VMStateDescription vmstate_fdrive_media_rate = {
+    .name = "fdrive/media_rate",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(media_rate, FDrive),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_fdrive = {
+    .name = "fdrive",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(head, FDrive),
+        VMSTATE_UINT8(track, FDrive),
+        VMSTATE_UINT8(sect, FDrive),
+        VMSTATE_END_OF_LIST()
+    },
+    .subsections = (VMStateSubsection[]) {
+        {
+            .vmsd = &vmstate_fdrive_media_changed,
+            .needed = &fdrive_media_changed_needed,
+        } , {
+            .vmsd = &vmstate_fdrive_media_rate,
+            .needed = &fdrive_media_rate_needed,
+        } , {
+            /* empty */
+        }
+    }
+};
+
+static void fdc_pre_save(void *opaque)
+{
+    FDCtrl *s = opaque;
+
+    s->dor_vmstate = s->dor | GET_CUR_DRV(s);
+}
+
+static int fdc_post_load(void *opaque, int version_id)
+{
+    FDCtrl *s = opaque;
+
+    SET_CUR_DRV(s, s->dor_vmstate & FD_DOR_SELMASK);
+    s->dor = s->dor_vmstate & ~FD_DOR_SELMASK;
+    return 0;
+}
+
+static const VMStateDescription vmstate_fdc = {
+    .name = "fdc",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .pre_save = fdc_pre_save,
+    .post_load = fdc_post_load,
+    .fields      = (VMStateField []) {
+        /* Controller State */
+        VMSTATE_UINT8(sra, FDCtrl),
+        VMSTATE_UINT8(srb, FDCtrl),
+        VMSTATE_UINT8(dor_vmstate, FDCtrl),
+        VMSTATE_UINT8(tdr, FDCtrl),
+        VMSTATE_UINT8(dsr, FDCtrl),
+        VMSTATE_UINT8(msr, FDCtrl),
+        VMSTATE_UINT8(status0, FDCtrl),
+        VMSTATE_UINT8(status1, FDCtrl),
+        VMSTATE_UINT8(status2, FDCtrl),
+        /* Command FIFO */
+        VMSTATE_VARRAY_INT32(fifo, FDCtrl, fifo_size, 0, vmstate_info_uint8,
+                             uint8_t),
+        VMSTATE_UINT32(data_pos, FDCtrl),
+        VMSTATE_UINT32(data_len, FDCtrl),
+        VMSTATE_UINT8(data_state, FDCtrl),
+        VMSTATE_UINT8(data_dir, FDCtrl),
+        VMSTATE_UINT8(eot, FDCtrl),
+        /* States kept only to be returned back */
+        VMSTATE_UINT8(timer0, FDCtrl),
+        VMSTATE_UINT8(timer1, FDCtrl),
+        VMSTATE_UINT8(precomp_trk, FDCtrl),
+        VMSTATE_UINT8(config, FDCtrl),
+        VMSTATE_UINT8(lock, FDCtrl),
+        VMSTATE_UINT8(pwrd, FDCtrl),
+        VMSTATE_UINT8_EQUAL(num_floppies, FDCtrl),
+        VMSTATE_STRUCT_ARRAY(drives, FDCtrl, MAX_FD, 1,
+                             vmstate_fdrive, FDrive),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void fdctrl_external_reset_sysbus(DeviceState *d)
+{
+    FDCtrlSysBus *sys = container_of(d, FDCtrlSysBus, busdev.qdev);
+    FDCtrl *s = &sys->state;
+
+    fdctrl_reset(s, 0);
+}
+
+static void fdctrl_external_reset_isa(DeviceState *d)
+{
+    FDCtrlISABus *isa = container_of(d, FDCtrlISABus, busdev.qdev);
+    FDCtrl *s = &isa->state;
+
+    fdctrl_reset(s, 0);
+}
+
+static void fdctrl_handle_tc(void *opaque, int irq, int level)
+{
+    //FDCtrl *s = opaque;
+
+    if (level) {
+        // XXX
+        FLOPPY_DPRINTF("TC pulsed\n");
+    }
+}
+
+/* Change IRQ state */
+static void fdctrl_reset_irq(FDCtrl *fdctrl)
+{
+    fdctrl->status0 = 0;
+    if (!(fdctrl->sra & FD_SRA_INTPEND))
+        return;
+    FLOPPY_DPRINTF("Reset interrupt\n");
+    qemu_set_irq(fdctrl->irq, 0);
+    fdctrl->sra &= ~FD_SRA_INTPEND;
+}
+
+static void fdctrl_raise_irq(FDCtrl *fdctrl)
+{
+    /* Sparc mutation */
+    if (fdctrl->sun4m && (fdctrl->msr & FD_MSR_CMDBUSY)) {
+        /* XXX: not sure */
+        fdctrl->msr &= ~FD_MSR_CMDBUSY;
+        fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
+        return;
+    }
+    if (!(fdctrl->sra & FD_SRA_INTPEND)) {
+        qemu_set_irq(fdctrl->irq, 1);
+        fdctrl->sra |= FD_SRA_INTPEND;
+    }
+
+    fdctrl->reset_sensei = 0;
+    FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", fdctrl->status0);
+}
+
+/* Reset controller */
+static void fdctrl_reset(FDCtrl *fdctrl, int do_irq)
+{
+    int i;
+
+    FLOPPY_DPRINTF("reset controller\n");
+    fdctrl_reset_irq(fdctrl);
+    /* Initialise controller */
+    fdctrl->sra = 0;
+    fdctrl->srb = 0xc0;
+    if (!fdctrl->drives[1].bs)
+        fdctrl->sra |= FD_SRA_nDRV2;
+    fdctrl->cur_drv = 0;
+    fdctrl->dor = FD_DOR_nRESET;
+    fdctrl->dor |= (fdctrl->dma_chann != -1) ? FD_DOR_DMAEN : 0;
+    fdctrl->msr = FD_MSR_RQM;
+    /* FIFO state */
+    fdctrl->data_pos = 0;
+    fdctrl->data_len = 0;
+    fdctrl->data_state = 0;
+    fdctrl->data_dir = FD_DIR_WRITE;
+    for (i = 0; i < MAX_FD; i++)
+        fd_recalibrate(&fdctrl->drives[i]);
+    fdctrl_reset_fifo(fdctrl);
+    if (do_irq) {
+        fdctrl->status0 |= FD_SR0_RDYCHG;
+        fdctrl_raise_irq(fdctrl);
+        fdctrl->reset_sensei = FD_RESET_SENSEI_COUNT;
+    }
+}
+
+static inline FDrive *drv0(FDCtrl *fdctrl)
+{
+    return &fdctrl->drives[(fdctrl->tdr & FD_TDR_BOOTSEL) >> 2];
+}
+
+static inline FDrive *drv1(FDCtrl *fdctrl)
+{
+    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (1 << 2))
+        return &fdctrl->drives[1];
+    else
+        return &fdctrl->drives[0];
+}
+
+#if MAX_FD == 4
+static inline FDrive *drv2(FDCtrl *fdctrl)
+{
+    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (2 << 2))
+        return &fdctrl->drives[2];
+    else
+        return &fdctrl->drives[1];
+}
+
+static inline FDrive *drv3(FDCtrl *fdctrl)
+{
+    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (3 << 2))
+        return &fdctrl->drives[3];
+    else
+        return &fdctrl->drives[2];
+}
+#endif
+
+static FDrive *get_cur_drv(FDCtrl *fdctrl)
+{
+    switch (fdctrl->cur_drv) {
+        case 0: return drv0(fdctrl);
+        case 1: return drv1(fdctrl);
+#if MAX_FD == 4
+        case 2: return drv2(fdctrl);
+        case 3: return drv3(fdctrl);
+#endif
+        default: return NULL;
+    }
+}
+
+/* Status A register : 0x00 (read-only) */
+static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl)
+{
+    uint32_t retval = fdctrl->sra;
+
+    FLOPPY_DPRINTF("status register A: 0x%02x\n", retval);
+
+    return retval;
+}
+
+/* Status B register : 0x01 (read-only) */
+static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl)
+{
+    uint32_t retval = fdctrl->srb;
+
+    FLOPPY_DPRINTF("status register B: 0x%02x\n", retval);
+
+    return retval;
+}
+
+/* Digital output register : 0x02 */
+static uint32_t fdctrl_read_dor(FDCtrl *fdctrl)
+{
+    uint32_t retval = fdctrl->dor;
+
+    /* Selected drive */
+    retval |= fdctrl->cur_drv;
+    FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval);
+
+    return retval;
+}
+
+static void fdctrl_write_dor(FDCtrl *fdctrl, uint32_t value)
+{
+    FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value);
+
+    /* Motors */
+    if (value & FD_DOR_MOTEN0)
+        fdctrl->srb |= FD_SRB_MTR0;
+    else
+        fdctrl->srb &= ~FD_SRB_MTR0;
+    if (value & FD_DOR_MOTEN1)
+        fdctrl->srb |= FD_SRB_MTR1;
+    else
+        fdctrl->srb &= ~FD_SRB_MTR1;
+
+    /* Drive */
+    if (value & 1)
+        fdctrl->srb |= FD_SRB_DR0;
+    else
+        fdctrl->srb &= ~FD_SRB_DR0;
+
+    /* Reset */
+    if (!(value & FD_DOR_nRESET)) {
+        if (fdctrl->dor & FD_DOR_nRESET) {
+            FLOPPY_DPRINTF("controller enter RESET state\n");
+        }
+    } else {
+        if (!(fdctrl->dor & FD_DOR_nRESET)) {
+            FLOPPY_DPRINTF("controller out of RESET state\n");
+            fdctrl_reset(fdctrl, 1);
+            fdctrl->dsr &= ~FD_DSR_PWRDOWN;
+        }
+    }
+    /* Selected drive */
+    fdctrl->cur_drv = value & FD_DOR_SELMASK;
+
+    fdctrl->dor = value;
+}
+
+/* Tape drive register : 0x03 */
+static uint32_t fdctrl_read_tape(FDCtrl *fdctrl)
+{
+    uint32_t retval = fdctrl->tdr;
+
+    FLOPPY_DPRINTF("tape drive register: 0x%02x\n", retval);
+
+    return retval;
+}
+
+static void fdctrl_write_tape(FDCtrl *fdctrl, uint32_t value)
+{
+    /* Reset mode */
+    if (!(fdctrl->dor & FD_DOR_nRESET)) {
+        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
+        return;
+    }
+    FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value);
+    /* Disk boot selection indicator */
+    fdctrl->tdr = value & FD_TDR_BOOTSEL;
+    /* Tape indicators: never allow */
+}
+
+/* Main status register : 0x04 (read) */
+static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl)
+{
+    uint32_t retval = fdctrl->msr;
+
+    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
+    fdctrl->dor |= FD_DOR_nRESET;
+
+    /* Sparc mutation */
+    if (fdctrl->sun4m) {
+        retval |= FD_MSR_DIO;
+        fdctrl_reset_irq(fdctrl);
+    };
+
+    FLOPPY_DPRINTF("main status register: 0x%02x\n", retval);
+
+    return retval;
+}
+
+/* Data select rate register : 0x04 (write) */
+static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value)
+{
+    /* Reset mode */
+    if (!(fdctrl->dor & FD_DOR_nRESET)) {
+        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
+        return;
+    }
+    FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value);
+    /* Reset: autoclear */
+    if (value & FD_DSR_SWRESET) {
+        fdctrl->dor &= ~FD_DOR_nRESET;
+        fdctrl_reset(fdctrl, 1);
+        fdctrl->dor |= FD_DOR_nRESET;
+    }
+    if (value & FD_DSR_PWRDOWN) {
+        fdctrl_reset(fdctrl, 1);
+    }
+    fdctrl->dsr = value;
+}
+
+/* Configuration control register: 0x07 (write) */
+static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value)
+{
+    /* Reset mode */
+    if (!(fdctrl->dor & FD_DOR_nRESET)) {
+        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
+        return;
+    }
+    FLOPPY_DPRINTF("configuration control register set to 0x%02x\n", value);
+
+    /* Only the rate selection bits used in AT mode, and we
+     * store those in the DSR.
+     */
+    fdctrl->dsr = (fdctrl->dsr & ~FD_DSR_DRATEMASK) |
+                  (value & FD_DSR_DRATEMASK);
+}
+
+static int fdctrl_media_changed(FDrive *drv)
+{
+    return drv->media_changed;
+}
+
+/* Digital input register : 0x07 (read-only) */
+static uint32_t fdctrl_read_dir(FDCtrl *fdctrl)
+{
+    uint32_t retval = 0;
+
+    if (fdctrl_media_changed(get_cur_drv(fdctrl))) {
+        retval |= FD_DIR_DSKCHG;
+    }
+    if (retval != 0) {
+        FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval);
+    }
+
+    return retval;
+}
+
+/* FIFO state control */
+static void fdctrl_reset_fifo(FDCtrl *fdctrl)
+{
+    fdctrl->data_dir = FD_DIR_WRITE;
+    fdctrl->data_pos = 0;
+    fdctrl->msr &= ~(FD_MSR_CMDBUSY | FD_MSR_DIO);
+}
+
+/* Set FIFO status for the host to read */
+static void fdctrl_set_fifo(FDCtrl *fdctrl, int fifo_len)
+{
+    fdctrl->data_dir = FD_DIR_READ;
+    fdctrl->data_len = fifo_len;
+    fdctrl->data_pos = 0;
+    fdctrl->msr |= FD_MSR_CMDBUSY | FD_MSR_RQM | FD_MSR_DIO;
+}
+
+/* Set an error: unimplemented/unknown command */
+static void fdctrl_unimplemented(FDCtrl *fdctrl, int direction)
+{
+    qemu_log_mask(LOG_UNIMP, "fdc: unimplemented command 0x%02x\n",
+                  fdctrl->fifo[0]);
+    fdctrl->fifo[0] = FD_SR0_INVCMD;
+    fdctrl_set_fifo(fdctrl, 1);
+}
+
+/* Seek to next sector
+ * returns 0 when end of track reached (for DBL_SIDES on head 1)
+ * otherwise returns 1
+ */
+static int fdctrl_seek_to_next_sect(FDCtrl *fdctrl, FDrive *cur_drv)
+{
+    FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d)\n",
+                   cur_drv->head, cur_drv->track, cur_drv->sect,
+                   fd_sector(cur_drv));
+    /* XXX: cur_drv->sect >= cur_drv->last_sect should be an
+       error in fact */
+    uint8_t new_head = cur_drv->head;
+    uint8_t new_track = cur_drv->track;
+    uint8_t new_sect = cur_drv->sect;
+
+    int ret = 1;
+
+    if (new_sect >= cur_drv->last_sect ||
+        new_sect == fdctrl->eot) {
+        new_sect = 1;
+        if (FD_MULTI_TRACK(fdctrl->data_state)) {
+            if (new_head == 0 &&
+                (cur_drv->flags & FDISK_DBL_SIDES) != 0) {
+                new_head = 1;
+            } else {
+                new_head = 0;
+                new_track++;
+                fdctrl->status0 |= FD_SR0_SEEK;
+                if ((cur_drv->flags & FDISK_DBL_SIDES) == 0) {
+                    ret = 0;
+                }
+            }
+        } else {
+            fdctrl->status0 |= FD_SR0_SEEK;
+            new_track++;
+            ret = 0;
+        }
+        if (ret == 1) {
+            FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
+                    new_head, new_track, new_sect, fd_sector(cur_drv));
+        }
+    } else {
+        new_sect++;
+    }
+    fd_seek(cur_drv, new_head, new_track, new_sect, 1);
+    return ret;
+}
+
+/* Callback for transfer end (stop or abort) */
+static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0,
+                                 uint8_t status1, uint8_t status2)
+{
+    FDrive *cur_drv;
+    cur_drv = get_cur_drv(fdctrl);
+
+    fdctrl->status0 &= ~(FD_SR0_DS0 | FD_SR0_DS1 | FD_SR0_HEAD);
+    fdctrl->status0 |= GET_CUR_DRV(fdctrl);
+    if (cur_drv->head) {
+        fdctrl->status0 |= FD_SR0_HEAD;
+    }
+    fdctrl->status0 |= status0;
+
+    FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
+                   status0, status1, status2, fdctrl->status0);
+    fdctrl->fifo[0] = fdctrl->status0;
+    fdctrl->fifo[1] = status1;
+    fdctrl->fifo[2] = status2;
+    fdctrl->fifo[3] = cur_drv->track;
+    fdctrl->fifo[4] = cur_drv->head;
+    fdctrl->fifo[5] = cur_drv->sect;
+    fdctrl->fifo[6] = FD_SECTOR_SC;
+    fdctrl->data_dir = FD_DIR_READ;
+    if (!(fdctrl->msr & FD_MSR_NONDMA)) {
+        DMA_release_DREQ(fdctrl->dma_chann);
+    }
+    fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
+    fdctrl->msr &= ~FD_MSR_NONDMA;
+
+    fdctrl_set_fifo(fdctrl, 7);
+    fdctrl_raise_irq(fdctrl);
+}
+
+/* Prepare a data transfer (either DMA or FIFO) */
+static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction)
+{
+    FDrive *cur_drv;
+    uint8_t kh, kt, ks;
+
+    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+    cur_drv = get_cur_drv(fdctrl);
+    kt = fdctrl->fifo[2];
+    kh = fdctrl->fifo[3];
+    ks = fdctrl->fifo[4];
+    FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n",
+                   GET_CUR_DRV(fdctrl), kh, kt, ks,
+                   fd_sector_calc(kh, kt, ks, cur_drv->last_sect,
+                                  NUM_SIDES(cur_drv)));
+    switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
+    case 2:
+        /* sect too big */
+        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
+        fdctrl->fifo[3] = kt;
+        fdctrl->fifo[4] = kh;
+        fdctrl->fifo[5] = ks;
+        return;
+    case 3:
+        /* track too big */
+        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00);
+        fdctrl->fifo[3] = kt;
+        fdctrl->fifo[4] = kh;
+        fdctrl->fifo[5] = ks;
+        return;
+    case 4:
+        /* No seek enabled */
+        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
+        fdctrl->fifo[3] = kt;
+        fdctrl->fifo[4] = kh;
+        fdctrl->fifo[5] = ks;
+        return;
+    case 1:
+        fdctrl->status0 |= FD_SR0_SEEK;
+        break;
+    default:
+        break;
+    }
+
+    /* Check the data rate. If the programmed data rate does not match
+     * the currently inserted medium, the operation has to fail. */
+    if (fdctrl->check_media_rate &&
+        (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) {
+        FLOPPY_DPRINTF("data rate mismatch (fdc=%d, media=%d)\n",
+                       fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate);
+        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00);
+        fdctrl->fifo[3] = kt;
+        fdctrl->fifo[4] = kh;
+        fdctrl->fifo[5] = ks;
+        return;
+    }
+
+    /* Set the FIFO state */
+    fdctrl->data_dir = direction;
+    fdctrl->data_pos = 0;
+    assert(fdctrl->msr & FD_MSR_CMDBUSY);
+    if (fdctrl->fifo[0] & 0x80)
+        fdctrl->data_state |= FD_STATE_MULTI;
+    else
+        fdctrl->data_state &= ~FD_STATE_MULTI;
+    if (fdctrl->fifo[5] == 0) {
+        fdctrl->data_len = fdctrl->fifo[8];
+    } else {
+        int tmp;
+        fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]);
+        tmp = (fdctrl->fifo[6] - ks + 1);
+        if (fdctrl->fifo[0] & 0x80)
+            tmp += fdctrl->fifo[6];
+        fdctrl->data_len *= tmp;
+    }
+    fdctrl->eot = fdctrl->fifo[6];
+    if (fdctrl->dor & FD_DOR_DMAEN) {
+        int dma_mode;
+        /* DMA transfer are enabled. Check if DMA channel is well programmed */
+        dma_mode = DMA_get_channel_mode(fdctrl->dma_chann);
+        dma_mode = (dma_mode >> 2) & 3;
+        FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n",
+                       dma_mode, direction,
+                       (128 << fdctrl->fifo[5]) *
+                       (cur_drv->last_sect - ks + 1), fdctrl->data_len);
+        if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL ||
+              direction == FD_DIR_SCANH) && dma_mode == 0) ||
+            (direction == FD_DIR_WRITE && dma_mode == 2) ||
+            (direction == FD_DIR_READ && dma_mode == 1) ||
+            (direction == FD_DIR_VERIFY)) {
+            /* No access is allowed until DMA transfer has completed */
+            fdctrl->msr &= ~FD_MSR_RQM;
+            if (direction != FD_DIR_VERIFY) {
+                /* Now, we just have to wait for the DMA controller to
+                 * recall us...
+                 */
+                DMA_hold_DREQ(fdctrl->dma_chann);
+                DMA_schedule(fdctrl->dma_chann);
+            } else {
+                /* Start transfer */
+                fdctrl_transfer_handler(fdctrl, fdctrl->dma_chann, 0,
+                                        fdctrl->data_len);
+            }
+            return;
+        } else {
+            FLOPPY_DPRINTF("bad dma_mode=%d direction=%d\n", dma_mode,
+                           direction);
+        }
+    }
+    FLOPPY_DPRINTF("start non-DMA transfer\n");
+    fdctrl->msr |= FD_MSR_NONDMA;
+    if (direction != FD_DIR_WRITE)
+        fdctrl->msr |= FD_MSR_DIO;
+    /* IO based transfer: calculate len */
+    fdctrl_raise_irq(fdctrl);
+}
+
+/* Prepare a transfer of deleted data */
+static void fdctrl_start_transfer_del(FDCtrl *fdctrl, int direction)
+{
+    qemu_log_mask(LOG_UNIMP, "fdctrl_start_transfer_del() unimplemented\n");
+
+    /* We don't handle deleted data,
+     * so we don't return *ANYTHING*
+     */
+    fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
+}
+
+/* handlers for DMA transfers */
+static int fdctrl_transfer_handler (void *opaque, int nchan,
+                                    int dma_pos, int dma_len)
+{
+    FDCtrl *fdctrl;
+    FDrive *cur_drv;
+    int len, start_pos, rel_pos;
+    uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00;
+
+    fdctrl = opaque;
+    if (fdctrl->msr & FD_MSR_RQM) {
+        FLOPPY_DPRINTF("Not in DMA transfer mode !\n");
+        return 0;
+    }
+    cur_drv = get_cur_drv(fdctrl);
+    if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL ||
+        fdctrl->data_dir == FD_DIR_SCANH)
+        status2 = FD_SR2_SNS;
+    if (dma_len > fdctrl->data_len)
+        dma_len = fdctrl->data_len;
+    if (cur_drv->bs == NULL) {
+        if (fdctrl->data_dir == FD_DIR_WRITE)
+            fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
+        else
+            fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
+        len = 0;
+        goto transfer_error;
+    }
+    rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
+    for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) {
+        len = dma_len - fdctrl->data_pos;
+        if (len + rel_pos > FD_SECTOR_LEN)
+            len = FD_SECTOR_LEN - rel_pos;
+        FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x "
+                       "(%d-0x%08x 0x%08x)\n", len, dma_len, fdctrl->data_pos,
+                       fdctrl->data_len, GET_CUR_DRV(fdctrl), cur_drv->head,
+                       cur_drv->track, cur_drv->sect, fd_sector(cur_drv),
+                       fd_sector(cur_drv) * FD_SECTOR_LEN);
+        if (fdctrl->data_dir != FD_DIR_WRITE ||
+            len < FD_SECTOR_LEN || rel_pos != 0) {
+            /* READ & SCAN commands and realign to a sector for WRITE */
+            if (bdrv_read(cur_drv->bs, fd_sector(cur_drv),
+                          fdctrl->fifo, 1) < 0) {
+                FLOPPY_DPRINTF("Floppy: error getting sector %d\n",
+                               fd_sector(cur_drv));
+                /* Sure, image size is too small... */
+                memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
+            }
+        }
+        switch (fdctrl->data_dir) {
+        case FD_DIR_READ:
+            /* READ commands */
+            DMA_write_memory (nchan, fdctrl->fifo + rel_pos,
+                              fdctrl->data_pos, len);
+            break;
+        case FD_DIR_WRITE:
+            /* WRITE commands */
+            if (cur_drv->ro) {
+                /* Handle readonly medium early, no need to do DMA, touch the
+                 * LED or attempt any writes. A real floppy doesn't attempt
+                 * to write to readonly media either. */
+                fdctrl_stop_transfer(fdctrl,
+                                     FD_SR0_ABNTERM | FD_SR0_SEEK, FD_SR1_NW,
+                                     0x00);
+                goto transfer_error;
+            }
+
+            DMA_read_memory (nchan, fdctrl->fifo + rel_pos,
+                             fdctrl->data_pos, len);
+            if (bdrv_write(cur_drv->bs, fd_sector(cur_drv),
+                           fdctrl->fifo, 1) < 0) {
+                FLOPPY_DPRINTF("error writing sector %d\n",
+                               fd_sector(cur_drv));
+                fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
+                goto transfer_error;
+            }
+            break;
+        case FD_DIR_VERIFY:
+            /* VERIFY commands */
+            break;
+        default:
+            /* SCAN commands */
+            {
+                uint8_t tmpbuf[FD_SECTOR_LEN];
+                int ret;
+                DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len);
+                ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len);
+                if (ret == 0) {
+                    status2 = FD_SR2_SEH;
+                    goto end_transfer;
+                }
+                if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) ||
+                    (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) {
+                    status2 = 0x00;
+                    goto end_transfer;
+                }
+            }
+            break;
+        }
+        fdctrl->data_pos += len;
+        rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
+        if (rel_pos == 0) {
+            /* Seek to next sector */
+            if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv))
+                break;
+        }
+    }
+ end_transfer:
+    len = fdctrl->data_pos - start_pos;
+    FLOPPY_DPRINTF("end transfer %d %d %d\n",
+                   fdctrl->data_pos, len, fdctrl->data_len);
+    if (fdctrl->data_dir == FD_DIR_SCANE ||
+        fdctrl->data_dir == FD_DIR_SCANL ||
+        fdctrl->data_dir == FD_DIR_SCANH)
+        status2 = FD_SR2_SEH;
+    fdctrl->data_len -= len;
+    fdctrl_stop_transfer(fdctrl, status0, status1, status2);
+ transfer_error:
+
+    return len;
+}
+
+/* Data register : 0x05 */
+static uint32_t fdctrl_read_data(FDCtrl *fdctrl)
+{
+    FDrive *cur_drv;
+    uint32_t retval = 0;
+    int pos;
+
+    cur_drv = get_cur_drv(fdctrl);
+    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
+    if (!(fdctrl->msr & FD_MSR_RQM) || !(fdctrl->msr & FD_MSR_DIO)) {
+        FLOPPY_DPRINTF("error: controller not ready for reading\n");
+        return 0;
+    }
+    pos = fdctrl->data_pos;
+    if (fdctrl->msr & FD_MSR_NONDMA) {
+        pos %= FD_SECTOR_LEN;
+        if (pos == 0) {
+            if (fdctrl->data_pos != 0)
+                if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
+                    FLOPPY_DPRINTF("error seeking to next sector %d\n",
+                                   fd_sector(cur_drv));
+                    return 0;
+                }
+            if (bdrv_read(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
+                FLOPPY_DPRINTF("error getting sector %d\n",
+                               fd_sector(cur_drv));
+                /* Sure, image size is too small... */
+                memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
+            }
+        }
+    }
+    retval = fdctrl->fifo[pos];
+    if (++fdctrl->data_pos == fdctrl->data_len) {
+        fdctrl->data_pos = 0;
+        /* Switch from transfer mode to status mode
+         * then from status mode to command mode
+         */
+        if (fdctrl->msr & FD_MSR_NONDMA) {
+            fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
+        } else {
+            fdctrl_reset_fifo(fdctrl);
+            fdctrl_reset_irq(fdctrl);
+        }
+    }
+    FLOPPY_DPRINTF("data register: 0x%02x\n", retval);
+
+    return retval;
+}
+
+static void fdctrl_format_sector(FDCtrl *fdctrl)
+{
+    FDrive *cur_drv;
+    uint8_t kh, kt, ks;
+
+    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+    cur_drv = get_cur_drv(fdctrl);
+    kt = fdctrl->fifo[6];
+    kh = fdctrl->fifo[7];
+    ks = fdctrl->fifo[8];
+    FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n",
+                   GET_CUR_DRV(fdctrl), kh, kt, ks,
+                   fd_sector_calc(kh, kt, ks, cur_drv->last_sect,
+                                  NUM_SIDES(cur_drv)));
+    switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
+    case 2:
+        /* sect too big */
+        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
+        fdctrl->fifo[3] = kt;
+        fdctrl->fifo[4] = kh;
+        fdctrl->fifo[5] = ks;
+        return;
+    case 3:
+        /* track too big */
+        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00);
+        fdctrl->fifo[3] = kt;
+        fdctrl->fifo[4] = kh;
+        fdctrl->fifo[5] = ks;
+        return;
+    case 4:
+        /* No seek enabled */
+        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
+        fdctrl->fifo[3] = kt;
+        fdctrl->fifo[4] = kh;
+        fdctrl->fifo[5] = ks;
+        return;
+    case 1:
+        fdctrl->status0 |= FD_SR0_SEEK;
+        break;
+    default:
+        break;
+    }
+    memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
+    if (cur_drv->bs == NULL ||
+        bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
+        FLOPPY_DPRINTF("error formatting sector %d\n", fd_sector(cur_drv));
+        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
+    } else {
+        if (cur_drv->sect == cur_drv->last_sect) {
+            fdctrl->data_state &= ~FD_STATE_FORMAT;
+            /* Last sector done */
+            fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
+        } else {
+            /* More to do */
+            fdctrl->data_pos = 0;
+            fdctrl->data_len = 4;
+        }
+    }
+}
+
+static void fdctrl_handle_lock(FDCtrl *fdctrl, int direction)
+{
+    fdctrl->lock = (fdctrl->fifo[0] & 0x80) ? 1 : 0;
+    fdctrl->fifo[0] = fdctrl->lock << 4;
+    fdctrl_set_fifo(fdctrl, 1);
+}
+
+static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction)
+{
+    FDrive *cur_drv = get_cur_drv(fdctrl);
+
+    /* Drives position */
+    fdctrl->fifo[0] = drv0(fdctrl)->track;
+    fdctrl->fifo[1] = drv1(fdctrl)->track;
+#if MAX_FD == 4
+    fdctrl->fifo[2] = drv2(fdctrl)->track;
+    fdctrl->fifo[3] = drv3(fdctrl)->track;
+#else
+    fdctrl->fifo[2] = 0;
+    fdctrl->fifo[3] = 0;
+#endif
+    /* timers */
+    fdctrl->fifo[4] = fdctrl->timer0;
+    fdctrl->fifo[5] = (fdctrl->timer1 << 1) | (fdctrl->dor & FD_DOR_DMAEN ? 1 : 0);
+    fdctrl->fifo[6] = cur_drv->last_sect;
+    fdctrl->fifo[7] = (fdctrl->lock << 7) |
+        (cur_drv->perpendicular << 2);
+    fdctrl->fifo[8] = fdctrl->config;
+    fdctrl->fifo[9] = fdctrl->precomp_trk;
+    fdctrl_set_fifo(fdctrl, 10);
+}
+
+static void fdctrl_handle_version(FDCtrl *fdctrl, int direction)
+{
+    /* Controller's version */
+    fdctrl->fifo[0] = fdctrl->version;
+    fdctrl_set_fifo(fdctrl, 1);
+}
+
+static void fdctrl_handle_partid(FDCtrl *fdctrl, int direction)
+{
+    fdctrl->fifo[0] = 0x41; /* Stepping 1 */
+    fdctrl_set_fifo(fdctrl, 1);
+}
+
+static void fdctrl_handle_restore(FDCtrl *fdctrl, int direction)
+{
+    FDrive *cur_drv = get_cur_drv(fdctrl);
+
+    /* Drives position */
+    drv0(fdctrl)->track = fdctrl->fifo[3];
+    drv1(fdctrl)->track = fdctrl->fifo[4];
+#if MAX_FD == 4
+    drv2(fdctrl)->track = fdctrl->fifo[5];
+    drv3(fdctrl)->track = fdctrl->fifo[6];
+#endif
+    /* timers */
+    fdctrl->timer0 = fdctrl->fifo[7];
+    fdctrl->timer1 = fdctrl->fifo[8];
+    cur_drv->last_sect = fdctrl->fifo[9];
+    fdctrl->lock = fdctrl->fifo[10] >> 7;
+    cur_drv->perpendicular = (fdctrl->fifo[10] >> 2) & 0xF;
+    fdctrl->config = fdctrl->fifo[11];
+    fdctrl->precomp_trk = fdctrl->fifo[12];
+    fdctrl->pwrd = fdctrl->fifo[13];
+    fdctrl_reset_fifo(fdctrl);
+}
+
+static void fdctrl_handle_save(FDCtrl *fdctrl, int direction)
+{
+    FDrive *cur_drv = get_cur_drv(fdctrl);
+
+    fdctrl->fifo[0] = 0;
+    fdctrl->fifo[1] = 0;
+    /* Drives position */
+    fdctrl->fifo[2] = drv0(fdctrl)->track;
+    fdctrl->fifo[3] = drv1(fdctrl)->track;
+#if MAX_FD == 4
+    fdctrl->fifo[4] = drv2(fdctrl)->track;
+    fdctrl->fifo[5] = drv3(fdctrl)->track;
+#else
+    fdctrl->fifo[4] = 0;
+    fdctrl->fifo[5] = 0;
+#endif
+    /* timers */
+    fdctrl->fifo[6] = fdctrl->timer0;
+    fdctrl->fifo[7] = fdctrl->timer1;
+    fdctrl->fifo[8] = cur_drv->last_sect;
+    fdctrl->fifo[9] = (fdctrl->lock << 7) |
+        (cur_drv->perpendicular << 2);
+    fdctrl->fifo[10] = fdctrl->config;
+    fdctrl->fifo[11] = fdctrl->precomp_trk;
+    fdctrl->fifo[12] = fdctrl->pwrd;
+    fdctrl->fifo[13] = 0;
+    fdctrl->fifo[14] = 0;
+    fdctrl_set_fifo(fdctrl, 15);
+}
+
+static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction)
+{
+    FDrive *cur_drv = get_cur_drv(fdctrl);
+
+    cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
+    qemu_mod_timer(fdctrl->result_timer,
+                   qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / 50));
+}
+
+static void fdctrl_handle_format_track(FDCtrl *fdctrl, int direction)
+{
+    FDrive *cur_drv;
+
+    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+    cur_drv = get_cur_drv(fdctrl);
+    fdctrl->data_state |= FD_STATE_FORMAT;
+    if (fdctrl->fifo[0] & 0x80)
+        fdctrl->data_state |= FD_STATE_MULTI;
+    else
+        fdctrl->data_state &= ~FD_STATE_MULTI;
+    cur_drv->bps =
+        fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2];
+#if 0
+    cur_drv->last_sect =
+        cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] :
+        fdctrl->fifo[3] / 2;
+#else
+    cur_drv->last_sect = fdctrl->fifo[3];
+#endif
+    /* TODO: implement format using DMA expected by the Bochs BIOS
+     * and Linux fdformat (read 3 bytes per sector via DMA and fill
+     * the sector with the specified fill byte
+     */
+    fdctrl->data_state &= ~FD_STATE_FORMAT;
+    fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
+}
+
+static void fdctrl_handle_specify(FDCtrl *fdctrl, int direction)
+{
+    fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF;
+    fdctrl->timer1 = fdctrl->fifo[2] >> 1;
+    if (fdctrl->fifo[2] & 1)
+        fdctrl->dor &= ~FD_DOR_DMAEN;
+    else
+        fdctrl->dor |= FD_DOR_DMAEN;
+    /* No result back */
+    fdctrl_reset_fifo(fdctrl);
+}
+
+static void fdctrl_handle_sense_drive_status(FDCtrl *fdctrl, int direction)
+{
+    FDrive *cur_drv;
+
+    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+    cur_drv = get_cur_drv(fdctrl);
+    cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
+    /* 1 Byte status back */
+    fdctrl->fifo[0] = (cur_drv->ro << 6) |
+        (cur_drv->track == 0 ? 0x10 : 0x00) |
+        (cur_drv->head << 2) |
+        GET_CUR_DRV(fdctrl) |
+        0x28;
+    fdctrl_set_fifo(fdctrl, 1);
+}
+
+static void fdctrl_handle_recalibrate(FDCtrl *fdctrl, int direction)
+{
+    FDrive *cur_drv;
+
+    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+    cur_drv = get_cur_drv(fdctrl);
+    fd_recalibrate(cur_drv);
+    fdctrl_reset_fifo(fdctrl);
+    /* Raise Interrupt */
+    fdctrl->status0 |= FD_SR0_SEEK;
+    fdctrl_raise_irq(fdctrl);
+}
+
+static void fdctrl_handle_sense_interrupt_status(FDCtrl *fdctrl, int direction)
+{
+    FDrive *cur_drv = get_cur_drv(fdctrl);
+
+    if (fdctrl->reset_sensei > 0) {
+        fdctrl->fifo[0] =
+            FD_SR0_RDYCHG + FD_RESET_SENSEI_COUNT - fdctrl->reset_sensei;
+        fdctrl->reset_sensei--;
+    } else if (!(fdctrl->sra & FD_SRA_INTPEND)) {
+        fdctrl->fifo[0] = FD_SR0_INVCMD;
+        fdctrl_set_fifo(fdctrl, 1);
+        return;
+    } else {
+        fdctrl->fifo[0] =
+                (fdctrl->status0 & ~(FD_SR0_HEAD | FD_SR0_DS1 | FD_SR0_DS0))
+                | GET_CUR_DRV(fdctrl);
+    }
+
+    fdctrl->fifo[1] = cur_drv->track;
+    fdctrl_set_fifo(fdctrl, 2);
+    fdctrl_reset_irq(fdctrl);
+    fdctrl->status0 = FD_SR0_RDYCHG;
+}
+
+static void fdctrl_handle_seek(FDCtrl *fdctrl, int direction)
+{
+    FDrive *cur_drv;
+
+    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+    cur_drv = get_cur_drv(fdctrl);
+    fdctrl_reset_fifo(fdctrl);
+    /* The seek command just sends step pulses to the drive and doesn't care if
+     * there is a medium inserted of if it's banging the head against the drive.
+     */
+    fd_seek(cur_drv, cur_drv->head, fdctrl->fifo[2], cur_drv->sect, 1);
+    /* Raise Interrupt */
+    fdctrl->status0 |= FD_SR0_SEEK;
+    fdctrl_raise_irq(fdctrl);
+}
+
+static void fdctrl_handle_perpendicular_mode(FDCtrl *fdctrl, int direction)
+{
+    FDrive *cur_drv = get_cur_drv(fdctrl);
+
+    if (fdctrl->fifo[1] & 0x80)
+        cur_drv->perpendicular = fdctrl->fifo[1] & 0x7;
+    /* No result back */
+    fdctrl_reset_fifo(fdctrl);
+}
+
+static void fdctrl_handle_configure(FDCtrl *fdctrl, int direction)
+{
+    fdctrl->config = fdctrl->fifo[2];
+    fdctrl->precomp_trk =  fdctrl->fifo[3];
+    /* No result back */
+    fdctrl_reset_fifo(fdctrl);
+}
+
+static void fdctrl_handle_powerdown_mode(FDCtrl *fdctrl, int direction)
+{
+    fdctrl->pwrd = fdctrl->fifo[1];
+    fdctrl->fifo[0] = fdctrl->fifo[1];
+    fdctrl_set_fifo(fdctrl, 1);
+}
+
+static void fdctrl_handle_option(FDCtrl *fdctrl, int direction)
+{
+    /* No result back */
+    fdctrl_reset_fifo(fdctrl);
+}
+
+static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direction)
+{
+    FDrive *cur_drv = get_cur_drv(fdctrl);
+
+    if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) {
+        /* Command parameters done */
+        if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) {
+            fdctrl->fifo[0] = fdctrl->fifo[1];
+            fdctrl->fifo[2] = 0;
+            fdctrl->fifo[3] = 0;
+            fdctrl_set_fifo(fdctrl, 4);
+        } else {
+            fdctrl_reset_fifo(fdctrl);
+        }
+    } else if (fdctrl->data_len > 7) {
+        /* ERROR */
+        fdctrl->fifo[0] = 0x80 |
+            (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
+        fdctrl_set_fifo(fdctrl, 1);
+    }
+}
+
+static void fdctrl_handle_relative_seek_in(FDCtrl *fdctrl, int direction)
+{
+    FDrive *cur_drv;
+
+    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+    cur_drv = get_cur_drv(fdctrl);
+    if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) {
+        fd_seek(cur_drv, cur_drv->head, cur_drv->max_track - 1,
+                cur_drv->sect, 1);
+    } else {
+        fd_seek(cur_drv, cur_drv->head,
+                cur_drv->track + fdctrl->fifo[2], cur_drv->sect, 1);
+    }
+    fdctrl_reset_fifo(fdctrl);
+    /* Raise Interrupt */
+    fdctrl->status0 |= FD_SR0_SEEK;
+    fdctrl_raise_irq(fdctrl);
+}
+
+static void fdctrl_handle_relative_seek_out(FDCtrl *fdctrl, int direction)
+{
+    FDrive *cur_drv;
+
+    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
+    cur_drv = get_cur_drv(fdctrl);
+    if (fdctrl->fifo[2] > cur_drv->track) {
+        fd_seek(cur_drv, cur_drv->head, 0, cur_drv->sect, 1);
+    } else {
+        fd_seek(cur_drv, cur_drv->head,
+                cur_drv->track - fdctrl->fifo[2], cur_drv->sect, 1);
+    }
+    fdctrl_reset_fifo(fdctrl);
+    /* Raise Interrupt */
+    fdctrl->status0 |= FD_SR0_SEEK;
+    fdctrl_raise_irq(fdctrl);
+}
+
+static const struct {
+    uint8_t value;
+    uint8_t mask;
+    const char* name;
+    int parameters;
+    void (*handler)(FDCtrl *fdctrl, int direction);
+    int direction;
+} handlers[] = {
+    { FD_CMD_READ, 0x1f, "READ", 8, fdctrl_start_transfer, FD_DIR_READ },
+    { FD_CMD_WRITE, 0x3f, "WRITE", 8, fdctrl_start_transfer, FD_DIR_WRITE },
+    { FD_CMD_SEEK, 0xff, "SEEK", 2, fdctrl_handle_seek },
+    { FD_CMD_SENSE_INTERRUPT_STATUS, 0xff, "SENSE INTERRUPT STATUS", 0, fdctrl_handle_sense_interrupt_status },
+    { FD_CMD_RECALIBRATE, 0xff, "RECALIBRATE", 1, fdctrl_handle_recalibrate },
+    { FD_CMD_FORMAT_TRACK, 0xbf, "FORMAT TRACK", 5, fdctrl_handle_format_track },
+    { FD_CMD_READ_TRACK, 0xbf, "READ TRACK", 8, fdctrl_start_transfer, FD_DIR_READ },
+    { FD_CMD_RESTORE, 0xff, "RESTORE", 17, fdctrl_handle_restore }, /* part of READ DELETED DATA */
+    { FD_CMD_SAVE, 0xff, "SAVE", 0, fdctrl_handle_save }, /* part of READ DELETED DATA */
+    { FD_CMD_READ_DELETED, 0x1f, "READ DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_READ },
+    { FD_CMD_SCAN_EQUAL, 0x1f, "SCAN EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANE },
+    { FD_CMD_VERIFY, 0x1f, "VERIFY", 8, fdctrl_start_transfer, FD_DIR_VERIFY },
+    { FD_CMD_SCAN_LOW_OR_EQUAL, 0x1f, "SCAN LOW OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANL },
+    { FD_CMD_SCAN_HIGH_OR_EQUAL, 0x1f, "SCAN HIGH OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANH },
+    { FD_CMD_WRITE_DELETED, 0x3f, "WRITE DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_WRITE },
+    { FD_CMD_READ_ID, 0xbf, "READ ID", 1, fdctrl_handle_readid },
+    { FD_CMD_SPECIFY, 0xff, "SPECIFY", 2, fdctrl_handle_specify },
+    { FD_CMD_SENSE_DRIVE_STATUS, 0xff, "SENSE DRIVE STATUS", 1, fdctrl_handle_sense_drive_status },
+    { FD_CMD_PERPENDICULAR_MODE, 0xff, "PERPENDICULAR MODE", 1, fdctrl_handle_perpendicular_mode },
+    { FD_CMD_CONFIGURE, 0xff, "CONFIGURE", 3, fdctrl_handle_configure },
+    { FD_CMD_POWERDOWN_MODE, 0xff, "POWERDOWN MODE", 2, fdctrl_handle_powerdown_mode },
+    { FD_CMD_OPTION, 0xff, "OPTION", 1, fdctrl_handle_option },
+    { FD_CMD_DRIVE_SPECIFICATION_COMMAND, 0xff, "DRIVE SPECIFICATION COMMAND", 5, fdctrl_handle_drive_specification_command },
+    { FD_CMD_RELATIVE_SEEK_OUT, 0xff, "RELATIVE SEEK OUT", 2, fdctrl_handle_relative_seek_out },
+    { FD_CMD_FORMAT_AND_WRITE, 0xff, "FORMAT AND WRITE", 10, fdctrl_unimplemented },
+    { FD_CMD_RELATIVE_SEEK_IN, 0xff, "RELATIVE SEEK IN", 2, fdctrl_handle_relative_seek_in },
+    { FD_CMD_LOCK, 0x7f, "LOCK", 0, fdctrl_handle_lock },
+    { FD_CMD_DUMPREG, 0xff, "DUMPREG", 0, fdctrl_handle_dumpreg },
+    { FD_CMD_VERSION, 0xff, "VERSION", 0, fdctrl_handle_version },
+    { FD_CMD_PART_ID, 0xff, "PART ID", 0, fdctrl_handle_partid },
+    { FD_CMD_WRITE, 0x1f, "WRITE (BeOS)", 8, fdctrl_start_transfer, FD_DIR_WRITE }, /* not in specification ; BeOS 4.5 bug */
+    { 0, 0, "unknown", 0, fdctrl_unimplemented }, /* default handler */
+};
+/* Associate command to an index in the 'handlers' array */
+static uint8_t command_to_handler[256];
+
+static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
+{
+    FDrive *cur_drv;
+    int pos;
+
+    /* Reset mode */
+    if (!(fdctrl->dor & FD_DOR_nRESET)) {
+        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
+        return;
+    }
+    if (!(fdctrl->msr & FD_MSR_RQM) || (fdctrl->msr & FD_MSR_DIO)) {
+        FLOPPY_DPRINTF("error: controller not ready for writing\n");
+        return;
+    }
+    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
+    /* Is it write command time ? */
+    if (fdctrl->msr & FD_MSR_NONDMA) {
+        /* FIFO data write */
+        pos = fdctrl->data_pos++;
+        pos %= FD_SECTOR_LEN;
+        fdctrl->fifo[pos] = value;
+        if (pos == FD_SECTOR_LEN - 1 ||
+            fdctrl->data_pos == fdctrl->data_len) {
+            cur_drv = get_cur_drv(fdctrl);
+            if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
+                FLOPPY_DPRINTF("error writing sector %d\n",
+                               fd_sector(cur_drv));
+                return;
+            }
+            if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
+                FLOPPY_DPRINTF("error seeking to next sector %d\n",
+                               fd_sector(cur_drv));
+                return;
+            }
+        }
+        /* Switch from transfer mode to status mode
+         * then from status mode to command mode
+         */
+        if (fdctrl->data_pos == fdctrl->data_len)
+            fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
+        return;
+    }
+    if (fdctrl->data_pos == 0) {
+        /* Command */
+        pos = command_to_handler[value & 0xff];
+        FLOPPY_DPRINTF("%s command\n", handlers[pos].name);
+        fdctrl->data_len = handlers[pos].parameters + 1;
+        fdctrl->msr |= FD_MSR_CMDBUSY;
+    }
+
+    FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
+    fdctrl->fifo[fdctrl->data_pos++] = value;
+    if (fdctrl->data_pos == fdctrl->data_len) {
+        /* We now have all parameters
+         * and will be able to treat the command
+         */
+        if (fdctrl->data_state & FD_STATE_FORMAT) {
+            fdctrl_format_sector(fdctrl);
+            return;
+        }
+
+        pos = command_to_handler[fdctrl->fifo[0] & 0xff];
+        FLOPPY_DPRINTF("treat %s command\n", handlers[pos].name);
+        (*handlers[pos].handler)(fdctrl, handlers[pos].direction);
+    }
+}
+
+static void fdctrl_result_timer(void *opaque)
+{
+    FDCtrl *fdctrl = opaque;
+    FDrive *cur_drv = get_cur_drv(fdctrl);
+
+    /* Pretend we are spinning.
+     * This is needed for Coherent, which uses READ ID to check for
+     * sector interleaving.
+     */
+    if (cur_drv->last_sect != 0) {
+        cur_drv->sect = (cur_drv->sect % cur_drv->last_sect) + 1;
+    }
+    /* READ_ID can't automatically succeed! */
+    if (fdctrl->check_media_rate &&
+        (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) {
+        FLOPPY_DPRINTF("read id rate mismatch (fdc=%d, media=%d)\n",
+                       fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate);
+        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00);
+    } else {
+        fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
+    }
+}
+
+static void fdctrl_change_cb(void *opaque, bool load)
+{
+    FDrive *drive = opaque;
+
+    drive->media_changed = 1;
+    fd_revalidate(drive);
+}
+
+static const BlockDevOps fdctrl_block_ops = {
+    .change_media_cb = fdctrl_change_cb,
+};
+
+/* Init functions */
+static int fdctrl_connect_drives(FDCtrl *fdctrl)
+{
+    unsigned int i;
+    FDrive *drive;
+
+    for (i = 0; i < MAX_FD; i++) {
+        drive = &fdctrl->drives[i];
+        drive->fdctrl = fdctrl;
+
+        if (drive->bs) {
+            if (bdrv_get_on_error(drive->bs, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
+                error_report("fdc doesn't support drive option werror");
+                return -1;
+            }
+            if (bdrv_get_on_error(drive->bs, 1) != BLOCKDEV_ON_ERROR_REPORT) {
+                error_report("fdc doesn't support drive option rerror");
+                return -1;
+            }
+        }
+
+        fd_init(drive);
+        fdctrl_change_cb(drive, 0);
+        if (drive->bs) {
+            bdrv_set_dev_ops(drive->bs, &fdctrl_block_ops, drive);
+        }
+    }
+    return 0;
+}
+
+ISADevice *fdctrl_init_isa(ISABus *bus, DriveInfo **fds)
+{
+    ISADevice *dev;
+
+    dev = isa_try_create(bus, "isa-fdc");
+    if (!dev) {
+        return NULL;
+    }
+
+    if (fds[0]) {
+        qdev_prop_set_drive_nofail(&dev->qdev, "driveA", fds[0]->bdrv);
+    }
+    if (fds[1]) {
+        qdev_prop_set_drive_nofail(&dev->qdev, "driveB", fds[1]->bdrv);
+    }
+    qdev_init_nofail(&dev->qdev);
+
+    return dev;
+}
+
+void fdctrl_init_sysbus(qemu_irq irq, int dma_chann,
+                        hwaddr mmio_base, DriveInfo **fds)
+{
+    FDCtrl *fdctrl;
+    DeviceState *dev;
+    FDCtrlSysBus *sys;
+
+    dev = qdev_create(NULL, "sysbus-fdc");
+    sys = DO_UPCAST(FDCtrlSysBus, busdev.qdev, dev);
+    fdctrl = &sys->state;
+    fdctrl->dma_chann = dma_chann; /* FIXME */
+    if (fds[0]) {
+        qdev_prop_set_drive_nofail(dev, "driveA", fds[0]->bdrv);
+    }
+    if (fds[1]) {
+        qdev_prop_set_drive_nofail(dev, "driveB", fds[1]->bdrv);
+    }
+    qdev_init_nofail(dev);
+    sysbus_connect_irq(&sys->busdev, 0, irq);
+    sysbus_mmio_map(&sys->busdev, 0, mmio_base);
+}
+
+void sun4m_fdctrl_init(qemu_irq irq, hwaddr io_base,
+                       DriveInfo **fds, qemu_irq *fdc_tc)
+{
+    DeviceState *dev;
+    FDCtrlSysBus *sys;
+
+    dev = qdev_create(NULL, "SUNW,fdtwo");
+    if (fds[0]) {
+        qdev_prop_set_drive_nofail(dev, "drive", fds[0]->bdrv);
+    }
+    qdev_init_nofail(dev);
+    sys = DO_UPCAST(FDCtrlSysBus, busdev.qdev, dev);
+    sysbus_connect_irq(&sys->busdev, 0, irq);
+    sysbus_mmio_map(&sys->busdev, 0, io_base);
+    *fdc_tc = qdev_get_gpio_in(dev, 0);
+}
+
+static int fdctrl_init_common(FDCtrl *fdctrl)
+{
+    int i, j;
+    static int command_tables_inited = 0;
+
+    /* Fill 'command_to_handler' lookup table */
+    if (!command_tables_inited) {
+        command_tables_inited = 1;
+        for (i = ARRAY_SIZE(handlers) - 1; i >= 0; i--) {
+            for (j = 0; j < sizeof(command_to_handler); j++) {
+                if ((j & handlers[i].mask) == handlers[i].value) {
+                    command_to_handler[j] = i;
+                }
+            }
+        }
+    }
+
+    FLOPPY_DPRINTF("init controller\n");
+    fdctrl->fifo = qemu_memalign(512, FD_SECTOR_LEN);
+    fdctrl->fifo_size = 512;
+    fdctrl->result_timer = qemu_new_timer_ns(vm_clock,
+                                          fdctrl_result_timer, fdctrl);
+
+    fdctrl->version = 0x90; /* Intel 82078 controller */
+    fdctrl->config = FD_CONFIG_EIS | FD_CONFIG_EFIFO; /* Implicit seek, polling & FIFO enabled */
+    fdctrl->num_floppies = MAX_FD;
+
+    if (fdctrl->dma_chann != -1)
+        DMA_register_channel(fdctrl->dma_chann, &fdctrl_transfer_handler, fdctrl);
+    return fdctrl_connect_drives(fdctrl);
+}
+
+static const MemoryRegionPortio fdc_portio_list[] = {
+    { 1, 5, 1, .read = fdctrl_read, .write = fdctrl_write },
+    { 7, 1, 1, .read = fdctrl_read, .write = fdctrl_write },
+    PORTIO_END_OF_LIST(),
+};
+
+static int isabus_fdc_init1(ISADevice *dev)
+{
+    FDCtrlISABus *isa = DO_UPCAST(FDCtrlISABus, busdev, dev);
+    FDCtrl *fdctrl = &isa->state;
+    int ret;
+
+    isa_register_portio_list(dev, isa->iobase, fdc_portio_list, fdctrl, "fdc");
+
+    isa_init_irq(&isa->busdev, &fdctrl->irq, isa->irq);
+    fdctrl->dma_chann = isa->dma;
+
+    qdev_set_legacy_instance_id(&dev->qdev, isa->iobase, 2);
+    ret = fdctrl_init_common(fdctrl);
+
+    add_boot_device_path(isa->bootindexA, &dev->qdev, "/floppy@0");
+    add_boot_device_path(isa->bootindexB, &dev->qdev, "/floppy@1");
+
+    return ret;
+}
+
+static int sysbus_fdc_init1(SysBusDevice *dev)
+{
+    FDCtrlSysBus *sys = DO_UPCAST(FDCtrlSysBus, busdev, dev);
+    FDCtrl *fdctrl = &sys->state;
+    int ret;
+
+    memory_region_init_io(&fdctrl->iomem, &fdctrl_mem_ops, fdctrl, "fdc", 0x08);
+    sysbus_init_mmio(dev, &fdctrl->iomem);
+    sysbus_init_irq(dev, &fdctrl->irq);
+    qdev_init_gpio_in(&dev->qdev, fdctrl_handle_tc, 1);
+    fdctrl->dma_chann = -1;
+
+    qdev_set_legacy_instance_id(&dev->qdev, 0 /* io */, 2); /* FIXME */
+    ret = fdctrl_init_common(fdctrl);
+
+    return ret;
+}
+
+static int sun4m_fdc_init1(SysBusDevice *dev)
+{
+    FDCtrl *fdctrl = &(FROM_SYSBUS(FDCtrlSysBus, dev)->state);
+
+    memory_region_init_io(&fdctrl->iomem, &fdctrl_mem_strict_ops, fdctrl,
+                          "fdctrl", 0x08);
+    sysbus_init_mmio(dev, &fdctrl->iomem);
+    sysbus_init_irq(dev, &fdctrl->irq);
+    qdev_init_gpio_in(&dev->qdev, fdctrl_handle_tc, 1);
+
+    fdctrl->sun4m = 1;
+    qdev_set_legacy_instance_id(&dev->qdev, 0 /* io */, 2); /* FIXME */
+    return fdctrl_init_common(fdctrl);
+}
+
+FDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i)
+{
+    FDCtrlISABus *isa = DO_UPCAST(FDCtrlISABus, busdev, fdc);
+
+    return isa->state.drives[i].drive;
+}
+
+static const VMStateDescription vmstate_isa_fdc ={
+    .name = "fdc",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .fields = (VMStateField []) {
+        VMSTATE_STRUCT(state, FDCtrlISABus, 0, vmstate_fdc, FDCtrl),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property isa_fdc_properties[] = {
+    DEFINE_PROP_HEX32("iobase", FDCtrlISABus, iobase, 0x3f0),
+    DEFINE_PROP_UINT32("irq", FDCtrlISABus, irq, 6),
+    DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2),
+    DEFINE_PROP_DRIVE("driveA", FDCtrlISABus, state.drives[0].bs),
+    DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].bs),
+    DEFINE_PROP_INT32("bootindexA", FDCtrlISABus, bootindexA, -1),
+    DEFINE_PROP_INT32("bootindexB", FDCtrlISABus, bootindexB, -1),
+    DEFINE_PROP_BIT("check_media_rate", FDCtrlISABus, state.check_media_rate,
+                    0, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void isabus_fdc_class_init1(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+    ic->init = isabus_fdc_init1;
+    dc->fw_name = "fdc";
+    dc->no_user = 1;
+    dc->reset = fdctrl_external_reset_isa;
+    dc->vmsd = &vmstate_isa_fdc;
+    dc->props = isa_fdc_properties;
+}
+
+static const TypeInfo isa_fdc_info = {
+    .name          = "isa-fdc",
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(FDCtrlISABus),
+    .class_init    = isabus_fdc_class_init1,
+};
+
+static const VMStateDescription vmstate_sysbus_fdc ={
+    .name = "fdc",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .fields = (VMStateField []) {
+        VMSTATE_STRUCT(state, FDCtrlSysBus, 0, vmstate_fdc, FDCtrl),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property sysbus_fdc_properties[] = {
+    DEFINE_PROP_DRIVE("driveA", FDCtrlSysBus, state.drives[0].bs),
+    DEFINE_PROP_DRIVE("driveB", FDCtrlSysBus, state.drives[1].bs),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void sysbus_fdc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = sysbus_fdc_init1;
+    dc->reset = fdctrl_external_reset_sysbus;
+    dc->vmsd = &vmstate_sysbus_fdc;
+    dc->props = sysbus_fdc_properties;
+}
+
+static const TypeInfo sysbus_fdc_info = {
+    .name          = "sysbus-fdc",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(FDCtrlSysBus),
+    .class_init    = sysbus_fdc_class_init,
+};
+
+static Property sun4m_fdc_properties[] = {
+    DEFINE_PROP_DRIVE("drive", FDCtrlSysBus, state.drives[0].bs),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void sun4m_fdc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = sun4m_fdc_init1;
+    dc->reset = fdctrl_external_reset_sysbus;
+    dc->vmsd = &vmstate_sysbus_fdc;
+    dc->props = sun4m_fdc_properties;
+}
+
+static const TypeInfo sun4m_fdc_info = {
+    .name          = "SUNW,fdtwo",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(FDCtrlSysBus),
+    .class_init    = sun4m_fdc_class_init,
+};
+
+static void fdc_register_types(void)
+{
+    type_register_static(&isa_fdc_info);
+    type_register_static(&sysbus_fdc_info);
+    type_register_static(&sun4m_fdc_info);
+}
+
+type_init(fdc_register_types)
diff --git a/hw/block/hd-geometry.c b/hw/block/hd-geometry.c
new file mode 100644 (file)
index 0000000..6feb4f8
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Hard disk geometry utilities
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "block/block.h"
+#include "hw/block/block.h"
+#include "trace.h"
+
+struct partition {
+        uint8_t boot_ind;           /* 0x80 - active */
+        uint8_t head;               /* starting head */
+        uint8_t sector;             /* starting sector */
+        uint8_t cyl;                /* starting cylinder */
+        uint8_t sys_ind;            /* What partition type */
+        uint8_t end_head;           /* end head */
+        uint8_t end_sector;         /* end sector */
+        uint8_t end_cyl;            /* end cylinder */
+        uint32_t start_sect;        /* starting sector counting from 0 */
+        uint32_t nr_sects;          /* nr of sectors in partition */
+} QEMU_PACKED;
+
+/* try to guess the disk logical geometry from the MSDOS partition table.
+   Return 0 if OK, -1 if could not guess */
+static int guess_disk_lchs(BlockDriverState *bs,
+                           int *pcylinders, int *pheads, int *psectors)
+{
+    uint8_t buf[BDRV_SECTOR_SIZE];
+    int i, heads, sectors, cylinders;
+    struct partition *p;
+    uint32_t nr_sects;
+    uint64_t nb_sectors;
+
+    bdrv_get_geometry(bs, &nb_sectors);
+
+    /**
+     * The function will be invoked during startup not only in sync I/O mode,
+     * but also in async I/O mode. So the I/O throttling function has to
+     * be disabled temporarily here, not permanently.
+     */
+    if (bdrv_read_unthrottled(bs, 0, buf, 1) < 0) {
+        return -1;
+    }
+    /* test msdos magic */
+    if (buf[510] != 0x55 || buf[511] != 0xaa) {
+        return -1;
+    }
+    for (i = 0; i < 4; i++) {
+        p = ((struct partition *)(buf + 0x1be)) + i;
+        nr_sects = le32_to_cpu(p->nr_sects);
+        if (nr_sects && p->end_head) {
+            /* We make the assumption that the partition terminates on
+               a cylinder boundary */
+            heads = p->end_head + 1;
+            sectors = p->end_sector & 63;
+            if (sectors == 0) {
+                continue;
+            }
+            cylinders = nb_sectors / (heads * sectors);
+            if (cylinders < 1 || cylinders > 16383) {
+                continue;
+            }
+            *pheads = heads;
+            *psectors = sectors;
+            *pcylinders = cylinders;
+            trace_hd_geometry_lchs_guess(bs, cylinders, heads, sectors);
+            return 0;
+        }
+    }
+    return -1;
+}
+
+static void guess_chs_for_size(BlockDriverState *bs,
+                uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs)
+{
+    uint64_t nb_sectors;
+    int cylinders;
+
+    bdrv_get_geometry(bs, &nb_sectors);
+
+    cylinders = nb_sectors / (16 * 63);
+    if (cylinders > 16383) {
+        cylinders = 16383;
+    } else if (cylinders < 2) {
+        cylinders = 2;
+    }
+    *pcyls = cylinders;
+    *pheads = 16;
+    *psecs = 63;
+}
+
+void hd_geometry_guess(BlockDriverState *bs,
+                       uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs,
+                       int *ptrans)
+{
+    int cylinders, heads, secs, translation;
+
+    if (guess_disk_lchs(bs, &cylinders, &heads, &secs) < 0) {
+        /* no LCHS guess: use a standard physical disk geometry  */
+        guess_chs_for_size(bs, pcyls, pheads, psecs);
+        translation = hd_bios_chs_auto_trans(*pcyls, *pheads, *psecs);
+    } else if (heads > 16) {
+        /* LCHS guess with heads > 16 means that a BIOS LBA
+           translation was active, so a standard physical disk
+           geometry is OK */
+        guess_chs_for_size(bs, pcyls, pheads, psecs);
+        translation = *pcyls * *pheads <= 131072
+            ? BIOS_ATA_TRANSLATION_LARGE
+            : BIOS_ATA_TRANSLATION_LBA;
+    } else {
+        /* LCHS guess with heads <= 16: use as physical geometry */
+        *pcyls = cylinders;
+        *pheads = heads;
+        *psecs = secs;
+        /* disable any translation to be in sync with
+           the logical geometry */
+        translation = BIOS_ATA_TRANSLATION_NONE;
+    }
+    if (ptrans) {
+        *ptrans = translation;
+    }
+    trace_hd_geometry_guess(bs, *pcyls, *pheads, *psecs, translation);
+}
+
+int hd_bios_chs_auto_trans(uint32_t cyls, uint32_t heads, uint32_t secs)
+{
+    return cyls <= 1024 && heads <= 16 && secs <= 63
+        ? BIOS_ATA_TRANSLATION_NONE
+        : BIOS_ATA_TRANSLATION_LBA;
+}
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
new file mode 100644 (file)
index 0000000..cd560e3
--- /dev/null
@@ -0,0 +1,672 @@
+/*
+ * ST M25P80 emulator. Emulate all SPI flash devices based on the m25p80 command
+ * set. Known devices table current as of Jun/2012 and taken from linux.
+ * See drivers/mtd/devices/m25p80.c.
+ *
+ * Copyright (C) 2011 Edgar E. Iglesias <edgar.iglesias@gmail.com>
+ * Copyright (C) 2012 Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
+ * Copyright (C) 2012 PetaLogix
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) a later version of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "sysemu/blockdev.h"
+#include "hw/ssi.h"
+#include "hw/arm/devices.h"
+
+#ifdef M25P80_ERR_DEBUG
+#define DB_PRINT(...) do { \
+    fprintf(stderr,  ": %s: ", __func__); \
+    fprintf(stderr, ## __VA_ARGS__); \
+    } while (0);
+#else
+    #define DB_PRINT(...)
+#endif
+
+/* Fields for FlashPartInfo->flags */
+
+/* erase capabilities */
+#define ER_4K 1
+#define ER_32K 2
+/* set to allow the page program command to write 0s back to 1. Useful for
+ * modelling EEPROM with SPI flash command set
+ */
+#define WR_1 0x100
+
+typedef struct FlashPartInfo {
+    const char *part_name;
+    /* jedec code. (jedec >> 16) & 0xff is the 1st byte, >> 8 the 2nd etc */
+    uint32_t jedec;
+    /* extended jedec code */
+    uint16_t ext_jedec;
+    /* there is confusion between manufacturers as to what a sector is. In this
+     * device model, a "sector" is the size that is erased by the ERASE_SECTOR
+     * command (opcode 0xd8).
+     */
+    uint32_t sector_size;
+    uint32_t n_sectors;
+    uint32_t page_size;
+    uint8_t flags;
+} FlashPartInfo;
+
+/* adapted from linux */
+
+#define INFO(_part_name, _jedec, _ext_jedec, _sector_size, _n_sectors, _flags)\
+    .part_name = (_part_name),\
+    .jedec = (_jedec),\
+    .ext_jedec = (_ext_jedec),\
+    .sector_size = (_sector_size),\
+    .n_sectors = (_n_sectors),\
+    .page_size = 256,\
+    .flags = (_flags),\
+
+#define JEDEC_NUMONYX 0x20
+#define JEDEC_WINBOND 0xEF
+#define JEDEC_SPANSION 0x01
+
+static const FlashPartInfo known_devices[] = {
+    /* Atmel -- some are (confusingly) marketed as "DataFlash" */
+    { INFO("at25fs010",   0x1f6601,      0,  32 << 10,   4, ER_4K) },
+    { INFO("at25fs040",   0x1f6604,      0,  64 << 10,   8, ER_4K) },
+
+    { INFO("at25df041a",  0x1f4401,      0,  64 << 10,   8, ER_4K) },
+    { INFO("at25df321a",  0x1f4701,      0,  64 << 10,  64, ER_4K) },
+    { INFO("at25df641",   0x1f4800,      0,  64 << 10, 128, ER_4K) },
+
+    { INFO("at26f004",    0x1f0400,      0,  64 << 10,   8, ER_4K) },
+    { INFO("at26df081a",  0x1f4501,      0,  64 << 10,  16, ER_4K) },
+    { INFO("at26df161a",  0x1f4601,      0,  64 << 10,  32, ER_4K) },
+    { INFO("at26df321",   0x1f4700,      0,  64 << 10,  64, ER_4K) },
+
+    /* EON -- en25xxx */
+    { INFO("en25f32",     0x1c3116,      0,  64 << 10,  64, ER_4K) },
+    { INFO("en25p32",     0x1c2016,      0,  64 << 10,  64, 0) },
+    { INFO("en25q32b",    0x1c3016,      0,  64 << 10,  64, 0) },
+    { INFO("en25p64",     0x1c2017,      0,  64 << 10, 128, 0) },
+
+    /* Intel/Numonyx -- xxxs33b */
+    { INFO("160s33b",     0x898911,      0,  64 << 10,  32, 0) },
+    { INFO("320s33b",     0x898912,      0,  64 << 10,  64, 0) },
+    { INFO("640s33b",     0x898913,      0,  64 << 10, 128, 0) },
+
+    /* Macronix */
+    { INFO("mx25l4005a",  0xc22013,      0,  64 << 10,   8, ER_4K) },
+    { INFO("mx25l8005",   0xc22014,      0,  64 << 10,  16, 0) },
+    { INFO("mx25l1606e",  0xc22015,      0,  64 << 10,  32, ER_4K) },
+    { INFO("mx25l3205d",  0xc22016,      0,  64 << 10,  64, 0) },
+    { INFO("mx25l6405d",  0xc22017,      0,  64 << 10, 128, 0) },
+    { INFO("mx25l12805d", 0xc22018,      0,  64 << 10, 256, 0) },
+    { INFO("mx25l12855e", 0xc22618,      0,  64 << 10, 256, 0) },
+    { INFO("mx25l25635e", 0xc22019,      0,  64 << 10, 512, 0) },
+    { INFO("mx25l25655e", 0xc22619,      0,  64 << 10, 512, 0) },
+
+    /* Spansion -- single (large) sector size only, at least
+     * for the chips listed here (without boot sectors).
+     */
+    { INFO("s25sl004a",   0x010212,      0,  64 << 10,   8, 0) },
+    { INFO("s25sl008a",   0x010213,      0,  64 << 10,  16, 0) },
+    { INFO("s25sl016a",   0x010214,      0,  64 << 10,  32, 0) },
+    { INFO("s25sl032a",   0x010215,      0,  64 << 10,  64, 0) },
+    { INFO("s25sl032p",   0x010215, 0x4d00,  64 << 10,  64, ER_4K) },
+    { INFO("s25sl064a",   0x010216,      0,  64 << 10, 128, 0) },
+    { INFO("s25fl256s0",  0x010219, 0x4d00, 256 << 10, 128, 0) },
+    { INFO("s25fl256s1",  0x010219, 0x4d01,  64 << 10, 512, 0) },
+    { INFO("s25fl512s",   0x010220, 0x4d00, 256 << 10, 256, 0) },
+    { INFO("s70fl01gs",   0x010221, 0x4d00, 256 << 10, 256, 0) },
+    { INFO("s25sl12800",  0x012018, 0x0300, 256 << 10,  64, 0) },
+    { INFO("s25sl12801",  0x012018, 0x0301,  64 << 10, 256, 0) },
+    { INFO("s25fl129p0",  0x012018, 0x4d00, 256 << 10,  64, 0) },
+    { INFO("s25fl129p1",  0x012018, 0x4d01,  64 << 10, 256, 0) },
+    { INFO("s25fl016k",   0xef4015,      0,  64 << 10,  32, ER_4K | ER_32K) },
+    { INFO("s25fl064k",   0xef4017,      0,  64 << 10, 128, ER_4K | ER_32K) },
+
+    /* SST -- large erase sizes are "overlays", "sectors" are 4<< 10 */
+    { INFO("sst25vf040b", 0xbf258d,      0,  64 << 10,   8, ER_4K) },
+    { INFO("sst25vf080b", 0xbf258e,      0,  64 << 10,  16, ER_4K) },
+    { INFO("sst25vf016b", 0xbf2541,      0,  64 << 10,  32, ER_4K) },
+    { INFO("sst25vf032b", 0xbf254a,      0,  64 << 10,  64, ER_4K) },
+    { INFO("sst25wf512",  0xbf2501,      0,  64 << 10,   1, ER_4K) },
+    { INFO("sst25wf010",  0xbf2502,      0,  64 << 10,   2, ER_4K) },
+    { INFO("sst25wf020",  0xbf2503,      0,  64 << 10,   4, ER_4K) },
+    { INFO("sst25wf040",  0xbf2504,      0,  64 << 10,   8, ER_4K) },
+
+    /* ST Microelectronics -- newer production may have feature updates */
+    { INFO("m25p05",      0x202010,      0,  32 << 10,   2, 0) },
+    { INFO("m25p10",      0x202011,      0,  32 << 10,   4, 0) },
+    { INFO("m25p20",      0x202012,      0,  64 << 10,   4, 0) },
+    { INFO("m25p40",      0x202013,      0,  64 << 10,   8, 0) },
+    { INFO("m25p80",      0x202014,      0,  64 << 10,  16, 0) },
+    { INFO("m25p16",      0x202015,      0,  64 << 10,  32, 0) },
+    { INFO("m25p32",      0x202016,      0,  64 << 10,  64, 0) },
+    { INFO("m25p64",      0x202017,      0,  64 << 10, 128, 0) },
+    { INFO("m25p128",     0x202018,      0, 256 << 10,  64, 0) },
+
+    { INFO("m45pe10",     0x204011,      0,  64 << 10,   2, 0) },
+    { INFO("m45pe80",     0x204014,      0,  64 << 10,  16, 0) },
+    { INFO("m45pe16",     0x204015,      0,  64 << 10,  32, 0) },
+
+    { INFO("m25pe80",     0x208014,      0,  64 << 10,  16, 0) },
+    { INFO("m25pe16",     0x208015,      0,  64 << 10,  32, ER_4K) },
+
+    { INFO("m25px32",     0x207116,      0,  64 << 10,  64, ER_4K) },
+    { INFO("m25px32-s0",  0x207316,      0,  64 << 10,  64, ER_4K) },
+    { INFO("m25px32-s1",  0x206316,      0,  64 << 10,  64, ER_4K) },
+    { INFO("m25px64",     0x207117,      0,  64 << 10, 128, 0) },
+
+    /* Winbond -- w25x "blocks" are 64k, "sectors" are 4KiB */
+    { INFO("w25x10",      0xef3011,      0,  64 << 10,   2, ER_4K) },
+    { INFO("w25x20",      0xef3012,      0,  64 << 10,   4, ER_4K) },
+    { INFO("w25x40",      0xef3013,      0,  64 << 10,   8, ER_4K) },
+    { INFO("w25x80",      0xef3014,      0,  64 << 10,  16, ER_4K) },
+    { INFO("w25x16",      0xef3015,      0,  64 << 10,  32, ER_4K) },
+    { INFO("w25x32",      0xef3016,      0,  64 << 10,  64, ER_4K) },
+    { INFO("w25q32",      0xef4016,      0,  64 << 10,  64, ER_4K) },
+    { INFO("w25x64",      0xef3017,      0,  64 << 10, 128, ER_4K) },
+    { INFO("w25q64",      0xef4017,      0,  64 << 10, 128, ER_4K) },
+
+    /* Numonyx -- n25q128 */
+    { INFO("n25q128",      0x20ba18,      0,  64 << 10, 256, 0) },
+};
+
+typedef enum {
+    NOP = 0,
+    WRSR = 0x1,
+    WRDI = 0x4,
+    RDSR = 0x5,
+    WREN = 0x6,
+    JEDEC_READ = 0x9f,
+    BULK_ERASE = 0xc7,
+
+    READ = 0x3,
+    FAST_READ = 0xb,
+    DOR = 0x3b,
+    QOR = 0x6b,
+    DIOR = 0xbb,
+    QIOR = 0xeb,
+
+    PP = 0x2,
+    DPP = 0xa2,
+    QPP = 0x32,
+
+    ERASE_4K = 0x20,
+    ERASE_32K = 0x52,
+    ERASE_SECTOR = 0xd8,
+} FlashCMD;
+
+typedef enum {
+    STATE_IDLE,
+    STATE_PAGE_PROGRAM,
+    STATE_READ,
+    STATE_COLLECTING_DATA,
+    STATE_READING_DATA,
+} CMDState;
+
+typedef struct Flash {
+    SSISlave ssidev;
+    uint32_t r;
+
+    BlockDriverState *bdrv;
+
+    uint8_t *storage;
+    uint32_t size;
+    int page_size;
+
+    uint8_t state;
+    uint8_t data[16];
+    uint32_t len;
+    uint32_t pos;
+    uint8_t needed_bytes;
+    uint8_t cmd_in_progress;
+    uint64_t cur_addr;
+    bool write_enable;
+
+    int64_t dirty_page;
+
+    const FlashPartInfo *pi;
+
+} Flash;
+
+typedef struct M25P80Class {
+    SSISlaveClass parent_class;
+    FlashPartInfo *pi;
+} M25P80Class;
+
+#define TYPE_M25P80 "m25p80-generic"
+#define M25P80(obj) \
+     OBJECT_CHECK(Flash, (obj), TYPE_M25P80)
+#define M25P80_CLASS(klass) \
+     OBJECT_CLASS_CHECK(M25P80Class, (klass), TYPE_M25P80)
+#define M25P80_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(M25P80Class, (obj), TYPE_M25P80)
+
+static void bdrv_sync_complete(void *opaque, int ret)
+{
+    /* do nothing. Masters do not directly interact with the backing store,
+     * only the working copy so no mutexing required.
+     */
+}
+
+static void flash_sync_page(Flash *s, int page)
+{
+    if (s->bdrv) {
+        int bdrv_sector, nb_sectors;
+        QEMUIOVector iov;
+
+        bdrv_sector = (page * s->pi->page_size) / BDRV_SECTOR_SIZE;
+        nb_sectors = DIV_ROUND_UP(s->pi->page_size, BDRV_SECTOR_SIZE);
+        qemu_iovec_init(&iov, 1);
+        qemu_iovec_add(&iov, s->storage + bdrv_sector * BDRV_SECTOR_SIZE,
+                                                nb_sectors * BDRV_SECTOR_SIZE);
+        bdrv_aio_writev(s->bdrv, bdrv_sector, &iov, nb_sectors,
+                                                bdrv_sync_complete, NULL);
+    }
+}
+
+static inline void flash_sync_area(Flash *s, int64_t off, int64_t len)
+{
+    int64_t start, end, nb_sectors;
+    QEMUIOVector iov;
+
+    if (!s->bdrv) {
+        return;
+    }
+
+    assert(!(len % BDRV_SECTOR_SIZE));
+    start = off / BDRV_SECTOR_SIZE;
+    end = (off + len) / BDRV_SECTOR_SIZE;
+    nb_sectors = end - start;
+    qemu_iovec_init(&iov, 1);
+    qemu_iovec_add(&iov, s->storage + (start * BDRV_SECTOR_SIZE),
+                                        nb_sectors * BDRV_SECTOR_SIZE);
+    bdrv_aio_writev(s->bdrv, start, &iov, nb_sectors, bdrv_sync_complete, NULL);
+}
+
+static void flash_erase(Flash *s, int offset, FlashCMD cmd)
+{
+    uint32_t len;
+    uint8_t capa_to_assert = 0;
+
+    switch (cmd) {
+    case ERASE_4K:
+        len = 4 << 10;
+        capa_to_assert = ER_4K;
+        break;
+    case ERASE_32K:
+        len = 32 << 10;
+        capa_to_assert = ER_32K;
+        break;
+    case ERASE_SECTOR:
+        len = s->pi->sector_size;
+        break;
+    case BULK_ERASE:
+        len = s->size;
+        break;
+    default:
+        abort();
+    }
+
+    DB_PRINT("offset = %#x, len = %d\n", offset, len);
+    if ((s->pi->flags & capa_to_assert) != capa_to_assert) {
+        hw_error("m25p80: %dk erase size not supported by device\n", len);
+    }
+
+    if (!s->write_enable) {
+        DB_PRINT("erase with write protect!\n");
+        return;
+    }
+    memset(s->storage + offset, 0xff, len);
+    flash_sync_area(s, offset, len);
+}
+
+static inline void flash_sync_dirty(Flash *s, int64_t newpage)
+{
+    if (s->dirty_page >= 0 && s->dirty_page != newpage) {
+        flash_sync_page(s, s->dirty_page);
+        s->dirty_page = newpage;
+    }
+}
+
+static inline
+void flash_write8(Flash *s, uint64_t addr, uint8_t data)
+{
+    int64_t page = addr / s->pi->page_size;
+    uint8_t prev = s->storage[s->cur_addr];
+
+    if (!s->write_enable) {
+        DB_PRINT("write with write protect!\n");
+    }
+
+    if ((prev ^ data) & data) {
+        DB_PRINT("programming zero to one! addr=%lx  %x -> %x\n",
+                  addr, prev, data);
+    }
+
+    if (s->pi->flags & WR_1) {
+        s->storage[s->cur_addr] = data;
+    } else {
+        s->storage[s->cur_addr] &= data;
+    }
+
+    flash_sync_dirty(s, page);
+    s->dirty_page = page;
+}
+
+static void complete_collecting_data(Flash *s)
+{
+    s->cur_addr = s->data[0] << 16;
+    s->cur_addr |= s->data[1] << 8;
+    s->cur_addr |= s->data[2];
+
+    s->state = STATE_IDLE;
+
+    switch (s->cmd_in_progress) {
+    case DPP:
+    case QPP:
+    case PP:
+        s->state = STATE_PAGE_PROGRAM;
+        break;
+    case READ:
+    case FAST_READ:
+    case DOR:
+    case QOR:
+    case DIOR:
+    case QIOR:
+        s->state = STATE_READ;
+        break;
+    case ERASE_4K:
+    case ERASE_32K:
+    case ERASE_SECTOR:
+        flash_erase(s, s->cur_addr, s->cmd_in_progress);
+        break;
+    case WRSR:
+        if (s->write_enable) {
+            s->write_enable = false;
+        }
+        break;
+    default:
+        break;
+    }
+}
+
+static void decode_new_cmd(Flash *s, uint32_t value)
+{
+    s->cmd_in_progress = value;
+    DB_PRINT("decoded new command:%x\n", value);
+
+    switch (value) {
+
+    case ERASE_4K:
+    case ERASE_32K:
+    case ERASE_SECTOR:
+    case READ:
+    case DPP:
+    case QPP:
+    case PP:
+        s->needed_bytes = 3;
+        s->pos = 0;
+        s->len = 0;
+        s->state = STATE_COLLECTING_DATA;
+        break;
+
+    case FAST_READ:
+    case DOR:
+    case QOR:
+        s->needed_bytes = 4;
+        s->pos = 0;
+        s->len = 0;
+        s->state = STATE_COLLECTING_DATA;
+        break;
+
+    case DIOR:
+        switch ((s->pi->jedec >> 16) & 0xFF) {
+        case JEDEC_WINBOND:
+        case JEDEC_SPANSION:
+            s->needed_bytes = 4;
+            break;
+        case JEDEC_NUMONYX:
+        default:
+            s->needed_bytes = 5;
+        }
+        s->pos = 0;
+        s->len = 0;
+        s->state = STATE_COLLECTING_DATA;
+        break;
+
+    case QIOR:
+        switch ((s->pi->jedec >> 16) & 0xFF) {
+        case JEDEC_WINBOND:
+        case JEDEC_SPANSION:
+            s->needed_bytes = 6;
+            break;
+        case JEDEC_NUMONYX:
+        default:
+            s->needed_bytes = 8;
+        }
+        s->pos = 0;
+        s->len = 0;
+        s->state = STATE_COLLECTING_DATA;
+        break;
+
+    case WRSR:
+        if (s->write_enable) {
+            s->needed_bytes = 1;
+            s->pos = 0;
+            s->len = 0;
+            s->state = STATE_COLLECTING_DATA;
+        }
+        break;
+
+    case WRDI:
+        s->write_enable = false;
+        break;
+    case WREN:
+        s->write_enable = true;
+        break;
+
+    case RDSR:
+        s->data[0] = (!!s->write_enable) << 1;
+        s->pos = 0;
+        s->len = 1;
+        s->state = STATE_READING_DATA;
+        break;
+
+    case JEDEC_READ:
+        DB_PRINT("populated jedec code\n");
+        s->data[0] = (s->pi->jedec >> 16) & 0xff;
+        s->data[1] = (s->pi->jedec >> 8) & 0xff;
+        s->data[2] = s->pi->jedec & 0xff;
+        if (s->pi->ext_jedec) {
+            s->data[3] = (s->pi->ext_jedec >> 8) & 0xff;
+            s->data[4] = s->pi->ext_jedec & 0xff;
+            s->len = 5;
+        } else {
+            s->len = 3;
+        }
+        s->pos = 0;
+        s->state = STATE_READING_DATA;
+        break;
+
+    case BULK_ERASE:
+        if (s->write_enable) {
+            DB_PRINT("chip erase\n");
+            flash_erase(s, 0, BULK_ERASE);
+        } else {
+            DB_PRINT("chip erase with write protect!\n");
+        }
+        break;
+    case NOP:
+        break;
+    default:
+        DB_PRINT("Unknown cmd %x\n", value);
+        break;
+    }
+}
+
+static int m25p80_cs(SSISlave *ss, bool select)
+{
+    Flash *s = FROM_SSI_SLAVE(Flash, ss);
+
+    if (select) {
+        s->len = 0;
+        s->pos = 0;
+        s->state = STATE_IDLE;
+        flash_sync_dirty(s, -1);
+    }
+
+    DB_PRINT("%sselect\n", select ? "de" : "");
+
+    return 0;
+}
+
+static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
+{
+    Flash *s = FROM_SSI_SLAVE(Flash, ss);
+    uint32_t r = 0;
+
+    switch (s->state) {
+
+    case STATE_PAGE_PROGRAM:
+        DB_PRINT("page program cur_addr=%lx data=%x\n", s->cur_addr,
+                 (uint8_t)tx);
+        flash_write8(s, s->cur_addr, (uint8_t)tx);
+        s->cur_addr++;
+        break;
+
+    case STATE_READ:
+        r = s->storage[s->cur_addr];
+        DB_PRINT("READ 0x%lx=%x\n", s->cur_addr, r);
+        s->cur_addr = (s->cur_addr + 1) % s->size;
+        break;
+
+    case STATE_COLLECTING_DATA:
+        s->data[s->len] = (uint8_t)tx;
+        s->len++;
+
+        if (s->len == s->needed_bytes) {
+            complete_collecting_data(s);
+        }
+        break;
+
+    case STATE_READING_DATA:
+        r = s->data[s->pos];
+        s->pos++;
+        if (s->pos == s->len) {
+            s->pos = 0;
+            s->state = STATE_IDLE;
+        }
+        break;
+
+    default:
+    case STATE_IDLE:
+        decode_new_cmd(s, (uint8_t)tx);
+        break;
+    }
+
+    return r;
+}
+
+static int m25p80_init(SSISlave *ss)
+{
+    DriveInfo *dinfo;
+    Flash *s = FROM_SSI_SLAVE(Flash, ss);
+    M25P80Class *mc = M25P80_GET_CLASS(s);
+
+    s->pi = mc->pi;
+
+    s->size = s->pi->sector_size * s->pi->n_sectors;
+    s->dirty_page = -1;
+    s->storage = qemu_blockalign(s->bdrv, s->size);
+
+    dinfo = drive_get_next(IF_MTD);
+
+    if (dinfo && dinfo->bdrv) {
+        DB_PRINT("Binding to IF_MTD drive\n");
+        s->bdrv = dinfo->bdrv;
+        /* FIXME: Move to late init */
+        if (bdrv_read(s->bdrv, 0, s->storage, DIV_ROUND_UP(s->size,
+                                                    BDRV_SECTOR_SIZE))) {
+            fprintf(stderr, "Failed to initialize SPI flash!\n");
+            return 1;
+        }
+    } else {
+        memset(s->storage, 0xFF, s->size);
+    }
+
+    return 0;
+}
+
+static void m25p80_pre_save(void *opaque)
+{
+    flash_sync_dirty((Flash *)opaque, -1);
+}
+
+static const VMStateDescription vmstate_m25p80 = {
+    .name = "xilinx_spi",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_save = m25p80_pre_save,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(state, Flash),
+        VMSTATE_UINT8_ARRAY(data, Flash, 16),
+        VMSTATE_UINT32(len, Flash),
+        VMSTATE_UINT32(pos, Flash),
+        VMSTATE_UINT8(needed_bytes, Flash),
+        VMSTATE_UINT8(cmd_in_progress, Flash),
+        VMSTATE_UINT64(cur_addr, Flash),
+        VMSTATE_BOOL(write_enable, Flash),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void m25p80_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
+    M25P80Class *mc = M25P80_CLASS(klass);
+
+    k->init = m25p80_init;
+    k->transfer = m25p80_transfer8;
+    k->set_cs = m25p80_cs;
+    k->cs_polarity = SSI_CS_LOW;
+    dc->vmsd = &vmstate_m25p80;
+    mc->pi = data;
+}
+
+static const TypeInfo m25p80_info = {
+    .name           = TYPE_M25P80,
+    .parent         = TYPE_SSI_SLAVE,
+    .instance_size  = sizeof(Flash),
+    .class_size     = sizeof(M25P80Class),
+    .abstract       = true,
+};
+
+static void m25p80_register_types(void)
+{
+    int i;
+
+    type_register_static(&m25p80_info);
+    for (i = 0; i < ARRAY_SIZE(known_devices); ++i) {
+        TypeInfo ti = {
+            .name       = known_devices[i].part_name,
+            .parent     = TYPE_M25P80,
+            .class_init = m25p80_class_init,
+            .class_data = (void *)&known_devices[i],
+        };
+        type_register(&ti);
+    }
+}
+
+type_init(m25p80_register_types)
diff --git a/hw/block/nand.c b/hw/block/nand.c
new file mode 100644 (file)
index 0000000..087ca14
--- /dev/null
@@ -0,0 +1,791 @@
+/*
+ * Flash NAND memory emulation.  Based on "16M x 8 Bit NAND Flash
+ * Memory" datasheet for the KM29U128AT / K9F2808U0A chips from
+ * Samsung Electronic.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * Support for additional features based on "MT29F2G16ABCWP 2Gx16"
+ * datasheet from Micron Technology and "NAND02G-B2C" datasheet
+ * from ST Microelectronics.
+ *
+ * This code is licensed under the GNU GPL v2.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#ifndef NAND_IO
+
+# include "hw/hw.h"
+# include "hw/block/flash.h"
+# include "sysemu/blockdev.h"
+# include "hw/sysbus.h"
+#include "qemu/error-report.h"
+
+# define NAND_CMD_READ0                0x00
+# define NAND_CMD_READ1                0x01
+# define NAND_CMD_READ2                0x50
+# define NAND_CMD_LPREAD2      0x30
+# define NAND_CMD_NOSERIALREAD2        0x35
+# define NAND_CMD_RANDOMREAD1  0x05
+# define NAND_CMD_RANDOMREAD2  0xe0
+# define NAND_CMD_READID       0x90
+# define NAND_CMD_RESET                0xff
+# define NAND_CMD_PAGEPROGRAM1 0x80
+# define NAND_CMD_PAGEPROGRAM2 0x10
+# define NAND_CMD_CACHEPROGRAM2        0x15
+# define NAND_CMD_BLOCKERASE1  0x60
+# define NAND_CMD_BLOCKERASE2  0xd0
+# define NAND_CMD_READSTATUS   0x70
+# define NAND_CMD_COPYBACKPRG1 0x85
+
+# define NAND_IOSTATUS_ERROR   (1 << 0)
+# define NAND_IOSTATUS_PLANE0  (1 << 1)
+# define NAND_IOSTATUS_PLANE1  (1 << 2)
+# define NAND_IOSTATUS_PLANE2  (1 << 3)
+# define NAND_IOSTATUS_PLANE3  (1 << 4)
+# define NAND_IOSTATUS_READY    (1 << 6)
+# define NAND_IOSTATUS_UNPROTCT        (1 << 7)
+
+# define MAX_PAGE              0x800
+# define MAX_OOB               0x40
+
+typedef struct NANDFlashState NANDFlashState;
+struct NANDFlashState {
+    SysBusDevice busdev;
+    uint8_t manf_id, chip_id;
+    uint8_t buswidth; /* in BYTES */
+    int size, pages;
+    int page_shift, oob_shift, erase_shift, addr_shift;
+    uint8_t *storage;
+    BlockDriverState *bdrv;
+    int mem_oob;
+
+    uint8_t cle, ale, ce, wp, gnd;
+
+    uint8_t io[MAX_PAGE + MAX_OOB + 0x400];
+    uint8_t *ioaddr;
+    int iolen;
+
+    uint32_t cmd;
+    uint64_t addr;
+    int addrlen;
+    int status;
+    int offset;
+
+    void (*blk_write)(NANDFlashState *s);
+    void (*blk_erase)(NANDFlashState *s);
+    void (*blk_load)(NANDFlashState *s, uint64_t addr, int offset);
+
+    uint32_t ioaddr_vmstate;
+};
+
+static void mem_and(uint8_t *dest, const uint8_t *src, size_t n)
+{
+    /* Like memcpy() but we logical-AND the data into the destination */
+    int i;
+    for (i = 0; i < n; i++) {
+        dest[i] &= src[i];
+    }
+}
+
+# define NAND_NO_AUTOINCR      0x00000001
+# define NAND_BUSWIDTH_16      0x00000002
+# define NAND_NO_PADDING       0x00000004
+# define NAND_CACHEPRG         0x00000008
+# define NAND_COPYBACK         0x00000010
+# define NAND_IS_AND           0x00000020
+# define NAND_4PAGE_ARRAY      0x00000040
+# define NAND_NO_READRDY       0x00000100
+# define NAND_SAMSUNG_LP       (NAND_NO_PADDING | NAND_COPYBACK)
+
+# define NAND_IO
+
+# define PAGE(addr)            ((addr) >> ADDR_SHIFT)
+# define PAGE_START(page)      (PAGE(page) * (PAGE_SIZE + OOB_SIZE))
+# define PAGE_MASK             ((1 << ADDR_SHIFT) - 1)
+# define OOB_SHIFT             (PAGE_SHIFT - 5)
+# define OOB_SIZE              (1 << OOB_SHIFT)
+# define SECTOR(addr)          ((addr) >> (9 + ADDR_SHIFT - PAGE_SHIFT))
+# define SECTOR_OFFSET(addr)   ((addr) & ((511 >> PAGE_SHIFT) << 8))
+
+# define PAGE_SIZE             256
+# define PAGE_SHIFT            8
+# define PAGE_SECTORS          1
+# define ADDR_SHIFT            8
+# include "nand.c"
+# define PAGE_SIZE             512
+# define PAGE_SHIFT            9
+# define PAGE_SECTORS          1
+# define ADDR_SHIFT            8
+# include "nand.c"
+# define PAGE_SIZE             2048
+# define PAGE_SHIFT            11
+# define PAGE_SECTORS          4
+# define ADDR_SHIFT            16
+# include "nand.c"
+
+/* Information based on Linux drivers/mtd/nand/nand_ids.c */
+static const struct {
+    int size;
+    int width;
+    int page_shift;
+    int erase_shift;
+    uint32_t options;
+} nand_flash_ids[0x100] = {
+    [0 ... 0xff] = { 0 },
+
+    [0x6e] = { 1,      8,      8, 4, 0 },
+    [0x64] = { 2,      8,      8, 4, 0 },
+    [0x6b] = { 4,      8,      9, 4, 0 },
+    [0xe8] = { 1,      8,      8, 4, 0 },
+    [0xec] = { 1,      8,      8, 4, 0 },
+    [0xea] = { 2,      8,      8, 4, 0 },
+    [0xd5] = { 4,      8,      9, 4, 0 },
+    [0xe3] = { 4,      8,      9, 4, 0 },
+    [0xe5] = { 4,      8,      9, 4, 0 },
+    [0xd6] = { 8,      8,      9, 4, 0 },
+
+    [0x39] = { 8,      8,      9, 4, 0 },
+    [0xe6] = { 8,      8,      9, 4, 0 },
+    [0x49] = { 8,      16,     9, 4, NAND_BUSWIDTH_16 },
+    [0x59] = { 8,      16,     9, 4, NAND_BUSWIDTH_16 },
+
+    [0x33] = { 16,     8,      9, 5, 0 },
+    [0x73] = { 16,     8,      9, 5, 0 },
+    [0x43] = { 16,     16,     9, 5, NAND_BUSWIDTH_16 },
+    [0x53] = { 16,     16,     9, 5, NAND_BUSWIDTH_16 },
+
+    [0x35] = { 32,     8,      9, 5, 0 },
+    [0x75] = { 32,     8,      9, 5, 0 },
+    [0x45] = { 32,     16,     9, 5, NAND_BUSWIDTH_16 },
+    [0x55] = { 32,     16,     9, 5, NAND_BUSWIDTH_16 },
+
+    [0x36] = { 64,     8,      9, 5, 0 },
+    [0x76] = { 64,     8,      9, 5, 0 },
+    [0x46] = { 64,     16,     9, 5, NAND_BUSWIDTH_16 },
+    [0x56] = { 64,     16,     9, 5, NAND_BUSWIDTH_16 },
+
+    [0x78] = { 128,    8,      9, 5, 0 },
+    [0x39] = { 128,    8,      9, 5, 0 },
+    [0x79] = { 128,    8,      9, 5, 0 },
+    [0x72] = { 128,    16,     9, 5, NAND_BUSWIDTH_16 },
+    [0x49] = { 128,    16,     9, 5, NAND_BUSWIDTH_16 },
+    [0x74] = { 128,    16,     9, 5, NAND_BUSWIDTH_16 },
+    [0x59] = { 128,    16,     9, 5, NAND_BUSWIDTH_16 },
+
+    [0x71] = { 256,    8,      9, 5, 0 },
+
+    /*
+     * These are the new chips with large page size. The pagesize and the
+     * erasesize is determined from the extended id bytes
+     */
+# define LP_OPTIONS    (NAND_SAMSUNG_LP | NAND_NO_READRDY | NAND_NO_AUTOINCR)
+# define LP_OPTIONS16  (LP_OPTIONS | NAND_BUSWIDTH_16)
+
+    /* 512 Megabit */
+    [0xa2] = { 64,     8,      0, 0, LP_OPTIONS },
+    [0xf2] = { 64,     8,      0, 0, LP_OPTIONS },
+    [0xb2] = { 64,     16,     0, 0, LP_OPTIONS16 },
+    [0xc2] = { 64,     16,     0, 0, LP_OPTIONS16 },
+
+    /* 1 Gigabit */
+    [0xa1] = { 128,    8,      0, 0, LP_OPTIONS },
+    [0xf1] = { 128,    8,      0, 0, LP_OPTIONS },
+    [0xb1] = { 128,    16,     0, 0, LP_OPTIONS16 },
+    [0xc1] = { 128,    16,     0, 0, LP_OPTIONS16 },
+
+    /* 2 Gigabit */
+    [0xaa] = { 256,    8,      0, 0, LP_OPTIONS },
+    [0xda] = { 256,    8,      0, 0, LP_OPTIONS },
+    [0xba] = { 256,    16,     0, 0, LP_OPTIONS16 },
+    [0xca] = { 256,    16,     0, 0, LP_OPTIONS16 },
+
+    /* 4 Gigabit */
+    [0xac] = { 512,    8,      0, 0, LP_OPTIONS },
+    [0xdc] = { 512,    8,      0, 0, LP_OPTIONS },
+    [0xbc] = { 512,    16,     0, 0, LP_OPTIONS16 },
+    [0xcc] = { 512,    16,     0, 0, LP_OPTIONS16 },
+
+    /* 8 Gigabit */
+    [0xa3] = { 1024,   8,      0, 0, LP_OPTIONS },
+    [0xd3] = { 1024,   8,      0, 0, LP_OPTIONS },
+    [0xb3] = { 1024,   16,     0, 0, LP_OPTIONS16 },
+    [0xc3] = { 1024,   16,     0, 0, LP_OPTIONS16 },
+
+    /* 16 Gigabit */
+    [0xa5] = { 2048,   8,      0, 0, LP_OPTIONS },
+    [0xd5] = { 2048,   8,      0, 0, LP_OPTIONS },
+    [0xb5] = { 2048,   16,     0, 0, LP_OPTIONS16 },
+    [0xc5] = { 2048,   16,     0, 0, LP_OPTIONS16 },
+};
+
+static void nand_reset(DeviceState *dev)
+{
+    NANDFlashState *s = FROM_SYSBUS(NANDFlashState, SYS_BUS_DEVICE(dev));
+    s->cmd = NAND_CMD_READ0;
+    s->addr = 0;
+    s->addrlen = 0;
+    s->iolen = 0;
+    s->offset = 0;
+    s->status &= NAND_IOSTATUS_UNPROTCT;
+    s->status |= NAND_IOSTATUS_READY;
+}
+
+static inline void nand_pushio_byte(NANDFlashState *s, uint8_t value)
+{
+    s->ioaddr[s->iolen++] = value;
+    for (value = s->buswidth; --value;) {
+        s->ioaddr[s->iolen++] = 0;
+    }
+}
+
+static void nand_command(NANDFlashState *s)
+{
+    unsigned int offset;
+    switch (s->cmd) {
+    case NAND_CMD_READ0:
+        s->iolen = 0;
+        break;
+
+    case NAND_CMD_READID:
+        s->ioaddr = s->io;
+        s->iolen = 0;
+        nand_pushio_byte(s, s->manf_id);
+        nand_pushio_byte(s, s->chip_id);
+        nand_pushio_byte(s, 'Q'); /* Don't-care byte (often 0xa5) */
+        if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
+            /* Page Size, Block Size, Spare Size; bit 6 indicates
+             * 8 vs 16 bit width NAND.
+             */
+            nand_pushio_byte(s, (s->buswidth == 2) ? 0x55 : 0x15);
+        } else {
+            nand_pushio_byte(s, 0xc0); /* Multi-plane */
+        }
+        break;
+
+    case NAND_CMD_RANDOMREAD2:
+    case NAND_CMD_NOSERIALREAD2:
+        if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP))
+            break;
+        offset = s->addr & ((1 << s->addr_shift) - 1);
+        s->blk_load(s, s->addr, offset);
+        if (s->gnd)
+            s->iolen = (1 << s->page_shift) - offset;
+        else
+            s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset;
+        break;
+
+    case NAND_CMD_RESET:
+        nand_reset(&s->busdev.qdev);
+        break;
+
+    case NAND_CMD_PAGEPROGRAM1:
+        s->ioaddr = s->io;
+        s->iolen = 0;
+        break;
+
+    case NAND_CMD_PAGEPROGRAM2:
+        if (s->wp) {
+            s->blk_write(s);
+        }
+        break;
+
+    case NAND_CMD_BLOCKERASE1:
+        break;
+
+    case NAND_CMD_BLOCKERASE2:
+        s->addr &= (1ull << s->addrlen * 8) - 1;
+        if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)
+            s->addr <<= 16;
+        else
+            s->addr <<= 8;
+
+        if (s->wp) {
+            s->blk_erase(s);
+        }
+        break;
+
+    case NAND_CMD_READSTATUS:
+        s->ioaddr = s->io;
+        s->iolen = 0;
+        nand_pushio_byte(s, s->status);
+        break;
+
+    default:
+        printf("%s: Unknown NAND command 0x%02x\n", __FUNCTION__, s->cmd);
+    }
+}
+
+static void nand_pre_save(void *opaque)
+{
+    NANDFlashState *s = opaque;
+
+    s->ioaddr_vmstate = s->ioaddr - s->io;
+}
+
+static int nand_post_load(void *opaque, int version_id)
+{
+    NANDFlashState *s = opaque;
+
+    if (s->ioaddr_vmstate > sizeof(s->io)) {
+        return -EINVAL;
+    }
+    s->ioaddr = s->io + s->ioaddr_vmstate;
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_nand = {
+    .name = "nand",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_save = nand_pre_save,
+    .post_load = nand_post_load,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(cle, NANDFlashState),
+        VMSTATE_UINT8(ale, NANDFlashState),
+        VMSTATE_UINT8(ce, NANDFlashState),
+        VMSTATE_UINT8(wp, NANDFlashState),
+        VMSTATE_UINT8(gnd, NANDFlashState),
+        VMSTATE_BUFFER(io, NANDFlashState),
+        VMSTATE_UINT32(ioaddr_vmstate, NANDFlashState),
+        VMSTATE_INT32(iolen, NANDFlashState),
+        VMSTATE_UINT32(cmd, NANDFlashState),
+        VMSTATE_UINT64(addr, NANDFlashState),
+        VMSTATE_INT32(addrlen, NANDFlashState),
+        VMSTATE_INT32(status, NANDFlashState),
+        VMSTATE_INT32(offset, NANDFlashState),
+        /* XXX: do we want to save s->storage too? */
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int nand_device_init(SysBusDevice *dev)
+{
+    int pagesize;
+    NANDFlashState *s = FROM_SYSBUS(NANDFlashState, dev);
+
+    s->buswidth = nand_flash_ids[s->chip_id].width >> 3;
+    s->size = nand_flash_ids[s->chip_id].size << 20;
+    if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
+        s->page_shift = 11;
+        s->erase_shift = 6;
+    } else {
+        s->page_shift = nand_flash_ids[s->chip_id].page_shift;
+        s->erase_shift = nand_flash_ids[s->chip_id].erase_shift;
+    }
+
+    switch (1 << s->page_shift) {
+    case 256:
+        nand_init_256(s);
+        break;
+    case 512:
+        nand_init_512(s);
+        break;
+    case 2048:
+        nand_init_2048(s);
+        break;
+    default:
+        error_report("Unsupported NAND block size");
+        return -1;
+    }
+
+    pagesize = 1 << s->oob_shift;
+    s->mem_oob = 1;
+    if (s->bdrv) {
+        if (bdrv_is_read_only(s->bdrv)) {
+            error_report("Can't use a read-only drive");
+            return -1;
+        }
+        if (bdrv_getlength(s->bdrv) >=
+                (s->pages << s->page_shift) + (s->pages << s->oob_shift)) {
+            pagesize = 0;
+            s->mem_oob = 0;
+        }
+    } else {
+        pagesize += 1 << s->page_shift;
+    }
+    if (pagesize) {
+        s->storage = (uint8_t *) memset(g_malloc(s->pages * pagesize),
+                        0xff, s->pages * pagesize);
+    }
+    /* Give s->ioaddr a sane value in case we save state before it is used. */
+    s->ioaddr = s->io;
+
+    return 0;
+}
+
+static Property nand_properties[] = {
+    DEFINE_PROP_UINT8("manufacturer_id", NANDFlashState, manf_id, 0),
+    DEFINE_PROP_UINT8("chip_id", NANDFlashState, chip_id, 0),
+    DEFINE_PROP_DRIVE("drive", NANDFlashState, bdrv),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void nand_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = nand_device_init;
+    dc->reset = nand_reset;
+    dc->vmsd = &vmstate_nand;
+    dc->props = nand_properties;
+}
+
+static const TypeInfo nand_info = {
+    .name          = "nand",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(NANDFlashState),
+    .class_init    = nand_class_init,
+};
+
+static void nand_register_types(void)
+{
+    type_register_static(&nand_info);
+}
+
+/*
+ * Chip inputs are CLE, ALE, CE, WP, GND and eight I/O pins.  Chip
+ * outputs are R/B and eight I/O pins.
+ *
+ * CE, WP and R/B are active low.
+ */
+void nand_setpins(DeviceState *dev, uint8_t cle, uint8_t ale,
+                  uint8_t ce, uint8_t wp, uint8_t gnd)
+{
+    NANDFlashState *s = (NANDFlashState *) dev;
+    s->cle = cle;
+    s->ale = ale;
+    s->ce = ce;
+    s->wp = wp;
+    s->gnd = gnd;
+    if (wp)
+        s->status |= NAND_IOSTATUS_UNPROTCT;
+    else
+        s->status &= ~NAND_IOSTATUS_UNPROTCT;
+}
+
+void nand_getpins(DeviceState *dev, int *rb)
+{
+    *rb = 1;
+}
+
+void nand_setio(DeviceState *dev, uint32_t value)
+{
+    int i;
+    NANDFlashState *s = (NANDFlashState *) dev;
+    if (!s->ce && s->cle) {
+        if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
+            if (s->cmd == NAND_CMD_READ0 && value == NAND_CMD_LPREAD2)
+                return;
+            if (value == NAND_CMD_RANDOMREAD1) {
+                s->addr &= ~((1 << s->addr_shift) - 1);
+                s->addrlen = 0;
+                return;
+            }
+        }
+        if (value == NAND_CMD_READ0)
+            s->offset = 0;
+       else if (value == NAND_CMD_READ1) {
+            s->offset = 0x100;
+            value = NAND_CMD_READ0;
+        }
+       else if (value == NAND_CMD_READ2) {
+            s->offset = 1 << s->page_shift;
+            value = NAND_CMD_READ0;
+        }
+
+        s->cmd = value;
+
+        if (s->cmd == NAND_CMD_READSTATUS ||
+                s->cmd == NAND_CMD_PAGEPROGRAM2 ||
+                s->cmd == NAND_CMD_BLOCKERASE1 ||
+                s->cmd == NAND_CMD_BLOCKERASE2 ||
+                s->cmd == NAND_CMD_NOSERIALREAD2 ||
+                s->cmd == NAND_CMD_RANDOMREAD2 ||
+                s->cmd == NAND_CMD_RESET)
+            nand_command(s);
+
+        if (s->cmd != NAND_CMD_RANDOMREAD2) {
+            s->addrlen = 0;
+        }
+    }
+
+    if (s->ale) {
+        unsigned int shift = s->addrlen * 8;
+        unsigned int mask = ~(0xff << shift);
+        unsigned int v = value << shift;
+
+        s->addr = (s->addr & mask) | v;
+        s->addrlen ++;
+
+        switch (s->addrlen) {
+        case 1:
+            if (s->cmd == NAND_CMD_READID) {
+                nand_command(s);
+            }
+            break;
+        case 2: /* fix cache address as a byte address */
+            s->addr <<= (s->buswidth - 1);
+            break;
+        case 3:
+            if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
+                    (s->cmd == NAND_CMD_READ0 ||
+                     s->cmd == NAND_CMD_PAGEPROGRAM1)) {
+                nand_command(s);
+            }
+            break;
+        case 4:
+            if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
+                    nand_flash_ids[s->chip_id].size < 256 && /* 1Gb or less */
+                    (s->cmd == NAND_CMD_READ0 ||
+                     s->cmd == NAND_CMD_PAGEPROGRAM1)) {
+                nand_command(s);
+            }
+            break;
+        case 5:
+            if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
+                    nand_flash_ids[s->chip_id].size >= 256 && /* 2Gb or more */
+                    (s->cmd == NAND_CMD_READ0 ||
+                     s->cmd == NAND_CMD_PAGEPROGRAM1)) {
+                nand_command(s);
+            }
+            break;
+        default:
+            break;
+        }
+    }
+
+    if (!s->cle && !s->ale && s->cmd == NAND_CMD_PAGEPROGRAM1) {
+        if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift)) {
+            for (i = s->buswidth; i--; value >>= 8) {
+                s->io[s->iolen ++] = (uint8_t) (value & 0xff);
+            }
+        }
+    } else if (!s->cle && !s->ale && s->cmd == NAND_CMD_COPYBACKPRG1) {
+        if ((s->addr & ((1 << s->addr_shift) - 1)) <
+                (1 << s->page_shift) + (1 << s->oob_shift)) {
+            for (i = s->buswidth; i--; s->addr++, value >>= 8) {
+                s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] =
+                    (uint8_t) (value & 0xff);
+            }
+        }
+    }
+}
+
+uint32_t nand_getio(DeviceState *dev)
+{
+    int offset;
+    uint32_t x = 0;
+    NANDFlashState *s = (NANDFlashState *) dev;
+
+    /* Allow sequential reading */
+    if (!s->iolen && s->cmd == NAND_CMD_READ0) {
+        offset = (int) (s->addr & ((1 << s->addr_shift) - 1)) + s->offset;
+        s->offset = 0;
+
+        s->blk_load(s, s->addr, offset);
+        if (s->gnd)
+            s->iolen = (1 << s->page_shift) - offset;
+        else
+            s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset;
+    }
+
+    if (s->ce || s->iolen <= 0)
+        return 0;
+
+    for (offset = s->buswidth; offset--;) {
+        x |= s->ioaddr[offset] << (offset << 3);
+    }
+    /* after receiving READ STATUS command all subsequent reads will
+     * return the status register value until another command is issued
+     */
+    if (s->cmd != NAND_CMD_READSTATUS) {
+        s->addr   += s->buswidth;
+        s->ioaddr += s->buswidth;
+        s->iolen  -= s->buswidth;
+    }
+    return x;
+}
+
+uint32_t nand_getbuswidth(DeviceState *dev)
+{
+    NANDFlashState *s = (NANDFlashState *) dev;
+    return s->buswidth << 3;
+}
+
+DeviceState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id)
+{
+    DeviceState *dev;
+
+    if (nand_flash_ids[chip_id].size == 0) {
+        hw_error("%s: Unsupported NAND chip ID.\n", __FUNCTION__);
+    }
+    dev = qdev_create(NULL, "nand");
+    qdev_prop_set_uint8(dev, "manufacturer_id", manf_id);
+    qdev_prop_set_uint8(dev, "chip_id", chip_id);
+    if (bdrv) {
+        qdev_prop_set_drive_nofail(dev, "drive", bdrv);
+    }
+
+    qdev_init_nofail(dev);
+    return dev;
+}
+
+type_init(nand_register_types)
+
+#else
+
+/* Program a single page */
+static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s)
+{
+    uint64_t off, page, sector, soff;
+    uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200];
+    if (PAGE(s->addr) >= s->pages)
+        return;
+
+    if (!s->bdrv) {
+        mem_and(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) +
+                        s->offset, s->io, s->iolen);
+    } else if (s->mem_oob) {
+        sector = SECTOR(s->addr);
+        off = (s->addr & PAGE_MASK) + s->offset;
+        soff = SECTOR_OFFSET(s->addr);
+        if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS) < 0) {
+            printf("%s: read error in sector %" PRIu64 "\n", __func__, sector);
+            return;
+        }
+
+        mem_and(iobuf + (soff | off), s->io, MIN(s->iolen, PAGE_SIZE - off));
+        if (off + s->iolen > PAGE_SIZE) {
+            page = PAGE(s->addr);
+            mem_and(s->storage + (page << OOB_SHIFT), s->io + PAGE_SIZE - off,
+                            MIN(OOB_SIZE, off + s->iolen - PAGE_SIZE));
+        }
+
+        if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS) < 0) {
+            printf("%s: write error in sector %" PRIu64 "\n", __func__, sector);
+        }
+    } else {
+        off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset;
+        sector = off >> 9;
+        soff = off & 0x1ff;
+        if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) < 0) {
+            printf("%s: read error in sector %" PRIu64 "\n", __func__, sector);
+            return;
+        }
+
+        mem_and(iobuf + soff, s->io, s->iolen);
+
+        if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) < 0) {
+            printf("%s: write error in sector %" PRIu64 "\n", __func__, sector);
+        }
+    }
+    s->offset = 0;
+}
+
+/* Erase a single block */
+static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s)
+{
+    uint64_t i, page, addr;
+    uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, };
+    addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1);
+
+    if (PAGE(addr) >= s->pages)
+        return;
+
+    if (!s->bdrv) {
+        memset(s->storage + PAGE_START(addr),
+                        0xff, (PAGE_SIZE + OOB_SIZE) << s->erase_shift);
+    } else if (s->mem_oob) {
+        memset(s->storage + (PAGE(addr) << OOB_SHIFT),
+                        0xff, OOB_SIZE << s->erase_shift);
+        i = SECTOR(addr);
+        page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift));
+        for (; i < page; i ++)
+            if (bdrv_write(s->bdrv, i, iobuf, 1) < 0) {
+                printf("%s: write error in sector %" PRIu64 "\n", __func__, i);
+            }
+    } else {
+        addr = PAGE_START(addr);
+        page = addr >> 9;
+        if (bdrv_read(s->bdrv, page, iobuf, 1) < 0) {
+            printf("%s: read error in sector %" PRIu64 "\n", __func__, page);
+        }
+        memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1);
+        if (bdrv_write(s->bdrv, page, iobuf, 1) < 0) {
+            printf("%s: write error in sector %" PRIu64 "\n", __func__, page);
+        }
+
+        memset(iobuf, 0xff, 0x200);
+        i = (addr & ~0x1ff) + 0x200;
+        for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200;
+                        i < addr; i += 0x200)
+            if (bdrv_write(s->bdrv, i >> 9, iobuf, 1) < 0) {
+                printf("%s: write error in sector %" PRIu64 "\n",
+                       __func__, i >> 9);
+            }
+
+        page = i >> 9;
+        if (bdrv_read(s->bdrv, page, iobuf, 1) < 0) {
+            printf("%s: read error in sector %" PRIu64 "\n", __func__, page);
+        }
+        memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1);
+        if (bdrv_write(s->bdrv, page, iobuf, 1) < 0) {
+            printf("%s: write error in sector %" PRIu64 "\n", __func__, page);
+        }
+    }
+}
+
+static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s,
+                uint64_t addr, int offset)
+{
+    if (PAGE(addr) >= s->pages)
+        return;
+
+    if (s->bdrv) {
+        if (s->mem_oob) {
+            if (bdrv_read(s->bdrv, SECTOR(addr), s->io, PAGE_SECTORS) < 0) {
+                printf("%s: read error in sector %" PRIu64 "\n",
+                                __func__, SECTOR(addr));
+            }
+            memcpy(s->io + SECTOR_OFFSET(s->addr) + PAGE_SIZE,
+                            s->storage + (PAGE(s->addr) << OOB_SHIFT),
+                            OOB_SIZE);
+            s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset;
+        } else {
+            if (bdrv_read(s->bdrv, PAGE_START(addr) >> 9,
+                                    s->io, (PAGE_SECTORS + 2)) < 0) {
+                printf("%s: read error in sector %" PRIu64 "\n",
+                                __func__, PAGE_START(addr) >> 9);
+            }
+            s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset;
+        }
+    } else {
+        memcpy(s->io, s->storage + PAGE_START(s->addr) +
+                        offset, PAGE_SIZE + OOB_SIZE - offset);
+        s->ioaddr = s->io;
+    }
+}
+
+static void glue(nand_init_, PAGE_SIZE)(NANDFlashState *s)
+{
+    s->oob_shift = PAGE_SHIFT - 5;
+    s->pages = s->size >> PAGE_SHIFT;
+    s->addr_shift = ADDR_SHIFT;
+
+    s->blk_erase = glue(nand_blk_erase_, PAGE_SIZE);
+    s->blk_write = glue(nand_blk_write_, PAGE_SIZE);
+    s->blk_load = glue(nand_blk_load_, PAGE_SIZE);
+}
+
+# undef PAGE_SIZE
+# undef PAGE_SHIFT
+# undef PAGE_SECTORS
+# undef ADDR_SHIFT
+#endif /* NAND_IO */
diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c
new file mode 100644 (file)
index 0000000..3ff20e0
--- /dev/null
@@ -0,0 +1,769 @@
+/*
+ *  CFI parallel flash with Intel command set emulation
+ *
+ *  Copyright (c) 2006 Thorsten Zitterell
+ *  Copyright (c) 2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * For now, this code can emulate flashes of 1, 2 or 4 bytes width.
+ * Supported commands/modes are:
+ * - flash read
+ * - flash write
+ * - flash ID read
+ * - sector erase
+ * - CFI queries
+ *
+ * It does not support timings
+ * It does not support flash interleaving
+ * It does not implement software data protection as found in many real chips
+ * It does not implement erase suspend/resume commands
+ * It does not implement multiple sectors erase
+ *
+ * It does not implement much more ...
+ */
+
+#include "hw/hw.h"
+#include "hw/block/flash.h"
+#include "block/block.h"
+#include "qemu/timer.h"
+#include "exec/address-spaces.h"
+#include "qemu/host-utils.h"
+#include "hw/sysbus.h"
+
+#define PFLASH_BUG(fmt, ...) \
+do { \
+    fprintf(stderr, "PFLASH: Possible BUG - " fmt, ## __VA_ARGS__); \
+    exit(1); \
+} while(0)
+
+/* #define PFLASH_DEBUG */
+#ifdef PFLASH_DEBUG
+#define DPRINTF(fmt, ...)                                   \
+do {                                                        \
+    fprintf(stderr, "PFLASH: " fmt , ## __VA_ARGS__);       \
+} while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+struct pflash_t {
+    SysBusDevice busdev;
+    BlockDriverState *bs;
+    uint32_t nb_blocs;
+    uint64_t sector_len;
+    uint8_t width;
+    uint8_t be;
+    uint8_t wcycle; /* if 0, the flash is read normally */
+    int ro;
+    uint8_t cmd;
+    uint8_t status;
+    uint16_t ident0;
+    uint16_t ident1;
+    uint16_t ident2;
+    uint16_t ident3;
+    uint8_t cfi_len;
+    uint8_t cfi_table[0x52];
+    uint64_t counter;
+    unsigned int writeblock_size;
+    QEMUTimer *timer;
+    MemoryRegion mem;
+    char *name;
+    void *storage;
+};
+
+static const VMStateDescription vmstate_pflash = {
+    .name = "pflash_cfi01",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(wcycle, pflash_t),
+        VMSTATE_UINT8(cmd, pflash_t),
+        VMSTATE_UINT8(status, pflash_t),
+        VMSTATE_UINT64(counter, pflash_t),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void pflash_timer (void *opaque)
+{
+    pflash_t *pfl = opaque;
+
+    DPRINTF("%s: command %02x done\n", __func__, pfl->cmd);
+    /* Reset flash */
+    pfl->status ^= 0x80;
+    memory_region_rom_device_set_readable(&pfl->mem, true);
+    pfl->wcycle = 0;
+    pfl->cmd = 0;
+}
+
+static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
+                             int width, int be)
+{
+    hwaddr boff;
+    uint32_t ret;
+    uint8_t *p;
+
+    ret = -1;
+    boff = offset & 0xFF; /* why this here ?? */
+
+    if (pfl->width == 2)
+        boff = boff >> 1;
+    else if (pfl->width == 4)
+        boff = boff >> 2;
+
+#if 0
+    DPRINTF("%s: reading offset " TARGET_FMT_plx " under cmd %02x width %d\n",
+            __func__, offset, pfl->cmd, width);
+#endif
+    switch (pfl->cmd) {
+    default:
+        /* This should never happen : reset state & treat it as a read */
+        DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd);
+        pfl->wcycle = 0;
+        pfl->cmd = 0;
+        /* fall through to read code */
+    case 0x00:
+        /* Flash area read */
+        p = pfl->storage;
+        switch (width) {
+        case 1:
+            ret = p[offset];
+            DPRINTF("%s: data offset " TARGET_FMT_plx " %02x\n",
+                    __func__, offset, ret);
+            break;
+        case 2:
+            if (be) {
+                ret = p[offset] << 8;
+                ret |= p[offset + 1];
+            } else {
+                ret = p[offset];
+                ret |= p[offset + 1] << 8;
+            }
+            DPRINTF("%s: data offset " TARGET_FMT_plx " %04x\n",
+                    __func__, offset, ret);
+            break;
+        case 4:
+            if (be) {
+                ret = p[offset] << 24;
+                ret |= p[offset + 1] << 16;
+                ret |= p[offset + 2] << 8;
+                ret |= p[offset + 3];
+            } else {
+                ret = p[offset];
+                ret |= p[offset + 1] << 8;
+                ret |= p[offset + 2] << 16;
+                ret |= p[offset + 3] << 24;
+            }
+            DPRINTF("%s: data offset " TARGET_FMT_plx " %08x\n",
+                    __func__, offset, ret);
+            break;
+        default:
+            DPRINTF("BUG in %s\n", __func__);
+        }
+
+        break;
+    case 0x10: /* Single byte program */
+    case 0x20: /* Block erase */
+    case 0x28: /* Block erase */
+    case 0x40: /* single byte program */
+    case 0x50: /* Clear status register */
+    case 0x60: /* Block /un)lock */
+    case 0x70: /* Status Register */
+    case 0xe8: /* Write block */
+        /* Status register read */
+        ret = pfl->status;
+        DPRINTF("%s: status %x\n", __func__, ret);
+        break;
+    case 0x90:
+        switch (boff) {
+        case 0:
+            ret = pfl->ident0 << 8 | pfl->ident1;
+            DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret);
+            break;
+        case 1:
+            ret = pfl->ident2 << 8 | pfl->ident3;
+            DPRINTF("%s: Device ID Code %04x\n", __func__, ret);
+            break;
+        default:
+            DPRINTF("%s: Read Device Information boff=%x\n", __func__,
+                    (unsigned)boff);
+            ret = 0;
+            break;
+        }
+        break;
+    case 0x98: /* Query mode */
+        if (boff > pfl->cfi_len)
+            ret = 0;
+        else
+            ret = pfl->cfi_table[boff];
+        break;
+    }
+    return ret;
+}
+
+/* update flash content on disk */
+static void pflash_update(pflash_t *pfl, int offset,
+                          int size)
+{
+    int offset_end;
+    if (pfl->bs) {
+        offset_end = offset + size;
+        /* round to sectors */
+        offset = offset >> 9;
+        offset_end = (offset_end + 511) >> 9;
+        bdrv_write(pfl->bs, offset, pfl->storage + (offset << 9),
+                   offset_end - offset);
+    }
+}
+
+static inline void pflash_data_write(pflash_t *pfl, hwaddr offset,
+                                     uint32_t value, int width, int be)
+{
+    uint8_t *p = pfl->storage;
+
+    DPRINTF("%s: block write offset " TARGET_FMT_plx
+            " value %x counter %016" PRIx64 "\n",
+            __func__, offset, value, pfl->counter);
+    switch (width) {
+    case 1:
+        p[offset] = value;
+        break;
+    case 2:
+        if (be) {
+            p[offset] = value >> 8;
+            p[offset + 1] = value;
+        } else {
+            p[offset] = value;
+            p[offset + 1] = value >> 8;
+        }
+        break;
+    case 4:
+        if (be) {
+            p[offset] = value >> 24;
+            p[offset + 1] = value >> 16;
+            p[offset + 2] = value >> 8;
+            p[offset + 3] = value;
+        } else {
+            p[offset] = value;
+            p[offset + 1] = value >> 8;
+            p[offset + 2] = value >> 16;
+            p[offset + 3] = value >> 24;
+        }
+        break;
+    }
+
+}
+
+static void pflash_write(pflash_t *pfl, hwaddr offset,
+                         uint32_t value, int width, int be)
+{
+    uint8_t *p;
+    uint8_t cmd;
+
+    cmd = value;
+
+    DPRINTF("%s: writing offset " TARGET_FMT_plx " value %08x width %d wcycle 0x%x\n",
+            __func__, offset, value, width, pfl->wcycle);
+
+    if (!pfl->wcycle) {
+        /* Set the device in I/O access mode */
+        memory_region_rom_device_set_readable(&pfl->mem, false);
+    }
+
+    switch (pfl->wcycle) {
+    case 0:
+        /* read mode */
+        switch (cmd) {
+        case 0x00: /* ??? */
+            goto reset_flash;
+        case 0x10: /* Single Byte Program */
+        case 0x40: /* Single Byte Program */
+            DPRINTF("%s: Single Byte Program\n", __func__);
+            break;
+        case 0x20: /* Block erase */
+            p = pfl->storage;
+            offset &= ~(pfl->sector_len - 1);
+
+            DPRINTF("%s: block erase at " TARGET_FMT_plx " bytes %x\n",
+                    __func__, offset, (unsigned)pfl->sector_len);
+
+            if (!pfl->ro) {
+                memset(p + offset, 0xff, pfl->sector_len);
+                pflash_update(pfl, offset, pfl->sector_len);
+            } else {
+                pfl->status |= 0x20; /* Block erase error */
+            }
+            pfl->status |= 0x80; /* Ready! */
+            break;
+        case 0x50: /* Clear status bits */
+            DPRINTF("%s: Clear status bits\n", __func__);
+            pfl->status = 0x0;
+            goto reset_flash;
+        case 0x60: /* Block (un)lock */
+            DPRINTF("%s: Block unlock\n", __func__);
+            break;
+        case 0x70: /* Status Register */
+            DPRINTF("%s: Read status register\n", __func__);
+            pfl->cmd = cmd;
+            return;
+        case 0x90: /* Read Device ID */
+            DPRINTF("%s: Read Device information\n", __func__);
+            pfl->cmd = cmd;
+            return;
+        case 0x98: /* CFI query */
+            DPRINTF("%s: CFI query\n", __func__);
+            break;
+        case 0xe8: /* Write to buffer */
+            DPRINTF("%s: Write to buffer\n", __func__);
+            pfl->status |= 0x80; /* Ready! */
+            break;
+        case 0xf0: /* Probe for AMD flash */
+            DPRINTF("%s: Probe for AMD flash\n", __func__);
+            goto reset_flash;
+        case 0xff: /* Read array mode */
+            DPRINTF("%s: Read array mode\n", __func__);
+            goto reset_flash;
+        default:
+            goto error_flash;
+        }
+        pfl->wcycle++;
+        pfl->cmd = cmd;
+        break;
+    case 1:
+        switch (pfl->cmd) {
+        case 0x10: /* Single Byte Program */
+        case 0x40: /* Single Byte Program */
+            DPRINTF("%s: Single Byte Program\n", __func__);
+            if (!pfl->ro) {
+                pflash_data_write(pfl, offset, value, width, be);
+                pflash_update(pfl, offset, width);
+            } else {
+                pfl->status |= 0x10; /* Programming error */
+            }
+            pfl->status |= 0x80; /* Ready! */
+            pfl->wcycle = 0;
+        break;
+        case 0x20: /* Block erase */
+        case 0x28:
+            if (cmd == 0xd0) { /* confirm */
+                pfl->wcycle = 0;
+                pfl->status |= 0x80;
+            } else if (cmd == 0xff) { /* read array mode */
+                goto reset_flash;
+            } else
+                goto error_flash;
+
+            break;
+        case 0xe8:
+            DPRINTF("%s: block write of %x bytes\n", __func__, value);
+            pfl->counter = value;
+            pfl->wcycle++;
+            break;
+        case 0x60:
+            if (cmd == 0xd0) {
+                pfl->wcycle = 0;
+                pfl->status |= 0x80;
+            } else if (cmd == 0x01) {
+                pfl->wcycle = 0;
+                pfl->status |= 0x80;
+            } else if (cmd == 0xff) {
+                goto reset_flash;
+            } else {
+                DPRINTF("%s: Unknown (un)locking command\n", __func__);
+                goto reset_flash;
+            }
+            break;
+        case 0x98:
+            if (cmd == 0xff) {
+                goto reset_flash;
+            } else {
+                DPRINTF("%s: leaving query mode\n", __func__);
+            }
+            break;
+        default:
+            goto error_flash;
+        }
+        break;
+    case 2:
+        switch (pfl->cmd) {
+        case 0xe8: /* Block write */
+            if (!pfl->ro) {
+                pflash_data_write(pfl, offset, value, width, be);
+            } else {
+                pfl->status |= 0x10; /* Programming error */
+            }
+
+            pfl->status |= 0x80;
+
+            if (!pfl->counter) {
+                hwaddr mask = pfl->writeblock_size - 1;
+                mask = ~mask;
+
+                DPRINTF("%s: block write finished\n", __func__);
+                pfl->wcycle++;
+                if (!pfl->ro) {
+                    /* Flush the entire write buffer onto backing storage.  */
+                    pflash_update(pfl, offset & mask, pfl->writeblock_size);
+                } else {
+                    pfl->status |= 0x10; /* Programming error */
+                }
+            }
+
+            pfl->counter--;
+            break;
+        default:
+            goto error_flash;
+        }
+        break;
+    case 3: /* Confirm mode */
+        switch (pfl->cmd) {
+        case 0xe8: /* Block write */
+            if (cmd == 0xd0) {
+                pfl->wcycle = 0;
+                pfl->status |= 0x80;
+            } else {
+                DPRINTF("%s: unknown command for \"write block\"\n", __func__);
+                PFLASH_BUG("Write block confirm");
+                goto reset_flash;
+            }
+            break;
+        default:
+            goto error_flash;
+        }
+        break;
+    default:
+        /* Should never happen */
+        DPRINTF("%s: invalid write state\n",  __func__);
+        goto reset_flash;
+    }
+    return;
+
+ error_flash:
+    qemu_log_mask(LOG_UNIMP, "%s: Unimplemented flash cmd sequence "
+                  "(offset " TARGET_FMT_plx ", wcycle 0x%x cmd 0x%x value 0x%x)"
+                  "\n", __func__, offset, pfl->wcycle, pfl->cmd, value);
+
+ reset_flash:
+    memory_region_rom_device_set_readable(&pfl->mem, true);
+
+    pfl->wcycle = 0;
+    pfl->cmd = 0;
+}
+
+
+static uint32_t pflash_readb_be(void *opaque, hwaddr addr)
+{
+    return pflash_read(opaque, addr, 1, 1);
+}
+
+static uint32_t pflash_readb_le(void *opaque, hwaddr addr)
+{
+    return pflash_read(opaque, addr, 1, 0);
+}
+
+static uint32_t pflash_readw_be(void *opaque, hwaddr addr)
+{
+    pflash_t *pfl = opaque;
+
+    return pflash_read(pfl, addr, 2, 1);
+}
+
+static uint32_t pflash_readw_le(void *opaque, hwaddr addr)
+{
+    pflash_t *pfl = opaque;
+
+    return pflash_read(pfl, addr, 2, 0);
+}
+
+static uint32_t pflash_readl_be(void *opaque, hwaddr addr)
+{
+    pflash_t *pfl = opaque;
+
+    return pflash_read(pfl, addr, 4, 1);
+}
+
+static uint32_t pflash_readl_le(void *opaque, hwaddr addr)
+{
+    pflash_t *pfl = opaque;
+
+    return pflash_read(pfl, addr, 4, 0);
+}
+
+static void pflash_writeb_be(void *opaque, hwaddr addr,
+                             uint32_t value)
+{
+    pflash_write(opaque, addr, value, 1, 1);
+}
+
+static void pflash_writeb_le(void *opaque, hwaddr addr,
+                             uint32_t value)
+{
+    pflash_write(opaque, addr, value, 1, 0);
+}
+
+static void pflash_writew_be(void *opaque, hwaddr addr,
+                             uint32_t value)
+{
+    pflash_t *pfl = opaque;
+
+    pflash_write(pfl, addr, value, 2, 1);
+}
+
+static void pflash_writew_le(void *opaque, hwaddr addr,
+                             uint32_t value)
+{
+    pflash_t *pfl = opaque;
+
+    pflash_write(pfl, addr, value, 2, 0);
+}
+
+static void pflash_writel_be(void *opaque, hwaddr addr,
+                             uint32_t value)
+{
+    pflash_t *pfl = opaque;
+
+    pflash_write(pfl, addr, value, 4, 1);
+}
+
+static void pflash_writel_le(void *opaque, hwaddr addr,
+                             uint32_t value)
+{
+    pflash_t *pfl = opaque;
+
+    pflash_write(pfl, addr, value, 4, 0);
+}
+
+static const MemoryRegionOps pflash_cfi01_ops_be = {
+    .old_mmio = {
+        .read = { pflash_readb_be, pflash_readw_be, pflash_readl_be, },
+        .write = { pflash_writeb_be, pflash_writew_be, pflash_writel_be, },
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const MemoryRegionOps pflash_cfi01_ops_le = {
+    .old_mmio = {
+        .read = { pflash_readb_le, pflash_readw_le, pflash_readl_le, },
+        .write = { pflash_writeb_le, pflash_writew_le, pflash_writel_le, },
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int pflash_cfi01_init(SysBusDevice *dev)
+{
+    pflash_t *pfl = FROM_SYSBUS(typeof(*pfl), dev);
+    uint64_t total_len;
+    int ret;
+
+    total_len = pfl->sector_len * pfl->nb_blocs;
+
+    /* XXX: to be fixed */
+#if 0
+    if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) &&
+        total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024))
+        return NULL;
+#endif
+
+    memory_region_init_rom_device(
+        &pfl->mem, pfl->be ? &pflash_cfi01_ops_be : &pflash_cfi01_ops_le, pfl,
+        pfl->name, total_len);
+    vmstate_register_ram(&pfl->mem, DEVICE(pfl));
+    pfl->storage = memory_region_get_ram_ptr(&pfl->mem);
+    sysbus_init_mmio(dev, &pfl->mem);
+
+    if (pfl->bs) {
+        /* read the initial flash content */
+        ret = bdrv_read(pfl->bs, 0, pfl->storage, total_len >> 9);
+
+        if (ret < 0) {
+            vmstate_unregister_ram(&pfl->mem, DEVICE(pfl));
+            memory_region_destroy(&pfl->mem);
+            return 1;
+        }
+    }
+
+    if (pfl->bs) {
+        pfl->ro = bdrv_is_read_only(pfl->bs);
+    } else {
+        pfl->ro = 0;
+    }
+
+    pfl->timer = qemu_new_timer_ns(vm_clock, pflash_timer, pfl);
+    pfl->wcycle = 0;
+    pfl->cmd = 0;
+    pfl->status = 0;
+    /* Hardcoded CFI table */
+    pfl->cfi_len = 0x52;
+    /* Standard "QRY" string */
+    pfl->cfi_table[0x10] = 'Q';
+    pfl->cfi_table[0x11] = 'R';
+    pfl->cfi_table[0x12] = 'Y';
+    /* Command set (Intel) */
+    pfl->cfi_table[0x13] = 0x01;
+    pfl->cfi_table[0x14] = 0x00;
+    /* Primary extended table address (none) */
+    pfl->cfi_table[0x15] = 0x31;
+    pfl->cfi_table[0x16] = 0x00;
+    /* Alternate command set (none) */
+    pfl->cfi_table[0x17] = 0x00;
+    pfl->cfi_table[0x18] = 0x00;
+    /* Alternate extended table (none) */
+    pfl->cfi_table[0x19] = 0x00;
+    pfl->cfi_table[0x1A] = 0x00;
+    /* Vcc min */
+    pfl->cfi_table[0x1B] = 0x45;
+    /* Vcc max */
+    pfl->cfi_table[0x1C] = 0x55;
+    /* Vpp min (no Vpp pin) */
+    pfl->cfi_table[0x1D] = 0x00;
+    /* Vpp max (no Vpp pin) */
+    pfl->cfi_table[0x1E] = 0x00;
+    /* Reserved */
+    pfl->cfi_table[0x1F] = 0x07;
+    /* Timeout for min size buffer write */
+    pfl->cfi_table[0x20] = 0x07;
+    /* Typical timeout for block erase */
+    pfl->cfi_table[0x21] = 0x0a;
+    /* Typical timeout for full chip erase (4096 ms) */
+    pfl->cfi_table[0x22] = 0x00;
+    /* Reserved */
+    pfl->cfi_table[0x23] = 0x04;
+    /* Max timeout for buffer write */
+    pfl->cfi_table[0x24] = 0x04;
+    /* Max timeout for block erase */
+    pfl->cfi_table[0x25] = 0x04;
+    /* Max timeout for chip erase */
+    pfl->cfi_table[0x26] = 0x00;
+    /* Device size */
+    pfl->cfi_table[0x27] = ctz32(total_len); // + 1;
+    /* Flash device interface (8 & 16 bits) */
+    pfl->cfi_table[0x28] = 0x02;
+    pfl->cfi_table[0x29] = 0x00;
+    /* Max number of bytes in multi-bytes write */
+    if (pfl->width == 1) {
+        pfl->cfi_table[0x2A] = 0x08;
+    } else {
+        pfl->cfi_table[0x2A] = 0x0B;
+    }
+    pfl->writeblock_size = 1 << pfl->cfi_table[0x2A];
+
+    pfl->cfi_table[0x2B] = 0x00;
+    /* Number of erase block regions (uniform) */
+    pfl->cfi_table[0x2C] = 0x01;
+    /* Erase block region 1 */
+    pfl->cfi_table[0x2D] = pfl->nb_blocs - 1;
+    pfl->cfi_table[0x2E] = (pfl->nb_blocs - 1) >> 8;
+    pfl->cfi_table[0x2F] = pfl->sector_len >> 8;
+    pfl->cfi_table[0x30] = pfl->sector_len >> 16;
+
+    /* Extended */
+    pfl->cfi_table[0x31] = 'P';
+    pfl->cfi_table[0x32] = 'R';
+    pfl->cfi_table[0x33] = 'I';
+
+    pfl->cfi_table[0x34] = '1';
+    pfl->cfi_table[0x35] = '0';
+
+    pfl->cfi_table[0x36] = 0x00;
+    pfl->cfi_table[0x37] = 0x00;
+    pfl->cfi_table[0x38] = 0x00;
+    pfl->cfi_table[0x39] = 0x00;
+
+    pfl->cfi_table[0x3a] = 0x00;
+
+    pfl->cfi_table[0x3b] = 0x00;
+    pfl->cfi_table[0x3c] = 0x00;
+
+    pfl->cfi_table[0x3f] = 0x01; /* Number of protection fields */
+
+    return 0;
+}
+
+static Property pflash_cfi01_properties[] = {
+    DEFINE_PROP_DRIVE("drive", struct pflash_t, bs),
+    DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0),
+    DEFINE_PROP_UINT64("sector-length", struct pflash_t, sector_len, 0),
+    DEFINE_PROP_UINT8("width", struct pflash_t, width, 0),
+    DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0),
+    DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0),
+    DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0),
+    DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0),
+    DEFINE_PROP_UINT16("id3", struct pflash_t, ident3, 0),
+    DEFINE_PROP_STRING("name", struct pflash_t, name),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pflash_cfi01_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = pflash_cfi01_init;
+    dc->props = pflash_cfi01_properties;
+    dc->vmsd = &vmstate_pflash;
+}
+
+
+static const TypeInfo pflash_cfi01_info = {
+    .name           = "cfi.pflash01",
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(struct pflash_t),
+    .class_init     = pflash_cfi01_class_init,
+};
+
+static void pflash_cfi01_register_types(void)
+{
+    type_register_static(&pflash_cfi01_info);
+}
+
+type_init(pflash_cfi01_register_types)
+
+pflash_t *pflash_cfi01_register(hwaddr base,
+                                DeviceState *qdev, const char *name,
+                                hwaddr size,
+                                BlockDriverState *bs,
+                                uint32_t sector_len, int nb_blocs, int width,
+                                uint16_t id0, uint16_t id1,
+                                uint16_t id2, uint16_t id3, int be)
+{
+    DeviceState *dev = qdev_create(NULL, "cfi.pflash01");
+    SysBusDevice *busdev = SYS_BUS_DEVICE(dev);
+    pflash_t *pfl = (pflash_t *)object_dynamic_cast(OBJECT(dev),
+                                                    "cfi.pflash01");
+
+    if (bs && qdev_prop_set_drive(dev, "drive", bs)) {
+        abort();
+    }
+    qdev_prop_set_uint32(dev, "num-blocks", nb_blocs);
+    qdev_prop_set_uint64(dev, "sector-length", sector_len);
+    qdev_prop_set_uint8(dev, "width", width);
+    qdev_prop_set_uint8(dev, "big-endian", !!be);
+    qdev_prop_set_uint16(dev, "id0", id0);
+    qdev_prop_set_uint16(dev, "id1", id1);
+    qdev_prop_set_uint16(dev, "id2", id2);
+    qdev_prop_set_uint16(dev, "id3", id3);
+    qdev_prop_set_string(dev, "name", name);
+    qdev_init_nofail(dev);
+
+    sysbus_mmio_map(busdev, 0, base);
+    return pfl;
+}
+
+MemoryRegion *pflash_cfi01_get_memory(pflash_t *fl)
+{
+    return &fl->mem;
+}
diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c
new file mode 100644 (file)
index 0000000..9a7fa70
--- /dev/null
@@ -0,0 +1,787 @@
+/*
+ *  CFI parallel flash with AMD command set emulation
+ *
+ *  Copyright (c) 2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * For now, this code can emulate flashes of 1, 2 or 4 bytes width.
+ * Supported commands/modes are:
+ * - flash read
+ * - flash write
+ * - flash ID read
+ * - sector erase
+ * - chip erase
+ * - unlock bypass command
+ * - CFI queries
+ *
+ * It does not support flash interleaving.
+ * It does not implement boot blocs with reduced size
+ * It does not implement software data protection as found in many real chips
+ * It does not implement erase suspend/resume commands
+ * It does not implement multiple sectors erase
+ */
+
+#include "hw/hw.h"
+#include "hw/block/flash.h"
+#include "qemu/timer.h"
+#include "block/block.h"
+#include "exec/address-spaces.h"
+#include "qemu/host-utils.h"
+#include "hw/sysbus.h"
+
+//#define PFLASH_DEBUG
+#ifdef PFLASH_DEBUG
+#define DPRINTF(fmt, ...)                                  \
+do {                                                       \
+    fprintf(stderr "PFLASH: " fmt , ## __VA_ARGS__);       \
+} while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+#define PFLASH_LAZY_ROMD_THRESHOLD 42
+
+struct pflash_t {
+    SysBusDevice busdev;
+    BlockDriverState *bs;
+    uint32_t sector_len;
+    uint32_t nb_blocs;
+    uint32_t chip_len;
+    uint8_t mappings;
+    uint8_t width;
+    uint8_t be;
+    int wcycle; /* if 0, the flash is read normally */
+    int bypass;
+    int ro;
+    uint8_t cmd;
+    uint8_t status;
+    /* FIXME: implement array device properties */
+    uint16_t ident0;
+    uint16_t ident1;
+    uint16_t ident2;
+    uint16_t ident3;
+    uint16_t unlock_addr0;
+    uint16_t unlock_addr1;
+    uint8_t cfi_len;
+    uint8_t cfi_table[0x52];
+    QEMUTimer *timer;
+    /* The device replicates the flash memory across its memory space.  Emulate
+     * that by having a container (.mem) filled with an array of aliases
+     * (.mem_mappings) pointing to the flash memory (.orig_mem).
+     */
+    MemoryRegion mem;
+    MemoryRegion *mem_mappings;    /* array; one per mapping */
+    MemoryRegion orig_mem;
+    int rom_mode;
+    int read_counter; /* used for lazy switch-back to rom mode */
+    char *name;
+    void *storage;
+};
+
+/*
+ * Set up replicated mappings of the same region.
+ */
+static void pflash_setup_mappings(pflash_t *pfl)
+{
+    unsigned i;
+    hwaddr size = memory_region_size(&pfl->orig_mem);
+
+    memory_region_init(&pfl->mem, "pflash", pfl->mappings * size);
+    pfl->mem_mappings = g_new(MemoryRegion, pfl->mappings);
+    for (i = 0; i < pfl->mappings; ++i) {
+        memory_region_init_alias(&pfl->mem_mappings[i], "pflash-alias",
+                                 &pfl->orig_mem, 0, size);
+        memory_region_add_subregion(&pfl->mem, i * size, &pfl->mem_mappings[i]);
+    }
+}
+
+static void pflash_register_memory(pflash_t *pfl, int rom_mode)
+{
+    memory_region_rom_device_set_readable(&pfl->orig_mem, rom_mode);
+    pfl->rom_mode = rom_mode;
+}
+
+static void pflash_timer (void *opaque)
+{
+    pflash_t *pfl = opaque;
+
+    DPRINTF("%s: command %02x done\n", __func__, pfl->cmd);
+    /* Reset flash */
+    pfl->status ^= 0x80;
+    if (pfl->bypass) {
+        pfl->wcycle = 2;
+    } else {
+        pflash_register_memory(pfl, 1);
+        pfl->wcycle = 0;
+    }
+    pfl->cmd = 0;
+}
+
+static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
+                             int width, int be)
+{
+    hwaddr boff;
+    uint32_t ret;
+    uint8_t *p;
+
+    DPRINTF("%s: offset " TARGET_FMT_plx "\n", __func__, offset);
+    ret = -1;
+    /* Lazy reset to ROMD mode after a certain amount of read accesses */
+    if (!pfl->rom_mode && pfl->wcycle == 0 &&
+        ++pfl->read_counter > PFLASH_LAZY_ROMD_THRESHOLD) {
+        pflash_register_memory(pfl, 1);
+    }
+    offset &= pfl->chip_len - 1;
+    boff = offset & 0xFF;
+    if (pfl->width == 2)
+        boff = boff >> 1;
+    else if (pfl->width == 4)
+        boff = boff >> 2;
+    switch (pfl->cmd) {
+    default:
+        /* This should never happen : reset state & treat it as a read*/
+        DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd);
+        pfl->wcycle = 0;
+        pfl->cmd = 0;
+        /* fall through to the read code */
+    case 0x80:
+        /* We accept reads during second unlock sequence... */
+    case 0x00:
+    flash_read:
+        /* Flash area read */
+        p = pfl->storage;
+        switch (width) {
+        case 1:
+            ret = p[offset];
+//            DPRINTF("%s: data offset %08x %02x\n", __func__, offset, ret);
+            break;
+        case 2:
+            if (be) {
+                ret = p[offset] << 8;
+                ret |= p[offset + 1];
+            } else {
+                ret = p[offset];
+                ret |= p[offset + 1] << 8;
+            }
+//            DPRINTF("%s: data offset %08x %04x\n", __func__, offset, ret);
+            break;
+        case 4:
+            if (be) {
+                ret = p[offset] << 24;
+                ret |= p[offset + 1] << 16;
+                ret |= p[offset + 2] << 8;
+                ret |= p[offset + 3];
+            } else {
+                ret = p[offset];
+                ret |= p[offset + 1] << 8;
+                ret |= p[offset + 2] << 16;
+                ret |= p[offset + 3] << 24;
+            }
+//            DPRINTF("%s: data offset %08x %08x\n", __func__, offset, ret);
+            break;
+        }
+        break;
+    case 0x90:
+        /* flash ID read */
+        switch (boff) {
+        case 0x00:
+        case 0x01:
+            ret = boff & 0x01 ? pfl->ident1 : pfl->ident0;
+            break;
+        case 0x02:
+            ret = 0x00; /* Pretend all sectors are unprotected */
+            break;
+        case 0x0E:
+        case 0x0F:
+            ret = boff & 0x01 ? pfl->ident3 : pfl->ident2;
+            if (ret == (uint8_t)-1) {
+                goto flash_read;
+            }
+            break;
+        default:
+            goto flash_read;
+        }
+        DPRINTF("%s: ID " TARGET_FMT_plx " %x\n", __func__, boff, ret);
+        break;
+    case 0xA0:
+    case 0x10:
+    case 0x30:
+        /* Status register read */
+        ret = pfl->status;
+        DPRINTF("%s: status %x\n", __func__, ret);
+        /* Toggle bit 6 */
+        pfl->status ^= 0x40;
+        break;
+    case 0x98:
+        /* CFI query mode */
+        if (boff > pfl->cfi_len)
+            ret = 0;
+        else
+            ret = pfl->cfi_table[boff];
+        break;
+    }
+
+    return ret;
+}
+
+/* update flash content on disk */
+static void pflash_update(pflash_t *pfl, int offset,
+                          int size)
+{
+    int offset_end;
+    if (pfl->bs) {
+        offset_end = offset + size;
+        /* round to sectors */
+        offset = offset >> 9;
+        offset_end = (offset_end + 511) >> 9;
+        bdrv_write(pfl->bs, offset, pfl->storage + (offset << 9),
+                   offset_end - offset);
+    }
+}
+
+static void pflash_write (pflash_t *pfl, hwaddr offset,
+                          uint32_t value, int width, int be)
+{
+    hwaddr boff;
+    uint8_t *p;
+    uint8_t cmd;
+
+    cmd = value;
+    if (pfl->cmd != 0xA0 && cmd == 0xF0) {
+#if 0
+        DPRINTF("%s: flash reset asked (%02x %02x)\n",
+                __func__, pfl->cmd, cmd);
+#endif
+        goto reset_flash;
+    }
+    DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d %d\n", __func__,
+            offset, value, width, pfl->wcycle);
+    offset &= pfl->chip_len - 1;
+
+    DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d\n", __func__,
+            offset, value, width);
+    boff = offset & (pfl->sector_len - 1);
+    if (pfl->width == 2)
+        boff = boff >> 1;
+    else if (pfl->width == 4)
+        boff = boff >> 2;
+    switch (pfl->wcycle) {
+    case 0:
+        /* Set the device in I/O access mode if required */
+        if (pfl->rom_mode)
+            pflash_register_memory(pfl, 0);
+        pfl->read_counter = 0;
+        /* We're in read mode */
+    check_unlock0:
+        if (boff == 0x55 && cmd == 0x98) {
+        enter_CFI_mode:
+            /* Enter CFI query mode */
+            pfl->wcycle = 7;
+            pfl->cmd = 0x98;
+            return;
+        }
+        if (boff != pfl->unlock_addr0 || cmd != 0xAA) {
+            DPRINTF("%s: unlock0 failed " TARGET_FMT_plx " %02x %04x\n",
+                    __func__, boff, cmd, pfl->unlock_addr0);
+            goto reset_flash;
+        }
+        DPRINTF("%s: unlock sequence started\n", __func__);
+        break;
+    case 1:
+        /* We started an unlock sequence */
+    check_unlock1:
+        if (boff != pfl->unlock_addr1 || cmd != 0x55) {
+            DPRINTF("%s: unlock1 failed " TARGET_FMT_plx " %02x\n", __func__,
+                    boff, cmd);
+            goto reset_flash;
+        }
+        DPRINTF("%s: unlock sequence done\n", __func__);
+        break;
+    case 2:
+        /* We finished an unlock sequence */
+        if (!pfl->bypass && boff != pfl->unlock_addr0) {
+            DPRINTF("%s: command failed " TARGET_FMT_plx " %02x\n", __func__,
+                    boff, cmd);
+            goto reset_flash;
+        }
+        switch (cmd) {
+        case 0x20:
+            pfl->bypass = 1;
+            goto do_bypass;
+        case 0x80:
+        case 0x90:
+        case 0xA0:
+            pfl->cmd = cmd;
+            DPRINTF("%s: starting command %02x\n", __func__, cmd);
+            break;
+        default:
+            DPRINTF("%s: unknown command %02x\n", __func__, cmd);
+            goto reset_flash;
+        }
+        break;
+    case 3:
+        switch (pfl->cmd) {
+        case 0x80:
+            /* We need another unlock sequence */
+            goto check_unlock0;
+        case 0xA0:
+            DPRINTF("%s: write data offset " TARGET_FMT_plx " %08x %d\n",
+                    __func__, offset, value, width);
+            p = pfl->storage;
+            if (!pfl->ro) {
+                switch (width) {
+                case 1:
+                    p[offset] &= value;
+                    pflash_update(pfl, offset, 1);
+                    break;
+                case 2:
+                    if (be) {
+                        p[offset] &= value >> 8;
+                        p[offset + 1] &= value;
+                    } else {
+                        p[offset] &= value;
+                        p[offset + 1] &= value >> 8;
+                    }
+                    pflash_update(pfl, offset, 2);
+                    break;
+                case 4:
+                    if (be) {
+                        p[offset] &= value >> 24;
+                        p[offset + 1] &= value >> 16;
+                        p[offset + 2] &= value >> 8;
+                        p[offset + 3] &= value;
+                    } else {
+                        p[offset] &= value;
+                        p[offset + 1] &= value >> 8;
+                        p[offset + 2] &= value >> 16;
+                        p[offset + 3] &= value >> 24;
+                    }
+                    pflash_update(pfl, offset, 4);
+                    break;
+                }
+            }
+            pfl->status = 0x00 | ~(value & 0x80);
+            /* Let's pretend write is immediate */
+            if (pfl->bypass)
+                goto do_bypass;
+            goto reset_flash;
+        case 0x90:
+            if (pfl->bypass && cmd == 0x00) {
+                /* Unlock bypass reset */
+                goto reset_flash;
+            }
+            /* We can enter CFI query mode from autoselect mode */
+            if (boff == 0x55 && cmd == 0x98)
+                goto enter_CFI_mode;
+            /* No break here */
+        default:
+            DPRINTF("%s: invalid write for command %02x\n",
+                    __func__, pfl->cmd);
+            goto reset_flash;
+        }
+    case 4:
+        switch (pfl->cmd) {
+        case 0xA0:
+            /* Ignore writes while flash data write is occurring */
+            /* As we suppose write is immediate, this should never happen */
+            return;
+        case 0x80:
+            goto check_unlock1;
+        default:
+            /* Should never happen */
+            DPRINTF("%s: invalid command state %02x (wc 4)\n",
+                    __func__, pfl->cmd);
+            goto reset_flash;
+        }
+        break;
+    case 5:
+        switch (cmd) {
+        case 0x10:
+            if (boff != pfl->unlock_addr0) {
+                DPRINTF("%s: chip erase: invalid address " TARGET_FMT_plx "\n",
+                        __func__, offset);
+                goto reset_flash;
+            }
+            /* Chip erase */
+            DPRINTF("%s: start chip erase\n", __func__);
+            if (!pfl->ro) {
+                memset(pfl->storage, 0xFF, pfl->chip_len);
+                pflash_update(pfl, 0, pfl->chip_len);
+            }
+            pfl->status = 0x00;
+            /* Let's wait 5 seconds before chip erase is done */
+            qemu_mod_timer(pfl->timer,
+                           qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() * 5));
+            break;
+        case 0x30:
+            /* Sector erase */
+            p = pfl->storage;
+            offset &= ~(pfl->sector_len - 1);
+            DPRINTF("%s: start sector erase at " TARGET_FMT_plx "\n", __func__,
+                    offset);
+            if (!pfl->ro) {
+                memset(p + offset, 0xFF, pfl->sector_len);
+                pflash_update(pfl, offset, pfl->sector_len);
+            }
+            pfl->status = 0x00;
+            /* Let's wait 1/2 second before sector erase is done */
+            qemu_mod_timer(pfl->timer,
+                           qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / 2));
+            break;
+        default:
+            DPRINTF("%s: invalid command %02x (wc 5)\n", __func__, cmd);
+            goto reset_flash;
+        }
+        pfl->cmd = cmd;
+        break;
+    case 6:
+        switch (pfl->cmd) {
+        case 0x10:
+            /* Ignore writes during chip erase */
+            return;
+        case 0x30:
+            /* Ignore writes during sector erase */
+            return;
+        default:
+            /* Should never happen */
+            DPRINTF("%s: invalid command state %02x (wc 6)\n",
+                    __func__, pfl->cmd);
+            goto reset_flash;
+        }
+        break;
+    case 7: /* Special value for CFI queries */
+        DPRINTF("%s: invalid write in CFI query mode\n", __func__);
+        goto reset_flash;
+    default:
+        /* Should never happen */
+        DPRINTF("%s: invalid write state (wc 7)\n",  __func__);
+        goto reset_flash;
+    }
+    pfl->wcycle++;
+
+    return;
+
+    /* Reset flash */
+ reset_flash:
+    pfl->bypass = 0;
+    pfl->wcycle = 0;
+    pfl->cmd = 0;
+    return;
+
+ do_bypass:
+    pfl->wcycle = 2;
+    pfl->cmd = 0;
+}
+
+
+static uint32_t pflash_readb_be(void *opaque, hwaddr addr)
+{
+    return pflash_read(opaque, addr, 1, 1);
+}
+
+static uint32_t pflash_readb_le(void *opaque, hwaddr addr)
+{
+    return pflash_read(opaque, addr, 1, 0);
+}
+
+static uint32_t pflash_readw_be(void *opaque, hwaddr addr)
+{
+    pflash_t *pfl = opaque;
+
+    return pflash_read(pfl, addr, 2, 1);
+}
+
+static uint32_t pflash_readw_le(void *opaque, hwaddr addr)
+{
+    pflash_t *pfl = opaque;
+
+    return pflash_read(pfl, addr, 2, 0);
+}
+
+static uint32_t pflash_readl_be(void *opaque, hwaddr addr)
+{
+    pflash_t *pfl = opaque;
+
+    return pflash_read(pfl, addr, 4, 1);
+}
+
+static uint32_t pflash_readl_le(void *opaque, hwaddr addr)
+{
+    pflash_t *pfl = opaque;
+
+    return pflash_read(pfl, addr, 4, 0);
+}
+
+static void pflash_writeb_be(void *opaque, hwaddr addr,
+                             uint32_t value)
+{
+    pflash_write(opaque, addr, value, 1, 1);
+}
+
+static void pflash_writeb_le(void *opaque, hwaddr addr,
+                             uint32_t value)
+{
+    pflash_write(opaque, addr, value, 1, 0);
+}
+
+static void pflash_writew_be(void *opaque, hwaddr addr,
+                             uint32_t value)
+{
+    pflash_t *pfl = opaque;
+
+    pflash_write(pfl, addr, value, 2, 1);
+}
+
+static void pflash_writew_le(void *opaque, hwaddr addr,
+                             uint32_t value)
+{
+    pflash_t *pfl = opaque;
+
+    pflash_write(pfl, addr, value, 2, 0);
+}
+
+static void pflash_writel_be(void *opaque, hwaddr addr,
+                             uint32_t value)
+{
+    pflash_t *pfl = opaque;
+
+    pflash_write(pfl, addr, value, 4, 1);
+}
+
+static void pflash_writel_le(void *opaque, hwaddr addr,
+                             uint32_t value)
+{
+    pflash_t *pfl = opaque;
+
+    pflash_write(pfl, addr, value, 4, 0);
+}
+
+static const MemoryRegionOps pflash_cfi02_ops_be = {
+    .old_mmio = {
+        .read = { pflash_readb_be, pflash_readw_be, pflash_readl_be, },
+        .write = { pflash_writeb_be, pflash_writew_be, pflash_writel_be, },
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const MemoryRegionOps pflash_cfi02_ops_le = {
+    .old_mmio = {
+        .read = { pflash_readb_le, pflash_readw_le, pflash_readl_le, },
+        .write = { pflash_writeb_le, pflash_writew_le, pflash_writel_le, },
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int pflash_cfi02_init(SysBusDevice *dev)
+{
+    pflash_t *pfl = FROM_SYSBUS(typeof(*pfl), dev);
+    uint32_t chip_len;
+    int ret;
+
+    chip_len = pfl->sector_len * pfl->nb_blocs;
+    /* XXX: to be fixed */
+#if 0
+    if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) &&
+        total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024))
+        return NULL;
+#endif
+
+    memory_region_init_rom_device(&pfl->orig_mem, pfl->be ?
+                                  &pflash_cfi02_ops_be : &pflash_cfi02_ops_le,
+                                  pfl, pfl->name, chip_len);
+    vmstate_register_ram(&pfl->orig_mem, DEVICE(pfl));
+    pfl->storage = memory_region_get_ram_ptr(&pfl->orig_mem);
+    pfl->chip_len = chip_len;
+    if (pfl->bs) {
+        /* read the initial flash content */
+        ret = bdrv_read(pfl->bs, 0, pfl->storage, chip_len >> 9);
+        if (ret < 0) {
+            g_free(pfl);
+            return 1;
+        }
+    }
+
+    pflash_setup_mappings(pfl);
+    pfl->rom_mode = 1;
+    sysbus_init_mmio(dev, &pfl->mem);
+
+    if (pfl->bs) {
+        pfl->ro = bdrv_is_read_only(pfl->bs);
+    } else {
+        pfl->ro = 0;
+    }
+
+    pfl->timer = qemu_new_timer_ns(vm_clock, pflash_timer, pfl);
+    pfl->wcycle = 0;
+    pfl->cmd = 0;
+    pfl->status = 0;
+    /* Hardcoded CFI table (mostly from SG29 Spansion flash) */
+    pfl->cfi_len = 0x52;
+    /* Standard "QRY" string */
+    pfl->cfi_table[0x10] = 'Q';
+    pfl->cfi_table[0x11] = 'R';
+    pfl->cfi_table[0x12] = 'Y';
+    /* Command set (AMD/Fujitsu) */
+    pfl->cfi_table[0x13] = 0x02;
+    pfl->cfi_table[0x14] = 0x00;
+    /* Primary extended table address */
+    pfl->cfi_table[0x15] = 0x31;
+    pfl->cfi_table[0x16] = 0x00;
+    /* Alternate command set (none) */
+    pfl->cfi_table[0x17] = 0x00;
+    pfl->cfi_table[0x18] = 0x00;
+    /* Alternate extended table (none) */
+    pfl->cfi_table[0x19] = 0x00;
+    pfl->cfi_table[0x1A] = 0x00;
+    /* Vcc min */
+    pfl->cfi_table[0x1B] = 0x27;
+    /* Vcc max */
+    pfl->cfi_table[0x1C] = 0x36;
+    /* Vpp min (no Vpp pin) */
+    pfl->cfi_table[0x1D] = 0x00;
+    /* Vpp max (no Vpp pin) */
+    pfl->cfi_table[0x1E] = 0x00;
+    /* Reserved */
+    pfl->cfi_table[0x1F] = 0x07;
+    /* Timeout for min size buffer write (NA) */
+    pfl->cfi_table[0x20] = 0x00;
+    /* Typical timeout for block erase (512 ms) */
+    pfl->cfi_table[0x21] = 0x09;
+    /* Typical timeout for full chip erase (4096 ms) */
+    pfl->cfi_table[0x22] = 0x0C;
+    /* Reserved */
+    pfl->cfi_table[0x23] = 0x01;
+    /* Max timeout for buffer write (NA) */
+    pfl->cfi_table[0x24] = 0x00;
+    /* Max timeout for block erase */
+    pfl->cfi_table[0x25] = 0x0A;
+    /* Max timeout for chip erase */
+    pfl->cfi_table[0x26] = 0x0D;
+    /* Device size */
+    pfl->cfi_table[0x27] = ctz32(chip_len);
+    /* Flash device interface (8 & 16 bits) */
+    pfl->cfi_table[0x28] = 0x02;
+    pfl->cfi_table[0x29] = 0x00;
+    /* Max number of bytes in multi-bytes write */
+    /* XXX: disable buffered write as it's not supported */
+    //    pfl->cfi_table[0x2A] = 0x05;
+    pfl->cfi_table[0x2A] = 0x00;
+    pfl->cfi_table[0x2B] = 0x00;
+    /* Number of erase block regions (uniform) */
+    pfl->cfi_table[0x2C] = 0x01;
+    /* Erase block region 1 */
+    pfl->cfi_table[0x2D] = pfl->nb_blocs - 1;
+    pfl->cfi_table[0x2E] = (pfl->nb_blocs - 1) >> 8;
+    pfl->cfi_table[0x2F] = pfl->sector_len >> 8;
+    pfl->cfi_table[0x30] = pfl->sector_len >> 16;
+
+    /* Extended */
+    pfl->cfi_table[0x31] = 'P';
+    pfl->cfi_table[0x32] = 'R';
+    pfl->cfi_table[0x33] = 'I';
+
+    pfl->cfi_table[0x34] = '1';
+    pfl->cfi_table[0x35] = '0';
+
+    pfl->cfi_table[0x36] = 0x00;
+    pfl->cfi_table[0x37] = 0x00;
+    pfl->cfi_table[0x38] = 0x00;
+    pfl->cfi_table[0x39] = 0x00;
+
+    pfl->cfi_table[0x3a] = 0x00;
+
+    pfl->cfi_table[0x3b] = 0x00;
+    pfl->cfi_table[0x3c] = 0x00;
+
+    return 0;
+}
+
+static Property pflash_cfi02_properties[] = {
+    DEFINE_PROP_DRIVE("drive", struct pflash_t, bs),
+    DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0),
+    DEFINE_PROP_UINT32("sector-length", struct pflash_t, sector_len, 0),
+    DEFINE_PROP_UINT8("width", struct pflash_t, width, 0),
+    DEFINE_PROP_UINT8("mappings", struct pflash_t, mappings, 0),
+    DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0),
+    DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0),
+    DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0),
+    DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0),
+    DEFINE_PROP_UINT16("id3", struct pflash_t, ident3, 0),
+    DEFINE_PROP_UINT16("unlock-addr0", struct pflash_t, unlock_addr0, 0),
+    DEFINE_PROP_UINT16("unlock-addr1", struct pflash_t, unlock_addr1, 0),
+    DEFINE_PROP_STRING("name", struct pflash_t, name),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pflash_cfi02_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = pflash_cfi02_init;
+    dc->props = pflash_cfi02_properties;
+}
+
+static const TypeInfo pflash_cfi02_info = {
+    .name           = "cfi.pflash02",
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(struct pflash_t),
+    .class_init     = pflash_cfi02_class_init,
+};
+
+static void pflash_cfi02_register_types(void)
+{
+    type_register_static(&pflash_cfi02_info);
+}
+
+type_init(pflash_cfi02_register_types)
+
+pflash_t *pflash_cfi02_register(hwaddr base,
+                                DeviceState *qdev, const char *name,
+                                hwaddr size,
+                                BlockDriverState *bs, uint32_t sector_len,
+                                int nb_blocs, int nb_mappings, int width,
+                                uint16_t id0, uint16_t id1,
+                                uint16_t id2, uint16_t id3,
+                                uint16_t unlock_addr0, uint16_t unlock_addr1,
+                                int be)
+{
+    DeviceState *dev = qdev_create(NULL, "cfi.pflash02");
+    SysBusDevice *busdev = SYS_BUS_DEVICE(dev);
+    pflash_t *pfl = (pflash_t *)object_dynamic_cast(OBJECT(dev),
+                                                    "cfi.pflash02");
+
+    if (bs && qdev_prop_set_drive(dev, "drive", bs)) {
+        abort();
+    }
+    qdev_prop_set_uint32(dev, "num-blocks", nb_blocs);
+    qdev_prop_set_uint32(dev, "sector-length", sector_len);
+    qdev_prop_set_uint8(dev, "width", width);
+    qdev_prop_set_uint8(dev, "mappings", nb_mappings);
+    qdev_prop_set_uint8(dev, "big-endian", !!be);
+    qdev_prop_set_uint16(dev, "id0", id0);
+    qdev_prop_set_uint16(dev, "id1", id1);
+    qdev_prop_set_uint16(dev, "id2", id2);
+    qdev_prop_set_uint16(dev, "id3", id3);
+    qdev_prop_set_uint16(dev, "unlock-addr0", unlock_addr0);
+    qdev_prop_set_uint16(dev, "unlock-addr1", unlock_addr1);
+    qdev_prop_set_string(dev, "name", name);
+    qdev_init_nofail(dev);
+
+    sysbus_mmio_map(busdev, 0, base);
+    return pfl;
+}
diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c
new file mode 100644 (file)
index 0000000..532347b
--- /dev/null
@@ -0,0 +1,972 @@
+/*
+ *  xen paravirt block device backend
+ *
+ *  (c) Gerd Hoffmann <kraxel@redhat.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; under version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *  Contributions after 2012-01-13 are licensed under the terms of the
+ *  GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <inttypes.h>
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+
+#include "hw/hw.h"
+#include "hw/xen/xen_backend.h"
+#include "hw/xen_blkif.h"
+#include "sysemu/blockdev.h"
+
+/* ------------------------------------------------------------- */
+
+static int batch_maps   = 0;
+
+static int max_requests = 32;
+
+/* ------------------------------------------------------------- */
+
+#define BLOCK_SIZE  512
+#define IOCB_COUNT  (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2)
+
+struct PersistentGrant {
+    void *page;
+    struct XenBlkDev *blkdev;
+};
+
+typedef struct PersistentGrant PersistentGrant;
+
+struct ioreq {
+    blkif_request_t     req;
+    int16_t             status;
+
+    /* parsed request */
+    off_t               start;
+    QEMUIOVector        v;
+    int                 presync;
+    int                 postsync;
+    uint8_t             mapped;
+
+    /* grant mapping */
+    uint32_t            domids[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+    uint32_t            refs[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+    int                 prot;
+    void                *page[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+    void                *pages;
+    int                 num_unmap;
+
+    /* aio status */
+    int                 aio_inflight;
+    int                 aio_errors;
+
+    struct XenBlkDev    *blkdev;
+    QLIST_ENTRY(ioreq)   list;
+    BlockAcctCookie     acct;
+};
+
+struct XenBlkDev {
+    struct XenDevice    xendev;  /* must be first */
+    char                *params;
+    char                *mode;
+    char                *type;
+    char                *dev;
+    char                *devtype;
+    const char          *fileproto;
+    const char          *filename;
+    int                 ring_ref;
+    void                *sring;
+    int64_t             file_blk;
+    int64_t             file_size;
+    int                 protocol;
+    blkif_back_rings_t  rings;
+    int                 more_work;
+    int                 cnt_map;
+
+    /* request lists */
+    QLIST_HEAD(inflight_head, ioreq) inflight;
+    QLIST_HEAD(finished_head, ioreq) finished;
+    QLIST_HEAD(freelist_head, ioreq) freelist;
+    int                 requests_total;
+    int                 requests_inflight;
+    int                 requests_finished;
+
+    /* Persistent grants extension */
+    gboolean            feature_persistent;
+    GTree               *persistent_gnts;
+    unsigned int        persistent_gnt_count;
+    unsigned int        max_grants;
+
+    /* qemu block driver */
+    DriveInfo           *dinfo;
+    BlockDriverState    *bs;
+    QEMUBH              *bh;
+};
+
+/* ------------------------------------------------------------- */
+
+static void ioreq_reset(struct ioreq *ioreq)
+{
+    memset(&ioreq->req, 0, sizeof(ioreq->req));
+    ioreq->status = 0;
+    ioreq->start = 0;
+    ioreq->presync = 0;
+    ioreq->postsync = 0;
+    ioreq->mapped = 0;
+
+    memset(ioreq->domids, 0, sizeof(ioreq->domids));
+    memset(ioreq->refs, 0, sizeof(ioreq->refs));
+    ioreq->prot = 0;
+    memset(ioreq->page, 0, sizeof(ioreq->page));
+    ioreq->pages = NULL;
+
+    ioreq->aio_inflight = 0;
+    ioreq->aio_errors = 0;
+
+    ioreq->blkdev = NULL;
+    memset(&ioreq->list, 0, sizeof(ioreq->list));
+    memset(&ioreq->acct, 0, sizeof(ioreq->acct));
+
+    qemu_iovec_reset(&ioreq->v);
+}
+
+static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+    uint ua = GPOINTER_TO_UINT(a);
+    uint ub = GPOINTER_TO_UINT(b);
+    return (ua > ub) - (ua < ub);
+}
+
+static void destroy_grant(gpointer pgnt)
+{
+    PersistentGrant *grant = pgnt;
+    XenGnttab gnt = grant->blkdev->xendev.gnttabdev;
+
+    if (xc_gnttab_munmap(gnt, grant->page, 1) != 0) {
+        xen_be_printf(&grant->blkdev->xendev, 0,
+                      "xc_gnttab_munmap failed: %s\n",
+                      strerror(errno));
+    }
+    grant->blkdev->persistent_gnt_count--;
+    xen_be_printf(&grant->blkdev->xendev, 3,
+                  "unmapped grant %p\n", grant->page);
+    g_free(grant);
+}
+
+static struct ioreq *ioreq_start(struct XenBlkDev *blkdev)
+{
+    struct ioreq *ioreq = NULL;
+
+    if (QLIST_EMPTY(&blkdev->freelist)) {
+        if (blkdev->requests_total >= max_requests) {
+            goto out;
+        }
+        /* allocate new struct */
+        ioreq = g_malloc0(sizeof(*ioreq));
+        ioreq->blkdev = blkdev;
+        blkdev->requests_total++;
+        qemu_iovec_init(&ioreq->v, BLKIF_MAX_SEGMENTS_PER_REQUEST);
+    } else {
+        /* get one from freelist */
+        ioreq = QLIST_FIRST(&blkdev->freelist);
+        QLIST_REMOVE(ioreq, list);
+    }
+    QLIST_INSERT_HEAD(&blkdev->inflight, ioreq, list);
+    blkdev->requests_inflight++;
+
+out:
+    return ioreq;
+}
+
+static void ioreq_finish(struct ioreq *ioreq)
+{
+    struct XenBlkDev *blkdev = ioreq->blkdev;
+
+    QLIST_REMOVE(ioreq, list);
+    QLIST_INSERT_HEAD(&blkdev->finished, ioreq, list);
+    blkdev->requests_inflight--;
+    blkdev->requests_finished++;
+}
+
+static void ioreq_release(struct ioreq *ioreq, bool finish)
+{
+    struct XenBlkDev *blkdev = ioreq->blkdev;
+
+    QLIST_REMOVE(ioreq, list);
+    ioreq_reset(ioreq);
+    ioreq->blkdev = blkdev;
+    QLIST_INSERT_HEAD(&blkdev->freelist, ioreq, list);
+    if (finish) {
+        blkdev->requests_finished--;
+    } else {
+        blkdev->requests_inflight--;
+    }
+}
+
+/*
+ * translate request into iovec + start offset
+ * do sanity checks along the way
+ */
+static int ioreq_parse(struct ioreq *ioreq)
+{
+    struct XenBlkDev *blkdev = ioreq->blkdev;
+    uintptr_t mem;
+    size_t len;
+    int i;
+
+    xen_be_printf(&blkdev->xendev, 3,
+                  "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n",
+                  ioreq->req.operation, ioreq->req.nr_segments,
+                  ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number);
+    switch (ioreq->req.operation) {
+    case BLKIF_OP_READ:
+        ioreq->prot = PROT_WRITE; /* to memory */
+        break;
+    case BLKIF_OP_FLUSH_DISKCACHE:
+        ioreq->presync = 1;
+        if (!ioreq->req.nr_segments) {
+            return 0;
+        }
+        /* fall through */
+    case BLKIF_OP_WRITE:
+        ioreq->prot = PROT_READ; /* from memory */
+        break;
+    default:
+        xen_be_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n",
+                      ioreq->req.operation);
+        goto err;
+    };
+
+    if (ioreq->req.operation != BLKIF_OP_READ && blkdev->mode[0] != 'w') {
+        xen_be_printf(&blkdev->xendev, 0, "error: write req for ro device\n");
+        goto err;
+    }
+
+    ioreq->start = ioreq->req.sector_number * blkdev->file_blk;
+    for (i = 0; i < ioreq->req.nr_segments; i++) {
+        if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) {
+            xen_be_printf(&blkdev->xendev, 0, "error: nr_segments too big\n");
+            goto err;
+        }
+        if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) {
+            xen_be_printf(&blkdev->xendev, 0, "error: first > last sector\n");
+            goto err;
+        }
+        if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) {
+            xen_be_printf(&blkdev->xendev, 0, "error: page crossing\n");
+            goto err;
+        }
+
+        ioreq->domids[i] = blkdev->xendev.dom;
+        ioreq->refs[i]   = ioreq->req.seg[i].gref;
+
+        mem = ioreq->req.seg[i].first_sect * blkdev->file_blk;
+        len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk;
+        qemu_iovec_add(&ioreq->v, (void*)mem, len);
+    }
+    if (ioreq->start + ioreq->v.size > blkdev->file_size) {
+        xen_be_printf(&blkdev->xendev, 0, "error: access beyond end of file\n");
+        goto err;
+    }
+    return 0;
+
+err:
+    ioreq->status = BLKIF_RSP_ERROR;
+    return -1;
+}
+
+static void ioreq_unmap(struct ioreq *ioreq)
+{
+    XenGnttab gnt = ioreq->blkdev->xendev.gnttabdev;
+    int i;
+
+    if (ioreq->num_unmap == 0 || ioreq->mapped == 0) {
+        return;
+    }
+    if (batch_maps) {
+        if (!ioreq->pages) {
+            return;
+        }
+        if (xc_gnttab_munmap(gnt, ioreq->pages, ioreq->num_unmap) != 0) {
+            xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n",
+                          strerror(errno));
+        }
+        ioreq->blkdev->cnt_map -= ioreq->num_unmap;
+        ioreq->pages = NULL;
+    } else {
+        for (i = 0; i < ioreq->num_unmap; i++) {
+            if (!ioreq->page[i]) {
+                continue;
+            }
+            if (xc_gnttab_munmap(gnt, ioreq->page[i], 1) != 0) {
+                xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n",
+                              strerror(errno));
+            }
+            ioreq->blkdev->cnt_map--;
+            ioreq->page[i] = NULL;
+        }
+    }
+    ioreq->mapped = 0;
+}
+
+static int ioreq_map(struct ioreq *ioreq)
+{
+    XenGnttab gnt = ioreq->blkdev->xendev.gnttabdev;
+    uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+    uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+    void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST];
+    int i, j, new_maps = 0;
+    PersistentGrant *grant;
+    /* domids and refs variables will contain the information necessary
+     * to map the grants that are needed to fulfill this request.
+     *
+     * After mapping the needed grants, the page array will contain the
+     * memory address of each granted page in the order specified in ioreq
+     * (disregarding if it's a persistent grant or not).
+     */
+
+    if (ioreq->v.niov == 0 || ioreq->mapped == 1) {
+        return 0;
+    }
+    if (ioreq->blkdev->feature_persistent) {
+        for (i = 0; i < ioreq->v.niov; i++) {
+            grant = g_tree_lookup(ioreq->blkdev->persistent_gnts,
+                                    GUINT_TO_POINTER(ioreq->refs[i]));
+
+            if (grant != NULL) {
+                page[i] = grant->page;
+                xen_be_printf(&ioreq->blkdev->xendev, 3,
+                              "using persistent-grant %" PRIu32 "\n",
+                              ioreq->refs[i]);
+            } else {
+                    /* Add the grant to the list of grants that
+                     * should be mapped
+                     */
+                    domids[new_maps] = ioreq->domids[i];
+                    refs[new_maps] = ioreq->refs[i];
+                    page[i] = NULL;
+                    new_maps++;
+            }
+        }
+        /* Set the protection to RW, since grants may be reused later
+         * with a different protection than the one needed for this request
+         */
+        ioreq->prot = PROT_WRITE | PROT_READ;
+    } else {
+        /* All grants in the request should be mapped */
+        memcpy(refs, ioreq->refs, sizeof(refs));
+        memcpy(domids, ioreq->domids, sizeof(domids));
+        memset(page, 0, sizeof(page));
+        new_maps = ioreq->v.niov;
+    }
+
+    if (batch_maps && new_maps) {
+        ioreq->pages = xc_gnttab_map_grant_refs
+            (gnt, new_maps, domids, refs, ioreq->prot);
+        if (ioreq->pages == NULL) {
+            xen_be_printf(&ioreq->blkdev->xendev, 0,
+                          "can't map %d grant refs (%s, %d maps)\n",
+                          new_maps, strerror(errno), ioreq->blkdev->cnt_map);
+            return -1;
+        }
+        for (i = 0, j = 0; i < ioreq->v.niov; i++) {
+            if (page[i] == NULL) {
+                page[i] = ioreq->pages + (j++) * XC_PAGE_SIZE;
+            }
+        }
+        ioreq->blkdev->cnt_map += new_maps;
+    } else if (new_maps)  {
+        for (i = 0; i < new_maps; i++) {
+            ioreq->page[i] = xc_gnttab_map_grant_ref
+                (gnt, domids[i], refs[i], ioreq->prot);
+            if (ioreq->page[i] == NULL) {
+                xen_be_printf(&ioreq->blkdev->xendev, 0,
+                              "can't map grant ref %d (%s, %d maps)\n",
+                              refs[i], strerror(errno), ioreq->blkdev->cnt_map);
+                ioreq_unmap(ioreq);
+                return -1;
+            }
+            ioreq->blkdev->cnt_map++;
+        }
+        for (i = 0, j = 0; i < ioreq->v.niov; i++) {
+            if (page[i] == NULL) {
+                page[i] = ioreq->page[j++];
+            }
+        }
+    }
+    if (ioreq->blkdev->feature_persistent) {
+        while ((ioreq->blkdev->persistent_gnt_count < ioreq->blkdev->max_grants)
+              && new_maps) {
+            /* Go through the list of newly mapped grants and add as many
+             * as possible to the list of persistently mapped grants.
+             *
+             * Since we start at the end of ioreq->page(s), we only need
+             * to decrease new_maps to prevent this granted pages from
+             * being unmapped in ioreq_unmap.
+             */
+            grant = g_malloc0(sizeof(*grant));
+            new_maps--;
+            if (batch_maps) {
+                grant->page = ioreq->pages + (new_maps) * XC_PAGE_SIZE;
+            } else {
+                grant->page = ioreq->page[new_maps];
+            }
+            grant->blkdev = ioreq->blkdev;
+            xen_be_printf(&ioreq->blkdev->xendev, 3,
+                          "adding grant %" PRIu32 " page: %p\n",
+                          refs[new_maps], grant->page);
+            g_tree_insert(ioreq->blkdev->persistent_gnts,
+                          GUINT_TO_POINTER(refs[new_maps]),
+                          grant);
+            ioreq->blkdev->persistent_gnt_count++;
+        }
+    }
+    for (i = 0; i < ioreq->v.niov; i++) {
+        ioreq->v.iov[i].iov_base += (uintptr_t)page[i];
+    }
+    ioreq->mapped = 1;
+    ioreq->num_unmap = new_maps;
+    return 0;
+}
+
+static int ioreq_runio_qemu_aio(struct ioreq *ioreq);
+
+static void qemu_aio_complete(void *opaque, int ret)
+{
+    struct ioreq *ioreq = opaque;
+
+    if (ret != 0) {
+        xen_be_printf(&ioreq->blkdev->xendev, 0, "%s I/O error\n",
+                      ioreq->req.operation == BLKIF_OP_READ ? "read" : "write");
+        ioreq->aio_errors++;
+    }
+
+    ioreq->aio_inflight--;
+    if (ioreq->presync) {
+        ioreq->presync = 0;
+        ioreq_runio_qemu_aio(ioreq);
+        return;
+    }
+    if (ioreq->aio_inflight > 0) {
+        return;
+    }
+    if (ioreq->postsync) {
+        ioreq->postsync = 0;
+        ioreq->aio_inflight++;
+        bdrv_aio_flush(ioreq->blkdev->bs, qemu_aio_complete, ioreq);
+        return;
+    }
+
+    ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY;
+    ioreq_unmap(ioreq);
+    ioreq_finish(ioreq);
+    bdrv_acct_done(ioreq->blkdev->bs, &ioreq->acct);
+    qemu_bh_schedule(ioreq->blkdev->bh);
+}
+
+static int ioreq_runio_qemu_aio(struct ioreq *ioreq)
+{
+    struct XenBlkDev *blkdev = ioreq->blkdev;
+
+    if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) {
+        goto err_no_map;
+    }
+
+    ioreq->aio_inflight++;
+    if (ioreq->presync) {
+        bdrv_aio_flush(ioreq->blkdev->bs, qemu_aio_complete, ioreq);
+        return 0;
+    }
+
+    switch (ioreq->req.operation) {
+    case BLKIF_OP_READ:
+        bdrv_acct_start(blkdev->bs, &ioreq->acct, ioreq->v.size, BDRV_ACCT_READ);
+        ioreq->aio_inflight++;
+        bdrv_aio_readv(blkdev->bs, ioreq->start / BLOCK_SIZE,
+                       &ioreq->v, ioreq->v.size / BLOCK_SIZE,
+                       qemu_aio_complete, ioreq);
+        break;
+    case BLKIF_OP_WRITE:
+    case BLKIF_OP_FLUSH_DISKCACHE:
+        if (!ioreq->req.nr_segments) {
+            break;
+        }
+
+        bdrv_acct_start(blkdev->bs, &ioreq->acct, ioreq->v.size, BDRV_ACCT_WRITE);
+        ioreq->aio_inflight++;
+        bdrv_aio_writev(blkdev->bs, ioreq->start / BLOCK_SIZE,
+                        &ioreq->v, ioreq->v.size / BLOCK_SIZE,
+                        qemu_aio_complete, ioreq);
+        break;
+    default:
+        /* unknown operation (shouldn't happen -- parse catches this) */
+        goto err;
+    }
+
+    qemu_aio_complete(ioreq, 0);
+
+    return 0;
+
+err:
+    ioreq_unmap(ioreq);
+err_no_map:
+    ioreq_finish(ioreq);
+    ioreq->status = BLKIF_RSP_ERROR;
+    return -1;
+}
+
+static int blk_send_response_one(struct ioreq *ioreq)
+{
+    struct XenBlkDev  *blkdev = ioreq->blkdev;
+    int               send_notify   = 0;
+    int               have_requests = 0;
+    blkif_response_t  resp;
+    void              *dst;
+
+    resp.id        = ioreq->req.id;
+    resp.operation = ioreq->req.operation;
+    resp.status    = ioreq->status;
+
+    /* Place on the response ring for the relevant domain. */
+    switch (blkdev->protocol) {
+    case BLKIF_PROTOCOL_NATIVE:
+        dst = RING_GET_RESPONSE(&blkdev->rings.native, blkdev->rings.native.rsp_prod_pvt);
+        break;
+    case BLKIF_PROTOCOL_X86_32:
+        dst = RING_GET_RESPONSE(&blkdev->rings.x86_32_part,
+                                blkdev->rings.x86_32_part.rsp_prod_pvt);
+        break;
+    case BLKIF_PROTOCOL_X86_64:
+        dst = RING_GET_RESPONSE(&blkdev->rings.x86_64_part,
+                                blkdev->rings.x86_64_part.rsp_prod_pvt);
+        break;
+    default:
+        dst = NULL;
+    }
+    memcpy(dst, &resp, sizeof(resp));
+    blkdev->rings.common.rsp_prod_pvt++;
+
+    RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify);
+    if (blkdev->rings.common.rsp_prod_pvt == blkdev->rings.common.req_cons) {
+        /*
+         * Tail check for pending requests. Allows frontend to avoid
+         * notifications if requests are already in flight (lower
+         * overheads and promotes batching).
+         */
+        RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests);
+    } else if (RING_HAS_UNCONSUMED_REQUESTS(&blkdev->rings.common)) {
+        have_requests = 1;
+    }
+
+    if (have_requests) {
+        blkdev->more_work++;
+    }
+    return send_notify;
+}
+
+/* walk finished list, send outstanding responses, free requests */
+static void blk_send_response_all(struct XenBlkDev *blkdev)
+{
+    struct ioreq *ioreq;
+    int send_notify = 0;
+
+    while (!QLIST_EMPTY(&blkdev->finished)) {
+        ioreq = QLIST_FIRST(&blkdev->finished);
+        send_notify += blk_send_response_one(ioreq);
+        ioreq_release(ioreq, true);
+    }
+    if (send_notify) {
+        xen_be_send_notify(&blkdev->xendev);
+    }
+}
+
+static int blk_get_request(struct XenBlkDev *blkdev, struct ioreq *ioreq, RING_IDX rc)
+{
+    switch (blkdev->protocol) {
+    case BLKIF_PROTOCOL_NATIVE:
+        memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc),
+               sizeof(ioreq->req));
+        break;
+    case BLKIF_PROTOCOL_X86_32:
+        blkif_get_x86_32_req(&ioreq->req,
+                             RING_GET_REQUEST(&blkdev->rings.x86_32_part, rc));
+        break;
+    case BLKIF_PROTOCOL_X86_64:
+        blkif_get_x86_64_req(&ioreq->req,
+                             RING_GET_REQUEST(&blkdev->rings.x86_64_part, rc));
+        break;
+    }
+    return 0;
+}
+
+static void blk_handle_requests(struct XenBlkDev *blkdev)
+{
+    RING_IDX rc, rp;
+    struct ioreq *ioreq;
+
+    blkdev->more_work = 0;
+
+    rc = blkdev->rings.common.req_cons;
+    rp = blkdev->rings.common.sring->req_prod;
+    xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
+
+    blk_send_response_all(blkdev);
+    while (rc != rp) {
+        /* pull request from ring */
+        if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) {
+            break;
+        }
+        ioreq = ioreq_start(blkdev);
+        if (ioreq == NULL) {
+            blkdev->more_work++;
+            break;
+        }
+        blk_get_request(blkdev, ioreq, rc);
+        blkdev->rings.common.req_cons = ++rc;
+
+        /* parse them */
+        if (ioreq_parse(ioreq) != 0) {
+            if (blk_send_response_one(ioreq)) {
+                xen_be_send_notify(&blkdev->xendev);
+            }
+            ioreq_release(ioreq, false);
+            continue;
+        }
+
+        ioreq_runio_qemu_aio(ioreq);
+    }
+
+    if (blkdev->more_work && blkdev->requests_inflight < max_requests) {
+        qemu_bh_schedule(blkdev->bh);
+    }
+}
+
+/* ------------------------------------------------------------- */
+
+static void blk_bh(void *opaque)
+{
+    struct XenBlkDev *blkdev = opaque;
+    blk_handle_requests(blkdev);
+}
+
+/*
+ * We need to account for the grant allocations requiring contiguous
+ * chunks; the worst case number would be
+ *     max_req * max_seg + (max_req - 1) * (max_seg - 1) + 1,
+ * but in order to keep things simple just use
+ *     2 * max_req * max_seg.
+ */
+#define MAX_GRANTS(max_req, max_seg) (2 * (max_req) * (max_seg))
+
+static void blk_alloc(struct XenDevice *xendev)
+{
+    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
+
+    QLIST_INIT(&blkdev->inflight);
+    QLIST_INIT(&blkdev->finished);
+    QLIST_INIT(&blkdev->freelist);
+    blkdev->bh = qemu_bh_new(blk_bh, blkdev);
+    if (xen_mode != XEN_EMULATE) {
+        batch_maps = 1;
+    }
+    if (xc_gnttab_set_max_grants(xendev->gnttabdev,
+            MAX_GRANTS(max_requests, BLKIF_MAX_SEGMENTS_PER_REQUEST)) < 0) {
+        xen_be_printf(xendev, 0, "xc_gnttab_set_max_grants failed: %s\n",
+                      strerror(errno));
+    }
+}
+
+static int blk_init(struct XenDevice *xendev)
+{
+    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
+    int info = 0;
+
+    /* read xenstore entries */
+    if (blkdev->params == NULL) {
+        char *h = NULL;
+        blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params");
+        if (blkdev->params != NULL) {
+            h = strchr(blkdev->params, ':');
+        }
+        if (h != NULL) {
+            blkdev->fileproto = blkdev->params;
+            blkdev->filename  = h+1;
+            *h = 0;
+        } else {
+            blkdev->fileproto = "<unset>";
+            blkdev->filename  = blkdev->params;
+        }
+    }
+    if (!strcmp("aio", blkdev->fileproto)) {
+        blkdev->fileproto = "raw";
+    }
+    if (blkdev->mode == NULL) {
+        blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode");
+    }
+    if (blkdev->type == NULL) {
+        blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type");
+    }
+    if (blkdev->dev == NULL) {
+        blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev");
+    }
+    if (blkdev->devtype == NULL) {
+        blkdev->devtype = xenstore_read_be_str(&blkdev->xendev, "device-type");
+    }
+
+    /* do we have all we need? */
+    if (blkdev->params == NULL ||
+        blkdev->mode == NULL   ||
+        blkdev->type == NULL   ||
+        blkdev->dev == NULL) {
+        goto out_error;
+    }
+
+    /* read-only ? */
+    if (strcmp(blkdev->mode, "w")) {
+        info  |= VDISK_READONLY;
+    }
+
+    /* cdrom ? */
+    if (blkdev->devtype && !strcmp(blkdev->devtype, "cdrom")) {
+        info  |= VDISK_CDROM;
+    }
+
+    blkdev->file_blk  = BLOCK_SIZE;
+
+    /* fill info
+     * blk_connect supplies sector-size and sectors
+     */
+    xenstore_write_be_int(&blkdev->xendev, "feature-flush-cache", 1);
+    xenstore_write_be_int(&blkdev->xendev, "feature-persistent", 1);
+    xenstore_write_be_int(&blkdev->xendev, "info", info);
+    return 0;
+
+out_error:
+    g_free(blkdev->params);
+    blkdev->params = NULL;
+    g_free(blkdev->mode);
+    blkdev->mode = NULL;
+    g_free(blkdev->type);
+    blkdev->type = NULL;
+    g_free(blkdev->dev);
+    blkdev->dev = NULL;
+    g_free(blkdev->devtype);
+    blkdev->devtype = NULL;
+    return -1;
+}
+
+static int blk_connect(struct XenDevice *xendev)
+{
+    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
+    int pers, index, qflags;
+
+    /* read-only ? */
+    qflags = BDRV_O_CACHE_WB | BDRV_O_NATIVE_AIO;
+    if (strcmp(blkdev->mode, "w") == 0) {
+        qflags |= BDRV_O_RDWR;
+    }
+
+    /* init qemu block driver */
+    index = (blkdev->xendev.dev - 202 * 256) / 16;
+    blkdev->dinfo = drive_get(IF_XEN, 0, index);
+    if (!blkdev->dinfo) {
+        /* setup via xenbus -> create new block driver instance */
+        xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n");
+        blkdev->bs = bdrv_new(blkdev->dev);
+        if (blkdev->bs) {
+            if (bdrv_open(blkdev->bs, blkdev->filename, NULL, qflags,
+                        bdrv_find_whitelisted_format(blkdev->fileproto)) != 0) {
+                bdrv_delete(blkdev->bs);
+                blkdev->bs = NULL;
+            }
+        }
+        if (!blkdev->bs) {
+            return -1;
+        }
+    } else {
+        /* setup via qemu cmdline -> already setup for us */
+        xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n");
+        blkdev->bs = blkdev->dinfo->bdrv;
+    }
+    bdrv_attach_dev_nofail(blkdev->bs, blkdev);
+    blkdev->file_size = bdrv_getlength(blkdev->bs);
+    if (blkdev->file_size < 0) {
+        xen_be_printf(&blkdev->xendev, 1, "bdrv_getlength: %d (%s) | drv %s\n",
+                      (int)blkdev->file_size, strerror(-blkdev->file_size),
+                      bdrv_get_format_name(blkdev->bs) ?: "-");
+        blkdev->file_size = 0;
+    }
+
+    xen_be_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\","
+                  " size %" PRId64 " (%" PRId64 " MB)\n",
+                  blkdev->type, blkdev->fileproto, blkdev->filename,
+                  blkdev->file_size, blkdev->file_size >> 20);
+
+    /* Fill in number of sector size and number of sectors */
+    xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk);
+    xenstore_write_be_int64(&blkdev->xendev, "sectors",
+                            blkdev->file_size / blkdev->file_blk);
+
+    if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref) == -1) {
+        return -1;
+    }
+    if (xenstore_read_fe_int(&blkdev->xendev, "event-channel",
+                             &blkdev->xendev.remote_port) == -1) {
+        return -1;
+    }
+    if (xenstore_read_fe_int(&blkdev->xendev, "feature-persistent", &pers)) {
+        blkdev->feature_persistent = FALSE;
+    } else {
+        blkdev->feature_persistent = !!pers;
+    }
+
+    blkdev->protocol = BLKIF_PROTOCOL_NATIVE;
+    if (blkdev->xendev.protocol) {
+        if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32) == 0) {
+            blkdev->protocol = BLKIF_PROTOCOL_X86_32;
+        }
+        if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64) == 0) {
+            blkdev->protocol = BLKIF_PROTOCOL_X86_64;
+        }
+    }
+
+    blkdev->sring = xc_gnttab_map_grant_ref(blkdev->xendev.gnttabdev,
+                                            blkdev->xendev.dom,
+                                            blkdev->ring_ref,
+                                            PROT_READ | PROT_WRITE);
+    if (!blkdev->sring) {
+        return -1;
+    }
+    blkdev->cnt_map++;
+
+    switch (blkdev->protocol) {
+    case BLKIF_PROTOCOL_NATIVE:
+    {
+        blkif_sring_t *sring_native = blkdev->sring;
+        BACK_RING_INIT(&blkdev->rings.native, sring_native, XC_PAGE_SIZE);
+        break;
+    }
+    case BLKIF_PROTOCOL_X86_32:
+    {
+        blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring;
+
+        BACK_RING_INIT(&blkdev->rings.x86_32_part, sring_x86_32, XC_PAGE_SIZE);
+        break;
+    }
+    case BLKIF_PROTOCOL_X86_64:
+    {
+        blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring;
+
+        BACK_RING_INIT(&blkdev->rings.x86_64_part, sring_x86_64, XC_PAGE_SIZE);
+        break;
+    }
+    }
+
+    if (blkdev->feature_persistent) {
+        /* Init persistent grants */
+        blkdev->max_grants = max_requests * BLKIF_MAX_SEGMENTS_PER_REQUEST;
+        blkdev->persistent_gnts = g_tree_new_full((GCompareDataFunc)int_cmp,
+                                             NULL, NULL,
+                                             (GDestroyNotify)destroy_grant);
+        blkdev->persistent_gnt_count = 0;
+    }
+
+    xen_be_bind_evtchn(&blkdev->xendev);
+
+    xen_be_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, "
+                  "remote port %d, local port %d\n",
+                  blkdev->xendev.protocol, blkdev->ring_ref,
+                  blkdev->xendev.remote_port, blkdev->xendev.local_port);
+    return 0;
+}
+
+static void blk_disconnect(struct XenDevice *xendev)
+{
+    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
+
+    if (blkdev->bs) {
+        if (!blkdev->dinfo) {
+            /* close/delete only if we created it ourself */
+            bdrv_close(blkdev->bs);
+            bdrv_detach_dev(blkdev->bs, blkdev);
+            bdrv_delete(blkdev->bs);
+        }
+        blkdev->bs = NULL;
+    }
+    xen_be_unbind_evtchn(&blkdev->xendev);
+
+    if (blkdev->sring) {
+        xc_gnttab_munmap(blkdev->xendev.gnttabdev, blkdev->sring, 1);
+        blkdev->cnt_map--;
+        blkdev->sring = NULL;
+    }
+}
+
+static int blk_free(struct XenDevice *xendev)
+{
+    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
+    struct ioreq *ioreq;
+
+    if (blkdev->bs || blkdev->sring) {
+        blk_disconnect(xendev);
+    }
+
+    /* Free persistent grants */
+    if (blkdev->feature_persistent) {
+        g_tree_destroy(blkdev->persistent_gnts);
+    }
+
+    while (!QLIST_EMPTY(&blkdev->freelist)) {
+        ioreq = QLIST_FIRST(&blkdev->freelist);
+        QLIST_REMOVE(ioreq, list);
+        qemu_iovec_destroy(&ioreq->v);
+        g_free(ioreq);
+    }
+
+    g_free(blkdev->params);
+    g_free(blkdev->mode);
+    g_free(blkdev->type);
+    g_free(blkdev->dev);
+    g_free(blkdev->devtype);
+    qemu_bh_delete(blkdev->bh);
+    return 0;
+}
+
+static void blk_event(struct XenDevice *xendev)
+{
+    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
+
+    qemu_bh_schedule(blkdev->bh);
+}
+
+struct XenDevOps xen_blkdev_ops = {
+    .size       = sizeof(struct XenBlkDev),
+    .flags      = DEVOPS_FLAG_NEED_GNTDEV,
+    .alloc      = blk_alloc,
+    .init       = blk_init,
+    .initialise    = blk_connect,
+    .disconnect = blk_disconnect,
+    .event      = blk_event,
+    .free       = blk_free,
+};
diff --git a/hw/bt-hci-csr.c b/hw/bt-hci-csr.c
deleted file mode 100644 (file)
index 55c819b..0000000
+++ /dev/null
@@ -1,454 +0,0 @@
-/*
- * Bluetooth serial HCI transport.
- * CSR41814 HCI with H4p vendor extensions.
- *
- * Copyright (C) 2008 Andrzej Zaborowski  <balrog@zabor.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 or
- * (at your option) version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "qemu-common.h"
-#include "char/char.h"
-#include "qemu/timer.h"
-#include "hw/irq.h"
-#include "bt/bt.h"
-#include "hw/bt.h"
-
-struct csrhci_s {
-    int enable;
-    qemu_irq *pins;
-    int pin_state;
-    int modem_state;
-    CharDriverState chr;
-#define FIFO_LEN       4096
-    int out_start;
-    int out_len;
-    int out_size;
-    uint8_t outfifo[FIFO_LEN * 2];
-    uint8_t inpkt[FIFO_LEN];
-    int in_len;
-    int in_hdr;
-    int in_data;
-    QEMUTimer *out_tm;
-    int64_t baud_delay;
-
-    bdaddr_t bd_addr;
-    struct HCIInfo *hci;
-};
-
-/* H4+ packet types */
-enum {
-    H4_CMD_PKT   = 1,
-    H4_ACL_PKT   = 2,
-    H4_SCO_PKT   = 3,
-    H4_EVT_PKT   = 4,
-    H4_NEG_PKT   = 6,
-    H4_ALIVE_PKT = 7,
-};
-
-/* CSR41814 negotiation start magic packet */
-static const uint8_t csrhci_neg_packet[] = {
-    H4_NEG_PKT, 10,
-    0x00, 0xa0, 0x01, 0x00, 0x00,
-    0x4c, 0x00, 0x96, 0x00, 0x00,
-};
-
-/* CSR41814 vendor-specific command OCFs */
-enum {
-    OCF_CSR_SEND_FIRMWARE = 0x000,
-};
-
-static inline void csrhci_fifo_wake(struct csrhci_s *s)
-{
-    if (!s->enable || !s->out_len)
-        return;
-
-    /* XXX: Should wait for s->modem_state & CHR_TIOCM_RTS? */
-    if (s->chr.chr_can_read && s->chr.chr_can_read(s->chr.handler_opaque) &&
-                    s->chr.chr_read) {
-        s->chr.chr_read(s->chr.handler_opaque,
-                        s->outfifo + s->out_start ++, 1);
-        s->out_len --;
-        if (s->out_start >= s->out_size) {
-            s->out_start = 0;
-            s->out_size = FIFO_LEN;
-        }
-    }
-
-    if (s->out_len)
-        qemu_mod_timer(s->out_tm, qemu_get_clock_ns(vm_clock) + s->baud_delay);
-}
-
-#define csrhci_out_packetz(s, len) memset(csrhci_out_packet(s, len), 0, len)
-static uint8_t *csrhci_out_packet(struct csrhci_s *s, int len)
-{
-    int off = s->out_start + s->out_len;
-
-    /* TODO: do the padding here, i.e. align len */
-    s->out_len += len;
-
-    if (off < FIFO_LEN) {
-        if (off + len > FIFO_LEN && (s->out_size = off + len) > FIFO_LEN * 2) {
-            fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len);
-            exit(-1);
-        }
-        return s->outfifo + off;
-    }
-
-    if (s->out_len > s->out_size) {
-        fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len);
-        exit(-1);
-    }
-
-    return s->outfifo + off - s->out_size;
-}
-
-static inline uint8_t *csrhci_out_packet_csr(struct csrhci_s *s,
-                int type, int len)
-{
-    uint8_t *ret = csrhci_out_packetz(s, len + 2);
-
-    *ret ++ = type;
-    *ret ++ = len;
-
-    return ret;
-}
-
-static inline uint8_t *csrhci_out_packet_event(struct csrhci_s *s,
-                int evt, int len)
-{
-    uint8_t *ret = csrhci_out_packetz(s,
-                    len + 1 + sizeof(struct hci_event_hdr));
-
-    *ret ++ = H4_EVT_PKT;
-    ((struct hci_event_hdr *) ret)->evt = evt;
-    ((struct hci_event_hdr *) ret)->plen = len;
-
-    return ret + sizeof(struct hci_event_hdr);
-}
-
-static void csrhci_in_packet_vendor(struct csrhci_s *s, int ocf,
-                uint8_t *data, int len)
-{
-    int offset;
-    uint8_t *rpkt;
-
-    switch (ocf) {
-    case OCF_CSR_SEND_FIRMWARE:
-        /* Check if this is the bd_address packet */
-        if (len >= 18 + 8 && data[12] == 0x01 && data[13] == 0x00) {
-            offset = 18;
-            s->bd_addr.b[0] = data[offset + 7];        /* Beyond cmd packet end(!?) */
-            s->bd_addr.b[1] = data[offset + 6];
-            s->bd_addr.b[2] = data[offset + 4];
-            s->bd_addr.b[3] = data[offset + 0];
-            s->bd_addr.b[4] = data[offset + 3];
-            s->bd_addr.b[5] = data[offset + 2];
-
-            s->hci->bdaddr_set(s->hci, s->bd_addr.b);
-            fprintf(stderr, "%s: bd_address loaded from firmware: "
-                            "%02x:%02x:%02x:%02x:%02x:%02x\n", __FUNCTION__,
-                            s->bd_addr.b[0], s->bd_addr.b[1], s->bd_addr.b[2],
-                            s->bd_addr.b[3], s->bd_addr.b[4], s->bd_addr.b[5]);
-        }
-
-        rpkt = csrhci_out_packet_event(s, EVT_VENDOR, 11);
-        /* Status bytes: no error */
-        rpkt[9] = 0x00;
-        rpkt[10] = 0x00;
-        break;
-
-    default:
-        fprintf(stderr, "%s: got a bad CMD packet\n", __FUNCTION__);
-        return;
-    }
-
-    csrhci_fifo_wake(s);
-}
-
-static void csrhci_in_packet(struct csrhci_s *s, uint8_t *pkt)
-{
-    uint8_t *rpkt;
-    int opc;
-
-    switch (*pkt ++) {
-    case H4_CMD_PKT:
-        opc = le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode);
-        if (cmd_opcode_ogf(opc) == OGF_VENDOR_CMD) {
-            csrhci_in_packet_vendor(s, cmd_opcode_ocf(opc),
-                            pkt + sizeof(struct hci_command_hdr),
-                            s->in_len - sizeof(struct hci_command_hdr) - 1);
-            return;
-        }
-
-        /* TODO: if the command is OCF_READ_LOCAL_COMMANDS or the likes,
-         * we need to send it to the HCI layer and then add our supported
-         * commands to the returned mask (such as OGF_VENDOR_CMD).  With
-         * bt-hci.c we could just have hooks for this kind of commands but
-         * we can't with bt-host.c.  */
-
-        s->hci->cmd_send(s->hci, pkt, s->in_len - 1);
-        break;
-
-    case H4_EVT_PKT:
-        goto bad_pkt;
-
-    case H4_ACL_PKT:
-        s->hci->acl_send(s->hci, pkt, s->in_len - 1);
-        break;
-
-    case H4_SCO_PKT:
-        s->hci->sco_send(s->hci, pkt, s->in_len - 1);
-        break;
-
-    case H4_NEG_PKT:
-        if (s->in_hdr != sizeof(csrhci_neg_packet) ||
-                        memcmp(pkt - 1, csrhci_neg_packet, s->in_hdr)) {
-            fprintf(stderr, "%s: got a bad NEG packet\n", __FUNCTION__);
-            return;
-        }
-        pkt += 2;
-
-        rpkt = csrhci_out_packet_csr(s, H4_NEG_PKT, 10);
-
-        *rpkt ++ = 0x20;       /* Operational settings negotiation Ok */
-        memcpy(rpkt, pkt, 7); rpkt += 7;
-        *rpkt ++ = 0xff;
-        *rpkt = 0xff;
-        break;
-
-    case H4_ALIVE_PKT:
-        if (s->in_hdr != 4 || pkt[1] != 0x55 || pkt[2] != 0x00) {
-            fprintf(stderr, "%s: got a bad ALIVE packet\n", __FUNCTION__);
-            return;
-        }
-
-        rpkt = csrhci_out_packet_csr(s, H4_ALIVE_PKT, 2);
-
-        *rpkt ++ = 0xcc;
-        *rpkt = 0x00;
-        break;
-
-    default:
-    bad_pkt:
-        /* TODO: error out */
-        fprintf(stderr, "%s: got a bad packet\n", __FUNCTION__);
-        break;
-    }
-
-    csrhci_fifo_wake(s);
-}
-
-static int csrhci_header_len(const uint8_t *pkt)
-{
-    switch (pkt[0]) {
-    case H4_CMD_PKT:
-        return HCI_COMMAND_HDR_SIZE;
-    case H4_EVT_PKT:
-        return HCI_EVENT_HDR_SIZE;
-    case H4_ACL_PKT:
-        return HCI_ACL_HDR_SIZE;
-    case H4_SCO_PKT:
-        return HCI_SCO_HDR_SIZE;
-    case H4_NEG_PKT:
-        return pkt[1] + 1;
-    case H4_ALIVE_PKT:
-        return 3;
-    }
-
-    exit(-1);
-}
-
-static int csrhci_data_len(const uint8_t *pkt)
-{
-    switch (*pkt ++) {
-    case H4_CMD_PKT:
-        /* It seems that vendor-specific command packets for H4+ are all
-         * one byte longer than indicated in the standard header.  */
-        if (le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode) == 0xfc00)
-            return (((struct hci_command_hdr *) pkt)->plen + 1) & ~1;
-
-        return ((struct hci_command_hdr *) pkt)->plen;
-    case H4_EVT_PKT:
-        return ((struct hci_event_hdr *) pkt)->plen;
-    case H4_ACL_PKT:
-        return le16_to_cpu(((struct hci_acl_hdr *) pkt)->dlen);
-    case H4_SCO_PKT:
-        return ((struct hci_sco_hdr *) pkt)->dlen;
-    case H4_NEG_PKT:
-    case H4_ALIVE_PKT:
-        return 0;
-    }
-
-    exit(-1);
-}
-
-static int csrhci_write(struct CharDriverState *chr,
-                const uint8_t *buf, int len)
-{
-    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
-    int plen = s->in_len;
-
-    if (!s->enable)
-        return 0;
-
-    s->in_len += len;
-    memcpy(s->inpkt + plen, buf, len);
-
-    while (1) {
-        if (s->in_len >= 2 && plen < 2)
-            s->in_hdr = csrhci_header_len(s->inpkt) + 1;
-
-        if (s->in_len >= s->in_hdr && plen < s->in_hdr)
-            s->in_data = csrhci_data_len(s->inpkt) + s->in_hdr;
-
-        if (s->in_len >= s->in_data) {
-            csrhci_in_packet(s, s->inpkt);
-
-            memmove(s->inpkt, s->inpkt + s->in_len, s->in_len - s->in_data);
-            s->in_len -= s->in_data;
-            s->in_hdr = INT_MAX;
-            s->in_data = INT_MAX;
-            plen = 0;
-        } else
-            break;
-    }
-
-    return len;
-}
-
-static void csrhci_out_hci_packet_event(void *opaque,
-                const uint8_t *data, int len)
-{
-    struct csrhci_s *s = (struct csrhci_s *) opaque;
-    uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1);       /* Align */
-
-    *pkt ++ = H4_EVT_PKT;
-    memcpy(pkt, data, len);
-
-    csrhci_fifo_wake(s);
-}
-
-static void csrhci_out_hci_packet_acl(void *opaque,
-                const uint8_t *data, int len)
-{
-    struct csrhci_s *s = (struct csrhci_s *) opaque;
-    uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1);       /* Align */
-
-    *pkt ++ = H4_ACL_PKT;
-    pkt[len & ~1] = 0;
-    memcpy(pkt, data, len);
-
-    csrhci_fifo_wake(s);
-}
-
-static int csrhci_ioctl(struct CharDriverState *chr, int cmd, void *arg)
-{
-    QEMUSerialSetParams *ssp;
-    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
-    int prev_state = s->modem_state;
-
-    switch (cmd) {
-    case CHR_IOCTL_SERIAL_SET_PARAMS:
-        ssp = (QEMUSerialSetParams *) arg;
-        s->baud_delay = get_ticks_per_sec() / ssp->speed;
-        /* Moments later... (but shorter than 100ms) */
-        s->modem_state |= CHR_TIOCM_CTS;
-        break;
-
-    case CHR_IOCTL_SERIAL_GET_TIOCM:
-        *(int *) arg = s->modem_state;
-        break;
-
-    case CHR_IOCTL_SERIAL_SET_TIOCM:
-        s->modem_state = *(int *) arg;
-        if (~s->modem_state & prev_state & CHR_TIOCM_RTS)
-            s->modem_state &= ~CHR_TIOCM_CTS;
-        break;
-
-    default:
-        return -ENOTSUP;
-    }
-    return 0;
-}
-
-static void csrhci_reset(struct csrhci_s *s)
-{
-    s->out_len = 0;
-    s->out_size = FIFO_LEN;
-    s->in_len = 0;
-    s->baud_delay = get_ticks_per_sec();
-    s->enable = 0;
-    s->in_hdr = INT_MAX;
-    s->in_data = INT_MAX;
-
-    s->modem_state = 0;
-    /* After a while... (but sooner than 10ms) */
-    s->modem_state |= CHR_TIOCM_CTS;
-
-    memset(&s->bd_addr, 0, sizeof(bdaddr_t));
-}
-
-static void csrhci_out_tick(void *opaque)
-{
-    csrhci_fifo_wake((struct csrhci_s *) opaque);
-}
-
-static void csrhci_pins(void *opaque, int line, int level)
-{
-    struct csrhci_s *s = (struct csrhci_s *) opaque;
-    int state = s->pin_state;
-
-    s->pin_state &= ~(1 << line);
-    s->pin_state |= (!!level) << line;
-
-    if ((state & ~s->pin_state) & (1 << csrhci_pin_reset)) {
-        /* TODO: Disappear from lower layers */
-        csrhci_reset(s);
-    }
-
-    if (s->pin_state == 3 && state != 3) {
-        s->enable = 1;
-        /* TODO: Wake lower layers up */
-    }
-}
-
-qemu_irq *csrhci_pins_get(CharDriverState *chr)
-{
-    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
-
-    return s->pins;
-}
-
-CharDriverState *uart_hci_init(qemu_irq wakeup)
-{
-    struct csrhci_s *s = (struct csrhci_s *)
-            g_malloc0(sizeof(struct csrhci_s));
-
-    s->chr.opaque = s;
-    s->chr.chr_write = csrhci_write;
-    s->chr.chr_ioctl = csrhci_ioctl;
-    s->chr.avail_connections = 1;
-
-    s->hci = qemu_next_hci();
-    s->hci->opaque = s;
-    s->hci->evt_recv = csrhci_out_hci_packet_event;
-    s->hci->acl_recv = csrhci_out_hci_packet_acl;
-
-    s->out_tm = qemu_new_timer_ns(vm_clock, csrhci_out_tick, s);
-    s->pins = qemu_allocate_irqs(csrhci_pins, s, __csrhci_pins);
-    csrhci_reset(s);
-
-    return &s->chr;
-}
diff --git a/hw/bt-hci.c b/hw/bt-hci.c
deleted file mode 100644 (file)
index a76edea..0000000
+++ /dev/null
@@ -1,2217 +0,0 @@
-/*
- * QEMU Bluetooth HCI logic.
- *
- * Copyright (C) 2007 OpenMoko, Inc.
- * Copyright (C) 2008 Andrzej Zaborowski  <balrog@zabor.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "qemu-common.h"
-#include "qemu/timer.h"
-#include "hw/usb.h"
-#include "bt/bt.h"
-#include "hw/bt.h"
-
-struct bt_hci_s {
-    uint8_t *(*evt_packet)(void *opaque);
-    void (*evt_submit)(void *opaque, int len);
-    void *opaque;
-    uint8_t evt_buf[256];
-
-    uint8_t acl_buf[4096];
-    int acl_len;
-
-    uint16_t asb_handle;
-    uint16_t psb_handle;
-
-    int last_cmd;      /* Note: Always little-endian */
-
-    struct bt_device_s *conn_req_host;
-
-    struct {
-        int inquire;
-        int periodic;
-        int responses_left;
-        int responses;
-        QEMUTimer *inquiry_done;
-        QEMUTimer *inquiry_next;
-        int inquiry_length;
-        int inquiry_period;
-        int inquiry_mode;
-
-#define HCI_HANDLE_OFFSET      0x20
-#define HCI_HANDLES_MAX                0x10
-        struct bt_hci_master_link_s {
-            struct bt_link_s *link;
-            void (*lmp_acl_data)(struct bt_link_s *link,
-                            const uint8_t *data, int start, int len);
-            QEMUTimer *acl_mode_timer;
-        } handle[HCI_HANDLES_MAX];
-        uint32_t role_bmp;
-        int last_handle;
-        int connecting;
-        bdaddr_t awaiting_bdaddr[HCI_HANDLES_MAX];
-    } lm;
-
-    uint8_t event_mask[8];
-    uint16_t voice_setting;    /* Notw: Always little-endian */
-    uint16_t conn_accept_tout;
-    QEMUTimer *conn_accept_timer;
-
-    struct HCIInfo info;
-    struct bt_device_s device;
-};
-
-#define DEFAULT_RSSI_DBM       20
-
-#define hci_from_info(ptr)     container_of((ptr), struct bt_hci_s, info)
-#define hci_from_device(ptr)   container_of((ptr), struct bt_hci_s, device)
-
-struct bt_hci_link_s {
-    struct bt_link_s btlink;
-    uint16_t handle;   /* Local */
-};
-
-/* LMP layer emulation */
-#if 0
-static void bt_submit_lmp(struct bt_device_s *bt, int length, uint8_t *data)
-{
-    int resp, resplen, error, op, tr;
-    uint8_t respdata[17];
-
-    if (length < 1)
-        return;
-
-    tr = *data & 1;
-    op = *(data ++) >> 1;
-    resp = LMP_ACCEPTED;
-    resplen = 2;
-    respdata[1] = op;
-    error = 0;
-    length --;
-
-    if (op >= 0x7c) {  /* Extended opcode */
-        op |= *(data ++) << 8;
-        resp = LMP_ACCEPTED_EXT;
-        resplen = 4;
-        respdata[0] = op >> 8;
-        respdata[1] = op & 0xff;
-        length --;
-    }
-
-    switch (op) {
-    case LMP_ACCEPTED:
-        /* data[0]     Op code
-         */
-        if (length < 1) {
-            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
-            goto not_accepted;
-        }
-        resp = 0;
-        break;
-
-    case LMP_ACCEPTED_EXT:
-        /* data[0]     Escape op code
-         * data[1]     Extended op code
-         */
-        if (length < 2) {
-            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
-            goto not_accepted;
-        }
-        resp = 0;
-        break;
-
-    case LMP_NOT_ACCEPTED:
-        /* data[0]     Op code
-         * data[1]     Error code
-         */
-        if (length < 2) {
-            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
-            goto not_accepted;
-        }
-        resp = 0;
-        break;
-
-    case LMP_NOT_ACCEPTED_EXT:
-        /* data[0]     Op code
-         * data[1]     Extended op code
-         * data[2]     Error code
-         */
-        if (length < 3) {
-            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
-            goto not_accepted;
-        }
-        resp = 0;
-        break;
-
-    case LMP_HOST_CONNECTION_REQ:
-        break;
-
-    case LMP_SETUP_COMPLETE:
-        resp = LMP_SETUP_COMPLETE;
-        resplen = 1;
-        bt->setup = 1;
-        break;
-
-    case LMP_DETACH:
-        /* data[0]     Error code
-         */
-        if (length < 1) {
-            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
-            goto not_accepted;
-        }
-        bt->setup = 0;
-        resp = 0;
-        break;
-
-    case LMP_SUPERVISION_TIMEOUT:
-        /* data[0,1]   Supervision timeout
-         */
-        if (length < 2) {
-            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
-            goto not_accepted;
-        }
-        resp = 0;
-        break;
-
-    case LMP_QUALITY_OF_SERVICE:
-        resp = 0;
-        /* Fall through */
-    case LMP_QOS_REQ:
-        /* data[0,1]   Poll interval
-         * data[2]     N(BC)
-         */
-        if (length < 3) {
-            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
-            goto not_accepted;
-        }
-        break;
-
-    case LMP_MAX_SLOT:
-        resp = 0;
-        /* Fall through */
-    case LMP_MAX_SLOT_REQ:
-        /* data[0]     Max slots
-         */
-        if (length < 1) {
-            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
-            goto not_accepted;
-        }
-        break;
-
-    case LMP_AU_RAND:
-    case LMP_IN_RAND:
-    case LMP_COMB_KEY:
-        /* data[0-15]  Random number
-         */
-        if (length < 16) {
-            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
-            goto not_accepted;
-        }
-        if (op == LMP_AU_RAND) {
-            if (bt->key_present) {
-                resp = LMP_SRES;
-                resplen = 5;
-                /* XXX: [Part H] Section 6.1 on page 801 */
-            } else {
-                error = HCI_PIN_OR_KEY_MISSING;
-                goto not_accepted;
-            }
-        } else if (op == LMP_IN_RAND) {
-            error = HCI_PAIRING_NOT_ALLOWED;
-            goto not_accepted;
-        } else {
-            /* XXX: [Part H] Section 3.2 on page 779 */
-            resp = LMP_UNIT_KEY;
-            resplen = 17;
-            memcpy(respdata + 1, bt->key, 16);
-
-            error = HCI_UNIT_LINK_KEY_USED;
-            goto not_accepted;
-        }
-        break;
-
-    case LMP_UNIT_KEY:
-        /* data[0-15]  Key
-         */
-        if (length < 16) {
-            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
-            goto not_accepted;
-        }
-        memcpy(bt->key, data, 16);
-        bt->key_present = 1;
-        break;
-
-    case LMP_SRES:
-        /* data[0-3]   Authentication response
-         */
-        if (length < 4) {
-            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
-            goto not_accepted;
-        }
-        break;
-
-    case LMP_CLKOFFSET_REQ:
-        resp = LMP_CLKOFFSET_RES;
-        resplen = 3;
-        respdata[1] = 0x33;
-        respdata[2] = 0x33;
-        break;
-
-    case LMP_CLKOFFSET_RES:
-        /* data[0,1]   Clock offset
-         * (Slave to master only)
-         */
-        if (length < 2) {
-            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
-            goto not_accepted;
-        }
-        break;
-
-    case LMP_VERSION_REQ:
-    case LMP_VERSION_RES:
-        /* data[0]     VersNr
-         * data[1,2]   CompId
-         * data[3,4]   SubVersNr
-         */
-        if (length < 5) {
-            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
-            goto not_accepted;
-        }
-        if (op == LMP_VERSION_REQ) {
-            resp = LMP_VERSION_RES;
-            resplen = 6;
-            respdata[1] = 0x20;
-            respdata[2] = 0xff;
-            respdata[3] = 0xff;
-            respdata[4] = 0xff;
-            respdata[5] = 0xff;
-        } else
-            resp = 0;
-        break;
-
-    case LMP_FEATURES_REQ:
-    case LMP_FEATURES_RES:
-        /* data[0-7]   Features
-         */
-        if (length < 8) {
-            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
-            goto not_accepted;
-        }
-        if (op == LMP_FEATURES_REQ) {
-            resp = LMP_FEATURES_RES;
-            resplen = 9;
-            respdata[1] = (bt->lmp_caps >> 0) & 0xff;
-            respdata[2] = (bt->lmp_caps >> 8) & 0xff;
-            respdata[3] = (bt->lmp_caps >> 16) & 0xff;
-            respdata[4] = (bt->lmp_caps >> 24) & 0xff;
-            respdata[5] = (bt->lmp_caps >> 32) & 0xff;
-            respdata[6] = (bt->lmp_caps >> 40) & 0xff;
-            respdata[7] = (bt->lmp_caps >> 48) & 0xff;
-            respdata[8] = (bt->lmp_caps >> 56) & 0xff;
-        } else
-            resp = 0;
-        break;
-
-    case LMP_NAME_REQ:
-        /* data[0]     Name offset
-         */
-        if (length < 1) {
-            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
-            goto not_accepted;
-        }
-        resp = LMP_NAME_RES;
-        resplen = 17;
-        respdata[1] = data[0];
-        respdata[2] = strlen(bt->lmp_name);
-        memset(respdata + 3, 0x00, 14);
-        if (respdata[2] > respdata[1])
-            memcpy(respdata + 3, bt->lmp_name + respdata[1],
-                            respdata[2] - respdata[1]);
-        break;
-
-    case LMP_NAME_RES:
-        /* data[0]     Name offset
-         * data[1]     Name length
-         * data[2-15]  Name fragment
-         */
-        if (length < 16) {
-            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
-            goto not_accepted;
-        }
-        resp = 0;
-        break;
-
-    default:
-        error = HCI_UNKNOWN_LMP_PDU;
-        /* Fall through */
-    not_accepted:
-        if (op >> 8) {
-            resp = LMP_NOT_ACCEPTED_EXT;
-            resplen = 5;
-            respdata[0] = op >> 8;
-            respdata[1] = op & 0xff;
-            respdata[2] = error;
-        } else {
-            resp = LMP_NOT_ACCEPTED;
-            resplen = 3;
-            respdata[0] = op & 0xff;
-            respdata[1] = error;
-        }
-    }
-
-    if (resp == 0)
-        return;
-
-    if (resp >> 8) {
-        respdata[0] = resp >> 8;
-        respdata[1] = resp & 0xff;
-    } else
-        respdata[0] = resp & 0xff;
-
-    respdata[0] <<= 1;
-    respdata[0] |= tr;
-}
-
-static void bt_submit_raw_acl(struct bt_piconet_s *net, int length, uint8_t *data)
-{
-    struct bt_device_s *slave;
-    if (length < 1)
-        return;
-
-    slave = 0;
-#if 0
-    slave = net->slave;
-#endif
-
-    switch (data[0] & 3) {
-    case LLID_ACLC:
-        bt_submit_lmp(slave, length - 1, data + 1);
-        break;
-    case LLID_ACLU_START:
-#if 0
-        bt_sumbit_l2cap(slave, length - 1, data + 1, (data[0] >> 2) & 1);
-        breka;
-#endif
-    default:
-    case LLID_ACLU_CONT:
-        break;
-    }
-}
-#endif
-
-/* HCI layer emulation */
-
-/* Note: we could ignore endiannes because unswapped handles will still
- * be valid as connection identifiers for the guest - they don't have to
- * be continuously allocated.  We do it though, to preserve similar
- * behaviour between hosts.  Some things, like the BD_ADDR cannot be
- * preserved though (for example if a real hci is used).  */
-#ifdef HOST_WORDS_BIGENDIAN
-# define HNDL(raw)     bswap16(raw)
-#else
-# define HNDL(raw)     (raw)
-#endif
-
-static const uint8_t bt_event_reserved_mask[8] = {
-    0xff, 0x9f, 0xfb, 0xff, 0x07, 0x18, 0x00, 0x00,
-};
-
-static inline uint8_t *bt_hci_event_start(struct bt_hci_s *hci,
-                int evt, int len)
-{
-    uint8_t *packet, mask;
-    int mask_byte;
-
-    if (len > 255) {
-        fprintf(stderr, "%s: HCI event params too long (%ib)\n",
-                        __FUNCTION__, len);
-        exit(-1);
-    }
-
-    mask_byte = (evt - 1) >> 3;
-    mask = 1 << ((evt - 1) & 3);
-    if (mask & bt_event_reserved_mask[mask_byte] & ~hci->event_mask[mask_byte])
-        return NULL;
-
-    packet = hci->evt_packet(hci->opaque);
-    packet[0] = evt;
-    packet[1] = len;
-
-    return &packet[2];
-}
-
-static inline void bt_hci_event(struct bt_hci_s *hci, int evt,
-                void *params, int len)
-{
-    uint8_t *packet = bt_hci_event_start(hci, evt, len);
-
-    if (!packet)
-        return;
-
-    if (len)
-        memcpy(packet, params, len);
-
-    hci->evt_submit(hci->opaque, len + 2);
-}
-
-static inline void bt_hci_event_status(struct bt_hci_s *hci, int status)
-{
-    evt_cmd_status params = {
-        .status        = status,
-        .ncmd  = 1,
-        .opcode        = hci->last_cmd,
-    };
-
-    bt_hci_event(hci, EVT_CMD_STATUS, &params, EVT_CMD_STATUS_SIZE);
-}
-
-static inline void bt_hci_event_complete(struct bt_hci_s *hci,
-                void *ret, int len)
-{
-    uint8_t *packet = bt_hci_event_start(hci, EVT_CMD_COMPLETE,
-                    len + EVT_CMD_COMPLETE_SIZE);
-    evt_cmd_complete *params = (evt_cmd_complete *) packet;
-
-    if (!packet)
-        return;
-
-    params->ncmd       = 1;
-    params->opcode     = hci->last_cmd;
-    if (len)
-        memcpy(&packet[EVT_CMD_COMPLETE_SIZE], ret, len);
-
-    hci->evt_submit(hci->opaque, len + EVT_CMD_COMPLETE_SIZE + 2);
-}
-
-static void bt_hci_inquiry_done(void *opaque)
-{
-    struct bt_hci_s *hci = (struct bt_hci_s *) opaque;
-    uint8_t status = HCI_SUCCESS;
-
-    if (!hci->lm.periodic)
-        hci->lm.inquire = 0;
-
-    /* The specification is inconsistent about this one.  Page 565 reads
-     * "The event parameters of Inquiry Complete event will have a summary
-     * of the result from the Inquiry process, which reports the number of
-     * nearby Bluetooth devices that responded [so hci->responses].", but
-     * Event Parameters (see page 729) has only Status.  */
-    bt_hci_event(hci, EVT_INQUIRY_COMPLETE, &status, 1);
-}
-
-static void bt_hci_inquiry_result_standard(struct bt_hci_s *hci,
-                struct bt_device_s *slave)
-{
-    inquiry_info params = {
-        .num_responses         = 1,
-        .bdaddr                        = BAINIT(&slave->bd_addr),
-        .pscan_rep_mode                = 0x00, /* R0 */
-        .pscan_period_mode     = 0x00, /* P0 - deprecated */
-        .pscan_mode            = 0x00, /* Standard scan - deprecated */
-        .dev_class[0]          = slave->class[0],
-        .dev_class[1]          = slave->class[1],
-        .dev_class[2]          = slave->class[2],
-        /* TODO: return the clkoff *differenece* */
-        .clock_offset          = slave->clkoff,        /* Note: no swapping */
-    };
-
-    bt_hci_event(hci, EVT_INQUIRY_RESULT, &params, INQUIRY_INFO_SIZE);
-}
-
-static void bt_hci_inquiry_result_with_rssi(struct bt_hci_s *hci,
-                struct bt_device_s *slave)
-{
-    inquiry_info_with_rssi params = {
-        .num_responses         = 1,
-        .bdaddr                        = BAINIT(&slave->bd_addr),
-        .pscan_rep_mode                = 0x00, /* R0 */
-        .pscan_period_mode     = 0x00, /* P0 - deprecated */
-        .dev_class[0]          = slave->class[0],
-        .dev_class[1]          = slave->class[1],
-        .dev_class[2]          = slave->class[2],
-        /* TODO: return the clkoff *differenece* */
-        .clock_offset          = slave->clkoff,        /* Note: no swapping */
-        .rssi                  = DEFAULT_RSSI_DBM,
-    };
-
-    bt_hci_event(hci, EVT_INQUIRY_RESULT_WITH_RSSI,
-                    &params, INQUIRY_INFO_WITH_RSSI_SIZE);
-}
-
-static void bt_hci_inquiry_result(struct bt_hci_s *hci,
-                struct bt_device_s *slave)
-{
-    if (!slave->inquiry_scan || !hci->lm.responses_left)
-        return;
-
-    hci->lm.responses_left --;
-    hci->lm.responses ++;
-
-    switch (hci->lm.inquiry_mode) {
-    case 0x00:
-        bt_hci_inquiry_result_standard(hci, slave);
-        return;
-    case 0x01:
-        bt_hci_inquiry_result_with_rssi(hci, slave);
-        return;
-    default:
-        fprintf(stderr, "%s: bad inquiry mode %02x\n", __FUNCTION__,
-                        hci->lm.inquiry_mode);
-        exit(-1);
-    }
-}
-
-static void bt_hci_mod_timer_1280ms(QEMUTimer *timer, int period)
-{
-    qemu_mod_timer(timer, qemu_get_clock_ns(vm_clock) +
-                   muldiv64(period << 7, get_ticks_per_sec(), 100));
-}
-
-static void bt_hci_inquiry_start(struct bt_hci_s *hci, int length)
-{
-    struct bt_device_s *slave;
-
-    hci->lm.inquiry_length = length;
-    for (slave = hci->device.net->slave; slave; slave = slave->next)
-        /* Don't uncover ourselves.  */
-        if (slave != &hci->device)
-            bt_hci_inquiry_result(hci, slave);
-
-    /* TODO: register for a callback on a new device's addition to the
-     * scatternet so that if it's added before inquiry_length expires,
-     * an Inquiry Result is generated immediately.  Alternatively re-loop
-     * through the devices on the inquiry_length expiration and report
-     * devices not seen before.  */
-    if (hci->lm.responses_left)
-        bt_hci_mod_timer_1280ms(hci->lm.inquiry_done, hci->lm.inquiry_length);
-    else
-        bt_hci_inquiry_done(hci);
-
-    if (hci->lm.periodic)
-        bt_hci_mod_timer_1280ms(hci->lm.inquiry_next, hci->lm.inquiry_period);
-}
-
-static void bt_hci_inquiry_next(void *opaque)
-{
-    struct bt_hci_s *hci = (struct bt_hci_s *) opaque;
-
-    hci->lm.responses_left += hci->lm.responses;
-    hci->lm.responses = 0;
-    bt_hci_inquiry_start(hci,  hci->lm.inquiry_length);
-}
-
-static inline int bt_hci_handle_bad(struct bt_hci_s *hci, uint16_t handle)
-{
-    return !(handle & HCI_HANDLE_OFFSET) ||
-            handle >= (HCI_HANDLE_OFFSET | HCI_HANDLES_MAX) ||
-            !hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link;
-}
-
-static inline int bt_hci_role_master(struct bt_hci_s *hci, uint16_t handle)
-{
-    return !!(hci->lm.role_bmp & (1 << (handle & ~HCI_HANDLE_OFFSET)));
-}
-
-static inline struct bt_device_s *bt_hci_remote_dev(struct bt_hci_s *hci,
-                uint16_t handle)
-{
-    struct bt_link_s *link = hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link;
-
-    return bt_hci_role_master(hci, handle) ? link->slave : link->host;
-}
-
-static void bt_hci_mode_tick(void *opaque);
-static void bt_hci_lmp_link_establish(struct bt_hci_s *hci,
-                struct bt_link_s *link, int master)
-{
-    hci->lm.handle[hci->lm.last_handle].link = link;
-
-    if (master) {
-        /* We are the master side of an ACL link */
-        hci->lm.role_bmp |= 1 << hci->lm.last_handle;
-
-        hci->lm.handle[hci->lm.last_handle].lmp_acl_data =
-                link->slave->lmp_acl_data;
-    } else {
-        /* We are the slave side of an ACL link */
-        hci->lm.role_bmp &= ~(1 << hci->lm.last_handle);
-
-        hci->lm.handle[hci->lm.last_handle].lmp_acl_data =
-                link->host->lmp_acl_resp;
-    }
-
-    /* Mode */
-    if (master) {
-        link->acl_mode = acl_active;
-        hci->lm.handle[hci->lm.last_handle].acl_mode_timer =
-                qemu_new_timer_ns(vm_clock, bt_hci_mode_tick, link);
-    }
-}
-
-static void bt_hci_lmp_link_teardown(struct bt_hci_s *hci, uint16_t handle)
-{
-    handle &= ~HCI_HANDLE_OFFSET;
-    hci->lm.handle[handle].link = NULL;
-
-    if (bt_hci_role_master(hci, handle)) {
-        qemu_del_timer(hci->lm.handle[handle].acl_mode_timer);
-        qemu_free_timer(hci->lm.handle[handle].acl_mode_timer);
-    }
-}
-
-static int bt_hci_connect(struct bt_hci_s *hci, bdaddr_t *bdaddr)
-{
-    struct bt_device_s *slave;
-    struct bt_link_s link;
-
-    for (slave = hci->device.net->slave; slave; slave = slave->next)
-        if (slave->page_scan && !bacmp(&slave->bd_addr, bdaddr))
-            break;
-    if (!slave || slave == &hci->device)
-        return -ENODEV;
-
-    bacpy(&hci->lm.awaiting_bdaddr[hci->lm.connecting ++], &slave->bd_addr);
-
-    link.slave = slave;
-    link.host = &hci->device;
-    link.slave->lmp_connection_request(&link); /* Always last */
-
-    return 0;
-}
-
-static void bt_hci_connection_reject(struct bt_hci_s *hci,
-                struct bt_device_s *host, uint8_t because)
-{
-    struct bt_link_s link = {
-        .slave = &hci->device,
-        .host  = host,
-        /* Rest uninitialised */
-    };
-
-    host->reject_reason = because;
-    host->lmp_connection_complete(&link);
-}
-
-static void bt_hci_connection_reject_event(struct bt_hci_s *hci,
-                bdaddr_t *bdaddr)
-{
-    evt_conn_complete params;
-
-    params.status      = HCI_NO_CONNECTION;
-    params.handle      = 0;
-    bacpy(&params.bdaddr, bdaddr);
-    params.link_type   = ACL_LINK;
-    params.encr_mode   = 0x00;         /* Encryption not required */
-    bt_hci_event(hci, EVT_CONN_COMPLETE, &params, EVT_CONN_COMPLETE_SIZE);
-}
-
-static void bt_hci_connection_accept(struct bt_hci_s *hci,
-                struct bt_device_s *host)
-{
-    struct bt_hci_link_s *link = g_malloc0(sizeof(struct bt_hci_link_s));
-    evt_conn_complete params;
-    uint16_t handle;
-    uint8_t status = HCI_SUCCESS;
-    int tries = HCI_HANDLES_MAX;
-
-    /* Make a connection handle */
-    do {
-        while (hci->lm.handle[++ hci->lm.last_handle].link && -- tries)
-            hci->lm.last_handle &= HCI_HANDLES_MAX - 1;
-        handle = hci->lm.last_handle | HCI_HANDLE_OFFSET;
-    } while ((handle == hci->asb_handle || handle == hci->psb_handle) &&
-            tries);
-
-    if (!tries) {
-        g_free(link);
-        bt_hci_connection_reject(hci, host, HCI_REJECTED_LIMITED_RESOURCES);
-        status = HCI_NO_CONNECTION;
-        goto complete;
-    }
-
-    link->btlink.slave = &hci->device;
-    link->btlink.host  = host;
-    link->handle = handle;
-
-    /* Link established */
-    bt_hci_lmp_link_establish(hci, &link->btlink, 0);
-
-complete:
-    params.status      = status;
-    params.handle      = HNDL(handle);
-    bacpy(&params.bdaddr, &host->bd_addr);
-    params.link_type   = ACL_LINK;
-    params.encr_mode   = 0x00;         /* Encryption not required */
-    bt_hci_event(hci, EVT_CONN_COMPLETE, &params, EVT_CONN_COMPLETE_SIZE);
-
-    /* Neets to be done at the very end because it can trigger a (nested)
-     * disconnected, in case the other and had cancelled the request
-     * locally.  */
-    if (status == HCI_SUCCESS) {
-        host->reject_reason = 0;
-        host->lmp_connection_complete(&link->btlink);
-    }
-}
-
-static void bt_hci_lmp_connection_request(struct bt_link_s *link)
-{
-    struct bt_hci_s *hci = hci_from_device(link->slave);
-    evt_conn_request params;
-
-    if (hci->conn_req_host) {
-        bt_hci_connection_reject(hci, link->host,
-                                 HCI_REJECTED_LIMITED_RESOURCES);
-        return;
-    }
-    hci->conn_req_host = link->host;
-    /* TODO: if masked and auto-accept, then auto-accept,
-     * if masked and not auto-accept, then auto-reject */
-    /* TODO: kick the hci->conn_accept_timer, timeout after
-     * hci->conn_accept_tout * 0.625 msec */
-
-    bacpy(&params.bdaddr, &link->host->bd_addr);
-    memcpy(&params.dev_class, &link->host->class, sizeof(params.dev_class));
-    params.link_type   = ACL_LINK;
-    bt_hci_event(hci, EVT_CONN_REQUEST, &params, EVT_CONN_REQUEST_SIZE);
-}
-
-static void bt_hci_conn_accept_timeout(void *opaque)
-{
-    struct bt_hci_s *hci = (struct bt_hci_s *) opaque;
-
-    if (!hci->conn_req_host)
-        /* Already accepted or rejected.  If the other end cancelled the
-         * connection request then we still have to reject or accept it
-         * and then we'll get a disconnect.  */
-        return;
-
-    /* TODO */
-}
-
-/* Remove from the list of devices which we wanted to connect to and
- * are awaiting a response from.  If the callback sees a response from
- * a device which is not on the list it will assume it's a connection
- * that's been cancelled by the host in the meantime and immediately
- * try to detach the link and send a Connection Complete.  */
-static int bt_hci_lmp_connection_ready(struct bt_hci_s *hci,
-                bdaddr_t *bdaddr)
-{
-    int i;
-
-    for (i = 0; i < hci->lm.connecting; i ++)
-        if (!bacmp(&hci->lm.awaiting_bdaddr[i], bdaddr)) {
-            if (i < -- hci->lm.connecting)
-                bacpy(&hci->lm.awaiting_bdaddr[i],
-                                &hci->lm.awaiting_bdaddr[hci->lm.connecting]);
-            return 0;
-        }
-
-    return 1;
-}
-
-static void bt_hci_lmp_connection_complete(struct bt_link_s *link)
-{
-    struct bt_hci_s *hci = hci_from_device(link->host);
-    evt_conn_complete params;
-    uint16_t handle;
-    uint8_t status = HCI_SUCCESS;
-    int tries = HCI_HANDLES_MAX;
-
-    if (bt_hci_lmp_connection_ready(hci, &link->slave->bd_addr)) {
-        if (!hci->device.reject_reason)
-            link->slave->lmp_disconnect_slave(link);
-        handle = 0;
-        status = HCI_NO_CONNECTION;
-        goto complete;
-    }
-
-    if (hci->device.reject_reason) {
-        handle = 0;
-        status = hci->device.reject_reason;
-        goto complete;
-    }
-
-    /* Make a connection handle */
-    do {
-        while (hci->lm.handle[++ hci->lm.last_handle].link && -- tries)
-            hci->lm.last_handle &= HCI_HANDLES_MAX - 1;
-        handle = hci->lm.last_handle | HCI_HANDLE_OFFSET;
-    } while ((handle == hci->asb_handle || handle == hci->psb_handle) &&
-            tries);
-
-    if (!tries) {
-        link->slave->lmp_disconnect_slave(link);
-        status = HCI_NO_CONNECTION;
-        goto complete;
-    }
-
-    /* Link established */
-    link->handle = handle;
-    bt_hci_lmp_link_establish(hci, link, 1);
-
-complete:
-    params.status      = status;
-    params.handle      = HNDL(handle);
-    params.link_type   = ACL_LINK;
-    bacpy(&params.bdaddr, &link->slave->bd_addr);
-    params.encr_mode   = 0x00;         /* Encryption not required */
-    bt_hci_event(hci, EVT_CONN_COMPLETE, &params, EVT_CONN_COMPLETE_SIZE);
-}
-
-static void bt_hci_disconnect(struct bt_hci_s *hci,
-                uint16_t handle, int reason)
-{
-    struct bt_link_s *btlink =
-            hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link;
-    struct bt_hci_link_s *link;
-    evt_disconn_complete params;
-
-    if (bt_hci_role_master(hci, handle)) {
-        btlink->slave->reject_reason = reason;
-        btlink->slave->lmp_disconnect_slave(btlink);
-        /* The link pointer is invalid from now on */
-
-        goto complete;
-    }
-
-    btlink->host->reject_reason = reason;
-    btlink->host->lmp_disconnect_master(btlink);
-
-    /* We are the slave, we get to clean this burden */
-    link = (struct bt_hci_link_s *) btlink;
-    g_free(link);
-
-complete:
-    bt_hci_lmp_link_teardown(hci, handle);
-
-    params.status      = HCI_SUCCESS;
-    params.handle      = HNDL(handle);
-    params.reason      = HCI_CONNECTION_TERMINATED;
-    bt_hci_event(hci, EVT_DISCONN_COMPLETE,
-                    &params, EVT_DISCONN_COMPLETE_SIZE);
-}
-
-/* TODO: use only one function */
-static void bt_hci_lmp_disconnect_host(struct bt_link_s *link)
-{
-    struct bt_hci_s *hci = hci_from_device(link->host);
-    uint16_t handle = link->handle;
-    evt_disconn_complete params;
-
-    bt_hci_lmp_link_teardown(hci, handle);
-
-    params.status      = HCI_SUCCESS;
-    params.handle      = HNDL(handle);
-    params.reason      = hci->device.reject_reason;
-    bt_hci_event(hci, EVT_DISCONN_COMPLETE,
-                    &params, EVT_DISCONN_COMPLETE_SIZE);
-}
-
-static void bt_hci_lmp_disconnect_slave(struct bt_link_s *btlink)
-{
-    struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink;
-    struct bt_hci_s *hci = hci_from_device(btlink->slave);
-    uint16_t handle = link->handle;
-    evt_disconn_complete params;
-
-    g_free(link);
-
-    bt_hci_lmp_link_teardown(hci, handle);
-
-    params.status      = HCI_SUCCESS;
-    params.handle      = HNDL(handle);
-    params.reason      = hci->device.reject_reason;
-    bt_hci_event(hci, EVT_DISCONN_COMPLETE,
-                    &params, EVT_DISCONN_COMPLETE_SIZE);
-}
-
-static int bt_hci_name_req(struct bt_hci_s *hci, bdaddr_t *bdaddr)
-{
-    struct bt_device_s *slave;
-    evt_remote_name_req_complete params;
-
-    for (slave = hci->device.net->slave; slave; slave = slave->next)
-        if (slave->page_scan && !bacmp(&slave->bd_addr, bdaddr))
-            break;
-    if (!slave)
-        return -ENODEV;
-
-    bt_hci_event_status(hci, HCI_SUCCESS);
-
-    params.status       = HCI_SUCCESS;
-    bacpy(&params.bdaddr, &slave->bd_addr);
-    pstrcpy(params.name, sizeof(params.name), slave->lmp_name ?: "");
-    bt_hci_event(hci, EVT_REMOTE_NAME_REQ_COMPLETE,
-                    &params, EVT_REMOTE_NAME_REQ_COMPLETE_SIZE);
-
-    return 0;
-}
-
-static int bt_hci_features_req(struct bt_hci_s *hci, uint16_t handle)
-{
-    struct bt_device_s *slave;
-    evt_read_remote_features_complete params;
-
-    if (bt_hci_handle_bad(hci, handle))
-        return -ENODEV;
-
-    slave = bt_hci_remote_dev(hci, handle);
-
-    bt_hci_event_status(hci, HCI_SUCCESS);
-
-    params.status      = HCI_SUCCESS;
-    params.handle      = HNDL(handle);
-    params.features[0] = (slave->lmp_caps >>  0) & 0xff;
-    params.features[1] = (slave->lmp_caps >>  8) & 0xff;
-    params.features[2] = (slave->lmp_caps >> 16) & 0xff;
-    params.features[3] = (slave->lmp_caps >> 24) & 0xff;
-    params.features[4] = (slave->lmp_caps >> 32) & 0xff;
-    params.features[5] = (slave->lmp_caps >> 40) & 0xff;
-    params.features[6] = (slave->lmp_caps >> 48) & 0xff;
-    params.features[7] = (slave->lmp_caps >> 56) & 0xff;
-    bt_hci_event(hci, EVT_READ_REMOTE_FEATURES_COMPLETE,
-                    &params, EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE);
-
-    return 0;
-}
-
-static int bt_hci_version_req(struct bt_hci_s *hci, uint16_t handle)
-{
-    evt_read_remote_version_complete params;
-
-    if (bt_hci_handle_bad(hci, handle))
-        return -ENODEV;
-
-    bt_hci_remote_dev(hci, handle);
-
-    bt_hci_event_status(hci, HCI_SUCCESS);
-
-    params.status      = HCI_SUCCESS;
-    params.handle      = HNDL(handle);
-    params.lmp_ver     = 0x03;
-    params.manufacturer        = cpu_to_le16(0xa000);
-    params.lmp_subver  = cpu_to_le16(0xa607);
-    bt_hci_event(hci, EVT_READ_REMOTE_VERSION_COMPLETE,
-                    &params, EVT_READ_REMOTE_VERSION_COMPLETE_SIZE);
-
-    return 0;
-}
-
-static int bt_hci_clkoffset_req(struct bt_hci_s *hci, uint16_t handle)
-{
-    struct bt_device_s *slave;
-    evt_read_clock_offset_complete params;
-
-    if (bt_hci_handle_bad(hci, handle))
-        return -ENODEV;
-
-    slave = bt_hci_remote_dev(hci, handle);
-
-    bt_hci_event_status(hci, HCI_SUCCESS);
-
-    params.status      = HCI_SUCCESS;
-    params.handle      = HNDL(handle);
-    /* TODO: return the clkoff *differenece* */
-    params.clock_offset        = slave->clkoff;        /* Note: no swapping */
-    bt_hci_event(hci, EVT_READ_CLOCK_OFFSET_COMPLETE,
-                    &params, EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE);
-
-    return 0;
-}
-
-static void bt_hci_event_mode(struct bt_hci_s *hci, struct bt_link_s *link,
-                uint16_t handle)
-{
-    evt_mode_change params = {
-        .status                = HCI_SUCCESS,
-        .handle                = HNDL(handle),
-        .mode          = link->acl_mode,
-        .interval      = cpu_to_le16(link->acl_interval),
-    };
-
-    bt_hci_event(hci, EVT_MODE_CHANGE, &params, EVT_MODE_CHANGE_SIZE);
-}
-
-static void bt_hci_lmp_mode_change_master(struct bt_hci_s *hci,
-                struct bt_link_s *link, int mode, uint16_t interval)
-{
-    link->acl_mode = mode;
-    link->acl_interval = interval;
-
-    bt_hci_event_mode(hci, link, link->handle);
-
-    link->slave->lmp_mode_change(link);
-}
-
-static void bt_hci_lmp_mode_change_slave(struct bt_link_s *btlink)
-{
-    struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink;
-    struct bt_hci_s *hci = hci_from_device(btlink->slave);
-
-    bt_hci_event_mode(hci, btlink, link->handle);
-}
-
-static int bt_hci_mode_change(struct bt_hci_s *hci, uint16_t handle,
-                int interval, int mode)
-{
-    struct bt_hci_master_link_s *link;
-
-    if (bt_hci_handle_bad(hci, handle) || !bt_hci_role_master(hci, handle))
-        return -ENODEV;
-
-    link = &hci->lm.handle[handle & ~HCI_HANDLE_OFFSET];
-    if (link->link->acl_mode != acl_active) {
-        bt_hci_event_status(hci, HCI_COMMAND_DISALLOWED);
-        return 0;
-    }
-
-    bt_hci_event_status(hci, HCI_SUCCESS);
-
-    qemu_mod_timer(link->acl_mode_timer, qemu_get_clock_ns(vm_clock) +
-                   muldiv64(interval * 625, get_ticks_per_sec(), 1000000));
-    bt_hci_lmp_mode_change_master(hci, link->link, mode, interval);
-
-    return 0;
-}
-
-static int bt_hci_mode_cancel(struct bt_hci_s *hci, uint16_t handle, int mode)
-{
-    struct bt_hci_master_link_s *link;
-
-    if (bt_hci_handle_bad(hci, handle) || !bt_hci_role_master(hci, handle))
-        return -ENODEV;
-
-    link = &hci->lm.handle[handle & ~HCI_HANDLE_OFFSET];
-    if (link->link->acl_mode != mode) {
-        bt_hci_event_status(hci, HCI_COMMAND_DISALLOWED);
-
-        return 0;
-    }
-
-    bt_hci_event_status(hci, HCI_SUCCESS);
-
-    qemu_del_timer(link->acl_mode_timer);
-    bt_hci_lmp_mode_change_master(hci, link->link, acl_active, 0);
-
-    return 0;
-}
-
-static void bt_hci_mode_tick(void *opaque)
-{
-    struct bt_link_s *link = opaque;
-    struct bt_hci_s *hci = hci_from_device(link->host);
-
-    bt_hci_lmp_mode_change_master(hci, link, acl_active, 0);
-}
-
-static void bt_hci_reset(struct bt_hci_s *hci)
-{
-    hci->acl_len = 0;
-    hci->last_cmd = 0;
-    hci->lm.connecting = 0;
-
-    hci->event_mask[0] = 0xff;
-    hci->event_mask[1] = 0xff;
-    hci->event_mask[2] = 0xff;
-    hci->event_mask[3] = 0xff;
-    hci->event_mask[4] = 0xff;
-    hci->event_mask[5] = 0x1f;
-    hci->event_mask[6] = 0x00;
-    hci->event_mask[7] = 0x00;
-    hci->device.inquiry_scan = 0;
-    hci->device.page_scan = 0;
-    if (hci->device.lmp_name)
-        g_free((void *) hci->device.lmp_name);
-    hci->device.lmp_name = NULL;
-    hci->device.class[0] = 0x00;
-    hci->device.class[1] = 0x00;
-    hci->device.class[2] = 0x00;
-    hci->voice_setting = 0x0000;
-    hci->conn_accept_tout = 0x1f40;
-    hci->lm.inquiry_mode = 0x00;
-
-    hci->psb_handle = 0x000;
-    hci->asb_handle = 0x000;
-
-    /* XXX: qemu_del_timer(sl->acl_mode_timer); for all links */
-    qemu_del_timer(hci->lm.inquiry_done);
-    qemu_del_timer(hci->lm.inquiry_next);
-    qemu_del_timer(hci->conn_accept_timer);
-}
-
-static void bt_hci_read_local_version_rp(struct bt_hci_s *hci)
-{
-    read_local_version_rp lv = {
-        .status                = HCI_SUCCESS,
-        .hci_ver       = 0x03,
-        .hci_rev       = cpu_to_le16(0xa607),
-        .lmp_ver       = 0x03,
-        .manufacturer  = cpu_to_le16(0xa000),
-        .lmp_subver    = cpu_to_le16(0xa607),
-    };
-
-    bt_hci_event_complete(hci, &lv, READ_LOCAL_VERSION_RP_SIZE);
-}
-
-static void bt_hci_read_local_commands_rp(struct bt_hci_s *hci)
-{
-    read_local_commands_rp lc = {
-        .status                = HCI_SUCCESS,
-        .commands      = {
-            /* Keep updated! */
-            /* Also, keep in sync with hci->device.lmp_caps in bt_new_hci */
-            0xbf, 0x80, 0xf9, 0x03, 0xb2, 0xc0, 0x03, 0xc3,
-            0x00, 0x0f, 0x80, 0x00, 0xc0, 0x00, 0xe8, 0x13,
-            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        },
-    };
-
-    bt_hci_event_complete(hci, &lc, READ_LOCAL_COMMANDS_RP_SIZE);
-}
-
-static void bt_hci_read_local_features_rp(struct bt_hci_s *hci)
-{
-    read_local_features_rp lf = {
-        .status                = HCI_SUCCESS,
-        .features      = {
-            (hci->device.lmp_caps >>  0) & 0xff,
-            (hci->device.lmp_caps >>  8) & 0xff,
-            (hci->device.lmp_caps >> 16) & 0xff,
-            (hci->device.lmp_caps >> 24) & 0xff,
-            (hci->device.lmp_caps >> 32) & 0xff,
-            (hci->device.lmp_caps >> 40) & 0xff,
-            (hci->device.lmp_caps >> 48) & 0xff,
-            (hci->device.lmp_caps >> 56) & 0xff,
-        },
-    };
-
-    bt_hci_event_complete(hci, &lf, READ_LOCAL_FEATURES_RP_SIZE);
-}
-
-static void bt_hci_read_local_ext_features_rp(struct bt_hci_s *hci, int page)
-{
-    read_local_ext_features_rp lef = {
-        .status                = HCI_SUCCESS,
-        .page_num      = page,
-        .max_page_num  = 0x00,
-        .features      = {
-            /* Keep updated! */
-            0x5f, 0x35, 0x85, 0x7e, 0x9b, 0x19, 0x00, 0x80,
-        },
-    };
-    if (page)
-        memset(lef.features, 0, sizeof(lef.features));
-
-    bt_hci_event_complete(hci, &lef, READ_LOCAL_EXT_FEATURES_RP_SIZE);
-}
-
-static void bt_hci_read_buffer_size_rp(struct bt_hci_s *hci)
-{
-    read_buffer_size_rp bs = {
-        /* This can be made configurable, for one standard USB dongle HCI
-         * the four values are cpu_to_le16(0x0180), 0x40,
-         * cpu_to_le16(0x0008), cpu_to_le16(0x0008).  */
-        .status                = HCI_SUCCESS,
-        .acl_mtu       = cpu_to_le16(0x0200),
-        .sco_mtu       = 0,
-        .acl_max_pkt   = cpu_to_le16(0x0001),
-        .sco_max_pkt   = cpu_to_le16(0x0000),
-    };
-
-    bt_hci_event_complete(hci, &bs, READ_BUFFER_SIZE_RP_SIZE);
-}
-
-/* Deprecated in V2.0 (page 661) */
-static void bt_hci_read_country_code_rp(struct bt_hci_s *hci)
-{
-    read_country_code_rp cc ={
-        .status                = HCI_SUCCESS,
-        .country_code  = 0x00, /* North America & Europe^1 and Japan */
-    };
-
-    bt_hci_event_complete(hci, &cc, READ_COUNTRY_CODE_RP_SIZE);
-
-    /* ^1. Except France, sorry */
-}
-
-static void bt_hci_read_bd_addr_rp(struct bt_hci_s *hci)
-{
-    read_bd_addr_rp ba = {
-        .status = HCI_SUCCESS,
-        .bdaddr = BAINIT(&hci->device.bd_addr),
-    };
-
-    bt_hci_event_complete(hci, &ba, READ_BD_ADDR_RP_SIZE);
-}
-
-static int bt_hci_link_quality_rp(struct bt_hci_s *hci, uint16_t handle)
-{
-    read_link_quality_rp lq = {
-        .status                = HCI_SUCCESS,
-        .handle                = HNDL(handle),
-        .link_quality  = 0xff,
-    };
-
-    if (bt_hci_handle_bad(hci, handle))
-        lq.status = HCI_NO_CONNECTION;
-
-    bt_hci_event_complete(hci, &lq, READ_LINK_QUALITY_RP_SIZE);
-    return 0;
-}
-
-/* Generate a Command Complete event with only the Status parameter */
-static inline void bt_hci_event_complete_status(struct bt_hci_s *hci,
-                uint8_t status)
-{
-    bt_hci_event_complete(hci, &status, 1);
-}
-
-static inline void bt_hci_event_complete_conn_cancel(struct bt_hci_s *hci,
-                uint8_t status, bdaddr_t *bd_addr)
-{
-    create_conn_cancel_rp params = {
-        .status = status,
-        .bdaddr = BAINIT(bd_addr),
-    };
-
-    bt_hci_event_complete(hci, &params, CREATE_CONN_CANCEL_RP_SIZE);
-}
-
-static inline void bt_hci_event_auth_complete(struct bt_hci_s *hci,
-                uint16_t handle)
-{
-    evt_auth_complete params = {
-        .status = HCI_SUCCESS,
-        .handle = HNDL(handle),
-    };
-
-    bt_hci_event(hci, EVT_AUTH_COMPLETE, &params, EVT_AUTH_COMPLETE_SIZE);
-}
-
-static inline void bt_hci_event_encrypt_change(struct bt_hci_s *hci,
-                uint16_t handle, uint8_t mode)
-{
-    evt_encrypt_change params = {
-        .status                = HCI_SUCCESS,
-        .handle                = HNDL(handle),
-        .encrypt       = mode,
-    };
-
-    bt_hci_event(hci, EVT_ENCRYPT_CHANGE, &params, EVT_ENCRYPT_CHANGE_SIZE);
-}
-
-static inline void bt_hci_event_complete_name_cancel(struct bt_hci_s *hci,
-                bdaddr_t *bd_addr)
-{
-    remote_name_req_cancel_rp params = {
-        .status = HCI_INVALID_PARAMETERS,
-        .bdaddr = BAINIT(bd_addr),
-    };
-
-    bt_hci_event_complete(hci, &params, REMOTE_NAME_REQ_CANCEL_RP_SIZE);
-}
-
-static inline void bt_hci_event_read_remote_ext_features(struct bt_hci_s *hci,
-                uint16_t handle)
-{
-    evt_read_remote_ext_features_complete params = {
-        .status = HCI_UNSUPPORTED_FEATURE,
-        .handle = HNDL(handle),
-        /* Rest uninitialised */
-    };
-
-    bt_hci_event(hci, EVT_READ_REMOTE_EXT_FEATURES_COMPLETE,
-                    &params, EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE);
-}
-
-static inline void bt_hci_event_complete_lmp_handle(struct bt_hci_s *hci,
-                uint16_t handle)
-{
-    read_lmp_handle_rp params = {
-        .status                = HCI_NO_CONNECTION,
-        .handle                = HNDL(handle),
-        .reserved      = 0,
-        /* Rest uninitialised */
-    };
-
-    bt_hci_event_complete(hci, &params, READ_LMP_HANDLE_RP_SIZE);
-}
-
-static inline void bt_hci_event_complete_role_discovery(struct bt_hci_s *hci,
-                int status, uint16_t handle, int master)
-{
-    role_discovery_rp params = {
-        .status                = status,
-        .handle                = HNDL(handle),
-        .role          = master ? 0x00 : 0x01,
-    };
-
-    bt_hci_event_complete(hci, &params, ROLE_DISCOVERY_RP_SIZE);
-}
-
-static inline void bt_hci_event_complete_flush(struct bt_hci_s *hci,
-                int status, uint16_t handle)
-{
-    flush_rp params = {
-        .status                = status,
-        .handle                = HNDL(handle),
-    };
-
-    bt_hci_event_complete(hci, &params, FLUSH_RP_SIZE);
-}
-
-static inline void bt_hci_event_complete_read_local_name(struct bt_hci_s *hci)
-{
-    read_local_name_rp params;
-    params.status = HCI_SUCCESS;
-    memset(params.name, 0, sizeof(params.name));
-    if (hci->device.lmp_name)
-        pstrcpy(params.name, sizeof(params.name), hci->device.lmp_name);
-
-    bt_hci_event_complete(hci, &params, READ_LOCAL_NAME_RP_SIZE);
-}
-
-static inline void bt_hci_event_complete_read_conn_accept_timeout(
-                struct bt_hci_s *hci)
-{
-    read_conn_accept_timeout_rp params = {
-        .status                = HCI_SUCCESS,
-        .timeout       = cpu_to_le16(hci->conn_accept_tout),
-    };
-
-    bt_hci_event_complete(hci, &params, READ_CONN_ACCEPT_TIMEOUT_RP_SIZE);
-}
-
-static inline void bt_hci_event_complete_read_scan_enable(struct bt_hci_s *hci)
-{
-    read_scan_enable_rp params = {
-        .status = HCI_SUCCESS,
-        .enable =
-                (hci->device.inquiry_scan ? SCAN_INQUIRY : 0) |
-                (hci->device.page_scan ? SCAN_PAGE : 0),
-    };
-
-    bt_hci_event_complete(hci, &params, READ_SCAN_ENABLE_RP_SIZE);
-}
-
-static inline void bt_hci_event_complete_read_local_class(struct bt_hci_s *hci)
-{
-    read_class_of_dev_rp params;
-
-    params.status = HCI_SUCCESS;
-    memcpy(params.dev_class, hci->device.class, sizeof(params.dev_class));
-
-    bt_hci_event_complete(hci, &params, READ_CLASS_OF_DEV_RP_SIZE);
-}
-
-static inline void bt_hci_event_complete_voice_setting(struct bt_hci_s *hci)
-{
-    read_voice_setting_rp params = {
-        .status                = HCI_SUCCESS,
-        .voice_setting = hci->voice_setting,   /* Note: no swapping */
-    };
-
-    bt_hci_event_complete(hci, &params, READ_VOICE_SETTING_RP_SIZE);
-}
-
-static inline void bt_hci_event_complete_read_inquiry_mode(
-                struct bt_hci_s *hci)
-{
-    read_inquiry_mode_rp params = {
-        .status                = HCI_SUCCESS,
-        .mode          = hci->lm.inquiry_mode,
-    };
-
-    bt_hci_event_complete(hci, &params, READ_INQUIRY_MODE_RP_SIZE);
-}
-
-static inline void bt_hci_event_num_comp_pkts(struct bt_hci_s *hci,
-                uint16_t handle, int packets)
-{
-    uint16_t buf[EVT_NUM_COMP_PKTS_SIZE(1) / 2 + 1];
-    evt_num_comp_pkts *params = (void *) ((uint8_t *) buf + 1);
-
-    params->num_hndl                   = 1;
-    params->connection->handle         = HNDL(handle);
-    params->connection->num_packets    = cpu_to_le16(packets);
-
-    bt_hci_event(hci, EVT_NUM_COMP_PKTS, params, EVT_NUM_COMP_PKTS_SIZE(1));
-}
-
-static void bt_submit_hci(struct HCIInfo *info,
-                const uint8_t *data, int length)
-{
-    struct bt_hci_s *hci = hci_from_info(info);
-    uint16_t cmd;
-    int paramlen, i;
-
-    if (length < HCI_COMMAND_HDR_SIZE)
-        goto short_hci;
-
-    memcpy(&hci->last_cmd, data, 2);
-
-    cmd = (data[1] << 8) | data[0];
-    paramlen = data[2];
-    if (cmd_opcode_ogf(cmd) == 0 || cmd_opcode_ocf(cmd) == 0)  /* NOP */
-        return;
-
-    data += HCI_COMMAND_HDR_SIZE;
-    length -= HCI_COMMAND_HDR_SIZE;
-
-    if (paramlen > length)
-        return;
-
-#define PARAM(cmd, param)      (((cmd##_cp *) data)->param)
-#define PARAM16(cmd, param)    le16_to_cpup(&PARAM(cmd, param))
-#define PARAMHANDLE(cmd)       HNDL(PARAM(cmd, handle))
-#define LENGTH_CHECK(cmd)      if (length < sizeof(cmd##_cp)) goto short_hci
-    /* Note: the supported commands bitmask in bt_hci_read_local_commands_rp
-     * needs to be updated every time a command is implemented here!  */
-    switch (cmd) {
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY):
-        LENGTH_CHECK(inquiry);
-
-        if (PARAM(inquiry, length) < 1) {
-            bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS);
-            break;
-        }
-
-        hci->lm.inquire = 1;
-        hci->lm.periodic = 0;
-        hci->lm.responses_left = PARAM(inquiry, num_rsp) ?: INT_MAX;
-        hci->lm.responses = 0;
-        bt_hci_event_status(hci, HCI_SUCCESS);
-        bt_hci_inquiry_start(hci, PARAM(inquiry, length));
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY_CANCEL):
-        if (!hci->lm.inquire || hci->lm.periodic) {
-            fprintf(stderr, "%s: Inquiry Cancel should only be issued after "
-                            "the Inquiry command has been issued, a Command "
-                            "Status event has been received for the Inquiry "
-                            "command, and before the Inquiry Complete event "
-                            "occurs", __FUNCTION__);
-            bt_hci_event_complete_status(hci, HCI_COMMAND_DISALLOWED);
-            break;
-        }
-
-        hci->lm.inquire = 0;
-        qemu_del_timer(hci->lm.inquiry_done);
-        bt_hci_event_complete_status(hci, HCI_SUCCESS);
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_PERIODIC_INQUIRY):
-        LENGTH_CHECK(periodic_inquiry);
-
-        if (!(PARAM(periodic_inquiry, length) <
-                                PARAM16(periodic_inquiry, min_period) &&
-                                PARAM16(periodic_inquiry, min_period) <
-                                PARAM16(periodic_inquiry, max_period)) ||
-                        PARAM(periodic_inquiry, length) < 1 ||
-                        PARAM16(periodic_inquiry, min_period) < 2 ||
-                        PARAM16(periodic_inquiry, max_period) < 3) {
-            bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS);
-            break;
-        }
-
-        hci->lm.inquire = 1;
-        hci->lm.periodic = 1;
-        hci->lm.responses_left = PARAM(periodic_inquiry, num_rsp);
-        hci->lm.responses = 0;
-        hci->lm.inquiry_period = PARAM16(periodic_inquiry, max_period);
-        bt_hci_event_complete_status(hci, HCI_SUCCESS);
-        bt_hci_inquiry_start(hci, PARAM(periodic_inquiry, length));
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_EXIT_PERIODIC_INQUIRY):
-        if (!hci->lm.inquire || !hci->lm.periodic) {
-            fprintf(stderr, "%s: Inquiry Cancel should only be issued after "
-                            "the Inquiry command has been issued, a Command "
-                            "Status event has been received for the Inquiry "
-                            "command, and before the Inquiry Complete event "
-                            "occurs", __FUNCTION__);
-            bt_hci_event_complete_status(hci, HCI_COMMAND_DISALLOWED);
-            break;
-        }
-        hci->lm.inquire = 0;
-        qemu_del_timer(hci->lm.inquiry_done);
-        qemu_del_timer(hci->lm.inquiry_next);
-        bt_hci_event_complete_status(hci, HCI_SUCCESS);
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_CREATE_CONN):
-        LENGTH_CHECK(create_conn);
-
-        if (hci->lm.connecting >= HCI_HANDLES_MAX) {
-            bt_hci_event_status(hci, HCI_REJECTED_LIMITED_RESOURCES);
-            break;
-        }
-        bt_hci_event_status(hci, HCI_SUCCESS);
-
-        if (bt_hci_connect(hci, &PARAM(create_conn, bdaddr)))
-            bt_hci_connection_reject_event(hci, &PARAM(create_conn, bdaddr));
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_DISCONNECT):
-        LENGTH_CHECK(disconnect);
-
-        if (bt_hci_handle_bad(hci, PARAMHANDLE(disconnect))) {
-            bt_hci_event_status(hci, HCI_NO_CONNECTION);
-            break;
-        }
-
-        bt_hci_event_status(hci, HCI_SUCCESS);
-        bt_hci_disconnect(hci, PARAMHANDLE(disconnect),
-                        PARAM(disconnect, reason));
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_CREATE_CONN_CANCEL):
-        LENGTH_CHECK(create_conn_cancel);
-
-        if (bt_hci_lmp_connection_ready(hci,
-                                &PARAM(create_conn_cancel, bdaddr))) {
-            for (i = 0; i < HCI_HANDLES_MAX; i ++)
-                if (bt_hci_role_master(hci, i) && hci->lm.handle[i].link &&
-                                !bacmp(&hci->lm.handle[i].link->slave->bd_addr,
-                                        &PARAM(create_conn_cancel, bdaddr)))
-                   break;
-
-            bt_hci_event_complete_conn_cancel(hci, i < HCI_HANDLES_MAX ?
-                            HCI_ACL_CONNECTION_EXISTS : HCI_NO_CONNECTION,
-                            &PARAM(create_conn_cancel, bdaddr));
-        } else
-            bt_hci_event_complete_conn_cancel(hci, HCI_SUCCESS,
-                            &PARAM(create_conn_cancel, bdaddr));
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ):
-        LENGTH_CHECK(accept_conn_req);
-
-        if (!hci->conn_req_host ||
-                        bacmp(&PARAM(accept_conn_req, bdaddr),
-                                &hci->conn_req_host->bd_addr)) {
-            bt_hci_event_status(hci, HCI_INVALID_PARAMETERS);
-            break;
-        }
-
-        bt_hci_event_status(hci, HCI_SUCCESS);
-        bt_hci_connection_accept(hci, hci->conn_req_host);
-        hci->conn_req_host = NULL;
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_REJECT_CONN_REQ):
-        LENGTH_CHECK(reject_conn_req);
-
-        if (!hci->conn_req_host ||
-                        bacmp(&PARAM(reject_conn_req, bdaddr),
-                                &hci->conn_req_host->bd_addr)) {
-            bt_hci_event_status(hci, HCI_INVALID_PARAMETERS);
-            break;
-        }
-
-        bt_hci_event_status(hci, HCI_SUCCESS);
-        bt_hci_connection_reject(hci, hci->conn_req_host,
-                        PARAM(reject_conn_req, reason));
-        bt_hci_connection_reject_event(hci, &hci->conn_req_host->bd_addr);
-        hci->conn_req_host = NULL;
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_AUTH_REQUESTED):
-        LENGTH_CHECK(auth_requested);
-
-        if (bt_hci_handle_bad(hci, PARAMHANDLE(auth_requested)))
-            bt_hci_event_status(hci, HCI_NO_CONNECTION);
-        else {
-            bt_hci_event_status(hci, HCI_SUCCESS);
-            bt_hci_event_auth_complete(hci, PARAMHANDLE(auth_requested));
-        }
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT):
-        LENGTH_CHECK(set_conn_encrypt);
-
-        if (bt_hci_handle_bad(hci, PARAMHANDLE(set_conn_encrypt)))
-            bt_hci_event_status(hci, HCI_NO_CONNECTION);
-        else {
-            bt_hci_event_status(hci, HCI_SUCCESS);
-            bt_hci_event_encrypt_change(hci,
-                            PARAMHANDLE(set_conn_encrypt),
-                            PARAM(set_conn_encrypt, encrypt));
-        }
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_REMOTE_NAME_REQ):
-        LENGTH_CHECK(remote_name_req);
-
-        if (bt_hci_name_req(hci, &PARAM(remote_name_req, bdaddr)))
-            bt_hci_event_status(hci, HCI_NO_CONNECTION);
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_REMOTE_NAME_REQ_CANCEL):
-        LENGTH_CHECK(remote_name_req_cancel);
-
-        bt_hci_event_complete_name_cancel(hci,
-                        &PARAM(remote_name_req_cancel, bdaddr));
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_FEATURES):
-        LENGTH_CHECK(read_remote_features);
-
-        if (bt_hci_features_req(hci, PARAMHANDLE(read_remote_features)))
-            bt_hci_event_status(hci, HCI_NO_CONNECTION);
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_EXT_FEATURES):
-        LENGTH_CHECK(read_remote_ext_features);
-
-        if (bt_hci_handle_bad(hci, PARAMHANDLE(read_remote_ext_features)))
-            bt_hci_event_status(hci, HCI_NO_CONNECTION);
-        else {
-            bt_hci_event_status(hci, HCI_SUCCESS);
-            bt_hci_event_read_remote_ext_features(hci,
-                            PARAMHANDLE(read_remote_ext_features));
-        }
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_VERSION):
-        LENGTH_CHECK(read_remote_version);
-
-        if (bt_hci_version_req(hci, PARAMHANDLE(read_remote_version)))
-            bt_hci_event_status(hci, HCI_NO_CONNECTION);
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_CLOCK_OFFSET):
-        LENGTH_CHECK(read_clock_offset);
-
-        if (bt_hci_clkoffset_req(hci, PARAMHANDLE(read_clock_offset)))
-            bt_hci_event_status(hci, HCI_NO_CONNECTION);
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_LMP_HANDLE):
-        LENGTH_CHECK(read_lmp_handle);
-
-        /* TODO: */
-        bt_hci_event_complete_lmp_handle(hci, PARAMHANDLE(read_lmp_handle));
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_POLICY, OCF_HOLD_MODE):
-        LENGTH_CHECK(hold_mode);
-
-        if (PARAM16(hold_mode, min_interval) >
-                        PARAM16(hold_mode, max_interval) ||
-                        PARAM16(hold_mode, min_interval) < 0x0002 ||
-                        PARAM16(hold_mode, max_interval) > 0xff00 ||
-                        (PARAM16(hold_mode, min_interval) & 1) ||
-                        (PARAM16(hold_mode, max_interval) & 1)) {
-            bt_hci_event_status(hci, HCI_INVALID_PARAMETERS);
-            break;
-        }
-
-        if (bt_hci_mode_change(hci, PARAMHANDLE(hold_mode),
-                                PARAM16(hold_mode, max_interval),
-                                acl_hold))
-            bt_hci_event_status(hci, HCI_NO_CONNECTION);
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_POLICY, OCF_PARK_MODE):
-        LENGTH_CHECK(park_mode);
-
-        if (PARAM16(park_mode, min_interval) >
-                        PARAM16(park_mode, max_interval) ||
-                        PARAM16(park_mode, min_interval) < 0x000e ||
-                        (PARAM16(park_mode, min_interval) & 1) ||
-                        (PARAM16(park_mode, max_interval) & 1)) {
-            bt_hci_event_status(hci, HCI_INVALID_PARAMETERS);
-            break;
-        }
-
-        if (bt_hci_mode_change(hci, PARAMHANDLE(park_mode),
-                                PARAM16(park_mode, max_interval),
-                                acl_parked))
-            bt_hci_event_status(hci, HCI_NO_CONNECTION);
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_POLICY, OCF_EXIT_PARK_MODE):
-        LENGTH_CHECK(exit_park_mode);
-
-        if (bt_hci_mode_cancel(hci, PARAMHANDLE(exit_park_mode),
-                                acl_parked))
-            bt_hci_event_status(hci, HCI_NO_CONNECTION);
-        break;
-
-    case cmd_opcode_pack(OGF_LINK_POLICY, OCF_ROLE_DISCOVERY):
-        LENGTH_CHECK(role_discovery);
-
-        if (bt_hci_handle_bad(hci, PARAMHANDLE(role_discovery)))
-            bt_hci_event_complete_role_discovery(hci,
-                            HCI_NO_CONNECTION, PARAMHANDLE(role_discovery), 0);
-        else
-            bt_hci_event_complete_role_discovery(hci,
-                            HCI_SUCCESS, PARAMHANDLE(role_discovery),
-                            bt_hci_role_master(hci,
-                                    PARAMHANDLE(role_discovery)));
-        break;
-
-    case cmd_opcode_pack(OGF_HOST_CTL, OCF_SET_EVENT_MASK):
-        LENGTH_CHECK(set_event_mask);
-
-        memcpy(hci->event_mask, PARAM(set_event_mask, mask), 8);
-        bt_hci_event_complete_status(hci, HCI_SUCCESS);
-        break;
-
-    case cmd_opcode_pack(OGF_HOST_CTL, OCF_RESET):
-        bt_hci_reset(hci);
-        bt_hci_event_status(hci, HCI_SUCCESS);
-        break;
-
-    case cmd_opcode_pack(OGF_HOST_CTL, OCF_SET_EVENT_FLT):
-        if (length >= 1 && PARAM(set_event_flt, flt_type) == FLT_CLEAR_ALL)
-            /* No length check */;
-        else
-            LENGTH_CHECK(set_event_flt);
-
-        /* Filters are not implemented */
-        bt_hci_event_complete_status(hci, HCI_SUCCESS);
-        break;
-
-    case cmd_opcode_pack(OGF_HOST_CTL, OCF_FLUSH):
-        LENGTH_CHECK(flush);
-
-        if (bt_hci_handle_bad(hci, PARAMHANDLE(flush)))
-            bt_hci_event_complete_flush(hci,
-                            HCI_NO_CONNECTION, PARAMHANDLE(flush));
-        else {
-            /* TODO: ordering? */
-            bt_hci_event(hci, EVT_FLUSH_OCCURRED,
-                            &PARAM(flush, handle),
-                            EVT_FLUSH_OCCURRED_SIZE);
-            bt_hci_event_complete_flush(hci,
-                            HCI_SUCCESS, PARAMHANDLE(flush));
-        }
-        break;
-
-    case cmd_opcode_pack(OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME):
-        LENGTH_CHECK(change_local_name);
-
-        if (hci->device.lmp_name)
-            g_free((void *) hci->device.lmp_name);
-        hci->device.lmp_name = g_strndup(PARAM(change_local_name, name),
-                        sizeof(PARAM(change_local_name, name)));
-        bt_hci_event_complete_status(hci, HCI_SUCCESS);
-        break;
-
-    case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_LOCAL_NAME):
-        bt_hci_event_complete_read_local_name(hci);
-        break;
-
-    case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_CONN_ACCEPT_TIMEOUT):
-        bt_hci_event_complete_read_conn_accept_timeout(hci);
-        break;
-
-    case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CONN_ACCEPT_TIMEOUT):
-        /* TODO */
-        LENGTH_CHECK(write_conn_accept_timeout);
-
-        if (PARAM16(write_conn_accept_timeout, timeout) < 0x0001 ||
-                        PARAM16(write_conn_accept_timeout, timeout) > 0xb540) {
-            bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS);
-            break;
-        }
-
-        hci->conn_accept_tout = PARAM16(write_conn_accept_timeout, timeout);
-        bt_hci_event_complete_status(hci, HCI_SUCCESS);
-        break;
-
-    case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_SCAN_ENABLE):
-        bt_hci_event_complete_read_scan_enable(hci);
-        break;
-
-    case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE):
-        LENGTH_CHECK(write_scan_enable);
-
-        /* TODO: check that the remaining bits are all 0 */
-        hci->device.inquiry_scan =
-                !!(PARAM(write_scan_enable, scan_enable) & SCAN_INQUIRY);
-        hci->device.page_scan =
-                !!(PARAM(write_scan_enable, scan_enable) & SCAN_PAGE);
-        bt_hci_event_complete_status(hci, HCI_SUCCESS);
-        break;
-
-    case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_CLASS_OF_DEV):
-        bt_hci_event_complete_read_local_class(hci);
-        break;
-
-    case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV):
-        LENGTH_CHECK(write_class_of_dev);
-
-        memcpy(hci->device.class, PARAM(write_class_of_dev, dev_class),
-                        sizeof(PARAM(write_class_of_dev, dev_class)));
-        bt_hci_event_complete_status(hci, HCI_SUCCESS);
-        break;
-
-    case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_VOICE_SETTING):
-        bt_hci_event_complete_voice_setting(hci);
-        break;
-
-    case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_VOICE_SETTING):
-        LENGTH_CHECK(write_voice_setting);
-
-        hci->voice_setting = PARAM(write_voice_setting, voice_setting);
-        bt_hci_event_complete_status(hci, HCI_SUCCESS);
-        break;
-
-    case cmd_opcode_pack(OGF_HOST_CTL, OCF_HOST_NUMBER_OF_COMPLETED_PACKETS):
-        if (length < data[0] * 2 + 1)
-            goto short_hci;
-
-        for (i = 0; i < data[0]; i ++)
-            if (bt_hci_handle_bad(hci,
-                                    data[i * 2 + 1] | (data[i * 2 + 2] << 8)))
-                bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS);
-        break;
-
-    case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_INQUIRY_MODE):
-        /* Only if (local_features[3] & 0x40) && (local_commands[12] & 0x40)
-         * else
-         *     goto unknown_command */
-        bt_hci_event_complete_read_inquiry_mode(hci);
-        break;
-
-    case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_INQUIRY_MODE):
-        /* Only if (local_features[3] & 0x40) && (local_commands[12] & 0x80)
-         * else
-         *     goto unknown_command */
-        LENGTH_CHECK(write_inquiry_mode);
-
-        if (PARAM(write_inquiry_mode, mode) > 0x01) {
-            bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS);
-            break;
-        }
-
-        hci->lm.inquiry_mode = PARAM(write_inquiry_mode, mode);
-        bt_hci_event_complete_status(hci, HCI_SUCCESS);
-        break;
-
-    case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_VERSION):
-        bt_hci_read_local_version_rp(hci);
-        break;
-
-    case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_COMMANDS):
-        bt_hci_read_local_commands_rp(hci);
-        break;
-
-    case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES):
-        bt_hci_read_local_features_rp(hci);
-        break;
-
-    case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_EXT_FEATURES):
-        LENGTH_CHECK(read_local_ext_features);
-
-        bt_hci_read_local_ext_features_rp(hci,
-                        PARAM(read_local_ext_features, page_num));
-        break;
-
-    case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_BUFFER_SIZE):
-        bt_hci_read_buffer_size_rp(hci);
-        break;
-
-    case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_COUNTRY_CODE):
-        bt_hci_read_country_code_rp(hci);
-        break;
-
-    case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_BD_ADDR):
-        bt_hci_read_bd_addr_rp(hci);
-        break;
-
-    case cmd_opcode_pack(OGF_STATUS_PARAM, OCF_READ_LINK_QUALITY):
-        LENGTH_CHECK(read_link_quality);
-
-        bt_hci_link_quality_rp(hci, PARAMHANDLE(read_link_quality));
-        break;
-
-    default:
-        bt_hci_event_status(hci, HCI_UNKNOWN_COMMAND);
-        break;
-
-    short_hci:
-        fprintf(stderr, "%s: HCI packet too short (%iB)\n",
-                        __FUNCTION__, length);
-        bt_hci_event_status(hci, HCI_INVALID_PARAMETERS);
-        break;
-    }
-}
-
-/* We could perform fragmentation here, we can't do "recombination" because
- * at this layer the length of the payload is not know ahead, so we only
- * know that a packet contained the last fragment of the SDU when the next
- * SDU starts.  */
-static inline void bt_hci_lmp_acl_data(struct bt_hci_s *hci, uint16_t handle,
-                const uint8_t *data, int start, int len)
-{
-    struct hci_acl_hdr *pkt = (void *) hci->acl_buf;
-
-    /* TODO: packet flags */
-    /* TODO: avoid memcpy'ing */
-
-    if (len + HCI_ACL_HDR_SIZE > sizeof(hci->acl_buf)) {
-        fprintf(stderr, "%s: can't take ACL packets %i bytes long\n",
-                        __FUNCTION__, len);
-        return;
-    }
-    memcpy(hci->acl_buf + HCI_ACL_HDR_SIZE, data, len);
-
-    pkt->handle = cpu_to_le16(
-                    acl_handle_pack(handle, start ? ACL_START : ACL_CONT));
-    pkt->dlen = cpu_to_le16(len);
-    hci->info.acl_recv(hci->info.opaque,
-                    hci->acl_buf, len + HCI_ACL_HDR_SIZE);
-}
-
-static void bt_hci_lmp_acl_data_slave(struct bt_link_s *btlink,
-                const uint8_t *data, int start, int len)
-{
-    struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink;
-
-    bt_hci_lmp_acl_data(hci_from_device(btlink->slave),
-                    link->handle, data, start, len);
-}
-
-static void bt_hci_lmp_acl_data_host(struct bt_link_s *link,
-                const uint8_t *data, int start, int len)
-{
-    bt_hci_lmp_acl_data(hci_from_device(link->host),
-                    link->handle, data, start, len);
-}
-
-static void bt_submit_acl(struct HCIInfo *info,
-                const uint8_t *data, int length)
-{
-    struct bt_hci_s *hci = hci_from_info(info);
-    uint16_t handle;
-    int datalen, flags;
-    struct bt_link_s *link;
-
-    if (length < HCI_ACL_HDR_SIZE) {
-        fprintf(stderr, "%s: ACL packet too short (%iB)\n",
-                        __FUNCTION__, length);
-        return;
-    }
-
-    handle = acl_handle((data[1] << 8) | data[0]);
-    flags = acl_flags((data[1] << 8) | data[0]);
-    datalen = (data[3] << 8) | data[2];
-    data += HCI_ACL_HDR_SIZE;
-    length -= HCI_ACL_HDR_SIZE;
-
-    if (bt_hci_handle_bad(hci, handle)) {
-        fprintf(stderr, "%s: invalid ACL handle %03x\n",
-                        __FUNCTION__, handle);
-        /* TODO: signal an error */
-        return;
-    }
-    handle &= ~HCI_HANDLE_OFFSET;
-
-    if (datalen > length) {
-        fprintf(stderr, "%s: ACL packet too short (%iB < %iB)\n",
-                        __FUNCTION__, length, datalen);
-        return;
-    }
-
-    link = hci->lm.handle[handle].link;
-
-    if ((flags & ~3) == ACL_ACTIVE_BCAST) {
-        if (!hci->asb_handle)
-            hci->asb_handle = handle;
-        else if (handle != hci->asb_handle) {
-            fprintf(stderr, "%s: Bad handle %03x in Active Slave Broadcast\n",
-                            __FUNCTION__, handle);
-            /* TODO: signal an error */
-            return;
-        }
-
-        /* TODO */
-    }
-
-    if ((flags & ~3) == ACL_PICO_BCAST) {
-        if (!hci->psb_handle)
-            hci->psb_handle = handle;
-        else if (handle != hci->psb_handle) {
-            fprintf(stderr, "%s: Bad handle %03x in Parked Slave Broadcast\n",
-                            __FUNCTION__, handle);
-            /* TODO: signal an error */
-            return;
-        }
-
-        /* TODO */
-    }
-
-    /* TODO: increase counter and send EVT_NUM_COMP_PKTS */
-    bt_hci_event_num_comp_pkts(hci, handle | HCI_HANDLE_OFFSET, 1);
-
-    /* Do this last as it can trigger further events even in this HCI */
-    hci->lm.handle[handle].lmp_acl_data(link, data,
-                    (flags & 3) == ACL_START, length);
-}
-
-static void bt_submit_sco(struct HCIInfo *info,
-                const uint8_t *data, int length)
-{
-    struct bt_hci_s *hci = hci_from_info(info);
-    uint16_t handle;
-    int datalen;
-
-    if (length < 3)
-        return;
-
-    handle = acl_handle((data[1] << 8) | data[0]);
-    datalen = data[2];
-    length -= 3;
-
-    if (bt_hci_handle_bad(hci, handle)) {
-        fprintf(stderr, "%s: invalid SCO handle %03x\n",
-                        __FUNCTION__, handle);
-        return;
-    }
-
-    if (datalen > length) {
-        fprintf(stderr, "%s: SCO packet too short (%iB < %iB)\n",
-                        __FUNCTION__, length, datalen);
-        return;
-    }
-
-    /* TODO */
-
-    /* TODO: increase counter and send EVT_NUM_COMP_PKTS if synchronous
-     * Flow Control is enabled.
-     * (See Read/Write_Synchronous_Flow_Control_Enable on page 513 and
-     * page 514.)  */
-}
-
-static uint8_t *bt_hci_evt_packet(void *opaque)
-{
-    /* TODO: allocate a packet from upper layer */
-    struct bt_hci_s *s = opaque;
-
-    return s->evt_buf;
-}
-
-static void bt_hci_evt_submit(void *opaque, int len)
-{
-    /* TODO: notify upper layer */
-    struct bt_hci_s *s = opaque;
-
-    s->info.evt_recv(s->info.opaque, s->evt_buf, len);
-}
-
-static int bt_hci_bdaddr_set(struct HCIInfo *info, const uint8_t *bd_addr)
-{
-    struct bt_hci_s *hci = hci_from_info(info);
-
-    bacpy(&hci->device.bd_addr, (const bdaddr_t *) bd_addr);
-    return 0;
-}
-
-static void bt_hci_done(struct HCIInfo *info);
-static void bt_hci_destroy(struct bt_device_s *dev)
-{
-    struct bt_hci_s *hci = hci_from_device(dev);
-
-    bt_hci_done(&hci->info);
-}
-
-struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net)
-{
-    struct bt_hci_s *s = g_malloc0(sizeof(struct bt_hci_s));
-
-    s->lm.inquiry_done = qemu_new_timer_ns(vm_clock, bt_hci_inquiry_done, s);
-    s->lm.inquiry_next = qemu_new_timer_ns(vm_clock, bt_hci_inquiry_next, s);
-    s->conn_accept_timer =
-            qemu_new_timer_ns(vm_clock, bt_hci_conn_accept_timeout, s);
-
-    s->evt_packet = bt_hci_evt_packet;
-    s->evt_submit = bt_hci_evt_submit;
-    s->opaque = s;
-
-    bt_device_init(&s->device, net);
-    s->device.lmp_connection_request = bt_hci_lmp_connection_request;
-    s->device.lmp_connection_complete = bt_hci_lmp_connection_complete;
-    s->device.lmp_disconnect_master = bt_hci_lmp_disconnect_host;
-    s->device.lmp_disconnect_slave = bt_hci_lmp_disconnect_slave;
-    s->device.lmp_acl_data = bt_hci_lmp_acl_data_slave;
-    s->device.lmp_acl_resp = bt_hci_lmp_acl_data_host;
-    s->device.lmp_mode_change = bt_hci_lmp_mode_change_slave;
-
-    /* Keep updated! */
-    /* Also keep in sync with supported commands bitmask in
-     * bt_hci_read_local_commands_rp */
-    s->device.lmp_caps = 0x8000199b7e85355fll;
-
-    bt_hci_reset(s);
-
-    s->info.cmd_send = bt_submit_hci;
-    s->info.sco_send = bt_submit_sco;
-    s->info.acl_send = bt_submit_acl;
-    s->info.bdaddr_set = bt_hci_bdaddr_set;
-
-    s->device.handle_destroy = bt_hci_destroy;
-
-    return &s->info;
-}
-
-static void bt_hci_done(struct HCIInfo *info)
-{
-    struct bt_hci_s *hci = hci_from_info(info);
-    int handle;
-
-    bt_device_done(&hci->device);
-
-    if (hci->device.lmp_name)
-        g_free((void *) hci->device.lmp_name);
-
-    /* Be gentle and send DISCONNECT to all connected peers and those
-     * currently waiting for us to accept or reject a connection request.
-     * This frees the links.  */
-    if (hci->conn_req_host) {
-        bt_hci_connection_reject(hci,
-                                 hci->conn_req_host, HCI_OE_POWER_OFF);
-        return;
-    }
-
-    for (handle = HCI_HANDLE_OFFSET;
-                    handle < (HCI_HANDLE_OFFSET | HCI_HANDLES_MAX); handle ++)
-        if (!bt_hci_handle_bad(hci, handle))
-            bt_hci_disconnect(hci, handle, HCI_OE_POWER_OFF);
-
-    /* TODO: this is not enough actually, there may be slaves from whom
-     * we have requested a connection who will soon (or not) respond with
-     * an accept or a reject, so we should also check if hci->lm.connecting
-     * is non-zero and if so, avoid freeing the hci but otherwise disappear
-     * from all qemu social life (e.g. stop scanning and request to be
-     * removed from s->device.net) and arrange for
-     * s->device.lmp_connection_complete to free the remaining bits once
-     * hci->lm.awaiting_bdaddr[] is empty.  */
-
-    qemu_free_timer(hci->lm.inquiry_done);
-    qemu_free_timer(hci->lm.inquiry_next);
-    qemu_free_timer(hci->conn_accept_timer);
-
-    g_free(hci);
-}
diff --git a/hw/bt-hid.c b/hw/bt-hid.c
deleted file mode 100644 (file)
index af494e1..0000000
+++ /dev/null
@@ -1,553 +0,0 @@
-/*
- * QEMU Bluetooth HID Profile wrapper for USB HID.
- *
- * Copyright (C) 2007-2008 OpenMoko, Inc.
- * Written by Andrzej Zaborowski <andrew@openedhand.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 or
- * (at your option) version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "qemu-common.h"
-#include "qemu/timer.h"
-#include "ui/console.h"
-#include "hw/input/hid.h"
-#include "hw/bt.h"
-
-enum hid_transaction_req {
-    BT_HANDSHAKE                       = 0x0,
-    BT_HID_CONTROL                     = 0x1,
-    BT_GET_REPORT                      = 0x4,
-    BT_SET_REPORT                      = 0x5,
-    BT_GET_PROTOCOL                    = 0x6,
-    BT_SET_PROTOCOL                    = 0x7,
-    BT_GET_IDLE                                = 0x8,
-    BT_SET_IDLE                                = 0x9,
-    BT_DATA                            = 0xa,
-    BT_DATC                            = 0xb,
-};
-
-enum hid_transaction_handshake {
-    BT_HS_SUCCESSFUL                   = 0x0,
-    BT_HS_NOT_READY                    = 0x1,
-    BT_HS_ERR_INVALID_REPORT_ID                = 0x2,
-    BT_HS_ERR_UNSUPPORTED_REQUEST      = 0x3,
-    BT_HS_ERR_INVALID_PARAMETER                = 0x4,
-    BT_HS_ERR_UNKNOWN                  = 0xe,
-    BT_HS_ERR_FATAL                    = 0xf,
-};
-
-enum hid_transaction_control {
-    BT_HC_NOP                          = 0x0,
-    BT_HC_HARD_RESET                   = 0x1,
-    BT_HC_SOFT_RESET                   = 0x2,
-    BT_HC_SUSPEND                      = 0x3,
-    BT_HC_EXIT_SUSPEND                 = 0x4,
-    BT_HC_VIRTUAL_CABLE_UNPLUG         = 0x5,
-};
-
-enum hid_protocol {
-    BT_HID_PROTO_BOOT                  = 0,
-    BT_HID_PROTO_REPORT                        = 1,
-};
-
-enum hid_boot_reportid {
-    BT_HID_BOOT_INVALID                        = 0,
-    BT_HID_BOOT_KEYBOARD,
-    BT_HID_BOOT_MOUSE,
-};
-
-enum hid_data_pkt {
-    BT_DATA_OTHER                      = 0,
-    BT_DATA_INPUT,
-    BT_DATA_OUTPUT,
-    BT_DATA_FEATURE,
-};
-
-#define BT_HID_MTU                     48
-
-/* HID interface requests */
-#define GET_REPORT                     0xa101
-#define GET_IDLE                       0xa102
-#define GET_PROTOCOL                   0xa103
-#define SET_REPORT                     0x2109
-#define SET_IDLE                       0x210a
-#define SET_PROTOCOL                   0x210b
-
-struct bt_hid_device_s {
-    struct bt_l2cap_device_s btdev;
-    struct bt_l2cap_conn_params_s *control;
-    struct bt_l2cap_conn_params_s *interrupt;
-    HIDState hid;
-
-    int proto;
-    int connected;
-    int data_type;
-    int intr_state;
-    struct {
-        int len;
-        uint8_t buffer[1024];
-    } dataother, datain, dataout, feature, intrdataout;
-    enum {
-        bt_state_ready,
-        bt_state_transaction,
-        bt_state_suspend,
-    } state;
-};
-
-static void bt_hid_reset(struct bt_hid_device_s *s)
-{
-    struct bt_scatternet_s *net = s->btdev.device.net;
-
-    /* Go as far as... */
-    bt_l2cap_device_done(&s->btdev);
-    bt_l2cap_device_init(&s->btdev, net);
-
-    hid_reset(&s->hid);
-    s->proto = BT_HID_PROTO_REPORT;
-    s->state = bt_state_ready;
-    s->dataother.len = 0;
-    s->datain.len = 0;
-    s->dataout.len = 0;
-    s->feature.len = 0;
-    s->intrdataout.len = 0;
-    s->intr_state = 0;
-}
-
-static int bt_hid_out(struct bt_hid_device_s *s)
-{
-    if (s->data_type == BT_DATA_OUTPUT) {
-        /* nothing */
-        ;
-    }
-
-    if (s->data_type == BT_DATA_FEATURE) {
-        /* XXX:
-         * does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE
-         * or a SET_REPORT? */
-        ;
-    }
-
-    return -1;
-}
-
-static int bt_hid_in(struct bt_hid_device_s *s)
-{
-    s->datain.len = hid_keyboard_poll(&s->hid, s->datain.buffer,
-                                      sizeof(s->datain.buffer));
-    return s->datain.len;
-}
-
-static void bt_hid_send_handshake(struct bt_hid_device_s *s, int result)
-{
-    *s->control->sdu_out(s->control, 1) =
-            (BT_HANDSHAKE << 4) | result;
-    s->control->sdu_submit(s->control);
-}
-
-static void bt_hid_send_control(struct bt_hid_device_s *s, int operation)
-{
-    *s->control->sdu_out(s->control, 1) =
-            (BT_HID_CONTROL << 4) | operation;
-    s->control->sdu_submit(s->control);
-}
-
-static void bt_hid_disconnect(struct bt_hid_device_s *s)
-{
-    /* Disconnect s->control and s->interrupt */
-}
-
-static void bt_hid_send_data(struct bt_l2cap_conn_params_s *ch, int type,
-                const uint8_t *data, int len)
-{
-    uint8_t *pkt, hdr = (BT_DATA << 4) | type;
-    int plen;
-
-    do {
-        plen = MIN(len, ch->remote_mtu - 1);
-        pkt = ch->sdu_out(ch, plen + 1);
-
-        pkt[0] = hdr;
-        if (plen)
-            memcpy(pkt + 1, data, plen);
-        ch->sdu_submit(ch);
-
-        len -= plen;
-        data += plen;
-        hdr = (BT_DATC << 4) | type;
-    } while (plen == ch->remote_mtu - 1);
-}
-
-static void bt_hid_control_transaction(struct bt_hid_device_s *s,
-                const uint8_t *data, int len)
-{
-    uint8_t type, parameter;
-    int rlen, ret = -1;
-    if (len < 1)
-        return;
-
-    type = data[0] >> 4;
-    parameter = data[0] & 0xf;
-
-    switch (type) {
-    case BT_HANDSHAKE:
-    case BT_DATA:
-        switch (parameter) {
-        default:
-            /* These are not expected to be sent this direction.  */
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-        }
-        break;
-
-    case BT_HID_CONTROL:
-        if (len != 1 || (parameter != BT_HC_VIRTUAL_CABLE_UNPLUG &&
-                                s->state == bt_state_transaction)) {
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        }
-        switch (parameter) {
-        case BT_HC_NOP:
-            break;
-        case BT_HC_HARD_RESET:
-        case BT_HC_SOFT_RESET:
-            bt_hid_reset(s);
-            break;
-        case BT_HC_SUSPEND:
-            if (s->state == bt_state_ready)
-                s->state = bt_state_suspend;
-            else
-                ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        case BT_HC_EXIT_SUSPEND:
-            if (s->state == bt_state_suspend)
-                s->state = bt_state_ready;
-            else
-                ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        case BT_HC_VIRTUAL_CABLE_UNPLUG:
-            bt_hid_disconnect(s);
-            break;
-        default:
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-        }
-        break;
-
-    case BT_GET_REPORT:
-        /* No ReportIDs declared.  */
-        if (((parameter & 8) && len != 3) ||
-                        (!(parameter & 8) && len != 1) ||
-                        s->state != bt_state_ready) {
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        }
-        if (parameter & 8)
-            rlen = data[2] | (data[3] << 8);
-        else
-            rlen = INT_MAX;
-        switch (parameter & 3) {
-        case BT_DATA_OTHER:
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        case BT_DATA_INPUT:
-            /* Here we can as well poll s->usbdev */
-            bt_hid_send_data(s->control, BT_DATA_INPUT,
-                            s->datain.buffer, MIN(rlen, s->datain.len));
-            break;
-        case BT_DATA_OUTPUT:
-            bt_hid_send_data(s->control, BT_DATA_OUTPUT,
-                            s->dataout.buffer, MIN(rlen, s->dataout.len));
-            break;
-        case BT_DATA_FEATURE:
-            bt_hid_send_data(s->control, BT_DATA_FEATURE,
-                            s->feature.buffer, MIN(rlen, s->feature.len));
-            break;
-        }
-        break;
-
-    case BT_SET_REPORT:
-        if (len < 2 || len > BT_HID_MTU || s->state != bt_state_ready ||
-                        (parameter & 3) == BT_DATA_OTHER ||
-                        (parameter & 3) == BT_DATA_INPUT) {
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        }
-        s->data_type = parameter & 3;
-        if (s->data_type == BT_DATA_OUTPUT) {
-            s->dataout.len = len - 1;
-            memcpy(s->dataout.buffer, data + 1, s->dataout.len);
-        } else {
-            s->feature.len = len - 1;
-            memcpy(s->feature.buffer, data + 1, s->feature.len);
-        }
-        if (len == BT_HID_MTU)
-            s->state = bt_state_transaction;
-        else
-            bt_hid_out(s);
-        break;
-
-    case BT_GET_PROTOCOL:
-        if (len != 1 || s->state == bt_state_transaction) {
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        }
-        *s->control->sdu_out(s->control, 1) = s->proto;
-        s->control->sdu_submit(s->control);
-        break;
-
-    case BT_SET_PROTOCOL:
-        if (len != 1 || s->state == bt_state_transaction ||
-                        (parameter != BT_HID_PROTO_BOOT &&
-                         parameter != BT_HID_PROTO_REPORT)) {
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        }
-        s->proto = parameter;
-        s->hid.protocol = parameter;
-        ret = BT_HS_SUCCESSFUL;
-        break;
-
-    case BT_GET_IDLE:
-        if (len != 1 || s->state == bt_state_transaction) {
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        }
-        *s->control->sdu_out(s->control, 1) = s->hid.idle;
-        s->control->sdu_submit(s->control);
-        break;
-
-    case BT_SET_IDLE:
-        if (len != 2 || s->state == bt_state_transaction) {
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        }
-
-        s->hid.idle = data[1];
-        /* XXX: Does this generate a handshake? */
-        break;
-
-    case BT_DATC:
-        if (len > BT_HID_MTU || s->state != bt_state_transaction) {
-            ret = BT_HS_ERR_INVALID_PARAMETER;
-            break;
-        }
-        if (s->data_type == BT_DATA_OUTPUT) {
-            memcpy(s->dataout.buffer + s->dataout.len, data + 1, len - 1);
-            s->dataout.len += len - 1;
-        } else {
-            memcpy(s->feature.buffer + s->feature.len, data + 1, len - 1);
-            s->feature.len += len - 1;
-        }
-        if (len < BT_HID_MTU) {
-            bt_hid_out(s);
-            s->state = bt_state_ready;
-        }
-        break;
-
-    default:
-        ret = BT_HS_ERR_UNSUPPORTED_REQUEST;
-    }
-
-    if (ret != -1)
-        bt_hid_send_handshake(s, ret);
-}
-
-static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len)
-{
-    struct bt_hid_device_s *hid = opaque;
-
-    bt_hid_control_transaction(hid, data, len);
-}
-
-static void bt_hid_datain(HIDState *hs)
-{
-    struct bt_hid_device_s *hid =
-        container_of(hs, struct bt_hid_device_s, hid);
-
-    /* If suspended, wake-up and send a wake-up event first.  We might
-     * want to also inspect the input report and ignore event like
-     * mouse movements until a button event occurs.  */
-    if (hid->state == bt_state_suspend) {
-        hid->state = bt_state_ready;
-    }
-
-    if (bt_hid_in(hid) > 0)
-        /* TODO: when in boot-mode precede any Input reports with the ReportID
-         * byte, here and in GetReport/SetReport on the Control channel.  */
-        bt_hid_send_data(hid->interrupt, BT_DATA_INPUT,
-                        hid->datain.buffer, hid->datain.len);
-}
-
-static void bt_hid_interrupt_sdu(void *opaque, const uint8_t *data, int len)
-{
-    struct bt_hid_device_s *hid = opaque;
-
-    if (len > BT_HID_MTU || len < 1)
-        goto bad;
-    if ((data[0] & 3) != BT_DATA_OUTPUT)
-        goto bad;
-    if ((data[0] >> 4) == BT_DATA) {
-        if (hid->intr_state)
-            goto bad;
-
-        hid->data_type = BT_DATA_OUTPUT;
-        hid->intrdataout.len = 0;
-    } else if ((data[0] >> 4) == BT_DATC) {
-        if (!hid->intr_state)
-            goto bad;
-    } else
-        goto bad;
-
-    memcpy(hid->intrdataout.buffer + hid->intrdataout.len, data + 1, len - 1);
-    hid->intrdataout.len += len - 1;
-    hid->intr_state = (len == BT_HID_MTU);
-    if (!hid->intr_state) {
-        memcpy(hid->dataout.buffer, hid->intrdataout.buffer,
-                        hid->dataout.len = hid->intrdataout.len);
-        bt_hid_out(hid);
-    }
-
-    return;
-bad:
-    fprintf(stderr, "%s: bad transaction on Interrupt channel.\n",
-                    __FUNCTION__);
-}
-
-/* "Virtual cable" plug/unplug event.  */
-static void bt_hid_connected_update(struct bt_hid_device_s *hid)
-{
-    int prev = hid->connected;
-
-    hid->connected = hid->control && hid->interrupt;
-
-    /* Stop page-/inquiry-scanning when a host is connected.  */
-    hid->btdev.device.page_scan = !hid->connected;
-    hid->btdev.device.inquiry_scan = !hid->connected;
-
-    if (hid->connected && !prev) {
-        hid_reset(&hid->hid);
-        hid->proto = BT_HID_PROTO_REPORT;
-    }
-
-    /* Should set HIDVirtualCable in SDP (possibly need to check that SDP
-     * isn't destroyed yet, in case we're being called from handle_destroy) */
-}
-
-static void bt_hid_close_control(void *opaque)
-{
-    struct bt_hid_device_s *hid = opaque;
-
-    hid->control = NULL;
-    bt_hid_connected_update(hid);
-}
-
-static void bt_hid_close_interrupt(void *opaque)
-{
-    struct bt_hid_device_s *hid = opaque;
-
-    hid->interrupt = NULL;
-    bt_hid_connected_update(hid);
-}
-
-static int bt_hid_new_control_ch(struct bt_l2cap_device_s *dev,
-                struct bt_l2cap_conn_params_s *params)
-{
-    struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
-
-    if (hid->control)
-        return 1;
-
-    hid->control = params;
-    hid->control->opaque = hid;
-    hid->control->close = bt_hid_close_control;
-    hid->control->sdu_in = bt_hid_control_sdu;
-
-    bt_hid_connected_update(hid);
-
-    return 0;
-}
-
-static int bt_hid_new_interrupt_ch(struct bt_l2cap_device_s *dev,
-                struct bt_l2cap_conn_params_s *params)
-{
-    struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
-
-    if (hid->interrupt)
-        return 1;
-
-    hid->interrupt = params;
-    hid->interrupt->opaque = hid;
-    hid->interrupt->close = bt_hid_close_interrupt;
-    hid->interrupt->sdu_in = bt_hid_interrupt_sdu;
-
-    bt_hid_connected_update(hid);
-
-    return 0;
-}
-
-static void bt_hid_destroy(struct bt_device_s *dev)
-{
-    struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
-
-    if (hid->connected)
-        bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG);
-    bt_l2cap_device_done(&hid->btdev);
-
-    hid_free(&hid->hid);
-
-    g_free(hid);
-}
-
-enum peripheral_minor_class {
-    class_other                = 0 << 4,
-    class_keyboard     = 1 << 4,
-    class_pointing     = 2 << 4,
-    class_combo                = 3 << 4,
-};
-
-static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net,
-                                       enum peripheral_minor_class minor)
-{
-    struct bt_hid_device_s *s = g_malloc0(sizeof(*s));
-    uint32_t class =
-            /* Format type */
-            (0 << 0) |
-            /* Device class */
-            (minor << 2) |
-            (5 << 8) |  /* "Peripheral" */
-            /* Service classes */
-            (1 << 13) | /* Limited discoverable mode */
-            (1 << 19);  /* Capturing device (?) */
-
-    bt_l2cap_device_init(&s->btdev, net);
-    bt_l2cap_sdp_init(&s->btdev);
-    bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_CTRL,
-                    BT_HID_MTU, bt_hid_new_control_ch);
-    bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR,
-                    BT_HID_MTU, bt_hid_new_interrupt_ch);
-
-    hid_init(&s->hid, HID_KEYBOARD, bt_hid_datain);
-    s->btdev.device.lmp_name = "BT Keyboard";
-
-    s->btdev.device.handle_destroy = bt_hid_destroy;
-
-    s->btdev.device.class[0] = (class >>  0) & 0xff;
-    s->btdev.device.class[1] = (class >>  8) & 0xff;
-    s->btdev.device.class[2] = (class >> 16) & 0xff;
-
-    return &s->btdev.device;
-}
-
-struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net)
-{
-    return bt_hid_init(net, class_keyboard);
-}
diff --git a/hw/bt-l2cap.c b/hw/bt-l2cap.c
deleted file mode 100644 (file)
index 521587a..0000000
+++ /dev/null
@@ -1,1365 +0,0 @@
-/*
- * QEMU Bluetooth L2CAP logic.
- *
- * Copyright (C) 2008 Andrzej Zaborowski  <balrog@zabor.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "qemu-common.h"
-#include "qemu/timer.h"
-#include "hw/bt.h"
-
-#define L2CAP_CID_MAX  0x100   /* Between 0x40 and 0x10000 */
-
-struct l2cap_instance_s {
-    struct bt_link_s *link;
-    struct bt_l2cap_device_s *dev;
-    int role;
-
-    uint8_t frame_in[65535 + L2CAP_HDR_SIZE] __attribute__ ((aligned (4)));
-    int frame_in_len;
-
-    uint8_t frame_out[65535 + L2CAP_HDR_SIZE] __attribute__ ((aligned (4)));
-    int frame_out_len;
-
-    /* Signalling channel timers.  They exist per-request but we can make
-     * sure we have no more than one outstanding request at any time.  */
-    QEMUTimer *rtx;
-    QEMUTimer *ertx;
-
-    int last_id;
-    int next_id;
-
-    struct l2cap_chan_s {
-        struct bt_l2cap_conn_params_s params;
-
-        void (*frame_in)(struct l2cap_chan_s *chan, uint16_t cid,
-                        const l2cap_hdr *hdr, int len);
-        int mps;
-        int min_mtu;
-
-        struct l2cap_instance_s *l2cap;
-
-        /* Only allocated channels */
-        uint16_t remote_cid;
-#define L2CAP_CFG_INIT 2
-#define L2CAP_CFG_ACC  1
-        int config_req_id; /* TODO: handle outgoing requests generically */
-        int config;
-
-        /* Only connection-oriented channels.  Note: if we allow the tx and
-         * rx traffic to be in different modes at any time, we need two.  */
-        int mode;
-
-        /* Only flow-controlled, connection-oriented channels */
-        uint8_t sdu[65536]; /* TODO: dynamically allocate */
-        int len_cur, len_total;
-        int rexmit;
-        int monitor_timeout;
-        QEMUTimer *monitor_timer;
-        QEMUTimer *retransmission_timer;
-    } *cid[L2CAP_CID_MAX];
-    /* The channel state machine states map as following:
-     * CLOSED           -> !cid[N]
-     * WAIT_CONNECT     -> never occurs
-     * WAIT_CONNECT_RSP -> never occurs
-     * CONFIG           -> cid[N] && config < 3
-     *   WAIT_CONFIG         -> never occurs, cid[N] && config == 0 && !config_r
-     *   WAIT_SEND_CONFIG    -> never occurs, cid[N] && config == 1 && !config_r
-     *   WAIT_CONFIG_REQ_RSP -> cid[N] && config == 0 && config_req_id
-     *   WAIT_CONFIG_RSP     -> cid[N] && config == 1 && config_req_id
-     *   WAIT_CONFIG_REQ     -> cid[N] && config == 2
-     * OPEN             -> cid[N] && config == 3
-     * WAIT_DISCONNECT  -> never occurs
-     */
-
-    struct l2cap_chan_s signalling_ch;
-    struct l2cap_chan_s group_ch;
-};
-
-struct slave_l2cap_instance_s {
-    struct bt_link_s link;     /* Underlying logical link (ACL) */
-    struct l2cap_instance_s l2cap;
-};
-
-struct bt_l2cap_psm_s {
-    int psm;
-    int min_mtu;
-    int (*new_channel)(struct bt_l2cap_device_s *device,
-                    struct bt_l2cap_conn_params_s *params);
-    struct bt_l2cap_psm_s *next;
-};
-
-static const uint16_t l2cap_fcs16_table[256] = {
-    0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
-    0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
-    0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
-    0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
-    0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
-    0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
-    0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
-    0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
-    0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
-    0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
-    0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
-    0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
-    0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
-    0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
-    0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
-    0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
-    0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
-    0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
-    0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
-    0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
-    0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
-    0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
-    0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
-    0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
-    0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
-    0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
-    0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
-    0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
-    0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
-    0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
-    0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
-    0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040,
-};
-
-static uint16_t l2cap_fcs16(const uint8_t *message, int len)
-{
-    uint16_t fcs = 0x0000;
-
-    while (len --)
-#if 0
-    {
-        int i;
-
-        fcs ^= *message ++;
-        for (i = 8; i; -- i)
-            if (fcs & 1)
-                fcs = (fcs >> 1) ^ 0xa001;
-            else
-                fcs = (fcs >> 1);
-    }
-#else
-        fcs = (fcs >> 8) ^ l2cap_fcs16_table[(fcs ^ *message ++) & 0xff];
-#endif
-
-    return fcs;
-}
-
-/* L2CAP layer logic (protocol) */
-
-static void l2cap_retransmission_timer_update(struct l2cap_chan_s *ch)
-{
-#if 0
-    if (ch->mode != L2CAP_MODE_BASIC && ch->rexmit)
-        qemu_mod_timer(ch->retransmission_timer);
-    else
-        qemu_del_timer(ch->retransmission_timer);
-#endif
-}
-
-static void l2cap_monitor_timer_update(struct l2cap_chan_s *ch)
-{
-#if 0
-    if (ch->mode != L2CAP_MODE_BASIC && !ch->rexmit)
-        qemu_mod_timer(ch->monitor_timer);
-    else
-        qemu_del_timer(ch->monitor_timer);
-#endif
-}
-
-static void l2cap_command_reject(struct l2cap_instance_s *l2cap, int id,
-                uint16_t reason, const void *data, int plen)
-{
-    uint8_t *pkt;
-    l2cap_cmd_hdr *hdr;
-    l2cap_cmd_rej *params;
-    uint16_t len;
-
-    reason = cpu_to_le16(reason);
-    len = cpu_to_le16(L2CAP_CMD_REJ_SIZE + plen);
-
-    pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
-                    L2CAP_CMD_HDR_SIZE + L2CAP_CMD_REJ_SIZE + plen);
-    hdr = (void *) (pkt + 0);
-    params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
-
-    hdr->code = L2CAP_COMMAND_REJ;
-    hdr->ident = id;
-    memcpy(&hdr->len, &len, sizeof(hdr->len));
-    memcpy(&params->reason, &reason, sizeof(reason));
-    if (plen)
-       memcpy(pkt + L2CAP_CMD_HDR_SIZE + L2CAP_CMD_REJ_SIZE, data, plen);
-
-    l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
-}
-
-static void l2cap_command_reject_cid(struct l2cap_instance_s *l2cap, int id,
-                uint16_t reason, uint16_t dcid, uint16_t scid)
-{
-    l2cap_cmd_rej_cid params = {
-        .dcid = dcid,
-        .scid = scid,
-    };
-
-    l2cap_command_reject(l2cap, id, reason, &params, L2CAP_CMD_REJ_CID_SIZE);
-}
-
-static void l2cap_connection_response(struct l2cap_instance_s *l2cap,
-                int dcid, int scid, int result, int status)
-{
-    uint8_t *pkt;
-    l2cap_cmd_hdr *hdr;
-    l2cap_conn_rsp *params;
-
-    pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
-                    L2CAP_CMD_HDR_SIZE + L2CAP_CONN_RSP_SIZE);
-    hdr = (void *) (pkt + 0);
-    params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
-
-    hdr->code = L2CAP_CONN_RSP;
-    hdr->ident = l2cap->last_id;
-    hdr->len = cpu_to_le16(L2CAP_CONN_RSP_SIZE);
-
-    params->dcid = cpu_to_le16(dcid);
-    params->scid = cpu_to_le16(scid);
-    params->result = cpu_to_le16(result);
-    params->status = cpu_to_le16(status);
-
-    l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
-}
-
-static void l2cap_configuration_request(struct l2cap_instance_s *l2cap,
-                int dcid, int flag, const uint8_t *data, int len)
-{
-    uint8_t *pkt;
-    l2cap_cmd_hdr *hdr;
-    l2cap_conf_req *params;
-
-    pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
-                    L2CAP_CMD_HDR_SIZE + L2CAP_CONF_REQ_SIZE(len));
-    hdr = (void *) (pkt + 0);
-    params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
-
-    /* TODO: unify the id sequencing */
-    l2cap->last_id = l2cap->next_id;
-    l2cap->next_id = l2cap->next_id == 255 ? 1 : l2cap->next_id + 1;
-
-    hdr->code = L2CAP_CONF_REQ;
-    hdr->ident = l2cap->last_id;
-    hdr->len = cpu_to_le16(L2CAP_CONF_REQ_SIZE(len));
-
-    params->dcid = cpu_to_le16(dcid);
-    params->flags = cpu_to_le16(flag);
-    if (len)
-        memcpy(params->data, data, len);
-
-    l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
-}
-
-static void l2cap_configuration_response(struct l2cap_instance_s *l2cap,
-                int scid, int flag, int result, const uint8_t *data, int len)
-{
-    uint8_t *pkt;
-    l2cap_cmd_hdr *hdr;
-    l2cap_conf_rsp *params;
-
-    pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
-                    L2CAP_CMD_HDR_SIZE + L2CAP_CONF_RSP_SIZE(len));
-    hdr = (void *) (pkt + 0);
-    params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
-
-    hdr->code = L2CAP_CONF_RSP;
-    hdr->ident = l2cap->last_id;
-    hdr->len = cpu_to_le16(L2CAP_CONF_RSP_SIZE(len));
-
-    params->scid = cpu_to_le16(scid);
-    params->flags = cpu_to_le16(flag);
-    params->result = cpu_to_le16(result);
-    if (len)
-        memcpy(params->data, data, len);
-
-    l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
-}
-
-static void l2cap_disconnection_response(struct l2cap_instance_s *l2cap,
-                int dcid, int scid)
-{
-    uint8_t *pkt;
-    l2cap_cmd_hdr *hdr;
-    l2cap_disconn_rsp *params;
-
-    pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
-                    L2CAP_CMD_HDR_SIZE + L2CAP_DISCONN_RSP_SIZE);
-    hdr = (void *) (pkt + 0);
-    params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
-
-    hdr->code = L2CAP_DISCONN_RSP;
-    hdr->ident = l2cap->last_id;
-    hdr->len = cpu_to_le16(L2CAP_DISCONN_RSP_SIZE);
-
-    params->dcid = cpu_to_le16(dcid);
-    params->scid = cpu_to_le16(scid);
-
-    l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
-}
-
-static void l2cap_echo_response(struct l2cap_instance_s *l2cap,
-                const uint8_t *data, int len)
-{
-    uint8_t *pkt;
-    l2cap_cmd_hdr *hdr;
-    uint8_t *params;
-
-    pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
-                    L2CAP_CMD_HDR_SIZE + len);
-    hdr = (void *) (pkt + 0);
-    params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
-
-    hdr->code = L2CAP_ECHO_RSP;
-    hdr->ident = l2cap->last_id;
-    hdr->len = cpu_to_le16(len);
-
-    memcpy(params, data, len);
-
-    l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
-}
-
-static void l2cap_info_response(struct l2cap_instance_s *l2cap, int type,
-                int result, const uint8_t *data, int len)
-{
-    uint8_t *pkt;
-    l2cap_cmd_hdr *hdr;
-    l2cap_info_rsp *params;
-
-    pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
-                    L2CAP_CMD_HDR_SIZE + L2CAP_INFO_RSP_SIZE + len);
-    hdr = (void *) (pkt + 0);
-    params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
-
-    hdr->code = L2CAP_INFO_RSP;
-    hdr->ident = l2cap->last_id;
-    hdr->len = cpu_to_le16(L2CAP_INFO_RSP_SIZE + len);
-
-    params->type = cpu_to_le16(type);
-    params->result = cpu_to_le16(result);
-    if (len)
-       memcpy(params->data, data, len);
-
-    l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
-}
-
-static uint8_t *l2cap_bframe_out(struct bt_l2cap_conn_params_s *parm, int len);
-static void l2cap_bframe_submit(struct bt_l2cap_conn_params_s *parms);
-#if 0
-static uint8_t *l2cap_iframe_out(struct bt_l2cap_conn_params_s *parm, int len);
-static void l2cap_iframe_submit(struct bt_l2cap_conn_params_s *parm);
-#endif
-static void l2cap_bframe_in(struct l2cap_chan_s *ch, uint16_t cid,
-                const l2cap_hdr *hdr, int len);
-static void l2cap_iframe_in(struct l2cap_chan_s *ch, uint16_t cid,
-                const l2cap_hdr *hdr, int len);
-
-static int l2cap_cid_new(struct l2cap_instance_s *l2cap)
-{
-    int i;
-
-    for (i = L2CAP_CID_ALLOC; i < L2CAP_CID_MAX; i ++)
-        if (!l2cap->cid[i])
-            return i;
-
-    return L2CAP_CID_INVALID;
-}
-
-static inline struct bt_l2cap_psm_s *l2cap_psm(
-                struct bt_l2cap_device_s *device, int psm)
-{
-    struct bt_l2cap_psm_s *ret = device->first_psm;
-
-    while (ret && ret->psm != psm)
-        ret = ret->next;
-
-    return ret;
-}
-
-static struct l2cap_chan_s *l2cap_channel_open(struct l2cap_instance_s *l2cap,
-                int psm, int source_cid)
-{
-    struct l2cap_chan_s *ch = NULL;
-    struct bt_l2cap_psm_s *psm_info;
-    int result, status;
-    int cid = l2cap_cid_new(l2cap);
-
-    if (cid) {
-        /* See what the channel is to be used for.. */
-        psm_info = l2cap_psm(l2cap->dev, psm);
-
-        if (psm_info) {
-            /* Device supports this use-case.  */
-            ch = g_malloc0(sizeof(*ch));
-            ch->params.sdu_out = l2cap_bframe_out;
-            ch->params.sdu_submit = l2cap_bframe_submit;
-            ch->frame_in = l2cap_bframe_in;
-            ch->mps = 65536;
-            ch->min_mtu = MAX(48, psm_info->min_mtu);
-            ch->params.remote_mtu = MAX(672, ch->min_mtu);
-            ch->remote_cid = source_cid;
-            ch->mode = L2CAP_MODE_BASIC;
-            ch->l2cap = l2cap;
-
-            /* Does it feel like opening yet another channel though?  */
-            if (!psm_info->new_channel(l2cap->dev, &ch->params)) {
-                l2cap->cid[cid] = ch;
-
-                result = L2CAP_CR_SUCCESS;
-                status = L2CAP_CS_NO_INFO;
-            } else {
-                g_free(ch);
-
-                result = L2CAP_CR_NO_MEM;
-                status = L2CAP_CS_NO_INFO;
-            }
-        } else {
-            result = L2CAP_CR_BAD_PSM;
-            status = L2CAP_CS_NO_INFO;
-        }
-    } else {
-        result = L2CAP_CR_NO_MEM;
-        status = L2CAP_CS_NO_INFO;
-    }
-
-    l2cap_connection_response(l2cap, cid, source_cid, result, status);
-
-    return ch;
-}
-
-static void l2cap_channel_close(struct l2cap_instance_s *l2cap,
-                int cid, int source_cid)
-{
-    struct l2cap_chan_s *ch = NULL;
-
-    /* According to Volume 3, section 6.1.1, pg 1048 of BT Core V2.0, a
-     * connection in CLOSED state still responds with a L2CAP_DisconnectRsp
-     * message on an L2CAP_DisconnectReq event.  */
-    if (unlikely(cid < L2CAP_CID_ALLOC)) {
-        l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL,
-                        cid, source_cid);
-        return;
-    }
-    if (likely(cid >= L2CAP_CID_ALLOC && cid < L2CAP_CID_MAX))
-        ch = l2cap->cid[cid];
-
-    if (likely(ch)) {
-        if (ch->remote_cid != source_cid) {
-            fprintf(stderr, "%s: Ignoring a Disconnection Request with the "
-                            "invalid SCID %04x.\n", __FUNCTION__, source_cid);
-            return;
-        }
-
-        l2cap->cid[cid] = NULL;
-
-        ch->params.close(ch->params.opaque);
-        g_free(ch);
-    }
-
-    l2cap_disconnection_response(l2cap, cid, source_cid);
-}
-
-static void l2cap_channel_config_null(struct l2cap_instance_s *l2cap,
-                struct l2cap_chan_s *ch)
-{
-    l2cap_configuration_request(l2cap, ch->remote_cid, 0, NULL, 0);
-    ch->config_req_id = l2cap->last_id;
-    ch->config &= ~L2CAP_CFG_INIT;
-}
-
-static void l2cap_channel_config_req_event(struct l2cap_instance_s *l2cap,
-                struct l2cap_chan_s *ch)
-{
-    /* Use all default channel options and terminate negotiation.  */
-    l2cap_channel_config_null(l2cap, ch);
-}
-
-static int l2cap_channel_config(struct l2cap_instance_s *l2cap,
-                struct l2cap_chan_s *ch, int flag,
-                const uint8_t *data, int len)
-{
-    l2cap_conf_opt *opt;
-    l2cap_conf_opt_qos *qos;
-    uint32_t val;
-    uint8_t rsp[len];
-    int result = L2CAP_CONF_SUCCESS;
-
-    data = memcpy(rsp, data, len);
-    while (len) {
-        opt = (void *) data;
-
-        if (len < L2CAP_CONF_OPT_SIZE ||
-                        len < L2CAP_CONF_OPT_SIZE + opt->len) {
-            result = L2CAP_CONF_REJECT;
-            break;
-        }
-        data += L2CAP_CONF_OPT_SIZE + opt->len;
-        len -= L2CAP_CONF_OPT_SIZE + opt->len;
-
-        switch (opt->type & 0x7f) {
-        case L2CAP_CONF_MTU:
-            if (opt->len != 2) {
-                result = L2CAP_CONF_REJECT;
-                break;
-            }
-
-            /* MTU */
-            val = le16_to_cpup((void *) opt->val);
-            if (val < ch->min_mtu) {
-                cpu_to_le16w((void *) opt->val, ch->min_mtu);
-                result = L2CAP_CONF_UNACCEPT;
-                break;
-            }
-
-            ch->params.remote_mtu = val;
-            break;
-
-        case L2CAP_CONF_FLUSH_TO:
-            if (opt->len != 2) {
-                result = L2CAP_CONF_REJECT;
-                break;
-            }
-
-            /* Flush Timeout */
-            val = le16_to_cpup((void *) opt->val);
-            if (val < 0x0001) {
-                opt->val[0] = 0xff;
-                opt->val[1] = 0xff;
-                result = L2CAP_CONF_UNACCEPT;
-                break;
-            }
-            break;
-
-        case L2CAP_CONF_QOS:
-            if (opt->len != L2CAP_CONF_OPT_QOS_SIZE) {
-                result = L2CAP_CONF_REJECT;
-                break;
-            }
-            qos = (void *) opt->val;
-
-            /* Flags */
-            val = qos->flags;
-            if (val) {
-                qos->flags = 0;
-                result = L2CAP_CONF_UNACCEPT;
-            }
-
-            /* Service type */
-            val = qos->service_type;
-            if (val != L2CAP_CONF_QOS_BEST_EFFORT &&
-                            val != L2CAP_CONF_QOS_NO_TRAFFIC) {
-                qos->service_type = L2CAP_CONF_QOS_BEST_EFFORT;
-                result = L2CAP_CONF_UNACCEPT;
-            }
-
-            if (val != L2CAP_CONF_QOS_NO_TRAFFIC) {
-                /* XXX: These values should possibly be calculated
-                 * based on LM / baseband properties also.  */
-
-                /* Token rate */
-                val = le32_to_cpu(qos->token_rate);
-                if (val == L2CAP_CONF_QOS_WILDCARD)
-                    qos->token_rate = cpu_to_le32(0x100000);
-
-                /* Token bucket size */
-                val = le32_to_cpu(qos->token_bucket_size);
-                if (val == L2CAP_CONF_QOS_WILDCARD)
-                    qos->token_bucket_size = cpu_to_le32(65500);
-
-                /* Any Peak bandwidth value is correct to return as-is */
-                /* Any Access latency value is correct to return as-is */
-                /* Any Delay variation value is correct to return as-is */
-            }
-            break;
-
-        case L2CAP_CONF_RFC:
-            if (opt->len != 9) {
-                result = L2CAP_CONF_REJECT;
-                break;
-            }
-
-            /* Mode */
-            val = opt->val[0];
-            switch (val) {
-            case L2CAP_MODE_BASIC:
-                ch->mode = val;
-                ch->frame_in = l2cap_bframe_in;
-
-                /* All other parameters shall be ignored */
-                break;
-
-            case L2CAP_MODE_RETRANS:
-            case L2CAP_MODE_FLOWCTL:
-                ch->mode = val;
-                ch->frame_in = l2cap_iframe_in;
-                /* Note: most of these parameters refer to incoming traffic
-                 * so we don't need to save them as long as we can accept
-                 * incoming PDUs at any values of the parameters.  */
-
-                /* TxWindow size */
-                val = opt->val[1];
-                if (val < 1 || val > 32) {
-                    opt->val[1] = 32;
-                    result = L2CAP_CONF_UNACCEPT;
-                    break;
-                }
-
-                /* MaxTransmit */
-                val = opt->val[2];
-                if (val < 1) {
-                    opt->val[2] = 1;
-                    result = L2CAP_CONF_UNACCEPT;
-                    break;
-                }
-
-                /* Remote Retransmission time-out shouldn't affect local
-                 * operation (?) */
-
-                /* The Monitor time-out drives the local Monitor timer (?),
-                 * so save the value.  */
-                val = (opt->val[6] << 8) | opt->val[5];
-                if (val < 30) {
-                    opt->val[5] = 100 & 0xff;
-                    opt->val[6] = 100 >> 8;
-                    result = L2CAP_CONF_UNACCEPT;
-                    break;
-                }
-                ch->monitor_timeout = val;
-                l2cap_monitor_timer_update(ch);
-
-                /* MPS */
-                val = (opt->val[8] << 8) | opt->val[7];
-                if (val < ch->min_mtu) {
-                    opt->val[7] = ch->min_mtu & 0xff;
-                    opt->val[8] = ch->min_mtu >> 8;
-                    result = L2CAP_CONF_UNACCEPT;
-                    break;
-                }
-                ch->mps = val;
-                break;
-
-            default:
-                result = L2CAP_CONF_UNACCEPT;
-                break;
-            }
-            break;
-
-        default:
-            if (!(opt->type >> 7))
-                result = L2CAP_CONF_UNKNOWN;
-            break;
-        }
-
-        if (result != L2CAP_CONF_SUCCESS)
-            break;     /* XXX: should continue? */
-    }
-
-    l2cap_configuration_response(l2cap, ch->remote_cid,
-                    flag, result, rsp, len);
-
-    return result == L2CAP_CONF_SUCCESS && !flag;
-}
-
-static void l2cap_channel_config_req_msg(struct l2cap_instance_s *l2cap,
-                int flag, int cid, const uint8_t *data, int len)
-{
-    struct l2cap_chan_s *ch;
-
-    if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) {
-        l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL,
-                        cid, 0x0000);
-        return;
-    }
-    ch = l2cap->cid[cid];
-
-    /* From OPEN go to WAIT_CONFIG_REQ and from WAIT_CONFIG_REQ_RSP to
-     * WAIT_CONFIG_REQ_RSP.  This is assuming the transition chart for OPEN
-     * on pg 1053, section 6.1.5, volume 3 of BT Core V2.0 has a mistake
-     * and on options-acceptable we go back to OPEN and otherwise to
-     * WAIT_CONFIG_REQ and not the other way.  */
-    ch->config &= ~L2CAP_CFG_ACC;
-
-    if (l2cap_channel_config(l2cap, ch, flag, data, len))
-        /* Go to OPEN or WAIT_CONFIG_RSP */
-        ch->config |= L2CAP_CFG_ACC;
-
-    /* TODO: if the incoming traffic flow control or retransmission mode
-     * changed then we probably need to also generate the
-     * ConfigureChannel_Req event and set the outgoing traffic to the same
-     * mode.  */
-    if (!(ch->config & L2CAP_CFG_INIT) && (ch->config & L2CAP_CFG_ACC) &&
-                    !ch->config_req_id)
-        l2cap_channel_config_req_event(l2cap, ch);
-}
-
-static int l2cap_channel_config_rsp_msg(struct l2cap_instance_s *l2cap,
-                int result, int flag, int cid, const uint8_t *data, int len)
-{
-    struct l2cap_chan_s *ch;
-
-    if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) {
-        l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL,
-                        cid, 0x0000);
-        return 0;
-    }
-    ch = l2cap->cid[cid];
-
-    if (ch->config_req_id != l2cap->last_id)
-        return 1;
-    ch->config_req_id = 0;
-
-    if (result == L2CAP_CONF_SUCCESS) {
-        if (!flag)
-            ch->config |= L2CAP_CFG_INIT;
-        else
-            l2cap_channel_config_null(l2cap, ch);
-    } else
-        /* Retry until we succeed */
-        l2cap_channel_config_req_event(l2cap, ch);
-
-    return 0;
-}
-
-static void l2cap_channel_open_req_msg(struct l2cap_instance_s *l2cap,
-                int psm, int source_cid)
-{
-    struct l2cap_chan_s *ch = l2cap_channel_open(l2cap, psm, source_cid);
-
-    if (!ch)
-        return;
-
-    /* Optional */
-    if (!(ch->config & L2CAP_CFG_INIT) && !ch->config_req_id)
-        l2cap_channel_config_req_event(l2cap, ch);
-}
-
-static void l2cap_info(struct l2cap_instance_s *l2cap, int type)
-{
-    uint8_t data[4];
-    int len = 0;
-    int result = L2CAP_IR_SUCCESS;
-
-    switch (type) {
-    case L2CAP_IT_CL_MTU:
-        data[len ++] = l2cap->group_ch.mps & 0xff;
-        data[len ++] = l2cap->group_ch.mps >> 8;
-        break;
-
-    case L2CAP_IT_FEAT_MASK:
-        /* (Prematurely) report Flow control and Retransmission modes.  */
-        data[len ++] = 0x03;
-        data[len ++] = 0x00;
-        data[len ++] = 0x00;
-        data[len ++] = 0x00;
-        break;
-
-    default:
-        result = L2CAP_IR_NOTSUPP;
-    }
-
-    l2cap_info_response(l2cap, type, result, data, len);
-}
-
-static void l2cap_command(struct l2cap_instance_s *l2cap, int code, int id,
-                const uint8_t *params, int len)
-{
-    int err;
-
-#if 0
-    /* TODO: do the IDs really have to be in sequence?  */
-    if (!id || (id != l2cap->last_id && id != l2cap->next_id)) {
-        fprintf(stderr, "%s: out of sequence command packet ignored.\n",
-                        __FUNCTION__);
-        return;
-    }
-#else
-    l2cap->next_id = id;
-#endif
-    if (id == l2cap->next_id) {
-        l2cap->last_id = l2cap->next_id;
-        l2cap->next_id = l2cap->next_id == 255 ? 1 : l2cap->next_id + 1;
-    } else {
-        /* TODO: Need to re-send the same response, without re-executing
-         * the corresponding command!  */
-    }
-
-    switch (code) {
-    case L2CAP_COMMAND_REJ:
-        if (unlikely(len != 2 && len != 4 && len != 6)) {
-            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
-            goto reject;
-        }
-
-        /* We never issue commands other than Command Reject currently.  */
-        fprintf(stderr, "%s: stray Command Reject (%02x, %04x) "
-                        "packet, ignoring.\n", __FUNCTION__, id,
-                        le16_to_cpu(((l2cap_cmd_rej *) params)->reason));
-        break;
-
-    case L2CAP_CONN_REQ:
-        if (unlikely(len != L2CAP_CONN_REQ_SIZE)) {
-            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
-            goto reject;
-        }
-
-        l2cap_channel_open_req_msg(l2cap,
-                        le16_to_cpu(((l2cap_conn_req *) params)->psm),
-                        le16_to_cpu(((l2cap_conn_req *) params)->scid));
-        break;
-
-    case L2CAP_CONN_RSP:
-        if (unlikely(len != L2CAP_CONN_RSP_SIZE)) {
-            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
-            goto reject;
-        }
-
-        /* We never issue Connection Requests currently. TODO  */
-        fprintf(stderr, "%s: unexpected Connection Response (%02x) "
-                        "packet, ignoring.\n", __FUNCTION__, id);
-        break;
-
-    case L2CAP_CONF_REQ:
-        if (unlikely(len < L2CAP_CONF_REQ_SIZE(0))) {
-            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
-            goto reject;
-        }
-
-        l2cap_channel_config_req_msg(l2cap,
-                        le16_to_cpu(((l2cap_conf_req *) params)->flags) & 1,
-                        le16_to_cpu(((l2cap_conf_req *) params)->dcid),
-                        ((l2cap_conf_req *) params)->data,
-                        len - L2CAP_CONF_REQ_SIZE(0));
-        break;
-
-    case L2CAP_CONF_RSP:
-        if (unlikely(len < L2CAP_CONF_RSP_SIZE(0))) {
-            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
-            goto reject;
-        }
-
-        if (l2cap_channel_config_rsp_msg(l2cap,
-                        le16_to_cpu(((l2cap_conf_rsp *) params)->result),
-                        le16_to_cpu(((l2cap_conf_rsp *) params)->flags) & 1,
-                        le16_to_cpu(((l2cap_conf_rsp *) params)->scid),
-                        ((l2cap_conf_rsp *) params)->data,
-                        len - L2CAP_CONF_RSP_SIZE(0)))
-            fprintf(stderr, "%s: unexpected Configure Response (%02x) "
-                            "packet, ignoring.\n", __FUNCTION__, id);
-        break;
-
-    case L2CAP_DISCONN_REQ:
-        if (unlikely(len != L2CAP_DISCONN_REQ_SIZE)) {
-            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
-            goto reject;
-        }
-
-        l2cap_channel_close(l2cap,
-                        le16_to_cpu(((l2cap_disconn_req *) params)->dcid),
-                        le16_to_cpu(((l2cap_disconn_req *) params)->scid));
-        break;
-
-    case L2CAP_DISCONN_RSP:
-        if (unlikely(len != L2CAP_DISCONN_RSP_SIZE)) {
-            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
-            goto reject;
-        }
-
-        /* We never issue Disconnection Requests currently. TODO  */
-        fprintf(stderr, "%s: unexpected Disconnection Response (%02x) "
-                        "packet, ignoring.\n", __FUNCTION__, id);
-        break;
-
-    case L2CAP_ECHO_REQ:
-        l2cap_echo_response(l2cap, params, len);
-        break;
-
-    case L2CAP_ECHO_RSP:
-        /* We never issue Echo Requests currently. TODO  */
-        fprintf(stderr, "%s: unexpected Echo Response (%02x) "
-                        "packet, ignoring.\n", __FUNCTION__, id);
-        break;
-
-    case L2CAP_INFO_REQ:
-        if (unlikely(len != L2CAP_INFO_REQ_SIZE)) {
-            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
-            goto reject;
-        }
-
-        l2cap_info(l2cap, le16_to_cpu(((l2cap_info_req *) params)->type));
-        break;
-
-    case L2CAP_INFO_RSP:
-        if (unlikely(len != L2CAP_INFO_RSP_SIZE)) {
-            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
-            goto reject;
-        }
-
-        /* We never issue Information Requests currently. TODO  */
-        fprintf(stderr, "%s: unexpected Information Response (%02x) "
-                        "packet, ignoring.\n", __FUNCTION__, id);
-        break;
-
-    default:
-        err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
-    reject:
-        l2cap_command_reject(l2cap, id, err, 0, 0);
-        break;
-    }
-}
-
-static void l2cap_rexmit_enable(struct l2cap_chan_s *ch, int enable)
-{
-    ch->rexmit = enable;
-
-    l2cap_retransmission_timer_update(ch);
-    l2cap_monitor_timer_update(ch);
-}
-
-/* Command frame SDU */
-static void l2cap_cframe_in(void *opaque, const uint8_t *data, int len)
-{
-    struct l2cap_instance_s *l2cap = opaque;
-    const l2cap_cmd_hdr *hdr;
-    int clen;
-
-    while (len) {
-        hdr = (void *) data;
-        if (len < L2CAP_CMD_HDR_SIZE)
-            /* TODO: signal an error */
-            return;
-        len -= L2CAP_CMD_HDR_SIZE;
-        data += L2CAP_CMD_HDR_SIZE;
-
-        clen = le16_to_cpu(hdr->len);
-        if (len < clen) {
-            l2cap_command_reject(l2cap, hdr->ident,
-                            L2CAP_REJ_CMD_NOT_UNDERSTOOD, 0, 0);
-            break;
-        }
-
-        l2cap_command(l2cap, hdr->code, hdr->ident, data, clen);
-        len -= clen;
-        data += clen;
-    }
-}
-
-/* Group frame SDU */
-static void l2cap_gframe_in(void *opaque, const uint8_t *data, int len)
-{
-}
-
-/* Supervisory frame */
-static void l2cap_sframe_in(struct l2cap_chan_s *ch, uint16_t ctrl)
-{
-}
-
-/* Basic L2CAP mode Information frame */
-static void l2cap_bframe_in(struct l2cap_chan_s *ch, uint16_t cid,
-                const l2cap_hdr *hdr, int len)
-{
-    /* We have a full SDU, no further processing */
-    ch->params.sdu_in(ch->params.opaque, hdr->data, len);
-}
-
-/* Flow Control and Retransmission mode frame */
-static void l2cap_iframe_in(struct l2cap_chan_s *ch, uint16_t cid,
-                const l2cap_hdr *hdr, int len)
-{
-    uint16_t fcs = le16_to_cpup((void *) (hdr->data + len - 2));
-
-    if (len < 4)
-        goto len_error;
-    if (l2cap_fcs16((const uint8_t *) hdr, L2CAP_HDR_SIZE + len - 2) != fcs)
-        goto fcs_error;
-
-    if ((hdr->data[0] >> 7) == ch->rexmit)
-        l2cap_rexmit_enable(ch, !(hdr->data[0] >> 7));
-
-    if (hdr->data[0] & 1) {
-        if (len != 4) {
-            /* TODO: Signal an error? */
-            return;
-        }
-        l2cap_sframe_in(ch, le16_to_cpup((void *) hdr->data));
-        return;
-    }
-
-    switch (hdr->data[1] >> 6) {       /* SAR */
-    case L2CAP_SAR_NO_SEG:
-        if (ch->len_total)
-            goto seg_error;
-        if (len - 4 > ch->mps)
-            goto len_error;
-
-        ch->params.sdu_in(ch->params.opaque, hdr->data + 2, len - 4);
-        break;
-
-    case L2CAP_SAR_START:
-        if (ch->len_total || len < 6)
-            goto seg_error;
-        if (len - 6 > ch->mps)
-            goto len_error;
-
-        ch->len_total = le16_to_cpup((void *) (hdr->data + 2));
-        if (len >= 6 + ch->len_total)
-            goto seg_error;
-
-        ch->len_cur = len - 6;
-        memcpy(ch->sdu, hdr->data + 4, ch->len_cur);
-        break;
-
-    case L2CAP_SAR_END:
-        if (!ch->len_total || ch->len_cur + len - 4 < ch->len_total)
-            goto seg_error;
-        if (len - 4 > ch->mps)
-            goto len_error;
-
-        memcpy(ch->sdu + ch->len_cur, hdr->data + 2, len - 4);
-        ch->params.sdu_in(ch->params.opaque, ch->sdu, ch->len_total);
-        break;
-
-    case L2CAP_SAR_CONT:
-        if (!ch->len_total || ch->len_cur + len - 4 >= ch->len_total)
-            goto seg_error;
-        if (len - 4 > ch->mps)
-            goto len_error;
-
-        memcpy(ch->sdu + ch->len_cur, hdr->data + 2, len - 4);
-        ch->len_cur += len - 4;
-        break;
-
-    seg_error:
-    len_error: /* TODO */
-    fcs_error: /* TODO */
-        ch->len_cur = 0;
-        ch->len_total = 0;
-        break;
-    }
-}
-
-static void l2cap_frame_in(struct l2cap_instance_s *l2cap,
-                const l2cap_hdr *frame)
-{
-    uint16_t cid = le16_to_cpu(frame->cid);
-    uint16_t len = le16_to_cpu(frame->len);
-
-    if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) {
-        fprintf(stderr, "%s: frame addressed to a non-existent L2CAP "
-                        "channel %04x received.\n", __FUNCTION__, cid);
-        return;
-    }
-
-    l2cap->cid[cid]->frame_in(l2cap->cid[cid], cid, frame, len);
-}
-
-/* "Recombination" */
-static void l2cap_pdu_in(struct l2cap_instance_s *l2cap,
-                const uint8_t *data, int len)
-{
-    const l2cap_hdr *hdr = (void *) l2cap->frame_in;
-
-    if (unlikely(len + l2cap->frame_in_len > sizeof(l2cap->frame_in))) {
-        if (l2cap->frame_in_len < sizeof(l2cap->frame_in)) {
-            memcpy(l2cap->frame_in + l2cap->frame_in_len, data,
-                            sizeof(l2cap->frame_in) - l2cap->frame_in_len);
-            l2cap->frame_in_len = sizeof(l2cap->frame_in);
-            /* TODO: truncate */
-            l2cap_frame_in(l2cap, hdr);
-        }
-
-        return;
-    }
-
-    memcpy(l2cap->frame_in + l2cap->frame_in_len, data, len);
-    l2cap->frame_in_len += len;
-
-    if (len >= L2CAP_HDR_SIZE)
-        if (len >= L2CAP_HDR_SIZE + le16_to_cpu(hdr->len))
-            l2cap_frame_in(l2cap, hdr);
-            /* There is never a start of a new PDU in the same ACL packet, so
-             * no need to memmove the remaining payload and loop.  */
-}
-
-static inline uint8_t *l2cap_pdu_out(struct l2cap_instance_s *l2cap,
-                uint16_t cid, uint16_t len)
-{
-    l2cap_hdr *hdr = (void *) l2cap->frame_out;
-
-    l2cap->frame_out_len = len + L2CAP_HDR_SIZE;
-
-    hdr->cid = cpu_to_le16(cid);
-    hdr->len = cpu_to_le16(len);
-
-    return l2cap->frame_out + L2CAP_HDR_SIZE;
-}
-
-static inline void l2cap_pdu_submit(struct l2cap_instance_s *l2cap)
-{
-    /* TODO: Fragmentation */
-    (l2cap->role ?
-     l2cap->link->slave->lmp_acl_data : l2cap->link->host->lmp_acl_resp)
-            (l2cap->link, l2cap->frame_out, 1, l2cap->frame_out_len);
-}
-
-static uint8_t *l2cap_bframe_out(struct bt_l2cap_conn_params_s *parm, int len)
-{
-    struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parm;
-
-    if (len > chan->params.remote_mtu) {
-        fprintf(stderr, "%s: B-Frame for CID %04x longer than %i octets.\n",
-                        __FUNCTION__,
-                        chan->remote_cid, chan->params.remote_mtu);
-        exit(-1);
-    }
-
-    return l2cap_pdu_out(chan->l2cap, chan->remote_cid, len);
-}
-
-static void l2cap_bframe_submit(struct bt_l2cap_conn_params_s *parms)
-{
-    struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parms;
-
-    l2cap_pdu_submit(chan->l2cap);
-}
-
-#if 0
-/* Stub: Only used if an emulated device requests outgoing flow control */
-static uint8_t *l2cap_iframe_out(struct bt_l2cap_conn_params_s *parm, int len)
-{
-    struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parm;
-
-    if (len > chan->params.remote_mtu) {
-        /* TODO: slice into segments and queue each segment as a separate
-         * I-Frame in a FIFO of I-Frames, local to the CID.  */
-    } else {
-        /* TODO: add to the FIFO of I-Frames, local to the CID.  */
-        /* Possibly we need to return a pointer to a contiguous buffer
-         * for now and then memcpy from it into FIFOs in l2cap_iframe_submit
-         * while segmenting at the same time.  */
-    }
-    return 0;
-}
-
-static void l2cap_iframe_submit(struct bt_l2cap_conn_params_s *parm)
-{
-    /* TODO: If flow control indicates clear to send, start submitting the
-     * invidual I-Frames from the FIFO, but don't remove them from there.
-     * Kick the appropriate timer until we get an S-Frame, and only then
-     * remove from FIFO or resubmit and re-kick the timer if the timer
-     * expired.  */
-}
-#endif
-
-static void l2cap_init(struct l2cap_instance_s *l2cap,
-                struct bt_link_s *link, int role)
-{
-    l2cap->link = link;
-    l2cap->role = role;
-    l2cap->dev = (struct bt_l2cap_device_s *)
-            (role ? link->host : link->slave);
-
-    l2cap->next_id = 1;
-
-    /* Establish the signalling channel */
-    l2cap->signalling_ch.params.sdu_in = l2cap_cframe_in;
-    l2cap->signalling_ch.params.sdu_out = l2cap_bframe_out;
-    l2cap->signalling_ch.params.sdu_submit = l2cap_bframe_submit;
-    l2cap->signalling_ch.params.opaque = l2cap;
-    l2cap->signalling_ch.params.remote_mtu = 48;
-    l2cap->signalling_ch.remote_cid = L2CAP_CID_SIGNALLING;
-    l2cap->signalling_ch.frame_in = l2cap_bframe_in;
-    l2cap->signalling_ch.mps = 65536;
-    l2cap->signalling_ch.min_mtu = 48;
-    l2cap->signalling_ch.mode = L2CAP_MODE_BASIC;
-    l2cap->signalling_ch.l2cap = l2cap;
-    l2cap->cid[L2CAP_CID_SIGNALLING] = &l2cap->signalling_ch;
-
-    /* Establish the connection-less data channel */
-    l2cap->group_ch.params.sdu_in = l2cap_gframe_in;
-    l2cap->group_ch.params.opaque = l2cap;
-    l2cap->group_ch.frame_in = l2cap_bframe_in;
-    l2cap->group_ch.mps = 65533;
-    l2cap->group_ch.l2cap = l2cap;
-    l2cap->group_ch.remote_cid = L2CAP_CID_INVALID;
-    l2cap->cid[L2CAP_CID_GROUP] = &l2cap->group_ch;
-}
-
-static void l2cap_teardown(struct l2cap_instance_s *l2cap, int send_disconnect)
-{
-    int cid;
-
-    /* Don't send DISCONNECT if we are currently handling a DISCONNECT
-     * sent from the other side.  */
-    if (send_disconnect) {
-        if (l2cap->role)
-            l2cap->dev->device.lmp_disconnect_slave(l2cap->link);
-            /* l2cap->link is invalid from now on.  */
-        else
-            l2cap->dev->device.lmp_disconnect_master(l2cap->link);
-    }
-
-    for (cid = L2CAP_CID_ALLOC; cid < L2CAP_CID_MAX; cid ++)
-        if (l2cap->cid[cid]) {
-            l2cap->cid[cid]->params.close(l2cap->cid[cid]->params.opaque);
-            g_free(l2cap->cid[cid]);
-        }
-
-    if (l2cap->role)
-        g_free(l2cap);
-    else
-        g_free(l2cap->link);
-}
-
-/* L2CAP glue to lower layers in bluetooth stack (LMP) */
-
-static void l2cap_lmp_connection_request(struct bt_link_s *link)
-{
-    struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->slave;
-    struct slave_l2cap_instance_s *l2cap;
-
-    /* Always accept - we only get called if (dev->device->page_scan).  */
-
-    l2cap = g_malloc0(sizeof(struct slave_l2cap_instance_s));
-    l2cap->link.slave = &dev->device;
-    l2cap->link.host = link->host;
-    l2cap_init(&l2cap->l2cap, &l2cap->link, 0);
-
-    /* Always at the end */
-    link->host->reject_reason = 0;
-    link->host->lmp_connection_complete(&l2cap->link);
-}
-
-/* Stub */
-static void l2cap_lmp_connection_complete(struct bt_link_s *link)
-{
-    struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host;
-    struct l2cap_instance_s *l2cap;
-
-    if (dev->device.reject_reason) {
-        /* Signal to upper layer */
-        return;
-    }
-
-    l2cap = g_malloc0(sizeof(struct l2cap_instance_s));
-    l2cap_init(l2cap, link, 1);
-
-    link->acl_mode = acl_active;
-
-    /* Signal to upper layer */
-}
-
-/* Stub */
-static void l2cap_lmp_disconnect_host(struct bt_link_s *link)
-{
-    struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host;
-    struct l2cap_instance_s *l2cap =
-            /* TODO: Retrieve from upper layer */ (void *) dev;
-
-    /* Signal to upper layer */
-
-    l2cap_teardown(l2cap, 0);
-}
-
-static void l2cap_lmp_disconnect_slave(struct bt_link_s *link)
-{
-    struct slave_l2cap_instance_s *l2cap =
-            (struct slave_l2cap_instance_s *) link;
-
-    l2cap_teardown(&l2cap->l2cap, 0);
-}
-
-static void l2cap_lmp_acl_data_slave(struct bt_link_s *link,
-                const uint8_t *data, int start, int len)
-{
-    struct slave_l2cap_instance_s *l2cap =
-            (struct slave_l2cap_instance_s *) link;
-
-    if (start)
-        l2cap->l2cap.frame_in_len = 0;
-
-    l2cap_pdu_in(&l2cap->l2cap, data, len);
-}
-
-/* Stub */
-static void l2cap_lmp_acl_data_host(struct bt_link_s *link,
-                const uint8_t *data, int start, int len)
-{
-    struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host;
-    struct l2cap_instance_s *l2cap =
-            /* TODO: Retrieve from upper layer */ (void *) dev;
-
-    if (start)
-        l2cap->frame_in_len = 0;
-
-    l2cap_pdu_in(l2cap, data, len);
-}
-
-static void l2cap_dummy_destroy(struct bt_device_s *dev)
-{
-    struct bt_l2cap_device_s *l2cap_dev = (struct bt_l2cap_device_s *) dev;
-
-    bt_l2cap_device_done(l2cap_dev);
-}
-
-void bt_l2cap_device_init(struct bt_l2cap_device_s *dev,
-                struct bt_scatternet_s *net)
-{
-    bt_device_init(&dev->device, net);
-
-    dev->device.lmp_connection_request = l2cap_lmp_connection_request;
-    dev->device.lmp_connection_complete = l2cap_lmp_connection_complete;
-    dev->device.lmp_disconnect_master = l2cap_lmp_disconnect_host;
-    dev->device.lmp_disconnect_slave = l2cap_lmp_disconnect_slave;
-    dev->device.lmp_acl_data = l2cap_lmp_acl_data_slave;
-    dev->device.lmp_acl_resp = l2cap_lmp_acl_data_host;
-
-    dev->device.handle_destroy = l2cap_dummy_destroy;
-}
-
-void bt_l2cap_device_done(struct bt_l2cap_device_s *dev)
-{
-    bt_device_done(&dev->device);
-
-    /* Should keep a list of all instances and go through it and
-     * invoke l2cap_teardown() for each.  */
-}
-
-void bt_l2cap_psm_register(struct bt_l2cap_device_s *dev, int psm, int min_mtu,
-                int (*new_channel)(struct bt_l2cap_device_s *dev,
-                        struct bt_l2cap_conn_params_s *params))
-{
-    struct bt_l2cap_psm_s *new_psm = l2cap_psm(dev, psm);
-
-    if (new_psm) {
-        fprintf(stderr, "%s: PSM %04x already registered for device `%s'.\n",
-                        __FUNCTION__, psm, dev->device.lmp_name);
-        exit(-1);
-    }
-
-    new_psm = g_malloc0(sizeof(*new_psm));
-    new_psm->psm = psm;
-    new_psm->min_mtu = min_mtu;
-    new_psm->new_channel = new_channel;
-    new_psm->next = dev->first_psm;
-    dev->first_psm = new_psm;
-}
diff --git a/hw/bt-sdp.c b/hw/bt-sdp.c
deleted file mode 100644 (file)
index 218e075..0000000
+++ /dev/null
@@ -1,967 +0,0 @@
-/*
- * Service Discover Protocol server for QEMU L2CAP devices
- *
- * Copyright (C) 2008 Andrzej Zaborowski  <balrog@zabor.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "qemu-common.h"
-#include "hw/bt.h"
-
-struct bt_l2cap_sdp_state_s {
-    struct bt_l2cap_conn_params_s *channel;
-
-    struct sdp_service_record_s {
-        int match;
-
-        int *uuid;
-        int uuids;
-        struct sdp_service_attribute_s {
-            int match;
-
-            int attribute_id;
-            int len;
-            void *pair;
-        } *attribute_list;
-        int attributes;
-    } *service_list;
-    int services;
-};
-
-static ssize_t sdp_datalen(const uint8_t **element, ssize_t *left)
-{
-    size_t len = *(*element) ++ & SDP_DSIZE_MASK;
-
-    if (!*left)
-        return -1;
-    (*left) --;
-
-    if (len < SDP_DSIZE_NEXT1)
-        return 1 << len;
-    else if (len == SDP_DSIZE_NEXT1) {
-        if (*left < 1)
-            return -1;
-        (*left) --;
-
-        return *(*element) ++;
-    } else if (len == SDP_DSIZE_NEXT2) {
-        if (*left < 2)
-            return -1;
-        (*left) -= 2;
-
-        len = (*(*element) ++) << 8;
-        return len | (*(*element) ++);
-    } else {
-        if (*left < 4)
-            return -1;
-        (*left) -= 4;
-
-        len = (*(*element) ++) << 24;
-        len |= (*(*element) ++) << 16;
-        len |= (*(*element) ++) << 8;
-        return len | (*(*element) ++);
-    }
-}
-
-static const uint8_t bt_base_uuid[12] = {
-    0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
-};
-
-static int sdp_uuid_match(struct sdp_service_record_s *record,
-                const uint8_t *uuid, ssize_t datalen)
-{
-    int *lo, hi, val;
-
-    if (datalen == 16 || datalen == 4) {
-        if (datalen == 16 && memcmp(uuid + 4, bt_base_uuid, 12))
-            return 0;
-
-        if (uuid[0] | uuid[1])
-            return 0;
-        uuid += 2;
-    }
-
-    val = (uuid[0] << 8) | uuid[1];
-    lo = record->uuid;
-    hi = record->uuids;
-    while (hi >>= 1)
-        if (lo[hi] <= val)
-            lo += hi;
-
-    return *lo == val;
-}
-
-#define CONTINUATION_PARAM_SIZE        (1 + sizeof(int))
-#define MAX_PDU_OUT_SIZE       96      /* Arbitrary */
-#define PDU_HEADER_SIZE                5
-#define MAX_RSP_PARAM_SIZE     (MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE - \
-                CONTINUATION_PARAM_SIZE)
-
-static int sdp_svc_match(struct bt_l2cap_sdp_state_s *sdp,
-                const uint8_t **req, ssize_t *len)
-{
-    size_t datalen;
-    int i;
-
-    if ((**req & ~SDP_DSIZE_MASK) != SDP_DTYPE_UUID)
-        return 1;
-
-    datalen = sdp_datalen(req, len);
-    if (datalen != 2 && datalen != 4 && datalen != 16)
-        return 1;
-
-    for (i = 0; i < sdp->services; i ++)
-        if (sdp_uuid_match(&sdp->service_list[i], *req, datalen))
-            sdp->service_list[i].match = 1;
-
-    (*req) += datalen;
-    (*len) -= datalen;
-
-    return 0;
-}
-
-static ssize_t sdp_svc_search(struct bt_l2cap_sdp_state_s *sdp,
-                uint8_t *rsp, const uint8_t *req, ssize_t len)
-{
-    ssize_t seqlen;
-    int i, count, start, end, max;
-    int32_t handle;
-
-    /* Perform the search */
-    for (i = 0; i < sdp->services; i ++)
-        sdp->service_list[i].match = 0;
-
-    if (len < 1)
-        return -SDP_INVALID_SYNTAX;
-    if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) {
-        seqlen = sdp_datalen(&req, &len);
-        if (seqlen < 3 || len < seqlen)
-            return -SDP_INVALID_SYNTAX;
-        len -= seqlen;
-
-        while (seqlen)
-            if (sdp_svc_match(sdp, &req, &seqlen))
-                return -SDP_INVALID_SYNTAX;
-    } else if (sdp_svc_match(sdp, &req, &seqlen))
-        return -SDP_INVALID_SYNTAX;
-
-    if (len < 3)
-        return -SDP_INVALID_SYNTAX;
-    max = (req[0] << 8) | req[1];
-    req += 2;
-    len -= 2;
-
-    if (*req) {
-        if (len <= sizeof(int))
-            return -SDP_INVALID_SYNTAX;
-        len -= sizeof(int);
-        memcpy(&start, req + 1, sizeof(int));
-    } else
-        start = 0;
-
-    if (len > 1)
-        return -SDP_INVALID_SYNTAX;
-
-    /* Output the results */
-    len = 4;
-    count = 0;
-    end = start;
-    for (i = 0; i < sdp->services; i ++)
-        if (sdp->service_list[i].match) {
-            if (count >= start && count < max && len + 4 < MAX_RSP_PARAM_SIZE) {
-                handle = i;
-                memcpy(rsp + len, &handle, 4);
-                len += 4;
-                end = count + 1;
-            }
-
-            count ++;
-        }
-
-    rsp[0] = count >> 8;
-    rsp[1] = count & 0xff;
-    rsp[2] = (end - start) >> 8;
-    rsp[3] = (end - start) & 0xff;
-
-    if (end < count) {
-        rsp[len ++] = sizeof(int);
-        memcpy(rsp + len, &end, sizeof(int));
-        len += 4;
-    } else
-        rsp[len ++] = 0;
-
-    return len;
-}
-
-static int sdp_attr_match(struct sdp_service_record_s *record,
-                const uint8_t **req, ssize_t *len)
-{
-    int i, start, end;
-
-    if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) {
-        (*req) ++;
-        if (*len < 3)
-            return 1;
-
-        start = (*(*req) ++) << 8;
-        start |= *(*req) ++;
-        end = start;
-        *len -= 3;
-    } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) {
-        (*req) ++;
-        if (*len < 5)
-            return 1;
-
-        start = (*(*req) ++) << 8;
-        start |= *(*req) ++;
-        end = (*(*req) ++) << 8;
-        end |= *(*req) ++;
-        *len -= 5;
-    } else
-        return 1;
-
-    for (i = 0; i < record->attributes; i ++)
-        if (record->attribute_list[i].attribute_id >= start &&
-                        record->attribute_list[i].attribute_id <= end)
-            record->attribute_list[i].match = 1;
-
-    return 0;
-}
-
-static ssize_t sdp_attr_get(struct bt_l2cap_sdp_state_s *sdp,
-                uint8_t *rsp, const uint8_t *req, ssize_t len)
-{
-    ssize_t seqlen;
-    int i, start, end, max;
-    int32_t handle;
-    struct sdp_service_record_s *record;
-    uint8_t *lst;
-
-    /* Perform the search */
-    if (len < 7)
-        return -SDP_INVALID_SYNTAX;
-    memcpy(&handle, req, 4);
-    req += 4;
-    len -= 4;
-
-    if (handle < 0 || handle > sdp->services)
-        return -SDP_INVALID_RECORD_HANDLE;
-    record = &sdp->service_list[handle];
-
-    for (i = 0; i < record->attributes; i ++)
-        record->attribute_list[i].match = 0;
-
-    max = (req[0] << 8) | req[1];
-    req += 2;
-    len -= 2;
-    if (max < 0x0007)
-        return -SDP_INVALID_SYNTAX;
-
-    if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) {
-        seqlen = sdp_datalen(&req, &len);
-        if (seqlen < 3 || len < seqlen)
-            return -SDP_INVALID_SYNTAX;
-        len -= seqlen;
-
-        while (seqlen)
-            if (sdp_attr_match(record, &req, &seqlen))
-                return -SDP_INVALID_SYNTAX;
-    } else if (sdp_attr_match(record, &req, &seqlen))
-        return -SDP_INVALID_SYNTAX;
-
-    if (len < 1)
-        return -SDP_INVALID_SYNTAX;
-
-    if (*req) {
-        if (len <= sizeof(int))
-            return -SDP_INVALID_SYNTAX;
-        len -= sizeof(int);
-        memcpy(&start, req + 1, sizeof(int));
-    } else
-        start = 0;
-
-    if (len > 1)
-        return -SDP_INVALID_SYNTAX;
-
-    /* Output the results */
-    lst = rsp + 2;
-    max = MIN(max, MAX_RSP_PARAM_SIZE);
-    len = 3 - start;
-    end = 0;
-    for (i = 0; i < record->attributes; i ++)
-        if (record->attribute_list[i].match) {
-            if (len >= 0 && len + record->attribute_list[i].len < max) {
-                memcpy(lst + len, record->attribute_list[i].pair,
-                                record->attribute_list[i].len);
-                end = len + record->attribute_list[i].len;
-            }
-            len += record->attribute_list[i].len;
-        }
-    if (0 >= start) {
-       lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2;
-       lst[1] = (len + start - 3) >> 8;
-       lst[2] = (len + start - 3) & 0xff;
-    }
-
-    rsp[0] = end >> 8;
-    rsp[1] = end & 0xff;
-
-    if (end < len) {
-        len = end + start;
-        lst[end ++] = sizeof(int);
-        memcpy(lst + end, &len, sizeof(int));
-        end += sizeof(int);
-    } else
-        lst[end ++] = 0;
-
-    return end + 2;
-}
-
-static int sdp_svc_attr_match(struct bt_l2cap_sdp_state_s *sdp,
-                const uint8_t **req, ssize_t *len)
-{
-    int i, j, start, end;
-    struct sdp_service_record_s *record;
-
-    if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) {
-        (*req) ++;
-        if (*len < 3)
-            return 1;
-
-        start = (*(*req) ++) << 8;
-        start |= *(*req) ++;
-        end = start;
-        *len -= 3;
-    } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) {
-        (*req) ++;
-        if (*len < 5)
-            return 1;
-
-        start = (*(*req) ++) << 8;
-        start |= *(*req) ++;
-        end = (*(*req) ++) << 8;
-        end |= *(*req) ++;
-        *len -= 5;
-    } else
-        return 1;
-
-    for (i = 0; i < sdp->services; i ++)
-        if ((record = &sdp->service_list[i])->match)
-            for (j = 0; j < record->attributes; j ++)
-                if (record->attribute_list[j].attribute_id >= start &&
-                                record->attribute_list[j].attribute_id <= end)
-                    record->attribute_list[j].match = 1;
-
-    return 0;
-}
-
-static ssize_t sdp_svc_search_attr_get(struct bt_l2cap_sdp_state_s *sdp,
-                uint8_t *rsp, const uint8_t *req, ssize_t len)
-{
-    ssize_t seqlen;
-    int i, j, start, end, max;
-    struct sdp_service_record_s *record;
-    uint8_t *lst;
-
-    /* Perform the search */
-    for (i = 0; i < sdp->services; i ++) {
-        sdp->service_list[i].match = 0;
-            for (j = 0; j < sdp->service_list[i].attributes; j ++)
-                sdp->service_list[i].attribute_list[j].match = 0;
-    }
-
-    if (len < 1)
-        return -SDP_INVALID_SYNTAX;
-    if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) {
-        seqlen = sdp_datalen(&req, &len);
-        if (seqlen < 3 || len < seqlen)
-            return -SDP_INVALID_SYNTAX;
-        len -= seqlen;
-
-        while (seqlen)
-            if (sdp_svc_match(sdp, &req, &seqlen))
-                return -SDP_INVALID_SYNTAX;
-    } else if (sdp_svc_match(sdp, &req, &seqlen))
-        return -SDP_INVALID_SYNTAX;
-
-    if (len < 3)
-        return -SDP_INVALID_SYNTAX;
-    max = (req[0] << 8) | req[1];
-    req += 2;
-    len -= 2;
-    if (max < 0x0007)
-        return -SDP_INVALID_SYNTAX;
-
-    if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) {
-        seqlen = sdp_datalen(&req, &len);
-        if (seqlen < 3 || len < seqlen)
-            return -SDP_INVALID_SYNTAX;
-        len -= seqlen;
-
-        while (seqlen)
-            if (sdp_svc_attr_match(sdp, &req, &seqlen))
-                return -SDP_INVALID_SYNTAX;
-    } else if (sdp_svc_attr_match(sdp, &req, &seqlen))
-        return -SDP_INVALID_SYNTAX;
-
-    if (len < 1)
-        return -SDP_INVALID_SYNTAX;
-
-    if (*req) {
-        if (len <= sizeof(int))
-            return -SDP_INVALID_SYNTAX;
-        len -= sizeof(int);
-        memcpy(&start, req + 1, sizeof(int));
-    } else
-        start = 0;
-
-    if (len > 1)
-        return -SDP_INVALID_SYNTAX;
-
-    /* Output the results */
-    /* This assumes empty attribute lists are never to be returned even
-     * for matching Service Records.  In practice this shouldn't happen
-     * as the requestor will usually include the always present
-     * ServiceRecordHandle AttributeID in AttributeIDList.  */
-    lst = rsp + 2;
-    max = MIN(max, MAX_RSP_PARAM_SIZE);
-    len = 3 - start;
-    end = 0;
-    for (i = 0; i < sdp->services; i ++)
-        if ((record = &sdp->service_list[i])->match) {
-            len += 3;
-            seqlen = len;
-            for (j = 0; j < record->attributes; j ++)
-                if (record->attribute_list[j].match) {
-                    if (len >= 0)
-                        if (len + record->attribute_list[j].len < max) {
-                            memcpy(lst + len, record->attribute_list[j].pair,
-                                            record->attribute_list[j].len);
-                            end = len + record->attribute_list[j].len;
-                        }
-                    len += record->attribute_list[j].len;
-                }
-            if (seqlen == len)
-                len -= 3;
-            else if (seqlen >= 3 && seqlen < max) {
-                lst[seqlen - 3] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2;
-                lst[seqlen - 2] = (len - seqlen) >> 8;
-                lst[seqlen - 1] = (len - seqlen) & 0xff;
-            }
-        }
-    if (len == 3 - start)
-        len -= 3;
-    else if (0 >= start) {
-       lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2;
-       lst[1] = (len + start - 3) >> 8;
-       lst[2] = (len + start - 3) & 0xff;
-    }
-
-    rsp[0] = end >> 8;
-    rsp[1] = end & 0xff;
-
-    if (end < len) {
-        len = end + start;
-        lst[end ++] = sizeof(int);
-        memcpy(lst + end, &len, sizeof(int));
-        end += sizeof(int);
-    } else
-        lst[end ++] = 0;
-
-    return end + 2;
-}
-
-static void bt_l2cap_sdp_sdu_in(void *opaque, const uint8_t *data, int len)
-{
-    struct bt_l2cap_sdp_state_s *sdp = opaque;
-    enum bt_sdp_cmd pdu_id;
-    uint8_t rsp[MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE], *sdu_out;
-    int transaction_id, plen;
-    int err = 0;
-    int rsp_len = 0;
-
-    if (len < 5) {
-        fprintf(stderr, "%s: short SDP PDU (%iB).\n", __FUNCTION__, len);
-        return;
-    }
-
-    pdu_id = *data ++;
-    transaction_id = (data[0] << 8) | data[1];
-    plen = (data[2] << 8) | data[3];
-    data += 4;
-    len -= 5;
-
-    if (len != plen) {
-        fprintf(stderr, "%s: wrong SDP PDU length (%iB != %iB).\n",
-                        __FUNCTION__, plen, len);
-        err = SDP_INVALID_PDU_SIZE;
-        goto respond;
-    }
-
-    switch (pdu_id) {
-    case SDP_SVC_SEARCH_REQ:
-        rsp_len = sdp_svc_search(sdp, rsp, data, len);
-        pdu_id = SDP_SVC_SEARCH_RSP;
-        break;
-
-    case SDP_SVC_ATTR_REQ:
-        rsp_len = sdp_attr_get(sdp, rsp, data, len);
-        pdu_id = SDP_SVC_ATTR_RSP;
-        break;
-
-    case SDP_SVC_SEARCH_ATTR_REQ:
-        rsp_len = sdp_svc_search_attr_get(sdp, rsp, data, len);
-        pdu_id = SDP_SVC_SEARCH_ATTR_RSP;
-        break;
-
-    case SDP_ERROR_RSP:
-    case SDP_SVC_ATTR_RSP:
-    case SDP_SVC_SEARCH_RSP:
-    case SDP_SVC_SEARCH_ATTR_RSP:
-    default:
-        fprintf(stderr, "%s: unexpected SDP PDU ID %02x.\n",
-                        __FUNCTION__, pdu_id);
-        err = SDP_INVALID_SYNTAX;
-        break;
-    }
-
-    if (rsp_len < 0) {
-        err = -rsp_len;
-        rsp_len = 0;
-    }
-
-respond:
-    if (err) {
-        pdu_id = SDP_ERROR_RSP;
-        rsp[rsp_len ++] = err >> 8;
-        rsp[rsp_len ++] = err & 0xff;
-    }
-
-    sdu_out = sdp->channel->sdu_out(sdp->channel, rsp_len + PDU_HEADER_SIZE);
-
-    sdu_out[0] = pdu_id;
-    sdu_out[1] = transaction_id >> 8;
-    sdu_out[2] = transaction_id & 0xff;
-    sdu_out[3] = rsp_len >> 8;
-    sdu_out[4] = rsp_len & 0xff;
-    memcpy(sdu_out + PDU_HEADER_SIZE, rsp, rsp_len);
-
-    sdp->channel->sdu_submit(sdp->channel);
-}
-
-static void bt_l2cap_sdp_close_ch(void *opaque)
-{
-    struct bt_l2cap_sdp_state_s *sdp = opaque;
-    int i;
-
-    for (i = 0; i < sdp->services; i ++) {
-        g_free(sdp->service_list[i].attribute_list->pair);
-        g_free(sdp->service_list[i].attribute_list);
-        g_free(sdp->service_list[i].uuid);
-    }
-    g_free(sdp->service_list);
-    g_free(sdp);
-}
-
-struct sdp_def_service_s {
-    uint16_t class_uuid;
-    struct sdp_def_attribute_s {
-        uint16_t id;
-        struct sdp_def_data_element_s {
-            uint8_t type;
-            union {
-                uint32_t uint;
-                const char *str;
-                struct sdp_def_data_element_s *list;
-            } value;
-        } data;
-    } attributes[];
-};
-
-/* Calculate a safe byte count to allocate that will store the given
- * element, at the same time count elements of a UUID type.  */
-static int sdp_attr_max_size(struct sdp_def_data_element_s *element,
-                int *uuids)
-{
-    int type = element->type & ~SDP_DSIZE_MASK;
-    int len;
-
-    if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_UUID ||
-                    type == SDP_DTYPE_BOOL) {
-        if (type == SDP_DTYPE_UUID)
-            (*uuids) ++;
-        return 1 + (1 << (element->type & SDP_DSIZE_MASK));
-    }
-
-    if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) {
-        if (element->type & SDP_DSIZE_MASK) {
-            for (len = 0; element->value.str[len] |
-                            element->value.str[len + 1]; len ++);
-            return len;
-        } else
-            return 2 + strlen(element->value.str);
-    }
-
-    if (type != SDP_DTYPE_SEQ)
-        exit(-1);
-    len = 2;
-    element = element->value.list;
-    while (element->type)
-        len += sdp_attr_max_size(element ++, uuids);
-    if (len > 255)
-        exit (-1);
-
-    return len;
-}
-
-static int sdp_attr_write(uint8_t *data,
-                struct sdp_def_data_element_s *element, int **uuid)
-{
-    int type = element->type & ~SDP_DSIZE_MASK;
-    int len = 0;
-
-    if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_BOOL) {
-        data[len ++] = element->type;
-        if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_1)
-            data[len ++] = (element->value.uint >>  0) & 0xff;
-        else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_2) {
-            data[len ++] = (element->value.uint >>  8) & 0xff;
-            data[len ++] = (element->value.uint >>  0) & 0xff;
-        } else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_4) {
-            data[len ++] = (element->value.uint >>  24) & 0xff;
-            data[len ++] = (element->value.uint >>  16) & 0xff;
-            data[len ++] = (element->value.uint >>  8) & 0xff;
-            data[len ++] = (element->value.uint >>  0) & 0xff;
-        }
-
-        return len;
-    }
-
-    if (type == SDP_DTYPE_UUID) {
-        *(*uuid) ++ = element->value.uint;
-
-        data[len ++] = element->type;
-        data[len ++] = (element->value.uint >>  24) & 0xff;
-        data[len ++] = (element->value.uint >>  16) & 0xff;
-        data[len ++] = (element->value.uint >>  8) & 0xff;
-        data[len ++] = (element->value.uint >>  0) & 0xff;
-        memcpy(data + len, bt_base_uuid, 12);
-
-        return len + 12;
-    }
-
-    data[0] = type | SDP_DSIZE_NEXT1;
-    if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) {
-        if (element->type & SDP_DSIZE_MASK)
-            for (len = 0; element->value.str[len] |
-                            element->value.str[len + 1]; len ++);
-        else
-            len = strlen(element->value.str);
-        memcpy(data + 2, element->value.str, data[1] = len);
-
-        return len + 2;
-    }
-
-    len = 2;
-    element = element->value.list;
-    while (element->type)
-        len += sdp_attr_write(data + len, element ++, uuid);
-    data[1] = len - 2;
-
-    return len;
-}
-
-static int sdp_attributeid_compare(const struct sdp_service_attribute_s *a,
-                const struct sdp_service_attribute_s *b)
-{
-    return (int) b->attribute_id - a->attribute_id;
-}
-
-static int sdp_uuid_compare(const int *a, const int *b)
-{
-    return *a - *b;
-}
-
-static void sdp_service_record_build(struct sdp_service_record_s *record,
-                struct sdp_def_service_s *def, int handle)
-{
-    int len = 0;
-    uint8_t *data;
-    int *uuid;
-
-    record->uuids = 0;
-    while (def->attributes[record->attributes].data.type) {
-        len += 3;
-        len += sdp_attr_max_size(&def->attributes[record->attributes ++].data,
-                        &record->uuids);
-    }
-    record->uuids = 1 << ffs(record->uuids - 1);
-    record->attribute_list =
-            g_malloc0(record->attributes * sizeof(*record->attribute_list));
-    record->uuid =
-            g_malloc0(record->uuids * sizeof(*record->uuid));
-    data = g_malloc(len);
-
-    record->attributes = 0;
-    uuid = record->uuid;
-    while (def->attributes[record->attributes].data.type) {
-        record->attribute_list[record->attributes].pair = data;
-
-        len = 0;
-        data[len ++] = SDP_DTYPE_UINT | SDP_DSIZE_2;
-        data[len ++] = def->attributes[record->attributes].id >> 8;
-        data[len ++] = def->attributes[record->attributes].id & 0xff;
-        len += sdp_attr_write(data + len,
-                        &def->attributes[record->attributes].data, &uuid);
-
-        /* Special case: assign a ServiceRecordHandle in sequence */
-        if (def->attributes[record->attributes].id == SDP_ATTR_RECORD_HANDLE)
-            def->attributes[record->attributes].data.value.uint = handle;
-        /* Note: we could also assign a ServiceDescription based on
-         * sdp->device.device->lmp_name.  */
-
-        record->attribute_list[record->attributes ++].len = len;
-        data += len;
-    }
-
-    /* Sort the attribute list by the AttributeID */
-    qsort(record->attribute_list, record->attributes,
-                    sizeof(*record->attribute_list),
-                    (void *) sdp_attributeid_compare);
-    /* Sort the searchable UUIDs list for bisection */
-    qsort(record->uuid, record->uuids,
-                    sizeof(*record->uuid),
-                    (void *) sdp_uuid_compare);
-}
-
-static void sdp_service_db_build(struct bt_l2cap_sdp_state_s *sdp,
-                struct sdp_def_service_s **service)
-{
-    sdp->services = 0;
-    while (service[sdp->services])
-        sdp->services ++;
-    sdp->service_list =
-            g_malloc0(sdp->services * sizeof(*sdp->service_list));
-
-    sdp->services = 0;
-    while (*service) {
-        sdp_service_record_build(&sdp->service_list[sdp->services],
-                        *service, sdp->services);
-        service ++;
-        sdp->services ++;
-    }
-}
-
-#define LAST { .type = 0 }
-#define SERVICE(name, attrs)                           \
-    static struct sdp_def_service_s glue(glue(sdp_service_, name), _s) = { \
-        .attributes = { attrs { .data = LAST } },      \
-    };
-#define ATTRIBUTE(attrid, val) { .id = glue(SDP_ATTR_, attrid), .data = val },
-#define UINT8(val)     {                               \
-        .type       = SDP_DTYPE_UINT | SDP_DSIZE_1,    \
-        .value.uint = val,                             \
-    },
-#define UINT16(val)    {                               \
-        .type       = SDP_DTYPE_UINT | SDP_DSIZE_2,    \
-        .value.uint = val,                             \
-    },
-#define UINT32(val)    {                               \
-        .type       = SDP_DTYPE_UINT | SDP_DSIZE_4,    \
-        .value.uint = val,                             \
-    },
-#define UUID128(val)   {                               \
-        .type       = SDP_DTYPE_UUID | SDP_DSIZE_16,   \
-        .value.uint = val,                             \
-    },
-#define SDP_TRUE       {                               \
-        .type       = SDP_DTYPE_BOOL | SDP_DSIZE_1,    \
-        .value.uint = 1,                               \
-    },
-#define SDP_FALSE      {                               \
-        .type       = SDP_DTYPE_BOOL | SDP_DSIZE_1,    \
-        .value.uint = 0,                               \
-    },
-#define STRING(val)    {                               \
-        .type       = SDP_DTYPE_STRING,                        \
-        .value.str  = val,                             \
-    },
-#define ARRAY(...)     {                               \
-        .type       = SDP_DTYPE_STRING | SDP_DSIZE_2,  \
-        .value.str  = (char []) { __VA_ARGS__, 0, 0 }, \
-    },
-#define URL(val)       {                               \
-        .type       = SDP_DTYPE_URL,                   \
-        .value.str  = val,                             \
-    },
-#if 1
-#define LIST(val)      {                               \
-        .type       = SDP_DTYPE_SEQ,                   \
-        .value.list = (struct sdp_def_data_element_s []) { val LAST }, \
-    },
-#endif
-
-/* Try to keep each single attribute below MAX_PDU_OUT_SIZE bytes
- * in resulting SDP data representation size.  */
-
-SERVICE(hid,
-    ATTRIBUTE(RECORD_HANDLE,   UINT32(0))      /* Filled in later */
-    ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(HID_SVCLASS_ID)))
-    ATTRIBUTE(RECORD_STATE,    UINT32(1))
-    ATTRIBUTE(PROTO_DESC_LIST, LIST(
-        LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_HID_CTRL))
-        LIST(UUID128(HIDP_UUID))
-    ))
-    ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002)))
-    ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST(
-        UINT16(0x656e) UINT16(0x006a) UINT16(0x0100)
-    ))
-    ATTRIBUTE(PFILE_DESC_LIST, LIST(
-        LIST(UUID128(HID_PROFILE_ID) UINT16(0x0100))
-    ))
-    ATTRIBUTE(DOC_URL,         URL("http://bellard.org/qemu/user-doc.html"))
-    ATTRIBUTE(SVCNAME_PRIMARY, STRING("QEMU Bluetooth HID"))
-    ATTRIBUTE(SVCDESC_PRIMARY, STRING("QEMU Keyboard/Mouse"))
-    ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU"))
-
-    /* Profile specific */
-    ATTRIBUTE(DEVICE_RELEASE_NUMBER,   UINT16(0x0091)) /* Deprecated, remove */
-    ATTRIBUTE(PARSER_VERSION,          UINT16(0x0111))
-    /* TODO: extract from l2cap_device->device.class[0] */
-    ATTRIBUTE(DEVICE_SUBCLASS,         UINT8(0x40))
-    ATTRIBUTE(COUNTRY_CODE,            UINT8(0x15))
-    ATTRIBUTE(VIRTUAL_CABLE,           SDP_TRUE)
-    ATTRIBUTE(RECONNECT_INITIATE,      SDP_FALSE)
-    /* TODO: extract from hid->usbdev->report_desc */
-    ATTRIBUTE(DESCRIPTOR_LIST,         LIST(
-        LIST(UINT8(0x22) ARRAY(
-            0x05, 0x01,        /* Usage Page (Generic Desktop) */
-            0x09, 0x06,        /* Usage (Keyboard) */
-            0xa1, 0x01,        /* Collection (Application) */
-            0x75, 0x01,        /*   Report Size (1) */
-            0x95, 0x08,        /*   Report Count (8) */
-            0x05, 0x07,        /*   Usage Page (Key Codes) */
-            0x19, 0xe0,        /*   Usage Minimum (224) */
-            0x29, 0xe7,        /*   Usage Maximum (231) */
-            0x15, 0x00,        /*   Logical Minimum (0) */
-            0x25, 0x01,        /*   Logical Maximum (1) */
-            0x81, 0x02,        /*   Input (Data, Variable, Absolute) */
-            0x95, 0x01,        /*   Report Count (1) */
-            0x75, 0x08,        /*   Report Size (8) */
-            0x81, 0x01,        /*   Input (Constant) */
-            0x95, 0x05,        /*   Report Count (5) */
-            0x75, 0x01,        /*   Report Size (1) */
-            0x05, 0x08,        /*   Usage Page (LEDs) */
-            0x19, 0x01,        /*   Usage Minimum (1) */
-            0x29, 0x05,        /*   Usage Maximum (5) */
-            0x91, 0x02,        /*   Output (Data, Variable, Absolute) */
-            0x95, 0x01,        /*   Report Count (1) */
-            0x75, 0x03,        /*   Report Size (3) */
-            0x91, 0x01,        /*   Output (Constant) */
-            0x95, 0x06,        /*   Report Count (6) */
-            0x75, 0x08,        /*   Report Size (8) */
-            0x15, 0x00,        /*   Logical Minimum (0) */
-            0x25, 0xff,        /*   Logical Maximum (255) */
-            0x05, 0x07,        /*   Usage Page (Key Codes) */
-            0x19, 0x00,        /*   Usage Minimum (0) */
-            0x29, 0xff,        /*   Usage Maximum (255) */
-            0x81, 0x00,        /*   Input (Data, Array) */
-            0xc0       /* End Collection */
-    ))))
-    ATTRIBUTE(LANG_ID_BASE_LIST,       LIST(
-        LIST(UINT16(0x0409) UINT16(0x0100))
-    ))
-    ATTRIBUTE(SDP_DISABLE,             SDP_FALSE)
-    ATTRIBUTE(BATTERY_POWER,           SDP_TRUE)
-    ATTRIBUTE(REMOTE_WAKEUP,           SDP_TRUE)
-    ATTRIBUTE(BOOT_DEVICE,             SDP_TRUE)       /* XXX: untested */
-    ATTRIBUTE(SUPERVISION_TIMEOUT,     UINT16(0x0c80))
-    ATTRIBUTE(NORMALLY_CONNECTABLE,    SDP_TRUE)
-    ATTRIBUTE(PROFILE_VERSION,         UINT16(0x0100))
-)
-
-SERVICE(sdp,
-    ATTRIBUTE(RECORD_HANDLE,   UINT32(0))      /* Filled in later */
-    ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(SDP_SERVER_SVCLASS_ID)))
-    ATTRIBUTE(RECORD_STATE,    UINT32(1))
-    ATTRIBUTE(PROTO_DESC_LIST, LIST(
-        LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP))
-        LIST(UUID128(SDP_UUID))
-    ))
-    ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002)))
-    ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST(
-        UINT16(0x656e) UINT16(0x006a) UINT16(0x0100)
-    ))
-    ATTRIBUTE(PFILE_DESC_LIST, LIST(
-        LIST(UUID128(SDP_SERVER_PROFILE_ID) UINT16(0x0100))
-    ))
-    ATTRIBUTE(DOC_URL,         URL("http://bellard.org/qemu/user-doc.html"))
-    ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU"))
-
-    /* Profile specific */
-    ATTRIBUTE(VERSION_NUM_LIST, LIST(UINT16(0x0100)))
-    ATTRIBUTE(SVCDB_STATE    , UINT32(1))
-)
-
-SERVICE(pnp,
-    ATTRIBUTE(RECORD_HANDLE,   UINT32(0))      /* Filled in later */
-    ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(PNP_INFO_SVCLASS_ID)))
-    ATTRIBUTE(RECORD_STATE,    UINT32(1))
-    ATTRIBUTE(PROTO_DESC_LIST, LIST(
-        LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP))
-        LIST(UUID128(SDP_UUID))
-    ))
-    ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002)))
-    ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST(
-        UINT16(0x656e) UINT16(0x006a) UINT16(0x0100)
-    ))
-    ATTRIBUTE(PFILE_DESC_LIST, LIST(
-        LIST(UUID128(PNP_INFO_PROFILE_ID) UINT16(0x0100))
-    ))
-    ATTRIBUTE(DOC_URL,         URL("http://bellard.org/qemu/user-doc.html"))
-    ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU"))
-
-    /* Profile specific */
-    ATTRIBUTE(SPECIFICATION_ID, UINT16(0x0100))
-    ATTRIBUTE(VERSION,         UINT16(0x0100))
-    ATTRIBUTE(PRIMARY_RECORD,  SDP_TRUE)
-)
-
-static int bt_l2cap_sdp_new_ch(struct bt_l2cap_device_s *dev,
-                struct bt_l2cap_conn_params_s *params)
-{
-    struct bt_l2cap_sdp_state_s *sdp = g_malloc0(sizeof(*sdp));
-    struct sdp_def_service_s *services[] = {
-        &sdp_service_sdp_s,
-        &sdp_service_hid_s,
-        &sdp_service_pnp_s,
-        NULL,
-    };
-
-    sdp->channel = params;
-    sdp->channel->opaque = sdp;
-    sdp->channel->close = bt_l2cap_sdp_close_ch;
-    sdp->channel->sdu_in = bt_l2cap_sdp_sdu_in;
-
-    sdp_service_db_build(sdp, services);
-
-    return 0;
-}
-
-void bt_l2cap_sdp_init(struct bt_l2cap_device_s *dev)
-{
-    bt_l2cap_psm_register(dev, BT_PSM_SDP,
-                    MAX_PDU_OUT_SIZE, bt_l2cap_sdp_new_ch);
-}
diff --git a/hw/bt.c b/hw/bt.c
deleted file mode 100644 (file)
index 24ef4de..0000000
--- a/hw/bt.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Convenience functions for bluetooth.
- *
- * Copyright (C) 2008 Andrzej Zaborowski  <balrog@zabor.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 or
- * (at your option) version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "qemu-common.h"
-#include "bt/bt.h"
-#include "hw/bt.h"
-
-/* Slave implementations can ignore this */
-static void bt_dummy_lmp_mode_change(struct bt_link_s *link)
-{
-}
-
-/* Slaves should never receive these PDUs */
-static void bt_dummy_lmp_connection_complete(struct bt_link_s *link)
-{
-    if (link->slave->reject_reason)
-        fprintf(stderr, "%s: stray LMP_not_accepted received, fixme\n",
-                        __FUNCTION__);
-    else
-        fprintf(stderr, "%s: stray LMP_accepted received, fixme\n",
-                        __FUNCTION__);
-    exit(-1);
-}
-
-static void bt_dummy_lmp_disconnect_master(struct bt_link_s *link)
-{
-    fprintf(stderr, "%s: stray LMP_detach received, fixme\n", __FUNCTION__);
-    exit(-1);
-}
-
-static void bt_dummy_lmp_acl_resp(struct bt_link_s *link,
-                const uint8_t *data, int start, int len)
-{
-    fprintf(stderr, "%s: stray ACL response PDU, fixme\n", __FUNCTION__);
-    exit(-1);
-}
-
-/* Slaves that don't hold any additional per link state can use these */
-static void bt_dummy_lmp_connection_request(struct bt_link_s *req)
-{
-    struct bt_link_s *link = g_malloc0(sizeof(struct bt_link_s));
-
-    link->slave = req->slave;
-    link->host = req->host;
-
-    req->host->reject_reason = 0;
-    req->host->lmp_connection_complete(link);
-}
-
-static void bt_dummy_lmp_disconnect_slave(struct bt_link_s *link)
-{
-    g_free(link);
-}
-
-static void bt_dummy_destroy(struct bt_device_s *device)
-{
-    bt_device_done(device);
-    g_free(device);
-}
-
-static int bt_dev_idx = 0;
-
-void bt_device_init(struct bt_device_s *dev, struct bt_scatternet_s *net)
-{
-    memset(dev, 0, sizeof(*dev));
-    dev->inquiry_scan = 1;
-    dev->page_scan = 1;
-
-    dev->bd_addr.b[0] = bt_dev_idx & 0xff;
-    dev->bd_addr.b[1] = bt_dev_idx >> 8;
-    dev->bd_addr.b[2] = 0xd0;
-    dev->bd_addr.b[3] = 0xba;
-    dev->bd_addr.b[4] = 0xbe;
-    dev->bd_addr.b[5] = 0xba;
-    bt_dev_idx ++;
-
-    /* Simple slave-only devices need to implement only .lmp_acl_data */
-    dev->lmp_connection_complete = bt_dummy_lmp_connection_complete;
-    dev->lmp_disconnect_master = bt_dummy_lmp_disconnect_master;
-    dev->lmp_acl_resp = bt_dummy_lmp_acl_resp;
-    dev->lmp_mode_change = bt_dummy_lmp_mode_change;
-    dev->lmp_connection_request = bt_dummy_lmp_connection_request;
-    dev->lmp_disconnect_slave = bt_dummy_lmp_disconnect_slave;
-
-    dev->handle_destroy = bt_dummy_destroy;
-
-    dev->net = net;
-    dev->next = net->slave;
-    net->slave = dev;
-}
-
-void bt_device_done(struct bt_device_s *dev)
-{
-    struct bt_device_s **p = &dev->net->slave;
-
-    while (*p && *p != dev)
-        p = &(*p)->next;
-    if (*p != dev) {
-        fprintf(stderr, "%s: bad bt device \"%s\"\n", __FUNCTION__,
-                        dev->lmp_name ?: "(null)");
-        exit(-1);
-    }
-
-    *p = dev->next;
-}
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..867a7d2e8aa0f123016cca46634f9b70827ee0d9 100644 (file)
@@ -0,0 +1,3 @@
+common-obj-y += core.o l2cap.o sdp.o hci.o hid.o
+common-obj-y += hci-csr.o
+
diff --git a/hw/bt/core.c b/hw/bt/core.c
new file mode 100644 (file)
index 0000000..24ef4de
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Convenience functions for bluetooth.
+ *
+ * Copyright (C) 2008 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu-common.h"
+#include "bt/bt.h"
+#include "hw/bt.h"
+
+/* Slave implementations can ignore this */
+static void bt_dummy_lmp_mode_change(struct bt_link_s *link)
+{
+}
+
+/* Slaves should never receive these PDUs */
+static void bt_dummy_lmp_connection_complete(struct bt_link_s *link)
+{
+    if (link->slave->reject_reason)
+        fprintf(stderr, "%s: stray LMP_not_accepted received, fixme\n",
+                        __FUNCTION__);
+    else
+        fprintf(stderr, "%s: stray LMP_accepted received, fixme\n",
+                        __FUNCTION__);
+    exit(-1);
+}
+
+static void bt_dummy_lmp_disconnect_master(struct bt_link_s *link)
+{
+    fprintf(stderr, "%s: stray LMP_detach received, fixme\n", __FUNCTION__);
+    exit(-1);
+}
+
+static void bt_dummy_lmp_acl_resp(struct bt_link_s *link,
+                const uint8_t *data, int start, int len)
+{
+    fprintf(stderr, "%s: stray ACL response PDU, fixme\n", __FUNCTION__);
+    exit(-1);
+}
+
+/* Slaves that don't hold any additional per link state can use these */
+static void bt_dummy_lmp_connection_request(struct bt_link_s *req)
+{
+    struct bt_link_s *link = g_malloc0(sizeof(struct bt_link_s));
+
+    link->slave = req->slave;
+    link->host = req->host;
+
+    req->host->reject_reason = 0;
+    req->host->lmp_connection_complete(link);
+}
+
+static void bt_dummy_lmp_disconnect_slave(struct bt_link_s *link)
+{
+    g_free(link);
+}
+
+static void bt_dummy_destroy(struct bt_device_s *device)
+{
+    bt_device_done(device);
+    g_free(device);
+}
+
+static int bt_dev_idx = 0;
+
+void bt_device_init(struct bt_device_s *dev, struct bt_scatternet_s *net)
+{
+    memset(dev, 0, sizeof(*dev));
+    dev->inquiry_scan = 1;
+    dev->page_scan = 1;
+
+    dev->bd_addr.b[0] = bt_dev_idx & 0xff;
+    dev->bd_addr.b[1] = bt_dev_idx >> 8;
+    dev->bd_addr.b[2] = 0xd0;
+    dev->bd_addr.b[3] = 0xba;
+    dev->bd_addr.b[4] = 0xbe;
+    dev->bd_addr.b[5] = 0xba;
+    bt_dev_idx ++;
+
+    /* Simple slave-only devices need to implement only .lmp_acl_data */
+    dev->lmp_connection_complete = bt_dummy_lmp_connection_complete;
+    dev->lmp_disconnect_master = bt_dummy_lmp_disconnect_master;
+    dev->lmp_acl_resp = bt_dummy_lmp_acl_resp;
+    dev->lmp_mode_change = bt_dummy_lmp_mode_change;
+    dev->lmp_connection_request = bt_dummy_lmp_connection_request;
+    dev->lmp_disconnect_slave = bt_dummy_lmp_disconnect_slave;
+
+    dev->handle_destroy = bt_dummy_destroy;
+
+    dev->net = net;
+    dev->next = net->slave;
+    net->slave = dev;
+}
+
+void bt_device_done(struct bt_device_s *dev)
+{
+    struct bt_device_s **p = &dev->net->slave;
+
+    while (*p && *p != dev)
+        p = &(*p)->next;
+    if (*p != dev) {
+        fprintf(stderr, "%s: bad bt device \"%s\"\n", __FUNCTION__,
+                        dev->lmp_name ?: "(null)");
+        exit(-1);
+    }
+
+    *p = dev->next;
+}
diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c
new file mode 100644 (file)
index 0000000..55c819b
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * Bluetooth serial HCI transport.
+ * CSR41814 HCI with H4p vendor extensions.
+ *
+ * Copyright (C) 2008 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu-common.h"
+#include "char/char.h"
+#include "qemu/timer.h"
+#include "hw/irq.h"
+#include "bt/bt.h"
+#include "hw/bt.h"
+
+struct csrhci_s {
+    int enable;
+    qemu_irq *pins;
+    int pin_state;
+    int modem_state;
+    CharDriverState chr;
+#define FIFO_LEN       4096
+    int out_start;
+    int out_len;
+    int out_size;
+    uint8_t outfifo[FIFO_LEN * 2];
+    uint8_t inpkt[FIFO_LEN];
+    int in_len;
+    int in_hdr;
+    int in_data;
+    QEMUTimer *out_tm;
+    int64_t baud_delay;
+
+    bdaddr_t bd_addr;
+    struct HCIInfo *hci;
+};
+
+/* H4+ packet types */
+enum {
+    H4_CMD_PKT   = 1,
+    H4_ACL_PKT   = 2,
+    H4_SCO_PKT   = 3,
+    H4_EVT_PKT   = 4,
+    H4_NEG_PKT   = 6,
+    H4_ALIVE_PKT = 7,
+};
+
+/* CSR41814 negotiation start magic packet */
+static const uint8_t csrhci_neg_packet[] = {
+    H4_NEG_PKT, 10,
+    0x00, 0xa0, 0x01, 0x00, 0x00,
+    0x4c, 0x00, 0x96, 0x00, 0x00,
+};
+
+/* CSR41814 vendor-specific command OCFs */
+enum {
+    OCF_CSR_SEND_FIRMWARE = 0x000,
+};
+
+static inline void csrhci_fifo_wake(struct csrhci_s *s)
+{
+    if (!s->enable || !s->out_len)
+        return;
+
+    /* XXX: Should wait for s->modem_state & CHR_TIOCM_RTS? */
+    if (s->chr.chr_can_read && s->chr.chr_can_read(s->chr.handler_opaque) &&
+                    s->chr.chr_read) {
+        s->chr.chr_read(s->chr.handler_opaque,
+                        s->outfifo + s->out_start ++, 1);
+        s->out_len --;
+        if (s->out_start >= s->out_size) {
+            s->out_start = 0;
+            s->out_size = FIFO_LEN;
+        }
+    }
+
+    if (s->out_len)
+        qemu_mod_timer(s->out_tm, qemu_get_clock_ns(vm_clock) + s->baud_delay);
+}
+
+#define csrhci_out_packetz(s, len) memset(csrhci_out_packet(s, len), 0, len)
+static uint8_t *csrhci_out_packet(struct csrhci_s *s, int len)
+{
+    int off = s->out_start + s->out_len;
+
+    /* TODO: do the padding here, i.e. align len */
+    s->out_len += len;
+
+    if (off < FIFO_LEN) {
+        if (off + len > FIFO_LEN && (s->out_size = off + len) > FIFO_LEN * 2) {
+            fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len);
+            exit(-1);
+        }
+        return s->outfifo + off;
+    }
+
+    if (s->out_len > s->out_size) {
+        fprintf(stderr, "%s: can't alloc %i bytes\n", __FUNCTION__, len);
+        exit(-1);
+    }
+
+    return s->outfifo + off - s->out_size;
+}
+
+static inline uint8_t *csrhci_out_packet_csr(struct csrhci_s *s,
+                int type, int len)
+{
+    uint8_t *ret = csrhci_out_packetz(s, len + 2);
+
+    *ret ++ = type;
+    *ret ++ = len;
+
+    return ret;
+}
+
+static inline uint8_t *csrhci_out_packet_event(struct csrhci_s *s,
+                int evt, int len)
+{
+    uint8_t *ret = csrhci_out_packetz(s,
+                    len + 1 + sizeof(struct hci_event_hdr));
+
+    *ret ++ = H4_EVT_PKT;
+    ((struct hci_event_hdr *) ret)->evt = evt;
+    ((struct hci_event_hdr *) ret)->plen = len;
+
+    return ret + sizeof(struct hci_event_hdr);
+}
+
+static void csrhci_in_packet_vendor(struct csrhci_s *s, int ocf,
+                uint8_t *data, int len)
+{
+    int offset;
+    uint8_t *rpkt;
+
+    switch (ocf) {
+    case OCF_CSR_SEND_FIRMWARE:
+        /* Check if this is the bd_address packet */
+        if (len >= 18 + 8 && data[12] == 0x01 && data[13] == 0x00) {
+            offset = 18;
+            s->bd_addr.b[0] = data[offset + 7];        /* Beyond cmd packet end(!?) */
+            s->bd_addr.b[1] = data[offset + 6];
+            s->bd_addr.b[2] = data[offset + 4];
+            s->bd_addr.b[3] = data[offset + 0];
+            s->bd_addr.b[4] = data[offset + 3];
+            s->bd_addr.b[5] = data[offset + 2];
+
+            s->hci->bdaddr_set(s->hci, s->bd_addr.b);
+            fprintf(stderr, "%s: bd_address loaded from firmware: "
+                            "%02x:%02x:%02x:%02x:%02x:%02x\n", __FUNCTION__,
+                            s->bd_addr.b[0], s->bd_addr.b[1], s->bd_addr.b[2],
+                            s->bd_addr.b[3], s->bd_addr.b[4], s->bd_addr.b[5]);
+        }
+
+        rpkt = csrhci_out_packet_event(s, EVT_VENDOR, 11);
+        /* Status bytes: no error */
+        rpkt[9] = 0x00;
+        rpkt[10] = 0x00;
+        break;
+
+    default:
+        fprintf(stderr, "%s: got a bad CMD packet\n", __FUNCTION__);
+        return;
+    }
+
+    csrhci_fifo_wake(s);
+}
+
+static void csrhci_in_packet(struct csrhci_s *s, uint8_t *pkt)
+{
+    uint8_t *rpkt;
+    int opc;
+
+    switch (*pkt ++) {
+    case H4_CMD_PKT:
+        opc = le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode);
+        if (cmd_opcode_ogf(opc) == OGF_VENDOR_CMD) {
+            csrhci_in_packet_vendor(s, cmd_opcode_ocf(opc),
+                            pkt + sizeof(struct hci_command_hdr),
+                            s->in_len - sizeof(struct hci_command_hdr) - 1);
+            return;
+        }
+
+        /* TODO: if the command is OCF_READ_LOCAL_COMMANDS or the likes,
+         * we need to send it to the HCI layer and then add our supported
+         * commands to the returned mask (such as OGF_VENDOR_CMD).  With
+         * bt-hci.c we could just have hooks for this kind of commands but
+         * we can't with bt-host.c.  */
+
+        s->hci->cmd_send(s->hci, pkt, s->in_len - 1);
+        break;
+
+    case H4_EVT_PKT:
+        goto bad_pkt;
+
+    case H4_ACL_PKT:
+        s->hci->acl_send(s->hci, pkt, s->in_len - 1);
+        break;
+
+    case H4_SCO_PKT:
+        s->hci->sco_send(s->hci, pkt, s->in_len - 1);
+        break;
+
+    case H4_NEG_PKT:
+        if (s->in_hdr != sizeof(csrhci_neg_packet) ||
+                        memcmp(pkt - 1, csrhci_neg_packet, s->in_hdr)) {
+            fprintf(stderr, "%s: got a bad NEG packet\n", __FUNCTION__);
+            return;
+        }
+        pkt += 2;
+
+        rpkt = csrhci_out_packet_csr(s, H4_NEG_PKT, 10);
+
+        *rpkt ++ = 0x20;       /* Operational settings negotiation Ok */
+        memcpy(rpkt, pkt, 7); rpkt += 7;
+        *rpkt ++ = 0xff;
+        *rpkt = 0xff;
+        break;
+
+    case H4_ALIVE_PKT:
+        if (s->in_hdr != 4 || pkt[1] != 0x55 || pkt[2] != 0x00) {
+            fprintf(stderr, "%s: got a bad ALIVE packet\n", __FUNCTION__);
+            return;
+        }
+
+        rpkt = csrhci_out_packet_csr(s, H4_ALIVE_PKT, 2);
+
+        *rpkt ++ = 0xcc;
+        *rpkt = 0x00;
+        break;
+
+    default:
+    bad_pkt:
+        /* TODO: error out */
+        fprintf(stderr, "%s: got a bad packet\n", __FUNCTION__);
+        break;
+    }
+
+    csrhci_fifo_wake(s);
+}
+
+static int csrhci_header_len(const uint8_t *pkt)
+{
+    switch (pkt[0]) {
+    case H4_CMD_PKT:
+        return HCI_COMMAND_HDR_SIZE;
+    case H4_EVT_PKT:
+        return HCI_EVENT_HDR_SIZE;
+    case H4_ACL_PKT:
+        return HCI_ACL_HDR_SIZE;
+    case H4_SCO_PKT:
+        return HCI_SCO_HDR_SIZE;
+    case H4_NEG_PKT:
+        return pkt[1] + 1;
+    case H4_ALIVE_PKT:
+        return 3;
+    }
+
+    exit(-1);
+}
+
+static int csrhci_data_len(const uint8_t *pkt)
+{
+    switch (*pkt ++) {
+    case H4_CMD_PKT:
+        /* It seems that vendor-specific command packets for H4+ are all
+         * one byte longer than indicated in the standard header.  */
+        if (le16_to_cpu(((struct hci_command_hdr *) pkt)->opcode) == 0xfc00)
+            return (((struct hci_command_hdr *) pkt)->plen + 1) & ~1;
+
+        return ((struct hci_command_hdr *) pkt)->plen;
+    case H4_EVT_PKT:
+        return ((struct hci_event_hdr *) pkt)->plen;
+    case H4_ACL_PKT:
+        return le16_to_cpu(((struct hci_acl_hdr *) pkt)->dlen);
+    case H4_SCO_PKT:
+        return ((struct hci_sco_hdr *) pkt)->dlen;
+    case H4_NEG_PKT:
+    case H4_ALIVE_PKT:
+        return 0;
+    }
+
+    exit(-1);
+}
+
+static int csrhci_write(struct CharDriverState *chr,
+                const uint8_t *buf, int len)
+{
+    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
+    int plen = s->in_len;
+
+    if (!s->enable)
+        return 0;
+
+    s->in_len += len;
+    memcpy(s->inpkt + plen, buf, len);
+
+    while (1) {
+        if (s->in_len >= 2 && plen < 2)
+            s->in_hdr = csrhci_header_len(s->inpkt) + 1;
+
+        if (s->in_len >= s->in_hdr && plen < s->in_hdr)
+            s->in_data = csrhci_data_len(s->inpkt) + s->in_hdr;
+
+        if (s->in_len >= s->in_data) {
+            csrhci_in_packet(s, s->inpkt);
+
+            memmove(s->inpkt, s->inpkt + s->in_len, s->in_len - s->in_data);
+            s->in_len -= s->in_data;
+            s->in_hdr = INT_MAX;
+            s->in_data = INT_MAX;
+            plen = 0;
+        } else
+            break;
+    }
+
+    return len;
+}
+
+static void csrhci_out_hci_packet_event(void *opaque,
+                const uint8_t *data, int len)
+{
+    struct csrhci_s *s = (struct csrhci_s *) opaque;
+    uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1);       /* Align */
+
+    *pkt ++ = H4_EVT_PKT;
+    memcpy(pkt, data, len);
+
+    csrhci_fifo_wake(s);
+}
+
+static void csrhci_out_hci_packet_acl(void *opaque,
+                const uint8_t *data, int len)
+{
+    struct csrhci_s *s = (struct csrhci_s *) opaque;
+    uint8_t *pkt = csrhci_out_packet(s, (len + 2) & ~1);       /* Align */
+
+    *pkt ++ = H4_ACL_PKT;
+    pkt[len & ~1] = 0;
+    memcpy(pkt, data, len);
+
+    csrhci_fifo_wake(s);
+}
+
+static int csrhci_ioctl(struct CharDriverState *chr, int cmd, void *arg)
+{
+    QEMUSerialSetParams *ssp;
+    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
+    int prev_state = s->modem_state;
+
+    switch (cmd) {
+    case CHR_IOCTL_SERIAL_SET_PARAMS:
+        ssp = (QEMUSerialSetParams *) arg;
+        s->baud_delay = get_ticks_per_sec() / ssp->speed;
+        /* Moments later... (but shorter than 100ms) */
+        s->modem_state |= CHR_TIOCM_CTS;
+        break;
+
+    case CHR_IOCTL_SERIAL_GET_TIOCM:
+        *(int *) arg = s->modem_state;
+        break;
+
+    case CHR_IOCTL_SERIAL_SET_TIOCM:
+        s->modem_state = *(int *) arg;
+        if (~s->modem_state & prev_state & CHR_TIOCM_RTS)
+            s->modem_state &= ~CHR_TIOCM_CTS;
+        break;
+
+    default:
+        return -ENOTSUP;
+    }
+    return 0;
+}
+
+static void csrhci_reset(struct csrhci_s *s)
+{
+    s->out_len = 0;
+    s->out_size = FIFO_LEN;
+    s->in_len = 0;
+    s->baud_delay = get_ticks_per_sec();
+    s->enable = 0;
+    s->in_hdr = INT_MAX;
+    s->in_data = INT_MAX;
+
+    s->modem_state = 0;
+    /* After a while... (but sooner than 10ms) */
+    s->modem_state |= CHR_TIOCM_CTS;
+
+    memset(&s->bd_addr, 0, sizeof(bdaddr_t));
+}
+
+static void csrhci_out_tick(void *opaque)
+{
+    csrhci_fifo_wake((struct csrhci_s *) opaque);
+}
+
+static void csrhci_pins(void *opaque, int line, int level)
+{
+    struct csrhci_s *s = (struct csrhci_s *) opaque;
+    int state = s->pin_state;
+
+    s->pin_state &= ~(1 << line);
+    s->pin_state |= (!!level) << line;
+
+    if ((state & ~s->pin_state) & (1 << csrhci_pin_reset)) {
+        /* TODO: Disappear from lower layers */
+        csrhci_reset(s);
+    }
+
+    if (s->pin_state == 3 && state != 3) {
+        s->enable = 1;
+        /* TODO: Wake lower layers up */
+    }
+}
+
+qemu_irq *csrhci_pins_get(CharDriverState *chr)
+{
+    struct csrhci_s *s = (struct csrhci_s *) chr->opaque;
+
+    return s->pins;
+}
+
+CharDriverState *uart_hci_init(qemu_irq wakeup)
+{
+    struct csrhci_s *s = (struct csrhci_s *)
+            g_malloc0(sizeof(struct csrhci_s));
+
+    s->chr.opaque = s;
+    s->chr.chr_write = csrhci_write;
+    s->chr.chr_ioctl = csrhci_ioctl;
+    s->chr.avail_connections = 1;
+
+    s->hci = qemu_next_hci();
+    s->hci->opaque = s;
+    s->hci->evt_recv = csrhci_out_hci_packet_event;
+    s->hci->acl_recv = csrhci_out_hci_packet_acl;
+
+    s->out_tm = qemu_new_timer_ns(vm_clock, csrhci_out_tick, s);
+    s->pins = qemu_allocate_irqs(csrhci_pins, s, __csrhci_pins);
+    csrhci_reset(s);
+
+    return &s->chr;
+}
diff --git a/hw/bt/hci.c b/hw/bt/hci.c
new file mode 100644 (file)
index 0000000..a76edea
--- /dev/null
@@ -0,0 +1,2217 @@
+/*
+ * QEMU Bluetooth HCI logic.
+ *
+ * Copyright (C) 2007 OpenMoko, Inc.
+ * Copyright (C) 2008 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "hw/usb.h"
+#include "bt/bt.h"
+#include "hw/bt.h"
+
+struct bt_hci_s {
+    uint8_t *(*evt_packet)(void *opaque);
+    void (*evt_submit)(void *opaque, int len);
+    void *opaque;
+    uint8_t evt_buf[256];
+
+    uint8_t acl_buf[4096];
+    int acl_len;
+
+    uint16_t asb_handle;
+    uint16_t psb_handle;
+
+    int last_cmd;      /* Note: Always little-endian */
+
+    struct bt_device_s *conn_req_host;
+
+    struct {
+        int inquire;
+        int periodic;
+        int responses_left;
+        int responses;
+        QEMUTimer *inquiry_done;
+        QEMUTimer *inquiry_next;
+        int inquiry_length;
+        int inquiry_period;
+        int inquiry_mode;
+
+#define HCI_HANDLE_OFFSET      0x20
+#define HCI_HANDLES_MAX                0x10
+        struct bt_hci_master_link_s {
+            struct bt_link_s *link;
+            void (*lmp_acl_data)(struct bt_link_s *link,
+                            const uint8_t *data, int start, int len);
+            QEMUTimer *acl_mode_timer;
+        } handle[HCI_HANDLES_MAX];
+        uint32_t role_bmp;
+        int last_handle;
+        int connecting;
+        bdaddr_t awaiting_bdaddr[HCI_HANDLES_MAX];
+    } lm;
+
+    uint8_t event_mask[8];
+    uint16_t voice_setting;    /* Notw: Always little-endian */
+    uint16_t conn_accept_tout;
+    QEMUTimer *conn_accept_timer;
+
+    struct HCIInfo info;
+    struct bt_device_s device;
+};
+
+#define DEFAULT_RSSI_DBM       20
+
+#define hci_from_info(ptr)     container_of((ptr), struct bt_hci_s, info)
+#define hci_from_device(ptr)   container_of((ptr), struct bt_hci_s, device)
+
+struct bt_hci_link_s {
+    struct bt_link_s btlink;
+    uint16_t handle;   /* Local */
+};
+
+/* LMP layer emulation */
+#if 0
+static void bt_submit_lmp(struct bt_device_s *bt, int length, uint8_t *data)
+{
+    int resp, resplen, error, op, tr;
+    uint8_t respdata[17];
+
+    if (length < 1)
+        return;
+
+    tr = *data & 1;
+    op = *(data ++) >> 1;
+    resp = LMP_ACCEPTED;
+    resplen = 2;
+    respdata[1] = op;
+    error = 0;
+    length --;
+
+    if (op >= 0x7c) {  /* Extended opcode */
+        op |= *(data ++) << 8;
+        resp = LMP_ACCEPTED_EXT;
+        resplen = 4;
+        respdata[0] = op >> 8;
+        respdata[1] = op & 0xff;
+        length --;
+    }
+
+    switch (op) {
+    case LMP_ACCEPTED:
+        /* data[0]     Op code
+         */
+        if (length < 1) {
+            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+            goto not_accepted;
+        }
+        resp = 0;
+        break;
+
+    case LMP_ACCEPTED_EXT:
+        /* data[0]     Escape op code
+         * data[1]     Extended op code
+         */
+        if (length < 2) {
+            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+            goto not_accepted;
+        }
+        resp = 0;
+        break;
+
+    case LMP_NOT_ACCEPTED:
+        /* data[0]     Op code
+         * data[1]     Error code
+         */
+        if (length < 2) {
+            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+            goto not_accepted;
+        }
+        resp = 0;
+        break;
+
+    case LMP_NOT_ACCEPTED_EXT:
+        /* data[0]     Op code
+         * data[1]     Extended op code
+         * data[2]     Error code
+         */
+        if (length < 3) {
+            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+            goto not_accepted;
+        }
+        resp = 0;
+        break;
+
+    case LMP_HOST_CONNECTION_REQ:
+        break;
+
+    case LMP_SETUP_COMPLETE:
+        resp = LMP_SETUP_COMPLETE;
+        resplen = 1;
+        bt->setup = 1;
+        break;
+
+    case LMP_DETACH:
+        /* data[0]     Error code
+         */
+        if (length < 1) {
+            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+            goto not_accepted;
+        }
+        bt->setup = 0;
+        resp = 0;
+        break;
+
+    case LMP_SUPERVISION_TIMEOUT:
+        /* data[0,1]   Supervision timeout
+         */
+        if (length < 2) {
+            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+            goto not_accepted;
+        }
+        resp = 0;
+        break;
+
+    case LMP_QUALITY_OF_SERVICE:
+        resp = 0;
+        /* Fall through */
+    case LMP_QOS_REQ:
+        /* data[0,1]   Poll interval
+         * data[2]     N(BC)
+         */
+        if (length < 3) {
+            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+            goto not_accepted;
+        }
+        break;
+
+    case LMP_MAX_SLOT:
+        resp = 0;
+        /* Fall through */
+    case LMP_MAX_SLOT_REQ:
+        /* data[0]     Max slots
+         */
+        if (length < 1) {
+            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+            goto not_accepted;
+        }
+        break;
+
+    case LMP_AU_RAND:
+    case LMP_IN_RAND:
+    case LMP_COMB_KEY:
+        /* data[0-15]  Random number
+         */
+        if (length < 16) {
+            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+            goto not_accepted;
+        }
+        if (op == LMP_AU_RAND) {
+            if (bt->key_present) {
+                resp = LMP_SRES;
+                resplen = 5;
+                /* XXX: [Part H] Section 6.1 on page 801 */
+            } else {
+                error = HCI_PIN_OR_KEY_MISSING;
+                goto not_accepted;
+            }
+        } else if (op == LMP_IN_RAND) {
+            error = HCI_PAIRING_NOT_ALLOWED;
+            goto not_accepted;
+        } else {
+            /* XXX: [Part H] Section 3.2 on page 779 */
+            resp = LMP_UNIT_KEY;
+            resplen = 17;
+            memcpy(respdata + 1, bt->key, 16);
+
+            error = HCI_UNIT_LINK_KEY_USED;
+            goto not_accepted;
+        }
+        break;
+
+    case LMP_UNIT_KEY:
+        /* data[0-15]  Key
+         */
+        if (length < 16) {
+            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+            goto not_accepted;
+        }
+        memcpy(bt->key, data, 16);
+        bt->key_present = 1;
+        break;
+
+    case LMP_SRES:
+        /* data[0-3]   Authentication response
+         */
+        if (length < 4) {
+            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+            goto not_accepted;
+        }
+        break;
+
+    case LMP_CLKOFFSET_REQ:
+        resp = LMP_CLKOFFSET_RES;
+        resplen = 3;
+        respdata[1] = 0x33;
+        respdata[2] = 0x33;
+        break;
+
+    case LMP_CLKOFFSET_RES:
+        /* data[0,1]   Clock offset
+         * (Slave to master only)
+         */
+        if (length < 2) {
+            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+            goto not_accepted;
+        }
+        break;
+
+    case LMP_VERSION_REQ:
+    case LMP_VERSION_RES:
+        /* data[0]     VersNr
+         * data[1,2]   CompId
+         * data[3,4]   SubVersNr
+         */
+        if (length < 5) {
+            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+            goto not_accepted;
+        }
+        if (op == LMP_VERSION_REQ) {
+            resp = LMP_VERSION_RES;
+            resplen = 6;
+            respdata[1] = 0x20;
+            respdata[2] = 0xff;
+            respdata[3] = 0xff;
+            respdata[4] = 0xff;
+            respdata[5] = 0xff;
+        } else
+            resp = 0;
+        break;
+
+    case LMP_FEATURES_REQ:
+    case LMP_FEATURES_RES:
+        /* data[0-7]   Features
+         */
+        if (length < 8) {
+            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+            goto not_accepted;
+        }
+        if (op == LMP_FEATURES_REQ) {
+            resp = LMP_FEATURES_RES;
+            resplen = 9;
+            respdata[1] = (bt->lmp_caps >> 0) & 0xff;
+            respdata[2] = (bt->lmp_caps >> 8) & 0xff;
+            respdata[3] = (bt->lmp_caps >> 16) & 0xff;
+            respdata[4] = (bt->lmp_caps >> 24) & 0xff;
+            respdata[5] = (bt->lmp_caps >> 32) & 0xff;
+            respdata[6] = (bt->lmp_caps >> 40) & 0xff;
+            respdata[7] = (bt->lmp_caps >> 48) & 0xff;
+            respdata[8] = (bt->lmp_caps >> 56) & 0xff;
+        } else
+            resp = 0;
+        break;
+
+    case LMP_NAME_REQ:
+        /* data[0]     Name offset
+         */
+        if (length < 1) {
+            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+            goto not_accepted;
+        }
+        resp = LMP_NAME_RES;
+        resplen = 17;
+        respdata[1] = data[0];
+        respdata[2] = strlen(bt->lmp_name);
+        memset(respdata + 3, 0x00, 14);
+        if (respdata[2] > respdata[1])
+            memcpy(respdata + 3, bt->lmp_name + respdata[1],
+                            respdata[2] - respdata[1]);
+        break;
+
+    case LMP_NAME_RES:
+        /* data[0]     Name offset
+         * data[1]     Name length
+         * data[2-15]  Name fragment
+         */
+        if (length < 16) {
+            error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
+            goto not_accepted;
+        }
+        resp = 0;
+        break;
+
+    default:
+        error = HCI_UNKNOWN_LMP_PDU;
+        /* Fall through */
+    not_accepted:
+        if (op >> 8) {
+            resp = LMP_NOT_ACCEPTED_EXT;
+            resplen = 5;
+            respdata[0] = op >> 8;
+            respdata[1] = op & 0xff;
+            respdata[2] = error;
+        } else {
+            resp = LMP_NOT_ACCEPTED;
+            resplen = 3;
+            respdata[0] = op & 0xff;
+            respdata[1] = error;
+        }
+    }
+
+    if (resp == 0)
+        return;
+
+    if (resp >> 8) {
+        respdata[0] = resp >> 8;
+        respdata[1] = resp & 0xff;
+    } else
+        respdata[0] = resp & 0xff;
+
+    respdata[0] <<= 1;
+    respdata[0] |= tr;
+}
+
+static void bt_submit_raw_acl(struct bt_piconet_s *net, int length, uint8_t *data)
+{
+    struct bt_device_s *slave;
+    if (length < 1)
+        return;
+
+    slave = 0;
+#if 0
+    slave = net->slave;
+#endif
+
+    switch (data[0] & 3) {
+    case LLID_ACLC:
+        bt_submit_lmp(slave, length - 1, data + 1);
+        break;
+    case LLID_ACLU_START:
+#if 0
+        bt_sumbit_l2cap(slave, length - 1, data + 1, (data[0] >> 2) & 1);
+        breka;
+#endif
+    default:
+    case LLID_ACLU_CONT:
+        break;
+    }
+}
+#endif
+
+/* HCI layer emulation */
+
+/* Note: we could ignore endiannes because unswapped handles will still
+ * be valid as connection identifiers for the guest - they don't have to
+ * be continuously allocated.  We do it though, to preserve similar
+ * behaviour between hosts.  Some things, like the BD_ADDR cannot be
+ * preserved though (for example if a real hci is used).  */
+#ifdef HOST_WORDS_BIGENDIAN
+# define HNDL(raw)     bswap16(raw)
+#else
+# define HNDL(raw)     (raw)
+#endif
+
+static const uint8_t bt_event_reserved_mask[8] = {
+    0xff, 0x9f, 0xfb, 0xff, 0x07, 0x18, 0x00, 0x00,
+};
+
+static inline uint8_t *bt_hci_event_start(struct bt_hci_s *hci,
+                int evt, int len)
+{
+    uint8_t *packet, mask;
+    int mask_byte;
+
+    if (len > 255) {
+        fprintf(stderr, "%s: HCI event params too long (%ib)\n",
+                        __FUNCTION__, len);
+        exit(-1);
+    }
+
+    mask_byte = (evt - 1) >> 3;
+    mask = 1 << ((evt - 1) & 3);
+    if (mask & bt_event_reserved_mask[mask_byte] & ~hci->event_mask[mask_byte])
+        return NULL;
+
+    packet = hci->evt_packet(hci->opaque);
+    packet[0] = evt;
+    packet[1] = len;
+
+    return &packet[2];
+}
+
+static inline void bt_hci_event(struct bt_hci_s *hci, int evt,
+                void *params, int len)
+{
+    uint8_t *packet = bt_hci_event_start(hci, evt, len);
+
+    if (!packet)
+        return;
+
+    if (len)
+        memcpy(packet, params, len);
+
+    hci->evt_submit(hci->opaque, len + 2);
+}
+
+static inline void bt_hci_event_status(struct bt_hci_s *hci, int status)
+{
+    evt_cmd_status params = {
+        .status        = status,
+        .ncmd  = 1,
+        .opcode        = hci->last_cmd,
+    };
+
+    bt_hci_event(hci, EVT_CMD_STATUS, &params, EVT_CMD_STATUS_SIZE);
+}
+
+static inline void bt_hci_event_complete(struct bt_hci_s *hci,
+                void *ret, int len)
+{
+    uint8_t *packet = bt_hci_event_start(hci, EVT_CMD_COMPLETE,
+                    len + EVT_CMD_COMPLETE_SIZE);
+    evt_cmd_complete *params = (evt_cmd_complete *) packet;
+
+    if (!packet)
+        return;
+
+    params->ncmd       = 1;
+    params->opcode     = hci->last_cmd;
+    if (len)
+        memcpy(&packet[EVT_CMD_COMPLETE_SIZE], ret, len);
+
+    hci->evt_submit(hci->opaque, len + EVT_CMD_COMPLETE_SIZE + 2);
+}
+
+static void bt_hci_inquiry_done(void *opaque)
+{
+    struct bt_hci_s *hci = (struct bt_hci_s *) opaque;
+    uint8_t status = HCI_SUCCESS;
+
+    if (!hci->lm.periodic)
+        hci->lm.inquire = 0;
+
+    /* The specification is inconsistent about this one.  Page 565 reads
+     * "The event parameters of Inquiry Complete event will have a summary
+     * of the result from the Inquiry process, which reports the number of
+     * nearby Bluetooth devices that responded [so hci->responses].", but
+     * Event Parameters (see page 729) has only Status.  */
+    bt_hci_event(hci, EVT_INQUIRY_COMPLETE, &status, 1);
+}
+
+static void bt_hci_inquiry_result_standard(struct bt_hci_s *hci,
+                struct bt_device_s *slave)
+{
+    inquiry_info params = {
+        .num_responses         = 1,
+        .bdaddr                        = BAINIT(&slave->bd_addr),
+        .pscan_rep_mode                = 0x00, /* R0 */
+        .pscan_period_mode     = 0x00, /* P0 - deprecated */
+        .pscan_mode            = 0x00, /* Standard scan - deprecated */
+        .dev_class[0]          = slave->class[0],
+        .dev_class[1]          = slave->class[1],
+        .dev_class[2]          = slave->class[2],
+        /* TODO: return the clkoff *differenece* */
+        .clock_offset          = slave->clkoff,        /* Note: no swapping */
+    };
+
+    bt_hci_event(hci, EVT_INQUIRY_RESULT, &params, INQUIRY_INFO_SIZE);
+}
+
+static void bt_hci_inquiry_result_with_rssi(struct bt_hci_s *hci,
+                struct bt_device_s *slave)
+{
+    inquiry_info_with_rssi params = {
+        .num_responses         = 1,
+        .bdaddr                        = BAINIT(&slave->bd_addr),
+        .pscan_rep_mode                = 0x00, /* R0 */
+        .pscan_period_mode     = 0x00, /* P0 - deprecated */
+        .dev_class[0]          = slave->class[0],
+        .dev_class[1]          = slave->class[1],
+        .dev_class[2]          = slave->class[2],
+        /* TODO: return the clkoff *differenece* */
+        .clock_offset          = slave->clkoff,        /* Note: no swapping */
+        .rssi                  = DEFAULT_RSSI_DBM,
+    };
+
+    bt_hci_event(hci, EVT_INQUIRY_RESULT_WITH_RSSI,
+                    &params, INQUIRY_INFO_WITH_RSSI_SIZE);
+}
+
+static void bt_hci_inquiry_result(struct bt_hci_s *hci,
+                struct bt_device_s *slave)
+{
+    if (!slave->inquiry_scan || !hci->lm.responses_left)
+        return;
+
+    hci->lm.responses_left --;
+    hci->lm.responses ++;
+
+    switch (hci->lm.inquiry_mode) {
+    case 0x00:
+        bt_hci_inquiry_result_standard(hci, slave);
+        return;
+    case 0x01:
+        bt_hci_inquiry_result_with_rssi(hci, slave);
+        return;
+    default:
+        fprintf(stderr, "%s: bad inquiry mode %02x\n", __FUNCTION__,
+                        hci->lm.inquiry_mode);
+        exit(-1);
+    }
+}
+
+static void bt_hci_mod_timer_1280ms(QEMUTimer *timer, int period)
+{
+    qemu_mod_timer(timer, qemu_get_clock_ns(vm_clock) +
+                   muldiv64(period << 7, get_ticks_per_sec(), 100));
+}
+
+static void bt_hci_inquiry_start(struct bt_hci_s *hci, int length)
+{
+    struct bt_device_s *slave;
+
+    hci->lm.inquiry_length = length;
+    for (slave = hci->device.net->slave; slave; slave = slave->next)
+        /* Don't uncover ourselves.  */
+        if (slave != &hci->device)
+            bt_hci_inquiry_result(hci, slave);
+
+    /* TODO: register for a callback on a new device's addition to the
+     * scatternet so that if it's added before inquiry_length expires,
+     * an Inquiry Result is generated immediately.  Alternatively re-loop
+     * through the devices on the inquiry_length expiration and report
+     * devices not seen before.  */
+    if (hci->lm.responses_left)
+        bt_hci_mod_timer_1280ms(hci->lm.inquiry_done, hci->lm.inquiry_length);
+    else
+        bt_hci_inquiry_done(hci);
+
+    if (hci->lm.periodic)
+        bt_hci_mod_timer_1280ms(hci->lm.inquiry_next, hci->lm.inquiry_period);
+}
+
+static void bt_hci_inquiry_next(void *opaque)
+{
+    struct bt_hci_s *hci = (struct bt_hci_s *) opaque;
+
+    hci->lm.responses_left += hci->lm.responses;
+    hci->lm.responses = 0;
+    bt_hci_inquiry_start(hci,  hci->lm.inquiry_length);
+}
+
+static inline int bt_hci_handle_bad(struct bt_hci_s *hci, uint16_t handle)
+{
+    return !(handle & HCI_HANDLE_OFFSET) ||
+            handle >= (HCI_HANDLE_OFFSET | HCI_HANDLES_MAX) ||
+            !hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link;
+}
+
+static inline int bt_hci_role_master(struct bt_hci_s *hci, uint16_t handle)
+{
+    return !!(hci->lm.role_bmp & (1 << (handle & ~HCI_HANDLE_OFFSET)));
+}
+
+static inline struct bt_device_s *bt_hci_remote_dev(struct bt_hci_s *hci,
+                uint16_t handle)
+{
+    struct bt_link_s *link = hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link;
+
+    return bt_hci_role_master(hci, handle) ? link->slave : link->host;
+}
+
+static void bt_hci_mode_tick(void *opaque);
+static void bt_hci_lmp_link_establish(struct bt_hci_s *hci,
+                struct bt_link_s *link, int master)
+{
+    hci->lm.handle[hci->lm.last_handle].link = link;
+
+    if (master) {
+        /* We are the master side of an ACL link */
+        hci->lm.role_bmp |= 1 << hci->lm.last_handle;
+
+        hci->lm.handle[hci->lm.last_handle].lmp_acl_data =
+                link->slave->lmp_acl_data;
+    } else {
+        /* We are the slave side of an ACL link */
+        hci->lm.role_bmp &= ~(1 << hci->lm.last_handle);
+
+        hci->lm.handle[hci->lm.last_handle].lmp_acl_data =
+                link->host->lmp_acl_resp;
+    }
+
+    /* Mode */
+    if (master) {
+        link->acl_mode = acl_active;
+        hci->lm.handle[hci->lm.last_handle].acl_mode_timer =
+                qemu_new_timer_ns(vm_clock, bt_hci_mode_tick, link);
+    }
+}
+
+static void bt_hci_lmp_link_teardown(struct bt_hci_s *hci, uint16_t handle)
+{
+    handle &= ~HCI_HANDLE_OFFSET;
+    hci->lm.handle[handle].link = NULL;
+
+    if (bt_hci_role_master(hci, handle)) {
+        qemu_del_timer(hci->lm.handle[handle].acl_mode_timer);
+        qemu_free_timer(hci->lm.handle[handle].acl_mode_timer);
+    }
+}
+
+static int bt_hci_connect(struct bt_hci_s *hci, bdaddr_t *bdaddr)
+{
+    struct bt_device_s *slave;
+    struct bt_link_s link;
+
+    for (slave = hci->device.net->slave; slave; slave = slave->next)
+        if (slave->page_scan && !bacmp(&slave->bd_addr, bdaddr))
+            break;
+    if (!slave || slave == &hci->device)
+        return -ENODEV;
+
+    bacpy(&hci->lm.awaiting_bdaddr[hci->lm.connecting ++], &slave->bd_addr);
+
+    link.slave = slave;
+    link.host = &hci->device;
+    link.slave->lmp_connection_request(&link); /* Always last */
+
+    return 0;
+}
+
+static void bt_hci_connection_reject(struct bt_hci_s *hci,
+                struct bt_device_s *host, uint8_t because)
+{
+    struct bt_link_s link = {
+        .slave = &hci->device,
+        .host  = host,
+        /* Rest uninitialised */
+    };
+
+    host->reject_reason = because;
+    host->lmp_connection_complete(&link);
+}
+
+static void bt_hci_connection_reject_event(struct bt_hci_s *hci,
+                bdaddr_t *bdaddr)
+{
+    evt_conn_complete params;
+
+    params.status      = HCI_NO_CONNECTION;
+    params.handle      = 0;
+    bacpy(&params.bdaddr, bdaddr);
+    params.link_type   = ACL_LINK;
+    params.encr_mode   = 0x00;         /* Encryption not required */
+    bt_hci_event(hci, EVT_CONN_COMPLETE, &params, EVT_CONN_COMPLETE_SIZE);
+}
+
+static void bt_hci_connection_accept(struct bt_hci_s *hci,
+                struct bt_device_s *host)
+{
+    struct bt_hci_link_s *link = g_malloc0(sizeof(struct bt_hci_link_s));
+    evt_conn_complete params;
+    uint16_t handle;
+    uint8_t status = HCI_SUCCESS;
+    int tries = HCI_HANDLES_MAX;
+
+    /* Make a connection handle */
+    do {
+        while (hci->lm.handle[++ hci->lm.last_handle].link && -- tries)
+            hci->lm.last_handle &= HCI_HANDLES_MAX - 1;
+        handle = hci->lm.last_handle | HCI_HANDLE_OFFSET;
+    } while ((handle == hci->asb_handle || handle == hci->psb_handle) &&
+            tries);
+
+    if (!tries) {
+        g_free(link);
+        bt_hci_connection_reject(hci, host, HCI_REJECTED_LIMITED_RESOURCES);
+        status = HCI_NO_CONNECTION;
+        goto complete;
+    }
+
+    link->btlink.slave = &hci->device;
+    link->btlink.host  = host;
+    link->handle = handle;
+
+    /* Link established */
+    bt_hci_lmp_link_establish(hci, &link->btlink, 0);
+
+complete:
+    params.status      = status;
+    params.handle      = HNDL(handle);
+    bacpy(&params.bdaddr, &host->bd_addr);
+    params.link_type   = ACL_LINK;
+    params.encr_mode   = 0x00;         /* Encryption not required */
+    bt_hci_event(hci, EVT_CONN_COMPLETE, &params, EVT_CONN_COMPLETE_SIZE);
+
+    /* Neets to be done at the very end because it can trigger a (nested)
+     * disconnected, in case the other and had cancelled the request
+     * locally.  */
+    if (status == HCI_SUCCESS) {
+        host->reject_reason = 0;
+        host->lmp_connection_complete(&link->btlink);
+    }
+}
+
+static void bt_hci_lmp_connection_request(struct bt_link_s *link)
+{
+    struct bt_hci_s *hci = hci_from_device(link->slave);
+    evt_conn_request params;
+
+    if (hci->conn_req_host) {
+        bt_hci_connection_reject(hci, link->host,
+                                 HCI_REJECTED_LIMITED_RESOURCES);
+        return;
+    }
+    hci->conn_req_host = link->host;
+    /* TODO: if masked and auto-accept, then auto-accept,
+     * if masked and not auto-accept, then auto-reject */
+    /* TODO: kick the hci->conn_accept_timer, timeout after
+     * hci->conn_accept_tout * 0.625 msec */
+
+    bacpy(&params.bdaddr, &link->host->bd_addr);
+    memcpy(&params.dev_class, &link->host->class, sizeof(params.dev_class));
+    params.link_type   = ACL_LINK;
+    bt_hci_event(hci, EVT_CONN_REQUEST, &params, EVT_CONN_REQUEST_SIZE);
+}
+
+static void bt_hci_conn_accept_timeout(void *opaque)
+{
+    struct bt_hci_s *hci = (struct bt_hci_s *) opaque;
+
+    if (!hci->conn_req_host)
+        /* Already accepted or rejected.  If the other end cancelled the
+         * connection request then we still have to reject or accept it
+         * and then we'll get a disconnect.  */
+        return;
+
+    /* TODO */
+}
+
+/* Remove from the list of devices which we wanted to connect to and
+ * are awaiting a response from.  If the callback sees a response from
+ * a device which is not on the list it will assume it's a connection
+ * that's been cancelled by the host in the meantime and immediately
+ * try to detach the link and send a Connection Complete.  */
+static int bt_hci_lmp_connection_ready(struct bt_hci_s *hci,
+                bdaddr_t *bdaddr)
+{
+    int i;
+
+    for (i = 0; i < hci->lm.connecting; i ++)
+        if (!bacmp(&hci->lm.awaiting_bdaddr[i], bdaddr)) {
+            if (i < -- hci->lm.connecting)
+                bacpy(&hci->lm.awaiting_bdaddr[i],
+                                &hci->lm.awaiting_bdaddr[hci->lm.connecting]);
+            return 0;
+        }
+
+    return 1;
+}
+
+static void bt_hci_lmp_connection_complete(struct bt_link_s *link)
+{
+    struct bt_hci_s *hci = hci_from_device(link->host);
+    evt_conn_complete params;
+    uint16_t handle;
+    uint8_t status = HCI_SUCCESS;
+    int tries = HCI_HANDLES_MAX;
+
+    if (bt_hci_lmp_connection_ready(hci, &link->slave->bd_addr)) {
+        if (!hci->device.reject_reason)
+            link->slave->lmp_disconnect_slave(link);
+        handle = 0;
+        status = HCI_NO_CONNECTION;
+        goto complete;
+    }
+
+    if (hci->device.reject_reason) {
+        handle = 0;
+        status = hci->device.reject_reason;
+        goto complete;
+    }
+
+    /* Make a connection handle */
+    do {
+        while (hci->lm.handle[++ hci->lm.last_handle].link && -- tries)
+            hci->lm.last_handle &= HCI_HANDLES_MAX - 1;
+        handle = hci->lm.last_handle | HCI_HANDLE_OFFSET;
+    } while ((handle == hci->asb_handle || handle == hci->psb_handle) &&
+            tries);
+
+    if (!tries) {
+        link->slave->lmp_disconnect_slave(link);
+        status = HCI_NO_CONNECTION;
+        goto complete;
+    }
+
+    /* Link established */
+    link->handle = handle;
+    bt_hci_lmp_link_establish(hci, link, 1);
+
+complete:
+    params.status      = status;
+    params.handle      = HNDL(handle);
+    params.link_type   = ACL_LINK;
+    bacpy(&params.bdaddr, &link->slave->bd_addr);
+    params.encr_mode   = 0x00;         /* Encryption not required */
+    bt_hci_event(hci, EVT_CONN_COMPLETE, &params, EVT_CONN_COMPLETE_SIZE);
+}
+
+static void bt_hci_disconnect(struct bt_hci_s *hci,
+                uint16_t handle, int reason)
+{
+    struct bt_link_s *btlink =
+            hci->lm.handle[handle & ~HCI_HANDLE_OFFSET].link;
+    struct bt_hci_link_s *link;
+    evt_disconn_complete params;
+
+    if (bt_hci_role_master(hci, handle)) {
+        btlink->slave->reject_reason = reason;
+        btlink->slave->lmp_disconnect_slave(btlink);
+        /* The link pointer is invalid from now on */
+
+        goto complete;
+    }
+
+    btlink->host->reject_reason = reason;
+    btlink->host->lmp_disconnect_master(btlink);
+
+    /* We are the slave, we get to clean this burden */
+    link = (struct bt_hci_link_s *) btlink;
+    g_free(link);
+
+complete:
+    bt_hci_lmp_link_teardown(hci, handle);
+
+    params.status      = HCI_SUCCESS;
+    params.handle      = HNDL(handle);
+    params.reason      = HCI_CONNECTION_TERMINATED;
+    bt_hci_event(hci, EVT_DISCONN_COMPLETE,
+                    &params, EVT_DISCONN_COMPLETE_SIZE);
+}
+
+/* TODO: use only one function */
+static void bt_hci_lmp_disconnect_host(struct bt_link_s *link)
+{
+    struct bt_hci_s *hci = hci_from_device(link->host);
+    uint16_t handle = link->handle;
+    evt_disconn_complete params;
+
+    bt_hci_lmp_link_teardown(hci, handle);
+
+    params.status      = HCI_SUCCESS;
+    params.handle      = HNDL(handle);
+    params.reason      = hci->device.reject_reason;
+    bt_hci_event(hci, EVT_DISCONN_COMPLETE,
+                    &params, EVT_DISCONN_COMPLETE_SIZE);
+}
+
+static void bt_hci_lmp_disconnect_slave(struct bt_link_s *btlink)
+{
+    struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink;
+    struct bt_hci_s *hci = hci_from_device(btlink->slave);
+    uint16_t handle = link->handle;
+    evt_disconn_complete params;
+
+    g_free(link);
+
+    bt_hci_lmp_link_teardown(hci, handle);
+
+    params.status      = HCI_SUCCESS;
+    params.handle      = HNDL(handle);
+    params.reason      = hci->device.reject_reason;
+    bt_hci_event(hci, EVT_DISCONN_COMPLETE,
+                    &params, EVT_DISCONN_COMPLETE_SIZE);
+}
+
+static int bt_hci_name_req(struct bt_hci_s *hci, bdaddr_t *bdaddr)
+{
+    struct bt_device_s *slave;
+    evt_remote_name_req_complete params;
+
+    for (slave = hci->device.net->slave; slave; slave = slave->next)
+        if (slave->page_scan && !bacmp(&slave->bd_addr, bdaddr))
+            break;
+    if (!slave)
+        return -ENODEV;
+
+    bt_hci_event_status(hci, HCI_SUCCESS);
+
+    params.status       = HCI_SUCCESS;
+    bacpy(&params.bdaddr, &slave->bd_addr);
+    pstrcpy(params.name, sizeof(params.name), slave->lmp_name ?: "");
+    bt_hci_event(hci, EVT_REMOTE_NAME_REQ_COMPLETE,
+                    &params, EVT_REMOTE_NAME_REQ_COMPLETE_SIZE);
+
+    return 0;
+}
+
+static int bt_hci_features_req(struct bt_hci_s *hci, uint16_t handle)
+{
+    struct bt_device_s *slave;
+    evt_read_remote_features_complete params;
+
+    if (bt_hci_handle_bad(hci, handle))
+        return -ENODEV;
+
+    slave = bt_hci_remote_dev(hci, handle);
+
+    bt_hci_event_status(hci, HCI_SUCCESS);
+
+    params.status      = HCI_SUCCESS;
+    params.handle      = HNDL(handle);
+    params.features[0] = (slave->lmp_caps >>  0) & 0xff;
+    params.features[1] = (slave->lmp_caps >>  8) & 0xff;
+    params.features[2] = (slave->lmp_caps >> 16) & 0xff;
+    params.features[3] = (slave->lmp_caps >> 24) & 0xff;
+    params.features[4] = (slave->lmp_caps >> 32) & 0xff;
+    params.features[5] = (slave->lmp_caps >> 40) & 0xff;
+    params.features[6] = (slave->lmp_caps >> 48) & 0xff;
+    params.features[7] = (slave->lmp_caps >> 56) & 0xff;
+    bt_hci_event(hci, EVT_READ_REMOTE_FEATURES_COMPLETE,
+                    &params, EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE);
+
+    return 0;
+}
+
+static int bt_hci_version_req(struct bt_hci_s *hci, uint16_t handle)
+{
+    evt_read_remote_version_complete params;
+
+    if (bt_hci_handle_bad(hci, handle))
+        return -ENODEV;
+
+    bt_hci_remote_dev(hci, handle);
+
+    bt_hci_event_status(hci, HCI_SUCCESS);
+
+    params.status      = HCI_SUCCESS;
+    params.handle      = HNDL(handle);
+    params.lmp_ver     = 0x03;
+    params.manufacturer        = cpu_to_le16(0xa000);
+    params.lmp_subver  = cpu_to_le16(0xa607);
+    bt_hci_event(hci, EVT_READ_REMOTE_VERSION_COMPLETE,
+                    &params, EVT_READ_REMOTE_VERSION_COMPLETE_SIZE);
+
+    return 0;
+}
+
+static int bt_hci_clkoffset_req(struct bt_hci_s *hci, uint16_t handle)
+{
+    struct bt_device_s *slave;
+    evt_read_clock_offset_complete params;
+
+    if (bt_hci_handle_bad(hci, handle))
+        return -ENODEV;
+
+    slave = bt_hci_remote_dev(hci, handle);
+
+    bt_hci_event_status(hci, HCI_SUCCESS);
+
+    params.status      = HCI_SUCCESS;
+    params.handle      = HNDL(handle);
+    /* TODO: return the clkoff *differenece* */
+    params.clock_offset        = slave->clkoff;        /* Note: no swapping */
+    bt_hci_event(hci, EVT_READ_CLOCK_OFFSET_COMPLETE,
+                    &params, EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE);
+
+    return 0;
+}
+
+static void bt_hci_event_mode(struct bt_hci_s *hci, struct bt_link_s *link,
+                uint16_t handle)
+{
+    evt_mode_change params = {
+        .status                = HCI_SUCCESS,
+        .handle                = HNDL(handle),
+        .mode          = link->acl_mode,
+        .interval      = cpu_to_le16(link->acl_interval),
+    };
+
+    bt_hci_event(hci, EVT_MODE_CHANGE, &params, EVT_MODE_CHANGE_SIZE);
+}
+
+static void bt_hci_lmp_mode_change_master(struct bt_hci_s *hci,
+                struct bt_link_s *link, int mode, uint16_t interval)
+{
+    link->acl_mode = mode;
+    link->acl_interval = interval;
+
+    bt_hci_event_mode(hci, link, link->handle);
+
+    link->slave->lmp_mode_change(link);
+}
+
+static void bt_hci_lmp_mode_change_slave(struct bt_link_s *btlink)
+{
+    struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink;
+    struct bt_hci_s *hci = hci_from_device(btlink->slave);
+
+    bt_hci_event_mode(hci, btlink, link->handle);
+}
+
+static int bt_hci_mode_change(struct bt_hci_s *hci, uint16_t handle,
+                int interval, int mode)
+{
+    struct bt_hci_master_link_s *link;
+
+    if (bt_hci_handle_bad(hci, handle) || !bt_hci_role_master(hci, handle))
+        return -ENODEV;
+
+    link = &hci->lm.handle[handle & ~HCI_HANDLE_OFFSET];
+    if (link->link->acl_mode != acl_active) {
+        bt_hci_event_status(hci, HCI_COMMAND_DISALLOWED);
+        return 0;
+    }
+
+    bt_hci_event_status(hci, HCI_SUCCESS);
+
+    qemu_mod_timer(link->acl_mode_timer, qemu_get_clock_ns(vm_clock) +
+                   muldiv64(interval * 625, get_ticks_per_sec(), 1000000));
+    bt_hci_lmp_mode_change_master(hci, link->link, mode, interval);
+
+    return 0;
+}
+
+static int bt_hci_mode_cancel(struct bt_hci_s *hci, uint16_t handle, int mode)
+{
+    struct bt_hci_master_link_s *link;
+
+    if (bt_hci_handle_bad(hci, handle) || !bt_hci_role_master(hci, handle))
+        return -ENODEV;
+
+    link = &hci->lm.handle[handle & ~HCI_HANDLE_OFFSET];
+    if (link->link->acl_mode != mode) {
+        bt_hci_event_status(hci, HCI_COMMAND_DISALLOWED);
+
+        return 0;
+    }
+
+    bt_hci_event_status(hci, HCI_SUCCESS);
+
+    qemu_del_timer(link->acl_mode_timer);
+    bt_hci_lmp_mode_change_master(hci, link->link, acl_active, 0);
+
+    return 0;
+}
+
+static void bt_hci_mode_tick(void *opaque)
+{
+    struct bt_link_s *link = opaque;
+    struct bt_hci_s *hci = hci_from_device(link->host);
+
+    bt_hci_lmp_mode_change_master(hci, link, acl_active, 0);
+}
+
+static void bt_hci_reset(struct bt_hci_s *hci)
+{
+    hci->acl_len = 0;
+    hci->last_cmd = 0;
+    hci->lm.connecting = 0;
+
+    hci->event_mask[0] = 0xff;
+    hci->event_mask[1] = 0xff;
+    hci->event_mask[2] = 0xff;
+    hci->event_mask[3] = 0xff;
+    hci->event_mask[4] = 0xff;
+    hci->event_mask[5] = 0x1f;
+    hci->event_mask[6] = 0x00;
+    hci->event_mask[7] = 0x00;
+    hci->device.inquiry_scan = 0;
+    hci->device.page_scan = 0;
+    if (hci->device.lmp_name)
+        g_free((void *) hci->device.lmp_name);
+    hci->device.lmp_name = NULL;
+    hci->device.class[0] = 0x00;
+    hci->device.class[1] = 0x00;
+    hci->device.class[2] = 0x00;
+    hci->voice_setting = 0x0000;
+    hci->conn_accept_tout = 0x1f40;
+    hci->lm.inquiry_mode = 0x00;
+
+    hci->psb_handle = 0x000;
+    hci->asb_handle = 0x000;
+
+    /* XXX: qemu_del_timer(sl->acl_mode_timer); for all links */
+    qemu_del_timer(hci->lm.inquiry_done);
+    qemu_del_timer(hci->lm.inquiry_next);
+    qemu_del_timer(hci->conn_accept_timer);
+}
+
+static void bt_hci_read_local_version_rp(struct bt_hci_s *hci)
+{
+    read_local_version_rp lv = {
+        .status                = HCI_SUCCESS,
+        .hci_ver       = 0x03,
+        .hci_rev       = cpu_to_le16(0xa607),
+        .lmp_ver       = 0x03,
+        .manufacturer  = cpu_to_le16(0xa000),
+        .lmp_subver    = cpu_to_le16(0xa607),
+    };
+
+    bt_hci_event_complete(hci, &lv, READ_LOCAL_VERSION_RP_SIZE);
+}
+
+static void bt_hci_read_local_commands_rp(struct bt_hci_s *hci)
+{
+    read_local_commands_rp lc = {
+        .status                = HCI_SUCCESS,
+        .commands      = {
+            /* Keep updated! */
+            /* Also, keep in sync with hci->device.lmp_caps in bt_new_hci */
+            0xbf, 0x80, 0xf9, 0x03, 0xb2, 0xc0, 0x03, 0xc3,
+            0x00, 0x0f, 0x80, 0x00, 0xc0, 0x00, 0xe8, 0x13,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        },
+    };
+
+    bt_hci_event_complete(hci, &lc, READ_LOCAL_COMMANDS_RP_SIZE);
+}
+
+static void bt_hci_read_local_features_rp(struct bt_hci_s *hci)
+{
+    read_local_features_rp lf = {
+        .status                = HCI_SUCCESS,
+        .features      = {
+            (hci->device.lmp_caps >>  0) & 0xff,
+            (hci->device.lmp_caps >>  8) & 0xff,
+            (hci->device.lmp_caps >> 16) & 0xff,
+            (hci->device.lmp_caps >> 24) & 0xff,
+            (hci->device.lmp_caps >> 32) & 0xff,
+            (hci->device.lmp_caps >> 40) & 0xff,
+            (hci->device.lmp_caps >> 48) & 0xff,
+            (hci->device.lmp_caps >> 56) & 0xff,
+        },
+    };
+
+    bt_hci_event_complete(hci, &lf, READ_LOCAL_FEATURES_RP_SIZE);
+}
+
+static void bt_hci_read_local_ext_features_rp(struct bt_hci_s *hci, int page)
+{
+    read_local_ext_features_rp lef = {
+        .status                = HCI_SUCCESS,
+        .page_num      = page,
+        .max_page_num  = 0x00,
+        .features      = {
+            /* Keep updated! */
+            0x5f, 0x35, 0x85, 0x7e, 0x9b, 0x19, 0x00, 0x80,
+        },
+    };
+    if (page)
+        memset(lef.features, 0, sizeof(lef.features));
+
+    bt_hci_event_complete(hci, &lef, READ_LOCAL_EXT_FEATURES_RP_SIZE);
+}
+
+static void bt_hci_read_buffer_size_rp(struct bt_hci_s *hci)
+{
+    read_buffer_size_rp bs = {
+        /* This can be made configurable, for one standard USB dongle HCI
+         * the four values are cpu_to_le16(0x0180), 0x40,
+         * cpu_to_le16(0x0008), cpu_to_le16(0x0008).  */
+        .status                = HCI_SUCCESS,
+        .acl_mtu       = cpu_to_le16(0x0200),
+        .sco_mtu       = 0,
+        .acl_max_pkt   = cpu_to_le16(0x0001),
+        .sco_max_pkt   = cpu_to_le16(0x0000),
+    };
+
+    bt_hci_event_complete(hci, &bs, READ_BUFFER_SIZE_RP_SIZE);
+}
+
+/* Deprecated in V2.0 (page 661) */
+static void bt_hci_read_country_code_rp(struct bt_hci_s *hci)
+{
+    read_country_code_rp cc ={
+        .status                = HCI_SUCCESS,
+        .country_code  = 0x00, /* North America & Europe^1 and Japan */
+    };
+
+    bt_hci_event_complete(hci, &cc, READ_COUNTRY_CODE_RP_SIZE);
+
+    /* ^1. Except France, sorry */
+}
+
+static void bt_hci_read_bd_addr_rp(struct bt_hci_s *hci)
+{
+    read_bd_addr_rp ba = {
+        .status = HCI_SUCCESS,
+        .bdaddr = BAINIT(&hci->device.bd_addr),
+    };
+
+    bt_hci_event_complete(hci, &ba, READ_BD_ADDR_RP_SIZE);
+}
+
+static int bt_hci_link_quality_rp(struct bt_hci_s *hci, uint16_t handle)
+{
+    read_link_quality_rp lq = {
+        .status                = HCI_SUCCESS,
+        .handle                = HNDL(handle),
+        .link_quality  = 0xff,
+    };
+
+    if (bt_hci_handle_bad(hci, handle))
+        lq.status = HCI_NO_CONNECTION;
+
+    bt_hci_event_complete(hci, &lq, READ_LINK_QUALITY_RP_SIZE);
+    return 0;
+}
+
+/* Generate a Command Complete event with only the Status parameter */
+static inline void bt_hci_event_complete_status(struct bt_hci_s *hci,
+                uint8_t status)
+{
+    bt_hci_event_complete(hci, &status, 1);
+}
+
+static inline void bt_hci_event_complete_conn_cancel(struct bt_hci_s *hci,
+                uint8_t status, bdaddr_t *bd_addr)
+{
+    create_conn_cancel_rp params = {
+        .status = status,
+        .bdaddr = BAINIT(bd_addr),
+    };
+
+    bt_hci_event_complete(hci, &params, CREATE_CONN_CANCEL_RP_SIZE);
+}
+
+static inline void bt_hci_event_auth_complete(struct bt_hci_s *hci,
+                uint16_t handle)
+{
+    evt_auth_complete params = {
+        .status = HCI_SUCCESS,
+        .handle = HNDL(handle),
+    };
+
+    bt_hci_event(hci, EVT_AUTH_COMPLETE, &params, EVT_AUTH_COMPLETE_SIZE);
+}
+
+static inline void bt_hci_event_encrypt_change(struct bt_hci_s *hci,
+                uint16_t handle, uint8_t mode)
+{
+    evt_encrypt_change params = {
+        .status                = HCI_SUCCESS,
+        .handle                = HNDL(handle),
+        .encrypt       = mode,
+    };
+
+    bt_hci_event(hci, EVT_ENCRYPT_CHANGE, &params, EVT_ENCRYPT_CHANGE_SIZE);
+}
+
+static inline void bt_hci_event_complete_name_cancel(struct bt_hci_s *hci,
+                bdaddr_t *bd_addr)
+{
+    remote_name_req_cancel_rp params = {
+        .status = HCI_INVALID_PARAMETERS,
+        .bdaddr = BAINIT(bd_addr),
+    };
+
+    bt_hci_event_complete(hci, &params, REMOTE_NAME_REQ_CANCEL_RP_SIZE);
+}
+
+static inline void bt_hci_event_read_remote_ext_features(struct bt_hci_s *hci,
+                uint16_t handle)
+{
+    evt_read_remote_ext_features_complete params = {
+        .status = HCI_UNSUPPORTED_FEATURE,
+        .handle = HNDL(handle),
+        /* Rest uninitialised */
+    };
+
+    bt_hci_event(hci, EVT_READ_REMOTE_EXT_FEATURES_COMPLETE,
+                    &params, EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE);
+}
+
+static inline void bt_hci_event_complete_lmp_handle(struct bt_hci_s *hci,
+                uint16_t handle)
+{
+    read_lmp_handle_rp params = {
+        .status                = HCI_NO_CONNECTION,
+        .handle                = HNDL(handle),
+        .reserved      = 0,
+        /* Rest uninitialised */
+    };
+
+    bt_hci_event_complete(hci, &params, READ_LMP_HANDLE_RP_SIZE);
+}
+
+static inline void bt_hci_event_complete_role_discovery(struct bt_hci_s *hci,
+                int status, uint16_t handle, int master)
+{
+    role_discovery_rp params = {
+        .status                = status,
+        .handle                = HNDL(handle),
+        .role          = master ? 0x00 : 0x01,
+    };
+
+    bt_hci_event_complete(hci, &params, ROLE_DISCOVERY_RP_SIZE);
+}
+
+static inline void bt_hci_event_complete_flush(struct bt_hci_s *hci,
+                int status, uint16_t handle)
+{
+    flush_rp params = {
+        .status                = status,
+        .handle                = HNDL(handle),
+    };
+
+    bt_hci_event_complete(hci, &params, FLUSH_RP_SIZE);
+}
+
+static inline void bt_hci_event_complete_read_local_name(struct bt_hci_s *hci)
+{
+    read_local_name_rp params;
+    params.status = HCI_SUCCESS;
+    memset(params.name, 0, sizeof(params.name));
+    if (hci->device.lmp_name)
+        pstrcpy(params.name, sizeof(params.name), hci->device.lmp_name);
+
+    bt_hci_event_complete(hci, &params, READ_LOCAL_NAME_RP_SIZE);
+}
+
+static inline void bt_hci_event_complete_read_conn_accept_timeout(
+                struct bt_hci_s *hci)
+{
+    read_conn_accept_timeout_rp params = {
+        .status                = HCI_SUCCESS,
+        .timeout       = cpu_to_le16(hci->conn_accept_tout),
+    };
+
+    bt_hci_event_complete(hci, &params, READ_CONN_ACCEPT_TIMEOUT_RP_SIZE);
+}
+
+static inline void bt_hci_event_complete_read_scan_enable(struct bt_hci_s *hci)
+{
+    read_scan_enable_rp params = {
+        .status = HCI_SUCCESS,
+        .enable =
+                (hci->device.inquiry_scan ? SCAN_INQUIRY : 0) |
+                (hci->device.page_scan ? SCAN_PAGE : 0),
+    };
+
+    bt_hci_event_complete(hci, &params, READ_SCAN_ENABLE_RP_SIZE);
+}
+
+static inline void bt_hci_event_complete_read_local_class(struct bt_hci_s *hci)
+{
+    read_class_of_dev_rp params;
+
+    params.status = HCI_SUCCESS;
+    memcpy(params.dev_class, hci->device.class, sizeof(params.dev_class));
+
+    bt_hci_event_complete(hci, &params, READ_CLASS_OF_DEV_RP_SIZE);
+}
+
+static inline void bt_hci_event_complete_voice_setting(struct bt_hci_s *hci)
+{
+    read_voice_setting_rp params = {
+        .status                = HCI_SUCCESS,
+        .voice_setting = hci->voice_setting,   /* Note: no swapping */
+    };
+
+    bt_hci_event_complete(hci, &params, READ_VOICE_SETTING_RP_SIZE);
+}
+
+static inline void bt_hci_event_complete_read_inquiry_mode(
+                struct bt_hci_s *hci)
+{
+    read_inquiry_mode_rp params = {
+        .status                = HCI_SUCCESS,
+        .mode          = hci->lm.inquiry_mode,
+    };
+
+    bt_hci_event_complete(hci, &params, READ_INQUIRY_MODE_RP_SIZE);
+}
+
+static inline void bt_hci_event_num_comp_pkts(struct bt_hci_s *hci,
+                uint16_t handle, int packets)
+{
+    uint16_t buf[EVT_NUM_COMP_PKTS_SIZE(1) / 2 + 1];
+    evt_num_comp_pkts *params = (void *) ((uint8_t *) buf + 1);
+
+    params->num_hndl                   = 1;
+    params->connection->handle         = HNDL(handle);
+    params->connection->num_packets    = cpu_to_le16(packets);
+
+    bt_hci_event(hci, EVT_NUM_COMP_PKTS, params, EVT_NUM_COMP_PKTS_SIZE(1));
+}
+
+static void bt_submit_hci(struct HCIInfo *info,
+                const uint8_t *data, int length)
+{
+    struct bt_hci_s *hci = hci_from_info(info);
+    uint16_t cmd;
+    int paramlen, i;
+
+    if (length < HCI_COMMAND_HDR_SIZE)
+        goto short_hci;
+
+    memcpy(&hci->last_cmd, data, 2);
+
+    cmd = (data[1] << 8) | data[0];
+    paramlen = data[2];
+    if (cmd_opcode_ogf(cmd) == 0 || cmd_opcode_ocf(cmd) == 0)  /* NOP */
+        return;
+
+    data += HCI_COMMAND_HDR_SIZE;
+    length -= HCI_COMMAND_HDR_SIZE;
+
+    if (paramlen > length)
+        return;
+
+#define PARAM(cmd, param)      (((cmd##_cp *) data)->param)
+#define PARAM16(cmd, param)    le16_to_cpup(&PARAM(cmd, param))
+#define PARAMHANDLE(cmd)       HNDL(PARAM(cmd, handle))
+#define LENGTH_CHECK(cmd)      if (length < sizeof(cmd##_cp)) goto short_hci
+    /* Note: the supported commands bitmask in bt_hci_read_local_commands_rp
+     * needs to be updated every time a command is implemented here!  */
+    switch (cmd) {
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY):
+        LENGTH_CHECK(inquiry);
+
+        if (PARAM(inquiry, length) < 1) {
+            bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS);
+            break;
+        }
+
+        hci->lm.inquire = 1;
+        hci->lm.periodic = 0;
+        hci->lm.responses_left = PARAM(inquiry, num_rsp) ?: INT_MAX;
+        hci->lm.responses = 0;
+        bt_hci_event_status(hci, HCI_SUCCESS);
+        bt_hci_inquiry_start(hci, PARAM(inquiry, length));
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY_CANCEL):
+        if (!hci->lm.inquire || hci->lm.periodic) {
+            fprintf(stderr, "%s: Inquiry Cancel should only be issued after "
+                            "the Inquiry command has been issued, a Command "
+                            "Status event has been received for the Inquiry "
+                            "command, and before the Inquiry Complete event "
+                            "occurs", __FUNCTION__);
+            bt_hci_event_complete_status(hci, HCI_COMMAND_DISALLOWED);
+            break;
+        }
+
+        hci->lm.inquire = 0;
+        qemu_del_timer(hci->lm.inquiry_done);
+        bt_hci_event_complete_status(hci, HCI_SUCCESS);
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_PERIODIC_INQUIRY):
+        LENGTH_CHECK(periodic_inquiry);
+
+        if (!(PARAM(periodic_inquiry, length) <
+                                PARAM16(periodic_inquiry, min_period) &&
+                                PARAM16(periodic_inquiry, min_period) <
+                                PARAM16(periodic_inquiry, max_period)) ||
+                        PARAM(periodic_inquiry, length) < 1 ||
+                        PARAM16(periodic_inquiry, min_period) < 2 ||
+                        PARAM16(periodic_inquiry, max_period) < 3) {
+            bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS);
+            break;
+        }
+
+        hci->lm.inquire = 1;
+        hci->lm.periodic = 1;
+        hci->lm.responses_left = PARAM(periodic_inquiry, num_rsp);
+        hci->lm.responses = 0;
+        hci->lm.inquiry_period = PARAM16(periodic_inquiry, max_period);
+        bt_hci_event_complete_status(hci, HCI_SUCCESS);
+        bt_hci_inquiry_start(hci, PARAM(periodic_inquiry, length));
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_EXIT_PERIODIC_INQUIRY):
+        if (!hci->lm.inquire || !hci->lm.periodic) {
+            fprintf(stderr, "%s: Inquiry Cancel should only be issued after "
+                            "the Inquiry command has been issued, a Command "
+                            "Status event has been received for the Inquiry "
+                            "command, and before the Inquiry Complete event "
+                            "occurs", __FUNCTION__);
+            bt_hci_event_complete_status(hci, HCI_COMMAND_DISALLOWED);
+            break;
+        }
+        hci->lm.inquire = 0;
+        qemu_del_timer(hci->lm.inquiry_done);
+        qemu_del_timer(hci->lm.inquiry_next);
+        bt_hci_event_complete_status(hci, HCI_SUCCESS);
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_CREATE_CONN):
+        LENGTH_CHECK(create_conn);
+
+        if (hci->lm.connecting >= HCI_HANDLES_MAX) {
+            bt_hci_event_status(hci, HCI_REJECTED_LIMITED_RESOURCES);
+            break;
+        }
+        bt_hci_event_status(hci, HCI_SUCCESS);
+
+        if (bt_hci_connect(hci, &PARAM(create_conn, bdaddr)))
+            bt_hci_connection_reject_event(hci, &PARAM(create_conn, bdaddr));
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_DISCONNECT):
+        LENGTH_CHECK(disconnect);
+
+        if (bt_hci_handle_bad(hci, PARAMHANDLE(disconnect))) {
+            bt_hci_event_status(hci, HCI_NO_CONNECTION);
+            break;
+        }
+
+        bt_hci_event_status(hci, HCI_SUCCESS);
+        bt_hci_disconnect(hci, PARAMHANDLE(disconnect),
+                        PARAM(disconnect, reason));
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_CREATE_CONN_CANCEL):
+        LENGTH_CHECK(create_conn_cancel);
+
+        if (bt_hci_lmp_connection_ready(hci,
+                                &PARAM(create_conn_cancel, bdaddr))) {
+            for (i = 0; i < HCI_HANDLES_MAX; i ++)
+                if (bt_hci_role_master(hci, i) && hci->lm.handle[i].link &&
+                                !bacmp(&hci->lm.handle[i].link->slave->bd_addr,
+                                        &PARAM(create_conn_cancel, bdaddr)))
+                   break;
+
+            bt_hci_event_complete_conn_cancel(hci, i < HCI_HANDLES_MAX ?
+                            HCI_ACL_CONNECTION_EXISTS : HCI_NO_CONNECTION,
+                            &PARAM(create_conn_cancel, bdaddr));
+        } else
+            bt_hci_event_complete_conn_cancel(hci, HCI_SUCCESS,
+                            &PARAM(create_conn_cancel, bdaddr));
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ):
+        LENGTH_CHECK(accept_conn_req);
+
+        if (!hci->conn_req_host ||
+                        bacmp(&PARAM(accept_conn_req, bdaddr),
+                                &hci->conn_req_host->bd_addr)) {
+            bt_hci_event_status(hci, HCI_INVALID_PARAMETERS);
+            break;
+        }
+
+        bt_hci_event_status(hci, HCI_SUCCESS);
+        bt_hci_connection_accept(hci, hci->conn_req_host);
+        hci->conn_req_host = NULL;
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_REJECT_CONN_REQ):
+        LENGTH_CHECK(reject_conn_req);
+
+        if (!hci->conn_req_host ||
+                        bacmp(&PARAM(reject_conn_req, bdaddr),
+                                &hci->conn_req_host->bd_addr)) {
+            bt_hci_event_status(hci, HCI_INVALID_PARAMETERS);
+            break;
+        }
+
+        bt_hci_event_status(hci, HCI_SUCCESS);
+        bt_hci_connection_reject(hci, hci->conn_req_host,
+                        PARAM(reject_conn_req, reason));
+        bt_hci_connection_reject_event(hci, &hci->conn_req_host->bd_addr);
+        hci->conn_req_host = NULL;
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_AUTH_REQUESTED):
+        LENGTH_CHECK(auth_requested);
+
+        if (bt_hci_handle_bad(hci, PARAMHANDLE(auth_requested)))
+            bt_hci_event_status(hci, HCI_NO_CONNECTION);
+        else {
+            bt_hci_event_status(hci, HCI_SUCCESS);
+            bt_hci_event_auth_complete(hci, PARAMHANDLE(auth_requested));
+        }
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT):
+        LENGTH_CHECK(set_conn_encrypt);
+
+        if (bt_hci_handle_bad(hci, PARAMHANDLE(set_conn_encrypt)))
+            bt_hci_event_status(hci, HCI_NO_CONNECTION);
+        else {
+            bt_hci_event_status(hci, HCI_SUCCESS);
+            bt_hci_event_encrypt_change(hci,
+                            PARAMHANDLE(set_conn_encrypt),
+                            PARAM(set_conn_encrypt, encrypt));
+        }
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_REMOTE_NAME_REQ):
+        LENGTH_CHECK(remote_name_req);
+
+        if (bt_hci_name_req(hci, &PARAM(remote_name_req, bdaddr)))
+            bt_hci_event_status(hci, HCI_NO_CONNECTION);
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_REMOTE_NAME_REQ_CANCEL):
+        LENGTH_CHECK(remote_name_req_cancel);
+
+        bt_hci_event_complete_name_cancel(hci,
+                        &PARAM(remote_name_req_cancel, bdaddr));
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_FEATURES):
+        LENGTH_CHECK(read_remote_features);
+
+        if (bt_hci_features_req(hci, PARAMHANDLE(read_remote_features)))
+            bt_hci_event_status(hci, HCI_NO_CONNECTION);
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_EXT_FEATURES):
+        LENGTH_CHECK(read_remote_ext_features);
+
+        if (bt_hci_handle_bad(hci, PARAMHANDLE(read_remote_ext_features)))
+            bt_hci_event_status(hci, HCI_NO_CONNECTION);
+        else {
+            bt_hci_event_status(hci, HCI_SUCCESS);
+            bt_hci_event_read_remote_ext_features(hci,
+                            PARAMHANDLE(read_remote_ext_features));
+        }
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_REMOTE_VERSION):
+        LENGTH_CHECK(read_remote_version);
+
+        if (bt_hci_version_req(hci, PARAMHANDLE(read_remote_version)))
+            bt_hci_event_status(hci, HCI_NO_CONNECTION);
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_CLOCK_OFFSET):
+        LENGTH_CHECK(read_clock_offset);
+
+        if (bt_hci_clkoffset_req(hci, PARAMHANDLE(read_clock_offset)))
+            bt_hci_event_status(hci, HCI_NO_CONNECTION);
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_CTL, OCF_READ_LMP_HANDLE):
+        LENGTH_CHECK(read_lmp_handle);
+
+        /* TODO: */
+        bt_hci_event_complete_lmp_handle(hci, PARAMHANDLE(read_lmp_handle));
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_POLICY, OCF_HOLD_MODE):
+        LENGTH_CHECK(hold_mode);
+
+        if (PARAM16(hold_mode, min_interval) >
+                        PARAM16(hold_mode, max_interval) ||
+                        PARAM16(hold_mode, min_interval) < 0x0002 ||
+                        PARAM16(hold_mode, max_interval) > 0xff00 ||
+                        (PARAM16(hold_mode, min_interval) & 1) ||
+                        (PARAM16(hold_mode, max_interval) & 1)) {
+            bt_hci_event_status(hci, HCI_INVALID_PARAMETERS);
+            break;
+        }
+
+        if (bt_hci_mode_change(hci, PARAMHANDLE(hold_mode),
+                                PARAM16(hold_mode, max_interval),
+                                acl_hold))
+            bt_hci_event_status(hci, HCI_NO_CONNECTION);
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_POLICY, OCF_PARK_MODE):
+        LENGTH_CHECK(park_mode);
+
+        if (PARAM16(park_mode, min_interval) >
+                        PARAM16(park_mode, max_interval) ||
+                        PARAM16(park_mode, min_interval) < 0x000e ||
+                        (PARAM16(park_mode, min_interval) & 1) ||
+                        (PARAM16(park_mode, max_interval) & 1)) {
+            bt_hci_event_status(hci, HCI_INVALID_PARAMETERS);
+            break;
+        }
+
+        if (bt_hci_mode_change(hci, PARAMHANDLE(park_mode),
+                                PARAM16(park_mode, max_interval),
+                                acl_parked))
+            bt_hci_event_status(hci, HCI_NO_CONNECTION);
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_POLICY, OCF_EXIT_PARK_MODE):
+        LENGTH_CHECK(exit_park_mode);
+
+        if (bt_hci_mode_cancel(hci, PARAMHANDLE(exit_park_mode),
+                                acl_parked))
+            bt_hci_event_status(hci, HCI_NO_CONNECTION);
+        break;
+
+    case cmd_opcode_pack(OGF_LINK_POLICY, OCF_ROLE_DISCOVERY):
+        LENGTH_CHECK(role_discovery);
+
+        if (bt_hci_handle_bad(hci, PARAMHANDLE(role_discovery)))
+            bt_hci_event_complete_role_discovery(hci,
+                            HCI_NO_CONNECTION, PARAMHANDLE(role_discovery), 0);
+        else
+            bt_hci_event_complete_role_discovery(hci,
+                            HCI_SUCCESS, PARAMHANDLE(role_discovery),
+                            bt_hci_role_master(hci,
+                                    PARAMHANDLE(role_discovery)));
+        break;
+
+    case cmd_opcode_pack(OGF_HOST_CTL, OCF_SET_EVENT_MASK):
+        LENGTH_CHECK(set_event_mask);
+
+        memcpy(hci->event_mask, PARAM(set_event_mask, mask), 8);
+        bt_hci_event_complete_status(hci, HCI_SUCCESS);
+        break;
+
+    case cmd_opcode_pack(OGF_HOST_CTL, OCF_RESET):
+        bt_hci_reset(hci);
+        bt_hci_event_status(hci, HCI_SUCCESS);
+        break;
+
+    case cmd_opcode_pack(OGF_HOST_CTL, OCF_SET_EVENT_FLT):
+        if (length >= 1 && PARAM(set_event_flt, flt_type) == FLT_CLEAR_ALL)
+            /* No length check */;
+        else
+            LENGTH_CHECK(set_event_flt);
+
+        /* Filters are not implemented */
+        bt_hci_event_complete_status(hci, HCI_SUCCESS);
+        break;
+
+    case cmd_opcode_pack(OGF_HOST_CTL, OCF_FLUSH):
+        LENGTH_CHECK(flush);
+
+        if (bt_hci_handle_bad(hci, PARAMHANDLE(flush)))
+            bt_hci_event_complete_flush(hci,
+                            HCI_NO_CONNECTION, PARAMHANDLE(flush));
+        else {
+            /* TODO: ordering? */
+            bt_hci_event(hci, EVT_FLUSH_OCCURRED,
+                            &PARAM(flush, handle),
+                            EVT_FLUSH_OCCURRED_SIZE);
+            bt_hci_event_complete_flush(hci,
+                            HCI_SUCCESS, PARAMHANDLE(flush));
+        }
+        break;
+
+    case cmd_opcode_pack(OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME):
+        LENGTH_CHECK(change_local_name);
+
+        if (hci->device.lmp_name)
+            g_free((void *) hci->device.lmp_name);
+        hci->device.lmp_name = g_strndup(PARAM(change_local_name, name),
+                        sizeof(PARAM(change_local_name, name)));
+        bt_hci_event_complete_status(hci, HCI_SUCCESS);
+        break;
+
+    case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_LOCAL_NAME):
+        bt_hci_event_complete_read_local_name(hci);
+        break;
+
+    case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_CONN_ACCEPT_TIMEOUT):
+        bt_hci_event_complete_read_conn_accept_timeout(hci);
+        break;
+
+    case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CONN_ACCEPT_TIMEOUT):
+        /* TODO */
+        LENGTH_CHECK(write_conn_accept_timeout);
+
+        if (PARAM16(write_conn_accept_timeout, timeout) < 0x0001 ||
+                        PARAM16(write_conn_accept_timeout, timeout) > 0xb540) {
+            bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS);
+            break;
+        }
+
+        hci->conn_accept_tout = PARAM16(write_conn_accept_timeout, timeout);
+        bt_hci_event_complete_status(hci, HCI_SUCCESS);
+        break;
+
+    case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_SCAN_ENABLE):
+        bt_hci_event_complete_read_scan_enable(hci);
+        break;
+
+    case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE):
+        LENGTH_CHECK(write_scan_enable);
+
+        /* TODO: check that the remaining bits are all 0 */
+        hci->device.inquiry_scan =
+                !!(PARAM(write_scan_enable, scan_enable) & SCAN_INQUIRY);
+        hci->device.page_scan =
+                !!(PARAM(write_scan_enable, scan_enable) & SCAN_PAGE);
+        bt_hci_event_complete_status(hci, HCI_SUCCESS);
+        break;
+
+    case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_CLASS_OF_DEV):
+        bt_hci_event_complete_read_local_class(hci);
+        break;
+
+    case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV):
+        LENGTH_CHECK(write_class_of_dev);
+
+        memcpy(hci->device.class, PARAM(write_class_of_dev, dev_class),
+                        sizeof(PARAM(write_class_of_dev, dev_class)));
+        bt_hci_event_complete_status(hci, HCI_SUCCESS);
+        break;
+
+    case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_VOICE_SETTING):
+        bt_hci_event_complete_voice_setting(hci);
+        break;
+
+    case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_VOICE_SETTING):
+        LENGTH_CHECK(write_voice_setting);
+
+        hci->voice_setting = PARAM(write_voice_setting, voice_setting);
+        bt_hci_event_complete_status(hci, HCI_SUCCESS);
+        break;
+
+    case cmd_opcode_pack(OGF_HOST_CTL, OCF_HOST_NUMBER_OF_COMPLETED_PACKETS):
+        if (length < data[0] * 2 + 1)
+            goto short_hci;
+
+        for (i = 0; i < data[0]; i ++)
+            if (bt_hci_handle_bad(hci,
+                                    data[i * 2 + 1] | (data[i * 2 + 2] << 8)))
+                bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS);
+        break;
+
+    case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_INQUIRY_MODE):
+        /* Only if (local_features[3] & 0x40) && (local_commands[12] & 0x40)
+         * else
+         *     goto unknown_command */
+        bt_hci_event_complete_read_inquiry_mode(hci);
+        break;
+
+    case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_INQUIRY_MODE):
+        /* Only if (local_features[3] & 0x40) && (local_commands[12] & 0x80)
+         * else
+         *     goto unknown_command */
+        LENGTH_CHECK(write_inquiry_mode);
+
+        if (PARAM(write_inquiry_mode, mode) > 0x01) {
+            bt_hci_event_complete_status(hci, HCI_INVALID_PARAMETERS);
+            break;
+        }
+
+        hci->lm.inquiry_mode = PARAM(write_inquiry_mode, mode);
+        bt_hci_event_complete_status(hci, HCI_SUCCESS);
+        break;
+
+    case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_VERSION):
+        bt_hci_read_local_version_rp(hci);
+        break;
+
+    case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_COMMANDS):
+        bt_hci_read_local_commands_rp(hci);
+        break;
+
+    case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES):
+        bt_hci_read_local_features_rp(hci);
+        break;
+
+    case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_EXT_FEATURES):
+        LENGTH_CHECK(read_local_ext_features);
+
+        bt_hci_read_local_ext_features_rp(hci,
+                        PARAM(read_local_ext_features, page_num));
+        break;
+
+    case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_BUFFER_SIZE):
+        bt_hci_read_buffer_size_rp(hci);
+        break;
+
+    case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_COUNTRY_CODE):
+        bt_hci_read_country_code_rp(hci);
+        break;
+
+    case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_BD_ADDR):
+        bt_hci_read_bd_addr_rp(hci);
+        break;
+
+    case cmd_opcode_pack(OGF_STATUS_PARAM, OCF_READ_LINK_QUALITY):
+        LENGTH_CHECK(read_link_quality);
+
+        bt_hci_link_quality_rp(hci, PARAMHANDLE(read_link_quality));
+        break;
+
+    default:
+        bt_hci_event_status(hci, HCI_UNKNOWN_COMMAND);
+        break;
+
+    short_hci:
+        fprintf(stderr, "%s: HCI packet too short (%iB)\n",
+                        __FUNCTION__, length);
+        bt_hci_event_status(hci, HCI_INVALID_PARAMETERS);
+        break;
+    }
+}
+
+/* We could perform fragmentation here, we can't do "recombination" because
+ * at this layer the length of the payload is not know ahead, so we only
+ * know that a packet contained the last fragment of the SDU when the next
+ * SDU starts.  */
+static inline void bt_hci_lmp_acl_data(struct bt_hci_s *hci, uint16_t handle,
+                const uint8_t *data, int start, int len)
+{
+    struct hci_acl_hdr *pkt = (void *) hci->acl_buf;
+
+    /* TODO: packet flags */
+    /* TODO: avoid memcpy'ing */
+
+    if (len + HCI_ACL_HDR_SIZE > sizeof(hci->acl_buf)) {
+        fprintf(stderr, "%s: can't take ACL packets %i bytes long\n",
+                        __FUNCTION__, len);
+        return;
+    }
+    memcpy(hci->acl_buf + HCI_ACL_HDR_SIZE, data, len);
+
+    pkt->handle = cpu_to_le16(
+                    acl_handle_pack(handle, start ? ACL_START : ACL_CONT));
+    pkt->dlen = cpu_to_le16(len);
+    hci->info.acl_recv(hci->info.opaque,
+                    hci->acl_buf, len + HCI_ACL_HDR_SIZE);
+}
+
+static void bt_hci_lmp_acl_data_slave(struct bt_link_s *btlink,
+                const uint8_t *data, int start, int len)
+{
+    struct bt_hci_link_s *link = (struct bt_hci_link_s *) btlink;
+
+    bt_hci_lmp_acl_data(hci_from_device(btlink->slave),
+                    link->handle, data, start, len);
+}
+
+static void bt_hci_lmp_acl_data_host(struct bt_link_s *link,
+                const uint8_t *data, int start, int len)
+{
+    bt_hci_lmp_acl_data(hci_from_device(link->host),
+                    link->handle, data, start, len);
+}
+
+static void bt_submit_acl(struct HCIInfo *info,
+                const uint8_t *data, int length)
+{
+    struct bt_hci_s *hci = hci_from_info(info);
+    uint16_t handle;
+    int datalen, flags;
+    struct bt_link_s *link;
+
+    if (length < HCI_ACL_HDR_SIZE) {
+        fprintf(stderr, "%s: ACL packet too short (%iB)\n",
+                        __FUNCTION__, length);
+        return;
+    }
+
+    handle = acl_handle((data[1] << 8) | data[0]);
+    flags = acl_flags((data[1] << 8) | data[0]);
+    datalen = (data[3] << 8) | data[2];
+    data += HCI_ACL_HDR_SIZE;
+    length -= HCI_ACL_HDR_SIZE;
+
+    if (bt_hci_handle_bad(hci, handle)) {
+        fprintf(stderr, "%s: invalid ACL handle %03x\n",
+                        __FUNCTION__, handle);
+        /* TODO: signal an error */
+        return;
+    }
+    handle &= ~HCI_HANDLE_OFFSET;
+
+    if (datalen > length) {
+        fprintf(stderr, "%s: ACL packet too short (%iB < %iB)\n",
+                        __FUNCTION__, length, datalen);
+        return;
+    }
+
+    link = hci->lm.handle[handle].link;
+
+    if ((flags & ~3) == ACL_ACTIVE_BCAST) {
+        if (!hci->asb_handle)
+            hci->asb_handle = handle;
+        else if (handle != hci->asb_handle) {
+            fprintf(stderr, "%s: Bad handle %03x in Active Slave Broadcast\n",
+                            __FUNCTION__, handle);
+            /* TODO: signal an error */
+            return;
+        }
+
+        /* TODO */
+    }
+
+    if ((flags & ~3) == ACL_PICO_BCAST) {
+        if (!hci->psb_handle)
+            hci->psb_handle = handle;
+        else if (handle != hci->psb_handle) {
+            fprintf(stderr, "%s: Bad handle %03x in Parked Slave Broadcast\n",
+                            __FUNCTION__, handle);
+            /* TODO: signal an error */
+            return;
+        }
+
+        /* TODO */
+    }
+
+    /* TODO: increase counter and send EVT_NUM_COMP_PKTS */
+    bt_hci_event_num_comp_pkts(hci, handle | HCI_HANDLE_OFFSET, 1);
+
+    /* Do this last as it can trigger further events even in this HCI */
+    hci->lm.handle[handle].lmp_acl_data(link, data,
+                    (flags & 3) == ACL_START, length);
+}
+
+static void bt_submit_sco(struct HCIInfo *info,
+                const uint8_t *data, int length)
+{
+    struct bt_hci_s *hci = hci_from_info(info);
+    uint16_t handle;
+    int datalen;
+
+    if (length < 3)
+        return;
+
+    handle = acl_handle((data[1] << 8) | data[0]);
+    datalen = data[2];
+    length -= 3;
+
+    if (bt_hci_handle_bad(hci, handle)) {
+        fprintf(stderr, "%s: invalid SCO handle %03x\n",
+                        __FUNCTION__, handle);
+        return;
+    }
+
+    if (datalen > length) {
+        fprintf(stderr, "%s: SCO packet too short (%iB < %iB)\n",
+                        __FUNCTION__, length, datalen);
+        return;
+    }
+
+    /* TODO */
+
+    /* TODO: increase counter and send EVT_NUM_COMP_PKTS if synchronous
+     * Flow Control is enabled.
+     * (See Read/Write_Synchronous_Flow_Control_Enable on page 513 and
+     * page 514.)  */
+}
+
+static uint8_t *bt_hci_evt_packet(void *opaque)
+{
+    /* TODO: allocate a packet from upper layer */
+    struct bt_hci_s *s = opaque;
+
+    return s->evt_buf;
+}
+
+static void bt_hci_evt_submit(void *opaque, int len)
+{
+    /* TODO: notify upper layer */
+    struct bt_hci_s *s = opaque;
+
+    s->info.evt_recv(s->info.opaque, s->evt_buf, len);
+}
+
+static int bt_hci_bdaddr_set(struct HCIInfo *info, const uint8_t *bd_addr)
+{
+    struct bt_hci_s *hci = hci_from_info(info);
+
+    bacpy(&hci->device.bd_addr, (const bdaddr_t *) bd_addr);
+    return 0;
+}
+
+static void bt_hci_done(struct HCIInfo *info);
+static void bt_hci_destroy(struct bt_device_s *dev)
+{
+    struct bt_hci_s *hci = hci_from_device(dev);
+
+    bt_hci_done(&hci->info);
+}
+
+struct HCIInfo *bt_new_hci(struct bt_scatternet_s *net)
+{
+    struct bt_hci_s *s = g_malloc0(sizeof(struct bt_hci_s));
+
+    s->lm.inquiry_done = qemu_new_timer_ns(vm_clock, bt_hci_inquiry_done, s);
+    s->lm.inquiry_next = qemu_new_timer_ns(vm_clock, bt_hci_inquiry_next, s);
+    s->conn_accept_timer =
+            qemu_new_timer_ns(vm_clock, bt_hci_conn_accept_timeout, s);
+
+    s->evt_packet = bt_hci_evt_packet;
+    s->evt_submit = bt_hci_evt_submit;
+    s->opaque = s;
+
+    bt_device_init(&s->device, net);
+    s->device.lmp_connection_request = bt_hci_lmp_connection_request;
+    s->device.lmp_connection_complete = bt_hci_lmp_connection_complete;
+    s->device.lmp_disconnect_master = bt_hci_lmp_disconnect_host;
+    s->device.lmp_disconnect_slave = bt_hci_lmp_disconnect_slave;
+    s->device.lmp_acl_data = bt_hci_lmp_acl_data_slave;
+    s->device.lmp_acl_resp = bt_hci_lmp_acl_data_host;
+    s->device.lmp_mode_change = bt_hci_lmp_mode_change_slave;
+
+    /* Keep updated! */
+    /* Also keep in sync with supported commands bitmask in
+     * bt_hci_read_local_commands_rp */
+    s->device.lmp_caps = 0x8000199b7e85355fll;
+
+    bt_hci_reset(s);
+
+    s->info.cmd_send = bt_submit_hci;
+    s->info.sco_send = bt_submit_sco;
+    s->info.acl_send = bt_submit_acl;
+    s->info.bdaddr_set = bt_hci_bdaddr_set;
+
+    s->device.handle_destroy = bt_hci_destroy;
+
+    return &s->info;
+}
+
+static void bt_hci_done(struct HCIInfo *info)
+{
+    struct bt_hci_s *hci = hci_from_info(info);
+    int handle;
+
+    bt_device_done(&hci->device);
+
+    if (hci->device.lmp_name)
+        g_free((void *) hci->device.lmp_name);
+
+    /* Be gentle and send DISCONNECT to all connected peers and those
+     * currently waiting for us to accept or reject a connection request.
+     * This frees the links.  */
+    if (hci->conn_req_host) {
+        bt_hci_connection_reject(hci,
+                                 hci->conn_req_host, HCI_OE_POWER_OFF);
+        return;
+    }
+
+    for (handle = HCI_HANDLE_OFFSET;
+                    handle < (HCI_HANDLE_OFFSET | HCI_HANDLES_MAX); handle ++)
+        if (!bt_hci_handle_bad(hci, handle))
+            bt_hci_disconnect(hci, handle, HCI_OE_POWER_OFF);
+
+    /* TODO: this is not enough actually, there may be slaves from whom
+     * we have requested a connection who will soon (or not) respond with
+     * an accept or a reject, so we should also check if hci->lm.connecting
+     * is non-zero and if so, avoid freeing the hci but otherwise disappear
+     * from all qemu social life (e.g. stop scanning and request to be
+     * removed from s->device.net) and arrange for
+     * s->device.lmp_connection_complete to free the remaining bits once
+     * hci->lm.awaiting_bdaddr[] is empty.  */
+
+    qemu_free_timer(hci->lm.inquiry_done);
+    qemu_free_timer(hci->lm.inquiry_next);
+    qemu_free_timer(hci->conn_accept_timer);
+
+    g_free(hci);
+}
diff --git a/hw/bt/hid.c b/hw/bt/hid.c
new file mode 100644 (file)
index 0000000..af494e1
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ * QEMU Bluetooth HID Profile wrapper for USB HID.
+ *
+ * Copyright (C) 2007-2008 OpenMoko, Inc.
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "ui/console.h"
+#include "hw/input/hid.h"
+#include "hw/bt.h"
+
+enum hid_transaction_req {
+    BT_HANDSHAKE                       = 0x0,
+    BT_HID_CONTROL                     = 0x1,
+    BT_GET_REPORT                      = 0x4,
+    BT_SET_REPORT                      = 0x5,
+    BT_GET_PROTOCOL                    = 0x6,
+    BT_SET_PROTOCOL                    = 0x7,
+    BT_GET_IDLE                                = 0x8,
+    BT_SET_IDLE                                = 0x9,
+    BT_DATA                            = 0xa,
+    BT_DATC                            = 0xb,
+};
+
+enum hid_transaction_handshake {
+    BT_HS_SUCCESSFUL                   = 0x0,
+    BT_HS_NOT_READY                    = 0x1,
+    BT_HS_ERR_INVALID_REPORT_ID                = 0x2,
+    BT_HS_ERR_UNSUPPORTED_REQUEST      = 0x3,
+    BT_HS_ERR_INVALID_PARAMETER                = 0x4,
+    BT_HS_ERR_UNKNOWN                  = 0xe,
+    BT_HS_ERR_FATAL                    = 0xf,
+};
+
+enum hid_transaction_control {
+    BT_HC_NOP                          = 0x0,
+    BT_HC_HARD_RESET                   = 0x1,
+    BT_HC_SOFT_RESET                   = 0x2,
+    BT_HC_SUSPEND                      = 0x3,
+    BT_HC_EXIT_SUSPEND                 = 0x4,
+    BT_HC_VIRTUAL_CABLE_UNPLUG         = 0x5,
+};
+
+enum hid_protocol {
+    BT_HID_PROTO_BOOT                  = 0,
+    BT_HID_PROTO_REPORT                        = 1,
+};
+
+enum hid_boot_reportid {
+    BT_HID_BOOT_INVALID                        = 0,
+    BT_HID_BOOT_KEYBOARD,
+    BT_HID_BOOT_MOUSE,
+};
+
+enum hid_data_pkt {
+    BT_DATA_OTHER                      = 0,
+    BT_DATA_INPUT,
+    BT_DATA_OUTPUT,
+    BT_DATA_FEATURE,
+};
+
+#define BT_HID_MTU                     48
+
+/* HID interface requests */
+#define GET_REPORT                     0xa101
+#define GET_IDLE                       0xa102
+#define GET_PROTOCOL                   0xa103
+#define SET_REPORT                     0x2109
+#define SET_IDLE                       0x210a
+#define SET_PROTOCOL                   0x210b
+
+struct bt_hid_device_s {
+    struct bt_l2cap_device_s btdev;
+    struct bt_l2cap_conn_params_s *control;
+    struct bt_l2cap_conn_params_s *interrupt;
+    HIDState hid;
+
+    int proto;
+    int connected;
+    int data_type;
+    int intr_state;
+    struct {
+        int len;
+        uint8_t buffer[1024];
+    } dataother, datain, dataout, feature, intrdataout;
+    enum {
+        bt_state_ready,
+        bt_state_transaction,
+        bt_state_suspend,
+    } state;
+};
+
+static void bt_hid_reset(struct bt_hid_device_s *s)
+{
+    struct bt_scatternet_s *net = s->btdev.device.net;
+
+    /* Go as far as... */
+    bt_l2cap_device_done(&s->btdev);
+    bt_l2cap_device_init(&s->btdev, net);
+
+    hid_reset(&s->hid);
+    s->proto = BT_HID_PROTO_REPORT;
+    s->state = bt_state_ready;
+    s->dataother.len = 0;
+    s->datain.len = 0;
+    s->dataout.len = 0;
+    s->feature.len = 0;
+    s->intrdataout.len = 0;
+    s->intr_state = 0;
+}
+
+static int bt_hid_out(struct bt_hid_device_s *s)
+{
+    if (s->data_type == BT_DATA_OUTPUT) {
+        /* nothing */
+        ;
+    }
+
+    if (s->data_type == BT_DATA_FEATURE) {
+        /* XXX:
+         * does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE
+         * or a SET_REPORT? */
+        ;
+    }
+
+    return -1;
+}
+
+static int bt_hid_in(struct bt_hid_device_s *s)
+{
+    s->datain.len = hid_keyboard_poll(&s->hid, s->datain.buffer,
+                                      sizeof(s->datain.buffer));
+    return s->datain.len;
+}
+
+static void bt_hid_send_handshake(struct bt_hid_device_s *s, int result)
+{
+    *s->control->sdu_out(s->control, 1) =
+            (BT_HANDSHAKE << 4) | result;
+    s->control->sdu_submit(s->control);
+}
+
+static void bt_hid_send_control(struct bt_hid_device_s *s, int operation)
+{
+    *s->control->sdu_out(s->control, 1) =
+            (BT_HID_CONTROL << 4) | operation;
+    s->control->sdu_submit(s->control);
+}
+
+static void bt_hid_disconnect(struct bt_hid_device_s *s)
+{
+    /* Disconnect s->control and s->interrupt */
+}
+
+static void bt_hid_send_data(struct bt_l2cap_conn_params_s *ch, int type,
+                const uint8_t *data, int len)
+{
+    uint8_t *pkt, hdr = (BT_DATA << 4) | type;
+    int plen;
+
+    do {
+        plen = MIN(len, ch->remote_mtu - 1);
+        pkt = ch->sdu_out(ch, plen + 1);
+
+        pkt[0] = hdr;
+        if (plen)
+            memcpy(pkt + 1, data, plen);
+        ch->sdu_submit(ch);
+
+        len -= plen;
+        data += plen;
+        hdr = (BT_DATC << 4) | type;
+    } while (plen == ch->remote_mtu - 1);
+}
+
+static void bt_hid_control_transaction(struct bt_hid_device_s *s,
+                const uint8_t *data, int len)
+{
+    uint8_t type, parameter;
+    int rlen, ret = -1;
+    if (len < 1)
+        return;
+
+    type = data[0] >> 4;
+    parameter = data[0] & 0xf;
+
+    switch (type) {
+    case BT_HANDSHAKE:
+    case BT_DATA:
+        switch (parameter) {
+        default:
+            /* These are not expected to be sent this direction.  */
+            ret = BT_HS_ERR_INVALID_PARAMETER;
+        }
+        break;
+
+    case BT_HID_CONTROL:
+        if (len != 1 || (parameter != BT_HC_VIRTUAL_CABLE_UNPLUG &&
+                                s->state == bt_state_transaction)) {
+            ret = BT_HS_ERR_INVALID_PARAMETER;
+            break;
+        }
+        switch (parameter) {
+        case BT_HC_NOP:
+            break;
+        case BT_HC_HARD_RESET:
+        case BT_HC_SOFT_RESET:
+            bt_hid_reset(s);
+            break;
+        case BT_HC_SUSPEND:
+            if (s->state == bt_state_ready)
+                s->state = bt_state_suspend;
+            else
+                ret = BT_HS_ERR_INVALID_PARAMETER;
+            break;
+        case BT_HC_EXIT_SUSPEND:
+            if (s->state == bt_state_suspend)
+                s->state = bt_state_ready;
+            else
+                ret = BT_HS_ERR_INVALID_PARAMETER;
+            break;
+        case BT_HC_VIRTUAL_CABLE_UNPLUG:
+            bt_hid_disconnect(s);
+            break;
+        default:
+            ret = BT_HS_ERR_INVALID_PARAMETER;
+        }
+        break;
+
+    case BT_GET_REPORT:
+        /* No ReportIDs declared.  */
+        if (((parameter & 8) && len != 3) ||
+                        (!(parameter & 8) && len != 1) ||
+                        s->state != bt_state_ready) {
+            ret = BT_HS_ERR_INVALID_PARAMETER;
+            break;
+        }
+        if (parameter & 8)
+            rlen = data[2] | (data[3] << 8);
+        else
+            rlen = INT_MAX;
+        switch (parameter & 3) {
+        case BT_DATA_OTHER:
+            ret = BT_HS_ERR_INVALID_PARAMETER;
+            break;
+        case BT_DATA_INPUT:
+            /* Here we can as well poll s->usbdev */
+            bt_hid_send_data(s->control, BT_DATA_INPUT,
+                            s->datain.buffer, MIN(rlen, s->datain.len));
+            break;
+        case BT_DATA_OUTPUT:
+            bt_hid_send_data(s->control, BT_DATA_OUTPUT,
+                            s->dataout.buffer, MIN(rlen, s->dataout.len));
+            break;
+        case BT_DATA_FEATURE:
+            bt_hid_send_data(s->control, BT_DATA_FEATURE,
+                            s->feature.buffer, MIN(rlen, s->feature.len));
+            break;
+        }
+        break;
+
+    case BT_SET_REPORT:
+        if (len < 2 || len > BT_HID_MTU || s->state != bt_state_ready ||
+                        (parameter & 3) == BT_DATA_OTHER ||
+                        (parameter & 3) == BT_DATA_INPUT) {
+            ret = BT_HS_ERR_INVALID_PARAMETER;
+            break;
+        }
+        s->data_type = parameter & 3;
+        if (s->data_type == BT_DATA_OUTPUT) {
+            s->dataout.len = len - 1;
+            memcpy(s->dataout.buffer, data + 1, s->dataout.len);
+        } else {
+            s->feature.len = len - 1;
+            memcpy(s->feature.buffer, data + 1, s->feature.len);
+        }
+        if (len == BT_HID_MTU)
+            s->state = bt_state_transaction;
+        else
+            bt_hid_out(s);
+        break;
+
+    case BT_GET_PROTOCOL:
+        if (len != 1 || s->state == bt_state_transaction) {
+            ret = BT_HS_ERR_INVALID_PARAMETER;
+            break;
+        }
+        *s->control->sdu_out(s->control, 1) = s->proto;
+        s->control->sdu_submit(s->control);
+        break;
+
+    case BT_SET_PROTOCOL:
+        if (len != 1 || s->state == bt_state_transaction ||
+                        (parameter != BT_HID_PROTO_BOOT &&
+                         parameter != BT_HID_PROTO_REPORT)) {
+            ret = BT_HS_ERR_INVALID_PARAMETER;
+            break;
+        }
+        s->proto = parameter;
+        s->hid.protocol = parameter;
+        ret = BT_HS_SUCCESSFUL;
+        break;
+
+    case BT_GET_IDLE:
+        if (len != 1 || s->state == bt_state_transaction) {
+            ret = BT_HS_ERR_INVALID_PARAMETER;
+            break;
+        }
+        *s->control->sdu_out(s->control, 1) = s->hid.idle;
+        s->control->sdu_submit(s->control);
+        break;
+
+    case BT_SET_IDLE:
+        if (len != 2 || s->state == bt_state_transaction) {
+            ret = BT_HS_ERR_INVALID_PARAMETER;
+            break;
+        }
+
+        s->hid.idle = data[1];
+        /* XXX: Does this generate a handshake? */
+        break;
+
+    case BT_DATC:
+        if (len > BT_HID_MTU || s->state != bt_state_transaction) {
+            ret = BT_HS_ERR_INVALID_PARAMETER;
+            break;
+        }
+        if (s->data_type == BT_DATA_OUTPUT) {
+            memcpy(s->dataout.buffer + s->dataout.len, data + 1, len - 1);
+            s->dataout.len += len - 1;
+        } else {
+            memcpy(s->feature.buffer + s->feature.len, data + 1, len - 1);
+            s->feature.len += len - 1;
+        }
+        if (len < BT_HID_MTU) {
+            bt_hid_out(s);
+            s->state = bt_state_ready;
+        }
+        break;
+
+    default:
+        ret = BT_HS_ERR_UNSUPPORTED_REQUEST;
+    }
+
+    if (ret != -1)
+        bt_hid_send_handshake(s, ret);
+}
+
+static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len)
+{
+    struct bt_hid_device_s *hid = opaque;
+
+    bt_hid_control_transaction(hid, data, len);
+}
+
+static void bt_hid_datain(HIDState *hs)
+{
+    struct bt_hid_device_s *hid =
+        container_of(hs, struct bt_hid_device_s, hid);
+
+    /* If suspended, wake-up and send a wake-up event first.  We might
+     * want to also inspect the input report and ignore event like
+     * mouse movements until a button event occurs.  */
+    if (hid->state == bt_state_suspend) {
+        hid->state = bt_state_ready;
+    }
+
+    if (bt_hid_in(hid) > 0)
+        /* TODO: when in boot-mode precede any Input reports with the ReportID
+         * byte, here and in GetReport/SetReport on the Control channel.  */
+        bt_hid_send_data(hid->interrupt, BT_DATA_INPUT,
+                        hid->datain.buffer, hid->datain.len);
+}
+
+static void bt_hid_interrupt_sdu(void *opaque, const uint8_t *data, int len)
+{
+    struct bt_hid_device_s *hid = opaque;
+
+    if (len > BT_HID_MTU || len < 1)
+        goto bad;
+    if ((data[0] & 3) != BT_DATA_OUTPUT)
+        goto bad;
+    if ((data[0] >> 4) == BT_DATA) {
+        if (hid->intr_state)
+            goto bad;
+
+        hid->data_type = BT_DATA_OUTPUT;
+        hid->intrdataout.len = 0;
+    } else if ((data[0] >> 4) == BT_DATC) {
+        if (!hid->intr_state)
+            goto bad;
+    } else
+        goto bad;
+
+    memcpy(hid->intrdataout.buffer + hid->intrdataout.len, data + 1, len - 1);
+    hid->intrdataout.len += len - 1;
+    hid->intr_state = (len == BT_HID_MTU);
+    if (!hid->intr_state) {
+        memcpy(hid->dataout.buffer, hid->intrdataout.buffer,
+                        hid->dataout.len = hid->intrdataout.len);
+        bt_hid_out(hid);
+    }
+
+    return;
+bad:
+    fprintf(stderr, "%s: bad transaction on Interrupt channel.\n",
+                    __FUNCTION__);
+}
+
+/* "Virtual cable" plug/unplug event.  */
+static void bt_hid_connected_update(struct bt_hid_device_s *hid)
+{
+    int prev = hid->connected;
+
+    hid->connected = hid->control && hid->interrupt;
+
+    /* Stop page-/inquiry-scanning when a host is connected.  */
+    hid->btdev.device.page_scan = !hid->connected;
+    hid->btdev.device.inquiry_scan = !hid->connected;
+
+    if (hid->connected && !prev) {
+        hid_reset(&hid->hid);
+        hid->proto = BT_HID_PROTO_REPORT;
+    }
+
+    /* Should set HIDVirtualCable in SDP (possibly need to check that SDP
+     * isn't destroyed yet, in case we're being called from handle_destroy) */
+}
+
+static void bt_hid_close_control(void *opaque)
+{
+    struct bt_hid_device_s *hid = opaque;
+
+    hid->control = NULL;
+    bt_hid_connected_update(hid);
+}
+
+static void bt_hid_close_interrupt(void *opaque)
+{
+    struct bt_hid_device_s *hid = opaque;
+
+    hid->interrupt = NULL;
+    bt_hid_connected_update(hid);
+}
+
+static int bt_hid_new_control_ch(struct bt_l2cap_device_s *dev,
+                struct bt_l2cap_conn_params_s *params)
+{
+    struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
+
+    if (hid->control)
+        return 1;
+
+    hid->control = params;
+    hid->control->opaque = hid;
+    hid->control->close = bt_hid_close_control;
+    hid->control->sdu_in = bt_hid_control_sdu;
+
+    bt_hid_connected_update(hid);
+
+    return 0;
+}
+
+static int bt_hid_new_interrupt_ch(struct bt_l2cap_device_s *dev,
+                struct bt_l2cap_conn_params_s *params)
+{
+    struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
+
+    if (hid->interrupt)
+        return 1;
+
+    hid->interrupt = params;
+    hid->interrupt->opaque = hid;
+    hid->interrupt->close = bt_hid_close_interrupt;
+    hid->interrupt->sdu_in = bt_hid_interrupt_sdu;
+
+    bt_hid_connected_update(hid);
+
+    return 0;
+}
+
+static void bt_hid_destroy(struct bt_device_s *dev)
+{
+    struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev;
+
+    if (hid->connected)
+        bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG);
+    bt_l2cap_device_done(&hid->btdev);
+
+    hid_free(&hid->hid);
+
+    g_free(hid);
+}
+
+enum peripheral_minor_class {
+    class_other                = 0 << 4,
+    class_keyboard     = 1 << 4,
+    class_pointing     = 2 << 4,
+    class_combo                = 3 << 4,
+};
+
+static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net,
+                                       enum peripheral_minor_class minor)
+{
+    struct bt_hid_device_s *s = g_malloc0(sizeof(*s));
+    uint32_t class =
+            /* Format type */
+            (0 << 0) |
+            /* Device class */
+            (minor << 2) |
+            (5 << 8) |  /* "Peripheral" */
+            /* Service classes */
+            (1 << 13) | /* Limited discoverable mode */
+            (1 << 19);  /* Capturing device (?) */
+
+    bt_l2cap_device_init(&s->btdev, net);
+    bt_l2cap_sdp_init(&s->btdev);
+    bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_CTRL,
+                    BT_HID_MTU, bt_hid_new_control_ch);
+    bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR,
+                    BT_HID_MTU, bt_hid_new_interrupt_ch);
+
+    hid_init(&s->hid, HID_KEYBOARD, bt_hid_datain);
+    s->btdev.device.lmp_name = "BT Keyboard";
+
+    s->btdev.device.handle_destroy = bt_hid_destroy;
+
+    s->btdev.device.class[0] = (class >>  0) & 0xff;
+    s->btdev.device.class[1] = (class >>  8) & 0xff;
+    s->btdev.device.class[2] = (class >> 16) & 0xff;
+
+    return &s->btdev.device;
+}
+
+struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net)
+{
+    return bt_hid_init(net, class_keyboard);
+}
diff --git a/hw/bt/l2cap.c b/hw/bt/l2cap.c
new file mode 100644 (file)
index 0000000..521587a
--- /dev/null
@@ -0,0 +1,1365 @@
+/*
+ * QEMU Bluetooth L2CAP logic.
+ *
+ * Copyright (C) 2008 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "hw/bt.h"
+
+#define L2CAP_CID_MAX  0x100   /* Between 0x40 and 0x10000 */
+
+struct l2cap_instance_s {
+    struct bt_link_s *link;
+    struct bt_l2cap_device_s *dev;
+    int role;
+
+    uint8_t frame_in[65535 + L2CAP_HDR_SIZE] __attribute__ ((aligned (4)));
+    int frame_in_len;
+
+    uint8_t frame_out[65535 + L2CAP_HDR_SIZE] __attribute__ ((aligned (4)));
+    int frame_out_len;
+
+    /* Signalling channel timers.  They exist per-request but we can make
+     * sure we have no more than one outstanding request at any time.  */
+    QEMUTimer *rtx;
+    QEMUTimer *ertx;
+
+    int last_id;
+    int next_id;
+
+    struct l2cap_chan_s {
+        struct bt_l2cap_conn_params_s params;
+
+        void (*frame_in)(struct l2cap_chan_s *chan, uint16_t cid,
+                        const l2cap_hdr *hdr, int len);
+        int mps;
+        int min_mtu;
+
+        struct l2cap_instance_s *l2cap;
+
+        /* Only allocated channels */
+        uint16_t remote_cid;
+#define L2CAP_CFG_INIT 2
+#define L2CAP_CFG_ACC  1
+        int config_req_id; /* TODO: handle outgoing requests generically */
+        int config;
+
+        /* Only connection-oriented channels.  Note: if we allow the tx and
+         * rx traffic to be in different modes at any time, we need two.  */
+        int mode;
+
+        /* Only flow-controlled, connection-oriented channels */
+        uint8_t sdu[65536]; /* TODO: dynamically allocate */
+        int len_cur, len_total;
+        int rexmit;
+        int monitor_timeout;
+        QEMUTimer *monitor_timer;
+        QEMUTimer *retransmission_timer;
+    } *cid[L2CAP_CID_MAX];
+    /* The channel state machine states map as following:
+     * CLOSED           -> !cid[N]
+     * WAIT_CONNECT     -> never occurs
+     * WAIT_CONNECT_RSP -> never occurs
+     * CONFIG           -> cid[N] && config < 3
+     *   WAIT_CONFIG         -> never occurs, cid[N] && config == 0 && !config_r
+     *   WAIT_SEND_CONFIG    -> never occurs, cid[N] && config == 1 && !config_r
+     *   WAIT_CONFIG_REQ_RSP -> cid[N] && config == 0 && config_req_id
+     *   WAIT_CONFIG_RSP     -> cid[N] && config == 1 && config_req_id
+     *   WAIT_CONFIG_REQ     -> cid[N] && config == 2
+     * OPEN             -> cid[N] && config == 3
+     * WAIT_DISCONNECT  -> never occurs
+     */
+
+    struct l2cap_chan_s signalling_ch;
+    struct l2cap_chan_s group_ch;
+};
+
+struct slave_l2cap_instance_s {
+    struct bt_link_s link;     /* Underlying logical link (ACL) */
+    struct l2cap_instance_s l2cap;
+};
+
+struct bt_l2cap_psm_s {
+    int psm;
+    int min_mtu;
+    int (*new_channel)(struct bt_l2cap_device_s *device,
+                    struct bt_l2cap_conn_params_s *params);
+    struct bt_l2cap_psm_s *next;
+};
+
+static const uint16_t l2cap_fcs16_table[256] = {
+    0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
+    0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
+    0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
+    0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
+    0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
+    0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
+    0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
+    0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
+    0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
+    0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
+    0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
+    0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
+    0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
+    0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
+    0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
+    0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
+    0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
+    0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
+    0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
+    0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
+    0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
+    0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
+    0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
+    0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
+    0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
+    0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
+    0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
+    0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
+    0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
+    0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
+    0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
+    0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040,
+};
+
+static uint16_t l2cap_fcs16(const uint8_t *message, int len)
+{
+    uint16_t fcs = 0x0000;
+
+    while (len --)
+#if 0
+    {
+        int i;
+
+        fcs ^= *message ++;
+        for (i = 8; i; -- i)
+            if (fcs & 1)
+                fcs = (fcs >> 1) ^ 0xa001;
+            else
+                fcs = (fcs >> 1);
+    }
+#else
+        fcs = (fcs >> 8) ^ l2cap_fcs16_table[(fcs ^ *message ++) & 0xff];
+#endif
+
+    return fcs;
+}
+
+/* L2CAP layer logic (protocol) */
+
+static void l2cap_retransmission_timer_update(struct l2cap_chan_s *ch)
+{
+#if 0
+    if (ch->mode != L2CAP_MODE_BASIC && ch->rexmit)
+        qemu_mod_timer(ch->retransmission_timer);
+    else
+        qemu_del_timer(ch->retransmission_timer);
+#endif
+}
+
+static void l2cap_monitor_timer_update(struct l2cap_chan_s *ch)
+{
+#if 0
+    if (ch->mode != L2CAP_MODE_BASIC && !ch->rexmit)
+        qemu_mod_timer(ch->monitor_timer);
+    else
+        qemu_del_timer(ch->monitor_timer);
+#endif
+}
+
+static void l2cap_command_reject(struct l2cap_instance_s *l2cap, int id,
+                uint16_t reason, const void *data, int plen)
+{
+    uint8_t *pkt;
+    l2cap_cmd_hdr *hdr;
+    l2cap_cmd_rej *params;
+    uint16_t len;
+
+    reason = cpu_to_le16(reason);
+    len = cpu_to_le16(L2CAP_CMD_REJ_SIZE + plen);
+
+    pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
+                    L2CAP_CMD_HDR_SIZE + L2CAP_CMD_REJ_SIZE + plen);
+    hdr = (void *) (pkt + 0);
+    params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
+
+    hdr->code = L2CAP_COMMAND_REJ;
+    hdr->ident = id;
+    memcpy(&hdr->len, &len, sizeof(hdr->len));
+    memcpy(&params->reason, &reason, sizeof(reason));
+    if (plen)
+       memcpy(pkt + L2CAP_CMD_HDR_SIZE + L2CAP_CMD_REJ_SIZE, data, plen);
+
+    l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
+}
+
+static void l2cap_command_reject_cid(struct l2cap_instance_s *l2cap, int id,
+                uint16_t reason, uint16_t dcid, uint16_t scid)
+{
+    l2cap_cmd_rej_cid params = {
+        .dcid = dcid,
+        .scid = scid,
+    };
+
+    l2cap_command_reject(l2cap, id, reason, &params, L2CAP_CMD_REJ_CID_SIZE);
+}
+
+static void l2cap_connection_response(struct l2cap_instance_s *l2cap,
+                int dcid, int scid, int result, int status)
+{
+    uint8_t *pkt;
+    l2cap_cmd_hdr *hdr;
+    l2cap_conn_rsp *params;
+
+    pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
+                    L2CAP_CMD_HDR_SIZE + L2CAP_CONN_RSP_SIZE);
+    hdr = (void *) (pkt + 0);
+    params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
+
+    hdr->code = L2CAP_CONN_RSP;
+    hdr->ident = l2cap->last_id;
+    hdr->len = cpu_to_le16(L2CAP_CONN_RSP_SIZE);
+
+    params->dcid = cpu_to_le16(dcid);
+    params->scid = cpu_to_le16(scid);
+    params->result = cpu_to_le16(result);
+    params->status = cpu_to_le16(status);
+
+    l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
+}
+
+static void l2cap_configuration_request(struct l2cap_instance_s *l2cap,
+                int dcid, int flag, const uint8_t *data, int len)
+{
+    uint8_t *pkt;
+    l2cap_cmd_hdr *hdr;
+    l2cap_conf_req *params;
+
+    pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
+                    L2CAP_CMD_HDR_SIZE + L2CAP_CONF_REQ_SIZE(len));
+    hdr = (void *) (pkt + 0);
+    params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
+
+    /* TODO: unify the id sequencing */
+    l2cap->last_id = l2cap->next_id;
+    l2cap->next_id = l2cap->next_id == 255 ? 1 : l2cap->next_id + 1;
+
+    hdr->code = L2CAP_CONF_REQ;
+    hdr->ident = l2cap->last_id;
+    hdr->len = cpu_to_le16(L2CAP_CONF_REQ_SIZE(len));
+
+    params->dcid = cpu_to_le16(dcid);
+    params->flags = cpu_to_le16(flag);
+    if (len)
+        memcpy(params->data, data, len);
+
+    l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
+}
+
+static void l2cap_configuration_response(struct l2cap_instance_s *l2cap,
+                int scid, int flag, int result, const uint8_t *data, int len)
+{
+    uint8_t *pkt;
+    l2cap_cmd_hdr *hdr;
+    l2cap_conf_rsp *params;
+
+    pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
+                    L2CAP_CMD_HDR_SIZE + L2CAP_CONF_RSP_SIZE(len));
+    hdr = (void *) (pkt + 0);
+    params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
+
+    hdr->code = L2CAP_CONF_RSP;
+    hdr->ident = l2cap->last_id;
+    hdr->len = cpu_to_le16(L2CAP_CONF_RSP_SIZE(len));
+
+    params->scid = cpu_to_le16(scid);
+    params->flags = cpu_to_le16(flag);
+    params->result = cpu_to_le16(result);
+    if (len)
+        memcpy(params->data, data, len);
+
+    l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
+}
+
+static void l2cap_disconnection_response(struct l2cap_instance_s *l2cap,
+                int dcid, int scid)
+{
+    uint8_t *pkt;
+    l2cap_cmd_hdr *hdr;
+    l2cap_disconn_rsp *params;
+
+    pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
+                    L2CAP_CMD_HDR_SIZE + L2CAP_DISCONN_RSP_SIZE);
+    hdr = (void *) (pkt + 0);
+    params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
+
+    hdr->code = L2CAP_DISCONN_RSP;
+    hdr->ident = l2cap->last_id;
+    hdr->len = cpu_to_le16(L2CAP_DISCONN_RSP_SIZE);
+
+    params->dcid = cpu_to_le16(dcid);
+    params->scid = cpu_to_le16(scid);
+
+    l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
+}
+
+static void l2cap_echo_response(struct l2cap_instance_s *l2cap,
+                const uint8_t *data, int len)
+{
+    uint8_t *pkt;
+    l2cap_cmd_hdr *hdr;
+    uint8_t *params;
+
+    pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
+                    L2CAP_CMD_HDR_SIZE + len);
+    hdr = (void *) (pkt + 0);
+    params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
+
+    hdr->code = L2CAP_ECHO_RSP;
+    hdr->ident = l2cap->last_id;
+    hdr->len = cpu_to_le16(len);
+
+    memcpy(params, data, len);
+
+    l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
+}
+
+static void l2cap_info_response(struct l2cap_instance_s *l2cap, int type,
+                int result, const uint8_t *data, int len)
+{
+    uint8_t *pkt;
+    l2cap_cmd_hdr *hdr;
+    l2cap_info_rsp *params;
+
+    pkt = l2cap->signalling_ch.params.sdu_out(&l2cap->signalling_ch.params,
+                    L2CAP_CMD_HDR_SIZE + L2CAP_INFO_RSP_SIZE + len);
+    hdr = (void *) (pkt + 0);
+    params = (void *) (pkt + L2CAP_CMD_HDR_SIZE);
+
+    hdr->code = L2CAP_INFO_RSP;
+    hdr->ident = l2cap->last_id;
+    hdr->len = cpu_to_le16(L2CAP_INFO_RSP_SIZE + len);
+
+    params->type = cpu_to_le16(type);
+    params->result = cpu_to_le16(result);
+    if (len)
+       memcpy(params->data, data, len);
+
+    l2cap->signalling_ch.params.sdu_submit(&l2cap->signalling_ch.params);
+}
+
+static uint8_t *l2cap_bframe_out(struct bt_l2cap_conn_params_s *parm, int len);
+static void l2cap_bframe_submit(struct bt_l2cap_conn_params_s *parms);
+#if 0
+static uint8_t *l2cap_iframe_out(struct bt_l2cap_conn_params_s *parm, int len);
+static void l2cap_iframe_submit(struct bt_l2cap_conn_params_s *parm);
+#endif
+static void l2cap_bframe_in(struct l2cap_chan_s *ch, uint16_t cid,
+                const l2cap_hdr *hdr, int len);
+static void l2cap_iframe_in(struct l2cap_chan_s *ch, uint16_t cid,
+                const l2cap_hdr *hdr, int len);
+
+static int l2cap_cid_new(struct l2cap_instance_s *l2cap)
+{
+    int i;
+
+    for (i = L2CAP_CID_ALLOC; i < L2CAP_CID_MAX; i ++)
+        if (!l2cap->cid[i])
+            return i;
+
+    return L2CAP_CID_INVALID;
+}
+
+static inline struct bt_l2cap_psm_s *l2cap_psm(
+                struct bt_l2cap_device_s *device, int psm)
+{
+    struct bt_l2cap_psm_s *ret = device->first_psm;
+
+    while (ret && ret->psm != psm)
+        ret = ret->next;
+
+    return ret;
+}
+
+static struct l2cap_chan_s *l2cap_channel_open(struct l2cap_instance_s *l2cap,
+                int psm, int source_cid)
+{
+    struct l2cap_chan_s *ch = NULL;
+    struct bt_l2cap_psm_s *psm_info;
+    int result, status;
+    int cid = l2cap_cid_new(l2cap);
+
+    if (cid) {
+        /* See what the channel is to be used for.. */
+        psm_info = l2cap_psm(l2cap->dev, psm);
+
+        if (psm_info) {
+            /* Device supports this use-case.  */
+            ch = g_malloc0(sizeof(*ch));
+            ch->params.sdu_out = l2cap_bframe_out;
+            ch->params.sdu_submit = l2cap_bframe_submit;
+            ch->frame_in = l2cap_bframe_in;
+            ch->mps = 65536;
+            ch->min_mtu = MAX(48, psm_info->min_mtu);
+            ch->params.remote_mtu = MAX(672, ch->min_mtu);
+            ch->remote_cid = source_cid;
+            ch->mode = L2CAP_MODE_BASIC;
+            ch->l2cap = l2cap;
+
+            /* Does it feel like opening yet another channel though?  */
+            if (!psm_info->new_channel(l2cap->dev, &ch->params)) {
+                l2cap->cid[cid] = ch;
+
+                result = L2CAP_CR_SUCCESS;
+                status = L2CAP_CS_NO_INFO;
+            } else {
+                g_free(ch);
+
+                result = L2CAP_CR_NO_MEM;
+                status = L2CAP_CS_NO_INFO;
+            }
+        } else {
+            result = L2CAP_CR_BAD_PSM;
+            status = L2CAP_CS_NO_INFO;
+        }
+    } else {
+        result = L2CAP_CR_NO_MEM;
+        status = L2CAP_CS_NO_INFO;
+    }
+
+    l2cap_connection_response(l2cap, cid, source_cid, result, status);
+
+    return ch;
+}
+
+static void l2cap_channel_close(struct l2cap_instance_s *l2cap,
+                int cid, int source_cid)
+{
+    struct l2cap_chan_s *ch = NULL;
+
+    /* According to Volume 3, section 6.1.1, pg 1048 of BT Core V2.0, a
+     * connection in CLOSED state still responds with a L2CAP_DisconnectRsp
+     * message on an L2CAP_DisconnectReq event.  */
+    if (unlikely(cid < L2CAP_CID_ALLOC)) {
+        l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL,
+                        cid, source_cid);
+        return;
+    }
+    if (likely(cid >= L2CAP_CID_ALLOC && cid < L2CAP_CID_MAX))
+        ch = l2cap->cid[cid];
+
+    if (likely(ch)) {
+        if (ch->remote_cid != source_cid) {
+            fprintf(stderr, "%s: Ignoring a Disconnection Request with the "
+                            "invalid SCID %04x.\n", __FUNCTION__, source_cid);
+            return;
+        }
+
+        l2cap->cid[cid] = NULL;
+
+        ch->params.close(ch->params.opaque);
+        g_free(ch);
+    }
+
+    l2cap_disconnection_response(l2cap, cid, source_cid);
+}
+
+static void l2cap_channel_config_null(struct l2cap_instance_s *l2cap,
+                struct l2cap_chan_s *ch)
+{
+    l2cap_configuration_request(l2cap, ch->remote_cid, 0, NULL, 0);
+    ch->config_req_id = l2cap->last_id;
+    ch->config &= ~L2CAP_CFG_INIT;
+}
+
+static void l2cap_channel_config_req_event(struct l2cap_instance_s *l2cap,
+                struct l2cap_chan_s *ch)
+{
+    /* Use all default channel options and terminate negotiation.  */
+    l2cap_channel_config_null(l2cap, ch);
+}
+
+static int l2cap_channel_config(struct l2cap_instance_s *l2cap,
+                struct l2cap_chan_s *ch, int flag,
+                const uint8_t *data, int len)
+{
+    l2cap_conf_opt *opt;
+    l2cap_conf_opt_qos *qos;
+    uint32_t val;
+    uint8_t rsp[len];
+    int result = L2CAP_CONF_SUCCESS;
+
+    data = memcpy(rsp, data, len);
+    while (len) {
+        opt = (void *) data;
+
+        if (len < L2CAP_CONF_OPT_SIZE ||
+                        len < L2CAP_CONF_OPT_SIZE + opt->len) {
+            result = L2CAP_CONF_REJECT;
+            break;
+        }
+        data += L2CAP_CONF_OPT_SIZE + opt->len;
+        len -= L2CAP_CONF_OPT_SIZE + opt->len;
+
+        switch (opt->type & 0x7f) {
+        case L2CAP_CONF_MTU:
+            if (opt->len != 2) {
+                result = L2CAP_CONF_REJECT;
+                break;
+            }
+
+            /* MTU */
+            val = le16_to_cpup((void *) opt->val);
+            if (val < ch->min_mtu) {
+                cpu_to_le16w((void *) opt->val, ch->min_mtu);
+                result = L2CAP_CONF_UNACCEPT;
+                break;
+            }
+
+            ch->params.remote_mtu = val;
+            break;
+
+        case L2CAP_CONF_FLUSH_TO:
+            if (opt->len != 2) {
+                result = L2CAP_CONF_REJECT;
+                break;
+            }
+
+            /* Flush Timeout */
+            val = le16_to_cpup((void *) opt->val);
+            if (val < 0x0001) {
+                opt->val[0] = 0xff;
+                opt->val[1] = 0xff;
+                result = L2CAP_CONF_UNACCEPT;
+                break;
+            }
+            break;
+
+        case L2CAP_CONF_QOS:
+            if (opt->len != L2CAP_CONF_OPT_QOS_SIZE) {
+                result = L2CAP_CONF_REJECT;
+                break;
+            }
+            qos = (void *) opt->val;
+
+            /* Flags */
+            val = qos->flags;
+            if (val) {
+                qos->flags = 0;
+                result = L2CAP_CONF_UNACCEPT;
+            }
+
+            /* Service type */
+            val = qos->service_type;
+            if (val != L2CAP_CONF_QOS_BEST_EFFORT &&
+                            val != L2CAP_CONF_QOS_NO_TRAFFIC) {
+                qos->service_type = L2CAP_CONF_QOS_BEST_EFFORT;
+                result = L2CAP_CONF_UNACCEPT;
+            }
+
+            if (val != L2CAP_CONF_QOS_NO_TRAFFIC) {
+                /* XXX: These values should possibly be calculated
+                 * based on LM / baseband properties also.  */
+
+                /* Token rate */
+                val = le32_to_cpu(qos->token_rate);
+                if (val == L2CAP_CONF_QOS_WILDCARD)
+                    qos->token_rate = cpu_to_le32(0x100000);
+
+                /* Token bucket size */
+                val = le32_to_cpu(qos->token_bucket_size);
+                if (val == L2CAP_CONF_QOS_WILDCARD)
+                    qos->token_bucket_size = cpu_to_le32(65500);
+
+                /* Any Peak bandwidth value is correct to return as-is */
+                /* Any Access latency value is correct to return as-is */
+                /* Any Delay variation value is correct to return as-is */
+            }
+            break;
+
+        case L2CAP_CONF_RFC:
+            if (opt->len != 9) {
+                result = L2CAP_CONF_REJECT;
+                break;
+            }
+
+            /* Mode */
+            val = opt->val[0];
+            switch (val) {
+            case L2CAP_MODE_BASIC:
+                ch->mode = val;
+                ch->frame_in = l2cap_bframe_in;
+
+                /* All other parameters shall be ignored */
+                break;
+
+            case L2CAP_MODE_RETRANS:
+            case L2CAP_MODE_FLOWCTL:
+                ch->mode = val;
+                ch->frame_in = l2cap_iframe_in;
+                /* Note: most of these parameters refer to incoming traffic
+                 * so we don't need to save them as long as we can accept
+                 * incoming PDUs at any values of the parameters.  */
+
+                /* TxWindow size */
+                val = opt->val[1];
+                if (val < 1 || val > 32) {
+                    opt->val[1] = 32;
+                    result = L2CAP_CONF_UNACCEPT;
+                    break;
+                }
+
+                /* MaxTransmit */
+                val = opt->val[2];
+                if (val < 1) {
+                    opt->val[2] = 1;
+                    result = L2CAP_CONF_UNACCEPT;
+                    break;
+                }
+
+                /* Remote Retransmission time-out shouldn't affect local
+                 * operation (?) */
+
+                /* The Monitor time-out drives the local Monitor timer (?),
+                 * so save the value.  */
+                val = (opt->val[6] << 8) | opt->val[5];
+                if (val < 30) {
+                    opt->val[5] = 100 & 0xff;
+                    opt->val[6] = 100 >> 8;
+                    result = L2CAP_CONF_UNACCEPT;
+                    break;
+                }
+                ch->monitor_timeout = val;
+                l2cap_monitor_timer_update(ch);
+
+                /* MPS */
+                val = (opt->val[8] << 8) | opt->val[7];
+                if (val < ch->min_mtu) {
+                    opt->val[7] = ch->min_mtu & 0xff;
+                    opt->val[8] = ch->min_mtu >> 8;
+                    result = L2CAP_CONF_UNACCEPT;
+                    break;
+                }
+                ch->mps = val;
+                break;
+
+            default:
+                result = L2CAP_CONF_UNACCEPT;
+                break;
+            }
+            break;
+
+        default:
+            if (!(opt->type >> 7))
+                result = L2CAP_CONF_UNKNOWN;
+            break;
+        }
+
+        if (result != L2CAP_CONF_SUCCESS)
+            break;     /* XXX: should continue? */
+    }
+
+    l2cap_configuration_response(l2cap, ch->remote_cid,
+                    flag, result, rsp, len);
+
+    return result == L2CAP_CONF_SUCCESS && !flag;
+}
+
+static void l2cap_channel_config_req_msg(struct l2cap_instance_s *l2cap,
+                int flag, int cid, const uint8_t *data, int len)
+{
+    struct l2cap_chan_s *ch;
+
+    if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) {
+        l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL,
+                        cid, 0x0000);
+        return;
+    }
+    ch = l2cap->cid[cid];
+
+    /* From OPEN go to WAIT_CONFIG_REQ and from WAIT_CONFIG_REQ_RSP to
+     * WAIT_CONFIG_REQ_RSP.  This is assuming the transition chart for OPEN
+     * on pg 1053, section 6.1.5, volume 3 of BT Core V2.0 has a mistake
+     * and on options-acceptable we go back to OPEN and otherwise to
+     * WAIT_CONFIG_REQ and not the other way.  */
+    ch->config &= ~L2CAP_CFG_ACC;
+
+    if (l2cap_channel_config(l2cap, ch, flag, data, len))
+        /* Go to OPEN or WAIT_CONFIG_RSP */
+        ch->config |= L2CAP_CFG_ACC;
+
+    /* TODO: if the incoming traffic flow control or retransmission mode
+     * changed then we probably need to also generate the
+     * ConfigureChannel_Req event and set the outgoing traffic to the same
+     * mode.  */
+    if (!(ch->config & L2CAP_CFG_INIT) && (ch->config & L2CAP_CFG_ACC) &&
+                    !ch->config_req_id)
+        l2cap_channel_config_req_event(l2cap, ch);
+}
+
+static int l2cap_channel_config_rsp_msg(struct l2cap_instance_s *l2cap,
+                int result, int flag, int cid, const uint8_t *data, int len)
+{
+    struct l2cap_chan_s *ch;
+
+    if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) {
+        l2cap_command_reject_cid(l2cap, l2cap->last_id, L2CAP_REJ_CID_INVAL,
+                        cid, 0x0000);
+        return 0;
+    }
+    ch = l2cap->cid[cid];
+
+    if (ch->config_req_id != l2cap->last_id)
+        return 1;
+    ch->config_req_id = 0;
+
+    if (result == L2CAP_CONF_SUCCESS) {
+        if (!flag)
+            ch->config |= L2CAP_CFG_INIT;
+        else
+            l2cap_channel_config_null(l2cap, ch);
+    } else
+        /* Retry until we succeed */
+        l2cap_channel_config_req_event(l2cap, ch);
+
+    return 0;
+}
+
+static void l2cap_channel_open_req_msg(struct l2cap_instance_s *l2cap,
+                int psm, int source_cid)
+{
+    struct l2cap_chan_s *ch = l2cap_channel_open(l2cap, psm, source_cid);
+
+    if (!ch)
+        return;
+
+    /* Optional */
+    if (!(ch->config & L2CAP_CFG_INIT) && !ch->config_req_id)
+        l2cap_channel_config_req_event(l2cap, ch);
+}
+
+static void l2cap_info(struct l2cap_instance_s *l2cap, int type)
+{
+    uint8_t data[4];
+    int len = 0;
+    int result = L2CAP_IR_SUCCESS;
+
+    switch (type) {
+    case L2CAP_IT_CL_MTU:
+        data[len ++] = l2cap->group_ch.mps & 0xff;
+        data[len ++] = l2cap->group_ch.mps >> 8;
+        break;
+
+    case L2CAP_IT_FEAT_MASK:
+        /* (Prematurely) report Flow control and Retransmission modes.  */
+        data[len ++] = 0x03;
+        data[len ++] = 0x00;
+        data[len ++] = 0x00;
+        data[len ++] = 0x00;
+        break;
+
+    default:
+        result = L2CAP_IR_NOTSUPP;
+    }
+
+    l2cap_info_response(l2cap, type, result, data, len);
+}
+
+static void l2cap_command(struct l2cap_instance_s *l2cap, int code, int id,
+                const uint8_t *params, int len)
+{
+    int err;
+
+#if 0
+    /* TODO: do the IDs really have to be in sequence?  */
+    if (!id || (id != l2cap->last_id && id != l2cap->next_id)) {
+        fprintf(stderr, "%s: out of sequence command packet ignored.\n",
+                        __FUNCTION__);
+        return;
+    }
+#else
+    l2cap->next_id = id;
+#endif
+    if (id == l2cap->next_id) {
+        l2cap->last_id = l2cap->next_id;
+        l2cap->next_id = l2cap->next_id == 255 ? 1 : l2cap->next_id + 1;
+    } else {
+        /* TODO: Need to re-send the same response, without re-executing
+         * the corresponding command!  */
+    }
+
+    switch (code) {
+    case L2CAP_COMMAND_REJ:
+        if (unlikely(len != 2 && len != 4 && len != 6)) {
+            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+            goto reject;
+        }
+
+        /* We never issue commands other than Command Reject currently.  */
+        fprintf(stderr, "%s: stray Command Reject (%02x, %04x) "
+                        "packet, ignoring.\n", __FUNCTION__, id,
+                        le16_to_cpu(((l2cap_cmd_rej *) params)->reason));
+        break;
+
+    case L2CAP_CONN_REQ:
+        if (unlikely(len != L2CAP_CONN_REQ_SIZE)) {
+            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+            goto reject;
+        }
+
+        l2cap_channel_open_req_msg(l2cap,
+                        le16_to_cpu(((l2cap_conn_req *) params)->psm),
+                        le16_to_cpu(((l2cap_conn_req *) params)->scid));
+        break;
+
+    case L2CAP_CONN_RSP:
+        if (unlikely(len != L2CAP_CONN_RSP_SIZE)) {
+            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+            goto reject;
+        }
+
+        /* We never issue Connection Requests currently. TODO  */
+        fprintf(stderr, "%s: unexpected Connection Response (%02x) "
+                        "packet, ignoring.\n", __FUNCTION__, id);
+        break;
+
+    case L2CAP_CONF_REQ:
+        if (unlikely(len < L2CAP_CONF_REQ_SIZE(0))) {
+            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+            goto reject;
+        }
+
+        l2cap_channel_config_req_msg(l2cap,
+                        le16_to_cpu(((l2cap_conf_req *) params)->flags) & 1,
+                        le16_to_cpu(((l2cap_conf_req *) params)->dcid),
+                        ((l2cap_conf_req *) params)->data,
+                        len - L2CAP_CONF_REQ_SIZE(0));
+        break;
+
+    case L2CAP_CONF_RSP:
+        if (unlikely(len < L2CAP_CONF_RSP_SIZE(0))) {
+            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+            goto reject;
+        }
+
+        if (l2cap_channel_config_rsp_msg(l2cap,
+                        le16_to_cpu(((l2cap_conf_rsp *) params)->result),
+                        le16_to_cpu(((l2cap_conf_rsp *) params)->flags) & 1,
+                        le16_to_cpu(((l2cap_conf_rsp *) params)->scid),
+                        ((l2cap_conf_rsp *) params)->data,
+                        len - L2CAP_CONF_RSP_SIZE(0)))
+            fprintf(stderr, "%s: unexpected Configure Response (%02x) "
+                            "packet, ignoring.\n", __FUNCTION__, id);
+        break;
+
+    case L2CAP_DISCONN_REQ:
+        if (unlikely(len != L2CAP_DISCONN_REQ_SIZE)) {
+            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+            goto reject;
+        }
+
+        l2cap_channel_close(l2cap,
+                        le16_to_cpu(((l2cap_disconn_req *) params)->dcid),
+                        le16_to_cpu(((l2cap_disconn_req *) params)->scid));
+        break;
+
+    case L2CAP_DISCONN_RSP:
+        if (unlikely(len != L2CAP_DISCONN_RSP_SIZE)) {
+            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+            goto reject;
+        }
+
+        /* We never issue Disconnection Requests currently. TODO  */
+        fprintf(stderr, "%s: unexpected Disconnection Response (%02x) "
+                        "packet, ignoring.\n", __FUNCTION__, id);
+        break;
+
+    case L2CAP_ECHO_REQ:
+        l2cap_echo_response(l2cap, params, len);
+        break;
+
+    case L2CAP_ECHO_RSP:
+        /* We never issue Echo Requests currently. TODO  */
+        fprintf(stderr, "%s: unexpected Echo Response (%02x) "
+                        "packet, ignoring.\n", __FUNCTION__, id);
+        break;
+
+    case L2CAP_INFO_REQ:
+        if (unlikely(len != L2CAP_INFO_REQ_SIZE)) {
+            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+            goto reject;
+        }
+
+        l2cap_info(l2cap, le16_to_cpu(((l2cap_info_req *) params)->type));
+        break;
+
+    case L2CAP_INFO_RSP:
+        if (unlikely(len != L2CAP_INFO_RSP_SIZE)) {
+            err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+            goto reject;
+        }
+
+        /* We never issue Information Requests currently. TODO  */
+        fprintf(stderr, "%s: unexpected Information Response (%02x) "
+                        "packet, ignoring.\n", __FUNCTION__, id);
+        break;
+
+    default:
+        err = L2CAP_REJ_CMD_NOT_UNDERSTOOD;
+    reject:
+        l2cap_command_reject(l2cap, id, err, 0, 0);
+        break;
+    }
+}
+
+static void l2cap_rexmit_enable(struct l2cap_chan_s *ch, int enable)
+{
+    ch->rexmit = enable;
+
+    l2cap_retransmission_timer_update(ch);
+    l2cap_monitor_timer_update(ch);
+}
+
+/* Command frame SDU */
+static void l2cap_cframe_in(void *opaque, const uint8_t *data, int len)
+{
+    struct l2cap_instance_s *l2cap = opaque;
+    const l2cap_cmd_hdr *hdr;
+    int clen;
+
+    while (len) {
+        hdr = (void *) data;
+        if (len < L2CAP_CMD_HDR_SIZE)
+            /* TODO: signal an error */
+            return;
+        len -= L2CAP_CMD_HDR_SIZE;
+        data += L2CAP_CMD_HDR_SIZE;
+
+        clen = le16_to_cpu(hdr->len);
+        if (len < clen) {
+            l2cap_command_reject(l2cap, hdr->ident,
+                            L2CAP_REJ_CMD_NOT_UNDERSTOOD, 0, 0);
+            break;
+        }
+
+        l2cap_command(l2cap, hdr->code, hdr->ident, data, clen);
+        len -= clen;
+        data += clen;
+    }
+}
+
+/* Group frame SDU */
+static void l2cap_gframe_in(void *opaque, const uint8_t *data, int len)
+{
+}
+
+/* Supervisory frame */
+static void l2cap_sframe_in(struct l2cap_chan_s *ch, uint16_t ctrl)
+{
+}
+
+/* Basic L2CAP mode Information frame */
+static void l2cap_bframe_in(struct l2cap_chan_s *ch, uint16_t cid,
+                const l2cap_hdr *hdr, int len)
+{
+    /* We have a full SDU, no further processing */
+    ch->params.sdu_in(ch->params.opaque, hdr->data, len);
+}
+
+/* Flow Control and Retransmission mode frame */
+static void l2cap_iframe_in(struct l2cap_chan_s *ch, uint16_t cid,
+                const l2cap_hdr *hdr, int len)
+{
+    uint16_t fcs = le16_to_cpup((void *) (hdr->data + len - 2));
+
+    if (len < 4)
+        goto len_error;
+    if (l2cap_fcs16((const uint8_t *) hdr, L2CAP_HDR_SIZE + len - 2) != fcs)
+        goto fcs_error;
+
+    if ((hdr->data[0] >> 7) == ch->rexmit)
+        l2cap_rexmit_enable(ch, !(hdr->data[0] >> 7));
+
+    if (hdr->data[0] & 1) {
+        if (len != 4) {
+            /* TODO: Signal an error? */
+            return;
+        }
+        l2cap_sframe_in(ch, le16_to_cpup((void *) hdr->data));
+        return;
+    }
+
+    switch (hdr->data[1] >> 6) {       /* SAR */
+    case L2CAP_SAR_NO_SEG:
+        if (ch->len_total)
+            goto seg_error;
+        if (len - 4 > ch->mps)
+            goto len_error;
+
+        ch->params.sdu_in(ch->params.opaque, hdr->data + 2, len - 4);
+        break;
+
+    case L2CAP_SAR_START:
+        if (ch->len_total || len < 6)
+            goto seg_error;
+        if (len - 6 > ch->mps)
+            goto len_error;
+
+        ch->len_total = le16_to_cpup((void *) (hdr->data + 2));
+        if (len >= 6 + ch->len_total)
+            goto seg_error;
+
+        ch->len_cur = len - 6;
+        memcpy(ch->sdu, hdr->data + 4, ch->len_cur);
+        break;
+
+    case L2CAP_SAR_END:
+        if (!ch->len_total || ch->len_cur + len - 4 < ch->len_total)
+            goto seg_error;
+        if (len - 4 > ch->mps)
+            goto len_error;
+
+        memcpy(ch->sdu + ch->len_cur, hdr->data + 2, len - 4);
+        ch->params.sdu_in(ch->params.opaque, ch->sdu, ch->len_total);
+        break;
+
+    case L2CAP_SAR_CONT:
+        if (!ch->len_total || ch->len_cur + len - 4 >= ch->len_total)
+            goto seg_error;
+        if (len - 4 > ch->mps)
+            goto len_error;
+
+        memcpy(ch->sdu + ch->len_cur, hdr->data + 2, len - 4);
+        ch->len_cur += len - 4;
+        break;
+
+    seg_error:
+    len_error: /* TODO */
+    fcs_error: /* TODO */
+        ch->len_cur = 0;
+        ch->len_total = 0;
+        break;
+    }
+}
+
+static void l2cap_frame_in(struct l2cap_instance_s *l2cap,
+                const l2cap_hdr *frame)
+{
+    uint16_t cid = le16_to_cpu(frame->cid);
+    uint16_t len = le16_to_cpu(frame->len);
+
+    if (unlikely(cid >= L2CAP_CID_MAX || !l2cap->cid[cid])) {
+        fprintf(stderr, "%s: frame addressed to a non-existent L2CAP "
+                        "channel %04x received.\n", __FUNCTION__, cid);
+        return;
+    }
+
+    l2cap->cid[cid]->frame_in(l2cap->cid[cid], cid, frame, len);
+}
+
+/* "Recombination" */
+static void l2cap_pdu_in(struct l2cap_instance_s *l2cap,
+                const uint8_t *data, int len)
+{
+    const l2cap_hdr *hdr = (void *) l2cap->frame_in;
+
+    if (unlikely(len + l2cap->frame_in_len > sizeof(l2cap->frame_in))) {
+        if (l2cap->frame_in_len < sizeof(l2cap->frame_in)) {
+            memcpy(l2cap->frame_in + l2cap->frame_in_len, data,
+                            sizeof(l2cap->frame_in) - l2cap->frame_in_len);
+            l2cap->frame_in_len = sizeof(l2cap->frame_in);
+            /* TODO: truncate */
+            l2cap_frame_in(l2cap, hdr);
+        }
+
+        return;
+    }
+
+    memcpy(l2cap->frame_in + l2cap->frame_in_len, data, len);
+    l2cap->frame_in_len += len;
+
+    if (len >= L2CAP_HDR_SIZE)
+        if (len >= L2CAP_HDR_SIZE + le16_to_cpu(hdr->len))
+            l2cap_frame_in(l2cap, hdr);
+            /* There is never a start of a new PDU in the same ACL packet, so
+             * no need to memmove the remaining payload and loop.  */
+}
+
+static inline uint8_t *l2cap_pdu_out(struct l2cap_instance_s *l2cap,
+                uint16_t cid, uint16_t len)
+{
+    l2cap_hdr *hdr = (void *) l2cap->frame_out;
+
+    l2cap->frame_out_len = len + L2CAP_HDR_SIZE;
+
+    hdr->cid = cpu_to_le16(cid);
+    hdr->len = cpu_to_le16(len);
+
+    return l2cap->frame_out + L2CAP_HDR_SIZE;
+}
+
+static inline void l2cap_pdu_submit(struct l2cap_instance_s *l2cap)
+{
+    /* TODO: Fragmentation */
+    (l2cap->role ?
+     l2cap->link->slave->lmp_acl_data : l2cap->link->host->lmp_acl_resp)
+            (l2cap->link, l2cap->frame_out, 1, l2cap->frame_out_len);
+}
+
+static uint8_t *l2cap_bframe_out(struct bt_l2cap_conn_params_s *parm, int len)
+{
+    struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parm;
+
+    if (len > chan->params.remote_mtu) {
+        fprintf(stderr, "%s: B-Frame for CID %04x longer than %i octets.\n",
+                        __FUNCTION__,
+                        chan->remote_cid, chan->params.remote_mtu);
+        exit(-1);
+    }
+
+    return l2cap_pdu_out(chan->l2cap, chan->remote_cid, len);
+}
+
+static void l2cap_bframe_submit(struct bt_l2cap_conn_params_s *parms)
+{
+    struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parms;
+
+    l2cap_pdu_submit(chan->l2cap);
+}
+
+#if 0
+/* Stub: Only used if an emulated device requests outgoing flow control */
+static uint8_t *l2cap_iframe_out(struct bt_l2cap_conn_params_s *parm, int len)
+{
+    struct l2cap_chan_s *chan = (struct l2cap_chan_s *) parm;
+
+    if (len > chan->params.remote_mtu) {
+        /* TODO: slice into segments and queue each segment as a separate
+         * I-Frame in a FIFO of I-Frames, local to the CID.  */
+    } else {
+        /* TODO: add to the FIFO of I-Frames, local to the CID.  */
+        /* Possibly we need to return a pointer to a contiguous buffer
+         * for now and then memcpy from it into FIFOs in l2cap_iframe_submit
+         * while segmenting at the same time.  */
+    }
+    return 0;
+}
+
+static void l2cap_iframe_submit(struct bt_l2cap_conn_params_s *parm)
+{
+    /* TODO: If flow control indicates clear to send, start submitting the
+     * invidual I-Frames from the FIFO, but don't remove them from there.
+     * Kick the appropriate timer until we get an S-Frame, and only then
+     * remove from FIFO or resubmit and re-kick the timer if the timer
+     * expired.  */
+}
+#endif
+
+static void l2cap_init(struct l2cap_instance_s *l2cap,
+                struct bt_link_s *link, int role)
+{
+    l2cap->link = link;
+    l2cap->role = role;
+    l2cap->dev = (struct bt_l2cap_device_s *)
+            (role ? link->host : link->slave);
+
+    l2cap->next_id = 1;
+
+    /* Establish the signalling channel */
+    l2cap->signalling_ch.params.sdu_in = l2cap_cframe_in;
+    l2cap->signalling_ch.params.sdu_out = l2cap_bframe_out;
+    l2cap->signalling_ch.params.sdu_submit = l2cap_bframe_submit;
+    l2cap->signalling_ch.params.opaque = l2cap;
+    l2cap->signalling_ch.params.remote_mtu = 48;
+    l2cap->signalling_ch.remote_cid = L2CAP_CID_SIGNALLING;
+    l2cap->signalling_ch.frame_in = l2cap_bframe_in;
+    l2cap->signalling_ch.mps = 65536;
+    l2cap->signalling_ch.min_mtu = 48;
+    l2cap->signalling_ch.mode = L2CAP_MODE_BASIC;
+    l2cap->signalling_ch.l2cap = l2cap;
+    l2cap->cid[L2CAP_CID_SIGNALLING] = &l2cap->signalling_ch;
+
+    /* Establish the connection-less data channel */
+    l2cap->group_ch.params.sdu_in = l2cap_gframe_in;
+    l2cap->group_ch.params.opaque = l2cap;
+    l2cap->group_ch.frame_in = l2cap_bframe_in;
+    l2cap->group_ch.mps = 65533;
+    l2cap->group_ch.l2cap = l2cap;
+    l2cap->group_ch.remote_cid = L2CAP_CID_INVALID;
+    l2cap->cid[L2CAP_CID_GROUP] = &l2cap->group_ch;
+}
+
+static void l2cap_teardown(struct l2cap_instance_s *l2cap, int send_disconnect)
+{
+    int cid;
+
+    /* Don't send DISCONNECT if we are currently handling a DISCONNECT
+     * sent from the other side.  */
+    if (send_disconnect) {
+        if (l2cap->role)
+            l2cap->dev->device.lmp_disconnect_slave(l2cap->link);
+            /* l2cap->link is invalid from now on.  */
+        else
+            l2cap->dev->device.lmp_disconnect_master(l2cap->link);
+    }
+
+    for (cid = L2CAP_CID_ALLOC; cid < L2CAP_CID_MAX; cid ++)
+        if (l2cap->cid[cid]) {
+            l2cap->cid[cid]->params.close(l2cap->cid[cid]->params.opaque);
+            g_free(l2cap->cid[cid]);
+        }
+
+    if (l2cap->role)
+        g_free(l2cap);
+    else
+        g_free(l2cap->link);
+}
+
+/* L2CAP glue to lower layers in bluetooth stack (LMP) */
+
+static void l2cap_lmp_connection_request(struct bt_link_s *link)
+{
+    struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->slave;
+    struct slave_l2cap_instance_s *l2cap;
+
+    /* Always accept - we only get called if (dev->device->page_scan).  */
+
+    l2cap = g_malloc0(sizeof(struct slave_l2cap_instance_s));
+    l2cap->link.slave = &dev->device;
+    l2cap->link.host = link->host;
+    l2cap_init(&l2cap->l2cap, &l2cap->link, 0);
+
+    /* Always at the end */
+    link->host->reject_reason = 0;
+    link->host->lmp_connection_complete(&l2cap->link);
+}
+
+/* Stub */
+static void l2cap_lmp_connection_complete(struct bt_link_s *link)
+{
+    struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host;
+    struct l2cap_instance_s *l2cap;
+
+    if (dev->device.reject_reason) {
+        /* Signal to upper layer */
+        return;
+    }
+
+    l2cap = g_malloc0(sizeof(struct l2cap_instance_s));
+    l2cap_init(l2cap, link, 1);
+
+    link->acl_mode = acl_active;
+
+    /* Signal to upper layer */
+}
+
+/* Stub */
+static void l2cap_lmp_disconnect_host(struct bt_link_s *link)
+{
+    struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host;
+    struct l2cap_instance_s *l2cap =
+            /* TODO: Retrieve from upper layer */ (void *) dev;
+
+    /* Signal to upper layer */
+
+    l2cap_teardown(l2cap, 0);
+}
+
+static void l2cap_lmp_disconnect_slave(struct bt_link_s *link)
+{
+    struct slave_l2cap_instance_s *l2cap =
+            (struct slave_l2cap_instance_s *) link;
+
+    l2cap_teardown(&l2cap->l2cap, 0);
+}
+
+static void l2cap_lmp_acl_data_slave(struct bt_link_s *link,
+                const uint8_t *data, int start, int len)
+{
+    struct slave_l2cap_instance_s *l2cap =
+            (struct slave_l2cap_instance_s *) link;
+
+    if (start)
+        l2cap->l2cap.frame_in_len = 0;
+
+    l2cap_pdu_in(&l2cap->l2cap, data, len);
+}
+
+/* Stub */
+static void l2cap_lmp_acl_data_host(struct bt_link_s *link,
+                const uint8_t *data, int start, int len)
+{
+    struct bt_l2cap_device_s *dev = (struct bt_l2cap_device_s *) link->host;
+    struct l2cap_instance_s *l2cap =
+            /* TODO: Retrieve from upper layer */ (void *) dev;
+
+    if (start)
+        l2cap->frame_in_len = 0;
+
+    l2cap_pdu_in(l2cap, data, len);
+}
+
+static void l2cap_dummy_destroy(struct bt_device_s *dev)
+{
+    struct bt_l2cap_device_s *l2cap_dev = (struct bt_l2cap_device_s *) dev;
+
+    bt_l2cap_device_done(l2cap_dev);
+}
+
+void bt_l2cap_device_init(struct bt_l2cap_device_s *dev,
+                struct bt_scatternet_s *net)
+{
+    bt_device_init(&dev->device, net);
+
+    dev->device.lmp_connection_request = l2cap_lmp_connection_request;
+    dev->device.lmp_connection_complete = l2cap_lmp_connection_complete;
+    dev->device.lmp_disconnect_master = l2cap_lmp_disconnect_host;
+    dev->device.lmp_disconnect_slave = l2cap_lmp_disconnect_slave;
+    dev->device.lmp_acl_data = l2cap_lmp_acl_data_slave;
+    dev->device.lmp_acl_resp = l2cap_lmp_acl_data_host;
+
+    dev->device.handle_destroy = l2cap_dummy_destroy;
+}
+
+void bt_l2cap_device_done(struct bt_l2cap_device_s *dev)
+{
+    bt_device_done(&dev->device);
+
+    /* Should keep a list of all instances and go through it and
+     * invoke l2cap_teardown() for each.  */
+}
+
+void bt_l2cap_psm_register(struct bt_l2cap_device_s *dev, int psm, int min_mtu,
+                int (*new_channel)(struct bt_l2cap_device_s *dev,
+                        struct bt_l2cap_conn_params_s *params))
+{
+    struct bt_l2cap_psm_s *new_psm = l2cap_psm(dev, psm);
+
+    if (new_psm) {
+        fprintf(stderr, "%s: PSM %04x already registered for device `%s'.\n",
+                        __FUNCTION__, psm, dev->device.lmp_name);
+        exit(-1);
+    }
+
+    new_psm = g_malloc0(sizeof(*new_psm));
+    new_psm->psm = psm;
+    new_psm->min_mtu = min_mtu;
+    new_psm->new_channel = new_channel;
+    new_psm->next = dev->first_psm;
+    dev->first_psm = new_psm;
+}
diff --git a/hw/bt/sdp.c b/hw/bt/sdp.c
new file mode 100644 (file)
index 0000000..218e075
--- /dev/null
@@ -0,0 +1,967 @@
+/*
+ * Service Discover Protocol server for QEMU L2CAP devices
+ *
+ * Copyright (C) 2008 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu-common.h"
+#include "hw/bt.h"
+
+struct bt_l2cap_sdp_state_s {
+    struct bt_l2cap_conn_params_s *channel;
+
+    struct sdp_service_record_s {
+        int match;
+
+        int *uuid;
+        int uuids;
+        struct sdp_service_attribute_s {
+            int match;
+
+            int attribute_id;
+            int len;
+            void *pair;
+        } *attribute_list;
+        int attributes;
+    } *service_list;
+    int services;
+};
+
+static ssize_t sdp_datalen(const uint8_t **element, ssize_t *left)
+{
+    size_t len = *(*element) ++ & SDP_DSIZE_MASK;
+
+    if (!*left)
+        return -1;
+    (*left) --;
+
+    if (len < SDP_DSIZE_NEXT1)
+        return 1 << len;
+    else if (len == SDP_DSIZE_NEXT1) {
+        if (*left < 1)
+            return -1;
+        (*left) --;
+
+        return *(*element) ++;
+    } else if (len == SDP_DSIZE_NEXT2) {
+        if (*left < 2)
+            return -1;
+        (*left) -= 2;
+
+        len = (*(*element) ++) << 8;
+        return len | (*(*element) ++);
+    } else {
+        if (*left < 4)
+            return -1;
+        (*left) -= 4;
+
+        len = (*(*element) ++) << 24;
+        len |= (*(*element) ++) << 16;
+        len |= (*(*element) ++) << 8;
+        return len | (*(*element) ++);
+    }
+}
+
+static const uint8_t bt_base_uuid[12] = {
+    0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+};
+
+static int sdp_uuid_match(struct sdp_service_record_s *record,
+                const uint8_t *uuid, ssize_t datalen)
+{
+    int *lo, hi, val;
+
+    if (datalen == 16 || datalen == 4) {
+        if (datalen == 16 && memcmp(uuid + 4, bt_base_uuid, 12))
+            return 0;
+
+        if (uuid[0] | uuid[1])
+            return 0;
+        uuid += 2;
+    }
+
+    val = (uuid[0] << 8) | uuid[1];
+    lo = record->uuid;
+    hi = record->uuids;
+    while (hi >>= 1)
+        if (lo[hi] <= val)
+            lo += hi;
+
+    return *lo == val;
+}
+
+#define CONTINUATION_PARAM_SIZE        (1 + sizeof(int))
+#define MAX_PDU_OUT_SIZE       96      /* Arbitrary */
+#define PDU_HEADER_SIZE                5
+#define MAX_RSP_PARAM_SIZE     (MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE - \
+                CONTINUATION_PARAM_SIZE)
+
+static int sdp_svc_match(struct bt_l2cap_sdp_state_s *sdp,
+                const uint8_t **req, ssize_t *len)
+{
+    size_t datalen;
+    int i;
+
+    if ((**req & ~SDP_DSIZE_MASK) != SDP_DTYPE_UUID)
+        return 1;
+
+    datalen = sdp_datalen(req, len);
+    if (datalen != 2 && datalen != 4 && datalen != 16)
+        return 1;
+
+    for (i = 0; i < sdp->services; i ++)
+        if (sdp_uuid_match(&sdp->service_list[i], *req, datalen))
+            sdp->service_list[i].match = 1;
+
+    (*req) += datalen;
+    (*len) -= datalen;
+
+    return 0;
+}
+
+static ssize_t sdp_svc_search(struct bt_l2cap_sdp_state_s *sdp,
+                uint8_t *rsp, const uint8_t *req, ssize_t len)
+{
+    ssize_t seqlen;
+    int i, count, start, end, max;
+    int32_t handle;
+
+    /* Perform the search */
+    for (i = 0; i < sdp->services; i ++)
+        sdp->service_list[i].match = 0;
+
+    if (len < 1)
+        return -SDP_INVALID_SYNTAX;
+    if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) {
+        seqlen = sdp_datalen(&req, &len);
+        if (seqlen < 3 || len < seqlen)
+            return -SDP_INVALID_SYNTAX;
+        len -= seqlen;
+
+        while (seqlen)
+            if (sdp_svc_match(sdp, &req, &seqlen))
+                return -SDP_INVALID_SYNTAX;
+    } else if (sdp_svc_match(sdp, &req, &seqlen))
+        return -SDP_INVALID_SYNTAX;
+
+    if (len < 3)
+        return -SDP_INVALID_SYNTAX;
+    max = (req[0] << 8) | req[1];
+    req += 2;
+    len -= 2;
+
+    if (*req) {
+        if (len <= sizeof(int))
+            return -SDP_INVALID_SYNTAX;
+        len -= sizeof(int);
+        memcpy(&start, req + 1, sizeof(int));
+    } else
+        start = 0;
+
+    if (len > 1)
+        return -SDP_INVALID_SYNTAX;
+
+    /* Output the results */
+    len = 4;
+    count = 0;
+    end = start;
+    for (i = 0; i < sdp->services; i ++)
+        if (sdp->service_list[i].match) {
+            if (count >= start && count < max && len + 4 < MAX_RSP_PARAM_SIZE) {
+                handle = i;
+                memcpy(rsp + len, &handle, 4);
+                len += 4;
+                end = count + 1;
+            }
+
+            count ++;
+        }
+
+    rsp[0] = count >> 8;
+    rsp[1] = count & 0xff;
+    rsp[2] = (end - start) >> 8;
+    rsp[3] = (end - start) & 0xff;
+
+    if (end < count) {
+        rsp[len ++] = sizeof(int);
+        memcpy(rsp + len, &end, sizeof(int));
+        len += 4;
+    } else
+        rsp[len ++] = 0;
+
+    return len;
+}
+
+static int sdp_attr_match(struct sdp_service_record_s *record,
+                const uint8_t **req, ssize_t *len)
+{
+    int i, start, end;
+
+    if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) {
+        (*req) ++;
+        if (*len < 3)
+            return 1;
+
+        start = (*(*req) ++) << 8;
+        start |= *(*req) ++;
+        end = start;
+        *len -= 3;
+    } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) {
+        (*req) ++;
+        if (*len < 5)
+            return 1;
+
+        start = (*(*req) ++) << 8;
+        start |= *(*req) ++;
+        end = (*(*req) ++) << 8;
+        end |= *(*req) ++;
+        *len -= 5;
+    } else
+        return 1;
+
+    for (i = 0; i < record->attributes; i ++)
+        if (record->attribute_list[i].attribute_id >= start &&
+                        record->attribute_list[i].attribute_id <= end)
+            record->attribute_list[i].match = 1;
+
+    return 0;
+}
+
+static ssize_t sdp_attr_get(struct bt_l2cap_sdp_state_s *sdp,
+                uint8_t *rsp, const uint8_t *req, ssize_t len)
+{
+    ssize_t seqlen;
+    int i, start, end, max;
+    int32_t handle;
+    struct sdp_service_record_s *record;
+    uint8_t *lst;
+
+    /* Perform the search */
+    if (len < 7)
+        return -SDP_INVALID_SYNTAX;
+    memcpy(&handle, req, 4);
+    req += 4;
+    len -= 4;
+
+    if (handle < 0 || handle > sdp->services)
+        return -SDP_INVALID_RECORD_HANDLE;
+    record = &sdp->service_list[handle];
+
+    for (i = 0; i < record->attributes; i ++)
+        record->attribute_list[i].match = 0;
+
+    max = (req[0] << 8) | req[1];
+    req += 2;
+    len -= 2;
+    if (max < 0x0007)
+        return -SDP_INVALID_SYNTAX;
+
+    if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) {
+        seqlen = sdp_datalen(&req, &len);
+        if (seqlen < 3 || len < seqlen)
+            return -SDP_INVALID_SYNTAX;
+        len -= seqlen;
+
+        while (seqlen)
+            if (sdp_attr_match(record, &req, &seqlen))
+                return -SDP_INVALID_SYNTAX;
+    } else if (sdp_attr_match(record, &req, &seqlen))
+        return -SDP_INVALID_SYNTAX;
+
+    if (len < 1)
+        return -SDP_INVALID_SYNTAX;
+
+    if (*req) {
+        if (len <= sizeof(int))
+            return -SDP_INVALID_SYNTAX;
+        len -= sizeof(int);
+        memcpy(&start, req + 1, sizeof(int));
+    } else
+        start = 0;
+
+    if (len > 1)
+        return -SDP_INVALID_SYNTAX;
+
+    /* Output the results */
+    lst = rsp + 2;
+    max = MIN(max, MAX_RSP_PARAM_SIZE);
+    len = 3 - start;
+    end = 0;
+    for (i = 0; i < record->attributes; i ++)
+        if (record->attribute_list[i].match) {
+            if (len >= 0 && len + record->attribute_list[i].len < max) {
+                memcpy(lst + len, record->attribute_list[i].pair,
+                                record->attribute_list[i].len);
+                end = len + record->attribute_list[i].len;
+            }
+            len += record->attribute_list[i].len;
+        }
+    if (0 >= start) {
+       lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2;
+       lst[1] = (len + start - 3) >> 8;
+       lst[2] = (len + start - 3) & 0xff;
+    }
+
+    rsp[0] = end >> 8;
+    rsp[1] = end & 0xff;
+
+    if (end < len) {
+        len = end + start;
+        lst[end ++] = sizeof(int);
+        memcpy(lst + end, &len, sizeof(int));
+        end += sizeof(int);
+    } else
+        lst[end ++] = 0;
+
+    return end + 2;
+}
+
+static int sdp_svc_attr_match(struct bt_l2cap_sdp_state_s *sdp,
+                const uint8_t **req, ssize_t *len)
+{
+    int i, j, start, end;
+    struct sdp_service_record_s *record;
+
+    if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_2)) {
+        (*req) ++;
+        if (*len < 3)
+            return 1;
+
+        start = (*(*req) ++) << 8;
+        start |= *(*req) ++;
+        end = start;
+        *len -= 3;
+    } else if (**req == (SDP_DTYPE_UINT | SDP_DSIZE_4)) {
+        (*req) ++;
+        if (*len < 5)
+            return 1;
+
+        start = (*(*req) ++) << 8;
+        start |= *(*req) ++;
+        end = (*(*req) ++) << 8;
+        end |= *(*req) ++;
+        *len -= 5;
+    } else
+        return 1;
+
+    for (i = 0; i < sdp->services; i ++)
+        if ((record = &sdp->service_list[i])->match)
+            for (j = 0; j < record->attributes; j ++)
+                if (record->attribute_list[j].attribute_id >= start &&
+                                record->attribute_list[j].attribute_id <= end)
+                    record->attribute_list[j].match = 1;
+
+    return 0;
+}
+
+static ssize_t sdp_svc_search_attr_get(struct bt_l2cap_sdp_state_s *sdp,
+                uint8_t *rsp, const uint8_t *req, ssize_t len)
+{
+    ssize_t seqlen;
+    int i, j, start, end, max;
+    struct sdp_service_record_s *record;
+    uint8_t *lst;
+
+    /* Perform the search */
+    for (i = 0; i < sdp->services; i ++) {
+        sdp->service_list[i].match = 0;
+            for (j = 0; j < sdp->service_list[i].attributes; j ++)
+                sdp->service_list[i].attribute_list[j].match = 0;
+    }
+
+    if (len < 1)
+        return -SDP_INVALID_SYNTAX;
+    if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) {
+        seqlen = sdp_datalen(&req, &len);
+        if (seqlen < 3 || len < seqlen)
+            return -SDP_INVALID_SYNTAX;
+        len -= seqlen;
+
+        while (seqlen)
+            if (sdp_svc_match(sdp, &req, &seqlen))
+                return -SDP_INVALID_SYNTAX;
+    } else if (sdp_svc_match(sdp, &req, &seqlen))
+        return -SDP_INVALID_SYNTAX;
+
+    if (len < 3)
+        return -SDP_INVALID_SYNTAX;
+    max = (req[0] << 8) | req[1];
+    req += 2;
+    len -= 2;
+    if (max < 0x0007)
+        return -SDP_INVALID_SYNTAX;
+
+    if ((*req & ~SDP_DSIZE_MASK) == SDP_DTYPE_SEQ) {
+        seqlen = sdp_datalen(&req, &len);
+        if (seqlen < 3 || len < seqlen)
+            return -SDP_INVALID_SYNTAX;
+        len -= seqlen;
+
+        while (seqlen)
+            if (sdp_svc_attr_match(sdp, &req, &seqlen))
+                return -SDP_INVALID_SYNTAX;
+    } else if (sdp_svc_attr_match(sdp, &req, &seqlen))
+        return -SDP_INVALID_SYNTAX;
+
+    if (len < 1)
+        return -SDP_INVALID_SYNTAX;
+
+    if (*req) {
+        if (len <= sizeof(int))
+            return -SDP_INVALID_SYNTAX;
+        len -= sizeof(int);
+        memcpy(&start, req + 1, sizeof(int));
+    } else
+        start = 0;
+
+    if (len > 1)
+        return -SDP_INVALID_SYNTAX;
+
+    /* Output the results */
+    /* This assumes empty attribute lists are never to be returned even
+     * for matching Service Records.  In practice this shouldn't happen
+     * as the requestor will usually include the always present
+     * ServiceRecordHandle AttributeID in AttributeIDList.  */
+    lst = rsp + 2;
+    max = MIN(max, MAX_RSP_PARAM_SIZE);
+    len = 3 - start;
+    end = 0;
+    for (i = 0; i < sdp->services; i ++)
+        if ((record = &sdp->service_list[i])->match) {
+            len += 3;
+            seqlen = len;
+            for (j = 0; j < record->attributes; j ++)
+                if (record->attribute_list[j].match) {
+                    if (len >= 0)
+                        if (len + record->attribute_list[j].len < max) {
+                            memcpy(lst + len, record->attribute_list[j].pair,
+                                            record->attribute_list[j].len);
+                            end = len + record->attribute_list[j].len;
+                        }
+                    len += record->attribute_list[j].len;
+                }
+            if (seqlen == len)
+                len -= 3;
+            else if (seqlen >= 3 && seqlen < max) {
+                lst[seqlen - 3] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2;
+                lst[seqlen - 2] = (len - seqlen) >> 8;
+                lst[seqlen - 1] = (len - seqlen) & 0xff;
+            }
+        }
+    if (len == 3 - start)
+        len -= 3;
+    else if (0 >= start) {
+       lst[0] = SDP_DTYPE_SEQ | SDP_DSIZE_NEXT2;
+       lst[1] = (len + start - 3) >> 8;
+       lst[2] = (len + start - 3) & 0xff;
+    }
+
+    rsp[0] = end >> 8;
+    rsp[1] = end & 0xff;
+
+    if (end < len) {
+        len = end + start;
+        lst[end ++] = sizeof(int);
+        memcpy(lst + end, &len, sizeof(int));
+        end += sizeof(int);
+    } else
+        lst[end ++] = 0;
+
+    return end + 2;
+}
+
+static void bt_l2cap_sdp_sdu_in(void *opaque, const uint8_t *data, int len)
+{
+    struct bt_l2cap_sdp_state_s *sdp = opaque;
+    enum bt_sdp_cmd pdu_id;
+    uint8_t rsp[MAX_PDU_OUT_SIZE - PDU_HEADER_SIZE], *sdu_out;
+    int transaction_id, plen;
+    int err = 0;
+    int rsp_len = 0;
+
+    if (len < 5) {
+        fprintf(stderr, "%s: short SDP PDU (%iB).\n", __FUNCTION__, len);
+        return;
+    }
+
+    pdu_id = *data ++;
+    transaction_id = (data[0] << 8) | data[1];
+    plen = (data[2] << 8) | data[3];
+    data += 4;
+    len -= 5;
+
+    if (len != plen) {
+        fprintf(stderr, "%s: wrong SDP PDU length (%iB != %iB).\n",
+                        __FUNCTION__, plen, len);
+        err = SDP_INVALID_PDU_SIZE;
+        goto respond;
+    }
+
+    switch (pdu_id) {
+    case SDP_SVC_SEARCH_REQ:
+        rsp_len = sdp_svc_search(sdp, rsp, data, len);
+        pdu_id = SDP_SVC_SEARCH_RSP;
+        break;
+
+    case SDP_SVC_ATTR_REQ:
+        rsp_len = sdp_attr_get(sdp, rsp, data, len);
+        pdu_id = SDP_SVC_ATTR_RSP;
+        break;
+
+    case SDP_SVC_SEARCH_ATTR_REQ:
+        rsp_len = sdp_svc_search_attr_get(sdp, rsp, data, len);
+        pdu_id = SDP_SVC_SEARCH_ATTR_RSP;
+        break;
+
+    case SDP_ERROR_RSP:
+    case SDP_SVC_ATTR_RSP:
+    case SDP_SVC_SEARCH_RSP:
+    case SDP_SVC_SEARCH_ATTR_RSP:
+    default:
+        fprintf(stderr, "%s: unexpected SDP PDU ID %02x.\n",
+                        __FUNCTION__, pdu_id);
+        err = SDP_INVALID_SYNTAX;
+        break;
+    }
+
+    if (rsp_len < 0) {
+        err = -rsp_len;
+        rsp_len = 0;
+    }
+
+respond:
+    if (err) {
+        pdu_id = SDP_ERROR_RSP;
+        rsp[rsp_len ++] = err >> 8;
+        rsp[rsp_len ++] = err & 0xff;
+    }
+
+    sdu_out = sdp->channel->sdu_out(sdp->channel, rsp_len + PDU_HEADER_SIZE);
+
+    sdu_out[0] = pdu_id;
+    sdu_out[1] = transaction_id >> 8;
+    sdu_out[2] = transaction_id & 0xff;
+    sdu_out[3] = rsp_len >> 8;
+    sdu_out[4] = rsp_len & 0xff;
+    memcpy(sdu_out + PDU_HEADER_SIZE, rsp, rsp_len);
+
+    sdp->channel->sdu_submit(sdp->channel);
+}
+
+static void bt_l2cap_sdp_close_ch(void *opaque)
+{
+    struct bt_l2cap_sdp_state_s *sdp = opaque;
+    int i;
+
+    for (i = 0; i < sdp->services; i ++) {
+        g_free(sdp->service_list[i].attribute_list->pair);
+        g_free(sdp->service_list[i].attribute_list);
+        g_free(sdp->service_list[i].uuid);
+    }
+    g_free(sdp->service_list);
+    g_free(sdp);
+}
+
+struct sdp_def_service_s {
+    uint16_t class_uuid;
+    struct sdp_def_attribute_s {
+        uint16_t id;
+        struct sdp_def_data_element_s {
+            uint8_t type;
+            union {
+                uint32_t uint;
+                const char *str;
+                struct sdp_def_data_element_s *list;
+            } value;
+        } data;
+    } attributes[];
+};
+
+/* Calculate a safe byte count to allocate that will store the given
+ * element, at the same time count elements of a UUID type.  */
+static int sdp_attr_max_size(struct sdp_def_data_element_s *element,
+                int *uuids)
+{
+    int type = element->type & ~SDP_DSIZE_MASK;
+    int len;
+
+    if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_UUID ||
+                    type == SDP_DTYPE_BOOL) {
+        if (type == SDP_DTYPE_UUID)
+            (*uuids) ++;
+        return 1 + (1 << (element->type & SDP_DSIZE_MASK));
+    }
+
+    if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) {
+        if (element->type & SDP_DSIZE_MASK) {
+            for (len = 0; element->value.str[len] |
+                            element->value.str[len + 1]; len ++);
+            return len;
+        } else
+            return 2 + strlen(element->value.str);
+    }
+
+    if (type != SDP_DTYPE_SEQ)
+        exit(-1);
+    len = 2;
+    element = element->value.list;
+    while (element->type)
+        len += sdp_attr_max_size(element ++, uuids);
+    if (len > 255)
+        exit (-1);
+
+    return len;
+}
+
+static int sdp_attr_write(uint8_t *data,
+                struct sdp_def_data_element_s *element, int **uuid)
+{
+    int type = element->type & ~SDP_DSIZE_MASK;
+    int len = 0;
+
+    if (type == SDP_DTYPE_UINT || type == SDP_DTYPE_BOOL) {
+        data[len ++] = element->type;
+        if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_1)
+            data[len ++] = (element->value.uint >>  0) & 0xff;
+        else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_2) {
+            data[len ++] = (element->value.uint >>  8) & 0xff;
+            data[len ++] = (element->value.uint >>  0) & 0xff;
+        } else if ((element->type & SDP_DSIZE_MASK) == SDP_DSIZE_4) {
+            data[len ++] = (element->value.uint >>  24) & 0xff;
+            data[len ++] = (element->value.uint >>  16) & 0xff;
+            data[len ++] = (element->value.uint >>  8) & 0xff;
+            data[len ++] = (element->value.uint >>  0) & 0xff;
+        }
+
+        return len;
+    }
+
+    if (type == SDP_DTYPE_UUID) {
+        *(*uuid) ++ = element->value.uint;
+
+        data[len ++] = element->type;
+        data[len ++] = (element->value.uint >>  24) & 0xff;
+        data[len ++] = (element->value.uint >>  16) & 0xff;
+        data[len ++] = (element->value.uint >>  8) & 0xff;
+        data[len ++] = (element->value.uint >>  0) & 0xff;
+        memcpy(data + len, bt_base_uuid, 12);
+
+        return len + 12;
+    }
+
+    data[0] = type | SDP_DSIZE_NEXT1;
+    if (type == SDP_DTYPE_STRING || type == SDP_DTYPE_URL) {
+        if (element->type & SDP_DSIZE_MASK)
+            for (len = 0; element->value.str[len] |
+                            element->value.str[len + 1]; len ++);
+        else
+            len = strlen(element->value.str);
+        memcpy(data + 2, element->value.str, data[1] = len);
+
+        return len + 2;
+    }
+
+    len = 2;
+    element = element->value.list;
+    while (element->type)
+        len += sdp_attr_write(data + len, element ++, uuid);
+    data[1] = len - 2;
+
+    return len;
+}
+
+static int sdp_attributeid_compare(const struct sdp_service_attribute_s *a,
+                const struct sdp_service_attribute_s *b)
+{
+    return (int) b->attribute_id - a->attribute_id;
+}
+
+static int sdp_uuid_compare(const int *a, const int *b)
+{
+    return *a - *b;
+}
+
+static void sdp_service_record_build(struct sdp_service_record_s *record,
+                struct sdp_def_service_s *def, int handle)
+{
+    int len = 0;
+    uint8_t *data;
+    int *uuid;
+
+    record->uuids = 0;
+    while (def->attributes[record->attributes].data.type) {
+        len += 3;
+        len += sdp_attr_max_size(&def->attributes[record->attributes ++].data,
+                        &record->uuids);
+    }
+    record->uuids = 1 << ffs(record->uuids - 1);
+    record->attribute_list =
+            g_malloc0(record->attributes * sizeof(*record->attribute_list));
+    record->uuid =
+            g_malloc0(record->uuids * sizeof(*record->uuid));
+    data = g_malloc(len);
+
+    record->attributes = 0;
+    uuid = record->uuid;
+    while (def->attributes[record->attributes].data.type) {
+        record->attribute_list[record->attributes].pair = data;
+
+        len = 0;
+        data[len ++] = SDP_DTYPE_UINT | SDP_DSIZE_2;
+        data[len ++] = def->attributes[record->attributes].id >> 8;
+        data[len ++] = def->attributes[record->attributes].id & 0xff;
+        len += sdp_attr_write(data + len,
+                        &def->attributes[record->attributes].data, &uuid);
+
+        /* Special case: assign a ServiceRecordHandle in sequence */
+        if (def->attributes[record->attributes].id == SDP_ATTR_RECORD_HANDLE)
+            def->attributes[record->attributes].data.value.uint = handle;
+        /* Note: we could also assign a ServiceDescription based on
+         * sdp->device.device->lmp_name.  */
+
+        record->attribute_list[record->attributes ++].len = len;
+        data += len;
+    }
+
+    /* Sort the attribute list by the AttributeID */
+    qsort(record->attribute_list, record->attributes,
+                    sizeof(*record->attribute_list),
+                    (void *) sdp_attributeid_compare);
+    /* Sort the searchable UUIDs list for bisection */
+    qsort(record->uuid, record->uuids,
+                    sizeof(*record->uuid),
+                    (void *) sdp_uuid_compare);
+}
+
+static void sdp_service_db_build(struct bt_l2cap_sdp_state_s *sdp,
+                struct sdp_def_service_s **service)
+{
+    sdp->services = 0;
+    while (service[sdp->services])
+        sdp->services ++;
+    sdp->service_list =
+            g_malloc0(sdp->services * sizeof(*sdp->service_list));
+
+    sdp->services = 0;
+    while (*service) {
+        sdp_service_record_build(&sdp->service_list[sdp->services],
+                        *service, sdp->services);
+        service ++;
+        sdp->services ++;
+    }
+}
+
+#define LAST { .type = 0 }
+#define SERVICE(name, attrs)                           \
+    static struct sdp_def_service_s glue(glue(sdp_service_, name), _s) = { \
+        .attributes = { attrs { .data = LAST } },      \
+    };
+#define ATTRIBUTE(attrid, val) { .id = glue(SDP_ATTR_, attrid), .data = val },
+#define UINT8(val)     {                               \
+        .type       = SDP_DTYPE_UINT | SDP_DSIZE_1,    \
+        .value.uint = val,                             \
+    },
+#define UINT16(val)    {                               \
+        .type       = SDP_DTYPE_UINT | SDP_DSIZE_2,    \
+        .value.uint = val,                             \
+    },
+#define UINT32(val)    {                               \
+        .type       = SDP_DTYPE_UINT | SDP_DSIZE_4,    \
+        .value.uint = val,                             \
+    },
+#define UUID128(val)   {                               \
+        .type       = SDP_DTYPE_UUID | SDP_DSIZE_16,   \
+        .value.uint = val,                             \
+    },
+#define SDP_TRUE       {                               \
+        .type       = SDP_DTYPE_BOOL | SDP_DSIZE_1,    \
+        .value.uint = 1,                               \
+    },
+#define SDP_FALSE      {                               \
+        .type       = SDP_DTYPE_BOOL | SDP_DSIZE_1,    \
+        .value.uint = 0,                               \
+    },
+#define STRING(val)    {                               \
+        .type       = SDP_DTYPE_STRING,                        \
+        .value.str  = val,                             \
+    },
+#define ARRAY(...)     {                               \
+        .type       = SDP_DTYPE_STRING | SDP_DSIZE_2,  \
+        .value.str  = (char []) { __VA_ARGS__, 0, 0 }, \
+    },
+#define URL(val)       {                               \
+        .type       = SDP_DTYPE_URL,                   \
+        .value.str  = val,                             \
+    },
+#if 1
+#define LIST(val)      {                               \
+        .type       = SDP_DTYPE_SEQ,                   \
+        .value.list = (struct sdp_def_data_element_s []) { val LAST }, \
+    },
+#endif
+
+/* Try to keep each single attribute below MAX_PDU_OUT_SIZE bytes
+ * in resulting SDP data representation size.  */
+
+SERVICE(hid,
+    ATTRIBUTE(RECORD_HANDLE,   UINT32(0))      /* Filled in later */
+    ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(HID_SVCLASS_ID)))
+    ATTRIBUTE(RECORD_STATE,    UINT32(1))
+    ATTRIBUTE(PROTO_DESC_LIST, LIST(
+        LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_HID_CTRL))
+        LIST(UUID128(HIDP_UUID))
+    ))
+    ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002)))
+    ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST(
+        UINT16(0x656e) UINT16(0x006a) UINT16(0x0100)
+    ))
+    ATTRIBUTE(PFILE_DESC_LIST, LIST(
+        LIST(UUID128(HID_PROFILE_ID) UINT16(0x0100))
+    ))
+    ATTRIBUTE(DOC_URL,         URL("http://bellard.org/qemu/user-doc.html"))
+    ATTRIBUTE(SVCNAME_PRIMARY, STRING("QEMU Bluetooth HID"))
+    ATTRIBUTE(SVCDESC_PRIMARY, STRING("QEMU Keyboard/Mouse"))
+    ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU"))
+
+    /* Profile specific */
+    ATTRIBUTE(DEVICE_RELEASE_NUMBER,   UINT16(0x0091)) /* Deprecated, remove */
+    ATTRIBUTE(PARSER_VERSION,          UINT16(0x0111))
+    /* TODO: extract from l2cap_device->device.class[0] */
+    ATTRIBUTE(DEVICE_SUBCLASS,         UINT8(0x40))
+    ATTRIBUTE(COUNTRY_CODE,            UINT8(0x15))
+    ATTRIBUTE(VIRTUAL_CABLE,           SDP_TRUE)
+    ATTRIBUTE(RECONNECT_INITIATE,      SDP_FALSE)
+    /* TODO: extract from hid->usbdev->report_desc */
+    ATTRIBUTE(DESCRIPTOR_LIST,         LIST(
+        LIST(UINT8(0x22) ARRAY(
+            0x05, 0x01,        /* Usage Page (Generic Desktop) */
+            0x09, 0x06,        /* Usage (Keyboard) */
+            0xa1, 0x01,        /* Collection (Application) */
+            0x75, 0x01,        /*   Report Size (1) */
+            0x95, 0x08,        /*   Report Count (8) */
+            0x05, 0x07,        /*   Usage Page (Key Codes) */
+            0x19, 0xe0,        /*   Usage Minimum (224) */
+            0x29, 0xe7,        /*   Usage Maximum (231) */
+            0x15, 0x00,        /*   Logical Minimum (0) */
+            0x25, 0x01,        /*   Logical Maximum (1) */
+            0x81, 0x02,        /*   Input (Data, Variable, Absolute) */
+            0x95, 0x01,        /*   Report Count (1) */
+            0x75, 0x08,        /*   Report Size (8) */
+            0x81, 0x01,        /*   Input (Constant) */
+            0x95, 0x05,        /*   Report Count (5) */
+            0x75, 0x01,        /*   Report Size (1) */
+            0x05, 0x08,        /*   Usage Page (LEDs) */
+            0x19, 0x01,        /*   Usage Minimum (1) */
+            0x29, 0x05,        /*   Usage Maximum (5) */
+            0x91, 0x02,        /*   Output (Data, Variable, Absolute) */
+            0x95, 0x01,        /*   Report Count (1) */
+            0x75, 0x03,        /*   Report Size (3) */
+            0x91, 0x01,        /*   Output (Constant) */
+            0x95, 0x06,        /*   Report Count (6) */
+            0x75, 0x08,        /*   Report Size (8) */
+            0x15, 0x00,        /*   Logical Minimum (0) */
+            0x25, 0xff,        /*   Logical Maximum (255) */
+            0x05, 0x07,        /*   Usage Page (Key Codes) */
+            0x19, 0x00,        /*   Usage Minimum (0) */
+            0x29, 0xff,        /*   Usage Maximum (255) */
+            0x81, 0x00,        /*   Input (Data, Array) */
+            0xc0       /* End Collection */
+    ))))
+    ATTRIBUTE(LANG_ID_BASE_LIST,       LIST(
+        LIST(UINT16(0x0409) UINT16(0x0100))
+    ))
+    ATTRIBUTE(SDP_DISABLE,             SDP_FALSE)
+    ATTRIBUTE(BATTERY_POWER,           SDP_TRUE)
+    ATTRIBUTE(REMOTE_WAKEUP,           SDP_TRUE)
+    ATTRIBUTE(BOOT_DEVICE,             SDP_TRUE)       /* XXX: untested */
+    ATTRIBUTE(SUPERVISION_TIMEOUT,     UINT16(0x0c80))
+    ATTRIBUTE(NORMALLY_CONNECTABLE,    SDP_TRUE)
+    ATTRIBUTE(PROFILE_VERSION,         UINT16(0x0100))
+)
+
+SERVICE(sdp,
+    ATTRIBUTE(RECORD_HANDLE,   UINT32(0))      /* Filled in later */
+    ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(SDP_SERVER_SVCLASS_ID)))
+    ATTRIBUTE(RECORD_STATE,    UINT32(1))
+    ATTRIBUTE(PROTO_DESC_LIST, LIST(
+        LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP))
+        LIST(UUID128(SDP_UUID))
+    ))
+    ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002)))
+    ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST(
+        UINT16(0x656e) UINT16(0x006a) UINT16(0x0100)
+    ))
+    ATTRIBUTE(PFILE_DESC_LIST, LIST(
+        LIST(UUID128(SDP_SERVER_PROFILE_ID) UINT16(0x0100))
+    ))
+    ATTRIBUTE(DOC_URL,         URL("http://bellard.org/qemu/user-doc.html"))
+    ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU"))
+
+    /* Profile specific */
+    ATTRIBUTE(VERSION_NUM_LIST, LIST(UINT16(0x0100)))
+    ATTRIBUTE(SVCDB_STATE    , UINT32(1))
+)
+
+SERVICE(pnp,
+    ATTRIBUTE(RECORD_HANDLE,   UINT32(0))      /* Filled in later */
+    ATTRIBUTE(SVCLASS_ID_LIST, LIST(UUID128(PNP_INFO_SVCLASS_ID)))
+    ATTRIBUTE(RECORD_STATE,    UINT32(1))
+    ATTRIBUTE(PROTO_DESC_LIST, LIST(
+        LIST(UUID128(L2CAP_UUID) UINT16(BT_PSM_SDP))
+        LIST(UUID128(SDP_UUID))
+    ))
+    ATTRIBUTE(BROWSE_GRP_LIST, LIST(UUID128(0x1002)))
+    ATTRIBUTE(LANG_BASE_ATTR_ID_LIST, LIST(
+        UINT16(0x656e) UINT16(0x006a) UINT16(0x0100)
+    ))
+    ATTRIBUTE(PFILE_DESC_LIST, LIST(
+        LIST(UUID128(PNP_INFO_PROFILE_ID) UINT16(0x0100))
+    ))
+    ATTRIBUTE(DOC_URL,         URL("http://bellard.org/qemu/user-doc.html"))
+    ATTRIBUTE(SVCPROV_PRIMARY, STRING("QEMU"))
+
+    /* Profile specific */
+    ATTRIBUTE(SPECIFICATION_ID, UINT16(0x0100))
+    ATTRIBUTE(VERSION,         UINT16(0x0100))
+    ATTRIBUTE(PRIMARY_RECORD,  SDP_TRUE)
+)
+
+static int bt_l2cap_sdp_new_ch(struct bt_l2cap_device_s *dev,
+                struct bt_l2cap_conn_params_s *params)
+{
+    struct bt_l2cap_sdp_state_s *sdp = g_malloc0(sizeof(*sdp));
+    struct sdp_def_service_s *services[] = {
+        &sdp_service_sdp_s,
+        &sdp_service_hid_s,
+        &sdp_service_pnp_s,
+        NULL,
+    };
+
+    sdp->channel = params;
+    sdp->channel->opaque = sdp;
+    sdp->channel->close = bt_l2cap_sdp_close_ch;
+    sdp->channel->sdu_in = bt_l2cap_sdp_sdu_in;
+
+    sdp_service_db_build(sdp, services);
+
+    return 0;
+}
+
+void bt_l2cap_sdp_init(struct bt_l2cap_device_s *dev)
+{
+    bt_l2cap_psm_register(dev, BT_PSM_SDP,
+                    MAX_PDU_OUT_SIZE, bt_l2cap_sdp_new_ch);
+}
diff --git a/hw/cadence_gem.c b/hw/cadence_gem.c
deleted file mode 100644 (file)
index e177057..0000000
+++ /dev/null
@@ -1,1219 +0,0 @@
-/*
- * QEMU Xilinx GEM emulation
- *
- * Copyright (c) 2011 Xilinx, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include <zlib.h> /* For crc32 */
-
-#include "hw/sysbus.h"
-#include "net/net.h"
-#include "net/checksum.h"
-
-#ifdef CADENCE_GEM_ERR_DEBUG
-#define DB_PRINT(...) do { \
-    fprintf(stderr,  ": %s: ", __func__); \
-    fprintf(stderr, ## __VA_ARGS__); \
-    } while (0);
-#else
-    #define DB_PRINT(...)
-#endif
-
-#define GEM_NWCTRL        (0x00000000/4) /* Network Control reg */
-#define GEM_NWCFG         (0x00000004/4) /* Network Config reg */
-#define GEM_NWSTATUS      (0x00000008/4) /* Network Status reg */
-#define GEM_USERIO        (0x0000000C/4) /* User IO reg */
-#define GEM_DMACFG        (0x00000010/4) /* DMA Control reg */
-#define GEM_TXSTATUS      (0x00000014/4) /* TX Status reg */
-#define GEM_RXQBASE       (0x00000018/4) /* RX Q Base address reg */
-#define GEM_TXQBASE       (0x0000001C/4) /* TX Q Base address reg */
-#define GEM_RXSTATUS      (0x00000020/4) /* RX Status reg */
-#define GEM_ISR           (0x00000024/4) /* Interrupt Status reg */
-#define GEM_IER           (0x00000028/4) /* Interrupt Enable reg */
-#define GEM_IDR           (0x0000002C/4) /* Interrupt Disable reg */
-#define GEM_IMR           (0x00000030/4) /* Interrupt Mask reg */
-#define GEM_PHYMNTNC      (0x00000034/4) /* Phy Maintaince reg */
-#define GEM_RXPAUSE       (0x00000038/4) /* RX Pause Time reg */
-#define GEM_TXPAUSE       (0x0000003C/4) /* TX Pause Time reg */
-#define GEM_TXPARTIALSF   (0x00000040/4) /* TX Partial Store and Forward */
-#define GEM_RXPARTIALSF   (0x00000044/4) /* RX Partial Store and Forward */
-#define GEM_HASHLO        (0x00000080/4) /* Hash Low address reg */
-#define GEM_HASHHI        (0x00000084/4) /* Hash High address reg */
-#define GEM_SPADDR1LO     (0x00000088/4) /* Specific addr 1 low reg */
-#define GEM_SPADDR1HI     (0x0000008C/4) /* Specific addr 1 high reg */
-#define GEM_SPADDR2LO     (0x00000090/4) /* Specific addr 2 low reg */
-#define GEM_SPADDR2HI     (0x00000094/4) /* Specific addr 2 high reg */
-#define GEM_SPADDR3LO     (0x00000098/4) /* Specific addr 3 low reg */
-#define GEM_SPADDR3HI     (0x0000009C/4) /* Specific addr 3 high reg */
-#define GEM_SPADDR4LO     (0x000000A0/4) /* Specific addr 4 low reg */
-#define GEM_SPADDR4HI     (0x000000A4/4) /* Specific addr 4 high reg */
-#define GEM_TIDMATCH1     (0x000000A8/4) /* Type ID1 Match reg */
-#define GEM_TIDMATCH2     (0x000000AC/4) /* Type ID2 Match reg */
-#define GEM_TIDMATCH3     (0x000000B0/4) /* Type ID3 Match reg */
-#define GEM_TIDMATCH4     (0x000000B4/4) /* Type ID4 Match reg */
-#define GEM_WOLAN         (0x000000B8/4) /* Wake on LAN reg */
-#define GEM_IPGSTRETCH    (0x000000BC/4) /* IPG Stretch reg */
-#define GEM_SVLAN         (0x000000C0/4) /* Stacked VLAN reg */
-#define GEM_MODID         (0x000000FC/4) /* Module ID reg */
-#define GEM_OCTTXLO       (0x00000100/4) /* Octects transmitted Low reg */
-#define GEM_OCTTXHI       (0x00000104/4) /* Octects transmitted High reg */
-#define GEM_TXCNT         (0x00000108/4) /* Error-free Frames transmitted */
-#define GEM_TXBCNT        (0x0000010C/4) /* Error-free Broadcast Frames */
-#define GEM_TXMCNT        (0x00000110/4) /* Error-free Multicast Frame */
-#define GEM_TXPAUSECNT    (0x00000114/4) /* Pause Frames Transmitted */
-#define GEM_TX64CNT       (0x00000118/4) /* Error-free 64 TX */
-#define GEM_TX65CNT       (0x0000011C/4) /* Error-free 65-127 TX */
-#define GEM_TX128CNT      (0x00000120/4) /* Error-free 128-255 TX */
-#define GEM_TX256CNT      (0x00000124/4) /* Error-free 256-511 */
-#define GEM_TX512CNT      (0x00000128/4) /* Error-free 512-1023 TX */
-#define GEM_TX1024CNT     (0x0000012C/4) /* Error-free 1024-1518 TX */
-#define GEM_TX1519CNT     (0x00000130/4) /* Error-free larger than 1519 TX */
-#define GEM_TXURUNCNT     (0x00000134/4) /* TX under run error counter */
-#define GEM_SINGLECOLLCNT (0x00000138/4) /* Single Collision Frames */
-#define GEM_MULTCOLLCNT   (0x0000013C/4) /* Multiple Collision Frames */
-#define GEM_EXCESSCOLLCNT (0x00000140/4) /* Excessive Collision Frames */
-#define GEM_LATECOLLCNT   (0x00000144/4) /* Late Collision Frames */
-#define GEM_DEFERTXCNT    (0x00000148/4) /* Deferred Transmission Frames */
-#define GEM_CSENSECNT     (0x0000014C/4) /* Carrier Sense Error Counter */
-#define GEM_OCTRXLO       (0x00000150/4) /* Octects Received register Low */
-#define GEM_OCTRXHI       (0x00000154/4) /* Octects Received register High */
-#define GEM_RXCNT         (0x00000158/4) /* Error-free Frames Received */
-#define GEM_RXBROADCNT    (0x0000015C/4) /* Error-free Broadcast Frames RX */
-#define GEM_RXMULTICNT    (0x00000160/4) /* Error-free Multicast Frames RX */
-#define GEM_RXPAUSECNT    (0x00000164/4) /* Pause Frames Received Counter */
-#define GEM_RX64CNT       (0x00000168/4) /* Error-free 64 byte Frames RX */
-#define GEM_RX65CNT       (0x0000016C/4) /* Error-free 65-127B Frames RX */
-#define GEM_RX128CNT      (0x00000170/4) /* Error-free 128-255B Frames RX */
-#define GEM_RX256CNT      (0x00000174/4) /* Error-free 256-512B Frames RX */
-#define GEM_RX512CNT      (0x00000178/4) /* Error-free 512-1023B Frames RX */
-#define GEM_RX1024CNT     (0x0000017C/4) /* Error-free 1024-1518B Frames RX */
-#define GEM_RX1519CNT     (0x00000180/4) /* Error-free 1519-max Frames RX */
-#define GEM_RXUNDERCNT    (0x00000184/4) /* Undersize Frames Received */
-#define GEM_RXOVERCNT     (0x00000188/4) /* Oversize Frames Received */
-#define GEM_RXJABCNT      (0x0000018C/4) /* Jabbers Received Counter */
-#define GEM_RXFCSCNT      (0x00000190/4) /* Frame Check seq. Error Counter */
-#define GEM_RXLENERRCNT   (0x00000194/4) /* Length Field Error Counter */
-#define GEM_RXSYMERRCNT   (0x00000198/4) /* Symbol Error Counter */
-#define GEM_RXALIGNERRCNT (0x0000019C/4) /* Alignment Error Counter */
-#define GEM_RXRSCERRCNT   (0x000001A0/4) /* Receive Resource Error Counter */
-#define GEM_RXORUNCNT     (0x000001A4/4) /* Receive Overrun Counter */
-#define GEM_RXIPCSERRCNT  (0x000001A8/4) /* IP header Checksum Error Counter */
-#define GEM_RXTCPCCNT     (0x000001AC/4) /* TCP Checksum Error Counter */
-#define GEM_RXUDPCCNT     (0x000001B0/4) /* UDP Checksum Error Counter */
-
-#define GEM_1588S         (0x000001D0/4) /* 1588 Timer Seconds */
-#define GEM_1588NS        (0x000001D4/4) /* 1588 Timer Nanoseconds */
-#define GEM_1588ADJ       (0x000001D8/4) /* 1588 Timer Adjust */
-#define GEM_1588INC       (0x000001DC/4) /* 1588 Timer Increment */
-#define GEM_PTPETXS       (0x000001E0/4) /* PTP Event Frame Transmitted (s) */
-#define GEM_PTPETXNS      (0x000001E4/4) /* PTP Event Frame Transmitted (ns) */
-#define GEM_PTPERXS       (0x000001E8/4) /* PTP Event Frame Received (s) */
-#define GEM_PTPERXNS      (0x000001EC/4) /* PTP Event Frame Received (ns) */
-#define GEM_PTPPTXS       (0x000001E0/4) /* PTP Peer Frame Transmitted (s) */
-#define GEM_PTPPTXNS      (0x000001E4/4) /* PTP Peer Frame Transmitted (ns) */
-#define GEM_PTPPRXS       (0x000001E8/4) /* PTP Peer Frame Received (s) */
-#define GEM_PTPPRXNS      (0x000001EC/4) /* PTP Peer Frame Received (ns) */
-
-/* Design Configuration Registers */
-#define GEM_DESCONF       (0x00000280/4)
-#define GEM_DESCONF2      (0x00000284/4)
-#define GEM_DESCONF3      (0x00000288/4)
-#define GEM_DESCONF4      (0x0000028C/4)
-#define GEM_DESCONF5      (0x00000290/4)
-#define GEM_DESCONF6      (0x00000294/4)
-#define GEM_DESCONF7      (0x00000298/4)
-
-#define GEM_MAXREG        (0x00000640/4) /* Last valid GEM address */
-
-/*****************************************/
-#define GEM_NWCTRL_TXSTART     0x00000200 /* Transmit Enable */
-#define GEM_NWCTRL_TXENA       0x00000008 /* Transmit Enable */
-#define GEM_NWCTRL_RXENA       0x00000004 /* Receive Enable */
-#define GEM_NWCTRL_LOCALLOOP   0x00000002 /* Local Loopback */
-
-#define GEM_NWCFG_STRIP_FCS    0x00020000 /* Strip FCS field */
-#define GEM_NWCFG_LERR_DISC    0x00010000 /* Discard RX frames with lenth err */
-#define GEM_NWCFG_BUFF_OFST_M  0x0000C000 /* Receive buffer offset mask */
-#define GEM_NWCFG_BUFF_OFST_S  14         /* Receive buffer offset shift */
-#define GEM_NWCFG_UCAST_HASH   0x00000080 /* accept unicast if hash match */
-#define GEM_NWCFG_MCAST_HASH   0x00000040 /* accept multicast if hash match */
-#define GEM_NWCFG_BCAST_REJ    0x00000020 /* Reject broadcast packets */
-#define GEM_NWCFG_PROMISC      0x00000010 /* Accept all packets */
-
-#define GEM_DMACFG_RBUFSZ_M    0x007F0000 /* DMA RX Buffer Size mask */
-#define GEM_DMACFG_RBUFSZ_S    16         /* DMA RX Buffer Size shift */
-#define GEM_DMACFG_RBUFSZ_MUL  64         /* DMA RX Buffer Size multiplier */
-#define GEM_DMACFG_TXCSUM_OFFL 0x00000800 /* Transmit checksum offload */
-
-#define GEM_TXSTATUS_TXCMPL    0x00000020 /* Transmit Complete */
-#define GEM_TXSTATUS_USED      0x00000001 /* sw owned descriptor encountered */
-
-#define GEM_RXSTATUS_FRMRCVD   0x00000002 /* Frame received */
-#define GEM_RXSTATUS_NOBUF     0x00000001 /* Buffer unavailable */
-
-/* GEM_ISR GEM_IER GEM_IDR GEM_IMR */
-#define GEM_INT_TXCMPL        0x00000080 /* Transmit Complete */
-#define GEM_INT_TXUSED         0x00000008
-#define GEM_INT_RXUSED         0x00000004
-#define GEM_INT_RXCMPL        0x00000002
-
-#define GEM_PHYMNTNC_OP_R      0x20000000 /* read operation */
-#define GEM_PHYMNTNC_OP_W      0x10000000 /* write operation */
-#define GEM_PHYMNTNC_ADDR      0x0F800000 /* Address bits */
-#define GEM_PHYMNTNC_ADDR_SHFT 23
-#define GEM_PHYMNTNC_REG       0x007C0000 /* register bits */
-#define GEM_PHYMNTNC_REG_SHIFT 18
-
-/* Marvell PHY definitions */
-#define BOARD_PHY_ADDRESS    23 /* PHY address we will emulate a device at */
-
-#define PHY_REG_CONTROL      0
-#define PHY_REG_STATUS       1
-#define PHY_REG_PHYID1       2
-#define PHY_REG_PHYID2       3
-#define PHY_REG_ANEGADV      4
-#define PHY_REG_LINKPABIL    5
-#define PHY_REG_ANEGEXP      6
-#define PHY_REG_NEXTP        7
-#define PHY_REG_LINKPNEXTP   8
-#define PHY_REG_100BTCTRL    9
-#define PHY_REG_1000BTSTAT   10
-#define PHY_REG_EXTSTAT      15
-#define PHY_REG_PHYSPCFC_CTL 16
-#define PHY_REG_PHYSPCFC_ST  17
-#define PHY_REG_INT_EN       18
-#define PHY_REG_INT_ST       19
-#define PHY_REG_EXT_PHYSPCFC_CTL  20
-#define PHY_REG_RXERR        21
-#define PHY_REG_EACD         22
-#define PHY_REG_LED          24
-#define PHY_REG_LED_OVRD     25
-#define PHY_REG_EXT_PHYSPCFC_CTL2 26
-#define PHY_REG_EXT_PHYSPCFC_ST   27
-#define PHY_REG_CABLE_DIAG   28
-
-#define PHY_REG_CONTROL_RST  0x8000
-#define PHY_REG_CONTROL_LOOP 0x4000
-#define PHY_REG_CONTROL_ANEG 0x1000
-
-#define PHY_REG_STATUS_LINK     0x0004
-#define PHY_REG_STATUS_ANEGCMPL 0x0020
-
-#define PHY_REG_INT_ST_ANEGCMPL 0x0800
-#define PHY_REG_INT_ST_LINKC    0x0400
-#define PHY_REG_INT_ST_ENERGY   0x0010
-
-/***********************************************************************/
-#define GEM_RX_REJECT  1
-#define GEM_RX_ACCEPT  0
-
-/***********************************************************************/
-
-#define DESC_1_USED 0x80000000
-#define DESC_1_LENGTH 0x00001FFF
-
-#define DESC_1_TX_WRAP 0x40000000
-#define DESC_1_TX_LAST 0x00008000
-
-#define DESC_0_RX_WRAP 0x00000002
-#define DESC_0_RX_OWNERSHIP 0x00000001
-
-#define DESC_1_RX_SOF 0x00004000
-#define DESC_1_RX_EOF 0x00008000
-
-static inline unsigned tx_desc_get_buffer(unsigned *desc)
-{
-    return desc[0];
-}
-
-static inline unsigned tx_desc_get_used(unsigned *desc)
-{
-    return (desc[1] & DESC_1_USED) ? 1 : 0;
-}
-
-static inline void tx_desc_set_used(unsigned *desc)
-{
-    desc[1] |= DESC_1_USED;
-}
-
-static inline unsigned tx_desc_get_wrap(unsigned *desc)
-{
-    return (desc[1] & DESC_1_TX_WRAP) ? 1 : 0;
-}
-
-static inline unsigned tx_desc_get_last(unsigned *desc)
-{
-    return (desc[1] & DESC_1_TX_LAST) ? 1 : 0;
-}
-
-static inline unsigned tx_desc_get_length(unsigned *desc)
-{
-    return desc[1] & DESC_1_LENGTH;
-}
-
-static inline void print_gem_tx_desc(unsigned *desc)
-{
-    DB_PRINT("TXDESC:\n");
-    DB_PRINT("bufaddr: 0x%08x\n", *desc);
-    DB_PRINT("used_hw: %d\n", tx_desc_get_used(desc));
-    DB_PRINT("wrap:    %d\n", tx_desc_get_wrap(desc));
-    DB_PRINT("last:    %d\n", tx_desc_get_last(desc));
-    DB_PRINT("length:  %d\n", tx_desc_get_length(desc));
-}
-
-static inline unsigned rx_desc_get_buffer(unsigned *desc)
-{
-    return desc[0] & ~0x3UL;
-}
-
-static inline unsigned rx_desc_get_wrap(unsigned *desc)
-{
-    return desc[0] & DESC_0_RX_WRAP ? 1 : 0;
-}
-
-static inline unsigned rx_desc_get_ownership(unsigned *desc)
-{
-    return desc[0] & DESC_0_RX_OWNERSHIP ? 1 : 0;
-}
-
-static inline void rx_desc_set_ownership(unsigned *desc)
-{
-    desc[0] |= DESC_0_RX_OWNERSHIP;
-}
-
-static inline void rx_desc_set_sof(unsigned *desc)
-{
-    desc[1] |= DESC_1_RX_SOF;
-}
-
-static inline void rx_desc_set_eof(unsigned *desc)
-{
-    desc[1] |= DESC_1_RX_EOF;
-}
-
-static inline void rx_desc_set_length(unsigned *desc, unsigned len)
-{
-    desc[1] &= ~DESC_1_LENGTH;
-    desc[1] |= len;
-}
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    NICState *nic;
-    NICConf conf;
-    qemu_irq irq;
-
-    /* GEM registers backing store */
-    uint32_t regs[GEM_MAXREG];
-    /* Mask of register bits which are write only */
-    uint32_t regs_wo[GEM_MAXREG];
-    /* Mask of register bits which are read only */
-    uint32_t regs_ro[GEM_MAXREG];
-    /* Mask of register bits which are clear on read */
-    uint32_t regs_rtc[GEM_MAXREG];
-    /* Mask of register bits which are write 1 to clear */
-    uint32_t regs_w1c[GEM_MAXREG];
-
-    /* PHY registers backing store */
-    uint16_t phy_regs[32];
-
-    uint8_t phy_loop; /* Are we in phy loopback? */
-
-    /* The current DMA descriptor pointers */
-    uint32_t rx_desc_addr;
-    uint32_t tx_desc_addr;
-
-} GemState;
-
-/* The broadcast MAC address: 0xFFFFFFFFFFFF */
-const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
-
-/*
- * gem_init_register_masks:
- * One time initialization.
- * Set masks to identify which register bits have magical clear properties
- */
-static void gem_init_register_masks(GemState *s)
-{
-    /* Mask of register bits which are read only*/
-    memset(&s->regs_ro[0], 0, sizeof(s->regs_ro));
-    s->regs_ro[GEM_NWCTRL]   = 0xFFF80000;
-    s->regs_ro[GEM_NWSTATUS] = 0xFFFFFFFF;
-    s->regs_ro[GEM_DMACFG]   = 0xFE00F000;
-    s->regs_ro[GEM_TXSTATUS] = 0xFFFFFE08;
-    s->regs_ro[GEM_RXQBASE]  = 0x00000003;
-    s->regs_ro[GEM_TXQBASE]  = 0x00000003;
-    s->regs_ro[GEM_RXSTATUS] = 0xFFFFFFF0;
-    s->regs_ro[GEM_ISR]      = 0xFFFFFFFF;
-    s->regs_ro[GEM_IMR]      = 0xFFFFFFFF;
-    s->regs_ro[GEM_MODID]    = 0xFFFFFFFF;
-
-    /* Mask of register bits which are clear on read */
-    memset(&s->regs_rtc[0], 0, sizeof(s->regs_rtc));
-    s->regs_rtc[GEM_ISR]      = 0xFFFFFFFF;
-
-    /* Mask of register bits which are write 1 to clear */
-    memset(&s->regs_w1c[0], 0, sizeof(s->regs_w1c));
-    s->regs_w1c[GEM_TXSTATUS] = 0x000001F7;
-    s->regs_w1c[GEM_RXSTATUS] = 0x0000000F;
-
-    /* Mask of register bits which are write only */
-    memset(&s->regs_wo[0], 0, sizeof(s->regs_wo));
-    s->regs_wo[GEM_NWCTRL]   = 0x00073E60;
-    s->regs_wo[GEM_IER]      = 0x07FFFFFF;
-    s->regs_wo[GEM_IDR]      = 0x07FFFFFF;
-}
-
-/*
- * phy_update_link:
- * Make the emulated PHY link state match the QEMU "interface" state.
- */
-static void phy_update_link(GemState *s)
-{
-    DB_PRINT("down %d\n", qemu_get_queue(s->nic)->link_down);
-
-    /* Autonegotiation status mirrors link status.  */
-    if (qemu_get_queue(s->nic)->link_down) {
-        s->phy_regs[PHY_REG_STATUS] &= ~(PHY_REG_STATUS_ANEGCMPL |
-                                         PHY_REG_STATUS_LINK);
-        s->phy_regs[PHY_REG_INT_ST] |= PHY_REG_INT_ST_LINKC;
-    } else {
-        s->phy_regs[PHY_REG_STATUS] |= (PHY_REG_STATUS_ANEGCMPL |
-                                         PHY_REG_STATUS_LINK);
-        s->phy_regs[PHY_REG_INT_ST] |= (PHY_REG_INT_ST_LINKC |
-                                        PHY_REG_INT_ST_ANEGCMPL |
-                                        PHY_REG_INT_ST_ENERGY);
-    }
-}
-
-static int gem_can_receive(NetClientState *nc)
-{
-    GemState *s;
-
-    s = qemu_get_nic_opaque(nc);
-
-    DB_PRINT("\n");
-
-    /* Do nothing if receive is not enabled. */
-    if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_RXENA)) {
-        return 0;
-    }
-
-    return 1;
-}
-
-/*
- * gem_update_int_status:
- * Raise or lower interrupt based on current status.
- */
-static void gem_update_int_status(GemState *s)
-{
-    if (s->regs[GEM_ISR]) {
-        DB_PRINT("asserting int. (0x%08x)\n", s->regs[GEM_ISR]);
-        qemu_set_irq(s->irq, 1);
-    }
-}
-
-/*
- * gem_receive_updatestats:
- * Increment receive statistics.
- */
-static void gem_receive_updatestats(GemState *s, const uint8_t *packet,
-                                    unsigned bytes)
-{
-    uint64_t octets;
-
-    /* Total octets (bytes) received */
-    octets = ((uint64_t)(s->regs[GEM_OCTRXLO]) << 32) |
-             s->regs[GEM_OCTRXHI];
-    octets += bytes;
-    s->regs[GEM_OCTRXLO] = octets >> 32;
-    s->regs[GEM_OCTRXHI] = octets;
-
-    /* Error-free Frames received */
-    s->regs[GEM_RXCNT]++;
-
-    /* Error-free Broadcast Frames counter */
-    if (!memcmp(packet, broadcast_addr, 6)) {
-        s->regs[GEM_RXBROADCNT]++;
-    }
-
-    /* Error-free Multicast Frames counter */
-    if (packet[0] == 0x01) {
-        s->regs[GEM_RXMULTICNT]++;
-    }
-
-    if (bytes <= 64) {
-        s->regs[GEM_RX64CNT]++;
-    } else if (bytes <= 127) {
-        s->regs[GEM_RX65CNT]++;
-    } else if (bytes <= 255) {
-        s->regs[GEM_RX128CNT]++;
-    } else if (bytes <= 511) {
-        s->regs[GEM_RX256CNT]++;
-    } else if (bytes <= 1023) {
-        s->regs[GEM_RX512CNT]++;
-    } else if (bytes <= 1518) {
-        s->regs[GEM_RX1024CNT]++;
-    } else {
-        s->regs[GEM_RX1519CNT]++;
-    }
-}
-
-/*
- * Get the MAC Address bit from the specified position
- */
-static unsigned get_bit(const uint8_t *mac, unsigned bit)
-{
-    unsigned byte;
-
-    byte = mac[bit / 8];
-    byte >>= (bit & 0x7);
-    byte &= 1;
-
-    return byte;
-}
-
-/*
- * Calculate a GEM MAC Address hash index
- */
-static unsigned calc_mac_hash(const uint8_t *mac)
-{
-    int index_bit, mac_bit;
-    unsigned hash_index;
-
-    hash_index = 0;
-    mac_bit = 5;
-    for (index_bit = 5; index_bit >= 0; index_bit--) {
-        hash_index |= (get_bit(mac,  mac_bit) ^
-                               get_bit(mac, mac_bit + 6) ^
-                               get_bit(mac, mac_bit + 12) ^
-                               get_bit(mac, mac_bit + 18) ^
-                               get_bit(mac, mac_bit + 24) ^
-                               get_bit(mac, mac_bit + 30) ^
-                               get_bit(mac, mac_bit + 36) ^
-                               get_bit(mac, mac_bit + 42)) << index_bit;
-        mac_bit--;
-    }
-
-    return hash_index;
-}
-
-/*
- * gem_mac_address_filter:
- * Accept or reject this destination address?
- * Returns:
- * GEM_RX_REJECT: reject
- * GEM_RX_ACCEPT: accept
- */
-static int gem_mac_address_filter(GemState *s, const uint8_t *packet)
-{
-    uint8_t *gem_spaddr;
-    int i;
-
-    /* Promiscuous mode? */
-    if (s->regs[GEM_NWCFG] & GEM_NWCFG_PROMISC) {
-        return GEM_RX_ACCEPT;
-    }
-
-    if (!memcmp(packet, broadcast_addr, 6)) {
-        /* Reject broadcast packets? */
-        if (s->regs[GEM_NWCFG] & GEM_NWCFG_BCAST_REJ) {
-            return GEM_RX_REJECT;
-        }
-        return GEM_RX_ACCEPT;
-    }
-
-    /* Accept packets -w- hash match? */
-    if ((packet[0] == 0x01 && (s->regs[GEM_NWCFG] & GEM_NWCFG_MCAST_HASH)) ||
-        (packet[0] != 0x01 && (s->regs[GEM_NWCFG] & GEM_NWCFG_UCAST_HASH))) {
-        unsigned hash_index;
-
-        hash_index = calc_mac_hash(packet);
-        if (hash_index < 32) {
-            if (s->regs[GEM_HASHLO] & (1<<hash_index)) {
-                return GEM_RX_ACCEPT;
-            }
-        } else {
-            hash_index -= 32;
-            if (s->regs[GEM_HASHHI] & (1<<hash_index)) {
-                return GEM_RX_ACCEPT;
-            }
-        }
-    }
-
-    /* Check all 4 specific addresses */
-    gem_spaddr = (uint8_t *)&(s->regs[GEM_SPADDR1LO]);
-    for (i = 0; i < 4; i++) {
-        if (!memcmp(packet, gem_spaddr, 6)) {
-            return GEM_RX_ACCEPT;
-        }
-
-        gem_spaddr += 8;
-    }
-
-    /* No address match; reject the packet */
-    return GEM_RX_REJECT;
-}
-
-/*
- * gem_receive:
- * Fit a packet handed to us by QEMU into the receive descriptor ring.
- */
-static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size)
-{
-    unsigned    desc[2];
-    hwaddr packet_desc_addr, last_desc_addr;
-    GemState *s;
-    unsigned   rxbufsize, bytes_to_copy;
-    unsigned   rxbuf_offset;
-    uint8_t    rxbuf[2048];
-    uint8_t   *rxbuf_ptr;
-
-    s = qemu_get_nic_opaque(nc);
-
-    /* Do nothing if receive is not enabled. */
-    if (!gem_can_receive(nc)) {
-        return -1;
-    }
-
-    /* Is this destination MAC address "for us" ? */
-    if (gem_mac_address_filter(s, buf) == GEM_RX_REJECT) {
-        return -1;
-    }
-
-    /* Discard packets with receive length error enabled ? */
-    if (s->regs[GEM_NWCFG] & GEM_NWCFG_LERR_DISC) {
-        unsigned type_len;
-
-        /* Fish the ethertype / length field out of the RX packet */
-        type_len = buf[12] << 8 | buf[13];
-        /* It is a length field, not an ethertype */
-        if (type_len < 0x600) {
-            if (size < type_len) {
-                /* discard */
-                return -1;
-            }
-        }
-    }
-
-    /*
-     * Determine configured receive buffer offset (probably 0)
-     */
-    rxbuf_offset = (s->regs[GEM_NWCFG] & GEM_NWCFG_BUFF_OFST_M) >>
-                   GEM_NWCFG_BUFF_OFST_S;
-
-    /* The configure size of each receive buffer.  Determines how many
-     * buffers needed to hold this packet.
-     */
-    rxbufsize = ((s->regs[GEM_DMACFG] & GEM_DMACFG_RBUFSZ_M) >>
-                 GEM_DMACFG_RBUFSZ_S) * GEM_DMACFG_RBUFSZ_MUL;
-    bytes_to_copy = size;
-
-    /* Strip of FCS field ? (usually yes) */
-    if (s->regs[GEM_NWCFG] & GEM_NWCFG_STRIP_FCS) {
-        rxbuf_ptr = (void *)buf;
-    } else {
-        unsigned crc_val;
-        int      crc_offset;
-
-        /* The application wants the FCS field, which QEMU does not provide.
-         * We must try and caclculate one.
-         */
-
-        memcpy(rxbuf, buf, size);
-        memset(rxbuf + size, 0, sizeof(rxbuf) - size);
-        rxbuf_ptr = rxbuf;
-        crc_val = cpu_to_le32(crc32(0, rxbuf, MAX(size, 60)));
-        if (size < 60) {
-            crc_offset = 60;
-        } else {
-            crc_offset = size;
-        }
-        memcpy(rxbuf + crc_offset, &crc_val, sizeof(crc_val));
-
-        bytes_to_copy += 4;
-        size += 4;
-    }
-
-    /* Pad to minimum length */
-    if (size < 64) {
-        size = 64;
-    }
-
-    DB_PRINT("config bufsize: %d packet size: %ld\n", rxbufsize, size);
-
-    packet_desc_addr = s->rx_desc_addr;
-    while (1) {
-        DB_PRINT("read descriptor 0x%x\n", (unsigned)packet_desc_addr);
-        /* read current descriptor */
-        cpu_physical_memory_read(packet_desc_addr,
-                                 (uint8_t *)&desc[0], sizeof(desc));
-
-        /* Descriptor owned by software ? */
-        if (rx_desc_get_ownership(desc) == 1) {
-            DB_PRINT("descriptor 0x%x owned by sw.\n",
-                     (unsigned)packet_desc_addr);
-            s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_NOBUF;
-            s->regs[GEM_ISR] |= GEM_INT_RXUSED & ~(s->regs[GEM_IMR]);
-            /* Handle interrupt consequences */
-            gem_update_int_status(s);
-            return -1;
-        }
-
-        DB_PRINT("copy %d bytes to 0x%x\n", MIN(bytes_to_copy, rxbufsize),
-                rx_desc_get_buffer(desc));
-
-        /*
-         * Let's have QEMU lend a helping hand.
-         */
-        if (rx_desc_get_buffer(desc) == 0) {
-            DB_PRINT("Invalid RX buffer (NULL) for descriptor 0x%x\n",
-                     (unsigned)packet_desc_addr);
-            break;
-        }
-
-        /* Copy packet data to emulated DMA buffer */
-        cpu_physical_memory_write(rx_desc_get_buffer(desc) + rxbuf_offset,
-                                  rxbuf_ptr, MIN(bytes_to_copy, rxbufsize));
-        bytes_to_copy -= MIN(bytes_to_copy, rxbufsize);
-        rxbuf_ptr += MIN(bytes_to_copy, rxbufsize);
-        if (bytes_to_copy == 0) {
-            break;
-        }
-
-        /* Next descriptor */
-        if (rx_desc_get_wrap(desc)) {
-            packet_desc_addr = s->regs[GEM_RXQBASE];
-        } else {
-            packet_desc_addr += 8;
-        }
-    }
-
-    DB_PRINT("set length: %ld, EOF on descriptor 0x%x\n", size,
-            (unsigned)packet_desc_addr);
-
-    /* Update last descriptor with EOF and total length */
-    rx_desc_set_eof(desc);
-    rx_desc_set_length(desc, size);
-    cpu_physical_memory_write(packet_desc_addr,
-                              (uint8_t *)&desc[0], sizeof(desc));
-
-    /* Advance RX packet descriptor Q */
-    last_desc_addr = packet_desc_addr;
-    packet_desc_addr = s->rx_desc_addr;
-    s->rx_desc_addr = last_desc_addr;
-    if (rx_desc_get_wrap(desc)) {
-        s->rx_desc_addr = s->regs[GEM_RXQBASE];
-        DB_PRINT("wrapping RX descriptor list\n");
-    } else {
-        DB_PRINT("incrementing RX descriptor list\n");
-        s->rx_desc_addr += 8;
-    }
-
-    DB_PRINT("set SOF, OWN on descriptor 0x%08x\n", (unsigned)packet_desc_addr);
-
-    /* Count it */
-    gem_receive_updatestats(s, buf, size);
-
-    /* Update first descriptor (which could also be the last) */
-    /* read descriptor */
-    cpu_physical_memory_read(packet_desc_addr,
-                             (uint8_t *)&desc[0], sizeof(desc));
-    rx_desc_set_sof(desc);
-    rx_desc_set_ownership(desc);
-    cpu_physical_memory_write(packet_desc_addr,
-                              (uint8_t *)&desc[0], sizeof(desc));
-
-    s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_FRMRCVD;
-    s->regs[GEM_ISR] |= GEM_INT_RXCMPL & ~(s->regs[GEM_IMR]);
-
-    /* Handle interrupt consequences */
-    gem_update_int_status(s);
-
-    return size;
-}
-
-/*
- * gem_transmit_updatestats:
- * Increment transmit statistics.
- */
-static void gem_transmit_updatestats(GemState *s, const uint8_t *packet,
-                                     unsigned bytes)
-{
-    uint64_t octets;
-
-    /* Total octets (bytes) transmitted */
-    octets = ((uint64_t)(s->regs[GEM_OCTTXLO]) << 32) |
-             s->regs[GEM_OCTTXHI];
-    octets += bytes;
-    s->regs[GEM_OCTTXLO] = octets >> 32;
-    s->regs[GEM_OCTTXHI] = octets;
-
-    /* Error-free Frames transmitted */
-    s->regs[GEM_TXCNT]++;
-
-    /* Error-free Broadcast Frames counter */
-    if (!memcmp(packet, broadcast_addr, 6)) {
-        s->regs[GEM_TXBCNT]++;
-    }
-
-    /* Error-free Multicast Frames counter */
-    if (packet[0] == 0x01) {
-        s->regs[GEM_TXMCNT]++;
-    }
-
-    if (bytes <= 64) {
-        s->regs[GEM_TX64CNT]++;
-    } else if (bytes <= 127) {
-        s->regs[GEM_TX65CNT]++;
-    } else if (bytes <= 255) {
-        s->regs[GEM_TX128CNT]++;
-    } else if (bytes <= 511) {
-        s->regs[GEM_TX256CNT]++;
-    } else if (bytes <= 1023) {
-        s->regs[GEM_TX512CNT]++;
-    } else if (bytes <= 1518) {
-        s->regs[GEM_TX1024CNT]++;
-    } else {
-        s->regs[GEM_TX1519CNT]++;
-    }
-}
-
-/*
- * gem_transmit:
- * Fish packets out of the descriptor ring and feed them to QEMU
- */
-static void gem_transmit(GemState *s)
-{
-    unsigned    desc[2];
-    hwaddr packet_desc_addr;
-    uint8_t     tx_packet[2048];
-    uint8_t     *p;
-    unsigned    total_bytes;
-
-    /* Do nothing if transmit is not enabled. */
-    if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_TXENA)) {
-        return;
-    }
-
-    DB_PRINT("\n");
-
-    /* The packet we will hand off to qemu.
-     * Packets scattered across multiple descriptors are gathered to this
-     * one contiguous buffer first.
-     */
-    p = tx_packet;
-    total_bytes = 0;
-
-    /* read current descriptor */
-    packet_desc_addr = s->tx_desc_addr;
-    cpu_physical_memory_read(packet_desc_addr,
-                             (uint8_t *)&desc[0], sizeof(desc));
-    /* Handle all descriptors owned by hardware */
-    while (tx_desc_get_used(desc) == 0) {
-
-        /* Do nothing if transmit is not enabled. */
-        if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_TXENA)) {
-            return;
-        }
-        print_gem_tx_desc(desc);
-
-        /* The real hardware would eat this (and possibly crash).
-         * For QEMU let's lend a helping hand.
-         */
-        if ((tx_desc_get_buffer(desc) == 0) ||
-            (tx_desc_get_length(desc) == 0)) {
-            DB_PRINT("Invalid TX descriptor @ 0x%x\n",
-                     (unsigned)packet_desc_addr);
-            break;
-        }
-
-        /* Gather this fragment of the packet from "dma memory" to our contig.
-         * buffer.
-         */
-        cpu_physical_memory_read(tx_desc_get_buffer(desc), p,
-                                 tx_desc_get_length(desc));
-        p += tx_desc_get_length(desc);
-        total_bytes += tx_desc_get_length(desc);
-
-        /* Last descriptor for this packet; hand the whole thing off */
-        if (tx_desc_get_last(desc)) {
-            /* Modify the 1st descriptor of this packet to be owned by
-             * the processor.
-             */
-            cpu_physical_memory_read(s->tx_desc_addr,
-                                     (uint8_t *)&desc[0], sizeof(desc));
-            tx_desc_set_used(desc);
-            cpu_physical_memory_write(s->tx_desc_addr,
-                                      (uint8_t *)&desc[0], sizeof(desc));
-            /* Advance the hardare current descriptor past this packet */
-            if (tx_desc_get_wrap(desc)) {
-                s->tx_desc_addr = s->regs[GEM_TXQBASE];
-            } else {
-                s->tx_desc_addr = packet_desc_addr + 8;
-            }
-            DB_PRINT("TX descriptor next: 0x%08x\n", s->tx_desc_addr);
-
-            s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_TXCMPL;
-            s->regs[GEM_ISR] |= GEM_INT_TXCMPL & ~(s->regs[GEM_IMR]);
-
-            /* Handle interrupt consequences */
-            gem_update_int_status(s);
-
-            /* Is checksum offload enabled? */
-            if (s->regs[GEM_DMACFG] & GEM_DMACFG_TXCSUM_OFFL) {
-                net_checksum_calculate(tx_packet, total_bytes);
-            }
-
-            /* Update MAC statistics */
-            gem_transmit_updatestats(s, tx_packet, total_bytes);
-
-            /* Send the packet somewhere */
-            if (s->phy_loop) {
-                gem_receive(qemu_get_queue(s->nic), tx_packet, total_bytes);
-            } else {
-                qemu_send_packet(qemu_get_queue(s->nic), tx_packet,
-                                 total_bytes);
-            }
-
-            /* Prepare for next packet */
-            p = tx_packet;
-            total_bytes = 0;
-        }
-
-        /* read next descriptor */
-        if (tx_desc_get_wrap(desc)) {
-            packet_desc_addr = s->regs[GEM_TXQBASE];
-        } else {
-            packet_desc_addr += 8;
-        }
-        cpu_physical_memory_read(packet_desc_addr,
-                                 (uint8_t *)&desc[0], sizeof(desc));
-    }
-
-    if (tx_desc_get_used(desc)) {
-        s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_USED;
-        s->regs[GEM_ISR] |= GEM_INT_TXUSED & ~(s->regs[GEM_IMR]);
-        gem_update_int_status(s);
-    }
-}
-
-static void gem_phy_reset(GemState *s)
-{
-    memset(&s->phy_regs[0], 0, sizeof(s->phy_regs));
-    s->phy_regs[PHY_REG_CONTROL] = 0x1140;
-    s->phy_regs[PHY_REG_STATUS] = 0x7969;
-    s->phy_regs[PHY_REG_PHYID1] = 0x0141;
-    s->phy_regs[PHY_REG_PHYID2] = 0x0CC2;
-    s->phy_regs[PHY_REG_ANEGADV] = 0x01E1;
-    s->phy_regs[PHY_REG_LINKPABIL] = 0xCDE1;
-    s->phy_regs[PHY_REG_ANEGEXP] = 0x000F;
-    s->phy_regs[PHY_REG_NEXTP] = 0x2001;
-    s->phy_regs[PHY_REG_LINKPNEXTP] = 0x40E6;
-    s->phy_regs[PHY_REG_100BTCTRL] = 0x0300;
-    s->phy_regs[PHY_REG_1000BTSTAT] = 0x7C00;
-    s->phy_regs[PHY_REG_EXTSTAT] = 0x3000;
-    s->phy_regs[PHY_REG_PHYSPCFC_CTL] = 0x0078;
-    s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0xBC00;
-    s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL] = 0x0C60;
-    s->phy_regs[PHY_REG_LED] = 0x4100;
-    s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL2] = 0x000A;
-    s->phy_regs[PHY_REG_EXT_PHYSPCFC_ST] = 0x848B;
-
-    phy_update_link(s);
-}
-
-static void gem_reset(DeviceState *d)
-{
-    GemState *s = FROM_SYSBUS(GemState, SYS_BUS_DEVICE(d));
-
-    DB_PRINT("\n");
-
-    /* Set post reset register values */
-    memset(&s->regs[0], 0, sizeof(s->regs));
-    s->regs[GEM_NWCFG] = 0x00080000;
-    s->regs[GEM_NWSTATUS] = 0x00000006;
-    s->regs[GEM_DMACFG] = 0x00020784;
-    s->regs[GEM_IMR] = 0x07ffffff;
-    s->regs[GEM_TXPAUSE] = 0x0000ffff;
-    s->regs[GEM_TXPARTIALSF] = 0x000003ff;
-    s->regs[GEM_RXPARTIALSF] = 0x000003ff;
-    s->regs[GEM_MODID] = 0x00020118;
-    s->regs[GEM_DESCONF] = 0x02500111;
-    s->regs[GEM_DESCONF2] = 0x2ab13fff;
-    s->regs[GEM_DESCONF5] = 0x002f2145;
-    s->regs[GEM_DESCONF6] = 0x00000200;
-
-    gem_phy_reset(s);
-
-    gem_update_int_status(s);
-}
-
-static uint16_t gem_phy_read(GemState *s, unsigned reg_num)
-{
-    DB_PRINT("reg: %d value: 0x%04x\n", reg_num, s->phy_regs[reg_num]);
-    return s->phy_regs[reg_num];
-}
-
-static void gem_phy_write(GemState *s, unsigned reg_num, uint16_t val)
-{
-    DB_PRINT("reg: %d value: 0x%04x\n", reg_num, val);
-
-    switch (reg_num) {
-    case PHY_REG_CONTROL:
-        if (val & PHY_REG_CONTROL_RST) {
-            /* Phy reset */
-            gem_phy_reset(s);
-            val &= ~(PHY_REG_CONTROL_RST | PHY_REG_CONTROL_LOOP);
-            s->phy_loop = 0;
-        }
-        if (val & PHY_REG_CONTROL_ANEG) {
-            /* Complete autonegotiation immediately */
-            val &= ~PHY_REG_CONTROL_ANEG;
-            s->phy_regs[PHY_REG_STATUS] |= PHY_REG_STATUS_ANEGCMPL;
-        }
-        if (val & PHY_REG_CONTROL_LOOP) {
-            DB_PRINT("PHY placed in loopback\n");
-            s->phy_loop = 1;
-        } else {
-            s->phy_loop = 0;
-        }
-        break;
-    }
-    s->phy_regs[reg_num] = val;
-}
-
-/*
- * gem_read32:
- * Read a GEM register.
- */
-static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size)
-{
-    GemState *s;
-    uint32_t retval;
-
-    s = (GemState *)opaque;
-
-    offset >>= 2;
-    retval = s->regs[offset];
-
-    DB_PRINT("offset: 0x%04x read: 0x%08x\n", (unsigned)offset*4, retval);
-
-    switch (offset) {
-    case GEM_ISR:
-        DB_PRINT("lowering irq on ISR read\n");
-        qemu_set_irq(s->irq, 0);
-        break;
-    case GEM_PHYMNTNC:
-        if (retval & GEM_PHYMNTNC_OP_R) {
-            uint32_t phy_addr, reg_num;
-
-            phy_addr = (retval & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT;
-            if (phy_addr == BOARD_PHY_ADDRESS) {
-                reg_num = (retval & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT;
-                retval &= 0xFFFF0000;
-                retval |= gem_phy_read(s, reg_num);
-            } else {
-                retval |= 0xFFFF; /* No device at this address */
-            }
-        }
-        break;
-    }
-
-    /* Squash read to clear bits */
-    s->regs[offset] &= ~(s->regs_rtc[offset]);
-
-    /* Do not provide write only bits */
-    retval &= ~(s->regs_wo[offset]);
-
-    DB_PRINT("0x%08x\n", retval);
-    return retval;
-}
-
-/*
- * gem_write32:
- * Write a GEM register.
- */
-static void gem_write(void *opaque, hwaddr offset, uint64_t val,
-        unsigned size)
-{
-    GemState *s = (GemState *)opaque;
-    uint32_t readonly;
-
-    DB_PRINT("offset: 0x%04x write: 0x%08x ", (unsigned)offset, (unsigned)val);
-    offset >>= 2;
-
-    /* Squash bits which are read only in write value */
-    val &= ~(s->regs_ro[offset]);
-    /* Preserve (only) bits which are read only in register */
-    readonly = s->regs[offset];
-    readonly &= s->regs_ro[offset];
-
-    /* Squash bits which are write 1 to clear */
-    val &= ~(s->regs_w1c[offset] & val);
-
-    /* Copy register write to backing store */
-    s->regs[offset] = val | readonly;
-
-    /* Handle register write side effects */
-    switch (offset) {
-    case GEM_NWCTRL:
-        if (val & GEM_NWCTRL_TXSTART) {
-            gem_transmit(s);
-        }
-        if (!(val & GEM_NWCTRL_TXENA)) {
-            /* Reset to start of Q when transmit disabled. */
-            s->tx_desc_addr = s->regs[GEM_TXQBASE];
-        }
-        if (val & GEM_NWCTRL_RXENA) {
-            qemu_flush_queued_packets(qemu_get_queue(s->nic));
-        }
-        break;
-
-    case GEM_TXSTATUS:
-        gem_update_int_status(s);
-        break;
-    case GEM_RXQBASE:
-        s->rx_desc_addr = val;
-        break;
-    case GEM_TXQBASE:
-        s->tx_desc_addr = val;
-        break;
-    case GEM_RXSTATUS:
-        gem_update_int_status(s);
-        break;
-    case GEM_IER:
-        s->regs[GEM_IMR] &= ~val;
-        gem_update_int_status(s);
-        break;
-    case GEM_IDR:
-        s->regs[GEM_IMR] |= val;
-        gem_update_int_status(s);
-        break;
-    case GEM_PHYMNTNC:
-        if (val & GEM_PHYMNTNC_OP_W) {
-            uint32_t phy_addr, reg_num;
-
-            phy_addr = (val & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT;
-            if (phy_addr == BOARD_PHY_ADDRESS) {
-                reg_num = (val & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT;
-                gem_phy_write(s, reg_num, val);
-            }
-        }
-        break;
-    }
-
-    DB_PRINT("newval: 0x%08x\n", s->regs[offset]);
-}
-
-static const MemoryRegionOps gem_ops = {
-    .read = gem_read,
-    .write = gem_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void gem_cleanup(NetClientState *nc)
-{
-    GemState *s = qemu_get_nic_opaque(nc);
-
-    DB_PRINT("\n");
-    s->nic = NULL;
-}
-
-static void gem_set_link(NetClientState *nc)
-{
-    DB_PRINT("\n");
-    phy_update_link(qemu_get_nic_opaque(nc));
-}
-
-static NetClientInfo net_gem_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
-    .size = sizeof(NICState),
-    .can_receive = gem_can_receive,
-    .receive = gem_receive,
-    .cleanup = gem_cleanup,
-    .link_status_changed = gem_set_link,
-};
-
-static int gem_init(SysBusDevice *dev)
-{
-    GemState *s;
-
-    DB_PRINT("\n");
-
-    s = FROM_SYSBUS(GemState, dev);
-    gem_init_register_masks(s);
-    memory_region_init_io(&s->iomem, &gem_ops, s, "enet", sizeof(s->regs));
-    sysbus_init_mmio(dev, &s->iomem);
-    sysbus_init_irq(dev, &s->irq);
-    qemu_macaddr_default_if_unset(&s->conf.macaddr);
-
-    s->nic = qemu_new_nic(&net_gem_info, &s->conf,
-            object_get_typename(OBJECT(dev)), dev->qdev.id, s);
-
-    return 0;
-}
-
-static const VMStateDescription vmstate_cadence_gem = {
-    .name = "cadence_gem",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT32_ARRAY(regs, GemState, GEM_MAXREG),
-        VMSTATE_UINT16_ARRAY(phy_regs, GemState, 32),
-        VMSTATE_UINT8(phy_loop, GemState),
-        VMSTATE_UINT32(rx_desc_addr, GemState),
-        VMSTATE_UINT32(tx_desc_addr, GemState),
-    }
-};
-
-static Property gem_properties[] = {
-    DEFINE_NIC_PROPERTIES(GemState, conf),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void gem_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sdc->init = gem_init;
-    dc->props = gem_properties;
-    dc->vmsd = &vmstate_cadence_gem;
-    dc->reset = gem_reset;
-}
-
-static const TypeInfo gem_info = {
-    .class_init = gem_class_init,
-    .name  = "cadence_gem",
-    .parent = TYPE_SYS_BUS_DEVICE,
-    .instance_size  = sizeof(GemState),
-};
-
-static void gem_register_types(void)
-{
-    type_register_static(&gem_info);
-}
-
-type_init(gem_register_types)
diff --git a/hw/cadence_ttc.c b/hw/cadence_ttc.c
deleted file mode 100644 (file)
index ba584f4..0000000
+++ /dev/null
@@ -1,489 +0,0 @@
-/*
- * Xilinx Zynq cadence TTC model
- *
- * Copyright (c) 2011 Xilinx Inc.
- * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
- * Copyright (c) 2012 PetaLogix Pty Ltd.
- * Written By Haibing Ma
- *            M. Habib
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/sysbus.h"
-#include "qemu/timer.h"
-
-#ifdef CADENCE_TTC_ERR_DEBUG
-#define DB_PRINT(...) do { \
-    fprintf(stderr,  ": %s: ", __func__); \
-    fprintf(stderr, ## __VA_ARGS__); \
-    } while (0);
-#else
-    #define DB_PRINT(...)
-#endif
-
-#define COUNTER_INTR_IV     0x00000001
-#define COUNTER_INTR_M1     0x00000002
-#define COUNTER_INTR_M2     0x00000004
-#define COUNTER_INTR_M3     0x00000008
-#define COUNTER_INTR_OV     0x00000010
-#define COUNTER_INTR_EV     0x00000020
-
-#define COUNTER_CTRL_DIS    0x00000001
-#define COUNTER_CTRL_INT    0x00000002
-#define COUNTER_CTRL_DEC    0x00000004
-#define COUNTER_CTRL_MATCH  0x00000008
-#define COUNTER_CTRL_RST    0x00000010
-
-#define CLOCK_CTRL_PS_EN    0x00000001
-#define CLOCK_CTRL_PS_V     0x0000001e
-
-typedef struct {
-    QEMUTimer *timer;
-    int freq;
-
-    uint32_t reg_clock;
-    uint32_t reg_count;
-    uint32_t reg_value;
-    uint16_t reg_interval;
-    uint16_t reg_match[3];
-    uint32_t reg_intr;
-    uint32_t reg_intr_en;
-    uint32_t reg_event_ctrl;
-    uint32_t reg_event;
-
-    uint64_t cpu_time;
-    unsigned int cpu_time_valid;
-
-    qemu_irq irq;
-} CadenceTimerState;
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    CadenceTimerState timer[3];
-} CadenceTTCState;
-
-static void cadence_timer_update(CadenceTimerState *s)
-{
-    qemu_set_irq(s->irq, !!(s->reg_intr & s->reg_intr_en));
-}
-
-static CadenceTimerState *cadence_timer_from_addr(void *opaque,
-                                        hwaddr offset)
-{
-    unsigned int index;
-    CadenceTTCState *s = (CadenceTTCState *)opaque;
-
-    index = (offset >> 2) % 3;
-
-    return &s->timer[index];
-}
-
-static uint64_t cadence_timer_get_ns(CadenceTimerState *s, uint64_t timer_steps)
-{
-    /* timer_steps has max value of 0x100000000. double check it
-     * (or overflow can happen below) */
-    assert(timer_steps <= 1ULL << 32);
-
-    uint64_t r = timer_steps * 1000000000ULL;
-    if (s->reg_clock & CLOCK_CTRL_PS_EN) {
-        r >>= 16 - (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1);
-    } else {
-        r >>= 16;
-    }
-    r /= (uint64_t)s->freq;
-    return r;
-}
-
-static uint64_t cadence_timer_get_steps(CadenceTimerState *s, uint64_t ns)
-{
-    uint64_t to_divide = 1000000000ULL;
-
-    uint64_t r = ns;
-     /* for very large intervals (> 8s) do some division first to stop
-      * overflow (costs some prescision) */
-    while (r >= 8ULL << 30 && to_divide > 1) {
-        r /= 1000;
-        to_divide /= 1000;
-    }
-    r <<= 16;
-    /* keep early-dividing as needed */
-    while (r >= 8ULL << 30 && to_divide > 1) {
-        r /= 1000;
-        to_divide /= 1000;
-    }
-    r *= (uint64_t)s->freq;
-    if (s->reg_clock & CLOCK_CTRL_PS_EN) {
-        r /= 1 << (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1);
-    }
-
-    r /= to_divide;
-    return r;
-}
-
-/* determine if x is in between a and b, exclusive of a, inclusive of b */
-
-static inline int64_t is_between(int64_t x, int64_t a, int64_t b)
-{
-    if (a < b) {
-        return x > a && x <= b;
-    }
-    return x < a && x >= b;
-}
-
-static void cadence_timer_run(CadenceTimerState *s)
-{
-    int i;
-    int64_t event_interval, next_value;
-
-    assert(s->cpu_time_valid); /* cadence_timer_sync must be called first */
-
-    if (s->reg_count & COUNTER_CTRL_DIS) {
-        s->cpu_time_valid = 0;
-        return;
-    }
-
-    { /* figure out what's going to happen next (rollover or match) */
-        int64_t interval = (uint64_t)((s->reg_count & COUNTER_CTRL_INT) ?
-                (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16;
-        next_value = (s->reg_count & COUNTER_CTRL_DEC) ? -1ULL : interval;
-        for (i = 0; i < 3; ++i) {
-            int64_t cand = (uint64_t)s->reg_match[i] << 16;
-            if (is_between(cand, (uint64_t)s->reg_value, next_value)) {
-                next_value = cand;
-            }
-        }
-    }
-    DB_PRINT("next timer event value: %09llx\n",
-            (unsigned long long)next_value);
-
-    event_interval = next_value - (int64_t)s->reg_value;
-    event_interval = (event_interval < 0) ? -event_interval : event_interval;
-
-    qemu_mod_timer(s->timer, s->cpu_time +
-                cadence_timer_get_ns(s, event_interval));
-}
-
-static void cadence_timer_sync(CadenceTimerState *s)
-{
-    int i;
-    int64_t r, x;
-    int64_t interval = ((s->reg_count & COUNTER_CTRL_INT) ?
-            (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16;
-    uint64_t old_time = s->cpu_time;
-
-    s->cpu_time = qemu_get_clock_ns(vm_clock);
-    DB_PRINT("cpu time: %lld ns\n", (long long)old_time);
-
-    if (!s->cpu_time_valid || old_time == s->cpu_time) {
-        s->cpu_time_valid = 1;
-        return;
-    }
-
-    r = (int64_t)cadence_timer_get_steps(s, s->cpu_time - old_time);
-    x = (int64_t)s->reg_value + ((s->reg_count & COUNTER_CTRL_DEC) ? -r : r);
-
-    for (i = 0; i < 3; ++i) {
-        int64_t m = (int64_t)s->reg_match[i] << 16;
-        if (m > interval) {
-            continue;
-        }
-        /* check to see if match event has occurred. check m +/- interval
-         * to account for match events in wrap around cases */
-        if (is_between(m, s->reg_value, x) ||
-            is_between(m + interval, s->reg_value, x) ||
-            is_between(m - interval, s->reg_value, x)) {
-            s->reg_intr |= (2 << i);
-        }
-    }
-    while (x < 0) {
-        x += interval;
-    }
-    s->reg_value = (uint32_t)(x % interval);
-
-    if (s->reg_value != x) {
-        s->reg_intr |= (s->reg_count & COUNTER_CTRL_INT) ?
-            COUNTER_INTR_IV : COUNTER_INTR_OV;
-    }
-    cadence_timer_update(s);
-}
-
-static void cadence_timer_tick(void *opaque)
-{
-    CadenceTimerState *s = opaque;
-
-    DB_PRINT("\n");
-    cadence_timer_sync(s);
-    cadence_timer_run(s);
-}
-
-static uint32_t cadence_ttc_read_imp(void *opaque, hwaddr offset)
-{
-    CadenceTimerState *s = cadence_timer_from_addr(opaque, offset);
-    uint32_t value;
-
-    cadence_timer_sync(s);
-    cadence_timer_run(s);
-
-    switch (offset) {
-    case 0x00: /* clock control */
-    case 0x04:
-    case 0x08:
-        return s->reg_clock;
-
-    case 0x0c: /* counter control */
-    case 0x10:
-    case 0x14:
-        return s->reg_count;
-
-    case 0x18: /* counter value */
-    case 0x1c:
-    case 0x20:
-        return (uint16_t)(s->reg_value >> 16);
-
-    case 0x24: /* reg_interval counter */
-    case 0x28:
-    case 0x2c:
-        return s->reg_interval;
-
-    case 0x30: /* match 1 counter */
-    case 0x34:
-    case 0x38:
-        return s->reg_match[0];
-
-    case 0x3c: /* match 2 counter */
-    case 0x40:
-    case 0x44:
-        return s->reg_match[1];
-
-    case 0x48: /* match 3 counter */
-    case 0x4c:
-    case 0x50:
-        return s->reg_match[2];
-
-    case 0x54: /* interrupt register */
-    case 0x58:
-    case 0x5c:
-        /* cleared after read */
-        value = s->reg_intr;
-        s->reg_intr = 0;
-        cadence_timer_update(s);
-        return value;
-
-    case 0x60: /* interrupt enable */
-    case 0x64:
-    case 0x68:
-        return s->reg_intr_en;
-
-    case 0x6c:
-    case 0x70:
-    case 0x74:
-        return s->reg_event_ctrl;
-
-    case 0x78:
-    case 0x7c:
-    case 0x80:
-        return s->reg_event;
-
-    default:
-        return 0;
-    }
-}
-
-static uint64_t cadence_ttc_read(void *opaque, hwaddr offset,
-    unsigned size)
-{
-    uint32_t ret = cadence_ttc_read_imp(opaque, offset);
-
-    DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)ret);
-    return ret;
-}
-
-static void cadence_ttc_write(void *opaque, hwaddr offset,
-        uint64_t value, unsigned size)
-{
-    CadenceTimerState *s = cadence_timer_from_addr(opaque, offset);
-
-    DB_PRINT("addr: %08x data %08x\n", (unsigned)offset, (unsigned)value);
-
-    cadence_timer_sync(s);
-
-    switch (offset) {
-    case 0x00: /* clock control */
-    case 0x04:
-    case 0x08:
-        s->reg_clock = value & 0x3F;
-        break;
-
-    case 0x0c: /* counter control */
-    case 0x10:
-    case 0x14:
-        if (value & COUNTER_CTRL_RST) {
-            s->reg_value = 0;
-        }
-        s->reg_count = value & 0x3f & ~COUNTER_CTRL_RST;
-        break;
-
-    case 0x24: /* interval register */
-    case 0x28:
-    case 0x2c:
-        s->reg_interval = value & 0xffff;
-        break;
-
-    case 0x30: /* match register */
-    case 0x34:
-    case 0x38:
-        s->reg_match[0] = value & 0xffff;
-
-    case 0x3c: /* match register */
-    case 0x40:
-    case 0x44:
-        s->reg_match[1] = value & 0xffff;
-
-    case 0x48: /* match register */
-    case 0x4c:
-    case 0x50:
-        s->reg_match[2] = value & 0xffff;
-        break;
-
-    case 0x54: /* interrupt register */
-    case 0x58:
-    case 0x5c:
-        break;
-
-    case 0x60: /* interrupt enable */
-    case 0x64:
-    case 0x68:
-        s->reg_intr_en = value & 0x3f;
-        break;
-
-    case 0x6c: /* event control */
-    case 0x70:
-    case 0x74:
-        s->reg_event_ctrl = value & 0x07;
-        break;
-
-    default:
-        return;
-    }
-
-    cadence_timer_run(s);
-    cadence_timer_update(s);
-}
-
-static const MemoryRegionOps cadence_ttc_ops = {
-    .read = cadence_ttc_read,
-    .write = cadence_ttc_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void cadence_timer_reset(CadenceTimerState *s)
-{
-   s->reg_count = 0x21;
-}
-
-static void cadence_timer_init(uint32_t freq, CadenceTimerState *s)
-{
-    memset(s, 0, sizeof(CadenceTimerState));
-    s->freq = freq;
-
-    cadence_timer_reset(s);
-
-    s->timer = qemu_new_timer_ns(vm_clock, cadence_timer_tick, s);
-}
-
-static int cadence_ttc_init(SysBusDevice *dev)
-{
-    CadenceTTCState *s = FROM_SYSBUS(CadenceTTCState, dev);
-    int i;
-
-    for (i = 0; i < 3; ++i) {
-        cadence_timer_init(133000000, &s->timer[i]);
-        sysbus_init_irq(dev, &s->timer[i].irq);
-    }
-
-    memory_region_init_io(&s->iomem, &cadence_ttc_ops, s, "timer", 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-
-    return 0;
-}
-
-static void cadence_timer_pre_save(void *opaque)
-{
-    cadence_timer_sync((CadenceTimerState *)opaque);
-}
-
-static int cadence_timer_post_load(void *opaque, int version_id)
-{
-    CadenceTimerState *s = opaque;
-
-    s->cpu_time_valid = 0;
-    cadence_timer_sync(s);
-    cadence_timer_run(s);
-    cadence_timer_update(s);
-    return 0;
-}
-
-static const VMStateDescription vmstate_cadence_timer = {
-    .name = "cadence_timer",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .pre_save = cadence_timer_pre_save,
-    .post_load = cadence_timer_post_load,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32(reg_clock, CadenceTimerState),
-        VMSTATE_UINT32(reg_count, CadenceTimerState),
-        VMSTATE_UINT32(reg_value, CadenceTimerState),
-        VMSTATE_UINT16(reg_interval, CadenceTimerState),
-        VMSTATE_UINT16_ARRAY(reg_match, CadenceTimerState, 3),
-        VMSTATE_UINT32(reg_intr, CadenceTimerState),
-        VMSTATE_UINT32(reg_intr_en, CadenceTimerState),
-        VMSTATE_UINT32(reg_event_ctrl, CadenceTimerState),
-        VMSTATE_UINT32(reg_event, CadenceTimerState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_cadence_ttc = {
-    .name = "cadence_TTC",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_STRUCT_ARRAY(timer, CadenceTTCState, 3, 0,
-                            vmstate_cadence_timer,
-                            CadenceTimerState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void cadence_ttc_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sdc->init = cadence_ttc_init;
-    dc->vmsd = &vmstate_cadence_ttc;
-}
-
-static const TypeInfo cadence_ttc_info = {
-    .name  = "cadence_ttc",
-    .parent = TYPE_SYS_BUS_DEVICE,
-    .instance_size  = sizeof(CadenceTTCState),
-    .class_init = cadence_ttc_class_init,
-};
-
-static void cadence_ttc_register_types(void)
-{
-    type_register_static(&cadence_ttc_info);
-}
-
-type_init(cadence_ttc_register_types)
diff --git a/hw/cadence_uart.c b/hw/cadence_uart.c
deleted file mode 100644 (file)
index 421ec99..0000000
+++ /dev/null
@@ -1,518 +0,0 @@
-/*
- * Device model for Cadence UART
- *
- * Copyright (c) 2010 Xilinx Inc.
- * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
- * Copyright (c) 2012 PetaLogix Pty Ltd.
- * Written by Haibing Ma
- *            M.Habib
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/sysbus.h"
-#include "char/char.h"
-#include "qemu/timer.h"
-
-#ifdef CADENCE_UART_ERR_DEBUG
-#define DB_PRINT(...) do { \
-    fprintf(stderr,  ": %s: ", __func__); \
-    fprintf(stderr, ## __VA_ARGS__); \
-    } while (0);
-#else
-    #define DB_PRINT(...)
-#endif
-
-#define UART_SR_INTR_RTRIG     0x00000001
-#define UART_SR_INTR_REMPTY    0x00000002
-#define UART_SR_INTR_RFUL      0x00000004
-#define UART_SR_INTR_TEMPTY    0x00000008
-#define UART_SR_INTR_TFUL      0x00000010
-/* bits fields in CSR that correlate to CISR. If any of these bits are set in
- * SR, then the same bit in CISR is set high too */
-#define UART_SR_TO_CISR_MASK   0x0000001F
-
-#define UART_INTR_ROVR         0x00000020
-#define UART_INTR_FRAME        0x00000040
-#define UART_INTR_PARE         0x00000080
-#define UART_INTR_TIMEOUT      0x00000100
-#define UART_INTR_DMSI         0x00000200
-
-#define UART_SR_RACTIVE    0x00000400
-#define UART_SR_TACTIVE    0x00000800
-#define UART_SR_FDELT      0x00001000
-
-#define UART_CR_RXRST       0x00000001
-#define UART_CR_TXRST       0x00000002
-#define UART_CR_RX_EN       0x00000004
-#define UART_CR_RX_DIS      0x00000008
-#define UART_CR_TX_EN       0x00000010
-#define UART_CR_TX_DIS      0x00000020
-#define UART_CR_RST_TO      0x00000040
-#define UART_CR_STARTBRK    0x00000080
-#define UART_CR_STOPBRK     0x00000100
-
-#define UART_MR_CLKS            0x00000001
-#define UART_MR_CHRL            0x00000006
-#define UART_MR_CHRL_SH         1
-#define UART_MR_PAR             0x00000038
-#define UART_MR_PAR_SH          3
-#define UART_MR_NBSTOP          0x000000C0
-#define UART_MR_NBSTOP_SH       6
-#define UART_MR_CHMODE          0x00000300
-#define UART_MR_CHMODE_SH       8
-#define UART_MR_UCLKEN          0x00000400
-#define UART_MR_IRMODE          0x00000800
-
-#define UART_DATA_BITS_6       (0x3 << UART_MR_CHRL_SH)
-#define UART_DATA_BITS_7       (0x2 << UART_MR_CHRL_SH)
-#define UART_PARITY_ODD        (0x1 << UART_MR_PAR_SH)
-#define UART_PARITY_EVEN       (0x0 << UART_MR_PAR_SH)
-#define UART_STOP_BITS_1       (0x3 << UART_MR_NBSTOP_SH)
-#define UART_STOP_BITS_2       (0x2 << UART_MR_NBSTOP_SH)
-#define NORMAL_MODE            (0x0 << UART_MR_CHMODE_SH)
-#define ECHO_MODE              (0x1 << UART_MR_CHMODE_SH)
-#define LOCAL_LOOPBACK         (0x2 << UART_MR_CHMODE_SH)
-#define REMOTE_LOOPBACK        (0x3 << UART_MR_CHMODE_SH)
-
-#define RX_FIFO_SIZE           16
-#define TX_FIFO_SIZE           16
-#define UART_INPUT_CLK         50000000
-
-#define R_CR       (0x00/4)
-#define R_MR       (0x04/4)
-#define R_IER      (0x08/4)
-#define R_IDR      (0x0C/4)
-#define R_IMR      (0x10/4)
-#define R_CISR     (0x14/4)
-#define R_BRGR     (0x18/4)
-#define R_RTOR     (0x1C/4)
-#define R_RTRIG    (0x20/4)
-#define R_MCR      (0x24/4)
-#define R_MSR      (0x28/4)
-#define R_SR       (0x2C/4)
-#define R_TX_RX    (0x30/4)
-#define R_BDIV     (0x34/4)
-#define R_FDEL     (0x38/4)
-#define R_PMIN     (0x3C/4)
-#define R_PWID     (0x40/4)
-#define R_TTRIG    (0x44/4)
-
-#define R_MAX (R_TTRIG + 1)
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    uint32_t r[R_MAX];
-    uint8_t r_fifo[RX_FIFO_SIZE];
-    uint32_t rx_wpos;
-    uint32_t rx_count;
-    uint64_t char_tx_time;
-    CharDriverState *chr;
-    qemu_irq irq;
-    struct QEMUTimer *fifo_trigger_handle;
-    struct QEMUTimer *tx_time_handle;
-} UartState;
-
-static void uart_update_status(UartState *s)
-{
-    s->r[R_CISR] |= s->r[R_SR] & UART_SR_TO_CISR_MASK;
-    qemu_set_irq(s->irq, !!(s->r[R_IMR] & s->r[R_CISR]));
-}
-
-static void fifo_trigger_update(void *opaque)
-{
-    UartState *s = (UartState *)opaque;
-
-    s->r[R_CISR] |= UART_INTR_TIMEOUT;
-
-    uart_update_status(s);
-}
-
-static void uart_tx_redo(UartState *s)
-{
-    uint64_t new_tx_time = qemu_get_clock_ns(vm_clock);
-
-    qemu_mod_timer(s->tx_time_handle, new_tx_time + s->char_tx_time);
-
-    s->r[R_SR] |= UART_SR_INTR_TEMPTY;
-
-    uart_update_status(s);
-}
-
-static void uart_tx_write(void *opaque)
-{
-    UartState *s = (UartState *)opaque;
-
-    uart_tx_redo(s);
-}
-
-static void uart_rx_reset(UartState *s)
-{
-    s->rx_wpos = 0;
-    s->rx_count = 0;
-    qemu_chr_accept_input(s->chr);
-
-    s->r[R_SR] |= UART_SR_INTR_REMPTY;
-    s->r[R_SR] &= ~UART_SR_INTR_RFUL;
-}
-
-static void uart_tx_reset(UartState *s)
-{
-    s->r[R_SR] |= UART_SR_INTR_TEMPTY;
-    s->r[R_SR] &= ~UART_SR_INTR_TFUL;
-}
-
-static void uart_send_breaks(UartState *s)
-{
-    int break_enabled = 1;
-
-    qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
-                               &break_enabled);
-}
-
-static void uart_parameters_setup(UartState *s)
-{
-    QEMUSerialSetParams ssp;
-    unsigned int baud_rate, packet_size;
-
-    baud_rate = (s->r[R_MR] & UART_MR_CLKS) ?
-            UART_INPUT_CLK / 8 : UART_INPUT_CLK;
-
-    ssp.speed = baud_rate / (s->r[R_BRGR] * (s->r[R_BDIV] + 1));
-    packet_size = 1;
-
-    switch (s->r[R_MR] & UART_MR_PAR) {
-    case UART_PARITY_EVEN:
-        ssp.parity = 'E';
-        packet_size++;
-        break;
-    case UART_PARITY_ODD:
-        ssp.parity = 'O';
-        packet_size++;
-        break;
-    default:
-        ssp.parity = 'N';
-        break;
-    }
-
-    switch (s->r[R_MR] & UART_MR_CHRL) {
-    case UART_DATA_BITS_6:
-        ssp.data_bits = 6;
-        break;
-    case UART_DATA_BITS_7:
-        ssp.data_bits = 7;
-        break;
-    default:
-        ssp.data_bits = 8;
-        break;
-    }
-
-    switch (s->r[R_MR] & UART_MR_NBSTOP) {
-    case UART_STOP_BITS_1:
-        ssp.stop_bits = 1;
-        break;
-    default:
-        ssp.stop_bits = 2;
-        break;
-    }
-
-    packet_size += ssp.data_bits + ssp.stop_bits;
-    s->char_tx_time = (get_ticks_per_sec() / ssp.speed) * packet_size;
-    qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
-}
-
-static int uart_can_receive(void *opaque)
-{
-    UartState *s = (UartState *)opaque;
-
-    return RX_FIFO_SIZE - s->rx_count;
-}
-
-static void uart_ctrl_update(UartState *s)
-{
-    if (s->r[R_CR] & UART_CR_TXRST) {
-        uart_tx_reset(s);
-    }
-
-    if (s->r[R_CR] & UART_CR_RXRST) {
-        uart_rx_reset(s);
-    }
-
-    s->r[R_CR] &= ~(UART_CR_TXRST | UART_CR_RXRST);
-
-    if ((s->r[R_CR] & UART_CR_TX_EN) && !(s->r[R_CR] & UART_CR_TX_DIS)) {
-            uart_tx_redo(s);
-    }
-
-    if (s->r[R_CR] & UART_CR_STARTBRK && !(s->r[R_CR] & UART_CR_STOPBRK)) {
-        uart_send_breaks(s);
-    }
-}
-
-static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size)
-{
-    UartState *s = (UartState *)opaque;
-    uint64_t new_rx_time = qemu_get_clock_ns(vm_clock);
-    int i;
-
-    if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) {
-        return;
-    }
-
-    s->r[R_SR] &= ~UART_SR_INTR_REMPTY;
-
-    if (s->rx_count == RX_FIFO_SIZE) {
-        s->r[R_CISR] |= UART_INTR_ROVR;
-    } else {
-        for (i = 0; i < size; i++) {
-            s->r_fifo[s->rx_wpos] = buf[i];
-            s->rx_wpos = (s->rx_wpos + 1) % RX_FIFO_SIZE;
-            s->rx_count++;
-
-            if (s->rx_count == RX_FIFO_SIZE) {
-                s->r[R_SR] |= UART_SR_INTR_RFUL;
-                break;
-            }
-
-            if (s->rx_count >= s->r[R_RTRIG]) {
-                s->r[R_SR] |= UART_SR_INTR_RTRIG;
-            }
-        }
-        qemu_mod_timer(s->fifo_trigger_handle, new_rx_time +
-                                                (s->char_tx_time * 4));
-    }
-    uart_update_status(s);
-}
-
-static void uart_write_tx_fifo(UartState *s, const uint8_t *buf, int size)
-{
-    if ((s->r[R_CR] & UART_CR_TX_DIS) || !(s->r[R_CR] & UART_CR_TX_EN)) {
-        return;
-    }
-
-    while (size) {
-        size -= qemu_chr_fe_write(s->chr, buf, size);
-    }
-}
-
-static void uart_receive(void *opaque, const uint8_t *buf, int size)
-{
-    UartState *s = (UartState *)opaque;
-    uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE;
-
-    if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) {
-        uart_write_rx_fifo(opaque, buf, size);
-    }
-    if (ch_mode == REMOTE_LOOPBACK || ch_mode == ECHO_MODE) {
-        uart_write_tx_fifo(s, buf, size);
-    }
-}
-
-static void uart_event(void *opaque, int event)
-{
-    UartState *s = (UartState *)opaque;
-    uint8_t buf = '\0';
-
-    if (event == CHR_EVENT_BREAK) {
-        uart_write_rx_fifo(opaque, &buf, 1);
-    }
-
-    uart_update_status(s);
-}
-
-static void uart_read_rx_fifo(UartState *s, uint32_t *c)
-{
-    if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) {
-        return;
-    }
-
-    s->r[R_SR] &= ~UART_SR_INTR_RFUL;
-
-    if (s->rx_count) {
-        uint32_t rx_rpos =
-                (RX_FIFO_SIZE + s->rx_wpos - s->rx_count) % RX_FIFO_SIZE;
-        *c = s->r_fifo[rx_rpos];
-        s->rx_count--;
-
-        if (!s->rx_count) {
-            s->r[R_SR] |= UART_SR_INTR_REMPTY;
-        }
-        qemu_chr_accept_input(s->chr);
-    } else {
-        *c = 0;
-        s->r[R_SR] |= UART_SR_INTR_REMPTY;
-    }
-
-    if (s->rx_count < s->r[R_RTRIG]) {
-        s->r[R_SR] &= ~UART_SR_INTR_RTRIG;
-    }
-    uart_update_status(s);
-}
-
-static void uart_write(void *opaque, hwaddr offset,
-                          uint64_t value, unsigned size)
-{
-    UartState *s = (UartState *)opaque;
-
-    DB_PRINT(" offset:%x data:%08x\n", (unsigned)offset, (unsigned)value);
-    offset >>= 2;
-    switch (offset) {
-    case R_IER: /* ier (wts imr) */
-        s->r[R_IMR] |= value;
-        break;
-    case R_IDR: /* idr (wtc imr) */
-        s->r[R_IMR] &= ~value;
-        break;
-    case R_IMR: /* imr (read only) */
-        break;
-    case R_CISR: /* cisr (wtc) */
-        s->r[R_CISR] &= ~value;
-        break;
-    case R_TX_RX: /* UARTDR */
-        switch (s->r[R_MR] & UART_MR_CHMODE) {
-        case NORMAL_MODE:
-            uart_write_tx_fifo(s, (uint8_t *) &value, 1);
-            break;
-        case LOCAL_LOOPBACK:
-            uart_write_rx_fifo(opaque, (uint8_t *) &value, 1);
-            break;
-        }
-        break;
-    default:
-        s->r[offset] = value;
-    }
-
-    switch (offset) {
-    case R_CR:
-        uart_ctrl_update(s);
-        break;
-    case R_MR:
-        uart_parameters_setup(s);
-        break;
-    }
-}
-
-static uint64_t uart_read(void *opaque, hwaddr offset,
-        unsigned size)
-{
-    UartState *s = (UartState *)opaque;
-    uint32_t c = 0;
-
-    offset >>= 2;
-    if (offset >= R_MAX) {
-        c = 0;
-    } else if (offset == R_TX_RX) {
-        uart_read_rx_fifo(s, &c);
-    } else {
-       c = s->r[offset];
-    }
-
-    DB_PRINT(" offset:%x data:%08x\n", (unsigned)(offset << 2), (unsigned)c);
-    return c;
-}
-
-static const MemoryRegionOps uart_ops = {
-    .read = uart_read,
-    .write = uart_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void cadence_uart_reset(UartState *s)
-{
-    s->r[R_CR] = 0x00000128;
-    s->r[R_IMR] = 0;
-    s->r[R_CISR] = 0;
-    s->r[R_RTRIG] = 0x00000020;
-    s->r[R_BRGR] = 0x0000000F;
-    s->r[R_TTRIG] = 0x00000020;
-
-    uart_rx_reset(s);
-    uart_tx_reset(s);
-
-    s->rx_count = 0;
-    s->rx_wpos = 0;
-}
-
-static int cadence_uart_init(SysBusDevice *dev)
-{
-    UartState *s = FROM_SYSBUS(UartState, dev);
-
-    memory_region_init_io(&s->iomem, &uart_ops, s, "uart", 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-    sysbus_init_irq(dev, &s->irq);
-
-    s->fifo_trigger_handle = qemu_new_timer_ns(vm_clock,
-            (QEMUTimerCB *)fifo_trigger_update, s);
-
-    s->tx_time_handle = qemu_new_timer_ns(vm_clock,
-            (QEMUTimerCB *)uart_tx_write, s);
-
-    s->char_tx_time = (get_ticks_per_sec() / 9600) * 10;
-
-    s->chr = qemu_char_get_next_serial();
-
-    cadence_uart_reset(s);
-
-    if (s->chr) {
-        qemu_chr_add_handlers(s->chr, uart_can_receive, uart_receive,
-                              uart_event, s);
-    }
-
-    return 0;
-}
-
-static int cadence_uart_post_load(void *opaque, int version_id)
-{
-    UartState *s = opaque;
-
-    uart_parameters_setup(s);
-    uart_update_status(s);
-    return 0;
-}
-
-static const VMStateDescription vmstate_cadence_uart = {
-    .name = "cadence_uart",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .post_load = cadence_uart_post_load,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32_ARRAY(r, UartState, R_MAX),
-        VMSTATE_UINT8_ARRAY(r_fifo, UartState, RX_FIFO_SIZE),
-        VMSTATE_UINT32(rx_count, UartState),
-        VMSTATE_UINT32(rx_wpos, UartState),
-        VMSTATE_TIMER(fifo_trigger_handle, UartState),
-        VMSTATE_TIMER(tx_time_handle, UartState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void cadence_uart_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sdc->init = cadence_uart_init;
-    dc->vmsd = &vmstate_cadence_uart;
-}
-
-static const TypeInfo cadence_uart_info = {
-    .name          = "cadence_uart",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(UartState),
-    .class_init    = cadence_uart_class_init,
-};
-
-static void cadence_uart_register_types(void)
-{
-    type_register_static(&cadence_uart_info);
-}
-
-type_init(cadence_uart_register_types)
diff --git a/hw/ccid-card-emulated.c b/hw/ccid-card-emulated.c
deleted file mode 100644 (file)
index c8f8ba3..0000000
+++ /dev/null
@@ -1,602 +0,0 @@
-/*
- * CCID Card Device. Emulated card.
- *
- * Copyright (c) 2011 Red Hat.
- * Written by Alon Levy.
- *
- * This code is licensed under the GNU LGPL, version 2 or later.
- */
-
-/*
- * It can be used to provide access to the local hardware in a non exclusive
- * way, or it can use certificates. It requires the usb-ccid bus.
- *
- * Usage 1: standard, mirror hardware reader+card:
- * qemu .. -usb -device usb-ccid -device ccid-card-emulated
- *
- * Usage 2: use certificates, no hardware required
- * one time: create the certificates:
- *  for i in 1 2 3; do
- *      certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=user$i" -n user$i
- *  done
- * qemu .. -usb -device usb-ccid \
- *  -device ccid-card-emulated,cert1=user1,cert2=user2,cert3=user3
- *
- * If you use a non default db for the certificates you can specify it using
- * the db parameter.
- */
-
-#include <eventt.h>
-#include <vevent.h>
-#include <vreader.h>
-#include <vcard_emul.h>
-
-#include "qemu/thread.h"
-#include "char/char.h"
-#include "monitor/monitor.h"
-#include "hw/ccid.h"
-
-#define DPRINTF(card, lvl, fmt, ...) \
-do {\
-    if (lvl <= card->debug) {\
-        printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__);\
-    } \
-} while (0)
-
-#define EMULATED_DEV_NAME "ccid-card-emulated"
-
-#define BACKEND_NSS_EMULATED_NAME "nss-emulated"
-#define BACKEND_CERTIFICATES_NAME "certificates"
-
-enum {
-    BACKEND_NSS_EMULATED = 1,
-    BACKEND_CERTIFICATES
-};
-
-#define DEFAULT_BACKEND BACKEND_NSS_EMULATED
-
-typedef struct EmulatedState EmulatedState;
-
-enum {
-    EMUL_READER_INSERT = 0,
-    EMUL_READER_REMOVE,
-    EMUL_CARD_INSERT,
-    EMUL_CARD_REMOVE,
-    EMUL_GUEST_APDU,
-    EMUL_RESPONSE_APDU,
-    EMUL_ERROR,
-};
-
-static const char *emul_event_to_string(uint32_t emul_event)
-{
-    switch (emul_event) {
-    case EMUL_READER_INSERT:
-        return "EMUL_READER_INSERT";
-    case EMUL_READER_REMOVE:
-        return "EMUL_READER_REMOVE";
-    case EMUL_CARD_INSERT:
-        return "EMUL_CARD_INSERT";
-    case EMUL_CARD_REMOVE:
-        return "EMUL_CARD_REMOVE";
-    case EMUL_GUEST_APDU:
-        return "EMUL_GUEST_APDU";
-    case EMUL_RESPONSE_APDU:
-        return "EMUL_RESPONSE_APDU";
-    case EMUL_ERROR:
-        return "EMUL_ERROR";
-    }
-    return "UNKNOWN";
-}
-
-typedef struct EmulEvent {
-    QSIMPLEQ_ENTRY(EmulEvent) entry;
-    union {
-        struct {
-            uint32_t type;
-        } gen;
-        struct {
-            uint32_t type;
-            uint64_t code;
-        } error;
-        struct {
-            uint32_t type;
-            uint32_t len;
-            uint8_t data[];
-        } data;
-    } p;
-} EmulEvent;
-
-#define MAX_ATR_SIZE 40
-struct EmulatedState {
-    CCIDCardState base;
-    uint8_t  debug;
-    char    *backend_str;
-    uint32_t backend;
-    char    *cert1;
-    char    *cert2;
-    char    *cert3;
-    char    *db;
-    uint8_t  atr[MAX_ATR_SIZE];
-    uint8_t  atr_length;
-    QSIMPLEQ_HEAD(event_list, EmulEvent) event_list;
-    QemuMutex event_list_mutex;
-    QemuThread event_thread_id;
-    VReader *reader;
-    QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list;
-    QemuMutex vreader_mutex; /* and guest_apdu_list mutex */
-    QemuMutex handle_apdu_mutex;
-    QemuCond handle_apdu_cond;
-    int      pipe[2];
-    int      quit_apdu_thread;
-    QemuThread apdu_thread_id;
-};
-
-static void emulated_apdu_from_guest(CCIDCardState *base,
-    const uint8_t *apdu, uint32_t len)
-{
-    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
-    EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len);
-
-    assert(event);
-    event->p.data.type = EMUL_GUEST_APDU;
-    event->p.data.len = len;
-    memcpy(event->p.data.data, apdu, len);
-    qemu_mutex_lock(&card->vreader_mutex);
-    QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry);
-    qemu_mutex_unlock(&card->vreader_mutex);
-    qemu_mutex_lock(&card->handle_apdu_mutex);
-    qemu_cond_signal(&card->handle_apdu_cond);
-    qemu_mutex_unlock(&card->handle_apdu_mutex);
-}
-
-static const uint8_t *emulated_get_atr(CCIDCardState *base, uint32_t *len)
-{
-    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
-
-    *len = card->atr_length;
-    return card->atr;
-}
-
-static void emulated_push_event(EmulatedState *card, EmulEvent *event)
-{
-    qemu_mutex_lock(&card->event_list_mutex);
-    QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry);
-    qemu_mutex_unlock(&card->event_list_mutex);
-    if (write(card->pipe[1], card, 1) != 1) {
-        DPRINTF(card, 1, "write to pipe failed\n");
-    }
-}
-
-static void emulated_push_type(EmulatedState *card, uint32_t type)
-{
-    EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent));
-
-    assert(event);
-    event->p.gen.type = type;
-    emulated_push_event(card, event);
-}
-
-static void emulated_push_error(EmulatedState *card, uint64_t code)
-{
-    EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent));
-
-    assert(event);
-    event->p.error.type = EMUL_ERROR;
-    event->p.error.code = code;
-    emulated_push_event(card, event);
-}
-
-static void emulated_push_data_type(EmulatedState *card, uint32_t type,
-    const uint8_t *data, uint32_t len)
-{
-    EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len);
-
-    assert(event);
-    event->p.data.type = type;
-    event->p.data.len = len;
-    memcpy(event->p.data.data, data, len);
-    emulated_push_event(card, event);
-}
-
-static void emulated_push_reader_insert(EmulatedState *card)
-{
-    emulated_push_type(card, EMUL_READER_INSERT);
-}
-
-static void emulated_push_reader_remove(EmulatedState *card)
-{
-    emulated_push_type(card, EMUL_READER_REMOVE);
-}
-
-static void emulated_push_card_insert(EmulatedState *card,
-    const uint8_t *atr, uint32_t len)
-{
-    emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len);
-}
-
-static void emulated_push_card_remove(EmulatedState *card)
-{
-    emulated_push_type(card, EMUL_CARD_REMOVE);
-}
-
-static void emulated_push_response_apdu(EmulatedState *card,
-    const uint8_t *apdu, uint32_t len)
-{
-    emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len);
-}
-
-#define APDU_BUF_SIZE 270
-static void *handle_apdu_thread(void* arg)
-{
-    EmulatedState *card = arg;
-    uint8_t recv_data[APDU_BUF_SIZE];
-    int recv_len;
-    VReaderStatus reader_status;
-    EmulEvent *event;
-
-    while (1) {
-        qemu_mutex_lock(&card->handle_apdu_mutex);
-        qemu_cond_wait(&card->handle_apdu_cond, &card->handle_apdu_mutex);
-        qemu_mutex_unlock(&card->handle_apdu_mutex);
-        if (card->quit_apdu_thread) {
-            card->quit_apdu_thread = 0; /* debugging */
-            break;
-        }
-        qemu_mutex_lock(&card->vreader_mutex);
-        while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) {
-            event = QSIMPLEQ_FIRST(&card->guest_apdu_list);
-            assert((unsigned long)event > 1000);
-            QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry);
-            if (event->p.data.type != EMUL_GUEST_APDU) {
-                DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n");
-                g_free(event);
-                continue;
-            }
-            if (card->reader == NULL) {
-                DPRINTF(card, 1, "reader is NULL\n");
-                g_free(event);
-                continue;
-            }
-            recv_len = sizeof(recv_data);
-            reader_status = vreader_xfr_bytes(card->reader,
-                    event->p.data.data, event->p.data.len,
-                    recv_data, &recv_len);
-            DPRINTF(card, 2, "got back apdu of length %d\n", recv_len);
-            if (reader_status == VREADER_OK) {
-                emulated_push_response_apdu(card, recv_data, recv_len);
-            } else {
-                emulated_push_error(card, reader_status);
-            }
-            g_free(event);
-        }
-        qemu_mutex_unlock(&card->vreader_mutex);
-    }
-    return NULL;
-}
-
-static void *event_thread(void *arg)
-{
-    int atr_len = MAX_ATR_SIZE;
-    uint8_t atr[MAX_ATR_SIZE];
-    VEvent *event = NULL;
-    EmulatedState *card = arg;
-
-    while (1) {
-        const char *reader_name;
-
-        event = vevent_wait_next_vevent();
-        if (event == NULL || event->type == VEVENT_LAST) {
-            break;
-        }
-        if (event->type != VEVENT_READER_INSERT) {
-            if (card->reader == NULL && event->reader != NULL) {
-                /* Happens after device_add followed by card remove or insert.
-                 * XXX: create synthetic add_reader events if vcard_emul_init
-                 * already called, which happens if device_del and device_add
-                 * are called */
-                card->reader = vreader_reference(event->reader);
-            } else {
-                if (event->reader != card->reader) {
-                    fprintf(stderr,
-                        "ERROR: wrong reader: quiting event_thread\n");
-                    break;
-                }
-            }
-        }
-        switch (event->type) {
-        case VEVENT_READER_INSERT:
-            /* TODO: take a specific reader. i.e. track which reader
-             * we are seeing here, check it is the one we want (the first,
-             * or by a particular name), and ignore if we don't want it.
-             */
-            reader_name = vreader_get_name(event->reader);
-            if (card->reader != NULL) {
-                DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n",
-                    vreader_get_name(card->reader), reader_name);
-                qemu_mutex_lock(&card->vreader_mutex);
-                vreader_free(card->reader);
-                qemu_mutex_unlock(&card->vreader_mutex);
-                emulated_push_reader_remove(card);
-            }
-            qemu_mutex_lock(&card->vreader_mutex);
-            DPRINTF(card, 2, "READER INSERT %s\n", reader_name);
-            card->reader = vreader_reference(event->reader);
-            qemu_mutex_unlock(&card->vreader_mutex);
-            emulated_push_reader_insert(card);
-            break;
-        case VEVENT_READER_REMOVE:
-            DPRINTF(card, 2, " READER REMOVE: %s\n",
-                    vreader_get_name(event->reader));
-            qemu_mutex_lock(&card->vreader_mutex);
-            vreader_free(card->reader);
-            card->reader = NULL;
-            qemu_mutex_unlock(&card->vreader_mutex);
-            emulated_push_reader_remove(card);
-            break;
-        case VEVENT_CARD_INSERT:
-            /* get the ATR (intended as a response to a power on from the
-             * reader */
-            atr_len = MAX_ATR_SIZE;
-            vreader_power_on(event->reader, atr, &atr_len);
-            card->atr_length = (uint8_t)atr_len;
-            DPRINTF(card, 2, " CARD INSERT\n");
-            emulated_push_card_insert(card, atr, atr_len);
-            break;
-        case VEVENT_CARD_REMOVE:
-            DPRINTF(card, 2, " CARD REMOVE\n");
-            emulated_push_card_remove(card);
-            break;
-        case VEVENT_LAST: /* quit */
-            vevent_delete(event);
-            return NULL;
-            break;
-        default:
-            break;
-        }
-        vevent_delete(event);
-    }
-    return NULL;
-}
-
-static void pipe_read(void *opaque)
-{
-    EmulatedState *card = opaque;
-    EmulEvent *event, *next;
-    char dummy;
-    int len;
-
-    do {
-        len = read(card->pipe[0], &dummy, sizeof(dummy));
-    } while (len == sizeof(dummy));
-    qemu_mutex_lock(&card->event_list_mutex);
-    QSIMPLEQ_FOREACH_SAFE(event, &card->event_list, entry, next) {
-        DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type));
-        switch (event->p.gen.type) {
-        case EMUL_RESPONSE_APDU:
-            ccid_card_send_apdu_to_guest(&card->base, event->p.data.data,
-                event->p.data.len);
-            break;
-        case EMUL_READER_INSERT:
-            ccid_card_ccid_attach(&card->base);
-            break;
-        case EMUL_READER_REMOVE:
-            ccid_card_ccid_detach(&card->base);
-            break;
-        case EMUL_CARD_INSERT:
-            assert(event->p.data.len <= MAX_ATR_SIZE);
-            card->atr_length = event->p.data.len;
-            memcpy(card->atr, event->p.data.data, card->atr_length);
-            ccid_card_card_inserted(&card->base);
-            break;
-        case EMUL_CARD_REMOVE:
-            ccid_card_card_removed(&card->base);
-            break;
-        case EMUL_ERROR:
-            ccid_card_card_error(&card->base, event->p.error.code);
-            break;
-        default:
-            DPRINTF(card, 2, "unexpected event\n");
-            break;
-        }
-        g_free(event);
-    }
-    QSIMPLEQ_INIT(&card->event_list);
-    qemu_mutex_unlock(&card->event_list_mutex);
-}
-
-static int init_pipe_signaling(EmulatedState *card)
-{
-    if (pipe(card->pipe) < 0) {
-        DPRINTF(card, 2, "pipe creation failed\n");
-        return -1;
-    }
-    fcntl(card->pipe[0], F_SETFL, O_NONBLOCK);
-    fcntl(card->pipe[1], F_SETFL, O_NONBLOCK);
-    fcntl(card->pipe[0], F_SETOWN, getpid());
-    qemu_set_fd_handler(card->pipe[0], pipe_read, NULL, card);
-    return 0;
-}
-
-#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb"
-#define CERTIFICATES_ARGS_TEMPLATE\
-    "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)"
-
-static int wrap_vcard_emul_init(VCardEmulOptions *options)
-{
-    static int called;
-    static int options_was_null;
-
-    if (called) {
-        if ((options == NULL) != options_was_null) {
-            printf("%s: warning: running emulated with certificates"
-                   " and emulated side by side is not supported\n",
-                   __func__);
-            return VCARD_EMUL_FAIL;
-        }
-        vcard_emul_replay_insertion_events();
-        return VCARD_EMUL_OK;
-    }
-    options_was_null = (options == NULL);
-    called = 1;
-    return vcard_emul_init(options);
-}
-
-static int emulated_initialize_vcard_from_certificates(EmulatedState *card)
-{
-    char emul_args[200];
-    VCardEmulOptions *options = NULL;
-
-    snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE,
-        card->db ? card->db : CERTIFICATES_DEFAULT_DB,
-        card->cert1, card->cert2, card->cert3);
-    options = vcard_emul_options(emul_args);
-    if (options == NULL) {
-        printf("%s: warning: not using certificates due to"
-               " initialization error\n", __func__);
-    }
-    return wrap_vcard_emul_init(options);
-}
-
-typedef struct EnumTable {
-    const char *name;
-    uint32_t value;
-} EnumTable;
-
-EnumTable backend_enum_table[] = {
-    {BACKEND_NSS_EMULATED_NAME, BACKEND_NSS_EMULATED},
-    {BACKEND_CERTIFICATES_NAME, BACKEND_CERTIFICATES},
-    {NULL, 0},
-};
-
-static uint32_t parse_enumeration(char *str,
-    EnumTable *table, uint32_t not_found_value)
-{
-    uint32_t ret = not_found_value;
-
-    while (table->name != NULL) {
-        if (strcmp(table->name, str) == 0) {
-            ret = table->value;
-            break;
-        }
-        table++;
-    }
-    return ret;
-}
-
-static int emulated_initfn(CCIDCardState *base)
-{
-    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
-    VCardEmulError ret;
-    EnumTable *ptable;
-
-    QSIMPLEQ_INIT(&card->event_list);
-    QSIMPLEQ_INIT(&card->guest_apdu_list);
-    qemu_mutex_init(&card->event_list_mutex);
-    qemu_mutex_init(&card->vreader_mutex);
-    qemu_mutex_init(&card->handle_apdu_mutex);
-    qemu_cond_init(&card->handle_apdu_cond);
-    card->reader = NULL;
-    card->quit_apdu_thread = 0;
-    if (init_pipe_signaling(card) < 0) {
-        return -1;
-    }
-    card->backend = parse_enumeration(card->backend_str, backend_enum_table, 0);
-    if (card->backend == 0) {
-        printf("unknown backend, must be one of:\n");
-        for (ptable = backend_enum_table; ptable->name != NULL; ++ptable) {
-            printf("%s\n", ptable->name);
-        }
-        return -1;
-    }
-
-    /* TODO: a passthru backened that works on local machine. third card type?*/
-    if (card->backend == BACKEND_CERTIFICATES) {
-        if (card->cert1 != NULL && card->cert2 != NULL && card->cert3 != NULL) {
-            ret = emulated_initialize_vcard_from_certificates(card);
-        } else {
-            printf("%s: you must provide all three certs for"
-                   " certificates backend\n", EMULATED_DEV_NAME);
-            return -1;
-        }
-    } else {
-        if (card->backend != BACKEND_NSS_EMULATED) {
-            printf("%s: bad backend specified. The options are:\n%s (default),"
-                " %s.\n", EMULATED_DEV_NAME, BACKEND_NSS_EMULATED_NAME,
-                BACKEND_CERTIFICATES_NAME);
-            return -1;
-        }
-        if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) {
-            printf("%s: unexpected cert parameters to nss emulated backend\n",
-                   EMULATED_DEV_NAME);
-            return -1;
-        }
-        /* default to mirroring the local hardware readers */
-        ret = wrap_vcard_emul_init(NULL);
-    }
-    if (ret != VCARD_EMUL_OK) {
-        printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME);
-        return -1;
-    }
-    qemu_thread_create(&card->event_thread_id, event_thread, card,
-                       QEMU_THREAD_JOINABLE);
-    qemu_thread_create(&card->apdu_thread_id, handle_apdu_thread, card,
-                       QEMU_THREAD_JOINABLE);
-    return 0;
-}
-
-static int emulated_exitfn(CCIDCardState *base)
-{
-    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
-    VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL);
-
-    vevent_queue_vevent(vevent); /* stop vevent thread */
-    qemu_thread_join(&card->event_thread_id);
-
-    card->quit_apdu_thread = 1; /* stop handle_apdu thread */
-    qemu_cond_signal(&card->handle_apdu_cond);
-    qemu_thread_join(&card->apdu_thread_id);
-
-    /* threads exited, can destroy all condvars/mutexes */
-    qemu_cond_destroy(&card->handle_apdu_cond);
-    qemu_mutex_destroy(&card->handle_apdu_mutex);
-    qemu_mutex_destroy(&card->vreader_mutex);
-    qemu_mutex_destroy(&card->event_list_mutex);
-    return 0;
-}
-
-static Property emulated_card_properties[] = {
-    DEFINE_PROP_STRING("backend", EmulatedState, backend_str),
-    DEFINE_PROP_STRING("cert1", EmulatedState, cert1),
-    DEFINE_PROP_STRING("cert2", EmulatedState, cert2),
-    DEFINE_PROP_STRING("cert3", EmulatedState, cert3),
-    DEFINE_PROP_STRING("db", EmulatedState, db),
-    DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void emulated_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    CCIDCardClass *cc = CCID_CARD_CLASS(klass);
-
-    cc->initfn = emulated_initfn;
-    cc->exitfn = emulated_exitfn;
-    cc->get_atr = emulated_get_atr;
-    cc->apdu_from_guest = emulated_apdu_from_guest;
-    dc->desc = "emulated smartcard";
-    dc->props = emulated_card_properties;
-}
-
-static const TypeInfo emulated_card_info = {
-    .name          = EMULATED_DEV_NAME,
-    .parent        = TYPE_CCID_CARD,
-    .instance_size = sizeof(EmulatedState),
-    .class_init    = emulated_class_initfn,
-};
-
-static void ccid_card_emulated_register_types(void)
-{
-    type_register_static(&emulated_card_info);
-}
-
-type_init(ccid_card_emulated_register_types)
diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c
deleted file mode 100644 (file)
index 984bd0b..0000000
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * CCID Passthru Card Device emulation
- *
- * Copyright (c) 2011 Red Hat.
- * Written by Alon Levy.
- *
- * This work is licensed under the terms of the GNU GPL, version 2.1 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#include "char/char.h"
-#include "qemu/sockets.h"
-#include "monitor/monitor.h"
-#include "hw/ccid.h"
-#include "libcacard/vscard_common.h"
-
-#define DPRINTF(card, lvl, fmt, ...)                    \
-do {                                                    \
-    if (lvl <= card->debug) {                           \
-        printf("ccid-card-passthru: " fmt , ## __VA_ARGS__);     \
-    }                                                   \
-} while (0)
-
-#define D_WARN 1
-#define D_INFO 2
-#define D_MORE_INFO 3
-#define D_VERBOSE 4
-
-/* TODO: do we still need this? */
-uint8_t DEFAULT_ATR[] = {
-/*
- * From some example somewhere
- * 0x3B, 0xB0, 0x18, 0x00, 0xD1, 0x81, 0x05, 0xB1, 0x40, 0x38, 0x1F, 0x03, 0x28
- */
-
-/* From an Athena smart card */
- 0x3B, 0xD5, 0x18, 0xFF, 0x80, 0x91, 0xFE, 0x1F, 0xC3, 0x80, 0x73, 0xC8, 0x21,
- 0x13, 0x08
-};
-
-
-#define PASSTHRU_DEV_NAME "ccid-card-passthru"
-#define VSCARD_IN_SIZE 65536
-
-/* maximum size of ATR - from 7816-3 */
-#define MAX_ATR_SIZE        40
-
-typedef struct PassthruState PassthruState;
-
-struct PassthruState {
-    CCIDCardState base;
-    CharDriverState *cs;
-    uint8_t  vscard_in_data[VSCARD_IN_SIZE];
-    uint32_t vscard_in_pos;
-    uint32_t vscard_in_hdr;
-    uint8_t  atr[MAX_ATR_SIZE];
-    uint8_t  atr_length;
-    uint8_t  debug;
-};
-
-/*
- * VSCard protocol over chardev
- * This code should not depend on the card type.
- */
-
-static void ccid_card_vscard_send_msg(PassthruState *s,
-        VSCMsgType type, uint32_t reader_id,
-        const uint8_t *payload, uint32_t length)
-{
-    VSCMsgHeader scr_msg_header;
-
-    scr_msg_header.type = htonl(type);
-    scr_msg_header.reader_id = htonl(reader_id);
-    scr_msg_header.length = htonl(length);
-    qemu_chr_fe_write(s->cs, (uint8_t *)&scr_msg_header, sizeof(VSCMsgHeader));
-    qemu_chr_fe_write(s->cs, payload, length);
-}
-
-static void ccid_card_vscard_send_apdu(PassthruState *s,
-    const uint8_t *apdu, uint32_t length)
-{
-    ccid_card_vscard_send_msg(
-        s, VSC_APDU, VSCARD_MINIMAL_READER_ID, apdu, length);
-}
-
-static void ccid_card_vscard_send_error(PassthruState *s,
-                    uint32_t reader_id, VSCErrorCode code)
-{
-    VSCMsgError msg = {.code = htonl(code)};
-
-    ccid_card_vscard_send_msg(
-        s, VSC_Error, reader_id, (uint8_t *)&msg, sizeof(msg));
-}
-
-static void ccid_card_vscard_send_init(PassthruState *s)
-{
-    VSCMsgInit msg = {
-        .version = htonl(VSCARD_VERSION),
-        .magic = VSCARD_MAGIC,
-        .capabilities = {0}
-    };
-
-    ccid_card_vscard_send_msg(s, VSC_Init, VSCARD_UNDEFINED_READER_ID,
-                         (uint8_t *)&msg, sizeof(msg));
-}
-
-static int ccid_card_vscard_can_read(void *opaque)
-{
-    PassthruState *card = opaque;
-
-    return VSCARD_IN_SIZE >= card->vscard_in_pos ?
-           VSCARD_IN_SIZE - card->vscard_in_pos : 0;
-}
-
-static void ccid_card_vscard_handle_init(
-    PassthruState *card, VSCMsgHeader *hdr, VSCMsgInit *init)
-{
-    uint32_t *capabilities;
-    int num_capabilities;
-    int i;
-
-    capabilities = init->capabilities;
-    num_capabilities =
-        1 + ((hdr->length - sizeof(VSCMsgInit)) / sizeof(uint32_t));
-    init->version = ntohl(init->version);
-    for (i = 0 ; i < num_capabilities; ++i) {
-        capabilities[i] = ntohl(capabilities[i]);
-    }
-    if (init->magic != VSCARD_MAGIC) {
-        error_report("wrong magic");
-        /* we can't disconnect the chardev */
-    }
-    if (init->version != VSCARD_VERSION) {
-        DPRINTF(card, D_WARN,
-            "got version %d, have %d", init->version, VSCARD_VERSION);
-    }
-    /* future handling of capabilities, none exist atm */
-    ccid_card_vscard_send_init(card);
-}
-
-static void ccid_card_vscard_handle_message(PassthruState *card,
-    VSCMsgHeader *scr_msg_header)
-{
-    uint8_t *data = (uint8_t *)&scr_msg_header[1];
-
-    switch (scr_msg_header->type) {
-    case VSC_ATR:
-        DPRINTF(card, D_INFO, "VSC_ATR %d\n", scr_msg_header->length);
-        if (scr_msg_header->length > MAX_ATR_SIZE) {
-            error_report("ATR size exceeds spec, ignoring");
-            ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
-                                        VSC_GENERAL_ERROR);
-            break;
-        }
-        memcpy(card->atr, data, scr_msg_header->length);
-        card->atr_length = scr_msg_header->length;
-        ccid_card_card_inserted(&card->base);
-        ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
-                                    VSC_SUCCESS);
-        break;
-    case VSC_APDU:
-        ccid_card_send_apdu_to_guest(
-            &card->base, data, scr_msg_header->length);
-        break;
-    case VSC_CardRemove:
-        DPRINTF(card, D_INFO, "VSC_CardRemove\n");
-        ccid_card_card_removed(&card->base);
-        ccid_card_vscard_send_error(card,
-            scr_msg_header->reader_id, VSC_SUCCESS);
-        break;
-    case VSC_Init:
-        ccid_card_vscard_handle_init(
-            card, scr_msg_header, (VSCMsgInit *)data);
-        break;
-    case VSC_Error:
-        ccid_card_card_error(&card->base, *(uint32_t *)data);
-        break;
-    case VSC_ReaderAdd:
-        if (ccid_card_ccid_attach(&card->base) < 0) {
-            ccid_card_vscard_send_error(card, VSCARD_UNDEFINED_READER_ID,
-                                      VSC_CANNOT_ADD_MORE_READERS);
-        } else {
-            ccid_card_vscard_send_error(card, VSCARD_MINIMAL_READER_ID,
-                                        VSC_SUCCESS);
-        }
-        break;
-    case VSC_ReaderRemove:
-        ccid_card_ccid_detach(&card->base);
-        ccid_card_vscard_send_error(card,
-            scr_msg_header->reader_id, VSC_SUCCESS);
-        break;
-    default:
-        printf("usb-ccid: chardev: unexpected message of type %X\n",
-               scr_msg_header->type);
-        ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
-            VSC_GENERAL_ERROR);
-    }
-}
-
-static void ccid_card_vscard_drop_connection(PassthruState *card)
-{
-    qemu_chr_delete(card->cs);
-    card->vscard_in_pos = card->vscard_in_hdr = 0;
-}
-
-static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size)
-{
-    PassthruState *card = opaque;
-    VSCMsgHeader *hdr;
-
-    if (card->vscard_in_pos + size > VSCARD_IN_SIZE) {
-        error_report(
-            "no room for data: pos %d +  size %d > %d. dropping connection.",
-            card->vscard_in_pos, size, VSCARD_IN_SIZE);
-        ccid_card_vscard_drop_connection(card);
-        return;
-    }
-    assert(card->vscard_in_pos < VSCARD_IN_SIZE);
-    assert(card->vscard_in_hdr < VSCARD_IN_SIZE);
-    memcpy(card->vscard_in_data + card->vscard_in_pos, buf, size);
-    card->vscard_in_pos += size;
-    hdr = (VSCMsgHeader *)(card->vscard_in_data + card->vscard_in_hdr);
-
-    while ((card->vscard_in_pos - card->vscard_in_hdr >= sizeof(VSCMsgHeader))
-         &&(card->vscard_in_pos - card->vscard_in_hdr >=
-                                  sizeof(VSCMsgHeader) + ntohl(hdr->length))) {
-        hdr->reader_id = ntohl(hdr->reader_id);
-        hdr->length = ntohl(hdr->length);
-        hdr->type = ntohl(hdr->type);
-        ccid_card_vscard_handle_message(card, hdr);
-        card->vscard_in_hdr += hdr->length + sizeof(VSCMsgHeader);
-        hdr = (VSCMsgHeader *)(card->vscard_in_data + card->vscard_in_hdr);
-    }
-    if (card->vscard_in_hdr == card->vscard_in_pos) {
-        card->vscard_in_pos = card->vscard_in_hdr = 0;
-    }
-}
-
-static void ccid_card_vscard_event(void *opaque, int event)
-{
-    PassthruState *card = opaque;
-
-    switch (event) {
-    case CHR_EVENT_BREAK:
-        card->vscard_in_pos = card->vscard_in_hdr = 0;
-        break;
-    case CHR_EVENT_FOCUS:
-        break;
-    case CHR_EVENT_OPENED:
-        DPRINTF(card, D_INFO, "%s: CHR_EVENT_OPENED\n", __func__);
-        break;
-    }
-}
-
-/* End VSCard handling */
-
-static void passthru_apdu_from_guest(
-    CCIDCardState *base, const uint8_t *apdu, uint32_t len)
-{
-    PassthruState *card = DO_UPCAST(PassthruState, base, base);
-
-    if (!card->cs) {
-        printf("ccid-passthru: no chardev, discarding apdu length %d\n", len);
-        return;
-    }
-    ccid_card_vscard_send_apdu(card, apdu, len);
-}
-
-static const uint8_t *passthru_get_atr(CCIDCardState *base, uint32_t *len)
-{
-    PassthruState *card = DO_UPCAST(PassthruState, base, base);
-
-    *len = card->atr_length;
-    return card->atr;
-}
-
-static int passthru_initfn(CCIDCardState *base)
-{
-    PassthruState *card = DO_UPCAST(PassthruState, base, base);
-
-    card->vscard_in_pos = 0;
-    card->vscard_in_hdr = 0;
-    if (card->cs) {
-        DPRINTF(card, D_INFO, "initing chardev\n");
-        qemu_chr_add_handlers(card->cs,
-            ccid_card_vscard_can_read,
-            ccid_card_vscard_read,
-            ccid_card_vscard_event, card);
-        ccid_card_vscard_send_init(card);
-    } else {
-        error_report("missing chardev");
-        return -1;
-    }
-    assert(sizeof(DEFAULT_ATR) <= MAX_ATR_SIZE);
-    memcpy(card->atr, DEFAULT_ATR, sizeof(DEFAULT_ATR));
-    card->atr_length = sizeof(DEFAULT_ATR);
-    return 0;
-}
-
-static int passthru_exitfn(CCIDCardState *base)
-{
-    return 0;
-}
-
-static VMStateDescription passthru_vmstate = {
-    .name = PASSTHRU_DEV_NAME,
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_BUFFER(vscard_in_data, PassthruState),
-        VMSTATE_UINT32(vscard_in_pos, PassthruState),
-        VMSTATE_UINT32(vscard_in_hdr, PassthruState),
-        VMSTATE_BUFFER(atr, PassthruState),
-        VMSTATE_UINT8(atr_length, PassthruState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property passthru_card_properties[] = {
-    DEFINE_PROP_CHR("chardev", PassthruState, cs),
-    DEFINE_PROP_UINT8("debug", PassthruState, debug, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void passthru_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    CCIDCardClass *cc = CCID_CARD_CLASS(klass);
-
-    cc->initfn = passthru_initfn;
-    cc->exitfn = passthru_exitfn;
-    cc->get_atr = passthru_get_atr;
-    cc->apdu_from_guest = passthru_apdu_from_guest;
-    dc->desc = "passthrough smartcard";
-    dc->vmsd = &passthru_vmstate;
-    dc->props = passthru_card_properties;
-}
-
-static const TypeInfo passthru_card_info = {
-    .name          = PASSTHRU_DEV_NAME,
-    .parent        = TYPE_CCID_CARD,
-    .instance_size = sizeof(PassthruState),
-    .class_init    = passthru_class_initfn,
-};
-
-static void ccid_card_passthru_register_types(void)
-{
-    type_register_static(&passthru_card_info);
-}
-
-type_init(ccid_card_passthru_register_types)
diff --git a/hw/cdrom.c b/hw/cdrom.c
deleted file mode 100644 (file)
index 38469fa..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * QEMU ATAPI CD-ROM Emulator
- *
- * Copyright (c) 2006 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-/* ??? Most of the ATAPI emulation is still in ide.c.  It should be moved
-   here.  */
-
-#include "qemu-common.h"
-#include "hw/scsi/scsi.h"
-
-static void lba_to_msf(uint8_t *buf, int lba)
-{
-    lba += 150;
-    buf[0] = (lba / 75) / 60;
-    buf[1] = (lba / 75) % 60;
-    buf[2] = lba % 75;
-}
-
-/* same toc as bochs. Return -1 if error or the toc length */
-/* XXX: check this */
-int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track)
-{
-    uint8_t *q;
-    int len;
-
-    if (start_track > 1 && start_track != 0xaa)
-        return -1;
-    q = buf + 2;
-    *q++ = 1; /* first session */
-    *q++ = 1; /* last session */
-    if (start_track <= 1) {
-        *q++ = 0; /* reserved */
-        *q++ = 0x14; /* ADR, control */
-        *q++ = 1;    /* track number */
-        *q++ = 0; /* reserved */
-        if (msf) {
-            *q++ = 0; /* reserved */
-            lba_to_msf(q, 0);
-            q += 3;
-        } else {
-            /* sector 0 */
-            cpu_to_be32wu((uint32_t *)q, 0);
-            q += 4;
-        }
-    }
-    /* lead out track */
-    *q++ = 0; /* reserved */
-    *q++ = 0x16; /* ADR, control */
-    *q++ = 0xaa; /* track number */
-    *q++ = 0; /* reserved */
-    if (msf) {
-        *q++ = 0; /* reserved */
-        lba_to_msf(q, nb_sectors);
-        q += 3;
-    } else {
-        cpu_to_be32wu((uint32_t *)q, nb_sectors);
-        q += 4;
-    }
-    len = q - buf;
-    cpu_to_be16wu((uint16_t *)buf, len - 2);
-    return len;
-}
-
-/* mostly same info as PearPc */
-int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num)
-{
-    uint8_t *q;
-    int len;
-
-    q = buf + 2;
-    *q++ = 1; /* first session */
-    *q++ = 1; /* last session */
-
-    *q++ = 1; /* session number */
-    *q++ = 0x14; /* data track */
-    *q++ = 0; /* track number */
-    *q++ = 0xa0; /* lead-in */
-    *q++ = 0; /* min */
-    *q++ = 0; /* sec */
-    *q++ = 0; /* frame */
-    *q++ = 0;
-    *q++ = 1; /* first track */
-    *q++ = 0x00; /* disk type */
-    *q++ = 0x00;
-
-    *q++ = 1; /* session number */
-    *q++ = 0x14; /* data track */
-    *q++ = 0; /* track number */
-    *q++ = 0xa1;
-    *q++ = 0; /* min */
-    *q++ = 0; /* sec */
-    *q++ = 0; /* frame */
-    *q++ = 0;
-    *q++ = 1; /* last track */
-    *q++ = 0x00;
-    *q++ = 0x00;
-
-    *q++ = 1; /* session number */
-    *q++ = 0x14; /* data track */
-    *q++ = 0; /* track number */
-    *q++ = 0xa2; /* lead-out */
-    *q++ = 0; /* min */
-    *q++ = 0; /* sec */
-    *q++ = 0; /* frame */
-    if (msf) {
-        *q++ = 0; /* reserved */
-        lba_to_msf(q, nb_sectors);
-        q += 3;
-    } else {
-        cpu_to_be32wu((uint32_t *)q, nb_sectors);
-        q += 4;
-    }
-
-    *q++ = 1; /* session number */
-    *q++ = 0x14; /* ADR, control */
-    *q++ = 0;    /* track number */
-    *q++ = 1;    /* point */
-    *q++ = 0; /* min */
-    *q++ = 0; /* sec */
-    *q++ = 0; /* frame */
-    if (msf) {
-        *q++ = 0;
-        lba_to_msf(q, 0);
-        q += 3;
-    } else {
-        *q++ = 0;
-        *q++ = 0;
-        *q++ = 0;
-        *q++ = 0;
-    }
-
-    len = q - buf;
-    cpu_to_be16wu((uint16_t *)buf, len - 2);
-    return len;
-}
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..eee23ff6373628bc0bd7b62f6f3f567557a0422a 100644 (file)
@@ -0,0 +1,10 @@
+common-obj-$(CONFIG_IPACK) += tpci200.o ipoctal232.o ipack.o
+common-obj-$(CONFIG_ESCC) += escc.o
+common-obj-$(CONFIG_PARALLEL) += parallel.o
+common-obj-$(CONFIG_PL011) += pl011.o
+common-obj-$(CONFIG_SERIAL) += serial.o serial-isa.o
+common-obj-$(CONFIG_SERIAL_PCI) += serial-pci.o
+common-obj-$(CONFIG_VIRTIO) += virtio-console.o
+common-obj-$(CONFIG_XILINX) += xilinx_uartlite.o
+common-obj-$(CONFIG_XEN_BACKEND) += xen_console.o
+common-obj-$(CONFIG_CADENCE) += cadence_uart.o
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
new file mode 100644 (file)
index 0000000..421ec99
--- /dev/null
@@ -0,0 +1,518 @@
+/*
+ * Device model for Cadence UART
+ *
+ * Copyright (c) 2010 Xilinx Inc.
+ * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
+ * Copyright (c) 2012 PetaLogix Pty Ltd.
+ * Written by Haibing Ma
+ *            M.Habib
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/sysbus.h"
+#include "char/char.h"
+#include "qemu/timer.h"
+
+#ifdef CADENCE_UART_ERR_DEBUG
+#define DB_PRINT(...) do { \
+    fprintf(stderr,  ": %s: ", __func__); \
+    fprintf(stderr, ## __VA_ARGS__); \
+    } while (0);
+#else
+    #define DB_PRINT(...)
+#endif
+
+#define UART_SR_INTR_RTRIG     0x00000001
+#define UART_SR_INTR_REMPTY    0x00000002
+#define UART_SR_INTR_RFUL      0x00000004
+#define UART_SR_INTR_TEMPTY    0x00000008
+#define UART_SR_INTR_TFUL      0x00000010
+/* bits fields in CSR that correlate to CISR. If any of these bits are set in
+ * SR, then the same bit in CISR is set high too */
+#define UART_SR_TO_CISR_MASK   0x0000001F
+
+#define UART_INTR_ROVR         0x00000020
+#define UART_INTR_FRAME        0x00000040
+#define UART_INTR_PARE         0x00000080
+#define UART_INTR_TIMEOUT      0x00000100
+#define UART_INTR_DMSI         0x00000200
+
+#define UART_SR_RACTIVE    0x00000400
+#define UART_SR_TACTIVE    0x00000800
+#define UART_SR_FDELT      0x00001000
+
+#define UART_CR_RXRST       0x00000001
+#define UART_CR_TXRST       0x00000002
+#define UART_CR_RX_EN       0x00000004
+#define UART_CR_RX_DIS      0x00000008
+#define UART_CR_TX_EN       0x00000010
+#define UART_CR_TX_DIS      0x00000020
+#define UART_CR_RST_TO      0x00000040
+#define UART_CR_STARTBRK    0x00000080
+#define UART_CR_STOPBRK     0x00000100
+
+#define UART_MR_CLKS            0x00000001
+#define UART_MR_CHRL            0x00000006
+#define UART_MR_CHRL_SH         1
+#define UART_MR_PAR             0x00000038
+#define UART_MR_PAR_SH          3
+#define UART_MR_NBSTOP          0x000000C0
+#define UART_MR_NBSTOP_SH       6
+#define UART_MR_CHMODE          0x00000300
+#define UART_MR_CHMODE_SH       8
+#define UART_MR_UCLKEN          0x00000400
+#define UART_MR_IRMODE          0x00000800
+
+#define UART_DATA_BITS_6       (0x3 << UART_MR_CHRL_SH)
+#define UART_DATA_BITS_7       (0x2 << UART_MR_CHRL_SH)
+#define UART_PARITY_ODD        (0x1 << UART_MR_PAR_SH)
+#define UART_PARITY_EVEN       (0x0 << UART_MR_PAR_SH)
+#define UART_STOP_BITS_1       (0x3 << UART_MR_NBSTOP_SH)
+#define UART_STOP_BITS_2       (0x2 << UART_MR_NBSTOP_SH)
+#define NORMAL_MODE            (0x0 << UART_MR_CHMODE_SH)
+#define ECHO_MODE              (0x1 << UART_MR_CHMODE_SH)
+#define LOCAL_LOOPBACK         (0x2 << UART_MR_CHMODE_SH)
+#define REMOTE_LOOPBACK        (0x3 << UART_MR_CHMODE_SH)
+
+#define RX_FIFO_SIZE           16
+#define TX_FIFO_SIZE           16
+#define UART_INPUT_CLK         50000000
+
+#define R_CR       (0x00/4)
+#define R_MR       (0x04/4)
+#define R_IER      (0x08/4)
+#define R_IDR      (0x0C/4)
+#define R_IMR      (0x10/4)
+#define R_CISR     (0x14/4)
+#define R_BRGR     (0x18/4)
+#define R_RTOR     (0x1C/4)
+#define R_RTRIG    (0x20/4)
+#define R_MCR      (0x24/4)
+#define R_MSR      (0x28/4)
+#define R_SR       (0x2C/4)
+#define R_TX_RX    (0x30/4)
+#define R_BDIV     (0x34/4)
+#define R_FDEL     (0x38/4)
+#define R_PMIN     (0x3C/4)
+#define R_PWID     (0x40/4)
+#define R_TTRIG    (0x44/4)
+
+#define R_MAX (R_TTRIG + 1)
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    uint32_t r[R_MAX];
+    uint8_t r_fifo[RX_FIFO_SIZE];
+    uint32_t rx_wpos;
+    uint32_t rx_count;
+    uint64_t char_tx_time;
+    CharDriverState *chr;
+    qemu_irq irq;
+    struct QEMUTimer *fifo_trigger_handle;
+    struct QEMUTimer *tx_time_handle;
+} UartState;
+
+static void uart_update_status(UartState *s)
+{
+    s->r[R_CISR] |= s->r[R_SR] & UART_SR_TO_CISR_MASK;
+    qemu_set_irq(s->irq, !!(s->r[R_IMR] & s->r[R_CISR]));
+}
+
+static void fifo_trigger_update(void *opaque)
+{
+    UartState *s = (UartState *)opaque;
+
+    s->r[R_CISR] |= UART_INTR_TIMEOUT;
+
+    uart_update_status(s);
+}
+
+static void uart_tx_redo(UartState *s)
+{
+    uint64_t new_tx_time = qemu_get_clock_ns(vm_clock);
+
+    qemu_mod_timer(s->tx_time_handle, new_tx_time + s->char_tx_time);
+
+    s->r[R_SR] |= UART_SR_INTR_TEMPTY;
+
+    uart_update_status(s);
+}
+
+static void uart_tx_write(void *opaque)
+{
+    UartState *s = (UartState *)opaque;
+
+    uart_tx_redo(s);
+}
+
+static void uart_rx_reset(UartState *s)
+{
+    s->rx_wpos = 0;
+    s->rx_count = 0;
+    qemu_chr_accept_input(s->chr);
+
+    s->r[R_SR] |= UART_SR_INTR_REMPTY;
+    s->r[R_SR] &= ~UART_SR_INTR_RFUL;
+}
+
+static void uart_tx_reset(UartState *s)
+{
+    s->r[R_SR] |= UART_SR_INTR_TEMPTY;
+    s->r[R_SR] &= ~UART_SR_INTR_TFUL;
+}
+
+static void uart_send_breaks(UartState *s)
+{
+    int break_enabled = 1;
+
+    qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
+                               &break_enabled);
+}
+
+static void uart_parameters_setup(UartState *s)
+{
+    QEMUSerialSetParams ssp;
+    unsigned int baud_rate, packet_size;
+
+    baud_rate = (s->r[R_MR] & UART_MR_CLKS) ?
+            UART_INPUT_CLK / 8 : UART_INPUT_CLK;
+
+    ssp.speed = baud_rate / (s->r[R_BRGR] * (s->r[R_BDIV] + 1));
+    packet_size = 1;
+
+    switch (s->r[R_MR] & UART_MR_PAR) {
+    case UART_PARITY_EVEN:
+        ssp.parity = 'E';
+        packet_size++;
+        break;
+    case UART_PARITY_ODD:
+        ssp.parity = 'O';
+        packet_size++;
+        break;
+    default:
+        ssp.parity = 'N';
+        break;
+    }
+
+    switch (s->r[R_MR] & UART_MR_CHRL) {
+    case UART_DATA_BITS_6:
+        ssp.data_bits = 6;
+        break;
+    case UART_DATA_BITS_7:
+        ssp.data_bits = 7;
+        break;
+    default:
+        ssp.data_bits = 8;
+        break;
+    }
+
+    switch (s->r[R_MR] & UART_MR_NBSTOP) {
+    case UART_STOP_BITS_1:
+        ssp.stop_bits = 1;
+        break;
+    default:
+        ssp.stop_bits = 2;
+        break;
+    }
+
+    packet_size += ssp.data_bits + ssp.stop_bits;
+    s->char_tx_time = (get_ticks_per_sec() / ssp.speed) * packet_size;
+    qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+}
+
+static int uart_can_receive(void *opaque)
+{
+    UartState *s = (UartState *)opaque;
+
+    return RX_FIFO_SIZE - s->rx_count;
+}
+
+static void uart_ctrl_update(UartState *s)
+{
+    if (s->r[R_CR] & UART_CR_TXRST) {
+        uart_tx_reset(s);
+    }
+
+    if (s->r[R_CR] & UART_CR_RXRST) {
+        uart_rx_reset(s);
+    }
+
+    s->r[R_CR] &= ~(UART_CR_TXRST | UART_CR_RXRST);
+
+    if ((s->r[R_CR] & UART_CR_TX_EN) && !(s->r[R_CR] & UART_CR_TX_DIS)) {
+            uart_tx_redo(s);
+    }
+
+    if (s->r[R_CR] & UART_CR_STARTBRK && !(s->r[R_CR] & UART_CR_STOPBRK)) {
+        uart_send_breaks(s);
+    }
+}
+
+static void uart_write_rx_fifo(void *opaque, const uint8_t *buf, int size)
+{
+    UartState *s = (UartState *)opaque;
+    uint64_t new_rx_time = qemu_get_clock_ns(vm_clock);
+    int i;
+
+    if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) {
+        return;
+    }
+
+    s->r[R_SR] &= ~UART_SR_INTR_REMPTY;
+
+    if (s->rx_count == RX_FIFO_SIZE) {
+        s->r[R_CISR] |= UART_INTR_ROVR;
+    } else {
+        for (i = 0; i < size; i++) {
+            s->r_fifo[s->rx_wpos] = buf[i];
+            s->rx_wpos = (s->rx_wpos + 1) % RX_FIFO_SIZE;
+            s->rx_count++;
+
+            if (s->rx_count == RX_FIFO_SIZE) {
+                s->r[R_SR] |= UART_SR_INTR_RFUL;
+                break;
+            }
+
+            if (s->rx_count >= s->r[R_RTRIG]) {
+                s->r[R_SR] |= UART_SR_INTR_RTRIG;
+            }
+        }
+        qemu_mod_timer(s->fifo_trigger_handle, new_rx_time +
+                                                (s->char_tx_time * 4));
+    }
+    uart_update_status(s);
+}
+
+static void uart_write_tx_fifo(UartState *s, const uint8_t *buf, int size)
+{
+    if ((s->r[R_CR] & UART_CR_TX_DIS) || !(s->r[R_CR] & UART_CR_TX_EN)) {
+        return;
+    }
+
+    while (size) {
+        size -= qemu_chr_fe_write(s->chr, buf, size);
+    }
+}
+
+static void uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+    UartState *s = (UartState *)opaque;
+    uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE;
+
+    if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) {
+        uart_write_rx_fifo(opaque, buf, size);
+    }
+    if (ch_mode == REMOTE_LOOPBACK || ch_mode == ECHO_MODE) {
+        uart_write_tx_fifo(s, buf, size);
+    }
+}
+
+static void uart_event(void *opaque, int event)
+{
+    UartState *s = (UartState *)opaque;
+    uint8_t buf = '\0';
+
+    if (event == CHR_EVENT_BREAK) {
+        uart_write_rx_fifo(opaque, &buf, 1);
+    }
+
+    uart_update_status(s);
+}
+
+static void uart_read_rx_fifo(UartState *s, uint32_t *c)
+{
+    if ((s->r[R_CR] & UART_CR_RX_DIS) || !(s->r[R_CR] & UART_CR_RX_EN)) {
+        return;
+    }
+
+    s->r[R_SR] &= ~UART_SR_INTR_RFUL;
+
+    if (s->rx_count) {
+        uint32_t rx_rpos =
+                (RX_FIFO_SIZE + s->rx_wpos - s->rx_count) % RX_FIFO_SIZE;
+        *c = s->r_fifo[rx_rpos];
+        s->rx_count--;
+
+        if (!s->rx_count) {
+            s->r[R_SR] |= UART_SR_INTR_REMPTY;
+        }
+        qemu_chr_accept_input(s->chr);
+    } else {
+        *c = 0;
+        s->r[R_SR] |= UART_SR_INTR_REMPTY;
+    }
+
+    if (s->rx_count < s->r[R_RTRIG]) {
+        s->r[R_SR] &= ~UART_SR_INTR_RTRIG;
+    }
+    uart_update_status(s);
+}
+
+static void uart_write(void *opaque, hwaddr offset,
+                          uint64_t value, unsigned size)
+{
+    UartState *s = (UartState *)opaque;
+
+    DB_PRINT(" offset:%x data:%08x\n", (unsigned)offset, (unsigned)value);
+    offset >>= 2;
+    switch (offset) {
+    case R_IER: /* ier (wts imr) */
+        s->r[R_IMR] |= value;
+        break;
+    case R_IDR: /* idr (wtc imr) */
+        s->r[R_IMR] &= ~value;
+        break;
+    case R_IMR: /* imr (read only) */
+        break;
+    case R_CISR: /* cisr (wtc) */
+        s->r[R_CISR] &= ~value;
+        break;
+    case R_TX_RX: /* UARTDR */
+        switch (s->r[R_MR] & UART_MR_CHMODE) {
+        case NORMAL_MODE:
+            uart_write_tx_fifo(s, (uint8_t *) &value, 1);
+            break;
+        case LOCAL_LOOPBACK:
+            uart_write_rx_fifo(opaque, (uint8_t *) &value, 1);
+            break;
+        }
+        break;
+    default:
+        s->r[offset] = value;
+    }
+
+    switch (offset) {
+    case R_CR:
+        uart_ctrl_update(s);
+        break;
+    case R_MR:
+        uart_parameters_setup(s);
+        break;
+    }
+}
+
+static uint64_t uart_read(void *opaque, hwaddr offset,
+        unsigned size)
+{
+    UartState *s = (UartState *)opaque;
+    uint32_t c = 0;
+
+    offset >>= 2;
+    if (offset >= R_MAX) {
+        c = 0;
+    } else if (offset == R_TX_RX) {
+        uart_read_rx_fifo(s, &c);
+    } else {
+       c = s->r[offset];
+    }
+
+    DB_PRINT(" offset:%x data:%08x\n", (unsigned)(offset << 2), (unsigned)c);
+    return c;
+}
+
+static const MemoryRegionOps uart_ops = {
+    .read = uart_read,
+    .write = uart_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void cadence_uart_reset(UartState *s)
+{
+    s->r[R_CR] = 0x00000128;
+    s->r[R_IMR] = 0;
+    s->r[R_CISR] = 0;
+    s->r[R_RTRIG] = 0x00000020;
+    s->r[R_BRGR] = 0x0000000F;
+    s->r[R_TTRIG] = 0x00000020;
+
+    uart_rx_reset(s);
+    uart_tx_reset(s);
+
+    s->rx_count = 0;
+    s->rx_wpos = 0;
+}
+
+static int cadence_uart_init(SysBusDevice *dev)
+{
+    UartState *s = FROM_SYSBUS(UartState, dev);
+
+    memory_region_init_io(&s->iomem, &uart_ops, s, "uart", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+
+    s->fifo_trigger_handle = qemu_new_timer_ns(vm_clock,
+            (QEMUTimerCB *)fifo_trigger_update, s);
+
+    s->tx_time_handle = qemu_new_timer_ns(vm_clock,
+            (QEMUTimerCB *)uart_tx_write, s);
+
+    s->char_tx_time = (get_ticks_per_sec() / 9600) * 10;
+
+    s->chr = qemu_char_get_next_serial();
+
+    cadence_uart_reset(s);
+
+    if (s->chr) {
+        qemu_chr_add_handlers(s->chr, uart_can_receive, uart_receive,
+                              uart_event, s);
+    }
+
+    return 0;
+}
+
+static int cadence_uart_post_load(void *opaque, int version_id)
+{
+    UartState *s = opaque;
+
+    uart_parameters_setup(s);
+    uart_update_status(s);
+    return 0;
+}
+
+static const VMStateDescription vmstate_cadence_uart = {
+    .name = "cadence_uart",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = cadence_uart_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(r, UartState, R_MAX),
+        VMSTATE_UINT8_ARRAY(r_fifo, UartState, RX_FIFO_SIZE),
+        VMSTATE_UINT32(rx_count, UartState),
+        VMSTATE_UINT32(rx_wpos, UartState),
+        VMSTATE_TIMER(fifo_trigger_handle, UartState),
+        VMSTATE_TIMER(tx_time_handle, UartState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void cadence_uart_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = cadence_uart_init;
+    dc->vmsd = &vmstate_cadence_uart;
+}
+
+static const TypeInfo cadence_uart_info = {
+    .name          = "cadence_uart",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(UartState),
+    .class_init    = cadence_uart_class_init,
+};
+
+static void cadence_uart_register_types(void)
+{
+    type_register_static(&cadence_uart_info);
+}
+
+type_init(cadence_uart_register_types)
diff --git a/hw/char/escc.c b/hw/char/escc.c
new file mode 100644 (file)
index 0000000..067b055
--- /dev/null
@@ -0,0 +1,938 @@
+/*
+ * QEMU ESCC (Z8030/Z8530/Z85C30/SCC/ESCC) serial port emulation
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/char/escc.h"
+#include "char/char.h"
+#include "ui/console.h"
+#include "trace.h"
+
+/*
+ * Chipset docs:
+ * "Z80C30/Z85C30/Z80230/Z85230/Z85233 SCC/ESCC User Manual",
+ * http://www.zilog.com/docs/serial/scc_escc_um.pdf
+ *
+ * On Sparc32 this is the serial port, mouse and keyboard part of chip STP2001
+ * (Slave I/O), also produced as NCR89C105. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
+ *
+ * The serial ports implement full AMD AM8530 or Zilog Z8530 chips,
+ * mouse and keyboard ports don't implement all functions and they are
+ * only asynchronous. There is no DMA.
+ *
+ * Z85C30 is also used on PowerMacs. There are some small differences
+ * between Sparc version (sunzilog) and PowerMac (pmac):
+ *  Offset between control and data registers
+ *  There is some kind of lockup bug, but we can ignore it
+ *  CTS is inverted
+ *  DMA on pmac using DBDMA chip
+ *  pmac can do IRDA and faster rates, sunzilog can only do 38400
+ *  pmac baud rate generator clock is 3.6864 MHz, sunzilog 4.9152 MHz
+ */
+
+/*
+ * Modifications:
+ *  2006-Aug-10  Igor Kovalenko :   Renamed KBDQueue to SERIOQueue, implemented
+ *                                  serial mouse queue.
+ *                                  Implemented serial mouse protocol.
+ *
+ *  2010-May-23  Artyom Tarasenko:  Reworked IUS logic
+ */
+
+typedef enum {
+    chn_a, chn_b,
+} ChnID;
+
+#define CHN_C(s) ((s)->chn == chn_b? 'b' : 'a')
+
+typedef enum {
+    ser, kbd, mouse,
+} ChnType;
+
+#define SERIO_QUEUE_SIZE 256
+
+typedef struct {
+    uint8_t data[SERIO_QUEUE_SIZE];
+    int rptr, wptr, count;
+} SERIOQueue;
+
+#define SERIAL_REGS 16
+typedef struct ChannelState {
+    qemu_irq irq;
+    uint32_t rxint, txint, rxint_under_svc, txint_under_svc;
+    struct ChannelState *otherchn;
+    uint32_t reg;
+    uint8_t wregs[SERIAL_REGS], rregs[SERIAL_REGS];
+    SERIOQueue queue;
+    CharDriverState *chr;
+    int e0_mode, led_mode, caps_lock_mode, num_lock_mode;
+    int disabled;
+    int clock;
+    uint32_t vmstate_dummy;
+    ChnID chn; // this channel, A (base+4) or B (base+0)
+    ChnType type;
+    uint8_t rx, tx;
+} ChannelState;
+
+struct SerialState {
+    SysBusDevice busdev;
+    struct ChannelState chn[2];
+    uint32_t it_shift;
+    MemoryRegion mmio;
+    uint32_t disabled;
+    uint32_t frequency;
+};
+
+#define SERIAL_CTRL 0
+#define SERIAL_DATA 1
+
+#define W_CMD     0
+#define CMD_PTR_MASK   0x07
+#define CMD_CMD_MASK   0x38
+#define CMD_HI         0x08
+#define CMD_CLR_TXINT  0x28
+#define CMD_CLR_IUS    0x38
+#define W_INTR    1
+#define INTR_INTALL    0x01
+#define INTR_TXINT     0x02
+#define INTR_RXMODEMSK 0x18
+#define INTR_RXINT1ST  0x08
+#define INTR_RXINTALL  0x10
+#define W_IVEC    2
+#define W_RXCTRL  3
+#define RXCTRL_RXEN    0x01
+#define W_TXCTRL1 4
+#define TXCTRL1_PAREN  0x01
+#define TXCTRL1_PAREV  0x02
+#define TXCTRL1_1STOP  0x04
+#define TXCTRL1_1HSTOP 0x08
+#define TXCTRL1_2STOP  0x0c
+#define TXCTRL1_STPMSK 0x0c
+#define TXCTRL1_CLK1X  0x00
+#define TXCTRL1_CLK16X 0x40
+#define TXCTRL1_CLK32X 0x80
+#define TXCTRL1_CLK64X 0xc0
+#define TXCTRL1_CLKMSK 0xc0
+#define W_TXCTRL2 5
+#define TXCTRL2_TXEN   0x08
+#define TXCTRL2_BITMSK 0x60
+#define TXCTRL2_5BITS  0x00
+#define TXCTRL2_7BITS  0x20
+#define TXCTRL2_6BITS  0x40
+#define TXCTRL2_8BITS  0x60
+#define W_SYNC1   6
+#define W_SYNC2   7
+#define W_TXBUF   8
+#define W_MINTR   9
+#define MINTR_STATUSHI 0x10
+#define MINTR_RST_MASK 0xc0
+#define MINTR_RST_B    0x40
+#define MINTR_RST_A    0x80
+#define MINTR_RST_ALL  0xc0
+#define W_MISC1  10
+#define W_CLOCK  11
+#define CLOCK_TRXC     0x08
+#define W_BRGLO  12
+#define W_BRGHI  13
+#define W_MISC2  14
+#define MISC2_PLLDIS   0x30
+#define W_EXTINT 15
+#define EXTINT_DCD     0x08
+#define EXTINT_SYNCINT 0x10
+#define EXTINT_CTSINT  0x20
+#define EXTINT_TXUNDRN 0x40
+#define EXTINT_BRKINT  0x80
+
+#define R_STATUS  0
+#define STATUS_RXAV    0x01
+#define STATUS_ZERO    0x02
+#define STATUS_TXEMPTY 0x04
+#define STATUS_DCD     0x08
+#define STATUS_SYNC    0x10
+#define STATUS_CTS     0x20
+#define STATUS_TXUNDRN 0x40
+#define STATUS_BRK     0x80
+#define R_SPEC    1
+#define SPEC_ALLSENT   0x01
+#define SPEC_BITS8     0x06
+#define R_IVEC    2
+#define IVEC_TXINTB    0x00
+#define IVEC_LONOINT   0x06
+#define IVEC_LORXINTA  0x0c
+#define IVEC_LORXINTB  0x04
+#define IVEC_LOTXINTA  0x08
+#define IVEC_HINOINT   0x60
+#define IVEC_HIRXINTA  0x30
+#define IVEC_HIRXINTB  0x20
+#define IVEC_HITXINTA  0x10
+#define R_INTR    3
+#define INTR_EXTINTB   0x01
+#define INTR_TXINTB    0x02
+#define INTR_RXINTB    0x04
+#define INTR_EXTINTA   0x08
+#define INTR_TXINTA    0x10
+#define INTR_RXINTA    0x20
+#define R_IPEN    4
+#define R_TXCTRL1 5
+#define R_TXCTRL2 6
+#define R_BC      7
+#define R_RXBUF   8
+#define R_RXCTRL  9
+#define R_MISC   10
+#define R_MISC1  11
+#define R_BRGLO  12
+#define R_BRGHI  13
+#define R_MISC1I 14
+#define R_EXTINT 15
+
+static void handle_kbd_command(ChannelState *s, int val);
+static int serial_can_receive(void *opaque);
+static void serial_receive_byte(ChannelState *s, int ch);
+
+static void clear_queue(void *opaque)
+{
+    ChannelState *s = opaque;
+    SERIOQueue *q = &s->queue;
+    q->rptr = q->wptr = q->count = 0;
+}
+
+static void put_queue(void *opaque, int b)
+{
+    ChannelState *s = opaque;
+    SERIOQueue *q = &s->queue;
+
+    trace_escc_put_queue(CHN_C(s), b);
+    if (q->count >= SERIO_QUEUE_SIZE)
+        return;
+    q->data[q->wptr] = b;
+    if (++q->wptr == SERIO_QUEUE_SIZE)
+        q->wptr = 0;
+    q->count++;
+    serial_receive_byte(s, 0);
+}
+
+static uint32_t get_queue(void *opaque)
+{
+    ChannelState *s = opaque;
+    SERIOQueue *q = &s->queue;
+    int val;
+
+    if (q->count == 0) {
+        return 0;
+    } else {
+        val = q->data[q->rptr];
+        if (++q->rptr == SERIO_QUEUE_SIZE)
+            q->rptr = 0;
+        q->count--;
+    }
+    trace_escc_get_queue(CHN_C(s), val);
+    if (q->count > 0)
+        serial_receive_byte(s, 0);
+    return val;
+}
+
+static int escc_update_irq_chn(ChannelState *s)
+{
+    if ((((s->wregs[W_INTR] & INTR_TXINT) && (s->txint == 1)) ||
+         // tx ints enabled, pending
+         ((((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINT1ST) ||
+           ((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINTALL)) &&
+          s->rxint == 1) || // rx ints enabled, pending
+         ((s->wregs[W_EXTINT] & EXTINT_BRKINT) &&
+          (s->rregs[R_STATUS] & STATUS_BRK)))) { // break int e&p
+        return 1;
+    }
+    return 0;
+}
+
+static void escc_update_irq(ChannelState *s)
+{
+    int irq;
+
+    irq = escc_update_irq_chn(s);
+    irq |= escc_update_irq_chn(s->otherchn);
+
+    trace_escc_update_irq(irq);
+    qemu_set_irq(s->irq, irq);
+}
+
+static void escc_reset_chn(ChannelState *s)
+{
+    int i;
+
+    s->reg = 0;
+    for (i = 0; i < SERIAL_REGS; i++) {
+        s->rregs[i] = 0;
+        s->wregs[i] = 0;
+    }
+    s->wregs[W_TXCTRL1] = TXCTRL1_1STOP; // 1X divisor, 1 stop bit, no parity
+    s->wregs[W_MINTR] = MINTR_RST_ALL;
+    s->wregs[W_CLOCK] = CLOCK_TRXC; // Synch mode tx clock = TRxC
+    s->wregs[W_MISC2] = MISC2_PLLDIS; // PLL disabled
+    s->wregs[W_EXTINT] = EXTINT_DCD | EXTINT_SYNCINT | EXTINT_CTSINT |
+        EXTINT_TXUNDRN | EXTINT_BRKINT; // Enable most interrupts
+    if (s->disabled)
+        s->rregs[R_STATUS] = STATUS_TXEMPTY | STATUS_DCD | STATUS_SYNC |
+            STATUS_CTS | STATUS_TXUNDRN;
+    else
+        s->rregs[R_STATUS] = STATUS_TXEMPTY | STATUS_TXUNDRN;
+    s->rregs[R_SPEC] = SPEC_BITS8 | SPEC_ALLSENT;
+
+    s->rx = s->tx = 0;
+    s->rxint = s->txint = 0;
+    s->rxint_under_svc = s->txint_under_svc = 0;
+    s->e0_mode = s->led_mode = s->caps_lock_mode = s->num_lock_mode = 0;
+    clear_queue(s);
+}
+
+static void escc_reset(DeviceState *d)
+{
+    SerialState *s = container_of(d, SerialState, busdev.qdev);
+
+    escc_reset_chn(&s->chn[0]);
+    escc_reset_chn(&s->chn[1]);
+}
+
+static inline void set_rxint(ChannelState *s)
+{
+    s->rxint = 1;
+    /* XXX: missing daisy chainnig: chn_b rx should have a lower priority
+       than chn_a rx/tx/special_condition service*/
+    s->rxint_under_svc = 1;
+    if (s->chn == chn_a) {
+        s->rregs[R_INTR] |= INTR_RXINTA;
+        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+            s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA;
+        else
+            s->otherchn->rregs[R_IVEC] = IVEC_LORXINTA;
+    } else {
+        s->otherchn->rregs[R_INTR] |= INTR_RXINTB;
+        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+            s->rregs[R_IVEC] = IVEC_HIRXINTB;
+        else
+            s->rregs[R_IVEC] = IVEC_LORXINTB;
+    }
+    escc_update_irq(s);
+}
+
+static inline void set_txint(ChannelState *s)
+{
+    s->txint = 1;
+    if (!s->rxint_under_svc) {
+        s->txint_under_svc = 1;
+        if (s->chn == chn_a) {
+            if (s->wregs[W_INTR] & INTR_TXINT) {
+                s->rregs[R_INTR] |= INTR_TXINTA;
+            }
+            if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+                s->otherchn->rregs[R_IVEC] = IVEC_HITXINTA;
+            else
+                s->otherchn->rregs[R_IVEC] = IVEC_LOTXINTA;
+        } else {
+            s->rregs[R_IVEC] = IVEC_TXINTB;
+            if (s->wregs[W_INTR] & INTR_TXINT) {
+                s->otherchn->rregs[R_INTR] |= INTR_TXINTB;
+            }
+        }
+    escc_update_irq(s);
+    }
+}
+
+static inline void clr_rxint(ChannelState *s)
+{
+    s->rxint = 0;
+    s->rxint_under_svc = 0;
+    if (s->chn == chn_a) {
+        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+            s->otherchn->rregs[R_IVEC] = IVEC_HINOINT;
+        else
+            s->otherchn->rregs[R_IVEC] = IVEC_LONOINT;
+        s->rregs[R_INTR] &= ~INTR_RXINTA;
+    } else {
+        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+            s->rregs[R_IVEC] = IVEC_HINOINT;
+        else
+            s->rregs[R_IVEC] = IVEC_LONOINT;
+        s->otherchn->rregs[R_INTR] &= ~INTR_RXINTB;
+    }
+    if (s->txint)
+        set_txint(s);
+    escc_update_irq(s);
+}
+
+static inline void clr_txint(ChannelState *s)
+{
+    s->txint = 0;
+    s->txint_under_svc = 0;
+    if (s->chn == chn_a) {
+        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+            s->otherchn->rregs[R_IVEC] = IVEC_HINOINT;
+        else
+            s->otherchn->rregs[R_IVEC] = IVEC_LONOINT;
+        s->rregs[R_INTR] &= ~INTR_TXINTA;
+    } else {
+        s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB;
+        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+            s->rregs[R_IVEC] = IVEC_HINOINT;
+        else
+            s->rregs[R_IVEC] = IVEC_LONOINT;
+        s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB;
+    }
+    if (s->rxint)
+        set_rxint(s);
+    escc_update_irq(s);
+}
+
+static void escc_update_parameters(ChannelState *s)
+{
+    int speed, parity, data_bits, stop_bits;
+    QEMUSerialSetParams ssp;
+
+    if (!s->chr || s->type != ser)
+        return;
+
+    if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREN) {
+        if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREV)
+            parity = 'E';
+        else
+            parity = 'O';
+    } else {
+        parity = 'N';
+    }
+    if ((s->wregs[W_TXCTRL1] & TXCTRL1_STPMSK) == TXCTRL1_2STOP)
+        stop_bits = 2;
+    else
+        stop_bits = 1;
+    switch (s->wregs[W_TXCTRL2] & TXCTRL2_BITMSK) {
+    case TXCTRL2_5BITS:
+        data_bits = 5;
+        break;
+    case TXCTRL2_7BITS:
+        data_bits = 7;
+        break;
+    case TXCTRL2_6BITS:
+        data_bits = 6;
+        break;
+    default:
+    case TXCTRL2_8BITS:
+        data_bits = 8;
+        break;
+    }
+    speed = s->clock / ((s->wregs[W_BRGLO] | (s->wregs[W_BRGHI] << 8)) + 2);
+    switch (s->wregs[W_TXCTRL1] & TXCTRL1_CLKMSK) {
+    case TXCTRL1_CLK1X:
+        break;
+    case TXCTRL1_CLK16X:
+        speed /= 16;
+        break;
+    case TXCTRL1_CLK32X:
+        speed /= 32;
+        break;
+    default:
+    case TXCTRL1_CLK64X:
+        speed /= 64;
+        break;
+    }
+    ssp.speed = speed;
+    ssp.parity = parity;
+    ssp.data_bits = data_bits;
+    ssp.stop_bits = stop_bits;
+    trace_escc_update_parameters(CHN_C(s), speed, parity, data_bits, stop_bits);
+    qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+}
+
+static void escc_mem_write(void *opaque, hwaddr addr,
+                           uint64_t val, unsigned size)
+{
+    SerialState *serial = opaque;
+    ChannelState *s;
+    uint32_t saddr;
+    int newreg, channel;
+
+    val &= 0xff;
+    saddr = (addr >> serial->it_shift) & 1;
+    channel = (addr >> (serial->it_shift + 1)) & 1;
+    s = &serial->chn[channel];
+    switch (saddr) {
+    case SERIAL_CTRL:
+        trace_escc_mem_writeb_ctrl(CHN_C(s), s->reg, val & 0xff);
+        newreg = 0;
+        switch (s->reg) {
+        case W_CMD:
+            newreg = val & CMD_PTR_MASK;
+            val &= CMD_CMD_MASK;
+            switch (val) {
+            case CMD_HI:
+                newreg |= CMD_HI;
+                break;
+            case CMD_CLR_TXINT:
+                clr_txint(s);
+                break;
+            case CMD_CLR_IUS:
+                if (s->rxint_under_svc) {
+                    s->rxint_under_svc = 0;
+                    if (s->txint) {
+                        set_txint(s);
+                    }
+                } else if (s->txint_under_svc) {
+                    s->txint_under_svc = 0;
+                }
+                escc_update_irq(s);
+                break;
+            default:
+                break;
+            }
+            break;
+        case W_INTR ... W_RXCTRL:
+        case W_SYNC1 ... W_TXBUF:
+        case W_MISC1 ... W_CLOCK:
+        case W_MISC2 ... W_EXTINT:
+            s->wregs[s->reg] = val;
+            break;
+        case W_TXCTRL1:
+        case W_TXCTRL2:
+            s->wregs[s->reg] = val;
+            escc_update_parameters(s);
+            break;
+        case W_BRGLO:
+        case W_BRGHI:
+            s->wregs[s->reg] = val;
+            s->rregs[s->reg] = val;
+            escc_update_parameters(s);
+            break;
+        case W_MINTR:
+            switch (val & MINTR_RST_MASK) {
+            case 0:
+            default:
+                break;
+            case MINTR_RST_B:
+                escc_reset_chn(&serial->chn[0]);
+                return;
+            case MINTR_RST_A:
+                escc_reset_chn(&serial->chn[1]);
+                return;
+            case MINTR_RST_ALL:
+                escc_reset(&serial->busdev.qdev);
+                return;
+            }
+            break;
+        default:
+            break;
+        }
+        if (s->reg == 0)
+            s->reg = newreg;
+        else
+            s->reg = 0;
+        break;
+    case SERIAL_DATA:
+        trace_escc_mem_writeb_data(CHN_C(s), val);
+        s->tx = val;
+        if (s->wregs[W_TXCTRL2] & TXCTRL2_TXEN) { // tx enabled
+            if (s->chr)
+                qemu_chr_fe_write(s->chr, &s->tx, 1);
+            else if (s->type == kbd && !s->disabled) {
+                handle_kbd_command(s, val);
+            }
+        }
+        s->rregs[R_STATUS] |= STATUS_TXEMPTY; // Tx buffer empty
+        s->rregs[R_SPEC] |= SPEC_ALLSENT; // All sent
+        set_txint(s);
+        break;
+    default:
+        break;
+    }
+}
+
+static uint64_t escc_mem_read(void *opaque, hwaddr addr,
+                              unsigned size)
+{
+    SerialState *serial = opaque;
+    ChannelState *s;
+    uint32_t saddr;
+    uint32_t ret;
+    int channel;
+
+    saddr = (addr >> serial->it_shift) & 1;
+    channel = (addr >> (serial->it_shift + 1)) & 1;
+    s = &serial->chn[channel];
+    switch (saddr) {
+    case SERIAL_CTRL:
+        trace_escc_mem_readb_ctrl(CHN_C(s), s->reg, s->rregs[s->reg]);
+        ret = s->rregs[s->reg];
+        s->reg = 0;
+        return ret;
+    case SERIAL_DATA:
+        s->rregs[R_STATUS] &= ~STATUS_RXAV;
+        clr_rxint(s);
+        if (s->type == kbd || s->type == mouse)
+            ret = get_queue(s);
+        else
+            ret = s->rx;
+        trace_escc_mem_readb_data(CHN_C(s), ret);
+        if (s->chr)
+            qemu_chr_accept_input(s->chr);
+        return ret;
+    default:
+        break;
+    }
+    return 0;
+}
+
+static const MemoryRegionOps escc_mem_ops = {
+    .read = escc_mem_read,
+    .write = escc_mem_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static int serial_can_receive(void *opaque)
+{
+    ChannelState *s = opaque;
+    int ret;
+
+    if (((s->wregs[W_RXCTRL] & RXCTRL_RXEN) == 0) // Rx not enabled
+        || ((s->rregs[R_STATUS] & STATUS_RXAV) == STATUS_RXAV))
+        // char already available
+        ret = 0;
+    else
+        ret = 1;
+    return ret;
+}
+
+static void serial_receive_byte(ChannelState *s, int ch)
+{
+    trace_escc_serial_receive_byte(CHN_C(s), ch);
+    s->rregs[R_STATUS] |= STATUS_RXAV;
+    s->rx = ch;
+    set_rxint(s);
+}
+
+static void serial_receive_break(ChannelState *s)
+{
+    s->rregs[R_STATUS] |= STATUS_BRK;
+    escc_update_irq(s);
+}
+
+static void serial_receive1(void *opaque, const uint8_t *buf, int size)
+{
+    ChannelState *s = opaque;
+    serial_receive_byte(s, buf[0]);
+}
+
+static void serial_event(void *opaque, int event)
+{
+    ChannelState *s = opaque;
+    if (event == CHR_EVENT_BREAK)
+        serial_receive_break(s);
+}
+
+static const VMStateDescription vmstate_escc_chn = {
+    .name ="escc_chn",
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT32(vmstate_dummy, ChannelState),
+        VMSTATE_UINT32(reg, ChannelState),
+        VMSTATE_UINT32(rxint, ChannelState),
+        VMSTATE_UINT32(txint, ChannelState),
+        VMSTATE_UINT32(rxint_under_svc, ChannelState),
+        VMSTATE_UINT32(txint_under_svc, ChannelState),
+        VMSTATE_UINT8(rx, ChannelState),
+        VMSTATE_UINT8(tx, ChannelState),
+        VMSTATE_BUFFER(wregs, ChannelState),
+        VMSTATE_BUFFER(rregs, ChannelState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_escc = {
+    .name ="escc",
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_STRUCT_ARRAY(chn, SerialState, 2, 2, vmstate_escc_chn,
+                             ChannelState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB,
+              CharDriverState *chrA, CharDriverState *chrB,
+              int clock, int it_shift)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+    SerialState *d;
+
+    dev = qdev_create(NULL, "escc");
+    qdev_prop_set_uint32(dev, "disabled", 0);
+    qdev_prop_set_uint32(dev, "frequency", clock);
+    qdev_prop_set_uint32(dev, "it_shift", it_shift);
+    qdev_prop_set_chr(dev, "chrB", chrB);
+    qdev_prop_set_chr(dev, "chrA", chrA);
+    qdev_prop_set_uint32(dev, "chnBtype", ser);
+    qdev_prop_set_uint32(dev, "chnAtype", ser);
+    qdev_init_nofail(dev);
+    s = SYS_BUS_DEVICE(dev);
+    sysbus_connect_irq(s, 0, irqB);
+    sysbus_connect_irq(s, 1, irqA);
+    if (base) {
+        sysbus_mmio_map(s, 0, base);
+    }
+
+    d = FROM_SYSBUS(SerialState, s);
+    return &d->mmio;
+}
+
+static const uint8_t keycodes[128] = {
+    127, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 53,
+    54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 89, 76, 77, 78,
+    79, 80, 81, 82, 83, 84, 85, 86, 87, 42, 99, 88, 100, 101, 102, 103,
+    104, 105, 106, 107, 108, 109, 110, 47, 19, 121, 119, 5, 6, 8, 10, 12,
+    14, 16, 17, 18, 7, 98, 23, 68, 69, 70, 71, 91, 92, 93, 125, 112,
+    113, 114, 94, 50, 0, 0, 124, 9, 11, 0, 0, 0, 0, 0, 0, 0,
+    90, 0, 46, 22, 13, 111, 52, 20, 96, 24, 28, 74, 27, 123, 44, 66,
+    0, 45, 2, 4, 48, 0, 0, 21, 0, 0, 0, 0, 0, 120, 122, 67,
+};
+
+static const uint8_t e0_keycodes[128] = {
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 76, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 109, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 68, 69, 70, 0, 91, 0, 93, 0, 112,
+    113, 114, 94, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    1, 3, 25, 26, 49, 52, 72, 73, 97, 99, 111, 118, 120, 122, 67, 0,
+};
+
+static void sunkbd_event(void *opaque, int ch)
+{
+    ChannelState *s = opaque;
+    int release = ch & 0x80;
+
+    trace_escc_sunkbd_event_in(ch);
+    switch (ch) {
+    case 58: // Caps lock press
+        s->caps_lock_mode ^= 1;
+        if (s->caps_lock_mode == 2)
+            return; // Drop second press
+        break;
+    case 69: // Num lock press
+        s->num_lock_mode ^= 1;
+        if (s->num_lock_mode == 2)
+            return; // Drop second press
+        break;
+    case 186: // Caps lock release
+        s->caps_lock_mode ^= 2;
+        if (s->caps_lock_mode == 3)
+            return; // Drop first release
+        break;
+    case 197: // Num lock release
+        s->num_lock_mode ^= 2;
+        if (s->num_lock_mode == 3)
+            return; // Drop first release
+        break;
+    case 0xe0:
+        s->e0_mode = 1;
+        return;
+    default:
+        break;
+    }
+    if (s->e0_mode) {
+        s->e0_mode = 0;
+        ch = e0_keycodes[ch & 0x7f];
+    } else {
+        ch = keycodes[ch & 0x7f];
+    }
+    trace_escc_sunkbd_event_out(ch);
+    put_queue(s, ch | release);
+}
+
+static void handle_kbd_command(ChannelState *s, int val)
+{
+    trace_escc_kbd_command(val);
+    if (s->led_mode) { // Ignore led byte
+        s->led_mode = 0;
+        return;
+    }
+    switch (val) {
+    case 1: // Reset, return type code
+        clear_queue(s);
+        put_queue(s, 0xff);
+        put_queue(s, 4); // Type 4
+        put_queue(s, 0x7f);
+        break;
+    case 0xe: // Set leds
+        s->led_mode = 1;
+        break;
+    case 7: // Query layout
+    case 0xf:
+        clear_queue(s);
+        put_queue(s, 0xfe);
+        put_queue(s, 0); // XXX, layout?
+        break;
+    default:
+        break;
+    }
+}
+
+static void sunmouse_event(void *opaque,
+                               int dx, int dy, int dz, int buttons_state)
+{
+    ChannelState *s = opaque;
+    int ch;
+
+    trace_escc_sunmouse_event(dx, dy, buttons_state);
+    ch = 0x80 | 0x7; /* protocol start byte, no buttons pressed */
+
+    if (buttons_state & MOUSE_EVENT_LBUTTON)
+        ch ^= 0x4;
+    if (buttons_state & MOUSE_EVENT_MBUTTON)
+        ch ^= 0x2;
+    if (buttons_state & MOUSE_EVENT_RBUTTON)
+        ch ^= 0x1;
+
+    put_queue(s, ch);
+
+    ch = dx;
+
+    if (ch > 127)
+        ch = 127;
+    else if (ch < -127)
+        ch = -127;
+
+    put_queue(s, ch & 0xff);
+
+    ch = -dy;
+
+    if (ch > 127)
+        ch = 127;
+    else if (ch < -127)
+        ch = -127;
+
+    put_queue(s, ch & 0xff);
+
+    // MSC protocol specify two extra motion bytes
+
+    put_queue(s, 0);
+    put_queue(s, 0);
+}
+
+void slavio_serial_ms_kbd_init(hwaddr base, qemu_irq irq,
+                               int disabled, int clock, int it_shift)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    dev = qdev_create(NULL, "escc");
+    qdev_prop_set_uint32(dev, "disabled", disabled);
+    qdev_prop_set_uint32(dev, "frequency", clock);
+    qdev_prop_set_uint32(dev, "it_shift", it_shift);
+    qdev_prop_set_chr(dev, "chrB", NULL);
+    qdev_prop_set_chr(dev, "chrA", NULL);
+    qdev_prop_set_uint32(dev, "chnBtype", mouse);
+    qdev_prop_set_uint32(dev, "chnAtype", kbd);
+    qdev_init_nofail(dev);
+    s = SYS_BUS_DEVICE(dev);
+    sysbus_connect_irq(s, 0, irq);
+    sysbus_connect_irq(s, 1, irq);
+    sysbus_mmio_map(s, 0, base);
+}
+
+static int escc_init1(SysBusDevice *dev)
+{
+    SerialState *s = FROM_SYSBUS(SerialState, dev);
+    unsigned int i;
+
+    s->chn[0].disabled = s->disabled;
+    s->chn[1].disabled = s->disabled;
+    for (i = 0; i < 2; i++) {
+        sysbus_init_irq(dev, &s->chn[i].irq);
+        s->chn[i].chn = 1 - i;
+        s->chn[i].clock = s->frequency / 2;
+        if (s->chn[i].chr) {
+            qemu_chr_add_handlers(s->chn[i].chr, serial_can_receive,
+                                  serial_receive1, serial_event, &s->chn[i]);
+        }
+    }
+    s->chn[0].otherchn = &s->chn[1];
+    s->chn[1].otherchn = &s->chn[0];
+
+    memory_region_init_io(&s->mmio, &escc_mem_ops, s, "escc",
+                          ESCC_SIZE << s->it_shift);
+    sysbus_init_mmio(dev, &s->mmio);
+
+    if (s->chn[0].type == mouse) {
+        qemu_add_mouse_event_handler(sunmouse_event, &s->chn[0], 0,
+                                     "QEMU Sun Mouse");
+    }
+    if (s->chn[1].type == kbd) {
+        qemu_add_kbd_event_handler(sunkbd_event, &s->chn[1]);
+    }
+
+    return 0;
+}
+
+static Property escc_properties[] = {
+    DEFINE_PROP_UINT32("frequency", SerialState, frequency,   0),
+    DEFINE_PROP_UINT32("it_shift",  SerialState, it_shift,    0),
+    DEFINE_PROP_UINT32("disabled",  SerialState, disabled,    0),
+    DEFINE_PROP_UINT32("chnBtype",  SerialState, chn[0].type, 0),
+    DEFINE_PROP_UINT32("chnAtype",  SerialState, chn[1].type, 0),
+    DEFINE_PROP_CHR("chrB", SerialState, chn[0].chr),
+    DEFINE_PROP_CHR("chrA", SerialState, chn[1].chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void escc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = escc_init1;
+    dc->reset = escc_reset;
+    dc->vmsd = &vmstate_escc;
+    dc->props = escc_properties;
+}
+
+static const TypeInfo escc_info = {
+    .name          = "escc",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SerialState),
+    .class_init    = escc_class_init,
+};
+
+static void escc_register_types(void)
+{
+    type_register_static(&escc_info);
+}
+
+type_init(escc_register_types)
diff --git a/hw/char/ipack.c b/hw/char/ipack.c
new file mode 100644 (file)
index 0000000..b1f46c1
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * QEMU IndustryPack emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#include "hw/ipack.h"
+
+IPackDevice *ipack_device_find(IPackBus *bus, int32_t slot)
+{
+    BusChild *kid;
+
+    QTAILQ_FOREACH(kid, &BUS(bus)->children, sibling) {
+        DeviceState *qdev = kid->child;
+        IPackDevice *ip = IPACK_DEVICE(qdev);
+        if (ip->slot == slot) {
+            return ip;
+        }
+    }
+    return NULL;
+}
+
+void ipack_bus_new_inplace(IPackBus *bus, DeviceState *parent,
+                           const char *name, uint8_t n_slots,
+                           qemu_irq_handler handler)
+{
+    qbus_create_inplace(&bus->qbus, TYPE_IPACK_BUS, parent, name);
+    bus->n_slots = n_slots;
+    bus->set_irq = handler;
+}
+
+static int ipack_device_dev_init(DeviceState *qdev)
+{
+    IPackBus *bus = IPACK_BUS(qdev_get_parent_bus(qdev));
+    IPackDevice *dev = IPACK_DEVICE(qdev);
+    IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev);
+
+    if (dev->slot < 0) {
+        dev->slot = bus->free_slot;
+    }
+    if (dev->slot >= bus->n_slots) {
+        return -1;
+    }
+    bus->free_slot = dev->slot + 1;
+
+    dev->irq = qemu_allocate_irqs(bus->set_irq, dev, 2);
+
+    return k->init(dev);
+}
+
+static int ipack_device_dev_exit(DeviceState *qdev)
+{
+    IPackDevice *dev = IPACK_DEVICE(qdev);
+    IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev);
+
+    if (k->exit) {
+        k->exit(dev);
+    }
+
+    qemu_free_irqs(dev->irq);
+
+    return 0;
+}
+
+static Property ipack_device_props[] = {
+    DEFINE_PROP_INT32("slot", IPackDevice, slot, -1),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void ipack_device_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *k = DEVICE_CLASS(klass);
+    k->bus_type = TYPE_IPACK_BUS;
+    k->init = ipack_device_dev_init;
+    k->exit = ipack_device_dev_exit;
+    k->props = ipack_device_props;
+}
+
+const VMStateDescription vmstate_ipack_device = {
+    .name = "ipack_device",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT32(slot, IPackDevice),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const TypeInfo ipack_device_info = {
+    .name          = TYPE_IPACK_DEVICE,
+    .parent        = TYPE_DEVICE,
+    .instance_size = sizeof(IPackDevice),
+    .class_size    = sizeof(IPackDeviceClass),
+    .class_init    = ipack_device_class_init,
+    .abstract      = true,
+};
+
+static const TypeInfo ipack_bus_info = {
+    .name = TYPE_IPACK_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(IPackBus),
+};
+
+static void ipack_register_types(void)
+{
+    type_register_static(&ipack_device_info);
+    type_register_static(&ipack_bus_info);
+}
+
+type_init(ipack_register_types)
diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c
new file mode 100644 (file)
index 0000000..685fee2
--- /dev/null
@@ -0,0 +1,605 @@
+/*
+ * QEMU GE IP-Octal 232 IndustryPack emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#include "hw/ipack.h"
+#include "qemu/bitops.h"
+#include "char/char.h"
+
+/* #define DEBUG_IPOCTAL */
+
+#ifdef DEBUG_IPOCTAL
+#define DPRINTF2(fmt, ...) \
+    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF2(fmt, ...) do { } while (0)
+#endif
+
+#define DPRINTF(fmt, ...) DPRINTF2("IP-Octal: " fmt, ## __VA_ARGS__)
+
+#define RX_FIFO_SIZE 3
+
+/* The IP-Octal has 8 channels (a-h)
+   divided into 4 blocks (A-D) */
+#define N_CHANNELS 8
+#define N_BLOCKS   4
+
+#define REG_MRa  0x01
+#define REG_MRb  0x11
+#define REG_SRa  0x03
+#define REG_SRb  0x13
+#define REG_CSRa 0x03
+#define REG_CSRb 0x13
+#define REG_CRa  0x05
+#define REG_CRb  0x15
+#define REG_RHRa 0x07
+#define REG_RHRb 0x17
+#define REG_THRa 0x07
+#define REG_THRb 0x17
+#define REG_ACR  0x09
+#define REG_ISR  0x0B
+#define REG_IMR  0x0B
+#define REG_OPCR 0x1B
+
+#define CR_ENABLE_RX    BIT(0)
+#define CR_DISABLE_RX   BIT(1)
+#define CR_ENABLE_TX    BIT(2)
+#define CR_DISABLE_TX   BIT(3)
+#define CR_CMD(cr)      ((cr) >> 4)
+#define CR_NO_OP        0
+#define CR_RESET_MR     1
+#define CR_RESET_RX     2
+#define CR_RESET_TX     3
+#define CR_RESET_ERR    4
+#define CR_RESET_BRKINT 5
+#define CR_START_BRK    6
+#define CR_STOP_BRK     7
+#define CR_ASSERT_RTSN  8
+#define CR_NEGATE_RTSN  9
+#define CR_TIMEOUT_ON   10
+#define CR_TIMEOUT_OFF  12
+
+#define SR_RXRDY   BIT(0)
+#define SR_FFULL   BIT(1)
+#define SR_TXRDY   BIT(2)
+#define SR_TXEMT   BIT(3)
+#define SR_OVERRUN BIT(4)
+#define SR_PARITY  BIT(5)
+#define SR_FRAMING BIT(6)
+#define SR_BREAK   BIT(7)
+
+#define ISR_TXRDYA BIT(0)
+#define ISR_RXRDYA BIT(1)
+#define ISR_BREAKA BIT(2)
+#define ISR_CNTRDY BIT(3)
+#define ISR_TXRDYB BIT(4)
+#define ISR_RXRDYB BIT(5)
+#define ISR_BREAKB BIT(6)
+#define ISR_MPICHG BIT(7)
+#define ISR_TXRDY(CH) (((CH) & 1) ? BIT(4) : BIT(0))
+#define ISR_RXRDY(CH) (((CH) & 1) ? BIT(5) : BIT(1))
+#define ISR_BREAK(CH) (((CH) & 1) ? BIT(6) : BIT(2))
+
+typedef struct IPOctalState IPOctalState;
+typedef struct SCC2698Channel SCC2698Channel;
+typedef struct SCC2698Block SCC2698Block;
+
+struct SCC2698Channel {
+    IPOctalState *ipoctal;
+    CharDriverState *dev;
+    bool rx_enabled;
+    uint8_t mr[2];
+    uint8_t mr_idx;
+    uint8_t sr;
+    uint8_t rhr[RX_FIFO_SIZE];
+    uint8_t rhr_idx;
+    uint8_t rx_pending;
+};
+
+struct SCC2698Block {
+    uint8_t imr;
+    uint8_t isr;
+};
+
+struct IPOctalState {
+    IPackDevice dev;
+    SCC2698Channel ch[N_CHANNELS];
+    SCC2698Block blk[N_BLOCKS];
+    uint8_t irq_vector;
+};
+
+#define TYPE_IPOCTAL "ipoctal232"
+
+#define IPOCTAL(obj) \
+    OBJECT_CHECK(IPOctalState, (obj), TYPE_IPOCTAL)
+
+static const VMStateDescription vmstate_scc2698_channel = {
+    .name = "scc2698_channel",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_BOOL(rx_enabled, SCC2698Channel),
+        VMSTATE_UINT8_ARRAY(mr, SCC2698Channel, 2),
+        VMSTATE_UINT8(mr_idx, SCC2698Channel),
+        VMSTATE_UINT8(sr, SCC2698Channel),
+        VMSTATE_UINT8_ARRAY(rhr, SCC2698Channel, RX_FIFO_SIZE),
+        VMSTATE_UINT8(rhr_idx, SCC2698Channel),
+        VMSTATE_UINT8(rx_pending, SCC2698Channel),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_scc2698_block = {
+    .name = "scc2698_block",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(imr, SCC2698Block),
+        VMSTATE_UINT8(isr, SCC2698Block),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_ipoctal = {
+    .name = "ipoctal232",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_IPACK_DEVICE(dev, IPOctalState),
+        VMSTATE_STRUCT_ARRAY(ch, IPOctalState, N_CHANNELS, 1,
+                             vmstate_scc2698_channel, SCC2698Channel),
+        VMSTATE_STRUCT_ARRAY(blk, IPOctalState, N_BLOCKS, 1,
+                             vmstate_scc2698_block, SCC2698Block),
+        VMSTATE_UINT8(irq_vector, IPOctalState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/* data[10] is 0x0C, not 0x0B as the doc says */
+static const uint8_t id_prom_data[] = {
+    0x49, 0x50, 0x41, 0x43, 0xF0, 0x22,
+    0xA1, 0x00, 0x00, 0x00, 0x0C, 0xCC
+};
+
+static void update_irq(IPOctalState *dev, unsigned block)
+{
+    /* Blocks A and B interrupt on INT0#, C and D on INT1#.
+       Thus, to get the status we have to check two blocks. */
+    SCC2698Block *blk0 = &dev->blk[block];
+    SCC2698Block *blk1 = &dev->blk[block^1];
+    unsigned intno = block / 2;
+
+    if ((blk0->isr & blk0->imr) || (blk1->isr & blk1->imr)) {
+        qemu_irq_raise(dev->dev.irq[intno]);
+    } else {
+        qemu_irq_lower(dev->dev.irq[intno]);
+    }
+}
+
+static void write_cr(IPOctalState *dev, unsigned channel, uint8_t val)
+{
+    SCC2698Channel *ch = &dev->ch[channel];
+    SCC2698Block *blk = &dev->blk[channel / 2];
+
+    DPRINTF("Write CR%c %u: ", channel + 'a', val);
+
+    /* The lower 4 bits are used to enable and disable Tx and Rx */
+    if (val & CR_ENABLE_RX) {
+        DPRINTF2("Rx on, ");
+        ch->rx_enabled = true;
+    }
+    if (val & CR_DISABLE_RX) {
+        DPRINTF2("Rx off, ");
+        ch->rx_enabled = false;
+    }
+    if (val & CR_ENABLE_TX) {
+        DPRINTF2("Tx on, ");
+        ch->sr |= SR_TXRDY | SR_TXEMT;
+        blk->isr |= ISR_TXRDY(channel);
+    }
+    if (val & CR_DISABLE_TX) {
+        DPRINTF2("Tx off, ");
+        ch->sr &= ~(SR_TXRDY | SR_TXEMT);
+        blk->isr &= ~ISR_TXRDY(channel);
+    }
+
+    DPRINTF2("cmd: ");
+
+    /* The rest of the bits implement different commands */
+    switch (CR_CMD(val)) {
+    case CR_NO_OP:
+        DPRINTF2("none");
+        break;
+    case CR_RESET_MR:
+        DPRINTF2("reset MR");
+        ch->mr_idx = 0;
+        break;
+    case CR_RESET_RX:
+        DPRINTF2("reset Rx");
+        ch->rx_enabled = false;
+        ch->rx_pending = 0;
+        ch->sr &= ~SR_RXRDY;
+        blk->isr &= ~ISR_RXRDY(channel);
+        break;
+    case CR_RESET_TX:
+        DPRINTF2("reset Tx");
+        ch->sr &= ~(SR_TXRDY | SR_TXEMT);
+        blk->isr &= ~ISR_TXRDY(channel);
+        break;
+    case CR_RESET_ERR:
+        DPRINTF2("reset err");
+        ch->sr &= ~(SR_OVERRUN | SR_PARITY | SR_FRAMING | SR_BREAK);
+        break;
+    case CR_RESET_BRKINT:
+        DPRINTF2("reset brk ch int");
+        blk->isr &= ~(ISR_BREAKA | ISR_BREAKB);
+        break;
+    default:
+        DPRINTF2("unsupported 0x%x", CR_CMD(val));
+    }
+
+    DPRINTF2("\n");
+}
+
+static uint16_t io_read(IPackDevice *ip, uint8_t addr)
+{
+    IPOctalState *dev = IPOCTAL(ip);
+    uint16_t ret = 0;
+    /* addr[7:6]: block   (A-D)
+       addr[7:5]: channel (a-h)
+       addr[5:0]: register */
+    unsigned block = addr >> 5;
+    unsigned channel = addr >> 4;
+    /* Big endian, accessed using 8-bit bytes at odd locations */
+    unsigned offset = (addr & 0x1F) ^ 1;
+    SCC2698Channel *ch = &dev->ch[channel];
+    SCC2698Block *blk = &dev->blk[block];
+    uint8_t old_isr = blk->isr;
+
+    switch (offset) {
+
+    case REG_MRa:
+    case REG_MRb:
+        ret = ch->mr[ch->mr_idx];
+        DPRINTF("Read MR%u%c: 0x%x\n", ch->mr_idx + 1, channel + 'a', ret);
+        ch->mr_idx = 1;
+        break;
+
+    case REG_SRa:
+    case REG_SRb:
+        ret = ch->sr;
+        DPRINTF("Read SR%c: 0x%x\n", channel + 'a', ret);
+        break;
+
+    case REG_RHRa:
+    case REG_RHRb:
+        ret = ch->rhr[ch->rhr_idx];
+        if (ch->rx_pending > 0) {
+            ch->rx_pending--;
+            if (ch->rx_pending == 0) {
+                ch->sr &= ~SR_RXRDY;
+                blk->isr &= ~ISR_RXRDY(channel);
+                if (ch->dev) {
+                    qemu_chr_accept_input(ch->dev);
+                }
+            } else {
+                ch->rhr_idx = (ch->rhr_idx + 1) % RX_FIFO_SIZE;
+            }
+            if (ch->sr & SR_BREAK) {
+                ch->sr &= ~SR_BREAK;
+                blk->isr |= ISR_BREAK(channel);
+            }
+        }
+        DPRINTF("Read RHR%c (0x%x)\n", channel + 'a', ret);
+        break;
+
+    case REG_ISR:
+        ret = blk->isr;
+        DPRINTF("Read ISR%c: 0x%x\n", block + 'A', ret);
+        break;
+
+    default:
+        DPRINTF("Read unknown/unsupported register 0x%02x\n", offset);
+    }
+
+    if (old_isr != blk->isr) {
+        update_irq(dev, block);
+    }
+
+    return ret;
+}
+
+static void io_write(IPackDevice *ip, uint8_t addr, uint16_t val)
+{
+    IPOctalState *dev = IPOCTAL(ip);
+    unsigned reg = val & 0xFF;
+    /* addr[7:6]: block   (A-D)
+       addr[7:5]: channel (a-h)
+       addr[5:0]: register */
+    unsigned block = addr >> 5;
+    unsigned channel = addr >> 4;
+    /* Big endian, accessed using 8-bit bytes at odd locations */
+    unsigned offset = (addr & 0x1F) ^ 1;
+    SCC2698Channel *ch = &dev->ch[channel];
+    SCC2698Block *blk = &dev->blk[block];
+    uint8_t old_isr = blk->isr;
+    uint8_t old_imr = blk->imr;
+
+    switch (offset) {
+
+    case REG_MRa:
+    case REG_MRb:
+        ch->mr[ch->mr_idx] = reg;
+        DPRINTF("Write MR%u%c 0x%x\n", ch->mr_idx + 1, channel + 'a', reg);
+        ch->mr_idx = 1;
+        break;
+
+    /* Not implemented */
+    case REG_CSRa:
+    case REG_CSRb:
+        DPRINTF("Write CSR%c: 0x%x\n", channel + 'a', reg);
+        break;
+
+    case REG_CRa:
+    case REG_CRb:
+        write_cr(dev, channel, reg);
+        break;
+
+    case REG_THRa:
+    case REG_THRb:
+        if (ch->sr & SR_TXRDY) {
+            DPRINTF("Write THR%c (0x%x)\n", channel + 'a', reg);
+            if (ch->dev) {
+                uint8_t thr = reg;
+                qemu_chr_fe_write(ch->dev, &thr, 1);
+            }
+        } else {
+            DPRINTF("Write THR%c (0x%x), Tx disabled\n", channel + 'a', reg);
+        }
+        break;
+
+    /* Not implemented */
+    case REG_ACR:
+        DPRINTF("Write ACR%c 0x%x\n", block + 'A', val);
+        break;
+
+    case REG_IMR:
+        DPRINTF("Write IMR%c 0x%x\n", block + 'A', val);
+        blk->imr = reg;
+        break;
+
+    /* Not implemented */
+    case REG_OPCR:
+        DPRINTF("Write OPCR%c 0x%x\n", block + 'A', val);
+        break;
+
+    default:
+        DPRINTF("Write unknown/unsupported register 0x%02x %u\n", offset, val);
+    }
+
+    if (old_isr != blk->isr || old_imr != blk->imr) {
+        update_irq(dev, block);
+    }
+}
+
+static uint16_t id_read(IPackDevice *ip, uint8_t addr)
+{
+    uint16_t ret = 0;
+    unsigned pos = addr / 2; /* The ID PROM data is stored every other byte */
+
+    if (pos < ARRAY_SIZE(id_prom_data)) {
+        ret = id_prom_data[pos];
+    } else {
+        DPRINTF("Attempt to read unavailable PROM data at 0x%x\n",  addr);
+    }
+
+    return ret;
+}
+
+static void id_write(IPackDevice *ip, uint8_t addr, uint16_t val)
+{
+    IPOctalState *dev = IPOCTAL(ip);
+    if (addr == 1) {
+        DPRINTF("Write IRQ vector: %u\n", (unsigned) val);
+        dev->irq_vector = val; /* Undocumented, but the hw works like that */
+    } else {
+        DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+    }
+}
+
+static uint16_t int_read(IPackDevice *ip, uint8_t addr)
+{
+    IPOctalState *dev = IPOCTAL(ip);
+    /* Read address 0 to ACK INT0# and address 2 to ACK INT1# */
+    if (addr != 0 && addr != 2) {
+        DPRINTF("Attempt to read from 0x%x\n", addr);
+        return 0;
+    } else {
+        /* Update interrupts if necessary */
+        update_irq(dev, addr);
+        return dev->irq_vector;
+    }
+}
+
+static void int_write(IPackDevice *ip, uint8_t addr, uint16_t val)
+{
+    DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+}
+
+static uint16_t mem_read16(IPackDevice *ip, uint32_t addr)
+{
+    DPRINTF("Attempt to read from 0x%x\n", addr);
+    return 0;
+}
+
+static void mem_write16(IPackDevice *ip, uint32_t addr, uint16_t val)
+{
+    DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+}
+
+static uint8_t mem_read8(IPackDevice *ip, uint32_t addr)
+{
+    DPRINTF("Attempt to read from 0x%x\n", addr);
+    return 0;
+}
+
+static void mem_write8(IPackDevice *ip, uint32_t addr, uint8_t val)
+{
+    IPOctalState *dev = IPOCTAL(ip);
+    if (addr == 1) {
+        DPRINTF("Write IRQ vector: %u\n", (unsigned) val);
+        dev->irq_vector = val;
+    } else {
+        DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
+    }
+}
+
+static int hostdev_can_receive(void *opaque)
+{
+    SCC2698Channel *ch = opaque;
+    int available_bytes = RX_FIFO_SIZE - ch->rx_pending;
+    return ch->rx_enabled ? available_bytes : 0;
+}
+
+static void hostdev_receive(void *opaque, const uint8_t *buf, int size)
+{
+    SCC2698Channel *ch = opaque;
+    IPOctalState *dev = ch->ipoctal;
+    unsigned pos = ch->rhr_idx + ch->rx_pending;
+    int i;
+
+    assert(size + ch->rx_pending <= RX_FIFO_SIZE);
+
+    /* Copy data to the RxFIFO */
+    for (i = 0; i < size; i++) {
+        pos %= RX_FIFO_SIZE;
+        ch->rhr[pos++] = buf[i];
+    }
+
+    ch->rx_pending += size;
+
+    /* If the RxFIFO was empty raise an interrupt */
+    if (!(ch->sr & SR_RXRDY)) {
+        unsigned block, channel = 0;
+        /* Find channel number to update the ISR register */
+        while (&dev->ch[channel] != ch) {
+            channel++;
+        }
+        block = channel / 2;
+        dev->blk[block].isr |= ISR_RXRDY(channel);
+        ch->sr |= SR_RXRDY;
+        update_irq(dev, block);
+    }
+}
+
+static void hostdev_event(void *opaque, int event)
+{
+    SCC2698Channel *ch = opaque;
+    switch (event) {
+    case CHR_EVENT_OPENED:
+        DPRINTF("Device %s opened\n", ch->dev->label);
+        break;
+    case CHR_EVENT_BREAK: {
+        uint8_t zero = 0;
+        DPRINTF("Device %s received break\n", ch->dev->label);
+
+        if (!(ch->sr & SR_BREAK)) {
+            IPOctalState *dev = ch->ipoctal;
+            unsigned block, channel = 0;
+
+            while (&dev->ch[channel] != ch) {
+                channel++;
+            }
+            block = channel / 2;
+
+            ch->sr |= SR_BREAK;
+            dev->blk[block].isr |= ISR_BREAK(channel);
+        }
+
+        /* Put a zero character in the buffer */
+        hostdev_receive(ch, &zero, 1);
+    }
+        break;
+    default:
+        DPRINTF("Device %s received event %d\n", ch->dev->label, event);
+    }
+}
+
+static int ipoctal_init(IPackDevice *ip)
+{
+    IPOctalState *s = IPOCTAL(ip);
+    unsigned i;
+
+    for (i = 0; i < N_CHANNELS; i++) {
+        SCC2698Channel *ch = &s->ch[i];
+        ch->ipoctal = s;
+
+        /* Redirect IP-Octal channels to host character devices */
+        if (ch->dev) {
+            qemu_chr_add_handlers(ch->dev, hostdev_can_receive,
+                                  hostdev_receive, hostdev_event, ch);
+            DPRINTF("Redirecting channel %u to %s\n", i, ch->dev->label);
+        } else {
+            DPRINTF("Could not redirect channel %u, no chardev set\n", i);
+        }
+    }
+
+    return 0;
+}
+
+static Property ipoctal_properties[] = {
+    DEFINE_PROP_CHR("chardev0", IPOctalState, ch[0].dev),
+    DEFINE_PROP_CHR("chardev1", IPOctalState, ch[1].dev),
+    DEFINE_PROP_CHR("chardev2", IPOctalState, ch[2].dev),
+    DEFINE_PROP_CHR("chardev3", IPOctalState, ch[3].dev),
+    DEFINE_PROP_CHR("chardev4", IPOctalState, ch[4].dev),
+    DEFINE_PROP_CHR("chardev5", IPOctalState, ch[5].dev),
+    DEFINE_PROP_CHR("chardev6", IPOctalState, ch[6].dev),
+    DEFINE_PROP_CHR("chardev7", IPOctalState, ch[7].dev),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ipoctal_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    IPackDeviceClass *ic = IPACK_DEVICE_CLASS(klass);
+
+    ic->init        = ipoctal_init;
+    ic->io_read     = io_read;
+    ic->io_write    = io_write;
+    ic->id_read     = id_read;
+    ic->id_write    = id_write;
+    ic->int_read    = int_read;
+    ic->int_write   = int_write;
+    ic->mem_read16  = mem_read16;
+    ic->mem_write16 = mem_write16;
+    ic->mem_read8   = mem_read8;
+    ic->mem_write8  = mem_write8;
+
+    dc->desc    = "GE IP-Octal 232 8-channel RS-232 IndustryPack";
+    dc->props   = ipoctal_properties;
+    dc->vmsd    = &vmstate_ipoctal;
+}
+
+static const TypeInfo ipoctal_info = {
+    .name          = TYPE_IPOCTAL,
+    .parent        = TYPE_IPACK_DEVICE,
+    .instance_size = sizeof(IPOctalState),
+    .class_init    = ipoctal_class_init,
+};
+
+static void ipoctal_register_types(void)
+{
+    type_register_static(&ipoctal_info);
+}
+
+type_init(ipoctal_register_types)
diff --git a/hw/char/parallel.c b/hw/char/parallel.c
new file mode 100644 (file)
index 0000000..863a6fb
--- /dev/null
@@ -0,0 +1,614 @@
+/*
+ * QEMU Parallel PORT emulation
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2007 Marko Kohtala
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "char/char.h"
+#include "hw/isa/isa.h"
+#include "hw/i386/pc.h"
+#include "sysemu/sysemu.h"
+
+//#define DEBUG_PARALLEL
+
+#ifdef DEBUG_PARALLEL
+#define pdebug(fmt, ...) printf("pp: " fmt, ## __VA_ARGS__)
+#else
+#define pdebug(fmt, ...) ((void)0)
+#endif
+
+#define PARA_REG_DATA 0
+#define PARA_REG_STS 1
+#define PARA_REG_CTR 2
+#define PARA_REG_EPP_ADDR 3
+#define PARA_REG_EPP_DATA 4
+
+/*
+ * These are the definitions for the Printer Status Register
+ */
+#define PARA_STS_BUSY  0x80    /* Busy complement */
+#define PARA_STS_ACK   0x40    /* Acknowledge */
+#define PARA_STS_PAPER 0x20    /* Out of paper */
+#define PARA_STS_ONLINE        0x10    /* Online */
+#define PARA_STS_ERROR 0x08    /* Error complement */
+#define PARA_STS_TMOUT 0x01    /* EPP timeout */
+
+/*
+ * These are the definitions for the Printer Control Register
+ */
+#define PARA_CTR_DIR   0x20    /* Direction (1=read, 0=write) */
+#define PARA_CTR_INTEN 0x10    /* IRQ Enable */
+#define PARA_CTR_SELECT        0x08    /* Select In complement */
+#define PARA_CTR_INIT  0x04    /* Initialize Printer complement */
+#define PARA_CTR_AUTOLF        0x02    /* Auto linefeed complement */
+#define PARA_CTR_STROBE        0x01    /* Strobe complement */
+
+#define PARA_CTR_SIGNAL (PARA_CTR_SELECT|PARA_CTR_INIT|PARA_CTR_AUTOLF|PARA_CTR_STROBE)
+
+typedef struct ParallelState {
+    MemoryRegion iomem;
+    uint8_t dataw;
+    uint8_t datar;
+    uint8_t status;
+    uint8_t control;
+    qemu_irq irq;
+    int irq_pending;
+    CharDriverState *chr;
+    int hw_driver;
+    int epp_timeout;
+    uint32_t last_read_offset; /* For debugging */
+    /* Memory-mapped interface */
+    int it_shift;
+} ParallelState;
+
+typedef struct ISAParallelState {
+    ISADevice dev;
+    uint32_t index;
+    uint32_t iobase;
+    uint32_t isairq;
+    ParallelState state;
+} ISAParallelState;
+
+static void parallel_update_irq(ParallelState *s)
+{
+    if (s->irq_pending)
+        qemu_irq_raise(s->irq);
+    else
+        qemu_irq_lower(s->irq);
+}
+
+static void
+parallel_ioport_write_sw(void *opaque, uint32_t addr, uint32_t val)
+{
+    ParallelState *s = opaque;
+
+    pdebug("write addr=0x%02x val=0x%02x\n", addr, val);
+
+    addr &= 7;
+    switch(addr) {
+    case PARA_REG_DATA:
+        s->dataw = val;
+        parallel_update_irq(s);
+        break;
+    case PARA_REG_CTR:
+        val |= 0xc0;
+        if ((val & PARA_CTR_INIT) == 0 ) {
+            s->status = PARA_STS_BUSY;
+            s->status |= PARA_STS_ACK;
+            s->status |= PARA_STS_ONLINE;
+            s->status |= PARA_STS_ERROR;
+        }
+        else if (val & PARA_CTR_SELECT) {
+            if (val & PARA_CTR_STROBE) {
+                s->status &= ~PARA_STS_BUSY;
+                if ((s->control & PARA_CTR_STROBE) == 0)
+                    qemu_chr_fe_write(s->chr, &s->dataw, 1);
+            } else {
+                if (s->control & PARA_CTR_INTEN) {
+                    s->irq_pending = 1;
+                }
+            }
+        }
+        parallel_update_irq(s);
+        s->control = val;
+        break;
+    }
+}
+
+static void parallel_ioport_write_hw(void *opaque, uint32_t addr, uint32_t val)
+{
+    ParallelState *s = opaque;
+    uint8_t parm = val;
+    int dir;
+
+    /* Sometimes programs do several writes for timing purposes on old
+       HW. Take care not to waste time on writes that do nothing. */
+
+    s->last_read_offset = ~0U;
+
+    addr &= 7;
+    switch(addr) {
+    case PARA_REG_DATA:
+        if (s->dataw == val)
+            return;
+        pdebug("wd%02x\n", val);
+        qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_WRITE_DATA, &parm);
+        s->dataw = val;
+        break;
+    case PARA_REG_STS:
+        pdebug("ws%02x\n", val);
+        if (val & PARA_STS_TMOUT)
+            s->epp_timeout = 0;
+        break;
+    case PARA_REG_CTR:
+        val |= 0xc0;
+        if (s->control == val)
+            return;
+        pdebug("wc%02x\n", val);
+
+        if ((val & PARA_CTR_DIR) != (s->control & PARA_CTR_DIR)) {
+            if (val & PARA_CTR_DIR) {
+                dir = 1;
+            } else {
+                dir = 0;
+            }
+            qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_DATA_DIR, &dir);
+            parm &= ~PARA_CTR_DIR;
+        }
+
+        qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_WRITE_CONTROL, &parm);
+        s->control = val;
+        break;
+    case PARA_REG_EPP_ADDR:
+        if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT)
+            /* Controls not correct for EPP address cycle, so do nothing */
+            pdebug("wa%02x s\n", val);
+        else {
+            struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 };
+            if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE_ADDR, &ioarg)) {
+                s->epp_timeout = 1;
+                pdebug("wa%02x t\n", val);
+            }
+            else
+                pdebug("wa%02x\n", val);
+        }
+        break;
+    case PARA_REG_EPP_DATA:
+        if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT)
+            /* Controls not correct for EPP data cycle, so do nothing */
+            pdebug("we%02x s\n", val);
+        else {
+            struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 };
+            if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg)) {
+                s->epp_timeout = 1;
+                pdebug("we%02x t\n", val);
+            }
+            else
+                pdebug("we%02x\n", val);
+        }
+        break;
+    }
+}
+
+static void
+parallel_ioport_eppdata_write_hw2(void *opaque, uint32_t addr, uint32_t val)
+{
+    ParallelState *s = opaque;
+    uint16_t eppdata = cpu_to_le16(val);
+    int err;
+    struct ParallelIOArg ioarg = {
+        .buffer = &eppdata, .count = sizeof(eppdata)
+    };
+    if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) {
+        /* Controls not correct for EPP data cycle, so do nothing */
+        pdebug("we%04x s\n", val);
+        return;
+    }
+    err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg);
+    if (err) {
+        s->epp_timeout = 1;
+        pdebug("we%04x t\n", val);
+    }
+    else
+        pdebug("we%04x\n", val);
+}
+
+static void
+parallel_ioport_eppdata_write_hw4(void *opaque, uint32_t addr, uint32_t val)
+{
+    ParallelState *s = opaque;
+    uint32_t eppdata = cpu_to_le32(val);
+    int err;
+    struct ParallelIOArg ioarg = {
+        .buffer = &eppdata, .count = sizeof(eppdata)
+    };
+    if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) {
+        /* Controls not correct for EPP data cycle, so do nothing */
+        pdebug("we%08x s\n", val);
+        return;
+    }
+    err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg);
+    if (err) {
+        s->epp_timeout = 1;
+        pdebug("we%08x t\n", val);
+    }
+    else
+        pdebug("we%08x\n", val);
+}
+
+static uint32_t parallel_ioport_read_sw(void *opaque, uint32_t addr)
+{
+    ParallelState *s = opaque;
+    uint32_t ret = 0xff;
+
+    addr &= 7;
+    switch(addr) {
+    case PARA_REG_DATA:
+        if (s->control & PARA_CTR_DIR)
+            ret = s->datar;
+        else
+            ret = s->dataw;
+        break;
+    case PARA_REG_STS:
+        ret = s->status;
+        s->irq_pending = 0;
+        if ((s->status & PARA_STS_BUSY) == 0 && (s->control & PARA_CTR_STROBE) == 0) {
+            /* XXX Fixme: wait 5 microseconds */
+            if (s->status & PARA_STS_ACK)
+                s->status &= ~PARA_STS_ACK;
+            else {
+                /* XXX Fixme: wait 5 microseconds */
+                s->status |= PARA_STS_ACK;
+                s->status |= PARA_STS_BUSY;
+            }
+        }
+        parallel_update_irq(s);
+        break;
+    case PARA_REG_CTR:
+        ret = s->control;
+        break;
+    }
+    pdebug("read addr=0x%02x val=0x%02x\n", addr, ret);
+    return ret;
+}
+
+static uint32_t parallel_ioport_read_hw(void *opaque, uint32_t addr)
+{
+    ParallelState *s = opaque;
+    uint8_t ret = 0xff;
+    addr &= 7;
+    switch(addr) {
+    case PARA_REG_DATA:
+        qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_DATA, &ret);
+        if (s->last_read_offset != addr || s->datar != ret)
+            pdebug("rd%02x\n", ret);
+        s->datar = ret;
+        break;
+    case PARA_REG_STS:
+        qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &ret);
+        ret &= ~PARA_STS_TMOUT;
+        if (s->epp_timeout)
+            ret |= PARA_STS_TMOUT;
+        if (s->last_read_offset != addr || s->status != ret)
+            pdebug("rs%02x\n", ret);
+        s->status = ret;
+        break;
+    case PARA_REG_CTR:
+        /* s->control has some bits fixed to 1. It is zero only when
+           it has not been yet written to.  */
+        if (s->control == 0) {
+            qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_CONTROL, &ret);
+            if (s->last_read_offset != addr)
+                pdebug("rc%02x\n", ret);
+            s->control = ret;
+        }
+        else {
+            ret = s->control;
+            if (s->last_read_offset != addr)
+                pdebug("rc%02x\n", ret);
+        }
+        break;
+    case PARA_REG_EPP_ADDR:
+        if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT))
+            /* Controls not correct for EPP addr cycle, so do nothing */
+            pdebug("ra%02x s\n", ret);
+        else {
+            struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 };
+            if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ_ADDR, &ioarg)) {
+                s->epp_timeout = 1;
+                pdebug("ra%02x t\n", ret);
+            }
+            else
+                pdebug("ra%02x\n", ret);
+        }
+        break;
+    case PARA_REG_EPP_DATA:
+        if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT))
+            /* Controls not correct for EPP data cycle, so do nothing */
+            pdebug("re%02x s\n", ret);
+        else {
+            struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 };
+            if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg)) {
+                s->epp_timeout = 1;
+                pdebug("re%02x t\n", ret);
+            }
+            else
+                pdebug("re%02x\n", ret);
+        }
+        break;
+    }
+    s->last_read_offset = addr;
+    return ret;
+}
+
+static uint32_t
+parallel_ioport_eppdata_read_hw2(void *opaque, uint32_t addr)
+{
+    ParallelState *s = opaque;
+    uint32_t ret;
+    uint16_t eppdata = ~0;
+    int err;
+    struct ParallelIOArg ioarg = {
+        .buffer = &eppdata, .count = sizeof(eppdata)
+    };
+    if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) {
+        /* Controls not correct for EPP data cycle, so do nothing */
+        pdebug("re%04x s\n", eppdata);
+        return eppdata;
+    }
+    err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg);
+    ret = le16_to_cpu(eppdata);
+
+    if (err) {
+        s->epp_timeout = 1;
+        pdebug("re%04x t\n", ret);
+    }
+    else
+        pdebug("re%04x\n", ret);
+    return ret;
+}
+
+static uint32_t
+parallel_ioport_eppdata_read_hw4(void *opaque, uint32_t addr)
+{
+    ParallelState *s = opaque;
+    uint32_t ret;
+    uint32_t eppdata = ~0U;
+    int err;
+    struct ParallelIOArg ioarg = {
+        .buffer = &eppdata, .count = sizeof(eppdata)
+    };
+    if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) {
+        /* Controls not correct for EPP data cycle, so do nothing */
+        pdebug("re%08x s\n", eppdata);
+        return eppdata;
+    }
+    err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg);
+    ret = le32_to_cpu(eppdata);
+
+    if (err) {
+        s->epp_timeout = 1;
+        pdebug("re%08x t\n", ret);
+    }
+    else
+        pdebug("re%08x\n", ret);
+    return ret;
+}
+
+static void parallel_ioport_ecp_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    pdebug("wecp%d=%02x\n", addr & 7, val);
+}
+
+static uint32_t parallel_ioport_ecp_read(void *opaque, uint32_t addr)
+{
+    uint8_t ret = 0xff;
+
+    pdebug("recp%d:%02x\n", addr & 7, ret);
+    return ret;
+}
+
+static void parallel_reset(void *opaque)
+{
+    ParallelState *s = opaque;
+
+    s->datar = ~0;
+    s->dataw = ~0;
+    s->status = PARA_STS_BUSY;
+    s->status |= PARA_STS_ACK;
+    s->status |= PARA_STS_ONLINE;
+    s->status |= PARA_STS_ERROR;
+    s->status |= PARA_STS_TMOUT;
+    s->control = PARA_CTR_SELECT;
+    s->control |= PARA_CTR_INIT;
+    s->control |= 0xc0;
+    s->irq_pending = 0;
+    s->hw_driver = 0;
+    s->epp_timeout = 0;
+    s->last_read_offset = ~0U;
+}
+
+static const int isa_parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
+
+static const MemoryRegionPortio isa_parallel_portio_hw_list[] = {
+    { 0, 8, 1,
+      .read = parallel_ioport_read_hw,
+      .write = parallel_ioport_write_hw },
+    { 4, 1, 2,
+      .read = parallel_ioport_eppdata_read_hw2,
+      .write = parallel_ioport_eppdata_write_hw2 },
+    { 4, 1, 4,
+      .read = parallel_ioport_eppdata_read_hw4,
+      .write = parallel_ioport_eppdata_write_hw4 },
+    { 0x400, 8, 1,
+      .read = parallel_ioport_ecp_read,
+      .write = parallel_ioport_ecp_write },
+    PORTIO_END_OF_LIST(),
+};
+
+static const MemoryRegionPortio isa_parallel_portio_sw_list[] = {
+    { 0, 8, 1,
+      .read = parallel_ioport_read_sw,
+      .write = parallel_ioport_write_sw },
+    PORTIO_END_OF_LIST(),
+};
+
+static int parallel_isa_initfn(ISADevice *dev)
+{
+    static int index;
+    ISAParallelState *isa = DO_UPCAST(ISAParallelState, dev, dev);
+    ParallelState *s = &isa->state;
+    int base;
+    uint8_t dummy;
+
+    if (!s->chr) {
+        fprintf(stderr, "Can't create parallel device, empty char device\n");
+        exit(1);
+    }
+
+    if (isa->index == -1)
+        isa->index = index;
+    if (isa->index >= MAX_PARALLEL_PORTS)
+        return -1;
+    if (isa->iobase == -1)
+        isa->iobase = isa_parallel_io[isa->index];
+    index++;
+
+    base = isa->iobase;
+    isa_init_irq(dev, &s->irq, isa->isairq);
+    qemu_register_reset(parallel_reset, s);
+
+    if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) {
+        s->hw_driver = 1;
+        s->status = dummy;
+    }
+
+    isa_register_portio_list(dev, base,
+                             (s->hw_driver
+                              ? &isa_parallel_portio_hw_list[0]
+                              : &isa_parallel_portio_sw_list[0]),
+                             s, "parallel");
+    return 0;
+}
+
+/* Memory mapped interface */
+static uint32_t parallel_mm_readb (void *opaque, hwaddr addr)
+{
+    ParallelState *s = opaque;
+
+    return parallel_ioport_read_sw(s, addr >> s->it_shift) & 0xFF;
+}
+
+static void parallel_mm_writeb (void *opaque,
+                                hwaddr addr, uint32_t value)
+{
+    ParallelState *s = opaque;
+
+    parallel_ioport_write_sw(s, addr >> s->it_shift, value & 0xFF);
+}
+
+static uint32_t parallel_mm_readw (void *opaque, hwaddr addr)
+{
+    ParallelState *s = opaque;
+
+    return parallel_ioport_read_sw(s, addr >> s->it_shift) & 0xFFFF;
+}
+
+static void parallel_mm_writew (void *opaque,
+                                hwaddr addr, uint32_t value)
+{
+    ParallelState *s = opaque;
+
+    parallel_ioport_write_sw(s, addr >> s->it_shift, value & 0xFFFF);
+}
+
+static uint32_t parallel_mm_readl (void *opaque, hwaddr addr)
+{
+    ParallelState *s = opaque;
+
+    return parallel_ioport_read_sw(s, addr >> s->it_shift);
+}
+
+static void parallel_mm_writel (void *opaque,
+                                hwaddr addr, uint32_t value)
+{
+    ParallelState *s = opaque;
+
+    parallel_ioport_write_sw(s, addr >> s->it_shift, value);
+}
+
+static const MemoryRegionOps parallel_mm_ops = {
+    .old_mmio = {
+        .read = { parallel_mm_readb, parallel_mm_readw, parallel_mm_readl },
+        .write = { parallel_mm_writeb, parallel_mm_writew, parallel_mm_writel },
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/* If fd is zero, it means that the parallel device uses the console */
+bool parallel_mm_init(MemoryRegion *address_space,
+                      hwaddr base, int it_shift, qemu_irq irq,
+                      CharDriverState *chr)
+{
+    ParallelState *s;
+
+    s = g_malloc0(sizeof(ParallelState));
+    s->irq = irq;
+    s->chr = chr;
+    s->it_shift = it_shift;
+    qemu_register_reset(parallel_reset, s);
+
+    memory_region_init_io(&s->iomem, &parallel_mm_ops, s,
+                          "parallel", 8 << it_shift);
+    memory_region_add_subregion(address_space, base, &s->iomem);
+    return true;
+}
+
+static Property parallel_isa_properties[] = {
+    DEFINE_PROP_UINT32("index", ISAParallelState, index,   -1),
+    DEFINE_PROP_HEX32("iobase", ISAParallelState, iobase,  -1),
+    DEFINE_PROP_UINT32("irq",   ISAParallelState, isairq,  7),
+    DEFINE_PROP_CHR("chardev",  ISAParallelState, state.chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void parallel_isa_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+    ic->init = parallel_isa_initfn;
+    dc->props = parallel_isa_properties;
+}
+
+static const TypeInfo parallel_isa_info = {
+    .name          = "isa-parallel",
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(ISAParallelState),
+    .class_init    = parallel_isa_class_initfn,
+};
+
+static void parallel_register_types(void)
+{
+    type_register_static(&parallel_isa_info);
+}
+
+type_init(parallel_register_types)
diff --git a/hw/char/pl011.c b/hw/char/pl011.c
new file mode 100644 (file)
index 0000000..332d5b9
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * Arm PrimeCell PL011 UART
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/sysbus.h"
+#include "char/char.h"
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    uint32_t readbuff;
+    uint32_t flags;
+    uint32_t lcr;
+    uint32_t cr;
+    uint32_t dmacr;
+    uint32_t int_enabled;
+    uint32_t int_level;
+    uint32_t read_fifo[16];
+    uint32_t ilpr;
+    uint32_t ibrd;
+    uint32_t fbrd;
+    uint32_t ifl;
+    int read_pos;
+    int read_count;
+    int read_trigger;
+    CharDriverState *chr;
+    qemu_irq irq;
+    const unsigned char *id;
+} pl011_state;
+
+#define PL011_INT_TX 0x20
+#define PL011_INT_RX 0x10
+
+#define PL011_FLAG_TXFE 0x80
+#define PL011_FLAG_RXFF 0x40
+#define PL011_FLAG_TXFF 0x20
+#define PL011_FLAG_RXFE 0x10
+
+static const unsigned char pl011_id_arm[8] =
+  { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+static const unsigned char pl011_id_luminary[8] =
+  { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl011_update(pl011_state *s)
+{
+    uint32_t flags;
+
+    flags = s->int_level & s->int_enabled;
+    qemu_set_irq(s->irq, flags != 0);
+}
+
+static uint64_t pl011_read(void *opaque, hwaddr offset,
+                           unsigned size)
+{
+    pl011_state *s = (pl011_state *)opaque;
+    uint32_t c;
+
+    if (offset >= 0xfe0 && offset < 0x1000) {
+        return s->id[(offset - 0xfe0) >> 2];
+    }
+    switch (offset >> 2) {
+    case 0: /* UARTDR */
+        s->flags &= ~PL011_FLAG_RXFF;
+        c = s->read_fifo[s->read_pos];
+        if (s->read_count > 0) {
+            s->read_count--;
+            if (++s->read_pos == 16)
+                s->read_pos = 0;
+        }
+        if (s->read_count == 0) {
+            s->flags |= PL011_FLAG_RXFE;
+        }
+        if (s->read_count == s->read_trigger - 1)
+            s->int_level &= ~ PL011_INT_RX;
+        pl011_update(s);
+        if (s->chr) {
+            qemu_chr_accept_input(s->chr);
+        }
+        return c;
+    case 1: /* UARTCR */
+        return 0;
+    case 6: /* UARTFR */
+        return s->flags;
+    case 8: /* UARTILPR */
+        return s->ilpr;
+    case 9: /* UARTIBRD */
+        return s->ibrd;
+    case 10: /* UARTFBRD */
+        return s->fbrd;
+    case 11: /* UARTLCR_H */
+        return s->lcr;
+    case 12: /* UARTCR */
+        return s->cr;
+    case 13: /* UARTIFLS */
+        return s->ifl;
+    case 14: /* UARTIMSC */
+        return s->int_enabled;
+    case 15: /* UARTRIS */
+        return s->int_level;
+    case 16: /* UARTMIS */
+        return s->int_level & s->int_enabled;
+    case 18: /* UARTDMACR */
+        return s->dmacr;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl011_read: Bad offset %x\n", (int)offset);
+        return 0;
+    }
+}
+
+static void pl011_set_read_trigger(pl011_state *s)
+{
+#if 0
+    /* The docs say the RX interrupt is triggered when the FIFO exceeds
+       the threshold.  However linux only reads the FIFO in response to an
+       interrupt.  Triggering the interrupt when the FIFO is non-empty seems
+       to make things work.  */
+    if (s->lcr & 0x10)
+        s->read_trigger = (s->ifl >> 1) & 0x1c;
+    else
+#endif
+        s->read_trigger = 1;
+}
+
+static void pl011_write(void *opaque, hwaddr offset,
+                        uint64_t value, unsigned size)
+{
+    pl011_state *s = (pl011_state *)opaque;
+    unsigned char ch;
+
+    switch (offset >> 2) {
+    case 0: /* UARTDR */
+        /* ??? Check if transmitter is enabled.  */
+        ch = value;
+        if (s->chr)
+            qemu_chr_fe_write(s->chr, &ch, 1);
+        s->int_level |= PL011_INT_TX;
+        pl011_update(s);
+        break;
+    case 1: /* UARTCR */
+        s->cr = value;
+        break;
+    case 6: /* UARTFR */
+        /* Writes to Flag register are ignored.  */
+        break;
+    case 8: /* UARTUARTILPR */
+        s->ilpr = value;
+        break;
+    case 9: /* UARTIBRD */
+        s->ibrd = value;
+        break;
+    case 10: /* UARTFBRD */
+        s->fbrd = value;
+        break;
+    case 11: /* UARTLCR_H */
+        s->lcr = value;
+        pl011_set_read_trigger(s);
+        break;
+    case 12: /* UARTCR */
+        /* ??? Need to implement the enable and loopback bits.  */
+        s->cr = value;
+        break;
+    case 13: /* UARTIFS */
+        s->ifl = value;
+        pl011_set_read_trigger(s);
+        break;
+    case 14: /* UARTIMSC */
+        s->int_enabled = value;
+        pl011_update(s);
+        break;
+    case 17: /* UARTICR */
+        s->int_level &= ~value;
+        pl011_update(s);
+        break;
+    case 18: /* UARTDMACR */
+        s->dmacr = value;
+        if (value & 3) {
+            qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n");
+        }
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl011_write: Bad offset %x\n", (int)offset);
+    }
+}
+
+static int pl011_can_receive(void *opaque)
+{
+    pl011_state *s = (pl011_state *)opaque;
+
+    if (s->lcr & 0x10)
+        return s->read_count < 16;
+    else
+        return s->read_count < 1;
+}
+
+static void pl011_put_fifo(void *opaque, uint32_t value)
+{
+    pl011_state *s = (pl011_state *)opaque;
+    int slot;
+
+    slot = s->read_pos + s->read_count;
+    if (slot >= 16)
+        slot -= 16;
+    s->read_fifo[slot] = value;
+    s->read_count++;
+    s->flags &= ~PL011_FLAG_RXFE;
+    if (s->cr & 0x10 || s->read_count == 16) {
+        s->flags |= PL011_FLAG_RXFF;
+    }
+    if (s->read_count == s->read_trigger) {
+        s->int_level |= PL011_INT_RX;
+        pl011_update(s);
+    }
+}
+
+static void pl011_receive(void *opaque, const uint8_t *buf, int size)
+{
+    pl011_put_fifo(opaque, *buf);
+}
+
+static void pl011_event(void *opaque, int event)
+{
+    if (event == CHR_EVENT_BREAK)
+        pl011_put_fifo(opaque, 0x400);
+}
+
+static const MemoryRegionOps pl011_ops = {
+    .read = pl011_read,
+    .write = pl011_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_pl011 = {
+    .name = "pl011",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32(readbuff, pl011_state),
+        VMSTATE_UINT32(flags, pl011_state),
+        VMSTATE_UINT32(lcr, pl011_state),
+        VMSTATE_UINT32(cr, pl011_state),
+        VMSTATE_UINT32(dmacr, pl011_state),
+        VMSTATE_UINT32(int_enabled, pl011_state),
+        VMSTATE_UINT32(int_level, pl011_state),
+        VMSTATE_UINT32_ARRAY(read_fifo, pl011_state, 16),
+        VMSTATE_UINT32(ilpr, pl011_state),
+        VMSTATE_UINT32(ibrd, pl011_state),
+        VMSTATE_UINT32(fbrd, pl011_state),
+        VMSTATE_UINT32(ifl, pl011_state),
+        VMSTATE_INT32(read_pos, pl011_state),
+        VMSTATE_INT32(read_count, pl011_state),
+        VMSTATE_INT32(read_trigger, pl011_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int pl011_init(SysBusDevice *dev, const unsigned char *id)
+{
+    pl011_state *s = FROM_SYSBUS(pl011_state, dev);
+
+    memory_region_init_io(&s->iomem, &pl011_ops, s, "pl011", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+    s->id = id;
+    s->chr = qemu_char_get_next_serial();
+
+    s->read_trigger = 1;
+    s->ifl = 0x12;
+    s->cr = 0x300;
+    s->flags = 0x90;
+    if (s->chr) {
+        qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive,
+                              pl011_event, s);
+    }
+    vmstate_register(&dev->qdev, -1, &vmstate_pl011, s);
+    return 0;
+}
+
+static int pl011_arm_init(SysBusDevice *dev)
+{
+    return pl011_init(dev, pl011_id_arm);
+}
+
+static int pl011_luminary_init(SysBusDevice *dev)
+{
+    return pl011_init(dev, pl011_id_luminary);
+}
+
+static void pl011_arm_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = pl011_arm_init;
+}
+
+static const TypeInfo pl011_arm_info = {
+    .name          = "pl011",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl011_state),
+    .class_init    = pl011_arm_class_init,
+};
+
+static void pl011_luminary_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = pl011_luminary_init;
+}
+
+static const TypeInfo pl011_luminary_info = {
+    .name          = "pl011_luminary",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl011_state),
+    .class_init    = pl011_luminary_class_init,
+};
+
+static void pl011_register_types(void)
+{
+    type_register_static(&pl011_arm_info);
+    type_register_static(&pl011_luminary_info);
+}
+
+type_init(pl011_register_types)
diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c
new file mode 100644 (file)
index 0000000..ed140d0
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * QEMU 16550A UART emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2008 Citrix Systems, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/char/serial.h"
+#include "hw/isa/isa.h"
+
+typedef struct ISASerialState {
+    ISADevice dev;
+    uint32_t index;
+    uint32_t iobase;
+    uint32_t isairq;
+    SerialState state;
+} ISASerialState;
+
+static const int isa_serial_io[MAX_SERIAL_PORTS] = {
+    0x3f8, 0x2f8, 0x3e8, 0x2e8
+};
+static const int isa_serial_irq[MAX_SERIAL_PORTS] = {
+    4, 3, 4, 3
+};
+
+static int serial_isa_initfn(ISADevice *dev)
+{
+    static int index;
+    ISASerialState *isa = DO_UPCAST(ISASerialState, dev, dev);
+    SerialState *s = &isa->state;
+
+    if (isa->index == -1) {
+        isa->index = index;
+    }
+    if (isa->index >= MAX_SERIAL_PORTS) {
+        return -1;
+    }
+    if (isa->iobase == -1) {
+        isa->iobase = isa_serial_io[isa->index];
+    }
+    if (isa->isairq == -1) {
+        isa->isairq = isa_serial_irq[isa->index];
+    }
+    index++;
+
+    s->baudbase = 115200;
+    isa_init_irq(dev, &s->irq, isa->isairq);
+    serial_init_core(s);
+    qdev_set_legacy_instance_id(&dev->qdev, isa->iobase, 3);
+
+    memory_region_init_io(&s->io, &serial_io_ops, s, "serial", 8);
+    isa_register_ioport(dev, &s->io, isa->iobase);
+    return 0;
+}
+
+static const VMStateDescription vmstate_isa_serial = {
+    .name = "serial",
+    .version_id = 3,
+    .minimum_version_id = 2,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT(state, ISASerialState, 0, vmstate_serial, SerialState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property serial_isa_properties[] = {
+    DEFINE_PROP_UINT32("index",  ISASerialState, index,   -1),
+    DEFINE_PROP_HEX32("iobase",  ISASerialState, iobase,  -1),
+    DEFINE_PROP_UINT32("irq",    ISASerialState, isairq,  -1),
+    DEFINE_PROP_CHR("chardev",   ISASerialState, state.chr),
+    DEFINE_PROP_UINT32("wakeup", ISASerialState, state.wakeup, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void serial_isa_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+    ic->init = serial_isa_initfn;
+    dc->vmsd = &vmstate_isa_serial;
+    dc->props = serial_isa_properties;
+}
+
+static const TypeInfo serial_isa_info = {
+    .name          = "isa-serial",
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(ISASerialState),
+    .class_init    = serial_isa_class_initfn,
+};
+
+static void serial_register_types(void)
+{
+    type_register_static(&serial_isa_info);
+}
+
+type_init(serial_register_types)
+
+bool serial_isa_init(ISABus *bus, int index, CharDriverState *chr)
+{
+    ISADevice *dev;
+
+    dev = isa_try_create(bus, "isa-serial");
+    if (!dev) {
+        return false;
+    }
+    qdev_prop_set_uint32(&dev->qdev, "index", index);
+    qdev_prop_set_chr(&dev->qdev, "chardev", chr);
+    if (qdev_init(&dev->qdev) < 0) {
+        return false;
+    }
+    return true;
+}
diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c
new file mode 100644 (file)
index 0000000..2138e35
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * QEMU 16550A UART emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2008 Citrix Systems, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* see docs/specs/pci-serial.txt */
+
+#include "hw/char/serial.h"
+#include "hw/pci/pci.h"
+
+#define PCI_SERIAL_MAX_PORTS 4
+
+typedef struct PCISerialState {
+    PCIDevice dev;
+    SerialState state;
+} PCISerialState;
+
+typedef struct PCIMultiSerialState {
+    PCIDevice    dev;
+    MemoryRegion iobar;
+    uint32_t     ports;
+    char         *name[PCI_SERIAL_MAX_PORTS];
+    SerialState  state[PCI_SERIAL_MAX_PORTS];
+    uint32_t     level[PCI_SERIAL_MAX_PORTS];
+    qemu_irq     *irqs;
+} PCIMultiSerialState;
+
+static int serial_pci_init(PCIDevice *dev)
+{
+    PCISerialState *pci = DO_UPCAST(PCISerialState, dev, dev);
+    SerialState *s = &pci->state;
+
+    s->baudbase = 115200;
+    serial_init_core(s);
+
+    pci->dev.config[PCI_INTERRUPT_PIN] = 0x01;
+    s->irq = pci->dev.irq[0];
+
+    memory_region_init_io(&s->io, &serial_io_ops, s, "serial", 8);
+    pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io);
+    return 0;
+}
+
+static void multi_serial_irq_mux(void *opaque, int n, int level)
+{
+    PCIMultiSerialState *pci = opaque;
+    int i, pending = 0;
+
+    pci->level[n] = level;
+    for (i = 0; i < pci->ports; i++) {
+        if (pci->level[i]) {
+            pending = 1;
+        }
+    }
+    qemu_set_irq(pci->dev.irq[0], pending);
+}
+
+static int multi_serial_pci_init(PCIDevice *dev)
+{
+    PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
+    PCIMultiSerialState *pci = DO_UPCAST(PCIMultiSerialState, dev, dev);
+    SerialState *s;
+    int i;
+
+    switch (pc->device_id) {
+    case 0x0003:
+        pci->ports = 2;
+        break;
+    case 0x0004:
+        pci->ports = 4;
+        break;
+    }
+    assert(pci->ports > 0);
+    assert(pci->ports <= PCI_SERIAL_MAX_PORTS);
+
+    pci->dev.config[PCI_INTERRUPT_PIN] = 0x01;
+    memory_region_init(&pci->iobar, "multiserial", 8 * pci->ports);
+    pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->iobar);
+    pci->irqs = qemu_allocate_irqs(multi_serial_irq_mux, pci,
+                                   pci->ports);
+
+    for (i = 0; i < pci->ports; i++) {
+        s = pci->state + i;
+        s->baudbase = 115200;
+        serial_init_core(s);
+        s->irq = pci->irqs[i];
+        pci->name[i] = g_strdup_printf("uart #%d", i+1);
+        memory_region_init_io(&s->io, &serial_io_ops, s, pci->name[i], 8);
+        memory_region_add_subregion(&pci->iobar, 8 * i, &s->io);
+    }
+    return 0;
+}
+
+static void serial_pci_exit(PCIDevice *dev)
+{
+    PCISerialState *pci = DO_UPCAST(PCISerialState, dev, dev);
+    SerialState *s = &pci->state;
+
+    serial_exit_core(s);
+    memory_region_destroy(&s->io);
+}
+
+static void multi_serial_pci_exit(PCIDevice *dev)
+{
+    PCIMultiSerialState *pci = DO_UPCAST(PCIMultiSerialState, dev, dev);
+    SerialState *s;
+    int i;
+
+    for (i = 0; i < pci->ports; i++) {
+        s = pci->state + i;
+        serial_exit_core(s);
+        memory_region_destroy(&s->io);
+        g_free(pci->name[i]);
+    }
+    memory_region_destroy(&pci->iobar);
+    qemu_free_irqs(pci->irqs);
+}
+
+static const VMStateDescription vmstate_pci_serial = {
+    .name = "pci-serial",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(dev, PCISerialState),
+        VMSTATE_STRUCT(state, PCISerialState, 0, vmstate_serial, SerialState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_pci_multi_serial = {
+    .name = "pci-serial-multi",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(dev, PCIMultiSerialState),
+        VMSTATE_STRUCT_ARRAY(state, PCIMultiSerialState, PCI_SERIAL_MAX_PORTS,
+                             0, vmstate_serial, SerialState),
+        VMSTATE_UINT32_ARRAY(level, PCIMultiSerialState, PCI_SERIAL_MAX_PORTS),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property serial_pci_properties[] = {
+    DEFINE_PROP_CHR("chardev",  PCISerialState, state.chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static Property multi_2x_serial_pci_properties[] = {
+    DEFINE_PROP_CHR("chardev1",  PCIMultiSerialState, state[0].chr),
+    DEFINE_PROP_CHR("chardev2",  PCIMultiSerialState, state[1].chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static Property multi_4x_serial_pci_properties[] = {
+    DEFINE_PROP_CHR("chardev1",  PCIMultiSerialState, state[0].chr),
+    DEFINE_PROP_CHR("chardev2",  PCIMultiSerialState, state[1].chr),
+    DEFINE_PROP_CHR("chardev3",  PCIMultiSerialState, state[2].chr),
+    DEFINE_PROP_CHR("chardev4",  PCIMultiSerialState, state[3].chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void serial_pci_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
+    pc->init = serial_pci_init;
+    pc->exit = serial_pci_exit;
+    pc->vendor_id = PCI_VENDOR_ID_REDHAT;
+    pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL;
+    pc->revision = 1;
+    pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL;
+    dc->vmsd = &vmstate_pci_serial;
+    dc->props = serial_pci_properties;
+}
+
+static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
+    pc->init = multi_serial_pci_init;
+    pc->exit = multi_serial_pci_exit;
+    pc->vendor_id = PCI_VENDOR_ID_REDHAT;
+    pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL2;
+    pc->revision = 1;
+    pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL;
+    dc->vmsd = &vmstate_pci_multi_serial;
+    dc->props = multi_2x_serial_pci_properties;
+}
+
+static void multi_4x_serial_pci_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
+    pc->init = multi_serial_pci_init;
+    pc->exit = multi_serial_pci_exit;
+    pc->vendor_id = PCI_VENDOR_ID_REDHAT;
+    pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL4;
+    pc->revision = 1;
+    pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL;
+    dc->vmsd = &vmstate_pci_multi_serial;
+    dc->props = multi_4x_serial_pci_properties;
+}
+
+static const TypeInfo serial_pci_info = {
+    .name          = "pci-serial",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCISerialState),
+    .class_init    = serial_pci_class_initfn,
+};
+
+static const TypeInfo multi_2x_serial_pci_info = {
+    .name          = "pci-serial-2x",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIMultiSerialState),
+    .class_init    = multi_2x_serial_pci_class_initfn,
+};
+
+static const TypeInfo multi_4x_serial_pci_info = {
+    .name          = "pci-serial-4x",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIMultiSerialState),
+    .class_init    = multi_4x_serial_pci_class_initfn,
+};
+
+static void serial_pci_register_types(void)
+{
+    type_register_static(&serial_pci_info);
+    type_register_static(&multi_2x_serial_pci_info);
+    type_register_static(&multi_4x_serial_pci_info);
+}
+
+type_init(serial_pci_register_types)
diff --git a/hw/char/serial.c b/hw/char/serial.c
new file mode 100644 (file)
index 0000000..1151bf1
--- /dev/null
@@ -0,0 +1,789 @@
+/*
+ * QEMU 16550A UART emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2008 Citrix Systems, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/char/serial.h"
+#include "char/char.h"
+#include "qemu/timer.h"
+#include "exec/address-spaces.h"
+
+//#define DEBUG_SERIAL
+
+#define UART_LCR_DLAB  0x80    /* Divisor latch access bit */
+
+#define UART_IER_MSI   0x08    /* Enable Modem status interrupt */
+#define UART_IER_RLSI  0x04    /* Enable receiver line status interrupt */
+#define UART_IER_THRI  0x02    /* Enable Transmitter holding register int. */
+#define UART_IER_RDI   0x01    /* Enable receiver data interrupt */
+
+#define UART_IIR_NO_INT        0x01    /* No interrupts pending */
+#define UART_IIR_ID    0x06    /* Mask for the interrupt ID */
+
+#define UART_IIR_MSI   0x00    /* Modem status interrupt */
+#define UART_IIR_THRI  0x02    /* Transmitter holding register empty */
+#define UART_IIR_RDI   0x04    /* Receiver data interrupt */
+#define UART_IIR_RLSI  0x06    /* Receiver line status interrupt */
+#define UART_IIR_CTI    0x0C    /* Character Timeout Indication */
+
+#define UART_IIR_FENF   0x80    /* Fifo enabled, but not functionning */
+#define UART_IIR_FE     0xC0    /* Fifo enabled */
+
+/*
+ * These are the definitions for the Modem Control Register
+ */
+#define UART_MCR_LOOP  0x10    /* Enable loopback test mode */
+#define UART_MCR_OUT2  0x08    /* Out2 complement */
+#define UART_MCR_OUT1  0x04    /* Out1 complement */
+#define UART_MCR_RTS   0x02    /* RTS complement */
+#define UART_MCR_DTR   0x01    /* DTR complement */
+
+/*
+ * These are the definitions for the Modem Status Register
+ */
+#define UART_MSR_DCD   0x80    /* Data Carrier Detect */
+#define UART_MSR_RI    0x40    /* Ring Indicator */
+#define UART_MSR_DSR   0x20    /* Data Set Ready */
+#define UART_MSR_CTS   0x10    /* Clear to Send */
+#define UART_MSR_DDCD  0x08    /* Delta DCD */
+#define UART_MSR_TERI  0x04    /* Trailing edge ring indicator */
+#define UART_MSR_DDSR  0x02    /* Delta DSR */
+#define UART_MSR_DCTS  0x01    /* Delta CTS */
+#define UART_MSR_ANY_DELTA 0x0F        /* Any of the delta bits! */
+
+#define UART_LSR_TEMT  0x40    /* Transmitter empty */
+#define UART_LSR_THRE  0x20    /* Transmit-hold-register empty */
+#define UART_LSR_BI    0x10    /* Break interrupt indicator */
+#define UART_LSR_FE    0x08    /* Frame error indicator */
+#define UART_LSR_PE    0x04    /* Parity error indicator */
+#define UART_LSR_OE    0x02    /* Overrun error indicator */
+#define UART_LSR_DR    0x01    /* Receiver data ready */
+#define UART_LSR_INT_ANY 0x1E  /* Any of the lsr-interrupt-triggering status bits */
+
+/* Interrupt trigger levels. The byte-counts are for 16550A - in newer UARTs the byte-count for each ITL is higher. */
+
+#define UART_FCR_ITL_1      0x00 /* 1 byte ITL */
+#define UART_FCR_ITL_2      0x40 /* 4 bytes ITL */
+#define UART_FCR_ITL_3      0x80 /* 8 bytes ITL */
+#define UART_FCR_ITL_4      0xC0 /* 14 bytes ITL */
+
+#define UART_FCR_DMS        0x08    /* DMA Mode Select */
+#define UART_FCR_XFR        0x04    /* XMIT Fifo Reset */
+#define UART_FCR_RFR        0x02    /* RCVR Fifo Reset */
+#define UART_FCR_FE         0x01    /* FIFO Enable */
+
+#define XMIT_FIFO           0
+#define RECV_FIFO           1
+#define MAX_XMIT_RETRY      4
+
+#ifdef DEBUG_SERIAL
+#define DPRINTF(fmt, ...) \
+do { fprintf(stderr, "serial: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+do {} while (0)
+#endif
+
+static void serial_receive1(void *opaque, const uint8_t *buf, int size);
+
+static void fifo_clear(SerialState *s, int fifo)
+{
+    SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo;
+    memset(f->data, 0, UART_FIFO_LENGTH);
+    f->count = 0;
+    f->head = 0;
+    f->tail = 0;
+}
+
+static int fifo_put(SerialState *s, int fifo, uint8_t chr)
+{
+    SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo;
+
+    /* Receive overruns do not overwrite FIFO contents. */
+    if (fifo == XMIT_FIFO || f->count < UART_FIFO_LENGTH) {
+
+        f->data[f->head++] = chr;
+
+        if (f->head == UART_FIFO_LENGTH)
+            f->head = 0;
+    }
+
+    if (f->count < UART_FIFO_LENGTH)
+        f->count++;
+    else if (fifo == RECV_FIFO)
+        s->lsr |= UART_LSR_OE;
+
+    return 1;
+}
+
+static uint8_t fifo_get(SerialState *s, int fifo)
+{
+    SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo;
+    uint8_t c;
+
+    if(f->count == 0)
+        return 0;
+
+    c = f->data[f->tail++];
+    if (f->tail == UART_FIFO_LENGTH)
+        f->tail = 0;
+    f->count--;
+
+    return c;
+}
+
+static void serial_update_irq(SerialState *s)
+{
+    uint8_t tmp_iir = UART_IIR_NO_INT;
+
+    if ((s->ier & UART_IER_RLSI) && (s->lsr & UART_LSR_INT_ANY)) {
+        tmp_iir = UART_IIR_RLSI;
+    } else if ((s->ier & UART_IER_RDI) && s->timeout_ipending) {
+        /* Note that(s->ier & UART_IER_RDI) can mask this interrupt,
+         * this is not in the specification but is observed on existing
+         * hardware.  */
+        tmp_iir = UART_IIR_CTI;
+    } else if ((s->ier & UART_IER_RDI) && (s->lsr & UART_LSR_DR) &&
+               (!(s->fcr & UART_FCR_FE) ||
+                s->recv_fifo.count >= s->recv_fifo.itl)) {
+        tmp_iir = UART_IIR_RDI;
+    } else if ((s->ier & UART_IER_THRI) && s->thr_ipending) {
+        tmp_iir = UART_IIR_THRI;
+    } else if ((s->ier & UART_IER_MSI) && (s->msr & UART_MSR_ANY_DELTA)) {
+        tmp_iir = UART_IIR_MSI;
+    }
+
+    s->iir = tmp_iir | (s->iir & 0xF0);
+
+    if (tmp_iir != UART_IIR_NO_INT) {
+        qemu_irq_raise(s->irq);
+    } else {
+        qemu_irq_lower(s->irq);
+    }
+}
+
+static void serial_update_parameters(SerialState *s)
+{
+    int speed, parity, data_bits, stop_bits, frame_size;
+    QEMUSerialSetParams ssp;
+
+    if (s->divider == 0)
+        return;
+
+    /* Start bit. */
+    frame_size = 1;
+    if (s->lcr & 0x08) {
+        /* Parity bit. */
+        frame_size++;
+        if (s->lcr & 0x10)
+            parity = 'E';
+        else
+            parity = 'O';
+    } else {
+            parity = 'N';
+    }
+    if (s->lcr & 0x04)
+        stop_bits = 2;
+    else
+        stop_bits = 1;
+
+    data_bits = (s->lcr & 0x03) + 5;
+    frame_size += data_bits + stop_bits;
+    speed = s->baudbase / s->divider;
+    ssp.speed = speed;
+    ssp.parity = parity;
+    ssp.data_bits = data_bits;
+    ssp.stop_bits = stop_bits;
+    s->char_transmit_time =  (get_ticks_per_sec() / speed) * frame_size;
+    qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+
+    DPRINTF("speed=%d parity=%c data=%d stop=%d\n",
+           speed, parity, data_bits, stop_bits);
+}
+
+static void serial_update_msl(SerialState *s)
+{
+    uint8_t omsr;
+    int flags;
+
+    qemu_del_timer(s->modem_status_poll);
+
+    if (qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) {
+        s->poll_msl = -1;
+        return;
+    }
+
+    omsr = s->msr;
+
+    s->msr = (flags & CHR_TIOCM_CTS) ? s->msr | UART_MSR_CTS : s->msr & ~UART_MSR_CTS;
+    s->msr = (flags & CHR_TIOCM_DSR) ? s->msr | UART_MSR_DSR : s->msr & ~UART_MSR_DSR;
+    s->msr = (flags & CHR_TIOCM_CAR) ? s->msr | UART_MSR_DCD : s->msr & ~UART_MSR_DCD;
+    s->msr = (flags & CHR_TIOCM_RI) ? s->msr | UART_MSR_RI : s->msr & ~UART_MSR_RI;
+
+    if (s->msr != omsr) {
+         /* Set delta bits */
+         s->msr = s->msr | ((s->msr >> 4) ^ (omsr >> 4));
+         /* UART_MSR_TERI only if change was from 1 -> 0 */
+         if ((s->msr & UART_MSR_TERI) && !(omsr & UART_MSR_RI))
+             s->msr &= ~UART_MSR_TERI;
+         serial_update_irq(s);
+    }
+
+    /* The real 16550A apparently has a 250ns response latency to line status changes.
+       We'll be lazy and poll only every 10ms, and only poll it at all if MSI interrupts are turned on */
+
+    if (s->poll_msl)
+        qemu_mod_timer(s->modem_status_poll, qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 100);
+}
+
+static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
+{
+    SerialState *s = opaque;
+
+    if (s->tsr_retry <= 0) {
+        if (s->fcr & UART_FCR_FE) {
+            s->tsr = fifo_get(s,XMIT_FIFO);
+            if (!s->xmit_fifo.count)
+                s->lsr |= UART_LSR_THRE;
+        } else if ((s->lsr & UART_LSR_THRE)) {
+            return FALSE;
+        } else {
+            s->tsr = s->thr;
+            s->lsr |= UART_LSR_THRE;
+            s->lsr &= ~UART_LSR_TEMT;
+        }
+    }
+
+    if (s->mcr & UART_MCR_LOOP) {
+        /* in loopback mode, say that we just received a char */
+        serial_receive1(s, &s->tsr, 1);
+    } else if (qemu_chr_fe_write(s->chr, &s->tsr, 1) != 1) {
+        if (s->tsr_retry >= 0 && s->tsr_retry < MAX_XMIT_RETRY &&
+            qemu_chr_fe_add_watch(s->chr, G_IO_OUT, serial_xmit, s) > 0) {
+            s->tsr_retry++;
+            return FALSE;
+        }
+        s->tsr_retry = 0;
+    } else {
+        s->tsr_retry = 0;
+    }
+
+    s->last_xmit_ts = qemu_get_clock_ns(vm_clock);
+
+    if (s->lsr & UART_LSR_THRE) {
+        s->lsr |= UART_LSR_TEMT;
+        s->thr_ipending = 1;
+        serial_update_irq(s);
+    }
+
+    return FALSE;
+}
+
+
+static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
+                                unsigned size)
+{
+    SerialState *s = opaque;
+
+    addr &= 7;
+    DPRINTF("write addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 "\n", addr, val);
+    switch(addr) {
+    default:
+    case 0:
+        if (s->lcr & UART_LCR_DLAB) {
+            s->divider = (s->divider & 0xff00) | val;
+            serial_update_parameters(s);
+        } else {
+            s->thr = (uint8_t) val;
+            if(s->fcr & UART_FCR_FE) {
+                fifo_put(s, XMIT_FIFO, s->thr);
+                s->thr_ipending = 0;
+                s->lsr &= ~UART_LSR_TEMT;
+                s->lsr &= ~UART_LSR_THRE;
+                serial_update_irq(s);
+            } else {
+                s->thr_ipending = 0;
+                s->lsr &= ~UART_LSR_THRE;
+                serial_update_irq(s);
+            }
+            serial_xmit(NULL, G_IO_OUT, s);
+        }
+        break;
+    case 1:
+        if (s->lcr & UART_LCR_DLAB) {
+            s->divider = (s->divider & 0x00ff) | (val << 8);
+            serial_update_parameters(s);
+        } else {
+            s->ier = val & 0x0f;
+            /* If the backend device is a real serial port, turn polling of the modem
+               status lines on physical port on or off depending on UART_IER_MSI state */
+            if (s->poll_msl >= 0) {
+                if (s->ier & UART_IER_MSI) {
+                     s->poll_msl = 1;
+                     serial_update_msl(s);
+                } else {
+                     qemu_del_timer(s->modem_status_poll);
+                     s->poll_msl = 0;
+                }
+            }
+            if (s->lsr & UART_LSR_THRE) {
+                s->thr_ipending = 1;
+                serial_update_irq(s);
+            }
+        }
+        break;
+    case 2:
+        val = val & 0xFF;
+
+        if (s->fcr == val)
+            break;
+
+        /* Did the enable/disable flag change? If so, make sure FIFOs get flushed */
+        if ((val ^ s->fcr) & UART_FCR_FE)
+            val |= UART_FCR_XFR | UART_FCR_RFR;
+
+        /* FIFO clear */
+
+        if (val & UART_FCR_RFR) {
+            qemu_del_timer(s->fifo_timeout_timer);
+            s->timeout_ipending=0;
+            fifo_clear(s,RECV_FIFO);
+        }
+
+        if (val & UART_FCR_XFR) {
+            fifo_clear(s,XMIT_FIFO);
+        }
+
+        if (val & UART_FCR_FE) {
+            s->iir |= UART_IIR_FE;
+            /* Set RECV_FIFO trigger Level */
+            switch (val & 0xC0) {
+            case UART_FCR_ITL_1:
+                s->recv_fifo.itl = 1;
+                break;
+            case UART_FCR_ITL_2:
+                s->recv_fifo.itl = 4;
+                break;
+            case UART_FCR_ITL_3:
+                s->recv_fifo.itl = 8;
+                break;
+            case UART_FCR_ITL_4:
+                s->recv_fifo.itl = 14;
+                break;
+            }
+        } else
+            s->iir &= ~UART_IIR_FE;
+
+        /* Set fcr - or at least the bits in it that are supposed to "stick" */
+        s->fcr = val & 0xC9;
+        serial_update_irq(s);
+        break;
+    case 3:
+        {
+            int break_enable;
+            s->lcr = val;
+            serial_update_parameters(s);
+            break_enable = (val >> 6) & 1;
+            if (break_enable != s->last_break_enable) {
+                s->last_break_enable = break_enable;
+                qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
+                               &break_enable);
+            }
+        }
+        break;
+    case 4:
+        {
+            int flags;
+            int old_mcr = s->mcr;
+            s->mcr = val & 0x1f;
+            if (val & UART_MCR_LOOP)
+                break;
+
+            if (s->poll_msl >= 0 && old_mcr != s->mcr) {
+
+                qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
+
+                flags &= ~(CHR_TIOCM_RTS | CHR_TIOCM_DTR);
+
+                if (val & UART_MCR_RTS)
+                    flags |= CHR_TIOCM_RTS;
+                if (val & UART_MCR_DTR)
+                    flags |= CHR_TIOCM_DTR;
+
+                qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_SET_TIOCM, &flags);
+                /* Update the modem status after a one-character-send wait-time, since there may be a response
+                   from the device/computer at the other end of the serial line */
+                qemu_mod_timer(s->modem_status_poll, qemu_get_clock_ns(vm_clock) + s->char_transmit_time);
+            }
+        }
+        break;
+    case 5:
+        break;
+    case 6:
+        break;
+    case 7:
+        s->scr = val;
+        break;
+    }
+}
+
+static uint64_t serial_ioport_read(void *opaque, hwaddr addr, unsigned size)
+{
+    SerialState *s = opaque;
+    uint32_t ret;
+
+    addr &= 7;
+    switch(addr) {
+    default:
+    case 0:
+        if (s->lcr & UART_LCR_DLAB) {
+            ret = s->divider & 0xff;
+        } else {
+            if(s->fcr & UART_FCR_FE) {
+                ret = fifo_get(s,RECV_FIFO);
+                if (s->recv_fifo.count == 0)
+                    s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
+                else
+                    qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock_ns (vm_clock) + s->char_transmit_time * 4);
+                s->timeout_ipending = 0;
+            } else {
+                ret = s->rbr;
+                s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
+            }
+            serial_update_irq(s);
+            if (!(s->mcr & UART_MCR_LOOP)) {
+                /* in loopback mode, don't receive any data */
+                qemu_chr_accept_input(s->chr);
+            }
+        }
+        break;
+    case 1:
+        if (s->lcr & UART_LCR_DLAB) {
+            ret = (s->divider >> 8) & 0xff;
+        } else {
+            ret = s->ier;
+        }
+        break;
+    case 2:
+        ret = s->iir;
+        if ((ret & UART_IIR_ID) == UART_IIR_THRI) {
+            s->thr_ipending = 0;
+            serial_update_irq(s);
+        }
+        break;
+    case 3:
+        ret = s->lcr;
+        break;
+    case 4:
+        ret = s->mcr;
+        break;
+    case 5:
+        ret = s->lsr;
+        /* Clear break and overrun interrupts */
+        if (s->lsr & (UART_LSR_BI|UART_LSR_OE)) {
+            s->lsr &= ~(UART_LSR_BI|UART_LSR_OE);
+            serial_update_irq(s);
+        }
+        break;
+    case 6:
+        if (s->mcr & UART_MCR_LOOP) {
+            /* in loopback, the modem output pins are connected to the
+               inputs */
+            ret = (s->mcr & 0x0c) << 4;
+            ret |= (s->mcr & 0x02) << 3;
+            ret |= (s->mcr & 0x01) << 5;
+        } else {
+            if (s->poll_msl >= 0)
+                serial_update_msl(s);
+            ret = s->msr;
+            /* Clear delta bits & msr int after read, if they were set */
+            if (s->msr & UART_MSR_ANY_DELTA) {
+                s->msr &= 0xF0;
+                serial_update_irq(s);
+            }
+        }
+        break;
+    case 7:
+        ret = s->scr;
+        break;
+    }
+    DPRINTF("read addr=0x%" HWADDR_PRIx " val=0x%02x\n", addr, ret);
+    return ret;
+}
+
+static int serial_can_receive(SerialState *s)
+{
+    if(s->fcr & UART_FCR_FE) {
+        if(s->recv_fifo.count < UART_FIFO_LENGTH)
+        /* Advertise (fifo.itl - fifo.count) bytes when count < ITL, and 1 if above. If UART_FIFO_LENGTH - fifo.count is
+        advertised the effect will be to almost always fill the fifo completely before the guest has a chance to respond,
+        effectively overriding the ITL that the guest has set. */
+             return (s->recv_fifo.count <= s->recv_fifo.itl) ? s->recv_fifo.itl - s->recv_fifo.count : 1;
+        else
+             return 0;
+    } else {
+    return !(s->lsr & UART_LSR_DR);
+    }
+}
+
+static void serial_receive_break(SerialState *s)
+{
+    s->rbr = 0;
+    /* When the LSR_DR is set a null byte is pushed into the fifo */
+    fifo_put(s, RECV_FIFO, '\0');
+    s->lsr |= UART_LSR_BI | UART_LSR_DR;
+    serial_update_irq(s);
+}
+
+/* There's data in recv_fifo and s->rbr has not been read for 4 char transmit times */
+static void fifo_timeout_int (void *opaque) {
+    SerialState *s = opaque;
+    if (s->recv_fifo.count) {
+        s->timeout_ipending = 1;
+        serial_update_irq(s);
+    }
+}
+
+static int serial_can_receive1(void *opaque)
+{
+    SerialState *s = opaque;
+    return serial_can_receive(s);
+}
+
+static void serial_receive1(void *opaque, const uint8_t *buf, int size)
+{
+    SerialState *s = opaque;
+
+    if (s->wakeup) {
+        qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
+    }
+    if(s->fcr & UART_FCR_FE) {
+        int i;
+        for (i = 0; i < size; i++) {
+            fifo_put(s, RECV_FIFO, buf[i]);
+        }
+        s->lsr |= UART_LSR_DR;
+        /* call the timeout receive callback in 4 char transmit time */
+        qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock_ns (vm_clock) + s->char_transmit_time * 4);
+    } else {
+        if (s->lsr & UART_LSR_DR)
+            s->lsr |= UART_LSR_OE;
+        s->rbr = buf[0];
+        s->lsr |= UART_LSR_DR;
+    }
+    serial_update_irq(s);
+}
+
+static void serial_event(void *opaque, int event)
+{
+    SerialState *s = opaque;
+    DPRINTF("event %x\n", event);
+    if (event == CHR_EVENT_BREAK)
+        serial_receive_break(s);
+}
+
+static void serial_pre_save(void *opaque)
+{
+    SerialState *s = opaque;
+    s->fcr_vmstate = s->fcr;
+}
+
+static int serial_post_load(void *opaque, int version_id)
+{
+    SerialState *s = opaque;
+
+    if (version_id < 3) {
+        s->fcr_vmstate = 0;
+    }
+    /* Initialize fcr via setter to perform essential side-effects */
+    serial_ioport_write(s, 0x02, s->fcr_vmstate, 1);
+    serial_update_parameters(s);
+    return 0;
+}
+
+const VMStateDescription vmstate_serial = {
+    .name = "serial",
+    .version_id = 3,
+    .minimum_version_id = 2,
+    .pre_save = serial_pre_save,
+    .post_load = serial_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT16_V(divider, SerialState, 2),
+        VMSTATE_UINT8(rbr, SerialState),
+        VMSTATE_UINT8(ier, SerialState),
+        VMSTATE_UINT8(iir, SerialState),
+        VMSTATE_UINT8(lcr, SerialState),
+        VMSTATE_UINT8(mcr, SerialState),
+        VMSTATE_UINT8(lsr, SerialState),
+        VMSTATE_UINT8(msr, SerialState),
+        VMSTATE_UINT8(scr, SerialState),
+        VMSTATE_UINT8_V(fcr_vmstate, SerialState, 3),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void serial_reset(void *opaque)
+{
+    SerialState *s = opaque;
+
+    s->rbr = 0;
+    s->ier = 0;
+    s->iir = UART_IIR_NO_INT;
+    s->lcr = 0;
+    s->lsr = UART_LSR_TEMT | UART_LSR_THRE;
+    s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
+    /* Default to 9600 baud, 1 start bit, 8 data bits, 1 stop bit, no parity. */
+    s->divider = 0x0C;
+    s->mcr = UART_MCR_OUT2;
+    s->scr = 0;
+    s->tsr_retry = 0;
+    s->char_transmit_time = (get_ticks_per_sec() / 9600) * 10;
+    s->poll_msl = 0;
+
+    fifo_clear(s,RECV_FIFO);
+    fifo_clear(s,XMIT_FIFO);
+
+    s->last_xmit_ts = qemu_get_clock_ns(vm_clock);
+
+    s->thr_ipending = 0;
+    s->last_break_enable = 0;
+    qemu_irq_lower(s->irq);
+}
+
+void serial_init_core(SerialState *s)
+{
+    if (!s->chr) {
+        fprintf(stderr, "Can't create serial device, empty char device\n");
+       exit(1);
+    }
+
+    s->modem_status_poll = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) serial_update_msl, s);
+
+    s->fifo_timeout_timer = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) fifo_timeout_int, s);
+    qemu_register_reset(serial_reset, s);
+
+    qemu_chr_add_handlers(s->chr, serial_can_receive1, serial_receive1,
+                          serial_event, s);
+}
+
+void serial_exit_core(SerialState *s)
+{
+    qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL);
+    qemu_unregister_reset(serial_reset, s);
+}
+
+/* Change the main reference oscillator frequency. */
+void serial_set_frequency(SerialState *s, uint32_t frequency)
+{
+    s->baudbase = frequency;
+    serial_update_parameters(s);
+}
+
+const MemoryRegionOps serial_io_ops = {
+    .read = serial_ioport_read,
+    .write = serial_ioport_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+SerialState *serial_init(int base, qemu_irq irq, int baudbase,
+                         CharDriverState *chr, MemoryRegion *system_io)
+{
+    SerialState *s;
+
+    s = g_malloc0(sizeof(SerialState));
+
+    s->irq = irq;
+    s->baudbase = baudbase;
+    s->chr = chr;
+    serial_init_core(s);
+
+    vmstate_register(NULL, base, &vmstate_serial, s);
+
+    memory_region_init_io(&s->io, &serial_io_ops, s, "serial", 8);
+    memory_region_add_subregion(system_io, base, &s->io);
+
+    return s;
+}
+
+/* Memory mapped interface */
+static uint64_t serial_mm_read(void *opaque, hwaddr addr,
+                               unsigned size)
+{
+    SerialState *s = opaque;
+    return serial_ioport_read(s, addr >> s->it_shift, 1);
+}
+
+static void serial_mm_write(void *opaque, hwaddr addr,
+                            uint64_t value, unsigned size)
+{
+    SerialState *s = opaque;
+    value &= ~0u >> (32 - (size * 8));
+    serial_ioport_write(s, addr >> s->it_shift, value, 1);
+}
+
+static const MemoryRegionOps serial_mm_ops[3] = {
+    [DEVICE_NATIVE_ENDIAN] = {
+        .read = serial_mm_read,
+        .write = serial_mm_write,
+        .endianness = DEVICE_NATIVE_ENDIAN,
+    },
+    [DEVICE_LITTLE_ENDIAN] = {
+        .read = serial_mm_read,
+        .write = serial_mm_write,
+        .endianness = DEVICE_LITTLE_ENDIAN,
+    },
+    [DEVICE_BIG_ENDIAN] = {
+        .read = serial_mm_read,
+        .write = serial_mm_write,
+        .endianness = DEVICE_BIG_ENDIAN,
+    },
+};
+
+SerialState *serial_mm_init(MemoryRegion *address_space,
+                            hwaddr base, int it_shift,
+                            qemu_irq irq, int baudbase,
+                            CharDriverState *chr, enum device_endian end)
+{
+    SerialState *s;
+
+    s = g_malloc0(sizeof(SerialState));
+
+    s->it_shift = it_shift;
+    s->irq = irq;
+    s->baudbase = baudbase;
+    s->chr = chr;
+
+    serial_init_core(s);
+    vmstate_register(NULL, base, &vmstate_serial, s);
+
+    memory_region_init_io(&s->io, &serial_mm_ops[end], s,
+                          "serial", 8 << it_shift);
+    memory_region_add_subregion(address_space, base, &s->io);
+
+    serial_update_msl(s);
+    return s;
+}
diff --git a/hw/char/tpci200.c b/hw/char/tpci200.c
new file mode 100644 (file)
index 0000000..e3408ef
--- /dev/null
@@ -0,0 +1,671 @@
+/*
+ * QEMU TEWS TPCI200 IndustryPack carrier emulation
+ *
+ * Copyright (C) 2012 Igalia, S.L.
+ * Author: Alberto Garcia <agarcia@igalia.com>
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any
+ * later version.
+ */
+
+#include "hw/ipack.h"
+#include "hw/pci/pci.h"
+#include "qemu/bitops.h"
+#include <stdio.h>
+
+/* #define DEBUG_TPCI */
+
+#ifdef DEBUG_TPCI
+#define DPRINTF(fmt, ...) \
+    do { fprintf(stderr, "TPCI200: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+#define N_MODULES 4
+
+#define IP_ID_SPACE  2
+#define IP_INT_SPACE 3
+#define IP_IO_SPACE_ADDR_MASK  0x7F
+#define IP_ID_SPACE_ADDR_MASK  0x3F
+#define IP_INT_SPACE_ADDR_MASK 0x3F
+
+#define STATUS_INT(IP, INTNO) BIT((IP) * 2 + (INTNO))
+#define STATUS_TIME(IP)       BIT((IP) + 12)
+#define STATUS_ERR_ANY        0xF00
+
+#define CTRL_CLKRATE          BIT(0)
+#define CTRL_RECOVER          BIT(1)
+#define CTRL_TIME_INT         BIT(2)
+#define CTRL_ERR_INT          BIT(3)
+#define CTRL_INT_EDGE(INTNO)  BIT(4 + (INTNO))
+#define CTRL_INT(INTNO)       BIT(6 + (INTNO))
+
+#define REG_REV_ID    0x00
+#define REG_IP_A_CTRL 0x02
+#define REG_IP_B_CTRL 0x04
+#define REG_IP_C_CTRL 0x06
+#define REG_IP_D_CTRL 0x08
+#define REG_RESET     0x0A
+#define REG_STATUS    0x0C
+#define IP_N_FROM_REG(REG) ((REG) / 2 - 1)
+
+typedef struct {
+    PCIDevice dev;
+    IPackBus bus;
+    MemoryRegion mmio;
+    MemoryRegion io;
+    MemoryRegion las0;
+    MemoryRegion las1;
+    MemoryRegion las2;
+    MemoryRegion las3;
+    bool big_endian[3];
+    uint8_t ctrl[N_MODULES];
+    uint16_t status;
+    uint8_t int_set;
+} TPCI200State;
+
+#define TYPE_TPCI200 "tpci200"
+
+#define TPCI200(obj) \
+    OBJECT_CHECK(TPCI200State, (obj), TYPE_TPCI200)
+
+static const uint8_t local_config_regs[] = {
+    0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xFC, 0xFF, 0x0F, 0x00, 0x00, 0x00,
+    0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+    0x00, 0x08, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x01,
+    0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x60, 0x41, 0xD4,
+    0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x01,
+    0x14, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x08, 0x01, 0x02,
+    0x00, 0x04, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x80, 0x02, 0x41,
+    0x00, 0x00, 0x00, 0x00, 0x40, 0x7A, 0x00, 0x52, 0x92, 0x24, 0x02
+};
+
+static void adjust_addr(bool big_endian, hwaddr *addr, unsigned size)
+{
+    /* During 8 bit access in big endian mode,
+       odd and even addresses are swapped */
+    if (big_endian && size == 1) {
+        *addr ^= 1;
+    }
+}
+
+static uint64_t adjust_value(bool big_endian, uint64_t *val, unsigned size)
+{
+    /* Local spaces only support 8/16 bit access,
+     * so there's no need to care for sizes > 2 */
+    if (big_endian && size == 2) {
+        *val = bswap16(*val);
+    }
+    return *val;
+}
+
+static void tpci200_set_irq(void *opaque, int intno, int level)
+{
+    IPackDevice *ip = opaque;
+    IPackBus *bus = IPACK_BUS(qdev_get_parent_bus(DEVICE(ip)));
+    PCIDevice *pcidev = PCI_DEVICE(BUS(bus)->parent);
+    TPCI200State *dev = TPCI200(pcidev);
+    unsigned ip_n = ip->slot;
+    uint16_t prev_status = dev->status;
+
+    assert(ip->slot >= 0 && ip->slot < N_MODULES);
+
+    /* The requested interrupt must be enabled in the IP CONTROL
+     * register */
+    if (!(dev->ctrl[ip_n] & CTRL_INT(intno))) {
+        return;
+    }
+
+    /* Update the interrupt status in the IP STATUS register */
+    if (level) {
+        dev->status |=  STATUS_INT(ip_n, intno);
+    } else {
+        dev->status &= ~STATUS_INT(ip_n, intno);
+    }
+
+    /* Return if there are no changes */
+    if (dev->status == prev_status) {
+        return;
+    }
+
+    DPRINTF("IP %u INT%u#: %u\n", ip_n, intno, level);
+
+    /* Check if the interrupt is edge sensitive */
+    if (dev->ctrl[ip_n] & CTRL_INT_EDGE(intno)) {
+        if (level) {
+            qemu_set_irq(dev->dev.irq[0], !dev->int_set);
+            qemu_set_irq(dev->dev.irq[0],  dev->int_set);
+        }
+    } else {
+        unsigned i, j;
+        uint16_t level_status = dev->status;
+
+        /* Check if there are any level sensitive interrupts set by
+           removing the ones that are edge sensitive from the status
+           register */
+        for (i = 0; i < N_MODULES; i++) {
+            for (j = 0; j < 2; j++) {
+                if (dev->ctrl[i] & CTRL_INT_EDGE(j)) {
+                    level_status &= ~STATUS_INT(i, j);
+                }
+            }
+        }
+
+        if (level_status && !dev->int_set) {
+            qemu_irq_raise(dev->dev.irq[0]);
+            dev->int_set = 1;
+        } else if (!level_status && dev->int_set) {
+            qemu_irq_lower(dev->dev.irq[0]);
+            dev->int_set = 0;
+        }
+    }
+}
+
+static uint64_t tpci200_read_cfg(void *opaque, hwaddr addr, unsigned size)
+{
+    TPCI200State *s = opaque;
+    uint8_t ret = 0;
+    if (addr < ARRAY_SIZE(local_config_regs)) {
+        ret = local_config_regs[addr];
+    }
+    /* Endianness is stored in the first bit of these registers */
+    if ((addr == 0x2b && s->big_endian[0]) ||
+        (addr == 0x2f && s->big_endian[1]) ||
+        (addr == 0x33 && s->big_endian[2])) {
+        ret |= 1;
+    }
+    DPRINTF("Read from LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) ret);
+    return ret;
+}
+
+static void tpci200_write_cfg(void *opaque, hwaddr addr, uint64_t val,
+                              unsigned size)
+{
+    TPCI200State *s = opaque;
+    /* Endianness is stored in the first bit of these registers */
+    if (addr == 0x2b || addr == 0x2f || addr == 0x33) {
+        unsigned las = (addr - 0x2b) / 4;
+        s->big_endian[las] = val & 1;
+        DPRINTF("LAS%u big endian mode: %u\n", las, (unsigned) val & 1);
+    } else {
+        DPRINTF("Write to LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) val);
+    }
+}
+
+static uint64_t tpci200_read_las0(void *opaque, hwaddr addr, unsigned size)
+{
+    TPCI200State *s = opaque;
+    uint64_t ret = 0;
+
+    switch (addr) {
+
+    case REG_REV_ID:
+        DPRINTF("Read REVISION ID\n"); /* Current value is 0x00 */
+        break;
+
+    case REG_IP_A_CTRL:
+    case REG_IP_B_CTRL:
+    case REG_IP_C_CTRL:
+    case REG_IP_D_CTRL:
+        {
+            unsigned ip_n = IP_N_FROM_REG(addr);
+            ret = s->ctrl[ip_n];
+            DPRINTF("Read IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) ret);
+        }
+        break;
+
+    case REG_RESET:
+        DPRINTF("Read RESET\n"); /* Not implemented */
+        break;
+
+    case REG_STATUS:
+        ret = s->status;
+        DPRINTF("Read STATUS: 0x%x\n", (unsigned) ret);
+        break;
+
+    /* Reserved */
+    default:
+        DPRINTF("Unsupported read from LAS0 0x%x\n", (unsigned) addr);
+        break;
+    }
+
+    return adjust_value(s->big_endian[0], &ret, size);
+}
+
+static void tpci200_write_las0(void *opaque, hwaddr addr, uint64_t val,
+                               unsigned size)
+{
+    TPCI200State *s = opaque;
+
+    adjust_value(s->big_endian[0], &val, size);
+
+    switch (addr) {
+
+    case REG_REV_ID:
+        DPRINTF("Write Revision ID: 0x%x\n", (unsigned) val); /* No effect */
+        break;
+
+    case REG_IP_A_CTRL:
+    case REG_IP_B_CTRL:
+    case REG_IP_C_CTRL:
+    case REG_IP_D_CTRL:
+        {
+            unsigned ip_n = IP_N_FROM_REG(addr);
+            s->ctrl[ip_n] = val;
+            DPRINTF("Write IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) val);
+        }
+        break;
+
+    case REG_RESET:
+        DPRINTF("Write RESET: 0x%x\n", (unsigned) val); /* Not implemented */
+        break;
+
+    case REG_STATUS:
+        {
+            unsigned i;
+
+            for (i = 0; i < N_MODULES; i++) {
+                IPackDevice *ip = ipack_device_find(&s->bus, i);
+
+                if (ip != NULL) {
+                    if (val & STATUS_INT(i, 0)) {
+                        DPRINTF("Clear IP %c INT0# status\n", 'A' + i);
+                        qemu_irq_lower(ip->irq[0]);
+                    }
+                    if (val & STATUS_INT(i, 1)) {
+                        DPRINTF("Clear IP %c INT1# status\n", 'A' + i);
+                        qemu_irq_lower(ip->irq[1]);
+                    }
+                }
+
+                if (val & STATUS_TIME(i)) {
+                    DPRINTF("Clear IP %c timeout\n", 'A' + i);
+                    s->status &= ~STATUS_TIME(i);
+                }
+            }
+
+            if (val & STATUS_ERR_ANY) {
+                DPRINTF("Unexpected write to STATUS register: 0x%x\n",
+                        (unsigned) val);
+            }
+        }
+        break;
+
+    /* Reserved */
+    default:
+        DPRINTF("Unsupported write to LAS0 0x%x: 0x%x\n",
+                (unsigned) addr, (unsigned) val);
+        break;
+    }
+}
+
+static uint64_t tpci200_read_las1(void *opaque, hwaddr addr, unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    uint64_t ret = 0;
+    unsigned ip_n, space;
+    uint8_t offset;
+
+    adjust_addr(s->big_endian[1], &addr, size);
+
+    /*
+     * The address is divided into the IP module number (0-4), the IP
+     * address space (I/O, ID, INT) and the offset within that space.
+     */
+    ip_n = addr >> 8;
+    space = (addr >> 6) & 3;
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Read LAS1: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        switch (space) {
+
+        case IP_ID_SPACE:
+            offset = addr & IP_ID_SPACE_ADDR_MASK;
+            if (k->id_read) {
+                ret = k->id_read(ip, offset);
+            }
+            break;
+
+        case IP_INT_SPACE:
+            offset = addr & IP_INT_SPACE_ADDR_MASK;
+
+            /* Read address 0 to ACK IP INT0# and address 2 to ACK IP INT1# */
+            if (offset == 0 || offset == 2) {
+                unsigned intno = offset / 2;
+                bool int_set = s->status & STATUS_INT(ip_n, intno);
+                bool int_edge_sensitive = s->ctrl[ip_n] & CTRL_INT_EDGE(intno);
+                if (int_set && !int_edge_sensitive) {
+                    qemu_irq_lower(ip->irq[intno]);
+                }
+            }
+
+            if (k->int_read) {
+                ret = k->int_read(ip, offset);
+            }
+            break;
+
+        default:
+            offset = addr & IP_IO_SPACE_ADDR_MASK;
+            if (k->io_read) {
+                ret = k->io_read(ip, offset);
+            }
+            break;
+        }
+    }
+
+    return adjust_value(s->big_endian[1], &ret, size);
+}
+
+static void tpci200_write_las1(void *opaque, hwaddr addr, uint64_t val,
+                               unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    unsigned ip_n, space;
+    uint8_t offset;
+
+    adjust_addr(s->big_endian[1], &addr, size);
+    adjust_value(s->big_endian[1], &val, size);
+
+    /*
+     * The address is divided into the IP module number, the IP
+     * address space (I/O, ID, INT) and the offset within that space.
+     */
+    ip_n = addr >> 8;
+    space = (addr >> 6) & 3;
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Write LAS1: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        switch (space) {
+
+        case IP_ID_SPACE:
+            offset = addr & IP_ID_SPACE_ADDR_MASK;
+            if (k->id_write) {
+                k->id_write(ip, offset, val);
+            }
+            break;
+
+        case IP_INT_SPACE:
+            offset = addr & IP_INT_SPACE_ADDR_MASK;
+            if (k->int_write) {
+                k->int_write(ip, offset, val);
+            }
+            break;
+
+        default:
+            offset = addr & IP_IO_SPACE_ADDR_MASK;
+            if (k->io_write) {
+                k->io_write(ip, offset, val);
+            }
+            break;
+        }
+    }
+}
+
+static uint64_t tpci200_read_las2(void *opaque, hwaddr addr, unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    uint64_t ret = 0;
+    unsigned ip_n;
+    uint32_t offset;
+
+    adjust_addr(s->big_endian[2], &addr, size);
+
+    /*
+     * The address is divided into the IP module number and the offset
+     * within the IP module MEM space.
+     */
+    ip_n = addr >> 23;
+    offset = addr & 0x7fffff;
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Read LAS2: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        if (k->mem_read16) {
+            ret = k->mem_read16(ip, offset);
+        }
+    }
+
+    return adjust_value(s->big_endian[2], &ret, size);
+}
+
+static void tpci200_write_las2(void *opaque, hwaddr addr, uint64_t val,
+                               unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    unsigned ip_n;
+    uint32_t offset;
+
+    adjust_addr(s->big_endian[2], &addr, size);
+    adjust_value(s->big_endian[2], &val, size);
+
+    /*
+     * The address is divided into the IP module number and the offset
+     * within the IP module MEM space.
+     */
+    ip_n = addr >> 23;
+    offset = addr & 0x7fffff;
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Write LAS2: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        if (k->mem_write16) {
+            k->mem_write16(ip, offset, val);
+        }
+    }
+}
+
+static uint64_t tpci200_read_las3(void *opaque, hwaddr addr, unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    uint64_t ret = 0;
+    /*
+     * The address is divided into the IP module number and the offset
+     * within the IP module MEM space.
+     */
+    unsigned ip_n = addr >> 22;
+    uint32_t offset = addr & 0x3fffff;
+
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Read LAS3: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        if (k->mem_read8) {
+            ret = k->mem_read8(ip, offset);
+        }
+    }
+
+    return ret;
+}
+
+static void tpci200_write_las3(void *opaque, hwaddr addr, uint64_t val,
+                               unsigned size)
+{
+    TPCI200State *s = opaque;
+    IPackDevice *ip;
+    /*
+     * The address is divided into the IP module number and the offset
+     * within the IP module MEM space.
+     */
+    unsigned ip_n = addr >> 22;
+    uint32_t offset = addr & 0x3fffff;
+
+    ip = ipack_device_find(&s->bus, ip_n);
+
+    if (ip == NULL) {
+        DPRINTF("Write LAS3: IP module %u not installed\n", ip_n);
+    } else {
+        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
+        if (k->mem_write8) {
+            k->mem_write8(ip, offset, val);
+        }
+    }
+}
+
+static const MemoryRegionOps tpci200_cfg_ops = {
+    .read = tpci200_read_cfg,
+    .write = tpci200_write_cfg,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 1,
+        .max_access_size = 4
+    },
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1
+    }
+};
+
+static const MemoryRegionOps tpci200_las0_ops = {
+    .read = tpci200_read_las0,
+    .write = tpci200_write_las0,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 2,
+        .max_access_size = 2
+    }
+};
+
+static const MemoryRegionOps tpci200_las1_ops = {
+    .read = tpci200_read_las1,
+    .write = tpci200_write_las1,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 1,
+        .max_access_size = 2
+    }
+};
+
+static const MemoryRegionOps tpci200_las2_ops = {
+    .read = tpci200_read_las2,
+    .write = tpci200_write_las2,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 1,
+        .max_access_size = 2
+    }
+};
+
+static const MemoryRegionOps tpci200_las3_ops = {
+    .read = tpci200_read_las3,
+    .write = tpci200_write_las3,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid =  {
+        .min_access_size = 1,
+        .max_access_size = 1
+    }
+};
+
+static int tpci200_initfn(PCIDevice *pci_dev)
+{
+    TPCI200State *s = TPCI200(pci_dev);
+    uint8_t *c = s->dev.config;
+
+    pci_set_word(c + PCI_COMMAND, 0x0003);
+    pci_set_word(c + PCI_STATUS,  0x0280);
+
+    pci_set_byte(c + PCI_INTERRUPT_PIN, 0x01); /* Interrupt pin A */
+
+    pci_set_byte(c + PCI_CAPABILITY_LIST, 0x40);
+    pci_set_long(c + 0x40, 0x48014801);
+    pci_set_long(c + 0x48, 0x00024C06);
+    pci_set_long(c + 0x4C, 0x00000003);
+
+    memory_region_init_io(&s->mmio, &tpci200_cfg_ops,
+                          s, "tpci200_mmio", 128);
+    memory_region_init_io(&s->io,   &tpci200_cfg_ops,
+                          s, "tpci200_io",   128);
+    memory_region_init_io(&s->las0, &tpci200_las0_ops,
+                          s, "tpci200_las0", 256);
+    memory_region_init_io(&s->las1, &tpci200_las1_ops,
+                          s, "tpci200_las1", 1024);
+    memory_region_init_io(&s->las2, &tpci200_las2_ops,
+                          s, "tpci200_las2", 1024*1024*32);
+    memory_region_init_io(&s->las3, &tpci200_las3_ops,
+                          s, "tpci200_las3", 1024*1024*16);
+    pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
+    pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO,     &s->io);
+    pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las0);
+    pci_register_bar(&s->dev, 3, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las1);
+    pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las2);
+    pci_register_bar(&s->dev, 5, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las3);
+
+    ipack_bus_new_inplace(&s->bus, DEVICE(&s->dev), NULL,
+                          N_MODULES, tpci200_set_irq);
+
+    return 0;
+}
+
+static void tpci200_exitfn(PCIDevice *pci_dev)
+{
+    TPCI200State *s = TPCI200(pci_dev);
+
+    memory_region_destroy(&s->mmio);
+    memory_region_destroy(&s->io);
+    memory_region_destroy(&s->las0);
+    memory_region_destroy(&s->las1);
+    memory_region_destroy(&s->las2);
+    memory_region_destroy(&s->las3);
+}
+
+static const VMStateDescription vmstate_tpci200 = {
+    .name = "tpci200",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(dev, TPCI200State),
+        VMSTATE_BOOL_ARRAY(big_endian, TPCI200State, 3),
+        VMSTATE_UINT8_ARRAY(ctrl, TPCI200State, N_MODULES),
+        VMSTATE_UINT16(status, TPCI200State),
+        VMSTATE_UINT8(int_set, TPCI200State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void tpci200_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = tpci200_initfn;
+    k->exit = tpci200_exitfn;
+    k->vendor_id = PCI_VENDOR_ID_TEWS;
+    k->device_id = PCI_DEVICE_ID_TEWS_TPCI200;
+    k->class_id = PCI_CLASS_BRIDGE_OTHER;
+    k->subsystem_vendor_id = PCI_VENDOR_ID_TEWS;
+    k->subsystem_id = 0x300A;
+    dc->desc = "TEWS TPCI200 IndustryPack carrier";
+    dc->vmsd = &vmstate_tpci200;
+}
+
+static const TypeInfo tpci200_info = {
+    .name          = TYPE_TPCI200,
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(TPCI200State),
+    .class_init    = tpci200_class_init,
+};
+
+static void tpci200_register_types(void)
+{
+    type_register_static(&tpci200_info);
+}
+
+type_init(tpci200_register_types)
diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
new file mode 100644 (file)
index 0000000..31f672c
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Virtio Console and Generic Serial Port Devices
+ *
+ * Copyright Red Hat, Inc. 2009, 2010
+ *
+ * Authors:
+ *  Amit Shah <amit.shah@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "char/char.h"
+#include "qemu/error-report.h"
+#include "trace.h"
+#include "hw/virtio/virtio-serial.h"
+
+typedef struct VirtConsole {
+    VirtIOSerialPort port;
+    CharDriverState *chr;
+} VirtConsole;
+
+/*
+ * Callback function that's called from chardevs when backend becomes
+ * writable.
+ */
+static gboolean chr_write_unblocked(GIOChannel *chan, GIOCondition cond,
+                                    void *opaque)
+{
+    VirtConsole *vcon = opaque;
+
+    virtio_serial_throttle_port(&vcon->port, false);
+    return FALSE;
+}
+
+/* Callback function that's called when the guest sends us data */
+static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
+{
+    VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
+    ssize_t ret;
+
+    if (!vcon->chr) {
+        /* If there's no backend, we can just say we consumed all data. */
+        return len;
+    }
+
+    ret = qemu_chr_fe_write(vcon->chr, buf, len);
+    trace_virtio_console_flush_buf(port->id, len, ret);
+
+    if (ret <= 0) {
+        VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
+
+        /*
+         * Ideally we'd get a better error code than just -1, but
+         * that's what the chardev interface gives us right now.  If
+         * we had a finer-grained message, like -EPIPE, we could close
+         * this connection.
+         */
+        ret = 0;
+        if (!k->is_console) {
+            virtio_serial_throttle_port(port, true);
+            qemu_chr_fe_add_watch(vcon->chr, G_IO_OUT, chr_write_unblocked,
+                                  vcon);
+        }
+    }
+    return ret;
+}
+
+/* Callback function that's called when the guest opens/closes the port */
+static void set_guest_connected(VirtIOSerialPort *port, int guest_connected)
+{
+    VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
+
+    if (!vcon->chr) {
+        return;
+    }
+    qemu_chr_fe_set_open(vcon->chr, guest_connected);
+}
+
+/* Readiness of the guest to accept data on a port */
+static int chr_can_read(void *opaque)
+{
+    VirtConsole *vcon = opaque;
+
+    return virtio_serial_guest_ready(&vcon->port);
+}
+
+/* Send data from a char device over to the guest */
+static void chr_read(void *opaque, const uint8_t *buf, int size)
+{
+    VirtConsole *vcon = opaque;
+
+    trace_virtio_console_chr_read(vcon->port.id, size);
+    virtio_serial_write(&vcon->port, buf, size);
+}
+
+static void chr_event(void *opaque, int event)
+{
+    VirtConsole *vcon = opaque;
+
+    trace_virtio_console_chr_event(vcon->port.id, event);
+    switch (event) {
+    case CHR_EVENT_OPENED:
+        virtio_serial_open(&vcon->port);
+        break;
+    case CHR_EVENT_CLOSED:
+        virtio_serial_close(&vcon->port);
+        break;
+    }
+}
+
+static int virtconsole_initfn(VirtIOSerialPort *port)
+{
+    VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
+    VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
+
+    if (port->id == 0 && !k->is_console) {
+        error_report("Port number 0 on virtio-serial devices reserved for virtconsole devices for backward compatibility.");
+        return -1;
+    }
+
+    if (vcon->chr) {
+        vcon->chr->explicit_fe_open = 1;
+        qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event,
+                              vcon);
+    }
+
+    return 0;
+}
+
+static Property virtconsole_properties[] = {
+    DEFINE_PROP_CHR("chardev", VirtConsole, chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtconsole_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass);
+
+    k->is_console = true;
+    k->init = virtconsole_initfn;
+    k->have_data = flush_buf;
+    k->set_guest_connected = set_guest_connected;
+    dc->props = virtconsole_properties;
+}
+
+static const TypeInfo virtconsole_info = {
+    .name          = "virtconsole",
+    .parent        = TYPE_VIRTIO_SERIAL_PORT,
+    .instance_size = sizeof(VirtConsole),
+    .class_init    = virtconsole_class_init,
+};
+
+static Property virtserialport_properties[] = {
+    DEFINE_PROP_CHR("chardev", VirtConsole, chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtserialport_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass);
+
+    k->init = virtconsole_initfn;
+    k->have_data = flush_buf;
+    k->set_guest_connected = set_guest_connected;
+    dc->props = virtserialport_properties;
+}
+
+static const TypeInfo virtserialport_info = {
+    .name          = "virtserialport",
+    .parent        = TYPE_VIRTIO_SERIAL_PORT,
+    .instance_size = sizeof(VirtConsole),
+    .class_init    = virtserialport_class_init,
+};
+
+static void virtconsole_register_types(void)
+{
+    type_register_static(&virtconsole_info);
+    type_register_static(&virtserialport_info);
+}
+
+type_init(virtconsole_register_types)
diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c
new file mode 100644 (file)
index 0000000..efc3232
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ *  Copyright (C) International Business Machines  Corp., 2005
+ *  Author(s): Anthony Liguori <aliguori@us.ibm.com>
+ *
+ *  Copyright (C) Red Hat 2007
+ *
+ *  Xen Console
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; under version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+
+#include "hw/hw.h"
+#include "char/char.h"
+#include "hw/xen/xen_backend.h"
+
+#include <xen/io/console.h>
+
+struct buffer {
+    uint8_t *data;
+    size_t consumed;
+    size_t size;
+    size_t capacity;
+    size_t max_capacity;
+};
+
+struct XenConsole {
+    struct XenDevice  xendev;  /* must be first */
+    struct buffer     buffer;
+    char              console[XEN_BUFSIZE];
+    int               ring_ref;
+    void              *sring;
+    CharDriverState   *chr;
+    int               backlog;
+};
+
+static void buffer_append(struct XenConsole *con)
+{
+    struct buffer *buffer = &con->buffer;
+    XENCONS_RING_IDX cons, prod, size;
+    struct xencons_interface *intf = con->sring;
+
+    cons = intf->out_cons;
+    prod = intf->out_prod;
+    xen_mb();
+
+    size = prod - cons;
+    if ((size == 0) || (size > sizeof(intf->out)))
+       return;
+
+    if ((buffer->capacity - buffer->size) < size) {
+       buffer->capacity += (size + 1024);
+       buffer->data = g_realloc(buffer->data, buffer->capacity);
+    }
+
+    while (cons != prod)
+       buffer->data[buffer->size++] = intf->out[
+           MASK_XENCONS_IDX(cons++, intf->out)];
+
+    xen_mb();
+    intf->out_cons = cons;
+    xen_be_send_notify(&con->xendev);
+
+    if (buffer->max_capacity &&
+       buffer->size > buffer->max_capacity) {
+       /* Discard the middle of the data. */
+
+       size_t over = buffer->size - buffer->max_capacity;
+       uint8_t *maxpos = buffer->data + buffer->max_capacity;
+
+       memmove(maxpos - over, maxpos, over);
+       buffer->data = g_realloc(buffer->data, buffer->max_capacity);
+       buffer->size = buffer->capacity = buffer->max_capacity;
+
+       if (buffer->consumed > buffer->max_capacity - over)
+           buffer->consumed = buffer->max_capacity - over;
+    }
+}
+
+static void buffer_advance(struct buffer *buffer, size_t len)
+{
+    buffer->consumed += len;
+    if (buffer->consumed == buffer->size) {
+       buffer->consumed = 0;
+       buffer->size = 0;
+    }
+}
+
+static int ring_free_bytes(struct XenConsole *con)
+{
+    struct xencons_interface *intf = con->sring;
+    XENCONS_RING_IDX cons, prod, space;
+
+    cons = intf->in_cons;
+    prod = intf->in_prod;
+    xen_mb();
+
+    space = prod - cons;
+    if (space > sizeof(intf->in))
+       return 0; /* ring is screwed: ignore it */
+
+    return (sizeof(intf->in) - space);
+}
+
+static int xencons_can_receive(void *opaque)
+{
+    struct XenConsole *con = opaque;
+    return ring_free_bytes(con);
+}
+
+static void xencons_receive(void *opaque, const uint8_t *buf, int len)
+{
+    struct XenConsole *con = opaque;
+    struct xencons_interface *intf = con->sring;
+    XENCONS_RING_IDX prod;
+    int i, max;
+
+    max = ring_free_bytes(con);
+    /* The can_receive() func limits this, but check again anyway */
+    if (max < len)
+       len = max;
+
+    prod = intf->in_prod;
+    for (i = 0; i < len; i++) {
+       intf->in[MASK_XENCONS_IDX(prod++, intf->in)] =
+           buf[i];
+    }
+    xen_wmb();
+    intf->in_prod = prod;
+    xen_be_send_notify(&con->xendev);
+}
+
+static void xencons_send(struct XenConsole *con)
+{
+    ssize_t len, size;
+
+    size = con->buffer.size - con->buffer.consumed;
+    if (con->chr)
+        len = qemu_chr_fe_write(con->chr, con->buffer.data + con->buffer.consumed,
+                             size);
+    else
+        len = size;
+    if (len < 1) {
+       if (!con->backlog) {
+           con->backlog = 1;
+           xen_be_printf(&con->xendev, 1, "backlog piling up, nobody listening?\n");
+       }
+    } else {
+       buffer_advance(&con->buffer, len);
+       if (con->backlog && len == size) {
+           con->backlog = 0;
+           xen_be_printf(&con->xendev, 1, "backlog is gone\n");
+       }
+    }
+}
+
+/* -------------------------------------------------------------------- */
+
+static int con_init(struct XenDevice *xendev)
+{
+    struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
+    char *type, *dom, label[32];
+    int ret = 0;
+    const char *output;
+
+    /* setup */
+    dom = xs_get_domain_path(xenstore, con->xendev.dom);
+    if (!xendev->dev) {
+        snprintf(con->console, sizeof(con->console), "%s/console", dom);
+    } else {
+        snprintf(con->console, sizeof(con->console), "%s/device/console/%d", dom, xendev->dev);
+    }
+    free(dom);
+
+    type = xenstore_read_str(con->console, "type");
+    if (!type || strcmp(type, "ioemu") != 0) {
+       xen_be_printf(xendev, 1, "not for me (type=%s)\n", type);
+        ret = -1;
+        goto out;
+    }
+
+    output = xenstore_read_str(con->console, "output");
+
+    /* no Xen override, use qemu output device */
+    if (output == NULL) {
+        con->chr = serial_hds[con->xendev.dev];
+    } else {
+        snprintf(label, sizeof(label), "xencons%d", con->xendev.dev);
+        con->chr = qemu_chr_new(label, output, NULL);
+    }
+
+    xenstore_store_pv_console_info(con->xendev.dev, con->chr);
+
+out:
+    g_free(type);
+    return ret;
+}
+
+static int con_initialise(struct XenDevice *xendev)
+{
+    struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
+    int limit;
+
+    if (xenstore_read_int(con->console, "ring-ref", &con->ring_ref) == -1)
+       return -1;
+    if (xenstore_read_int(con->console, "port", &con->xendev.remote_port) == -1)
+       return -1;
+    if (xenstore_read_int(con->console, "limit", &limit) == 0)
+       con->buffer.max_capacity = limit;
+
+    if (!xendev->dev) {
+        con->sring = xc_map_foreign_range(xen_xc, con->xendev.dom,
+                                          XC_PAGE_SIZE,
+                                          PROT_READ|PROT_WRITE,
+                                          con->ring_ref);
+    } else {
+        con->sring = xc_gnttab_map_grant_ref(xendev->gnttabdev, con->xendev.dom,
+                                             con->ring_ref,
+                                             PROT_READ|PROT_WRITE);
+    }
+    if (!con->sring)
+       return -1;
+
+    xen_be_bind_evtchn(&con->xendev);
+    if (con->chr) {
+        if (qemu_chr_fe_claim(con->chr) == 0) {
+            qemu_chr_add_handlers(con->chr, xencons_can_receive,
+                                  xencons_receive, NULL, con);
+        } else {
+            xen_be_printf(xendev, 0,
+                          "xen_console_init error chardev %s already used\n",
+                          con->chr->label);
+            con->chr = NULL;
+        }
+    }
+
+    xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n",
+                 con->ring_ref,
+                 con->xendev.remote_port,
+                 con->xendev.local_port,
+                 con->buffer.max_capacity);
+    return 0;
+}
+
+static void con_disconnect(struct XenDevice *xendev)
+{
+    struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
+
+    if (!xendev->dev) {
+        return;
+    }
+    if (con->chr) {
+        qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_release(con->chr);
+    }
+    xen_be_unbind_evtchn(&con->xendev);
+
+    if (con->sring) {
+        if (!xendev->gnttabdev) {
+            munmap(con->sring, XC_PAGE_SIZE);
+        } else {
+            xc_gnttab_munmap(xendev->gnttabdev, con->sring, 1);
+        }
+       con->sring = NULL;
+    }
+}
+
+static void con_event(struct XenDevice *xendev)
+{
+    struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
+
+    buffer_append(con);
+    if (con->buffer.size - con->buffer.consumed)
+       xencons_send(con);
+}
+
+/* -------------------------------------------------------------------- */
+
+struct XenDevOps xen_console_ops = {
+    .size       = sizeof(struct XenConsole),
+    .flags      = DEVOPS_FLAG_IGNORE_STATE|DEVOPS_FLAG_NEED_GNTDEV,
+    .init       = con_init,
+    .initialise = con_initialise,
+    .event      = con_event,
+    .disconnect = con_disconnect,
+};
diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c
new file mode 100644 (file)
index 0000000..079f4d4
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * QEMU model of Xilinx uartlite.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/sysbus.h"
+#include "char/char.h"
+
+#define DUART(x)
+
+#define R_RX            0
+#define R_TX            1
+#define R_STATUS        2
+#define R_CTRL          3
+#define R_MAX           4
+
+#define STATUS_RXVALID    0x01
+#define STATUS_RXFULL     0x02
+#define STATUS_TXEMPTY    0x04
+#define STATUS_TXFULL     0x08
+#define STATUS_IE         0x10
+#define STATUS_OVERRUN    0x20
+#define STATUS_FRAME      0x40
+#define STATUS_PARITY     0x80
+
+#define CONTROL_RST_TX    0x01
+#define CONTROL_RST_RX    0x02
+#define CONTROL_IE        0x10
+
+struct xlx_uartlite
+{
+    SysBusDevice busdev;
+    MemoryRegion mmio;
+    CharDriverState *chr;
+    qemu_irq irq;
+
+    uint8_t rx_fifo[8];
+    unsigned int rx_fifo_pos;
+    unsigned int rx_fifo_len;
+
+    uint32_t regs[R_MAX];
+};
+
+static void uart_update_irq(struct xlx_uartlite *s)
+{
+    unsigned int irq;
+
+    if (s->rx_fifo_len)
+        s->regs[R_STATUS] |= STATUS_IE;
+
+    irq = (s->regs[R_STATUS] & STATUS_IE) && (s->regs[R_CTRL] & CONTROL_IE);
+    qemu_set_irq(s->irq, irq);
+}
+
+static void uart_update_status(struct xlx_uartlite *s)
+{
+    uint32_t r;
+
+    r = s->regs[R_STATUS];
+    r &= ~7;
+    r |= 1 << 2; /* Tx fifo is always empty. We are fast :) */
+    r |= (s->rx_fifo_len == sizeof (s->rx_fifo)) << 1;
+    r |= (!!s->rx_fifo_len);
+    s->regs[R_STATUS] = r;
+}
+
+static uint64_t
+uart_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    struct xlx_uartlite *s = opaque;
+    uint32_t r = 0;
+    addr >>= 2;
+    switch (addr)
+    {
+        case R_RX:
+            r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 7];
+            if (s->rx_fifo_len)
+                s->rx_fifo_len--;
+            uart_update_status(s);
+            uart_update_irq(s);
+            qemu_chr_accept_input(s->chr);
+            break;
+
+        default:
+            if (addr < ARRAY_SIZE(s->regs))
+                r = s->regs[addr];
+            DUART(qemu_log("%s addr=%x v=%x\n", __func__, addr, r));
+            break;
+    }
+    return r;
+}
+
+static void
+uart_write(void *opaque, hwaddr addr,
+           uint64_t val64, unsigned int size)
+{
+    struct xlx_uartlite *s = opaque;
+    uint32_t value = val64;
+    unsigned char ch = value;
+
+    addr >>= 2;
+    switch (addr)
+    {
+        case R_STATUS:
+            hw_error("write to UART STATUS?\n");
+            break;
+
+        case R_CTRL:
+            if (value & CONTROL_RST_RX) {
+                s->rx_fifo_pos = 0;
+                s->rx_fifo_len = 0;
+            }
+            s->regs[addr] = value;
+            break;
+
+        case R_TX:
+            if (s->chr)
+                qemu_chr_fe_write(s->chr, &ch, 1);
+
+            s->regs[addr] = value;
+
+            /* hax.  */
+            s->regs[R_STATUS] |= STATUS_IE;
+            break;
+
+        default:
+            DUART(printf("%s addr=%x v=%x\n", __func__, addr, value));
+            if (addr < ARRAY_SIZE(s->regs))
+                s->regs[addr] = value;
+            break;
+    }
+    uart_update_status(s);
+    uart_update_irq(s);
+}
+
+static const MemoryRegionOps uart_ops = {
+    .read = uart_read,
+    .write = uart_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4
+    }
+};
+
+static void uart_rx(void *opaque, const uint8_t *buf, int size)
+{
+    struct xlx_uartlite *s = opaque;
+
+    /* Got a byte.  */
+    if (s->rx_fifo_len >= 8) {
+        printf("WARNING: UART dropped char.\n");
+        return;
+    }
+    s->rx_fifo[s->rx_fifo_pos] = *buf;
+    s->rx_fifo_pos++;
+    s->rx_fifo_pos &= 0x7;
+    s->rx_fifo_len++;
+
+    uart_update_status(s);
+    uart_update_irq(s);
+}
+
+static int uart_can_rx(void *opaque)
+{
+    struct xlx_uartlite *s = opaque;
+
+    return s->rx_fifo_len < sizeof(s->rx_fifo);
+}
+
+static void uart_event(void *opaque, int event)
+{
+
+}
+
+static int xilinx_uartlite_init(SysBusDevice *dev)
+{
+    struct xlx_uartlite *s = FROM_SYSBUS(typeof (*s), dev);
+
+    sysbus_init_irq(dev, &s->irq);
+
+    uart_update_status(s);
+    memory_region_init_io(&s->mmio, &uart_ops, s, "xlnx.xps-uartlite",
+                                                                R_MAX * 4);
+    sysbus_init_mmio(dev, &s->mmio);
+
+    s->chr = qemu_char_get_next_serial();
+    if (s->chr)
+        qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
+    return 0;
+}
+
+static void xilinx_uartlite_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = xilinx_uartlite_init;
+}
+
+static const TypeInfo xilinx_uartlite_info = {
+    .name          = "xlnx.xps-uartlite",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof (struct xlx_uartlite),
+    .class_init    = xilinx_uartlite_class_init,
+};
+
+static void xilinx_uart_register_types(void)
+{
+    type_register_static(&xilinx_uartlite_info);
+}
+
+type_init(xilinx_uart_register_types)
diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c
deleted file mode 100644 (file)
index 7a4d634..0000000
+++ /dev/null
@@ -1,3021 +0,0 @@
-/*
- * QEMU Cirrus CLGD 54xx VGA Emulator.
- *
- * Copyright (c) 2004 Fabrice Bellard
- * Copyright (c) 2004 Makoto Suzuki (suzu)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-/*
- * Reference: Finn Thogersons' VGADOC4b
- *   available at http://home.worldonline.dk/~finth/
- */
-#include "hw/hw.h"
-#include "hw/pci/pci.h"
-#include "ui/console.h"
-#include "hw/vga_int.h"
-#include "hw/loader.h"
-
-/*
- * TODO:
- *    - destination write mask support not complete (bits 5..7)
- *    - optimize linear mappings
- *    - optimize bitblt functions
- */
-
-//#define DEBUG_CIRRUS
-//#define DEBUG_BITBLT
-
-/***************************************
- *
- *  definitions
- *
- ***************************************/
-
-// ID
-#define CIRRUS_ID_CLGD5422  (0x23<<2)
-#define CIRRUS_ID_CLGD5426  (0x24<<2)
-#define CIRRUS_ID_CLGD5424  (0x25<<2)
-#define CIRRUS_ID_CLGD5428  (0x26<<2)
-#define CIRRUS_ID_CLGD5430  (0x28<<2)
-#define CIRRUS_ID_CLGD5434  (0x2A<<2)
-#define CIRRUS_ID_CLGD5436  (0x2B<<2)
-#define CIRRUS_ID_CLGD5446  (0x2E<<2)
-
-// sequencer 0x07
-#define CIRRUS_SR7_BPP_VGA            0x00
-#define CIRRUS_SR7_BPP_SVGA           0x01
-#define CIRRUS_SR7_BPP_MASK           0x0e
-#define CIRRUS_SR7_BPP_8              0x00
-#define CIRRUS_SR7_BPP_16_DOUBLEVCLK  0x02
-#define CIRRUS_SR7_BPP_24             0x04
-#define CIRRUS_SR7_BPP_16             0x06
-#define CIRRUS_SR7_BPP_32             0x08
-#define CIRRUS_SR7_ISAADDR_MASK       0xe0
-
-// sequencer 0x0f
-#define CIRRUS_MEMSIZE_512k        0x08
-#define CIRRUS_MEMSIZE_1M          0x10
-#define CIRRUS_MEMSIZE_2M          0x18
-#define CIRRUS_MEMFLAGS_BANKSWITCH 0x80        // bank switching is enabled.
-
-// sequencer 0x12
-#define CIRRUS_CURSOR_SHOW         0x01
-#define CIRRUS_CURSOR_HIDDENPEL    0x02
-#define CIRRUS_CURSOR_LARGE        0x04        // 64x64 if set, 32x32 if clear
-
-// sequencer 0x17
-#define CIRRUS_BUSTYPE_VLBFAST   0x10
-#define CIRRUS_BUSTYPE_PCI       0x20
-#define CIRRUS_BUSTYPE_VLBSLOW   0x30
-#define CIRRUS_BUSTYPE_ISA       0x38
-#define CIRRUS_MMIO_ENABLE       0x04
-#define CIRRUS_MMIO_USE_PCIADDR  0x40  // 0xb8000 if cleared.
-#define CIRRUS_MEMSIZEEXT_DOUBLE 0x80
-
-// control 0x0b
-#define CIRRUS_BANKING_DUAL             0x01
-#define CIRRUS_BANKING_GRANULARITY_16K  0x20   // set:16k, clear:4k
-
-// control 0x30
-#define CIRRUS_BLTMODE_BACKWARDS        0x01
-#define CIRRUS_BLTMODE_MEMSYSDEST       0x02
-#define CIRRUS_BLTMODE_MEMSYSSRC        0x04
-#define CIRRUS_BLTMODE_TRANSPARENTCOMP  0x08
-#define CIRRUS_BLTMODE_PATTERNCOPY      0x40
-#define CIRRUS_BLTMODE_COLOREXPAND      0x80
-#define CIRRUS_BLTMODE_PIXELWIDTHMASK   0x30
-#define CIRRUS_BLTMODE_PIXELWIDTH8      0x00
-#define CIRRUS_BLTMODE_PIXELWIDTH16     0x10
-#define CIRRUS_BLTMODE_PIXELWIDTH24     0x20
-#define CIRRUS_BLTMODE_PIXELWIDTH32     0x30
-
-// control 0x31
-#define CIRRUS_BLT_BUSY                 0x01
-#define CIRRUS_BLT_START                0x02
-#define CIRRUS_BLT_RESET                0x04
-#define CIRRUS_BLT_FIFOUSED             0x10
-#define CIRRUS_BLT_AUTOSTART            0x80
-
-// control 0x32
-#define CIRRUS_ROP_0                    0x00
-#define CIRRUS_ROP_SRC_AND_DST          0x05
-#define CIRRUS_ROP_NOP                  0x06
-#define CIRRUS_ROP_SRC_AND_NOTDST       0x09
-#define CIRRUS_ROP_NOTDST               0x0b
-#define CIRRUS_ROP_SRC                  0x0d
-#define CIRRUS_ROP_1                    0x0e
-#define CIRRUS_ROP_NOTSRC_AND_DST       0x50
-#define CIRRUS_ROP_SRC_XOR_DST          0x59
-#define CIRRUS_ROP_SRC_OR_DST           0x6d
-#define CIRRUS_ROP_NOTSRC_OR_NOTDST     0x90
-#define CIRRUS_ROP_SRC_NOTXOR_DST       0x95
-#define CIRRUS_ROP_SRC_OR_NOTDST        0xad
-#define CIRRUS_ROP_NOTSRC               0xd0
-#define CIRRUS_ROP_NOTSRC_OR_DST        0xd6
-#define CIRRUS_ROP_NOTSRC_AND_NOTDST    0xda
-
-#define CIRRUS_ROP_NOP_INDEX 2
-#define CIRRUS_ROP_SRC_INDEX 5
-
-// control 0x33
-#define CIRRUS_BLTMODEEXT_SOLIDFILL        0x04
-#define CIRRUS_BLTMODEEXT_COLOREXPINV      0x02
-#define CIRRUS_BLTMODEEXT_DWORDGRANULARITY 0x01
-
-// memory-mapped IO
-#define CIRRUS_MMIO_BLTBGCOLOR        0x00     // dword
-#define CIRRUS_MMIO_BLTFGCOLOR        0x04     // dword
-#define CIRRUS_MMIO_BLTWIDTH          0x08     // word
-#define CIRRUS_MMIO_BLTHEIGHT         0x0a     // word
-#define CIRRUS_MMIO_BLTDESTPITCH      0x0c     // word
-#define CIRRUS_MMIO_BLTSRCPITCH       0x0e     // word
-#define CIRRUS_MMIO_BLTDESTADDR       0x10     // dword
-#define CIRRUS_MMIO_BLTSRCADDR        0x14     // dword
-#define CIRRUS_MMIO_BLTWRITEMASK      0x17     // byte
-#define CIRRUS_MMIO_BLTMODE           0x18     // byte
-#define CIRRUS_MMIO_BLTROP            0x1a     // byte
-#define CIRRUS_MMIO_BLTMODEEXT        0x1b     // byte
-#define CIRRUS_MMIO_BLTTRANSPARENTCOLOR 0x1c   // word?
-#define CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK 0x20       // word?
-#define CIRRUS_MMIO_LINEARDRAW_START_X 0x24    // word
-#define CIRRUS_MMIO_LINEARDRAW_START_Y 0x26    // word
-#define CIRRUS_MMIO_LINEARDRAW_END_X  0x28     // word
-#define CIRRUS_MMIO_LINEARDRAW_END_Y  0x2a     // word
-#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_INC 0x2c      // byte
-#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ROLLOVER 0x2d // byte
-#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_MASK 0x2e     // byte
-#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ACCUM 0x2f    // byte
-#define CIRRUS_MMIO_BRESENHAM_K1      0x30     // word
-#define CIRRUS_MMIO_BRESENHAM_K3      0x32     // word
-#define CIRRUS_MMIO_BRESENHAM_ERROR   0x34     // word
-#define CIRRUS_MMIO_BRESENHAM_DELTA_MAJOR 0x36 // word
-#define CIRRUS_MMIO_BRESENHAM_DIRECTION 0x38   // byte
-#define CIRRUS_MMIO_LINEDRAW_MODE     0x39     // byte
-#define CIRRUS_MMIO_BLTSTATUS         0x40     // byte
-
-#define CIRRUS_PNPMMIO_SIZE         0x1000
-
-#define BLTUNSAFE(s) \
-    ( \
-        ( /* check dst is within bounds */ \
-            (s)->cirrus_blt_height * ABS((s)->cirrus_blt_dstpitch) \
-                + ((s)->cirrus_blt_dstaddr & (s)->cirrus_addr_mask) > \
-                    (s)->vga.vram_size \
-        ) || \
-        ( /* check src is within bounds */ \
-            (s)->cirrus_blt_height * ABS((s)->cirrus_blt_srcpitch) \
-                + ((s)->cirrus_blt_srcaddr & (s)->cirrus_addr_mask) > \
-                    (s)->vga.vram_size \
-        ) \
-    )
-
-struct CirrusVGAState;
-typedef void (*cirrus_bitblt_rop_t) (struct CirrusVGAState *s,
-                                     uint8_t * dst, const uint8_t * src,
-                                    int dstpitch, int srcpitch,
-                                    int bltwidth, int bltheight);
-typedef void (*cirrus_fill_t)(struct CirrusVGAState *s,
-                              uint8_t *dst, int dst_pitch, int width, int height);
-
-typedef struct CirrusVGAState {
-    VGACommonState vga;
-
-    MemoryRegion cirrus_vga_io;
-    MemoryRegion cirrus_linear_io;
-    MemoryRegion cirrus_linear_bitblt_io;
-    MemoryRegion cirrus_mmio_io;
-    MemoryRegion pci_bar;
-    bool linear_vram;  /* vga.vram mapped over cirrus_linear_io */
-    MemoryRegion low_mem_container; /* container for 0xa0000-0xc0000 */
-    MemoryRegion low_mem;           /* always mapped, overridden by: */
-    MemoryRegion cirrus_bank[2];    /*   aliases at 0xa0000-0xb0000  */
-    uint32_t cirrus_addr_mask;
-    uint32_t linear_mmio_mask;
-    uint8_t cirrus_shadow_gr0;
-    uint8_t cirrus_shadow_gr1;
-    uint8_t cirrus_hidden_dac_lockindex;
-    uint8_t cirrus_hidden_dac_data;
-    uint32_t cirrus_bank_base[2];
-    uint32_t cirrus_bank_limit[2];
-    uint8_t cirrus_hidden_palette[48];
-    uint32_t hw_cursor_x;
-    uint32_t hw_cursor_y;
-    int cirrus_blt_pixelwidth;
-    int cirrus_blt_width;
-    int cirrus_blt_height;
-    int cirrus_blt_dstpitch;
-    int cirrus_blt_srcpitch;
-    uint32_t cirrus_blt_fgcol;
-    uint32_t cirrus_blt_bgcol;
-    uint32_t cirrus_blt_dstaddr;
-    uint32_t cirrus_blt_srcaddr;
-    uint8_t cirrus_blt_mode;
-    uint8_t cirrus_blt_modeext;
-    cirrus_bitblt_rop_t cirrus_rop;
-#define CIRRUS_BLTBUFSIZE (2048 * 4) /* one line width */
-    uint8_t cirrus_bltbuf[CIRRUS_BLTBUFSIZE];
-    uint8_t *cirrus_srcptr;
-    uint8_t *cirrus_srcptr_end;
-    uint32_t cirrus_srccounter;
-    /* hwcursor display state */
-    int last_hw_cursor_size;
-    int last_hw_cursor_x;
-    int last_hw_cursor_y;
-    int last_hw_cursor_y_start;
-    int last_hw_cursor_y_end;
-    int real_vram_size; /* XXX: suppress that */
-    int device_id;
-    int bustype;
-} CirrusVGAState;
-
-typedef struct PCICirrusVGAState {
-    PCIDevice dev;
-    CirrusVGAState cirrus_vga;
-} PCICirrusVGAState;
-
-typedef struct ISACirrusVGAState {
-    ISADevice dev;
-    CirrusVGAState cirrus_vga;
-} ISACirrusVGAState;
-
-static uint8_t rop_to_index[256];
-
-/***************************************
- *
- *  prototypes.
- *
- ***************************************/
-
-
-static void cirrus_bitblt_reset(CirrusVGAState *s);
-static void cirrus_update_memory_access(CirrusVGAState *s);
-
-/***************************************
- *
- *  raster operations
- *
- ***************************************/
-
-static void cirrus_bitblt_rop_nop(CirrusVGAState *s,
-                                  uint8_t *dst,const uint8_t *src,
-                                  int dstpitch,int srcpitch,
-                                  int bltwidth,int bltheight)
-{
-}
-
-static void cirrus_bitblt_fill_nop(CirrusVGAState *s,
-                                   uint8_t *dst,
-                                   int dstpitch, int bltwidth,int bltheight)
-{
-}
-
-#define ROP_NAME 0
-#define ROP_FN(d, s) 0
-#include "hw/cirrus_vga_rop.h"
-
-#define ROP_NAME src_and_dst
-#define ROP_FN(d, s) (s) & (d)
-#include "hw/cirrus_vga_rop.h"
-
-#define ROP_NAME src_and_notdst
-#define ROP_FN(d, s) (s) & (~(d))
-#include "hw/cirrus_vga_rop.h"
-
-#define ROP_NAME notdst
-#define ROP_FN(d, s) ~(d)
-#include "hw/cirrus_vga_rop.h"
-
-#define ROP_NAME src
-#define ROP_FN(d, s) s
-#include "hw/cirrus_vga_rop.h"
-
-#define ROP_NAME 1
-#define ROP_FN(d, s) ~0
-#include "hw/cirrus_vga_rop.h"
-
-#define ROP_NAME notsrc_and_dst
-#define ROP_FN(d, s) (~(s)) & (d)
-#include "hw/cirrus_vga_rop.h"
-
-#define ROP_NAME src_xor_dst
-#define ROP_FN(d, s) (s) ^ (d)
-#include "hw/cirrus_vga_rop.h"
-
-#define ROP_NAME src_or_dst
-#define ROP_FN(d, s) (s) | (d)
-#include "hw/cirrus_vga_rop.h"
-
-#define ROP_NAME notsrc_or_notdst
-#define ROP_FN(d, s) (~(s)) | (~(d))
-#include "hw/cirrus_vga_rop.h"
-
-#define ROP_NAME src_notxor_dst
-#define ROP_FN(d, s) ~((s) ^ (d))
-#include "hw/cirrus_vga_rop.h"
-
-#define ROP_NAME src_or_notdst
-#define ROP_FN(d, s) (s) | (~(d))
-#include "hw/cirrus_vga_rop.h"
-
-#define ROP_NAME notsrc
-#define ROP_FN(d, s) (~(s))
-#include "hw/cirrus_vga_rop.h"
-
-#define ROP_NAME notsrc_or_dst
-#define ROP_FN(d, s) (~(s)) | (d)
-#include "hw/cirrus_vga_rop.h"
-
-#define ROP_NAME notsrc_and_notdst
-#define ROP_FN(d, s) (~(s)) & (~(d))
-#include "hw/cirrus_vga_rop.h"
-
-static const cirrus_bitblt_rop_t cirrus_fwd_rop[16] = {
-    cirrus_bitblt_rop_fwd_0,
-    cirrus_bitblt_rop_fwd_src_and_dst,
-    cirrus_bitblt_rop_nop,
-    cirrus_bitblt_rop_fwd_src_and_notdst,
-    cirrus_bitblt_rop_fwd_notdst,
-    cirrus_bitblt_rop_fwd_src,
-    cirrus_bitblt_rop_fwd_1,
-    cirrus_bitblt_rop_fwd_notsrc_and_dst,
-    cirrus_bitblt_rop_fwd_src_xor_dst,
-    cirrus_bitblt_rop_fwd_src_or_dst,
-    cirrus_bitblt_rop_fwd_notsrc_or_notdst,
-    cirrus_bitblt_rop_fwd_src_notxor_dst,
-    cirrus_bitblt_rop_fwd_src_or_notdst,
-    cirrus_bitblt_rop_fwd_notsrc,
-    cirrus_bitblt_rop_fwd_notsrc_or_dst,
-    cirrus_bitblt_rop_fwd_notsrc_and_notdst,
-};
-
-static const cirrus_bitblt_rop_t cirrus_bkwd_rop[16] = {
-    cirrus_bitblt_rop_bkwd_0,
-    cirrus_bitblt_rop_bkwd_src_and_dst,
-    cirrus_bitblt_rop_nop,
-    cirrus_bitblt_rop_bkwd_src_and_notdst,
-    cirrus_bitblt_rop_bkwd_notdst,
-    cirrus_bitblt_rop_bkwd_src,
-    cirrus_bitblt_rop_bkwd_1,
-    cirrus_bitblt_rop_bkwd_notsrc_and_dst,
-    cirrus_bitblt_rop_bkwd_src_xor_dst,
-    cirrus_bitblt_rop_bkwd_src_or_dst,
-    cirrus_bitblt_rop_bkwd_notsrc_or_notdst,
-    cirrus_bitblt_rop_bkwd_src_notxor_dst,
-    cirrus_bitblt_rop_bkwd_src_or_notdst,
-    cirrus_bitblt_rop_bkwd_notsrc,
-    cirrus_bitblt_rop_bkwd_notsrc_or_dst,
-    cirrus_bitblt_rop_bkwd_notsrc_and_notdst,
-};
-
-#define TRANSP_ROP(name) {\
-    name ## _8,\
-    name ## _16,\
-        }
-#define TRANSP_NOP(func) {\
-    func,\
-    func,\
-        }
-
-static const cirrus_bitblt_rop_t cirrus_fwd_transp_rop[16][2] = {
-    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_0),
-    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_and_dst),
-    TRANSP_NOP(cirrus_bitblt_rop_nop),
-    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_and_notdst),
-    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notdst),
-    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src),
-    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_1),
-    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_and_dst),
-    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_xor_dst),
-    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_or_dst),
-    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_or_notdst),
-    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_notxor_dst),
-    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_or_notdst),
-    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc),
-    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_or_dst),
-    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_and_notdst),
-};
-
-static const cirrus_bitblt_rop_t cirrus_bkwd_transp_rop[16][2] = {
-    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_0),
-    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_and_dst),
-    TRANSP_NOP(cirrus_bitblt_rop_nop),
-    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_and_notdst),
-    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notdst),
-    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src),
-    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_1),
-    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_and_dst),
-    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_xor_dst),
-    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_or_dst),
-    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_or_notdst),
-    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_notxor_dst),
-    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_or_notdst),
-    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc),
-    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_or_dst),
-    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_and_notdst),
-};
-
-#define ROP2(name) {\
-    name ## _8,\
-    name ## _16,\
-    name ## _24,\
-    name ## _32,\
-        }
-
-#define ROP_NOP2(func) {\
-    func,\
-    func,\
-    func,\
-    func,\
-        }
-
-static const cirrus_bitblt_rop_t cirrus_patternfill[16][4] = {
-    ROP2(cirrus_patternfill_0),
-    ROP2(cirrus_patternfill_src_and_dst),
-    ROP_NOP2(cirrus_bitblt_rop_nop),
-    ROP2(cirrus_patternfill_src_and_notdst),
-    ROP2(cirrus_patternfill_notdst),
-    ROP2(cirrus_patternfill_src),
-    ROP2(cirrus_patternfill_1),
-    ROP2(cirrus_patternfill_notsrc_and_dst),
-    ROP2(cirrus_patternfill_src_xor_dst),
-    ROP2(cirrus_patternfill_src_or_dst),
-    ROP2(cirrus_patternfill_notsrc_or_notdst),
-    ROP2(cirrus_patternfill_src_notxor_dst),
-    ROP2(cirrus_patternfill_src_or_notdst),
-    ROP2(cirrus_patternfill_notsrc),
-    ROP2(cirrus_patternfill_notsrc_or_dst),
-    ROP2(cirrus_patternfill_notsrc_and_notdst),
-};
-
-static const cirrus_bitblt_rop_t cirrus_colorexpand_transp[16][4] = {
-    ROP2(cirrus_colorexpand_transp_0),
-    ROP2(cirrus_colorexpand_transp_src_and_dst),
-    ROP_NOP2(cirrus_bitblt_rop_nop),
-    ROP2(cirrus_colorexpand_transp_src_and_notdst),
-    ROP2(cirrus_colorexpand_transp_notdst),
-    ROP2(cirrus_colorexpand_transp_src),
-    ROP2(cirrus_colorexpand_transp_1),
-    ROP2(cirrus_colorexpand_transp_notsrc_and_dst),
-    ROP2(cirrus_colorexpand_transp_src_xor_dst),
-    ROP2(cirrus_colorexpand_transp_src_or_dst),
-    ROP2(cirrus_colorexpand_transp_notsrc_or_notdst),
-    ROP2(cirrus_colorexpand_transp_src_notxor_dst),
-    ROP2(cirrus_colorexpand_transp_src_or_notdst),
-    ROP2(cirrus_colorexpand_transp_notsrc),
-    ROP2(cirrus_colorexpand_transp_notsrc_or_dst),
-    ROP2(cirrus_colorexpand_transp_notsrc_and_notdst),
-};
-
-static const cirrus_bitblt_rop_t cirrus_colorexpand[16][4] = {
-    ROP2(cirrus_colorexpand_0),
-    ROP2(cirrus_colorexpand_src_and_dst),
-    ROP_NOP2(cirrus_bitblt_rop_nop),
-    ROP2(cirrus_colorexpand_src_and_notdst),
-    ROP2(cirrus_colorexpand_notdst),
-    ROP2(cirrus_colorexpand_src),
-    ROP2(cirrus_colorexpand_1),
-    ROP2(cirrus_colorexpand_notsrc_and_dst),
-    ROP2(cirrus_colorexpand_src_xor_dst),
-    ROP2(cirrus_colorexpand_src_or_dst),
-    ROP2(cirrus_colorexpand_notsrc_or_notdst),
-    ROP2(cirrus_colorexpand_src_notxor_dst),
-    ROP2(cirrus_colorexpand_src_or_notdst),
-    ROP2(cirrus_colorexpand_notsrc),
-    ROP2(cirrus_colorexpand_notsrc_or_dst),
-    ROP2(cirrus_colorexpand_notsrc_and_notdst),
-};
-
-static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern_transp[16][4] = {
-    ROP2(cirrus_colorexpand_pattern_transp_0),
-    ROP2(cirrus_colorexpand_pattern_transp_src_and_dst),
-    ROP_NOP2(cirrus_bitblt_rop_nop),
-    ROP2(cirrus_colorexpand_pattern_transp_src_and_notdst),
-    ROP2(cirrus_colorexpand_pattern_transp_notdst),
-    ROP2(cirrus_colorexpand_pattern_transp_src),
-    ROP2(cirrus_colorexpand_pattern_transp_1),
-    ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_dst),
-    ROP2(cirrus_colorexpand_pattern_transp_src_xor_dst),
-    ROP2(cirrus_colorexpand_pattern_transp_src_or_dst),
-    ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_notdst),
-    ROP2(cirrus_colorexpand_pattern_transp_src_notxor_dst),
-    ROP2(cirrus_colorexpand_pattern_transp_src_or_notdst),
-    ROP2(cirrus_colorexpand_pattern_transp_notsrc),
-    ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_dst),
-    ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_notdst),
-};
-
-static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern[16][4] = {
-    ROP2(cirrus_colorexpand_pattern_0),
-    ROP2(cirrus_colorexpand_pattern_src_and_dst),
-    ROP_NOP2(cirrus_bitblt_rop_nop),
-    ROP2(cirrus_colorexpand_pattern_src_and_notdst),
-    ROP2(cirrus_colorexpand_pattern_notdst),
-    ROP2(cirrus_colorexpand_pattern_src),
-    ROP2(cirrus_colorexpand_pattern_1),
-    ROP2(cirrus_colorexpand_pattern_notsrc_and_dst),
-    ROP2(cirrus_colorexpand_pattern_src_xor_dst),
-    ROP2(cirrus_colorexpand_pattern_src_or_dst),
-    ROP2(cirrus_colorexpand_pattern_notsrc_or_notdst),
-    ROP2(cirrus_colorexpand_pattern_src_notxor_dst),
-    ROP2(cirrus_colorexpand_pattern_src_or_notdst),
-    ROP2(cirrus_colorexpand_pattern_notsrc),
-    ROP2(cirrus_colorexpand_pattern_notsrc_or_dst),
-    ROP2(cirrus_colorexpand_pattern_notsrc_and_notdst),
-};
-
-static const cirrus_fill_t cirrus_fill[16][4] = {
-    ROP2(cirrus_fill_0),
-    ROP2(cirrus_fill_src_and_dst),
-    ROP_NOP2(cirrus_bitblt_fill_nop),
-    ROP2(cirrus_fill_src_and_notdst),
-    ROP2(cirrus_fill_notdst),
-    ROP2(cirrus_fill_src),
-    ROP2(cirrus_fill_1),
-    ROP2(cirrus_fill_notsrc_and_dst),
-    ROP2(cirrus_fill_src_xor_dst),
-    ROP2(cirrus_fill_src_or_dst),
-    ROP2(cirrus_fill_notsrc_or_notdst),
-    ROP2(cirrus_fill_src_notxor_dst),
-    ROP2(cirrus_fill_src_or_notdst),
-    ROP2(cirrus_fill_notsrc),
-    ROP2(cirrus_fill_notsrc_or_dst),
-    ROP2(cirrus_fill_notsrc_and_notdst),
-};
-
-static inline void cirrus_bitblt_fgcol(CirrusVGAState *s)
-{
-    unsigned int color;
-    switch (s->cirrus_blt_pixelwidth) {
-    case 1:
-        s->cirrus_blt_fgcol = s->cirrus_shadow_gr1;
-        break;
-    case 2:
-        color = s->cirrus_shadow_gr1 | (s->vga.gr[0x11] << 8);
-        s->cirrus_blt_fgcol = le16_to_cpu(color);
-        break;
-    case 3:
-        s->cirrus_blt_fgcol = s->cirrus_shadow_gr1 |
-            (s->vga.gr[0x11] << 8) | (s->vga.gr[0x13] << 16);
-        break;
-    default:
-    case 4:
-        color = s->cirrus_shadow_gr1 | (s->vga.gr[0x11] << 8) |
-            (s->vga.gr[0x13] << 16) | (s->vga.gr[0x15] << 24);
-        s->cirrus_blt_fgcol = le32_to_cpu(color);
-        break;
-    }
-}
-
-static inline void cirrus_bitblt_bgcol(CirrusVGAState *s)
-{
-    unsigned int color;
-    switch (s->cirrus_blt_pixelwidth) {
-    case 1:
-        s->cirrus_blt_bgcol = s->cirrus_shadow_gr0;
-        break;
-    case 2:
-        color = s->cirrus_shadow_gr0 | (s->vga.gr[0x10] << 8);
-        s->cirrus_blt_bgcol = le16_to_cpu(color);
-        break;
-    case 3:
-        s->cirrus_blt_bgcol = s->cirrus_shadow_gr0 |
-            (s->vga.gr[0x10] << 8) | (s->vga.gr[0x12] << 16);
-        break;
-    default:
-    case 4:
-        color = s->cirrus_shadow_gr0 | (s->vga.gr[0x10] << 8) |
-            (s->vga.gr[0x12] << 16) | (s->vga.gr[0x14] << 24);
-        s->cirrus_blt_bgcol = le32_to_cpu(color);
-        break;
-    }
-}
-
-static void cirrus_invalidate_region(CirrusVGAState * s, int off_begin,
-                                    int off_pitch, int bytesperline,
-                                    int lines)
-{
-    int y;
-    int off_cur;
-    int off_cur_end;
-
-    for (y = 0; y < lines; y++) {
-       off_cur = off_begin;
-       off_cur_end = (off_cur + bytesperline) & s->cirrus_addr_mask;
-        memory_region_set_dirty(&s->vga.vram, off_cur, off_cur_end - off_cur);
-       off_begin += off_pitch;
-    }
-}
-
-static int cirrus_bitblt_common_patterncopy(CirrusVGAState * s,
-                                           const uint8_t * src)
-{
-    uint8_t *dst;
-
-    dst = s->vga.vram_ptr + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask);
-
-    if (BLTUNSAFE(s))
-        return 0;
-
-    (*s->cirrus_rop) (s, dst, src,
-                      s->cirrus_blt_dstpitch, 0,
-                      s->cirrus_blt_width, s->cirrus_blt_height);
-    cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
-                             s->cirrus_blt_dstpitch, s->cirrus_blt_width,
-                             s->cirrus_blt_height);
-    return 1;
-}
-
-/* fill */
-
-static int cirrus_bitblt_solidfill(CirrusVGAState *s, int blt_rop)
-{
-    cirrus_fill_t rop_func;
-
-    if (BLTUNSAFE(s))
-        return 0;
-    rop_func = cirrus_fill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
-    rop_func(s, s->vga.vram_ptr + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask),
-             s->cirrus_blt_dstpitch,
-             s->cirrus_blt_width, s->cirrus_blt_height);
-    cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
-                            s->cirrus_blt_dstpitch, s->cirrus_blt_width,
-                            s->cirrus_blt_height);
-    cirrus_bitblt_reset(s);
-    return 1;
-}
-
-/***************************************
- *
- *  bitblt (video-to-video)
- *
- ***************************************/
-
-static int cirrus_bitblt_videotovideo_patterncopy(CirrusVGAState * s)
-{
-    return cirrus_bitblt_common_patterncopy(s,
-                                           s->vga.vram_ptr + ((s->cirrus_blt_srcaddr & ~7) &
-                                            s->cirrus_addr_mask));
-}
-
-static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h)
-{
-    int sx = 0, sy = 0;
-    int dx = 0, dy = 0;
-    int depth = 0;
-    int notify = 0;
-
-    /* make sure to only copy if it's a plain copy ROP */
-    if (*s->cirrus_rop == cirrus_bitblt_rop_fwd_src ||
-        *s->cirrus_rop == cirrus_bitblt_rop_bkwd_src) {
-
-        int width, height;
-
-        depth = s->vga.get_bpp(&s->vga) / 8;
-        s->vga.get_resolution(&s->vga, &width, &height);
-
-        /* extra x, y */
-        sx = (src % ABS(s->cirrus_blt_srcpitch)) / depth;
-        sy = (src / ABS(s->cirrus_blt_srcpitch));
-        dx = (dst % ABS(s->cirrus_blt_dstpitch)) / depth;
-        dy = (dst / ABS(s->cirrus_blt_dstpitch));
-
-        /* normalize width */
-        w /= depth;
-
-        /* if we're doing a backward copy, we have to adjust
-           our x/y to be the upper left corner (instead of the lower
-           right corner) */
-        if (s->cirrus_blt_dstpitch < 0) {
-            sx -= (s->cirrus_blt_width / depth) - 1;
-            dx -= (s->cirrus_blt_width / depth) - 1;
-            sy -= s->cirrus_blt_height - 1;
-            dy -= s->cirrus_blt_height - 1;
-        }
-
-        /* are we in the visible portion of memory? */
-        if (sx >= 0 && sy >= 0 && dx >= 0 && dy >= 0 &&
-            (sx + w) <= width && (sy + h) <= height &&
-            (dx + w) <= width && (dy + h) <= height) {
-            notify = 1;
-        }
-    }
-
-    /* we have to flush all pending changes so that the copy
-       is generated at the appropriate moment in time */
-    if (notify)
-       vga_hw_update();
-
-    (*s->cirrus_rop) (s, s->vga.vram_ptr +
-                     (s->cirrus_blt_dstaddr & s->cirrus_addr_mask),
-                     s->vga.vram_ptr +
-                     (s->cirrus_blt_srcaddr & s->cirrus_addr_mask),
-                     s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch,
-                     s->cirrus_blt_width, s->cirrus_blt_height);
-
-    if (notify) {
-        qemu_console_copy(s->vga.con,
-                         sx, sy, dx, dy,
-                         s->cirrus_blt_width / depth,
-                         s->cirrus_blt_height);
-    }
-
-    /* we don't have to notify the display that this portion has
-       changed since qemu_console_copy implies this */
-
-    cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
-                               s->cirrus_blt_dstpitch, s->cirrus_blt_width,
-                               s->cirrus_blt_height);
-}
-
-static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s)
-{
-    if (BLTUNSAFE(s))
-        return 0;
-
-    cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->vga.start_addr,
-            s->cirrus_blt_srcaddr - s->vga.start_addr,
-            s->cirrus_blt_width, s->cirrus_blt_height);
-
-    return 1;
-}
-
-/***************************************
- *
- *  bitblt (cpu-to-video)
- *
- ***************************************/
-
-static void cirrus_bitblt_cputovideo_next(CirrusVGAState * s)
-{
-    int copy_count;
-    uint8_t *end_ptr;
-
-    if (s->cirrus_srccounter > 0) {
-        if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
-            cirrus_bitblt_common_patterncopy(s, s->cirrus_bltbuf);
-        the_end:
-            s->cirrus_srccounter = 0;
-            cirrus_bitblt_reset(s);
-        } else {
-            /* at least one scan line */
-            do {
-                (*s->cirrus_rop)(s, s->vga.vram_ptr +
-                                 (s->cirrus_blt_dstaddr & s->cirrus_addr_mask),
-                                  s->cirrus_bltbuf, 0, 0, s->cirrus_blt_width, 1);
-                cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, 0,
-                                         s->cirrus_blt_width, 1);
-                s->cirrus_blt_dstaddr += s->cirrus_blt_dstpitch;
-                s->cirrus_srccounter -= s->cirrus_blt_srcpitch;
-                if (s->cirrus_srccounter <= 0)
-                    goto the_end;
-                /* more bytes than needed can be transferred because of
-                   word alignment, so we keep them for the next line */
-                /* XXX: keep alignment to speed up transfer */
-                end_ptr = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
-                copy_count = s->cirrus_srcptr_end - end_ptr;
-                memmove(s->cirrus_bltbuf, end_ptr, copy_count);
-                s->cirrus_srcptr = s->cirrus_bltbuf + copy_count;
-                s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
-            } while (s->cirrus_srcptr >= s->cirrus_srcptr_end);
-        }
-    }
-}
-
-/***************************************
- *
- *  bitblt wrapper
- *
- ***************************************/
-
-static void cirrus_bitblt_reset(CirrusVGAState * s)
-{
-    int need_update;
-
-    s->vga.gr[0x31] &=
-       ~(CIRRUS_BLT_START | CIRRUS_BLT_BUSY | CIRRUS_BLT_FIFOUSED);
-    need_update = s->cirrus_srcptr != &s->cirrus_bltbuf[0]
-        || s->cirrus_srcptr_end != &s->cirrus_bltbuf[0];
-    s->cirrus_srcptr = &s->cirrus_bltbuf[0];
-    s->cirrus_srcptr_end = &s->cirrus_bltbuf[0];
-    s->cirrus_srccounter = 0;
-    if (!need_update)
-        return;
-    cirrus_update_memory_access(s);
-}
-
-static int cirrus_bitblt_cputovideo(CirrusVGAState * s)
-{
-    int w;
-
-    s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_MEMSYSSRC;
-    s->cirrus_srcptr = &s->cirrus_bltbuf[0];
-    s->cirrus_srcptr_end = &s->cirrus_bltbuf[0];
-
-    if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
-       if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
-           s->cirrus_blt_srcpitch = 8;
-       } else {
-            /* XXX: check for 24 bpp */
-           s->cirrus_blt_srcpitch = 8 * 8 * s->cirrus_blt_pixelwidth;
-       }
-       s->cirrus_srccounter = s->cirrus_blt_srcpitch;
-    } else {
-       if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
-            w = s->cirrus_blt_width / s->cirrus_blt_pixelwidth;
-            if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_DWORDGRANULARITY)
-                s->cirrus_blt_srcpitch = ((w + 31) >> 5);
-            else
-                s->cirrus_blt_srcpitch = ((w + 7) >> 3);
-       } else {
-            /* always align input size to 32 bits */
-           s->cirrus_blt_srcpitch = (s->cirrus_blt_width + 3) & ~3;
-       }
-        s->cirrus_srccounter = s->cirrus_blt_srcpitch * s->cirrus_blt_height;
-    }
-    s->cirrus_srcptr = s->cirrus_bltbuf;
-    s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
-    cirrus_update_memory_access(s);
-    return 1;
-}
-
-static int cirrus_bitblt_videotocpu(CirrusVGAState * s)
-{
-    /* XXX */
-#ifdef DEBUG_BITBLT
-    printf("cirrus: bitblt (video to cpu) is not implemented yet\n");
-#endif
-    return 0;
-}
-
-static int cirrus_bitblt_videotovideo(CirrusVGAState * s)
-{
-    int ret;
-
-    if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
-       ret = cirrus_bitblt_videotovideo_patterncopy(s);
-    } else {
-       ret = cirrus_bitblt_videotovideo_copy(s);
-    }
-    if (ret)
-       cirrus_bitblt_reset(s);
-    return ret;
-}
-
-static void cirrus_bitblt_start(CirrusVGAState * s)
-{
-    uint8_t blt_rop;
-
-    s->vga.gr[0x31] |= CIRRUS_BLT_BUSY;
-
-    s->cirrus_blt_width = (s->vga.gr[0x20] | (s->vga.gr[0x21] << 8)) + 1;
-    s->cirrus_blt_height = (s->vga.gr[0x22] | (s->vga.gr[0x23] << 8)) + 1;
-    s->cirrus_blt_dstpitch = (s->vga.gr[0x24] | (s->vga.gr[0x25] << 8));
-    s->cirrus_blt_srcpitch = (s->vga.gr[0x26] | (s->vga.gr[0x27] << 8));
-    s->cirrus_blt_dstaddr =
-       (s->vga.gr[0x28] | (s->vga.gr[0x29] << 8) | (s->vga.gr[0x2a] << 16));
-    s->cirrus_blt_srcaddr =
-       (s->vga.gr[0x2c] | (s->vga.gr[0x2d] << 8) | (s->vga.gr[0x2e] << 16));
-    s->cirrus_blt_mode = s->vga.gr[0x30];
-    s->cirrus_blt_modeext = s->vga.gr[0x33];
-    blt_rop = s->vga.gr[0x32];
-
-#ifdef DEBUG_BITBLT
-    printf("rop=0x%02x mode=0x%02x modeext=0x%02x w=%d h=%d dpitch=%d spitch=%d daddr=0x%08x saddr=0x%08x writemask=0x%02x\n",
-           blt_rop,
-           s->cirrus_blt_mode,
-           s->cirrus_blt_modeext,
-           s->cirrus_blt_width,
-           s->cirrus_blt_height,
-           s->cirrus_blt_dstpitch,
-           s->cirrus_blt_srcpitch,
-           s->cirrus_blt_dstaddr,
-           s->cirrus_blt_srcaddr,
-           s->vga.gr[0x2f]);
-#endif
-
-    switch (s->cirrus_blt_mode & CIRRUS_BLTMODE_PIXELWIDTHMASK) {
-    case CIRRUS_BLTMODE_PIXELWIDTH8:
-       s->cirrus_blt_pixelwidth = 1;
-       break;
-    case CIRRUS_BLTMODE_PIXELWIDTH16:
-       s->cirrus_blt_pixelwidth = 2;
-       break;
-    case CIRRUS_BLTMODE_PIXELWIDTH24:
-       s->cirrus_blt_pixelwidth = 3;
-       break;
-    case CIRRUS_BLTMODE_PIXELWIDTH32:
-       s->cirrus_blt_pixelwidth = 4;
-       break;
-    default:
-#ifdef DEBUG_BITBLT
-       printf("cirrus: bitblt - pixel width is unknown\n");
-#endif
-       goto bitblt_ignore;
-    }
-    s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_PIXELWIDTHMASK;
-
-    if ((s->
-        cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSSRC |
-                           CIRRUS_BLTMODE_MEMSYSDEST))
-       == (CIRRUS_BLTMODE_MEMSYSSRC | CIRRUS_BLTMODE_MEMSYSDEST)) {
-#ifdef DEBUG_BITBLT
-       printf("cirrus: bitblt - memory-to-memory copy is requested\n");
-#endif
-       goto bitblt_ignore;
-    }
-
-    if ((s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_SOLIDFILL) &&
-        (s->cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSDEST |
-                               CIRRUS_BLTMODE_TRANSPARENTCOMP |
-                               CIRRUS_BLTMODE_PATTERNCOPY |
-                               CIRRUS_BLTMODE_COLOREXPAND)) ==
-         (CIRRUS_BLTMODE_PATTERNCOPY | CIRRUS_BLTMODE_COLOREXPAND)) {
-        cirrus_bitblt_fgcol(s);
-        cirrus_bitblt_solidfill(s, blt_rop);
-    } else {
-        if ((s->cirrus_blt_mode & (CIRRUS_BLTMODE_COLOREXPAND |
-                                   CIRRUS_BLTMODE_PATTERNCOPY)) ==
-            CIRRUS_BLTMODE_COLOREXPAND) {
-
-            if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) {
-                if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV)
-                    cirrus_bitblt_bgcol(s);
-                else
-                    cirrus_bitblt_fgcol(s);
-                s->cirrus_rop = cirrus_colorexpand_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
-            } else {
-                cirrus_bitblt_fgcol(s);
-                cirrus_bitblt_bgcol(s);
-                s->cirrus_rop = cirrus_colorexpand[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
-            }
-        } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
-            if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
-                if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) {
-                    if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV)
-                        cirrus_bitblt_bgcol(s);
-                    else
-                        cirrus_bitblt_fgcol(s);
-                    s->cirrus_rop = cirrus_colorexpand_pattern_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
-                } else {
-                    cirrus_bitblt_fgcol(s);
-                    cirrus_bitblt_bgcol(s);
-                    s->cirrus_rop = cirrus_colorexpand_pattern[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
-                }
-            } else {
-                s->cirrus_rop = cirrus_patternfill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
-            }
-        } else {
-           if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) {
-               if (s->cirrus_blt_pixelwidth > 2) {
-                   printf("src transparent without colorexpand must be 8bpp or 16bpp\n");
-                   goto bitblt_ignore;
-               }
-               if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) {
-                   s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch;
-                   s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch;
-                   s->cirrus_rop = cirrus_bkwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
-               } else {
-                   s->cirrus_rop = cirrus_fwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
-               }
-           } else {
-               if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) {
-                   s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch;
-                   s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch;
-                   s->cirrus_rop = cirrus_bkwd_rop[rop_to_index[blt_rop]];
-               } else {
-                   s->cirrus_rop = cirrus_fwd_rop[rop_to_index[blt_rop]];
-               }
-           }
-       }
-        // setup bitblt engine.
-        if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSSRC) {
-            if (!cirrus_bitblt_cputovideo(s))
-                goto bitblt_ignore;
-        } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSDEST) {
-            if (!cirrus_bitblt_videotocpu(s))
-                goto bitblt_ignore;
-        } else {
-            if (!cirrus_bitblt_videotovideo(s))
-                goto bitblt_ignore;
-        }
-    }
-    return;
-  bitblt_ignore:;
-    cirrus_bitblt_reset(s);
-}
-
-static void cirrus_write_bitblt(CirrusVGAState * s, unsigned reg_value)
-{
-    unsigned old_value;
-
-    old_value = s->vga.gr[0x31];
-    s->vga.gr[0x31] = reg_value;
-
-    if (((old_value & CIRRUS_BLT_RESET) != 0) &&
-       ((reg_value & CIRRUS_BLT_RESET) == 0)) {
-       cirrus_bitblt_reset(s);
-    } else if (((old_value & CIRRUS_BLT_START) == 0) &&
-              ((reg_value & CIRRUS_BLT_START) != 0)) {
-       cirrus_bitblt_start(s);
-    }
-}
-
-
-/***************************************
- *
- *  basic parameters
- *
- ***************************************/
-
-static void cirrus_get_offsets(VGACommonState *s1,
-                               uint32_t *pline_offset,
-                               uint32_t *pstart_addr,
-                               uint32_t *pline_compare)
-{
-    CirrusVGAState * s = container_of(s1, CirrusVGAState, vga);
-    uint32_t start_addr, line_offset, line_compare;
-
-    line_offset = s->vga.cr[0x13]
-       | ((s->vga.cr[0x1b] & 0x10) << 4);
-    line_offset <<= 3;
-    *pline_offset = line_offset;
-
-    start_addr = (s->vga.cr[0x0c] << 8)
-       | s->vga.cr[0x0d]
-       | ((s->vga.cr[0x1b] & 0x01) << 16)
-       | ((s->vga.cr[0x1b] & 0x0c) << 15)
-       | ((s->vga.cr[0x1d] & 0x80) << 12);
-    *pstart_addr = start_addr;
-
-    line_compare = s->vga.cr[0x18] |
-        ((s->vga.cr[0x07] & 0x10) << 4) |
-        ((s->vga.cr[0x09] & 0x40) << 3);
-    *pline_compare = line_compare;
-}
-
-static uint32_t cirrus_get_bpp16_depth(CirrusVGAState * s)
-{
-    uint32_t ret = 16;
-
-    switch (s->cirrus_hidden_dac_data & 0xf) {
-    case 0:
-       ret = 15;
-       break;                  /* Sierra HiColor */
-    case 1:
-       ret = 16;
-       break;                  /* XGA HiColor */
-    default:
-#ifdef DEBUG_CIRRUS
-       printf("cirrus: invalid DAC value %x in 16bpp\n",
-              (s->cirrus_hidden_dac_data & 0xf));
-#endif
-       ret = 15;               /* XXX */
-       break;
-    }
-    return ret;
-}
-
-static int cirrus_get_bpp(VGACommonState *s1)
-{
-    CirrusVGAState * s = container_of(s1, CirrusVGAState, vga);
-    uint32_t ret = 8;
-
-    if ((s->vga.sr[0x07] & 0x01) != 0) {
-       /* Cirrus SVGA */
-       switch (s->vga.sr[0x07] & CIRRUS_SR7_BPP_MASK) {
-       case CIRRUS_SR7_BPP_8:
-           ret = 8;
-           break;
-       case CIRRUS_SR7_BPP_16_DOUBLEVCLK:
-           ret = cirrus_get_bpp16_depth(s);
-           break;
-       case CIRRUS_SR7_BPP_24:
-           ret = 24;
-           break;
-       case CIRRUS_SR7_BPP_16:
-           ret = cirrus_get_bpp16_depth(s);
-           break;
-       case CIRRUS_SR7_BPP_32:
-           ret = 32;
-           break;
-       default:
-#ifdef DEBUG_CIRRUS
-           printf("cirrus: unknown bpp - sr7=%x\n", s->vga.sr[0x7]);
-#endif
-           ret = 8;
-           break;
-       }
-    } else {
-       /* VGA */
-       ret = 0;
-    }
-
-    return ret;
-}
-
-static void cirrus_get_resolution(VGACommonState *s, int *pwidth, int *pheight)
-{
-    int width, height;
-
-    width = (s->cr[0x01] + 1) * 8;
-    height = s->cr[0x12] |
-        ((s->cr[0x07] & 0x02) << 7) |
-        ((s->cr[0x07] & 0x40) << 3);
-    height = (height + 1);
-    /* interlace support */
-    if (s->cr[0x1a] & 0x01)
-        height = height * 2;
-    *pwidth = width;
-    *pheight = height;
-}
-
-/***************************************
- *
- * bank memory
- *
- ***************************************/
-
-static void cirrus_update_bank_ptr(CirrusVGAState * s, unsigned bank_index)
-{
-    unsigned offset;
-    unsigned limit;
-
-    if ((s->vga.gr[0x0b] & 0x01) != 0) /* dual bank */
-       offset = s->vga.gr[0x09 + bank_index];
-    else                       /* single bank */
-       offset = s->vga.gr[0x09];
-
-    if ((s->vga.gr[0x0b] & 0x20) != 0)
-       offset <<= 14;
-    else
-       offset <<= 12;
-
-    if (s->real_vram_size <= offset)
-       limit = 0;
-    else
-       limit = s->real_vram_size - offset;
-
-    if (((s->vga.gr[0x0b] & 0x01) == 0) && (bank_index != 0)) {
-       if (limit > 0x8000) {
-           offset += 0x8000;
-           limit -= 0x8000;
-       } else {
-           limit = 0;
-       }
-    }
-
-    if (limit > 0) {
-       s->cirrus_bank_base[bank_index] = offset;
-       s->cirrus_bank_limit[bank_index] = limit;
-    } else {
-       s->cirrus_bank_base[bank_index] = 0;
-       s->cirrus_bank_limit[bank_index] = 0;
-    }
-}
-
-/***************************************
- *
- *  I/O access between 0x3c4-0x3c5
- *
- ***************************************/
-
-static int cirrus_vga_read_sr(CirrusVGAState * s)
-{
-    switch (s->vga.sr_index) {
-    case 0x00:                 // Standard VGA
-    case 0x01:                 // Standard VGA
-    case 0x02:                 // Standard VGA
-    case 0x03:                 // Standard VGA
-    case 0x04:                 // Standard VGA
-       return s->vga.sr[s->vga.sr_index];
-    case 0x06:                 // Unlock Cirrus extensions
-       return s->vga.sr[s->vga.sr_index];
-    case 0x10:
-    case 0x30:
-    case 0x50:
-    case 0x70:                 // Graphics Cursor X
-    case 0x90:
-    case 0xb0:
-    case 0xd0:
-    case 0xf0:                 // Graphics Cursor X
-       return s->vga.sr[0x10];
-    case 0x11:
-    case 0x31:
-    case 0x51:
-    case 0x71:                 // Graphics Cursor Y
-    case 0x91:
-    case 0xb1:
-    case 0xd1:
-    case 0xf1:                 // Graphics Cursor Y
-       return s->vga.sr[0x11];
-    case 0x05:                 // ???
-    case 0x07:                 // Extended Sequencer Mode
-    case 0x08:                 // EEPROM Control
-    case 0x09:                 // Scratch Register 0
-    case 0x0a:                 // Scratch Register 1
-    case 0x0b:                 // VCLK 0
-    case 0x0c:                 // VCLK 1
-    case 0x0d:                 // VCLK 2
-    case 0x0e:                 // VCLK 3
-    case 0x0f:                 // DRAM Control
-    case 0x12:                 // Graphics Cursor Attribute
-    case 0x13:                 // Graphics Cursor Pattern Address
-    case 0x14:                 // Scratch Register 2
-    case 0x15:                 // Scratch Register 3
-    case 0x16:                 // Performance Tuning Register
-    case 0x17:                 // Configuration Readback and Extended Control
-    case 0x18:                 // Signature Generator Control
-    case 0x19:                 // Signal Generator Result
-    case 0x1a:                 // Signal Generator Result
-    case 0x1b:                 // VCLK 0 Denominator & Post
-    case 0x1c:                 // VCLK 1 Denominator & Post
-    case 0x1d:                 // VCLK 2 Denominator & Post
-    case 0x1e:                 // VCLK 3 Denominator & Post
-    case 0x1f:                 // BIOS Write Enable and MCLK select
-#ifdef DEBUG_CIRRUS
-       printf("cirrus: handled inport sr_index %02x\n", s->vga.sr_index);
-#endif
-       return s->vga.sr[s->vga.sr_index];
-    default:
-#ifdef DEBUG_CIRRUS
-       printf("cirrus: inport sr_index %02x\n", s->vga.sr_index);
-#endif
-       return 0xff;
-       break;
-    }
-}
-
-static void cirrus_vga_write_sr(CirrusVGAState * s, uint32_t val)
-{
-    switch (s->vga.sr_index) {
-    case 0x00:                 // Standard VGA
-    case 0x01:                 // Standard VGA
-    case 0x02:                 // Standard VGA
-    case 0x03:                 // Standard VGA
-    case 0x04:                 // Standard VGA
-       s->vga.sr[s->vga.sr_index] = val & sr_mask[s->vga.sr_index];
-       if (s->vga.sr_index == 1)
-            s->vga.update_retrace_info(&s->vga);
-        break;
-    case 0x06:                 // Unlock Cirrus extensions
-       val &= 0x17;
-       if (val == 0x12) {
-           s->vga.sr[s->vga.sr_index] = 0x12;
-       } else {
-           s->vga.sr[s->vga.sr_index] = 0x0f;
-       }
-       break;
-    case 0x10:
-    case 0x30:
-    case 0x50:
-    case 0x70:                 // Graphics Cursor X
-    case 0x90:
-    case 0xb0:
-    case 0xd0:
-    case 0xf0:                 // Graphics Cursor X
-       s->vga.sr[0x10] = val;
-       s->hw_cursor_x = (val << 3) | (s->vga.sr_index >> 5);
-       break;
-    case 0x11:
-    case 0x31:
-    case 0x51:
-    case 0x71:                 // Graphics Cursor Y
-    case 0x91:
-    case 0xb1:
-    case 0xd1:
-    case 0xf1:                 // Graphics Cursor Y
-       s->vga.sr[0x11] = val;
-       s->hw_cursor_y = (val << 3) | (s->vga.sr_index >> 5);
-       break;
-    case 0x07:                 // Extended Sequencer Mode
-    cirrus_update_memory_access(s);
-    case 0x08:                 // EEPROM Control
-    case 0x09:                 // Scratch Register 0
-    case 0x0a:                 // Scratch Register 1
-    case 0x0b:                 // VCLK 0
-    case 0x0c:                 // VCLK 1
-    case 0x0d:                 // VCLK 2
-    case 0x0e:                 // VCLK 3
-    case 0x0f:                 // DRAM Control
-    case 0x12:                 // Graphics Cursor Attribute
-    case 0x13:                 // Graphics Cursor Pattern Address
-    case 0x14:                 // Scratch Register 2
-    case 0x15:                 // Scratch Register 3
-    case 0x16:                 // Performance Tuning Register
-    case 0x18:                 // Signature Generator Control
-    case 0x19:                 // Signature Generator Result
-    case 0x1a:                 // Signature Generator Result
-    case 0x1b:                 // VCLK 0 Denominator & Post
-    case 0x1c:                 // VCLK 1 Denominator & Post
-    case 0x1d:                 // VCLK 2 Denominator & Post
-    case 0x1e:                 // VCLK 3 Denominator & Post
-    case 0x1f:                 // BIOS Write Enable and MCLK select
-       s->vga.sr[s->vga.sr_index] = val;
-#ifdef DEBUG_CIRRUS
-       printf("cirrus: handled outport sr_index %02x, sr_value %02x\n",
-              s->vga.sr_index, val);
-#endif
-       break;
-    case 0x17:                 // Configuration Readback and Extended Control
-       s->vga.sr[s->vga.sr_index] = (s->vga.sr[s->vga.sr_index] & 0x38)
-                                   | (val & 0xc7);
-        cirrus_update_memory_access(s);
-        break;
-    default:
-#ifdef DEBUG_CIRRUS
-       printf("cirrus: outport sr_index %02x, sr_value %02x\n",
-               s->vga.sr_index, val);
-#endif
-       break;
-    }
-}
-
-/***************************************
- *
- *  I/O access at 0x3c6
- *
- ***************************************/
-
-static int cirrus_read_hidden_dac(CirrusVGAState * s)
-{
-    if (++s->cirrus_hidden_dac_lockindex == 5) {
-        s->cirrus_hidden_dac_lockindex = 0;
-        return s->cirrus_hidden_dac_data;
-    }
-    return 0xff;
-}
-
-static void cirrus_write_hidden_dac(CirrusVGAState * s, int reg_value)
-{
-    if (s->cirrus_hidden_dac_lockindex == 4) {
-       s->cirrus_hidden_dac_data = reg_value;
-#if defined(DEBUG_CIRRUS)
-       printf("cirrus: outport hidden DAC, value %02x\n", reg_value);
-#endif
-    }
-    s->cirrus_hidden_dac_lockindex = 0;
-}
-
-/***************************************
- *
- *  I/O access at 0x3c9
- *
- ***************************************/
-
-static int cirrus_vga_read_palette(CirrusVGAState * s)
-{
-    int val;
-
-    if ((s->vga.sr[0x12] & CIRRUS_CURSOR_HIDDENPEL)) {
-        val = s->cirrus_hidden_palette[(s->vga.dac_read_index & 0x0f) * 3 +
-                                       s->vga.dac_sub_index];
-    } else {
-        val = s->vga.palette[s->vga.dac_read_index * 3 + s->vga.dac_sub_index];
-    }
-    if (++s->vga.dac_sub_index == 3) {
-       s->vga.dac_sub_index = 0;
-       s->vga.dac_read_index++;
-    }
-    return val;
-}
-
-static void cirrus_vga_write_palette(CirrusVGAState * s, int reg_value)
-{
-    s->vga.dac_cache[s->vga.dac_sub_index] = reg_value;
-    if (++s->vga.dac_sub_index == 3) {
-        if ((s->vga.sr[0x12] & CIRRUS_CURSOR_HIDDENPEL)) {
-            memcpy(&s->cirrus_hidden_palette[(s->vga.dac_write_index & 0x0f) * 3],
-                   s->vga.dac_cache, 3);
-        } else {
-            memcpy(&s->vga.palette[s->vga.dac_write_index * 3], s->vga.dac_cache, 3);
-        }
-        /* XXX update cursor */
-       s->vga.dac_sub_index = 0;
-       s->vga.dac_write_index++;
-    }
-}
-
-/***************************************
- *
- *  I/O access between 0x3ce-0x3cf
- *
- ***************************************/
-
-static int cirrus_vga_read_gr(CirrusVGAState * s, unsigned reg_index)
-{
-    switch (reg_index) {
-    case 0x00: // Standard VGA, BGCOLOR 0x000000ff
-        return s->cirrus_shadow_gr0;
-    case 0x01: // Standard VGA, FGCOLOR 0x000000ff
-        return s->cirrus_shadow_gr1;
-    case 0x02:                 // Standard VGA
-    case 0x03:                 // Standard VGA
-    case 0x04:                 // Standard VGA
-    case 0x06:                 // Standard VGA
-    case 0x07:                 // Standard VGA
-    case 0x08:                 // Standard VGA
-        return s->vga.gr[s->vga.gr_index];
-    case 0x05:                 // Standard VGA, Cirrus extended mode
-    default:
-       break;
-    }
-
-    if (reg_index < 0x3a) {
-       return s->vga.gr[reg_index];
-    } else {
-#ifdef DEBUG_CIRRUS
-       printf("cirrus: inport gr_index %02x\n", reg_index);
-#endif
-       return 0xff;
-    }
-}
-
-static void
-cirrus_vga_write_gr(CirrusVGAState * s, unsigned reg_index, int reg_value)
-{
-#if defined(DEBUG_BITBLT) && 0
-    printf("gr%02x: %02x\n", reg_index, reg_value);
-#endif
-    switch (reg_index) {
-    case 0x00:                 // Standard VGA, BGCOLOR 0x000000ff
-       s->vga.gr[reg_index] = reg_value & gr_mask[reg_index];
-       s->cirrus_shadow_gr0 = reg_value;
-       break;
-    case 0x01:                 // Standard VGA, FGCOLOR 0x000000ff
-       s->vga.gr[reg_index] = reg_value & gr_mask[reg_index];
-       s->cirrus_shadow_gr1 = reg_value;
-       break;
-    case 0x02:                 // Standard VGA
-    case 0x03:                 // Standard VGA
-    case 0x04:                 // Standard VGA
-    case 0x06:                 // Standard VGA
-    case 0x07:                 // Standard VGA
-    case 0x08:                 // Standard VGA
-       s->vga.gr[reg_index] = reg_value & gr_mask[reg_index];
-        break;
-    case 0x05:                 // Standard VGA, Cirrus extended mode
-       s->vga.gr[reg_index] = reg_value & 0x7f;
-        cirrus_update_memory_access(s);
-       break;
-    case 0x09:                 // bank offset #0
-    case 0x0A:                 // bank offset #1
-       s->vga.gr[reg_index] = reg_value;
-       cirrus_update_bank_ptr(s, 0);
-       cirrus_update_bank_ptr(s, 1);
-        cirrus_update_memory_access(s);
-        break;
-    case 0x0B:
-       s->vga.gr[reg_index] = reg_value;
-       cirrus_update_bank_ptr(s, 0);
-       cirrus_update_bank_ptr(s, 1);
-        cirrus_update_memory_access(s);
-       break;
-    case 0x10:                 // BGCOLOR 0x0000ff00
-    case 0x11:                 // FGCOLOR 0x0000ff00
-    case 0x12:                 // BGCOLOR 0x00ff0000
-    case 0x13:                 // FGCOLOR 0x00ff0000
-    case 0x14:                 // BGCOLOR 0xff000000
-    case 0x15:                 // FGCOLOR 0xff000000
-    case 0x20:                 // BLT WIDTH 0x0000ff
-    case 0x22:                 // BLT HEIGHT 0x0000ff
-    case 0x24:                 // BLT DEST PITCH 0x0000ff
-    case 0x26:                 // BLT SRC PITCH 0x0000ff
-    case 0x28:                 // BLT DEST ADDR 0x0000ff
-    case 0x29:                 // BLT DEST ADDR 0x00ff00
-    case 0x2c:                 // BLT SRC ADDR 0x0000ff
-    case 0x2d:                 // BLT SRC ADDR 0x00ff00
-    case 0x2f:                  // BLT WRITEMASK
-    case 0x30:                 // BLT MODE
-    case 0x32:                 // RASTER OP
-    case 0x33:                 // BLT MODEEXT
-    case 0x34:                 // BLT TRANSPARENT COLOR 0x00ff
-    case 0x35:                 // BLT TRANSPARENT COLOR 0xff00
-    case 0x38:                 // BLT TRANSPARENT COLOR MASK 0x00ff
-    case 0x39:                 // BLT TRANSPARENT COLOR MASK 0xff00
-       s->vga.gr[reg_index] = reg_value;
-       break;
-    case 0x21:                 // BLT WIDTH 0x001f00
-    case 0x23:                 // BLT HEIGHT 0x001f00
-    case 0x25:                 // BLT DEST PITCH 0x001f00
-    case 0x27:                 // BLT SRC PITCH 0x001f00
-       s->vga.gr[reg_index] = reg_value & 0x1f;
-       break;
-    case 0x2a:                 // BLT DEST ADDR 0x3f0000
-       s->vga.gr[reg_index] = reg_value & 0x3f;
-        /* if auto start mode, starts bit blt now */
-        if (s->vga.gr[0x31] & CIRRUS_BLT_AUTOSTART) {
-            cirrus_bitblt_start(s);
-        }
-       break;
-    case 0x2e:                 // BLT SRC ADDR 0x3f0000
-       s->vga.gr[reg_index] = reg_value & 0x3f;
-       break;
-    case 0x31:                 // BLT STATUS/START
-       cirrus_write_bitblt(s, reg_value);
-       break;
-    default:
-#ifdef DEBUG_CIRRUS
-       printf("cirrus: outport gr_index %02x, gr_value %02x\n", reg_index,
-              reg_value);
-#endif
-       break;
-    }
-}
-
-/***************************************
- *
- *  I/O access between 0x3d4-0x3d5
- *
- ***************************************/
-
-static int cirrus_vga_read_cr(CirrusVGAState * s, unsigned reg_index)
-{
-    switch (reg_index) {
-    case 0x00:                 // Standard VGA
-    case 0x01:                 // Standard VGA
-    case 0x02:                 // Standard VGA
-    case 0x03:                 // Standard VGA
-    case 0x04:                 // Standard VGA
-    case 0x05:                 // Standard VGA
-    case 0x06:                 // Standard VGA
-    case 0x07:                 // Standard VGA
-    case 0x08:                 // Standard VGA
-    case 0x09:                 // Standard VGA
-    case 0x0a:                 // Standard VGA
-    case 0x0b:                 // Standard VGA
-    case 0x0c:                 // Standard VGA
-    case 0x0d:                 // Standard VGA
-    case 0x0e:                 // Standard VGA
-    case 0x0f:                 // Standard VGA
-    case 0x10:                 // Standard VGA
-    case 0x11:                 // Standard VGA
-    case 0x12:                 // Standard VGA
-    case 0x13:                 // Standard VGA
-    case 0x14:                 // Standard VGA
-    case 0x15:                 // Standard VGA
-    case 0x16:                 // Standard VGA
-    case 0x17:                 // Standard VGA
-    case 0x18:                 // Standard VGA
-       return s->vga.cr[s->vga.cr_index];
-    case 0x24:                 // Attribute Controller Toggle Readback (R)
-        return (s->vga.ar_flip_flop << 7);
-    case 0x19:                 // Interlace End
-    case 0x1a:                 // Miscellaneous Control
-    case 0x1b:                 // Extended Display Control
-    case 0x1c:                 // Sync Adjust and Genlock
-    case 0x1d:                 // Overlay Extended Control
-    case 0x22:                 // Graphics Data Latches Readback (R)
-    case 0x25:                 // Part Status
-    case 0x27:                 // Part ID (R)
-       return s->vga.cr[s->vga.cr_index];
-    case 0x26:                 // Attribute Controller Index Readback (R)
-       return s->vga.ar_index & 0x3f;
-       break;
-    default:
-#ifdef DEBUG_CIRRUS
-       printf("cirrus: inport cr_index %02x\n", reg_index);
-#endif
-       return 0xff;
-    }
-}
-
-static void cirrus_vga_write_cr(CirrusVGAState * s, int reg_value)
-{
-    switch (s->vga.cr_index) {
-    case 0x00:                 // Standard VGA
-    case 0x01:                 // Standard VGA
-    case 0x02:                 // Standard VGA
-    case 0x03:                 // Standard VGA
-    case 0x04:                 // Standard VGA
-    case 0x05:                 // Standard VGA
-    case 0x06:                 // Standard VGA
-    case 0x07:                 // Standard VGA
-    case 0x08:                 // Standard VGA
-    case 0x09:                 // Standard VGA
-    case 0x0a:                 // Standard VGA
-    case 0x0b:                 // Standard VGA
-    case 0x0c:                 // Standard VGA
-    case 0x0d:                 // Standard VGA
-    case 0x0e:                 // Standard VGA
-    case 0x0f:                 // Standard VGA
-    case 0x10:                 // Standard VGA
-    case 0x11:                 // Standard VGA
-    case 0x12:                 // Standard VGA
-    case 0x13:                 // Standard VGA
-    case 0x14:                 // Standard VGA
-    case 0x15:                 // Standard VGA
-    case 0x16:                 // Standard VGA
-    case 0x17:                 // Standard VGA
-    case 0x18:                 // Standard VGA
-       /* handle CR0-7 protection */
-       if ((s->vga.cr[0x11] & 0x80) && s->vga.cr_index <= 7) {
-           /* can always write bit 4 of CR7 */
-           if (s->vga.cr_index == 7)
-               s->vga.cr[7] = (s->vga.cr[7] & ~0x10) | (reg_value & 0x10);
-           return;
-       }
-       s->vga.cr[s->vga.cr_index] = reg_value;
-       switch(s->vga.cr_index) {
-       case 0x00:
-       case 0x04:
-       case 0x05:
-       case 0x06:
-       case 0x07:
-       case 0x11:
-       case 0x17:
-           s->vga.update_retrace_info(&s->vga);
-           break;
-       }
-        break;
-    case 0x19:                 // Interlace End
-    case 0x1a:                 // Miscellaneous Control
-    case 0x1b:                 // Extended Display Control
-    case 0x1c:                 // Sync Adjust and Genlock
-    case 0x1d:                 // Overlay Extended Control
-       s->vga.cr[s->vga.cr_index] = reg_value;
-#ifdef DEBUG_CIRRUS
-       printf("cirrus: handled outport cr_index %02x, cr_value %02x\n",
-              s->vga.cr_index, reg_value);
-#endif
-       break;
-    case 0x22:                 // Graphics Data Latches Readback (R)
-    case 0x24:                 // Attribute Controller Toggle Readback (R)
-    case 0x26:                 // Attribute Controller Index Readback (R)
-    case 0x27:                 // Part ID (R)
-       break;
-    case 0x25:                 // Part Status
-    default:
-#ifdef DEBUG_CIRRUS
-       printf("cirrus: outport cr_index %02x, cr_value %02x\n",
-               s->vga.cr_index, reg_value);
-#endif
-       break;
-    }
-}
-
-/***************************************
- *
- *  memory-mapped I/O (bitblt)
- *
- ***************************************/
-
-static uint8_t cirrus_mmio_blt_read(CirrusVGAState * s, unsigned address)
-{
-    int value = 0xff;
-
-    switch (address) {
-    case (CIRRUS_MMIO_BLTBGCOLOR + 0):
-       value = cirrus_vga_read_gr(s, 0x00);
-       break;
-    case (CIRRUS_MMIO_BLTBGCOLOR + 1):
-       value = cirrus_vga_read_gr(s, 0x10);
-       break;
-    case (CIRRUS_MMIO_BLTBGCOLOR + 2):
-       value = cirrus_vga_read_gr(s, 0x12);
-       break;
-    case (CIRRUS_MMIO_BLTBGCOLOR + 3):
-       value = cirrus_vga_read_gr(s, 0x14);
-       break;
-    case (CIRRUS_MMIO_BLTFGCOLOR + 0):
-       value = cirrus_vga_read_gr(s, 0x01);
-       break;
-    case (CIRRUS_MMIO_BLTFGCOLOR + 1):
-       value = cirrus_vga_read_gr(s, 0x11);
-       break;
-    case (CIRRUS_MMIO_BLTFGCOLOR + 2):
-       value = cirrus_vga_read_gr(s, 0x13);
-       break;
-    case (CIRRUS_MMIO_BLTFGCOLOR + 3):
-       value = cirrus_vga_read_gr(s, 0x15);
-       break;
-    case (CIRRUS_MMIO_BLTWIDTH + 0):
-       value = cirrus_vga_read_gr(s, 0x20);
-       break;
-    case (CIRRUS_MMIO_BLTWIDTH + 1):
-       value = cirrus_vga_read_gr(s, 0x21);
-       break;
-    case (CIRRUS_MMIO_BLTHEIGHT + 0):
-       value = cirrus_vga_read_gr(s, 0x22);
-       break;
-    case (CIRRUS_MMIO_BLTHEIGHT + 1):
-       value = cirrus_vga_read_gr(s, 0x23);
-       break;
-    case (CIRRUS_MMIO_BLTDESTPITCH + 0):
-       value = cirrus_vga_read_gr(s, 0x24);
-       break;
-    case (CIRRUS_MMIO_BLTDESTPITCH + 1):
-       value = cirrus_vga_read_gr(s, 0x25);
-       break;
-    case (CIRRUS_MMIO_BLTSRCPITCH + 0):
-       value = cirrus_vga_read_gr(s, 0x26);
-       break;
-    case (CIRRUS_MMIO_BLTSRCPITCH + 1):
-       value = cirrus_vga_read_gr(s, 0x27);
-       break;
-    case (CIRRUS_MMIO_BLTDESTADDR + 0):
-       value = cirrus_vga_read_gr(s, 0x28);
-       break;
-    case (CIRRUS_MMIO_BLTDESTADDR + 1):
-       value = cirrus_vga_read_gr(s, 0x29);
-       break;
-    case (CIRRUS_MMIO_BLTDESTADDR + 2):
-       value = cirrus_vga_read_gr(s, 0x2a);
-       break;
-    case (CIRRUS_MMIO_BLTSRCADDR + 0):
-       value = cirrus_vga_read_gr(s, 0x2c);
-       break;
-    case (CIRRUS_MMIO_BLTSRCADDR + 1):
-       value = cirrus_vga_read_gr(s, 0x2d);
-       break;
-    case (CIRRUS_MMIO_BLTSRCADDR + 2):
-       value = cirrus_vga_read_gr(s, 0x2e);
-       break;
-    case CIRRUS_MMIO_BLTWRITEMASK:
-       value = cirrus_vga_read_gr(s, 0x2f);
-       break;
-    case CIRRUS_MMIO_BLTMODE:
-       value = cirrus_vga_read_gr(s, 0x30);
-       break;
-    case CIRRUS_MMIO_BLTROP:
-       value = cirrus_vga_read_gr(s, 0x32);
-       break;
-    case CIRRUS_MMIO_BLTMODEEXT:
-       value = cirrus_vga_read_gr(s, 0x33);
-       break;
-    case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0):
-       value = cirrus_vga_read_gr(s, 0x34);
-       break;
-    case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1):
-       value = cirrus_vga_read_gr(s, 0x35);
-       break;
-    case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0):
-       value = cirrus_vga_read_gr(s, 0x38);
-       break;
-    case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1):
-       value = cirrus_vga_read_gr(s, 0x39);
-       break;
-    case CIRRUS_MMIO_BLTSTATUS:
-       value = cirrus_vga_read_gr(s, 0x31);
-       break;
-    default:
-#ifdef DEBUG_CIRRUS
-       printf("cirrus: mmio read - address 0x%04x\n", address);
-#endif
-       break;
-    }
-
-    return (uint8_t) value;
-}
-
-static void cirrus_mmio_blt_write(CirrusVGAState * s, unsigned address,
-                                 uint8_t value)
-{
-    switch (address) {
-    case (CIRRUS_MMIO_BLTBGCOLOR + 0):
-       cirrus_vga_write_gr(s, 0x00, value);
-       break;
-    case (CIRRUS_MMIO_BLTBGCOLOR + 1):
-       cirrus_vga_write_gr(s, 0x10, value);
-       break;
-    case (CIRRUS_MMIO_BLTBGCOLOR + 2):
-       cirrus_vga_write_gr(s, 0x12, value);
-       break;
-    case (CIRRUS_MMIO_BLTBGCOLOR + 3):
-       cirrus_vga_write_gr(s, 0x14, value);
-       break;
-    case (CIRRUS_MMIO_BLTFGCOLOR + 0):
-       cirrus_vga_write_gr(s, 0x01, value);
-       break;
-    case (CIRRUS_MMIO_BLTFGCOLOR + 1):
-       cirrus_vga_write_gr(s, 0x11, value);
-       break;
-    case (CIRRUS_MMIO_BLTFGCOLOR + 2):
-       cirrus_vga_write_gr(s, 0x13, value);
-       break;
-    case (CIRRUS_MMIO_BLTFGCOLOR + 3):
-       cirrus_vga_write_gr(s, 0x15, value);
-       break;
-    case (CIRRUS_MMIO_BLTWIDTH + 0):
-       cirrus_vga_write_gr(s, 0x20, value);
-       break;
-    case (CIRRUS_MMIO_BLTWIDTH + 1):
-       cirrus_vga_write_gr(s, 0x21, value);
-       break;
-    case (CIRRUS_MMIO_BLTHEIGHT + 0):
-       cirrus_vga_write_gr(s, 0x22, value);
-       break;
-    case (CIRRUS_MMIO_BLTHEIGHT + 1):
-       cirrus_vga_write_gr(s, 0x23, value);
-       break;
-    case (CIRRUS_MMIO_BLTDESTPITCH + 0):
-       cirrus_vga_write_gr(s, 0x24, value);
-       break;
-    case (CIRRUS_MMIO_BLTDESTPITCH + 1):
-       cirrus_vga_write_gr(s, 0x25, value);
-       break;
-    case (CIRRUS_MMIO_BLTSRCPITCH + 0):
-       cirrus_vga_write_gr(s, 0x26, value);
-       break;
-    case (CIRRUS_MMIO_BLTSRCPITCH + 1):
-       cirrus_vga_write_gr(s, 0x27, value);
-       break;
-    case (CIRRUS_MMIO_BLTDESTADDR + 0):
-       cirrus_vga_write_gr(s, 0x28, value);
-       break;
-    case (CIRRUS_MMIO_BLTDESTADDR + 1):
-       cirrus_vga_write_gr(s, 0x29, value);
-       break;
-    case (CIRRUS_MMIO_BLTDESTADDR + 2):
-       cirrus_vga_write_gr(s, 0x2a, value);
-       break;
-    case (CIRRUS_MMIO_BLTDESTADDR + 3):
-       /* ignored */
-       break;
-    case (CIRRUS_MMIO_BLTSRCADDR + 0):
-       cirrus_vga_write_gr(s, 0x2c, value);
-       break;
-    case (CIRRUS_MMIO_BLTSRCADDR + 1):
-       cirrus_vga_write_gr(s, 0x2d, value);
-       break;
-    case (CIRRUS_MMIO_BLTSRCADDR + 2):
-       cirrus_vga_write_gr(s, 0x2e, value);
-       break;
-    case CIRRUS_MMIO_BLTWRITEMASK:
-       cirrus_vga_write_gr(s, 0x2f, value);
-       break;
-    case CIRRUS_MMIO_BLTMODE:
-       cirrus_vga_write_gr(s, 0x30, value);
-       break;
-    case CIRRUS_MMIO_BLTROP:
-       cirrus_vga_write_gr(s, 0x32, value);
-       break;
-    case CIRRUS_MMIO_BLTMODEEXT:
-       cirrus_vga_write_gr(s, 0x33, value);
-       break;
-    case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0):
-       cirrus_vga_write_gr(s, 0x34, value);
-       break;
-    case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1):
-       cirrus_vga_write_gr(s, 0x35, value);
-       break;
-    case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0):
-       cirrus_vga_write_gr(s, 0x38, value);
-       break;
-    case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1):
-       cirrus_vga_write_gr(s, 0x39, value);
-       break;
-    case CIRRUS_MMIO_BLTSTATUS:
-       cirrus_vga_write_gr(s, 0x31, value);
-       break;
-    default:
-#ifdef DEBUG_CIRRUS
-       printf("cirrus: mmio write - addr 0x%04x val 0x%02x (ignored)\n",
-              address, value);
-#endif
-       break;
-    }
-}
-
-/***************************************
- *
- *  write mode 4/5
- *
- ***************************************/
-
-static void cirrus_mem_writeb_mode4and5_8bpp(CirrusVGAState * s,
-                                            unsigned mode,
-                                            unsigned offset,
-                                            uint32_t mem_value)
-{
-    int x;
-    unsigned val = mem_value;
-    uint8_t *dst;
-
-    dst = s->vga.vram_ptr + (offset &= s->cirrus_addr_mask);
-    for (x = 0; x < 8; x++) {
-       if (val & 0x80) {
-           *dst = s->cirrus_shadow_gr1;
-       } else if (mode == 5) {
-           *dst = s->cirrus_shadow_gr0;
-       }
-       val <<= 1;
-       dst++;
-    }
-    memory_region_set_dirty(&s->vga.vram, offset, 8);
-}
-
-static void cirrus_mem_writeb_mode4and5_16bpp(CirrusVGAState * s,
-                                             unsigned mode,
-                                             unsigned offset,
-                                             uint32_t mem_value)
-{
-    int x;
-    unsigned val = mem_value;
-    uint8_t *dst;
-
-    dst = s->vga.vram_ptr + (offset &= s->cirrus_addr_mask);
-    for (x = 0; x < 8; x++) {
-       if (val & 0x80) {
-           *dst = s->cirrus_shadow_gr1;
-           *(dst + 1) = s->vga.gr[0x11];
-       } else if (mode == 5) {
-           *dst = s->cirrus_shadow_gr0;
-           *(dst + 1) = s->vga.gr[0x10];
-       }
-       val <<= 1;
-       dst += 2;
-    }
-    memory_region_set_dirty(&s->vga.vram, offset, 16);
-}
-
-/***************************************
- *
- *  memory access between 0xa0000-0xbffff
- *
- ***************************************/
-
-static uint64_t cirrus_vga_mem_read(void *opaque,
-                                    hwaddr addr,
-                                    uint32_t size)
-{
-    CirrusVGAState *s = opaque;
-    unsigned bank_index;
-    unsigned bank_offset;
-    uint32_t val;
-
-    if ((s->vga.sr[0x07] & 0x01) == 0) {
-        return vga_mem_readb(&s->vga, addr);
-    }
-
-    if (addr < 0x10000) {
-       /* XXX handle bitblt */
-       /* video memory */
-       bank_index = addr >> 15;
-       bank_offset = addr & 0x7fff;
-       if (bank_offset < s->cirrus_bank_limit[bank_index]) {
-           bank_offset += s->cirrus_bank_base[bank_index];
-           if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
-               bank_offset <<= 4;
-           } else if (s->vga.gr[0x0B] & 0x02) {
-               bank_offset <<= 3;
-           }
-           bank_offset &= s->cirrus_addr_mask;
-           val = *(s->vga.vram_ptr + bank_offset);
-       } else
-           val = 0xff;
-    } else if (addr >= 0x18000 && addr < 0x18100) {
-       /* memory-mapped I/O */
-       val = 0xff;
-       if ((s->vga.sr[0x17] & 0x44) == 0x04) {
-           val = cirrus_mmio_blt_read(s, addr & 0xff);
-       }
-    } else {
-       val = 0xff;
-#ifdef DEBUG_CIRRUS
-       printf("cirrus: mem_readb " TARGET_FMT_plx "\n", addr);
-#endif
-    }
-    return val;
-}
-
-static void cirrus_vga_mem_write(void *opaque,
-                                 hwaddr addr,
-                                 uint64_t mem_value,
-                                 uint32_t size)
-{
-    CirrusVGAState *s = opaque;
-    unsigned bank_index;
-    unsigned bank_offset;
-    unsigned mode;
-
-    if ((s->vga.sr[0x07] & 0x01) == 0) {
-        vga_mem_writeb(&s->vga, addr, mem_value);
-        return;
-    }
-
-    if (addr < 0x10000) {
-       if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
-           /* bitblt */
-           *s->cirrus_srcptr++ = (uint8_t) mem_value;
-           if (s->cirrus_srcptr >= s->cirrus_srcptr_end) {
-               cirrus_bitblt_cputovideo_next(s);
-           }
-       } else {
-           /* video memory */
-           bank_index = addr >> 15;
-           bank_offset = addr & 0x7fff;
-           if (bank_offset < s->cirrus_bank_limit[bank_index]) {
-               bank_offset += s->cirrus_bank_base[bank_index];
-               if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
-                   bank_offset <<= 4;
-               } else if (s->vga.gr[0x0B] & 0x02) {
-                   bank_offset <<= 3;
-               }
-               bank_offset &= s->cirrus_addr_mask;
-               mode = s->vga.gr[0x05] & 0x7;
-               if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) {
-                   *(s->vga.vram_ptr + bank_offset) = mem_value;
-                    memory_region_set_dirty(&s->vga.vram, bank_offset,
-                                            sizeof(mem_value));
-               } else {
-                   if ((s->vga.gr[0x0B] & 0x14) != 0x14) {
-                       cirrus_mem_writeb_mode4and5_8bpp(s, mode,
-                                                        bank_offset,
-                                                        mem_value);
-                   } else {
-                       cirrus_mem_writeb_mode4and5_16bpp(s, mode,
-                                                         bank_offset,
-                                                         mem_value);
-                   }
-               }
-           }
-       }
-    } else if (addr >= 0x18000 && addr < 0x18100) {
-       /* memory-mapped I/O */
-       if ((s->vga.sr[0x17] & 0x44) == 0x04) {
-           cirrus_mmio_blt_write(s, addr & 0xff, mem_value);
-       }
-    } else {
-#ifdef DEBUG_CIRRUS
-        printf("cirrus: mem_writeb " TARGET_FMT_plx " value %02x\n", addr,
-               mem_value);
-#endif
-    }
-}
-
-static const MemoryRegionOps cirrus_vga_mem_ops = {
-    .read = cirrus_vga_mem_read,
-    .write = cirrus_vga_mem_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-/***************************************
- *
- *  hardware cursor
- *
- ***************************************/
-
-static inline void invalidate_cursor1(CirrusVGAState *s)
-{
-    if (s->last_hw_cursor_size) {
-        vga_invalidate_scanlines(&s->vga,
-                                 s->last_hw_cursor_y + s->last_hw_cursor_y_start,
-                                 s->last_hw_cursor_y + s->last_hw_cursor_y_end);
-    }
-}
-
-static inline void cirrus_cursor_compute_yrange(CirrusVGAState *s)
-{
-    const uint8_t *src;
-    uint32_t content;
-    int y, y_min, y_max;
-
-    src = s->vga.vram_ptr + s->real_vram_size - 16 * 1024;
-    if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) {
-        src += (s->vga.sr[0x13] & 0x3c) * 256;
-        y_min = 64;
-        y_max = -1;
-        for(y = 0; y < 64; y++) {
-            content = ((uint32_t *)src)[0] |
-                ((uint32_t *)src)[1] |
-                ((uint32_t *)src)[2] |
-                ((uint32_t *)src)[3];
-            if (content) {
-                if (y < y_min)
-                    y_min = y;
-                if (y > y_max)
-                    y_max = y;
-            }
-            src += 16;
-        }
-    } else {
-        src += (s->vga.sr[0x13] & 0x3f) * 256;
-        y_min = 32;
-        y_max = -1;
-        for(y = 0; y < 32; y++) {
-            content = ((uint32_t *)src)[0] |
-                ((uint32_t *)(src + 128))[0];
-            if (content) {
-                if (y < y_min)
-                    y_min = y;
-                if (y > y_max)
-                    y_max = y;
-            }
-            src += 4;
-        }
-    }
-    if (y_min > y_max) {
-        s->last_hw_cursor_y_start = 0;
-        s->last_hw_cursor_y_end = 0;
-    } else {
-        s->last_hw_cursor_y_start = y_min;
-        s->last_hw_cursor_y_end = y_max + 1;
-    }
-}
-
-/* NOTE: we do not currently handle the cursor bitmap change, so we
-   update the cursor only if it moves. */
-static void cirrus_cursor_invalidate(VGACommonState *s1)
-{
-    CirrusVGAState *s = container_of(s1, CirrusVGAState, vga);
-    int size;
-
-    if (!(s->vga.sr[0x12] & CIRRUS_CURSOR_SHOW)) {
-        size = 0;
-    } else {
-        if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE)
-            size = 64;
-        else
-            size = 32;
-    }
-    /* invalidate last cursor and new cursor if any change */
-    if (s->last_hw_cursor_size != size ||
-        s->last_hw_cursor_x != s->hw_cursor_x ||
-        s->last_hw_cursor_y != s->hw_cursor_y) {
-
-        invalidate_cursor1(s);
-
-        s->last_hw_cursor_size = size;
-        s->last_hw_cursor_x = s->hw_cursor_x;
-        s->last_hw_cursor_y = s->hw_cursor_y;
-        /* compute the real cursor min and max y */
-        cirrus_cursor_compute_yrange(s);
-        invalidate_cursor1(s);
-    }
-}
-
-#define DEPTH 8
-#include "hw/cirrus_vga_template.h"
-
-#define DEPTH 16
-#include "hw/cirrus_vga_template.h"
-
-#define DEPTH 32
-#include "hw/cirrus_vga_template.h"
-
-static void cirrus_cursor_draw_line(VGACommonState *s1, uint8_t *d1, int scr_y)
-{
-    CirrusVGAState *s = container_of(s1, CirrusVGAState, vga);
-    DisplaySurface *surface = qemu_console_surface(s->vga.con);
-    int w, h, bpp, x1, x2, poffset;
-    unsigned int color0, color1;
-    const uint8_t *palette, *src;
-    uint32_t content;
-
-    if (!(s->vga.sr[0x12] & CIRRUS_CURSOR_SHOW))
-        return;
-    /* fast test to see if the cursor intersects with the scan line */
-    if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) {
-        h = 64;
-    } else {
-        h = 32;
-    }
-    if (scr_y < s->hw_cursor_y ||
-        scr_y >= (s->hw_cursor_y + h))
-        return;
-
-    src = s->vga.vram_ptr + s->real_vram_size - 16 * 1024;
-    if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) {
-        src += (s->vga.sr[0x13] & 0x3c) * 256;
-        src += (scr_y - s->hw_cursor_y) * 16;
-        poffset = 8;
-        content = ((uint32_t *)src)[0] |
-            ((uint32_t *)src)[1] |
-            ((uint32_t *)src)[2] |
-            ((uint32_t *)src)[3];
-    } else {
-        src += (s->vga.sr[0x13] & 0x3f) * 256;
-        src += (scr_y - s->hw_cursor_y) * 4;
-        poffset = 128;
-        content = ((uint32_t *)src)[0] |
-            ((uint32_t *)(src + 128))[0];
-    }
-    /* if nothing to draw, no need to continue */
-    if (!content)
-        return;
-    w = h;
-
-    x1 = s->hw_cursor_x;
-    if (x1 >= s->vga.last_scr_width)
-        return;
-    x2 = s->hw_cursor_x + w;
-    if (x2 > s->vga.last_scr_width)
-        x2 = s->vga.last_scr_width;
-    w = x2 - x1;
-    palette = s->cirrus_hidden_palette;
-    color0 = s->vga.rgb_to_pixel(c6_to_8(palette[0x0 * 3]),
-                                 c6_to_8(palette[0x0 * 3 + 1]),
-                                 c6_to_8(palette[0x0 * 3 + 2]));
-    color1 = s->vga.rgb_to_pixel(c6_to_8(palette[0xf * 3]),
-                                 c6_to_8(palette[0xf * 3 + 1]),
-                                 c6_to_8(palette[0xf * 3 + 2]));
-    bpp = surface_bytes_per_pixel(surface);
-    d1 += x1 * bpp;
-    switch (surface_bits_per_pixel(surface)) {
-    default:
-        break;
-    case 8:
-        vga_draw_cursor_line_8(d1, src, poffset, w, color0, color1, 0xff);
-        break;
-    case 15:
-        vga_draw_cursor_line_16(d1, src, poffset, w, color0, color1, 0x7fff);
-        break;
-    case 16:
-        vga_draw_cursor_line_16(d1, src, poffset, w, color0, color1, 0xffff);
-        break;
-    case 32:
-        vga_draw_cursor_line_32(d1, src, poffset, w, color0, color1, 0xffffff);
-        break;
-    }
-}
-
-/***************************************
- *
- *  LFB memory access
- *
- ***************************************/
-
-static uint64_t cirrus_linear_read(void *opaque, hwaddr addr,
-                                   unsigned size)
-{
-    CirrusVGAState *s = opaque;
-    uint32_t ret;
-
-    addr &= s->cirrus_addr_mask;
-
-    if (((s->vga.sr[0x17] & 0x44) == 0x44) &&
-        ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) {
-       /* memory-mapped I/O */
-       ret = cirrus_mmio_blt_read(s, addr & 0xff);
-    } else if (0) {
-       /* XXX handle bitblt */
-       ret = 0xff;
-    } else {
-       /* video memory */
-       if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
-           addr <<= 4;
-       } else if (s->vga.gr[0x0B] & 0x02) {
-           addr <<= 3;
-       }
-       addr &= s->cirrus_addr_mask;
-       ret = *(s->vga.vram_ptr + addr);
-    }
-
-    return ret;
-}
-
-static void cirrus_linear_write(void *opaque, hwaddr addr,
-                                uint64_t val, unsigned size)
-{
-    CirrusVGAState *s = opaque;
-    unsigned mode;
-
-    addr &= s->cirrus_addr_mask;
-
-    if (((s->vga.sr[0x17] & 0x44) == 0x44) &&
-        ((addr & s->linear_mmio_mask) ==  s->linear_mmio_mask)) {
-       /* memory-mapped I/O */
-       cirrus_mmio_blt_write(s, addr & 0xff, val);
-    } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
-       /* bitblt */
-       *s->cirrus_srcptr++ = (uint8_t) val;
-       if (s->cirrus_srcptr >= s->cirrus_srcptr_end) {
-           cirrus_bitblt_cputovideo_next(s);
-       }
-    } else {
-       /* video memory */
-       if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
-           addr <<= 4;
-       } else if (s->vga.gr[0x0B] & 0x02) {
-           addr <<= 3;
-       }
-       addr &= s->cirrus_addr_mask;
-
-       mode = s->vga.gr[0x05] & 0x7;
-       if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) {
-           *(s->vga.vram_ptr + addr) = (uint8_t) val;
-            memory_region_set_dirty(&s->vga.vram, addr, 1);
-       } else {
-           if ((s->vga.gr[0x0B] & 0x14) != 0x14) {
-               cirrus_mem_writeb_mode4and5_8bpp(s, mode, addr, val);
-           } else {
-               cirrus_mem_writeb_mode4and5_16bpp(s, mode, addr, val);
-           }
-       }
-    }
-}
-
-/***************************************
- *
- *  system to screen memory access
- *
- ***************************************/
-
-
-static uint64_t cirrus_linear_bitblt_read(void *opaque,
-                                          hwaddr addr,
-                                          unsigned size)
-{
-    CirrusVGAState *s = opaque;
-    uint32_t ret;
-
-    /* XXX handle bitblt */
-    (void)s;
-    ret = 0xff;
-    return ret;
-}
-
-static void cirrus_linear_bitblt_write(void *opaque,
-                                       hwaddr addr,
-                                       uint64_t val,
-                                       unsigned size)
-{
-    CirrusVGAState *s = opaque;
-
-    if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
-       /* bitblt */
-       *s->cirrus_srcptr++ = (uint8_t) val;
-       if (s->cirrus_srcptr >= s->cirrus_srcptr_end) {
-           cirrus_bitblt_cputovideo_next(s);
-       }
-    }
-}
-
-static const MemoryRegionOps cirrus_linear_bitblt_io_ops = {
-    .read = cirrus_linear_bitblt_read,
-    .write = cirrus_linear_bitblt_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-static void map_linear_vram_bank(CirrusVGAState *s, unsigned bank)
-{
-    MemoryRegion *mr = &s->cirrus_bank[bank];
-    bool enabled = !(s->cirrus_srcptr != s->cirrus_srcptr_end)
-        && !((s->vga.sr[0x07] & 0x01) == 0)
-        && !((s->vga.gr[0x0B] & 0x14) == 0x14)
-        && !(s->vga.gr[0x0B] & 0x02);
-
-    memory_region_set_enabled(mr, enabled);
-    memory_region_set_alias_offset(mr, s->cirrus_bank_base[bank]);
-}
-
-static void map_linear_vram(CirrusVGAState *s)
-{
-    if (s->bustype == CIRRUS_BUSTYPE_PCI && !s->linear_vram) {
-        s->linear_vram = true;
-        memory_region_add_subregion_overlap(&s->pci_bar, 0, &s->vga.vram, 1);
-    }
-    map_linear_vram_bank(s, 0);
-    map_linear_vram_bank(s, 1);
-}
-
-static void unmap_linear_vram(CirrusVGAState *s)
-{
-    if (s->bustype == CIRRUS_BUSTYPE_PCI && s->linear_vram) {
-        s->linear_vram = false;
-        memory_region_del_subregion(&s->pci_bar, &s->vga.vram);
-    }
-    memory_region_set_enabled(&s->cirrus_bank[0], false);
-    memory_region_set_enabled(&s->cirrus_bank[1], false);
-}
-
-/* Compute the memory access functions */
-static void cirrus_update_memory_access(CirrusVGAState *s)
-{
-    unsigned mode;
-
-    memory_region_transaction_begin();
-    if ((s->vga.sr[0x17] & 0x44) == 0x44) {
-        goto generic_io;
-    } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
-        goto generic_io;
-    } else {
-       if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
-            goto generic_io;
-       } else if (s->vga.gr[0x0B] & 0x02) {
-            goto generic_io;
-        }
-
-       mode = s->vga.gr[0x05] & 0x7;
-       if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) {
-            map_linear_vram(s);
-        } else {
-        generic_io:
-            unmap_linear_vram(s);
-        }
-    }
-    memory_region_transaction_commit();
-}
-
-
-/* I/O ports */
-
-static uint64_t cirrus_vga_ioport_read(void *opaque, hwaddr addr,
-                                       unsigned size)
-{
-    CirrusVGAState *c = opaque;
-    VGACommonState *s = &c->vga;
-    int val, index;
-
-    qemu_flush_coalesced_mmio_buffer();
-    addr += 0x3b0;
-
-    if (vga_ioport_invalid(s, addr)) {
-       val = 0xff;
-    } else {
-       switch (addr) {
-       case 0x3c0:
-           if (s->ar_flip_flop == 0) {
-               val = s->ar_index;
-           } else {
-               val = 0;
-           }
-           break;
-       case 0x3c1:
-           index = s->ar_index & 0x1f;
-           if (index < 21)
-               val = s->ar[index];
-           else
-               val = 0;
-           break;
-       case 0x3c2:
-           val = s->st00;
-           break;
-       case 0x3c4:
-           val = s->sr_index;
-           break;
-       case 0x3c5:
-           val = cirrus_vga_read_sr(c);
-            break;
-#ifdef DEBUG_VGA_REG
-           printf("vga: read SR%x = 0x%02x\n", s->sr_index, val);
-#endif
-           break;
-       case 0x3c6:
-           val = cirrus_read_hidden_dac(c);
-           break;
-       case 0x3c7:
-           val = s->dac_state;
-           break;
-       case 0x3c8:
-           val = s->dac_write_index;
-           c->cirrus_hidden_dac_lockindex = 0;
-           break;
-        case 0x3c9:
-            val = cirrus_vga_read_palette(c);
-            break;
-       case 0x3ca:
-           val = s->fcr;
-           break;
-       case 0x3cc:
-           val = s->msr;
-           break;
-       case 0x3ce:
-           val = s->gr_index;
-           break;
-       case 0x3cf:
-           val = cirrus_vga_read_gr(c, s->gr_index);
-#ifdef DEBUG_VGA_REG
-           printf("vga: read GR%x = 0x%02x\n", s->gr_index, val);
-#endif
-           break;
-       case 0x3b4:
-       case 0x3d4:
-           val = s->cr_index;
-           break;
-       case 0x3b5:
-       case 0x3d5:
-            val = cirrus_vga_read_cr(c, s->cr_index);
-#ifdef DEBUG_VGA_REG
-           printf("vga: read CR%x = 0x%02x\n", s->cr_index, val);
-#endif
-           break;
-       case 0x3ba:
-       case 0x3da:
-           /* just toggle to fool polling */
-           val = s->st01 = s->retrace(s);
-           s->ar_flip_flop = 0;
-           break;
-       default:
-           val = 0x00;
-           break;
-       }
-    }
-#if defined(DEBUG_VGA)
-    printf("VGA: read addr=0x%04x data=0x%02x\n", addr, val);
-#endif
-    return val;
-}
-
-static void cirrus_vga_ioport_write(void *opaque, hwaddr addr, uint64_t val,
-                                    unsigned size)
-{
-    CirrusVGAState *c = opaque;
-    VGACommonState *s = &c->vga;
-    int index;
-
-    qemu_flush_coalesced_mmio_buffer();
-    addr += 0x3b0;
-
-    /* check port range access depending on color/monochrome mode */
-    if (vga_ioport_invalid(s, addr)) {
-       return;
-    }
-#ifdef DEBUG_VGA
-    printf("VGA: write addr=0x%04x data=0x%02x\n", addr, val);
-#endif
-
-    switch (addr) {
-    case 0x3c0:
-       if (s->ar_flip_flop == 0) {
-           val &= 0x3f;
-           s->ar_index = val;
-       } else {
-           index = s->ar_index & 0x1f;
-           switch (index) {
-           case 0x00 ... 0x0f:
-               s->ar[index] = val & 0x3f;
-               break;
-           case 0x10:
-               s->ar[index] = val & ~0x10;
-               break;
-           case 0x11:
-               s->ar[index] = val;
-               break;
-           case 0x12:
-               s->ar[index] = val & ~0xc0;
-               break;
-           case 0x13:
-               s->ar[index] = val & ~0xf0;
-               break;
-           case 0x14:
-               s->ar[index] = val & ~0xf0;
-               break;
-           default:
-               break;
-           }
-       }
-       s->ar_flip_flop ^= 1;
-       break;
-    case 0x3c2:
-       s->msr = val & ~0x10;
-       s->update_retrace_info(s);
-       break;
-    case 0x3c4:
-       s->sr_index = val;
-       break;
-    case 0x3c5:
-#ifdef DEBUG_VGA_REG
-       printf("vga: write SR%x = 0x%02x\n", s->sr_index, val);
-#endif
-       cirrus_vga_write_sr(c, val);
-        break;
-       break;
-    case 0x3c6:
-       cirrus_write_hidden_dac(c, val);
-       break;
-    case 0x3c7:
-       s->dac_read_index = val;
-       s->dac_sub_index = 0;
-       s->dac_state = 3;
-       break;
-    case 0x3c8:
-       s->dac_write_index = val;
-       s->dac_sub_index = 0;
-       s->dac_state = 0;
-       break;
-    case 0x3c9:
-        cirrus_vga_write_palette(c, val);
-        break;
-    case 0x3ce:
-       s->gr_index = val;
-       break;
-    case 0x3cf:
-#ifdef DEBUG_VGA_REG
-       printf("vga: write GR%x = 0x%02x\n", s->gr_index, val);
-#endif
-       cirrus_vga_write_gr(c, s->gr_index, val);
-       break;
-    case 0x3b4:
-    case 0x3d4:
-       s->cr_index = val;
-       break;
-    case 0x3b5:
-    case 0x3d5:
-#ifdef DEBUG_VGA_REG
-       printf("vga: write CR%x = 0x%02x\n", s->cr_index, val);
-#endif
-       cirrus_vga_write_cr(c, val);
-       break;
-    case 0x3ba:
-    case 0x3da:
-       s->fcr = val & 0x10;
-       break;
-    }
-}
-
-/***************************************
- *
- *  memory-mapped I/O access
- *
- ***************************************/
-
-static uint64_t cirrus_mmio_read(void *opaque, hwaddr addr,
-                                 unsigned size)
-{
-    CirrusVGAState *s = opaque;
-
-    if (addr >= 0x100) {
-        return cirrus_mmio_blt_read(s, addr - 0x100);
-    } else {
-        return cirrus_vga_ioport_read(s, addr + 0x10, size);
-    }
-}
-
-static void cirrus_mmio_write(void *opaque, hwaddr addr,
-                              uint64_t val, unsigned size)
-{
-    CirrusVGAState *s = opaque;
-
-    if (addr >= 0x100) {
-       cirrus_mmio_blt_write(s, addr - 0x100, val);
-    } else {
-        cirrus_vga_ioport_write(s, addr + 0x10, val, size);
-    }
-}
-
-static const MemoryRegionOps cirrus_mmio_io_ops = {
-    .read = cirrus_mmio_read,
-    .write = cirrus_mmio_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-/* load/save state */
-
-static int cirrus_post_load(void *opaque, int version_id)
-{
-    CirrusVGAState *s = opaque;
-
-    s->vga.gr[0x00] = s->cirrus_shadow_gr0 & 0x0f;
-    s->vga.gr[0x01] = s->cirrus_shadow_gr1 & 0x0f;
-
-    cirrus_update_memory_access(s);
-    /* force refresh */
-    s->vga.graphic_mode = -1;
-    cirrus_update_bank_ptr(s, 0);
-    cirrus_update_bank_ptr(s, 1);
-    return 0;
-}
-
-static const VMStateDescription vmstate_cirrus_vga = {
-    .name = "cirrus_vga",
-    .version_id = 2,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .post_load = cirrus_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT32(vga.latch, CirrusVGAState),
-        VMSTATE_UINT8(vga.sr_index, CirrusVGAState),
-        VMSTATE_BUFFER(vga.sr, CirrusVGAState),
-        VMSTATE_UINT8(vga.gr_index, CirrusVGAState),
-        VMSTATE_UINT8(cirrus_shadow_gr0, CirrusVGAState),
-        VMSTATE_UINT8(cirrus_shadow_gr1, CirrusVGAState),
-        VMSTATE_BUFFER_START_MIDDLE(vga.gr, CirrusVGAState, 2),
-        VMSTATE_UINT8(vga.ar_index, CirrusVGAState),
-        VMSTATE_BUFFER(vga.ar, CirrusVGAState),
-        VMSTATE_INT32(vga.ar_flip_flop, CirrusVGAState),
-        VMSTATE_UINT8(vga.cr_index, CirrusVGAState),
-        VMSTATE_BUFFER(vga.cr, CirrusVGAState),
-        VMSTATE_UINT8(vga.msr, CirrusVGAState),
-        VMSTATE_UINT8(vga.fcr, CirrusVGAState),
-        VMSTATE_UINT8(vga.st00, CirrusVGAState),
-        VMSTATE_UINT8(vga.st01, CirrusVGAState),
-        VMSTATE_UINT8(vga.dac_state, CirrusVGAState),
-        VMSTATE_UINT8(vga.dac_sub_index, CirrusVGAState),
-        VMSTATE_UINT8(vga.dac_read_index, CirrusVGAState),
-        VMSTATE_UINT8(vga.dac_write_index, CirrusVGAState),
-        VMSTATE_BUFFER(vga.dac_cache, CirrusVGAState),
-        VMSTATE_BUFFER(vga.palette, CirrusVGAState),
-        VMSTATE_INT32(vga.bank_offset, CirrusVGAState),
-        VMSTATE_UINT8(cirrus_hidden_dac_lockindex, CirrusVGAState),
-        VMSTATE_UINT8(cirrus_hidden_dac_data, CirrusVGAState),
-        VMSTATE_UINT32(hw_cursor_x, CirrusVGAState),
-        VMSTATE_UINT32(hw_cursor_y, CirrusVGAState),
-        /* XXX: we do not save the bitblt state - we assume we do not save
-           the state when the blitter is active */
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_pci_cirrus_vga = {
-    .name = "cirrus_vga",
-    .version_id = 2,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .fields      = (VMStateField []) {
-        VMSTATE_PCI_DEVICE(dev, PCICirrusVGAState),
-        VMSTATE_STRUCT(cirrus_vga, PCICirrusVGAState, 0,
-                       vmstate_cirrus_vga, CirrusVGAState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-/***************************************
- *
- *  initialize
- *
- ***************************************/
-
-static void cirrus_reset(void *opaque)
-{
-    CirrusVGAState *s = opaque;
-
-    vga_common_reset(&s->vga);
-    unmap_linear_vram(s);
-    s->vga.sr[0x06] = 0x0f;
-    if (s->device_id == CIRRUS_ID_CLGD5446) {
-        /* 4MB 64 bit memory config, always PCI */
-        s->vga.sr[0x1F] = 0x2d;                // MemClock
-        s->vga.gr[0x18] = 0x0f;             // fastest memory configuration
-        s->vga.sr[0x0f] = 0x98;
-        s->vga.sr[0x17] = 0x20;
-        s->vga.sr[0x15] = 0x04; /* memory size, 3=2MB, 4=4MB */
-    } else {
-        s->vga.sr[0x1F] = 0x22;                // MemClock
-        s->vga.sr[0x0F] = CIRRUS_MEMSIZE_2M;
-        s->vga.sr[0x17] = s->bustype;
-        s->vga.sr[0x15] = 0x03; /* memory size, 3=2MB, 4=4MB */
-    }
-    s->vga.cr[0x27] = s->device_id;
-
-    s->cirrus_hidden_dac_lockindex = 5;
-    s->cirrus_hidden_dac_data = 0;
-}
-
-static const MemoryRegionOps cirrus_linear_io_ops = {
-    .read = cirrus_linear_read,
-    .write = cirrus_linear_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-static const MemoryRegionOps cirrus_vga_io_ops = {
-    .read = cirrus_vga_ioport_read,
-    .write = cirrus_vga_ioport_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci,
-                               MemoryRegion *system_memory,
-                               MemoryRegion *system_io)
-{
-    int i;
-    static int inited;
-
-    if (!inited) {
-        inited = 1;
-        for(i = 0;i < 256; i++)
-            rop_to_index[i] = CIRRUS_ROP_NOP_INDEX; /* nop rop */
-        rop_to_index[CIRRUS_ROP_0] = 0;
-        rop_to_index[CIRRUS_ROP_SRC_AND_DST] = 1;
-        rop_to_index[CIRRUS_ROP_NOP] = 2;
-        rop_to_index[CIRRUS_ROP_SRC_AND_NOTDST] = 3;
-        rop_to_index[CIRRUS_ROP_NOTDST] = 4;
-        rop_to_index[CIRRUS_ROP_SRC] = 5;
-        rop_to_index[CIRRUS_ROP_1] = 6;
-        rop_to_index[CIRRUS_ROP_NOTSRC_AND_DST] = 7;
-        rop_to_index[CIRRUS_ROP_SRC_XOR_DST] = 8;
-        rop_to_index[CIRRUS_ROP_SRC_OR_DST] = 9;
-        rop_to_index[CIRRUS_ROP_NOTSRC_OR_NOTDST] = 10;
-        rop_to_index[CIRRUS_ROP_SRC_NOTXOR_DST] = 11;
-        rop_to_index[CIRRUS_ROP_SRC_OR_NOTDST] = 12;
-        rop_to_index[CIRRUS_ROP_NOTSRC] = 13;
-        rop_to_index[CIRRUS_ROP_NOTSRC_OR_DST] = 14;
-        rop_to_index[CIRRUS_ROP_NOTSRC_AND_NOTDST] = 15;
-        s->device_id = device_id;
-        if (is_pci)
-            s->bustype = CIRRUS_BUSTYPE_PCI;
-        else
-            s->bustype = CIRRUS_BUSTYPE_ISA;
-    }
-
-    /* Register ioport 0x3b0 - 0x3df */
-    memory_region_init_io(&s->cirrus_vga_io, &cirrus_vga_io_ops, s,
-                          "cirrus-io", 0x30);
-    memory_region_add_subregion(system_io, 0x3b0, &s->cirrus_vga_io);
-
-    memory_region_init(&s->low_mem_container,
-                       "cirrus-lowmem-container",
-                       0x20000);
-
-    memory_region_init_io(&s->low_mem, &cirrus_vga_mem_ops, s,
-                          "cirrus-low-memory", 0x20000);
-    memory_region_add_subregion(&s->low_mem_container, 0, &s->low_mem);
-    for (i = 0; i < 2; ++i) {
-        static const char *names[] = { "vga.bank0", "vga.bank1" };
-        MemoryRegion *bank = &s->cirrus_bank[i];
-        memory_region_init_alias(bank, names[i], &s->vga.vram, 0, 0x8000);
-        memory_region_set_enabled(bank, false);
-        memory_region_add_subregion_overlap(&s->low_mem_container, i * 0x8000,
-                                            bank, 1);
-    }
-    memory_region_add_subregion_overlap(system_memory,
-                                        isa_mem_base + 0x000a0000,
-                                        &s->low_mem_container,
-                                        1);
-    memory_region_set_coalescing(&s->low_mem);
-
-    /* I/O handler for LFB */
-    memory_region_init_io(&s->cirrus_linear_io, &cirrus_linear_io_ops, s,
-                          "cirrus-linear-io", s->vga.vram_size_mb
-                                              * 1024 * 1024);
-    memory_region_set_flush_coalesced(&s->cirrus_linear_io);
-
-    /* I/O handler for LFB */
-    memory_region_init_io(&s->cirrus_linear_bitblt_io,
-                          &cirrus_linear_bitblt_io_ops,
-                          s,
-                          "cirrus-bitblt-mmio",
-                          0x400000);
-    memory_region_set_flush_coalesced(&s->cirrus_linear_bitblt_io);
-
-    /* I/O handler for memory-mapped I/O */
-    memory_region_init_io(&s->cirrus_mmio_io, &cirrus_mmio_io_ops, s,
-                          "cirrus-mmio", CIRRUS_PNPMMIO_SIZE);
-    memory_region_set_flush_coalesced(&s->cirrus_mmio_io);
-
-    s->real_vram_size =
-        (s->device_id == CIRRUS_ID_CLGD5446) ? 4096 * 1024 : 2048 * 1024;
-
-    /* XXX: s->vga.vram_size must be a power of two */
-    s->cirrus_addr_mask = s->real_vram_size - 1;
-    s->linear_mmio_mask = s->real_vram_size - 256;
-
-    s->vga.get_bpp = cirrus_get_bpp;
-    s->vga.get_offsets = cirrus_get_offsets;
-    s->vga.get_resolution = cirrus_get_resolution;
-    s->vga.cursor_invalidate = cirrus_cursor_invalidate;
-    s->vga.cursor_draw_line = cirrus_cursor_draw_line;
-
-    qemu_register_reset(cirrus_reset, s);
-}
-
-/***************************************
- *
- *  ISA bus support
- *
- ***************************************/
-
-static int vga_initfn(ISADevice *dev)
-{
-    ISACirrusVGAState *d = DO_UPCAST(ISACirrusVGAState, dev, dev);
-    VGACommonState *s = &d->cirrus_vga.vga;
-
-    vga_common_init(s);
-    cirrus_init_common(&d->cirrus_vga, CIRRUS_ID_CLGD5430, 0,
-                       isa_address_space(dev), isa_address_space_io(dev));
-    s->con = graphic_console_init(s->update, s->invalidate,
-                                  s->screen_dump, s->text_update,
-                                  s);
-    rom_add_vga(VGABIOS_CIRRUS_FILENAME);
-    /* XXX ISA-LFB support */
-    /* FIXME not qdev yet */
-    return 0;
-}
-
-static Property isa_vga_cirrus_properties[] = {
-    DEFINE_PROP_UINT32("vgamem_mb", struct ISACirrusVGAState,
-                       cirrus_vga.vga.vram_size_mb, 8),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void isa_cirrus_vga_class_init(ObjectClass *klass, void *data)
-{
-    ISADeviceClass *k = ISA_DEVICE_CLASS(klass);
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    dc->vmsd  = &vmstate_cirrus_vga;
-    k->init   = vga_initfn;
-    dc->props = isa_vga_cirrus_properties;
-}
-
-static const TypeInfo isa_cirrus_vga_info = {
-    .name          = "isa-cirrus-vga",
-    .parent        = TYPE_ISA_DEVICE,
-    .instance_size = sizeof(ISACirrusVGAState),
-    .class_init = isa_cirrus_vga_class_init,
-};
-
-/***************************************
- *
- *  PCI bus support
- *
- ***************************************/
-
-static int pci_cirrus_vga_initfn(PCIDevice *dev)
-{
-     PCICirrusVGAState *d = DO_UPCAST(PCICirrusVGAState, dev, dev);
-     CirrusVGAState *s = &d->cirrus_vga;
-     PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
-     int16_t device_id = pc->device_id;
-
-     /* setup VGA */
-     vga_common_init(&s->vga);
-     cirrus_init_common(s, device_id, 1, pci_address_space(dev),
-                        pci_address_space_io(dev));
-     s->vga.con = graphic_console_init(s->vga.update, s->vga.invalidate,
-                                       s->vga.screen_dump, s->vga.text_update,
-                                       &s->vga);
-
-     /* setup PCI */
-
-    memory_region_init(&s->pci_bar, "cirrus-pci-bar0", 0x2000000);
-
-    /* XXX: add byte swapping apertures */
-    memory_region_add_subregion(&s->pci_bar, 0, &s->cirrus_linear_io);
-    memory_region_add_subregion(&s->pci_bar, 0x1000000,
-                                &s->cirrus_linear_bitblt_io);
-
-     /* setup memory space */
-     /* memory #0 LFB */
-     /* memory #1 memory-mapped I/O */
-     /* XXX: s->vga.vram_size must be a power of two */
-     pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->pci_bar);
-     if (device_id == CIRRUS_ID_CLGD5446) {
-         pci_register_bar(&d->dev, 1, 0, &s->cirrus_mmio_io);
-     }
-     return 0;
-}
-
-static Property pci_vga_cirrus_properties[] = {
-    DEFINE_PROP_UINT32("vgamem_mb", struct PCICirrusVGAState,
-                       cirrus_vga.vga.vram_size_mb, 8),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void cirrus_vga_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->no_hotplug = 1;
-    k->init = pci_cirrus_vga_initfn;
-    k->romfile = VGABIOS_CIRRUS_FILENAME;
-    k->vendor_id = PCI_VENDOR_ID_CIRRUS;
-    k->device_id = CIRRUS_ID_CLGD5446;
-    k->class_id = PCI_CLASS_DISPLAY_VGA;
-    dc->desc = "Cirrus CLGD 54xx VGA";
-    dc->vmsd = &vmstate_pci_cirrus_vga;
-    dc->props = pci_vga_cirrus_properties;
-}
-
-static const TypeInfo cirrus_vga_info = {
-    .name          = "cirrus-vga",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCICirrusVGAState),
-    .class_init    = cirrus_vga_class_init,
-};
-
-static void cirrus_vga_register_types(void)
-{
-    type_register_static(&isa_cirrus_vga_info);
-    type_register_static(&cirrus_vga_info);
-}
-
-type_init(cirrus_vga_register_types)
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..94109f32e220199daa6340941e3504c24f7d0a67 100644 (file)
@@ -0,0 +1,14 @@
+# core qdev-related obj files, also used by *-user:
+common-obj-y += qdev.o qdev-properties.o
+# irq.o needed for qdev GPIO handling:
+common-obj-y += irq.o
+
+common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o
+common-obj-$(CONFIG_XILINX_AXI) += stream.o
+common-obj-$(CONFIG_PTIMER) += ptimer.o
+common-obj-$(CONFIG_SOFTMMU) += sysbus.o
+common-obj-$(CONFIG_SOFTMMU) += null-machine.o
+common-obj-$(CONFIG_SOFTMMU) += loader.o
+common-obj-$(CONFIG_SOFTMMU) += qdev-addr.o
+common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o
+
diff --git a/hw/core/empty_slot.c b/hw/core/empty_slot.c
new file mode 100644 (file)
index 0000000..5234a4d
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * QEMU Empty Slot
+ *
+ * The empty_slot device emulates known to a bus but not connected devices.
+ *
+ * Copyright (c) 2010 Artyom Tarasenko
+ *
+ * This code is licensed under the GNU GPL v2 or (at your option) any later
+ * version.
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/empty_slot.h"
+
+//#define DEBUG_EMPTY_SLOT
+
+#ifdef DEBUG_EMPTY_SLOT
+#define DPRINTF(fmt, ...)                                       \
+    do { printf("empty_slot: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+typedef struct EmptySlot {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    uint64_t size;
+} EmptySlot;
+
+static uint64_t empty_slot_read(void *opaque, hwaddr addr,
+                                unsigned size)
+{
+    DPRINTF("read from " TARGET_FMT_plx "\n", addr);
+    return 0;
+}
+
+static void empty_slot_write(void *opaque, hwaddr addr,
+                             uint64_t val, unsigned size)
+{
+    DPRINTF("write 0x%x to " TARGET_FMT_plx "\n", (unsigned)val, addr);
+}
+
+static const MemoryRegionOps empty_slot_ops = {
+    .read = empty_slot_read,
+    .write = empty_slot_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+void empty_slot_init(hwaddr addr, uint64_t slot_size)
+{
+    if (slot_size > 0) {
+        /* Only empty slots larger than 0 byte need handling. */
+        DeviceState *dev;
+        SysBusDevice *s;
+        EmptySlot *e;
+
+        dev = qdev_create(NULL, "empty_slot");
+        s = SYS_BUS_DEVICE(dev);
+        e = FROM_SYSBUS(EmptySlot, s);
+        e->size = slot_size;
+
+        qdev_init_nofail(dev);
+
+        sysbus_mmio_map(s, 0, addr);
+    }
+}
+
+static int empty_slot_init1(SysBusDevice *dev)
+{
+    EmptySlot *s = FROM_SYSBUS(EmptySlot, dev);
+
+    memory_region_init_io(&s->iomem, &empty_slot_ops, s,
+                          "empty-slot", s->size);
+    sysbus_init_mmio(dev, &s->iomem);
+    return 0;
+}
+
+static void empty_slot_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = empty_slot_init1;
+}
+
+static const TypeInfo empty_slot_info = {
+    .name          = "empty_slot",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(EmptySlot),
+    .class_init    = empty_slot_class_init,
+};
+
+static void empty_slot_register_types(void)
+{
+    type_register_static(&empty_slot_info);
+}
+
+type_init(empty_slot_register_types)
diff --git a/hw/core/irq.c b/hw/core/irq.c
new file mode 100644 (file)
index 0000000..2078542
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * QEMU IRQ/GPIO common code.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "hw/irq.h"
+
+struct IRQState {
+    qemu_irq_handler handler;
+    void *opaque;
+    int n;
+};
+
+void qemu_set_irq(qemu_irq irq, int level)
+{
+    if (!irq)
+        return;
+
+    irq->handler(irq->opaque, irq->n, level);
+}
+
+qemu_irq *qemu_extend_irqs(qemu_irq *old, int n_old, qemu_irq_handler handler,
+                           void *opaque, int n)
+{
+    qemu_irq *s;
+    struct IRQState *p;
+    int i;
+
+    if (!old) {
+        n_old = 0;
+    }
+    s = old ? g_renew(qemu_irq, old, n + n_old) : g_new(qemu_irq, n);
+    p = old ? g_renew(struct IRQState, s[0], n + n_old) :
+                g_new(struct IRQState, n);
+    for (i = 0; i < n + n_old; i++) {
+        if (i >= n_old) {
+            p->handler = handler;
+            p->opaque = opaque;
+            p->n = i;
+        }
+        s[i] = p;
+        p++;
+    }
+    return s;
+}
+
+qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n)
+{
+    return qemu_extend_irqs(NULL, 0, handler, opaque, n);
+}
+
+
+void qemu_free_irqs(qemu_irq *s)
+{
+    g_free(s[0]);
+    g_free(s);
+}
+
+static void qemu_notirq(void *opaque, int line, int level)
+{
+    struct IRQState *irq = opaque;
+
+    irq->handler(irq->opaque, irq->n, !level);
+}
+
+qemu_irq qemu_irq_invert(qemu_irq irq)
+{
+    /* The default state for IRQs is low, so raise the output now.  */
+    qemu_irq_raise(irq);
+    return qemu_allocate_irqs(qemu_notirq, irq, 1)[0];
+}
+
+static void qemu_splitirq(void *opaque, int line, int level)
+{
+    struct IRQState **irq = opaque;
+    irq[0]->handler(irq[0]->opaque, irq[0]->n, level);
+    irq[1]->handler(irq[1]->opaque, irq[1]->n, level);
+}
+
+qemu_irq qemu_irq_split(qemu_irq irq1, qemu_irq irq2)
+{
+    qemu_irq *s = g_malloc0(2 * sizeof(qemu_irq));
+    s[0] = irq1;
+    s[1] = irq2;
+    return qemu_allocate_irqs(qemu_splitirq, s, 1)[0];
+}
+
+static void proxy_irq_handler(void *opaque, int n, int level)
+{
+    qemu_irq **target = opaque;
+
+    if (*target) {
+        qemu_set_irq((*target)[n], level);
+    }
+}
+
+qemu_irq *qemu_irq_proxy(qemu_irq **target, int n)
+{
+    return qemu_allocate_irqs(proxy_irq_handler, target, n);
+}
+
+void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n)
+{
+    int i;
+    qemu_irq *old_irqs = qemu_allocate_irqs(NULL, NULL, n);
+    for (i = 0; i < n; i++) {
+        *old_irqs[i] = *gpio_in[i];
+        gpio_in[i]->handler = handler;
+        gpio_in[i]->opaque = old_irqs;
+    }
+}
+
+void qemu_irq_intercept_out(qemu_irq **gpio_out, qemu_irq_handler handler, int n)
+{
+    qemu_irq *old_irqs = *gpio_out;
+    *gpio_out = qemu_allocate_irqs(handler, old_irqs, n);
+}
diff --git a/hw/core/loader.c b/hw/core/loader.c
new file mode 100644 (file)
index 0000000..2f5072d
--- /dev/null
@@ -0,0 +1,850 @@
+/*
+ * QEMU Executable loader
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Gunzip functionality in this file is derived from u-boot:
+ *
+ * (C) Copyright 2008 Semihalf
+ *
+ * (C) Copyright 2000-2005
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "disas/disas.h"
+#include "monitor/monitor.h"
+#include "sysemu/sysemu.h"
+#include "hw/uboot_image.h"
+#include "hw/loader.h"
+#include "hw/nvram/fw_cfg.h"
+#include "exec/memory.h"
+#include "exec/address-spaces.h"
+
+#include <zlib.h>
+
+static int roms_loaded;
+
+/* return the size or -1 if error */
+int get_image_size(const char *filename)
+{
+    int fd, size;
+    fd = open(filename, O_RDONLY | O_BINARY);
+    if (fd < 0)
+        return -1;
+    size = lseek(fd, 0, SEEK_END);
+    close(fd);
+    return size;
+}
+
+/* return the size or -1 if error */
+/* deprecated, because caller does not specify buffer size! */
+int load_image(const char *filename, uint8_t *addr)
+{
+    int fd, size;
+    fd = open(filename, O_RDONLY | O_BINARY);
+    if (fd < 0)
+        return -1;
+    size = lseek(fd, 0, SEEK_END);
+    lseek(fd, 0, SEEK_SET);
+    if (read(fd, addr, size) != size) {
+        close(fd);
+        return -1;
+    }
+    close(fd);
+    return size;
+}
+
+/* read()-like version */
+ssize_t read_targphys(const char *name,
+                      int fd, hwaddr dst_addr, size_t nbytes)
+{
+    uint8_t *buf;
+    ssize_t did;
+
+    buf = g_malloc(nbytes);
+    did = read(fd, buf, nbytes);
+    if (did > 0)
+        rom_add_blob_fixed("read", buf, did, dst_addr);
+    g_free(buf);
+    return did;
+}
+
+/* return the size or -1 if error */
+int load_image_targphys(const char *filename,
+                        hwaddr addr, uint64_t max_sz)
+{
+    int size;
+
+    size = get_image_size(filename);
+    if (size > max_sz) {
+        return -1;
+    }
+    if (size > 0) {
+        rom_add_file_fixed(filename, addr, -1);
+    }
+    return size;
+}
+
+void pstrcpy_targphys(const char *name, hwaddr dest, int buf_size,
+                      const char *source)
+{
+    const char *nulp;
+    char *ptr;
+
+    if (buf_size <= 0) return;
+    nulp = memchr(source, 0, buf_size);
+    if (nulp) {
+        rom_add_blob_fixed(name, source, (nulp - source) + 1, dest);
+    } else {
+        rom_add_blob_fixed(name, source, buf_size, dest);
+        ptr = rom_ptr(dest + buf_size - 1);
+        *ptr = 0;
+    }
+}
+
+/* A.OUT loader */
+
+struct exec
+{
+  uint32_t a_info;   /* Use macros N_MAGIC, etc for access */
+  uint32_t a_text;   /* length of text, in bytes */
+  uint32_t a_data;   /* length of data, in bytes */
+  uint32_t a_bss;    /* length of uninitialized data area, in bytes */
+  uint32_t a_syms;   /* length of symbol table data in file, in bytes */
+  uint32_t a_entry;  /* start address */
+  uint32_t a_trsize; /* length of relocation info for text, in bytes */
+  uint32_t a_drsize; /* length of relocation info for data, in bytes */
+};
+
+static void bswap_ahdr(struct exec *e)
+{
+    bswap32s(&e->a_info);
+    bswap32s(&e->a_text);
+    bswap32s(&e->a_data);
+    bswap32s(&e->a_bss);
+    bswap32s(&e->a_syms);
+    bswap32s(&e->a_entry);
+    bswap32s(&e->a_trsize);
+    bswap32s(&e->a_drsize);
+}
+
+#define N_MAGIC(exec) ((exec).a_info & 0xffff)
+#define OMAGIC 0407
+#define NMAGIC 0410
+#define ZMAGIC 0413
+#define QMAGIC 0314
+#define _N_HDROFF(x) (1024 - sizeof (struct exec))
+#define N_TXTOFF(x)                                                    \
+    (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) :    \
+     (N_MAGIC(x) == QMAGIC ? 0 : sizeof (struct exec)))
+#define N_TXTADDR(x, target_page_size) (N_MAGIC(x) == QMAGIC ? target_page_size : 0)
+#define _N_SEGMENT_ROUND(x, target_page_size) (((x) + target_page_size - 1) & ~(target_page_size - 1))
+
+#define _N_TXTENDADDR(x, target_page_size) (N_TXTADDR(x, target_page_size)+(x).a_text)
+
+#define N_DATADDR(x, target_page_size) \
+    (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x, target_page_size)) \
+     : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x, target_page_size), target_page_size)))
+
+
+int load_aout(const char *filename, hwaddr addr, int max_sz,
+              int bswap_needed, hwaddr target_page_size)
+{
+    int fd;
+    ssize_t size, ret;
+    struct exec e;
+    uint32_t magic;
+
+    fd = open(filename, O_RDONLY | O_BINARY);
+    if (fd < 0)
+        return -1;
+
+    size = read(fd, &e, sizeof(e));
+    if (size < 0)
+        goto fail;
+
+    if (bswap_needed) {
+        bswap_ahdr(&e);
+    }
+
+    magic = N_MAGIC(e);
+    switch (magic) {
+    case ZMAGIC:
+    case QMAGIC:
+    case OMAGIC:
+        if (e.a_text + e.a_data > max_sz)
+            goto fail;
+       lseek(fd, N_TXTOFF(e), SEEK_SET);
+       size = read_targphys(filename, fd, addr, e.a_text + e.a_data);
+       if (size < 0)
+           goto fail;
+       break;
+    case NMAGIC:
+        if (N_DATADDR(e, target_page_size) + e.a_data > max_sz)
+            goto fail;
+       lseek(fd, N_TXTOFF(e), SEEK_SET);
+       size = read_targphys(filename, fd, addr, e.a_text);
+       if (size < 0)
+           goto fail;
+        ret = read_targphys(filename, fd, addr + N_DATADDR(e, target_page_size),
+                            e.a_data);
+       if (ret < 0)
+           goto fail;
+       size += ret;
+       break;
+    default:
+       goto fail;
+    }
+    close(fd);
+    return size;
+ fail:
+    close(fd);
+    return -1;
+}
+
+/* ELF loader */
+
+static void *load_at(int fd, int offset, int size)
+{
+    void *ptr;
+    if (lseek(fd, offset, SEEK_SET) < 0)
+        return NULL;
+    ptr = g_malloc(size);
+    if (read(fd, ptr, size) != size) {
+        g_free(ptr);
+        return NULL;
+    }
+    return ptr;
+}
+
+#ifdef ELF_CLASS
+#undef ELF_CLASS
+#endif
+
+#define ELF_CLASS   ELFCLASS32
+#include "elf.h"
+
+#define SZ             32
+#define elf_word        uint32_t
+#define elf_sword        int32_t
+#define bswapSZs       bswap32s
+#include "hw/elf_ops.h"
+
+#undef elfhdr
+#undef elf_phdr
+#undef elf_shdr
+#undef elf_sym
+#undef elf_note
+#undef elf_word
+#undef elf_sword
+#undef bswapSZs
+#undef SZ
+#define elfhdr         elf64_hdr
+#define elf_phdr       elf64_phdr
+#define elf_note       elf64_note
+#define elf_shdr       elf64_shdr
+#define elf_sym                elf64_sym
+#define elf_word        uint64_t
+#define elf_sword        int64_t
+#define bswapSZs       bswap64s
+#define SZ             64
+#include "hw/elf_ops.h"
+
+/* return < 0 if error, otherwise the number of bytes loaded in memory */
+int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
+             void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
+             uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb)
+{
+    int fd, data_order, target_data_order, must_swab, ret;
+    uint8_t e_ident[EI_NIDENT];
+
+    fd = open(filename, O_RDONLY | O_BINARY);
+    if (fd < 0) {
+        perror(filename);
+        return -1;
+    }
+    if (read(fd, e_ident, sizeof(e_ident)) != sizeof(e_ident))
+        goto fail;
+    if (e_ident[0] != ELFMAG0 ||
+        e_ident[1] != ELFMAG1 ||
+        e_ident[2] != ELFMAG2 ||
+        e_ident[3] != ELFMAG3)
+        goto fail;
+#ifdef HOST_WORDS_BIGENDIAN
+    data_order = ELFDATA2MSB;
+#else
+    data_order = ELFDATA2LSB;
+#endif
+    must_swab = data_order != e_ident[EI_DATA];
+    if (big_endian) {
+        target_data_order = ELFDATA2MSB;
+    } else {
+        target_data_order = ELFDATA2LSB;
+    }
+
+    if (target_data_order != e_ident[EI_DATA]) {
+        goto fail;
+    }
+
+    lseek(fd, 0, SEEK_SET);
+    if (e_ident[EI_CLASS] == ELFCLASS64) {
+        ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab,
+                         pentry, lowaddr, highaddr, elf_machine, clear_lsb);
+    } else {
+        ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab,
+                         pentry, lowaddr, highaddr, elf_machine, clear_lsb);
+    }
+
+    close(fd);
+    return ret;
+
+ fail:
+    close(fd);
+    return -1;
+}
+
+static void bswap_uboot_header(uboot_image_header_t *hdr)
+{
+#ifndef HOST_WORDS_BIGENDIAN
+    bswap32s(&hdr->ih_magic);
+    bswap32s(&hdr->ih_hcrc);
+    bswap32s(&hdr->ih_time);
+    bswap32s(&hdr->ih_size);
+    bswap32s(&hdr->ih_load);
+    bswap32s(&hdr->ih_ep);
+    bswap32s(&hdr->ih_dcrc);
+#endif
+}
+
+
+#define ZALLOC_ALIGNMENT       16
+
+static void *zalloc(void *x, unsigned items, unsigned size)
+{
+    void *p;
+
+    size *= items;
+    size = (size + ZALLOC_ALIGNMENT - 1) & ~(ZALLOC_ALIGNMENT - 1);
+
+    p = g_malloc(size);
+
+    return (p);
+}
+
+static void zfree(void *x, void *addr)
+{
+    g_free(addr);
+}
+
+
+#define HEAD_CRC       2
+#define EXTRA_FIELD    4
+#define ORIG_NAME      8
+#define COMMENT                0x10
+#define RESERVED       0xe0
+
+#define DEFLATED       8
+
+/* This is the usual maximum in uboot, so if a uImage overflows this, it would
+ * overflow on real hardware too. */
+#define UBOOT_MAX_GUNZIP_BYTES (64 << 20)
+
+static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src,
+                      size_t srclen)
+{
+    z_stream s;
+    ssize_t dstbytes;
+    int r, i, flags;
+
+    /* skip header */
+    i = 10;
+    flags = src[3];
+    if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
+        puts ("Error: Bad gzipped data\n");
+        return -1;
+    }
+    if ((flags & EXTRA_FIELD) != 0)
+        i = 12 + src[10] + (src[11] << 8);
+    if ((flags & ORIG_NAME) != 0)
+        while (src[i++] != 0)
+            ;
+    if ((flags & COMMENT) != 0)
+        while (src[i++] != 0)
+            ;
+    if ((flags & HEAD_CRC) != 0)
+        i += 2;
+    if (i >= srclen) {
+        puts ("Error: gunzip out of data in header\n");
+        return -1;
+    }
+
+    s.zalloc = zalloc;
+    s.zfree = zfree;
+
+    r = inflateInit2(&s, -MAX_WBITS);
+    if (r != Z_OK) {
+        printf ("Error: inflateInit2() returned %d\n", r);
+        return (-1);
+    }
+    s.next_in = src + i;
+    s.avail_in = srclen - i;
+    s.next_out = dst;
+    s.avail_out = dstlen;
+    r = inflate(&s, Z_FINISH);
+    if (r != Z_OK && r != Z_STREAM_END) {
+        printf ("Error: inflate() returned %d\n", r);
+        return -1;
+    }
+    dstbytes = s.next_out - (unsigned char *) dst;
+    inflateEnd(&s);
+
+    return dstbytes;
+}
+
+/* Load a U-Boot image.  */
+int load_uimage(const char *filename, hwaddr *ep,
+                hwaddr *loadaddr, int *is_linux)
+{
+    int fd;
+    int size;
+    uboot_image_header_t h;
+    uboot_image_header_t *hdr = &h;
+    uint8_t *data = NULL;
+    int ret = -1;
+
+    fd = open(filename, O_RDONLY | O_BINARY);
+    if (fd < 0)
+        return -1;
+
+    size = read(fd, hdr, sizeof(uboot_image_header_t));
+    if (size < 0)
+        goto out;
+
+    bswap_uboot_header(hdr);
+
+    if (hdr->ih_magic != IH_MAGIC)
+        goto out;
+
+    /* TODO: Implement other image types.  */
+    if (hdr->ih_type != IH_TYPE_KERNEL) {
+        fprintf(stderr, "Can only load u-boot image type \"kernel\"\n");
+        goto out;
+    }
+
+    switch (hdr->ih_comp) {
+    case IH_COMP_NONE:
+    case IH_COMP_GZIP:
+        break;
+    default:
+        fprintf(stderr,
+                "Unable to load u-boot images with compression type %d\n",
+                hdr->ih_comp);
+        goto out;
+    }
+
+    /* TODO: Check CPU type.  */
+    if (is_linux) {
+        if (hdr->ih_os == IH_OS_LINUX)
+            *is_linux = 1;
+        else
+            *is_linux = 0;
+    }
+
+    *ep = hdr->ih_ep;
+    data = g_malloc(hdr->ih_size);
+
+    if (read(fd, data, hdr->ih_size) != hdr->ih_size) {
+        fprintf(stderr, "Error reading file\n");
+        goto out;
+    }
+
+    if (hdr->ih_comp == IH_COMP_GZIP) {
+        uint8_t *compressed_data;
+        size_t max_bytes;
+        ssize_t bytes;
+
+        compressed_data = data;
+        max_bytes = UBOOT_MAX_GUNZIP_BYTES;
+        data = g_malloc(max_bytes);
+
+        bytes = gunzip(data, max_bytes, compressed_data, hdr->ih_size);
+        g_free(compressed_data);
+        if (bytes < 0) {
+            fprintf(stderr, "Unable to decompress gzipped image!\n");
+            goto out;
+        }
+        hdr->ih_size = bytes;
+    }
+
+    rom_add_blob_fixed(filename, data, hdr->ih_size, hdr->ih_load);
+
+    if (loadaddr)
+        *loadaddr = hdr->ih_load;
+
+    ret = hdr->ih_size;
+
+out:
+    if (data)
+        g_free(data);
+    close(fd);
+    return ret;
+}
+
+/*
+ * Functions for reboot-persistent memory regions.
+ *  - used for vga bios and option roms.
+ *  - also linux kernel (-kernel / -initrd).
+ */
+
+typedef struct Rom Rom;
+
+struct Rom {
+    char *name;
+    char *path;
+
+    /* datasize is the amount of memory allocated in "data". If datasize is less
+     * than romsize, it means that the area from datasize to romsize is filled
+     * with zeros.
+     */
+    size_t romsize;
+    size_t datasize;
+
+    uint8_t *data;
+    int isrom;
+    char *fw_dir;
+    char *fw_file;
+
+    hwaddr addr;
+    QTAILQ_ENTRY(Rom) next;
+};
+
+static FWCfgState *fw_cfg;
+static QTAILQ_HEAD(, Rom) roms = QTAILQ_HEAD_INITIALIZER(roms);
+
+static void rom_insert(Rom *rom)
+{
+    Rom *item;
+
+    if (roms_loaded) {
+        hw_error ("ROM images must be loaded at startup\n");
+    }
+
+    /* list is ordered by load address */
+    QTAILQ_FOREACH(item, &roms, next) {
+        if (rom->addr >= item->addr)
+            continue;
+        QTAILQ_INSERT_BEFORE(item, rom, next);
+        return;
+    }
+    QTAILQ_INSERT_TAIL(&roms, rom, next);
+}
+
+int rom_add_file(const char *file, const char *fw_dir,
+                 hwaddr addr, int32_t bootindex)
+{
+    Rom *rom;
+    int rc, fd = -1;
+    char devpath[100];
+
+    rom = g_malloc0(sizeof(*rom));
+    rom->name = g_strdup(file);
+    rom->path = qemu_find_file(QEMU_FILE_TYPE_BIOS, rom->name);
+    if (rom->path == NULL) {
+        rom->path = g_strdup(file);
+    }
+
+    fd = open(rom->path, O_RDONLY | O_BINARY);
+    if (fd == -1) {
+        fprintf(stderr, "Could not open option rom '%s': %s\n",
+                rom->path, strerror(errno));
+        goto err;
+    }
+
+    if (fw_dir) {
+        rom->fw_dir  = g_strdup(fw_dir);
+        rom->fw_file = g_strdup(file);
+    }
+    rom->addr     = addr;
+    rom->romsize  = lseek(fd, 0, SEEK_END);
+    rom->datasize = rom->romsize;
+    rom->data     = g_malloc0(rom->datasize);
+    lseek(fd, 0, SEEK_SET);
+    rc = read(fd, rom->data, rom->datasize);
+    if (rc != rom->datasize) {
+        fprintf(stderr, "rom: file %-20s: read error: rc=%d (expected %zd)\n",
+                rom->name, rc, rom->datasize);
+        goto err;
+    }
+    close(fd);
+    rom_insert(rom);
+    if (rom->fw_file && fw_cfg) {
+        const char *basename;
+        char fw_file_name[56];
+
+        basename = strrchr(rom->fw_file, '/');
+        if (basename) {
+            basename++;
+        } else {
+            basename = rom->fw_file;
+        }
+        snprintf(fw_file_name, sizeof(fw_file_name), "%s/%s", rom->fw_dir,
+                 basename);
+        fw_cfg_add_file(fw_cfg, fw_file_name, rom->data, rom->romsize);
+        snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);
+    } else {
+        snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr);
+    }
+
+    add_boot_device_path(bootindex, NULL, devpath);
+    return 0;
+
+err:
+    if (fd != -1)
+        close(fd);
+    g_free(rom->data);
+    g_free(rom->path);
+    g_free(rom->name);
+    g_free(rom);
+    return -1;
+}
+
+int rom_add_blob(const char *name, const void *blob, size_t len,
+                 hwaddr addr)
+{
+    Rom *rom;
+
+    rom           = g_malloc0(sizeof(*rom));
+    rom->name     = g_strdup(name);
+    rom->addr     = addr;
+    rom->romsize  = len;
+    rom->datasize = len;
+    rom->data     = g_malloc0(rom->datasize);
+    memcpy(rom->data, blob, len);
+    rom_insert(rom);
+    return 0;
+}
+
+/* This function is specific for elf program because we don't need to allocate
+ * all the rom. We just allocate the first part and the rest is just zeros. This
+ * is why romsize and datasize are different. Also, this function seize the
+ * memory ownership of "data", so we don't have to allocate and copy the buffer.
+ */
+int rom_add_elf_program(const char *name, void *data, size_t datasize,
+                        size_t romsize, hwaddr addr)
+{
+    Rom *rom;
+
+    rom           = g_malloc0(sizeof(*rom));
+    rom->name     = g_strdup(name);
+    rom->addr     = addr;
+    rom->datasize = datasize;
+    rom->romsize  = romsize;
+    rom->data     = data;
+    rom_insert(rom);
+    return 0;
+}
+
+int rom_add_vga(const char *file)
+{
+    return rom_add_file(file, "vgaroms", 0, -1);
+}
+
+int rom_add_option(const char *file, int32_t bootindex)
+{
+    return rom_add_file(file, "genroms", 0, bootindex);
+}
+
+static void rom_reset(void *unused)
+{
+    Rom *rom;
+
+    QTAILQ_FOREACH(rom, &roms, next) {
+        if (rom->fw_file) {
+            continue;
+        }
+        if (rom->data == NULL) {
+            continue;
+        }
+        cpu_physical_memory_write_rom(rom->addr, rom->data, rom->datasize);
+        if (rom->isrom) {
+            /* rom needs to be written only once */
+            g_free(rom->data);
+            rom->data = NULL;
+        }
+    }
+}
+
+int rom_load_all(void)
+{
+    hwaddr addr = 0;
+    MemoryRegionSection section;
+    Rom *rom;
+
+    QTAILQ_FOREACH(rom, &roms, next) {
+        if (rom->fw_file) {
+            continue;
+        }
+        if (addr > rom->addr) {
+            fprintf(stderr, "rom: requested regions overlap "
+                    "(rom %s. free=0x" TARGET_FMT_plx
+                    ", addr=0x" TARGET_FMT_plx ")\n",
+                    rom->name, addr, rom->addr);
+            return -1;
+        }
+        addr  = rom->addr;
+        addr += rom->romsize;
+        section = memory_region_find(get_system_memory(), rom->addr, 1);
+        rom->isrom = section.size && memory_region_is_rom(section.mr);
+    }
+    qemu_register_reset(rom_reset, NULL);
+    roms_loaded = 1;
+    return 0;
+}
+
+void rom_set_fw(void *f)
+{
+    fw_cfg = f;
+}
+
+static Rom *find_rom(hwaddr addr)
+{
+    Rom *rom;
+
+    QTAILQ_FOREACH(rom, &roms, next) {
+        if (rom->fw_file) {
+            continue;
+        }
+        if (rom->addr > addr) {
+            continue;
+        }
+        if (rom->addr + rom->romsize < addr) {
+            continue;
+        }
+        return rom;
+    }
+    return NULL;
+}
+
+/*
+ * Copies memory from registered ROMs to dest. Any memory that is contained in
+ * a ROM between addr and addr + size is copied. Note that this can involve
+ * multiple ROMs, which need not start at addr and need not end at addr + size.
+ */
+int rom_copy(uint8_t *dest, hwaddr addr, size_t size)
+{
+    hwaddr end = addr + size;
+    uint8_t *s, *d = dest;
+    size_t l = 0;
+    Rom *rom;
+
+    QTAILQ_FOREACH(rom, &roms, next) {
+        if (rom->fw_file) {
+            continue;
+        }
+        if (rom->addr + rom->romsize < addr) {
+            continue;
+        }
+        if (rom->addr > end) {
+            break;
+        }
+        if (!rom->data) {
+            continue;
+        }
+
+        d = dest + (rom->addr - addr);
+        s = rom->data;
+        l = rom->datasize;
+
+        if ((d + l) > (dest + size)) {
+            l = dest - d;
+        }
+
+        memcpy(d, s, l);
+
+        if (rom->romsize > rom->datasize) {
+            /* If datasize is less than romsize, it means that we didn't
+             * allocate all the ROM because the trailing data are only zeros.
+             */
+
+            d += l;
+            l = rom->romsize - rom->datasize;
+
+            if ((d + l) > (dest + size)) {
+                /* Rom size doesn't fit in the destination area. Adjust to avoid
+                 * overflow.
+                 */
+                l = dest - d;
+            }
+
+            if (l > 0) {
+                memset(d, 0x0, l);
+            }
+        }
+    }
+
+    return (d + l) - dest;
+}
+
+void *rom_ptr(hwaddr addr)
+{
+    Rom *rom;
+
+    rom = find_rom(addr);
+    if (!rom || !rom->data)
+        return NULL;
+    return rom->data + (addr - rom->addr);
+}
+
+void do_info_roms(Monitor *mon, const QDict *qdict)
+{
+    Rom *rom;
+
+    QTAILQ_FOREACH(rom, &roms, next) {
+        if (!rom->fw_file) {
+            monitor_printf(mon, "addr=" TARGET_FMT_plx
+                           " size=0x%06zx mem=%s name=\"%s\"\n",
+                           rom->addr, rom->romsize,
+                           rom->isrom ? "rom" : "ram",
+                           rom->name);
+        } else {
+            monitor_printf(mon, "fw=%s/%s"
+                           " size=0x%06zx name=\"%s\"\n",
+                           rom->fw_dir,
+                           rom->fw_file,
+                           rom->romsize,
+                           rom->name);
+        }
+    }
+}
diff --git a/hw/core/null-machine.c b/hw/core/null-machine.c
new file mode 100644 (file)
index 0000000..bdf109f
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Empty machine
+ *
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+
+static void machine_none_init(QEMUMachineInitArgs *args)
+{
+}
+
+static QEMUMachine machine_none = {
+    .name = "none",
+    .desc = "empty machine",
+    .init = machine_none_init,
+    .max_cpus = 0,
+    DEFAULT_MACHINE_OPTIONS,
+};
+
+static void register_machines(void)
+{
+    qemu_register_machine(&machine_none);
+}
+
+machine_init(register_machines);
+
diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c
new file mode 100644 (file)
index 0000000..4bc96c9
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * General purpose implementation of a simple periodic countdown timer.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licensed under the GNU LGPL.
+ */
+#include "hw/hw.h"
+#include "qemu/timer.h"
+#include "hw/ptimer.h"
+#include "qemu/host-utils.h"
+
+struct ptimer_state
+{
+    uint8_t enabled; /* 0 = disabled, 1 = periodic, 2 = oneshot.  */
+    uint64_t limit;
+    uint64_t delta;
+    uint32_t period_frac;
+    int64_t period;
+    int64_t last_event;
+    int64_t next_event;
+    QEMUBH *bh;
+    QEMUTimer *timer;
+};
+
+/* Use a bottom-half routine to avoid reentrancy issues.  */
+static void ptimer_trigger(ptimer_state *s)
+{
+    if (s->bh) {
+        qemu_bh_schedule(s->bh);
+    }
+}
+
+static void ptimer_reload(ptimer_state *s)
+{
+    if (s->delta == 0) {
+        ptimer_trigger(s);
+        s->delta = s->limit;
+    }
+    if (s->delta == 0 || s->period == 0) {
+        fprintf(stderr, "Timer with period zero, disabling\n");
+        s->enabled = 0;
+        return;
+    }
+
+    s->last_event = s->next_event;
+    s->next_event = s->last_event + s->delta * s->period;
+    if (s->period_frac) {
+        s->next_event += ((int64_t)s->period_frac * s->delta) >> 32;
+    }
+    qemu_mod_timer(s->timer, s->next_event);
+}
+
+static void ptimer_tick(void *opaque)
+{
+    ptimer_state *s = (ptimer_state *)opaque;
+    ptimer_trigger(s);
+    s->delta = 0;
+    if (s->enabled == 2) {
+        s->enabled = 0;
+    } else {
+        ptimer_reload(s);
+    }
+}
+
+uint64_t ptimer_get_count(ptimer_state *s)
+{
+    int64_t now;
+    uint64_t counter;
+
+    if (s->enabled) {
+        now = qemu_get_clock_ns(vm_clock);
+        /* Figure out the current counter value.  */
+        if (now - s->next_event > 0
+            || s->period == 0) {
+            /* Prevent timer underflowing if it should already have
+               triggered.  */
+            counter = 0;
+        } else {
+            uint64_t rem;
+            uint64_t div;
+            int clz1, clz2;
+            int shift;
+
+            /* We need to divide time by period, where time is stored in
+               rem (64-bit integer) and period is stored in period/period_frac
+               (64.32 fixed point).
+              
+               Doing full precision division is hard, so scale values and
+               do a 64-bit division.  The result should be rounded down,
+               so that the rounding error never causes the timer to go
+               backwards.
+            */
+
+            rem = s->next_event - now;
+            div = s->period;
+
+            clz1 = clz64(rem);
+            clz2 = clz64(div);
+            shift = clz1 < clz2 ? clz1 : clz2;
+
+            rem <<= shift;
+            div <<= shift;
+            if (shift >= 32) {
+                div |= ((uint64_t)s->period_frac << (shift - 32));
+            } else {
+                if (shift != 0)
+                    div |= (s->period_frac >> (32 - shift));
+                /* Look at remaining bits of period_frac and round div up if 
+                   necessary.  */
+                if ((uint32_t)(s->period_frac << shift))
+                    div += 1;
+            }
+            counter = rem / div;
+        }
+    } else {
+        counter = s->delta;
+    }
+    return counter;
+}
+
+void ptimer_set_count(ptimer_state *s, uint64_t count)
+{
+    s->delta = count;
+    if (s->enabled) {
+        s->next_event = qemu_get_clock_ns(vm_clock);
+        ptimer_reload(s);
+    }
+}
+
+void ptimer_run(ptimer_state *s, int oneshot)
+{
+    if (s->enabled) {
+        return;
+    }
+    if (s->period == 0) {
+        fprintf(stderr, "Timer with period zero, disabling\n");
+        return;
+    }
+    s->enabled = oneshot ? 2 : 1;
+    s->next_event = qemu_get_clock_ns(vm_clock);
+    ptimer_reload(s);
+}
+
+/* Pause a timer.  Note that this may cause it to "lose" time, even if it
+   is immediately restarted.  */
+void ptimer_stop(ptimer_state *s)
+{
+    if (!s->enabled)
+        return;
+
+    s->delta = ptimer_get_count(s);
+    qemu_del_timer(s->timer);
+    s->enabled = 0;
+}
+
+/* Set counter increment interval in nanoseconds.  */
+void ptimer_set_period(ptimer_state *s, int64_t period)
+{
+    s->period = period;
+    s->period_frac = 0;
+    if (s->enabled) {
+        s->next_event = qemu_get_clock_ns(vm_clock);
+        ptimer_reload(s);
+    }
+}
+
+/* Set counter frequency in Hz.  */
+void ptimer_set_freq(ptimer_state *s, uint32_t freq)
+{
+    s->period = 1000000000ll / freq;
+    s->period_frac = (1000000000ll << 32) / freq;
+    if (s->enabled) {
+        s->next_event = qemu_get_clock_ns(vm_clock);
+        ptimer_reload(s);
+    }
+}
+
+/* Set the initial countdown value.  If reload is nonzero then also set
+   count = limit.  */
+void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload)
+{
+    /*
+     * Artificially limit timeout rate to something
+     * achievable under QEMU.  Otherwise, QEMU spends all
+     * its time generating timer interrupts, and there
+     * is no forward progress.
+     * About ten microseconds is the fastest that really works
+     * on the current generation of host machines.
+     */
+
+    if (limit * s->period < 10000 && s->period) {
+        limit = 10000 / s->period;
+    }
+
+    s->limit = limit;
+    if (reload)
+        s->delta = limit;
+    if (s->enabled && reload) {
+        s->next_event = qemu_get_clock_ns(vm_clock);
+        ptimer_reload(s);
+    }
+}
+
+const VMStateDescription vmstate_ptimer = {
+    .name = "ptimer",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(enabled, ptimer_state),
+        VMSTATE_UINT64(limit, ptimer_state),
+        VMSTATE_UINT64(delta, ptimer_state),
+        VMSTATE_UINT32(period_frac, ptimer_state),
+        VMSTATE_INT64(period, ptimer_state),
+        VMSTATE_INT64(last_event, ptimer_state),
+        VMSTATE_INT64(next_event, ptimer_state),
+        VMSTATE_TIMER(timer, ptimer_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+ptimer_state *ptimer_init(QEMUBH *bh)
+{
+    ptimer_state *s;
+
+    s = (ptimer_state *)g_malloc0(sizeof(ptimer_state));
+    s->bh = bh;
+    s->timer = qemu_new_timer_ns(vm_clock, ptimer_tick, s);
+    return s;
+}
diff --git a/hw/core/qdev-addr.c b/hw/core/qdev-addr.c
new file mode 100644 (file)
index 0000000..80a38bb
--- /dev/null
@@ -0,0 +1,78 @@
+#include "hw/qdev.h"
+#include "hw/qdev-addr.h"
+#include "exec/hwaddr.h"
+#include "qapi/qmp/qerror.h"
+#include "qapi/visitor.h"
+
+/* --- target physical address --- */
+
+static int parse_taddr(DeviceState *dev, Property *prop, const char *str)
+{
+    hwaddr *ptr = qdev_get_prop_ptr(dev, prop);
+
+    *ptr = strtoull(str, NULL, 16);
+    return 0;
+}
+
+static int print_taddr(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+    hwaddr *ptr = qdev_get_prop_ptr(dev, prop);
+    return snprintf(dest, len, "0x" TARGET_FMT_plx, *ptr);
+}
+
+static void get_taddr(Object *obj, Visitor *v, void *opaque,
+                      const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    hwaddr *ptr = qdev_get_prop_ptr(dev, prop);
+    int64_t value;
+
+    value = *ptr;
+    visit_type_int64(v, &value, name, errp);
+}
+
+static void set_taddr(Object *obj, Visitor *v, void *opaque,
+                      const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    hwaddr *ptr = qdev_get_prop_ptr(dev, prop);
+    Error *local_err = NULL;
+    int64_t value;
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_int64(v, &value, name, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+    if ((uint64_t)value <= (uint64_t) ~(hwaddr)0) {
+        *ptr = value;
+    } else {
+        error_set(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE,
+                  dev->id?:"", name, value, (uint64_t) 0,
+                  (uint64_t) ~(hwaddr)0);
+    }
+}
+
+
+PropertyInfo qdev_prop_taddr = {
+    .name  = "taddr",
+    .parse = parse_taddr,
+    .print = print_taddr,
+    .get   = get_taddr,
+    .set   = set_taddr,
+};
+
+void qdev_prop_set_taddr(DeviceState *dev, const char *name, hwaddr value)
+{
+    Error *errp = NULL;
+    object_property_set_int(OBJECT(dev), value, name, &errp);
+    assert(!errp);
+
+}
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
new file mode 100644 (file)
index 0000000..8c2e152
--- /dev/null
@@ -0,0 +1,391 @@
+/*
+ * qdev property parsing and global properties
+ * (parts specific for qemu-system-*)
+ *
+ * This file is based on code from hw/qdev-properties.c from
+ * commit 074a86fccd185616469dfcdc0e157f438aebba18,
+ * Copyright (c) Gerd Hoffmann <kraxel@redhat.com> and other contributors.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "net/net.h"
+#include "hw/qdev.h"
+#include "qapi/qmp/qerror.h"
+#include "sysemu/blockdev.h"
+#include "hw/block/block.h"
+#include "net/hub.h"
+#include "qapi/visitor.h"
+#include "char/char.h"
+
+static void get_pointer(Object *obj, Visitor *v, Property *prop,
+                        const char *(*print)(void *ptr),
+                        const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    void **ptr = qdev_get_prop_ptr(dev, prop);
+    char *p;
+
+    p = (char *) (*ptr ? print(*ptr) : "");
+    visit_type_str(v, &p, name, errp);
+}
+
+static void set_pointer(Object *obj, Visitor *v, Property *prop,
+                        int (*parse)(DeviceState *dev, const char *str,
+                                     void **ptr),
+                        const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Error *local_err = NULL;
+    void **ptr = qdev_get_prop_ptr(dev, prop);
+    char *str;
+    int ret;
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_str(v, &str, name, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+    if (!*str) {
+        g_free(str);
+        *ptr = NULL;
+        return;
+    }
+    ret = parse(dev, str, ptr);
+    error_set_from_qdev_prop_error(errp, ret, dev, prop, str);
+    g_free(str);
+}
+
+/* --- drive --- */
+
+static int parse_drive(DeviceState *dev, const char *str, void **ptr)
+{
+    BlockDriverState *bs;
+
+    bs = bdrv_find(str);
+    if (bs == NULL) {
+        return -ENOENT;
+    }
+    if (bdrv_attach_dev(bs, dev) < 0) {
+        return -EEXIST;
+    }
+    *ptr = bs;
+    return 0;
+}
+
+static void release_drive(Object *obj, const char *name, void *opaque)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    BlockDriverState **ptr = qdev_get_prop_ptr(dev, prop);
+
+    if (*ptr) {
+        bdrv_detach_dev(*ptr, dev);
+        blockdev_auto_del(*ptr);
+    }
+}
+
+static const char *print_drive(void *ptr)
+{
+    return bdrv_get_device_name(ptr);
+}
+
+static void get_drive(Object *obj, Visitor *v, void *opaque,
+                      const char *name, Error **errp)
+{
+    get_pointer(obj, v, opaque, print_drive, name, errp);
+}
+
+static void set_drive(Object *obj, Visitor *v, void *opaque,
+                      const char *name, Error **errp)
+{
+    set_pointer(obj, v, opaque, parse_drive, name, errp);
+}
+
+PropertyInfo qdev_prop_drive = {
+    .name  = "drive",
+    .get   = get_drive,
+    .set   = set_drive,
+    .release = release_drive,
+};
+
+/* --- character device --- */
+
+static int parse_chr(DeviceState *dev, const char *str, void **ptr)
+{
+    CharDriverState *chr = qemu_chr_find(str);
+    if (chr == NULL) {
+        return -ENOENT;
+    }
+    if (qemu_chr_fe_claim(chr) != 0) {
+        return -EEXIST;
+    }
+    *ptr = chr;
+    return 0;
+}
+
+static void release_chr(Object *obj, const char *name, void *opaque)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    CharDriverState **ptr = qdev_get_prop_ptr(dev, prop);
+    CharDriverState *chr = *ptr;
+
+    if (chr) {
+        qemu_chr_add_handlers(chr, NULL, NULL, NULL, NULL);
+        qemu_chr_fe_release(chr);
+    }
+}
+
+
+static const char *print_chr(void *ptr)
+{
+    CharDriverState *chr = ptr;
+
+    return chr->label ? chr->label : "";
+}
+
+static void get_chr(Object *obj, Visitor *v, void *opaque,
+                    const char *name, Error **errp)
+{
+    get_pointer(obj, v, opaque, print_chr, name, errp);
+}
+
+static void set_chr(Object *obj, Visitor *v, void *opaque,
+                    const char *name, Error **errp)
+{
+    set_pointer(obj, v, opaque, parse_chr, name, errp);
+}
+
+PropertyInfo qdev_prop_chr = {
+    .name  = "chr",
+    .get   = get_chr,
+    .set   = set_chr,
+    .release = release_chr,
+};
+
+/* --- netdev device --- */
+
+static int parse_netdev(DeviceState *dev, const char *str, void **ptr)
+{
+    NICPeers *peers_ptr = (NICPeers *)ptr;
+    NICConf *conf = container_of(peers_ptr, NICConf, peers);
+    NetClientState **ncs = peers_ptr->ncs;
+    NetClientState *peers[MAX_QUEUE_NUM];
+    int queues, i = 0;
+    int ret;
+
+    queues = qemu_find_net_clients_except(str, peers,
+                                          NET_CLIENT_OPTIONS_KIND_NIC,
+                                          MAX_QUEUE_NUM);
+    if (queues == 0) {
+        ret = -ENOENT;
+        goto err;
+    }
+
+    if (queues > MAX_QUEUE_NUM) {
+        ret = -E2BIG;
+        goto err;
+    }
+
+    for (i = 0; i < queues; i++) {
+        if (peers[i] == NULL) {
+            ret = -ENOENT;
+            goto err;
+        }
+
+        if (peers[i]->peer) {
+            ret = -EEXIST;
+            goto err;
+        }
+
+        ncs[i] = peers[i];
+        ncs[i]->queue_index = i;
+    }
+
+    conf->queues = queues;
+
+    return 0;
+
+err:
+    return ret;
+}
+
+static const char *print_netdev(void *ptr)
+{
+    NetClientState *netdev = ptr;
+
+    return netdev->name ? netdev->name : "";
+}
+
+static void get_netdev(Object *obj, Visitor *v, void *opaque,
+                       const char *name, Error **errp)
+{
+    get_pointer(obj, v, opaque, print_netdev, name, errp);
+}
+
+static void set_netdev(Object *obj, Visitor *v, void *opaque,
+                       const char *name, Error **errp)
+{
+    set_pointer(obj, v, opaque, parse_netdev, name, errp);
+}
+
+PropertyInfo qdev_prop_netdev = {
+    .name  = "netdev",
+    .get   = get_netdev,
+    .set   = set_netdev,
+};
+
+/* --- vlan --- */
+
+static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+    NetClientState **ptr = qdev_get_prop_ptr(dev, prop);
+
+    if (*ptr) {
+        int id;
+        if (!net_hub_id_for_client(*ptr, &id)) {
+            return snprintf(dest, len, "%d", id);
+        }
+    }
+
+    return snprintf(dest, len, "<null>");
+}
+
+static void get_vlan(Object *obj, Visitor *v, void *opaque,
+                     const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    NetClientState **ptr = qdev_get_prop_ptr(dev, prop);
+    int32_t id = -1;
+
+    if (*ptr) {
+        int hub_id;
+        if (!net_hub_id_for_client(*ptr, &hub_id)) {
+            id = hub_id;
+        }
+    }
+
+    visit_type_int32(v, &id, name, errp);
+}
+
+static void set_vlan(Object *obj, Visitor *v, void *opaque,
+                     const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop);
+    NetClientState **ptr = &peers_ptr->ncs[0];
+    Error *local_err = NULL;
+    int32_t id;
+    NetClientState *hubport;
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_int32(v, &id, name, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+    if (id == -1) {
+        *ptr = NULL;
+        return;
+    }
+
+    hubport = net_hub_port_find(id);
+    if (!hubport) {
+        error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+                  name, prop->info->name);
+        return;
+    }
+    *ptr = hubport;
+}
+
+PropertyInfo qdev_prop_vlan = {
+    .name  = "vlan",
+    .print = print_vlan,
+    .get   = get_vlan,
+    .set   = set_vlan,
+};
+
+int qdev_prop_set_drive(DeviceState *dev, const char *name,
+                        BlockDriverState *value)
+{
+    Error *errp = NULL;
+    const char *bdrv_name = value ? bdrv_get_device_name(value) : "";
+    object_property_set_str(OBJECT(dev), bdrv_name,
+                            name, &errp);
+    if (errp) {
+        qerror_report_err(errp);
+        error_free(errp);
+        return -1;
+    }
+    return 0;
+}
+
+void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name,
+                                BlockDriverState *value)
+{
+    if (qdev_prop_set_drive(dev, name, value) < 0) {
+        exit(1);
+    }
+}
+void qdev_prop_set_chr(DeviceState *dev, const char *name,
+                       CharDriverState *value)
+{
+    Error *errp = NULL;
+    assert(!value || value->label);
+    object_property_set_str(OBJECT(dev),
+                            value ? value->label : "", name, &errp);
+    assert_no_error(errp);
+}
+
+void qdev_prop_set_netdev(DeviceState *dev, const char *name,
+                          NetClientState *value)
+{
+    Error *errp = NULL;
+    assert(!value || value->name);
+    object_property_set_str(OBJECT(dev),
+                            value ? value->name : "", name, &errp);
+    assert_no_error(errp);
+}
+
+void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
+{
+    qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a);
+    if (nd->netdev) {
+        qdev_prop_set_netdev(dev, "netdev", nd->netdev);
+    }
+    if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED &&
+        object_property_find(OBJECT(dev), "vectors", NULL)) {
+        qdev_prop_set_uint32(dev, "vectors", nd->nvectors);
+    }
+    nd->instantiated = 1;
+}
+
+static int qdev_add_one_global(QemuOpts *opts, void *opaque)
+{
+    GlobalProperty *g;
+
+    g = g_malloc0(sizeof(*g));
+    g->driver   = qemu_opt_get(opts, "driver");
+    g->property = qemu_opt_get(opts, "property");
+    g->value    = qemu_opt_get(opts, "value");
+    qdev_prop_register_global(g);
+    return 0;
+}
+
+void qemu_add_globals(void)
+{
+    qemu_opts_foreach(qemu_find_opts("global"), qdev_add_one_global, NULL, 0);
+}
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
new file mode 100644 (file)
index 0000000..9a0872d
--- /dev/null
@@ -0,0 +1,1092 @@
+#include "net/net.h"
+#include "hw/qdev.h"
+#include "qapi/qmp/qerror.h"
+#include "sysemu/blockdev.h"
+#include "hw/block/block.h"
+#include "net/hub.h"
+#include "qapi/visitor.h"
+#include "char/char.h"
+
+void qdev_prop_set_after_realize(DeviceState *dev, const char *name,
+                                  Error **errp)
+{
+    if (dev->id) {
+        error_setg(errp, "Attempt to set property '%s' on device '%s' "
+                   "(type '%s') after it was realized", name, dev->id,
+                   object_get_typename(OBJECT(dev)));
+    } else {
+        error_setg(errp, "Attempt to set property '%s' on anonymous device "
+                   "(type '%s') after it was realized", name,
+                   object_get_typename(OBJECT(dev)));
+    }
+}
+
+void *qdev_get_prop_ptr(DeviceState *dev, Property *prop)
+{
+    void *ptr = dev;
+    ptr += prop->offset;
+    return ptr;
+}
+
+static void get_enum(Object *obj, Visitor *v, void *opaque,
+                     const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    int *ptr = qdev_get_prop_ptr(dev, prop);
+
+    visit_type_enum(v, ptr, prop->info->enum_table,
+                    prop->info->name, prop->name, errp);
+}
+
+static void set_enum(Object *obj, Visitor *v, void *opaque,
+                     const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    int *ptr = qdev_get_prop_ptr(dev, prop);
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_enum(v, ptr, prop->info->enum_table,
+                    prop->info->name, prop->name, errp);
+}
+
+/* Bit */
+
+static uint32_t qdev_get_prop_mask(Property *prop)
+{
+    assert(prop->info == &qdev_prop_bit);
+    return 0x1 << prop->bitnr;
+}
+
+static void bit_prop_set(DeviceState *dev, Property *props, bool val)
+{
+    uint32_t *p = qdev_get_prop_ptr(dev, props);
+    uint32_t mask = qdev_get_prop_mask(props);
+    if (val) {
+        *p |= mask;
+    } else {
+        *p &= ~mask;
+    }
+}
+
+static int print_bit(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+    uint32_t *p = qdev_get_prop_ptr(dev, prop);
+    return snprintf(dest, len, (*p & qdev_get_prop_mask(prop)) ? "on" : "off");
+}
+
+static void get_bit(Object *obj, Visitor *v, void *opaque,
+                    const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    uint32_t *p = qdev_get_prop_ptr(dev, prop);
+    bool value = (*p & qdev_get_prop_mask(prop)) != 0;
+
+    visit_type_bool(v, &value, name, errp);
+}
+
+static void set_bit(Object *obj, Visitor *v, void *opaque,
+                    const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    Error *local_err = NULL;
+    bool value;
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_bool(v, &value, name, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+    bit_prop_set(dev, prop, value);
+}
+
+PropertyInfo qdev_prop_bit = {
+    .name  = "boolean",
+    .legacy_name  = "on/off",
+    .print = print_bit,
+    .get   = get_bit,
+    .set   = set_bit,
+};
+
+/* --- 8bit integer --- */
+
+static void get_uint8(Object *obj, Visitor *v, void *opaque,
+                      const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+    visit_type_uint8(v, ptr, name, errp);
+}
+
+static void set_uint8(Object *obj, Visitor *v, void *opaque,
+                      const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_uint8(v, ptr, name, errp);
+}
+
+PropertyInfo qdev_prop_uint8 = {
+    .name  = "uint8",
+    .get   = get_uint8,
+    .set   = set_uint8,
+};
+
+/* --- 8bit hex value --- */
+
+static int parse_hex8(DeviceState *dev, Property *prop, const char *str)
+{
+    uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
+    char *end;
+
+    if (str[0] != '0' || str[1] != 'x') {
+        return -EINVAL;
+    }
+
+    *ptr = strtoul(str, &end, 16);
+    if ((*end != '\0') || (end == str)) {
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static int print_hex8(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+    uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
+    return snprintf(dest, len, "0x%" PRIx8, *ptr);
+}
+
+PropertyInfo qdev_prop_hex8 = {
+    .name  = "uint8",
+    .legacy_name  = "hex8",
+    .parse = parse_hex8,
+    .print = print_hex8,
+    .get   = get_uint8,
+    .set   = set_uint8,
+};
+
+/* --- 16bit integer --- */
+
+static void get_uint16(Object *obj, Visitor *v, void *opaque,
+                       const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    uint16_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+    visit_type_uint16(v, ptr, name, errp);
+}
+
+static void set_uint16(Object *obj, Visitor *v, void *opaque,
+                       const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    uint16_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_uint16(v, ptr, name, errp);
+}
+
+PropertyInfo qdev_prop_uint16 = {
+    .name  = "uint16",
+    .get   = get_uint16,
+    .set   = set_uint16,
+};
+
+/* --- 32bit integer --- */
+
+static void get_uint32(Object *obj, Visitor *v, void *opaque,
+                       const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+    visit_type_uint32(v, ptr, name, errp);
+}
+
+static void set_uint32(Object *obj, Visitor *v, void *opaque,
+                       const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_uint32(v, ptr, name, errp);
+}
+
+static void get_int32(Object *obj, Visitor *v, void *opaque,
+                      const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    int32_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+    visit_type_int32(v, ptr, name, errp);
+}
+
+static void set_int32(Object *obj, Visitor *v, void *opaque,
+                      const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    int32_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_int32(v, ptr, name, errp);
+}
+
+PropertyInfo qdev_prop_uint32 = {
+    .name  = "uint32",
+    .get   = get_uint32,
+    .set   = set_uint32,
+};
+
+PropertyInfo qdev_prop_int32 = {
+    .name  = "int32",
+    .get   = get_int32,
+    .set   = set_int32,
+};
+
+/* --- 32bit hex value --- */
+
+static int parse_hex32(DeviceState *dev, Property *prop, const char *str)
+{
+    uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
+    char *end;
+
+    if (str[0] != '0' || str[1] != 'x') {
+        return -EINVAL;
+    }
+
+    *ptr = strtoul(str, &end, 16);
+    if ((*end != '\0') || (end == str)) {
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static int print_hex32(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+    uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
+    return snprintf(dest, len, "0x%" PRIx32, *ptr);
+}
+
+PropertyInfo qdev_prop_hex32 = {
+    .name  = "uint32",
+    .legacy_name  = "hex32",
+    .parse = parse_hex32,
+    .print = print_hex32,
+    .get   = get_uint32,
+    .set   = set_uint32,
+};
+
+/* --- 64bit integer --- */
+
+static void get_uint64(Object *obj, Visitor *v, void *opaque,
+                       const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+    visit_type_uint64(v, ptr, name, errp);
+}
+
+static void set_uint64(Object *obj, Visitor *v, void *opaque,
+                       const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_uint64(v, ptr, name, errp);
+}
+
+PropertyInfo qdev_prop_uint64 = {
+    .name  = "uint64",
+    .get   = get_uint64,
+    .set   = set_uint64,
+};
+
+/* --- 64bit hex value --- */
+
+static int parse_hex64(DeviceState *dev, Property *prop, const char *str)
+{
+    uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
+    char *end;
+
+    if (str[0] != '0' || str[1] != 'x') {
+        return -EINVAL;
+    }
+
+    *ptr = strtoull(str, &end, 16);
+    if ((*end != '\0') || (end == str)) {
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static int print_hex64(DeviceState *dev, Property *prop, char *dest, size_t len)
+{
+    uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
+    return snprintf(dest, len, "0x%" PRIx64, *ptr);
+}
+
+PropertyInfo qdev_prop_hex64 = {
+    .name  = "uint64",
+    .legacy_name  = "hex64",
+    .parse = parse_hex64,
+    .print = print_hex64,
+    .get   = get_uint64,
+    .set   = set_uint64,
+};
+
+/* --- string --- */
+
+static void release_string(Object *obj, const char *name, void *opaque)
+{
+    Property *prop = opaque;
+    g_free(*(char **)qdev_get_prop_ptr(DEVICE(obj), prop));
+}
+
+static int print_string(DeviceState *dev, Property *prop, char *dest,
+                        size_t len)
+{
+    char **ptr = qdev_get_prop_ptr(dev, prop);
+    if (!*ptr) {
+        return snprintf(dest, len, "<null>");
+    }
+    return snprintf(dest, len, "\"%s\"", *ptr);
+}
+
+static void get_string(Object *obj, Visitor *v, void *opaque,
+                       const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    char **ptr = qdev_get_prop_ptr(dev, prop);
+
+    if (!*ptr) {
+        char *str = (char *)"";
+        visit_type_str(v, &str, name, errp);
+    } else {
+        visit_type_str(v, ptr, name, errp);
+    }
+}
+
+static void set_string(Object *obj, Visitor *v, void *opaque,
+                       const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    char **ptr = qdev_get_prop_ptr(dev, prop);
+    Error *local_err = NULL;
+    char *str;
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_str(v, &str, name, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+    if (*ptr) {
+        g_free(*ptr);
+    }
+    *ptr = str;
+}
+
+PropertyInfo qdev_prop_string = {
+    .name  = "string",
+    .print = print_string,
+    .release = release_string,
+    .get   = get_string,
+    .set   = set_string,
+};
+
+/* --- pointer --- */
+
+/* Not a proper property, just for dirty hacks.  TODO Remove it!  */
+PropertyInfo qdev_prop_ptr = {
+    .name  = "ptr",
+};
+
+/* --- mac address --- */
+
+/*
+ * accepted syntax versions:
+ *   01:02:03:04:05:06
+ *   01-02-03-04-05-06
+ */
+static void get_mac(Object *obj, Visitor *v, void *opaque,
+                    const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    MACAddr *mac = qdev_get_prop_ptr(dev, prop);
+    char buffer[2 * 6 + 5 + 1];
+    char *p = buffer;
+
+    snprintf(buffer, sizeof(buffer), "%02x:%02x:%02x:%02x:%02x:%02x",
+             mac->a[0], mac->a[1], mac->a[2],
+             mac->a[3], mac->a[4], mac->a[5]);
+
+    visit_type_str(v, &p, name, errp);
+}
+
+static void set_mac(Object *obj, Visitor *v, void *opaque,
+                    const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    MACAddr *mac = qdev_get_prop_ptr(dev, prop);
+    Error *local_err = NULL;
+    int i, pos;
+    char *str, *p;
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_str(v, &str, name, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    for (i = 0, pos = 0; i < 6; i++, pos += 3) {
+        if (!qemu_isxdigit(str[pos])) {
+            goto inval;
+        }
+        if (!qemu_isxdigit(str[pos+1])) {
+            goto inval;
+        }
+        if (i == 5) {
+            if (str[pos+2] != '\0') {
+                goto inval;
+            }
+        } else {
+            if (str[pos+2] != ':' && str[pos+2] != '-') {
+                goto inval;
+            }
+        }
+        mac->a[i] = strtol(str+pos, &p, 16);
+    }
+    g_free(str);
+    return;
+
+inval:
+    error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str);
+    g_free(str);
+}
+
+PropertyInfo qdev_prop_macaddr = {
+    .name  = "macaddr",
+    .get   = get_mac,
+    .set   = set_mac,
+};
+
+/* --- lost tick policy --- */
+
+static const char *lost_tick_policy_table[LOST_TICK_MAX+1] = {
+    [LOST_TICK_DISCARD] = "discard",
+    [LOST_TICK_DELAY] = "delay",
+    [LOST_TICK_MERGE] = "merge",
+    [LOST_TICK_SLEW] = "slew",
+    [LOST_TICK_MAX] = NULL,
+};
+
+QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int));
+
+PropertyInfo qdev_prop_losttickpolicy = {
+    .name  = "LostTickPolicy",
+    .enum_table  = lost_tick_policy_table,
+    .get   = get_enum,
+    .set   = set_enum,
+};
+
+/* --- BIOS CHS translation */
+
+static const char *bios_chs_trans_table[] = {
+    [BIOS_ATA_TRANSLATION_AUTO] = "auto",
+    [BIOS_ATA_TRANSLATION_NONE] = "none",
+    [BIOS_ATA_TRANSLATION_LBA]  = "lba",
+};
+
+PropertyInfo qdev_prop_bios_chs_trans = {
+    .name = "bios-chs-trans",
+    .enum_table = bios_chs_trans_table,
+    .get = get_enum,
+    .set = set_enum,
+};
+
+/* --- pci address --- */
+
+/*
+ * bus-local address, i.e. "$slot" or "$slot.$fn"
+ */
+static void set_pci_devfn(Object *obj, Visitor *v, void *opaque,
+                          const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    int32_t value, *ptr = qdev_get_prop_ptr(dev, prop);
+    unsigned int slot, fn, n;
+    Error *local_err = NULL;
+    char *str;
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_str(v, &str, name, &local_err);
+    if (local_err) {
+        error_free(local_err);
+        local_err = NULL;
+        visit_type_int32(v, &value, name, &local_err);
+        if (local_err) {
+            error_propagate(errp, local_err);
+        } else if (value < -1 || value > 255) {
+            error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
+                      "pci_devfn");
+        } else {
+            *ptr = value;
+        }
+        return;
+    }
+
+    if (sscanf(str, "%x.%x%n", &slot, &fn, &n) != 2) {
+        fn = 0;
+        if (sscanf(str, "%x%n", &slot, &n) != 1) {
+            goto invalid;
+        }
+    }
+    if (str[n] != '\0' || fn > 7 || slot > 31) {
+        goto invalid;
+    }
+    *ptr = slot << 3 | fn;
+    g_free(str);
+    return;
+
+invalid:
+    error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str);
+    g_free(str);
+}
+
+static int print_pci_devfn(DeviceState *dev, Property *prop, char *dest,
+                           size_t len)
+{
+    int32_t *ptr = qdev_get_prop_ptr(dev, prop);
+
+    if (*ptr == -1) {
+        return snprintf(dest, len, "<unset>");
+    } else {
+        return snprintf(dest, len, "%02x.%x", *ptr >> 3, *ptr & 7);
+    }
+}
+
+PropertyInfo qdev_prop_pci_devfn = {
+    .name  = "int32",
+    .legacy_name  = "pci-devfn",
+    .print = print_pci_devfn,
+    .get   = get_int32,
+    .set   = set_pci_devfn,
+};
+
+/* --- blocksize --- */
+
+static void set_blocksize(Object *obj, Visitor *v, void *opaque,
+                          const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    uint16_t value, *ptr = qdev_get_prop_ptr(dev, prop);
+    Error *local_err = NULL;
+    const int64_t min = 512;
+    const int64_t max = 32768;
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_uint16(v, &value, name, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+    if (value < min || value > max) {
+        error_set(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE,
+                  dev->id?:"", name, (int64_t)value, min, max);
+        return;
+    }
+
+    /* We rely on power-of-2 blocksizes for bitmasks */
+    if ((value & (value - 1)) != 0) {
+        error_set(errp, QERR_PROPERTY_VALUE_NOT_POWER_OF_2,
+                  dev->id?:"", name, (int64_t)value);
+        return;
+    }
+
+    *ptr = value;
+}
+
+PropertyInfo qdev_prop_blocksize = {
+    .name  = "blocksize",
+    .get   = get_uint16,
+    .set   = set_blocksize,
+};
+
+/* --- pci host address --- */
+
+static void get_pci_host_devaddr(Object *obj, Visitor *v, void *opaque,
+                                 const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    PCIHostDeviceAddress *addr = qdev_get_prop_ptr(dev, prop);
+    char buffer[] = "xxxx:xx:xx.x";
+    char *p = buffer;
+    int rc = 0;
+
+    rc = snprintf(buffer, sizeof(buffer), "%04x:%02x:%02x.%d",
+                  addr->domain, addr->bus, addr->slot, addr->function);
+    assert(rc == sizeof(buffer) - 1);
+
+    visit_type_str(v, &p, name, errp);
+}
+
+/*
+ * Parse [<domain>:]<bus>:<slot>.<func>
+ *   if <domain> is not supplied, it's assumed to be 0.
+ */
+static void set_pci_host_devaddr(Object *obj, Visitor *v, void *opaque,
+                                 const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    PCIHostDeviceAddress *addr = qdev_get_prop_ptr(dev, prop);
+    Error *local_err = NULL;
+    char *str, *p;
+    char *e;
+    unsigned long val;
+    unsigned long dom = 0, bus = 0;
+    unsigned int slot = 0, func = 0;
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_str(v, &str, name, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    p = str;
+    val = strtoul(p, &e, 16);
+    if (e == p || *e != ':') {
+        goto inval;
+    }
+    bus = val;
+
+    p = e + 1;
+    val = strtoul(p, &e, 16);
+    if (e == p) {
+        goto inval;
+    }
+    if (*e == ':') {
+        dom = bus;
+        bus = val;
+        p = e + 1;
+        val = strtoul(p, &e, 16);
+        if (e == p) {
+            goto inval;
+        }
+    }
+    slot = val;
+
+    if (*e != '.') {
+        goto inval;
+    }
+    p = e + 1;
+    val = strtoul(p, &e, 10);
+    if (e == p) {
+        goto inval;
+    }
+    func = val;
+
+    if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7) {
+        goto inval;
+    }
+
+    if (*e) {
+        goto inval;
+    }
+
+    addr->domain = dom;
+    addr->bus = bus;
+    addr->slot = slot;
+    addr->function = func;
+
+    g_free(str);
+    return;
+
+inval:
+    error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str);
+    g_free(str);
+}
+
+PropertyInfo qdev_prop_pci_host_devaddr = {
+    .name = "pci-host-devaddr",
+    .get = get_pci_host_devaddr,
+    .set = set_pci_host_devaddr,
+};
+
+/* --- support for array properties --- */
+
+/* Used as an opaque for the object properties we add for each
+ * array element. Note that the struct Property must be first
+ * in the struct so that a pointer to this works as the opaque
+ * for the underlying element's property hooks as well as for
+ * our own release callback.
+ */
+typedef struct {
+    struct Property prop;
+    char *propname;
+    ObjectPropertyRelease *release;
+} ArrayElementProperty;
+
+/* object property release callback for array element properties:
+ * we call the underlying element's property release hook, and
+ * then free the memory we allocated when we added the property.
+ */
+static void array_element_release(Object *obj, const char *name, void *opaque)
+{
+    ArrayElementProperty *p = opaque;
+    if (p->release) {
+        p->release(obj, name, opaque);
+    }
+    g_free(p->propname);
+    g_free(p);
+}
+
+static void set_prop_arraylen(Object *obj, Visitor *v, void *opaque,
+                              const char *name, Error **errp)
+{
+    /* Setter for the property which defines the length of a
+     * variable-sized property array. As well as actually setting the
+     * array-length field in the device struct, we have to create the
+     * array itself and dynamically add the corresponding properties.
+     */
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    uint32_t *alenptr = qdev_get_prop_ptr(dev, prop);
+    void **arrayptr = (void *)dev + prop->arrayoffset;
+    void *eltptr;
+    const char *arrayname;
+    int i;
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+    if (*alenptr) {
+        error_setg(errp, "array size property %s may not be set more than once",
+                   name);
+        return;
+    }
+    visit_type_uint32(v, alenptr, name, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+    if (!*alenptr) {
+        return;
+    }
+
+    /* DEFINE_PROP_ARRAY guarantees that name should start with this prefix;
+     * strip it off so we can get the name of the array itself.
+     */
+    assert(strncmp(name, PROP_ARRAY_LEN_PREFIX,
+                   strlen(PROP_ARRAY_LEN_PREFIX)) == 0);
+    arrayname = name + strlen(PROP_ARRAY_LEN_PREFIX);
+
+    /* Note that it is the responsibility of the individual device's deinit
+     * to free the array proper.
+     */
+    *arrayptr = eltptr = g_malloc0(*alenptr * prop->arrayfieldsize);
+    for (i = 0; i < *alenptr; i++, eltptr += prop->arrayfieldsize) {
+        char *propname = g_strdup_printf("%s[%d]", arrayname, i);
+        ArrayElementProperty *arrayprop = g_new0(ArrayElementProperty, 1);
+        arrayprop->release = prop->arrayinfo->release;
+        arrayprop->propname = propname;
+        arrayprop->prop.info = prop->arrayinfo;
+        arrayprop->prop.name = propname;
+        /* This ugly piece of pointer arithmetic sets up the offset so
+         * that when the underlying get/set hooks call qdev_get_prop_ptr
+         * they get the right answer despite the array element not actually
+         * being inside the device struct.
+         */
+        arrayprop->prop.offset = eltptr - (void *)dev;
+        assert(qdev_get_prop_ptr(dev, &arrayprop->prop) == eltptr);
+        object_property_add(obj, propname,
+                            arrayprop->prop.info->name,
+                            arrayprop->prop.info->get,
+                            arrayprop->prop.info->set,
+                            array_element_release,
+                            arrayprop, errp);
+        if (error_is_set(errp)) {
+            return;
+        }
+    }
+}
+
+PropertyInfo qdev_prop_arraylen = {
+    .name = "uint32",
+    .get = get_uint32,
+    .set = set_prop_arraylen,
+};
+
+/* --- public helpers --- */
+
+static Property *qdev_prop_walk(Property *props, const char *name)
+{
+    if (!props) {
+        return NULL;
+    }
+    while (props->name) {
+        if (strcmp(props->name, name) == 0) {
+            return props;
+        }
+        props++;
+    }
+    return NULL;
+}
+
+static Property *qdev_prop_find(DeviceState *dev, const char *name)
+{
+    ObjectClass *class;
+    Property *prop;
+
+    /* device properties */
+    class = object_get_class(OBJECT(dev));
+    do {
+        prop = qdev_prop_walk(DEVICE_CLASS(class)->props, name);
+        if (prop) {
+            return prop;
+        }
+        class = object_class_get_parent(class);
+    } while (class != object_class_by_name(TYPE_DEVICE));
+
+    return NULL;
+}
+
+void error_set_from_qdev_prop_error(Error **errp, int ret, DeviceState *dev,
+                                    Property *prop, const char *value)
+{
+    switch (ret) {
+    case -EEXIST:
+        error_set(errp, QERR_PROPERTY_VALUE_IN_USE,
+                  object_get_typename(OBJECT(dev)), prop->name, value);
+        break;
+    default:
+    case -EINVAL:
+        error_set(errp, QERR_PROPERTY_VALUE_BAD,
+                  object_get_typename(OBJECT(dev)), prop->name, value);
+        break;
+    case -ENOENT:
+        error_set(errp, QERR_PROPERTY_VALUE_NOT_FOUND,
+                  object_get_typename(OBJECT(dev)), prop->name, value);
+        break;
+    case 0:
+        break;
+    }
+}
+
+int qdev_prop_parse(DeviceState *dev, const char *name, const char *value)
+{
+    char *legacy_name;
+    Error *err = NULL;
+
+    legacy_name = g_strdup_printf("legacy-%s", name);
+    if (object_property_get_type(OBJECT(dev), legacy_name, NULL)) {
+        object_property_parse(OBJECT(dev), value, legacy_name, &err);
+    } else {
+        object_property_parse(OBJECT(dev), value, name, &err);
+    }
+    g_free(legacy_name);
+
+    if (err) {
+        qerror_report_err(err);
+        error_free(err);
+        return -1;
+    }
+    return 0;
+}
+
+void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value)
+{
+    Error *errp = NULL;
+    object_property_set_bool(OBJECT(dev), value, name, &errp);
+    assert_no_error(errp);
+}
+
+void qdev_prop_set_uint8(DeviceState *dev, const char *name, uint8_t value)
+{
+    Error *errp = NULL;
+    object_property_set_int(OBJECT(dev), value, name, &errp);
+    assert_no_error(errp);
+}
+
+void qdev_prop_set_uint16(DeviceState *dev, const char *name, uint16_t value)
+{
+    Error *errp = NULL;
+    object_property_set_int(OBJECT(dev), value, name, &errp);
+    assert_no_error(errp);
+}
+
+void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value)
+{
+    Error *errp = NULL;
+    object_property_set_int(OBJECT(dev), value, name, &errp);
+    assert_no_error(errp);
+}
+
+void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value)
+{
+    Error *errp = NULL;
+    object_property_set_int(OBJECT(dev), value, name, &errp);
+    assert_no_error(errp);
+}
+
+void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value)
+{
+    Error *errp = NULL;
+    object_property_set_int(OBJECT(dev), value, name, &errp);
+    assert_no_error(errp);
+}
+
+void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value)
+{
+    Error *errp = NULL;
+    object_property_set_str(OBJECT(dev), value, name, &errp);
+    assert_no_error(errp);
+}
+
+void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value)
+{
+    Error *errp = NULL;
+    char str[2 * 6 + 5 + 1];
+    snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x",
+             value[0], value[1], value[2], value[3], value[4], value[5]);
+
+    object_property_set_str(OBJECT(dev), str, name, &errp);
+    assert_no_error(errp);
+}
+
+void qdev_prop_set_enum(DeviceState *dev, const char *name, int value)
+{
+    Property *prop;
+    Error *errp = NULL;
+
+    prop = qdev_prop_find(dev, name);
+    object_property_set_str(OBJECT(dev), prop->info->enum_table[value],
+                            name, &errp);
+    assert_no_error(errp);
+}
+
+void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value)
+{
+    Property *prop;
+    void **ptr;
+
+    prop = qdev_prop_find(dev, name);
+    assert(prop && prop->info == &qdev_prop_ptr);
+    ptr = qdev_get_prop_ptr(dev, prop);
+    *ptr = value;
+}
+
+static QTAILQ_HEAD(, GlobalProperty) global_props =
+        QTAILQ_HEAD_INITIALIZER(global_props);
+
+void qdev_prop_register_global(GlobalProperty *prop)
+{
+    QTAILQ_INSERT_TAIL(&global_props, prop, next);
+}
+
+void qdev_prop_register_global_list(GlobalProperty *props)
+{
+    int i;
+
+    for (i = 0; props[i].driver != NULL; i++) {
+        qdev_prop_register_global(props+i);
+    }
+}
+
+void qdev_prop_set_globals(DeviceState *dev)
+{
+    ObjectClass *class = object_get_class(OBJECT(dev));
+
+    do {
+        GlobalProperty *prop;
+        QTAILQ_FOREACH(prop, &global_props, next) {
+            if (strcmp(object_class_get_name(class), prop->driver) != 0) {
+                continue;
+            }
+            if (qdev_prop_parse(dev, prop->property, prop->value) != 0) {
+                exit(1);
+            }
+        }
+        class = object_class_get_parent(class);
+    } while (class);
+}
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
new file mode 100644 (file)
index 0000000..e2bb37d
--- /dev/null
@@ -0,0 +1,882 @@
+/*
+ *  Dynamic device configuration and creation.
+ *
+ *  Copyright (c) 2009 CodeSourcery
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* The theory here is that it should be possible to create a machine without
+   knowledge of specific devices.  Historically board init routines have
+   passed a bunch of arguments to each device, requiring the board know
+   exactly which device it is dealing with.  This file provides an abstract
+   API for device configuration and initialization.  Devices will generally
+   inherit from a particular bus (e.g. PCI or I2C) rather than
+   this API directly.  */
+
+#include "hw/qdev.h"
+#include "sysemu/sysemu.h"
+#include "qapi/error.h"
+#include "qapi/qmp/qerror.h"
+#include "qapi/visitor.h"
+#include "qapi/qmp/qjson.h"
+#include "monitor/monitor.h"
+
+int qdev_hotplug = 0;
+static bool qdev_hot_added = false;
+static bool qdev_hot_removed = false;
+
+const VMStateDescription *qdev_get_vmsd(DeviceState *dev)
+{
+    DeviceClass *dc = DEVICE_GET_CLASS(dev);
+    return dc->vmsd;
+}
+
+const char *qdev_fw_name(DeviceState *dev)
+{
+    DeviceClass *dc = DEVICE_GET_CLASS(dev);
+
+    if (dc->fw_name) {
+        return dc->fw_name;
+    }
+
+    return object_get_typename(OBJECT(dev));
+}
+
+static void qdev_property_add_legacy(DeviceState *dev, Property *prop,
+                                     Error **errp);
+
+static void bus_remove_child(BusState *bus, DeviceState *child)
+{
+    BusChild *kid;
+
+    QTAILQ_FOREACH(kid, &bus->children, sibling) {
+        if (kid->child == child) {
+            char name[32];
+
+            snprintf(name, sizeof(name), "child[%d]", kid->index);
+            QTAILQ_REMOVE(&bus->children, kid, sibling);
+
+            /* This gives back ownership of kid->child back to us.  */
+            object_property_del(OBJECT(bus), name, NULL);
+            object_unref(OBJECT(kid->child));
+            g_free(kid);
+            return;
+        }
+    }
+}
+
+static void bus_add_child(BusState *bus, DeviceState *child)
+{
+    char name[32];
+    BusChild *kid = g_malloc0(sizeof(*kid));
+
+    if (qdev_hotplug) {
+        assert(bus->allow_hotplug);
+    }
+
+    kid->index = bus->max_index++;
+    kid->child = child;
+    object_ref(OBJECT(kid->child));
+
+    QTAILQ_INSERT_HEAD(&bus->children, kid, sibling);
+
+    /* This transfers ownership of kid->child to the property.  */
+    snprintf(name, sizeof(name), "child[%d]", kid->index);
+    object_property_add_link(OBJECT(bus), name,
+                             object_get_typename(OBJECT(child)),
+                             (Object **)&kid->child,
+                             NULL);
+}
+
+void qdev_set_parent_bus(DeviceState *dev, BusState *bus)
+{
+    dev->parent_bus = bus;
+    object_ref(OBJECT(bus));
+    bus_add_child(bus, dev);
+}
+
+/* Create a new device.  This only initializes the device state structure
+   and allows properties to be set.  qdev_init should be called to
+   initialize the actual device emulation.  */
+DeviceState *qdev_create(BusState *bus, const char *name)
+{
+    DeviceState *dev;
+
+    dev = qdev_try_create(bus, name);
+    if (!dev) {
+        if (bus) {
+            error_report("Unknown device '%s' for bus '%s'", name,
+                         object_get_typename(OBJECT(bus)));
+        } else {
+            error_report("Unknown device '%s' for default sysbus", name);
+        }
+        abort();
+    }
+
+    return dev;
+}
+
+DeviceState *qdev_try_create(BusState *bus, const char *type)
+{
+    DeviceState *dev;
+
+    if (object_class_by_name(type) == NULL) {
+        return NULL;
+    }
+    dev = DEVICE(object_new(type));
+    if (!dev) {
+        return NULL;
+    }
+
+    if (!bus) {
+        bus = sysbus_get_default();
+    }
+
+    qdev_set_parent_bus(dev, bus);
+    object_unref(OBJECT(dev));
+    return dev;
+}
+
+/* Initialize a device.  Device properties should be set before calling
+   this function.  IRQs and MMIO regions should be connected/mapped after
+   calling this function.
+   On failure, destroy the device and return negative value.
+   Return 0 on success.  */
+int qdev_init(DeviceState *dev)
+{
+    Error *local_err = NULL;
+
+    assert(!dev->realized);
+
+    object_property_set_bool(OBJECT(dev), true, "realized", &local_err);
+    if (local_err != NULL) {
+        error_free(local_err);
+        qdev_free(dev);
+        return -1;
+    }
+    return 0;
+}
+
+static void device_realize(DeviceState *dev, Error **err)
+{
+    DeviceClass *dc = DEVICE_GET_CLASS(dev);
+
+    if (dc->init) {
+        int rc = dc->init(dev);
+        if (rc < 0) {
+            error_setg(err, "Device initialization failed.");
+            return;
+        }
+    }
+}
+
+void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,
+                                 int required_for_version)
+{
+    assert(!dev->realized);
+    dev->instance_id_alias = alias_id;
+    dev->alias_required_for_version = required_for_version;
+}
+
+void qdev_unplug(DeviceState *dev, Error **errp)
+{
+    DeviceClass *dc = DEVICE_GET_CLASS(dev);
+
+    if (!dev->parent_bus->allow_hotplug) {
+        error_set(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name);
+        return;
+    }
+    assert(dc->unplug != NULL);
+
+    qdev_hot_removed = true;
+
+    if (dc->unplug(dev) < 0) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+        return;
+    }
+}
+
+static int qdev_reset_one(DeviceState *dev, void *opaque)
+{
+    device_reset(dev);
+
+    return 0;
+}
+
+static int qbus_reset_one(BusState *bus, void *opaque)
+{
+    BusClass *bc = BUS_GET_CLASS(bus);
+    if (bc->reset) {
+        return bc->reset(bus);
+    }
+    return 0;
+}
+
+void qdev_reset_all(DeviceState *dev)
+{
+    qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL);
+}
+
+void qbus_reset_all(BusState *bus)
+{
+    qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL);
+}
+
+void qbus_reset_all_fn(void *opaque)
+{
+    BusState *bus = opaque;
+    qbus_reset_all(bus);
+}
+
+/* can be used as ->unplug() callback for the simple cases */
+int qdev_simple_unplug_cb(DeviceState *dev)
+{
+    /* just zap it */
+    qdev_free(dev);
+    return 0;
+}
+
+
+/* Like qdev_init(), but terminate program via error_report() instead of
+   returning an error value.  This is okay during machine creation.
+   Don't use for hotplug, because there callers need to recover from
+   failure.  Exception: if you know the device's init() callback can't
+   fail, then qdev_init_nofail() can't fail either, and is therefore
+   usable even then.  But relying on the device implementation that
+   way is somewhat unclean, and best avoided.  */
+void qdev_init_nofail(DeviceState *dev)
+{
+    const char *typename = object_get_typename(OBJECT(dev));
+
+    if (qdev_init(dev) < 0) {
+        error_report("Initialization of device %s failed", typename);
+        exit(1);
+    }
+}
+
+/* Unlink device from bus and free the structure.  */
+void qdev_free(DeviceState *dev)
+{
+    object_unparent(OBJECT(dev));
+}
+
+void qdev_machine_creation_done(void)
+{
+    /*
+     * ok, initial machine setup is done, starting from now we can
+     * only create hotpluggable devices
+     */
+    qdev_hotplug = 1;
+}
+
+bool qdev_machine_modified(void)
+{
+    return qdev_hot_added || qdev_hot_removed;
+}
+
+BusState *qdev_get_parent_bus(DeviceState *dev)
+{
+    return dev->parent_bus;
+}
+
+void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
+{
+    dev->gpio_in = qemu_extend_irqs(dev->gpio_in, dev->num_gpio_in, handler,
+                                        dev, n);
+    dev->num_gpio_in += n;
+}
+
+void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
+{
+    assert(dev->num_gpio_out == 0);
+    dev->num_gpio_out = n;
+    dev->gpio_out = pins;
+}
+
+qemu_irq qdev_get_gpio_in(DeviceState *dev, int n)
+{
+    assert(n >= 0 && n < dev->num_gpio_in);
+    return dev->gpio_in[n];
+}
+
+void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin)
+{
+    assert(n >= 0 && n < dev->num_gpio_out);
+    dev->gpio_out[n] = pin;
+}
+
+BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
+{
+    BusState *bus;
+
+    QLIST_FOREACH(bus, &dev->child_bus, sibling) {
+        if (strcmp(name, bus->name) == 0) {
+            return bus;
+        }
+    }
+    return NULL;
+}
+
+int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
+                       qbus_walkerfn *busfn, void *opaque)
+{
+    BusChild *kid;
+    int err;
+
+    if (busfn) {
+        err = busfn(bus, opaque);
+        if (err) {
+            return err;
+        }
+    }
+
+    QTAILQ_FOREACH(kid, &bus->children, sibling) {
+        err = qdev_walk_children(kid->child, devfn, busfn, opaque);
+        if (err < 0) {
+            return err;
+        }
+    }
+
+    return 0;
+}
+
+int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
+                       qbus_walkerfn *busfn, void *opaque)
+{
+    BusState *bus;
+    int err;
+
+    if (devfn) {
+        err = devfn(dev, opaque);
+        if (err) {
+            return err;
+        }
+    }
+
+    QLIST_FOREACH(bus, &dev->child_bus, sibling) {
+        err = qbus_walk_children(bus, devfn, busfn, opaque);
+        if (err < 0) {
+            return err;
+        }
+    }
+
+    return 0;
+}
+
+DeviceState *qdev_find_recursive(BusState *bus, const char *id)
+{
+    BusChild *kid;
+    DeviceState *ret;
+    BusState *child;
+
+    QTAILQ_FOREACH(kid, &bus->children, sibling) {
+        DeviceState *dev = kid->child;
+
+        if (dev->id && strcmp(dev->id, id) == 0) {
+            return dev;
+        }
+
+        QLIST_FOREACH(child, &dev->child_bus, sibling) {
+            ret = qdev_find_recursive(child, id);
+            if (ret) {
+                return ret;
+            }
+        }
+    }
+    return NULL;
+}
+
+static void qbus_realize(BusState *bus, DeviceState *parent, const char *name)
+{
+    const char *typename = object_get_typename(OBJECT(bus));
+    char *buf;
+    int i,len;
+
+    bus->parent = parent;
+
+    if (name) {
+        bus->name = g_strdup(name);
+    } else if (bus->parent && bus->parent->id) {
+        /* parent device has id -> use it for bus name */
+        len = strlen(bus->parent->id) + 16;
+        buf = g_malloc(len);
+        snprintf(buf, len, "%s.%d", bus->parent->id, bus->parent->num_child_bus);
+        bus->name = buf;
+    } else {
+        /* no id -> use lowercase bus type for bus name */
+        len = strlen(typename) + 16;
+        buf = g_malloc(len);
+        len = snprintf(buf, len, "%s.%d", typename,
+                       bus->parent ? bus->parent->num_child_bus : 0);
+        for (i = 0; i < len; i++)
+            buf[i] = qemu_tolower(buf[i]);
+        bus->name = buf;
+    }
+
+    if (bus->parent) {
+        QLIST_INSERT_HEAD(&bus->parent->child_bus, bus, sibling);
+        bus->parent->num_child_bus++;
+        object_property_add_child(OBJECT(bus->parent), bus->name, OBJECT(bus), NULL);
+        object_unref(OBJECT(bus));
+    } else if (bus != sysbus_get_default()) {
+        /* TODO: once all bus devices are qdevified,
+           only reset handler for main_system_bus should be registered here. */
+        qemu_register_reset(qbus_reset_all_fn, bus);
+    }
+}
+
+static void bus_unparent(Object *obj)
+{
+    BusState *bus = BUS(obj);
+    BusChild *kid;
+
+    while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) {
+        DeviceState *dev = kid->child;
+        qdev_free(dev);
+    }
+    if (bus->parent) {
+        QLIST_REMOVE(bus, sibling);
+        bus->parent->num_child_bus--;
+        bus->parent = NULL;
+    } else {
+        assert(bus != sysbus_get_default()); /* main_system_bus is never freed */
+        qemu_unregister_reset(qbus_reset_all_fn, bus);
+    }
+}
+
+void qbus_create_inplace(void *bus, const char *typename,
+                         DeviceState *parent, const char *name)
+{
+    object_initialize(bus, typename);
+    qbus_realize(bus, parent, name);
+}
+
+BusState *qbus_create(const char *typename, DeviceState *parent, const char *name)
+{
+    BusState *bus;
+
+    bus = BUS(object_new(typename));
+    qbus_realize(bus, parent, name);
+
+    return bus;
+}
+
+void qbus_free(BusState *bus)
+{
+    object_unparent(OBJECT(bus));
+}
+
+static char *bus_get_fw_dev_path(BusState *bus, DeviceState *dev)
+{
+    BusClass *bc = BUS_GET_CLASS(bus);
+
+    if (bc->get_fw_dev_path) {
+        return bc->get_fw_dev_path(dev);
+    }
+
+    return NULL;
+}
+
+static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size)
+{
+    int l = 0;
+
+    if (dev && dev->parent_bus) {
+        char *d;
+        l = qdev_get_fw_dev_path_helper(dev->parent_bus->parent, p, size);
+        d = bus_get_fw_dev_path(dev->parent_bus, dev);
+        if (d) {
+            l += snprintf(p + l, size - l, "%s", d);
+            g_free(d);
+        } else {
+            l += snprintf(p + l, size - l, "%s", object_get_typename(OBJECT(dev)));
+        }
+    }
+    l += snprintf(p + l , size - l, "/");
+
+    return l;
+}
+
+char* qdev_get_fw_dev_path(DeviceState *dev)
+{
+    char path[128];
+    int l;
+
+    l = qdev_get_fw_dev_path_helper(dev, path, 128);
+
+    path[l-1] = '\0';
+
+    return g_strdup(path);
+}
+
+char *qdev_get_dev_path(DeviceState *dev)
+{
+    BusClass *bc;
+
+    if (!dev || !dev->parent_bus) {
+        return NULL;
+    }
+
+    bc = BUS_GET_CLASS(dev->parent_bus);
+    if (bc->get_dev_path) {
+        return bc->get_dev_path(dev);
+    }
+
+    return NULL;
+}
+
+/**
+ * Legacy property handling
+ */
+
+static void qdev_get_legacy_property(Object *obj, Visitor *v, void *opaque,
+                                     const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+
+    char buffer[1024];
+    char *ptr = buffer;
+
+    prop->info->print(dev, prop, buffer, sizeof(buffer));
+    visit_type_str(v, &ptr, name, errp);
+}
+
+static void qdev_set_legacy_property(Object *obj, Visitor *v, void *opaque,
+                                     const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    Error *local_err = NULL;
+    char *ptr = NULL;
+    int ret;
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_str(v, &ptr, name, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    ret = prop->info->parse(dev, prop, ptr);
+    error_set_from_qdev_prop_error(errp, ret, dev, prop, ptr);
+    g_free(ptr);
+}
+
+/**
+ * @qdev_add_legacy_property - adds a legacy property
+ *
+ * Do not use this is new code!  Properties added through this interface will
+ * be given names and types in the "legacy" namespace.
+ *
+ * Legacy properties are string versions of other OOM properties.  The format
+ * of the string depends on the property type.
+ */
+void qdev_property_add_legacy(DeviceState *dev, Property *prop,
+                              Error **errp)
+{
+    gchar *name, *type;
+
+    /* Register pointer properties as legacy properties */
+    if (!prop->info->print && !prop->info->parse &&
+        (prop->info->set || prop->info->get)) {
+        return;
+    }
+
+    name = g_strdup_printf("legacy-%s", prop->name);
+    type = g_strdup_printf("legacy<%s>",
+                           prop->info->legacy_name ?: prop->info->name);
+
+    object_property_add(OBJECT(dev), name, type,
+                        prop->info->print ? qdev_get_legacy_property : prop->info->get,
+                        prop->info->parse ? qdev_set_legacy_property : prop->info->set,
+                        NULL,
+                        prop, errp);
+
+    g_free(type);
+    g_free(name);
+}
+
+/**
+ * @qdev_property_add_static - add a @Property to a device.
+ *
+ * Static properties access data in a struct.  The actual type of the
+ * property and the field depends on the property type.
+ */
+void qdev_property_add_static(DeviceState *dev, Property *prop,
+                              Error **errp)
+{
+    Error *local_err = NULL;
+    Object *obj = OBJECT(dev);
+
+    /*
+     * TODO qdev_prop_ptr does not have getters or setters.  It must
+     * go now that it can be replaced with links.  The test should be
+     * removed along with it: all static properties are read/write.
+     */
+    if (!prop->info->get && !prop->info->set) {
+        return;
+    }
+
+    object_property_add(obj, prop->name, prop->info->name,
+                        prop->info->get, prop->info->set,
+                        prop->info->release,
+                        prop, &local_err);
+
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+    if (prop->qtype == QTYPE_NONE) {
+        return;
+    }
+
+    if (prop->qtype == QTYPE_QBOOL) {
+        object_property_set_bool(obj, prop->defval, prop->name, &local_err);
+    } else if (prop->info->enum_table) {
+        object_property_set_str(obj, prop->info->enum_table[prop->defval],
+                                prop->name, &local_err);
+    } else if (prop->qtype == QTYPE_QINT) {
+        object_property_set_int(obj, prop->defval, prop->name, &local_err);
+    }
+    assert_no_error(local_err);
+}
+
+static bool device_get_realized(Object *obj, Error **err)
+{
+    DeviceState *dev = DEVICE(obj);
+    return dev->realized;
+}
+
+static void device_set_realized(Object *obj, bool value, Error **err)
+{
+    DeviceState *dev = DEVICE(obj);
+    DeviceClass *dc = DEVICE_GET_CLASS(dev);
+    Error *local_err = NULL;
+
+    if (value && !dev->realized) {
+        if (dc->realize) {
+            dc->realize(dev, &local_err);
+        }
+
+        if (!obj->parent && local_err == NULL) {
+            static int unattached_count;
+            gchar *name = g_strdup_printf("device[%d]", unattached_count++);
+
+            object_property_add_child(container_get(qdev_get_machine(),
+                                                    "/unattached"),
+                                      name, obj, &local_err);
+            g_free(name);
+        }
+
+        if (qdev_get_vmsd(dev) && local_err == NULL) {
+            vmstate_register_with_alias_id(dev, -1, qdev_get_vmsd(dev), dev,
+                                           dev->instance_id_alias,
+                                           dev->alias_required_for_version);
+        }
+        if (dev->hotplugged && local_err == NULL) {
+            device_reset(dev);
+        }
+    } else if (!value && dev->realized) {
+        if (dc->unrealize) {
+            dc->unrealize(dev, &local_err);
+        }
+    }
+
+    if (local_err != NULL) {
+        error_propagate(err, local_err);
+        return;
+    }
+
+    dev->realized = value;
+}
+
+static void device_initfn(Object *obj)
+{
+    DeviceState *dev = DEVICE(obj);
+    ObjectClass *class;
+    Property *prop;
+    Error *err = NULL;
+
+    if (qdev_hotplug) {
+        dev->hotplugged = 1;
+        qdev_hot_added = true;
+    }
+
+    dev->instance_id_alias = -1;
+    dev->realized = false;
+
+    object_property_add_bool(obj, "realized",
+                             device_get_realized, device_set_realized, NULL);
+
+    class = object_get_class(OBJECT(dev));
+    do {
+        for (prop = DEVICE_CLASS(class)->props; prop && prop->name; prop++) {
+            qdev_property_add_legacy(dev, prop, &err);
+            assert_no_error(err);
+            qdev_property_add_static(dev, prop, &err);
+            assert_no_error(err);
+        }
+        class = object_class_get_parent(class);
+    } while (class != object_class_by_name(TYPE_DEVICE));
+    qdev_prop_set_globals(dev);
+
+    object_property_add_link(OBJECT(dev), "parent_bus", TYPE_BUS,
+                             (Object **)&dev->parent_bus, &err);
+    assert_no_error(err);
+}
+
+/* Unlink device from bus and free the structure.  */
+static void device_finalize(Object *obj)
+{
+    DeviceState *dev = DEVICE(obj);
+    if (dev->opts) {
+        qemu_opts_del(dev->opts);
+    }
+}
+
+static void device_class_base_init(ObjectClass *class, void *data)
+{
+    DeviceClass *klass = DEVICE_CLASS(class);
+
+    /* We explicitly look up properties in the superclasses,
+     * so do not propagate them to the subclasses.
+     */
+    klass->props = NULL;
+}
+
+static void device_unparent(Object *obj)
+{
+    DeviceState *dev = DEVICE(obj);
+    DeviceClass *dc = DEVICE_GET_CLASS(dev);
+    BusState *bus;
+    QObject *event_data;
+    bool have_realized = dev->realized;
+
+    while (dev->num_child_bus) {
+        bus = QLIST_FIRST(&dev->child_bus);
+        qbus_free(bus);
+    }
+    if (dev->realized) {
+        if (qdev_get_vmsd(dev)) {
+            vmstate_unregister(dev, qdev_get_vmsd(dev), dev);
+        }
+        if (dc->exit) {
+            dc->exit(dev);
+        }
+    }
+    if (dev->parent_bus) {
+        bus_remove_child(dev->parent_bus, dev);
+        object_unref(OBJECT(dev->parent_bus));
+        dev->parent_bus = NULL;
+    }
+
+    /* Only send event if the device had been completely realized */
+    if (have_realized) {
+        gchar *path = object_get_canonical_path(OBJECT(dev));
+
+        if (dev->id) {
+            event_data = qobject_from_jsonf("{ 'device': %s, 'path': %s }",
+                                            dev->id, path);
+        } else {
+            event_data = qobject_from_jsonf("{ 'path': %s }", path);
+        }
+        monitor_protocol_event(QEVENT_DEVICE_DELETED, event_data);
+        qobject_decref(event_data);
+        g_free(path);
+    }
+}
+
+static void device_class_init(ObjectClass *class, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(class);
+
+    class->unparent = device_unparent;
+    dc->realize = device_realize;
+}
+
+void device_reset(DeviceState *dev)
+{
+    DeviceClass *klass = DEVICE_GET_CLASS(dev);
+
+    if (klass->reset) {
+        klass->reset(dev);
+    }
+}
+
+Object *qdev_get_machine(void)
+{
+    static Object *dev;
+
+    if (dev == NULL) {
+        dev = container_get(object_get_root(), "/machine");
+    }
+
+    return dev;
+}
+
+static const TypeInfo device_type_info = {
+    .name = TYPE_DEVICE,
+    .parent = TYPE_OBJECT,
+    .instance_size = sizeof(DeviceState),
+    .instance_init = device_initfn,
+    .instance_finalize = device_finalize,
+    .class_base_init = device_class_base_init,
+    .class_init = device_class_init,
+    .abstract = true,
+    .class_size = sizeof(DeviceClass),
+};
+
+static void qbus_initfn(Object *obj)
+{
+    BusState *bus = BUS(obj);
+
+    QTAILQ_INIT(&bus->children);
+}
+
+static void bus_class_init(ObjectClass *class, void *data)
+{
+    class->unparent = bus_unparent;
+}
+
+static void qbus_finalize(Object *obj)
+{
+    BusState *bus = BUS(obj);
+
+    g_free((char *)bus->name);
+}
+
+static const TypeInfo bus_info = {
+    .name = TYPE_BUS,
+    .parent = TYPE_OBJECT,
+    .instance_size = sizeof(BusState),
+    .abstract = true,
+    .class_size = sizeof(BusClass),
+    .instance_init = qbus_initfn,
+    .instance_finalize = qbus_finalize,
+    .class_init = bus_class_init,
+};
+
+static void qdev_register_types(void)
+{
+    type_register_static(&bus_info);
+    type_register_static(&device_type_info);
+}
+
+type_init(qdev_register_types)
diff --git a/hw/core/stream.c b/hw/core/stream.c
new file mode 100644 (file)
index 0000000..a07d6a5
--- /dev/null
@@ -0,0 +1,23 @@
+#include "hw/stream.h"
+
+void
+stream_push(StreamSlave *sink, uint8_t *buf, size_t len, uint32_t *app)
+{
+    StreamSlaveClass *k =  STREAM_SLAVE_GET_CLASS(sink);
+
+    k->push(sink, buf, len, app);
+}
+
+static const TypeInfo stream_slave_info = {
+    .name          = TYPE_STREAM_SLAVE,
+    .parent        = TYPE_INTERFACE,
+    .class_size = sizeof(StreamSlaveClass),
+};
+
+
+static void stream_slave_register_types(void)
+{
+    type_register_static(&stream_slave_info);
+}
+
+type_init(stream_slave_register_types)
diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c
new file mode 100644 (file)
index 0000000..9004d8c
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ *  System (CPU) Bus device support code
+ *
+ *  Copyright (c) 2009 CodeSourcery
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/sysbus.h"
+#include "monitor/monitor.h"
+#include "exec/address-spaces.h"
+
+static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent);
+static char *sysbus_get_fw_dev_path(DeviceState *dev);
+
+static void system_bus_class_init(ObjectClass *klass, void *data)
+{
+    BusClass *k = BUS_CLASS(klass);
+
+    k->print_dev = sysbus_dev_print;
+    k->get_fw_dev_path = sysbus_get_fw_dev_path;
+}
+
+static const TypeInfo system_bus_info = {
+    .name = TYPE_SYSTEM_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(BusState),
+    .class_init = system_bus_class_init,
+};
+
+void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq)
+{
+    assert(n >= 0 && n < dev->num_irq);
+    dev->irqs[n] = NULL;
+    if (dev->irqp[n]) {
+        *dev->irqp[n] = irq;
+    }
+}
+
+static void sysbus_mmio_map_common(SysBusDevice *dev, int n, hwaddr addr,
+                                   bool may_overlap, unsigned priority)
+{
+    assert(n >= 0 && n < dev->num_mmio);
+
+    if (dev->mmio[n].addr == addr) {
+        /* ??? region already mapped here.  */
+        return;
+    }
+    if (dev->mmio[n].addr != (hwaddr)-1) {
+        /* Unregister previous mapping.  */
+        memory_region_del_subregion(get_system_memory(), dev->mmio[n].memory);
+    }
+    dev->mmio[n].addr = addr;
+    if (may_overlap) {
+        memory_region_add_subregion_overlap(get_system_memory(),
+                                            addr,
+                                            dev->mmio[n].memory,
+                                            priority);
+    }
+    else {
+        memory_region_add_subregion(get_system_memory(),
+                                    addr,
+                                    dev->mmio[n].memory);
+    }
+}
+
+void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr)
+{
+    sysbus_mmio_map_common(dev, n, addr, false, 0);
+}
+
+void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr,
+                             unsigned priority)
+{
+    sysbus_mmio_map_common(dev, n, addr, true, priority);
+}
+
+/* Request an IRQ source.  The actual IRQ object may be populated later.  */
+void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p)
+{
+    int n;
+
+    assert(dev->num_irq < QDEV_MAX_IRQ);
+    n = dev->num_irq++;
+    dev->irqp[n] = p;
+}
+
+/* Pass IRQs from a target device.  */
+void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target)
+{
+    int i;
+    assert(dev->num_irq == 0);
+    dev->num_irq = target->num_irq;
+    for (i = 0; i < dev->num_irq; i++) {
+        dev->irqp[i] = target->irqp[i];
+    }
+}
+
+void sysbus_init_mmio(SysBusDevice *dev, MemoryRegion *memory)
+{
+    int n;
+
+    assert(dev->num_mmio < QDEV_MAX_MMIO);
+    n = dev->num_mmio++;
+    dev->mmio[n].addr = -1;
+    dev->mmio[n].memory = memory;
+}
+
+MemoryRegion *sysbus_mmio_get_region(SysBusDevice *dev, int n)
+{
+    return dev->mmio[n].memory;
+}
+
+void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size)
+{
+    pio_addr_t i;
+
+    for (i = 0; i < size; i++) {
+        assert(dev->num_pio < QDEV_MAX_PIO);
+        dev->pio[dev->num_pio++] = ioport++;
+    }
+}
+
+static int sysbus_device_init(DeviceState *dev)
+{
+    SysBusDevice *sd = SYS_BUS_DEVICE(dev);
+    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_GET_CLASS(sd);
+
+    if (!sbc->init) {
+        return 0;
+    }
+    return sbc->init(sd);
+}
+
+DeviceState *sysbus_create_varargs(const char *name,
+                                   hwaddr addr, ...)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+    va_list va;
+    qemu_irq irq;
+    int n;
+
+    dev = qdev_create(NULL, name);
+    s = SYS_BUS_DEVICE(dev);
+    qdev_init_nofail(dev);
+    if (addr != (hwaddr)-1) {
+        sysbus_mmio_map(s, 0, addr);
+    }
+    va_start(va, addr);
+    n = 0;
+    while (1) {
+        irq = va_arg(va, qemu_irq);
+        if (!irq) {
+            break;
+        }
+        sysbus_connect_irq(s, n, irq);
+        n++;
+    }
+    va_end(va);
+    return dev;
+}
+
+DeviceState *sysbus_try_create_varargs(const char *name,
+                                       hwaddr addr, ...)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+    va_list va;
+    qemu_irq irq;
+    int n;
+
+    dev = qdev_try_create(NULL, name);
+    if (!dev) {
+        return NULL;
+    }
+    s = SYS_BUS_DEVICE(dev);
+    qdev_init_nofail(dev);
+    if (addr != (hwaddr)-1) {
+        sysbus_mmio_map(s, 0, addr);
+    }
+    va_start(va, addr);
+    n = 0;
+    while (1) {
+        irq = va_arg(va, qemu_irq);
+        if (!irq) {
+            break;
+        }
+        sysbus_connect_irq(s, n, irq);
+        n++;
+    }
+    va_end(va);
+    return dev;
+}
+
+static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent)
+{
+    SysBusDevice *s = SYS_BUS_DEVICE(dev);
+    hwaddr size;
+    int i;
+
+    monitor_printf(mon, "%*sirq %d\n", indent, "", s->num_irq);
+    for (i = 0; i < s->num_mmio; i++) {
+        size = memory_region_size(s->mmio[i].memory);
+        monitor_printf(mon, "%*smmio " TARGET_FMT_plx "/" TARGET_FMT_plx "\n",
+                       indent, "", s->mmio[i].addr, size);
+    }
+}
+
+static char *sysbus_get_fw_dev_path(DeviceState *dev)
+{
+    SysBusDevice *s = SYS_BUS_DEVICE(dev);
+    char path[40];
+    int off;
+
+    off = snprintf(path, sizeof(path), "%s", qdev_fw_name(dev));
+
+    if (s->num_mmio) {
+        snprintf(path + off, sizeof(path) - off, "@"TARGET_FMT_plx,
+                 s->mmio[0].addr);
+    } else if (s->num_pio) {
+        snprintf(path + off, sizeof(path) - off, "@i%04x", s->pio[0]);
+    }
+
+    return g_strdup(path);
+}
+
+void sysbus_add_io(SysBusDevice *dev, hwaddr addr,
+                       MemoryRegion *mem)
+{
+    memory_region_add_subregion(get_system_io(), addr, mem);
+}
+
+void sysbus_del_io(SysBusDevice *dev, MemoryRegion *mem)
+{
+    memory_region_del_subregion(get_system_io(), mem);
+}
+
+MemoryRegion *sysbus_address_space(SysBusDevice *dev)
+{
+    return get_system_memory();
+}
+
+static void sysbus_device_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *k = DEVICE_CLASS(klass);
+    k->init = sysbus_device_init;
+    k->bus_type = TYPE_SYSTEM_BUS;
+}
+
+static const TypeInfo sysbus_device_type_info = {
+    .name = TYPE_SYS_BUS_DEVICE,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(SysBusDevice),
+    .abstract = true,
+    .class_size = sizeof(SysBusDeviceClass),
+    .class_init = sysbus_device_class_init,
+};
+
+/* This is a nasty hack to allow passing a NULL bus to qdev_create.  */
+static BusState *main_system_bus;
+
+static void main_system_bus_create(void)
+{
+    /* assign main_system_bus before qbus_create_inplace()
+     * in order to make "if (bus != sysbus_get_default())" work */
+    main_system_bus = g_malloc0(system_bus_info.instance_size);
+    qbus_create_inplace(main_system_bus, TYPE_SYSTEM_BUS, NULL,
+                        "main-system-bus");
+    OBJECT(main_system_bus)->free = g_free;
+    object_property_add_child(container_get(qdev_get_machine(),
+                                            "/unattached"),
+                              "sysbus", OBJECT(main_system_bus), NULL);
+}
+
+BusState *sysbus_get_default(void)
+{
+    if (!main_system_bus) {
+        main_system_bus_create();
+    }
+    return main_system_bus;
+}
+
+static void sysbus_register_types(void)
+{
+    type_register_static(&system_bus_info);
+    type_register_static(&sysbus_device_type_info);
+}
+
+type_init(sysbus_register_types)
diff --git a/hw/cs4231a.c b/hw/cs4231a.c
deleted file mode 100644 (file)
index 5711b62..0000000
+++ /dev/null
@@ -1,697 +0,0 @@
-/*
- * QEMU Crystal CS4231 audio chip emulation
- *
- * Copyright (c) 2006 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/audio/audio.h"
-#include "audio/audio.h"
-#include "hw/isa/isa.h"
-#include "hw/qdev.h"
-#include "qemu/timer.h"
-
-/*
-  Missing features:
-  ADC
-  Loopback
-  Timer
-  ADPCM
-  More...
-*/
-
-/* #define DEBUG */
-/* #define DEBUG_XLAW */
-
-static struct {
-    int aci_counter;
-} conf = {1};
-
-#ifdef DEBUG
-#define dolog(...) AUD_log ("cs4231a", __VA_ARGS__)
-#else
-#define dolog(...)
-#endif
-
-#define lwarn(...) AUD_log ("cs4231a", "warning: " __VA_ARGS__)
-#define lerr(...) AUD_log ("cs4231a", "error: " __VA_ARGS__)
-
-#define CS_REGS 16
-#define CS_DREGS 32
-
-typedef struct CSState {
-    ISADevice dev;
-    QEMUSoundCard card;
-    MemoryRegion ioports;
-    qemu_irq pic;
-    uint32_t regs[CS_REGS];
-    uint8_t dregs[CS_DREGS];
-    uint32_t irq;
-    uint32_t dma;
-    uint32_t port;
-    int shift;
-    int dma_running;
-    int audio_free;
-    int transferred;
-    int aci_counter;
-    SWVoiceOut *voice;
-    int16_t *tab;
-} CSState;
-
-#define MODE2 (1 << 6)
-#define MCE (1 << 6)
-#define PMCE (1 << 4)
-#define CMCE (1 << 5)
-#define TE (1 << 6)
-#define PEN (1 << 0)
-#define INT (1 << 0)
-#define IEN (1 << 1)
-#define PPIO (1 << 6)
-#define PI (1 << 4)
-#define CI (1 << 5)
-#define TI (1 << 6)
-
-enum {
-    Index_Address,
-    Index_Data,
-    Status,
-    PIO_Data
-};
-
-enum {
-    Left_ADC_Input_Control,
-    Right_ADC_Input_Control,
-    Left_AUX1_Input_Control,
-    Right_AUX1_Input_Control,
-    Left_AUX2_Input_Control,
-    Right_AUX2_Input_Control,
-    Left_DAC_Output_Control,
-    Right_DAC_Output_Control,
-    FS_And_Playback_Data_Format,
-    Interface_Configuration,
-    Pin_Control,
-    Error_Status_And_Initialization,
-    MODE_And_ID,
-    Loopback_Control,
-    Playback_Upper_Base_Count,
-    Playback_Lower_Base_Count,
-    Alternate_Feature_Enable_I,
-    Alternate_Feature_Enable_II,
-    Left_Line_Input_Control,
-    Right_Line_Input_Control,
-    Timer_Low_Base,
-    Timer_High_Base,
-    RESERVED,
-    Alternate_Feature_Enable_III,
-    Alternate_Feature_Status,
-    Version_Chip_ID,
-    Mono_Input_And_Output_Control,
-    RESERVED_2,
-    Capture_Data_Format,
-    RESERVED_3,
-    Capture_Upper_Base_Count,
-    Capture_Lower_Base_Count
-};
-
-static int freqs[2][8] = {
-    { 8000, 16000, 27420, 32000,    -1,    -1, 48000, 9000 },
-    { 5510, 11025, 18900, 22050, 37800, 44100, 33075, 6620 }
-};
-
-/* Tables courtesy http://hazelware.luggle.com/tutorials/mulawcompression.html */
-static int16_t MuLawDecompressTable[256] =
-{
-     -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
-     -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
-     -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
-     -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
-      -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
-      -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
-      -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
-      -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
-      -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
-      -1372, -1308, -1244, -1180, -1116, -1052,  -988,  -924,
-       -876,  -844,  -812,  -780,  -748,  -716,  -684,  -652,
-       -620,  -588,  -556,  -524,  -492,  -460,  -428,  -396,
-       -372,  -356,  -340,  -324,  -308,  -292,  -276,  -260,
-       -244,  -228,  -212,  -196,  -180,  -164,  -148,  -132,
-       -120,  -112,  -104,   -96,   -88,   -80,   -72,   -64,
-        -56,   -48,   -40,   -32,   -24,   -16,    -8,     0,
-      32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
-      23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
-      15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
-      11900, 11388, 10876, 10364,  9852,  9340,  8828,  8316,
-       7932,  7676,  7420,  7164,  6908,  6652,  6396,  6140,
-       5884,  5628,  5372,  5116,  4860,  4604,  4348,  4092,
-       3900,  3772,  3644,  3516,  3388,  3260,  3132,  3004,
-       2876,  2748,  2620,  2492,  2364,  2236,  2108,  1980,
-       1884,  1820,  1756,  1692,  1628,  1564,  1500,  1436,
-       1372,  1308,  1244,  1180,  1116,  1052,   988,   924,
-        876,   844,   812,   780,   748,   716,   684,   652,
-        620,   588,   556,   524,   492,   460,   428,   396,
-        372,   356,   340,   324,   308,   292,   276,   260,
-        244,   228,   212,   196,   180,   164,   148,   132,
-        120,   112,   104,    96,    88,    80,    72,    64,
-         56,    48,    40,    32,    24,    16,     8,     0
-};
-
-static int16_t ALawDecompressTable[256] =
-{
-     -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
-     -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
-     -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
-     -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
-     -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
-     -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
-     -11008,-10496,-12032,-11520,-8960, -8448, -9984, -9472,
-     -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
-     -344,  -328,  -376,  -360,  -280,  -264,  -312,  -296,
-     -472,  -456,  -504,  -488,  -408,  -392,  -440,  -424,
-     -88,   -72,   -120,  -104,  -24,   -8,    -56,   -40,
-     -216,  -200,  -248,  -232,  -152,  -136,  -184,  -168,
-     -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
-     -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
-     -688,  -656,  -752,  -720,  -560,  -528,  -624,  -592,
-     -944,  -912,  -1008, -976,  -816,  -784,  -880,  -848,
-      5504,  5248,  6016,  5760,  4480,  4224,  4992,  4736,
-      7552,  7296,  8064,  7808,  6528,  6272,  7040,  6784,
-      2752,  2624,  3008,  2880,  2240,  2112,  2496,  2368,
-      3776,  3648,  4032,  3904,  3264,  3136,  3520,  3392,
-      22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
-      30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
-      11008, 10496, 12032, 11520, 8960,  8448,  9984,  9472,
-      15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
-      344,   328,   376,   360,   280,   264,   312,   296,
-      472,   456,   504,   488,   408,   392,   440,   424,
-      88,    72,   120,   104,    24,     8,    56,    40,
-      216,   200,   248,   232,   152,   136,   184,   168,
-      1376,  1312,  1504,  1440,  1120,  1056,  1248,  1184,
-      1888,  1824,  2016,  1952,  1632,  1568,  1760,  1696,
-      688,   656,   752,   720,   560,   528,   624,   592,
-      944,   912,  1008,   976,   816,   784,   880,   848
-};
-
-static void cs_reset (void *opaque)
-{
-    CSState *s = opaque;
-
-    s->regs[Index_Address] = 0x40;
-    s->regs[Index_Data]    = 0x00;
-    s->regs[Status]        = 0x00;
-    s->regs[PIO_Data]      = 0x00;
-
-    s->dregs[Left_ADC_Input_Control]          = 0x00;
-    s->dregs[Right_ADC_Input_Control]         = 0x00;
-    s->dregs[Left_AUX1_Input_Control]         = 0x88;
-    s->dregs[Right_AUX1_Input_Control]        = 0x88;
-    s->dregs[Left_AUX2_Input_Control]         = 0x88;
-    s->dregs[Right_AUX2_Input_Control]        = 0x88;
-    s->dregs[Left_DAC_Output_Control]         = 0x80;
-    s->dregs[Right_DAC_Output_Control]        = 0x80;
-    s->dregs[FS_And_Playback_Data_Format]     = 0x00;
-    s->dregs[Interface_Configuration]         = 0x08;
-    s->dregs[Pin_Control]                     = 0x00;
-    s->dregs[Error_Status_And_Initialization] = 0x00;
-    s->dregs[MODE_And_ID]                     = 0x8a;
-    s->dregs[Loopback_Control]                = 0x00;
-    s->dregs[Playback_Upper_Base_Count]       = 0x00;
-    s->dregs[Playback_Lower_Base_Count]       = 0x00;
-    s->dregs[Alternate_Feature_Enable_I]      = 0x00;
-    s->dregs[Alternate_Feature_Enable_II]     = 0x00;
-    s->dregs[Left_Line_Input_Control]         = 0x88;
-    s->dregs[Right_Line_Input_Control]        = 0x88;
-    s->dregs[Timer_Low_Base]                  = 0x00;
-    s->dregs[Timer_High_Base]                 = 0x00;
-    s->dregs[RESERVED]                        = 0x00;
-    s->dregs[Alternate_Feature_Enable_III]    = 0x00;
-    s->dregs[Alternate_Feature_Status]        = 0x00;
-    s->dregs[Version_Chip_ID]                 = 0xa0;
-    s->dregs[Mono_Input_And_Output_Control]   = 0xa0;
-    s->dregs[RESERVED_2]                      = 0x00;
-    s->dregs[Capture_Data_Format]             = 0x00;
-    s->dregs[RESERVED_3]                      = 0x00;
-    s->dregs[Capture_Upper_Base_Count]        = 0x00;
-    s->dregs[Capture_Lower_Base_Count]        = 0x00;
-}
-
-static void cs_audio_callback (void *opaque, int free)
-{
-    CSState *s = opaque;
-    s->audio_free = free;
-}
-
-static void cs_reset_voices (CSState *s, uint32_t val)
-{
-    int xtal;
-    struct audsettings as;
-
-#ifdef DEBUG_XLAW
-    if (val == 0 || val == 32)
-        val = (1 << 4) | (1 << 5);
-#endif
-
-    xtal = val & 1;
-    as.freq = freqs[xtal][(val >> 1) & 7];
-
-    if (as.freq == -1) {
-        lerr ("unsupported frequency (val=%#x)\n", val);
-        goto error;
-    }
-
-    as.nchannels = (val & (1 << 4)) ? 2 : 1;
-    as.endianness = 0;
-    s->tab = NULL;
-
-    switch ((val >> 5) & ((s->dregs[MODE_And_ID] & MODE2) ? 7 : 3)) {
-    case 0:
-        as.fmt = AUD_FMT_U8;
-        s->shift = as.nchannels == 2;
-        break;
-
-    case 1:
-        s->tab = MuLawDecompressTable;
-        goto x_law;
-    case 3:
-        s->tab = ALawDecompressTable;
-    x_law:
-        as.fmt = AUD_FMT_S16;
-        as.endianness = AUDIO_HOST_ENDIANNESS;
-        s->shift = as.nchannels == 2;
-        break;
-
-    case 6:
-        as.endianness = 1;
-    case 2:
-        as.fmt = AUD_FMT_S16;
-        s->shift = as.nchannels;
-        break;
-
-    case 7:
-    case 4:
-        lerr ("attempt to use reserved format value (%#x)\n", val);
-        goto error;
-
-    case 5:
-        lerr ("ADPCM 4 bit IMA compatible format is not supported\n");
-        goto error;
-    }
-
-    s->voice = AUD_open_out (
-        &s->card,
-        s->voice,
-        "cs4231a",
-        s,
-        cs_audio_callback,
-        &as
-        );
-
-    if (s->dregs[Interface_Configuration] & PEN) {
-        if (!s->dma_running) {
-            DMA_hold_DREQ (s->dma);
-            AUD_set_active_out (s->voice, 1);
-            s->transferred = 0;
-        }
-        s->dma_running = 1;
-    }
-    else {
-        if (s->dma_running) {
-            DMA_release_DREQ (s->dma);
-            AUD_set_active_out (s->voice, 0);
-        }
-        s->dma_running = 0;
-    }
-    return;
-
- error:
-    if (s->dma_running) {
-        DMA_release_DREQ (s->dma);
-        AUD_set_active_out (s->voice, 0);
-    }
-}
-
-static uint64_t cs_read (void *opaque, hwaddr addr, unsigned size)
-{
-    CSState *s = opaque;
-    uint32_t saddr, iaddr, ret;
-
-    saddr = addr;
-    iaddr = ~0U;
-
-    switch (saddr) {
-    case Index_Address:
-        ret = s->regs[saddr] & ~0x80;
-        break;
-
-    case Index_Data:
-        if (!(s->dregs[MODE_And_ID] & MODE2))
-            iaddr = s->regs[Index_Address] & 0x0f;
-        else
-            iaddr = s->regs[Index_Address] & 0x1f;
-
-        ret = s->dregs[iaddr];
-        if (iaddr == Error_Status_And_Initialization) {
-            /* keep SEAL happy */
-            if (s->aci_counter) {
-                ret |= 1 << 5;
-                s->aci_counter -= 1;
-            }
-        }
-        break;
-
-    default:
-        ret = s->regs[saddr];
-        break;
-    }
-    dolog ("read %d:%d -> %d\n", saddr, iaddr, ret);
-    return ret;
-}
-
-static void cs_write (void *opaque, hwaddr addr,
-                      uint64_t val64, unsigned size)
-{
-    CSState *s = opaque;
-    uint32_t saddr, iaddr, val;
-
-    saddr = addr;
-    val = val64;
-
-    switch (saddr) {
-    case Index_Address:
-        if (!(s->regs[Index_Address] & MCE) && (val & MCE)
-            && (s->dregs[Interface_Configuration] & (3 << 3)))
-            s->aci_counter = conf.aci_counter;
-
-        s->regs[Index_Address] = val & ~(1 << 7);
-        break;
-
-    case Index_Data:
-        if (!(s->dregs[MODE_And_ID] & MODE2))
-            iaddr = s->regs[Index_Address] & 0x0f;
-        else
-            iaddr = s->regs[Index_Address] & 0x1f;
-
-        switch (iaddr) {
-        case RESERVED:
-        case RESERVED_2:
-        case RESERVED_3:
-            lwarn ("attempt to write %#x to reserved indirect register %d\n",
-                   val, iaddr);
-            break;
-
-        case FS_And_Playback_Data_Format:
-            if (s->regs[Index_Address] & MCE) {
-                cs_reset_voices (s, val);
-            }
-            else {
-                if (s->dregs[Alternate_Feature_Status] & PMCE) {
-                    val = (val & ~0x0f) | (s->dregs[iaddr] & 0x0f);
-                    cs_reset_voices (s, val);
-                }
-                else {
-                    lwarn ("[P]MCE(%#x, %#x) is not set, val=%#x\n",
-                           s->regs[Index_Address],
-                           s->dregs[Alternate_Feature_Status],
-                           val);
-                    break;
-                }
-            }
-            s->dregs[iaddr] = val;
-            break;
-
-        case Interface_Configuration:
-            val &= ~(1 << 5);   /* D5 is reserved */
-            s->dregs[iaddr] = val;
-            if (val & PPIO) {
-                lwarn ("PIO is not supported (%#x)\n", val);
-                break;
-            }
-            if (val & PEN) {
-                if (!s->dma_running) {
-                    cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]);
-                }
-            }
-            else {
-                if (s->dma_running) {
-                    DMA_release_DREQ (s->dma);
-                    AUD_set_active_out (s->voice, 0);
-                    s->dma_running = 0;
-                }
-            }
-            break;
-
-        case Error_Status_And_Initialization:
-            lwarn ("attempt to write to read only register %d\n", iaddr);
-            break;
-
-        case MODE_And_ID:
-            dolog ("val=%#x\n", val);
-            if (val & MODE2)
-                s->dregs[iaddr] |= MODE2;
-            else
-                s->dregs[iaddr] &= ~MODE2;
-            break;
-
-        case Alternate_Feature_Enable_I:
-            if (val & TE)
-                lerr ("timer is not yet supported\n");
-            s->dregs[iaddr] = val;
-            break;
-
-        case Alternate_Feature_Status:
-            if ((s->dregs[iaddr] & PI) && !(val & PI)) {
-                /* XXX: TI CI */
-                qemu_irq_lower (s->pic);
-                s->regs[Status] &= ~INT;
-            }
-            s->dregs[iaddr] = val;
-            break;
-
-        case Version_Chip_ID:
-            lwarn ("write to Version_Chip_ID register %#x\n", val);
-            s->dregs[iaddr] = val;
-            break;
-
-        default:
-            s->dregs[iaddr] = val;
-            break;
-        }
-        dolog ("written value %#x to indirect register %d\n", val, iaddr);
-        break;
-
-    case Status:
-        if (s->regs[Status] & INT) {
-            qemu_irq_lower (s->pic);
-        }
-        s->regs[Status] &= ~INT;
-        s->dregs[Alternate_Feature_Status] &= ~(PI | CI | TI);
-        break;
-
-    case PIO_Data:
-        lwarn ("attempt to write value %#x to PIO register\n", val);
-        break;
-    }
-}
-
-static int cs_write_audio (CSState *s, int nchan, int dma_pos,
-                           int dma_len, int len)
-{
-    int temp, net;
-    uint8_t tmpbuf[4096];
-
-    temp = len;
-    net = 0;
-
-    while (temp) {
-        int left = dma_len - dma_pos;
-        int copied;
-        size_t to_copy;
-
-        to_copy = audio_MIN (temp, left);
-        if (to_copy > sizeof (tmpbuf)) {
-            to_copy = sizeof (tmpbuf);
-        }
-
-        copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy);
-        if (s->tab) {
-            int i;
-            int16_t linbuf[4096];
-
-            for (i = 0; i < copied; ++i)
-                linbuf[i] = s->tab[tmpbuf[i]];
-            copied = AUD_write (s->voice, linbuf, copied << 1);
-            copied >>= 1;
-        }
-        else {
-            copied = AUD_write (s->voice, tmpbuf, copied);
-        }
-
-        temp -= copied;
-        dma_pos = (dma_pos + copied) % dma_len;
-        net += copied;
-
-        if (!copied) {
-            break;
-        }
-    }
-
-    return net;
-}
-
-static int cs_dma_read (void *opaque, int nchan, int dma_pos, int dma_len)
-{
-    CSState *s = opaque;
-    int copy, written;
-    int till = -1;
-
-    copy = s->voice ? (s->audio_free >> (s->tab != NULL)) : dma_len;
-
-    if (s->dregs[Pin_Control] & IEN) {
-        till = (s->dregs[Playback_Lower_Base_Count]
-            | (s->dregs[Playback_Upper_Base_Count] << 8)) << s->shift;
-        till -= s->transferred;
-        copy = audio_MIN (till, copy);
-    }
-
-    if ((copy <= 0) || (dma_len <= 0)) {
-        return dma_pos;
-    }
-
-    written = cs_write_audio (s, nchan, dma_pos, dma_len, copy);
-
-    dma_pos = (dma_pos + written) % dma_len;
-    s->audio_free -= (written << (s->tab != NULL));
-
-    if (written == till) {
-        s->regs[Status] |= INT;
-        s->dregs[Alternate_Feature_Status] |= PI;
-        s->transferred = 0;
-        qemu_irq_raise (s->pic);
-    }
-    else {
-        s->transferred += written;
-    }
-
-    return dma_pos;
-}
-
-static int cs4231a_pre_load (void *opaque)
-{
-    CSState *s = opaque;
-
-    if (s->dma_running) {
-        DMA_release_DREQ (s->dma);
-        AUD_set_active_out (s->voice, 0);
-    }
-    s->dma_running = 0;
-    return 0;
-}
-
-static int cs4231a_post_load (void *opaque, int version_id)
-{
-    CSState *s = opaque;
-
-    if (s->dma_running && (s->dregs[Interface_Configuration] & PEN)) {
-        s->dma_running = 0;
-        cs_reset_voices (s, s->dregs[FS_And_Playback_Data_Format]);
-    }
-    return 0;
-}
-
-static const VMStateDescription vmstate_cs4231a = {
-    .name = "cs4231a",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .pre_load = cs4231a_pre_load,
-    .post_load = cs4231a_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT32_ARRAY (regs, CSState, CS_REGS),
-        VMSTATE_BUFFER (dregs, CSState),
-        VMSTATE_INT32 (dma_running, CSState),
-        VMSTATE_INT32 (audio_free, CSState),
-        VMSTATE_INT32 (transferred, CSState),
-        VMSTATE_INT32 (aci_counter, CSState),
-        VMSTATE_END_OF_LIST ()
-    }
-};
-
-static const MemoryRegionOps cs_ioport_ops = {
-    .read = cs_read,
-    .write = cs_write,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    }
-};
-
-static int cs4231a_initfn (ISADevice *dev)
-{
-    CSState *s = DO_UPCAST (CSState, dev, dev);
-
-    isa_init_irq (dev, &s->pic, s->irq);
-
-    memory_region_init_io (&s->ioports, &cs_ioport_ops, s, "cs4231a", 4);
-    isa_register_ioport (dev, &s->ioports, s->port);
-
-    DMA_register_channel (s->dma, cs_dma_read, s);
-
-    qemu_register_reset (cs_reset, s);
-    cs_reset (s);
-
-    AUD_register_card ("cs4231a", &s->card);
-    return 0;
-}
-
-int cs4231a_init (ISABus *bus)
-{
-    isa_create_simple (bus, "cs4231a");
-    return 0;
-}
-
-static Property cs4231a_properties[] = {
-    DEFINE_PROP_HEX32  ("iobase",  CSState, port, 0x534),
-    DEFINE_PROP_UINT32 ("irq",     CSState, irq,  9),
-    DEFINE_PROP_UINT32 ("dma",     CSState, dma,  3),
-    DEFINE_PROP_END_OF_LIST (),
-};
-
-static void cs4231a_class_initfn (ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS (klass);
-    ISADeviceClass *ic = ISA_DEVICE_CLASS (klass);
-    ic->init = cs4231a_initfn;
-    dc->desc = "Crystal Semiconductor CS4231A";
-    dc->vmsd = &vmstate_cs4231a;
-    dc->props = cs4231a_properties;
-}
-
-static const TypeInfo cs4231a_info = {
-    .name          = "cs4231a",
-    .parent        = TYPE_ISA_DEVICE,
-    .instance_size = sizeof (CSState),
-    .class_init    = cs4231a_class_initfn,
-};
-
-static void cs4231a_register_types (void)
-{
-    type_register_static (&cs4231a_info);
-}
-
-type_init (cs4231a_register_types)
diff --git a/hw/cuda.c b/hw/cuda.c
deleted file mode 100644 (file)
index f797796..0000000
--- a/hw/cuda.c
+++ /dev/null
@@ -1,740 +0,0 @@
-/*
- * QEMU PowerMac CUDA device support
- *
- * Copyright (c) 2004-2007 Fabrice Bellard
- * Copyright (c) 2007 Jocelyn Mayer
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/ppc/mac.h"
-#include "hw/input/adb.h"
-#include "qemu/timer.h"
-#include "sysemu/sysemu.h"
-
-/* XXX: implement all timer modes */
-
-/* debug CUDA */
-//#define DEBUG_CUDA
-
-/* debug CUDA packets */
-//#define DEBUG_CUDA_PACKET
-
-#ifdef DEBUG_CUDA
-#define CUDA_DPRINTF(fmt, ...)                                  \
-    do { printf("CUDA: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define CUDA_DPRINTF(fmt, ...)
-#endif
-
-/* Bits in B data register: all active low */
-#define TREQ           0x08            /* Transfer request (input) */
-#define TACK           0x10            /* Transfer acknowledge (output) */
-#define TIP            0x20            /* Transfer in progress (output) */
-
-/* Bits in ACR */
-#define SR_CTRL                0x1c            /* Shift register control bits */
-#define SR_EXT         0x0c            /* Shift on external clock */
-#define SR_OUT         0x10            /* Shift out if 1 */
-
-/* Bits in IFR and IER */
-#define IER_SET                0x80            /* set bits in IER */
-#define IER_CLR                0               /* clear bits in IER */
-#define SR_INT         0x04            /* Shift register full/empty */
-#define T1_INT          0x40            /* Timer 1 interrupt */
-#define T2_INT          0x20            /* Timer 2 interrupt */
-
-/* Bits in ACR */
-#define T1MODE          0xc0            /* Timer 1 mode */
-#define T1MODE_CONT     0x40            /*  continuous interrupts */
-
-/* commands (1st byte) */
-#define ADB_PACKET     0
-#define CUDA_PACKET    1
-#define ERROR_PACKET   2
-#define TIMER_PACKET   3
-#define POWER_PACKET   4
-#define MACIIC_PACKET  5
-#define PMU_PACKET     6
-
-
-/* CUDA commands (2nd byte) */
-#define CUDA_WARM_START                        0x0
-#define CUDA_AUTOPOLL                  0x1
-#define CUDA_GET_6805_ADDR             0x2
-#define CUDA_GET_TIME                  0x3
-#define CUDA_GET_PRAM                  0x7
-#define CUDA_SET_6805_ADDR             0x8
-#define CUDA_SET_TIME                  0x9
-#define CUDA_POWERDOWN                 0xa
-#define CUDA_POWERUP_TIME              0xb
-#define CUDA_SET_PRAM                  0xc
-#define CUDA_MS_RESET                  0xd
-#define CUDA_SEND_DFAC                 0xe
-#define CUDA_BATTERY_SWAP_SENSE                0x10
-#define CUDA_RESET_SYSTEM              0x11
-#define CUDA_SET_IPL                   0x12
-#define CUDA_FILE_SERVER_FLAG          0x13
-#define CUDA_SET_AUTO_RATE             0x14
-#define CUDA_GET_AUTO_RATE             0x16
-#define CUDA_SET_DEVICE_LIST           0x19
-#define CUDA_GET_DEVICE_LIST           0x1a
-#define CUDA_SET_ONE_SECOND_MODE       0x1b
-#define CUDA_SET_POWER_MESSAGES                0x21
-#define CUDA_GET_SET_IIC               0x22
-#define CUDA_WAKEUP                    0x23
-#define CUDA_TIMER_TICKLE              0x24
-#define CUDA_COMBINED_FORMAT_IIC       0x25
-
-#define CUDA_TIMER_FREQ (4700000 / 6)
-#define CUDA_ADB_POLL_FREQ 50
-
-/* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */
-#define RTC_OFFSET                      2082844800
-
-static void cuda_update(CUDAState *s);
-static void cuda_receive_packet_from_host(CUDAState *s,
-                                          const uint8_t *data, int len);
-static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
-                              int64_t current_time);
-
-static void cuda_update_irq(CUDAState *s)
-{
-    if (s->ifr & s->ier & (SR_INT | T1_INT)) {
-        qemu_irq_raise(s->irq);
-    } else {
-        qemu_irq_lower(s->irq);
-    }
-}
-
-static unsigned int get_counter(CUDATimer *s)
-{
-    int64_t d;
-    unsigned int counter;
-
-    d = muldiv64(qemu_get_clock_ns(vm_clock) - s->load_time,
-                 CUDA_TIMER_FREQ, get_ticks_per_sec());
-    if (s->index == 0) {
-        /* the timer goes down from latch to -1 (period of latch + 2) */
-        if (d <= (s->counter_value + 1)) {
-            counter = (s->counter_value - d) & 0xffff;
-        } else {
-            counter = (d - (s->counter_value + 1)) % (s->latch + 2);
-            counter = (s->latch - counter) & 0xffff;
-        }
-    } else {
-        counter = (s->counter_value - d) & 0xffff;
-    }
-    return counter;
-}
-
-static void set_counter(CUDAState *s, CUDATimer *ti, unsigned int val)
-{
-    CUDA_DPRINTF("T%d.counter=%d\n", 1 + (ti->timer == NULL), val);
-    ti->load_time = qemu_get_clock_ns(vm_clock);
-    ti->counter_value = val;
-    cuda_timer_update(s, ti, ti->load_time);
-}
-
-static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time)
-{
-    int64_t d, next_time;
-    unsigned int counter;
-
-    /* current counter value */
-    d = muldiv64(current_time - s->load_time,
-                 CUDA_TIMER_FREQ, get_ticks_per_sec());
-    /* the timer goes down from latch to -1 (period of latch + 2) */
-    if (d <= (s->counter_value + 1)) {
-        counter = (s->counter_value - d) & 0xffff;
-    } else {
-        counter = (d - (s->counter_value + 1)) % (s->latch + 2);
-        counter = (s->latch - counter) & 0xffff;
-    }
-
-    /* Note: we consider the irq is raised on 0 */
-    if (counter == 0xffff) {
-        next_time = d + s->latch + 1;
-    } else if (counter == 0) {
-        next_time = d + s->latch + 2;
-    } else {
-        next_time = d + counter;
-    }
-    CUDA_DPRINTF("latch=%d counter=%" PRId64 " delta_next=%" PRId64 "\n",
-                 s->latch, d, next_time - d);
-    next_time = muldiv64(next_time, get_ticks_per_sec(), CUDA_TIMER_FREQ) +
-        s->load_time;
-    if (next_time <= current_time)
-        next_time = current_time + 1;
-    return next_time;
-}
-
-static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
-                              int64_t current_time)
-{
-    if (!ti->timer)
-        return;
-    if ((s->acr & T1MODE) != T1MODE_CONT) {
-        qemu_del_timer(ti->timer);
-    } else {
-        ti->next_irq_time = get_next_irq_time(ti, current_time);
-        qemu_mod_timer(ti->timer, ti->next_irq_time);
-    }
-}
-
-static void cuda_timer1(void *opaque)
-{
-    CUDAState *s = opaque;
-    CUDATimer *ti = &s->timers[0];
-
-    cuda_timer_update(s, ti, ti->next_irq_time);
-    s->ifr |= T1_INT;
-    cuda_update_irq(s);
-}
-
-static uint32_t cuda_readb(void *opaque, hwaddr addr)
-{
-    CUDAState *s = opaque;
-    uint32_t val;
-
-    addr = (addr >> 9) & 0xf;
-    switch(addr) {
-    case 0:
-        val = s->b;
-        break;
-    case 1:
-        val = s->a;
-        break;
-    case 2:
-        val = s->dirb;
-        break;
-    case 3:
-        val = s->dira;
-        break;
-    case 4:
-        val = get_counter(&s->timers[0]) & 0xff;
-        s->ifr &= ~T1_INT;
-        cuda_update_irq(s);
-        break;
-    case 5:
-        val = get_counter(&s->timers[0]) >> 8;
-        cuda_update_irq(s);
-        break;
-    case 6:
-        val = s->timers[0].latch & 0xff;
-        break;
-    case 7:
-        /* XXX: check this */
-        val = (s->timers[0].latch >> 8) & 0xff;
-        break;
-    case 8:
-        val = get_counter(&s->timers[1]) & 0xff;
-        s->ifr &= ~T2_INT;
-        break;
-    case 9:
-        val = get_counter(&s->timers[1]) >> 8;
-        break;
-    case 10:
-        val = s->sr;
-        s->ifr &= ~SR_INT;
-        cuda_update_irq(s);
-        break;
-    case 11:
-        val = s->acr;
-        break;
-    case 12:
-        val = s->pcr;
-        break;
-    case 13:
-        val = s->ifr;
-        if (s->ifr & s->ier)
-            val |= 0x80;
-        break;
-    case 14:
-        val = s->ier | 0x80;
-        break;
-    default:
-    case 15:
-        val = s->anh;
-        break;
-    }
-    if (addr != 13 || val != 0) {
-        CUDA_DPRINTF("read: reg=0x%x val=%02x\n", (int)addr, val);
-    }
-
-    return val;
-}
-
-static void cuda_writeb(void *opaque, hwaddr addr, uint32_t val)
-{
-    CUDAState *s = opaque;
-
-    addr = (addr >> 9) & 0xf;
-    CUDA_DPRINTF("write: reg=0x%x val=%02x\n", (int)addr, val);
-
-    switch(addr) {
-    case 0:
-        s->b = val;
-        cuda_update(s);
-        break;
-    case 1:
-        s->a = val;
-        break;
-    case 2:
-        s->dirb = val;
-        break;
-    case 3:
-        s->dira = val;
-        break;
-    case 4:
-        s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
-        cuda_timer_update(s, &s->timers[0], qemu_get_clock_ns(vm_clock));
-        break;
-    case 5:
-        s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
-        s->ifr &= ~T1_INT;
-        set_counter(s, &s->timers[0], s->timers[0].latch);
-        break;
-    case 6:
-        s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
-        cuda_timer_update(s, &s->timers[0], qemu_get_clock_ns(vm_clock));
-        break;
-    case 7:
-        s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
-        s->ifr &= ~T1_INT;
-        cuda_timer_update(s, &s->timers[0], qemu_get_clock_ns(vm_clock));
-        break;
-    case 8:
-        s->timers[1].latch = val;
-        set_counter(s, &s->timers[1], val);
-        break;
-    case 9:
-        set_counter(s, &s->timers[1], (val << 8) | s->timers[1].latch);
-        break;
-    case 10:
-        s->sr = val;
-        break;
-    case 11:
-        s->acr = val;
-        cuda_timer_update(s, &s->timers[0], qemu_get_clock_ns(vm_clock));
-        cuda_update(s);
-        break;
-    case 12:
-        s->pcr = val;
-        break;
-    case 13:
-        /* reset bits */
-        s->ifr &= ~val;
-        cuda_update_irq(s);
-        break;
-    case 14:
-        if (val & IER_SET) {
-            /* set bits */
-            s->ier |= val & 0x7f;
-        } else {
-            /* reset bits */
-            s->ier &= ~val;
-        }
-        cuda_update_irq(s);
-        break;
-    default:
-    case 15:
-        s->anh = val;
-        break;
-    }
-}
-
-/* NOTE: TIP and TREQ are negated */
-static void cuda_update(CUDAState *s)
-{
-    int packet_received, len;
-
-    packet_received = 0;
-    if (!(s->b & TIP)) {
-        /* transfer requested from host */
-
-        if (s->acr & SR_OUT) {
-            /* data output */
-            if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
-                if (s->data_out_index < sizeof(s->data_out)) {
-                    CUDA_DPRINTF("send: %02x\n", s->sr);
-                    s->data_out[s->data_out_index++] = s->sr;
-                    s->ifr |= SR_INT;
-                    cuda_update_irq(s);
-                }
-            }
-        } else {
-            if (s->data_in_index < s->data_in_size) {
-                /* data input */
-                if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
-                    s->sr = s->data_in[s->data_in_index++];
-                    CUDA_DPRINTF("recv: %02x\n", s->sr);
-                    /* indicate end of transfer */
-                    if (s->data_in_index >= s->data_in_size) {
-                        s->b = (s->b | TREQ);
-                    }
-                    s->ifr |= SR_INT;
-                    cuda_update_irq(s);
-                }
-            }
-        }
-    } else {
-        /* no transfer requested: handle sync case */
-        if ((s->last_b & TIP) && (s->b & TACK) != (s->last_b & TACK)) {
-            /* update TREQ state each time TACK change state */
-            if (s->b & TACK)
-                s->b = (s->b | TREQ);
-            else
-                s->b = (s->b & ~TREQ);
-            s->ifr |= SR_INT;
-            cuda_update_irq(s);
-        } else {
-            if (!(s->last_b & TIP)) {
-                /* handle end of host to cuda transfer */
-                packet_received = (s->data_out_index > 0);
-                /* always an IRQ at the end of transfer */
-                s->ifr |= SR_INT;
-                cuda_update_irq(s);
-            }
-            /* signal if there is data to read */
-            if (s->data_in_index < s->data_in_size) {
-                s->b = (s->b & ~TREQ);
-            }
-        }
-    }
-
-    s->last_acr = s->acr;
-    s->last_b = s->b;
-
-    /* NOTE: cuda_receive_packet_from_host() can call cuda_update()
-       recursively */
-    if (packet_received) {
-        len = s->data_out_index;
-        s->data_out_index = 0;
-        cuda_receive_packet_from_host(s, s->data_out, len);
-    }
-}
-
-static void cuda_send_packet_to_host(CUDAState *s,
-                                     const uint8_t *data, int len)
-{
-#ifdef DEBUG_CUDA_PACKET
-    {
-        int i;
-        printf("cuda_send_packet_to_host:\n");
-        for(i = 0; i < len; i++)
-            printf(" %02x", data[i]);
-        printf("\n");
-    }
-#endif
-    memcpy(s->data_in, data, len);
-    s->data_in_size = len;
-    s->data_in_index = 0;
-    cuda_update(s);
-    s->ifr |= SR_INT;
-    cuda_update_irq(s);
-}
-
-static void cuda_adb_poll(void *opaque)
-{
-    CUDAState *s = opaque;
-    uint8_t obuf[ADB_MAX_OUT_LEN + 2];
-    int olen;
-
-    olen = adb_poll(&s->adb_bus, obuf + 2);
-    if (olen > 0) {
-        obuf[0] = ADB_PACKET;
-        obuf[1] = 0x40; /* polled data */
-        cuda_send_packet_to_host(s, obuf, olen + 2);
-    }
-    qemu_mod_timer(s->adb_poll_timer,
-                   qemu_get_clock_ns(vm_clock) +
-                   (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ));
-}
-
-static void cuda_receive_packet(CUDAState *s,
-                                const uint8_t *data, int len)
-{
-    uint8_t obuf[16];
-    int autopoll;
-    uint32_t ti;
-
-    switch(data[0]) {
-    case CUDA_AUTOPOLL:
-        autopoll = (data[1] != 0);
-        if (autopoll != s->autopoll) {
-            s->autopoll = autopoll;
-            if (autopoll) {
-                qemu_mod_timer(s->adb_poll_timer,
-                               qemu_get_clock_ns(vm_clock) +
-                               (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ));
-            } else {
-                qemu_del_timer(s->adb_poll_timer);
-            }
-        }
-        obuf[0] = CUDA_PACKET;
-        obuf[1] = data[1];
-        cuda_send_packet_to_host(s, obuf, 2);
-        break;
-    case CUDA_SET_TIME:
-        ti = (((uint32_t)data[1]) << 24) + (((uint32_t)data[2]) << 16) + (((uint32_t)data[3]) << 8) + data[4];
-        s->tick_offset = ti - (qemu_get_clock_ns(vm_clock) / get_ticks_per_sec());
-        obuf[0] = CUDA_PACKET;
-        obuf[1] = 0;
-        obuf[2] = 0;
-        cuda_send_packet_to_host(s, obuf, 3);
-        break;
-    case CUDA_GET_TIME:
-        ti = s->tick_offset + (qemu_get_clock_ns(vm_clock) / get_ticks_per_sec());
-        obuf[0] = CUDA_PACKET;
-        obuf[1] = 0;
-        obuf[2] = 0;
-        obuf[3] = ti >> 24;
-        obuf[4] = ti >> 16;
-        obuf[5] = ti >> 8;
-        obuf[6] = ti;
-        cuda_send_packet_to_host(s, obuf, 7);
-        break;
-    case CUDA_FILE_SERVER_FLAG:
-    case CUDA_SET_DEVICE_LIST:
-    case CUDA_SET_AUTO_RATE:
-    case CUDA_SET_POWER_MESSAGES:
-        obuf[0] = CUDA_PACKET;
-        obuf[1] = 0;
-        cuda_send_packet_to_host(s, obuf, 2);
-        break;
-    case CUDA_POWERDOWN:
-        obuf[0] = CUDA_PACKET;
-        obuf[1] = 0;
-        cuda_send_packet_to_host(s, obuf, 2);
-        qemu_system_shutdown_request();
-        break;
-    case CUDA_RESET_SYSTEM:
-        obuf[0] = CUDA_PACKET;
-        obuf[1] = 0;
-        cuda_send_packet_to_host(s, obuf, 2);
-        qemu_system_reset_request();
-        break;
-    default:
-        break;
-    }
-}
-
-static void cuda_receive_packet_from_host(CUDAState *s,
-                                          const uint8_t *data, int len)
-{
-#ifdef DEBUG_CUDA_PACKET
-    {
-        int i;
-        printf("cuda_receive_packet_from_host:\n");
-        for(i = 0; i < len; i++)
-            printf(" %02x", data[i]);
-        printf("\n");
-    }
-#endif
-    switch(data[0]) {
-    case ADB_PACKET:
-        {
-            uint8_t obuf[ADB_MAX_OUT_LEN + 2];
-            int olen;
-            olen = adb_request(&s->adb_bus, obuf + 2, data + 1, len - 1);
-            if (olen > 0) {
-                obuf[0] = ADB_PACKET;
-                obuf[1] = 0x00;
-            } else {
-                /* error */
-                obuf[0] = ADB_PACKET;
-                obuf[1] = -olen;
-                olen = 0;
-            }
-            cuda_send_packet_to_host(s, obuf, olen + 2);
-        }
-        break;
-    case CUDA_PACKET:
-        cuda_receive_packet(s, data + 1, len - 1);
-        break;
-    }
-}
-
-static void cuda_writew (void *opaque, hwaddr addr, uint32_t value)
-{
-}
-
-static void cuda_writel (void *opaque, hwaddr addr, uint32_t value)
-{
-}
-
-static uint32_t cuda_readw (void *opaque, hwaddr addr)
-{
-    return 0;
-}
-
-static uint32_t cuda_readl (void *opaque, hwaddr addr)
-{
-    return 0;
-}
-
-static const MemoryRegionOps cuda_ops = {
-    .old_mmio = {
-        .write = {
-            cuda_writeb,
-            cuda_writew,
-            cuda_writel,
-        },
-        .read = {
-            cuda_readb,
-            cuda_readw,
-            cuda_readl,
-        },
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static bool cuda_timer_exist(void *opaque, int version_id)
-{
-    CUDATimer *s = opaque;
-
-    return s->timer != NULL;
-}
-
-static const VMStateDescription vmstate_cuda_timer = {
-    .name = "cuda_timer",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT16(latch, CUDATimer),
-        VMSTATE_UINT16(counter_value, CUDATimer),
-        VMSTATE_INT64(load_time, CUDATimer),
-        VMSTATE_INT64(next_irq_time, CUDATimer),
-        VMSTATE_TIMER_TEST(timer, CUDATimer, cuda_timer_exist),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_cuda = {
-    .name = "cuda",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT8(a, CUDAState),
-        VMSTATE_UINT8(b, CUDAState),
-        VMSTATE_UINT8(dira, CUDAState),
-        VMSTATE_UINT8(dirb, CUDAState),
-        VMSTATE_UINT8(sr, CUDAState),
-        VMSTATE_UINT8(acr, CUDAState),
-        VMSTATE_UINT8(pcr, CUDAState),
-        VMSTATE_UINT8(ifr, CUDAState),
-        VMSTATE_UINT8(ier, CUDAState),
-        VMSTATE_UINT8(anh, CUDAState),
-        VMSTATE_INT32(data_in_size, CUDAState),
-        VMSTATE_INT32(data_in_index, CUDAState),
-        VMSTATE_INT32(data_out_index, CUDAState),
-        VMSTATE_UINT8(autopoll, CUDAState),
-        VMSTATE_BUFFER(data_in, CUDAState),
-        VMSTATE_BUFFER(data_out, CUDAState),
-        VMSTATE_UINT32(tick_offset, CUDAState),
-        VMSTATE_STRUCT_ARRAY(timers, CUDAState, 2, 1,
-                             vmstate_cuda_timer, CUDATimer),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void cuda_reset(DeviceState *dev)
-{
-    CUDAState *s = CUDA(dev);
-
-    s->b = 0;
-    s->a = 0;
-    s->dirb = 0;
-    s->dira = 0;
-    s->sr = 0;
-    s->acr = 0;
-    s->pcr = 0;
-    s->ifr = 0;
-    s->ier = 0;
-    //    s->ier = T1_INT | SR_INT;
-    s->anh = 0;
-    s->data_in_size = 0;
-    s->data_in_index = 0;
-    s->data_out_index = 0;
-    s->autopoll = 0;
-
-    s->timers[0].latch = 0xffff;
-    set_counter(s, &s->timers[0], 0xffff);
-
-    s->timers[1].latch = 0;
-    set_counter(s, &s->timers[1], 0xffff);
-}
-
-static void cuda_realizefn(DeviceState *dev, Error **errp)
-{
-    CUDAState *s = CUDA(dev);
-    struct tm tm;
-
-    s->timers[0].timer = qemu_new_timer_ns(vm_clock, cuda_timer1, s);
-
-    qemu_get_timedate(&tm, 0);
-    s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET;
-
-    s->adb_poll_timer = qemu_new_timer_ns(vm_clock, cuda_adb_poll, s);
-}
-
-static void cuda_initfn(Object *obj)
-{
-    SysBusDevice *d = SYS_BUS_DEVICE(obj);
-    CUDAState *s = CUDA(obj);
-    int i;
-
-    memory_region_init_io(&s->mem, &cuda_ops, s, "cuda", 0x2000);
-    sysbus_init_mmio(d, &s->mem);
-    sysbus_init_irq(d, &s->irq);
-
-    for (i = 0; i < ARRAY_SIZE(s->timers); i++) {
-        s->timers[i].index = i;
-    }
-
-    qbus_create_inplace((BusState *)&s->adb_bus, TYPE_ADB_BUS, DEVICE(obj),
-                        "adb.0");
-}
-
-static void cuda_class_init(ObjectClass *oc, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(oc);
-
-    dc->realize = cuda_realizefn;
-    dc->reset = cuda_reset;
-    dc->vmsd = &vmstate_cuda;
-}
-
-static const TypeInfo cuda_type_info = {
-    .name = TYPE_CUDA,
-    .parent = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(CUDAState),
-    .instance_init = cuda_initfn,
-    .class_init = cuda_class_init,
-};
-
-static void cuda_register_types(void)
-{
-    type_register_static(&cuda_type_info);
-}
-
-type_init(cuda_register_types)
diff --git a/hw/dec_pci.c b/hw/dec_pci.c
deleted file mode 100644 (file)
index 6ec3d22..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * QEMU DEC 21154 PCI bridge
- *
- * Copyright (c) 2006-2007 Fabrice Bellard
- * Copyright (c) 2007 Jocelyn Mayer
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/dec_pci.h"
-#include "hw/sysbus.h"
-#include "hw/pci/pci.h"
-#include "hw/pci/pci_host.h"
-#include "hw/pci/pci_bridge.h"
-#include "hw/pci/pci_bus.h"
-
-/* debug DEC */
-//#define DEBUG_DEC
-
-#ifdef DEBUG_DEC
-#define DEC_DPRINTF(fmt, ...)                               \
-    do { printf("DEC: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DEC_DPRINTF(fmt, ...)
-#endif
-
-#define DEC_21154(obj) OBJECT_CHECK(DECState, (obj), TYPE_DEC_21154)
-
-typedef struct DECState {
-    PCIHostState parent_obj;
-} DECState;
-
-static int dec_map_irq(PCIDevice *pci_dev, int irq_num)
-{
-    return irq_num;
-}
-
-static int dec_pci_bridge_initfn(PCIDevice *pci_dev)
-{
-    return pci_bridge_initfn(pci_dev, TYPE_PCI_BUS);
-}
-
-static void dec_21154_pci_bridge_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = dec_pci_bridge_initfn;
-    k->exit = pci_bridge_exitfn;
-    k->vendor_id = PCI_VENDOR_ID_DEC;
-    k->device_id = PCI_DEVICE_ID_DEC_21154;
-    k->config_write = pci_bridge_write_config;
-    k->is_bridge = 1;
-    dc->desc = "DEC 21154 PCI-PCI bridge";
-    dc->reset = pci_bridge_reset;
-    dc->vmsd = &vmstate_pci_device;
-}
-
-static const TypeInfo dec_21154_pci_bridge_info = {
-    .name          = "dec-21154-p2p-bridge",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIBridge),
-    .class_init    = dec_21154_pci_bridge_class_init,
-};
-
-PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn)
-{
-    PCIDevice *dev;
-    PCIBridge *br;
-
-    dev = pci_create_multifunction(parent_bus, devfn, false,
-                                   "dec-21154-p2p-bridge");
-    br = DO_UPCAST(PCIBridge, dev, dev);
-    pci_bridge_map_irq(br, "DEC 21154 PCI-PCI bridge", dec_map_irq);
-    qdev_init_nofail(&dev->qdev);
-    return pci_bridge_get_sec_bus(br);
-}
-
-static int pci_dec_21154_device_init(SysBusDevice *dev)
-{
-    PCIHostState *phb;
-
-    phb = PCI_HOST_BRIDGE(dev);
-
-    memory_region_init_io(&phb->conf_mem, &pci_host_conf_le_ops,
-                          dev, "pci-conf-idx", 0x1000);
-    memory_region_init_io(&phb->data_mem, &pci_host_data_le_ops,
-                          dev, "pci-data-idx", 0x1000);
-    sysbus_init_mmio(dev, &phb->conf_mem);
-    sysbus_init_mmio(dev, &phb->data_mem);
-    return 0;
-}
-
-static int dec_21154_pci_host_init(PCIDevice *d)
-{
-    /* PCI2PCI bridge same values as PearPC - check this */
-    return 0;
-}
-
-static void dec_21154_pci_host_class_init(ObjectClass *klass, void *data)
-{
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = dec_21154_pci_host_init;
-    k->vendor_id = PCI_VENDOR_ID_DEC;
-    k->device_id = PCI_DEVICE_ID_DEC_21154;
-    k->revision = 0x02;
-    k->class_id = PCI_CLASS_BRIDGE_PCI;
-    k->is_bridge = 1;
-}
-
-static const TypeInfo dec_21154_pci_host_info = {
-    .name          = "dec-21154",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIDevice),
-    .class_init    = dec_21154_pci_host_class_init,
-};
-
-static void pci_dec_21154_device_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sdc->init = pci_dec_21154_device_init;
-}
-
-static const TypeInfo pci_dec_21154_device_info = {
-    .name          = TYPE_DEC_21154,
-    .parent        = TYPE_PCI_HOST_BRIDGE,
-    .instance_size = sizeof(DECState),
-    .class_init    = pci_dec_21154_device_class_init,
-};
-
-static void dec_register_types(void)
-{
-    type_register_static(&pci_dec_21154_device_info);
-    type_register_static(&dec_21154_pci_host_info);
-    type_register_static(&dec_21154_pci_bridge_info);
-}
-
-type_init(dec_register_types)
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3ac154d708279201f65146443f0262de2b5125e4 100644 (file)
@@ -0,0 +1,13 @@
+common-obj-$(CONFIG_ADS7846) += ads7846.o
+common-obj-$(CONFIG_VGA_CIRRUS) += cirrus_vga.o
+common-obj-$(CONFIG_G364FB) += g364fb.o
+common-obj-$(CONFIG_JAZZ_LED) += jazz_led.o
+common-obj-$(CONFIG_PL110) += pl110.o
+common-obj-$(CONFIG_SSD0303) += ssd0303.o
+common-obj-$(CONFIG_SSD0323) += ssd0323.o
+common-obj-$(CONFIG_XEN_BACKEND) += xenfb.o
+
+common-obj-$(CONFIG_VGA_PCI) += vga-pci.o
+common-obj-$(CONFIG_VGA_ISA) += vga-isa.o
+common-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o
+common-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o
diff --git a/hw/display/ads7846.c b/hw/display/ads7846.c
new file mode 100644 (file)
index 0000000..5da3dc5
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * TI ADS7846 / TSC2046 chip emulation.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GNU GPL v2.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "hw/ssi.h"
+#include "ui/console.h"
+
+typedef struct {
+    SSISlave ssidev;
+    qemu_irq interrupt;
+
+    int input[8];
+    int pressure;
+    int noise;
+
+    int cycle;
+    int output;
+} ADS7846State;
+
+/* Control-byte bitfields */
+#define CB_PD0         (1 << 0)
+#define CB_PD1         (1 << 1)
+#define CB_SER         (1 << 2)
+#define CB_MODE                (1 << 3)
+#define CB_A0          (1 << 4)
+#define CB_A1          (1 << 5)
+#define CB_A2          (1 << 6)
+#define CB_START       (1 << 7)
+
+#define X_AXIS_DMAX    3470
+#define X_AXIS_MIN     290
+#define Y_AXIS_DMAX    3450
+#define Y_AXIS_MIN     200
+
+#define ADS_VBAT       2000
+#define ADS_VAUX       2000
+#define ADS_TEMP0      2000
+#define ADS_TEMP1      3000
+#define ADS_XPOS(x, y) (X_AXIS_MIN + ((X_AXIS_DMAX * (x)) >> 15))
+#define ADS_YPOS(x, y) (Y_AXIS_MIN + ((Y_AXIS_DMAX * (y)) >> 15))
+#define ADS_Z1POS(x, y)        600
+#define ADS_Z2POS(x, y)        (600 + 6000 / ADS_XPOS(x, y))
+
+static void ads7846_int_update(ADS7846State *s)
+{
+    if (s->interrupt)
+        qemu_set_irq(s->interrupt, s->pressure == 0);
+}
+
+static uint32_t ads7846_transfer(SSISlave *dev, uint32_t value)
+{
+    ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, dev);
+
+    switch (s->cycle ++) {
+    case 0:
+        if (!(value & CB_START)) {
+            s->cycle = 0;
+            break;
+        }
+
+        s->output = s->input[(value >> 4) & 7];
+
+        /* Imitate the ADC noise, some drivers expect this.  */
+        s->noise = (s->noise + 3) & 7;
+        switch ((value >> 4) & 7) {
+        case 1: s->output += s->noise ^ 2; break;
+        case 3: s->output += s->noise ^ 0; break;
+        case 4: s->output += s->noise ^ 7; break;
+        case 5: s->output += s->noise ^ 5; break;
+        }
+
+        if (value & CB_MODE)
+            s->output >>= 4;   /* 8 bits instead of 12 */
+
+        break;
+    case 1:
+        s->cycle = 0;
+        break;
+    }
+    return s->output;
+}
+
+static void ads7846_ts_event(void *opaque,
+                int x, int y, int z, int buttons_state)
+{
+    ADS7846State *s = opaque;
+
+    if (buttons_state) {
+        x = 0x7fff - x;
+        s->input[1] = ADS_XPOS(x, y);
+        s->input[3] = ADS_Z1POS(x, y);
+        s->input[4] = ADS_Z2POS(x, y);
+        s->input[5] = ADS_YPOS(x, y);
+    }
+
+    if (s->pressure == !buttons_state) {
+        s->pressure = !!buttons_state;
+
+        ads7846_int_update(s);
+    }
+}
+
+static int ads7856_post_load(void *opaque, int version_id)
+{
+    ADS7846State *s = opaque;
+
+    s->pressure = 0;
+    ads7846_int_update(s);
+    return 0;
+}
+
+static const VMStateDescription vmstate_ads7846 = {
+    .name = "ads7846",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = ads7856_post_load,
+    .fields      = (VMStateField[]) {
+        VMSTATE_SSI_SLAVE(ssidev, ADS7846State),
+        VMSTATE_INT32_ARRAY(input, ADS7846State, 8),
+        VMSTATE_INT32(noise, ADS7846State),
+        VMSTATE_INT32(cycle, ADS7846State),
+        VMSTATE_INT32(output, ADS7846State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int ads7846_init(SSISlave *dev)
+{
+    ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, dev);
+
+    qdev_init_gpio_out(&dev->qdev, &s->interrupt, 1);
+
+    s->input[0] = ADS_TEMP0;   /* TEMP0 */
+    s->input[2] = ADS_VBAT;    /* VBAT */
+    s->input[6] = ADS_VAUX;    /* VAUX */
+    s->input[7] = ADS_TEMP1;   /* TEMP1 */
+
+    /* We want absolute coordinates */
+    qemu_add_mouse_event_handler(ads7846_ts_event, s, 1,
+                    "QEMU ADS7846-driven Touchscreen");
+
+    ads7846_int_update(s);
+
+    vmstate_register(NULL, -1, &vmstate_ads7846, s);
+    return 0;
+}
+
+static void ads7846_class_init(ObjectClass *klass, void *data)
+{
+    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
+
+    k->init = ads7846_init;
+    k->transfer = ads7846_transfer;
+}
+
+static const TypeInfo ads7846_info = {
+    .name          = "ads7846",
+    .parent        = TYPE_SSI_SLAVE,
+    .instance_size = sizeof(ADS7846State),
+    .class_init    = ads7846_class_init,
+};
+
+static void ads7846_register_types(void)
+{
+    type_register_static(&ads7846_info);
+}
+
+type_init(ads7846_register_types)
diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
new file mode 100644 (file)
index 0000000..7a4d634
--- /dev/null
@@ -0,0 +1,3021 @@
+/*
+ * QEMU Cirrus CLGD 54xx VGA Emulator.
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ * Copyright (c) 2004 Makoto Suzuki (suzu)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/*
+ * Reference: Finn Thogersons' VGADOC4b
+ *   available at http://home.worldonline.dk/~finth/
+ */
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "ui/console.h"
+#include "hw/vga_int.h"
+#include "hw/loader.h"
+
+/*
+ * TODO:
+ *    - destination write mask support not complete (bits 5..7)
+ *    - optimize linear mappings
+ *    - optimize bitblt functions
+ */
+
+//#define DEBUG_CIRRUS
+//#define DEBUG_BITBLT
+
+/***************************************
+ *
+ *  definitions
+ *
+ ***************************************/
+
+// ID
+#define CIRRUS_ID_CLGD5422  (0x23<<2)
+#define CIRRUS_ID_CLGD5426  (0x24<<2)
+#define CIRRUS_ID_CLGD5424  (0x25<<2)
+#define CIRRUS_ID_CLGD5428  (0x26<<2)
+#define CIRRUS_ID_CLGD5430  (0x28<<2)
+#define CIRRUS_ID_CLGD5434  (0x2A<<2)
+#define CIRRUS_ID_CLGD5436  (0x2B<<2)
+#define CIRRUS_ID_CLGD5446  (0x2E<<2)
+
+// sequencer 0x07
+#define CIRRUS_SR7_BPP_VGA            0x00
+#define CIRRUS_SR7_BPP_SVGA           0x01
+#define CIRRUS_SR7_BPP_MASK           0x0e
+#define CIRRUS_SR7_BPP_8              0x00
+#define CIRRUS_SR7_BPP_16_DOUBLEVCLK  0x02
+#define CIRRUS_SR7_BPP_24             0x04
+#define CIRRUS_SR7_BPP_16             0x06
+#define CIRRUS_SR7_BPP_32             0x08
+#define CIRRUS_SR7_ISAADDR_MASK       0xe0
+
+// sequencer 0x0f
+#define CIRRUS_MEMSIZE_512k        0x08
+#define CIRRUS_MEMSIZE_1M          0x10
+#define CIRRUS_MEMSIZE_2M          0x18
+#define CIRRUS_MEMFLAGS_BANKSWITCH 0x80        // bank switching is enabled.
+
+// sequencer 0x12
+#define CIRRUS_CURSOR_SHOW         0x01
+#define CIRRUS_CURSOR_HIDDENPEL    0x02
+#define CIRRUS_CURSOR_LARGE        0x04        // 64x64 if set, 32x32 if clear
+
+// sequencer 0x17
+#define CIRRUS_BUSTYPE_VLBFAST   0x10
+#define CIRRUS_BUSTYPE_PCI       0x20
+#define CIRRUS_BUSTYPE_VLBSLOW   0x30
+#define CIRRUS_BUSTYPE_ISA       0x38
+#define CIRRUS_MMIO_ENABLE       0x04
+#define CIRRUS_MMIO_USE_PCIADDR  0x40  // 0xb8000 if cleared.
+#define CIRRUS_MEMSIZEEXT_DOUBLE 0x80
+
+// control 0x0b
+#define CIRRUS_BANKING_DUAL             0x01
+#define CIRRUS_BANKING_GRANULARITY_16K  0x20   // set:16k, clear:4k
+
+// control 0x30
+#define CIRRUS_BLTMODE_BACKWARDS        0x01
+#define CIRRUS_BLTMODE_MEMSYSDEST       0x02
+#define CIRRUS_BLTMODE_MEMSYSSRC        0x04
+#define CIRRUS_BLTMODE_TRANSPARENTCOMP  0x08
+#define CIRRUS_BLTMODE_PATTERNCOPY      0x40
+#define CIRRUS_BLTMODE_COLOREXPAND      0x80
+#define CIRRUS_BLTMODE_PIXELWIDTHMASK   0x30
+#define CIRRUS_BLTMODE_PIXELWIDTH8      0x00
+#define CIRRUS_BLTMODE_PIXELWIDTH16     0x10
+#define CIRRUS_BLTMODE_PIXELWIDTH24     0x20
+#define CIRRUS_BLTMODE_PIXELWIDTH32     0x30
+
+// control 0x31
+#define CIRRUS_BLT_BUSY                 0x01
+#define CIRRUS_BLT_START                0x02
+#define CIRRUS_BLT_RESET                0x04
+#define CIRRUS_BLT_FIFOUSED             0x10
+#define CIRRUS_BLT_AUTOSTART            0x80
+
+// control 0x32
+#define CIRRUS_ROP_0                    0x00
+#define CIRRUS_ROP_SRC_AND_DST          0x05
+#define CIRRUS_ROP_NOP                  0x06
+#define CIRRUS_ROP_SRC_AND_NOTDST       0x09
+#define CIRRUS_ROP_NOTDST               0x0b
+#define CIRRUS_ROP_SRC                  0x0d
+#define CIRRUS_ROP_1                    0x0e
+#define CIRRUS_ROP_NOTSRC_AND_DST       0x50
+#define CIRRUS_ROP_SRC_XOR_DST          0x59
+#define CIRRUS_ROP_SRC_OR_DST           0x6d
+#define CIRRUS_ROP_NOTSRC_OR_NOTDST     0x90
+#define CIRRUS_ROP_SRC_NOTXOR_DST       0x95
+#define CIRRUS_ROP_SRC_OR_NOTDST        0xad
+#define CIRRUS_ROP_NOTSRC               0xd0
+#define CIRRUS_ROP_NOTSRC_OR_DST        0xd6
+#define CIRRUS_ROP_NOTSRC_AND_NOTDST    0xda
+
+#define CIRRUS_ROP_NOP_INDEX 2
+#define CIRRUS_ROP_SRC_INDEX 5
+
+// control 0x33
+#define CIRRUS_BLTMODEEXT_SOLIDFILL        0x04
+#define CIRRUS_BLTMODEEXT_COLOREXPINV      0x02
+#define CIRRUS_BLTMODEEXT_DWORDGRANULARITY 0x01
+
+// memory-mapped IO
+#define CIRRUS_MMIO_BLTBGCOLOR        0x00     // dword
+#define CIRRUS_MMIO_BLTFGCOLOR        0x04     // dword
+#define CIRRUS_MMIO_BLTWIDTH          0x08     // word
+#define CIRRUS_MMIO_BLTHEIGHT         0x0a     // word
+#define CIRRUS_MMIO_BLTDESTPITCH      0x0c     // word
+#define CIRRUS_MMIO_BLTSRCPITCH       0x0e     // word
+#define CIRRUS_MMIO_BLTDESTADDR       0x10     // dword
+#define CIRRUS_MMIO_BLTSRCADDR        0x14     // dword
+#define CIRRUS_MMIO_BLTWRITEMASK      0x17     // byte
+#define CIRRUS_MMIO_BLTMODE           0x18     // byte
+#define CIRRUS_MMIO_BLTROP            0x1a     // byte
+#define CIRRUS_MMIO_BLTMODEEXT        0x1b     // byte
+#define CIRRUS_MMIO_BLTTRANSPARENTCOLOR 0x1c   // word?
+#define CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK 0x20       // word?
+#define CIRRUS_MMIO_LINEARDRAW_START_X 0x24    // word
+#define CIRRUS_MMIO_LINEARDRAW_START_Y 0x26    // word
+#define CIRRUS_MMIO_LINEARDRAW_END_X  0x28     // word
+#define CIRRUS_MMIO_LINEARDRAW_END_Y  0x2a     // word
+#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_INC 0x2c      // byte
+#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ROLLOVER 0x2d // byte
+#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_MASK 0x2e     // byte
+#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ACCUM 0x2f    // byte
+#define CIRRUS_MMIO_BRESENHAM_K1      0x30     // word
+#define CIRRUS_MMIO_BRESENHAM_K3      0x32     // word
+#define CIRRUS_MMIO_BRESENHAM_ERROR   0x34     // word
+#define CIRRUS_MMIO_BRESENHAM_DELTA_MAJOR 0x36 // word
+#define CIRRUS_MMIO_BRESENHAM_DIRECTION 0x38   // byte
+#define CIRRUS_MMIO_LINEDRAW_MODE     0x39     // byte
+#define CIRRUS_MMIO_BLTSTATUS         0x40     // byte
+
+#define CIRRUS_PNPMMIO_SIZE         0x1000
+
+#define BLTUNSAFE(s) \
+    ( \
+        ( /* check dst is within bounds */ \
+            (s)->cirrus_blt_height * ABS((s)->cirrus_blt_dstpitch) \
+                + ((s)->cirrus_blt_dstaddr & (s)->cirrus_addr_mask) > \
+                    (s)->vga.vram_size \
+        ) || \
+        ( /* check src is within bounds */ \
+            (s)->cirrus_blt_height * ABS((s)->cirrus_blt_srcpitch) \
+                + ((s)->cirrus_blt_srcaddr & (s)->cirrus_addr_mask) > \
+                    (s)->vga.vram_size \
+        ) \
+    )
+
+struct CirrusVGAState;
+typedef void (*cirrus_bitblt_rop_t) (struct CirrusVGAState *s,
+                                     uint8_t * dst, const uint8_t * src,
+                                    int dstpitch, int srcpitch,
+                                    int bltwidth, int bltheight);
+typedef void (*cirrus_fill_t)(struct CirrusVGAState *s,
+                              uint8_t *dst, int dst_pitch, int width, int height);
+
+typedef struct CirrusVGAState {
+    VGACommonState vga;
+
+    MemoryRegion cirrus_vga_io;
+    MemoryRegion cirrus_linear_io;
+    MemoryRegion cirrus_linear_bitblt_io;
+    MemoryRegion cirrus_mmio_io;
+    MemoryRegion pci_bar;
+    bool linear_vram;  /* vga.vram mapped over cirrus_linear_io */
+    MemoryRegion low_mem_container; /* container for 0xa0000-0xc0000 */
+    MemoryRegion low_mem;           /* always mapped, overridden by: */
+    MemoryRegion cirrus_bank[2];    /*   aliases at 0xa0000-0xb0000  */
+    uint32_t cirrus_addr_mask;
+    uint32_t linear_mmio_mask;
+    uint8_t cirrus_shadow_gr0;
+    uint8_t cirrus_shadow_gr1;
+    uint8_t cirrus_hidden_dac_lockindex;
+    uint8_t cirrus_hidden_dac_data;
+    uint32_t cirrus_bank_base[2];
+    uint32_t cirrus_bank_limit[2];
+    uint8_t cirrus_hidden_palette[48];
+    uint32_t hw_cursor_x;
+    uint32_t hw_cursor_y;
+    int cirrus_blt_pixelwidth;
+    int cirrus_blt_width;
+    int cirrus_blt_height;
+    int cirrus_blt_dstpitch;
+    int cirrus_blt_srcpitch;
+    uint32_t cirrus_blt_fgcol;
+    uint32_t cirrus_blt_bgcol;
+    uint32_t cirrus_blt_dstaddr;
+    uint32_t cirrus_blt_srcaddr;
+    uint8_t cirrus_blt_mode;
+    uint8_t cirrus_blt_modeext;
+    cirrus_bitblt_rop_t cirrus_rop;
+#define CIRRUS_BLTBUFSIZE (2048 * 4) /* one line width */
+    uint8_t cirrus_bltbuf[CIRRUS_BLTBUFSIZE];
+    uint8_t *cirrus_srcptr;
+    uint8_t *cirrus_srcptr_end;
+    uint32_t cirrus_srccounter;
+    /* hwcursor display state */
+    int last_hw_cursor_size;
+    int last_hw_cursor_x;
+    int last_hw_cursor_y;
+    int last_hw_cursor_y_start;
+    int last_hw_cursor_y_end;
+    int real_vram_size; /* XXX: suppress that */
+    int device_id;
+    int bustype;
+} CirrusVGAState;
+
+typedef struct PCICirrusVGAState {
+    PCIDevice dev;
+    CirrusVGAState cirrus_vga;
+} PCICirrusVGAState;
+
+typedef struct ISACirrusVGAState {
+    ISADevice dev;
+    CirrusVGAState cirrus_vga;
+} ISACirrusVGAState;
+
+static uint8_t rop_to_index[256];
+
+/***************************************
+ *
+ *  prototypes.
+ *
+ ***************************************/
+
+
+static void cirrus_bitblt_reset(CirrusVGAState *s);
+static void cirrus_update_memory_access(CirrusVGAState *s);
+
+/***************************************
+ *
+ *  raster operations
+ *
+ ***************************************/
+
+static void cirrus_bitblt_rop_nop(CirrusVGAState *s,
+                                  uint8_t *dst,const uint8_t *src,
+                                  int dstpitch,int srcpitch,
+                                  int bltwidth,int bltheight)
+{
+}
+
+static void cirrus_bitblt_fill_nop(CirrusVGAState *s,
+                                   uint8_t *dst,
+                                   int dstpitch, int bltwidth,int bltheight)
+{
+}
+
+#define ROP_NAME 0
+#define ROP_FN(d, s) 0
+#include "hw/cirrus_vga_rop.h"
+
+#define ROP_NAME src_and_dst
+#define ROP_FN(d, s) (s) & (d)
+#include "hw/cirrus_vga_rop.h"
+
+#define ROP_NAME src_and_notdst
+#define ROP_FN(d, s) (s) & (~(d))
+#include "hw/cirrus_vga_rop.h"
+
+#define ROP_NAME notdst
+#define ROP_FN(d, s) ~(d)
+#include "hw/cirrus_vga_rop.h"
+
+#define ROP_NAME src
+#define ROP_FN(d, s) s
+#include "hw/cirrus_vga_rop.h"
+
+#define ROP_NAME 1
+#define ROP_FN(d, s) ~0
+#include "hw/cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc_and_dst
+#define ROP_FN(d, s) (~(s)) & (d)
+#include "hw/cirrus_vga_rop.h"
+
+#define ROP_NAME src_xor_dst
+#define ROP_FN(d, s) (s) ^ (d)
+#include "hw/cirrus_vga_rop.h"
+
+#define ROP_NAME src_or_dst
+#define ROP_FN(d, s) (s) | (d)
+#include "hw/cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc_or_notdst
+#define ROP_FN(d, s) (~(s)) | (~(d))
+#include "hw/cirrus_vga_rop.h"
+
+#define ROP_NAME src_notxor_dst
+#define ROP_FN(d, s) ~((s) ^ (d))
+#include "hw/cirrus_vga_rop.h"
+
+#define ROP_NAME src_or_notdst
+#define ROP_FN(d, s) (s) | (~(d))
+#include "hw/cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc
+#define ROP_FN(d, s) (~(s))
+#include "hw/cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc_or_dst
+#define ROP_FN(d, s) (~(s)) | (d)
+#include "hw/cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc_and_notdst
+#define ROP_FN(d, s) (~(s)) & (~(d))
+#include "hw/cirrus_vga_rop.h"
+
+static const cirrus_bitblt_rop_t cirrus_fwd_rop[16] = {
+    cirrus_bitblt_rop_fwd_0,
+    cirrus_bitblt_rop_fwd_src_and_dst,
+    cirrus_bitblt_rop_nop,
+    cirrus_bitblt_rop_fwd_src_and_notdst,
+    cirrus_bitblt_rop_fwd_notdst,
+    cirrus_bitblt_rop_fwd_src,
+    cirrus_bitblt_rop_fwd_1,
+    cirrus_bitblt_rop_fwd_notsrc_and_dst,
+    cirrus_bitblt_rop_fwd_src_xor_dst,
+    cirrus_bitblt_rop_fwd_src_or_dst,
+    cirrus_bitblt_rop_fwd_notsrc_or_notdst,
+    cirrus_bitblt_rop_fwd_src_notxor_dst,
+    cirrus_bitblt_rop_fwd_src_or_notdst,
+    cirrus_bitblt_rop_fwd_notsrc,
+    cirrus_bitblt_rop_fwd_notsrc_or_dst,
+    cirrus_bitblt_rop_fwd_notsrc_and_notdst,
+};
+
+static const cirrus_bitblt_rop_t cirrus_bkwd_rop[16] = {
+    cirrus_bitblt_rop_bkwd_0,
+    cirrus_bitblt_rop_bkwd_src_and_dst,
+    cirrus_bitblt_rop_nop,
+    cirrus_bitblt_rop_bkwd_src_and_notdst,
+    cirrus_bitblt_rop_bkwd_notdst,
+    cirrus_bitblt_rop_bkwd_src,
+    cirrus_bitblt_rop_bkwd_1,
+    cirrus_bitblt_rop_bkwd_notsrc_and_dst,
+    cirrus_bitblt_rop_bkwd_src_xor_dst,
+    cirrus_bitblt_rop_bkwd_src_or_dst,
+    cirrus_bitblt_rop_bkwd_notsrc_or_notdst,
+    cirrus_bitblt_rop_bkwd_src_notxor_dst,
+    cirrus_bitblt_rop_bkwd_src_or_notdst,
+    cirrus_bitblt_rop_bkwd_notsrc,
+    cirrus_bitblt_rop_bkwd_notsrc_or_dst,
+    cirrus_bitblt_rop_bkwd_notsrc_and_notdst,
+};
+
+#define TRANSP_ROP(name) {\
+    name ## _8,\
+    name ## _16,\
+        }
+#define TRANSP_NOP(func) {\
+    func,\
+    func,\
+        }
+
+static const cirrus_bitblt_rop_t cirrus_fwd_transp_rop[16][2] = {
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_0),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_and_dst),
+    TRANSP_NOP(cirrus_bitblt_rop_nop),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_and_notdst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notdst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_1),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_and_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_xor_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_or_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_or_notdst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_notxor_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_or_notdst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_or_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_bkwd_transp_rop[16][2] = {
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_0),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_and_dst),
+    TRANSP_NOP(cirrus_bitblt_rop_nop),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_and_notdst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notdst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_1),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_and_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_xor_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_or_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_or_notdst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_notxor_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_or_notdst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_or_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_and_notdst),
+};
+
+#define ROP2(name) {\
+    name ## _8,\
+    name ## _16,\
+    name ## _24,\
+    name ## _32,\
+        }
+
+#define ROP_NOP2(func) {\
+    func,\
+    func,\
+    func,\
+    func,\
+        }
+
+static const cirrus_bitblt_rop_t cirrus_patternfill[16][4] = {
+    ROP2(cirrus_patternfill_0),
+    ROP2(cirrus_patternfill_src_and_dst),
+    ROP_NOP2(cirrus_bitblt_rop_nop),
+    ROP2(cirrus_patternfill_src_and_notdst),
+    ROP2(cirrus_patternfill_notdst),
+    ROP2(cirrus_patternfill_src),
+    ROP2(cirrus_patternfill_1),
+    ROP2(cirrus_patternfill_notsrc_and_dst),
+    ROP2(cirrus_patternfill_src_xor_dst),
+    ROP2(cirrus_patternfill_src_or_dst),
+    ROP2(cirrus_patternfill_notsrc_or_notdst),
+    ROP2(cirrus_patternfill_src_notxor_dst),
+    ROP2(cirrus_patternfill_src_or_notdst),
+    ROP2(cirrus_patternfill_notsrc),
+    ROP2(cirrus_patternfill_notsrc_or_dst),
+    ROP2(cirrus_patternfill_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_colorexpand_transp[16][4] = {
+    ROP2(cirrus_colorexpand_transp_0),
+    ROP2(cirrus_colorexpand_transp_src_and_dst),
+    ROP_NOP2(cirrus_bitblt_rop_nop),
+    ROP2(cirrus_colorexpand_transp_src_and_notdst),
+    ROP2(cirrus_colorexpand_transp_notdst),
+    ROP2(cirrus_colorexpand_transp_src),
+    ROP2(cirrus_colorexpand_transp_1),
+    ROP2(cirrus_colorexpand_transp_notsrc_and_dst),
+    ROP2(cirrus_colorexpand_transp_src_xor_dst),
+    ROP2(cirrus_colorexpand_transp_src_or_dst),
+    ROP2(cirrus_colorexpand_transp_notsrc_or_notdst),
+    ROP2(cirrus_colorexpand_transp_src_notxor_dst),
+    ROP2(cirrus_colorexpand_transp_src_or_notdst),
+    ROP2(cirrus_colorexpand_transp_notsrc),
+    ROP2(cirrus_colorexpand_transp_notsrc_or_dst),
+    ROP2(cirrus_colorexpand_transp_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_colorexpand[16][4] = {
+    ROP2(cirrus_colorexpand_0),
+    ROP2(cirrus_colorexpand_src_and_dst),
+    ROP_NOP2(cirrus_bitblt_rop_nop),
+    ROP2(cirrus_colorexpand_src_and_notdst),
+    ROP2(cirrus_colorexpand_notdst),
+    ROP2(cirrus_colorexpand_src),
+    ROP2(cirrus_colorexpand_1),
+    ROP2(cirrus_colorexpand_notsrc_and_dst),
+    ROP2(cirrus_colorexpand_src_xor_dst),
+    ROP2(cirrus_colorexpand_src_or_dst),
+    ROP2(cirrus_colorexpand_notsrc_or_notdst),
+    ROP2(cirrus_colorexpand_src_notxor_dst),
+    ROP2(cirrus_colorexpand_src_or_notdst),
+    ROP2(cirrus_colorexpand_notsrc),
+    ROP2(cirrus_colorexpand_notsrc_or_dst),
+    ROP2(cirrus_colorexpand_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern_transp[16][4] = {
+    ROP2(cirrus_colorexpand_pattern_transp_0),
+    ROP2(cirrus_colorexpand_pattern_transp_src_and_dst),
+    ROP_NOP2(cirrus_bitblt_rop_nop),
+    ROP2(cirrus_colorexpand_pattern_transp_src_and_notdst),
+    ROP2(cirrus_colorexpand_pattern_transp_notdst),
+    ROP2(cirrus_colorexpand_pattern_transp_src),
+    ROP2(cirrus_colorexpand_pattern_transp_1),
+    ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_dst),
+    ROP2(cirrus_colorexpand_pattern_transp_src_xor_dst),
+    ROP2(cirrus_colorexpand_pattern_transp_src_or_dst),
+    ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_notdst),
+    ROP2(cirrus_colorexpand_pattern_transp_src_notxor_dst),
+    ROP2(cirrus_colorexpand_pattern_transp_src_or_notdst),
+    ROP2(cirrus_colorexpand_pattern_transp_notsrc),
+    ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_dst),
+    ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern[16][4] = {
+    ROP2(cirrus_colorexpand_pattern_0),
+    ROP2(cirrus_colorexpand_pattern_src_and_dst),
+    ROP_NOP2(cirrus_bitblt_rop_nop),
+    ROP2(cirrus_colorexpand_pattern_src_and_notdst),
+    ROP2(cirrus_colorexpand_pattern_notdst),
+    ROP2(cirrus_colorexpand_pattern_src),
+    ROP2(cirrus_colorexpand_pattern_1),
+    ROP2(cirrus_colorexpand_pattern_notsrc_and_dst),
+    ROP2(cirrus_colorexpand_pattern_src_xor_dst),
+    ROP2(cirrus_colorexpand_pattern_src_or_dst),
+    ROP2(cirrus_colorexpand_pattern_notsrc_or_notdst),
+    ROP2(cirrus_colorexpand_pattern_src_notxor_dst),
+    ROP2(cirrus_colorexpand_pattern_src_or_notdst),
+    ROP2(cirrus_colorexpand_pattern_notsrc),
+    ROP2(cirrus_colorexpand_pattern_notsrc_or_dst),
+    ROP2(cirrus_colorexpand_pattern_notsrc_and_notdst),
+};
+
+static const cirrus_fill_t cirrus_fill[16][4] = {
+    ROP2(cirrus_fill_0),
+    ROP2(cirrus_fill_src_and_dst),
+    ROP_NOP2(cirrus_bitblt_fill_nop),
+    ROP2(cirrus_fill_src_and_notdst),
+    ROP2(cirrus_fill_notdst),
+    ROP2(cirrus_fill_src),
+    ROP2(cirrus_fill_1),
+    ROP2(cirrus_fill_notsrc_and_dst),
+    ROP2(cirrus_fill_src_xor_dst),
+    ROP2(cirrus_fill_src_or_dst),
+    ROP2(cirrus_fill_notsrc_or_notdst),
+    ROP2(cirrus_fill_src_notxor_dst),
+    ROP2(cirrus_fill_src_or_notdst),
+    ROP2(cirrus_fill_notsrc),
+    ROP2(cirrus_fill_notsrc_or_dst),
+    ROP2(cirrus_fill_notsrc_and_notdst),
+};
+
+static inline void cirrus_bitblt_fgcol(CirrusVGAState *s)
+{
+    unsigned int color;
+    switch (s->cirrus_blt_pixelwidth) {
+    case 1:
+        s->cirrus_blt_fgcol = s->cirrus_shadow_gr1;
+        break;
+    case 2:
+        color = s->cirrus_shadow_gr1 | (s->vga.gr[0x11] << 8);
+        s->cirrus_blt_fgcol = le16_to_cpu(color);
+        break;
+    case 3:
+        s->cirrus_blt_fgcol = s->cirrus_shadow_gr1 |
+            (s->vga.gr[0x11] << 8) | (s->vga.gr[0x13] << 16);
+        break;
+    default:
+    case 4:
+        color = s->cirrus_shadow_gr1 | (s->vga.gr[0x11] << 8) |
+            (s->vga.gr[0x13] << 16) | (s->vga.gr[0x15] << 24);
+        s->cirrus_blt_fgcol = le32_to_cpu(color);
+        break;
+    }
+}
+
+static inline void cirrus_bitblt_bgcol(CirrusVGAState *s)
+{
+    unsigned int color;
+    switch (s->cirrus_blt_pixelwidth) {
+    case 1:
+        s->cirrus_blt_bgcol = s->cirrus_shadow_gr0;
+        break;
+    case 2:
+        color = s->cirrus_shadow_gr0 | (s->vga.gr[0x10] << 8);
+        s->cirrus_blt_bgcol = le16_to_cpu(color);
+        break;
+    case 3:
+        s->cirrus_blt_bgcol = s->cirrus_shadow_gr0 |
+            (s->vga.gr[0x10] << 8) | (s->vga.gr[0x12] << 16);
+        break;
+    default:
+    case 4:
+        color = s->cirrus_shadow_gr0 | (s->vga.gr[0x10] << 8) |
+            (s->vga.gr[0x12] << 16) | (s->vga.gr[0x14] << 24);
+        s->cirrus_blt_bgcol = le32_to_cpu(color);
+        break;
+    }
+}
+
+static void cirrus_invalidate_region(CirrusVGAState * s, int off_begin,
+                                    int off_pitch, int bytesperline,
+                                    int lines)
+{
+    int y;
+    int off_cur;
+    int off_cur_end;
+
+    for (y = 0; y < lines; y++) {
+       off_cur = off_begin;
+       off_cur_end = (off_cur + bytesperline) & s->cirrus_addr_mask;
+        memory_region_set_dirty(&s->vga.vram, off_cur, off_cur_end - off_cur);
+       off_begin += off_pitch;
+    }
+}
+
+static int cirrus_bitblt_common_patterncopy(CirrusVGAState * s,
+                                           const uint8_t * src)
+{
+    uint8_t *dst;
+
+    dst = s->vga.vram_ptr + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask);
+
+    if (BLTUNSAFE(s))
+        return 0;
+
+    (*s->cirrus_rop) (s, dst, src,
+                      s->cirrus_blt_dstpitch, 0,
+                      s->cirrus_blt_width, s->cirrus_blt_height);
+    cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
+                             s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+                             s->cirrus_blt_height);
+    return 1;
+}
+
+/* fill */
+
+static int cirrus_bitblt_solidfill(CirrusVGAState *s, int blt_rop)
+{
+    cirrus_fill_t rop_func;
+
+    if (BLTUNSAFE(s))
+        return 0;
+    rop_func = cirrus_fill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+    rop_func(s, s->vga.vram_ptr + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask),
+             s->cirrus_blt_dstpitch,
+             s->cirrus_blt_width, s->cirrus_blt_height);
+    cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
+                            s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+                            s->cirrus_blt_height);
+    cirrus_bitblt_reset(s);
+    return 1;
+}
+
+/***************************************
+ *
+ *  bitblt (video-to-video)
+ *
+ ***************************************/
+
+static int cirrus_bitblt_videotovideo_patterncopy(CirrusVGAState * s)
+{
+    return cirrus_bitblt_common_patterncopy(s,
+                                           s->vga.vram_ptr + ((s->cirrus_blt_srcaddr & ~7) &
+                                            s->cirrus_addr_mask));
+}
+
+static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h)
+{
+    int sx = 0, sy = 0;
+    int dx = 0, dy = 0;
+    int depth = 0;
+    int notify = 0;
+
+    /* make sure to only copy if it's a plain copy ROP */
+    if (*s->cirrus_rop == cirrus_bitblt_rop_fwd_src ||
+        *s->cirrus_rop == cirrus_bitblt_rop_bkwd_src) {
+
+        int width, height;
+
+        depth = s->vga.get_bpp(&s->vga) / 8;
+        s->vga.get_resolution(&s->vga, &width, &height);
+
+        /* extra x, y */
+        sx = (src % ABS(s->cirrus_blt_srcpitch)) / depth;
+        sy = (src / ABS(s->cirrus_blt_srcpitch));
+        dx = (dst % ABS(s->cirrus_blt_dstpitch)) / depth;
+        dy = (dst / ABS(s->cirrus_blt_dstpitch));
+
+        /* normalize width */
+        w /= depth;
+
+        /* if we're doing a backward copy, we have to adjust
+           our x/y to be the upper left corner (instead of the lower
+           right corner) */
+        if (s->cirrus_blt_dstpitch < 0) {
+            sx -= (s->cirrus_blt_width / depth) - 1;
+            dx -= (s->cirrus_blt_width / depth) - 1;
+            sy -= s->cirrus_blt_height - 1;
+            dy -= s->cirrus_blt_height - 1;
+        }
+
+        /* are we in the visible portion of memory? */
+        if (sx >= 0 && sy >= 0 && dx >= 0 && dy >= 0 &&
+            (sx + w) <= width && (sy + h) <= height &&
+            (dx + w) <= width && (dy + h) <= height) {
+            notify = 1;
+        }
+    }
+
+    /* we have to flush all pending changes so that the copy
+       is generated at the appropriate moment in time */
+    if (notify)
+       vga_hw_update();
+
+    (*s->cirrus_rop) (s, s->vga.vram_ptr +
+                     (s->cirrus_blt_dstaddr & s->cirrus_addr_mask),
+                     s->vga.vram_ptr +
+                     (s->cirrus_blt_srcaddr & s->cirrus_addr_mask),
+                     s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch,
+                     s->cirrus_blt_width, s->cirrus_blt_height);
+
+    if (notify) {
+        qemu_console_copy(s->vga.con,
+                         sx, sy, dx, dy,
+                         s->cirrus_blt_width / depth,
+                         s->cirrus_blt_height);
+    }
+
+    /* we don't have to notify the display that this portion has
+       changed since qemu_console_copy implies this */
+
+    cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
+                               s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+                               s->cirrus_blt_height);
+}
+
+static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s)
+{
+    if (BLTUNSAFE(s))
+        return 0;
+
+    cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->vga.start_addr,
+            s->cirrus_blt_srcaddr - s->vga.start_addr,
+            s->cirrus_blt_width, s->cirrus_blt_height);
+
+    return 1;
+}
+
+/***************************************
+ *
+ *  bitblt (cpu-to-video)
+ *
+ ***************************************/
+
+static void cirrus_bitblt_cputovideo_next(CirrusVGAState * s)
+{
+    int copy_count;
+    uint8_t *end_ptr;
+
+    if (s->cirrus_srccounter > 0) {
+        if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
+            cirrus_bitblt_common_patterncopy(s, s->cirrus_bltbuf);
+        the_end:
+            s->cirrus_srccounter = 0;
+            cirrus_bitblt_reset(s);
+        } else {
+            /* at least one scan line */
+            do {
+                (*s->cirrus_rop)(s, s->vga.vram_ptr +
+                                 (s->cirrus_blt_dstaddr & s->cirrus_addr_mask),
+                                  s->cirrus_bltbuf, 0, 0, s->cirrus_blt_width, 1);
+                cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, 0,
+                                         s->cirrus_blt_width, 1);
+                s->cirrus_blt_dstaddr += s->cirrus_blt_dstpitch;
+                s->cirrus_srccounter -= s->cirrus_blt_srcpitch;
+                if (s->cirrus_srccounter <= 0)
+                    goto the_end;
+                /* more bytes than needed can be transferred because of
+                   word alignment, so we keep them for the next line */
+                /* XXX: keep alignment to speed up transfer */
+                end_ptr = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
+                copy_count = s->cirrus_srcptr_end - end_ptr;
+                memmove(s->cirrus_bltbuf, end_ptr, copy_count);
+                s->cirrus_srcptr = s->cirrus_bltbuf + copy_count;
+                s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
+            } while (s->cirrus_srcptr >= s->cirrus_srcptr_end);
+        }
+    }
+}
+
+/***************************************
+ *
+ *  bitblt wrapper
+ *
+ ***************************************/
+
+static void cirrus_bitblt_reset(CirrusVGAState * s)
+{
+    int need_update;
+
+    s->vga.gr[0x31] &=
+       ~(CIRRUS_BLT_START | CIRRUS_BLT_BUSY | CIRRUS_BLT_FIFOUSED);
+    need_update = s->cirrus_srcptr != &s->cirrus_bltbuf[0]
+        || s->cirrus_srcptr_end != &s->cirrus_bltbuf[0];
+    s->cirrus_srcptr = &s->cirrus_bltbuf[0];
+    s->cirrus_srcptr_end = &s->cirrus_bltbuf[0];
+    s->cirrus_srccounter = 0;
+    if (!need_update)
+        return;
+    cirrus_update_memory_access(s);
+}
+
+static int cirrus_bitblt_cputovideo(CirrusVGAState * s)
+{
+    int w;
+
+    s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_MEMSYSSRC;
+    s->cirrus_srcptr = &s->cirrus_bltbuf[0];
+    s->cirrus_srcptr_end = &s->cirrus_bltbuf[0];
+
+    if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
+       if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
+           s->cirrus_blt_srcpitch = 8;
+       } else {
+            /* XXX: check for 24 bpp */
+           s->cirrus_blt_srcpitch = 8 * 8 * s->cirrus_blt_pixelwidth;
+       }
+       s->cirrus_srccounter = s->cirrus_blt_srcpitch;
+    } else {
+       if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
+            w = s->cirrus_blt_width / s->cirrus_blt_pixelwidth;
+            if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_DWORDGRANULARITY)
+                s->cirrus_blt_srcpitch = ((w + 31) >> 5);
+            else
+                s->cirrus_blt_srcpitch = ((w + 7) >> 3);
+       } else {
+            /* always align input size to 32 bits */
+           s->cirrus_blt_srcpitch = (s->cirrus_blt_width + 3) & ~3;
+       }
+        s->cirrus_srccounter = s->cirrus_blt_srcpitch * s->cirrus_blt_height;
+    }
+    s->cirrus_srcptr = s->cirrus_bltbuf;
+    s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
+    cirrus_update_memory_access(s);
+    return 1;
+}
+
+static int cirrus_bitblt_videotocpu(CirrusVGAState * s)
+{
+    /* XXX */
+#ifdef DEBUG_BITBLT
+    printf("cirrus: bitblt (video to cpu) is not implemented yet\n");
+#endif
+    return 0;
+}
+
+static int cirrus_bitblt_videotovideo(CirrusVGAState * s)
+{
+    int ret;
+
+    if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
+       ret = cirrus_bitblt_videotovideo_patterncopy(s);
+    } else {
+       ret = cirrus_bitblt_videotovideo_copy(s);
+    }
+    if (ret)
+       cirrus_bitblt_reset(s);
+    return ret;
+}
+
+static void cirrus_bitblt_start(CirrusVGAState * s)
+{
+    uint8_t blt_rop;
+
+    s->vga.gr[0x31] |= CIRRUS_BLT_BUSY;
+
+    s->cirrus_blt_width = (s->vga.gr[0x20] | (s->vga.gr[0x21] << 8)) + 1;
+    s->cirrus_blt_height = (s->vga.gr[0x22] | (s->vga.gr[0x23] << 8)) + 1;
+    s->cirrus_blt_dstpitch = (s->vga.gr[0x24] | (s->vga.gr[0x25] << 8));
+    s->cirrus_blt_srcpitch = (s->vga.gr[0x26] | (s->vga.gr[0x27] << 8));
+    s->cirrus_blt_dstaddr =
+       (s->vga.gr[0x28] | (s->vga.gr[0x29] << 8) | (s->vga.gr[0x2a] << 16));
+    s->cirrus_blt_srcaddr =
+       (s->vga.gr[0x2c] | (s->vga.gr[0x2d] << 8) | (s->vga.gr[0x2e] << 16));
+    s->cirrus_blt_mode = s->vga.gr[0x30];
+    s->cirrus_blt_modeext = s->vga.gr[0x33];
+    blt_rop = s->vga.gr[0x32];
+
+#ifdef DEBUG_BITBLT
+    printf("rop=0x%02x mode=0x%02x modeext=0x%02x w=%d h=%d dpitch=%d spitch=%d daddr=0x%08x saddr=0x%08x writemask=0x%02x\n",
+           blt_rop,
+           s->cirrus_blt_mode,
+           s->cirrus_blt_modeext,
+           s->cirrus_blt_width,
+           s->cirrus_blt_height,
+           s->cirrus_blt_dstpitch,
+           s->cirrus_blt_srcpitch,
+           s->cirrus_blt_dstaddr,
+           s->cirrus_blt_srcaddr,
+           s->vga.gr[0x2f]);
+#endif
+
+    switch (s->cirrus_blt_mode & CIRRUS_BLTMODE_PIXELWIDTHMASK) {
+    case CIRRUS_BLTMODE_PIXELWIDTH8:
+       s->cirrus_blt_pixelwidth = 1;
+       break;
+    case CIRRUS_BLTMODE_PIXELWIDTH16:
+       s->cirrus_blt_pixelwidth = 2;
+       break;
+    case CIRRUS_BLTMODE_PIXELWIDTH24:
+       s->cirrus_blt_pixelwidth = 3;
+       break;
+    case CIRRUS_BLTMODE_PIXELWIDTH32:
+       s->cirrus_blt_pixelwidth = 4;
+       break;
+    default:
+#ifdef DEBUG_BITBLT
+       printf("cirrus: bitblt - pixel width is unknown\n");
+#endif
+       goto bitblt_ignore;
+    }
+    s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_PIXELWIDTHMASK;
+
+    if ((s->
+        cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSSRC |
+                           CIRRUS_BLTMODE_MEMSYSDEST))
+       == (CIRRUS_BLTMODE_MEMSYSSRC | CIRRUS_BLTMODE_MEMSYSDEST)) {
+#ifdef DEBUG_BITBLT
+       printf("cirrus: bitblt - memory-to-memory copy is requested\n");
+#endif
+       goto bitblt_ignore;
+    }
+
+    if ((s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_SOLIDFILL) &&
+        (s->cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSDEST |
+                               CIRRUS_BLTMODE_TRANSPARENTCOMP |
+                               CIRRUS_BLTMODE_PATTERNCOPY |
+                               CIRRUS_BLTMODE_COLOREXPAND)) ==
+         (CIRRUS_BLTMODE_PATTERNCOPY | CIRRUS_BLTMODE_COLOREXPAND)) {
+        cirrus_bitblt_fgcol(s);
+        cirrus_bitblt_solidfill(s, blt_rop);
+    } else {
+        if ((s->cirrus_blt_mode & (CIRRUS_BLTMODE_COLOREXPAND |
+                                   CIRRUS_BLTMODE_PATTERNCOPY)) ==
+            CIRRUS_BLTMODE_COLOREXPAND) {
+
+            if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) {
+                if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV)
+                    cirrus_bitblt_bgcol(s);
+                else
+                    cirrus_bitblt_fgcol(s);
+                s->cirrus_rop = cirrus_colorexpand_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+            } else {
+                cirrus_bitblt_fgcol(s);
+                cirrus_bitblt_bgcol(s);
+                s->cirrus_rop = cirrus_colorexpand[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+            }
+        } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
+            if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
+                if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) {
+                    if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV)
+                        cirrus_bitblt_bgcol(s);
+                    else
+                        cirrus_bitblt_fgcol(s);
+                    s->cirrus_rop = cirrus_colorexpand_pattern_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+                } else {
+                    cirrus_bitblt_fgcol(s);
+                    cirrus_bitblt_bgcol(s);
+                    s->cirrus_rop = cirrus_colorexpand_pattern[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+                }
+            } else {
+                s->cirrus_rop = cirrus_patternfill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+            }
+        } else {
+           if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) {
+               if (s->cirrus_blt_pixelwidth > 2) {
+                   printf("src transparent without colorexpand must be 8bpp or 16bpp\n");
+                   goto bitblt_ignore;
+               }
+               if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) {
+                   s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch;
+                   s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch;
+                   s->cirrus_rop = cirrus_bkwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+               } else {
+                   s->cirrus_rop = cirrus_fwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+               }
+           } else {
+               if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) {
+                   s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch;
+                   s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch;
+                   s->cirrus_rop = cirrus_bkwd_rop[rop_to_index[blt_rop]];
+               } else {
+                   s->cirrus_rop = cirrus_fwd_rop[rop_to_index[blt_rop]];
+               }
+           }
+       }
+        // setup bitblt engine.
+        if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSSRC) {
+            if (!cirrus_bitblt_cputovideo(s))
+                goto bitblt_ignore;
+        } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSDEST) {
+            if (!cirrus_bitblt_videotocpu(s))
+                goto bitblt_ignore;
+        } else {
+            if (!cirrus_bitblt_videotovideo(s))
+                goto bitblt_ignore;
+        }
+    }
+    return;
+  bitblt_ignore:;
+    cirrus_bitblt_reset(s);
+}
+
+static void cirrus_write_bitblt(CirrusVGAState * s, unsigned reg_value)
+{
+    unsigned old_value;
+
+    old_value = s->vga.gr[0x31];
+    s->vga.gr[0x31] = reg_value;
+
+    if (((old_value & CIRRUS_BLT_RESET) != 0) &&
+       ((reg_value & CIRRUS_BLT_RESET) == 0)) {
+       cirrus_bitblt_reset(s);
+    } else if (((old_value & CIRRUS_BLT_START) == 0) &&
+              ((reg_value & CIRRUS_BLT_START) != 0)) {
+       cirrus_bitblt_start(s);
+    }
+}
+
+
+/***************************************
+ *
+ *  basic parameters
+ *
+ ***************************************/
+
+static void cirrus_get_offsets(VGACommonState *s1,
+                               uint32_t *pline_offset,
+                               uint32_t *pstart_addr,
+                               uint32_t *pline_compare)
+{
+    CirrusVGAState * s = container_of(s1, CirrusVGAState, vga);
+    uint32_t start_addr, line_offset, line_compare;
+
+    line_offset = s->vga.cr[0x13]
+       | ((s->vga.cr[0x1b] & 0x10) << 4);
+    line_offset <<= 3;
+    *pline_offset = line_offset;
+
+    start_addr = (s->vga.cr[0x0c] << 8)
+       | s->vga.cr[0x0d]
+       | ((s->vga.cr[0x1b] & 0x01) << 16)
+       | ((s->vga.cr[0x1b] & 0x0c) << 15)
+       | ((s->vga.cr[0x1d] & 0x80) << 12);
+    *pstart_addr = start_addr;
+
+    line_compare = s->vga.cr[0x18] |
+        ((s->vga.cr[0x07] & 0x10) << 4) |
+        ((s->vga.cr[0x09] & 0x40) << 3);
+    *pline_compare = line_compare;
+}
+
+static uint32_t cirrus_get_bpp16_depth(CirrusVGAState * s)
+{
+    uint32_t ret = 16;
+
+    switch (s->cirrus_hidden_dac_data & 0xf) {
+    case 0:
+       ret = 15;
+       break;                  /* Sierra HiColor */
+    case 1:
+       ret = 16;
+       break;                  /* XGA HiColor */
+    default:
+#ifdef DEBUG_CIRRUS
+       printf("cirrus: invalid DAC value %x in 16bpp\n",
+              (s->cirrus_hidden_dac_data & 0xf));
+#endif
+       ret = 15;               /* XXX */
+       break;
+    }
+    return ret;
+}
+
+static int cirrus_get_bpp(VGACommonState *s1)
+{
+    CirrusVGAState * s = container_of(s1, CirrusVGAState, vga);
+    uint32_t ret = 8;
+
+    if ((s->vga.sr[0x07] & 0x01) != 0) {
+       /* Cirrus SVGA */
+       switch (s->vga.sr[0x07] & CIRRUS_SR7_BPP_MASK) {
+       case CIRRUS_SR7_BPP_8:
+           ret = 8;
+           break;
+       case CIRRUS_SR7_BPP_16_DOUBLEVCLK:
+           ret = cirrus_get_bpp16_depth(s);
+           break;
+       case CIRRUS_SR7_BPP_24:
+           ret = 24;
+           break;
+       case CIRRUS_SR7_BPP_16:
+           ret = cirrus_get_bpp16_depth(s);
+           break;
+       case CIRRUS_SR7_BPP_32:
+           ret = 32;
+           break;
+       default:
+#ifdef DEBUG_CIRRUS
+           printf("cirrus: unknown bpp - sr7=%x\n", s->vga.sr[0x7]);
+#endif
+           ret = 8;
+           break;
+       }
+    } else {
+       /* VGA */
+       ret = 0;
+    }
+
+    return ret;
+}
+
+static void cirrus_get_resolution(VGACommonState *s, int *pwidth, int *pheight)
+{
+    int width, height;
+
+    width = (s->cr[0x01] + 1) * 8;
+    height = s->cr[0x12] |
+        ((s->cr[0x07] & 0x02) << 7) |
+        ((s->cr[0x07] & 0x40) << 3);
+    height = (height + 1);
+    /* interlace support */
+    if (s->cr[0x1a] & 0x01)
+        height = height * 2;
+    *pwidth = width;
+    *pheight = height;
+}
+
+/***************************************
+ *
+ * bank memory
+ *
+ ***************************************/
+
+static void cirrus_update_bank_ptr(CirrusVGAState * s, unsigned bank_index)
+{
+    unsigned offset;
+    unsigned limit;
+
+    if ((s->vga.gr[0x0b] & 0x01) != 0) /* dual bank */
+       offset = s->vga.gr[0x09 + bank_index];
+    else                       /* single bank */
+       offset = s->vga.gr[0x09];
+
+    if ((s->vga.gr[0x0b] & 0x20) != 0)
+       offset <<= 14;
+    else
+       offset <<= 12;
+
+    if (s->real_vram_size <= offset)
+       limit = 0;
+    else
+       limit = s->real_vram_size - offset;
+
+    if (((s->vga.gr[0x0b] & 0x01) == 0) && (bank_index != 0)) {
+       if (limit > 0x8000) {
+           offset += 0x8000;
+           limit -= 0x8000;
+       } else {
+           limit = 0;
+       }
+    }
+
+    if (limit > 0) {
+       s->cirrus_bank_base[bank_index] = offset;
+       s->cirrus_bank_limit[bank_index] = limit;
+    } else {
+       s->cirrus_bank_base[bank_index] = 0;
+       s->cirrus_bank_limit[bank_index] = 0;
+    }
+}
+
+/***************************************
+ *
+ *  I/O access between 0x3c4-0x3c5
+ *
+ ***************************************/
+
+static int cirrus_vga_read_sr(CirrusVGAState * s)
+{
+    switch (s->vga.sr_index) {
+    case 0x00:                 // Standard VGA
+    case 0x01:                 // Standard VGA
+    case 0x02:                 // Standard VGA
+    case 0x03:                 // Standard VGA
+    case 0x04:                 // Standard VGA
+       return s->vga.sr[s->vga.sr_index];
+    case 0x06:                 // Unlock Cirrus extensions
+       return s->vga.sr[s->vga.sr_index];
+    case 0x10:
+    case 0x30:
+    case 0x50:
+    case 0x70:                 // Graphics Cursor X
+    case 0x90:
+    case 0xb0:
+    case 0xd0:
+    case 0xf0:                 // Graphics Cursor X
+       return s->vga.sr[0x10];
+    case 0x11:
+    case 0x31:
+    case 0x51:
+    case 0x71:                 // Graphics Cursor Y
+    case 0x91:
+    case 0xb1:
+    case 0xd1:
+    case 0xf1:                 // Graphics Cursor Y
+       return s->vga.sr[0x11];
+    case 0x05:                 // ???
+    case 0x07:                 // Extended Sequencer Mode
+    case 0x08:                 // EEPROM Control
+    case 0x09:                 // Scratch Register 0
+    case 0x0a:                 // Scratch Register 1
+    case 0x0b:                 // VCLK 0
+    case 0x0c:                 // VCLK 1
+    case 0x0d:                 // VCLK 2
+    case 0x0e:                 // VCLK 3
+    case 0x0f:                 // DRAM Control
+    case 0x12:                 // Graphics Cursor Attribute
+    case 0x13:                 // Graphics Cursor Pattern Address
+    case 0x14:                 // Scratch Register 2
+    case 0x15:                 // Scratch Register 3
+    case 0x16:                 // Performance Tuning Register
+    case 0x17:                 // Configuration Readback and Extended Control
+    case 0x18:                 // Signature Generator Control
+    case 0x19:                 // Signal Generator Result
+    case 0x1a:                 // Signal Generator Result
+    case 0x1b:                 // VCLK 0 Denominator & Post
+    case 0x1c:                 // VCLK 1 Denominator & Post
+    case 0x1d:                 // VCLK 2 Denominator & Post
+    case 0x1e:                 // VCLK 3 Denominator & Post
+    case 0x1f:                 // BIOS Write Enable and MCLK select
+#ifdef DEBUG_CIRRUS
+       printf("cirrus: handled inport sr_index %02x\n", s->vga.sr_index);
+#endif
+       return s->vga.sr[s->vga.sr_index];
+    default:
+#ifdef DEBUG_CIRRUS
+       printf("cirrus: inport sr_index %02x\n", s->vga.sr_index);
+#endif
+       return 0xff;
+       break;
+    }
+}
+
+static void cirrus_vga_write_sr(CirrusVGAState * s, uint32_t val)
+{
+    switch (s->vga.sr_index) {
+    case 0x00:                 // Standard VGA
+    case 0x01:                 // Standard VGA
+    case 0x02:                 // Standard VGA
+    case 0x03:                 // Standard VGA
+    case 0x04:                 // Standard VGA
+       s->vga.sr[s->vga.sr_index] = val & sr_mask[s->vga.sr_index];
+       if (s->vga.sr_index == 1)
+            s->vga.update_retrace_info(&s->vga);
+        break;
+    case 0x06:                 // Unlock Cirrus extensions
+       val &= 0x17;
+       if (val == 0x12) {
+           s->vga.sr[s->vga.sr_index] = 0x12;
+       } else {
+           s->vga.sr[s->vga.sr_index] = 0x0f;
+       }
+       break;
+    case 0x10:
+    case 0x30:
+    case 0x50:
+    case 0x70:                 // Graphics Cursor X
+    case 0x90:
+    case 0xb0:
+    case 0xd0:
+    case 0xf0:                 // Graphics Cursor X
+       s->vga.sr[0x10] = val;
+       s->hw_cursor_x = (val << 3) | (s->vga.sr_index >> 5);
+       break;
+    case 0x11:
+    case 0x31:
+    case 0x51:
+    case 0x71:                 // Graphics Cursor Y
+    case 0x91:
+    case 0xb1:
+    case 0xd1:
+    case 0xf1:                 // Graphics Cursor Y
+       s->vga.sr[0x11] = val;
+       s->hw_cursor_y = (val << 3) | (s->vga.sr_index >> 5);
+       break;
+    case 0x07:                 // Extended Sequencer Mode
+    cirrus_update_memory_access(s);
+    case 0x08:                 // EEPROM Control
+    case 0x09:                 // Scratch Register 0
+    case 0x0a:                 // Scratch Register 1
+    case 0x0b:                 // VCLK 0
+    case 0x0c:                 // VCLK 1
+    case 0x0d:                 // VCLK 2
+    case 0x0e:                 // VCLK 3
+    case 0x0f:                 // DRAM Control
+    case 0x12:                 // Graphics Cursor Attribute
+    case 0x13:                 // Graphics Cursor Pattern Address
+    case 0x14:                 // Scratch Register 2
+    case 0x15:                 // Scratch Register 3
+    case 0x16:                 // Performance Tuning Register
+    case 0x18:                 // Signature Generator Control
+    case 0x19:                 // Signature Generator Result
+    case 0x1a:                 // Signature Generator Result
+    case 0x1b:                 // VCLK 0 Denominator & Post
+    case 0x1c:                 // VCLK 1 Denominator & Post
+    case 0x1d:                 // VCLK 2 Denominator & Post
+    case 0x1e:                 // VCLK 3 Denominator & Post
+    case 0x1f:                 // BIOS Write Enable and MCLK select
+       s->vga.sr[s->vga.sr_index] = val;
+#ifdef DEBUG_CIRRUS
+       printf("cirrus: handled outport sr_index %02x, sr_value %02x\n",
+              s->vga.sr_index, val);
+#endif
+       break;
+    case 0x17:                 // Configuration Readback and Extended Control
+       s->vga.sr[s->vga.sr_index] = (s->vga.sr[s->vga.sr_index] & 0x38)
+                                   | (val & 0xc7);
+        cirrus_update_memory_access(s);
+        break;
+    default:
+#ifdef DEBUG_CIRRUS
+       printf("cirrus: outport sr_index %02x, sr_value %02x\n",
+               s->vga.sr_index, val);
+#endif
+       break;
+    }
+}
+
+/***************************************
+ *
+ *  I/O access at 0x3c6
+ *
+ ***************************************/
+
+static int cirrus_read_hidden_dac(CirrusVGAState * s)
+{
+    if (++s->cirrus_hidden_dac_lockindex == 5) {
+        s->cirrus_hidden_dac_lockindex = 0;
+        return s->cirrus_hidden_dac_data;
+    }
+    return 0xff;
+}
+
+static void cirrus_write_hidden_dac(CirrusVGAState * s, int reg_value)
+{
+    if (s->cirrus_hidden_dac_lockindex == 4) {
+       s->cirrus_hidden_dac_data = reg_value;
+#if defined(DEBUG_CIRRUS)
+       printf("cirrus: outport hidden DAC, value %02x\n", reg_value);
+#endif
+    }
+    s->cirrus_hidden_dac_lockindex = 0;
+}
+
+/***************************************
+ *
+ *  I/O access at 0x3c9
+ *
+ ***************************************/
+
+static int cirrus_vga_read_palette(CirrusVGAState * s)
+{
+    int val;
+
+    if ((s->vga.sr[0x12] & CIRRUS_CURSOR_HIDDENPEL)) {
+        val = s->cirrus_hidden_palette[(s->vga.dac_read_index & 0x0f) * 3 +
+                                       s->vga.dac_sub_index];
+    } else {
+        val = s->vga.palette[s->vga.dac_read_index * 3 + s->vga.dac_sub_index];
+    }
+    if (++s->vga.dac_sub_index == 3) {
+       s->vga.dac_sub_index = 0;
+       s->vga.dac_read_index++;
+    }
+    return val;
+}
+
+static void cirrus_vga_write_palette(CirrusVGAState * s, int reg_value)
+{
+    s->vga.dac_cache[s->vga.dac_sub_index] = reg_value;
+    if (++s->vga.dac_sub_index == 3) {
+        if ((s->vga.sr[0x12] & CIRRUS_CURSOR_HIDDENPEL)) {
+            memcpy(&s->cirrus_hidden_palette[(s->vga.dac_write_index & 0x0f) * 3],
+                   s->vga.dac_cache, 3);
+        } else {
+            memcpy(&s->vga.palette[s->vga.dac_write_index * 3], s->vga.dac_cache, 3);
+        }
+        /* XXX update cursor */
+       s->vga.dac_sub_index = 0;
+       s->vga.dac_write_index++;
+    }
+}
+
+/***************************************
+ *
+ *  I/O access between 0x3ce-0x3cf
+ *
+ ***************************************/
+
+static int cirrus_vga_read_gr(CirrusVGAState * s, unsigned reg_index)
+{
+    switch (reg_index) {
+    case 0x00: // Standard VGA, BGCOLOR 0x000000ff
+        return s->cirrus_shadow_gr0;
+    case 0x01: // Standard VGA, FGCOLOR 0x000000ff
+        return s->cirrus_shadow_gr1;
+    case 0x02:                 // Standard VGA
+    case 0x03:                 // Standard VGA
+    case 0x04:                 // Standard VGA
+    case 0x06:                 // Standard VGA
+    case 0x07:                 // Standard VGA
+    case 0x08:                 // Standard VGA
+        return s->vga.gr[s->vga.gr_index];
+    case 0x05:                 // Standard VGA, Cirrus extended mode
+    default:
+       break;
+    }
+
+    if (reg_index < 0x3a) {
+       return s->vga.gr[reg_index];
+    } else {
+#ifdef DEBUG_CIRRUS
+       printf("cirrus: inport gr_index %02x\n", reg_index);
+#endif
+       return 0xff;
+    }
+}
+
+static void
+cirrus_vga_write_gr(CirrusVGAState * s, unsigned reg_index, int reg_value)
+{
+#if defined(DEBUG_BITBLT) && 0
+    printf("gr%02x: %02x\n", reg_index, reg_value);
+#endif
+    switch (reg_index) {
+    case 0x00:                 // Standard VGA, BGCOLOR 0x000000ff
+       s->vga.gr[reg_index] = reg_value & gr_mask[reg_index];
+       s->cirrus_shadow_gr0 = reg_value;
+       break;
+    case 0x01:                 // Standard VGA, FGCOLOR 0x000000ff
+       s->vga.gr[reg_index] = reg_value & gr_mask[reg_index];
+       s->cirrus_shadow_gr1 = reg_value;
+       break;
+    case 0x02:                 // Standard VGA
+    case 0x03:                 // Standard VGA
+    case 0x04:                 // Standard VGA
+    case 0x06:                 // Standard VGA
+    case 0x07:                 // Standard VGA
+    case 0x08:                 // Standard VGA
+       s->vga.gr[reg_index] = reg_value & gr_mask[reg_index];
+        break;
+    case 0x05:                 // Standard VGA, Cirrus extended mode
+       s->vga.gr[reg_index] = reg_value & 0x7f;
+        cirrus_update_memory_access(s);
+       break;
+    case 0x09:                 // bank offset #0
+    case 0x0A:                 // bank offset #1
+       s->vga.gr[reg_index] = reg_value;
+       cirrus_update_bank_ptr(s, 0);
+       cirrus_update_bank_ptr(s, 1);
+        cirrus_update_memory_access(s);
+        break;
+    case 0x0B:
+       s->vga.gr[reg_index] = reg_value;
+       cirrus_update_bank_ptr(s, 0);
+       cirrus_update_bank_ptr(s, 1);
+        cirrus_update_memory_access(s);
+       break;
+    case 0x10:                 // BGCOLOR 0x0000ff00
+    case 0x11:                 // FGCOLOR 0x0000ff00
+    case 0x12:                 // BGCOLOR 0x00ff0000
+    case 0x13:                 // FGCOLOR 0x00ff0000
+    case 0x14:                 // BGCOLOR 0xff000000
+    case 0x15:                 // FGCOLOR 0xff000000
+    case 0x20:                 // BLT WIDTH 0x0000ff
+    case 0x22:                 // BLT HEIGHT 0x0000ff
+    case 0x24:                 // BLT DEST PITCH 0x0000ff
+    case 0x26:                 // BLT SRC PITCH 0x0000ff
+    case 0x28:                 // BLT DEST ADDR 0x0000ff
+    case 0x29:                 // BLT DEST ADDR 0x00ff00
+    case 0x2c:                 // BLT SRC ADDR 0x0000ff
+    case 0x2d:                 // BLT SRC ADDR 0x00ff00
+    case 0x2f:                  // BLT WRITEMASK
+    case 0x30:                 // BLT MODE
+    case 0x32:                 // RASTER OP
+    case 0x33:                 // BLT MODEEXT
+    case 0x34:                 // BLT TRANSPARENT COLOR 0x00ff
+    case 0x35:                 // BLT TRANSPARENT COLOR 0xff00
+    case 0x38:                 // BLT TRANSPARENT COLOR MASK 0x00ff
+    case 0x39:                 // BLT TRANSPARENT COLOR MASK 0xff00
+       s->vga.gr[reg_index] = reg_value;
+       break;
+    case 0x21:                 // BLT WIDTH 0x001f00
+    case 0x23:                 // BLT HEIGHT 0x001f00
+    case 0x25:                 // BLT DEST PITCH 0x001f00
+    case 0x27:                 // BLT SRC PITCH 0x001f00
+       s->vga.gr[reg_index] = reg_value & 0x1f;
+       break;
+    case 0x2a:                 // BLT DEST ADDR 0x3f0000
+       s->vga.gr[reg_index] = reg_value & 0x3f;
+        /* if auto start mode, starts bit blt now */
+        if (s->vga.gr[0x31] & CIRRUS_BLT_AUTOSTART) {
+            cirrus_bitblt_start(s);
+        }
+       break;
+    case 0x2e:                 // BLT SRC ADDR 0x3f0000
+       s->vga.gr[reg_index] = reg_value & 0x3f;
+       break;
+    case 0x31:                 // BLT STATUS/START
+       cirrus_write_bitblt(s, reg_value);
+       break;
+    default:
+#ifdef DEBUG_CIRRUS
+       printf("cirrus: outport gr_index %02x, gr_value %02x\n", reg_index,
+              reg_value);
+#endif
+       break;
+    }
+}
+
+/***************************************
+ *
+ *  I/O access between 0x3d4-0x3d5
+ *
+ ***************************************/
+
+static int cirrus_vga_read_cr(CirrusVGAState * s, unsigned reg_index)
+{
+    switch (reg_index) {
+    case 0x00:                 // Standard VGA
+    case 0x01:                 // Standard VGA
+    case 0x02:                 // Standard VGA
+    case 0x03:                 // Standard VGA
+    case 0x04:                 // Standard VGA
+    case 0x05:                 // Standard VGA
+    case 0x06:                 // Standard VGA
+    case 0x07:                 // Standard VGA
+    case 0x08:                 // Standard VGA
+    case 0x09:                 // Standard VGA
+    case 0x0a:                 // Standard VGA
+    case 0x0b:                 // Standard VGA
+    case 0x0c:                 // Standard VGA
+    case 0x0d:                 // Standard VGA
+    case 0x0e:                 // Standard VGA
+    case 0x0f:                 // Standard VGA
+    case 0x10:                 // Standard VGA
+    case 0x11:                 // Standard VGA
+    case 0x12:                 // Standard VGA
+    case 0x13:                 // Standard VGA
+    case 0x14:                 // Standard VGA
+    case 0x15:                 // Standard VGA
+    case 0x16:                 // Standard VGA
+    case 0x17:                 // Standard VGA
+    case 0x18:                 // Standard VGA
+       return s->vga.cr[s->vga.cr_index];
+    case 0x24:                 // Attribute Controller Toggle Readback (R)
+        return (s->vga.ar_flip_flop << 7);
+    case 0x19:                 // Interlace End
+    case 0x1a:                 // Miscellaneous Control
+    case 0x1b:                 // Extended Display Control
+    case 0x1c:                 // Sync Adjust and Genlock
+    case 0x1d:                 // Overlay Extended Control
+    case 0x22:                 // Graphics Data Latches Readback (R)
+    case 0x25:                 // Part Status
+    case 0x27:                 // Part ID (R)
+       return s->vga.cr[s->vga.cr_index];
+    case 0x26:                 // Attribute Controller Index Readback (R)
+       return s->vga.ar_index & 0x3f;
+       break;
+    default:
+#ifdef DEBUG_CIRRUS
+       printf("cirrus: inport cr_index %02x\n", reg_index);
+#endif
+       return 0xff;
+    }
+}
+
+static void cirrus_vga_write_cr(CirrusVGAState * s, int reg_value)
+{
+    switch (s->vga.cr_index) {
+    case 0x00:                 // Standard VGA
+    case 0x01:                 // Standard VGA
+    case 0x02:                 // Standard VGA
+    case 0x03:                 // Standard VGA
+    case 0x04:                 // Standard VGA
+    case 0x05:                 // Standard VGA
+    case 0x06:                 // Standard VGA
+    case 0x07:                 // Standard VGA
+    case 0x08:                 // Standard VGA
+    case 0x09:                 // Standard VGA
+    case 0x0a:                 // Standard VGA
+    case 0x0b:                 // Standard VGA
+    case 0x0c:                 // Standard VGA
+    case 0x0d:                 // Standard VGA
+    case 0x0e:                 // Standard VGA
+    case 0x0f:                 // Standard VGA
+    case 0x10:                 // Standard VGA
+    case 0x11:                 // Standard VGA
+    case 0x12:                 // Standard VGA
+    case 0x13:                 // Standard VGA
+    case 0x14:                 // Standard VGA
+    case 0x15:                 // Standard VGA
+    case 0x16:                 // Standard VGA
+    case 0x17:                 // Standard VGA
+    case 0x18:                 // Standard VGA
+       /* handle CR0-7 protection */
+       if ((s->vga.cr[0x11] & 0x80) && s->vga.cr_index <= 7) {
+           /* can always write bit 4 of CR7 */
+           if (s->vga.cr_index == 7)
+               s->vga.cr[7] = (s->vga.cr[7] & ~0x10) | (reg_value & 0x10);
+           return;
+       }
+       s->vga.cr[s->vga.cr_index] = reg_value;
+       switch(s->vga.cr_index) {
+       case 0x00:
+       case 0x04:
+       case 0x05:
+       case 0x06:
+       case 0x07:
+       case 0x11:
+       case 0x17:
+           s->vga.update_retrace_info(&s->vga);
+           break;
+       }
+        break;
+    case 0x19:                 // Interlace End
+    case 0x1a:                 // Miscellaneous Control
+    case 0x1b:                 // Extended Display Control
+    case 0x1c:                 // Sync Adjust and Genlock
+    case 0x1d:                 // Overlay Extended Control
+       s->vga.cr[s->vga.cr_index] = reg_value;
+#ifdef DEBUG_CIRRUS
+       printf("cirrus: handled outport cr_index %02x, cr_value %02x\n",
+              s->vga.cr_index, reg_value);
+#endif
+       break;
+    case 0x22:                 // Graphics Data Latches Readback (R)
+    case 0x24:                 // Attribute Controller Toggle Readback (R)
+    case 0x26:                 // Attribute Controller Index Readback (R)
+    case 0x27:                 // Part ID (R)
+       break;
+    case 0x25:                 // Part Status
+    default:
+#ifdef DEBUG_CIRRUS
+       printf("cirrus: outport cr_index %02x, cr_value %02x\n",
+               s->vga.cr_index, reg_value);
+#endif
+       break;
+    }
+}
+
+/***************************************
+ *
+ *  memory-mapped I/O (bitblt)
+ *
+ ***************************************/
+
+static uint8_t cirrus_mmio_blt_read(CirrusVGAState * s, unsigned address)
+{
+    int value = 0xff;
+
+    switch (address) {
+    case (CIRRUS_MMIO_BLTBGCOLOR + 0):
+       value = cirrus_vga_read_gr(s, 0x00);
+       break;
+    case (CIRRUS_MMIO_BLTBGCOLOR + 1):
+       value = cirrus_vga_read_gr(s, 0x10);
+       break;
+    case (CIRRUS_MMIO_BLTBGCOLOR + 2):
+       value = cirrus_vga_read_gr(s, 0x12);
+       break;
+    case (CIRRUS_MMIO_BLTBGCOLOR + 3):
+       value = cirrus_vga_read_gr(s, 0x14);
+       break;
+    case (CIRRUS_MMIO_BLTFGCOLOR + 0):
+       value = cirrus_vga_read_gr(s, 0x01);
+       break;
+    case (CIRRUS_MMIO_BLTFGCOLOR + 1):
+       value = cirrus_vga_read_gr(s, 0x11);
+       break;
+    case (CIRRUS_MMIO_BLTFGCOLOR + 2):
+       value = cirrus_vga_read_gr(s, 0x13);
+       break;
+    case (CIRRUS_MMIO_BLTFGCOLOR + 3):
+       value = cirrus_vga_read_gr(s, 0x15);
+       break;
+    case (CIRRUS_MMIO_BLTWIDTH + 0):
+       value = cirrus_vga_read_gr(s, 0x20);
+       break;
+    case (CIRRUS_MMIO_BLTWIDTH + 1):
+       value = cirrus_vga_read_gr(s, 0x21);
+       break;
+    case (CIRRUS_MMIO_BLTHEIGHT + 0):
+       value = cirrus_vga_read_gr(s, 0x22);
+       break;
+    case (CIRRUS_MMIO_BLTHEIGHT + 1):
+       value = cirrus_vga_read_gr(s, 0x23);
+       break;
+    case (CIRRUS_MMIO_BLTDESTPITCH + 0):
+       value = cirrus_vga_read_gr(s, 0x24);
+       break;
+    case (CIRRUS_MMIO_BLTDESTPITCH + 1):
+       value = cirrus_vga_read_gr(s, 0x25);
+       break;
+    case (CIRRUS_MMIO_BLTSRCPITCH + 0):
+       value = cirrus_vga_read_gr(s, 0x26);
+       break;
+    case (CIRRUS_MMIO_BLTSRCPITCH + 1):
+       value = cirrus_vga_read_gr(s, 0x27);
+       break;
+    case (CIRRUS_MMIO_BLTDESTADDR + 0):
+       value = cirrus_vga_read_gr(s, 0x28);
+       break;
+    case (CIRRUS_MMIO_BLTDESTADDR + 1):
+       value = cirrus_vga_read_gr(s, 0x29);
+       break;
+    case (CIRRUS_MMIO_BLTDESTADDR + 2):
+       value = cirrus_vga_read_gr(s, 0x2a);
+       break;
+    case (CIRRUS_MMIO_BLTSRCADDR + 0):
+       value = cirrus_vga_read_gr(s, 0x2c);
+       break;
+    case (CIRRUS_MMIO_BLTSRCADDR + 1):
+       value = cirrus_vga_read_gr(s, 0x2d);
+       break;
+    case (CIRRUS_MMIO_BLTSRCADDR + 2):
+       value = cirrus_vga_read_gr(s, 0x2e);
+       break;
+    case CIRRUS_MMIO_BLTWRITEMASK:
+       value = cirrus_vga_read_gr(s, 0x2f);
+       break;
+    case CIRRUS_MMIO_BLTMODE:
+       value = cirrus_vga_read_gr(s, 0x30);
+       break;
+    case CIRRUS_MMIO_BLTROP:
+       value = cirrus_vga_read_gr(s, 0x32);
+       break;
+    case CIRRUS_MMIO_BLTMODEEXT:
+       value = cirrus_vga_read_gr(s, 0x33);
+       break;
+    case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0):
+       value = cirrus_vga_read_gr(s, 0x34);
+       break;
+    case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1):
+       value = cirrus_vga_read_gr(s, 0x35);
+       break;
+    case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0):
+       value = cirrus_vga_read_gr(s, 0x38);
+       break;
+    case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1):
+       value = cirrus_vga_read_gr(s, 0x39);
+       break;
+    case CIRRUS_MMIO_BLTSTATUS:
+       value = cirrus_vga_read_gr(s, 0x31);
+       break;
+    default:
+#ifdef DEBUG_CIRRUS
+       printf("cirrus: mmio read - address 0x%04x\n", address);
+#endif
+       break;
+    }
+
+    return (uint8_t) value;
+}
+
+static void cirrus_mmio_blt_write(CirrusVGAState * s, unsigned address,
+                                 uint8_t value)
+{
+    switch (address) {
+    case (CIRRUS_MMIO_BLTBGCOLOR + 0):
+       cirrus_vga_write_gr(s, 0x00, value);
+       break;
+    case (CIRRUS_MMIO_BLTBGCOLOR + 1):
+       cirrus_vga_write_gr(s, 0x10, value);
+       break;
+    case (CIRRUS_MMIO_BLTBGCOLOR + 2):
+       cirrus_vga_write_gr(s, 0x12, value);
+       break;
+    case (CIRRUS_MMIO_BLTBGCOLOR + 3):
+       cirrus_vga_write_gr(s, 0x14, value);
+       break;
+    case (CIRRUS_MMIO_BLTFGCOLOR + 0):
+       cirrus_vga_write_gr(s, 0x01, value);
+       break;
+    case (CIRRUS_MMIO_BLTFGCOLOR + 1):
+       cirrus_vga_write_gr(s, 0x11, value);
+       break;
+    case (CIRRUS_MMIO_BLTFGCOLOR + 2):
+       cirrus_vga_write_gr(s, 0x13, value);
+       break;
+    case (CIRRUS_MMIO_BLTFGCOLOR + 3):
+       cirrus_vga_write_gr(s, 0x15, value);
+       break;
+    case (CIRRUS_MMIO_BLTWIDTH + 0):
+       cirrus_vga_write_gr(s, 0x20, value);
+       break;
+    case (CIRRUS_MMIO_BLTWIDTH + 1):
+       cirrus_vga_write_gr(s, 0x21, value);
+       break;
+    case (CIRRUS_MMIO_BLTHEIGHT + 0):
+       cirrus_vga_write_gr(s, 0x22, value);
+       break;
+    case (CIRRUS_MMIO_BLTHEIGHT + 1):
+       cirrus_vga_write_gr(s, 0x23, value);
+       break;
+    case (CIRRUS_MMIO_BLTDESTPITCH + 0):
+       cirrus_vga_write_gr(s, 0x24, value);
+       break;
+    case (CIRRUS_MMIO_BLTDESTPITCH + 1):
+       cirrus_vga_write_gr(s, 0x25, value);
+       break;
+    case (CIRRUS_MMIO_BLTSRCPITCH + 0):
+       cirrus_vga_write_gr(s, 0x26, value);
+       break;
+    case (CIRRUS_MMIO_BLTSRCPITCH + 1):
+       cirrus_vga_write_gr(s, 0x27, value);
+       break;
+    case (CIRRUS_MMIO_BLTDESTADDR + 0):
+       cirrus_vga_write_gr(s, 0x28, value);
+       break;
+    case (CIRRUS_MMIO_BLTDESTADDR + 1):
+       cirrus_vga_write_gr(s, 0x29, value);
+       break;
+    case (CIRRUS_MMIO_BLTDESTADDR + 2):
+       cirrus_vga_write_gr(s, 0x2a, value);
+       break;
+    case (CIRRUS_MMIO_BLTDESTADDR + 3):
+       /* ignored */
+       break;
+    case (CIRRUS_MMIO_BLTSRCADDR + 0):
+       cirrus_vga_write_gr(s, 0x2c, value);
+       break;
+    case (CIRRUS_MMIO_BLTSRCADDR + 1):
+       cirrus_vga_write_gr(s, 0x2d, value);
+       break;
+    case (CIRRUS_MMIO_BLTSRCADDR + 2):
+       cirrus_vga_write_gr(s, 0x2e, value);
+       break;
+    case CIRRUS_MMIO_BLTWRITEMASK:
+       cirrus_vga_write_gr(s, 0x2f, value);
+       break;
+    case CIRRUS_MMIO_BLTMODE:
+       cirrus_vga_write_gr(s, 0x30, value);
+       break;
+    case CIRRUS_MMIO_BLTROP:
+       cirrus_vga_write_gr(s, 0x32, value);
+       break;
+    case CIRRUS_MMIO_BLTMODEEXT:
+       cirrus_vga_write_gr(s, 0x33, value);
+       break;
+    case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0):
+       cirrus_vga_write_gr(s, 0x34, value);
+       break;
+    case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1):
+       cirrus_vga_write_gr(s, 0x35, value);
+       break;
+    case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0):
+       cirrus_vga_write_gr(s, 0x38, value);
+       break;
+    case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1):
+       cirrus_vga_write_gr(s, 0x39, value);
+       break;
+    case CIRRUS_MMIO_BLTSTATUS:
+       cirrus_vga_write_gr(s, 0x31, value);
+       break;
+    default:
+#ifdef DEBUG_CIRRUS
+       printf("cirrus: mmio write - addr 0x%04x val 0x%02x (ignored)\n",
+              address, value);
+#endif
+       break;
+    }
+}
+
+/***************************************
+ *
+ *  write mode 4/5
+ *
+ ***************************************/
+
+static void cirrus_mem_writeb_mode4and5_8bpp(CirrusVGAState * s,
+                                            unsigned mode,
+                                            unsigned offset,
+                                            uint32_t mem_value)
+{
+    int x;
+    unsigned val = mem_value;
+    uint8_t *dst;
+
+    dst = s->vga.vram_ptr + (offset &= s->cirrus_addr_mask);
+    for (x = 0; x < 8; x++) {
+       if (val & 0x80) {
+           *dst = s->cirrus_shadow_gr1;
+       } else if (mode == 5) {
+           *dst = s->cirrus_shadow_gr0;
+       }
+       val <<= 1;
+       dst++;
+    }
+    memory_region_set_dirty(&s->vga.vram, offset, 8);
+}
+
+static void cirrus_mem_writeb_mode4and5_16bpp(CirrusVGAState * s,
+                                             unsigned mode,
+                                             unsigned offset,
+                                             uint32_t mem_value)
+{
+    int x;
+    unsigned val = mem_value;
+    uint8_t *dst;
+
+    dst = s->vga.vram_ptr + (offset &= s->cirrus_addr_mask);
+    for (x = 0; x < 8; x++) {
+       if (val & 0x80) {
+           *dst = s->cirrus_shadow_gr1;
+           *(dst + 1) = s->vga.gr[0x11];
+       } else if (mode == 5) {
+           *dst = s->cirrus_shadow_gr0;
+           *(dst + 1) = s->vga.gr[0x10];
+       }
+       val <<= 1;
+       dst += 2;
+    }
+    memory_region_set_dirty(&s->vga.vram, offset, 16);
+}
+
+/***************************************
+ *
+ *  memory access between 0xa0000-0xbffff
+ *
+ ***************************************/
+
+static uint64_t cirrus_vga_mem_read(void *opaque,
+                                    hwaddr addr,
+                                    uint32_t size)
+{
+    CirrusVGAState *s = opaque;
+    unsigned bank_index;
+    unsigned bank_offset;
+    uint32_t val;
+
+    if ((s->vga.sr[0x07] & 0x01) == 0) {
+        return vga_mem_readb(&s->vga, addr);
+    }
+
+    if (addr < 0x10000) {
+       /* XXX handle bitblt */
+       /* video memory */
+       bank_index = addr >> 15;
+       bank_offset = addr & 0x7fff;
+       if (bank_offset < s->cirrus_bank_limit[bank_index]) {
+           bank_offset += s->cirrus_bank_base[bank_index];
+           if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
+               bank_offset <<= 4;
+           } else if (s->vga.gr[0x0B] & 0x02) {
+               bank_offset <<= 3;
+           }
+           bank_offset &= s->cirrus_addr_mask;
+           val = *(s->vga.vram_ptr + bank_offset);
+       } else
+           val = 0xff;
+    } else if (addr >= 0x18000 && addr < 0x18100) {
+       /* memory-mapped I/O */
+       val = 0xff;
+       if ((s->vga.sr[0x17] & 0x44) == 0x04) {
+           val = cirrus_mmio_blt_read(s, addr & 0xff);
+       }
+    } else {
+       val = 0xff;
+#ifdef DEBUG_CIRRUS
+       printf("cirrus: mem_readb " TARGET_FMT_plx "\n", addr);
+#endif
+    }
+    return val;
+}
+
+static void cirrus_vga_mem_write(void *opaque,
+                                 hwaddr addr,
+                                 uint64_t mem_value,
+                                 uint32_t size)
+{
+    CirrusVGAState *s = opaque;
+    unsigned bank_index;
+    unsigned bank_offset;
+    unsigned mode;
+
+    if ((s->vga.sr[0x07] & 0x01) == 0) {
+        vga_mem_writeb(&s->vga, addr, mem_value);
+        return;
+    }
+
+    if (addr < 0x10000) {
+       if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
+           /* bitblt */
+           *s->cirrus_srcptr++ = (uint8_t) mem_value;
+           if (s->cirrus_srcptr >= s->cirrus_srcptr_end) {
+               cirrus_bitblt_cputovideo_next(s);
+           }
+       } else {
+           /* video memory */
+           bank_index = addr >> 15;
+           bank_offset = addr & 0x7fff;
+           if (bank_offset < s->cirrus_bank_limit[bank_index]) {
+               bank_offset += s->cirrus_bank_base[bank_index];
+               if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
+                   bank_offset <<= 4;
+               } else if (s->vga.gr[0x0B] & 0x02) {
+                   bank_offset <<= 3;
+               }
+               bank_offset &= s->cirrus_addr_mask;
+               mode = s->vga.gr[0x05] & 0x7;
+               if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) {
+                   *(s->vga.vram_ptr + bank_offset) = mem_value;
+                    memory_region_set_dirty(&s->vga.vram, bank_offset,
+                                            sizeof(mem_value));
+               } else {
+                   if ((s->vga.gr[0x0B] & 0x14) != 0x14) {
+                       cirrus_mem_writeb_mode4and5_8bpp(s, mode,
+                                                        bank_offset,
+                                                        mem_value);
+                   } else {
+                       cirrus_mem_writeb_mode4and5_16bpp(s, mode,
+                                                         bank_offset,
+                                                         mem_value);
+                   }
+               }
+           }
+       }
+    } else if (addr >= 0x18000 && addr < 0x18100) {
+       /* memory-mapped I/O */
+       if ((s->vga.sr[0x17] & 0x44) == 0x04) {
+           cirrus_mmio_blt_write(s, addr & 0xff, mem_value);
+       }
+    } else {
+#ifdef DEBUG_CIRRUS
+        printf("cirrus: mem_writeb " TARGET_FMT_plx " value %02x\n", addr,
+               mem_value);
+#endif
+    }
+}
+
+static const MemoryRegionOps cirrus_vga_mem_ops = {
+    .read = cirrus_vga_mem_read,
+    .write = cirrus_vga_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+/***************************************
+ *
+ *  hardware cursor
+ *
+ ***************************************/
+
+static inline void invalidate_cursor1(CirrusVGAState *s)
+{
+    if (s->last_hw_cursor_size) {
+        vga_invalidate_scanlines(&s->vga,
+                                 s->last_hw_cursor_y + s->last_hw_cursor_y_start,
+                                 s->last_hw_cursor_y + s->last_hw_cursor_y_end);
+    }
+}
+
+static inline void cirrus_cursor_compute_yrange(CirrusVGAState *s)
+{
+    const uint8_t *src;
+    uint32_t content;
+    int y, y_min, y_max;
+
+    src = s->vga.vram_ptr + s->real_vram_size - 16 * 1024;
+    if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) {
+        src += (s->vga.sr[0x13] & 0x3c) * 256;
+        y_min = 64;
+        y_max = -1;
+        for(y = 0; y < 64; y++) {
+            content = ((uint32_t *)src)[0] |
+                ((uint32_t *)src)[1] |
+                ((uint32_t *)src)[2] |
+                ((uint32_t *)src)[3];
+            if (content) {
+                if (y < y_min)
+                    y_min = y;
+                if (y > y_max)
+                    y_max = y;
+            }
+            src += 16;
+        }
+    } else {
+        src += (s->vga.sr[0x13] & 0x3f) * 256;
+        y_min = 32;
+        y_max = -1;
+        for(y = 0; y < 32; y++) {
+            content = ((uint32_t *)src)[0] |
+                ((uint32_t *)(src + 128))[0];
+            if (content) {
+                if (y < y_min)
+                    y_min = y;
+                if (y > y_max)
+                    y_max = y;
+            }
+            src += 4;
+        }
+    }
+    if (y_min > y_max) {
+        s->last_hw_cursor_y_start = 0;
+        s->last_hw_cursor_y_end = 0;
+    } else {
+        s->last_hw_cursor_y_start = y_min;
+        s->last_hw_cursor_y_end = y_max + 1;
+    }
+}
+
+/* NOTE: we do not currently handle the cursor bitmap change, so we
+   update the cursor only if it moves. */
+static void cirrus_cursor_invalidate(VGACommonState *s1)
+{
+    CirrusVGAState *s = container_of(s1, CirrusVGAState, vga);
+    int size;
+
+    if (!(s->vga.sr[0x12] & CIRRUS_CURSOR_SHOW)) {
+        size = 0;
+    } else {
+        if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE)
+            size = 64;
+        else
+            size = 32;
+    }
+    /* invalidate last cursor and new cursor if any change */
+    if (s->last_hw_cursor_size != size ||
+        s->last_hw_cursor_x != s->hw_cursor_x ||
+        s->last_hw_cursor_y != s->hw_cursor_y) {
+
+        invalidate_cursor1(s);
+
+        s->last_hw_cursor_size = size;
+        s->last_hw_cursor_x = s->hw_cursor_x;
+        s->last_hw_cursor_y = s->hw_cursor_y;
+        /* compute the real cursor min and max y */
+        cirrus_cursor_compute_yrange(s);
+        invalidate_cursor1(s);
+    }
+}
+
+#define DEPTH 8
+#include "hw/cirrus_vga_template.h"
+
+#define DEPTH 16
+#include "hw/cirrus_vga_template.h"
+
+#define DEPTH 32
+#include "hw/cirrus_vga_template.h"
+
+static void cirrus_cursor_draw_line(VGACommonState *s1, uint8_t *d1, int scr_y)
+{
+    CirrusVGAState *s = container_of(s1, CirrusVGAState, vga);
+    DisplaySurface *surface = qemu_console_surface(s->vga.con);
+    int w, h, bpp, x1, x2, poffset;
+    unsigned int color0, color1;
+    const uint8_t *palette, *src;
+    uint32_t content;
+
+    if (!(s->vga.sr[0x12] & CIRRUS_CURSOR_SHOW))
+        return;
+    /* fast test to see if the cursor intersects with the scan line */
+    if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) {
+        h = 64;
+    } else {
+        h = 32;
+    }
+    if (scr_y < s->hw_cursor_y ||
+        scr_y >= (s->hw_cursor_y + h))
+        return;
+
+    src = s->vga.vram_ptr + s->real_vram_size - 16 * 1024;
+    if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) {
+        src += (s->vga.sr[0x13] & 0x3c) * 256;
+        src += (scr_y - s->hw_cursor_y) * 16;
+        poffset = 8;
+        content = ((uint32_t *)src)[0] |
+            ((uint32_t *)src)[1] |
+            ((uint32_t *)src)[2] |
+            ((uint32_t *)src)[3];
+    } else {
+        src += (s->vga.sr[0x13] & 0x3f) * 256;
+        src += (scr_y - s->hw_cursor_y) * 4;
+        poffset = 128;
+        content = ((uint32_t *)src)[0] |
+            ((uint32_t *)(src + 128))[0];
+    }
+    /* if nothing to draw, no need to continue */
+    if (!content)
+        return;
+    w = h;
+
+    x1 = s->hw_cursor_x;
+    if (x1 >= s->vga.last_scr_width)
+        return;
+    x2 = s->hw_cursor_x + w;
+    if (x2 > s->vga.last_scr_width)
+        x2 = s->vga.last_scr_width;
+    w = x2 - x1;
+    palette = s->cirrus_hidden_palette;
+    color0 = s->vga.rgb_to_pixel(c6_to_8(palette[0x0 * 3]),
+                                 c6_to_8(palette[0x0 * 3 + 1]),
+                                 c6_to_8(palette[0x0 * 3 + 2]));
+    color1 = s->vga.rgb_to_pixel(c6_to_8(palette[0xf * 3]),
+                                 c6_to_8(palette[0xf * 3 + 1]),
+                                 c6_to_8(palette[0xf * 3 + 2]));
+    bpp = surface_bytes_per_pixel(surface);
+    d1 += x1 * bpp;
+    switch (surface_bits_per_pixel(surface)) {
+    default:
+        break;
+    case 8:
+        vga_draw_cursor_line_8(d1, src, poffset, w, color0, color1, 0xff);
+        break;
+    case 15:
+        vga_draw_cursor_line_16(d1, src, poffset, w, color0, color1, 0x7fff);
+        break;
+    case 16:
+        vga_draw_cursor_line_16(d1, src, poffset, w, color0, color1, 0xffff);
+        break;
+    case 32:
+        vga_draw_cursor_line_32(d1, src, poffset, w, color0, color1, 0xffffff);
+        break;
+    }
+}
+
+/***************************************
+ *
+ *  LFB memory access
+ *
+ ***************************************/
+
+static uint64_t cirrus_linear_read(void *opaque, hwaddr addr,
+                                   unsigned size)
+{
+    CirrusVGAState *s = opaque;
+    uint32_t ret;
+
+    addr &= s->cirrus_addr_mask;
+
+    if (((s->vga.sr[0x17] & 0x44) == 0x44) &&
+        ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) {
+       /* memory-mapped I/O */
+       ret = cirrus_mmio_blt_read(s, addr & 0xff);
+    } else if (0) {
+       /* XXX handle bitblt */
+       ret = 0xff;
+    } else {
+       /* video memory */
+       if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
+           addr <<= 4;
+       } else if (s->vga.gr[0x0B] & 0x02) {
+           addr <<= 3;
+       }
+       addr &= s->cirrus_addr_mask;
+       ret = *(s->vga.vram_ptr + addr);
+    }
+
+    return ret;
+}
+
+static void cirrus_linear_write(void *opaque, hwaddr addr,
+                                uint64_t val, unsigned size)
+{
+    CirrusVGAState *s = opaque;
+    unsigned mode;
+
+    addr &= s->cirrus_addr_mask;
+
+    if (((s->vga.sr[0x17] & 0x44) == 0x44) &&
+        ((addr & s->linear_mmio_mask) ==  s->linear_mmio_mask)) {
+       /* memory-mapped I/O */
+       cirrus_mmio_blt_write(s, addr & 0xff, val);
+    } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
+       /* bitblt */
+       *s->cirrus_srcptr++ = (uint8_t) val;
+       if (s->cirrus_srcptr >= s->cirrus_srcptr_end) {
+           cirrus_bitblt_cputovideo_next(s);
+       }
+    } else {
+       /* video memory */
+       if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
+           addr <<= 4;
+       } else if (s->vga.gr[0x0B] & 0x02) {
+           addr <<= 3;
+       }
+       addr &= s->cirrus_addr_mask;
+
+       mode = s->vga.gr[0x05] & 0x7;
+       if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) {
+           *(s->vga.vram_ptr + addr) = (uint8_t) val;
+            memory_region_set_dirty(&s->vga.vram, addr, 1);
+       } else {
+           if ((s->vga.gr[0x0B] & 0x14) != 0x14) {
+               cirrus_mem_writeb_mode4and5_8bpp(s, mode, addr, val);
+           } else {
+               cirrus_mem_writeb_mode4and5_16bpp(s, mode, addr, val);
+           }
+       }
+    }
+}
+
+/***************************************
+ *
+ *  system to screen memory access
+ *
+ ***************************************/
+
+
+static uint64_t cirrus_linear_bitblt_read(void *opaque,
+                                          hwaddr addr,
+                                          unsigned size)
+{
+    CirrusVGAState *s = opaque;
+    uint32_t ret;
+
+    /* XXX handle bitblt */
+    (void)s;
+    ret = 0xff;
+    return ret;
+}
+
+static void cirrus_linear_bitblt_write(void *opaque,
+                                       hwaddr addr,
+                                       uint64_t val,
+                                       unsigned size)
+{
+    CirrusVGAState *s = opaque;
+
+    if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
+       /* bitblt */
+       *s->cirrus_srcptr++ = (uint8_t) val;
+       if (s->cirrus_srcptr >= s->cirrus_srcptr_end) {
+           cirrus_bitblt_cputovideo_next(s);
+       }
+    }
+}
+
+static const MemoryRegionOps cirrus_linear_bitblt_io_ops = {
+    .read = cirrus_linear_bitblt_read,
+    .write = cirrus_linear_bitblt_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static void map_linear_vram_bank(CirrusVGAState *s, unsigned bank)
+{
+    MemoryRegion *mr = &s->cirrus_bank[bank];
+    bool enabled = !(s->cirrus_srcptr != s->cirrus_srcptr_end)
+        && !((s->vga.sr[0x07] & 0x01) == 0)
+        && !((s->vga.gr[0x0B] & 0x14) == 0x14)
+        && !(s->vga.gr[0x0B] & 0x02);
+
+    memory_region_set_enabled(mr, enabled);
+    memory_region_set_alias_offset(mr, s->cirrus_bank_base[bank]);
+}
+
+static void map_linear_vram(CirrusVGAState *s)
+{
+    if (s->bustype == CIRRUS_BUSTYPE_PCI && !s->linear_vram) {
+        s->linear_vram = true;
+        memory_region_add_subregion_overlap(&s->pci_bar, 0, &s->vga.vram, 1);
+    }
+    map_linear_vram_bank(s, 0);
+    map_linear_vram_bank(s, 1);
+}
+
+static void unmap_linear_vram(CirrusVGAState *s)
+{
+    if (s->bustype == CIRRUS_BUSTYPE_PCI && s->linear_vram) {
+        s->linear_vram = false;
+        memory_region_del_subregion(&s->pci_bar, &s->vga.vram);
+    }
+    memory_region_set_enabled(&s->cirrus_bank[0], false);
+    memory_region_set_enabled(&s->cirrus_bank[1], false);
+}
+
+/* Compute the memory access functions */
+static void cirrus_update_memory_access(CirrusVGAState *s)
+{
+    unsigned mode;
+
+    memory_region_transaction_begin();
+    if ((s->vga.sr[0x17] & 0x44) == 0x44) {
+        goto generic_io;
+    } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
+        goto generic_io;
+    } else {
+       if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
+            goto generic_io;
+       } else if (s->vga.gr[0x0B] & 0x02) {
+            goto generic_io;
+        }
+
+       mode = s->vga.gr[0x05] & 0x7;
+       if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) {
+            map_linear_vram(s);
+        } else {
+        generic_io:
+            unmap_linear_vram(s);
+        }
+    }
+    memory_region_transaction_commit();
+}
+
+
+/* I/O ports */
+
+static uint64_t cirrus_vga_ioport_read(void *opaque, hwaddr addr,
+                                       unsigned size)
+{
+    CirrusVGAState *c = opaque;
+    VGACommonState *s = &c->vga;
+    int val, index;
+
+    qemu_flush_coalesced_mmio_buffer();
+    addr += 0x3b0;
+
+    if (vga_ioport_invalid(s, addr)) {
+       val = 0xff;
+    } else {
+       switch (addr) {
+       case 0x3c0:
+           if (s->ar_flip_flop == 0) {
+               val = s->ar_index;
+           } else {
+               val = 0;
+           }
+           break;
+       case 0x3c1:
+           index = s->ar_index & 0x1f;
+           if (index < 21)
+               val = s->ar[index];
+           else
+               val = 0;
+           break;
+       case 0x3c2:
+           val = s->st00;
+           break;
+       case 0x3c4:
+           val = s->sr_index;
+           break;
+       case 0x3c5:
+           val = cirrus_vga_read_sr(c);
+            break;
+#ifdef DEBUG_VGA_REG
+           printf("vga: read SR%x = 0x%02x\n", s->sr_index, val);
+#endif
+           break;
+       case 0x3c6:
+           val = cirrus_read_hidden_dac(c);
+           break;
+       case 0x3c7:
+           val = s->dac_state;
+           break;
+       case 0x3c8:
+           val = s->dac_write_index;
+           c->cirrus_hidden_dac_lockindex = 0;
+           break;
+        case 0x3c9:
+            val = cirrus_vga_read_palette(c);
+            break;
+       case 0x3ca:
+           val = s->fcr;
+           break;
+       case 0x3cc:
+           val = s->msr;
+           break;
+       case 0x3ce:
+           val = s->gr_index;
+           break;
+       case 0x3cf:
+           val = cirrus_vga_read_gr(c, s->gr_index);
+#ifdef DEBUG_VGA_REG
+           printf("vga: read GR%x = 0x%02x\n", s->gr_index, val);
+#endif
+           break;
+       case 0x3b4:
+       case 0x3d4:
+           val = s->cr_index;
+           break;
+       case 0x3b5:
+       case 0x3d5:
+            val = cirrus_vga_read_cr(c, s->cr_index);
+#ifdef DEBUG_VGA_REG
+           printf("vga: read CR%x = 0x%02x\n", s->cr_index, val);
+#endif
+           break;
+       case 0x3ba:
+       case 0x3da:
+           /* just toggle to fool polling */
+           val = s->st01 = s->retrace(s);
+           s->ar_flip_flop = 0;
+           break;
+       default:
+           val = 0x00;
+           break;
+       }
+    }
+#if defined(DEBUG_VGA)
+    printf("VGA: read addr=0x%04x data=0x%02x\n", addr, val);
+#endif
+    return val;
+}
+
+static void cirrus_vga_ioport_write(void *opaque, hwaddr addr, uint64_t val,
+                                    unsigned size)
+{
+    CirrusVGAState *c = opaque;
+    VGACommonState *s = &c->vga;
+    int index;
+
+    qemu_flush_coalesced_mmio_buffer();
+    addr += 0x3b0;
+
+    /* check port range access depending on color/monochrome mode */
+    if (vga_ioport_invalid(s, addr)) {
+       return;
+    }
+#ifdef DEBUG_VGA
+    printf("VGA: write addr=0x%04x data=0x%02x\n", addr, val);
+#endif
+
+    switch (addr) {
+    case 0x3c0:
+       if (s->ar_flip_flop == 0) {
+           val &= 0x3f;
+           s->ar_index = val;
+       } else {
+           index = s->ar_index & 0x1f;
+           switch (index) {
+           case 0x00 ... 0x0f:
+               s->ar[index] = val & 0x3f;
+               break;
+           case 0x10:
+               s->ar[index] = val & ~0x10;
+               break;
+           case 0x11:
+               s->ar[index] = val;
+               break;
+           case 0x12:
+               s->ar[index] = val & ~0xc0;
+               break;
+           case 0x13:
+               s->ar[index] = val & ~0xf0;
+               break;
+           case 0x14:
+               s->ar[index] = val & ~0xf0;
+               break;
+           default:
+               break;
+           }
+       }
+       s->ar_flip_flop ^= 1;
+       break;
+    case 0x3c2:
+       s->msr = val & ~0x10;
+       s->update_retrace_info(s);
+       break;
+    case 0x3c4:
+       s->sr_index = val;
+       break;
+    case 0x3c5:
+#ifdef DEBUG_VGA_REG
+       printf("vga: write SR%x = 0x%02x\n", s->sr_index, val);
+#endif
+       cirrus_vga_write_sr(c, val);
+        break;
+       break;
+    case 0x3c6:
+       cirrus_write_hidden_dac(c, val);
+       break;
+    case 0x3c7:
+       s->dac_read_index = val;
+       s->dac_sub_index = 0;
+       s->dac_state = 3;
+       break;
+    case 0x3c8:
+       s->dac_write_index = val;
+       s->dac_sub_index = 0;
+       s->dac_state = 0;
+       break;
+    case 0x3c9:
+        cirrus_vga_write_palette(c, val);
+        break;
+    case 0x3ce:
+       s->gr_index = val;
+       break;
+    case 0x3cf:
+#ifdef DEBUG_VGA_REG
+       printf("vga: write GR%x = 0x%02x\n", s->gr_index, val);
+#endif
+       cirrus_vga_write_gr(c, s->gr_index, val);
+       break;
+    case 0x3b4:
+    case 0x3d4:
+       s->cr_index = val;
+       break;
+    case 0x3b5:
+    case 0x3d5:
+#ifdef DEBUG_VGA_REG
+       printf("vga: write CR%x = 0x%02x\n", s->cr_index, val);
+#endif
+       cirrus_vga_write_cr(c, val);
+       break;
+    case 0x3ba:
+    case 0x3da:
+       s->fcr = val & 0x10;
+       break;
+    }
+}
+
+/***************************************
+ *
+ *  memory-mapped I/O access
+ *
+ ***************************************/
+
+static uint64_t cirrus_mmio_read(void *opaque, hwaddr addr,
+                                 unsigned size)
+{
+    CirrusVGAState *s = opaque;
+
+    if (addr >= 0x100) {
+        return cirrus_mmio_blt_read(s, addr - 0x100);
+    } else {
+        return cirrus_vga_ioport_read(s, addr + 0x10, size);
+    }
+}
+
+static void cirrus_mmio_write(void *opaque, hwaddr addr,
+                              uint64_t val, unsigned size)
+{
+    CirrusVGAState *s = opaque;
+
+    if (addr >= 0x100) {
+       cirrus_mmio_blt_write(s, addr - 0x100, val);
+    } else {
+        cirrus_vga_ioport_write(s, addr + 0x10, val, size);
+    }
+}
+
+static const MemoryRegionOps cirrus_mmio_io_ops = {
+    .read = cirrus_mmio_read,
+    .write = cirrus_mmio_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+/* load/save state */
+
+static int cirrus_post_load(void *opaque, int version_id)
+{
+    CirrusVGAState *s = opaque;
+
+    s->vga.gr[0x00] = s->cirrus_shadow_gr0 & 0x0f;
+    s->vga.gr[0x01] = s->cirrus_shadow_gr1 & 0x0f;
+
+    cirrus_update_memory_access(s);
+    /* force refresh */
+    s->vga.graphic_mode = -1;
+    cirrus_update_bank_ptr(s, 0);
+    cirrus_update_bank_ptr(s, 1);
+    return 0;
+}
+
+static const VMStateDescription vmstate_cirrus_vga = {
+    .name = "cirrus_vga",
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = cirrus_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT32(vga.latch, CirrusVGAState),
+        VMSTATE_UINT8(vga.sr_index, CirrusVGAState),
+        VMSTATE_BUFFER(vga.sr, CirrusVGAState),
+        VMSTATE_UINT8(vga.gr_index, CirrusVGAState),
+        VMSTATE_UINT8(cirrus_shadow_gr0, CirrusVGAState),
+        VMSTATE_UINT8(cirrus_shadow_gr1, CirrusVGAState),
+        VMSTATE_BUFFER_START_MIDDLE(vga.gr, CirrusVGAState, 2),
+        VMSTATE_UINT8(vga.ar_index, CirrusVGAState),
+        VMSTATE_BUFFER(vga.ar, CirrusVGAState),
+        VMSTATE_INT32(vga.ar_flip_flop, CirrusVGAState),
+        VMSTATE_UINT8(vga.cr_index, CirrusVGAState),
+        VMSTATE_BUFFER(vga.cr, CirrusVGAState),
+        VMSTATE_UINT8(vga.msr, CirrusVGAState),
+        VMSTATE_UINT8(vga.fcr, CirrusVGAState),
+        VMSTATE_UINT8(vga.st00, CirrusVGAState),
+        VMSTATE_UINT8(vga.st01, CirrusVGAState),
+        VMSTATE_UINT8(vga.dac_state, CirrusVGAState),
+        VMSTATE_UINT8(vga.dac_sub_index, CirrusVGAState),
+        VMSTATE_UINT8(vga.dac_read_index, CirrusVGAState),
+        VMSTATE_UINT8(vga.dac_write_index, CirrusVGAState),
+        VMSTATE_BUFFER(vga.dac_cache, CirrusVGAState),
+        VMSTATE_BUFFER(vga.palette, CirrusVGAState),
+        VMSTATE_INT32(vga.bank_offset, CirrusVGAState),
+        VMSTATE_UINT8(cirrus_hidden_dac_lockindex, CirrusVGAState),
+        VMSTATE_UINT8(cirrus_hidden_dac_data, CirrusVGAState),
+        VMSTATE_UINT32(hw_cursor_x, CirrusVGAState),
+        VMSTATE_UINT32(hw_cursor_y, CirrusVGAState),
+        /* XXX: we do not save the bitblt state - we assume we do not save
+           the state when the blitter is active */
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_pci_cirrus_vga = {
+    .name = "cirrus_vga",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields      = (VMStateField []) {
+        VMSTATE_PCI_DEVICE(dev, PCICirrusVGAState),
+        VMSTATE_STRUCT(cirrus_vga, PCICirrusVGAState, 0,
+                       vmstate_cirrus_vga, CirrusVGAState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/***************************************
+ *
+ *  initialize
+ *
+ ***************************************/
+
+static void cirrus_reset(void *opaque)
+{
+    CirrusVGAState *s = opaque;
+
+    vga_common_reset(&s->vga);
+    unmap_linear_vram(s);
+    s->vga.sr[0x06] = 0x0f;
+    if (s->device_id == CIRRUS_ID_CLGD5446) {
+        /* 4MB 64 bit memory config, always PCI */
+        s->vga.sr[0x1F] = 0x2d;                // MemClock
+        s->vga.gr[0x18] = 0x0f;             // fastest memory configuration
+        s->vga.sr[0x0f] = 0x98;
+        s->vga.sr[0x17] = 0x20;
+        s->vga.sr[0x15] = 0x04; /* memory size, 3=2MB, 4=4MB */
+    } else {
+        s->vga.sr[0x1F] = 0x22;                // MemClock
+        s->vga.sr[0x0F] = CIRRUS_MEMSIZE_2M;
+        s->vga.sr[0x17] = s->bustype;
+        s->vga.sr[0x15] = 0x03; /* memory size, 3=2MB, 4=4MB */
+    }
+    s->vga.cr[0x27] = s->device_id;
+
+    s->cirrus_hidden_dac_lockindex = 5;
+    s->cirrus_hidden_dac_data = 0;
+}
+
+static const MemoryRegionOps cirrus_linear_io_ops = {
+    .read = cirrus_linear_read,
+    .write = cirrus_linear_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static const MemoryRegionOps cirrus_vga_io_ops = {
+    .read = cirrus_vga_ioport_read,
+    .write = cirrus_vga_ioport_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci,
+                               MemoryRegion *system_memory,
+                               MemoryRegion *system_io)
+{
+    int i;
+    static int inited;
+
+    if (!inited) {
+        inited = 1;
+        for(i = 0;i < 256; i++)
+            rop_to_index[i] = CIRRUS_ROP_NOP_INDEX; /* nop rop */
+        rop_to_index[CIRRUS_ROP_0] = 0;
+        rop_to_index[CIRRUS_ROP_SRC_AND_DST] = 1;
+        rop_to_index[CIRRUS_ROP_NOP] = 2;
+        rop_to_index[CIRRUS_ROP_SRC_AND_NOTDST] = 3;
+        rop_to_index[CIRRUS_ROP_NOTDST] = 4;
+        rop_to_index[CIRRUS_ROP_SRC] = 5;
+        rop_to_index[CIRRUS_ROP_1] = 6;
+        rop_to_index[CIRRUS_ROP_NOTSRC_AND_DST] = 7;
+        rop_to_index[CIRRUS_ROP_SRC_XOR_DST] = 8;
+        rop_to_index[CIRRUS_ROP_SRC_OR_DST] = 9;
+        rop_to_index[CIRRUS_ROP_NOTSRC_OR_NOTDST] = 10;
+        rop_to_index[CIRRUS_ROP_SRC_NOTXOR_DST] = 11;
+        rop_to_index[CIRRUS_ROP_SRC_OR_NOTDST] = 12;
+        rop_to_index[CIRRUS_ROP_NOTSRC] = 13;
+        rop_to_index[CIRRUS_ROP_NOTSRC_OR_DST] = 14;
+        rop_to_index[CIRRUS_ROP_NOTSRC_AND_NOTDST] = 15;
+        s->device_id = device_id;
+        if (is_pci)
+            s->bustype = CIRRUS_BUSTYPE_PCI;
+        else
+            s->bustype = CIRRUS_BUSTYPE_ISA;
+    }
+
+    /* Register ioport 0x3b0 - 0x3df */
+    memory_region_init_io(&s->cirrus_vga_io, &cirrus_vga_io_ops, s,
+                          "cirrus-io", 0x30);
+    memory_region_add_subregion(system_io, 0x3b0, &s->cirrus_vga_io);
+
+    memory_region_init(&s->low_mem_container,
+                       "cirrus-lowmem-container",
+                       0x20000);
+
+    memory_region_init_io(&s->low_mem, &cirrus_vga_mem_ops, s,
+                          "cirrus-low-memory", 0x20000);
+    memory_region_add_subregion(&s->low_mem_container, 0, &s->low_mem);
+    for (i = 0; i < 2; ++i) {
+        static const char *names[] = { "vga.bank0", "vga.bank1" };
+        MemoryRegion *bank = &s->cirrus_bank[i];
+        memory_region_init_alias(bank, names[i], &s->vga.vram, 0, 0x8000);
+        memory_region_set_enabled(bank, false);
+        memory_region_add_subregion_overlap(&s->low_mem_container, i * 0x8000,
+                                            bank, 1);
+    }
+    memory_region_add_subregion_overlap(system_memory,
+                                        isa_mem_base + 0x000a0000,
+                                        &s->low_mem_container,
+                                        1);
+    memory_region_set_coalescing(&s->low_mem);
+
+    /* I/O handler for LFB */
+    memory_region_init_io(&s->cirrus_linear_io, &cirrus_linear_io_ops, s,
+                          "cirrus-linear-io", s->vga.vram_size_mb
+                                              * 1024 * 1024);
+    memory_region_set_flush_coalesced(&s->cirrus_linear_io);
+
+    /* I/O handler for LFB */
+    memory_region_init_io(&s->cirrus_linear_bitblt_io,
+                          &cirrus_linear_bitblt_io_ops,
+                          s,
+                          "cirrus-bitblt-mmio",
+                          0x400000);
+    memory_region_set_flush_coalesced(&s->cirrus_linear_bitblt_io);
+
+    /* I/O handler for memory-mapped I/O */
+    memory_region_init_io(&s->cirrus_mmio_io, &cirrus_mmio_io_ops, s,
+                          "cirrus-mmio", CIRRUS_PNPMMIO_SIZE);
+    memory_region_set_flush_coalesced(&s->cirrus_mmio_io);
+
+    s->real_vram_size =
+        (s->device_id == CIRRUS_ID_CLGD5446) ? 4096 * 1024 : 2048 * 1024;
+
+    /* XXX: s->vga.vram_size must be a power of two */
+    s->cirrus_addr_mask = s->real_vram_size - 1;
+    s->linear_mmio_mask = s->real_vram_size - 256;
+
+    s->vga.get_bpp = cirrus_get_bpp;
+    s->vga.get_offsets = cirrus_get_offsets;
+    s->vga.get_resolution = cirrus_get_resolution;
+    s->vga.cursor_invalidate = cirrus_cursor_invalidate;
+    s->vga.cursor_draw_line = cirrus_cursor_draw_line;
+
+    qemu_register_reset(cirrus_reset, s);
+}
+
+/***************************************
+ *
+ *  ISA bus support
+ *
+ ***************************************/
+
+static int vga_initfn(ISADevice *dev)
+{
+    ISACirrusVGAState *d = DO_UPCAST(ISACirrusVGAState, dev, dev);
+    VGACommonState *s = &d->cirrus_vga.vga;
+
+    vga_common_init(s);
+    cirrus_init_common(&d->cirrus_vga, CIRRUS_ID_CLGD5430, 0,
+                       isa_address_space(dev), isa_address_space_io(dev));
+    s->con = graphic_console_init(s->update, s->invalidate,
+                                  s->screen_dump, s->text_update,
+                                  s);
+    rom_add_vga(VGABIOS_CIRRUS_FILENAME);
+    /* XXX ISA-LFB support */
+    /* FIXME not qdev yet */
+    return 0;
+}
+
+static Property isa_vga_cirrus_properties[] = {
+    DEFINE_PROP_UINT32("vgamem_mb", struct ISACirrusVGAState,
+                       cirrus_vga.vga.vram_size_mb, 8),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void isa_cirrus_vga_class_init(ObjectClass *klass, void *data)
+{
+    ISADeviceClass *k = ISA_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd  = &vmstate_cirrus_vga;
+    k->init   = vga_initfn;
+    dc->props = isa_vga_cirrus_properties;
+}
+
+static const TypeInfo isa_cirrus_vga_info = {
+    .name          = "isa-cirrus-vga",
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(ISACirrusVGAState),
+    .class_init = isa_cirrus_vga_class_init,
+};
+
+/***************************************
+ *
+ *  PCI bus support
+ *
+ ***************************************/
+
+static int pci_cirrus_vga_initfn(PCIDevice *dev)
+{
+     PCICirrusVGAState *d = DO_UPCAST(PCICirrusVGAState, dev, dev);
+     CirrusVGAState *s = &d->cirrus_vga;
+     PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
+     int16_t device_id = pc->device_id;
+
+     /* setup VGA */
+     vga_common_init(&s->vga);
+     cirrus_init_common(s, device_id, 1, pci_address_space(dev),
+                        pci_address_space_io(dev));
+     s->vga.con = graphic_console_init(s->vga.update, s->vga.invalidate,
+                                       s->vga.screen_dump, s->vga.text_update,
+                                       &s->vga);
+
+     /* setup PCI */
+
+    memory_region_init(&s->pci_bar, "cirrus-pci-bar0", 0x2000000);
+
+    /* XXX: add byte swapping apertures */
+    memory_region_add_subregion(&s->pci_bar, 0, &s->cirrus_linear_io);
+    memory_region_add_subregion(&s->pci_bar, 0x1000000,
+                                &s->cirrus_linear_bitblt_io);
+
+     /* setup memory space */
+     /* memory #0 LFB */
+     /* memory #1 memory-mapped I/O */
+     /* XXX: s->vga.vram_size must be a power of two */
+     pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->pci_bar);
+     if (device_id == CIRRUS_ID_CLGD5446) {
+         pci_register_bar(&d->dev, 1, 0, &s->cirrus_mmio_io);
+     }
+     return 0;
+}
+
+static Property pci_vga_cirrus_properties[] = {
+    DEFINE_PROP_UINT32("vgamem_mb", struct PCICirrusVGAState,
+                       cirrus_vga.vga.vram_size_mb, 8),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void cirrus_vga_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->no_hotplug = 1;
+    k->init = pci_cirrus_vga_initfn;
+    k->romfile = VGABIOS_CIRRUS_FILENAME;
+    k->vendor_id = PCI_VENDOR_ID_CIRRUS;
+    k->device_id = CIRRUS_ID_CLGD5446;
+    k->class_id = PCI_CLASS_DISPLAY_VGA;
+    dc->desc = "Cirrus CLGD 54xx VGA";
+    dc->vmsd = &vmstate_pci_cirrus_vga;
+    dc->props = pci_vga_cirrus_properties;
+}
+
+static const TypeInfo cirrus_vga_info = {
+    .name          = "cirrus-vga",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCICirrusVGAState),
+    .class_init    = cirrus_vga_class_init,
+};
+
+static void cirrus_vga_register_types(void)
+{
+    type_register_static(&isa_cirrus_vga_info);
+    type_register_static(&cirrus_vga_info);
+}
+
+type_init(cirrus_vga_register_types)
diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c
new file mode 100644 (file)
index 0000000..f7014e9
--- /dev/null
@@ -0,0 +1,617 @@
+/*
+ * QEMU G364 framebuffer Emulator.
+ *
+ * Copyright (c) 2007-2011 Herve Poussineau
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "ui/console.h"
+#include "ui/pixel_ops.h"
+#include "trace.h"
+#include "hw/sysbus.h"
+
+typedef struct G364State {
+    /* hardware */
+    uint8_t *vram;
+    uint32_t vram_size;
+    qemu_irq irq;
+    MemoryRegion mem_vram;
+    MemoryRegion mem_ctrl;
+    /* registers */
+    uint8_t color_palette[256][3];
+    uint8_t cursor_palette[3][3];
+    uint16_t cursor[512];
+    uint32_t cursor_position;
+    uint32_t ctla;
+    uint32_t top_of_screen;
+    uint32_t width, height; /* in pixels */
+    /* display refresh support */
+    QemuConsole *con;
+    int depth;
+    int blanked;
+} G364State;
+
+#define REG_BOOT     0x000000
+#define REG_DISPLAY  0x000118
+#define REG_VDISPLAY 0x000150
+#define REG_CTLA     0x000300
+#define REG_TOP      0x000400
+#define REG_CURS_PAL 0x000508
+#define REG_CURS_POS 0x000638
+#define REG_CLR_PAL  0x000800
+#define REG_CURS_PAT 0x001000
+#define REG_RESET    0x100000
+
+#define CTLA_FORCE_BLANK 0x00000400
+#define CTLA_NO_CURSOR   0x00800000
+
+#define G364_PAGE_SIZE 4096
+
+static inline int check_dirty(G364State *s, ram_addr_t page)
+{
+    return memory_region_get_dirty(&s->mem_vram, page, G364_PAGE_SIZE,
+                                   DIRTY_MEMORY_VGA);
+}
+
+static inline void reset_dirty(G364State *s,
+                               ram_addr_t page_min, ram_addr_t page_max)
+{
+    memory_region_reset_dirty(&s->mem_vram,
+                              page_min,
+                              page_max + G364_PAGE_SIZE - page_min - 1,
+                              DIRTY_MEMORY_VGA);
+}
+
+static void g364fb_draw_graphic8(G364State *s)
+{
+    DisplaySurface *surface = qemu_console_surface(s->con);
+    int i, w;
+    uint8_t *vram;
+    uint8_t *data_display, *dd;
+    ram_addr_t page, page_min, page_max;
+    int x, y;
+    int xmin, xmax;
+    int ymin, ymax;
+    int xcursor, ycursor;
+    unsigned int (*rgb_to_pixel)(unsigned int r, unsigned int g, unsigned int b);
+
+    switch (surface_bits_per_pixel(surface)) {
+        case 8:
+            rgb_to_pixel = rgb_to_pixel8;
+            w = 1;
+            break;
+        case 15:
+            rgb_to_pixel = rgb_to_pixel15;
+            w = 2;
+            break;
+        case 16:
+            rgb_to_pixel = rgb_to_pixel16;
+            w = 2;
+            break;
+        case 32:
+            rgb_to_pixel = rgb_to_pixel32;
+            w = 4;
+            break;
+        default:
+            hw_error("g364: unknown host depth %d",
+                     surface_bits_per_pixel(surface));
+            return;
+    }
+
+    page = 0;
+    page_min = (ram_addr_t)-1;
+    page_max = 0;
+
+    x = y = 0;
+    xmin = s->width;
+    xmax = 0;
+    ymin = s->height;
+    ymax = 0;
+
+    if (!(s->ctla & CTLA_NO_CURSOR)) {
+        xcursor = s->cursor_position >> 12;
+        ycursor = s->cursor_position & 0xfff;
+    } else {
+        xcursor = ycursor = -65;
+    }
+
+    vram = s->vram + s->top_of_screen;
+    /* XXX: out of range in vram? */
+    data_display = dd = surface_data(surface);
+    while (y < s->height) {
+        if (check_dirty(s, page)) {
+            if (y < ymin)
+                ymin = ymax = y;
+            if (page_min == (ram_addr_t)-1)
+                page_min = page;
+            page_max = page;
+            if (x < xmin)
+                xmin = x;
+            for (i = 0; i < G364_PAGE_SIZE; i++) {
+                uint8_t index;
+                unsigned int color;
+                if (unlikely((y >= ycursor && y < ycursor + 64) &&
+                    (x >= xcursor && x < xcursor + 64))) {
+                    /* pointer area */
+                    int xdiff = x - xcursor;
+                    uint16_t curs = s->cursor[(y - ycursor) * 8 + xdiff / 8];
+                    int op = (curs >> ((xdiff & 7) * 2)) & 3;
+                    if (likely(op == 0)) {
+                        /* transparent */
+                        index = *vram;
+                        color = (*rgb_to_pixel)(
+                            s->color_palette[index][0],
+                            s->color_palette[index][1],
+                            s->color_palette[index][2]);
+                    } else {
+                        /* get cursor color */
+                        index = op - 1;
+                        color = (*rgb_to_pixel)(
+                            s->cursor_palette[index][0],
+                            s->cursor_palette[index][1],
+                            s->cursor_palette[index][2]);
+                    }
+                } else {
+                    /* normal area */
+                    index = *vram;
+                    color = (*rgb_to_pixel)(
+                        s->color_palette[index][0],
+                        s->color_palette[index][1],
+                        s->color_palette[index][2]);
+                }
+                memcpy(dd, &color, w);
+                dd += w;
+                x++;
+                vram++;
+                if (x == s->width) {
+                    xmax = s->width - 1;
+                    y++;
+                    if (y == s->height) {
+                        ymax = s->height - 1;
+                        goto done;
+                    }
+                    data_display = dd = data_display + surface_stride(surface);
+                    xmin = 0;
+                    x = 0;
+                }
+            }
+            if (x > xmax)
+                xmax = x;
+            if (y > ymax)
+                ymax = y;
+        } else {
+            int dy;
+            if (page_min != (ram_addr_t)-1) {
+                reset_dirty(s, page_min, page_max);
+                page_min = (ram_addr_t)-1;
+                page_max = 0;
+                dpy_gfx_update(s->con, xmin, ymin,
+                               xmax - xmin + 1, ymax - ymin + 1);
+                xmin = s->width;
+                xmax = 0;
+                ymin = s->height;
+                ymax = 0;
+            }
+            x += G364_PAGE_SIZE;
+            dy = x / s->width;
+            x = x % s->width;
+            y += dy;
+            vram += G364_PAGE_SIZE;
+            data_display += dy * surface_stride(surface);
+            dd = data_display + x * w;
+        }
+        page += G364_PAGE_SIZE;
+    }
+
+done:
+    if (page_min != (ram_addr_t)-1) {
+        dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
+        reset_dirty(s, page_min, page_max);
+    }
+}
+
+static void g364fb_draw_blank(G364State *s)
+{
+    DisplaySurface *surface = qemu_console_surface(s->con);
+    int i, w;
+    uint8_t *d;
+
+    if (s->blanked) {
+        /* Screen is already blank. No need to redraw it */
+        return;
+    }
+
+    w = s->width * surface_bytes_per_pixel(surface);
+    d = surface_data(surface);
+    for (i = 0; i < s->height; i++) {
+        memset(d, 0, w);
+        d += surface_stride(surface);
+    }
+
+    dpy_gfx_update(s->con, 0, 0, s->width, s->height);
+    s->blanked = 1;
+}
+
+static void g364fb_update_display(void *opaque)
+{
+    G364State *s = opaque;
+    DisplaySurface *surface = qemu_console_surface(s->con);
+
+    qemu_flush_coalesced_mmio_buffer();
+
+    if (s->width == 0 || s->height == 0)
+        return;
+
+    if (s->width != surface_width(surface) ||
+        s->height != surface_height(surface)) {
+        qemu_console_resize(s->con, s->width, s->height);
+    }
+
+    if (s->ctla & CTLA_FORCE_BLANK) {
+        g364fb_draw_blank(s);
+    } else if (s->depth == 8) {
+        g364fb_draw_graphic8(s);
+    } else {
+        error_report("g364: unknown guest depth %d", s->depth);
+    }
+
+    qemu_irq_raise(s->irq);
+}
+
+static inline void g364fb_invalidate_display(void *opaque)
+{
+    G364State *s = opaque;
+
+    s->blanked = 0;
+    memory_region_set_dirty(&s->mem_vram, 0, s->vram_size);
+}
+
+static void g364fb_reset(G364State *s)
+{
+    qemu_irq_lower(s->irq);
+
+    memset(s->color_palette, 0, sizeof(s->color_palette));
+    memset(s->cursor_palette, 0, sizeof(s->cursor_palette));
+    memset(s->cursor, 0, sizeof(s->cursor));
+    s->cursor_position = 0;
+    s->ctla = 0;
+    s->top_of_screen = 0;
+    s->width = s->height = 0;
+    memset(s->vram, 0, s->vram_size);
+    g364fb_invalidate_display(s);
+}
+
+static void g364fb_screen_dump(void *opaque, const char *filename, bool cswitch,
+                               Error **errp)
+{
+    G364State *s = opaque;
+    int ret, y, x;
+    uint8_t index;
+    uint8_t *data_buffer;
+    FILE *f;
+
+    qemu_flush_coalesced_mmio_buffer();
+
+    if (s->depth != 8) {
+        error_setg(errp, "g364: unknown guest depth %d", s->depth);
+        return;
+    }
+
+    f = fopen(filename, "wb");
+    if (!f) {
+        error_setg(errp, "failed to open file '%s': %s", filename,
+                   strerror(errno));
+        return;
+    }
+
+    if (s->ctla & CTLA_FORCE_BLANK) {
+        /* blank screen */
+        ret = fprintf(f, "P4\n%d %d\n", s->width, s->height);
+        if (ret < 0) {
+            goto write_err;
+        }
+        for (y = 0; y < s->height; y++)
+            for (x = 0; x < s->width; x++) {
+                ret = fputc(0, f);
+                if (ret == EOF) {
+                    goto write_err;
+                }
+            }
+    } else {
+        data_buffer = s->vram + s->top_of_screen;
+        ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
+        if (ret < 0) {
+            goto write_err;
+        }
+        for (y = 0; y < s->height; y++)
+            for (x = 0; x < s->width; x++, data_buffer++) {
+                index = *data_buffer;
+                ret = fputc(s->color_palette[index][0], f);
+                if (ret == EOF) {
+                    goto write_err;
+                }
+                ret = fputc(s->color_palette[index][1], f);
+                if (ret == EOF) {
+                    goto write_err;
+                }
+                ret = fputc(s->color_palette[index][2], f);
+                if (ret == EOF) {
+                    goto write_err;
+                }
+        }
+    }
+
+out:
+    fclose(f);
+    return;
+
+write_err:
+    error_setg(errp, "failed to write to file '%s': %s", filename,
+               strerror(errno));
+    unlink(filename);
+    goto out;
+}
+
+/* called for accesses to io ports */
+static uint64_t g364fb_ctrl_read(void *opaque,
+                                 hwaddr addr,
+                                 unsigned int size)
+{
+    G364State *s = opaque;
+    uint32_t val;
+
+    if (addr >= REG_CURS_PAT && addr < REG_CURS_PAT + 0x1000) {
+        /* cursor pattern */
+        int idx = (addr - REG_CURS_PAT) >> 3;
+        val = s->cursor[idx];
+    } else if (addr >= REG_CURS_PAL && addr < REG_CURS_PAL + 0x18) {
+        /* cursor palette */
+        int idx = (addr - REG_CURS_PAL) >> 3;
+        val = ((uint32_t)s->cursor_palette[idx][0] << 16);
+        val |= ((uint32_t)s->cursor_palette[idx][1] << 8);
+        val |= ((uint32_t)s->cursor_palette[idx][2] << 0);
+    } else {
+        switch (addr) {
+            case REG_DISPLAY:
+                val = s->width / 4;
+                break;
+            case REG_VDISPLAY:
+                val = s->height * 2;
+                break;
+            case REG_CTLA:
+                val = s->ctla;
+                break;
+            default:
+            {
+                error_report("g364: invalid read at [" TARGET_FMT_plx "]",
+                             addr);
+                val = 0;
+                break;
+            }
+        }
+    }
+
+    trace_g364fb_read(addr, val);
+
+    return val;
+}
+
+static void g364fb_update_depth(G364State *s)
+{
+    static const int depths[8] = { 1, 2, 4, 8, 15, 16, 0 };
+    s->depth = depths[(s->ctla & 0x00700000) >> 20];
+}
+
+static void g364_invalidate_cursor_position(G364State *s)
+{
+    DisplaySurface *surface = qemu_console_surface(s->con);
+    int ymin, ymax, start, end;
+
+    /* invalidate only near the cursor */
+    ymin = s->cursor_position & 0xfff;
+    ymax = MIN(s->height, ymin + 64);
+    start = ymin * surface_stride(surface);
+    end = (ymax + 1) * surface_stride(surface);
+
+    memory_region_set_dirty(&s->mem_vram, start, end - start);
+}
+
+static void g364fb_ctrl_write(void *opaque,
+                              hwaddr addr,
+                              uint64_t val,
+                              unsigned int size)
+{
+    G364State *s = opaque;
+
+    trace_g364fb_write(addr, val);
+
+    if (addr >= REG_CLR_PAL && addr < REG_CLR_PAL + 0x800) {
+        /* color palette */
+        int idx = (addr - REG_CLR_PAL) >> 3;
+        s->color_palette[idx][0] = (val >> 16) & 0xff;
+        s->color_palette[idx][1] = (val >> 8) & 0xff;
+        s->color_palette[idx][2] = val & 0xff;
+        g364fb_invalidate_display(s);
+    } else if (addr >= REG_CURS_PAT && addr < REG_CURS_PAT + 0x1000) {
+        /* cursor pattern */
+        int idx = (addr - REG_CURS_PAT) >> 3;
+        s->cursor[idx] = val;
+        g364fb_invalidate_display(s);
+    } else if (addr >= REG_CURS_PAL && addr < REG_CURS_PAL + 0x18) {
+        /* cursor palette */
+        int idx = (addr - REG_CURS_PAL) >> 3;
+        s->cursor_palette[idx][0] = (val >> 16) & 0xff;
+        s->cursor_palette[idx][1] = (val >> 8) & 0xff;
+        s->cursor_palette[idx][2] = val & 0xff;
+        g364fb_invalidate_display(s);
+    } else {
+        switch (addr) {
+        case REG_BOOT: /* Boot timing */
+        case 0x00108: /* Line timing: half sync */
+        case 0x00110: /* Line timing: back porch */
+        case 0x00120: /* Line timing: short display */
+        case 0x00128: /* Frame timing: broad pulse */
+        case 0x00130: /* Frame timing: v sync */
+        case 0x00138: /* Frame timing: v preequalise */
+        case 0x00140: /* Frame timing: v postequalise */
+        case 0x00148: /* Frame timing: v blank */
+        case 0x00158: /* Line timing: line time */
+        case 0x00160: /* Frame store: line start */
+        case 0x00168: /* vram cycle: mem init */
+        case 0x00170: /* vram cycle: transfer delay */
+        case 0x00200: /* vram cycle: mask register */
+            /* ignore */
+            break;
+        case REG_TOP:
+            s->top_of_screen = val;
+            g364fb_invalidate_display(s);
+            break;
+        case REG_DISPLAY:
+            s->width = val * 4;
+            break;
+        case REG_VDISPLAY:
+            s->height = val / 2;
+            break;
+        case REG_CTLA:
+            s->ctla = val;
+            g364fb_update_depth(s);
+            g364fb_invalidate_display(s);
+            break;
+        case REG_CURS_POS:
+            g364_invalidate_cursor_position(s);
+            s->cursor_position = val;
+            g364_invalidate_cursor_position(s);
+            break;
+        case REG_RESET:
+            g364fb_reset(s);
+            break;
+        default:
+            error_report("g364: invalid write of 0x%" PRIx64
+                         " at [" TARGET_FMT_plx "]", val, addr);
+            break;
+        }
+    }
+    qemu_irq_lower(s->irq);
+}
+
+static const MemoryRegionOps g364fb_ctrl_ops = {
+    .read = g364fb_ctrl_read,
+    .write = g364fb_ctrl_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl.min_access_size = 4,
+    .impl.max_access_size = 4,
+};
+
+static int g364fb_post_load(void *opaque, int version_id)
+{
+    G364State *s = opaque;
+
+    /* force refresh */
+    g364fb_update_depth(s);
+    g364fb_invalidate_display(s);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_g364fb = {
+    .name = "g364fb",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = g364fb_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_VBUFFER_UINT32(vram, G364State, 1, NULL, 0, vram_size),
+        VMSTATE_BUFFER_UNSAFE(color_palette, G364State, 0, 256 * 3),
+        VMSTATE_BUFFER_UNSAFE(cursor_palette, G364State, 0, 9),
+        VMSTATE_UINT16_ARRAY(cursor, G364State, 512),
+        VMSTATE_UINT32(cursor_position, G364State),
+        VMSTATE_UINT32(ctla, G364State),
+        VMSTATE_UINT32(top_of_screen, G364State),
+        VMSTATE_UINT32(width, G364State),
+        VMSTATE_UINT32(height, G364State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void g364fb_init(DeviceState *dev, G364State *s)
+{
+    s->vram = g_malloc0(s->vram_size);
+
+    s->con = graphic_console_init(g364fb_update_display,
+                                  g364fb_invalidate_display,
+                                  g364fb_screen_dump, NULL, s);
+
+    memory_region_init_io(&s->mem_ctrl, &g364fb_ctrl_ops, s, "ctrl", 0x180000);
+    memory_region_init_ram_ptr(&s->mem_vram, "vram",
+                               s->vram_size, s->vram);
+    vmstate_register_ram(&s->mem_vram, dev);
+    memory_region_set_coalescing(&s->mem_vram);
+}
+
+typedef struct {
+    SysBusDevice busdev;
+    G364State g364;
+} G364SysBusState;
+
+static int g364fb_sysbus_init(SysBusDevice *dev)
+{
+    G364State *s = &FROM_SYSBUS(G364SysBusState, dev)->g364;
+
+    g364fb_init(&dev->qdev, s);
+    sysbus_init_irq(dev, &s->irq);
+    sysbus_init_mmio(dev, &s->mem_ctrl);
+    sysbus_init_mmio(dev, &s->mem_vram);
+
+    return 0;
+}
+
+static void g364fb_sysbus_reset(DeviceState *d)
+{
+    G364SysBusState *s = DO_UPCAST(G364SysBusState, busdev.qdev, d);
+    g364fb_reset(&s->g364);
+}
+
+static Property g364fb_sysbus_properties[] = {
+    DEFINE_PROP_HEX32("vram_size", G364SysBusState, g364.vram_size,
+    8 * 1024 * 1024),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void g364fb_sysbus_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = g364fb_sysbus_init;
+    dc->desc = "G364 framebuffer";
+    dc->reset = g364fb_sysbus_reset;
+    dc->vmsd = &vmstate_g364fb;
+    dc->props = g364fb_sysbus_properties;
+}
+
+static const TypeInfo g364fb_sysbus_info = {
+    .name          = "sysbus-g364",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(G364SysBusState),
+    .class_init    = g364fb_sysbus_class_init,
+};
+
+static void g364fb_register_types(void)
+{
+    type_register_static(&g364fb_sysbus_info);
+}
+
+type_init(g364fb_register_types)
diff --git a/hw/display/jazz_led.c b/hw/display/jazz_led.c
new file mode 100644 (file)
index 0000000..05528c7
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * QEMU JAZZ LED emulator.
+ *
+ * Copyright (c) 2007-2012 Herve Poussineau
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "ui/console.h"
+#include "ui/pixel_ops.h"
+#include "trace.h"
+#include "hw/sysbus.h"
+
+typedef enum {
+    REDRAW_NONE = 0, REDRAW_SEGMENTS = 1, REDRAW_BACKGROUND = 2,
+} screen_state_t;
+
+typedef struct LedState {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    uint8_t segments;
+    QemuConsole *con;
+    screen_state_t state;
+} LedState;
+
+static uint64_t jazz_led_read(void *opaque, hwaddr addr,
+                              unsigned int size)
+{
+    LedState *s = opaque;
+    uint8_t val;
+
+    val = s->segments;
+    trace_jazz_led_read(addr, val);
+
+    return val;
+}
+
+static void jazz_led_write(void *opaque, hwaddr addr,
+                           uint64_t val, unsigned int size)
+{
+    LedState *s = opaque;
+    uint8_t new_val = val & 0xff;
+
+    trace_jazz_led_write(addr, new_val);
+
+    s->segments = new_val;
+    s->state |= REDRAW_SEGMENTS;
+}
+
+static const MemoryRegionOps led_ops = {
+    .read = jazz_led_read,
+    .write = jazz_led_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 1,
+};
+
+/***********************************************************/
+/* jazz_led display */
+
+static void draw_horizontal_line(DisplaySurface *ds,
+                                 int posy, int posx1, int posx2,
+                                 uint32_t color)
+{
+    uint8_t *d;
+    int x, bpp;
+
+    bpp = (surface_bits_per_pixel(ds) + 7) >> 3;
+    d = surface_data(ds) + surface_stride(ds) * posy + bpp * posx1;
+    switch(bpp) {
+        case 1:
+            for (x = posx1; x <= posx2; x++) {
+                *((uint8_t *)d) = color;
+                d++;
+            }
+            break;
+        case 2:
+            for (x = posx1; x <= posx2; x++) {
+                *((uint16_t *)d) = color;
+                d += 2;
+            }
+            break;
+        case 4:
+            for (x = posx1; x <= posx2; x++) {
+                *((uint32_t *)d) = color;
+                d += 4;
+            }
+            break;
+    }
+}
+
+static void draw_vertical_line(DisplaySurface *ds,
+                               int posx, int posy1, int posy2,
+                               uint32_t color)
+{
+    uint8_t *d;
+    int y, bpp;
+
+    bpp = (surface_bits_per_pixel(ds) + 7) >> 3;
+    d = surface_data(ds) + surface_stride(ds) * posy1 + bpp * posx;
+    switch(bpp) {
+        case 1:
+            for (y = posy1; y <= posy2; y++) {
+                *((uint8_t *)d) = color;
+                d += surface_stride(ds);
+            }
+            break;
+        case 2:
+            for (y = posy1; y <= posy2; y++) {
+                *((uint16_t *)d) = color;
+                d += surface_stride(ds);
+            }
+            break;
+        case 4:
+            for (y = posy1; y <= posy2; y++) {
+                *((uint32_t *)d) = color;
+                d += surface_stride(ds);
+            }
+            break;
+    }
+}
+
+static void jazz_led_update_display(void *opaque)
+{
+    LedState *s = opaque;
+    DisplaySurface *surface = qemu_console_surface(s->con);
+    uint8_t *d1;
+    uint32_t color_segment, color_led;
+    int y, bpp;
+
+    if (s->state & REDRAW_BACKGROUND) {
+        /* clear screen */
+        bpp = (surface_bits_per_pixel(surface) + 7) >> 3;
+        d1 = surface_data(surface);
+        for (y = 0; y < surface_height(surface); y++) {
+            memset(d1, 0x00, surface_width(surface) * bpp);
+            d1 += surface_stride(surface);
+        }
+    }
+
+    if (s->state & REDRAW_SEGMENTS) {
+        /* set colors according to bpp */
+        switch (surface_bits_per_pixel(surface)) {
+            case 8:
+                color_segment = rgb_to_pixel8(0xaa, 0xaa, 0xaa);
+                color_led = rgb_to_pixel8(0x00, 0xff, 0x00);
+                break;
+            case 15:
+                color_segment = rgb_to_pixel15(0xaa, 0xaa, 0xaa);
+                color_led = rgb_to_pixel15(0x00, 0xff, 0x00);
+                break;
+            case 16:
+                color_segment = rgb_to_pixel16(0xaa, 0xaa, 0xaa);
+                color_led = rgb_to_pixel16(0x00, 0xff, 0x00);
+            case 24:
+                color_segment = rgb_to_pixel24(0xaa, 0xaa, 0xaa);
+                color_led = rgb_to_pixel24(0x00, 0xff, 0x00);
+                break;
+            case 32:
+                color_segment = rgb_to_pixel32(0xaa, 0xaa, 0xaa);
+                color_led = rgb_to_pixel32(0x00, 0xff, 0x00);
+                break;
+            default:
+                return;
+        }
+
+        /* display segments */
+        draw_horizontal_line(surface, 40, 10, 40,
+                             (s->segments & 0x02) ? color_segment : 0);
+        draw_vertical_line(surface, 10, 10, 40,
+                           (s->segments & 0x04) ? color_segment : 0);
+        draw_vertical_line(surface, 10, 40, 70,
+                           (s->segments & 0x08) ? color_segment : 0);
+        draw_horizontal_line(surface, 70, 10, 40,
+                             (s->segments & 0x10) ? color_segment : 0);
+        draw_vertical_line(surface, 40, 40, 70,
+                           (s->segments & 0x20) ? color_segment : 0);
+        draw_vertical_line(surface, 40, 10, 40,
+                           (s->segments & 0x40) ? color_segment : 0);
+        draw_horizontal_line(surface, 10, 10, 40,
+                             (s->segments & 0x80) ? color_segment : 0);
+
+        /* display led */
+        if (!(s->segments & 0x01))
+            color_led = 0; /* black */
+        draw_horizontal_line(surface, 68, 50, 50, color_led);
+        draw_horizontal_line(surface, 69, 49, 51, color_led);
+        draw_horizontal_line(surface, 70, 48, 52, color_led);
+        draw_horizontal_line(surface, 71, 49, 51, color_led);
+        draw_horizontal_line(surface, 72, 50, 50, color_led);
+    }
+
+    s->state = REDRAW_NONE;
+    dpy_gfx_update(s->con, 0, 0,
+                   surface_width(surface), surface_height(surface));
+}
+
+static void jazz_led_invalidate_display(void *opaque)
+{
+    LedState *s = opaque;
+    s->state |= REDRAW_SEGMENTS | REDRAW_BACKGROUND;
+}
+
+static void jazz_led_text_update(void *opaque, console_ch_t *chardata)
+{
+    LedState *s = opaque;
+    char buf[2];
+
+    dpy_text_cursor(s->con, -1, -1);
+    qemu_console_resize(s->con, 2, 1);
+
+    /* TODO: draw the segments */
+    snprintf(buf, 2, "%02hhx\n", s->segments);
+    console_write_ch(chardata++, 0x00200100 | buf[0]);
+    console_write_ch(chardata++, 0x00200100 | buf[1]);
+
+    dpy_text_update(s->con, 0, 0, 2, 1);
+}
+
+static int jazz_led_post_load(void *opaque, int version_id)
+{
+    /* force refresh */
+    jazz_led_invalidate_display(opaque);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_jazz_led = {
+    .name = "jazz-led",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .post_load = jazz_led_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(segments, LedState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int jazz_led_init(SysBusDevice *dev)
+{
+    LedState *s = FROM_SYSBUS(LedState, dev);
+
+    memory_region_init_io(&s->iomem, &led_ops, s, "led", 1);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    s->con = graphic_console_init(jazz_led_update_display,
+                                  jazz_led_invalidate_display,
+                                  NULL,
+                                  jazz_led_text_update, s);
+
+    return 0;
+}
+
+static void jazz_led_reset(DeviceState *d)
+{
+    LedState *s = DO_UPCAST(LedState, busdev.qdev, d);
+
+    s->segments = 0;
+    s->state = REDRAW_SEGMENTS | REDRAW_BACKGROUND;
+    qemu_console_resize(s->con, 60, 80);
+}
+
+static void jazz_led_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = jazz_led_init;
+    dc->desc = "Jazz LED display",
+    dc->vmsd = &vmstate_jazz_led;
+    dc->reset = jazz_led_reset;
+}
+
+static const TypeInfo jazz_led_info = {
+    .name          = "jazz-led",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(LedState),
+    .class_init    = jazz_led_class_init,
+};
+
+static void jazz_led_register(void)
+{
+    type_register_static(&jazz_led_info);
+}
+
+type_init(jazz_led_register);
diff --git a/hw/display/pl110.c b/hw/display/pl110.c
new file mode 100644 (file)
index 0000000..fbef675
--- /dev/null
@@ -0,0 +1,533 @@
+/*
+ * Arm PrimeCell PL110 Color LCD Controller
+ *
+ * Copyright (c) 2005-2009 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GNU LGPL
+ */
+
+#include "hw/sysbus.h"
+#include "ui/console.h"
+#include "hw/framebuffer.h"
+#include "ui/pixel_ops.h"
+
+#define PL110_CR_EN   0x001
+#define PL110_CR_BGR  0x100
+#define PL110_CR_BEBO 0x200
+#define PL110_CR_BEPO 0x400
+#define PL110_CR_PWR  0x800
+
+enum pl110_bppmode
+{
+    BPP_1,
+    BPP_2,
+    BPP_4,
+    BPP_8,
+    BPP_16,
+    BPP_32,
+    BPP_16_565, /* PL111 only */
+    BPP_12      /* PL111 only */
+};
+
+
+/* The Versatile/PB uses a slightly modified PL110 controller.  */
+enum pl110_version
+{
+    PL110,
+    PL110_VERSATILE,
+    PL111
+};
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    QemuConsole *con;
+
+    int version;
+    uint32_t timing[4];
+    uint32_t cr;
+    uint32_t upbase;
+    uint32_t lpbase;
+    uint32_t int_status;
+    uint32_t int_mask;
+    int cols;
+    int rows;
+    enum pl110_bppmode bpp;
+    int invalidate;
+    uint32_t mux_ctrl;
+    uint32_t palette[256];
+    uint32_t raw_palette[128];
+    qemu_irq irq;
+} pl110_state;
+
+static int vmstate_pl110_post_load(void *opaque, int version_id);
+
+static const VMStateDescription vmstate_pl110 = {
+    .name = "pl110",
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .post_load = vmstate_pl110_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_INT32(version, pl110_state),
+        VMSTATE_UINT32_ARRAY(timing, pl110_state, 4),
+        VMSTATE_UINT32(cr, pl110_state),
+        VMSTATE_UINT32(upbase, pl110_state),
+        VMSTATE_UINT32(lpbase, pl110_state),
+        VMSTATE_UINT32(int_status, pl110_state),
+        VMSTATE_UINT32(int_mask, pl110_state),
+        VMSTATE_INT32(cols, pl110_state),
+        VMSTATE_INT32(rows, pl110_state),
+        VMSTATE_UINT32(bpp, pl110_state),
+        VMSTATE_INT32(invalidate, pl110_state),
+        VMSTATE_UINT32_ARRAY(palette, pl110_state, 256),
+        VMSTATE_UINT32_ARRAY(raw_palette, pl110_state, 128),
+        VMSTATE_UINT32_V(mux_ctrl, pl110_state, 2),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const unsigned char pl110_id[] =
+{ 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+
+/* The Arm documentation (DDI0224C) says the CLDC on the Versatile board
+   has a different ID.  However Linux only looks for the normal ID.  */
+#if 0
+static const unsigned char pl110_versatile_id[] =
+{ 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+#else
+#define pl110_versatile_id pl110_id
+#endif
+
+static const unsigned char pl111_id[] = {
+    0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1
+};
+
+/* Indexed by pl110_version */
+static const unsigned char *idregs[] = {
+    pl110_id,
+    pl110_versatile_id,
+    pl111_id
+};
+
+#define BITS 8
+#include "hw/pl110_template.h"
+#define BITS 15
+#include "hw/pl110_template.h"
+#define BITS 16
+#include "hw/pl110_template.h"
+#define BITS 24
+#include "hw/pl110_template.h"
+#define BITS 32
+#include "hw/pl110_template.h"
+
+static int pl110_enabled(pl110_state *s)
+{
+  return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
+}
+
+static void pl110_update_display(void *opaque)
+{
+    pl110_state *s = (pl110_state *)opaque;
+    DisplaySurface *surface = qemu_console_surface(s->con);
+    drawfn* fntable;
+    drawfn fn;
+    int dest_width;
+    int src_width;
+    int bpp_offset;
+    int first;
+    int last;
+
+    if (!pl110_enabled(s))
+        return;
+
+    switch (surface_bits_per_pixel(surface)) {
+    case 0:
+        return;
+    case 8:
+        fntable = pl110_draw_fn_8;
+        dest_width = 1;
+        break;
+    case 15:
+        fntable = pl110_draw_fn_15;
+        dest_width = 2;
+        break;
+    case 16:
+        fntable = pl110_draw_fn_16;
+        dest_width = 2;
+        break;
+    case 24:
+        fntable = pl110_draw_fn_24;
+        dest_width = 3;
+        break;
+    case 32:
+        fntable = pl110_draw_fn_32;
+        dest_width = 4;
+        break;
+    default:
+        fprintf(stderr, "pl110: Bad color depth\n");
+        exit(1);
+    }
+    if (s->cr & PL110_CR_BGR)
+        bpp_offset = 0;
+    else
+        bpp_offset = 24;
+
+    if ((s->version != PL111) && (s->bpp == BPP_16)) {
+        /* The PL110's native 16 bit mode is 5551; however
+         * most boards with a PL110 implement an external
+         * mux which allows bits to be reshuffled to give
+         * 565 format. The mux is typically controlled by
+         * an external system register.
+         * This is controlled by a GPIO input pin
+         * so boards can wire it up to their register.
+         *
+         * The PL111 straightforwardly implements both
+         * 5551 and 565 under control of the bpp field
+         * in the LCDControl register.
+         */
+        switch (s->mux_ctrl) {
+        case 3: /* 565 BGR */
+            bpp_offset = (BPP_16_565 - BPP_16);
+            break;
+        case 1: /* 5551 */
+            break;
+        case 0: /* 888; also if we have loaded vmstate from an old version */
+        case 2: /* 565 RGB */
+        default:
+            /* treat as 565 but honour BGR bit */
+            bpp_offset += (BPP_16_565 - BPP_16);
+            break;
+        }
+    }
+
+    if (s->cr & PL110_CR_BEBO)
+        fn = fntable[s->bpp + 8 + bpp_offset];
+    else if (s->cr & PL110_CR_BEPO)
+        fn = fntable[s->bpp + 16 + bpp_offset];
+    else
+        fn = fntable[s->bpp + bpp_offset];
+
+    src_width = s->cols;
+    switch (s->bpp) {
+    case BPP_1:
+        src_width >>= 3;
+        break;
+    case BPP_2:
+        src_width >>= 2;
+        break;
+    case BPP_4:
+        src_width >>= 1;
+        break;
+    case BPP_8:
+        break;
+    case BPP_16:
+    case BPP_16_565:
+    case BPP_12:
+        src_width <<= 1;
+        break;
+    case BPP_32:
+        src_width <<= 2;
+        break;
+    }
+    dest_width *= s->cols;
+    first = 0;
+    framebuffer_update_display(surface, sysbus_address_space(&s->busdev),
+                               s->upbase, s->cols, s->rows,
+                               src_width, dest_width, 0,
+                               s->invalidate,
+                               fn, s->palette,
+                               &first, &last);
+    if (first >= 0) {
+        dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1);
+    }
+    s->invalidate = 0;
+}
+
+static void pl110_invalidate_display(void * opaque)
+{
+    pl110_state *s = (pl110_state *)opaque;
+    s->invalidate = 1;
+    if (pl110_enabled(s)) {
+        qemu_console_resize(s->con, s->cols, s->rows);
+    }
+}
+
+static void pl110_update_palette(pl110_state *s, int n)
+{
+    DisplaySurface *surface = qemu_console_surface(s->con);
+    int i;
+    uint32_t raw;
+    unsigned int r, g, b;
+
+    raw = s->raw_palette[n];
+    n <<= 1;
+    for (i = 0; i < 2; i++) {
+        r = (raw & 0x1f) << 3;
+        raw >>= 5;
+        g = (raw & 0x1f) << 3;
+        raw >>= 5;
+        b = (raw & 0x1f) << 3;
+        /* The I bit is ignored.  */
+        raw >>= 6;
+        switch (surface_bits_per_pixel(surface)) {
+        case 8:
+            s->palette[n] = rgb_to_pixel8(r, g, b);
+            break;
+        case 15:
+            s->palette[n] = rgb_to_pixel15(r, g, b);
+            break;
+        case 16:
+            s->palette[n] = rgb_to_pixel16(r, g, b);
+            break;
+        case 24:
+        case 32:
+            s->palette[n] = rgb_to_pixel32(r, g, b);
+            break;
+        }
+        n++;
+    }
+}
+
+static void pl110_resize(pl110_state *s, int width, int height)
+{
+    if (width != s->cols || height != s->rows) {
+        if (pl110_enabled(s)) {
+            qemu_console_resize(s->con, width, height);
+        }
+    }
+    s->cols = width;
+    s->rows = height;
+}
+
+/* Update interrupts.  */
+static void pl110_update(pl110_state *s)
+{
+  /* TODO: Implement interrupts.  */
+}
+
+static uint64_t pl110_read(void *opaque, hwaddr offset,
+                           unsigned size)
+{
+    pl110_state *s = (pl110_state *)opaque;
+
+    if (offset >= 0xfe0 && offset < 0x1000) {
+        return idregs[s->version][(offset - 0xfe0) >> 2];
+    }
+    if (offset >= 0x200 && offset < 0x400) {
+        return s->raw_palette[(offset - 0x200) >> 2];
+    }
+    switch (offset >> 2) {
+    case 0: /* LCDTiming0 */
+        return s->timing[0];
+    case 1: /* LCDTiming1 */
+        return s->timing[1];
+    case 2: /* LCDTiming2 */
+        return s->timing[2];
+    case 3: /* LCDTiming3 */
+        return s->timing[3];
+    case 4: /* LCDUPBASE */
+        return s->upbase;
+    case 5: /* LCDLPBASE */
+        return s->lpbase;
+    case 6: /* LCDIMSC */
+        if (s->version != PL110) {
+            return s->cr;
+        }
+        return s->int_mask;
+    case 7: /* LCDControl */
+        if (s->version != PL110) {
+            return s->int_mask;
+        }
+        return s->cr;
+    case 8: /* LCDRIS */
+        return s->int_status;
+    case 9: /* LCDMIS */
+        return s->int_status & s->int_mask;
+    case 11: /* LCDUPCURR */
+        /* TODO: Implement vertical refresh.  */
+        return s->upbase;
+    case 12: /* LCDLPCURR */
+        return s->lpbase;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl110_read: Bad offset %x\n", (int)offset);
+        return 0;
+    }
+}
+
+static void pl110_write(void *opaque, hwaddr offset,
+                        uint64_t val, unsigned size)
+{
+    pl110_state *s = (pl110_state *)opaque;
+    int n;
+
+    /* For simplicity invalidate the display whenever a control register
+       is written to.  */
+    s->invalidate = 1;
+    if (offset >= 0x200 && offset < 0x400) {
+        /* Palette.  */
+        n = (offset - 0x200) >> 2;
+        s->raw_palette[(offset - 0x200) >> 2] = val;
+        pl110_update_palette(s, n);
+        return;
+    }
+    switch (offset >> 2) {
+    case 0: /* LCDTiming0 */
+        s->timing[0] = val;
+        n = ((val & 0xfc) + 4) * 4;
+        pl110_resize(s, n, s->rows);
+        break;
+    case 1: /* LCDTiming1 */
+        s->timing[1] = val;
+        n = (val & 0x3ff) + 1;
+        pl110_resize(s, s->cols, n);
+        break;
+    case 2: /* LCDTiming2 */
+        s->timing[2] = val;
+        break;
+    case 3: /* LCDTiming3 */
+        s->timing[3] = val;
+        break;
+    case 4: /* LCDUPBASE */
+        s->upbase = val;
+        break;
+    case 5: /* LCDLPBASE */
+        s->lpbase = val;
+        break;
+    case 6: /* LCDIMSC */
+        if (s->version != PL110) {
+            goto control;
+        }
+    imsc:
+        s->int_mask = val;
+        pl110_update(s);
+        break;
+    case 7: /* LCDControl */
+        if (s->version != PL110) {
+            goto imsc;
+        }
+    control:
+        s->cr = val;
+        s->bpp = (val >> 1) & 7;
+        if (pl110_enabled(s)) {
+            qemu_console_resize(s->con, s->cols, s->rows);
+        }
+        break;
+    case 10: /* LCDICR */
+        s->int_status &= ~val;
+        pl110_update(s);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl110_write: Bad offset %x\n", (int)offset);
+    }
+}
+
+static const MemoryRegionOps pl110_ops = {
+    .read = pl110_read,
+    .write = pl110_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void pl110_mux_ctrl_set(void *opaque, int line, int level)
+{
+    pl110_state *s = (pl110_state *)opaque;
+    s->mux_ctrl = level;
+}
+
+static int vmstate_pl110_post_load(void *opaque, int version_id)
+{
+    pl110_state *s = opaque;
+    /* Make sure we redraw, and at the right size */
+    pl110_invalidate_display(s);
+    return 0;
+}
+
+static int pl110_init(SysBusDevice *dev)
+{
+    pl110_state *s = FROM_SYSBUS(pl110_state, dev);
+
+    memory_region_init_io(&s->iomem, &pl110_ops, s, "pl110", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+    qdev_init_gpio_in(&s->busdev.qdev, pl110_mux_ctrl_set, 1);
+    s->con = graphic_console_init(pl110_update_display,
+                                  pl110_invalidate_display,
+                                  NULL, NULL, s);
+    return 0;
+}
+
+static int pl110_versatile_init(SysBusDevice *dev)
+{
+    pl110_state *s = FROM_SYSBUS(pl110_state, dev);
+    s->version = PL110_VERSATILE;
+    return pl110_init(dev);
+}
+
+static int pl111_init(SysBusDevice *dev)
+{
+    pl110_state *s = FROM_SYSBUS(pl110_state, dev);
+    s->version = PL111;
+    return pl110_init(dev);
+}
+
+static void pl110_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = pl110_init;
+    dc->no_user = 1;
+    dc->vmsd = &vmstate_pl110;
+}
+
+static const TypeInfo pl110_info = {
+    .name          = "pl110",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl110_state),
+    .class_init    = pl110_class_init,
+};
+
+static void pl110_versatile_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = pl110_versatile_init;
+    dc->no_user = 1;
+    dc->vmsd = &vmstate_pl110;
+}
+
+static const TypeInfo pl110_versatile_info = {
+    .name          = "pl110_versatile",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl110_state),
+    .class_init    = pl110_versatile_class_init,
+};
+
+static void pl111_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = pl111_init;
+    dc->no_user = 1;
+    dc->vmsd = &vmstate_pl110;
+}
+
+static const TypeInfo pl111_info = {
+    .name          = "pl111",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl110_state),
+    .class_init    = pl111_class_init,
+};
+
+static void pl110_register_types(void)
+{
+    type_register_static(&pl110_info);
+    type_register_static(&pl110_versatile_info);
+    type_register_static(&pl111_info);
+}
+
+type_init(pl110_register_types)
diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c
new file mode 100644 (file)
index 0000000..183a878
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * SSD0303 OLED controller with OSRAM Pictiva 96x16 display.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+/* The controller can support a variety of different displays, but we only
+   implement one.  Most of the commends relating to brightness and geometry
+   setup are ignored. */
+#include "hw/i2c/i2c.h"
+#include "ui/console.h"
+
+//#define DEBUG_SSD0303 1
+
+#ifdef DEBUG_SSD0303
+#define DPRINTF(fmt, ...) \
+do { printf("ssd0303: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "ssd0303: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "ssd0303: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+/* Scaling factor for pixels.  */
+#define MAGNIFY 4
+
+enum ssd0303_mode
+{
+    SSD0303_IDLE,
+    SSD0303_DATA,
+    SSD0303_CMD
+};
+
+enum ssd0303_cmd {
+    SSD0303_CMD_NONE,
+    SSD0303_CMD_SKIP1
+};
+
+typedef struct {
+    I2CSlave i2c;
+    QemuConsole *con;
+    int row;
+    int col;
+    int start_line;
+    int mirror;
+    int flash;
+    int enabled;
+    int inverse;
+    int redraw;
+    enum ssd0303_mode mode;
+    enum ssd0303_cmd cmd_state;
+    uint8_t framebuffer[132*8];
+} ssd0303_state;
+
+static int ssd0303_recv(I2CSlave *i2c)
+{
+    BADF("Reads not implemented\n");
+    return -1;
+}
+
+static int ssd0303_send(I2CSlave *i2c, uint8_t data)
+{
+    ssd0303_state *s = (ssd0303_state *)i2c;
+    enum ssd0303_cmd old_cmd_state;
+    switch (s->mode) {
+    case SSD0303_IDLE:
+        DPRINTF("byte 0x%02x\n", data);
+        if (data == 0x80)
+            s->mode = SSD0303_CMD;
+        else if (data == 0x40)
+            s->mode = SSD0303_DATA;
+        else
+            BADF("Unexpected byte 0x%x\n", data);
+        break;
+    case SSD0303_DATA:
+        DPRINTF("data 0x%02x\n", data);
+        if (s->col < 132) {
+            s->framebuffer[s->col + s->row * 132] = data;
+            s->col++;
+            s->redraw = 1;
+        }
+        break;
+    case SSD0303_CMD:
+        old_cmd_state = s->cmd_state;
+        s->cmd_state = SSD0303_CMD_NONE;
+        switch (old_cmd_state) {
+        case SSD0303_CMD_NONE:
+            DPRINTF("cmd 0x%02x\n", data);
+            s->mode = SSD0303_IDLE;
+            switch (data) {
+            case 0x00 ... 0x0f: /* Set lower column address.  */
+                s->col = (s->col & 0xf0) | (data & 0xf);
+                break;
+            case 0x10 ... 0x20: /* Set higher column address.  */
+                s->col = (s->col & 0x0f) | ((data & 0xf) << 4);
+                break;
+            case 0x40 ... 0x7f: /* Set start line.  */
+                s->start_line = 0;
+                break;
+            case 0x81: /* Set contrast (Ignored).  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xa0: /* Mirror off.  */
+                s->mirror = 0;
+                break;
+            case 0xa1: /* Mirror off.  */
+                s->mirror = 1;
+                break;
+            case 0xa4: /* Entire display off.  */
+                s->flash = 0;
+                break;
+            case 0xa5: /* Entire display on.  */
+                s->flash = 1;
+                break;
+            case 0xa6: /* Inverse off.  */
+                s->inverse = 0;
+                break;
+            case 0xa7: /* Inverse on.  */
+                s->inverse = 1;
+                break;
+            case 0xa8: /* Set multiplied ratio (Ignored).  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xad: /* DC-DC power control.  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xae: /* Display off.  */
+                s->enabled = 0;
+                break;
+            case 0xaf: /* Display on.  */
+                s->enabled = 1;
+                break;
+            case 0xb0 ... 0xbf: /* Set Page address.  */
+                s->row = data & 7;
+                break;
+            case 0xc0 ... 0xc8: /* Set COM output direction (Ignored).  */
+                break;
+            case 0xd3: /* Set display offset (Ignored).  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xd5: /* Set display clock (Ignored).  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xd8: /* Set color and power mode (Ignored).  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xd9: /* Set pre-charge period (Ignored).  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xda: /* Set COM pin configuration (Ignored).  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xdb: /* Set VCOM dselect level (Ignored).  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xe3: /* no-op.  */
+                break;
+            default:
+                BADF("Unknown command: 0x%x\n", data);
+            }
+            break;
+        case SSD0303_CMD_SKIP1:
+            DPRINTF("skip 0x%02x\n", data);
+            break;
+        }
+        break;
+    }
+    return 0;
+}
+
+static void ssd0303_event(I2CSlave *i2c, enum i2c_event event)
+{
+    ssd0303_state *s = (ssd0303_state *)i2c;
+    switch (event) {
+    case I2C_FINISH:
+        s->mode = SSD0303_IDLE;
+        break;
+    case I2C_START_RECV:
+    case I2C_START_SEND:
+    case I2C_NACK:
+        /* Nothing to do.  */
+        break;
+    }
+}
+
+static void ssd0303_update_display(void *opaque)
+{
+    ssd0303_state *s = (ssd0303_state *)opaque;
+    DisplaySurface *surface = qemu_console_surface(s->con);
+    uint8_t *dest;
+    uint8_t *src;
+    int x;
+    int y;
+    int line;
+    char *colors[2];
+    char colortab[MAGNIFY * 8];
+    int dest_width;
+    uint8_t mask;
+
+    if (!s->redraw)
+        return;
+
+    switch (surface_bits_per_pixel(surface)) {
+    case 0:
+        return;
+    case 15:
+        dest_width = 2;
+        break;
+    case 16:
+        dest_width = 2;
+        break;
+    case 24:
+        dest_width = 3;
+        break;
+    case 32:
+        dest_width = 4;
+        break;
+    default:
+        BADF("Bad color depth\n");
+        return;
+    }
+    dest_width *= MAGNIFY;
+    memset(colortab, 0xff, dest_width);
+    memset(colortab + dest_width, 0, dest_width);
+    if (s->flash) {
+        colors[0] = colortab;
+        colors[1] = colortab;
+    } else if (s->inverse) {
+        colors[0] = colortab;
+        colors[1] = colortab + dest_width;
+    } else {
+        colors[0] = colortab + dest_width;
+        colors[1] = colortab;
+    }
+    dest = surface_data(surface);
+    for (y = 0; y < 16; y++) {
+        line = (y + s->start_line) & 63;
+        src = s->framebuffer + 132 * (line >> 3) + 36;
+        mask = 1 << (line & 7);
+        for (x = 0; x < 96; x++) {
+            memcpy(dest, colors[(*src & mask) != 0], dest_width);
+            dest += dest_width;
+            src++;
+        }
+        for (x = 1; x < MAGNIFY; x++) {
+            memcpy(dest, dest - dest_width * 96, dest_width * 96);
+            dest += dest_width * 96;
+        }
+    }
+    s->redraw = 0;
+    dpy_gfx_update(s->con, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY);
+}
+
+static void ssd0303_invalidate_display(void * opaque)
+{
+    ssd0303_state *s = (ssd0303_state *)opaque;
+    s->redraw = 1;
+}
+
+static const VMStateDescription vmstate_ssd0303 = {
+    .name = "ssd0303_oled",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_INT32(row, ssd0303_state),
+        VMSTATE_INT32(col, ssd0303_state),
+        VMSTATE_INT32(start_line, ssd0303_state),
+        VMSTATE_INT32(mirror, ssd0303_state),
+        VMSTATE_INT32(flash, ssd0303_state),
+        VMSTATE_INT32(enabled, ssd0303_state),
+        VMSTATE_INT32(inverse, ssd0303_state),
+        VMSTATE_INT32(redraw, ssd0303_state),
+        VMSTATE_UINT32(mode, ssd0303_state),
+        VMSTATE_UINT32(cmd_state, ssd0303_state),
+        VMSTATE_BUFFER(framebuffer, ssd0303_state),
+        VMSTATE_I2C_SLAVE(i2c, ssd0303_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int ssd0303_init(I2CSlave *i2c)
+{
+    ssd0303_state *s = FROM_I2C_SLAVE(ssd0303_state, i2c);
+
+    s->con = graphic_console_init(ssd0303_update_display,
+                                  ssd0303_invalidate_display,
+                                  NULL, NULL, s);
+    qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY);
+    return 0;
+}
+
+static void ssd0303_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+    k->init = ssd0303_init;
+    k->event = ssd0303_event;
+    k->recv = ssd0303_recv;
+    k->send = ssd0303_send;
+    dc->vmsd = &vmstate_ssd0303;
+}
+
+static const TypeInfo ssd0303_info = {
+    .name          = "ssd0303",
+    .parent        = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(ssd0303_state),
+    .class_init    = ssd0303_class_init,
+};
+
+static void ssd0303_register_types(void)
+{
+    type_register_static(&ssd0303_info);
+}
+
+type_init(ssd0303_register_types)
diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c
new file mode 100644 (file)
index 0000000..5cf2f70
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * SSD0323 OLED controller with OSRAM Pictiva 128x64 display.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+/* The controller can support a variety of different displays, but we only
+   implement one.  Most of the commends relating to brightness and geometry
+   setup are ignored. */
+#include "hw/ssi.h"
+#include "ui/console.h"
+
+//#define DEBUG_SSD0323 1
+
+#ifdef DEBUG_SSD0323
+#define DPRINTF(fmt, ...) \
+do { printf("ssd0323: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { \
+    fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__); abort(); \
+} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+/* Scaling factor for pixels.  */
+#define MAGNIFY 4
+
+#define REMAP_SWAP_COLUMN 0x01
+#define REMAP_SWAP_NYBBLE 0x02
+#define REMAP_VERTICAL    0x04
+#define REMAP_SWAP_COM    0x10
+#define REMAP_SPLIT_COM   0x40
+
+enum ssd0323_mode
+{
+    SSD0323_CMD,
+    SSD0323_DATA
+};
+
+typedef struct {
+    SSISlave ssidev;
+    QemuConsole *con;
+
+    int cmd_len;
+    int cmd;
+    int cmd_data[8];
+    int row;
+    int row_start;
+    int row_end;
+    int col;
+    int col_start;
+    int col_end;
+    int redraw;
+    int remap;
+    enum ssd0323_mode mode;
+    uint8_t framebuffer[128 * 80 / 2];
+} ssd0323_state;
+
+static uint32_t ssd0323_transfer(SSISlave *dev, uint32_t data)
+{
+    ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, dev);
+
+    switch (s->mode) {
+    case SSD0323_DATA:
+        DPRINTF("data 0x%02x\n", data);
+        s->framebuffer[s->col + s->row * 64] = data;
+        if (s->remap & REMAP_VERTICAL) {
+            s->row++;
+            if (s->row > s->row_end) {
+                s->row = s->row_start;
+                s->col++;
+            }
+            if (s->col > s->col_end) {
+                s->col = s->col_start;
+            }
+        } else {
+            s->col++;
+            if (s->col > s->col_end) {
+                s->row++;
+                s->col = s->col_start;
+            }
+            if (s->row > s->row_end) {
+                s->row = s->row_start;
+            }
+        }
+        s->redraw = 1;
+        break;
+    case SSD0323_CMD:
+        DPRINTF("cmd 0x%02x\n", data);
+        if (s->cmd_len == 0) {
+            s->cmd = data;
+        } else {
+            s->cmd_data[s->cmd_len - 1] = data;
+        }
+        s->cmd_len++;
+        switch (s->cmd) {
+#define DATA(x) if (s->cmd_len <= (x)) return 0
+        case 0x15: /* Set column.  */
+            DATA(2);
+            s->col = s->col_start = s->cmd_data[0] % 64;
+            s->col_end = s->cmd_data[1] % 64;
+            break;
+        case 0x75: /* Set row.  */
+            DATA(2);
+            s->row = s->row_start = s->cmd_data[0] % 80;
+            s->row_end = s->cmd_data[1] % 80;
+            break;
+        case 0x81: /* Set contrast */
+            DATA(1);
+            break;
+        case 0x84: case 0x85: case 0x86: /* Max current.  */
+            DATA(0);
+            break;
+        case 0xa0: /* Set remapping.  */
+            /* FIXME: Implement this.  */
+            DATA(1);
+            s->remap = s->cmd_data[0];
+            break;
+        case 0xa1: /* Set display start line.  */
+        case 0xa2: /* Set display offset.  */
+            /* FIXME: Implement these.  */
+            DATA(1);
+            break;
+        case 0xa4: /* Normal mode.  */
+        case 0xa5: /* All on.  */
+        case 0xa6: /* All off.  */
+        case 0xa7: /* Inverse.  */
+            /* FIXME: Implement these.  */
+            DATA(0);
+            break;
+        case 0xa8: /* Set multiplex ratio.  */
+        case 0xad: /* Set DC-DC converter.  */
+            DATA(1);
+            /* Ignored.  Don't care.  */
+            break;
+        case 0xae: /* Display off.  */
+        case 0xaf: /* Display on.  */
+            DATA(0);
+            /* TODO: Implement power control.  */
+            break;
+        case 0xb1: /* Set phase length.  */
+        case 0xb2: /* Set row period.  */
+        case 0xb3: /* Set clock rate.  */
+        case 0xbc: /* Set precharge.  */
+        case 0xbe: /* Set VCOMH.  */
+        case 0xbf: /* Set segment low.  */
+            DATA(1);
+            /* Ignored.  Don't care.  */
+            break;
+        case 0xb8: /* Set grey scale table.  */
+            /* FIXME: Implement this.  */
+            DATA(8);
+            break;
+        case 0xe3: /* NOP.  */
+            DATA(0);
+            break;
+        case 0xff: /* Nasty hack because we don't handle chip selects
+                      properly.  */
+            break;
+        default:
+            BADF("Unknown command: 0x%x\n", data);
+        }
+        s->cmd_len = 0;
+        return 0;
+    }
+    return 0;
+}
+
+static void ssd0323_update_display(void *opaque)
+{
+    ssd0323_state *s = (ssd0323_state *)opaque;
+    DisplaySurface *surface = qemu_console_surface(s->con);
+    uint8_t *dest;
+    uint8_t *src;
+    int x;
+    int y;
+    int i;
+    int line;
+    char *colors[16];
+    char colortab[MAGNIFY * 64];
+    char *p;
+    int dest_width;
+
+    if (!s->redraw)
+        return;
+
+    switch (surface_bits_per_pixel(surface)) {
+    case 0:
+        return;
+    case 15:
+        dest_width = 2;
+        break;
+    case 16:
+        dest_width = 2;
+        break;
+    case 24:
+        dest_width = 3;
+        break;
+    case 32:
+        dest_width = 4;
+        break;
+    default:
+        BADF("Bad color depth\n");
+        return;
+    }
+    p = colortab;
+    for (i = 0; i < 16; i++) {
+        int n;
+        colors[i] = p;
+        switch (surface_bits_per_pixel(surface)) {
+        case 15:
+            n = i * 2 + (i >> 3);
+            p[0] = n | (n << 5);
+            p[1] = (n << 2) | (n >> 3);
+            break;
+        case 16:
+            n = i * 2 + (i >> 3);
+            p[0] = n | (n << 6) | ((n << 1) & 0x20);
+            p[1] = (n << 3) | (n >> 2);
+            break;
+        case 24:
+        case 32:
+            n = (i << 4) | i;
+            p[0] = p[1] = p[2] = n;
+            break;
+        default:
+            BADF("Bad color depth\n");
+            return;
+        }
+        p += dest_width;
+    }
+    /* TODO: Implement row/column remapping.  */
+    dest = surface_data(surface);
+    for (y = 0; y < 64; y++) {
+        line = y;
+        src = s->framebuffer + 64 * line;
+        for (x = 0; x < 64; x++) {
+            int val;
+            val = *src >> 4;
+            for (i = 0; i < MAGNIFY; i++) {
+                memcpy(dest, colors[val], dest_width);
+                dest += dest_width;
+            }
+            val = *src & 0xf;
+            for (i = 0; i < MAGNIFY; i++) {
+                memcpy(dest, colors[val], dest_width);
+                dest += dest_width;
+            }
+            src++;
+        }
+        for (i = 1; i < MAGNIFY; i++) {
+            memcpy(dest, dest - dest_width * MAGNIFY * 128,
+                   dest_width * 128 * MAGNIFY);
+            dest += dest_width * 128 * MAGNIFY;
+        }
+    }
+    s->redraw = 0;
+    dpy_gfx_update(s->con, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY);
+}
+
+static void ssd0323_invalidate_display(void * opaque)
+{
+    ssd0323_state *s = (ssd0323_state *)opaque;
+    s->redraw = 1;
+}
+
+/* Command/data input.  */
+static void ssd0323_cd(void *opaque, int n, int level)
+{
+    ssd0323_state *s = (ssd0323_state *)opaque;
+    DPRINTF("%s mode\n", level ? "Data" : "Command");
+    s->mode = level ? SSD0323_DATA : SSD0323_CMD;
+}
+
+static void ssd0323_save(QEMUFile *f, void *opaque)
+{
+    SSISlave *ss = SSI_SLAVE(opaque);
+    ssd0323_state *s = (ssd0323_state *)opaque;
+    int i;
+
+    qemu_put_be32(f, s->cmd_len);
+    qemu_put_be32(f, s->cmd);
+    for (i = 0; i < 8; i++)
+        qemu_put_be32(f, s->cmd_data[i]);
+    qemu_put_be32(f, s->row);
+    qemu_put_be32(f, s->row_start);
+    qemu_put_be32(f, s->row_end);
+    qemu_put_be32(f, s->col);
+    qemu_put_be32(f, s->col_start);
+    qemu_put_be32(f, s->col_end);
+    qemu_put_be32(f, s->redraw);
+    qemu_put_be32(f, s->remap);
+    qemu_put_be32(f, s->mode);
+    qemu_put_buffer(f, s->framebuffer, sizeof(s->framebuffer));
+
+    qemu_put_be32(f, ss->cs);
+}
+
+static int ssd0323_load(QEMUFile *f, void *opaque, int version_id)
+{
+    SSISlave *ss = SSI_SLAVE(opaque);
+    ssd0323_state *s = (ssd0323_state *)opaque;
+    int i;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    s->cmd_len = qemu_get_be32(f);
+    s->cmd = qemu_get_be32(f);
+    for (i = 0; i < 8; i++)
+        s->cmd_data[i] = qemu_get_be32(f);
+    s->row = qemu_get_be32(f);
+    s->row_start = qemu_get_be32(f);
+    s->row_end = qemu_get_be32(f);
+    s->col = qemu_get_be32(f);
+    s->col_start = qemu_get_be32(f);
+    s->col_end = qemu_get_be32(f);
+    s->redraw = qemu_get_be32(f);
+    s->remap = qemu_get_be32(f);
+    s->mode = qemu_get_be32(f);
+    qemu_get_buffer(f, s->framebuffer, sizeof(s->framebuffer));
+
+    ss->cs = qemu_get_be32(f);
+
+    return 0;
+}
+
+static int ssd0323_init(SSISlave *dev)
+{
+    ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, dev);
+
+    s->col_end = 63;
+    s->row_end = 79;
+    s->con = graphic_console_init(ssd0323_update_display,
+                                  ssd0323_invalidate_display,
+                                  NULL, NULL, s);
+    qemu_console_resize(s->con, 128 * MAGNIFY, 64 * MAGNIFY);
+
+    qdev_init_gpio_in(&dev->qdev, ssd0323_cd, 1);
+
+    register_savevm(&dev->qdev, "ssd0323_oled", -1, 1,
+                    ssd0323_save, ssd0323_load, s);
+    return 0;
+}
+
+static void ssd0323_class_init(ObjectClass *klass, void *data)
+{
+    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
+
+    k->init = ssd0323_init;
+    k->transfer = ssd0323_transfer;
+    k->cs_polarity = SSI_CS_HIGH;
+}
+
+static const TypeInfo ssd0323_info = {
+    .name          = "ssd0323",
+    .parent        = TYPE_SSI_SLAVE,
+    .instance_size = sizeof(ssd0323_state),
+    .class_init    = ssd0323_class_init,
+};
+
+static void ssd03232_register_types(void)
+{
+    type_register_static(&ssd0323_info);
+}
+
+type_init(ssd03232_register_types)
diff --git a/hw/display/vga-isa-mm.c b/hw/display/vga-isa-mm.c
new file mode 100644 (file)
index 0000000..3b08720
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * QEMU ISA MM VGA Emulator.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "ui/console.h"
+#include "hw/i386/pc.h"
+#include "hw/vga_int.h"
+#include "ui/pixel_ops.h"
+#include "qemu/timer.h"
+
+#define VGA_RAM_SIZE (8192 * 1024)
+
+typedef struct ISAVGAMMState {
+    VGACommonState vga;
+    int it_shift;
+} ISAVGAMMState;
+
+/* Memory mapped interface */
+static uint32_t vga_mm_readb (void *opaque, hwaddr addr)
+{
+    ISAVGAMMState *s = opaque;
+
+    return vga_ioport_read(&s->vga, addr >> s->it_shift) & 0xff;
+}
+
+static void vga_mm_writeb (void *opaque,
+                           hwaddr addr, uint32_t value)
+{
+    ISAVGAMMState *s = opaque;
+
+    vga_ioport_write(&s->vga, addr >> s->it_shift, value & 0xff);
+}
+
+static uint32_t vga_mm_readw (void *opaque, hwaddr addr)
+{
+    ISAVGAMMState *s = opaque;
+
+    return vga_ioport_read(&s->vga, addr >> s->it_shift) & 0xffff;
+}
+
+static void vga_mm_writew (void *opaque,
+                           hwaddr addr, uint32_t value)
+{
+    ISAVGAMMState *s = opaque;
+
+    vga_ioport_write(&s->vga, addr >> s->it_shift, value & 0xffff);
+}
+
+static uint32_t vga_mm_readl (void *opaque, hwaddr addr)
+{
+    ISAVGAMMState *s = opaque;
+
+    return vga_ioport_read(&s->vga, addr >> s->it_shift);
+}
+
+static void vga_mm_writel (void *opaque,
+                           hwaddr addr, uint32_t value)
+{
+    ISAVGAMMState *s = opaque;
+
+    vga_ioport_write(&s->vga, addr >> s->it_shift, value);
+}
+
+static const MemoryRegionOps vga_mm_ctrl_ops = {
+    .old_mmio = {
+        .read = {
+            vga_mm_readb,
+            vga_mm_readw,
+            vga_mm_readl,
+        },
+        .write = {
+            vga_mm_writeb,
+            vga_mm_writew,
+            vga_mm_writel,
+        },
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void vga_mm_init(ISAVGAMMState *s, hwaddr vram_base,
+                        hwaddr ctrl_base, int it_shift,
+                        MemoryRegion *address_space)
+{
+    MemoryRegion *s_ioport_ctrl, *vga_io_memory;
+
+    s->it_shift = it_shift;
+    s_ioport_ctrl = g_malloc(sizeof(*s_ioport_ctrl));
+    memory_region_init_io(s_ioport_ctrl, &vga_mm_ctrl_ops, s,
+                          "vga-mm-ctrl", 0x100000);
+    memory_region_set_flush_coalesced(s_ioport_ctrl);
+
+    vga_io_memory = g_malloc(sizeof(*vga_io_memory));
+    /* XXX: endianness? */
+    memory_region_init_io(vga_io_memory, &vga_mem_ops, &s->vga,
+                          "vga-mem", 0x20000);
+
+    vmstate_register(NULL, 0, &vmstate_vga_common, s);
+
+    memory_region_add_subregion(address_space, ctrl_base, s_ioport_ctrl);
+    s->vga.bank_offset = 0;
+    memory_region_add_subregion(address_space,
+                                vram_base + 0x000a0000, vga_io_memory);
+    memory_region_set_coalescing(vga_io_memory);
+}
+
+int isa_vga_mm_init(hwaddr vram_base,
+                    hwaddr ctrl_base, int it_shift,
+                    MemoryRegion *address_space)
+{
+    ISAVGAMMState *s;
+
+    s = g_malloc0(sizeof(*s));
+
+    s->vga.vram_size_mb = VGA_RAM_SIZE >> 20;
+    vga_common_init(&s->vga);
+    vga_mm_init(s, vram_base, ctrl_base, it_shift, address_space);
+
+    s->vga.con = graphic_console_init(s->vga.update, s->vga.invalidate,
+                                      s->vga.screen_dump, s->vga.text_update,
+                                      s);
+
+    vga_init_vbe(&s->vga, address_space);
+    return 0;
+}
diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c
new file mode 100644 (file)
index 0000000..89d7fa6
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * QEMU ISA VGA Emulator.
+ *
+ * see docs/specs/standard-vga.txt for virtual hardware specs.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "ui/console.h"
+#include "hw/i386/pc.h"
+#include "hw/vga_int.h"
+#include "ui/pixel_ops.h"
+#include "qemu/timer.h"
+#include "hw/loader.h"
+
+typedef struct ISAVGAState {
+    ISADevice dev;
+    struct VGACommonState state;
+} ISAVGAState;
+
+static void vga_reset_isa(DeviceState *dev)
+{
+    ISAVGAState *d = container_of(dev, ISAVGAState, dev.qdev);
+    VGACommonState *s = &d->state;
+
+    vga_common_reset(s);
+}
+
+static int vga_initfn(ISADevice *dev)
+{
+    ISAVGAState *d = DO_UPCAST(ISAVGAState, dev, dev);
+    VGACommonState *s = &d->state;
+    MemoryRegion *vga_io_memory;
+    const MemoryRegionPortio *vga_ports, *vbe_ports;
+
+    vga_common_init(s);
+    s->legacy_address_space = isa_address_space(dev);
+    vga_io_memory = vga_init_io(s, &vga_ports, &vbe_ports);
+    isa_register_portio_list(dev, 0x3b0, vga_ports, s, "vga");
+    if (vbe_ports) {
+        isa_register_portio_list(dev, 0x1ce, vbe_ports, s, "vbe");
+    }
+    memory_region_add_subregion_overlap(isa_address_space(dev),
+                                        isa_mem_base + 0x000a0000,
+                                        vga_io_memory, 1);
+    memory_region_set_coalescing(vga_io_memory);
+    s->con = graphic_console_init(s->update, s->invalidate,
+                                  s->screen_dump, s->text_update, s);
+
+    vga_init_vbe(s, isa_address_space(dev));
+    /* ROM BIOS */
+    rom_add_vga(VGABIOS_FILENAME);
+    return 0;
+}
+
+static Property vga_isa_properties[] = {
+    DEFINE_PROP_UINT32("vgamem_mb", ISAVGAState, state.vram_size_mb, 8),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vga_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+    ic->init = vga_initfn;
+    dc->reset = vga_reset_isa;
+    dc->vmsd = &vmstate_vga_common;
+    dc->props = vga_isa_properties;
+}
+
+static const TypeInfo vga_info = {
+    .name          = "isa-vga",
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(ISAVGAState),
+    .class_init    = vga_class_initfn,
+};
+
+static void vga_register_types(void)
+{
+    type_register_static(&vga_info);
+}
+
+type_init(vga_register_types)
diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c
new file mode 100644 (file)
index 0000000..05fa9bc
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * QEMU PCI VGA Emulator.
+ *
+ * see docs/specs/standard-vga.txt for virtual hardware specs.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "ui/console.h"
+#include "hw/pci/pci.h"
+#include "hw/vga_int.h"
+#include "ui/pixel_ops.h"
+#include "qemu/timer.h"
+#include "hw/loader.h"
+
+#define PCI_VGA_IOPORT_OFFSET 0x400
+#define PCI_VGA_IOPORT_SIZE   (0x3e0 - 0x3c0)
+#define PCI_VGA_BOCHS_OFFSET  0x500
+#define PCI_VGA_BOCHS_SIZE    (0x0b * 2)
+#define PCI_VGA_MMIO_SIZE     0x1000
+
+enum vga_pci_flags {
+    PCI_VGA_FLAG_ENABLE_MMIO = 1,
+};
+
+typedef struct PCIVGAState {
+    PCIDevice dev;
+    VGACommonState vga;
+    uint32_t flags;
+    MemoryRegion mmio;
+    MemoryRegion ioport;
+    MemoryRegion bochs;
+} PCIVGAState;
+
+static const VMStateDescription vmstate_vga_pci = {
+    .name = "vga",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields      = (VMStateField []) {
+        VMSTATE_PCI_DEVICE(dev, PCIVGAState),
+        VMSTATE_STRUCT(vga, PCIVGAState, 0, vmstate_vga_common, VGACommonState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static uint64_t pci_vga_ioport_read(void *ptr, hwaddr addr,
+                                    unsigned size)
+{
+    PCIVGAState *d = ptr;
+    uint64_t ret = 0;
+
+    switch (size) {
+    case 1:
+        ret = vga_ioport_read(&d->vga, addr);
+        break;
+    case 2:
+        ret  = vga_ioport_read(&d->vga, addr);
+        ret |= vga_ioport_read(&d->vga, addr+1) << 8;
+        break;
+    }
+    return ret;
+}
+
+static void pci_vga_ioport_write(void *ptr, hwaddr addr,
+                                 uint64_t val, unsigned size)
+{
+    PCIVGAState *d = ptr;
+
+    switch (size) {
+    case 1:
+        vga_ioport_write(&d->vga, addr + 0x3c0, val);
+        break;
+    case 2:
+        /*
+         * Update bytes in little endian order.  Allows to update
+         * indexed registers with a single word write because the
+         * index byte is updated first.
+         */
+        vga_ioport_write(&d->vga, addr + 0x3c0, val & 0xff);
+        vga_ioport_write(&d->vga, addr + 0x3c1, (val >> 8) & 0xff);
+        break;
+    }
+}
+
+static const MemoryRegionOps pci_vga_ioport_ops = {
+    .read = pci_vga_ioport_read,
+    .write = pci_vga_ioport_write,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 2,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t pci_vga_bochs_read(void *ptr, hwaddr addr,
+                                   unsigned size)
+{
+    PCIVGAState *d = ptr;
+    int index = addr >> 1;
+
+    vbe_ioport_write_index(&d->vga, 0, index);
+    return vbe_ioport_read_data(&d->vga, 0);
+}
+
+static void pci_vga_bochs_write(void *ptr, hwaddr addr,
+                                uint64_t val, unsigned size)
+{
+    PCIVGAState *d = ptr;
+    int index = addr >> 1;
+
+    vbe_ioport_write_index(&d->vga, 0, index);
+    vbe_ioport_write_data(&d->vga, 0, val);
+}
+
+static const MemoryRegionOps pci_vga_bochs_ops = {
+    .read = pci_vga_bochs_read,
+    .write = pci_vga_bochs_write,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+    .impl.min_access_size = 2,
+    .impl.max_access_size = 2,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static int pci_std_vga_initfn(PCIDevice *dev)
+{
+    PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, dev);
+    VGACommonState *s = &d->vga;
+
+    /* vga + console init */
+    vga_common_init(s);
+    vga_init(s, pci_address_space(dev), pci_address_space_io(dev), true);
+
+    s->con = graphic_console_init(s->update, s->invalidate,
+                                  s->screen_dump, s->text_update, s);
+
+    /* XXX: VGA_RAM_SIZE must be a power of two */
+    pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
+
+    /* mmio bar for vga register access */
+    if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_MMIO)) {
+        memory_region_init(&d->mmio, "vga.mmio", 4096);
+        memory_region_init_io(&d->ioport, &pci_vga_ioport_ops, d,
+                              "vga ioports remapped", PCI_VGA_IOPORT_SIZE);
+        memory_region_init_io(&d->bochs, &pci_vga_bochs_ops, d,
+                              "bochs dispi interface", PCI_VGA_BOCHS_SIZE);
+
+        memory_region_add_subregion(&d->mmio, PCI_VGA_IOPORT_OFFSET,
+                                    &d->ioport);
+        memory_region_add_subregion(&d->mmio, PCI_VGA_BOCHS_OFFSET,
+                                    &d->bochs);
+        pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
+    }
+
+    if (!dev->rom_bar) {
+        /* compatibility with pc-0.13 and older */
+        vga_init_vbe(s, pci_address_space(dev));
+    }
+
+    return 0;
+}
+
+static Property vga_pci_properties[] = {
+    DEFINE_PROP_UINT32("vgamem_mb", PCIVGAState, vga.vram_size_mb, 16),
+    DEFINE_PROP_BIT("mmio", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_MMIO, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vga_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->no_hotplug = 1;
+    k->init = pci_std_vga_initfn;
+    k->romfile = "vgabios-stdvga.bin";
+    k->vendor_id = PCI_VENDOR_ID_QEMU;
+    k->device_id = PCI_DEVICE_ID_QEMU_VGA;
+    k->class_id = PCI_CLASS_DISPLAY_VGA;
+    dc->vmsd = &vmstate_vga_pci;
+    dc->props = vga_pci_properties;
+}
+
+static const TypeInfo vga_info = {
+    .name          = "VGA",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIVGAState),
+    .class_init    = vga_class_init,
+};
+
+static void vga_register_types(void)
+{
+    type_register_static(&vga_info);
+}
+
+type_init(vga_register_types)
diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c
new file mode 100644 (file)
index 0000000..5b9ce8f
--- /dev/null
@@ -0,0 +1,1282 @@
+/*
+ * QEMU VMware-SVGA "chipset".
+ *
+ * Copyright (c) 2007 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/loader.h"
+#include "ui/console.h"
+#include "hw/pci/pci.h"
+
+#undef VERBOSE
+#define HW_RECT_ACCEL
+#define HW_FILL_ACCEL
+#define HW_MOUSE_ACCEL
+
+#include "hw/vga_int.h"
+
+/* See http://vmware-svga.sf.net/ for some documentation on VMWare SVGA */
+
+struct vmsvga_state_s {
+    VGACommonState vga;
+
+    int invalidated;
+    int depth;
+    int bypp;
+    int enable;
+    int config;
+    struct {
+        int id;
+        int x;
+        int y;
+        int on;
+    } cursor;
+
+    int index;
+    int scratch_size;
+    uint32_t *scratch;
+    int new_width;
+    int new_height;
+    uint32_t guest;
+    uint32_t svgaid;
+    int syncing;
+
+    MemoryRegion fifo_ram;
+    uint8_t *fifo_ptr;
+    unsigned int fifo_size;
+
+    union {
+        uint32_t *fifo;
+        struct QEMU_PACKED {
+            uint32_t min;
+            uint32_t max;
+            uint32_t next_cmd;
+            uint32_t stop;
+            /* Add registers here when adding capabilities.  */
+            uint32_t fifo[0];
+        } *cmd;
+    };
+
+#define REDRAW_FIFO_LEN  512
+    struct vmsvga_rect_s {
+        int x, y, w, h;
+    } redraw_fifo[REDRAW_FIFO_LEN];
+    int redraw_fifo_first, redraw_fifo_last;
+};
+
+struct pci_vmsvga_state_s {
+    PCIDevice card;
+    struct vmsvga_state_s chip;
+    MemoryRegion io_bar;
+};
+
+#define SVGA_MAGIC              0x900000UL
+#define SVGA_MAKE_ID(ver)       (SVGA_MAGIC << 8 | (ver))
+#define SVGA_ID_0               SVGA_MAKE_ID(0)
+#define SVGA_ID_1               SVGA_MAKE_ID(1)
+#define SVGA_ID_2               SVGA_MAKE_ID(2)
+
+#define SVGA_LEGACY_BASE_PORT   0x4560
+#define SVGA_INDEX_PORT         0x0
+#define SVGA_VALUE_PORT         0x1
+#define SVGA_BIOS_PORT          0x2
+
+#define SVGA_VERSION_2
+
+#ifdef SVGA_VERSION_2
+# define SVGA_ID                SVGA_ID_2
+# define SVGA_IO_BASE           SVGA_LEGACY_BASE_PORT
+# define SVGA_IO_MUL            1
+# define SVGA_FIFO_SIZE         0x10000
+# define SVGA_PCI_DEVICE_ID     PCI_DEVICE_ID_VMWARE_SVGA2
+#else
+# define SVGA_ID                SVGA_ID_1
+# define SVGA_IO_BASE           SVGA_LEGACY_BASE_PORT
+# define SVGA_IO_MUL            4
+# define SVGA_FIFO_SIZE         0x10000
+# define SVGA_PCI_DEVICE_ID     PCI_DEVICE_ID_VMWARE_SVGA
+#endif
+
+enum {
+    /* ID 0, 1 and 2 registers */
+    SVGA_REG_ID = 0,
+    SVGA_REG_ENABLE = 1,
+    SVGA_REG_WIDTH = 2,
+    SVGA_REG_HEIGHT = 3,
+    SVGA_REG_MAX_WIDTH = 4,
+    SVGA_REG_MAX_HEIGHT = 5,
+    SVGA_REG_DEPTH = 6,
+    SVGA_REG_BITS_PER_PIXEL = 7,        /* Current bpp in the guest */
+    SVGA_REG_PSEUDOCOLOR = 8,
+    SVGA_REG_RED_MASK = 9,
+    SVGA_REG_GREEN_MASK = 10,
+    SVGA_REG_BLUE_MASK = 11,
+    SVGA_REG_BYTES_PER_LINE = 12,
+    SVGA_REG_FB_START = 13,
+    SVGA_REG_FB_OFFSET = 14,
+    SVGA_REG_VRAM_SIZE = 15,
+    SVGA_REG_FB_SIZE = 16,
+
+    /* ID 1 and 2 registers */
+    SVGA_REG_CAPABILITIES = 17,
+    SVGA_REG_MEM_START = 18,            /* Memory for command FIFO */
+    SVGA_REG_MEM_SIZE = 19,
+    SVGA_REG_CONFIG_DONE = 20,          /* Set when memory area configured */
+    SVGA_REG_SYNC = 21,                 /* Write to force synchronization */
+    SVGA_REG_BUSY = 22,                 /* Read to check if sync is done */
+    SVGA_REG_GUEST_ID = 23,             /* Set guest OS identifier */
+    SVGA_REG_CURSOR_ID = 24,            /* ID of cursor */
+    SVGA_REG_CURSOR_X = 25,             /* Set cursor X position */
+    SVGA_REG_CURSOR_Y = 26,             /* Set cursor Y position */
+    SVGA_REG_CURSOR_ON = 27,            /* Turn cursor on/off */
+    SVGA_REG_HOST_BITS_PER_PIXEL = 28,  /* Current bpp in the host */
+    SVGA_REG_SCRATCH_SIZE = 29,         /* Number of scratch registers */
+    SVGA_REG_MEM_REGS = 30,             /* Number of FIFO registers */
+    SVGA_REG_NUM_DISPLAYS = 31,         /* Number of guest displays */
+    SVGA_REG_PITCHLOCK = 32,            /* Fixed pitch for all modes */
+
+    SVGA_PALETTE_BASE = 1024,           /* Base of SVGA color map */
+    SVGA_PALETTE_END  = SVGA_PALETTE_BASE + 767,
+    SVGA_SCRATCH_BASE = SVGA_PALETTE_BASE + 768,
+};
+
+#define SVGA_CAP_NONE                   0
+#define SVGA_CAP_RECT_FILL              (1 << 0)
+#define SVGA_CAP_RECT_COPY              (1 << 1)
+#define SVGA_CAP_RECT_PAT_FILL          (1 << 2)
+#define SVGA_CAP_LEGACY_OFFSCREEN       (1 << 3)
+#define SVGA_CAP_RASTER_OP              (1 << 4)
+#define SVGA_CAP_CURSOR                 (1 << 5)
+#define SVGA_CAP_CURSOR_BYPASS          (1 << 6)
+#define SVGA_CAP_CURSOR_BYPASS_2        (1 << 7)
+#define SVGA_CAP_8BIT_EMULATION         (1 << 8)
+#define SVGA_CAP_ALPHA_CURSOR           (1 << 9)
+#define SVGA_CAP_GLYPH                  (1 << 10)
+#define SVGA_CAP_GLYPH_CLIPPING         (1 << 11)
+#define SVGA_CAP_OFFSCREEN_1            (1 << 12)
+#define SVGA_CAP_ALPHA_BLEND            (1 << 13)
+#define SVGA_CAP_3D                     (1 << 14)
+#define SVGA_CAP_EXTENDED_FIFO          (1 << 15)
+#define SVGA_CAP_MULTIMON               (1 << 16)
+#define SVGA_CAP_PITCHLOCK              (1 << 17)
+
+/*
+ * FIFO offsets (seen as an array of 32-bit words)
+ */
+enum {
+    /*
+     * The original defined FIFO offsets
+     */
+    SVGA_FIFO_MIN = 0,
+    SVGA_FIFO_MAX,      /* The distance from MIN to MAX must be at least 10K */
+    SVGA_FIFO_NEXT_CMD,
+    SVGA_FIFO_STOP,
+
+    /*
+     * Additional offsets added as of SVGA_CAP_EXTENDED_FIFO
+     */
+    SVGA_FIFO_CAPABILITIES = 4,
+    SVGA_FIFO_FLAGS,
+    SVGA_FIFO_FENCE,
+    SVGA_FIFO_3D_HWVERSION,
+    SVGA_FIFO_PITCHLOCK,
+};
+
+#define SVGA_FIFO_CAP_NONE              0
+#define SVGA_FIFO_CAP_FENCE             (1 << 0)
+#define SVGA_FIFO_CAP_ACCELFRONT        (1 << 1)
+#define SVGA_FIFO_CAP_PITCHLOCK         (1 << 2)
+
+#define SVGA_FIFO_FLAG_NONE             0
+#define SVGA_FIFO_FLAG_ACCELFRONT       (1 << 0)
+
+/* These values can probably be changed arbitrarily.  */
+#define SVGA_SCRATCH_SIZE               0x8000
+#define SVGA_MAX_WIDTH                  2360
+#define SVGA_MAX_HEIGHT                 1770
+
+#ifdef VERBOSE
+# define GUEST_OS_BASE          0x5001
+static const char *vmsvga_guest_id[] = {
+    [0x00] = "Dos",
+    [0x01] = "Windows 3.1",
+    [0x02] = "Windows 95",
+    [0x03] = "Windows 98",
+    [0x04] = "Windows ME",
+    [0x05] = "Windows NT",
+    [0x06] = "Windows 2000",
+    [0x07] = "Linux",
+    [0x08] = "OS/2",
+    [0x09] = "an unknown OS",
+    [0x0a] = "BSD",
+    [0x0b] = "Whistler",
+    [0x0c] = "an unknown OS",
+    [0x0d] = "an unknown OS",
+    [0x0e] = "an unknown OS",
+    [0x0f] = "an unknown OS",
+    [0x10] = "an unknown OS",
+    [0x11] = "an unknown OS",
+    [0x12] = "an unknown OS",
+    [0x13] = "an unknown OS",
+    [0x14] = "an unknown OS",
+    [0x15] = "Windows 2003",
+};
+#endif
+
+enum {
+    SVGA_CMD_INVALID_CMD = 0,
+    SVGA_CMD_UPDATE = 1,
+    SVGA_CMD_RECT_FILL = 2,
+    SVGA_CMD_RECT_COPY = 3,
+    SVGA_CMD_DEFINE_BITMAP = 4,
+    SVGA_CMD_DEFINE_BITMAP_SCANLINE = 5,
+    SVGA_CMD_DEFINE_PIXMAP = 6,
+    SVGA_CMD_DEFINE_PIXMAP_SCANLINE = 7,
+    SVGA_CMD_RECT_BITMAP_FILL = 8,
+    SVGA_CMD_RECT_PIXMAP_FILL = 9,
+    SVGA_CMD_RECT_BITMAP_COPY = 10,
+    SVGA_CMD_RECT_PIXMAP_COPY = 11,
+    SVGA_CMD_FREE_OBJECT = 12,
+    SVGA_CMD_RECT_ROP_FILL = 13,
+    SVGA_CMD_RECT_ROP_COPY = 14,
+    SVGA_CMD_RECT_ROP_BITMAP_FILL = 15,
+    SVGA_CMD_RECT_ROP_PIXMAP_FILL = 16,
+    SVGA_CMD_RECT_ROP_BITMAP_COPY = 17,
+    SVGA_CMD_RECT_ROP_PIXMAP_COPY = 18,
+    SVGA_CMD_DEFINE_CURSOR = 19,
+    SVGA_CMD_DISPLAY_CURSOR = 20,
+    SVGA_CMD_MOVE_CURSOR = 21,
+    SVGA_CMD_DEFINE_ALPHA_CURSOR = 22,
+    SVGA_CMD_DRAW_GLYPH = 23,
+    SVGA_CMD_DRAW_GLYPH_CLIPPED = 24,
+    SVGA_CMD_UPDATE_VERBOSE = 25,
+    SVGA_CMD_SURFACE_FILL = 26,
+    SVGA_CMD_SURFACE_COPY = 27,
+    SVGA_CMD_SURFACE_ALPHA_BLEND = 28,
+    SVGA_CMD_FRONT_ROP_FILL = 29,
+    SVGA_CMD_FENCE = 30,
+};
+
+/* Legal values for the SVGA_REG_CURSOR_ON register in cursor bypass mode */
+enum {
+    SVGA_CURSOR_ON_HIDE = 0,
+    SVGA_CURSOR_ON_SHOW = 1,
+    SVGA_CURSOR_ON_REMOVE_FROM_FB = 2,
+    SVGA_CURSOR_ON_RESTORE_TO_FB = 3,
+};
+
+static inline void vmsvga_update_rect(struct vmsvga_state_s *s,
+                int x, int y, int w, int h)
+{
+    DisplaySurface *surface = qemu_console_surface(s->vga.con);
+    int line;
+    int bypl;
+    int width;
+    int start;
+    uint8_t *src;
+    uint8_t *dst;
+
+    if (x < 0) {
+        fprintf(stderr, "%s: update x was < 0 (%d)\n", __func__, x);
+        w += x;
+        x = 0;
+    }
+    if (w < 0) {
+        fprintf(stderr, "%s: update w was < 0 (%d)\n", __func__, w);
+        w = 0;
+    }
+    if (x + w > surface_width(surface)) {
+        fprintf(stderr, "%s: update width too large x: %d, w: %d\n",
+                __func__, x, w);
+        x = MIN(x, surface_width(surface));
+        w = surface_width(surface) - x;
+    }
+
+    if (y < 0) {
+        fprintf(stderr, "%s: update y was < 0 (%d)\n",  __func__, y);
+        h += y;
+        y = 0;
+    }
+    if (h < 0) {
+        fprintf(stderr, "%s: update h was < 0 (%d)\n",  __func__, h);
+        h = 0;
+    }
+    if (y + h > surface_height(surface)) {
+        fprintf(stderr, "%s: update height too large y: %d, h: %d\n",
+                __func__, y, h);
+        y = MIN(y, surface_height(surface));
+        h = surface_height(surface) - y;
+    }
+
+    bypl = surface_stride(surface);
+    width = surface_bytes_per_pixel(surface) * w;
+    start = surface_bytes_per_pixel(surface) * x + bypl * y;
+    src = s->vga.vram_ptr + start;
+    dst = surface_data(surface) + start;
+
+    for (line = h; line > 0; line--, src += bypl, dst += bypl) {
+        memcpy(dst, src, width);
+    }
+    dpy_gfx_update(s->vga.con, x, y, w, h);
+}
+
+static inline void vmsvga_update_rect_delayed(struct vmsvga_state_s *s,
+                int x, int y, int w, int h)
+{
+    struct vmsvga_rect_s *rect = &s->redraw_fifo[s->redraw_fifo_last++];
+
+    s->redraw_fifo_last &= REDRAW_FIFO_LEN - 1;
+    rect->x = x;
+    rect->y = y;
+    rect->w = w;
+    rect->h = h;
+}
+
+static inline void vmsvga_update_rect_flush(struct vmsvga_state_s *s)
+{
+    struct vmsvga_rect_s *rect;
+
+    if (s->invalidated) {
+        s->redraw_fifo_first = s->redraw_fifo_last;
+        return;
+    }
+    /* Overlapping region updates can be optimised out here - if someone
+     * knows a smart algorithm to do that, please share.  */
+    while (s->redraw_fifo_first != s->redraw_fifo_last) {
+        rect = &s->redraw_fifo[s->redraw_fifo_first++];
+        s->redraw_fifo_first &= REDRAW_FIFO_LEN - 1;
+        vmsvga_update_rect(s, rect->x, rect->y, rect->w, rect->h);
+    }
+}
+
+#ifdef HW_RECT_ACCEL
+static inline void vmsvga_copy_rect(struct vmsvga_state_s *s,
+                int x0, int y0, int x1, int y1, int w, int h)
+{
+    DisplaySurface *surface = qemu_console_surface(s->vga.con);
+    uint8_t *vram = s->vga.vram_ptr;
+    int bypl = surface_stride(surface);
+    int bypp = surface_bytes_per_pixel(surface);
+    int width = bypp * w;
+    int line = h;
+    uint8_t *ptr[2];
+
+    if (y1 > y0) {
+        ptr[0] = vram + bypp * x0 + bypl * (y0 + h - 1);
+        ptr[1] = vram + bypp * x1 + bypl * (y1 + h - 1);
+        for (; line > 0; line --, ptr[0] -= bypl, ptr[1] -= bypl) {
+            memmove(ptr[1], ptr[0], width);
+        }
+    } else {
+        ptr[0] = vram + bypp * x0 + bypl * y0;
+        ptr[1] = vram + bypp * x1 + bypl * y1;
+        for (; line > 0; line --, ptr[0] += bypl, ptr[1] += bypl) {
+            memmove(ptr[1], ptr[0], width);
+        }
+    }
+
+    vmsvga_update_rect_delayed(s, x1, y1, w, h);
+}
+#endif
+
+#ifdef HW_FILL_ACCEL
+static inline void vmsvga_fill_rect(struct vmsvga_state_s *s,
+                uint32_t c, int x, int y, int w, int h)
+{
+    DisplaySurface *surface = qemu_console_surface(s->vga.con);
+    int bypl = surface_stride(surface);
+    int width = surface_bytes_per_pixel(surface) * w;
+    int line = h;
+    int column;
+    uint8_t *fst;
+    uint8_t *dst;
+    uint8_t *src;
+    uint8_t col[4];
+
+    col[0] = c;
+    col[1] = c >> 8;
+    col[2] = c >> 16;
+    col[3] = c >> 24;
+
+    fst = s->vga.vram_ptr + surface_bytes_per_pixel(surface) * x + bypl * y;
+
+    if (line--) {
+        dst = fst;
+        src = col;
+        for (column = width; column > 0; column--) {
+            *(dst++) = *(src++);
+            if (src - col == surface_bytes_per_pixel(surface)) {
+                src = col;
+            }
+        }
+        dst = fst;
+        for (; line > 0; line--) {
+            dst += bypl;
+            memcpy(dst, fst, width);
+        }
+    }
+
+    vmsvga_update_rect_delayed(s, x, y, w, h);
+}
+#endif
+
+struct vmsvga_cursor_definition_s {
+    int width;
+    int height;
+    int id;
+    int bpp;
+    int hot_x;
+    int hot_y;
+    uint32_t mask[1024];
+    uint32_t image[4096];
+};
+
+#define SVGA_BITMAP_SIZE(w, h)          ((((w) + 31) >> 5) * (h))
+#define SVGA_PIXMAP_SIZE(w, h, bpp)     (((((w) * (bpp)) + 31) >> 5) * (h))
+
+#ifdef HW_MOUSE_ACCEL
+static inline void vmsvga_cursor_define(struct vmsvga_state_s *s,
+                struct vmsvga_cursor_definition_s *c)
+{
+    QEMUCursor *qc;
+    int i, pixels;
+
+    qc = cursor_alloc(c->width, c->height);
+    qc->hot_x = c->hot_x;
+    qc->hot_y = c->hot_y;
+    switch (c->bpp) {
+    case 1:
+        cursor_set_mono(qc, 0xffffff, 0x000000, (void *)c->image,
+                        1, (void *)c->mask);
+#ifdef DEBUG
+        cursor_print_ascii_art(qc, "vmware/mono");
+#endif
+        break;
+    case 32:
+        /* fill alpha channel from mask, set color to zero */
+        cursor_set_mono(qc, 0x000000, 0x000000, (void *)c->mask,
+                        1, (void *)c->mask);
+        /* add in rgb values */
+        pixels = c->width * c->height;
+        for (i = 0; i < pixels; i++) {
+            qc->data[i] |= c->image[i] & 0xffffff;
+        }
+#ifdef DEBUG
+        cursor_print_ascii_art(qc, "vmware/32bit");
+#endif
+        break;
+    default:
+        fprintf(stderr, "%s: unhandled bpp %d, using fallback cursor\n",
+                __func__, c->bpp);
+        cursor_put(qc);
+        qc = cursor_builtin_left_ptr();
+    }
+
+    dpy_cursor_define(s->vga.con, qc);
+    cursor_put(qc);
+}
+#endif
+
+#define CMD(f)  le32_to_cpu(s->cmd->f)
+
+static inline int vmsvga_fifo_length(struct vmsvga_state_s *s)
+{
+    int num;
+
+    if (!s->config || !s->enable) {
+        return 0;
+    }
+    num = CMD(next_cmd) - CMD(stop);
+    if (num < 0) {
+        num += CMD(max) - CMD(min);
+    }
+    return num >> 2;
+}
+
+static inline uint32_t vmsvga_fifo_read_raw(struct vmsvga_state_s *s)
+{
+    uint32_t cmd = s->fifo[CMD(stop) >> 2];
+
+    s->cmd->stop = cpu_to_le32(CMD(stop) + 4);
+    if (CMD(stop) >= CMD(max)) {
+        s->cmd->stop = s->cmd->min;
+    }
+    return cmd;
+}
+
+static inline uint32_t vmsvga_fifo_read(struct vmsvga_state_s *s)
+{
+    return le32_to_cpu(vmsvga_fifo_read_raw(s));
+}
+
+static void vmsvga_fifo_run(struct vmsvga_state_s *s)
+{
+    uint32_t cmd, colour;
+    int args, len;
+    int x, y, dx, dy, width, height;
+    struct vmsvga_cursor_definition_s cursor;
+    uint32_t cmd_start;
+
+    len = vmsvga_fifo_length(s);
+    while (len > 0) {
+        /* May need to go back to the start of the command if incomplete */
+        cmd_start = s->cmd->stop;
+
+        switch (cmd = vmsvga_fifo_read(s)) {
+        case SVGA_CMD_UPDATE:
+        case SVGA_CMD_UPDATE_VERBOSE:
+            len -= 5;
+            if (len < 0) {
+                goto rewind;
+            }
+
+            x = vmsvga_fifo_read(s);
+            y = vmsvga_fifo_read(s);
+            width = vmsvga_fifo_read(s);
+            height = vmsvga_fifo_read(s);
+            vmsvga_update_rect_delayed(s, x, y, width, height);
+            break;
+
+        case SVGA_CMD_RECT_FILL:
+            len -= 6;
+            if (len < 0) {
+                goto rewind;
+            }
+
+            colour = vmsvga_fifo_read(s);
+            x = vmsvga_fifo_read(s);
+            y = vmsvga_fifo_read(s);
+            width = vmsvga_fifo_read(s);
+            height = vmsvga_fifo_read(s);
+#ifdef HW_FILL_ACCEL
+            vmsvga_fill_rect(s, colour, x, y, width, height);
+            break;
+#else
+            args = 0;
+            goto badcmd;
+#endif
+
+        case SVGA_CMD_RECT_COPY:
+            len -= 7;
+            if (len < 0) {
+                goto rewind;
+            }
+
+            x = vmsvga_fifo_read(s);
+            y = vmsvga_fifo_read(s);
+            dx = vmsvga_fifo_read(s);
+            dy = vmsvga_fifo_read(s);
+            width = vmsvga_fifo_read(s);
+            height = vmsvga_fifo_read(s);
+#ifdef HW_RECT_ACCEL
+            vmsvga_copy_rect(s, x, y, dx, dy, width, height);
+            break;
+#else
+            args = 0;
+            goto badcmd;
+#endif
+
+        case SVGA_CMD_DEFINE_CURSOR:
+            len -= 8;
+            if (len < 0) {
+                goto rewind;
+            }
+
+            cursor.id = vmsvga_fifo_read(s);
+            cursor.hot_x = vmsvga_fifo_read(s);
+            cursor.hot_y = vmsvga_fifo_read(s);
+            cursor.width = x = vmsvga_fifo_read(s);
+            cursor.height = y = vmsvga_fifo_read(s);
+            vmsvga_fifo_read(s);
+            cursor.bpp = vmsvga_fifo_read(s);
+
+            args = SVGA_BITMAP_SIZE(x, y) + SVGA_PIXMAP_SIZE(x, y, cursor.bpp);
+            if (SVGA_BITMAP_SIZE(x, y) > sizeof cursor.mask ||
+                SVGA_PIXMAP_SIZE(x, y, cursor.bpp) > sizeof cursor.image) {
+                    goto badcmd;
+            }
+
+            len -= args;
+            if (len < 0) {
+                goto rewind;
+            }
+
+            for (args = 0; args < SVGA_BITMAP_SIZE(x, y); args++) {
+                cursor.mask[args] = vmsvga_fifo_read_raw(s);
+            }
+            for (args = 0; args < SVGA_PIXMAP_SIZE(x, y, cursor.bpp); args++) {
+                cursor.image[args] = vmsvga_fifo_read_raw(s);
+            }
+#ifdef HW_MOUSE_ACCEL
+            vmsvga_cursor_define(s, &cursor);
+            break;
+#else
+            args = 0;
+            goto badcmd;
+#endif
+
+        /*
+         * Other commands that we at least know the number of arguments
+         * for so we can avoid FIFO desync if driver uses them illegally.
+         */
+        case SVGA_CMD_DEFINE_ALPHA_CURSOR:
+            len -= 6;
+            if (len < 0) {
+                goto rewind;
+            }
+            vmsvga_fifo_read(s);
+            vmsvga_fifo_read(s);
+            vmsvga_fifo_read(s);
+            x = vmsvga_fifo_read(s);
+            y = vmsvga_fifo_read(s);
+            args = x * y;
+            goto badcmd;
+        case SVGA_CMD_RECT_ROP_FILL:
+            args = 6;
+            goto badcmd;
+        case SVGA_CMD_RECT_ROP_COPY:
+            args = 7;
+            goto badcmd;
+        case SVGA_CMD_DRAW_GLYPH_CLIPPED:
+            len -= 4;
+            if (len < 0) {
+                goto rewind;
+            }
+            vmsvga_fifo_read(s);
+            vmsvga_fifo_read(s);
+            args = 7 + (vmsvga_fifo_read(s) >> 2);
+            goto badcmd;
+        case SVGA_CMD_SURFACE_ALPHA_BLEND:
+            args = 12;
+            goto badcmd;
+
+        /*
+         * Other commands that are not listed as depending on any
+         * CAPABILITIES bits, but are not described in the README either.
+         */
+        case SVGA_CMD_SURFACE_FILL:
+        case SVGA_CMD_SURFACE_COPY:
+        case SVGA_CMD_FRONT_ROP_FILL:
+        case SVGA_CMD_FENCE:
+        case SVGA_CMD_INVALID_CMD:
+            break; /* Nop */
+
+        default:
+            args = 0;
+        badcmd:
+            len -= args;
+            if (len < 0) {
+                goto rewind;
+            }
+            while (args--) {
+                vmsvga_fifo_read(s);
+            }
+            printf("%s: Unknown command 0x%02x in SVGA command FIFO\n",
+                   __func__, cmd);
+            break;
+
+        rewind:
+            s->cmd->stop = cmd_start;
+            break;
+        }
+    }
+
+    s->syncing = 0;
+}
+
+static uint32_t vmsvga_index_read(void *opaque, uint32_t address)
+{
+    struct vmsvga_state_s *s = opaque;
+
+    return s->index;
+}
+
+static void vmsvga_index_write(void *opaque, uint32_t address, uint32_t index)
+{
+    struct vmsvga_state_s *s = opaque;
+
+    s->index = index;
+}
+
+static uint32_t vmsvga_value_read(void *opaque, uint32_t address)
+{
+    uint32_t caps;
+    struct vmsvga_state_s *s = opaque;
+    DisplaySurface *surface = qemu_console_surface(s->vga.con);
+
+    switch (s->index) {
+    case SVGA_REG_ID:
+        return s->svgaid;
+
+    case SVGA_REG_ENABLE:
+        return s->enable;
+
+    case SVGA_REG_WIDTH:
+        return surface_width(surface);
+
+    case SVGA_REG_HEIGHT:
+        return surface_height(surface);
+
+    case SVGA_REG_MAX_WIDTH:
+        return SVGA_MAX_WIDTH;
+
+    case SVGA_REG_MAX_HEIGHT:
+        return SVGA_MAX_HEIGHT;
+
+    case SVGA_REG_DEPTH:
+        return s->depth;
+
+    case SVGA_REG_BITS_PER_PIXEL:
+        return (s->depth + 7) & ~7;
+
+    case SVGA_REG_PSEUDOCOLOR:
+        return 0x0;
+
+    case SVGA_REG_RED_MASK:
+        return surface->pf.rmask;
+
+    case SVGA_REG_GREEN_MASK:
+        return surface->pf.gmask;
+
+    case SVGA_REG_BLUE_MASK:
+        return surface->pf.bmask;
+
+    case SVGA_REG_BYTES_PER_LINE:
+        return s->bypp * s->new_width;
+
+    case SVGA_REG_FB_START: {
+        struct pci_vmsvga_state_s *pci_vmsvga
+            = container_of(s, struct pci_vmsvga_state_s, chip);
+        return pci_get_bar_addr(&pci_vmsvga->card, 1);
+    }
+
+    case SVGA_REG_FB_OFFSET:
+        return 0x0;
+
+    case SVGA_REG_VRAM_SIZE:
+        return s->vga.vram_size; /* No physical VRAM besides the framebuffer */
+
+    case SVGA_REG_FB_SIZE:
+        return s->vga.vram_size;
+
+    case SVGA_REG_CAPABILITIES:
+        caps = SVGA_CAP_NONE;
+#ifdef HW_RECT_ACCEL
+        caps |= SVGA_CAP_RECT_COPY;
+#endif
+#ifdef HW_FILL_ACCEL
+        caps |= SVGA_CAP_RECT_FILL;
+#endif
+#ifdef HW_MOUSE_ACCEL
+        if (dpy_cursor_define_supported(s->vga.con)) {
+            caps |= SVGA_CAP_CURSOR | SVGA_CAP_CURSOR_BYPASS_2 |
+                    SVGA_CAP_CURSOR_BYPASS;
+        }
+#endif
+        return caps;
+
+    case SVGA_REG_MEM_START: {
+        struct pci_vmsvga_state_s *pci_vmsvga
+            = container_of(s, struct pci_vmsvga_state_s, chip);
+        return pci_get_bar_addr(&pci_vmsvga->card, 2);
+    }
+
+    case SVGA_REG_MEM_SIZE:
+        return s->fifo_size;
+
+    case SVGA_REG_CONFIG_DONE:
+        return s->config;
+
+    case SVGA_REG_SYNC:
+    case SVGA_REG_BUSY:
+        return s->syncing;
+
+    case SVGA_REG_GUEST_ID:
+        return s->guest;
+
+    case SVGA_REG_CURSOR_ID:
+        return s->cursor.id;
+
+    case SVGA_REG_CURSOR_X:
+        return s->cursor.x;
+
+    case SVGA_REG_CURSOR_Y:
+        return s->cursor.x;
+
+    case SVGA_REG_CURSOR_ON:
+        return s->cursor.on;
+
+    case SVGA_REG_HOST_BITS_PER_PIXEL:
+        return (s->depth + 7) & ~7;
+
+    case SVGA_REG_SCRATCH_SIZE:
+        return s->scratch_size;
+
+    case SVGA_REG_MEM_REGS:
+    case SVGA_REG_NUM_DISPLAYS:
+    case SVGA_REG_PITCHLOCK:
+    case SVGA_PALETTE_BASE ... SVGA_PALETTE_END:
+        return 0;
+
+    default:
+        if (s->index >= SVGA_SCRATCH_BASE &&
+            s->index < SVGA_SCRATCH_BASE + s->scratch_size) {
+            return s->scratch[s->index - SVGA_SCRATCH_BASE];
+        }
+        printf("%s: Bad register %02x\n", __func__, s->index);
+    }
+
+    return 0;
+}
+
+static void vmsvga_value_write(void *opaque, uint32_t address, uint32_t value)
+{
+    struct vmsvga_state_s *s = opaque;
+
+    switch (s->index) {
+    case SVGA_REG_ID:
+        if (value == SVGA_ID_2 || value == SVGA_ID_1 || value == SVGA_ID_0) {
+            s->svgaid = value;
+        }
+        break;
+
+    case SVGA_REG_ENABLE:
+        s->enable = !!value;
+        s->invalidated = 1;
+        s->vga.invalidate(&s->vga);
+        if (s->enable && s->config) {
+            vga_dirty_log_stop(&s->vga);
+        } else {
+            vga_dirty_log_start(&s->vga);
+        }
+        break;
+
+    case SVGA_REG_WIDTH:
+        if (value <= SVGA_MAX_WIDTH) {
+            s->new_width = value;
+            s->invalidated = 1;
+        } else {
+            printf("%s: Bad width: %i\n", __func__, value);
+        }
+        break;
+
+    case SVGA_REG_HEIGHT:
+        if (value <= SVGA_MAX_HEIGHT) {
+            s->new_height = value;
+            s->invalidated = 1;
+        } else {
+            printf("%s: Bad height: %i\n", __func__, value);
+        }
+        break;
+
+    case SVGA_REG_BITS_PER_PIXEL:
+        if (value != s->depth) {
+            printf("%s: Bad bits per pixel: %i bits\n", __func__, value);
+            s->config = 0;
+        }
+        break;
+
+    case SVGA_REG_CONFIG_DONE:
+        if (value) {
+            s->fifo = (uint32_t *) s->fifo_ptr;
+            /* Check range and alignment.  */
+            if ((CMD(min) | CMD(max) | CMD(next_cmd) | CMD(stop)) & 3) {
+                break;
+            }
+            if (CMD(min) < (uint8_t *) s->cmd->fifo - (uint8_t *) s->fifo) {
+                break;
+            }
+            if (CMD(max) > SVGA_FIFO_SIZE) {
+                break;
+            }
+            if (CMD(max) < CMD(min) + 10 * 1024) {
+                break;
+            }
+            vga_dirty_log_stop(&s->vga);
+        }
+        s->config = !!value;
+        break;
+
+    case SVGA_REG_SYNC:
+        s->syncing = 1;
+        vmsvga_fifo_run(s); /* Or should we just wait for update_display? */
+        break;
+
+    case SVGA_REG_GUEST_ID:
+        s->guest = value;
+#ifdef VERBOSE
+        if (value >= GUEST_OS_BASE && value < GUEST_OS_BASE +
+            ARRAY_SIZE(vmsvga_guest_id)) {
+            printf("%s: guest runs %s.\n", __func__,
+                   vmsvga_guest_id[value - GUEST_OS_BASE]);
+        }
+#endif
+        break;
+
+    case SVGA_REG_CURSOR_ID:
+        s->cursor.id = value;
+        break;
+
+    case SVGA_REG_CURSOR_X:
+        s->cursor.x = value;
+        break;
+
+    case SVGA_REG_CURSOR_Y:
+        s->cursor.y = value;
+        break;
+
+    case SVGA_REG_CURSOR_ON:
+        s->cursor.on |= (value == SVGA_CURSOR_ON_SHOW);
+        s->cursor.on &= (value != SVGA_CURSOR_ON_HIDE);
+#ifdef HW_MOUSE_ACCEL
+        if (value <= SVGA_CURSOR_ON_SHOW) {
+            dpy_mouse_set(s->vga.con, s->cursor.x, s->cursor.y, s->cursor.on);
+        }
+#endif
+        break;
+
+    case SVGA_REG_DEPTH:
+    case SVGA_REG_MEM_REGS:
+    case SVGA_REG_NUM_DISPLAYS:
+    case SVGA_REG_PITCHLOCK:
+    case SVGA_PALETTE_BASE ... SVGA_PALETTE_END:
+        break;
+
+    default:
+        if (s->index >= SVGA_SCRATCH_BASE &&
+                s->index < SVGA_SCRATCH_BASE + s->scratch_size) {
+            s->scratch[s->index - SVGA_SCRATCH_BASE] = value;
+            break;
+        }
+        printf("%s: Bad register %02x\n", __func__, s->index);
+    }
+}
+
+static uint32_t vmsvga_bios_read(void *opaque, uint32_t address)
+{
+    printf("%s: what are we supposed to return?\n", __func__);
+    return 0xcafe;
+}
+
+static void vmsvga_bios_write(void *opaque, uint32_t address, uint32_t data)
+{
+    printf("%s: what are we supposed to do with (%08x)?\n", __func__, data);
+}
+
+static inline void vmsvga_check_size(struct vmsvga_state_s *s)
+{
+    DisplaySurface *surface = qemu_console_surface(s->vga.con);
+
+    if (s->new_width != surface_width(surface) ||
+        s->new_height != surface_height(surface)) {
+        qemu_console_resize(s->vga.con, s->new_width, s->new_height);
+        s->invalidated = 1;
+    }
+}
+
+static void vmsvga_update_display(void *opaque)
+{
+    struct vmsvga_state_s *s = opaque;
+    DisplaySurface *surface = qemu_console_surface(s->vga.con);
+    bool dirty = false;
+
+    if (!s->enable) {
+        s->vga.update(&s->vga);
+        return;
+    }
+
+    vmsvga_check_size(s);
+
+    vmsvga_fifo_run(s);
+    vmsvga_update_rect_flush(s);
+
+    /*
+     * Is it more efficient to look at vram VGA-dirty bits or wait
+     * for the driver to issue SVGA_CMD_UPDATE?
+     */
+    if (memory_region_is_logging(&s->vga.vram)) {
+        vga_sync_dirty_bitmap(&s->vga);
+        dirty = memory_region_get_dirty(&s->vga.vram, 0,
+            surface_stride(surface) * surface_height(surface),
+            DIRTY_MEMORY_VGA);
+    }
+    if (s->invalidated || dirty) {
+        s->invalidated = 0;
+        memcpy(surface_data(surface), s->vga.vram_ptr,
+               surface_stride(surface) * surface_height(surface));
+        dpy_gfx_update(s->vga.con, 0, 0,
+                   surface_width(surface), surface_height(surface));
+    }
+    if (dirty) {
+        memory_region_reset_dirty(&s->vga.vram, 0,
+            surface_stride(surface) * surface_height(surface),
+            DIRTY_MEMORY_VGA);
+    }
+}
+
+static void vmsvga_reset(DeviceState *dev)
+{
+    struct pci_vmsvga_state_s *pci =
+        DO_UPCAST(struct pci_vmsvga_state_s, card.qdev, dev);
+    struct vmsvga_state_s *s = &pci->chip;
+
+    s->index = 0;
+    s->enable = 0;
+    s->config = 0;
+    s->svgaid = SVGA_ID;
+    s->cursor.on = 0;
+    s->redraw_fifo_first = 0;
+    s->redraw_fifo_last = 0;
+    s->syncing = 0;
+
+    vga_dirty_log_start(&s->vga);
+}
+
+static void vmsvga_invalidate_display(void *opaque)
+{
+    struct vmsvga_state_s *s = opaque;
+    if (!s->enable) {
+        s->vga.invalidate(&s->vga);
+        return;
+    }
+
+    s->invalidated = 1;
+}
+
+/* save the vga display in a PPM image even if no display is
+   available */
+static void vmsvga_screen_dump(void *opaque, const char *filename, bool cswitch,
+                               Error **errp)
+{
+    struct vmsvga_state_s *s = opaque;
+    DisplaySurface *surface = qemu_console_surface(s->vga.con);
+
+    if (!s->enable) {
+        s->vga.screen_dump(&s->vga, filename, cswitch, errp);
+        return;
+    }
+
+    if (surface_bits_per_pixel(surface) == 32) {
+        DisplaySurface *ds = qemu_create_displaysurface_from(
+                                 surface_width(surface),
+                                 surface_height(surface),
+                                 32,
+                                 surface_stride(surface),
+                                 s->vga.vram_ptr, false);
+        ppm_save(filename, ds, errp);
+        g_free(ds);
+    }
+}
+
+static void vmsvga_text_update(void *opaque, console_ch_t *chardata)
+{
+    struct vmsvga_state_s *s = opaque;
+
+    if (s->vga.text_update) {
+        s->vga.text_update(&s->vga, chardata);
+    }
+}
+
+static int vmsvga_post_load(void *opaque, int version_id)
+{
+    struct vmsvga_state_s *s = opaque;
+
+    s->invalidated = 1;
+    if (s->config) {
+        s->fifo = (uint32_t *) s->fifo_ptr;
+    }
+    return 0;
+}
+
+static const VMStateDescription vmstate_vmware_vga_internal = {
+    .name = "vmware_vga_internal",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .post_load = vmsvga_post_load,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT32_EQUAL(depth, struct vmsvga_state_s),
+        VMSTATE_INT32(enable, struct vmsvga_state_s),
+        VMSTATE_INT32(config, struct vmsvga_state_s),
+        VMSTATE_INT32(cursor.id, struct vmsvga_state_s),
+        VMSTATE_INT32(cursor.x, struct vmsvga_state_s),
+        VMSTATE_INT32(cursor.y, struct vmsvga_state_s),
+        VMSTATE_INT32(cursor.on, struct vmsvga_state_s),
+        VMSTATE_INT32(index, struct vmsvga_state_s),
+        VMSTATE_VARRAY_INT32(scratch, struct vmsvga_state_s,
+                             scratch_size, 0, vmstate_info_uint32, uint32_t),
+        VMSTATE_INT32(new_width, struct vmsvga_state_s),
+        VMSTATE_INT32(new_height, struct vmsvga_state_s),
+        VMSTATE_UINT32(guest, struct vmsvga_state_s),
+        VMSTATE_UINT32(svgaid, struct vmsvga_state_s),
+        VMSTATE_INT32(syncing, struct vmsvga_state_s),
+        VMSTATE_UNUSED(4), /* was fb_size */
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_vmware_vga = {
+    .name = "vmware_vga",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields      = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(card, struct pci_vmsvga_state_s),
+        VMSTATE_STRUCT(chip, struct pci_vmsvga_state_s, 0,
+                       vmstate_vmware_vga_internal, struct vmsvga_state_s),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void vmsvga_init(struct vmsvga_state_s *s,
+                        MemoryRegion *address_space, MemoryRegion *io)
+{
+    DisplaySurface *surface;
+
+    s->scratch_size = SVGA_SCRATCH_SIZE;
+    s->scratch = g_malloc(s->scratch_size * 4);
+
+    s->vga.con = graphic_console_init(vmsvga_update_display,
+                                      vmsvga_invalidate_display,
+                                      vmsvga_screen_dump,
+                                      vmsvga_text_update, s);
+    surface = qemu_console_surface(s->vga.con);
+
+    s->fifo_size = SVGA_FIFO_SIZE;
+    memory_region_init_ram(&s->fifo_ram, "vmsvga.fifo", s->fifo_size);
+    vmstate_register_ram_global(&s->fifo_ram);
+    s->fifo_ptr = memory_region_get_ram_ptr(&s->fifo_ram);
+
+    vga_common_init(&s->vga);
+    vga_init(&s->vga, address_space, io, true);
+    vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga);
+    /* Save some values here in case they are changed later.
+     * This is suspicious and needs more though why it is needed. */
+    s->depth = surface_bits_per_pixel(surface);
+    s->bypp = surface_bytes_per_pixel(surface);
+}
+
+static uint64_t vmsvga_io_read(void *opaque, hwaddr addr, unsigned size)
+{
+    struct vmsvga_state_s *s = opaque;
+
+    switch (addr) {
+    case SVGA_IO_MUL * SVGA_INDEX_PORT: return vmsvga_index_read(s, addr);
+    case SVGA_IO_MUL * SVGA_VALUE_PORT: return vmsvga_value_read(s, addr);
+    case SVGA_IO_MUL * SVGA_BIOS_PORT: return vmsvga_bios_read(s, addr);
+    default: return -1u;
+    }
+}
+
+static void vmsvga_io_write(void *opaque, hwaddr addr,
+                            uint64_t data, unsigned size)
+{
+    struct vmsvga_state_s *s = opaque;
+
+    switch (addr) {
+    case SVGA_IO_MUL * SVGA_INDEX_PORT:
+        vmsvga_index_write(s, addr, data);
+        break;
+    case SVGA_IO_MUL * SVGA_VALUE_PORT:
+        vmsvga_value_write(s, addr, data);
+        break;
+    case SVGA_IO_MUL * SVGA_BIOS_PORT:
+        vmsvga_bios_write(s, addr, data);
+        break;
+    }
+}
+
+static const MemoryRegionOps vmsvga_io_ops = {
+    .read = vmsvga_io_read,
+    .write = vmsvga_io_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static int pci_vmsvga_initfn(PCIDevice *dev)
+{
+    struct pci_vmsvga_state_s *s =
+        DO_UPCAST(struct pci_vmsvga_state_s, card, dev);
+
+    s->card.config[PCI_CACHE_LINE_SIZE] = 0x08;         /* Cache line size */
+    s->card.config[PCI_LATENCY_TIMER] = 0x40;           /* Latency timer */
+    s->card.config[PCI_INTERRUPT_LINE] = 0xff;          /* End */
+
+    memory_region_init_io(&s->io_bar, &vmsvga_io_ops, &s->chip,
+                          "vmsvga-io", 0x10);
+    memory_region_set_flush_coalesced(&s->io_bar);
+    pci_register_bar(&s->card, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
+
+    vmsvga_init(&s->chip, pci_address_space(dev), pci_address_space_io(dev));
+
+    pci_register_bar(&s->card, 1, PCI_BASE_ADDRESS_MEM_PREFETCH,
+                     &s->chip.vga.vram);
+    pci_register_bar(&s->card, 2, PCI_BASE_ADDRESS_MEM_PREFETCH,
+                     &s->chip.fifo_ram);
+
+    if (!dev->rom_bar) {
+        /* compatibility with pc-0.13 and older */
+        vga_init_vbe(&s->chip.vga, pci_address_space(dev));
+    }
+
+    return 0;
+}
+
+static Property vga_vmware_properties[] = {
+    DEFINE_PROP_UINT32("vgamem_mb", struct pci_vmsvga_state_s,
+                       chip.vga.vram_size_mb, 16),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vmsvga_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->no_hotplug = 1;
+    k->init = pci_vmsvga_initfn;
+    k->romfile = "vgabios-vmware.bin";
+    k->vendor_id = PCI_VENDOR_ID_VMWARE;
+    k->device_id = SVGA_PCI_DEVICE_ID;
+    k->class_id = PCI_CLASS_DISPLAY_VGA;
+    k->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE;
+    k->subsystem_id = SVGA_PCI_DEVICE_ID;
+    dc->reset = vmsvga_reset;
+    dc->vmsd = &vmstate_vmware_vga;
+    dc->props = vga_vmware_properties;
+}
+
+static const TypeInfo vmsvga_info = {
+    .name          = "vmware-svga",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(struct pci_vmsvga_state_s),
+    .class_init    = vmsvga_class_init,
+};
+
+static void vmsvga_register_types(void)
+{
+    type_register_static(&vmsvga_info);
+}
+
+type_init(vmsvga_register_types)
diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c
new file mode 100644 (file)
index 0000000..8e42661
--- /dev/null
@@ -0,0 +1,1021 @@
+/*
+ *  xen paravirt framebuffer backend
+ *
+ *  Copyright IBM, Corp. 2005-2006
+ *  Copyright Red Hat, Inc. 2006-2008
+ *
+ *  Authors:
+ *       Anthony Liguori <aliguori@us.ibm.com>,
+ *       Markus Armbruster <armbru@redhat.com>,
+ *       Daniel P. Berrange <berrange@redhat.com>,
+ *       Pat Campbell <plc@novell.com>,
+ *       Gerd Hoffmann <kraxel@redhat.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; under version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "hw/hw.h"
+#include "ui/console.h"
+#include "char/char.h"
+#include "hw/xen/xen_backend.h"
+
+#include <xen/event_channel.h>
+#include <xen/io/fbif.h>
+#include <xen/io/kbdif.h>
+#include <xen/io/protocols.h>
+
+#ifndef BTN_LEFT
+#define BTN_LEFT 0x110 /* from <linux/input.h> */
+#endif
+
+/* -------------------------------------------------------------------- */
+
+struct common {
+    struct XenDevice  xendev;  /* must be first */
+    void              *page;
+    QemuConsole       *con;
+};
+
+struct XenInput {
+    struct common c;
+    int abs_pointer_wanted; /* Whether guest supports absolute pointer */
+    int button_state;       /* Last seen pointer button state */
+    int extended;
+    QEMUPutMouseEntry *qmouse;
+};
+
+#define UP_QUEUE 8
+
+struct XenFB {
+    struct common     c;
+    size_t            fb_len;
+    int               row_stride;
+    int               depth;
+    int               width;
+    int               height;
+    int               offset;
+    void              *pixels;
+    int               fbpages;
+    int               feature_update;
+    int               refresh_period;
+    int               bug_trigger;
+    int               have_console;
+    int               do_resize;
+
+    struct {
+       int x,y,w,h;
+    } up_rects[UP_QUEUE];
+    int               up_count;
+    int               up_fullscreen;
+};
+
+/* -------------------------------------------------------------------- */
+
+static int common_bind(struct common *c)
+{
+    int mfn;
+
+    if (xenstore_read_fe_int(&c->xendev, "page-ref", &mfn) == -1)
+       return -1;
+    if (xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port) == -1)
+       return -1;
+
+    c->page = xc_map_foreign_range(xen_xc, c->xendev.dom,
+                                  XC_PAGE_SIZE,
+                                  PROT_READ | PROT_WRITE, mfn);
+    if (c->page == NULL)
+       return -1;
+
+    xen_be_bind_evtchn(&c->xendev);
+    xen_be_printf(&c->xendev, 1, "ring mfn %d, remote-port %d, local-port %d\n",
+                 mfn, c->xendev.remote_port, c->xendev.local_port);
+
+    return 0;
+}
+
+static void common_unbind(struct common *c)
+{
+    xen_be_unbind_evtchn(&c->xendev);
+    if (c->page) {
+       munmap(c->page, XC_PAGE_SIZE);
+       c->page = NULL;
+    }
+}
+
+/* -------------------------------------------------------------------- */
+
+#if 0
+/*
+ * These two tables are not needed any more, but left in here
+ * intentionally as documentation, to show how scancode2linux[]
+ * was generated.
+ *
+ * Tables to map from scancode to Linux input layer keycode.
+ * Scancodes are hardware-specific.  These maps assumes a
+ * standard AT or PS/2 keyboard which is what QEMU feeds us.
+ */
+const unsigned char atkbd_set2_keycode[512] = {
+
+     0, 67, 65, 63, 61, 59, 60, 88,  0, 68, 66, 64, 62, 15, 41,117,
+     0, 56, 42, 93, 29, 16,  2,  0,  0,  0, 44, 31, 30, 17,  3,  0,
+     0, 46, 45, 32, 18,  5,  4, 95,  0, 57, 47, 33, 20, 19,  6,183,
+     0, 49, 48, 35, 34, 21,  7,184,  0,  0, 50, 36, 22,  8,  9,185,
+     0, 51, 37, 23, 24, 11, 10,  0,  0, 52, 53, 38, 39, 25, 12,  0,
+     0, 89, 40,  0, 26, 13,  0,  0, 58, 54, 28, 27,  0, 43,  0, 85,
+     0, 86, 91, 90, 92,  0, 14, 94,  0, 79,124, 75, 71,121,  0,  0,
+    82, 83, 80, 76, 77, 72,  1, 69, 87, 78, 81, 74, 55, 73, 70, 99,
+
+      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+    217,100,255,  0, 97,165,  0,  0,156,  0,  0,  0,  0,  0,  0,125,
+    173,114,  0,113,  0,  0,  0,126,128,  0,  0,140,  0,  0,  0,127,
+    159,  0,115,  0,164,  0,  0,116,158,  0,150,166,  0,  0,  0,142,
+    157,  0,  0,  0,  0,  0,  0,  0,155,  0, 98,  0,  0,163,  0,  0,
+    226,  0,  0,  0,  0,  0,  0,  0,  0,255, 96,  0,  0,  0,143,  0,
+      0,  0,  0,  0,  0,  0,  0,  0,  0,107,  0,105,102,  0,  0,112,
+    110,111,108,112,106,103,  0,119,  0,118,109,  0, 99,104,119,  0,
+
+};
+
+const unsigned char atkbd_unxlate_table[128] = {
+
+      0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
+     21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
+     35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
+     50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88,  5,  6,  4, 12,  3,
+     11,  2, 10,  1,  9,119,126,108,117,125,123,107,115,116,121,105,
+    114,122,112,113,127, 96, 97,120,  7, 15, 23, 31, 39, 47, 55, 63,
+     71, 79, 86, 94,  8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
+     19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
+
+};
+#endif
+
+/*
+ * for (i = 0; i < 128; i++) {
+ *     scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]];
+ *     scancode2linux[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80];
+ * }
+ */
+static const unsigned char scancode2linux[512] = {
+      0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
+     16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+     32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+     48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+     64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+     80, 81, 82, 83, 99,  0, 86, 87, 88,117,  0,  0, 95,183,184,185,
+      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+     93,  0,  0, 89,  0,  0, 85, 91, 90, 92,  0, 94,  0,124,121,  0,
+
+      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+    165,  0,  0,  0,  0,  0,  0,  0,  0,163,  0,  0, 96, 97,  0,  0,
+    113,140,164,  0,166,  0,  0,  0,  0,  0,255,  0,  0,  0,114,  0,
+    115,  0,150,  0,  0, 98,255, 99,100,  0,  0,  0,  0,  0,  0,  0,
+      0,  0,  0,  0,  0,119,119,102,103,104,  0,105,112,106,118,107,
+    108,109,110,111,  0,  0,  0,  0,  0,  0,  0,125,126,127,116,142,
+      0,  0,  0,143,  0,217,156,173,128,159,158,157,155,226,  0,112,
+      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+};
+
+/* Send an event to the keyboard frontend driver */
+static int xenfb_kbd_event(struct XenInput *xenfb,
+                          union xenkbd_in_event *event)
+{
+    struct xenkbd_page *page = xenfb->c.page;
+    uint32_t prod;
+
+    if (xenfb->c.xendev.be_state != XenbusStateConnected)
+       return 0;
+    if (!page)
+        return 0;
+
+    prod = page->in_prod;
+    if (prod - page->in_cons == XENKBD_IN_RING_LEN) {
+       errno = EAGAIN;
+       return -1;
+    }
+
+    xen_mb();          /* ensure ring space available */
+    XENKBD_IN_RING_REF(page, prod) = *event;
+    xen_wmb();         /* ensure ring contents visible */
+    page->in_prod = prod + 1;
+    return xen_be_send_notify(&xenfb->c.xendev);
+}
+
+/* Send a keyboard (or mouse button) event */
+static int xenfb_send_key(struct XenInput *xenfb, bool down, int keycode)
+{
+    union xenkbd_in_event event;
+
+    memset(&event, 0, XENKBD_IN_EVENT_SIZE);
+    event.type = XENKBD_TYPE_KEY;
+    event.key.pressed = down ? 1 : 0;
+    event.key.keycode = keycode;
+
+    return xenfb_kbd_event(xenfb, &event);
+}
+
+/* Send a relative mouse movement event */
+static int xenfb_send_motion(struct XenInput *xenfb,
+                            int rel_x, int rel_y, int rel_z)
+{
+    union xenkbd_in_event event;
+
+    memset(&event, 0, XENKBD_IN_EVENT_SIZE);
+    event.type = XENKBD_TYPE_MOTION;
+    event.motion.rel_x = rel_x;
+    event.motion.rel_y = rel_y;
+#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207
+    event.motion.rel_z = rel_z;
+#endif
+
+    return xenfb_kbd_event(xenfb, &event);
+}
+
+/* Send an absolute mouse movement event */
+static int xenfb_send_position(struct XenInput *xenfb,
+                              int abs_x, int abs_y, int z)
+{
+    union xenkbd_in_event event;
+
+    memset(&event, 0, XENKBD_IN_EVENT_SIZE);
+    event.type = XENKBD_TYPE_POS;
+    event.pos.abs_x = abs_x;
+    event.pos.abs_y = abs_y;
+#if __XEN_LATEST_INTERFACE_VERSION__ == 0x00030207
+    event.pos.abs_z = z;
+#endif
+#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030208
+    event.pos.rel_z = z;
+#endif
+
+    return xenfb_kbd_event(xenfb, &event);
+}
+
+/*
+ * Send a key event from the client to the guest OS
+ * QEMU gives us a raw scancode from an AT / PS/2 style keyboard.
+ * We have to turn this into a Linux Input layer keycode.
+ *
+ * Extra complexity from the fact that with extended scancodes
+ * (like those produced by arrow keys) this method gets called
+ * twice, but we only want to send a single event. So we have to
+ * track the '0xe0' scancode state & collapse the extended keys
+ * as needed.
+ *
+ * Wish we could just send scancodes straight to the guest which
+ * already has code for dealing with this...
+ */
+static void xenfb_key_event(void *opaque, int scancode)
+{
+    struct XenInput *xenfb = opaque;
+    int down = 1;
+
+    if (scancode == 0xe0) {
+       xenfb->extended = 1;
+       return;
+    } else if (scancode & 0x80) {
+       scancode &= 0x7f;
+       down = 0;
+    }
+    if (xenfb->extended) {
+       scancode |= 0x80;
+       xenfb->extended = 0;
+    }
+    xenfb_send_key(xenfb, down, scancode2linux[scancode]);
+}
+
+/*
+ * Send a mouse event from the client to the guest OS
+ *
+ * The QEMU mouse can be in either relative, or absolute mode.
+ * Movement is sent separately from button state, which has to
+ * be encoded as virtual key events. We also don't actually get
+ * given any button up/down events, so have to track changes in
+ * the button state.
+ */
+static void xenfb_mouse_event(void *opaque,
+                             int dx, int dy, int dz, int button_state)
+{
+    struct XenInput *xenfb = opaque;
+    DisplaySurface *surface = qemu_console_surface(xenfb->c.con);
+    int dw = surface_width(surface);
+    int dh = surface_height(surface);
+    int i;
+
+    if (xenfb->abs_pointer_wanted)
+       xenfb_send_position(xenfb,
+                           dx * (dw - 1) / 0x7fff,
+                           dy * (dh - 1) / 0x7fff,
+                           dz);
+    else
+       xenfb_send_motion(xenfb, dx, dy, dz);
+
+    for (i = 0 ; i < 8 ; i++) {
+       int lastDown = xenfb->button_state & (1 << i);
+       int down = button_state & (1 << i);
+       if (down == lastDown)
+           continue;
+
+       if (xenfb_send_key(xenfb, down, BTN_LEFT+i) < 0)
+           return;
+    }
+    xenfb->button_state = button_state;
+}
+
+static int input_init(struct XenDevice *xendev)
+{
+    xenstore_write_be_int(xendev, "feature-abs-pointer", 1);
+    return 0;
+}
+
+static int input_initialise(struct XenDevice *xendev)
+{
+    struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
+    int rc;
+
+    if (!in->c.con) {
+        xen_be_printf(xendev, 1, "ds not set (yet)\n");
+        return -1;
+    }
+
+    rc = common_bind(&in->c);
+    if (rc != 0)
+       return rc;
+
+    qemu_add_kbd_event_handler(xenfb_key_event, in);
+    return 0;
+}
+
+static void input_connected(struct XenDevice *xendev)
+{
+    struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
+
+    if (xenstore_read_fe_int(xendev, "request-abs-pointer",
+                             &in->abs_pointer_wanted) == -1) {
+        in->abs_pointer_wanted = 0;
+    }
+
+    if (in->qmouse) {
+        qemu_remove_mouse_event_handler(in->qmouse);
+    }
+    in->qmouse = qemu_add_mouse_event_handler(xenfb_mouse_event, in,
+                                             in->abs_pointer_wanted,
+                                             "Xen PVFB Mouse");
+}
+
+static void input_disconnect(struct XenDevice *xendev)
+{
+    struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
+
+    if (in->qmouse) {
+       qemu_remove_mouse_event_handler(in->qmouse);
+       in->qmouse = NULL;
+    }
+    qemu_add_kbd_event_handler(NULL, NULL);
+    common_unbind(&in->c);
+}
+
+static void input_event(struct XenDevice *xendev)
+{
+    struct XenInput *xenfb = container_of(xendev, struct XenInput, c.xendev);
+    struct xenkbd_page *page = xenfb->c.page;
+
+    /* We don't understand any keyboard events, so just ignore them. */
+    if (page->out_prod == page->out_cons)
+       return;
+    page->out_cons = page->out_prod;
+    xen_be_send_notify(&xenfb->c.xendev);
+}
+
+/* -------------------------------------------------------------------- */
+
+static void xenfb_copy_mfns(int mode, int count, unsigned long *dst, void *src)
+{
+    uint32_t *src32 = src;
+    uint64_t *src64 = src;
+    int i;
+
+    for (i = 0; i < count; i++)
+       dst[i] = (mode == 32) ? src32[i] : src64[i];
+}
+
+static int xenfb_map_fb(struct XenFB *xenfb)
+{
+    struct xenfb_page *page = xenfb->c.page;
+    char *protocol = xenfb->c.xendev.protocol;
+    int n_fbdirs;
+    unsigned long *pgmfns = NULL;
+    unsigned long *fbmfns = NULL;
+    void *map, *pd;
+    int mode, ret = -1;
+
+    /* default to native */
+    pd = page->pd;
+    mode = sizeof(unsigned long) * 8;
+
+    if (!protocol) {
+       /*
+        * Undefined protocol, some guesswork needed.
+        *
+        * Old frontends which don't set the protocol use
+        * one page directory only, thus pd[1] must be zero.
+        * pd[1] of the 32bit struct layout and the lower
+        * 32 bits of pd[0] of the 64bit struct layout have
+        * the same location, so we can check that ...
+        */
+       uint32_t *ptr32 = NULL;
+       uint32_t *ptr64 = NULL;
+#if defined(__i386__)
+       ptr32 = (void*)page->pd;
+       ptr64 = ((void*)page->pd) + 4;
+#elif defined(__x86_64__)
+       ptr32 = ((void*)page->pd) - 4;
+       ptr64 = (void*)page->pd;
+#endif
+       if (ptr32) {
+           if (ptr32[1] == 0) {
+               mode = 32;
+               pd   = ptr32;
+           } else {
+               mode = 64;
+               pd   = ptr64;
+           }
+       }
+#if defined(__x86_64__)
+    } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_32) == 0) {
+       /* 64bit dom0, 32bit domU */
+       mode = 32;
+       pd   = ((void*)page->pd) - 4;
+#elif defined(__i386__)
+    } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_64) == 0) {
+       /* 32bit dom0, 64bit domU */
+       mode = 64;
+       pd   = ((void*)page->pd) + 4;
+#endif
+    }
+
+    if (xenfb->pixels) {
+        munmap(xenfb->pixels, xenfb->fbpages * XC_PAGE_SIZE);
+        xenfb->pixels = NULL;
+    }
+
+    xenfb->fbpages = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
+    n_fbdirs = xenfb->fbpages * mode / 8;
+    n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
+
+    pgmfns = g_malloc0(sizeof(unsigned long) * n_fbdirs);
+    fbmfns = g_malloc0(sizeof(unsigned long) * xenfb->fbpages);
+
+    xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd);
+    map = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom,
+                              PROT_READ, pgmfns, n_fbdirs);
+    if (map == NULL)
+       goto out;
+    xenfb_copy_mfns(mode, xenfb->fbpages, fbmfns, map);
+    munmap(map, n_fbdirs * XC_PAGE_SIZE);
+
+    xenfb->pixels = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom,
+                                        PROT_READ | PROT_WRITE, fbmfns, xenfb->fbpages);
+    if (xenfb->pixels == NULL)
+       goto out;
+
+    ret = 0; /* all is fine */
+
+out:
+    g_free(pgmfns);
+    g_free(fbmfns);
+    return ret;
+}
+
+static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim,
+                             int width, int height, int depth,
+                             size_t fb_len, int offset, int row_stride)
+{
+    size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd);
+    size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz;
+    size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz;
+    size_t fb_len_max = fb_pages * XC_PAGE_SIZE;
+    int max_width, max_height;
+
+    if (fb_len_lim > fb_len_max) {
+       xen_be_printf(&xenfb->c.xendev, 0, "fb size limit %zu exceeds %zu, corrected\n",
+                     fb_len_lim, fb_len_max);
+       fb_len_lim = fb_len_max;
+    }
+    if (fb_len_lim && fb_len > fb_len_lim) {
+       xen_be_printf(&xenfb->c.xendev, 0, "frontend fb size %zu limited to %zu\n",
+                     fb_len, fb_len_lim);
+       fb_len = fb_len_lim;
+    }
+    if (depth != 8 && depth != 16 && depth != 24 && depth != 32) {
+       xen_be_printf(&xenfb->c.xendev, 0, "can't handle frontend fb depth %d\n",
+                     depth);
+       return -1;
+    }
+    if (row_stride <= 0 || row_stride > fb_len) {
+       xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend stride %d\n", row_stride);
+       return -1;
+    }
+    max_width = row_stride / (depth / 8);
+    if (width < 0 || width > max_width) {
+       xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend width %d limited to %d\n",
+                     width, max_width);
+       width = max_width;
+    }
+    if (offset < 0 || offset >= fb_len) {
+       xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend offset %d (max %zu)\n",
+                     offset, fb_len - 1);
+       return -1;
+    }
+    max_height = (fb_len - offset) / row_stride;
+    if (height < 0 || height > max_height) {
+       xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend height %d limited to %d\n",
+                     height, max_height);
+       height = max_height;
+    }
+    xenfb->fb_len = fb_len;
+    xenfb->row_stride = row_stride;
+    xenfb->depth = depth;
+    xenfb->width = width;
+    xenfb->height = height;
+    xenfb->offset = offset;
+    xenfb->up_fullscreen = 1;
+    xenfb->do_resize = 1;
+    xen_be_printf(&xenfb->c.xendev, 1, "framebuffer %dx%dx%d offset %d stride %d\n",
+                 width, height, depth, offset, row_stride);
+    return 0;
+}
+
+/* A convenient function for munging pixels between different depths */
+#define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB)                        \
+    for (line = y ; line < (y+h) ; line++) {                           \
+       SRC_T *src = (SRC_T *)(xenfb->pixels                            \
+                              + xenfb->offset                          \
+                              + (line * xenfb->row_stride)             \
+                              + (x * xenfb->depth / 8));               \
+       DST_T *dst = (DST_T *)(data                                     \
+                              + (line * linesize)                      \
+                              + (x * bpp / 8));                        \
+       int col;                                                        \
+       const int RSS = 32 - (RSB + GSB + BSB);                         \
+       const int GSS = 32 - (GSB + BSB);                               \
+       const int BSS = 32 - (BSB);                                     \
+       const uint32_t RSM = (~0U) << (32 - RSB);                       \
+       const uint32_t GSM = (~0U) << (32 - GSB);                       \
+       const uint32_t BSM = (~0U) << (32 - BSB);                       \
+       const int RDS = 32 - (RDB + GDB + BDB);                         \
+       const int GDS = 32 - (GDB + BDB);                               \
+       const int BDS = 32 - (BDB);                                     \
+       const uint32_t RDM = (~0U) << (32 - RDB);                       \
+       const uint32_t GDM = (~0U) << (32 - GDB);                       \
+       const uint32_t BDM = (~0U) << (32 - BDB);                       \
+       for (col = x ; col < (x+w) ; col++) {                           \
+           uint32_t spix = *src;                                       \
+           *dst = (((spix << RSS) & RSM & RDM) >> RDS) |               \
+               (((spix << GSS) & GSM & GDM) >> GDS) |                  \
+               (((spix << BSS) & BSM & BDM) >> BDS);                   \
+           src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8);   \
+           dst = (DST_T *) ((unsigned long) dst + bpp / 8);            \
+       }                                                               \
+    }
+
+
+/*
+ * This copies data from the guest framebuffer region, into QEMU's
+ * displaysurface. qemu uses 16 or 32 bpp.  In case the pv framebuffer
+ * uses something else we must convert and copy, otherwise we can
+ * supply the buffer directly and no thing here.
+ */
+static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h)
+{
+    DisplaySurface *surface = qemu_console_surface(xenfb->c.con);
+    int line, oops = 0;
+    int bpp = surface_bits_per_pixel(surface);
+    int linesize = surface_stride(surface);
+    uint8_t *data = surface_data(surface);
+
+    if (!is_buffer_shared(surface)) {
+        switch (xenfb->depth) {
+        case 8:
+            if (bpp == 16) {
+                BLT(uint8_t, uint16_t,   3, 3, 2,   5, 6, 5);
+            } else if (bpp == 32) {
+                BLT(uint8_t, uint32_t,   3, 3, 2,   8, 8, 8);
+            } else {
+                oops = 1;
+            }
+            break;
+        case 24:
+            if (bpp == 16) {
+                BLT(uint32_t, uint16_t,  8, 8, 8,   5, 6, 5);
+            } else if (bpp == 32) {
+                BLT(uint32_t, uint32_t,  8, 8, 8,   8, 8, 8);
+            } else {
+                oops = 1;
+            }
+            break;
+        default:
+            oops = 1;
+       }
+    }
+    if (oops) /* should not happen */
+        xen_be_printf(&xenfb->c.xendev, 0, "%s: oops: convert %d -> %d bpp?\n",
+                      __FUNCTION__, xenfb->depth, bpp);
+
+    dpy_gfx_update(xenfb->c.con, x, y, w, h);
+}
+
+#if 0 /* def XENFB_TYPE_REFRESH_PERIOD */
+static int xenfb_queue_full(struct XenFB *xenfb)
+{
+    struct xenfb_page *page = xenfb->c.page;
+    uint32_t cons, prod;
+
+    if (!page)
+        return 1;
+
+    prod = page->in_prod;
+    cons = page->in_cons;
+    return prod - cons == XENFB_IN_RING_LEN;
+}
+
+static void xenfb_send_event(struct XenFB *xenfb, union xenfb_in_event *event)
+{
+    uint32_t prod;
+    struct xenfb_page *page = xenfb->c.page;
+
+    prod = page->in_prod;
+    /* caller ensures !xenfb_queue_full() */
+    xen_mb();                   /* ensure ring space available */
+    XENFB_IN_RING_REF(page, prod) = *event;
+    xen_wmb();                  /* ensure ring contents visible */
+    page->in_prod = prod + 1;
+
+    xen_be_send_notify(&xenfb->c.xendev);
+}
+
+static void xenfb_send_refresh_period(struct XenFB *xenfb, int period)
+{
+    union xenfb_in_event event;
+
+    memset(&event, 0, sizeof(event));
+    event.type = XENFB_TYPE_REFRESH_PERIOD;
+    event.refresh_period.period = period;
+    xenfb_send_event(xenfb, &event);
+}
+#endif
+
+/*
+ * Periodic update of display.
+ * Also transmit the refresh interval to the frontend.
+ *
+ * Never ever do any qemu display operations
+ * (resize, screen update) outside this function.
+ * Our screen might be inactive.  When asked for
+ * an update we know it is active.
+ */
+static void xenfb_update(void *opaque)
+{
+    struct XenFB *xenfb = opaque;
+    DisplaySurface *surface;
+    int i;
+
+    if (xenfb->c.xendev.be_state != XenbusStateConnected)
+        return;
+
+    if (xenfb->feature_update) {
+#if 0 /* XENFB_TYPE_REFRESH_PERIOD */
+        struct DisplayChangeListener *l;
+        int period = 99999999;
+        int idle = 1;
+
+       if (xenfb_queue_full(xenfb))
+           return;
+
+        QLIST_FOREACH(l, &xenfb->c.ds->listeners, next) {
+            if (l->idle)
+                continue;
+            idle = 0;
+            if (!l->gui_timer_interval) {
+                if (period > GUI_REFRESH_INTERVAL)
+                    period = GUI_REFRESH_INTERVAL;
+            } else {
+                if (period > l->gui_timer_interval)
+                    period = l->gui_timer_interval;
+            }
+        }
+        if (idle)
+           period = XENFB_NO_REFRESH;
+
+       if (xenfb->refresh_period != period) {
+           xenfb_send_refresh_period(xenfb, period);
+           xenfb->refresh_period = period;
+            xen_be_printf(&xenfb->c.xendev, 1, "refresh period: %d\n", period);
+       }
+#else
+       ; /* nothing */
+#endif
+    } else {
+       /* we don't get update notifications, thus use the
+        * sledge hammer approach ... */
+       xenfb->up_fullscreen = 1;
+    }
+
+    /* resize if needed */
+    if (xenfb->do_resize) {
+        xenfb->do_resize = 0;
+        switch (xenfb->depth) {
+        case 16:
+        case 32:
+            /* console.c supported depth -> buffer can be used directly */
+            surface = qemu_create_displaysurface_from
+                (xenfb->width, xenfb->height, xenfb->depth,
+                 xenfb->row_stride, xenfb->pixels + xenfb->offset,
+                 false);
+            break;
+        default:
+            /* we must convert stuff */
+            surface = qemu_create_displaysurface(xenfb->width, xenfb->height);
+            break;
+        }
+        dpy_gfx_replace_surface(xenfb->c.con, surface);
+        xen_be_printf(&xenfb->c.xendev, 1, "update: resizing: %dx%d @ %d bpp%s\n",
+                      xenfb->width, xenfb->height, xenfb->depth,
+                      is_buffer_shared(surface) ? " (shared)" : "");
+        xenfb->up_fullscreen = 1;
+    }
+
+    /* run queued updates */
+    if (xenfb->up_fullscreen) {
+       xen_be_printf(&xenfb->c.xendev, 3, "update: fullscreen\n");
+       xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height);
+    } else if (xenfb->up_count) {
+       xen_be_printf(&xenfb->c.xendev, 3, "update: %d rects\n", xenfb->up_count);
+       for (i = 0; i < xenfb->up_count; i++)
+           xenfb_guest_copy(xenfb,
+                            xenfb->up_rects[i].x,
+                            xenfb->up_rects[i].y,
+                            xenfb->up_rects[i].w,
+                            xenfb->up_rects[i].h);
+    } else {
+       xen_be_printf(&xenfb->c.xendev, 3, "update: nothing\n");
+    }
+    xenfb->up_count = 0;
+    xenfb->up_fullscreen = 0;
+}
+
+/* QEMU display state changed, so refresh the framebuffer copy */
+static void xenfb_invalidate(void *opaque)
+{
+    struct XenFB *xenfb = opaque;
+    xenfb->up_fullscreen = 1;
+}
+
+static void xenfb_handle_events(struct XenFB *xenfb)
+{
+    uint32_t prod, cons;
+    struct xenfb_page *page = xenfb->c.page;
+
+    prod = page->out_prod;
+    if (prod == page->out_cons)
+       return;
+    xen_rmb();         /* ensure we see ring contents up to prod */
+    for (cons = page->out_cons; cons != prod; cons++) {
+       union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons);
+       int x, y, w, h;
+
+       switch (event->type) {
+       case XENFB_TYPE_UPDATE:
+           if (xenfb->up_count == UP_QUEUE)
+               xenfb->up_fullscreen = 1;
+           if (xenfb->up_fullscreen)
+               break;
+           x = MAX(event->update.x, 0);
+           y = MAX(event->update.y, 0);
+           w = MIN(event->update.width, xenfb->width - x);
+           h = MIN(event->update.height, xenfb->height - y);
+           if (w < 0 || h < 0) {
+                xen_be_printf(&xenfb->c.xendev, 1, "bogus update ignored\n");
+               break;
+           }
+           if (x != event->update.x ||
+                y != event->update.y ||
+               w != event->update.width ||
+               h != event->update.height) {
+                xen_be_printf(&xenfb->c.xendev, 1, "bogus update clipped\n");
+           }
+           if (w == xenfb->width && h > xenfb->height / 2) {
+               /* scroll detector: updated more than 50% of the lines,
+                * don't bother keeping track of the rectangles then */
+               xenfb->up_fullscreen = 1;
+           } else {
+               xenfb->up_rects[xenfb->up_count].x = x;
+               xenfb->up_rects[xenfb->up_count].y = y;
+               xenfb->up_rects[xenfb->up_count].w = w;
+               xenfb->up_rects[xenfb->up_count].h = h;
+               xenfb->up_count++;
+           }
+           break;
+#ifdef XENFB_TYPE_RESIZE
+       case XENFB_TYPE_RESIZE:
+           if (xenfb_configure_fb(xenfb, xenfb->fb_len,
+                                  event->resize.width,
+                                  event->resize.height,
+                                  event->resize.depth,
+                                  xenfb->fb_len,
+                                  event->resize.offset,
+                                  event->resize.stride) < 0)
+               break;
+           xenfb_invalidate(xenfb);
+           break;
+#endif
+       }
+    }
+    xen_mb();          /* ensure we're done with ring contents */
+    page->out_cons = cons;
+}
+
+static int fb_init(struct XenDevice *xendev)
+{
+    struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
+
+    fb->refresh_period = -1;
+
+#ifdef XENFB_TYPE_RESIZE
+    xenstore_write_be_int(xendev, "feature-resize", 1);
+#endif
+    return 0;
+}
+
+static int fb_initialise(struct XenDevice *xendev)
+{
+    struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
+    struct xenfb_page *fb_page;
+    int videoram;
+    int rc;
+
+    if (xenstore_read_fe_int(xendev, "videoram", &videoram) == -1)
+       videoram = 0;
+
+    rc = common_bind(&fb->c);
+    if (rc != 0)
+       return rc;
+
+    fb_page = fb->c.page;
+    rc = xenfb_configure_fb(fb, videoram * 1024 * 1024U,
+                           fb_page->width, fb_page->height, fb_page->depth,
+                           fb_page->mem_length, 0, fb_page->line_length);
+    if (rc != 0)
+       return rc;
+
+    rc = xenfb_map_fb(fb);
+    if (rc != 0)
+       return rc;
+
+#if 0  /* handled in xen_init_display() for now */
+    if (!fb->have_console) {
+        fb->c.ds = graphic_console_init(xenfb_update,
+                                        xenfb_invalidate,
+                                        NULL,
+                                        NULL,
+                                        fb);
+        fb->have_console = 1;
+    }
+#endif
+
+    if (xenstore_read_fe_int(xendev, "feature-update", &fb->feature_update) == -1)
+       fb->feature_update = 0;
+    if (fb->feature_update)
+       xenstore_write_be_int(xendev, "request-update", 1);
+
+    xen_be_printf(xendev, 1, "feature-update=%d, videoram=%d\n",
+                 fb->feature_update, videoram);
+    return 0;
+}
+
+static void fb_disconnect(struct XenDevice *xendev)
+{
+    struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
+
+    /*
+     * FIXME: qemu can't un-init gfx display (yet?).
+     *   Replacing the framebuffer with anonymous shared memory
+     *   instead.  This releases the guest pages and keeps qemu happy.
+     */
+    fb->pixels = mmap(fb->pixels, fb->fbpages * XC_PAGE_SIZE,
+                      PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON,
+                      -1, 0);
+    common_unbind(&fb->c);
+    fb->feature_update = 0;
+    fb->bug_trigger    = 0;
+}
+
+static void fb_frontend_changed(struct XenDevice *xendev, const char *node)
+{
+    struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
+
+    /*
+     * Set state to Connected *again* once the frontend switched
+     * to connected.  We must trigger the watch a second time to
+     * workaround a frontend bug.
+     */
+    if (fb->bug_trigger == 0 && strcmp(node, "state") == 0 &&
+        xendev->fe_state == XenbusStateConnected &&
+        xendev->be_state == XenbusStateConnected) {
+        xen_be_printf(xendev, 2, "re-trigger connected (frontend bug)\n");
+        xen_be_set_state(xendev, XenbusStateConnected);
+        fb->bug_trigger = 1; /* only once */
+    }
+}
+
+static void fb_event(struct XenDevice *xendev)
+{
+    struct XenFB *xenfb = container_of(xendev, struct XenFB, c.xendev);
+
+    xenfb_handle_events(xenfb);
+    xen_be_send_notify(&xenfb->c.xendev);
+}
+
+/* -------------------------------------------------------------------- */
+
+struct XenDevOps xen_kbdmouse_ops = {
+    .size       = sizeof(struct XenInput),
+    .init       = input_init,
+    .initialise = input_initialise,
+    .connected  = input_connected,
+    .disconnect = input_disconnect,
+    .event      = input_event,
+};
+
+struct XenDevOps xen_framebuffer_ops = {
+    .size       = sizeof(struct XenFB),
+    .init       = fb_init,
+    .initialise = fb_initialise,
+    .disconnect = fb_disconnect,
+    .event      = fb_event,
+    .frontend_changed = fb_frontend_changed,
+};
+
+/*
+ * FIXME/TODO: Kill this.
+ * Temporary needed while DisplayState reorganization is in flight.
+ */
+void xen_init_display(int domid)
+{
+    struct XenDevice *xfb, *xin;
+    struct XenFB *fb;
+    struct XenInput *in;
+    int i = 0;
+
+wait_more:
+    i++;
+    main_loop_wait(true);
+    xfb = xen_be_find_xendev("vfb", domid, 0);
+    xin = xen_be_find_xendev("vkbd", domid, 0);
+    if (!xfb || !xin) {
+        if (i < 256) {
+            usleep(10000);
+            goto wait_more;
+        }
+        xen_be_printf(NULL, 1, "displaystate setup failed\n");
+        return;
+    }
+
+    /* vfb */
+    fb = container_of(xfb, struct XenFB, c.xendev);
+    fb->c.con = graphic_console_init(xenfb_update,
+                                     xenfb_invalidate,
+                                     NULL,
+                                     NULL,
+                                     fb);
+    fb->have_console = 1;
+
+    /* vkbd */
+    in = container_of(xin, struct XenInput, c.xendev);
+    in->c.con = fb->c.con;
+
+    /* retry ->init() */
+    xen_be_check_state(xin);
+    xen_be_check_state(xfb);
+}
diff --git a/hw/dma.c b/hw/dma.c
deleted file mode 100644 (file)
index eb60d45..0000000
--- a/hw/dma.c
+++ /dev/null
@@ -1,600 +0,0 @@
-/*
- * QEMU DMA emulation
- *
- * Copyright (c) 2003-2004 Vassili Karpov (malc)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/isa/isa.h"
-#include "qemu/main-loop.h"
-
-/* #define DEBUG_DMA */
-
-#define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__)
-#ifdef DEBUG_DMA
-#define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__)
-#define ldebug(...) fprintf (stderr, "dma: " __VA_ARGS__)
-#else
-#define linfo(...)
-#define ldebug(...)
-#endif
-
-struct dma_regs {
-    int now[2];
-    uint16_t base[2];
-    uint8_t mode;
-    uint8_t page;
-    uint8_t pageh;
-    uint8_t dack;
-    uint8_t eop;
-    DMA_transfer_handler transfer_handler;
-    void *opaque;
-};
-
-#define ADDR 0
-#define COUNT 1
-
-static struct dma_cont {
-    uint8_t status;
-    uint8_t command;
-    uint8_t mask;
-    uint8_t flip_flop;
-    int dshift;
-    struct dma_regs regs[4];
-    qemu_irq *cpu_request_exit;
-    MemoryRegion channel_io;
-    MemoryRegion cont_io;
-} dma_controllers[2];
-
-enum {
-    CMD_MEMORY_TO_MEMORY = 0x01,
-    CMD_FIXED_ADDRESS    = 0x02,
-    CMD_BLOCK_CONTROLLER = 0x04,
-    CMD_COMPRESSED_TIME  = 0x08,
-    CMD_CYCLIC_PRIORITY  = 0x10,
-    CMD_EXTENDED_WRITE   = 0x20,
-    CMD_LOW_DREQ         = 0x40,
-    CMD_LOW_DACK         = 0x80,
-    CMD_NOT_SUPPORTED    = CMD_MEMORY_TO_MEMORY | CMD_FIXED_ADDRESS
-    | CMD_COMPRESSED_TIME | CMD_CYCLIC_PRIORITY | CMD_EXTENDED_WRITE
-    | CMD_LOW_DREQ | CMD_LOW_DACK
-
-};
-
-static void DMA_run (void);
-
-static int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0};
-
-static void write_page (void *opaque, uint32_t nport, uint32_t data)
-{
-    struct dma_cont *d = opaque;
-    int ichan;
-
-    ichan = channels[nport & 7];
-    if (-1 == ichan) {
-        dolog ("invalid channel %#x %#x\n", nport, data);
-        return;
-    }
-    d->regs[ichan].page = data;
-}
-
-static void write_pageh (void *opaque, uint32_t nport, uint32_t data)
-{
-    struct dma_cont *d = opaque;
-    int ichan;
-
-    ichan = channels[nport & 7];
-    if (-1 == ichan) {
-        dolog ("invalid channel %#x %#x\n", nport, data);
-        return;
-    }
-    d->regs[ichan].pageh = data;
-}
-
-static uint32_t read_page (void *opaque, uint32_t nport)
-{
-    struct dma_cont *d = opaque;
-    int ichan;
-
-    ichan = channels[nport & 7];
-    if (-1 == ichan) {
-        dolog ("invalid channel read %#x\n", nport);
-        return 0;
-    }
-    return d->regs[ichan].page;
-}
-
-static uint32_t read_pageh (void *opaque, uint32_t nport)
-{
-    struct dma_cont *d = opaque;
-    int ichan;
-
-    ichan = channels[nport & 7];
-    if (-1 == ichan) {
-        dolog ("invalid channel read %#x\n", nport);
-        return 0;
-    }
-    return d->regs[ichan].pageh;
-}
-
-static inline void init_chan (struct dma_cont *d, int ichan)
-{
-    struct dma_regs *r;
-
-    r = d->regs + ichan;
-    r->now[ADDR] = r->base[ADDR] << d->dshift;
-    r->now[COUNT] = 0;
-}
-
-static inline int getff (struct dma_cont *d)
-{
-    int ff;
-
-    ff = d->flip_flop;
-    d->flip_flop = !ff;
-    return ff;
-}
-
-static uint64_t read_chan(void *opaque, hwaddr nport, unsigned size)
-{
-    struct dma_cont *d = opaque;
-    int ichan, nreg, iport, ff, val, dir;
-    struct dma_regs *r;
-
-    iport = (nport >> d->dshift) & 0x0f;
-    ichan = iport >> 1;
-    nreg = iport & 1;
-    r = d->regs + ichan;
-
-    dir = ((r->mode >> 5) & 1) ? -1 : 1;
-    ff = getff (d);
-    if (nreg)
-        val = (r->base[COUNT] << d->dshift) - r->now[COUNT];
-    else
-        val = r->now[ADDR] + r->now[COUNT] * dir;
-
-    ldebug ("read_chan %#x -> %d\n", iport, val);
-    return (val >> (d->dshift + (ff << 3))) & 0xff;
-}
-
-static void write_chan(void *opaque, hwaddr nport, uint64_t data,
-                       unsigned size)
-{
-    struct dma_cont *d = opaque;
-    int iport, ichan, nreg;
-    struct dma_regs *r;
-
-    iport = (nport >> d->dshift) & 0x0f;
-    ichan = iport >> 1;
-    nreg = iport & 1;
-    r = d->regs + ichan;
-    if (getff (d)) {
-        r->base[nreg] = (r->base[nreg] & 0xff) | ((data << 8) & 0xff00);
-        init_chan (d, ichan);
-    } else {
-        r->base[nreg] = (r->base[nreg] & 0xff00) | (data & 0xff);
-    }
-}
-
-static void write_cont(void *opaque, hwaddr nport, uint64_t data,
-                       unsigned size)
-{
-    struct dma_cont *d = opaque;
-    int iport, ichan = 0;
-
-    iport = (nport >> d->dshift) & 0x0f;
-    switch (iport) {
-    case 0x00:                  /* command */
-        if ((data != 0) && (data & CMD_NOT_SUPPORTED)) {
-            dolog("command %"PRIx64" not supported\n", data);
-            return;
-        }
-        d->command = data;
-        break;
-
-    case 0x01:
-        ichan = data & 3;
-        if (data & 4) {
-            d->status |= 1 << (ichan + 4);
-        }
-        else {
-            d->status &= ~(1 << (ichan + 4));
-        }
-        d->status &= ~(1 << ichan);
-        DMA_run();
-        break;
-
-    case 0x02:                  /* single mask */
-        if (data & 4)
-            d->mask |= 1 << (data & 3);
-        else
-            d->mask &= ~(1 << (data & 3));
-        DMA_run();
-        break;
-
-    case 0x03:                  /* mode */
-        {
-            ichan = data & 3;
-#ifdef DEBUG_DMA
-            {
-                int op, ai, dir, opmode;
-                op = (data >> 2) & 3;
-                ai = (data >> 4) & 1;
-                dir = (data >> 5) & 1;
-                opmode = (data >> 6) & 3;
-
-                linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n",
-                       ichan, op, ai, dir, opmode);
-            }
-#endif
-            d->regs[ichan].mode = data;
-            break;
-        }
-
-    case 0x04:                  /* clear flip flop */
-        d->flip_flop = 0;
-        break;
-
-    case 0x05:                  /* reset */
-        d->flip_flop = 0;
-        d->mask = ~0;
-        d->status = 0;
-        d->command = 0;
-        break;
-
-    case 0x06:                  /* clear mask for all channels */
-        d->mask = 0;
-        DMA_run();
-        break;
-
-    case 0x07:                  /* write mask for all channels */
-        d->mask = data;
-        DMA_run();
-        break;
-
-    default:
-        dolog ("unknown iport %#x\n", iport);
-        break;
-    }
-
-#ifdef DEBUG_DMA
-    if (0xc != iport) {
-        linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n",
-               nport, ichan, data);
-    }
-#endif
-}
-
-static uint64_t read_cont(void *opaque, hwaddr nport, unsigned size)
-{
-    struct dma_cont *d = opaque;
-    int iport, val;
-
-    iport = (nport >> d->dshift) & 0x0f;
-    switch (iport) {
-    case 0x00:                  /* status */
-        val = d->status;
-        d->status &= 0xf0;
-        break;
-    case 0x01:                  /* mask */
-        val = d->mask;
-        break;
-    default:
-        val = 0;
-        break;
-    }
-
-    ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val);
-    return val;
-}
-
-int DMA_get_channel_mode (int nchan)
-{
-    return dma_controllers[nchan > 3].regs[nchan & 3].mode;
-}
-
-void DMA_hold_DREQ (int nchan)
-{
-    int ncont, ichan;
-
-    ncont = nchan > 3;
-    ichan = nchan & 3;
-    linfo ("held cont=%d chan=%d\n", ncont, ichan);
-    dma_controllers[ncont].status |= 1 << (ichan + 4);
-    DMA_run();
-}
-
-void DMA_release_DREQ (int nchan)
-{
-    int ncont, ichan;
-
-    ncont = nchan > 3;
-    ichan = nchan & 3;
-    linfo ("released cont=%d chan=%d\n", ncont, ichan);
-    dma_controllers[ncont].status &= ~(1 << (ichan + 4));
-    DMA_run();
-}
-
-static void channel_run (int ncont, int ichan)
-{
-    int n;
-    struct dma_regs *r = &dma_controllers[ncont].regs[ichan];
-#ifdef DEBUG_DMA
-    int dir, opmode;
-
-    dir = (r->mode >> 5) & 1;
-    opmode = (r->mode >> 6) & 3;
-
-    if (dir) {
-        dolog ("DMA in address decrement mode\n");
-    }
-    if (opmode != 1) {
-        dolog ("DMA not in single mode select %#x\n", opmode);
-    }
-#endif
-
-    n = r->transfer_handler (r->opaque, ichan + (ncont << 2),
-                             r->now[COUNT], (r->base[COUNT] + 1) << ncont);
-    r->now[COUNT] = n;
-    ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont);
-}
-
-static QEMUBH *dma_bh;
-
-static void DMA_run (void)
-{
-    struct dma_cont *d;
-    int icont, ichan;
-    int rearm = 0;
-    static int running = 0;
-
-    if (running) {
-        rearm = 1;
-        goto out;
-    } else {
-        running = 1;
-    }
-
-    d = dma_controllers;
-
-    for (icont = 0; icont < 2; icont++, d++) {
-        for (ichan = 0; ichan < 4; ichan++) {
-            int mask;
-
-            mask = 1 << ichan;
-
-            if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) {
-                channel_run (icont, ichan);
-                rearm = 1;
-            }
-        }
-    }
-
-    running = 0;
-out:
-    if (rearm)
-        qemu_bh_schedule_idle(dma_bh);
-}
-
-static void DMA_run_bh(void *unused)
-{
-    DMA_run();
-}
-
-void DMA_register_channel (int nchan,
-                           DMA_transfer_handler transfer_handler,
-                           void *opaque)
-{
-    struct dma_regs *r;
-    int ichan, ncont;
-
-    ncont = nchan > 3;
-    ichan = nchan & 3;
-
-    r = dma_controllers[ncont].regs + ichan;
-    r->transfer_handler = transfer_handler;
-    r->opaque = opaque;
-}
-
-int DMA_read_memory (int nchan, void *buf, int pos, int len)
-{
-    struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
-    hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
-
-    if (r->mode & 0x20) {
-        int i;
-        uint8_t *p = buf;
-
-        cpu_physical_memory_read (addr - pos - len, buf, len);
-        /* What about 16bit transfers? */
-        for (i = 0; i < len >> 1; i++) {
-            uint8_t b = p[len - i - 1];
-            p[i] = b;
-        }
-    }
-    else
-        cpu_physical_memory_read (addr + pos, buf, len);
-
-    return len;
-}
-
-int DMA_write_memory (int nchan, void *buf, int pos, int len)
-{
-    struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
-    hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
-
-    if (r->mode & 0x20) {
-        int i;
-        uint8_t *p = buf;
-
-        cpu_physical_memory_write (addr - pos - len, buf, len);
-        /* What about 16bit transfers? */
-        for (i = 0; i < len; i++) {
-            uint8_t b = p[len - i - 1];
-            p[i] = b;
-        }
-    }
-    else
-        cpu_physical_memory_write (addr + pos, buf, len);
-
-    return len;
-}
-
-/* request the emulator to transfer a new DMA memory block ASAP */
-void DMA_schedule(int nchan)
-{
-    struct dma_cont *d = &dma_controllers[nchan > 3];
-
-    qemu_irq_pulse(*d->cpu_request_exit);
-}
-
-static void dma_reset(void *opaque)
-{
-    struct dma_cont *d = opaque;
-    write_cont(d, (0x05 << d->dshift), 0, 1);
-}
-
-static int dma_phony_handler (void *opaque, int nchan, int dma_pos, int dma_len)
-{
-    dolog ("unregistered DMA channel used nchan=%d dma_pos=%d dma_len=%d\n",
-           nchan, dma_pos, dma_len);
-    return dma_pos;
-}
-
-
-static const MemoryRegionOps channel_io_ops = {
-    .read = read_chan,
-    .write = write_chan,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-/* IOport from page_base */
-static const MemoryRegionPortio page_portio_list[] = {
-    { 0x01, 3, 1, .write = write_page, .read = read_page, },
-    { 0x07, 1, 1, .write = write_page, .read = read_page, },
-    PORTIO_END_OF_LIST(),
-};
-
-/* IOport from pageh_base */
-static const MemoryRegionPortio pageh_portio_list[] = {
-    { 0x01, 3, 1, .write = write_pageh, .read = read_pageh, },
-    { 0x07, 3, 1, .write = write_pageh, .read = read_pageh, },
-    PORTIO_END_OF_LIST(),
-};
-
-static const MemoryRegionOps cont_io_ops = {
-    .read = read_cont,
-    .write = write_cont,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-/* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */
-static void dma_init2(struct dma_cont *d, int base, int dshift,
-                      int page_base, int pageh_base,
-                      qemu_irq *cpu_request_exit)
-{
-    int i;
-
-    d->dshift = dshift;
-    d->cpu_request_exit = cpu_request_exit;
-
-    memory_region_init_io(&d->channel_io, &channel_io_ops, d,
-                          "dma-chan", 8 << d->dshift);
-    memory_region_add_subregion(isa_address_space_io(NULL),
-                                base, &d->channel_io);
-
-    isa_register_portio_list(NULL, page_base, page_portio_list, d,
-                             "dma-page");
-    if (pageh_base >= 0) {
-        isa_register_portio_list(NULL, pageh_base, pageh_portio_list, d,
-                                 "dma-pageh");
-    }
-
-    memory_region_init_io(&d->cont_io, &cont_io_ops, d, "dma-cont",
-                          8 << d->dshift);
-    memory_region_add_subregion(isa_address_space_io(NULL),
-                                base + (8 << d->dshift), &d->cont_io);
-
-    qemu_register_reset(dma_reset, d);
-    dma_reset(d);
-    for (i = 0; i < ARRAY_SIZE (d->regs); ++i) {
-        d->regs[i].transfer_handler = dma_phony_handler;
-    }
-}
-
-static const VMStateDescription vmstate_dma_regs = {
-    .name = "dma_regs",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField []) {
-        VMSTATE_INT32_ARRAY(now, struct dma_regs, 2),
-        VMSTATE_UINT16_ARRAY(base, struct dma_regs, 2),
-        VMSTATE_UINT8(mode, struct dma_regs),
-        VMSTATE_UINT8(page, struct dma_regs),
-        VMSTATE_UINT8(pageh, struct dma_regs),
-        VMSTATE_UINT8(dack, struct dma_regs),
-        VMSTATE_UINT8(eop, struct dma_regs),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int dma_post_load(void *opaque, int version_id)
-{
-    DMA_run();
-
-    return 0;
-}
-
-static const VMStateDescription vmstate_dma = {
-    .name = "dma",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .post_load = dma_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT8(command, struct dma_cont),
-        VMSTATE_UINT8(mask, struct dma_cont),
-        VMSTATE_UINT8(flip_flop, struct dma_cont),
-        VMSTATE_INT32(dshift, struct dma_cont),
-        VMSTATE_STRUCT_ARRAY(regs, struct dma_cont, 4, 1, vmstate_dma_regs, struct dma_regs),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit)
-{
-    dma_init2(&dma_controllers[0], 0x00, 0, 0x80,
-              high_page_enable ? 0x480 : -1, cpu_request_exit);
-    dma_init2(&dma_controllers[1], 0xc0, 1, 0x88,
-              high_page_enable ? 0x488 : -1, cpu_request_exit);
-    vmstate_register (NULL, 0, &vmstate_dma, &dma_controllers[0]);
-    vmstate_register (NULL, 1, &vmstate_dma, &dma_controllers[1]);
-
-    dma_bh = qemu_bh_new(DMA_run_bh, NULL);
-}
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bce31cdf8735a1640e1bb23d49dc2993e5360271 100644 (file)
@@ -0,0 +1,7 @@
+common-obj-$(CONFIG_PUV3) += puv3_dma.o
+common-obj-$(CONFIG_RC4030) += rc4030.o
+common-obj-$(CONFIG_PL080) += pl080.o
+common-obj-$(CONFIG_PL330) += pl330.o
+common-obj-$(CONFIG_I82374) += i82374.o
+common-obj-$(CONFIG_I8257) += i8257.o
+common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o
diff --git a/hw/dma/i82374.c b/hw/dma/i82374.c
new file mode 100644 (file)
index 0000000..835639d
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * QEMU Intel 82374 emulation (Enhanced DMA controller)
+ *
+ * Copyright (c) 2010 Hervé Poussineau
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/isa/isa.h"
+
+//#define DEBUG_I82374
+
+#ifdef DEBUG_I82374
+#define DPRINTF(fmt, ...) \
+do { fprintf(stderr, "i82374: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+do {} while (0)
+#endif
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "i82374 ERROR: " fmt , ## __VA_ARGS__); } while (0)
+
+typedef struct I82374State {
+    uint8_t commands[8];
+    qemu_irq out;
+} I82374State;
+
+static const VMStateDescription vmstate_i82374 = {
+    .name = "i82374",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8_ARRAY(commands, I82374State, 8),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static uint32_t i82374_read_isr(void *opaque, uint32_t nport)
+{
+    uint32_t val = 0;
+
+    BADF("%s: %08x\n", __func__, nport);
+
+    DPRINTF("%s: %08x=%08x\n", __func__, nport, val);
+    return val;
+}
+
+static void i82374_write_command(void *opaque, uint32_t nport, uint32_t data)
+{
+    DPRINTF("%s: %08x=%08x\n", __func__, nport, data);
+
+    if (data != 0x42) {
+        /* Not Stop S/G command */
+        BADF("%s: %08x=%08x\n", __func__, nport, data);
+    }
+}
+
+static uint32_t i82374_read_status(void *opaque, uint32_t nport)
+{
+    uint32_t val = 0;
+
+    BADF("%s: %08x\n", __func__, nport);
+
+    DPRINTF("%s: %08x=%08x\n", __func__, nport, val);
+    return val;
+}
+
+static void i82374_write_descriptor(void *opaque, uint32_t nport, uint32_t data)
+{
+    DPRINTF("%s: %08x=%08x\n", __func__, nport, data);
+
+    BADF("%s: %08x=%08x\n", __func__, nport, data);
+}
+
+static uint32_t i82374_read_descriptor(void *opaque, uint32_t nport)
+{
+    uint32_t val = 0;
+
+    BADF("%s: %08x\n", __func__, nport);
+
+    DPRINTF("%s: %08x=%08x\n", __func__, nport, val);
+    return val;
+}
+
+static void i82374_init(I82374State *s)
+{
+    DMA_init(1, &s->out);
+    memset(s->commands, 0, sizeof(s->commands));
+}
+
+typedef struct ISAi82374State {
+    ISADevice dev;
+    uint32_t iobase;
+    I82374State state;
+} ISAi82374State;
+
+static const VMStateDescription vmstate_isa_i82374 = {
+    .name = "isa-i82374",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT(state, ISAi82374State, 0, vmstate_i82374, I82374State),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static int i82374_isa_init(ISADevice *dev)
+{
+    ISAi82374State *isa = DO_UPCAST(ISAi82374State, dev, dev);
+    I82374State *s = &isa->state;
+
+    register_ioport_read(isa->iobase + 0x0A, 1, 1, i82374_read_isr, s);
+    register_ioport_write(isa->iobase + 0x10, 8, 1, i82374_write_command, s);
+    register_ioport_read(isa->iobase + 0x18, 8, 1, i82374_read_status, s);
+    register_ioport_write(isa->iobase + 0x20, 0x20, 1, i82374_write_descriptor, s);
+    register_ioport_read(isa->iobase + 0x20, 0x20, 1, i82374_read_descriptor, s);
+
+    i82374_init(s);
+
+    qdev_init_gpio_out(&dev->qdev, &s->out, 1);
+
+    return 0;
+}
+
+static Property i82374_properties[] = {
+    DEFINE_PROP_HEX32("iobase", ISAi82374State, iobase, 0x400),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void i82374_class_init(ObjectClass *klass, void *data)
+{
+    ISADeviceClass *k = ISA_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    
+    k->init  = i82374_isa_init;
+    dc->vmsd = &vmstate_isa_i82374;
+    dc->props = i82374_properties;
+}
+
+static const TypeInfo i82374_isa_info = {
+    .name  = "i82374",
+    .parent = TYPE_ISA_DEVICE,
+    .instance_size  = sizeof(ISAi82374State),
+    .class_init = i82374_class_init,
+};
+
+static void i82374_register_types(void)
+{
+    type_register_static(&i82374_isa_info);
+}
+
+type_init(i82374_register_types)
diff --git a/hw/dma/i8257.c b/hw/dma/i8257.c
new file mode 100644 (file)
index 0000000..eb60d45
--- /dev/null
@@ -0,0 +1,600 @@
+/*
+ * QEMU DMA emulation
+ *
+ * Copyright (c) 2003-2004 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/isa/isa.h"
+#include "qemu/main-loop.h"
+
+/* #define DEBUG_DMA */
+
+#define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#ifdef DEBUG_DMA
+#define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#define ldebug(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#else
+#define linfo(...)
+#define ldebug(...)
+#endif
+
+struct dma_regs {
+    int now[2];
+    uint16_t base[2];
+    uint8_t mode;
+    uint8_t page;
+    uint8_t pageh;
+    uint8_t dack;
+    uint8_t eop;
+    DMA_transfer_handler transfer_handler;
+    void *opaque;
+};
+
+#define ADDR 0
+#define COUNT 1
+
+static struct dma_cont {
+    uint8_t status;
+    uint8_t command;
+    uint8_t mask;
+    uint8_t flip_flop;
+    int dshift;
+    struct dma_regs regs[4];
+    qemu_irq *cpu_request_exit;
+    MemoryRegion channel_io;
+    MemoryRegion cont_io;
+} dma_controllers[2];
+
+enum {
+    CMD_MEMORY_TO_MEMORY = 0x01,
+    CMD_FIXED_ADDRESS    = 0x02,
+    CMD_BLOCK_CONTROLLER = 0x04,
+    CMD_COMPRESSED_TIME  = 0x08,
+    CMD_CYCLIC_PRIORITY  = 0x10,
+    CMD_EXTENDED_WRITE   = 0x20,
+    CMD_LOW_DREQ         = 0x40,
+    CMD_LOW_DACK         = 0x80,
+    CMD_NOT_SUPPORTED    = CMD_MEMORY_TO_MEMORY | CMD_FIXED_ADDRESS
+    | CMD_COMPRESSED_TIME | CMD_CYCLIC_PRIORITY | CMD_EXTENDED_WRITE
+    | CMD_LOW_DREQ | CMD_LOW_DACK
+
+};
+
+static void DMA_run (void);
+
+static int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0};
+
+static void write_page (void *opaque, uint32_t nport, uint32_t data)
+{
+    struct dma_cont *d = opaque;
+    int ichan;
+
+    ichan = channels[nport & 7];
+    if (-1 == ichan) {
+        dolog ("invalid channel %#x %#x\n", nport, data);
+        return;
+    }
+    d->regs[ichan].page = data;
+}
+
+static void write_pageh (void *opaque, uint32_t nport, uint32_t data)
+{
+    struct dma_cont *d = opaque;
+    int ichan;
+
+    ichan = channels[nport & 7];
+    if (-1 == ichan) {
+        dolog ("invalid channel %#x %#x\n", nport, data);
+        return;
+    }
+    d->regs[ichan].pageh = data;
+}
+
+static uint32_t read_page (void *opaque, uint32_t nport)
+{
+    struct dma_cont *d = opaque;
+    int ichan;
+
+    ichan = channels[nport & 7];
+    if (-1 == ichan) {
+        dolog ("invalid channel read %#x\n", nport);
+        return 0;
+    }
+    return d->regs[ichan].page;
+}
+
+static uint32_t read_pageh (void *opaque, uint32_t nport)
+{
+    struct dma_cont *d = opaque;
+    int ichan;
+
+    ichan = channels[nport & 7];
+    if (-1 == ichan) {
+        dolog ("invalid channel read %#x\n", nport);
+        return 0;
+    }
+    return d->regs[ichan].pageh;
+}
+
+static inline void init_chan (struct dma_cont *d, int ichan)
+{
+    struct dma_regs *r;
+
+    r = d->regs + ichan;
+    r->now[ADDR] = r->base[ADDR] << d->dshift;
+    r->now[COUNT] = 0;
+}
+
+static inline int getff (struct dma_cont *d)
+{
+    int ff;
+
+    ff = d->flip_flop;
+    d->flip_flop = !ff;
+    return ff;
+}
+
+static uint64_t read_chan(void *opaque, hwaddr nport, unsigned size)
+{
+    struct dma_cont *d = opaque;
+    int ichan, nreg, iport, ff, val, dir;
+    struct dma_regs *r;
+
+    iport = (nport >> d->dshift) & 0x0f;
+    ichan = iport >> 1;
+    nreg = iport & 1;
+    r = d->regs + ichan;
+
+    dir = ((r->mode >> 5) & 1) ? -1 : 1;
+    ff = getff (d);
+    if (nreg)
+        val = (r->base[COUNT] << d->dshift) - r->now[COUNT];
+    else
+        val = r->now[ADDR] + r->now[COUNT] * dir;
+
+    ldebug ("read_chan %#x -> %d\n", iport, val);
+    return (val >> (d->dshift + (ff << 3))) & 0xff;
+}
+
+static void write_chan(void *opaque, hwaddr nport, uint64_t data,
+                       unsigned size)
+{
+    struct dma_cont *d = opaque;
+    int iport, ichan, nreg;
+    struct dma_regs *r;
+
+    iport = (nport >> d->dshift) & 0x0f;
+    ichan = iport >> 1;
+    nreg = iport & 1;
+    r = d->regs + ichan;
+    if (getff (d)) {
+        r->base[nreg] = (r->base[nreg] & 0xff) | ((data << 8) & 0xff00);
+        init_chan (d, ichan);
+    } else {
+        r->base[nreg] = (r->base[nreg] & 0xff00) | (data & 0xff);
+    }
+}
+
+static void write_cont(void *opaque, hwaddr nport, uint64_t data,
+                       unsigned size)
+{
+    struct dma_cont *d = opaque;
+    int iport, ichan = 0;
+
+    iport = (nport >> d->dshift) & 0x0f;
+    switch (iport) {
+    case 0x00:                  /* command */
+        if ((data != 0) && (data & CMD_NOT_SUPPORTED)) {
+            dolog("command %"PRIx64" not supported\n", data);
+            return;
+        }
+        d->command = data;
+        break;
+
+    case 0x01:
+        ichan = data & 3;
+        if (data & 4) {
+            d->status |= 1 << (ichan + 4);
+        }
+        else {
+            d->status &= ~(1 << (ichan + 4));
+        }
+        d->status &= ~(1 << ichan);
+        DMA_run();
+        break;
+
+    case 0x02:                  /* single mask */
+        if (data & 4)
+            d->mask |= 1 << (data & 3);
+        else
+            d->mask &= ~(1 << (data & 3));
+        DMA_run();
+        break;
+
+    case 0x03:                  /* mode */
+        {
+            ichan = data & 3;
+#ifdef DEBUG_DMA
+            {
+                int op, ai, dir, opmode;
+                op = (data >> 2) & 3;
+                ai = (data >> 4) & 1;
+                dir = (data >> 5) & 1;
+                opmode = (data >> 6) & 3;
+
+                linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n",
+                       ichan, op, ai, dir, opmode);
+            }
+#endif
+            d->regs[ichan].mode = data;
+            break;
+        }
+
+    case 0x04:                  /* clear flip flop */
+        d->flip_flop = 0;
+        break;
+
+    case 0x05:                  /* reset */
+        d->flip_flop = 0;
+        d->mask = ~0;
+        d->status = 0;
+        d->command = 0;
+        break;
+
+    case 0x06:                  /* clear mask for all channels */
+        d->mask = 0;
+        DMA_run();
+        break;
+
+    case 0x07:                  /* write mask for all channels */
+        d->mask = data;
+        DMA_run();
+        break;
+
+    default:
+        dolog ("unknown iport %#x\n", iport);
+        break;
+    }
+
+#ifdef DEBUG_DMA
+    if (0xc != iport) {
+        linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n",
+               nport, ichan, data);
+    }
+#endif
+}
+
+static uint64_t read_cont(void *opaque, hwaddr nport, unsigned size)
+{
+    struct dma_cont *d = opaque;
+    int iport, val;
+
+    iport = (nport >> d->dshift) & 0x0f;
+    switch (iport) {
+    case 0x00:                  /* status */
+        val = d->status;
+        d->status &= 0xf0;
+        break;
+    case 0x01:                  /* mask */
+        val = d->mask;
+        break;
+    default:
+        val = 0;
+        break;
+    }
+
+    ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val);
+    return val;
+}
+
+int DMA_get_channel_mode (int nchan)
+{
+    return dma_controllers[nchan > 3].regs[nchan & 3].mode;
+}
+
+void DMA_hold_DREQ (int nchan)
+{
+    int ncont, ichan;
+
+    ncont = nchan > 3;
+    ichan = nchan & 3;
+    linfo ("held cont=%d chan=%d\n", ncont, ichan);
+    dma_controllers[ncont].status |= 1 << (ichan + 4);
+    DMA_run();
+}
+
+void DMA_release_DREQ (int nchan)
+{
+    int ncont, ichan;
+
+    ncont = nchan > 3;
+    ichan = nchan & 3;
+    linfo ("released cont=%d chan=%d\n", ncont, ichan);
+    dma_controllers[ncont].status &= ~(1 << (ichan + 4));
+    DMA_run();
+}
+
+static void channel_run (int ncont, int ichan)
+{
+    int n;
+    struct dma_regs *r = &dma_controllers[ncont].regs[ichan];
+#ifdef DEBUG_DMA
+    int dir, opmode;
+
+    dir = (r->mode >> 5) & 1;
+    opmode = (r->mode >> 6) & 3;
+
+    if (dir) {
+        dolog ("DMA in address decrement mode\n");
+    }
+    if (opmode != 1) {
+        dolog ("DMA not in single mode select %#x\n", opmode);
+    }
+#endif
+
+    n = r->transfer_handler (r->opaque, ichan + (ncont << 2),
+                             r->now[COUNT], (r->base[COUNT] + 1) << ncont);
+    r->now[COUNT] = n;
+    ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont);
+}
+
+static QEMUBH *dma_bh;
+
+static void DMA_run (void)
+{
+    struct dma_cont *d;
+    int icont, ichan;
+    int rearm = 0;
+    static int running = 0;
+
+    if (running) {
+        rearm = 1;
+        goto out;
+    } else {
+        running = 1;
+    }
+
+    d = dma_controllers;
+
+    for (icont = 0; icont < 2; icont++, d++) {
+        for (ichan = 0; ichan < 4; ichan++) {
+            int mask;
+
+            mask = 1 << ichan;
+
+            if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) {
+                channel_run (icont, ichan);
+                rearm = 1;
+            }
+        }
+    }
+
+    running = 0;
+out:
+    if (rearm)
+        qemu_bh_schedule_idle(dma_bh);
+}
+
+static void DMA_run_bh(void *unused)
+{
+    DMA_run();
+}
+
+void DMA_register_channel (int nchan,
+                           DMA_transfer_handler transfer_handler,
+                           void *opaque)
+{
+    struct dma_regs *r;
+    int ichan, ncont;
+
+    ncont = nchan > 3;
+    ichan = nchan & 3;
+
+    r = dma_controllers[ncont].regs + ichan;
+    r->transfer_handler = transfer_handler;
+    r->opaque = opaque;
+}
+
+int DMA_read_memory (int nchan, void *buf, int pos, int len)
+{
+    struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
+    hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
+
+    if (r->mode & 0x20) {
+        int i;
+        uint8_t *p = buf;
+
+        cpu_physical_memory_read (addr - pos - len, buf, len);
+        /* What about 16bit transfers? */
+        for (i = 0; i < len >> 1; i++) {
+            uint8_t b = p[len - i - 1];
+            p[i] = b;
+        }
+    }
+    else
+        cpu_physical_memory_read (addr + pos, buf, len);
+
+    return len;
+}
+
+int DMA_write_memory (int nchan, void *buf, int pos, int len)
+{
+    struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
+    hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
+
+    if (r->mode & 0x20) {
+        int i;
+        uint8_t *p = buf;
+
+        cpu_physical_memory_write (addr - pos - len, buf, len);
+        /* What about 16bit transfers? */
+        for (i = 0; i < len; i++) {
+            uint8_t b = p[len - i - 1];
+            p[i] = b;
+        }
+    }
+    else
+        cpu_physical_memory_write (addr + pos, buf, len);
+
+    return len;
+}
+
+/* request the emulator to transfer a new DMA memory block ASAP */
+void DMA_schedule(int nchan)
+{
+    struct dma_cont *d = &dma_controllers[nchan > 3];
+
+    qemu_irq_pulse(*d->cpu_request_exit);
+}
+
+static void dma_reset(void *opaque)
+{
+    struct dma_cont *d = opaque;
+    write_cont(d, (0x05 << d->dshift), 0, 1);
+}
+
+static int dma_phony_handler (void *opaque, int nchan, int dma_pos, int dma_len)
+{
+    dolog ("unregistered DMA channel used nchan=%d dma_pos=%d dma_len=%d\n",
+           nchan, dma_pos, dma_len);
+    return dma_pos;
+}
+
+
+static const MemoryRegionOps channel_io_ops = {
+    .read = read_chan,
+    .write = write_chan,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+/* IOport from page_base */
+static const MemoryRegionPortio page_portio_list[] = {
+    { 0x01, 3, 1, .write = write_page, .read = read_page, },
+    { 0x07, 1, 1, .write = write_page, .read = read_page, },
+    PORTIO_END_OF_LIST(),
+};
+
+/* IOport from pageh_base */
+static const MemoryRegionPortio pageh_portio_list[] = {
+    { 0x01, 3, 1, .write = write_pageh, .read = read_pageh, },
+    { 0x07, 3, 1, .write = write_pageh, .read = read_pageh, },
+    PORTIO_END_OF_LIST(),
+};
+
+static const MemoryRegionOps cont_io_ops = {
+    .read = read_cont,
+    .write = write_cont,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+/* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */
+static void dma_init2(struct dma_cont *d, int base, int dshift,
+                      int page_base, int pageh_base,
+                      qemu_irq *cpu_request_exit)
+{
+    int i;
+
+    d->dshift = dshift;
+    d->cpu_request_exit = cpu_request_exit;
+
+    memory_region_init_io(&d->channel_io, &channel_io_ops, d,
+                          "dma-chan", 8 << d->dshift);
+    memory_region_add_subregion(isa_address_space_io(NULL),
+                                base, &d->channel_io);
+
+    isa_register_portio_list(NULL, page_base, page_portio_list, d,
+                             "dma-page");
+    if (pageh_base >= 0) {
+        isa_register_portio_list(NULL, pageh_base, pageh_portio_list, d,
+                                 "dma-pageh");
+    }
+
+    memory_region_init_io(&d->cont_io, &cont_io_ops, d, "dma-cont",
+                          8 << d->dshift);
+    memory_region_add_subregion(isa_address_space_io(NULL),
+                                base + (8 << d->dshift), &d->cont_io);
+
+    qemu_register_reset(dma_reset, d);
+    dma_reset(d);
+    for (i = 0; i < ARRAY_SIZE (d->regs); ++i) {
+        d->regs[i].transfer_handler = dma_phony_handler;
+    }
+}
+
+static const VMStateDescription vmstate_dma_regs = {
+    .name = "dma_regs",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_INT32_ARRAY(now, struct dma_regs, 2),
+        VMSTATE_UINT16_ARRAY(base, struct dma_regs, 2),
+        VMSTATE_UINT8(mode, struct dma_regs),
+        VMSTATE_UINT8(page, struct dma_regs),
+        VMSTATE_UINT8(pageh, struct dma_regs),
+        VMSTATE_UINT8(dack, struct dma_regs),
+        VMSTATE_UINT8(eop, struct dma_regs),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int dma_post_load(void *opaque, int version_id)
+{
+    DMA_run();
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_dma = {
+    .name = "dma",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = dma_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT8(command, struct dma_cont),
+        VMSTATE_UINT8(mask, struct dma_cont),
+        VMSTATE_UINT8(flip_flop, struct dma_cont),
+        VMSTATE_INT32(dshift, struct dma_cont),
+        VMSTATE_STRUCT_ARRAY(regs, struct dma_cont, 4, 1, vmstate_dma_regs, struct dma_regs),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit)
+{
+    dma_init2(&dma_controllers[0], 0x00, 0, 0x80,
+              high_page_enable ? 0x480 : -1, cpu_request_exit);
+    dma_init2(&dma_controllers[1], 0xc0, 1, 0x88,
+              high_page_enable ? 0x488 : -1, cpu_request_exit);
+    vmstate_register (NULL, 0, &vmstate_dma, &dma_controllers[0]);
+    vmstate_register (NULL, 1, &vmstate_dma, &dma_controllers[1]);
+
+    dma_bh = qemu_bh_new(DMA_run_bh, NULL);
+}
diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c
new file mode 100644 (file)
index 0000000..00b66b4
--- /dev/null
@@ -0,0 +1,421 @@
+/*
+ * Arm PrimeCell PL080/PL081 DMA controller
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/sysbus.h"
+
+#define PL080_MAX_CHANNELS 8
+#define PL080_CONF_E    0x1
+#define PL080_CONF_M1   0x2
+#define PL080_CONF_M2   0x4
+
+#define PL080_CCONF_H   0x40000
+#define PL080_CCONF_A   0x20000
+#define PL080_CCONF_L   0x10000
+#define PL080_CCONF_ITC 0x08000
+#define PL080_CCONF_IE  0x04000
+#define PL080_CCONF_E   0x00001
+
+#define PL080_CCTRL_I   0x80000000
+#define PL080_CCTRL_DI  0x08000000
+#define PL080_CCTRL_SI  0x04000000
+#define PL080_CCTRL_D   0x02000000
+#define PL080_CCTRL_S   0x01000000
+
+typedef struct {
+    uint32_t src;
+    uint32_t dest;
+    uint32_t lli;
+    uint32_t ctrl;
+    uint32_t conf;
+} pl080_channel;
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    uint8_t tc_int;
+    uint8_t tc_mask;
+    uint8_t err_int;
+    uint8_t err_mask;
+    uint32_t conf;
+    uint32_t sync;
+    uint32_t req_single;
+    uint32_t req_burst;
+    pl080_channel chan[PL080_MAX_CHANNELS];
+    int nchannels;
+    /* Flag to avoid recursive DMA invocations.  */
+    int running;
+    qemu_irq irq;
+} pl080_state;
+
+static const VMStateDescription vmstate_pl080_channel = {
+    .name = "pl080_channel",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(src, pl080_channel),
+        VMSTATE_UINT32(dest, pl080_channel),
+        VMSTATE_UINT32(lli, pl080_channel),
+        VMSTATE_UINT32(ctrl, pl080_channel),
+        VMSTATE_UINT32(conf, pl080_channel),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_pl080 = {
+    .name = "pl080",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(tc_int, pl080_state),
+        VMSTATE_UINT8(tc_mask, pl080_state),
+        VMSTATE_UINT8(err_int, pl080_state),
+        VMSTATE_UINT8(err_mask, pl080_state),
+        VMSTATE_UINT32(conf, pl080_state),
+        VMSTATE_UINT32(sync, pl080_state),
+        VMSTATE_UINT32(req_single, pl080_state),
+        VMSTATE_UINT32(req_burst, pl080_state),
+        VMSTATE_UINT8(tc_int, pl080_state),
+        VMSTATE_UINT8(tc_int, pl080_state),
+        VMSTATE_UINT8(tc_int, pl080_state),
+        VMSTATE_STRUCT_ARRAY(chan, pl080_state, PL080_MAX_CHANNELS,
+                             1, vmstate_pl080_channel, pl080_channel),
+        VMSTATE_INT32(running, pl080_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const unsigned char pl080_id[] =
+{ 0x80, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static const unsigned char pl081_id[] =
+{ 0x81, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl080_update(pl080_state *s)
+{
+    if ((s->tc_int & s->tc_mask)
+            || (s->err_int & s->err_mask))
+        qemu_irq_raise(s->irq);
+    else
+        qemu_irq_lower(s->irq);
+}
+
+static void pl080_run(pl080_state *s)
+{
+    int c;
+    int flow;
+    pl080_channel *ch;
+    int swidth;
+    int dwidth;
+    int xsize;
+    int n;
+    int src_id;
+    int dest_id;
+    int size;
+    uint8_t buff[4];
+    uint32_t req;
+
+    s->tc_mask = 0;
+    for (c = 0; c < s->nchannels; c++) {
+        if (s->chan[c].conf & PL080_CCONF_ITC)
+            s->tc_mask |= 1 << c;
+        if (s->chan[c].conf & PL080_CCONF_IE)
+            s->err_mask |= 1 << c;
+    }
+
+    if ((s->conf & PL080_CONF_E) == 0)
+        return;
+
+hw_error("DMA active\n");
+    /* If we are already in the middle of a DMA operation then indicate that
+       there may be new DMA requests and return immediately.  */
+    if (s->running) {
+        s->running++;
+        return;
+    }
+    s->running = 1;
+    while (s->running) {
+        for (c = 0; c < s->nchannels; c++) {
+            ch = &s->chan[c];
+again:
+            /* Test if thiws channel has any pending DMA requests.  */
+            if ((ch->conf & (PL080_CCONF_H | PL080_CCONF_E))
+                    != PL080_CCONF_E)
+                continue;
+            flow = (ch->conf >> 11) & 7;
+            if (flow >= 4) {
+                hw_error(
+                    "pl080_run: Peripheral flow control not implemented\n");
+            }
+            src_id = (ch->conf >> 1) & 0x1f;
+            dest_id = (ch->conf >> 6) & 0x1f;
+            size = ch->ctrl & 0xfff;
+            req = s->req_single | s->req_burst;
+            switch (flow) {
+            case 0:
+                break;
+            case 1:
+                if ((req & (1u << dest_id)) == 0)
+                    size = 0;
+                break;
+            case 2:
+                if ((req & (1u << src_id)) == 0)
+                    size = 0;
+                break;
+            case 3:
+                if ((req & (1u << src_id)) == 0
+                        || (req & (1u << dest_id)) == 0)
+                    size = 0;
+                break;
+            }
+            if (!size)
+                continue;
+
+            /* Transfer one element.  */
+            /* ??? Should transfer multiple elements for a burst request.  */
+            /* ??? Unclear what the proper behavior is when source and
+               destination widths are different.  */
+            swidth = 1 << ((ch->ctrl >> 18) & 7);
+            dwidth = 1 << ((ch->ctrl >> 21) & 7);
+            for (n = 0; n < dwidth; n+= swidth) {
+                cpu_physical_memory_read(ch->src, buff + n, swidth);
+                if (ch->ctrl & PL080_CCTRL_SI)
+                    ch->src += swidth;
+            }
+            xsize = (dwidth < swidth) ? swidth : dwidth;
+            /* ??? This may pad the value incorrectly for dwidth < 32.  */
+            for (n = 0; n < xsize; n += dwidth) {
+                cpu_physical_memory_write(ch->dest + n, buff + n, dwidth);
+                if (ch->ctrl & PL080_CCTRL_DI)
+                    ch->dest += swidth;
+            }
+
+            size--;
+            ch->ctrl = (ch->ctrl & 0xfffff000) | size;
+            if (size == 0) {
+                /* Transfer complete.  */
+                if (ch->lli) {
+                    ch->src = ldl_le_phys(ch->lli);
+                    ch->dest = ldl_le_phys(ch->lli + 4);
+                    ch->ctrl = ldl_le_phys(ch->lli + 12);
+                    ch->lli = ldl_le_phys(ch->lli + 8);
+                } else {
+                    ch->conf &= ~PL080_CCONF_E;
+                }
+                if (ch->ctrl & PL080_CCTRL_I) {
+                    s->tc_int |= 1 << c;
+                }
+            }
+            goto again;
+        }
+        if (--s->running)
+            s->running = 1;
+    }
+}
+
+static uint64_t pl080_read(void *opaque, hwaddr offset,
+                           unsigned size)
+{
+    pl080_state *s = (pl080_state *)opaque;
+    uint32_t i;
+    uint32_t mask;
+
+    if (offset >= 0xfe0 && offset < 0x1000) {
+        if (s->nchannels == 8) {
+            return pl080_id[(offset - 0xfe0) >> 2];
+        } else {
+            return pl081_id[(offset - 0xfe0) >> 2];
+        }
+    }
+    if (offset >= 0x100 && offset < 0x200) {
+        i = (offset & 0xe0) >> 5;
+        if (i >= s->nchannels)
+            goto bad_offset;
+        switch (offset >> 2) {
+        case 0: /* SrcAddr */
+            return s->chan[i].src;
+        case 1: /* DestAddr */
+            return s->chan[i].dest;
+        case 2: /* LLI */
+            return s->chan[i].lli;
+        case 3: /* Control */
+            return s->chan[i].ctrl;
+        case 4: /* Configuration */
+            return s->chan[i].conf;
+        default:
+            goto bad_offset;
+        }
+    }
+    switch (offset >> 2) {
+    case 0: /* IntStatus */
+        return (s->tc_int & s->tc_mask) | (s->err_int & s->err_mask);
+    case 1: /* IntTCStatus */
+        return (s->tc_int & s->tc_mask);
+    case 3: /* IntErrorStatus */
+        return (s->err_int & s->err_mask);
+    case 5: /* RawIntTCStatus */
+        return s->tc_int;
+    case 6: /* RawIntErrorStatus */
+        return s->err_int;
+    case 7: /* EnbldChns */
+        mask = 0;
+        for (i = 0; i < s->nchannels; i++) {
+            if (s->chan[i].conf & PL080_CCONF_E)
+                mask |= 1 << i;
+        }
+        return mask;
+    case 8: /* SoftBReq */
+    case 9: /* SoftSReq */
+    case 10: /* SoftLBReq */
+    case 11: /* SoftLSReq */
+        /* ??? Implement these. */
+        return 0;
+    case 12: /* Configuration */
+        return s->conf;
+    case 13: /* Sync */
+        return s->sync;
+    default:
+    bad_offset:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl080_read: Bad offset %x\n", (int)offset);
+        return 0;
+    }
+}
+
+static void pl080_write(void *opaque, hwaddr offset,
+                        uint64_t value, unsigned size)
+{
+    pl080_state *s = (pl080_state *)opaque;
+    int i;
+
+    if (offset >= 0x100 && offset < 0x200) {
+        i = (offset & 0xe0) >> 5;
+        if (i >= s->nchannels)
+            goto bad_offset;
+        switch (offset >> 2) {
+        case 0: /* SrcAddr */
+            s->chan[i].src = value;
+            break;
+        case 1: /* DestAddr */
+            s->chan[i].dest = value;
+            break;
+        case 2: /* LLI */
+            s->chan[i].lli = value;
+            break;
+        case 3: /* Control */
+            s->chan[i].ctrl = value;
+            break;
+        case 4: /* Configuration */
+            s->chan[i].conf = value;
+            pl080_run(s);
+            break;
+        }
+    }
+    switch (offset >> 2) {
+    case 2: /* IntTCClear */
+        s->tc_int &= ~value;
+        break;
+    case 4: /* IntErrorClear */
+        s->err_int &= ~value;
+        break;
+    case 8: /* SoftBReq */
+    case 9: /* SoftSReq */
+    case 10: /* SoftLBReq */
+    case 11: /* SoftLSReq */
+        /* ??? Implement these.  */
+        qemu_log_mask(LOG_UNIMP, "pl080_write: Soft DMA not implemented\n");
+        break;
+    case 12: /* Configuration */
+        s->conf = value;
+        if (s->conf & (PL080_CONF_M1 | PL080_CONF_M1)) {
+            qemu_log_mask(LOG_UNIMP,
+                          "pl080_write: Big-endian DMA not implemented\n");
+        }
+        pl080_run(s);
+        break;
+    case 13: /* Sync */
+        s->sync = value;
+        break;
+    default:
+    bad_offset:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl080_write: Bad offset %x\n", (int)offset);
+    }
+    pl080_update(s);
+}
+
+static const MemoryRegionOps pl080_ops = {
+    .read = pl080_read,
+    .write = pl080_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int pl08x_init(SysBusDevice *dev, int nchannels)
+{
+    pl080_state *s = FROM_SYSBUS(pl080_state, dev);
+
+    memory_region_init_io(&s->iomem, &pl080_ops, s, "pl080", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+    s->nchannels = nchannels;
+    return 0;
+}
+
+static int pl080_init(SysBusDevice *dev)
+{
+    return pl08x_init(dev, 8);
+}
+
+static int pl081_init(SysBusDevice *dev)
+{
+    return pl08x_init(dev, 2);
+}
+
+static void pl080_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = pl080_init;
+    dc->no_user = 1;
+    dc->vmsd = &vmstate_pl080;
+}
+
+static const TypeInfo pl080_info = {
+    .name          = "pl080",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl080_state),
+    .class_init    = pl080_class_init,
+};
+
+static void pl081_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = pl081_init;
+    dc->no_user = 1;
+    dc->vmsd = &vmstate_pl080;
+}
+
+static const TypeInfo pl081_info = {
+    .name          = "pl081",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl080_state),
+    .class_init    = pl081_class_init,
+};
+
+/* The PL080 and PL081 are the same except for the number of channels
+   they implement (8 and 2 respectively).  */
+static void pl080_register_types(void)
+{
+    type_register_static(&pl080_info);
+    type_register_static(&pl081_info);
+}
+
+type_init(pl080_register_types)
diff --git a/hw/dma/pl330.c b/hw/dma/pl330.c
new file mode 100644 (file)
index 0000000..8b33138
--- /dev/null
@@ -0,0 +1,1653 @@
+/*
+ * ARM PrimeCell PL330 DMA Controller
+ *
+ * Copyright (c) 2009 Samsung Electronics.
+ * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
+ * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
+ * Copyright (c) 2012 PetaLogix Pty Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2 or later.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "sysemu/dma.h"
+
+#ifndef PL330_ERR_DEBUG
+#define PL330_ERR_DEBUG 0
+#endif
+
+#define DB_PRINT_L(lvl, fmt, args...) do {\
+    if (PL330_ERR_DEBUG >= lvl) {\
+        fprintf(stderr, "PL330: %s:" fmt, __func__, ## args);\
+    } \
+} while (0);
+
+#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
+
+#define PL330_PERIPH_NUM            32
+#define PL330_MAX_BURST_LEN         128
+#define PL330_INSN_MAXSIZE          6
+
+#define PL330_FIFO_OK               0
+#define PL330_FIFO_STALL            1
+#define PL330_FIFO_ERR              (-1)
+
+#define PL330_FAULT_UNDEF_INSTR             (1 <<  0)
+#define PL330_FAULT_OPERAND_INVALID         (1 <<  1)
+#define PL330_FAULT_DMAGO_ERR               (1 <<  4)
+#define PL330_FAULT_EVENT_ERR               (1 <<  5)
+#define PL330_FAULT_CH_PERIPH_ERR           (1 <<  6)
+#define PL330_FAULT_CH_RDWR_ERR             (1 <<  7)
+#define PL330_FAULT_ST_DATA_UNAVAILABLE     (1 << 12)
+#define PL330_FAULT_FIFOEMPTY_ERR           (1 << 13)
+#define PL330_FAULT_INSTR_FETCH_ERR         (1 << 16)
+#define PL330_FAULT_DATA_WRITE_ERR          (1 << 17)
+#define PL330_FAULT_DATA_READ_ERR           (1 << 18)
+#define PL330_FAULT_DBG_INSTR               (1 << 30)
+#define PL330_FAULT_LOCKUP_ERR              (1 << 31)
+
+#define PL330_UNTAGGED              0xff
+
+#define PL330_SINGLE                0x0
+#define PL330_BURST                 0x1
+
+#define PL330_WATCHDOG_LIMIT        1024
+
+/* IOMEM mapped registers */
+#define PL330_REG_DSR               0x000
+#define PL330_REG_DPC               0x004
+#define PL330_REG_INTEN             0x020
+#define PL330_REG_INT_EVENT_RIS     0x024
+#define PL330_REG_INTMIS            0x028
+#define PL330_REG_INTCLR            0x02C
+#define PL330_REG_FSRD              0x030
+#define PL330_REG_FSRC              0x034
+#define PL330_REG_FTRD              0x038
+#define PL330_REG_FTR_BASE          0x040
+#define PL330_REG_CSR_BASE          0x100
+#define PL330_REG_CPC_BASE          0x104
+#define PL330_REG_CHANCTRL          0x400
+#define PL330_REG_DBGSTATUS         0xD00
+#define PL330_REG_DBGCMD            0xD04
+#define PL330_REG_DBGINST0          0xD08
+#define PL330_REG_DBGINST1          0xD0C
+#define PL330_REG_CR0_BASE          0xE00
+#define PL330_REG_PERIPH_ID         0xFE0
+
+#define PL330_IOMEM_SIZE    0x1000
+
+#define CFG_BOOT_ADDR 2
+#define CFG_INS 3
+#define CFG_PNS 4
+#define CFG_CRD 5
+
+static const uint32_t pl330_id[] = {
+    0x30, 0x13, 0x24, 0x00, 0x0D, 0xF0, 0x05, 0xB1
+};
+
+/* DMA channel states as they are described in PL330 Technical Reference Manual
+ * Most of them will not be used in emulation.
+ */
+typedef enum  {
+    pl330_chan_stopped = 0,
+    pl330_chan_executing = 1,
+    pl330_chan_cache_miss = 2,
+    pl330_chan_updating_pc = 3,
+    pl330_chan_waiting_event = 4,
+    pl330_chan_at_barrier = 5,
+    pl330_chan_queue_busy = 6,
+    pl330_chan_waiting_periph = 7,
+    pl330_chan_killing = 8,
+    pl330_chan_completing = 9,
+    pl330_chan_fault_completing = 14,
+    pl330_chan_fault = 15,
+} PL330ChanState;
+
+typedef struct PL330State PL330State;
+
+typedef struct PL330Chan {
+    uint32_t src;
+    uint32_t dst;
+    uint32_t pc;
+    uint32_t control;
+    uint32_t status;
+    uint32_t lc[2];
+    uint32_t fault_type;
+    uint32_t watchdog_timer;
+
+    bool ns;
+    uint8_t request_flag;
+    uint8_t wakeup;
+    uint8_t wfp_sbp;
+
+    uint8_t state;
+    uint8_t stall;
+
+    bool is_manager;
+    PL330State *parent;
+    uint8_t tag;
+} PL330Chan;
+
+static const VMStateDescription vmstate_pl330_chan = {
+    .name = "pl330_chan",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(src, PL330Chan),
+        VMSTATE_UINT32(dst, PL330Chan),
+        VMSTATE_UINT32(pc, PL330Chan),
+        VMSTATE_UINT32(control, PL330Chan),
+        VMSTATE_UINT32(status, PL330Chan),
+        VMSTATE_UINT32_ARRAY(lc, PL330Chan, 2),
+        VMSTATE_UINT32(fault_type, PL330Chan),
+        VMSTATE_UINT32(watchdog_timer, PL330Chan),
+        VMSTATE_BOOL(ns, PL330Chan),
+        VMSTATE_UINT8(request_flag, PL330Chan),
+        VMSTATE_UINT8(wakeup, PL330Chan),
+        VMSTATE_UINT8(wfp_sbp, PL330Chan),
+        VMSTATE_UINT8(state, PL330Chan),
+        VMSTATE_UINT8(stall, PL330Chan),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+typedef struct PL330Fifo {
+    uint8_t *buf;
+    uint8_t *tag;
+    uint32_t head;
+    uint32_t num;
+    uint32_t buf_size;
+} PL330Fifo;
+
+static const VMStateDescription vmstate_pl330_fifo = {
+    .name = "pl330_chan",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_VBUFFER_UINT32(buf, PL330Fifo, 1, NULL, 0, buf_size),
+        VMSTATE_VBUFFER_UINT32(tag, PL330Fifo, 1, NULL, 0, buf_size),
+        VMSTATE_UINT32(head, PL330Fifo),
+        VMSTATE_UINT32(num, PL330Fifo),
+        VMSTATE_UINT32(buf_size, PL330Fifo),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+typedef struct PL330QueueEntry {
+    uint32_t addr;
+    uint32_t len;
+    uint8_t n;
+    bool inc;
+    bool z;
+    uint8_t tag;
+    uint8_t seqn;
+} PL330QueueEntry;
+
+static const VMStateDescription vmstate_pl330_queue_entry = {
+    .name = "pl330_queue_entry",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(addr, PL330QueueEntry),
+        VMSTATE_UINT32(len, PL330QueueEntry),
+        VMSTATE_UINT8(n, PL330QueueEntry),
+        VMSTATE_BOOL(inc, PL330QueueEntry),
+        VMSTATE_BOOL(z, PL330QueueEntry),
+        VMSTATE_UINT8(tag, PL330QueueEntry),
+        VMSTATE_UINT8(seqn, PL330QueueEntry),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+typedef struct PL330Queue {
+    PL330State *parent;
+    PL330QueueEntry *queue;
+    uint32_t queue_size;
+} PL330Queue;
+
+static const VMStateDescription vmstate_pl330_queue = {
+    .name = "pl330_queue",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT_VARRAY_UINT32(queue, PL330Queue, queue_size, 1,
+                                 vmstate_pl330_queue_entry, PL330QueueEntry),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+struct PL330State {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    qemu_irq irq_abort;
+    qemu_irq *irq;
+
+    /* Config registers. cfg[5] = CfgDn. */
+    uint32_t cfg[6];
+#define EVENT_SEC_STATE 3
+#define PERIPH_SEC_STATE 4
+    /* cfg 0 bits and pieces */
+    uint32_t num_chnls;
+    uint8_t num_periph_req;
+    uint8_t num_events;
+    uint8_t mgr_ns_at_rst;
+    /* cfg 1 bits and pieces */
+    uint8_t i_cache_len;
+    uint8_t num_i_cache_lines;
+    /* CRD bits and pieces */
+    uint8_t data_width;
+    uint8_t wr_cap;
+    uint8_t wr_q_dep;
+    uint8_t rd_cap;
+    uint8_t rd_q_dep;
+    uint16_t data_buffer_dep;
+
+    PL330Chan manager;
+    PL330Chan *chan;
+    PL330Fifo fifo;
+    PL330Queue read_queue;
+    PL330Queue write_queue;
+    uint8_t *lo_seqn;
+    uint8_t *hi_seqn;
+    QEMUTimer *timer; /* is used for restore dma. */
+
+    uint32_t inten;
+    uint32_t int_status;
+    uint32_t ev_status;
+    uint32_t dbg[2];
+    uint8_t debug_status;
+    uint8_t num_faulting;
+    uint8_t periph_busy[PL330_PERIPH_NUM];
+
+};
+
+#define TYPE_PL330 "pl330"
+#define PL330(obj) OBJECT_CHECK(PL330State, (obj), TYPE_PL330)
+
+static const VMStateDescription vmstate_pl330 = {
+    .name = "pl330",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT(manager, PL330State, 0, vmstate_pl330_chan, PL330Chan),
+        VMSTATE_STRUCT_VARRAY_UINT32(chan, PL330State, num_chnls, 0,
+                                     vmstate_pl330_chan, PL330Chan),
+        VMSTATE_VBUFFER_UINT32(lo_seqn, PL330State, 1, NULL, 0, num_chnls),
+        VMSTATE_VBUFFER_UINT32(hi_seqn, PL330State, 1, NULL, 0, num_chnls),
+        VMSTATE_STRUCT(fifo, PL330State, 0, vmstate_pl330_fifo, PL330Fifo),
+        VMSTATE_STRUCT(read_queue, PL330State, 0, vmstate_pl330_queue,
+                       PL330Queue),
+        VMSTATE_STRUCT(write_queue, PL330State, 0, vmstate_pl330_queue,
+                       PL330Queue),
+        VMSTATE_TIMER(timer, PL330State),
+        VMSTATE_UINT32(inten, PL330State),
+        VMSTATE_UINT32(int_status, PL330State),
+        VMSTATE_UINT32(ev_status, PL330State),
+        VMSTATE_UINT32_ARRAY(dbg, PL330State, 2),
+        VMSTATE_UINT8(debug_status, PL330State),
+        VMSTATE_UINT8(num_faulting, PL330State),
+        VMSTATE_UINT8_ARRAY(periph_busy, PL330State, PL330_PERIPH_NUM),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+typedef struct PL330InsnDesc {
+    /* OPCODE of the instruction */
+    uint8_t opcode;
+    /* Mask so we can select several sibling instructions, such as
+       DMALD, DMALDS and DMALDB */
+    uint8_t opmask;
+    /* Size of instruction in bytes */
+    uint8_t size;
+    /* Interpreter */
+    void (*exec)(PL330Chan *, uint8_t opcode, uint8_t *args, int len);
+} PL330InsnDesc;
+
+
+/* MFIFO Implementation
+ *
+ * MFIFO is implemented as a cyclic buffer of BUF_SIZE size. Tagged bytes are
+ * stored in this buffer. Data is stored in BUF field, tags - in the
+ * corresponding array elements of TAG field.
+ */
+
+/* Initialize queue. */
+
+static void pl330_fifo_init(PL330Fifo *s, uint32_t size)
+{
+    s->buf = g_malloc0(size);
+    s->tag = g_malloc0(size);
+    s->buf_size = size;
+}
+
+/* Cyclic increment */
+
+static inline int pl330_fifo_inc(PL330Fifo *s, int x)
+{
+    return (x + 1) % s->buf_size;
+}
+
+/* Number of empty bytes in MFIFO */
+
+static inline int pl330_fifo_num_free(PL330Fifo *s)
+{
+    return s->buf_size - s->num;
+}
+
+/* Push LEN bytes of data stored in BUF to MFIFO and tag it with TAG.
+ * Zero returned on success, PL330_FIFO_STALL if there is no enough free
+ * space in MFIFO to store requested amount of data. If push was unsuccessful
+ * no data is stored to MFIFO.
+ */
+
+static int pl330_fifo_push(PL330Fifo *s, uint8_t *buf, int len, uint8_t tag)
+{
+    int i;
+
+    if (s->buf_size - s->num < len) {
+        return PL330_FIFO_STALL;
+    }
+    for (i = 0; i < len; i++) {
+        int push_idx = (s->head + s->num + i) % s->buf_size;
+        s->buf[push_idx] = buf[i];
+        s->tag[push_idx] = tag;
+    }
+    s->num += len;
+    return PL330_FIFO_OK;
+}
+
+/* Get LEN bytes of data from MFIFO and store it to BUF. Tag value of each
+ * byte is verified. Zero returned on success, PL330_FIFO_ERR on tag mismatch
+ * and PL330_FIFO_STALL if there is no enough data in MFIFO. If get was
+ * unsuccessful no data is removed from MFIFO.
+ */
+
+static int pl330_fifo_get(PL330Fifo *s, uint8_t *buf, int len, uint8_t tag)
+{
+    int i;
+
+    if (s->num < len) {
+        return PL330_FIFO_STALL;
+    }
+    for (i = 0; i < len; i++) {
+        if (s->tag[s->head] == tag) {
+            int get_idx = (s->head + i) % s->buf_size;
+            buf[i] = s->buf[get_idx];
+        } else { /* Tag mismatch - Rollback transaction */
+            return PL330_FIFO_ERR;
+        }
+    }
+    s->head = (s->head + len) % s->buf_size;
+    s->num -= len;
+    return PL330_FIFO_OK;
+}
+
+/* Reset MFIFO. This completely erases all data in it. */
+
+static inline void pl330_fifo_reset(PL330Fifo *s)
+{
+    s->head = 0;
+    s->num = 0;
+}
+
+/* Return tag of the first byte stored in MFIFO. If MFIFO is empty
+ * PL330_UNTAGGED is returned.
+ */
+
+static inline uint8_t pl330_fifo_tag(PL330Fifo *s)
+{
+    return (!s->num) ? PL330_UNTAGGED : s->tag[s->head];
+}
+
+/* Returns non-zero if tag TAG is present in fifo or zero otherwise */
+
+static int pl330_fifo_has_tag(PL330Fifo *s, uint8_t tag)
+{
+    int i, n;
+
+    i = s->head;
+    for (n = 0; n < s->num; n++) {
+        if (s->tag[i] == tag) {
+            return 1;
+        }
+        i = pl330_fifo_inc(s, i);
+    }
+    return 0;
+}
+
+/* Remove all entry tagged with TAG from MFIFO */
+
+static void pl330_fifo_tagged_remove(PL330Fifo *s, uint8_t tag)
+{
+    int i, t, n;
+
+    t = i = s->head;
+    for (n = 0; n < s->num; n++) {
+        if (s->tag[i] != tag) {
+            s->buf[t] = s->buf[i];
+            s->tag[t] = s->tag[i];
+            t = pl330_fifo_inc(s, t);
+        } else {
+            s->num = s->num - 1;
+        }
+        i = pl330_fifo_inc(s, i);
+    }
+}
+
+/* Read-Write Queue implementation
+ *
+ * A Read-Write Queue stores up to QUEUE_SIZE instructions (loads or stores).
+ * Each instruction is described by source (for loads) or destination (for
+ * stores) address ADDR, width of data to be loaded/stored LEN, number of
+ * stores/loads to be performed N, INC bit, Z bit and TAG to identify channel
+ * this instruction belongs to. Queue does not store any information about
+ * nature of the instruction: is it load or store. PL330 has different queues
+ * for loads and stores so this is already known at the top level where it
+ * matters.
+ *
+ * Queue works as FIFO for instructions with equivalent tags, but can issue
+ * instructions with different tags in arbitrary order. SEQN field attached to
+ * each instruction helps to achieve this. For each TAG queue contains
+ * instructions with consecutive SEQN values ranging from LO_SEQN[TAG] to
+ * HI_SEQN[TAG]-1 inclusive. SEQN is 8-bit unsigned integer, so SEQN=255 is
+ * followed by SEQN=0.
+ *
+ * Z bit indicates that zeroes should be stored. No MFIFO fetches are performed
+ * in this case.
+ */
+
+static void pl330_queue_reset(PL330Queue *s)
+{
+    int i;
+
+    for (i = 0; i < s->queue_size; i++) {
+        s->queue[i].tag = PL330_UNTAGGED;
+    }
+}
+
+/* Initialize queue */
+static void pl330_queue_init(PL330Queue *s, int size, PL330State *parent)
+{
+    s->parent = parent;
+    s->queue = g_new0(PL330QueueEntry, size);
+    s->queue_size = size;
+}
+
+/* Returns pointer to an empty slot or NULL if queue is full */
+static PL330QueueEntry *pl330_queue_find_empty(PL330Queue *s)
+{
+    int i;
+
+    for (i = 0; i < s->queue_size; i++) {
+        if (s->queue[i].tag == PL330_UNTAGGED) {
+            return &s->queue[i];
+        }
+    }
+    return NULL;
+}
+
+/* Put instruction in queue.
+ * Return value:
+ * - zero - OK
+ * - non-zero - queue is full
+ */
+
+static int pl330_queue_put_insn(PL330Queue *s, uint32_t addr,
+                                int len, int n, bool inc, bool z, uint8_t tag)
+{
+    PL330QueueEntry *entry = pl330_queue_find_empty(s);
+
+    if (!entry) {
+        return 1;
+    }
+    entry->tag = tag;
+    entry->addr = addr;
+    entry->len = len;
+    entry->n = n;
+    entry->z = z;
+    entry->inc = inc;
+    entry->seqn = s->parent->hi_seqn[tag];
+    s->parent->hi_seqn[tag]++;
+    return 0;
+}
+
+/* Returns a pointer to queue slot containing instruction which satisfies
+ *  following conditions:
+ *   - it has valid tag value (not PL330_UNTAGGED)
+ *   - if enforce_seq is set it has to be issuable without violating queue
+ *     logic (see above)
+ *   - if TAG argument is not PL330_UNTAGGED this instruction has tag value
+ *     equivalent to the argument TAG value.
+ *  If such instruction cannot be found NULL is returned.
+ */
+
+static PL330QueueEntry *pl330_queue_find_insn(PL330Queue *s, uint8_t tag,
+                                              bool enforce_seq)
+{
+    int i;
+
+    for (i = 0; i < s->queue_size; i++) {
+        if (s->queue[i].tag != PL330_UNTAGGED) {
+            if ((!enforce_seq ||
+                    s->queue[i].seqn == s->parent->lo_seqn[s->queue[i].tag]) &&
+                    (s->queue[i].tag == tag || tag == PL330_UNTAGGED ||
+                    s->queue[i].z)) {
+                return &s->queue[i];
+            }
+        }
+    }
+    return NULL;
+}
+
+/* Removes instruction from queue. */
+
+static inline void pl330_queue_remove_insn(PL330Queue *s, PL330QueueEntry *e)
+{
+    s->parent->lo_seqn[e->tag]++;
+    e->tag = PL330_UNTAGGED;
+}
+
+/* Removes all instructions tagged with TAG from queue. */
+
+static inline void pl330_queue_remove_tagged(PL330Queue *s, uint8_t tag)
+{
+    int i;
+
+    for (i = 0; i < s->queue_size; i++) {
+        if (s->queue[i].tag == tag) {
+            s->queue[i].tag = PL330_UNTAGGED;
+        }
+    }
+}
+
+/* DMA instruction execution engine */
+
+/* Moves DMA channel to the FAULT state and updates it's status. */
+
+static inline void pl330_fault(PL330Chan *ch, uint32_t flags)
+{
+    DB_PRINT("ch: %p, flags: %x\n", ch, flags);
+    ch->fault_type |= flags;
+    if (ch->state == pl330_chan_fault) {
+        return;
+    }
+    ch->state = pl330_chan_fault;
+    ch->parent->num_faulting++;
+    if (ch->parent->num_faulting == 1) {
+        DB_PRINT("abort interrupt raised\n");
+        qemu_irq_raise(ch->parent->irq_abort);
+    }
+}
+
+/*
+ * For information about instructions see PL330 Technical Reference Manual.
+ *
+ * Arguments:
+ *   CH - channel executing the instruction
+ *   OPCODE - opcode
+ *   ARGS - array of 8-bit arguments
+ *   LEN - number of elements in ARGS array
+ */
+
+static void pl330_dmaaddh(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
+{
+    uint16_t im = (((uint16_t)args[1]) << 8) | ((uint16_t)args[0]);
+    uint8_t ra = (opcode >> 1) & 1;
+
+    if (ch->is_manager) {
+        pl330_fault(ch, PL330_FAULT_UNDEF_INSTR);
+        return;
+    }
+    if (ra) {
+        ch->dst += im;
+    } else {
+        ch->src += im;
+    }
+}
+
+static void pl330_dmaend(PL330Chan *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    PL330State *s = ch->parent;
+
+    if (ch->state == pl330_chan_executing && !ch->is_manager) {
+        /* Wait for all transfers to complete */
+        if (pl330_fifo_has_tag(&s->fifo, ch->tag) ||
+            pl330_queue_find_insn(&s->read_queue, ch->tag, false) != NULL ||
+            pl330_queue_find_insn(&s->write_queue, ch->tag, false) != NULL) {
+
+            ch->stall = 1;
+            return;
+        }
+    }
+    DB_PRINT("DMA ending!\n");
+    pl330_fifo_tagged_remove(&s->fifo, ch->tag);
+    pl330_queue_remove_tagged(&s->read_queue, ch->tag);
+    pl330_queue_remove_tagged(&s->write_queue, ch->tag);
+    ch->state = pl330_chan_stopped;
+}
+
+static void pl330_dmaflushp(PL330Chan *ch, uint8_t opcode,
+                                            uint8_t *args, int len)
+{
+    uint8_t periph_id;
+
+    if (args[0] & 7) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    periph_id = (args[0] >> 3) & 0x1f;
+    if (periph_id >= ch->parent->num_periph_req) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) {
+        pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR);
+        return;
+    }
+    /* Do nothing */
+}
+
+static void pl330_dmago(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
+{
+    uint8_t chan_id;
+    uint8_t ns;
+    uint32_t pc;
+    PL330Chan *s;
+
+    DB_PRINT("\n");
+
+    if (!ch->is_manager) {
+        pl330_fault(ch, PL330_FAULT_UNDEF_INSTR);
+        return;
+    }
+    ns = !!(opcode & 2);
+    chan_id = args[0] & 7;
+    if ((args[0] >> 3)) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if (chan_id >= ch->parent->num_chnls) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    pc = (((uint32_t)args[4]) << 24) | (((uint32_t)args[3]) << 16) |
+         (((uint32_t)args[2]) << 8)  | (((uint32_t)args[1]));
+    if (ch->parent->chan[chan_id].state != pl330_chan_stopped) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if (ch->ns && !ns) {
+        pl330_fault(ch, PL330_FAULT_DMAGO_ERR);
+        return;
+    }
+    s = &ch->parent->chan[chan_id];
+    s->ns = ns;
+    s->pc = pc;
+    s->state = pl330_chan_executing;
+}
+
+static void pl330_dmald(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
+{
+    uint8_t bs = opcode & 3;
+    uint32_t size, num;
+    bool inc;
+
+    if (bs == 2) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if ((bs == 1 && ch->request_flag == PL330_BURST) ||
+        (bs == 3 && ch->request_flag == PL330_SINGLE)) {
+        /* Perform NOP */
+        return;
+    }
+    if (bs == 1 && ch->request_flag == PL330_SINGLE) {
+        num = 1;
+    } else {
+        num = ((ch->control >> 4) & 0xf) + 1;
+    }
+    size = (uint32_t)1 << ((ch->control >> 1) & 0x7);
+    inc = !!(ch->control & 1);
+    ch->stall = pl330_queue_put_insn(&ch->parent->read_queue, ch->src,
+                                    size, num, inc, 0, ch->tag);
+    if (!ch->stall) {
+        DB_PRINT("channel:%d address:%08x size:%d num:%d %c\n",
+                 ch->tag, ch->src, size, num, inc ? 'Y' : 'N');
+        ch->src += inc ? size * num - (ch->src & (size - 1)) : 0;
+    }
+}
+
+static void pl330_dmaldp(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
+{
+    uint8_t periph_id;
+
+    if (args[0] & 7) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    periph_id = (args[0] >> 3) & 0x1f;
+    if (periph_id >= ch->parent->num_periph_req) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) {
+        pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR);
+        return;
+    }
+    pl330_dmald(ch, opcode, args, len);
+}
+
+static void pl330_dmalp(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
+{
+    uint8_t lc = (opcode & 2) >> 1;
+
+    ch->lc[lc] = args[0];
+}
+
+static void pl330_dmakill(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
+{
+    if (ch->state == pl330_chan_fault ||
+        ch->state == pl330_chan_fault_completing) {
+        /* This is the only way for a channel to leave the faulting state */
+        ch->fault_type = 0;
+        ch->parent->num_faulting--;
+        if (ch->parent->num_faulting == 0) {
+            DB_PRINT("abort interrupt lowered\n");
+            qemu_irq_lower(ch->parent->irq_abort);
+        }
+    }
+    ch->state = pl330_chan_killing;
+    pl330_fifo_tagged_remove(&ch->parent->fifo, ch->tag);
+    pl330_queue_remove_tagged(&ch->parent->read_queue, ch->tag);
+    pl330_queue_remove_tagged(&ch->parent->write_queue, ch->tag);
+    ch->state = pl330_chan_stopped;
+}
+
+static void pl330_dmalpend(PL330Chan *ch, uint8_t opcode,
+                                    uint8_t *args, int len)
+{
+    uint8_t nf = (opcode & 0x10) >> 4;
+    uint8_t bs = opcode & 3;
+    uint8_t lc = (opcode & 4) >> 2;
+
+    if (bs == 2) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if ((bs == 1 && ch->request_flag == PL330_BURST) ||
+        (bs == 3 && ch->request_flag == PL330_SINGLE)) {
+        /* Perform NOP */
+        return;
+    }
+    if (!nf || ch->lc[lc]) {
+        if (nf) {
+            ch->lc[lc]--;
+        }
+        DB_PRINT("loop reiteration\n");
+        ch->pc -= args[0];
+        ch->pc -= len + 1;
+        /* "ch->pc -= args[0] + len + 1" is incorrect when args[0] == 256 */
+    } else {
+        DB_PRINT("loop fallthrough\n");
+    }
+}
+
+
+static void pl330_dmamov(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
+{
+    uint8_t rd = args[0] & 7;
+    uint32_t im;
+
+    if ((args[0] >> 3)) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    im = (((uint32_t)args[4]) << 24) | (((uint32_t)args[3]) << 16) |
+         (((uint32_t)args[2]) << 8)  | (((uint32_t)args[1]));
+    switch (rd) {
+    case 0:
+        ch->src = im;
+        break;
+    case 1:
+        ch->control = im;
+        break;
+    case 2:
+        ch->dst = im;
+        break;
+    default:
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+}
+
+static void pl330_dmanop(PL330Chan *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    /* NOP is NOP. */
+}
+
+static void pl330_dmarmb(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
+{
+   if (pl330_queue_find_insn(&ch->parent->read_queue, ch->tag, false)) {
+        ch->state = pl330_chan_at_barrier;
+        ch->stall = 1;
+        return;
+    } else {
+        ch->state = pl330_chan_executing;
+    }
+}
+
+static void pl330_dmasev(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
+{
+    uint8_t ev_id;
+
+    if (args[0] & 7) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    ev_id = (args[0] >> 3) & 0x1f;
+    if (ev_id >= ch->parent->num_events) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if (ch->ns && !(ch->parent->cfg[CFG_INS] & (1 << ev_id))) {
+        pl330_fault(ch, PL330_FAULT_EVENT_ERR);
+        return;
+    }
+    if (ch->parent->inten & (1 << ev_id)) {
+        ch->parent->int_status |= (1 << ev_id);
+        DB_PRINT("event interrupt raised %d\n", ev_id);
+        qemu_irq_raise(ch->parent->irq[ev_id]);
+    }
+    ch->parent->ev_status |= (1 << ev_id);
+}
+
+static void pl330_dmast(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
+{
+    uint8_t bs = opcode & 3;
+    uint32_t size, num;
+    bool inc;
+
+    if (bs == 2) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if ((bs == 1 && ch->request_flag == PL330_BURST) ||
+        (bs == 3 && ch->request_flag == PL330_SINGLE)) {
+        /* Perform NOP */
+        return;
+    }
+    num = ((ch->control >> 18) & 0xf) + 1;
+    size = (uint32_t)1 << ((ch->control >> 15) & 0x7);
+    inc = !!((ch->control >> 14) & 1);
+    ch->stall = pl330_queue_put_insn(&ch->parent->write_queue, ch->dst,
+                                    size, num, inc, 0, ch->tag);
+    if (!ch->stall) {
+        DB_PRINT("channel:%d address:%08x size:%d num:%d %c\n",
+                 ch->tag, ch->dst, size, num, inc ? 'Y' : 'N');
+        ch->dst += inc ? size * num - (ch->dst & (size - 1)) : 0;
+    }
+}
+
+static void pl330_dmastp(PL330Chan *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    uint8_t periph_id;
+
+    if (args[0] & 7) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    periph_id = (args[0] >> 3) & 0x1f;
+    if (periph_id >= ch->parent->num_periph_req) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) {
+        pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR);
+        return;
+    }
+    pl330_dmast(ch, opcode, args, len);
+}
+
+static void pl330_dmastz(PL330Chan *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    uint32_t size, num;
+    bool inc;
+
+    num = ((ch->control >> 18) & 0xf) + 1;
+    size = (uint32_t)1 << ((ch->control >> 15) & 0x7);
+    inc = !!((ch->control >> 14) & 1);
+    ch->stall = pl330_queue_put_insn(&ch->parent->write_queue, ch->dst,
+                                    size, num, inc, 1, ch->tag);
+    if (inc) {
+        ch->dst += size * num;
+    }
+}
+
+static void pl330_dmawfe(PL330Chan *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    uint8_t ev_id;
+    int i;
+
+    if (args[0] & 5) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    ev_id = (args[0] >> 3) & 0x1f;
+    if (ev_id >= ch->parent->num_events) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if (ch->ns && !(ch->parent->cfg[CFG_INS] & (1 << ev_id))) {
+        pl330_fault(ch, PL330_FAULT_EVENT_ERR);
+        return;
+    }
+    ch->wakeup = ev_id;
+    ch->state = pl330_chan_waiting_event;
+    if (~ch->parent->inten & ch->parent->ev_status & 1 << ev_id) {
+        ch->state = pl330_chan_executing;
+        /* If anyone else is currently waiting on the same event, let them
+         * clear the ev_status so they pick up event as well
+         */
+        for (i = 0; i < ch->parent->num_chnls; ++i) {
+            PL330Chan *peer = &ch->parent->chan[i];
+            if (peer->state == pl330_chan_waiting_event &&
+                    peer->wakeup == ev_id) {
+                return;
+            }
+        }
+        ch->parent->ev_status &= ~(1 << ev_id);
+    } else {
+        ch->stall = 1;
+    }
+}
+
+static void pl330_dmawfp(PL330Chan *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    uint8_t bs = opcode & 3;
+    uint8_t periph_id;
+
+    if (args[0] & 7) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    periph_id = (args[0] >> 3) & 0x1f;
+    if (periph_id >= ch->parent->num_periph_req) {
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+    if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) {
+        pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR);
+        return;
+    }
+    switch (bs) {
+    case 0: /* S */
+        ch->request_flag = PL330_SINGLE;
+        ch->wfp_sbp = 0;
+        break;
+    case 1: /* P */
+        ch->request_flag = PL330_BURST;
+        ch->wfp_sbp = 2;
+        break;
+    case 2: /* B */
+        ch->request_flag = PL330_BURST;
+        ch->wfp_sbp = 1;
+        break;
+    default:
+        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
+        return;
+    }
+
+    if (ch->parent->periph_busy[periph_id]) {
+        ch->state = pl330_chan_waiting_periph;
+        ch->stall = 1;
+    } else if (ch->state == pl330_chan_waiting_periph) {
+        ch->state = pl330_chan_executing;
+    }
+}
+
+static void pl330_dmawmb(PL330Chan *ch, uint8_t opcode,
+                         uint8_t *args, int len)
+{
+    if (pl330_queue_find_insn(&ch->parent->write_queue, ch->tag, false)) {
+        ch->state = pl330_chan_at_barrier;
+        ch->stall = 1;
+        return;
+    } else {
+        ch->state = pl330_chan_executing;
+    }
+}
+
+/* NULL terminated array of the instruction descriptions. */
+static const PL330InsnDesc insn_desc[] = {
+    { .opcode = 0x54, .opmask = 0xFD, .size = 3, .exec = pl330_dmaaddh, },
+    { .opcode = 0x00, .opmask = 0xFF, .size = 1, .exec = pl330_dmaend, },
+    { .opcode = 0x35, .opmask = 0xFF, .size = 2, .exec = pl330_dmaflushp, },
+    { .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago, },
+    { .opcode = 0x04, .opmask = 0xFC, .size = 1, .exec = pl330_dmald, },
+    { .opcode = 0x25, .opmask = 0xFD, .size = 2, .exec = pl330_dmaldp, },
+    { .opcode = 0x20, .opmask = 0xFD, .size = 2, .exec = pl330_dmalp, },
+    /* dmastp  must be before dmalpend in this list, because their maps
+     * are overlapping
+     */
+    { .opcode = 0x29, .opmask = 0xFD, .size = 2, .exec = pl330_dmastp, },
+    { .opcode = 0x28, .opmask = 0xE8, .size = 2, .exec = pl330_dmalpend, },
+    { .opcode = 0x01, .opmask = 0xFF, .size = 1, .exec = pl330_dmakill, },
+    { .opcode = 0xBC, .opmask = 0xFF, .size = 6, .exec = pl330_dmamov, },
+    { .opcode = 0x18, .opmask = 0xFF, .size = 1, .exec = pl330_dmanop, },
+    { .opcode = 0x12, .opmask = 0xFF, .size = 1, .exec = pl330_dmarmb, },
+    { .opcode = 0x34, .opmask = 0xFF, .size = 2, .exec = pl330_dmasev, },
+    { .opcode = 0x08, .opmask = 0xFC, .size = 1, .exec = pl330_dmast, },
+    { .opcode = 0x0C, .opmask = 0xFF, .size = 1, .exec = pl330_dmastz, },
+    { .opcode = 0x36, .opmask = 0xFF, .size = 2, .exec = pl330_dmawfe, },
+    { .opcode = 0x30, .opmask = 0xFC, .size = 2, .exec = pl330_dmawfp, },
+    { .opcode = 0x13, .opmask = 0xFF, .size = 1, .exec = pl330_dmawmb, },
+    { .opcode = 0x00, .opmask = 0x00, .size = 0, .exec = NULL, }
+};
+
+/* Instructions which can be issued via debug registers. */
+static const PL330InsnDesc debug_insn_desc[] = {
+    { .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago, },
+    { .opcode = 0x01, .opmask = 0xFF, .size = 1, .exec = pl330_dmakill, },
+    { .opcode = 0x34, .opmask = 0xFF, .size = 2, .exec = pl330_dmasev, },
+    { .opcode = 0x00, .opmask = 0x00, .size = 0, .exec = NULL, }
+};
+
+static inline const PL330InsnDesc *pl330_fetch_insn(PL330Chan *ch)
+{
+    uint8_t opcode;
+    int i;
+
+    dma_memory_read(&dma_context_memory, ch->pc, &opcode, 1);
+    for (i = 0; insn_desc[i].size; i++) {
+        if ((opcode & insn_desc[i].opmask) == insn_desc[i].opcode) {
+            return &insn_desc[i];
+        }
+    }
+    return NULL;
+}
+
+static inline void pl330_exec_insn(PL330Chan *ch, const PL330InsnDesc *insn)
+{
+    uint8_t buf[PL330_INSN_MAXSIZE];
+
+    assert(insn->size <= PL330_INSN_MAXSIZE);
+    dma_memory_read(&dma_context_memory, ch->pc, buf, insn->size);
+    insn->exec(ch, buf[0], &buf[1], insn->size - 1);
+}
+
+static inline void pl330_update_pc(PL330Chan *ch,
+                                   const PL330InsnDesc *insn)
+{
+    ch->pc += insn->size;
+}
+
+/* Try to execute current instruction in channel CH. Number of executed
+   instructions is returned (0 or 1). */
+static int pl330_chan_exec(PL330Chan *ch)
+{
+    const PL330InsnDesc *insn;
+
+    if (ch->state != pl330_chan_executing &&
+            ch->state != pl330_chan_waiting_periph &&
+            ch->state != pl330_chan_at_barrier &&
+            ch->state != pl330_chan_waiting_event) {
+        DB_PRINT("%d\n", ch->state);
+        return 0;
+    }
+    ch->stall = 0;
+    insn = pl330_fetch_insn(ch);
+    if (!insn) {
+        DB_PRINT("pl330 undefined instruction\n");
+        pl330_fault(ch, PL330_FAULT_UNDEF_INSTR);
+        return 0;
+    }
+    pl330_exec_insn(ch, insn);
+    if (!ch->stall) {
+        pl330_update_pc(ch, insn);
+        ch->watchdog_timer = 0;
+        return 1;
+    /* WDT only active in exec state */
+    } else if (ch->state == pl330_chan_executing) {
+        ch->watchdog_timer++;
+        if (ch->watchdog_timer >= PL330_WATCHDOG_LIMIT) {
+            pl330_fault(ch, PL330_FAULT_LOCKUP_ERR);
+        }
+    }
+    return 0;
+}
+
+/* Try to execute 1 instruction in each channel, one instruction from read
+   queue and one instruction from write queue. Number of successfully executed
+   instructions is returned. */
+static int pl330_exec_cycle(PL330Chan *channel)
+{
+    PL330State *s = channel->parent;
+    PL330QueueEntry *q;
+    int i;
+    int num_exec = 0;
+    int fifo_res = 0;
+    uint8_t buf[PL330_MAX_BURST_LEN];
+
+    /* Execute one instruction in each channel */
+    num_exec += pl330_chan_exec(channel);
+
+    /* Execute one instruction from read queue */
+    q = pl330_queue_find_insn(&s->read_queue, PL330_UNTAGGED, true);
+    if (q != NULL && q->len <= pl330_fifo_num_free(&s->fifo)) {
+        int len = q->len - (q->addr & (q->len - 1));
+
+        dma_memory_read(&dma_context_memory, q->addr, buf, len);
+        if (PL330_ERR_DEBUG > 1) {
+            DB_PRINT("PL330 read from memory @%08x (size = %08x):\n",
+                      q->addr, len);
+            hexdump((char *)buf, stderr, "", len);
+        }
+        fifo_res = pl330_fifo_push(&s->fifo, buf, len, q->tag);
+        if (fifo_res == PL330_FIFO_OK) {
+            if (q->inc) {
+                q->addr += len;
+            }
+            q->n--;
+            if (!q->n) {
+                pl330_queue_remove_insn(&s->read_queue, q);
+            }
+            num_exec++;
+        }
+    }
+
+    /* Execute one instruction from write queue. */
+    q = pl330_queue_find_insn(&s->write_queue, pl330_fifo_tag(&s->fifo), true);
+    if (q != NULL) {
+        int len = q->len - (q->addr & (q->len - 1));
+
+        if (q->z) {
+            for (i = 0; i < len; i++) {
+                buf[i] = 0;
+            }
+        } else {
+            fifo_res = pl330_fifo_get(&s->fifo, buf, len, q->tag);
+        }
+        if (fifo_res == PL330_FIFO_OK || q->z) {
+            dma_memory_write(&dma_context_memory, q->addr, buf, len);
+            if (PL330_ERR_DEBUG > 1) {
+                DB_PRINT("PL330 read from memory @%08x (size = %08x):\n",
+                         q->addr, len);
+                hexdump((char *)buf, stderr, "", len);
+            }
+            if (q->inc) {
+                q->addr += len;
+            }
+            num_exec++;
+        } else if (fifo_res == PL330_FIFO_STALL) {
+            pl330_fault(&channel->parent->chan[q->tag],
+                                PL330_FAULT_FIFOEMPTY_ERR);
+        }
+        q->n--;
+        if (!q->n) {
+            pl330_queue_remove_insn(&s->write_queue, q);
+        }
+    }
+
+    return num_exec;
+}
+
+static int pl330_exec_channel(PL330Chan *channel)
+{
+    int insr_exec = 0;
+
+    /* TODO: Is it all right to execute everything or should we do per-cycle
+       simulation? */
+    while (pl330_exec_cycle(channel)) {
+        insr_exec++;
+    }
+
+    /* Detect deadlock */
+    if (channel->state == pl330_chan_executing) {
+        pl330_fault(channel, PL330_FAULT_LOCKUP_ERR);
+    }
+    /* Situation when one of the queues has deadlocked but all channels
+     * have finished their programs should be impossible.
+     */
+
+    return insr_exec;
+}
+
+static inline void pl330_exec(PL330State *s)
+{
+    DB_PRINT("\n");
+    int i, insr_exec;
+    do {
+        insr_exec = pl330_exec_channel(&s->manager);
+
+        for (i = 0; i < s->num_chnls; i++) {
+            insr_exec += pl330_exec_channel(&s->chan[i]);
+        }
+    } while (insr_exec);
+}
+
+static void pl330_exec_cycle_timer(void *opaque)
+{
+    PL330State *s = (PL330State *)opaque;
+    pl330_exec(s);
+}
+
+/* Stop or restore dma operations */
+
+static void pl330_dma_stop_irq(void *opaque, int irq, int level)
+{
+    PL330State *s = (PL330State *)opaque;
+
+    if (s->periph_busy[irq] != level) {
+        s->periph_busy[irq] = level;
+        qemu_mod_timer(s->timer, qemu_get_clock_ns(vm_clock));
+    }
+}
+
+static void pl330_debug_exec(PL330State *s)
+{
+    uint8_t args[5];
+    uint8_t opcode;
+    uint8_t chan_id;
+    int i;
+    PL330Chan *ch;
+    const PL330InsnDesc *insn;
+
+    s->debug_status = 1;
+    chan_id = (s->dbg[0] >>  8) & 0x07;
+    opcode  = (s->dbg[0] >> 16) & 0xff;
+    args[0] = (s->dbg[0] >> 24) & 0xff;
+    args[1] = (s->dbg[1] >>  0) & 0xff;
+    args[2] = (s->dbg[1] >>  8) & 0xff;
+    args[3] = (s->dbg[1] >> 16) & 0xff;
+    args[4] = (s->dbg[1] >> 24) & 0xff;
+    DB_PRINT("chan id: %d\n", chan_id);
+    if (s->dbg[0] & 1) {
+        ch = &s->chan[chan_id];
+    } else {
+        ch = &s->manager;
+    }
+    insn = NULL;
+    for (i = 0; debug_insn_desc[i].size; i++) {
+        if ((opcode & debug_insn_desc[i].opmask) == debug_insn_desc[i].opcode) {
+            insn = &debug_insn_desc[i];
+        }
+    }
+    if (!insn) {
+        pl330_fault(ch, PL330_FAULT_UNDEF_INSTR | PL330_FAULT_DBG_INSTR);
+        return ;
+    }
+    ch->stall = 0;
+    insn->exec(ch, opcode, args, insn->size - 1);
+    if (ch->fault_type) {
+        ch->fault_type |= PL330_FAULT_DBG_INSTR;
+    }
+    if (ch->stall) {
+        qemu_log_mask(LOG_UNIMP, "pl330: stall of debug instruction not "
+                      "implemented\n");
+    }
+    s->debug_status = 0;
+}
+
+/* IOMEM mapped registers */
+
+static void pl330_iomem_write(void *opaque, hwaddr offset,
+                              uint64_t value, unsigned size)
+{
+    PL330State *s = (PL330State *) opaque;
+    uint32_t i;
+
+    DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)value);
+
+    switch (offset) {
+    case PL330_REG_INTEN:
+        s->inten = value;
+        break;
+    case PL330_REG_INTCLR:
+        for (i = 0; i < s->num_events; i++) {
+            if (s->int_status & s->inten & value & (1 << i)) {
+                DB_PRINT("event interrupt lowered %d\n", i);
+                qemu_irq_lower(s->irq[i]);
+            }
+        }
+        s->ev_status &= ~(value & s->inten);
+        s->int_status &= ~(value & s->inten);
+        break;
+    case PL330_REG_DBGCMD:
+        if ((value & 3) == 0) {
+            pl330_debug_exec(s);
+            pl330_exec(s);
+        } else {
+            qemu_log_mask(LOG_GUEST_ERROR, "pl330: write of illegal value %u "
+                          "for offset " TARGET_FMT_plx "\n", (unsigned)value,
+                          offset);
+        }
+        break;
+    case PL330_REG_DBGINST0:
+        DB_PRINT("s->dbg[0] = %08x\n", (unsigned)value);
+        s->dbg[0] = value;
+        break;
+    case PL330_REG_DBGINST1:
+        DB_PRINT("s->dbg[1] = %08x\n", (unsigned)value);
+        s->dbg[1] = value;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad write offset " TARGET_FMT_plx
+                      "\n", offset);
+        break;
+    }
+}
+
+static inline uint32_t pl330_iomem_read_imp(void *opaque,
+        hwaddr offset)
+{
+    PL330State *s = (PL330State *)opaque;
+    int chan_id;
+    int i;
+    uint32_t res;
+
+    if (offset >= PL330_REG_PERIPH_ID && offset < PL330_REG_PERIPH_ID + 32) {
+        return pl330_id[(offset - PL330_REG_PERIPH_ID) >> 2];
+    }
+    if (offset >= PL330_REG_CR0_BASE && offset < PL330_REG_CR0_BASE + 24) {
+        return s->cfg[(offset - PL330_REG_CR0_BASE) >> 2];
+    }
+    if (offset >= PL330_REG_CHANCTRL && offset < PL330_REG_DBGSTATUS) {
+        offset -= PL330_REG_CHANCTRL;
+        chan_id = offset >> 5;
+        if (chan_id >= s->num_chnls) {
+            qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
+                          TARGET_FMT_plx "\n", offset);
+            return 0;
+        }
+        switch (offset & 0x1f) {
+        case 0x00:
+            return s->chan[chan_id].src;
+        case 0x04:
+            return s->chan[chan_id].dst;
+        case 0x08:
+            return s->chan[chan_id].control;
+        case 0x0C:
+            return s->chan[chan_id].lc[0];
+        case 0x10:
+            return s->chan[chan_id].lc[1];
+        default:
+            qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
+                          TARGET_FMT_plx "\n", offset);
+            return 0;
+        }
+    }
+    if (offset >= PL330_REG_CSR_BASE && offset < 0x400) {
+        offset -= PL330_REG_CSR_BASE;
+        chan_id = offset >> 3;
+        if (chan_id >= s->num_chnls) {
+            qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
+                          TARGET_FMT_plx "\n", offset);
+            return 0;
+        }
+        switch ((offset >> 2) & 1) {
+        case 0x0:
+            res = (s->chan[chan_id].ns << 21) |
+                    (s->chan[chan_id].wakeup << 4) |
+                    (s->chan[chan_id].state) |
+                    (s->chan[chan_id].wfp_sbp << 14);
+            return res;
+        case 0x1:
+            return s->chan[chan_id].pc;
+        default:
+            qemu_log_mask(LOG_GUEST_ERROR, "pl330: read error\n");
+            return 0;
+        }
+    }
+    if (offset >= PL330_REG_FTR_BASE && offset < 0x100) {
+        offset -= PL330_REG_FTR_BASE;
+        chan_id = offset >> 2;
+        if (chan_id >= s->num_chnls) {
+            qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
+                          TARGET_FMT_plx "\n", offset);
+            return 0;
+        }
+        return s->chan[chan_id].fault_type;
+    }
+    switch (offset) {
+    case PL330_REG_DSR:
+        return (s->manager.ns << 9) | (s->manager.wakeup << 4) |
+            (s->manager.state & 0xf);
+    case PL330_REG_DPC:
+        return s->manager.pc;
+    case PL330_REG_INTEN:
+        return s->inten;
+    case PL330_REG_INT_EVENT_RIS:
+        return s->ev_status;
+    case PL330_REG_INTMIS:
+        return s->int_status;
+    case PL330_REG_INTCLR:
+        /* Documentation says that we can't read this register
+         * but linux kernel does it
+         */
+        return 0;
+    case PL330_REG_FSRD:
+        return s->manager.state ? 1 : 0;
+    case PL330_REG_FSRC:
+        res = 0;
+        for (i = 0; i < s->num_chnls; i++) {
+            if (s->chan[i].state == pl330_chan_fault ||
+                s->chan[i].state == pl330_chan_fault_completing) {
+                res |= 1 << i;
+            }
+        }
+        return res;
+    case PL330_REG_FTRD:
+        return s->manager.fault_type;
+    case PL330_REG_DBGSTATUS:
+        return s->debug_status;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
+                      TARGET_FMT_plx "\n", offset);
+    }
+    return 0;
+}
+
+static uint64_t pl330_iomem_read(void *opaque, hwaddr offset,
+        unsigned size)
+{
+    int ret = pl330_iomem_read_imp(opaque, offset);
+    DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, ret);
+    return ret;
+}
+
+static const MemoryRegionOps pl330_ops = {
+    .read = pl330_iomem_read,
+    .write = pl330_iomem_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    }
+};
+
+/* Controller logic and initialization */
+
+static void pl330_chan_reset(PL330Chan *ch)
+{
+    ch->src = 0;
+    ch->dst = 0;
+    ch->pc = 0;
+    ch->state = pl330_chan_stopped;
+    ch->watchdog_timer = 0;
+    ch->stall = 0;
+    ch->control = 0;
+    ch->status = 0;
+    ch->fault_type = 0;
+}
+
+static void pl330_reset(DeviceState *d)
+{
+    int i;
+    PL330State *s = PL330(d);
+
+    s->inten = 0;
+    s->int_status = 0;
+    s->ev_status = 0;
+    s->debug_status = 0;
+    s->num_faulting = 0;
+    s->manager.ns = s->mgr_ns_at_rst;
+    pl330_fifo_reset(&s->fifo);
+    pl330_queue_reset(&s->read_queue);
+    pl330_queue_reset(&s->write_queue);
+
+    for (i = 0; i < s->num_chnls; i++) {
+        pl330_chan_reset(&s->chan[i]);
+    }
+    for (i = 0; i < s->num_periph_req; i++) {
+        s->periph_busy[i] = 0;
+    }
+
+    qemu_del_timer(s->timer);
+}
+
+static void pl330_realize(DeviceState *dev, Error **errp)
+{
+    int i;
+    PL330State *s = PL330(dev);
+
+    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq_abort);
+    memory_region_init_io(&s->iomem, &pl330_ops, s, "dma", PL330_IOMEM_SIZE);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
+
+    s->timer = qemu_new_timer_ns(vm_clock, pl330_exec_cycle_timer, s);
+
+    s->cfg[0] = (s->mgr_ns_at_rst ? 0x4 : 0) |
+                (s->num_periph_req > 0 ? 1 : 0) |
+                ((s->num_chnls - 1) & 0x7) << 4 |
+                ((s->num_periph_req - 1) & 0x1f) << 12 |
+                ((s->num_events - 1) & 0x1f) << 17;
+
+    switch (s->i_cache_len) {
+    case (4):
+        s->cfg[1] |= 2;
+        break;
+    case (8):
+        s->cfg[1] |= 3;
+        break;
+    case (16):
+        s->cfg[1] |= 4;
+        break;
+    case (32):
+        s->cfg[1] |= 5;
+        break;
+    default:
+        error_setg(errp, "Bad value for i-cache_len property: %d\n",
+                   s->i_cache_len);
+        return;
+    }
+    s->cfg[1] |= ((s->num_i_cache_lines - 1) & 0xf) << 4;
+
+    s->chan = g_new0(PL330Chan, s->num_chnls);
+    s->hi_seqn = g_new0(uint8_t, s->num_chnls);
+    s->lo_seqn = g_new0(uint8_t, s->num_chnls);
+    for (i = 0; i < s->num_chnls; i++) {
+        s->chan[i].parent = s;
+        s->chan[i].tag = (uint8_t)i;
+    }
+    s->manager.parent = s;
+    s->manager.tag = s->num_chnls;
+    s->manager.is_manager = true;
+
+    s->irq = g_new0(qemu_irq, s->num_events);
+    for (i = 0; i < s->num_events; i++) {
+        sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]);
+    }
+
+    qdev_init_gpio_in(dev, pl330_dma_stop_irq, PL330_PERIPH_NUM);
+
+    switch (s->data_width) {
+    case (32):
+        s->cfg[CFG_CRD] |= 0x2;
+        break;
+    case (64):
+        s->cfg[CFG_CRD] |= 0x3;
+        break;
+    case (128):
+        s->cfg[CFG_CRD] |= 0x4;
+        break;
+    default:
+        error_setg(errp, "Bad value for data_width property: %d\n",
+                   s->data_width);
+        return;
+    }
+
+    s->cfg[CFG_CRD] |= ((s->wr_cap - 1) & 0x7) << 4 |
+                    ((s->wr_q_dep - 1) & 0xf) << 8 |
+                    ((s->rd_cap - 1) & 0x7) << 12 |
+                    ((s->rd_q_dep - 1) & 0xf) << 16 |
+                    ((s->data_buffer_dep - 1) & 0x1ff) << 20;
+
+    pl330_queue_init(&s->read_queue, s->rd_q_dep, s);
+    pl330_queue_init(&s->write_queue, s->wr_q_dep, s);
+    pl330_fifo_init(&s->fifo, s->data_buffer_dep);
+}
+
+static Property pl330_properties[] = {
+    /* CR0 */
+    DEFINE_PROP_UINT32("num_chnls", PL330State, num_chnls, 8),
+    DEFINE_PROP_UINT8("num_periph_req", PL330State, num_periph_req, 4),
+    DEFINE_PROP_UINT8("num_events", PL330State, num_events, 16),
+    DEFINE_PROP_UINT8("mgr_ns_at_rst", PL330State, mgr_ns_at_rst, 0),
+    /* CR1 */
+    DEFINE_PROP_UINT8("i-cache_len", PL330State, i_cache_len, 4),
+    DEFINE_PROP_UINT8("num_i-cache_lines", PL330State, num_i_cache_lines, 8),
+    /* CR2-4 */
+    DEFINE_PROP_UINT32("boot_addr", PL330State, cfg[CFG_BOOT_ADDR], 0),
+    DEFINE_PROP_UINT32("INS", PL330State, cfg[CFG_INS], 0),
+    DEFINE_PROP_UINT32("PNS", PL330State, cfg[CFG_PNS], 0),
+    /* CRD */
+    DEFINE_PROP_UINT8("data_width", PL330State, data_width, 64),
+    DEFINE_PROP_UINT8("wr_cap", PL330State, wr_cap, 8),
+    DEFINE_PROP_UINT8("wr_q_dep", PL330State, wr_q_dep, 16),
+    DEFINE_PROP_UINT8("rd_cap", PL330State, rd_cap, 8),
+    DEFINE_PROP_UINT8("rd_q_dep", PL330State, rd_q_dep, 16),
+    DEFINE_PROP_UINT16("data_buffer_dep", PL330State, data_buffer_dep, 256),
+
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pl330_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = pl330_realize;
+    dc->reset = pl330_reset;
+    dc->props = pl330_properties;
+    dc->vmsd = &vmstate_pl330;
+}
+
+static const TypeInfo pl330_type_info = {
+    .name           = TYPE_PL330,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(PL330State),
+    .class_init      = pl330_class_init,
+};
+
+static void pl330_register_types(void)
+{
+    type_register_static(&pl330_type_info);
+}
+
+type_init(pl330_register_types)
diff --git a/hw/dma/puv3_dma.c b/hw/dma/puv3_dma.c
new file mode 100644 (file)
index 0000000..32844b5
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * DMA device simulation in PKUnity SoC
+ *
+ * Copyright (C) 2010-2012 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or any later version.
+ * See the COPYING file in the top-level directory.
+ */
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+
+#undef DEBUG_PUV3
+#include "hw/unicore32/puv3.h"
+
+#define PUV3_DMA_CH_NR          (6)
+#define PUV3_DMA_CH_MASK        (0xff)
+#define PUV3_DMA_CH(offset)     ((offset) >> 8)
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    uint32_t reg_CFG[PUV3_DMA_CH_NR];
+} PUV3DMAState;
+
+static uint64_t puv3_dma_read(void *opaque, hwaddr offset,
+        unsigned size)
+{
+    PUV3DMAState *s = opaque;
+    uint32_t ret = 0;
+
+    assert(PUV3_DMA_CH(offset) < PUV3_DMA_CH_NR);
+
+    switch (offset & PUV3_DMA_CH_MASK) {
+    case 0x10:
+        ret = s->reg_CFG[PUV3_DMA_CH(offset)];
+        break;
+    default:
+        DPRINTF("Bad offset 0x%x\n", offset);
+    }
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
+
+    return ret;
+}
+
+static void puv3_dma_write(void *opaque, hwaddr offset,
+        uint64_t value, unsigned size)
+{
+    PUV3DMAState *s = opaque;
+
+    assert(PUV3_DMA_CH(offset) < PUV3_DMA_CH_NR);
+
+    switch (offset & PUV3_DMA_CH_MASK) {
+    case 0x10:
+        s->reg_CFG[PUV3_DMA_CH(offset)] = value;
+        break;
+    default:
+        DPRINTF("Bad offset 0x%x\n", offset);
+    }
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
+}
+
+static const MemoryRegionOps puv3_dma_ops = {
+    .read = puv3_dma_read,
+    .write = puv3_dma_write,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int puv3_dma_init(SysBusDevice *dev)
+{
+    PUV3DMAState *s = FROM_SYSBUS(PUV3DMAState, dev);
+    int i;
+
+    for (i = 0; i < PUV3_DMA_CH_NR; i++) {
+        s->reg_CFG[i] = 0x0;
+    }
+
+    memory_region_init_io(&s->iomem, &puv3_dma_ops, s, "puv3_dma",
+            PUV3_REGS_OFFSET);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static void puv3_dma_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = puv3_dma_init;
+}
+
+static const TypeInfo puv3_dma_info = {
+    .name = "puv3_dma",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(PUV3DMAState),
+    .class_init = puv3_dma_class_init,
+};
+
+static void puv3_dma_register_type(void)
+{
+    type_register_static(&puv3_dma_info);
+}
+
+type_init(puv3_dma_register_type)
diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c
new file mode 100644 (file)
index 0000000..03f92f1
--- /dev/null
@@ -0,0 +1,825 @@
+/*
+ * QEMU JAZZ RC4030 chipset
+ *
+ * Copyright (c) 2007-2009 Herve Poussineau
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/mips/mips.h"
+#include "qemu/timer.h"
+
+/********************************************************/
+/* debug rc4030 */
+
+//#define DEBUG_RC4030
+//#define DEBUG_RC4030_DMA
+
+#ifdef DEBUG_RC4030
+#define DPRINTF(fmt, ...) \
+do { printf("rc4030: " fmt , ## __VA_ARGS__); } while (0)
+static const char* irq_names[] = { "parallel", "floppy", "sound", "video",
+            "network", "scsi", "keyboard", "mouse", "serial0", "serial1" };
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#define RC4030_ERROR(fmt, ...) \
+do { fprintf(stderr, "rc4030 ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
+
+/********************************************************/
+/* rc4030 emulation                                     */
+
+typedef struct dma_pagetable_entry {
+    int32_t frame;
+    int32_t owner;
+} QEMU_PACKED dma_pagetable_entry;
+
+#define DMA_PAGESIZE    4096
+#define DMA_REG_ENABLE  1
+#define DMA_REG_COUNT   2
+#define DMA_REG_ADDRESS 3
+
+#define DMA_FLAG_ENABLE     0x0001
+#define DMA_FLAG_MEM_TO_DEV 0x0002
+#define DMA_FLAG_TC_INTR    0x0100
+#define DMA_FLAG_MEM_INTR   0x0200
+#define DMA_FLAG_ADDR_INTR  0x0400
+
+typedef struct rc4030State
+{
+    uint32_t config; /* 0x0000: RC4030 config register */
+    uint32_t revision; /* 0x0008: RC4030 Revision register */
+    uint32_t invalid_address_register; /* 0x0010: Invalid Address register */
+
+    /* DMA */
+    uint32_t dma_regs[8][4];
+    uint32_t dma_tl_base; /* 0x0018: DMA transl. table base */
+    uint32_t dma_tl_limit; /* 0x0020: DMA transl. table limit */
+
+    /* cache */
+    uint32_t cache_maint; /* 0x0030: Cache Maintenance */
+    uint32_t remote_failed_address; /* 0x0038: Remote Failed Address */
+    uint32_t memory_failed_address; /* 0x0040: Memory Failed Address */
+    uint32_t cache_ptag; /* 0x0048: I/O Cache Physical Tag */
+    uint32_t cache_ltag; /* 0x0050: I/O Cache Logical Tag */
+    uint32_t cache_bmask; /* 0x0058: I/O Cache Byte Mask */
+
+    uint32_t nmi_interrupt; /* 0x0200: interrupt source */
+    uint32_t offset210;
+    uint32_t nvram_protect; /* 0x0220: NV ram protect register */
+    uint32_t rem_speed[16];
+    uint32_t imr_jazz; /* Local bus int enable mask */
+    uint32_t isr_jazz; /* Local bus int source */
+
+    /* timer */
+    QEMUTimer *periodic_timer;
+    uint32_t itr; /* Interval timer reload */
+
+    qemu_irq timer_irq;
+    qemu_irq jazz_bus_irq;
+
+    MemoryRegion iomem_chipset;
+    MemoryRegion iomem_jazzio;
+} rc4030State;
+
+static void set_next_tick(rc4030State *s)
+{
+    qemu_irq_lower(s->timer_irq);
+    uint32_t tm_hz;
+
+    tm_hz = 1000 / (s->itr + 1);
+
+    qemu_mod_timer(s->periodic_timer, qemu_get_clock_ns(vm_clock) +
+                   get_ticks_per_sec() / tm_hz);
+}
+
+/* called for accesses to rc4030 */
+static uint32_t rc4030_readl(void *opaque, hwaddr addr)
+{
+    rc4030State *s = opaque;
+    uint32_t val;
+
+    addr &= 0x3fff;
+    switch (addr & ~0x3) {
+    /* Global config register */
+    case 0x0000:
+        val = s->config;
+        break;
+    /* Revision register */
+    case 0x0008:
+        val = s->revision;
+        break;
+    /* Invalid Address register */
+    case 0x0010:
+        val = s->invalid_address_register;
+        break;
+    /* DMA transl. table base */
+    case 0x0018:
+        val = s->dma_tl_base;
+        break;
+    /* DMA transl. table limit */
+    case 0x0020:
+        val = s->dma_tl_limit;
+        break;
+    /* Remote Failed Address */
+    case 0x0038:
+        val = s->remote_failed_address;
+        break;
+    /* Memory Failed Address */
+    case 0x0040:
+        val = s->memory_failed_address;
+        break;
+    /* I/O Cache Byte Mask */
+    case 0x0058:
+        val = s->cache_bmask;
+        /* HACK */
+        if (s->cache_bmask == (uint32_t)-1)
+            s->cache_bmask = 0;
+        break;
+    /* Remote Speed Registers */
+    case 0x0070:
+    case 0x0078:
+    case 0x0080:
+    case 0x0088:
+    case 0x0090:
+    case 0x0098:
+    case 0x00a0:
+    case 0x00a8:
+    case 0x00b0:
+    case 0x00b8:
+    case 0x00c0:
+    case 0x00c8:
+    case 0x00d0:
+    case 0x00d8:
+    case 0x00e0:
+    case 0x00e8:
+        val = s->rem_speed[(addr - 0x0070) >> 3];
+        break;
+    /* DMA channel base address */
+    case 0x0100:
+    case 0x0108:
+    case 0x0110:
+    case 0x0118:
+    case 0x0120:
+    case 0x0128:
+    case 0x0130:
+    case 0x0138:
+    case 0x0140:
+    case 0x0148:
+    case 0x0150:
+    case 0x0158:
+    case 0x0160:
+    case 0x0168:
+    case 0x0170:
+    case 0x0178:
+    case 0x0180:
+    case 0x0188:
+    case 0x0190:
+    case 0x0198:
+    case 0x01a0:
+    case 0x01a8:
+    case 0x01b0:
+    case 0x01b8:
+    case 0x01c0:
+    case 0x01c8:
+    case 0x01d0:
+    case 0x01d8:
+    case 0x01e0:
+    case 0x01e8:
+    case 0x01f0:
+    case 0x01f8:
+        {
+            int entry = (addr - 0x0100) >> 5;
+            int idx = (addr & 0x1f) >> 3;
+            val = s->dma_regs[entry][idx];
+        }
+        break;
+    /* Interrupt source */
+    case 0x0200:
+        val = s->nmi_interrupt;
+        break;
+    /* Error type */
+    case 0x0208:
+        val = 0;
+        break;
+    /* Offset 0x0210 */
+    case 0x0210:
+        val = s->offset210;
+        break;
+    /* NV ram protect register */
+    case 0x0220:
+        val = s->nvram_protect;
+        break;
+    /* Interval timer count */
+    case 0x0230:
+        val = 0;
+        qemu_irq_lower(s->timer_irq);
+        break;
+    /* EISA interrupt */
+    case 0x0238:
+        val = 7; /* FIXME: should be read from EISA controller */
+        break;
+    default:
+        RC4030_ERROR("invalid read [" TARGET_FMT_plx "]\n", addr);
+        val = 0;
+        break;
+    }
+
+    if ((addr & ~3) != 0x230) {
+        DPRINTF("read 0x%02x at " TARGET_FMT_plx "\n", val, addr);
+    }
+
+    return val;
+}
+
+static uint32_t rc4030_readw(void *opaque, hwaddr addr)
+{
+    uint32_t v = rc4030_readl(opaque, addr & ~0x3);
+    if (addr & 0x2)
+        return v >> 16;
+    else
+        return v & 0xffff;
+}
+
+static uint32_t rc4030_readb(void *opaque, hwaddr addr)
+{
+    uint32_t v = rc4030_readl(opaque, addr & ~0x3);
+    return (v >> (8 * (addr & 0x3))) & 0xff;
+}
+
+static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val)
+{
+    rc4030State *s = opaque;
+    addr &= 0x3fff;
+
+    DPRINTF("write 0x%02x at " TARGET_FMT_plx "\n", val, addr);
+
+    switch (addr & ~0x3) {
+    /* Global config register */
+    case 0x0000:
+        s->config = val;
+        break;
+    /* DMA transl. table base */
+    case 0x0018:
+        s->dma_tl_base = val;
+        break;
+    /* DMA transl. table limit */
+    case 0x0020:
+        s->dma_tl_limit = val;
+        break;
+    /* DMA transl. table invalidated */
+    case 0x0028:
+        break;
+    /* Cache Maintenance */
+    case 0x0030:
+        s->cache_maint = val;
+        break;
+    /* I/O Cache Physical Tag */
+    case 0x0048:
+        s->cache_ptag = val;
+        break;
+    /* I/O Cache Logical Tag */
+    case 0x0050:
+        s->cache_ltag = val;
+        break;
+    /* I/O Cache Byte Mask */
+    case 0x0058:
+        s->cache_bmask |= val; /* HACK */
+        break;
+    /* I/O Cache Buffer Window */
+    case 0x0060:
+        /* HACK */
+        if (s->cache_ltag == 0x80000001 && s->cache_bmask == 0xf0f0f0f) {
+            hwaddr dest = s->cache_ptag & ~0x1;
+            dest += (s->cache_maint & 0x3) << 3;
+            cpu_physical_memory_write(dest, &val, 4);
+        }
+        break;
+    /* Remote Speed Registers */
+    case 0x0070:
+    case 0x0078:
+    case 0x0080:
+    case 0x0088:
+    case 0x0090:
+    case 0x0098:
+    case 0x00a0:
+    case 0x00a8:
+    case 0x00b0:
+    case 0x00b8:
+    case 0x00c0:
+    case 0x00c8:
+    case 0x00d0:
+    case 0x00d8:
+    case 0x00e0:
+    case 0x00e8:
+        s->rem_speed[(addr - 0x0070) >> 3] = val;
+        break;
+    /* DMA channel base address */
+    case 0x0100:
+    case 0x0108:
+    case 0x0110:
+    case 0x0118:
+    case 0x0120:
+    case 0x0128:
+    case 0x0130:
+    case 0x0138:
+    case 0x0140:
+    case 0x0148:
+    case 0x0150:
+    case 0x0158:
+    case 0x0160:
+    case 0x0168:
+    case 0x0170:
+    case 0x0178:
+    case 0x0180:
+    case 0x0188:
+    case 0x0190:
+    case 0x0198:
+    case 0x01a0:
+    case 0x01a8:
+    case 0x01b0:
+    case 0x01b8:
+    case 0x01c0:
+    case 0x01c8:
+    case 0x01d0:
+    case 0x01d8:
+    case 0x01e0:
+    case 0x01e8:
+    case 0x01f0:
+    case 0x01f8:
+        {
+            int entry = (addr - 0x0100) >> 5;
+            int idx = (addr & 0x1f) >> 3;
+            s->dma_regs[entry][idx] = val;
+        }
+        break;
+    /* Offset 0x0210 */
+    case 0x0210:
+        s->offset210 = val;
+        break;
+    /* Interval timer reload */
+    case 0x0228:
+        s->itr = val;
+        qemu_irq_lower(s->timer_irq);
+        set_next_tick(s);
+        break;
+    /* EISA interrupt */
+    case 0x0238:
+        break;
+    default:
+        RC4030_ERROR("invalid write of 0x%02x at [" TARGET_FMT_plx "]\n", val, addr);
+        break;
+    }
+}
+
+static void rc4030_writew(void *opaque, hwaddr addr, uint32_t val)
+{
+    uint32_t old_val = rc4030_readl(opaque, addr & ~0x3);
+
+    if (addr & 0x2)
+        val = (val << 16) | (old_val & 0x0000ffff);
+    else
+        val = val | (old_val & 0xffff0000);
+    rc4030_writel(opaque, addr & ~0x3, val);
+}
+
+static void rc4030_writeb(void *opaque, hwaddr addr, uint32_t val)
+{
+    uint32_t old_val = rc4030_readl(opaque, addr & ~0x3);
+
+    switch (addr & 3) {
+    case 0:
+        val = val | (old_val & 0xffffff00);
+        break;
+    case 1:
+        val = (val << 8) | (old_val & 0xffff00ff);
+        break;
+    case 2:
+        val = (val << 16) | (old_val & 0xff00ffff);
+        break;
+    case 3:
+        val = (val << 24) | (old_val & 0x00ffffff);
+        break;
+    }
+    rc4030_writel(opaque, addr & ~0x3, val);
+}
+
+static const MemoryRegionOps rc4030_ops = {
+    .old_mmio = {
+        .read = { rc4030_readb, rc4030_readw, rc4030_readl, },
+        .write = { rc4030_writeb, rc4030_writew, rc4030_writel, },
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void update_jazz_irq(rc4030State *s)
+{
+    uint16_t pending;
+
+    pending = s->isr_jazz & s->imr_jazz;
+
+#ifdef DEBUG_RC4030
+    if (s->isr_jazz != 0) {
+        uint32_t irq = 0;
+        DPRINTF("pending irqs:");
+        for (irq = 0; irq < ARRAY_SIZE(irq_names); irq++) {
+            if (s->isr_jazz & (1 << irq)) {
+                printf(" %s", irq_names[irq]);
+                if (!(s->imr_jazz & (1 << irq))) {
+                    printf("(ignored)");
+                }
+            }
+        }
+        printf("\n");
+    }
+#endif
+
+    if (pending != 0)
+        qemu_irq_raise(s->jazz_bus_irq);
+    else
+        qemu_irq_lower(s->jazz_bus_irq);
+}
+
+static void rc4030_irq_jazz_request(void *opaque, int irq, int level)
+{
+    rc4030State *s = opaque;
+
+    if (level) {
+        s->isr_jazz |= 1 << irq;
+    } else {
+        s->isr_jazz &= ~(1 << irq);
+    }
+
+    update_jazz_irq(s);
+}
+
+static void rc4030_periodic_timer(void *opaque)
+{
+    rc4030State *s = opaque;
+
+    set_next_tick(s);
+    qemu_irq_raise(s->timer_irq);
+}
+
+static uint32_t jazzio_readw(void *opaque, hwaddr addr)
+{
+    rc4030State *s = opaque;
+    uint32_t val;
+    uint32_t irq;
+    addr &= 0xfff;
+
+    switch (addr) {
+    /* Local bus int source */
+    case 0x00: {
+        uint32_t pending = s->isr_jazz & s->imr_jazz;
+        val = 0;
+        irq = 0;
+        while (pending) {
+            if (pending & 1) {
+                DPRINTF("returning irq %s\n", irq_names[irq]);
+                val = (irq + 1) << 2;
+                break;
+            }
+            irq++;
+            pending >>= 1;
+        }
+        break;
+    }
+    /* Local bus int enable mask */
+    case 0x02:
+        val = s->imr_jazz;
+        break;
+    default:
+        RC4030_ERROR("(jazz io controller) invalid read [" TARGET_FMT_plx "]\n", addr);
+        val = 0;
+    }
+
+    DPRINTF("(jazz io controller) read 0x%04x at " TARGET_FMT_plx "\n", val, addr);
+
+    return val;
+}
+
+static uint32_t jazzio_readb(void *opaque, hwaddr addr)
+{
+    uint32_t v;
+    v = jazzio_readw(opaque, addr & ~0x1);
+    return (v >> (8 * (addr & 0x1))) & 0xff;
+}
+
+static uint32_t jazzio_readl(void *opaque, hwaddr addr)
+{
+    uint32_t v;
+    v = jazzio_readw(opaque, addr);
+    v |= jazzio_readw(opaque, addr + 2) << 16;
+    return v;
+}
+
+static void jazzio_writew(void *opaque, hwaddr addr, uint32_t val)
+{
+    rc4030State *s = opaque;
+    addr &= 0xfff;
+
+    DPRINTF("(jazz io controller) write 0x%04x at " TARGET_FMT_plx "\n", val, addr);
+
+    switch (addr) {
+    /* Local bus int enable mask */
+    case 0x02:
+        s->imr_jazz = val;
+        update_jazz_irq(s);
+        break;
+    default:
+        RC4030_ERROR("(jazz io controller) invalid write of 0x%04x at [" TARGET_FMT_plx "]\n", val, addr);
+        break;
+    }
+}
+
+static void jazzio_writeb(void *opaque, hwaddr addr, uint32_t val)
+{
+    uint32_t old_val = jazzio_readw(opaque, addr & ~0x1);
+
+    switch (addr & 1) {
+    case 0:
+        val = val | (old_val & 0xff00);
+        break;
+    case 1:
+        val = (val << 8) | (old_val & 0x00ff);
+        break;
+    }
+    jazzio_writew(opaque, addr & ~0x1, val);
+}
+
+static void jazzio_writel(void *opaque, hwaddr addr, uint32_t val)
+{
+    jazzio_writew(opaque, addr, val & 0xffff);
+    jazzio_writew(opaque, addr + 2, (val >> 16) & 0xffff);
+}
+
+static const MemoryRegionOps jazzio_ops = {
+    .old_mmio = {
+        .read = { jazzio_readb, jazzio_readw, jazzio_readl, },
+        .write = { jazzio_writeb, jazzio_writew, jazzio_writel, },
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void rc4030_reset(void *opaque)
+{
+    rc4030State *s = opaque;
+    int i;
+
+    s->config = 0x410; /* some boards seem to accept 0x104 too */
+    s->revision = 1;
+    s->invalid_address_register = 0;
+
+    memset(s->dma_regs, 0, sizeof(s->dma_regs));
+    s->dma_tl_base = s->dma_tl_limit = 0;
+
+    s->remote_failed_address = s->memory_failed_address = 0;
+    s->cache_maint = 0;
+    s->cache_ptag = s->cache_ltag = 0;
+    s->cache_bmask = 0;
+
+    s->offset210 = 0x18186;
+    s->nvram_protect = 7;
+    for (i = 0; i < 15; i++)
+        s->rem_speed[i] = 7;
+    s->imr_jazz = 0x10; /* XXX: required by firmware, but why? */
+    s->isr_jazz = 0;
+
+    s->itr = 0;
+
+    qemu_irq_lower(s->timer_irq);
+    qemu_irq_lower(s->jazz_bus_irq);
+}
+
+static int rc4030_load(QEMUFile *f, void *opaque, int version_id)
+{
+    rc4030State* s = opaque;
+    int i, j;
+
+    if (version_id != 2)
+        return -EINVAL;
+
+    s->config = qemu_get_be32(f);
+    s->invalid_address_register = qemu_get_be32(f);
+    for (i = 0; i < 8; i++)
+        for (j = 0; j < 4; j++)
+            s->dma_regs[i][j] = qemu_get_be32(f);
+    s->dma_tl_base = qemu_get_be32(f);
+    s->dma_tl_limit = qemu_get_be32(f);
+    s->cache_maint = qemu_get_be32(f);
+    s->remote_failed_address = qemu_get_be32(f);
+    s->memory_failed_address = qemu_get_be32(f);
+    s->cache_ptag = qemu_get_be32(f);
+    s->cache_ltag = qemu_get_be32(f);
+    s->cache_bmask = qemu_get_be32(f);
+    s->offset210 = qemu_get_be32(f);
+    s->nvram_protect = qemu_get_be32(f);
+    for (i = 0; i < 15; i++)
+        s->rem_speed[i] = qemu_get_be32(f);
+    s->imr_jazz = qemu_get_be32(f);
+    s->isr_jazz = qemu_get_be32(f);
+    s->itr = qemu_get_be32(f);
+
+    set_next_tick(s);
+    update_jazz_irq(s);
+
+    return 0;
+}
+
+static void rc4030_save(QEMUFile *f, void *opaque)
+{
+    rc4030State* s = opaque;
+    int i, j;
+
+    qemu_put_be32(f, s->config);
+    qemu_put_be32(f, s->invalid_address_register);
+    for (i = 0; i < 8; i++)
+        for (j = 0; j < 4; j++)
+            qemu_put_be32(f, s->dma_regs[i][j]);
+    qemu_put_be32(f, s->dma_tl_base);
+    qemu_put_be32(f, s->dma_tl_limit);
+    qemu_put_be32(f, s->cache_maint);
+    qemu_put_be32(f, s->remote_failed_address);
+    qemu_put_be32(f, s->memory_failed_address);
+    qemu_put_be32(f, s->cache_ptag);
+    qemu_put_be32(f, s->cache_ltag);
+    qemu_put_be32(f, s->cache_bmask);
+    qemu_put_be32(f, s->offset210);
+    qemu_put_be32(f, s->nvram_protect);
+    for (i = 0; i < 15; i++)
+        qemu_put_be32(f, s->rem_speed[i]);
+    qemu_put_be32(f, s->imr_jazz);
+    qemu_put_be32(f, s->isr_jazz);
+    qemu_put_be32(f, s->itr);
+}
+
+void rc4030_dma_memory_rw(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write)
+{
+    rc4030State *s = opaque;
+    hwaddr entry_addr;
+    hwaddr phys_addr;
+    dma_pagetable_entry entry;
+    int index;
+    int ncpy, i;
+
+    i = 0;
+    for (;;) {
+        if (i == len) {
+            break;
+        }
+
+        ncpy = DMA_PAGESIZE - (addr & (DMA_PAGESIZE - 1));
+        if (ncpy > len - i)
+            ncpy = len - i;
+
+        /* Get DMA translation table entry */
+        index = addr / DMA_PAGESIZE;
+        if (index >= s->dma_tl_limit / sizeof(dma_pagetable_entry)) {
+            break;
+        }
+        entry_addr = s->dma_tl_base + index * sizeof(dma_pagetable_entry);
+        /* XXX: not sure. should we really use only lowest bits? */
+        entry_addr &= 0x7fffffff;
+        cpu_physical_memory_read(entry_addr, &entry, sizeof(entry));
+
+        /* Read/write data at right place */
+        phys_addr = entry.frame + (addr & (DMA_PAGESIZE - 1));
+        cpu_physical_memory_rw(phys_addr, &buf[i], ncpy, is_write);
+
+        i += ncpy;
+        addr += ncpy;
+    }
+}
+
+static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_write)
+{
+    rc4030State *s = opaque;
+    hwaddr dma_addr;
+    int dev_to_mem;
+
+    s->dma_regs[n][DMA_REG_ENABLE] &= ~(DMA_FLAG_TC_INTR | DMA_FLAG_MEM_INTR | DMA_FLAG_ADDR_INTR);
+
+    /* Check DMA channel consistency */
+    dev_to_mem = (s->dma_regs[n][DMA_REG_ENABLE] & DMA_FLAG_MEM_TO_DEV) ? 0 : 1;
+    if (!(s->dma_regs[n][DMA_REG_ENABLE] & DMA_FLAG_ENABLE) ||
+        (is_write != dev_to_mem)) {
+        s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_MEM_INTR;
+        s->nmi_interrupt |= 1 << n;
+        return;
+    }
+
+    /* Get start address and len */
+    if (len > s->dma_regs[n][DMA_REG_COUNT])
+        len = s->dma_regs[n][DMA_REG_COUNT];
+    dma_addr = s->dma_regs[n][DMA_REG_ADDRESS];
+
+    /* Read/write data at right place */
+    rc4030_dma_memory_rw(opaque, dma_addr, buf, len, is_write);
+
+    s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_TC_INTR;
+    s->dma_regs[n][DMA_REG_COUNT] -= len;
+
+#ifdef DEBUG_RC4030_DMA
+    {
+        int i, j;
+        printf("rc4030 dma: Copying %d bytes %s host %p\n",
+            len, is_write ? "from" : "to", buf);
+        for (i = 0; i < len; i += 16) {
+            int n = 16;
+            if (n > len - i) {
+                n = len - i;
+            }
+            for (j = 0; j < n; j++)
+                printf("%02x ", buf[i + j]);
+            while (j++ < 16)
+                printf("   ");
+            printf("| ");
+            for (j = 0; j < n; j++)
+                printf("%c", isprint(buf[i + j]) ? buf[i + j] : '.');
+            printf("\n");
+        }
+    }
+#endif
+}
+
+struct rc4030DMAState {
+    void *opaque;
+    int n;
+};
+
+void rc4030_dma_read(void *dma, uint8_t *buf, int len)
+{
+    rc4030_dma s = dma;
+    rc4030_do_dma(s->opaque, s->n, buf, len, 0);
+}
+
+void rc4030_dma_write(void *dma, uint8_t *buf, int len)
+{
+    rc4030_dma s = dma;
+    rc4030_do_dma(s->opaque, s->n, buf, len, 1);
+}
+
+static rc4030_dma *rc4030_allocate_dmas(void *opaque, int n)
+{
+    rc4030_dma *s;
+    struct rc4030DMAState *p;
+    int i;
+
+    s = (rc4030_dma *)g_malloc0(sizeof(rc4030_dma) * n);
+    p = (struct rc4030DMAState *)g_malloc0(sizeof(struct rc4030DMAState) * n);
+    for (i = 0; i < n; i++) {
+        p->opaque = opaque;
+        p->n = i;
+        s[i] = p;
+        p++;
+    }
+    return s;
+}
+
+void *rc4030_init(qemu_irq timer, qemu_irq jazz_bus,
+                  qemu_irq **irqs, rc4030_dma **dmas,
+                  MemoryRegion *sysmem)
+{
+    rc4030State *s;
+
+    s = g_malloc0(sizeof(rc4030State));
+
+    *irqs = qemu_allocate_irqs(rc4030_irq_jazz_request, s, 16);
+    *dmas = rc4030_allocate_dmas(s, 4);
+
+    s->periodic_timer = qemu_new_timer_ns(vm_clock, rc4030_periodic_timer, s);
+    s->timer_irq = timer;
+    s->jazz_bus_irq = jazz_bus;
+
+    qemu_register_reset(rc4030_reset, s);
+    register_savevm(NULL, "rc4030", 0, 2, rc4030_save, rc4030_load, s);
+    rc4030_reset(s);
+
+    memory_region_init_io(&s->iomem_chipset, &rc4030_ops, s,
+                          "rc4030.chipset", 0x300);
+    memory_region_add_subregion(sysmem, 0x80000000, &s->iomem_chipset);
+    memory_region_init_io(&s->iomem_jazzio, &jazzio_ops, s,
+                          "rc4030.jazzio", 0x00001000);
+    memory_region_add_subregion(sysmem, 0xf0000000, &s->iomem_jazzio);
+
+    return s;
+}
diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c
new file mode 100644 (file)
index 0000000..8db1a74
--- /dev/null
@@ -0,0 +1,523 @@
+/*
+ * QEMU model of Xilinx AXI-DMA block.
+ *
+ * Copyright (c) 2011 Edgar E. Iglesias.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "hw/ptimer.h"
+#include "qemu/log.h"
+#include "hw/qdev-addr.h"
+
+#include "hw/stream.h"
+
+#define D(x)
+
+#define R_DMACR             (0x00 / 4)
+#define R_DMASR             (0x04 / 4)
+#define R_CURDESC           (0x08 / 4)
+#define R_TAILDESC          (0x10 / 4)
+#define R_MAX               (0x30 / 4)
+
+enum {
+    DMACR_RUNSTOP = 1,
+    DMACR_TAILPTR_MODE = 2,
+    DMACR_RESET = 4
+};
+
+enum {
+    DMASR_HALTED = 1,
+    DMASR_IDLE  = 2,
+    DMASR_IOC_IRQ  = 1 << 12,
+    DMASR_DLY_IRQ  = 1 << 13,
+
+    DMASR_IRQ_MASK = 7 << 12
+};
+
+struct SDesc {
+    uint64_t nxtdesc;
+    uint64_t buffer_address;
+    uint64_t reserved;
+    uint32_t control;
+    uint32_t status;
+    uint32_t app[6];
+};
+
+enum {
+    SDESC_CTRL_EOF = (1 << 26),
+    SDESC_CTRL_SOF = (1 << 27),
+
+    SDESC_CTRL_LEN_MASK = (1 << 23) - 1
+};
+
+enum {
+    SDESC_STATUS_EOF = (1 << 26),
+    SDESC_STATUS_SOF_BIT = 27,
+    SDESC_STATUS_SOF = (1 << SDESC_STATUS_SOF_BIT),
+    SDESC_STATUS_COMPLETE = (1 << 31)
+};
+
+struct Stream {
+    QEMUBH *bh;
+    ptimer_state *ptimer;
+    qemu_irq irq;
+
+    int nr;
+
+    struct SDesc desc;
+    int pos;
+    unsigned int complete_cnt;
+    uint32_t regs[R_MAX];
+};
+
+struct XilinxAXIDMA {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    uint32_t freqhz;
+    StreamSlave *tx_dev;
+
+    struct Stream streams[2];
+};
+
+/*
+ * Helper calls to extract info from desriptors and other trivial
+ * state from regs.
+ */
+static inline int stream_desc_sof(struct SDesc *d)
+{
+    return d->control & SDESC_CTRL_SOF;
+}
+
+static inline int stream_desc_eof(struct SDesc *d)
+{
+    return d->control & SDESC_CTRL_EOF;
+}
+
+static inline int stream_resetting(struct Stream *s)
+{
+    return !!(s->regs[R_DMACR] & DMACR_RESET);
+}
+
+static inline int stream_running(struct Stream *s)
+{
+    return s->regs[R_DMACR] & DMACR_RUNSTOP;
+}
+
+static inline int stream_halted(struct Stream *s)
+{
+    return s->regs[R_DMASR] & DMASR_HALTED;
+}
+
+static inline int stream_idle(struct Stream *s)
+{
+    return !!(s->regs[R_DMASR] & DMASR_IDLE);
+}
+
+static void stream_reset(struct Stream *s)
+{
+    s->regs[R_DMASR] = DMASR_HALTED;  /* starts up halted.  */
+    s->regs[R_DMACR] = 1 << 16; /* Starts with one in compl threshold.  */
+}
+
+/* Map an offset addr into a channel index.  */
+static inline int streamid_from_addr(hwaddr addr)
+{
+    int sid;
+
+    sid = addr / (0x30);
+    sid &= 1;
+    return sid;
+}
+
+#ifdef DEBUG_ENET
+static void stream_desc_show(struct SDesc *d)
+{
+    qemu_log("buffer_addr  = " PRIx64 "\n", d->buffer_address);
+    qemu_log("nxtdesc      = " PRIx64 "\n", d->nxtdesc);
+    qemu_log("control      = %x\n", d->control);
+    qemu_log("status       = %x\n", d->status);
+}
+#endif
+
+static void stream_desc_load(struct Stream *s, hwaddr addr)
+{
+    struct SDesc *d = &s->desc;
+    int i;
+
+    cpu_physical_memory_read(addr, (void *) d, sizeof *d);
+
+    /* Convert from LE into host endianness.  */
+    d->buffer_address = le64_to_cpu(d->buffer_address);
+    d->nxtdesc = le64_to_cpu(d->nxtdesc);
+    d->control = le32_to_cpu(d->control);
+    d->status = le32_to_cpu(d->status);
+    for (i = 0; i < ARRAY_SIZE(d->app); i++) {
+        d->app[i] = le32_to_cpu(d->app[i]);
+    }
+}
+
+static void stream_desc_store(struct Stream *s, hwaddr addr)
+{
+    struct SDesc *d = &s->desc;
+    int i;
+
+    /* Convert from host endianness into LE.  */
+    d->buffer_address = cpu_to_le64(d->buffer_address);
+    d->nxtdesc = cpu_to_le64(d->nxtdesc);
+    d->control = cpu_to_le32(d->control);
+    d->status = cpu_to_le32(d->status);
+    for (i = 0; i < ARRAY_SIZE(d->app); i++) {
+        d->app[i] = cpu_to_le32(d->app[i]);
+    }
+    cpu_physical_memory_write(addr, (void *) d, sizeof *d);
+}
+
+static void stream_update_irq(struct Stream *s)
+{
+    unsigned int pending, mask, irq;
+
+    pending = s->regs[R_DMASR] & DMASR_IRQ_MASK;
+    mask = s->regs[R_DMACR] & DMASR_IRQ_MASK;
+
+    irq = pending & mask;
+
+    qemu_set_irq(s->irq, !!irq);
+}
+
+static void stream_reload_complete_cnt(struct Stream *s)
+{
+    unsigned int comp_th;
+    comp_th = (s->regs[R_DMACR] >> 16) & 0xff;
+    s->complete_cnt = comp_th;
+}
+
+static void timer_hit(void *opaque)
+{
+    struct Stream *s = opaque;
+
+    stream_reload_complete_cnt(s);
+    s->regs[R_DMASR] |= DMASR_DLY_IRQ;
+    stream_update_irq(s);
+}
+
+static void stream_complete(struct Stream *s)
+{
+    unsigned int comp_delay;
+
+    /* Start the delayed timer.  */
+    comp_delay = s->regs[R_DMACR] >> 24;
+    if (comp_delay) {
+        ptimer_stop(s->ptimer);
+        ptimer_set_count(s->ptimer, comp_delay);
+        ptimer_run(s->ptimer, 1);
+    }
+
+    s->complete_cnt--;
+    if (s->complete_cnt == 0) {
+        /* Raise the IOC irq.  */
+        s->regs[R_DMASR] |= DMASR_IOC_IRQ;
+        stream_reload_complete_cnt(s);
+    }
+}
+
+static void stream_process_mem2s(struct Stream *s,
+                                 StreamSlave *tx_dev)
+{
+    uint32_t prev_d;
+    unsigned char txbuf[16 * 1024];
+    unsigned int txlen;
+    uint32_t app[6];
+
+    if (!stream_running(s) || stream_idle(s)) {
+        return;
+    }
+
+    while (1) {
+        stream_desc_load(s, s->regs[R_CURDESC]);
+
+        if (s->desc.status & SDESC_STATUS_COMPLETE) {
+            s->regs[R_DMASR] |= DMASR_IDLE;
+            break;
+        }
+
+        if (stream_desc_sof(&s->desc)) {
+            s->pos = 0;
+            memcpy(app, s->desc.app, sizeof app);
+        }
+
+        txlen = s->desc.control & SDESC_CTRL_LEN_MASK;
+        if ((txlen + s->pos) > sizeof txbuf) {
+            hw_error("%s: too small internal txbuf! %d\n", __func__,
+                     txlen + s->pos);
+        }
+
+        cpu_physical_memory_read(s->desc.buffer_address,
+                                 txbuf + s->pos, txlen);
+        s->pos += txlen;
+
+        if (stream_desc_eof(&s->desc)) {
+            stream_push(tx_dev, txbuf, s->pos, app);
+            s->pos = 0;
+            stream_complete(s);
+        }
+
+        /* Update the descriptor.  */
+        s->desc.status = txlen | SDESC_STATUS_COMPLETE;
+        stream_desc_store(s, s->regs[R_CURDESC]);
+
+        /* Advance.  */
+        prev_d = s->regs[R_CURDESC];
+        s->regs[R_CURDESC] = s->desc.nxtdesc;
+        if (prev_d == s->regs[R_TAILDESC]) {
+            s->regs[R_DMASR] |= DMASR_IDLE;
+            break;
+        }
+    }
+}
+
+static void stream_process_s2mem(struct Stream *s,
+                                 unsigned char *buf, size_t len, uint32_t *app)
+{
+    uint32_t prev_d;
+    unsigned int rxlen;
+    int pos = 0;
+    int sof = 1;
+
+    if (!stream_running(s) || stream_idle(s)) {
+        return;
+    }
+
+    while (len) {
+        stream_desc_load(s, s->regs[R_CURDESC]);
+
+        if (s->desc.status & SDESC_STATUS_COMPLETE) {
+            s->regs[R_DMASR] |= DMASR_IDLE;
+            break;
+        }
+
+        rxlen = s->desc.control & SDESC_CTRL_LEN_MASK;
+        if (rxlen > len) {
+            /* It fits.  */
+            rxlen = len;
+        }
+
+        cpu_physical_memory_write(s->desc.buffer_address, buf + pos, rxlen);
+        len -= rxlen;
+        pos += rxlen;
+
+        /* Update the descriptor.  */
+        if (!len) {
+            int i;
+
+            stream_complete(s);
+            for (i = 0; i < 5; i++) {
+                s->desc.app[i] = app[i];
+            }
+            s->desc.status |= SDESC_STATUS_EOF;
+        }
+
+        s->desc.status |= sof << SDESC_STATUS_SOF_BIT;
+        s->desc.status |= SDESC_STATUS_COMPLETE;
+        stream_desc_store(s, s->regs[R_CURDESC]);
+        sof = 0;
+
+        /* Advance.  */
+        prev_d = s->regs[R_CURDESC];
+        s->regs[R_CURDESC] = s->desc.nxtdesc;
+        if (prev_d == s->regs[R_TAILDESC]) {
+            s->regs[R_DMASR] |= DMASR_IDLE;
+            break;
+        }
+    }
+}
+
+static void
+axidma_push(StreamSlave *obj, unsigned char *buf, size_t len, uint32_t *app)
+{
+    struct XilinxAXIDMA *d = FROM_SYSBUS(typeof(*d), SYS_BUS_DEVICE(obj));
+    struct Stream *s = &d->streams[1];
+
+    if (!app) {
+        hw_error("No stream app data!\n");
+    }
+    stream_process_s2mem(s, buf, len, app);
+    stream_update_irq(s);
+}
+
+static uint64_t axidma_read(void *opaque, hwaddr addr,
+                            unsigned size)
+{
+    struct XilinxAXIDMA *d = opaque;
+    struct Stream *s;
+    uint32_t r = 0;
+    int sid;
+
+    sid = streamid_from_addr(addr);
+    s = &d->streams[sid];
+
+    addr = addr % 0x30;
+    addr >>= 2;
+    switch (addr) {
+        case R_DMACR:
+            /* Simulate one cycles reset delay.  */
+            s->regs[addr] &= ~DMACR_RESET;
+            r = s->regs[addr];
+            break;
+        case R_DMASR:
+            s->regs[addr] &= 0xffff;
+            s->regs[addr] |= (s->complete_cnt & 0xff) << 16;
+            s->regs[addr] |= (ptimer_get_count(s->ptimer) & 0xff) << 24;
+            r = s->regs[addr];
+            break;
+        default:
+            r = s->regs[addr];
+            D(qemu_log("%s ch=%d addr=" TARGET_FMT_plx " v=%x\n",
+                           __func__, sid, addr * 4, r));
+            break;
+    }
+    return r;
+
+}
+
+static void axidma_write(void *opaque, hwaddr addr,
+                         uint64_t value, unsigned size)
+{
+    struct XilinxAXIDMA *d = opaque;
+    struct Stream *s;
+    int sid;
+
+    sid = streamid_from_addr(addr);
+    s = &d->streams[sid];
+
+    addr = addr % 0x30;
+    addr >>= 2;
+    switch (addr) {
+        case R_DMACR:
+            /* Tailptr mode is always on.  */
+            value |= DMACR_TAILPTR_MODE;
+            /* Remember our previous reset state.  */
+            value |= (s->regs[addr] & DMACR_RESET);
+            s->regs[addr] = value;
+
+            if (value & DMACR_RESET) {
+                stream_reset(s);
+            }
+
+            if ((value & 1) && !stream_resetting(s)) {
+                /* Start processing.  */
+                s->regs[R_DMASR] &= ~(DMASR_HALTED | DMASR_IDLE);
+            }
+            stream_reload_complete_cnt(s);
+            break;
+
+        case R_DMASR:
+            /* Mask away write to clear irq lines.  */
+            value &= ~(value & DMASR_IRQ_MASK);
+            s->regs[addr] = value;
+            break;
+
+        case R_TAILDESC:
+            s->regs[addr] = value;
+            s->regs[R_DMASR] &= ~DMASR_IDLE; /* Not idle.  */
+            if (!sid) {
+                stream_process_mem2s(s, d->tx_dev);
+            }
+            break;
+        default:
+            D(qemu_log("%s: ch=%d addr=" TARGET_FMT_plx " v=%x\n",
+                  __func__, sid, addr * 4, (unsigned)value));
+            s->regs[addr] = value;
+            break;
+    }
+    stream_update_irq(s);
+}
+
+static const MemoryRegionOps axidma_ops = {
+    .read = axidma_read,
+    .write = axidma_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int xilinx_axidma_init(SysBusDevice *dev)
+{
+    struct XilinxAXIDMA *s = FROM_SYSBUS(typeof(*s), dev);
+    int i;
+
+    sysbus_init_irq(dev, &s->streams[0].irq);
+    sysbus_init_irq(dev, &s->streams[1].irq);
+
+    memory_region_init_io(&s->iomem, &axidma_ops, s,
+                          "xlnx.axi-dma", R_MAX * 4 * 2);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    for (i = 0; i < 2; i++) {
+        stream_reset(&s->streams[i]);
+        s->streams[i].nr = i;
+        s->streams[i].bh = qemu_bh_new(timer_hit, &s->streams[i]);
+        s->streams[i].ptimer = ptimer_init(s->streams[i].bh);
+        ptimer_set_freq(s->streams[i].ptimer, s->freqhz);
+    }
+    return 0;
+}
+
+static void xilinx_axidma_initfn(Object *obj)
+{
+    struct XilinxAXIDMA *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj));
+
+    object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
+                             (Object **) &s->tx_dev, NULL);
+}
+
+static Property axidma_properties[] = {
+    DEFINE_PROP_UINT32("freqhz", struct XilinxAXIDMA, freqhz, 50000000),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void axidma_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
+
+    k->init = xilinx_axidma_init;
+    dc->props = axidma_properties;
+    ssc->push = axidma_push;
+}
+
+static const TypeInfo axidma_info = {
+    .name          = "xlnx.axi-dma",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(struct XilinxAXIDMA),
+    .class_init    = axidma_class_init,
+    .instance_init = xilinx_axidma_initfn,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_STREAM_SLAVE },
+        { }
+    }
+};
+
+static void xilinx_axidma_register_types(void)
+{
+    type_register_static(&axidma_info);
+}
+
+type_init(xilinx_axidma_register_types)
diff --git a/hw/dp8393x.c b/hw/dp8393x.c
deleted file mode 100644 (file)
index 2289f08..0000000
+++ /dev/null
@@ -1,914 +0,0 @@
-/*
- * QEMU NS SONIC DP8393x netcard
- *
- * Copyright (c) 2008-2009 Herve Poussineau
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/hw.h"
-#include "qemu/timer.h"
-#include "net/net.h"
-#include "hw/mips/mips.h"
-
-//#define DEBUG_SONIC
-
-/* Calculate CRCs properly on Rx packets */
-#define SONIC_CALCULATE_RXCRC
-
-#if defined(SONIC_CALCULATE_RXCRC)
-/* For crc32 */
-#include <zlib.h>
-#endif
-
-#ifdef DEBUG_SONIC
-#define DPRINTF(fmt, ...) \
-do { printf("sonic: " fmt , ##  __VA_ARGS__); } while (0)
-static const char* reg_names[] = {
-    "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA",
-    "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0",
-    "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP",
-    "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA",
-    "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC",
-    "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT",
-    "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37",
-    "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" };
-#else
-#define DPRINTF(fmt, ...) do {} while (0)
-#endif
-
-#define SONIC_ERROR(fmt, ...) \
-do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
-
-#define SONIC_CR     0x00
-#define SONIC_DCR    0x01
-#define SONIC_RCR    0x02
-#define SONIC_TCR    0x03
-#define SONIC_IMR    0x04
-#define SONIC_ISR    0x05
-#define SONIC_UTDA   0x06
-#define SONIC_CTDA   0x07
-#define SONIC_TPS    0x08
-#define SONIC_TFC    0x09
-#define SONIC_TSA0   0x0a
-#define SONIC_TSA1   0x0b
-#define SONIC_TFS    0x0c
-#define SONIC_URDA   0x0d
-#define SONIC_CRDA   0x0e
-#define SONIC_CRBA0  0x0f
-#define SONIC_CRBA1  0x10
-#define SONIC_RBWC0  0x11
-#define SONIC_RBWC1  0x12
-#define SONIC_EOBC   0x13
-#define SONIC_URRA   0x14
-#define SONIC_RSA    0x15
-#define SONIC_REA    0x16
-#define SONIC_RRP    0x17
-#define SONIC_RWP    0x18
-#define SONIC_TRBA0  0x19
-#define SONIC_TRBA1  0x1a
-#define SONIC_LLFA   0x1f
-#define SONIC_TTDA   0x20
-#define SONIC_CEP    0x21
-#define SONIC_CAP2   0x22
-#define SONIC_CAP1   0x23
-#define SONIC_CAP0   0x24
-#define SONIC_CE     0x25
-#define SONIC_CDP    0x26
-#define SONIC_CDC    0x27
-#define SONIC_SR     0x28
-#define SONIC_WT0    0x29
-#define SONIC_WT1    0x2a
-#define SONIC_RSC    0x2b
-#define SONIC_CRCT   0x2c
-#define SONIC_FAET   0x2d
-#define SONIC_MPT    0x2e
-#define SONIC_MDT    0x2f
-#define SONIC_DCR2   0x3f
-
-#define SONIC_CR_HTX     0x0001
-#define SONIC_CR_TXP     0x0002
-#define SONIC_CR_RXDIS   0x0004
-#define SONIC_CR_RXEN    0x0008
-#define SONIC_CR_STP     0x0010
-#define SONIC_CR_ST      0x0020
-#define SONIC_CR_RST     0x0080
-#define SONIC_CR_RRRA    0x0100
-#define SONIC_CR_LCAM    0x0200
-#define SONIC_CR_MASK    0x03bf
-
-#define SONIC_DCR_DW     0x0020
-#define SONIC_DCR_LBR    0x2000
-#define SONIC_DCR_EXBUS  0x8000
-
-#define SONIC_RCR_PRX    0x0001
-#define SONIC_RCR_LBK    0x0002
-#define SONIC_RCR_FAER   0x0004
-#define SONIC_RCR_CRCR   0x0008
-#define SONIC_RCR_CRS    0x0020
-#define SONIC_RCR_LPKT   0x0040
-#define SONIC_RCR_BC     0x0080
-#define SONIC_RCR_MC     0x0100
-#define SONIC_RCR_LB0    0x0200
-#define SONIC_RCR_LB1    0x0400
-#define SONIC_RCR_AMC    0x0800
-#define SONIC_RCR_PRO    0x1000
-#define SONIC_RCR_BRD    0x2000
-#define SONIC_RCR_RNT    0x4000
-
-#define SONIC_TCR_PTX    0x0001
-#define SONIC_TCR_BCM    0x0002
-#define SONIC_TCR_FU     0x0004
-#define SONIC_TCR_EXC    0x0040
-#define SONIC_TCR_CRSL   0x0080
-#define SONIC_TCR_NCRS   0x0100
-#define SONIC_TCR_EXD    0x0400
-#define SONIC_TCR_CRCI   0x2000
-#define SONIC_TCR_PINT   0x8000
-
-#define SONIC_ISR_RBE    0x0020
-#define SONIC_ISR_RDE    0x0040
-#define SONIC_ISR_TC     0x0080
-#define SONIC_ISR_TXDN   0x0200
-#define SONIC_ISR_PKTRX  0x0400
-#define SONIC_ISR_PINT   0x0800
-#define SONIC_ISR_LCD    0x1000
-
-typedef struct dp8393xState {
-    /* Hardware */
-    int it_shift;
-    qemu_irq irq;
-#ifdef DEBUG_SONIC
-    int irq_level;
-#endif
-    QEMUTimer *watchdog;
-    int64_t wt_last_update;
-    NICConf conf;
-    NICState *nic;
-    MemoryRegion *address_space;
-    MemoryRegion mmio;
-
-    /* Registers */
-    uint8_t cam[16][6];
-    uint16_t regs[0x40];
-
-    /* Temporaries */
-    uint8_t tx_buffer[0x10000];
-    int loopback_packet;
-
-    /* Memory access */
-    void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write);
-    void* mem_opaque;
-} dp8393xState;
-
-static void dp8393x_update_irq(dp8393xState *s)
-{
-    int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
-
-#ifdef DEBUG_SONIC
-    if (level != s->irq_level) {
-        s->irq_level = level;
-        if (level) {
-            DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]);
-        } else {
-            DPRINTF("lower irq\n");
-        }
-    }
-#endif
-
-    qemu_set_irq(s->irq, level);
-}
-
-static void do_load_cam(dp8393xState *s)
-{
-    uint16_t data[8];
-    int width, size;
-    uint16_t index = 0;
-
-    width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
-    size = sizeof(uint16_t) * 4 * width;
-
-    while (s->regs[SONIC_CDC] & 0x1f) {
-        /* Fill current entry */
-        s->memory_rw(s->mem_opaque,
-            (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
-            (uint8_t *)data, size, 0);
-        s->cam[index][0] = data[1 * width] & 0xff;
-        s->cam[index][1] = data[1 * width] >> 8;
-        s->cam[index][2] = data[2 * width] & 0xff;
-        s->cam[index][3] = data[2 * width] >> 8;
-        s->cam[index][4] = data[3 * width] & 0xff;
-        s->cam[index][5] = data[3 * width] >> 8;
-        DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index,
-            s->cam[index][0], s->cam[index][1], s->cam[index][2],
-            s->cam[index][3], s->cam[index][4], s->cam[index][5]);
-        /* Move to next entry */
-        s->regs[SONIC_CDC]--;
-        s->regs[SONIC_CDP] += size;
-        index++;
-    }
-
-    /* Read CAM enable */
-    s->memory_rw(s->mem_opaque,
-        (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
-        (uint8_t *)data, size, 0);
-    s->regs[SONIC_CE] = data[0 * width];
-    DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
-
-    /* Done */
-    s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
-    s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
-    dp8393x_update_irq(s);
-}
-
-static void do_read_rra(dp8393xState *s)
-{
-    uint16_t data[8];
-    int width, size;
-
-    /* Read memory */
-    width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
-    size = sizeof(uint16_t) * 4 * width;
-    s->memory_rw(s->mem_opaque,
-        (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP],
-        (uint8_t *)data, size, 0);
-
-    /* Update SONIC registers */
-    s->regs[SONIC_CRBA0] = data[0 * width];
-    s->regs[SONIC_CRBA1] = data[1 * width];
-    s->regs[SONIC_RBWC0] = data[2 * width];
-    s->regs[SONIC_RBWC1] = data[3 * width];
-    DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n",
-        s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
-        s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
-
-    /* Go to next entry */
-    s->regs[SONIC_RRP] += size;
-
-    /* Handle wrap */
-    if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) {
-        s->regs[SONIC_RRP] = s->regs[SONIC_RSA];
-    }
-
-    /* Check resource exhaustion */
-    if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP])
-    {
-        s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
-        dp8393x_update_irq(s);
-    }
-
-    /* Done */
-    s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
-}
-
-static void do_software_reset(dp8393xState *s)
-{
-    qemu_del_timer(s->watchdog);
-
-    s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX);
-    s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
-}
-
-static void set_next_tick(dp8393xState *s)
-{
-    uint32_t ticks;
-    int64_t delay;
-
-    if (s->regs[SONIC_CR] & SONIC_CR_STP) {
-        qemu_del_timer(s->watchdog);
-        return;
-    }
-
-    ticks = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
-    s->wt_last_update = qemu_get_clock_ns(vm_clock);
-    delay = get_ticks_per_sec() * ticks / 5000000;
-    qemu_mod_timer(s->watchdog, s->wt_last_update + delay);
-}
-
-static void update_wt_regs(dp8393xState *s)
-{
-    int64_t elapsed;
-    uint32_t val;
-
-    if (s->regs[SONIC_CR] & SONIC_CR_STP) {
-        qemu_del_timer(s->watchdog);
-        return;
-    }
-
-    elapsed = s->wt_last_update - qemu_get_clock_ns(vm_clock);
-    val = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
-    val -= elapsed / 5000000;
-    s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
-    s->regs[SONIC_WT0] = (val >> 0)  & 0xffff;
-    set_next_tick(s);
-
-}
-
-static void do_start_timer(dp8393xState *s)
-{
-    s->regs[SONIC_CR] &= ~SONIC_CR_STP;
-    set_next_tick(s);
-}
-
-static void do_stop_timer(dp8393xState *s)
-{
-    s->regs[SONIC_CR] &= ~SONIC_CR_ST;
-    update_wt_regs(s);
-}
-
-static void do_receiver_enable(dp8393xState *s)
-{
-    s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
-}
-
-static void do_receiver_disable(dp8393xState *s)
-{
-    s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
-}
-
-static void do_transmit_packets(dp8393xState *s)
-{
-    NetClientState *nc = qemu_get_queue(s->nic);
-    uint16_t data[12];
-    int width, size;
-    int tx_len, len;
-    uint16_t i;
-
-    width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
-
-    while (1) {
-        /* Read memory */
-        DPRINTF("Transmit packet at %08x\n",
-                (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_CTDA]);
-        size = sizeof(uint16_t) * 6 * width;
-        s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
-        s->memory_rw(s->mem_opaque,
-            ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * width,
-            (uint8_t *)data, size, 0);
-        tx_len = 0;
-
-        /* Update registers */
-        s->regs[SONIC_TCR] = data[0 * width] & 0xf000;
-        s->regs[SONIC_TPS] = data[1 * width];
-        s->regs[SONIC_TFC] = data[2 * width];
-        s->regs[SONIC_TSA0] = data[3 * width];
-        s->regs[SONIC_TSA1] = data[4 * width];
-        s->regs[SONIC_TFS] = data[5 * width];
-
-        /* Handle programmable interrupt */
-        if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
-            s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
-        } else {
-            s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
-        }
-
-        for (i = 0; i < s->regs[SONIC_TFC]; ) {
-            /* Append fragment */
-            len = s->regs[SONIC_TFS];
-            if (tx_len + len > sizeof(s->tx_buffer)) {
-                len = sizeof(s->tx_buffer) - tx_len;
-            }
-            s->memory_rw(s->mem_opaque,
-                (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0],
-                &s->tx_buffer[tx_len], len, 0);
-            tx_len += len;
-
-            i++;
-            if (i != s->regs[SONIC_TFC]) {
-                /* Read next fragment details */
-                size = sizeof(uint16_t) * 3 * width;
-                s->memory_rw(s->mem_opaque,
-                    ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * i) * width,
-                    (uint8_t *)data, size, 0);
-                s->regs[SONIC_TSA0] = data[0 * width];
-                s->regs[SONIC_TSA1] = data[1 * width];
-                s->regs[SONIC_TFS] = data[2 * width];
-            }
-        }
-
-        /* Handle Ethernet checksum */
-        if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
-            /* Don't append FCS there, to look like slirp packets
-             * which don't have one */
-        } else {
-            /* Remove existing FCS */
-            tx_len -= 4;
-        }
-
-        if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
-            /* Loopback */
-            s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
-            if (nc->info->can_receive(nc)) {
-                s->loopback_packet = 1;
-                nc->info->receive(nc, s->tx_buffer, tx_len);
-            }
-        } else {
-            /* Transmit packet */
-            qemu_send_packet(nc, s->tx_buffer, tx_len);
-        }
-        s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
-
-        /* Write status */
-        data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */
-        size = sizeof(uint16_t) * width;
-        s->memory_rw(s->mem_opaque,
-            (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA],
-            (uint8_t *)data, size, 1);
-
-        if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
-            /* Read footer of packet */
-            size = sizeof(uint16_t) * width;
-            s->memory_rw(s->mem_opaque,
-                ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * s->regs[SONIC_TFC]) * width,
-                (uint8_t *)data, size, 0);
-            s->regs[SONIC_CTDA] = data[0 * width] & ~0x1;
-            if (data[0 * width] & 0x1) {
-                /* EOL detected */
-                break;
-            }
-        }
-    }
-
-    /* Done */
-    s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
-    s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
-    dp8393x_update_irq(s);
-}
-
-static void do_halt_transmission(dp8393xState *s)
-{
-    /* Nothing to do */
-}
-
-static void do_command(dp8393xState *s, uint16_t command)
-{
-    if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
-        s->regs[SONIC_CR] &= ~SONIC_CR_RST;
-        return;
-    }
-
-    s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
-
-    if (command & SONIC_CR_HTX)
-        do_halt_transmission(s);
-    if (command & SONIC_CR_TXP)
-        do_transmit_packets(s);
-    if (command & SONIC_CR_RXDIS)
-        do_receiver_disable(s);
-    if (command & SONIC_CR_RXEN)
-        do_receiver_enable(s);
-    if (command & SONIC_CR_STP)
-        do_stop_timer(s);
-    if (command & SONIC_CR_ST)
-        do_start_timer(s);
-    if (command & SONIC_CR_RST)
-        do_software_reset(s);
-    if (command & SONIC_CR_RRRA)
-        do_read_rra(s);
-    if (command & SONIC_CR_LCAM)
-        do_load_cam(s);
-}
-
-static uint16_t read_register(dp8393xState *s, int reg)
-{
-    uint16_t val = 0;
-
-    switch (reg) {
-        /* Update data before reading it */
-        case SONIC_WT0:
-        case SONIC_WT1:
-            update_wt_regs(s);
-            val = s->regs[reg];
-            break;
-        /* Accept read to some registers only when in reset mode */
-        case SONIC_CAP2:
-        case SONIC_CAP1:
-        case SONIC_CAP0:
-            if (s->regs[SONIC_CR] & SONIC_CR_RST) {
-                val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8;
-                val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)];
-            }
-            break;
-        /* All other registers have no special contrainst */
-        default:
-            val = s->regs[reg];
-    }
-
-    DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]);
-
-    return val;
-}
-
-static void write_register(dp8393xState *s, int reg, uint16_t val)
-{
-    DPRINTF("write 0x%04x to reg %s\n", val, reg_names[reg]);
-
-    switch (reg) {
-        /* Command register */
-        case SONIC_CR:
-            do_command(s, val);
-            break;
-        /* Prevent write to read-only registers */
-        case SONIC_CAP2:
-        case SONIC_CAP1:
-        case SONIC_CAP0:
-        case SONIC_SR:
-        case SONIC_MDT:
-            DPRINTF("writing to reg %d invalid\n", reg);
-            break;
-        /* Accept write to some registers only when in reset mode */
-        case SONIC_DCR:
-            if (s->regs[SONIC_CR] & SONIC_CR_RST) {
-                s->regs[reg] = val & 0xbfff;
-            } else {
-                DPRINTF("writing to DCR invalid\n");
-            }
-            break;
-        case SONIC_DCR2:
-            if (s->regs[SONIC_CR] & SONIC_CR_RST) {
-                s->regs[reg] = val & 0xf017;
-            } else {
-                DPRINTF("writing to DCR2 invalid\n");
-            }
-            break;
-        /* 12 lower bytes are Read Only */
-        case SONIC_TCR:
-            s->regs[reg] = val & 0xf000;
-            break;
-        /* 9 lower bytes are Read Only */
-        case SONIC_RCR:
-            s->regs[reg] = val & 0xffe0;
-            break;
-        /* Ignore most significant bit */
-        case SONIC_IMR:
-            s->regs[reg] = val & 0x7fff;
-            dp8393x_update_irq(s);
-            break;
-        /* Clear bits by writing 1 to them */
-        case SONIC_ISR:
-            val &= s->regs[reg];
-            s->regs[reg] &= ~val;
-            if (val & SONIC_ISR_RBE) {
-                do_read_rra(s);
-            }
-            dp8393x_update_irq(s);
-            break;
-        /* Ignore least significant bit */
-        case SONIC_RSA:
-        case SONIC_REA:
-        case SONIC_RRP:
-        case SONIC_RWP:
-            s->regs[reg] = val & 0xfffe;
-            break;
-        /* Invert written value for some registers */
-        case SONIC_CRCT:
-        case SONIC_FAET:
-        case SONIC_MPT:
-            s->regs[reg] = val ^ 0xffff;
-            break;
-        /* All other registers have no special contrainst */
-        default:
-            s->regs[reg] = val;
-    }
-
-    if (reg == SONIC_WT0 || reg == SONIC_WT1) {
-        set_next_tick(s);
-    }
-}
-
-static void dp8393x_watchdog(void *opaque)
-{
-    dp8393xState *s = opaque;
-
-    if (s->regs[SONIC_CR] & SONIC_CR_STP) {
-        return;
-    }
-
-    s->regs[SONIC_WT1] = 0xffff;
-    s->regs[SONIC_WT0] = 0xffff;
-    set_next_tick(s);
-
-    /* Signal underflow */
-    s->regs[SONIC_ISR] |= SONIC_ISR_TC;
-    dp8393x_update_irq(s);
-}
-
-static uint32_t dp8393x_readw(void *opaque, hwaddr addr)
-{
-    dp8393xState *s = opaque;
-    int reg;
-
-    if ((addr & ((1 << s->it_shift) - 1)) != 0) {
-        return 0;
-    }
-
-    reg = addr >> s->it_shift;
-    return read_register(s, reg);
-}
-
-static uint32_t dp8393x_readb(void *opaque, hwaddr addr)
-{
-    uint16_t v = dp8393x_readw(opaque, addr & ~0x1);
-    return (v >> (8 * (addr & 0x1))) & 0xff;
-}
-
-static uint32_t dp8393x_readl(void *opaque, hwaddr addr)
-{
-    uint32_t v;
-    v = dp8393x_readw(opaque, addr);
-    v |= dp8393x_readw(opaque, addr + 2) << 16;
-    return v;
-}
-
-static void dp8393x_writew(void *opaque, hwaddr addr, uint32_t val)
-{
-    dp8393xState *s = opaque;
-    int reg;
-
-    if ((addr & ((1 << s->it_shift) - 1)) != 0) {
-        return;
-    }
-
-    reg = addr >> s->it_shift;
-
-    write_register(s, reg, (uint16_t)val);
-}
-
-static void dp8393x_writeb(void *opaque, hwaddr addr, uint32_t val)
-{
-    uint16_t old_val = dp8393x_readw(opaque, addr & ~0x1);
-
-    switch (addr & 3) {
-    case 0:
-        val = val | (old_val & 0xff00);
-        break;
-    case 1:
-        val = (val << 8) | (old_val & 0x00ff);
-        break;
-    }
-    dp8393x_writew(opaque, addr & ~0x1, val);
-}
-
-static void dp8393x_writel(void *opaque, hwaddr addr, uint32_t val)
-{
-    dp8393x_writew(opaque, addr, val & 0xffff);
-    dp8393x_writew(opaque, addr + 2, (val >> 16) & 0xffff);
-}
-
-static const MemoryRegionOps dp8393x_ops = {
-    .old_mmio = {
-        .read = { dp8393x_readb, dp8393x_readw, dp8393x_readl, },
-        .write = { dp8393x_writeb, dp8393x_writew, dp8393x_writel, },
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int nic_can_receive(NetClientState *nc)
-{
-    dp8393xState *s = qemu_get_nic_opaque(nc);
-
-    if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN))
-        return 0;
-    if (s->regs[SONIC_ISR] & SONIC_ISR_RBE)
-        return 0;
-    return 1;
-}
-
-static int receive_filter(dp8393xState *s, const uint8_t * buf, int size)
-{
-    static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
-    int i;
-
-    /* Check for runt packet (remember that checksum is not there) */
-    if (size < 64 - 4) {
-        return (s->regs[SONIC_RCR] & SONIC_RCR_RNT) ? 0 : -1;
-    }
-
-    /* Check promiscuous mode */
-    if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
-        return 0;
-    }
-
-    /* Check multicast packets */
-    if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
-        return SONIC_RCR_MC;
-    }
-
-    /* Check broadcast */
-    if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) {
-        return SONIC_RCR_BC;
-    }
-
-    /* Check CAM */
-    for (i = 0; i < 16; i++) {
-        if (s->regs[SONIC_CE] & (1 << i)) {
-             /* Entry enabled */
-             if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) {
-                 return 0;
-             }
-        }
-    }
-
-    return -1;
-}
-
-static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
-{
-    dp8393xState *s = qemu_get_nic_opaque(nc);
-    uint16_t data[10];
-    int packet_type;
-    uint32_t available, address;
-    int width, rx_len = size;
-    uint32_t checksum;
-
-    width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
-
-    s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
-        SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
-
-    packet_type = receive_filter(s, buf, size);
-    if (packet_type < 0) {
-        DPRINTF("packet not for netcard\n");
-        return -1;
-    }
-
-    /* XXX: Check byte ordering */
-
-    /* Check for EOL */
-    if (s->regs[SONIC_LLFA] & 0x1) {
-        /* Are we still in resource exhaustion? */
-        size = sizeof(uint16_t) * 1 * width;
-        address = ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width;
-        s->memory_rw(s->mem_opaque, address, (uint8_t*)data, size, 0);
-        if (data[0 * width] & 0x1) {
-            /* Still EOL ; stop reception */
-            return -1;
-        } else {
-            s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
-        }
-    }
-
-    /* Save current position */
-    s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
-    s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
-
-    /* Calculate the ethernet checksum */
-#ifdef SONIC_CALCULATE_RXCRC
-    checksum = cpu_to_le32(crc32(0, buf, rx_len));
-#else
-    checksum = 0;
-#endif
-
-    /* Put packet into RBA */
-    DPRINTF("Receive packet at %08x\n", (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]);
-    address = (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
-    s->memory_rw(s->mem_opaque, address, (uint8_t*)buf, rx_len, 1);
-    address += rx_len;
-    s->memory_rw(s->mem_opaque, address, (uint8_t*)&checksum, 4, 1);
-    rx_len += 4;
-    s->regs[SONIC_CRBA1] = address >> 16;
-    s->regs[SONIC_CRBA0] = address & 0xffff;
-    available = (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
-    available -= rx_len / 2;
-    s->regs[SONIC_RBWC1] = available >> 16;
-    s->regs[SONIC_RBWC0] = available & 0xffff;
-
-    /* Update status */
-    if (((s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]) < s->regs[SONIC_EOBC]) {
-        s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
-    }
-    s->regs[SONIC_RCR] |= packet_type;
-    s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
-    if (s->loopback_packet) {
-        s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
-        s->loopback_packet = 0;
-    }
-
-    /* Write status to memory */
-    DPRINTF("Write status at %08x\n", (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]);
-    data[0 * width] = s->regs[SONIC_RCR]; /* status */
-    data[1 * width] = rx_len; /* byte count */
-    data[2 * width] = s->regs[SONIC_TRBA0]; /* pkt_ptr0 */
-    data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */
-    data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */
-    size = sizeof(uint16_t) * 5 * width;
-    s->memory_rw(s->mem_opaque, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA], (uint8_t *)data, size, 1);
-
-    /* Move to next descriptor */
-    size = sizeof(uint16_t) * width;
-    s->memory_rw(s->mem_opaque,
-        ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width,
-        (uint8_t *)data, size, 0);
-    s->regs[SONIC_LLFA] = data[0 * width];
-    if (s->regs[SONIC_LLFA] & 0x1) {
-        /* EOL detected */
-        s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
-    } else {
-        data[0 * width] = 0; /* in_use */
-        s->memory_rw(s->mem_opaque,
-            ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 6 * width,
-            (uint8_t *)data, size, 1);
-        s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
-        s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
-        s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff);
-
-        if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
-            /* Read next RRA */
-            do_read_rra(s);
-        }
-    }
-
-    /* Done */
-    dp8393x_update_irq(s);
-
-    return size;
-}
-
-static void nic_reset(void *opaque)
-{
-    dp8393xState *s = opaque;
-    qemu_del_timer(s->watchdog);
-
-    s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
-    s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
-    s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT);
-    s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX;
-    s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM;
-    s->regs[SONIC_IMR] = 0;
-    s->regs[SONIC_ISR] = 0;
-    s->regs[SONIC_DCR2] = 0;
-    s->regs[SONIC_EOBC] = 0x02F8;
-    s->regs[SONIC_RSC] = 0;
-    s->regs[SONIC_CE] = 0;
-    s->regs[SONIC_RSC] = 0;
-
-    /* Network cable is connected */
-    s->regs[SONIC_RCR] |= SONIC_RCR_CRS;
-
-    dp8393x_update_irq(s);
-}
-
-static void nic_cleanup(NetClientState *nc)
-{
-    dp8393xState *s = qemu_get_nic_opaque(nc);
-
-    memory_region_del_subregion(s->address_space, &s->mmio);
-    memory_region_destroy(&s->mmio);
-
-    qemu_del_timer(s->watchdog);
-    qemu_free_timer(s->watchdog);
-
-    g_free(s);
-}
-
-static NetClientInfo net_dp83932_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
-    .size = sizeof(NICState),
-    .can_receive = nic_can_receive,
-    .receive = nic_receive,
-    .cleanup = nic_cleanup,
-};
-
-void dp83932_init(NICInfo *nd, hwaddr base, int it_shift,
-                  MemoryRegion *address_space,
-                  qemu_irq irq, void* mem_opaque,
-                  void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write))
-{
-    dp8393xState *s;
-
-    qemu_check_nic_model(nd, "dp83932");
-
-    s = g_malloc0(sizeof(dp8393xState));
-
-    s->address_space = address_space;
-    s->mem_opaque = mem_opaque;
-    s->memory_rw = memory_rw;
-    s->it_shift = it_shift;
-    s->irq = irq;
-    s->watchdog = qemu_new_timer_ns(vm_clock, dp8393x_watchdog, s);
-    s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
-
-    s->conf.macaddr = nd->macaddr;
-    s->conf.peers.ncs[0] = nd->netdev;
-
-    s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, nd->model, nd->name, s);
-
-    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
-    qemu_register_reset(nic_reset, s);
-    nic_reset(s);
-
-    memory_region_init_io(&s->mmio, &dp8393x_ops, s,
-                          "dp8393x", 0x40 << it_shift);
-    memory_region_add_subregion(address_space, base, &s->mmio);
-}
diff --git a/hw/ds1225y.c b/hw/ds1225y.c
deleted file mode 100644 (file)
index 488f1d7..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * QEMU NVRAM emulation for DS1225Y chip
- *
- * Copyright (c) 2007-2008 Hervé Poussineau
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/sysbus.h"
-#include "trace.h"
-
-typedef struct {
-    DeviceState qdev;
-    MemoryRegion iomem;
-    uint32_t chip_size;
-    char *filename;
-    FILE *file;
-    uint8_t *contents;
-} NvRamState;
-
-static uint64_t nvram_read(void *opaque, hwaddr addr, unsigned size)
-{
-    NvRamState *s = opaque;
-    uint32_t val;
-
-    val = s->contents[addr];
-    trace_nvram_read(addr, val);
-    return val;
-}
-
-static void nvram_write(void *opaque, hwaddr addr, uint64_t val,
-                        unsigned size)
-{
-    NvRamState *s = opaque;
-
-    val &= 0xff;
-    trace_nvram_write(addr, s->contents[addr], val);
-
-    s->contents[addr] = val;
-    if (s->file) {
-        fseek(s->file, addr, SEEK_SET);
-        fputc(val, s->file);
-        fflush(s->file);
-    }
-}
-
-static const MemoryRegionOps nvram_ops = {
-    .read = nvram_read,
-    .write = nvram_write,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static int nvram_post_load(void *opaque, int version_id)
-{
-    NvRamState *s = opaque;
-
-    /* Close file, as filename may has changed in load/store process */
-    if (s->file) {
-        fclose(s->file);
-    }
-
-    /* Write back nvram contents */
-    s->file = fopen(s->filename, "wb");
-    if (s->file) {
-        /* Write back contents, as 'wb' mode cleaned the file */
-        if (fwrite(s->contents, s->chip_size, 1, s->file) != 1) {
-            printf("nvram_post_load: short write\n");
-        }
-        fflush(s->file);
-    }
-
-    return 0;
-}
-
-static const VMStateDescription vmstate_nvram = {
-    .name = "nvram",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .post_load = nvram_post_load,
-    .fields = (VMStateField[]) {
-        VMSTATE_VARRAY_UINT32(contents, NvRamState, chip_size, 0,
-                              vmstate_info_uint8, uint8_t),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-typedef struct {
-    SysBusDevice busdev;
-    NvRamState nvram;
-} SysBusNvRamState;
-
-static int nvram_sysbus_initfn(SysBusDevice *dev)
-{
-    NvRamState *s = &FROM_SYSBUS(SysBusNvRamState, dev)->nvram;
-    FILE *file;
-
-    s->contents = g_malloc0(s->chip_size);
-
-    memory_region_init_io(&s->iomem, &nvram_ops, s, "nvram", s->chip_size);
-    sysbus_init_mmio(dev, &s->iomem);
-
-    /* Read current file */
-    file = fopen(s->filename, "rb");
-    if (file) {
-        /* Read nvram contents */
-        if (fread(s->contents, s->chip_size, 1, file) != 1) {
-            printf("nvram_sysbus_initfn: short read\n");
-        }
-        fclose(file);
-    }
-    nvram_post_load(s, 0);
-
-    return 0;
-}
-
-static Property nvram_sysbus_properties[] = {
-    DEFINE_PROP_UINT32("size", SysBusNvRamState, nvram.chip_size, 0x2000),
-    DEFINE_PROP_STRING("filename", SysBusNvRamState, nvram.filename),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void nvram_sysbus_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = nvram_sysbus_initfn;
-    dc->vmsd = &vmstate_nvram;
-    dc->props = nvram_sysbus_properties;
-}
-
-static const TypeInfo nvram_sysbus_info = {
-    .name          = "ds1225y",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(SysBusNvRamState),
-    .class_init    = nvram_sysbus_class_init,
-};
-
-static void nvram_register_types(void)
-{
-    type_register_static(&nvram_sysbus_info);
-}
-
-type_init(nvram_register_types)
diff --git a/hw/ds1338.c b/hw/ds1338.c
deleted file mode 100644 (file)
index 8987cdc..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * MAXIM DS1338 I2C RTC+NVRAM
- *
- * Copyright (c) 2009 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the GNU GPL v2.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "hw/i2c/i2c.h"
-
-/* Size of NVRAM including both the user-accessible area and the
- * secondary register area.
- */
-#define NVRAM_SIZE 64
-
-/* Flags definitions */
-#define SECONDS_CH 0x80
-#define HOURS_12   0x40
-#define HOURS_PM   0x20
-#define CTRL_OSF   0x20
-
-typedef struct {
-    I2CSlave i2c;
-    int64_t offset;
-    uint8_t wday_offset;
-    uint8_t nvram[NVRAM_SIZE];
-    int32_t ptr;
-    bool addr_byte;
-} DS1338State;
-
-static const VMStateDescription vmstate_ds1338 = {
-    .name = "ds1338",
-    .version_id = 2,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_I2C_SLAVE(i2c, DS1338State),
-        VMSTATE_INT64(offset, DS1338State),
-        VMSTATE_UINT8_V(wday_offset, DS1338State, 2),
-        VMSTATE_UINT8_ARRAY(nvram, DS1338State, NVRAM_SIZE),
-        VMSTATE_INT32(ptr, DS1338State),
-        VMSTATE_BOOL(addr_byte, DS1338State),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void capture_current_time(DS1338State *s)
-{
-    /* Capture the current time into the secondary registers
-     * which will be actually read by the data transfer operation.
-     */
-    struct tm now;
-    qemu_get_timedate(&now, s->offset);
-    s->nvram[0] = to_bcd(now.tm_sec);
-    s->nvram[1] = to_bcd(now.tm_min);
-    if (s->nvram[2] & HOURS_12) {
-        int tmp = now.tm_hour;
-        if (tmp % 12 == 0) {
-            tmp += 12;
-        }
-        if (tmp <= 12) {
-            s->nvram[2] = HOURS_12 | to_bcd(tmp);
-        } else {
-            s->nvram[2] = HOURS_12 | HOURS_PM | to_bcd(tmp - 12);
-        }
-    } else {
-        s->nvram[2] = to_bcd(now.tm_hour);
-    }
-    s->nvram[3] = (now.tm_wday + s->wday_offset) % 7 + 1;
-    s->nvram[4] = to_bcd(now.tm_mday);
-    s->nvram[5] = to_bcd(now.tm_mon + 1);
-    s->nvram[6] = to_bcd(now.tm_year - 100);
-}
-
-static void inc_regptr(DS1338State *s)
-{
-    /* The register pointer wraps around after 0x3F; wraparound
-     * causes the current time/date to be retransferred into
-     * the secondary registers.
-     */
-    s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1);
-    if (!s->ptr) {
-        capture_current_time(s);
-    }
-}
-
-static void ds1338_event(I2CSlave *i2c, enum i2c_event event)
-{
-    DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
-
-    switch (event) {
-    case I2C_START_RECV:
-        /* In h/w, capture happens on any START condition, not just a
-         * START_RECV, but there is no need to actually capture on
-         * START_SEND, because the guest can't get at that data
-         * without going through a START_RECV which would overwrite it.
-         */
-        capture_current_time(s);
-        break;
-    case I2C_START_SEND:
-        s->addr_byte = true;
-        break;
-    default:
-        break;
-    }
-}
-
-static int ds1338_recv(I2CSlave *i2c)
-{
-    DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
-    uint8_t res;
-
-    res  = s->nvram[s->ptr];
-    inc_regptr(s);
-    return res;
-}
-
-static int ds1338_send(I2CSlave *i2c, uint8_t data)
-{
-    DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
-    if (s->addr_byte) {
-        s->ptr = data & (NVRAM_SIZE - 1);
-        s->addr_byte = false;
-        return 0;
-    }
-    if (s->ptr < 7) {
-        /* Time register. */
-        struct tm now;
-        qemu_get_timedate(&now, s->offset);
-        switch(s->ptr) {
-        case 0:
-            /* TODO: Implement CH (stop) bit.  */
-            now.tm_sec = from_bcd(data & 0x7f);
-            break;
-        case 1:
-            now.tm_min = from_bcd(data & 0x7f);
-            break;
-        case 2:
-            if (data & HOURS_12) {
-                int tmp = from_bcd(data & (HOURS_PM - 1));
-                if (data & HOURS_PM) {
-                    tmp += 12;
-                }
-                if (tmp % 12 == 0) {
-                    tmp -= 12;
-                }
-                now.tm_hour = tmp;
-            } else {
-                now.tm_hour = from_bcd(data & (HOURS_12 - 1));
-            }
-            break;
-        case 3:
-            {
-                /* The day field is supposed to contain a value in
-                   the range 1-7. Otherwise behavior is undefined.
-                 */
-                int user_wday = (data & 7) - 1;
-                s->wday_offset = (user_wday - now.tm_wday + 7) % 7;
-            }
-            break;
-        case 4:
-            now.tm_mday = from_bcd(data & 0x3f);
-            break;
-        case 5:
-            now.tm_mon = from_bcd(data & 0x1f) - 1;
-            break;
-        case 6:
-            now.tm_year = from_bcd(data) + 100;
-            break;
-        }
-        s->offset = qemu_timedate_diff(&now);
-    } else if (s->ptr == 7) {
-        /* Control register. */
-
-        /* Ensure bits 2, 3 and 6 will read back as zero. */
-        data &= 0xB3;
-
-        /* Attempting to write the OSF flag to logic 1 leaves the
-           value unchanged. */
-        data = (data & ~CTRL_OSF) | (data & s->nvram[s->ptr] & CTRL_OSF);
-
-        s->nvram[s->ptr] = data;
-    } else {
-        s->nvram[s->ptr] = data;
-    }
-    inc_regptr(s);
-    return 0;
-}
-
-static int ds1338_init(I2CSlave *i2c)
-{
-    return 0;
-}
-
-static void ds1338_reset(DeviceState *dev)
-{
-    DS1338State *s = FROM_I2C_SLAVE(DS1338State, I2C_SLAVE(dev));
-
-    /* The clock is running and synchronized with the host */
-    s->offset = 0;
-    s->wday_offset = 0;
-    memset(s->nvram, 0, NVRAM_SIZE);
-    s->ptr = 0;
-    s->addr_byte = false;
-}
-
-static void ds1338_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
-
-    k->init = ds1338_init;
-    k->event = ds1338_event;
-    k->recv = ds1338_recv;
-    k->send = ds1338_send;
-    dc->reset = ds1338_reset;
-    dc->vmsd = &vmstate_ds1338;
-}
-
-static const TypeInfo ds1338_info = {
-    .name          = "ds1338",
-    .parent        = TYPE_I2C_SLAVE,
-    .instance_size = sizeof(DS1338State),
-    .class_init    = ds1338_class_init,
-};
-
-static void ds1338_register_types(void)
-{
-    type_register_static(&ds1338_info);
-}
-
-type_init(ds1338_register_types)
diff --git a/hw/e1000.c b/hw/e1000.c
deleted file mode 100644 (file)
index 3f18041..0000000
+++ /dev/null
@@ -1,1404 +0,0 @@
-/*
- * QEMU e1000 emulation
- *
- * Software developer's manual:
- * http://download.intel.com/design/network/manuals/8254x_GBe_SDM.pdf
- *
- * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc.
- * Copyright (c) 2008 Qumranet
- * Based on work done by:
- * Copyright (c) 2007 Dan Aloni
- * Copyright (c) 2004 Antony T Curtis
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "hw/hw.h"
-#include "hw/pci/pci.h"
-#include "net/net.h"
-#include "net/checksum.h"
-#include "hw/loader.h"
-#include "sysemu/sysemu.h"
-#include "sysemu/dma.h"
-
-#include "hw/e1000_hw.h"
-
-#define E1000_DEBUG
-
-#ifdef E1000_DEBUG
-enum {
-    DEBUG_GENERAL,     DEBUG_IO,       DEBUG_MMIO,     DEBUG_INTERRUPT,
-    DEBUG_RX,          DEBUG_TX,       DEBUG_MDIC,     DEBUG_EEPROM,
-    DEBUG_UNKNOWN,     DEBUG_TXSUM,    DEBUG_TXERR,    DEBUG_RXERR,
-    DEBUG_RXFILTER,     DEBUG_PHY,      DEBUG_NOTYET,
-};
-#define DBGBIT(x)      (1<<DEBUG_##x)
-static int debugflags = DBGBIT(TXERR) | DBGBIT(GENERAL);
-
-#define        DBGOUT(what, fmt, ...) do { \
-    if (debugflags & DBGBIT(what)) \
-        fprintf(stderr, "e1000: " fmt, ## __VA_ARGS__); \
-    } while (0)
-#else
-#define        DBGOUT(what, fmt, ...) do {} while (0)
-#endif
-
-#define IOPORT_SIZE       0x40
-#define PNPMMIO_SIZE      0x20000
-#define MIN_BUF_SIZE      60 /* Min. octets in an ethernet frame sans FCS */
-
-/* this is the size past which hardware will drop packets when setting LPE=0 */
-#define MAXIMUM_ETHERNET_VLAN_SIZE 1522
-/* this is the size past which hardware will drop packets when setting LPE=1 */
-#define MAXIMUM_ETHERNET_LPE_SIZE 16384
-
-/*
- * HW models:
- *  E1000_DEV_ID_82540EM works with Windows and Linux
- *  E1000_DEV_ID_82573L OK with windoze and Linux 2.6.22,
- *     appears to perform better than 82540EM, but breaks with Linux 2.6.18
- *  E1000_DEV_ID_82544GC_COPPER appears to work; not well tested
- *  Others never tested
- */
-enum { E1000_DEVID = E1000_DEV_ID_82540EM };
-
-/*
- * May need to specify additional MAC-to-PHY entries --
- * Intel's Windows driver refuses to initialize unless they match
- */
-enum {
-    PHY_ID2_INIT = E1000_DEVID == E1000_DEV_ID_82573L ?                0xcc2 :
-                   E1000_DEVID == E1000_DEV_ID_82544GC_COPPER ?        0xc30 :
-                   /* default to E1000_DEV_ID_82540EM */       0xc20
-};
-
-typedef struct E1000State_st {
-    PCIDevice dev;
-    NICState *nic;
-    NICConf conf;
-    MemoryRegion mmio;
-    MemoryRegion io;
-
-    uint32_t mac_reg[0x8000];
-    uint16_t phy_reg[0x20];
-    uint16_t eeprom_data[64];
-
-    uint32_t rxbuf_size;
-    uint32_t rxbuf_min_shift;
-    struct e1000_tx {
-        unsigned char header[256];
-        unsigned char vlan_header[4];
-        /* Fields vlan and data must not be reordered or separated. */
-        unsigned char vlan[4];
-        unsigned char data[0x10000];
-        uint16_t size;
-        unsigned char sum_needed;
-        unsigned char vlan_needed;
-        uint8_t ipcss;
-        uint8_t ipcso;
-        uint16_t ipcse;
-        uint8_t tucss;
-        uint8_t tucso;
-        uint16_t tucse;
-        uint8_t hdr_len;
-        uint16_t mss;
-        uint32_t paylen;
-        uint16_t tso_frames;
-        char tse;
-        int8_t ip;
-        int8_t tcp;
-        char cptse;     // current packet tse bit
-    } tx;
-
-    struct {
-        uint32_t val_in;       // shifted in from guest driver
-        uint16_t bitnum_in;
-        uint16_t bitnum_out;
-        uint16_t reading;
-        uint32_t old_eecd;
-    } eecd_state;
-
-    QEMUTimer *autoneg_timer;
-
-/* Compatibility flags for migration to/from qemu 1.3.0 and older */
-#define E1000_FLAG_AUTONEG_BIT 0
-#define E1000_FLAG_AUTONEG (1 << E1000_FLAG_AUTONEG_BIT)
-    uint32_t compat_flags;
-} E1000State;
-
-#define        defreg(x)       x = (E1000_##x>>2)
-enum {
-    defreg(CTRL),      defreg(EECD),   defreg(EERD),   defreg(GPRC),
-    defreg(GPTC),      defreg(ICR),    defreg(ICS),    defreg(IMC),
-    defreg(IMS),       defreg(LEDCTL), defreg(MANC),   defreg(MDIC),
-    defreg(MPC),       defreg(PBA),    defreg(RCTL),   defreg(RDBAH),
-    defreg(RDBAL),     defreg(RDH),    defreg(RDLEN),  defreg(RDT),
-    defreg(STATUS),    defreg(SWSM),   defreg(TCTL),   defreg(TDBAH),
-    defreg(TDBAL),     defreg(TDH),    defreg(TDLEN),  defreg(TDT),
-    defreg(TORH),      defreg(TORL),   defreg(TOTH),   defreg(TOTL),
-    defreg(TPR),       defreg(TPT),    defreg(TXDCTL), defreg(WUFC),
-    defreg(RA),                defreg(MTA),    defreg(CRCERRS),defreg(VFTA),
-    defreg(VET),
-};
-
-static void
-e1000_link_down(E1000State *s)
-{
-    s->mac_reg[STATUS] &= ~E1000_STATUS_LU;
-    s->phy_reg[PHY_STATUS] &= ~MII_SR_LINK_STATUS;
-}
-
-static void
-e1000_link_up(E1000State *s)
-{
-    s->mac_reg[STATUS] |= E1000_STATUS_LU;
-    s->phy_reg[PHY_STATUS] |= MII_SR_LINK_STATUS;
-}
-
-static void
-set_phy_ctrl(E1000State *s, int index, uint16_t val)
-{
-    /*
-     * QEMU 1.3 does not support link auto-negotiation emulation, so if we
-     * migrate during auto negotiation, after migration the link will be
-     * down.
-     */
-    if (!(s->compat_flags & E1000_FLAG_AUTONEG)) {
-        return;
-    }
-    if ((val & MII_CR_AUTO_NEG_EN) && (val & MII_CR_RESTART_AUTO_NEG)) {
-        e1000_link_down(s);
-        s->phy_reg[PHY_STATUS] &= ~MII_SR_AUTONEG_COMPLETE;
-        DBGOUT(PHY, "Start link auto negotiation\n");
-        qemu_mod_timer(s->autoneg_timer, qemu_get_clock_ms(vm_clock) + 500);
-    }
-}
-
-static void
-e1000_autoneg_timer(void *opaque)
-{
-    E1000State *s = opaque;
-    if (!qemu_get_queue(s->nic)->link_down) {
-        e1000_link_up(s);
-    }
-    s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE;
-    DBGOUT(PHY, "Auto negotiation is completed\n");
-}
-
-static void (*phyreg_writeops[])(E1000State *, int, uint16_t) = {
-    [PHY_CTRL] = set_phy_ctrl,
-};
-
-enum { NPHYWRITEOPS = ARRAY_SIZE(phyreg_writeops) };
-
-enum { PHY_R = 1, PHY_W = 2, PHY_RW = PHY_R | PHY_W };
-static const char phy_regcap[0x20] = {
-    [PHY_STATUS] = PHY_R,      [M88E1000_EXT_PHY_SPEC_CTRL] = PHY_RW,
-    [PHY_ID1] = PHY_R,         [M88E1000_PHY_SPEC_CTRL] = PHY_RW,
-    [PHY_CTRL] = PHY_RW,       [PHY_1000T_CTRL] = PHY_RW,
-    [PHY_LP_ABILITY] = PHY_R,  [PHY_1000T_STATUS] = PHY_R,
-    [PHY_AUTONEG_ADV] = PHY_RW,        [M88E1000_RX_ERR_CNTR] = PHY_R,
-    [PHY_ID2] = PHY_R,         [M88E1000_PHY_SPEC_STATUS] = PHY_R
-};
-
-static const uint16_t phy_reg_init[] = {
-    [PHY_CTRL] = 0x1140,
-    [PHY_STATUS] = 0x794d, /* link initially up with not completed autoneg */
-    [PHY_ID1] = 0x141,                         [PHY_ID2] = PHY_ID2_INIT,
-    [PHY_1000T_CTRL] = 0x0e00,                 [M88E1000_PHY_SPEC_CTRL] = 0x360,
-    [M88E1000_EXT_PHY_SPEC_CTRL] = 0x0d60,     [PHY_AUTONEG_ADV] = 0xde1,
-    [PHY_LP_ABILITY] = 0x1e0,                  [PHY_1000T_STATUS] = 0x3c00,
-    [M88E1000_PHY_SPEC_STATUS] = 0xac00,
-};
-
-static const uint32_t mac_reg_init[] = {
-    [PBA] =     0x00100030,
-    [LEDCTL] =  0x602,
-    [CTRL] =    E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN0 |
-                E1000_CTRL_SPD_1000 | E1000_CTRL_SLU,
-    [STATUS] =  0x80000000 | E1000_STATUS_GIO_MASTER_ENABLE |
-                E1000_STATUS_ASDV | E1000_STATUS_MTXCKOK |
-                E1000_STATUS_SPEED_1000 | E1000_STATUS_FD |
-                E1000_STATUS_LU,
-    [MANC] =    E1000_MANC_EN_MNG2HOST | E1000_MANC_RCV_TCO_EN |
-                E1000_MANC_ARP_EN | E1000_MANC_0298_EN |
-                E1000_MANC_RMCP_EN,
-};
-
-static void
-set_interrupt_cause(E1000State *s, int index, uint32_t val)
-{
-    if (val && (E1000_DEVID >= E1000_DEV_ID_82547EI_MOBILE)) {
-        /* Only for 8257x */
-        val |= E1000_ICR_INT_ASSERTED;
-    }
-    s->mac_reg[ICR] = val;
-
-    /*
-     * Make sure ICR and ICS registers have the same value.
-     * The spec says that the ICS register is write-only.  However in practice,
-     * on real hardware ICS is readable, and for reads it has the same value as
-     * ICR (except that ICS does not have the clear on read behaviour of ICR).
-     *
-     * The VxWorks PRO/1000 driver uses this behaviour.
-     */
-    s->mac_reg[ICS] = val;
-
-    qemu_set_irq(s->dev.irq[0], (s->mac_reg[IMS] & s->mac_reg[ICR]) != 0);
-}
-
-static void
-set_ics(E1000State *s, int index, uint32_t val)
-{
-    DBGOUT(INTERRUPT, "set_ics %x, ICR %x, IMR %x\n", val, s->mac_reg[ICR],
-        s->mac_reg[IMS]);
-    set_interrupt_cause(s, 0, val | s->mac_reg[ICR]);
-}
-
-static int
-rxbufsize(uint32_t v)
-{
-    v &= E1000_RCTL_BSEX | E1000_RCTL_SZ_16384 | E1000_RCTL_SZ_8192 |
-         E1000_RCTL_SZ_4096 | E1000_RCTL_SZ_2048 | E1000_RCTL_SZ_1024 |
-         E1000_RCTL_SZ_512 | E1000_RCTL_SZ_256;
-    switch (v) {
-    case E1000_RCTL_BSEX | E1000_RCTL_SZ_16384:
-        return 16384;
-    case E1000_RCTL_BSEX | E1000_RCTL_SZ_8192:
-        return 8192;
-    case E1000_RCTL_BSEX | E1000_RCTL_SZ_4096:
-        return 4096;
-    case E1000_RCTL_SZ_1024:
-        return 1024;
-    case E1000_RCTL_SZ_512:
-        return 512;
-    case E1000_RCTL_SZ_256:
-        return 256;
-    }
-    return 2048;
-}
-
-static void e1000_reset(void *opaque)
-{
-    E1000State *d = opaque;
-    uint8_t *macaddr = d->conf.macaddr.a;
-    int i;
-
-    qemu_del_timer(d->autoneg_timer);
-    memset(d->phy_reg, 0, sizeof d->phy_reg);
-    memmove(d->phy_reg, phy_reg_init, sizeof phy_reg_init);
-    memset(d->mac_reg, 0, sizeof d->mac_reg);
-    memmove(d->mac_reg, mac_reg_init, sizeof mac_reg_init);
-    d->rxbuf_min_shift = 1;
-    memset(&d->tx, 0, sizeof d->tx);
-
-    if (qemu_get_queue(d->nic)->link_down) {
-        e1000_link_down(d);
-    }
-
-    /* Some guests expect pre-initialized RAH/RAL (AddrValid flag + MACaddr) */
-    d->mac_reg[RA] = 0;
-    d->mac_reg[RA + 1] = E1000_RAH_AV;
-    for (i = 0; i < 4; i++) {
-        d->mac_reg[RA] |= macaddr[i] << (8 * i);
-        d->mac_reg[RA + 1] |= (i < 2) ? macaddr[i + 4] << (8 * i) : 0;
-    }
-}
-
-static void
-set_ctrl(E1000State *s, int index, uint32_t val)
-{
-    /* RST is self clearing */
-    s->mac_reg[CTRL] = val & ~E1000_CTRL_RST;
-}
-
-static void
-set_rx_control(E1000State *s, int index, uint32_t val)
-{
-    s->mac_reg[RCTL] = val;
-    s->rxbuf_size = rxbufsize(val);
-    s->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1;
-    DBGOUT(RX, "RCTL: %d, mac_reg[RCTL] = 0x%x\n", s->mac_reg[RDT],
-           s->mac_reg[RCTL]);
-    qemu_flush_queued_packets(qemu_get_queue(s->nic));
-}
-
-static void
-set_mdic(E1000State *s, int index, uint32_t val)
-{
-    uint32_t data = val & E1000_MDIC_DATA_MASK;
-    uint32_t addr = ((val & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT);
-
-    if ((val & E1000_MDIC_PHY_MASK) >> E1000_MDIC_PHY_SHIFT != 1) // phy #
-        val = s->mac_reg[MDIC] | E1000_MDIC_ERROR;
-    else if (val & E1000_MDIC_OP_READ) {
-        DBGOUT(MDIC, "MDIC read reg 0x%x\n", addr);
-        if (!(phy_regcap[addr] & PHY_R)) {
-            DBGOUT(MDIC, "MDIC read reg %x unhandled\n", addr);
-            val |= E1000_MDIC_ERROR;
-        } else
-            val = (val ^ data) | s->phy_reg[addr];
-    } else if (val & E1000_MDIC_OP_WRITE) {
-        DBGOUT(MDIC, "MDIC write reg 0x%x, value 0x%x\n", addr, data);
-        if (!(phy_regcap[addr] & PHY_W)) {
-            DBGOUT(MDIC, "MDIC write reg %x unhandled\n", addr);
-            val |= E1000_MDIC_ERROR;
-        } else {
-            if (addr < NPHYWRITEOPS && phyreg_writeops[addr]) {
-                phyreg_writeops[addr](s, index, data);
-            }
-            s->phy_reg[addr] = data;
-        }
-    }
-    s->mac_reg[MDIC] = val | E1000_MDIC_READY;
-
-    if (val & E1000_MDIC_INT_EN) {
-        set_ics(s, 0, E1000_ICR_MDAC);
-    }
-}
-
-static uint32_t
-get_eecd(E1000State *s, int index)
-{
-    uint32_t ret = E1000_EECD_PRES|E1000_EECD_GNT | s->eecd_state.old_eecd;
-
-    DBGOUT(EEPROM, "reading eeprom bit %d (reading %d)\n",
-           s->eecd_state.bitnum_out, s->eecd_state.reading);
-    if (!s->eecd_state.reading ||
-        ((s->eeprom_data[(s->eecd_state.bitnum_out >> 4) & 0x3f] >>
-          ((s->eecd_state.bitnum_out & 0xf) ^ 0xf))) & 1)
-        ret |= E1000_EECD_DO;
-    return ret;
-}
-
-static void
-set_eecd(E1000State *s, int index, uint32_t val)
-{
-    uint32_t oldval = s->eecd_state.old_eecd;
-
-    s->eecd_state.old_eecd = val & (E1000_EECD_SK | E1000_EECD_CS |
-            E1000_EECD_DI|E1000_EECD_FWE_MASK|E1000_EECD_REQ);
-    if (!(E1000_EECD_CS & val))                        // CS inactive; nothing to do
-       return;
-    if (E1000_EECD_CS & (val ^ oldval)) {      // CS rise edge; reset state
-       s->eecd_state.val_in = 0;
-       s->eecd_state.bitnum_in = 0;
-       s->eecd_state.bitnum_out = 0;
-       s->eecd_state.reading = 0;
-    }
-    if (!(E1000_EECD_SK & (val ^ oldval)))     // no clock edge
-        return;
-    if (!(E1000_EECD_SK & val)) {              // falling edge
-        s->eecd_state.bitnum_out++;
-        return;
-    }
-    s->eecd_state.val_in <<= 1;
-    if (val & E1000_EECD_DI)
-        s->eecd_state.val_in |= 1;
-    if (++s->eecd_state.bitnum_in == 9 && !s->eecd_state.reading) {
-        s->eecd_state.bitnum_out = ((s->eecd_state.val_in & 0x3f)<<4)-1;
-        s->eecd_state.reading = (((s->eecd_state.val_in >> 6) & 7) ==
-            EEPROM_READ_OPCODE_MICROWIRE);
-    }
-    DBGOUT(EEPROM, "eeprom bitnum in %d out %d, reading %d\n",
-           s->eecd_state.bitnum_in, s->eecd_state.bitnum_out,
-           s->eecd_state.reading);
-}
-
-static uint32_t
-flash_eerd_read(E1000State *s, int x)
-{
-    unsigned int index, r = s->mac_reg[EERD] & ~E1000_EEPROM_RW_REG_START;
-
-    if ((s->mac_reg[EERD] & E1000_EEPROM_RW_REG_START) == 0)
-        return (s->mac_reg[EERD]);
-
-    if ((index = r >> E1000_EEPROM_RW_ADDR_SHIFT) > EEPROM_CHECKSUM_REG)
-        return (E1000_EEPROM_RW_REG_DONE | r);
-
-    return ((s->eeprom_data[index] << E1000_EEPROM_RW_REG_DATA) |
-           E1000_EEPROM_RW_REG_DONE | r);
-}
-
-static void
-putsum(uint8_t *data, uint32_t n, uint32_t sloc, uint32_t css, uint32_t cse)
-{
-    uint32_t sum;
-
-    if (cse && cse < n)
-        n = cse + 1;
-    if (sloc < n-1) {
-        sum = net_checksum_add(n-css, data+css);
-        cpu_to_be16wu((uint16_t *)(data + sloc),
-                      net_checksum_finish(sum));
-    }
-}
-
-static inline int
-vlan_enabled(E1000State *s)
-{
-    return ((s->mac_reg[CTRL] & E1000_CTRL_VME) != 0);
-}
-
-static inline int
-vlan_rx_filter_enabled(E1000State *s)
-{
-    return ((s->mac_reg[RCTL] & E1000_RCTL_VFE) != 0);
-}
-
-static inline int
-is_vlan_packet(E1000State *s, const uint8_t *buf)
-{
-    return (be16_to_cpup((uint16_t *)(buf + 12)) ==
-                le16_to_cpup((uint16_t *)(s->mac_reg + VET)));
-}
-
-static inline int
-is_vlan_txd(uint32_t txd_lower)
-{
-    return ((txd_lower & E1000_TXD_CMD_VLE) != 0);
-}
-
-/* FCS aka Ethernet CRC-32. We don't get it from backends and can't
- * fill it in, just pad descriptor length by 4 bytes unless guest
- * told us to strip it off the packet. */
-static inline int
-fcs_len(E1000State *s)
-{
-    return (s->mac_reg[RCTL] & E1000_RCTL_SECRC) ? 0 : 4;
-}
-
-static void
-e1000_send_packet(E1000State *s, const uint8_t *buf, int size)
-{
-    NetClientState *nc = qemu_get_queue(s->nic);
-    if (s->phy_reg[PHY_CTRL] & MII_CR_LOOPBACK) {
-        nc->info->receive(nc, buf, size);
-    } else {
-        qemu_send_packet(nc, buf, size);
-    }
-}
-
-static void
-xmit_seg(E1000State *s)
-{
-    uint16_t len, *sp;
-    unsigned int frames = s->tx.tso_frames, css, sofar, n;
-    struct e1000_tx *tp = &s->tx;
-
-    if (tp->tse && tp->cptse) {
-        css = tp->ipcss;
-        DBGOUT(TXSUM, "frames %d size %d ipcss %d\n",
-               frames, tp->size, css);
-        if (tp->ip) {          // IPv4
-            cpu_to_be16wu((uint16_t *)(tp->data+css+2),
-                          tp->size - css);
-            cpu_to_be16wu((uint16_t *)(tp->data+css+4),
-                          be16_to_cpup((uint16_t *)(tp->data+css+4))+frames);
-        } else                 // IPv6
-            cpu_to_be16wu((uint16_t *)(tp->data+css+4),
-                          tp->size - css);
-        css = tp->tucss;
-        len = tp->size - css;
-        DBGOUT(TXSUM, "tcp %d tucss %d len %d\n", tp->tcp, css, len);
-        if (tp->tcp) {
-            sofar = frames * tp->mss;
-            cpu_to_be32wu((uint32_t *)(tp->data+css+4),        // seq
-                be32_to_cpupu((uint32_t *)(tp->data+css+4))+sofar);
-            if (tp->paylen - sofar > tp->mss)
-                tp->data[css + 13] &= ~9;              // PSH, FIN
-        } else // UDP
-            cpu_to_be16wu((uint16_t *)(tp->data+css+4), len);
-        if (tp->sum_needed & E1000_TXD_POPTS_TXSM) {
-            unsigned int phsum;
-            // add pseudo-header length before checksum calculation
-            sp = (uint16_t *)(tp->data + tp->tucso);
-            phsum = be16_to_cpup(sp) + len;
-            phsum = (phsum >> 16) + (phsum & 0xffff);
-            cpu_to_be16wu(sp, phsum);
-        }
-        tp->tso_frames++;
-    }
-
-    if (tp->sum_needed & E1000_TXD_POPTS_TXSM)
-        putsum(tp->data, tp->size, tp->tucso, tp->tucss, tp->tucse);
-    if (tp->sum_needed & E1000_TXD_POPTS_IXSM)
-        putsum(tp->data, tp->size, tp->ipcso, tp->ipcss, tp->ipcse);
-    if (tp->vlan_needed) {
-        memmove(tp->vlan, tp->data, 4);
-        memmove(tp->data, tp->data + 4, 8);
-        memcpy(tp->data + 8, tp->vlan_header, 4);
-        e1000_send_packet(s, tp->vlan, tp->size + 4);
-    } else
-        e1000_send_packet(s, tp->data, tp->size);
-    s->mac_reg[TPT]++;
-    s->mac_reg[GPTC]++;
-    n = s->mac_reg[TOTL];
-    if ((s->mac_reg[TOTL] += s->tx.size) < n)
-        s->mac_reg[TOTH]++;
-}
-
-static void
-process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
-{
-    uint32_t txd_lower = le32_to_cpu(dp->lower.data);
-    uint32_t dtype = txd_lower & (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D);
-    unsigned int split_size = txd_lower & 0xffff, bytes, sz, op;
-    unsigned int msh = 0xfffff, hdr = 0;
-    uint64_t addr;
-    struct e1000_context_desc *xp = (struct e1000_context_desc *)dp;
-    struct e1000_tx *tp = &s->tx;
-
-    if (dtype == E1000_TXD_CMD_DEXT) { // context descriptor
-        op = le32_to_cpu(xp->cmd_and_length);
-        tp->ipcss = xp->lower_setup.ip_fields.ipcss;
-        tp->ipcso = xp->lower_setup.ip_fields.ipcso;
-        tp->ipcse = le16_to_cpu(xp->lower_setup.ip_fields.ipcse);
-        tp->tucss = xp->upper_setup.tcp_fields.tucss;
-        tp->tucso = xp->upper_setup.tcp_fields.tucso;
-        tp->tucse = le16_to_cpu(xp->upper_setup.tcp_fields.tucse);
-        tp->paylen = op & 0xfffff;
-        tp->hdr_len = xp->tcp_seg_setup.fields.hdr_len;
-        tp->mss = le16_to_cpu(xp->tcp_seg_setup.fields.mss);
-        tp->ip = (op & E1000_TXD_CMD_IP) ? 1 : 0;
-        tp->tcp = (op & E1000_TXD_CMD_TCP) ? 1 : 0;
-        tp->tse = (op & E1000_TXD_CMD_TSE) ? 1 : 0;
-        tp->tso_frames = 0;
-        if (tp->tucso == 0) {  // this is probably wrong
-            DBGOUT(TXSUM, "TCP/UDP: cso 0!\n");
-            tp->tucso = tp->tucss + (tp->tcp ? 16 : 6);
-        }
-        return;
-    } else if (dtype == (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D)) {
-        // data descriptor
-        if (tp->size == 0) {
-            tp->sum_needed = le32_to_cpu(dp->upper.data) >> 8;
-        }
-        tp->cptse = ( txd_lower & E1000_TXD_CMD_TSE ) ? 1 : 0;
-    } else {
-        // legacy descriptor
-        tp->cptse = 0;
-    }
-
-    if (vlan_enabled(s) && is_vlan_txd(txd_lower) &&
-        (tp->cptse || txd_lower & E1000_TXD_CMD_EOP)) {
-        tp->vlan_needed = 1;
-        cpu_to_be16wu((uint16_t *)(tp->vlan_header),
-                      le16_to_cpup((uint16_t *)(s->mac_reg + VET)));
-        cpu_to_be16wu((uint16_t *)(tp->vlan_header + 2),
-                      le16_to_cpu(dp->upper.fields.special));
-    }
-        
-    addr = le64_to_cpu(dp->buffer_addr);
-    if (tp->tse && tp->cptse) {
-        hdr = tp->hdr_len;
-        msh = hdr + tp->mss;
-        do {
-            bytes = split_size;
-            if (tp->size + bytes > msh)
-                bytes = msh - tp->size;
-
-            bytes = MIN(sizeof(tp->data) - tp->size, bytes);
-            pci_dma_read(&s->dev, addr, tp->data + tp->size, bytes);
-            if ((sz = tp->size + bytes) >= hdr && tp->size < hdr)
-                memmove(tp->header, tp->data, hdr);
-            tp->size = sz;
-            addr += bytes;
-            if (sz == msh) {
-                xmit_seg(s);
-                memmove(tp->data, tp->header, hdr);
-                tp->size = hdr;
-            }
-        } while (split_size -= bytes);
-    } else if (!tp->tse && tp->cptse) {
-        // context descriptor TSE is not set, while data descriptor TSE is set
-        DBGOUT(TXERR, "TCP segmentation error\n");
-    } else {
-        split_size = MIN(sizeof(tp->data) - tp->size, split_size);
-        pci_dma_read(&s->dev, addr, tp->data + tp->size, split_size);
-        tp->size += split_size;
-    }
-
-    if (!(txd_lower & E1000_TXD_CMD_EOP))
-        return;
-    if (!(tp->tse && tp->cptse && tp->size < hdr))
-        xmit_seg(s);
-    tp->tso_frames = 0;
-    tp->sum_needed = 0;
-    tp->vlan_needed = 0;
-    tp->size = 0;
-    tp->cptse = 0;
-}
-
-static uint32_t
-txdesc_writeback(E1000State *s, dma_addr_t base, struct e1000_tx_desc *dp)
-{
-    uint32_t txd_upper, txd_lower = le32_to_cpu(dp->lower.data);
-
-    if (!(txd_lower & (E1000_TXD_CMD_RS|E1000_TXD_CMD_RPS)))
-        return 0;
-    txd_upper = (le32_to_cpu(dp->upper.data) | E1000_TXD_STAT_DD) &
-                ~(E1000_TXD_STAT_EC | E1000_TXD_STAT_LC | E1000_TXD_STAT_TU);
-    dp->upper.data = cpu_to_le32(txd_upper);
-    pci_dma_write(&s->dev, base + ((char *)&dp->upper - (char *)dp),
-                  &dp->upper, sizeof(dp->upper));
-    return E1000_ICR_TXDW;
-}
-
-static uint64_t tx_desc_base(E1000State *s)
-{
-    uint64_t bah = s->mac_reg[TDBAH];
-    uint64_t bal = s->mac_reg[TDBAL] & ~0xf;
-
-    return (bah << 32) + bal;
-}
-
-static void
-start_xmit(E1000State *s)
-{
-    dma_addr_t base;
-    struct e1000_tx_desc desc;
-    uint32_t tdh_start = s->mac_reg[TDH], cause = E1000_ICS_TXQE;
-
-    if (!(s->mac_reg[TCTL] & E1000_TCTL_EN)) {
-        DBGOUT(TX, "tx disabled\n");
-        return;
-    }
-
-    while (s->mac_reg[TDH] != s->mac_reg[TDT]) {
-        base = tx_desc_base(s) +
-               sizeof(struct e1000_tx_desc) * s->mac_reg[TDH];
-        pci_dma_read(&s->dev, base, &desc, sizeof(desc));
-
-        DBGOUT(TX, "index %d: %p : %x %x\n", s->mac_reg[TDH],
-               (void *)(intptr_t)desc.buffer_addr, desc.lower.data,
-               desc.upper.data);
-
-        process_tx_desc(s, &desc);
-        cause |= txdesc_writeback(s, base, &desc);
-
-        if (++s->mac_reg[TDH] * sizeof(desc) >= s->mac_reg[TDLEN])
-            s->mac_reg[TDH] = 0;
-        /*
-         * the following could happen only if guest sw assigns
-         * bogus values to TDT/TDLEN.
-         * there's nothing too intelligent we could do about this.
-         */
-        if (s->mac_reg[TDH] == tdh_start) {
-            DBGOUT(TXERR, "TDH wraparound @%x, TDT %x, TDLEN %x\n",
-                   tdh_start, s->mac_reg[TDT], s->mac_reg[TDLEN]);
-            break;
-        }
-    }
-    set_ics(s, 0, cause);
-}
-
-static int
-receive_filter(E1000State *s, const uint8_t *buf, int size)
-{
-    static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
-    static const int mta_shift[] = {4, 3, 2, 0};
-    uint32_t f, rctl = s->mac_reg[RCTL], ra[2], *rp;
-
-    if (is_vlan_packet(s, buf) && vlan_rx_filter_enabled(s)) {
-        uint16_t vid = be16_to_cpup((uint16_t *)(buf + 14));
-        uint32_t vfta = le32_to_cpup((uint32_t *)(s->mac_reg + VFTA) +
-                                     ((vid >> 5) & 0x7f));
-        if ((vfta & (1 << (vid & 0x1f))) == 0)
-            return 0;
-    }
-
-    if (rctl & E1000_RCTL_UPE)                 // promiscuous
-        return 1;
-
-    if ((buf[0] & 1) && (rctl & E1000_RCTL_MPE))       // promiscuous mcast
-        return 1;
-
-    if ((rctl & E1000_RCTL_BAM) && !memcmp(buf, bcast, sizeof bcast))
-        return 1;
-
-    for (rp = s->mac_reg + RA; rp < s->mac_reg + RA + 32; rp += 2) {
-        if (!(rp[1] & E1000_RAH_AV))
-            continue;
-        ra[0] = cpu_to_le32(rp[0]);
-        ra[1] = cpu_to_le32(rp[1]);
-        if (!memcmp(buf, (uint8_t *)ra, 6)) {
-            DBGOUT(RXFILTER,
-                   "unicast match[%d]: %02x:%02x:%02x:%02x:%02x:%02x\n",
-                   (int)(rp - s->mac_reg - RA)/2,
-                   buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
-            return 1;
-        }
-    }
-    DBGOUT(RXFILTER, "unicast mismatch: %02x:%02x:%02x:%02x:%02x:%02x\n",
-           buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
-
-    f = mta_shift[(rctl >> E1000_RCTL_MO_SHIFT) & 3];
-    f = (((buf[5] << 8) | buf[4]) >> f) & 0xfff;
-    if (s->mac_reg[MTA + (f >> 5)] & (1 << (f & 0x1f)))
-        return 1;
-    DBGOUT(RXFILTER,
-           "dropping, inexact filter mismatch: %02x:%02x:%02x:%02x:%02x:%02x MO %d MTA[%d] %x\n",
-           buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
-           (rctl >> E1000_RCTL_MO_SHIFT) & 3, f >> 5,
-           s->mac_reg[MTA + (f >> 5)]);
-
-    return 0;
-}
-
-static void
-e1000_set_link_status(NetClientState *nc)
-{
-    E1000State *s = qemu_get_nic_opaque(nc);
-    uint32_t old_status = s->mac_reg[STATUS];
-
-    if (nc->link_down) {
-        e1000_link_down(s);
-    } else {
-        e1000_link_up(s);
-    }
-
-    if (s->mac_reg[STATUS] != old_status)
-        set_ics(s, 0, E1000_ICR_LSC);
-}
-
-static bool e1000_has_rxbufs(E1000State *s, size_t total_size)
-{
-    int bufs;
-    /* Fast-path short packets */
-    if (total_size <= s->rxbuf_size) {
-        return s->mac_reg[RDH] != s->mac_reg[RDT];
-    }
-    if (s->mac_reg[RDH] < s->mac_reg[RDT]) {
-        bufs = s->mac_reg[RDT] - s->mac_reg[RDH];
-    } else if (s->mac_reg[RDH] > s->mac_reg[RDT]) {
-        bufs = s->mac_reg[RDLEN] /  sizeof(struct e1000_rx_desc) +
-            s->mac_reg[RDT] - s->mac_reg[RDH];
-    } else {
-        return false;
-    }
-    return total_size <= bufs * s->rxbuf_size;
-}
-
-static int
-e1000_can_receive(NetClientState *nc)
-{
-    E1000State *s = qemu_get_nic_opaque(nc);
-
-    return (s->mac_reg[STATUS] & E1000_STATUS_LU) &&
-        (s->mac_reg[RCTL] & E1000_RCTL_EN) && e1000_has_rxbufs(s, 1);
-}
-
-static uint64_t rx_desc_base(E1000State *s)
-{
-    uint64_t bah = s->mac_reg[RDBAH];
-    uint64_t bal = s->mac_reg[RDBAL] & ~0xf;
-
-    return (bah << 32) + bal;
-}
-
-static ssize_t
-e1000_receive(NetClientState *nc, const uint8_t *buf, size_t size)
-{
-    E1000State *s = qemu_get_nic_opaque(nc);
-    struct e1000_rx_desc desc;
-    dma_addr_t base;
-    unsigned int n, rdt;
-    uint32_t rdh_start;
-    uint16_t vlan_special = 0;
-    uint8_t vlan_status = 0, vlan_offset = 0;
-    uint8_t min_buf[MIN_BUF_SIZE];
-    size_t desc_offset;
-    size_t desc_size;
-    size_t total_size;
-
-    if (!(s->mac_reg[STATUS] & E1000_STATUS_LU)) {
-        return -1;
-    }
-
-    if (!(s->mac_reg[RCTL] & E1000_RCTL_EN)) {
-        return -1;
-    }
-
-    /* Pad to minimum Ethernet frame length */
-    if (size < sizeof(min_buf)) {
-        memcpy(min_buf, buf, size);
-        memset(&min_buf[size], 0, sizeof(min_buf) - size);
-        buf = min_buf;
-        size = sizeof(min_buf);
-    }
-
-    /* Discard oversized packets if !LPE and !SBP. */
-    if ((size > MAXIMUM_ETHERNET_LPE_SIZE ||
-        (size > MAXIMUM_ETHERNET_VLAN_SIZE
-        && !(s->mac_reg[RCTL] & E1000_RCTL_LPE)))
-        && !(s->mac_reg[RCTL] & E1000_RCTL_SBP)) {
-        return size;
-    }
-
-    if (!receive_filter(s, buf, size))
-        return size;
-
-    if (vlan_enabled(s) && is_vlan_packet(s, buf)) {
-        vlan_special = cpu_to_le16(be16_to_cpup((uint16_t *)(buf + 14)));
-        memmove((uint8_t *)buf + 4, buf, 12);
-        vlan_status = E1000_RXD_STAT_VP;
-        vlan_offset = 4;
-        size -= 4;
-    }
-
-    rdh_start = s->mac_reg[RDH];
-    desc_offset = 0;
-    total_size = size + fcs_len(s);
-    if (!e1000_has_rxbufs(s, total_size)) {
-            set_ics(s, 0, E1000_ICS_RXO);
-            return -1;
-    }
-    do {
-        desc_size = total_size - desc_offset;
-        if (desc_size > s->rxbuf_size) {
-            desc_size = s->rxbuf_size;
-        }
-        base = rx_desc_base(s) + sizeof(desc) * s->mac_reg[RDH];
-        pci_dma_read(&s->dev, base, &desc, sizeof(desc));
-        desc.special = vlan_special;
-        desc.status |= (vlan_status | E1000_RXD_STAT_DD);
-        if (desc.buffer_addr) {
-            if (desc_offset < size) {
-                size_t copy_size = size - desc_offset;
-                if (copy_size > s->rxbuf_size) {
-                    copy_size = s->rxbuf_size;
-                }
-                pci_dma_write(&s->dev, le64_to_cpu(desc.buffer_addr),
-                              buf + desc_offset + vlan_offset, copy_size);
-            }
-            desc_offset += desc_size;
-            desc.length = cpu_to_le16(desc_size);
-            if (desc_offset >= total_size) {
-                desc.status |= E1000_RXD_STAT_EOP | E1000_RXD_STAT_IXSM;
-            } else {
-                /* Guest zeroing out status is not a hardware requirement.
-                   Clear EOP in case guest didn't do it. */
-                desc.status &= ~E1000_RXD_STAT_EOP;
-            }
-        } else { // as per intel docs; skip descriptors with null buf addr
-            DBGOUT(RX, "Null RX descriptor!!\n");
-        }
-        pci_dma_write(&s->dev, base, &desc, sizeof(desc));
-
-        if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN])
-            s->mac_reg[RDH] = 0;
-        /* see comment in start_xmit; same here */
-        if (s->mac_reg[RDH] == rdh_start) {
-            DBGOUT(RXERR, "RDH wraparound @%x, RDT %x, RDLEN %x\n",
-                   rdh_start, s->mac_reg[RDT], s->mac_reg[RDLEN]);
-            set_ics(s, 0, E1000_ICS_RXO);
-            return -1;
-        }
-    } while (desc_offset < total_size);
-
-    s->mac_reg[GPRC]++;
-    s->mac_reg[TPR]++;
-    /* TOR - Total Octets Received:
-     * This register includes bytes received in a packet from the <Destination
-     * Address> field through the <CRC> field, inclusively.
-     */
-    n = s->mac_reg[TORL] + size + /* Always include FCS length. */ 4;
-    if (n < s->mac_reg[TORL])
-        s->mac_reg[TORH]++;
-    s->mac_reg[TORL] = n;
-
-    n = E1000_ICS_RXT0;
-    if ((rdt = s->mac_reg[RDT]) < s->mac_reg[RDH])
-        rdt += s->mac_reg[RDLEN] / sizeof(desc);
-    if (((rdt - s->mac_reg[RDH]) * sizeof(desc)) <= s->mac_reg[RDLEN] >>
-        s->rxbuf_min_shift)
-        n |= E1000_ICS_RXDMT0;
-
-    set_ics(s, 0, n);
-
-    return size;
-}
-
-static uint32_t
-mac_readreg(E1000State *s, int index)
-{
-    return s->mac_reg[index];
-}
-
-static uint32_t
-mac_icr_read(E1000State *s, int index)
-{
-    uint32_t ret = s->mac_reg[ICR];
-
-    DBGOUT(INTERRUPT, "ICR read: %x\n", ret);
-    set_interrupt_cause(s, 0, 0);
-    return ret;
-}
-
-static uint32_t
-mac_read_clr4(E1000State *s, int index)
-{
-    uint32_t ret = s->mac_reg[index];
-
-    s->mac_reg[index] = 0;
-    return ret;
-}
-
-static uint32_t
-mac_read_clr8(E1000State *s, int index)
-{
-    uint32_t ret = s->mac_reg[index];
-
-    s->mac_reg[index] = 0;
-    s->mac_reg[index-1] = 0;
-    return ret;
-}
-
-static void
-mac_writereg(E1000State *s, int index, uint32_t val)
-{
-    s->mac_reg[index] = val;
-}
-
-static void
-set_rdt(E1000State *s, int index, uint32_t val)
-{
-    s->mac_reg[index] = val & 0xffff;
-    if (e1000_has_rxbufs(s, 1)) {
-        qemu_flush_queued_packets(qemu_get_queue(s->nic));
-    }
-}
-
-static void
-set_16bit(E1000State *s, int index, uint32_t val)
-{
-    s->mac_reg[index] = val & 0xffff;
-}
-
-static void
-set_dlen(E1000State *s, int index, uint32_t val)
-{
-    s->mac_reg[index] = val & 0xfff80;
-}
-
-static void
-set_tctl(E1000State *s, int index, uint32_t val)
-{
-    s->mac_reg[index] = val;
-    s->mac_reg[TDT] &= 0xffff;
-    start_xmit(s);
-}
-
-static void
-set_icr(E1000State *s, int index, uint32_t val)
-{
-    DBGOUT(INTERRUPT, "set_icr %x\n", val);
-    set_interrupt_cause(s, 0, s->mac_reg[ICR] & ~val);
-}
-
-static void
-set_imc(E1000State *s, int index, uint32_t val)
-{
-    s->mac_reg[IMS] &= ~val;
-    set_ics(s, 0, 0);
-}
-
-static void
-set_ims(E1000State *s, int index, uint32_t val)
-{
-    s->mac_reg[IMS] |= val;
-    set_ics(s, 0, 0);
-}
-
-#define getreg(x)      [x] = mac_readreg
-static uint32_t (*macreg_readops[])(E1000State *, int) = {
-    getreg(PBA),       getreg(RCTL),   getreg(TDH),    getreg(TXDCTL),
-    getreg(WUFC),      getreg(TDT),    getreg(CTRL),   getreg(LEDCTL),
-    getreg(MANC),      getreg(MDIC),   getreg(SWSM),   getreg(STATUS),
-    getreg(TORL),      getreg(TOTL),   getreg(IMS),    getreg(TCTL),
-    getreg(RDH),       getreg(RDT),    getreg(VET),    getreg(ICS),
-    getreg(TDBAL),     getreg(TDBAH),  getreg(RDBAH),  getreg(RDBAL),
-    getreg(TDLEN),     getreg(RDLEN),
-
-    [TOTH] = mac_read_clr8,    [TORH] = mac_read_clr8, [GPRC] = mac_read_clr4,
-    [GPTC] = mac_read_clr4,    [TPR] = mac_read_clr4,  [TPT] = mac_read_clr4,
-    [ICR] = mac_icr_read,      [EECD] = get_eecd,      [EERD] = flash_eerd_read,
-    [CRCERRS ... MPC] = &mac_readreg,
-    [RA ... RA+31] = &mac_readreg,
-    [MTA ... MTA+127] = &mac_readreg,
-    [VFTA ... VFTA+127] = &mac_readreg,
-};
-enum { NREADOPS = ARRAY_SIZE(macreg_readops) };
-
-#define putreg(x)      [x] = mac_writereg
-static void (*macreg_writeops[])(E1000State *, int, uint32_t) = {
-    putreg(PBA),       putreg(EERD),   putreg(SWSM),   putreg(WUFC),
-    putreg(TDBAL),     putreg(TDBAH),  putreg(TXDCTL), putreg(RDBAH),
-    putreg(RDBAL),     putreg(LEDCTL), putreg(VET),
-    [TDLEN] = set_dlen,        [RDLEN] = set_dlen,     [TCTL] = set_tctl,
-    [TDT] = set_tctl,  [MDIC] = set_mdic,      [ICS] = set_ics,
-    [TDH] = set_16bit, [RDH] = set_16bit,      [RDT] = set_rdt,
-    [IMC] = set_imc,   [IMS] = set_ims,        [ICR] = set_icr,
-    [EECD] = set_eecd, [RCTL] = set_rx_control, [CTRL] = set_ctrl,
-    [RA ... RA+31] = &mac_writereg,
-    [MTA ... MTA+127] = &mac_writereg,
-    [VFTA ... VFTA+127] = &mac_writereg,
-};
-
-enum { NWRITEOPS = ARRAY_SIZE(macreg_writeops) };
-
-static void
-e1000_mmio_write(void *opaque, hwaddr addr, uint64_t val,
-                 unsigned size)
-{
-    E1000State *s = opaque;
-    unsigned int index = (addr & 0x1ffff) >> 2;
-
-    if (index < NWRITEOPS && macreg_writeops[index]) {
-        macreg_writeops[index](s, index, val);
-    } else if (index < NREADOPS && macreg_readops[index]) {
-        DBGOUT(MMIO, "e1000_mmio_writel RO %x: 0x%04"PRIx64"\n", index<<2, val);
-    } else {
-        DBGOUT(UNKNOWN, "MMIO unknown write addr=0x%08x,val=0x%08"PRIx64"\n",
-               index<<2, val);
-    }
-}
-
-static uint64_t
-e1000_mmio_read(void *opaque, hwaddr addr, unsigned size)
-{
-    E1000State *s = opaque;
-    unsigned int index = (addr & 0x1ffff) >> 2;
-
-    if (index < NREADOPS && macreg_readops[index])
-    {
-        return macreg_readops[index](s, index);
-    }
-    DBGOUT(UNKNOWN, "MMIO unknown read addr=0x%08x\n", index<<2);
-    return 0;
-}
-
-static const MemoryRegionOps e1000_mmio_ops = {
-    .read = e1000_mmio_read,
-    .write = e1000_mmio_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .impl = {
-        .min_access_size = 4,
-        .max_access_size = 4,
-    },
-};
-
-static uint64_t e1000_io_read(void *opaque, hwaddr addr,
-                              unsigned size)
-{
-    E1000State *s = opaque;
-
-    (void)s;
-    return 0;
-}
-
-static void e1000_io_write(void *opaque, hwaddr addr,
-                           uint64_t val, unsigned size)
-{
-    E1000State *s = opaque;
-
-    (void)s;
-}
-
-static const MemoryRegionOps e1000_io_ops = {
-    .read = e1000_io_read,
-    .write = e1000_io_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static bool is_version_1(void *opaque, int version_id)
-{
-    return version_id == 1;
-}
-
-static void e1000_pre_save(void *opaque)
-{
-    E1000State *s = opaque;
-    NetClientState *nc = qemu_get_queue(s->nic);
-
-    if (!(s->compat_flags & E1000_FLAG_AUTONEG)) {
-        return;
-    }
-
-    /*
-     * If link is down and auto-negotiation is ongoing, complete
-     * auto-negotiation immediately.  This allows is to look at
-     * MII_SR_AUTONEG_COMPLETE to infer link status on load.
-     */
-    if (nc->link_down &&
-        s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN &&
-        s->phy_reg[PHY_CTRL] & MII_CR_RESTART_AUTO_NEG) {
-         s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE;
-    }
-}
-
-static int e1000_post_load(void *opaque, int version_id)
-{
-    E1000State *s = opaque;
-    NetClientState *nc = qemu_get_queue(s->nic);
-
-    /* nc.link_down can't be migrated, so infer link_down according
-     * to link status bit in mac_reg[STATUS].
-     * Alternatively, restart link negotiation if it was in progress. */
-    nc->link_down = (s->mac_reg[STATUS] & E1000_STATUS_LU) == 0;
-
-    if (!(s->compat_flags & E1000_FLAG_AUTONEG)) {
-        return 0;
-    }
-
-    if (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN &&
-        s->phy_reg[PHY_CTRL] & MII_CR_RESTART_AUTO_NEG &&
-        !(s->phy_reg[PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) {
-        nc->link_down = false;
-        qemu_mod_timer(s->autoneg_timer, qemu_get_clock_ms(vm_clock) + 500);
-    }
-
-    return 0;
-}
-
-static const VMStateDescription vmstate_e1000 = {
-    .name = "e1000",
-    .version_id = 2,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .pre_save = e1000_pre_save,
-    .post_load = e1000_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_PCI_DEVICE(dev, E1000State),
-        VMSTATE_UNUSED_TEST(is_version_1, 4), /* was instance id */
-        VMSTATE_UNUSED(4), /* Was mmio_base.  */
-        VMSTATE_UINT32(rxbuf_size, E1000State),
-        VMSTATE_UINT32(rxbuf_min_shift, E1000State),
-        VMSTATE_UINT32(eecd_state.val_in, E1000State),
-        VMSTATE_UINT16(eecd_state.bitnum_in, E1000State),
-        VMSTATE_UINT16(eecd_state.bitnum_out, E1000State),
-        VMSTATE_UINT16(eecd_state.reading, E1000State),
-        VMSTATE_UINT32(eecd_state.old_eecd, E1000State),
-        VMSTATE_UINT8(tx.ipcss, E1000State),
-        VMSTATE_UINT8(tx.ipcso, E1000State),
-        VMSTATE_UINT16(tx.ipcse, E1000State),
-        VMSTATE_UINT8(tx.tucss, E1000State),
-        VMSTATE_UINT8(tx.tucso, E1000State),
-        VMSTATE_UINT16(tx.tucse, E1000State),
-        VMSTATE_UINT32(tx.paylen, E1000State),
-        VMSTATE_UINT8(tx.hdr_len, E1000State),
-        VMSTATE_UINT16(tx.mss, E1000State),
-        VMSTATE_UINT16(tx.size, E1000State),
-        VMSTATE_UINT16(tx.tso_frames, E1000State),
-        VMSTATE_UINT8(tx.sum_needed, E1000State),
-        VMSTATE_INT8(tx.ip, E1000State),
-        VMSTATE_INT8(tx.tcp, E1000State),
-        VMSTATE_BUFFER(tx.header, E1000State),
-        VMSTATE_BUFFER(tx.data, E1000State),
-        VMSTATE_UINT16_ARRAY(eeprom_data, E1000State, 64),
-        VMSTATE_UINT16_ARRAY(phy_reg, E1000State, 0x20),
-        VMSTATE_UINT32(mac_reg[CTRL], E1000State),
-        VMSTATE_UINT32(mac_reg[EECD], E1000State),
-        VMSTATE_UINT32(mac_reg[EERD], E1000State),
-        VMSTATE_UINT32(mac_reg[GPRC], E1000State),
-        VMSTATE_UINT32(mac_reg[GPTC], E1000State),
-        VMSTATE_UINT32(mac_reg[ICR], E1000State),
-        VMSTATE_UINT32(mac_reg[ICS], E1000State),
-        VMSTATE_UINT32(mac_reg[IMC], E1000State),
-        VMSTATE_UINT32(mac_reg[IMS], E1000State),
-        VMSTATE_UINT32(mac_reg[LEDCTL], E1000State),
-        VMSTATE_UINT32(mac_reg[MANC], E1000State),
-        VMSTATE_UINT32(mac_reg[MDIC], E1000State),
-        VMSTATE_UINT32(mac_reg[MPC], E1000State),
-        VMSTATE_UINT32(mac_reg[PBA], E1000State),
-        VMSTATE_UINT32(mac_reg[RCTL], E1000State),
-        VMSTATE_UINT32(mac_reg[RDBAH], E1000State),
-        VMSTATE_UINT32(mac_reg[RDBAL], E1000State),
-        VMSTATE_UINT32(mac_reg[RDH], E1000State),
-        VMSTATE_UINT32(mac_reg[RDLEN], E1000State),
-        VMSTATE_UINT32(mac_reg[RDT], E1000State),
-        VMSTATE_UINT32(mac_reg[STATUS], E1000State),
-        VMSTATE_UINT32(mac_reg[SWSM], E1000State),
-        VMSTATE_UINT32(mac_reg[TCTL], E1000State),
-        VMSTATE_UINT32(mac_reg[TDBAH], E1000State),
-        VMSTATE_UINT32(mac_reg[TDBAL], E1000State),
-        VMSTATE_UINT32(mac_reg[TDH], E1000State),
-        VMSTATE_UINT32(mac_reg[TDLEN], E1000State),
-        VMSTATE_UINT32(mac_reg[TDT], E1000State),
-        VMSTATE_UINT32(mac_reg[TORH], E1000State),
-        VMSTATE_UINT32(mac_reg[TORL], E1000State),
-        VMSTATE_UINT32(mac_reg[TOTH], E1000State),
-        VMSTATE_UINT32(mac_reg[TOTL], E1000State),
-        VMSTATE_UINT32(mac_reg[TPR], E1000State),
-        VMSTATE_UINT32(mac_reg[TPT], E1000State),
-        VMSTATE_UINT32(mac_reg[TXDCTL], E1000State),
-        VMSTATE_UINT32(mac_reg[WUFC], E1000State),
-        VMSTATE_UINT32(mac_reg[VET], E1000State),
-        VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, RA, 32),
-        VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, MTA, 128),
-        VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, VFTA, 128),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const uint16_t e1000_eeprom_template[64] = {
-    0x0000, 0x0000, 0x0000, 0x0000,      0xffff, 0x0000,      0x0000, 0x0000,
-    0x3000, 0x1000, 0x6403, E1000_DEVID, 0x8086, E1000_DEVID, 0x8086, 0x3040,
-    0x0008, 0x2000, 0x7e14, 0x0048,      0x1000, 0x00d8,      0x0000, 0x2700,
-    0x6cc9, 0x3150, 0x0722, 0x040b,      0x0984, 0x0000,      0xc000, 0x0706,
-    0x1008, 0x0000, 0x0f04, 0x7fff,      0x4d01, 0xffff,      0xffff, 0xffff,
-    0xffff, 0xffff, 0xffff, 0xffff,      0xffff, 0xffff,      0xffff, 0xffff,
-    0x0100, 0x4000, 0x121c, 0xffff,      0xffff, 0xffff,      0xffff, 0xffff,
-    0xffff, 0xffff, 0xffff, 0xffff,      0xffff, 0xffff,      0xffff, 0x0000,
-};
-
-/* PCI interface */
-
-static void
-e1000_mmio_setup(E1000State *d)
-{
-    int i;
-    const uint32_t excluded_regs[] = {
-        E1000_MDIC, E1000_ICR, E1000_ICS, E1000_IMS,
-        E1000_IMC, E1000_TCTL, E1000_TDT, PNPMMIO_SIZE
-    };
-
-    memory_region_init_io(&d->mmio, &e1000_mmio_ops, d, "e1000-mmio",
-                          PNPMMIO_SIZE);
-    memory_region_add_coalescing(&d->mmio, 0, excluded_regs[0]);
-    for (i = 0; excluded_regs[i] != PNPMMIO_SIZE; i++)
-        memory_region_add_coalescing(&d->mmio, excluded_regs[i] + 4,
-                                     excluded_regs[i+1] - excluded_regs[i] - 4);
-    memory_region_init_io(&d->io, &e1000_io_ops, d, "e1000-io", IOPORT_SIZE);
-}
-
-static void
-e1000_cleanup(NetClientState *nc)
-{
-    E1000State *s = qemu_get_nic_opaque(nc);
-
-    s->nic = NULL;
-}
-
-static void
-pci_e1000_uninit(PCIDevice *dev)
-{
-    E1000State *d = DO_UPCAST(E1000State, dev, dev);
-
-    qemu_del_timer(d->autoneg_timer);
-    qemu_free_timer(d->autoneg_timer);
-    memory_region_destroy(&d->mmio);
-    memory_region_destroy(&d->io);
-    qemu_del_nic(d->nic);
-}
-
-static NetClientInfo net_e1000_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
-    .size = sizeof(NICState),
-    .can_receive = e1000_can_receive,
-    .receive = e1000_receive,
-    .cleanup = e1000_cleanup,
-    .link_status_changed = e1000_set_link_status,
-};
-
-static int pci_e1000_init(PCIDevice *pci_dev)
-{
-    E1000State *d = DO_UPCAST(E1000State, dev, pci_dev);
-    uint8_t *pci_conf;
-    uint16_t checksum = 0;
-    int i;
-    uint8_t *macaddr;
-
-    pci_conf = d->dev.config;
-
-    /* TODO: RST# value should be 0, PCI spec 6.2.4 */
-    pci_conf[PCI_CACHE_LINE_SIZE] = 0x10;
-
-    pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */
-
-    e1000_mmio_setup(d);
-
-    pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
-
-    pci_register_bar(&d->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->io);
-
-    memmove(d->eeprom_data, e1000_eeprom_template,
-        sizeof e1000_eeprom_template);
-    qemu_macaddr_default_if_unset(&d->conf.macaddr);
-    macaddr = d->conf.macaddr.a;
-    for (i = 0; i < 3; i++)
-        d->eeprom_data[i] = (macaddr[2*i+1]<<8) | macaddr[2*i];
-    for (i = 0; i < EEPROM_CHECKSUM_REG; i++)
-        checksum += d->eeprom_data[i];
-    checksum = (uint16_t) EEPROM_SUM - checksum;
-    d->eeprom_data[EEPROM_CHECKSUM_REG] = checksum;
-
-    d->nic = qemu_new_nic(&net_e1000_info, &d->conf,
-                          object_get_typename(OBJECT(d)), d->dev.qdev.id, d);
-
-    qemu_format_nic_info_str(qemu_get_queue(d->nic), macaddr);
-
-    add_boot_device_path(d->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0");
-
-    d->autoneg_timer = qemu_new_timer_ms(vm_clock, e1000_autoneg_timer, d);
-
-    return 0;
-}
-
-static void qdev_e1000_reset(DeviceState *dev)
-{
-    E1000State *d = DO_UPCAST(E1000State, dev.qdev, dev);
-    e1000_reset(d);
-}
-
-static Property e1000_properties[] = {
-    DEFINE_NIC_PROPERTIES(E1000State, conf),
-    DEFINE_PROP_BIT("autonegotiation", E1000State,
-                    compat_flags, E1000_FLAG_AUTONEG_BIT, true),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void e1000_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = pci_e1000_init;
-    k->exit = pci_e1000_uninit;
-    k->romfile = "efi-e1000.rom";
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->device_id = E1000_DEVID;
-    k->revision = 0x03;
-    k->class_id = PCI_CLASS_NETWORK_ETHERNET;
-    dc->desc = "Intel Gigabit Ethernet";
-    dc->reset = qdev_e1000_reset;
-    dc->vmsd = &vmstate_e1000;
-    dc->props = e1000_properties;
-}
-
-static const TypeInfo e1000_info = {
-    .name          = "e1000",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(E1000State),
-    .class_init    = e1000_class_init,
-};
-
-static void e1000_register_types(void)
-{
-    type_register_static(&e1000_info);
-}
-
-type_init(e1000_register_types)
diff --git a/hw/ecc.c b/hw/ecc.c
deleted file mode 100644 (file)
index 8c888cc..0000000
--- a/hw/ecc.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Calculate Error-correcting Codes. Used by NAND Flash controllers
- * (not by NAND chips).
- *
- * Copyright (c) 2006 Openedhand Ltd.
- * Written by Andrzej Zaborowski <balrog@zabor.org>
- *
- * This code is licensed under the GNU GPL v2.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "hw/hw.h"
-#include "hw/block/flash.h"
-
-/*
- * Pre-calculated 256-way 1 byte column parity.  Table borrowed from Linux.
- */
-static const uint8_t nand_ecc_precalc_table[] = {
-    0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
-    0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
-    0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
-    0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
-    0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
-    0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
-    0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
-    0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
-    0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
-    0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
-    0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
-    0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
-    0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
-    0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
-    0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
-    0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
-    0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
-    0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
-    0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
-    0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
-    0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
-    0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
-    0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
-    0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
-    0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
-    0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
-    0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
-    0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
-    0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
-    0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
-    0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
-    0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
-};
-
-/* Update ECC parity count.  */
-uint8_t ecc_digest(ECCState *s, uint8_t sample)
-{
-    uint8_t idx = nand_ecc_precalc_table[sample];
-
-    s->cp ^= idx & 0x3f;
-    if (idx & 0x40) {
-        s->lp[0] ^= ~s->count;
-        s->lp[1] ^= s->count;
-    }
-    s->count ++;
-
-    return sample;
-}
-
-/* Reinitialise the counters.  */
-void ecc_reset(ECCState *s)
-{
-    s->lp[0] = 0x0000;
-    s->lp[1] = 0x0000;
-    s->cp = 0x00;
-    s->count = 0;
-}
-
-/* Save/restore */
-VMStateDescription vmstate_ecc_state = {
-    .name = "ecc-state",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .fields = (VMStateField []) {
-        VMSTATE_UINT8(cp, ECCState),
-        VMSTATE_UINT16_ARRAY(lp, ECCState, 2),
-        VMSTATE_UINT16(count, ECCState),
-        VMSTATE_END_OF_LIST(),
-    },
-};
diff --git a/hw/eepro100.c b/hw/eepro100.c
deleted file mode 100644 (file)
index dc99ea6..0000000
+++ /dev/null
@@ -1,2115 +0,0 @@
-/*
- * QEMU i8255x (PRO100) emulation
- *
- * Copyright (C) 2006-2011 Stefan Weil
- *
- * Portions of the code are copies from grub / etherboot eepro100.c
- * and linux e100.c.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) version 3 or any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * Tested features (i82559):
- *      PXE boot (i386 guest, i386 / mips / mipsel / ppc host) ok
- *      Linux networking (i386) ok
- *
- * Untested:
- *      Windows networking
- *
- * References:
- *
- * Intel 8255x 10/100 Mbps Ethernet Controller Family
- * Open Source Software Developer Manual
- *
- * TODO:
- *      * PHY emulation should be separated from nic emulation.
- *        Most nic emulations could share the same phy code.
- *      * i82550 is untested. It is programmed like the i82559.
- *      * i82562 is untested. It is programmed like the i82559.
- *      * Power management (i82558 and later) is not implemented.
- *      * Wake-on-LAN is not implemented.
- */
-
-#include <stddef.h>             /* offsetof */
-#include "hw/hw.h"
-#include "hw/pci/pci.h"
-#include "net/net.h"
-#include "hw/nvram/eeprom93xx.h"
-#include "sysemu/sysemu.h"
-#include "sysemu/dma.h"
-
-/* QEMU sends frames smaller than 60 bytes to ethernet nics.
- * Such frames are rejected by real nics and their emulations.
- * To avoid this behaviour, other nic emulations pad received
- * frames. The following definition enables this padding for
- * eepro100, too. We keep the define around in case it might
- * become useful the future if the core networking is ever
- * changed to pad short packets itself. */
-#define CONFIG_PAD_RECEIVED_FRAMES
-
-#define KiB 1024
-
-/* Debug EEPRO100 card. */
-#if 0
-# define DEBUG_EEPRO100
-#endif
-
-#ifdef DEBUG_EEPRO100
-#define logout(fmt, ...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ## __VA_ARGS__)
-#else
-#define logout(fmt, ...) ((void)0)
-#endif
-
-/* Set flags to 0 to disable debug output. */
-#define INT     1       /* interrupt related actions */
-#define MDI     1       /* mdi related actions */
-#define OTHER   1
-#define RXTX    1
-#define EEPROM  1       /* eeprom related actions */
-
-#define TRACE(flag, command) ((flag) ? (command) : (void)0)
-
-#define missing(text) fprintf(stderr, "eepro100: feature is missing in this emulation: " text "\n")
-
-#define MAX_ETH_FRAME_SIZE 1514
-
-/* This driver supports several different devices which are declared here. */
-#define i82550          0x82550
-#define i82551          0x82551
-#define i82557A         0x82557a
-#define i82557B         0x82557b
-#define i82557C         0x82557c
-#define i82558A         0x82558a
-#define i82558B         0x82558b
-#define i82559A         0x82559a
-#define i82559B         0x82559b
-#define i82559C         0x82559c
-#define i82559ER        0x82559e
-#define i82562          0x82562
-#define i82801          0x82801
-
-/* Use 64 word EEPROM. TODO: could be a runtime option. */
-#define EEPROM_SIZE     64
-
-#define PCI_MEM_SIZE            (4 * KiB)
-#define PCI_IO_SIZE             64
-#define PCI_FLASH_SIZE          (128 * KiB)
-
-#define BIT(n) (1 << (n))
-#define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m)
-
-/* The SCB accepts the following controls for the Tx and Rx units: */
-#define  CU_NOP         0x0000  /* No operation. */
-#define  CU_START       0x0010  /* CU start. */
-#define  CU_RESUME      0x0020  /* CU resume. */
-#define  CU_STATSADDR   0x0040  /* Load dump counters address. */
-#define  CU_SHOWSTATS   0x0050  /* Dump statistical counters. */
-#define  CU_CMD_BASE    0x0060  /* Load CU base address. */
-#define  CU_DUMPSTATS   0x0070  /* Dump and reset statistical counters. */
-#define  CU_SRESUME     0x00a0  /* CU static resume. */
-
-#define  RU_NOP         0x0000
-#define  RX_START       0x0001
-#define  RX_RESUME      0x0002
-#define  RU_ABORT       0x0004
-#define  RX_ADDR_LOAD   0x0006
-#define  RX_RESUMENR    0x0007
-#define INT_MASK        0x0100
-#define DRVR_INT        0x0200  /* Driver generated interrupt. */
-
-typedef struct {
-    const char *name;
-    const char *desc;
-    uint16_t device_id;
-    uint8_t revision;
-    uint16_t subsystem_vendor_id;
-    uint16_t subsystem_id;
-
-    uint32_t device;
-    uint8_t stats_size;
-    bool has_extended_tcb_support;
-    bool power_management;
-} E100PCIDeviceInfo;
-
-/* Offsets to the various registers.
-   All accesses need not be longword aligned. */
-typedef enum {
-    SCBStatus = 0,              /* Status Word. */
-    SCBAck = 1,
-    SCBCmd = 2,                 /* Rx/Command Unit command and status. */
-    SCBIntmask = 3,
-    SCBPointer = 4,             /* General purpose pointer. */
-    SCBPort = 8,                /* Misc. commands and operands.  */
-    SCBflash = 12,              /* Flash memory control. */
-    SCBeeprom = 14,             /* EEPROM control. */
-    SCBCtrlMDI = 16,            /* MDI interface control. */
-    SCBEarlyRx = 20,            /* Early receive byte count. */
-    SCBFlow = 24,               /* Flow Control. */
-    SCBpmdr = 27,               /* Power Management Driver. */
-    SCBgctrl = 28,              /* General Control. */
-    SCBgstat = 29,              /* General Status. */
-} E100RegisterOffset;
-
-/* A speedo3 transmit buffer descriptor with two buffers... */
-typedef struct {
-    uint16_t status;
-    uint16_t command;
-    uint32_t link;              /* void * */
-    uint32_t tbd_array_addr;    /* transmit buffer descriptor array address. */
-    uint16_t tcb_bytes;         /* transmit command block byte count (in lower 14 bits */
-    uint8_t tx_threshold;       /* transmit threshold */
-    uint8_t tbd_count;          /* TBD number */
-#if 0
-    /* This constitutes two "TBD" entries: hdr and data */
-    uint32_t tx_buf_addr0;  /* void *, header of frame to be transmitted.  */
-    int32_t  tx_buf_size0;  /* Length of Tx hdr. */
-    uint32_t tx_buf_addr1;  /* void *, data to be transmitted.  */
-    int32_t  tx_buf_size1;  /* Length of Tx data. */
-#endif
-} eepro100_tx_t;
-
-/* Receive frame descriptor. */
-typedef struct {
-    int16_t status;
-    uint16_t command;
-    uint32_t link;              /* struct RxFD * */
-    uint32_t rx_buf_addr;       /* void * */
-    uint16_t count;
-    uint16_t size;
-    /* Ethernet frame data follows. */
-} eepro100_rx_t;
-
-typedef enum {
-    COMMAND_EL = BIT(15),
-    COMMAND_S = BIT(14),
-    COMMAND_I = BIT(13),
-    COMMAND_NC = BIT(4),
-    COMMAND_SF = BIT(3),
-    COMMAND_CMD = BITS(2, 0),
-} scb_command_bit;
-
-typedef enum {
-    STATUS_C = BIT(15),
-    STATUS_OK = BIT(13),
-} scb_status_bit;
-
-typedef struct {
-    uint32_t tx_good_frames, tx_max_collisions, tx_late_collisions,
-             tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions,
-             tx_multiple_collisions, tx_total_collisions;
-    uint32_t rx_good_frames, rx_crc_errors, rx_alignment_errors,
-             rx_resource_errors, rx_overrun_errors, rx_cdt_errors,
-             rx_short_frame_errors;
-    uint32_t fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported;
-    uint16_t xmt_tco_frames, rcv_tco_frames;
-    /* TODO: i82559 has six reserved statistics but a total of 24 dwords. */
-    uint32_t reserved[4];
-} eepro100_stats_t;
-
-typedef enum {
-    cu_idle = 0,
-    cu_suspended = 1,
-    cu_active = 2,
-    cu_lpq_active = 2,
-    cu_hqp_active = 3
-} cu_state_t;
-
-typedef enum {
-    ru_idle = 0,
-    ru_suspended = 1,
-    ru_no_resources = 2,
-    ru_ready = 4
-} ru_state_t;
-
-typedef struct {
-    PCIDevice dev;
-    /* Hash register (multicast mask array, multiple individual addresses). */
-    uint8_t mult[8];
-    MemoryRegion mmio_bar;
-    MemoryRegion io_bar;
-    MemoryRegion flash_bar;
-    NICState *nic;
-    NICConf conf;
-    uint8_t scb_stat;           /* SCB stat/ack byte */
-    uint8_t int_stat;           /* PCI interrupt status */
-    /* region must not be saved by nic_save. */
-    uint16_t mdimem[32];
-    eeprom_t *eeprom;
-    uint32_t device;            /* device variant */
-    /* (cu_base + cu_offset) address the next command block in the command block list. */
-    uint32_t cu_base;           /* CU base address */
-    uint32_t cu_offset;         /* CU address offset */
-    /* (ru_base + ru_offset) address the RFD in the Receive Frame Area. */
-    uint32_t ru_base;           /* RU base address */
-    uint32_t ru_offset;         /* RU address offset */
-    uint32_t statsaddr;         /* pointer to eepro100_stats_t */
-
-    /* Temporary status information (no need to save these values),
-     * used while processing CU commands. */
-    eepro100_tx_t tx;           /* transmit buffer descriptor */
-    uint32_t cb_address;        /* = cu_base + cu_offset */
-
-    /* Statistical counters. Also used for wake-up packet (i82559). */
-    eepro100_stats_t statistics;
-
-    /* Data in mem is always in the byte order of the controller (le).
-     * It must be dword aligned to allow direct access to 32 bit values. */
-    uint8_t mem[PCI_MEM_SIZE] __attribute__((aligned(8)));
-
-    /* Configuration bytes. */
-    uint8_t configuration[22];
-
-    /* vmstate for each particular nic */
-    VMStateDescription *vmstate;
-
-    /* Quasi static device properties (no need to save them). */
-    uint16_t stats_size;
-    bool has_extended_tcb_support;
-} EEPRO100State;
-
-/* Word indices in EEPROM. */
-typedef enum {
-    EEPROM_CNFG_MDIX  = 0x03,
-    EEPROM_ID         = 0x05,
-    EEPROM_PHY_ID     = 0x06,
-    EEPROM_VENDOR_ID  = 0x0c,
-    EEPROM_CONFIG_ASF = 0x0d,
-    EEPROM_DEVICE_ID  = 0x23,
-    EEPROM_SMBUS_ADDR = 0x90,
-} EEPROMOffset;
-
-/* Bit values for EEPROM ID word. */
-typedef enum {
-    EEPROM_ID_MDM = BIT(0),     /* Modem */
-    EEPROM_ID_STB = BIT(1),     /* Standby Enable */
-    EEPROM_ID_WMR = BIT(2),     /* ??? */
-    EEPROM_ID_WOL = BIT(5),     /* Wake on LAN */
-    EEPROM_ID_DPD = BIT(6),     /* Deep Power Down */
-    EEPROM_ID_ALT = BIT(7),     /* */
-    /* BITS(10, 8) device revision */
-    EEPROM_ID_BD = BIT(11),     /* boot disable */
-    EEPROM_ID_ID = BIT(13),     /* id bit */
-    /* BITS(15, 14) signature */
-    EEPROM_ID_VALID = BIT(14),  /* signature for valid eeprom */
-} eeprom_id_bit;
-
-/* Default values for MDI (PHY) registers */
-static const uint16_t eepro100_mdi_default[] = {
-    /* MDI Registers 0 - 6, 7 */
-    0x3000, 0x780d, 0x02a8, 0x0154, 0x05e1, 0x0000, 0x0000, 0x0000,
-    /* MDI Registers 8 - 15 */
-    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-    /* MDI Registers 16 - 31 */
-    0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-};
-
-/* Readonly mask for MDI (PHY) registers */
-static const uint16_t eepro100_mdi_mask[] = {
-    0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000,
-    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-    0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
-    0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
-};
-
-#define POLYNOMIAL 0x04c11db6
-
-static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s);
-
-/* From FreeBSD (locally modified). */
-static unsigned e100_compute_mcast_idx(const uint8_t *ep)
-{
-    uint32_t crc;
-    int carry, i, j;
-    uint8_t b;
-
-    crc = 0xffffffff;
-    for (i = 0; i < 6; i++) {
-        b = *ep++;
-        for (j = 0; j < 8; j++) {
-            carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
-            crc <<= 1;
-            b >>= 1;
-            if (carry) {
-                crc = ((crc ^ POLYNOMIAL) | carry);
-            }
-        }
-    }
-    return (crc & BITS(7, 2)) >> 2;
-}
-
-/* Read a 16 bit control/status (CSR) register. */
-static uint16_t e100_read_reg2(EEPRO100State *s, E100RegisterOffset addr)
-{
-    assert(!((uintptr_t)&s->mem[addr] & 1));
-    return le16_to_cpup((uint16_t *)&s->mem[addr]);
-}
-
-/* Read a 32 bit control/status (CSR) register. */
-static uint32_t e100_read_reg4(EEPRO100State *s, E100RegisterOffset addr)
-{
-    assert(!((uintptr_t)&s->mem[addr] & 3));
-    return le32_to_cpup((uint32_t *)&s->mem[addr]);
-}
-
-/* Write a 16 bit control/status (CSR) register. */
-static void e100_write_reg2(EEPRO100State *s, E100RegisterOffset addr,
-                            uint16_t val)
-{
-    assert(!((uintptr_t)&s->mem[addr] & 1));
-    cpu_to_le16w((uint16_t *)&s->mem[addr], val);
-}
-
-/* Read a 32 bit control/status (CSR) register. */
-static void e100_write_reg4(EEPRO100State *s, E100RegisterOffset addr,
-                            uint32_t val)
-{
-    assert(!((uintptr_t)&s->mem[addr] & 3));
-    cpu_to_le32w((uint32_t *)&s->mem[addr], val);
-}
-
-#if defined(DEBUG_EEPRO100)
-static const char *nic_dump(const uint8_t * buf, unsigned size)
-{
-    static char dump[3 * 16 + 1];
-    char *p = &dump[0];
-    if (size > 16) {
-        size = 16;
-    }
-    while (size-- > 0) {
-        p += sprintf(p, " %02x", *buf++);
-    }
-    return dump;
-}
-#endif                          /* DEBUG_EEPRO100 */
-
-enum scb_stat_ack {
-    stat_ack_not_ours = 0x00,
-    stat_ack_sw_gen = 0x04,
-    stat_ack_rnr = 0x10,
-    stat_ack_cu_idle = 0x20,
-    stat_ack_frame_rx = 0x40,
-    stat_ack_cu_cmd_done = 0x80,
-    stat_ack_not_present = 0xFF,
-    stat_ack_rx = (stat_ack_sw_gen | stat_ack_rnr | stat_ack_frame_rx),
-    stat_ack_tx = (stat_ack_cu_idle | stat_ack_cu_cmd_done),
-};
-
-static void disable_interrupt(EEPRO100State * s)
-{
-    if (s->int_stat) {
-        TRACE(INT, logout("interrupt disabled\n"));
-        qemu_irq_lower(s->dev.irq[0]);
-        s->int_stat = 0;
-    }
-}
-
-static void enable_interrupt(EEPRO100State * s)
-{
-    if (!s->int_stat) {
-        TRACE(INT, logout("interrupt enabled\n"));
-        qemu_irq_raise(s->dev.irq[0]);
-        s->int_stat = 1;
-    }
-}
-
-static void eepro100_acknowledge(EEPRO100State * s)
-{
-    s->scb_stat &= ~s->mem[SCBAck];
-    s->mem[SCBAck] = s->scb_stat;
-    if (s->scb_stat == 0) {
-        disable_interrupt(s);
-    }
-}
-
-static void eepro100_interrupt(EEPRO100State * s, uint8_t status)
-{
-    uint8_t mask = ~s->mem[SCBIntmask];
-    s->mem[SCBAck] |= status;
-    status = s->scb_stat = s->mem[SCBAck];
-    status &= (mask | 0x0f);
-#if 0
-    status &= (~s->mem[SCBIntmask] | 0x0xf);
-#endif
-    if (status && (mask & 0x01)) {
-        /* SCB mask and SCB Bit M do not disable interrupt. */
-        enable_interrupt(s);
-    } else if (s->int_stat) {
-        disable_interrupt(s);
-    }
-}
-
-static void eepro100_cx_interrupt(EEPRO100State * s)
-{
-    /* CU completed action command. */
-    /* Transmit not ok (82557 only, not in emulation). */
-    eepro100_interrupt(s, 0x80);
-}
-
-static void eepro100_cna_interrupt(EEPRO100State * s)
-{
-    /* CU left the active state. */
-    eepro100_interrupt(s, 0x20);
-}
-
-static void eepro100_fr_interrupt(EEPRO100State * s)
-{
-    /* RU received a complete frame. */
-    eepro100_interrupt(s, 0x40);
-}
-
-static void eepro100_rnr_interrupt(EEPRO100State * s)
-{
-    /* RU is not ready. */
-    eepro100_interrupt(s, 0x10);
-}
-
-static void eepro100_mdi_interrupt(EEPRO100State * s)
-{
-    /* MDI completed read or write cycle. */
-    eepro100_interrupt(s, 0x08);
-}
-
-static void eepro100_swi_interrupt(EEPRO100State * s)
-{
-    /* Software has requested an interrupt. */
-    eepro100_interrupt(s, 0x04);
-}
-
-#if 0
-static void eepro100_fcp_interrupt(EEPRO100State * s)
-{
-    /* Flow control pause interrupt (82558 and later). */
-    eepro100_interrupt(s, 0x01);
-}
-#endif
-
-static void e100_pci_reset(EEPRO100State * s)
-{
-    E100PCIDeviceInfo *info = eepro100_get_class(s);
-    uint32_t device = s->device;
-    uint8_t *pci_conf = s->dev.config;
-
-    TRACE(OTHER, logout("%p\n", s));
-
-    /* PCI Status */
-    pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM |
-                                        PCI_STATUS_FAST_BACK);
-    /* PCI Latency Timer */
-    pci_set_byte(pci_conf + PCI_LATENCY_TIMER, 0x20);   /* latency timer = 32 clocks */
-    /* Capability Pointer is set by PCI framework. */
-    /* Interrupt Line */
-    /* Interrupt Pin */
-    pci_set_byte(pci_conf + PCI_INTERRUPT_PIN, 1);      /* interrupt pin A */
-    /* Minimum Grant */
-    pci_set_byte(pci_conf + PCI_MIN_GNT, 0x08);
-    /* Maximum Latency */
-    pci_set_byte(pci_conf + PCI_MAX_LAT, 0x18);
-
-    s->stats_size = info->stats_size;
-    s->has_extended_tcb_support = info->has_extended_tcb_support;
-
-    switch (device) {
-    case i82550:
-    case i82551:
-    case i82557A:
-    case i82557B:
-    case i82557C:
-    case i82558A:
-    case i82558B:
-    case i82559A:
-    case i82559B:
-    case i82559ER:
-    case i82562:
-    case i82801:
-    case i82559C:
-        break;
-    default:
-        logout("Device %X is undefined!\n", device);
-    }
-
-    /* Standard TxCB. */
-    s->configuration[6] |= BIT(4);
-
-    /* Standard statistical counters. */
-    s->configuration[6] |= BIT(5);
-
-    if (s->stats_size == 80) {
-        /* TODO: check TCO Statistical Counters bit. Documentation not clear. */
-        if (s->configuration[6] & BIT(2)) {
-            /* TCO statistical counters. */
-            assert(s->configuration[6] & BIT(5));
-        } else {
-            if (s->configuration[6] & BIT(5)) {
-                /* No extended statistical counters, i82557 compatible. */
-                s->stats_size = 64;
-            } else {
-                /* i82558 compatible. */
-                s->stats_size = 76;
-            }
-        }
-    } else {
-        if (s->configuration[6] & BIT(5)) {
-            /* No extended statistical counters. */
-            s->stats_size = 64;
-        }
-    }
-    assert(s->stats_size > 0 && s->stats_size <= sizeof(s->statistics));
-
-    if (info->power_management) {
-        /* Power Management Capabilities */
-        int cfg_offset = 0xdc;
-        int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM,
-                                   cfg_offset, PCI_PM_SIZEOF);
-        assert(r >= 0);
-        pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21);
-#if 0 /* TODO: replace dummy code for power management emulation. */
-        /* TODO: Power Management Control / Status. */
-        pci_set_word(pci_conf + cfg_offset + PCI_PM_CTRL, 0x0000);
-        /* TODO: Ethernet Power Consumption Registers (i82559 and later). */
-        pci_set_byte(pci_conf + cfg_offset + PCI_PM_PPB_EXTENSIONS, 0x0000);
-#endif
-    }
-
-#if EEPROM_SIZE > 0
-    if (device == i82557C || device == i82558B || device == i82559C) {
-        /*
-        TODO: get vendor id from EEPROM for i82557C or later.
-        TODO: get device id from EEPROM for i82557C or later.
-        TODO: status bit 4 can be disabled by EEPROM for i82558, i82559.
-        TODO: header type is determined by EEPROM for i82559.
-        TODO: get subsystem id from EEPROM for i82557C or later.
-        TODO: get subsystem vendor id from EEPROM for i82557C or later.
-        TODO: exp. rom baddr depends on a bit in EEPROM for i82558 or later.
-        TODO: capability pointer depends on EEPROM for i82558.
-        */
-        logout("Get device id and revision from EEPROM!!!\n");
-    }
-#endif /* EEPROM_SIZE > 0 */
-}
-
-static void nic_selective_reset(EEPRO100State * s)
-{
-    size_t i;
-    uint16_t *eeprom_contents = eeprom93xx_data(s->eeprom);
-#if 0
-    eeprom93xx_reset(s->eeprom);
-#endif
-    memcpy(eeprom_contents, s->conf.macaddr.a, 6);
-    eeprom_contents[EEPROM_ID] = EEPROM_ID_VALID;
-    if (s->device == i82557B || s->device == i82557C)
-        eeprom_contents[5] = 0x0100;
-    eeprom_contents[EEPROM_PHY_ID] = 1;
-    uint16_t sum = 0;
-    for (i = 0; i < EEPROM_SIZE - 1; i++) {
-        sum += eeprom_contents[i];
-    }
-    eeprom_contents[EEPROM_SIZE - 1] = 0xbaba - sum;
-    TRACE(EEPROM, logout("checksum=0x%04x\n", eeprom_contents[EEPROM_SIZE - 1]));
-
-    memset(s->mem, 0, sizeof(s->mem));
-    e100_write_reg4(s, SCBCtrlMDI, BIT(21));
-
-    assert(sizeof(s->mdimem) == sizeof(eepro100_mdi_default));
-    memcpy(&s->mdimem[0], &eepro100_mdi_default[0], sizeof(s->mdimem));
-}
-
-static void nic_reset(void *opaque)
-{
-    EEPRO100State *s = opaque;
-    TRACE(OTHER, logout("%p\n", s));
-    /* TODO: Clearing of hash register for selective reset, too? */
-    memset(&s->mult[0], 0, sizeof(s->mult));
-    nic_selective_reset(s);
-}
-
-#if defined(DEBUG_EEPRO100)
-static const char * const e100_reg[PCI_IO_SIZE / 4] = {
-    "Command/Status",
-    "General Pointer",
-    "Port",
-    "EEPROM/Flash Control",
-    "MDI Control",
-    "Receive DMA Byte Count",
-    "Flow Control",
-    "General Status/Control"
-};
-
-static char *regname(uint32_t addr)
-{
-    static char buf[32];
-    if (addr < PCI_IO_SIZE) {
-        const char *r = e100_reg[addr / 4];
-        if (r != 0) {
-            snprintf(buf, sizeof(buf), "%s+%u", r, addr % 4);
-        } else {
-            snprintf(buf, sizeof(buf), "0x%02x", addr);
-        }
-    } else {
-        snprintf(buf, sizeof(buf), "??? 0x%08x", addr);
-    }
-    return buf;
-}
-#endif                          /* DEBUG_EEPRO100 */
-
-/*****************************************************************************
- *
- * Command emulation.
- *
- ****************************************************************************/
-
-#if 0
-static uint16_t eepro100_read_command(EEPRO100State * s)
-{
-    uint16_t val = 0xffff;
-    TRACE(OTHER, logout("val=0x%04x\n", val));
-    return val;
-}
-#endif
-
-/* Commands that can be put in a command list entry. */
-enum commands {
-    CmdNOp = 0,
-    CmdIASetup = 1,
-    CmdConfigure = 2,
-    CmdMulticastList = 3,
-    CmdTx = 4,
-    CmdTDR = 5,                 /* load microcode */
-    CmdDump = 6,
-    CmdDiagnose = 7,
-
-    /* And some extra flags: */
-    CmdSuspend = 0x4000,        /* Suspend after completion. */
-    CmdIntr = 0x2000,           /* Interrupt after completion. */
-    CmdTxFlex = 0x0008,         /* Use "Flexible mode" for CmdTx command. */
-};
-
-static cu_state_t get_cu_state(EEPRO100State * s)
-{
-    return ((s->mem[SCBStatus] & BITS(7, 6)) >> 6);
-}
-
-static void set_cu_state(EEPRO100State * s, cu_state_t state)
-{
-    s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(7, 6)) + (state << 6);
-}
-
-static ru_state_t get_ru_state(EEPRO100State * s)
-{
-    return ((s->mem[SCBStatus] & BITS(5, 2)) >> 2);
-}
-
-static void set_ru_state(EEPRO100State * s, ru_state_t state)
-{
-    s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(5, 2)) + (state << 2);
-}
-
-static void dump_statistics(EEPRO100State * s)
-{
-    /* Dump statistical data. Most data is never changed by the emulation
-     * and always 0, so we first just copy the whole block and then those
-     * values which really matter.
-     * Number of data should check configuration!!!
-     */
-    pci_dma_write(&s->dev, s->statsaddr, &s->statistics, s->stats_size);
-    stl_le_pci_dma(&s->dev, s->statsaddr + 0,
-                   s->statistics.tx_good_frames);
-    stl_le_pci_dma(&s->dev, s->statsaddr + 36,
-                   s->statistics.rx_good_frames);
-    stl_le_pci_dma(&s->dev, s->statsaddr + 48,
-                   s->statistics.rx_resource_errors);
-    stl_le_pci_dma(&s->dev, s->statsaddr + 60,
-                   s->statistics.rx_short_frame_errors);
-#if 0
-    stw_le_pci_dma(&s->dev, s->statsaddr + 76, s->statistics.xmt_tco_frames);
-    stw_le_pci_dma(&s->dev, s->statsaddr + 78, s->statistics.rcv_tco_frames);
-    missing("CU dump statistical counters");
-#endif
-}
-
-static void read_cb(EEPRO100State *s)
-{
-    pci_dma_read(&s->dev, s->cb_address, &s->tx, sizeof(s->tx));
-    s->tx.status = le16_to_cpu(s->tx.status);
-    s->tx.command = le16_to_cpu(s->tx.command);
-    s->tx.link = le32_to_cpu(s->tx.link);
-    s->tx.tbd_array_addr = le32_to_cpu(s->tx.tbd_array_addr);
-    s->tx.tcb_bytes = le16_to_cpu(s->tx.tcb_bytes);
-}
-
-static void tx_command(EEPRO100State *s)
-{
-    uint32_t tbd_array = le32_to_cpu(s->tx.tbd_array_addr);
-    uint16_t tcb_bytes = (le16_to_cpu(s->tx.tcb_bytes) & 0x3fff);
-    /* Sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes. */
-    uint8_t buf[2600];
-    uint16_t size = 0;
-    uint32_t tbd_address = s->cb_address + 0x10;
-    TRACE(RXTX, logout
-        ("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n",
-         tbd_array, tcb_bytes, s->tx.tbd_count));
-
-    if (tcb_bytes > 2600) {
-        logout("TCB byte count too large, using 2600\n");
-        tcb_bytes = 2600;
-    }
-    if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) {
-        logout
-            ("illegal values of TBD array address and TCB byte count!\n");
-    }
-    assert(tcb_bytes <= sizeof(buf));
-    while (size < tcb_bytes) {
-        uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address);
-        uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4);
-#if 0
-        uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6);
-#endif
-        tbd_address += 8;
-        TRACE(RXTX, logout
-            ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n",
-             tx_buffer_address, tx_buffer_size));
-        tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
-        pci_dma_read(&s->dev, tx_buffer_address, &buf[size], tx_buffer_size);
-        size += tx_buffer_size;
-    }
-    if (tbd_array == 0xffffffff) {
-        /* Simplified mode. Was already handled by code above. */
-    } else {
-        /* Flexible mode. */
-        uint8_t tbd_count = 0;
-        if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) {
-            /* Extended Flexible TCB. */
-            for (; tbd_count < 2; tbd_count++) {
-                uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev,
-                                                            tbd_address);
-                uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev,
-                                                          tbd_address + 4);
-                uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev,
-                                                        tbd_address + 6);
-                tbd_address += 8;
-                TRACE(RXTX, logout
-                    ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n",
-                     tx_buffer_address, tx_buffer_size));
-                tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
-                pci_dma_read(&s->dev, tx_buffer_address,
-                             &buf[size], tx_buffer_size);
-                size += tx_buffer_size;
-                if (tx_buffer_el & 1) {
-                    break;
-                }
-            }
-        }
-        tbd_address = tbd_array;
-        for (; tbd_count < s->tx.tbd_count; tbd_count++) {
-            uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address);
-            uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4);
-            uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6);
-            tbd_address += 8;
-            TRACE(RXTX, logout
-                ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n",
-                 tx_buffer_address, tx_buffer_size));
-            tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
-            pci_dma_read(&s->dev, tx_buffer_address,
-                         &buf[size], tx_buffer_size);
-            size += tx_buffer_size;
-            if (tx_buffer_el & 1) {
-                break;
-            }
-        }
-    }
-    TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size)));
-    qemu_send_packet(qemu_get_queue(s->nic), buf, size);
-    s->statistics.tx_good_frames++;
-    /* Transmit with bad status would raise an CX/TNO interrupt.
-     * (82557 only). Emulation never has bad status. */
-#if 0
-    eepro100_cx_interrupt(s);
-#endif
-}
-
-static void set_multicast_list(EEPRO100State *s)
-{
-    uint16_t multicast_count = s->tx.tbd_array_addr & BITS(13, 0);
-    uint16_t i;
-    memset(&s->mult[0], 0, sizeof(s->mult));
-    TRACE(OTHER, logout("multicast list, multicast count = %u\n", multicast_count));
-    for (i = 0; i < multicast_count; i += 6) {
-        uint8_t multicast_addr[6];
-        pci_dma_read(&s->dev, s->cb_address + 10 + i, multicast_addr, 6);
-        TRACE(OTHER, logout("multicast entry %s\n", nic_dump(multicast_addr, 6)));
-        unsigned mcast_idx = e100_compute_mcast_idx(multicast_addr);
-        assert(mcast_idx < 64);
-        s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7));
-    }
-}
-
-static void action_command(EEPRO100State *s)
-{
-    for (;;) {
-        bool bit_el;
-        bool bit_s;
-        bool bit_i;
-        bool bit_nc;
-        uint16_t ok_status = STATUS_OK;
-        s->cb_address = s->cu_base + s->cu_offset;
-        read_cb(s);
-        bit_el = ((s->tx.command & COMMAND_EL) != 0);
-        bit_s = ((s->tx.command & COMMAND_S) != 0);
-        bit_i = ((s->tx.command & COMMAND_I) != 0);
-        bit_nc = ((s->tx.command & COMMAND_NC) != 0);
-#if 0
-        bool bit_sf = ((s->tx.command & COMMAND_SF) != 0);
-#endif
-        s->cu_offset = s->tx.link;
-        TRACE(OTHER,
-              logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n",
-                     s->tx.status, s->tx.command, s->tx.link));
-        switch (s->tx.command & COMMAND_CMD) {
-        case CmdNOp:
-            /* Do nothing. */
-            break;
-        case CmdIASetup:
-            pci_dma_read(&s->dev, s->cb_address + 8, &s->conf.macaddr.a[0], 6);
-            TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6)));
-            break;
-        case CmdConfigure:
-            pci_dma_read(&s->dev, s->cb_address + 8,
-                         &s->configuration[0], sizeof(s->configuration));
-            TRACE(OTHER, logout("configuration: %s\n",
-                                nic_dump(&s->configuration[0], 16)));
-            TRACE(OTHER, logout("configuration: %s\n",
-                                nic_dump(&s->configuration[16],
-                                ARRAY_SIZE(s->configuration) - 16)));
-            if (s->configuration[20] & BIT(6)) {
-                TRACE(OTHER, logout("Multiple IA bit\n"));
-            }
-            break;
-        case CmdMulticastList:
-            set_multicast_list(s);
-            break;
-        case CmdTx:
-            if (bit_nc) {
-                missing("CmdTx: NC = 0");
-                ok_status = 0;
-                break;
-            }
-            tx_command(s);
-            break;
-        case CmdTDR:
-            TRACE(OTHER, logout("load microcode\n"));
-            /* Starting with offset 8, the command contains
-             * 64 dwords microcode which we just ignore here. */
-            break;
-        case CmdDiagnose:
-            TRACE(OTHER, logout("diagnose\n"));
-            /* Make sure error flag is not set. */
-            s->tx.status = 0;
-            break;
-        default:
-            missing("undefined command");
-            ok_status = 0;
-            break;
-        }
-        /* Write new status. */
-        stw_le_pci_dma(&s->dev, s->cb_address,
-                       s->tx.status | ok_status | STATUS_C);
-        if (bit_i) {
-            /* CU completed action. */
-            eepro100_cx_interrupt(s);
-        }
-        if (bit_el) {
-            /* CU becomes idle. Terminate command loop. */
-            set_cu_state(s, cu_idle);
-            eepro100_cna_interrupt(s);
-            break;
-        } else if (bit_s) {
-            /* CU becomes suspended. Terminate command loop. */
-            set_cu_state(s, cu_suspended);
-            eepro100_cna_interrupt(s);
-            break;
-        } else {
-            /* More entries in list. */
-            TRACE(OTHER, logout("CU list with at least one more entry\n"));
-        }
-    }
-    TRACE(OTHER, logout("CU list empty\n"));
-    /* List is empty. Now CU is idle or suspended. */
-}
-
-static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
-{
-    cu_state_t cu_state;
-    switch (val) {
-    case CU_NOP:
-        /* No operation. */
-        break;
-    case CU_START:
-        cu_state = get_cu_state(s);
-        if (cu_state != cu_idle && cu_state != cu_suspended) {
-            /* Intel documentation says that CU must be idle or suspended
-             * for the CU start command. */
-            logout("unexpected CU state is %u\n", cu_state);
-        }
-        set_cu_state(s, cu_active);
-        s->cu_offset = e100_read_reg4(s, SCBPointer);
-        action_command(s);
-        break;
-    case CU_RESUME:
-        if (get_cu_state(s) != cu_suspended) {
-            logout("bad CU resume from CU state %u\n", get_cu_state(s));
-            /* Workaround for bad Linux eepro100 driver which resumes
-             * from idle state. */
-#if 0
-            missing("cu resume");
-#endif
-            set_cu_state(s, cu_suspended);
-        }
-        if (get_cu_state(s) == cu_suspended) {
-            TRACE(OTHER, logout("CU resuming\n"));
-            set_cu_state(s, cu_active);
-            action_command(s);
-        }
-        break;
-    case CU_STATSADDR:
-        /* Load dump counters address. */
-        s->statsaddr = e100_read_reg4(s, SCBPointer);
-        TRACE(OTHER, logout("val=0x%02x (dump counters address)\n", val));
-        if (s->statsaddr & 3) {
-            /* Memory must be Dword aligned. */
-            logout("unaligned dump counters address\n");
-            /* Handling of misaligned addresses is undefined.
-             * Here we align the address by ignoring the lower bits. */
-            /* TODO: Test unaligned dump counter address on real hardware. */
-            s->statsaddr &= ~3;
-        }
-        break;
-    case CU_SHOWSTATS:
-        /* Dump statistical counters. */
-        TRACE(OTHER, logout("val=0x%02x (dump stats)\n", val));
-        dump_statistics(s);
-        stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa005);
-        break;
-    case CU_CMD_BASE:
-        /* Load CU base. */
-        TRACE(OTHER, logout("val=0x%02x (CU base address)\n", val));
-        s->cu_base = e100_read_reg4(s, SCBPointer);
-        break;
-    case CU_DUMPSTATS:
-        /* Dump and reset statistical counters. */
-        TRACE(OTHER, logout("val=0x%02x (dump stats and reset)\n", val));
-        dump_statistics(s);
-        stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa007);
-        memset(&s->statistics, 0, sizeof(s->statistics));
-        break;
-    case CU_SRESUME:
-        /* CU static resume. */
-        missing("CU static resume");
-        break;
-    default:
-        missing("Undefined CU command");
-    }
-}
-
-static void eepro100_ru_command(EEPRO100State * s, uint8_t val)
-{
-    switch (val) {
-    case RU_NOP:
-        /* No operation. */
-        break;
-    case RX_START:
-        /* RU start. */
-        if (get_ru_state(s) != ru_idle) {
-            logout("RU state is %u, should be %u\n", get_ru_state(s), ru_idle);
-#if 0
-            assert(!"wrong RU state");
-#endif
-        }
-        set_ru_state(s, ru_ready);
-        s->ru_offset = e100_read_reg4(s, SCBPointer);
-        qemu_flush_queued_packets(qemu_get_queue(s->nic));
-        TRACE(OTHER, logout("val=0x%02x (rx start)\n", val));
-        break;
-    case RX_RESUME:
-        /* Restart RU. */
-        if (get_ru_state(s) != ru_suspended) {
-            logout("RU state is %u, should be %u\n", get_ru_state(s),
-                   ru_suspended);
-#if 0
-            assert(!"wrong RU state");
-#endif
-        }
-        set_ru_state(s, ru_ready);
-        break;
-    case RU_ABORT:
-        /* RU abort. */
-        if (get_ru_state(s) == ru_ready) {
-            eepro100_rnr_interrupt(s);
-        }
-        set_ru_state(s, ru_idle);
-        break;
-    case RX_ADDR_LOAD:
-        /* Load RU base. */
-        TRACE(OTHER, logout("val=0x%02x (RU base address)\n", val));
-        s->ru_base = e100_read_reg4(s, SCBPointer);
-        break;
-    default:
-        logout("val=0x%02x (undefined RU command)\n", val);
-        missing("Undefined SU command");
-    }
-}
-
-static void eepro100_write_command(EEPRO100State * s, uint8_t val)
-{
-    eepro100_ru_command(s, val & 0x0f);
-    eepro100_cu_command(s, val & 0xf0);
-    if ((val) == 0) {
-        TRACE(OTHER, logout("val=0x%02x\n", val));
-    }
-    /* Clear command byte after command was accepted. */
-    s->mem[SCBCmd] = 0;
-}
-
-/*****************************************************************************
- *
- * EEPROM emulation.
- *
- ****************************************************************************/
-
-#define EEPROM_CS       0x02
-#define EEPROM_SK       0x01
-#define EEPROM_DI       0x04
-#define EEPROM_DO       0x08
-
-static uint16_t eepro100_read_eeprom(EEPRO100State * s)
-{
-    uint16_t val = e100_read_reg2(s, SCBeeprom);
-    if (eeprom93xx_read(s->eeprom)) {
-        val |= EEPROM_DO;
-    } else {
-        val &= ~EEPROM_DO;
-    }
-    TRACE(EEPROM, logout("val=0x%04x\n", val));
-    return val;
-}
-
-static void eepro100_write_eeprom(eeprom_t * eeprom, uint8_t val)
-{
-    TRACE(EEPROM, logout("val=0x%02x\n", val));
-
-    /* mask unwritable bits */
-#if 0
-    val = SET_MASKED(val, 0x31, eeprom->value);
-#endif
-
-    int eecs = ((val & EEPROM_CS) != 0);
-    int eesk = ((val & EEPROM_SK) != 0);
-    int eedi = ((val & EEPROM_DI) != 0);
-    eeprom93xx_write(eeprom, eecs, eesk, eedi);
-}
-
-/*****************************************************************************
- *
- * MDI emulation.
- *
- ****************************************************************************/
-
-#if defined(DEBUG_EEPRO100)
-static const char * const mdi_op_name[] = {
-    "opcode 0",
-    "write",
-    "read",
-    "opcode 3"
-};
-
-static const char * const mdi_reg_name[] = {
-    "Control",
-    "Status",
-    "PHY Identification (Word 1)",
-    "PHY Identification (Word 2)",
-    "Auto-Negotiation Advertisement",
-    "Auto-Negotiation Link Partner Ability",
-    "Auto-Negotiation Expansion"
-};
-
-static const char *reg2name(uint8_t reg)
-{
-    static char buffer[10];
-    const char *p = buffer;
-    if (reg < ARRAY_SIZE(mdi_reg_name)) {
-        p = mdi_reg_name[reg];
-    } else {
-        snprintf(buffer, sizeof(buffer), "reg=0x%02x", reg);
-    }
-    return p;
-}
-#endif                          /* DEBUG_EEPRO100 */
-
-static uint32_t eepro100_read_mdi(EEPRO100State * s)
-{
-    uint32_t val = e100_read_reg4(s, SCBCtrlMDI);
-
-#ifdef DEBUG_EEPRO100
-    uint8_t raiseint = (val & BIT(29)) >> 29;
-    uint8_t opcode = (val & BITS(27, 26)) >> 26;
-    uint8_t phy = (val & BITS(25, 21)) >> 21;
-    uint8_t reg = (val & BITS(20, 16)) >> 16;
-    uint16_t data = (val & BITS(15, 0));
-#endif
-    /* Emulation takes no time to finish MDI transaction. */
-    val |= BIT(28);
-    TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
-                      val, raiseint, mdi_op_name[opcode], phy,
-                      reg2name(reg), data));
-    return val;
-}
-
-static void eepro100_write_mdi(EEPRO100State *s)
-{
-    uint32_t val = e100_read_reg4(s, SCBCtrlMDI);
-    uint8_t raiseint = (val & BIT(29)) >> 29;
-    uint8_t opcode = (val & BITS(27, 26)) >> 26;
-    uint8_t phy = (val & BITS(25, 21)) >> 21;
-    uint8_t reg = (val & BITS(20, 16)) >> 16;
-    uint16_t data = (val & BITS(15, 0));
-    TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
-          val, raiseint, mdi_op_name[opcode], phy, reg2name(reg), data));
-    if (phy != 1) {
-        /* Unsupported PHY address. */
-#if 0
-        logout("phy must be 1 but is %u\n", phy);
-#endif
-        data = 0;
-    } else if (opcode != 1 && opcode != 2) {
-        /* Unsupported opcode. */
-        logout("opcode must be 1 or 2 but is %u\n", opcode);
-        data = 0;
-    } else if (reg > 6) {
-        /* Unsupported register. */
-        logout("register must be 0...6 but is %u\n", reg);
-        data = 0;
-    } else {
-        TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
-                          val, raiseint, mdi_op_name[opcode], phy,
-                          reg2name(reg), data));
-        if (opcode == 1) {
-            /* MDI write */
-            switch (reg) {
-            case 0:            /* Control Register */
-                if (data & 0x8000) {
-                    /* Reset status and control registers to default. */
-                    s->mdimem[0] = eepro100_mdi_default[0];
-                    s->mdimem[1] = eepro100_mdi_default[1];
-                    data = s->mdimem[reg];
-                } else {
-                    /* Restart Auto Configuration = Normal Operation */
-                    data &= ~0x0200;
-                }
-                break;
-            case 1:            /* Status Register */
-                missing("not writable");
-                data = s->mdimem[reg];
-                break;
-            case 2:            /* PHY Identification Register (Word 1) */
-            case 3:            /* PHY Identification Register (Word 2) */
-                missing("not implemented");
-                break;
-            case 4:            /* Auto-Negotiation Advertisement Register */
-            case 5:            /* Auto-Negotiation Link Partner Ability Register */
-                break;
-            case 6:            /* Auto-Negotiation Expansion Register */
-            default:
-                missing("not implemented");
-            }
-            s->mdimem[reg] = data;
-        } else if (opcode == 2) {
-            /* MDI read */
-            switch (reg) {
-            case 0:            /* Control Register */
-                if (data & 0x8000) {
-                    /* Reset status and control registers to default. */
-                    s->mdimem[0] = eepro100_mdi_default[0];
-                    s->mdimem[1] = eepro100_mdi_default[1];
-                }
-                break;
-            case 1:            /* Status Register */
-                s->mdimem[reg] |= 0x0020;
-                break;
-            case 2:            /* PHY Identification Register (Word 1) */
-            case 3:            /* PHY Identification Register (Word 2) */
-            case 4:            /* Auto-Negotiation Advertisement Register */
-                break;
-            case 5:            /* Auto-Negotiation Link Partner Ability Register */
-                s->mdimem[reg] = 0x41fe;
-                break;
-            case 6:            /* Auto-Negotiation Expansion Register */
-                s->mdimem[reg] = 0x0001;
-                break;
-            }
-            data = s->mdimem[reg];
-        }
-        /* Emulation takes no time to finish MDI transaction.
-         * Set MDI bit in SCB status register. */
-        s->mem[SCBAck] |= 0x08;
-        val |= BIT(28);
-        if (raiseint) {
-            eepro100_mdi_interrupt(s);
-        }
-    }
-    val = (val & 0xffff0000) + data;
-    e100_write_reg4(s, SCBCtrlMDI, val);
-}
-
-/*****************************************************************************
- *
- * Port emulation.
- *
- ****************************************************************************/
-
-#define PORT_SOFTWARE_RESET     0
-#define PORT_SELFTEST           1
-#define PORT_SELECTIVE_RESET    2
-#define PORT_DUMP               3
-#define PORT_SELECTION_MASK     3
-
-typedef struct {
-    uint32_t st_sign;           /* Self Test Signature */
-    uint32_t st_result;         /* Self Test Results */
-} eepro100_selftest_t;
-
-static uint32_t eepro100_read_port(EEPRO100State * s)
-{
-    return 0;
-}
-
-static void eepro100_write_port(EEPRO100State *s)
-{
-    uint32_t val = e100_read_reg4(s, SCBPort);
-    uint32_t address = (val & ~PORT_SELECTION_MASK);
-    uint8_t selection = (val & PORT_SELECTION_MASK);
-    switch (selection) {
-    case PORT_SOFTWARE_RESET:
-        nic_reset(s);
-        break;
-    case PORT_SELFTEST:
-        TRACE(OTHER, logout("selftest address=0x%08x\n", address));
-        eepro100_selftest_t data;
-        pci_dma_read(&s->dev, address, (uint8_t *) &data, sizeof(data));
-        data.st_sign = 0xffffffff;
-        data.st_result = 0;
-        pci_dma_write(&s->dev, address, (uint8_t *) &data, sizeof(data));
-        break;
-    case PORT_SELECTIVE_RESET:
-        TRACE(OTHER, logout("selective reset, selftest address=0x%08x\n", address));
-        nic_selective_reset(s);
-        break;
-    default:
-        logout("val=0x%08x\n", val);
-        missing("unknown port selection");
-    }
-}
-
-/*****************************************************************************
- *
- * General hardware emulation.
- *
- ****************************************************************************/
-
-static uint8_t eepro100_read1(EEPRO100State * s, uint32_t addr)
-{
-    uint8_t val = 0;
-    if (addr <= sizeof(s->mem) - sizeof(val)) {
-        val = s->mem[addr];
-    }
-
-    switch (addr) {
-    case SCBStatus:
-    case SCBAck:
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-        break;
-    case SCBCmd:
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-#if 0
-        val = eepro100_read_command(s);
-#endif
-        break;
-    case SCBIntmask:
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-        break;
-    case SCBPort + 3:
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-        break;
-    case SCBeeprom:
-        val = eepro100_read_eeprom(s);
-        break;
-    case SCBCtrlMDI:
-    case SCBCtrlMDI + 1:
-    case SCBCtrlMDI + 2:
-    case SCBCtrlMDI + 3:
-        val = (uint8_t)(eepro100_read_mdi(s) >> (8 * (addr & 3)));
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-        break;
-    case SCBpmdr:       /* Power Management Driver Register */
-        val = 0;
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-        break;
-    case SCBgctrl:      /* General Control Register */
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-        break;
-    case SCBgstat:      /* General Status Register */
-        /* 100 Mbps full duplex, valid link */
-        val = 0x07;
-        TRACE(OTHER, logout("addr=General Status val=%02x\n", val));
-        break;
-    default:
-        logout("addr=%s val=0x%02x\n", regname(addr), val);
-        missing("unknown byte read");
-    }
-    return val;
-}
-
-static uint16_t eepro100_read2(EEPRO100State * s, uint32_t addr)
-{
-    uint16_t val = 0;
-    if (addr <= sizeof(s->mem) - sizeof(val)) {
-        val = e100_read_reg2(s, addr);
-    }
-
-    switch (addr) {
-    case SCBStatus:
-    case SCBCmd:
-        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
-        break;
-    case SCBeeprom:
-        val = eepro100_read_eeprom(s);
-        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
-        break;
-    case SCBCtrlMDI:
-    case SCBCtrlMDI + 2:
-        val = (uint16_t)(eepro100_read_mdi(s) >> (8 * (addr & 3)));
-        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
-        break;
-    default:
-        logout("addr=%s val=0x%04x\n", regname(addr), val);
-        missing("unknown word read");
-    }
-    return val;
-}
-
-static uint32_t eepro100_read4(EEPRO100State * s, uint32_t addr)
-{
-    uint32_t val = 0;
-    if (addr <= sizeof(s->mem) - sizeof(val)) {
-        val = e100_read_reg4(s, addr);
-    }
-
-    switch (addr) {
-    case SCBStatus:
-        TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
-        break;
-    case SCBPointer:
-        TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
-        break;
-    case SCBPort:
-        val = eepro100_read_port(s);
-        TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
-        break;
-    case SCBflash:
-        val = eepro100_read_eeprom(s);
-        TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
-        break;
-    case SCBCtrlMDI:
-        val = eepro100_read_mdi(s);
-        break;
-    default:
-        logout("addr=%s val=0x%08x\n", regname(addr), val);
-        missing("unknown longword read");
-    }
-    return val;
-}
-
-static void eepro100_write1(EEPRO100State * s, uint32_t addr, uint8_t val)
-{
-    /* SCBStatus is readonly. */
-    if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
-        s->mem[addr] = val;
-    }
-
-    switch (addr) {
-    case SCBStatus:
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-        break;
-    case SCBAck:
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-        eepro100_acknowledge(s);
-        break;
-    case SCBCmd:
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-        eepro100_write_command(s, val);
-        break;
-    case SCBIntmask:
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-        if (val & BIT(1)) {
-            eepro100_swi_interrupt(s);
-        }
-        eepro100_interrupt(s, 0);
-        break;
-    case SCBPointer:
-    case SCBPointer + 1:
-    case SCBPointer + 2:
-    case SCBPointer + 3:
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-        break;
-    case SCBPort:
-    case SCBPort + 1:
-    case SCBPort + 2:
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-        break;
-    case SCBPort + 3:
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-        eepro100_write_port(s);
-        break;
-    case SCBFlow:       /* does not exist on 82557 */
-    case SCBFlow + 1:
-    case SCBFlow + 2:
-    case SCBpmdr:       /* does not exist on 82557 */
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-        break;
-    case SCBeeprom:
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-        eepro100_write_eeprom(s->eeprom, val);
-        break;
-    case SCBCtrlMDI:
-    case SCBCtrlMDI + 1:
-    case SCBCtrlMDI + 2:
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-        break;
-    case SCBCtrlMDI + 3:
-        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
-        eepro100_write_mdi(s);
-        break;
-    default:
-        logout("addr=%s val=0x%02x\n", regname(addr), val);
-        missing("unknown byte write");
-    }
-}
-
-static void eepro100_write2(EEPRO100State * s, uint32_t addr, uint16_t val)
-{
-    /* SCBStatus is readonly. */
-    if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
-        e100_write_reg2(s, addr, val);
-    }
-
-    switch (addr) {
-    case SCBStatus:
-        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
-        s->mem[SCBAck] = (val >> 8);
-        eepro100_acknowledge(s);
-        break;
-    case SCBCmd:
-        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
-        eepro100_write_command(s, val);
-        eepro100_write1(s, SCBIntmask, val >> 8);
-        break;
-    case SCBPointer:
-    case SCBPointer + 2:
-        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
-        break;
-    case SCBPort:
-        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
-        break;
-    case SCBPort + 2:
-        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
-        eepro100_write_port(s);
-        break;
-    case SCBeeprom:
-        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
-        eepro100_write_eeprom(s->eeprom, val);
-        break;
-    case SCBCtrlMDI:
-        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
-        break;
-    case SCBCtrlMDI + 2:
-        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
-        eepro100_write_mdi(s);
-        break;
-    default:
-        logout("addr=%s val=0x%04x\n", regname(addr), val);
-        missing("unknown word write");
-    }
-}
-
-static void eepro100_write4(EEPRO100State * s, uint32_t addr, uint32_t val)
-{
-    if (addr <= sizeof(s->mem) - sizeof(val)) {
-        e100_write_reg4(s, addr, val);
-    }
-
-    switch (addr) {
-    case SCBPointer:
-        TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
-        break;
-    case SCBPort:
-        TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
-        eepro100_write_port(s);
-        break;
-    case SCBflash:
-        TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
-        val = val >> 16;
-        eepro100_write_eeprom(s->eeprom, val);
-        break;
-    case SCBCtrlMDI:
-        TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
-        eepro100_write_mdi(s);
-        break;
-    default:
-        logout("addr=%s val=0x%08x\n", regname(addr), val);
-        missing("unknown longword write");
-    }
-}
-
-static uint64_t eepro100_read(void *opaque, hwaddr addr,
-                              unsigned size)
-{
-    EEPRO100State *s = opaque;
-
-    switch (size) {
-    case 1: return eepro100_read1(s, addr);
-    case 2: return eepro100_read2(s, addr);
-    case 4: return eepro100_read4(s, addr);
-    default: abort();
-    }
-}
-
-static void eepro100_write(void *opaque, hwaddr addr,
-                           uint64_t data, unsigned size)
-{
-    EEPRO100State *s = opaque;
-
-    switch (size) {
-    case 1:
-        eepro100_write1(s, addr, data);
-        break;
-    case 2:
-        eepro100_write2(s, addr, data);
-        break;
-    case 4:
-        eepro100_write4(s, addr, data);
-        break;
-    default:
-        abort();
-    }
-}
-
-static const MemoryRegionOps eepro100_ops = {
-    .read = eepro100_read,
-    .write = eepro100_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static int nic_can_receive(NetClientState *nc)
-{
-    EEPRO100State *s = qemu_get_nic_opaque(nc);
-    TRACE(RXTX, logout("%p\n", s));
-    return get_ru_state(s) == ru_ready;
-#if 0
-    return !eepro100_buffer_full(s);
-#endif
-}
-
-static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
-{
-    /* TODO:
-     * - Magic packets should set bit 30 in power management driver register.
-     * - Interesting packets should set bit 29 in power management driver register.
-     */
-    EEPRO100State *s = qemu_get_nic_opaque(nc);
-    uint16_t rfd_status = 0xa000;
-#if defined(CONFIG_PAD_RECEIVED_FRAMES)
-    uint8_t min_buf[60];
-#endif
-    static const uint8_t broadcast_macaddr[6] =
-        { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-
-#if defined(CONFIG_PAD_RECEIVED_FRAMES)
-    /* Pad to minimum Ethernet frame length */
-    if (size < sizeof(min_buf)) {
-        memcpy(min_buf, buf, size);
-        memset(&min_buf[size], 0, sizeof(min_buf) - size);
-        buf = min_buf;
-        size = sizeof(min_buf);
-    }
-#endif
-
-    if (s->configuration[8] & 0x80) {
-        /* CSMA is disabled. */
-        logout("%p received while CSMA is disabled\n", s);
-        return -1;
-#if !defined(CONFIG_PAD_RECEIVED_FRAMES)
-    } else if (size < 64 && (s->configuration[7] & BIT(0))) {
-        /* Short frame and configuration byte 7/0 (discard short receive) set:
-         * Short frame is discarded */
-        logout("%p received short frame (%zu byte)\n", s, size);
-        s->statistics.rx_short_frame_errors++;
-        return -1;
-#endif
-    } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & BIT(3))) {
-        /* Long frame and configuration byte 18/3 (long receive ok) not set:
-         * Long frames are discarded. */
-        logout("%p received long frame (%zu byte), ignored\n", s, size);
-        return -1;
-    } else if (memcmp(buf, s->conf.macaddr.a, 6) == 0) {       /* !!! */
-        /* Frame matches individual address. */
-        /* TODO: check configuration byte 15/4 (ignore U/L). */
-        TRACE(RXTX, logout("%p received frame for me, len=%zu\n", s, size));
-    } else if (memcmp(buf, broadcast_macaddr, 6) == 0) {
-        /* Broadcast frame. */
-        TRACE(RXTX, logout("%p received broadcast, len=%zu\n", s, size));
-        rfd_status |= 0x0002;
-    } else if (buf[0] & 0x01) {
-        /* Multicast frame. */
-        TRACE(RXTX, logout("%p received multicast, len=%zu,%s\n", s, size, nic_dump(buf, size)));
-        if (s->configuration[21] & BIT(3)) {
-          /* Multicast all bit is set, receive all multicast frames. */
-        } else {
-          unsigned mcast_idx = e100_compute_mcast_idx(buf);
-          assert(mcast_idx < 64);
-          if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
-            /* Multicast frame is allowed in hash table. */
-          } else if (s->configuration[15] & BIT(0)) {
-              /* Promiscuous: receive all. */
-              rfd_status |= 0x0004;
-          } else {
-              TRACE(RXTX, logout("%p multicast ignored\n", s));
-              return -1;
-          }
-        }
-        /* TODO: Next not for promiscuous mode? */
-        rfd_status |= 0x0002;
-    } else if (s->configuration[15] & BIT(0)) {
-        /* Promiscuous: receive all. */
-        TRACE(RXTX, logout("%p received frame in promiscuous mode, len=%zu\n", s, size));
-        rfd_status |= 0x0004;
-    } else if (s->configuration[20] & BIT(6)) {
-        /* Multiple IA bit set. */
-        unsigned mcast_idx = compute_mcast_idx(buf);
-        assert(mcast_idx < 64);
-        if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
-            TRACE(RXTX, logout("%p accepted, multiple IA bit set\n", s));
-        } else {
-            TRACE(RXTX, logout("%p frame ignored, multiple IA bit set\n", s));
-            return -1;
-        }
-    } else {
-        TRACE(RXTX, logout("%p received frame, ignored, len=%zu,%s\n", s, size,
-              nic_dump(buf, size)));
-        return size;
-    }
-
-    if (get_ru_state(s) != ru_ready) {
-        /* No resources available. */
-        logout("no resources, state=%u\n", get_ru_state(s));
-        /* TODO: RNR interrupt only at first failed frame? */
-        eepro100_rnr_interrupt(s);
-        s->statistics.rx_resource_errors++;
-#if 0
-        assert(!"no resources");
-#endif
-        return -1;
-    }
-    /* !!! */
-    eepro100_rx_t rx;
-    pci_dma_read(&s->dev, s->ru_base + s->ru_offset,
-                 &rx, sizeof(eepro100_rx_t));
-    uint16_t rfd_command = le16_to_cpu(rx.command);
-    uint16_t rfd_size = le16_to_cpu(rx.size);
-
-    if (size > rfd_size) {
-        logout("Receive buffer (%" PRId16 " bytes) too small for data "
-            "(%zu bytes); data truncated\n", rfd_size, size);
-        size = rfd_size;
-    }
-#if !defined(CONFIG_PAD_RECEIVED_FRAMES)
-    if (size < 64) {
-        rfd_status |= 0x0080;
-    }
-#endif
-    TRACE(OTHER, logout("command 0x%04x, link 0x%08x, addr 0x%08x, size %u\n",
-          rfd_command, rx.link, rx.rx_buf_addr, rfd_size));
-    stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset +
-                offsetof(eepro100_rx_t, status), rfd_status);
-    stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset +
-                offsetof(eepro100_rx_t, count), size);
-    /* Early receive interrupt not supported. */
-#if 0
-    eepro100_er_interrupt(s);
-#endif
-    /* Receive CRC Transfer not supported. */
-    if (s->configuration[18] & BIT(2)) {
-        missing("Receive CRC Transfer");
-        return -1;
-    }
-    /* TODO: check stripping enable bit. */
-#if 0
-    assert(!(s->configuration[17] & BIT(0)));
-#endif
-    pci_dma_write(&s->dev, s->ru_base + s->ru_offset +
-                  sizeof(eepro100_rx_t), buf, size);
-    s->statistics.rx_good_frames++;
-    eepro100_fr_interrupt(s);
-    s->ru_offset = le32_to_cpu(rx.link);
-    if (rfd_command & COMMAND_EL) {
-        /* EL bit is set, so this was the last frame. */
-        logout("receive: Running out of frames\n");
-        set_ru_state(s, ru_no_resources);
-        eepro100_rnr_interrupt(s);
-    }
-    if (rfd_command & COMMAND_S) {
-        /* S bit is set. */
-        set_ru_state(s, ru_suspended);
-    }
-    return size;
-}
-
-static const VMStateDescription vmstate_eepro100 = {
-    .version_id = 3,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .fields      = (VMStateField []) {
-        VMSTATE_PCI_DEVICE(dev, EEPRO100State),
-        VMSTATE_UNUSED(32),
-        VMSTATE_BUFFER(mult, EEPRO100State),
-        VMSTATE_BUFFER(mem, EEPRO100State),
-        /* Save all members of struct between scb_stat and mem. */
-        VMSTATE_UINT8(scb_stat, EEPRO100State),
-        VMSTATE_UINT8(int_stat, EEPRO100State),
-        VMSTATE_UNUSED(3*4),
-        VMSTATE_MACADDR(conf.macaddr, EEPRO100State),
-        VMSTATE_UNUSED(19*4),
-        VMSTATE_UINT16_ARRAY(mdimem, EEPRO100State, 32),
-        /* The eeprom should be saved and restored by its own routines. */
-        VMSTATE_UINT32(device, EEPRO100State),
-        /* TODO check device. */
-        VMSTATE_UINT32(cu_base, EEPRO100State),
-        VMSTATE_UINT32(cu_offset, EEPRO100State),
-        VMSTATE_UINT32(ru_base, EEPRO100State),
-        VMSTATE_UINT32(ru_offset, EEPRO100State),
-        VMSTATE_UINT32(statsaddr, EEPRO100State),
-        /* Save eepro100_stats_t statistics. */
-        VMSTATE_UINT32(statistics.tx_good_frames, EEPRO100State),
-        VMSTATE_UINT32(statistics.tx_max_collisions, EEPRO100State),
-        VMSTATE_UINT32(statistics.tx_late_collisions, EEPRO100State),
-        VMSTATE_UINT32(statistics.tx_underruns, EEPRO100State),
-        VMSTATE_UINT32(statistics.tx_lost_crs, EEPRO100State),
-        VMSTATE_UINT32(statistics.tx_deferred, EEPRO100State),
-        VMSTATE_UINT32(statistics.tx_single_collisions, EEPRO100State),
-        VMSTATE_UINT32(statistics.tx_multiple_collisions, EEPRO100State),
-        VMSTATE_UINT32(statistics.tx_total_collisions, EEPRO100State),
-        VMSTATE_UINT32(statistics.rx_good_frames, EEPRO100State),
-        VMSTATE_UINT32(statistics.rx_crc_errors, EEPRO100State),
-        VMSTATE_UINT32(statistics.rx_alignment_errors, EEPRO100State),
-        VMSTATE_UINT32(statistics.rx_resource_errors, EEPRO100State),
-        VMSTATE_UINT32(statistics.rx_overrun_errors, EEPRO100State),
-        VMSTATE_UINT32(statistics.rx_cdt_errors, EEPRO100State),
-        VMSTATE_UINT32(statistics.rx_short_frame_errors, EEPRO100State),
-        VMSTATE_UINT32(statistics.fc_xmt_pause, EEPRO100State),
-        VMSTATE_UINT32(statistics.fc_rcv_pause, EEPRO100State),
-        VMSTATE_UINT32(statistics.fc_rcv_unsupported, EEPRO100State),
-        VMSTATE_UINT16(statistics.xmt_tco_frames, EEPRO100State),
-        VMSTATE_UINT16(statistics.rcv_tco_frames, EEPRO100State),
-        /* Configuration bytes. */
-        VMSTATE_BUFFER(configuration, EEPRO100State),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void nic_cleanup(NetClientState *nc)
-{
-    EEPRO100State *s = qemu_get_nic_opaque(nc);
-
-    s->nic = NULL;
-}
-
-static void pci_nic_uninit(PCIDevice *pci_dev)
-{
-    EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
-
-    memory_region_destroy(&s->mmio_bar);
-    memory_region_destroy(&s->io_bar);
-    memory_region_destroy(&s->flash_bar);
-    vmstate_unregister(&pci_dev->qdev, s->vmstate, s);
-    eeprom93xx_free(&pci_dev->qdev, s->eeprom);
-    qemu_del_nic(s->nic);
-}
-
-static NetClientInfo net_eepro100_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
-    .size = sizeof(NICState),
-    .can_receive = nic_can_receive,
-    .receive = nic_receive,
-    .cleanup = nic_cleanup,
-};
-
-static int e100_nic_init(PCIDevice *pci_dev)
-{
-    EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
-    E100PCIDeviceInfo *info = eepro100_get_class(s);
-
-    TRACE(OTHER, logout("\n"));
-
-    s->device = info->device;
-
-    e100_pci_reset(s);
-
-    /* Add 64 * 2 EEPROM. i82557 and i82558 support a 64 word EEPROM,
-     * i82559 and later support 64 or 256 word EEPROM. */
-    s->eeprom = eeprom93xx_new(&pci_dev->qdev, EEPROM_SIZE);
-
-    /* Handler for memory-mapped I/O */
-    memory_region_init_io(&s->mmio_bar, &eepro100_ops, s, "eepro100-mmio",
-                          PCI_MEM_SIZE);
-    pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->mmio_bar);
-    memory_region_init_io(&s->io_bar, &eepro100_ops, s, "eepro100-io",
-                          PCI_IO_SIZE);
-    pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
-    /* FIXME: flash aliases to mmio?! */
-    memory_region_init_io(&s->flash_bar, &eepro100_ops, s, "eepro100-flash",
-                          PCI_FLASH_SIZE);
-    pci_register_bar(&s->dev, 2, 0, &s->flash_bar);
-
-    qemu_macaddr_default_if_unset(&s->conf.macaddr);
-    logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6));
-
-    nic_reset(s);
-
-    s->nic = qemu_new_nic(&net_eepro100_info, &s->conf,
-                          object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s);
-
-    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
-    TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str));
-
-    qemu_register_reset(nic_reset, s);
-
-    s->vmstate = g_malloc(sizeof(vmstate_eepro100));
-    memcpy(s->vmstate, &vmstate_eepro100, sizeof(vmstate_eepro100));
-    s->vmstate->name = qemu_get_queue(s->nic)->model;
-    vmstate_register(&pci_dev->qdev, -1, s->vmstate, s);
-
-    add_boot_device_path(s->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0");
-
-    return 0;
-}
-
-static E100PCIDeviceInfo e100_devices[] = {
-    {
-        .name = "i82550",
-        .desc = "Intel i82550 Ethernet",
-        .device = i82550,
-        /* TODO: check device id. */
-        .device_id = PCI_DEVICE_ID_INTEL_82551IT,
-        /* Revision ID: 0x0c, 0x0d, 0x0e. */
-        .revision = 0x0e,
-        /* TODO: check size of statistical counters. */
-        .stats_size = 80,
-        /* TODO: check extended tcb support. */
-        .has_extended_tcb_support = true,
-        .power_management = true,
-    },{
-        .name = "i82551",
-        .desc = "Intel i82551 Ethernet",
-        .device = i82551,
-        .device_id = PCI_DEVICE_ID_INTEL_82551IT,
-        /* Revision ID: 0x0f, 0x10. */
-        .revision = 0x0f,
-        /* TODO: check size of statistical counters. */
-        .stats_size = 80,
-        .has_extended_tcb_support = true,
-        .power_management = true,
-    },{
-        .name = "i82557a",
-        .desc = "Intel i82557A Ethernet",
-        .device = i82557A,
-        .device_id = PCI_DEVICE_ID_INTEL_82557,
-        .revision = 0x01,
-        .power_management = false,
-    },{
-        .name = "i82557b",
-        .desc = "Intel i82557B Ethernet",
-        .device = i82557B,
-        .device_id = PCI_DEVICE_ID_INTEL_82557,
-        .revision = 0x02,
-        .power_management = false,
-    },{
-        .name = "i82557c",
-        .desc = "Intel i82557C Ethernet",
-        .device = i82557C,
-        .device_id = PCI_DEVICE_ID_INTEL_82557,
-        .revision = 0x03,
-        .power_management = false,
-    },{
-        .name = "i82558a",
-        .desc = "Intel i82558A Ethernet",
-        .device = i82558A,
-        .device_id = PCI_DEVICE_ID_INTEL_82557,
-        .revision = 0x04,
-        .stats_size = 76,
-        .has_extended_tcb_support = true,
-        .power_management = true,
-    },{
-        .name = "i82558b",
-        .desc = "Intel i82558B Ethernet",
-        .device = i82558B,
-        .device_id = PCI_DEVICE_ID_INTEL_82557,
-        .revision = 0x05,
-        .stats_size = 76,
-        .has_extended_tcb_support = true,
-        .power_management = true,
-    },{
-        .name = "i82559a",
-        .desc = "Intel i82559A Ethernet",
-        .device = i82559A,
-        .device_id = PCI_DEVICE_ID_INTEL_82557,
-        .revision = 0x06,
-        .stats_size = 80,
-        .has_extended_tcb_support = true,
-        .power_management = true,
-    },{
-        .name = "i82559b",
-        .desc = "Intel i82559B Ethernet",
-        .device = i82559B,
-        .device_id = PCI_DEVICE_ID_INTEL_82557,
-        .revision = 0x07,
-        .stats_size = 80,
-        .has_extended_tcb_support = true,
-        .power_management = true,
-    },{
-        .name = "i82559c",
-        .desc = "Intel i82559C Ethernet",
-        .device = i82559C,
-        .device_id = PCI_DEVICE_ID_INTEL_82557,
-#if 0
-        .revision = 0x08,
-#endif
-        /* TODO: Windows wants revision id 0x0c. */
-        .revision = 0x0c,
-#if EEPROM_SIZE > 0
-        .subsystem_vendor_id = PCI_VENDOR_ID_INTEL,
-        .subsystem_id = 0x0040,
-#endif
-        .stats_size = 80,
-        .has_extended_tcb_support = true,
-        .power_management = true,
-    },{
-        .name = "i82559er",
-        .desc = "Intel i82559ER Ethernet",
-        .device = i82559ER,
-        .device_id = PCI_DEVICE_ID_INTEL_82551IT,
-        .revision = 0x09,
-        .stats_size = 80,
-        .has_extended_tcb_support = true,
-        .power_management = true,
-    },{
-        .name = "i82562",
-        .desc = "Intel i82562 Ethernet",
-        .device = i82562,
-        /* TODO: check device id. */
-        .device_id = PCI_DEVICE_ID_INTEL_82551IT,
-        /* TODO: wrong revision id. */
-        .revision = 0x0e,
-        .stats_size = 80,
-        .has_extended_tcb_support = true,
-        .power_management = true,
-    },{
-        /* Toshiba Tecra 8200. */
-        .name = "i82801",
-        .desc = "Intel i82801 Ethernet",
-        .device = i82801,
-        .device_id = 0x2449,
-        .revision = 0x03,
-        .stats_size = 80,
-        .has_extended_tcb_support = true,
-        .power_management = true,
-    }
-};
-
-static E100PCIDeviceInfo *eepro100_get_class_by_name(const char *typename)
-{
-    E100PCIDeviceInfo *info = NULL;
-    int i;
-
-    /* This is admittedly awkward but also temporary.  QOM allows for
-     * parameterized typing and for subclassing both of which would suitable
-     * handle what's going on here.  But class_data is already being used as
-     * a stop-gap hack to allow incremental qdev conversion so we cannot use it
-     * right now.  Once we merge the final QOM series, we can come back here and
-     * do this in a much more elegant fashion.
-     */
-    for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
-        if (strcmp(e100_devices[i].name, typename) == 0) {
-            info = &e100_devices[i];
-            break;
-        }
-    }
-    assert(info != NULL);
-
-    return info;
-}
-
-static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s)
-{
-    return eepro100_get_class_by_name(object_get_typename(OBJECT(s)));
-}
-
-static Property e100_properties[] = {
-    DEFINE_NIC_PROPERTIES(EEPRO100State, conf),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void eepro100_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-    E100PCIDeviceInfo *info;
-
-    info = eepro100_get_class_by_name(object_class_get_name(klass));
-
-    dc->props = e100_properties;
-    dc->desc = info->desc;
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->class_id = PCI_CLASS_NETWORK_ETHERNET;
-    k->romfile = "pxe-eepro100.rom";
-    k->init = e100_nic_init;
-    k->exit = pci_nic_uninit;
-    k->device_id = info->device_id;
-    k->revision = info->revision;
-    k->subsystem_vendor_id = info->subsystem_vendor_id;
-    k->subsystem_id = info->subsystem_id;
-}
-
-static void eepro100_register_types(void)
-{
-    size_t i;
-    for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
-        TypeInfo type_info = {};
-        E100PCIDeviceInfo *info = &e100_devices[i];
-
-        type_info.name = info->name;
-        type_info.parent = TYPE_PCI_DEVICE;
-        type_info.class_init = eepro100_class_init;
-        type_info.instance_size = sizeof(EEPRO100State);
-        
-        type_register(&type_info);
-    }
-}
-
-type_init(eepro100_register_types)
diff --git a/hw/eeprom93xx.c b/hw/eeprom93xx.c
deleted file mode 100644 (file)
index 08f4df5..0000000
+++ /dev/null
@@ -1,337 +0,0 @@
-/*
- * QEMU EEPROM 93xx emulation
- *
- * Copyright (c) 2006-2007 Stefan Weil
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-/* Emulation for serial EEPROMs:
- * NMC93C06 256-Bit (16 x 16)
- * NMC93C46 1024-Bit (64 x 16)
- * NMC93C56 2028 Bit (128 x 16)
- * NMC93C66 4096 Bit (256 x 16)
- * Compatible devices include FM93C46 and others.
- *
- * Other drivers use these interface functions:
- * eeprom93xx_new   - add a new EEPROM (with 16, 64 or 256 words)
- * eeprom93xx_free  - destroy EEPROM
- * eeprom93xx_read  - read data from the EEPROM
- * eeprom93xx_write - write data to the EEPROM
- * eeprom93xx_data  - get EEPROM data array for external manipulation
- *
- * Todo list:
- * - No emulation of EEPROM timings.
- */
-
-#include "hw/hw.h"
-#include "hw/nvram/eeprom93xx.h"
-
-/* Debug EEPROM emulation. */
-//~ #define DEBUG_EEPROM
-
-#ifdef DEBUG_EEPROM
-#define logout(fmt, ...) fprintf(stderr, "EEPROM\t%-24s" fmt, __func__, ## __VA_ARGS__)
-#else
-#define logout(fmt, ...) ((void)0)
-#endif
-
-#define EEPROM_INSTANCE  0
-#define OLD_EEPROM_VERSION 20061112
-#define EEPROM_VERSION (OLD_EEPROM_VERSION + 1)
-
-#if 0
-typedef enum {
-  eeprom_read  = 0x80,   /* read register xx */
-  eeprom_write = 0x40,   /* write register xx */
-  eeprom_erase = 0xc0,   /* erase register xx */
-  eeprom_ewen  = 0x30,   /* erase / write enable */
-  eeprom_ewds  = 0x00,   /* erase / write disable */
-  eeprom_eral  = 0x20,   /* erase all registers */
-  eeprom_wral  = 0x10,   /* write all registers */
-  eeprom_amask = 0x0f,
-  eeprom_imask = 0xf0
-} eeprom_instruction_t;
-#endif
-
-#ifdef DEBUG_EEPROM
-static const char *opstring[] = {
-  "extended", "write", "read", "erase"
-};
-#endif
-
-struct _eeprom_t {
-    uint8_t  tick;
-    uint8_t  address;
-    uint8_t  command;
-    uint8_t  writable;
-
-    uint8_t eecs;
-    uint8_t eesk;
-    uint8_t eedo;
-
-    uint8_t  addrbits;
-    uint16_t size;
-    uint16_t data;
-    uint16_t contents[0];
-};
-
-/* Code for saving and restoring of EEPROM state. */
-
-/* Restore an uint16_t from an uint8_t
-   This is a Big hack, but it is how the old state did it.
- */
-
-static int get_uint16_from_uint8(QEMUFile *f, void *pv, size_t size)
-{
-    uint16_t *v = pv;
-    *v = qemu_get_ubyte(f);
-    return 0;
-}
-
-static void put_unused(QEMUFile *f, void *pv, size_t size)
-{
-    fprintf(stderr, "uint16_from_uint8 is used only for backwards compatibility.\n");
-    fprintf(stderr, "Never should be used to write a new state.\n");
-    exit(0);
-}
-
-static const VMStateInfo vmstate_hack_uint16_from_uint8 = {
-    .name = "uint16_from_uint8",
-    .get  = get_uint16_from_uint8,
-    .put  = put_unused,
-};
-
-#define VMSTATE_UINT16_HACK_TEST(_f, _s, _t)                           \
-    VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_hack_uint16_from_uint8, uint16_t)
-
-static bool is_old_eeprom_version(void *opaque, int version_id)
-{
-    return version_id == OLD_EEPROM_VERSION;
-}
-
-static const VMStateDescription vmstate_eeprom = {
-    .name = "eeprom",
-    .version_id = EEPROM_VERSION,
-    .minimum_version_id = OLD_EEPROM_VERSION,
-    .minimum_version_id_old = OLD_EEPROM_VERSION,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT8(tick, eeprom_t),
-        VMSTATE_UINT8(address, eeprom_t),
-        VMSTATE_UINT8(command, eeprom_t),
-        VMSTATE_UINT8(writable, eeprom_t),
-
-        VMSTATE_UINT8(eecs, eeprom_t),
-        VMSTATE_UINT8(eesk, eeprom_t),
-        VMSTATE_UINT8(eedo, eeprom_t),
-
-        VMSTATE_UINT8(addrbits, eeprom_t),
-        VMSTATE_UINT16_HACK_TEST(size, eeprom_t, is_old_eeprom_version),
-        VMSTATE_UNUSED_TEST(is_old_eeprom_version, 1),
-        VMSTATE_UINT16_EQUAL_V(size, eeprom_t, EEPROM_VERSION),
-        VMSTATE_UINT16(data, eeprom_t),
-        VMSTATE_VARRAY_UINT16_UNSAFE(contents, eeprom_t, size, 0,
-                                     vmstate_info_uint16, uint16_t),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-void eeprom93xx_write(eeprom_t *eeprom, int eecs, int eesk, int eedi)
-{
-    uint8_t tick = eeprom->tick;
-    uint8_t eedo = eeprom->eedo;
-    uint16_t address = eeprom->address;
-    uint8_t command = eeprom->command;
-
-    logout("CS=%u SK=%u DI=%u DO=%u, tick = %u\n",
-           eecs, eesk, eedi, eedo, tick);
-
-    if (! eeprom->eecs && eecs) {
-        /* Start chip select cycle. */
-        logout("Cycle start, waiting for 1st start bit (0)\n");
-        tick = 0;
-        command = 0x0;
-        address = 0x0;
-    } else if (eeprom->eecs && ! eecs) {
-        /* End chip select cycle. This triggers write / erase. */
-        if (eeprom->writable) {
-            uint8_t subcommand = address >> (eeprom->addrbits - 2);
-            if (command == 0 && subcommand == 2) {
-                /* Erase all. */
-                for (address = 0; address < eeprom->size; address++) {
-                    eeprom->contents[address] = 0xffff;
-                }
-            } else if (command == 3) {
-                /* Erase word. */
-                eeprom->contents[address] = 0xffff;
-            } else if (tick >= 2 + 2 + eeprom->addrbits + 16) {
-                if (command == 1) {
-                    /* Write word. */
-                    eeprom->contents[address] &= eeprom->data;
-                } else if (command == 0 && subcommand == 1) {
-                    /* Write all. */
-                    for (address = 0; address < eeprom->size; address++) {
-                        eeprom->contents[address] &= eeprom->data;
-                    }
-                }
-            }
-        }
-        /* Output DO is tristate, read results in 1. */
-        eedo = 1;
-    } else if (eecs && ! eeprom->eesk && eesk) {
-        /* Raising edge of clock shifts data in. */
-        if (tick == 0) {
-            /* Wait for 1st start bit. */
-            if (eedi == 0) {
-                logout("Got correct 1st start bit, waiting for 2nd start bit (1)\n");
-                tick++;
-            } else {
-                logout("wrong 1st start bit (is 1, should be 0)\n");
-                tick = 2;
-                //~ assert(!"wrong start bit");
-            }
-        } else if (tick == 1) {
-            /* Wait for 2nd start bit. */
-            if (eedi != 0) {
-                logout("Got correct 2nd start bit, getting command + address\n");
-                tick++;
-            } else {
-                logout("1st start bit is longer than needed\n");
-            }
-        } else if (tick < 2 + 2) {
-            /* Got 2 start bits, transfer 2 opcode bits. */
-            tick++;
-            command <<= 1;
-            if (eedi) {
-                command += 1;
-            }
-        } else if (tick < 2 + 2 + eeprom->addrbits) {
-            /* Got 2 start bits and 2 opcode bits, transfer all address bits. */
-            tick++;
-            address = ((address << 1) | eedi);
-            if (tick == 2 + 2 + eeprom->addrbits) {
-                logout("%s command, address = 0x%02x (value 0x%04x)\n",
-                       opstring[command], address, eeprom->contents[address]);
-                if (command == 2) {
-                    eedo = 0;
-                }
-                address = address % eeprom->size;
-                if (command == 0) {
-                    /* Command code in upper 2 bits of address. */
-                    switch (address >> (eeprom->addrbits - 2)) {
-                        case 0:
-                            logout("write disable command\n");
-                            eeprom->writable = 0;
-                            break;
-                        case 1:
-                            logout("write all command\n");
-                            break;
-                        case 2:
-                            logout("erase all command\n");
-                            break;
-                        case 3:
-                            logout("write enable command\n");
-                            eeprom->writable = 1;
-                            break;
-                    }
-                } else {
-                    /* Read, write or erase word. */
-                    eeprom->data = eeprom->contents[address];
-                }
-            }
-        } else if (tick < 2 + 2 + eeprom->addrbits + 16) {
-            /* Transfer 16 data bits. */
-            tick++;
-            if (command == 2) {
-                /* Read word. */
-                eedo = ((eeprom->data & 0x8000) != 0);
-            }
-            eeprom->data <<= 1;
-            eeprom->data += eedi;
-        } else {
-            logout("additional unneeded tick, not processed\n");
-        }
-    }
-    /* Save status of EEPROM. */
-    eeprom->tick = tick;
-    eeprom->eecs = eecs;
-    eeprom->eesk = eesk;
-    eeprom->eedo = eedo;
-    eeprom->address = address;
-    eeprom->command = command;
-}
-
-uint16_t eeprom93xx_read(eeprom_t *eeprom)
-{
-    /* Return status of pin DO (0 or 1). */
-    logout("CS=%u DO=%u\n", eeprom->eecs, eeprom->eedo);
-    return (eeprom->eedo);
-}
-
-#if 0
-void eeprom93xx_reset(eeprom_t *eeprom)
-{
-    /* prepare eeprom */
-    logout("eeprom = 0x%p\n", eeprom);
-    eeprom->tick = 0;
-    eeprom->command = 0;
-}
-#endif
-
-eeprom_t *eeprom93xx_new(DeviceState *dev, uint16_t nwords)
-{
-    /* Add a new EEPROM (with 16, 64 or 256 words). */
-    eeprom_t *eeprom;
-    uint8_t addrbits;
-
-    switch (nwords) {
-        case 16:
-        case 64:
-            addrbits = 6;
-            break;
-        case 128:
-        case 256:
-            addrbits = 8;
-            break;
-        default:
-            assert(!"Unsupported EEPROM size, fallback to 64 words!");
-            nwords = 64;
-            addrbits = 6;
-    }
-
-    eeprom = (eeprom_t *)g_malloc0(sizeof(*eeprom) + nwords * 2);
-    eeprom->size = nwords;
-    eeprom->addrbits = addrbits;
-    /* Output DO is tristate, read results in 1. */
-    eeprom->eedo = 1;
-    logout("eeprom = 0x%p, nwords = %u\n", eeprom, nwords);
-    vmstate_register(dev, 0, &vmstate_eeprom, eeprom);
-    return eeprom;
-}
-
-void eeprom93xx_free(DeviceState *dev, eeprom_t *eeprom)
-{
-    /* Destroy EEPROM. */
-    logout("eeprom = 0x%p\n", eeprom);
-    vmstate_unregister(dev, &vmstate_eeprom, eeprom);
-    g_free(eeprom);
-}
-
-uint16_t *eeprom93xx_data(eeprom_t *eeprom)
-{
-    /* Get EEPROM data array. */
-    return &eeprom->contents[0];
-}
-
-/* eof */
diff --git a/hw/empty_slot.c b/hw/empty_slot.c
deleted file mode 100644 (file)
index 5234a4d..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * QEMU Empty Slot
- *
- * The empty_slot device emulates known to a bus but not connected devices.
- *
- * Copyright (c) 2010 Artyom Tarasenko
- *
- * This code is licensed under the GNU GPL v2 or (at your option) any later
- * version.
- */
-
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-#include "hw/empty_slot.h"
-
-//#define DEBUG_EMPTY_SLOT
-
-#ifdef DEBUG_EMPTY_SLOT
-#define DPRINTF(fmt, ...)                                       \
-    do { printf("empty_slot: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while (0)
-#endif
-
-typedef struct EmptySlot {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    uint64_t size;
-} EmptySlot;
-
-static uint64_t empty_slot_read(void *opaque, hwaddr addr,
-                                unsigned size)
-{
-    DPRINTF("read from " TARGET_FMT_plx "\n", addr);
-    return 0;
-}
-
-static void empty_slot_write(void *opaque, hwaddr addr,
-                             uint64_t val, unsigned size)
-{
-    DPRINTF("write 0x%x to " TARGET_FMT_plx "\n", (unsigned)val, addr);
-}
-
-static const MemoryRegionOps empty_slot_ops = {
-    .read = empty_slot_read,
-    .write = empty_slot_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-void empty_slot_init(hwaddr addr, uint64_t slot_size)
-{
-    if (slot_size > 0) {
-        /* Only empty slots larger than 0 byte need handling. */
-        DeviceState *dev;
-        SysBusDevice *s;
-        EmptySlot *e;
-
-        dev = qdev_create(NULL, "empty_slot");
-        s = SYS_BUS_DEVICE(dev);
-        e = FROM_SYSBUS(EmptySlot, s);
-        e->size = slot_size;
-
-        qdev_init_nofail(dev);
-
-        sysbus_mmio_map(s, 0, addr);
-    }
-}
-
-static int empty_slot_init1(SysBusDevice *dev)
-{
-    EmptySlot *s = FROM_SYSBUS(EmptySlot, dev);
-
-    memory_region_init_io(&s->iomem, &empty_slot_ops, s,
-                          "empty-slot", s->size);
-    sysbus_init_mmio(dev, &s->iomem);
-    return 0;
-}
-
-static void empty_slot_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = empty_slot_init1;
-}
-
-static const TypeInfo empty_slot_info = {
-    .name          = "empty_slot",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(EmptySlot),
-    .class_init    = empty_slot_class_init,
-};
-
-static void empty_slot_register_types(void)
-{
-    type_register_static(&empty_slot_info);
-}
-
-type_init(empty_slot_register_types)
diff --git a/hw/es1370.c b/hw/es1370.c
deleted file mode 100644 (file)
index 9fe5708..0000000
+++ /dev/null
@@ -1,1089 +0,0 @@
-/*
- * QEMU ES1370 emulation
- *
- * Copyright (c) 2005 Vassili Karpov (malc)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-/* #define DEBUG_ES1370 */
-/* #define VERBOSE_ES1370 */
-#define SILENT_ES1370
-
-#include "hw/hw.h"
-#include "hw/audio/audio.h"
-#include "audio/audio.h"
-#include "hw/pci/pci.h"
-#include "sysemu/dma.h"
-
-/* Missing stuff:
-   SCTRL_P[12](END|ST)INC
-   SCTRL_P1SCTRLD
-   SCTRL_P2DACSEN
-   CTRL_DAC_SYNC
-   MIDI
-   non looped mode
-   surely more
-*/
-
-/*
-  Following macros and samplerate array were copied verbatim from
-  Linux kernel 2.4.30: drivers/sound/es1370.c
-
-  Copyright (C) 1998-2001, 2003 Thomas Sailer (t.sailer@alumni.ethz.ch)
-*/
-
-/* Start blatant GPL violation */
-
-#define ES1370_REG_CONTROL        0x00
-#define ES1370_REG_STATUS         0x04
-#define ES1370_REG_UART_DATA      0x08
-#define ES1370_REG_UART_STATUS    0x09
-#define ES1370_REG_UART_CONTROL   0x09
-#define ES1370_REG_UART_TEST      0x0a
-#define ES1370_REG_MEMPAGE        0x0c
-#define ES1370_REG_CODEC          0x10
-#define ES1370_REG_SERIAL_CONTROL 0x20
-#define ES1370_REG_DAC1_SCOUNT    0x24
-#define ES1370_REG_DAC2_SCOUNT    0x28
-#define ES1370_REG_ADC_SCOUNT     0x2c
-
-#define ES1370_REG_DAC1_FRAMEADR    0xc30
-#define ES1370_REG_DAC1_FRAMECNT    0xc34
-#define ES1370_REG_DAC2_FRAMEADR    0xc38
-#define ES1370_REG_DAC2_FRAMECNT    0xc3c
-#define ES1370_REG_ADC_FRAMEADR     0xd30
-#define ES1370_REG_ADC_FRAMECNT     0xd34
-#define ES1370_REG_PHANTOM_FRAMEADR 0xd38
-#define ES1370_REG_PHANTOM_FRAMECNT 0xd3c
-
-static const unsigned dac1_samplerate[] = { 5512, 11025, 22050, 44100 };
-
-#define DAC2_SRTODIV(x) (((1411200+(x)/2)/(x))-2)
-#define DAC2_DIVTOSR(x) (1411200/((x)+2))
-
-#define CTRL_ADC_STOP   0x80000000  /* 1 = ADC stopped */
-#define CTRL_XCTL1      0x40000000  /* electret mic bias */
-#define CTRL_OPEN       0x20000000  /* no function, can be read and written */
-#define CTRL_PCLKDIV    0x1fff0000  /* ADC/DAC2 clock divider */
-#define CTRL_SH_PCLKDIV 16
-#define CTRL_MSFMTSEL   0x00008000  /* MPEG serial data fmt: 0 = Sony, 1 = I2S */
-#define CTRL_M_SBB      0x00004000  /* DAC2 clock: 0 = PCLKDIV, 1 = MPEG */
-#define CTRL_WTSRSEL    0x00003000  /* DAC1 clock freq: 0=5512, 1=11025, 2=22050, 3=44100 */
-#define CTRL_SH_WTSRSEL 12
-#define CTRL_DAC_SYNC   0x00000800  /* 1 = DAC2 runs off DAC1 clock */
-#define CTRL_CCB_INTRM  0x00000400  /* 1 = CCB "voice" ints enabled */
-#define CTRL_M_CB       0x00000200  /* recording source: 0 = ADC, 1 = MPEG */
-#define CTRL_XCTL0      0x00000100  /* 0 = Line in, 1 = Line out */
-#define CTRL_BREQ       0x00000080  /* 1 = test mode (internal mem test) */
-#define CTRL_DAC1_EN    0x00000040  /* enable DAC1 */
-#define CTRL_DAC2_EN    0x00000020  /* enable DAC2 */
-#define CTRL_ADC_EN     0x00000010  /* enable ADC */
-#define CTRL_UART_EN    0x00000008  /* enable MIDI uart */
-#define CTRL_JYSTK_EN   0x00000004  /* enable Joystick port (presumably at address 0x200) */
-#define CTRL_CDC_EN     0x00000002  /* enable serial (CODEC) interface */
-#define CTRL_SERR_DIS   0x00000001  /* 1 = disable PCI SERR signal */
-
-#define STAT_INTR       0x80000000  /* wired or of all interrupt bits */
-#define STAT_CSTAT      0x00000400  /* 1 = codec busy or codec write in progress */
-#define STAT_CBUSY      0x00000200  /* 1 = codec busy */
-#define STAT_CWRIP      0x00000100  /* 1 = codec write in progress */
-#define STAT_VC         0x00000060  /* CCB int source, 0=DAC1, 1=DAC2, 2=ADC, 3=undef */
-#define STAT_SH_VC      5
-#define STAT_MCCB       0x00000010  /* CCB int pending */
-#define STAT_UART       0x00000008  /* UART int pending */
-#define STAT_DAC1       0x00000004  /* DAC1 int pending */
-#define STAT_DAC2       0x00000002  /* DAC2 int pending */
-#define STAT_ADC        0x00000001  /* ADC int pending */
-
-#define USTAT_RXINT     0x80        /* UART rx int pending */
-#define USTAT_TXINT     0x04        /* UART tx int pending */
-#define USTAT_TXRDY     0x02        /* UART tx ready */
-#define USTAT_RXRDY     0x01        /* UART rx ready */
-
-#define UCTRL_RXINTEN   0x80        /* 1 = enable RX ints */
-#define UCTRL_TXINTEN   0x60        /* TX int enable field mask */
-#define UCTRL_ENA_TXINT 0x20        /* enable TX int */
-#define UCTRL_CNTRL     0x03        /* control field */
-#define UCTRL_CNTRL_SWR 0x03        /* software reset command */
-
-#define SCTRL_P2ENDINC    0x00380000  /*  */
-#define SCTRL_SH_P2ENDINC 19
-#define SCTRL_P2STINC     0x00070000  /*  */
-#define SCTRL_SH_P2STINC  16
-#define SCTRL_R1LOOPSEL   0x00008000  /* 0 = loop mode */
-#define SCTRL_P2LOOPSEL   0x00004000  /* 0 = loop mode */
-#define SCTRL_P1LOOPSEL   0x00002000  /* 0 = loop mode */
-#define SCTRL_P2PAUSE     0x00001000  /* 1 = pause mode */
-#define SCTRL_P1PAUSE     0x00000800  /* 1 = pause mode */
-#define SCTRL_R1INTEN     0x00000400  /* enable interrupt */
-#define SCTRL_P2INTEN     0x00000200  /* enable interrupt */
-#define SCTRL_P1INTEN     0x00000100  /* enable interrupt */
-#define SCTRL_P1SCTRLD    0x00000080  /* reload sample count register for DAC1 */
-#define SCTRL_P2DACSEN    0x00000040  /* 1 = DAC2 play back last sample when disabled */
-#define SCTRL_R1SEB       0x00000020  /* 1 = 16bit */
-#define SCTRL_R1SMB       0x00000010  /* 1 = stereo */
-#define SCTRL_R1FMT       0x00000030  /* format mask */
-#define SCTRL_SH_R1FMT    4
-#define SCTRL_P2SEB       0x00000008  /* 1 = 16bit */
-#define SCTRL_P2SMB       0x00000004  /* 1 = stereo */
-#define SCTRL_P2FMT       0x0000000c  /* format mask */
-#define SCTRL_SH_P2FMT    2
-#define SCTRL_P1SEB       0x00000002  /* 1 = 16bit */
-#define SCTRL_P1SMB       0x00000001  /* 1 = stereo */
-#define SCTRL_P1FMT       0x00000003  /* format mask */
-#define SCTRL_SH_P1FMT    0
-
-/* End blatant GPL violation */
-
-#define NB_CHANNELS 3
-#define DAC1_CHANNEL 0
-#define DAC2_CHANNEL 1
-#define ADC_CHANNEL 2
-
-#define IO_READ_PROTO(n) \
-static uint32_t n (void *opaque, uint32_t addr)
-#define IO_WRITE_PROTO(n) \
-static void n (void *opaque, uint32_t addr, uint32_t val)
-
-static void es1370_dac1_callback (void *opaque, int free);
-static void es1370_dac2_callback (void *opaque, int free);
-static void es1370_adc_callback (void *opaque, int avail);
-
-#ifdef DEBUG_ES1370
-
-#define ldebug(...) AUD_log ("es1370", __VA_ARGS__)
-
-static void print_ctl (uint32_t val)
-{
-    char buf[1024];
-
-    buf[0] = '\0';
-#define a(n) if (val & CTRL_##n) strcat (buf, " "#n)
-    a (ADC_STOP);
-    a (XCTL1);
-    a (OPEN);
-    a (MSFMTSEL);
-    a (M_SBB);
-    a (DAC_SYNC);
-    a (CCB_INTRM);
-    a (M_CB);
-    a (XCTL0);
-    a (BREQ);
-    a (DAC1_EN);
-    a (DAC2_EN);
-    a (ADC_EN);
-    a (UART_EN);
-    a (JYSTK_EN);
-    a (CDC_EN);
-    a (SERR_DIS);
-#undef a
-    AUD_log ("es1370", "ctl - PCLKDIV %d(DAC2 freq %d), freq %d,%s\n",
-             (val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV,
-             DAC2_DIVTOSR ((val & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV),
-             dac1_samplerate[(val & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL],
-             buf);
-}
-
-static void print_sctl (uint32_t val)
-{
-    static const char *fmt_names[] = {"8M", "8S", "16M", "16S"};
-    char buf[1024];
-
-    buf[0] = '\0';
-
-#define a(n) if (val & SCTRL_##n) strcat (buf, " "#n)
-#define b(n) if (!(val & SCTRL_##n)) strcat (buf, " "#n)
-    b (R1LOOPSEL);
-    b (P2LOOPSEL);
-    b (P1LOOPSEL);
-    a (P2PAUSE);
-    a (P1PAUSE);
-    a (R1INTEN);
-    a (P2INTEN);
-    a (P1INTEN);
-    a (P1SCTRLD);
-    a (P2DACSEN);
-    if (buf[0]) {
-        strcat (buf, "\n        ");
-    }
-    else {
-        buf[0] = ' ';
-        buf[1] = '\0';
-    }
-#undef b
-#undef a
-    AUD_log ("es1370",
-             "%s"
-             "p2_end_inc %d, p2_st_inc %d, r1_fmt %s, p2_fmt %s, p1_fmt %s\n",
-             buf,
-             (val & SCTRL_P2ENDINC) >> SCTRL_SH_P2ENDINC,
-             (val & SCTRL_P2STINC) >> SCTRL_SH_P2STINC,
-             fmt_names [(val >> SCTRL_SH_R1FMT) & 3],
-             fmt_names [(val >> SCTRL_SH_P2FMT) & 3],
-             fmt_names [(val >> SCTRL_SH_P1FMT) & 3]
-        );
-}
-#else
-#define ldebug(...)
-#define print_ctl(...)
-#define print_sctl(...)
-#endif
-
-#ifdef VERBOSE_ES1370
-#define dolog(...) AUD_log ("es1370", __VA_ARGS__)
-#else
-#define dolog(...)
-#endif
-
-#ifndef SILENT_ES1370
-#define lwarn(...) AUD_log ("es1370: warning", __VA_ARGS__)
-#else
-#define lwarn(...)
-#endif
-
-struct chan {
-    uint32_t shift;
-    uint32_t leftover;
-    uint32_t scount;
-    uint32_t frame_addr;
-    uint32_t frame_cnt;
-};
-
-typedef struct ES1370State {
-    PCIDevice dev;
-    QEMUSoundCard card;
-    MemoryRegion io;
-    struct chan chan[NB_CHANNELS];
-    SWVoiceOut *dac_voice[2];
-    SWVoiceIn *adc_voice;
-
-    uint32_t ctl;
-    uint32_t status;
-    uint32_t mempage;
-    uint32_t codec;
-    uint32_t sctl;
-} ES1370State;
-
-struct chan_bits {
-    uint32_t ctl_en;
-    uint32_t stat_int;
-    uint32_t sctl_pause;
-    uint32_t sctl_inten;
-    uint32_t sctl_fmt;
-    uint32_t sctl_sh_fmt;
-    uint32_t sctl_loopsel;
-    void (*calc_freq) (ES1370State *s, uint32_t ctl,
-                       uint32_t *old_freq, uint32_t *new_freq);
-};
-
-static void es1370_dac1_calc_freq (ES1370State *s, uint32_t ctl,
-                                   uint32_t *old_freq, uint32_t *new_freq);
-static void es1370_dac2_and_adc_calc_freq (ES1370State *s, uint32_t ctl,
-                                           uint32_t *old_freq,
-                                           uint32_t *new_freq);
-
-static const struct chan_bits es1370_chan_bits[] = {
-    {CTRL_DAC1_EN, STAT_DAC1, SCTRL_P1PAUSE, SCTRL_P1INTEN,
-     SCTRL_P1FMT, SCTRL_SH_P1FMT, SCTRL_P1LOOPSEL,
-     es1370_dac1_calc_freq},
-
-    {CTRL_DAC2_EN, STAT_DAC2, SCTRL_P2PAUSE, SCTRL_P2INTEN,
-     SCTRL_P2FMT, SCTRL_SH_P2FMT, SCTRL_P2LOOPSEL,
-     es1370_dac2_and_adc_calc_freq},
-
-    {CTRL_ADC_EN, STAT_ADC, 0, SCTRL_R1INTEN,
-     SCTRL_R1FMT, SCTRL_SH_R1FMT, SCTRL_R1LOOPSEL,
-     es1370_dac2_and_adc_calc_freq}
-};
-
-static void es1370_update_status (ES1370State *s, uint32_t new_status)
-{
-    uint32_t level = new_status & (STAT_DAC1 | STAT_DAC2 | STAT_ADC);
-
-    if (level) {
-        s->status = new_status | STAT_INTR;
-    }
-    else {
-        s->status = new_status & ~STAT_INTR;
-    }
-    qemu_set_irq (s->dev.irq[0], !!level);
-}
-
-static void es1370_reset (ES1370State *s)
-{
-    size_t i;
-
-    s->ctl = 1;
-    s->status = 0x60;
-    s->mempage = 0;
-    s->codec = 0;
-    s->sctl = 0;
-
-    for (i = 0; i < NB_CHANNELS; ++i) {
-        struct chan *d = &s->chan[i];
-        d->scount = 0;
-        d->leftover = 0;
-        if (i == ADC_CHANNEL) {
-            AUD_close_in (&s->card, s->adc_voice);
-            s->adc_voice = NULL;
-        }
-        else {
-            AUD_close_out (&s->card, s->dac_voice[i]);
-            s->dac_voice[i] = NULL;
-        }
-    }
-    qemu_irq_lower (s->dev.irq[0]);
-}
-
-static void es1370_maybe_lower_irq (ES1370State *s, uint32_t sctl)
-{
-    uint32_t new_status = s->status;
-
-    if (!(sctl & SCTRL_P1INTEN) && (s->sctl & SCTRL_P1INTEN)) {
-        new_status &= ~STAT_DAC1;
-    }
-
-    if (!(sctl & SCTRL_P2INTEN) && (s->sctl & SCTRL_P2INTEN)) {
-        new_status &= ~STAT_DAC2;
-    }
-
-    if (!(sctl & SCTRL_R1INTEN) && (s->sctl & SCTRL_R1INTEN)) {
-        new_status &= ~STAT_ADC;
-    }
-
-    if (new_status != s->status) {
-        es1370_update_status (s, new_status);
-    }
-}
-
-static void es1370_dac1_calc_freq (ES1370State *s, uint32_t ctl,
-                                   uint32_t *old_freq, uint32_t *new_freq)
-
-{
-    *old_freq = dac1_samplerate[(s->ctl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL];
-    *new_freq = dac1_samplerate[(ctl & CTRL_WTSRSEL) >> CTRL_SH_WTSRSEL];
-}
-
-static void es1370_dac2_and_adc_calc_freq (ES1370State *s, uint32_t ctl,
-                                           uint32_t *old_freq,
-                                           uint32_t *new_freq)
-
-{
-    uint32_t old_pclkdiv, new_pclkdiv;
-
-    new_pclkdiv = (ctl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV;
-    old_pclkdiv = (s->ctl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV;
-    *new_freq = DAC2_DIVTOSR (new_pclkdiv);
-    *old_freq = DAC2_DIVTOSR (old_pclkdiv);
-}
-
-static void es1370_update_voices (ES1370State *s, uint32_t ctl, uint32_t sctl)
-{
-    size_t i;
-    uint32_t old_freq, new_freq, old_fmt, new_fmt;
-
-    for (i = 0; i < NB_CHANNELS; ++i) {
-        struct chan *d = &s->chan[i];
-        const struct chan_bits *b = &es1370_chan_bits[i];
-
-        new_fmt = (sctl & b->sctl_fmt) >> b->sctl_sh_fmt;
-        old_fmt = (s->sctl & b->sctl_fmt) >> b->sctl_sh_fmt;
-
-        b->calc_freq (s, ctl, &old_freq, &new_freq);
-
-        if ((old_fmt != new_fmt) || (old_freq != new_freq)) {
-            d->shift = (new_fmt & 1) + (new_fmt >> 1);
-            ldebug ("channel %zu, freq = %d, nchannels %d, fmt %d, shift %d\n",
-                    i,
-                    new_freq,
-                    1 << (new_fmt & 1),
-                    (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8,
-                    d->shift);
-            if (new_freq) {
-                struct audsettings as;
-
-                as.freq = new_freq;
-                as.nchannels = 1 << (new_fmt & 1);
-                as.fmt = (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8;
-                as.endianness = 0;
-
-                if (i == ADC_CHANNEL) {
-                    s->adc_voice =
-                        AUD_open_in (
-                            &s->card,
-                            s->adc_voice,
-                            "es1370.adc",
-                            s,
-                            es1370_adc_callback,
-                            &as
-                            );
-                }
-                else {
-                    s->dac_voice[i] =
-                        AUD_open_out (
-                            &s->card,
-                            s->dac_voice[i],
-                            i ? "es1370.dac2" : "es1370.dac1",
-                            s,
-                            i ? es1370_dac2_callback : es1370_dac1_callback,
-                            &as
-                            );
-                }
-            }
-        }
-
-        if (((ctl ^ s->ctl) & b->ctl_en)
-            || ((sctl ^ s->sctl) & b->sctl_pause)) {
-            int on = (ctl & b->ctl_en) && !(sctl & b->sctl_pause);
-
-            if (i == ADC_CHANNEL) {
-                AUD_set_active_in (s->adc_voice, on);
-            }
-            else {
-                AUD_set_active_out (s->dac_voice[i], on);
-            }
-        }
-    }
-
-    s->ctl = ctl;
-    s->sctl = sctl;
-}
-
-static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr)
-{
-    addr &= 0xff;
-    if (addr >= 0x30 && addr <= 0x3f)
-        addr |= s->mempage << 8;
-    return addr;
-}
-
-IO_WRITE_PROTO (es1370_writeb)
-{
-    ES1370State *s = opaque;
-    uint32_t shift, mask;
-
-    addr = es1370_fixup (s, addr);
-
-    switch (addr) {
-    case ES1370_REG_CONTROL:
-    case ES1370_REG_CONTROL + 1:
-    case ES1370_REG_CONTROL + 2:
-    case ES1370_REG_CONTROL + 3:
-        shift = (addr - ES1370_REG_CONTROL) << 3;
-        mask = 0xff << shift;
-        val = (s->ctl & ~mask) | ((val & 0xff) << shift);
-        es1370_update_voices (s, val, s->sctl);
-        print_ctl (val);
-        break;
-    case ES1370_REG_MEMPAGE:
-        s->mempage = val;
-        break;
-    case ES1370_REG_SERIAL_CONTROL:
-    case ES1370_REG_SERIAL_CONTROL + 1:
-    case ES1370_REG_SERIAL_CONTROL + 2:
-    case ES1370_REG_SERIAL_CONTROL + 3:
-        shift = (addr - ES1370_REG_SERIAL_CONTROL) << 3;
-        mask = 0xff << shift;
-        val = (s->sctl & ~mask) | ((val & 0xff) << shift);
-        es1370_maybe_lower_irq (s, val);
-        es1370_update_voices (s, s->ctl, val);
-        print_sctl (val);
-        break;
-    default:
-        lwarn ("writeb %#x <- %#x\n", addr, val);
-        break;
-    }
-}
-
-IO_WRITE_PROTO (es1370_writew)
-{
-    ES1370State *s = opaque;
-    addr = es1370_fixup (s, addr);
-    uint32_t shift, mask;
-    struct chan *d = &s->chan[0];
-
-    switch (addr) {
-    case ES1370_REG_CODEC:
-        dolog ("ignored codec write address %#x, data %#x\n",
-               (val >> 8) & 0xff, val & 0xff);
-        s->codec = val;
-        break;
-
-    case ES1370_REG_CONTROL:
-    case ES1370_REG_CONTROL + 2:
-        shift = (addr != ES1370_REG_CONTROL) << 4;
-        mask = 0xffff << shift;
-        val = (s->ctl & ~mask) | ((val & 0xffff) << shift);
-        es1370_update_voices (s, val, s->sctl);
-        print_ctl (val);
-        break;
-
-    case ES1370_REG_ADC_SCOUNT:
-        d++;
-    case ES1370_REG_DAC2_SCOUNT:
-        d++;
-    case ES1370_REG_DAC1_SCOUNT:
-        d->scount = (d->scount & ~0xffff) | (val & 0xffff);
-        break;
-
-    default:
-        lwarn ("writew %#x <- %#x\n", addr, val);
-        break;
-    }
-}
-
-IO_WRITE_PROTO (es1370_writel)
-{
-    ES1370State *s = opaque;
-    struct chan *d = &s->chan[0];
-
-    addr = es1370_fixup (s, addr);
-
-    switch (addr) {
-    case ES1370_REG_CONTROL:
-        es1370_update_voices (s, val, s->sctl);
-        print_ctl (val);
-        break;
-
-    case ES1370_REG_MEMPAGE:
-        s->mempage = val & 0xf;
-        break;
-
-    case ES1370_REG_SERIAL_CONTROL:
-        es1370_maybe_lower_irq (s, val);
-        es1370_update_voices (s, s->ctl, val);
-        print_sctl (val);
-        break;
-
-    case ES1370_REG_ADC_SCOUNT:
-        d++;
-    case ES1370_REG_DAC2_SCOUNT:
-        d++;
-    case ES1370_REG_DAC1_SCOUNT:
-        d->scount = (val & 0xffff) | (d->scount & ~0xffff);
-        ldebug ("chan %td CURR_SAMP_CT %d, SAMP_CT %d\n",
-                d - &s->chan[0], val >> 16, (val & 0xffff));
-        break;
-
-    case ES1370_REG_ADC_FRAMEADR:
-        d++;
-    case ES1370_REG_DAC2_FRAMEADR:
-        d++;
-    case ES1370_REG_DAC1_FRAMEADR:
-        d->frame_addr = val;
-        ldebug ("chan %td frame address %#x\n", d - &s->chan[0], val);
-        break;
-
-    case ES1370_REG_PHANTOM_FRAMECNT:
-        lwarn ("writing to phantom frame count %#x\n", val);
-        break;
-    case ES1370_REG_PHANTOM_FRAMEADR:
-        lwarn ("writing to phantom frame address %#x\n", val);
-        break;
-
-    case ES1370_REG_ADC_FRAMECNT:
-        d++;
-    case ES1370_REG_DAC2_FRAMECNT:
-        d++;
-    case ES1370_REG_DAC1_FRAMECNT:
-        d->frame_cnt = val;
-        d->leftover = 0;
-        ldebug ("chan %td frame count %d, buffer size %d\n",
-                d - &s->chan[0], val >> 16, val & 0xffff);
-        break;
-
-    default:
-        lwarn ("writel %#x <- %#x\n", addr, val);
-        break;
-    }
-}
-
-IO_READ_PROTO (es1370_readb)
-{
-    ES1370State *s = opaque;
-    uint32_t val;
-
-    addr = es1370_fixup (s, addr);
-
-    switch (addr) {
-    case 0x1b:                  /* Legacy */
-        lwarn ("Attempt to read from legacy register\n");
-        val = 5;
-        break;
-    case ES1370_REG_MEMPAGE:
-        val = s->mempage;
-        break;
-    case ES1370_REG_CONTROL + 0:
-    case ES1370_REG_CONTROL + 1:
-    case ES1370_REG_CONTROL + 2:
-    case ES1370_REG_CONTROL + 3:
-        val = s->ctl >> ((addr - ES1370_REG_CONTROL) << 3);
-        break;
-    case ES1370_REG_STATUS + 0:
-    case ES1370_REG_STATUS + 1:
-    case ES1370_REG_STATUS + 2:
-    case ES1370_REG_STATUS + 3:
-        val = s->status >> ((addr - ES1370_REG_STATUS) << 3);
-        break;
-    default:
-        val = ~0;
-        lwarn ("readb %#x -> %#x\n", addr, val);
-        break;
-    }
-    return val;
-}
-
-IO_READ_PROTO (es1370_readw)
-{
-    ES1370State *s = opaque;
-    struct chan *d = &s->chan[0];
-    uint32_t val;
-
-    addr = es1370_fixup (s, addr);
-
-    switch (addr) {
-    case ES1370_REG_ADC_SCOUNT + 2:
-        d++;
-    case ES1370_REG_DAC2_SCOUNT + 2:
-        d++;
-    case ES1370_REG_DAC1_SCOUNT + 2:
-        val = d->scount >> 16;
-        break;
-
-    case ES1370_REG_ADC_FRAMECNT:
-        d++;
-    case ES1370_REG_DAC2_FRAMECNT:
-        d++;
-    case ES1370_REG_DAC1_FRAMECNT:
-        val = d->frame_cnt & 0xffff;
-        break;
-
-    case ES1370_REG_ADC_FRAMECNT + 2:
-        d++;
-    case ES1370_REG_DAC2_FRAMECNT + 2:
-        d++;
-    case ES1370_REG_DAC1_FRAMECNT + 2:
-        val = d->frame_cnt >> 16;
-        break;
-
-    default:
-        val = ~0;
-        lwarn ("readw %#x -> %#x\n", addr, val);
-        break;
-    }
-
-    return val;
-}
-
-IO_READ_PROTO (es1370_readl)
-{
-    ES1370State *s = opaque;
-    uint32_t val;
-    struct chan *d = &s->chan[0];
-
-    addr = es1370_fixup (s, addr);
-
-    switch (addr) {
-    case ES1370_REG_CONTROL:
-        val = s->ctl;
-        break;
-    case ES1370_REG_STATUS:
-        val = s->status;
-        break;
-    case ES1370_REG_MEMPAGE:
-        val = s->mempage;
-        break;
-    case ES1370_REG_CODEC:
-        val = s->codec;
-        break;
-    case ES1370_REG_SERIAL_CONTROL:
-        val = s->sctl;
-        break;
-
-    case ES1370_REG_ADC_SCOUNT:
-        d++;
-    case ES1370_REG_DAC2_SCOUNT:
-        d++;
-    case ES1370_REG_DAC1_SCOUNT:
-        val = d->scount;
-#ifdef DEBUG_ES1370
-        {
-            uint32_t curr_count = d->scount >> 16;
-            uint32_t count = d->scount & 0xffff;
-
-            curr_count <<= d->shift;
-            count <<= d->shift;
-            dolog ("read scount curr %d, total %d\n", curr_count, count);
-        }
-#endif
-        break;
-
-    case ES1370_REG_ADC_FRAMECNT:
-        d++;
-    case ES1370_REG_DAC2_FRAMECNT:
-        d++;
-    case ES1370_REG_DAC1_FRAMECNT:
-        val = d->frame_cnt;
-#ifdef DEBUG_ES1370
-        {
-            uint32_t size = ((d->frame_cnt & 0xffff) + 1) << 2;
-            uint32_t curr = ((d->frame_cnt >> 16) + 1) << 2;
-            if (curr > size) {
-                dolog ("read framecnt curr %d, size %d %d\n", curr, size,
-                       curr > size);
-            }
-        }
-#endif
-        break;
-
-    case ES1370_REG_ADC_FRAMEADR:
-        d++;
-    case ES1370_REG_DAC2_FRAMEADR:
-        d++;
-    case ES1370_REG_DAC1_FRAMEADR:
-        val = d->frame_addr;
-        break;
-
-    case ES1370_REG_PHANTOM_FRAMECNT:
-        val = ~0U;
-        lwarn ("reading from phantom frame count\n");
-        break;
-    case ES1370_REG_PHANTOM_FRAMEADR:
-        val = ~0U;
-        lwarn ("reading from phantom frame address\n");
-        break;
-
-    default:
-        val = ~0U;
-        lwarn ("readl %#x -> %#x\n", addr, val);
-        break;
-    }
-    return val;
-}
-
-static void es1370_transfer_audio (ES1370State *s, struct chan *d, int loop_sel,
-                                   int max, int *irq)
-{
-    uint8_t tmpbuf[4096];
-    uint32_t addr = d->frame_addr;
-    int sc = d->scount & 0xffff;
-    int csc = d->scount >> 16;
-    int csc_bytes = (csc + 1) << d->shift;
-    int cnt = d->frame_cnt >> 16;
-    int size = d->frame_cnt & 0xffff;
-    int left = ((size - cnt + 1) << 2) + d->leftover;
-    int transferred = 0;
-    int temp = audio_MIN (max, audio_MIN (left, csc_bytes));
-    int index = d - &s->chan[0];
-
-    addr += (cnt << 2) + d->leftover;
-
-    if (index == ADC_CHANNEL) {
-        while (temp) {
-            int acquired, to_copy;
-
-            to_copy = audio_MIN ((size_t) temp, sizeof (tmpbuf));
-            acquired = AUD_read (s->adc_voice, tmpbuf, to_copy);
-            if (!acquired)
-                break;
-
-            pci_dma_write (&s->dev, addr, tmpbuf, acquired);
-
-            temp -= acquired;
-            addr += acquired;
-            transferred += acquired;
-        }
-    }
-    else {
-        SWVoiceOut *voice = s->dac_voice[index];
-
-        while (temp) {
-            int copied, to_copy;
-
-            to_copy = audio_MIN ((size_t) temp, sizeof (tmpbuf));
-            pci_dma_read (&s->dev, addr, tmpbuf, to_copy);
-            copied = AUD_write (voice, tmpbuf, to_copy);
-            if (!copied)
-                break;
-            temp -= copied;
-            addr += copied;
-            transferred += copied;
-        }
-    }
-
-    if (csc_bytes == transferred) {
-        *irq = 1;
-        d->scount = sc | (sc << 16);
-        ldebug ("sc = %d, rate = %f\n",
-                (sc + 1) << d->shift,
-                (sc + 1) / (double) 44100);
-    }
-    else {
-        *irq = 0;
-        d->scount = sc | (((csc_bytes - transferred - 1) >> d->shift) << 16);
-    }
-
-    cnt += (transferred + d->leftover) >> 2;
-
-    if (s->sctl & loop_sel) {
-        /* Bah, how stupid is that having a 0 represent true value?
-           i just spent few hours on this shit */
-        AUD_log ("es1370: warning", "non looping mode\n");
-    }
-    else {
-        d->frame_cnt = size;
-
-        if ((uint32_t) cnt <= d->frame_cnt)
-            d->frame_cnt |= cnt << 16;
-    }
-
-    d->leftover = (transferred + d->leftover) & 3;
-}
-
-static void es1370_run_channel (ES1370State *s, size_t chan, int free_or_avail)
-{
-    uint32_t new_status = s->status;
-    int max_bytes, irq;
-    struct chan *d = &s->chan[chan];
-    const struct chan_bits *b = &es1370_chan_bits[chan];
-
-    if (!(s->ctl & b->ctl_en) || (s->sctl & b->sctl_pause)) {
-        return;
-    }
-
-    max_bytes = free_or_avail;
-    max_bytes &= ~((1 << d->shift) - 1);
-    if (!max_bytes) {
-        return;
-    }
-
-    es1370_transfer_audio (s, d, b->sctl_loopsel, max_bytes, &irq);
-
-    if (irq) {
-        if (s->sctl & b->sctl_inten) {
-            new_status |= b->stat_int;
-        }
-    }
-
-    if (new_status != s->status) {
-        es1370_update_status (s, new_status);
-    }
-}
-
-static void es1370_dac1_callback (void *opaque, int free)
-{
-    ES1370State *s = opaque;
-
-    es1370_run_channel (s, DAC1_CHANNEL, free);
-}
-
-static void es1370_dac2_callback (void *opaque, int free)
-{
-    ES1370State *s = opaque;
-
-    es1370_run_channel (s, DAC2_CHANNEL, free);
-}
-
-static void es1370_adc_callback (void *opaque, int avail)
-{
-    ES1370State *s = opaque;
-
-    es1370_run_channel (s, ADC_CHANNEL, avail);
-}
-
-static uint64_t es1370_read(void *opaque, hwaddr addr,
-                            unsigned size)
-{
-    switch (size) {
-    case 1:
-        return es1370_readb(opaque, addr);
-    case 2:
-        return es1370_readw(opaque, addr);
-    case 4:
-        return es1370_readl(opaque, addr);
-    default:
-        return -1;
-    }
-}
-
-static void es1370_write(void *opaque, hwaddr addr, uint64_t val,
-                      unsigned size)
-{
-    switch (size) {
-    case 1:
-        es1370_writeb(opaque, addr, val);
-        break;
-    case 2:
-        es1370_writew(opaque, addr, val);
-        break;
-    case 4:
-        es1370_writel(opaque, addr, val);
-        break;
-    }
-}
-
-static const MemoryRegionOps es1370_io_ops = {
-    .read = es1370_read,
-    .write = es1370_write,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 4,
-    },
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static const VMStateDescription vmstate_es1370_channel = {
-    .name = "es1370_channel",
-    .version_id = 2,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT32 (shift, struct chan),
-        VMSTATE_UINT32 (leftover, struct chan),
-        VMSTATE_UINT32 (scount, struct chan),
-        VMSTATE_UINT32 (frame_addr, struct chan),
-        VMSTATE_UINT32 (frame_cnt, struct chan),
-        VMSTATE_END_OF_LIST ()
-    }
-};
-
-static int es1370_post_load (void *opaque, int version_id)
-{
-    uint32_t ctl, sctl;
-    ES1370State *s = opaque;
-    size_t i;
-
-    for (i = 0; i < NB_CHANNELS; ++i) {
-        if (i == ADC_CHANNEL) {
-            if (s->adc_voice) {
-                AUD_close_in (&s->card, s->adc_voice);
-                s->adc_voice = NULL;
-            }
-        }
-        else {
-            if (s->dac_voice[i]) {
-                AUD_close_out (&s->card, s->dac_voice[i]);
-                s->dac_voice[i] = NULL;
-            }
-        }
-    }
-
-    ctl = s->ctl;
-    sctl = s->sctl;
-    s->ctl = 0;
-    s->sctl = 0;
-    es1370_update_voices (s, ctl, sctl);
-    return 0;
-}
-
-static const VMStateDescription vmstate_es1370 = {
-    .name = "es1370",
-    .version_id = 2,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .post_load = es1370_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_PCI_DEVICE (dev, ES1370State),
-        VMSTATE_STRUCT_ARRAY (chan, ES1370State, NB_CHANNELS, 2,
-                              vmstate_es1370_channel, struct chan),
-        VMSTATE_UINT32 (ctl, ES1370State),
-        VMSTATE_UINT32 (status, ES1370State),
-        VMSTATE_UINT32 (mempage, ES1370State),
-        VMSTATE_UINT32 (codec, ES1370State),
-        VMSTATE_UINT32 (sctl, ES1370State),
-        VMSTATE_END_OF_LIST ()
-    }
-};
-
-static void es1370_on_reset (void *opaque)
-{
-    ES1370State *s = opaque;
-    es1370_reset (s);
-}
-
-static int es1370_initfn (PCIDevice *dev)
-{
-    ES1370State *s = DO_UPCAST (ES1370State, dev, dev);
-    uint8_t *c = s->dev.config;
-
-    c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_SLOW >> 8;
-
-#if 0
-    c[PCI_CAPABILITY_LIST] = 0xdc;
-    c[PCI_INTERRUPT_LINE] = 10;
-    c[0xdc] = 0x00;
-#endif
-
-    c[PCI_INTERRUPT_PIN] = 1;
-    c[PCI_MIN_GNT] = 0x0c;
-    c[PCI_MAX_LAT] = 0x80;
-
-    memory_region_init_io (&s->io, &es1370_io_ops, s, "es1370", 256);
-    pci_register_bar (&s->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io);
-    qemu_register_reset (es1370_on_reset, s);
-
-    AUD_register_card ("es1370", &s->card);
-    es1370_reset (s);
-    return 0;
-}
-
-static void es1370_exitfn (PCIDevice *dev)
-{
-    ES1370State *s = DO_UPCAST (ES1370State, dev, dev);
-
-    memory_region_destroy (&s->io);
-}
-
-int es1370_init (PCIBus *bus)
-{
-    pci_create_simple (bus, -1, "ES1370");
-    return 0;
-}
-
-static void es1370_class_init (ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS (klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS (klass);
-
-    k->init = es1370_initfn;
-    k->exit = es1370_exitfn;
-    k->vendor_id = PCI_VENDOR_ID_ENSONIQ;
-    k->device_id = PCI_DEVICE_ID_ENSONIQ_ES1370;
-    k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO;
-    k->subsystem_vendor_id = 0x4942;
-    k->subsystem_id = 0x4c4c;
-    dc->desc = "ENSONIQ AudioPCI ES1370";
-    dc->vmsd = &vmstate_es1370;
-}
-
-static const TypeInfo es1370_info = {
-    .name          = "ES1370",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof (ES1370State),
-    .class_init    = es1370_class_init,
-};
-
-static void es1370_register_types (void)
-{
-    type_register_static (&es1370_info);
-}
-
-type_init (es1370_register_types)
-
diff --git a/hw/escc.c b/hw/escc.c
deleted file mode 100644 (file)
index 067b055..0000000
--- a/hw/escc.c
+++ /dev/null
@@ -1,938 +0,0 @@
-/*
- * QEMU ESCC (Z8030/Z8530/Z85C30/SCC/ESCC) serial port emulation
- *
- * Copyright (c) 2003-2005 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-#include "hw/char/escc.h"
-#include "char/char.h"
-#include "ui/console.h"
-#include "trace.h"
-
-/*
- * Chipset docs:
- * "Z80C30/Z85C30/Z80230/Z85230/Z85233 SCC/ESCC User Manual",
- * http://www.zilog.com/docs/serial/scc_escc_um.pdf
- *
- * On Sparc32 this is the serial port, mouse and keyboard part of chip STP2001
- * (Slave I/O), also produced as NCR89C105. See
- * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
- *
- * The serial ports implement full AMD AM8530 or Zilog Z8530 chips,
- * mouse and keyboard ports don't implement all functions and they are
- * only asynchronous. There is no DMA.
- *
- * Z85C30 is also used on PowerMacs. There are some small differences
- * between Sparc version (sunzilog) and PowerMac (pmac):
- *  Offset between control and data registers
- *  There is some kind of lockup bug, but we can ignore it
- *  CTS is inverted
- *  DMA on pmac using DBDMA chip
- *  pmac can do IRDA and faster rates, sunzilog can only do 38400
- *  pmac baud rate generator clock is 3.6864 MHz, sunzilog 4.9152 MHz
- */
-
-/*
- * Modifications:
- *  2006-Aug-10  Igor Kovalenko :   Renamed KBDQueue to SERIOQueue, implemented
- *                                  serial mouse queue.
- *                                  Implemented serial mouse protocol.
- *
- *  2010-May-23  Artyom Tarasenko:  Reworked IUS logic
- */
-
-typedef enum {
-    chn_a, chn_b,
-} ChnID;
-
-#define CHN_C(s) ((s)->chn == chn_b? 'b' : 'a')
-
-typedef enum {
-    ser, kbd, mouse,
-} ChnType;
-
-#define SERIO_QUEUE_SIZE 256
-
-typedef struct {
-    uint8_t data[SERIO_QUEUE_SIZE];
-    int rptr, wptr, count;
-} SERIOQueue;
-
-#define SERIAL_REGS 16
-typedef struct ChannelState {
-    qemu_irq irq;
-    uint32_t rxint, txint, rxint_under_svc, txint_under_svc;
-    struct ChannelState *otherchn;
-    uint32_t reg;
-    uint8_t wregs[SERIAL_REGS], rregs[SERIAL_REGS];
-    SERIOQueue queue;
-    CharDriverState *chr;
-    int e0_mode, led_mode, caps_lock_mode, num_lock_mode;
-    int disabled;
-    int clock;
-    uint32_t vmstate_dummy;
-    ChnID chn; // this channel, A (base+4) or B (base+0)
-    ChnType type;
-    uint8_t rx, tx;
-} ChannelState;
-
-struct SerialState {
-    SysBusDevice busdev;
-    struct ChannelState chn[2];
-    uint32_t it_shift;
-    MemoryRegion mmio;
-    uint32_t disabled;
-    uint32_t frequency;
-};
-
-#define SERIAL_CTRL 0
-#define SERIAL_DATA 1
-
-#define W_CMD     0
-#define CMD_PTR_MASK   0x07
-#define CMD_CMD_MASK   0x38
-#define CMD_HI         0x08
-#define CMD_CLR_TXINT  0x28
-#define CMD_CLR_IUS    0x38
-#define W_INTR    1
-#define INTR_INTALL    0x01
-#define INTR_TXINT     0x02
-#define INTR_RXMODEMSK 0x18
-#define INTR_RXINT1ST  0x08
-#define INTR_RXINTALL  0x10
-#define W_IVEC    2
-#define W_RXCTRL  3
-#define RXCTRL_RXEN    0x01
-#define W_TXCTRL1 4
-#define TXCTRL1_PAREN  0x01
-#define TXCTRL1_PAREV  0x02
-#define TXCTRL1_1STOP  0x04
-#define TXCTRL1_1HSTOP 0x08
-#define TXCTRL1_2STOP  0x0c
-#define TXCTRL1_STPMSK 0x0c
-#define TXCTRL1_CLK1X  0x00
-#define TXCTRL1_CLK16X 0x40
-#define TXCTRL1_CLK32X 0x80
-#define TXCTRL1_CLK64X 0xc0
-#define TXCTRL1_CLKMSK 0xc0
-#define W_TXCTRL2 5
-#define TXCTRL2_TXEN   0x08
-#define TXCTRL2_BITMSK 0x60
-#define TXCTRL2_5BITS  0x00
-#define TXCTRL2_7BITS  0x20
-#define TXCTRL2_6BITS  0x40
-#define TXCTRL2_8BITS  0x60
-#define W_SYNC1   6
-#define W_SYNC2   7
-#define W_TXBUF   8
-#define W_MINTR   9
-#define MINTR_STATUSHI 0x10
-#define MINTR_RST_MASK 0xc0
-#define MINTR_RST_B    0x40
-#define MINTR_RST_A    0x80
-#define MINTR_RST_ALL  0xc0
-#define W_MISC1  10
-#define W_CLOCK  11
-#define CLOCK_TRXC     0x08
-#define W_BRGLO  12
-#define W_BRGHI  13
-#define W_MISC2  14
-#define MISC2_PLLDIS   0x30
-#define W_EXTINT 15
-#define EXTINT_DCD     0x08
-#define EXTINT_SYNCINT 0x10
-#define EXTINT_CTSINT  0x20
-#define EXTINT_TXUNDRN 0x40
-#define EXTINT_BRKINT  0x80
-
-#define R_STATUS  0
-#define STATUS_RXAV    0x01
-#define STATUS_ZERO    0x02
-#define STATUS_TXEMPTY 0x04
-#define STATUS_DCD     0x08
-#define STATUS_SYNC    0x10
-#define STATUS_CTS     0x20
-#define STATUS_TXUNDRN 0x40
-#define STATUS_BRK     0x80
-#define R_SPEC    1
-#define SPEC_ALLSENT   0x01
-#define SPEC_BITS8     0x06
-#define R_IVEC    2
-#define IVEC_TXINTB    0x00
-#define IVEC_LONOINT   0x06
-#define IVEC_LORXINTA  0x0c
-#define IVEC_LORXINTB  0x04
-#define IVEC_LOTXINTA  0x08
-#define IVEC_HINOINT   0x60
-#define IVEC_HIRXINTA  0x30
-#define IVEC_HIRXINTB  0x20
-#define IVEC_HITXINTA  0x10
-#define R_INTR    3
-#define INTR_EXTINTB   0x01
-#define INTR_TXINTB    0x02
-#define INTR_RXINTB    0x04
-#define INTR_EXTINTA   0x08
-#define INTR_TXINTA    0x10
-#define INTR_RXINTA    0x20
-#define R_IPEN    4
-#define R_TXCTRL1 5
-#define R_TXCTRL2 6
-#define R_BC      7
-#define R_RXBUF   8
-#define R_RXCTRL  9
-#define R_MISC   10
-#define R_MISC1  11
-#define R_BRGLO  12
-#define R_BRGHI  13
-#define R_MISC1I 14
-#define R_EXTINT 15
-
-static void handle_kbd_command(ChannelState *s, int val);
-static int serial_can_receive(void *opaque);
-static void serial_receive_byte(ChannelState *s, int ch);
-
-static void clear_queue(void *opaque)
-{
-    ChannelState *s = opaque;
-    SERIOQueue *q = &s->queue;
-    q->rptr = q->wptr = q->count = 0;
-}
-
-static void put_queue(void *opaque, int b)
-{
-    ChannelState *s = opaque;
-    SERIOQueue *q = &s->queue;
-
-    trace_escc_put_queue(CHN_C(s), b);
-    if (q->count >= SERIO_QUEUE_SIZE)
-        return;
-    q->data[q->wptr] = b;
-    if (++q->wptr == SERIO_QUEUE_SIZE)
-        q->wptr = 0;
-    q->count++;
-    serial_receive_byte(s, 0);
-}
-
-static uint32_t get_queue(void *opaque)
-{
-    ChannelState *s = opaque;
-    SERIOQueue *q = &s->queue;
-    int val;
-
-    if (q->count == 0) {
-        return 0;
-    } else {
-        val = q->data[q->rptr];
-        if (++q->rptr == SERIO_QUEUE_SIZE)
-            q->rptr = 0;
-        q->count--;
-    }
-    trace_escc_get_queue(CHN_C(s), val);
-    if (q->count > 0)
-        serial_receive_byte(s, 0);
-    return val;
-}
-
-static int escc_update_irq_chn(ChannelState *s)
-{
-    if ((((s->wregs[W_INTR] & INTR_TXINT) && (s->txint == 1)) ||
-         // tx ints enabled, pending
-         ((((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINT1ST) ||
-           ((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINTALL)) &&
-          s->rxint == 1) || // rx ints enabled, pending
-         ((s->wregs[W_EXTINT] & EXTINT_BRKINT) &&
-          (s->rregs[R_STATUS] & STATUS_BRK)))) { // break int e&p
-        return 1;
-    }
-    return 0;
-}
-
-static void escc_update_irq(ChannelState *s)
-{
-    int irq;
-
-    irq = escc_update_irq_chn(s);
-    irq |= escc_update_irq_chn(s->otherchn);
-
-    trace_escc_update_irq(irq);
-    qemu_set_irq(s->irq, irq);
-}
-
-static void escc_reset_chn(ChannelState *s)
-{
-    int i;
-
-    s->reg = 0;
-    for (i = 0; i < SERIAL_REGS; i++) {
-        s->rregs[i] = 0;
-        s->wregs[i] = 0;
-    }
-    s->wregs[W_TXCTRL1] = TXCTRL1_1STOP; // 1X divisor, 1 stop bit, no parity
-    s->wregs[W_MINTR] = MINTR_RST_ALL;
-    s->wregs[W_CLOCK] = CLOCK_TRXC; // Synch mode tx clock = TRxC
-    s->wregs[W_MISC2] = MISC2_PLLDIS; // PLL disabled
-    s->wregs[W_EXTINT] = EXTINT_DCD | EXTINT_SYNCINT | EXTINT_CTSINT |
-        EXTINT_TXUNDRN | EXTINT_BRKINT; // Enable most interrupts
-    if (s->disabled)
-        s->rregs[R_STATUS] = STATUS_TXEMPTY | STATUS_DCD | STATUS_SYNC |
-            STATUS_CTS | STATUS_TXUNDRN;
-    else
-        s->rregs[R_STATUS] = STATUS_TXEMPTY | STATUS_TXUNDRN;
-    s->rregs[R_SPEC] = SPEC_BITS8 | SPEC_ALLSENT;
-
-    s->rx = s->tx = 0;
-    s->rxint = s->txint = 0;
-    s->rxint_under_svc = s->txint_under_svc = 0;
-    s->e0_mode = s->led_mode = s->caps_lock_mode = s->num_lock_mode = 0;
-    clear_queue(s);
-}
-
-static void escc_reset(DeviceState *d)
-{
-    SerialState *s = container_of(d, SerialState, busdev.qdev);
-
-    escc_reset_chn(&s->chn[0]);
-    escc_reset_chn(&s->chn[1]);
-}
-
-static inline void set_rxint(ChannelState *s)
-{
-    s->rxint = 1;
-    /* XXX: missing daisy chainnig: chn_b rx should have a lower priority
-       than chn_a rx/tx/special_condition service*/
-    s->rxint_under_svc = 1;
-    if (s->chn == chn_a) {
-        s->rregs[R_INTR] |= INTR_RXINTA;
-        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
-            s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA;
-        else
-            s->otherchn->rregs[R_IVEC] = IVEC_LORXINTA;
-    } else {
-        s->otherchn->rregs[R_INTR] |= INTR_RXINTB;
-        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
-            s->rregs[R_IVEC] = IVEC_HIRXINTB;
-        else
-            s->rregs[R_IVEC] = IVEC_LORXINTB;
-    }
-    escc_update_irq(s);
-}
-
-static inline void set_txint(ChannelState *s)
-{
-    s->txint = 1;
-    if (!s->rxint_under_svc) {
-        s->txint_under_svc = 1;
-        if (s->chn == chn_a) {
-            if (s->wregs[W_INTR] & INTR_TXINT) {
-                s->rregs[R_INTR] |= INTR_TXINTA;
-            }
-            if (s->wregs[W_MINTR] & MINTR_STATUSHI)
-                s->otherchn->rregs[R_IVEC] = IVEC_HITXINTA;
-            else
-                s->otherchn->rregs[R_IVEC] = IVEC_LOTXINTA;
-        } else {
-            s->rregs[R_IVEC] = IVEC_TXINTB;
-            if (s->wregs[W_INTR] & INTR_TXINT) {
-                s->otherchn->rregs[R_INTR] |= INTR_TXINTB;
-            }
-        }
-    escc_update_irq(s);
-    }
-}
-
-static inline void clr_rxint(ChannelState *s)
-{
-    s->rxint = 0;
-    s->rxint_under_svc = 0;
-    if (s->chn == chn_a) {
-        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
-            s->otherchn->rregs[R_IVEC] = IVEC_HINOINT;
-        else
-            s->otherchn->rregs[R_IVEC] = IVEC_LONOINT;
-        s->rregs[R_INTR] &= ~INTR_RXINTA;
-    } else {
-        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
-            s->rregs[R_IVEC] = IVEC_HINOINT;
-        else
-            s->rregs[R_IVEC] = IVEC_LONOINT;
-        s->otherchn->rregs[R_INTR] &= ~INTR_RXINTB;
-    }
-    if (s->txint)
-        set_txint(s);
-    escc_update_irq(s);
-}
-
-static inline void clr_txint(ChannelState *s)
-{
-    s->txint = 0;
-    s->txint_under_svc = 0;
-    if (s->chn == chn_a) {
-        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
-            s->otherchn->rregs[R_IVEC] = IVEC_HINOINT;
-        else
-            s->otherchn->rregs[R_IVEC] = IVEC_LONOINT;
-        s->rregs[R_INTR] &= ~INTR_TXINTA;
-    } else {
-        s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB;
-        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
-            s->rregs[R_IVEC] = IVEC_HINOINT;
-        else
-            s->rregs[R_IVEC] = IVEC_LONOINT;
-        s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB;
-    }
-    if (s->rxint)
-        set_rxint(s);
-    escc_update_irq(s);
-}
-
-static void escc_update_parameters(ChannelState *s)
-{
-    int speed, parity, data_bits, stop_bits;
-    QEMUSerialSetParams ssp;
-
-    if (!s->chr || s->type != ser)
-        return;
-
-    if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREN) {
-        if (s->wregs[W_TXCTRL1] & TXCTRL1_PAREV)
-            parity = 'E';
-        else
-            parity = 'O';
-    } else {
-        parity = 'N';
-    }
-    if ((s->wregs[W_TXCTRL1] & TXCTRL1_STPMSK) == TXCTRL1_2STOP)
-        stop_bits = 2;
-    else
-        stop_bits = 1;
-    switch (s->wregs[W_TXCTRL2] & TXCTRL2_BITMSK) {
-    case TXCTRL2_5BITS:
-        data_bits = 5;
-        break;
-    case TXCTRL2_7BITS:
-        data_bits = 7;
-        break;
-    case TXCTRL2_6BITS:
-        data_bits = 6;
-        break;
-    default:
-    case TXCTRL2_8BITS:
-        data_bits = 8;
-        break;
-    }
-    speed = s->clock / ((s->wregs[W_BRGLO] | (s->wregs[W_BRGHI] << 8)) + 2);
-    switch (s->wregs[W_TXCTRL1] & TXCTRL1_CLKMSK) {
-    case TXCTRL1_CLK1X:
-        break;
-    case TXCTRL1_CLK16X:
-        speed /= 16;
-        break;
-    case TXCTRL1_CLK32X:
-        speed /= 32;
-        break;
-    default:
-    case TXCTRL1_CLK64X:
-        speed /= 64;
-        break;
-    }
-    ssp.speed = speed;
-    ssp.parity = parity;
-    ssp.data_bits = data_bits;
-    ssp.stop_bits = stop_bits;
-    trace_escc_update_parameters(CHN_C(s), speed, parity, data_bits, stop_bits);
-    qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
-}
-
-static void escc_mem_write(void *opaque, hwaddr addr,
-                           uint64_t val, unsigned size)
-{
-    SerialState *serial = opaque;
-    ChannelState *s;
-    uint32_t saddr;
-    int newreg, channel;
-
-    val &= 0xff;
-    saddr = (addr >> serial->it_shift) & 1;
-    channel = (addr >> (serial->it_shift + 1)) & 1;
-    s = &serial->chn[channel];
-    switch (saddr) {
-    case SERIAL_CTRL:
-        trace_escc_mem_writeb_ctrl(CHN_C(s), s->reg, val & 0xff);
-        newreg = 0;
-        switch (s->reg) {
-        case W_CMD:
-            newreg = val & CMD_PTR_MASK;
-            val &= CMD_CMD_MASK;
-            switch (val) {
-            case CMD_HI:
-                newreg |= CMD_HI;
-                break;
-            case CMD_CLR_TXINT:
-                clr_txint(s);
-                break;
-            case CMD_CLR_IUS:
-                if (s->rxint_under_svc) {
-                    s->rxint_under_svc = 0;
-                    if (s->txint) {
-                        set_txint(s);
-                    }
-                } else if (s->txint_under_svc) {
-                    s->txint_under_svc = 0;
-                }
-                escc_update_irq(s);
-                break;
-            default:
-                break;
-            }
-            break;
-        case W_INTR ... W_RXCTRL:
-        case W_SYNC1 ... W_TXBUF:
-        case W_MISC1 ... W_CLOCK:
-        case W_MISC2 ... W_EXTINT:
-            s->wregs[s->reg] = val;
-            break;
-        case W_TXCTRL1:
-        case W_TXCTRL2:
-            s->wregs[s->reg] = val;
-            escc_update_parameters(s);
-            break;
-        case W_BRGLO:
-        case W_BRGHI:
-            s->wregs[s->reg] = val;
-            s->rregs[s->reg] = val;
-            escc_update_parameters(s);
-            break;
-        case W_MINTR:
-            switch (val & MINTR_RST_MASK) {
-            case 0:
-            default:
-                break;
-            case MINTR_RST_B:
-                escc_reset_chn(&serial->chn[0]);
-                return;
-            case MINTR_RST_A:
-                escc_reset_chn(&serial->chn[1]);
-                return;
-            case MINTR_RST_ALL:
-                escc_reset(&serial->busdev.qdev);
-                return;
-            }
-            break;
-        default:
-            break;
-        }
-        if (s->reg == 0)
-            s->reg = newreg;
-        else
-            s->reg = 0;
-        break;
-    case SERIAL_DATA:
-        trace_escc_mem_writeb_data(CHN_C(s), val);
-        s->tx = val;
-        if (s->wregs[W_TXCTRL2] & TXCTRL2_TXEN) { // tx enabled
-            if (s->chr)
-                qemu_chr_fe_write(s->chr, &s->tx, 1);
-            else if (s->type == kbd && !s->disabled) {
-                handle_kbd_command(s, val);
-            }
-        }
-        s->rregs[R_STATUS] |= STATUS_TXEMPTY; // Tx buffer empty
-        s->rregs[R_SPEC] |= SPEC_ALLSENT; // All sent
-        set_txint(s);
-        break;
-    default:
-        break;
-    }
-}
-
-static uint64_t escc_mem_read(void *opaque, hwaddr addr,
-                              unsigned size)
-{
-    SerialState *serial = opaque;
-    ChannelState *s;
-    uint32_t saddr;
-    uint32_t ret;
-    int channel;
-
-    saddr = (addr >> serial->it_shift) & 1;
-    channel = (addr >> (serial->it_shift + 1)) & 1;
-    s = &serial->chn[channel];
-    switch (saddr) {
-    case SERIAL_CTRL:
-        trace_escc_mem_readb_ctrl(CHN_C(s), s->reg, s->rregs[s->reg]);
-        ret = s->rregs[s->reg];
-        s->reg = 0;
-        return ret;
-    case SERIAL_DATA:
-        s->rregs[R_STATUS] &= ~STATUS_RXAV;
-        clr_rxint(s);
-        if (s->type == kbd || s->type == mouse)
-            ret = get_queue(s);
-        else
-            ret = s->rx;
-        trace_escc_mem_readb_data(CHN_C(s), ret);
-        if (s->chr)
-            qemu_chr_accept_input(s->chr);
-        return ret;
-    default:
-        break;
-    }
-    return 0;
-}
-
-static const MemoryRegionOps escc_mem_ops = {
-    .read = escc_mem_read,
-    .write = escc_mem_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .valid = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-static int serial_can_receive(void *opaque)
-{
-    ChannelState *s = opaque;
-    int ret;
-
-    if (((s->wregs[W_RXCTRL] & RXCTRL_RXEN) == 0) // Rx not enabled
-        || ((s->rregs[R_STATUS] & STATUS_RXAV) == STATUS_RXAV))
-        // char already available
-        ret = 0;
-    else
-        ret = 1;
-    return ret;
-}
-
-static void serial_receive_byte(ChannelState *s, int ch)
-{
-    trace_escc_serial_receive_byte(CHN_C(s), ch);
-    s->rregs[R_STATUS] |= STATUS_RXAV;
-    s->rx = ch;
-    set_rxint(s);
-}
-
-static void serial_receive_break(ChannelState *s)
-{
-    s->rregs[R_STATUS] |= STATUS_BRK;
-    escc_update_irq(s);
-}
-
-static void serial_receive1(void *opaque, const uint8_t *buf, int size)
-{
-    ChannelState *s = opaque;
-    serial_receive_byte(s, buf[0]);
-}
-
-static void serial_event(void *opaque, int event)
-{
-    ChannelState *s = opaque;
-    if (event == CHR_EVENT_BREAK)
-        serial_receive_break(s);
-}
-
-static const VMStateDescription vmstate_escc_chn = {
-    .name ="escc_chn",
-    .version_id = 2,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT32(vmstate_dummy, ChannelState),
-        VMSTATE_UINT32(reg, ChannelState),
-        VMSTATE_UINT32(rxint, ChannelState),
-        VMSTATE_UINT32(txint, ChannelState),
-        VMSTATE_UINT32(rxint_under_svc, ChannelState),
-        VMSTATE_UINT32(txint_under_svc, ChannelState),
-        VMSTATE_UINT8(rx, ChannelState),
-        VMSTATE_UINT8(tx, ChannelState),
-        VMSTATE_BUFFER(wregs, ChannelState),
-        VMSTATE_BUFFER(rregs, ChannelState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_escc = {
-    .name ="escc",
-    .version_id = 2,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField []) {
-        VMSTATE_STRUCT_ARRAY(chn, SerialState, 2, 2, vmstate_escc_chn,
-                             ChannelState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB,
-              CharDriverState *chrA, CharDriverState *chrB,
-              int clock, int it_shift)
-{
-    DeviceState *dev;
-    SysBusDevice *s;
-    SerialState *d;
-
-    dev = qdev_create(NULL, "escc");
-    qdev_prop_set_uint32(dev, "disabled", 0);
-    qdev_prop_set_uint32(dev, "frequency", clock);
-    qdev_prop_set_uint32(dev, "it_shift", it_shift);
-    qdev_prop_set_chr(dev, "chrB", chrB);
-    qdev_prop_set_chr(dev, "chrA", chrA);
-    qdev_prop_set_uint32(dev, "chnBtype", ser);
-    qdev_prop_set_uint32(dev, "chnAtype", ser);
-    qdev_init_nofail(dev);
-    s = SYS_BUS_DEVICE(dev);
-    sysbus_connect_irq(s, 0, irqB);
-    sysbus_connect_irq(s, 1, irqA);
-    if (base) {
-        sysbus_mmio_map(s, 0, base);
-    }
-
-    d = FROM_SYSBUS(SerialState, s);
-    return &d->mmio;
-}
-
-static const uint8_t keycodes[128] = {
-    127, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 53,
-    54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 89, 76, 77, 78,
-    79, 80, 81, 82, 83, 84, 85, 86, 87, 42, 99, 88, 100, 101, 102, 103,
-    104, 105, 106, 107, 108, 109, 110, 47, 19, 121, 119, 5, 6, 8, 10, 12,
-    14, 16, 17, 18, 7, 98, 23, 68, 69, 70, 71, 91, 92, 93, 125, 112,
-    113, 114, 94, 50, 0, 0, 124, 9, 11, 0, 0, 0, 0, 0, 0, 0,
-    90, 0, 46, 22, 13, 111, 52, 20, 96, 24, 28, 74, 27, 123, 44, 66,
-    0, 45, 2, 4, 48, 0, 0, 21, 0, 0, 0, 0, 0, 120, 122, 67,
-};
-
-static const uint8_t e0_keycodes[128] = {
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 76, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 109, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 68, 69, 70, 0, 91, 0, 93, 0, 112,
-    113, 114, 94, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    1, 3, 25, 26, 49, 52, 72, 73, 97, 99, 111, 118, 120, 122, 67, 0,
-};
-
-static void sunkbd_event(void *opaque, int ch)
-{
-    ChannelState *s = opaque;
-    int release = ch & 0x80;
-
-    trace_escc_sunkbd_event_in(ch);
-    switch (ch) {
-    case 58: // Caps lock press
-        s->caps_lock_mode ^= 1;
-        if (s->caps_lock_mode == 2)
-            return; // Drop second press
-        break;
-    case 69: // Num lock press
-        s->num_lock_mode ^= 1;
-        if (s->num_lock_mode == 2)
-            return; // Drop second press
-        break;
-    case 186: // Caps lock release
-        s->caps_lock_mode ^= 2;
-        if (s->caps_lock_mode == 3)
-            return; // Drop first release
-        break;
-    case 197: // Num lock release
-        s->num_lock_mode ^= 2;
-        if (s->num_lock_mode == 3)
-            return; // Drop first release
-        break;
-    case 0xe0:
-        s->e0_mode = 1;
-        return;
-    default:
-        break;
-    }
-    if (s->e0_mode) {
-        s->e0_mode = 0;
-        ch = e0_keycodes[ch & 0x7f];
-    } else {
-        ch = keycodes[ch & 0x7f];
-    }
-    trace_escc_sunkbd_event_out(ch);
-    put_queue(s, ch | release);
-}
-
-static void handle_kbd_command(ChannelState *s, int val)
-{
-    trace_escc_kbd_command(val);
-    if (s->led_mode) { // Ignore led byte
-        s->led_mode = 0;
-        return;
-    }
-    switch (val) {
-    case 1: // Reset, return type code
-        clear_queue(s);
-        put_queue(s, 0xff);
-        put_queue(s, 4); // Type 4
-        put_queue(s, 0x7f);
-        break;
-    case 0xe: // Set leds
-        s->led_mode = 1;
-        break;
-    case 7: // Query layout
-    case 0xf:
-        clear_queue(s);
-        put_queue(s, 0xfe);
-        put_queue(s, 0); // XXX, layout?
-        break;
-    default:
-        break;
-    }
-}
-
-static void sunmouse_event(void *opaque,
-                               int dx, int dy, int dz, int buttons_state)
-{
-    ChannelState *s = opaque;
-    int ch;
-
-    trace_escc_sunmouse_event(dx, dy, buttons_state);
-    ch = 0x80 | 0x7; /* protocol start byte, no buttons pressed */
-
-    if (buttons_state & MOUSE_EVENT_LBUTTON)
-        ch ^= 0x4;
-    if (buttons_state & MOUSE_EVENT_MBUTTON)
-        ch ^= 0x2;
-    if (buttons_state & MOUSE_EVENT_RBUTTON)
-        ch ^= 0x1;
-
-    put_queue(s, ch);
-
-    ch = dx;
-
-    if (ch > 127)
-        ch = 127;
-    else if (ch < -127)
-        ch = -127;
-
-    put_queue(s, ch & 0xff);
-
-    ch = -dy;
-
-    if (ch > 127)
-        ch = 127;
-    else if (ch < -127)
-        ch = -127;
-
-    put_queue(s, ch & 0xff);
-
-    // MSC protocol specify two extra motion bytes
-
-    put_queue(s, 0);
-    put_queue(s, 0);
-}
-
-void slavio_serial_ms_kbd_init(hwaddr base, qemu_irq irq,
-                               int disabled, int clock, int it_shift)
-{
-    DeviceState *dev;
-    SysBusDevice *s;
-
-    dev = qdev_create(NULL, "escc");
-    qdev_prop_set_uint32(dev, "disabled", disabled);
-    qdev_prop_set_uint32(dev, "frequency", clock);
-    qdev_prop_set_uint32(dev, "it_shift", it_shift);
-    qdev_prop_set_chr(dev, "chrB", NULL);
-    qdev_prop_set_chr(dev, "chrA", NULL);
-    qdev_prop_set_uint32(dev, "chnBtype", mouse);
-    qdev_prop_set_uint32(dev, "chnAtype", kbd);
-    qdev_init_nofail(dev);
-    s = SYS_BUS_DEVICE(dev);
-    sysbus_connect_irq(s, 0, irq);
-    sysbus_connect_irq(s, 1, irq);
-    sysbus_mmio_map(s, 0, base);
-}
-
-static int escc_init1(SysBusDevice *dev)
-{
-    SerialState *s = FROM_SYSBUS(SerialState, dev);
-    unsigned int i;
-
-    s->chn[0].disabled = s->disabled;
-    s->chn[1].disabled = s->disabled;
-    for (i = 0; i < 2; i++) {
-        sysbus_init_irq(dev, &s->chn[i].irq);
-        s->chn[i].chn = 1 - i;
-        s->chn[i].clock = s->frequency / 2;
-        if (s->chn[i].chr) {
-            qemu_chr_add_handlers(s->chn[i].chr, serial_can_receive,
-                                  serial_receive1, serial_event, &s->chn[i]);
-        }
-    }
-    s->chn[0].otherchn = &s->chn[1];
-    s->chn[1].otherchn = &s->chn[0];
-
-    memory_region_init_io(&s->mmio, &escc_mem_ops, s, "escc",
-                          ESCC_SIZE << s->it_shift);
-    sysbus_init_mmio(dev, &s->mmio);
-
-    if (s->chn[0].type == mouse) {
-        qemu_add_mouse_event_handler(sunmouse_event, &s->chn[0], 0,
-                                     "QEMU Sun Mouse");
-    }
-    if (s->chn[1].type == kbd) {
-        qemu_add_kbd_event_handler(sunkbd_event, &s->chn[1]);
-    }
-
-    return 0;
-}
-
-static Property escc_properties[] = {
-    DEFINE_PROP_UINT32("frequency", SerialState, frequency,   0),
-    DEFINE_PROP_UINT32("it_shift",  SerialState, it_shift,    0),
-    DEFINE_PROP_UINT32("disabled",  SerialState, disabled,    0),
-    DEFINE_PROP_UINT32("chnBtype",  SerialState, chn[0].type, 0),
-    DEFINE_PROP_UINT32("chnAtype",  SerialState, chn[1].type, 0),
-    DEFINE_PROP_CHR("chrB", SerialState, chn[0].chr),
-    DEFINE_PROP_CHR("chrA", SerialState, chn[1].chr),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void escc_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = escc_init1;
-    dc->reset = escc_reset;
-    dc->vmsd = &vmstate_escc;
-    dc->props = escc_properties;
-}
-
-static const TypeInfo escc_info = {
-    .name          = "escc",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(SerialState),
-    .class_init    = escc_class_init,
-};
-
-static void escc_register_types(void)
-{
-    type_register_static(&escc_info);
-}
-
-type_init(escc_register_types)
diff --git a/hw/esp-pci.c b/hw/esp-pci.c
deleted file mode 100644 (file)
index 3ca5c8c..0000000
+++ /dev/null
@@ -1,518 +0,0 @@
-/*
- * QEMU ESP/NCR53C9x emulation
- *
- * Copyright (c) 2005-2006 Fabrice Bellard
- * Copyright (c) 2012 Herve Poussineau
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/pci/pci.h"
-#include "hw/nvram/eeprom93xx.h"
-#include "hw/scsi/esp.h"
-#include "trace.h"
-#include "qemu/log.h"
-
-#define TYPE_AM53C974_DEVICE "am53c974"
-
-#define DMA_CMD   0x0
-#define DMA_STC   0x1
-#define DMA_SPA   0x2
-#define DMA_WBC   0x3
-#define DMA_WAC   0x4
-#define DMA_STAT  0x5
-#define DMA_SMDLA 0x6
-#define DMA_WMAC  0x7
-
-#define DMA_CMD_MASK   0x03
-#define DMA_CMD_DIAG   0x04
-#define DMA_CMD_MDL    0x10
-#define DMA_CMD_INTE_P 0x20
-#define DMA_CMD_INTE_D 0x40
-#define DMA_CMD_DIR    0x80
-
-#define DMA_STAT_PWDN    0x01
-#define DMA_STAT_ERROR   0x02
-#define DMA_STAT_ABORT   0x04
-#define DMA_STAT_DONE    0x08
-#define DMA_STAT_SCSIINT 0x10
-#define DMA_STAT_BCMBLT  0x20
-
-#define SBAC_STATUS 0x1000
-
-typedef struct PCIESPState {
-    PCIDevice dev;
-    MemoryRegion io;
-    uint32_t dma_regs[8];
-    uint32_t sbac;
-    ESPState esp;
-} PCIESPState;
-
-static void esp_pci_handle_idle(PCIESPState *pci, uint32_t val)
-{
-    trace_esp_pci_dma_idle(val);
-    esp_dma_enable(&pci->esp, 0, 0);
-}
-
-static void esp_pci_handle_blast(PCIESPState *pci, uint32_t val)
-{
-    trace_esp_pci_dma_blast(val);
-    qemu_log_mask(LOG_UNIMP, "am53c974: cmd BLAST not implemented\n");
-}
-
-static void esp_pci_handle_abort(PCIESPState *pci, uint32_t val)
-{
-    trace_esp_pci_dma_abort(val);
-    if (pci->esp.current_req) {
-        scsi_req_cancel(pci->esp.current_req);
-    }
-}
-
-static void esp_pci_handle_start(PCIESPState *pci, uint32_t val)
-{
-    trace_esp_pci_dma_start(val);
-
-    pci->dma_regs[DMA_WBC] = pci->dma_regs[DMA_STC];
-    pci->dma_regs[DMA_WAC] = pci->dma_regs[DMA_SPA];
-    pci->dma_regs[DMA_WMAC] = pci->dma_regs[DMA_SMDLA];
-
-    pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_BCMBLT | DMA_STAT_SCSIINT
-                               | DMA_STAT_DONE | DMA_STAT_ABORT
-                               | DMA_STAT_ERROR | DMA_STAT_PWDN);
-
-    esp_dma_enable(&pci->esp, 0, 1);
-}
-
-static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val)
-{
-    trace_esp_pci_dma_write(saddr, pci->dma_regs[saddr], val);
-    switch (saddr) {
-    case DMA_CMD:
-        pci->dma_regs[saddr] = val;
-        switch (val & DMA_CMD_MASK) {
-        case 0x0: /* IDLE */
-            esp_pci_handle_idle(pci, val);
-            break;
-        case 0x1: /* BLAST */
-            esp_pci_handle_blast(pci, val);
-            break;
-        case 0x2: /* ABORT */
-            esp_pci_handle_abort(pci, val);
-            break;
-        case 0x3: /* START */
-            esp_pci_handle_start(pci, val);
-            break;
-        default: /* can't happen */
-            abort();
-        }
-        break;
-    case DMA_STC:
-    case DMA_SPA:
-    case DMA_SMDLA:
-        pci->dma_regs[saddr] = val;
-        break;
-    case DMA_STAT:
-        if (!(pci->sbac & SBAC_STATUS)) {
-            /* clear some bits on write */
-            uint32_t mask = DMA_STAT_ERROR | DMA_STAT_ABORT | DMA_STAT_DONE;
-            pci->dma_regs[DMA_STAT] &= ~(val & mask);
-        }
-        break;
-    default:
-        trace_esp_pci_error_invalid_write_dma(val, saddr);
-        return;
-    }
-}
-
-static uint32_t esp_pci_dma_read(PCIESPState *pci, uint32_t saddr)
-{
-    uint32_t val;
-
-    val = pci->dma_regs[saddr];
-    if (saddr == DMA_STAT) {
-        if (pci->esp.rregs[ESP_RSTAT] & STAT_INT) {
-            val |= DMA_STAT_SCSIINT;
-        }
-        if (pci->sbac & SBAC_STATUS) {
-            pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_ERROR | DMA_STAT_ABORT |
-                                         DMA_STAT_DONE);
-        }
-    }
-
-    trace_esp_pci_dma_read(saddr, val);
-    return val;
-}
-
-static void esp_pci_io_write(void *opaque, hwaddr addr,
-                             uint64_t val, unsigned int size)
-{
-    PCIESPState *pci = opaque;
-
-    if (size < 4 || addr & 3) {
-        /* need to upgrade request: we only support 4-bytes accesses */
-        uint32_t current = 0, mask;
-        int shift;
-
-        if (addr < 0x40) {
-            current = pci->esp.wregs[addr >> 2];
-        } else if (addr < 0x60) {
-            current = pci->dma_regs[(addr - 0x40) >> 2];
-        } else if (addr < 0x74) {
-            current = pci->sbac;
-        }
-
-        shift = (4 - size) * 8;
-        mask = (~(uint32_t)0 << shift) >> shift;
-
-        shift = ((4 - (addr & 3)) & 3) * 8;
-        val <<= shift;
-        val |= current & ~(mask << shift);
-        addr &= ~3;
-        size = 4;
-    }
-
-    if (addr < 0x40) {
-        /* SCSI core reg */
-        esp_reg_write(&pci->esp, addr >> 2, val);
-    } else if (addr < 0x60) {
-        /* PCI DMA CCB */
-        esp_pci_dma_write(pci, (addr - 0x40) >> 2, val);
-    } else if (addr == 0x70) {
-        /* DMA SCSI Bus and control */
-        trace_esp_pci_sbac_write(pci->sbac, val);
-        pci->sbac = val;
-    } else {
-        trace_esp_pci_error_invalid_write((int)addr);
-    }
-}
-
-static uint64_t esp_pci_io_read(void *opaque, hwaddr addr,
-                                unsigned int size)
-{
-    PCIESPState *pci = opaque;
-    uint32_t ret;
-
-    if (addr < 0x40) {
-        /* SCSI core reg */
-        ret = esp_reg_read(&pci->esp, addr >> 2);
-    } else if (addr < 0x60) {
-        /* PCI DMA CCB */
-        ret = esp_pci_dma_read(pci, (addr - 0x40) >> 2);
-    } else if (addr == 0x70) {
-        /* DMA SCSI Bus and control */
-        trace_esp_pci_sbac_read(pci->sbac);
-        ret = pci->sbac;
-    } else {
-        /* Invalid region */
-        trace_esp_pci_error_invalid_read((int)addr);
-        ret = 0;
-    }
-
-    /* give only requested data */
-    ret >>= (addr & 3) * 8;
-    ret &= ~(~(uint64_t)0 << (8 * size));
-
-    return ret;
-}
-
-static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len,
-                                  DMADirection dir)
-{
-    dma_addr_t addr;
-    DMADirection expected_dir;
-
-    if (pci->dma_regs[DMA_CMD] & DMA_CMD_DIR) {
-        expected_dir = DMA_DIRECTION_FROM_DEVICE;
-    } else {
-        expected_dir = DMA_DIRECTION_TO_DEVICE;
-    }
-
-    if (dir != expected_dir) {
-        trace_esp_pci_error_invalid_dma_direction();
-        return;
-    }
-
-    if (pci->dma_regs[DMA_STAT] & DMA_CMD_MDL) {
-        qemu_log_mask(LOG_UNIMP, "am53c974: MDL transfer not implemented\n");
-    }
-
-    addr = pci->dma_regs[DMA_SPA];
-    if (pci->dma_regs[DMA_WBC] < len) {
-        len = pci->dma_regs[DMA_WBC];
-    }
-
-    pci_dma_rw(&pci->dev, addr, buf, len, dir);
-
-    /* update status registers */
-    pci->dma_regs[DMA_WBC] -= len;
-    pci->dma_regs[DMA_WAC] += len;
-}
-
-static void esp_pci_dma_memory_read(void *opaque, uint8_t *buf, int len)
-{
-    PCIESPState *pci = opaque;
-    esp_pci_dma_memory_rw(pci, buf, len, DMA_DIRECTION_TO_DEVICE);
-}
-
-static void esp_pci_dma_memory_write(void *opaque, uint8_t *buf, int len)
-{
-    PCIESPState *pci = opaque;
-    esp_pci_dma_memory_rw(pci, buf, len, DMA_DIRECTION_FROM_DEVICE);
-}
-
-static const MemoryRegionOps esp_pci_io_ops = {
-    .read = esp_pci_io_read,
-    .write = esp_pci_io_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 4,
-    },
-};
-
-static void esp_pci_hard_reset(DeviceState *dev)
-{
-    PCIESPState *pci = DO_UPCAST(PCIESPState, dev.qdev, dev);
-    esp_hard_reset(&pci->esp);
-    pci->dma_regs[DMA_CMD] &= ~(DMA_CMD_DIR | DMA_CMD_INTE_D | DMA_CMD_INTE_P
-                              | DMA_CMD_MDL | DMA_CMD_DIAG | DMA_CMD_MASK);
-    pci->dma_regs[DMA_WBC] &= ~0xffff;
-    pci->dma_regs[DMA_WAC] = 0xffffffff;
-    pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_BCMBLT | DMA_STAT_SCSIINT
-                               | DMA_STAT_DONE | DMA_STAT_ABORT
-                               | DMA_STAT_ERROR);
-    pci->dma_regs[DMA_WMAC] = 0xfffffffd;
-}
-
-static const VMStateDescription vmstate_esp_pci_scsi = {
-    .name = "pciespscsi",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .fields = (VMStateField[]) {
-        VMSTATE_PCI_DEVICE(dev, PCIESPState),
-        VMSTATE_BUFFER_UNSAFE(dma_regs, PCIESPState, 0, 8 * sizeof(uint32_t)),
-        VMSTATE_STRUCT(esp, PCIESPState, 0, vmstate_esp, ESPState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void esp_pci_command_complete(SCSIRequest *req, uint32_t status,
-                                     size_t resid)
-{
-    ESPState *s = req->hba_private;
-    PCIESPState *pci = container_of(s, PCIESPState, esp);
-
-    esp_command_complete(req, status, resid);
-    pci->dma_regs[DMA_WBC] = 0;
-    pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE;
-}
-
-static const struct SCSIBusInfo esp_pci_scsi_info = {
-    .tcq = false,
-    .max_target = ESP_MAX_DEVS,
-    .max_lun = 7,
-
-    .transfer_data = esp_transfer_data,
-    .complete = esp_pci_command_complete,
-    .cancel = esp_request_cancelled,
-};
-
-static int esp_pci_scsi_init(PCIDevice *dev)
-{
-    PCIESPState *pci = DO_UPCAST(PCIESPState, dev, dev);
-    ESPState *s = &pci->esp;
-    uint8_t *pci_conf;
-
-    pci_conf = pci->dev.config;
-
-    /* Interrupt pin A */
-    pci_conf[PCI_INTERRUPT_PIN] = 0x01;
-
-    s->dma_memory_read = esp_pci_dma_memory_read;
-    s->dma_memory_write = esp_pci_dma_memory_write;
-    s->dma_opaque = pci;
-    s->chip_id = TCHI_AM53C974;
-    memory_region_init_io(&pci->io, &esp_pci_io_ops, pci, "esp-io", 0x80);
-
-    pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->io);
-    s->irq = pci->dev.irq[0];
-
-    scsi_bus_new(&s->bus, &dev->qdev, &esp_pci_scsi_info);
-    if (!dev->qdev.hotplugged) {
-        return scsi_bus_legacy_handle_cmdline(&s->bus);
-    }
-    return 0;
-}
-
-static void esp_pci_scsi_uninit(PCIDevice *d)
-{
-    PCIESPState *pci = DO_UPCAST(PCIESPState, dev, d);
-
-    memory_region_destroy(&pci->io);
-}
-
-static void esp_pci_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = esp_pci_scsi_init;
-    k->exit = esp_pci_scsi_uninit;
-    k->vendor_id = PCI_VENDOR_ID_AMD;
-    k->device_id = PCI_DEVICE_ID_AMD_SCSI;
-    k->revision = 0x10;
-    k->class_id = PCI_CLASS_STORAGE_SCSI;
-    dc->desc = "AMD Am53c974 PCscsi-PCI SCSI adapter";
-    dc->reset = esp_pci_hard_reset;
-    dc->vmsd = &vmstate_esp_pci_scsi;
-}
-
-static const TypeInfo esp_pci_info = {
-    .name = TYPE_AM53C974_DEVICE,
-    .parent = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIESPState),
-    .class_init = esp_pci_class_init,
-};
-
-typedef struct {
-    PCIESPState pci;
-    eeprom_t *eeprom;
-} DC390State;
-
-#define TYPE_DC390_DEVICE "dc390"
-#define DC390(obj) \
-    OBJECT_CHECK(DC390State, obj, TYPE_DC390_DEVICE)
-
-#define EE_ADAPT_SCSI_ID 64
-#define EE_MODE2         65
-#define EE_DELAY         66
-#define EE_TAG_CMD_NUM   67
-#define EE_ADAPT_OPTIONS 68
-#define EE_BOOT_SCSI_ID  69
-#define EE_BOOT_SCSI_LUN 70
-#define EE_CHKSUM1       126
-#define EE_CHKSUM2       127
-
-#define EE_ADAPT_OPTION_F6_F8_AT_BOOT   0x01
-#define EE_ADAPT_OPTION_BOOT_FROM_CDROM 0x02
-#define EE_ADAPT_OPTION_INT13           0x04
-#define EE_ADAPT_OPTION_SCAM_SUPPORT    0x08
-
-
-static uint32_t dc390_read_config(PCIDevice *dev, uint32_t addr, int l)
-{
-    DC390State *pci = DC390(dev);
-    uint32_t val;
-
-    val = pci_default_read_config(dev, addr, l);
-
-    if (addr == 0x00 && l == 1) {
-        /* First byte of address space is AND-ed with EEPROM DO line */
-        if (!eeprom93xx_read(pci->eeprom)) {
-            val &= ~0xff;
-        }
-    }
-
-    return val;
-}
-
-static void dc390_write_config(PCIDevice *dev,
-                               uint32_t addr, uint32_t val, int l)
-{
-    DC390State *pci = DC390(dev);
-    if (addr == 0x80) {
-        /* EEPROM write */
-        int eesk = val & 0x80 ? 1 : 0;
-        int eedi = val & 0x40 ? 1 : 0;
-        eeprom93xx_write(pci->eeprom, 1, eesk, eedi);
-    } else if (addr == 0xc0) {
-        /* EEPROM CS low */
-        eeprom93xx_write(pci->eeprom, 0, 0, 0);
-    } else {
-        pci_default_write_config(dev, addr, val, l);
-    }
-}
-
-static int dc390_scsi_init(PCIDevice *dev)
-{
-    DC390State *pci = DC390(dev);
-    uint8_t *contents;
-    uint16_t chksum = 0;
-    int i, ret;
-
-    /* init base class */
-    ret = esp_pci_scsi_init(dev);
-    if (ret < 0) {
-        return ret;
-    }
-
-    /* EEPROM */
-    pci->eeprom = eeprom93xx_new(DEVICE(dev), 64);
-
-    /* set default eeprom values */
-    contents = (uint8_t *)eeprom93xx_data(pci->eeprom);
-
-    for (i = 0; i < 16; i++) {
-        contents[i * 2] = 0x57;
-        contents[i * 2 + 1] = 0x00;
-    }
-    contents[EE_ADAPT_SCSI_ID] = 7;
-    contents[EE_MODE2] = 0x0f;
-    contents[EE_TAG_CMD_NUM] = 0x04;
-    contents[EE_ADAPT_OPTIONS] = EE_ADAPT_OPTION_F6_F8_AT_BOOT
-                               | EE_ADAPT_OPTION_BOOT_FROM_CDROM
-                               | EE_ADAPT_OPTION_INT13;
-
-    /* update eeprom checksum */
-    for (i = 0; i < EE_CHKSUM1; i += 2) {
-        chksum += contents[i] + (((uint16_t)contents[i + 1]) << 8);
-    }
-    chksum = 0x1234 - chksum;
-    contents[EE_CHKSUM1] = chksum & 0xff;
-    contents[EE_CHKSUM2] = chksum >> 8;
-
-    return 0;
-}
-
-static void dc390_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = dc390_scsi_init;
-    k->config_read = dc390_read_config;
-    k->config_write = dc390_write_config;
-    dc->desc = "Tekram DC-390 SCSI adapter";
-}
-
-static const TypeInfo dc390_info = {
-    .name = "dc390",
-    .parent = TYPE_AM53C974_DEVICE,
-    .instance_size = sizeof(DC390State),
-    .class_init = dc390_class_init,
-};
-
-static void esp_pci_register_types(void)
-{
-    type_register_static(&esp_pci_info);
-    type_register_static(&dc390_info);
-}
-
-type_init(esp_pci_register_types)
diff --git a/hw/esp.c b/hw/esp.c
deleted file mode 100644 (file)
index 17adbec..0000000
--- a/hw/esp.c
+++ /dev/null
@@ -1,727 +0,0 @@
-/*
- * QEMU ESP/NCR53C9x emulation
- *
- * Copyright (c) 2005-2006 Fabrice Bellard
- * Copyright (c) 2012 Herve Poussineau
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/sysbus.h"
-#include "hw/scsi/esp.h"
-#include "trace.h"
-#include "qemu/log.h"
-
-/*
- * On Sparc32, this is the ESP (NCR53C90) part of chip STP2000 (Master I/O),
- * also produced as NCR89C100. See
- * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
- * and
- * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt
- */
-
-static void esp_raise_irq(ESPState *s)
-{
-    if (!(s->rregs[ESP_RSTAT] & STAT_INT)) {
-        s->rregs[ESP_RSTAT] |= STAT_INT;
-        qemu_irq_raise(s->irq);
-        trace_esp_raise_irq();
-    }
-}
-
-static void esp_lower_irq(ESPState *s)
-{
-    if (s->rregs[ESP_RSTAT] & STAT_INT) {
-        s->rregs[ESP_RSTAT] &= ~STAT_INT;
-        qemu_irq_lower(s->irq);
-        trace_esp_lower_irq();
-    }
-}
-
-void esp_dma_enable(ESPState *s, int irq, int level)
-{
-    if (level) {
-        s->dma_enabled = 1;
-        trace_esp_dma_enable();
-        if (s->dma_cb) {
-            s->dma_cb(s);
-            s->dma_cb = NULL;
-        }
-    } else {
-        trace_esp_dma_disable();
-        s->dma_enabled = 0;
-    }
-}
-
-void esp_request_cancelled(SCSIRequest *req)
-{
-    ESPState *s = req->hba_private;
-
-    if (req == s->current_req) {
-        scsi_req_unref(s->current_req);
-        s->current_req = NULL;
-        s->current_dev = NULL;
-    }
-}
-
-static uint32_t get_cmd(ESPState *s, uint8_t *buf)
-{
-    uint32_t dmalen;
-    int target;
-
-    target = s->wregs[ESP_WBUSID] & BUSID_DID;
-    if (s->dma) {
-        dmalen = s->rregs[ESP_TCLO];
-        dmalen |= s->rregs[ESP_TCMID] << 8;
-        dmalen |= s->rregs[ESP_TCHI] << 16;
-        s->dma_memory_read(s->dma_opaque, buf, dmalen);
-    } else {
-        dmalen = s->ti_size;
-        memcpy(buf, s->ti_buf, dmalen);
-        buf[0] = buf[2] >> 5;
-    }
-    trace_esp_get_cmd(dmalen, target);
-
-    s->ti_size = 0;
-    s->ti_rptr = 0;
-    s->ti_wptr = 0;
-
-    if (s->current_req) {
-        /* Started a new command before the old one finished.  Cancel it.  */
-        scsi_req_cancel(s->current_req);
-        s->async_len = 0;
-    }
-
-    s->current_dev = scsi_device_find(&s->bus, 0, target, 0);
-    if (!s->current_dev) {
-        // No such drive
-        s->rregs[ESP_RSTAT] = 0;
-        s->rregs[ESP_RINTR] = INTR_DC;
-        s->rregs[ESP_RSEQ] = SEQ_0;
-        esp_raise_irq(s);
-        return 0;
-    }
-    return dmalen;
-}
-
-static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid)
-{
-    int32_t datalen;
-    int lun;
-    SCSIDevice *current_lun;
-
-    trace_esp_do_busid_cmd(busid);
-    lun = busid & 7;
-    current_lun = scsi_device_find(&s->bus, 0, s->current_dev->id, lun);
-    s->current_req = scsi_req_new(current_lun, 0, lun, buf, s);
-    datalen = scsi_req_enqueue(s->current_req);
-    s->ti_size = datalen;
-    if (datalen != 0) {
-        s->rregs[ESP_RSTAT] = STAT_TC;
-        s->dma_left = 0;
-        s->dma_counter = 0;
-        if (datalen > 0) {
-            s->rregs[ESP_RSTAT] |= STAT_DI;
-        } else {
-            s->rregs[ESP_RSTAT] |= STAT_DO;
-        }
-        scsi_req_continue(s->current_req);
-    }
-    s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
-    s->rregs[ESP_RSEQ] = SEQ_CD;
-    esp_raise_irq(s);
-}
-
-static void do_cmd(ESPState *s, uint8_t *buf)
-{
-    uint8_t busid = buf[0];
-
-    do_busid_cmd(s, &buf[1], busid);
-}
-
-static void handle_satn(ESPState *s)
-{
-    uint8_t buf[32];
-    int len;
-
-    if (s->dma && !s->dma_enabled) {
-        s->dma_cb = handle_satn;
-        return;
-    }
-    len = get_cmd(s, buf);
-    if (len)
-        do_cmd(s, buf);
-}
-
-static void handle_s_without_atn(ESPState *s)
-{
-    uint8_t buf[32];
-    int len;
-
-    if (s->dma && !s->dma_enabled) {
-        s->dma_cb = handle_s_without_atn;
-        return;
-    }
-    len = get_cmd(s, buf);
-    if (len) {
-        do_busid_cmd(s, buf, 0);
-    }
-}
-
-static void handle_satn_stop(ESPState *s)
-{
-    if (s->dma && !s->dma_enabled) {
-        s->dma_cb = handle_satn_stop;
-        return;
-    }
-    s->cmdlen = get_cmd(s, s->cmdbuf);
-    if (s->cmdlen) {
-        trace_esp_handle_satn_stop(s->cmdlen);
-        s->do_cmd = 1;
-        s->rregs[ESP_RSTAT] = STAT_TC | STAT_CD;
-        s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
-        s->rregs[ESP_RSEQ] = SEQ_CD;
-        esp_raise_irq(s);
-    }
-}
-
-static void write_response(ESPState *s)
-{
-    trace_esp_write_response(s->status);
-    s->ti_buf[0] = s->status;
-    s->ti_buf[1] = 0;
-    if (s->dma) {
-        s->dma_memory_write(s->dma_opaque, s->ti_buf, 2);
-        s->rregs[ESP_RSTAT] = STAT_TC | STAT_ST;
-        s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
-        s->rregs[ESP_RSEQ] = SEQ_CD;
-    } else {
-        s->ti_size = 2;
-        s->ti_rptr = 0;
-        s->ti_wptr = 0;
-        s->rregs[ESP_RFLAGS] = 2;
-    }
-    esp_raise_irq(s);
-}
-
-static void esp_dma_done(ESPState *s)
-{
-    s->rregs[ESP_RSTAT] |= STAT_TC;
-    s->rregs[ESP_RINTR] = INTR_BS;
-    s->rregs[ESP_RSEQ] = 0;
-    s->rregs[ESP_RFLAGS] = 0;
-    s->rregs[ESP_TCLO] = 0;
-    s->rregs[ESP_TCMID] = 0;
-    s->rregs[ESP_TCHI] = 0;
-    esp_raise_irq(s);
-}
-
-static void esp_do_dma(ESPState *s)
-{
-    uint32_t len;
-    int to_device;
-
-    to_device = (s->ti_size < 0);
-    len = s->dma_left;
-    if (s->do_cmd) {
-        trace_esp_do_dma(s->cmdlen, len);
-        s->dma_memory_read(s->dma_opaque, &s->cmdbuf[s->cmdlen], len);
-        s->ti_size = 0;
-        s->cmdlen = 0;
-        s->do_cmd = 0;
-        do_cmd(s, s->cmdbuf);
-        return;
-    }
-    if (s->async_len == 0) {
-        /* Defer until data is available.  */
-        return;
-    }
-    if (len > s->async_len) {
-        len = s->async_len;
-    }
-    if (to_device) {
-        s->dma_memory_read(s->dma_opaque, s->async_buf, len);
-    } else {
-        s->dma_memory_write(s->dma_opaque, s->async_buf, len);
-    }
-    s->dma_left -= len;
-    s->async_buf += len;
-    s->async_len -= len;
-    if (to_device)
-        s->ti_size += len;
-    else
-        s->ti_size -= len;
-    if (s->async_len == 0) {
-        scsi_req_continue(s->current_req);
-        /* If there is still data to be read from the device then
-           complete the DMA operation immediately.  Otherwise defer
-           until the scsi layer has completed.  */
-        if (to_device || s->dma_left != 0 || s->ti_size == 0) {
-            return;
-        }
-    }
-
-    /* Partially filled a scsi buffer. Complete immediately.  */
-    esp_dma_done(s);
-}
-
-void esp_command_complete(SCSIRequest *req, uint32_t status,
-                                 size_t resid)
-{
-    ESPState *s = req->hba_private;
-
-    trace_esp_command_complete();
-    if (s->ti_size != 0) {
-        trace_esp_command_complete_unexpected();
-    }
-    s->ti_size = 0;
-    s->dma_left = 0;
-    s->async_len = 0;
-    if (status) {
-        trace_esp_command_complete_fail();
-    }
-    s->status = status;
-    s->rregs[ESP_RSTAT] = STAT_ST;
-    esp_dma_done(s);
-    if (s->current_req) {
-        scsi_req_unref(s->current_req);
-        s->current_req = NULL;
-        s->current_dev = NULL;
-    }
-}
-
-void esp_transfer_data(SCSIRequest *req, uint32_t len)
-{
-    ESPState *s = req->hba_private;
-
-    trace_esp_transfer_data(s->dma_left, s->ti_size);
-    s->async_len = len;
-    s->async_buf = scsi_req_get_buf(req);
-    if (s->dma_left) {
-        esp_do_dma(s);
-    } else if (s->dma_counter != 0 && s->ti_size <= 0) {
-        /* If this was the last part of a DMA transfer then the
-           completion interrupt is deferred to here.  */
-        esp_dma_done(s);
-    }
-}
-
-static void handle_ti(ESPState *s)
-{
-    uint32_t dmalen, minlen;
-
-    if (s->dma && !s->dma_enabled) {
-        s->dma_cb = handle_ti;
-        return;
-    }
-
-    dmalen = s->rregs[ESP_TCLO];
-    dmalen |= s->rregs[ESP_TCMID] << 8;
-    dmalen |= s->rregs[ESP_TCHI] << 16;
-    if (dmalen==0) {
-      dmalen=0x10000;
-    }
-    s->dma_counter = dmalen;
-
-    if (s->do_cmd)
-        minlen = (dmalen < 32) ? dmalen : 32;
-    else if (s->ti_size < 0)
-        minlen = (dmalen < -s->ti_size) ? dmalen : -s->ti_size;
-    else
-        minlen = (dmalen < s->ti_size) ? dmalen : s->ti_size;
-    trace_esp_handle_ti(minlen);
-    if (s->dma) {
-        s->dma_left = minlen;
-        s->rregs[ESP_RSTAT] &= ~STAT_TC;
-        esp_do_dma(s);
-    } else if (s->do_cmd) {
-        trace_esp_handle_ti_cmd(s->cmdlen);
-        s->ti_size = 0;
-        s->cmdlen = 0;
-        s->do_cmd = 0;
-        do_cmd(s, s->cmdbuf);
-        return;
-    }
-}
-
-void esp_hard_reset(ESPState *s)
-{
-    memset(s->rregs, 0, ESP_REGS);
-    memset(s->wregs, 0, ESP_REGS);
-    s->rregs[ESP_TCHI] = s->chip_id;
-    s->ti_size = 0;
-    s->ti_rptr = 0;
-    s->ti_wptr = 0;
-    s->dma = 0;
-    s->do_cmd = 0;
-    s->dma_cb = NULL;
-
-    s->rregs[ESP_CFG1] = 7;
-}
-
-static void esp_soft_reset(ESPState *s)
-{
-    qemu_irq_lower(s->irq);
-    esp_hard_reset(s);
-}
-
-static void parent_esp_reset(ESPState *s, int irq, int level)
-{
-    if (level) {
-        esp_soft_reset(s);
-    }
-}
-
-uint64_t esp_reg_read(ESPState *s, uint32_t saddr)
-{
-    uint32_t old_val;
-
-    trace_esp_mem_readb(saddr, s->rregs[saddr]);
-    switch (saddr) {
-    case ESP_FIFO:
-        if (s->ti_size > 0) {
-            s->ti_size--;
-            if ((s->rregs[ESP_RSTAT] & STAT_PIO_MASK) == 0) {
-                /* Data out.  */
-                qemu_log_mask(LOG_UNIMP,
-                              "esp: PIO data read not implemented\n");
-                s->rregs[ESP_FIFO] = 0;
-            } else {
-                s->rregs[ESP_FIFO] = s->ti_buf[s->ti_rptr++];
-            }
-            esp_raise_irq(s);
-        }
-        if (s->ti_size == 0) {
-            s->ti_rptr = 0;
-            s->ti_wptr = 0;
-        }
-        break;
-    case ESP_RINTR:
-        /* Clear sequence step, interrupt register and all status bits
-           except TC */
-        old_val = s->rregs[ESP_RINTR];
-        s->rregs[ESP_RINTR] = 0;
-        s->rregs[ESP_RSTAT] &= ~STAT_TC;
-        s->rregs[ESP_RSEQ] = SEQ_CD;
-        esp_lower_irq(s);
-
-        return old_val;
-    default:
-        break;
-    }
-    return s->rregs[saddr];
-}
-
-void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val)
-{
-    trace_esp_mem_writeb(saddr, s->wregs[saddr], val);
-    switch (saddr) {
-    case ESP_TCLO:
-    case ESP_TCMID:
-    case ESP_TCHI:
-        s->rregs[ESP_RSTAT] &= ~STAT_TC;
-        break;
-    case ESP_FIFO:
-        if (s->do_cmd) {
-            s->cmdbuf[s->cmdlen++] = val & 0xff;
-        } else if (s->ti_size == TI_BUFSZ - 1) {
-            trace_esp_error_fifo_overrun();
-        } else {
-            s->ti_size++;
-            s->ti_buf[s->ti_wptr++] = val & 0xff;
-        }
-        break;
-    case ESP_CMD:
-        s->rregs[saddr] = val;
-        if (val & CMD_DMA) {
-            s->dma = 1;
-            /* Reload DMA counter.  */
-            s->rregs[ESP_TCLO] = s->wregs[ESP_TCLO];
-            s->rregs[ESP_TCMID] = s->wregs[ESP_TCMID];
-            s->rregs[ESP_TCHI] = s->wregs[ESP_TCHI];
-        } else {
-            s->dma = 0;
-        }
-        switch(val & CMD_CMD) {
-        case CMD_NOP:
-            trace_esp_mem_writeb_cmd_nop(val);
-            break;
-        case CMD_FLUSH:
-            trace_esp_mem_writeb_cmd_flush(val);
-            //s->ti_size = 0;
-            s->rregs[ESP_RINTR] = INTR_FC;
-            s->rregs[ESP_RSEQ] = 0;
-            s->rregs[ESP_RFLAGS] = 0;
-            break;
-        case CMD_RESET:
-            trace_esp_mem_writeb_cmd_reset(val);
-            esp_soft_reset(s);
-            break;
-        case CMD_BUSRESET:
-            trace_esp_mem_writeb_cmd_bus_reset(val);
-            s->rregs[ESP_RINTR] = INTR_RST;
-            if (!(s->wregs[ESP_CFG1] & CFG1_RESREPT)) {
-                esp_raise_irq(s);
-            }
-            break;
-        case CMD_TI:
-            handle_ti(s);
-            break;
-        case CMD_ICCS:
-            trace_esp_mem_writeb_cmd_iccs(val);
-            write_response(s);
-            s->rregs[ESP_RINTR] = INTR_FC;
-            s->rregs[ESP_RSTAT] |= STAT_MI;
-            break;
-        case CMD_MSGACC:
-            trace_esp_mem_writeb_cmd_msgacc(val);
-            s->rregs[ESP_RINTR] = INTR_DC;
-            s->rregs[ESP_RSEQ] = 0;
-            s->rregs[ESP_RFLAGS] = 0;
-            esp_raise_irq(s);
-            break;
-        case CMD_PAD:
-            trace_esp_mem_writeb_cmd_pad(val);
-            s->rregs[ESP_RSTAT] = STAT_TC;
-            s->rregs[ESP_RINTR] = INTR_FC;
-            s->rregs[ESP_RSEQ] = 0;
-            break;
-        case CMD_SATN:
-            trace_esp_mem_writeb_cmd_satn(val);
-            break;
-        case CMD_RSTATN:
-            trace_esp_mem_writeb_cmd_rstatn(val);
-            break;
-        case CMD_SEL:
-            trace_esp_mem_writeb_cmd_sel(val);
-            handle_s_without_atn(s);
-            break;
-        case CMD_SELATN:
-            trace_esp_mem_writeb_cmd_selatn(val);
-            handle_satn(s);
-            break;
-        case CMD_SELATNS:
-            trace_esp_mem_writeb_cmd_selatns(val);
-            handle_satn_stop(s);
-            break;
-        case CMD_ENSEL:
-            trace_esp_mem_writeb_cmd_ensel(val);
-            s->rregs[ESP_RINTR] = 0;
-            break;
-        case CMD_DISSEL:
-            trace_esp_mem_writeb_cmd_dissel(val);
-            s->rregs[ESP_RINTR] = 0;
-            esp_raise_irq(s);
-            break;
-        default:
-            trace_esp_error_unhandled_command(val);
-            break;
-        }
-        break;
-    case ESP_WBUSID ... ESP_WSYNO:
-        break;
-    case ESP_CFG1:
-    case ESP_CFG2: case ESP_CFG3:
-    case ESP_RES3: case ESP_RES4:
-        s->rregs[saddr] = val;
-        break;
-    case ESP_WCCF ... ESP_WTEST:
-        break;
-    default:
-        trace_esp_error_invalid_write(val, saddr);
-        return;
-    }
-    s->wregs[saddr] = val;
-}
-
-static bool esp_mem_accepts(void *opaque, hwaddr addr,
-                            unsigned size, bool is_write)
-{
-    return (size == 1) || (is_write && size == 4);
-}
-
-const VMStateDescription vmstate_esp = {
-    .name ="esp",
-    .version_id = 3,
-    .minimum_version_id = 3,
-    .minimum_version_id_old = 3,
-    .fields      = (VMStateField []) {
-        VMSTATE_BUFFER(rregs, ESPState),
-        VMSTATE_BUFFER(wregs, ESPState),
-        VMSTATE_INT32(ti_size, ESPState),
-        VMSTATE_UINT32(ti_rptr, ESPState),
-        VMSTATE_UINT32(ti_wptr, ESPState),
-        VMSTATE_BUFFER(ti_buf, ESPState),
-        VMSTATE_UINT32(status, ESPState),
-        VMSTATE_UINT32(dma, ESPState),
-        VMSTATE_BUFFER(cmdbuf, ESPState),
-        VMSTATE_UINT32(cmdlen, ESPState),
-        VMSTATE_UINT32(do_cmd, ESPState),
-        VMSTATE_UINT32(dma_left, ESPState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    uint32_t it_shift;
-    ESPState esp;
-} SysBusESPState;
-
-static void sysbus_esp_mem_write(void *opaque, hwaddr addr,
-                                 uint64_t val, unsigned int size)
-{
-    SysBusESPState *sysbus = opaque;
-    uint32_t saddr;
-
-    saddr = addr >> sysbus->it_shift;
-    esp_reg_write(&sysbus->esp, saddr, val);
-}
-
-static uint64_t sysbus_esp_mem_read(void *opaque, hwaddr addr,
-                                    unsigned int size)
-{
-    SysBusESPState *sysbus = opaque;
-    uint32_t saddr;
-
-    saddr = addr >> sysbus->it_shift;
-    return esp_reg_read(&sysbus->esp, saddr);
-}
-
-static const MemoryRegionOps sysbus_esp_mem_ops = {
-    .read = sysbus_esp_mem_read,
-    .write = sysbus_esp_mem_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .valid.accepts = esp_mem_accepts,
-};
-
-void esp_init(hwaddr espaddr, int it_shift,
-              ESPDMAMemoryReadWriteFunc dma_memory_read,
-              ESPDMAMemoryReadWriteFunc dma_memory_write,
-              void *dma_opaque, qemu_irq irq, qemu_irq *reset,
-              qemu_irq *dma_enable)
-{
-    DeviceState *dev;
-    SysBusDevice *s;
-    SysBusESPState *sysbus;
-    ESPState *esp;
-
-    dev = qdev_create(NULL, "esp");
-    sysbus = DO_UPCAST(SysBusESPState, busdev.qdev, dev);
-    esp = &sysbus->esp;
-    esp->dma_memory_read = dma_memory_read;
-    esp->dma_memory_write = dma_memory_write;
-    esp->dma_opaque = dma_opaque;
-    sysbus->it_shift = it_shift;
-    /* XXX for now until rc4030 has been changed to use DMA enable signal */
-    esp->dma_enabled = 1;
-    qdev_init_nofail(dev);
-    s = SYS_BUS_DEVICE(dev);
-    sysbus_connect_irq(s, 0, irq);
-    sysbus_mmio_map(s, 0, espaddr);
-    *reset = qdev_get_gpio_in(dev, 0);
-    *dma_enable = qdev_get_gpio_in(dev, 1);
-}
-
-static const struct SCSIBusInfo esp_scsi_info = {
-    .tcq = false,
-    .max_target = ESP_MAX_DEVS,
-    .max_lun = 7,
-
-    .transfer_data = esp_transfer_data,
-    .complete = esp_command_complete,
-    .cancel = esp_request_cancelled
-};
-
-static void sysbus_esp_gpio_demux(void *opaque, int irq, int level)
-{
-    DeviceState *d = opaque;
-    SysBusESPState *sysbus = container_of(d, SysBusESPState, busdev.qdev);
-    ESPState *s = &sysbus->esp;
-
-    switch (irq) {
-    case 0:
-        parent_esp_reset(s, irq, level);
-        break;
-    case 1:
-        esp_dma_enable(opaque, irq, level);
-        break;
-    }
-}
-
-static int sysbus_esp_init(SysBusDevice *dev)
-{
-    SysBusESPState *sysbus = FROM_SYSBUS(SysBusESPState, dev);
-    ESPState *s = &sysbus->esp;
-
-    sysbus_init_irq(dev, &s->irq);
-    assert(sysbus->it_shift != -1);
-
-    s->chip_id = TCHI_FAS100A;
-    memory_region_init_io(&sysbus->iomem, &sysbus_esp_mem_ops, sysbus,
-                          "esp", ESP_REGS << sysbus->it_shift);
-    sysbus_init_mmio(dev, &sysbus->iomem);
-
-    qdev_init_gpio_in(&dev->qdev, sysbus_esp_gpio_demux, 2);
-
-    scsi_bus_new(&s->bus, &dev->qdev, &esp_scsi_info);
-    return scsi_bus_legacy_handle_cmdline(&s->bus);
-}
-
-static void sysbus_esp_hard_reset(DeviceState *dev)
-{
-    SysBusESPState *sysbus = DO_UPCAST(SysBusESPState, busdev.qdev, dev);
-    esp_hard_reset(&sysbus->esp);
-}
-
-static const VMStateDescription vmstate_sysbus_esp_scsi = {
-    .name = "sysbusespscsi",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .fields = (VMStateField[]) {
-        VMSTATE_STRUCT(esp, SysBusESPState, 0, vmstate_esp, ESPState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void sysbus_esp_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = sysbus_esp_init;
-    dc->reset = sysbus_esp_hard_reset;
-    dc->vmsd = &vmstate_sysbus_esp_scsi;
-}
-
-static const TypeInfo sysbus_esp_info = {
-    .name          = "esp",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(SysBusESPState),
-    .class_init    = sysbus_esp_class_init,
-};
-
-static void esp_register_types(void)
-{
-    type_register_static(&sysbus_esp_info);
-}
-
-type_init(esp_register_types)
diff --git a/hw/fdc.c b/hw/fdc.c
deleted file mode 100644 (file)
index 1ed874f..0000000
--- a/hw/fdc.c
+++ /dev/null
@@ -1,2284 +0,0 @@
-/*
- * QEMU Floppy disk emulator (Intel 82078)
- *
- * Copyright (c) 2003, 2007 Jocelyn Mayer
- * Copyright (c) 2008 Hervé Poussineau
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-/*
- * The controller is used in Sun4m systems in a slightly different
- * way. There are changes in DOR register and DMA is not available.
- */
-
-#include "hw/hw.h"
-#include "hw/block/fdc.h"
-#include "qemu/error-report.h"
-#include "qemu/timer.h"
-#include "hw/isa/isa.h"
-#include "hw/sysbus.h"
-#include "hw/qdev-addr.h"
-#include "sysemu/blockdev.h"
-#include "sysemu/sysemu.h"
-#include "qemu/log.h"
-
-/********************************************************/
-/* debug Floppy devices */
-//#define DEBUG_FLOPPY
-
-#ifdef DEBUG_FLOPPY
-#define FLOPPY_DPRINTF(fmt, ...)                                \
-    do { printf("FLOPPY: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define FLOPPY_DPRINTF(fmt, ...)
-#endif
-
-/********************************************************/
-/* Floppy drive emulation                               */
-
-typedef enum FDriveRate {
-    FDRIVE_RATE_500K = 0x00,  /* 500 Kbps */
-    FDRIVE_RATE_300K = 0x01,  /* 300 Kbps */
-    FDRIVE_RATE_250K = 0x02,  /* 250 Kbps */
-    FDRIVE_RATE_1M   = 0x03,  /*   1 Mbps */
-} FDriveRate;
-
-typedef struct FDFormat {
-    FDriveType drive;
-    uint8_t last_sect;
-    uint8_t max_track;
-    uint8_t max_head;
-    FDriveRate rate;
-} FDFormat;
-
-static const FDFormat fd_formats[] = {
-    /* First entry is default format */
-    /* 1.44 MB 3"1/2 floppy disks */
-    { FDRIVE_DRV_144, 18, 80, 1, FDRIVE_RATE_500K, },
-    { FDRIVE_DRV_144, 20, 80, 1, FDRIVE_RATE_500K, },
-    { FDRIVE_DRV_144, 21, 80, 1, FDRIVE_RATE_500K, },
-    { FDRIVE_DRV_144, 21, 82, 1, FDRIVE_RATE_500K, },
-    { FDRIVE_DRV_144, 21, 83, 1, FDRIVE_RATE_500K, },
-    { FDRIVE_DRV_144, 22, 80, 1, FDRIVE_RATE_500K, },
-    { FDRIVE_DRV_144, 23, 80, 1, FDRIVE_RATE_500K, },
-    { FDRIVE_DRV_144, 24, 80, 1, FDRIVE_RATE_500K, },
-    /* 2.88 MB 3"1/2 floppy disks */
-    { FDRIVE_DRV_288, 36, 80, 1, FDRIVE_RATE_1M, },
-    { FDRIVE_DRV_288, 39, 80, 1, FDRIVE_RATE_1M, },
-    { FDRIVE_DRV_288, 40, 80, 1, FDRIVE_RATE_1M, },
-    { FDRIVE_DRV_288, 44, 80, 1, FDRIVE_RATE_1M, },
-    { FDRIVE_DRV_288, 48, 80, 1, FDRIVE_RATE_1M, },
-    /* 720 kB 3"1/2 floppy disks */
-    { FDRIVE_DRV_144,  9, 80, 1, FDRIVE_RATE_250K, },
-    { FDRIVE_DRV_144, 10, 80, 1, FDRIVE_RATE_250K, },
-    { FDRIVE_DRV_144, 10, 82, 1, FDRIVE_RATE_250K, },
-    { FDRIVE_DRV_144, 10, 83, 1, FDRIVE_RATE_250K, },
-    { FDRIVE_DRV_144, 13, 80, 1, FDRIVE_RATE_250K, },
-    { FDRIVE_DRV_144, 14, 80, 1, FDRIVE_RATE_250K, },
-    /* 1.2 MB 5"1/4 floppy disks */
-    { FDRIVE_DRV_120, 15, 80, 1, FDRIVE_RATE_500K, },
-    { FDRIVE_DRV_120, 18, 80, 1, FDRIVE_RATE_500K, },
-    { FDRIVE_DRV_120, 18, 82, 1, FDRIVE_RATE_500K, },
-    { FDRIVE_DRV_120, 18, 83, 1, FDRIVE_RATE_500K, },
-    { FDRIVE_DRV_120, 20, 80, 1, FDRIVE_RATE_500K, },
-    /* 720 kB 5"1/4 floppy disks */
-    { FDRIVE_DRV_120,  9, 80, 1, FDRIVE_RATE_250K, },
-    { FDRIVE_DRV_120, 11, 80, 1, FDRIVE_RATE_250K, },
-    /* 360 kB 5"1/4 floppy disks */
-    { FDRIVE_DRV_120,  9, 40, 1, FDRIVE_RATE_300K, },
-    { FDRIVE_DRV_120,  9, 40, 0, FDRIVE_RATE_300K, },
-    { FDRIVE_DRV_120, 10, 41, 1, FDRIVE_RATE_300K, },
-    { FDRIVE_DRV_120, 10, 42, 1, FDRIVE_RATE_300K, },
-    /* 320 kB 5"1/4 floppy disks */
-    { FDRIVE_DRV_120,  8, 40, 1, FDRIVE_RATE_250K, },
-    { FDRIVE_DRV_120,  8, 40, 0, FDRIVE_RATE_250K, },
-    /* 360 kB must match 5"1/4 better than 3"1/2... */
-    { FDRIVE_DRV_144,  9, 80, 0, FDRIVE_RATE_250K, },
-    /* end */
-    { FDRIVE_DRV_NONE, -1, -1, 0, 0, },
-};
-
-static void pick_geometry(BlockDriverState *bs, int *nb_heads,
-                          int *max_track, int *last_sect,
-                          FDriveType drive_in, FDriveType *drive,
-                          FDriveRate *rate)
-{
-    const FDFormat *parse;
-    uint64_t nb_sectors, size;
-    int i, first_match, match;
-
-    bdrv_get_geometry(bs, &nb_sectors);
-    match = -1;
-    first_match = -1;
-    for (i = 0; ; i++) {
-        parse = &fd_formats[i];
-        if (parse->drive == FDRIVE_DRV_NONE) {
-            break;
-        }
-        if (drive_in == parse->drive ||
-            drive_in == FDRIVE_DRV_NONE) {
-            size = (parse->max_head + 1) * parse->max_track *
-                parse->last_sect;
-            if (nb_sectors == size) {
-                match = i;
-                break;
-            }
-            if (first_match == -1) {
-                first_match = i;
-            }
-        }
-    }
-    if (match == -1) {
-        if (first_match == -1) {
-            match = 1;
-        } else {
-            match = first_match;
-        }
-        parse = &fd_formats[match];
-    }
-    *nb_heads = parse->max_head + 1;
-    *max_track = parse->max_track;
-    *last_sect = parse->last_sect;
-    *drive = parse->drive;
-    *rate = parse->rate;
-}
-
-#define GET_CUR_DRV(fdctrl) ((fdctrl)->cur_drv)
-#define SET_CUR_DRV(fdctrl, drive) ((fdctrl)->cur_drv = (drive))
-
-/* Will always be a fixed parameter for us */
-#define FD_SECTOR_LEN          512
-#define FD_SECTOR_SC           2   /* Sector size code */
-#define FD_RESET_SENSEI_COUNT  4   /* Number of sense interrupts on RESET */
-
-typedef struct FDCtrl FDCtrl;
-
-/* Floppy disk drive emulation */
-typedef enum FDiskFlags {
-    FDISK_DBL_SIDES  = 0x01,
-} FDiskFlags;
-
-typedef struct FDrive {
-    FDCtrl *fdctrl;
-    BlockDriverState *bs;
-    /* Drive status */
-    FDriveType drive;
-    uint8_t perpendicular;    /* 2.88 MB access mode    */
-    /* Position */
-    uint8_t head;
-    uint8_t track;
-    uint8_t sect;
-    /* Media */
-    FDiskFlags flags;
-    uint8_t last_sect;        /* Nb sector per track    */
-    uint8_t max_track;        /* Nb of tracks           */
-    uint16_t bps;             /* Bytes per sector       */
-    uint8_t ro;               /* Is read-only           */
-    uint8_t media_changed;    /* Is media changed       */
-    uint8_t media_rate;       /* Data rate of medium    */
-} FDrive;
-
-static void fd_init(FDrive *drv)
-{
-    /* Drive */
-    drv->drive = FDRIVE_DRV_NONE;
-    drv->perpendicular = 0;
-    /* Disk */
-    drv->last_sect = 0;
-    drv->max_track = 0;
-}
-
-#define NUM_SIDES(drv) ((drv)->flags & FDISK_DBL_SIDES ? 2 : 1)
-
-static int fd_sector_calc(uint8_t head, uint8_t track, uint8_t sect,
-                          uint8_t last_sect, uint8_t num_sides)
-{
-    return (((track * num_sides) + head) * last_sect) + sect - 1;
-}
-
-/* Returns current position, in sectors, for given drive */
-static int fd_sector(FDrive *drv)
-{
-    return fd_sector_calc(drv->head, drv->track, drv->sect, drv->last_sect,
-                          NUM_SIDES(drv));
-}
-
-/* Seek to a new position:
- * returns 0 if already on right track
- * returns 1 if track changed
- * returns 2 if track is invalid
- * returns 3 if sector is invalid
- * returns 4 if seek is disabled
- */
-static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect,
-                   int enable_seek)
-{
-    uint32_t sector;
-    int ret;
-
-    if (track > drv->max_track ||
-        (head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) {
-        FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n",
-                       head, track, sect, 1,
-                       (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1,
-                       drv->max_track, drv->last_sect);
-        return 2;
-    }
-    if (sect > drv->last_sect) {
-        FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n",
-                       head, track, sect, 1,
-                       (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1,
-                       drv->max_track, drv->last_sect);
-        return 3;
-    }
-    sector = fd_sector_calc(head, track, sect, drv->last_sect, NUM_SIDES(drv));
-    ret = 0;
-    if (sector != fd_sector(drv)) {
-#if 0
-        if (!enable_seek) {
-            FLOPPY_DPRINTF("error: no implicit seek %d %02x %02x"
-                           " (max=%d %02x %02x)\n",
-                           head, track, sect, 1, drv->max_track,
-                           drv->last_sect);
-            return 4;
-        }
-#endif
-        drv->head = head;
-        if (drv->track != track) {
-            if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
-                drv->media_changed = 0;
-            }
-            ret = 1;
-        }
-        drv->track = track;
-        drv->sect = sect;
-    }
-
-    if (drv->bs == NULL || !bdrv_is_inserted(drv->bs)) {
-        ret = 2;
-    }
-
-    return ret;
-}
-
-/* Set drive back to track 0 */
-static void fd_recalibrate(FDrive *drv)
-{
-    FLOPPY_DPRINTF("recalibrate\n");
-    fd_seek(drv, 0, 0, 1, 1);
-}
-
-/* Revalidate a disk drive after a disk change */
-static void fd_revalidate(FDrive *drv)
-{
-    int nb_heads, max_track, last_sect, ro;
-    FDriveType drive;
-    FDriveRate rate;
-
-    FLOPPY_DPRINTF("revalidate\n");
-    if (drv->bs != NULL) {
-        ro = bdrv_is_read_only(drv->bs);
-        pick_geometry(drv->bs, &nb_heads, &max_track,
-                      &last_sect, drv->drive, &drive, &rate);
-        if (!bdrv_is_inserted(drv->bs)) {
-            FLOPPY_DPRINTF("No disk in drive\n");
-        } else {
-            FLOPPY_DPRINTF("Floppy disk (%d h %d t %d s) %s\n", nb_heads,
-                           max_track, last_sect, ro ? "ro" : "rw");
-        }
-        if (nb_heads == 1) {
-            drv->flags &= ~FDISK_DBL_SIDES;
-        } else {
-            drv->flags |= FDISK_DBL_SIDES;
-        }
-        drv->max_track = max_track;
-        drv->last_sect = last_sect;
-        drv->ro = ro;
-        drv->drive = drive;
-        drv->media_rate = rate;
-    } else {
-        FLOPPY_DPRINTF("No drive connected\n");
-        drv->last_sect = 0;
-        drv->max_track = 0;
-        drv->flags &= ~FDISK_DBL_SIDES;
-    }
-}
-
-/********************************************************/
-/* Intel 82078 floppy disk controller emulation          */
-
-static void fdctrl_reset(FDCtrl *fdctrl, int do_irq);
-static void fdctrl_reset_fifo(FDCtrl *fdctrl);
-static int fdctrl_transfer_handler (void *opaque, int nchan,
-                                    int dma_pos, int dma_len);
-static void fdctrl_raise_irq(FDCtrl *fdctrl);
-static FDrive *get_cur_drv(FDCtrl *fdctrl);
-
-static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl);
-static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl);
-static uint32_t fdctrl_read_dor(FDCtrl *fdctrl);
-static void fdctrl_write_dor(FDCtrl *fdctrl, uint32_t value);
-static uint32_t fdctrl_read_tape(FDCtrl *fdctrl);
-static void fdctrl_write_tape(FDCtrl *fdctrl, uint32_t value);
-static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl);
-static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value);
-static uint32_t fdctrl_read_data(FDCtrl *fdctrl);
-static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value);
-static uint32_t fdctrl_read_dir(FDCtrl *fdctrl);
-static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value);
-
-enum {
-    FD_DIR_WRITE   = 0,
-    FD_DIR_READ    = 1,
-    FD_DIR_SCANE   = 2,
-    FD_DIR_SCANL   = 3,
-    FD_DIR_SCANH   = 4,
-    FD_DIR_VERIFY  = 5,
-};
-
-enum {
-    FD_STATE_MULTI  = 0x01,    /* multi track flag */
-    FD_STATE_FORMAT = 0x02,    /* format flag */
-};
-
-enum {
-    FD_REG_SRA = 0x00,
-    FD_REG_SRB = 0x01,
-    FD_REG_DOR = 0x02,
-    FD_REG_TDR = 0x03,
-    FD_REG_MSR = 0x04,
-    FD_REG_DSR = 0x04,
-    FD_REG_FIFO = 0x05,
-    FD_REG_DIR = 0x07,
-    FD_REG_CCR = 0x07,
-};
-
-enum {
-    FD_CMD_READ_TRACK = 0x02,
-    FD_CMD_SPECIFY = 0x03,
-    FD_CMD_SENSE_DRIVE_STATUS = 0x04,
-    FD_CMD_WRITE = 0x05,
-    FD_CMD_READ = 0x06,
-    FD_CMD_RECALIBRATE = 0x07,
-    FD_CMD_SENSE_INTERRUPT_STATUS = 0x08,
-    FD_CMD_WRITE_DELETED = 0x09,
-    FD_CMD_READ_ID = 0x0a,
-    FD_CMD_READ_DELETED = 0x0c,
-    FD_CMD_FORMAT_TRACK = 0x0d,
-    FD_CMD_DUMPREG = 0x0e,
-    FD_CMD_SEEK = 0x0f,
-    FD_CMD_VERSION = 0x10,
-    FD_CMD_SCAN_EQUAL = 0x11,
-    FD_CMD_PERPENDICULAR_MODE = 0x12,
-    FD_CMD_CONFIGURE = 0x13,
-    FD_CMD_LOCK = 0x14,
-    FD_CMD_VERIFY = 0x16,
-    FD_CMD_POWERDOWN_MODE = 0x17,
-    FD_CMD_PART_ID = 0x18,
-    FD_CMD_SCAN_LOW_OR_EQUAL = 0x19,
-    FD_CMD_SCAN_HIGH_OR_EQUAL = 0x1d,
-    FD_CMD_SAVE = 0x2e,
-    FD_CMD_OPTION = 0x33,
-    FD_CMD_RESTORE = 0x4e,
-    FD_CMD_DRIVE_SPECIFICATION_COMMAND = 0x8e,
-    FD_CMD_RELATIVE_SEEK_OUT = 0x8f,
-    FD_CMD_FORMAT_AND_WRITE = 0xcd,
-    FD_CMD_RELATIVE_SEEK_IN = 0xcf,
-};
-
-enum {
-    FD_CONFIG_PRETRK = 0xff, /* Pre-compensation set to track 0 */
-    FD_CONFIG_FIFOTHR = 0x0f, /* FIFO threshold set to 1 byte */
-    FD_CONFIG_POLL  = 0x10, /* Poll enabled */
-    FD_CONFIG_EFIFO = 0x20, /* FIFO disabled */
-    FD_CONFIG_EIS   = 0x40, /* No implied seeks */
-};
-
-enum {
-    FD_SR0_DS0      = 0x01,
-    FD_SR0_DS1      = 0x02,
-    FD_SR0_HEAD     = 0x04,
-    FD_SR0_EQPMT    = 0x10,
-    FD_SR0_SEEK     = 0x20,
-    FD_SR0_ABNTERM  = 0x40,
-    FD_SR0_INVCMD   = 0x80,
-    FD_SR0_RDYCHG   = 0xc0,
-};
-
-enum {
-    FD_SR1_MA       = 0x01, /* Missing address mark */
-    FD_SR1_NW       = 0x02, /* Not writable */
-    FD_SR1_EC       = 0x80, /* End of cylinder */
-};
-
-enum {
-    FD_SR2_SNS      = 0x04, /* Scan not satisfied */
-    FD_SR2_SEH      = 0x08, /* Scan equal hit */
-};
-
-enum {
-    FD_SRA_DIR      = 0x01,
-    FD_SRA_nWP      = 0x02,
-    FD_SRA_nINDX    = 0x04,
-    FD_SRA_HDSEL    = 0x08,
-    FD_SRA_nTRK0    = 0x10,
-    FD_SRA_STEP     = 0x20,
-    FD_SRA_nDRV2    = 0x40,
-    FD_SRA_INTPEND  = 0x80,
-};
-
-enum {
-    FD_SRB_MTR0     = 0x01,
-    FD_SRB_MTR1     = 0x02,
-    FD_SRB_WGATE    = 0x04,
-    FD_SRB_RDATA    = 0x08,
-    FD_SRB_WDATA    = 0x10,
-    FD_SRB_DR0      = 0x20,
-};
-
-enum {
-#if MAX_FD == 4
-    FD_DOR_SELMASK  = 0x03,
-#else
-    FD_DOR_SELMASK  = 0x01,
-#endif
-    FD_DOR_nRESET   = 0x04,
-    FD_DOR_DMAEN    = 0x08,
-    FD_DOR_MOTEN0   = 0x10,
-    FD_DOR_MOTEN1   = 0x20,
-    FD_DOR_MOTEN2   = 0x40,
-    FD_DOR_MOTEN3   = 0x80,
-};
-
-enum {
-#if MAX_FD == 4
-    FD_TDR_BOOTSEL  = 0x0c,
-#else
-    FD_TDR_BOOTSEL  = 0x04,
-#endif
-};
-
-enum {
-    FD_DSR_DRATEMASK= 0x03,
-    FD_DSR_PWRDOWN  = 0x40,
-    FD_DSR_SWRESET  = 0x80,
-};
-
-enum {
-    FD_MSR_DRV0BUSY = 0x01,
-    FD_MSR_DRV1BUSY = 0x02,
-    FD_MSR_DRV2BUSY = 0x04,
-    FD_MSR_DRV3BUSY = 0x08,
-    FD_MSR_CMDBUSY  = 0x10,
-    FD_MSR_NONDMA   = 0x20,
-    FD_MSR_DIO      = 0x40,
-    FD_MSR_RQM      = 0x80,
-};
-
-enum {
-    FD_DIR_DSKCHG   = 0x80,
-};
-
-#define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI)
-#define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT)
-
-struct FDCtrl {
-    MemoryRegion iomem;
-    qemu_irq irq;
-    /* Controller state */
-    QEMUTimer *result_timer;
-    int dma_chann;
-    /* Controller's identification */
-    uint8_t version;
-    /* HW */
-    uint8_t sra;
-    uint8_t srb;
-    uint8_t dor;
-    uint8_t dor_vmstate; /* only used as temp during vmstate */
-    uint8_t tdr;
-    uint8_t dsr;
-    uint8_t msr;
-    uint8_t cur_drv;
-    uint8_t status0;
-    uint8_t status1;
-    uint8_t status2;
-    /* Command FIFO */
-    uint8_t *fifo;
-    int32_t fifo_size;
-    uint32_t data_pos;
-    uint32_t data_len;
-    uint8_t data_state;
-    uint8_t data_dir;
-    uint8_t eot; /* last wanted sector */
-    /* States kept only to be returned back */
-    /* precompensation */
-    uint8_t precomp_trk;
-    uint8_t config;
-    uint8_t lock;
-    /* Power down config (also with status regB access mode */
-    uint8_t pwrd;
-    /* Floppy drives */
-    uint8_t num_floppies;
-    /* Sun4m quirks? */
-    int sun4m;
-    FDrive drives[MAX_FD];
-    int reset_sensei;
-    uint32_t check_media_rate;
-    /* Timers state */
-    uint8_t timer0;
-    uint8_t timer1;
-};
-
-typedef struct FDCtrlSysBus {
-    SysBusDevice busdev;
-    struct FDCtrl state;
-} FDCtrlSysBus;
-
-typedef struct FDCtrlISABus {
-    ISADevice busdev;
-    uint32_t iobase;
-    uint32_t irq;
-    uint32_t dma;
-    struct FDCtrl state;
-    int32_t bootindexA;
-    int32_t bootindexB;
-} FDCtrlISABus;
-
-static uint32_t fdctrl_read (void *opaque, uint32_t reg)
-{
-    FDCtrl *fdctrl = opaque;
-    uint32_t retval;
-
-    reg &= 7;
-    switch (reg) {
-    case FD_REG_SRA:
-        retval = fdctrl_read_statusA(fdctrl);
-        break;
-    case FD_REG_SRB:
-        retval = fdctrl_read_statusB(fdctrl);
-        break;
-    case FD_REG_DOR:
-        retval = fdctrl_read_dor(fdctrl);
-        break;
-    case FD_REG_TDR:
-        retval = fdctrl_read_tape(fdctrl);
-        break;
-    case FD_REG_MSR:
-        retval = fdctrl_read_main_status(fdctrl);
-        break;
-    case FD_REG_FIFO:
-        retval = fdctrl_read_data(fdctrl);
-        break;
-    case FD_REG_DIR:
-        retval = fdctrl_read_dir(fdctrl);
-        break;
-    default:
-        retval = (uint32_t)(-1);
-        break;
-    }
-    FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval);
-
-    return retval;
-}
-
-static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
-{
-    FDCtrl *fdctrl = opaque;
-
-    FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value);
-
-    reg &= 7;
-    switch (reg) {
-    case FD_REG_DOR:
-        fdctrl_write_dor(fdctrl, value);
-        break;
-    case FD_REG_TDR:
-        fdctrl_write_tape(fdctrl, value);
-        break;
-    case FD_REG_DSR:
-        fdctrl_write_rate(fdctrl, value);
-        break;
-    case FD_REG_FIFO:
-        fdctrl_write_data(fdctrl, value);
-        break;
-    case FD_REG_CCR:
-        fdctrl_write_ccr(fdctrl, value);
-        break;
-    default:
-        break;
-    }
-}
-
-static uint64_t fdctrl_read_mem (void *opaque, hwaddr reg,
-                                 unsigned ize)
-{
-    return fdctrl_read(opaque, (uint32_t)reg);
-}
-
-static void fdctrl_write_mem (void *opaque, hwaddr reg,
-                              uint64_t value, unsigned size)
-{
-    fdctrl_write(opaque, (uint32_t)reg, value);
-}
-
-static const MemoryRegionOps fdctrl_mem_ops = {
-    .read = fdctrl_read_mem,
-    .write = fdctrl_write_mem,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static const MemoryRegionOps fdctrl_mem_strict_ops = {
-    .read = fdctrl_read_mem,
-    .write = fdctrl_write_mem,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .valid = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-static bool fdrive_media_changed_needed(void *opaque)
-{
-    FDrive *drive = opaque;
-
-    return (drive->bs != NULL && drive->media_changed != 1);
-}
-
-static const VMStateDescription vmstate_fdrive_media_changed = {
-    .name = "fdrive/media_changed",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT8(media_changed, FDrive),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static bool fdrive_media_rate_needed(void *opaque)
-{
-    FDrive *drive = opaque;
-
-    return drive->fdctrl->check_media_rate;
-}
-
-static const VMStateDescription vmstate_fdrive_media_rate = {
-    .name = "fdrive/media_rate",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT8(media_rate, FDrive),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_fdrive = {
-    .name = "fdrive",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT8(head, FDrive),
-        VMSTATE_UINT8(track, FDrive),
-        VMSTATE_UINT8(sect, FDrive),
-        VMSTATE_END_OF_LIST()
-    },
-    .subsections = (VMStateSubsection[]) {
-        {
-            .vmsd = &vmstate_fdrive_media_changed,
-            .needed = &fdrive_media_changed_needed,
-        } , {
-            .vmsd = &vmstate_fdrive_media_rate,
-            .needed = &fdrive_media_rate_needed,
-        } , {
-            /* empty */
-        }
-    }
-};
-
-static void fdc_pre_save(void *opaque)
-{
-    FDCtrl *s = opaque;
-
-    s->dor_vmstate = s->dor | GET_CUR_DRV(s);
-}
-
-static int fdc_post_load(void *opaque, int version_id)
-{
-    FDCtrl *s = opaque;
-
-    SET_CUR_DRV(s, s->dor_vmstate & FD_DOR_SELMASK);
-    s->dor = s->dor_vmstate & ~FD_DOR_SELMASK;
-    return 0;
-}
-
-static const VMStateDescription vmstate_fdc = {
-    .name = "fdc",
-    .version_id = 2,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .pre_save = fdc_pre_save,
-    .post_load = fdc_post_load,
-    .fields      = (VMStateField []) {
-        /* Controller State */
-        VMSTATE_UINT8(sra, FDCtrl),
-        VMSTATE_UINT8(srb, FDCtrl),
-        VMSTATE_UINT8(dor_vmstate, FDCtrl),
-        VMSTATE_UINT8(tdr, FDCtrl),
-        VMSTATE_UINT8(dsr, FDCtrl),
-        VMSTATE_UINT8(msr, FDCtrl),
-        VMSTATE_UINT8(status0, FDCtrl),
-        VMSTATE_UINT8(status1, FDCtrl),
-        VMSTATE_UINT8(status2, FDCtrl),
-        /* Command FIFO */
-        VMSTATE_VARRAY_INT32(fifo, FDCtrl, fifo_size, 0, vmstate_info_uint8,
-                             uint8_t),
-        VMSTATE_UINT32(data_pos, FDCtrl),
-        VMSTATE_UINT32(data_len, FDCtrl),
-        VMSTATE_UINT8(data_state, FDCtrl),
-        VMSTATE_UINT8(data_dir, FDCtrl),
-        VMSTATE_UINT8(eot, FDCtrl),
-        /* States kept only to be returned back */
-        VMSTATE_UINT8(timer0, FDCtrl),
-        VMSTATE_UINT8(timer1, FDCtrl),
-        VMSTATE_UINT8(precomp_trk, FDCtrl),
-        VMSTATE_UINT8(config, FDCtrl),
-        VMSTATE_UINT8(lock, FDCtrl),
-        VMSTATE_UINT8(pwrd, FDCtrl),
-        VMSTATE_UINT8_EQUAL(num_floppies, FDCtrl),
-        VMSTATE_STRUCT_ARRAY(drives, FDCtrl, MAX_FD, 1,
-                             vmstate_fdrive, FDrive),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void fdctrl_external_reset_sysbus(DeviceState *d)
-{
-    FDCtrlSysBus *sys = container_of(d, FDCtrlSysBus, busdev.qdev);
-    FDCtrl *s = &sys->state;
-
-    fdctrl_reset(s, 0);
-}
-
-static void fdctrl_external_reset_isa(DeviceState *d)
-{
-    FDCtrlISABus *isa = container_of(d, FDCtrlISABus, busdev.qdev);
-    FDCtrl *s = &isa->state;
-
-    fdctrl_reset(s, 0);
-}
-
-static void fdctrl_handle_tc(void *opaque, int irq, int level)
-{
-    //FDCtrl *s = opaque;
-
-    if (level) {
-        // XXX
-        FLOPPY_DPRINTF("TC pulsed\n");
-    }
-}
-
-/* Change IRQ state */
-static void fdctrl_reset_irq(FDCtrl *fdctrl)
-{
-    fdctrl->status0 = 0;
-    if (!(fdctrl->sra & FD_SRA_INTPEND))
-        return;
-    FLOPPY_DPRINTF("Reset interrupt\n");
-    qemu_set_irq(fdctrl->irq, 0);
-    fdctrl->sra &= ~FD_SRA_INTPEND;
-}
-
-static void fdctrl_raise_irq(FDCtrl *fdctrl)
-{
-    /* Sparc mutation */
-    if (fdctrl->sun4m && (fdctrl->msr & FD_MSR_CMDBUSY)) {
-        /* XXX: not sure */
-        fdctrl->msr &= ~FD_MSR_CMDBUSY;
-        fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
-        return;
-    }
-    if (!(fdctrl->sra & FD_SRA_INTPEND)) {
-        qemu_set_irq(fdctrl->irq, 1);
-        fdctrl->sra |= FD_SRA_INTPEND;
-    }
-
-    fdctrl->reset_sensei = 0;
-    FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", fdctrl->status0);
-}
-
-/* Reset controller */
-static void fdctrl_reset(FDCtrl *fdctrl, int do_irq)
-{
-    int i;
-
-    FLOPPY_DPRINTF("reset controller\n");
-    fdctrl_reset_irq(fdctrl);
-    /* Initialise controller */
-    fdctrl->sra = 0;
-    fdctrl->srb = 0xc0;
-    if (!fdctrl->drives[1].bs)
-        fdctrl->sra |= FD_SRA_nDRV2;
-    fdctrl->cur_drv = 0;
-    fdctrl->dor = FD_DOR_nRESET;
-    fdctrl->dor |= (fdctrl->dma_chann != -1) ? FD_DOR_DMAEN : 0;
-    fdctrl->msr = FD_MSR_RQM;
-    /* FIFO state */
-    fdctrl->data_pos = 0;
-    fdctrl->data_len = 0;
-    fdctrl->data_state = 0;
-    fdctrl->data_dir = FD_DIR_WRITE;
-    for (i = 0; i < MAX_FD; i++)
-        fd_recalibrate(&fdctrl->drives[i]);
-    fdctrl_reset_fifo(fdctrl);
-    if (do_irq) {
-        fdctrl->status0 |= FD_SR0_RDYCHG;
-        fdctrl_raise_irq(fdctrl);
-        fdctrl->reset_sensei = FD_RESET_SENSEI_COUNT;
-    }
-}
-
-static inline FDrive *drv0(FDCtrl *fdctrl)
-{
-    return &fdctrl->drives[(fdctrl->tdr & FD_TDR_BOOTSEL) >> 2];
-}
-
-static inline FDrive *drv1(FDCtrl *fdctrl)
-{
-    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (1 << 2))
-        return &fdctrl->drives[1];
-    else
-        return &fdctrl->drives[0];
-}
-
-#if MAX_FD == 4
-static inline FDrive *drv2(FDCtrl *fdctrl)
-{
-    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (2 << 2))
-        return &fdctrl->drives[2];
-    else
-        return &fdctrl->drives[1];
-}
-
-static inline FDrive *drv3(FDCtrl *fdctrl)
-{
-    if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (3 << 2))
-        return &fdctrl->drives[3];
-    else
-        return &fdctrl->drives[2];
-}
-#endif
-
-static FDrive *get_cur_drv(FDCtrl *fdctrl)
-{
-    switch (fdctrl->cur_drv) {
-        case 0: return drv0(fdctrl);
-        case 1: return drv1(fdctrl);
-#if MAX_FD == 4
-        case 2: return drv2(fdctrl);
-        case 3: return drv3(fdctrl);
-#endif
-        default: return NULL;
-    }
-}
-
-/* Status A register : 0x00 (read-only) */
-static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl)
-{
-    uint32_t retval = fdctrl->sra;
-
-    FLOPPY_DPRINTF("status register A: 0x%02x\n", retval);
-
-    return retval;
-}
-
-/* Status B register : 0x01 (read-only) */
-static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl)
-{
-    uint32_t retval = fdctrl->srb;
-
-    FLOPPY_DPRINTF("status register B: 0x%02x\n", retval);
-
-    return retval;
-}
-
-/* Digital output register : 0x02 */
-static uint32_t fdctrl_read_dor(FDCtrl *fdctrl)
-{
-    uint32_t retval = fdctrl->dor;
-
-    /* Selected drive */
-    retval |= fdctrl->cur_drv;
-    FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval);
-
-    return retval;
-}
-
-static void fdctrl_write_dor(FDCtrl *fdctrl, uint32_t value)
-{
-    FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value);
-
-    /* Motors */
-    if (value & FD_DOR_MOTEN0)
-        fdctrl->srb |= FD_SRB_MTR0;
-    else
-        fdctrl->srb &= ~FD_SRB_MTR0;
-    if (value & FD_DOR_MOTEN1)
-        fdctrl->srb |= FD_SRB_MTR1;
-    else
-        fdctrl->srb &= ~FD_SRB_MTR1;
-
-    /* Drive */
-    if (value & 1)
-        fdctrl->srb |= FD_SRB_DR0;
-    else
-        fdctrl->srb &= ~FD_SRB_DR0;
-
-    /* Reset */
-    if (!(value & FD_DOR_nRESET)) {
-        if (fdctrl->dor & FD_DOR_nRESET) {
-            FLOPPY_DPRINTF("controller enter RESET state\n");
-        }
-    } else {
-        if (!(fdctrl->dor & FD_DOR_nRESET)) {
-            FLOPPY_DPRINTF("controller out of RESET state\n");
-            fdctrl_reset(fdctrl, 1);
-            fdctrl->dsr &= ~FD_DSR_PWRDOWN;
-        }
-    }
-    /* Selected drive */
-    fdctrl->cur_drv = value & FD_DOR_SELMASK;
-
-    fdctrl->dor = value;
-}
-
-/* Tape drive register : 0x03 */
-static uint32_t fdctrl_read_tape(FDCtrl *fdctrl)
-{
-    uint32_t retval = fdctrl->tdr;
-
-    FLOPPY_DPRINTF("tape drive register: 0x%02x\n", retval);
-
-    return retval;
-}
-
-static void fdctrl_write_tape(FDCtrl *fdctrl, uint32_t value)
-{
-    /* Reset mode */
-    if (!(fdctrl->dor & FD_DOR_nRESET)) {
-        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
-        return;
-    }
-    FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value);
-    /* Disk boot selection indicator */
-    fdctrl->tdr = value & FD_TDR_BOOTSEL;
-    /* Tape indicators: never allow */
-}
-
-/* Main status register : 0x04 (read) */
-static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl)
-{
-    uint32_t retval = fdctrl->msr;
-
-    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
-    fdctrl->dor |= FD_DOR_nRESET;
-
-    /* Sparc mutation */
-    if (fdctrl->sun4m) {
-        retval |= FD_MSR_DIO;
-        fdctrl_reset_irq(fdctrl);
-    };
-
-    FLOPPY_DPRINTF("main status register: 0x%02x\n", retval);
-
-    return retval;
-}
-
-/* Data select rate register : 0x04 (write) */
-static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value)
-{
-    /* Reset mode */
-    if (!(fdctrl->dor & FD_DOR_nRESET)) {
-        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
-        return;
-    }
-    FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value);
-    /* Reset: autoclear */
-    if (value & FD_DSR_SWRESET) {
-        fdctrl->dor &= ~FD_DOR_nRESET;
-        fdctrl_reset(fdctrl, 1);
-        fdctrl->dor |= FD_DOR_nRESET;
-    }
-    if (value & FD_DSR_PWRDOWN) {
-        fdctrl_reset(fdctrl, 1);
-    }
-    fdctrl->dsr = value;
-}
-
-/* Configuration control register: 0x07 (write) */
-static void fdctrl_write_ccr(FDCtrl *fdctrl, uint32_t value)
-{
-    /* Reset mode */
-    if (!(fdctrl->dor & FD_DOR_nRESET)) {
-        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
-        return;
-    }
-    FLOPPY_DPRINTF("configuration control register set to 0x%02x\n", value);
-
-    /* Only the rate selection bits used in AT mode, and we
-     * store those in the DSR.
-     */
-    fdctrl->dsr = (fdctrl->dsr & ~FD_DSR_DRATEMASK) |
-                  (value & FD_DSR_DRATEMASK);
-}
-
-static int fdctrl_media_changed(FDrive *drv)
-{
-    return drv->media_changed;
-}
-
-/* Digital input register : 0x07 (read-only) */
-static uint32_t fdctrl_read_dir(FDCtrl *fdctrl)
-{
-    uint32_t retval = 0;
-
-    if (fdctrl_media_changed(get_cur_drv(fdctrl))) {
-        retval |= FD_DIR_DSKCHG;
-    }
-    if (retval != 0) {
-        FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval);
-    }
-
-    return retval;
-}
-
-/* FIFO state control */
-static void fdctrl_reset_fifo(FDCtrl *fdctrl)
-{
-    fdctrl->data_dir = FD_DIR_WRITE;
-    fdctrl->data_pos = 0;
-    fdctrl->msr &= ~(FD_MSR_CMDBUSY | FD_MSR_DIO);
-}
-
-/* Set FIFO status for the host to read */
-static void fdctrl_set_fifo(FDCtrl *fdctrl, int fifo_len)
-{
-    fdctrl->data_dir = FD_DIR_READ;
-    fdctrl->data_len = fifo_len;
-    fdctrl->data_pos = 0;
-    fdctrl->msr |= FD_MSR_CMDBUSY | FD_MSR_RQM | FD_MSR_DIO;
-}
-
-/* Set an error: unimplemented/unknown command */
-static void fdctrl_unimplemented(FDCtrl *fdctrl, int direction)
-{
-    qemu_log_mask(LOG_UNIMP, "fdc: unimplemented command 0x%02x\n",
-                  fdctrl->fifo[0]);
-    fdctrl->fifo[0] = FD_SR0_INVCMD;
-    fdctrl_set_fifo(fdctrl, 1);
-}
-
-/* Seek to next sector
- * returns 0 when end of track reached (for DBL_SIDES on head 1)
- * otherwise returns 1
- */
-static int fdctrl_seek_to_next_sect(FDCtrl *fdctrl, FDrive *cur_drv)
-{
-    FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d)\n",
-                   cur_drv->head, cur_drv->track, cur_drv->sect,
-                   fd_sector(cur_drv));
-    /* XXX: cur_drv->sect >= cur_drv->last_sect should be an
-       error in fact */
-    uint8_t new_head = cur_drv->head;
-    uint8_t new_track = cur_drv->track;
-    uint8_t new_sect = cur_drv->sect;
-
-    int ret = 1;
-
-    if (new_sect >= cur_drv->last_sect ||
-        new_sect == fdctrl->eot) {
-        new_sect = 1;
-        if (FD_MULTI_TRACK(fdctrl->data_state)) {
-            if (new_head == 0 &&
-                (cur_drv->flags & FDISK_DBL_SIDES) != 0) {
-                new_head = 1;
-            } else {
-                new_head = 0;
-                new_track++;
-                fdctrl->status0 |= FD_SR0_SEEK;
-                if ((cur_drv->flags & FDISK_DBL_SIDES) == 0) {
-                    ret = 0;
-                }
-            }
-        } else {
-            fdctrl->status0 |= FD_SR0_SEEK;
-            new_track++;
-            ret = 0;
-        }
-        if (ret == 1) {
-            FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
-                    new_head, new_track, new_sect, fd_sector(cur_drv));
-        }
-    } else {
-        new_sect++;
-    }
-    fd_seek(cur_drv, new_head, new_track, new_sect, 1);
-    return ret;
-}
-
-/* Callback for transfer end (stop or abort) */
-static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0,
-                                 uint8_t status1, uint8_t status2)
-{
-    FDrive *cur_drv;
-    cur_drv = get_cur_drv(fdctrl);
-
-    fdctrl->status0 &= ~(FD_SR0_DS0 | FD_SR0_DS1 | FD_SR0_HEAD);
-    fdctrl->status0 |= GET_CUR_DRV(fdctrl);
-    if (cur_drv->head) {
-        fdctrl->status0 |= FD_SR0_HEAD;
-    }
-    fdctrl->status0 |= status0;
-
-    FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
-                   status0, status1, status2, fdctrl->status0);
-    fdctrl->fifo[0] = fdctrl->status0;
-    fdctrl->fifo[1] = status1;
-    fdctrl->fifo[2] = status2;
-    fdctrl->fifo[3] = cur_drv->track;
-    fdctrl->fifo[4] = cur_drv->head;
-    fdctrl->fifo[5] = cur_drv->sect;
-    fdctrl->fifo[6] = FD_SECTOR_SC;
-    fdctrl->data_dir = FD_DIR_READ;
-    if (!(fdctrl->msr & FD_MSR_NONDMA)) {
-        DMA_release_DREQ(fdctrl->dma_chann);
-    }
-    fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO;
-    fdctrl->msr &= ~FD_MSR_NONDMA;
-
-    fdctrl_set_fifo(fdctrl, 7);
-    fdctrl_raise_irq(fdctrl);
-}
-
-/* Prepare a data transfer (either DMA or FIFO) */
-static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction)
-{
-    FDrive *cur_drv;
-    uint8_t kh, kt, ks;
-
-    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
-    cur_drv = get_cur_drv(fdctrl);
-    kt = fdctrl->fifo[2];
-    kh = fdctrl->fifo[3];
-    ks = fdctrl->fifo[4];
-    FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n",
-                   GET_CUR_DRV(fdctrl), kh, kt, ks,
-                   fd_sector_calc(kh, kt, ks, cur_drv->last_sect,
-                                  NUM_SIDES(cur_drv)));
-    switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
-    case 2:
-        /* sect too big */
-        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
-        fdctrl->fifo[3] = kt;
-        fdctrl->fifo[4] = kh;
-        fdctrl->fifo[5] = ks;
-        return;
-    case 3:
-        /* track too big */
-        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00);
-        fdctrl->fifo[3] = kt;
-        fdctrl->fifo[4] = kh;
-        fdctrl->fifo[5] = ks;
-        return;
-    case 4:
-        /* No seek enabled */
-        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
-        fdctrl->fifo[3] = kt;
-        fdctrl->fifo[4] = kh;
-        fdctrl->fifo[5] = ks;
-        return;
-    case 1:
-        fdctrl->status0 |= FD_SR0_SEEK;
-        break;
-    default:
-        break;
-    }
-
-    /* Check the data rate. If the programmed data rate does not match
-     * the currently inserted medium, the operation has to fail. */
-    if (fdctrl->check_media_rate &&
-        (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) {
-        FLOPPY_DPRINTF("data rate mismatch (fdc=%d, media=%d)\n",
-                       fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate);
-        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00);
-        fdctrl->fifo[3] = kt;
-        fdctrl->fifo[4] = kh;
-        fdctrl->fifo[5] = ks;
-        return;
-    }
-
-    /* Set the FIFO state */
-    fdctrl->data_dir = direction;
-    fdctrl->data_pos = 0;
-    assert(fdctrl->msr & FD_MSR_CMDBUSY);
-    if (fdctrl->fifo[0] & 0x80)
-        fdctrl->data_state |= FD_STATE_MULTI;
-    else
-        fdctrl->data_state &= ~FD_STATE_MULTI;
-    if (fdctrl->fifo[5] == 0) {
-        fdctrl->data_len = fdctrl->fifo[8];
-    } else {
-        int tmp;
-        fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]);
-        tmp = (fdctrl->fifo[6] - ks + 1);
-        if (fdctrl->fifo[0] & 0x80)
-            tmp += fdctrl->fifo[6];
-        fdctrl->data_len *= tmp;
-    }
-    fdctrl->eot = fdctrl->fifo[6];
-    if (fdctrl->dor & FD_DOR_DMAEN) {
-        int dma_mode;
-        /* DMA transfer are enabled. Check if DMA channel is well programmed */
-        dma_mode = DMA_get_channel_mode(fdctrl->dma_chann);
-        dma_mode = (dma_mode >> 2) & 3;
-        FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n",
-                       dma_mode, direction,
-                       (128 << fdctrl->fifo[5]) *
-                       (cur_drv->last_sect - ks + 1), fdctrl->data_len);
-        if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL ||
-              direction == FD_DIR_SCANH) && dma_mode == 0) ||
-            (direction == FD_DIR_WRITE && dma_mode == 2) ||
-            (direction == FD_DIR_READ && dma_mode == 1) ||
-            (direction == FD_DIR_VERIFY)) {
-            /* No access is allowed until DMA transfer has completed */
-            fdctrl->msr &= ~FD_MSR_RQM;
-            if (direction != FD_DIR_VERIFY) {
-                /* Now, we just have to wait for the DMA controller to
-                 * recall us...
-                 */
-                DMA_hold_DREQ(fdctrl->dma_chann);
-                DMA_schedule(fdctrl->dma_chann);
-            } else {
-                /* Start transfer */
-                fdctrl_transfer_handler(fdctrl, fdctrl->dma_chann, 0,
-                                        fdctrl->data_len);
-            }
-            return;
-        } else {
-            FLOPPY_DPRINTF("bad dma_mode=%d direction=%d\n", dma_mode,
-                           direction);
-        }
-    }
-    FLOPPY_DPRINTF("start non-DMA transfer\n");
-    fdctrl->msr |= FD_MSR_NONDMA;
-    if (direction != FD_DIR_WRITE)
-        fdctrl->msr |= FD_MSR_DIO;
-    /* IO based transfer: calculate len */
-    fdctrl_raise_irq(fdctrl);
-}
-
-/* Prepare a transfer of deleted data */
-static void fdctrl_start_transfer_del(FDCtrl *fdctrl, int direction)
-{
-    qemu_log_mask(LOG_UNIMP, "fdctrl_start_transfer_del() unimplemented\n");
-
-    /* We don't handle deleted data,
-     * so we don't return *ANYTHING*
-     */
-    fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
-}
-
-/* handlers for DMA transfers */
-static int fdctrl_transfer_handler (void *opaque, int nchan,
-                                    int dma_pos, int dma_len)
-{
-    FDCtrl *fdctrl;
-    FDrive *cur_drv;
-    int len, start_pos, rel_pos;
-    uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00;
-
-    fdctrl = opaque;
-    if (fdctrl->msr & FD_MSR_RQM) {
-        FLOPPY_DPRINTF("Not in DMA transfer mode !\n");
-        return 0;
-    }
-    cur_drv = get_cur_drv(fdctrl);
-    if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL ||
-        fdctrl->data_dir == FD_DIR_SCANH)
-        status2 = FD_SR2_SNS;
-    if (dma_len > fdctrl->data_len)
-        dma_len = fdctrl->data_len;
-    if (cur_drv->bs == NULL) {
-        if (fdctrl->data_dir == FD_DIR_WRITE)
-            fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
-        else
-            fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
-        len = 0;
-        goto transfer_error;
-    }
-    rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
-    for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) {
-        len = dma_len - fdctrl->data_pos;
-        if (len + rel_pos > FD_SECTOR_LEN)
-            len = FD_SECTOR_LEN - rel_pos;
-        FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x "
-                       "(%d-0x%08x 0x%08x)\n", len, dma_len, fdctrl->data_pos,
-                       fdctrl->data_len, GET_CUR_DRV(fdctrl), cur_drv->head,
-                       cur_drv->track, cur_drv->sect, fd_sector(cur_drv),
-                       fd_sector(cur_drv) * FD_SECTOR_LEN);
-        if (fdctrl->data_dir != FD_DIR_WRITE ||
-            len < FD_SECTOR_LEN || rel_pos != 0) {
-            /* READ & SCAN commands and realign to a sector for WRITE */
-            if (bdrv_read(cur_drv->bs, fd_sector(cur_drv),
-                          fdctrl->fifo, 1) < 0) {
-                FLOPPY_DPRINTF("Floppy: error getting sector %d\n",
-                               fd_sector(cur_drv));
-                /* Sure, image size is too small... */
-                memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
-            }
-        }
-        switch (fdctrl->data_dir) {
-        case FD_DIR_READ:
-            /* READ commands */
-            DMA_write_memory (nchan, fdctrl->fifo + rel_pos,
-                              fdctrl->data_pos, len);
-            break;
-        case FD_DIR_WRITE:
-            /* WRITE commands */
-            if (cur_drv->ro) {
-                /* Handle readonly medium early, no need to do DMA, touch the
-                 * LED or attempt any writes. A real floppy doesn't attempt
-                 * to write to readonly media either. */
-                fdctrl_stop_transfer(fdctrl,
-                                     FD_SR0_ABNTERM | FD_SR0_SEEK, FD_SR1_NW,
-                                     0x00);
-                goto transfer_error;
-            }
-
-            DMA_read_memory (nchan, fdctrl->fifo + rel_pos,
-                             fdctrl->data_pos, len);
-            if (bdrv_write(cur_drv->bs, fd_sector(cur_drv),
-                           fdctrl->fifo, 1) < 0) {
-                FLOPPY_DPRINTF("error writing sector %d\n",
-                               fd_sector(cur_drv));
-                fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
-                goto transfer_error;
-            }
-            break;
-        case FD_DIR_VERIFY:
-            /* VERIFY commands */
-            break;
-        default:
-            /* SCAN commands */
-            {
-                uint8_t tmpbuf[FD_SECTOR_LEN];
-                int ret;
-                DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len);
-                ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len);
-                if (ret == 0) {
-                    status2 = FD_SR2_SEH;
-                    goto end_transfer;
-                }
-                if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) ||
-                    (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) {
-                    status2 = 0x00;
-                    goto end_transfer;
-                }
-            }
-            break;
-        }
-        fdctrl->data_pos += len;
-        rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
-        if (rel_pos == 0) {
-            /* Seek to next sector */
-            if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv))
-                break;
-        }
-    }
- end_transfer:
-    len = fdctrl->data_pos - start_pos;
-    FLOPPY_DPRINTF("end transfer %d %d %d\n",
-                   fdctrl->data_pos, len, fdctrl->data_len);
-    if (fdctrl->data_dir == FD_DIR_SCANE ||
-        fdctrl->data_dir == FD_DIR_SCANL ||
-        fdctrl->data_dir == FD_DIR_SCANH)
-        status2 = FD_SR2_SEH;
-    fdctrl->data_len -= len;
-    fdctrl_stop_transfer(fdctrl, status0, status1, status2);
- transfer_error:
-
-    return len;
-}
-
-/* Data register : 0x05 */
-static uint32_t fdctrl_read_data(FDCtrl *fdctrl)
-{
-    FDrive *cur_drv;
-    uint32_t retval = 0;
-    int pos;
-
-    cur_drv = get_cur_drv(fdctrl);
-    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
-    if (!(fdctrl->msr & FD_MSR_RQM) || !(fdctrl->msr & FD_MSR_DIO)) {
-        FLOPPY_DPRINTF("error: controller not ready for reading\n");
-        return 0;
-    }
-    pos = fdctrl->data_pos;
-    if (fdctrl->msr & FD_MSR_NONDMA) {
-        pos %= FD_SECTOR_LEN;
-        if (pos == 0) {
-            if (fdctrl->data_pos != 0)
-                if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
-                    FLOPPY_DPRINTF("error seeking to next sector %d\n",
-                                   fd_sector(cur_drv));
-                    return 0;
-                }
-            if (bdrv_read(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
-                FLOPPY_DPRINTF("error getting sector %d\n",
-                               fd_sector(cur_drv));
-                /* Sure, image size is too small... */
-                memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
-            }
-        }
-    }
-    retval = fdctrl->fifo[pos];
-    if (++fdctrl->data_pos == fdctrl->data_len) {
-        fdctrl->data_pos = 0;
-        /* Switch from transfer mode to status mode
-         * then from status mode to command mode
-         */
-        if (fdctrl->msr & FD_MSR_NONDMA) {
-            fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
-        } else {
-            fdctrl_reset_fifo(fdctrl);
-            fdctrl_reset_irq(fdctrl);
-        }
-    }
-    FLOPPY_DPRINTF("data register: 0x%02x\n", retval);
-
-    return retval;
-}
-
-static void fdctrl_format_sector(FDCtrl *fdctrl)
-{
-    FDrive *cur_drv;
-    uint8_t kh, kt, ks;
-
-    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
-    cur_drv = get_cur_drv(fdctrl);
-    kt = fdctrl->fifo[6];
-    kh = fdctrl->fifo[7];
-    ks = fdctrl->fifo[8];
-    FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n",
-                   GET_CUR_DRV(fdctrl), kh, kt, ks,
-                   fd_sector_calc(kh, kt, ks, cur_drv->last_sect,
-                                  NUM_SIDES(cur_drv)));
-    switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) {
-    case 2:
-        /* sect too big */
-        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
-        fdctrl->fifo[3] = kt;
-        fdctrl->fifo[4] = kh;
-        fdctrl->fifo[5] = ks;
-        return;
-    case 3:
-        /* track too big */
-        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00);
-        fdctrl->fifo[3] = kt;
-        fdctrl->fifo[4] = kh;
-        fdctrl->fifo[5] = ks;
-        return;
-    case 4:
-        /* No seek enabled */
-        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00);
-        fdctrl->fifo[3] = kt;
-        fdctrl->fifo[4] = kh;
-        fdctrl->fifo[5] = ks;
-        return;
-    case 1:
-        fdctrl->status0 |= FD_SR0_SEEK;
-        break;
-    default:
-        break;
-    }
-    memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
-    if (cur_drv->bs == NULL ||
-        bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
-        FLOPPY_DPRINTF("error formatting sector %d\n", fd_sector(cur_drv));
-        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM | FD_SR0_SEEK, 0x00, 0x00);
-    } else {
-        if (cur_drv->sect == cur_drv->last_sect) {
-            fdctrl->data_state &= ~FD_STATE_FORMAT;
-            /* Last sector done */
-            fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
-        } else {
-            /* More to do */
-            fdctrl->data_pos = 0;
-            fdctrl->data_len = 4;
-        }
-    }
-}
-
-static void fdctrl_handle_lock(FDCtrl *fdctrl, int direction)
-{
-    fdctrl->lock = (fdctrl->fifo[0] & 0x80) ? 1 : 0;
-    fdctrl->fifo[0] = fdctrl->lock << 4;
-    fdctrl_set_fifo(fdctrl, 1);
-}
-
-static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction)
-{
-    FDrive *cur_drv = get_cur_drv(fdctrl);
-
-    /* Drives position */
-    fdctrl->fifo[0] = drv0(fdctrl)->track;
-    fdctrl->fifo[1] = drv1(fdctrl)->track;
-#if MAX_FD == 4
-    fdctrl->fifo[2] = drv2(fdctrl)->track;
-    fdctrl->fifo[3] = drv3(fdctrl)->track;
-#else
-    fdctrl->fifo[2] = 0;
-    fdctrl->fifo[3] = 0;
-#endif
-    /* timers */
-    fdctrl->fifo[4] = fdctrl->timer0;
-    fdctrl->fifo[5] = (fdctrl->timer1 << 1) | (fdctrl->dor & FD_DOR_DMAEN ? 1 : 0);
-    fdctrl->fifo[6] = cur_drv->last_sect;
-    fdctrl->fifo[7] = (fdctrl->lock << 7) |
-        (cur_drv->perpendicular << 2);
-    fdctrl->fifo[8] = fdctrl->config;
-    fdctrl->fifo[9] = fdctrl->precomp_trk;
-    fdctrl_set_fifo(fdctrl, 10);
-}
-
-static void fdctrl_handle_version(FDCtrl *fdctrl, int direction)
-{
-    /* Controller's version */
-    fdctrl->fifo[0] = fdctrl->version;
-    fdctrl_set_fifo(fdctrl, 1);
-}
-
-static void fdctrl_handle_partid(FDCtrl *fdctrl, int direction)
-{
-    fdctrl->fifo[0] = 0x41; /* Stepping 1 */
-    fdctrl_set_fifo(fdctrl, 1);
-}
-
-static void fdctrl_handle_restore(FDCtrl *fdctrl, int direction)
-{
-    FDrive *cur_drv = get_cur_drv(fdctrl);
-
-    /* Drives position */
-    drv0(fdctrl)->track = fdctrl->fifo[3];
-    drv1(fdctrl)->track = fdctrl->fifo[4];
-#if MAX_FD == 4
-    drv2(fdctrl)->track = fdctrl->fifo[5];
-    drv3(fdctrl)->track = fdctrl->fifo[6];
-#endif
-    /* timers */
-    fdctrl->timer0 = fdctrl->fifo[7];
-    fdctrl->timer1 = fdctrl->fifo[8];
-    cur_drv->last_sect = fdctrl->fifo[9];
-    fdctrl->lock = fdctrl->fifo[10] >> 7;
-    cur_drv->perpendicular = (fdctrl->fifo[10] >> 2) & 0xF;
-    fdctrl->config = fdctrl->fifo[11];
-    fdctrl->precomp_trk = fdctrl->fifo[12];
-    fdctrl->pwrd = fdctrl->fifo[13];
-    fdctrl_reset_fifo(fdctrl);
-}
-
-static void fdctrl_handle_save(FDCtrl *fdctrl, int direction)
-{
-    FDrive *cur_drv = get_cur_drv(fdctrl);
-
-    fdctrl->fifo[0] = 0;
-    fdctrl->fifo[1] = 0;
-    /* Drives position */
-    fdctrl->fifo[2] = drv0(fdctrl)->track;
-    fdctrl->fifo[3] = drv1(fdctrl)->track;
-#if MAX_FD == 4
-    fdctrl->fifo[4] = drv2(fdctrl)->track;
-    fdctrl->fifo[5] = drv3(fdctrl)->track;
-#else
-    fdctrl->fifo[4] = 0;
-    fdctrl->fifo[5] = 0;
-#endif
-    /* timers */
-    fdctrl->fifo[6] = fdctrl->timer0;
-    fdctrl->fifo[7] = fdctrl->timer1;
-    fdctrl->fifo[8] = cur_drv->last_sect;
-    fdctrl->fifo[9] = (fdctrl->lock << 7) |
-        (cur_drv->perpendicular << 2);
-    fdctrl->fifo[10] = fdctrl->config;
-    fdctrl->fifo[11] = fdctrl->precomp_trk;
-    fdctrl->fifo[12] = fdctrl->pwrd;
-    fdctrl->fifo[13] = 0;
-    fdctrl->fifo[14] = 0;
-    fdctrl_set_fifo(fdctrl, 15);
-}
-
-static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction)
-{
-    FDrive *cur_drv = get_cur_drv(fdctrl);
-
-    cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
-    qemu_mod_timer(fdctrl->result_timer,
-                   qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / 50));
-}
-
-static void fdctrl_handle_format_track(FDCtrl *fdctrl, int direction)
-{
-    FDrive *cur_drv;
-
-    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
-    cur_drv = get_cur_drv(fdctrl);
-    fdctrl->data_state |= FD_STATE_FORMAT;
-    if (fdctrl->fifo[0] & 0x80)
-        fdctrl->data_state |= FD_STATE_MULTI;
-    else
-        fdctrl->data_state &= ~FD_STATE_MULTI;
-    cur_drv->bps =
-        fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2];
-#if 0
-    cur_drv->last_sect =
-        cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] :
-        fdctrl->fifo[3] / 2;
-#else
-    cur_drv->last_sect = fdctrl->fifo[3];
-#endif
-    /* TODO: implement format using DMA expected by the Bochs BIOS
-     * and Linux fdformat (read 3 bytes per sector via DMA and fill
-     * the sector with the specified fill byte
-     */
-    fdctrl->data_state &= ~FD_STATE_FORMAT;
-    fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
-}
-
-static void fdctrl_handle_specify(FDCtrl *fdctrl, int direction)
-{
-    fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF;
-    fdctrl->timer1 = fdctrl->fifo[2] >> 1;
-    if (fdctrl->fifo[2] & 1)
-        fdctrl->dor &= ~FD_DOR_DMAEN;
-    else
-        fdctrl->dor |= FD_DOR_DMAEN;
-    /* No result back */
-    fdctrl_reset_fifo(fdctrl);
-}
-
-static void fdctrl_handle_sense_drive_status(FDCtrl *fdctrl, int direction)
-{
-    FDrive *cur_drv;
-
-    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
-    cur_drv = get_cur_drv(fdctrl);
-    cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
-    /* 1 Byte status back */
-    fdctrl->fifo[0] = (cur_drv->ro << 6) |
-        (cur_drv->track == 0 ? 0x10 : 0x00) |
-        (cur_drv->head << 2) |
-        GET_CUR_DRV(fdctrl) |
-        0x28;
-    fdctrl_set_fifo(fdctrl, 1);
-}
-
-static void fdctrl_handle_recalibrate(FDCtrl *fdctrl, int direction)
-{
-    FDrive *cur_drv;
-
-    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
-    cur_drv = get_cur_drv(fdctrl);
-    fd_recalibrate(cur_drv);
-    fdctrl_reset_fifo(fdctrl);
-    /* Raise Interrupt */
-    fdctrl->status0 |= FD_SR0_SEEK;
-    fdctrl_raise_irq(fdctrl);
-}
-
-static void fdctrl_handle_sense_interrupt_status(FDCtrl *fdctrl, int direction)
-{
-    FDrive *cur_drv = get_cur_drv(fdctrl);
-
-    if (fdctrl->reset_sensei > 0) {
-        fdctrl->fifo[0] =
-            FD_SR0_RDYCHG + FD_RESET_SENSEI_COUNT - fdctrl->reset_sensei;
-        fdctrl->reset_sensei--;
-    } else if (!(fdctrl->sra & FD_SRA_INTPEND)) {
-        fdctrl->fifo[0] = FD_SR0_INVCMD;
-        fdctrl_set_fifo(fdctrl, 1);
-        return;
-    } else {
-        fdctrl->fifo[0] =
-                (fdctrl->status0 & ~(FD_SR0_HEAD | FD_SR0_DS1 | FD_SR0_DS0))
-                | GET_CUR_DRV(fdctrl);
-    }
-
-    fdctrl->fifo[1] = cur_drv->track;
-    fdctrl_set_fifo(fdctrl, 2);
-    fdctrl_reset_irq(fdctrl);
-    fdctrl->status0 = FD_SR0_RDYCHG;
-}
-
-static void fdctrl_handle_seek(FDCtrl *fdctrl, int direction)
-{
-    FDrive *cur_drv;
-
-    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
-    cur_drv = get_cur_drv(fdctrl);
-    fdctrl_reset_fifo(fdctrl);
-    /* The seek command just sends step pulses to the drive and doesn't care if
-     * there is a medium inserted of if it's banging the head against the drive.
-     */
-    fd_seek(cur_drv, cur_drv->head, fdctrl->fifo[2], cur_drv->sect, 1);
-    /* Raise Interrupt */
-    fdctrl->status0 |= FD_SR0_SEEK;
-    fdctrl_raise_irq(fdctrl);
-}
-
-static void fdctrl_handle_perpendicular_mode(FDCtrl *fdctrl, int direction)
-{
-    FDrive *cur_drv = get_cur_drv(fdctrl);
-
-    if (fdctrl->fifo[1] & 0x80)
-        cur_drv->perpendicular = fdctrl->fifo[1] & 0x7;
-    /* No result back */
-    fdctrl_reset_fifo(fdctrl);
-}
-
-static void fdctrl_handle_configure(FDCtrl *fdctrl, int direction)
-{
-    fdctrl->config = fdctrl->fifo[2];
-    fdctrl->precomp_trk =  fdctrl->fifo[3];
-    /* No result back */
-    fdctrl_reset_fifo(fdctrl);
-}
-
-static void fdctrl_handle_powerdown_mode(FDCtrl *fdctrl, int direction)
-{
-    fdctrl->pwrd = fdctrl->fifo[1];
-    fdctrl->fifo[0] = fdctrl->fifo[1];
-    fdctrl_set_fifo(fdctrl, 1);
-}
-
-static void fdctrl_handle_option(FDCtrl *fdctrl, int direction)
-{
-    /* No result back */
-    fdctrl_reset_fifo(fdctrl);
-}
-
-static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direction)
-{
-    FDrive *cur_drv = get_cur_drv(fdctrl);
-
-    if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) {
-        /* Command parameters done */
-        if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) {
-            fdctrl->fifo[0] = fdctrl->fifo[1];
-            fdctrl->fifo[2] = 0;
-            fdctrl->fifo[3] = 0;
-            fdctrl_set_fifo(fdctrl, 4);
-        } else {
-            fdctrl_reset_fifo(fdctrl);
-        }
-    } else if (fdctrl->data_len > 7) {
-        /* ERROR */
-        fdctrl->fifo[0] = 0x80 |
-            (cur_drv->head << 2) | GET_CUR_DRV(fdctrl);
-        fdctrl_set_fifo(fdctrl, 1);
-    }
-}
-
-static void fdctrl_handle_relative_seek_in(FDCtrl *fdctrl, int direction)
-{
-    FDrive *cur_drv;
-
-    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
-    cur_drv = get_cur_drv(fdctrl);
-    if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) {
-        fd_seek(cur_drv, cur_drv->head, cur_drv->max_track - 1,
-                cur_drv->sect, 1);
-    } else {
-        fd_seek(cur_drv, cur_drv->head,
-                cur_drv->track + fdctrl->fifo[2], cur_drv->sect, 1);
-    }
-    fdctrl_reset_fifo(fdctrl);
-    /* Raise Interrupt */
-    fdctrl->status0 |= FD_SR0_SEEK;
-    fdctrl_raise_irq(fdctrl);
-}
-
-static void fdctrl_handle_relative_seek_out(FDCtrl *fdctrl, int direction)
-{
-    FDrive *cur_drv;
-
-    SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK);
-    cur_drv = get_cur_drv(fdctrl);
-    if (fdctrl->fifo[2] > cur_drv->track) {
-        fd_seek(cur_drv, cur_drv->head, 0, cur_drv->sect, 1);
-    } else {
-        fd_seek(cur_drv, cur_drv->head,
-                cur_drv->track - fdctrl->fifo[2], cur_drv->sect, 1);
-    }
-    fdctrl_reset_fifo(fdctrl);
-    /* Raise Interrupt */
-    fdctrl->status0 |= FD_SR0_SEEK;
-    fdctrl_raise_irq(fdctrl);
-}
-
-static const struct {
-    uint8_t value;
-    uint8_t mask;
-    const char* name;
-    int parameters;
-    void (*handler)(FDCtrl *fdctrl, int direction);
-    int direction;
-} handlers[] = {
-    { FD_CMD_READ, 0x1f, "READ", 8, fdctrl_start_transfer, FD_DIR_READ },
-    { FD_CMD_WRITE, 0x3f, "WRITE", 8, fdctrl_start_transfer, FD_DIR_WRITE },
-    { FD_CMD_SEEK, 0xff, "SEEK", 2, fdctrl_handle_seek },
-    { FD_CMD_SENSE_INTERRUPT_STATUS, 0xff, "SENSE INTERRUPT STATUS", 0, fdctrl_handle_sense_interrupt_status },
-    { FD_CMD_RECALIBRATE, 0xff, "RECALIBRATE", 1, fdctrl_handle_recalibrate },
-    { FD_CMD_FORMAT_TRACK, 0xbf, "FORMAT TRACK", 5, fdctrl_handle_format_track },
-    { FD_CMD_READ_TRACK, 0xbf, "READ TRACK", 8, fdctrl_start_transfer, FD_DIR_READ },
-    { FD_CMD_RESTORE, 0xff, "RESTORE", 17, fdctrl_handle_restore }, /* part of READ DELETED DATA */
-    { FD_CMD_SAVE, 0xff, "SAVE", 0, fdctrl_handle_save }, /* part of READ DELETED DATA */
-    { FD_CMD_READ_DELETED, 0x1f, "READ DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_READ },
-    { FD_CMD_SCAN_EQUAL, 0x1f, "SCAN EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANE },
-    { FD_CMD_VERIFY, 0x1f, "VERIFY", 8, fdctrl_start_transfer, FD_DIR_VERIFY },
-    { FD_CMD_SCAN_LOW_OR_EQUAL, 0x1f, "SCAN LOW OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANL },
-    { FD_CMD_SCAN_HIGH_OR_EQUAL, 0x1f, "SCAN HIGH OR EQUAL", 8, fdctrl_start_transfer, FD_DIR_SCANH },
-    { FD_CMD_WRITE_DELETED, 0x3f, "WRITE DELETED DATA", 8, fdctrl_start_transfer_del, FD_DIR_WRITE },
-    { FD_CMD_READ_ID, 0xbf, "READ ID", 1, fdctrl_handle_readid },
-    { FD_CMD_SPECIFY, 0xff, "SPECIFY", 2, fdctrl_handle_specify },
-    { FD_CMD_SENSE_DRIVE_STATUS, 0xff, "SENSE DRIVE STATUS", 1, fdctrl_handle_sense_drive_status },
-    { FD_CMD_PERPENDICULAR_MODE, 0xff, "PERPENDICULAR MODE", 1, fdctrl_handle_perpendicular_mode },
-    { FD_CMD_CONFIGURE, 0xff, "CONFIGURE", 3, fdctrl_handle_configure },
-    { FD_CMD_POWERDOWN_MODE, 0xff, "POWERDOWN MODE", 2, fdctrl_handle_powerdown_mode },
-    { FD_CMD_OPTION, 0xff, "OPTION", 1, fdctrl_handle_option },
-    { FD_CMD_DRIVE_SPECIFICATION_COMMAND, 0xff, "DRIVE SPECIFICATION COMMAND", 5, fdctrl_handle_drive_specification_command },
-    { FD_CMD_RELATIVE_SEEK_OUT, 0xff, "RELATIVE SEEK OUT", 2, fdctrl_handle_relative_seek_out },
-    { FD_CMD_FORMAT_AND_WRITE, 0xff, "FORMAT AND WRITE", 10, fdctrl_unimplemented },
-    { FD_CMD_RELATIVE_SEEK_IN, 0xff, "RELATIVE SEEK IN", 2, fdctrl_handle_relative_seek_in },
-    { FD_CMD_LOCK, 0x7f, "LOCK", 0, fdctrl_handle_lock },
-    { FD_CMD_DUMPREG, 0xff, "DUMPREG", 0, fdctrl_handle_dumpreg },
-    { FD_CMD_VERSION, 0xff, "VERSION", 0, fdctrl_handle_version },
-    { FD_CMD_PART_ID, 0xff, "PART ID", 0, fdctrl_handle_partid },
-    { FD_CMD_WRITE, 0x1f, "WRITE (BeOS)", 8, fdctrl_start_transfer, FD_DIR_WRITE }, /* not in specification ; BeOS 4.5 bug */
-    { 0, 0, "unknown", 0, fdctrl_unimplemented }, /* default handler */
-};
-/* Associate command to an index in the 'handlers' array */
-static uint8_t command_to_handler[256];
-
-static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value)
-{
-    FDrive *cur_drv;
-    int pos;
-
-    /* Reset mode */
-    if (!(fdctrl->dor & FD_DOR_nRESET)) {
-        FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
-        return;
-    }
-    if (!(fdctrl->msr & FD_MSR_RQM) || (fdctrl->msr & FD_MSR_DIO)) {
-        FLOPPY_DPRINTF("error: controller not ready for writing\n");
-        return;
-    }
-    fdctrl->dsr &= ~FD_DSR_PWRDOWN;
-    /* Is it write command time ? */
-    if (fdctrl->msr & FD_MSR_NONDMA) {
-        /* FIFO data write */
-        pos = fdctrl->data_pos++;
-        pos %= FD_SECTOR_LEN;
-        fdctrl->fifo[pos] = value;
-        if (pos == FD_SECTOR_LEN - 1 ||
-            fdctrl->data_pos == fdctrl->data_len) {
-            cur_drv = get_cur_drv(fdctrl);
-            if (bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
-                FLOPPY_DPRINTF("error writing sector %d\n",
-                               fd_sector(cur_drv));
-                return;
-            }
-            if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) {
-                FLOPPY_DPRINTF("error seeking to next sector %d\n",
-                               fd_sector(cur_drv));
-                return;
-            }
-        }
-        /* Switch from transfer mode to status mode
-         * then from status mode to command mode
-         */
-        if (fdctrl->data_pos == fdctrl->data_len)
-            fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
-        return;
-    }
-    if (fdctrl->data_pos == 0) {
-        /* Command */
-        pos = command_to_handler[value & 0xff];
-        FLOPPY_DPRINTF("%s command\n", handlers[pos].name);
-        fdctrl->data_len = handlers[pos].parameters + 1;
-        fdctrl->msr |= FD_MSR_CMDBUSY;
-    }
-
-    FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
-    fdctrl->fifo[fdctrl->data_pos++] = value;
-    if (fdctrl->data_pos == fdctrl->data_len) {
-        /* We now have all parameters
-         * and will be able to treat the command
-         */
-        if (fdctrl->data_state & FD_STATE_FORMAT) {
-            fdctrl_format_sector(fdctrl);
-            return;
-        }
-
-        pos = command_to_handler[fdctrl->fifo[0] & 0xff];
-        FLOPPY_DPRINTF("treat %s command\n", handlers[pos].name);
-        (*handlers[pos].handler)(fdctrl, handlers[pos].direction);
-    }
-}
-
-static void fdctrl_result_timer(void *opaque)
-{
-    FDCtrl *fdctrl = opaque;
-    FDrive *cur_drv = get_cur_drv(fdctrl);
-
-    /* Pretend we are spinning.
-     * This is needed for Coherent, which uses READ ID to check for
-     * sector interleaving.
-     */
-    if (cur_drv->last_sect != 0) {
-        cur_drv->sect = (cur_drv->sect % cur_drv->last_sect) + 1;
-    }
-    /* READ_ID can't automatically succeed! */
-    if (fdctrl->check_media_rate &&
-        (fdctrl->dsr & FD_DSR_DRATEMASK) != cur_drv->media_rate) {
-        FLOPPY_DPRINTF("read id rate mismatch (fdc=%d, media=%d)\n",
-                       fdctrl->dsr & FD_DSR_DRATEMASK, cur_drv->media_rate);
-        fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00);
-    } else {
-        fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
-    }
-}
-
-static void fdctrl_change_cb(void *opaque, bool load)
-{
-    FDrive *drive = opaque;
-
-    drive->media_changed = 1;
-    fd_revalidate(drive);
-}
-
-static const BlockDevOps fdctrl_block_ops = {
-    .change_media_cb = fdctrl_change_cb,
-};
-
-/* Init functions */
-static int fdctrl_connect_drives(FDCtrl *fdctrl)
-{
-    unsigned int i;
-    FDrive *drive;
-
-    for (i = 0; i < MAX_FD; i++) {
-        drive = &fdctrl->drives[i];
-        drive->fdctrl = fdctrl;
-
-        if (drive->bs) {
-            if (bdrv_get_on_error(drive->bs, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
-                error_report("fdc doesn't support drive option werror");
-                return -1;
-            }
-            if (bdrv_get_on_error(drive->bs, 1) != BLOCKDEV_ON_ERROR_REPORT) {
-                error_report("fdc doesn't support drive option rerror");
-                return -1;
-            }
-        }
-
-        fd_init(drive);
-        fdctrl_change_cb(drive, 0);
-        if (drive->bs) {
-            bdrv_set_dev_ops(drive->bs, &fdctrl_block_ops, drive);
-        }
-    }
-    return 0;
-}
-
-ISADevice *fdctrl_init_isa(ISABus *bus, DriveInfo **fds)
-{
-    ISADevice *dev;
-
-    dev = isa_try_create(bus, "isa-fdc");
-    if (!dev) {
-        return NULL;
-    }
-
-    if (fds[0]) {
-        qdev_prop_set_drive_nofail(&dev->qdev, "driveA", fds[0]->bdrv);
-    }
-    if (fds[1]) {
-        qdev_prop_set_drive_nofail(&dev->qdev, "driveB", fds[1]->bdrv);
-    }
-    qdev_init_nofail(&dev->qdev);
-
-    return dev;
-}
-
-void fdctrl_init_sysbus(qemu_irq irq, int dma_chann,
-                        hwaddr mmio_base, DriveInfo **fds)
-{
-    FDCtrl *fdctrl;
-    DeviceState *dev;
-    FDCtrlSysBus *sys;
-
-    dev = qdev_create(NULL, "sysbus-fdc");
-    sys = DO_UPCAST(FDCtrlSysBus, busdev.qdev, dev);
-    fdctrl = &sys->state;
-    fdctrl->dma_chann = dma_chann; /* FIXME */
-    if (fds[0]) {
-        qdev_prop_set_drive_nofail(dev, "driveA", fds[0]->bdrv);
-    }
-    if (fds[1]) {
-        qdev_prop_set_drive_nofail(dev, "driveB", fds[1]->bdrv);
-    }
-    qdev_init_nofail(dev);
-    sysbus_connect_irq(&sys->busdev, 0, irq);
-    sysbus_mmio_map(&sys->busdev, 0, mmio_base);
-}
-
-void sun4m_fdctrl_init(qemu_irq irq, hwaddr io_base,
-                       DriveInfo **fds, qemu_irq *fdc_tc)
-{
-    DeviceState *dev;
-    FDCtrlSysBus *sys;
-
-    dev = qdev_create(NULL, "SUNW,fdtwo");
-    if (fds[0]) {
-        qdev_prop_set_drive_nofail(dev, "drive", fds[0]->bdrv);
-    }
-    qdev_init_nofail(dev);
-    sys = DO_UPCAST(FDCtrlSysBus, busdev.qdev, dev);
-    sysbus_connect_irq(&sys->busdev, 0, irq);
-    sysbus_mmio_map(&sys->busdev, 0, io_base);
-    *fdc_tc = qdev_get_gpio_in(dev, 0);
-}
-
-static int fdctrl_init_common(FDCtrl *fdctrl)
-{
-    int i, j;
-    static int command_tables_inited = 0;
-
-    /* Fill 'command_to_handler' lookup table */
-    if (!command_tables_inited) {
-        command_tables_inited = 1;
-        for (i = ARRAY_SIZE(handlers) - 1; i >= 0; i--) {
-            for (j = 0; j < sizeof(command_to_handler); j++) {
-                if ((j & handlers[i].mask) == handlers[i].value) {
-                    command_to_handler[j] = i;
-                }
-            }
-        }
-    }
-
-    FLOPPY_DPRINTF("init controller\n");
-    fdctrl->fifo = qemu_memalign(512, FD_SECTOR_LEN);
-    fdctrl->fifo_size = 512;
-    fdctrl->result_timer = qemu_new_timer_ns(vm_clock,
-                                          fdctrl_result_timer, fdctrl);
-
-    fdctrl->version = 0x90; /* Intel 82078 controller */
-    fdctrl->config = FD_CONFIG_EIS | FD_CONFIG_EFIFO; /* Implicit seek, polling & FIFO enabled */
-    fdctrl->num_floppies = MAX_FD;
-
-    if (fdctrl->dma_chann != -1)
-        DMA_register_channel(fdctrl->dma_chann, &fdctrl_transfer_handler, fdctrl);
-    return fdctrl_connect_drives(fdctrl);
-}
-
-static const MemoryRegionPortio fdc_portio_list[] = {
-    { 1, 5, 1, .read = fdctrl_read, .write = fdctrl_write },
-    { 7, 1, 1, .read = fdctrl_read, .write = fdctrl_write },
-    PORTIO_END_OF_LIST(),
-};
-
-static int isabus_fdc_init1(ISADevice *dev)
-{
-    FDCtrlISABus *isa = DO_UPCAST(FDCtrlISABus, busdev, dev);
-    FDCtrl *fdctrl = &isa->state;
-    int ret;
-
-    isa_register_portio_list(dev, isa->iobase, fdc_portio_list, fdctrl, "fdc");
-
-    isa_init_irq(&isa->busdev, &fdctrl->irq, isa->irq);
-    fdctrl->dma_chann = isa->dma;
-
-    qdev_set_legacy_instance_id(&dev->qdev, isa->iobase, 2);
-    ret = fdctrl_init_common(fdctrl);
-
-    add_boot_device_path(isa->bootindexA, &dev->qdev, "/floppy@0");
-    add_boot_device_path(isa->bootindexB, &dev->qdev, "/floppy@1");
-
-    return ret;
-}
-
-static int sysbus_fdc_init1(SysBusDevice *dev)
-{
-    FDCtrlSysBus *sys = DO_UPCAST(FDCtrlSysBus, busdev, dev);
-    FDCtrl *fdctrl = &sys->state;
-    int ret;
-
-    memory_region_init_io(&fdctrl->iomem, &fdctrl_mem_ops, fdctrl, "fdc", 0x08);
-    sysbus_init_mmio(dev, &fdctrl->iomem);
-    sysbus_init_irq(dev, &fdctrl->irq);
-    qdev_init_gpio_in(&dev->qdev, fdctrl_handle_tc, 1);
-    fdctrl->dma_chann = -1;
-
-    qdev_set_legacy_instance_id(&dev->qdev, 0 /* io */, 2); /* FIXME */
-    ret = fdctrl_init_common(fdctrl);
-
-    return ret;
-}
-
-static int sun4m_fdc_init1(SysBusDevice *dev)
-{
-    FDCtrl *fdctrl = &(FROM_SYSBUS(FDCtrlSysBus, dev)->state);
-
-    memory_region_init_io(&fdctrl->iomem, &fdctrl_mem_strict_ops, fdctrl,
-                          "fdctrl", 0x08);
-    sysbus_init_mmio(dev, &fdctrl->iomem);
-    sysbus_init_irq(dev, &fdctrl->irq);
-    qdev_init_gpio_in(&dev->qdev, fdctrl_handle_tc, 1);
-
-    fdctrl->sun4m = 1;
-    qdev_set_legacy_instance_id(&dev->qdev, 0 /* io */, 2); /* FIXME */
-    return fdctrl_init_common(fdctrl);
-}
-
-FDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i)
-{
-    FDCtrlISABus *isa = DO_UPCAST(FDCtrlISABus, busdev, fdc);
-
-    return isa->state.drives[i].drive;
-}
-
-static const VMStateDescription vmstate_isa_fdc ={
-    .name = "fdc",
-    .version_id = 2,
-    .minimum_version_id = 2,
-    .fields = (VMStateField []) {
-        VMSTATE_STRUCT(state, FDCtrlISABus, 0, vmstate_fdc, FDCtrl),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property isa_fdc_properties[] = {
-    DEFINE_PROP_HEX32("iobase", FDCtrlISABus, iobase, 0x3f0),
-    DEFINE_PROP_UINT32("irq", FDCtrlISABus, irq, 6),
-    DEFINE_PROP_UINT32("dma", FDCtrlISABus, dma, 2),
-    DEFINE_PROP_DRIVE("driveA", FDCtrlISABus, state.drives[0].bs),
-    DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].bs),
-    DEFINE_PROP_INT32("bootindexA", FDCtrlISABus, bootindexA, -1),
-    DEFINE_PROP_INT32("bootindexB", FDCtrlISABus, bootindexB, -1),
-    DEFINE_PROP_BIT("check_media_rate", FDCtrlISABus, state.check_media_rate,
-                    0, true),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void isabus_fdc_class_init1(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
-    ic->init = isabus_fdc_init1;
-    dc->fw_name = "fdc";
-    dc->no_user = 1;
-    dc->reset = fdctrl_external_reset_isa;
-    dc->vmsd = &vmstate_isa_fdc;
-    dc->props = isa_fdc_properties;
-}
-
-static const TypeInfo isa_fdc_info = {
-    .name          = "isa-fdc",
-    .parent        = TYPE_ISA_DEVICE,
-    .instance_size = sizeof(FDCtrlISABus),
-    .class_init    = isabus_fdc_class_init1,
-};
-
-static const VMStateDescription vmstate_sysbus_fdc ={
-    .name = "fdc",
-    .version_id = 2,
-    .minimum_version_id = 2,
-    .fields = (VMStateField []) {
-        VMSTATE_STRUCT(state, FDCtrlSysBus, 0, vmstate_fdc, FDCtrl),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property sysbus_fdc_properties[] = {
-    DEFINE_PROP_DRIVE("driveA", FDCtrlSysBus, state.drives[0].bs),
-    DEFINE_PROP_DRIVE("driveB", FDCtrlSysBus, state.drives[1].bs),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void sysbus_fdc_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = sysbus_fdc_init1;
-    dc->reset = fdctrl_external_reset_sysbus;
-    dc->vmsd = &vmstate_sysbus_fdc;
-    dc->props = sysbus_fdc_properties;
-}
-
-static const TypeInfo sysbus_fdc_info = {
-    .name          = "sysbus-fdc",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(FDCtrlSysBus),
-    .class_init    = sysbus_fdc_class_init,
-};
-
-static Property sun4m_fdc_properties[] = {
-    DEFINE_PROP_DRIVE("drive", FDCtrlSysBus, state.drives[0].bs),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void sun4m_fdc_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = sun4m_fdc_init1;
-    dc->reset = fdctrl_external_reset_sysbus;
-    dc->vmsd = &vmstate_sysbus_fdc;
-    dc->props = sun4m_fdc_properties;
-}
-
-static const TypeInfo sun4m_fdc_info = {
-    .name          = "SUNW,fdtwo",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(FDCtrlSysBus),
-    .class_init    = sun4m_fdc_class_init,
-};
-
-static void fdc_register_types(void)
-{
-    type_register_static(&isa_fdc_info);
-    type_register_static(&sysbus_fdc_info);
-    type_register_static(&sun4m_fdc_info);
-}
-
-type_init(fdc_register_types)
diff --git a/hw/fmopl.c b/hw/fmopl.c
deleted file mode 100644 (file)
index e50ba6c..0000000
+++ /dev/null
@@ -1,1395 +0,0 @@
-/*
-**
-** File: fmopl.c -- software implementation of FM sound generator
-**
-** Copyright (C) 1999,2000 Tatsuyuki Satoh , MultiArcadeMachineEmurator development
-**
-** Version 0.37a
-**
-*/
-
-/*
-       preliminary :
-       Problem :
-       note:
-*/
-
-/* This version of fmopl.c is a fork of the MAME one, relicensed under the LGPL.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#define INLINE         static inline
-#define HAS_YM3812     1
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <math.h>
-//#include "driver.h"          /* use M.A.M.E. */
-#include "hw/fmopl.h"
-
-#ifndef PI
-#define PI 3.14159265358979323846
-#endif
-
-#ifndef ARRAY_SIZE
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-#endif
-
-/* -------------------- for debug --------------------- */
-/* #define OPL_OUTPUT_LOG */
-#ifdef OPL_OUTPUT_LOG
-static FILE *opl_dbg_fp = NULL;
-static FM_OPL *opl_dbg_opl[16];
-static int opl_dbg_maxchip,opl_dbg_chip;
-#endif
-
-/* -------------------- preliminary define section --------------------- */
-/* attack/decay rate time rate */
-#define OPL_ARRATE     141280  /* RATE 4 =  2826.24ms @ 3.6MHz */
-#define OPL_DRRATE    1956000  /* RATE 4 = 39280.64ms @ 3.6MHz */
-
-#define DELTAT_MIXING_LEVEL (1) /* DELTA-T ADPCM MIXING LEVEL */
-
-#define FREQ_BITS 24                   /* frequency turn          */
-
-/* counter bits = 20 , octerve 7 */
-#define FREQ_RATE   (1<<(FREQ_BITS-20))
-#define TL_BITS    (FREQ_BITS+2)
-
-/* final output shift , limit minimum and maximum */
-#define OPL_OUTSB   (TL_BITS+3-16)             /* OPL output final shift 16bit */
-#define OPL_MAXOUT (0x7fff<<OPL_OUTSB)
-#define OPL_MINOUT (-0x8000<<OPL_OUTSB)
-
-/* -------------------- quality selection --------------------- */
-
-/* sinwave entries */
-/* used static memory = SIN_ENT * 4 (byte) */
-#define SIN_ENT 2048
-
-/* output level entries (envelope,sinwave) */
-/* envelope counter lower bits */
-#define ENV_BITS 16
-/* envelope output entries */
-#define EG_ENT   4096
-/* used dynamic memory = EG_ENT*4*4(byte)or EG_ENT*6*4(byte) */
-/* used static  memory = EG_ENT*4 (byte)                     */
-
-#define EG_OFF   ((2*EG_ENT)<<ENV_BITS)  /* OFF          */
-#define EG_DED   EG_OFF
-#define EG_DST   (EG_ENT<<ENV_BITS)      /* DECAY  START */
-#define EG_AED   EG_DST
-#define EG_AST   0                       /* ATTACK START */
-
-#define EG_STEP (96.0/EG_ENT) /* OPL is 0.1875 dB step  */
-
-/* LFO table entries */
-#define VIB_ENT 512
-#define VIB_SHIFT (32-9)
-#define AMS_ENT 512
-#define AMS_SHIFT (32-9)
-
-#define VIB_RATE 256
-
-/* -------------------- local defines , macros --------------------- */
-
-/* register number to channel number , slot offset */
-#define SLOT1 0
-#define SLOT2 1
-
-/* envelope phase */
-#define ENV_MOD_RR  0x00
-#define ENV_MOD_DR  0x01
-#define ENV_MOD_AR  0x02
-
-/* -------------------- tables --------------------- */
-static const int slot_array[32]=
-{
-        0, 2, 4, 1, 3, 5,-1,-1,
-        6, 8,10, 7, 9,11,-1,-1,
-       12,14,16,13,15,17,-1,-1,
-       -1,-1,-1,-1,-1,-1,-1,-1
-};
-
-/* key scale level */
-/* table is 3dB/OCT , DV converts this in TL step at 6dB/OCT */
-#define DV (EG_STEP/2)
-static const UINT32 KSL_TABLE[8*16]=
-{
-       /* OCT 0 */
-        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
-        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
-        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
-        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
-       /* OCT 1 */
-        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
-        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
-        0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV,
-        1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV,
-       /* OCT 2 */
-        0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
-        0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV,
-        3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV,
-        4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV,
-       /* OCT 3 */
-        0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV,
-        3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV,
-        6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV,
-        7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV,
-       /* OCT 4 */
-        0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV,
-        6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV,
-        9.000/DV, 9.750/DV,10.125/DV,10.500/DV,
-       10.875/DV,11.250/DV,11.625/DV,12.000/DV,
-       /* OCT 5 */
-        0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV,
-        9.000/DV,10.125/DV,10.875/DV,11.625/DV,
-       12.000/DV,12.750/DV,13.125/DV,13.500/DV,
-       13.875/DV,14.250/DV,14.625/DV,15.000/DV,
-       /* OCT 6 */
-        0.000/DV, 6.000/DV, 9.000/DV,10.875/DV,
-       12.000/DV,13.125/DV,13.875/DV,14.625/DV,
-       15.000/DV,15.750/DV,16.125/DV,16.500/DV,
-       16.875/DV,17.250/DV,17.625/DV,18.000/DV,
-       /* OCT 7 */
-        0.000/DV, 9.000/DV,12.000/DV,13.875/DV,
-       15.000/DV,16.125/DV,16.875/DV,17.625/DV,
-       18.000/DV,18.750/DV,19.125/DV,19.500/DV,
-       19.875/DV,20.250/DV,20.625/DV,21.000/DV
-};
-#undef DV
-
-/* sustain lebel table (3db per step) */
-/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
-#define SC(db) (db*((3/EG_STEP)*(1<<ENV_BITS)))+EG_DST
-static const INT32 SL_TABLE[16]={
- SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
- SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
-};
-#undef SC
-
-#define TL_MAX (EG_ENT*2) /* limit(tl + ksr + envelope) + sinwave */
-/* TotalLevel : 48 24 12  6  3 1.5 0.75 (dB) */
-/* TL_TABLE[ 0      to TL_MAX          ] : plus  section */
-/* TL_TABLE[ TL_MAX to TL_MAX+TL_MAX-1 ] : minus section */
-static INT32 *TL_TABLE;
-
-/* pointers to TL_TABLE with sinwave output offset */
-static INT32 **SIN_TABLE;
-
-/* LFO table */
-static INT32 *AMS_TABLE;
-static INT32 *VIB_TABLE;
-
-/* envelope output curve table */
-/* attack + decay + OFF */
-static INT32 ENV_CURVE[2*EG_ENT+1];
-
-/* multiple table */
-#define ML 2
-static const UINT32 MUL_TABLE[16]= {
-/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */
-   0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML,
-   8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML
-};
-#undef ML
-
-/* dummy attack / decay rate ( when rate == 0 ) */
-static INT32 RATE_0[16]=
-{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
-
-/* -------------------- static state --------------------- */
-
-/* lock level of common table */
-static int num_lock = 0;
-
-/* work table */
-static void *cur_chip = NULL;  /* current chip point */
-/* currenct chip state */
-/* static OPLSAMPLE  *bufL,*bufR; */
-static OPL_CH *S_CH;
-static OPL_CH *E_CH;
-OPL_SLOT *SLOT7_1,*SLOT7_2,*SLOT8_1,*SLOT8_2;
-
-static INT32 outd[1];
-static INT32 ams;
-static INT32 vib;
-INT32  *ams_table;
-INT32  *vib_table;
-static INT32 amsIncr;
-static INT32 vibIncr;
-static INT32 feedback2;                /* connect for SLOT 2 */
-
-/* log output level */
-#define LOG_ERR  3      /* ERROR       */
-#define LOG_WAR  2      /* WARNING     */
-#define LOG_INF  1      /* INFORMATION */
-
-//#define LOG_LEVEL LOG_INF
-#define LOG_LEVEL      LOG_ERR
-
-//#define LOG(n,x) if( (n)>=LOG_LEVEL ) logerror x
-#define LOG(n,x)
-
-/* --------------------- subroutines  --------------------- */
-
-INLINE int Limit( int val, int max, int min ) {
-       if ( val > max )
-               val = max;
-       else if ( val < min )
-               val = min;
-
-       return val;
-}
-
-/* status set and IRQ handling */
-INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag)
-{
-       /* set status flag */
-       OPL->status |= flag;
-       if(!(OPL->status & 0x80))
-       {
-               if(OPL->status & OPL->statusmask)
-               {       /* IRQ on */
-                       OPL->status |= 0x80;
-                       /* callback user interrupt handler (IRQ is OFF to ON) */
-                       if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1);
-               }
-       }
-}
-
-/* status reset and IRQ handling */
-INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag)
-{
-       /* reset status flag */
-       OPL->status &=~flag;
-       if((OPL->status & 0x80))
-       {
-               if (!(OPL->status & OPL->statusmask) )
-               {
-                       OPL->status &= 0x7f;
-                       /* callback user interrupt handler (IRQ is ON to OFF) */
-                       if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0);
-               }
-       }
-}
-
-/* IRQ mask set */
-INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag)
-{
-       OPL->statusmask = flag;
-       /* IRQ handling check */
-       OPL_STATUS_SET(OPL,0);
-       OPL_STATUS_RESET(OPL,0);
-}
-
-/* ----- key on  ----- */
-INLINE void OPL_KEYON(OPL_SLOT *SLOT)
-{
-       /* sin wave restart */
-       SLOT->Cnt = 0;
-       /* set attack */
-       SLOT->evm = ENV_MOD_AR;
-       SLOT->evs = SLOT->evsa;
-       SLOT->evc = EG_AST;
-       SLOT->eve = EG_AED;
-}
-/* ----- key off ----- */
-INLINE void OPL_KEYOFF(OPL_SLOT *SLOT)
-{
-       if( SLOT->evm > ENV_MOD_RR)
-       {
-               /* set envelope counter from envleope output */
-               SLOT->evm = ENV_MOD_RR;
-               if( !(SLOT->evc&EG_DST) )
-                       //SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<<ENV_BITS) + EG_DST;
-                       SLOT->evc = EG_DST;
-               SLOT->eve = EG_DED;
-               SLOT->evs = SLOT->evsr;
-       }
-}
-
-/* ---------- calcrate Envelope Generator & Phase Generator ---------- */
-/* return : envelope output */
-INLINE UINT32 OPL_CALC_SLOT( OPL_SLOT *SLOT )
-{
-       /* calcrate envelope generator */
-       if( (SLOT->evc+=SLOT->evs) >= SLOT->eve )
-       {
-               switch( SLOT->evm ){
-               case ENV_MOD_AR: /* ATTACK -> DECAY1 */
-                       /* next DR */
-                       SLOT->evm = ENV_MOD_DR;
-                       SLOT->evc = EG_DST;
-                       SLOT->eve = SLOT->SL;
-                       SLOT->evs = SLOT->evsd;
-                       break;
-               case ENV_MOD_DR: /* DECAY -> SL or RR */
-                       SLOT->evc = SLOT->SL;
-                       SLOT->eve = EG_DED;
-                       if(SLOT->eg_typ)
-                       {
-                               SLOT->evs = 0;
-                       }
-                       else
-                       {
-                               SLOT->evm = ENV_MOD_RR;
-                               SLOT->evs = SLOT->evsr;
-                       }
-                       break;
-               case ENV_MOD_RR: /* RR -> OFF */
-                       SLOT->evc = EG_OFF;
-                       SLOT->eve = EG_OFF+1;
-                       SLOT->evs = 0;
-                       break;
-               }
-       }
-       /* calcrate envelope */
-       return SLOT->TLL+ENV_CURVE[SLOT->evc>>ENV_BITS]+(SLOT->ams ? ams : 0);
-}
-
-/* set algorithm connection */
-static void set_algorithm( OPL_CH *CH)
-{
-       INT32 *carrier = &outd[0];
-       CH->connect1 = CH->CON ? carrier : &feedback2;
-       CH->connect2 = carrier;
-}
-
-/* ---------- frequency counter for operater update ---------- */
-INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT)
-{
-       int ksr;
-
-       /* frequency step counter */
-       SLOT->Incr = CH->fc * SLOT->mul;
-       ksr = CH->kcode >> SLOT->KSR;
-
-       if( SLOT->ksr != ksr )
-       {
-               SLOT->ksr = ksr;
-               /* attack , decay rate recalcration */
-               SLOT->evsa = SLOT->AR[ksr];
-               SLOT->evsd = SLOT->DR[ksr];
-               SLOT->evsr = SLOT->RR[ksr];
-       }
-       SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
-}
-
-/* set multi,am,vib,EG-TYP,KSR,mul */
-INLINE void set_mul(FM_OPL *OPL,int slot,int v)
-{
-       OPL_CH   *CH   = &OPL->P_CH[slot/2];
-       OPL_SLOT *SLOT = &CH->SLOT[slot&1];
-
-       SLOT->mul    = MUL_TABLE[v&0x0f];
-       SLOT->KSR    = (v&0x10) ? 0 : 2;
-       SLOT->eg_typ = (v&0x20)>>5;
-       SLOT->vib    = (v&0x40);
-       SLOT->ams    = (v&0x80);
-       CALC_FCSLOT(CH,SLOT);
-}
-
-/* set ksl & tl */
-INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v)
-{
-       OPL_CH   *CH   = &OPL->P_CH[slot/2];
-       OPL_SLOT *SLOT = &CH->SLOT[slot&1];
-       int ksl = v>>6; /* 0 / 1.5 / 3 / 6 db/OCT */
-
-       SLOT->ksl = ksl ? 3-ksl : 31;
-       SLOT->TL  = (v&0x3f)*(0.75/EG_STEP); /* 0.75db step */
-
-       if( !(OPL->mode&0x80) )
-       {       /* not CSM latch total level */
-               SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
-       }
-}
-
-/* set attack rate & decay rate  */
-INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v)
-{
-       OPL_CH   *CH   = &OPL->P_CH[slot/2];
-       OPL_SLOT *SLOT = &CH->SLOT[slot&1];
-       int ar = v>>4;
-       int dr = v&0x0f;
-
-       SLOT->AR = ar ? &OPL->AR_TABLE[ar<<2] : RATE_0;
-       SLOT->evsa = SLOT->AR[SLOT->ksr];
-       if( SLOT->evm == ENV_MOD_AR ) SLOT->evs = SLOT->evsa;
-
-       SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0;
-       SLOT->evsd = SLOT->DR[SLOT->ksr];
-       if( SLOT->evm == ENV_MOD_DR ) SLOT->evs = SLOT->evsd;
-}
-
-/* set sustain level & release rate */
-INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v)
-{
-       OPL_CH   *CH   = &OPL->P_CH[slot/2];
-       OPL_SLOT *SLOT = &CH->SLOT[slot&1];
-       int sl = v>>4;
-       int rr = v & 0x0f;
-
-       SLOT->SL = SL_TABLE[sl];
-       if( SLOT->evm == ENV_MOD_DR ) SLOT->eve = SLOT->SL;
-       SLOT->RR = &OPL->DR_TABLE[rr<<2];
-       SLOT->evsr = SLOT->RR[SLOT->ksr];
-       if( SLOT->evm == ENV_MOD_RR ) SLOT->evs = SLOT->evsr;
-}
-
-/* operator output calcrator */
-#define OP_OUT(slot,env,con)   slot->wavetable[((slot->Cnt+con)/(0x1000000/SIN_ENT))&(SIN_ENT-1)][env]
-/* ---------- calcrate one of channel ---------- */
-INLINE void OPL_CALC_CH( OPL_CH *CH )
-{
-       UINT32 env_out;
-       OPL_SLOT *SLOT;
-
-       feedback2 = 0;
-       /* SLOT 1 */
-       SLOT = &CH->SLOT[SLOT1];
-       env_out=OPL_CALC_SLOT(SLOT);
-       if( env_out < EG_ENT-1 )
-       {
-               /* PG */
-               if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
-               else          SLOT->Cnt += SLOT->Incr;
-               /* connectoion */
-               if(CH->FB)
-               {
-                       int feedback1 = (CH->op1_out[0]+CH->op1_out[1])>>CH->FB;
-                       CH->op1_out[1] = CH->op1_out[0];
-                       *CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT,env_out,feedback1);
-               }
-               else
-               {
-                       *CH->connect1 += OP_OUT(SLOT,env_out,0);
-               }
-       }else
-       {
-               CH->op1_out[1] = CH->op1_out[0];
-               CH->op1_out[0] = 0;
-       }
-       /* SLOT 2 */
-       SLOT = &CH->SLOT[SLOT2];
-       env_out=OPL_CALC_SLOT(SLOT);
-       if( env_out < EG_ENT-1 )
-       {
-               /* PG */
-               if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
-               else          SLOT->Cnt += SLOT->Incr;
-               /* connectoion */
-               outd[0] += OP_OUT(SLOT,env_out, feedback2);
-       }
-}
-
-/* ---------- calcrate rhythm block ---------- */
-#define WHITE_NOISE_db 6.0
-INLINE void OPL_CALC_RH( OPL_CH *CH )
-{
-       UINT32 env_tam,env_sd,env_top,env_hh;
-       int whitenoise = (rand()&1)*(WHITE_NOISE_db/EG_STEP);
-       INT32 tone8;
-
-       OPL_SLOT *SLOT;
-       int env_out;
-
-       /* BD : same as FM serial mode and output level is large */
-       feedback2 = 0;
-       /* SLOT 1 */
-       SLOT = &CH[6].SLOT[SLOT1];
-       env_out=OPL_CALC_SLOT(SLOT);
-       if( env_out < EG_ENT-1 )
-       {
-               /* PG */
-               if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
-               else          SLOT->Cnt += SLOT->Incr;
-               /* connectoion */
-               if(CH[6].FB)
-               {
-                       int feedback1 = (CH[6].op1_out[0]+CH[6].op1_out[1])>>CH[6].FB;
-                       CH[6].op1_out[1] = CH[6].op1_out[0];
-                       feedback2 = CH[6].op1_out[0] = OP_OUT(SLOT,env_out,feedback1);
-               }
-               else
-               {
-                       feedback2 = OP_OUT(SLOT,env_out,0);
-               }
-       }else
-       {
-               feedback2 = 0;
-               CH[6].op1_out[1] = CH[6].op1_out[0];
-               CH[6].op1_out[0] = 0;
-       }
-       /* SLOT 2 */
-       SLOT = &CH[6].SLOT[SLOT2];
-       env_out=OPL_CALC_SLOT(SLOT);
-       if( env_out < EG_ENT-1 )
-       {
-               /* PG */
-               if(SLOT->vib) SLOT->Cnt += (SLOT->Incr*vib/VIB_RATE);
-               else          SLOT->Cnt += SLOT->Incr;
-               /* connectoion */
-               outd[0] += OP_OUT(SLOT,env_out, feedback2)*2;
-       }
-
-       // SD  (17) = mul14[fnum7] + white noise
-       // TAM (15) = mul15[fnum8]
-       // TOP (18) = fnum6(mul18[fnum8]+whitenoise)
-       // HH  (14) = fnum7(mul18[fnum8]+whitenoise) + white noise
-       env_sd =OPL_CALC_SLOT(SLOT7_2) + whitenoise;
-       env_tam=OPL_CALC_SLOT(SLOT8_1);
-       env_top=OPL_CALC_SLOT(SLOT8_2);
-       env_hh =OPL_CALC_SLOT(SLOT7_1) + whitenoise;
-
-       /* PG */
-       if(SLOT7_1->vib) SLOT7_1->Cnt += (2*SLOT7_1->Incr*vib/VIB_RATE);
-       else             SLOT7_1->Cnt += 2*SLOT7_1->Incr;
-       if(SLOT7_2->vib) SLOT7_2->Cnt += ((CH[7].fc*8)*vib/VIB_RATE);
-       else             SLOT7_2->Cnt += (CH[7].fc*8);
-       if(SLOT8_1->vib) SLOT8_1->Cnt += (SLOT8_1->Incr*vib/VIB_RATE);
-       else             SLOT8_1->Cnt += SLOT8_1->Incr;
-       if(SLOT8_2->vib) SLOT8_2->Cnt += ((CH[8].fc*48)*vib/VIB_RATE);
-       else             SLOT8_2->Cnt += (CH[8].fc*48);
-
-       tone8 = OP_OUT(SLOT8_2,whitenoise,0 );
-
-       /* SD */
-       if( env_sd < EG_ENT-1 )
-               outd[0] += OP_OUT(SLOT7_1,env_sd, 0)*8;
-       /* TAM */
-       if( env_tam < EG_ENT-1 )
-               outd[0] += OP_OUT(SLOT8_1,env_tam, 0)*2;
-       /* TOP-CY */
-       if( env_top < EG_ENT-1 )
-               outd[0] += OP_OUT(SLOT7_2,env_top,tone8)*2;
-       /* HH */
-       if( env_hh  < EG_ENT-1 )
-               outd[0] += OP_OUT(SLOT7_2,env_hh,tone8)*2;
-}
-
-/* ----------- initialize time tabls ----------- */
-static void init_timetables( FM_OPL *OPL , int ARRATE , int DRRATE )
-{
-       int i;
-       double rate;
-
-       /* make attack rate & decay rate tables */
-       for (i = 0;i < 4;i++) OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0;
-       for (i = 4;i <= 60;i++){
-               rate  = OPL->freqbase;                                          /* frequency rate */
-               if( i < 60 ) rate *= 1.0+(i&3)*0.25;            /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */
-               rate *= 1<<((i>>2)-1);                                          /* b2-5 : shift bit */
-               rate *= (double)(EG_ENT<<ENV_BITS);
-               OPL->AR_TABLE[i] = rate / ARRATE;
-               OPL->DR_TABLE[i] = rate / DRRATE;
-       }
-       for (i = 60; i < ARRAY_SIZE(OPL->AR_TABLE); i++)
-       {
-               OPL->AR_TABLE[i] = EG_AED-1;
-               OPL->DR_TABLE[i] = OPL->DR_TABLE[60];
-       }
-#if 0
-       for (i = 0;i < 64 ;i++){        /* make for overflow area */
-               LOG(LOG_WAR, ("rate %2d , ar %f ms , dr %f ms\n", i,
-                       ((double)(EG_ENT<<ENV_BITS) / OPL->AR_TABLE[i]) * (1000.0 / OPL->rate),
-                       ((double)(EG_ENT<<ENV_BITS) / OPL->DR_TABLE[i]) * (1000.0 / OPL->rate) ));
-       }
-#endif
-}
-
-/* ---------- generic table initialize ---------- */
-static int OPLOpenTable( void )
-{
-       int s,t;
-       double rate;
-       int i,j;
-       double pom;
-
-       /* allocate dynamic tables */
-       if( (TL_TABLE = malloc(TL_MAX*2*sizeof(INT32))) == NULL)
-               return 0;
-       if( (SIN_TABLE = malloc(SIN_ENT*4 *sizeof(INT32 *))) == NULL)
-       {
-               free(TL_TABLE);
-               return 0;
-       }
-       if( (AMS_TABLE = malloc(AMS_ENT*2 *sizeof(INT32))) == NULL)
-       {
-               free(TL_TABLE);
-               free(SIN_TABLE);
-               return 0;
-       }
-       if( (VIB_TABLE = malloc(VIB_ENT*2 *sizeof(INT32))) == NULL)
-       {
-               free(TL_TABLE);
-               free(SIN_TABLE);
-               free(AMS_TABLE);
-               return 0;
-       }
-       /* make total level table */
-       for (t = 0;t < EG_ENT-1 ;t++){
-               rate = ((1<<TL_BITS)-1)/pow(10,EG_STEP*t/20);   /* dB -> voltage */
-               TL_TABLE[       t] =  (int)rate;
-               TL_TABLE[TL_MAX+t] = -TL_TABLE[t];
-/*             LOG(LOG_INF,("TotalLevel(%3d) = %x\n",t,TL_TABLE[t]));*/
-       }
-       /* fill volume off area */
-       for ( t = EG_ENT-1; t < TL_MAX ;t++){
-               TL_TABLE[t] = TL_TABLE[TL_MAX+t] = 0;
-       }
-
-       /* make sinwave table (total level offet) */
-       /* degree 0 = degree 180                   = off */
-       SIN_TABLE[0] = SIN_TABLE[SIN_ENT/2]         = &TL_TABLE[EG_ENT-1];
-       for (s = 1;s <= SIN_ENT/4;s++){
-               pom = sin(2*PI*s/SIN_ENT); /* sin     */
-               pom = 20*log10(1/pom);     /* decibel */
-               j = pom / EG_STEP;         /* TL_TABLE steps */
-
-        /* degree 0   -  90    , degree 180 -  90 : plus section */
-               SIN_TABLE[          s] = SIN_TABLE[SIN_ENT/2-s] = &TL_TABLE[j];
-        /* degree 180 - 270    , degree 360 - 270 : minus section */
-               SIN_TABLE[SIN_ENT/2+s] = SIN_TABLE[SIN_ENT  -s] = &TL_TABLE[TL_MAX+j];
-/*             LOG(LOG_INF,("sin(%3d) = %f:%f db\n",s,pom,(double)j * EG_STEP));*/
-       }
-       for (s = 0;s < SIN_ENT;s++)
-       {
-               SIN_TABLE[SIN_ENT*1+s] = s<(SIN_ENT/2) ? SIN_TABLE[s] : &TL_TABLE[EG_ENT];
-               SIN_TABLE[SIN_ENT*2+s] = SIN_TABLE[s % (SIN_ENT/2)];
-               SIN_TABLE[SIN_ENT*3+s] = (s/(SIN_ENT/4))&1 ? &TL_TABLE[EG_ENT] : SIN_TABLE[SIN_ENT*2+s];
-       }
-
-       /* envelope counter -> envelope output table */
-       for (i=0; i<EG_ENT; i++)
-       {
-               /* ATTACK curve */
-               pom = pow( ((double)(EG_ENT-1-i)/EG_ENT) , 8 ) * EG_ENT;
-               /* if( pom >= EG_ENT ) pom = EG_ENT-1; */
-               ENV_CURVE[i] = (int)pom;
-               /* DECAY ,RELEASE curve */
-               ENV_CURVE[(EG_DST>>ENV_BITS)+i]= i;
-       }
-       /* off */
-       ENV_CURVE[EG_OFF>>ENV_BITS]= EG_ENT-1;
-       /* make LFO ams table */
-       for (i=0; i<AMS_ENT; i++)
-       {
-               pom = (1.0+sin(2*PI*i/AMS_ENT))/2; /* sin */
-               AMS_TABLE[i]         = (1.0/EG_STEP)*pom; /* 1dB   */
-               AMS_TABLE[AMS_ENT+i] = (4.8/EG_STEP)*pom; /* 4.8dB */
-       }
-       /* make LFO vibrate table */
-       for (i=0; i<VIB_ENT; i++)
-       {
-               /* 100cent = 1seminote = 6% ?? */
-               pom = (double)VIB_RATE*0.06*sin(2*PI*i/VIB_ENT); /* +-100sect step */
-               VIB_TABLE[i]         = VIB_RATE + (pom*0.07); /* +- 7cent */
-               VIB_TABLE[VIB_ENT+i] = VIB_RATE + (pom*0.14); /* +-14cent */
-               /* LOG(LOG_INF,("vib %d=%d\n",i,VIB_TABLE[VIB_ENT+i])); */
-       }
-       return 1;
-}
-
-
-static void OPLCloseTable( void )
-{
-       free(TL_TABLE);
-       free(SIN_TABLE);
-       free(AMS_TABLE);
-       free(VIB_TABLE);
-}
-
-/* CSM Key Control */
-INLINE void CSMKeyControll(OPL_CH *CH)
-{
-       OPL_SLOT *slot1 = &CH->SLOT[SLOT1];
-       OPL_SLOT *slot2 = &CH->SLOT[SLOT2];
-       /* all key off */
-       OPL_KEYOFF(slot1);
-       OPL_KEYOFF(slot2);
-       /* total level latch */
-       slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
-       slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
-       /* key on */
-       CH->op1_out[0] = CH->op1_out[1] = 0;
-       OPL_KEYON(slot1);
-       OPL_KEYON(slot2);
-}
-
-/* ---------- opl initialize ---------- */
-static void OPL_initialize(FM_OPL *OPL)
-{
-       int fn;
-
-       /* frequency base */
-       OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72  : 0;
-       /* Timer base time */
-       OPL->TimerBase = 1.0/((double)OPL->clock / 72.0 );
-       /* make time tables */
-       init_timetables( OPL , OPL_ARRATE , OPL_DRRATE );
-       /* make fnumber -> increment counter table */
-       for( fn=0 ; fn < 1024 ; fn++ )
-       {
-               OPL->FN_TABLE[fn] = OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2;
-       }
-       /* LFO freq.table */
-       OPL->amsIncr = OPL->rate ? (double)AMS_ENT*(1<<AMS_SHIFT) / OPL->rate * 3.7 * ((double)OPL->clock/3600000) : 0;
-       OPL->vibIncr = OPL->rate ? (double)VIB_ENT*(1<<VIB_SHIFT) / OPL->rate * 6.4 * ((double)OPL->clock/3600000) : 0;
-}
-
-/* ---------- write a OPL registers ---------- */
-static void OPLWriteReg(FM_OPL *OPL, int r, int v)
-{
-       OPL_CH *CH;
-       int slot;
-       int block_fnum;
-
-       switch(r&0xe0)
-       {
-       case 0x00: /* 00-1f:control */
-               switch(r&0x1f)
-               {
-               case 0x01:
-                       /* wave selector enable */
-                       if(OPL->type&OPL_TYPE_WAVESEL)
-                       {
-                               OPL->wavesel = v&0x20;
-                               if(!OPL->wavesel)
-                               {
-                                       /* preset compatible mode */
-                                       int c;
-                                       for(c=0;c<OPL->max_ch;c++)
-                                       {
-                                               OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0];
-                                               OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0];
-                                       }
-                               }
-                       }
-                       return;
-               case 0x02:      /* Timer 1 */
-                       OPL->T[0] = (256-v)*4;
-                       break;
-               case 0x03:      /* Timer 2 */
-                       OPL->T[1] = (256-v)*16;
-                       return;
-               case 0x04:      /* IRQ clear / mask and Timer enable */
-                       if(v&0x80)
-                       {       /* IRQ flag clear */
-                               OPL_STATUS_RESET(OPL,0x7f);
-                       }
-                       else
-                       {       /* set IRQ mask ,timer enable*/
-                               UINT8 st1 = v&1;
-                               UINT8 st2 = (v>>1)&1;
-                               /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */
-                               OPL_STATUS_RESET(OPL,v&0x78);
-                               OPL_STATUSMASK_SET(OPL,((~v)&0x78)|0x01);
-                               /* timer 2 */
-                               if(OPL->st[1] != st2)
-                               {
-                                       double interval = st2 ? (double)OPL->T[1]*OPL->TimerBase : 0.0;
-                                       OPL->st[1] = st2;
-                                       if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+1,interval);
-                               }
-                               /* timer 1 */
-                               if(OPL->st[0] != st1)
-                               {
-                                       double interval = st1 ? (double)OPL->T[0]*OPL->TimerBase : 0.0;
-                                       OPL->st[0] = st1;
-                                       if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+0,interval);
-                               }
-                       }
-                       return;
-#if BUILD_Y8950
-               case 0x06:              /* Key Board OUT */
-                       if(OPL->type&OPL_TYPE_KEYBOARD)
-                       {
-                               if(OPL->keyboardhandler_w)
-                                       OPL->keyboardhandler_w(OPL->keyboard_param,v);
-                               else
-                                       LOG(LOG_WAR,("OPL:write unmapped KEYBOARD port\n"));
-                       }
-                       return;
-               case 0x07:      /* DELTA-T control : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */
-                       if(OPL->type&OPL_TYPE_ADPCM)
-                               YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
-                       return;
-               case 0x08:      /* MODE,DELTA-T : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */
-                       OPL->mode = v;
-                       v&=0x1f;        /* for DELTA-T unit */
-               case 0x09:              /* START ADD */
-               case 0x0a:
-               case 0x0b:              /* STOP ADD  */
-               case 0x0c:
-               case 0x0d:              /* PRESCALE   */
-               case 0x0e:
-               case 0x0f:              /* ADPCM data */
-               case 0x10:              /* DELTA-N    */
-               case 0x11:              /* DELTA-N    */
-               case 0x12:              /* EG-CTRL    */
-                       if(OPL->type&OPL_TYPE_ADPCM)
-                               YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
-                       return;
-#if 0
-               case 0x15:              /* DAC data    */
-               case 0x16:
-               case 0x17:              /* SHIFT    */
-                       return;
-               case 0x18:              /* I/O CTRL (Direction) */
-                       if(OPL->type&OPL_TYPE_IO)
-                               OPL->portDirection = v&0x0f;
-                       return;
-               case 0x19:              /* I/O DATA */
-                       if(OPL->type&OPL_TYPE_IO)
-                       {
-                               OPL->portLatch = v;
-                               if(OPL->porthandler_w)
-                                       OPL->porthandler_w(OPL->port_param,v&OPL->portDirection);
-                       }
-                       return;
-               case 0x1a:              /* PCM data */
-                       return;
-#endif
-#endif
-               }
-               break;
-       case 0x20:      /* am,vib,ksr,eg type,mul */
-               slot = slot_array[r&0x1f];
-               if(slot == -1) return;
-               set_mul(OPL,slot,v);
-               return;
-       case 0x40:
-               slot = slot_array[r&0x1f];
-               if(slot == -1) return;
-               set_ksl_tl(OPL,slot,v);
-               return;
-       case 0x60:
-               slot = slot_array[r&0x1f];
-               if(slot == -1) return;
-               set_ar_dr(OPL,slot,v);
-               return;
-       case 0x80:
-               slot = slot_array[r&0x1f];
-               if(slot == -1) return;
-               set_sl_rr(OPL,slot,v);
-               return;
-       case 0xa0:
-               switch(r)
-               {
-               case 0xbd:
-                       /* amsep,vibdep,r,bd,sd,tom,tc,hh */
-                       {
-                       UINT8 rkey = OPL->rhythm^v;
-                       OPL->ams_table = &AMS_TABLE[v&0x80 ? AMS_ENT : 0];
-                       OPL->vib_table = &VIB_TABLE[v&0x40 ? VIB_ENT : 0];
-                       OPL->rhythm  = v&0x3f;
-                       if(OPL->rhythm&0x20)
-                       {
-#if 0
-                               usrintf_showmessage("OPL Rhythm mode select");
-#endif
-                               /* BD key on/off */
-                               if(rkey&0x10)
-                               {
-                                       if(v&0x10)
-                                       {
-                                               OPL->P_CH[6].op1_out[0] = OPL->P_CH[6].op1_out[1] = 0;
-                                               OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT1]);
-                                               OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT2]);
-                                       }
-                                       else
-                                       {
-                                               OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1]);
-                                               OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2]);
-                                       }
-                               }
-                               /* SD key on/off */
-                               if(rkey&0x08)
-                               {
-                                       if(v&0x08) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT2]);
-                                       else       OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2]);
-                               }/* TAM key on/off */
-                               if(rkey&0x04)
-                               {
-                                       if(v&0x04) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT1]);
-                                       else       OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1]);
-                               }
-                               /* TOP-CY key on/off */
-                               if(rkey&0x02)
-                               {
-                                       if(v&0x02) OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT2]);
-                                       else       OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2]);
-                               }
-                               /* HH key on/off */
-                               if(rkey&0x01)
-                               {
-                                       if(v&0x01) OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT1]);
-                                       else       OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1]);
-                               }
-                       }
-                       }
-                       return;
-               }
-               /* keyon,block,fnum */
-               if( (r&0x0f) > 8) return;
-               CH = &OPL->P_CH[r&0x0f];
-               if(!(r&0x10))
-               {       /* a0-a8 */
-                       block_fnum  = (CH->block_fnum&0x1f00) | v;
-               }
-               else
-               {       /* b0-b8 */
-                       int keyon = (v>>5)&1;
-                       block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);
-                       if(CH->keyon != keyon)
-                       {
-                               if( (CH->keyon=keyon) )
-                               {
-                                       CH->op1_out[0] = CH->op1_out[1] = 0;
-                                       OPL_KEYON(&CH->SLOT[SLOT1]);
-                                       OPL_KEYON(&CH->SLOT[SLOT2]);
-                               }
-                               else
-                               {
-                                       OPL_KEYOFF(&CH->SLOT[SLOT1]);
-                                       OPL_KEYOFF(&CH->SLOT[SLOT2]);
-                               }
-                       }
-               }
-               /* update */
-               if(CH->block_fnum != block_fnum)
-               {
-                       int blockRv = 7-(block_fnum>>10);
-                       int fnum   = block_fnum&0x3ff;
-                       CH->block_fnum = block_fnum;
-
-                       CH->ksl_base = KSL_TABLE[block_fnum>>6];
-                       CH->fc = OPL->FN_TABLE[fnum]>>blockRv;
-                       CH->kcode = CH->block_fnum>>9;
-                       if( (OPL->mode&0x40) && CH->block_fnum&0x100) CH->kcode |=1;
-                       CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
-                       CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
-               }
-               return;
-       case 0xc0:
-               /* FB,C */
-               if( (r&0x0f) > 8) return;
-               CH = &OPL->P_CH[r&0x0f];
-               {
-               int feedback = (v>>1)&7;
-               CH->FB   = feedback ? (8+1) - feedback : 0;
-               CH->CON = v&1;
-               set_algorithm(CH);
-               }
-               return;
-       case 0xe0: /* wave type */
-               slot = slot_array[r&0x1f];
-               if(slot == -1) return;
-               CH = &OPL->P_CH[slot/2];
-               if(OPL->wavesel)
-               {
-                       /* LOG(LOG_INF,("OPL SLOT %d wave select %d\n",slot,v&3)); */
-                       CH->SLOT[slot&1].wavetable = &SIN_TABLE[(v&0x03)*SIN_ENT];
-               }
-               return;
-       }
-}
-
-/* lock/unlock for common table */
-static int OPL_LockTable(void)
-{
-       num_lock++;
-       if(num_lock>1) return 0;
-       /* first time */
-       cur_chip = NULL;
-       /* allocate total level table (128kb space) */
-       if( !OPLOpenTable() )
-       {
-               num_lock--;
-               return -1;
-       }
-       return 0;
-}
-
-static void OPL_UnLockTable(void)
-{
-       if(num_lock) num_lock--;
-       if(num_lock) return;
-       /* last time */
-       cur_chip = NULL;
-       OPLCloseTable();
-}
-
-#if (BUILD_YM3812 || BUILD_YM3526)
-/*******************************************************************************/
-/*             YM3812 local section                                                   */
-/*******************************************************************************/
-
-/* ---------- update one of chip ----------- */
-void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
-{
-    int i;
-       int data;
-       OPLSAMPLE *buf = buffer;
-       UINT32 amsCnt  = OPL->amsCnt;
-       UINT32 vibCnt  = OPL->vibCnt;
-       UINT8 rhythm = OPL->rhythm&0x20;
-       OPL_CH *CH,*R_CH;
-
-       if( (void *)OPL != cur_chip ){
-               cur_chip = (void *)OPL;
-               /* channel pointers */
-               S_CH = OPL->P_CH;
-               E_CH = &S_CH[9];
-               /* rhythm slot */
-               SLOT7_1 = &S_CH[7].SLOT[SLOT1];
-               SLOT7_2 = &S_CH[7].SLOT[SLOT2];
-               SLOT8_1 = &S_CH[8].SLOT[SLOT1];
-               SLOT8_2 = &S_CH[8].SLOT[SLOT2];
-               /* LFO state */
-               amsIncr = OPL->amsIncr;
-               vibIncr = OPL->vibIncr;
-               ams_table = OPL->ams_table;
-               vib_table = OPL->vib_table;
-       }
-       R_CH = rhythm ? &S_CH[6] : E_CH;
-    for( i=0; i < length ; i++ )
-       {
-               /*            channel A         channel B         channel C      */
-               /* LFO */
-               ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT];
-               vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT];
-               outd[0] = 0;
-               /* FM part */
-               for(CH=S_CH ; CH < R_CH ; CH++)
-                       OPL_CALC_CH(CH);
-               /* Rythn part */
-               if(rhythm)
-                       OPL_CALC_RH(S_CH);
-               /* limit check */
-               data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT );
-               /* store to sound buffer */
-               buf[i] = data >> OPL_OUTSB;
-       }
-
-       OPL->amsCnt = amsCnt;
-       OPL->vibCnt = vibCnt;
-#ifdef OPL_OUTPUT_LOG
-       if(opl_dbg_fp)
-       {
-               for(opl_dbg_chip=0;opl_dbg_chip<opl_dbg_maxchip;opl_dbg_chip++)
-                       if( opl_dbg_opl[opl_dbg_chip] == OPL) break;
-               fprintf(opl_dbg_fp,"%c%c%c",0x20+opl_dbg_chip,length&0xff,length/256);
-       }
-#endif
-}
-#endif /* (BUILD_YM3812 || BUILD_YM3526) */
-
-#if BUILD_Y8950
-
-void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length)
-{
-    int i;
-       int data;
-       OPLSAMPLE *buf = buffer;
-       UINT32 amsCnt  = OPL->amsCnt;
-       UINT32 vibCnt  = OPL->vibCnt;
-       UINT8 rhythm = OPL->rhythm&0x20;
-       OPL_CH *CH,*R_CH;
-       YM_DELTAT *DELTAT = OPL->deltat;
-
-       /* setup DELTA-T unit */
-       YM_DELTAT_DECODE_PRESET(DELTAT);
-
-       if( (void *)OPL != cur_chip ){
-               cur_chip = (void *)OPL;
-               /* channel pointers */
-               S_CH = OPL->P_CH;
-               E_CH = &S_CH[9];
-               /* rhythm slot */
-               SLOT7_1 = &S_CH[7].SLOT[SLOT1];
-               SLOT7_2 = &S_CH[7].SLOT[SLOT2];
-               SLOT8_1 = &S_CH[8].SLOT[SLOT1];
-               SLOT8_2 = &S_CH[8].SLOT[SLOT2];
-               /* LFO state */
-               amsIncr = OPL->amsIncr;
-               vibIncr = OPL->vibIncr;
-               ams_table = OPL->ams_table;
-               vib_table = OPL->vib_table;
-       }
-       R_CH = rhythm ? &S_CH[6] : E_CH;
-    for( i=0; i < length ; i++ )
-       {
-               /*            channel A         channel B         channel C      */
-               /* LFO */
-               ams = ams_table[(amsCnt+=amsIncr)>>AMS_SHIFT];
-               vib = vib_table[(vibCnt+=vibIncr)>>VIB_SHIFT];
-               outd[0] = 0;
-               /* deltaT ADPCM */
-               if( DELTAT->portstate )
-                       YM_DELTAT_ADPCM_CALC(DELTAT);
-               /* FM part */
-               for(CH=S_CH ; CH < R_CH ; CH++)
-                       OPL_CALC_CH(CH);
-               /* Rythn part */
-               if(rhythm)
-                       OPL_CALC_RH(S_CH);
-               /* limit check */
-               data = Limit( outd[0] , OPL_MAXOUT, OPL_MINOUT );
-               /* store to sound buffer */
-               buf[i] = data >> OPL_OUTSB;
-       }
-       OPL->amsCnt = amsCnt;
-       OPL->vibCnt = vibCnt;
-       /* deltaT START flag */
-       if( !DELTAT->portstate )
-               OPL->status &= 0xfe;
-}
-#endif
-
-/* ---------- reset one of chip ---------- */
-void OPLResetChip(FM_OPL *OPL)
-{
-       int c,s;
-       int i;
-
-       /* reset chip */
-       OPL->mode   = 0;        /* normal mode */
-       OPL_STATUS_RESET(OPL,0x7f);
-       /* reset with register write */
-       OPLWriteReg(OPL,0x01,0); /* wabesel disable */
-       OPLWriteReg(OPL,0x02,0); /* Timer1 */
-       OPLWriteReg(OPL,0x03,0); /* Timer2 */
-       OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */
-       for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0);
-       /* reset OPerator paramater */
-       for( c = 0 ; c < OPL->max_ch ; c++ )
-       {
-               OPL_CH *CH = &OPL->P_CH[c];
-               /* OPL->P_CH[c].PAN = OPN_CENTER; */
-               for(s = 0 ; s < 2 ; s++ )
-               {
-                       /* wave table */
-                       CH->SLOT[s].wavetable = &SIN_TABLE[0];
-                       /* CH->SLOT[s].evm = ENV_MOD_RR; */
-                       CH->SLOT[s].evc = EG_OFF;
-                       CH->SLOT[s].eve = EG_OFF+1;
-                       CH->SLOT[s].evs = 0;
-               }
-       }
-#if BUILD_Y8950
-       if(OPL->type&OPL_TYPE_ADPCM)
-       {
-               YM_DELTAT *DELTAT = OPL->deltat;
-
-               DELTAT->freqbase = OPL->freqbase;
-               DELTAT->output_pointer = outd;
-               DELTAT->portshift = 5;
-               DELTAT->output_range = DELTAT_MIXING_LEVEL<<TL_BITS;
-               YM_DELTAT_ADPCM_Reset(DELTAT,0);
-       }
-#endif
-}
-
-/* ----------  Create one of vietual YM3812 ----------       */
-/* 'rate'  is sampling rate and 'bufsiz' is the size of the  */
-FM_OPL *OPLCreate(int type, int clock, int rate)
-{
-       char *ptr;
-       FM_OPL *OPL;
-       int state_size;
-       int max_ch = 9; /* normaly 9 channels */
-
-       if( OPL_LockTable() ==-1) return NULL;
-       /* allocate OPL state space */
-       state_size  = sizeof(FM_OPL);
-       state_size += sizeof(OPL_CH)*max_ch;
-#if BUILD_Y8950
-       if(type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT);
-#endif
-       /* allocate memory block */
-       ptr = malloc(state_size);
-       if(ptr==NULL) return NULL;
-       /* clear */
-       memset(ptr,0,state_size);
-       OPL        = (FM_OPL *)ptr; ptr+=sizeof(FM_OPL);
-       OPL->P_CH  = (OPL_CH *)ptr; ptr+=sizeof(OPL_CH)*max_ch;
-#if BUILD_Y8950
-       if(type&OPL_TYPE_ADPCM) OPL->deltat = (YM_DELTAT *)ptr; ptr+=sizeof(YM_DELTAT);
-#endif
-       /* set channel state pointer */
-       OPL->type  = type;
-       OPL->clock = clock;
-       OPL->rate  = rate;
-       OPL->max_ch = max_ch;
-       /* init grobal tables */
-       OPL_initialize(OPL);
-       /* reset chip */
-       OPLResetChip(OPL);
-#ifdef OPL_OUTPUT_LOG
-       if(!opl_dbg_fp)
-       {
-               opl_dbg_fp = fopen("opllog.opl","wb");
-               opl_dbg_maxchip = 0;
-       }
-       if(opl_dbg_fp)
-       {
-               opl_dbg_opl[opl_dbg_maxchip] = OPL;
-               fprintf(opl_dbg_fp,"%c%c%c%c%c%c",0x00+opl_dbg_maxchip,
-                       type,
-                       clock&0xff,
-                       (clock/0x100)&0xff,
-                       (clock/0x10000)&0xff,
-                       (clock/0x1000000)&0xff);
-               opl_dbg_maxchip++;
-       }
-#endif
-       return OPL;
-}
-
-/* ----------  Destroy one of vietual YM3812 ----------       */
-void OPLDestroy(FM_OPL *OPL)
-{
-#ifdef OPL_OUTPUT_LOG
-       if(opl_dbg_fp)
-       {
-               fclose(opl_dbg_fp);
-               opl_dbg_fp = NULL;
-       }
-#endif
-       OPL_UnLockTable();
-       free(OPL);
-}
-
-/* ----------  Option handlers ----------       */
-
-void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset)
-{
-       OPL->TimerHandler   = TimerHandler;
-       OPL->TimerParam = channelOffset;
-}
-void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param)
-{
-       OPL->IRQHandler     = IRQHandler;
-       OPL->IRQParam = param;
-}
-void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param)
-{
-       OPL->UpdateHandler = UpdateHandler;
-       OPL->UpdateParam = param;
-}
-#if BUILD_Y8950
-void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param)
-{
-       OPL->porthandler_w = PortHandler_w;
-       OPL->porthandler_r = PortHandler_r;
-       OPL->port_param = param;
-}
-
-void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param)
-{
-       OPL->keyboardhandler_w = KeyboardHandler_w;
-       OPL->keyboardhandler_r = KeyboardHandler_r;
-       OPL->keyboard_param = param;
-}
-#endif
-/* ---------- YM3812 I/O interface ---------- */
-int OPLWrite(FM_OPL *OPL,int a,int v)
-{
-       if( !(a&1) )
-       {       /* address port */
-               OPL->address = v & 0xff;
-       }
-       else
-       {       /* data port */
-               if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
-#ifdef OPL_OUTPUT_LOG
-       if(opl_dbg_fp)
-       {
-               for(opl_dbg_chip=0;opl_dbg_chip<opl_dbg_maxchip;opl_dbg_chip++)
-                       if( opl_dbg_opl[opl_dbg_chip] == OPL) break;
-               fprintf(opl_dbg_fp,"%c%c%c",0x10+opl_dbg_chip,OPL->address,v);
-       }
-#endif
-               OPLWriteReg(OPL,OPL->address,v);
-       }
-       return OPL->status>>7;
-}
-
-unsigned char OPLRead(FM_OPL *OPL,int a)
-{
-       if( !(a&1) )
-       {       /* status port */
-               return OPL->status & (OPL->statusmask|0x80);
-       }
-       /* data port */
-       switch(OPL->address)
-       {
-       case 0x05: /* KeyBoard IN */
-               if(OPL->type&OPL_TYPE_KEYBOARD)
-               {
-                       if(OPL->keyboardhandler_r)
-                               return OPL->keyboardhandler_r(OPL->keyboard_param);
-                       else {
-                               LOG(LOG_WAR,("OPL:read unmapped KEYBOARD port\n"));
-                       }
-               }
-               return 0;
-#if 0
-       case 0x0f: /* ADPCM-DATA  */
-               return 0;
-#endif
-       case 0x19: /* I/O DATA    */
-               if(OPL->type&OPL_TYPE_IO)
-               {
-                       if(OPL->porthandler_r)
-                               return OPL->porthandler_r(OPL->port_param);
-                       else {
-                               LOG(LOG_WAR,("OPL:read unmapped I/O port\n"));
-                       }
-               }
-               return 0;
-       case 0x1a: /* PCM-DATA    */
-               return 0;
-       }
-       return 0;
-}
-
-int OPLTimerOver(FM_OPL *OPL,int c)
-{
-       if( c )
-       {       /* Timer B */
-               OPL_STATUS_SET(OPL,0x20);
-       }
-       else
-       {       /* Timer A */
-               OPL_STATUS_SET(OPL,0x40);
-               /* CSM mode key,TL control */
-               if( OPL->mode & 0x80 )
-               {       /* CSM mode total level latch and auto key on */
-                       int ch;
-                       if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
-                       for(ch=0;ch<9;ch++)
-                               CSMKeyControll( &OPL->P_CH[ch] );
-               }
-       }
-       /* reload timer */
-       if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+c,(double)OPL->T[c]*OPL->TimerBase);
-       return OPL->status>>7;
-}
diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c
deleted file mode 100644 (file)
index 97bba87..0000000
+++ /dev/null
@@ -1,574 +0,0 @@
-/*
- * QEMU Firmware configuration device emulation
- *
- * Copyright (c) 2008 Gleb Natapov
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "sysemu/sysemu.h"
-#include "hw/isa/isa.h"
-#include "hw/nvram/fw_cfg.h"
-#include "hw/sysbus.h"
-#include "trace.h"
-#include "qemu/error-report.h"
-#include "qemu/config-file.h"
-
-#define FW_CFG_SIZE 2
-#define FW_CFG_DATA_SIZE 1
-
-typedef struct FWCfgEntry {
-    uint32_t len;
-    uint8_t *data;
-    void *callback_opaque;
-    FWCfgCallback callback;
-} FWCfgEntry;
-
-struct FWCfgState {
-    SysBusDevice busdev;
-    MemoryRegion ctl_iomem, data_iomem, comb_iomem;
-    uint32_t ctl_iobase, data_iobase;
-    FWCfgEntry entries[2][FW_CFG_MAX_ENTRY];
-    FWCfgFiles *files;
-    uint16_t cur_entry;
-    uint32_t cur_offset;
-    Notifier machine_ready;
-};
-
-#define JPG_FILE 0
-#define BMP_FILE 1
-
-static char *read_splashfile(char *filename, size_t *file_sizep,
-                             int *file_typep)
-{
-    GError *err = NULL;
-    gboolean res;
-    gchar *content;
-    int file_type;
-    unsigned int filehead;
-    int bmp_bpp;
-
-    res = g_file_get_contents(filename, &content, file_sizep, &err);
-    if (res == FALSE) {
-        error_report("failed to read splash file '%s'", filename);
-        g_error_free(err);
-        return NULL;
-    }
-
-    /* check file size */
-    if (*file_sizep < 30) {
-        goto error;
-    }
-
-    /* check magic ID */
-    filehead = ((content[0] & 0xff) + (content[1] << 8)) & 0xffff;
-    if (filehead == 0xd8ff) {
-        file_type = JPG_FILE;
-    } else if (filehead == 0x4d42) {
-        file_type = BMP_FILE;
-    } else {
-        goto error;
-    }
-
-    /* check BMP bpp */
-    if (file_type == BMP_FILE) {
-        bmp_bpp = (content[28] + (content[29] << 8)) & 0xffff;
-        if (bmp_bpp != 24) {
-            goto error;
-        }
-    }
-
-    /* return values */
-    *file_typep = file_type;
-
-    return content;
-
-error:
-    error_report("splash file '%s' format not recognized; must be JPEG "
-                 "or 24 bit BMP", filename);
-    g_free(content);
-    return NULL;
-}
-
-static void fw_cfg_bootsplash(FWCfgState *s)
-{
-    int boot_splash_time = -1;
-    const char *boot_splash_filename = NULL;
-    char *p;
-    char *filename, *file_data;
-    size_t file_size;
-    int file_type;
-    const char *temp;
-
-    /* get user configuration */
-    QemuOptsList *plist = qemu_find_opts("boot-opts");
-    QemuOpts *opts = QTAILQ_FIRST(&plist->head);
-    if (opts != NULL) {
-        temp = qemu_opt_get(opts, "splash");
-        if (temp != NULL) {
-            boot_splash_filename = temp;
-        }
-        temp = qemu_opt_get(opts, "splash-time");
-        if (temp != NULL) {
-            p = (char *)temp;
-            boot_splash_time = strtol(p, (char **)&p, 10);
-        }
-    }
-
-    /* insert splash time if user configurated */
-    if (boot_splash_time >= 0) {
-        /* validate the input */
-        if (boot_splash_time > 0xffff) {
-            error_report("splash time is big than 65535, force it to 65535.");
-            boot_splash_time = 0xffff;
-        }
-        /* use little endian format */
-        qemu_extra_params_fw[0] = (uint8_t)(boot_splash_time & 0xff);
-        qemu_extra_params_fw[1] = (uint8_t)((boot_splash_time >> 8) & 0xff);
-        fw_cfg_add_file(s, "etc/boot-menu-wait", qemu_extra_params_fw, 2);
-    }
-
-    /* insert splash file if user configurated */
-    if (boot_splash_filename != NULL) {
-        filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, boot_splash_filename);
-        if (filename == NULL) {
-            error_report("failed to find file '%s'.", boot_splash_filename);
-            return;
-        }
-
-        /* loading file data */
-        file_data = read_splashfile(filename, &file_size, &file_type);
-        if (file_data == NULL) {
-            g_free(filename);
-            return;
-        }
-        if (boot_splash_filedata != NULL) {
-            g_free(boot_splash_filedata);
-        }
-        boot_splash_filedata = (uint8_t *)file_data;
-        boot_splash_filedata_size = file_size;
-
-        /* insert data */
-        if (file_type == JPG_FILE) {
-            fw_cfg_add_file(s, "bootsplash.jpg",
-                    boot_splash_filedata, boot_splash_filedata_size);
-        } else {
-            fw_cfg_add_file(s, "bootsplash.bmp",
-                    boot_splash_filedata, boot_splash_filedata_size);
-        }
-        g_free(filename);
-    }
-}
-
-static void fw_cfg_reboot(FWCfgState *s)
-{
-    int reboot_timeout = -1;
-    char *p;
-    const char *temp;
-
-    /* get user configuration */
-    QemuOptsList *plist = qemu_find_opts("boot-opts");
-    QemuOpts *opts = QTAILQ_FIRST(&plist->head);
-    if (opts != NULL) {
-        temp = qemu_opt_get(opts, "reboot-timeout");
-        if (temp != NULL) {
-            p = (char *)temp;
-            reboot_timeout = strtol(p, (char **)&p, 10);
-        }
-    }
-    /* validate the input */
-    if (reboot_timeout > 0xffff) {
-        error_report("reboot timeout is larger than 65535, force it to 65535.");
-        reboot_timeout = 0xffff;
-    }
-    fw_cfg_add_file(s, "etc/boot-fail-wait", g_memdup(&reboot_timeout, 4), 4);
-}
-
-static void fw_cfg_write(FWCfgState *s, uint8_t value)
-{
-    int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
-    FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
-
-    trace_fw_cfg_write(s, value);
-
-    if (s->cur_entry & FW_CFG_WRITE_CHANNEL && e->callback &&
-        s->cur_offset < e->len) {
-        e->data[s->cur_offset++] = value;
-        if (s->cur_offset == e->len) {
-            e->callback(e->callback_opaque, e->data);
-            s->cur_offset = 0;
-        }
-    }
-}
-
-static int fw_cfg_select(FWCfgState *s, uint16_t key)
-{
-    int ret;
-
-    s->cur_offset = 0;
-    if ((key & FW_CFG_ENTRY_MASK) >= FW_CFG_MAX_ENTRY) {
-        s->cur_entry = FW_CFG_INVALID;
-        ret = 0;
-    } else {
-        s->cur_entry = key;
-        ret = 1;
-    }
-
-    trace_fw_cfg_select(s, key, ret);
-    return ret;
-}
-
-static uint8_t fw_cfg_read(FWCfgState *s)
-{
-    int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
-    FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
-    uint8_t ret;
-
-    if (s->cur_entry == FW_CFG_INVALID || !e->data || s->cur_offset >= e->len)
-        ret = 0;
-    else
-        ret = e->data[s->cur_offset++];
-
-    trace_fw_cfg_read(s, ret);
-    return ret;
-}
-
-static uint64_t fw_cfg_data_mem_read(void *opaque, hwaddr addr,
-                                     unsigned size)
-{
-    return fw_cfg_read(opaque);
-}
-
-static void fw_cfg_data_mem_write(void *opaque, hwaddr addr,
-                                  uint64_t value, unsigned size)
-{
-    fw_cfg_write(opaque, (uint8_t)value);
-}
-
-static void fw_cfg_ctl_mem_write(void *opaque, hwaddr addr,
-                                 uint64_t value, unsigned size)
-{
-    fw_cfg_select(opaque, (uint16_t)value);
-}
-
-static bool fw_cfg_ctl_mem_valid(void *opaque, hwaddr addr,
-                                 unsigned size, bool is_write)
-{
-    return is_write && size == 2;
-}
-
-static uint64_t fw_cfg_comb_read(void *opaque, hwaddr addr,
-                                 unsigned size)
-{
-    return fw_cfg_read(opaque);
-}
-
-static void fw_cfg_comb_write(void *opaque, hwaddr addr,
-                              uint64_t value, unsigned size)
-{
-    switch (size) {
-    case 1:
-        fw_cfg_write(opaque, (uint8_t)value);
-        break;
-    case 2:
-        fw_cfg_select(opaque, (uint16_t)value);
-        break;
-    }
-}
-
-static bool fw_cfg_comb_valid(void *opaque, hwaddr addr,
-                                  unsigned size, bool is_write)
-{
-    return (size == 1) || (is_write && size == 2);
-}
-
-static const MemoryRegionOps fw_cfg_ctl_mem_ops = {
-    .write = fw_cfg_ctl_mem_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .valid.accepts = fw_cfg_ctl_mem_valid,
-};
-
-static const MemoryRegionOps fw_cfg_data_mem_ops = {
-    .read = fw_cfg_data_mem_read,
-    .write = fw_cfg_data_mem_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .valid = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-static const MemoryRegionOps fw_cfg_comb_mem_ops = {
-    .read = fw_cfg_comb_read,
-    .write = fw_cfg_comb_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .valid.accepts = fw_cfg_comb_valid,
-};
-
-static void fw_cfg_reset(DeviceState *d)
-{
-    FWCfgState *s = DO_UPCAST(FWCfgState, busdev.qdev, d);
-
-    fw_cfg_select(s, 0);
-}
-
-/* Save restore 32 bit int as uint16_t
-   This is a Big hack, but it is how the old state did it.
-   Or we broke compatibility in the state, or we can't use struct tm
- */
-
-static int get_uint32_as_uint16(QEMUFile *f, void *pv, size_t size)
-{
-    uint32_t *v = pv;
-    *v = qemu_get_be16(f);
-    return 0;
-}
-
-static void put_unused(QEMUFile *f, void *pv, size_t size)
-{
-    fprintf(stderr, "uint32_as_uint16 is only used for backward compatibility.\n");
-    fprintf(stderr, "This functions shouldn't be called.\n");
-}
-
-static const VMStateInfo vmstate_hack_uint32_as_uint16 = {
-    .name = "int32_as_uint16",
-    .get  = get_uint32_as_uint16,
-    .put  = put_unused,
-};
-
-#define VMSTATE_UINT16_HACK(_f, _s, _t)                                    \
-    VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_hack_uint32_as_uint16, uint32_t)
-
-
-static bool is_version_1(void *opaque, int version_id)
-{
-    return version_id == 1;
-}
-
-static const VMStateDescription vmstate_fw_cfg = {
-    .name = "fw_cfg",
-    .version_id = 2,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT16(cur_entry, FWCfgState),
-        VMSTATE_UINT16_HACK(cur_offset, FWCfgState, is_version_1),
-        VMSTATE_UINT32_V(cur_offset, FWCfgState, 2),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len)
-{
-    int arch = !!(key & FW_CFG_ARCH_LOCAL);
-
-    key &= FW_CFG_ENTRY_MASK;
-
-    assert(key < FW_CFG_MAX_ENTRY && len < UINT32_MAX);
-
-    s->entries[arch][key].data = data;
-    s->entries[arch][key].len = (uint32_t)len;
-}
-
-void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value)
-{
-    size_t sz = strlen(value) + 1;
-
-    return fw_cfg_add_bytes(s, key, g_memdup(value, sz), sz);
-}
-
-void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value)
-{
-    uint16_t *copy;
-
-    copy = g_malloc(sizeof(value));
-    *copy = cpu_to_le16(value);
-    fw_cfg_add_bytes(s, key, copy, sizeof(value));
-}
-
-void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value)
-{
-    uint32_t *copy;
-
-    copy = g_malloc(sizeof(value));
-    *copy = cpu_to_le32(value);
-    fw_cfg_add_bytes(s, key, copy, sizeof(value));
-}
-
-void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value)
-{
-    uint64_t *copy;
-
-    copy = g_malloc(sizeof(value));
-    *copy = cpu_to_le64(value);
-    fw_cfg_add_bytes(s, key, copy, sizeof(value));
-}
-
-void fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback,
-                         void *callback_opaque, void *data, size_t len)
-{
-    int arch = !!(key & FW_CFG_ARCH_LOCAL);
-
-    assert(key & FW_CFG_WRITE_CHANNEL);
-
-    key &= FW_CFG_ENTRY_MASK;
-
-    assert(key < FW_CFG_MAX_ENTRY && len <= UINT32_MAX);
-
-    s->entries[arch][key].data = data;
-    s->entries[arch][key].len = (uint32_t)len;
-    s->entries[arch][key].callback_opaque = callback_opaque;
-    s->entries[arch][key].callback = callback;
-}
-
-void fw_cfg_add_file(FWCfgState *s,  const char *filename,
-                     void *data, size_t len)
-{
-    int i, index;
-    size_t dsize;
-
-    if (!s->files) {
-        dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * FW_CFG_FILE_SLOTS;
-        s->files = g_malloc0(dsize);
-        fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, s->files, dsize);
-    }
-
-    index = be32_to_cpu(s->files->count);
-    assert(index < FW_CFG_FILE_SLOTS);
-
-    fw_cfg_add_bytes(s, FW_CFG_FILE_FIRST + index, data, len);
-
-    pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name),
-            filename);
-    for (i = 0; i < index; i++) {
-        if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
-            trace_fw_cfg_add_file_dupe(s, s->files->f[index].name);
-            return;
-        }
-    }
-
-    s->files->f[index].size   = cpu_to_be32(len);
-    s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index);
-    trace_fw_cfg_add_file(s, index, s->files->f[index].name, len);
-
-    s->files->count = cpu_to_be32(index+1);
-}
-
-static void fw_cfg_machine_ready(struct Notifier *n, void *data)
-{
-    size_t len;
-    FWCfgState *s = container_of(n, FWCfgState, machine_ready);
-    char *bootindex = get_boot_devices_list(&len);
-
-    fw_cfg_add_file(s, "bootorder", (uint8_t*)bootindex, len);
-}
-
-FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,
-                        hwaddr ctl_addr, hwaddr data_addr)
-{
-    DeviceState *dev;
-    SysBusDevice *d;
-    FWCfgState *s;
-
-    dev = qdev_create(NULL, "fw_cfg");
-    qdev_prop_set_uint32(dev, "ctl_iobase", ctl_port);
-    qdev_prop_set_uint32(dev, "data_iobase", data_port);
-    qdev_init_nofail(dev);
-    d = SYS_BUS_DEVICE(dev);
-
-    s = DO_UPCAST(FWCfgState, busdev.qdev, dev);
-
-    if (ctl_addr) {
-        sysbus_mmio_map(d, 0, ctl_addr);
-    }
-    if (data_addr) {
-        sysbus_mmio_map(d, 1, data_addr);
-    }
-    fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4);
-    fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16);
-    fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)(display_type == DT_NOGRAPHIC));
-    fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
-    fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu);
-    fw_cfg_bootsplash(s);
-    fw_cfg_reboot(s);
-
-    s->machine_ready.notify = fw_cfg_machine_ready;
-    qemu_add_machine_init_done_notifier(&s->machine_ready);
-
-    return s;
-}
-
-static int fw_cfg_init1(SysBusDevice *dev)
-{
-    FWCfgState *s = FROM_SYSBUS(FWCfgState, dev);
-
-    memory_region_init_io(&s->ctl_iomem, &fw_cfg_ctl_mem_ops, s,
-                          "fwcfg.ctl", FW_CFG_SIZE);
-    sysbus_init_mmio(dev, &s->ctl_iomem);
-    memory_region_init_io(&s->data_iomem, &fw_cfg_data_mem_ops, s,
-                          "fwcfg.data", FW_CFG_DATA_SIZE);
-    sysbus_init_mmio(dev, &s->data_iomem);
-    /* In case ctl and data overlap: */
-    memory_region_init_io(&s->comb_iomem, &fw_cfg_comb_mem_ops, s,
-                          "fwcfg", FW_CFG_SIZE);
-
-    if (s->ctl_iobase + 1 == s->data_iobase) {
-        sysbus_add_io(dev, s->ctl_iobase, &s->comb_iomem);
-    } else {
-        if (s->ctl_iobase) {
-            sysbus_add_io(dev, s->ctl_iobase, &s->ctl_iomem);
-        }
-        if (s->data_iobase) {
-            sysbus_add_io(dev, s->data_iobase, &s->data_iomem);
-        }
-    }
-    return 0;
-}
-
-static Property fw_cfg_properties[] = {
-    DEFINE_PROP_HEX32("ctl_iobase", FWCfgState, ctl_iobase, -1),
-    DEFINE_PROP_HEX32("data_iobase", FWCfgState, data_iobase, -1),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void fw_cfg_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = fw_cfg_init1;
-    dc->no_user = 1;
-    dc->reset = fw_cfg_reset;
-    dc->vmsd = &vmstate_fw_cfg;
-    dc->props = fw_cfg_properties;
-}
-
-static const TypeInfo fw_cfg_info = {
-    .name          = "fw_cfg",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(FWCfgState),
-    .class_init    = fw_cfg_class_init,
-};
-
-static void fw_cfg_register_types(void)
-{
-    type_register_static(&fw_cfg_info);
-}
-
-type_init(fw_cfg_register_types)
diff --git a/hw/g364fb.c b/hw/g364fb.c
deleted file mode 100644 (file)
index f7014e9..0000000
+++ /dev/null
@@ -1,617 +0,0 @@
-/*
- * QEMU G364 framebuffer Emulator.
- *
- * Copyright (c) 2007-2011 Herve Poussineau
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/hw.h"
-#include "ui/console.h"
-#include "ui/pixel_ops.h"
-#include "trace.h"
-#include "hw/sysbus.h"
-
-typedef struct G364State {
-    /* hardware */
-    uint8_t *vram;
-    uint32_t vram_size;
-    qemu_irq irq;
-    MemoryRegion mem_vram;
-    MemoryRegion mem_ctrl;
-    /* registers */
-    uint8_t color_palette[256][3];
-    uint8_t cursor_palette[3][3];
-    uint16_t cursor[512];
-    uint32_t cursor_position;
-    uint32_t ctla;
-    uint32_t top_of_screen;
-    uint32_t width, height; /* in pixels */
-    /* display refresh support */
-    QemuConsole *con;
-    int depth;
-    int blanked;
-} G364State;
-
-#define REG_BOOT     0x000000
-#define REG_DISPLAY  0x000118
-#define REG_VDISPLAY 0x000150
-#define REG_CTLA     0x000300
-#define REG_TOP      0x000400
-#define REG_CURS_PAL 0x000508
-#define REG_CURS_POS 0x000638
-#define REG_CLR_PAL  0x000800
-#define REG_CURS_PAT 0x001000
-#define REG_RESET    0x100000
-
-#define CTLA_FORCE_BLANK 0x00000400
-#define CTLA_NO_CURSOR   0x00800000
-
-#define G364_PAGE_SIZE 4096
-
-static inline int check_dirty(G364State *s, ram_addr_t page)
-{
-    return memory_region_get_dirty(&s->mem_vram, page, G364_PAGE_SIZE,
-                                   DIRTY_MEMORY_VGA);
-}
-
-static inline void reset_dirty(G364State *s,
-                               ram_addr_t page_min, ram_addr_t page_max)
-{
-    memory_region_reset_dirty(&s->mem_vram,
-                              page_min,
-                              page_max + G364_PAGE_SIZE - page_min - 1,
-                              DIRTY_MEMORY_VGA);
-}
-
-static void g364fb_draw_graphic8(G364State *s)
-{
-    DisplaySurface *surface = qemu_console_surface(s->con);
-    int i, w;
-    uint8_t *vram;
-    uint8_t *data_display, *dd;
-    ram_addr_t page, page_min, page_max;
-    int x, y;
-    int xmin, xmax;
-    int ymin, ymax;
-    int xcursor, ycursor;
-    unsigned int (*rgb_to_pixel)(unsigned int r, unsigned int g, unsigned int b);
-
-    switch (surface_bits_per_pixel(surface)) {
-        case 8:
-            rgb_to_pixel = rgb_to_pixel8;
-            w = 1;
-            break;
-        case 15:
-            rgb_to_pixel = rgb_to_pixel15;
-            w = 2;
-            break;
-        case 16:
-            rgb_to_pixel = rgb_to_pixel16;
-            w = 2;
-            break;
-        case 32:
-            rgb_to_pixel = rgb_to_pixel32;
-            w = 4;
-            break;
-        default:
-            hw_error("g364: unknown host depth %d",
-                     surface_bits_per_pixel(surface));
-            return;
-    }
-
-    page = 0;
-    page_min = (ram_addr_t)-1;
-    page_max = 0;
-
-    x = y = 0;
-    xmin = s->width;
-    xmax = 0;
-    ymin = s->height;
-    ymax = 0;
-
-    if (!(s->ctla & CTLA_NO_CURSOR)) {
-        xcursor = s->cursor_position >> 12;
-        ycursor = s->cursor_position & 0xfff;
-    } else {
-        xcursor = ycursor = -65;
-    }
-
-    vram = s->vram + s->top_of_screen;
-    /* XXX: out of range in vram? */
-    data_display = dd = surface_data(surface);
-    while (y < s->height) {
-        if (check_dirty(s, page)) {
-            if (y < ymin)
-                ymin = ymax = y;
-            if (page_min == (ram_addr_t)-1)
-                page_min = page;
-            page_max = page;
-            if (x < xmin)
-                xmin = x;
-            for (i = 0; i < G364_PAGE_SIZE; i++) {
-                uint8_t index;
-                unsigned int color;
-                if (unlikely((y >= ycursor && y < ycursor + 64) &&
-                    (x >= xcursor && x < xcursor + 64))) {
-                    /* pointer area */
-                    int xdiff = x - xcursor;
-                    uint16_t curs = s->cursor[(y - ycursor) * 8 + xdiff / 8];
-                    int op = (curs >> ((xdiff & 7) * 2)) & 3;
-                    if (likely(op == 0)) {
-                        /* transparent */
-                        index = *vram;
-                        color = (*rgb_to_pixel)(
-                            s->color_palette[index][0],
-                            s->color_palette[index][1],
-                            s->color_palette[index][2]);
-                    } else {
-                        /* get cursor color */
-                        index = op - 1;
-                        color = (*rgb_to_pixel)(
-                            s->cursor_palette[index][0],
-                            s->cursor_palette[index][1],
-                            s->cursor_palette[index][2]);
-                    }
-                } else {
-                    /* normal area */
-                    index = *vram;
-                    color = (*rgb_to_pixel)(
-                        s->color_palette[index][0],
-                        s->color_palette[index][1],
-                        s->color_palette[index][2]);
-                }
-                memcpy(dd, &color, w);
-                dd += w;
-                x++;
-                vram++;
-                if (x == s->width) {
-                    xmax = s->width - 1;
-                    y++;
-                    if (y == s->height) {
-                        ymax = s->height - 1;
-                        goto done;
-                    }
-                    data_display = dd = data_display + surface_stride(surface);
-                    xmin = 0;
-                    x = 0;
-                }
-            }
-            if (x > xmax)
-                xmax = x;
-            if (y > ymax)
-                ymax = y;
-        } else {
-            int dy;
-            if (page_min != (ram_addr_t)-1) {
-                reset_dirty(s, page_min, page_max);
-                page_min = (ram_addr_t)-1;
-                page_max = 0;
-                dpy_gfx_update(s->con, xmin, ymin,
-                               xmax - xmin + 1, ymax - ymin + 1);
-                xmin = s->width;
-                xmax = 0;
-                ymin = s->height;
-                ymax = 0;
-            }
-            x += G364_PAGE_SIZE;
-            dy = x / s->width;
-            x = x % s->width;
-            y += dy;
-            vram += G364_PAGE_SIZE;
-            data_display += dy * surface_stride(surface);
-            dd = data_display + x * w;
-        }
-        page += G364_PAGE_SIZE;
-    }
-
-done:
-    if (page_min != (ram_addr_t)-1) {
-        dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
-        reset_dirty(s, page_min, page_max);
-    }
-}
-
-static void g364fb_draw_blank(G364State *s)
-{
-    DisplaySurface *surface = qemu_console_surface(s->con);
-    int i, w;
-    uint8_t *d;
-
-    if (s->blanked) {
-        /* Screen is already blank. No need to redraw it */
-        return;
-    }
-
-    w = s->width * surface_bytes_per_pixel(surface);
-    d = surface_data(surface);
-    for (i = 0; i < s->height; i++) {
-        memset(d, 0, w);
-        d += surface_stride(surface);
-    }
-
-    dpy_gfx_update(s->con, 0, 0, s->width, s->height);
-    s->blanked = 1;
-}
-
-static void g364fb_update_display(void *opaque)
-{
-    G364State *s = opaque;
-    DisplaySurface *surface = qemu_console_surface(s->con);
-
-    qemu_flush_coalesced_mmio_buffer();
-
-    if (s->width == 0 || s->height == 0)
-        return;
-
-    if (s->width != surface_width(surface) ||
-        s->height != surface_height(surface)) {
-        qemu_console_resize(s->con, s->width, s->height);
-    }
-
-    if (s->ctla & CTLA_FORCE_BLANK) {
-        g364fb_draw_blank(s);
-    } else if (s->depth == 8) {
-        g364fb_draw_graphic8(s);
-    } else {
-        error_report("g364: unknown guest depth %d", s->depth);
-    }
-
-    qemu_irq_raise(s->irq);
-}
-
-static inline void g364fb_invalidate_display(void *opaque)
-{
-    G364State *s = opaque;
-
-    s->blanked = 0;
-    memory_region_set_dirty(&s->mem_vram, 0, s->vram_size);
-}
-
-static void g364fb_reset(G364State *s)
-{
-    qemu_irq_lower(s->irq);
-
-    memset(s->color_palette, 0, sizeof(s->color_palette));
-    memset(s->cursor_palette, 0, sizeof(s->cursor_palette));
-    memset(s->cursor, 0, sizeof(s->cursor));
-    s->cursor_position = 0;
-    s->ctla = 0;
-    s->top_of_screen = 0;
-    s->width = s->height = 0;
-    memset(s->vram, 0, s->vram_size);
-    g364fb_invalidate_display(s);
-}
-
-static void g364fb_screen_dump(void *opaque, const char *filename, bool cswitch,
-                               Error **errp)
-{
-    G364State *s = opaque;
-    int ret, y, x;
-    uint8_t index;
-    uint8_t *data_buffer;
-    FILE *f;
-
-    qemu_flush_coalesced_mmio_buffer();
-
-    if (s->depth != 8) {
-        error_setg(errp, "g364: unknown guest depth %d", s->depth);
-        return;
-    }
-
-    f = fopen(filename, "wb");
-    if (!f) {
-        error_setg(errp, "failed to open file '%s': %s", filename,
-                   strerror(errno));
-        return;
-    }
-
-    if (s->ctla & CTLA_FORCE_BLANK) {
-        /* blank screen */
-        ret = fprintf(f, "P4\n%d %d\n", s->width, s->height);
-        if (ret < 0) {
-            goto write_err;
-        }
-        for (y = 0; y < s->height; y++)
-            for (x = 0; x < s->width; x++) {
-                ret = fputc(0, f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-            }
-    } else {
-        data_buffer = s->vram + s->top_of_screen;
-        ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
-        if (ret < 0) {
-            goto write_err;
-        }
-        for (y = 0; y < s->height; y++)
-            for (x = 0; x < s->width; x++, data_buffer++) {
-                index = *data_buffer;
-                ret = fputc(s->color_palette[index][0], f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-                ret = fputc(s->color_palette[index][1], f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-                ret = fputc(s->color_palette[index][2], f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-        }
-    }
-
-out:
-    fclose(f);
-    return;
-
-write_err:
-    error_setg(errp, "failed to write to file '%s': %s", filename,
-               strerror(errno));
-    unlink(filename);
-    goto out;
-}
-
-/* called for accesses to io ports */
-static uint64_t g364fb_ctrl_read(void *opaque,
-                                 hwaddr addr,
-                                 unsigned int size)
-{
-    G364State *s = opaque;
-    uint32_t val;
-
-    if (addr >= REG_CURS_PAT && addr < REG_CURS_PAT + 0x1000) {
-        /* cursor pattern */
-        int idx = (addr - REG_CURS_PAT) >> 3;
-        val = s->cursor[idx];
-    } else if (addr >= REG_CURS_PAL && addr < REG_CURS_PAL + 0x18) {
-        /* cursor palette */
-        int idx = (addr - REG_CURS_PAL) >> 3;
-        val = ((uint32_t)s->cursor_palette[idx][0] << 16);
-        val |= ((uint32_t)s->cursor_palette[idx][1] << 8);
-        val |= ((uint32_t)s->cursor_palette[idx][2] << 0);
-    } else {
-        switch (addr) {
-            case REG_DISPLAY:
-                val = s->width / 4;
-                break;
-            case REG_VDISPLAY:
-                val = s->height * 2;
-                break;
-            case REG_CTLA:
-                val = s->ctla;
-                break;
-            default:
-            {
-                error_report("g364: invalid read at [" TARGET_FMT_plx "]",
-                             addr);
-                val = 0;
-                break;
-            }
-        }
-    }
-
-    trace_g364fb_read(addr, val);
-
-    return val;
-}
-
-static void g364fb_update_depth(G364State *s)
-{
-    static const int depths[8] = { 1, 2, 4, 8, 15, 16, 0 };
-    s->depth = depths[(s->ctla & 0x00700000) >> 20];
-}
-
-static void g364_invalidate_cursor_position(G364State *s)
-{
-    DisplaySurface *surface = qemu_console_surface(s->con);
-    int ymin, ymax, start, end;
-
-    /* invalidate only near the cursor */
-    ymin = s->cursor_position & 0xfff;
-    ymax = MIN(s->height, ymin + 64);
-    start = ymin * surface_stride(surface);
-    end = (ymax + 1) * surface_stride(surface);
-
-    memory_region_set_dirty(&s->mem_vram, start, end - start);
-}
-
-static void g364fb_ctrl_write(void *opaque,
-                              hwaddr addr,
-                              uint64_t val,
-                              unsigned int size)
-{
-    G364State *s = opaque;
-
-    trace_g364fb_write(addr, val);
-
-    if (addr >= REG_CLR_PAL && addr < REG_CLR_PAL + 0x800) {
-        /* color palette */
-        int idx = (addr - REG_CLR_PAL) >> 3;
-        s->color_palette[idx][0] = (val >> 16) & 0xff;
-        s->color_palette[idx][1] = (val >> 8) & 0xff;
-        s->color_palette[idx][2] = val & 0xff;
-        g364fb_invalidate_display(s);
-    } else if (addr >= REG_CURS_PAT && addr < REG_CURS_PAT + 0x1000) {
-        /* cursor pattern */
-        int idx = (addr - REG_CURS_PAT) >> 3;
-        s->cursor[idx] = val;
-        g364fb_invalidate_display(s);
-    } else if (addr >= REG_CURS_PAL && addr < REG_CURS_PAL + 0x18) {
-        /* cursor palette */
-        int idx = (addr - REG_CURS_PAL) >> 3;
-        s->cursor_palette[idx][0] = (val >> 16) & 0xff;
-        s->cursor_palette[idx][1] = (val >> 8) & 0xff;
-        s->cursor_palette[idx][2] = val & 0xff;
-        g364fb_invalidate_display(s);
-    } else {
-        switch (addr) {
-        case REG_BOOT: /* Boot timing */
-        case 0x00108: /* Line timing: half sync */
-        case 0x00110: /* Line timing: back porch */
-        case 0x00120: /* Line timing: short display */
-        case 0x00128: /* Frame timing: broad pulse */
-        case 0x00130: /* Frame timing: v sync */
-        case 0x00138: /* Frame timing: v preequalise */
-        case 0x00140: /* Frame timing: v postequalise */
-        case 0x00148: /* Frame timing: v blank */
-        case 0x00158: /* Line timing: line time */
-        case 0x00160: /* Frame store: line start */
-        case 0x00168: /* vram cycle: mem init */
-        case 0x00170: /* vram cycle: transfer delay */
-        case 0x00200: /* vram cycle: mask register */
-            /* ignore */
-            break;
-        case REG_TOP:
-            s->top_of_screen = val;
-            g364fb_invalidate_display(s);
-            break;
-        case REG_DISPLAY:
-            s->width = val * 4;
-            break;
-        case REG_VDISPLAY:
-            s->height = val / 2;
-            break;
-        case REG_CTLA:
-            s->ctla = val;
-            g364fb_update_depth(s);
-            g364fb_invalidate_display(s);
-            break;
-        case REG_CURS_POS:
-            g364_invalidate_cursor_position(s);
-            s->cursor_position = val;
-            g364_invalidate_cursor_position(s);
-            break;
-        case REG_RESET:
-            g364fb_reset(s);
-            break;
-        default:
-            error_report("g364: invalid write of 0x%" PRIx64
-                         " at [" TARGET_FMT_plx "]", val, addr);
-            break;
-        }
-    }
-    qemu_irq_lower(s->irq);
-}
-
-static const MemoryRegionOps g364fb_ctrl_ops = {
-    .read = g364fb_ctrl_read,
-    .write = g364fb_ctrl_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .impl.min_access_size = 4,
-    .impl.max_access_size = 4,
-};
-
-static int g364fb_post_load(void *opaque, int version_id)
-{
-    G364State *s = opaque;
-
-    /* force refresh */
-    g364fb_update_depth(s);
-    g364fb_invalidate_display(s);
-
-    return 0;
-}
-
-static const VMStateDescription vmstate_g364fb = {
-    .name = "g364fb",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .post_load = g364fb_post_load,
-    .fields = (VMStateField[]) {
-        VMSTATE_VBUFFER_UINT32(vram, G364State, 1, NULL, 0, vram_size),
-        VMSTATE_BUFFER_UNSAFE(color_palette, G364State, 0, 256 * 3),
-        VMSTATE_BUFFER_UNSAFE(cursor_palette, G364State, 0, 9),
-        VMSTATE_UINT16_ARRAY(cursor, G364State, 512),
-        VMSTATE_UINT32(cursor_position, G364State),
-        VMSTATE_UINT32(ctla, G364State),
-        VMSTATE_UINT32(top_of_screen, G364State),
-        VMSTATE_UINT32(width, G364State),
-        VMSTATE_UINT32(height, G364State),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void g364fb_init(DeviceState *dev, G364State *s)
-{
-    s->vram = g_malloc0(s->vram_size);
-
-    s->con = graphic_console_init(g364fb_update_display,
-                                  g364fb_invalidate_display,
-                                  g364fb_screen_dump, NULL, s);
-
-    memory_region_init_io(&s->mem_ctrl, &g364fb_ctrl_ops, s, "ctrl", 0x180000);
-    memory_region_init_ram_ptr(&s->mem_vram, "vram",
-                               s->vram_size, s->vram);
-    vmstate_register_ram(&s->mem_vram, dev);
-    memory_region_set_coalescing(&s->mem_vram);
-}
-
-typedef struct {
-    SysBusDevice busdev;
-    G364State g364;
-} G364SysBusState;
-
-static int g364fb_sysbus_init(SysBusDevice *dev)
-{
-    G364State *s = &FROM_SYSBUS(G364SysBusState, dev)->g364;
-
-    g364fb_init(&dev->qdev, s);
-    sysbus_init_irq(dev, &s->irq);
-    sysbus_init_mmio(dev, &s->mem_ctrl);
-    sysbus_init_mmio(dev, &s->mem_vram);
-
-    return 0;
-}
-
-static void g364fb_sysbus_reset(DeviceState *d)
-{
-    G364SysBusState *s = DO_UPCAST(G364SysBusState, busdev.qdev, d);
-    g364fb_reset(&s->g364);
-}
-
-static Property g364fb_sysbus_properties[] = {
-    DEFINE_PROP_HEX32("vram_size", G364SysBusState, g364.vram_size,
-    8 * 1024 * 1024),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void g364fb_sysbus_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = g364fb_sysbus_init;
-    dc->desc = "G364 framebuffer";
-    dc->reset = g364fb_sysbus_reset;
-    dc->vmsd = &vmstate_g364fb;
-    dc->props = g364fb_sysbus_properties;
-}
-
-static const TypeInfo g364fb_sysbus_info = {
-    .name          = "sysbus-g364",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(G364SysBusState),
-    .class_init    = g364fb_sysbus_class_init,
-};
-
-static void g364fb_register_types(void)
-{
-    type_register_static(&g364fb_sysbus_info);
-}
-
-type_init(g364fb_register_types)
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f8d8ee87f94d8197fbe4053f9924dd28c356854f 100644 (file)
@@ -0,0 +1,3 @@
+common-obj-$(CONFIG_MAX7310) += max7310.o
+common-obj-$(CONFIG_PL061) += pl061.o
+common-obj-$(CONFIG_PUV3) += puv3_gpio.o
diff --git a/hw/gpio/max7310.c b/hw/gpio/max7310.c
new file mode 100644 (file)
index 0000000..59b2877
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * MAX7310 8-port GPIO expansion chip.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This file is licensed under GNU GPL.
+ */
+
+#include "hw/i2c/i2c.h"
+
+typedef struct {
+    I2CSlave i2c;
+    int i2c_command_byte;
+    int len;
+
+    uint8_t level;
+    uint8_t direction;
+    uint8_t polarity;
+    uint8_t status;
+    uint8_t command;
+    qemu_irq handler[8];
+    qemu_irq *gpio_in;
+} MAX7310State;
+
+static void max7310_reset(DeviceState *dev)
+{
+    MAX7310State *s = FROM_I2C_SLAVE(MAX7310State, I2C_SLAVE(dev));
+    s->level &= s->direction;
+    s->direction = 0xff;
+    s->polarity = 0xf0;
+    s->status = 0x01;
+    s->command = 0x00;
+}
+
+static int max7310_rx(I2CSlave *i2c)
+{
+    MAX7310State *s = (MAX7310State *) i2c;
+
+    switch (s->command) {
+    case 0x00: /* Input port */
+        return s->level ^ s->polarity;
+        break;
+
+    case 0x01: /* Output port */
+        return s->level & ~s->direction;
+        break;
+
+    case 0x02: /* Polarity inversion */
+        return s->polarity;
+
+    case 0x03: /* Configuration */
+        return s->direction;
+
+    case 0x04: /* Timeout */
+        return s->status;
+        break;
+
+    case 0xff: /* Reserved */
+        return 0xff;
+
+    default:
+#ifdef VERBOSE
+        printf("%s: unknown register %02x\n", __FUNCTION__, s->command);
+#endif
+        break;
+    }
+    return 0xff;
+}
+
+static int max7310_tx(I2CSlave *i2c, uint8_t data)
+{
+    MAX7310State *s = (MAX7310State *) i2c;
+    uint8_t diff;
+    int line;
+
+    if (s->len ++ > 1) {
+#ifdef VERBOSE
+        printf("%s: message too long (%i bytes)\n", __FUNCTION__, s->len);
+#endif
+        return 1;
+    }
+
+    if (s->i2c_command_byte) {
+        s->command = data;
+        s->i2c_command_byte = 0;
+        return 0;
+    }
+
+    switch (s->command) {
+    case 0x01: /* Output port */
+        for (diff = (data ^ s->level) & ~s->direction; diff;
+                        diff &= ~(1 << line)) {
+            line = ffs(diff) - 1;
+            if (s->handler[line])
+                qemu_set_irq(s->handler[line], (data >> line) & 1);
+        }
+        s->level = (s->level & s->direction) | (data & ~s->direction);
+        break;
+
+    case 0x02: /* Polarity inversion */
+        s->polarity = data;
+        break;
+
+    case 0x03: /* Configuration */
+        s->level &= ~(s->direction ^ data);
+        s->direction = data;
+        break;
+
+    case 0x04: /* Timeout */
+        s->status = data;
+        break;
+
+    case 0x00: /* Input port - ignore writes */
+       break;
+    default:
+#ifdef VERBOSE
+        printf("%s: unknown register %02x\n", __FUNCTION__, s->command);
+#endif
+        return 1;
+    }
+
+    return 0;
+}
+
+static void max7310_event(I2CSlave *i2c, enum i2c_event event)
+{
+    MAX7310State *s = (MAX7310State *) i2c;
+    s->len = 0;
+
+    switch (event) {
+    case I2C_START_SEND:
+        s->i2c_command_byte = 1;
+        break;
+    case I2C_FINISH:
+#ifdef VERBOSE
+        if (s->len == 1)
+            printf("%s: message too short (%i bytes)\n", __FUNCTION__, s->len);
+#endif
+        break;
+    default:
+        break;
+    }
+}
+
+static const VMStateDescription vmstate_max7310 = {
+    .name = "max7310",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields      = (VMStateField []) {
+        VMSTATE_INT32(i2c_command_byte, MAX7310State),
+        VMSTATE_INT32(len, MAX7310State),
+        VMSTATE_UINT8(level, MAX7310State),
+        VMSTATE_UINT8(direction, MAX7310State),
+        VMSTATE_UINT8(polarity, MAX7310State),
+        VMSTATE_UINT8(status, MAX7310State),
+        VMSTATE_UINT8(command, MAX7310State),
+        VMSTATE_I2C_SLAVE(i2c, MAX7310State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void max7310_gpio_set(void *opaque, int line, int level)
+{
+    MAX7310State *s = (MAX7310State *) opaque;
+    if (line >= ARRAY_SIZE(s->handler) || line  < 0)
+        hw_error("bad GPIO line");
+
+    if (level)
+        s->level |= s->direction & (1 << line);
+    else
+        s->level &= ~(s->direction & (1 << line));
+}
+
+/* MAX7310 is SMBus-compatible (can be used with only SMBus protocols),
+ * but also accepts sequences that are not SMBus so return an I2C device.  */
+static int max7310_init(I2CSlave *i2c)
+{
+    MAX7310State *s = FROM_I2C_SLAVE(MAX7310State, i2c);
+
+    qdev_init_gpio_in(&i2c->qdev, max7310_gpio_set, 8);
+    qdev_init_gpio_out(&i2c->qdev, s->handler, 8);
+
+    return 0;
+}
+
+static void max7310_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+    k->init = max7310_init;
+    k->event = max7310_event;
+    k->recv = max7310_rx;
+    k->send = max7310_tx;
+    dc->reset = max7310_reset;
+    dc->vmsd = &vmstate_max7310;
+}
+
+static const TypeInfo max7310_info = {
+    .name          = "max7310",
+    .parent        = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(MAX7310State),
+    .class_init    = max7310_class_init,
+};
+
+static void max7310_register_types(void)
+{
+    type_register_static(&max7310_info);
+}
+
+type_init(max7310_register_types)
diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c
new file mode 100644 (file)
index 0000000..74bc109
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * Arm PrimeCell PL061 General Purpose IO with additional
+ * Luminary Micro Stellaris bits.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/sysbus.h"
+
+//#define DEBUG_PL061 1
+
+#ifdef DEBUG_PL061
+#define DPRINTF(fmt, ...) \
+do { printf("pl061: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "pl061: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "pl061: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+static const uint8_t pl061_id[12] =
+  { 0x00, 0x00, 0x00, 0x00, 0x61, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+static const uint8_t pl061_id_luminary[12] =
+  { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    uint32_t locked;
+    uint32_t data;
+    uint32_t old_data;
+    uint32_t dir;
+    uint32_t isense;
+    uint32_t ibe;
+    uint32_t iev;
+    uint32_t im;
+    uint32_t istate;
+    uint32_t afsel;
+    uint32_t dr2r;
+    uint32_t dr4r;
+    uint32_t dr8r;
+    uint32_t odr;
+    uint32_t pur;
+    uint32_t pdr;
+    uint32_t slr;
+    uint32_t den;
+    uint32_t cr;
+    uint32_t float_high;
+    uint32_t amsel;
+    qemu_irq irq;
+    qemu_irq out[8];
+    const unsigned char *id;
+} pl061_state;
+
+static const VMStateDescription vmstate_pl061 = {
+    .name = "pl061",
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(locked, pl061_state),
+        VMSTATE_UINT32(data, pl061_state),
+        VMSTATE_UINT32(old_data, pl061_state),
+        VMSTATE_UINT32(dir, pl061_state),
+        VMSTATE_UINT32(isense, pl061_state),
+        VMSTATE_UINT32(ibe, pl061_state),
+        VMSTATE_UINT32(iev, pl061_state),
+        VMSTATE_UINT32(im, pl061_state),
+        VMSTATE_UINT32(istate, pl061_state),
+        VMSTATE_UINT32(afsel, pl061_state),
+        VMSTATE_UINT32(dr2r, pl061_state),
+        VMSTATE_UINT32(dr4r, pl061_state),
+        VMSTATE_UINT32(dr8r, pl061_state),
+        VMSTATE_UINT32(odr, pl061_state),
+        VMSTATE_UINT32(pur, pl061_state),
+        VMSTATE_UINT32(pdr, pl061_state),
+        VMSTATE_UINT32(slr, pl061_state),
+        VMSTATE_UINT32(den, pl061_state),
+        VMSTATE_UINT32(cr, pl061_state),
+        VMSTATE_UINT32(float_high, pl061_state),
+        VMSTATE_UINT32_V(amsel, pl061_state, 2),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void pl061_update(pl061_state *s)
+{
+    uint8_t changed;
+    uint8_t mask;
+    uint8_t out;
+    int i;
+
+    /* Outputs float high.  */
+    /* FIXME: This is board dependent.  */
+    out = (s->data & s->dir) | ~s->dir;
+    changed = s->old_data ^ out;
+    if (!changed)
+        return;
+
+    s->old_data = out;
+    for (i = 0; i < 8; i++) {
+        mask = 1 << i;
+        if (changed & mask) {
+            DPRINTF("Set output %d = %d\n", i, (out & mask) != 0);
+            qemu_set_irq(s->out[i], (out & mask) != 0);
+        }
+    }
+
+    /* FIXME: Implement input interrupts.  */
+}
+
+static uint64_t pl061_read(void *opaque, hwaddr offset,
+                           unsigned size)
+{
+    pl061_state *s = (pl061_state *)opaque;
+
+    if (offset >= 0xfd0 && offset < 0x1000) {
+        return s->id[(offset - 0xfd0) >> 2];
+    }
+    if (offset < 0x400) {
+        return s->data & (offset >> 2);
+    }
+    switch (offset) {
+    case 0x400: /* Direction */
+        return s->dir;
+    case 0x404: /* Interrupt sense */
+        return s->isense;
+    case 0x408: /* Interrupt both edges */
+        return s->ibe;
+    case 0x40c: /* Interrupt event */
+        return s->iev;
+    case 0x410: /* Interrupt mask */
+        return s->im;
+    case 0x414: /* Raw interrupt status */
+        return s->istate;
+    case 0x418: /* Masked interrupt status */
+        return s->istate | s->im;
+    case 0x420: /* Alternate function select */
+        return s->afsel;
+    case 0x500: /* 2mA drive */
+        return s->dr2r;
+    case 0x504: /* 4mA drive */
+        return s->dr4r;
+    case 0x508: /* 8mA drive */
+        return s->dr8r;
+    case 0x50c: /* Open drain */
+        return s->odr;
+    case 0x510: /* Pull-up */
+        return s->pur;
+    case 0x514: /* Pull-down */
+        return s->pdr;
+    case 0x518: /* Slew rate control */
+        return s->slr;
+    case 0x51c: /* Digital enable */
+        return s->den;
+    case 0x520: /* Lock */
+        return s->locked;
+    case 0x524: /* Commit */
+        return s->cr;
+    case 0x528: /* Analog mode select */
+        return s->amsel;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl061_read: Bad offset %x\n", (int)offset);
+        return 0;
+    }
+}
+
+static void pl061_write(void *opaque, hwaddr offset,
+                        uint64_t value, unsigned size)
+{
+    pl061_state *s = (pl061_state *)opaque;
+    uint8_t mask;
+
+    if (offset < 0x400) {
+        mask = (offset >> 2) & s->dir;
+        s->data = (s->data & ~mask) | (value & mask);
+        pl061_update(s);
+        return;
+    }
+    switch (offset) {
+    case 0x400: /* Direction */
+        s->dir = value & 0xff;
+        break;
+    case 0x404: /* Interrupt sense */
+        s->isense = value & 0xff;
+        break;
+    case 0x408: /* Interrupt both edges */
+        s->ibe = value & 0xff;
+        break;
+    case 0x40c: /* Interrupt event */
+        s->iev = value & 0xff;
+        break;
+    case 0x410: /* Interrupt mask */
+        s->im = value & 0xff;
+        break;
+    case 0x41c: /* Interrupt clear */
+        s->istate &= ~value;
+        break;
+    case 0x420: /* Alternate function select */
+        mask = s->cr;
+        s->afsel = (s->afsel & ~mask) | (value & mask);
+        break;
+    case 0x500: /* 2mA drive */
+        s->dr2r = value & 0xff;
+        break;
+    case 0x504: /* 4mA drive */
+        s->dr4r = value & 0xff;
+        break;
+    case 0x508: /* 8mA drive */
+        s->dr8r = value & 0xff;
+        break;
+    case 0x50c: /* Open drain */
+        s->odr = value & 0xff;
+        break;
+    case 0x510: /* Pull-up */
+        s->pur = value & 0xff;
+        break;
+    case 0x514: /* Pull-down */
+        s->pdr = value & 0xff;
+        break;
+    case 0x518: /* Slew rate control */
+        s->slr = value & 0xff;
+        break;
+    case 0x51c: /* Digital enable */
+        s->den = value & 0xff;
+        break;
+    case 0x520: /* Lock */
+        s->locked = (value != 0xacce551);
+        break;
+    case 0x524: /* Commit */
+        if (!s->locked)
+            s->cr = value & 0xff;
+        break;
+    case 0x528:
+        s->amsel = value & 0xff;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl061_write: Bad offset %x\n", (int)offset);
+    }
+    pl061_update(s);
+}
+
+static void pl061_reset(pl061_state *s)
+{
+  s->locked = 1;
+  s->cr = 0xff;
+}
+
+static void pl061_set_irq(void * opaque, int irq, int level)
+{
+    pl061_state *s = (pl061_state *)opaque;
+    uint8_t mask;
+
+    mask = 1 << irq;
+    if ((s->dir & mask) == 0) {
+        s->data &= ~mask;
+        if (level)
+            s->data |= mask;
+        pl061_update(s);
+    }
+}
+
+static const MemoryRegionOps pl061_ops = {
+    .read = pl061_read,
+    .write = pl061_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int pl061_init(SysBusDevice *dev, const unsigned char *id)
+{
+    pl061_state *s = FROM_SYSBUS(pl061_state, dev);
+    s->id = id;
+    memory_region_init_io(&s->iomem, &pl061_ops, s, "pl061", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+    qdev_init_gpio_in(&dev->qdev, pl061_set_irq, 8);
+    qdev_init_gpio_out(&dev->qdev, s->out, 8);
+    pl061_reset(s);
+    return 0;
+}
+
+static int pl061_init_luminary(SysBusDevice *dev)
+{
+    return pl061_init(dev, pl061_id_luminary);
+}
+
+static int pl061_init_arm(SysBusDevice *dev)
+{
+    return pl061_init(dev, pl061_id);
+}
+
+static void pl061_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = pl061_init_arm;
+    dc->vmsd = &vmstate_pl061;
+}
+
+static const TypeInfo pl061_info = {
+    .name          = "pl061",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl061_state),
+    .class_init    = pl061_class_init,
+};
+
+static void pl061_luminary_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = pl061_init_luminary;
+    dc->vmsd = &vmstate_pl061;
+}
+
+static const TypeInfo pl061_luminary_info = {
+    .name          = "pl061_luminary",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl061_state),
+    .class_init    = pl061_luminary_class_init,
+};
+
+static void pl061_register_types(void)
+{
+    type_register_static(&pl061_info);
+    type_register_static(&pl061_luminary_info);
+}
+
+type_init(pl061_register_types)
diff --git a/hw/gpio/puv3_gpio.c b/hw/gpio/puv3_gpio.c
new file mode 100644 (file)
index 0000000..5bab97e
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * GPIO device simulation in PKUnity SoC
+ *
+ * Copyright (C) 2010-2012 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or any later version.
+ * See the COPYING file in the top-level directory.
+ */
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+
+#undef DEBUG_PUV3
+#include "hw/unicore32/puv3.h"
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    qemu_irq irq[9];
+
+    uint32_t reg_GPLR;
+    uint32_t reg_GPDR;
+    uint32_t reg_GPIR;
+} PUV3GPIOState;
+
+static uint64_t puv3_gpio_read(void *opaque, hwaddr offset,
+        unsigned size)
+{
+    PUV3GPIOState *s = opaque;
+    uint32_t ret = 0;
+
+    switch (offset) {
+    case 0x00:
+        ret = s->reg_GPLR;
+        break;
+    case 0x04:
+        ret = s->reg_GPDR;
+        break;
+    case 0x20:
+        ret = s->reg_GPIR;
+        break;
+    default:
+        DPRINTF("Bad offset 0x%x\n", offset);
+    }
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
+
+    return ret;
+}
+
+static void puv3_gpio_write(void *opaque, hwaddr offset,
+        uint64_t value, unsigned size)
+{
+    PUV3GPIOState *s = opaque;
+
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
+    switch (offset) {
+    case 0x04:
+        s->reg_GPDR = value;
+        break;
+    case 0x08:
+        if (s->reg_GPDR & value) {
+            s->reg_GPLR |= value;
+        } else {
+            DPRINTF("Write gpio input port error!");
+        }
+        break;
+    case 0x0c:
+        if (s->reg_GPDR & value) {
+            s->reg_GPLR &= ~value;
+        } else {
+            DPRINTF("Write gpio input port error!");
+        }
+        break;
+    case 0x10: /* GRER */
+    case 0x14: /* GFER */
+    case 0x18: /* GEDR */
+        break;
+    case 0x20: /* GPIR */
+        s->reg_GPIR = value;
+        break;
+    default:
+        DPRINTF("Bad offset 0x%x\n", offset);
+    }
+}
+
+static const MemoryRegionOps puv3_gpio_ops = {
+    .read = puv3_gpio_read,
+    .write = puv3_gpio_write,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int puv3_gpio_init(SysBusDevice *dev)
+{
+    PUV3GPIOState *s = FROM_SYSBUS(PUV3GPIOState, dev);
+
+    s->reg_GPLR = 0;
+    s->reg_GPDR = 0;
+
+    /* FIXME: these irqs not handled yet */
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW0]);
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW1]);
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW2]);
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW3]);
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW4]);
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW5]);
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW6]);
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW7]);
+    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOHIGH]);
+
+    memory_region_init_io(&s->iomem, &puv3_gpio_ops, s, "puv3_gpio",
+            PUV3_REGS_OFFSET);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static void puv3_gpio_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = puv3_gpio_init;
+}
+
+static const TypeInfo puv3_gpio_info = {
+    .name = "puv3_gpio",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(PUV3GPIOState),
+    .class_init = puv3_gpio_class_init,
+};
+
+static void puv3_gpio_register_type(void)
+{
+    type_register_static(&puv3_gpio_info);
+}
+
+type_init(puv3_gpio_register_type)
diff --git a/hw/grackle_pci.c b/hw/grackle_pci.c
deleted file mode 100644 (file)
index 69344d9..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * QEMU Grackle PCI host (heathrow OldWorld PowerMac)
- *
- * Copyright (c) 2006-2007 Fabrice Bellard
- * Copyright (c) 2007 Jocelyn Mayer
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/pci/pci_host.h"
-#include "hw/ppc/mac.h"
-#include "hw/pci/pci.h"
-
-/* debug Grackle */
-//#define DEBUG_GRACKLE
-
-#ifdef DEBUG_GRACKLE
-#define GRACKLE_DPRINTF(fmt, ...)                               \
-    do { printf("GRACKLE: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define GRACKLE_DPRINTF(fmt, ...)
-#endif
-
-#define GRACKLE_PCI_HOST_BRIDGE(obj) \
-    OBJECT_CHECK(GrackleState, (obj), TYPE_GRACKLE_PCI_HOST_BRIDGE)
-
-typedef struct GrackleState {
-    PCIHostState parent_obj;
-
-    MemoryRegion pci_mmio;
-    MemoryRegion pci_hole;
-} GrackleState;
-
-/* Don't know if this matches real hardware, but it agrees with OHW.  */
-static int pci_grackle_map_irq(PCIDevice *pci_dev, int irq_num)
-{
-    return (irq_num + (pci_dev->devfn >> 3)) & 3;
-}
-
-static void pci_grackle_set_irq(void *opaque, int irq_num, int level)
-{
-    qemu_irq *pic = opaque;
-
-    GRACKLE_DPRINTF("set_irq num %d level %d\n", irq_num, level);
-    qemu_set_irq(pic[irq_num + 0x15], level);
-}
-
-PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic,
-                         MemoryRegion *address_space_mem,
-                         MemoryRegion *address_space_io)
-{
-    DeviceState *dev;
-    SysBusDevice *s;
-    PCIHostState *phb;
-    GrackleState *d;
-
-    dev = qdev_create(NULL, TYPE_GRACKLE_PCI_HOST_BRIDGE);
-    qdev_init_nofail(dev);
-    s = SYS_BUS_DEVICE(dev);
-    phb = PCI_HOST_BRIDGE(dev);
-    d = GRACKLE_PCI_HOST_BRIDGE(dev);
-
-    memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL);
-    memory_region_init_alias(&d->pci_hole, "pci-hole", &d->pci_mmio,
-                             0x80000000ULL, 0x7e000000ULL);
-    memory_region_add_subregion(address_space_mem, 0x80000000ULL,
-                                &d->pci_hole);
-
-    phb->bus = pci_register_bus(dev, "pci",
-                                pci_grackle_set_irq,
-                                pci_grackle_map_irq,
-                                pic,
-                                &d->pci_mmio,
-                                address_space_io,
-                                0, 4, TYPE_PCI_BUS);
-
-    pci_create_simple(phb->bus, 0, "grackle");
-
-    sysbus_mmio_map(s, 0, base);
-    sysbus_mmio_map(s, 1, base + 0x00200000);
-
-    return phb->bus;
-}
-
-static int pci_grackle_init_device(SysBusDevice *dev)
-{
-    PCIHostState *phb;
-
-    phb = PCI_HOST_BRIDGE(dev);
-
-    memory_region_init_io(&phb->conf_mem, &pci_host_conf_le_ops,
-                          dev, "pci-conf-idx", 0x1000);
-    memory_region_init_io(&phb->data_mem, &pci_host_data_le_ops,
-                          dev, "pci-data-idx", 0x1000);
-    sysbus_init_mmio(dev, &phb->conf_mem);
-    sysbus_init_mmio(dev, &phb->data_mem);
-
-    return 0;
-}
-
-static int grackle_pci_host_init(PCIDevice *d)
-{
-    d->config[0x09] = 0x01;
-    return 0;
-}
-
-static void grackle_pci_class_init(ObjectClass *klass, void *data)
-{
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    k->init      = grackle_pci_host_init;
-    k->vendor_id = PCI_VENDOR_ID_MOTOROLA;
-    k->device_id = PCI_DEVICE_ID_MOTOROLA_MPC106;
-    k->revision  = 0x00;
-    k->class_id  = PCI_CLASS_BRIDGE_HOST;
-    dc->no_user = 1;
-}
-
-static const TypeInfo grackle_pci_info = {
-    .name          = "grackle",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIDevice),
-    .class_init = grackle_pci_class_init,
-};
-
-static void pci_grackle_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    k->init = pci_grackle_init_device;
-    dc->no_user = 1;
-}
-
-static const TypeInfo grackle_pci_host_info = {
-    .name          = TYPE_GRACKLE_PCI_HOST_BRIDGE,
-    .parent        = TYPE_PCI_HOST_BRIDGE,
-    .instance_size = sizeof(GrackleState),
-    .class_init    = pci_grackle_class_init,
-};
-
-static void grackle_register_types(void)
-{
-    type_register_static(&grackle_pci_info);
-    type_register_static(&grackle_pci_host_info);
-}
-
-type_init(grackle_register_types)
diff --git a/hw/gus.c b/hw/gus.c
deleted file mode 100644 (file)
index e44704b..0000000
--- a/hw/gus.c
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * QEMU Proxy for Gravis Ultrasound GF1 emulation by Tibor "TS" Schütz
- *
- * Copyright (c) 2002-2005 Vassili Karpov (malc)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/audio/audio.h"
-#include "audio/audio.h"
-#include "hw/isa/isa.h"
-#include "hw/gusemu.h"
-#include "hw/gustate.h"
-
-#define dolog(...) AUD_log ("audio", __VA_ARGS__)
-#ifdef DEBUG
-#define ldebug(...) dolog (__VA_ARGS__)
-#else
-#define ldebug(...)
-#endif
-
-#ifdef HOST_WORDS_BIGENDIAN
-#define GUS_ENDIANNESS 1
-#else
-#define GUS_ENDIANNESS 0
-#endif
-
-#define IO_READ_PROTO(name) \
-    static uint32_t name (void *opaque, uint32_t nport)
-#define IO_WRITE_PROTO(name) \
-    static void name (void *opaque, uint32_t nport, uint32_t val)
-
-typedef struct GUSState {
-    ISADevice dev;
-    GUSEmuState emu;
-    QEMUSoundCard card;
-    uint32_t freq;
-    uint32_t port;
-    int pos, left, shift, irqs;
-    GUSsample *mixbuf;
-    uint8_t himem[1024 * 1024 + 32 + 4096];
-    int samples;
-    SWVoiceOut *voice;
-    int64_t last_ticks;
-    qemu_irq pic;
-} GUSState;
-
-IO_READ_PROTO (gus_readb)
-{
-    GUSState *s = opaque;
-
-    return gus_read (&s->emu, nport, 1);
-}
-
-IO_READ_PROTO (gus_readw)
-{
-    GUSState *s = opaque;
-
-    return gus_read (&s->emu, nport, 2);
-}
-
-IO_WRITE_PROTO (gus_writeb)
-{
-    GUSState *s = opaque;
-
-    gus_write (&s->emu, nport, 1, val);
-}
-
-IO_WRITE_PROTO (gus_writew)
-{
-    GUSState *s = opaque;
-
-    gus_write (&s->emu, nport, 2, val);
-}
-
-static int write_audio (GUSState *s, int samples)
-{
-    int net = 0;
-    int pos = s->pos;
-
-    while (samples) {
-        int nbytes, wbytes, wsampl;
-
-        nbytes = samples << s->shift;
-        wbytes = AUD_write (
-            s->voice,
-            s->mixbuf + (pos << (s->shift - 1)),
-            nbytes
-            );
-
-        if (wbytes) {
-            wsampl = wbytes >> s->shift;
-
-            samples -= wsampl;
-            pos = (pos + wsampl) % s->samples;
-
-            net += wsampl;
-        }
-        else {
-            break;
-        }
-    }
-
-    return net;
-}
-
-static void GUS_callback (void *opaque, int free)
-{
-    int samples, to_play, net = 0;
-    GUSState *s = opaque;
-
-    samples = free >> s->shift;
-    to_play = audio_MIN (samples, s->left);
-
-    while (to_play) {
-        int written = write_audio (s, to_play);
-
-        if (!written) {
-            goto reset;
-        }
-
-        s->left -= written;
-        to_play -= written;
-        samples -= written;
-        net += written;
-    }
-
-    samples = audio_MIN (samples, s->samples);
-    if (samples) {
-        gus_mixvoices (&s->emu, s->freq, samples, s->mixbuf);
-
-        while (samples) {
-            int written = write_audio (s, samples);
-            if (!written) {
-                break;
-            }
-            samples -= written;
-            net += written;
-        }
-    }
-    s->left = samples;
-
- reset:
-    gus_irqgen (&s->emu, muldiv64 (net, 1000000, s->freq));
-}
-
-int GUS_irqrequest (GUSEmuState *emu, int hwirq, int n)
-{
-    GUSState *s = emu->opaque;
-    /* qemu_irq_lower (s->pic); */
-    qemu_irq_raise (s->pic);
-    s->irqs += n;
-    ldebug ("irqrequest %d %d %d\n", hwirq, n, s->irqs);
-    return n;
-}
-
-void GUS_irqclear (GUSEmuState *emu, int hwirq)
-{
-    GUSState *s = emu->opaque;
-    ldebug ("irqclear %d %d\n", hwirq, s->irqs);
-    qemu_irq_lower (s->pic);
-    s->irqs -= 1;
-#ifdef IRQ_STORM
-    if (s->irqs > 0) {
-        qemu_irq_raise (s->pic[hwirq]);
-    }
-#endif
-}
-
-void GUS_dmarequest (GUSEmuState *der)
-{
-    /* GUSState *s = (GUSState *) der; */
-    ldebug ("dma request %d\n", der->gusdma);
-    DMA_hold_DREQ (der->gusdma);
-}
-
-static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
-{
-    GUSState *s = opaque;
-    char tmpbuf[4096];
-    int pos = dma_pos, mode, left = dma_len - dma_pos;
-
-    ldebug ("read DMA %#x %d\n", dma_pos, dma_len);
-    mode = DMA_get_channel_mode (s->emu.gusdma);
-    while (left) {
-        int to_copy = audio_MIN ((size_t) left, sizeof (tmpbuf));
-        int copied;
-
-        ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos);
-        copied = DMA_read_memory (nchan, tmpbuf, pos, to_copy);
-        gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied);
-        left -= copied;
-        pos += copied;
-    }
-
-    if (0 == ((mode >> 4) & 1)) {
-        DMA_release_DREQ (s->emu.gusdma);
-    }
-    return dma_len;
-}
-
-static const VMStateDescription vmstate_gus = {
-    .name = "gus",
-    .version_id = 2,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .fields      = (VMStateField []) {
-        VMSTATE_INT32 (pos, GUSState),
-        VMSTATE_INT32 (left, GUSState),
-        VMSTATE_INT32 (shift, GUSState),
-        VMSTATE_INT32 (irqs, GUSState),
-        VMSTATE_INT32 (samples, GUSState),
-        VMSTATE_INT64 (last_ticks, GUSState),
-        VMSTATE_BUFFER (himem, GUSState),
-        VMSTATE_END_OF_LIST ()
-    }
-};
-
-static const MemoryRegionPortio gus_portio_list1[] = {
-    {0x000,  1, 1, .write = gus_writeb },
-    {0x000,  1, 2, .write = gus_writew },
-    {0x006, 10, 1, .read = gus_readb, .write = gus_writeb },
-    {0x006, 10, 2, .read = gus_readw, .write = gus_writew },
-    {0x100,  8, 1, .read = gus_readb, .write = gus_writeb },
-    {0x100,  8, 2, .read = gus_readw, .write = gus_writew },
-    PORTIO_END_OF_LIST (),
-};
-
-static const MemoryRegionPortio gus_portio_list2[] = {
-    {0, 1, 1, .read = gus_readb },
-    {0, 1, 2, .read = gus_readw },
-    PORTIO_END_OF_LIST (),
-};
-
-static int gus_initfn (ISADevice *dev)
-{
-    GUSState *s = DO_UPCAST (GUSState, dev, dev);
-    struct audsettings as;
-
-    AUD_register_card ("gus", &s->card);
-
-    as.freq = s->freq;
-    as.nchannels = 2;
-    as.fmt = AUD_FMT_S16;
-    as.endianness = GUS_ENDIANNESS;
-
-    s->voice = AUD_open_out (
-        &s->card,
-        NULL,
-        "gus",
-        s,
-        GUS_callback,
-        &as
-        );
-
-    if (!s->voice) {
-        AUD_remove_card (&s->card);
-        return -1;
-    }
-
-    s->shift = 2;
-    s->samples = AUD_get_buffer_size_out (s->voice) >> s->shift;
-    s->mixbuf = g_malloc0 (s->samples << s->shift);
-
-    isa_register_portio_list (dev, s->port, gus_portio_list1, s, "gus");
-    isa_register_portio_list (dev, (s->port + 0x100) & 0xf00,
-                              gus_portio_list2, s, "gus");
-
-    DMA_register_channel (s->emu.gusdma, GUS_read_DMA, s);
-    s->emu.himemaddr = s->himem;
-    s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32;
-    s->emu.opaque = s;
-    isa_init_irq (dev, &s->pic, s->emu.gusirq);
-
-    AUD_set_active_out (s->voice, 1);
-
-    return 0;
-}
-
-int GUS_init (ISABus *bus)
-{
-    isa_create_simple (bus, "gus");
-    return 0;
-}
-
-static Property gus_properties[] = {
-    DEFINE_PROP_UINT32 ("freq",    GUSState, freq,        44100),
-    DEFINE_PROP_HEX32  ("iobase",  GUSState, port,        0x240),
-    DEFINE_PROP_UINT32 ("irq",     GUSState, emu.gusirq,  7),
-    DEFINE_PROP_UINT32 ("dma",     GUSState, emu.gusdma,  3),
-    DEFINE_PROP_END_OF_LIST (),
-};
-
-static void gus_class_initfn (ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS (klass);
-    ISADeviceClass *ic = ISA_DEVICE_CLASS (klass);
-    ic->init = gus_initfn;
-    dc->desc = "Gravis Ultrasound GF1";
-    dc->vmsd = &vmstate_gus;
-    dc->props = gus_properties;
-}
-
-static const TypeInfo gus_info = {
-    .name          = "gus",
-    .parent        = TYPE_ISA_DEVICE,
-    .instance_size = sizeof (GUSState),
-    .class_init    = gus_class_initfn,
-};
-
-static void gus_register_types (void)
-{
-    type_register_static (&gus_info);
-}
-
-type_init (gus_register_types)
diff --git a/hw/gusemu_hal.c b/hw/gusemu_hal.c
deleted file mode 100644 (file)
index 0eee617..0000000
+++ /dev/null
@@ -1,554 +0,0 @@
-/*
- * GUSEMU32 - bus interface part
- *
- * Copyright (C) 2000-2007 Tibor "TS" Schütz
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-/*
- * TODO: check mixer: see 7.20 of sdk for panning pos (applies to all gus models?)?
- */
-
-#include "hw/gustate.h"
-#include "hw/gusemu.h"
-
-#define GUSregb(position) (*            (gusptr+(position)))
-#define GUSregw(position) (*(GUSword *) (gusptr+(position)))
-#define GUSregd(position) (*(GUSdword *)(gusptr+(position)))
-
-/* size given in bytes */
-unsigned int gus_read(GUSEmuState * state, int port, int size)
-{
-    int             value_read = 0;
-
-    GUSbyte        *gusptr;
-    gusptr = state->gusdatapos;
-    GUSregd(portaccesses)++;
-
-    switch (port & 0xff0f)
-    {
-        /* MixerCtrlReg (read not supported on GUS classic) */
-        /* case 0x200: return GUSregb(MixerCtrlReg2x0); */
-    case 0x206:                          /* IRQstatReg / SB2x6IRQ */
-        /* adlib/sb bits set in port handlers */
-        /* timer/voice bits set in gus_irqgen() */
-        /* dma bit set in gus_dma_transferdata */
-        /* midi not implemented yet */
-        return GUSregb(IRQStatReg2x6);
-    /* case 0x308:                       */ /* AdLib388 */
-    case 0x208:
-        if (GUSregb(GUS45TimerCtrl) & 1)
-            return GUSregb(TimerStatus2x8);
-        return GUSregb(AdLibStatus2x8);  /* AdLibStatus */
-    case 0x309:                          /* AdLib389 */
-    case 0x209:
-        return GUSregb(AdLibData2x9);    /* AdLibData */
-    case 0x20A:
-        return GUSregb(AdLibCommand2xA); /* AdLib2x8_2xA */
-
-#if 0
-    case 0x20B:                          /* GUS hidden registers (read not supported on GUS classic) */
-        switch (GUSregb(RegCtrl_2xF) & 0x07)
-        {
-        case 0:                                 /* IRQ/DMA select */
-            if (GUSregb(MixerCtrlReg2x0) & 0x40)
-                return GUSregb(IRQ_2xB);        /* control register select bit */
-            else
-                return GUSregb(DMA_2xB);
-            /* case 1-5:                        */ /* general purpose emulation regs  */
-            /*  return ...                      */ /* + status reset reg (write only) */
-        case 6:
-            return GUSregb(Jumper_2xB);         /* Joystick/MIDI enable (JumperReg) */
-        default:;
-        }
-        break;
-#endif
-
-    case 0x20C:                          /* SB2xCd */
-        value_read = GUSregb(SB2xCd);
-        if (GUSregb(StatRead_2xF) & 0x20)
-            GUSregb(SB2xCd) ^= 0x80; /* toggle MSB on read */
-        return value_read;
-        /* case 0x20D:                   */ /* SB2xD is write only -> 2xE writes to it*/
-    case 0x20E:
-        if (GUSregb(RegCtrl_2xF) & 0x80) /* 2xE read IRQ enabled? */
-        {
-            GUSregb(StatRead_2xF) |= 0x80;
-            GUS_irqrequest(state, state->gusirq, 1);
-        }
-        return GUSregb(SB2xE);           /* SB2xE */
-    case 0x20F:                          /* StatRead_2xF */
-        /*set/clear fixed bits */
-        /*value_read = (GUSregb(StatRead_2xF) & 0xf9)|1; */ /*(LSB not set on GUS classic!)*/
-        value_read = (GUSregb(StatRead_2xF) & 0xf9);
-        if (GUSregb(MixerCtrlReg2x0) & 0x08)
-            value_read |= 2;    /* DMA/IRQ enabled flag */
-        return value_read;
-    /* case 0x300:                      */ /* MIDI (not implemented) */
-    /* case 0x301:                      */ /* MIDI (not implemented) */
-    case 0x302:
-        return GUSregb(VoiceSelReg3x2); /* VoiceSelReg */
-    case 0x303:
-        return GUSregb(FunkSelReg3x3);  /* FunkSelReg */
-    case 0x304:                         /* DataRegLoByte3x4 + DataRegWord3x4 */
-    case 0x305:                         /* DataRegHiByte3x5 */
-        switch (GUSregb(FunkSelReg3x3))
-        {
-    /* common functions */
-        case 0x41:                      /* DramDMAContrReg */
-            value_read = GUSregb(GUS41DMACtrl); /* &0xfb */
-            GUSregb(GUS41DMACtrl) &= 0xbb;
-            if (state->gusdma >= 4)
-                value_read |= 0x04;
-            if (GUSregb(IRQStatReg2x6) & 0x80)
-            {
-                value_read |= 0x40;
-                GUSregb(IRQStatReg2x6) &= 0x7f;
-                if (!GUSregb(IRQStatReg2x6))
-                    GUS_irqclear(state, state->gusirq);
-            }
-            return (GUSbyte) value_read;
-            /* DramDMAmemPosReg */
-            /* case 0x42: value_read=GUSregw(GUS42DMAStart); break;*/
-            /* 43h+44h write only */
-        case 0x45:
-            return GUSregb(GUS45TimerCtrl);         /* TimerCtrlReg */
-            /* 46h+47h write only */
-            /* 48h: samp freq - write only */
-        case 0x49:
-            return GUSregb(GUS49SampCtrl) & 0xbf;   /* SampCtrlReg */
-        /* case 4bh:                                */ /* joystick trim not supported */
-        /* case 0x4c: return GUSregb(GUS4cReset);   */ /* GUSreset: write only*/
-    /* voice specific functions */
-        case 0x80:
-        case 0x81:
-        case 0x82:
-        case 0x83:
-        case 0x84:
-        case 0x85:
-        case 0x86:
-        case 0x87:
-        case 0x88:
-        case 0x89:
-        case 0x8a:
-        case 0x8b:
-        case 0x8c:
-        case 0x8d:
-            {
-                int             offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f);
-                offset += ((int) GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Funktion*2 */
-                value_read = GUSregw(offset);
-            }
-            break;
-    /* voice unspecific functions */
-        case 0x8e:                                  /* NumVoice */
-            return GUSregb(NumVoices);
-        case 0x8f:                                  /* irqstatreg */
-            /* (pseudo IRQ-FIFO is processed during a gus_write(0x3X3,0x8f)) */
-            return GUSregb(SynVoiceIRQ8f);
-        default:
-            return 0xffff;
-        }
-        if (size == 1)
-        {
-            if ((port & 0xff0f) == 0x305)
-                value_read = value_read >> 8;
-            value_read &= 0xff;
-        }
-        return (GUSword) value_read;
-    /* case 0x306:                                  */ /* Mixer/Version info */
-        /*  return 0xff; */ /* Pre 3.6 boards, ICS mixer NOT present */
-    case 0x307:                                     /* DRAMaccess */
-        {
-            GUSbyte        *adr;
-            adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff);
-            return *adr;
-        }
-    default:;
-    }
-    return 0xffff;
-}
-
-void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
-{
-    GUSbyte        *gusptr;
-    gusptr = state->gusdatapos;
-    GUSregd(portaccesses)++;
-
-    switch (port & 0xff0f)
-    {
-    case 0x200:                 /* MixerCtrlReg */
-        GUSregb(MixerCtrlReg2x0) = (GUSbyte) data;
-        break;
-    case 0x206:                 /* IRQstatReg / SB2x6IRQ */
-        if (GUSregb(GUS45TimerCtrl) & 0x20) /* SB IRQ enabled? -> set 2x6IRQ bit */
-        {
-            GUSregb(TimerStatus2x8) |= 0x08;
-            GUSregb(IRQStatReg2x6) = 0x10;
-            GUS_irqrequest(state, state->gusirq, 1);
-        }
-        break;
-    case 0x308:                /* AdLib 388h */
-    case 0x208:                /* AdLibCommandReg */
-        GUSregb(AdLibCommand2xA) = (GUSbyte) data;
-        break;
-    case 0x309:                /* AdLib 389h */
-    case 0x209:                /* AdLibDataReg */
-        if ((GUSregb(AdLibCommand2xA) == 0x04) && (!(GUSregb(GUS45TimerCtrl) & 1))) /* GUS auto timer mode enabled? */
-        {
-            if (data & 0x80)
-                GUSregb(TimerStatus2x8) &= 0x1f; /* AdLib IRQ reset? -> clear maskable adl. timer int regs */
-            else
-                GUSregb(TimerDataReg2x9) = (GUSbyte) data;
-        }
-        else
-        {
-            GUSregb(AdLibData2x9) = (GUSbyte) data;
-            if (GUSregb(GUS45TimerCtrl) & 0x02)
-            {
-                GUSregb(TimerStatus2x8) |= 0x01;
-                GUSregb(IRQStatReg2x6) = 0x10;
-                GUS_irqrequest(state, state->gusirq, 1);
-            }
-        }
-        break;
-    case 0x20A:
-        GUSregb(AdLibStatus2x8) = (GUSbyte) data;
-        break;                 /* AdLibStatus2x8 */
-    case 0x20B:                /* GUS hidden registers */
-        switch (GUSregb(RegCtrl_2xF) & 0x7)
-        {
-        case 0:
-            if (GUSregb(MixerCtrlReg2x0) & 0x40)
-                GUSregb(IRQ_2xB) = (GUSbyte) data; /* control register select bit */
-            else
-                GUSregb(DMA_2xB) = (GUSbyte) data;
-            break;
-            /* case 1-4: general purpose emulation regs */
-        case 5:                                    /* clear stat reg 2xF */
-            GUSregb(StatRead_2xF) = 0; /* ToDo: is this identical with GUS classic? */
-            if (!GUSregb(IRQStatReg2x6))
-                GUS_irqclear(state, state->gusirq);
-            break;
-        case 6:                                    /* Jumper reg (Joystick/MIDI enable) */
-            GUSregb(Jumper_2xB) = (GUSbyte) data;
-            break;
-        default:;
-        }
-        break;
-    case 0x20C:                /* SB2xCd */
-        if (GUSregb(GUS45TimerCtrl) & 0x20)
-        {
-            GUSregb(TimerStatus2x8) |= 0x10; /* SB IRQ enabled? -> set 2xCIRQ bit */
-            GUSregb(IRQStatReg2x6) = 0x10;
-            GUS_irqrequest(state, state->gusirq, 1);
-        }
-    case 0x20D:                /* SB2xCd no IRQ */
-        GUSregb(SB2xCd) = (GUSbyte) data;
-        break;
-    case 0x20E:                /* SB2xE */
-        GUSregb(SB2xE) = (GUSbyte) data;
-        break;
-    case 0x20F:
-        GUSregb(RegCtrl_2xF) = (GUSbyte) data;
-        break;                 /* CtrlReg2xF */
-    case 0x302:                /* VoiceSelReg */
-        GUSregb(VoiceSelReg3x2) = (GUSbyte) data;
-        break;
-    case 0x303:                /* FunkSelReg */
-        GUSregb(FunkSelReg3x3) = (GUSbyte) data;
-        if ((GUSbyte) data == 0x8f) /* set irqstatreg, get voicereg and clear IRQ */
-        {
-            int             voice;
-            if (GUSregd(voicewavetableirq)) /* WavetableIRQ */
-            {
-                for (voice = 0; voice < 31; voice++)
-                {
-                    if (GUSregd(voicewavetableirq) & (1 << voice))
-                    {
-                        GUSregd(voicewavetableirq) ^= (1 << voice); /* clear IRQ bit */
-                        GUSregb(voice << 5) &= 0x7f; /* clear voice reg irq bit */
-                        if (!GUSregd(voicewavetableirq))
-                            GUSregb(IRQStatReg2x6) &= 0xdf;
-                        if (!GUSregb(IRQStatReg2x6))
-                            GUS_irqclear(state, state->gusirq);
-                        GUSregb(SynVoiceIRQ8f) = voice | 0x60; /* (bit==0 => IRQ wartend) */
-                        return;
-                    }
-                }
-            }
-            else if (GUSregd(voicevolrampirq)) /* VolRamp IRQ */
-            {
-                for (voice = 0; voice < 31; voice++)
-                {
-                    if (GUSregd(voicevolrampirq) & (1 << voice))
-                    {
-                        GUSregd(voicevolrampirq) ^= (1 << voice); /* clear IRQ bit */
-                        GUSregb((voice << 5) + VSRVolRampControl) &= 0x7f; /* clear voice volume reg irq bit */
-                        if (!GUSregd(voicevolrampirq))
-                            GUSregb(IRQStatReg2x6) &= 0xbf;
-                        if (!GUSregb(IRQStatReg2x6))
-                            GUS_irqclear(state, state->gusirq);
-                        GUSregb(SynVoiceIRQ8f) = voice | 0x80; /* (bit==0 => IRQ wartend) */
-                        return;
-                    }
-                }
-            }
-            GUSregb(SynVoiceIRQ8f) = 0xe8; /* kein IRQ wartet */
-        }
-        break;
-    case 0x304:
-    case 0x305:
-        {
-            GUSword         writedata = (GUSword) data;
-            GUSword         readmask = 0x0000;
-            if (size == 1)
-            {
-                readmask = 0xff00;
-                writedata &= 0xff;
-                if ((port & 0xff0f) == 0x305)
-                {
-                    writedata = (GUSword) (writedata << 8);
-                    readmask = 0x00ff;
-                }
-            }
-            switch (GUSregb(FunkSelReg3x3))
-            {
-                /* voice specific functions */
-            case 0x00:
-            case 0x01:
-            case 0x02:
-            case 0x03:
-            case 0x04:
-            case 0x05:
-            case 0x06:
-            case 0x07:
-            case 0x08:
-            case 0x09:
-            case 0x0a:
-            case 0x0b:
-            case 0x0c:
-            case 0x0d:
-                {
-                    int             offset;
-                    if (!(GUSregb(GUS4cReset) & 0x01))
-                        break;  /* reset flag active? */
-                    offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f);
-                    offset += (GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /*  = Voice*32 + Funktion*2 */
-                    GUSregw(offset) = (GUSword) ((GUSregw(offset) & readmask) | writedata);
-                }
-                break;
-                /* voice unspecific functions */
-            case 0x0e:         /* NumVoices */
-                GUSregb(NumVoices) = (GUSbyte) data;
-                break;
-            /* case 0x0f:      */ /* read only */
-                /* common functions */
-            case 0x41:         /* DramDMAContrReg */
-                GUSregb(GUS41DMACtrl) = (GUSbyte) data;
-                if (data & 0x01)
-                    GUS_dmarequest(state);
-                break;
-            case 0x42:         /* DramDMAmemPosReg */
-                GUSregw(GUS42DMAStart) = (GUSregw(GUS42DMAStart) & readmask) | writedata;
-                GUSregb(GUS50DMAHigh) &= 0xf; /* compatibility stuff... */
-                break;
-            case 0x43:         /* DRAMaddrLo */
-                GUSregd(GUSDRAMPOS24bit) =
-                    (GUSregd(GUSDRAMPOS24bit) & (readmask | 0xff0000)) | writedata;
-                break;
-            case 0x44:         /* DRAMaddrHi */
-                GUSregd(GUSDRAMPOS24bit) =
-                    (GUSregd(GUSDRAMPOS24bit) & 0xffff) | ((data & 0x0f) << 16);
-                break;
-            case 0x45:         /* TCtrlReg */
-                GUSregb(GUS45TimerCtrl) = (GUSbyte) data;
-                if (!(data & 0x20))
-                    GUSregb(TimerStatus2x8) &= 0xe7;    /* sb IRQ dis? -> clear 2x8/2xC sb IRQ flags */
-                if (!(data & 0x02))
-                    GUSregb(TimerStatus2x8) &= 0xfe;    /* adlib data IRQ dis? -> clear 2x8 adlib IRQ flag */
-                if (!(GUSregb(TimerStatus2x8) & 0x19))
-                    GUSregb(IRQStatReg2x6) &= 0xef;     /* 0xe6; $$clear IRQ if both IRQ bits are inactive or cleared */
-                /* catch up delayed timer IRQs: */
-                if ((GUSregw(TimerIRQs) > 1) && (GUSregb(TimerDataReg2x9) & 3))
-                {
-                    if (GUSregb(TimerDataReg2x9) & 1)   /* start timer 1 (80us decrement rate) */
-                    {
-                        if (!(GUSregb(TimerDataReg2x9) & 0x40))
-                            GUSregb(TimerStatus2x8) |= 0xc0;    /* maskable bits */
-                        if (data & 4) /* timer1 irq enable */
-                        {
-                            GUSregb(TimerStatus2x8) |= 4;       /* nonmaskable bit */
-                            GUSregb(IRQStatReg2x6) |= 4;        /* timer 1 irq pending */
-                        }
-                    }
-                    if (GUSregb(TimerDataReg2x9) & 2)   /* start timer 2 (320us decrement rate) */
-                    {
-                        if (!(GUSregb(TimerDataReg2x9) & 0x20))
-                            GUSregb(TimerStatus2x8) |= 0xa0;    /* maskable bits */
-                        if (data & 8) /* timer2 irq enable */
-                        {
-                            GUSregb(TimerStatus2x8) |= 2;       /* nonmaskable bit */
-                            GUSregb(IRQStatReg2x6) |= 8;        /* timer 2 irq pending */
-                        }
-                    }
-                    GUSregw(TimerIRQs)--;
-                    if (GUSregw(BusyTimerIRQs) > 1)
-                        GUSregw(BusyTimerIRQs)--;
-                    else
-                        GUSregw(BusyTimerIRQs) =
-                            GUS_irqrequest(state, state->gusirq, GUSregw(TimerIRQs));
-                }
-                else
-                    GUSregw(TimerIRQs) = 0;
-
-                if (!(data & 0x04))
-                {
-                    GUSregb(TimerStatus2x8) &= 0xfb; /* clear non-maskable timer1 bit */
-                    GUSregb(IRQStatReg2x6)  &= 0xfb;
-                }
-                if (!(data & 0x08))
-                {
-                    GUSregb(TimerStatus2x8) &= 0xfd; /* clear non-maskable timer2 bit */
-                    GUSregb(IRQStatReg2x6)  &= 0xf7;
-                }
-                if (!GUSregb(IRQStatReg2x6))
-                    GUS_irqclear(state, state->gusirq);
-                break;
-            case 0x46:          /* Counter1 */
-                GUSregb(GUS46Counter1) = (GUSbyte) data;
-                break;
-            case 0x47:          /* Counter2 */
-                GUSregb(GUS47Counter2) = (GUSbyte) data;
-                break;
-            /* case 0x48:       */ /* sampling freq reg not emulated (same as interwave) */
-            case 0x49:          /* SampCtrlReg */
-                GUSregb(GUS49SampCtrl) = (GUSbyte) data;
-                break;
-            /* case 0x4b:       */ /* joystick trim not emulated */
-            case 0x4c:          /* GUSreset */
-                GUSregb(GUS4cReset) = (GUSbyte) data;
-                if (!(GUSregb(GUS4cReset) & 1)) /* reset... */
-                {
-                    GUSregd(voicewavetableirq) = 0;
-                    GUSregd(voicevolrampirq) = 0;
-                    GUSregw(TimerIRQs) = 0;
-                    GUSregw(BusyTimerIRQs) = 0;
-                    GUSregb(NumVoices) = 0xcd;
-                    GUSregb(IRQStatReg2x6) = 0;
-                    GUSregb(TimerStatus2x8) = 0;
-                    GUSregb(AdLibData2x9) = 0;
-                    GUSregb(TimerDataReg2x9) = 0;
-                    GUSregb(GUS41DMACtrl) = 0;
-                    GUSregb(GUS45TimerCtrl) = 0;
-                    GUSregb(GUS49SampCtrl) = 0;
-                    GUSregb(GUS4cReset) &= 0xf9; /* clear IRQ and DAC enable bits */
-                    GUS_irqclear(state, state->gusirq);
-                }
-                /* IRQ enable bit checked elsewhere */
-                /* EnableDAC bit may be used by external callers */
-                break;
-            }
-        }
-        break;
-    case 0x307:                /* DRAMaccess */
-        {
-            GUSbyte        *adr;
-            adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff);
-            *adr = (GUSbyte) data;
-        }
-        break;
-    }
-}
-
-/* Attention when breaking up a single DMA transfer to multiple ones:
- * it may lead to multiple terminal count interrupts and broken transfers:
- *
- * 1. Whenever you transfer a piece of data, the gusemu callback is invoked
- * 2. The callback may generate a TC irq (if the register was set up to do so)
- * 3. The irq may result in the program using the GUS to reprogram the GUS
- *
- * Some programs also decide to upload by just checking if TC occurs
- * (via interrupt or a cleared GUS dma flag)
- * and then start the next transfer, without checking DMA state
- *
- * Thus: Always make sure to set the TC flag correctly!
- *
- * Note that the genuine GUS had a granularity of 16 bytes/words for low/high DMA
- * while later cards had atomic granularity provided by an additional GUS50DMAHigh register
- * GUSemu also uses this register to support byte-granular transfers for better compatibility
- * with emulators other than GUSemu32
- */
-
-void gus_dma_transferdata(GUSEmuState * state, char *dma_addr, unsigned int count, int TC)
-{
-    /* this function gets called by the callback function as soon as a DMA transfer is about to start
-     * dma_addr is a translated address within accessible memory, not the physical one,
-     * count is (real dma count register)+1
-     * note that the amount of bytes transferred is fully determined by values in the DMA registers
-     * do not forget to update DMA states after transferring the entire block:
-     * DREQ cleared & TC asserted after the _whole_ transfer */
-
-    char           *srcaddr;
-    char           *destaddr;
-    char            msbmask = 0;
-    GUSbyte        *gusptr;
-    gusptr = state->gusdatapos;
-
-    srcaddr = dma_addr; /* system memory address */
-    {
-        int             offset = (GUSregw(GUS42DMAStart) << 4) + (GUSregb(GUS50DMAHigh) & 0xf);
-        if (state->gusdma >= 4)
-            offset = (offset & 0xc0000) + (2 * (offset & 0x1fff0)); /* 16 bit address translation */
-        destaddr = (char *) state->himemaddr + offset; /* wavetable RAM address */
-    }
-
-    GUSregw(GUS42DMAStart) += (GUSword)  (count >> 4);                           /* ToDo: add 16bit GUS page limit? */
-    GUSregb(GUS50DMAHigh)   = (GUSbyte) ((count + GUSregb(GUS50DMAHigh)) & 0xf); /* ToDo: add 16bit GUS page limit? */
-
-    if (GUSregb(GUS41DMACtrl) & 0x02)   /* direction, 0 := sysram->gusram */
-    {
-        char           *tmpaddr = destaddr;
-        destaddr = srcaddr;
-        srcaddr = tmpaddr;
-    }
-
-    if ((GUSregb(GUS41DMACtrl) & 0x80) && (!(GUSregb(GUS41DMACtrl) & 0x02)))
-        msbmask = (const char) 0x80;    /* invert MSB */
-    for (; count > 0; count--)
-    {
-        if (GUSregb(GUS41DMACtrl) & 0x40)
-            *(destaddr++) = *(srcaddr++);               /* 16 bit lobyte */
-        else
-            *(destaddr++) = (msbmask ^ (*(srcaddr++))); /* 8 bit */
-        if (state->gusdma >= 4)
-            *(destaddr++) = (msbmask ^ (*(srcaddr++))); /* 16 bit hibyte */
-    }
-
-    if (TC)
-    {
-        (GUSregb(GUS41DMACtrl)) &= 0xfe;        /* clear DMA request bit */
-        if (GUSregb(GUS41DMACtrl) & 0x20)       /* DMA terminal count IRQ */
-        {
-            GUSregb(IRQStatReg2x6) |= 0x80;
-            GUS_irqrequest(state, state->gusirq, 1);
-        }
-    }
-}
diff --git a/hw/gusemu_mixer.c b/hw/gusemu_mixer.c
deleted file mode 100644 (file)
index 816c58a..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * GUSEMU32 - mixing engine (similar to Interwave GF1 compatibility)
- *
- * Copyright (C) 2000-2007 Tibor "TS" Schütz
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/gusemu.h"
-#include "hw/gustate.h"
-
-#define GUSregb(position)  (*            (gusptr+(position)))
-#define GUSregw(position)  (*(GUSword *) (gusptr+(position)))
-#define GUSregd(position)  (*(GUSdword *)(gusptr+(position)))
-
-#define GUSvoice(position) (*(GUSword *)(voiceptr+(position)))
-
-/* samples are always 16bit stereo (4 bytes each, first right then left interleaved) */
-void gus_mixvoices(GUSEmuState * state, unsigned int playback_freq, unsigned int numsamples,
-                   GUSsample *bufferpos)
-{
-    /* note that byte registers are stored in the upper half of each voice register! */
-    GUSbyte        *gusptr;
-    int             Voice;
-    GUSword        *voiceptr;
-
-    unsigned int    count;
-    for (count = 0; count < numsamples * 2; count++)
-        *(bufferpos + count) = 0;       /* clear */
-
-    gusptr = state->gusdatapos;
-    voiceptr = (GUSword *) gusptr;
-    if (!(GUSregb(GUS4cReset) & 0x01))  /* reset flag active? */
-        return;
-
-    for (Voice = 0; Voice <= (GUSregb(NumVoices) & 31); Voice++)
-    {
-        if (GUSvoice(wVSRControl)        &  0x200)
-            GUSvoice(wVSRControl)        |= 0x100; /* voice stop request */
-        if (GUSvoice(wVSRVolRampControl) &  0x200)
-            GUSvoice(wVSRVolRampControl) |= 0x100; /* Volume ramp stop request */
-        if (!(GUSvoice(wVSRControl) & GUSvoice(wVSRVolRampControl) & 0x100)) /* neither voice nor volume calculation active - save some time here ;) */
-        {
-            unsigned int    sample;
-
-            unsigned int    LoopStart = (GUSvoice(wVSRLoopStartHi) << 16) | GUSvoice(wVSRLoopStartLo); /* 23.9 format */
-            unsigned int    LoopEnd   = (GUSvoice(wVSRLoopEndHi)   << 16) | GUSvoice(wVSRLoopEndLo);   /* 23.9 format */
-            unsigned int    CurrPos   = (GUSvoice(wVSRCurrPosHi)   << 16) | GUSvoice(wVSRCurrPosLo);   /* 23.9 format */
-            int             VoiceIncrement = ((((unsigned long) GUSvoice(wVSRFreq) * 44100) / playback_freq) * (14 >> 1)) /
-                                             ((GUSregb(NumVoices) & 31) + 1); /* 6.10 increment/frame to 23.9 increment/sample */
-
-            int             PanningPos = (GUSvoice(wVSRPanning) >> 8) & 0xf;
-
-            unsigned int    Volume32   = 32 * GUSvoice(wVSRCurrVol); /* 32 times larger than original gus for maintaining precision while ramping */
-            unsigned int    StartVol32 = (GUSvoice(wVSRVolRampStartVol) & 0xff00) * 32;
-            unsigned int    EndVol32   = (GUSvoice(wVSRVolRampEndVol)   & 0xff00) * 32;
-            int             VolumeIncrement32 = (32 * 16 * (GUSvoice(wVSRVolRampRate) & 0x3f00) >> 8) >> ((((GUSvoice(wVSRVolRampRate) & 0xc000) >> 8) >> 6) * 3); /* including 1/8/64/512 volume speed divisor */
-            VolumeIncrement32 = (((VolumeIncrement32 * 44100 / 2) / playback_freq) * 14) / ((GUSregb(NumVoices) & 31) + 1); /* adjust ramping speed to playback speed */
-
-            if (GUSvoice(wVSRControl) & 0x4000)
-                VoiceIncrement    = -VoiceIncrement;    /* reverse playback */
-            if (GUSvoice(wVSRVolRampControl) & 0x4000)
-                VolumeIncrement32 = -VolumeIncrement32; /* reverse ramping */
-
-            for (sample = 0; sample < numsamples; sample++)
-            {
-                int             sample1, sample2, Volume;
-                if (GUSvoice(wVSRControl) & 0x400)      /* 16bit */
-                {
-                    int offset = ((CurrPos >> 9) & 0xc0000) + (((CurrPos >> 9) & 0x1ffff) << 1);
-                    GUSchar *adr;
-                    adr = (GUSchar *) state->himemaddr + offset;
-                    sample1 = (*adr & 0xff) + (*(adr + 1) * 256);
-                    sample2 = (*(adr + 2) & 0xff) + (*(adr + 2 + 1) * 256);
-                }
-                else            /* 8bit */
-                {
-                    int offset = (CurrPos >> 9) & 0xfffff;
-                    GUSchar *adr;
-                    adr = (GUSchar *) state->himemaddr + offset;
-                    sample1 = (*adr) * 256;
-                    sample2 = (*(adr + 1)) * 256;
-                }
-
-                Volume = ((((Volume32 >> (4 + 5)) & 0xff) + 256) << (Volume32 >> ((4 + 8) + 5))) / 512; /* semi-logarithmic volume, +5 due to additional precision */
-                sample1 = (((sample1 * Volume) >> 16) * (512 - (CurrPos % 512))) / 512;
-                sample2 = (((sample2 * Volume) >> 16) * (CurrPos % 512)) / 512;
-                sample1 += sample2;
-
-                if (!(GUSvoice(wVSRVolRampControl) & 0x100))
-                {
-                    Volume32 += VolumeIncrement32;
-                    if ((GUSvoice(wVSRVolRampControl) & 0x4000) ? (Volume32 <= StartVol32) : (Volume32 >= EndVol32)) /* ramp up boundary cross */
-                    {
-                        if (GUSvoice(wVSRVolRampControl) & 0x2000)
-                            GUSvoice(wVSRVolRampControl) |= 0x8000;     /* volramp IRQ enabled? -> IRQ wait flag */
-                        if (GUSvoice(wVSRVolRampControl) & 0x800)       /* loop enabled */
-                        {
-                            if (GUSvoice(wVSRVolRampControl) & 0x1000)  /* bidir. loop */
-                            {
-                                GUSvoice(wVSRVolRampControl) ^= 0x4000; /* toggle dir */
-                                VolumeIncrement32 = -VolumeIncrement32;
-                            }
-                            else
-                                Volume32 = (GUSvoice(wVSRVolRampControl) & 0x4000) ? EndVol32 : StartVol32; /* unidir. loop ramp */
-                        }
-                        else
-                        {
-                            GUSvoice(wVSRVolRampControl) |= 0x100;
-                            Volume32 =
-                                (GUSvoice(wVSRVolRampControl) & 0x4000) ? StartVol32 : EndVol32;
-                        }
-                    }
-                }
-                if ((GUSvoice(wVSRVolRampControl) & 0xa000) == 0xa000)  /* volramp IRQ set and enabled? */
-                {
-                    GUSregd(voicevolrampirq) |= 1 << Voice;             /* set irq slot */
-                }
-                else
-                {
-                    GUSregd(voicevolrampirq) &= (~(1 << Voice));        /* clear irq slot */
-                    GUSvoice(wVSRVolRampControl) &= 0x7f00;
-                }
-
-                if (!(GUSvoice(wVSRControl) & 0x100))
-                {
-                    CurrPos += VoiceIncrement;
-                    if ((GUSvoice(wVSRControl) & 0x4000) ? (CurrPos <= LoopStart) : (CurrPos >= LoopEnd)) /* playback boundary cross */
-                    {
-                        if (GUSvoice(wVSRControl) & 0x2000)
-                            GUSvoice(wVSRControl) |= 0x8000;       /* voice IRQ enabled -> IRQ wait flag */
-                        if (GUSvoice(wVSRControl) & 0x800)         /* loop enabled */
-                        {
-                            if (GUSvoice(wVSRControl) & 0x1000)    /* pingpong loop */
-                            {
-                                GUSvoice(wVSRControl) ^= 0x4000;   /* toggle dir */
-                                VoiceIncrement = -VoiceIncrement;
-                            }
-                            else
-                                CurrPos = (GUSvoice(wVSRControl) & 0x4000) ? LoopEnd : LoopStart; /* unidir. loop */
-                        }
-                        else if (!(GUSvoice(wVSRVolRampControl) & 0x400))
-                            GUSvoice(wVSRControl) |= 0x100;        /* loop disabled, rollover check */
-                    }
-                }
-                if ((GUSvoice(wVSRControl) & 0xa000) == 0xa000)    /* wavetable IRQ set and enabled? */
-                {
-                    GUSregd(voicewavetableirq) |= 1 << Voice;      /* set irq slot */
-                }
-                else
-                {
-                    GUSregd(voicewavetableirq) &= (~(1 << Voice)); /* clear irq slot */
-                    GUSvoice(wVSRControl) &= 0x7f00;
-                }
-
-                /* mix samples into buffer */
-                *(bufferpos + 2 * sample)     += (GUSsample) ((sample1 * PanningPos) >> 4);        /* right */
-                *(bufferpos + 2 * sample + 1) += (GUSsample) ((sample1 * (15 - PanningPos)) >> 4); /* left */
-            }
-            /* write back voice and volume */
-            GUSvoice(wVSRCurrVol)   = Volume32 / 32;
-            GUSvoice(wVSRCurrPosHi) = CurrPos >> 16;
-            GUSvoice(wVSRCurrPosLo) = CurrPos & 0xffff;
-        }
-        voiceptr += 16; /* next voice */
-    }
-}
-
-void gus_irqgen(GUSEmuState * state, unsigned int elapsed_time)
-/* time given in microseconds */
-{
-    int             requestedIRQs = 0;
-    GUSbyte        *gusptr;
-    gusptr = state->gusdatapos;
-    if (GUSregb(TimerDataReg2x9) & 1) /* start timer 1 (80us decrement rate) */
-    {
-        unsigned int    timer1fraction = state->timer1fraction;
-        int             newtimerirqs;
-        newtimerirqs          = (elapsed_time + timer1fraction) / (80 * (256 - GUSregb(GUS46Counter1)));
-        state->timer1fraction = (elapsed_time + timer1fraction) % (80 * (256 - GUSregb(GUS46Counter1)));
-        if (newtimerirqs)
-        {
-            if (!(GUSregb(TimerDataReg2x9) & 0x40))
-                GUSregb(TimerStatus2x8) |= 0xc0; /* maskable bits */
-            if (GUSregb(GUS45TimerCtrl) & 4)     /* timer1 irq enable */
-            {
-                GUSregb(TimerStatus2x8) |= 4;    /* nonmaskable bit */
-                GUSregb(IRQStatReg2x6)  |= 4;    /* timer 1 irq pending */
-                GUSregw(TimerIRQs) += newtimerirqs;
-                requestedIRQs += newtimerirqs;
-            }
-        }
-    }
-    if (GUSregb(TimerDataReg2x9) & 2) /* start timer 2 (320us decrement rate) */
-    {
-        unsigned int timer2fraction = state->timer2fraction;
-        int             newtimerirqs;
-        newtimerirqs          = (elapsed_time + timer2fraction) / (320 * (256 - GUSregb(GUS47Counter2)));
-        state->timer2fraction = (elapsed_time + timer2fraction) % (320 * (256 - GUSregb(GUS47Counter2)));
-        if (newtimerirqs)
-        {
-            if (!(GUSregb(TimerDataReg2x9) & 0x20))
-                GUSregb(TimerStatus2x8) |= 0xa0; /* maskable bits */
-            if (GUSregb(GUS45TimerCtrl) & 8)     /* timer2 irq enable */
-            {
-                GUSregb(TimerStatus2x8) |= 2;    /* nonmaskable bit */
-                GUSregb(IRQStatReg2x6)  |= 8;    /* timer 2 irq pending */
-                GUSregw(TimerIRQs) += newtimerirqs;
-                requestedIRQs += newtimerirqs;
-            }
-        }
-    }
-    if (GUSregb(GUS4cReset) & 0x4) /* synth IRQ enable */
-    {
-        if (GUSregd(voicewavetableirq))
-            GUSregb(IRQStatReg2x6) |= 0x20;
-        if (GUSregd(voicevolrampirq))
-            GUSregb(IRQStatReg2x6) |= 0x40;
-    }
-    if ((!requestedIRQs) && GUSregb(IRQStatReg2x6))
-        requestedIRQs++;
-    if (GUSregb(IRQStatReg2x6))
-        GUSregw(BusyTimerIRQs) = GUS_irqrequest(state, state->gusirq, requestedIRQs);
-}
diff --git a/hw/hd-geometry.c b/hw/hd-geometry.c
deleted file mode 100644 (file)
index 6feb4f8..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Hard disk geometry utilities
- *
- * Copyright (C) 2012 Red Hat, Inc.
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- *
- * This file incorporates work covered by the following copyright and
- * permission notice:
- *
- * Copyright (c) 2003 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "block/block.h"
-#include "hw/block/block.h"
-#include "trace.h"
-
-struct partition {
-        uint8_t boot_ind;           /* 0x80 - active */
-        uint8_t head;               /* starting head */
-        uint8_t sector;             /* starting sector */
-        uint8_t cyl;                /* starting cylinder */
-        uint8_t sys_ind;            /* What partition type */
-        uint8_t end_head;           /* end head */
-        uint8_t end_sector;         /* end sector */
-        uint8_t end_cyl;            /* end cylinder */
-        uint32_t start_sect;        /* starting sector counting from 0 */
-        uint32_t nr_sects;          /* nr of sectors in partition */
-} QEMU_PACKED;
-
-/* try to guess the disk logical geometry from the MSDOS partition table.
-   Return 0 if OK, -1 if could not guess */
-static int guess_disk_lchs(BlockDriverState *bs,
-                           int *pcylinders, int *pheads, int *psectors)
-{
-    uint8_t buf[BDRV_SECTOR_SIZE];
-    int i, heads, sectors, cylinders;
-    struct partition *p;
-    uint32_t nr_sects;
-    uint64_t nb_sectors;
-
-    bdrv_get_geometry(bs, &nb_sectors);
-
-    /**
-     * The function will be invoked during startup not only in sync I/O mode,
-     * but also in async I/O mode. So the I/O throttling function has to
-     * be disabled temporarily here, not permanently.
-     */
-    if (bdrv_read_unthrottled(bs, 0, buf, 1) < 0) {
-        return -1;
-    }
-    /* test msdos magic */
-    if (buf[510] != 0x55 || buf[511] != 0xaa) {
-        return -1;
-    }
-    for (i = 0; i < 4; i++) {
-        p = ((struct partition *)(buf + 0x1be)) + i;
-        nr_sects = le32_to_cpu(p->nr_sects);
-        if (nr_sects && p->end_head) {
-            /* We make the assumption that the partition terminates on
-               a cylinder boundary */
-            heads = p->end_head + 1;
-            sectors = p->end_sector & 63;
-            if (sectors == 0) {
-                continue;
-            }
-            cylinders = nb_sectors / (heads * sectors);
-            if (cylinders < 1 || cylinders > 16383) {
-                continue;
-            }
-            *pheads = heads;
-            *psectors = sectors;
-            *pcylinders = cylinders;
-            trace_hd_geometry_lchs_guess(bs, cylinders, heads, sectors);
-            return 0;
-        }
-    }
-    return -1;
-}
-
-static void guess_chs_for_size(BlockDriverState *bs,
-                uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs)
-{
-    uint64_t nb_sectors;
-    int cylinders;
-
-    bdrv_get_geometry(bs, &nb_sectors);
-
-    cylinders = nb_sectors / (16 * 63);
-    if (cylinders > 16383) {
-        cylinders = 16383;
-    } else if (cylinders < 2) {
-        cylinders = 2;
-    }
-    *pcyls = cylinders;
-    *pheads = 16;
-    *psecs = 63;
-}
-
-void hd_geometry_guess(BlockDriverState *bs,
-                       uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs,
-                       int *ptrans)
-{
-    int cylinders, heads, secs, translation;
-
-    if (guess_disk_lchs(bs, &cylinders, &heads, &secs) < 0) {
-        /* no LCHS guess: use a standard physical disk geometry  */
-        guess_chs_for_size(bs, pcyls, pheads, psecs);
-        translation = hd_bios_chs_auto_trans(*pcyls, *pheads, *psecs);
-    } else if (heads > 16) {
-        /* LCHS guess with heads > 16 means that a BIOS LBA
-           translation was active, so a standard physical disk
-           geometry is OK */
-        guess_chs_for_size(bs, pcyls, pheads, psecs);
-        translation = *pcyls * *pheads <= 131072
-            ? BIOS_ATA_TRANSLATION_LARGE
-            : BIOS_ATA_TRANSLATION_LBA;
-    } else {
-        /* LCHS guess with heads <= 16: use as physical geometry */
-        *pcyls = cylinders;
-        *pheads = heads;
-        *psecs = secs;
-        /* disable any translation to be in sync with
-           the logical geometry */
-        translation = BIOS_ATA_TRANSLATION_NONE;
-    }
-    if (ptrans) {
-        *ptrans = translation;
-    }
-    trace_hd_geometry_guess(bs, *pcyls, *pheads, *psecs, translation);
-}
-
-int hd_bios_chs_auto_trans(uint32_t cyls, uint32_t heads, uint32_t secs)
-{
-    return cyls <= 1024 && heads <= 16 && secs <= 63
-        ? BIOS_ATA_TRANSLATION_NONE
-        : BIOS_ATA_TRANSLATION_LBA;
-}
diff --git a/hw/hda-audio.c b/hw/hda-audio.c
deleted file mode 100644 (file)
index 6bdd820..0000000
+++ /dev/null
@@ -1,1098 +0,0 @@
-/*
- * Copyright (C) 2010 Red Hat, Inc.
- *
- * written by Gerd Hoffmann <kraxel@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 or
- * (at your option) version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/hw.h"
-#include "hw/pci/pci.h"
-#include "hw/intel-hda.h"
-#include "hw/intel-hda-defs.h"
-#include "audio/audio.h"
-
-/* -------------------------------------------------------------------------- */
-
-typedef struct desc_param {
-    uint32_t id;
-    uint32_t val;
-} desc_param;
-
-typedef struct desc_node {
-    uint32_t nid;
-    const char *name;
-    const desc_param *params;
-    uint32_t nparams;
-    uint32_t config;
-    uint32_t pinctl;
-    uint32_t *conn;
-    uint32_t stindex;
-} desc_node;
-
-typedef struct desc_codec {
-    const char *name;
-    uint32_t iid;
-    const desc_node *nodes;
-    uint32_t nnodes;
-} desc_codec;
-
-static const desc_param* hda_codec_find_param(const desc_node *node, uint32_t id)
-{
-    int i;
-
-    for (i = 0; i < node->nparams; i++) {
-        if (node->params[i].id == id) {
-            return &node->params[i];
-        }
-    }
-    return NULL;
-}
-
-static const desc_node* hda_codec_find_node(const desc_codec *codec, uint32_t nid)
-{
-    int i;
-
-    for (i = 0; i < codec->nnodes; i++) {
-        if (codec->nodes[i].nid == nid) {
-            return &codec->nodes[i];
-        }
-    }
-    return NULL;
-}
-
-static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
-{
-    if (format & AC_FMT_TYPE_NON_PCM) {
-        return;
-    }
-
-    as->freq = (format & AC_FMT_BASE_44K) ? 44100 : 48000;
-
-    switch ((format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT) {
-    case 1: as->freq *= 2; break;
-    case 2: as->freq *= 3; break;
-    case 3: as->freq *= 4; break;
-    }
-
-    switch ((format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT) {
-    case 1: as->freq /= 2; break;
-    case 2: as->freq /= 3; break;
-    case 3: as->freq /= 4; break;
-    case 4: as->freq /= 5; break;
-    case 5: as->freq /= 6; break;
-    case 6: as->freq /= 7; break;
-    case 7: as->freq /= 8; break;
-    }
-
-    switch (format & AC_FMT_BITS_MASK) {
-    case AC_FMT_BITS_8:  as->fmt = AUD_FMT_S8;  break;
-    case AC_FMT_BITS_16: as->fmt = AUD_FMT_S16; break;
-    case AC_FMT_BITS_32: as->fmt = AUD_FMT_S32; break;
-    }
-
-    as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1;
-}
-
-/* -------------------------------------------------------------------------- */
-/*
- * HDA codec descriptions
- */
-
-/* some defines */
-
-#define QEMU_HDA_ID_VENDOR  0x1af4
-#define QEMU_HDA_PCM_FORMATS (AC_SUPPCM_BITS_16 |       \
-                              0x1fc /* 16 -> 96 kHz */)
-#define QEMU_HDA_AMP_NONE    (0)
-#define QEMU_HDA_AMP_STEPS   0x4a
-
-#ifdef CONFIG_MIXEMU
-# define QEMU_HDA_ID_OUTPUT  ((QEMU_HDA_ID_VENDOR << 16) | 0x12)
-# define QEMU_HDA_ID_DUPLEX  ((QEMU_HDA_ID_VENDOR << 16) | 0x22)
-# define QEMU_HDA_ID_MICRO   ((QEMU_HDA_ID_VENDOR << 16) | 0x32)
-# define QEMU_HDA_AMP_CAPS                                              \
-    (AC_AMPCAP_MUTE |                                                   \
-     (QEMU_HDA_AMP_STEPS << AC_AMPCAP_OFFSET_SHIFT)    |                \
-     (QEMU_HDA_AMP_STEPS << AC_AMPCAP_NUM_STEPS_SHIFT) |                \
-     (3                  << AC_AMPCAP_STEP_SIZE_SHIFT))
-#else
-# define QEMU_HDA_ID_OUTPUT  ((QEMU_HDA_ID_VENDOR << 16) | 0x11)
-# define QEMU_HDA_ID_DUPLEX  ((QEMU_HDA_ID_VENDOR << 16) | 0x21)
-# define QEMU_HDA_ID_MICRO   ((QEMU_HDA_ID_VENDOR << 16) | 0x31)
-# define QEMU_HDA_AMP_CAPS   QEMU_HDA_AMP_NONE
-#endif
-
-/* common: audio output widget */
-static const desc_param common_params_audio_dac[] = {
-    {
-        .id  = AC_PAR_AUDIO_WIDGET_CAP,
-        .val = ((AC_WID_AUD_OUT << AC_WCAP_TYPE_SHIFT) |
-                AC_WCAP_FORMAT_OVRD |
-                AC_WCAP_AMP_OVRD |
-                AC_WCAP_OUT_AMP |
-                AC_WCAP_STEREO),
-    },{
-        .id  = AC_PAR_PCM,
-        .val = QEMU_HDA_PCM_FORMATS,
-    },{
-        .id  = AC_PAR_STREAM,
-        .val = AC_SUPFMT_PCM,
-    },{
-        .id  = AC_PAR_AMP_IN_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_AMP_OUT_CAP,
-        .val = QEMU_HDA_AMP_CAPS,
-    },
-};
-
-/* common: audio input widget */
-static const desc_param common_params_audio_adc[] = {
-    {
-        .id  = AC_PAR_AUDIO_WIDGET_CAP,
-        .val = ((AC_WID_AUD_IN << AC_WCAP_TYPE_SHIFT) |
-                AC_WCAP_CONN_LIST |
-                AC_WCAP_FORMAT_OVRD |
-                AC_WCAP_AMP_OVRD |
-                AC_WCAP_IN_AMP |
-                AC_WCAP_STEREO),
-    },{
-        .id  = AC_PAR_CONNLIST_LEN,
-        .val = 1,
-    },{
-        .id  = AC_PAR_PCM,
-        .val = QEMU_HDA_PCM_FORMATS,
-    },{
-        .id  = AC_PAR_STREAM,
-        .val = AC_SUPFMT_PCM,
-    },{
-        .id  = AC_PAR_AMP_IN_CAP,
-        .val = QEMU_HDA_AMP_CAPS,
-    },{
-        .id  = AC_PAR_AMP_OUT_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },
-};
-
-/* common: pin widget (line-out) */
-static const desc_param common_params_audio_lineout[] = {
-    {
-        .id  = AC_PAR_AUDIO_WIDGET_CAP,
-        .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) |
-                AC_WCAP_CONN_LIST |
-                AC_WCAP_STEREO),
-    },{
-        .id  = AC_PAR_PIN_CAP,
-        .val = AC_PINCAP_OUT,
-    },{
-        .id  = AC_PAR_CONNLIST_LEN,
-        .val = 1,
-    },{
-        .id  = AC_PAR_AMP_IN_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_AMP_OUT_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },
-};
-
-/* common: pin widget (line-in) */
-static const desc_param common_params_audio_linein[] = {
-    {
-        .id  = AC_PAR_AUDIO_WIDGET_CAP,
-        .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) |
-                AC_WCAP_STEREO),
-    },{
-        .id  = AC_PAR_PIN_CAP,
-        .val = AC_PINCAP_IN,
-    },{
-        .id  = AC_PAR_AMP_IN_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_AMP_OUT_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },
-};
-
-/* output: root node */
-static const desc_param output_params_root[] = {
-    {
-        .id  = AC_PAR_VENDOR_ID,
-        .val = QEMU_HDA_ID_OUTPUT,
-    },{
-        .id  = AC_PAR_SUBSYSTEM_ID,
-        .val = QEMU_HDA_ID_OUTPUT,
-    },{
-        .id  = AC_PAR_REV_ID,
-        .val = 0x00100101,
-    },{
-        .id  = AC_PAR_NODE_COUNT,
-        .val = 0x00010001,
-    },
-};
-
-/* output: audio function */
-static const desc_param output_params_audio_func[] = {
-    {
-        .id  = AC_PAR_FUNCTION_TYPE,
-        .val = AC_GRP_AUDIO_FUNCTION,
-    },{
-        .id  = AC_PAR_SUBSYSTEM_ID,
-        .val = QEMU_HDA_ID_OUTPUT,
-    },{
-        .id  = AC_PAR_NODE_COUNT,
-        .val = 0x00020002,
-    },{
-        .id  = AC_PAR_PCM,
-        .val = QEMU_HDA_PCM_FORMATS,
-    },{
-        .id  = AC_PAR_STREAM,
-        .val = AC_SUPFMT_PCM,
-    },{
-        .id  = AC_PAR_AMP_IN_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_AMP_OUT_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_GPIO_CAP,
-        .val = 0,
-    },{
-        .id  = AC_PAR_AUDIO_FG_CAP,
-        .val = 0x00000808,
-    },{
-        .id  = AC_PAR_POWER_STATE,
-        .val = 0,
-    },
-};
-
-/* output: nodes */
-static const desc_node output_nodes[] = {
-    {
-        .nid     = AC_NODE_ROOT,
-        .name    = "root",
-        .params  = output_params_root,
-        .nparams = ARRAY_SIZE(output_params_root),
-    },{
-        .nid     = 1,
-        .name    = "func",
-        .params  = output_params_audio_func,
-        .nparams = ARRAY_SIZE(output_params_audio_func),
-    },{
-        .nid     = 2,
-        .name    = "dac",
-        .params  = common_params_audio_dac,
-        .nparams = ARRAY_SIZE(common_params_audio_dac),
-        .stindex = 0,
-    },{
-        .nid     = 3,
-        .name    = "out",
-        .params  = common_params_audio_lineout,
-        .nparams = ARRAY_SIZE(common_params_audio_lineout),
-        .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
-                    (AC_JACK_LINE_OUT     << AC_DEFCFG_DEVICE_SHIFT)    |
-                    (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
-                    (AC_JACK_COLOR_GREEN  << AC_DEFCFG_COLOR_SHIFT)     |
-                    0x10),
-        .pinctl  = AC_PINCTL_OUT_EN,
-        .conn    = (uint32_t[]) { 2 },
-    }
-};
-
-/* output: codec */
-static const desc_codec output = {
-    .name   = "output",
-    .iid    = QEMU_HDA_ID_OUTPUT,
-    .nodes  = output_nodes,
-    .nnodes = ARRAY_SIZE(output_nodes),
-};
-
-/* duplex: root node */
-static const desc_param duplex_params_root[] = {
-    {
-        .id  = AC_PAR_VENDOR_ID,
-        .val = QEMU_HDA_ID_DUPLEX,
-    },{
-        .id  = AC_PAR_SUBSYSTEM_ID,
-        .val = QEMU_HDA_ID_DUPLEX,
-    },{
-        .id  = AC_PAR_REV_ID,
-        .val = 0x00100101,
-    },{
-        .id  = AC_PAR_NODE_COUNT,
-        .val = 0x00010001,
-    },
-};
-
-/* duplex: audio function */
-static const desc_param duplex_params_audio_func[] = {
-    {
-        .id  = AC_PAR_FUNCTION_TYPE,
-        .val = AC_GRP_AUDIO_FUNCTION,
-    },{
-        .id  = AC_PAR_SUBSYSTEM_ID,
-        .val = QEMU_HDA_ID_DUPLEX,
-    },{
-        .id  = AC_PAR_NODE_COUNT,
-        .val = 0x00020004,
-    },{
-        .id  = AC_PAR_PCM,
-        .val = QEMU_HDA_PCM_FORMATS,
-    },{
-        .id  = AC_PAR_STREAM,
-        .val = AC_SUPFMT_PCM,
-    },{
-        .id  = AC_PAR_AMP_IN_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_AMP_OUT_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_GPIO_CAP,
-        .val = 0,
-    },{
-        .id  = AC_PAR_AUDIO_FG_CAP,
-        .val = 0x00000808,
-    },{
-        .id  = AC_PAR_POWER_STATE,
-        .val = 0,
-    },
-};
-
-/* duplex: nodes */
-static const desc_node duplex_nodes[] = {
-    {
-        .nid     = AC_NODE_ROOT,
-        .name    = "root",
-        .params  = duplex_params_root,
-        .nparams = ARRAY_SIZE(duplex_params_root),
-    },{
-        .nid     = 1,
-        .name    = "func",
-        .params  = duplex_params_audio_func,
-        .nparams = ARRAY_SIZE(duplex_params_audio_func),
-    },{
-        .nid     = 2,
-        .name    = "dac",
-        .params  = common_params_audio_dac,
-        .nparams = ARRAY_SIZE(common_params_audio_dac),
-        .stindex = 0,
-    },{
-        .nid     = 3,
-        .name    = "out",
-        .params  = common_params_audio_lineout,
-        .nparams = ARRAY_SIZE(common_params_audio_lineout),
-        .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
-                    (AC_JACK_LINE_OUT     << AC_DEFCFG_DEVICE_SHIFT)    |
-                    (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
-                    (AC_JACK_COLOR_GREEN  << AC_DEFCFG_COLOR_SHIFT)     |
-                    0x10),
-        .pinctl  = AC_PINCTL_OUT_EN,
-        .conn    = (uint32_t[]) { 2 },
-    },{
-        .nid     = 4,
-        .name    = "adc",
-        .params  = common_params_audio_adc,
-        .nparams = ARRAY_SIZE(common_params_audio_adc),
-        .stindex = 1,
-        .conn    = (uint32_t[]) { 5 },
-    },{
-        .nid     = 5,
-        .name    = "in",
-        .params  = common_params_audio_linein,
-        .nparams = ARRAY_SIZE(common_params_audio_linein),
-        .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
-                    (AC_JACK_LINE_IN      << AC_DEFCFG_DEVICE_SHIFT)    |
-                    (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
-                    (AC_JACK_COLOR_RED    << AC_DEFCFG_COLOR_SHIFT)     |
-                    0x20),
-        .pinctl  = AC_PINCTL_IN_EN,
-    }
-};
-
-/* duplex: codec */
-static const desc_codec duplex = {
-    .name   = "duplex",
-    .iid    = QEMU_HDA_ID_DUPLEX,
-    .nodes  = duplex_nodes,
-    .nnodes = ARRAY_SIZE(duplex_nodes),
-};
-
-/* micro: root node */
-static const desc_param micro_params_root[] = {
-    {
-        .id  = AC_PAR_VENDOR_ID,
-        .val = QEMU_HDA_ID_MICRO,
-    },{
-        .id  = AC_PAR_SUBSYSTEM_ID,
-        .val = QEMU_HDA_ID_MICRO,
-    },{
-        .id  = AC_PAR_REV_ID,
-        .val = 0x00100101,
-    },{
-        .id  = AC_PAR_NODE_COUNT,
-        .val = 0x00010001,
-    },
-};
-
-/* micro: audio function */
-static const desc_param micro_params_audio_func[] = {
-    {
-        .id  = AC_PAR_FUNCTION_TYPE,
-        .val = AC_GRP_AUDIO_FUNCTION,
-    },{
-        .id  = AC_PAR_SUBSYSTEM_ID,
-        .val = QEMU_HDA_ID_MICRO,
-    },{
-        .id  = AC_PAR_NODE_COUNT,
-        .val = 0x00020004,
-    },{
-        .id  = AC_PAR_PCM,
-        .val = QEMU_HDA_PCM_FORMATS,
-    },{
-        .id  = AC_PAR_STREAM,
-        .val = AC_SUPFMT_PCM,
-    },{
-        .id  = AC_PAR_AMP_IN_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_AMP_OUT_CAP,
-        .val = QEMU_HDA_AMP_NONE,
-    },{
-        .id  = AC_PAR_GPIO_CAP,
-        .val = 0,
-    },{
-        .id  = AC_PAR_AUDIO_FG_CAP,
-        .val = 0x00000808,
-    },{
-        .id  = AC_PAR_POWER_STATE,
-        .val = 0,
-    },
-};
-
-/* micro: nodes */
-static const desc_node micro_nodes[] = {
-    {
-        .nid     = AC_NODE_ROOT,
-        .name    = "root",
-        .params  = micro_params_root,
-        .nparams = ARRAY_SIZE(micro_params_root),
-    },{
-        .nid     = 1,
-        .name    = "func",
-        .params  = micro_params_audio_func,
-        .nparams = ARRAY_SIZE(micro_params_audio_func),
-    },{
-        .nid     = 2,
-        .name    = "dac",
-        .params  = common_params_audio_dac,
-        .nparams = ARRAY_SIZE(common_params_audio_dac),
-        .stindex = 0,
-    },{
-        .nid     = 3,
-        .name    = "out",
-        .params  = common_params_audio_lineout,
-        .nparams = ARRAY_SIZE(common_params_audio_lineout),
-        .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
-                    (AC_JACK_SPEAKER      << AC_DEFCFG_DEVICE_SHIFT)    |
-                    (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
-                    (AC_JACK_COLOR_GREEN  << AC_DEFCFG_COLOR_SHIFT)     |
-                    0x10),
-        .pinctl  = AC_PINCTL_OUT_EN,
-        .conn    = (uint32_t[]) { 2 },
-    },{
-        .nid     = 4,
-        .name    = "adc",
-        .params  = common_params_audio_adc,
-        .nparams = ARRAY_SIZE(common_params_audio_adc),
-        .stindex = 1,
-        .conn    = (uint32_t[]) { 5 },
-    },{
-        .nid     = 5,
-        .name    = "in",
-        .params  = common_params_audio_linein,
-        .nparams = ARRAY_SIZE(common_params_audio_linein),
-        .config  = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) |
-                    (AC_JACK_MIC_IN       << AC_DEFCFG_DEVICE_SHIFT)    |
-                    (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) |
-                    (AC_JACK_COLOR_RED    << AC_DEFCFG_COLOR_SHIFT)     |
-                    0x20),
-        .pinctl  = AC_PINCTL_IN_EN,
-    }
-};
-
-/* micro: codec */
-static const desc_codec micro = {
-    .name   = "micro",
-    .iid    = QEMU_HDA_ID_MICRO,
-    .nodes  = micro_nodes,
-    .nnodes = ARRAY_SIZE(micro_nodes),
-};
-
-/* -------------------------------------------------------------------------- */
-
-static const char *fmt2name[] = {
-    [ AUD_FMT_U8  ] = "PCM-U8",
-    [ AUD_FMT_S8  ] = "PCM-S8",
-    [ AUD_FMT_U16 ] = "PCM-U16",
-    [ AUD_FMT_S16 ] = "PCM-S16",
-    [ AUD_FMT_U32 ] = "PCM-U32",
-    [ AUD_FMT_S32 ] = "PCM-S32",
-};
-
-typedef struct HDAAudioState HDAAudioState;
-typedef struct HDAAudioStream HDAAudioStream;
-
-struct HDAAudioStream {
-    HDAAudioState *state;
-    const desc_node *node;
-    bool output, running;
-    uint32_t stream;
-    uint32_t channel;
-    uint32_t format;
-    uint32_t gain_left, gain_right;
-    bool mute_left, mute_right;
-    struct audsettings as;
-    union {
-        SWVoiceIn *in;
-        SWVoiceOut *out;
-    } voice;
-    uint8_t buf[HDA_BUFFER_SIZE];
-    uint32_t bpos;
-};
-
-struct HDAAudioState {
-    HDACodecDevice hda;
-    const char *name;
-
-    QEMUSoundCard card;
-    const desc_codec *desc;
-    HDAAudioStream st[4];
-    bool running_compat[16];
-    bool running_real[2 * 16];
-
-    /* properties */
-    uint32_t debug;
-};
-
-static void hda_audio_input_cb(void *opaque, int avail)
-{
-    HDAAudioStream *st = opaque;
-    int recv = 0;
-    int len;
-    bool rc;
-
-    while (avail - recv >= sizeof(st->buf)) {
-        if (st->bpos != sizeof(st->buf)) {
-            len = AUD_read(st->voice.in, st->buf + st->bpos,
-                           sizeof(st->buf) - st->bpos);
-            st->bpos += len;
-            recv += len;
-            if (st->bpos != sizeof(st->buf)) {
-                break;
-            }
-        }
-        rc = hda_codec_xfer(&st->state->hda, st->stream, false,
-                            st->buf, sizeof(st->buf));
-        if (!rc) {
-            break;
-        }
-        st->bpos = 0;
-    }
-}
-
-static void hda_audio_output_cb(void *opaque, int avail)
-{
-    HDAAudioStream *st = opaque;
-    int sent = 0;
-    int len;
-    bool rc;
-
-    while (avail - sent >= sizeof(st->buf)) {
-        if (st->bpos == sizeof(st->buf)) {
-            rc = hda_codec_xfer(&st->state->hda, st->stream, true,
-                                st->buf, sizeof(st->buf));
-            if (!rc) {
-                break;
-            }
-            st->bpos = 0;
-        }
-        len = AUD_write(st->voice.out, st->buf + st->bpos,
-                        sizeof(st->buf) - st->bpos);
-        st->bpos += len;
-        sent += len;
-        if (st->bpos != sizeof(st->buf)) {
-            break;
-        }
-    }
-}
-
-static void hda_audio_set_running(HDAAudioStream *st, bool running)
-{
-    if (st->node == NULL) {
-        return;
-    }
-    if (st->running == running) {
-        return;
-    }
-    st->running = running;
-    dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name,
-           st->running ? "on" : "off", st->stream);
-    if (st->output) {
-        AUD_set_active_out(st->voice.out, st->running);
-    } else {
-        AUD_set_active_in(st->voice.in, st->running);
-    }
-}
-
-static void hda_audio_set_amp(HDAAudioStream *st)
-{
-    bool muted;
-    uint32_t left, right;
-
-    if (st->node == NULL) {
-        return;
-    }
-
-    muted = st->mute_left && st->mute_right;
-    left  = st->mute_left  ? 0 : st->gain_left;
-    right = st->mute_right ? 0 : st->gain_right;
-
-    left = left * 255 / QEMU_HDA_AMP_STEPS;
-    right = right * 255 / QEMU_HDA_AMP_STEPS;
-
-    if (st->output) {
-        AUD_set_volume_out(st->voice.out, muted, left, right);
-    } else {
-        AUD_set_volume_in(st->voice.in, muted, left, right);
-    }
-}
-
-static void hda_audio_setup(HDAAudioStream *st)
-{
-    if (st->node == NULL) {
-        return;
-    }
-
-    dprint(st->state, 1, "%s: format: %d x %s @ %d Hz\n",
-           st->node->name, st->as.nchannels,
-           fmt2name[st->as.fmt], st->as.freq);
-
-    if (st->output) {
-        st->voice.out = AUD_open_out(&st->state->card, st->voice.out,
-                                     st->node->name, st,
-                                     hda_audio_output_cb, &st->as);
-    } else {
-        st->voice.in = AUD_open_in(&st->state->card, st->voice.in,
-                                   st->node->name, st,
-                                   hda_audio_input_cb, &st->as);
-    }
-}
-
-static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data)
-{
-    HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
-    HDAAudioStream *st;
-    const desc_node *node = NULL;
-    const desc_param *param;
-    uint32_t verb, payload, response, count, shift;
-
-    if ((data & 0x70000) == 0x70000) {
-        /* 12/8 id/payload */
-        verb = (data >> 8) & 0xfff;
-        payload = data & 0x00ff;
-    } else {
-        /* 4/16 id/payload */
-        verb = (data >> 8) & 0xf00;
-        payload = data & 0xffff;
-    }
-
-    node = hda_codec_find_node(a->desc, nid);
-    if (node == NULL) {
-        goto fail;
-    }
-    dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n",
-           __FUNCTION__, nid, node->name, verb, payload);
-
-    switch (verb) {
-    /* all nodes */
-    case AC_VERB_PARAMETERS:
-        param = hda_codec_find_param(node, payload);
-        if (param == NULL) {
-            goto fail;
-        }
-        hda_codec_response(hda, true, param->val);
-        break;
-    case AC_VERB_GET_SUBSYSTEM_ID:
-        hda_codec_response(hda, true, a->desc->iid);
-        break;
-
-    /* all functions */
-    case AC_VERB_GET_CONNECT_LIST:
-        param = hda_codec_find_param(node, AC_PAR_CONNLIST_LEN);
-        count = param ? param->val : 0;
-        response = 0;
-        shift = 0;
-        while (payload < count && shift < 32) {
-            response |= node->conn[payload] << shift;
-            payload++;
-            shift += 8;
-        }
-        hda_codec_response(hda, true, response);
-        break;
-
-    /* pin widget */
-    case AC_VERB_GET_CONFIG_DEFAULT:
-        hda_codec_response(hda, true, node->config);
-        break;
-    case AC_VERB_GET_PIN_WIDGET_CONTROL:
-        hda_codec_response(hda, true, node->pinctl);
-        break;
-    case AC_VERB_SET_PIN_WIDGET_CONTROL:
-        if (node->pinctl != payload) {
-            dprint(a, 1, "unhandled pin control bit\n");
-        }
-        hda_codec_response(hda, true, 0);
-        break;
-
-    /* audio in/out widget */
-    case AC_VERB_SET_CHANNEL_STREAMID:
-        st = a->st + node->stindex;
-        if (st->node == NULL) {
-            goto fail;
-        }
-        hda_audio_set_running(st, false);
-        st->stream = (payload >> 4) & 0x0f;
-        st->channel = payload & 0x0f;
-        dprint(a, 2, "%s: stream %d, channel %d\n",
-               st->node->name, st->stream, st->channel);
-        hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]);
-        hda_codec_response(hda, true, 0);
-        break;
-    case AC_VERB_GET_CONV:
-        st = a->st + node->stindex;
-        if (st->node == NULL) {
-            goto fail;
-        }
-        response = st->stream << 4 | st->channel;
-        hda_codec_response(hda, true, response);
-        break;
-    case AC_VERB_SET_STREAM_FORMAT:
-        st = a->st + node->stindex;
-        if (st->node == NULL) {
-            goto fail;
-        }
-        st->format = payload;
-        hda_codec_parse_fmt(st->format, &st->as);
-        hda_audio_setup(st);
-        hda_codec_response(hda, true, 0);
-        break;
-    case AC_VERB_GET_STREAM_FORMAT:
-        st = a->st + node->stindex;
-        if (st->node == NULL) {
-            goto fail;
-        }
-        hda_codec_response(hda, true, st->format);
-        break;
-    case AC_VERB_GET_AMP_GAIN_MUTE:
-        st = a->st + node->stindex;
-        if (st->node == NULL) {
-            goto fail;
-        }
-        if (payload & AC_AMP_GET_LEFT) {
-            response = st->gain_left | (st->mute_left ? AC_AMP_MUTE : 0);
-        } else {
-            response = st->gain_right | (st->mute_right ? AC_AMP_MUTE : 0);
-        }
-        hda_codec_response(hda, true, response);
-        break;
-    case AC_VERB_SET_AMP_GAIN_MUTE:
-        st = a->st + node->stindex;
-        if (st->node == NULL) {
-            goto fail;
-        }
-        dprint(a, 1, "amp (%s): %s%s%s%s index %d  gain %3d %s\n",
-               st->node->name,
-               (payload & AC_AMP_SET_OUTPUT) ? "o" : "-",
-               (payload & AC_AMP_SET_INPUT)  ? "i" : "-",
-               (payload & AC_AMP_SET_LEFT)   ? "l" : "-",
-               (payload & AC_AMP_SET_RIGHT)  ? "r" : "-",
-               (payload & AC_AMP_SET_INDEX) >> AC_AMP_SET_INDEX_SHIFT,
-               (payload & AC_AMP_GAIN),
-               (payload & AC_AMP_MUTE) ? "muted" : "");
-        if (payload & AC_AMP_SET_LEFT) {
-            st->gain_left = payload & AC_AMP_GAIN;
-            st->mute_left = payload & AC_AMP_MUTE;
-        }
-        if (payload & AC_AMP_SET_RIGHT) {
-            st->gain_right = payload & AC_AMP_GAIN;
-            st->mute_right = payload & AC_AMP_MUTE;
-        }
-        hda_audio_set_amp(st);
-        hda_codec_response(hda, true, 0);
-        break;
-
-    /* not supported */
-    case AC_VERB_SET_POWER_STATE:
-    case AC_VERB_GET_POWER_STATE:
-    case AC_VERB_GET_SDI_SELECT:
-        hda_codec_response(hda, true, 0);
-        break;
-    default:
-        goto fail;
-    }
-    return;
-
-fail:
-    dprint(a, 1, "%s: not handled: nid %d (%s), verb 0x%x, payload 0x%x\n",
-           __FUNCTION__, nid, node ? node->name : "?", verb, payload);
-    hda_codec_response(hda, true, 0);
-}
-
-static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output)
-{
-    HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
-    int s;
-
-    a->running_compat[stnr] = running;
-    a->running_real[output * 16 + stnr] = running;
-    for (s = 0; s < ARRAY_SIZE(a->st); s++) {
-        if (a->st[s].node == NULL) {
-            continue;
-        }
-        if (a->st[s].output != output) {
-            continue;
-        }
-        if (a->st[s].stream != stnr) {
-            continue;
-        }
-        hda_audio_set_running(&a->st[s], running);
-    }
-}
-
-static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc)
-{
-    HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
-    HDAAudioStream *st;
-    const desc_node *node;
-    const desc_param *param;
-    uint32_t i, type;
-
-    a->desc = desc;
-    a->name = object_get_typename(OBJECT(a));
-    dprint(a, 1, "%s: cad %d\n", __FUNCTION__, a->hda.cad);
-
-    AUD_register_card("hda", &a->card);
-    for (i = 0; i < a->desc->nnodes; i++) {
-        node = a->desc->nodes + i;
-        param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP);
-        if (NULL == param)
-            continue;
-        type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
-        switch (type) {
-        case AC_WID_AUD_OUT:
-        case AC_WID_AUD_IN:
-            assert(node->stindex < ARRAY_SIZE(a->st));
-            st = a->st + node->stindex;
-            st->state = a;
-            st->node = node;
-            if (type == AC_WID_AUD_OUT) {
-                /* unmute output by default */
-                st->gain_left = QEMU_HDA_AMP_STEPS;
-                st->gain_right = QEMU_HDA_AMP_STEPS;
-                st->bpos = sizeof(st->buf);
-                st->output = true;
-            } else {
-                st->output = false;
-            }
-            st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 |
-                (1 << AC_FMT_CHAN_SHIFT);
-            hda_codec_parse_fmt(st->format, &st->as);
-            hda_audio_setup(st);
-            break;
-        }
-    }
-    return 0;
-}
-
-static int hda_audio_exit(HDACodecDevice *hda)
-{
-    HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda);
-    HDAAudioStream *st;
-    int i;
-
-    dprint(a, 1, "%s\n", __FUNCTION__);
-    for (i = 0; i < ARRAY_SIZE(a->st); i++) {
-        st = a->st + i;
-        if (st->node == NULL) {
-            continue;
-        }
-        if (st->output) {
-            AUD_close_out(&a->card, st->voice.out);
-        } else {
-            AUD_close_in(&a->card, st->voice.in);
-        }
-    }
-    AUD_remove_card(&a->card);
-    return 0;
-}
-
-static int hda_audio_post_load(void *opaque, int version)
-{
-    HDAAudioState *a = opaque;
-    HDAAudioStream *st;
-    int i;
-
-    dprint(a, 1, "%s\n", __FUNCTION__);
-    if (version == 1) {
-        /* assume running_compat[] is for output streams */
-        for (i = 0; i < ARRAY_SIZE(a->running_compat); i++)
-            a->running_real[16 + i] = a->running_compat[i];
-    }
-
-    for (i = 0; i < ARRAY_SIZE(a->st); i++) {
-        st = a->st + i;
-        if (st->node == NULL)
-            continue;
-        hda_codec_parse_fmt(st->format, &st->as);
-        hda_audio_setup(st);
-        hda_audio_set_amp(st);
-        hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]);
-    }
-    return 0;
-}
-
-static const VMStateDescription vmstate_hda_audio_stream = {
-    .name = "hda-audio-stream",
-    .version_id = 1,
-    .fields = (VMStateField []) {
-        VMSTATE_UINT32(stream, HDAAudioStream),
-        VMSTATE_UINT32(channel, HDAAudioStream),
-        VMSTATE_UINT32(format, HDAAudioStream),
-        VMSTATE_UINT32(gain_left, HDAAudioStream),
-        VMSTATE_UINT32(gain_right, HDAAudioStream),
-        VMSTATE_BOOL(mute_left, HDAAudioStream),
-        VMSTATE_BOOL(mute_right, HDAAudioStream),
-        VMSTATE_UINT32(bpos, HDAAudioStream),
-        VMSTATE_BUFFER(buf, HDAAudioStream),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_hda_audio = {
-    .name = "hda-audio",
-    .version_id = 2,
-    .post_load = hda_audio_post_load,
-    .fields = (VMStateField []) {
-        VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0,
-                             vmstate_hda_audio_stream,
-                             HDAAudioStream),
-        VMSTATE_BOOL_ARRAY(running_compat, HDAAudioState, 16),
-        VMSTATE_BOOL_ARRAY_V(running_real, HDAAudioState, 2 * 16, 2),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property hda_audio_properties[] = {
-    DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static int hda_audio_init_output(HDACodecDevice *hda)
-{
-    return hda_audio_init(hda, &output);
-}
-
-static int hda_audio_init_duplex(HDACodecDevice *hda)
-{
-    return hda_audio_init(hda, &duplex);
-}
-
-static int hda_audio_init_micro(HDACodecDevice *hda)
-{
-    return hda_audio_init(hda, &micro);
-}
-
-static void hda_audio_output_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
-
-    k->init = hda_audio_init_output;
-    k->exit = hda_audio_exit;
-    k->command = hda_audio_command;
-    k->stream = hda_audio_stream;
-    dc->desc = "HDA Audio Codec, output-only (line-out)";
-    dc->vmsd = &vmstate_hda_audio;
-    dc->props = hda_audio_properties;
-}
-
-static const TypeInfo hda_audio_output_info = {
-    .name          = "hda-output",
-    .parent        = TYPE_HDA_CODEC_DEVICE,
-    .instance_size = sizeof(HDAAudioState),
-    .class_init    = hda_audio_output_class_init,
-};
-
-static void hda_audio_duplex_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
-
-    k->init = hda_audio_init_duplex;
-    k->exit = hda_audio_exit;
-    k->command = hda_audio_command;
-    k->stream = hda_audio_stream;
-    dc->desc = "HDA Audio Codec, duplex (line-out, line-in)";
-    dc->vmsd = &vmstate_hda_audio;
-    dc->props = hda_audio_properties;
-}
-
-static const TypeInfo hda_audio_duplex_info = {
-    .name          = "hda-duplex",
-    .parent        = TYPE_HDA_CODEC_DEVICE,
-    .instance_size = sizeof(HDAAudioState),
-    .class_init    = hda_audio_duplex_class_init,
-};
-
-static void hda_audio_micro_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass);
-
-    k->init = hda_audio_init_micro;
-    k->exit = hda_audio_exit;
-    k->command = hda_audio_command;
-    k->stream = hda_audio_stream;
-    dc->desc = "HDA Audio Codec, duplex (speaker, microphone)";
-    dc->vmsd = &vmstate_hda_audio;
-    dc->props = hda_audio_properties;
-}
-
-static const TypeInfo hda_audio_micro_info = {
-    .name          = "hda-micro",
-    .parent        = TYPE_HDA_CODEC_DEVICE,
-    .instance_size = sizeof(HDAAudioState),
-    .class_init    = hda_audio_micro_class_init,
-};
-
-static void hda_audio_register_types(void)
-{
-    type_register_static(&hda_audio_output_info);
-    type_register_static(&hda_audio_duplex_info);
-    type_register_static(&hda_audio_micro_info);
-}
-
-type_init(hda_audio_register_types)
diff --git a/hw/heathrow_pic.c b/hw/heathrow_pic.c
deleted file mode 100644 (file)
index beb9661..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Heathrow PIC support (OldWorld PowerMac)
- *
- * Copyright (c) 2005-2007 Fabrice Bellard
- * Copyright (c) 2007 Jocelyn Mayer
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/ppc/mac.h"
-
-/* debug PIC */
-//#define DEBUG_PIC
-
-#ifdef DEBUG_PIC
-#define PIC_DPRINTF(fmt, ...)                                   \
-    do { printf("PIC: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define PIC_DPRINTF(fmt, ...)
-#endif
-
-typedef struct HeathrowPIC {
-    uint32_t events;
-    uint32_t mask;
-    uint32_t levels;
-    uint32_t level_triggered;
-} HeathrowPIC;
-
-typedef struct HeathrowPICS {
-    MemoryRegion mem;
-    HeathrowPIC pics[2];
-    qemu_irq *irqs;
-} HeathrowPICS;
-
-static inline int check_irq(HeathrowPIC *pic)
-{
-    return (pic->events | (pic->levels & pic->level_triggered)) & pic->mask;
-}
-
-/* update the CPU irq state */
-static void heathrow_pic_update(HeathrowPICS *s)
-{
-    if (check_irq(&s->pics[0]) || check_irq(&s->pics[1])) {
-        qemu_irq_raise(s->irqs[0]);
-    } else {
-        qemu_irq_lower(s->irqs[0]);
-    }
-}
-
-static void pic_write(void *opaque, hwaddr addr,
-                      uint64_t value, unsigned size)
-{
-    HeathrowPICS *s = opaque;
-    HeathrowPIC *pic;
-    unsigned int n;
-
-    n = ((addr & 0xfff) - 0x10) >> 4;
-    PIC_DPRINTF("writel: " TARGET_FMT_plx " %u: %08x\n", addr, n, value);
-    if (n >= 2)
-        return;
-    pic = &s->pics[n];
-    switch(addr & 0xf) {
-    case 0x04:
-        pic->mask = value;
-        heathrow_pic_update(s);
-        break;
-    case 0x08:
-        /* do not reset level triggered IRQs */
-        value &= ~pic->level_triggered;
-        pic->events &= ~value;
-        heathrow_pic_update(s);
-        break;
-    default:
-        break;
-    }
-}
-
-static uint64_t pic_read(void *opaque, hwaddr addr,
-                         unsigned size)
-{
-    HeathrowPICS *s = opaque;
-    HeathrowPIC *pic;
-    unsigned int n;
-    uint32_t value;
-
-    n = ((addr & 0xfff) - 0x10) >> 4;
-    if (n >= 2) {
-        value = 0;
-    } else {
-        pic = &s->pics[n];
-        switch(addr & 0xf) {
-        case 0x0:
-            value = pic->events;
-            break;
-        case 0x4:
-            value = pic->mask;
-            break;
-        case 0xc:
-            value = pic->levels;
-            break;
-        default:
-            value = 0;
-            break;
-        }
-    }
-    PIC_DPRINTF("readl: " TARGET_FMT_plx " %u: %08x\n", addr, n, value);
-    return value;
-}
-
-static const MemoryRegionOps heathrow_pic_ops = {
-    .read = pic_read,
-    .write = pic_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void heathrow_pic_set_irq(void *opaque, int num, int level)
-{
-    HeathrowPICS *s = opaque;
-    HeathrowPIC *pic;
-    unsigned int irq_bit;
-
-#if defined(DEBUG)
-    {
-        static int last_level[64];
-        if (last_level[num] != level) {
-            PIC_DPRINTF("set_irq: num=0x%02x level=%d\n", num, level);
-            last_level[num] = level;
-        }
-    }
-#endif
-    pic = &s->pics[1 - (num >> 5)];
-    irq_bit = 1 << (num & 0x1f);
-    if (level) {
-        pic->events |= irq_bit & ~pic->level_triggered;
-        pic->levels |= irq_bit;
-    } else {
-        pic->levels &= ~irq_bit;
-    }
-    heathrow_pic_update(s);
-}
-
-static const VMStateDescription vmstate_heathrow_pic_one = {
-    .name = "heathrow_pic_one",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT32(events, HeathrowPIC),
-        VMSTATE_UINT32(mask, HeathrowPIC),
-        VMSTATE_UINT32(levels, HeathrowPIC),
-        VMSTATE_UINT32(level_triggered, HeathrowPIC),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_heathrow_pic = {
-    .name = "heathrow_pic",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_STRUCT_ARRAY(pics, HeathrowPICS, 2, 1,
-                             vmstate_heathrow_pic_one, HeathrowPIC),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void heathrow_pic_reset_one(HeathrowPIC *s)
-{
-    memset(s, '\0', sizeof(HeathrowPIC));
-}
-
-static void heathrow_pic_reset(void *opaque)
-{
-    HeathrowPICS *s = opaque;
-
-    heathrow_pic_reset_one(&s->pics[0]);
-    heathrow_pic_reset_one(&s->pics[1]);
-
-    s->pics[0].level_triggered = 0;
-    s->pics[1].level_triggered = 0x1ff00000;
-}
-
-qemu_irq *heathrow_pic_init(MemoryRegion **pmem,
-                            int nb_cpus, qemu_irq **irqs)
-{
-    HeathrowPICS *s;
-
-    s = g_malloc0(sizeof(HeathrowPICS));
-    /* only 1 CPU */
-    s->irqs = irqs[0];
-    memory_region_init_io(&s->mem, &heathrow_pic_ops, s,
-                          "heathrow-pic", 0x1000);
-    *pmem = &s->mem;
-
-    vmstate_register(NULL, -1, &vmstate_heathrow_pic, s);
-    qemu_register_reset(heathrow_pic_reset, s);
-    return qemu_allocate_irqs(heathrow_pic_set_irq, s, 64);
-}
diff --git a/hw/hid.c b/hw/hid.c
deleted file mode 100644 (file)
index 5fbde98..0000000
--- a/hw/hid.c
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
- * QEMU HID devices
- *
- * Copyright (c) 2005 Fabrice Bellard
- * Copyright (c) 2007 OpenMoko, Inc.  (andrew@openedhand.com)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "ui/console.h"
-#include "qemu/timer.h"
-#include "hw/input/hid.h"
-
-#define HID_USAGE_ERROR_ROLLOVER        0x01
-#define HID_USAGE_POSTFAIL              0x02
-#define HID_USAGE_ERROR_UNDEFINED       0x03
-
-/* Indices are QEMU keycodes, values are from HID Usage Table.  Indices
- * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d.  */
-static const uint8_t hid_usage_keys[0x100] = {
-    0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
-    0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
-    0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
-    0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
-    0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
-    0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19,
-    0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
-    0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
-    0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
-    0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
-    0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44,
-    0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
-    0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65,
-
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
-    0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a,
-    0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
-    0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-bool hid_has_events(HIDState *hs)
-{
-    return hs->n > 0 || hs->idle_pending;
-}
-
-static void hid_idle_timer(void *opaque)
-{
-    HIDState *hs = opaque;
-
-    hs->idle_pending = true;
-    hs->event(hs);
-}
-
-static void hid_del_idle_timer(HIDState *hs)
-{
-    if (hs->idle_timer) {
-        qemu_del_timer(hs->idle_timer);
-        qemu_free_timer(hs->idle_timer);
-        hs->idle_timer = NULL;
-    }
-}
-
-void hid_set_next_idle(HIDState *hs)
-{
-    if (hs->idle) {
-        uint64_t expire_time = qemu_get_clock_ns(vm_clock) +
-                               get_ticks_per_sec() * hs->idle * 4 / 1000;
-        if (!hs->idle_timer) {
-            hs->idle_timer = qemu_new_timer_ns(vm_clock, hid_idle_timer, hs);
-        }
-        qemu_mod_timer_ns(hs->idle_timer, expire_time);
-    } else {
-        hid_del_idle_timer(hs);
-    }
-}
-
-static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons)
-{
-    e->xdx = e->ydy = e->dz = 0;
-    e->buttons_state = buttons;
-}
-
-static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel,
-                                      int x1, int y1, int z1) {
-    if (xyrel) {
-        e->xdx += x1;
-        e->ydy += y1;
-    } else {
-        e->xdx = x1;
-        e->ydy = y1;
-        /* Windows drivers do not like the 0/0 position and ignore such
-         * events. */
-        if (!(x1 | y1)) {
-            e->xdx = 1;
-        }
-    }
-    e->dz += z1;
-}
-
-static void hid_pointer_event(void *opaque,
-                              int x1, int y1, int z1, int buttons_state)
-{
-    HIDState *hs = opaque;
-    unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK;
-    unsigned previous_slot = (use_slot - 1) & QUEUE_MASK;
-
-    /* We combine events where feasible to keep the queue small.  We shouldn't
-     * combine anything with the first event of a particular button state, as
-     * that would change the location of the button state change.  When the
-     * queue is empty, a second event is needed because we don't know if
-     * the first event changed the button state.  */
-    if (hs->n == QUEUE_LENGTH) {
-        /* Queue full.  Discard old button state, combine motion normally.  */
-        hs->ptr.queue[use_slot].buttons_state = buttons_state;
-    } else if (hs->n < 2 ||
-               hs->ptr.queue[use_slot].buttons_state != buttons_state ||
-               hs->ptr.queue[previous_slot].buttons_state !=
-               hs->ptr.queue[use_slot].buttons_state) {
-        /* Cannot or should not combine, so add an empty item to the queue.  */
-        QUEUE_INCR(use_slot);
-        hs->n++;
-        hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state);
-    }
-    hid_pointer_event_combine(&hs->ptr.queue[use_slot],
-                              hs->kind == HID_MOUSE,
-                              x1, y1, z1);
-    hs->event(hs);
-}
-
-static void hid_keyboard_event(void *opaque, int keycode)
-{
-    HIDState *hs = opaque;
-    int slot;
-
-    if (hs->n == QUEUE_LENGTH) {
-        fprintf(stderr, "usb-kbd: warning: key event queue full\n");
-        return;
-    }
-    slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
-    hs->kbd.keycodes[slot] = keycode;
-    hs->event(hs);
-}
-
-static void hid_keyboard_process_keycode(HIDState *hs)
-{
-    uint8_t hid_code, key;
-    int i, keycode, slot;
-
-    if (hs->n == 0) {
-        return;
-    }
-    slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
-    keycode = hs->kbd.keycodes[slot];
-
-    key = keycode & 0x7f;
-    hid_code = hid_usage_keys[key | ((hs->kbd.modifiers >> 1) & (1 << 7))];
-    hs->kbd.modifiers &= ~(1 << 8);
-
-    switch (hid_code) {
-    case 0x00:
-        return;
-
-    case 0xe0:
-        if (hs->kbd.modifiers & (1 << 9)) {
-            hs->kbd.modifiers ^= 3 << 8;
-            return;
-        }
-    case 0xe1 ... 0xe7:
-        if (keycode & (1 << 7)) {
-            hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f));
-            return;
-        }
-    case 0xe8 ... 0xef:
-        hs->kbd.modifiers |= 1 << (hid_code & 0x0f);
-        return;
-    }
-
-    if (keycode & (1 << 7)) {
-        for (i = hs->kbd.keys - 1; i >= 0; i--) {
-            if (hs->kbd.key[i] == hid_code) {
-                hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys];
-                hs->kbd.key[hs->kbd.keys] = 0x00;
-                break;
-            }
-        }
-        if (i < 0) {
-            return;
-        }
-    } else {
-        for (i = hs->kbd.keys - 1; i >= 0; i--) {
-            if (hs->kbd.key[i] == hid_code) {
-                break;
-            }
-        }
-        if (i < 0) {
-            if (hs->kbd.keys < sizeof(hs->kbd.key)) {
-                hs->kbd.key[hs->kbd.keys++] = hid_code;
-            }
-        } else {
-            return;
-        }
-    }
-}
-
-static inline int int_clamp(int val, int vmin, int vmax)
-{
-    if (val < vmin) {
-        return vmin;
-    } else if (val > vmax) {
-        return vmax;
-    } else {
-        return val;
-    }
-}
-
-void hid_pointer_activate(HIDState *hs)
-{
-    if (!hs->ptr.mouse_grabbed) {
-        qemu_activate_mouse_event_handler(hs->ptr.eh_entry);
-        hs->ptr.mouse_grabbed = 1;
-    }
-}
-
-int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
-{
-    int dx, dy, dz, b, l;
-    int index;
-    HIDPointerEvent *e;
-
-    hs->idle_pending = false;
-
-    hid_pointer_activate(hs);
-
-    /* When the buffer is empty, return the last event.  Relative
-       movements will all be zero.  */
-    index = (hs->n ? hs->head : hs->head - 1);
-    e = &hs->ptr.queue[index & QUEUE_MASK];
-
-    if (hs->kind == HID_MOUSE) {
-        dx = int_clamp(e->xdx, -127, 127);
-        dy = int_clamp(e->ydy, -127, 127);
-        e->xdx -= dx;
-        e->ydy -= dy;
-    } else {
-        dx = e->xdx;
-        dy = e->ydy;
-    }
-    dz = int_clamp(e->dz, -127, 127);
-    e->dz -= dz;
-
-    b = 0;
-    if (e->buttons_state & MOUSE_EVENT_LBUTTON) {
-        b |= 0x01;
-    }
-    if (e->buttons_state & MOUSE_EVENT_RBUTTON) {
-        b |= 0x02;
-    }
-    if (e->buttons_state & MOUSE_EVENT_MBUTTON) {
-        b |= 0x04;
-    }
-
-    if (hs->n &&
-        !e->dz &&
-        (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
-        /* that deals with this event */
-        QUEUE_INCR(hs->head);
-        hs->n--;
-    }
-
-    /* Appears we have to invert the wheel direction */
-    dz = 0 - dz;
-    l = 0;
-    switch (hs->kind) {
-    case HID_MOUSE:
-        if (len > l) {
-            buf[l++] = b;
-        }
-        if (len > l) {
-            buf[l++] = dx;
-        }
-        if (len > l) {
-            buf[l++] = dy;
-        }
-        if (len > l) {
-            buf[l++] = dz;
-        }
-        break;
-
-    case HID_TABLET:
-        if (len > l) {
-            buf[l++] = b;
-        }
-        if (len > l) {
-            buf[l++] = dx & 0xff;
-        }
-        if (len > l) {
-            buf[l++] = dx >> 8;
-        }
-        if (len > l) {
-            buf[l++] = dy & 0xff;
-        }
-        if (len > l) {
-            buf[l++] = dy >> 8;
-        }
-        if (len > l) {
-            buf[l++] = dz;
-        }
-        break;
-
-    default:
-        abort();
-    }
-
-    return l;
-}
-
-int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len)
-{
-    hs->idle_pending = false;
-
-    if (len < 2) {
-        return 0;
-    }
-
-    hid_keyboard_process_keycode(hs);
-
-    buf[0] = hs->kbd.modifiers & 0xff;
-    buf[1] = 0;
-    if (hs->kbd.keys > 6) {
-        memset(buf + 2, HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2);
-    } else {
-        memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2);
-    }
-
-    return MIN(8, len);
-}
-
-int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len)
-{
-    if (len > 0) {
-        int ledstate = 0;
-        /* 0x01: Num Lock LED
-         * 0x02: Caps Lock LED
-         * 0x04: Scroll Lock LED
-         * 0x08: Compose LED
-         * 0x10: Kana LED */
-        hs->kbd.leds = buf[0];
-        if (hs->kbd.leds & 0x04) {
-            ledstate |= QEMU_SCROLL_LOCK_LED;
-        }
-        if (hs->kbd.leds & 0x01) {
-            ledstate |= QEMU_NUM_LOCK_LED;
-        }
-        if (hs->kbd.leds & 0x02) {
-            ledstate |= QEMU_CAPS_LOCK_LED;
-        }
-        kbd_put_ledstate(ledstate);
-    }
-    return 0;
-}
-
-void hid_reset(HIDState *hs)
-{
-    switch (hs->kind) {
-    case HID_KEYBOARD:
-        memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes));
-        memset(hs->kbd.key, 0, sizeof(hs->kbd.key));
-        hs->kbd.keys = 0;
-        break;
-    case HID_MOUSE:
-    case HID_TABLET:
-        memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue));
-        break;
-    }
-    hs->head = 0;
-    hs->n = 0;
-    hs->protocol = 1;
-    hs->idle = 0;
-    hs->idle_pending = false;
-    hid_del_idle_timer(hs);
-}
-
-void hid_free(HIDState *hs)
-{
-    switch (hs->kind) {
-    case HID_KEYBOARD:
-        qemu_remove_kbd_event_handler();
-        break;
-    case HID_MOUSE:
-    case HID_TABLET:
-        qemu_remove_mouse_event_handler(hs->ptr.eh_entry);
-        break;
-    }
-    hid_del_idle_timer(hs);
-}
-
-void hid_init(HIDState *hs, int kind, HIDEventFunc event)
-{
-    hs->kind = kind;
-    hs->event = event;
-
-    if (hs->kind == HID_KEYBOARD) {
-        qemu_add_kbd_event_handler(hid_keyboard_event, hs);
-    } else if (hs->kind == HID_MOUSE) {
-        hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
-                                                        0, "QEMU HID Mouse");
-    } else if (hs->kind == HID_TABLET) {
-        hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
-                                                        1, "QEMU HID Tablet");
-    }
-}
-
-static int hid_post_load(void *opaque, int version_id)
-{
-    HIDState *s = opaque;
-
-    hid_set_next_idle(s);
-    return 0;
-}
-
-static const VMStateDescription vmstate_hid_ptr_queue = {
-    .name = "HIDPointerEventQueue",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_INT32(xdx, HIDPointerEvent),
-        VMSTATE_INT32(ydy, HIDPointerEvent),
-        VMSTATE_INT32(dz, HIDPointerEvent),
-        VMSTATE_INT32(buttons_state, HIDPointerEvent),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-const VMStateDescription vmstate_hid_ptr_device = {
-    .name = "HIDPointerDevice",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .post_load = hid_post_load,
-    .fields = (VMStateField[]) {
-        VMSTATE_STRUCT_ARRAY(ptr.queue, HIDState, QUEUE_LENGTH, 0,
-                             vmstate_hid_ptr_queue, HIDPointerEvent),
-        VMSTATE_UINT32(head, HIDState),
-        VMSTATE_UINT32(n, HIDState),
-        VMSTATE_INT32(protocol, HIDState),
-        VMSTATE_UINT8(idle, HIDState),
-        VMSTATE_END_OF_LIST(),
-    }
-};
-
-const VMStateDescription vmstate_hid_keyboard_device = {
-    .name = "HIDKeyboardDevice",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .post_load = hid_post_load,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32_ARRAY(kbd.keycodes, HIDState, QUEUE_LENGTH),
-        VMSTATE_UINT32(head, HIDState),
-        VMSTATE_UINT32(n, HIDState),
-        VMSTATE_UINT16(kbd.modifiers, HIDState),
-        VMSTATE_UINT8(kbd.leds, HIDState),
-        VMSTATE_UINT8_ARRAY(kbd.key, HIDState, 16),
-        VMSTATE_INT32(kbd.keys, HIDState),
-        VMSTATE_INT32(protocol, HIDState),
-        VMSTATE_UINT8(idle, HIDState),
-        VMSTATE_END_OF_LIST(),
-    }
-};
diff --git a/hw/hpet.c b/hw/hpet.c
deleted file mode 100644 (file)
index 95dd01d..0000000
--- a/hw/hpet.c
+++ /dev/null
@@ -1,760 +0,0 @@
-/*
- *  High Precisition Event Timer emulation
- *
- *  Copyright (c) 2007 Alexander Graf
- *  Copyright (c) 2008 IBM Corporation
- *
- *  Authors: Beth Kon <bkon@us.ibm.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- * *****************************************************************
- *
- * This driver attempts to emulate an HPET device in software.
- */
-
-#include "hw/hw.h"
-#include "hw/i386/pc.h"
-#include "ui/console.h"
-#include "qemu/timer.h"
-#include "hw/timer/hpet.h"
-#include "hw/sysbus.h"
-#include "hw/timer/mc146818rtc.h"
-#include "hw/timer/i8254.h"
-
-//#define HPET_DEBUG
-#ifdef HPET_DEBUG
-#define DPRINTF printf
-#else
-#define DPRINTF(...)
-#endif
-
-#define HPET_MSI_SUPPORT        0
-
-struct HPETState;
-typedef struct HPETTimer {  /* timers */
-    uint8_t tn;             /*timer number*/
-    QEMUTimer *qemu_timer;
-    struct HPETState *state;
-    /* Memory-mapped, software visible timer registers */
-    uint64_t config;        /* configuration/cap */
-    uint64_t cmp;           /* comparator */
-    uint64_t fsb;           /* FSB route */
-    /* Hidden register state */
-    uint64_t period;        /* Last value written to comparator */
-    uint8_t wrap_flag;      /* timer pop will indicate wrap for one-shot 32-bit
-                             * mode. Next pop will be actual timer expiration.
-                             */
-} HPETTimer;
-
-typedef struct HPETState {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    uint64_t hpet_offset;
-    qemu_irq irqs[HPET_NUM_IRQ_ROUTES];
-    uint32_t flags;
-    uint8_t rtc_irq_level;
-    qemu_irq pit_enabled;
-    uint8_t num_timers;
-    HPETTimer timer[HPET_MAX_TIMERS];
-
-    /* Memory-mapped, software visible registers */
-    uint64_t capability;        /* capabilities */
-    uint64_t config;            /* configuration */
-    uint64_t isr;               /* interrupt status reg */
-    uint64_t hpet_counter;      /* main counter */
-    uint8_t  hpet_id;           /* instance id */
-} HPETState;
-
-static uint32_t hpet_in_legacy_mode(HPETState *s)
-{
-    return s->config & HPET_CFG_LEGACY;
-}
-
-static uint32_t timer_int_route(struct HPETTimer *timer)
-{
-    return (timer->config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT;
-}
-
-static uint32_t timer_fsb_route(HPETTimer *t)
-{
-    return t->config & HPET_TN_FSB_ENABLE;
-}
-
-static uint32_t hpet_enabled(HPETState *s)
-{
-    return s->config & HPET_CFG_ENABLE;
-}
-
-static uint32_t timer_is_periodic(HPETTimer *t)
-{
-    return t->config & HPET_TN_PERIODIC;
-}
-
-static uint32_t timer_enabled(HPETTimer *t)
-{
-    return t->config & HPET_TN_ENABLE;
-}
-
-static uint32_t hpet_time_after(uint64_t a, uint64_t b)
-{
-    return ((int32_t)(b) - (int32_t)(a) < 0);
-}
-
-static uint32_t hpet_time_after64(uint64_t a, uint64_t b)
-{
-    return ((int64_t)(b) - (int64_t)(a) < 0);
-}
-
-static uint64_t ticks_to_ns(uint64_t value)
-{
-    return (muldiv64(value, HPET_CLK_PERIOD, FS_PER_NS));
-}
-
-static uint64_t ns_to_ticks(uint64_t value)
-{
-    return (muldiv64(value, FS_PER_NS, HPET_CLK_PERIOD));
-}
-
-static uint64_t hpet_fixup_reg(uint64_t new, uint64_t old, uint64_t mask)
-{
-    new &= mask;
-    new |= old & ~mask;
-    return new;
-}
-
-static int activating_bit(uint64_t old, uint64_t new, uint64_t mask)
-{
-    return (!(old & mask) && (new & mask));
-}
-
-static int deactivating_bit(uint64_t old, uint64_t new, uint64_t mask)
-{
-    return ((old & mask) && !(new & mask));
-}
-
-static uint64_t hpet_get_ticks(HPETState *s)
-{
-    return ns_to_ticks(qemu_get_clock_ns(vm_clock) + s->hpet_offset);
-}
-
-/*
- * calculate diff between comparator value and current ticks
- */
-static inline uint64_t hpet_calculate_diff(HPETTimer *t, uint64_t current)
-{
-
-    if (t->config & HPET_TN_32BIT) {
-        uint32_t diff, cmp;
-
-        cmp = (uint32_t)t->cmp;
-        diff = cmp - (uint32_t)current;
-        diff = (int32_t)diff > 0 ? diff : (uint32_t)1;
-        return (uint64_t)diff;
-    } else {
-        uint64_t diff, cmp;
-
-        cmp = t->cmp;
-        diff = cmp - current;
-        diff = (int64_t)diff > 0 ? diff : (uint64_t)1;
-        return diff;
-    }
-}
-
-static void update_irq(struct HPETTimer *timer, int set)
-{
-    uint64_t mask;
-    HPETState *s;
-    int route;
-
-    if (timer->tn <= 1 && hpet_in_legacy_mode(timer->state)) {
-        /* if LegacyReplacementRoute bit is set, HPET specification requires
-         * timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC,
-         * timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC.
-         */
-        route = (timer->tn == 0) ? 0 : RTC_ISA_IRQ;
-    } else {
-        route = timer_int_route(timer);
-    }
-    s = timer->state;
-    mask = 1 << timer->tn;
-    if (!set || !timer_enabled(timer) || !hpet_enabled(timer->state)) {
-        s->isr &= ~mask;
-        if (!timer_fsb_route(timer)) {
-            qemu_irq_lower(s->irqs[route]);
-        }
-    } else if (timer_fsb_route(timer)) {
-        stl_le_phys(timer->fsb >> 32, timer->fsb & 0xffffffff);
-    } else if (timer->config & HPET_TN_TYPE_LEVEL) {
-        s->isr |= mask;
-        qemu_irq_raise(s->irqs[route]);
-    } else {
-        s->isr &= ~mask;
-        qemu_irq_pulse(s->irqs[route]);
-    }
-}
-
-static void hpet_pre_save(void *opaque)
-{
-    HPETState *s = opaque;
-
-    /* save current counter value */
-    s->hpet_counter = hpet_get_ticks(s);
-}
-
-static int hpet_pre_load(void *opaque)
-{
-    HPETState *s = opaque;
-
-    /* version 1 only supports 3, later versions will load the actual value */
-    s->num_timers = HPET_MIN_TIMERS;
-    return 0;
-}
-
-static int hpet_post_load(void *opaque, int version_id)
-{
-    HPETState *s = opaque;
-
-    /* Recalculate the offset between the main counter and guest time */
-    s->hpet_offset = ticks_to_ns(s->hpet_counter) - qemu_get_clock_ns(vm_clock);
-
-    /* Push number of timers into capability returned via HPET_ID */
-    s->capability &= ~HPET_ID_NUM_TIM_MASK;
-    s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
-    hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
-
-    /* Derive HPET_MSI_SUPPORT from the capability of the first timer. */
-    s->flags &= ~(1 << HPET_MSI_SUPPORT);
-    if (s->timer[0].config & HPET_TN_FSB_CAP) {
-        s->flags |= 1 << HPET_MSI_SUPPORT;
-    }
-    return 0;
-}
-
-static bool hpet_rtc_irq_level_needed(void *opaque)
-{
-    HPETState *s = opaque;
-
-    return s->rtc_irq_level != 0;
-}
-
-static const VMStateDescription vmstate_hpet_rtc_irq_level = {
-    .name = "hpet/rtc_irq_level",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT8(rtc_irq_level, HPETState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_hpet_timer = {
-    .name = "hpet_timer",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT8(tn, HPETTimer),
-        VMSTATE_UINT64(config, HPETTimer),
-        VMSTATE_UINT64(cmp, HPETTimer),
-        VMSTATE_UINT64(fsb, HPETTimer),
-        VMSTATE_UINT64(period, HPETTimer),
-        VMSTATE_UINT8(wrap_flag, HPETTimer),
-        VMSTATE_TIMER(qemu_timer, HPETTimer),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_hpet = {
-    .name = "hpet",
-    .version_id = 2,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .pre_save = hpet_pre_save,
-    .pre_load = hpet_pre_load,
-    .post_load = hpet_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT64(config, HPETState),
-        VMSTATE_UINT64(isr, HPETState),
-        VMSTATE_UINT64(hpet_counter, HPETState),
-        VMSTATE_UINT8_V(num_timers, HPETState, 2),
-        VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0,
-                                    vmstate_hpet_timer, HPETTimer),
-        VMSTATE_END_OF_LIST()
-    },
-    .subsections = (VMStateSubsection[]) {
-        {
-            .vmsd = &vmstate_hpet_rtc_irq_level,
-            .needed = hpet_rtc_irq_level_needed,
-        }, {
-            /* empty */
-        }
-    }
-};
-
-/*
- * timer expiration callback
- */
-static void hpet_timer(void *opaque)
-{
-    HPETTimer *t = opaque;
-    uint64_t diff;
-
-    uint64_t period = t->period;
-    uint64_t cur_tick = hpet_get_ticks(t->state);
-
-    if (timer_is_periodic(t) && period != 0) {
-        if (t->config & HPET_TN_32BIT) {
-            while (hpet_time_after(cur_tick, t->cmp)) {
-                t->cmp = (uint32_t)(t->cmp + t->period);
-            }
-        } else {
-            while (hpet_time_after64(cur_tick, t->cmp)) {
-                t->cmp += period;
-            }
-        }
-        diff = hpet_calculate_diff(t, cur_tick);
-        qemu_mod_timer(t->qemu_timer,
-                       qemu_get_clock_ns(vm_clock) + (int64_t)ticks_to_ns(diff));
-    } else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) {
-        if (t->wrap_flag) {
-            diff = hpet_calculate_diff(t, cur_tick);
-            qemu_mod_timer(t->qemu_timer, qemu_get_clock_ns(vm_clock) +
-                           (int64_t)ticks_to_ns(diff));
-            t->wrap_flag = 0;
-        }
-    }
-    update_irq(t, 1);
-}
-
-static void hpet_set_timer(HPETTimer *t)
-{
-    uint64_t diff;
-    uint32_t wrap_diff;  /* how many ticks until we wrap? */
-    uint64_t cur_tick = hpet_get_ticks(t->state);
-
-    /* whenever new timer is being set up, make sure wrap_flag is 0 */
-    t->wrap_flag = 0;
-    diff = hpet_calculate_diff(t, cur_tick);
-
-    /* hpet spec says in one-shot 32-bit mode, generate an interrupt when
-     * counter wraps in addition to an interrupt with comparator match.
-     */
-    if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) {
-        wrap_diff = 0xffffffff - (uint32_t)cur_tick;
-        if (wrap_diff < (uint32_t)diff) {
-            diff = wrap_diff;
-            t->wrap_flag = 1;
-        }
-    }
-    qemu_mod_timer(t->qemu_timer,
-                   qemu_get_clock_ns(vm_clock) + (int64_t)ticks_to_ns(diff));
-}
-
-static void hpet_del_timer(HPETTimer *t)
-{
-    qemu_del_timer(t->qemu_timer);
-    update_irq(t, 0);
-}
-
-#ifdef HPET_DEBUG
-static uint32_t hpet_ram_readb(void *opaque, hwaddr addr)
-{
-    printf("qemu: hpet_read b at %" PRIx64 "\n", addr);
-    return 0;
-}
-
-static uint32_t hpet_ram_readw(void *opaque, hwaddr addr)
-{
-    printf("qemu: hpet_read w at %" PRIx64 "\n", addr);
-    return 0;
-}
-#endif
-
-static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
-                              unsigned size)
-{
-    HPETState *s = opaque;
-    uint64_t cur_tick, index;
-
-    DPRINTF("qemu: Enter hpet_ram_readl at %" PRIx64 "\n", addr);
-    index = addr;
-    /*address range of all TN regs*/
-    if (index >= 0x100 && index <= 0x3ff) {
-        uint8_t timer_id = (addr - 0x100) / 0x20;
-        HPETTimer *timer = &s->timer[timer_id];
-
-        if (timer_id > s->num_timers) {
-            DPRINTF("qemu: timer id out of range\n");
-            return 0;
-        }
-
-        switch ((addr - 0x100) % 0x20) {
-        case HPET_TN_CFG:
-            return timer->config;
-        case HPET_TN_CFG + 4: // Interrupt capabilities
-            return timer->config >> 32;
-        case HPET_TN_CMP: // comparator register
-            return timer->cmp;
-        case HPET_TN_CMP + 4:
-            return timer->cmp >> 32;
-        case HPET_TN_ROUTE:
-            return timer->fsb;
-        case HPET_TN_ROUTE + 4:
-            return timer->fsb >> 32;
-        default:
-            DPRINTF("qemu: invalid hpet_ram_readl\n");
-            break;
-        }
-    } else {
-        switch (index) {
-        case HPET_ID:
-            return s->capability;
-        case HPET_PERIOD:
-            return s->capability >> 32;
-        case HPET_CFG:
-            return s->config;
-        case HPET_CFG + 4:
-            DPRINTF("qemu: invalid HPET_CFG + 4 hpet_ram_readl\n");
-            return 0;
-        case HPET_COUNTER:
-            if (hpet_enabled(s)) {
-                cur_tick = hpet_get_ticks(s);
-            } else {
-                cur_tick = s->hpet_counter;
-            }
-            DPRINTF("qemu: reading counter  = %" PRIx64 "\n", cur_tick);
-            return cur_tick;
-        case HPET_COUNTER + 4:
-            if (hpet_enabled(s)) {
-                cur_tick = hpet_get_ticks(s);
-            } else {
-                cur_tick = s->hpet_counter;
-            }
-            DPRINTF("qemu: reading counter + 4  = %" PRIx64 "\n", cur_tick);
-            return cur_tick >> 32;
-        case HPET_STATUS:
-            return s->isr;
-        default:
-            DPRINTF("qemu: invalid hpet_ram_readl\n");
-            break;
-        }
-    }
-    return 0;
-}
-
-static void hpet_ram_write(void *opaque, hwaddr addr,
-                           uint64_t value, unsigned size)
-{
-    int i;
-    HPETState *s = opaque;
-    uint64_t old_val, new_val, val, index;
-
-    DPRINTF("qemu: Enter hpet_ram_writel at %" PRIx64 " = %#x\n", addr, value);
-    index = addr;
-    old_val = hpet_ram_read(opaque, addr, 4);
-    new_val = value;
-
-    /*address range of all TN regs*/
-    if (index >= 0x100 && index <= 0x3ff) {
-        uint8_t timer_id = (addr - 0x100) / 0x20;
-        HPETTimer *timer = &s->timer[timer_id];
-
-        DPRINTF("qemu: hpet_ram_writel timer_id = %#x\n", timer_id);
-        if (timer_id > s->num_timers) {
-            DPRINTF("qemu: timer id out of range\n");
-            return;
-        }
-        switch ((addr - 0x100) % 0x20) {
-        case HPET_TN_CFG:
-            DPRINTF("qemu: hpet_ram_writel HPET_TN_CFG\n");
-            if (activating_bit(old_val, new_val, HPET_TN_FSB_ENABLE)) {
-                update_irq(timer, 0);
-            }
-            val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK);
-            timer->config = (timer->config & 0xffffffff00000000ULL) | val;
-            if (new_val & HPET_TN_32BIT) {
-                timer->cmp = (uint32_t)timer->cmp;
-                timer->period = (uint32_t)timer->period;
-            }
-            if (activating_bit(old_val, new_val, HPET_TN_ENABLE)) {
-                hpet_set_timer(timer);
-            } else if (deactivating_bit(old_val, new_val, HPET_TN_ENABLE)) {
-                hpet_del_timer(timer);
-            }
-            break;
-        case HPET_TN_CFG + 4: // Interrupt capabilities
-            DPRINTF("qemu: invalid HPET_TN_CFG+4 write\n");
-            break;
-        case HPET_TN_CMP: // comparator register
-            DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP\n");
-            if (timer->config & HPET_TN_32BIT) {
-                new_val = (uint32_t)new_val;
-            }
-            if (!timer_is_periodic(timer)
-                || (timer->config & HPET_TN_SETVAL)) {
-                timer->cmp = (timer->cmp & 0xffffffff00000000ULL) | new_val;
-            }
-            if (timer_is_periodic(timer)) {
-                /*
-                 * FIXME: Clamp period to reasonable min value?
-                 * Clamp period to reasonable max value
-                 */
-                new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1;
-                timer->period =
-                    (timer->period & 0xffffffff00000000ULL) | new_val;
-            }
-            timer->config &= ~HPET_TN_SETVAL;
-            if (hpet_enabled(s)) {
-                hpet_set_timer(timer);
-            }
-            break;
-        case HPET_TN_CMP + 4: // comparator register high order
-            DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP + 4\n");
-            if (!timer_is_periodic(timer)
-                || (timer->config & HPET_TN_SETVAL)) {
-                timer->cmp = (timer->cmp & 0xffffffffULL) | new_val << 32;
-            } else {
-                /*
-                 * FIXME: Clamp period to reasonable min value?
-                 * Clamp period to reasonable max value
-                 */
-                new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1;
-                timer->period =
-                    (timer->period & 0xffffffffULL) | new_val << 32;
-                }
-                timer->config &= ~HPET_TN_SETVAL;
-                if (hpet_enabled(s)) {
-                    hpet_set_timer(timer);
-                }
-                break;
-        case HPET_TN_ROUTE:
-            timer->fsb = (timer->fsb & 0xffffffff00000000ULL) | new_val;
-            break;
-        case HPET_TN_ROUTE + 4:
-            timer->fsb = (new_val << 32) | (timer->fsb & 0xffffffff);
-            break;
-        default:
-            DPRINTF("qemu: invalid hpet_ram_writel\n");
-            break;
-        }
-        return;
-    } else {
-        switch (index) {
-        case HPET_ID:
-            return;
-        case HPET_CFG:
-            val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK);
-            s->config = (s->config & 0xffffffff00000000ULL) | val;
-            if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
-                /* Enable main counter and interrupt generation. */
-                s->hpet_offset =
-                    ticks_to_ns(s->hpet_counter) - qemu_get_clock_ns(vm_clock);
-                for (i = 0; i < s->num_timers; i++) {
-                    if ((&s->timer[i])->cmp != ~0ULL) {
-                        hpet_set_timer(&s->timer[i]);
-                    }
-                }
-            } else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
-                /* Halt main counter and disable interrupt generation. */
-                s->hpet_counter = hpet_get_ticks(s);
-                for (i = 0; i < s->num_timers; i++) {
-                    hpet_del_timer(&s->timer[i]);
-                }
-            }
-            /* i8254 and RTC output pins are disabled
-             * when HPET is in legacy mode */
-            if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
-                qemu_set_irq(s->pit_enabled, 0);
-                qemu_irq_lower(s->irqs[0]);
-                qemu_irq_lower(s->irqs[RTC_ISA_IRQ]);
-            } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
-                qemu_irq_lower(s->irqs[0]);
-                qemu_set_irq(s->pit_enabled, 1);
-                qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level);
-            }
-            break;
-        case HPET_CFG + 4:
-            DPRINTF("qemu: invalid HPET_CFG+4 write\n");
-            break;
-        case HPET_STATUS:
-            val = new_val & s->isr;
-            for (i = 0; i < s->num_timers; i++) {
-                if (val & (1 << i)) {
-                    update_irq(&s->timer[i], 0);
-                }
-            }
-            break;
-        case HPET_COUNTER:
-            if (hpet_enabled(s)) {
-                DPRINTF("qemu: Writing counter while HPET enabled!\n");
-            }
-            s->hpet_counter =
-                (s->hpet_counter & 0xffffffff00000000ULL) | value;
-            DPRINTF("qemu: HPET counter written. ctr = %#x -> %" PRIx64 "\n",
-                    value, s->hpet_counter);
-            break;
-        case HPET_COUNTER + 4:
-            if (hpet_enabled(s)) {
-                DPRINTF("qemu: Writing counter while HPET enabled!\n");
-            }
-            s->hpet_counter =
-                (s->hpet_counter & 0xffffffffULL) | (((uint64_t)value) << 32);
-            DPRINTF("qemu: HPET counter + 4 written. ctr = %#x -> %" PRIx64 "\n",
-                    value, s->hpet_counter);
-            break;
-        default:
-            DPRINTF("qemu: invalid hpet_ram_writel\n");
-            break;
-        }
-    }
-}
-
-static const MemoryRegionOps hpet_ram_ops = {
-    .read = hpet_ram_read,
-    .write = hpet_ram_write,
-    .valid = {
-        .min_access_size = 4,
-        .max_access_size = 4,
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void hpet_reset(DeviceState *d)
-{
-    HPETState *s = FROM_SYSBUS(HPETState, SYS_BUS_DEVICE(d));
-    int i;
-
-    for (i = 0; i < s->num_timers; i++) {
-        HPETTimer *timer = &s->timer[i];
-
-        hpet_del_timer(timer);
-        timer->cmp = ~0ULL;
-        timer->config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP;
-        if (s->flags & (1 << HPET_MSI_SUPPORT)) {
-            timer->config |= HPET_TN_FSB_CAP;
-        }
-        /* advertise availability of ioapic inti2 */
-        timer->config |=  0x00000004ULL << 32;
-        timer->period = 0ULL;
-        timer->wrap_flag = 0;
-    }
-
-    qemu_set_irq(s->pit_enabled, 1);
-    s->hpet_counter = 0ULL;
-    s->hpet_offset = 0ULL;
-    s->config = 0ULL;
-    hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
-    hpet_cfg.hpet[s->hpet_id].address = SYS_BUS_DEVICE(d)->mmio[0].addr;
-
-    /* to document that the RTC lowers its output on reset as well */
-    s->rtc_irq_level = 0;
-}
-
-static void hpet_handle_legacy_irq(void *opaque, int n, int level)
-{
-    HPETState *s = FROM_SYSBUS(HPETState, opaque);
-
-    if (n == HPET_LEGACY_PIT_INT) {
-        if (!hpet_in_legacy_mode(s)) {
-            qemu_set_irq(s->irqs[0], level);
-        }
-    } else {
-        s->rtc_irq_level = level;
-        if (!hpet_in_legacy_mode(s)) {
-            qemu_set_irq(s->irqs[RTC_ISA_IRQ], level);
-        }
-    }
-}
-
-static int hpet_init(SysBusDevice *dev)
-{
-    HPETState *s = FROM_SYSBUS(HPETState, dev);
-    int i;
-    HPETTimer *timer;
-
-    if (hpet_cfg.count == UINT8_MAX) {
-        /* first instance */
-        hpet_cfg.count = 0;
-    }
-
-    if (hpet_cfg.count == 8) {
-        fprintf(stderr, "Only 8 instances of HPET is allowed\n");
-        return -1;
-    }
-
-    s->hpet_id = hpet_cfg.count++;
-
-    for (i = 0; i < HPET_NUM_IRQ_ROUTES; i++) {
-        sysbus_init_irq(dev, &s->irqs[i]);
-    }
-
-    if (s->num_timers < HPET_MIN_TIMERS) {
-        s->num_timers = HPET_MIN_TIMERS;
-    } else if (s->num_timers > HPET_MAX_TIMERS) {
-        s->num_timers = HPET_MAX_TIMERS;
-    }
-    for (i = 0; i < HPET_MAX_TIMERS; i++) {
-        timer = &s->timer[i];
-        timer->qemu_timer = qemu_new_timer_ns(vm_clock, hpet_timer, timer);
-        timer->tn = i;
-        timer->state = s;
-    }
-
-    /* 64-bit main counter; LegacyReplacementRoute. */
-    s->capability = 0x8086a001ULL;
-    s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
-    s->capability |= ((HPET_CLK_PERIOD) << 32);
-
-    qdev_init_gpio_in(&dev->qdev, hpet_handle_legacy_irq, 2);
-    qdev_init_gpio_out(&dev->qdev, &s->pit_enabled, 1);
-
-    /* HPET Area */
-    memory_region_init_io(&s->iomem, &hpet_ram_ops, s, "hpet", 0x400);
-    sysbus_init_mmio(dev, &s->iomem);
-    return 0;
-}
-
-static Property hpet_device_properties[] = {
-    DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS),
-    DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void hpet_device_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = hpet_init;
-    dc->no_user = 1;
-    dc->reset = hpet_reset;
-    dc->vmsd = &vmstate_hpet;
-    dc->props = hpet_device_properties;
-}
-
-static const TypeInfo hpet_device_info = {
-    .name          = "hpet",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(HPETState),
-    .class_init    = hpet_device_class_init,
-};
-
-static void hpet_register_types(void)
-{
-    type_register_static(&hpet_device_info);
-}
-
-type_init(hpet_register_types)
diff --git a/hw/i2c.c b/hw/i2c.c
deleted file mode 100644 (file)
index 0c4fc1d..0000000
--- a/hw/i2c.c
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * QEMU I2C bus interface.
- *
- * Copyright (c) 2007 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the LGPL.
- */
-
-#include "hw/i2c/i2c.h"
-
-struct i2c_bus
-{
-    BusState qbus;
-    I2CSlave *current_dev;
-    I2CSlave *dev;
-    uint8_t saved_address;
-};
-
-static Property i2c_props[] = {
-    DEFINE_PROP_UINT8("address", struct I2CSlave, address, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-#define TYPE_I2C_BUS "i2c-bus"
-#define I2C_BUS(obj) OBJECT_CHECK(i2c_bus, (obj), TYPE_I2C_BUS)
-
-static const TypeInfo i2c_bus_info = {
-    .name = TYPE_I2C_BUS,
-    .parent = TYPE_BUS,
-    .instance_size = sizeof(i2c_bus),
-};
-
-static void i2c_bus_pre_save(void *opaque)
-{
-    i2c_bus *bus = opaque;
-
-    bus->saved_address = bus->current_dev ? bus->current_dev->address : -1;
-}
-
-static int i2c_bus_post_load(void *opaque, int version_id)
-{
-    i2c_bus *bus = opaque;
-
-    /* The bus is loaded before attached devices, so load and save the
-       current device id.  Devices will check themselves as loaded.  */
-    bus->current_dev = NULL;
-    return 0;
-}
-
-static const VMStateDescription vmstate_i2c_bus = {
-    .name = "i2c_bus",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .pre_save = i2c_bus_pre_save,
-    .post_load = i2c_bus_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT8(saved_address, i2c_bus),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-/* Create a new I2C bus.  */
-i2c_bus *i2c_init_bus(DeviceState *parent, const char *name)
-{
-    i2c_bus *bus;
-
-    bus = FROM_QBUS(i2c_bus, qbus_create(TYPE_I2C_BUS, parent, name));
-    vmstate_register(NULL, -1, &vmstate_i2c_bus, bus);
-    return bus;
-}
-
-void i2c_set_slave_address(I2CSlave *dev, uint8_t address)
-{
-    dev->address = address;
-}
-
-/* Return nonzero if bus is busy.  */
-int i2c_bus_busy(i2c_bus *bus)
-{
-    return bus->current_dev != NULL;
-}
-
-/* Returns non-zero if the address is not valid.  */
-/* TODO: Make this handle multiple masters.  */
-int i2c_start_transfer(i2c_bus *bus, uint8_t address, int recv)
-{
-    BusChild *kid;
-    I2CSlave *slave = NULL;
-    I2CSlaveClass *sc;
-
-    QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
-        DeviceState *qdev = kid->child;
-        I2CSlave *candidate = I2C_SLAVE(qdev);
-        if (candidate->address == address) {
-            slave = candidate;
-            break;
-        }
-    }
-
-    if (!slave) {
-        return 1;
-    }
-
-    sc = I2C_SLAVE_GET_CLASS(slave);
-    /* If the bus is already busy, assume this is a repeated
-       start condition.  */
-    bus->current_dev = slave;
-    if (sc->event) {
-        sc->event(slave, recv ? I2C_START_RECV : I2C_START_SEND);
-    }
-    return 0;
-}
-
-void i2c_end_transfer(i2c_bus *bus)
-{
-    I2CSlave *dev = bus->current_dev;
-    I2CSlaveClass *sc;
-
-    if (!dev) {
-        return;
-    }
-
-    sc = I2C_SLAVE_GET_CLASS(dev);
-    if (sc->event) {
-        sc->event(dev, I2C_FINISH);
-    }
-
-    bus->current_dev = NULL;
-}
-
-int i2c_send(i2c_bus *bus, uint8_t data)
-{
-    I2CSlave *dev = bus->current_dev;
-    I2CSlaveClass *sc;
-
-    if (!dev) {
-        return -1;
-    }
-
-    sc = I2C_SLAVE_GET_CLASS(dev);
-    if (sc->send) {
-        return sc->send(dev, data);
-    }
-
-    return -1;
-}
-
-int i2c_recv(i2c_bus *bus)
-{
-    I2CSlave *dev = bus->current_dev;
-    I2CSlaveClass *sc;
-
-    if (!dev) {
-        return -1;
-    }
-
-    sc = I2C_SLAVE_GET_CLASS(dev);
-    if (sc->recv) {
-        return sc->recv(dev);
-    }
-
-    return -1;
-}
-
-void i2c_nack(i2c_bus *bus)
-{
-    I2CSlave *dev = bus->current_dev;
-    I2CSlaveClass *sc;
-
-    if (!dev) {
-        return;
-    }
-
-    sc = I2C_SLAVE_GET_CLASS(dev);
-    if (sc->event) {
-        sc->event(dev, I2C_NACK);
-    }
-}
-
-static int i2c_slave_post_load(void *opaque, int version_id)
-{
-    I2CSlave *dev = opaque;
-    i2c_bus *bus;
-    bus = FROM_QBUS(i2c_bus, qdev_get_parent_bus(&dev->qdev));
-    if (bus->saved_address == dev->address) {
-        bus->current_dev = dev;
-    }
-    return 0;
-}
-
-const VMStateDescription vmstate_i2c_slave = {
-    .name = "I2CSlave",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .post_load = i2c_slave_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT8(address, I2CSlave),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int i2c_slave_qdev_init(DeviceState *dev)
-{
-    I2CSlave *s = I2C_SLAVE(dev);
-    I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(s);
-
-    return sc->init(s);
-}
-
-DeviceState *i2c_create_slave(i2c_bus *bus, const char *name, uint8_t addr)
-{
-    DeviceState *dev;
-
-    dev = qdev_create(&bus->qbus, name);
-    qdev_prop_set_uint8(dev, "address", addr);
-    qdev_init_nofail(dev);
-    return dev;
-}
-
-static void i2c_slave_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *k = DEVICE_CLASS(klass);
-    k->init = i2c_slave_qdev_init;
-    k->bus_type = TYPE_I2C_BUS;
-    k->props = i2c_props;
-}
-
-static const TypeInfo i2c_slave_type_info = {
-    .name = TYPE_I2C_SLAVE,
-    .parent = TYPE_DEVICE,
-    .instance_size = sizeof(I2CSlave),
-    .abstract = true,
-    .class_size = sizeof(I2CSlaveClass),
-    .class_init = i2c_slave_class_init,
-};
-
-static void i2c_slave_register_types(void)
-{
-    type_register_static(&i2c_bus_info);
-    type_register_static(&i2c_slave_type_info);
-}
-
-type_init(i2c_slave_register_types)
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f6bd8fa6ed6c0beeb7ed20b22e8ae197d38ffb03 100644 (file)
@@ -0,0 +1,4 @@
+common-obj-y += core.o smbus.o smbus_eeprom.o
+common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o
+common-obj-$(CONFIG_ACPI) += smbus_ich9.o
+common-obj-$(CONFIG_APM) += pm_smbus.o
diff --git a/hw/i2c/core.c b/hw/i2c/core.c
new file mode 100644 (file)
index 0000000..0c4fc1d
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * QEMU I2C bus interface.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the LGPL.
+ */
+
+#include "hw/i2c/i2c.h"
+
+struct i2c_bus
+{
+    BusState qbus;
+    I2CSlave *current_dev;
+    I2CSlave *dev;
+    uint8_t saved_address;
+};
+
+static Property i2c_props[] = {
+    DEFINE_PROP_UINT8("address", struct I2CSlave, address, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+#define TYPE_I2C_BUS "i2c-bus"
+#define I2C_BUS(obj) OBJECT_CHECK(i2c_bus, (obj), TYPE_I2C_BUS)
+
+static const TypeInfo i2c_bus_info = {
+    .name = TYPE_I2C_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(i2c_bus),
+};
+
+static void i2c_bus_pre_save(void *opaque)
+{
+    i2c_bus *bus = opaque;
+
+    bus->saved_address = bus->current_dev ? bus->current_dev->address : -1;
+}
+
+static int i2c_bus_post_load(void *opaque, int version_id)
+{
+    i2c_bus *bus = opaque;
+
+    /* The bus is loaded before attached devices, so load and save the
+       current device id.  Devices will check themselves as loaded.  */
+    bus->current_dev = NULL;
+    return 0;
+}
+
+static const VMStateDescription vmstate_i2c_bus = {
+    .name = "i2c_bus",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_save = i2c_bus_pre_save,
+    .post_load = i2c_bus_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT8(saved_address, i2c_bus),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/* Create a new I2C bus.  */
+i2c_bus *i2c_init_bus(DeviceState *parent, const char *name)
+{
+    i2c_bus *bus;
+
+    bus = FROM_QBUS(i2c_bus, qbus_create(TYPE_I2C_BUS, parent, name));
+    vmstate_register(NULL, -1, &vmstate_i2c_bus, bus);
+    return bus;
+}
+
+void i2c_set_slave_address(I2CSlave *dev, uint8_t address)
+{
+    dev->address = address;
+}
+
+/* Return nonzero if bus is busy.  */
+int i2c_bus_busy(i2c_bus *bus)
+{
+    return bus->current_dev != NULL;
+}
+
+/* Returns non-zero if the address is not valid.  */
+/* TODO: Make this handle multiple masters.  */
+int i2c_start_transfer(i2c_bus *bus, uint8_t address, int recv)
+{
+    BusChild *kid;
+    I2CSlave *slave = NULL;
+    I2CSlaveClass *sc;
+
+    QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
+        DeviceState *qdev = kid->child;
+        I2CSlave *candidate = I2C_SLAVE(qdev);
+        if (candidate->address == address) {
+            slave = candidate;
+            break;
+        }
+    }
+
+    if (!slave) {
+        return 1;
+    }
+
+    sc = I2C_SLAVE_GET_CLASS(slave);
+    /* If the bus is already busy, assume this is a repeated
+       start condition.  */
+    bus->current_dev = slave;
+    if (sc->event) {
+        sc->event(slave, recv ? I2C_START_RECV : I2C_START_SEND);
+    }
+    return 0;
+}
+
+void i2c_end_transfer(i2c_bus *bus)
+{
+    I2CSlave *dev = bus->current_dev;
+    I2CSlaveClass *sc;
+
+    if (!dev) {
+        return;
+    }
+
+    sc = I2C_SLAVE_GET_CLASS(dev);
+    if (sc->event) {
+        sc->event(dev, I2C_FINISH);
+    }
+
+    bus->current_dev = NULL;
+}
+
+int i2c_send(i2c_bus *bus, uint8_t data)
+{
+    I2CSlave *dev = bus->current_dev;
+    I2CSlaveClass *sc;
+
+    if (!dev) {
+        return -1;
+    }
+
+    sc = I2C_SLAVE_GET_CLASS(dev);
+    if (sc->send) {
+        return sc->send(dev, data);
+    }
+
+    return -1;
+}
+
+int i2c_recv(i2c_bus *bus)
+{
+    I2CSlave *dev = bus->current_dev;
+    I2CSlaveClass *sc;
+
+    if (!dev) {
+        return -1;
+    }
+
+    sc = I2C_SLAVE_GET_CLASS(dev);
+    if (sc->recv) {
+        return sc->recv(dev);
+    }
+
+    return -1;
+}
+
+void i2c_nack(i2c_bus *bus)
+{
+    I2CSlave *dev = bus->current_dev;
+    I2CSlaveClass *sc;
+
+    if (!dev) {
+        return;
+    }
+
+    sc = I2C_SLAVE_GET_CLASS(dev);
+    if (sc->event) {
+        sc->event(dev, I2C_NACK);
+    }
+}
+
+static int i2c_slave_post_load(void *opaque, int version_id)
+{
+    I2CSlave *dev = opaque;
+    i2c_bus *bus;
+    bus = FROM_QBUS(i2c_bus, qdev_get_parent_bus(&dev->qdev));
+    if (bus->saved_address == dev->address) {
+        bus->current_dev = dev;
+    }
+    return 0;
+}
+
+const VMStateDescription vmstate_i2c_slave = {
+    .name = "I2CSlave",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = i2c_slave_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT8(address, I2CSlave),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int i2c_slave_qdev_init(DeviceState *dev)
+{
+    I2CSlave *s = I2C_SLAVE(dev);
+    I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(s);
+
+    return sc->init(s);
+}
+
+DeviceState *i2c_create_slave(i2c_bus *bus, const char *name, uint8_t addr)
+{
+    DeviceState *dev;
+
+    dev = qdev_create(&bus->qbus, name);
+    qdev_prop_set_uint8(dev, "address", addr);
+    qdev_init_nofail(dev);
+    return dev;
+}
+
+static void i2c_slave_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *k = DEVICE_CLASS(klass);
+    k->init = i2c_slave_qdev_init;
+    k->bus_type = TYPE_I2C_BUS;
+    k->props = i2c_props;
+}
+
+static const TypeInfo i2c_slave_type_info = {
+    .name = TYPE_I2C_SLAVE,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(I2CSlave),
+    .abstract = true,
+    .class_size = sizeof(I2CSlaveClass),
+    .class_init = i2c_slave_class_init,
+};
+
+static void i2c_slave_register_types(void)
+{
+    type_register_static(&i2c_bus_info);
+    type_register_static(&i2c_slave_type_info);
+}
+
+type_init(i2c_slave_register_types)
diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c
new file mode 100644 (file)
index 0000000..0b5bb89
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * PC SMBus implementation
+ * splitted from acpi.c
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "hw/i2c/pm_smbus.h"
+#include "hw/i2c/smbus.h"
+
+/* no save/load? */
+
+#define SMBHSTSTS       0x00
+#define SMBHSTCNT       0x02
+#define SMBHSTCMD       0x03
+#define SMBHSTADD       0x04
+#define SMBHSTDAT0      0x05
+#define SMBHSTDAT1      0x06
+#define SMBBLKDAT       0x07
+
+//#define DEBUG
+
+#ifdef DEBUG
+# define SMBUS_DPRINTF(format, ...)     printf(format, ## __VA_ARGS__)
+#else
+# define SMBUS_DPRINTF(format, ...)     do { } while (0)
+#endif
+
+
+static void smb_transaction(PMSMBus *s)
+{
+    uint8_t prot = (s->smb_ctl >> 2) & 0x07;
+    uint8_t read = s->smb_addr & 0x01;
+    uint8_t cmd = s->smb_cmd;
+    uint8_t addr = s->smb_addr >> 1;
+    i2c_bus *bus = s->smbus;
+
+    SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot);
+    switch(prot) {
+    case 0x0:
+        smbus_quick_command(bus, addr, read);
+        break;
+    case 0x1:
+        if (read) {
+            s->smb_data0 = smbus_receive_byte(bus, addr);
+        } else {
+            smbus_send_byte(bus, addr, cmd);
+        }
+        break;
+    case 0x2:
+        if (read) {
+            s->smb_data0 = smbus_read_byte(bus, addr, cmd);
+        } else {
+            smbus_write_byte(bus, addr, cmd, s->smb_data0);
+        }
+        break;
+    case 0x3:
+        if (read) {
+            uint16_t val;
+            val = smbus_read_word(bus, addr, cmd);
+            s->smb_data0 = val;
+            s->smb_data1 = val >> 8;
+        } else {
+            smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0);
+        }
+        break;
+    case 0x5:
+        if (read) {
+            s->smb_data0 = smbus_read_block(bus, addr, cmd, s->smb_data);
+        } else {
+            smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0);
+        }
+        break;
+    default:
+        goto error;
+    }
+    return;
+
+  error:
+    s->smb_stat |= 0x04;
+}
+
+static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
+                              unsigned width)
+{
+    PMSMBus *s = opaque;
+
+    SMBUS_DPRINTF("SMB writeb port=0x%04x val=0x%02x\n", addr, val);
+    switch(addr) {
+    case SMBHSTSTS:
+        s->smb_stat = 0;
+        s->smb_index = 0;
+        break;
+    case SMBHSTCNT:
+        s->smb_ctl = val;
+        if (val & 0x40)
+            smb_transaction(s);
+        break;
+    case SMBHSTCMD:
+        s->smb_cmd = val;
+        break;
+    case SMBHSTADD:
+        s->smb_addr = val;
+        break;
+    case SMBHSTDAT0:
+        s->smb_data0 = val;
+        break;
+    case SMBHSTDAT1:
+        s->smb_data1 = val;
+        break;
+    case SMBBLKDAT:
+        s->smb_data[s->smb_index++] = val;
+        if (s->smb_index > 31)
+            s->smb_index = 0;
+        break;
+    default:
+        break;
+    }
+}
+
+static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width)
+{
+    PMSMBus *s = opaque;
+    uint32_t val;
+
+    switch(addr) {
+    case SMBHSTSTS:
+        val = s->smb_stat;
+        break;
+    case SMBHSTCNT:
+        s->smb_index = 0;
+        val = s->smb_ctl & 0x1f;
+        break;
+    case SMBHSTCMD:
+        val = s->smb_cmd;
+        break;
+    case SMBHSTADD:
+        val = s->smb_addr;
+        break;
+    case SMBHSTDAT0:
+        val = s->smb_data0;
+        break;
+    case SMBHSTDAT1:
+        val = s->smb_data1;
+        break;
+    case SMBBLKDAT:
+        val = s->smb_data[s->smb_index++];
+        if (s->smb_index > 31)
+            s->smb_index = 0;
+        break;
+    default:
+        val = 0;
+        break;
+    }
+    SMBUS_DPRINTF("SMB readb port=0x%04x val=0x%02x\n", addr, val);
+    return val;
+}
+
+static const MemoryRegionOps pm_smbus_ops = {
+    .read = smb_ioport_readb,
+    .write = smb_ioport_writeb,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 1,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+void pm_smbus_init(DeviceState *parent, PMSMBus *smb)
+{
+    smb->smbus = i2c_init_bus(parent, "i2c");
+    memory_region_init_io(&smb->io, &pm_smbus_ops, smb, "pm-smbus", 64);
+}
diff --git a/hw/i2c/smbus.c b/hw/i2c/smbus.c
new file mode 100644 (file)
index 0000000..25d2d04
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * QEMU SMBus device emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the LGPL.
+ */
+
+/* TODO: Implement PEC.  */
+
+#include "hw/hw.h"
+#include "hw/i2c/i2c.h"
+#include "hw/i2c/smbus.h"
+
+//#define DEBUG_SMBUS 1
+
+#ifdef DEBUG_SMBUS
+#define DPRINTF(fmt, ...) \
+do { printf("smbus(%02x): " fmt , dev->i2c.address, ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+enum {
+    SMBUS_IDLE,
+    SMBUS_WRITE_DATA,
+    SMBUS_RECV_BYTE,
+    SMBUS_READ_DATA,
+    SMBUS_DONE,
+    SMBUS_CONFUSED = -1
+};
+
+static void smbus_do_quick_cmd(SMBusDevice *dev, int recv)
+{
+    SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
+
+    DPRINTF("Quick Command %d\n", recv);
+    if (sc->quick_cmd) {
+        sc->quick_cmd(dev, recv);
+    }
+}
+
+static void smbus_do_write(SMBusDevice *dev)
+{
+    SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
+
+    if (dev->data_len == 0) {
+        smbus_do_quick_cmd(dev, 0);
+    } else if (dev->data_len == 1) {
+        DPRINTF("Send Byte\n");
+        if (sc->send_byte) {
+            sc->send_byte(dev, dev->data_buf[0]);
+        }
+    } else {
+        dev->command = dev->data_buf[0];
+        DPRINTF("Command %d len %d\n", dev->command, dev->data_len - 1);
+        if (sc->write_data) {
+            sc->write_data(dev, dev->command, dev->data_buf + 1,
+                           dev->data_len - 1);
+        }
+    }
+}
+
+static void smbus_i2c_event(I2CSlave *s, enum i2c_event event)
+{
+    SMBusDevice *dev = SMBUS_DEVICE(s);
+
+    switch (event) {
+    case I2C_START_SEND:
+        switch (dev->mode) {
+        case SMBUS_IDLE:
+            DPRINTF("Incoming data\n");
+            dev->mode = SMBUS_WRITE_DATA;
+            break;
+        default:
+            BADF("Unexpected send start condition in state %d\n", dev->mode);
+            dev->mode = SMBUS_CONFUSED;
+            break;
+        }
+        break;
+
+    case I2C_START_RECV:
+        switch (dev->mode) {
+        case SMBUS_IDLE:
+            DPRINTF("Read mode\n");
+            dev->mode = SMBUS_RECV_BYTE;
+            break;
+        case SMBUS_WRITE_DATA:
+            if (dev->data_len == 0) {
+                BADF("Read after write with no data\n");
+                dev->mode = SMBUS_CONFUSED;
+            } else {
+                if (dev->data_len > 1) {
+                    smbus_do_write(dev);
+                } else {
+                    dev->command = dev->data_buf[0];
+                    DPRINTF("%02x: Command %d\n", dev->i2c.address,
+                            dev->command);
+                }
+                DPRINTF("Read mode\n");
+                dev->data_len = 0;
+                dev->mode = SMBUS_READ_DATA;
+            }
+            break;
+        default:
+            BADF("Unexpected recv start condition in state %d\n", dev->mode);
+            dev->mode = SMBUS_CONFUSED;
+            break;
+        }
+        break;
+
+    case I2C_FINISH:
+        switch (dev->mode) {
+        case SMBUS_WRITE_DATA:
+            smbus_do_write(dev);
+            break;
+        case SMBUS_RECV_BYTE:
+            smbus_do_quick_cmd(dev, 1);
+            break;
+        case SMBUS_READ_DATA:
+            BADF("Unexpected stop during receive\n");
+            break;
+        default:
+            /* Nothing to do.  */
+            break;
+        }
+        dev->mode = SMBUS_IDLE;
+        dev->data_len = 0;
+        break;
+
+    case I2C_NACK:
+        switch (dev->mode) {
+        case SMBUS_DONE:
+            /* Nothing to do.  */
+            break;
+        case SMBUS_READ_DATA:
+            dev->mode = SMBUS_DONE;
+            break;
+        default:
+            BADF("Unexpected NACK in state %d\n", dev->mode);
+            dev->mode = SMBUS_CONFUSED;
+            break;
+        }
+    }
+}
+
+static int smbus_i2c_recv(I2CSlave *s)
+{
+    SMBusDevice *dev = SMBUS_DEVICE(s);
+    SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
+    int ret;
+
+    switch (dev->mode) {
+    case SMBUS_RECV_BYTE:
+        if (sc->receive_byte) {
+            ret = sc->receive_byte(dev);
+        } else {
+            ret = 0;
+        }
+        DPRINTF("Receive Byte %02x\n", ret);
+        dev->mode = SMBUS_DONE;
+        break;
+    case SMBUS_READ_DATA:
+        if (sc->read_data) {
+            ret = sc->read_data(dev, dev->command, dev->data_len);
+            dev->data_len++;
+        } else {
+            ret = 0;
+        }
+        DPRINTF("Read data %02x\n", ret);
+        break;
+    default:
+        BADF("Unexpected read in state %d\n", dev->mode);
+        dev->mode = SMBUS_CONFUSED;
+        ret = 0;
+        break;
+    }
+    return ret;
+}
+
+static int smbus_i2c_send(I2CSlave *s, uint8_t data)
+{
+    SMBusDevice *dev = SMBUS_DEVICE(s);
+
+    switch (dev->mode) {
+    case SMBUS_WRITE_DATA:
+        DPRINTF("Write data %02x\n", data);
+        dev->data_buf[dev->data_len++] = data;
+        break;
+    default:
+        BADF("Unexpected write in state %d\n", dev->mode);
+        break;
+    }
+    return 0;
+}
+
+static int smbus_device_init(I2CSlave *i2c)
+{
+    SMBusDevice *dev = SMBUS_DEVICE(i2c);
+    SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
+
+    return sc->init(dev);
+}
+
+/* Master device commands.  */
+void smbus_quick_command(i2c_bus *bus, uint8_t addr, int read)
+{
+    i2c_start_transfer(bus, addr, read);
+    i2c_end_transfer(bus);
+}
+
+uint8_t smbus_receive_byte(i2c_bus *bus, uint8_t addr)
+{
+    uint8_t data;
+
+    i2c_start_transfer(bus, addr, 1);
+    data = i2c_recv(bus);
+    i2c_nack(bus);
+    i2c_end_transfer(bus);
+    return data;
+}
+
+void smbus_send_byte(i2c_bus *bus, uint8_t addr, uint8_t data)
+{
+    i2c_start_transfer(bus, addr, 0);
+    i2c_send(bus, data);
+    i2c_end_transfer(bus);
+}
+
+uint8_t smbus_read_byte(i2c_bus *bus, uint8_t addr, uint8_t command)
+{
+    uint8_t data;
+    i2c_start_transfer(bus, addr, 0);
+    i2c_send(bus, command);
+    i2c_start_transfer(bus, addr, 1);
+    data = i2c_recv(bus);
+    i2c_nack(bus);
+    i2c_end_transfer(bus);
+    return data;
+}
+
+void smbus_write_byte(i2c_bus *bus, uint8_t addr, uint8_t command, uint8_t data)
+{
+    i2c_start_transfer(bus, addr, 0);
+    i2c_send(bus, command);
+    i2c_send(bus, data);
+    i2c_end_transfer(bus);
+}
+
+uint16_t smbus_read_word(i2c_bus *bus, uint8_t addr, uint8_t command)
+{
+    uint16_t data;
+    i2c_start_transfer(bus, addr, 0);
+    i2c_send(bus, command);
+    i2c_start_transfer(bus, addr, 1);
+    data = i2c_recv(bus);
+    data |= i2c_recv(bus) << 8;
+    i2c_nack(bus);
+    i2c_end_transfer(bus);
+    return data;
+}
+
+void smbus_write_word(i2c_bus *bus, uint8_t addr, uint8_t command, uint16_t data)
+{
+    i2c_start_transfer(bus, addr, 0);
+    i2c_send(bus, command);
+    i2c_send(bus, data & 0xff);
+    i2c_send(bus, data >> 8);
+    i2c_end_transfer(bus);
+}
+
+int smbus_read_block(i2c_bus *bus, uint8_t addr, uint8_t command, uint8_t *data)
+{
+    int len;
+    int i;
+
+    i2c_start_transfer(bus, addr, 0);
+    i2c_send(bus, command);
+    i2c_start_transfer(bus, addr, 1);
+    len = i2c_recv(bus);
+    if (len > 32)
+        len = 0;
+    for (i = 0; i < len; i++)
+        data[i] = i2c_recv(bus);
+    i2c_nack(bus);
+    i2c_end_transfer(bus);
+    return len;
+}
+
+void smbus_write_block(i2c_bus *bus, uint8_t addr, uint8_t command, uint8_t *data,
+                       int len)
+{
+    int i;
+
+    if (len > 32)
+        len = 32;
+
+    i2c_start_transfer(bus, addr, 0);
+    i2c_send(bus, command);
+    i2c_send(bus, len);
+    for (i = 0; i < len; i++)
+        i2c_send(bus, data[i]);
+    i2c_end_transfer(bus);
+}
+
+static void smbus_device_class_init(ObjectClass *klass, void *data)
+{
+    I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
+
+    sc->init = smbus_device_init;
+    sc->event = smbus_i2c_event;
+    sc->recv = smbus_i2c_recv;
+    sc->send = smbus_i2c_send;
+}
+
+static const TypeInfo smbus_device_type_info = {
+    .name = TYPE_SMBUS_DEVICE,
+    .parent = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(SMBusDevice),
+    .abstract = true,
+    .class_size = sizeof(SMBusDeviceClass),
+    .class_init = smbus_device_class_init,
+};
+
+static void smbus_device_register_types(void)
+{
+    type_register_static(&smbus_device_type_info);
+}
+
+type_init(smbus_device_register_types)
diff --git a/hw/i2c/smbus_eeprom.c b/hw/i2c/smbus_eeprom.c
new file mode 100644 (file)
index 0000000..0154283
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * QEMU SMBus EEPROM device
+ *
+ * Copyright (c) 2007 Arastra, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/i2c/i2c.h"
+#include "hw/i2c/smbus.h"
+
+//#define DEBUG
+
+typedef struct SMBusEEPROMDevice {
+    SMBusDevice smbusdev;
+    void *data;
+    uint8_t offset;
+} SMBusEEPROMDevice;
+
+static void eeprom_quick_cmd(SMBusDevice *dev, uint8_t read)
+{
+#ifdef DEBUG
+    printf("eeprom_quick_cmd: addr=0x%02x read=%d\n", dev->i2c.address, read);
+#endif
+}
+
+static void eeprom_send_byte(SMBusDevice *dev, uint8_t val)
+{
+    SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
+#ifdef DEBUG
+    printf("eeprom_send_byte: addr=0x%02x val=0x%02x\n",
+           dev->i2c.address, val);
+#endif
+    eeprom->offset = val;
+}
+
+static uint8_t eeprom_receive_byte(SMBusDevice *dev)
+{
+    SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
+    uint8_t *data = eeprom->data;
+    uint8_t val = data[eeprom->offset++];
+#ifdef DEBUG
+    printf("eeprom_receive_byte: addr=0x%02x val=0x%02x\n",
+           dev->i2c.address, val);
+#endif
+    return val;
+}
+
+static void eeprom_write_data(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len)
+{
+    SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
+    int n;
+#ifdef DEBUG
+    printf("eeprom_write_byte: addr=0x%02x cmd=0x%02x val=0x%02x\n",
+           dev->i2c.address, cmd, buf[0]);
+#endif
+    /* An page write operation is not a valid SMBus command.
+       It is a block write without a length byte.  Fortunately we
+       get the full block anyway.  */
+    /* TODO: Should this set the current location?  */
+    if (cmd + len > 256)
+        n = 256 - cmd;
+    else
+        n = len;
+    memcpy(eeprom->data + cmd, buf, n);
+    len -= n;
+    if (len)
+        memcpy(eeprom->data, buf + n, len);
+}
+
+static uint8_t eeprom_read_data(SMBusDevice *dev, uint8_t cmd, int n)
+{
+    SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
+    /* If this is the first byte then set the current position.  */
+    if (n == 0)
+        eeprom->offset = cmd;
+    /* As with writes, we implement block reads without the
+       SMBus length byte.  */
+    return eeprom_receive_byte(dev);
+}
+
+static int smbus_eeprom_initfn(SMBusDevice *dev)
+{
+    SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *)dev;
+
+    eeprom->offset = 0;
+    return 0;
+}
+
+static Property smbus_eeprom_properties[] = {
+    DEFINE_PROP_PTR("data", SMBusEEPROMDevice, data),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(klass);
+
+    sc->init = smbus_eeprom_initfn;
+    sc->quick_cmd = eeprom_quick_cmd;
+    sc->send_byte = eeprom_send_byte;
+    sc->receive_byte = eeprom_receive_byte;
+    sc->write_data = eeprom_write_data;
+    sc->read_data = eeprom_read_data;
+    dc->props = smbus_eeprom_properties;
+}
+
+static const TypeInfo smbus_eeprom_info = {
+    .name          = "smbus-eeprom",
+    .parent        = TYPE_SMBUS_DEVICE,
+    .instance_size = sizeof(SMBusEEPROMDevice),
+    .class_init    = smbus_eeprom_class_initfn,
+};
+
+static void smbus_eeprom_register_types(void)
+{
+    type_register_static(&smbus_eeprom_info);
+}
+
+type_init(smbus_eeprom_register_types)
+
+void smbus_eeprom_init(i2c_bus *smbus, int nb_eeprom,
+                       const uint8_t *eeprom_spd, int eeprom_spd_size)
+{
+    int i;
+    uint8_t *eeprom_buf = g_malloc0(8 * 256); /* XXX: make this persistent */
+    if (eeprom_spd_size > 0) {
+        memcpy(eeprom_buf, eeprom_spd, eeprom_spd_size);
+    }
+
+    for (i = 0; i < nb_eeprom; i++) {
+        DeviceState *eeprom;
+        eeprom = qdev_create((BusState *)smbus, "smbus-eeprom");
+        qdev_prop_set_uint8(eeprom, "address", 0x50 + i);
+        qdev_prop_set_ptr(eeprom, "data", eeprom_buf + (i * 256));
+        qdev_init_nofail(eeprom);
+    }
+}
diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c
new file mode 100644 (file)
index 0000000..ca22978
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * ACPI implementation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
+ *               VA Linux Systems Japan K.K.
+ * Copyright (C) 2012 Jason Baron <jbaron@redhat.com>
+ *
+ * This is based on acpi.c, but heavily rewritten.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ *
+ */
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "hw/i2c/pm_smbus.h"
+#include "hw/pci/pci.h"
+#include "sysemu/sysemu.h"
+#include "hw/i2c/i2c.h"
+#include "hw/i2c/smbus.h"
+
+#include "hw/i386/ich9.h"
+
+#define TYPE_ICH9_SMB_DEVICE "ICH9 SMB"
+#define ICH9_SMB_DEVICE(obj) \
+     OBJECT_CHECK(ICH9SMBState, (obj), TYPE_ICH9_SMB_DEVICE)
+
+typedef struct ICH9SMBState {
+    PCIDevice dev;
+
+    PMSMBus smb;
+} ICH9SMBState;
+
+static const VMStateDescription vmstate_ich9_smbus = {
+    .name = "ich9_smb",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(dev, struct ICH9SMBState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ich9_smbus_write_config(PCIDevice *d, uint32_t address,
+                                    uint32_t val, int len)
+{
+    ICH9SMBState *s = ICH9_SMB_DEVICE(d);
+
+    pci_default_write_config(d, address, val, len);
+    if (range_covers_byte(address, len, ICH9_SMB_HOSTC)) {
+        uint8_t hostc = s->dev.config[ICH9_SMB_HOSTC];
+        if ((hostc & ICH9_SMB_HOSTC_HST_EN) &&
+            !(hostc & ICH9_SMB_HOSTC_I2C_EN)) {
+            memory_region_set_enabled(&s->smb.io, true);
+        } else {
+            memory_region_set_enabled(&s->smb.io, false);
+        }
+    }
+}
+
+static int ich9_smbus_initfn(PCIDevice *d)
+{
+    ICH9SMBState *s = ICH9_SMB_DEVICE(d);
+
+    /* TODO? D31IP.SMIP in chipset configuration space */
+    pci_config_set_interrupt_pin(d->config, 0x01); /* interrupt pin 1 */
+
+    pci_set_byte(d->config + ICH9_SMB_HOSTC, 0);
+    /* TODO bar0, bar1: 64bit BAR support*/
+
+    pm_smbus_init(&d->qdev, &s->smb);
+    pci_register_bar(d, ICH9_SMB_SMB_BASE_BAR, PCI_BASE_ADDRESS_SPACE_IO,
+                     &s->smb.io);
+    return 0;
+}
+
+static void ich9_smb_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_ICH9_6;
+    k->revision = ICH9_A2_SMB_REVISION;
+    k->class_id = PCI_CLASS_SERIAL_SMBUS;
+    dc->no_user = 1;
+    dc->vmsd = &vmstate_ich9_smbus;
+    dc->desc = "ICH9 SMBUS Bridge";
+    k->init = ich9_smbus_initfn;
+    k->config_write = ich9_smbus_write_config;
+}
+
+i2c_bus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base)
+{
+    PCIDevice *d =
+        pci_create_simple_multifunction(bus, devfn, true, TYPE_ICH9_SMB_DEVICE);
+    ICH9SMBState *s = ICH9_SMB_DEVICE(d);
+    return s->smb.smbus;
+}
+
+static const TypeInfo ich9_smb_info = {
+    .name   = TYPE_ICH9_SMB_DEVICE,
+    .parent = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(ICH9SMBState),
+    .class_init = ich9_smb_class_init,
+};
+
+static void ich9_smb_register(void)
+{
+    type_register_static(&ich9_smb_info);
+}
+
+type_init(ich9_smb_register);
diff --git a/hw/i2c/versatile_i2c.c b/hw/i2c/versatile_i2c.c
new file mode 100644 (file)
index 0000000..d0444ae
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * ARM Versatile I2C controller
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Copyright (c) 2012 Oskar Andero <oskar.andero@gmail.com>
+ *
+ * This file is derived from hw/realview.c by Paul Brook
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "hw/sysbus.h"
+#include "hw/bitbang_i2c.h"
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    bitbang_i2c_interface *bitbang;
+    int out;
+    int in;
+} VersatileI2CState;
+
+static uint64_t versatile_i2c_read(void *opaque, hwaddr offset,
+                                   unsigned size)
+{
+    VersatileI2CState *s = (VersatileI2CState *)opaque;
+
+    if (offset == 0) {
+        return (s->out & 1) | (s->in << 1);
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Bad offset 0x%x\n", __func__, (int)offset);
+        return -1;
+    }
+}
+
+static void versatile_i2c_write(void *opaque, hwaddr offset,
+                                uint64_t value, unsigned size)
+{
+    VersatileI2CState *s = (VersatileI2CState *)opaque;
+
+    switch (offset) {
+    case 0:
+        s->out |= value & 3;
+        break;
+    case 4:
+        s->out &= ~value;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Bad offset 0x%x\n", __func__, (int)offset);
+    }
+    bitbang_i2c_set(s->bitbang, BITBANG_I2C_SCL, (s->out & 1) != 0);
+    s->in = bitbang_i2c_set(s->bitbang, BITBANG_I2C_SDA, (s->out & 2) != 0);
+}
+
+static const MemoryRegionOps versatile_i2c_ops = {
+    .read = versatile_i2c_read,
+    .write = versatile_i2c_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int versatile_i2c_init(SysBusDevice *dev)
+{
+    VersatileI2CState *s = FROM_SYSBUS(VersatileI2CState, dev);
+    i2c_bus *bus;
+
+    bus = i2c_init_bus(&dev->qdev, "i2c");
+    s->bitbang = bitbang_i2c_init(bus);
+    memory_region_init_io(&s->iomem, &versatile_i2c_ops, s,
+                          "versatile_i2c", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    return 0;
+}
+
+static void versatile_i2c_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = versatile_i2c_init;
+}
+
+static const TypeInfo versatile_i2c_info = {
+    .name          = "versatile_i2c",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(VersatileI2CState),
+    .class_init    = versatile_i2c_class_init,
+};
+
+static void versatile_i2c_register_types(void)
+{
+    type_register_static(&versatile_i2c_info);
+}
+
+type_init(versatile_i2c_register_types)
diff --git a/hw/i82374.c b/hw/i82374.c
deleted file mode 100644 (file)
index 835639d..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * QEMU Intel 82374 emulation (Enhanced DMA controller)
- *
- * Copyright (c) 2010 Hervé Poussineau
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/isa/isa.h"
-
-//#define DEBUG_I82374
-
-#ifdef DEBUG_I82374
-#define DPRINTF(fmt, ...) \
-do { fprintf(stderr, "i82374: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) \
-do {} while (0)
-#endif
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "i82374 ERROR: " fmt , ## __VA_ARGS__); } while (0)
-
-typedef struct I82374State {
-    uint8_t commands[8];
-    qemu_irq out;
-} I82374State;
-
-static const VMStateDescription vmstate_i82374 = {
-    .name = "i82374",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT8_ARRAY(commands, I82374State, 8),
-        VMSTATE_END_OF_LIST()
-    },
-};
-
-static uint32_t i82374_read_isr(void *opaque, uint32_t nport)
-{
-    uint32_t val = 0;
-
-    BADF("%s: %08x\n", __func__, nport);
-
-    DPRINTF("%s: %08x=%08x\n", __func__, nport, val);
-    return val;
-}
-
-static void i82374_write_command(void *opaque, uint32_t nport, uint32_t data)
-{
-    DPRINTF("%s: %08x=%08x\n", __func__, nport, data);
-
-    if (data != 0x42) {
-        /* Not Stop S/G command */
-        BADF("%s: %08x=%08x\n", __func__, nport, data);
-    }
-}
-
-static uint32_t i82374_read_status(void *opaque, uint32_t nport)
-{
-    uint32_t val = 0;
-
-    BADF("%s: %08x\n", __func__, nport);
-
-    DPRINTF("%s: %08x=%08x\n", __func__, nport, val);
-    return val;
-}
-
-static void i82374_write_descriptor(void *opaque, uint32_t nport, uint32_t data)
-{
-    DPRINTF("%s: %08x=%08x\n", __func__, nport, data);
-
-    BADF("%s: %08x=%08x\n", __func__, nport, data);
-}
-
-static uint32_t i82374_read_descriptor(void *opaque, uint32_t nport)
-{
-    uint32_t val = 0;
-
-    BADF("%s: %08x\n", __func__, nport);
-
-    DPRINTF("%s: %08x=%08x\n", __func__, nport, val);
-    return val;
-}
-
-static void i82374_init(I82374State *s)
-{
-    DMA_init(1, &s->out);
-    memset(s->commands, 0, sizeof(s->commands));
-}
-
-typedef struct ISAi82374State {
-    ISADevice dev;
-    uint32_t iobase;
-    I82374State state;
-} ISAi82374State;
-
-static const VMStateDescription vmstate_isa_i82374 = {
-    .name = "isa-i82374",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .fields = (VMStateField[]) {
-        VMSTATE_STRUCT(state, ISAi82374State, 0, vmstate_i82374, I82374State),
-        VMSTATE_END_OF_LIST()
-    },
-};
-
-static int i82374_isa_init(ISADevice *dev)
-{
-    ISAi82374State *isa = DO_UPCAST(ISAi82374State, dev, dev);
-    I82374State *s = &isa->state;
-
-    register_ioport_read(isa->iobase + 0x0A, 1, 1, i82374_read_isr, s);
-    register_ioport_write(isa->iobase + 0x10, 8, 1, i82374_write_command, s);
-    register_ioport_read(isa->iobase + 0x18, 8, 1, i82374_read_status, s);
-    register_ioport_write(isa->iobase + 0x20, 0x20, 1, i82374_write_descriptor, s);
-    register_ioport_read(isa->iobase + 0x20, 0x20, 1, i82374_read_descriptor, s);
-
-    i82374_init(s);
-
-    qdev_init_gpio_out(&dev->qdev, &s->out, 1);
-
-    return 0;
-}
-
-static Property i82374_properties[] = {
-    DEFINE_PROP_HEX32("iobase", ISAi82374State, iobase, 0x400),
-    DEFINE_PROP_END_OF_LIST()
-};
-
-static void i82374_class_init(ObjectClass *klass, void *data)
-{
-    ISADeviceClass *k = ISA_DEVICE_CLASS(klass);
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    
-    k->init  = i82374_isa_init;
-    dc->vmsd = &vmstate_isa_i82374;
-    dc->props = i82374_properties;
-}
-
-static const TypeInfo i82374_isa_info = {
-    .name  = "i82374",
-    .parent = TYPE_ISA_DEVICE,
-    .instance_size  = sizeof(ISAi82374State),
-    .class_init = i82374_class_init,
-};
-
-static void i82374_register_types(void)
-{
-    type_register_static(&i82374_isa_info);
-}
-
-type_init(i82374_register_types)
diff --git a/hw/i82378.c b/hw/i82378.c
deleted file mode 100644 (file)
index cced9af..0000000
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * QEMU Intel i82378 emulation (PCI to ISA bridge)
- *
- * Copyright (c) 2010-2011 Hervé Poussineau
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/pci/pci.h"
-#include "hw/i386/pc.h"
-#include "hw/timer/i8254.h"
-#include "hw/audio/pcspk.h"
-
-//#define DEBUG_I82378
-
-#ifdef DEBUG_I82378
-#define DPRINTF(fmt, ...) \
-do { fprintf(stderr, "i82378: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) \
-do {} while (0)
-#endif
-
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "i82378 ERROR: " fmt , ## __VA_ARGS__); } while (0)
-
-typedef struct I82378State {
-    qemu_irq out[2];
-    qemu_irq *i8259;
-    MemoryRegion io;
-    MemoryRegion mem;
-} I82378State;
-
-typedef struct PCIi82378State {
-    PCIDevice pci_dev;
-    uint32_t isa_io_base;
-    uint32_t isa_mem_base;
-    I82378State state;
-} PCIi82378State;
-
-static const VMStateDescription vmstate_pci_i82378 = {
-    .name = "pci-i82378",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .fields = (VMStateField[]) {
-        VMSTATE_PCI_DEVICE(pci_dev, PCIi82378State),
-        VMSTATE_END_OF_LIST()
-    },
-};
-
-static void i82378_io_write(void *opaque, hwaddr addr,
-                            uint64_t value, unsigned int size)
-{
-    switch (size) {
-    case 1:
-        DPRINTF("%s: " TARGET_FMT_plx "=%02" PRIx64 "\n", __func__,
-                addr, value);
-        cpu_outb(addr, value);
-        break;
-    case 2:
-        DPRINTF("%s: " TARGET_FMT_plx "=%04" PRIx64 "\n", __func__,
-                addr, value);
-        cpu_outw(addr, value);
-        break;
-    case 4:
-        DPRINTF("%s: " TARGET_FMT_plx "=%08" PRIx64 "\n", __func__,
-                addr, value);
-        cpu_outl(addr, value);
-        break;
-    default:
-        abort();
-    }
-}
-
-static uint64_t i82378_io_read(void *opaque, hwaddr addr,
-                               unsigned int size)
-{
-    DPRINTF("%s: " TARGET_FMT_plx "\n", __func__, addr);
-    switch (size) {
-    case 1:
-        return cpu_inb(addr);
-    case 2:
-        return cpu_inw(addr);
-    case 4:
-        return cpu_inl(addr);
-    default:
-        abort();
-    }
-}
-
-static const MemoryRegionOps i82378_io_ops = {
-    .read = i82378_io_read,
-    .write = i82378_io_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void i82378_mem_write(void *opaque, hwaddr addr,
-                             uint64_t value, unsigned int size)
-{
-    switch (size) {
-    case 1:
-        DPRINTF("%s: " TARGET_FMT_plx "=%02" PRIx64 "\n", __func__,
-                addr, value);
-        cpu_outb(addr, value);
-        break;
-    case 2:
-        DPRINTF("%s: " TARGET_FMT_plx "=%04" PRIx64 "\n", __func__,
-                addr, value);
-        cpu_outw(addr, value);
-        break;
-    case 4:
-        DPRINTF("%s: " TARGET_FMT_plx "=%08" PRIx64 "\n", __func__,
-                addr, value);
-        cpu_outl(addr, value);
-        break;
-    default:
-        abort();
-    }
-}
-
-static uint64_t i82378_mem_read(void *opaque, hwaddr addr,
-                                unsigned int size)
-{
-    DPRINTF("%s: " TARGET_FMT_plx "\n", __func__, addr);
-    switch (size) {
-    case 1:
-        return cpu_inb(addr);
-    case 2:
-        return cpu_inw(addr);
-    case 4:
-        return cpu_inl(addr);
-    default:
-        abort();
-    }
-}
-
-static const MemoryRegionOps i82378_mem_ops = {
-    .read = i82378_mem_read,
-    .write = i82378_mem_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void i82378_request_out0_irq(void *opaque, int irq, int level)
-{
-    I82378State *s = opaque;
-    qemu_set_irq(s->out[0], level);
-}
-
-static void i82378_request_pic_irq(void *opaque, int irq, int level)
-{
-    DeviceState *dev = opaque;
-    PCIDevice *pci = DO_UPCAST(PCIDevice, qdev, dev);
-    PCIi82378State *s = DO_UPCAST(PCIi82378State, pci_dev, pci);
-
-    qemu_set_irq(s->state.i8259[irq], level);
-}
-
-static void i82378_init(DeviceState *dev, I82378State *s)
-{
-    ISABus *isabus = DO_UPCAST(ISABus, qbus, qdev_get_child_bus(dev, "isa.0"));
-    ISADevice *pit;
-    ISADevice *isa;
-    qemu_irq *out0_irq;
-
-    /* This device has:
-       2 82C59 (irq)
-       1 82C54 (pit)
-       2 82C37 (dma)
-       NMI
-       Utility Bus Support Registers
-
-       All devices accept byte access only, except timer
-     */
-
-    qdev_init_gpio_out(dev, s->out, 2);
-    qdev_init_gpio_in(dev, i82378_request_pic_irq, 16);
-
-    /* Workaround the fact that i8259 is not qdev'ified... */
-    out0_irq = qemu_allocate_irqs(i82378_request_out0_irq, s, 1);
-
-    /* 2 82C59 (irq) */
-    s->i8259 = i8259_init(isabus, *out0_irq);
-    isa_bus_irqs(isabus, s->i8259);
-
-    /* 1 82C54 (pit) */
-    pit = pit_init(isabus, 0x40, 0, NULL);
-
-    /* speaker */
-    pcspk_init(isabus, pit);
-
-    /* 2 82C37 (dma) */
-    isa = isa_create_simple(isabus, "i82374");
-    qdev_connect_gpio_out(&isa->qdev, 0, s->out[1]);
-
-    /* timer */
-    isa_create_simple(isabus, "mc146818rtc");
-}
-
-static int pci_i82378_init(PCIDevice *dev)
-{
-    PCIi82378State *pci = DO_UPCAST(PCIi82378State, pci_dev, dev);
-    I82378State *s = &pci->state;
-    uint8_t *pci_conf;
-
-    pci_conf = dev->config;
-    pci_set_word(pci_conf + PCI_COMMAND,
-                 PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
-    pci_set_word(pci_conf + PCI_STATUS,
-                 PCI_STATUS_DEVSEL_MEDIUM);
-
-    pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin 0 */
-
-    memory_region_init_io(&s->io, &i82378_io_ops, s, "i82378-io", 0x00010000);
-    pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->io);
-
-    memory_region_init_io(&s->mem, &i82378_mem_ops, s, "i82378-mem", 0x01000000);
-    pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem);
-
-    /* Make I/O address read only */
-    pci_set_word(dev->wmask + PCI_COMMAND, PCI_COMMAND_SPECIAL);
-    pci_set_long(dev->wmask + PCI_BASE_ADDRESS_0, 0);
-    pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, pci->isa_io_base);
-
-    isa_mem_base = pci->isa_mem_base;
-    isa_bus_new(&dev->qdev, pci_address_space_io(dev));
-
-    i82378_init(&dev->qdev, s);
-
-    return 0;
-}
-
-static Property i82378_properties[] = {
-    DEFINE_PROP_HEX32("iobase", PCIi82378State, isa_io_base, 0x80000000),
-    DEFINE_PROP_HEX32("membase", PCIi82378State, isa_mem_base, 0xc0000000),
-    DEFINE_PROP_END_OF_LIST()
-};
-
-static void pci_i82378_class_init(ObjectClass *klass, void *data)
-{
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    k->init = pci_i82378_init;
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->device_id = PCI_DEVICE_ID_INTEL_82378;
-    k->revision = 0x03;
-    k->class_id = PCI_CLASS_BRIDGE_ISA;
-    k->subsystem_vendor_id = 0x0;
-    k->subsystem_id = 0x0;
-    dc->vmsd = &vmstate_pci_i82378;
-    dc->props = i82378_properties;
-}
-
-static const TypeInfo pci_i82378_info = {
-    .name = "i82378",
-    .parent = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIi82378State),
-    .class_init = pci_i82378_class_init,
-};
-
-static void i82378_register_types(void)
-{
-    type_register_static(&pci_i82378_info);
-}
-
-type_init(i82378_register_types)
diff --git a/hw/i8254.c b/hw/i8254.c
deleted file mode 100644 (file)
index 20c0c36..0000000
+++ /dev/null
@@ -1,362 +0,0 @@
-/*
- * QEMU 8253/8254 interval timer emulation
- *
- * Copyright (c) 2003-2004 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/i386/pc.h"
-#include "hw/isa/isa.h"
-#include "qemu/timer.h"
-#include "hw/timer/i8254.h"
-#include "hw/timer/i8254_internal.h"
-
-//#define DEBUG_PIT
-
-#define RW_STATE_LSB 1
-#define RW_STATE_MSB 2
-#define RW_STATE_WORD0 3
-#define RW_STATE_WORD1 4
-
-static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
-
-static int pit_get_count(PITChannelState *s)
-{
-    uint64_t d;
-    int counter;
-
-    d = muldiv64(qemu_get_clock_ns(vm_clock) - s->count_load_time, PIT_FREQ,
-                 get_ticks_per_sec());
-    switch(s->mode) {
-    case 0:
-    case 1:
-    case 4:
-    case 5:
-        counter = (s->count - d) & 0xffff;
-        break;
-    case 3:
-        /* XXX: may be incorrect for odd counts */
-        counter = s->count - ((2 * d) % s->count);
-        break;
-    default:
-        counter = s->count - (d % s->count);
-        break;
-    }
-    return counter;
-}
-
-/* val must be 0 or 1 */
-static void pit_set_channel_gate(PITCommonState *s, PITChannelState *sc,
-                                 int val)
-{
-    switch (sc->mode) {
-    default:
-    case 0:
-    case 4:
-        /* XXX: just disable/enable counting */
-        break;
-    case 1:
-    case 5:
-        if (sc->gate < val) {
-            /* restart counting on rising edge */
-            sc->count_load_time = qemu_get_clock_ns(vm_clock);
-            pit_irq_timer_update(sc, sc->count_load_time);
-        }
-        break;
-    case 2:
-    case 3:
-        if (sc->gate < val) {
-            /* restart counting on rising edge */
-            sc->count_load_time = qemu_get_clock_ns(vm_clock);
-            pit_irq_timer_update(sc, sc->count_load_time);
-        }
-        /* XXX: disable/enable counting */
-        break;
-    }
-    sc->gate = val;
-}
-
-static inline void pit_load_count(PITChannelState *s, int val)
-{
-    if (val == 0)
-        val = 0x10000;
-    s->count_load_time = qemu_get_clock_ns(vm_clock);
-    s->count = val;
-    pit_irq_timer_update(s, s->count_load_time);
-}
-
-/* if already latched, do not latch again */
-static void pit_latch_count(PITChannelState *s)
-{
-    if (!s->count_latched) {
-        s->latched_count = pit_get_count(s);
-        s->count_latched = s->rw_mode;
-    }
-}
-
-static void pit_ioport_write(void *opaque, hwaddr addr,
-                             uint64_t val, unsigned size)
-{
-    PITCommonState *pit = opaque;
-    int channel, access;
-    PITChannelState *s;
-
-    addr &= 3;
-    if (addr == 3) {
-        channel = val >> 6;
-        if (channel == 3) {
-            /* read back command */
-            for(channel = 0; channel < 3; channel++) {
-                s = &pit->channels[channel];
-                if (val & (2 << channel)) {
-                    if (!(val & 0x20)) {
-                        pit_latch_count(s);
-                    }
-                    if (!(val & 0x10) && !s->status_latched) {
-                        /* status latch */
-                        /* XXX: add BCD and null count */
-                        s->status =
-                            (pit_get_out(s,
-                                         qemu_get_clock_ns(vm_clock)) << 7) |
-                            (s->rw_mode << 4) |
-                            (s->mode << 1) |
-                            s->bcd;
-                        s->status_latched = 1;
-                    }
-                }
-            }
-        } else {
-            s = &pit->channels[channel];
-            access = (val >> 4) & 3;
-            if (access == 0) {
-                pit_latch_count(s);
-            } else {
-                s->rw_mode = access;
-                s->read_state = access;
-                s->write_state = access;
-
-                s->mode = (val >> 1) & 7;
-                s->bcd = val & 1;
-                /* XXX: update irq timer ? */
-            }
-        }
-    } else {
-        s = &pit->channels[addr];
-        switch(s->write_state) {
-        default:
-        case RW_STATE_LSB:
-            pit_load_count(s, val);
-            break;
-        case RW_STATE_MSB:
-            pit_load_count(s, val << 8);
-            break;
-        case RW_STATE_WORD0:
-            s->write_latch = val;
-            s->write_state = RW_STATE_WORD1;
-            break;
-        case RW_STATE_WORD1:
-            pit_load_count(s, s->write_latch | (val << 8));
-            s->write_state = RW_STATE_WORD0;
-            break;
-        }
-    }
-}
-
-static uint64_t pit_ioport_read(void *opaque, hwaddr addr,
-                                unsigned size)
-{
-    PITCommonState *pit = opaque;
-    int ret, count;
-    PITChannelState *s;
-
-    addr &= 3;
-    s = &pit->channels[addr];
-    if (s->status_latched) {
-        s->status_latched = 0;
-        ret = s->status;
-    } else if (s->count_latched) {
-        switch(s->count_latched) {
-        default:
-        case RW_STATE_LSB:
-            ret = s->latched_count & 0xff;
-            s->count_latched = 0;
-            break;
-        case RW_STATE_MSB:
-            ret = s->latched_count >> 8;
-            s->count_latched = 0;
-            break;
-        case RW_STATE_WORD0:
-            ret = s->latched_count & 0xff;
-            s->count_latched = RW_STATE_MSB;
-            break;
-        }
-    } else {
-        switch(s->read_state) {
-        default:
-        case RW_STATE_LSB:
-            count = pit_get_count(s);
-            ret = count & 0xff;
-            break;
-        case RW_STATE_MSB:
-            count = pit_get_count(s);
-            ret = (count >> 8) & 0xff;
-            break;
-        case RW_STATE_WORD0:
-            count = pit_get_count(s);
-            ret = count & 0xff;
-            s->read_state = RW_STATE_WORD1;
-            break;
-        case RW_STATE_WORD1:
-            count = pit_get_count(s);
-            ret = (count >> 8) & 0xff;
-            s->read_state = RW_STATE_WORD0;
-            break;
-        }
-    }
-    return ret;
-}
-
-static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
-{
-    int64_t expire_time;
-    int irq_level;
-
-    if (!s->irq_timer || s->irq_disabled) {
-        return;
-    }
-    expire_time = pit_get_next_transition_time(s, current_time);
-    irq_level = pit_get_out(s, current_time);
-    qemu_set_irq(s->irq, irq_level);
-#ifdef DEBUG_PIT
-    printf("irq_level=%d next_delay=%f\n",
-           irq_level,
-           (double)(expire_time - current_time) / get_ticks_per_sec());
-#endif
-    s->next_transition_time = expire_time;
-    if (expire_time != -1)
-        qemu_mod_timer(s->irq_timer, expire_time);
-    else
-        qemu_del_timer(s->irq_timer);
-}
-
-static void pit_irq_timer(void *opaque)
-{
-    PITChannelState *s = opaque;
-
-    pit_irq_timer_update(s, s->next_transition_time);
-}
-
-static void pit_reset(DeviceState *dev)
-{
-    PITCommonState *pit = DO_UPCAST(PITCommonState, dev.qdev, dev);
-    PITChannelState *s;
-
-    pit_reset_common(pit);
-
-    s = &pit->channels[0];
-    if (!s->irq_disabled) {
-        qemu_mod_timer(s->irq_timer, s->next_transition_time);
-    }
-}
-
-/* When HPET is operating in legacy mode, suppress the ignored timer IRQ,
- * reenable it when legacy mode is left again. */
-static void pit_irq_control(void *opaque, int n, int enable)
-{
-    PITCommonState *pit = opaque;
-    PITChannelState *s = &pit->channels[0];
-
-    if (enable) {
-        s->irq_disabled = 0;
-        pit_irq_timer_update(s, qemu_get_clock_ns(vm_clock));
-    } else {
-        s->irq_disabled = 1;
-        qemu_del_timer(s->irq_timer);
-    }
-}
-
-static const MemoryRegionOps pit_ioport_ops = {
-    .read = pit_ioport_read,
-    .write = pit_ioport_write,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void pit_post_load(PITCommonState *s)
-{
-    PITChannelState *sc = &s->channels[0];
-
-    if (sc->next_transition_time != -1) {
-        qemu_mod_timer(sc->irq_timer, sc->next_transition_time);
-    } else {
-        qemu_del_timer(sc->irq_timer);
-    }
-}
-
-static int pit_initfn(PITCommonState *pit)
-{
-    PITChannelState *s;
-
-    s = &pit->channels[0];
-    /* the timer 0 is connected to an IRQ */
-    s->irq_timer = qemu_new_timer_ns(vm_clock, pit_irq_timer, s);
-    qdev_init_gpio_out(&pit->dev.qdev, &s->irq, 1);
-
-    memory_region_init_io(&pit->ioports, &pit_ioport_ops, pit, "pit", 4);
-
-    qdev_init_gpio_in(&pit->dev.qdev, pit_irq_control, 1);
-
-    return 0;
-}
-
-static Property pit_properties[] = {
-    DEFINE_PROP_HEX32("iobase", PITCommonState, iobase,  -1),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void pit_class_initfn(ObjectClass *klass, void *data)
-{
-    PITCommonClass *k = PIT_COMMON_CLASS(klass);
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    k->init = pit_initfn;
-    k->set_channel_gate = pit_set_channel_gate;
-    k->get_channel_info = pit_get_channel_info_common;
-    k->post_load = pit_post_load;
-    dc->reset = pit_reset;
-    dc->props = pit_properties;
-}
-
-static const TypeInfo pit_info = {
-    .name          = "isa-pit",
-    .parent        = TYPE_PIT_COMMON,
-    .instance_size = sizeof(PITCommonState),
-    .class_init    = pit_class_initfn,
-};
-
-static void pit_register_types(void)
-{
-    type_register_static(&pit_info);
-}
-
-type_init(pit_register_types)
diff --git a/hw/i8254_common.c b/hw/i8254_common.c
deleted file mode 100644 (file)
index 5342df4..0000000
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * QEMU 8253/8254 - common bits of emulated and KVM kernel model
- *
- * Copyright (c) 2003-2004 Fabrice Bellard
- * Copyright (c) 2012      Jan Kiszka, Siemens AG
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/i386/pc.h"
-#include "hw/isa/isa.h"
-#include "qemu/timer.h"
-#include "hw/timer/i8254.h"
-#include "hw/timer/i8254_internal.h"
-
-/* val must be 0 or 1 */
-void pit_set_gate(ISADevice *dev, int channel, int val)
-{
-    PITCommonState *pit = PIT_COMMON(dev);
-    PITChannelState *s = &pit->channels[channel];
-    PITCommonClass *c = PIT_COMMON_GET_CLASS(pit);
-
-    c->set_channel_gate(pit, s, val);
-}
-
-/* get pit output bit */
-int pit_get_out(PITChannelState *s, int64_t current_time)
-{
-    uint64_t d;
-    int out;
-
-    d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
-                 get_ticks_per_sec());
-    switch (s->mode) {
-    default:
-    case 0:
-        out = (d >= s->count);
-        break;
-    case 1:
-        out = (d < s->count);
-        break;
-    case 2:
-        if ((d % s->count) == 0 && d != 0) {
-            out = 1;
-        } else {
-            out = 0;
-        }
-        break;
-    case 3:
-        out = (d % s->count) < ((s->count + 1) >> 1);
-        break;
-    case 4:
-    case 5:
-        out = (d == s->count);
-        break;
-    }
-    return out;
-}
-
-/* return -1 if no transition will occur.  */
-int64_t pit_get_next_transition_time(PITChannelState *s, int64_t current_time)
-{
-    uint64_t d, next_time, base;
-    int period2;
-
-    d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
-                 get_ticks_per_sec());
-    switch (s->mode) {
-    default:
-    case 0:
-    case 1:
-        if (d < s->count) {
-            next_time = s->count;
-        } else {
-            return -1;
-        }
-        break;
-    case 2:
-        base = (d / s->count) * s->count;
-        if ((d - base) == 0 && d != 0) {
-            next_time = base + s->count;
-        } else {
-            next_time = base + s->count + 1;
-        }
-        break;
-    case 3:
-        base = (d / s->count) * s->count;
-        period2 = ((s->count + 1) >> 1);
-        if ((d - base) < period2) {
-            next_time = base + period2;
-        } else {
-            next_time = base + s->count;
-        }
-        break;
-    case 4:
-    case 5:
-        if (d < s->count) {
-            next_time = s->count;
-        } else if (d == s->count) {
-            next_time = s->count + 1;
-        } else {
-            return -1;
-        }
-        break;
-    }
-    /* convert to timer units */
-    next_time = s->count_load_time + muldiv64(next_time, get_ticks_per_sec(),
-                                              PIT_FREQ);
-    /* fix potential rounding problems */
-    /* XXX: better solution: use a clock at PIT_FREQ Hz */
-    if (next_time <= current_time) {
-        next_time = current_time + 1;
-    }
-    return next_time;
-}
-
-void pit_get_channel_info_common(PITCommonState *s, PITChannelState *sc,
-                                 PITChannelInfo *info)
-{
-    info->gate = sc->gate;
-    info->mode = sc->mode;
-    info->initial_count = sc->count;
-    info->out = pit_get_out(sc, qemu_get_clock_ns(vm_clock));
-}
-
-void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info)
-{
-    PITCommonState *pit = PIT_COMMON(dev);
-    PITChannelState *s = &pit->channels[channel];
-    PITCommonClass *c = PIT_COMMON_GET_CLASS(pit);
-
-    c->get_channel_info(pit, s, info);
-}
-
-void pit_reset_common(PITCommonState *pit)
-{
-    PITChannelState *s;
-    int i;
-
-    for (i = 0; i < 3; i++) {
-        s = &pit->channels[i];
-        s->mode = 3;
-        s->gate = (i != 2);
-        s->count_load_time = qemu_get_clock_ns(vm_clock);
-        s->count = 0x10000;
-        if (i == 0 && !s->irq_disabled) {
-            s->next_transition_time =
-                pit_get_next_transition_time(s, s->count_load_time);
-        }
-    }
-}
-
-static int pit_init_common(ISADevice *dev)
-{
-    PITCommonState *pit = PIT_COMMON(dev);
-    PITCommonClass *c = PIT_COMMON_GET_CLASS(pit);
-    int ret;
-
-    ret = c->init(pit);
-    if (ret < 0) {
-        return ret;
-    }
-
-    isa_register_ioport(dev, &pit->ioports, pit->iobase);
-
-    qdev_set_legacy_instance_id(&dev->qdev, pit->iobase, 2);
-
-    return 0;
-}
-
-static const VMStateDescription vmstate_pit_channel = {
-    .name = "pit channel",
-    .version_id = 2,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .fields = (VMStateField[]) {
-        VMSTATE_INT32(count, PITChannelState),
-        VMSTATE_UINT16(latched_count, PITChannelState),
-        VMSTATE_UINT8(count_latched, PITChannelState),
-        VMSTATE_UINT8(status_latched, PITChannelState),
-        VMSTATE_UINT8(status, PITChannelState),
-        VMSTATE_UINT8(read_state, PITChannelState),
-        VMSTATE_UINT8(write_state, PITChannelState),
-        VMSTATE_UINT8(write_latch, PITChannelState),
-        VMSTATE_UINT8(rw_mode, PITChannelState),
-        VMSTATE_UINT8(mode, PITChannelState),
-        VMSTATE_UINT8(bcd, PITChannelState),
-        VMSTATE_UINT8(gate, PITChannelState),
-        VMSTATE_INT64(count_load_time, PITChannelState),
-        VMSTATE_INT64(next_transition_time, PITChannelState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int pit_load_old(QEMUFile *f, void *opaque, int version_id)
-{
-    PITCommonState *pit = opaque;
-    PITCommonClass *c = PIT_COMMON_GET_CLASS(pit);
-    PITChannelState *s;
-    int i;
-
-    if (version_id != 1) {
-        return -EINVAL;
-    }
-
-    for (i = 0; i < 3; i++) {
-        s = &pit->channels[i];
-        s->count = qemu_get_be32(f);
-        qemu_get_be16s(f, &s->latched_count);
-        qemu_get_8s(f, &s->count_latched);
-        qemu_get_8s(f, &s->status_latched);
-        qemu_get_8s(f, &s->status);
-        qemu_get_8s(f, &s->read_state);
-        qemu_get_8s(f, &s->write_state);
-        qemu_get_8s(f, &s->write_latch);
-        qemu_get_8s(f, &s->rw_mode);
-        qemu_get_8s(f, &s->mode);
-        qemu_get_8s(f, &s->bcd);
-        qemu_get_8s(f, &s->gate);
-        s->count_load_time = qemu_get_be64(f);
-        s->irq_disabled = 0;
-        if (i == 0) {
-            s->next_transition_time = qemu_get_be64(f);
-        }
-    }
-    if (c->post_load) {
-        c->post_load(pit);
-    }
-    return 0;
-}
-
-static void pit_dispatch_pre_save(void *opaque)
-{
-    PITCommonState *s = opaque;
-    PITCommonClass *c = PIT_COMMON_GET_CLASS(s);
-
-    if (c->pre_save) {
-        c->pre_save(s);
-    }
-}
-
-static int pit_dispatch_post_load(void *opaque, int version_id)
-{
-    PITCommonState *s = opaque;
-    PITCommonClass *c = PIT_COMMON_GET_CLASS(s);
-
-    if (c->post_load) {
-        c->post_load(s);
-    }
-    return 0;
-}
-
-static const VMStateDescription vmstate_pit_common = {
-    .name = "i8254",
-    .version_id = 3,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 1,
-    .load_state_old = pit_load_old,
-    .pre_save = pit_dispatch_pre_save,
-    .post_load = pit_dispatch_post_load,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32_V(channels[0].irq_disabled, PITCommonState, 3),
-        VMSTATE_STRUCT_ARRAY(channels, PITCommonState, 3, 2,
-                             vmstate_pit_channel, PITChannelState),
-        VMSTATE_INT64(channels[0].next_transition_time,
-                      PITCommonState), /* formerly irq_timer */
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void pit_common_class_init(ObjectClass *klass, void *data)
-{
-    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    ic->init = pit_init_common;
-    dc->vmsd = &vmstate_pit_common;
-    dc->no_user = 1;
-}
-
-static const TypeInfo pit_common_type = {
-    .name          = TYPE_PIT_COMMON,
-    .parent        = TYPE_ISA_DEVICE,
-    .instance_size = sizeof(PITCommonState),
-    .class_size    = sizeof(PITCommonClass),
-    .class_init    = pit_common_class_init,
-    .abstract      = true,
-};
-
-static void register_devices(void)
-{
-    type_register_static(&pit_common_type);
-}
-
-type_init(register_devices);
diff --git a/hw/i8259.c b/hw/i8259.c
deleted file mode 100644 (file)
index ce14bd0..0000000
+++ /dev/null
@@ -1,496 +0,0 @@
-/*
- * QEMU 8259 interrupt controller emulation
- *
- * Copyright (c) 2003-2004 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/i386/pc.h"
-#include "hw/isa/isa.h"
-#include "monitor/monitor.h"
-#include "qemu/timer.h"
-#include "hw/isa/i8259_internal.h"
-
-/* debug PIC */
-//#define DEBUG_PIC
-
-#ifdef DEBUG_PIC
-#define DPRINTF(fmt, ...)                                       \
-    do { printf("pic: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...)
-#endif
-
-//#define DEBUG_IRQ_LATENCY
-//#define DEBUG_IRQ_COUNT
-
-#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
-static int irq_level[16];
-#endif
-#ifdef DEBUG_IRQ_COUNT
-static uint64_t irq_count[16];
-#endif
-#ifdef DEBUG_IRQ_LATENCY
-static int64_t irq_time[16];
-#endif
-DeviceState *isa_pic;
-static PICCommonState *slave_pic;
-
-/* return the highest priority found in mask (highest = smallest
-   number). Return 8 if no irq */
-static int get_priority(PICCommonState *s, int mask)
-{
-    int priority;
-
-    if (mask == 0) {
-        return 8;
-    }
-    priority = 0;
-    while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) {
-        priority++;
-    }
-    return priority;
-}
-
-/* return the pic wanted interrupt. return -1 if none */
-static int pic_get_irq(PICCommonState *s)
-{
-    int mask, cur_priority, priority;
-
-    mask = s->irr & ~s->imr;
-    priority = get_priority(s, mask);
-    if (priority == 8) {
-        return -1;
-    }
-    /* compute current priority. If special fully nested mode on the
-       master, the IRQ coming from the slave is not taken into account
-       for the priority computation. */
-    mask = s->isr;
-    if (s->special_mask) {
-        mask &= ~s->imr;
-    }
-    if (s->special_fully_nested_mode && s->master) {
-        mask &= ~(1 << 2);
-    }
-    cur_priority = get_priority(s, mask);
-    if (priority < cur_priority) {
-        /* higher priority found: an irq should be generated */
-        return (priority + s->priority_add) & 7;
-    } else {
-        return -1;
-    }
-}
-
-/* Update INT output. Must be called every time the output may have changed. */
-static void pic_update_irq(PICCommonState *s)
-{
-    int irq;
-
-    irq = pic_get_irq(s);
-    if (irq >= 0) {
-        DPRINTF("pic%d: imr=%x irr=%x padd=%d\n",
-                s->master ? 0 : 1, s->imr, s->irr, s->priority_add);
-        qemu_irq_raise(s->int_out[0]);
-    } else {
-        qemu_irq_lower(s->int_out[0]);
-    }
-}
-
-/* set irq level. If an edge is detected, then the IRR is set to 1 */
-static void pic_set_irq(void *opaque, int irq, int level)
-{
-    PICCommonState *s = opaque;
-    int mask = 1 << irq;
-
-#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) || \
-    defined(DEBUG_IRQ_LATENCY)
-    int irq_index = s->master ? irq : irq + 8;
-#endif
-#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
-    if (level != irq_level[irq_index]) {
-        DPRINTF("pic_set_irq: irq=%d level=%d\n", irq_index, level);
-        irq_level[irq_index] = level;
-#ifdef DEBUG_IRQ_COUNT
-        if (level == 1) {
-            irq_count[irq_index]++;
-        }
-#endif
-    }
-#endif
-#ifdef DEBUG_IRQ_LATENCY
-    if (level) {
-        irq_time[irq_index] = qemu_get_clock_ns(vm_clock);
-    }
-#endif
-
-    if (s->elcr & mask) {
-        /* level triggered */
-        if (level) {
-            s->irr |= mask;
-            s->last_irr |= mask;
-        } else {
-            s->irr &= ~mask;
-            s->last_irr &= ~mask;
-        }
-    } else {
-        /* edge triggered */
-        if (level) {
-            if ((s->last_irr & mask) == 0) {
-                s->irr |= mask;
-            }
-            s->last_irr |= mask;
-        } else {
-            s->last_irr &= ~mask;
-        }
-    }
-    pic_update_irq(s);
-}
-
-/* acknowledge interrupt 'irq' */
-static void pic_intack(PICCommonState *s, int irq)
-{
-    if (s->auto_eoi) {
-        if (s->rotate_on_auto_eoi) {
-            s->priority_add = (irq + 1) & 7;
-        }
-    } else {
-        s->isr |= (1 << irq);
-    }
-    /* We don't clear a level sensitive interrupt here */
-    if (!(s->elcr & (1 << irq))) {
-        s->irr &= ~(1 << irq);
-    }
-    pic_update_irq(s);
-}
-
-int pic_read_irq(DeviceState *d)
-{
-    PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, d);
-    int irq, irq2, intno;
-
-    irq = pic_get_irq(s);
-    if (irq >= 0) {
-        if (irq == 2) {
-            irq2 = pic_get_irq(slave_pic);
-            if (irq2 >= 0) {
-                pic_intack(slave_pic, irq2);
-            } else {
-                /* spurious IRQ on slave controller */
-                irq2 = 7;
-            }
-            intno = slave_pic->irq_base + irq2;
-        } else {
-            intno = s->irq_base + irq;
-        }
-        pic_intack(s, irq);
-    } else {
-        /* spurious IRQ on host controller */
-        irq = 7;
-        intno = s->irq_base + irq;
-    }
-
-#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_LATENCY)
-    if (irq == 2) {
-        irq = irq2 + 8;
-    }
-#endif
-#ifdef DEBUG_IRQ_LATENCY
-    printf("IRQ%d latency=%0.3fus\n",
-           irq,
-           (double)(qemu_get_clock_ns(vm_clock) -
-                    irq_time[irq]) * 1000000.0 / get_ticks_per_sec());
-#endif
-    DPRINTF("pic_interrupt: irq=%d\n", irq);
-    return intno;
-}
-
-static void pic_init_reset(PICCommonState *s)
-{
-    pic_reset_common(s);
-    pic_update_irq(s);
-}
-
-static void pic_reset(DeviceState *dev)
-{
-    PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, dev);
-
-    s->elcr = 0;
-    pic_init_reset(s);
-}
-
-static void pic_ioport_write(void *opaque, hwaddr addr64,
-                             uint64_t val64, unsigned size)
-{
-    PICCommonState *s = opaque;
-    uint32_t addr = addr64;
-    uint32_t val = val64;
-    int priority, cmd, irq;
-
-    DPRINTF("write: addr=0x%02x val=0x%02x\n", addr, val);
-    if (addr == 0) {
-        if (val & 0x10) {
-            pic_init_reset(s);
-            s->init_state = 1;
-            s->init4 = val & 1;
-            s->single_mode = val & 2;
-            if (val & 0x08) {
-                hw_error("level sensitive irq not supported");
-            }
-        } else if (val & 0x08) {
-            if (val & 0x04) {
-                s->poll = 1;
-            }
-            if (val & 0x02) {
-                s->read_reg_select = val & 1;
-            }
-            if (val & 0x40) {
-                s->special_mask = (val >> 5) & 1;
-            }
-        } else {
-            cmd = val >> 5;
-            switch (cmd) {
-            case 0:
-            case 4:
-                s->rotate_on_auto_eoi = cmd >> 2;
-                break;
-            case 1: /* end of interrupt */
-            case 5:
-                priority = get_priority(s, s->isr);
-                if (priority != 8) {
-                    irq = (priority + s->priority_add) & 7;
-                    s->isr &= ~(1 << irq);
-                    if (cmd == 5) {
-                        s->priority_add = (irq + 1) & 7;
-                    }
-                    pic_update_irq(s);
-                }
-                break;
-            case 3:
-                irq = val & 7;
-                s->isr &= ~(1 << irq);
-                pic_update_irq(s);
-                break;
-            case 6:
-                s->priority_add = (val + 1) & 7;
-                pic_update_irq(s);
-                break;
-            case 7:
-                irq = val & 7;
-                s->isr &= ~(1 << irq);
-                s->priority_add = (irq + 1) & 7;
-                pic_update_irq(s);
-                break;
-            default:
-                /* no operation */
-                break;
-            }
-        }
-    } else {
-        switch (s->init_state) {
-        case 0:
-            /* normal mode */
-            s->imr = val;
-            pic_update_irq(s);
-            break;
-        case 1:
-            s->irq_base = val & 0xf8;
-            s->init_state = s->single_mode ? (s->init4 ? 3 : 0) : 2;
-            break;
-        case 2:
-            if (s->init4) {
-                s->init_state = 3;
-            } else {
-                s->init_state = 0;
-            }
-            break;
-        case 3:
-            s->special_fully_nested_mode = (val >> 4) & 1;
-            s->auto_eoi = (val >> 1) & 1;
-            s->init_state = 0;
-            break;
-        }
-    }
-}
-
-static uint64_t pic_ioport_read(void *opaque, hwaddr addr,
-                                unsigned size)
-{
-    PICCommonState *s = opaque;
-    int ret;
-
-    if (s->poll) {
-        ret = pic_get_irq(s);
-        if (ret >= 0) {
-            pic_intack(s, ret);
-            ret |= 0x80;
-        } else {
-            ret = 0;
-        }
-        s->poll = 0;
-    } else {
-        if (addr == 0) {
-            if (s->read_reg_select) {
-                ret = s->isr;
-            } else {
-                ret = s->irr;
-            }
-        } else {
-            ret = s->imr;
-        }
-    }
-    DPRINTF("read: addr=0x%02x val=0x%02x\n", addr, ret);
-    return ret;
-}
-
-int pic_get_output(DeviceState *d)
-{
-    PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, d);
-
-    return (pic_get_irq(s) >= 0);
-}
-
-static void elcr_ioport_write(void *opaque, hwaddr addr,
-                              uint64_t val, unsigned size)
-{
-    PICCommonState *s = opaque;
-    s->elcr = val & s->elcr_mask;
-}
-
-static uint64_t elcr_ioport_read(void *opaque, hwaddr addr,
-                                 unsigned size)
-{
-    PICCommonState *s = opaque;
-    return s->elcr;
-}
-
-static const MemoryRegionOps pic_base_ioport_ops = {
-    .read = pic_ioport_read,
-    .write = pic_ioport_write,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-static const MemoryRegionOps pic_elcr_ioport_ops = {
-    .read = elcr_ioport_read,
-    .write = elcr_ioport_write,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-static void pic_init(PICCommonState *s)
-{
-    memory_region_init_io(&s->base_io, &pic_base_ioport_ops, s, "pic", 2);
-    memory_region_init_io(&s->elcr_io, &pic_elcr_ioport_ops, s, "elcr", 1);
-
-    qdev_init_gpio_out(&s->dev.qdev, s->int_out, ARRAY_SIZE(s->int_out));
-    qdev_init_gpio_in(&s->dev.qdev, pic_set_irq, 8);
-}
-
-void pic_info(Monitor *mon, const QDict *qdict)
-{
-    int i;
-    PICCommonState *s;
-
-    if (!isa_pic) {
-        return;
-    }
-    for (i = 0; i < 2; i++) {
-        s = i == 0 ? DO_UPCAST(PICCommonState, dev.qdev, isa_pic) : slave_pic;
-        monitor_printf(mon, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d "
-                       "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n",
-                       i, s->irr, s->imr, s->isr, s->priority_add,
-                       s->irq_base, s->read_reg_select, s->elcr,
-                       s->special_fully_nested_mode);
-    }
-}
-
-void irq_info(Monitor *mon, const QDict *qdict)
-{
-#ifndef DEBUG_IRQ_COUNT
-    monitor_printf(mon, "irq statistic code not compiled.\n");
-#else
-    int i;
-    int64_t count;
-
-    monitor_printf(mon, "IRQ statistics:\n");
-    for (i = 0; i < 16; i++) {
-        count = irq_count[i];
-        if (count > 0) {
-            monitor_printf(mon, "%2d: %" PRId64 "\n", i, count);
-        }
-    }
-#endif
-}
-
-qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq)
-{
-    qemu_irq *irq_set;
-    ISADevice *dev;
-    int i;
-
-    irq_set = g_malloc(ISA_NUM_IRQS * sizeof(qemu_irq));
-
-    dev = i8259_init_chip("isa-i8259", bus, true);
-
-    qdev_connect_gpio_out(&dev->qdev, 0, parent_irq);
-    for (i = 0 ; i < 8; i++) {
-        irq_set[i] = qdev_get_gpio_in(&dev->qdev, i);
-    }
-
-    isa_pic = &dev->qdev;
-
-    dev = i8259_init_chip("isa-i8259", bus, false);
-
-    qdev_connect_gpio_out(&dev->qdev, 0, irq_set[2]);
-    for (i = 0 ; i < 8; i++) {
-        irq_set[i + 8] = qdev_get_gpio_in(&dev->qdev, i);
-    }
-
-    slave_pic = DO_UPCAST(PICCommonState, dev, dev);
-
-    return irq_set;
-}
-
-static void i8259_class_init(ObjectClass *klass, void *data)
-{
-    PICCommonClass *k = PIC_COMMON_CLASS(klass);
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    k->init = pic_init;
-    dc->reset = pic_reset;
-}
-
-static const TypeInfo i8259_info = {
-    .name       = "isa-i8259",
-    .instance_size = sizeof(PICCommonState),
-    .parent     = TYPE_PIC_COMMON,
-    .class_init = i8259_class_init,
-};
-
-static void pic_register_types(void)
-{
-    type_register_static(&i8259_info);
-}
-
-type_init(pic_register_types)
diff --git a/hw/i8259_common.c b/hw/i8259_common.c
deleted file mode 100644 (file)
index 996ba9d..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * QEMU 8259 - common bits of emulated and KVM kernel model
- *
- * Copyright (c) 2003-2004 Fabrice Bellard
- * Copyright (c) 2011      Jan Kiszka, Siemens AG
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/i386/pc.h"
-#include "hw/isa/i8259_internal.h"
-
-void pic_reset_common(PICCommonState *s)
-{
-    s->last_irr = 0;
-    s->irr &= s->elcr;
-    s->imr = 0;
-    s->isr = 0;
-    s->priority_add = 0;
-    s->irq_base = 0;
-    s->read_reg_select = 0;
-    s->poll = 0;
-    s->special_mask = 0;
-    s->init_state = 0;
-    s->auto_eoi = 0;
-    s->rotate_on_auto_eoi = 0;
-    s->special_fully_nested_mode = 0;
-    s->init4 = 0;
-    s->single_mode = 0;
-    /* Note: ELCR is not reset */
-}
-
-static void pic_dispatch_pre_save(void *opaque)
-{
-    PICCommonState *s = opaque;
-    PICCommonClass *info = PIC_COMMON_GET_CLASS(s);
-
-    if (info->pre_save) {
-        info->pre_save(s);
-    }
-}
-
-static int pic_dispatch_post_load(void *opaque, int version_id)
-{
-    PICCommonState *s = opaque;
-    PICCommonClass *info = PIC_COMMON_GET_CLASS(s);
-
-    if (info->post_load) {
-        info->post_load(s);
-    }
-    return 0;
-}
-
-static int pic_init_common(ISADevice *dev)
-{
-    PICCommonState *s = DO_UPCAST(PICCommonState, dev, dev);
-    PICCommonClass *info = PIC_COMMON_GET_CLASS(s);
-
-    info->init(s);
-
-    isa_register_ioport(NULL, &s->base_io, s->iobase);
-    if (s->elcr_addr != -1) {
-        isa_register_ioport(NULL, &s->elcr_io, s->elcr_addr);
-    }
-
-    qdev_set_legacy_instance_id(&s->dev.qdev, s->iobase, 1);
-
-    return 0;
-}
-
-ISADevice *i8259_init_chip(const char *name, ISABus *bus, bool master)
-{
-    ISADevice *dev;
-
-    dev = isa_create(bus, name);
-    qdev_prop_set_uint32(&dev->qdev, "iobase", master ? 0x20 : 0xa0);
-    qdev_prop_set_uint32(&dev->qdev, "elcr_addr", master ? 0x4d0 : 0x4d1);
-    qdev_prop_set_uint8(&dev->qdev, "elcr_mask", master ? 0xf8 : 0xde);
-    qdev_prop_set_bit(&dev->qdev, "master", master);
-    qdev_init_nofail(&dev->qdev);
-
-    return dev;
-}
-
-static const VMStateDescription vmstate_pic_common = {
-    .name = "i8259",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .pre_save = pic_dispatch_pre_save,
-    .post_load = pic_dispatch_post_load,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT8(last_irr, PICCommonState),
-        VMSTATE_UINT8(irr, PICCommonState),
-        VMSTATE_UINT8(imr, PICCommonState),
-        VMSTATE_UINT8(isr, PICCommonState),
-        VMSTATE_UINT8(priority_add, PICCommonState),
-        VMSTATE_UINT8(irq_base, PICCommonState),
-        VMSTATE_UINT8(read_reg_select, PICCommonState),
-        VMSTATE_UINT8(poll, PICCommonState),
-        VMSTATE_UINT8(special_mask, PICCommonState),
-        VMSTATE_UINT8(init_state, PICCommonState),
-        VMSTATE_UINT8(auto_eoi, PICCommonState),
-        VMSTATE_UINT8(rotate_on_auto_eoi, PICCommonState),
-        VMSTATE_UINT8(special_fully_nested_mode, PICCommonState),
-        VMSTATE_UINT8(init4, PICCommonState),
-        VMSTATE_UINT8(single_mode, PICCommonState),
-        VMSTATE_UINT8(elcr, PICCommonState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property pic_properties_common[] = {
-    DEFINE_PROP_HEX32("iobase", PICCommonState, iobase,  -1),
-    DEFINE_PROP_HEX32("elcr_addr", PICCommonState, elcr_addr,  -1),
-    DEFINE_PROP_HEX8("elcr_mask", PICCommonState, elcr_mask,  -1),
-    DEFINE_PROP_BIT("master", PICCommonState, master,  0, false),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void pic_common_class_init(ObjectClass *klass, void *data)
-{
-    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    dc->vmsd = &vmstate_pic_common;
-    dc->no_user = 1;
-    dc->props = pic_properties_common;
-    ic->init = pic_init_common;
-}
-
-static const TypeInfo pic_common_type = {
-    .name = TYPE_PIC_COMMON,
-    .parent = TYPE_ISA_DEVICE,
-    .instance_size = sizeof(PICCommonState),
-    .class_size = sizeof(PICCommonClass),
-    .class_init = pic_common_class_init,
-    .abstract = true,
-};
-
-static void register_types(void)
-{
-    type_register_static(&pic_common_type);
-}
-
-type_init(register_types);
diff --git a/hw/i82801b11.c b/hw/i82801b11.c
deleted file mode 100644 (file)
index 5807a92..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (c) 2006 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-/*
- * QEMU i82801b11 dmi-to-pci Bridge Emulation
- *
- *  Copyright (c) 2009, 2010, 2011
- *                Isaku Yamahata <yamahata at valinux co jp>
- *                VA Linux Systems Japan K.K.
- *  Copyright (C) 2012 Jason Baron <jbaron@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "hw/pci/pci.h"
-#include "hw/i386/ich9.h"
-
-
-/*****************************************************************************/
-/* ICH9 DMI-to-PCI bridge */
-#define I82801ba_SSVID_OFFSET   0x50
-#define I82801ba_SSVID_SVID     0
-#define I82801ba_SSVID_SSID     0
-
-typedef struct I82801b11Bridge {
-    PCIBridge br;
-} I82801b11Bridge;
-
-static int i82801b11_bridge_initfn(PCIDevice *d)
-{
-    int rc;
-
-    rc = pci_bridge_initfn(d, TYPE_PCI_BUS);
-    if (rc < 0) {
-        return rc;
-    }
-
-    rc = pci_bridge_ssvid_init(d, I82801ba_SSVID_OFFSET,
-                               I82801ba_SSVID_SVID, I82801ba_SSVID_SSID);
-    if (rc < 0) {
-        goto err_bridge;
-    }
-    pci_config_set_prog_interface(d->config, PCI_CLASS_BRDIGE_PCI_INF_SUB);
-    return 0;
-
-err_bridge:
-    pci_bridge_exitfn(d);
-
-    return rc;
-}
-
-static void i82801b11_bridge_class_init(ObjectClass *klass, void *data)
-{
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->is_bridge = 1;
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->device_id = PCI_DEVICE_ID_INTEL_82801BA_11;
-    k->revision = ICH9_D2P_A2_REVISION;
-    k->init = i82801b11_bridge_initfn;
-}
-
-static const TypeInfo i82801b11_bridge_info = {
-    .name          = "i82801b11-bridge",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(I82801b11Bridge),
-    .class_init    = i82801b11_bridge_class_init,
-};
-
-PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int sec_bus)
-{
-    PCIDevice *d;
-    PCIBridge *br;
-    char buf[16];
-    DeviceState *qdev;
-
-    d = pci_create_multifunction(bus, devfn, true, "i82801b11-bridge");
-    if (!d) {
-        return NULL;
-    }
-    br = DO_UPCAST(PCIBridge, dev, d);
-    qdev = &br->dev.qdev;
-
-    snprintf(buf, sizeof(buf), "pci.%d", sec_bus);
-    pci_bridge_map_irq(br, buf, pci_swizzle_map_irq_fn);
-    qdev_init_nofail(qdev);
-
-    return pci_bridge_get_sec_bus(br);
-}
-
-static void d2pbr_register(void)
-{
-    type_register_static(&i82801b11_bridge_info);
-}
-
-type_init(d2pbr_register);
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..824997e367e11c1ee405a980c2dbaaca3f1ca3ed 100644 (file)
@@ -0,0 +1,9 @@
+common-obj-$(CONFIG_ADB) += adb.o
+common-obj-y += hid.o
+common-obj-$(CONFIG_LM832X) += lm832x.o
+common-obj-$(CONFIG_PCKBD) += pckbd.o
+common-obj-$(CONFIG_PL050) += pl050.o
+common-obj-y += ps2.o
+common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o
+common-obj-$(CONFIG_TSC2005) += tsc2005.o
+common-obj-$(CONFIG_VMMOUSE) += vmmouse.o
diff --git a/hw/input/adb.c b/hw/input/adb.c
new file mode 100644 (file)
index 0000000..a75d3fd
--- /dev/null
@@ -0,0 +1,581 @@
+/*
+ * QEMU ADB support
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/input/adb.h"
+#include "ui/console.h"
+
+/* debug ADB */
+//#define DEBUG_ADB
+
+#ifdef DEBUG_ADB
+#define ADB_DPRINTF(fmt, ...) \
+do { printf("ADB: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define ADB_DPRINTF(fmt, ...)
+#endif
+
+/* ADB commands */
+#define ADB_BUSRESET           0x00
+#define ADB_FLUSH               0x01
+#define ADB_WRITEREG           0x08
+#define ADB_READREG            0x0c
+
+/* ADB device commands */
+#define ADB_CMD_SELF_TEST              0xff
+#define ADB_CMD_CHANGE_ID              0xfe
+#define ADB_CMD_CHANGE_ID_AND_ACT      0xfd
+#define ADB_CMD_CHANGE_ID_AND_ENABLE   0x00
+
+/* ADB default device IDs (upper 4 bits of ADB command byte) */
+#define ADB_DEVID_DONGLE   1
+#define ADB_DEVID_KEYBOARD 2
+#define ADB_DEVID_MOUSE    3
+#define ADB_DEVID_TABLET   4
+#define ADB_DEVID_MODEM    5
+#define ADB_DEVID_MISC     7
+
+/* error codes */
+#define ADB_RET_NOTPRESENT (-2)
+
+static void adb_device_reset(ADBDevice *d)
+{
+    qdev_reset_all(DEVICE(d));
+}
+
+int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len)
+{
+    ADBDevice *d;
+    int devaddr, cmd, i;
+
+    cmd = buf[0] & 0xf;
+    if (cmd == ADB_BUSRESET) {
+        for(i = 0; i < s->nb_devices; i++) {
+            d = s->devices[i];
+            adb_device_reset(d);
+        }
+        return 0;
+    }
+    devaddr = buf[0] >> 4;
+    for(i = 0; i < s->nb_devices; i++) {
+        d = s->devices[i];
+        if (d->devaddr == devaddr) {
+            ADBDeviceClass *adc = ADB_DEVICE_GET_CLASS(d);
+            return adc->devreq(d, obuf, buf, len);
+        }
+    }
+    return ADB_RET_NOTPRESENT;
+}
+
+/* XXX: move that to cuda ? */
+int adb_poll(ADBBusState *s, uint8_t *obuf)
+{
+    ADBDevice *d;
+    int olen, i;
+    uint8_t buf[1];
+
+    olen = 0;
+    for(i = 0; i < s->nb_devices; i++) {
+        if (s->poll_index >= s->nb_devices)
+            s->poll_index = 0;
+        d = s->devices[s->poll_index];
+        buf[0] = ADB_READREG | (d->devaddr << 4);
+        olen = adb_request(s, obuf + 1, buf, 1);
+        /* if there is data, we poll again the same device */
+        if (olen > 0) {
+            obuf[0] = buf[0];
+            olen++;
+            break;
+        }
+        s->poll_index++;
+    }
+    return olen;
+}
+
+static const TypeInfo adb_bus_type_info = {
+    .name = TYPE_ADB_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(ADBBusState),
+};
+
+static void adb_device_realizefn(DeviceState *dev, Error **errp)
+{
+    ADBDevice *d = ADB_DEVICE(dev);
+    ADBBusState *bus = ADB_BUS(qdev_get_parent_bus(dev));
+
+    if (bus->nb_devices >= MAX_ADB_DEVICES) {
+        return;
+    }
+
+    bus->devices[bus->nb_devices++] = d;
+}
+
+static void adb_device_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->realize = adb_device_realizefn;
+    dc->bus_type = TYPE_ADB_BUS;
+}
+
+static const TypeInfo adb_device_type_info = {
+    .name = TYPE_ADB_DEVICE,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(ADBDevice),
+    .abstract = true,
+    .class_init = adb_device_class_init,
+};
+
+/***************************************************************/
+/* Keyboard ADB device */
+
+#define ADB_KEYBOARD(obj) OBJECT_CHECK(KBDState, (obj), TYPE_ADB_KEYBOARD)
+
+typedef struct KBDState {
+    /*< private >*/
+    ADBDevice parent_obj;
+    /*< public >*/
+
+    uint8_t data[128];
+    int rptr, wptr, count;
+} KBDState;
+
+#define ADB_KEYBOARD_CLASS(class) \
+    OBJECT_CLASS_CHECK(ADBKeyboardClass, (class), TYPE_ADB_KEYBOARD)
+#define ADB_KEYBOARD_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(ADBKeyboardClass, (obj), TYPE_ADB_KEYBOARD)
+
+typedef struct ADBKeyboardClass {
+    /*< private >*/
+    ADBDeviceClass parent_class;
+    /*< public >*/
+
+    DeviceRealize parent_realize;
+} ADBKeyboardClass;
+
+static const uint8_t pc_to_adb_keycode[256] = {
+  0, 53, 18, 19, 20, 21, 23, 22, 26, 28, 25, 29, 27, 24, 51, 48,
+ 12, 13, 14, 15, 17, 16, 32, 34, 31, 35, 33, 30, 36, 54,  0,  1,
+  2,  3,  5,  4, 38, 40, 37, 41, 39, 50, 56, 42,  6,  7,  8,  9,
+ 11, 45, 46, 43, 47, 44,123, 67, 58, 49, 57,122,120, 99,118, 96,
+ 97, 98,100,101,109, 71,107, 89, 91, 92, 78, 86, 87, 88, 69, 83,
+ 84, 85, 82, 65,  0,  0, 10,103,111,  0,  0,110, 81,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0, 94,  0, 93,  0,  0,  0,  0,  0,  0,104,102,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 76,125,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,105,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0, 75,  0,  0,124,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0,  0,  0,115, 62,116,  0, 59,  0, 60,  0,119,
+ 61,121,114,117,  0,  0,  0,  0,  0,  0,  0, 55,126,  0,127,  0,
+  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+  0,  0,  0,  0,  0, 95,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+};
+
+static void adb_kbd_put_keycode(void *opaque, int keycode)
+{
+    KBDState *s = opaque;
+
+    if (s->count < sizeof(s->data)) {
+        s->data[s->wptr] = keycode;
+        if (++s->wptr == sizeof(s->data))
+            s->wptr = 0;
+        s->count++;
+    }
+}
+
+static int adb_kbd_poll(ADBDevice *d, uint8_t *obuf)
+{
+    static int ext_keycode;
+    KBDState *s = ADB_KEYBOARD(d);
+    int adb_keycode, keycode;
+    int olen;
+
+    olen = 0;
+    for(;;) {
+        if (s->count == 0)
+            break;
+        keycode = s->data[s->rptr];
+        if (++s->rptr == sizeof(s->data))
+            s->rptr = 0;
+        s->count--;
+
+        if (keycode == 0xe0) {
+            ext_keycode = 1;
+        } else {
+            if (ext_keycode)
+                adb_keycode =  pc_to_adb_keycode[keycode | 0x80];
+            else
+                adb_keycode =  pc_to_adb_keycode[keycode & 0x7f];
+            obuf[0] = adb_keycode | (keycode & 0x80);
+            /* NOTE: could put a second keycode if needed */
+            obuf[1] = 0xff;
+            olen = 2;
+            ext_keycode = 0;
+            break;
+        }
+    }
+    return olen;
+}
+
+static int adb_kbd_request(ADBDevice *d, uint8_t *obuf,
+                           const uint8_t *buf, int len)
+{
+    KBDState *s = ADB_KEYBOARD(d);
+    int cmd, reg, olen;
+
+    if ((buf[0] & 0x0f) == ADB_FLUSH) {
+        /* flush keyboard fifo */
+        s->wptr = s->rptr = s->count = 0;
+        return 0;
+    }
+
+    cmd = buf[0] & 0xc;
+    reg = buf[0] & 0x3;
+    olen = 0;
+    switch(cmd) {
+    case ADB_WRITEREG:
+        switch(reg) {
+        case 2:
+            /* LED status */
+            break;
+        case 3:
+            switch(buf[2]) {
+            case ADB_CMD_SELF_TEST:
+                break;
+            case ADB_CMD_CHANGE_ID:
+            case ADB_CMD_CHANGE_ID_AND_ACT:
+            case ADB_CMD_CHANGE_ID_AND_ENABLE:
+                d->devaddr = buf[1] & 0xf;
+                break;
+            default:
+                /* XXX: check this */
+                d->devaddr = buf[1] & 0xf;
+                d->handler = buf[2];
+                break;
+            }
+        }
+        break;
+    case ADB_READREG:
+        switch(reg) {
+        case 0:
+            olen = adb_kbd_poll(d, obuf);
+            break;
+        case 1:
+            break;
+        case 2:
+            obuf[0] = 0x00; /* XXX: check this */
+            obuf[1] = 0x07; /* led status */
+            olen = 2;
+            break;
+        case 3:
+            obuf[0] = d->handler;
+            obuf[1] = d->devaddr;
+            olen = 2;
+            break;
+        }
+        break;
+    }
+    return olen;
+}
+
+static const VMStateDescription vmstate_adb_kbd = {
+    .name = "adb_kbd",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_BUFFER(data, KBDState),
+        VMSTATE_INT32(rptr, KBDState),
+        VMSTATE_INT32(wptr, KBDState),
+        VMSTATE_INT32(count, KBDState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void adb_kbd_reset(DeviceState *dev)
+{
+    ADBDevice *d = ADB_DEVICE(dev);
+    KBDState *s = ADB_KEYBOARD(dev);
+
+    d->handler = 1;
+    d->devaddr = ADB_DEVID_KEYBOARD;
+    memset(s->data, 0, sizeof(s->data));
+    s->rptr = 0;
+    s->wptr = 0;
+    s->count = 0;
+}
+
+static void adb_kbd_realizefn(DeviceState *dev, Error **errp)
+{
+    ADBDevice *d = ADB_DEVICE(dev);
+    ADBKeyboardClass *akc = ADB_KEYBOARD_GET_CLASS(dev);
+
+    akc->parent_realize(dev, errp);
+
+    qemu_add_kbd_event_handler(adb_kbd_put_keycode, d);
+}
+
+static void adb_kbd_initfn(Object *obj)
+{
+    ADBDevice *d = ADB_DEVICE(obj);
+
+    d->devaddr = ADB_DEVID_KEYBOARD;
+}
+
+static void adb_kbd_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc);
+    ADBKeyboardClass *akc = ADB_KEYBOARD_CLASS(oc);
+
+    akc->parent_realize = dc->realize;
+    dc->realize = adb_kbd_realizefn;
+
+    adc->devreq = adb_kbd_request;
+    dc->reset = adb_kbd_reset;
+    dc->vmsd = &vmstate_adb_kbd;
+}
+
+static const TypeInfo adb_kbd_type_info = {
+    .name = TYPE_ADB_KEYBOARD,
+    .parent = TYPE_ADB_DEVICE,
+    .instance_size = sizeof(KBDState),
+    .instance_init = adb_kbd_initfn,
+    .class_init = adb_kbd_class_init,
+    .class_size = sizeof(ADBKeyboardClass),
+};
+
+/***************************************************************/
+/* Mouse ADB device */
+
+#define ADB_MOUSE(obj) OBJECT_CHECK(MouseState, (obj), TYPE_ADB_MOUSE)
+
+typedef struct MouseState {
+    /*< public >*/
+    ADBDevice parent_obj;
+    /*< private >*/
+
+    int buttons_state, last_buttons_state;
+    int dx, dy, dz;
+} MouseState;
+
+#define ADB_MOUSE_CLASS(class) \
+    OBJECT_CLASS_CHECK(ADBMouseClass, (class), TYPE_ADB_MOUSE)
+#define ADB_MOUSE_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(ADBMouseClass, (obj), TYPE_ADB_MOUSE)
+
+typedef struct ADBMouseClass {
+    /*< public >*/
+    ADBDeviceClass parent_class;
+    /*< private >*/
+
+    DeviceRealize parent_realize;
+} ADBMouseClass;
+
+static void adb_mouse_event(void *opaque,
+                            int dx1, int dy1, int dz1, int buttons_state)
+{
+    MouseState *s = opaque;
+
+    s->dx += dx1;
+    s->dy += dy1;
+    s->dz += dz1;
+    s->buttons_state = buttons_state;
+}
+
+
+static int adb_mouse_poll(ADBDevice *d, uint8_t *obuf)
+{
+    MouseState *s = ADB_MOUSE(d);
+    int dx, dy;
+
+    if (s->last_buttons_state == s->buttons_state &&
+        s->dx == 0 && s->dy == 0)
+        return 0;
+
+    dx = s->dx;
+    if (dx < -63)
+        dx = -63;
+    else if (dx > 63)
+        dx = 63;
+
+    dy = s->dy;
+    if (dy < -63)
+        dy = -63;
+    else if (dy > 63)
+        dy = 63;
+
+    s->dx -= dx;
+    s->dy -= dy;
+    s->last_buttons_state = s->buttons_state;
+
+    dx &= 0x7f;
+    dy &= 0x7f;
+
+    if (!(s->buttons_state & MOUSE_EVENT_LBUTTON))
+        dy |= 0x80;
+    if (!(s->buttons_state & MOUSE_EVENT_RBUTTON))
+        dx |= 0x80;
+
+    obuf[0] = dy;
+    obuf[1] = dx;
+    return 2;
+}
+
+static int adb_mouse_request(ADBDevice *d, uint8_t *obuf,
+                             const uint8_t *buf, int len)
+{
+    MouseState *s = ADB_MOUSE(d);
+    int cmd, reg, olen;
+
+    if ((buf[0] & 0x0f) == ADB_FLUSH) {
+        /* flush mouse fifo */
+        s->buttons_state = s->last_buttons_state;
+        s->dx = 0;
+        s->dy = 0;
+        s->dz = 0;
+        return 0;
+    }
+
+    cmd = buf[0] & 0xc;
+    reg = buf[0] & 0x3;
+    olen = 0;
+    switch(cmd) {
+    case ADB_WRITEREG:
+        ADB_DPRINTF("write reg %d val 0x%2.2x\n", reg, buf[1]);
+        switch(reg) {
+        case 2:
+            break;
+        case 3:
+            switch(buf[2]) {
+            case ADB_CMD_SELF_TEST:
+                break;
+            case ADB_CMD_CHANGE_ID:
+            case ADB_CMD_CHANGE_ID_AND_ACT:
+            case ADB_CMD_CHANGE_ID_AND_ENABLE:
+                d->devaddr = buf[1] & 0xf;
+                break;
+            default:
+                /* XXX: check this */
+                d->devaddr = buf[1] & 0xf;
+                break;
+            }
+        }
+        break;
+    case ADB_READREG:
+        switch(reg) {
+        case 0:
+            olen = adb_mouse_poll(d, obuf);
+            break;
+        case 1:
+            break;
+        case 3:
+            obuf[0] = d->handler;
+            obuf[1] = d->devaddr;
+            olen = 2;
+            break;
+        }
+        ADB_DPRINTF("read reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x\n", reg,
+                    obuf[0], obuf[1]);
+        break;
+    }
+    return olen;
+}
+
+static void adb_mouse_reset(DeviceState *dev)
+{
+    ADBDevice *d = ADB_DEVICE(dev);
+    MouseState *s = ADB_MOUSE(dev);
+
+    d->handler = 2;
+    d->devaddr = ADB_DEVID_MOUSE;
+    s->last_buttons_state = s->buttons_state = 0;
+    s->dx = s->dy = s->dz = 0;
+}
+
+static const VMStateDescription vmstate_adb_mouse = {
+    .name = "adb_mouse",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT32(buttons_state, MouseState),
+        VMSTATE_INT32(last_buttons_state, MouseState),
+        VMSTATE_INT32(dx, MouseState),
+        VMSTATE_INT32(dy, MouseState),
+        VMSTATE_INT32(dz, MouseState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void adb_mouse_realizefn(DeviceState *dev, Error **errp)
+{
+    MouseState *s = ADB_MOUSE(dev);
+    ADBMouseClass *amc = ADB_MOUSE_GET_CLASS(dev);
+
+    amc->parent_realize(dev, errp);
+
+    qemu_add_mouse_event_handler(adb_mouse_event, s, 0, "QEMU ADB Mouse");
+}
+
+static void adb_mouse_initfn(Object *obj)
+{
+    ADBDevice *d = ADB_DEVICE(obj);
+
+    d->devaddr = ADB_DEVID_MOUSE;
+}
+
+static void adb_mouse_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc);
+    ADBMouseClass *amc = ADB_MOUSE_CLASS(oc);
+
+    amc->parent_realize = dc->realize;
+    dc->realize = adb_mouse_realizefn;
+
+    adc->devreq = adb_mouse_request;
+    dc->reset = adb_mouse_reset;
+    dc->vmsd = &vmstate_adb_mouse;
+}
+
+static const TypeInfo adb_mouse_type_info = {
+    .name = TYPE_ADB_MOUSE,
+    .parent = TYPE_ADB_DEVICE,
+    .instance_size = sizeof(MouseState),
+    .instance_init = adb_mouse_initfn,
+    .class_init = adb_mouse_class_init,
+    .class_size = sizeof(ADBMouseClass),
+};
+
+
+static void adb_register_types(void)
+{
+    type_register_static(&adb_bus_type_info);
+    type_register_static(&adb_device_type_info);
+    type_register_static(&adb_kbd_type_info);
+    type_register_static(&adb_mouse_type_info);
+}
+
+type_init(adb_register_types)
diff --git a/hw/input/hid.c b/hw/input/hid.c
new file mode 100644 (file)
index 0000000..5fbde98
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+ * QEMU HID devices
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ * Copyright (c) 2007 OpenMoko, Inc.  (andrew@openedhand.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "ui/console.h"
+#include "qemu/timer.h"
+#include "hw/input/hid.h"
+
+#define HID_USAGE_ERROR_ROLLOVER        0x01
+#define HID_USAGE_POSTFAIL              0x02
+#define HID_USAGE_ERROR_UNDEFINED       0x03
+
+/* Indices are QEMU keycodes, values are from HID Usage Table.  Indices
+ * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d.  */
+static const uint8_t hid_usage_keys[0x100] = {
+    0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+    0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
+    0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
+    0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
+    0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
+    0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19,
+    0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
+    0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
+    0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
+    0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
+    0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44,
+    0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
+    0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65,
+
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
+    0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a,
+    0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
+    0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+bool hid_has_events(HIDState *hs)
+{
+    return hs->n > 0 || hs->idle_pending;
+}
+
+static void hid_idle_timer(void *opaque)
+{
+    HIDState *hs = opaque;
+
+    hs->idle_pending = true;
+    hs->event(hs);
+}
+
+static void hid_del_idle_timer(HIDState *hs)
+{
+    if (hs->idle_timer) {
+        qemu_del_timer(hs->idle_timer);
+        qemu_free_timer(hs->idle_timer);
+        hs->idle_timer = NULL;
+    }
+}
+
+void hid_set_next_idle(HIDState *hs)
+{
+    if (hs->idle) {
+        uint64_t expire_time = qemu_get_clock_ns(vm_clock) +
+                               get_ticks_per_sec() * hs->idle * 4 / 1000;
+        if (!hs->idle_timer) {
+            hs->idle_timer = qemu_new_timer_ns(vm_clock, hid_idle_timer, hs);
+        }
+        qemu_mod_timer_ns(hs->idle_timer, expire_time);
+    } else {
+        hid_del_idle_timer(hs);
+    }
+}
+
+static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons)
+{
+    e->xdx = e->ydy = e->dz = 0;
+    e->buttons_state = buttons;
+}
+
+static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel,
+                                      int x1, int y1, int z1) {
+    if (xyrel) {
+        e->xdx += x1;
+        e->ydy += y1;
+    } else {
+        e->xdx = x1;
+        e->ydy = y1;
+        /* Windows drivers do not like the 0/0 position and ignore such
+         * events. */
+        if (!(x1 | y1)) {
+            e->xdx = 1;
+        }
+    }
+    e->dz += z1;
+}
+
+static void hid_pointer_event(void *opaque,
+                              int x1, int y1, int z1, int buttons_state)
+{
+    HIDState *hs = opaque;
+    unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK;
+    unsigned previous_slot = (use_slot - 1) & QUEUE_MASK;
+
+    /* We combine events where feasible to keep the queue small.  We shouldn't
+     * combine anything with the first event of a particular button state, as
+     * that would change the location of the button state change.  When the
+     * queue is empty, a second event is needed because we don't know if
+     * the first event changed the button state.  */
+    if (hs->n == QUEUE_LENGTH) {
+        /* Queue full.  Discard old button state, combine motion normally.  */
+        hs->ptr.queue[use_slot].buttons_state = buttons_state;
+    } else if (hs->n < 2 ||
+               hs->ptr.queue[use_slot].buttons_state != buttons_state ||
+               hs->ptr.queue[previous_slot].buttons_state !=
+               hs->ptr.queue[use_slot].buttons_state) {
+        /* Cannot or should not combine, so add an empty item to the queue.  */
+        QUEUE_INCR(use_slot);
+        hs->n++;
+        hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state);
+    }
+    hid_pointer_event_combine(&hs->ptr.queue[use_slot],
+                              hs->kind == HID_MOUSE,
+                              x1, y1, z1);
+    hs->event(hs);
+}
+
+static void hid_keyboard_event(void *opaque, int keycode)
+{
+    HIDState *hs = opaque;
+    int slot;
+
+    if (hs->n == QUEUE_LENGTH) {
+        fprintf(stderr, "usb-kbd: warning: key event queue full\n");
+        return;
+    }
+    slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
+    hs->kbd.keycodes[slot] = keycode;
+    hs->event(hs);
+}
+
+static void hid_keyboard_process_keycode(HIDState *hs)
+{
+    uint8_t hid_code, key;
+    int i, keycode, slot;
+
+    if (hs->n == 0) {
+        return;
+    }
+    slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
+    keycode = hs->kbd.keycodes[slot];
+
+    key = keycode & 0x7f;
+    hid_code = hid_usage_keys[key | ((hs->kbd.modifiers >> 1) & (1 << 7))];
+    hs->kbd.modifiers &= ~(1 << 8);
+
+    switch (hid_code) {
+    case 0x00:
+        return;
+
+    case 0xe0:
+        if (hs->kbd.modifiers & (1 << 9)) {
+            hs->kbd.modifiers ^= 3 << 8;
+            return;
+        }
+    case 0xe1 ... 0xe7:
+        if (keycode & (1 << 7)) {
+            hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f));
+            return;
+        }
+    case 0xe8 ... 0xef:
+        hs->kbd.modifiers |= 1 << (hid_code & 0x0f);
+        return;
+    }
+
+    if (keycode & (1 << 7)) {
+        for (i = hs->kbd.keys - 1; i >= 0; i--) {
+            if (hs->kbd.key[i] == hid_code) {
+                hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys];
+                hs->kbd.key[hs->kbd.keys] = 0x00;
+                break;
+            }
+        }
+        if (i < 0) {
+            return;
+        }
+    } else {
+        for (i = hs->kbd.keys - 1; i >= 0; i--) {
+            if (hs->kbd.key[i] == hid_code) {
+                break;
+            }
+        }
+        if (i < 0) {
+            if (hs->kbd.keys < sizeof(hs->kbd.key)) {
+                hs->kbd.key[hs->kbd.keys++] = hid_code;
+            }
+        } else {
+            return;
+        }
+    }
+}
+
+static inline int int_clamp(int val, int vmin, int vmax)
+{
+    if (val < vmin) {
+        return vmin;
+    } else if (val > vmax) {
+        return vmax;
+    } else {
+        return val;
+    }
+}
+
+void hid_pointer_activate(HIDState *hs)
+{
+    if (!hs->ptr.mouse_grabbed) {
+        qemu_activate_mouse_event_handler(hs->ptr.eh_entry);
+        hs->ptr.mouse_grabbed = 1;
+    }
+}
+
+int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
+{
+    int dx, dy, dz, b, l;
+    int index;
+    HIDPointerEvent *e;
+
+    hs->idle_pending = false;
+
+    hid_pointer_activate(hs);
+
+    /* When the buffer is empty, return the last event.  Relative
+       movements will all be zero.  */
+    index = (hs->n ? hs->head : hs->head - 1);
+    e = &hs->ptr.queue[index & QUEUE_MASK];
+
+    if (hs->kind == HID_MOUSE) {
+        dx = int_clamp(e->xdx, -127, 127);
+        dy = int_clamp(e->ydy, -127, 127);
+        e->xdx -= dx;
+        e->ydy -= dy;
+    } else {
+        dx = e->xdx;
+        dy = e->ydy;
+    }
+    dz = int_clamp(e->dz, -127, 127);
+    e->dz -= dz;
+
+    b = 0;
+    if (e->buttons_state & MOUSE_EVENT_LBUTTON) {
+        b |= 0x01;
+    }
+    if (e->buttons_state & MOUSE_EVENT_RBUTTON) {
+        b |= 0x02;
+    }
+    if (e->buttons_state & MOUSE_EVENT_MBUTTON) {
+        b |= 0x04;
+    }
+
+    if (hs->n &&
+        !e->dz &&
+        (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
+        /* that deals with this event */
+        QUEUE_INCR(hs->head);
+        hs->n--;
+    }
+
+    /* Appears we have to invert the wheel direction */
+    dz = 0 - dz;
+    l = 0;
+    switch (hs->kind) {
+    case HID_MOUSE:
+        if (len > l) {
+            buf[l++] = b;
+        }
+        if (len > l) {
+            buf[l++] = dx;
+        }
+        if (len > l) {
+            buf[l++] = dy;
+        }
+        if (len > l) {
+            buf[l++] = dz;
+        }
+        break;
+
+    case HID_TABLET:
+        if (len > l) {
+            buf[l++] = b;
+        }
+        if (len > l) {
+            buf[l++] = dx & 0xff;
+        }
+        if (len > l) {
+            buf[l++] = dx >> 8;
+        }
+        if (len > l) {
+            buf[l++] = dy & 0xff;
+        }
+        if (len > l) {
+            buf[l++] = dy >> 8;
+        }
+        if (len > l) {
+            buf[l++] = dz;
+        }
+        break;
+
+    default:
+        abort();
+    }
+
+    return l;
+}
+
+int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len)
+{
+    hs->idle_pending = false;
+
+    if (len < 2) {
+        return 0;
+    }
+
+    hid_keyboard_process_keycode(hs);
+
+    buf[0] = hs->kbd.modifiers & 0xff;
+    buf[1] = 0;
+    if (hs->kbd.keys > 6) {
+        memset(buf + 2, HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2);
+    } else {
+        memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2);
+    }
+
+    return MIN(8, len);
+}
+
+int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len)
+{
+    if (len > 0) {
+        int ledstate = 0;
+        /* 0x01: Num Lock LED
+         * 0x02: Caps Lock LED
+         * 0x04: Scroll Lock LED
+         * 0x08: Compose LED
+         * 0x10: Kana LED */
+        hs->kbd.leds = buf[0];
+        if (hs->kbd.leds & 0x04) {
+            ledstate |= QEMU_SCROLL_LOCK_LED;
+        }
+        if (hs->kbd.leds & 0x01) {
+            ledstate |= QEMU_NUM_LOCK_LED;
+        }
+        if (hs->kbd.leds & 0x02) {
+            ledstate |= QEMU_CAPS_LOCK_LED;
+        }
+        kbd_put_ledstate(ledstate);
+    }
+    return 0;
+}
+
+void hid_reset(HIDState *hs)
+{
+    switch (hs->kind) {
+    case HID_KEYBOARD:
+        memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes));
+        memset(hs->kbd.key, 0, sizeof(hs->kbd.key));
+        hs->kbd.keys = 0;
+        break;
+    case HID_MOUSE:
+    case HID_TABLET:
+        memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue));
+        break;
+    }
+    hs->head = 0;
+    hs->n = 0;
+    hs->protocol = 1;
+    hs->idle = 0;
+    hs->idle_pending = false;
+    hid_del_idle_timer(hs);
+}
+
+void hid_free(HIDState *hs)
+{
+    switch (hs->kind) {
+    case HID_KEYBOARD:
+        qemu_remove_kbd_event_handler();
+        break;
+    case HID_MOUSE:
+    case HID_TABLET:
+        qemu_remove_mouse_event_handler(hs->ptr.eh_entry);
+        break;
+    }
+    hid_del_idle_timer(hs);
+}
+
+void hid_init(HIDState *hs, int kind, HIDEventFunc event)
+{
+    hs->kind = kind;
+    hs->event = event;
+
+    if (hs->kind == HID_KEYBOARD) {
+        qemu_add_kbd_event_handler(hid_keyboard_event, hs);
+    } else if (hs->kind == HID_MOUSE) {
+        hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
+                                                        0, "QEMU HID Mouse");
+    } else if (hs->kind == HID_TABLET) {
+        hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
+                                                        1, "QEMU HID Tablet");
+    }
+}
+
+static int hid_post_load(void *opaque, int version_id)
+{
+    HIDState *s = opaque;
+
+    hid_set_next_idle(s);
+    return 0;
+}
+
+static const VMStateDescription vmstate_hid_ptr_queue = {
+    .name = "HIDPointerEventQueue",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_INT32(xdx, HIDPointerEvent),
+        VMSTATE_INT32(ydy, HIDPointerEvent),
+        VMSTATE_INT32(dz, HIDPointerEvent),
+        VMSTATE_INT32(buttons_state, HIDPointerEvent),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+const VMStateDescription vmstate_hid_ptr_device = {
+    .name = "HIDPointerDevice",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = hid_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT_ARRAY(ptr.queue, HIDState, QUEUE_LENGTH, 0,
+                             vmstate_hid_ptr_queue, HIDPointerEvent),
+        VMSTATE_UINT32(head, HIDState),
+        VMSTATE_UINT32(n, HIDState),
+        VMSTATE_INT32(protocol, HIDState),
+        VMSTATE_UINT8(idle, HIDState),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+const VMStateDescription vmstate_hid_keyboard_device = {
+    .name = "HIDKeyboardDevice",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = hid_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(kbd.keycodes, HIDState, QUEUE_LENGTH),
+        VMSTATE_UINT32(head, HIDState),
+        VMSTATE_UINT32(n, HIDState),
+        VMSTATE_UINT16(kbd.modifiers, HIDState),
+        VMSTATE_UINT8(kbd.leds, HIDState),
+        VMSTATE_UINT8_ARRAY(kbd.key, HIDState, 16),
+        VMSTATE_INT32(kbd.keys, HIDState),
+        VMSTATE_INT32(protocol, HIDState),
+        VMSTATE_UINT8(idle, HIDState),
+        VMSTATE_END_OF_LIST(),
+    }
+};
diff --git a/hw/input/lm832x.c b/hw/input/lm832x.c
new file mode 100644 (file)
index 0000000..bacbeb2
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ * National Semiconductor LM8322/8323 GPIO keyboard & PWM chips.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "hw/i2c/i2c.h"
+#include "qemu/timer.h"
+#include "ui/console.h"
+
+typedef struct {
+    I2CSlave i2c;
+    uint8_t i2c_dir;
+    uint8_t i2c_cycle;
+    uint8_t reg;
+
+    qemu_irq nirq;
+    uint16_t model;
+
+    struct {
+        qemu_irq out[2];
+        int in[2][2];
+    } mux;
+
+    uint8_t config;
+    uint8_t status;
+    uint8_t acttime;
+    uint8_t error;
+    uint8_t clock;
+
+    struct {
+        uint16_t pull;
+        uint16_t mask;
+        uint16_t dir;
+        uint16_t level;
+        qemu_irq out[16];
+    } gpio;
+
+    struct {
+        uint8_t dbnctime;
+        uint8_t size;
+        uint8_t start;
+        uint8_t len;
+        uint8_t fifo[16];
+    } kbd;
+
+    struct {
+        uint16_t file[256];
+       uint8_t faddr;
+        uint8_t addr[3];
+        QEMUTimer *tm[3];
+    } pwm;
+} LM823KbdState;
+
+#define INT_KEYPAD             (1 << 0)
+#define INT_ERROR              (1 << 3)
+#define INT_NOINIT             (1 << 4)
+#define INT_PWMEND(n)          (1 << (5 + n))
+
+#define ERR_BADPAR             (1 << 0)
+#define ERR_CMDUNK             (1 << 1)
+#define ERR_KEYOVR             (1 << 2)
+#define ERR_FIFOOVR            (1 << 6)
+
+static void lm_kbd_irq_update(LM823KbdState *s)
+{
+    qemu_set_irq(s->nirq, !s->status);
+}
+
+static void lm_kbd_gpio_update(LM823KbdState *s)
+{
+}
+
+static void lm_kbd_reset(LM823KbdState *s)
+{
+    s->config = 0x80;
+    s->status = INT_NOINIT;
+    s->acttime = 125;
+    s->kbd.dbnctime = 3;
+    s->kbd.size = 0x33;
+    s->clock = 0x08;
+
+    lm_kbd_irq_update(s);
+    lm_kbd_gpio_update(s);
+}
+
+static void lm_kbd_error(LM823KbdState *s, int err)
+{
+    s->error |= err;
+    s->status |= INT_ERROR;
+    lm_kbd_irq_update(s);
+}
+
+static void lm_kbd_pwm_tick(LM823KbdState *s, int line)
+{
+}
+
+static void lm_kbd_pwm_start(LM823KbdState *s, int line)
+{
+    lm_kbd_pwm_tick(s, line);
+}
+
+static void lm_kbd_pwm0_tick(void *opaque)
+{
+    lm_kbd_pwm_tick(opaque, 0);
+}
+static void lm_kbd_pwm1_tick(void *opaque)
+{
+    lm_kbd_pwm_tick(opaque, 1);
+}
+static void lm_kbd_pwm2_tick(void *opaque)
+{
+    lm_kbd_pwm_tick(opaque, 2);
+}
+
+enum {
+    LM832x_CMD_READ_ID         = 0x80, /* Read chip ID. */
+    LM832x_CMD_WRITE_CFG       = 0x81, /* Set configuration item. */
+    LM832x_CMD_READ_INT                = 0x82, /* Get interrupt status. */
+    LM832x_CMD_RESET           = 0x83, /* Reset, same as external one */
+    LM823x_CMD_WRITE_PULL_DOWN = 0x84, /* Select GPIO pull-up/down. */
+    LM832x_CMD_WRITE_PORT_SEL  = 0x85, /* Select GPIO in/out. */
+    LM832x_CMD_WRITE_PORT_STATE        = 0x86, /* Set GPIO pull-up/down. */
+    LM832x_CMD_READ_PORT_SEL   = 0x87, /* Get GPIO in/out. */
+    LM832x_CMD_READ_PORT_STATE = 0x88, /* Get GPIO pull-up/down. */
+    LM832x_CMD_READ_FIFO       = 0x89, /* Read byte from FIFO. */
+    LM832x_CMD_RPT_READ_FIFO   = 0x8a, /* Read FIFO (no increment). */
+    LM832x_CMD_SET_ACTIVE      = 0x8b, /* Set active time. */
+    LM832x_CMD_READ_ERROR      = 0x8c, /* Get error status. */
+    LM832x_CMD_READ_ROTATOR    = 0x8e, /* Read rotator status. */
+    LM832x_CMD_SET_DEBOUNCE    = 0x8f, /* Set debouncing time. */
+    LM832x_CMD_SET_KEY_SIZE    = 0x90, /* Set keypad size. */
+    LM832x_CMD_READ_KEY_SIZE   = 0x91, /* Get keypad size. */
+    LM832x_CMD_READ_CFG                = 0x92, /* Get configuration item. */
+    LM832x_CMD_WRITE_CLOCK     = 0x93, /* Set clock config. */
+    LM832x_CMD_READ_CLOCK      = 0x94, /* Get clock config. */
+    LM832x_CMD_PWM_WRITE       = 0x95, /* Write PWM script. */
+    LM832x_CMD_PWM_START       = 0x96, /* Start PWM engine. */
+    LM832x_CMD_PWM_STOP                = 0x97, /* Stop PWM engine. */
+    LM832x_GENERAL_ERROR       = 0xff, /* There was one error.
+                                           Previously was represented by -1
+                                           This is not a command */
+};
+
+#define LM832x_MAX_KPX         8
+#define LM832x_MAX_KPY         12
+
+static uint8_t lm_kbd_read(LM823KbdState *s, int reg, int byte)
+{
+    int ret;
+
+    switch (reg) {
+    case LM832x_CMD_READ_ID:
+        ret = 0x0400;
+        break;
+
+    case LM832x_CMD_READ_INT:
+        ret = s->status;
+        if (!(s->status & INT_NOINIT)) {
+            s->status = 0;
+            lm_kbd_irq_update(s);
+        }
+        break;
+
+    case LM832x_CMD_READ_PORT_SEL:
+        ret = s->gpio.dir;
+        break;
+    case LM832x_CMD_READ_PORT_STATE:
+        ret = s->gpio.mask;
+        break;
+
+    case LM832x_CMD_READ_FIFO:
+        if (s->kbd.len <= 1)
+            return 0x00;
+
+        /* Example response from the two commands after a INT_KEYPAD
+         * interrupt caused by the key 0x3c being pressed:
+         * RPT_READ_FIFO: 55 bc 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01
+         *     READ_FIFO: bc 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01
+         * RPT_READ_FIFO: bc 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01
+         *
+         * 55 is the code of the key release event serviced in the previous
+         * interrupt handling.
+         *
+         * TODO: find out whether the FIFO is advanced a single character
+         * before reading every byte or the whole size of the FIFO at the
+         * last LM832x_CMD_READ_FIFO.  This affects LM832x_CMD_RPT_READ_FIFO
+         * output in cases where there are more than one event in the FIFO.
+         * Assume 0xbc and 0x3c events are in the FIFO:
+         * RPT_READ_FIFO: 55 bc 3c 00 4e ff 0a 50 08 00 29 d9 08 01 c9
+         *     READ_FIFO: bc 3c 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9
+         * Does RPT_READ_FIFO now return 0xbc and 0x3c or only 0x3c?
+         */
+        s->kbd.start ++;
+        s->kbd.start &= sizeof(s->kbd.fifo) - 1;
+        s->kbd.len --;
+
+        return s->kbd.fifo[s->kbd.start];
+    case LM832x_CMD_RPT_READ_FIFO:
+        if (byte >= s->kbd.len)
+            return 0x00;
+
+        return s->kbd.fifo[(s->kbd.start + byte) & (sizeof(s->kbd.fifo) - 1)];
+
+    case LM832x_CMD_READ_ERROR:
+        return s->error;
+
+    case LM832x_CMD_READ_ROTATOR:
+        return 0;
+
+    case LM832x_CMD_READ_KEY_SIZE:
+        return s->kbd.size;
+
+    case LM832x_CMD_READ_CFG:
+        return s->config & 0xf;
+
+    case LM832x_CMD_READ_CLOCK:
+        return (s->clock & 0xfc) | 2;
+
+    default:
+        lm_kbd_error(s, ERR_CMDUNK);
+        fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, reg);
+        return 0x00;
+    }
+
+    return ret >> (byte << 3);
+}
+
+static void lm_kbd_write(LM823KbdState *s, int reg, int byte, uint8_t value)
+{
+    switch (reg) {
+    case LM832x_CMD_WRITE_CFG:
+        s->config = value;
+        /* This must be done whenever s->mux.in is updated (never).  */
+        if ((s->config >> 1) & 1)                      /* MUX1EN */
+            qemu_set_irq(s->mux.out[0], s->mux.in[0][(s->config >> 0) & 1]);
+        if ((s->config >> 3) & 1)                      /* MUX2EN */
+            qemu_set_irq(s->mux.out[0], s->mux.in[0][(s->config >> 2) & 1]);
+        /* TODO: check that this is issued only following the chip reset
+         * and not in the middle of operation and that it is followed by
+         * the GPIO ports re-resablishing through WRITE_PORT_SEL and
+         * WRITE_PORT_STATE (using a timer perhaps) and otherwise output
+         * warnings.  */
+        s->status = 0;
+        lm_kbd_irq_update(s);
+        s->kbd.len = 0;
+        s->kbd.start = 0;
+        s->reg = LM832x_GENERAL_ERROR;
+        break;
+
+    case LM832x_CMD_RESET:
+        if (value == 0xaa)
+            lm_kbd_reset(s);
+        else
+            lm_kbd_error(s, ERR_BADPAR);
+        s->reg = LM832x_GENERAL_ERROR;
+        break;
+
+    case LM823x_CMD_WRITE_PULL_DOWN:
+        if (!byte)
+            s->gpio.pull = value;
+        else {
+            s->gpio.pull |= value << 8;
+            lm_kbd_gpio_update(s);
+            s->reg = LM832x_GENERAL_ERROR;
+        }
+        break;
+    case LM832x_CMD_WRITE_PORT_SEL:
+        if (!byte)
+            s->gpio.dir = value;
+        else {
+            s->gpio.dir |= value << 8;
+            lm_kbd_gpio_update(s);
+            s->reg = LM832x_GENERAL_ERROR;
+        }
+        break;
+    case LM832x_CMD_WRITE_PORT_STATE:
+        if (!byte)
+            s->gpio.mask = value;
+        else {
+            s->gpio.mask |= value << 8;
+            lm_kbd_gpio_update(s);
+            s->reg = LM832x_GENERAL_ERROR;
+        }
+        break;
+
+    case LM832x_CMD_SET_ACTIVE:
+        s->acttime = value;
+        s->reg = LM832x_GENERAL_ERROR;
+        break;
+
+    case LM832x_CMD_SET_DEBOUNCE:
+        s->kbd.dbnctime = value;
+        s->reg = LM832x_GENERAL_ERROR;
+        if (!value)
+            lm_kbd_error(s, ERR_BADPAR);
+        break;
+
+    case LM832x_CMD_SET_KEY_SIZE:
+        s->kbd.size = value;
+        s->reg = LM832x_GENERAL_ERROR;
+        if (
+                        (value & 0xf) < 3 || (value & 0xf) > LM832x_MAX_KPY ||
+                        (value >> 4) < 3 || (value >> 4) > LM832x_MAX_KPX)
+            lm_kbd_error(s, ERR_BADPAR);
+        break;
+
+    case LM832x_CMD_WRITE_CLOCK:
+        s->clock = value;
+        s->reg = LM832x_GENERAL_ERROR;
+        if ((value & 3) && (value & 3) != 3) {
+            lm_kbd_error(s, ERR_BADPAR);
+            fprintf(stderr, "%s: invalid clock setting in RCPWM\n",
+                            __FUNCTION__);
+        }
+        /* TODO: Validate that the command is only issued once */
+        break;
+
+    case LM832x_CMD_PWM_WRITE:
+        if (byte == 0) {
+            if (!(value & 3) || (value >> 2) > 59) {
+                lm_kbd_error(s, ERR_BADPAR);
+                s->reg = LM832x_GENERAL_ERROR;
+                break;
+            }
+
+            s->pwm.faddr = value;
+            s->pwm.file[s->pwm.faddr] = 0;
+        } else if (byte == 1) {
+            s->pwm.file[s->pwm.faddr] |= value << 8;
+        } else if (byte == 2) {
+            s->pwm.file[s->pwm.faddr] |= value << 0;
+            s->reg = LM832x_GENERAL_ERROR;
+        }
+        break;
+    case LM832x_CMD_PWM_START:
+        s->reg = LM832x_GENERAL_ERROR;
+        if (!(value & 3) || (value >> 2) > 59) {
+            lm_kbd_error(s, ERR_BADPAR);
+            break;
+        }
+
+        s->pwm.addr[(value & 3) - 1] = value >> 2;
+        lm_kbd_pwm_start(s, (value & 3) - 1);
+        break;
+    case LM832x_CMD_PWM_STOP:
+        s->reg = LM832x_GENERAL_ERROR;
+        if (!(value & 3)) {
+            lm_kbd_error(s, ERR_BADPAR);
+            break;
+        }
+
+        qemu_del_timer(s->pwm.tm[(value & 3) - 1]);
+        break;
+
+    case LM832x_GENERAL_ERROR:
+        lm_kbd_error(s, ERR_BADPAR);
+        break;
+    default:
+        lm_kbd_error(s, ERR_CMDUNK);
+        fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, reg);
+        break;
+    }
+}
+
+static void lm_i2c_event(I2CSlave *i2c, enum i2c_event event)
+{
+    LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, i2c);
+
+    switch (event) {
+    case I2C_START_RECV:
+    case I2C_START_SEND:
+        s->i2c_cycle = 0;
+        s->i2c_dir = (event == I2C_START_SEND);
+        break;
+
+    default:
+        break;
+    }
+}
+
+static int lm_i2c_rx(I2CSlave *i2c)
+{
+    LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, i2c);
+
+    return lm_kbd_read(s, s->reg, s->i2c_cycle ++);
+}
+
+static int lm_i2c_tx(I2CSlave *i2c, uint8_t data)
+{
+    LM823KbdState *s = (LM823KbdState *) i2c;
+
+    if (!s->i2c_cycle)
+        s->reg = data;
+    else
+        lm_kbd_write(s, s->reg, s->i2c_cycle - 1, data);
+    s->i2c_cycle ++;
+
+    return 0;
+}
+
+static int lm_kbd_post_load(void *opaque, int version_id)
+{
+    LM823KbdState *s = opaque;
+
+    lm_kbd_irq_update(s);
+    lm_kbd_gpio_update(s);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_lm_kbd = {
+    .name = "LM8323",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .post_load = lm_kbd_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_I2C_SLAVE(i2c, LM823KbdState),
+        VMSTATE_UINT8(i2c_dir, LM823KbdState),
+        VMSTATE_UINT8(i2c_cycle, LM823KbdState),
+        VMSTATE_UINT8(reg, LM823KbdState),
+        VMSTATE_UINT8(config, LM823KbdState),
+        VMSTATE_UINT8(status, LM823KbdState),
+        VMSTATE_UINT8(acttime, LM823KbdState),
+        VMSTATE_UINT8(error, LM823KbdState),
+        VMSTATE_UINT8(clock, LM823KbdState),
+        VMSTATE_UINT16(gpio.pull, LM823KbdState),
+        VMSTATE_UINT16(gpio.mask, LM823KbdState),
+        VMSTATE_UINT16(gpio.dir, LM823KbdState),
+        VMSTATE_UINT16(gpio.level, LM823KbdState),
+        VMSTATE_UINT8(kbd.dbnctime, LM823KbdState),
+        VMSTATE_UINT8(kbd.size, LM823KbdState),
+        VMSTATE_UINT8(kbd.start, LM823KbdState),
+        VMSTATE_UINT8(kbd.len, LM823KbdState),
+        VMSTATE_BUFFER(kbd.fifo, LM823KbdState),
+        VMSTATE_UINT16_ARRAY(pwm.file, LM823KbdState, 256),
+        VMSTATE_UINT8(pwm.faddr, LM823KbdState),
+        VMSTATE_BUFFER(pwm.addr, LM823KbdState),
+        VMSTATE_TIMER_ARRAY(pwm.tm, LM823KbdState, 3),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+
+static int lm8323_init(I2CSlave *i2c)
+{
+    LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, i2c);
+
+    s->model = 0x8323;
+    s->pwm.tm[0] = qemu_new_timer_ns(vm_clock, lm_kbd_pwm0_tick, s);
+    s->pwm.tm[1] = qemu_new_timer_ns(vm_clock, lm_kbd_pwm1_tick, s);
+    s->pwm.tm[2] = qemu_new_timer_ns(vm_clock, lm_kbd_pwm2_tick, s);
+    qdev_init_gpio_out(&i2c->qdev, &s->nirq, 1);
+
+    lm_kbd_reset(s);
+
+    qemu_register_reset((void *) lm_kbd_reset, s);
+    return 0;
+}
+
+void lm832x_key_event(DeviceState *dev, int key, int state)
+{
+    LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, I2C_SLAVE(dev));
+
+    if ((s->status & INT_ERROR) && (s->error & ERR_FIFOOVR))
+        return;
+
+    if (s->kbd.len >= sizeof(s->kbd.fifo)) {
+        lm_kbd_error(s, ERR_FIFOOVR);
+        return;
+    }
+
+    s->kbd.fifo[(s->kbd.start + s->kbd.len ++) & (sizeof(s->kbd.fifo) - 1)] =
+            key | (state << 7);
+
+    /* We never set ERR_KEYOVR because we support multiple keys fine.  */
+    s->status |= INT_KEYPAD;
+    lm_kbd_irq_update(s);
+}
+
+static void lm8323_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+    k->init = lm8323_init;
+    k->event = lm_i2c_event;
+    k->recv = lm_i2c_rx;
+    k->send = lm_i2c_tx;
+    dc->vmsd = &vmstate_lm_kbd;
+}
+
+static const TypeInfo lm8323_info = {
+    .name          = "lm8323",
+    .parent        = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(LM823KbdState),
+    .class_init    = lm8323_class_init,
+};
+
+static void lm832x_register_types(void)
+{
+    type_register_static(&lm8323_info);
+}
+
+type_init(lm832x_register_types)
diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c
new file mode 100644 (file)
index 0000000..08ceb9f
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+ * QEMU PC keyboard emulation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/isa/isa.h"
+#include "hw/i386/pc.h"
+#include "hw/input/ps2.h"
+#include "sysemu/sysemu.h"
+
+/* debug PC keyboard */
+//#define DEBUG_KBD
+#ifdef DEBUG_KBD
+#define DPRINTF(fmt, ...)                                       \
+    do { printf("KBD: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+/*     Keyboard Controller Commands */
+#define KBD_CCMD_READ_MODE     0x20    /* Read mode bits */
+#define KBD_CCMD_WRITE_MODE    0x60    /* Write mode bits */
+#define KBD_CCMD_GET_VERSION   0xA1    /* Get controller version */
+#define KBD_CCMD_MOUSE_DISABLE 0xA7    /* Disable mouse interface */
+#define KBD_CCMD_MOUSE_ENABLE  0xA8    /* Enable mouse interface */
+#define KBD_CCMD_TEST_MOUSE    0xA9    /* Mouse interface test */
+#define KBD_CCMD_SELF_TEST     0xAA    /* Controller self test */
+#define KBD_CCMD_KBD_TEST      0xAB    /* Keyboard interface test */
+#define KBD_CCMD_KBD_DISABLE   0xAD    /* Keyboard interface disable */
+#define KBD_CCMD_KBD_ENABLE    0xAE    /* Keyboard interface enable */
+#define KBD_CCMD_READ_INPORT    0xC0    /* read input port */
+#define KBD_CCMD_READ_OUTPORT  0xD0    /* read output port */
+#define KBD_CCMD_WRITE_OUTPORT 0xD1    /* write output port */
+#define KBD_CCMD_WRITE_OBUF    0xD2
+#define KBD_CCMD_WRITE_AUX_OBUF        0xD3    /* Write to output buffer as if
+                                          initiated by the auxiliary device */
+#define KBD_CCMD_WRITE_MOUSE   0xD4    /* Write the following byte to the mouse */
+#define KBD_CCMD_DISABLE_A20    0xDD    /* HP vectra only ? */
+#define KBD_CCMD_ENABLE_A20     0xDF    /* HP vectra only ? */
+#define KBD_CCMD_PULSE_BITS_3_0 0xF0    /* Pulse bits 3-0 of the output port P2. */
+#define KBD_CCMD_RESET          0xFE    /* Pulse bit 0 of the output port P2 = CPU reset. */
+#define KBD_CCMD_NO_OP          0xFF    /* Pulse no bits of the output port P2. */
+
+/* Keyboard Commands */
+#define KBD_CMD_SET_LEDS       0xED    /* Set keyboard leds */
+#define KBD_CMD_ECHO           0xEE
+#define KBD_CMD_GET_ID                 0xF2    /* get keyboard ID */
+#define KBD_CMD_SET_RATE       0xF3    /* Set typematic rate */
+#define KBD_CMD_ENABLE         0xF4    /* Enable scanning */
+#define KBD_CMD_RESET_DISABLE  0xF5    /* reset and disable scanning */
+#define KBD_CMD_RESET_ENABLE           0xF6    /* reset and enable scanning */
+#define KBD_CMD_RESET          0xFF    /* Reset */
+
+/* Keyboard Replies */
+#define KBD_REPLY_POR          0xAA    /* Power on reset */
+#define KBD_REPLY_ACK          0xFA    /* Command ACK */
+#define KBD_REPLY_RESEND       0xFE    /* Command NACK, send the cmd again */
+
+/* Status Register Bits */
+#define KBD_STAT_OBF           0x01    /* Keyboard output buffer full */
+#define KBD_STAT_IBF           0x02    /* Keyboard input buffer full */
+#define KBD_STAT_SELFTEST      0x04    /* Self test successful */
+#define KBD_STAT_CMD           0x08    /* Last write was a command write (0=data) */
+#define KBD_STAT_UNLOCKED      0x10    /* Zero if keyboard locked */
+#define KBD_STAT_MOUSE_OBF     0x20    /* Mouse output buffer full */
+#define KBD_STAT_GTO           0x40    /* General receive/xmit timeout */
+#define KBD_STAT_PERR          0x80    /* Parity error */
+
+/* Controller Mode Register Bits */
+#define KBD_MODE_KBD_INT       0x01    /* Keyboard data generate IRQ1 */
+#define KBD_MODE_MOUSE_INT     0x02    /* Mouse data generate IRQ12 */
+#define KBD_MODE_SYS           0x04    /* The system flag (?) */
+#define KBD_MODE_NO_KEYLOCK    0x08    /* The keylock doesn't affect the keyboard if set */
+#define KBD_MODE_DISABLE_KBD   0x10    /* Disable keyboard interface */
+#define KBD_MODE_DISABLE_MOUSE 0x20    /* Disable mouse interface */
+#define KBD_MODE_KCC           0x40    /* Scan code conversion to PC format */
+#define KBD_MODE_RFU           0x80
+
+/* Output Port Bits */
+#define KBD_OUT_RESET           0x01    /* 1=normal mode, 0=reset */
+#define KBD_OUT_A20             0x02    /* x86 only */
+#define KBD_OUT_OBF             0x10    /* Keyboard output buffer full */
+#define KBD_OUT_MOUSE_OBF       0x20    /* Mouse output buffer full */
+
+/* Mouse Commands */
+#define AUX_SET_SCALE11                0xE6    /* Set 1:1 scaling */
+#define AUX_SET_SCALE21                0xE7    /* Set 2:1 scaling */
+#define AUX_SET_RES            0xE8    /* Set resolution */
+#define AUX_GET_SCALE          0xE9    /* Get scaling factor */
+#define AUX_SET_STREAM         0xEA    /* Set stream mode */
+#define AUX_POLL               0xEB    /* Poll */
+#define AUX_RESET_WRAP         0xEC    /* Reset wrap mode */
+#define AUX_SET_WRAP           0xEE    /* Set wrap mode */
+#define AUX_SET_REMOTE         0xF0    /* Set remote mode */
+#define AUX_GET_TYPE           0xF2    /* Get type */
+#define AUX_SET_SAMPLE         0xF3    /* Set sample rate */
+#define AUX_ENABLE_DEV         0xF4    /* Enable aux device */
+#define AUX_DISABLE_DEV                0xF5    /* Disable aux device */
+#define AUX_SET_DEFAULT                0xF6
+#define AUX_RESET              0xFF    /* Reset aux device */
+#define AUX_ACK                        0xFA    /* Command byte ACK. */
+
+#define MOUSE_STATUS_REMOTE     0x40
+#define MOUSE_STATUS_ENABLED    0x20
+#define MOUSE_STATUS_SCALE21    0x10
+
+#define KBD_PENDING_KBD         1
+#define KBD_PENDING_AUX         2
+
+typedef struct KBDState {
+    uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
+    uint8_t status;
+    uint8_t mode;
+    uint8_t outport;
+    /* Bitmask of devices with data available.  */
+    uint8_t pending;
+    void *kbd;
+    void *mouse;
+
+    qemu_irq irq_kbd;
+    qemu_irq irq_mouse;
+    qemu_irq *a20_out;
+    hwaddr mask;
+} KBDState;
+
+/* update irq and KBD_STAT_[MOUSE_]OBF */
+/* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be
+   incorrect, but it avoids having to simulate exact delays */
+static void kbd_update_irq(KBDState *s)
+{
+    int irq_kbd_level, irq_mouse_level;
+
+    irq_kbd_level = 0;
+    irq_mouse_level = 0;
+    s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
+    s->outport &= ~(KBD_OUT_OBF | KBD_OUT_MOUSE_OBF);
+    if (s->pending) {
+        s->status |= KBD_STAT_OBF;
+        s->outport |= KBD_OUT_OBF;
+        /* kbd data takes priority over aux data.  */
+        if (s->pending == KBD_PENDING_AUX) {
+            s->status |= KBD_STAT_MOUSE_OBF;
+            s->outport |= KBD_OUT_MOUSE_OBF;
+            if (s->mode & KBD_MODE_MOUSE_INT)
+                irq_mouse_level = 1;
+        } else {
+            if ((s->mode & KBD_MODE_KBD_INT) &&
+                !(s->mode & KBD_MODE_DISABLE_KBD))
+                irq_kbd_level = 1;
+        }
+    }
+    qemu_set_irq(s->irq_kbd, irq_kbd_level);
+    qemu_set_irq(s->irq_mouse, irq_mouse_level);
+}
+
+static void kbd_update_kbd_irq(void *opaque, int level)
+{
+    KBDState *s = (KBDState *)opaque;
+
+    if (level)
+        s->pending |= KBD_PENDING_KBD;
+    else
+        s->pending &= ~KBD_PENDING_KBD;
+    kbd_update_irq(s);
+}
+
+static void kbd_update_aux_irq(void *opaque, int level)
+{
+    KBDState *s = (KBDState *)opaque;
+
+    if (level)
+        s->pending |= KBD_PENDING_AUX;
+    else
+        s->pending &= ~KBD_PENDING_AUX;
+    kbd_update_irq(s);
+}
+
+static uint64_t kbd_read_status(void *opaque, hwaddr addr,
+                                unsigned size)
+{
+    KBDState *s = opaque;
+    int val;
+    val = s->status;
+    DPRINTF("kbd: read status=0x%02x\n", val);
+    return val;
+}
+
+static void kbd_queue(KBDState *s, int b, int aux)
+{
+    if (aux)
+        ps2_queue(s->mouse, b);
+    else
+        ps2_queue(s->kbd, b);
+}
+
+static void outport_write(KBDState *s, uint32_t val)
+{
+    DPRINTF("kbd: write outport=0x%02x\n", val);
+    s->outport = val;
+    if (s->a20_out) {
+        qemu_set_irq(*s->a20_out, (val >> 1) & 1);
+    }
+    if (!(val & 1)) {
+        qemu_system_reset_request();
+    }
+}
+
+static void kbd_write_command(void *opaque, hwaddr addr,
+                              uint64_t val, unsigned size)
+{
+    KBDState *s = opaque;
+
+    DPRINTF("kbd: write cmd=0x%02x\n", val);
+
+    /* Bits 3-0 of the output port P2 of the keyboard controller may be pulsed
+     * low for approximately 6 micro seconds. Bits 3-0 of the KBD_CCMD_PULSE
+     * command specify the output port bits to be pulsed.
+     * 0: Bit should be pulsed. 1: Bit should not be modified.
+     * The only useful version of this command is pulsing bit 0,
+     * which does a CPU reset.
+     */
+    if((val & KBD_CCMD_PULSE_BITS_3_0) == KBD_CCMD_PULSE_BITS_3_0) {
+        if(!(val & 1))
+            val = KBD_CCMD_RESET;
+        else
+            val = KBD_CCMD_NO_OP;
+    }
+
+    switch(val) {
+    case KBD_CCMD_READ_MODE:
+        kbd_queue(s, s->mode, 0);
+        break;
+    case KBD_CCMD_WRITE_MODE:
+    case KBD_CCMD_WRITE_OBUF:
+    case KBD_CCMD_WRITE_AUX_OBUF:
+    case KBD_CCMD_WRITE_MOUSE:
+    case KBD_CCMD_WRITE_OUTPORT:
+        s->write_cmd = val;
+        break;
+    case KBD_CCMD_MOUSE_DISABLE:
+        s->mode |= KBD_MODE_DISABLE_MOUSE;
+        break;
+    case KBD_CCMD_MOUSE_ENABLE:
+        s->mode &= ~KBD_MODE_DISABLE_MOUSE;
+        break;
+    case KBD_CCMD_TEST_MOUSE:
+        kbd_queue(s, 0x00, 0);
+        break;
+    case KBD_CCMD_SELF_TEST:
+        s->status |= KBD_STAT_SELFTEST;
+        kbd_queue(s, 0x55, 0);
+        break;
+    case KBD_CCMD_KBD_TEST:
+        kbd_queue(s, 0x00, 0);
+        break;
+    case KBD_CCMD_KBD_DISABLE:
+        s->mode |= KBD_MODE_DISABLE_KBD;
+        kbd_update_irq(s);
+        break;
+    case KBD_CCMD_KBD_ENABLE:
+        s->mode &= ~KBD_MODE_DISABLE_KBD;
+        kbd_update_irq(s);
+        break;
+    case KBD_CCMD_READ_INPORT:
+        kbd_queue(s, 0x00, 0);
+        break;
+    case KBD_CCMD_READ_OUTPORT:
+        kbd_queue(s, s->outport, 0);
+        break;
+    case KBD_CCMD_ENABLE_A20:
+        if (s->a20_out) {
+            qemu_irq_raise(*s->a20_out);
+        }
+        s->outport |= KBD_OUT_A20;
+        break;
+    case KBD_CCMD_DISABLE_A20:
+        if (s->a20_out) {
+            qemu_irq_lower(*s->a20_out);
+        }
+        s->outport &= ~KBD_OUT_A20;
+        break;
+    case KBD_CCMD_RESET:
+        qemu_system_reset_request();
+        break;
+    case KBD_CCMD_NO_OP:
+        /* ignore that */
+        break;
+    default:
+        fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", (int)val);
+        break;
+    }
+}
+
+static uint64_t kbd_read_data(void *opaque, hwaddr addr,
+                              unsigned size)
+{
+    KBDState *s = opaque;
+    uint32_t val;
+
+    if (s->pending == KBD_PENDING_AUX)
+        val = ps2_read_data(s->mouse);
+    else
+        val = ps2_read_data(s->kbd);
+
+    DPRINTF("kbd: read data=0x%02x\n", val);
+    return val;
+}
+
+static void kbd_write_data(void *opaque, hwaddr addr,
+                           uint64_t val, unsigned size)
+{
+    KBDState *s = opaque;
+
+    DPRINTF("kbd: write data=0x%02x\n", val);
+
+    switch(s->write_cmd) {
+    case 0:
+        ps2_write_keyboard(s->kbd, val);
+        break;
+    case KBD_CCMD_WRITE_MODE:
+        s->mode = val;
+        ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0);
+        /* ??? */
+        kbd_update_irq(s);
+        break;
+    case KBD_CCMD_WRITE_OBUF:
+        kbd_queue(s, val, 0);
+        break;
+    case KBD_CCMD_WRITE_AUX_OBUF:
+        kbd_queue(s, val, 1);
+        break;
+    case KBD_CCMD_WRITE_OUTPORT:
+        outport_write(s, val);
+        break;
+    case KBD_CCMD_WRITE_MOUSE:
+        ps2_write_mouse(s->mouse, val);
+        break;
+    default:
+        break;
+    }
+    s->write_cmd = 0;
+}
+
+static void kbd_reset(void *opaque)
+{
+    KBDState *s = opaque;
+
+    s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
+    s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
+    s->outport = KBD_OUT_RESET | KBD_OUT_A20;
+}
+
+static const VMStateDescription vmstate_kbd = {
+    .name = "pckbd",
+    .version_id = 3,
+    .minimum_version_id = 3,
+    .minimum_version_id_old = 3,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT8(write_cmd, KBDState),
+        VMSTATE_UINT8(status, KBDState),
+        VMSTATE_UINT8(mode, KBDState),
+        VMSTATE_UINT8(pending, KBDState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/* Memory mapped interface */
+static uint32_t kbd_mm_readb (void *opaque, hwaddr addr)
+{
+    KBDState *s = opaque;
+
+    if (addr & s->mask)
+        return kbd_read_status(s, 0, 1) & 0xff;
+    else
+        return kbd_read_data(s, 0, 1) & 0xff;
+}
+
+static void kbd_mm_writeb (void *opaque, hwaddr addr, uint32_t value)
+{
+    KBDState *s = opaque;
+
+    if (addr & s->mask)
+        kbd_write_command(s, 0, value & 0xff, 1);
+    else
+        kbd_write_data(s, 0, value & 0xff, 1);
+}
+
+static const MemoryRegionOps i8042_mmio_ops = {
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .old_mmio = {
+        .read = { kbd_mm_readb, kbd_mm_readb, kbd_mm_readb },
+        .write = { kbd_mm_writeb, kbd_mm_writeb, kbd_mm_writeb },
+    },
+};
+
+void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
+                   MemoryRegion *region, ram_addr_t size,
+                   hwaddr mask)
+{
+    KBDState *s = g_malloc0(sizeof(KBDState));
+
+    s->irq_kbd = kbd_irq;
+    s->irq_mouse = mouse_irq;
+    s->mask = mask;
+
+    vmstate_register(NULL, 0, &vmstate_kbd, s);
+
+    memory_region_init_io(region, &i8042_mmio_ops, s, "i8042", size);
+
+    s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
+    s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
+    qemu_register_reset(kbd_reset, s);
+}
+
+typedef struct ISAKBDState {
+    ISADevice dev;
+    KBDState kbd;
+    MemoryRegion io[2];
+} ISAKBDState;
+
+void i8042_isa_mouse_fake_event(void *opaque)
+{
+    ISADevice *dev = opaque;
+    KBDState *s = &(DO_UPCAST(ISAKBDState, dev, dev)->kbd);
+
+    ps2_mouse_fake_event(s->mouse);
+}
+
+void i8042_setup_a20_line(ISADevice *dev, qemu_irq *a20_out)
+{
+    KBDState *s = &(DO_UPCAST(ISAKBDState, dev, dev)->kbd);
+
+    s->a20_out = a20_out;
+}
+
+static const VMStateDescription vmstate_kbd_isa = {
+    .name = "pckbd",
+    .version_id = 3,
+    .minimum_version_id = 3,
+    .minimum_version_id_old = 3,
+    .fields      = (VMStateField []) {
+        VMSTATE_STRUCT(kbd, ISAKBDState, 0, vmstate_kbd, KBDState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const MemoryRegionOps i8042_data_ops = {
+    .read = kbd_read_data,
+    .write = kbd_write_data,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static const MemoryRegionOps i8042_cmd_ops = {
+    .read = kbd_read_status,
+    .write = kbd_write_command,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static int i8042_initfn(ISADevice *dev)
+{
+    ISAKBDState *isa_s = DO_UPCAST(ISAKBDState, dev, dev);
+    KBDState *s = &isa_s->kbd;
+
+    isa_init_irq(dev, &s->irq_kbd, 1);
+    isa_init_irq(dev, &s->irq_mouse, 12);
+
+    memory_region_init_io(isa_s->io + 0, &i8042_data_ops, s, "i8042-data", 1);
+    isa_register_ioport(dev, isa_s->io + 0, 0x60);
+
+    memory_region_init_io(isa_s->io + 1, &i8042_cmd_ops, s, "i8042-cmd", 1);
+    isa_register_ioport(dev, isa_s->io + 1, 0x64);
+
+    s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
+    s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
+    qemu_register_reset(kbd_reset, s);
+    return 0;
+}
+
+static void i8042_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+    ic->init = i8042_initfn;
+    dc->no_user = 1;
+    dc->vmsd = &vmstate_kbd_isa;
+}
+
+static const TypeInfo i8042_info = {
+    .name          = "i8042",
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(ISAKBDState),
+    .class_init    = i8042_class_initfn,
+};
+
+static void i8042_register_types(void)
+{
+    type_register_static(&i8042_info);
+}
+
+type_init(i8042_register_types)
diff --git a/hw/input/pl050.c b/hw/input/pl050.c
new file mode 100644 (file)
index 0000000..7dd8a59
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Arm PrimeCell PL050 Keyboard / Mouse Interface
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/input/ps2.h"
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    void *dev;
+    uint32_t cr;
+    uint32_t clk;
+    uint32_t last;
+    int pending;
+    qemu_irq irq;
+    int is_mouse;
+} pl050_state;
+
+static const VMStateDescription vmstate_pl050 = {
+    .name = "pl050",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(cr, pl050_state),
+        VMSTATE_UINT32(clk, pl050_state),
+        VMSTATE_UINT32(last, pl050_state),
+        VMSTATE_INT32(pending, pl050_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+#define PL050_TXEMPTY         (1 << 6)
+#define PL050_TXBUSY          (1 << 5)
+#define PL050_RXFULL          (1 << 4)
+#define PL050_RXBUSY          (1 << 3)
+#define PL050_RXPARITY        (1 << 2)
+#define PL050_KMIC            (1 << 1)
+#define PL050_KMID            (1 << 0)
+
+static const unsigned char pl050_id[] =
+{ 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl050_update(void *opaque, int level)
+{
+    pl050_state *s = (pl050_state *)opaque;
+    int raise;
+
+    s->pending = level;
+    raise = (s->pending && (s->cr & 0x10) != 0)
+            || (s->cr & 0x08) != 0;
+    qemu_set_irq(s->irq, raise);
+}
+
+static uint64_t pl050_read(void *opaque, hwaddr offset,
+                           unsigned size)
+{
+    pl050_state *s = (pl050_state *)opaque;
+    if (offset >= 0xfe0 && offset < 0x1000)
+        return pl050_id[(offset - 0xfe0) >> 2];
+
+    switch (offset >> 2) {
+    case 0: /* KMICR */
+        return s->cr;
+    case 1: /* KMISTAT */
+        {
+            uint8_t val;
+            uint32_t stat;
+
+            val = s->last;
+            val = val ^ (val >> 4);
+            val = val ^ (val >> 2);
+            val = (val ^ (val >> 1)) & 1;
+
+            stat = PL050_TXEMPTY;
+            if (val)
+                stat |= PL050_RXPARITY;
+            if (s->pending)
+                stat |= PL050_RXFULL;
+
+            return stat;
+        }
+    case 2: /* KMIDATA */
+        if (s->pending)
+            s->last = ps2_read_data(s->dev);
+        return s->last;
+    case 3: /* KMICLKDIV */
+        return s->clk;
+    case 4: /* KMIIR */
+        return s->pending | 2;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl050_read: Bad offset %x\n", (int)offset);
+        return 0;
+    }
+}
+
+static void pl050_write(void *opaque, hwaddr offset,
+                        uint64_t value, unsigned size)
+{
+    pl050_state *s = (pl050_state *)opaque;
+    switch (offset >> 2) {
+    case 0: /* KMICR */
+        s->cr = value;
+        pl050_update(s, s->pending);
+        /* ??? Need to implement the enable/disable bit.  */
+        break;
+    case 2: /* KMIDATA */
+        /* ??? This should toggle the TX interrupt line.  */
+        /* ??? This means kbd/mouse can block each other.  */
+        if (s->is_mouse) {
+            ps2_write_mouse(s->dev, value);
+        } else {
+            ps2_write_keyboard(s->dev, value);
+        }
+        break;
+    case 3: /* KMICLKDIV */
+        s->clk = value;
+        return;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl050_write: Bad offset %x\n", (int)offset);
+    }
+}
+static const MemoryRegionOps pl050_ops = {
+    .read = pl050_read,
+    .write = pl050_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int pl050_init(SysBusDevice *dev, int is_mouse)
+{
+    pl050_state *s = FROM_SYSBUS(pl050_state, dev);
+
+    memory_region_init_io(&s->iomem, &pl050_ops, s, "pl050", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+    s->is_mouse = is_mouse;
+    if (s->is_mouse)
+        s->dev = ps2_mouse_init(pl050_update, s);
+    else
+        s->dev = ps2_kbd_init(pl050_update, s);
+    return 0;
+}
+
+static int pl050_init_keyboard(SysBusDevice *dev)
+{
+    return pl050_init(dev, 0);
+}
+
+static int pl050_init_mouse(SysBusDevice *dev)
+{
+    return pl050_init(dev, 1);
+}
+
+static void pl050_kbd_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = pl050_init_keyboard;
+    dc->vmsd = &vmstate_pl050;
+}
+
+static const TypeInfo pl050_kbd_info = {
+    .name          = "pl050_keyboard",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl050_state),
+    .class_init    = pl050_kbd_class_init,
+};
+
+static void pl050_mouse_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = pl050_init_mouse;
+    dc->vmsd = &vmstate_pl050;
+}
+
+static const TypeInfo pl050_mouse_info = {
+    .name          = "pl050_mouse",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl050_state),
+    .class_init    = pl050_mouse_class_init,
+};
+
+static void pl050_register_types(void)
+{
+    type_register_static(&pl050_kbd_info);
+    type_register_static(&pl050_mouse_info);
+}
+
+type_init(pl050_register_types)
diff --git a/hw/input/ps2.c b/hw/input/ps2.c
new file mode 100644 (file)
index 0000000..3412079
--- /dev/null
@@ -0,0 +1,676 @@
+/*
+ * QEMU PS/2 keyboard/mouse emulation
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/input/ps2.h"
+#include "ui/console.h"
+#include "sysemu/sysemu.h"
+
+/* debug PC keyboard */
+//#define DEBUG_KBD
+
+/* debug PC keyboard : only mouse */
+//#define DEBUG_MOUSE
+
+/* Keyboard Commands */
+#define KBD_CMD_SET_LEDS       0xED    /* Set keyboard leds */
+#define KBD_CMD_ECHO           0xEE
+#define KBD_CMD_SCANCODE       0xF0    /* Get/set scancode set */
+#define KBD_CMD_GET_ID                 0xF2    /* get keyboard ID */
+#define KBD_CMD_SET_RATE       0xF3    /* Set typematic rate */
+#define KBD_CMD_ENABLE         0xF4    /* Enable scanning */
+#define KBD_CMD_RESET_DISABLE  0xF5    /* reset and disable scanning */
+#define KBD_CMD_RESET_ENABLE           0xF6    /* reset and enable scanning */
+#define KBD_CMD_RESET          0xFF    /* Reset */
+
+/* Keyboard Replies */
+#define KBD_REPLY_POR          0xAA    /* Power on reset */
+#define KBD_REPLY_ID           0xAB    /* Keyboard ID */
+#define KBD_REPLY_ACK          0xFA    /* Command ACK */
+#define KBD_REPLY_RESEND       0xFE    /* Command NACK, send the cmd again */
+
+/* Mouse Commands */
+#define AUX_SET_SCALE11                0xE6    /* Set 1:1 scaling */
+#define AUX_SET_SCALE21                0xE7    /* Set 2:1 scaling */
+#define AUX_SET_RES            0xE8    /* Set resolution */
+#define AUX_GET_SCALE          0xE9    /* Get scaling factor */
+#define AUX_SET_STREAM         0xEA    /* Set stream mode */
+#define AUX_POLL               0xEB    /* Poll */
+#define AUX_RESET_WRAP         0xEC    /* Reset wrap mode */
+#define AUX_SET_WRAP           0xEE    /* Set wrap mode */
+#define AUX_SET_REMOTE         0xF0    /* Set remote mode */
+#define AUX_GET_TYPE           0xF2    /* Get type */
+#define AUX_SET_SAMPLE         0xF3    /* Set sample rate */
+#define AUX_ENABLE_DEV         0xF4    /* Enable aux device */
+#define AUX_DISABLE_DEV                0xF5    /* Disable aux device */
+#define AUX_SET_DEFAULT                0xF6
+#define AUX_RESET              0xFF    /* Reset aux device */
+#define AUX_ACK                        0xFA    /* Command byte ACK. */
+
+#define MOUSE_STATUS_REMOTE     0x40
+#define MOUSE_STATUS_ENABLED    0x20
+#define MOUSE_STATUS_SCALE21    0x10
+
+#define PS2_QUEUE_SIZE 256
+
+typedef struct {
+    uint8_t data[PS2_QUEUE_SIZE];
+    int rptr, wptr, count;
+} PS2Queue;
+
+typedef struct {
+    PS2Queue queue;
+    int32_t write_cmd;
+    void (*update_irq)(void *, int);
+    void *update_arg;
+} PS2State;
+
+typedef struct {
+    PS2State common;
+    int scan_enabled;
+    /* QEMU uses translated PC scancodes internally.  To avoid multiple
+       conversions we do the translation (if any) in the PS/2 emulation
+       not the keyboard controller.  */
+    int translate;
+    int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */
+    int ledstate;
+} PS2KbdState;
+
+typedef struct {
+    PS2State common;
+    uint8_t mouse_status;
+    uint8_t mouse_resolution;
+    uint8_t mouse_sample_rate;
+    uint8_t mouse_wrap;
+    uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
+    uint8_t mouse_detect_state;
+    int mouse_dx; /* current values, needed for 'poll' mode */
+    int mouse_dy;
+    int mouse_dz;
+    uint8_t mouse_buttons;
+} PS2MouseState;
+
+/* Table to convert from PC scancodes to raw scancodes.  */
+static const unsigned char ps2_raw_keycode[128] = {
+  0, 118,  22,  30,  38,  37,  46,  54,  61,  62,  70,  69,  78,  85, 102,  13,
+ 21,  29,  36,  45,  44,  53,  60,  67,  68,  77,  84,  91,  90,  20,  28,  27,
+ 35,  43,  52,  51,  59,  66,  75,  76,  82,  14,  18,  93,  26,  34,  33,  42,
+ 50,  49,  58,  65,  73,  74,  89, 124,  17,  41,  88,   5,   6,   4,  12,   3,
+ 11,   2,  10,   1,   9, 119, 126, 108, 117, 125, 123, 107, 115, 116, 121, 105,
+114, 122, 112, 113, 127,  96,  97, 120,   7,  15,  23,  31,  39,  47,  55,  63,
+ 71,  79,  86,  94,   8,  16,  24,  32,  40,  48,  56,  64,  72,  80,  87, 111,
+ 19,  25,  57,  81,  83,  92,  95,  98,  99, 100, 101, 103, 104, 106, 109, 110
+};
+static const unsigned char ps2_raw_keycode_set3[128] = {
+  0,   8,  22,  30,  38,  37,  46,  54,  61,  62,  70,  69,  78,  85, 102,  13,
+ 21,  29,  36,  45,  44,  53,  60,  67,  68,  77,  84,  91,  90,  17,  28,  27,
+ 35,  43,  52,  51,  59,  66,  75,  76,  82,  14,  18,  92,  26,  34,  33,  42,
+ 50,  49,  58,  65,  73,  74,  89, 126,  25,  41,  20,   7,  15,  23,  31,  39,
+ 47,   2,  63,  71,  79, 118,  95, 108, 117, 125, 132, 107, 115, 116, 124, 105,
+114, 122, 112, 113, 127,  96,  97,  86,  94,  15,  23,  31,  39,  47,  55,  63,
+ 71,  79,  86,  94,   8,  16,  24,  32,  40,  48,  56,  64,  72,  80,  87, 111,
+ 19,  25,  57,  81,  83,  92,  95,  98,  99, 100, 101, 103, 104, 106, 109, 110
+};
+
+void ps2_queue(void *opaque, int b)
+{
+    PS2State *s = (PS2State *)opaque;
+    PS2Queue *q = &s->queue;
+
+    if (q->count >= PS2_QUEUE_SIZE)
+        return;
+    q->data[q->wptr] = b;
+    if (++q->wptr == PS2_QUEUE_SIZE)
+        q->wptr = 0;
+    q->count++;
+    s->update_irq(s->update_arg, 1);
+}
+
+/*
+   keycode is expressed as follow:
+   bit 7    - 0 key pressed, 1 = key released
+   bits 6-0 - translated scancode set 2
+ */
+static void ps2_put_keycode(void *opaque, int keycode)
+{
+    PS2KbdState *s = opaque;
+
+    qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
+    /* XXX: add support for scancode set 1 */
+    if (!s->translate && keycode < 0xe0 && s->scancode_set > 1) {
+        if (keycode & 0x80) {
+            ps2_queue(&s->common, 0xf0);
+        }
+        if (s->scancode_set == 2) {
+            keycode = ps2_raw_keycode[keycode & 0x7f];
+        } else if (s->scancode_set == 3) {
+            keycode = ps2_raw_keycode_set3[keycode & 0x7f];
+        }
+      }
+    ps2_queue(&s->common, keycode);
+}
+
+uint32_t ps2_read_data(void *opaque)
+{
+    PS2State *s = (PS2State *)opaque;
+    PS2Queue *q;
+    int val, index;
+
+    q = &s->queue;
+    if (q->count == 0) {
+        /* NOTE: if no data left, we return the last keyboard one
+           (needed for EMM386) */
+        /* XXX: need a timer to do things correctly */
+        index = q->rptr - 1;
+        if (index < 0)
+            index = PS2_QUEUE_SIZE - 1;
+        val = q->data[index];
+    } else {
+        val = q->data[q->rptr];
+        if (++q->rptr == PS2_QUEUE_SIZE)
+            q->rptr = 0;
+        q->count--;
+        /* reading deasserts IRQ */
+        s->update_irq(s->update_arg, 0);
+        /* reassert IRQs if data left */
+        s->update_irq(s->update_arg, q->count != 0);
+    }
+    return val;
+}
+
+static void ps2_set_ledstate(PS2KbdState *s, int ledstate)
+{
+    s->ledstate = ledstate;
+    kbd_put_ledstate(ledstate);
+}
+
+static void ps2_reset_keyboard(PS2KbdState *s)
+{
+    s->scan_enabled = 1;
+    s->scancode_set = 2;
+    ps2_set_ledstate(s, 0);
+}
+
+void ps2_write_keyboard(void *opaque, int val)
+{
+    PS2KbdState *s = (PS2KbdState *)opaque;
+
+    switch(s->common.write_cmd) {
+    default:
+    case -1:
+        switch(val) {
+        case 0x00:
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        case 0x05:
+            ps2_queue(&s->common, KBD_REPLY_RESEND);
+            break;
+        case KBD_CMD_GET_ID:
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            /* We emulate a MF2 AT keyboard here */
+            ps2_queue(&s->common, KBD_REPLY_ID);
+            if (s->translate)
+                ps2_queue(&s->common, 0x41);
+            else
+                ps2_queue(&s->common, 0x83);
+            break;
+        case KBD_CMD_ECHO:
+            ps2_queue(&s->common, KBD_CMD_ECHO);
+            break;
+        case KBD_CMD_ENABLE:
+            s->scan_enabled = 1;
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        case KBD_CMD_SCANCODE:
+        case KBD_CMD_SET_LEDS:
+        case KBD_CMD_SET_RATE:
+            s->common.write_cmd = val;
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        case KBD_CMD_RESET_DISABLE:
+            ps2_reset_keyboard(s);
+            s->scan_enabled = 0;
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        case KBD_CMD_RESET_ENABLE:
+            ps2_reset_keyboard(s);
+            s->scan_enabled = 1;
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        case KBD_CMD_RESET:
+            ps2_reset_keyboard(s);
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            ps2_queue(&s->common, KBD_REPLY_POR);
+            break;
+        default:
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+            break;
+        }
+        break;
+    case KBD_CMD_SCANCODE:
+        if (val == 0) {
+            if (s->scancode_set == 1)
+                ps2_put_keycode(s, 0x43);
+            else if (s->scancode_set == 2)
+                ps2_put_keycode(s, 0x41);
+            else if (s->scancode_set == 3)
+                ps2_put_keycode(s, 0x3f);
+        } else {
+            if (val >= 1 && val <= 3)
+                s->scancode_set = val;
+            ps2_queue(&s->common, KBD_REPLY_ACK);
+        }
+        s->common.write_cmd = -1;
+        break;
+    case KBD_CMD_SET_LEDS:
+        ps2_set_ledstate(s, val);
+        ps2_queue(&s->common, KBD_REPLY_ACK);
+        s->common.write_cmd = -1;
+        break;
+    case KBD_CMD_SET_RATE:
+        ps2_queue(&s->common, KBD_REPLY_ACK);
+        s->common.write_cmd = -1;
+        break;
+    }
+}
+
+/* Set the scancode translation mode.
+   0 = raw scancodes.
+   1 = translated scancodes (used by qemu internally).  */
+
+void ps2_keyboard_set_translation(void *opaque, int mode)
+{
+    PS2KbdState *s = (PS2KbdState *)opaque;
+    s->translate = mode;
+}
+
+static void ps2_mouse_send_packet(PS2MouseState *s)
+{
+    unsigned int b;
+    int dx1, dy1, dz1;
+
+    dx1 = s->mouse_dx;
+    dy1 = s->mouse_dy;
+    dz1 = s->mouse_dz;
+    /* XXX: increase range to 8 bits ? */
+    if (dx1 > 127)
+        dx1 = 127;
+    else if (dx1 < -127)
+        dx1 = -127;
+    if (dy1 > 127)
+        dy1 = 127;
+    else if (dy1 < -127)
+        dy1 = -127;
+    b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
+    ps2_queue(&s->common, b);
+    ps2_queue(&s->common, dx1 & 0xff);
+    ps2_queue(&s->common, dy1 & 0xff);
+    /* extra byte for IMPS/2 or IMEX */
+    switch(s->mouse_type) {
+    default:
+        break;
+    case 3:
+        if (dz1 > 127)
+            dz1 = 127;
+        else if (dz1 < -127)
+                dz1 = -127;
+        ps2_queue(&s->common, dz1 & 0xff);
+        break;
+    case 4:
+        if (dz1 > 7)
+            dz1 = 7;
+        else if (dz1 < -7)
+            dz1 = -7;
+        b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
+        ps2_queue(&s->common, b);
+        break;
+    }
+
+    /* update deltas */
+    s->mouse_dx -= dx1;
+    s->mouse_dy -= dy1;
+    s->mouse_dz -= dz1;
+}
+
+static void ps2_mouse_event(void *opaque,
+                            int dx, int dy, int dz, int buttons_state)
+{
+    PS2MouseState *s = opaque;
+
+    /* check if deltas are recorded when disabled */
+    if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
+        return;
+
+    s->mouse_dx += dx;
+    s->mouse_dy -= dy;
+    s->mouse_dz += dz;
+    /* XXX: SDL sometimes generates nul events: we delete them */
+    if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 &&
+        s->mouse_buttons == buttons_state)
+       return;
+    s->mouse_buttons = buttons_state;
+
+    if (buttons_state) {
+        qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
+    }
+
+    if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
+        (s->common.queue.count < (PS2_QUEUE_SIZE - 16))) {
+        for(;;) {
+            /* if not remote, send event. Multiple events are sent if
+               too big deltas */
+            ps2_mouse_send_packet(s);
+            if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
+                break;
+        }
+    }
+}
+
+void ps2_mouse_fake_event(void *opaque)
+{
+    ps2_mouse_event(opaque, 1, 0, 0, 0);
+}
+
+void ps2_write_mouse(void *opaque, int val)
+{
+    PS2MouseState *s = (PS2MouseState *)opaque;
+#ifdef DEBUG_MOUSE
+    printf("kbd: write mouse 0x%02x\n", val);
+#endif
+    switch(s->common.write_cmd) {
+    default:
+    case -1:
+        /* mouse command */
+        if (s->mouse_wrap) {
+            if (val == AUX_RESET_WRAP) {
+                s->mouse_wrap = 0;
+                ps2_queue(&s->common, AUX_ACK);
+                return;
+            } else if (val != AUX_RESET) {
+                ps2_queue(&s->common, val);
+                return;
+            }
+        }
+        switch(val) {
+        case AUX_SET_SCALE11:
+            s->mouse_status &= ~MOUSE_STATUS_SCALE21;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_SET_SCALE21:
+            s->mouse_status |= MOUSE_STATUS_SCALE21;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_SET_STREAM:
+            s->mouse_status &= ~MOUSE_STATUS_REMOTE;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_SET_WRAP:
+            s->mouse_wrap = 1;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_SET_REMOTE:
+            s->mouse_status |= MOUSE_STATUS_REMOTE;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_GET_TYPE:
+            ps2_queue(&s->common, AUX_ACK);
+            ps2_queue(&s->common, s->mouse_type);
+            break;
+        case AUX_SET_RES:
+        case AUX_SET_SAMPLE:
+            s->common.write_cmd = val;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_GET_SCALE:
+            ps2_queue(&s->common, AUX_ACK);
+            ps2_queue(&s->common, s->mouse_status);
+            ps2_queue(&s->common, s->mouse_resolution);
+            ps2_queue(&s->common, s->mouse_sample_rate);
+            break;
+        case AUX_POLL:
+            ps2_queue(&s->common, AUX_ACK);
+            ps2_mouse_send_packet(s);
+            break;
+        case AUX_ENABLE_DEV:
+            s->mouse_status |= MOUSE_STATUS_ENABLED;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_DISABLE_DEV:
+            s->mouse_status &= ~MOUSE_STATUS_ENABLED;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_SET_DEFAULT:
+            s->mouse_sample_rate = 100;
+            s->mouse_resolution = 2;
+            s->mouse_status = 0;
+            ps2_queue(&s->common, AUX_ACK);
+            break;
+        case AUX_RESET:
+            s->mouse_sample_rate = 100;
+            s->mouse_resolution = 2;
+            s->mouse_status = 0;
+            s->mouse_type = 0;
+            ps2_queue(&s->common, AUX_ACK);
+            ps2_queue(&s->common, 0xaa);
+            ps2_queue(&s->common, s->mouse_type);
+            break;
+        default:
+            break;
+        }
+        break;
+    case AUX_SET_SAMPLE:
+        s->mouse_sample_rate = val;
+        /* detect IMPS/2 or IMEX */
+        switch(s->mouse_detect_state) {
+        default:
+        case 0:
+            if (val == 200)
+                s->mouse_detect_state = 1;
+            break;
+        case 1:
+            if (val == 100)
+                s->mouse_detect_state = 2;
+            else if (val == 200)
+                s->mouse_detect_state = 3;
+            else
+                s->mouse_detect_state = 0;
+            break;
+        case 2:
+            if (val == 80)
+                s->mouse_type = 3; /* IMPS/2 */
+            s->mouse_detect_state = 0;
+            break;
+        case 3:
+            if (val == 80)
+                s->mouse_type = 4; /* IMEX */
+            s->mouse_detect_state = 0;
+            break;
+        }
+        ps2_queue(&s->common, AUX_ACK);
+        s->common.write_cmd = -1;
+        break;
+    case AUX_SET_RES:
+        s->mouse_resolution = val;
+        ps2_queue(&s->common, AUX_ACK);
+        s->common.write_cmd = -1;
+        break;
+    }
+}
+
+static void ps2_common_reset(PS2State *s)
+{
+    PS2Queue *q;
+    s->write_cmd = -1;
+    q = &s->queue;
+    q->rptr = 0;
+    q->wptr = 0;
+    q->count = 0;
+    s->update_irq(s->update_arg, 0);
+}
+
+static void ps2_kbd_reset(void *opaque)
+{
+    PS2KbdState *s = (PS2KbdState *) opaque;
+
+    ps2_common_reset(&s->common);
+    s->scan_enabled = 0;
+    s->translate = 0;
+    s->scancode_set = 0;
+}
+
+static void ps2_mouse_reset(void *opaque)
+{
+    PS2MouseState *s = (PS2MouseState *) opaque;
+
+    ps2_common_reset(&s->common);
+    s->mouse_status = 0;
+    s->mouse_resolution = 0;
+    s->mouse_sample_rate = 0;
+    s->mouse_wrap = 0;
+    s->mouse_type = 0;
+    s->mouse_detect_state = 0;
+    s->mouse_dx = 0;
+    s->mouse_dy = 0;
+    s->mouse_dz = 0;
+    s->mouse_buttons = 0;
+}
+
+static const VMStateDescription vmstate_ps2_common = {
+    .name = "PS2 Common State",
+    .version_id = 3,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields      = (VMStateField []) {
+        VMSTATE_INT32(write_cmd, PS2State),
+        VMSTATE_INT32(queue.rptr, PS2State),
+        VMSTATE_INT32(queue.wptr, PS2State),
+        VMSTATE_INT32(queue.count, PS2State),
+        VMSTATE_BUFFER(queue.data, PS2State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static bool ps2_keyboard_ledstate_needed(void *opaque)
+{
+    PS2KbdState *s = opaque;
+
+    return s->ledstate != 0; /* 0 is default state */
+}
+
+static int ps2_kbd_ledstate_post_load(void *opaque, int version_id)
+{
+    PS2KbdState *s = opaque;
+
+    kbd_put_ledstate(s->ledstate);
+    return 0;
+}
+
+static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
+    .name = "ps2kbd/ledstate",
+    .version_id = 3,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .post_load = ps2_kbd_ledstate_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_INT32(ledstate, PS2KbdState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int ps2_kbd_post_load(void* opaque, int version_id)
+{
+    PS2KbdState *s = (PS2KbdState*)opaque;
+
+    if (version_id == 2)
+        s->scancode_set=2;
+    return 0;
+}
+
+static const VMStateDescription vmstate_ps2_keyboard = {
+    .name = "ps2kbd",
+    .version_id = 3,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .post_load = ps2_kbd_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_STRUCT(common, PS2KbdState, 0, vmstate_ps2_common, PS2State),
+        VMSTATE_INT32(scan_enabled, PS2KbdState),
+        VMSTATE_INT32(translate, PS2KbdState),
+        VMSTATE_INT32_V(scancode_set, PS2KbdState,3),
+        VMSTATE_END_OF_LIST()
+    },
+    .subsections = (VMStateSubsection []) {
+        {
+            .vmsd = &vmstate_ps2_keyboard_ledstate,
+            .needed = ps2_keyboard_ledstate_needed,
+        }, {
+            /* empty */
+        }
+    }
+};
+
+static const VMStateDescription vmstate_ps2_mouse = {
+    .name = "ps2mouse",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields      = (VMStateField []) {
+        VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State),
+        VMSTATE_UINT8(mouse_status, PS2MouseState),
+        VMSTATE_UINT8(mouse_resolution, PS2MouseState),
+        VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
+        VMSTATE_UINT8(mouse_wrap, PS2MouseState),
+        VMSTATE_UINT8(mouse_type, PS2MouseState),
+        VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
+        VMSTATE_INT32(mouse_dx, PS2MouseState),
+        VMSTATE_INT32(mouse_dy, PS2MouseState),
+        VMSTATE_INT32(mouse_dz, PS2MouseState),
+        VMSTATE_UINT8(mouse_buttons, PS2MouseState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
+{
+    PS2KbdState *s = (PS2KbdState *)g_malloc0(sizeof(PS2KbdState));
+
+    s->common.update_irq = update_irq;
+    s->common.update_arg = update_arg;
+    s->scancode_set = 2;
+    vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s);
+    qemu_add_kbd_event_handler(ps2_put_keycode, s);
+    qemu_register_reset(ps2_kbd_reset, s);
+    return s;
+}
+
+void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
+{
+    PS2MouseState *s = (PS2MouseState *)g_malloc0(sizeof(PS2MouseState));
+
+    s->common.update_irq = update_irq;
+    s->common.update_arg = update_arg;
+    vmstate_register(NULL, 0, &vmstate_ps2_mouse, s);
+    qemu_add_mouse_event_handler(ps2_mouse_event, s, 0, "QEMU PS/2 Mouse");
+    qemu_register_reset(ps2_mouse_reset, s);
+    return s;
+}
diff --git a/hw/input/stellaris_input.c b/hw/input/stellaris_input.c
new file mode 100644 (file)
index 0000000..f83fc3f
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Gamepad style buttons connected to IRQ/GPIO lines
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+#include "hw/hw.h"
+#include "hw/arm/devices.h"
+#include "ui/console.h"
+
+typedef struct {
+    qemu_irq irq;
+    int keycode;
+    uint8_t pressed;
+} gamepad_button;
+
+typedef struct {
+    gamepad_button *buttons;
+    int num_buttons;
+    int extension;
+} gamepad_state;
+
+static void stellaris_gamepad_put_key(void * opaque, int keycode)
+{
+    gamepad_state *s = (gamepad_state *)opaque;
+    int i;
+    int down;
+
+    if (keycode == 0xe0 && !s->extension) {
+        s->extension = 0x80;
+        return;
+    }
+
+    down = (keycode & 0x80) == 0;
+    keycode = (keycode & 0x7f) | s->extension;
+
+    for (i = 0; i < s->num_buttons; i++) {
+        if (s->buttons[i].keycode == keycode
+                && s->buttons[i].pressed != down) {
+            s->buttons[i].pressed = down;
+            qemu_set_irq(s->buttons[i].irq, down);
+        }
+    }
+
+    s->extension = 0;
+}
+
+static const VMStateDescription vmstate_stellaris_button = {
+    .name = "stellaris_button",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(pressed, gamepad_button),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_stellaris_gamepad = {
+    .name = "stellaris_gamepad",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT32(extension, gamepad_state),
+        VMSTATE_STRUCT_VARRAY_INT32(buttons, gamepad_state, num_buttons, 0,
+                              vmstate_stellaris_button, gamepad_button),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/* Returns an array 5 ouput slots.  */
+void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode)
+{
+    gamepad_state *s;
+    int i;
+
+    s = (gamepad_state *)g_malloc0(sizeof (gamepad_state));
+    s->buttons = (gamepad_button *)g_malloc0(n * sizeof (gamepad_button));
+    for (i = 0; i < n; i++) {
+        s->buttons[i].irq = irq[i];
+        s->buttons[i].keycode = keycode[i];
+    }
+    s->num_buttons = n;
+    qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s);
+    vmstate_register(NULL, -1, &vmstate_stellaris_gamepad, s);
+}
diff --git a/hw/input/tsc2005.c b/hw/input/tsc2005.c
new file mode 100644 (file)
index 0000000..34ee1fb
--- /dev/null
@@ -0,0 +1,593 @@
+/*
+ * TI TSC2005 emulator.
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski  <balrog@zabor.org>
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "qemu/timer.h"
+#include "ui/console.h"
+#include "hw/arm/devices.h"
+
+#define TSC_CUT_RESOLUTION(value, p)   ((value) >> (16 - (p ? 12 : 10)))
+
+typedef struct {
+    qemu_irq pint;     /* Combination of the nPENIRQ and DAV signals */
+    QEMUTimer *timer;
+    uint16_t model;
+
+    int x, y;
+    int pressure;
+
+    int state, reg, irq, command;
+    uint16_t data, dav;
+
+    int busy;
+    int enabled;
+    int host_mode;
+    int function;
+    int nextfunction;
+    int precision;
+    int nextprecision;
+    int filter;
+    int pin_func;
+    int timing[2];
+    int noise;
+    int reset;
+    int pdst;
+    int pnd0;
+    uint16_t temp_thr[2];
+    uint16_t aux_thr[2];
+
+    int tr[8];
+} TSC2005State;
+
+enum {
+    TSC_MODE_XYZ_SCAN  = 0x0,
+    TSC_MODE_XY_SCAN,
+    TSC_MODE_X,
+    TSC_MODE_Y,
+    TSC_MODE_Z,
+    TSC_MODE_AUX,
+    TSC_MODE_TEMP1,
+    TSC_MODE_TEMP2,
+    TSC_MODE_AUX_SCAN,
+    TSC_MODE_X_TEST,
+    TSC_MODE_Y_TEST,
+    TSC_MODE_TS_TEST,
+    TSC_MODE_RESERVED,
+    TSC_MODE_XX_DRV,
+    TSC_MODE_YY_DRV,
+    TSC_MODE_YX_DRV,
+};
+
+static const uint16_t mode_regs[16] = {
+    0xf000,    /* X, Y, Z scan */
+    0xc000,    /* X, Y scan */
+    0x8000,    /* X */
+    0x4000,    /* Y */
+    0x3000,    /* Z */
+    0x0800,    /* AUX */
+    0x0400,    /* TEMP1 */
+    0x0200,    /* TEMP2 */
+    0x0800,    /* AUX scan */
+    0x0040,    /* X test */
+    0x0020,    /* Y test */
+    0x0080,    /* Short-circuit test */
+    0x0000,    /* Reserved */
+    0x0000,    /* X+, X- drivers */
+    0x0000,    /* Y+, Y- drivers */
+    0x0000,    /* Y+, X- drivers */
+};
+
+#define X_TRANSFORM(s)                 \
+    ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3])
+#define Y_TRANSFORM(s)                 \
+    ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7])
+#define Z1_TRANSFORM(s)                        \
+    ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4)
+#define Z2_TRANSFORM(s)                        \
+    ((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4)
+
+#define AUX_VAL                                (700 << 4)      /* +/- 3 at 12-bit */
+#define TEMP1_VAL                      (1264 << 4)     /* +/- 5 at 12-bit */
+#define TEMP2_VAL                      (1531 << 4)     /* +/- 5 at 12-bit */
+
+static uint16_t tsc2005_read(TSC2005State *s, int reg)
+{
+    uint16_t ret;
+
+    switch (reg) {
+    case 0x0:  /* X */
+        s->dav &= ~mode_regs[TSC_MODE_X];
+        return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) +
+                (s->noise & 3);
+    case 0x1:  /* Y */
+        s->dav &= ~mode_regs[TSC_MODE_Y];
+        s->noise ++;
+        return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^
+                (s->noise & 3);
+    case 0x2:  /* Z1 */
+        s->dav &= 0xdfff;
+        return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) -
+                (s->noise & 3);
+    case 0x3:  /* Z2 */
+        s->dav &= 0xefff;
+        return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) |
+                (s->noise & 3);
+
+    case 0x4:  /* AUX */
+        s->dav &= ~mode_regs[TSC_MODE_AUX];
+        return TSC_CUT_RESOLUTION(AUX_VAL, s->precision);
+
+    case 0x5:  /* TEMP1 */
+        s->dav &= ~mode_regs[TSC_MODE_TEMP1];
+        return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) -
+                (s->noise & 5);
+    case 0x6:  /* TEMP2 */
+        s->dav &= 0xdfff;
+        s->dav &= ~mode_regs[TSC_MODE_TEMP2];
+        return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^
+                (s->noise & 3);
+
+    case 0x7:  /* Status */
+        ret = s->dav | (s->reset << 7) | (s->pdst << 2) | 0x0;
+        s->dav &= ~(mode_regs[TSC_MODE_X_TEST] | mode_regs[TSC_MODE_Y_TEST] |
+                        mode_regs[TSC_MODE_TS_TEST]);
+        s->reset = 1;
+        return ret;
+
+    case 0x8:  /* AUX high treshold */
+        return s->aux_thr[1];
+    case 0x9:  /* AUX low treshold */
+        return s->aux_thr[0];
+
+    case 0xa:  /* TEMP high treshold */
+        return s->temp_thr[1];
+    case 0xb:  /* TEMP low treshold */
+        return s->temp_thr[0];
+
+    case 0xc:  /* CFR0 */
+        return (s->pressure << 15) | ((!s->busy) << 14) |
+                (s->nextprecision << 13) | s->timing[0]; 
+    case 0xd:  /* CFR1 */
+        return s->timing[1];
+    case 0xe:  /* CFR2 */
+        return (s->pin_func << 14) | s->filter;
+
+    case 0xf:  /* Function select status */
+        return s->function >= 0 ? 1 << s->function : 0;
+    }
+
+    /* Never gets here */
+    return 0xffff;
+}
+
+static void tsc2005_write(TSC2005State *s, int reg, uint16_t data)
+{
+    switch (reg) {
+    case 0x8:  /* AUX high treshold */
+        s->aux_thr[1] = data;
+        break;
+    case 0x9:  /* AUX low treshold */
+        s->aux_thr[0] = data;
+        break;
+
+    case 0xa:  /* TEMP high treshold */
+        s->temp_thr[1] = data;
+        break;
+    case 0xb:  /* TEMP low treshold */
+        s->temp_thr[0] = data;
+        break;
+
+    case 0xc:  /* CFR0 */
+        s->host_mode = data >> 15;
+        if (s->enabled != !(data & 0x4000)) {
+            s->enabled = !(data & 0x4000);
+            fprintf(stderr, "%s: touchscreen sense %sabled\n",
+                            __FUNCTION__, s->enabled ? "en" : "dis");
+            if (s->busy && !s->enabled)
+                qemu_del_timer(s->timer);
+            s->busy &= s->enabled;
+        }
+        s->nextprecision = (data >> 13) & 1;
+        s->timing[0] = data & 0x1fff;
+        if ((s->timing[0] >> 11) == 3)
+            fprintf(stderr, "%s: illegal conversion clock setting\n",
+                            __FUNCTION__);
+        break;
+    case 0xd:  /* CFR1 */
+        s->timing[1] = data & 0xf07;
+        break;
+    case 0xe:  /* CFR2 */
+        s->pin_func = (data >> 14) & 3;
+        s->filter = data & 0x3fff;
+        break;
+
+    default:
+        fprintf(stderr, "%s: write into read-only register %x\n",
+                        __FUNCTION__, reg);
+    }
+}
+
+/* This handles most of the chip's logic.  */
+static void tsc2005_pin_update(TSC2005State *s)
+{
+    int64_t expires;
+    int pin_state;
+
+    switch (s->pin_func) {
+    case 0:
+        pin_state = !s->pressure && !!s->dav;
+        break;
+    case 1:
+    case 3:
+    default:
+        pin_state = !s->dav;
+        break;
+    case 2:
+        pin_state = !s->pressure;
+    }
+
+    if (pin_state != s->irq) {
+        s->irq = pin_state;
+        qemu_set_irq(s->pint, s->irq);
+    }
+
+    switch (s->nextfunction) {
+    case TSC_MODE_XYZ_SCAN:
+    case TSC_MODE_XY_SCAN:
+        if (!s->host_mode && s->dav)
+            s->enabled = 0;
+        if (!s->pressure)
+            return;
+        /* Fall through */
+    case TSC_MODE_AUX_SCAN:
+        break;
+
+    case TSC_MODE_X:
+    case TSC_MODE_Y:
+    case TSC_MODE_Z:
+        if (!s->pressure)
+            return;
+        /* Fall through */
+    case TSC_MODE_AUX:
+    case TSC_MODE_TEMP1:
+    case TSC_MODE_TEMP2:
+    case TSC_MODE_X_TEST:
+    case TSC_MODE_Y_TEST:
+    case TSC_MODE_TS_TEST:
+        if (s->dav)
+            s->enabled = 0;
+        break;
+
+    case TSC_MODE_RESERVED:
+    case TSC_MODE_XX_DRV:
+    case TSC_MODE_YY_DRV:
+    case TSC_MODE_YX_DRV:
+    default:
+        return;
+    }
+
+    if (!s->enabled || s->busy)
+        return;
+
+    s->busy = 1;
+    s->precision = s->nextprecision;
+    s->function = s->nextfunction;
+    s->pdst = !s->pnd0;        /* Synchronised on internal clock */
+    expires = qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() >> 7);
+    qemu_mod_timer(s->timer, expires);
+}
+
+static void tsc2005_reset(TSC2005State *s)
+{
+    s->state = 0;
+    s->pin_func = 0;
+    s->enabled = 0;
+    s->busy = 0;
+    s->nextprecision = 0;
+    s->nextfunction = 0;
+    s->timing[0] = 0;
+    s->timing[1] = 0;
+    s->irq = 0;
+    s->dav = 0;
+    s->reset = 0;
+    s->pdst = 1;
+    s->pnd0 = 0;
+    s->function = -1;
+    s->temp_thr[0] = 0x000;
+    s->temp_thr[1] = 0xfff;
+    s->aux_thr[0] = 0x000;
+    s->aux_thr[1] = 0xfff;
+
+    tsc2005_pin_update(s);
+}
+
+static uint8_t tsc2005_txrx_word(void *opaque, uint8_t value)
+{
+    TSC2005State *s = opaque;
+    uint32_t ret = 0;
+
+    switch (s->state ++) {
+    case 0:
+        if (value & 0x80) {
+            /* Command */
+            if (value & (1 << 1))
+                tsc2005_reset(s);
+            else {
+                s->nextfunction = (value >> 3) & 0xf;
+                s->nextprecision = (value >> 2) & 1;
+                if (s->enabled != !(value & 1)) {
+                    s->enabled = !(value & 1);
+                    fprintf(stderr, "%s: touchscreen sense %sabled\n",
+                                    __FUNCTION__, s->enabled ? "en" : "dis");
+                    if (s->busy && !s->enabled)
+                        qemu_del_timer(s->timer);
+                    s->busy &= s->enabled;
+                }
+                tsc2005_pin_update(s);
+            }
+
+            s->state = 0;
+        } else if (value) {
+            /* Data transfer */
+            s->reg = (value >> 3) & 0xf;
+            s->pnd0 = (value >> 1) & 1;
+            s->command = value & 1;
+
+            if (s->command) {
+                /* Read */
+                s->data = tsc2005_read(s, s->reg);
+                tsc2005_pin_update(s);
+            } else
+                s->data = 0;
+        } else
+            s->state = 0;
+        break;
+
+    case 1:
+        if (s->command)
+            ret = (s->data >> 8) & 0xff;
+        else
+            s->data |= value << 8;
+        break;
+
+    case 2:
+        if (s->command)
+            ret = s->data & 0xff;
+        else {
+            s->data |= value;
+            tsc2005_write(s, s->reg, s->data);
+            tsc2005_pin_update(s);
+        }
+
+        s->state = 0;
+        break;
+    }
+
+    return ret;
+}
+
+uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len)
+{
+    uint32_t ret = 0;
+
+    len &= ~7;
+    while (len > 0) {
+        len -= 8;
+        ret |= tsc2005_txrx_word(opaque, (value >> len) & 0xff) << len;
+    }
+
+    return ret;
+}
+
+static void tsc2005_timer_tick(void *opaque)
+{
+    TSC2005State *s = opaque;
+
+    /* Timer ticked -- a set of conversions has been finished.  */
+
+    if (!s->busy)
+        return;
+
+    s->busy = 0;
+    s->dav |= mode_regs[s->function];
+    s->function = -1;
+    tsc2005_pin_update(s);
+}
+
+static void tsc2005_touchscreen_event(void *opaque,
+                int x, int y, int z, int buttons_state)
+{
+    TSC2005State *s = opaque;
+    int p = s->pressure;
+
+    if (buttons_state) {
+        s->x = x;
+        s->y = y;
+    }
+    s->pressure = !!buttons_state;
+
+    /*
+     * Note: We would get better responsiveness in the guest by
+     * signaling TS events immediately, but for now we simulate
+     * the first conversion delay for sake of correctness.
+     */
+    if (p != s->pressure)
+        tsc2005_pin_update(s);
+}
+
+static void tsc2005_save(QEMUFile *f, void *opaque)
+{
+    TSC2005State *s = (TSC2005State *) opaque;
+    int i;
+
+    qemu_put_be16(f, s->x);
+    qemu_put_be16(f, s->y);
+    qemu_put_byte(f, s->pressure);
+
+    qemu_put_byte(f, s->state);
+    qemu_put_byte(f, s->reg);
+    qemu_put_byte(f, s->command);
+
+    qemu_put_byte(f, s->irq);
+    qemu_put_be16s(f, &s->dav);
+    qemu_put_be16s(f, &s->data);
+
+    qemu_put_timer(f, s->timer);
+    qemu_put_byte(f, s->enabled);
+    qemu_put_byte(f, s->host_mode);
+    qemu_put_byte(f, s->function);
+    qemu_put_byte(f, s->nextfunction);
+    qemu_put_byte(f, s->precision);
+    qemu_put_byte(f, s->nextprecision);
+    qemu_put_be16(f, s->filter);
+    qemu_put_byte(f, s->pin_func);
+    qemu_put_be16(f, s->timing[0]);
+    qemu_put_be16(f, s->timing[1]);
+    qemu_put_be16s(f, &s->temp_thr[0]);
+    qemu_put_be16s(f, &s->temp_thr[1]);
+    qemu_put_be16s(f, &s->aux_thr[0]);
+    qemu_put_be16s(f, &s->aux_thr[1]);
+    qemu_put_be32(f, s->noise);
+    qemu_put_byte(f, s->reset);
+    qemu_put_byte(f, s->pdst);
+    qemu_put_byte(f, s->pnd0);
+
+    for (i = 0; i < 8; i ++)
+        qemu_put_be32(f, s->tr[i]);
+}
+
+static int tsc2005_load(QEMUFile *f, void *opaque, int version_id)
+{
+    TSC2005State *s = (TSC2005State *) opaque;
+    int i;
+
+    s->x = qemu_get_be16(f);
+    s->y = qemu_get_be16(f);
+    s->pressure = qemu_get_byte(f);
+
+    s->state = qemu_get_byte(f);
+    s->reg = qemu_get_byte(f);
+    s->command = qemu_get_byte(f);
+
+    s->irq = qemu_get_byte(f);
+    qemu_get_be16s(f, &s->dav);
+    qemu_get_be16s(f, &s->data);
+
+    qemu_get_timer(f, s->timer);
+    s->enabled = qemu_get_byte(f);
+    s->host_mode = qemu_get_byte(f);
+    s->function = qemu_get_byte(f);
+    s->nextfunction = qemu_get_byte(f);
+    s->precision = qemu_get_byte(f);
+    s->nextprecision = qemu_get_byte(f);
+    s->filter = qemu_get_be16(f);
+    s->pin_func = qemu_get_byte(f);
+    s->timing[0] = qemu_get_be16(f);
+    s->timing[1] = qemu_get_be16(f);
+    qemu_get_be16s(f, &s->temp_thr[0]);
+    qemu_get_be16s(f, &s->temp_thr[1]);
+    qemu_get_be16s(f, &s->aux_thr[0]);
+    qemu_get_be16s(f, &s->aux_thr[1]);
+    s->noise = qemu_get_be32(f);
+    s->reset = qemu_get_byte(f);
+    s->pdst = qemu_get_byte(f);
+    s->pnd0 = qemu_get_byte(f);
+
+    for (i = 0; i < 8; i ++)
+        s->tr[i] = qemu_get_be32(f);
+
+    s->busy = qemu_timer_pending(s->timer);
+    tsc2005_pin_update(s);
+
+    return 0;
+}
+
+void *tsc2005_init(qemu_irq pintdav)
+{
+    TSC2005State *s;
+
+    s = (TSC2005State *)
+            g_malloc0(sizeof(TSC2005State));
+    s->x = 400;
+    s->y = 240;
+    s->pressure = 0;
+    s->precision = s->nextprecision = 0;
+    s->timer = qemu_new_timer_ns(vm_clock, tsc2005_timer_tick, s);
+    s->pint = pintdav;
+    s->model = 0x2005;
+
+    s->tr[0] = 0;
+    s->tr[1] = 1;
+    s->tr[2] = 1;
+    s->tr[3] = 0;
+    s->tr[4] = 1;
+    s->tr[5] = 0;
+    s->tr[6] = 1;
+    s->tr[7] = 0;
+
+    tsc2005_reset(s);
+
+    qemu_add_mouse_event_handler(tsc2005_touchscreen_event, s, 1,
+                    "QEMU TSC2005-driven Touchscreen");
+
+    qemu_register_reset((void *) tsc2005_reset, s);
+    register_savevm(NULL, "tsc2005", -1, 0, tsc2005_save, tsc2005_load, s);
+
+    return s;
+}
+
+/*
+ * Use tslib generated calibration data to generate ADC input values
+ * from the touchscreen.  Assuming 12-bit precision was used during
+ * tslib calibration.
+ */
+void tsc2005_set_transform(void *opaque, MouseTransformInfo *info)
+{
+    TSC2005State *s = (TSC2005State *) opaque;
+
+    /* This version assumes touchscreen X & Y axis are parallel or
+     * perpendicular to LCD's  X & Y axis in some way.  */
+    if (abs(info->a[0]) > abs(info->a[1])) {
+        s->tr[0] = 0;
+        s->tr[1] = -info->a[6] * info->x;
+        s->tr[2] = info->a[0];
+        s->tr[3] = -info->a[2] / info->a[0];
+        s->tr[4] = info->a[6] * info->y;
+        s->tr[5] = 0;
+        s->tr[6] = info->a[4];
+        s->tr[7] = -info->a[5] / info->a[4];
+    } else {
+        s->tr[0] = info->a[6] * info->y;
+        s->tr[1] = 0;
+        s->tr[2] = info->a[1];
+        s->tr[3] = -info->a[2] / info->a[1];
+        s->tr[4] = 0;
+        s->tr[5] = -info->a[6] * info->x;
+        s->tr[6] = info->a[3];
+        s->tr[7] = -info->a[5] / info->a[3];
+    }
+
+    s->tr[0] >>= 11;
+    s->tr[1] >>= 11;
+    s->tr[3] <<= 4;
+    s->tr[4] >>= 11;
+    s->tr[5] >>= 11;
+    s->tr[7] <<= 4;
+}
diff --git a/hw/input/vmmouse.c b/hw/input/vmmouse.c
new file mode 100644 (file)
index 0000000..f4f9c93
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * QEMU VMMouse emulation
+ *
+ * Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "ui/console.h"
+#include "hw/input/ps2.h"
+#include "hw/i386/pc.h"
+#include "hw/qdev.h"
+
+/* debug only vmmouse */
+//#define DEBUG_VMMOUSE
+
+/* VMMouse Commands */
+#define VMMOUSE_GETVERSION     10
+#define VMMOUSE_DATA           39
+#define VMMOUSE_STATUS         40
+#define VMMOUSE_COMMAND                41
+
+#define VMMOUSE_READ_ID                        0x45414552
+#define VMMOUSE_DISABLE                        0x000000f5
+#define VMMOUSE_REQUEST_RELATIVE       0x4c455252
+#define VMMOUSE_REQUEST_ABSOLUTE       0x53424152
+
+#define VMMOUSE_QUEUE_SIZE     1024
+
+#define VMMOUSE_VERSION                0x3442554a
+
+#ifdef DEBUG_VMMOUSE
+#define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+typedef struct _VMMouseState
+{
+    ISADevice dev;
+    uint32_t queue[VMMOUSE_QUEUE_SIZE];
+    int32_t queue_size;
+    uint16_t nb_queue;
+    uint16_t status;
+    uint8_t absolute;
+    QEMUPutMouseEntry *entry;
+    void *ps2_mouse;
+} VMMouseState;
+
+static uint32_t vmmouse_get_status(VMMouseState *s)
+{
+    DPRINTF("vmmouse_get_status()\n");
+    return (s->status << 16) | s->nb_queue;
+}
+
+static void vmmouse_mouse_event(void *opaque, int x, int y, int dz, int buttons_state)
+{
+    VMMouseState *s = opaque;
+    int buttons = 0;
+
+    if (s->nb_queue > (VMMOUSE_QUEUE_SIZE - 4))
+        return;
+
+    DPRINTF("vmmouse_mouse_event(%d, %d, %d, %d)\n",
+            x, y, dz, buttons_state);
+
+    if ((buttons_state & MOUSE_EVENT_LBUTTON))
+        buttons |= 0x20;
+    if ((buttons_state & MOUSE_EVENT_RBUTTON))
+        buttons |= 0x10;
+    if ((buttons_state & MOUSE_EVENT_MBUTTON))
+        buttons |= 0x08;
+
+    if (s->absolute) {
+        x <<= 1;
+        y <<= 1;
+    }
+
+    s->queue[s->nb_queue++] = buttons;
+    s->queue[s->nb_queue++] = x;
+    s->queue[s->nb_queue++] = y;
+    s->queue[s->nb_queue++] = dz;
+
+    /* need to still generate PS2 events to notify driver to
+       read from queue */
+    i8042_isa_mouse_fake_event(s->ps2_mouse);
+}
+
+static void vmmouse_remove_handler(VMMouseState *s)
+{
+    if (s->entry) {
+        qemu_remove_mouse_event_handler(s->entry);
+        s->entry = NULL;
+    }
+}
+
+static void vmmouse_update_handler(VMMouseState *s, int absolute)
+{
+    if (s->status != 0) {
+        return;
+    }
+    if (s->absolute != absolute) {
+        s->absolute = absolute;
+        vmmouse_remove_handler(s);
+    }
+    if (s->entry == NULL) {
+        s->entry = qemu_add_mouse_event_handler(vmmouse_mouse_event,
+                                                s, s->absolute,
+                                                "vmmouse");
+        qemu_activate_mouse_event_handler(s->entry);
+    }
+}
+
+static void vmmouse_read_id(VMMouseState *s)
+{
+    DPRINTF("vmmouse_read_id()\n");
+
+    if (s->nb_queue == VMMOUSE_QUEUE_SIZE)
+        return;
+
+    s->queue[s->nb_queue++] = VMMOUSE_VERSION;
+    s->status = 0;
+}
+
+static void vmmouse_request_relative(VMMouseState *s)
+{
+    DPRINTF("vmmouse_request_relative()\n");
+    vmmouse_update_handler(s, 0);
+}
+
+static void vmmouse_request_absolute(VMMouseState *s)
+{
+    DPRINTF("vmmouse_request_absolute()\n");
+    vmmouse_update_handler(s, 1);
+}
+
+static void vmmouse_disable(VMMouseState *s)
+{
+    DPRINTF("vmmouse_disable()\n");
+    s->status = 0xffff;
+    vmmouse_remove_handler(s);
+}
+
+static void vmmouse_data(VMMouseState *s, uint32_t *data, uint32_t size)
+{
+    int i;
+
+    DPRINTF("vmmouse_data(%d)\n", size);
+
+    if (size == 0 || size > 6 || size > s->nb_queue) {
+        printf("vmmouse: driver requested too much data %d\n", size);
+        s->status = 0xffff;
+        vmmouse_remove_handler(s);
+        return;
+    }
+
+    for (i = 0; i < size; i++)
+        data[i] = s->queue[i];
+
+    s->nb_queue -= size;
+    if (s->nb_queue)
+        memmove(s->queue, &s->queue[size], sizeof(s->queue[0]) * s->nb_queue);
+}
+
+static uint32_t vmmouse_ioport_read(void *opaque, uint32_t addr)
+{
+    VMMouseState *s = opaque;
+    uint32_t data[6];
+    uint16_t command;
+
+    vmmouse_get_data(data);
+
+    command = data[2] & 0xFFFF;
+
+    switch (command) {
+    case VMMOUSE_STATUS:
+        data[0] = vmmouse_get_status(s);
+        break;
+    case VMMOUSE_COMMAND:
+        switch (data[1]) {
+        case VMMOUSE_DISABLE:
+            vmmouse_disable(s);
+            break;
+        case VMMOUSE_READ_ID:
+            vmmouse_read_id(s);
+            break;
+        case VMMOUSE_REQUEST_RELATIVE:
+            vmmouse_request_relative(s);
+            break;
+        case VMMOUSE_REQUEST_ABSOLUTE:
+            vmmouse_request_absolute(s);
+            break;
+        default:
+            printf("vmmouse: unknown command %x\n", data[1]);
+            break;
+        }
+        break;
+    case VMMOUSE_DATA:
+        vmmouse_data(s, data, data[1]);
+        break;
+    default:
+        printf("vmmouse: unknown command %x\n", command);
+        break;
+    }
+
+    vmmouse_set_data(data);
+    return data[0];
+}
+
+static int vmmouse_post_load(void *opaque, int version_id)
+{
+    VMMouseState *s = opaque;
+
+    vmmouse_remove_handler(s);
+    vmmouse_update_handler(s, s->absolute);
+    return 0;
+}
+
+static const VMStateDescription vmstate_vmmouse = {
+    .name = "vmmouse",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .post_load = vmmouse_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_INT32_EQUAL(queue_size, VMMouseState),
+        VMSTATE_UINT32_ARRAY(queue, VMMouseState, VMMOUSE_QUEUE_SIZE),
+        VMSTATE_UINT16(nb_queue, VMMouseState),
+        VMSTATE_UINT16(status, VMMouseState),
+        VMSTATE_UINT8(absolute, VMMouseState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void vmmouse_reset(DeviceState *d)
+{
+    VMMouseState *s = container_of(d, VMMouseState, dev.qdev);
+
+    s->queue_size = VMMOUSE_QUEUE_SIZE;
+
+    vmmouse_disable(s);
+}
+
+static int vmmouse_initfn(ISADevice *dev)
+{
+    VMMouseState *s = DO_UPCAST(VMMouseState, dev, dev);
+
+    DPRINTF("vmmouse_init\n");
+
+    vmport_register(VMMOUSE_STATUS, vmmouse_ioport_read, s);
+    vmport_register(VMMOUSE_COMMAND, vmmouse_ioport_read, s);
+    vmport_register(VMMOUSE_DATA, vmmouse_ioport_read, s);
+
+    return 0;
+}
+
+static Property vmmouse_properties[] = {
+    DEFINE_PROP_PTR("ps2_mouse", VMMouseState, ps2_mouse),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vmmouse_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+    ic->init = vmmouse_initfn;
+    dc->no_user = 1;
+    dc->reset = vmmouse_reset;
+    dc->vmsd = &vmstate_vmmouse;
+    dc->props = vmmouse_properties;
+}
+
+static const TypeInfo vmmouse_info = {
+    .name          = "vmmouse",
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(VMMouseState),
+    .class_init    = vmmouse_class_initfn,
+};
+
+static void vmmouse_register_types(void)
+{
+    type_register_static(&vmmouse_info);
+}
+
+type_init(vmmouse_register_types)
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2813adb3e752ba1d37654e2243cd7251b1ccef58 100644 (file)
@@ -0,0 +1,5 @@
+common-obj-$(CONFIG_HEATHROW_PIC) += heathrow_pic.o
+common-obj-$(CONFIG_I8259) += i8259_common.o i8259.o
+common-obj-$(CONFIG_PL190) += pl190.o
+common-obj-$(CONFIG_PUV3) += puv3_intc.o
+common-obj-$(CONFIG_XILINX) += xilinx_intc.o
diff --git a/hw/intc/heathrow_pic.c b/hw/intc/heathrow_pic.c
new file mode 100644 (file)
index 0000000..beb9661
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * Heathrow PIC support (OldWorld PowerMac)
+ *
+ * Copyright (c) 2005-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/ppc/mac.h"
+
+/* debug PIC */
+//#define DEBUG_PIC
+
+#ifdef DEBUG_PIC
+#define PIC_DPRINTF(fmt, ...)                                   \
+    do { printf("PIC: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define PIC_DPRINTF(fmt, ...)
+#endif
+
+typedef struct HeathrowPIC {
+    uint32_t events;
+    uint32_t mask;
+    uint32_t levels;
+    uint32_t level_triggered;
+} HeathrowPIC;
+
+typedef struct HeathrowPICS {
+    MemoryRegion mem;
+    HeathrowPIC pics[2];
+    qemu_irq *irqs;
+} HeathrowPICS;
+
+static inline int check_irq(HeathrowPIC *pic)
+{
+    return (pic->events | (pic->levels & pic->level_triggered)) & pic->mask;
+}
+
+/* update the CPU irq state */
+static void heathrow_pic_update(HeathrowPICS *s)
+{
+    if (check_irq(&s->pics[0]) || check_irq(&s->pics[1])) {
+        qemu_irq_raise(s->irqs[0]);
+    } else {
+        qemu_irq_lower(s->irqs[0]);
+    }
+}
+
+static void pic_write(void *opaque, hwaddr addr,
+                      uint64_t value, unsigned size)
+{
+    HeathrowPICS *s = opaque;
+    HeathrowPIC *pic;
+    unsigned int n;
+
+    n = ((addr & 0xfff) - 0x10) >> 4;
+    PIC_DPRINTF("writel: " TARGET_FMT_plx " %u: %08x\n", addr, n, value);
+    if (n >= 2)
+        return;
+    pic = &s->pics[n];
+    switch(addr & 0xf) {
+    case 0x04:
+        pic->mask = value;
+        heathrow_pic_update(s);
+        break;
+    case 0x08:
+        /* do not reset level triggered IRQs */
+        value &= ~pic->level_triggered;
+        pic->events &= ~value;
+        heathrow_pic_update(s);
+        break;
+    default:
+        break;
+    }
+}
+
+static uint64_t pic_read(void *opaque, hwaddr addr,
+                         unsigned size)
+{
+    HeathrowPICS *s = opaque;
+    HeathrowPIC *pic;
+    unsigned int n;
+    uint32_t value;
+
+    n = ((addr & 0xfff) - 0x10) >> 4;
+    if (n >= 2) {
+        value = 0;
+    } else {
+        pic = &s->pics[n];
+        switch(addr & 0xf) {
+        case 0x0:
+            value = pic->events;
+            break;
+        case 0x4:
+            value = pic->mask;
+            break;
+        case 0xc:
+            value = pic->levels;
+            break;
+        default:
+            value = 0;
+            break;
+        }
+    }
+    PIC_DPRINTF("readl: " TARGET_FMT_plx " %u: %08x\n", addr, n, value);
+    return value;
+}
+
+static const MemoryRegionOps heathrow_pic_ops = {
+    .read = pic_read,
+    .write = pic_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void heathrow_pic_set_irq(void *opaque, int num, int level)
+{
+    HeathrowPICS *s = opaque;
+    HeathrowPIC *pic;
+    unsigned int irq_bit;
+
+#if defined(DEBUG)
+    {
+        static int last_level[64];
+        if (last_level[num] != level) {
+            PIC_DPRINTF("set_irq: num=0x%02x level=%d\n", num, level);
+            last_level[num] = level;
+        }
+    }
+#endif
+    pic = &s->pics[1 - (num >> 5)];
+    irq_bit = 1 << (num & 0x1f);
+    if (level) {
+        pic->events |= irq_bit & ~pic->level_triggered;
+        pic->levels |= irq_bit;
+    } else {
+        pic->levels &= ~irq_bit;
+    }
+    heathrow_pic_update(s);
+}
+
+static const VMStateDescription vmstate_heathrow_pic_one = {
+    .name = "heathrow_pic_one",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32(events, HeathrowPIC),
+        VMSTATE_UINT32(mask, HeathrowPIC),
+        VMSTATE_UINT32(levels, HeathrowPIC),
+        VMSTATE_UINT32(level_triggered, HeathrowPIC),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_heathrow_pic = {
+    .name = "heathrow_pic",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_STRUCT_ARRAY(pics, HeathrowPICS, 2, 1,
+                             vmstate_heathrow_pic_one, HeathrowPIC),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void heathrow_pic_reset_one(HeathrowPIC *s)
+{
+    memset(s, '\0', sizeof(HeathrowPIC));
+}
+
+static void heathrow_pic_reset(void *opaque)
+{
+    HeathrowPICS *s = opaque;
+
+    heathrow_pic_reset_one(&s->pics[0]);
+    heathrow_pic_reset_one(&s->pics[1]);
+
+    s->pics[0].level_triggered = 0;
+    s->pics[1].level_triggered = 0x1ff00000;
+}
+
+qemu_irq *heathrow_pic_init(MemoryRegion **pmem,
+                            int nb_cpus, qemu_irq **irqs)
+{
+    HeathrowPICS *s;
+
+    s = g_malloc0(sizeof(HeathrowPICS));
+    /* only 1 CPU */
+    s->irqs = irqs[0];
+    memory_region_init_io(&s->mem, &heathrow_pic_ops, s,
+                          "heathrow-pic", 0x1000);
+    *pmem = &s->mem;
+
+    vmstate_register(NULL, -1, &vmstate_heathrow_pic, s);
+    qemu_register_reset(heathrow_pic_reset, s);
+    return qemu_allocate_irqs(heathrow_pic_set_irq, s, 64);
+}
diff --git a/hw/intc/i8259.c b/hw/intc/i8259.c
new file mode 100644 (file)
index 0000000..ce14bd0
--- /dev/null
@@ -0,0 +1,496 @@
+/*
+ * QEMU 8259 interrupt controller emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "hw/isa/isa.h"
+#include "monitor/monitor.h"
+#include "qemu/timer.h"
+#include "hw/isa/i8259_internal.h"
+
+/* debug PIC */
+//#define DEBUG_PIC
+
+#ifdef DEBUG_PIC
+#define DPRINTF(fmt, ...)                                       \
+    do { printf("pic: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+//#define DEBUG_IRQ_LATENCY
+//#define DEBUG_IRQ_COUNT
+
+#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
+static int irq_level[16];
+#endif
+#ifdef DEBUG_IRQ_COUNT
+static uint64_t irq_count[16];
+#endif
+#ifdef DEBUG_IRQ_LATENCY
+static int64_t irq_time[16];
+#endif
+DeviceState *isa_pic;
+static PICCommonState *slave_pic;
+
+/* return the highest priority found in mask (highest = smallest
+   number). Return 8 if no irq */
+static int get_priority(PICCommonState *s, int mask)
+{
+    int priority;
+
+    if (mask == 0) {
+        return 8;
+    }
+    priority = 0;
+    while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) {
+        priority++;
+    }
+    return priority;
+}
+
+/* return the pic wanted interrupt. return -1 if none */
+static int pic_get_irq(PICCommonState *s)
+{
+    int mask, cur_priority, priority;
+
+    mask = s->irr & ~s->imr;
+    priority = get_priority(s, mask);
+    if (priority == 8) {
+        return -1;
+    }
+    /* compute current priority. If special fully nested mode on the
+       master, the IRQ coming from the slave is not taken into account
+       for the priority computation. */
+    mask = s->isr;
+    if (s->special_mask) {
+        mask &= ~s->imr;
+    }
+    if (s->special_fully_nested_mode && s->master) {
+        mask &= ~(1 << 2);
+    }
+    cur_priority = get_priority(s, mask);
+    if (priority < cur_priority) {
+        /* higher priority found: an irq should be generated */
+        return (priority + s->priority_add) & 7;
+    } else {
+        return -1;
+    }
+}
+
+/* Update INT output. Must be called every time the output may have changed. */
+static void pic_update_irq(PICCommonState *s)
+{
+    int irq;
+
+    irq = pic_get_irq(s);
+    if (irq >= 0) {
+        DPRINTF("pic%d: imr=%x irr=%x padd=%d\n",
+                s->master ? 0 : 1, s->imr, s->irr, s->priority_add);
+        qemu_irq_raise(s->int_out[0]);
+    } else {
+        qemu_irq_lower(s->int_out[0]);
+    }
+}
+
+/* set irq level. If an edge is detected, then the IRR is set to 1 */
+static void pic_set_irq(void *opaque, int irq, int level)
+{
+    PICCommonState *s = opaque;
+    int mask = 1 << irq;
+
+#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) || \
+    defined(DEBUG_IRQ_LATENCY)
+    int irq_index = s->master ? irq : irq + 8;
+#endif
+#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT)
+    if (level != irq_level[irq_index]) {
+        DPRINTF("pic_set_irq: irq=%d level=%d\n", irq_index, level);
+        irq_level[irq_index] = level;
+#ifdef DEBUG_IRQ_COUNT
+        if (level == 1) {
+            irq_count[irq_index]++;
+        }
+#endif
+    }
+#endif
+#ifdef DEBUG_IRQ_LATENCY
+    if (level) {
+        irq_time[irq_index] = qemu_get_clock_ns(vm_clock);
+    }
+#endif
+
+    if (s->elcr & mask) {
+        /* level triggered */
+        if (level) {
+            s->irr |= mask;
+            s->last_irr |= mask;
+        } else {
+            s->irr &= ~mask;
+            s->last_irr &= ~mask;
+        }
+    } else {
+        /* edge triggered */
+        if (level) {
+            if ((s->last_irr & mask) == 0) {
+                s->irr |= mask;
+            }
+            s->last_irr |= mask;
+        } else {
+            s->last_irr &= ~mask;
+        }
+    }
+    pic_update_irq(s);
+}
+
+/* acknowledge interrupt 'irq' */
+static void pic_intack(PICCommonState *s, int irq)
+{
+    if (s->auto_eoi) {
+        if (s->rotate_on_auto_eoi) {
+            s->priority_add = (irq + 1) & 7;
+        }
+    } else {
+        s->isr |= (1 << irq);
+    }
+    /* We don't clear a level sensitive interrupt here */
+    if (!(s->elcr & (1 << irq))) {
+        s->irr &= ~(1 << irq);
+    }
+    pic_update_irq(s);
+}
+
+int pic_read_irq(DeviceState *d)
+{
+    PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, d);
+    int irq, irq2, intno;
+
+    irq = pic_get_irq(s);
+    if (irq >= 0) {
+        if (irq == 2) {
+            irq2 = pic_get_irq(slave_pic);
+            if (irq2 >= 0) {
+                pic_intack(slave_pic, irq2);
+            } else {
+                /* spurious IRQ on slave controller */
+                irq2 = 7;
+            }
+            intno = slave_pic->irq_base + irq2;
+        } else {
+            intno = s->irq_base + irq;
+        }
+        pic_intack(s, irq);
+    } else {
+        /* spurious IRQ on host controller */
+        irq = 7;
+        intno = s->irq_base + irq;
+    }
+
+#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_LATENCY)
+    if (irq == 2) {
+        irq = irq2 + 8;
+    }
+#endif
+#ifdef DEBUG_IRQ_LATENCY
+    printf("IRQ%d latency=%0.3fus\n",
+           irq,
+           (double)(qemu_get_clock_ns(vm_clock) -
+                    irq_time[irq]) * 1000000.0 / get_ticks_per_sec());
+#endif
+    DPRINTF("pic_interrupt: irq=%d\n", irq);
+    return intno;
+}
+
+static void pic_init_reset(PICCommonState *s)
+{
+    pic_reset_common(s);
+    pic_update_irq(s);
+}
+
+static void pic_reset(DeviceState *dev)
+{
+    PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, dev);
+
+    s->elcr = 0;
+    pic_init_reset(s);
+}
+
+static void pic_ioport_write(void *opaque, hwaddr addr64,
+                             uint64_t val64, unsigned size)
+{
+    PICCommonState *s = opaque;
+    uint32_t addr = addr64;
+    uint32_t val = val64;
+    int priority, cmd, irq;
+
+    DPRINTF("write: addr=0x%02x val=0x%02x\n", addr, val);
+    if (addr == 0) {
+        if (val & 0x10) {
+            pic_init_reset(s);
+            s->init_state = 1;
+            s->init4 = val & 1;
+            s->single_mode = val & 2;
+            if (val & 0x08) {
+                hw_error("level sensitive irq not supported");
+            }
+        } else if (val & 0x08) {
+            if (val & 0x04) {
+                s->poll = 1;
+            }
+            if (val & 0x02) {
+                s->read_reg_select = val & 1;
+            }
+            if (val & 0x40) {
+                s->special_mask = (val >> 5) & 1;
+            }
+        } else {
+            cmd = val >> 5;
+            switch (cmd) {
+            case 0:
+            case 4:
+                s->rotate_on_auto_eoi = cmd >> 2;
+                break;
+            case 1: /* end of interrupt */
+            case 5:
+                priority = get_priority(s, s->isr);
+                if (priority != 8) {
+                    irq = (priority + s->priority_add) & 7;
+                    s->isr &= ~(1 << irq);
+                    if (cmd == 5) {
+                        s->priority_add = (irq + 1) & 7;
+                    }
+                    pic_update_irq(s);
+                }
+                break;
+            case 3:
+                irq = val & 7;
+                s->isr &= ~(1 << irq);
+                pic_update_irq(s);
+                break;
+            case 6:
+                s->priority_add = (val + 1) & 7;
+                pic_update_irq(s);
+                break;
+            case 7:
+                irq = val & 7;
+                s->isr &= ~(1 << irq);
+                s->priority_add = (irq + 1) & 7;
+                pic_update_irq(s);
+                break;
+            default:
+                /* no operation */
+                break;
+            }
+        }
+    } else {
+        switch (s->init_state) {
+        case 0:
+            /* normal mode */
+            s->imr = val;
+            pic_update_irq(s);
+            break;
+        case 1:
+            s->irq_base = val & 0xf8;
+            s->init_state = s->single_mode ? (s->init4 ? 3 : 0) : 2;
+            break;
+        case 2:
+            if (s->init4) {
+                s->init_state = 3;
+            } else {
+                s->init_state = 0;
+            }
+            break;
+        case 3:
+            s->special_fully_nested_mode = (val >> 4) & 1;
+            s->auto_eoi = (val >> 1) & 1;
+            s->init_state = 0;
+            break;
+        }
+    }
+}
+
+static uint64_t pic_ioport_read(void *opaque, hwaddr addr,
+                                unsigned size)
+{
+    PICCommonState *s = opaque;
+    int ret;
+
+    if (s->poll) {
+        ret = pic_get_irq(s);
+        if (ret >= 0) {
+            pic_intack(s, ret);
+            ret |= 0x80;
+        } else {
+            ret = 0;
+        }
+        s->poll = 0;
+    } else {
+        if (addr == 0) {
+            if (s->read_reg_select) {
+                ret = s->isr;
+            } else {
+                ret = s->irr;
+            }
+        } else {
+            ret = s->imr;
+        }
+    }
+    DPRINTF("read: addr=0x%02x val=0x%02x\n", addr, ret);
+    return ret;
+}
+
+int pic_get_output(DeviceState *d)
+{
+    PICCommonState *s = DO_UPCAST(PICCommonState, dev.qdev, d);
+
+    return (pic_get_irq(s) >= 0);
+}
+
+static void elcr_ioport_write(void *opaque, hwaddr addr,
+                              uint64_t val, unsigned size)
+{
+    PICCommonState *s = opaque;
+    s->elcr = val & s->elcr_mask;
+}
+
+static uint64_t elcr_ioport_read(void *opaque, hwaddr addr,
+                                 unsigned size)
+{
+    PICCommonState *s = opaque;
+    return s->elcr;
+}
+
+static const MemoryRegionOps pic_base_ioport_ops = {
+    .read = pic_ioport_read,
+    .write = pic_ioport_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static const MemoryRegionOps pic_elcr_ioport_ops = {
+    .read = elcr_ioport_read,
+    .write = elcr_ioport_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static void pic_init(PICCommonState *s)
+{
+    memory_region_init_io(&s->base_io, &pic_base_ioport_ops, s, "pic", 2);
+    memory_region_init_io(&s->elcr_io, &pic_elcr_ioport_ops, s, "elcr", 1);
+
+    qdev_init_gpio_out(&s->dev.qdev, s->int_out, ARRAY_SIZE(s->int_out));
+    qdev_init_gpio_in(&s->dev.qdev, pic_set_irq, 8);
+}
+
+void pic_info(Monitor *mon, const QDict *qdict)
+{
+    int i;
+    PICCommonState *s;
+
+    if (!isa_pic) {
+        return;
+    }
+    for (i = 0; i < 2; i++) {
+        s = i == 0 ? DO_UPCAST(PICCommonState, dev.qdev, isa_pic) : slave_pic;
+        monitor_printf(mon, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d "
+                       "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n",
+                       i, s->irr, s->imr, s->isr, s->priority_add,
+                       s->irq_base, s->read_reg_select, s->elcr,
+                       s->special_fully_nested_mode);
+    }
+}
+
+void irq_info(Monitor *mon, const QDict *qdict)
+{
+#ifndef DEBUG_IRQ_COUNT
+    monitor_printf(mon, "irq statistic code not compiled.\n");
+#else
+    int i;
+    int64_t count;
+
+    monitor_printf(mon, "IRQ statistics:\n");
+    for (i = 0; i < 16; i++) {
+        count = irq_count[i];
+        if (count > 0) {
+            monitor_printf(mon, "%2d: %" PRId64 "\n", i, count);
+        }
+    }
+#endif
+}
+
+qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq)
+{
+    qemu_irq *irq_set;
+    ISADevice *dev;
+    int i;
+
+    irq_set = g_malloc(ISA_NUM_IRQS * sizeof(qemu_irq));
+
+    dev = i8259_init_chip("isa-i8259", bus, true);
+
+    qdev_connect_gpio_out(&dev->qdev, 0, parent_irq);
+    for (i = 0 ; i < 8; i++) {
+        irq_set[i] = qdev_get_gpio_in(&dev->qdev, i);
+    }
+
+    isa_pic = &dev->qdev;
+
+    dev = i8259_init_chip("isa-i8259", bus, false);
+
+    qdev_connect_gpio_out(&dev->qdev, 0, irq_set[2]);
+    for (i = 0 ; i < 8; i++) {
+        irq_set[i + 8] = qdev_get_gpio_in(&dev->qdev, i);
+    }
+
+    slave_pic = DO_UPCAST(PICCommonState, dev, dev);
+
+    return irq_set;
+}
+
+static void i8259_class_init(ObjectClass *klass, void *data)
+{
+    PICCommonClass *k = PIC_COMMON_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    k->init = pic_init;
+    dc->reset = pic_reset;
+}
+
+static const TypeInfo i8259_info = {
+    .name       = "isa-i8259",
+    .instance_size = sizeof(PICCommonState),
+    .parent     = TYPE_PIC_COMMON,
+    .class_init = i8259_class_init,
+};
+
+static void pic_register_types(void)
+{
+    type_register_static(&i8259_info);
+}
+
+type_init(pic_register_types)
diff --git a/hw/intc/i8259_common.c b/hw/intc/i8259_common.c
new file mode 100644 (file)
index 0000000..996ba9d
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * QEMU 8259 - common bits of emulated and KVM kernel model
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2011      Jan Kiszka, Siemens AG
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/i386/pc.h"
+#include "hw/isa/i8259_internal.h"
+
+void pic_reset_common(PICCommonState *s)
+{
+    s->last_irr = 0;
+    s->irr &= s->elcr;
+    s->imr = 0;
+    s->isr = 0;
+    s->priority_add = 0;
+    s->irq_base = 0;
+    s->read_reg_select = 0;
+    s->poll = 0;
+    s->special_mask = 0;
+    s->init_state = 0;
+    s->auto_eoi = 0;
+    s->rotate_on_auto_eoi = 0;
+    s->special_fully_nested_mode = 0;
+    s->init4 = 0;
+    s->single_mode = 0;
+    /* Note: ELCR is not reset */
+}
+
+static void pic_dispatch_pre_save(void *opaque)
+{
+    PICCommonState *s = opaque;
+    PICCommonClass *info = PIC_COMMON_GET_CLASS(s);
+
+    if (info->pre_save) {
+        info->pre_save(s);
+    }
+}
+
+static int pic_dispatch_post_load(void *opaque, int version_id)
+{
+    PICCommonState *s = opaque;
+    PICCommonClass *info = PIC_COMMON_GET_CLASS(s);
+
+    if (info->post_load) {
+        info->post_load(s);
+    }
+    return 0;
+}
+
+static int pic_init_common(ISADevice *dev)
+{
+    PICCommonState *s = DO_UPCAST(PICCommonState, dev, dev);
+    PICCommonClass *info = PIC_COMMON_GET_CLASS(s);
+
+    info->init(s);
+
+    isa_register_ioport(NULL, &s->base_io, s->iobase);
+    if (s->elcr_addr != -1) {
+        isa_register_ioport(NULL, &s->elcr_io, s->elcr_addr);
+    }
+
+    qdev_set_legacy_instance_id(&s->dev.qdev, s->iobase, 1);
+
+    return 0;
+}
+
+ISADevice *i8259_init_chip(const char *name, ISABus *bus, bool master)
+{
+    ISADevice *dev;
+
+    dev = isa_create(bus, name);
+    qdev_prop_set_uint32(&dev->qdev, "iobase", master ? 0x20 : 0xa0);
+    qdev_prop_set_uint32(&dev->qdev, "elcr_addr", master ? 0x4d0 : 0x4d1);
+    qdev_prop_set_uint8(&dev->qdev, "elcr_mask", master ? 0xf8 : 0xde);
+    qdev_prop_set_bit(&dev->qdev, "master", master);
+    qdev_init_nofail(&dev->qdev);
+
+    return dev;
+}
+
+static const VMStateDescription vmstate_pic_common = {
+    .name = "i8259",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_save = pic_dispatch_pre_save,
+    .post_load = pic_dispatch_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(last_irr, PICCommonState),
+        VMSTATE_UINT8(irr, PICCommonState),
+        VMSTATE_UINT8(imr, PICCommonState),
+        VMSTATE_UINT8(isr, PICCommonState),
+        VMSTATE_UINT8(priority_add, PICCommonState),
+        VMSTATE_UINT8(irq_base, PICCommonState),
+        VMSTATE_UINT8(read_reg_select, PICCommonState),
+        VMSTATE_UINT8(poll, PICCommonState),
+        VMSTATE_UINT8(special_mask, PICCommonState),
+        VMSTATE_UINT8(init_state, PICCommonState),
+        VMSTATE_UINT8(auto_eoi, PICCommonState),
+        VMSTATE_UINT8(rotate_on_auto_eoi, PICCommonState),
+        VMSTATE_UINT8(special_fully_nested_mode, PICCommonState),
+        VMSTATE_UINT8(init4, PICCommonState),
+        VMSTATE_UINT8(single_mode, PICCommonState),
+        VMSTATE_UINT8(elcr, PICCommonState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property pic_properties_common[] = {
+    DEFINE_PROP_HEX32("iobase", PICCommonState, iobase,  -1),
+    DEFINE_PROP_HEX32("elcr_addr", PICCommonState, elcr_addr,  -1),
+    DEFINE_PROP_HEX8("elcr_mask", PICCommonState, elcr_mask,  -1),
+    DEFINE_PROP_BIT("master", PICCommonState, master,  0, false),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pic_common_class_init(ObjectClass *klass, void *data)
+{
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd = &vmstate_pic_common;
+    dc->no_user = 1;
+    dc->props = pic_properties_common;
+    ic->init = pic_init_common;
+}
+
+static const TypeInfo pic_common_type = {
+    .name = TYPE_PIC_COMMON,
+    .parent = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(PICCommonState),
+    .class_size = sizeof(PICCommonClass),
+    .class_init = pic_common_class_init,
+    .abstract = true,
+};
+
+static void register_types(void)
+{
+    type_register_static(&pic_common_type);
+}
+
+type_init(register_types);
diff --git a/hw/intc/pl190.c b/hw/intc/pl190.c
new file mode 100644 (file)
index 0000000..9610673
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * Arm PrimeCell PL190 Vector Interrupt Controller
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/sysbus.h"
+
+/* The number of virtual priority levels.  16 user vectors plus the
+   unvectored IRQ.  Chained interrupts would require an additional level
+   if implemented.  */
+
+#define PL190_NUM_PRIO 17
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    uint32_t level;
+    uint32_t soft_level;
+    uint32_t irq_enable;
+    uint32_t fiq_select;
+    uint8_t vect_control[16];
+    uint32_t vect_addr[PL190_NUM_PRIO];
+    /* Mask containing interrupts with higher priority than this one.  */
+    uint32_t prio_mask[PL190_NUM_PRIO + 1];
+    int protected;
+    /* Current priority level.  */
+    int priority;
+    int prev_prio[PL190_NUM_PRIO];
+    qemu_irq irq;
+    qemu_irq fiq;
+} pl190_state;
+
+static const unsigned char pl190_id[] =
+{ 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 };
+
+static inline uint32_t pl190_irq_level(pl190_state *s)
+{
+    return (s->level | s->soft_level) & s->irq_enable & ~s->fiq_select;
+}
+
+/* Update interrupts.  */
+static void pl190_update(pl190_state *s)
+{
+    uint32_t level = pl190_irq_level(s);
+    int set;
+
+    set = (level & s->prio_mask[s->priority]) != 0;
+    qemu_set_irq(s->irq, set);
+    set = ((s->level | s->soft_level) & s->fiq_select) != 0;
+    qemu_set_irq(s->fiq, set);
+}
+
+static void pl190_set_irq(void *opaque, int irq, int level)
+{
+    pl190_state *s = (pl190_state *)opaque;
+
+    if (level)
+        s->level |= 1u << irq;
+    else
+        s->level &= ~(1u << irq);
+    pl190_update(s);
+}
+
+static void pl190_update_vectors(pl190_state *s)
+{
+    uint32_t mask;
+    int i;
+    int n;
+
+    mask = 0;
+    for (i = 0; i < 16; i++)
+      {
+        s->prio_mask[i] = mask;
+        if (s->vect_control[i] & 0x20)
+          {
+            n = s->vect_control[i] & 0x1f;
+            mask |= 1 << n;
+          }
+      }
+    s->prio_mask[16] = mask;
+    pl190_update(s);
+}
+
+static uint64_t pl190_read(void *opaque, hwaddr offset,
+                           unsigned size)
+{
+    pl190_state *s = (pl190_state *)opaque;
+    int i;
+
+    if (offset >= 0xfe0 && offset < 0x1000) {
+        return pl190_id[(offset - 0xfe0) >> 2];
+    }
+    if (offset >= 0x100 && offset < 0x140) {
+        return s->vect_addr[(offset - 0x100) >> 2];
+    }
+    if (offset >= 0x200 && offset < 0x240) {
+        return s->vect_control[(offset - 0x200) >> 2];
+    }
+    switch (offset >> 2) {
+    case 0: /* IRQSTATUS */
+        return pl190_irq_level(s);
+    case 1: /* FIQSATUS */
+        return (s->level | s->soft_level) & s->fiq_select;
+    case 2: /* RAWINTR */
+        return s->level | s->soft_level;
+    case 3: /* INTSELECT */
+        return s->fiq_select;
+    case 4: /* INTENABLE */
+        return s->irq_enable;
+    case 6: /* SOFTINT */
+        return s->soft_level;
+    case 8: /* PROTECTION */
+        return s->protected;
+    case 12: /* VECTADDR */
+        /* Read vector address at the start of an ISR.  Increases the
+         * current priority level to that of the current interrupt.
+         *
+         * Since an enabled interrupt X at priority P causes prio_mask[Y]
+         * to have bit X set for all Y > P, this loop will stop with
+         * i == the priority of the highest priority set interrupt.
+         */
+        for (i = 0; i < s->priority; i++) {
+            if ((s->level | s->soft_level) & s->prio_mask[i + 1]) {
+                break;
+            }
+        }
+
+        /* Reading this value with no pending interrupts is undefined.
+           We return the default address.  */
+        if (i == PL190_NUM_PRIO)
+          return s->vect_addr[16];
+        if (i < s->priority)
+          {
+            s->prev_prio[i] = s->priority;
+            s->priority = i;
+            pl190_update(s);
+          }
+        return s->vect_addr[s->priority];
+    case 13: /* DEFVECTADDR */
+        return s->vect_addr[16];
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl190_read: Bad offset %x\n", (int)offset);
+        return 0;
+    }
+}
+
+static void pl190_write(void *opaque, hwaddr offset,
+                        uint64_t val, unsigned size)
+{
+    pl190_state *s = (pl190_state *)opaque;
+
+    if (offset >= 0x100 && offset < 0x140) {
+        s->vect_addr[(offset - 0x100) >> 2] = val;
+        pl190_update_vectors(s);
+        return;
+    }
+    if (offset >= 0x200 && offset < 0x240) {
+        s->vect_control[(offset - 0x200) >> 2] = val;
+        pl190_update_vectors(s);
+        return;
+    }
+    switch (offset >> 2) {
+    case 0: /* SELECT */
+        /* This is a readonly register, but linux tries to write to it
+           anyway.  Ignore the write.  */
+        break;
+    case 3: /* INTSELECT */
+        s->fiq_select = val;
+        break;
+    case 4: /* INTENABLE */
+        s->irq_enable |= val;
+        break;
+    case 5: /* INTENCLEAR */
+        s->irq_enable &= ~val;
+        break;
+    case 6: /* SOFTINT */
+        s->soft_level |= val;
+        break;
+    case 7: /* SOFTINTCLEAR */
+        s->soft_level &= ~val;
+        break;
+    case 8: /* PROTECTION */
+        /* TODO: Protection (supervisor only access) is not implemented.  */
+        s->protected = val & 1;
+        break;
+    case 12: /* VECTADDR */
+        /* Restore the previous priority level.  The value written is
+           ignored.  */
+        if (s->priority < PL190_NUM_PRIO)
+            s->priority = s->prev_prio[s->priority];
+        break;
+    case 13: /* DEFVECTADDR */
+        s->vect_addr[16] = val;
+        break;
+    case 0xc0: /* ITCR */
+        if (val) {
+            qemu_log_mask(LOG_UNIMP, "pl190: Test mode not implemented\n");
+        }
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                     "pl190_write: Bad offset %x\n", (int)offset);
+        return;
+    }
+    pl190_update(s);
+}
+
+static const MemoryRegionOps pl190_ops = {
+    .read = pl190_read,
+    .write = pl190_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void pl190_reset(DeviceState *d)
+{
+  pl190_state *s = DO_UPCAST(pl190_state, busdev.qdev, d);
+  int i;
+
+  for (i = 0; i < 16; i++)
+    {
+      s->vect_addr[i] = 0;
+      s->vect_control[i] = 0;
+    }
+  s->vect_addr[16] = 0;
+  s->prio_mask[17] = 0xffffffff;
+  s->priority = PL190_NUM_PRIO;
+  pl190_update_vectors(s);
+}
+
+static int pl190_init(SysBusDevice *dev)
+{
+    pl190_state *s = FROM_SYSBUS(pl190_state, dev);
+
+    memory_region_init_io(&s->iomem, &pl190_ops, s, "pl190", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    qdev_init_gpio_in(&dev->qdev, pl190_set_irq, 32);
+    sysbus_init_irq(dev, &s->irq);
+    sysbus_init_irq(dev, &s->fiq);
+    return 0;
+}
+
+static const VMStateDescription vmstate_pl190 = {
+    .name = "pl190",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(level, pl190_state),
+        VMSTATE_UINT32(soft_level, pl190_state),
+        VMSTATE_UINT32(irq_enable, pl190_state),
+        VMSTATE_UINT32(fiq_select, pl190_state),
+        VMSTATE_UINT8_ARRAY(vect_control, pl190_state, 16),
+        VMSTATE_UINT32_ARRAY(vect_addr, pl190_state, PL190_NUM_PRIO),
+        VMSTATE_UINT32_ARRAY(prio_mask, pl190_state, PL190_NUM_PRIO+1),
+        VMSTATE_INT32(protected, pl190_state),
+        VMSTATE_INT32(priority, pl190_state),
+        VMSTATE_INT32_ARRAY(prev_prio, pl190_state, PL190_NUM_PRIO),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void pl190_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = pl190_init;
+    dc->no_user = 1;
+    dc->reset = pl190_reset;
+    dc->vmsd = &vmstate_pl190;
+}
+
+static const TypeInfo pl190_info = {
+    .name          = "pl190",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl190_state),
+    .class_init    = pl190_class_init,
+};
+
+static void pl190_register_types(void)
+{
+    type_register_static(&pl190_info);
+}
+
+type_init(pl190_register_types)
diff --git a/hw/intc/puv3_intc.c b/hw/intc/puv3_intc.c
new file mode 100644 (file)
index 0000000..0cd5e9e
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * INTC device simulation in PKUnity SoC
+ *
+ * Copyright (C) 2010-2012 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or any later version.
+ * See the COPYING file in the top-level directory.
+ */
+#include "hw/sysbus.h"
+
+#undef DEBUG_PUV3
+#include "hw/unicore32/puv3.h"
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    qemu_irq parent_irq;
+
+    uint32_t reg_ICMR;
+    uint32_t reg_ICPR;
+} PUV3INTCState;
+
+/* Update interrupt status after enabled or pending bits have been changed.  */
+static void puv3_intc_update(PUV3INTCState *s)
+{
+    if (s->reg_ICMR & s->reg_ICPR) {
+        qemu_irq_raise(s->parent_irq);
+    } else {
+        qemu_irq_lower(s->parent_irq);
+    }
+}
+
+/* Process a change in an external INTC input. */
+static void puv3_intc_handler(void *opaque, int irq, int level)
+{
+    PUV3INTCState *s = opaque;
+
+    DPRINTF("irq 0x%x, level 0x%x\n", irq, level);
+    if (level) {
+        s->reg_ICPR |= (1 << irq);
+    } else {
+        s->reg_ICPR &= ~(1 << irq);
+    }
+    puv3_intc_update(s);
+}
+
+static uint64_t puv3_intc_read(void *opaque, hwaddr offset,
+        unsigned size)
+{
+    PUV3INTCState *s = opaque;
+    uint32_t ret = 0;
+
+    switch (offset) {
+    case 0x04: /* INTC_ICMR */
+        ret = s->reg_ICMR;
+        break;
+    case 0x0c: /* INTC_ICIP */
+        ret = s->reg_ICPR; /* the same value with ICPR */
+        break;
+    default:
+        DPRINTF("Bad offset %x\n", (int)offset);
+    }
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
+    return ret;
+}
+
+static void puv3_intc_write(void *opaque, hwaddr offset,
+        uint64_t value, unsigned size)
+{
+    PUV3INTCState *s = opaque;
+
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
+    switch (offset) {
+    case 0x00: /* INTC_ICLR */
+    case 0x14: /* INTC_ICCR */
+        break;
+    case 0x04: /* INTC_ICMR */
+        s->reg_ICMR = value;
+        break;
+    default:
+        DPRINTF("Bad offset 0x%x\n", (int)offset);
+        return;
+    }
+    puv3_intc_update(s);
+}
+
+static const MemoryRegionOps puv3_intc_ops = {
+    .read = puv3_intc_read,
+    .write = puv3_intc_write,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int puv3_intc_init(SysBusDevice *dev)
+{
+    PUV3INTCState *s = FROM_SYSBUS(PUV3INTCState, dev);
+
+    qdev_init_gpio_in(&s->busdev.qdev, puv3_intc_handler, PUV3_IRQS_NR);
+    sysbus_init_irq(&s->busdev, &s->parent_irq);
+
+    s->reg_ICMR = 0;
+    s->reg_ICPR = 0;
+
+    memory_region_init_io(&s->iomem, &puv3_intc_ops, s, "puv3_intc",
+            PUV3_REGS_OFFSET);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static void puv3_intc_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = puv3_intc_init;
+}
+
+static const TypeInfo puv3_intc_info = {
+    .name = "puv3_intc",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(PUV3INTCState),
+    .class_init = puv3_intc_class_init,
+};
+
+static void puv3_intc_register_type(void)
+{
+    type_register_static(&puv3_intc_info);
+}
+
+type_init(puv3_intc_register_type)
diff --git a/hw/intc/xilinx_intc.c b/hw/intc/xilinx_intc.c
new file mode 100644 (file)
index 0000000..b106e72
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * QEMU Xilinx OPB Interrupt Controller.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/hw.h"
+
+#define D(x)
+
+#define R_ISR       0
+#define R_IPR       1
+#define R_IER       2
+#define R_IAR       3
+#define R_SIE       4
+#define R_CIE       5
+#define R_IVR       6
+#define R_MER       7
+#define R_MAX       8
+
+struct xlx_pic
+{
+    SysBusDevice busdev;
+    MemoryRegion mmio;
+    qemu_irq parent_irq;
+
+    /* Configuration reg chosen at synthesis-time. QEMU populates
+       the bits at board-setup.  */
+    uint32_t c_kind_of_intr;
+
+    /* Runtime control registers.  */
+    uint32_t regs[R_MAX];
+};
+
+static void update_irq(struct xlx_pic *p)
+{
+    uint32_t i;
+    /* Update the pending register.  */
+    p->regs[R_IPR] = p->regs[R_ISR] & p->regs[R_IER];
+
+    /* Update the vector register.  */
+    for (i = 0; i < 32; i++) {
+        if (p->regs[R_IPR] & (1 << i))
+            break;
+    }
+    if (i == 32)
+        i = ~0;
+
+    p->regs[R_IVR] = i;
+    if ((p->regs[R_MER] & 1) && p->regs[R_IPR]) {
+        qemu_irq_raise(p->parent_irq);
+    } else {
+        qemu_irq_lower(p->parent_irq);
+    }
+}
+
+static uint64_t
+pic_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    struct xlx_pic *p = opaque;
+    uint32_t r = 0;
+
+    addr >>= 2;
+    switch (addr)
+    {
+        default:
+            if (addr < ARRAY_SIZE(p->regs))
+                r = p->regs[addr];
+            break;
+
+    }
+    D(printf("%s %x=%x\n", __func__, addr * 4, r));
+    return r;
+}
+
+static void
+pic_write(void *opaque, hwaddr addr,
+          uint64_t val64, unsigned int size)
+{
+    struct xlx_pic *p = opaque;
+    uint32_t value = val64;
+
+    addr >>= 2;
+    D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value));
+    switch (addr) 
+    {
+        case R_IAR:
+            p->regs[R_ISR] &= ~value; /* ACK.  */
+            break;
+        case R_SIE:
+            p->regs[R_IER] |= value;  /* Atomic set ie.  */
+            break;
+        case R_CIE:
+            p->regs[R_IER] &= ~value; /* Atomic clear ie.  */
+            break;
+        default:
+            if (addr < ARRAY_SIZE(p->regs))
+                p->regs[addr] = value;
+            break;
+    }
+    update_irq(p);
+}
+
+static const MemoryRegionOps pic_ops = {
+    .read = pic_read,
+    .write = pic_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void irq_handler(void *opaque, int irq, int level)
+{
+    struct xlx_pic *p = opaque;
+
+    if (!(p->regs[R_MER] & 2)) {
+        qemu_irq_lower(p->parent_irq);
+        return;
+    }
+
+    /* Update source flops. Don't clear unless level triggered.
+       Edge triggered interrupts only go away when explicitely acked to
+       the interrupt controller.  */
+    if (!(p->c_kind_of_intr & (1 << irq)) || level) {
+        p->regs[R_ISR] &= ~(1 << irq);
+        p->regs[R_ISR] |= (level << irq);
+    }
+    update_irq(p);
+}
+
+static int xilinx_intc_init(SysBusDevice *dev)
+{
+    struct xlx_pic *p = FROM_SYSBUS(typeof (*p), dev);
+
+    qdev_init_gpio_in(&dev->qdev, irq_handler, 32);
+    sysbus_init_irq(dev, &p->parent_irq);
+
+    memory_region_init_io(&p->mmio, &pic_ops, p, "xlnx.xps-intc", R_MAX * 4);
+    sysbus_init_mmio(dev, &p->mmio);
+    return 0;
+}
+
+static Property xilinx_intc_properties[] = {
+    DEFINE_PROP_UINT32("kind-of-intr", struct xlx_pic, c_kind_of_intr, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xilinx_intc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = xilinx_intc_init;
+    dc->props = xilinx_intc_properties;
+}
+
+static const TypeInfo xilinx_intc_info = {
+    .name          = "xlnx.xps-intc",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(struct xlx_pic),
+    .class_init    = xilinx_intc_class_init,
+};
+
+static void xilinx_intc_register_types(void)
+{
+    type_register_static(&xilinx_intc_info);
+}
+
+type_init(xilinx_intc_register_types)
diff --git a/hw/intel-hda.c b/hw/intel-hda.c
deleted file mode 100644 (file)
index 68201cd..0000000
+++ /dev/null
@@ -1,1329 +0,0 @@
-/*
- * Copyright (C) 2010 Red Hat, Inc.
- *
- * written by Gerd Hoffmann <kraxel@redhat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 or
- * (at your option) version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/hw.h"
-#include "hw/pci/pci.h"
-#include "hw/pci/msi.h"
-#include "qemu/timer.h"
-#include "hw/audio/audio.h"
-#include "hw/intel-hda.h"
-#include "hw/intel-hda-defs.h"
-#include "sysemu/dma.h"
-
-/* --------------------------------------------------------------------- */
-/* hda bus                                                               */
-
-static Property hda_props[] = {
-    DEFINE_PROP_UINT32("cad", HDACodecDevice, cad, -1),
-    DEFINE_PROP_END_OF_LIST()
-};
-
-static const TypeInfo hda_codec_bus_info = {
-    .name = TYPE_HDA_BUS,
-    .parent = TYPE_BUS,
-    .instance_size = sizeof(HDACodecBus),
-};
-
-void hda_codec_bus_init(DeviceState *dev, HDACodecBus *bus,
-                        hda_codec_response_func response,
-                        hda_codec_xfer_func xfer)
-{
-    qbus_create_inplace(&bus->qbus, TYPE_HDA_BUS, dev, NULL);
-    bus->response = response;
-    bus->xfer = xfer;
-}
-
-static int hda_codec_dev_init(DeviceState *qdev)
-{
-    HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, qdev->parent_bus);
-    HDACodecDevice *dev = DO_UPCAST(HDACodecDevice, qdev, qdev);
-    HDACodecDeviceClass *cdc = HDA_CODEC_DEVICE_GET_CLASS(dev);
-
-    if (dev->cad == -1) {
-        dev->cad = bus->next_cad;
-    }
-    if (dev->cad >= 15) {
-        return -1;
-    }
-    bus->next_cad = dev->cad + 1;
-    return cdc->init(dev);
-}
-
-static int hda_codec_dev_exit(DeviceState *qdev)
-{
-    HDACodecDevice *dev = DO_UPCAST(HDACodecDevice, qdev, qdev);
-    HDACodecDeviceClass *cdc = HDA_CODEC_DEVICE_GET_CLASS(dev);
-
-    if (cdc->exit) {
-        cdc->exit(dev);
-    }
-    return 0;
-}
-
-HDACodecDevice *hda_codec_find(HDACodecBus *bus, uint32_t cad)
-{
-    BusChild *kid;
-    HDACodecDevice *cdev;
-
-    QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
-        DeviceState *qdev = kid->child;
-        cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
-        if (cdev->cad == cad) {
-            return cdev;
-        }
-    }
-    return NULL;
-}
-
-void hda_codec_response(HDACodecDevice *dev, bool solicited, uint32_t response)
-{
-    HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus);
-    bus->response(dev, solicited, response);
-}
-
-bool hda_codec_xfer(HDACodecDevice *dev, uint32_t stnr, bool output,
-                    uint8_t *buf, uint32_t len)
-{
-    HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus);
-    return bus->xfer(dev, stnr, output, buf, len);
-}
-
-/* --------------------------------------------------------------------- */
-/* intel hda emulation                                                   */
-
-typedef struct IntelHDAStream IntelHDAStream;
-typedef struct IntelHDAState IntelHDAState;
-typedef struct IntelHDAReg IntelHDAReg;
-
-typedef struct bpl {
-    uint64_t addr;
-    uint32_t len;
-    uint32_t flags;
-} bpl;
-
-struct IntelHDAStream {
-    /* registers */
-    uint32_t ctl;
-    uint32_t lpib;
-    uint32_t cbl;
-    uint32_t lvi;
-    uint32_t fmt;
-    uint32_t bdlp_lbase;
-    uint32_t bdlp_ubase;
-
-    /* state */
-    bpl      *bpl;
-    uint32_t bentries;
-    uint32_t bsize, be, bp;
-};
-
-struct IntelHDAState {
-    PCIDevice pci;
-    const char *name;
-    HDACodecBus codecs;
-
-    /* registers */
-    uint32_t g_ctl;
-    uint32_t wake_en;
-    uint32_t state_sts;
-    uint32_t int_ctl;
-    uint32_t int_sts;
-    uint32_t wall_clk;
-
-    uint32_t corb_lbase;
-    uint32_t corb_ubase;
-    uint32_t corb_rp;
-    uint32_t corb_wp;
-    uint32_t corb_ctl;
-    uint32_t corb_sts;
-    uint32_t corb_size;
-
-    uint32_t rirb_lbase;
-    uint32_t rirb_ubase;
-    uint32_t rirb_wp;
-    uint32_t rirb_cnt;
-    uint32_t rirb_ctl;
-    uint32_t rirb_sts;
-    uint32_t rirb_size;
-
-    uint32_t dp_lbase;
-    uint32_t dp_ubase;
-
-    uint32_t icw;
-    uint32_t irr;
-    uint32_t ics;
-
-    /* streams */
-    IntelHDAStream st[8];
-
-    /* state */
-    MemoryRegion mmio;
-    uint32_t rirb_count;
-    int64_t wall_base_ns;
-
-    /* debug logging */
-    const IntelHDAReg *last_reg;
-    uint32_t last_val;
-    uint32_t last_write;
-    uint32_t last_sec;
-    uint32_t repeat_count;
-
-    /* properties */
-    uint32_t debug;
-    uint32_t msi;
-};
-
-struct IntelHDAReg {
-    const char *name;      /* register name */
-    uint32_t   size;       /* size in bytes */
-    uint32_t   reset;      /* reset value */
-    uint32_t   wmask;      /* write mask */
-    uint32_t   wclear;     /* write 1 to clear bits */
-    uint32_t   offset;     /* location in IntelHDAState */
-    uint32_t   shift;      /* byte access entries for dwords */
-    uint32_t   stream;
-    void       (*whandler)(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old);
-    void       (*rhandler)(IntelHDAState *d, const IntelHDAReg *reg);
-};
-
-static void intel_hda_reset(DeviceState *dev);
-
-/* --------------------------------------------------------------------- */
-
-static hwaddr intel_hda_addr(uint32_t lbase, uint32_t ubase)
-{
-    hwaddr addr;
-
-    addr = ((uint64_t)ubase << 32) | lbase;
-    return addr;
-}
-
-static void intel_hda_update_int_sts(IntelHDAState *d)
-{
-    uint32_t sts = 0;
-    uint32_t i;
-
-    /* update controller status */
-    if (d->rirb_sts & ICH6_RBSTS_IRQ) {
-        sts |= (1 << 30);
-    }
-    if (d->rirb_sts & ICH6_RBSTS_OVERRUN) {
-        sts |= (1 << 30);
-    }
-    if (d->state_sts & d->wake_en) {
-        sts |= (1 << 30);
-    }
-
-    /* update stream status */
-    for (i = 0; i < 8; i++) {
-        /* buffer completion interrupt */
-        if (d->st[i].ctl & (1 << 26)) {
-            sts |= (1 << i);
-        }
-    }
-
-    /* update global status */
-    if (sts & d->int_ctl) {
-        sts |= (1 << 31);
-    }
-
-    d->int_sts = sts;
-}
-
-static void intel_hda_update_irq(IntelHDAState *d)
-{
-    int msi = d->msi && msi_enabled(&d->pci);
-    int level;
-
-    intel_hda_update_int_sts(d);
-    if (d->int_sts & (1 << 31) && d->int_ctl & (1 << 31)) {
-        level = 1;
-    } else {
-        level = 0;
-    }
-    dprint(d, 2, "%s: level %d [%s]\n", __FUNCTION__,
-           level, msi ? "msi" : "intx");
-    if (msi) {
-        if (level) {
-            msi_notify(&d->pci, 0);
-        }
-    } else {
-        qemu_set_irq(d->pci.irq[0], level);
-    }
-}
-
-static int intel_hda_send_command(IntelHDAState *d, uint32_t verb)
-{
-    uint32_t cad, nid, data;
-    HDACodecDevice *codec;
-    HDACodecDeviceClass *cdc;
-
-    cad = (verb >> 28) & 0x0f;
-    if (verb & (1 << 27)) {
-        /* indirect node addressing, not specified in HDA 1.0 */
-        dprint(d, 1, "%s: indirect node addressing (guest bug?)\n", __FUNCTION__);
-        return -1;
-    }
-    nid = (verb >> 20) & 0x7f;
-    data = verb & 0xfffff;
-
-    codec = hda_codec_find(&d->codecs, cad);
-    if (codec == NULL) {
-        dprint(d, 1, "%s: addressed non-existing codec\n", __FUNCTION__);
-        return -1;
-    }
-    cdc = HDA_CODEC_DEVICE_GET_CLASS(codec);
-    cdc->command(codec, nid, data);
-    return 0;
-}
-
-static void intel_hda_corb_run(IntelHDAState *d)
-{
-    hwaddr addr;
-    uint32_t rp, verb;
-
-    if (d->ics & ICH6_IRS_BUSY) {
-        dprint(d, 2, "%s: [icw] verb 0x%08x\n", __FUNCTION__, d->icw);
-        intel_hda_send_command(d, d->icw);
-        return;
-    }
-
-    for (;;) {
-        if (!(d->corb_ctl & ICH6_CORBCTL_RUN)) {
-            dprint(d, 2, "%s: !run\n", __FUNCTION__);
-            return;
-        }
-        if ((d->corb_rp & 0xff) == d->corb_wp) {
-            dprint(d, 2, "%s: corb ring empty\n", __FUNCTION__);
-            return;
-        }
-        if (d->rirb_count == d->rirb_cnt) {
-            dprint(d, 2, "%s: rirb count reached\n", __FUNCTION__);
-            return;
-        }
-
-        rp = (d->corb_rp + 1) & 0xff;
-        addr = intel_hda_addr(d->corb_lbase, d->corb_ubase);
-        verb = ldl_le_pci_dma(&d->pci, addr + 4*rp);
-        d->corb_rp = rp;
-
-        dprint(d, 2, "%s: [rp 0x%x] verb 0x%08x\n", __FUNCTION__, rp, verb);
-        intel_hda_send_command(d, verb);
-    }
-}
-
-static void intel_hda_response(HDACodecDevice *dev, bool solicited, uint32_t response)
-{
-    HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus);
-    IntelHDAState *d = container_of(bus, IntelHDAState, codecs);
-    hwaddr addr;
-    uint32_t wp, ex;
-
-    if (d->ics & ICH6_IRS_BUSY) {
-        dprint(d, 2, "%s: [irr] response 0x%x, cad 0x%x\n",
-               __FUNCTION__, response, dev->cad);
-        d->irr = response;
-        d->ics &= ~(ICH6_IRS_BUSY | 0xf0);
-        d->ics |= (ICH6_IRS_VALID | (dev->cad << 4));
-        return;
-    }
-
-    if (!(d->rirb_ctl & ICH6_RBCTL_DMA_EN)) {
-        dprint(d, 1, "%s: rirb dma disabled, drop codec response\n", __FUNCTION__);
-        return;
-    }
-
-    ex = (solicited ? 0 : (1 << 4)) | dev->cad;
-    wp = (d->rirb_wp + 1) & 0xff;
-    addr = intel_hda_addr(d->rirb_lbase, d->rirb_ubase);
-    stl_le_pci_dma(&d->pci, addr + 8*wp, response);
-    stl_le_pci_dma(&d->pci, addr + 8*wp + 4, ex);
-    d->rirb_wp = wp;
-
-    dprint(d, 2, "%s: [wp 0x%x] response 0x%x, extra 0x%x\n",
-           __FUNCTION__, wp, response, ex);
-
-    d->rirb_count++;
-    if (d->rirb_count == d->rirb_cnt) {
-        dprint(d, 2, "%s: rirb count reached (%d)\n", __FUNCTION__, d->rirb_count);
-        if (d->rirb_ctl & ICH6_RBCTL_IRQ_EN) {
-            d->rirb_sts |= ICH6_RBSTS_IRQ;
-            intel_hda_update_irq(d);
-        }
-    } else if ((d->corb_rp & 0xff) == d->corb_wp) {
-        dprint(d, 2, "%s: corb ring empty (%d/%d)\n", __FUNCTION__,
-               d->rirb_count, d->rirb_cnt);
-        if (d->rirb_ctl & ICH6_RBCTL_IRQ_EN) {
-            d->rirb_sts |= ICH6_RBSTS_IRQ;
-            intel_hda_update_irq(d);
-        }
-    }
-}
-
-static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output,
-                           uint8_t *buf, uint32_t len)
-{
-    HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus);
-    IntelHDAState *d = container_of(bus, IntelHDAState, codecs);
-    hwaddr addr;
-    uint32_t s, copy, left;
-    IntelHDAStream *st;
-    bool irq = false;
-
-    st = output ? d->st + 4 : d->st;
-    for (s = 0; s < 4; s++) {
-        if (stnr == ((st[s].ctl >> 20) & 0x0f)) {
-            st = st + s;
-            break;
-        }
-    }
-    if (s == 4) {
-        return false;
-    }
-    if (st->bpl == NULL) {
-        return false;
-    }
-    if (st->ctl & (1 << 26)) {
-        /*
-         * Wait with the next DMA xfer until the guest
-         * has acked the buffer completion interrupt
-         */
-        return false;
-    }
-
-    left = len;
-    while (left > 0) {
-        copy = left;
-        if (copy > st->bsize - st->lpib)
-            copy = st->bsize - st->lpib;
-        if (copy > st->bpl[st->be].len - st->bp)
-            copy = st->bpl[st->be].len - st->bp;
-
-        dprint(d, 3, "dma: entry %d, pos %d/%d, copy %d\n",
-               st->be, st->bp, st->bpl[st->be].len, copy);
-
-        pci_dma_rw(&d->pci, st->bpl[st->be].addr + st->bp, buf, copy, !output);
-        st->lpib += copy;
-        st->bp += copy;
-        buf += copy;
-        left -= copy;
-
-        if (st->bpl[st->be].len == st->bp) {
-            /* bpl entry filled */
-            if (st->bpl[st->be].flags & 0x01) {
-                irq = true;
-            }
-            st->bp = 0;
-            st->be++;
-            if (st->be == st->bentries) {
-                /* bpl wrap around */
-                st->be = 0;
-                st->lpib = 0;
-            }
-        }
-    }
-    if (d->dp_lbase & 0x01) {
-        addr = intel_hda_addr(d->dp_lbase & ~0x01, d->dp_ubase);
-        stl_le_pci_dma(&d->pci, addr + 8*s, st->lpib);
-    }
-    dprint(d, 3, "dma: --\n");
-
-    if (irq) {
-        st->ctl |= (1 << 26); /* buffer completion interrupt */
-        intel_hda_update_irq(d);
-    }
-    return true;
-}
-
-static void intel_hda_parse_bdl(IntelHDAState *d, IntelHDAStream *st)
-{
-    hwaddr addr;
-    uint8_t buf[16];
-    uint32_t i;
-
-    addr = intel_hda_addr(st->bdlp_lbase, st->bdlp_ubase);
-    st->bentries = st->lvi +1;
-    g_free(st->bpl);
-    st->bpl = g_malloc(sizeof(bpl) * st->bentries);
-    for (i = 0; i < st->bentries; i++, addr += 16) {
-        pci_dma_read(&d->pci, addr, buf, 16);
-        st->bpl[i].addr  = le64_to_cpu(*(uint64_t *)buf);
-        st->bpl[i].len   = le32_to_cpu(*(uint32_t *)(buf + 8));
-        st->bpl[i].flags = le32_to_cpu(*(uint32_t *)(buf + 12));
-        dprint(d, 1, "bdl/%d: 0x%" PRIx64 " +0x%x, 0x%x\n",
-               i, st->bpl[i].addr, st->bpl[i].len, st->bpl[i].flags);
-    }
-
-    st->bsize = st->cbl;
-    st->lpib  = 0;
-    st->be    = 0;
-    st->bp    = 0;
-}
-
-static void intel_hda_notify_codecs(IntelHDAState *d, uint32_t stream, bool running, bool output)
-{
-    BusChild *kid;
-    HDACodecDevice *cdev;
-
-    QTAILQ_FOREACH(kid, &d->codecs.qbus.children, sibling) {
-        DeviceState *qdev = kid->child;
-        HDACodecDeviceClass *cdc;
-
-        cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
-        cdc = HDA_CODEC_DEVICE_GET_CLASS(cdev);
-        if (cdc->stream) {
-            cdc->stream(cdev, stream, running, output);
-        }
-    }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void intel_hda_set_g_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
-{
-    if ((d->g_ctl & ICH6_GCTL_RESET) == 0) {
-        intel_hda_reset(&d->pci.qdev);
-    }
-}
-
-static void intel_hda_set_wake_en(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
-{
-    intel_hda_update_irq(d);
-}
-
-static void intel_hda_set_state_sts(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
-{
-    intel_hda_update_irq(d);
-}
-
-static void intel_hda_set_int_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
-{
-    intel_hda_update_irq(d);
-}
-
-static void intel_hda_get_wall_clk(IntelHDAState *d, const IntelHDAReg *reg)
-{
-    int64_t ns;
-
-    ns = qemu_get_clock_ns(vm_clock) - d->wall_base_ns;
-    d->wall_clk = (uint32_t)(ns * 24 / 1000);  /* 24 MHz */
-}
-
-static void intel_hda_set_corb_wp(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
-{
-    intel_hda_corb_run(d);
-}
-
-static void intel_hda_set_corb_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
-{
-    intel_hda_corb_run(d);
-}
-
-static void intel_hda_set_rirb_wp(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
-{
-    if (d->rirb_wp & ICH6_RIRBWP_RST) {
-        d->rirb_wp = 0;
-    }
-}
-
-static void intel_hda_set_rirb_sts(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
-{
-    intel_hda_update_irq(d);
-
-    if ((old & ICH6_RBSTS_IRQ) && !(d->rirb_sts & ICH6_RBSTS_IRQ)) {
-        /* cleared ICH6_RBSTS_IRQ */
-        d->rirb_count = 0;
-        intel_hda_corb_run(d);
-    }
-}
-
-static void intel_hda_set_ics(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
-{
-    if (d->ics & ICH6_IRS_BUSY) {
-        intel_hda_corb_run(d);
-    }
-}
-
-static void intel_hda_set_st_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old)
-{
-    bool output = reg->stream >= 4;
-    IntelHDAStream *st = d->st + reg->stream;
-
-    if (st->ctl & 0x01) {
-        /* reset */
-        dprint(d, 1, "st #%d: reset\n", reg->stream);
-        st->ctl = 0;
-    }
-    if ((st->ctl & 0x02) != (old & 0x02)) {
-        uint32_t stnr = (st->ctl >> 20) & 0x0f;
-        /* run bit flipped */
-        if (st->ctl & 0x02) {
-            /* start */
-            dprint(d, 1, "st #%d: start %d (ring buf %d bytes)\n",
-                   reg->stream, stnr, st->cbl);
-            intel_hda_parse_bdl(d, st);
-            intel_hda_notify_codecs(d, stnr, true, output);
-        } else {
-            /* stop */
-            dprint(d, 1, "st #%d: stop %d\n", reg->stream, stnr);
-            intel_hda_notify_codecs(d, stnr, false, output);
-        }
-    }
-    intel_hda_update_irq(d);
-}
-
-/* --------------------------------------------------------------------- */
-
-#define ST_REG(_n, _o) (0x80 + (_n) * 0x20 + (_o))
-
-static const struct IntelHDAReg regtab[] = {
-    /* global */
-    [ ICH6_REG_GCAP ] = {
-        .name     = "GCAP",
-        .size     = 2,
-        .reset    = 0x4401,
-    },
-    [ ICH6_REG_VMIN ] = {
-        .name     = "VMIN",
-        .size     = 1,
-    },
-    [ ICH6_REG_VMAJ ] = {
-        .name     = "VMAJ",
-        .size     = 1,
-        .reset    = 1,
-    },
-    [ ICH6_REG_OUTPAY ] = {
-        .name     = "OUTPAY",
-        .size     = 2,
-        .reset    = 0x3c,
-    },
-    [ ICH6_REG_INPAY ] = {
-        .name     = "INPAY",
-        .size     = 2,
-        .reset    = 0x1d,
-    },
-    [ ICH6_REG_GCTL ] = {
-        .name     = "GCTL",
-        .size     = 4,
-        .wmask    = 0x0103,
-        .offset   = offsetof(IntelHDAState, g_ctl),
-        .whandler = intel_hda_set_g_ctl,
-    },
-    [ ICH6_REG_WAKEEN ] = {
-        .name     = "WAKEEN",
-        .size     = 2,
-        .wmask    = 0x7fff,
-        .offset   = offsetof(IntelHDAState, wake_en),
-        .whandler = intel_hda_set_wake_en,
-    },
-    [ ICH6_REG_STATESTS ] = {
-        .name     = "STATESTS",
-        .size     = 2,
-        .wmask    = 0x7fff,
-        .wclear   = 0x7fff,
-        .offset   = offsetof(IntelHDAState, state_sts),
-        .whandler = intel_hda_set_state_sts,
-    },
-
-    /* interrupts */
-    [ ICH6_REG_INTCTL ] = {
-        .name     = "INTCTL",
-        .size     = 4,
-        .wmask    = 0xc00000ff,
-        .offset   = offsetof(IntelHDAState, int_ctl),
-        .whandler = intel_hda_set_int_ctl,
-    },
-    [ ICH6_REG_INTSTS ] = {
-        .name     = "INTSTS",
-        .size     = 4,
-        .wmask    = 0xc00000ff,
-        .wclear   = 0xc00000ff,
-        .offset   = offsetof(IntelHDAState, int_sts),
-    },
-
-    /* misc */
-    [ ICH6_REG_WALLCLK ] = {
-        .name     = "WALLCLK",
-        .size     = 4,
-        .offset   = offsetof(IntelHDAState, wall_clk),
-        .rhandler = intel_hda_get_wall_clk,
-    },
-    [ ICH6_REG_WALLCLK + 0x2000 ] = {
-        .name     = "WALLCLK(alias)",
-        .size     = 4,
-        .offset   = offsetof(IntelHDAState, wall_clk),
-        .rhandler = intel_hda_get_wall_clk,
-    },
-
-    /* dma engine */
-    [ ICH6_REG_CORBLBASE ] = {
-        .name     = "CORBLBASE",
-        .size     = 4,
-        .wmask    = 0xffffff80,
-        .offset   = offsetof(IntelHDAState, corb_lbase),
-    },
-    [ ICH6_REG_CORBUBASE ] = {
-        .name     = "CORBUBASE",
-        .size     = 4,
-        .wmask    = 0xffffffff,
-        .offset   = offsetof(IntelHDAState, corb_ubase),
-    },
-    [ ICH6_REG_CORBWP ] = {
-        .name     = "CORBWP",
-        .size     = 2,
-        .wmask    = 0xff,
-        .offset   = offsetof(IntelHDAState, corb_wp),
-        .whandler = intel_hda_set_corb_wp,
-    },
-    [ ICH6_REG_CORBRP ] = {
-        .name     = "CORBRP",
-        .size     = 2,
-        .wmask    = 0x80ff,
-        .offset   = offsetof(IntelHDAState, corb_rp),
-    },
-    [ ICH6_REG_CORBCTL ] = {
-        .name     = "CORBCTL",
-        .size     = 1,
-        .wmask    = 0x03,
-        .offset   = offsetof(IntelHDAState, corb_ctl),
-        .whandler = intel_hda_set_corb_ctl,
-    },
-    [ ICH6_REG_CORBSTS ] = {
-        .name     = "CORBSTS",
-        .size     = 1,
-        .wmask    = 0x01,
-        .wclear   = 0x01,
-        .offset   = offsetof(IntelHDAState, corb_sts),
-    },
-    [ ICH6_REG_CORBSIZE ] = {
-        .name     = "CORBSIZE",
-        .size     = 1,
-        .reset    = 0x42,
-        .offset   = offsetof(IntelHDAState, corb_size),
-    },
-    [ ICH6_REG_RIRBLBASE ] = {
-        .name     = "RIRBLBASE",
-        .size     = 4,
-        .wmask    = 0xffffff80,
-        .offset   = offsetof(IntelHDAState, rirb_lbase),
-    },
-    [ ICH6_REG_RIRBUBASE ] = {
-        .name     = "RIRBUBASE",
-        .size     = 4,
-        .wmask    = 0xffffffff,
-        .offset   = offsetof(IntelHDAState, rirb_ubase),
-    },
-    [ ICH6_REG_RIRBWP ] = {
-        .name     = "RIRBWP",
-        .size     = 2,
-        .wmask    = 0x8000,
-        .offset   = offsetof(IntelHDAState, rirb_wp),
-        .whandler = intel_hda_set_rirb_wp,
-    },
-    [ ICH6_REG_RINTCNT ] = {
-        .name     = "RINTCNT",
-        .size     = 2,
-        .wmask    = 0xff,
-        .offset   = offsetof(IntelHDAState, rirb_cnt),
-    },
-    [ ICH6_REG_RIRBCTL ] = {
-        .name     = "RIRBCTL",
-        .size     = 1,
-        .wmask    = 0x07,
-        .offset   = offsetof(IntelHDAState, rirb_ctl),
-    },
-    [ ICH6_REG_RIRBSTS ] = {
-        .name     = "RIRBSTS",
-        .size     = 1,
-        .wmask    = 0x05,
-        .wclear   = 0x05,
-        .offset   = offsetof(IntelHDAState, rirb_sts),
-        .whandler = intel_hda_set_rirb_sts,
-    },
-    [ ICH6_REG_RIRBSIZE ] = {
-        .name     = "RIRBSIZE",
-        .size     = 1,
-        .reset    = 0x42,
-        .offset   = offsetof(IntelHDAState, rirb_size),
-    },
-
-    [ ICH6_REG_DPLBASE ] = {
-        .name     = "DPLBASE",
-        .size     = 4,
-        .wmask    = 0xffffff81,
-        .offset   = offsetof(IntelHDAState, dp_lbase),
-    },
-    [ ICH6_REG_DPUBASE ] = {
-        .name     = "DPUBASE",
-        .size     = 4,
-        .wmask    = 0xffffffff,
-        .offset   = offsetof(IntelHDAState, dp_ubase),
-    },
-
-    [ ICH6_REG_IC ] = {
-        .name     = "ICW",
-        .size     = 4,
-        .wmask    = 0xffffffff,
-        .offset   = offsetof(IntelHDAState, icw),
-    },
-    [ ICH6_REG_IR ] = {
-        .name     = "IRR",
-        .size     = 4,
-        .offset   = offsetof(IntelHDAState, irr),
-    },
-    [ ICH6_REG_IRS ] = {
-        .name     = "ICS",
-        .size     = 2,
-        .wmask    = 0x0003,
-        .wclear   = 0x0002,
-        .offset   = offsetof(IntelHDAState, ics),
-        .whandler = intel_hda_set_ics,
-    },
-
-#define HDA_STREAM(_t, _i)                                            \
-    [ ST_REG(_i, ICH6_REG_SD_CTL) ] = {                               \
-        .stream   = _i,                                               \
-        .name     = _t stringify(_i) " CTL",                          \
-        .size     = 4,                                                \
-        .wmask    = 0x1cff001f,                                       \
-        .offset   = offsetof(IntelHDAState, st[_i].ctl),              \
-        .whandler = intel_hda_set_st_ctl,                             \
-    },                                                                \
-    [ ST_REG(_i, ICH6_REG_SD_CTL) + 2] = {                            \
-        .stream   = _i,                                               \
-        .name     = _t stringify(_i) " CTL(stnr)",                    \
-        .size     = 1,                                                \
-        .shift    = 16,                                               \
-        .wmask    = 0x00ff0000,                                       \
-        .offset   = offsetof(IntelHDAState, st[_i].ctl),              \
-        .whandler = intel_hda_set_st_ctl,                             \
-    },                                                                \
-    [ ST_REG(_i, ICH6_REG_SD_STS)] = {                                \
-        .stream   = _i,                                               \
-        .name     = _t stringify(_i) " CTL(sts)",                     \
-        .size     = 1,                                                \
-        .shift    = 24,                                               \
-        .wmask    = 0x1c000000,                                       \
-        .wclear   = 0x1c000000,                                       \
-        .offset   = offsetof(IntelHDAState, st[_i].ctl),              \
-        .whandler = intel_hda_set_st_ctl,                             \
-    },                                                                \
-    [ ST_REG(_i, ICH6_REG_SD_LPIB) ] = {                              \
-        .stream   = _i,                                               \
-        .name     = _t stringify(_i) " LPIB",                         \
-        .size     = 4,                                                \
-        .offset   = offsetof(IntelHDAState, st[_i].lpib),             \
-    },                                                                \
-    [ ST_REG(_i, ICH6_REG_SD_LPIB) + 0x2000 ] = {                     \
-        .stream   = _i,                                               \
-        .name     = _t stringify(_i) " LPIB(alias)",                  \
-        .size     = 4,                                                \
-        .offset   = offsetof(IntelHDAState, st[_i].lpib),             \
-    },                                                                \
-    [ ST_REG(_i, ICH6_REG_SD_CBL) ] = {                               \
-        .stream   = _i,                                               \
-        .name     = _t stringify(_i) " CBL",                          \
-        .size     = 4,                                                \
-        .wmask    = 0xffffffff,                                       \
-        .offset   = offsetof(IntelHDAState, st[_i].cbl),              \
-    },                                                                \
-    [ ST_REG(_i, ICH6_REG_SD_LVI) ] = {                               \
-        .stream   = _i,                                               \
-        .name     = _t stringify(_i) " LVI",                          \
-        .size     = 2,                                                \
-        .wmask    = 0x00ff,                                           \
-        .offset   = offsetof(IntelHDAState, st[_i].lvi),              \
-    },                                                                \
-    [ ST_REG(_i, ICH6_REG_SD_FIFOSIZE) ] = {                          \
-        .stream   = _i,                                               \
-        .name     = _t stringify(_i) " FIFOS",                        \
-        .size     = 2,                                                \
-        .reset    = HDA_BUFFER_SIZE,                                  \
-    },                                                                \
-    [ ST_REG(_i, ICH6_REG_SD_FORMAT) ] = {                            \
-        .stream   = _i,                                               \
-        .name     = _t stringify(_i) " FMT",                          \
-        .size     = 2,                                                \
-        .wmask    = 0x7f7f,                                           \
-        .offset   = offsetof(IntelHDAState, st[_i].fmt),              \
-    },                                                                \
-    [ ST_REG(_i, ICH6_REG_SD_BDLPL) ] = {                             \
-        .stream   = _i,                                               \
-        .name     = _t stringify(_i) " BDLPL",                        \
-        .size     = 4,                                                \
-        .wmask    = 0xffffff80,                                       \
-        .offset   = offsetof(IntelHDAState, st[_i].bdlp_lbase),       \
-    },                                                                \
-    [ ST_REG(_i, ICH6_REG_SD_BDLPU) ] = {                             \
-        .stream   = _i,                                               \
-        .name     = _t stringify(_i) " BDLPU",                        \
-        .size     = 4,                                                \
-        .wmask    = 0xffffffff,                                       \
-        .offset   = offsetof(IntelHDAState, st[_i].bdlp_ubase),       \
-    },                                                                \
-
-    HDA_STREAM("IN", 0)
-    HDA_STREAM("IN", 1)
-    HDA_STREAM("IN", 2)
-    HDA_STREAM("IN", 3)
-
-    HDA_STREAM("OUT", 4)
-    HDA_STREAM("OUT", 5)
-    HDA_STREAM("OUT", 6)
-    HDA_STREAM("OUT", 7)
-
-};
-
-static const IntelHDAReg *intel_hda_reg_find(IntelHDAState *d, hwaddr addr)
-{
-    const IntelHDAReg *reg;
-
-    if (addr >= sizeof(regtab)/sizeof(regtab[0])) {
-        goto noreg;
-    }
-    reg = regtab+addr;
-    if (reg->name == NULL) {
-        goto noreg;
-    }
-    return reg;
-
-noreg:
-    dprint(d, 1, "unknown register, addr 0x%x\n", (int) addr);
-    return NULL;
-}
-
-static uint32_t *intel_hda_reg_addr(IntelHDAState *d, const IntelHDAReg *reg)
-{
-    uint8_t *addr = (void*)d;
-
-    addr += reg->offset;
-    return (uint32_t*)addr;
-}
-
-static void intel_hda_reg_write(IntelHDAState *d, const IntelHDAReg *reg, uint32_t val,
-                                uint32_t wmask)
-{
-    uint32_t *addr;
-    uint32_t old;
-
-    if (!reg) {
-        return;
-    }
-
-    if (d->debug) {
-        time_t now = time(NULL);
-        if (d->last_write && d->last_reg == reg && d->last_val == val) {
-            d->repeat_count++;
-            if (d->last_sec != now) {
-                dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
-                d->last_sec = now;
-                d->repeat_count = 0;
-            }
-        } else {
-            if (d->repeat_count) {
-                dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
-            }
-            dprint(d, 2, "write %-16s: 0x%x (%x)\n", reg->name, val, wmask);
-            d->last_write = 1;
-            d->last_reg   = reg;
-            d->last_val   = val;
-            d->last_sec   = now;
-            d->repeat_count = 0;
-        }
-    }
-    assert(reg->offset != 0);
-
-    addr = intel_hda_reg_addr(d, reg);
-    old = *addr;
-
-    if (reg->shift) {
-        val <<= reg->shift;
-        wmask <<= reg->shift;
-    }
-    wmask &= reg->wmask;
-    *addr &= ~wmask;
-    *addr |= wmask & val;
-    *addr &= ~(val & reg->wclear);
-
-    if (reg->whandler) {
-        reg->whandler(d, reg, old);
-    }
-}
-
-static uint32_t intel_hda_reg_read(IntelHDAState *d, const IntelHDAReg *reg,
-                                   uint32_t rmask)
-{
-    uint32_t *addr, ret;
-
-    if (!reg) {
-        return 0;
-    }
-
-    if (reg->rhandler) {
-        reg->rhandler(d, reg);
-    }
-
-    if (reg->offset == 0) {
-        /* constant read-only register */
-        ret = reg->reset;
-    } else {
-        addr = intel_hda_reg_addr(d, reg);
-        ret = *addr;
-        if (reg->shift) {
-            ret >>= reg->shift;
-        }
-        ret &= rmask;
-    }
-    if (d->debug) {
-        time_t now = time(NULL);
-        if (!d->last_write && d->last_reg == reg && d->last_val == ret) {
-            d->repeat_count++;
-            if (d->last_sec != now) {
-                dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
-                d->last_sec = now;
-                d->repeat_count = 0;
-            }
-        } else {
-            if (d->repeat_count) {
-                dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count);
-            }
-            dprint(d, 2, "read  %-16s: 0x%x (%x)\n", reg->name, ret, rmask);
-            d->last_write = 0;
-            d->last_reg   = reg;
-            d->last_val   = ret;
-            d->last_sec   = now;
-            d->repeat_count = 0;
-        }
-    }
-    return ret;
-}
-
-static void intel_hda_regs_reset(IntelHDAState *d)
-{
-    uint32_t *addr;
-    int i;
-
-    for (i = 0; i < sizeof(regtab)/sizeof(regtab[0]); i++) {
-        if (regtab[i].name == NULL) {
-            continue;
-        }
-        if (regtab[i].offset == 0) {
-            continue;
-        }
-        addr = intel_hda_reg_addr(d, regtab + i);
-        *addr = regtab[i].reset;
-    }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void intel_hda_mmio_writeb(void *opaque, hwaddr addr, uint32_t val)
-{
-    IntelHDAState *d = opaque;
-    const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
-
-    intel_hda_reg_write(d, reg, val, 0xff);
-}
-
-static void intel_hda_mmio_writew(void *opaque, hwaddr addr, uint32_t val)
-{
-    IntelHDAState *d = opaque;
-    const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
-
-    intel_hda_reg_write(d, reg, val, 0xffff);
-}
-
-static void intel_hda_mmio_writel(void *opaque, hwaddr addr, uint32_t val)
-{
-    IntelHDAState *d = opaque;
-    const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
-
-    intel_hda_reg_write(d, reg, val, 0xffffffff);
-}
-
-static uint32_t intel_hda_mmio_readb(void *opaque, hwaddr addr)
-{
-    IntelHDAState *d = opaque;
-    const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
-
-    return intel_hda_reg_read(d, reg, 0xff);
-}
-
-static uint32_t intel_hda_mmio_readw(void *opaque, hwaddr addr)
-{
-    IntelHDAState *d = opaque;
-    const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
-
-    return intel_hda_reg_read(d, reg, 0xffff);
-}
-
-static uint32_t intel_hda_mmio_readl(void *opaque, hwaddr addr)
-{
-    IntelHDAState *d = opaque;
-    const IntelHDAReg *reg = intel_hda_reg_find(d, addr);
-
-    return intel_hda_reg_read(d, reg, 0xffffffff);
-}
-
-static const MemoryRegionOps intel_hda_mmio_ops = {
-    .old_mmio = {
-        .read = {
-            intel_hda_mmio_readb,
-            intel_hda_mmio_readw,
-            intel_hda_mmio_readl,
-        },
-        .write = {
-            intel_hda_mmio_writeb,
-            intel_hda_mmio_writew,
-            intel_hda_mmio_writel,
-        },
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-/* --------------------------------------------------------------------- */
-
-static void intel_hda_reset(DeviceState *dev)
-{
-    BusChild *kid;
-    IntelHDAState *d = DO_UPCAST(IntelHDAState, pci.qdev, dev);
-    HDACodecDevice *cdev;
-
-    intel_hda_regs_reset(d);
-    d->wall_base_ns = qemu_get_clock_ns(vm_clock);
-
-    /* reset codecs */
-    QTAILQ_FOREACH(kid, &d->codecs.qbus.children, sibling) {
-        DeviceState *qdev = kid->child;
-        cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
-        device_reset(DEVICE(cdev));
-        d->state_sts |= (1 << cdev->cad);
-    }
-    intel_hda_update_irq(d);
-}
-
-static int intel_hda_init(PCIDevice *pci)
-{
-    IntelHDAState *d = DO_UPCAST(IntelHDAState, pci, pci);
-    uint8_t *conf = d->pci.config;
-
-    d->name = object_get_typename(OBJECT(d));
-
-    pci_config_set_interrupt_pin(conf, 1);
-
-    /* HDCTL off 0x40 bit 0 selects signaling mode (1-HDA, 0 - Ac97) 18.1.19 */
-    conf[0x40] = 0x01;
-
-    memory_region_init_io(&d->mmio, &intel_hda_mmio_ops, d,
-                          "intel-hda", 0x4000);
-    pci_register_bar(&d->pci, 0, 0, &d->mmio);
-    if (d->msi) {
-        msi_init(&d->pci, 0x50, 1, true, false);
-    }
-
-    hda_codec_bus_init(&d->pci.qdev, &d->codecs,
-                       intel_hda_response, intel_hda_xfer);
-
-    return 0;
-}
-
-static void intel_hda_exit(PCIDevice *pci)
-{
-    IntelHDAState *d = DO_UPCAST(IntelHDAState, pci, pci);
-
-    msi_uninit(&d->pci);
-    memory_region_destroy(&d->mmio);
-}
-
-static int intel_hda_post_load(void *opaque, int version)
-{
-    IntelHDAState* d = opaque;
-    int i;
-
-    dprint(d, 1, "%s\n", __FUNCTION__);
-    for (i = 0; i < ARRAY_SIZE(d->st); i++) {
-        if (d->st[i].ctl & 0x02) {
-            intel_hda_parse_bdl(d, &d->st[i]);
-        }
-    }
-    intel_hda_update_irq(d);
-    return 0;
-}
-
-static const VMStateDescription vmstate_intel_hda_stream = {
-    .name = "intel-hda-stream",
-    .version_id = 1,
-    .fields = (VMStateField []) {
-        VMSTATE_UINT32(ctl, IntelHDAStream),
-        VMSTATE_UINT32(lpib, IntelHDAStream),
-        VMSTATE_UINT32(cbl, IntelHDAStream),
-        VMSTATE_UINT32(lvi, IntelHDAStream),
-        VMSTATE_UINT32(fmt, IntelHDAStream),
-        VMSTATE_UINT32(bdlp_lbase, IntelHDAStream),
-        VMSTATE_UINT32(bdlp_ubase, IntelHDAStream),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_intel_hda = {
-    .name = "intel-hda",
-    .version_id = 1,
-    .post_load = intel_hda_post_load,
-    .fields = (VMStateField []) {
-        VMSTATE_PCI_DEVICE(pci, IntelHDAState),
-
-        /* registers */
-        VMSTATE_UINT32(g_ctl, IntelHDAState),
-        VMSTATE_UINT32(wake_en, IntelHDAState),
-        VMSTATE_UINT32(state_sts, IntelHDAState),
-        VMSTATE_UINT32(int_ctl, IntelHDAState),
-        VMSTATE_UINT32(int_sts, IntelHDAState),
-        VMSTATE_UINT32(wall_clk, IntelHDAState),
-        VMSTATE_UINT32(corb_lbase, IntelHDAState),
-        VMSTATE_UINT32(corb_ubase, IntelHDAState),
-        VMSTATE_UINT32(corb_rp, IntelHDAState),
-        VMSTATE_UINT32(corb_wp, IntelHDAState),
-        VMSTATE_UINT32(corb_ctl, IntelHDAState),
-        VMSTATE_UINT32(corb_sts, IntelHDAState),
-        VMSTATE_UINT32(corb_size, IntelHDAState),
-        VMSTATE_UINT32(rirb_lbase, IntelHDAState),
-        VMSTATE_UINT32(rirb_ubase, IntelHDAState),
-        VMSTATE_UINT32(rirb_wp, IntelHDAState),
-        VMSTATE_UINT32(rirb_cnt, IntelHDAState),
-        VMSTATE_UINT32(rirb_ctl, IntelHDAState),
-        VMSTATE_UINT32(rirb_sts, IntelHDAState),
-        VMSTATE_UINT32(rirb_size, IntelHDAState),
-        VMSTATE_UINT32(dp_lbase, IntelHDAState),
-        VMSTATE_UINT32(dp_ubase, IntelHDAState),
-        VMSTATE_UINT32(icw, IntelHDAState),
-        VMSTATE_UINT32(irr, IntelHDAState),
-        VMSTATE_UINT32(ics, IntelHDAState),
-        VMSTATE_STRUCT_ARRAY(st, IntelHDAState, 8, 0,
-                             vmstate_intel_hda_stream,
-                             IntelHDAStream),
-
-        /* additional state info */
-        VMSTATE_UINT32(rirb_count, IntelHDAState),
-        VMSTATE_INT64(wall_base_ns, IntelHDAState),
-
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property intel_hda_properties[] = {
-    DEFINE_PROP_UINT32("debug", IntelHDAState, debug, 0),
-    DEFINE_PROP_UINT32("msi", IntelHDAState, msi, 1),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void intel_hda_class_init_common(ObjectClass *klass)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = intel_hda_init;
-    k->exit = intel_hda_exit;
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->class_id = PCI_CLASS_MULTIMEDIA_HD_AUDIO;
-    dc->reset = intel_hda_reset;
-    dc->vmsd = &vmstate_intel_hda;
-    dc->props = intel_hda_properties;
-}
-
-static void intel_hda_class_init_ich6(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    intel_hda_class_init_common(klass);
-    k->device_id = 0x2668;
-    k->revision = 1;
-    dc->desc = "Intel HD Audio Controller (ich6)";
-}
-
-static void intel_hda_class_init_ich9(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    intel_hda_class_init_common(klass);
-    k->device_id = 0x293e;
-    k->revision = 3;
-    dc->desc = "Intel HD Audio Controller (ich9)";
-}
-
-static const TypeInfo intel_hda_info_ich6 = {
-    .name          = "intel-hda",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(IntelHDAState),
-    .class_init    = intel_hda_class_init_ich6,
-};
-
-static const TypeInfo intel_hda_info_ich9 = {
-    .name          = "ich9-intel-hda",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(IntelHDAState),
-    .class_init    = intel_hda_class_init_ich9,
-};
-
-static void hda_codec_device_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *k = DEVICE_CLASS(klass);
-    k->init = hda_codec_dev_init;
-    k->exit = hda_codec_dev_exit;
-    k->bus_type = TYPE_HDA_BUS;
-    k->props = hda_props;
-}
-
-static const TypeInfo hda_codec_device_type_info = {
-    .name = TYPE_HDA_CODEC_DEVICE,
-    .parent = TYPE_DEVICE,
-    .instance_size = sizeof(HDACodecDevice),
-    .abstract = true,
-    .class_size = sizeof(HDACodecDeviceClass),
-    .class_init = hda_codec_device_class_init,
-};
-
-static void intel_hda_register_types(void)
-{
-    type_register_static(&hda_codec_bus_info);
-    type_register_static(&intel_hda_info_ich6);
-    type_register_static(&intel_hda_info_ich9);
-    type_register_static(&hda_codec_device_type_info);
-}
-
-type_init(intel_hda_register_types)
-
-/*
- * create intel hda controller with codec attached to it,
- * so '-soundhw hda' works.
- */
-int intel_hda_and_codec_init(PCIBus *bus)
-{
-    PCIDevice *controller;
-    BusState *hdabus;
-    DeviceState *codec;
-
-    controller = pci_create_simple(bus, -1, "intel-hda");
-    hdabus = QLIST_FIRST(&controller->qdev.child_bus);
-    codec = qdev_create(hdabus, "hda-duplex");
-    qdev_init_nofail(codec);
-    return 0;
-}
-
diff --git a/hw/ioh3420.c b/hw/ioh3420.c
deleted file mode 100644 (file)
index 5cff61e..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * ioh3420.c
- * Intel X58 north bridge IOH
- * PCI Express root port device id 3420
- *
- * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
- *                    VA Linux Systems Japan K.K.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/pci/pci_ids.h"
-#include "hw/pci/msi.h"
-#include "hw/pci/pcie.h"
-#include "hw/ioh3420.h"
-
-#define PCI_DEVICE_ID_IOH_EPORT         0x3420  /* D0:F0 express mode */
-#define PCI_DEVICE_ID_IOH_REV           0x2
-#define IOH_EP_SSVID_OFFSET             0x40
-#define IOH_EP_SSVID_SVID               PCI_VENDOR_ID_INTEL
-#define IOH_EP_SSVID_SSID               0
-#define IOH_EP_MSI_OFFSET               0x60
-#define IOH_EP_MSI_SUPPORTED_FLAGS      PCI_MSI_FLAGS_MASKBIT
-#define IOH_EP_MSI_NR_VECTOR            2
-#define IOH_EP_EXP_OFFSET               0x90
-#define IOH_EP_AER_OFFSET               0x100
-
-/*
- * If two MSI vector are allocated, Advanced Error Interrupt Message Number
- * is 1. otherwise 0.
- * 17.12.5.10 RPERRSTS,  32:27 bit Advanced Error Interrupt Message Number.
- */
-static uint8_t ioh3420_aer_vector(const PCIDevice *d)
-{
-    switch (msi_nr_vectors_allocated(d)) {
-    case 1:
-        return 0;
-    case 2:
-        return 1;
-    case 4:
-    case 8:
-    case 16:
-    case 32:
-    default:
-        break;
-    }
-    abort();
-    return 0;
-}
-
-static void ioh3420_aer_vector_update(PCIDevice *d)
-{
-    pcie_aer_root_set_vector(d, ioh3420_aer_vector(d));
-}
-
-static void ioh3420_write_config(PCIDevice *d,
-                                   uint32_t address, uint32_t val, int len)
-{
-    uint32_t root_cmd =
-        pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_ROOT_COMMAND);
-
-    pci_bridge_write_config(d, address, val, len);
-    ioh3420_aer_vector_update(d);
-    pcie_cap_slot_write_config(d, address, val, len);
-    pcie_aer_write_config(d, address, val, len);
-    pcie_aer_root_write_config(d, address, val, len, root_cmd);
-}
-
-static void ioh3420_reset(DeviceState *qdev)
-{
-    PCIDevice *d = PCI_DEVICE(qdev);
-
-    ioh3420_aer_vector_update(d);
-    pcie_cap_root_reset(d);
-    pcie_cap_deverr_reset(d);
-    pcie_cap_slot_reset(d);
-    pcie_aer_root_reset(d);
-    pci_bridge_reset(qdev);
-    pci_bridge_disable_base_limit(d);
-}
-
-static int ioh3420_initfn(PCIDevice *d)
-{
-    PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
-    PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
-    PCIESlot *s = DO_UPCAST(PCIESlot, port, p);
-    int rc;
-
-    rc = pci_bridge_initfn(d, TYPE_PCIE_BUS);
-    if (rc < 0) {
-        return rc;
-    }
-
-    pcie_port_init_reg(d);
-
-    rc = pci_bridge_ssvid_init(d, IOH_EP_SSVID_OFFSET,
-                               IOH_EP_SSVID_SVID, IOH_EP_SSVID_SSID);
-    if (rc < 0) {
-        goto err_bridge;
-    }
-    rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR,
-                  IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
-                  IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
-    if (rc < 0) {
-        goto err_bridge;
-    }
-    rc = pcie_cap_init(d, IOH_EP_EXP_OFFSET, PCI_EXP_TYPE_ROOT_PORT, p->port);
-    if (rc < 0) {
-        goto err_msi;
-    }
-    pcie_cap_deverr_init(d);
-    pcie_cap_slot_init(d, s->slot);
-    pcie_chassis_create(s->chassis);
-    rc = pcie_chassis_add_slot(s);
-    if (rc < 0) {
-        goto err_pcie_cap;
-    }
-    pcie_cap_root_init(d);
-    rc = pcie_aer_init(d, IOH_EP_AER_OFFSET);
-    if (rc < 0) {
-        goto err;
-    }
-    pcie_aer_root_init(d);
-    ioh3420_aer_vector_update(d);
-    return 0;
-
-err:
-    pcie_chassis_del_slot(s);
-err_pcie_cap:
-    pcie_cap_exit(d);
-err_msi:
-    msi_uninit(d);
-err_bridge:
-    pci_bridge_exitfn(d);
-    return rc;
-}
-
-static void ioh3420_exitfn(PCIDevice *d)
-{
-    PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
-    PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
-    PCIESlot *s = DO_UPCAST(PCIESlot, port, p);
-
-    pcie_aer_exit(d);
-    pcie_chassis_del_slot(s);
-    pcie_cap_exit(d);
-    msi_uninit(d);
-    pci_bridge_exitfn(d);
-}
-
-PCIESlot *ioh3420_init(PCIBus *bus, int devfn, bool multifunction,
-                         const char *bus_name, pci_map_irq_fn map_irq,
-                         uint8_t port, uint8_t chassis, uint16_t slot)
-{
-    PCIDevice *d;
-    PCIBridge *br;
-    DeviceState *qdev;
-
-    d = pci_create_multifunction(bus, devfn, multifunction, "ioh3420");
-    if (!d) {
-        return NULL;
-    }
-    br = DO_UPCAST(PCIBridge, dev, d);
-
-    qdev = &br->dev.qdev;
-    pci_bridge_map_irq(br, bus_name, map_irq);
-    qdev_prop_set_uint8(qdev, "port", port);
-    qdev_prop_set_uint8(qdev, "chassis", chassis);
-    qdev_prop_set_uint16(qdev, "slot", slot);
-    qdev_init_nofail(qdev);
-
-    return DO_UPCAST(PCIESlot, port, DO_UPCAST(PCIEPort, br, br));
-}
-
-static const VMStateDescription vmstate_ioh3420 = {
-    .name = "ioh-3240-express-root-port",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .post_load = pcie_cap_slot_post_load,
-    .fields = (VMStateField[]) {
-        VMSTATE_PCIE_DEVICE(port.br.dev, PCIESlot),
-        VMSTATE_STRUCT(port.br.dev.exp.aer_log, PCIESlot, 0,
-                       vmstate_pcie_aer_log, PCIEAERLog),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property ioh3420_properties[] = {
-    DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0),
-    DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0),
-    DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0),
-    DEFINE_PROP_UINT16("aer_log_max", PCIESlot,
-    port.br.dev.exp.aer_log.log_max,
-    PCIE_AER_LOG_MAX_DEFAULT),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void ioh3420_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->is_express = 1;
-    k->is_bridge = 1;
-    k->config_write = ioh3420_write_config;
-    k->init = ioh3420_initfn;
-    k->exit = ioh3420_exitfn;
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->device_id = PCI_DEVICE_ID_IOH_EPORT;
-    k->revision = PCI_DEVICE_ID_IOH_REV;
-    dc->desc = "Intel IOH device id 3420 PCIE Root Port";
-    dc->reset = ioh3420_reset;
-    dc->vmsd = &vmstate_ioh3420;
-    dc->props = ioh3420_properties;
-}
-
-static const TypeInfo ioh3420_info = {
-    .name          = "ioh3420",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIESlot),
-    .class_init    = ioh3420_class_init,
-};
-
-static void ioh3420_register_types(void)
-{
-    type_register_static(&ioh3420_info);
-}
-
-type_init(ioh3420_register_types)
-
-/*
- * Local variables:
- *  c-indent-level: 4
- *  c-basic-offset: 4
- *  tab-width: 8
- *  indent-tab-mode: nil
- * End:
- */
diff --git a/hw/ipack.c b/hw/ipack.c
deleted file mode 100644 (file)
index b1f46c1..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * QEMU IndustryPack emulation
- *
- * Copyright (C) 2012 Igalia, S.L.
- * Author: Alberto Garcia <agarcia@igalia.com>
- *
- * This code is licensed under the GNU GPL v2 or (at your option) any
- * later version.
- */
-
-#include "hw/ipack.h"
-
-IPackDevice *ipack_device_find(IPackBus *bus, int32_t slot)
-{
-    BusChild *kid;
-
-    QTAILQ_FOREACH(kid, &BUS(bus)->children, sibling) {
-        DeviceState *qdev = kid->child;
-        IPackDevice *ip = IPACK_DEVICE(qdev);
-        if (ip->slot == slot) {
-            return ip;
-        }
-    }
-    return NULL;
-}
-
-void ipack_bus_new_inplace(IPackBus *bus, DeviceState *parent,
-                           const char *name, uint8_t n_slots,
-                           qemu_irq_handler handler)
-{
-    qbus_create_inplace(&bus->qbus, TYPE_IPACK_BUS, parent, name);
-    bus->n_slots = n_slots;
-    bus->set_irq = handler;
-}
-
-static int ipack_device_dev_init(DeviceState *qdev)
-{
-    IPackBus *bus = IPACK_BUS(qdev_get_parent_bus(qdev));
-    IPackDevice *dev = IPACK_DEVICE(qdev);
-    IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev);
-
-    if (dev->slot < 0) {
-        dev->slot = bus->free_slot;
-    }
-    if (dev->slot >= bus->n_slots) {
-        return -1;
-    }
-    bus->free_slot = dev->slot + 1;
-
-    dev->irq = qemu_allocate_irqs(bus->set_irq, dev, 2);
-
-    return k->init(dev);
-}
-
-static int ipack_device_dev_exit(DeviceState *qdev)
-{
-    IPackDevice *dev = IPACK_DEVICE(qdev);
-    IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(dev);
-
-    if (k->exit) {
-        k->exit(dev);
-    }
-
-    qemu_free_irqs(dev->irq);
-
-    return 0;
-}
-
-static Property ipack_device_props[] = {
-    DEFINE_PROP_INT32("slot", IPackDevice, slot, -1),
-    DEFINE_PROP_END_OF_LIST()
-};
-
-static void ipack_device_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *k = DEVICE_CLASS(klass);
-    k->bus_type = TYPE_IPACK_BUS;
-    k->init = ipack_device_dev_init;
-    k->exit = ipack_device_dev_exit;
-    k->props = ipack_device_props;
-}
-
-const VMStateDescription vmstate_ipack_device = {
-    .name = "ipack_device",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_INT32(slot, IPackDevice),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const TypeInfo ipack_device_info = {
-    .name          = TYPE_IPACK_DEVICE,
-    .parent        = TYPE_DEVICE,
-    .instance_size = sizeof(IPackDevice),
-    .class_size    = sizeof(IPackDeviceClass),
-    .class_init    = ipack_device_class_init,
-    .abstract      = true,
-};
-
-static const TypeInfo ipack_bus_info = {
-    .name = TYPE_IPACK_BUS,
-    .parent = TYPE_BUS,
-    .instance_size = sizeof(IPackBus),
-};
-
-static void ipack_register_types(void)
-{
-    type_register_static(&ipack_device_info);
-    type_register_static(&ipack_bus_info);
-}
-
-type_init(ipack_register_types)
diff --git a/hw/ipoctal232.c b/hw/ipoctal232.c
deleted file mode 100644 (file)
index 685fee2..0000000
+++ /dev/null
@@ -1,605 +0,0 @@
-/*
- * QEMU GE IP-Octal 232 IndustryPack emulation
- *
- * Copyright (C) 2012 Igalia, S.L.
- * Author: Alberto Garcia <agarcia@igalia.com>
- *
- * This code is licensed under the GNU GPL v2 or (at your option) any
- * later version.
- */
-
-#include "hw/ipack.h"
-#include "qemu/bitops.h"
-#include "char/char.h"
-
-/* #define DEBUG_IPOCTAL */
-
-#ifdef DEBUG_IPOCTAL
-#define DPRINTF2(fmt, ...) \
-    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF2(fmt, ...) do { } while (0)
-#endif
-
-#define DPRINTF(fmt, ...) DPRINTF2("IP-Octal: " fmt, ## __VA_ARGS__)
-
-#define RX_FIFO_SIZE 3
-
-/* The IP-Octal has 8 channels (a-h)
-   divided into 4 blocks (A-D) */
-#define N_CHANNELS 8
-#define N_BLOCKS   4
-
-#define REG_MRa  0x01
-#define REG_MRb  0x11
-#define REG_SRa  0x03
-#define REG_SRb  0x13
-#define REG_CSRa 0x03
-#define REG_CSRb 0x13
-#define REG_CRa  0x05
-#define REG_CRb  0x15
-#define REG_RHRa 0x07
-#define REG_RHRb 0x17
-#define REG_THRa 0x07
-#define REG_THRb 0x17
-#define REG_ACR  0x09
-#define REG_ISR  0x0B
-#define REG_IMR  0x0B
-#define REG_OPCR 0x1B
-
-#define CR_ENABLE_RX    BIT(0)
-#define CR_DISABLE_RX   BIT(1)
-#define CR_ENABLE_TX    BIT(2)
-#define CR_DISABLE_TX   BIT(3)
-#define CR_CMD(cr)      ((cr) >> 4)
-#define CR_NO_OP        0
-#define CR_RESET_MR     1
-#define CR_RESET_RX     2
-#define CR_RESET_TX     3
-#define CR_RESET_ERR    4
-#define CR_RESET_BRKINT 5
-#define CR_START_BRK    6
-#define CR_STOP_BRK     7
-#define CR_ASSERT_RTSN  8
-#define CR_NEGATE_RTSN  9
-#define CR_TIMEOUT_ON   10
-#define CR_TIMEOUT_OFF  12
-
-#define SR_RXRDY   BIT(0)
-#define SR_FFULL   BIT(1)
-#define SR_TXRDY   BIT(2)
-#define SR_TXEMT   BIT(3)
-#define SR_OVERRUN BIT(4)
-#define SR_PARITY  BIT(5)
-#define SR_FRAMING BIT(6)
-#define SR_BREAK   BIT(7)
-
-#define ISR_TXRDYA BIT(0)
-#define ISR_RXRDYA BIT(1)
-#define ISR_BREAKA BIT(2)
-#define ISR_CNTRDY BIT(3)
-#define ISR_TXRDYB BIT(4)
-#define ISR_RXRDYB BIT(5)
-#define ISR_BREAKB BIT(6)
-#define ISR_MPICHG BIT(7)
-#define ISR_TXRDY(CH) (((CH) & 1) ? BIT(4) : BIT(0))
-#define ISR_RXRDY(CH) (((CH) & 1) ? BIT(5) : BIT(1))
-#define ISR_BREAK(CH) (((CH) & 1) ? BIT(6) : BIT(2))
-
-typedef struct IPOctalState IPOctalState;
-typedef struct SCC2698Channel SCC2698Channel;
-typedef struct SCC2698Block SCC2698Block;
-
-struct SCC2698Channel {
-    IPOctalState *ipoctal;
-    CharDriverState *dev;
-    bool rx_enabled;
-    uint8_t mr[2];
-    uint8_t mr_idx;
-    uint8_t sr;
-    uint8_t rhr[RX_FIFO_SIZE];
-    uint8_t rhr_idx;
-    uint8_t rx_pending;
-};
-
-struct SCC2698Block {
-    uint8_t imr;
-    uint8_t isr;
-};
-
-struct IPOctalState {
-    IPackDevice dev;
-    SCC2698Channel ch[N_CHANNELS];
-    SCC2698Block blk[N_BLOCKS];
-    uint8_t irq_vector;
-};
-
-#define TYPE_IPOCTAL "ipoctal232"
-
-#define IPOCTAL(obj) \
-    OBJECT_CHECK(IPOctalState, (obj), TYPE_IPOCTAL)
-
-static const VMStateDescription vmstate_scc2698_channel = {
-    .name = "scc2698_channel",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_BOOL(rx_enabled, SCC2698Channel),
-        VMSTATE_UINT8_ARRAY(mr, SCC2698Channel, 2),
-        VMSTATE_UINT8(mr_idx, SCC2698Channel),
-        VMSTATE_UINT8(sr, SCC2698Channel),
-        VMSTATE_UINT8_ARRAY(rhr, SCC2698Channel, RX_FIFO_SIZE),
-        VMSTATE_UINT8(rhr_idx, SCC2698Channel),
-        VMSTATE_UINT8(rx_pending, SCC2698Channel),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_scc2698_block = {
-    .name = "scc2698_block",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT8(imr, SCC2698Block),
-        VMSTATE_UINT8(isr, SCC2698Block),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_ipoctal = {
-    .name = "ipoctal232",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_IPACK_DEVICE(dev, IPOctalState),
-        VMSTATE_STRUCT_ARRAY(ch, IPOctalState, N_CHANNELS, 1,
-                             vmstate_scc2698_channel, SCC2698Channel),
-        VMSTATE_STRUCT_ARRAY(blk, IPOctalState, N_BLOCKS, 1,
-                             vmstate_scc2698_block, SCC2698Block),
-        VMSTATE_UINT8(irq_vector, IPOctalState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-/* data[10] is 0x0C, not 0x0B as the doc says */
-static const uint8_t id_prom_data[] = {
-    0x49, 0x50, 0x41, 0x43, 0xF0, 0x22,
-    0xA1, 0x00, 0x00, 0x00, 0x0C, 0xCC
-};
-
-static void update_irq(IPOctalState *dev, unsigned block)
-{
-    /* Blocks A and B interrupt on INT0#, C and D on INT1#.
-       Thus, to get the status we have to check two blocks. */
-    SCC2698Block *blk0 = &dev->blk[block];
-    SCC2698Block *blk1 = &dev->blk[block^1];
-    unsigned intno = block / 2;
-
-    if ((blk0->isr & blk0->imr) || (blk1->isr & blk1->imr)) {
-        qemu_irq_raise(dev->dev.irq[intno]);
-    } else {
-        qemu_irq_lower(dev->dev.irq[intno]);
-    }
-}
-
-static void write_cr(IPOctalState *dev, unsigned channel, uint8_t val)
-{
-    SCC2698Channel *ch = &dev->ch[channel];
-    SCC2698Block *blk = &dev->blk[channel / 2];
-
-    DPRINTF("Write CR%c %u: ", channel + 'a', val);
-
-    /* The lower 4 bits are used to enable and disable Tx and Rx */
-    if (val & CR_ENABLE_RX) {
-        DPRINTF2("Rx on, ");
-        ch->rx_enabled = true;
-    }
-    if (val & CR_DISABLE_RX) {
-        DPRINTF2("Rx off, ");
-        ch->rx_enabled = false;
-    }
-    if (val & CR_ENABLE_TX) {
-        DPRINTF2("Tx on, ");
-        ch->sr |= SR_TXRDY | SR_TXEMT;
-        blk->isr |= ISR_TXRDY(channel);
-    }
-    if (val & CR_DISABLE_TX) {
-        DPRINTF2("Tx off, ");
-        ch->sr &= ~(SR_TXRDY | SR_TXEMT);
-        blk->isr &= ~ISR_TXRDY(channel);
-    }
-
-    DPRINTF2("cmd: ");
-
-    /* The rest of the bits implement different commands */
-    switch (CR_CMD(val)) {
-    case CR_NO_OP:
-        DPRINTF2("none");
-        break;
-    case CR_RESET_MR:
-        DPRINTF2("reset MR");
-        ch->mr_idx = 0;
-        break;
-    case CR_RESET_RX:
-        DPRINTF2("reset Rx");
-        ch->rx_enabled = false;
-        ch->rx_pending = 0;
-        ch->sr &= ~SR_RXRDY;
-        blk->isr &= ~ISR_RXRDY(channel);
-        break;
-    case CR_RESET_TX:
-        DPRINTF2("reset Tx");
-        ch->sr &= ~(SR_TXRDY | SR_TXEMT);
-        blk->isr &= ~ISR_TXRDY(channel);
-        break;
-    case CR_RESET_ERR:
-        DPRINTF2("reset err");
-        ch->sr &= ~(SR_OVERRUN | SR_PARITY | SR_FRAMING | SR_BREAK);
-        break;
-    case CR_RESET_BRKINT:
-        DPRINTF2("reset brk ch int");
-        blk->isr &= ~(ISR_BREAKA | ISR_BREAKB);
-        break;
-    default:
-        DPRINTF2("unsupported 0x%x", CR_CMD(val));
-    }
-
-    DPRINTF2("\n");
-}
-
-static uint16_t io_read(IPackDevice *ip, uint8_t addr)
-{
-    IPOctalState *dev = IPOCTAL(ip);
-    uint16_t ret = 0;
-    /* addr[7:6]: block   (A-D)
-       addr[7:5]: channel (a-h)
-       addr[5:0]: register */
-    unsigned block = addr >> 5;
-    unsigned channel = addr >> 4;
-    /* Big endian, accessed using 8-bit bytes at odd locations */
-    unsigned offset = (addr & 0x1F) ^ 1;
-    SCC2698Channel *ch = &dev->ch[channel];
-    SCC2698Block *blk = &dev->blk[block];
-    uint8_t old_isr = blk->isr;
-
-    switch (offset) {
-
-    case REG_MRa:
-    case REG_MRb:
-        ret = ch->mr[ch->mr_idx];
-        DPRINTF("Read MR%u%c: 0x%x\n", ch->mr_idx + 1, channel + 'a', ret);
-        ch->mr_idx = 1;
-        break;
-
-    case REG_SRa:
-    case REG_SRb:
-        ret = ch->sr;
-        DPRINTF("Read SR%c: 0x%x\n", channel + 'a', ret);
-        break;
-
-    case REG_RHRa:
-    case REG_RHRb:
-        ret = ch->rhr[ch->rhr_idx];
-        if (ch->rx_pending > 0) {
-            ch->rx_pending--;
-            if (ch->rx_pending == 0) {
-                ch->sr &= ~SR_RXRDY;
-                blk->isr &= ~ISR_RXRDY(channel);
-                if (ch->dev) {
-                    qemu_chr_accept_input(ch->dev);
-                }
-            } else {
-                ch->rhr_idx = (ch->rhr_idx + 1) % RX_FIFO_SIZE;
-            }
-            if (ch->sr & SR_BREAK) {
-                ch->sr &= ~SR_BREAK;
-                blk->isr |= ISR_BREAK(channel);
-            }
-        }
-        DPRINTF("Read RHR%c (0x%x)\n", channel + 'a', ret);
-        break;
-
-    case REG_ISR:
-        ret = blk->isr;
-        DPRINTF("Read ISR%c: 0x%x\n", block + 'A', ret);
-        break;
-
-    default:
-        DPRINTF("Read unknown/unsupported register 0x%02x\n", offset);
-    }
-
-    if (old_isr != blk->isr) {
-        update_irq(dev, block);
-    }
-
-    return ret;
-}
-
-static void io_write(IPackDevice *ip, uint8_t addr, uint16_t val)
-{
-    IPOctalState *dev = IPOCTAL(ip);
-    unsigned reg = val & 0xFF;
-    /* addr[7:6]: block   (A-D)
-       addr[7:5]: channel (a-h)
-       addr[5:0]: register */
-    unsigned block = addr >> 5;
-    unsigned channel = addr >> 4;
-    /* Big endian, accessed using 8-bit bytes at odd locations */
-    unsigned offset = (addr & 0x1F) ^ 1;
-    SCC2698Channel *ch = &dev->ch[channel];
-    SCC2698Block *blk = &dev->blk[block];
-    uint8_t old_isr = blk->isr;
-    uint8_t old_imr = blk->imr;
-
-    switch (offset) {
-
-    case REG_MRa:
-    case REG_MRb:
-        ch->mr[ch->mr_idx] = reg;
-        DPRINTF("Write MR%u%c 0x%x\n", ch->mr_idx + 1, channel + 'a', reg);
-        ch->mr_idx = 1;
-        break;
-
-    /* Not implemented */
-    case REG_CSRa:
-    case REG_CSRb:
-        DPRINTF("Write CSR%c: 0x%x\n", channel + 'a', reg);
-        break;
-
-    case REG_CRa:
-    case REG_CRb:
-        write_cr(dev, channel, reg);
-        break;
-
-    case REG_THRa:
-    case REG_THRb:
-        if (ch->sr & SR_TXRDY) {
-            DPRINTF("Write THR%c (0x%x)\n", channel + 'a', reg);
-            if (ch->dev) {
-                uint8_t thr = reg;
-                qemu_chr_fe_write(ch->dev, &thr, 1);
-            }
-        } else {
-            DPRINTF("Write THR%c (0x%x), Tx disabled\n", channel + 'a', reg);
-        }
-        break;
-
-    /* Not implemented */
-    case REG_ACR:
-        DPRINTF("Write ACR%c 0x%x\n", block + 'A', val);
-        break;
-
-    case REG_IMR:
-        DPRINTF("Write IMR%c 0x%x\n", block + 'A', val);
-        blk->imr = reg;
-        break;
-
-    /* Not implemented */
-    case REG_OPCR:
-        DPRINTF("Write OPCR%c 0x%x\n", block + 'A', val);
-        break;
-
-    default:
-        DPRINTF("Write unknown/unsupported register 0x%02x %u\n", offset, val);
-    }
-
-    if (old_isr != blk->isr || old_imr != blk->imr) {
-        update_irq(dev, block);
-    }
-}
-
-static uint16_t id_read(IPackDevice *ip, uint8_t addr)
-{
-    uint16_t ret = 0;
-    unsigned pos = addr / 2; /* The ID PROM data is stored every other byte */
-
-    if (pos < ARRAY_SIZE(id_prom_data)) {
-        ret = id_prom_data[pos];
-    } else {
-        DPRINTF("Attempt to read unavailable PROM data at 0x%x\n",  addr);
-    }
-
-    return ret;
-}
-
-static void id_write(IPackDevice *ip, uint8_t addr, uint16_t val)
-{
-    IPOctalState *dev = IPOCTAL(ip);
-    if (addr == 1) {
-        DPRINTF("Write IRQ vector: %u\n", (unsigned) val);
-        dev->irq_vector = val; /* Undocumented, but the hw works like that */
-    } else {
-        DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
-    }
-}
-
-static uint16_t int_read(IPackDevice *ip, uint8_t addr)
-{
-    IPOctalState *dev = IPOCTAL(ip);
-    /* Read address 0 to ACK INT0# and address 2 to ACK INT1# */
-    if (addr != 0 && addr != 2) {
-        DPRINTF("Attempt to read from 0x%x\n", addr);
-        return 0;
-    } else {
-        /* Update interrupts if necessary */
-        update_irq(dev, addr);
-        return dev->irq_vector;
-    }
-}
-
-static void int_write(IPackDevice *ip, uint8_t addr, uint16_t val)
-{
-    DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
-}
-
-static uint16_t mem_read16(IPackDevice *ip, uint32_t addr)
-{
-    DPRINTF("Attempt to read from 0x%x\n", addr);
-    return 0;
-}
-
-static void mem_write16(IPackDevice *ip, uint32_t addr, uint16_t val)
-{
-    DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
-}
-
-static uint8_t mem_read8(IPackDevice *ip, uint32_t addr)
-{
-    DPRINTF("Attempt to read from 0x%x\n", addr);
-    return 0;
-}
-
-static void mem_write8(IPackDevice *ip, uint32_t addr, uint8_t val)
-{
-    IPOctalState *dev = IPOCTAL(ip);
-    if (addr == 1) {
-        DPRINTF("Write IRQ vector: %u\n", (unsigned) val);
-        dev->irq_vector = val;
-    } else {
-        DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
-    }
-}
-
-static int hostdev_can_receive(void *opaque)
-{
-    SCC2698Channel *ch = opaque;
-    int available_bytes = RX_FIFO_SIZE - ch->rx_pending;
-    return ch->rx_enabled ? available_bytes : 0;
-}
-
-static void hostdev_receive(void *opaque, const uint8_t *buf, int size)
-{
-    SCC2698Channel *ch = opaque;
-    IPOctalState *dev = ch->ipoctal;
-    unsigned pos = ch->rhr_idx + ch->rx_pending;
-    int i;
-
-    assert(size + ch->rx_pending <= RX_FIFO_SIZE);
-
-    /* Copy data to the RxFIFO */
-    for (i = 0; i < size; i++) {
-        pos %= RX_FIFO_SIZE;
-        ch->rhr[pos++] = buf[i];
-    }
-
-    ch->rx_pending += size;
-
-    /* If the RxFIFO was empty raise an interrupt */
-    if (!(ch->sr & SR_RXRDY)) {
-        unsigned block, channel = 0;
-        /* Find channel number to update the ISR register */
-        while (&dev->ch[channel] != ch) {
-            channel++;
-        }
-        block = channel / 2;
-        dev->blk[block].isr |= ISR_RXRDY(channel);
-        ch->sr |= SR_RXRDY;
-        update_irq(dev, block);
-    }
-}
-
-static void hostdev_event(void *opaque, int event)
-{
-    SCC2698Channel *ch = opaque;
-    switch (event) {
-    case CHR_EVENT_OPENED:
-        DPRINTF("Device %s opened\n", ch->dev->label);
-        break;
-    case CHR_EVENT_BREAK: {
-        uint8_t zero = 0;
-        DPRINTF("Device %s received break\n", ch->dev->label);
-
-        if (!(ch->sr & SR_BREAK)) {
-            IPOctalState *dev = ch->ipoctal;
-            unsigned block, channel = 0;
-
-            while (&dev->ch[channel] != ch) {
-                channel++;
-            }
-            block = channel / 2;
-
-            ch->sr |= SR_BREAK;
-            dev->blk[block].isr |= ISR_BREAK(channel);
-        }
-
-        /* Put a zero character in the buffer */
-        hostdev_receive(ch, &zero, 1);
-    }
-        break;
-    default:
-        DPRINTF("Device %s received event %d\n", ch->dev->label, event);
-    }
-}
-
-static int ipoctal_init(IPackDevice *ip)
-{
-    IPOctalState *s = IPOCTAL(ip);
-    unsigned i;
-
-    for (i = 0; i < N_CHANNELS; i++) {
-        SCC2698Channel *ch = &s->ch[i];
-        ch->ipoctal = s;
-
-        /* Redirect IP-Octal channels to host character devices */
-        if (ch->dev) {
-            qemu_chr_add_handlers(ch->dev, hostdev_can_receive,
-                                  hostdev_receive, hostdev_event, ch);
-            DPRINTF("Redirecting channel %u to %s\n", i, ch->dev->label);
-        } else {
-            DPRINTF("Could not redirect channel %u, no chardev set\n", i);
-        }
-    }
-
-    return 0;
-}
-
-static Property ipoctal_properties[] = {
-    DEFINE_PROP_CHR("chardev0", IPOctalState, ch[0].dev),
-    DEFINE_PROP_CHR("chardev1", IPOctalState, ch[1].dev),
-    DEFINE_PROP_CHR("chardev2", IPOctalState, ch[2].dev),
-    DEFINE_PROP_CHR("chardev3", IPOctalState, ch[3].dev),
-    DEFINE_PROP_CHR("chardev4", IPOctalState, ch[4].dev),
-    DEFINE_PROP_CHR("chardev5", IPOctalState, ch[5].dev),
-    DEFINE_PROP_CHR("chardev6", IPOctalState, ch[6].dev),
-    DEFINE_PROP_CHR("chardev7", IPOctalState, ch[7].dev),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void ipoctal_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    IPackDeviceClass *ic = IPACK_DEVICE_CLASS(klass);
-
-    ic->init        = ipoctal_init;
-    ic->io_read     = io_read;
-    ic->io_write    = io_write;
-    ic->id_read     = id_read;
-    ic->id_write    = id_write;
-    ic->int_read    = int_read;
-    ic->int_write   = int_write;
-    ic->mem_read16  = mem_read16;
-    ic->mem_write16 = mem_write16;
-    ic->mem_read8   = mem_read8;
-    ic->mem_write8  = mem_write8;
-
-    dc->desc    = "GE IP-Octal 232 8-channel RS-232 IndustryPack";
-    dc->props   = ipoctal_properties;
-    dc->vmsd    = &vmstate_ipoctal;
-}
-
-static const TypeInfo ipoctal_info = {
-    .name          = TYPE_IPOCTAL,
-    .parent        = TYPE_IPACK_DEVICE,
-    .instance_size = sizeof(IPOctalState),
-    .class_init    = ipoctal_class_init,
-};
-
-static void ipoctal_register_types(void)
-{
-    type_register_static(&ipoctal_info);
-}
-
-type_init(ipoctal_register_types)
diff --git a/hw/irq.c b/hw/irq.c
deleted file mode 100644 (file)
index 2078542..0000000
--- a/hw/irq.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * QEMU IRQ/GPIO common code.
- *
- * Copyright (c) 2007 CodeSourcery.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "qemu-common.h"
-#include "hw/irq.h"
-
-struct IRQState {
-    qemu_irq_handler handler;
-    void *opaque;
-    int n;
-};
-
-void qemu_set_irq(qemu_irq irq, int level)
-{
-    if (!irq)
-        return;
-
-    irq->handler(irq->opaque, irq->n, level);
-}
-
-qemu_irq *qemu_extend_irqs(qemu_irq *old, int n_old, qemu_irq_handler handler,
-                           void *opaque, int n)
-{
-    qemu_irq *s;
-    struct IRQState *p;
-    int i;
-
-    if (!old) {
-        n_old = 0;
-    }
-    s = old ? g_renew(qemu_irq, old, n + n_old) : g_new(qemu_irq, n);
-    p = old ? g_renew(struct IRQState, s[0], n + n_old) :
-                g_new(struct IRQState, n);
-    for (i = 0; i < n + n_old; i++) {
-        if (i >= n_old) {
-            p->handler = handler;
-            p->opaque = opaque;
-            p->n = i;
-        }
-        s[i] = p;
-        p++;
-    }
-    return s;
-}
-
-qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n)
-{
-    return qemu_extend_irqs(NULL, 0, handler, opaque, n);
-}
-
-
-void qemu_free_irqs(qemu_irq *s)
-{
-    g_free(s[0]);
-    g_free(s);
-}
-
-static void qemu_notirq(void *opaque, int line, int level)
-{
-    struct IRQState *irq = opaque;
-
-    irq->handler(irq->opaque, irq->n, !level);
-}
-
-qemu_irq qemu_irq_invert(qemu_irq irq)
-{
-    /* The default state for IRQs is low, so raise the output now.  */
-    qemu_irq_raise(irq);
-    return qemu_allocate_irqs(qemu_notirq, irq, 1)[0];
-}
-
-static void qemu_splitirq(void *opaque, int line, int level)
-{
-    struct IRQState **irq = opaque;
-    irq[0]->handler(irq[0]->opaque, irq[0]->n, level);
-    irq[1]->handler(irq[1]->opaque, irq[1]->n, level);
-}
-
-qemu_irq qemu_irq_split(qemu_irq irq1, qemu_irq irq2)
-{
-    qemu_irq *s = g_malloc0(2 * sizeof(qemu_irq));
-    s[0] = irq1;
-    s[1] = irq2;
-    return qemu_allocate_irqs(qemu_splitirq, s, 1)[0];
-}
-
-static void proxy_irq_handler(void *opaque, int n, int level)
-{
-    qemu_irq **target = opaque;
-
-    if (*target) {
-        qemu_set_irq((*target)[n], level);
-    }
-}
-
-qemu_irq *qemu_irq_proxy(qemu_irq **target, int n)
-{
-    return qemu_allocate_irqs(proxy_irq_handler, target, n);
-}
-
-void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n)
-{
-    int i;
-    qemu_irq *old_irqs = qemu_allocate_irqs(NULL, NULL, n);
-    for (i = 0; i < n; i++) {
-        *old_irqs[i] = *gpio_in[i];
-        gpio_in[i]->handler = handler;
-        gpio_in[i]->opaque = old_irqs;
-    }
-}
-
-void qemu_irq_intercept_out(qemu_irq **gpio_out, qemu_irq_handler handler, int n)
-{
-    qemu_irq *old_irqs = *gpio_out;
-    *gpio_out = qemu_allocate_irqs(handler, old_irqs, n);
-}
diff --git a/hw/isa-bus.c b/hw/isa-bus.c
deleted file mode 100644 (file)
index 7860b17..0000000
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * isa bus support for qdev.
- *
- * Copyright (c) 2009 Gerd Hoffmann <kraxel@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-#include "hw/hw.h"
-#include "monitor/monitor.h"
-#include "hw/sysbus.h"
-#include "sysemu/sysemu.h"
-#include "hw/isa/isa.h"
-#include "exec/address-spaces.h"
-
-static ISABus *isabus;
-hwaddr isa_mem_base = 0;
-
-static void isabus_dev_print(Monitor *mon, DeviceState *dev, int indent);
-static char *isabus_get_fw_dev_path(DeviceState *dev);
-
-static void isa_bus_class_init(ObjectClass *klass, void *data)
-{
-    BusClass *k = BUS_CLASS(klass);
-
-    k->print_dev = isabus_dev_print;
-    k->get_fw_dev_path = isabus_get_fw_dev_path;
-}
-
-static const TypeInfo isa_bus_info = {
-    .name = TYPE_ISA_BUS,
-    .parent = TYPE_BUS,
-    .instance_size = sizeof(ISABus),
-    .class_init = isa_bus_class_init,
-};
-
-ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space_io)
-{
-    if (isabus) {
-        fprintf(stderr, "Can't create a second ISA bus\n");
-        return NULL;
-    }
-    if (NULL == dev) {
-        dev = qdev_create(NULL, "isabus-bridge");
-        qdev_init_nofail(dev);
-    }
-
-    isabus = FROM_QBUS(ISABus, qbus_create(TYPE_ISA_BUS, dev, NULL));
-    isabus->address_space_io = address_space_io;
-    return isabus;
-}
-
-void isa_bus_irqs(ISABus *bus, qemu_irq *irqs)
-{
-    if (!bus) {
-        hw_error("Can't set isa irqs with no isa bus present.");
-    }
-    bus->irqs = irqs;
-}
-
-/*
- * isa_get_irq() returns the corresponding qemu_irq entry for the i8259.
- *
- * This function is only for special cases such as the 'ferr', and
- * temporary use for normal devices until they are converted to qdev.
- */
-qemu_irq isa_get_irq(ISADevice *dev, int isairq)
-{
-    assert(!dev || DO_UPCAST(ISABus, qbus, dev->qdev.parent_bus) == isabus);
-    if (isairq < 0 || isairq > 15) {
-        hw_error("isa irq %d invalid", isairq);
-    }
-    return isabus->irqs[isairq];
-}
-
-void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq)
-{
-    assert(dev->nirqs < ARRAY_SIZE(dev->isairq));
-    dev->isairq[dev->nirqs] = isairq;
-    *p = isa_get_irq(dev, isairq);
-    dev->nirqs++;
-}
-
-static inline void isa_init_ioport(ISADevice *dev, uint16_t ioport)
-{
-    if (dev && (dev->ioport_id == 0 || ioport < dev->ioport_id)) {
-        dev->ioport_id = ioport;
-    }
-}
-
-void isa_register_ioport(ISADevice *dev, MemoryRegion *io, uint16_t start)
-{
-    memory_region_add_subregion(isabus->address_space_io, start, io);
-    isa_init_ioport(dev, start);
-}
-
-void isa_register_portio_list(ISADevice *dev, uint16_t start,
-                              const MemoryRegionPortio *pio_start,
-                              void *opaque, const char *name)
-{
-    PortioList *piolist = g_new(PortioList, 1);
-
-    /* START is how we should treat DEV, regardless of the actual
-       contents of the portio array.  This is how the old code
-       actually handled e.g. the FDC device.  */
-    isa_init_ioport(dev, start);
-
-    portio_list_init(piolist, pio_start, opaque, name);
-    portio_list_add(piolist, isabus->address_space_io, start);
-}
-
-static int isa_qdev_init(DeviceState *qdev)
-{
-    ISADevice *dev = ISA_DEVICE(qdev);
-    ISADeviceClass *klass = ISA_DEVICE_GET_CLASS(dev);
-
-    if (klass->init) {
-        return klass->init(dev);
-    }
-
-    return 0;
-}
-
-static void isa_device_init(Object *obj)
-{
-    ISADevice *dev = ISA_DEVICE(obj);
-
-    dev->isairq[0] = -1;
-    dev->isairq[1] = -1;
-}
-
-ISADevice *isa_create(ISABus *bus, const char *name)
-{
-    DeviceState *dev;
-
-    if (!bus) {
-        hw_error("Tried to create isa device %s with no isa bus present.",
-                 name);
-    }
-    dev = qdev_create(&bus->qbus, name);
-    return ISA_DEVICE(dev);
-}
-
-ISADevice *isa_try_create(ISABus *bus, const char *name)
-{
-    DeviceState *dev;
-
-    if (!bus) {
-        hw_error("Tried to create isa device %s with no isa bus present.",
-                 name);
-    }
-    dev = qdev_try_create(&bus->qbus, name);
-    return ISA_DEVICE(dev);
-}
-
-ISADevice *isa_create_simple(ISABus *bus, const char *name)
-{
-    ISADevice *dev;
-
-    dev = isa_create(bus, name);
-    qdev_init_nofail(&dev->qdev);
-    return dev;
-}
-
-ISADevice *isa_vga_init(ISABus *bus)
-{
-    switch (vga_interface_type) {
-    case VGA_CIRRUS:
-        return isa_create_simple(bus, "isa-cirrus-vga");
-    case VGA_QXL:
-        fprintf(stderr, "%s: qxl: no PCI bus\n", __func__);
-        return NULL;
-    case VGA_STD:
-        return isa_create_simple(bus, "isa-vga");
-    case VGA_VMWARE:
-        fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __func__);
-        return NULL;
-    case VGA_NONE:
-    default:
-        return NULL;
-    }
-}
-
-static void isabus_dev_print(Monitor *mon, DeviceState *dev, int indent)
-{
-    ISADevice *d = ISA_DEVICE(dev);
-
-    if (d->isairq[1] != -1) {
-        monitor_printf(mon, "%*sisa irqs %d,%d\n", indent, "",
-                       d->isairq[0], d->isairq[1]);
-    } else if (d->isairq[0] != -1) {
-        monitor_printf(mon, "%*sisa irq %d\n", indent, "",
-                       d->isairq[0]);
-    }
-}
-
-static int isabus_bridge_init(SysBusDevice *dev)
-{
-    /* nothing */
-    return 0;
-}
-
-static void isabus_bridge_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = isabus_bridge_init;
-    dc->fw_name = "isa";
-    dc->no_user = 1;
-}
-
-static const TypeInfo isabus_bridge_info = {
-    .name          = "isabus-bridge",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(SysBusDevice),
-    .class_init    = isabus_bridge_class_init,
-};
-
-static void isa_device_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *k = DEVICE_CLASS(klass);
-    k->init = isa_qdev_init;
-    k->bus_type = TYPE_ISA_BUS;
-}
-
-static const TypeInfo isa_device_type_info = {
-    .name = TYPE_ISA_DEVICE,
-    .parent = TYPE_DEVICE,
-    .instance_size = sizeof(ISADevice),
-    .instance_init = isa_device_init,
-    .abstract = true,
-    .class_size = sizeof(ISADeviceClass),
-    .class_init = isa_device_class_init,
-};
-
-static void isabus_register_types(void)
-{
-    type_register_static(&isa_bus_info);
-    type_register_static(&isabus_bridge_info);
-    type_register_static(&isa_device_type_info);
-}
-
-static char *isabus_get_fw_dev_path(DeviceState *dev)
-{
-    ISADevice *d = (ISADevice*)dev;
-    char path[40];
-    int off;
-
-    off = snprintf(path, sizeof(path), "%s", qdev_fw_name(dev));
-    if (d->ioport_id) {
-        snprintf(path + off, sizeof(path) - off, "@%04x", d->ioport_id);
-    }
-
-    return g_strdup(path);
-}
-
-MemoryRegion *isa_address_space(ISADevice *dev)
-{
-    return get_system_memory();
-}
-
-MemoryRegion *isa_address_space_io(ISADevice *dev)
-{
-    if (dev) {
-        return isa_bus_from_device(dev)->address_space_io;
-    }
-
-    return isabus->address_space_io;
-}
-
-type_init(isabus_register_types)
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ad3643bd422f2f348f46d8c14713ac1ea0f84d58 100644 (file)
@@ -0,0 +1,7 @@
+common-obj-y += isa-bus.o
+common-obj-$(CONFIG_APM) += apm.o
+common-obj-$(CONFIG_I82378) += i82378.o
+common-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
+common-obj-$(CONFIG_PC87312) += pc87312.o
+common-obj-$(CONFIG_PIIX4) += piix4.o
+
diff --git a/hw/isa/apm.c b/hw/isa/apm.c
new file mode 100644 (file)
index 0000000..5f21d21
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * QEMU PC APM controller Emulation
+ * This is split out from acpi.c
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "hw/isa/apm.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+
+//#define DEBUG
+
+#ifdef DEBUG
+# define APM_DPRINTF(format, ...)       printf(format, ## __VA_ARGS__)
+#else
+# define APM_DPRINTF(format, ...)       do { } while (0)
+#endif
+
+/* fixed I/O location */
+#define APM_CNT_IOPORT  0xb2
+#define APM_STS_IOPORT  0xb3
+
+static void apm_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
+                              unsigned size)
+{
+    APMState *apm = opaque;
+    addr &= 1;
+    APM_DPRINTF("apm_ioport_writeb addr=0x%x val=0x%02x\n", addr, val);
+    if (addr == 0) {
+        apm->apmc = val;
+
+        if (apm->callback) {
+            (apm->callback)(val, apm->arg);
+        }
+    } else {
+        apm->apms = val;
+    }
+}
+
+static uint64_t apm_ioport_readb(void *opaque, hwaddr addr, unsigned size)
+{
+    APMState *apm = opaque;
+    uint32_t val;
+
+    addr &= 1;
+    if (addr == 0) {
+        val = apm->apmc;
+    } else {
+        val = apm->apms;
+    }
+    APM_DPRINTF("apm_ioport_readb addr=0x%x val=0x%02x\n", addr, val);
+    return val;
+}
+
+const VMStateDescription vmstate_apm = {
+    .name = "APM State",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(apmc, APMState),
+        VMSTATE_UINT8(apms, APMState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const MemoryRegionOps apm_ops = {
+    .read = apm_ioport_readb,
+    .write = apm_ioport_writeb,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+void apm_init(PCIDevice *dev, APMState *apm, apm_ctrl_changed_t callback,
+              void *arg)
+{
+    apm->callback = callback;
+    apm->arg = arg;
+
+    /* ioport 0xb2, 0xb3 */
+    memory_region_init_io(&apm->io, &apm_ops, apm, "apm-io", 2);
+    memory_region_add_subregion(pci_address_space_io(dev), APM_CNT_IOPORT,
+                                &apm->io);
+}
diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c
new file mode 100644 (file)
index 0000000..cced9af
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * QEMU Intel i82378 emulation (PCI to ISA bridge)
+ *
+ * Copyright (c) 2010-2011 Hervé Poussineau
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/pci/pci.h"
+#include "hw/i386/pc.h"
+#include "hw/timer/i8254.h"
+#include "hw/audio/pcspk.h"
+
+//#define DEBUG_I82378
+
+#ifdef DEBUG_I82378
+#define DPRINTF(fmt, ...) \
+do { fprintf(stderr, "i82378: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+do {} while (0)
+#endif
+
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "i82378 ERROR: " fmt , ## __VA_ARGS__); } while (0)
+
+typedef struct I82378State {
+    qemu_irq out[2];
+    qemu_irq *i8259;
+    MemoryRegion io;
+    MemoryRegion mem;
+} I82378State;
+
+typedef struct PCIi82378State {
+    PCIDevice pci_dev;
+    uint32_t isa_io_base;
+    uint32_t isa_mem_base;
+    I82378State state;
+} PCIi82378State;
+
+static const VMStateDescription vmstate_pci_i82378 = {
+    .name = "pci-i82378",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(pci_dev, PCIi82378State),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static void i82378_io_write(void *opaque, hwaddr addr,
+                            uint64_t value, unsigned int size)
+{
+    switch (size) {
+    case 1:
+        DPRINTF("%s: " TARGET_FMT_plx "=%02" PRIx64 "\n", __func__,
+                addr, value);
+        cpu_outb(addr, value);
+        break;
+    case 2:
+        DPRINTF("%s: " TARGET_FMT_plx "=%04" PRIx64 "\n", __func__,
+                addr, value);
+        cpu_outw(addr, value);
+        break;
+    case 4:
+        DPRINTF("%s: " TARGET_FMT_plx "=%08" PRIx64 "\n", __func__,
+                addr, value);
+        cpu_outl(addr, value);
+        break;
+    default:
+        abort();
+    }
+}
+
+static uint64_t i82378_io_read(void *opaque, hwaddr addr,
+                               unsigned int size)
+{
+    DPRINTF("%s: " TARGET_FMT_plx "\n", __func__, addr);
+    switch (size) {
+    case 1:
+        return cpu_inb(addr);
+    case 2:
+        return cpu_inw(addr);
+    case 4:
+        return cpu_inl(addr);
+    default:
+        abort();
+    }
+}
+
+static const MemoryRegionOps i82378_io_ops = {
+    .read = i82378_io_read,
+    .write = i82378_io_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void i82378_mem_write(void *opaque, hwaddr addr,
+                             uint64_t value, unsigned int size)
+{
+    switch (size) {
+    case 1:
+        DPRINTF("%s: " TARGET_FMT_plx "=%02" PRIx64 "\n", __func__,
+                addr, value);
+        cpu_outb(addr, value);
+        break;
+    case 2:
+        DPRINTF("%s: " TARGET_FMT_plx "=%04" PRIx64 "\n", __func__,
+                addr, value);
+        cpu_outw(addr, value);
+        break;
+    case 4:
+        DPRINTF("%s: " TARGET_FMT_plx "=%08" PRIx64 "\n", __func__,
+                addr, value);
+        cpu_outl(addr, value);
+        break;
+    default:
+        abort();
+    }
+}
+
+static uint64_t i82378_mem_read(void *opaque, hwaddr addr,
+                                unsigned int size)
+{
+    DPRINTF("%s: " TARGET_FMT_plx "\n", __func__, addr);
+    switch (size) {
+    case 1:
+        return cpu_inb(addr);
+    case 2:
+        return cpu_inw(addr);
+    case 4:
+        return cpu_inl(addr);
+    default:
+        abort();
+    }
+}
+
+static const MemoryRegionOps i82378_mem_ops = {
+    .read = i82378_mem_read,
+    .write = i82378_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void i82378_request_out0_irq(void *opaque, int irq, int level)
+{
+    I82378State *s = opaque;
+    qemu_set_irq(s->out[0], level);
+}
+
+static void i82378_request_pic_irq(void *opaque, int irq, int level)
+{
+    DeviceState *dev = opaque;
+    PCIDevice *pci = DO_UPCAST(PCIDevice, qdev, dev);
+    PCIi82378State *s = DO_UPCAST(PCIi82378State, pci_dev, pci);
+
+    qemu_set_irq(s->state.i8259[irq], level);
+}
+
+static void i82378_init(DeviceState *dev, I82378State *s)
+{
+    ISABus *isabus = DO_UPCAST(ISABus, qbus, qdev_get_child_bus(dev, "isa.0"));
+    ISADevice *pit;
+    ISADevice *isa;
+    qemu_irq *out0_irq;
+
+    /* This device has:
+       2 82C59 (irq)
+       1 82C54 (pit)
+       2 82C37 (dma)
+       NMI
+       Utility Bus Support Registers
+
+       All devices accept byte access only, except timer
+     */
+
+    qdev_init_gpio_out(dev, s->out, 2);
+    qdev_init_gpio_in(dev, i82378_request_pic_irq, 16);
+
+    /* Workaround the fact that i8259 is not qdev'ified... */
+    out0_irq = qemu_allocate_irqs(i82378_request_out0_irq, s, 1);
+
+    /* 2 82C59 (irq) */
+    s->i8259 = i8259_init(isabus, *out0_irq);
+    isa_bus_irqs(isabus, s->i8259);
+
+    /* 1 82C54 (pit) */
+    pit = pit_init(isabus, 0x40, 0, NULL);
+
+    /* speaker */
+    pcspk_init(isabus, pit);
+
+    /* 2 82C37 (dma) */
+    isa = isa_create_simple(isabus, "i82374");
+    qdev_connect_gpio_out(&isa->qdev, 0, s->out[1]);
+
+    /* timer */
+    isa_create_simple(isabus, "mc146818rtc");
+}
+
+static int pci_i82378_init(PCIDevice *dev)
+{
+    PCIi82378State *pci = DO_UPCAST(PCIi82378State, pci_dev, dev);
+    I82378State *s = &pci->state;
+    uint8_t *pci_conf;
+
+    pci_conf = dev->config;
+    pci_set_word(pci_conf + PCI_COMMAND,
+                 PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+    pci_set_word(pci_conf + PCI_STATUS,
+                 PCI_STATUS_DEVSEL_MEDIUM);
+
+    pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin 0 */
+
+    memory_region_init_io(&s->io, &i82378_io_ops, s, "i82378-io", 0x00010000);
+    pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->io);
+
+    memory_region_init_io(&s->mem, &i82378_mem_ops, s, "i82378-mem", 0x01000000);
+    pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem);
+
+    /* Make I/O address read only */
+    pci_set_word(dev->wmask + PCI_COMMAND, PCI_COMMAND_SPECIAL);
+    pci_set_long(dev->wmask + PCI_BASE_ADDRESS_0, 0);
+    pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, pci->isa_io_base);
+
+    isa_mem_base = pci->isa_mem_base;
+    isa_bus_new(&dev->qdev, pci_address_space_io(dev));
+
+    i82378_init(&dev->qdev, s);
+
+    return 0;
+}
+
+static Property i82378_properties[] = {
+    DEFINE_PROP_HEX32("iobase", PCIi82378State, isa_io_base, 0x80000000),
+    DEFINE_PROP_HEX32("membase", PCIi82378State, isa_mem_base, 0xc0000000),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void pci_i82378_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    k->init = pci_i82378_init;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_82378;
+    k->revision = 0x03;
+    k->class_id = PCI_CLASS_BRIDGE_ISA;
+    k->subsystem_vendor_id = 0x0;
+    k->subsystem_id = 0x0;
+    dc->vmsd = &vmstate_pci_i82378;
+    dc->props = i82378_properties;
+}
+
+static const TypeInfo pci_i82378_info = {
+    .name = "i82378",
+    .parent = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIi82378State),
+    .class_init = pci_i82378_class_init,
+};
+
+static void i82378_register_types(void)
+{
+    type_register_static(&pci_i82378_info);
+}
+
+type_init(i82378_register_types)
diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c
new file mode 100644 (file)
index 0000000..7860b17
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * isa bus support for qdev.
+ *
+ * Copyright (c) 2009 Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "hw/hw.h"
+#include "monitor/monitor.h"
+#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "hw/isa/isa.h"
+#include "exec/address-spaces.h"
+
+static ISABus *isabus;
+hwaddr isa_mem_base = 0;
+
+static void isabus_dev_print(Monitor *mon, DeviceState *dev, int indent);
+static char *isabus_get_fw_dev_path(DeviceState *dev);
+
+static void isa_bus_class_init(ObjectClass *klass, void *data)
+{
+    BusClass *k = BUS_CLASS(klass);
+
+    k->print_dev = isabus_dev_print;
+    k->get_fw_dev_path = isabus_get_fw_dev_path;
+}
+
+static const TypeInfo isa_bus_info = {
+    .name = TYPE_ISA_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(ISABus),
+    .class_init = isa_bus_class_init,
+};
+
+ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space_io)
+{
+    if (isabus) {
+        fprintf(stderr, "Can't create a second ISA bus\n");
+        return NULL;
+    }
+    if (NULL == dev) {
+        dev = qdev_create(NULL, "isabus-bridge");
+        qdev_init_nofail(dev);
+    }
+
+    isabus = FROM_QBUS(ISABus, qbus_create(TYPE_ISA_BUS, dev, NULL));
+    isabus->address_space_io = address_space_io;
+    return isabus;
+}
+
+void isa_bus_irqs(ISABus *bus, qemu_irq *irqs)
+{
+    if (!bus) {
+        hw_error("Can't set isa irqs with no isa bus present.");
+    }
+    bus->irqs = irqs;
+}
+
+/*
+ * isa_get_irq() returns the corresponding qemu_irq entry for the i8259.
+ *
+ * This function is only for special cases such as the 'ferr', and
+ * temporary use for normal devices until they are converted to qdev.
+ */
+qemu_irq isa_get_irq(ISADevice *dev, int isairq)
+{
+    assert(!dev || DO_UPCAST(ISABus, qbus, dev->qdev.parent_bus) == isabus);
+    if (isairq < 0 || isairq > 15) {
+        hw_error("isa irq %d invalid", isairq);
+    }
+    return isabus->irqs[isairq];
+}
+
+void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq)
+{
+    assert(dev->nirqs < ARRAY_SIZE(dev->isairq));
+    dev->isairq[dev->nirqs] = isairq;
+    *p = isa_get_irq(dev, isairq);
+    dev->nirqs++;
+}
+
+static inline void isa_init_ioport(ISADevice *dev, uint16_t ioport)
+{
+    if (dev && (dev->ioport_id == 0 || ioport < dev->ioport_id)) {
+        dev->ioport_id = ioport;
+    }
+}
+
+void isa_register_ioport(ISADevice *dev, MemoryRegion *io, uint16_t start)
+{
+    memory_region_add_subregion(isabus->address_space_io, start, io);
+    isa_init_ioport(dev, start);
+}
+
+void isa_register_portio_list(ISADevice *dev, uint16_t start,
+                              const MemoryRegionPortio *pio_start,
+                              void *opaque, const char *name)
+{
+    PortioList *piolist = g_new(PortioList, 1);
+
+    /* START is how we should treat DEV, regardless of the actual
+       contents of the portio array.  This is how the old code
+       actually handled e.g. the FDC device.  */
+    isa_init_ioport(dev, start);
+
+    portio_list_init(piolist, pio_start, opaque, name);
+    portio_list_add(piolist, isabus->address_space_io, start);
+}
+
+static int isa_qdev_init(DeviceState *qdev)
+{
+    ISADevice *dev = ISA_DEVICE(qdev);
+    ISADeviceClass *klass = ISA_DEVICE_GET_CLASS(dev);
+
+    if (klass->init) {
+        return klass->init(dev);
+    }
+
+    return 0;
+}
+
+static void isa_device_init(Object *obj)
+{
+    ISADevice *dev = ISA_DEVICE(obj);
+
+    dev->isairq[0] = -1;
+    dev->isairq[1] = -1;
+}
+
+ISADevice *isa_create(ISABus *bus, const char *name)
+{
+    DeviceState *dev;
+
+    if (!bus) {
+        hw_error("Tried to create isa device %s with no isa bus present.",
+                 name);
+    }
+    dev = qdev_create(&bus->qbus, name);
+    return ISA_DEVICE(dev);
+}
+
+ISADevice *isa_try_create(ISABus *bus, const char *name)
+{
+    DeviceState *dev;
+
+    if (!bus) {
+        hw_error("Tried to create isa device %s with no isa bus present.",
+                 name);
+    }
+    dev = qdev_try_create(&bus->qbus, name);
+    return ISA_DEVICE(dev);
+}
+
+ISADevice *isa_create_simple(ISABus *bus, const char *name)
+{
+    ISADevice *dev;
+
+    dev = isa_create(bus, name);
+    qdev_init_nofail(&dev->qdev);
+    return dev;
+}
+
+ISADevice *isa_vga_init(ISABus *bus)
+{
+    switch (vga_interface_type) {
+    case VGA_CIRRUS:
+        return isa_create_simple(bus, "isa-cirrus-vga");
+    case VGA_QXL:
+        fprintf(stderr, "%s: qxl: no PCI bus\n", __func__);
+        return NULL;
+    case VGA_STD:
+        return isa_create_simple(bus, "isa-vga");
+    case VGA_VMWARE:
+        fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __func__);
+        return NULL;
+    case VGA_NONE:
+    default:
+        return NULL;
+    }
+}
+
+static void isabus_dev_print(Monitor *mon, DeviceState *dev, int indent)
+{
+    ISADevice *d = ISA_DEVICE(dev);
+
+    if (d->isairq[1] != -1) {
+        monitor_printf(mon, "%*sisa irqs %d,%d\n", indent, "",
+                       d->isairq[0], d->isairq[1]);
+    } else if (d->isairq[0] != -1) {
+        monitor_printf(mon, "%*sisa irq %d\n", indent, "",
+                       d->isairq[0]);
+    }
+}
+
+static int isabus_bridge_init(SysBusDevice *dev)
+{
+    /* nothing */
+    return 0;
+}
+
+static void isabus_bridge_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = isabus_bridge_init;
+    dc->fw_name = "isa";
+    dc->no_user = 1;
+}
+
+static const TypeInfo isabus_bridge_info = {
+    .name          = "isabus-bridge",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SysBusDevice),
+    .class_init    = isabus_bridge_class_init,
+};
+
+static void isa_device_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *k = DEVICE_CLASS(klass);
+    k->init = isa_qdev_init;
+    k->bus_type = TYPE_ISA_BUS;
+}
+
+static const TypeInfo isa_device_type_info = {
+    .name = TYPE_ISA_DEVICE,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(ISADevice),
+    .instance_init = isa_device_init,
+    .abstract = true,
+    .class_size = sizeof(ISADeviceClass),
+    .class_init = isa_device_class_init,
+};
+
+static void isabus_register_types(void)
+{
+    type_register_static(&isa_bus_info);
+    type_register_static(&isabus_bridge_info);
+    type_register_static(&isa_device_type_info);
+}
+
+static char *isabus_get_fw_dev_path(DeviceState *dev)
+{
+    ISADevice *d = (ISADevice*)dev;
+    char path[40];
+    int off;
+
+    off = snprintf(path, sizeof(path), "%s", qdev_fw_name(dev));
+    if (d->ioport_id) {
+        snprintf(path + off, sizeof(path) - off, "@%04x", d->ioport_id);
+    }
+
+    return g_strdup(path);
+}
+
+MemoryRegion *isa_address_space(ISADevice *dev)
+{
+    return get_system_memory();
+}
+
+MemoryRegion *isa_address_space_io(ISADevice *dev)
+{
+    if (dev) {
+        return isa_bus_from_device(dev)->address_space_io;
+    }
+
+    return isabus->address_space_io;
+}
+
+type_init(isabus_register_types)
diff --git a/hw/isa/isa_mmio.c b/hw/isa/isa_mmio.c
new file mode 100644 (file)
index 0000000..d4dbf13
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Memory mapped access to ISA IO space.
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/isa/isa.h"
+#include "exec/address-spaces.h"
+
+static void isa_mmio_writeb (void *opaque, hwaddr addr,
+                                  uint32_t val)
+{
+    cpu_outb(addr & IOPORTS_MASK, val);
+}
+
+static void isa_mmio_writew(void *opaque, hwaddr addr,
+                               uint32_t val)
+{
+    cpu_outw(addr & IOPORTS_MASK, val);
+}
+
+static void isa_mmio_writel(void *opaque, hwaddr addr,
+                               uint32_t val)
+{
+    cpu_outl(addr & IOPORTS_MASK, val);
+}
+
+static uint32_t isa_mmio_readb (void *opaque, hwaddr addr)
+{
+    return cpu_inb(addr & IOPORTS_MASK);
+}
+
+static uint32_t isa_mmio_readw(void *opaque, hwaddr addr)
+{
+    return cpu_inw(addr & IOPORTS_MASK);
+}
+
+static uint32_t isa_mmio_readl(void *opaque, hwaddr addr)
+{
+    return cpu_inl(addr & IOPORTS_MASK);
+}
+
+static const MemoryRegionOps isa_mmio_ops = {
+    .old_mmio = {
+        .write = { isa_mmio_writeb, isa_mmio_writew, isa_mmio_writel },
+        .read = { isa_mmio_readb, isa_mmio_readw, isa_mmio_readl, },
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+void isa_mmio_setup(MemoryRegion *mr, hwaddr size)
+{
+    memory_region_init_io(mr, &isa_mmio_ops, NULL, "isa-mmio", size);
+}
+
+void isa_mmio_init(hwaddr base, hwaddr size)
+{
+    MemoryRegion *mr = g_malloc(sizeof(*mr));
+
+    isa_mmio_setup(mr, size);
+    memory_region_add_subregion(get_system_memory(), base, mr);
+}
diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c
new file mode 100644 (file)
index 0000000..9f5e185
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+ * QEMU National Semiconductor PC87312 (Super I/O)
+ *
+ * Copyright (c) 2010-2012 Herve Poussineau
+ * Copyright (c) 2011-2012 Andreas Färber
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/isa/pc87312.h"
+#include "qemu/error-report.h"
+#include "sysemu/blockdev.h"
+#include "sysemu/sysemu.h"
+#include "char/char.h"
+#include "trace.h"
+
+
+#define REG_FER 0
+#define REG_FAR 1
+#define REG_PTR 2
+
+#define FER_PARALLEL_EN   0x01
+#define FER_UART1_EN      0x02
+#define FER_UART2_EN      0x04
+#define FER_FDC_EN        0x08
+#define FER_FDC_4         0x10
+#define FER_FDC_ADDR      0x20
+#define FER_IDE_EN        0x40
+#define FER_IDE_ADDR      0x80
+
+#define FAR_PARALLEL_ADDR 0x03
+#define FAR_UART1_ADDR    0x0C
+#define FAR_UART2_ADDR    0x30
+#define FAR_UART_3_4      0xC0
+
+#define PTR_POWER_DOWN    0x01
+#define PTR_CLOCK_DOWN    0x02
+#define PTR_PWDN          0x04
+#define PTR_IRQ_5_7       0x08
+#define PTR_UART1_TEST    0x10
+#define PTR_UART2_TEST    0x20
+#define PTR_LOCK_CONF     0x40
+#define PTR_EPP_MODE      0x80
+
+
+/* Parallel port */
+
+static inline bool is_parallel_enabled(PC87312State *s)
+{
+    return s->regs[REG_FER] & FER_PARALLEL_EN;
+}
+
+static const uint32_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 };
+
+static inline uint32_t get_parallel_iobase(PC87312State *s)
+{
+    return parallel_base[s->regs[REG_FAR] & FAR_PARALLEL_ADDR];
+}
+
+static const uint32_t parallel_irq[] = { 5, 7, 5, 0 };
+
+static inline uint32_t get_parallel_irq(PC87312State *s)
+{
+    int idx;
+    idx = (s->regs[REG_FAR] & FAR_PARALLEL_ADDR);
+    if (idx == 0) {
+        return (s->regs[REG_PTR] & PTR_IRQ_5_7) ? 7 : 5;
+    } else {
+        return parallel_irq[idx];
+    }
+}
+
+static inline bool is_parallel_epp(PC87312State *s)
+{
+    return s->regs[REG_PTR] & PTR_EPP_MODE;
+}
+
+
+/* UARTs */
+
+static const uint32_t uart_base[2][4] = {
+    { 0x3e8, 0x338, 0x2e8, 0x220 },
+    { 0x2e8, 0x238, 0x2e0, 0x228 }
+};
+
+static inline uint32_t get_uart_iobase(PC87312State *s, int i)
+{
+    int idx;
+    idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
+    if (idx == 0) {
+        return 0x3f8;
+    } else if (idx == 1) {
+        return 0x2f8;
+    } else {
+        return uart_base[idx & 1][(s->regs[REG_FAR] & FAR_UART_3_4) >> 6];
+    }
+}
+
+static inline uint32_t get_uart_irq(PC87312State *s, int i)
+{
+    int idx;
+    idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
+    return (idx & 1) ? 3 : 4;
+}
+
+static inline bool is_uart_enabled(PC87312State *s, int i)
+{
+    return s->regs[REG_FER] & (FER_UART1_EN << i);
+}
+
+
+/* Floppy controller */
+
+static inline bool is_fdc_enabled(PC87312State *s)
+{
+    return s->regs[REG_FER] & FER_FDC_EN;
+}
+
+static inline uint32_t get_fdc_iobase(PC87312State *s)
+{
+    return (s->regs[REG_FER] & FER_FDC_ADDR) ? 0x370 : 0x3f0;
+}
+
+
+/* IDE controller */
+
+static inline bool is_ide_enabled(PC87312State *s)
+{
+    return s->regs[REG_FER] & FER_IDE_EN;
+}
+
+static inline uint32_t get_ide_iobase(PC87312State *s)
+{
+    return (s->regs[REG_FER] & FER_IDE_ADDR) ? 0x170 : 0x1f0;
+}
+
+
+static void reconfigure_devices(PC87312State *s)
+{
+    error_report("pc87312: unsupported device reconfiguration (%02x %02x %02x)",
+                 s->regs[REG_FER], s->regs[REG_FAR], s->regs[REG_PTR]);
+}
+
+static void pc87312_soft_reset(PC87312State *s)
+{
+    static const uint8_t fer_init[] = {
+        0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4b, 0x4b,
+        0x4b, 0x4b, 0x4b, 0x4b, 0x0f, 0x0f, 0x0f, 0x0f,
+        0x49, 0x49, 0x49, 0x49, 0x07, 0x07, 0x07, 0x07,
+        0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x08, 0x00,
+    };
+    static const uint8_t far_init[] = {
+        0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x00, 0x01,
+        0x01, 0x09, 0x08, 0x08, 0x10, 0x11, 0x39, 0x24,
+        0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x39, 0x24,
+        0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x10, 0x10,
+    };
+    static const uint8_t ptr_init[] = {
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+    };
+
+    s->read_id_step = 0;
+    s->selected_index = REG_FER;
+
+    s->regs[REG_FER] = fer_init[s->config & 0x1f];
+    s->regs[REG_FAR] = far_init[s->config & 0x1f];
+    s->regs[REG_PTR] = ptr_init[s->config & 0x1f];
+}
+
+static void pc87312_hard_reset(PC87312State *s)
+{
+    pc87312_soft_reset(s);
+}
+
+static void pc87312_io_write(void *opaque, hwaddr addr, uint64_t val,
+                             unsigned int size)
+{
+    PC87312State *s = opaque;
+
+    trace_pc87312_io_write(addr, val);
+
+    if ((addr & 1) == 0) {
+        /* Index register */
+        s->read_id_step = 2;
+        s->selected_index = val;
+    } else {
+        /* Data register */
+        if (s->selected_index < 3) {
+            s->regs[s->selected_index] = val;
+            reconfigure_devices(s);
+        }
+    }
+}
+
+static uint64_t pc87312_io_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    PC87312State *s = opaque;
+    uint32_t val;
+
+    if ((addr & 1) == 0) {
+        /* Index register */
+        if (s->read_id_step++ == 0) {
+            val = 0x88;
+        } else if (s->read_id_step++ == 1) {
+            val = 0;
+        } else {
+            val = s->selected_index;
+        }
+    } else {
+        /* Data register */
+        if (s->selected_index < 3) {
+            val = s->regs[s->selected_index];
+        } else {
+            /* Invalid selected index */
+            val = 0;
+        }
+    }
+
+    trace_pc87312_io_read(addr, val);
+    return val;
+}
+
+static const MemoryRegionOps pc87312_io_ops = {
+    .read  = pc87312_io_read,
+    .write = pc87312_io_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static int pc87312_post_load(void *opaque, int version_id)
+{
+    PC87312State *s = opaque;
+
+    reconfigure_devices(s);
+    return 0;
+}
+
+static void pc87312_reset(DeviceState *d)
+{
+    PC87312State *s = PC87312(d);
+
+    pc87312_soft_reset(s);
+}
+
+static int pc87312_init(ISADevice *dev)
+{
+    PC87312State *s;
+    DeviceState *d;
+    ISADevice *isa;
+    ISABus *bus;
+    CharDriverState *chr;
+    DriveInfo *drive;
+    char name[5];
+    int i;
+
+    s = PC87312(dev);
+    bus = isa_bus_from_device(dev);
+    pc87312_hard_reset(s);
+    isa_register_ioport(dev, &s->io, s->iobase);
+
+    if (is_parallel_enabled(s)) {
+        chr = parallel_hds[0];
+        if (chr == NULL) {
+            chr = qemu_chr_new("par0", "null", NULL);
+        }
+        isa = isa_create(bus, "isa-parallel");
+        d = DEVICE(isa);
+        qdev_prop_set_uint32(d, "index", 0);
+        qdev_prop_set_uint32(d, "iobase", get_parallel_iobase(s));
+        qdev_prop_set_uint32(d, "irq", get_parallel_irq(s));
+        qdev_prop_set_chr(d, "chardev", chr);
+        qdev_init_nofail(d);
+        s->parallel.dev = isa;
+        trace_pc87312_info_parallel(get_parallel_iobase(s),
+                                    get_parallel_irq(s));
+    }
+
+    for (i = 0; i < 2; i++) {
+        if (is_uart_enabled(s, i)) {
+            chr = serial_hds[i];
+            if (chr == NULL) {
+                snprintf(name, sizeof(name), "ser%d", i);
+                chr = qemu_chr_new(name, "null", NULL);
+            }
+            isa = isa_create(bus, "isa-serial");
+            d = DEVICE(isa);
+            qdev_prop_set_uint32(d, "index", i);
+            qdev_prop_set_uint32(d, "iobase", get_uart_iobase(s, i));
+            qdev_prop_set_uint32(d, "irq", get_uart_irq(s, i));
+            qdev_prop_set_chr(d, "chardev", chr);
+            qdev_init_nofail(d);
+            s->uart[i].dev = isa;
+            trace_pc87312_info_serial(i, get_uart_iobase(s, i),
+                                      get_uart_irq(s, i));
+        }
+    }
+
+    if (is_fdc_enabled(s)) {
+        isa = isa_create(bus, "isa-fdc");
+        d = DEVICE(isa);
+        qdev_prop_set_uint32(d, "iobase", get_fdc_iobase(s));
+        qdev_prop_set_uint32(d, "irq", 6);
+        drive = drive_get(IF_FLOPPY, 0, 0);
+        if (drive != NULL) {
+            qdev_prop_set_drive_nofail(d, "driveA", drive->bdrv);
+        }
+        drive = drive_get(IF_FLOPPY, 0, 1);
+        if (drive != NULL) {
+            qdev_prop_set_drive_nofail(d, "driveB", drive->bdrv);
+        }
+        qdev_init_nofail(d);
+        s->fdc.dev = isa;
+        trace_pc87312_info_floppy(get_fdc_iobase(s));
+    }
+
+    if (is_ide_enabled(s)) {
+        isa = isa_create(bus, "isa-ide");
+        d = DEVICE(isa);
+        qdev_prop_set_uint32(d, "iobase", get_ide_iobase(s));
+        qdev_prop_set_uint32(d, "iobase2", get_ide_iobase(s) + 0x206);
+        qdev_prop_set_uint32(d, "irq", 14);
+        qdev_init_nofail(d);
+        s->ide.dev = isa;
+        trace_pc87312_info_ide(get_ide_iobase(s));
+    }
+
+    return 0;
+}
+
+static void pc87312_initfn(Object *obj)
+{
+    PC87312State *s = PC87312(obj);
+
+    memory_region_init_io(&s->io, &pc87312_io_ops, s, "pc87312", 2);
+}
+
+static const VMStateDescription vmstate_pc87312 = {
+    .name = "pc87312",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .post_load = pc87312_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(read_id_step, PC87312State),
+        VMSTATE_UINT8(selected_index, PC87312State),
+        VMSTATE_UINT8_ARRAY(regs, PC87312State, 3),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property pc87312_properties[] = {
+    DEFINE_PROP_HEX32("iobase", PC87312State, iobase, 0x398),
+    DEFINE_PROP_UINT8("config", PC87312State, config, 1),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void pc87312_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+
+    ic->init = pc87312_init;
+    dc->reset = pc87312_reset;
+    dc->vmsd = &vmstate_pc87312;
+    dc->props = pc87312_properties;
+}
+
+static const TypeInfo pc87312_type_info = {
+    .name          = TYPE_PC87312,
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(PC87312State),
+    .instance_init = pc87312_initfn,
+    .class_init    = pc87312_class_init,
+};
+
+static void pc87312_register_types(void)
+{
+    type_register_static(&pc87312_type_info);
+}
+
+type_init(pc87312_register_types)
diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c
new file mode 100644 (file)
index 0000000..d750413
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * QEMU PIIX4 PCI Bridge Emulation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "hw/pci/pci.h"
+#include "hw/isa/isa.h"
+#include "hw/sysbus.h"
+
+PCIDevice *piix4_dev;
+
+typedef struct PIIX4State {
+    PCIDevice dev;
+} PIIX4State;
+
+static void piix4_reset(void *opaque)
+{
+    PIIX4State *d = opaque;
+    uint8_t *pci_conf = d->dev.config;
+
+    pci_conf[0x04] = 0x07; // master, memory and I/O
+    pci_conf[0x05] = 0x00;
+    pci_conf[0x06] = 0x00;
+    pci_conf[0x07] = 0x02; // PCI_status_devsel_medium
+    pci_conf[0x4c] = 0x4d;
+    pci_conf[0x4e] = 0x03;
+    pci_conf[0x4f] = 0x00;
+    pci_conf[0x60] = 0x0a; // PCI A -> IRQ 10
+    pci_conf[0x61] = 0x0a; // PCI B -> IRQ 10
+    pci_conf[0x62] = 0x0b; // PCI C -> IRQ 11
+    pci_conf[0x63] = 0x0b; // PCI D -> IRQ 11
+    pci_conf[0x69] = 0x02;
+    pci_conf[0x70] = 0x80;
+    pci_conf[0x76] = 0x0c;
+    pci_conf[0x77] = 0x0c;
+    pci_conf[0x78] = 0x02;
+    pci_conf[0x79] = 0x00;
+    pci_conf[0x80] = 0x00;
+    pci_conf[0x82] = 0x00;
+    pci_conf[0xa0] = 0x08;
+    pci_conf[0xa2] = 0x00;
+    pci_conf[0xa3] = 0x00;
+    pci_conf[0xa4] = 0x00;
+    pci_conf[0xa5] = 0x00;
+    pci_conf[0xa6] = 0x00;
+    pci_conf[0xa7] = 0x00;
+    pci_conf[0xa8] = 0x0f;
+    pci_conf[0xaa] = 0x00;
+    pci_conf[0xab] = 0x00;
+    pci_conf[0xac] = 0x00;
+    pci_conf[0xae] = 0x00;
+}
+
+static const VMStateDescription vmstate_piix4 = {
+    .name = "PIIX4",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields      = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(dev, PIIX4State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int piix4_initfn(PCIDevice *dev)
+{
+    PIIX4State *d = DO_UPCAST(PIIX4State, dev, dev);
+
+    isa_bus_new(&d->dev.qdev, pci_address_space_io(dev));
+    piix4_dev = &d->dev;
+    qemu_register_reset(piix4_reset, d);
+    return 0;
+}
+
+int piix4_init(PCIBus *bus, ISABus **isa_bus, int devfn)
+{
+    PCIDevice *d;
+
+    d = pci_create_simple_multifunction(bus, devfn, true, "PIIX4");
+    *isa_bus = DO_UPCAST(ISABus, qbus, qdev_get_child_bus(&d->qdev, "isa.0"));
+    return d->devfn;
+}
+
+static void piix4_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->no_hotplug = 1;
+    k->init = piix4_initfn;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_82371AB_0;
+    k->class_id = PCI_CLASS_BRIDGE_ISA;
+    dc->desc = "ISA bridge";
+    dc->no_user = 1;
+    dc->vmsd = &vmstate_piix4;
+}
+
+static const TypeInfo piix4_info = {
+    .name          = "PIIX4",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PIIX4State),
+    .class_init    = piix4_class_init,
+};
+
+static void piix4_register_types(void)
+{
+    type_register_static(&piix4_info);
+}
+
+type_init(piix4_register_types)
diff --git a/hw/isa_mmio.c b/hw/isa_mmio.c
deleted file mode 100644 (file)
index d4dbf13..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Memory mapped access to ISA IO space.
- *
- * Copyright (c) 2006 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/hw.h"
-#include "hw/isa/isa.h"
-#include "exec/address-spaces.h"
-
-static void isa_mmio_writeb (void *opaque, hwaddr addr,
-                                  uint32_t val)
-{
-    cpu_outb(addr & IOPORTS_MASK, val);
-}
-
-static void isa_mmio_writew(void *opaque, hwaddr addr,
-                               uint32_t val)
-{
-    cpu_outw(addr & IOPORTS_MASK, val);
-}
-
-static void isa_mmio_writel(void *opaque, hwaddr addr,
-                               uint32_t val)
-{
-    cpu_outl(addr & IOPORTS_MASK, val);
-}
-
-static uint32_t isa_mmio_readb (void *opaque, hwaddr addr)
-{
-    return cpu_inb(addr & IOPORTS_MASK);
-}
-
-static uint32_t isa_mmio_readw(void *opaque, hwaddr addr)
-{
-    return cpu_inw(addr & IOPORTS_MASK);
-}
-
-static uint32_t isa_mmio_readl(void *opaque, hwaddr addr)
-{
-    return cpu_inl(addr & IOPORTS_MASK);
-}
-
-static const MemoryRegionOps isa_mmio_ops = {
-    .old_mmio = {
-        .write = { isa_mmio_writeb, isa_mmio_writew, isa_mmio_writel },
-        .read = { isa_mmio_readb, isa_mmio_readw, isa_mmio_readl, },
-    },
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-void isa_mmio_setup(MemoryRegion *mr, hwaddr size)
-{
-    memory_region_init_io(mr, &isa_mmio_ops, NULL, "isa-mmio", size);
-}
-
-void isa_mmio_init(hwaddr base, hwaddr size)
-{
-    MemoryRegion *mr = g_malloc(sizeof(*mr));
-
-    isa_mmio_setup(mr, size);
-    memory_region_add_subregion(get_system_memory(), base, mr);
-}
diff --git a/hw/jazz_led.c b/hw/jazz_led.c
deleted file mode 100644 (file)
index 05528c7..0000000
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * QEMU JAZZ LED emulator.
- *
- * Copyright (c) 2007-2012 Herve Poussineau
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "qemu-common.h"
-#include "ui/console.h"
-#include "ui/pixel_ops.h"
-#include "trace.h"
-#include "hw/sysbus.h"
-
-typedef enum {
-    REDRAW_NONE = 0, REDRAW_SEGMENTS = 1, REDRAW_BACKGROUND = 2,
-} screen_state_t;
-
-typedef struct LedState {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    uint8_t segments;
-    QemuConsole *con;
-    screen_state_t state;
-} LedState;
-
-static uint64_t jazz_led_read(void *opaque, hwaddr addr,
-                              unsigned int size)
-{
-    LedState *s = opaque;
-    uint8_t val;
-
-    val = s->segments;
-    trace_jazz_led_read(addr, val);
-
-    return val;
-}
-
-static void jazz_led_write(void *opaque, hwaddr addr,
-                           uint64_t val, unsigned int size)
-{
-    LedState *s = opaque;
-    uint8_t new_val = val & 0xff;
-
-    trace_jazz_led_write(addr, new_val);
-
-    s->segments = new_val;
-    s->state |= REDRAW_SEGMENTS;
-}
-
-static const MemoryRegionOps led_ops = {
-    .read = jazz_led_read,
-    .write = jazz_led_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .impl.min_access_size = 1,
-    .impl.max_access_size = 1,
-};
-
-/***********************************************************/
-/* jazz_led display */
-
-static void draw_horizontal_line(DisplaySurface *ds,
-                                 int posy, int posx1, int posx2,
-                                 uint32_t color)
-{
-    uint8_t *d;
-    int x, bpp;
-
-    bpp = (surface_bits_per_pixel(ds) + 7) >> 3;
-    d = surface_data(ds) + surface_stride(ds) * posy + bpp * posx1;
-    switch(bpp) {
-        case 1:
-            for (x = posx1; x <= posx2; x++) {
-                *((uint8_t *)d) = color;
-                d++;
-            }
-            break;
-        case 2:
-            for (x = posx1; x <= posx2; x++) {
-                *((uint16_t *)d) = color;
-                d += 2;
-            }
-            break;
-        case 4:
-            for (x = posx1; x <= posx2; x++) {
-                *((uint32_t *)d) = color;
-                d += 4;
-            }
-            break;
-    }
-}
-
-static void draw_vertical_line(DisplaySurface *ds,
-                               int posx, int posy1, int posy2,
-                               uint32_t color)
-{
-    uint8_t *d;
-    int y, bpp;
-
-    bpp = (surface_bits_per_pixel(ds) + 7) >> 3;
-    d = surface_data(ds) + surface_stride(ds) * posy1 + bpp * posx;
-    switch(bpp) {
-        case 1:
-            for (y = posy1; y <= posy2; y++) {
-                *((uint8_t *)d) = color;
-                d += surface_stride(ds);
-            }
-            break;
-        case 2:
-            for (y = posy1; y <= posy2; y++) {
-                *((uint16_t *)d) = color;
-                d += surface_stride(ds);
-            }
-            break;
-        case 4:
-            for (y = posy1; y <= posy2; y++) {
-                *((uint32_t *)d) = color;
-                d += surface_stride(ds);
-            }
-            break;
-    }
-}
-
-static void jazz_led_update_display(void *opaque)
-{
-    LedState *s = opaque;
-    DisplaySurface *surface = qemu_console_surface(s->con);
-    uint8_t *d1;
-    uint32_t color_segment, color_led;
-    int y, bpp;
-
-    if (s->state & REDRAW_BACKGROUND) {
-        /* clear screen */
-        bpp = (surface_bits_per_pixel(surface) + 7) >> 3;
-        d1 = surface_data(surface);
-        for (y = 0; y < surface_height(surface); y++) {
-            memset(d1, 0x00, surface_width(surface) * bpp);
-            d1 += surface_stride(surface);
-        }
-    }
-
-    if (s->state & REDRAW_SEGMENTS) {
-        /* set colors according to bpp */
-        switch (surface_bits_per_pixel(surface)) {
-            case 8:
-                color_segment = rgb_to_pixel8(0xaa, 0xaa, 0xaa);
-                color_led = rgb_to_pixel8(0x00, 0xff, 0x00);
-                break;
-            case 15:
-                color_segment = rgb_to_pixel15(0xaa, 0xaa, 0xaa);
-                color_led = rgb_to_pixel15(0x00, 0xff, 0x00);
-                break;
-            case 16:
-                color_segment = rgb_to_pixel16(0xaa, 0xaa, 0xaa);
-                color_led = rgb_to_pixel16(0x00, 0xff, 0x00);
-            case 24:
-                color_segment = rgb_to_pixel24(0xaa, 0xaa, 0xaa);
-                color_led = rgb_to_pixel24(0x00, 0xff, 0x00);
-                break;
-            case 32:
-                color_segment = rgb_to_pixel32(0xaa, 0xaa, 0xaa);
-                color_led = rgb_to_pixel32(0x00, 0xff, 0x00);
-                break;
-            default:
-                return;
-        }
-
-        /* display segments */
-        draw_horizontal_line(surface, 40, 10, 40,
-                             (s->segments & 0x02) ? color_segment : 0);
-        draw_vertical_line(surface, 10, 10, 40,
-                           (s->segments & 0x04) ? color_segment : 0);
-        draw_vertical_line(surface, 10, 40, 70,
-                           (s->segments & 0x08) ? color_segment : 0);
-        draw_horizontal_line(surface, 70, 10, 40,
-                             (s->segments & 0x10) ? color_segment : 0);
-        draw_vertical_line(surface, 40, 40, 70,
-                           (s->segments & 0x20) ? color_segment : 0);
-        draw_vertical_line(surface, 40, 10, 40,
-                           (s->segments & 0x40) ? color_segment : 0);
-        draw_horizontal_line(surface, 10, 10, 40,
-                             (s->segments & 0x80) ? color_segment : 0);
-
-        /* display led */
-        if (!(s->segments & 0x01))
-            color_led = 0; /* black */
-        draw_horizontal_line(surface, 68, 50, 50, color_led);
-        draw_horizontal_line(surface, 69, 49, 51, color_led);
-        draw_horizontal_line(surface, 70, 48, 52, color_led);
-        draw_horizontal_line(surface, 71, 49, 51, color_led);
-        draw_horizontal_line(surface, 72, 50, 50, color_led);
-    }
-
-    s->state = REDRAW_NONE;
-    dpy_gfx_update(s->con, 0, 0,
-                   surface_width(surface), surface_height(surface));
-}
-
-static void jazz_led_invalidate_display(void *opaque)
-{
-    LedState *s = opaque;
-    s->state |= REDRAW_SEGMENTS | REDRAW_BACKGROUND;
-}
-
-static void jazz_led_text_update(void *opaque, console_ch_t *chardata)
-{
-    LedState *s = opaque;
-    char buf[2];
-
-    dpy_text_cursor(s->con, -1, -1);
-    qemu_console_resize(s->con, 2, 1);
-
-    /* TODO: draw the segments */
-    snprintf(buf, 2, "%02hhx\n", s->segments);
-    console_write_ch(chardata++, 0x00200100 | buf[0]);
-    console_write_ch(chardata++, 0x00200100 | buf[1]);
-
-    dpy_text_update(s->con, 0, 0, 2, 1);
-}
-
-static int jazz_led_post_load(void *opaque, int version_id)
-{
-    /* force refresh */
-    jazz_led_invalidate_display(opaque);
-
-    return 0;
-}
-
-static const VMStateDescription vmstate_jazz_led = {
-    .name = "jazz-led",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .post_load = jazz_led_post_load,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT8(segments, LedState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int jazz_led_init(SysBusDevice *dev)
-{
-    LedState *s = FROM_SYSBUS(LedState, dev);
-
-    memory_region_init_io(&s->iomem, &led_ops, s, "led", 1);
-    sysbus_init_mmio(dev, &s->iomem);
-
-    s->con = graphic_console_init(jazz_led_update_display,
-                                  jazz_led_invalidate_display,
-                                  NULL,
-                                  jazz_led_text_update, s);
-
-    return 0;
-}
-
-static void jazz_led_reset(DeviceState *d)
-{
-    LedState *s = DO_UPCAST(LedState, busdev.qdev, d);
-
-    s->segments = 0;
-    s->state = REDRAW_SEGMENTS | REDRAW_BACKGROUND;
-    qemu_console_resize(s->con, 60, 80);
-}
-
-static void jazz_led_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = jazz_led_init;
-    dc->desc = "Jazz LED display",
-    dc->vmsd = &vmstate_jazz_led;
-    dc->reset = jazz_led_reset;
-}
-
-static const TypeInfo jazz_led_info = {
-    .name          = "jazz-led",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(LedState),
-    .class_init    = jazz_led_class_init,
-};
-
-static void jazz_led_register(void)
-{
-    type_register_static(&jazz_led_info);
-}
-
-type_init(jazz_led_register);
diff --git a/hw/lan9118.c b/hw/lan9118.c
deleted file mode 100644 (file)
index 04cf267..0000000
+++ /dev/null
@@ -1,1399 +0,0 @@
-/*
- * SMSC LAN9118 Ethernet interface emulation
- *
- * Copyright (c) 2009 CodeSourcery, LLC.
- * Written by Paul Brook
- *
- * This code is licensed under the GNU GPL v2
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "hw/sysbus.h"
-#include "net/net.h"
-#include "hw/arm/devices.h"
-#include "sysemu/sysemu.h"
-#include "hw/ptimer.h"
-/* For crc32 */
-#include <zlib.h>
-
-//#define DEBUG_LAN9118
-
-#ifdef DEBUG_LAN9118
-#define DPRINTF(fmt, ...) \
-do { printf("lan9118: " fmt , ## __VA_ARGS__); } while (0)
-#define BADF(fmt, ...) \
-do { hw_error("lan9118: error: " fmt , ## __VA_ARGS__);} while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0)
-#endif
-
-#define CSR_ID_REV      0x50
-#define CSR_IRQ_CFG     0x54
-#define CSR_INT_STS     0x58
-#define CSR_INT_EN      0x5c
-#define CSR_BYTE_TEST   0x64
-#define CSR_FIFO_INT    0x68
-#define CSR_RX_CFG      0x6c
-#define CSR_TX_CFG      0x70
-#define CSR_HW_CFG      0x74
-#define CSR_RX_DP_CTRL  0x78
-#define CSR_RX_FIFO_INF 0x7c
-#define CSR_TX_FIFO_INF 0x80
-#define CSR_PMT_CTRL    0x84
-#define CSR_GPIO_CFG    0x88
-#define CSR_GPT_CFG     0x8c
-#define CSR_GPT_CNT     0x90
-#define CSR_WORD_SWAP   0x98
-#define CSR_FREE_RUN    0x9c
-#define CSR_RX_DROP     0xa0
-#define CSR_MAC_CSR_CMD 0xa4
-#define CSR_MAC_CSR_DATA 0xa8
-#define CSR_AFC_CFG     0xac
-#define CSR_E2P_CMD     0xb0
-#define CSR_E2P_DATA    0xb4
-
-/* IRQ_CFG */
-#define IRQ_INT         0x00001000
-#define IRQ_EN          0x00000100
-#define IRQ_POL         0x00000010
-#define IRQ_TYPE        0x00000001
-
-/* INT_STS/INT_EN */
-#define SW_INT          0x80000000
-#define TXSTOP_INT      0x02000000
-#define RXSTOP_INT      0x01000000
-#define RXDFH_INT       0x00800000
-#define TX_IOC_INT      0x00200000
-#define RXD_INT         0x00100000
-#define GPT_INT         0x00080000
-#define PHY_INT         0x00040000
-#define PME_INT         0x00020000
-#define TXSO_INT        0x00010000
-#define RWT_INT         0x00008000
-#define RXE_INT         0x00004000
-#define TXE_INT         0x00002000
-#define TDFU_INT        0x00000800
-#define TDFO_INT        0x00000400
-#define TDFA_INT        0x00000200
-#define TSFF_INT        0x00000100
-#define TSFL_INT        0x00000080
-#define RXDF_INT        0x00000040
-#define RDFL_INT        0x00000020
-#define RSFF_INT        0x00000010
-#define RSFL_INT        0x00000008
-#define GPIO2_INT       0x00000004
-#define GPIO1_INT       0x00000002
-#define GPIO0_INT       0x00000001
-#define RESERVED_INT    0x7c001000
-
-#define MAC_CR          1
-#define MAC_ADDRH       2
-#define MAC_ADDRL       3
-#define MAC_HASHH       4
-#define MAC_HASHL       5
-#define MAC_MII_ACC     6
-#define MAC_MII_DATA    7
-#define MAC_FLOW        8
-#define MAC_VLAN1       9 /* TODO */
-#define MAC_VLAN2       10 /* TODO */
-#define MAC_WUFF        11 /* TODO */
-#define MAC_WUCSR       12 /* TODO */
-
-#define MAC_CR_RXALL    0x80000000
-#define MAC_CR_RCVOWN   0x00800000
-#define MAC_CR_LOOPBK   0x00200000
-#define MAC_CR_FDPX     0x00100000
-#define MAC_CR_MCPAS    0x00080000
-#define MAC_CR_PRMS     0x00040000
-#define MAC_CR_INVFILT  0x00020000
-#define MAC_CR_PASSBAD  0x00010000
-#define MAC_CR_HO       0x00008000
-#define MAC_CR_HPFILT   0x00002000
-#define MAC_CR_LCOLL    0x00001000
-#define MAC_CR_BCAST    0x00000800
-#define MAC_CR_DISRTY   0x00000400
-#define MAC_CR_PADSTR   0x00000100
-#define MAC_CR_BOLMT    0x000000c0
-#define MAC_CR_DFCHK    0x00000020
-#define MAC_CR_TXEN     0x00000008
-#define MAC_CR_RXEN     0x00000004
-#define MAC_CR_RESERVED 0x7f404213
-
-#define PHY_INT_ENERGYON            0x80
-#define PHY_INT_AUTONEG_COMPLETE    0x40
-#define PHY_INT_FAULT               0x20
-#define PHY_INT_DOWN                0x10
-#define PHY_INT_AUTONEG_LP          0x08
-#define PHY_INT_PARFAULT            0x04
-#define PHY_INT_AUTONEG_PAGE        0x02
-
-#define GPT_TIMER_EN    0x20000000
-
-enum tx_state {
-    TX_IDLE,
-    TX_B,
-    TX_DATA
-};
-
-typedef struct {
-    /* state is a tx_state but we can't put enums in VMStateDescriptions. */
-    uint32_t state;
-    uint32_t cmd_a;
-    uint32_t cmd_b;
-    int32_t buffer_size;
-    int32_t offset;
-    int32_t pad;
-    int32_t fifo_used;
-    int32_t len;
-    uint8_t data[2048];
-} LAN9118Packet;
-
-static const VMStateDescription vmstate_lan9118_packet = {
-    .name = "lan9118_packet",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32(state, LAN9118Packet),
-        VMSTATE_UINT32(cmd_a, LAN9118Packet),
-        VMSTATE_UINT32(cmd_b, LAN9118Packet),
-        VMSTATE_INT32(buffer_size, LAN9118Packet),
-        VMSTATE_INT32(offset, LAN9118Packet),
-        VMSTATE_INT32(pad, LAN9118Packet),
-        VMSTATE_INT32(fifo_used, LAN9118Packet),
-        VMSTATE_INT32(len, LAN9118Packet),
-        VMSTATE_UINT8_ARRAY(data, LAN9118Packet, 2048),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-typedef struct {
-    SysBusDevice busdev;
-    NICState *nic;
-    NICConf conf;
-    qemu_irq irq;
-    MemoryRegion mmio;
-    ptimer_state *timer;
-
-    uint32_t irq_cfg;
-    uint32_t int_sts;
-    uint32_t int_en;
-    uint32_t fifo_int;
-    uint32_t rx_cfg;
-    uint32_t tx_cfg;
-    uint32_t hw_cfg;
-    uint32_t pmt_ctrl;
-    uint32_t gpio_cfg;
-    uint32_t gpt_cfg;
-    uint32_t word_swap;
-    uint32_t free_timer_start;
-    uint32_t mac_cmd;
-    uint32_t mac_data;
-    uint32_t afc_cfg;
-    uint32_t e2p_cmd;
-    uint32_t e2p_data;
-
-    uint32_t mac_cr;
-    uint32_t mac_hashh;
-    uint32_t mac_hashl;
-    uint32_t mac_mii_acc;
-    uint32_t mac_mii_data;
-    uint32_t mac_flow;
-
-    uint32_t phy_status;
-    uint32_t phy_control;
-    uint32_t phy_advertise;
-    uint32_t phy_int;
-    uint32_t phy_int_mask;
-
-    int32_t eeprom_writable;
-    uint8_t eeprom[128];
-
-    int32_t tx_fifo_size;
-    LAN9118Packet *txp;
-    LAN9118Packet tx_packet;
-
-    int32_t tx_status_fifo_used;
-    int32_t tx_status_fifo_head;
-    uint32_t tx_status_fifo[512];
-
-    int32_t rx_status_fifo_size;
-    int32_t rx_status_fifo_used;
-    int32_t rx_status_fifo_head;
-    uint32_t rx_status_fifo[896];
-    int32_t rx_fifo_size;
-    int32_t rx_fifo_used;
-    int32_t rx_fifo_head;
-    uint32_t rx_fifo[3360];
-    int32_t rx_packet_size_head;
-    int32_t rx_packet_size_tail;
-    int32_t rx_packet_size[1024];
-
-    int32_t rxp_offset;
-    int32_t rxp_size;
-    int32_t rxp_pad;
-
-    uint32_t write_word_prev_offset;
-    uint32_t write_word_n;
-    uint16_t write_word_l;
-    uint16_t write_word_h;
-    uint32_t read_word_prev_offset;
-    uint32_t read_word_n;
-    uint32_t read_long;
-
-    uint32_t mode_16bit;
-} lan9118_state;
-
-static const VMStateDescription vmstate_lan9118 = {
-    .name = "lan9118",
-    .version_id = 2,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_PTIMER(timer, lan9118_state),
-        VMSTATE_UINT32(irq_cfg, lan9118_state),
-        VMSTATE_UINT32(int_sts, lan9118_state),
-        VMSTATE_UINT32(int_en, lan9118_state),
-        VMSTATE_UINT32(fifo_int, lan9118_state),
-        VMSTATE_UINT32(rx_cfg, lan9118_state),
-        VMSTATE_UINT32(tx_cfg, lan9118_state),
-        VMSTATE_UINT32(hw_cfg, lan9118_state),
-        VMSTATE_UINT32(pmt_ctrl, lan9118_state),
-        VMSTATE_UINT32(gpio_cfg, lan9118_state),
-        VMSTATE_UINT32(gpt_cfg, lan9118_state),
-        VMSTATE_UINT32(word_swap, lan9118_state),
-        VMSTATE_UINT32(free_timer_start, lan9118_state),
-        VMSTATE_UINT32(mac_cmd, lan9118_state),
-        VMSTATE_UINT32(mac_data, lan9118_state),
-        VMSTATE_UINT32(afc_cfg, lan9118_state),
-        VMSTATE_UINT32(e2p_cmd, lan9118_state),
-        VMSTATE_UINT32(e2p_data, lan9118_state),
-        VMSTATE_UINT32(mac_cr, lan9118_state),
-        VMSTATE_UINT32(mac_hashh, lan9118_state),
-        VMSTATE_UINT32(mac_hashl, lan9118_state),
-        VMSTATE_UINT32(mac_mii_acc, lan9118_state),
-        VMSTATE_UINT32(mac_mii_data, lan9118_state),
-        VMSTATE_UINT32(mac_flow, lan9118_state),
-        VMSTATE_UINT32(phy_status, lan9118_state),
-        VMSTATE_UINT32(phy_control, lan9118_state),
-        VMSTATE_UINT32(phy_advertise, lan9118_state),
-        VMSTATE_UINT32(phy_int, lan9118_state),
-        VMSTATE_UINT32(phy_int_mask, lan9118_state),
-        VMSTATE_INT32(eeprom_writable, lan9118_state),
-        VMSTATE_UINT8_ARRAY(eeprom, lan9118_state, 128),
-        VMSTATE_INT32(tx_fifo_size, lan9118_state),
-        /* txp always points at tx_packet so need not be saved */
-        VMSTATE_STRUCT(tx_packet, lan9118_state, 0,
-                       vmstate_lan9118_packet, LAN9118Packet),
-        VMSTATE_INT32(tx_status_fifo_used, lan9118_state),
-        VMSTATE_INT32(tx_status_fifo_head, lan9118_state),
-        VMSTATE_UINT32_ARRAY(tx_status_fifo, lan9118_state, 512),
-        VMSTATE_INT32(rx_status_fifo_size, lan9118_state),
-        VMSTATE_INT32(rx_status_fifo_used, lan9118_state),
-        VMSTATE_INT32(rx_status_fifo_head, lan9118_state),
-        VMSTATE_UINT32_ARRAY(rx_status_fifo, lan9118_state, 896),
-        VMSTATE_INT32(rx_fifo_size, lan9118_state),
-        VMSTATE_INT32(rx_fifo_used, lan9118_state),
-        VMSTATE_INT32(rx_fifo_head, lan9118_state),
-        VMSTATE_UINT32_ARRAY(rx_fifo, lan9118_state, 3360),
-        VMSTATE_INT32(rx_packet_size_head, lan9118_state),
-        VMSTATE_INT32(rx_packet_size_tail, lan9118_state),
-        VMSTATE_INT32_ARRAY(rx_packet_size, lan9118_state, 1024),
-        VMSTATE_INT32(rxp_offset, lan9118_state),
-        VMSTATE_INT32(rxp_size, lan9118_state),
-        VMSTATE_INT32(rxp_pad, lan9118_state),
-        VMSTATE_UINT32_V(write_word_prev_offset, lan9118_state, 2),
-        VMSTATE_UINT32_V(write_word_n, lan9118_state, 2),
-        VMSTATE_UINT16_V(write_word_l, lan9118_state, 2),
-        VMSTATE_UINT16_V(write_word_h, lan9118_state, 2),
-        VMSTATE_UINT32_V(read_word_prev_offset, lan9118_state, 2),
-        VMSTATE_UINT32_V(read_word_n, lan9118_state, 2),
-        VMSTATE_UINT32_V(read_long, lan9118_state, 2),
-        VMSTATE_UINT32_V(mode_16bit, lan9118_state, 2),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void lan9118_update(lan9118_state *s)
-{
-    int level;
-
-    /* TODO: Implement FIFO level IRQs.  */
-    level = (s->int_sts & s->int_en) != 0;
-    if (level) {
-        s->irq_cfg |= IRQ_INT;
-    } else {
-        s->irq_cfg &= ~IRQ_INT;
-    }
-    if ((s->irq_cfg & IRQ_EN) == 0) {
-        level = 0;
-    }
-    if ((s->irq_cfg & (IRQ_TYPE | IRQ_POL)) != (IRQ_TYPE | IRQ_POL)) {
-        /* Interrupt is active low unless we're configured as
-         * active-high polarity, push-pull type.
-         */
-        level = !level;
-    }
-    qemu_set_irq(s->irq, level);
-}
-
-static void lan9118_mac_changed(lan9118_state *s)
-{
-    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
-}
-
-static void lan9118_reload_eeprom(lan9118_state *s)
-{
-    int i;
-    if (s->eeprom[0] != 0xa5) {
-        s->e2p_cmd &= ~0x10;
-        DPRINTF("MACADDR load failed\n");
-        return;
-    }
-    for (i = 0; i < 6; i++) {
-        s->conf.macaddr.a[i] = s->eeprom[i + 1];
-    }
-    s->e2p_cmd |= 0x10;
-    DPRINTF("MACADDR loaded from eeprom\n");
-    lan9118_mac_changed(s);
-}
-
-static void phy_update_irq(lan9118_state *s)
-{
-    if (s->phy_int & s->phy_int_mask) {
-        s->int_sts |= PHY_INT;
-    } else {
-        s->int_sts &= ~PHY_INT;
-    }
-    lan9118_update(s);
-}
-
-static void phy_update_link(lan9118_state *s)
-{
-    /* Autonegotiation status mirrors link status.  */
-    if (qemu_get_queue(s->nic)->link_down) {
-        s->phy_status &= ~0x0024;
-        s->phy_int |= PHY_INT_DOWN;
-    } else {
-        s->phy_status |= 0x0024;
-        s->phy_int |= PHY_INT_ENERGYON;
-        s->phy_int |= PHY_INT_AUTONEG_COMPLETE;
-    }
-    phy_update_irq(s);
-}
-
-static void lan9118_set_link(NetClientState *nc)
-{
-    phy_update_link(qemu_get_nic_opaque(nc));
-}
-
-static void phy_reset(lan9118_state *s)
-{
-    s->phy_status = 0x7809;
-    s->phy_control = 0x3000;
-    s->phy_advertise = 0x01e1;
-    s->phy_int_mask = 0;
-    s->phy_int = 0;
-    phy_update_link(s);
-}
-
-static void lan9118_reset(DeviceState *d)
-{
-    lan9118_state *s = FROM_SYSBUS(lan9118_state, SYS_BUS_DEVICE(d));
-    s->irq_cfg &= (IRQ_TYPE | IRQ_POL);
-    s->int_sts = 0;
-    s->int_en = 0;
-    s->fifo_int = 0x48000000;
-    s->rx_cfg = 0;
-    s->tx_cfg = 0;
-    s->hw_cfg = s->mode_16bit ? 0x00050000 : 0x00050004;
-    s->pmt_ctrl &= 0x45;
-    s->gpio_cfg = 0;
-    s->txp->fifo_used = 0;
-    s->txp->state = TX_IDLE;
-    s->txp->cmd_a = 0xffffffffu;
-    s->txp->cmd_b = 0xffffffffu;
-    s->txp->len = 0;
-    s->txp->fifo_used = 0;
-    s->tx_fifo_size = 4608;
-    s->tx_status_fifo_used = 0;
-    s->rx_status_fifo_size = 704;
-    s->rx_fifo_size = 2640;
-    s->rx_fifo_used = 0;
-    s->rx_status_fifo_size = 176;
-    s->rx_status_fifo_used = 0;
-    s->rxp_offset = 0;
-    s->rxp_size = 0;
-    s->rxp_pad = 0;
-    s->rx_packet_size_tail = s->rx_packet_size_head;
-    s->rx_packet_size[s->rx_packet_size_head] = 0;
-    s->mac_cmd = 0;
-    s->mac_data = 0;
-    s->afc_cfg = 0;
-    s->e2p_cmd = 0;
-    s->e2p_data = 0;
-    s->free_timer_start = qemu_get_clock_ns(vm_clock) / 40;
-
-    ptimer_stop(s->timer);
-    ptimer_set_count(s->timer, 0xffff);
-    s->gpt_cfg = 0xffff;
-
-    s->mac_cr = MAC_CR_PRMS;
-    s->mac_hashh = 0;
-    s->mac_hashl = 0;
-    s->mac_mii_acc = 0;
-    s->mac_mii_data = 0;
-    s->mac_flow = 0;
-
-    s->read_word_n = 0;
-    s->write_word_n = 0;
-
-    phy_reset(s);
-
-    s->eeprom_writable = 0;
-    lan9118_reload_eeprom(s);
-}
-
-static int lan9118_can_receive(NetClientState *nc)
-{
-    return 1;
-}
-
-static void rx_fifo_push(lan9118_state *s, uint32_t val)
-{
-    int fifo_pos;
-    fifo_pos = s->rx_fifo_head + s->rx_fifo_used;
-    if (fifo_pos >= s->rx_fifo_size)
-      fifo_pos -= s->rx_fifo_size;
-    s->rx_fifo[fifo_pos] = val;
-    s->rx_fifo_used++;
-}
-
-/* Return nonzero if the packet is accepted by the filter.  */
-static int lan9118_filter(lan9118_state *s, const uint8_t *addr)
-{
-    int multicast;
-    uint32_t hash;
-
-    if (s->mac_cr & MAC_CR_PRMS) {
-        return 1;
-    }
-    if (addr[0] == 0xff && addr[1] == 0xff && addr[2] == 0xff &&
-        addr[3] == 0xff && addr[4] == 0xff && addr[5] == 0xff) {
-        return (s->mac_cr & MAC_CR_BCAST) == 0;
-    }
-
-    multicast = addr[0] & 1;
-    if (multicast &&s->mac_cr & MAC_CR_MCPAS) {
-        return 1;
-    }
-    if (multicast ? (s->mac_cr & MAC_CR_HPFILT) == 0
-                  : (s->mac_cr & MAC_CR_HO) == 0) {
-        /* Exact matching.  */
-        hash = memcmp(addr, s->conf.macaddr.a, 6);
-        if (s->mac_cr & MAC_CR_INVFILT) {
-            return hash != 0;
-        } else {
-            return hash == 0;
-        }
-    } else {
-        /* Hash matching  */
-        hash = compute_mcast_idx(addr);
-        if (hash & 0x20) {
-            return (s->mac_hashh >> (hash & 0x1f)) & 1;
-        } else {
-            return (s->mac_hashl >> (hash & 0x1f)) & 1;
-        }
-    }
-}
-
-static ssize_t lan9118_receive(NetClientState *nc, const uint8_t *buf,
-                               size_t size)
-{
-    lan9118_state *s = qemu_get_nic_opaque(nc);
-    int fifo_len;
-    int offset;
-    int src_pos;
-    int n;
-    int filter;
-    uint32_t val;
-    uint32_t crc;
-    uint32_t status;
-
-    if ((s->mac_cr & MAC_CR_RXEN) == 0) {
-        return -1;
-    }
-
-    if (size >= 2048 || size < 14) {
-        return -1;
-    }
-
-    /* TODO: Implement FIFO overflow notification.  */
-    if (s->rx_status_fifo_used == s->rx_status_fifo_size) {
-        return -1;
-    }
-
-    filter = lan9118_filter(s, buf);
-    if (!filter && (s->mac_cr & MAC_CR_RXALL) == 0) {
-        return size;
-    }
-
-    offset = (s->rx_cfg >> 8) & 0x1f;
-    n = offset & 3;
-    fifo_len = (size + n + 3) >> 2;
-    /* Add a word for the CRC.  */
-    fifo_len++;
-    if (s->rx_fifo_size - s->rx_fifo_used < fifo_len) {
-        return -1;
-    }
-
-    DPRINTF("Got packet len:%d fifo:%d filter:%s\n",
-            (int)size, fifo_len, filter ? "pass" : "fail");
-    val = 0;
-    crc = bswap32(crc32(~0, buf, size));
-    for (src_pos = 0; src_pos < size; src_pos++) {
-        val = (val >> 8) | ((uint32_t)buf[src_pos] << 24);
-        n++;
-        if (n == 4) {
-            n = 0;
-            rx_fifo_push(s, val);
-            val = 0;
-        }
-    }
-    if (n) {
-        val >>= ((4 - n) * 8);
-        val |= crc << (n * 8);
-        rx_fifo_push(s, val);
-        val = crc >> ((4 - n) * 8);
-        rx_fifo_push(s, val);
-    } else {
-        rx_fifo_push(s, crc);
-    }
-    n = s->rx_status_fifo_head + s->rx_status_fifo_used;
-    if (n >= s->rx_status_fifo_size) {
-        n -= s->rx_status_fifo_size;
-    }
-    s->rx_packet_size[s->rx_packet_size_tail] = fifo_len;
-    s->rx_packet_size_tail = (s->rx_packet_size_tail + 1023) & 1023;
-    s->rx_status_fifo_used++;
-
-    status = (size + 4) << 16;
-    if (buf[0] == 0xff && buf[1] == 0xff && buf[2] == 0xff &&
-        buf[3] == 0xff && buf[4] == 0xff && buf[5] == 0xff) {
-        status |= 0x00002000;
-    } else if (buf[0] & 1) {
-        status |= 0x00000400;
-    }
-    if (!filter) {
-        status |= 0x40000000;
-    }
-    s->rx_status_fifo[n] = status;
-
-    if (s->rx_status_fifo_used > (s->fifo_int & 0xff)) {
-        s->int_sts |= RSFL_INT;
-    }
-    lan9118_update(s);
-
-    return size;
-}
-
-static uint32_t rx_fifo_pop(lan9118_state *s)
-{
-    int n;
-    uint32_t val;
-
-    if (s->rxp_size == 0 && s->rxp_pad == 0) {
-        s->rxp_size = s->rx_packet_size[s->rx_packet_size_head];
-        s->rx_packet_size[s->rx_packet_size_head] = 0;
-        if (s->rxp_size != 0) {
-            s->rx_packet_size_head = (s->rx_packet_size_head + 1023) & 1023;
-            s->rxp_offset = (s->rx_cfg >> 10) & 7;
-            n = s->rxp_offset + s->rxp_size;
-            switch (s->rx_cfg >> 30) {
-            case 1:
-                n = (-n) & 3;
-                break;
-            case 2:
-                n = (-n) & 7;
-                break;
-            default:
-                n = 0;
-                break;
-            }
-            s->rxp_pad = n;
-            DPRINTF("Pop packet size:%d offset:%d pad: %d\n",
-                    s->rxp_size, s->rxp_offset, s->rxp_pad);
-        }
-    }
-    if (s->rxp_offset > 0) {
-        s->rxp_offset--;
-        val = 0;
-    } else if (s->rxp_size > 0) {
-        s->rxp_size--;
-        val = s->rx_fifo[s->rx_fifo_head++];
-        if (s->rx_fifo_head >= s->rx_fifo_size) {
-            s->rx_fifo_head -= s->rx_fifo_size;
-        }
-        s->rx_fifo_used--;
-    } else if (s->rxp_pad > 0) {
-        s->rxp_pad--;
-        val =  0;
-    } else {
-        DPRINTF("RX underflow\n");
-        s->int_sts |= RXE_INT;
-        val =  0;
-    }
-    lan9118_update(s);
-    return val;
-}
-
-static void do_tx_packet(lan9118_state *s)
-{
-    int n;
-    uint32_t status;
-
-    /* FIXME: Honor TX disable, and allow queueing of packets.  */
-    if (s->phy_control & 0x4000)  {
-        /* This assumes the receive routine doesn't touch the VLANClient.  */
-        lan9118_receive(qemu_get_queue(s->nic), s->txp->data, s->txp->len);
-    } else {
-        qemu_send_packet(qemu_get_queue(s->nic), s->txp->data, s->txp->len);
-    }
-    s->txp->fifo_used = 0;
-
-    if (s->tx_status_fifo_used == 512) {
-        /* Status FIFO full */
-        return;
-    }
-    /* Add entry to status FIFO.  */
-    status = s->txp->cmd_b & 0xffff0000u;
-    DPRINTF("Sent packet tag:%04x len %d\n", status >> 16, s->txp->len);
-    n = (s->tx_status_fifo_head + s->tx_status_fifo_used) & 511;
-    s->tx_status_fifo[n] = status;
-    s->tx_status_fifo_used++;
-    if (s->tx_status_fifo_used == 512) {
-        s->int_sts |= TSFF_INT;
-        /* TODO: Stop transmission.  */
-    }
-}
-
-static uint32_t rx_status_fifo_pop(lan9118_state *s)
-{
-    uint32_t val;
-
-    val = s->rx_status_fifo[s->rx_status_fifo_head];
-    if (s->rx_status_fifo_used != 0) {
-        s->rx_status_fifo_used--;
-        s->rx_status_fifo_head++;
-        if (s->rx_status_fifo_head >= s->rx_status_fifo_size) {
-            s->rx_status_fifo_head -= s->rx_status_fifo_size;
-        }
-        /* ??? What value should be returned when the FIFO is empty?  */
-        DPRINTF("RX status pop 0x%08x\n", val);
-    }
-    return val;
-}
-
-static uint32_t tx_status_fifo_pop(lan9118_state *s)
-{
-    uint32_t val;
-
-    val = s->tx_status_fifo[s->tx_status_fifo_head];
-    if (s->tx_status_fifo_used != 0) {
-        s->tx_status_fifo_used--;
-        s->tx_status_fifo_head = (s->tx_status_fifo_head + 1) & 511;
-        /* ??? What value should be returned when the FIFO is empty?  */
-    }
-    return val;
-}
-
-static void tx_fifo_push(lan9118_state *s, uint32_t val)
-{
-    int n;
-
-    if (s->txp->fifo_used == s->tx_fifo_size) {
-        s->int_sts |= TDFO_INT;
-        return;
-    }
-    switch (s->txp->state) {
-    case TX_IDLE:
-        s->txp->cmd_a = val & 0x831f37ff;
-        s->txp->fifo_used++;
-        s->txp->state = TX_B;
-        break;
-    case TX_B:
-        if (s->txp->cmd_a & 0x2000) {
-            /* First segment */
-            s->txp->cmd_b = val;
-            s->txp->fifo_used++;
-            s->txp->buffer_size = s->txp->cmd_a & 0x7ff;
-            s->txp->offset = (s->txp->cmd_a >> 16) & 0x1f;
-            /* End alignment does not include command words.  */
-            n = (s->txp->buffer_size + s->txp->offset + 3) >> 2;
-            switch ((n >> 24) & 3) {
-            case 1:
-                n = (-n) & 3;
-                break;
-            case 2:
-                n = (-n) & 7;
-                break;
-            default:
-                n = 0;
-            }
-            s->txp->pad = n;
-            s->txp->len = 0;
-        }
-        DPRINTF("Block len:%d offset:%d pad:%d cmd %08x\n",
-                s->txp->buffer_size, s->txp->offset, s->txp->pad,
-                s->txp->cmd_a);
-        s->txp->state = TX_DATA;
-        break;
-    case TX_DATA:
-        if (s->txp->offset >= 4) {
-            s->txp->offset -= 4;
-            break;
-        }
-        if (s->txp->buffer_size <= 0 && s->txp->pad != 0) {
-            s->txp->pad--;
-        } else {
-            n = 4;
-            while (s->txp->offset) {
-                val >>= 8;
-                n--;
-                s->txp->offset--;
-            }
-            /* Documentation is somewhat unclear on the ordering of bytes
-               in FIFO words.  Empirical results show it to be little-endian.
-               */
-            /* TODO: FIFO overflow checking.  */
-            while (n--) {
-                s->txp->data[s->txp->len] = val & 0xff;
-                s->txp->len++;
-                val >>= 8;
-                s->txp->buffer_size--;
-            }
-            s->txp->fifo_used++;
-        }
-        if (s->txp->buffer_size <= 0 && s->txp->pad == 0) {
-            if (s->txp->cmd_a & 0x1000) {
-                do_tx_packet(s);
-            }
-            if (s->txp->cmd_a & 0x80000000) {
-                s->int_sts |= TX_IOC_INT;
-            }
-            s->txp->state = TX_IDLE;
-        }
-        break;
-    }
-}
-
-static uint32_t do_phy_read(lan9118_state *s, int reg)
-{
-    uint32_t val;
-
-    switch (reg) {
-    case 0: /* Basic Control */
-        return s->phy_control;
-    case 1: /* Basic Status */
-        return s->phy_status;
-    case 2: /* ID1 */
-        return 0x0007;
-    case 3: /* ID2 */
-        return 0xc0d1;
-    case 4: /* Auto-neg advertisement */
-        return s->phy_advertise;
-    case 5: /* Auto-neg Link Partner Ability */
-        return 0x0f71;
-    case 6: /* Auto-neg Expansion */
-        return 1;
-        /* TODO 17, 18, 27, 29, 30, 31 */
-    case 29: /* Interrupt source.  */
-        val = s->phy_int;
-        s->phy_int = 0;
-        phy_update_irq(s);
-        return val;
-    case 30: /* Interrupt mask */
-        return s->phy_int_mask;
-    default:
-        BADF("PHY read reg %d\n", reg);
-        return 0;
-    }
-}
-
-static void do_phy_write(lan9118_state *s, int reg, uint32_t val)
-{
-    switch (reg) {
-    case 0: /* Basic Control */
-        if (val & 0x8000) {
-            phy_reset(s);
-            break;
-        }
-        s->phy_control = val & 0x7980;
-        /* Complete autonegotiation immediately.  */
-        if (val & 0x1000) {
-            s->phy_status |= 0x0020;
-        }
-        break;
-    case 4: /* Auto-neg advertisement */
-        s->phy_advertise = (val & 0x2d7f) | 0x80;
-        break;
-        /* TODO 17, 18, 27, 31 */
-    case 30: /* Interrupt mask */
-        s->phy_int_mask = val & 0xff;
-        phy_update_irq(s);
-        break;
-    default:
-        BADF("PHY write reg %d = 0x%04x\n", reg, val);
-    }
-}
-
-static void do_mac_write(lan9118_state *s, int reg, uint32_t val)
-{
-    switch (reg) {
-    case MAC_CR:
-        if ((s->mac_cr & MAC_CR_RXEN) != 0 && (val & MAC_CR_RXEN) == 0) {
-            s->int_sts |= RXSTOP_INT;
-        }
-        s->mac_cr = val & ~MAC_CR_RESERVED;
-        DPRINTF("MAC_CR: %08x\n", val);
-        break;
-    case MAC_ADDRH:
-        s->conf.macaddr.a[4] = val & 0xff;
-        s->conf.macaddr.a[5] = (val >> 8) & 0xff;
-        lan9118_mac_changed(s);
-        break;
-    case MAC_ADDRL:
-        s->conf.macaddr.a[0] = val & 0xff;
-        s->conf.macaddr.a[1] = (val >> 8) & 0xff;
-        s->conf.macaddr.a[2] = (val >> 16) & 0xff;
-        s->conf.macaddr.a[3] = (val >> 24) & 0xff;
-        lan9118_mac_changed(s);
-        break;
-    case MAC_HASHH:
-        s->mac_hashh = val;
-        break;
-    case MAC_HASHL:
-        s->mac_hashl = val;
-        break;
-    case MAC_MII_ACC:
-        s->mac_mii_acc = val & 0xffc2;
-        if (val & 2) {
-            DPRINTF("PHY write %d = 0x%04x\n",
-                    (val >> 6) & 0x1f, s->mac_mii_data);
-            do_phy_write(s, (val >> 6) & 0x1f, s->mac_mii_data);
-        } else {
-            s->mac_mii_data = do_phy_read(s, (val >> 6) & 0x1f);
-            DPRINTF("PHY read %d = 0x%04x\n",
-                    (val >> 6) & 0x1f, s->mac_mii_data);
-        }
-        break;
-    case MAC_MII_DATA:
-        s->mac_mii_data = val & 0xffff;
-        break;
-    case MAC_FLOW:
-        s->mac_flow = val & 0xffff0000;
-        break;
-    case MAC_VLAN1:
-        /* Writing to this register changes a condition for
-         * FrameTooLong bit in rx_status.  Since we do not set
-         * FrameTooLong anyway, just ignore write to this.
-         */
-        break;
-    default:
-        hw_error("lan9118: Unimplemented MAC register write: %d = 0x%x\n",
-                 s->mac_cmd & 0xf, val);
-    }
-}
-
-static uint32_t do_mac_read(lan9118_state *s, int reg)
-{
-    switch (reg) {
-    case MAC_CR:
-        return s->mac_cr;
-    case MAC_ADDRH:
-        return s->conf.macaddr.a[4] | (s->conf.macaddr.a[5] << 8);
-    case MAC_ADDRL:
-        return s->conf.macaddr.a[0] | (s->conf.macaddr.a[1] << 8)
-               | (s->conf.macaddr.a[2] << 16) | (s->conf.macaddr.a[3] << 24);
-    case MAC_HASHH:
-        return s->mac_hashh;
-        break;
-    case MAC_HASHL:
-        return s->mac_hashl;
-        break;
-    case MAC_MII_ACC:
-        return s->mac_mii_acc;
-    case MAC_MII_DATA:
-        return s->mac_mii_data;
-    case MAC_FLOW:
-        return s->mac_flow;
-    default:
-        hw_error("lan9118: Unimplemented MAC register read: %d\n",
-                 s->mac_cmd & 0xf);
-    }
-}
-
-static void lan9118_eeprom_cmd(lan9118_state *s, int cmd, int addr)
-{
-    s->e2p_cmd = (s->e2p_cmd & 0x10) | (cmd << 28) | addr;
-    switch (cmd) {
-    case 0:
-        s->e2p_data = s->eeprom[addr];
-        DPRINTF("EEPROM Read %d = 0x%02x\n", addr, s->e2p_data);
-        break;
-    case 1:
-        s->eeprom_writable = 0;
-        DPRINTF("EEPROM Write Disable\n");
-        break;
-    case 2: /* EWEN */
-        s->eeprom_writable = 1;
-        DPRINTF("EEPROM Write Enable\n");
-        break;
-    case 3: /* WRITE */
-        if (s->eeprom_writable) {
-            s->eeprom[addr] &= s->e2p_data;
-            DPRINTF("EEPROM Write %d = 0x%02x\n", addr, s->e2p_data);
-        } else {
-            DPRINTF("EEPROM Write %d (ignored)\n", addr);
-        }
-        break;
-    case 4: /* WRAL */
-        if (s->eeprom_writable) {
-            for (addr = 0; addr < 128; addr++) {
-                s->eeprom[addr] &= s->e2p_data;
-            }
-            DPRINTF("EEPROM Write All 0x%02x\n", s->e2p_data);
-        } else {
-            DPRINTF("EEPROM Write All (ignored)\n");
-        }
-        break;
-    case 5: /* ERASE */
-        if (s->eeprom_writable) {
-            s->eeprom[addr] = 0xff;
-            DPRINTF("EEPROM Erase %d\n", addr);
-        } else {
-            DPRINTF("EEPROM Erase %d (ignored)\n", addr);
-        }
-        break;
-    case 6: /* ERAL */
-        if (s->eeprom_writable) {
-            memset(s->eeprom, 0xff, 128);
-            DPRINTF("EEPROM Erase All\n");
-        } else {
-            DPRINTF("EEPROM Erase All (ignored)\n");
-        }
-        break;
-    case 7: /* RELOAD */
-        lan9118_reload_eeprom(s);
-        break;
-    }
-}
-
-static void lan9118_tick(void *opaque)
-{
-    lan9118_state *s = (lan9118_state *)opaque;
-    if (s->int_en & GPT_INT) {
-        s->int_sts |= GPT_INT;
-    }
-    lan9118_update(s);
-}
-
-static void lan9118_writel(void *opaque, hwaddr offset,
-                           uint64_t val, unsigned size)
-{
-    lan9118_state *s = (lan9118_state *)opaque;
-    offset &= 0xff;
-
-    //DPRINTF("Write reg 0x%02x = 0x%08x\n", (int)offset, val);
-    if (offset >= 0x20 && offset < 0x40) {
-        /* TX FIFO */
-        tx_fifo_push(s, val);
-        return;
-    }
-    switch (offset) {
-    case CSR_IRQ_CFG:
-        /* TODO: Implement interrupt deassertion intervals.  */
-        val &= (IRQ_EN | IRQ_POL | IRQ_TYPE);
-        s->irq_cfg = (s->irq_cfg & IRQ_INT) | val;
-        break;
-    case CSR_INT_STS:
-        s->int_sts &= ~val;
-        break;
-    case CSR_INT_EN:
-        s->int_en = val & ~RESERVED_INT;
-        s->int_sts |= val & SW_INT;
-        break;
-    case CSR_FIFO_INT:
-        DPRINTF("FIFO INT levels %08x\n", val);
-        s->fifo_int = val;
-        break;
-    case CSR_RX_CFG:
-        if (val & 0x8000) {
-            /* RX_DUMP */
-            s->rx_fifo_used = 0;
-            s->rx_status_fifo_used = 0;
-            s->rx_packet_size_tail = s->rx_packet_size_head;
-            s->rx_packet_size[s->rx_packet_size_head] = 0;
-        }
-        s->rx_cfg = val & 0xcfff1ff0;
-        break;
-    case CSR_TX_CFG:
-        if (val & 0x8000) {
-            s->tx_status_fifo_used = 0;
-        }
-        if (val & 0x4000) {
-            s->txp->state = TX_IDLE;
-            s->txp->fifo_used = 0;
-            s->txp->cmd_a = 0xffffffff;
-        }
-        s->tx_cfg = val & 6;
-        break;
-    case CSR_HW_CFG:
-        if (val & 1) {
-            /* SRST */
-            lan9118_reset(&s->busdev.qdev);
-        } else {
-            s->hw_cfg = (val & 0x003f300) | (s->hw_cfg & 0x4);
-        }
-        break;
-    case CSR_RX_DP_CTRL:
-        if (val & 0x80000000) {
-            /* Skip forward to next packet.  */
-            s->rxp_pad = 0;
-            s->rxp_offset = 0;
-            if (s->rxp_size == 0) {
-                /* Pop a word to start the next packet.  */
-                rx_fifo_pop(s);
-                s->rxp_pad = 0;
-                s->rxp_offset = 0;
-            }
-            s->rx_fifo_head += s->rxp_size;
-            if (s->rx_fifo_head >= s->rx_fifo_size) {
-                s->rx_fifo_head -= s->rx_fifo_size;
-            }
-        }
-        break;
-    case CSR_PMT_CTRL:
-        if (val & 0x400) {
-            phy_reset(s);
-        }
-        s->pmt_ctrl &= ~0x34e;
-        s->pmt_ctrl |= (val & 0x34e);
-        break;
-    case CSR_GPIO_CFG:
-        /* Probably just enabling LEDs.  */
-        s->gpio_cfg = val & 0x7777071f;
-        break;
-    case CSR_GPT_CFG:
-        if ((s->gpt_cfg ^ val) & GPT_TIMER_EN) {
-            if (val & GPT_TIMER_EN) {
-                ptimer_set_count(s->timer, val & 0xffff);
-                ptimer_run(s->timer, 0);
-            } else {
-                ptimer_stop(s->timer);
-                ptimer_set_count(s->timer, 0xffff);
-            }
-        }
-        s->gpt_cfg = val & (GPT_TIMER_EN | 0xffff);
-        break;
-    case CSR_WORD_SWAP:
-        /* Ignored because we're in 32-bit mode.  */
-        s->word_swap = val;
-        break;
-    case CSR_MAC_CSR_CMD:
-        s->mac_cmd = val & 0x4000000f;
-        if (val & 0x80000000) {
-            if (val & 0x40000000) {
-                s->mac_data = do_mac_read(s, val & 0xf);
-                DPRINTF("MAC read %d = 0x%08x\n", val & 0xf, s->mac_data);
-            } else {
-                DPRINTF("MAC write %d = 0x%08x\n", val & 0xf, s->mac_data);
-                do_mac_write(s, val & 0xf, s->mac_data);
-            }
-        }
-        break;
-    case CSR_MAC_CSR_DATA:
-        s->mac_data = val;
-        break;
-    case CSR_AFC_CFG:
-        s->afc_cfg = val & 0x00ffffff;
-        break;
-    case CSR_E2P_CMD:
-        lan9118_eeprom_cmd(s, (val >> 28) & 7, val & 0x7f);
-        break;
-    case CSR_E2P_DATA:
-        s->e2p_data = val & 0xff;
-        break;
-
-    default:
-        hw_error("lan9118_write: Bad reg 0x%x = %x\n", (int)offset, (int)val);
-        break;
-    }
-    lan9118_update(s);
-}
-
-static void lan9118_writew(void *opaque, hwaddr offset,
-                           uint32_t val)
-{
-    lan9118_state *s = (lan9118_state *)opaque;
-    offset &= 0xff;
-
-    if (s->write_word_prev_offset != (offset & ~0x3)) {
-        /* New offset, reset word counter */
-        s->write_word_n = 0;
-        s->write_word_prev_offset = offset & ~0x3;
-    }
-
-    if (offset & 0x2) {
-        s->write_word_h = val;
-    } else {
-        s->write_word_l = val;
-    }
-
-    //DPRINTF("Writew reg 0x%02x = 0x%08x\n", (int)offset, val);
-    s->write_word_n++;
-    if (s->write_word_n == 2) {
-        s->write_word_n = 0;
-        lan9118_writel(s, offset & ~3, s->write_word_l +
-                (s->write_word_h << 16), 4);
-    }
-}
-
-static void lan9118_16bit_mode_write(void *opaque, hwaddr offset,
-                                     uint64_t val, unsigned size)
-{
-    switch (size) {
-    case 2:
-        lan9118_writew(opaque, offset, (uint32_t)val);
-        return;
-    case 4:
-        lan9118_writel(opaque, offset, val, size);
-        return;
-    }
-
-    hw_error("lan9118_write: Bad size 0x%x\n", size);
-}
-
-static uint64_t lan9118_readl(void *opaque, hwaddr offset,
-                              unsigned size)
-{
-    lan9118_state *s = (lan9118_state *)opaque;
-
-    //DPRINTF("Read reg 0x%02x\n", (int)offset);
-    if (offset < 0x20) {
-        /* RX FIFO */
-        return rx_fifo_pop(s);
-    }
-    switch (offset) {
-    case 0x40:
-        return rx_status_fifo_pop(s);
-    case 0x44:
-        return s->rx_status_fifo[s->tx_status_fifo_head];
-    case 0x48:
-        return tx_status_fifo_pop(s);
-    case 0x4c:
-        return s->tx_status_fifo[s->tx_status_fifo_head];
-    case CSR_ID_REV:
-        return 0x01180001;
-    case CSR_IRQ_CFG:
-        return s->irq_cfg;
-    case CSR_INT_STS:
-        return s->int_sts;
-    case CSR_INT_EN:
-        return s->int_en;
-    case CSR_BYTE_TEST:
-        return 0x87654321;
-    case CSR_FIFO_INT:
-        return s->fifo_int;
-    case CSR_RX_CFG:
-        return s->rx_cfg;
-    case CSR_TX_CFG:
-        return s->tx_cfg;
-    case CSR_HW_CFG:
-        return s->hw_cfg;
-    case CSR_RX_DP_CTRL:
-        return 0;
-    case CSR_RX_FIFO_INF:
-        return (s->rx_status_fifo_used << 16) | (s->rx_fifo_used << 2);
-    case CSR_TX_FIFO_INF:
-        return (s->tx_status_fifo_used << 16)
-               | (s->tx_fifo_size - s->txp->fifo_used);
-    case CSR_PMT_CTRL:
-        return s->pmt_ctrl;
-    case CSR_GPIO_CFG:
-        return s->gpio_cfg;
-    case CSR_GPT_CFG:
-        return s->gpt_cfg;
-    case CSR_GPT_CNT:
-        return ptimer_get_count(s->timer);
-    case CSR_WORD_SWAP:
-        return s->word_swap;
-    case CSR_FREE_RUN:
-        return (qemu_get_clock_ns(vm_clock) / 40) - s->free_timer_start;
-    case CSR_RX_DROP:
-        /* TODO: Implement dropped frames counter.  */
-        return 0;
-    case CSR_MAC_CSR_CMD:
-        return s->mac_cmd;
-    case CSR_MAC_CSR_DATA:
-        return s->mac_data;
-    case CSR_AFC_CFG:
-        return s->afc_cfg;
-    case CSR_E2P_CMD:
-        return s->e2p_cmd;
-    case CSR_E2P_DATA:
-        return s->e2p_data;
-    }
-    hw_error("lan9118_read: Bad reg 0x%x\n", (int)offset);
-    return 0;
-}
-
-static uint32_t lan9118_readw(void *opaque, hwaddr offset)
-{
-    lan9118_state *s = (lan9118_state *)opaque;
-    uint32_t val;
-
-    if (s->read_word_prev_offset != (offset & ~0x3)) {
-        /* New offset, reset word counter */
-        s->read_word_n = 0;
-        s->read_word_prev_offset = offset & ~0x3;
-    }
-
-    s->read_word_n++;
-    if (s->read_word_n == 1) {
-        s->read_long = lan9118_readl(s, offset & ~3, 4);
-    } else {
-        s->read_word_n = 0;
-    }
-
-    if (offset & 2) {
-        val = s->read_long >> 16;
-    } else {
-        val = s->read_long & 0xFFFF;
-    }
-
-    //DPRINTF("Readw reg 0x%02x, val 0x%x\n", (int)offset, val);
-    return val;
-}
-
-static uint64_t lan9118_16bit_mode_read(void *opaque, hwaddr offset,
-                                        unsigned size)
-{
-    switch (size) {
-    case 2:
-        return lan9118_readw(opaque, offset);
-    case 4:
-        return lan9118_readl(opaque, offset, size);
-    }
-
-    hw_error("lan9118_read: Bad size 0x%x\n", size);
-    return 0;
-}
-
-static const MemoryRegionOps lan9118_mem_ops = {
-    .read = lan9118_readl,
-    .write = lan9118_writel,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static const MemoryRegionOps lan9118_16bit_mem_ops = {
-    .read = lan9118_16bit_mode_read,
-    .write = lan9118_16bit_mode_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void lan9118_cleanup(NetClientState *nc)
-{
-    lan9118_state *s = qemu_get_nic_opaque(nc);
-
-    s->nic = NULL;
-}
-
-static NetClientInfo net_lan9118_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
-    .size = sizeof(NICState),
-    .can_receive = lan9118_can_receive,
-    .receive = lan9118_receive,
-    .cleanup = lan9118_cleanup,
-    .link_status_changed = lan9118_set_link,
-};
-
-static int lan9118_init1(SysBusDevice *dev)
-{
-    lan9118_state *s = FROM_SYSBUS(lan9118_state, dev);
-    QEMUBH *bh;
-    int i;
-    const MemoryRegionOps *mem_ops =
-            s->mode_16bit ? &lan9118_16bit_mem_ops : &lan9118_mem_ops;
-
-    memory_region_init_io(&s->mmio, mem_ops, s, "lan9118-mmio", 0x100);
-    sysbus_init_mmio(dev, &s->mmio);
-    sysbus_init_irq(dev, &s->irq);
-    qemu_macaddr_default_if_unset(&s->conf.macaddr);
-
-    s->nic = qemu_new_nic(&net_lan9118_info, &s->conf,
-                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
-    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
-    s->eeprom[0] = 0xa5;
-    for (i = 0; i < 6; i++) {
-        s->eeprom[i + 1] = s->conf.macaddr.a[i];
-    }
-    s->pmt_ctrl = 1;
-    s->txp = &s->tx_packet;
-
-    bh = qemu_bh_new(lan9118_tick, s);
-    s->timer = ptimer_init(bh);
-    ptimer_set_freq(s->timer, 10000);
-    ptimer_set_limit(s->timer, 0xffff, 1);
-
-    return 0;
-}
-
-static Property lan9118_properties[] = {
-    DEFINE_NIC_PROPERTIES(lan9118_state, conf),
-    DEFINE_PROP_UINT32("mode_16bit", lan9118_state, mode_16bit, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void lan9118_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = lan9118_init1;
-    dc->reset = lan9118_reset;
-    dc->props = lan9118_properties;
-    dc->vmsd = &vmstate_lan9118;
-}
-
-static const TypeInfo lan9118_info = {
-    .name          = "lan9118",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(lan9118_state),
-    .class_init    = lan9118_class_init,
-};
-
-static void lan9118_register_types(void)
-{
-    type_register_static(&lan9118_info);
-}
-
-/* Legacy helper function.  Should go away when machine config files are
-   implemented.  */
-void lan9118_init(NICInfo *nd, uint32_t base, qemu_irq irq)
-{
-    DeviceState *dev;
-    SysBusDevice *s;
-
-    qemu_check_nic_model(nd, "lan9118");
-    dev = qdev_create(NULL, "lan9118");
-    qdev_set_nic_properties(dev, nd);
-    qdev_init_nofail(dev);
-    s = SYS_BUS_DEVICE(dev);
-    sysbus_mmio_map(s, 0, base);
-    sysbus_connect_irq(s, 0, irq);
-}
-
-type_init(lan9118_register_types)
diff --git a/hw/lm4549.c b/hw/lm4549.c
deleted file mode 100644 (file)
index 67335cb..0000000
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * LM4549 Audio Codec Interface
- *
- * Copyright (c) 2011
- * Written by Mathieu Sonet - www.elasticsheep.com
- *
- * This code is licensed under the GPL.
- *
- * *****************************************************************
- *
- * This driver emulates the LM4549 codec.
- *
- * It supports only one playback voice and no record voice.
- */
-
-#include "hw/hw.h"
-#include "audio/audio.h"
-#include "hw/lm4549.h"
-
-#if 0
-#define LM4549_DEBUG  1
-#endif
-
-#if 0
-#define LM4549_DUMP_DAC_INPUT 1
-#endif
-
-#ifdef LM4549_DEBUG
-#define DPRINTF(fmt, ...) \
-do { printf("lm4549: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while (0)
-#endif
-
-#if defined(LM4549_DUMP_DAC_INPUT)
-#include <stdio.h>
-static FILE *fp_dac_input;
-#endif
-
-/* LM4549 register list */
-enum {
-    LM4549_Reset                    = 0x00,
-    LM4549_Master_Volume            = 0x02,
-    LM4549_Line_Out_Volume          = 0x04,
-    LM4549_Master_Volume_Mono       = 0x06,
-    LM4549_PC_Beep_Volume           = 0x0A,
-    LM4549_Phone_Volume             = 0x0C,
-    LM4549_Mic_Volume               = 0x0E,
-    LM4549_Line_In_Volume           = 0x10,
-    LM4549_CD_Volume                = 0x12,
-    LM4549_Video_Volume             = 0x14,
-    LM4549_Aux_Volume               = 0x16,
-    LM4549_PCM_Out_Volume           = 0x18,
-    LM4549_Record_Select            = 0x1A,
-    LM4549_Record_Gain              = 0x1C,
-    LM4549_General_Purpose          = 0x20,
-    LM4549_3D_Control               = 0x22,
-    LM4549_Powerdown_Ctrl_Stat      = 0x26,
-    LM4549_Ext_Audio_ID             = 0x28,
-    LM4549_Ext_Audio_Stat_Ctrl      = 0x2A,
-    LM4549_PCM_Front_DAC_Rate       = 0x2C,
-    LM4549_PCM_ADC_Rate             = 0x32,
-    LM4549_Vendor_ID1               = 0x7C,
-    LM4549_Vendor_ID2               = 0x7E
-};
-
-static void lm4549_reset(lm4549_state *s)
-{
-    uint16_t *regfile = s->regfile;
-
-    regfile[LM4549_Reset]               = 0x0d50;
-    regfile[LM4549_Master_Volume]       = 0x8008;
-    regfile[LM4549_Line_Out_Volume]     = 0x8000;
-    regfile[LM4549_Master_Volume_Mono]  = 0x8000;
-    regfile[LM4549_PC_Beep_Volume]      = 0x0000;
-    regfile[LM4549_Phone_Volume]        = 0x8008;
-    regfile[LM4549_Mic_Volume]          = 0x8008;
-    regfile[LM4549_Line_In_Volume]      = 0x8808;
-    regfile[LM4549_CD_Volume]           = 0x8808;
-    regfile[LM4549_Video_Volume]        = 0x8808;
-    regfile[LM4549_Aux_Volume]          = 0x8808;
-    regfile[LM4549_PCM_Out_Volume]      = 0x8808;
-    regfile[LM4549_Record_Select]       = 0x0000;
-    regfile[LM4549_Record_Gain]         = 0x8000;
-    regfile[LM4549_General_Purpose]     = 0x0000;
-    regfile[LM4549_3D_Control]          = 0x0101;
-    regfile[LM4549_Powerdown_Ctrl_Stat] = 0x000f;
-    regfile[LM4549_Ext_Audio_ID]        = 0x0001;
-    regfile[LM4549_Ext_Audio_Stat_Ctrl] = 0x0000;
-    regfile[LM4549_PCM_Front_DAC_Rate]  = 0xbb80;
-    regfile[LM4549_PCM_ADC_Rate]        = 0xbb80;
-    regfile[LM4549_Vendor_ID1]          = 0x4e53;
-    regfile[LM4549_Vendor_ID2]          = 0x4331;
-}
-
-static void lm4549_audio_transfer(lm4549_state *s)
-{
-    uint32_t written_bytes, written_samples;
-    uint32_t i;
-
-    /* Activate the voice */
-    AUD_set_active_out(s->voice, 1);
-    s->voice_is_active = 1;
-
-    /* Try to write the buffer content */
-    written_bytes = AUD_write(s->voice, s->buffer,
-                              s->buffer_level * sizeof(uint16_t));
-    written_samples = written_bytes >> 1;
-
-#if defined(LM4549_DUMP_DAC_INPUT)
-    fwrite(s->buffer, sizeof(uint8_t), written_bytes, fp_dac_input);
-#endif
-
-    s->buffer_level -= written_samples;
-
-    if (s->buffer_level > 0) {
-        /* Move the data back to the start of the buffer */
-        for (i = 0; i < s->buffer_level; i++) {
-            s->buffer[i] = s->buffer[i + written_samples];
-        }
-    }
-}
-
-static void lm4549_audio_out_callback(void *opaque, int free)
-{
-    lm4549_state *s = (lm4549_state *)opaque;
-    static uint32_t prev_buffer_level;
-
-#ifdef LM4549_DEBUG
-    int size = AUD_get_buffer_size_out(s->voice);
-    DPRINTF("audio_out_callback size = %i free = %i\n", size, free);
-#endif
-
-    /* Detect that no data are consumed
-       => disable the voice */
-    if (s->buffer_level == prev_buffer_level) {
-        AUD_set_active_out(s->voice, 0);
-        s->voice_is_active = 0;
-    }
-    prev_buffer_level = s->buffer_level;
-
-    /* Check if a buffer transfer is pending */
-    if (s->buffer_level == LM4549_BUFFER_SIZE) {
-        lm4549_audio_transfer(s);
-
-        /* Request more data */
-        if (s->data_req_cb != NULL) {
-            (s->data_req_cb)(s->opaque);
-        }
-    }
-}
-
-uint32_t lm4549_read(lm4549_state *s, hwaddr offset)
-{
-    uint16_t *regfile = s->regfile;
-    uint32_t value = 0;
-
-    /* Read the stored value */
-    assert(offset < 128);
-    value = regfile[offset];
-
-    DPRINTF("read [0x%02x] = 0x%04x\n", offset, value);
-
-    return value;
-}
-
-void lm4549_write(lm4549_state *s,
-                  hwaddr offset, uint32_t value)
-{
-    uint16_t *regfile = s->regfile;
-
-    assert(offset < 128);
-    DPRINTF("write [0x%02x] = 0x%04x\n", offset, value);
-
-    switch (offset) {
-    case LM4549_Reset:
-        lm4549_reset(s);
-        break;
-
-    case LM4549_PCM_Front_DAC_Rate:
-        regfile[LM4549_PCM_Front_DAC_Rate] = value;
-        DPRINTF("DAC rate change = %i\n", value);
-
-        /* Re-open a voice with the new sample rate */
-        struct audsettings as;
-        as.freq = value;
-        as.nchannels = 2;
-        as.fmt = AUD_FMT_S16;
-        as.endianness = 0;
-
-        s->voice = AUD_open_out(
-            &s->card,
-            s->voice,
-            "lm4549.out",
-            s,
-            lm4549_audio_out_callback,
-            &as
-        );
-        break;
-
-    case LM4549_Powerdown_Ctrl_Stat:
-        value &= ~0xf;
-        value |= regfile[LM4549_Powerdown_Ctrl_Stat] & 0xf;
-        regfile[LM4549_Powerdown_Ctrl_Stat] = value;
-        break;
-
-    case LM4549_Ext_Audio_ID:
-    case LM4549_Vendor_ID1:
-    case LM4549_Vendor_ID2:
-        DPRINTF("Write to read-only register 0x%x\n", (int)offset);
-        break;
-
-    default:
-        /* Store the new value */
-        regfile[offset] = value;
-        break;
-    }
-}
-
-uint32_t lm4549_write_samples(lm4549_state *s, uint32_t left, uint32_t right)
-{
-    /* The left and right samples are in 20-bit resolution.
-       The LM4549 has 18-bit resolution and only uses the bits [19:2].
-       This model supports 16-bit playback.
-    */
-
-    if (s->buffer_level > LM4549_BUFFER_SIZE - 2) {
-        DPRINTF("write_sample Buffer full\n");
-        return 0;
-    }
-
-    /* Store 16-bit samples in the buffer */
-    s->buffer[s->buffer_level++] = (left >> 4);
-    s->buffer[s->buffer_level++] = (right >> 4);
-
-    if (s->buffer_level == LM4549_BUFFER_SIZE) {
-        /* Trigger the transfer of the buffer to the audio host */
-        lm4549_audio_transfer(s);
-    }
-
-    return 1;
-}
-
-static int lm4549_post_load(void *opaque, int version_id)
-{
-    lm4549_state *s = (lm4549_state *)opaque;
-    uint16_t *regfile = s->regfile;
-
-    /* Re-open a voice with the current sample rate */
-    uint32_t freq = regfile[LM4549_PCM_Front_DAC_Rate];
-
-    DPRINTF("post_load freq = %i\n", freq);
-    DPRINTF("post_load voice_is_active = %i\n", s->voice_is_active);
-
-    struct audsettings as;
-    as.freq = freq;
-    as.nchannels = 2;
-    as.fmt = AUD_FMT_S16;
-    as.endianness = 0;
-
-    s->voice = AUD_open_out(
-        &s->card,
-        s->voice,
-        "lm4549.out",
-        s,
-        lm4549_audio_out_callback,
-        &as
-    );
-
-    /* Request data */
-    if (s->voice_is_active == 1) {
-        lm4549_audio_out_callback(s, AUD_get_buffer_size_out(s->voice));
-    }
-
-    return 0;
-}
-
-void lm4549_init(lm4549_state *s, lm4549_callback data_req_cb, void* opaque)
-{
-    struct audsettings as;
-
-    /* Store the callback and opaque pointer */
-    s->data_req_cb = data_req_cb;
-    s->opaque = opaque;
-
-    /* Init the registers */
-    lm4549_reset(s);
-
-    /* Register an audio card */
-    AUD_register_card("lm4549", &s->card);
-
-    /* Open a default voice */
-    as.freq = 48000;
-    as.nchannels = 2;
-    as.fmt = AUD_FMT_S16;
-    as.endianness = 0;
-
-    s->voice = AUD_open_out(
-        &s->card,
-        s->voice,
-        "lm4549.out",
-        s,
-        lm4549_audio_out_callback,
-        &as
-    );
-
-    AUD_set_volume_out(s->voice, 0, 255, 255);
-
-    s->voice_is_active = 0;
-
-    /* Reset the input buffer */
-    memset(s->buffer, 0x00, sizeof(s->buffer));
-    s->buffer_level = 0;
-
-#if defined(LM4549_DUMP_DAC_INPUT)
-    fp_dac_input = fopen("lm4549_dac_input.pcm", "wb");
-    if (!fp_dac_input) {
-        hw_error("Unable to open lm4549_dac_input.pcm for writing\n");
-    }
-#endif
-}
-
-const VMStateDescription vmstate_lm4549_state = {
-    .name = "lm4549_state",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .post_load = &lm4549_post_load,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT32(voice_is_active, lm4549_state),
-        VMSTATE_UINT16_ARRAY(regfile, lm4549_state, 128),
-        VMSTATE_UINT16_ARRAY(buffer, lm4549_state, LM4549_BUFFER_SIZE),
-        VMSTATE_UINT32(buffer_level, lm4549_state),
-        VMSTATE_END_OF_LIST()
-    }
-};
diff --git a/hw/lm832x.c b/hw/lm832x.c
deleted file mode 100644 (file)
index bacbeb2..0000000
+++ /dev/null
@@ -1,521 +0,0 @@
-/*
- * National Semiconductor LM8322/8323 GPIO keyboard & PWM chips.
- *
- * Copyright (C) 2008 Nokia Corporation
- * Written by Andrzej Zaborowski <andrew@openedhand.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 or
- * (at your option) version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/hw.h"
-#include "hw/i2c/i2c.h"
-#include "qemu/timer.h"
-#include "ui/console.h"
-
-typedef struct {
-    I2CSlave i2c;
-    uint8_t i2c_dir;
-    uint8_t i2c_cycle;
-    uint8_t reg;
-
-    qemu_irq nirq;
-    uint16_t model;
-
-    struct {
-        qemu_irq out[2];
-        int in[2][2];
-    } mux;
-
-    uint8_t config;
-    uint8_t status;
-    uint8_t acttime;
-    uint8_t error;
-    uint8_t clock;
-
-    struct {
-        uint16_t pull;
-        uint16_t mask;
-        uint16_t dir;
-        uint16_t level;
-        qemu_irq out[16];
-    } gpio;
-
-    struct {
-        uint8_t dbnctime;
-        uint8_t size;
-        uint8_t start;
-        uint8_t len;
-        uint8_t fifo[16];
-    } kbd;
-
-    struct {
-        uint16_t file[256];
-       uint8_t faddr;
-        uint8_t addr[3];
-        QEMUTimer *tm[3];
-    } pwm;
-} LM823KbdState;
-
-#define INT_KEYPAD             (1 << 0)
-#define INT_ERROR              (1 << 3)
-#define INT_NOINIT             (1 << 4)
-#define INT_PWMEND(n)          (1 << (5 + n))
-
-#define ERR_BADPAR             (1 << 0)
-#define ERR_CMDUNK             (1 << 1)
-#define ERR_KEYOVR             (1 << 2)
-#define ERR_FIFOOVR            (1 << 6)
-
-static void lm_kbd_irq_update(LM823KbdState *s)
-{
-    qemu_set_irq(s->nirq, !s->status);
-}
-
-static void lm_kbd_gpio_update(LM823KbdState *s)
-{
-}
-
-static void lm_kbd_reset(LM823KbdState *s)
-{
-    s->config = 0x80;
-    s->status = INT_NOINIT;
-    s->acttime = 125;
-    s->kbd.dbnctime = 3;
-    s->kbd.size = 0x33;
-    s->clock = 0x08;
-
-    lm_kbd_irq_update(s);
-    lm_kbd_gpio_update(s);
-}
-
-static void lm_kbd_error(LM823KbdState *s, int err)
-{
-    s->error |= err;
-    s->status |= INT_ERROR;
-    lm_kbd_irq_update(s);
-}
-
-static void lm_kbd_pwm_tick(LM823KbdState *s, int line)
-{
-}
-
-static void lm_kbd_pwm_start(LM823KbdState *s, int line)
-{
-    lm_kbd_pwm_tick(s, line);
-}
-
-static void lm_kbd_pwm0_tick(void *opaque)
-{
-    lm_kbd_pwm_tick(opaque, 0);
-}
-static void lm_kbd_pwm1_tick(void *opaque)
-{
-    lm_kbd_pwm_tick(opaque, 1);
-}
-static void lm_kbd_pwm2_tick(void *opaque)
-{
-    lm_kbd_pwm_tick(opaque, 2);
-}
-
-enum {
-    LM832x_CMD_READ_ID         = 0x80, /* Read chip ID. */
-    LM832x_CMD_WRITE_CFG       = 0x81, /* Set configuration item. */
-    LM832x_CMD_READ_INT                = 0x82, /* Get interrupt status. */
-    LM832x_CMD_RESET           = 0x83, /* Reset, same as external one */
-    LM823x_CMD_WRITE_PULL_DOWN = 0x84, /* Select GPIO pull-up/down. */
-    LM832x_CMD_WRITE_PORT_SEL  = 0x85, /* Select GPIO in/out. */
-    LM832x_CMD_WRITE_PORT_STATE        = 0x86, /* Set GPIO pull-up/down. */
-    LM832x_CMD_READ_PORT_SEL   = 0x87, /* Get GPIO in/out. */
-    LM832x_CMD_READ_PORT_STATE = 0x88, /* Get GPIO pull-up/down. */
-    LM832x_CMD_READ_FIFO       = 0x89, /* Read byte from FIFO. */
-    LM832x_CMD_RPT_READ_FIFO   = 0x8a, /* Read FIFO (no increment). */
-    LM832x_CMD_SET_ACTIVE      = 0x8b, /* Set active time. */
-    LM832x_CMD_READ_ERROR      = 0x8c, /* Get error status. */
-    LM832x_CMD_READ_ROTATOR    = 0x8e, /* Read rotator status. */
-    LM832x_CMD_SET_DEBOUNCE    = 0x8f, /* Set debouncing time. */
-    LM832x_CMD_SET_KEY_SIZE    = 0x90, /* Set keypad size. */
-    LM832x_CMD_READ_KEY_SIZE   = 0x91, /* Get keypad size. */
-    LM832x_CMD_READ_CFG                = 0x92, /* Get configuration item. */
-    LM832x_CMD_WRITE_CLOCK     = 0x93, /* Set clock config. */
-    LM832x_CMD_READ_CLOCK      = 0x94, /* Get clock config. */
-    LM832x_CMD_PWM_WRITE       = 0x95, /* Write PWM script. */
-    LM832x_CMD_PWM_START       = 0x96, /* Start PWM engine. */
-    LM832x_CMD_PWM_STOP                = 0x97, /* Stop PWM engine. */
-    LM832x_GENERAL_ERROR       = 0xff, /* There was one error.
-                                           Previously was represented by -1
-                                           This is not a command */
-};
-
-#define LM832x_MAX_KPX         8
-#define LM832x_MAX_KPY         12
-
-static uint8_t lm_kbd_read(LM823KbdState *s, int reg, int byte)
-{
-    int ret;
-
-    switch (reg) {
-    case LM832x_CMD_READ_ID:
-        ret = 0x0400;
-        break;
-
-    case LM832x_CMD_READ_INT:
-        ret = s->status;
-        if (!(s->status & INT_NOINIT)) {
-            s->status = 0;
-            lm_kbd_irq_update(s);
-        }
-        break;
-
-    case LM832x_CMD_READ_PORT_SEL:
-        ret = s->gpio.dir;
-        break;
-    case LM832x_CMD_READ_PORT_STATE:
-        ret = s->gpio.mask;
-        break;
-
-    case LM832x_CMD_READ_FIFO:
-        if (s->kbd.len <= 1)
-            return 0x00;
-
-        /* Example response from the two commands after a INT_KEYPAD
-         * interrupt caused by the key 0x3c being pressed:
-         * RPT_READ_FIFO: 55 bc 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01
-         *     READ_FIFO: bc 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01
-         * RPT_READ_FIFO: bc 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01
-         *
-         * 55 is the code of the key release event serviced in the previous
-         * interrupt handling.
-         *
-         * TODO: find out whether the FIFO is advanced a single character
-         * before reading every byte or the whole size of the FIFO at the
-         * last LM832x_CMD_READ_FIFO.  This affects LM832x_CMD_RPT_READ_FIFO
-         * output in cases where there are more than one event in the FIFO.
-         * Assume 0xbc and 0x3c events are in the FIFO:
-         * RPT_READ_FIFO: 55 bc 3c 00 4e ff 0a 50 08 00 29 d9 08 01 c9
-         *     READ_FIFO: bc 3c 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9
-         * Does RPT_READ_FIFO now return 0xbc and 0x3c or only 0x3c?
-         */
-        s->kbd.start ++;
-        s->kbd.start &= sizeof(s->kbd.fifo) - 1;
-        s->kbd.len --;
-
-        return s->kbd.fifo[s->kbd.start];
-    case LM832x_CMD_RPT_READ_FIFO:
-        if (byte >= s->kbd.len)
-            return 0x00;
-
-        return s->kbd.fifo[(s->kbd.start + byte) & (sizeof(s->kbd.fifo) - 1)];
-
-    case LM832x_CMD_READ_ERROR:
-        return s->error;
-
-    case LM832x_CMD_READ_ROTATOR:
-        return 0;
-
-    case LM832x_CMD_READ_KEY_SIZE:
-        return s->kbd.size;
-
-    case LM832x_CMD_READ_CFG:
-        return s->config & 0xf;
-
-    case LM832x_CMD_READ_CLOCK:
-        return (s->clock & 0xfc) | 2;
-
-    default:
-        lm_kbd_error(s, ERR_CMDUNK);
-        fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, reg);
-        return 0x00;
-    }
-
-    return ret >> (byte << 3);
-}
-
-static void lm_kbd_write(LM823KbdState *s, int reg, int byte, uint8_t value)
-{
-    switch (reg) {
-    case LM832x_CMD_WRITE_CFG:
-        s->config = value;
-        /* This must be done whenever s->mux.in is updated (never).  */
-        if ((s->config >> 1) & 1)                      /* MUX1EN */
-            qemu_set_irq(s->mux.out[0], s->mux.in[0][(s->config >> 0) & 1]);
-        if ((s->config >> 3) & 1)                      /* MUX2EN */
-            qemu_set_irq(s->mux.out[0], s->mux.in[0][(s->config >> 2) & 1]);
-        /* TODO: check that this is issued only following the chip reset
-         * and not in the middle of operation and that it is followed by
-         * the GPIO ports re-resablishing through WRITE_PORT_SEL and
-         * WRITE_PORT_STATE (using a timer perhaps) and otherwise output
-         * warnings.  */
-        s->status = 0;
-        lm_kbd_irq_update(s);
-        s->kbd.len = 0;
-        s->kbd.start = 0;
-        s->reg = LM832x_GENERAL_ERROR;
-        break;
-
-    case LM832x_CMD_RESET:
-        if (value == 0xaa)
-            lm_kbd_reset(s);
-        else
-            lm_kbd_error(s, ERR_BADPAR);
-        s->reg = LM832x_GENERAL_ERROR;
-        break;
-
-    case LM823x_CMD_WRITE_PULL_DOWN:
-        if (!byte)
-            s->gpio.pull = value;
-        else {
-            s->gpio.pull |= value << 8;
-            lm_kbd_gpio_update(s);
-            s->reg = LM832x_GENERAL_ERROR;
-        }
-        break;
-    case LM832x_CMD_WRITE_PORT_SEL:
-        if (!byte)
-            s->gpio.dir = value;
-        else {
-            s->gpio.dir |= value << 8;
-            lm_kbd_gpio_update(s);
-            s->reg = LM832x_GENERAL_ERROR;
-        }
-        break;
-    case LM832x_CMD_WRITE_PORT_STATE:
-        if (!byte)
-            s->gpio.mask = value;
-        else {
-            s->gpio.mask |= value << 8;
-            lm_kbd_gpio_update(s);
-            s->reg = LM832x_GENERAL_ERROR;
-        }
-        break;
-
-    case LM832x_CMD_SET_ACTIVE:
-        s->acttime = value;
-        s->reg = LM832x_GENERAL_ERROR;
-        break;
-
-    case LM832x_CMD_SET_DEBOUNCE:
-        s->kbd.dbnctime = value;
-        s->reg = LM832x_GENERAL_ERROR;
-        if (!value)
-            lm_kbd_error(s, ERR_BADPAR);
-        break;
-
-    case LM832x_CMD_SET_KEY_SIZE:
-        s->kbd.size = value;
-        s->reg = LM832x_GENERAL_ERROR;
-        if (
-                        (value & 0xf) < 3 || (value & 0xf) > LM832x_MAX_KPY ||
-                        (value >> 4) < 3 || (value >> 4) > LM832x_MAX_KPX)
-            lm_kbd_error(s, ERR_BADPAR);
-        break;
-
-    case LM832x_CMD_WRITE_CLOCK:
-        s->clock = value;
-        s->reg = LM832x_GENERAL_ERROR;
-        if ((value & 3) && (value & 3) != 3) {
-            lm_kbd_error(s, ERR_BADPAR);
-            fprintf(stderr, "%s: invalid clock setting in RCPWM\n",
-                            __FUNCTION__);
-        }
-        /* TODO: Validate that the command is only issued once */
-        break;
-
-    case LM832x_CMD_PWM_WRITE:
-        if (byte == 0) {
-            if (!(value & 3) || (value >> 2) > 59) {
-                lm_kbd_error(s, ERR_BADPAR);
-                s->reg = LM832x_GENERAL_ERROR;
-                break;
-            }
-
-            s->pwm.faddr = value;
-            s->pwm.file[s->pwm.faddr] = 0;
-        } else if (byte == 1) {
-            s->pwm.file[s->pwm.faddr] |= value << 8;
-        } else if (byte == 2) {
-            s->pwm.file[s->pwm.faddr] |= value << 0;
-            s->reg = LM832x_GENERAL_ERROR;
-        }
-        break;
-    case LM832x_CMD_PWM_START:
-        s->reg = LM832x_GENERAL_ERROR;
-        if (!(value & 3) || (value >> 2) > 59) {
-            lm_kbd_error(s, ERR_BADPAR);
-            break;
-        }
-
-        s->pwm.addr[(value & 3) - 1] = value >> 2;
-        lm_kbd_pwm_start(s, (value & 3) - 1);
-        break;
-    case LM832x_CMD_PWM_STOP:
-        s->reg = LM832x_GENERAL_ERROR;
-        if (!(value & 3)) {
-            lm_kbd_error(s, ERR_BADPAR);
-            break;
-        }
-
-        qemu_del_timer(s->pwm.tm[(value & 3) - 1]);
-        break;
-
-    case LM832x_GENERAL_ERROR:
-        lm_kbd_error(s, ERR_BADPAR);
-        break;
-    default:
-        lm_kbd_error(s, ERR_CMDUNK);
-        fprintf(stderr, "%s: unknown command %02x\n", __FUNCTION__, reg);
-        break;
-    }
-}
-
-static void lm_i2c_event(I2CSlave *i2c, enum i2c_event event)
-{
-    LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, i2c);
-
-    switch (event) {
-    case I2C_START_RECV:
-    case I2C_START_SEND:
-        s->i2c_cycle = 0;
-        s->i2c_dir = (event == I2C_START_SEND);
-        break;
-
-    default:
-        break;
-    }
-}
-
-static int lm_i2c_rx(I2CSlave *i2c)
-{
-    LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, i2c);
-
-    return lm_kbd_read(s, s->reg, s->i2c_cycle ++);
-}
-
-static int lm_i2c_tx(I2CSlave *i2c, uint8_t data)
-{
-    LM823KbdState *s = (LM823KbdState *) i2c;
-
-    if (!s->i2c_cycle)
-        s->reg = data;
-    else
-        lm_kbd_write(s, s->reg, s->i2c_cycle - 1, data);
-    s->i2c_cycle ++;
-
-    return 0;
-}
-
-static int lm_kbd_post_load(void *opaque, int version_id)
-{
-    LM823KbdState *s = opaque;
-
-    lm_kbd_irq_update(s);
-    lm_kbd_gpio_update(s);
-
-    return 0;
-}
-
-static const VMStateDescription vmstate_lm_kbd = {
-    .name = "LM8323",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .post_load = lm_kbd_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_I2C_SLAVE(i2c, LM823KbdState),
-        VMSTATE_UINT8(i2c_dir, LM823KbdState),
-        VMSTATE_UINT8(i2c_cycle, LM823KbdState),
-        VMSTATE_UINT8(reg, LM823KbdState),
-        VMSTATE_UINT8(config, LM823KbdState),
-        VMSTATE_UINT8(status, LM823KbdState),
-        VMSTATE_UINT8(acttime, LM823KbdState),
-        VMSTATE_UINT8(error, LM823KbdState),
-        VMSTATE_UINT8(clock, LM823KbdState),
-        VMSTATE_UINT16(gpio.pull, LM823KbdState),
-        VMSTATE_UINT16(gpio.mask, LM823KbdState),
-        VMSTATE_UINT16(gpio.dir, LM823KbdState),
-        VMSTATE_UINT16(gpio.level, LM823KbdState),
-        VMSTATE_UINT8(kbd.dbnctime, LM823KbdState),
-        VMSTATE_UINT8(kbd.size, LM823KbdState),
-        VMSTATE_UINT8(kbd.start, LM823KbdState),
-        VMSTATE_UINT8(kbd.len, LM823KbdState),
-        VMSTATE_BUFFER(kbd.fifo, LM823KbdState),
-        VMSTATE_UINT16_ARRAY(pwm.file, LM823KbdState, 256),
-        VMSTATE_UINT8(pwm.faddr, LM823KbdState),
-        VMSTATE_BUFFER(pwm.addr, LM823KbdState),
-        VMSTATE_TIMER_ARRAY(pwm.tm, LM823KbdState, 3),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-
-static int lm8323_init(I2CSlave *i2c)
-{
-    LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, i2c);
-
-    s->model = 0x8323;
-    s->pwm.tm[0] = qemu_new_timer_ns(vm_clock, lm_kbd_pwm0_tick, s);
-    s->pwm.tm[1] = qemu_new_timer_ns(vm_clock, lm_kbd_pwm1_tick, s);
-    s->pwm.tm[2] = qemu_new_timer_ns(vm_clock, lm_kbd_pwm2_tick, s);
-    qdev_init_gpio_out(&i2c->qdev, &s->nirq, 1);
-
-    lm_kbd_reset(s);
-
-    qemu_register_reset((void *) lm_kbd_reset, s);
-    return 0;
-}
-
-void lm832x_key_event(DeviceState *dev, int key, int state)
-{
-    LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, I2C_SLAVE(dev));
-
-    if ((s->status & INT_ERROR) && (s->error & ERR_FIFOOVR))
-        return;
-
-    if (s->kbd.len >= sizeof(s->kbd.fifo)) {
-        lm_kbd_error(s, ERR_FIFOOVR);
-        return;
-    }
-
-    s->kbd.fifo[(s->kbd.start + s->kbd.len ++) & (sizeof(s->kbd.fifo) - 1)] =
-            key | (state << 7);
-
-    /* We never set ERR_KEYOVR because we support multiple keys fine.  */
-    s->status |= INT_KEYPAD;
-    lm_kbd_irq_update(s);
-}
-
-static void lm8323_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
-
-    k->init = lm8323_init;
-    k->event = lm_i2c_event;
-    k->recv = lm_i2c_rx;
-    k->send = lm_i2c_tx;
-    dc->vmsd = &vmstate_lm_kbd;
-}
-
-static const TypeInfo lm8323_info = {
-    .name          = "lm8323",
-    .parent        = TYPE_I2C_SLAVE,
-    .instance_size = sizeof(LM823KbdState),
-    .class_init    = lm8323_class_init,
-};
-
-static void lm832x_register_types(void)
-{
-    type_register_static(&lm8323_info);
-}
-
-type_init(lm832x_register_types)
diff --git a/hw/loader.c b/hw/loader.c
deleted file mode 100644 (file)
index 2f5072d..0000000
+++ /dev/null
@@ -1,850 +0,0 @@
-/*
- * QEMU Executable loader
- *
- * Copyright (c) 2006 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * Gunzip functionality in this file is derived from u-boot:
- *
- * (C) Copyright 2008 Semihalf
- *
- * (C) Copyright 2000-2005
- * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/hw.h"
-#include "disas/disas.h"
-#include "monitor/monitor.h"
-#include "sysemu/sysemu.h"
-#include "hw/uboot_image.h"
-#include "hw/loader.h"
-#include "hw/nvram/fw_cfg.h"
-#include "exec/memory.h"
-#include "exec/address-spaces.h"
-
-#include <zlib.h>
-
-static int roms_loaded;
-
-/* return the size or -1 if error */
-int get_image_size(const char *filename)
-{
-    int fd, size;
-    fd = open(filename, O_RDONLY | O_BINARY);
-    if (fd < 0)
-        return -1;
-    size = lseek(fd, 0, SEEK_END);
-    close(fd);
-    return size;
-}
-
-/* return the size or -1 if error */
-/* deprecated, because caller does not specify buffer size! */
-int load_image(const char *filename, uint8_t *addr)
-{
-    int fd, size;
-    fd = open(filename, O_RDONLY | O_BINARY);
-    if (fd < 0)
-        return -1;
-    size = lseek(fd, 0, SEEK_END);
-    lseek(fd, 0, SEEK_SET);
-    if (read(fd, addr, size) != size) {
-        close(fd);
-        return -1;
-    }
-    close(fd);
-    return size;
-}
-
-/* read()-like version */
-ssize_t read_targphys(const char *name,
-                      int fd, hwaddr dst_addr, size_t nbytes)
-{
-    uint8_t *buf;
-    ssize_t did;
-
-    buf = g_malloc(nbytes);
-    did = read(fd, buf, nbytes);
-    if (did > 0)
-        rom_add_blob_fixed("read", buf, did, dst_addr);
-    g_free(buf);
-    return did;
-}
-
-/* return the size or -1 if error */
-int load_image_targphys(const char *filename,
-                        hwaddr addr, uint64_t max_sz)
-{
-    int size;
-
-    size = get_image_size(filename);
-    if (size > max_sz) {
-        return -1;
-    }
-    if (size > 0) {
-        rom_add_file_fixed(filename, addr, -1);
-    }
-    return size;
-}
-
-void pstrcpy_targphys(const char *name, hwaddr dest, int buf_size,
-                      const char *source)
-{
-    const char *nulp;
-    char *ptr;
-
-    if (buf_size <= 0) return;
-    nulp = memchr(source, 0, buf_size);
-    if (nulp) {
-        rom_add_blob_fixed(name, source, (nulp - source) + 1, dest);
-    } else {
-        rom_add_blob_fixed(name, source, buf_size, dest);
-        ptr = rom_ptr(dest + buf_size - 1);
-        *ptr = 0;
-    }
-}
-
-/* A.OUT loader */
-
-struct exec
-{
-  uint32_t a_info;   /* Use macros N_MAGIC, etc for access */
-  uint32_t a_text;   /* length of text, in bytes */
-  uint32_t a_data;   /* length of data, in bytes */
-  uint32_t a_bss;    /* length of uninitialized data area, in bytes */
-  uint32_t a_syms;   /* length of symbol table data in file, in bytes */
-  uint32_t a_entry;  /* start address */
-  uint32_t a_trsize; /* length of relocation info for text, in bytes */
-  uint32_t a_drsize; /* length of relocation info for data, in bytes */
-};
-
-static void bswap_ahdr(struct exec *e)
-{
-    bswap32s(&e->a_info);
-    bswap32s(&e->a_text);
-    bswap32s(&e->a_data);
-    bswap32s(&e->a_bss);
-    bswap32s(&e->a_syms);
-    bswap32s(&e->a_entry);
-    bswap32s(&e->a_trsize);
-    bswap32s(&e->a_drsize);
-}
-
-#define N_MAGIC(exec) ((exec).a_info & 0xffff)
-#define OMAGIC 0407
-#define NMAGIC 0410
-#define ZMAGIC 0413
-#define QMAGIC 0314
-#define _N_HDROFF(x) (1024 - sizeof (struct exec))
-#define N_TXTOFF(x)                                                    \
-    (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) :    \
-     (N_MAGIC(x) == QMAGIC ? 0 : sizeof (struct exec)))
-#define N_TXTADDR(x, target_page_size) (N_MAGIC(x) == QMAGIC ? target_page_size : 0)
-#define _N_SEGMENT_ROUND(x, target_page_size) (((x) + target_page_size - 1) & ~(target_page_size - 1))
-
-#define _N_TXTENDADDR(x, target_page_size) (N_TXTADDR(x, target_page_size)+(x).a_text)
-
-#define N_DATADDR(x, target_page_size) \
-    (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x, target_page_size)) \
-     : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x, target_page_size), target_page_size)))
-
-
-int load_aout(const char *filename, hwaddr addr, int max_sz,
-              int bswap_needed, hwaddr target_page_size)
-{
-    int fd;
-    ssize_t size, ret;
-    struct exec e;
-    uint32_t magic;
-
-    fd = open(filename, O_RDONLY | O_BINARY);
-    if (fd < 0)
-        return -1;
-
-    size = read(fd, &e, sizeof(e));
-    if (size < 0)
-        goto fail;
-
-    if (bswap_needed) {
-        bswap_ahdr(&e);
-    }
-
-    magic = N_MAGIC(e);
-    switch (magic) {
-    case ZMAGIC:
-    case QMAGIC:
-    case OMAGIC:
-        if (e.a_text + e.a_data > max_sz)
-            goto fail;
-       lseek(fd, N_TXTOFF(e), SEEK_SET);
-       size = read_targphys(filename, fd, addr, e.a_text + e.a_data);
-       if (size < 0)
-           goto fail;
-       break;
-    case NMAGIC:
-        if (N_DATADDR(e, target_page_size) + e.a_data > max_sz)
-            goto fail;
-       lseek(fd, N_TXTOFF(e), SEEK_SET);
-       size = read_targphys(filename, fd, addr, e.a_text);
-       if (size < 0)
-           goto fail;
-        ret = read_targphys(filename, fd, addr + N_DATADDR(e, target_page_size),
-                            e.a_data);
-       if (ret < 0)
-           goto fail;
-       size += ret;
-       break;
-    default:
-       goto fail;
-    }
-    close(fd);
-    return size;
- fail:
-    close(fd);
-    return -1;
-}
-
-/* ELF loader */
-
-static void *load_at(int fd, int offset, int size)
-{
-    void *ptr;
-    if (lseek(fd, offset, SEEK_SET) < 0)
-        return NULL;
-    ptr = g_malloc(size);
-    if (read(fd, ptr, size) != size) {
-        g_free(ptr);
-        return NULL;
-    }
-    return ptr;
-}
-
-#ifdef ELF_CLASS
-#undef ELF_CLASS
-#endif
-
-#define ELF_CLASS   ELFCLASS32
-#include "elf.h"
-
-#define SZ             32
-#define elf_word        uint32_t
-#define elf_sword        int32_t
-#define bswapSZs       bswap32s
-#include "hw/elf_ops.h"
-
-#undef elfhdr
-#undef elf_phdr
-#undef elf_shdr
-#undef elf_sym
-#undef elf_note
-#undef elf_word
-#undef elf_sword
-#undef bswapSZs
-#undef SZ
-#define elfhdr         elf64_hdr
-#define elf_phdr       elf64_phdr
-#define elf_note       elf64_note
-#define elf_shdr       elf64_shdr
-#define elf_sym                elf64_sym
-#define elf_word        uint64_t
-#define elf_sword        int64_t
-#define bswapSZs       bswap64s
-#define SZ             64
-#include "hw/elf_ops.h"
-
-/* return < 0 if error, otherwise the number of bytes loaded in memory */
-int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
-             void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
-             uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb)
-{
-    int fd, data_order, target_data_order, must_swab, ret;
-    uint8_t e_ident[EI_NIDENT];
-
-    fd = open(filename, O_RDONLY | O_BINARY);
-    if (fd < 0) {
-        perror(filename);
-        return -1;
-    }
-    if (read(fd, e_ident, sizeof(e_ident)) != sizeof(e_ident))
-        goto fail;
-    if (e_ident[0] != ELFMAG0 ||
-        e_ident[1] != ELFMAG1 ||
-        e_ident[2] != ELFMAG2 ||
-        e_ident[3] != ELFMAG3)
-        goto fail;
-#ifdef HOST_WORDS_BIGENDIAN
-    data_order = ELFDATA2MSB;
-#else
-    data_order = ELFDATA2LSB;
-#endif
-    must_swab = data_order != e_ident[EI_DATA];
-    if (big_endian) {
-        target_data_order = ELFDATA2MSB;
-    } else {
-        target_data_order = ELFDATA2LSB;
-    }
-
-    if (target_data_order != e_ident[EI_DATA]) {
-        goto fail;
-    }
-
-    lseek(fd, 0, SEEK_SET);
-    if (e_ident[EI_CLASS] == ELFCLASS64) {
-        ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab,
-                         pentry, lowaddr, highaddr, elf_machine, clear_lsb);
-    } else {
-        ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab,
-                         pentry, lowaddr, highaddr, elf_machine, clear_lsb);
-    }
-
-    close(fd);
-    return ret;
-
- fail:
-    close(fd);
-    return -1;
-}
-
-static void bswap_uboot_header(uboot_image_header_t *hdr)
-{
-#ifndef HOST_WORDS_BIGENDIAN
-    bswap32s(&hdr->ih_magic);
-    bswap32s(&hdr->ih_hcrc);
-    bswap32s(&hdr->ih_time);
-    bswap32s(&hdr->ih_size);
-    bswap32s(&hdr->ih_load);
-    bswap32s(&hdr->ih_ep);
-    bswap32s(&hdr->ih_dcrc);
-#endif
-}
-
-
-#define ZALLOC_ALIGNMENT       16
-
-static void *zalloc(void *x, unsigned items, unsigned size)
-{
-    void *p;
-
-    size *= items;
-    size = (size + ZALLOC_ALIGNMENT - 1) & ~(ZALLOC_ALIGNMENT - 1);
-
-    p = g_malloc(size);
-
-    return (p);
-}
-
-static void zfree(void *x, void *addr)
-{
-    g_free(addr);
-}
-
-
-#define HEAD_CRC       2
-#define EXTRA_FIELD    4
-#define ORIG_NAME      8
-#define COMMENT                0x10
-#define RESERVED       0xe0
-
-#define DEFLATED       8
-
-/* This is the usual maximum in uboot, so if a uImage overflows this, it would
- * overflow on real hardware too. */
-#define UBOOT_MAX_GUNZIP_BYTES (64 << 20)
-
-static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src,
-                      size_t srclen)
-{
-    z_stream s;
-    ssize_t dstbytes;
-    int r, i, flags;
-
-    /* skip header */
-    i = 10;
-    flags = src[3];
-    if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
-        puts ("Error: Bad gzipped data\n");
-        return -1;
-    }
-    if ((flags & EXTRA_FIELD) != 0)
-        i = 12 + src[10] + (src[11] << 8);
-    if ((flags & ORIG_NAME) != 0)
-        while (src[i++] != 0)
-            ;
-    if ((flags & COMMENT) != 0)
-        while (src[i++] != 0)
-            ;
-    if ((flags & HEAD_CRC) != 0)
-        i += 2;
-    if (i >= srclen) {
-        puts ("Error: gunzip out of data in header\n");
-        return -1;
-    }
-
-    s.zalloc = zalloc;
-    s.zfree = zfree;
-
-    r = inflateInit2(&s, -MAX_WBITS);
-    if (r != Z_OK) {
-        printf ("Error: inflateInit2() returned %d\n", r);
-        return (-1);
-    }
-    s.next_in = src + i;
-    s.avail_in = srclen - i;
-    s.next_out = dst;
-    s.avail_out = dstlen;
-    r = inflate(&s, Z_FINISH);
-    if (r != Z_OK && r != Z_STREAM_END) {
-        printf ("Error: inflate() returned %d\n", r);
-        return -1;
-    }
-    dstbytes = s.next_out - (unsigned char *) dst;
-    inflateEnd(&s);
-
-    return dstbytes;
-}
-
-/* Load a U-Boot image.  */
-int load_uimage(const char *filename, hwaddr *ep,
-                hwaddr *loadaddr, int *is_linux)
-{
-    int fd;
-    int size;
-    uboot_image_header_t h;
-    uboot_image_header_t *hdr = &h;
-    uint8_t *data = NULL;
-    int ret = -1;
-
-    fd = open(filename, O_RDONLY | O_BINARY);
-    if (fd < 0)
-        return -1;
-
-    size = read(fd, hdr, sizeof(uboot_image_header_t));
-    if (size < 0)
-        goto out;
-
-    bswap_uboot_header(hdr);
-
-    if (hdr->ih_magic != IH_MAGIC)
-        goto out;
-
-    /* TODO: Implement other image types.  */
-    if (hdr->ih_type != IH_TYPE_KERNEL) {
-        fprintf(stderr, "Can only load u-boot image type \"kernel\"\n");
-        goto out;
-    }
-
-    switch (hdr->ih_comp) {
-    case IH_COMP_NONE:
-    case IH_COMP_GZIP:
-        break;
-    default:
-        fprintf(stderr,
-                "Unable to load u-boot images with compression type %d\n",
-                hdr->ih_comp);
-        goto out;
-    }
-
-    /* TODO: Check CPU type.  */
-    if (is_linux) {
-        if (hdr->ih_os == IH_OS_LINUX)
-            *is_linux = 1;
-        else
-            *is_linux = 0;
-    }
-
-    *ep = hdr->ih_ep;
-    data = g_malloc(hdr->ih_size);
-
-    if (read(fd, data, hdr->ih_size) != hdr->ih_size) {
-        fprintf(stderr, "Error reading file\n");
-        goto out;
-    }
-
-    if (hdr->ih_comp == IH_COMP_GZIP) {
-        uint8_t *compressed_data;
-        size_t max_bytes;
-        ssize_t bytes;
-
-        compressed_data = data;
-        max_bytes = UBOOT_MAX_GUNZIP_BYTES;
-        data = g_malloc(max_bytes);
-
-        bytes = gunzip(data, max_bytes, compressed_data, hdr->ih_size);
-        g_free(compressed_data);
-        if (bytes < 0) {
-            fprintf(stderr, "Unable to decompress gzipped image!\n");
-            goto out;
-        }
-        hdr->ih_size = bytes;
-    }
-
-    rom_add_blob_fixed(filename, data, hdr->ih_size, hdr->ih_load);
-
-    if (loadaddr)
-        *loadaddr = hdr->ih_load;
-
-    ret = hdr->ih_size;
-
-out:
-    if (data)
-        g_free(data);
-    close(fd);
-    return ret;
-}
-
-/*
- * Functions for reboot-persistent memory regions.
- *  - used for vga bios and option roms.
- *  - also linux kernel (-kernel / -initrd).
- */
-
-typedef struct Rom Rom;
-
-struct Rom {
-    char *name;
-    char *path;
-
-    /* datasize is the amount of memory allocated in "data". If datasize is less
-     * than romsize, it means that the area from datasize to romsize is filled
-     * with zeros.
-     */
-    size_t romsize;
-    size_t datasize;
-
-    uint8_t *data;
-    int isrom;
-    char *fw_dir;
-    char *fw_file;
-
-    hwaddr addr;
-    QTAILQ_ENTRY(Rom) next;
-};
-
-static FWCfgState *fw_cfg;
-static QTAILQ_HEAD(, Rom) roms = QTAILQ_HEAD_INITIALIZER(roms);
-
-static void rom_insert(Rom *rom)
-{
-    Rom *item;
-
-    if (roms_loaded) {
-        hw_error ("ROM images must be loaded at startup\n");
-    }
-
-    /* list is ordered by load address */
-    QTAILQ_FOREACH(item, &roms, next) {
-        if (rom->addr >= item->addr)
-            continue;
-        QTAILQ_INSERT_BEFORE(item, rom, next);
-        return;
-    }
-    QTAILQ_INSERT_TAIL(&roms, rom, next);
-}
-
-int rom_add_file(const char *file, const char *fw_dir,
-                 hwaddr addr, int32_t bootindex)
-{
-    Rom *rom;
-    int rc, fd = -1;
-    char devpath[100];
-
-    rom = g_malloc0(sizeof(*rom));
-    rom->name = g_strdup(file);
-    rom->path = qemu_find_file(QEMU_FILE_TYPE_BIOS, rom->name);
-    if (rom->path == NULL) {
-        rom->path = g_strdup(file);
-    }
-
-    fd = open(rom->path, O_RDONLY | O_BINARY);
-    if (fd == -1) {
-        fprintf(stderr, "Could not open option rom '%s': %s\n",
-                rom->path, strerror(errno));
-        goto err;
-    }
-
-    if (fw_dir) {
-        rom->fw_dir  = g_strdup(fw_dir);
-        rom->fw_file = g_strdup(file);
-    }
-    rom->addr     = addr;
-    rom->romsize  = lseek(fd, 0, SEEK_END);
-    rom->datasize = rom->romsize;
-    rom->data     = g_malloc0(rom->datasize);
-    lseek(fd, 0, SEEK_SET);
-    rc = read(fd, rom->data, rom->datasize);
-    if (rc != rom->datasize) {
-        fprintf(stderr, "rom: file %-20s: read error: rc=%d (expected %zd)\n",
-                rom->name, rc, rom->datasize);
-        goto err;
-    }
-    close(fd);
-    rom_insert(rom);
-    if (rom->fw_file && fw_cfg) {
-        const char *basename;
-        char fw_file_name[56];
-
-        basename = strrchr(rom->fw_file, '/');
-        if (basename) {
-            basename++;
-        } else {
-            basename = rom->fw_file;
-        }
-        snprintf(fw_file_name, sizeof(fw_file_name), "%s/%s", rom->fw_dir,
-                 basename);
-        fw_cfg_add_file(fw_cfg, fw_file_name, rom->data, rom->romsize);
-        snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);
-    } else {
-        snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr);
-    }
-
-    add_boot_device_path(bootindex, NULL, devpath);
-    return 0;
-
-err:
-    if (fd != -1)
-        close(fd);
-    g_free(rom->data);
-    g_free(rom->path);
-    g_free(rom->name);
-    g_free(rom);
-    return -1;
-}
-
-int rom_add_blob(const char *name, const void *blob, size_t len,
-                 hwaddr addr)
-{
-    Rom *rom;
-
-    rom           = g_malloc0(sizeof(*rom));
-    rom->name     = g_strdup(name);
-    rom->addr     = addr;
-    rom->romsize  = len;
-    rom->datasize = len;
-    rom->data     = g_malloc0(rom->datasize);
-    memcpy(rom->data, blob, len);
-    rom_insert(rom);
-    return 0;
-}
-
-/* This function is specific for elf program because we don't need to allocate
- * all the rom. We just allocate the first part and the rest is just zeros. This
- * is why romsize and datasize are different. Also, this function seize the
- * memory ownership of "data", so we don't have to allocate and copy the buffer.
- */
-int rom_add_elf_program(const char *name, void *data, size_t datasize,
-                        size_t romsize, hwaddr addr)
-{
-    Rom *rom;
-
-    rom           = g_malloc0(sizeof(*rom));
-    rom->name     = g_strdup(name);
-    rom->addr     = addr;
-    rom->datasize = datasize;
-    rom->romsize  = romsize;
-    rom->data     = data;
-    rom_insert(rom);
-    return 0;
-}
-
-int rom_add_vga(const char *file)
-{
-    return rom_add_file(file, "vgaroms", 0, -1);
-}
-
-int rom_add_option(const char *file, int32_t bootindex)
-{
-    return rom_add_file(file, "genroms", 0, bootindex);
-}
-
-static void rom_reset(void *unused)
-{
-    Rom *rom;
-
-    QTAILQ_FOREACH(rom, &roms, next) {
-        if (rom->fw_file) {
-            continue;
-        }
-        if (rom->data == NULL) {
-            continue;
-        }
-        cpu_physical_memory_write_rom(rom->addr, rom->data, rom->datasize);
-        if (rom->isrom) {
-            /* rom needs to be written only once */
-            g_free(rom->data);
-            rom->data = NULL;
-        }
-    }
-}
-
-int rom_load_all(void)
-{
-    hwaddr addr = 0;
-    MemoryRegionSection section;
-    Rom *rom;
-
-    QTAILQ_FOREACH(rom, &roms, next) {
-        if (rom->fw_file) {
-            continue;
-        }
-        if (addr > rom->addr) {
-            fprintf(stderr, "rom: requested regions overlap "
-                    "(rom %s. free=0x" TARGET_FMT_plx
-                    ", addr=0x" TARGET_FMT_plx ")\n",
-                    rom->name, addr, rom->addr);
-            return -1;
-        }
-        addr  = rom->addr;
-        addr += rom->romsize;
-        section = memory_region_find(get_system_memory(), rom->addr, 1);
-        rom->isrom = section.size && memory_region_is_rom(section.mr);
-    }
-    qemu_register_reset(rom_reset, NULL);
-    roms_loaded = 1;
-    return 0;
-}
-
-void rom_set_fw(void *f)
-{
-    fw_cfg = f;
-}
-
-static Rom *find_rom(hwaddr addr)
-{
-    Rom *rom;
-
-    QTAILQ_FOREACH(rom, &roms, next) {
-        if (rom->fw_file) {
-            continue;
-        }
-        if (rom->addr > addr) {
-            continue;
-        }
-        if (rom->addr + rom->romsize < addr) {
-            continue;
-        }
-        return rom;
-    }
-    return NULL;
-}
-
-/*
- * Copies memory from registered ROMs to dest. Any memory that is contained in
- * a ROM between addr and addr + size is copied. Note that this can involve
- * multiple ROMs, which need not start at addr and need not end at addr + size.
- */
-int rom_copy(uint8_t *dest, hwaddr addr, size_t size)
-{
-    hwaddr end = addr + size;
-    uint8_t *s, *d = dest;
-    size_t l = 0;
-    Rom *rom;
-
-    QTAILQ_FOREACH(rom, &roms, next) {
-        if (rom->fw_file) {
-            continue;
-        }
-        if (rom->addr + rom->romsize < addr) {
-            continue;
-        }
-        if (rom->addr > end) {
-            break;
-        }
-        if (!rom->data) {
-            continue;
-        }
-
-        d = dest + (rom->addr - addr);
-        s = rom->data;
-        l = rom->datasize;
-
-        if ((d + l) > (dest + size)) {
-            l = dest - d;
-        }
-
-        memcpy(d, s, l);
-
-        if (rom->romsize > rom->datasize) {
-            /* If datasize is less than romsize, it means that we didn't
-             * allocate all the ROM because the trailing data are only zeros.
-             */
-
-            d += l;
-            l = rom->romsize - rom->datasize;
-
-            if ((d + l) > (dest + size)) {
-                /* Rom size doesn't fit in the destination area. Adjust to avoid
-                 * overflow.
-                 */
-                l = dest - d;
-            }
-
-            if (l > 0) {
-                memset(d, 0x0, l);
-            }
-        }
-    }
-
-    return (d + l) - dest;
-}
-
-void *rom_ptr(hwaddr addr)
-{
-    Rom *rom;
-
-    rom = find_rom(addr);
-    if (!rom || !rom->data)
-        return NULL;
-    return rom->data + (addr - rom->addr);
-}
-
-void do_info_roms(Monitor *mon, const QDict *qdict)
-{
-    Rom *rom;
-
-    QTAILQ_FOREACH(rom, &roms, next) {
-        if (!rom->fw_file) {
-            monitor_printf(mon, "addr=" TARGET_FMT_plx
-                           " size=0x%06zx mem=%s name=\"%s\"\n",
-                           rom->addr, rom->romsize,
-                           rom->isrom ? "rom" : "ram",
-                           rom->name);
-        } else {
-            monitor_printf(mon, "fw=%s/%s"
-                           " size=0x%06zx name=\"%s\"\n",
-                           rom->fw_dir,
-                           rom->fw_file,
-                           rom->romsize,
-                           rom->name);
-        }
-    }
-}
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
deleted file mode 100644 (file)
index c601b29..0000000
+++ /dev/null
@@ -1,2136 +0,0 @@
-/*
- * QEMU LSI53C895A SCSI Host Bus Adapter emulation
- *
- * Copyright (c) 2006 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the LGPL.
- */
-
-/* ??? Need to check if the {read,write}[wl] routines work properly on
-   big-endian targets.  */
-
-#include <assert.h>
-
-#include "hw/hw.h"
-#include "hw/pci/pci.h"
-#include "hw/scsi/scsi.h"
-#include "sysemu/dma.h"
-
-//#define DEBUG_LSI
-//#define DEBUG_LSI_REG
-
-#ifdef DEBUG_LSI
-#define DPRINTF(fmt, ...) \
-do { printf("lsi_scsi: " fmt , ## __VA_ARGS__); } while (0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0)
-#endif
-
-#define LSI_MAX_DEVS 7
-
-#define LSI_SCNTL0_TRG    0x01
-#define LSI_SCNTL0_AAP    0x02
-#define LSI_SCNTL0_EPC    0x08
-#define LSI_SCNTL0_WATN   0x10
-#define LSI_SCNTL0_START  0x20
-
-#define LSI_SCNTL1_SST    0x01
-#define LSI_SCNTL1_IARB   0x02
-#define LSI_SCNTL1_AESP   0x04
-#define LSI_SCNTL1_RST    0x08
-#define LSI_SCNTL1_CON    0x10
-#define LSI_SCNTL1_DHP    0x20
-#define LSI_SCNTL1_ADB    0x40
-#define LSI_SCNTL1_EXC    0x80
-
-#define LSI_SCNTL2_WSR    0x01
-#define LSI_SCNTL2_VUE0   0x02
-#define LSI_SCNTL2_VUE1   0x04
-#define LSI_SCNTL2_WSS    0x08
-#define LSI_SCNTL2_SLPHBEN 0x10
-#define LSI_SCNTL2_SLPMD  0x20
-#define LSI_SCNTL2_CHM    0x40
-#define LSI_SCNTL2_SDU    0x80
-
-#define LSI_ISTAT0_DIP    0x01
-#define LSI_ISTAT0_SIP    0x02
-#define LSI_ISTAT0_INTF   0x04
-#define LSI_ISTAT0_CON    0x08
-#define LSI_ISTAT0_SEM    0x10
-#define LSI_ISTAT0_SIGP   0x20
-#define LSI_ISTAT0_SRST   0x40
-#define LSI_ISTAT0_ABRT   0x80
-
-#define LSI_ISTAT1_SI     0x01
-#define LSI_ISTAT1_SRUN   0x02
-#define LSI_ISTAT1_FLSH   0x04
-
-#define LSI_SSTAT0_SDP0   0x01
-#define LSI_SSTAT0_RST    0x02
-#define LSI_SSTAT0_WOA    0x04
-#define LSI_SSTAT0_LOA    0x08
-#define LSI_SSTAT0_AIP    0x10
-#define LSI_SSTAT0_OLF    0x20
-#define LSI_SSTAT0_ORF    0x40
-#define LSI_SSTAT0_ILF    0x80
-
-#define LSI_SIST0_PAR     0x01
-#define LSI_SIST0_RST     0x02
-#define LSI_SIST0_UDC     0x04
-#define LSI_SIST0_SGE     0x08
-#define LSI_SIST0_RSL     0x10
-#define LSI_SIST0_SEL     0x20
-#define LSI_SIST0_CMP     0x40
-#define LSI_SIST0_MA      0x80
-
-#define LSI_SIST1_HTH     0x01
-#define LSI_SIST1_GEN     0x02
-#define LSI_SIST1_STO     0x04
-#define LSI_SIST1_SBMC    0x10
-
-#define LSI_SOCL_IO       0x01
-#define LSI_SOCL_CD       0x02
-#define LSI_SOCL_MSG      0x04
-#define LSI_SOCL_ATN      0x08
-#define LSI_SOCL_SEL      0x10
-#define LSI_SOCL_BSY      0x20
-#define LSI_SOCL_ACK      0x40
-#define LSI_SOCL_REQ      0x80
-
-#define LSI_DSTAT_IID     0x01
-#define LSI_DSTAT_SIR     0x04
-#define LSI_DSTAT_SSI     0x08
-#define LSI_DSTAT_ABRT    0x10
-#define LSI_DSTAT_BF      0x20
-#define LSI_DSTAT_MDPE    0x40
-#define LSI_DSTAT_DFE     0x80
-
-#define LSI_DCNTL_COM     0x01
-#define LSI_DCNTL_IRQD    0x02
-#define LSI_DCNTL_STD     0x04
-#define LSI_DCNTL_IRQM    0x08
-#define LSI_DCNTL_SSM     0x10
-#define LSI_DCNTL_PFEN    0x20
-#define LSI_DCNTL_PFF     0x40
-#define LSI_DCNTL_CLSE    0x80
-
-#define LSI_DMODE_MAN     0x01
-#define LSI_DMODE_BOF     0x02
-#define LSI_DMODE_ERMP    0x04
-#define LSI_DMODE_ERL     0x08
-#define LSI_DMODE_DIOM    0x10
-#define LSI_DMODE_SIOM    0x20
-
-#define LSI_CTEST2_DACK   0x01
-#define LSI_CTEST2_DREQ   0x02
-#define LSI_CTEST2_TEOP   0x04
-#define LSI_CTEST2_PCICIE 0x08
-#define LSI_CTEST2_CM     0x10
-#define LSI_CTEST2_CIO    0x20
-#define LSI_CTEST2_SIGP   0x40
-#define LSI_CTEST2_DDIR   0x80
-
-#define LSI_CTEST5_BL2    0x04
-#define LSI_CTEST5_DDIR   0x08
-#define LSI_CTEST5_MASR   0x10
-#define LSI_CTEST5_DFSN   0x20
-#define LSI_CTEST5_BBCK   0x40
-#define LSI_CTEST5_ADCK   0x80
-
-#define LSI_CCNTL0_DILS   0x01
-#define LSI_CCNTL0_DISFC  0x10
-#define LSI_CCNTL0_ENNDJ  0x20
-#define LSI_CCNTL0_PMJCTL 0x40
-#define LSI_CCNTL0_ENPMJ  0x80
-
-#define LSI_CCNTL1_EN64DBMV  0x01
-#define LSI_CCNTL1_EN64TIBMV 0x02
-#define LSI_CCNTL1_64TIMOD   0x04
-#define LSI_CCNTL1_DDAC      0x08
-#define LSI_CCNTL1_ZMOD      0x80
-
-/* Enable Response to Reselection */
-#define LSI_SCID_RRE      0x60
-
-#define LSI_CCNTL1_40BIT (LSI_CCNTL1_EN64TIBMV|LSI_CCNTL1_64TIMOD)
-
-#define PHASE_DO          0
-#define PHASE_DI          1
-#define PHASE_CMD         2
-#define PHASE_ST          3
-#define PHASE_MO          6
-#define PHASE_MI          7
-#define PHASE_MASK        7
-
-/* Maximum length of MSG IN data.  */
-#define LSI_MAX_MSGIN_LEN 8
-
-/* Flag set if this is a tagged command.  */
-#define LSI_TAG_VALID     (1 << 16)
-
-typedef struct lsi_request {
-    SCSIRequest *req;
-    uint32_t tag;
-    uint32_t dma_len;
-    uint8_t *dma_buf;
-    uint32_t pending;
-    int out;
-    QTAILQ_ENTRY(lsi_request) next;
-} lsi_request;
-
-typedef struct {
-    PCIDevice dev;
-    MemoryRegion mmio_io;
-    MemoryRegion ram_io;
-    MemoryRegion io_io;
-
-    int carry; /* ??? Should this be an a visible register somewhere?  */
-    int status;
-    /* Action to take at the end of a MSG IN phase.
-       0 = COMMAND, 1 = disconnect, 2 = DATA OUT, 3 = DATA IN.  */
-    int msg_action;
-    int msg_len;
-    uint8_t msg[LSI_MAX_MSGIN_LEN];
-    /* 0 if SCRIPTS are running or stopped.
-     * 1 if a Wait Reselect instruction has been issued.
-     * 2 if processing DMA from lsi_execute_script.
-     * 3 if a DMA operation is in progress.  */
-    int waiting;
-    SCSIBus bus;
-    int current_lun;
-    /* The tag is a combination of the device ID and the SCSI tag.  */
-    uint32_t select_tag;
-    int command_complete;
-    QTAILQ_HEAD(, lsi_request) queue;
-    lsi_request *current;
-
-    uint32_t dsa;
-    uint32_t temp;
-    uint32_t dnad;
-    uint32_t dbc;
-    uint8_t istat0;
-    uint8_t istat1;
-    uint8_t dcmd;
-    uint8_t dstat;
-    uint8_t dien;
-    uint8_t sist0;
-    uint8_t sist1;
-    uint8_t sien0;
-    uint8_t sien1;
-    uint8_t mbox0;
-    uint8_t mbox1;
-    uint8_t dfifo;
-    uint8_t ctest2;
-    uint8_t ctest3;
-    uint8_t ctest4;
-    uint8_t ctest5;
-    uint8_t ccntl0;
-    uint8_t ccntl1;
-    uint32_t dsp;
-    uint32_t dsps;
-    uint8_t dmode;
-    uint8_t dcntl;
-    uint8_t scntl0;
-    uint8_t scntl1;
-    uint8_t scntl2;
-    uint8_t scntl3;
-    uint8_t sstat0;
-    uint8_t sstat1;
-    uint8_t scid;
-    uint8_t sxfer;
-    uint8_t socl;
-    uint8_t sdid;
-    uint8_t ssid;
-    uint8_t sfbr;
-    uint8_t stest1;
-    uint8_t stest2;
-    uint8_t stest3;
-    uint8_t sidl;
-    uint8_t stime0;
-    uint8_t respid0;
-    uint8_t respid1;
-    uint32_t mmrs;
-    uint32_t mmws;
-    uint32_t sfs;
-    uint32_t drs;
-    uint32_t sbms;
-    uint32_t dbms;
-    uint32_t dnad64;
-    uint32_t pmjad1;
-    uint32_t pmjad2;
-    uint32_t rbc;
-    uint32_t ua;
-    uint32_t ia;
-    uint32_t sbc;
-    uint32_t csbc;
-    uint32_t scratch[18]; /* SCRATCHA-SCRATCHR */
-    uint8_t sbr;
-
-    /* Script ram is stored as 32-bit words in host byteorder.  */
-    uint32_t script_ram[2048];
-} LSIState;
-
-static inline int lsi_irq_on_rsl(LSIState *s)
-{
-    return (s->sien0 & LSI_SIST0_RSL) && (s->scid & LSI_SCID_RRE);
-}
-
-static void lsi_soft_reset(LSIState *s)
-{
-    DPRINTF("Reset\n");
-    s->carry = 0;
-
-    s->msg_action = 0;
-    s->msg_len = 0;
-    s->waiting = 0;
-    s->dsa = 0;
-    s->dnad = 0;
-    s->dbc = 0;
-    s->temp = 0;
-    memset(s->scratch, 0, sizeof(s->scratch));
-    s->istat0 = 0;
-    s->istat1 = 0;
-    s->dcmd = 0x40;
-    s->dstat = LSI_DSTAT_DFE;
-    s->dien = 0;
-    s->sist0 = 0;
-    s->sist1 = 0;
-    s->sien0 = 0;
-    s->sien1 = 0;
-    s->mbox0 = 0;
-    s->mbox1 = 0;
-    s->dfifo = 0;
-    s->ctest2 = LSI_CTEST2_DACK;
-    s->ctest3 = 0;
-    s->ctest4 = 0;
-    s->ctest5 = 0;
-    s->ccntl0 = 0;
-    s->ccntl1 = 0;
-    s->dsp = 0;
-    s->dsps = 0;
-    s->dmode = 0;
-    s->dcntl = 0;
-    s->scntl0 = 0xc0;
-    s->scntl1 = 0;
-    s->scntl2 = 0;
-    s->scntl3 = 0;
-    s->sstat0 = 0;
-    s->sstat1 = 0;
-    s->scid = 7;
-    s->sxfer = 0;
-    s->socl = 0;
-    s->sdid = 0;
-    s->ssid = 0;
-    s->stest1 = 0;
-    s->stest2 = 0;
-    s->stest3 = 0;
-    s->sidl = 0;
-    s->stime0 = 0;
-    s->respid0 = 0x80;
-    s->respid1 = 0;
-    s->mmrs = 0;
-    s->mmws = 0;
-    s->sfs = 0;
-    s->drs = 0;
-    s->sbms = 0;
-    s->dbms = 0;
-    s->dnad64 = 0;
-    s->pmjad1 = 0;
-    s->pmjad2 = 0;
-    s->rbc = 0;
-    s->ua = 0;
-    s->ia = 0;
-    s->sbc = 0;
-    s->csbc = 0;
-    s->sbr = 0;
-    assert(QTAILQ_EMPTY(&s->queue));
-    assert(!s->current);
-}
-
-static int lsi_dma_40bit(LSIState *s)
-{
-    if ((s->ccntl1 & LSI_CCNTL1_40BIT) == LSI_CCNTL1_40BIT)
-        return 1;
-    return 0;
-}
-
-static int lsi_dma_ti64bit(LSIState *s)
-{
-    if ((s->ccntl1 & LSI_CCNTL1_EN64TIBMV) == LSI_CCNTL1_EN64TIBMV)
-        return 1;
-    return 0;
-}
-
-static int lsi_dma_64bit(LSIState *s)
-{
-    if ((s->ccntl1 & LSI_CCNTL1_EN64DBMV) == LSI_CCNTL1_EN64DBMV)
-        return 1;
-    return 0;
-}
-
-static uint8_t lsi_reg_readb(LSIState *s, int offset);
-static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val);
-static void lsi_execute_script(LSIState *s);
-static void lsi_reselect(LSIState *s, lsi_request *p);
-
-static inline uint32_t read_dword(LSIState *s, uint32_t addr)
-{
-    uint32_t buf;
-
-    pci_dma_read(&s->dev, addr, &buf, 4);
-    return cpu_to_le32(buf);
-}
-
-static void lsi_stop_script(LSIState *s)
-{
-    s->istat1 &= ~LSI_ISTAT1_SRUN;
-}
-
-static void lsi_update_irq(LSIState *s)
-{
-    int level;
-    static int last_level;
-    lsi_request *p;
-
-    /* It's unclear whether the DIP/SIP bits should be cleared when the
-       Interrupt Status Registers are cleared or when istat0 is read.
-       We currently do the formwer, which seems to work.  */
-    level = 0;
-    if (s->dstat) {
-        if (s->dstat & s->dien)
-            level = 1;
-        s->istat0 |= LSI_ISTAT0_DIP;
-    } else {
-        s->istat0 &= ~LSI_ISTAT0_DIP;
-    }
-
-    if (s->sist0 || s->sist1) {
-        if ((s->sist0 & s->sien0) || (s->sist1 & s->sien1))
-            level = 1;
-        s->istat0 |= LSI_ISTAT0_SIP;
-    } else {
-        s->istat0 &= ~LSI_ISTAT0_SIP;
-    }
-    if (s->istat0 & LSI_ISTAT0_INTF)
-        level = 1;
-
-    if (level != last_level) {
-        DPRINTF("Update IRQ level %d dstat %02x sist %02x%02x\n",
-                level, s->dstat, s->sist1, s->sist0);
-        last_level = level;
-    }
-    qemu_set_irq(s->dev.irq[0], level);
-
-    if (!level && lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON)) {
-        DPRINTF("Handled IRQs & disconnected, looking for pending "
-                "processes\n");
-        QTAILQ_FOREACH(p, &s->queue, next) {
-            if (p->pending) {
-                lsi_reselect(s, p);
-                break;
-            }
-        }
-    }
-}
-
-/* Stop SCRIPTS execution and raise a SCSI interrupt.  */
-static void lsi_script_scsi_interrupt(LSIState *s, int stat0, int stat1)
-{
-    uint32_t mask0;
-    uint32_t mask1;
-
-    DPRINTF("SCSI Interrupt 0x%02x%02x prev 0x%02x%02x\n",
-            stat1, stat0, s->sist1, s->sist0);
-    s->sist0 |= stat0;
-    s->sist1 |= stat1;
-    /* Stop processor on fatal or unmasked interrupt.  As a special hack
-       we don't stop processing when raising STO.  Instead continue
-       execution and stop at the next insn that accesses the SCSI bus.  */
-    mask0 = s->sien0 | ~(LSI_SIST0_CMP | LSI_SIST0_SEL | LSI_SIST0_RSL);
-    mask1 = s->sien1 | ~(LSI_SIST1_GEN | LSI_SIST1_HTH);
-    mask1 &= ~LSI_SIST1_STO;
-    if (s->sist0 & mask0 || s->sist1 & mask1) {
-        lsi_stop_script(s);
-    }
-    lsi_update_irq(s);
-}
-
-/* Stop SCRIPTS execution and raise a DMA interrupt.  */
-static void lsi_script_dma_interrupt(LSIState *s, int stat)
-{
-    DPRINTF("DMA Interrupt 0x%x prev 0x%x\n", stat, s->dstat);
-    s->dstat |= stat;
-    lsi_update_irq(s);
-    lsi_stop_script(s);
-}
-
-static inline void lsi_set_phase(LSIState *s, int phase)
-{
-    s->sstat1 = (s->sstat1 & ~PHASE_MASK) | phase;
-}
-
-static void lsi_bad_phase(LSIState *s, int out, int new_phase)
-{
-    /* Trigger a phase mismatch.  */
-    if (s->ccntl0 & LSI_CCNTL0_ENPMJ) {
-        if ((s->ccntl0 & LSI_CCNTL0_PMJCTL)) {
-            s->dsp = out ? s->pmjad1 : s->pmjad2;
-        } else {
-            s->dsp = (s->scntl2 & LSI_SCNTL2_WSR ? s->pmjad2 : s->pmjad1);
-        }
-        DPRINTF("Data phase mismatch jump to %08x\n", s->dsp);
-    } else {
-        DPRINTF("Phase mismatch interrupt\n");
-        lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0);
-        lsi_stop_script(s);
-    }
-    lsi_set_phase(s, new_phase);
-}
-
-
-/* Resume SCRIPTS execution after a DMA operation.  */
-static void lsi_resume_script(LSIState *s)
-{
-    if (s->waiting != 2) {
-        s->waiting = 0;
-        lsi_execute_script(s);
-    } else {
-        s->waiting = 0;
-    }
-}
-
-static void lsi_disconnect(LSIState *s)
-{
-    s->scntl1 &= ~LSI_SCNTL1_CON;
-    s->sstat1 &= ~PHASE_MASK;
-}
-
-static void lsi_bad_selection(LSIState *s, uint32_t id)
-{
-    DPRINTF("Selected absent target %d\n", id);
-    lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO);
-    lsi_disconnect(s);
-}
-
-/* Initiate a SCSI layer data transfer.  */
-static void lsi_do_dma(LSIState *s, int out)
-{
-    uint32_t count;
-    dma_addr_t addr;
-    SCSIDevice *dev;
-
-    assert(s->current);
-    if (!s->current->dma_len) {
-        /* Wait until data is available.  */
-        DPRINTF("DMA no data available\n");
-        return;
-    }
-
-    dev = s->current->req->dev;
-    assert(dev);
-
-    count = s->dbc;
-    if (count > s->current->dma_len)
-        count = s->current->dma_len;
-
-    addr = s->dnad;
-    /* both 40 and Table Indirect 64-bit DMAs store upper bits in dnad64 */
-    if (lsi_dma_40bit(s) || lsi_dma_ti64bit(s))
-        addr |= ((uint64_t)s->dnad64 << 32);
-    else if (s->dbms)
-        addr |= ((uint64_t)s->dbms << 32);
-    else if (s->sbms)
-        addr |= ((uint64_t)s->sbms << 32);
-
-    DPRINTF("DMA addr=0x" DMA_ADDR_FMT " len=%d\n", addr, count);
-    s->csbc += count;
-    s->dnad += count;
-    s->dbc -= count;
-     if (s->current->dma_buf == NULL) {
-        s->current->dma_buf = scsi_req_get_buf(s->current->req);
-    }
-    /* ??? Set SFBR to first data byte.  */
-    if (out) {
-        pci_dma_read(&s->dev, addr, s->current->dma_buf, count);
-    } else {
-        pci_dma_write(&s->dev, addr, s->current->dma_buf, count);
-    }
-    s->current->dma_len -= count;
-    if (s->current->dma_len == 0) {
-        s->current->dma_buf = NULL;
-        scsi_req_continue(s->current->req);
-    } else {
-        s->current->dma_buf += count;
-        lsi_resume_script(s);
-    }
-}
-
-
-/* Add a command to the queue.  */
-static void lsi_queue_command(LSIState *s)
-{
-    lsi_request *p = s->current;
-
-    DPRINTF("Queueing tag=0x%x\n", p->tag);
-    assert(s->current != NULL);
-    assert(s->current->dma_len == 0);
-    QTAILQ_INSERT_TAIL(&s->queue, s->current, next);
-    s->current = NULL;
-
-    p->pending = 0;
-    p->out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
-}
-
-/* Queue a byte for a MSG IN phase.  */
-static void lsi_add_msg_byte(LSIState *s, uint8_t data)
-{
-    if (s->msg_len >= LSI_MAX_MSGIN_LEN) {
-        BADF("MSG IN data too long\n");
-    } else {
-        DPRINTF("MSG IN 0x%02x\n", data);
-        s->msg[s->msg_len++] = data;
-    }
-}
-
-/* Perform reselection to continue a command.  */
-static void lsi_reselect(LSIState *s, lsi_request *p)
-{
-    int id;
-
-    assert(s->current == NULL);
-    QTAILQ_REMOVE(&s->queue, p, next);
-    s->current = p;
-
-    id = (p->tag >> 8) & 0xf;
-    s->ssid = id | 0x80;
-    /* LSI53C700 Family Compatibility, see LSI53C895A 4-73 */
-    if (!(s->dcntl & LSI_DCNTL_COM)) {
-        s->sfbr = 1 << (id & 0x7);
-    }
-    DPRINTF("Reselected target %d\n", id);
-    s->scntl1 |= LSI_SCNTL1_CON;
-    lsi_set_phase(s, PHASE_MI);
-    s->msg_action = p->out ? 2 : 3;
-    s->current->dma_len = p->pending;
-    lsi_add_msg_byte(s, 0x80);
-    if (s->current->tag & LSI_TAG_VALID) {
-        lsi_add_msg_byte(s, 0x20);
-        lsi_add_msg_byte(s, p->tag & 0xff);
-    }
-
-    if (lsi_irq_on_rsl(s)) {
-        lsi_script_scsi_interrupt(s, LSI_SIST0_RSL, 0);
-    }
-}
-
-static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag)
-{
-    lsi_request *p;
-
-    QTAILQ_FOREACH(p, &s->queue, next) {
-        if (p->tag == tag) {
-            return p;
-        }
-    }
-
-    return NULL;
-}
-
-static void lsi_request_free(LSIState *s, lsi_request *p)
-{
-    if (p == s->current) {
-        s->current = NULL;
-    } else {
-        QTAILQ_REMOVE(&s->queue, p, next);
-    }
-    g_free(p);
-}
-
-static void lsi_request_cancelled(SCSIRequest *req)
-{
-    LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
-    lsi_request *p = req->hba_private;
-
-    req->hba_private = NULL;
-    lsi_request_free(s, p);
-    scsi_req_unref(req);
-}
-
-/* Record that data is available for a queued command.  Returns zero if
-   the device was reselected, nonzero if the IO is deferred.  */
-static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len)
-{
-    lsi_request *p = req->hba_private;
-
-    if (p->pending) {
-        BADF("Multiple IO pending for request %p\n", p);
-    }
-    p->pending = len;
-    /* Reselect if waiting for it, or if reselection triggers an IRQ
-       and the bus is free.
-       Since no interrupt stacking is implemented in the emulation, it
-       is also required that there are no pending interrupts waiting
-       for service from the device driver. */
-    if (s->waiting == 1 ||
-        (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON) &&
-         !(s->istat0 & (LSI_ISTAT0_SIP | LSI_ISTAT0_DIP)))) {
-        /* Reselect device.  */
-        lsi_reselect(s, p);
-        return 0;
-    } else {
-        DPRINTF("Queueing IO tag=0x%x\n", p->tag);
-        p->pending = len;
-        return 1;
-    }
-}
-
- /* Callback to indicate that the SCSI layer has completed a command.  */
-static void lsi_command_complete(SCSIRequest *req, uint32_t status, size_t resid)
-{
-    LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
-    int out;
-
-    out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
-    DPRINTF("Command complete status=%d\n", (int)status);
-    s->status = status;
-    s->command_complete = 2;
-    if (s->waiting && s->dbc != 0) {
-        /* Raise phase mismatch for short transfers.  */
-        lsi_bad_phase(s, out, PHASE_ST);
-    } else {
-        lsi_set_phase(s, PHASE_ST);
-    }
-
-    if (req->hba_private == s->current) {
-        req->hba_private = NULL;
-        lsi_request_free(s, s->current);
-        scsi_req_unref(req);
-    }
-    lsi_resume_script(s);
-}
-
- /* Callback to indicate that the SCSI layer has completed a transfer.  */
-static void lsi_transfer_data(SCSIRequest *req, uint32_t len)
-{
-    LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
-    int out;
-
-    assert(req->hba_private);
-    if (s->waiting == 1 || req->hba_private != s->current ||
-        (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) {
-        if (lsi_queue_req(s, req, len)) {
-            return;
-        }
-    }
-
-    out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
-
-    /* host adapter (re)connected */
-    DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, len);
-    s->current->dma_len = len;
-    s->command_complete = 1;
-    if (s->waiting) {
-        if (s->waiting == 1 || s->dbc == 0) {
-            lsi_resume_script(s);
-        } else {
-            lsi_do_dma(s, out);
-        }
-    }
-}
-
-static void lsi_do_command(LSIState *s)
-{
-    SCSIDevice *dev;
-    uint8_t buf[16];
-    uint32_t id;
-    int n;
-
-    DPRINTF("Send command len=%d\n", s->dbc);
-    if (s->dbc > 16)
-        s->dbc = 16;
-    pci_dma_read(&s->dev, s->dnad, buf, s->dbc);
-    s->sfbr = buf[0];
-    s->command_complete = 0;
-
-    id = (s->select_tag >> 8) & 0xf;
-    dev = scsi_device_find(&s->bus, 0, id, s->current_lun);
-    if (!dev) {
-        lsi_bad_selection(s, id);
-        return;
-    }
-
-    assert(s->current == NULL);
-    s->current = g_malloc0(sizeof(lsi_request));
-    s->current->tag = s->select_tag;
-    s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun, buf,
-                                   s->current);
-
-    n = scsi_req_enqueue(s->current->req);
-    if (n) {
-        if (n > 0) {
-            lsi_set_phase(s, PHASE_DI);
-        } else if (n < 0) {
-            lsi_set_phase(s, PHASE_DO);
-        }
-        scsi_req_continue(s->current->req);
-    }
-    if (!s->command_complete) {
-        if (n) {
-            /* Command did not complete immediately so disconnect.  */
-            lsi_add_msg_byte(s, 2); /* SAVE DATA POINTER */
-            lsi_add_msg_byte(s, 4); /* DISCONNECT */
-            /* wait data */
-            lsi_set_phase(s, PHASE_MI);
-            s->msg_action = 1;
-            lsi_queue_command(s);
-        } else {
-            /* wait command complete */
-            lsi_set_phase(s, PHASE_DI);
-        }
-    }
-}
-
-static void lsi_do_status(LSIState *s)
-{
-    uint8_t status;
-    DPRINTF("Get status len=%d status=%d\n", s->dbc, s->status);
-    if (s->dbc != 1)
-        BADF("Bad Status move\n");
-    s->dbc = 1;
-    status = s->status;
-    s->sfbr = status;
-    pci_dma_write(&s->dev, s->dnad, &status, 1);
-    lsi_set_phase(s, PHASE_MI);
-    s->msg_action = 1;
-    lsi_add_msg_byte(s, 0); /* COMMAND COMPLETE */
-}
-
-static void lsi_do_msgin(LSIState *s)
-{
-    int len;
-    DPRINTF("Message in len=%d/%d\n", s->dbc, s->msg_len);
-    s->sfbr = s->msg[0];
-    len = s->msg_len;
-    if (len > s->dbc)
-        len = s->dbc;
-    pci_dma_write(&s->dev, s->dnad, s->msg, len);
-    /* Linux drivers rely on the last byte being in the SIDL.  */
-    s->sidl = s->msg[len - 1];
-    s->msg_len -= len;
-    if (s->msg_len) {
-        memmove(s->msg, s->msg + len, s->msg_len);
-    } else {
-        /* ??? Check if ATN (not yet implemented) is asserted and maybe
-           switch to PHASE_MO.  */
-        switch (s->msg_action) {
-        case 0:
-            lsi_set_phase(s, PHASE_CMD);
-            break;
-        case 1:
-            lsi_disconnect(s);
-            break;
-        case 2:
-            lsi_set_phase(s, PHASE_DO);
-            break;
-        case 3:
-            lsi_set_phase(s, PHASE_DI);
-            break;
-        default:
-            abort();
-        }
-    }
-}
-
-/* Read the next byte during a MSGOUT phase.  */
-static uint8_t lsi_get_msgbyte(LSIState *s)
-{
-    uint8_t data;
-    pci_dma_read(&s->dev, s->dnad, &data, 1);
-    s->dnad++;
-    s->dbc--;
-    return data;
-}
-
-/* Skip the next n bytes during a MSGOUT phase. */
-static void lsi_skip_msgbytes(LSIState *s, unsigned int n)
-{
-    s->dnad += n;
-    s->dbc  -= n;
-}
-
-static void lsi_do_msgout(LSIState *s)
-{
-    uint8_t msg;
-    int len;
-    uint32_t current_tag;
-    lsi_request *current_req, *p, *p_next;
-
-    if (s->current) {
-        current_tag = s->current->tag;
-        current_req = s->current;
-    } else {
-        current_tag = s->select_tag;
-        current_req = lsi_find_by_tag(s, current_tag);
-    }
-
-    DPRINTF("MSG out len=%d\n", s->dbc);
-    while (s->dbc) {
-        msg = lsi_get_msgbyte(s);
-        s->sfbr = msg;
-
-        switch (msg) {
-        case 0x04:
-            DPRINTF("MSG: Disconnect\n");
-            lsi_disconnect(s);
-            break;
-        case 0x08:
-            DPRINTF("MSG: No Operation\n");
-            lsi_set_phase(s, PHASE_CMD);
-            break;
-        case 0x01:
-            len = lsi_get_msgbyte(s);
-            msg = lsi_get_msgbyte(s);
-            (void)len; /* avoid a warning about unused variable*/
-            DPRINTF("Extended message 0x%x (len %d)\n", msg, len);
-            switch (msg) {
-            case 1:
-                DPRINTF("SDTR (ignored)\n");
-                lsi_skip_msgbytes(s, 2);
-                break;
-            case 3:
-                DPRINTF("WDTR (ignored)\n");
-                lsi_skip_msgbytes(s, 1);
-                break;
-            default:
-                goto bad;
-            }
-            break;
-        case 0x20: /* SIMPLE queue */
-            s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID;
-            DPRINTF("SIMPLE queue tag=0x%x\n", s->select_tag & 0xff);
-            break;
-        case 0x21: /* HEAD of queue */
-            BADF("HEAD queue not implemented\n");
-            s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID;
-            break;
-        case 0x22: /* ORDERED queue */
-            BADF("ORDERED queue not implemented\n");
-            s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID;
-            break;
-        case 0x0d:
-            /* The ABORT TAG message clears the current I/O process only. */
-            DPRINTF("MSG: ABORT TAG tag=0x%x\n", current_tag);
-            if (current_req) {
-                scsi_req_cancel(current_req->req);
-            }
-            lsi_disconnect(s);
-            break;
-        case 0x06:
-        case 0x0e:
-        case 0x0c:
-            /* The ABORT message clears all I/O processes for the selecting
-               initiator on the specified logical unit of the target. */
-            if (msg == 0x06) {
-                DPRINTF("MSG: ABORT tag=0x%x\n", current_tag);
-            }
-            /* The CLEAR QUEUE message clears all I/O processes for all
-               initiators on the specified logical unit of the target. */
-            if (msg == 0x0e) {
-                DPRINTF("MSG: CLEAR QUEUE tag=0x%x\n", current_tag);
-            }
-            /* The BUS DEVICE RESET message clears all I/O processes for all
-               initiators on all logical units of the target. */
-            if (msg == 0x0c) {
-                DPRINTF("MSG: BUS DEVICE RESET tag=0x%x\n", current_tag);
-            }
-
-            /* clear the current I/O process */
-            if (s->current) {
-                scsi_req_cancel(s->current->req);
-            }
-
-            /* As the current implemented devices scsi_disk and scsi_generic
-               only support one LUN, we don't need to keep track of LUNs.
-               Clearing I/O processes for other initiators could be possible
-               for scsi_generic by sending a SG_SCSI_RESET to the /dev/sgX
-               device, but this is currently not implemented (and seems not
-               to be really necessary). So let's simply clear all queued
-               commands for the current device: */
-            QTAILQ_FOREACH_SAFE(p, &s->queue, next, p_next) {
-                if ((p->tag & 0x0000ff00) == (current_tag & 0x0000ff00)) {
-                    scsi_req_cancel(p->req);
-                }
-            }
-
-            lsi_disconnect(s);
-            break;
-        default:
-            if ((msg & 0x80) == 0) {
-                goto bad;
-            }
-            s->current_lun = msg & 7;
-            DPRINTF("Select LUN %d\n", s->current_lun);
-            lsi_set_phase(s, PHASE_CMD);
-            break;
-        }
-    }
-    return;
-bad:
-    BADF("Unimplemented message 0x%02x\n", msg);
-    lsi_set_phase(s, PHASE_MI);
-    lsi_add_msg_byte(s, 7); /* MESSAGE REJECT */
-    s->msg_action = 0;
-}
-
-/* Sign extend a 24-bit value.  */
-static inline int32_t sxt24(int32_t n)
-{
-    return (n << 8) >> 8;
-}
-
-#define LSI_BUF_SIZE 4096
-static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count)
-{
-    int n;
-    uint8_t buf[LSI_BUF_SIZE];
-
-    DPRINTF("memcpy dest 0x%08x src 0x%08x count %d\n", dest, src, count);
-    while (count) {
-        n = (count > LSI_BUF_SIZE) ? LSI_BUF_SIZE : count;
-        pci_dma_read(&s->dev, src, buf, n);
-        pci_dma_write(&s->dev, dest, buf, n);
-        src += n;
-        dest += n;
-        count -= n;
-    }
-}
-
-static void lsi_wait_reselect(LSIState *s)
-{
-    lsi_request *p;
-
-    DPRINTF("Wait Reselect\n");
-
-    QTAILQ_FOREACH(p, &s->queue, next) {
-        if (p->pending) {
-            lsi_reselect(s, p);
-            break;
-        }
-    }
-    if (s->current == NULL) {
-        s->waiting = 1;
-    }
-}
-
-static void lsi_execute_script(LSIState *s)
-{
-    uint32_t insn;
-    uint32_t addr, addr_high;
-    int opcode;
-    int insn_processed = 0;
-
-    s->istat1 |= LSI_ISTAT1_SRUN;
-again:
-    insn_processed++;
-    insn = read_dword(s, s->dsp);
-    if (!insn) {
-        /* If we receive an empty opcode increment the DSP by 4 bytes
-           instead of 8 and execute the next opcode at that location */
-        s->dsp += 4;
-        goto again;
-    }
-    addr = read_dword(s, s->dsp + 4);
-    addr_high = 0;
-    DPRINTF("SCRIPTS dsp=%08x opcode %08x arg %08x\n", s->dsp, insn, addr);
-    s->dsps = addr;
-    s->dcmd = insn >> 24;
-    s->dsp += 8;
-    switch (insn >> 30) {
-    case 0: /* Block move.  */
-        if (s->sist1 & LSI_SIST1_STO) {
-            DPRINTF("Delayed select timeout\n");
-            lsi_stop_script(s);
-            break;
-        }
-        s->dbc = insn & 0xffffff;
-        s->rbc = s->dbc;
-        /* ??? Set ESA.  */
-        s->ia = s->dsp - 8;
-        if (insn & (1 << 29)) {
-            /* Indirect addressing.  */
-            addr = read_dword(s, addr);
-        } else if (insn & (1 << 28)) {
-            uint32_t buf[2];
-            int32_t offset;
-            /* Table indirect addressing.  */
-
-            /* 32-bit Table indirect */
-            offset = sxt24(addr);
-            pci_dma_read(&s->dev, s->dsa + offset, buf, 8);
-            /* byte count is stored in bits 0:23 only */
-            s->dbc = cpu_to_le32(buf[0]) & 0xffffff;
-            s->rbc = s->dbc;
-            addr = cpu_to_le32(buf[1]);
-
-            /* 40-bit DMA, upper addr bits [39:32] stored in first DWORD of
-             * table, bits [31:24] */
-            if (lsi_dma_40bit(s))
-                addr_high = cpu_to_le32(buf[0]) >> 24;
-            else if (lsi_dma_ti64bit(s)) {
-                int selector = (cpu_to_le32(buf[0]) >> 24) & 0x1f;
-                switch (selector) {
-                case 0 ... 0x0f:
-                    /* offset index into scratch registers since
-                     * TI64 mode can use registers C to R */
-                    addr_high = s->scratch[2 + selector];
-                    break;
-                case 0x10:
-                    addr_high = s->mmrs;
-                    break;
-                case 0x11:
-                    addr_high = s->mmws;
-                    break;
-                case 0x12:
-                    addr_high = s->sfs;
-                    break;
-                case 0x13:
-                    addr_high = s->drs;
-                    break;
-                case 0x14:
-                    addr_high = s->sbms;
-                    break;
-                case 0x15:
-                    addr_high = s->dbms;
-                    break;
-                default:
-                    BADF("Illegal selector specified (0x%x > 0x15)"
-                         " for 64-bit DMA block move", selector);
-                    break;
-                }
-            }
-        } else if (lsi_dma_64bit(s)) {
-            /* fetch a 3rd dword if 64-bit direct move is enabled and
-               only if we're not doing table indirect or indirect addressing */
-            s->dbms = read_dword(s, s->dsp);
-            s->dsp += 4;
-            s->ia = s->dsp - 12;
-        }
-        if ((s->sstat1 & PHASE_MASK) != ((insn >> 24) & 7)) {
-            DPRINTF("Wrong phase got %d expected %d\n",
-                    s->sstat1 & PHASE_MASK, (insn >> 24) & 7);
-            lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0);
-            break;
-        }
-        s->dnad = addr;
-        s->dnad64 = addr_high;
-        switch (s->sstat1 & 0x7) {
-        case PHASE_DO:
-            s->waiting = 2;
-            lsi_do_dma(s, 1);
-            if (s->waiting)
-                s->waiting = 3;
-            break;
-        case PHASE_DI:
-            s->waiting = 2;
-            lsi_do_dma(s, 0);
-            if (s->waiting)
-                s->waiting = 3;
-            break;
-        case PHASE_CMD:
-            lsi_do_command(s);
-            break;
-        case PHASE_ST:
-            lsi_do_status(s);
-            break;
-        case PHASE_MO:
-            lsi_do_msgout(s);
-            break;
-        case PHASE_MI:
-            lsi_do_msgin(s);
-            break;
-        default:
-            BADF("Unimplemented phase %d\n", s->sstat1 & PHASE_MASK);
-            exit(1);
-        }
-        s->dfifo = s->dbc & 0xff;
-        s->ctest5 = (s->ctest5 & 0xfc) | ((s->dbc >> 8) & 3);
-        s->sbc = s->dbc;
-        s->rbc -= s->dbc;
-        s->ua = addr + s->dbc;
-        break;
-
-    case 1: /* IO or Read/Write instruction.  */
-        opcode = (insn >> 27) & 7;
-        if (opcode < 5) {
-            uint32_t id;
-
-            if (insn & (1 << 25)) {
-                id = read_dword(s, s->dsa + sxt24(insn));
-            } else {
-                id = insn;
-            }
-            id = (id >> 16) & 0xf;
-            if (insn & (1 << 26)) {
-                addr = s->dsp + sxt24(addr);
-            }
-            s->dnad = addr;
-            switch (opcode) {
-            case 0: /* Select */
-                s->sdid = id;
-                if (s->scntl1 & LSI_SCNTL1_CON) {
-                    DPRINTF("Already reselected, jumping to alternative address\n");
-                    s->dsp = s->dnad;
-                    break;
-                }
-                s->sstat0 |= LSI_SSTAT0_WOA;
-                s->scntl1 &= ~LSI_SCNTL1_IARB;
-                if (!scsi_device_find(&s->bus, 0, id, 0)) {
-                    lsi_bad_selection(s, id);
-                    break;
-                }
-                DPRINTF("Selected target %d%s\n",
-                        id, insn & (1 << 3) ? " ATN" : "");
-                /* ??? Linux drivers compain when this is set.  Maybe
-                   it only applies in low-level mode (unimplemented).
-                lsi_script_scsi_interrupt(s, LSI_SIST0_CMP, 0); */
-                s->select_tag = id << 8;
-                s->scntl1 |= LSI_SCNTL1_CON;
-                if (insn & (1 << 3)) {
-                    s->socl |= LSI_SOCL_ATN;
-                }
-                lsi_set_phase(s, PHASE_MO);
-                break;
-            case 1: /* Disconnect */
-                DPRINTF("Wait Disconnect\n");
-                s->scntl1 &= ~LSI_SCNTL1_CON;
-                break;
-            case 2: /* Wait Reselect */
-                if (!lsi_irq_on_rsl(s)) {
-                    lsi_wait_reselect(s);
-                }
-                break;
-            case 3: /* Set */
-                DPRINTF("Set%s%s%s%s\n",
-                        insn & (1 << 3) ? " ATN" : "",
-                        insn & (1 << 6) ? " ACK" : "",
-                        insn & (1 << 9) ? " TM" : "",
-                        insn & (1 << 10) ? " CC" : "");
-                if (insn & (1 << 3)) {
-                    s->socl |= LSI_SOCL_ATN;
-                    lsi_set_phase(s, PHASE_MO);
-                }
-                if (insn & (1 << 9)) {
-                    BADF("Target mode not implemented\n");
-                    exit(1);
-                }
-                if (insn & (1 << 10))
-                    s->carry = 1;
-                break;
-            case 4: /* Clear */
-                DPRINTF("Clear%s%s%s%s\n",
-                        insn & (1 << 3) ? " ATN" : "",
-                        insn & (1 << 6) ? " ACK" : "",
-                        insn & (1 << 9) ? " TM" : "",
-                        insn & (1 << 10) ? " CC" : "");
-                if (insn & (1 << 3)) {
-                    s->socl &= ~LSI_SOCL_ATN;
-                }
-                if (insn & (1 << 10))
-                    s->carry = 0;
-                break;
-            }
-        } else {
-            uint8_t op0;
-            uint8_t op1;
-            uint8_t data8;
-            int reg;
-            int operator;
-#ifdef DEBUG_LSI
-            static const char *opcode_names[3] =
-                {"Write", "Read", "Read-Modify-Write"};
-            static const char *operator_names[8] =
-                {"MOV", "SHL", "OR", "XOR", "AND", "SHR", "ADD", "ADC"};
-#endif
-
-            reg = ((insn >> 16) & 0x7f) | (insn & 0x80);
-            data8 = (insn >> 8) & 0xff;
-            opcode = (insn >> 27) & 7;
-            operator = (insn >> 24) & 7;
-            DPRINTF("%s reg 0x%x %s data8=0x%02x sfbr=0x%02x%s\n",
-                    opcode_names[opcode - 5], reg,
-                    operator_names[operator], data8, s->sfbr,
-                    (insn & (1 << 23)) ? " SFBR" : "");
-            op0 = op1 = 0;
-            switch (opcode) {
-            case 5: /* From SFBR */
-                op0 = s->sfbr;
-                op1 = data8;
-                break;
-            case 6: /* To SFBR */
-                if (operator)
-                    op0 = lsi_reg_readb(s, reg);
-                op1 = data8;
-                break;
-            case 7: /* Read-modify-write */
-                if (operator)
-                    op0 = lsi_reg_readb(s, reg);
-                if (insn & (1 << 23)) {
-                    op1 = s->sfbr;
-                } else {
-                    op1 = data8;
-                }
-                break;
-            }
-
-            switch (operator) {
-            case 0: /* move */
-                op0 = op1;
-                break;
-            case 1: /* Shift left */
-                op1 = op0 >> 7;
-                op0 = (op0 << 1) | s->carry;
-                s->carry = op1;
-                break;
-            case 2: /* OR */
-                op0 |= op1;
-                break;
-            case 3: /* XOR */
-                op0 ^= op1;
-                break;
-            case 4: /* AND */
-                op0 &= op1;
-                break;
-            case 5: /* SHR */
-                op1 = op0 & 1;
-                op0 = (op0 >> 1) | (s->carry << 7);
-                s->carry = op1;
-                break;
-            case 6: /* ADD */
-                op0 += op1;
-                s->carry = op0 < op1;
-                break;
-            case 7: /* ADC */
-                op0 += op1 + s->carry;
-                if (s->carry)
-                    s->carry = op0 <= op1;
-                else
-                    s->carry = op0 < op1;
-                break;
-            }
-
-            switch (opcode) {
-            case 5: /* From SFBR */
-            case 7: /* Read-modify-write */
-                lsi_reg_writeb(s, reg, op0);
-                break;
-            case 6: /* To SFBR */
-                s->sfbr = op0;
-                break;
-            }
-        }
-        break;
-
-    case 2: /* Transfer Control.  */
-        {
-            int cond;
-            int jmp;
-
-            if ((insn & 0x002e0000) == 0) {
-                DPRINTF("NOP\n");
-                break;
-            }
-            if (s->sist1 & LSI_SIST1_STO) {
-                DPRINTF("Delayed select timeout\n");
-                lsi_stop_script(s);
-                break;
-            }
-            cond = jmp = (insn & (1 << 19)) != 0;
-            if (cond == jmp && (insn & (1 << 21))) {
-                DPRINTF("Compare carry %d\n", s->carry == jmp);
-                cond = s->carry != 0;
-            }
-            if (cond == jmp && (insn & (1 << 17))) {
-                DPRINTF("Compare phase %d %c= %d\n",
-                        (s->sstat1 & PHASE_MASK),
-                        jmp ? '=' : '!',
-                        ((insn >> 24) & 7));
-                cond = (s->sstat1 & PHASE_MASK) == ((insn >> 24) & 7);
-            }
-            if (cond == jmp && (insn & (1 << 18))) {
-                uint8_t mask;
-
-                mask = (~insn >> 8) & 0xff;
-                DPRINTF("Compare data 0x%x & 0x%x %c= 0x%x\n",
-                        s->sfbr, mask, jmp ? '=' : '!', insn & mask);
-                cond = (s->sfbr & mask) == (insn & mask);
-            }
-            if (cond == jmp) {
-                if (insn & (1 << 23)) {
-                    /* Relative address.  */
-                    addr = s->dsp + sxt24(addr);
-                }
-                switch ((insn >> 27) & 7) {
-                case 0: /* Jump */
-                    DPRINTF("Jump to 0x%08x\n", addr);
-                    s->dsp = addr;
-                    break;
-                case 1: /* Call */
-                    DPRINTF("Call 0x%08x\n", addr);
-                    s->temp = s->dsp;
-                    s->dsp = addr;
-                    break;
-                case 2: /* Return */
-                    DPRINTF("Return to 0x%08x\n", s->temp);
-                    s->dsp = s->temp;
-                    break;
-                case 3: /* Interrupt */
-                    DPRINTF("Interrupt 0x%08x\n", s->dsps);
-                    if ((insn & (1 << 20)) != 0) {
-                        s->istat0 |= LSI_ISTAT0_INTF;
-                        lsi_update_irq(s);
-                    } else {
-                        lsi_script_dma_interrupt(s, LSI_DSTAT_SIR);
-                    }
-                    break;
-                default:
-                    DPRINTF("Illegal transfer control\n");
-                    lsi_script_dma_interrupt(s, LSI_DSTAT_IID);
-                    break;
-                }
-            } else {
-                DPRINTF("Control condition failed\n");
-            }
-        }
-        break;
-
-    case 3:
-        if ((insn & (1 << 29)) == 0) {
-            /* Memory move.  */
-            uint32_t dest;
-            /* ??? The docs imply the destination address is loaded into
-               the TEMP register.  However the Linux drivers rely on
-               the value being presrved.  */
-            dest = read_dword(s, s->dsp);
-            s->dsp += 4;
-            lsi_memcpy(s, dest, addr, insn & 0xffffff);
-        } else {
-            uint8_t data[7];
-            int reg;
-            int n;
-            int i;
-
-            if (insn & (1 << 28)) {
-                addr = s->dsa + sxt24(addr);
-            }
-            n = (insn & 7);
-            reg = (insn >> 16) & 0xff;
-            if (insn & (1 << 24)) {
-                pci_dma_read(&s->dev, addr, data, n);
-                DPRINTF("Load reg 0x%x size %d addr 0x%08x = %08x\n", reg, n,
-                        addr, *(int *)data);
-                for (i = 0; i < n; i++) {
-                    lsi_reg_writeb(s, reg + i, data[i]);
-                }
-            } else {
-                DPRINTF("Store reg 0x%x size %d addr 0x%08x\n", reg, n, addr);
-                for (i = 0; i < n; i++) {
-                    data[i] = lsi_reg_readb(s, reg + i);
-                }
-                pci_dma_write(&s->dev, addr, data, n);
-            }
-        }
-    }
-    if (insn_processed > 10000 && !s->waiting) {
-        /* Some windows drivers make the device spin waiting for a memory
-           location to change.  If we have been executed a lot of code then
-           assume this is the case and force an unexpected device disconnect.
-           This is apparently sufficient to beat the drivers into submission.
-         */
-        if (!(s->sien0 & LSI_SIST0_UDC))
-            fprintf(stderr, "inf. loop with UDC masked\n");
-        lsi_script_scsi_interrupt(s, LSI_SIST0_UDC, 0);
-        lsi_disconnect(s);
-    } else if (s->istat1 & LSI_ISTAT1_SRUN && !s->waiting) {
-        if (s->dcntl & LSI_DCNTL_SSM) {
-            lsi_script_dma_interrupt(s, LSI_DSTAT_SSI);
-        } else {
-            goto again;
-        }
-    }
-    DPRINTF("SCRIPTS execution stopped\n");
-}
-
-static uint8_t lsi_reg_readb(LSIState *s, int offset)
-{
-    uint8_t tmp;
-#define CASE_GET_REG24(name, addr) \
-    case addr: return s->name & 0xff; \
-    case addr + 1: return (s->name >> 8) & 0xff; \
-    case addr + 2: return (s->name >> 16) & 0xff;
-
-#define CASE_GET_REG32(name, addr) \
-    case addr: return s->name & 0xff; \
-    case addr + 1: return (s->name >> 8) & 0xff; \
-    case addr + 2: return (s->name >> 16) & 0xff; \
-    case addr + 3: return (s->name >> 24) & 0xff;
-
-#ifdef DEBUG_LSI_REG
-    DPRINTF("Read reg %x\n", offset);
-#endif
-    switch (offset) {
-    case 0x00: /* SCNTL0 */
-        return s->scntl0;
-    case 0x01: /* SCNTL1 */
-        return s->scntl1;
-    case 0x02: /* SCNTL2 */
-        return s->scntl2;
-    case 0x03: /* SCNTL3 */
-        return s->scntl3;
-    case 0x04: /* SCID */
-        return s->scid;
-    case 0x05: /* SXFER */
-        return s->sxfer;
-    case 0x06: /* SDID */
-        return s->sdid;
-    case 0x07: /* GPREG0 */
-        return 0x7f;
-    case 0x08: /* Revision ID */
-        return 0x00;
-    case 0xa: /* SSID */
-        return s->ssid;
-    case 0xb: /* SBCL */
-        /* ??? This is not correct. However it's (hopefully) only
-           used for diagnostics, so should be ok.  */
-        return 0;
-    case 0xc: /* DSTAT */
-        tmp = s->dstat | 0x80;
-        if ((s->istat0 & LSI_ISTAT0_INTF) == 0)
-            s->dstat = 0;
-        lsi_update_irq(s);
-        return tmp;
-    case 0x0d: /* SSTAT0 */
-        return s->sstat0;
-    case 0x0e: /* SSTAT1 */
-        return s->sstat1;
-    case 0x0f: /* SSTAT2 */
-        return s->scntl1 & LSI_SCNTL1_CON ? 0 : 2;
-    CASE_GET_REG32(dsa, 0x10)
-    case 0x14: /* ISTAT0 */
-        return s->istat0;
-    case 0x15: /* ISTAT1 */
-        return s->istat1;
-    case 0x16: /* MBOX0 */
-        return s->mbox0;
-    case 0x17: /* MBOX1 */
-        return s->mbox1;
-    case 0x18: /* CTEST0 */
-        return 0xff;
-    case 0x19: /* CTEST1 */
-        return 0;
-    case 0x1a: /* CTEST2 */
-        tmp = s->ctest2 | LSI_CTEST2_DACK | LSI_CTEST2_CM;
-        if (s->istat0 & LSI_ISTAT0_SIGP) {
-            s->istat0 &= ~LSI_ISTAT0_SIGP;
-            tmp |= LSI_CTEST2_SIGP;
-        }
-        return tmp;
-    case 0x1b: /* CTEST3 */
-        return s->ctest3;
-    CASE_GET_REG32(temp, 0x1c)
-    case 0x20: /* DFIFO */
-        return 0;
-    case 0x21: /* CTEST4 */
-        return s->ctest4;
-    case 0x22: /* CTEST5 */
-        return s->ctest5;
-    case 0x23: /* CTEST6 */
-         return 0;
-    CASE_GET_REG24(dbc, 0x24)
-    case 0x27: /* DCMD */
-        return s->dcmd;
-    CASE_GET_REG32(dnad, 0x28)
-    CASE_GET_REG32(dsp, 0x2c)
-    CASE_GET_REG32(dsps, 0x30)
-    CASE_GET_REG32(scratch[0], 0x34)
-    case 0x38: /* DMODE */
-        return s->dmode;
-    case 0x39: /* DIEN */
-        return s->dien;
-    case 0x3a: /* SBR */
-        return s->sbr;
-    case 0x3b: /* DCNTL */
-        return s->dcntl;
-    case 0x40: /* SIEN0 */
-        return s->sien0;
-    case 0x41: /* SIEN1 */
-        return s->sien1;
-    case 0x42: /* SIST0 */
-        tmp = s->sist0;
-        s->sist0 = 0;
-        lsi_update_irq(s);
-        return tmp;
-    case 0x43: /* SIST1 */
-        tmp = s->sist1;
-        s->sist1 = 0;
-        lsi_update_irq(s);
-        return tmp;
-    case 0x46: /* MACNTL */
-        return 0x0f;
-    case 0x47: /* GPCNTL0 */
-        return 0x0f;
-    case 0x48: /* STIME0 */
-        return s->stime0;
-    case 0x4a: /* RESPID0 */
-        return s->respid0;
-    case 0x4b: /* RESPID1 */
-        return s->respid1;
-    case 0x4d: /* STEST1 */
-        return s->stest1;
-    case 0x4e: /* STEST2 */
-        return s->stest2;
-    case 0x4f: /* STEST3 */
-        return s->stest3;
-    case 0x50: /* SIDL */
-        /* This is needed by the linux drivers.  We currently only update it
-           during the MSG IN phase.  */
-        return s->sidl;
-    case 0x52: /* STEST4 */
-        return 0xe0;
-    case 0x56: /* CCNTL0 */
-        return s->ccntl0;
-    case 0x57: /* CCNTL1 */
-        return s->ccntl1;
-    case 0x58: /* SBDL */
-        /* Some drivers peek at the data bus during the MSG IN phase.  */
-        if ((s->sstat1 & PHASE_MASK) == PHASE_MI)
-            return s->msg[0];
-        return 0;
-    case 0x59: /* SBDL high */
-        return 0;
-    CASE_GET_REG32(mmrs, 0xa0)
-    CASE_GET_REG32(mmws, 0xa4)
-    CASE_GET_REG32(sfs, 0xa8)
-    CASE_GET_REG32(drs, 0xac)
-    CASE_GET_REG32(sbms, 0xb0)
-    CASE_GET_REG32(dbms, 0xb4)
-    CASE_GET_REG32(dnad64, 0xb8)
-    CASE_GET_REG32(pmjad1, 0xc0)
-    CASE_GET_REG32(pmjad2, 0xc4)
-    CASE_GET_REG32(rbc, 0xc8)
-    CASE_GET_REG32(ua, 0xcc)
-    CASE_GET_REG32(ia, 0xd4)
-    CASE_GET_REG32(sbc, 0xd8)
-    CASE_GET_REG32(csbc, 0xdc)
-    }
-    if (offset >= 0x5c && offset < 0xa0) {
-        int n;
-        int shift;
-        n = (offset - 0x58) >> 2;
-        shift = (offset & 3) * 8;
-        return (s->scratch[n] >> shift) & 0xff;
-    }
-    BADF("readb 0x%x\n", offset);
-    exit(1);
-#undef CASE_GET_REG24
-#undef CASE_GET_REG32
-}
-
-static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
-{
-#define CASE_SET_REG24(name, addr) \
-    case addr    : s->name &= 0xffffff00; s->name |= val;       break; \
-    case addr + 1: s->name &= 0xffff00ff; s->name |= val << 8;  break; \
-    case addr + 2: s->name &= 0xff00ffff; s->name |= val << 16; break;
-
-#define CASE_SET_REG32(name, addr) \
-    case addr    : s->name &= 0xffffff00; s->name |= val;       break; \
-    case addr + 1: s->name &= 0xffff00ff; s->name |= val << 8;  break; \
-    case addr + 2: s->name &= 0xff00ffff; s->name |= val << 16; break; \
-    case addr + 3: s->name &= 0x00ffffff; s->name |= val << 24; break;
-
-#ifdef DEBUG_LSI_REG
-    DPRINTF("Write reg %x = %02x\n", offset, val);
-#endif
-    switch (offset) {
-    case 0x00: /* SCNTL0 */
-        s->scntl0 = val;
-        if (val & LSI_SCNTL0_START) {
-            BADF("Start sequence not implemented\n");
-        }
-        break;
-    case 0x01: /* SCNTL1 */
-        s->scntl1 = val & ~LSI_SCNTL1_SST;
-        if (val & LSI_SCNTL1_IARB) {
-            BADF("Immediate Arbritration not implemented\n");
-        }
-        if (val & LSI_SCNTL1_RST) {
-            if (!(s->sstat0 & LSI_SSTAT0_RST)) {
-                qbus_reset_all(&s->bus.qbus);
-                s->sstat0 |= LSI_SSTAT0_RST;
-                lsi_script_scsi_interrupt(s, LSI_SIST0_RST, 0);
-            }
-        } else {
-            s->sstat0 &= ~LSI_SSTAT0_RST;
-        }
-        break;
-    case 0x02: /* SCNTL2 */
-        val &= ~(LSI_SCNTL2_WSR | LSI_SCNTL2_WSS);
-        s->scntl2 = val;
-        break;
-    case 0x03: /* SCNTL3 */
-        s->scntl3 = val;
-        break;
-    case 0x04: /* SCID */
-        s->scid = val;
-        break;
-    case 0x05: /* SXFER */
-        s->sxfer = val;
-        break;
-    case 0x06: /* SDID */
-        if ((val & 0xf) != (s->ssid & 0xf))
-            BADF("Destination ID does not match SSID\n");
-        s->sdid = val & 0xf;
-        break;
-    case 0x07: /* GPREG0 */
-        break;
-    case 0x08: /* SFBR */
-        /* The CPU is not allowed to write to this register.  However the
-           SCRIPTS register move instructions are.  */
-        s->sfbr = val;
-        break;
-    case 0x0a: case 0x0b:
-        /* Openserver writes to these readonly registers on startup */
-       return;
-    case 0x0c: case 0x0d: case 0x0e: case 0x0f:
-        /* Linux writes to these readonly registers on startup.  */
-        return;
-    CASE_SET_REG32(dsa, 0x10)
-    case 0x14: /* ISTAT0 */
-        s->istat0 = (s->istat0 & 0x0f) | (val & 0xf0);
-        if (val & LSI_ISTAT0_ABRT) {
-            lsi_script_dma_interrupt(s, LSI_DSTAT_ABRT);
-        }
-        if (val & LSI_ISTAT0_INTF) {
-            s->istat0 &= ~LSI_ISTAT0_INTF;
-            lsi_update_irq(s);
-        }
-        if (s->waiting == 1 && val & LSI_ISTAT0_SIGP) {
-            DPRINTF("Woken by SIGP\n");
-            s->waiting = 0;
-            s->dsp = s->dnad;
-            lsi_execute_script(s);
-        }
-        if (val & LSI_ISTAT0_SRST) {
-            qdev_reset_all(&s->dev.qdev);
-        }
-        break;
-    case 0x16: /* MBOX0 */
-        s->mbox0 = val;
-        break;
-    case 0x17: /* MBOX1 */
-        s->mbox1 = val;
-        break;
-    case 0x1a: /* CTEST2 */
-       s->ctest2 = val & LSI_CTEST2_PCICIE;
-       break;
-    case 0x1b: /* CTEST3 */
-        s->ctest3 = val & 0x0f;
-        break;
-    CASE_SET_REG32(temp, 0x1c)
-    case 0x21: /* CTEST4 */
-        if (val & 7) {
-           BADF("Unimplemented CTEST4-FBL 0x%x\n", val);
-        }
-        s->ctest4 = val;
-        break;
-    case 0x22: /* CTEST5 */
-        if (val & (LSI_CTEST5_ADCK | LSI_CTEST5_BBCK)) {
-            BADF("CTEST5 DMA increment not implemented\n");
-        }
-        s->ctest5 = val;
-        break;
-    CASE_SET_REG24(dbc, 0x24)
-    CASE_SET_REG32(dnad, 0x28)
-    case 0x2c: /* DSP[0:7] */
-        s->dsp &= 0xffffff00;
-        s->dsp |= val;
-        break;
-    case 0x2d: /* DSP[8:15] */
-        s->dsp &= 0xffff00ff;
-        s->dsp |= val << 8;
-        break;
-    case 0x2e: /* DSP[16:23] */
-        s->dsp &= 0xff00ffff;
-        s->dsp |= val << 16;
-        break;
-    case 0x2f: /* DSP[24:31] */
-        s->dsp &= 0x00ffffff;
-        s->dsp |= val << 24;
-        if ((s->dmode & LSI_DMODE_MAN) == 0
-            && (s->istat1 & LSI_ISTAT1_SRUN) == 0)
-            lsi_execute_script(s);
-        break;
-    CASE_SET_REG32(dsps, 0x30)
-    CASE_SET_REG32(scratch[0], 0x34)
-    case 0x38: /* DMODE */
-        if (val & (LSI_DMODE_SIOM | LSI_DMODE_DIOM)) {
-            BADF("IO mappings not implemented\n");
-        }
-        s->dmode = val;
-        break;
-    case 0x39: /* DIEN */
-        s->dien = val;
-        lsi_update_irq(s);
-        break;
-    case 0x3a: /* SBR */
-        s->sbr = val;
-        break;
-    case 0x3b: /* DCNTL */
-        s->dcntl = val & ~(LSI_DCNTL_PFF | LSI_DCNTL_STD);
-        if ((val & LSI_DCNTL_STD) && (s->istat1 & LSI_ISTAT1_SRUN) == 0)
-            lsi_execute_script(s);
-        break;
-    case 0x40: /* SIEN0 */
-        s->sien0 = val;
-        lsi_update_irq(s);
-        break;
-    case 0x41: /* SIEN1 */
-        s->sien1 = val;
-        lsi_update_irq(s);
-        break;
-    case 0x47: /* GPCNTL0 */
-        break;
-    case 0x48: /* STIME0 */
-        s->stime0 = val;
-        break;
-    case 0x49: /* STIME1 */
-        if (val & 0xf) {
-            DPRINTF("General purpose timer not implemented\n");
-            /* ??? Raising the interrupt immediately seems to be sufficient
-               to keep the FreeBSD driver happy.  */
-            lsi_script_scsi_interrupt(s, 0, LSI_SIST1_GEN);
-        }
-        break;
-    case 0x4a: /* RESPID0 */
-        s->respid0 = val;
-        break;
-    case 0x4b: /* RESPID1 */
-        s->respid1 = val;
-        break;
-    case 0x4d: /* STEST1 */
-        s->stest1 = val;
-        break;
-    case 0x4e: /* STEST2 */
-        if (val & 1) {
-            BADF("Low level mode not implemented\n");
-        }
-        s->stest2 = val;
-        break;
-    case 0x4f: /* STEST3 */
-        if (val & 0x41) {
-            BADF("SCSI FIFO test mode not implemented\n");
-        }
-        s->stest3 = val;
-        break;
-    case 0x56: /* CCNTL0 */
-        s->ccntl0 = val;
-        break;
-    case 0x57: /* CCNTL1 */
-        s->ccntl1 = val;
-        break;
-    CASE_SET_REG32(mmrs, 0xa0)
-    CASE_SET_REG32(mmws, 0xa4)
-    CASE_SET_REG32(sfs, 0xa8)
-    CASE_SET_REG32(drs, 0xac)
-    CASE_SET_REG32(sbms, 0xb0)
-    CASE_SET_REG32(dbms, 0xb4)
-    CASE_SET_REG32(dnad64, 0xb8)
-    CASE_SET_REG32(pmjad1, 0xc0)
-    CASE_SET_REG32(pmjad2, 0xc4)
-    CASE_SET_REG32(rbc, 0xc8)
-    CASE_SET_REG32(ua, 0xcc)
-    CASE_SET_REG32(ia, 0xd4)
-    CASE_SET_REG32(sbc, 0xd8)
-    CASE_SET_REG32(csbc, 0xdc)
-    default:
-        if (offset >= 0x5c && offset < 0xa0) {
-            int n;
-            int shift;
-            n = (offset - 0x58) >> 2;
-            shift = (offset & 3) * 8;
-            s->scratch[n] &= ~(0xff << shift);
-            s->scratch[n] |= (val & 0xff) << shift;
-        } else {
-            BADF("Unhandled writeb 0x%x = 0x%x\n", offset, val);
-        }
-    }
-#undef CASE_SET_REG24
-#undef CASE_SET_REG32
-}
-
-static void lsi_mmio_write(void *opaque, hwaddr addr,
-                           uint64_t val, unsigned size)
-{
-    LSIState *s = opaque;
-
-    lsi_reg_writeb(s, addr & 0xff, val);
-}
-
-static uint64_t lsi_mmio_read(void *opaque, hwaddr addr,
-                              unsigned size)
-{
-    LSIState *s = opaque;
-
-    return lsi_reg_readb(s, addr & 0xff);
-}
-
-static const MemoryRegionOps lsi_mmio_ops = {
-    .read = lsi_mmio_read,
-    .write = lsi_mmio_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-static void lsi_ram_write(void *opaque, hwaddr addr,
-                          uint64_t val, unsigned size)
-{
-    LSIState *s = opaque;
-    uint32_t newval;
-    uint32_t mask;
-    int shift;
-
-    newval = s->script_ram[addr >> 2];
-    shift = (addr & 3) * 8;
-    mask = ((uint64_t)1 << (size * 8)) - 1;
-    newval &= ~(mask << shift);
-    newval |= val << shift;
-    s->script_ram[addr >> 2] = newval;
-}
-
-static uint64_t lsi_ram_read(void *opaque, hwaddr addr,
-                             unsigned size)
-{
-    LSIState *s = opaque;
-    uint32_t val;
-    uint32_t mask;
-
-    val = s->script_ram[addr >> 2];
-    mask = ((uint64_t)1 << (size * 8)) - 1;
-    val >>= (addr & 3) * 8;
-    return val & mask;
-}
-
-static const MemoryRegionOps lsi_ram_ops = {
-    .read = lsi_ram_read,
-    .write = lsi_ram_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static uint64_t lsi_io_read(void *opaque, hwaddr addr,
-                            unsigned size)
-{
-    LSIState *s = opaque;
-    return lsi_reg_readb(s, addr & 0xff);
-}
-
-static void lsi_io_write(void *opaque, hwaddr addr,
-                         uint64_t val, unsigned size)
-{
-    LSIState *s = opaque;
-    lsi_reg_writeb(s, addr & 0xff, val);
-}
-
-static const MemoryRegionOps lsi_io_ops = {
-    .read = lsi_io_read,
-    .write = lsi_io_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-static void lsi_scsi_reset(DeviceState *dev)
-{
-    LSIState *s = DO_UPCAST(LSIState, dev.qdev, dev);
-
-    lsi_soft_reset(s);
-}
-
-static void lsi_pre_save(void *opaque)
-{
-    LSIState *s = opaque;
-
-    if (s->current) {
-        assert(s->current->dma_buf == NULL);
-        assert(s->current->dma_len == 0);
-    }
-    assert(QTAILQ_EMPTY(&s->queue));
-}
-
-static const VMStateDescription vmstate_lsi_scsi = {
-    .name = "lsiscsi",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .pre_save = lsi_pre_save,
-    .fields      = (VMStateField []) {
-        VMSTATE_PCI_DEVICE(dev, LSIState),
-
-        VMSTATE_INT32(carry, LSIState),
-        VMSTATE_INT32(status, LSIState),
-        VMSTATE_INT32(msg_action, LSIState),
-        VMSTATE_INT32(msg_len, LSIState),
-        VMSTATE_BUFFER(msg, LSIState),
-        VMSTATE_INT32(waiting, LSIState),
-
-        VMSTATE_UINT32(dsa, LSIState),
-        VMSTATE_UINT32(temp, LSIState),
-        VMSTATE_UINT32(dnad, LSIState),
-        VMSTATE_UINT32(dbc, LSIState),
-        VMSTATE_UINT8(istat0, LSIState),
-        VMSTATE_UINT8(istat1, LSIState),
-        VMSTATE_UINT8(dcmd, LSIState),
-        VMSTATE_UINT8(dstat, LSIState),
-        VMSTATE_UINT8(dien, LSIState),
-        VMSTATE_UINT8(sist0, LSIState),
-        VMSTATE_UINT8(sist1, LSIState),
-        VMSTATE_UINT8(sien0, LSIState),
-        VMSTATE_UINT8(sien1, LSIState),
-        VMSTATE_UINT8(mbox0, LSIState),
-        VMSTATE_UINT8(mbox1, LSIState),
-        VMSTATE_UINT8(dfifo, LSIState),
-        VMSTATE_UINT8(ctest2, LSIState),
-        VMSTATE_UINT8(ctest3, LSIState),
-        VMSTATE_UINT8(ctest4, LSIState),
-        VMSTATE_UINT8(ctest5, LSIState),
-        VMSTATE_UINT8(ccntl0, LSIState),
-        VMSTATE_UINT8(ccntl1, LSIState),
-        VMSTATE_UINT32(dsp, LSIState),
-        VMSTATE_UINT32(dsps, LSIState),
-        VMSTATE_UINT8(dmode, LSIState),
-        VMSTATE_UINT8(dcntl, LSIState),
-        VMSTATE_UINT8(scntl0, LSIState),
-        VMSTATE_UINT8(scntl1, LSIState),
-        VMSTATE_UINT8(scntl2, LSIState),
-        VMSTATE_UINT8(scntl3, LSIState),
-        VMSTATE_UINT8(sstat0, LSIState),
-        VMSTATE_UINT8(sstat1, LSIState),
-        VMSTATE_UINT8(scid, LSIState),
-        VMSTATE_UINT8(sxfer, LSIState),
-        VMSTATE_UINT8(socl, LSIState),
-        VMSTATE_UINT8(sdid, LSIState),
-        VMSTATE_UINT8(ssid, LSIState),
-        VMSTATE_UINT8(sfbr, LSIState),
-        VMSTATE_UINT8(stest1, LSIState),
-        VMSTATE_UINT8(stest2, LSIState),
-        VMSTATE_UINT8(stest3, LSIState),
-        VMSTATE_UINT8(sidl, LSIState),
-        VMSTATE_UINT8(stime0, LSIState),
-        VMSTATE_UINT8(respid0, LSIState),
-        VMSTATE_UINT8(respid1, LSIState),
-        VMSTATE_UINT32(mmrs, LSIState),
-        VMSTATE_UINT32(mmws, LSIState),
-        VMSTATE_UINT32(sfs, LSIState),
-        VMSTATE_UINT32(drs, LSIState),
-        VMSTATE_UINT32(sbms, LSIState),
-        VMSTATE_UINT32(dbms, LSIState),
-        VMSTATE_UINT32(dnad64, LSIState),
-        VMSTATE_UINT32(pmjad1, LSIState),
-        VMSTATE_UINT32(pmjad2, LSIState),
-        VMSTATE_UINT32(rbc, LSIState),
-        VMSTATE_UINT32(ua, LSIState),
-        VMSTATE_UINT32(ia, LSIState),
-        VMSTATE_UINT32(sbc, LSIState),
-        VMSTATE_UINT32(csbc, LSIState),
-        VMSTATE_BUFFER_UNSAFE(scratch, LSIState, 0, 18 * sizeof(uint32_t)),
-        VMSTATE_UINT8(sbr, LSIState),
-
-        VMSTATE_BUFFER_UNSAFE(script_ram, LSIState, 0, 2048 * sizeof(uint32_t)),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void lsi_scsi_uninit(PCIDevice *d)
-{
-    LSIState *s = DO_UPCAST(LSIState, dev, d);
-
-    memory_region_destroy(&s->mmio_io);
-    memory_region_destroy(&s->ram_io);
-    memory_region_destroy(&s->io_io);
-}
-
-static const struct SCSIBusInfo lsi_scsi_info = {
-    .tcq = true,
-    .max_target = LSI_MAX_DEVS,
-    .max_lun = 0,  /* LUN support is buggy */
-
-    .transfer_data = lsi_transfer_data,
-    .complete = lsi_command_complete,
-    .cancel = lsi_request_cancelled
-};
-
-static int lsi_scsi_init(PCIDevice *dev)
-{
-    LSIState *s = DO_UPCAST(LSIState, dev, dev);
-    uint8_t *pci_conf;
-
-    pci_conf = s->dev.config;
-
-    /* PCI latency timer = 255 */
-    pci_conf[PCI_LATENCY_TIMER] = 0xff;
-    /* Interrupt pin A */
-    pci_conf[PCI_INTERRUPT_PIN] = 0x01;
-
-    memory_region_init_io(&s->mmio_io, &lsi_mmio_ops, s, "lsi-mmio", 0x400);
-    memory_region_init_io(&s->ram_io, &lsi_ram_ops, s, "lsi-ram", 0x2000);
-    memory_region_init_io(&s->io_io, &lsi_io_ops, s, "lsi-io", 256);
-
-    pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_io);
-    pci_register_bar(&s->dev, 1, 0, &s->mmio_io);
-    pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ram_io);
-    QTAILQ_INIT(&s->queue);
-
-    scsi_bus_new(&s->bus, &dev->qdev, &lsi_scsi_info);
-    if (!dev->qdev.hotplugged) {
-        return scsi_bus_legacy_handle_cmdline(&s->bus);
-    }
-    return 0;
-}
-
-static void lsi_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = lsi_scsi_init;
-    k->exit = lsi_scsi_uninit;
-    k->vendor_id = PCI_VENDOR_ID_LSI_LOGIC;
-    k->device_id = PCI_DEVICE_ID_LSI_53C895A;
-    k->class_id = PCI_CLASS_STORAGE_SCSI;
-    k->subsystem_id = 0x1000;
-    dc->reset = lsi_scsi_reset;
-    dc->vmsd = &vmstate_lsi_scsi;
-}
-
-static const TypeInfo lsi_info = {
-    .name          = "lsi53c895a",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(LSIState),
-    .class_init    = lsi_class_init,
-};
-
-static void lsi53c895a_register_types(void)
-{
-    type_register_static(&lsi_info);
-}
-
-type_init(lsi53c895a_register_types)
diff --git a/hw/m25p80.c b/hw/m25p80.c
deleted file mode 100644 (file)
index cd560e3..0000000
+++ /dev/null
@@ -1,672 +0,0 @@
-/*
- * ST M25P80 emulator. Emulate all SPI flash devices based on the m25p80 command
- * set. Known devices table current as of Jun/2012 and taken from linux.
- * See drivers/mtd/devices/m25p80.c.
- *
- * Copyright (C) 2011 Edgar E. Iglesias <edgar.iglesias@gmail.com>
- * Copyright (C) 2012 Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>
- * Copyright (C) 2012 PetaLogix
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 or
- * (at your option) a later version of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/hw.h"
-#include "sysemu/blockdev.h"
-#include "hw/ssi.h"
-#include "hw/arm/devices.h"
-
-#ifdef M25P80_ERR_DEBUG
-#define DB_PRINT(...) do { \
-    fprintf(stderr,  ": %s: ", __func__); \
-    fprintf(stderr, ## __VA_ARGS__); \
-    } while (0);
-#else
-    #define DB_PRINT(...)
-#endif
-
-/* Fields for FlashPartInfo->flags */
-
-/* erase capabilities */
-#define ER_4K 1
-#define ER_32K 2
-/* set to allow the page program command to write 0s back to 1. Useful for
- * modelling EEPROM with SPI flash command set
- */
-#define WR_1 0x100
-
-typedef struct FlashPartInfo {
-    const char *part_name;
-    /* jedec code. (jedec >> 16) & 0xff is the 1st byte, >> 8 the 2nd etc */
-    uint32_t jedec;
-    /* extended jedec code */
-    uint16_t ext_jedec;
-    /* there is confusion between manufacturers as to what a sector is. In this
-     * device model, a "sector" is the size that is erased by the ERASE_SECTOR
-     * command (opcode 0xd8).
-     */
-    uint32_t sector_size;
-    uint32_t n_sectors;
-    uint32_t page_size;
-    uint8_t flags;
-} FlashPartInfo;
-
-/* adapted from linux */
-
-#define INFO(_part_name, _jedec, _ext_jedec, _sector_size, _n_sectors, _flags)\
-    .part_name = (_part_name),\
-    .jedec = (_jedec),\
-    .ext_jedec = (_ext_jedec),\
-    .sector_size = (_sector_size),\
-    .n_sectors = (_n_sectors),\
-    .page_size = 256,\
-    .flags = (_flags),\
-
-#define JEDEC_NUMONYX 0x20
-#define JEDEC_WINBOND 0xEF
-#define JEDEC_SPANSION 0x01
-
-static const FlashPartInfo known_devices[] = {
-    /* Atmel -- some are (confusingly) marketed as "DataFlash" */
-    { INFO("at25fs010",   0x1f6601,      0,  32 << 10,   4, ER_4K) },
-    { INFO("at25fs040",   0x1f6604,      0,  64 << 10,   8, ER_4K) },
-
-    { INFO("at25df041a",  0x1f4401,      0,  64 << 10,   8, ER_4K) },
-    { INFO("at25df321a",  0x1f4701,      0,  64 << 10,  64, ER_4K) },
-    { INFO("at25df641",   0x1f4800,      0,  64 << 10, 128, ER_4K) },
-
-    { INFO("at26f004",    0x1f0400,      0,  64 << 10,   8, ER_4K) },
-    { INFO("at26df081a",  0x1f4501,      0,  64 << 10,  16, ER_4K) },
-    { INFO("at26df161a",  0x1f4601,      0,  64 << 10,  32, ER_4K) },
-    { INFO("at26df321",   0x1f4700,      0,  64 << 10,  64, ER_4K) },
-
-    /* EON -- en25xxx */
-    { INFO("en25f32",     0x1c3116,      0,  64 << 10,  64, ER_4K) },
-    { INFO("en25p32",     0x1c2016,      0,  64 << 10,  64, 0) },
-    { INFO("en25q32b",    0x1c3016,      0,  64 << 10,  64, 0) },
-    { INFO("en25p64",     0x1c2017,      0,  64 << 10, 128, 0) },
-
-    /* Intel/Numonyx -- xxxs33b */
-    { INFO("160s33b",     0x898911,      0,  64 << 10,  32, 0) },
-    { INFO("320s33b",     0x898912,      0,  64 << 10,  64, 0) },
-    { INFO("640s33b",     0x898913,      0,  64 << 10, 128, 0) },
-
-    /* Macronix */
-    { INFO("mx25l4005a",  0xc22013,      0,  64 << 10,   8, ER_4K) },
-    { INFO("mx25l8005",   0xc22014,      0,  64 << 10,  16, 0) },
-    { INFO("mx25l1606e",  0xc22015,      0,  64 << 10,  32, ER_4K) },
-    { INFO("mx25l3205d",  0xc22016,      0,  64 << 10,  64, 0) },
-    { INFO("mx25l6405d",  0xc22017,      0,  64 << 10, 128, 0) },
-    { INFO("mx25l12805d", 0xc22018,      0,  64 << 10, 256, 0) },
-    { INFO("mx25l12855e", 0xc22618,      0,  64 << 10, 256, 0) },
-    { INFO("mx25l25635e", 0xc22019,      0,  64 << 10, 512, 0) },
-    { INFO("mx25l25655e", 0xc22619,      0,  64 << 10, 512, 0) },
-
-    /* Spansion -- single (large) sector size only, at least
-     * for the chips listed here (without boot sectors).
-     */
-    { INFO("s25sl004a",   0x010212,      0,  64 << 10,   8, 0) },
-    { INFO("s25sl008a",   0x010213,      0,  64 << 10,  16, 0) },
-    { INFO("s25sl016a",   0x010214,      0,  64 << 10,  32, 0) },
-    { INFO("s25sl032a",   0x010215,      0,  64 << 10,  64, 0) },
-    { INFO("s25sl032p",   0x010215, 0x4d00,  64 << 10,  64, ER_4K) },
-    { INFO("s25sl064a",   0x010216,      0,  64 << 10, 128, 0) },
-    { INFO("s25fl256s0",  0x010219, 0x4d00, 256 << 10, 128, 0) },
-    { INFO("s25fl256s1",  0x010219, 0x4d01,  64 << 10, 512, 0) },
-    { INFO("s25fl512s",   0x010220, 0x4d00, 256 << 10, 256, 0) },
-    { INFO("s70fl01gs",   0x010221, 0x4d00, 256 << 10, 256, 0) },
-    { INFO("s25sl12800",  0x012018, 0x0300, 256 << 10,  64, 0) },
-    { INFO("s25sl12801",  0x012018, 0x0301,  64 << 10, 256, 0) },
-    { INFO("s25fl129p0",  0x012018, 0x4d00, 256 << 10,  64, 0) },
-    { INFO("s25fl129p1",  0x012018, 0x4d01,  64 << 10, 256, 0) },
-    { INFO("s25fl016k",   0xef4015,      0,  64 << 10,  32, ER_4K | ER_32K) },
-    { INFO("s25fl064k",   0xef4017,      0,  64 << 10, 128, ER_4K | ER_32K) },
-
-    /* SST -- large erase sizes are "overlays", "sectors" are 4<< 10 */
-    { INFO("sst25vf040b", 0xbf258d,      0,  64 << 10,   8, ER_4K) },
-    { INFO("sst25vf080b", 0xbf258e,      0,  64 << 10,  16, ER_4K) },
-    { INFO("sst25vf016b", 0xbf2541,      0,  64 << 10,  32, ER_4K) },
-    { INFO("sst25vf032b", 0xbf254a,      0,  64 << 10,  64, ER_4K) },
-    { INFO("sst25wf512",  0xbf2501,      0,  64 << 10,   1, ER_4K) },
-    { INFO("sst25wf010",  0xbf2502,      0,  64 << 10,   2, ER_4K) },
-    { INFO("sst25wf020",  0xbf2503,      0,  64 << 10,   4, ER_4K) },
-    { INFO("sst25wf040",  0xbf2504,      0,  64 << 10,   8, ER_4K) },
-
-    /* ST Microelectronics -- newer production may have feature updates */
-    { INFO("m25p05",      0x202010,      0,  32 << 10,   2, 0) },
-    { INFO("m25p10",      0x202011,      0,  32 << 10,   4, 0) },
-    { INFO("m25p20",      0x202012,      0,  64 << 10,   4, 0) },
-    { INFO("m25p40",      0x202013,      0,  64 << 10,   8, 0) },
-    { INFO("m25p80",      0x202014,      0,  64 << 10,  16, 0) },
-    { INFO("m25p16",      0x202015,      0,  64 << 10,  32, 0) },
-    { INFO("m25p32",      0x202016,      0,  64 << 10,  64, 0) },
-    { INFO("m25p64",      0x202017,      0,  64 << 10, 128, 0) },
-    { INFO("m25p128",     0x202018,      0, 256 << 10,  64, 0) },
-
-    { INFO("m45pe10",     0x204011,      0,  64 << 10,   2, 0) },
-    { INFO("m45pe80",     0x204014,      0,  64 << 10,  16, 0) },
-    { INFO("m45pe16",     0x204015,      0,  64 << 10,  32, 0) },
-
-    { INFO("m25pe80",     0x208014,      0,  64 << 10,  16, 0) },
-    { INFO("m25pe16",     0x208015,      0,  64 << 10,  32, ER_4K) },
-
-    { INFO("m25px32",     0x207116,      0,  64 << 10,  64, ER_4K) },
-    { INFO("m25px32-s0",  0x207316,      0,  64 << 10,  64, ER_4K) },
-    { INFO("m25px32-s1",  0x206316,      0,  64 << 10,  64, ER_4K) },
-    { INFO("m25px64",     0x207117,      0,  64 << 10, 128, 0) },
-
-    /* Winbond -- w25x "blocks" are 64k, "sectors" are 4KiB */
-    { INFO("w25x10",      0xef3011,      0,  64 << 10,   2, ER_4K) },
-    { INFO("w25x20",      0xef3012,      0,  64 << 10,   4, ER_4K) },
-    { INFO("w25x40",      0xef3013,      0,  64 << 10,   8, ER_4K) },
-    { INFO("w25x80",      0xef3014,      0,  64 << 10,  16, ER_4K) },
-    { INFO("w25x16",      0xef3015,      0,  64 << 10,  32, ER_4K) },
-    { INFO("w25x32",      0xef3016,      0,  64 << 10,  64, ER_4K) },
-    { INFO("w25q32",      0xef4016,      0,  64 << 10,  64, ER_4K) },
-    { INFO("w25x64",      0xef3017,      0,  64 << 10, 128, ER_4K) },
-    { INFO("w25q64",      0xef4017,      0,  64 << 10, 128, ER_4K) },
-
-    /* Numonyx -- n25q128 */
-    { INFO("n25q128",      0x20ba18,      0,  64 << 10, 256, 0) },
-};
-
-typedef enum {
-    NOP = 0,
-    WRSR = 0x1,
-    WRDI = 0x4,
-    RDSR = 0x5,
-    WREN = 0x6,
-    JEDEC_READ = 0x9f,
-    BULK_ERASE = 0xc7,
-
-    READ = 0x3,
-    FAST_READ = 0xb,
-    DOR = 0x3b,
-    QOR = 0x6b,
-    DIOR = 0xbb,
-    QIOR = 0xeb,
-
-    PP = 0x2,
-    DPP = 0xa2,
-    QPP = 0x32,
-
-    ERASE_4K = 0x20,
-    ERASE_32K = 0x52,
-    ERASE_SECTOR = 0xd8,
-} FlashCMD;
-
-typedef enum {
-    STATE_IDLE,
-    STATE_PAGE_PROGRAM,
-    STATE_READ,
-    STATE_COLLECTING_DATA,
-    STATE_READING_DATA,
-} CMDState;
-
-typedef struct Flash {
-    SSISlave ssidev;
-    uint32_t r;
-
-    BlockDriverState *bdrv;
-
-    uint8_t *storage;
-    uint32_t size;
-    int page_size;
-
-    uint8_t state;
-    uint8_t data[16];
-    uint32_t len;
-    uint32_t pos;
-    uint8_t needed_bytes;
-    uint8_t cmd_in_progress;
-    uint64_t cur_addr;
-    bool write_enable;
-
-    int64_t dirty_page;
-
-    const FlashPartInfo *pi;
-
-} Flash;
-
-typedef struct M25P80Class {
-    SSISlaveClass parent_class;
-    FlashPartInfo *pi;
-} M25P80Class;
-
-#define TYPE_M25P80 "m25p80-generic"
-#define M25P80(obj) \
-     OBJECT_CHECK(Flash, (obj), TYPE_M25P80)
-#define M25P80_CLASS(klass) \
-     OBJECT_CLASS_CHECK(M25P80Class, (klass), TYPE_M25P80)
-#define M25P80_GET_CLASS(obj) \
-     OBJECT_GET_CLASS(M25P80Class, (obj), TYPE_M25P80)
-
-static void bdrv_sync_complete(void *opaque, int ret)
-{
-    /* do nothing. Masters do not directly interact with the backing store,
-     * only the working copy so no mutexing required.
-     */
-}
-
-static void flash_sync_page(Flash *s, int page)
-{
-    if (s->bdrv) {
-        int bdrv_sector, nb_sectors;
-        QEMUIOVector iov;
-
-        bdrv_sector = (page * s->pi->page_size) / BDRV_SECTOR_SIZE;
-        nb_sectors = DIV_ROUND_UP(s->pi->page_size, BDRV_SECTOR_SIZE);
-        qemu_iovec_init(&iov, 1);
-        qemu_iovec_add(&iov, s->storage + bdrv_sector * BDRV_SECTOR_SIZE,
-                                                nb_sectors * BDRV_SECTOR_SIZE);
-        bdrv_aio_writev(s->bdrv, bdrv_sector, &iov, nb_sectors,
-                                                bdrv_sync_complete, NULL);
-    }
-}
-
-static inline void flash_sync_area(Flash *s, int64_t off, int64_t len)
-{
-    int64_t start, end, nb_sectors;
-    QEMUIOVector iov;
-
-    if (!s->bdrv) {
-        return;
-    }
-
-    assert(!(len % BDRV_SECTOR_SIZE));
-    start = off / BDRV_SECTOR_SIZE;
-    end = (off + len) / BDRV_SECTOR_SIZE;
-    nb_sectors = end - start;
-    qemu_iovec_init(&iov, 1);
-    qemu_iovec_add(&iov, s->storage + (start * BDRV_SECTOR_SIZE),
-                                        nb_sectors * BDRV_SECTOR_SIZE);
-    bdrv_aio_writev(s->bdrv, start, &iov, nb_sectors, bdrv_sync_complete, NULL);
-}
-
-static void flash_erase(Flash *s, int offset, FlashCMD cmd)
-{
-    uint32_t len;
-    uint8_t capa_to_assert = 0;
-
-    switch (cmd) {
-    case ERASE_4K:
-        len = 4 << 10;
-        capa_to_assert = ER_4K;
-        break;
-    case ERASE_32K:
-        len = 32 << 10;
-        capa_to_assert = ER_32K;
-        break;
-    case ERASE_SECTOR:
-        len = s->pi->sector_size;
-        break;
-    case BULK_ERASE:
-        len = s->size;
-        break;
-    default:
-        abort();
-    }
-
-    DB_PRINT("offset = %#x, len = %d\n", offset, len);
-    if ((s->pi->flags & capa_to_assert) != capa_to_assert) {
-        hw_error("m25p80: %dk erase size not supported by device\n", len);
-    }
-
-    if (!s->write_enable) {
-        DB_PRINT("erase with write protect!\n");
-        return;
-    }
-    memset(s->storage + offset, 0xff, len);
-    flash_sync_area(s, offset, len);
-}
-
-static inline void flash_sync_dirty(Flash *s, int64_t newpage)
-{
-    if (s->dirty_page >= 0 && s->dirty_page != newpage) {
-        flash_sync_page(s, s->dirty_page);
-        s->dirty_page = newpage;
-    }
-}
-
-static inline
-void flash_write8(Flash *s, uint64_t addr, uint8_t data)
-{
-    int64_t page = addr / s->pi->page_size;
-    uint8_t prev = s->storage[s->cur_addr];
-
-    if (!s->write_enable) {
-        DB_PRINT("write with write protect!\n");
-    }
-
-    if ((prev ^ data) & data) {
-        DB_PRINT("programming zero to one! addr=%lx  %x -> %x\n",
-                  addr, prev, data);
-    }
-
-    if (s->pi->flags & WR_1) {
-        s->storage[s->cur_addr] = data;
-    } else {
-        s->storage[s->cur_addr] &= data;
-    }
-
-    flash_sync_dirty(s, page);
-    s->dirty_page = page;
-}
-
-static void complete_collecting_data(Flash *s)
-{
-    s->cur_addr = s->data[0] << 16;
-    s->cur_addr |= s->data[1] << 8;
-    s->cur_addr |= s->data[2];
-
-    s->state = STATE_IDLE;
-
-    switch (s->cmd_in_progress) {
-    case DPP:
-    case QPP:
-    case PP:
-        s->state = STATE_PAGE_PROGRAM;
-        break;
-    case READ:
-    case FAST_READ:
-    case DOR:
-    case QOR:
-    case DIOR:
-    case QIOR:
-        s->state = STATE_READ;
-        break;
-    case ERASE_4K:
-    case ERASE_32K:
-    case ERASE_SECTOR:
-        flash_erase(s, s->cur_addr, s->cmd_in_progress);
-        break;
-    case WRSR:
-        if (s->write_enable) {
-            s->write_enable = false;
-        }
-        break;
-    default:
-        break;
-    }
-}
-
-static void decode_new_cmd(Flash *s, uint32_t value)
-{
-    s->cmd_in_progress = value;
-    DB_PRINT("decoded new command:%x\n", value);
-
-    switch (value) {
-
-    case ERASE_4K:
-    case ERASE_32K:
-    case ERASE_SECTOR:
-    case READ:
-    case DPP:
-    case QPP:
-    case PP:
-        s->needed_bytes = 3;
-        s->pos = 0;
-        s->len = 0;
-        s->state = STATE_COLLECTING_DATA;
-        break;
-
-    case FAST_READ:
-    case DOR:
-    case QOR:
-        s->needed_bytes = 4;
-        s->pos = 0;
-        s->len = 0;
-        s->state = STATE_COLLECTING_DATA;
-        break;
-
-    case DIOR:
-        switch ((s->pi->jedec >> 16) & 0xFF) {
-        case JEDEC_WINBOND:
-        case JEDEC_SPANSION:
-            s->needed_bytes = 4;
-            break;
-        case JEDEC_NUMONYX:
-        default:
-            s->needed_bytes = 5;
-        }
-        s->pos = 0;
-        s->len = 0;
-        s->state = STATE_COLLECTING_DATA;
-        break;
-
-    case QIOR:
-        switch ((s->pi->jedec >> 16) & 0xFF) {
-        case JEDEC_WINBOND:
-        case JEDEC_SPANSION:
-            s->needed_bytes = 6;
-            break;
-        case JEDEC_NUMONYX:
-        default:
-            s->needed_bytes = 8;
-        }
-        s->pos = 0;
-        s->len = 0;
-        s->state = STATE_COLLECTING_DATA;
-        break;
-
-    case WRSR:
-        if (s->write_enable) {
-            s->needed_bytes = 1;
-            s->pos = 0;
-            s->len = 0;
-            s->state = STATE_COLLECTING_DATA;
-        }
-        break;
-
-    case WRDI:
-        s->write_enable = false;
-        break;
-    case WREN:
-        s->write_enable = true;
-        break;
-
-    case RDSR:
-        s->data[0] = (!!s->write_enable) << 1;
-        s->pos = 0;
-        s->len = 1;
-        s->state = STATE_READING_DATA;
-        break;
-
-    case JEDEC_READ:
-        DB_PRINT("populated jedec code\n");
-        s->data[0] = (s->pi->jedec >> 16) & 0xff;
-        s->data[1] = (s->pi->jedec >> 8) & 0xff;
-        s->data[2] = s->pi->jedec & 0xff;
-        if (s->pi->ext_jedec) {
-            s->data[3] = (s->pi->ext_jedec >> 8) & 0xff;
-            s->data[4] = s->pi->ext_jedec & 0xff;
-            s->len = 5;
-        } else {
-            s->len = 3;
-        }
-        s->pos = 0;
-        s->state = STATE_READING_DATA;
-        break;
-
-    case BULK_ERASE:
-        if (s->write_enable) {
-            DB_PRINT("chip erase\n");
-            flash_erase(s, 0, BULK_ERASE);
-        } else {
-            DB_PRINT("chip erase with write protect!\n");
-        }
-        break;
-    case NOP:
-        break;
-    default:
-        DB_PRINT("Unknown cmd %x\n", value);
-        break;
-    }
-}
-
-static int m25p80_cs(SSISlave *ss, bool select)
-{
-    Flash *s = FROM_SSI_SLAVE(Flash, ss);
-
-    if (select) {
-        s->len = 0;
-        s->pos = 0;
-        s->state = STATE_IDLE;
-        flash_sync_dirty(s, -1);
-    }
-
-    DB_PRINT("%sselect\n", select ? "de" : "");
-
-    return 0;
-}
-
-static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
-{
-    Flash *s = FROM_SSI_SLAVE(Flash, ss);
-    uint32_t r = 0;
-
-    switch (s->state) {
-
-    case STATE_PAGE_PROGRAM:
-        DB_PRINT("page program cur_addr=%lx data=%x\n", s->cur_addr,
-                 (uint8_t)tx);
-        flash_write8(s, s->cur_addr, (uint8_t)tx);
-        s->cur_addr++;
-        break;
-
-    case STATE_READ:
-        r = s->storage[s->cur_addr];
-        DB_PRINT("READ 0x%lx=%x\n", s->cur_addr, r);
-        s->cur_addr = (s->cur_addr + 1) % s->size;
-        break;
-
-    case STATE_COLLECTING_DATA:
-        s->data[s->len] = (uint8_t)tx;
-        s->len++;
-
-        if (s->len == s->needed_bytes) {
-            complete_collecting_data(s);
-        }
-        break;
-
-    case STATE_READING_DATA:
-        r = s->data[s->pos];
-        s->pos++;
-        if (s->pos == s->len) {
-            s->pos = 0;
-            s->state = STATE_IDLE;
-        }
-        break;
-
-    default:
-    case STATE_IDLE:
-        decode_new_cmd(s, (uint8_t)tx);
-        break;
-    }
-
-    return r;
-}
-
-static int m25p80_init(SSISlave *ss)
-{
-    DriveInfo *dinfo;
-    Flash *s = FROM_SSI_SLAVE(Flash, ss);
-    M25P80Class *mc = M25P80_GET_CLASS(s);
-
-    s->pi = mc->pi;
-
-    s->size = s->pi->sector_size * s->pi->n_sectors;
-    s->dirty_page = -1;
-    s->storage = qemu_blockalign(s->bdrv, s->size);
-
-    dinfo = drive_get_next(IF_MTD);
-
-    if (dinfo && dinfo->bdrv) {
-        DB_PRINT("Binding to IF_MTD drive\n");
-        s->bdrv = dinfo->bdrv;
-        /* FIXME: Move to late init */
-        if (bdrv_read(s->bdrv, 0, s->storage, DIV_ROUND_UP(s->size,
-                                                    BDRV_SECTOR_SIZE))) {
-            fprintf(stderr, "Failed to initialize SPI flash!\n");
-            return 1;
-        }
-    } else {
-        memset(s->storage, 0xFF, s->size);
-    }
-
-    return 0;
-}
-
-static void m25p80_pre_save(void *opaque)
-{
-    flash_sync_dirty((Flash *)opaque, -1);
-}
-
-static const VMStateDescription vmstate_m25p80 = {
-    .name = "xilinx_spi",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .pre_save = m25p80_pre_save,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT8(state, Flash),
-        VMSTATE_UINT8_ARRAY(data, Flash, 16),
-        VMSTATE_UINT32(len, Flash),
-        VMSTATE_UINT32(pos, Flash),
-        VMSTATE_UINT8(needed_bytes, Flash),
-        VMSTATE_UINT8(cmd_in_progress, Flash),
-        VMSTATE_UINT64(cur_addr, Flash),
-        VMSTATE_BOOL(write_enable, Flash),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void m25p80_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
-    M25P80Class *mc = M25P80_CLASS(klass);
-
-    k->init = m25p80_init;
-    k->transfer = m25p80_transfer8;
-    k->set_cs = m25p80_cs;
-    k->cs_polarity = SSI_CS_LOW;
-    dc->vmsd = &vmstate_m25p80;
-    mc->pi = data;
-}
-
-static const TypeInfo m25p80_info = {
-    .name           = TYPE_M25P80,
-    .parent         = TYPE_SSI_SLAVE,
-    .instance_size  = sizeof(Flash),
-    .class_size     = sizeof(M25P80Class),
-    .abstract       = true,
-};
-
-static void m25p80_register_types(void)
-{
-    int i;
-
-    type_register_static(&m25p80_info);
-    for (i = 0; i < ARRAY_SIZE(known_devices); ++i) {
-        TypeInfo ti = {
-            .name       = known_devices[i].part_name,
-            .parent     = TYPE_M25P80,
-            .class_init = m25p80_class_init,
-            .class_data = (void *)&known_devices[i],
-        };
-        type_register(&ti);
-    }
-}
-
-type_init(m25p80_register_types)
diff --git a/hw/m48t59.c b/hw/m48t59.c
deleted file mode 100644 (file)
index 5019e06..0000000
+++ /dev/null
@@ -1,778 +0,0 @@
-/*
- * QEMU M48T59 and M48T08 NVRAM emulation for PPC PREP and Sparc platforms
- *
- * Copyright (c) 2003-2005, 2007 Jocelyn Mayer
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/timer/m48t59.h"
-#include "qemu/timer.h"
-#include "sysemu/sysemu.h"
-#include "hw/sysbus.h"
-#include "hw/isa/isa.h"
-#include "exec/address-spaces.h"
-
-//#define DEBUG_NVRAM
-
-#if defined(DEBUG_NVRAM)
-#define NVRAM_PRINTF(fmt, ...) do { printf(fmt , ## __VA_ARGS__); } while (0)
-#else
-#define NVRAM_PRINTF(fmt, ...) do { } while (0)
-#endif
-
-/*
- * The M48T02, M48T08 and M48T59 chips are very similar. The newer '59 has
- * alarm and a watchdog timer and related control registers. In the
- * PPC platform there is also a nvram lock function.
- */
-
-/*
- * Chipset docs:
- * http://www.st.com/stonline/products/literature/ds/2410/m48t02.pdf
- * http://www.st.com/stonline/products/literature/ds/2411/m48t08.pdf
- * http://www.st.com/stonline/products/literature/od/7001/m48t59y.pdf
- */
-
-struct M48t59State {
-    /* Hardware parameters */
-    qemu_irq IRQ;
-    MemoryRegion iomem;
-    uint32_t io_base;
-    uint32_t size;
-    /* RTC management */
-    time_t   time_offset;
-    time_t   stop_time;
-    /* Alarm & watchdog */
-    struct tm alarm;
-    struct QEMUTimer *alrm_timer;
-    struct QEMUTimer *wd_timer;
-    /* NVRAM storage */
-    uint8_t *buffer;
-    /* Model parameters */
-    uint32_t model; /* 2 = m48t02, 8 = m48t08, 59 = m48t59 */
-    /* NVRAM storage */
-    uint16_t addr;
-    uint8_t  lock;
-};
-
-typedef struct M48t59ISAState {
-    ISADevice busdev;
-    M48t59State state;
-    MemoryRegion io;
-} M48t59ISAState;
-
-typedef struct M48t59SysBusState {
-    SysBusDevice busdev;
-    M48t59State state;
-    MemoryRegion io;
-} M48t59SysBusState;
-
-/* Fake timer functions */
-
-/* Alarm management */
-static void alarm_cb (void *opaque)
-{
-    struct tm tm;
-    uint64_t next_time;
-    M48t59State *NVRAM = opaque;
-
-    qemu_set_irq(NVRAM->IRQ, 1);
-    if ((NVRAM->buffer[0x1FF5] & 0x80) == 0 &&
-       (NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
-       (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
-       (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
-        /* Repeat once a month */
-        qemu_get_timedate(&tm, NVRAM->time_offset);
-        tm.tm_mon++;
-        if (tm.tm_mon == 13) {
-            tm.tm_mon = 1;
-            tm.tm_year++;
-        }
-        next_time = qemu_timedate_diff(&tm) - NVRAM->time_offset;
-    } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
-              (NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
-              (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
-              (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
-        /* Repeat once a day */
-        next_time = 24 * 60 * 60;
-    } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
-              (NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
-              (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
-              (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
-        /* Repeat once an hour */
-        next_time = 60 * 60;
-    } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
-              (NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
-              (NVRAM->buffer[0x1FF3] & 0x80) != 0 &&
-              (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
-        /* Repeat once a minute */
-        next_time = 60;
-    } else {
-        /* Repeat once a second */
-        next_time = 1;
-    }
-    qemu_mod_timer(NVRAM->alrm_timer, qemu_get_clock_ns(rtc_clock) +
-                    next_time * 1000);
-    qemu_set_irq(NVRAM->IRQ, 0);
-}
-
-static void set_alarm(M48t59State *NVRAM)
-{
-    int diff;
-    if (NVRAM->alrm_timer != NULL) {
-        qemu_del_timer(NVRAM->alrm_timer);
-        diff = qemu_timedate_diff(&NVRAM->alarm) - NVRAM->time_offset;
-        if (diff > 0)
-            qemu_mod_timer(NVRAM->alrm_timer, diff * 1000);
-    }
-}
-
-/* RTC management helpers */
-static inline void get_time(M48t59State *NVRAM, struct tm *tm)
-{
-    qemu_get_timedate(tm, NVRAM->time_offset);
-}
-
-static void set_time(M48t59State *NVRAM, struct tm *tm)
-{
-    NVRAM->time_offset = qemu_timedate_diff(tm);
-    set_alarm(NVRAM);
-}
-
-/* Watchdog management */
-static void watchdog_cb (void *opaque)
-{
-    M48t59State *NVRAM = opaque;
-
-    NVRAM->buffer[0x1FF0] |= 0x80;
-    if (NVRAM->buffer[0x1FF7] & 0x80) {
-       NVRAM->buffer[0x1FF7] = 0x00;
-       NVRAM->buffer[0x1FFC] &= ~0x40;
-        /* May it be a hw CPU Reset instead ? */
-        qemu_system_reset_request();
-    } else {
-       qemu_set_irq(NVRAM->IRQ, 1);
-       qemu_set_irq(NVRAM->IRQ, 0);
-    }
-}
-
-static void set_up_watchdog(M48t59State *NVRAM, uint8_t value)
-{
-    uint64_t interval; /* in 1/16 seconds */
-
-    NVRAM->buffer[0x1FF0] &= ~0x80;
-    if (NVRAM->wd_timer != NULL) {
-        qemu_del_timer(NVRAM->wd_timer);
-        if (value != 0) {
-            interval = (1 << (2 * (value & 0x03))) * ((value >> 2) & 0x1F);
-            qemu_mod_timer(NVRAM->wd_timer, ((uint64_t)time(NULL) * 1000) +
-                           ((interval * 1000) >> 4));
-        }
-    }
-}
-
-/* Direct access to NVRAM */
-void m48t59_write (void *opaque, uint32_t addr, uint32_t val)
-{
-    M48t59State *NVRAM = opaque;
-    struct tm tm;
-    int tmp;
-
-    if (addr > 0x1FF8 && addr < 0x2000)
-       NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val);
-
-    /* check for NVRAM access */
-    if ((NVRAM->model == 2 && addr < 0x7f8) ||
-        (NVRAM->model == 8 && addr < 0x1ff8) ||
-        (NVRAM->model == 59 && addr < 0x1ff0)) {
-        goto do_write;
-    }
-
-    /* TOD access */
-    switch (addr) {
-    case 0x1FF0:
-        /* flags register : read-only */
-        break;
-    case 0x1FF1:
-        /* unused */
-        break;
-    case 0x1FF2:
-        /* alarm seconds */
-        tmp = from_bcd(val & 0x7F);
-        if (tmp >= 0 && tmp <= 59) {
-            NVRAM->alarm.tm_sec = tmp;
-            NVRAM->buffer[0x1FF2] = val;
-            set_alarm(NVRAM);
-        }
-        break;
-    case 0x1FF3:
-        /* alarm minutes */
-        tmp = from_bcd(val & 0x7F);
-        if (tmp >= 0 && tmp <= 59) {
-            NVRAM->alarm.tm_min = tmp;
-            NVRAM->buffer[0x1FF3] = val;
-            set_alarm(NVRAM);
-        }
-        break;
-    case 0x1FF4:
-        /* alarm hours */
-        tmp = from_bcd(val & 0x3F);
-        if (tmp >= 0 && tmp <= 23) {
-            NVRAM->alarm.tm_hour = tmp;
-            NVRAM->buffer[0x1FF4] = val;
-            set_alarm(NVRAM);
-        }
-        break;
-    case 0x1FF5:
-        /* alarm date */
-        tmp = from_bcd(val & 0x3F);
-        if (tmp != 0) {
-            NVRAM->alarm.tm_mday = tmp;
-            NVRAM->buffer[0x1FF5] = val;
-            set_alarm(NVRAM);
-        }
-        break;
-    case 0x1FF6:
-        /* interrupts */
-        NVRAM->buffer[0x1FF6] = val;
-        break;
-    case 0x1FF7:
-        /* watchdog */
-        NVRAM->buffer[0x1FF7] = val;
-        set_up_watchdog(NVRAM, val);
-        break;
-    case 0x1FF8:
-    case 0x07F8:
-        /* control */
-       NVRAM->buffer[addr] = (val & ~0xA0) | 0x90;
-        break;
-    case 0x1FF9:
-    case 0x07F9:
-        /* seconds (BCD) */
-       tmp = from_bcd(val & 0x7F);
-       if (tmp >= 0 && tmp <= 59) {
-           get_time(NVRAM, &tm);
-           tm.tm_sec = tmp;
-           set_time(NVRAM, &tm);
-       }
-        if ((val & 0x80) ^ (NVRAM->buffer[addr] & 0x80)) {
-           if (val & 0x80) {
-               NVRAM->stop_time = time(NULL);
-           } else {
-               NVRAM->time_offset += NVRAM->stop_time - time(NULL);
-               NVRAM->stop_time = 0;
-           }
-       }
-        NVRAM->buffer[addr] = val & 0x80;
-        break;
-    case 0x1FFA:
-    case 0x07FA:
-        /* minutes (BCD) */
-       tmp = from_bcd(val & 0x7F);
-       if (tmp >= 0 && tmp <= 59) {
-           get_time(NVRAM, &tm);
-           tm.tm_min = tmp;
-           set_time(NVRAM, &tm);
-       }
-        break;
-    case 0x1FFB:
-    case 0x07FB:
-        /* hours (BCD) */
-       tmp = from_bcd(val & 0x3F);
-       if (tmp >= 0 && tmp <= 23) {
-           get_time(NVRAM, &tm);
-           tm.tm_hour = tmp;
-           set_time(NVRAM, &tm);
-       }
-        break;
-    case 0x1FFC:
-    case 0x07FC:
-        /* day of the week / century */
-       tmp = from_bcd(val & 0x07);
-       get_time(NVRAM, &tm);
-       tm.tm_wday = tmp;
-       set_time(NVRAM, &tm);
-        NVRAM->buffer[addr] = val & 0x40;
-        break;
-    case 0x1FFD:
-    case 0x07FD:
-        /* date (BCD) */
-       tmp = from_bcd(val & 0x3F);
-       if (tmp != 0) {
-           get_time(NVRAM, &tm);
-           tm.tm_mday = tmp;
-           set_time(NVRAM, &tm);
-       }
-        break;
-    case 0x1FFE:
-    case 0x07FE:
-        /* month */
-       tmp = from_bcd(val & 0x1F);
-       if (tmp >= 1 && tmp <= 12) {
-           get_time(NVRAM, &tm);
-           tm.tm_mon = tmp - 1;
-           set_time(NVRAM, &tm);
-       }
-        break;
-    case 0x1FFF:
-    case 0x07FF:
-        /* year */
-       tmp = from_bcd(val);
-       if (tmp >= 0 && tmp <= 99) {
-           get_time(NVRAM, &tm);
-            if (NVRAM->model == 8) {
-                tm.tm_year = from_bcd(val) + 68; // Base year is 1968
-            } else {
-                tm.tm_year = from_bcd(val);
-            }
-           set_time(NVRAM, &tm);
-       }
-        break;
-    default:
-        /* Check lock registers state */
-        if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1))
-            break;
-        if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2))
-            break;
-    do_write:
-        if (addr < NVRAM->size) {
-            NVRAM->buffer[addr] = val & 0xFF;
-       }
-        break;
-    }
-}
-
-uint32_t m48t59_read (void *opaque, uint32_t addr)
-{
-    M48t59State *NVRAM = opaque;
-    struct tm tm;
-    uint32_t retval = 0xFF;
-
-    /* check for NVRAM access */
-    if ((NVRAM->model == 2 && addr < 0x078f) ||
-        (NVRAM->model == 8 && addr < 0x1ff8) ||
-        (NVRAM->model == 59 && addr < 0x1ff0)) {
-        goto do_read;
-    }
-
-    /* TOD access */
-    switch (addr) {
-    case 0x1FF0:
-        /* flags register */
-       goto do_read;
-    case 0x1FF1:
-        /* unused */
-       retval = 0;
-        break;
-    case 0x1FF2:
-        /* alarm seconds */
-       goto do_read;
-    case 0x1FF3:
-        /* alarm minutes */
-       goto do_read;
-    case 0x1FF4:
-        /* alarm hours */
-       goto do_read;
-    case 0x1FF5:
-        /* alarm date */
-       goto do_read;
-    case 0x1FF6:
-        /* interrupts */
-       goto do_read;
-    case 0x1FF7:
-       /* A read resets the watchdog */
-       set_up_watchdog(NVRAM, NVRAM->buffer[0x1FF7]);
-       goto do_read;
-    case 0x1FF8:
-    case 0x07F8:
-        /* control */
-       goto do_read;
-    case 0x1FF9:
-    case 0x07F9:
-        /* seconds (BCD) */
-        get_time(NVRAM, &tm);
-        retval = (NVRAM->buffer[addr] & 0x80) | to_bcd(tm.tm_sec);
-        break;
-    case 0x1FFA:
-    case 0x07FA:
-        /* minutes (BCD) */
-        get_time(NVRAM, &tm);
-        retval = to_bcd(tm.tm_min);
-        break;
-    case 0x1FFB:
-    case 0x07FB:
-        /* hours (BCD) */
-        get_time(NVRAM, &tm);
-        retval = to_bcd(tm.tm_hour);
-        break;
-    case 0x1FFC:
-    case 0x07FC:
-        /* day of the week / century */
-        get_time(NVRAM, &tm);
-        retval = NVRAM->buffer[addr] | tm.tm_wday;
-        break;
-    case 0x1FFD:
-    case 0x07FD:
-        /* date */
-        get_time(NVRAM, &tm);
-        retval = to_bcd(tm.tm_mday);
-        break;
-    case 0x1FFE:
-    case 0x07FE:
-        /* month */
-        get_time(NVRAM, &tm);
-        retval = to_bcd(tm.tm_mon + 1);
-        break;
-    case 0x1FFF:
-    case 0x07FF:
-        /* year */
-        get_time(NVRAM, &tm);
-        if (NVRAM->model == 8) {
-            retval = to_bcd(tm.tm_year - 68); // Base year is 1968
-        } else {
-            retval = to_bcd(tm.tm_year);
-        }
-        break;
-    default:
-        /* Check lock registers state */
-        if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1))
-            break;
-        if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2))
-            break;
-    do_read:
-        if (addr < NVRAM->size) {
-            retval = NVRAM->buffer[addr];
-       }
-        break;
-    }
-    if (addr > 0x1FF9 && addr < 0x2000)
-       NVRAM_PRINTF("%s: 0x%08x <= 0x%08x\n", __func__, addr, retval);
-
-    return retval;
-}
-
-void m48t59_toggle_lock (void *opaque, int lock)
-{
-    M48t59State *NVRAM = opaque;
-
-    NVRAM->lock ^= 1 << lock;
-}
-
-/* IO access to NVRAM */
-static void NVRAM_writeb(void *opaque, hwaddr addr, uint64_t val,
-                         unsigned size)
-{
-    M48t59State *NVRAM = opaque;
-
-    NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val);
-    switch (addr) {
-    case 0:
-        NVRAM->addr &= ~0x00FF;
-        NVRAM->addr |= val;
-        break;
-    case 1:
-        NVRAM->addr &= ~0xFF00;
-        NVRAM->addr |= val << 8;
-        break;
-    case 3:
-        m48t59_write(NVRAM, NVRAM->addr, val);
-        NVRAM->addr = 0x0000;
-        break;
-    default:
-        break;
-    }
-}
-
-static uint64_t NVRAM_readb(void *opaque, hwaddr addr, unsigned size)
-{
-    M48t59State *NVRAM = opaque;
-    uint32_t retval;
-
-    switch (addr) {
-    case 3:
-        retval = m48t59_read(NVRAM, NVRAM->addr);
-        break;
-    default:
-        retval = -1;
-        break;
-    }
-    NVRAM_PRINTF("%s: 0x%08x <= 0x%08x\n", __func__, addr, retval);
-
-    return retval;
-}
-
-static void nvram_writeb (void *opaque, hwaddr addr, uint32_t value)
-{
-    M48t59State *NVRAM = opaque;
-
-    m48t59_write(NVRAM, addr, value & 0xff);
-}
-
-static void nvram_writew (void *opaque, hwaddr addr, uint32_t value)
-{
-    M48t59State *NVRAM = opaque;
-
-    m48t59_write(NVRAM, addr, (value >> 8) & 0xff);
-    m48t59_write(NVRAM, addr + 1, value & 0xff);
-}
-
-static void nvram_writel (void *opaque, hwaddr addr, uint32_t value)
-{
-    M48t59State *NVRAM = opaque;
-
-    m48t59_write(NVRAM, addr, (value >> 24) & 0xff);
-    m48t59_write(NVRAM, addr + 1, (value >> 16) & 0xff);
-    m48t59_write(NVRAM, addr + 2, (value >> 8) & 0xff);
-    m48t59_write(NVRAM, addr + 3, value & 0xff);
-}
-
-static uint32_t nvram_readb (void *opaque, hwaddr addr)
-{
-    M48t59State *NVRAM = opaque;
-    uint32_t retval;
-
-    retval = m48t59_read(NVRAM, addr);
-    return retval;
-}
-
-static uint32_t nvram_readw (void *opaque, hwaddr addr)
-{
-    M48t59State *NVRAM = opaque;
-    uint32_t retval;
-
-    retval = m48t59_read(NVRAM, addr) << 8;
-    retval |= m48t59_read(NVRAM, addr + 1);
-    return retval;
-}
-
-static uint32_t nvram_readl (void *opaque, hwaddr addr)
-{
-    M48t59State *NVRAM = opaque;
-    uint32_t retval;
-
-    retval = m48t59_read(NVRAM, addr) << 24;
-    retval |= m48t59_read(NVRAM, addr + 1) << 16;
-    retval |= m48t59_read(NVRAM, addr + 2) << 8;
-    retval |= m48t59_read(NVRAM, addr + 3);
-    return retval;
-}
-
-static const MemoryRegionOps nvram_ops = {
-    .old_mmio = {
-        .read = { nvram_readb, nvram_readw, nvram_readl, },
-        .write = { nvram_writeb, nvram_writew, nvram_writel, },
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static const VMStateDescription vmstate_m48t59 = {
-    .name = "m48t59",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT8(lock, M48t59State),
-        VMSTATE_UINT16(addr, M48t59State),
-        VMSTATE_VBUFFER_UINT32(buffer, M48t59State, 0, NULL, 0, size),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void m48t59_reset_common(M48t59State *NVRAM)
-{
-    NVRAM->addr = 0;
-    NVRAM->lock = 0;
-    if (NVRAM->alrm_timer != NULL)
-        qemu_del_timer(NVRAM->alrm_timer);
-
-    if (NVRAM->wd_timer != NULL)
-        qemu_del_timer(NVRAM->wd_timer);
-}
-
-static void m48t59_reset_isa(DeviceState *d)
-{
-    M48t59ISAState *isa = container_of(d, M48t59ISAState, busdev.qdev);
-    M48t59State *NVRAM = &isa->state;
-
-    m48t59_reset_common(NVRAM);
-}
-
-static void m48t59_reset_sysbus(DeviceState *d)
-{
-    M48t59SysBusState *sys = container_of(d, M48t59SysBusState, busdev.qdev);
-    M48t59State *NVRAM = &sys->state;
-
-    m48t59_reset_common(NVRAM);
-}
-
-static const MemoryRegionOps m48t59_io_ops = {
-    .read = NVRAM_readb,
-    .write = NVRAM_writeb,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-/* Initialisation routine */
-M48t59State *m48t59_init(qemu_irq IRQ, hwaddr mem_base,
-                         uint32_t io_base, uint16_t size, int model)
-{
-    DeviceState *dev;
-    SysBusDevice *s;
-    M48t59SysBusState *d;
-    M48t59State *state;
-
-    dev = qdev_create(NULL, "m48t59");
-    qdev_prop_set_uint32(dev, "model", model);
-    qdev_prop_set_uint32(dev, "size", size);
-    qdev_prop_set_uint32(dev, "io_base", io_base);
-    qdev_init_nofail(dev);
-    s = SYS_BUS_DEVICE(dev);
-    d = FROM_SYSBUS(M48t59SysBusState, s);
-    state = &d->state;
-    sysbus_connect_irq(s, 0, IRQ);
-    memory_region_init_io(&d->io, &m48t59_io_ops, state, "m48t59", 4);
-    if (io_base != 0) {
-        memory_region_add_subregion(get_system_io(), io_base, &d->io);
-    }
-    if (mem_base != 0) {
-        sysbus_mmio_map(s, 0, mem_base);
-    }
-
-    return state;
-}
-
-M48t59State *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size,
-                             int model)
-{
-    M48t59ISAState *d;
-    ISADevice *dev;
-    M48t59State *s;
-
-    dev = isa_create(bus, "m48t59_isa");
-    qdev_prop_set_uint32(&dev->qdev, "model", model);
-    qdev_prop_set_uint32(&dev->qdev, "size", size);
-    qdev_prop_set_uint32(&dev->qdev, "io_base", io_base);
-    qdev_init_nofail(&dev->qdev);
-    d = DO_UPCAST(M48t59ISAState, busdev, dev);
-    s = &d->state;
-
-    memory_region_init_io(&d->io, &m48t59_io_ops, s, "m48t59", 4);
-    if (io_base != 0) {
-        isa_register_ioport(dev, &d->io, io_base);
-    }
-
-    return s;
-}
-
-static void m48t59_init_common(M48t59State *s)
-{
-    s->buffer = g_malloc0(s->size);
-    if (s->model == 59) {
-        s->alrm_timer = qemu_new_timer_ns(rtc_clock, &alarm_cb, s);
-        s->wd_timer = qemu_new_timer_ns(vm_clock, &watchdog_cb, s);
-    }
-    qemu_get_timedate(&s->alarm, 0);
-
-    vmstate_register(NULL, -1, &vmstate_m48t59, s);
-}
-
-static int m48t59_init_isa1(ISADevice *dev)
-{
-    M48t59ISAState *d = DO_UPCAST(M48t59ISAState, busdev, dev);
-    M48t59State *s = &d->state;
-
-    isa_init_irq(dev, &s->IRQ, 8);
-    m48t59_init_common(s);
-
-    return 0;
-}
-
-static int m48t59_init1(SysBusDevice *dev)
-{
-    M48t59SysBusState *d = FROM_SYSBUS(M48t59SysBusState, dev);
-    M48t59State *s = &d->state;
-
-    sysbus_init_irq(dev, &s->IRQ);
-
-    memory_region_init_io(&s->iomem, &nvram_ops, s, "m48t59.nvram", s->size);
-    sysbus_init_mmio(dev, &s->iomem);
-    m48t59_init_common(s);
-
-    return 0;
-}
-
-static Property m48t59_isa_properties[] = {
-    DEFINE_PROP_UINT32("size",    M48t59ISAState, state.size,    -1),
-    DEFINE_PROP_UINT32("model",   M48t59ISAState, state.model,   -1),
-    DEFINE_PROP_HEX32( "io_base", M48t59ISAState, state.io_base,  0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void m48t59_init_class_isa1(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
-    ic->init = m48t59_init_isa1;
-    dc->no_user = 1;
-    dc->reset = m48t59_reset_isa;
-    dc->props = m48t59_isa_properties;
-}
-
-static const TypeInfo m48t59_isa_info = {
-    .name          = "m48t59_isa",
-    .parent        = TYPE_ISA_DEVICE,
-    .instance_size = sizeof(M48t59ISAState),
-    .class_init    = m48t59_init_class_isa1,
-};
-
-static Property m48t59_properties[] = {
-    DEFINE_PROP_UINT32("size",    M48t59SysBusState, state.size,    -1),
-    DEFINE_PROP_UINT32("model",   M48t59SysBusState, state.model,   -1),
-    DEFINE_PROP_HEX32( "io_base", M48t59SysBusState, state.io_base,  0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void m48t59_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = m48t59_init1;
-    dc->reset = m48t59_reset_sysbus;
-    dc->props = m48t59_properties;
-}
-
-static const TypeInfo m48t59_info = {
-    .name          = "m48t59",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(M48t59SysBusState),
-    .class_init    = m48t59_class_init,
-};
-
-static void m48t59_register_types(void)
-{
-    type_register_static(&m48t59_info);
-    type_register_static(&m48t59_isa_info);
-}
-
-type_init(m48t59_register_types)
diff --git a/hw/mac_dbdma.c b/hw/mac_dbdma.c
deleted file mode 100644 (file)
index a2363bb..0000000
+++ /dev/null
@@ -1,859 +0,0 @@
-/*
- * PowerMac descriptor-based DMA emulation
- *
- * Copyright (c) 2005-2007 Fabrice Bellard
- * Copyright (c) 2007 Jocelyn Mayer
- * Copyright (c) 2009 Laurent Vivier
- *
- * some parts from linux-2.6.28, arch/powerpc/include/asm/dbdma.h
- *
- *   Definitions for using the Apple Descriptor-Based DMA controller
- *   in Power Macintosh computers.
- *
- *   Copyright (C) 1996 Paul Mackerras.
- *
- * some parts from mol 0.9.71
- *
- *   Descriptor based DMA emulation
- *
- *   Copyright (C) 1998-2004 Samuel Rydh (samuel@ibrium.se)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/isa/isa.h"
-#include "hw/ppc/mac_dbdma.h"
-#include "qemu/main-loop.h"
-
-/* debug DBDMA */
-//#define DEBUG_DBDMA
-
-#ifdef DEBUG_DBDMA
-#define DBDMA_DPRINTF(fmt, ...)                                 \
-    do { printf("DBDMA: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DBDMA_DPRINTF(fmt, ...)
-#endif
-
-/*
- */
-
-/*
- * DBDMA control/status registers.  All little-endian.
- */
-
-#define DBDMA_CONTROL         0x00
-#define DBDMA_STATUS          0x01
-#define DBDMA_CMDPTR_HI       0x02
-#define DBDMA_CMDPTR_LO       0x03
-#define DBDMA_INTR_SEL        0x04
-#define DBDMA_BRANCH_SEL      0x05
-#define DBDMA_WAIT_SEL        0x06
-#define DBDMA_XFER_MODE       0x07
-#define DBDMA_DATA2PTR_HI     0x08
-#define DBDMA_DATA2PTR_LO     0x09
-#define DBDMA_RES1            0x0A
-#define DBDMA_ADDRESS_HI      0x0B
-#define DBDMA_BRANCH_ADDR_HI  0x0C
-#define DBDMA_RES2            0x0D
-#define DBDMA_RES3            0x0E
-#define DBDMA_RES4            0x0F
-
-#define DBDMA_REGS            16
-#define DBDMA_SIZE            (DBDMA_REGS * sizeof(uint32_t))
-
-#define DBDMA_CHANNEL_SHIFT   7
-#define DBDMA_CHANNEL_SIZE    (1 << DBDMA_CHANNEL_SHIFT)
-
-#define DBDMA_CHANNELS        (0x1000 >> DBDMA_CHANNEL_SHIFT)
-
-/* Bits in control and status registers */
-
-#define RUN    0x8000
-#define PAUSE  0x4000
-#define FLUSH  0x2000
-#define WAKE   0x1000
-#define DEAD   0x0800
-#define ACTIVE 0x0400
-#define BT     0x0100
-#define DEVSTAT        0x00ff
-
-/*
- * DBDMA command structure.  These fields are all little-endian!
- */
-
-typedef struct dbdma_cmd {
-    uint16_t req_count;          /* requested byte transfer count */
-    uint16_t command;    /* command word (has bit-fields) */
-    uint32_t phy_addr;   /* physical data address */
-    uint32_t cmd_dep;    /* command-dependent field */
-    uint16_t res_count;          /* residual count after completion */
-    uint16_t xfer_status; /* transfer status */
-} dbdma_cmd;
-
-/* DBDMA command values in command field */
-
-#define COMMAND_MASK    0xf000
-#define OUTPUT_MORE    0x0000  /* transfer memory data to stream */
-#define OUTPUT_LAST    0x1000  /* ditto followed by end marker */
-#define INPUT_MORE     0x2000  /* transfer stream data to memory */
-#define INPUT_LAST     0x3000  /* ditto, expect end marker */
-#define STORE_WORD     0x4000  /* write word (4 bytes) to device reg */
-#define LOAD_WORD      0x5000  /* read word (4 bytes) from device reg */
-#define DBDMA_NOP      0x6000  /* do nothing */
-#define DBDMA_STOP     0x7000  /* suspend processing */
-
-/* Key values in command field */
-
-#define KEY_MASK        0x0700
-#define KEY_STREAM0    0x0000  /* usual data stream */
-#define KEY_STREAM1    0x0100  /* control/status stream */
-#define KEY_STREAM2    0x0200  /* device-dependent stream */
-#define KEY_STREAM3    0x0300  /* device-dependent stream */
-#define KEY_STREAM4    0x0400  /* reserved */
-#define KEY_REGS       0x0500  /* device register space */
-#define KEY_SYSTEM     0x0600  /* system memory-mapped space */
-#define KEY_DEVICE     0x0700  /* device memory-mapped space */
-
-/* Interrupt control values in command field */
-
-#define INTR_MASK       0x0030
-#define INTR_NEVER     0x0000  /* don't interrupt */
-#define INTR_IFSET     0x0010  /* intr if condition bit is 1 */
-#define INTR_IFCLR     0x0020  /* intr if condition bit is 0 */
-#define INTR_ALWAYS    0x0030  /* always interrupt */
-
-/* Branch control values in command field */
-
-#define BR_MASK         0x000c
-#define BR_NEVER       0x0000  /* don't branch */
-#define BR_IFSET       0x0004  /* branch if condition bit is 1 */
-#define BR_IFCLR       0x0008  /* branch if condition bit is 0 */
-#define BR_ALWAYS      0x000c  /* always branch */
-
-/* Wait control values in command field */
-
-#define WAIT_MASK       0x0003
-#define WAIT_NEVER     0x0000  /* don't wait */
-#define WAIT_IFSET     0x0001  /* wait if condition bit is 1 */
-#define WAIT_IFCLR     0x0002  /* wait if condition bit is 0 */
-#define WAIT_ALWAYS    0x0003  /* always wait */
-
-typedef struct DBDMA_channel {
-    int channel;
-    uint32_t regs[DBDMA_REGS];
-    qemu_irq irq;
-    DBDMA_io io;
-    DBDMA_rw rw;
-    DBDMA_flush flush;
-    dbdma_cmd current;
-    int processing;
-} DBDMA_channel;
-
-typedef struct {
-    MemoryRegion mem;
-    DBDMA_channel channels[DBDMA_CHANNELS];
-} DBDMAState;
-
-#ifdef DEBUG_DBDMA
-static void dump_dbdma_cmd(dbdma_cmd *cmd)
-{
-    printf("dbdma_cmd %p\n", cmd);
-    printf("    req_count 0x%04x\n", le16_to_cpu(cmd->req_count));
-    printf("    command 0x%04x\n", le16_to_cpu(cmd->command));
-    printf("    phy_addr 0x%08x\n", le32_to_cpu(cmd->phy_addr));
-    printf("    cmd_dep 0x%08x\n", le32_to_cpu(cmd->cmd_dep));
-    printf("    res_count 0x%04x\n", le16_to_cpu(cmd->res_count));
-    printf("    xfer_status 0x%04x\n", le16_to_cpu(cmd->xfer_status));
-}
-#else
-static void dump_dbdma_cmd(dbdma_cmd *cmd)
-{
-}
-#endif
-static void dbdma_cmdptr_load(DBDMA_channel *ch)
-{
-    DBDMA_DPRINTF("dbdma_cmdptr_load 0x%08x\n",
-                  ch->regs[DBDMA_CMDPTR_LO]);
-    cpu_physical_memory_read(ch->regs[DBDMA_CMDPTR_LO],
-                             (uint8_t*)&ch->current, sizeof(dbdma_cmd));
-}
-
-static void dbdma_cmdptr_save(DBDMA_channel *ch)
-{
-    DBDMA_DPRINTF("dbdma_cmdptr_save 0x%08x\n",
-                  ch->regs[DBDMA_CMDPTR_LO]);
-    DBDMA_DPRINTF("xfer_status 0x%08x res_count 0x%04x\n",
-                  le16_to_cpu(ch->current.xfer_status),
-                  le16_to_cpu(ch->current.res_count));
-    cpu_physical_memory_write(ch->regs[DBDMA_CMDPTR_LO],
-                              (uint8_t*)&ch->current, sizeof(dbdma_cmd));
-}
-
-static void kill_channel(DBDMA_channel *ch)
-{
-    DBDMA_DPRINTF("kill_channel\n");
-
-    ch->regs[DBDMA_STATUS] |= DEAD;
-    ch->regs[DBDMA_STATUS] &= ~ACTIVE;
-
-    qemu_irq_raise(ch->irq);
-}
-
-static void conditional_interrupt(DBDMA_channel *ch)
-{
-    dbdma_cmd *current = &ch->current;
-    uint16_t intr;
-    uint16_t sel_mask, sel_value;
-    uint32_t status;
-    int cond;
-
-    DBDMA_DPRINTF("conditional_interrupt\n");
-
-    intr = le16_to_cpu(current->command) & INTR_MASK;
-
-    switch(intr) {
-    case INTR_NEVER:  /* don't interrupt */
-        return;
-    case INTR_ALWAYS: /* always interrupt */
-        qemu_irq_raise(ch->irq);
-        return;
-    }
-
-    status = ch->regs[DBDMA_STATUS] & DEVSTAT;
-
-    sel_mask = (ch->regs[DBDMA_INTR_SEL] >> 16) & 0x0f;
-    sel_value = ch->regs[DBDMA_INTR_SEL] & 0x0f;
-
-    cond = (status & sel_mask) == (sel_value & sel_mask);
-
-    switch(intr) {
-    case INTR_IFSET:  /* intr if condition bit is 1 */
-        if (cond)
-            qemu_irq_raise(ch->irq);
-        return;
-    case INTR_IFCLR:  /* intr if condition bit is 0 */
-        if (!cond)
-            qemu_irq_raise(ch->irq);
-        return;
-    }
-}
-
-static int conditional_wait(DBDMA_channel *ch)
-{
-    dbdma_cmd *current = &ch->current;
-    uint16_t wait;
-    uint16_t sel_mask, sel_value;
-    uint32_t status;
-    int cond;
-
-    DBDMA_DPRINTF("conditional_wait\n");
-
-    wait = le16_to_cpu(current->command) & WAIT_MASK;
-
-    switch(wait) {
-    case WAIT_NEVER:  /* don't wait */
-        return 0;
-    case WAIT_ALWAYS: /* always wait */
-        return 1;
-    }
-
-    status = ch->regs[DBDMA_STATUS] & DEVSTAT;
-
-    sel_mask = (ch->regs[DBDMA_WAIT_SEL] >> 16) & 0x0f;
-    sel_value = ch->regs[DBDMA_WAIT_SEL] & 0x0f;
-
-    cond = (status & sel_mask) == (sel_value & sel_mask);
-
-    switch(wait) {
-    case WAIT_IFSET:  /* wait if condition bit is 1 */
-        if (cond)
-            return 1;
-        return 0;
-    case WAIT_IFCLR:  /* wait if condition bit is 0 */
-        if (!cond)
-            return 1;
-        return 0;
-    }
-    return 0;
-}
-
-static void next(DBDMA_channel *ch)
-{
-    uint32_t cp;
-
-    ch->regs[DBDMA_STATUS] &= ~BT;
-
-    cp = ch->regs[DBDMA_CMDPTR_LO];
-    ch->regs[DBDMA_CMDPTR_LO] = cp + sizeof(dbdma_cmd);
-    dbdma_cmdptr_load(ch);
-}
-
-static void branch(DBDMA_channel *ch)
-{
-    dbdma_cmd *current = &ch->current;
-
-    ch->regs[DBDMA_CMDPTR_LO] = current->cmd_dep;
-    ch->regs[DBDMA_STATUS] |= BT;
-    dbdma_cmdptr_load(ch);
-}
-
-static void conditional_branch(DBDMA_channel *ch)
-{
-    dbdma_cmd *current = &ch->current;
-    uint16_t br;
-    uint16_t sel_mask, sel_value;
-    uint32_t status;
-    int cond;
-
-    DBDMA_DPRINTF("conditional_branch\n");
-
-    /* check if we must branch */
-
-    br = le16_to_cpu(current->command) & BR_MASK;
-
-    switch(br) {
-    case BR_NEVER:  /* don't branch */
-        next(ch);
-        return;
-    case BR_ALWAYS: /* always branch */
-        branch(ch);
-        return;
-    }
-
-    status = ch->regs[DBDMA_STATUS] & DEVSTAT;
-
-    sel_mask = (ch->regs[DBDMA_BRANCH_SEL] >> 16) & 0x0f;
-    sel_value = ch->regs[DBDMA_BRANCH_SEL] & 0x0f;
-
-    cond = (status & sel_mask) == (sel_value & sel_mask);
-
-    switch(br) {
-    case BR_IFSET:  /* branch if condition bit is 1 */
-        if (cond)
-            branch(ch);
-        else
-            next(ch);
-        return;
-    case BR_IFCLR:  /* branch if condition bit is 0 */
-        if (!cond)
-            branch(ch);
-        else
-            next(ch);
-        return;
-    }
-}
-
-static QEMUBH *dbdma_bh;
-static void channel_run(DBDMA_channel *ch);
-
-static void dbdma_end(DBDMA_io *io)
-{
-    DBDMA_channel *ch = io->channel;
-    dbdma_cmd *current = &ch->current;
-
-    if (conditional_wait(ch))
-        goto wait;
-
-    current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]);
-    current->res_count = cpu_to_le16(io->len);
-    dbdma_cmdptr_save(ch);
-    if (io->is_last)
-        ch->regs[DBDMA_STATUS] &= ~FLUSH;
-
-    conditional_interrupt(ch);
-    conditional_branch(ch);
-
-wait:
-    ch->processing = 0;
-    if ((ch->regs[DBDMA_STATUS] & RUN) &&
-        (ch->regs[DBDMA_STATUS] & ACTIVE))
-        channel_run(ch);
-}
-
-static void start_output(DBDMA_channel *ch, int key, uint32_t addr,
-                        uint16_t req_count, int is_last)
-{
-    DBDMA_DPRINTF("start_output\n");
-
-    /* KEY_REGS, KEY_DEVICE and KEY_STREAM
-     * are not implemented in the mac-io chip
-     */
-
-    DBDMA_DPRINTF("addr 0x%x key 0x%x\n", addr, key);
-    if (!addr || key > KEY_STREAM3) {
-        kill_channel(ch);
-        return;
-    }
-
-    ch->io.addr = addr;
-    ch->io.len = req_count;
-    ch->io.is_last = is_last;
-    ch->io.dma_end = dbdma_end;
-    ch->io.is_dma_out = 1;
-    ch->processing = 1;
-    if (ch->rw) {
-        ch->rw(&ch->io);
-    }
-}
-
-static void start_input(DBDMA_channel *ch, int key, uint32_t addr,
-                       uint16_t req_count, int is_last)
-{
-    DBDMA_DPRINTF("start_input\n");
-
-    /* KEY_REGS, KEY_DEVICE and KEY_STREAM
-     * are not implemented in the mac-io chip
-     */
-
-    if (!addr || key > KEY_STREAM3) {
-        kill_channel(ch);
-        return;
-    }
-
-    ch->io.addr = addr;
-    ch->io.len = req_count;
-    ch->io.is_last = is_last;
-    ch->io.dma_end = dbdma_end;
-    ch->io.is_dma_out = 0;
-    ch->processing = 1;
-    if (ch->rw) {
-        ch->rw(&ch->io);
-    }
-}
-
-static void load_word(DBDMA_channel *ch, int key, uint32_t addr,
-                     uint16_t len)
-{
-    dbdma_cmd *current = &ch->current;
-    uint32_t val;
-
-    DBDMA_DPRINTF("load_word\n");
-
-    /* only implements KEY_SYSTEM */
-
-    if (key != KEY_SYSTEM) {
-        printf("DBDMA: LOAD_WORD, unimplemented key %x\n", key);
-        kill_channel(ch);
-        return;
-    }
-
-    cpu_physical_memory_read(addr, (uint8_t*)&val, len);
-
-    if (len == 2)
-        val = (val << 16) | (current->cmd_dep & 0x0000ffff);
-    else if (len == 1)
-        val = (val << 24) | (current->cmd_dep & 0x00ffffff);
-
-    current->cmd_dep = val;
-
-    if (conditional_wait(ch))
-        goto wait;
-
-    current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]);
-    dbdma_cmdptr_save(ch);
-    ch->regs[DBDMA_STATUS] &= ~FLUSH;
-
-    conditional_interrupt(ch);
-    next(ch);
-
-wait:
-    qemu_bh_schedule(dbdma_bh);
-}
-
-static void store_word(DBDMA_channel *ch, int key, uint32_t addr,
-                      uint16_t len)
-{
-    dbdma_cmd *current = &ch->current;
-    uint32_t val;
-
-    DBDMA_DPRINTF("store_word\n");
-
-    /* only implements KEY_SYSTEM */
-
-    if (key != KEY_SYSTEM) {
-        printf("DBDMA: STORE_WORD, unimplemented key %x\n", key);
-        kill_channel(ch);
-        return;
-    }
-
-    val = current->cmd_dep;
-    if (len == 2)
-        val >>= 16;
-    else if (len == 1)
-        val >>= 24;
-
-    cpu_physical_memory_write(addr, (uint8_t*)&val, len);
-
-    if (conditional_wait(ch))
-        goto wait;
-
-    current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]);
-    dbdma_cmdptr_save(ch);
-    ch->regs[DBDMA_STATUS] &= ~FLUSH;
-
-    conditional_interrupt(ch);
-    next(ch);
-
-wait:
-    qemu_bh_schedule(dbdma_bh);
-}
-
-static void nop(DBDMA_channel *ch)
-{
-    dbdma_cmd *current = &ch->current;
-
-    if (conditional_wait(ch))
-        goto wait;
-
-    current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]);
-    dbdma_cmdptr_save(ch);
-
-    conditional_interrupt(ch);
-    conditional_branch(ch);
-
-wait:
-    qemu_bh_schedule(dbdma_bh);
-}
-
-static void stop(DBDMA_channel *ch)
-{
-    ch->regs[DBDMA_STATUS] &= ~(ACTIVE|DEAD|FLUSH);
-
-    /* the stop command does not increment command pointer */
-}
-
-static void channel_run(DBDMA_channel *ch)
-{
-    dbdma_cmd *current = &ch->current;
-    uint16_t cmd, key;
-    uint16_t req_count;
-    uint32_t phy_addr;
-
-    DBDMA_DPRINTF("channel_run\n");
-    dump_dbdma_cmd(current);
-
-    /* clear WAKE flag at command fetch */
-
-    ch->regs[DBDMA_STATUS] &= ~WAKE;
-
-    cmd = le16_to_cpu(current->command) & COMMAND_MASK;
-
-    switch (cmd) {
-    case DBDMA_NOP:
-        nop(ch);
-       return;
-
-    case DBDMA_STOP:
-        stop(ch);
-       return;
-    }
-
-    key = le16_to_cpu(current->command) & 0x0700;
-    req_count = le16_to_cpu(current->req_count);
-    phy_addr = le32_to_cpu(current->phy_addr);
-
-    if (key == KEY_STREAM4) {
-        printf("command %x, invalid key 4\n", cmd);
-        kill_channel(ch);
-        return;
-    }
-
-    switch (cmd) {
-    case OUTPUT_MORE:
-        start_output(ch, key, phy_addr, req_count, 0);
-       return;
-
-    case OUTPUT_LAST:
-        start_output(ch, key, phy_addr, req_count, 1);
-       return;
-
-    case INPUT_MORE:
-        start_input(ch, key, phy_addr, req_count, 0);
-       return;
-
-    case INPUT_LAST:
-        start_input(ch, key, phy_addr, req_count, 1);
-       return;
-    }
-
-    if (key < KEY_REGS) {
-        printf("command %x, invalid key %x\n", cmd, key);
-        key = KEY_SYSTEM;
-    }
-
-    /* for LOAD_WORD and STORE_WORD, req_count is on 3 bits
-     * and BRANCH is invalid
-     */
-
-    req_count = req_count & 0x0007;
-    if (req_count & 0x4) {
-        req_count = 4;
-        phy_addr &= ~3;
-    } else if (req_count & 0x2) {
-        req_count = 2;
-        phy_addr &= ~1;
-    } else
-        req_count = 1;
-
-    switch (cmd) {
-    case LOAD_WORD:
-        load_word(ch, key, phy_addr, req_count);
-       return;
-
-    case STORE_WORD:
-        store_word(ch, key, phy_addr, req_count);
-       return;
-    }
-}
-
-static void DBDMA_run(DBDMAState *s)
-{
-    int channel;
-
-    for (channel = 0; channel < DBDMA_CHANNELS; channel++) {
-        DBDMA_channel *ch = &s->channels[channel];
-        uint32_t status = ch->regs[DBDMA_STATUS];
-        if (!ch->processing && (status & RUN) && (status & ACTIVE)) {
-            channel_run(ch);
-        }
-    }
-}
-
-static void DBDMA_run_bh(void *opaque)
-{
-    DBDMAState *s = opaque;
-
-    DBDMA_DPRINTF("DBDMA_run_bh\n");
-
-    DBDMA_run(s);
-}
-
-void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq,
-                            DBDMA_rw rw, DBDMA_flush flush,
-                            void *opaque)
-{
-    DBDMAState *s = dbdma;
-    DBDMA_channel *ch = &s->channels[nchan];
-
-    DBDMA_DPRINTF("DBDMA_register_channel 0x%x\n", nchan);
-
-    ch->irq = irq;
-    ch->channel = nchan;
-    ch->rw = rw;
-    ch->flush = flush;
-    ch->io.opaque = opaque;
-    ch->io.channel = ch;
-}
-
-static void
-dbdma_control_write(DBDMA_channel *ch)
-{
-    uint16_t mask, value;
-    uint32_t status;
-
-    mask = (ch->regs[DBDMA_CONTROL] >> 16) & 0xffff;
-    value = ch->regs[DBDMA_CONTROL] & 0xffff;
-
-    value &= (RUN | PAUSE | FLUSH | WAKE | DEVSTAT);
-
-    status = ch->regs[DBDMA_STATUS];
-
-    status = (value & mask) | (status & ~mask);
-
-    if (status & WAKE)
-        status |= ACTIVE;
-    if (status & RUN) {
-        status |= ACTIVE;
-        status &= ~DEAD;
-    }
-    if (status & PAUSE)
-        status &= ~ACTIVE;
-    if ((ch->regs[DBDMA_STATUS] & RUN) && !(status & RUN)) {
-        /* RUN is cleared */
-        status &= ~(ACTIVE|DEAD);
-        if ((status & FLUSH) && ch->flush) {
-            ch->flush(&ch->io);
-            status &= ~FLUSH;
-        }
-    }
-
-    DBDMA_DPRINTF("    status 0x%08x\n", status);
-
-    ch->regs[DBDMA_STATUS] = status;
-
-    if (status & ACTIVE)
-        qemu_bh_schedule(dbdma_bh);
-    if ((status & FLUSH) && ch->flush)
-        ch->flush(&ch->io);
-}
-
-static void dbdma_write(void *opaque, hwaddr addr,
-                        uint64_t value, unsigned size)
-{
-    int channel = addr >> DBDMA_CHANNEL_SHIFT;
-    DBDMAState *s = opaque;
-    DBDMA_channel *ch = &s->channels[channel];
-    int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2;
-
-    DBDMA_DPRINTF("writel 0x" TARGET_FMT_plx " <= 0x%08x\n", addr, value);
-    DBDMA_DPRINTF("channel 0x%x reg 0x%x\n",
-                  (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg);
-
-    /* cmdptr cannot be modified if channel is RUN or ACTIVE */
-
-    if (reg == DBDMA_CMDPTR_LO &&
-        (ch->regs[DBDMA_STATUS] & (RUN | ACTIVE)))
-       return;
-
-    ch->regs[reg] = value;
-
-    switch(reg) {
-    case DBDMA_CONTROL:
-        dbdma_control_write(ch);
-        break;
-    case DBDMA_CMDPTR_LO:
-        /* 16-byte aligned */
-        ch->regs[DBDMA_CMDPTR_LO] &= ~0xf;
-        dbdma_cmdptr_load(ch);
-        break;
-    case DBDMA_STATUS:
-    case DBDMA_INTR_SEL:
-    case DBDMA_BRANCH_SEL:
-    case DBDMA_WAIT_SEL:
-        /* nothing to do */
-        break;
-    case DBDMA_XFER_MODE:
-    case DBDMA_CMDPTR_HI:
-    case DBDMA_DATA2PTR_HI:
-    case DBDMA_DATA2PTR_LO:
-    case DBDMA_ADDRESS_HI:
-    case DBDMA_BRANCH_ADDR_HI:
-    case DBDMA_RES1:
-    case DBDMA_RES2:
-    case DBDMA_RES3:
-    case DBDMA_RES4:
-        /* unused */
-        break;
-    }
-}
-
-static uint64_t dbdma_read(void *opaque, hwaddr addr,
-                           unsigned size)
-{
-    uint32_t value;
-    int channel = addr >> DBDMA_CHANNEL_SHIFT;
-    DBDMAState *s = opaque;
-    DBDMA_channel *ch = &s->channels[channel];
-    int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2;
-
-    value = ch->regs[reg];
-
-    DBDMA_DPRINTF("readl 0x" TARGET_FMT_plx " => 0x%08x\n", addr, value);
-    DBDMA_DPRINTF("channel 0x%x reg 0x%x\n",
-                  (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg);
-
-    switch(reg) {
-    case DBDMA_CONTROL:
-        value = 0;
-        break;
-    case DBDMA_STATUS:
-    case DBDMA_CMDPTR_LO:
-    case DBDMA_INTR_SEL:
-    case DBDMA_BRANCH_SEL:
-    case DBDMA_WAIT_SEL:
-        /* nothing to do */
-        break;
-    case DBDMA_XFER_MODE:
-    case DBDMA_CMDPTR_HI:
-    case DBDMA_DATA2PTR_HI:
-    case DBDMA_DATA2PTR_LO:
-    case DBDMA_ADDRESS_HI:
-    case DBDMA_BRANCH_ADDR_HI:
-        /* unused */
-        value = 0;
-        break;
-    case DBDMA_RES1:
-    case DBDMA_RES2:
-    case DBDMA_RES3:
-    case DBDMA_RES4:
-        /* reserved */
-        break;
-    }
-
-    return value;
-}
-
-static const MemoryRegionOps dbdma_ops = {
-    .read = dbdma_read,
-    .write = dbdma_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .valid = {
-        .min_access_size = 4,
-        .max_access_size = 4,
-    },
-};
-
-static const VMStateDescription vmstate_dbdma_channel = {
-    .name = "dbdma_channel",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT32_ARRAY(regs, struct DBDMA_channel, DBDMA_REGS),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_dbdma = {
-    .name = "dbdma",
-    .version_id = 2,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .fields      = (VMStateField[]) {
-        VMSTATE_STRUCT_ARRAY(channels, DBDMAState, DBDMA_CHANNELS, 1,
-                             vmstate_dbdma_channel, DBDMA_channel),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void dbdma_reset(void *opaque)
-{
-    DBDMAState *s = opaque;
-    int i;
-
-    for (i = 0; i < DBDMA_CHANNELS; i++)
-        memset(s->channels[i].regs, 0, DBDMA_SIZE);
-}
-
-void* DBDMA_init (MemoryRegion **dbdma_mem)
-{
-    DBDMAState *s;
-
-    s = g_malloc0(sizeof(DBDMAState));
-
-    memory_region_init_io(&s->mem, &dbdma_ops, s, "dbdma", 0x1000);
-    *dbdma_mem = &s->mem;
-    vmstate_register(NULL, -1, &vmstate_dbdma, s);
-    qemu_register_reset(dbdma_reset, s);
-
-    dbdma_bh = qemu_bh_new(DBDMA_run_bh, s);
-
-    return s;
-}
diff --git a/hw/mac_nvram.c b/hw/mac_nvram.c
deleted file mode 100644 (file)
index 5223330..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * PowerMac NVRAM emulation
- *
- * Copyright (c) 2005-2007 Fabrice Bellard
- * Copyright (c) 2007 Jocelyn Mayer
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/sparc/firmware_abi.h"
-#include "sysemu/sysemu.h"
-#include "hw/ppc/mac.h"
-
-/* debug NVR */
-//#define DEBUG_NVR
-
-#ifdef DEBUG_NVR
-#define NVR_DPRINTF(fmt, ...)                                   \
-    do { printf("NVR: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define NVR_DPRINTF(fmt, ...)
-#endif
-
-#define DEF_SYSTEM_SIZE 0xc10
-
-/* Direct access to NVRAM */
-uint8_t macio_nvram_read(MacIONVRAMState *s, uint32_t addr)
-{
-    uint32_t ret;
-
-    if (addr < s->size) {
-        ret = s->data[addr];
-    } else {
-        ret = -1;
-    }
-    NVR_DPRINTF("read addr %04" PRIx32 " val %" PRIx8 "\n", addr, ret);
-
-    return ret;
-}
-
-void macio_nvram_write(MacIONVRAMState *s, uint32_t addr, uint8_t val)
-{
-    NVR_DPRINTF("write addr %04" PRIx32 " val %" PRIx8 "\n", addr, val);
-    if (addr < s->size) {
-        s->data[addr] = val;
-    }
-}
-
-/* macio style NVRAM device */
-static void macio_nvram_writeb(void *opaque, hwaddr addr,
-                               uint64_t value, unsigned size)
-{
-    MacIONVRAMState *s = opaque;
-
-    addr = (addr >> s->it_shift) & (s->size - 1);
-    s->data[addr] = value;
-    NVR_DPRINTF("writeb addr %04" PHYS_PRIx " val %" PRIx64 "\n", addr, value);
-}
-
-static uint64_t macio_nvram_readb(void *opaque, hwaddr addr,
-                                  unsigned size)
-{
-    MacIONVRAMState *s = opaque;
-    uint32_t value;
-
-    addr = (addr >> s->it_shift) & (s->size - 1);
-    value = s->data[addr];
-    NVR_DPRINTF("readb addr %04x val %x\n", (int)addr, value);
-
-    return value;
-}
-
-static const MemoryRegionOps macio_nvram_ops = {
-    .read = macio_nvram_readb,
-    .write = macio_nvram_writeb,
-    .endianness = DEVICE_BIG_ENDIAN,
-};
-
-static const VMStateDescription vmstate_macio_nvram = {
-    .name = "macio_nvram",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_VBUFFER_UINT32(data, MacIONVRAMState, 0, NULL, 0, size),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-
-static void macio_nvram_reset(DeviceState *dev)
-{
-}
-
-static void macio_nvram_realizefn(DeviceState *dev, Error **errp)
-{
-    SysBusDevice *d = SYS_BUS_DEVICE(dev);
-    MacIONVRAMState *s = MACIO_NVRAM(dev);
-
-    s->data = g_malloc0(s->size);
-
-    memory_region_init_io(&s->mem, &macio_nvram_ops, s, "macio-nvram",
-                          s->size << s->it_shift);
-    sysbus_init_mmio(d, &s->mem);
-}
-
-static void macio_nvram_unrealizefn(DeviceState *dev, Error **errp)
-{
-    MacIONVRAMState *s = MACIO_NVRAM(dev);
-
-    g_free(s->data);
-}
-
-static Property macio_nvram_properties[] = {
-    DEFINE_PROP_UINT32("size", MacIONVRAMState, size, 0),
-    DEFINE_PROP_UINT32("it_shift", MacIONVRAMState, it_shift, 0),
-    DEFINE_PROP_END_OF_LIST()
-};
-
-static void macio_nvram_class_init(ObjectClass *oc, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(oc);
-
-    dc->realize = macio_nvram_realizefn;
-    dc->unrealize = macio_nvram_unrealizefn;
-    dc->reset = macio_nvram_reset;
-    dc->vmsd = &vmstate_macio_nvram;
-    dc->props = macio_nvram_properties;
-}
-
-static const TypeInfo macio_nvram_type_info = {
-    .name = TYPE_MACIO_NVRAM,
-    .parent = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(MacIONVRAMState),
-    .class_init = macio_nvram_class_init,
-};
-
-static void macio_nvram_register_types(void)
-{
-    type_register_static(&macio_nvram_type_info);
-}
-
-/* Set up a system OpenBIOS NVRAM partition */
-void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len)
-{
-    unsigned int i;
-    uint32_t start = 0, end;
-    struct OpenBIOS_nvpart_v1 *part_header;
-
-    // OpenBIOS nvram variables
-    // Variable partition
-    part_header = (struct OpenBIOS_nvpart_v1 *)nvr->data;
-    part_header->signature = OPENBIOS_PART_SYSTEM;
-    pstrcpy(part_header->name, sizeof(part_header->name), "system");
-
-    end = start + sizeof(struct OpenBIOS_nvpart_v1);
-    for (i = 0; i < nb_prom_envs; i++)
-        end = OpenBIOS_set_var(nvr->data, end, prom_envs[i]);
-
-    // End marker
-    nvr->data[end++] = '\0';
-
-    end = start + ((end - start + 15) & ~15);
-    /* XXX: OpenBIOS is not able to grow up a partition. Leave some space for
-       new variables. */
-    if (end < DEF_SYSTEM_SIZE)
-        end = DEF_SYSTEM_SIZE;
-    OpenBIOS_finish_partition(part_header, end - start);
-
-    // free partition
-    start = end;
-    part_header = (struct OpenBIOS_nvpart_v1 *)&nvr->data[start];
-    part_header->signature = OPENBIOS_PART_FREE;
-    pstrcpy(part_header->name, sizeof(part_header->name), "free");
-
-    end = len;
-    OpenBIOS_finish_partition(part_header, end - start);
-}
-
-type_init(macio_nvram_register_types)
diff --git a/hw/macio.c b/hw/macio.c
deleted file mode 100644 (file)
index 2f389dd..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- * PowerMac MacIO device emulation
- *
- * Copyright (c) 2005-2007 Fabrice Bellard
- * Copyright (c) 2007 Jocelyn Mayer
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/ppc/mac.h"
-#include "hw/pci/pci.h"
-#include "hw/ppc/mac_dbdma.h"
-#include "hw/char/escc.h"
-
-#define TYPE_MACIO "macio"
-#define MACIO(obj) OBJECT_CHECK(MacIOState, (obj), TYPE_MACIO)
-
-typedef struct MacIOState
-{
-    /*< private >*/
-    PCIDevice parent;
-    /*< public >*/
-
-    MemoryRegion bar;
-    CUDAState cuda;
-    void *dbdma;
-    MemoryRegion *pic_mem;
-    MemoryRegion *escc_mem;
-} MacIOState;
-
-#define OLDWORLD_MACIO(obj) \
-    OBJECT_CHECK(OldWorldMacIOState, (obj), TYPE_OLDWORLD_MACIO)
-
-typedef struct OldWorldMacIOState {
-    /*< private >*/
-    MacIOState parent_obj;
-    /*< public >*/
-
-    qemu_irq irqs[3];
-
-    MacIONVRAMState nvram;
-    MACIOIDEState ide;
-} OldWorldMacIOState;
-
-#define NEWWORLD_MACIO(obj) \
-    OBJECT_CHECK(NewWorldMacIOState, (obj), TYPE_NEWWORLD_MACIO)
-
-typedef struct NewWorldMacIOState {
-    /*< private >*/
-    MacIOState parent_obj;
-    /*< public >*/
-    qemu_irq irqs[5];
-    MACIOIDEState ide[2];
-} NewWorldMacIOState;
-
-static void macio_bar_setup(MacIOState *macio_state)
-{
-    MemoryRegion *bar = &macio_state->bar;
-
-    if (macio_state->escc_mem) {
-        memory_region_add_subregion(bar, 0x13000, macio_state->escc_mem);
-    }
-}
-
-static int macio_common_initfn(PCIDevice *d)
-{
-    MacIOState *s = MACIO(d);
-    SysBusDevice *sysbus_dev;
-    int ret;
-
-    d->config[0x3d] = 0x01; // interrupt on pin 1
-
-    ret = qdev_init(DEVICE(&s->cuda));
-    if (ret < 0) {
-        return ret;
-    }
-    sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
-    memory_region_add_subregion(&s->bar, 0x16000,
-                                sysbus_mmio_get_region(sysbus_dev, 0));
-
-    macio_bar_setup(s);
-    pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar);
-
-    return 0;
-}
-
-static int macio_oldworld_initfn(PCIDevice *d)
-{
-    MacIOState *s = MACIO(d);
-    OldWorldMacIOState *os = OLDWORLD_MACIO(d);
-    SysBusDevice *sysbus_dev;
-    int ret = macio_common_initfn(d);
-    if (ret < 0) {
-        return ret;
-    }
-
-    sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
-    sysbus_connect_irq(sysbus_dev, 0, os->irqs[0]);
-
-    ret = qdev_init(DEVICE(&os->nvram));
-    if (ret < 0) {
-        return ret;
-    }
-    sysbus_dev = SYS_BUS_DEVICE(&os->nvram);
-    memory_region_add_subregion(&s->bar, 0x60000,
-                                sysbus_mmio_get_region(sysbus_dev, 0));
-    pmac_format_nvram_partition(&os->nvram, os->nvram.size);
-
-    if (s->pic_mem) {
-        /* Heathrow PIC */
-        memory_region_add_subregion(&s->bar, 0x00000, s->pic_mem);
-    }
-
-    sysbus_dev = SYS_BUS_DEVICE(&os->ide);
-    sysbus_connect_irq(sysbus_dev, 0, os->irqs[1]);
-    sysbus_connect_irq(sysbus_dev, 1, os->irqs[2]);
-    macio_ide_register_dma(&os->ide, s->dbdma, 0x16);
-    ret = qdev_init(DEVICE(&os->ide));
-    if (ret < 0) {
-        return ret;
-    }
-
-    return 0;
-}
-
-static void macio_oldworld_init(Object *obj)
-{
-    MacIOState *s = MACIO(obj);
-    OldWorldMacIOState *os = OLDWORLD_MACIO(obj);
-    DeviceState *dev;
-
-    qdev_init_gpio_out(DEVICE(obj), os->irqs, ARRAY_SIZE(os->irqs));
-
-    object_initialize(&os->nvram, TYPE_MACIO_NVRAM);
-    dev = DEVICE(&os->nvram);
-    qdev_prop_set_uint32(dev, "size", 0x2000);
-    qdev_prop_set_uint32(dev, "it_shift", 4);
-
-    object_initialize(&os->ide, TYPE_MACIO_IDE);
-    qdev_set_parent_bus(DEVICE(&os->ide), sysbus_get_default());
-    memory_region_add_subregion(&s->bar, 0x1f000 + (1 * 0x1000), &os->ide.mem);
-    object_property_add_child(obj, "ide", OBJECT(&os->ide), NULL);
-}
-
-static int macio_newworld_initfn(PCIDevice *d)
-{
-    MacIOState *s = MACIO(d);
-    NewWorldMacIOState *ns = NEWWORLD_MACIO(d);
-    SysBusDevice *sysbus_dev;
-    int ret = macio_common_initfn(d);
-    if (ret < 0) {
-        return ret;
-    }
-
-    sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
-    sysbus_connect_irq(sysbus_dev, 0, ns->irqs[0]);
-
-    if (s->pic_mem) {
-        /* OpenPIC */
-        memory_region_add_subregion(&s->bar, 0x40000, s->pic_mem);
-    }
-
-    sysbus_dev = SYS_BUS_DEVICE(&ns->ide[0]);
-    sysbus_connect_irq(sysbus_dev, 0, ns->irqs[1]);
-    sysbus_connect_irq(sysbus_dev, 1, ns->irqs[2]);
-    macio_ide_register_dma(&ns->ide[0], s->dbdma, 0x16);
-    ret = qdev_init(DEVICE(&ns->ide[0]));
-    if (ret < 0) {
-        return ret;
-    }
-
-    sysbus_dev = SYS_BUS_DEVICE(&ns->ide[1]);
-    sysbus_connect_irq(sysbus_dev, 0, ns->irqs[3]);
-    sysbus_connect_irq(sysbus_dev, 1, ns->irqs[4]);
-    macio_ide_register_dma(&ns->ide[1], s->dbdma, 0x1a);
-    ret = qdev_init(DEVICE(&ns->ide[1]));
-    if (ret < 0) {
-        return ret;
-    }
-
-    return 0;
-}
-
-static void macio_newworld_init(Object *obj)
-{
-    MacIOState *s = MACIO(obj);
-    NewWorldMacIOState *ns = NEWWORLD_MACIO(obj);
-    int i;
-    gchar *name;
-
-    qdev_init_gpio_out(DEVICE(obj), ns->irqs, ARRAY_SIZE(ns->irqs));
-
-    for (i = 0; i < 2; i++) {
-        object_initialize(&ns->ide[i], TYPE_MACIO_IDE);
-        qdev_set_parent_bus(DEVICE(&ns->ide[i]), sysbus_get_default());
-        memory_region_add_subregion(&s->bar, 0x1f000 + ((i + 1) * 0x1000),
-                                    &ns->ide[i].mem);
-        name = g_strdup_printf("ide[%i]", i);
-        object_property_add_child(obj, name, OBJECT(&ns->ide[i]), NULL);
-        g_free(name);
-    }
-}
-
-static void macio_instance_init(Object *obj)
-{
-    MacIOState *s = MACIO(obj);
-    MemoryRegion *dbdma_mem;
-
-    memory_region_init(&s->bar, "macio", 0x80000);
-
-    object_initialize(&s->cuda, TYPE_CUDA);
-    qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default());
-    object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL);
-
-    s->dbdma = DBDMA_init(&dbdma_mem);
-    memory_region_add_subregion(&s->bar, 0x08000, dbdma_mem);
-}
-
-static void macio_oldworld_class_init(ObjectClass *oc, void *data)
-{
-    PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc);
-
-    pdc->init = macio_oldworld_initfn;
-    pdc->device_id = PCI_DEVICE_ID_APPLE_343S1201;
-}
-
-static void macio_newworld_class_init(ObjectClass *oc, void *data)
-{
-    PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc);
-
-    pdc->init = macio_newworld_initfn;
-    pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL;
-}
-
-static void macio_class_init(ObjectClass *klass, void *data)
-{
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->vendor_id = PCI_VENDOR_ID_APPLE;
-    k->class_id = PCI_CLASS_OTHERS << 8;
-}
-
-static const TypeInfo macio_oldworld_type_info = {
-    .name          = TYPE_OLDWORLD_MACIO,
-    .parent        = TYPE_MACIO,
-    .instance_size = sizeof(OldWorldMacIOState),
-    .instance_init = macio_oldworld_init,
-    .class_init    = macio_oldworld_class_init,
-};
-
-static const TypeInfo macio_newworld_type_info = {
-    .name          = TYPE_NEWWORLD_MACIO,
-    .parent        = TYPE_MACIO,
-    .instance_size = sizeof(NewWorldMacIOState),
-    .instance_init = macio_newworld_init,
-    .class_init    = macio_newworld_class_init,
-};
-
-static const TypeInfo macio_type_info = {
-    .name          = TYPE_MACIO,
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(MacIOState),
-    .instance_init = macio_instance_init,
-    .abstract      = true,
-    .class_init    = macio_class_init,
-};
-
-static void macio_register_types(void)
-{
-    type_register_static(&macio_type_info);
-    type_register_static(&macio_oldworld_type_info);
-    type_register_static(&macio_newworld_type_info);
-}
-
-type_init(macio_register_types)
-
-void macio_init(PCIDevice *d,
-                MemoryRegion *pic_mem,
-                MemoryRegion *escc_mem)
-{
-    MacIOState *macio_state = MACIO(d);
-
-    macio_state->pic_mem = pic_mem;
-    macio_state->escc_mem = escc_mem;
-    /* Note: this code is strongly inspirated from the corresponding code
-       in PearPC */
-
-    qdev_init_nofail(DEVICE(d));
-}
diff --git a/hw/max111x.c b/hw/max111x.c
deleted file mode 100644 (file)
index d477ecd..0000000
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Maxim MAX1110/1111 ADC chip emulation.
- *
- * Copyright (c) 2006 Openedhand Ltd.
- * Written by Andrzej Zaborowski <balrog@zabor.org>
- *
- * This code is licensed under the GNU GPLv2.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "hw/ssi.h"
-
-typedef struct {
-    SSISlave ssidev;
-    qemu_irq interrupt;
-    uint8_t tb1, rb2, rb3;
-    int cycle;
-
-    uint8_t input[8];
-    int inputs, com;
-} MAX111xState;
-
-/* Control-byte bitfields */
-#define CB_PD0         (1 << 0)
-#define CB_PD1         (1 << 1)
-#define CB_SGL         (1 << 2)
-#define CB_UNI         (1 << 3)
-#define CB_SEL0                (1 << 4)
-#define CB_SEL1                (1 << 5)
-#define CB_SEL2                (1 << 6)
-#define CB_START       (1 << 7)
-
-#define CHANNEL_NUM(v, b0, b1, b2)     \
-                       ((((v) >> (2 + (b0))) & 4) |    \
-                        (((v) >> (3 + (b1))) & 2) |    \
-                        (((v) >> (4 + (b2))) & 1))
-
-static uint32_t max111x_read(MAX111xState *s)
-{
-    if (!s->tb1)
-        return 0;
-
-    switch (s->cycle ++) {
-    case 1:
-        return s->rb2;
-    case 2:
-        return s->rb3;
-    }
-
-    return 0;
-}
-
-/* Interpret a control-byte */
-static void max111x_write(MAX111xState *s, uint32_t value)
-{
-    int measure, chan;
-
-    /* Ignore the value if START bit is zero */
-    if (!(value & CB_START))
-        return;
-
-    s->cycle = 0;
-
-    if (!(value & CB_PD1)) {
-        s->tb1 = 0;
-        return;
-    }
-
-    s->tb1 = value;
-
-    if (s->inputs == 8)
-        chan = CHANNEL_NUM(value, 1, 0, 2);
-    else
-        chan = CHANNEL_NUM(value & ~CB_SEL0, 0, 1, 2);
-
-    if (value & CB_SGL)
-        measure = s->input[chan] - s->com;
-    else
-        measure = s->input[chan] - s->input[chan ^ 1];
-
-    if (!(value & CB_UNI))
-        measure ^= 0x80;
-
-    s->rb2 = (measure >> 2) & 0x3f;
-    s->rb3 = (measure << 6) & 0xc0;
-
-    /* FIXME: When should the IRQ be lowered?  */
-    qemu_irq_raise(s->interrupt);
-}
-
-static uint32_t max111x_transfer(SSISlave *dev, uint32_t value)
-{
-    MAX111xState *s = FROM_SSI_SLAVE(MAX111xState, dev);
-    max111x_write(s, value);
-    return max111x_read(s);
-}
-
-static const VMStateDescription vmstate_max111x = {
-    .name = "max111x",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_SSI_SLAVE(ssidev, MAX111xState),
-        VMSTATE_UINT8(tb1, MAX111xState),
-        VMSTATE_UINT8(rb2, MAX111xState),
-        VMSTATE_UINT8(rb3, MAX111xState),
-        VMSTATE_INT32_EQUAL(inputs, MAX111xState),
-        VMSTATE_INT32(com, MAX111xState),
-        VMSTATE_ARRAY_INT32_UNSAFE(input, MAX111xState, inputs,
-                                   vmstate_info_uint8, uint8_t),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int max111x_init(SSISlave *dev, int inputs)
-{
-    MAX111xState *s = FROM_SSI_SLAVE(MAX111xState, dev);
-
-    qdev_init_gpio_out(&dev->qdev, &s->interrupt, 1);
-
-    s->inputs = inputs;
-    /* TODO: add a user interface for setting these */
-    s->input[0] = 0xf0;
-    s->input[1] = 0xe0;
-    s->input[2] = 0xd0;
-    s->input[3] = 0xc0;
-    s->input[4] = 0xb0;
-    s->input[5] = 0xa0;
-    s->input[6] = 0x90;
-    s->input[7] = 0x80;
-    s->com = 0;
-
-    vmstate_register(&dev->qdev, -1, &vmstate_max111x, s);
-    return 0;
-}
-
-static int max1110_init(SSISlave *dev)
-{
-    return max111x_init(dev, 8);
-}
-
-static int max1111_init(SSISlave *dev)
-{
-    return max111x_init(dev, 4);
-}
-
-void max111x_set_input(DeviceState *dev, int line, uint8_t value)
-{
-    MAX111xState *s = FROM_SSI_SLAVE(MAX111xState, SSI_SLAVE_FROM_QDEV(dev));
-    assert(line >= 0 && line < s->inputs);
-    s->input[line] = value;
-}
-
-static void max1110_class_init(ObjectClass *klass, void *data)
-{
-    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
-
-    k->init = max1110_init;
-    k->transfer = max111x_transfer;
-}
-
-static const TypeInfo max1110_info = {
-    .name          = "max1110",
-    .parent        = TYPE_SSI_SLAVE,
-    .instance_size = sizeof(MAX111xState),
-    .class_init    = max1110_class_init,
-};
-
-static void max1111_class_init(ObjectClass *klass, void *data)
-{
-    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
-
-    k->init = max1111_init;
-    k->transfer = max111x_transfer;
-}
-
-static const TypeInfo max1111_info = {
-    .name          = "max1111",
-    .parent        = TYPE_SSI_SLAVE,
-    .instance_size = sizeof(MAX111xState),
-    .class_init    = max1111_class_init,
-};
-
-static void max111x_register_types(void)
-{
-    type_register_static(&max1110_info);
-    type_register_static(&max1111_info);
-}
-
-type_init(max111x_register_types)
diff --git a/hw/max7310.c b/hw/max7310.c
deleted file mode 100644 (file)
index 59b2877..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * MAX7310 8-port GPIO expansion chip.
- *
- * Copyright (c) 2006 Openedhand Ltd.
- * Written by Andrzej Zaborowski <balrog@zabor.org>
- *
- * This file is licensed under GNU GPL.
- */
-
-#include "hw/i2c/i2c.h"
-
-typedef struct {
-    I2CSlave i2c;
-    int i2c_command_byte;
-    int len;
-
-    uint8_t level;
-    uint8_t direction;
-    uint8_t polarity;
-    uint8_t status;
-    uint8_t command;
-    qemu_irq handler[8];
-    qemu_irq *gpio_in;
-} MAX7310State;
-
-static void max7310_reset(DeviceState *dev)
-{
-    MAX7310State *s = FROM_I2C_SLAVE(MAX7310State, I2C_SLAVE(dev));
-    s->level &= s->direction;
-    s->direction = 0xff;
-    s->polarity = 0xf0;
-    s->status = 0x01;
-    s->command = 0x00;
-}
-
-static int max7310_rx(I2CSlave *i2c)
-{
-    MAX7310State *s = (MAX7310State *) i2c;
-
-    switch (s->command) {
-    case 0x00: /* Input port */
-        return s->level ^ s->polarity;
-        break;
-
-    case 0x01: /* Output port */
-        return s->level & ~s->direction;
-        break;
-
-    case 0x02: /* Polarity inversion */
-        return s->polarity;
-
-    case 0x03: /* Configuration */
-        return s->direction;
-
-    case 0x04: /* Timeout */
-        return s->status;
-        break;
-
-    case 0xff: /* Reserved */
-        return 0xff;
-
-    default:
-#ifdef VERBOSE
-        printf("%s: unknown register %02x\n", __FUNCTION__, s->command);
-#endif
-        break;
-    }
-    return 0xff;
-}
-
-static int max7310_tx(I2CSlave *i2c, uint8_t data)
-{
-    MAX7310State *s = (MAX7310State *) i2c;
-    uint8_t diff;
-    int line;
-
-    if (s->len ++ > 1) {
-#ifdef VERBOSE
-        printf("%s: message too long (%i bytes)\n", __FUNCTION__, s->len);
-#endif
-        return 1;
-    }
-
-    if (s->i2c_command_byte) {
-        s->command = data;
-        s->i2c_command_byte = 0;
-        return 0;
-    }
-
-    switch (s->command) {
-    case 0x01: /* Output port */
-        for (diff = (data ^ s->level) & ~s->direction; diff;
-                        diff &= ~(1 << line)) {
-            line = ffs(diff) - 1;
-            if (s->handler[line])
-                qemu_set_irq(s->handler[line], (data >> line) & 1);
-        }
-        s->level = (s->level & s->direction) | (data & ~s->direction);
-        break;
-
-    case 0x02: /* Polarity inversion */
-        s->polarity = data;
-        break;
-
-    case 0x03: /* Configuration */
-        s->level &= ~(s->direction ^ data);
-        s->direction = data;
-        break;
-
-    case 0x04: /* Timeout */
-        s->status = data;
-        break;
-
-    case 0x00: /* Input port - ignore writes */
-       break;
-    default:
-#ifdef VERBOSE
-        printf("%s: unknown register %02x\n", __FUNCTION__, s->command);
-#endif
-        return 1;
-    }
-
-    return 0;
-}
-
-static void max7310_event(I2CSlave *i2c, enum i2c_event event)
-{
-    MAX7310State *s = (MAX7310State *) i2c;
-    s->len = 0;
-
-    switch (event) {
-    case I2C_START_SEND:
-        s->i2c_command_byte = 1;
-        break;
-    case I2C_FINISH:
-#ifdef VERBOSE
-        if (s->len == 1)
-            printf("%s: message too short (%i bytes)\n", __FUNCTION__, s->len);
-#endif
-        break;
-    default:
-        break;
-    }
-}
-
-static const VMStateDescription vmstate_max7310 = {
-    .name = "max7310",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .fields      = (VMStateField []) {
-        VMSTATE_INT32(i2c_command_byte, MAX7310State),
-        VMSTATE_INT32(len, MAX7310State),
-        VMSTATE_UINT8(level, MAX7310State),
-        VMSTATE_UINT8(direction, MAX7310State),
-        VMSTATE_UINT8(polarity, MAX7310State),
-        VMSTATE_UINT8(status, MAX7310State),
-        VMSTATE_UINT8(command, MAX7310State),
-        VMSTATE_I2C_SLAVE(i2c, MAX7310State),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void max7310_gpio_set(void *opaque, int line, int level)
-{
-    MAX7310State *s = (MAX7310State *) opaque;
-    if (line >= ARRAY_SIZE(s->handler) || line  < 0)
-        hw_error("bad GPIO line");
-
-    if (level)
-        s->level |= s->direction & (1 << line);
-    else
-        s->level &= ~(s->direction & (1 << line));
-}
-
-/* MAX7310 is SMBus-compatible (can be used with only SMBus protocols),
- * but also accepts sequences that are not SMBus so return an I2C device.  */
-static int max7310_init(I2CSlave *i2c)
-{
-    MAX7310State *s = FROM_I2C_SLAVE(MAX7310State, i2c);
-
-    qdev_init_gpio_in(&i2c->qdev, max7310_gpio_set, 8);
-    qdev_init_gpio_out(&i2c->qdev, s->handler, 8);
-
-    return 0;
-}
-
-static void max7310_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
-
-    k->init = max7310_init;
-    k->event = max7310_event;
-    k->recv = max7310_rx;
-    k->send = max7310_tx;
-    dc->reset = max7310_reset;
-    dc->vmsd = &vmstate_max7310;
-}
-
-static const TypeInfo max7310_info = {
-    .name          = "max7310",
-    .parent        = TYPE_I2C_SLAVE,
-    .instance_size = sizeof(MAX7310State),
-    .class_init    = max7310_class_init,
-};
-
-static void max7310_register_types(void)
-{
-    type_register_static(&max7310_info);
-}
-
-type_init(max7310_register_types)
diff --git a/hw/megasas.c b/hw/megasas.c
deleted file mode 100644 (file)
index f46f800..0000000
+++ /dev/null
@@ -1,2213 +0,0 @@
-/*
- * QEMU MegaRAID SAS 8708EM2 Host Bus Adapter emulation
- * Based on the linux driver code at drivers/scsi/megaraid
- *
- * Copyright (c) 2009-2012 Hannes Reinecke, SUSE Labs
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/hw.h"
-#include "hw/pci/pci.h"
-#include "sysemu/dma.h"
-#include "hw/pci/msix.h"
-#include "qemu/iov.h"
-#include "hw/scsi/scsi.h"
-#include "block/scsi.h"
-#include "trace.h"
-
-#include "hw/mfi.h"
-
-#define MEGASAS_VERSION "1.70"
-#define MEGASAS_MAX_FRAMES 2048         /* Firmware limit at 65535 */
-#define MEGASAS_DEFAULT_FRAMES 1000     /* Windows requires this */
-#define MEGASAS_MAX_SGE 128             /* Firmware limit */
-#define MEGASAS_DEFAULT_SGE 80
-#define MEGASAS_MAX_SECTORS 0xFFFF      /* No real limit */
-#define MEGASAS_MAX_ARRAYS 128
-
-#define MEGASAS_HBA_SERIAL "QEMU123456"
-#define NAA_LOCALLY_ASSIGNED_ID 0x3ULL
-#define IEEE_COMPANY_LOCALLY_ASSIGNED 0x525400
-
-#define MEGASAS_FLAG_USE_JBOD      0
-#define MEGASAS_MASK_USE_JBOD      (1 << MEGASAS_FLAG_USE_JBOD)
-#define MEGASAS_FLAG_USE_MSIX      1
-#define MEGASAS_MASK_USE_MSIX      (1 << MEGASAS_FLAG_USE_MSIX)
-#define MEGASAS_FLAG_USE_QUEUE64   2
-#define MEGASAS_MASK_USE_QUEUE64   (1 << MEGASAS_FLAG_USE_QUEUE64)
-
-static const char *mfi_frame_desc[] = {
-    "MFI init", "LD Read", "LD Write", "LD SCSI", "PD SCSI",
-    "MFI Doorbell", "MFI Abort", "MFI SMP", "MFI Stop"};
-
-typedef struct MegasasCmd {
-    uint32_t index;
-    uint16_t flags;
-    uint16_t count;
-    uint64_t context;
-
-    hwaddr pa;
-    hwaddr pa_size;
-    union mfi_frame *frame;
-    SCSIRequest *req;
-    QEMUSGList qsg;
-    void *iov_buf;
-    size_t iov_size;
-    size_t iov_offset;
-    struct MegasasState *state;
-} MegasasCmd;
-
-typedef struct MegasasState {
-    PCIDevice dev;
-    MemoryRegion mmio_io;
-    MemoryRegion port_io;
-    MemoryRegion queue_io;
-    uint32_t frame_hi;
-
-    int fw_state;
-    uint32_t fw_sge;
-    uint32_t fw_cmds;
-    uint32_t flags;
-    int fw_luns;
-    int intr_mask;
-    int doorbell;
-    int busy;
-
-    MegasasCmd *event_cmd;
-    int event_locale;
-    int event_class;
-    int event_count;
-    int shutdown_event;
-    int boot_event;
-
-    uint64_t sas_addr;
-    char *hba_serial;
-
-    uint64_t reply_queue_pa;
-    void *reply_queue;
-    int reply_queue_len;
-    int reply_queue_head;
-    int reply_queue_tail;
-    uint64_t consumer_pa;
-    uint64_t producer_pa;
-
-    MegasasCmd frames[MEGASAS_MAX_FRAMES];
-
-    SCSIBus bus;
-} MegasasState;
-
-#define MEGASAS_INTR_DISABLED_MASK 0xFFFFFFFF
-
-static bool megasas_intr_enabled(MegasasState *s)
-{
-    if ((s->intr_mask & MEGASAS_INTR_DISABLED_MASK) !=
-        MEGASAS_INTR_DISABLED_MASK) {
-        return true;
-    }
-    return false;
-}
-
-static bool megasas_use_queue64(MegasasState *s)
-{
-    return s->flags & MEGASAS_MASK_USE_QUEUE64;
-}
-
-static bool megasas_use_msix(MegasasState *s)
-{
-    return s->flags & MEGASAS_MASK_USE_MSIX;
-}
-
-static bool megasas_is_jbod(MegasasState *s)
-{
-    return s->flags & MEGASAS_MASK_USE_JBOD;
-}
-
-static void megasas_frame_set_cmd_status(unsigned long frame, uint8_t v)
-{
-    stb_phys(frame + offsetof(struct mfi_frame_header, cmd_status), v);
-}
-
-static void megasas_frame_set_scsi_status(unsigned long frame, uint8_t v)
-{
-    stb_phys(frame + offsetof(struct mfi_frame_header, scsi_status), v);
-}
-
-/*
- * Context is considered opaque, but the HBA firmware is running
- * in little endian mode. So convert it to little endian, too.
- */
-static uint64_t megasas_frame_get_context(unsigned long frame)
-{
-    return ldq_le_phys(frame + offsetof(struct mfi_frame_header, context));
-}
-
-static bool megasas_frame_is_ieee_sgl(MegasasCmd *cmd)
-{
-    return cmd->flags & MFI_FRAME_IEEE_SGL;
-}
-
-static bool megasas_frame_is_sgl64(MegasasCmd *cmd)
-{
-    return cmd->flags & MFI_FRAME_SGL64;
-}
-
-static bool megasas_frame_is_sense64(MegasasCmd *cmd)
-{
-    return cmd->flags & MFI_FRAME_SENSE64;
-}
-
-static uint64_t megasas_sgl_get_addr(MegasasCmd *cmd,
-                                     union mfi_sgl *sgl)
-{
-    uint64_t addr;
-
-    if (megasas_frame_is_ieee_sgl(cmd)) {
-        addr = le64_to_cpu(sgl->sg_skinny->addr);
-    } else if (megasas_frame_is_sgl64(cmd)) {
-        addr = le64_to_cpu(sgl->sg64->addr);
-    } else {
-        addr = le32_to_cpu(sgl->sg32->addr);
-    }
-    return addr;
-}
-
-static uint32_t megasas_sgl_get_len(MegasasCmd *cmd,
-                                    union mfi_sgl *sgl)
-{
-    uint32_t len;
-
-    if (megasas_frame_is_ieee_sgl(cmd)) {
-        len = le32_to_cpu(sgl->sg_skinny->len);
-    } else if (megasas_frame_is_sgl64(cmd)) {
-        len = le32_to_cpu(sgl->sg64->len);
-    } else {
-        len = le32_to_cpu(sgl->sg32->len);
-    }
-    return len;
-}
-
-static union mfi_sgl *megasas_sgl_next(MegasasCmd *cmd,
-                                       union mfi_sgl *sgl)
-{
-    uint8_t *next = (uint8_t *)sgl;
-
-    if (megasas_frame_is_ieee_sgl(cmd)) {
-        next += sizeof(struct mfi_sg_skinny);
-    } else if (megasas_frame_is_sgl64(cmd)) {
-        next += sizeof(struct mfi_sg64);
-    } else {
-        next += sizeof(struct mfi_sg32);
-    }
-
-    if (next >= (uint8_t *)cmd->frame + cmd->pa_size) {
-        return NULL;
-    }
-    return (union mfi_sgl *)next;
-}
-
-static void megasas_soft_reset(MegasasState *s);
-
-static int megasas_map_sgl(MegasasState *s, MegasasCmd *cmd, union mfi_sgl *sgl)
-{
-    int i;
-    int iov_count = 0;
-    size_t iov_size = 0;
-
-    cmd->flags = le16_to_cpu(cmd->frame->header.flags);
-    iov_count = cmd->frame->header.sge_count;
-    if (iov_count > MEGASAS_MAX_SGE) {
-        trace_megasas_iovec_sgl_overflow(cmd->index, iov_count,
-                                         MEGASAS_MAX_SGE);
-        return iov_count;
-    }
-    qemu_sglist_init(&cmd->qsg, iov_count, pci_dma_context(&s->dev));
-    for (i = 0; i < iov_count; i++) {
-        dma_addr_t iov_pa, iov_size_p;
-
-        if (!sgl) {
-            trace_megasas_iovec_sgl_underflow(cmd->index, i);
-            goto unmap;
-        }
-        iov_pa = megasas_sgl_get_addr(cmd, sgl);
-        iov_size_p = megasas_sgl_get_len(cmd, sgl);
-        if (!iov_pa || !iov_size_p) {
-            trace_megasas_iovec_sgl_invalid(cmd->index, i,
-                                            iov_pa, iov_size_p);
-            goto unmap;
-        }
-        qemu_sglist_add(&cmd->qsg, iov_pa, iov_size_p);
-        sgl = megasas_sgl_next(cmd, sgl);
-        iov_size += (size_t)iov_size_p;
-    }
-    if (cmd->iov_size > iov_size) {
-        trace_megasas_iovec_overflow(cmd->index, iov_size, cmd->iov_size);
-    } else if (cmd->iov_size < iov_size) {
-        trace_megasas_iovec_underflow(cmd->iov_size, iov_size, cmd->iov_size);
-    }
-    cmd->iov_offset = 0;
-    return 0;
-unmap:
-    qemu_sglist_destroy(&cmd->qsg);
-    return iov_count - i;
-}
-
-static void megasas_unmap_sgl(MegasasCmd *cmd)
-{
-    qemu_sglist_destroy(&cmd->qsg);
-    cmd->iov_offset = 0;
-}
-
-/*
- * passthrough sense and io sense are at the same offset
- */
-static int megasas_build_sense(MegasasCmd *cmd, uint8_t *sense_ptr,
-    uint8_t sense_len)
-{
-    uint32_t pa_hi = 0, pa_lo;
-    hwaddr pa;
-
-    if (sense_len > cmd->frame->header.sense_len) {
-        sense_len = cmd->frame->header.sense_len;
-    }
-    if (sense_len) {
-        pa_lo = le32_to_cpu(cmd->frame->pass.sense_addr_lo);
-        if (megasas_frame_is_sense64(cmd)) {
-            pa_hi = le32_to_cpu(cmd->frame->pass.sense_addr_hi);
-        }
-        pa = ((uint64_t) pa_hi << 32) | pa_lo;
-        cpu_physical_memory_write(pa, sense_ptr, sense_len);
-        cmd->frame->header.sense_len = sense_len;
-    }
-    return sense_len;
-}
-
-static void megasas_write_sense(MegasasCmd *cmd, SCSISense sense)
-{
-    uint8_t sense_buf[SCSI_SENSE_BUF_SIZE];
-    uint8_t sense_len = 18;
-
-    memset(sense_buf, 0, sense_len);
-    sense_buf[0] = 0xf0;
-    sense_buf[2] = sense.key;
-    sense_buf[7] = 10;
-    sense_buf[12] = sense.asc;
-    sense_buf[13] = sense.ascq;
-    megasas_build_sense(cmd, sense_buf, sense_len);
-}
-
-static void megasas_copy_sense(MegasasCmd *cmd)
-{
-    uint8_t sense_buf[SCSI_SENSE_BUF_SIZE];
-    uint8_t sense_len;
-
-    sense_len = scsi_req_get_sense(cmd->req, sense_buf,
-                                   SCSI_SENSE_BUF_SIZE);
-    megasas_build_sense(cmd, sense_buf, sense_len);
-}
-
-/*
- * Format an INQUIRY CDB
- */
-static int megasas_setup_inquiry(uint8_t *cdb, int pg, int len)
-{
-    memset(cdb, 0, 6);
-    cdb[0] = INQUIRY;
-    if (pg > 0) {
-        cdb[1] = 0x1;
-        cdb[2] = pg;
-    }
-    cdb[3] = (len >> 8) & 0xff;
-    cdb[4] = (len & 0xff);
-    return len;
-}
-
-/*
- * Encode lba and len into a READ_16/WRITE_16 CDB
- */
-static void megasas_encode_lba(uint8_t *cdb, uint64_t lba,
-                               uint32_t len, bool is_write)
-{
-    memset(cdb, 0x0, 16);
-    if (is_write) {
-        cdb[0] = WRITE_16;
-    } else {
-        cdb[0] = READ_16;
-    }
-    cdb[2] = (lba >> 56) & 0xff;
-    cdb[3] = (lba >> 48) & 0xff;
-    cdb[4] = (lba >> 40) & 0xff;
-    cdb[5] = (lba >> 32) & 0xff;
-    cdb[6] = (lba >> 24) & 0xff;
-    cdb[7] = (lba >> 16) & 0xff;
-    cdb[8] = (lba >> 8) & 0xff;
-    cdb[9] = (lba) & 0xff;
-    cdb[10] = (len >> 24) & 0xff;
-    cdb[11] = (len >> 16) & 0xff;
-    cdb[12] = (len >> 8) & 0xff;
-    cdb[13] = (len) & 0xff;
-}
-
-/*
- * Utility functions
- */
-static uint64_t megasas_fw_time(void)
-{
-    struct tm curtime;
-    uint64_t bcd_time;
-
-    qemu_get_timedate(&curtime, 0);
-    bcd_time = ((uint64_t)curtime.tm_sec & 0xff) << 48 |
-        ((uint64_t)curtime.tm_min & 0xff)  << 40 |
-        ((uint64_t)curtime.tm_hour & 0xff) << 32 |
-        ((uint64_t)curtime.tm_mday & 0xff) << 24 |
-        ((uint64_t)curtime.tm_mon & 0xff)  << 16 |
-        ((uint64_t)(curtime.tm_year + 1900) & 0xffff);
-
-    return bcd_time;
-}
-
-/*
- * Default disk sata address
- * 0x1221 is the magic number as
- * present in real hardware,
- * so use it here, too.
- */
-static uint64_t megasas_get_sata_addr(uint16_t id)
-{
-    uint64_t addr = (0x1221ULL << 48);
-    return addr & (id << 24);
-}
-
-/*
- * Frame handling
- */
-static int megasas_next_index(MegasasState *s, int index, int limit)
-{
-    index++;
-    if (index == limit) {
-        index = 0;
-    }
-    return index;
-}
-
-static MegasasCmd *megasas_lookup_frame(MegasasState *s,
-    hwaddr frame)
-{
-    MegasasCmd *cmd = NULL;
-    int num = 0, index;
-
-    index = s->reply_queue_head;
-
-    while (num < s->fw_cmds) {
-        if (s->frames[index].pa && s->frames[index].pa == frame) {
-            cmd = &s->frames[index];
-            break;
-        }
-        index = megasas_next_index(s, index, s->fw_cmds);
-        num++;
-    }
-
-    return cmd;
-}
-
-static MegasasCmd *megasas_next_frame(MegasasState *s,
-    hwaddr frame)
-{
-    MegasasCmd *cmd = NULL;
-    int num = 0, index;
-
-    cmd = megasas_lookup_frame(s, frame);
-    if (cmd) {
-        trace_megasas_qf_found(cmd->index, cmd->pa);
-        return cmd;
-    }
-    index = s->reply_queue_head;
-    num = 0;
-    while (num < s->fw_cmds) {
-        if (!s->frames[index].pa) {
-            cmd = &s->frames[index];
-            break;
-        }
-        index = megasas_next_index(s, index, s->fw_cmds);
-        num++;
-    }
-    if (!cmd) {
-        trace_megasas_qf_failed(frame);
-    }
-    trace_megasas_qf_new(index, cmd);
-    return cmd;
-}
-
-static MegasasCmd *megasas_enqueue_frame(MegasasState *s,
-    hwaddr frame, uint64_t context, int count)
-{
-    MegasasCmd *cmd = NULL;
-    int frame_size = MFI_FRAME_SIZE * 16;
-    hwaddr frame_size_p = frame_size;
-
-    cmd = megasas_next_frame(s, frame);
-    /* All frames busy */
-    if (!cmd) {
-        return NULL;
-    }
-    if (!cmd->pa) {
-        cmd->pa = frame;
-        /* Map all possible frames */
-        cmd->frame = cpu_physical_memory_map(frame, &frame_size_p, 0);
-        if (frame_size_p != frame_size) {
-            trace_megasas_qf_map_failed(cmd->index, (unsigned long)frame);
-            if (cmd->frame) {
-                cpu_physical_memory_unmap(cmd->frame, frame_size_p, 0, 0);
-                cmd->frame = NULL;
-                cmd->pa = 0;
-            }
-            s->event_count++;
-            return NULL;
-        }
-        cmd->pa_size = frame_size_p;
-        cmd->context = context;
-        if (!megasas_use_queue64(s)) {
-            cmd->context &= (uint64_t)0xFFFFFFFF;
-        }
-    }
-    cmd->count = count;
-    s->busy++;
-
-    trace_megasas_qf_enqueue(cmd->index, cmd->count, cmd->context,
-                             s->reply_queue_head, s->busy);
-
-    return cmd;
-}
-
-static void megasas_complete_frame(MegasasState *s, uint64_t context)
-{
-    int tail, queue_offset;
-
-    /* Decrement busy count */
-    s->busy--;
-
-    if (s->reply_queue_pa) {
-        /*
-         * Put command on the reply queue.
-         * Context is opaque, but emulation is running in
-         * little endian. So convert it.
-         */
-        tail = s->reply_queue_head;
-        if (megasas_use_queue64(s)) {
-            queue_offset = tail * sizeof(uint64_t);
-            stq_le_phys(s->reply_queue_pa + queue_offset, context);
-        } else {
-            queue_offset = tail * sizeof(uint32_t);
-            stl_le_phys(s->reply_queue_pa + queue_offset, context);
-        }
-        s->reply_queue_head = megasas_next_index(s, tail, s->fw_cmds);
-        trace_megasas_qf_complete(context, tail, queue_offset,
-                                  s->busy, s->doorbell);
-    }
-
-    if (megasas_intr_enabled(s)) {
-        /* Notify HBA */
-        s->doorbell++;
-        if (s->doorbell == 1) {
-            if (msix_enabled(&s->dev)) {
-                trace_megasas_msix_raise(0);
-                msix_notify(&s->dev, 0);
-            } else {
-                trace_megasas_irq_raise();
-                qemu_irq_raise(s->dev.irq[0]);
-            }
-        }
-    } else {
-        trace_megasas_qf_complete_noirq(context);
-    }
-}
-
-static void megasas_reset_frames(MegasasState *s)
-{
-    int i;
-    MegasasCmd *cmd;
-
-    for (i = 0; i < s->fw_cmds; i++) {
-        cmd = &s->frames[i];
-        if (cmd->pa) {
-            cpu_physical_memory_unmap(cmd->frame, cmd->pa_size, 0, 0);
-            cmd->frame = NULL;
-            cmd->pa = 0;
-        }
-    }
-}
-
-static void megasas_abort_command(MegasasCmd *cmd)
-{
-    if (cmd->req) {
-        scsi_req_cancel(cmd->req);
-        cmd->req = NULL;
-    }
-}
-
-static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd)
-{
-    uint32_t pa_hi, pa_lo;
-    hwaddr iq_pa, initq_size;
-    struct mfi_init_qinfo *initq;
-    uint32_t flags;
-    int ret = MFI_STAT_OK;
-
-    pa_lo = le32_to_cpu(cmd->frame->init.qinfo_new_addr_lo);
-    pa_hi = le32_to_cpu(cmd->frame->init.qinfo_new_addr_hi);
-    iq_pa = (((uint64_t) pa_hi << 32) | pa_lo);
-    trace_megasas_init_firmware((uint64_t)iq_pa);
-    initq_size = sizeof(*initq);
-    initq = cpu_physical_memory_map(iq_pa, &initq_size, 0);
-    if (!initq || initq_size != sizeof(*initq)) {
-        trace_megasas_initq_map_failed(cmd->index);
-        s->event_count++;
-        ret = MFI_STAT_MEMORY_NOT_AVAILABLE;
-        goto out;
-    }
-    s->reply_queue_len = le32_to_cpu(initq->rq_entries) & 0xFFFF;
-    if (s->reply_queue_len > s->fw_cmds) {
-        trace_megasas_initq_mismatch(s->reply_queue_len, s->fw_cmds);
-        s->event_count++;
-        ret = MFI_STAT_INVALID_PARAMETER;
-        goto out;
-    }
-    pa_lo = le32_to_cpu(initq->rq_addr_lo);
-    pa_hi = le32_to_cpu(initq->rq_addr_hi);
-    s->reply_queue_pa = ((uint64_t) pa_hi << 32) | pa_lo;
-    pa_lo = le32_to_cpu(initq->ci_addr_lo);
-    pa_hi = le32_to_cpu(initq->ci_addr_hi);
-    s->consumer_pa = ((uint64_t) pa_hi << 32) | pa_lo;
-    pa_lo = le32_to_cpu(initq->pi_addr_lo);
-    pa_hi = le32_to_cpu(initq->pi_addr_hi);
-    s->producer_pa = ((uint64_t) pa_hi << 32) | pa_lo;
-    s->reply_queue_head = ldl_le_phys(s->producer_pa);
-    s->reply_queue_tail = ldl_le_phys(s->consumer_pa);
-    flags = le32_to_cpu(initq->flags);
-    if (flags & MFI_QUEUE_FLAG_CONTEXT64) {
-        s->flags |= MEGASAS_MASK_USE_QUEUE64;
-    }
-    trace_megasas_init_queue((unsigned long)s->reply_queue_pa,
-                             s->reply_queue_len, s->reply_queue_head,
-                             s->reply_queue_tail, flags);
-    megasas_reset_frames(s);
-    s->fw_state = MFI_FWSTATE_OPERATIONAL;
-out:
-    if (initq) {
-        cpu_physical_memory_unmap(initq, initq_size, 0, 0);
-    }
-    return ret;
-}
-
-static int megasas_map_dcmd(MegasasState *s, MegasasCmd *cmd)
-{
-    dma_addr_t iov_pa, iov_size;
-
-    cmd->flags = le16_to_cpu(cmd->frame->header.flags);
-    if (!cmd->frame->header.sge_count) {
-        trace_megasas_dcmd_zero_sge(cmd->index);
-        cmd->iov_size = 0;
-        return 0;
-    } else if (cmd->frame->header.sge_count > 1) {
-        trace_megasas_dcmd_invalid_sge(cmd->index,
-                                       cmd->frame->header.sge_count);
-        cmd->iov_size = 0;
-        return -1;
-    }
-    iov_pa = megasas_sgl_get_addr(cmd, &cmd->frame->dcmd.sgl);
-    iov_size = megasas_sgl_get_len(cmd, &cmd->frame->dcmd.sgl);
-    qemu_sglist_init(&cmd->qsg, 1, pci_dma_context(&s->dev));
-    qemu_sglist_add(&cmd->qsg, iov_pa, iov_size);
-    cmd->iov_size = iov_size;
-    return cmd->iov_size;
-}
-
-static void megasas_finish_dcmd(MegasasCmd *cmd, uint32_t iov_size)
-{
-    trace_megasas_finish_dcmd(cmd->index, iov_size);
-
-    if (cmd->frame->header.sge_count) {
-        qemu_sglist_destroy(&cmd->qsg);
-    }
-    if (iov_size > cmd->iov_size) {
-        if (megasas_frame_is_ieee_sgl(cmd)) {
-            cmd->frame->dcmd.sgl.sg_skinny->len = cpu_to_le32(iov_size);
-        } else if (megasas_frame_is_sgl64(cmd)) {
-            cmd->frame->dcmd.sgl.sg64->len = cpu_to_le32(iov_size);
-        } else {
-            cmd->frame->dcmd.sgl.sg32->len = cpu_to_le32(iov_size);
-        }
-    }
-    cmd->iov_size = 0;
-}
-
-static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
-{
-    struct mfi_ctrl_info info;
-    size_t dcmd_size = sizeof(info);
-    BusChild *kid;
-    int num_ld_disks = 0;
-    uint16_t sdev_id;
-
-    memset(&info, 0x0, cmd->iov_size);
-    if (cmd->iov_size < dcmd_size) {
-        trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
-                                            dcmd_size);
-        return MFI_STAT_INVALID_PARAMETER;
-    }
-
-    info.pci.vendor = cpu_to_le16(PCI_VENDOR_ID_LSI_LOGIC);
-    info.pci.device = cpu_to_le16(PCI_DEVICE_ID_LSI_SAS1078);
-    info.pci.subvendor = cpu_to_le16(PCI_VENDOR_ID_LSI_LOGIC);
-    info.pci.subdevice = cpu_to_le16(0x1013);
-
-    /*
-     * For some reason the firmware supports
-     * only up to 8 device ports.
-     * Despite supporting a far larger number
-     * of devices for the physical devices.
-     * So just display the first 8 devices
-     * in the device port list, independent
-     * of how many logical devices are actually
-     * present.
-     */
-    info.host.type = MFI_INFO_HOST_PCIE;
-    info.device.type = MFI_INFO_DEV_SAS3G;
-    info.device.port_count = 8;
-    QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
-        SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
-
-        if (num_ld_disks < 8) {
-            sdev_id = ((sdev->id & 0xFF) >> 8) | (sdev->lun & 0xFF);
-            info.device.port_addr[num_ld_disks] =
-                cpu_to_le64(megasas_get_sata_addr(sdev_id));
-        }
-        num_ld_disks++;
-    }
-
-    memcpy(info.product_name, "MegaRAID SAS 8708EM2", 20);
-    snprintf(info.serial_number, 32, "%s", s->hba_serial);
-    snprintf(info.package_version, 0x60, "%s-QEMU", QEMU_VERSION);
-    memcpy(info.image_component[0].name, "APP", 3);
-    memcpy(info.image_component[0].version, MEGASAS_VERSION "-QEMU", 9);
-    memcpy(info.image_component[0].build_date, __DATE__, 11);
-    memcpy(info.image_component[0].build_time, __TIME__, 8);
-    info.image_component_count = 1;
-    if (s->dev.has_rom) {
-        uint8_t biosver[32];
-        uint8_t *ptr;
-
-        ptr = memory_region_get_ram_ptr(&s->dev.rom);
-        memcpy(biosver, ptr + 0x41, 31);
-        qemu_put_ram_ptr(ptr);
-        memcpy(info.image_component[1].name, "BIOS", 4);
-        memcpy(info.image_component[1].version, biosver,
-               strlen((const char *)biosver));
-        info.image_component_count++;
-    }
-    info.current_fw_time = cpu_to_le32(megasas_fw_time());
-    info.max_arms = 32;
-    info.max_spans = 8;
-    info.max_arrays = MEGASAS_MAX_ARRAYS;
-    info.max_lds = s->fw_luns;
-    info.max_cmds = cpu_to_le16(s->fw_cmds);
-    info.max_sg_elements = cpu_to_le16(s->fw_sge);
-    info.max_request_size = cpu_to_le32(MEGASAS_MAX_SECTORS);
-    info.lds_present = cpu_to_le16(num_ld_disks);
-    info.pd_present = cpu_to_le16(num_ld_disks);
-    info.pd_disks_present = cpu_to_le16(num_ld_disks);
-    info.hw_present = cpu_to_le32(MFI_INFO_HW_NVRAM |
-                                   MFI_INFO_HW_MEM |
-                                   MFI_INFO_HW_FLASH);
-    info.memory_size = cpu_to_le16(512);
-    info.nvram_size = cpu_to_le16(32);
-    info.flash_size = cpu_to_le16(16);
-    info.raid_levels = cpu_to_le32(MFI_INFO_RAID_0);
-    info.adapter_ops = cpu_to_le32(MFI_INFO_AOPS_RBLD_RATE |
-                                    MFI_INFO_AOPS_SELF_DIAGNOSTIC |
-                                    MFI_INFO_AOPS_MIXED_ARRAY);
-    info.ld_ops = cpu_to_le32(MFI_INFO_LDOPS_DISK_CACHE_POLICY |
-                               MFI_INFO_LDOPS_ACCESS_POLICY |
-                               MFI_INFO_LDOPS_IO_POLICY |
-                               MFI_INFO_LDOPS_WRITE_POLICY |
-                               MFI_INFO_LDOPS_READ_POLICY);
-    info.max_strips_per_io = cpu_to_le16(s->fw_sge);
-    info.stripe_sz_ops.min = 3;
-    info.stripe_sz_ops.max = ffs(MEGASAS_MAX_SECTORS + 1) - 1;
-    info.properties.pred_fail_poll_interval = cpu_to_le16(300);
-    info.properties.intr_throttle_cnt = cpu_to_le16(16);
-    info.properties.intr_throttle_timeout = cpu_to_le16(50);
-    info.properties.rebuild_rate = 30;
-    info.properties.patrol_read_rate = 30;
-    info.properties.bgi_rate = 30;
-    info.properties.cc_rate = 30;
-    info.properties.recon_rate = 30;
-    info.properties.cache_flush_interval = 4;
-    info.properties.spinup_drv_cnt = 2;
-    info.properties.spinup_delay = 6;
-    info.properties.ecc_bucket_size = 15;
-    info.properties.ecc_bucket_leak_rate = cpu_to_le16(1440);
-    info.properties.expose_encl_devices = 1;
-    info.properties.OnOffProperties = cpu_to_le32(MFI_CTRL_PROP_EnableJBOD);
-    info.pd_ops = cpu_to_le32(MFI_INFO_PDOPS_FORCE_ONLINE |
-                               MFI_INFO_PDOPS_FORCE_OFFLINE);
-    info.pd_mix_support = cpu_to_le32(MFI_INFO_PDMIX_SAS |
-                                       MFI_INFO_PDMIX_SATA |
-                                       MFI_INFO_PDMIX_LD);
-
-    cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg);
-    return MFI_STAT_OK;
-}
-
-static int megasas_mfc_get_defaults(MegasasState *s, MegasasCmd *cmd)
-{
-    struct mfi_defaults info;
-    size_t dcmd_size = sizeof(struct mfi_defaults);
-
-    memset(&info, 0x0, dcmd_size);
-    if (cmd->iov_size < dcmd_size) {
-        trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
-                                            dcmd_size);
-        return MFI_STAT_INVALID_PARAMETER;
-    }
-
-    info.sas_addr = cpu_to_le64(s->sas_addr);
-    info.stripe_size = 3;
-    info.flush_time = 4;
-    info.background_rate = 30;
-    info.allow_mix_in_enclosure = 1;
-    info.allow_mix_in_ld = 1;
-    info.direct_pd_mapping = 1;
-    /* Enable for BIOS support */
-    info.bios_enumerate_lds = 1;
-    info.disable_ctrl_r = 1;
-    info.expose_enclosure_devices = 1;
-    info.disable_preboot_cli = 1;
-    info.cluster_disable = 1;
-
-    cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg);
-    return MFI_STAT_OK;
-}
-
-static int megasas_dcmd_get_bios_info(MegasasState *s, MegasasCmd *cmd)
-{
-    struct mfi_bios_data info;
-    size_t dcmd_size = sizeof(info);
-
-    memset(&info, 0x0, dcmd_size);
-    if (cmd->iov_size < dcmd_size) {
-        trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
-                                            dcmd_size);
-        return MFI_STAT_INVALID_PARAMETER;
-    }
-    info.continue_on_error = 1;
-    info.verbose = 1;
-    if (megasas_is_jbod(s)) {
-        info.expose_all_drives = 1;
-    }
-
-    cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg);
-    return MFI_STAT_OK;
-}
-
-static int megasas_dcmd_get_fw_time(MegasasState *s, MegasasCmd *cmd)
-{
-    uint64_t fw_time;
-    size_t dcmd_size = sizeof(fw_time);
-
-    fw_time = cpu_to_le64(megasas_fw_time());
-
-    cmd->iov_size -= dma_buf_read((uint8_t *)&fw_time, dcmd_size, &cmd->qsg);
-    return MFI_STAT_OK;
-}
-
-static int megasas_dcmd_set_fw_time(MegasasState *s, MegasasCmd *cmd)
-{
-    uint64_t fw_time;
-
-    /* This is a dummy; setting of firmware time is not allowed */
-    memcpy(&fw_time, cmd->frame->dcmd.mbox, sizeof(fw_time));
-
-    trace_megasas_dcmd_set_fw_time(cmd->index, fw_time);
-    fw_time = cpu_to_le64(megasas_fw_time());
-    return MFI_STAT_OK;
-}
-
-static int megasas_event_info(MegasasState *s, MegasasCmd *cmd)
-{
-    struct mfi_evt_log_state info;
-    size_t dcmd_size = sizeof(info);
-
-    memset(&info, 0, dcmd_size);
-
-    info.newest_seq_num = cpu_to_le32(s->event_count);
-    info.shutdown_seq_num = cpu_to_le32(s->shutdown_event);
-    info.boot_seq_num = cpu_to_le32(s->boot_event);
-
-    cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg);
-    return MFI_STAT_OK;
-}
-
-static int megasas_event_wait(MegasasState *s, MegasasCmd *cmd)
-{
-    union mfi_evt event;
-
-    if (cmd->iov_size < sizeof(struct mfi_evt_detail)) {
-        trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
-                                            sizeof(struct mfi_evt_detail));
-        return MFI_STAT_INVALID_PARAMETER;
-    }
-    s->event_count = cpu_to_le32(cmd->frame->dcmd.mbox[0]);
-    event.word = cpu_to_le32(cmd->frame->dcmd.mbox[4]);
-    s->event_locale = event.members.locale;
-    s->event_class = event.members.class;
-    s->event_cmd = cmd;
-    /* Decrease busy count; event frame doesn't count here */
-    s->busy--;
-    cmd->iov_size = sizeof(struct mfi_evt_detail);
-    return MFI_STAT_INVALID_STATUS;
-}
-
-static int megasas_dcmd_pd_get_list(MegasasState *s, MegasasCmd *cmd)
-{
-    struct mfi_pd_list info;
-    size_t dcmd_size = sizeof(info);
-    BusChild *kid;
-    uint32_t offset, dcmd_limit, num_pd_disks = 0, max_pd_disks;
-    uint16_t sdev_id;
-
-    memset(&info, 0, dcmd_size);
-    offset = 8;
-    dcmd_limit = offset + sizeof(struct mfi_pd_address);
-    if (cmd->iov_size < dcmd_limit) {
-        trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
-                                            dcmd_limit);
-        return MFI_STAT_INVALID_PARAMETER;
-    }
-
-    max_pd_disks = (cmd->iov_size - offset) / sizeof(struct mfi_pd_address);
-    if (max_pd_disks > s->fw_luns) {
-        max_pd_disks = s->fw_luns;
-    }
-
-    QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
-        SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
-
-        sdev_id = ((sdev->id & 0xFF) >> 8) | (sdev->lun & 0xFF);
-        info.addr[num_pd_disks].device_id = cpu_to_le16(sdev_id);
-        info.addr[num_pd_disks].encl_device_id = 0xFFFF;
-        info.addr[num_pd_disks].encl_index = 0;
-        info.addr[num_pd_disks].slot_number = (sdev->id & 0xFF);
-        info.addr[num_pd_disks].scsi_dev_type = sdev->type;
-        info.addr[num_pd_disks].connect_port_bitmap = 0x1;
-        info.addr[num_pd_disks].sas_addr[0] =
-            cpu_to_le64(megasas_get_sata_addr(sdev_id));
-        num_pd_disks++;
-        offset += sizeof(struct mfi_pd_address);
-    }
-    trace_megasas_dcmd_pd_get_list(cmd->index, num_pd_disks,
-                                   max_pd_disks, offset);
-
-    info.size = cpu_to_le32(offset);
-    info.count = cpu_to_le32(num_pd_disks);
-
-    cmd->iov_size -= dma_buf_read((uint8_t *)&info, offset, &cmd->qsg);
-    return MFI_STAT_OK;
-}
-
-static int megasas_dcmd_pd_list_query(MegasasState *s, MegasasCmd *cmd)
-{
-    uint16_t flags;
-
-    /* mbox0 contains flags */
-    flags = le16_to_cpu(cmd->frame->dcmd.mbox[0]);
-    trace_megasas_dcmd_pd_list_query(cmd->index, flags);
-    if (flags == MR_PD_QUERY_TYPE_ALL ||
-        megasas_is_jbod(s)) {
-        return megasas_dcmd_pd_get_list(s, cmd);
-    }
-
-    return MFI_STAT_OK;
-}
-
-static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
-                                      MegasasCmd *cmd)
-{
-    struct mfi_pd_info *info = cmd->iov_buf;
-    size_t dcmd_size = sizeof(struct mfi_pd_info);
-    BlockConf *conf = &sdev->conf;
-    uint64_t pd_size;
-    uint16_t sdev_id = ((sdev->id & 0xFF) >> 8) | (lun & 0xFF);
-    uint8_t cmdbuf[6];
-    SCSIRequest *req;
-    size_t len, resid;
-
-    if (!cmd->iov_buf) {
-        cmd->iov_buf = g_malloc(dcmd_size);
-        memset(cmd->iov_buf, 0, dcmd_size);
-        info = cmd->iov_buf;
-        info->inquiry_data[0] = 0x7f; /* Force PQual 0x3, PType 0x1f */
-        info->vpd_page83[0] = 0x7f;
-        megasas_setup_inquiry(cmdbuf, 0, sizeof(info->inquiry_data));
-        req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
-        if (!req) {
-            trace_megasas_dcmd_req_alloc_failed(cmd->index,
-                                                "PD get info std inquiry");
-            g_free(cmd->iov_buf);
-            cmd->iov_buf = NULL;
-            return MFI_STAT_FLASH_ALLOC_FAIL;
-        }
-        trace_megasas_dcmd_internal_submit(cmd->index,
-                                           "PD get info std inquiry", lun);
-        len = scsi_req_enqueue(req);
-        if (len > 0) {
-            cmd->iov_size = len;
-            scsi_req_continue(req);
-        }
-        return MFI_STAT_INVALID_STATUS;
-    } else if (info->inquiry_data[0] != 0x7f && info->vpd_page83[0] == 0x7f) {
-        megasas_setup_inquiry(cmdbuf, 0x83, sizeof(info->vpd_page83));
-        req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
-        if (!req) {
-            trace_megasas_dcmd_req_alloc_failed(cmd->index,
-                                                "PD get info vpd inquiry");
-            return MFI_STAT_FLASH_ALLOC_FAIL;
-        }
-        trace_megasas_dcmd_internal_submit(cmd->index,
-                                           "PD get info vpd inquiry", lun);
-        len = scsi_req_enqueue(req);
-        if (len > 0) {
-            cmd->iov_size = len;
-            scsi_req_continue(req);
-        }
-        return MFI_STAT_INVALID_STATUS;
-    }
-    /* Finished, set FW state */
-    if ((info->inquiry_data[0] >> 5) == 0) {
-        if (megasas_is_jbod(cmd->state)) {
-            info->fw_state = cpu_to_le16(MFI_PD_STATE_SYSTEM);
-        } else {
-            info->fw_state = cpu_to_le16(MFI_PD_STATE_ONLINE);
-        }
-    } else {
-        info->fw_state = cpu_to_le16(MFI_PD_STATE_OFFLINE);
-    }
-
-    info->ref.v.device_id = cpu_to_le16(sdev_id);
-    info->state.ddf.pd_type = cpu_to_le16(MFI_PD_DDF_TYPE_IN_VD|
-                                          MFI_PD_DDF_TYPE_INTF_SAS);
-    bdrv_get_geometry(conf->bs, &pd_size);
-    info->raw_size = cpu_to_le64(pd_size);
-    info->non_coerced_size = cpu_to_le64(pd_size);
-    info->coerced_size = cpu_to_le64(pd_size);
-    info->encl_device_id = 0xFFFF;
-    info->slot_number = (sdev->id & 0xFF);
-    info->path_info.count = 1;
-    info->path_info.sas_addr[0] =
-        cpu_to_le64(megasas_get_sata_addr(sdev_id));
-    info->connected_port_bitmap = 0x1;
-    info->device_speed = 1;
-    info->link_speed = 1;
-    resid = dma_buf_read(cmd->iov_buf, dcmd_size, &cmd->qsg);
-    g_free(cmd->iov_buf);
-    cmd->iov_size = dcmd_size - resid;
-    cmd->iov_buf = NULL;
-    return MFI_STAT_OK;
-}
-
-static int megasas_dcmd_pd_get_info(MegasasState *s, MegasasCmd *cmd)
-{
-    size_t dcmd_size = sizeof(struct mfi_pd_info);
-    uint16_t pd_id;
-    SCSIDevice *sdev = NULL;
-    int retval = MFI_STAT_DEVICE_NOT_FOUND;
-
-    if (cmd->iov_size < dcmd_size) {
-        return MFI_STAT_INVALID_PARAMETER;
-    }
-
-    /* mbox0 has the ID */
-    pd_id = le16_to_cpu(cmd->frame->dcmd.mbox[0]);
-    sdev = scsi_device_find(&s->bus, 0, pd_id, 0);
-    trace_megasas_dcmd_pd_get_info(cmd->index, pd_id);
-
-    if (sdev) {
-        /* Submit inquiry */
-        retval = megasas_pd_get_info_submit(sdev, pd_id, cmd);
-    }
-
-    return retval;
-}
-
-static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd)
-{
-    struct mfi_ld_list info;
-    size_t dcmd_size = sizeof(info), resid;
-    uint32_t num_ld_disks = 0, max_ld_disks = s->fw_luns;
-    uint64_t ld_size;
-    BusChild *kid;
-
-    memset(&info, 0, dcmd_size);
-    if (cmd->iov_size < dcmd_size) {
-        trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
-                                            dcmd_size);
-        return MFI_STAT_INVALID_PARAMETER;
-    }
-
-    if (megasas_is_jbod(s)) {
-        max_ld_disks = 0;
-    }
-    QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
-        SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
-        BlockConf *conf = &sdev->conf;
-
-        if (num_ld_disks >= max_ld_disks) {
-            break;
-        }
-        /* Logical device size is in blocks */
-        bdrv_get_geometry(conf->bs, &ld_size);
-        info.ld_list[num_ld_disks].ld.v.target_id = sdev->id;
-        info.ld_list[num_ld_disks].ld.v.lun_id = sdev->lun;
-        info.ld_list[num_ld_disks].state = MFI_LD_STATE_OPTIMAL;
-        info.ld_list[num_ld_disks].size = cpu_to_le64(ld_size);
-        num_ld_disks++;
-    }
-    info.ld_count = cpu_to_le32(num_ld_disks);
-    trace_megasas_dcmd_ld_get_list(cmd->index, num_ld_disks, max_ld_disks);
-
-    resid = dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg);
-    cmd->iov_size = dcmd_size - resid;
-    return MFI_STAT_OK;
-}
-
-static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
-                                      MegasasCmd *cmd)
-{
-    struct mfi_ld_info *info = cmd->iov_buf;
-    size_t dcmd_size = sizeof(struct mfi_ld_info);
-    uint8_t cdb[6];
-    SCSIRequest *req;
-    ssize_t len, resid;
-    BlockConf *conf = &sdev->conf;
-    uint16_t sdev_id = ((sdev->id & 0xFF) >> 8) | (lun & 0xFF);
-    uint64_t ld_size;
-
-    if (!cmd->iov_buf) {
-        cmd->iov_buf = g_malloc(dcmd_size);
-        memset(cmd->iov_buf, 0x0, dcmd_size);
-        info = cmd->iov_buf;
-        megasas_setup_inquiry(cdb, 0x83, sizeof(info->vpd_page83));
-        req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd);
-        if (!req) {
-            trace_megasas_dcmd_req_alloc_failed(cmd->index,
-                                                "LD get info vpd inquiry");
-            g_free(cmd->iov_buf);
-            cmd->iov_buf = NULL;
-            return MFI_STAT_FLASH_ALLOC_FAIL;
-        }
-        trace_megasas_dcmd_internal_submit(cmd->index,
-                                           "LD get info vpd inquiry", lun);
-        len = scsi_req_enqueue(req);
-        if (len > 0) {
-            cmd->iov_size = len;
-            scsi_req_continue(req);
-        }
-        return MFI_STAT_INVALID_STATUS;
-    }
-
-    info->ld_config.params.state = MFI_LD_STATE_OPTIMAL;
-    info->ld_config.properties.ld.v.target_id = lun;
-    info->ld_config.params.stripe_size = 3;
-    info->ld_config.params.num_drives = 1;
-    info->ld_config.params.is_consistent = 1;
-    /* Logical device size is in blocks */
-    bdrv_get_geometry(conf->bs, &ld_size);
-    info->size = cpu_to_le64(ld_size);
-    memset(info->ld_config.span, 0, sizeof(info->ld_config.span));
-    info->ld_config.span[0].start_block = 0;
-    info->ld_config.span[0].num_blocks = info->size;
-    info->ld_config.span[0].array_ref = cpu_to_le16(sdev_id);
-
-    resid = dma_buf_read(cmd->iov_buf, dcmd_size, &cmd->qsg);
-    g_free(cmd->iov_buf);
-    cmd->iov_size = dcmd_size - resid;
-    cmd->iov_buf = NULL;
-    return MFI_STAT_OK;
-}
-
-static int megasas_dcmd_ld_get_info(MegasasState *s, MegasasCmd *cmd)
-{
-    struct mfi_ld_info info;
-    size_t dcmd_size = sizeof(info);
-    uint16_t ld_id;
-    uint32_t max_ld_disks = s->fw_luns;
-    SCSIDevice *sdev = NULL;
-    int retval = MFI_STAT_DEVICE_NOT_FOUND;
-
-    if (cmd->iov_size < dcmd_size) {
-        return MFI_STAT_INVALID_PARAMETER;
-    }
-
-    /* mbox0 has the ID */
-    ld_id = le16_to_cpu(cmd->frame->dcmd.mbox[0]);
-    trace_megasas_dcmd_ld_get_info(cmd->index, ld_id);
-
-    if (megasas_is_jbod(s)) {
-        return MFI_STAT_DEVICE_NOT_FOUND;
-    }
-
-    if (ld_id < max_ld_disks) {
-        sdev = scsi_device_find(&s->bus, 0, ld_id, 0);
-    }
-
-    if (sdev) {
-        retval = megasas_ld_get_info_submit(sdev, ld_id, cmd);
-    }
-
-    return retval;
-}
-
-static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd)
-{
-    uint8_t data[4096];
-    struct mfi_config_data *info;
-    int num_pd_disks = 0, array_offset, ld_offset;
-    BusChild *kid;
-
-    if (cmd->iov_size > 4096) {
-        return MFI_STAT_INVALID_PARAMETER;
-    }
-
-    QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
-        num_pd_disks++;
-    }
-    info = (struct mfi_config_data *)&data;
-    /*
-     * Array mapping:
-     * - One array per SCSI device
-     * - One logical drive per SCSI device
-     *   spanning the entire device
-     */
-    info->array_count = num_pd_disks;
-    info->array_size = sizeof(struct mfi_array) * num_pd_disks;
-    info->log_drv_count = num_pd_disks;
-    info->log_drv_size = sizeof(struct mfi_ld_config) * num_pd_disks;
-    info->spares_count = 0;
-    info->spares_size = sizeof(struct mfi_spare);
-    info->size = sizeof(struct mfi_config_data) + info->array_size +
-        info->log_drv_size;
-    if (info->size > 4096) {
-        return MFI_STAT_INVALID_PARAMETER;
-    }
-
-    array_offset = sizeof(struct mfi_config_data);
-    ld_offset = array_offset + sizeof(struct mfi_array) * num_pd_disks;
-
-    QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
-        SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
-        BlockConf *conf = &sdev->conf;
-        uint16_t sdev_id = ((sdev->id & 0xFF) >> 8) | (sdev->lun & 0xFF);
-        struct mfi_array *array;
-        struct mfi_ld_config *ld;
-        uint64_t pd_size;
-        int i;
-
-        array = (struct mfi_array *)(data + array_offset);
-        bdrv_get_geometry(conf->bs, &pd_size);
-        array->size = cpu_to_le64(pd_size);
-        array->num_drives = 1;
-        array->array_ref = cpu_to_le16(sdev_id);
-        array->pd[0].ref.v.device_id = cpu_to_le16(sdev_id);
-        array->pd[0].ref.v.seq_num = 0;
-        array->pd[0].fw_state = MFI_PD_STATE_ONLINE;
-        array->pd[0].encl.pd = 0xFF;
-        array->pd[0].encl.slot = (sdev->id & 0xFF);
-        for (i = 1; i < MFI_MAX_ROW_SIZE; i++) {
-            array->pd[i].ref.v.device_id = 0xFFFF;
-            array->pd[i].ref.v.seq_num = 0;
-            array->pd[i].fw_state = MFI_PD_STATE_UNCONFIGURED_GOOD;
-            array->pd[i].encl.pd = 0xFF;
-            array->pd[i].encl.slot = 0xFF;
-        }
-        array_offset += sizeof(struct mfi_array);
-        ld = (struct mfi_ld_config *)(data + ld_offset);
-        memset(ld, 0, sizeof(struct mfi_ld_config));
-        ld->properties.ld.v.target_id = (sdev->id & 0xFF);
-        ld->properties.default_cache_policy = MR_LD_CACHE_READ_AHEAD |
-            MR_LD_CACHE_READ_ADAPTIVE;
-        ld->properties.current_cache_policy = MR_LD_CACHE_READ_AHEAD |
-            MR_LD_CACHE_READ_ADAPTIVE;
-        ld->params.state = MFI_LD_STATE_OPTIMAL;
-        ld->params.stripe_size = 3;
-        ld->params.num_drives = 1;
-        ld->params.span_depth = 1;
-        ld->params.is_consistent = 1;
-        ld->span[0].start_block = 0;
-        ld->span[0].num_blocks = cpu_to_le64(pd_size);
-        ld->span[0].array_ref = cpu_to_le16(sdev_id);
-        ld_offset += sizeof(struct mfi_ld_config);
-    }
-
-    cmd->iov_size -= dma_buf_read((uint8_t *)data, info->size, &cmd->qsg);
-    return MFI_STAT_OK;
-}
-
-static int megasas_dcmd_get_properties(MegasasState *s, MegasasCmd *cmd)
-{
-    struct mfi_ctrl_props info;
-    size_t dcmd_size = sizeof(info);
-
-    memset(&info, 0x0, dcmd_size);
-    if (cmd->iov_size < dcmd_size) {
-        trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
-                                            dcmd_size);
-        return MFI_STAT_INVALID_PARAMETER;
-    }
-    info.pred_fail_poll_interval = cpu_to_le16(300);
-    info.intr_throttle_cnt = cpu_to_le16(16);
-    info.intr_throttle_timeout = cpu_to_le16(50);
-    info.rebuild_rate = 30;
-    info.patrol_read_rate = 30;
-    info.bgi_rate = 30;
-    info.cc_rate = 30;
-    info.recon_rate = 30;
-    info.cache_flush_interval = 4;
-    info.spinup_drv_cnt = 2;
-    info.spinup_delay = 6;
-    info.ecc_bucket_size = 15;
-    info.ecc_bucket_leak_rate = cpu_to_le16(1440);
-    info.expose_encl_devices = 1;
-
-    cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg);
-    return MFI_STAT_OK;
-}
-
-static int megasas_cache_flush(MegasasState *s, MegasasCmd *cmd)
-{
-    bdrv_drain_all();
-    return MFI_STAT_OK;
-}
-
-static int megasas_ctrl_shutdown(MegasasState *s, MegasasCmd *cmd)
-{
-    s->fw_state = MFI_FWSTATE_READY;
-    return MFI_STAT_OK;
-}
-
-static int megasas_cluster_reset_ld(MegasasState *s, MegasasCmd *cmd)
-{
-    return MFI_STAT_INVALID_DCMD;
-}
-
-static int megasas_dcmd_set_properties(MegasasState *s, MegasasCmd *cmd)
-{
-    struct mfi_ctrl_props info;
-    size_t dcmd_size = sizeof(info);
-
-    if (cmd->iov_size < dcmd_size) {
-        trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
-                                            dcmd_size);
-        return MFI_STAT_INVALID_PARAMETER;
-    }
-    dma_buf_write((uint8_t *)&info, cmd->iov_size, &cmd->qsg);
-    trace_megasas_dcmd_unsupported(cmd->index, cmd->iov_size);
-    return MFI_STAT_OK;
-}
-
-static int megasas_dcmd_dummy(MegasasState *s, MegasasCmd *cmd)
-{
-    trace_megasas_dcmd_dummy(cmd->index, cmd->iov_size);
-    return MFI_STAT_OK;
-}
-
-static const struct dcmd_cmd_tbl_t {
-    int opcode;
-    const char *desc;
-    int (*func)(MegasasState *s, MegasasCmd *cmd);
-} dcmd_cmd_tbl[] = {
-    { MFI_DCMD_CTRL_MFI_HOST_MEM_ALLOC, "CTRL_HOST_MEM_ALLOC",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_CTRL_GET_INFO, "CTRL_GET_INFO",
-      megasas_ctrl_get_info },
-    { MFI_DCMD_CTRL_GET_PROPERTIES, "CTRL_GET_PROPERTIES",
-      megasas_dcmd_get_properties },
-    { MFI_DCMD_CTRL_SET_PROPERTIES, "CTRL_SET_PROPERTIES",
-      megasas_dcmd_set_properties },
-    { MFI_DCMD_CTRL_ALARM_GET, "CTRL_ALARM_GET",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_CTRL_ALARM_ENABLE, "CTRL_ALARM_ENABLE",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_CTRL_ALARM_DISABLE, "CTRL_ALARM_DISABLE",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_CTRL_ALARM_SILENCE, "CTRL_ALARM_SILENCE",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_CTRL_ALARM_TEST, "CTRL_ALARM_TEST",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_CTRL_EVENT_GETINFO, "CTRL_EVENT_GETINFO",
-      megasas_event_info },
-    { MFI_DCMD_CTRL_EVENT_GET, "CTRL_EVENT_GET",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_CTRL_EVENT_WAIT, "CTRL_EVENT_WAIT",
-      megasas_event_wait },
-    { MFI_DCMD_CTRL_SHUTDOWN, "CTRL_SHUTDOWN",
-      megasas_ctrl_shutdown },
-    { MFI_DCMD_HIBERNATE_STANDBY, "CTRL_STANDBY",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_CTRL_GET_TIME, "CTRL_GET_TIME",
-      megasas_dcmd_get_fw_time },
-    { MFI_DCMD_CTRL_SET_TIME, "CTRL_SET_TIME",
-      megasas_dcmd_set_fw_time },
-    { MFI_DCMD_CTRL_BIOS_DATA_GET, "CTRL_BIOS_DATA_GET",
-      megasas_dcmd_get_bios_info },
-    { MFI_DCMD_CTRL_FACTORY_DEFAULTS, "CTRL_FACTORY_DEFAULTS",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_CTRL_MFC_DEFAULTS_GET, "CTRL_MFC_DEFAULTS_GET",
-      megasas_mfc_get_defaults },
-    { MFI_DCMD_CTRL_MFC_DEFAULTS_SET, "CTRL_MFC_DEFAULTS_SET",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_CTRL_CACHE_FLUSH, "CTRL_CACHE_FLUSH",
-      megasas_cache_flush },
-    { MFI_DCMD_PD_GET_LIST, "PD_GET_LIST",
-      megasas_dcmd_pd_get_list },
-    { MFI_DCMD_PD_LIST_QUERY, "PD_LIST_QUERY",
-      megasas_dcmd_pd_list_query },
-    { MFI_DCMD_PD_GET_INFO, "PD_GET_INFO",
-      megasas_dcmd_pd_get_info },
-    { MFI_DCMD_PD_STATE_SET, "PD_STATE_SET",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_PD_REBUILD, "PD_REBUILD",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_PD_BLINK, "PD_BLINK",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_PD_UNBLINK, "PD_UNBLINK",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_LD_GET_LIST, "LD_GET_LIST",
-      megasas_dcmd_ld_get_list},
-    { MFI_DCMD_LD_GET_INFO, "LD_GET_INFO",
-      megasas_dcmd_ld_get_info },
-    { MFI_DCMD_LD_GET_PROP, "LD_GET_PROP",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_LD_SET_PROP, "LD_SET_PROP",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_LD_DELETE, "LD_DELETE",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_CFG_READ, "CFG_READ",
-      megasas_dcmd_cfg_read },
-    { MFI_DCMD_CFG_ADD, "CFG_ADD",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_CFG_CLEAR, "CFG_CLEAR",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_CFG_FOREIGN_READ, "CFG_FOREIGN_READ",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_CFG_FOREIGN_IMPORT, "CFG_FOREIGN_IMPORT",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_BBU_STATUS, "BBU_STATUS",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_BBU_CAPACITY_INFO, "BBU_CAPACITY_INFO",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_BBU_DESIGN_INFO, "BBU_DESIGN_INFO",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_BBU_PROP_GET, "BBU_PROP_GET",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_CLUSTER, "CLUSTER",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_CLUSTER_RESET_ALL, "CLUSTER_RESET_ALL",
-      megasas_dcmd_dummy },
-    { MFI_DCMD_CLUSTER_RESET_LD, "CLUSTER_RESET_LD",
-      megasas_cluster_reset_ld },
-    { -1, NULL, NULL }
-};
-
-static int megasas_handle_dcmd(MegasasState *s, MegasasCmd *cmd)
-{
-    int opcode, len;
-    int retval = 0;
-    const struct dcmd_cmd_tbl_t *cmdptr = dcmd_cmd_tbl;
-
-    opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
-    trace_megasas_handle_dcmd(cmd->index, opcode);
-    len = megasas_map_dcmd(s, cmd);
-    if (len < 0) {
-        return MFI_STAT_MEMORY_NOT_AVAILABLE;
-    }
-    while (cmdptr->opcode != -1 && cmdptr->opcode != opcode) {
-        cmdptr++;
-    }
-    if (cmdptr->opcode == -1) {
-        trace_megasas_dcmd_unhandled(cmd->index, opcode, len);
-        retval = megasas_dcmd_dummy(s, cmd);
-    } else {
-        trace_megasas_dcmd_enter(cmd->index, cmdptr->desc, len);
-        retval = cmdptr->func(s, cmd);
-    }
-    if (retval != MFI_STAT_INVALID_STATUS) {
-        megasas_finish_dcmd(cmd, len);
-    }
-    return retval;
-}
-
-static int megasas_finish_internal_dcmd(MegasasCmd *cmd,
-                                        SCSIRequest *req)
-{
-    int opcode;
-    int retval = MFI_STAT_OK;
-    int lun = req->lun;
-
-    opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
-    scsi_req_unref(req);
-    trace_megasas_dcmd_internal_finish(cmd->index, opcode, lun);
-    switch (opcode) {
-    case MFI_DCMD_PD_GET_INFO:
-        retval = megasas_pd_get_info_submit(req->dev, lun, cmd);
-        break;
-    case MFI_DCMD_LD_GET_INFO:
-        retval = megasas_ld_get_info_submit(req->dev, lun, cmd);
-        break;
-    default:
-        trace_megasas_dcmd_internal_invalid(cmd->index, opcode);
-        retval = MFI_STAT_INVALID_DCMD;
-        break;
-    }
-    if (retval != MFI_STAT_INVALID_STATUS) {
-        megasas_finish_dcmd(cmd, cmd->iov_size);
-    }
-    return retval;
-}
-
-static int megasas_enqueue_req(MegasasCmd *cmd, bool is_write)
-{
-    int len;
-
-    len = scsi_req_enqueue(cmd->req);
-    if (len < 0) {
-        len = -len;
-    }
-    if (len > 0) {
-        if (len > cmd->iov_size) {
-            if (is_write) {
-                trace_megasas_iov_write_overflow(cmd->index, len,
-                                                 cmd->iov_size);
-            } else {
-                trace_megasas_iov_read_overflow(cmd->index, len,
-                                                cmd->iov_size);
-            }
-        }
-        if (len < cmd->iov_size) {
-            if (is_write) {
-                trace_megasas_iov_write_underflow(cmd->index, len,
-                                                  cmd->iov_size);
-            } else {
-                trace_megasas_iov_read_underflow(cmd->index, len,
-                                                 cmd->iov_size);
-            }
-            cmd->iov_size = len;
-        }
-        scsi_req_continue(cmd->req);
-    }
-    return len;
-}
-
-static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd,
-                               bool is_logical)
-{
-    uint8_t *cdb;
-    int len;
-    bool is_write;
-    struct SCSIDevice *sdev = NULL;
-
-    cdb = cmd->frame->pass.cdb;
-
-    if (cmd->frame->header.target_id < s->fw_luns) {
-        sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id,
-                                cmd->frame->header.lun_id);
-    }
-    cmd->iov_size = le32_to_cpu(cmd->frame->header.data_len);
-    trace_megasas_handle_scsi(mfi_frame_desc[cmd->frame->header.frame_cmd],
-                              is_logical, cmd->frame->header.target_id,
-                              cmd->frame->header.lun_id, sdev, cmd->iov_size);
-
-    if (!sdev || (megasas_is_jbod(s) && is_logical)) {
-        trace_megasas_scsi_target_not_present(
-            mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical,
-            cmd->frame->header.target_id, cmd->frame->header.lun_id);
-        return MFI_STAT_DEVICE_NOT_FOUND;
-    }
-
-    if (cmd->frame->header.cdb_len > 16) {
-        trace_megasas_scsi_invalid_cdb_len(
-                mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical,
-                cmd->frame->header.target_id, cmd->frame->header.lun_id,
-                cmd->frame->header.cdb_len);
-        megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE));
-        cmd->frame->header.scsi_status = CHECK_CONDITION;
-        s->event_count++;
-        return MFI_STAT_SCSI_DONE_WITH_ERROR;
-    }
-
-    if (megasas_map_sgl(s, cmd, &cmd->frame->pass.sgl)) {
-        megasas_write_sense(cmd, SENSE_CODE(TARGET_FAILURE));
-        cmd->frame->header.scsi_status = CHECK_CONDITION;
-        s->event_count++;
-        return MFI_STAT_SCSI_DONE_WITH_ERROR;
-    }
-
-    cmd->req = scsi_req_new(sdev, cmd->index,
-                            cmd->frame->header.lun_id, cdb, cmd);
-    if (!cmd->req) {
-        trace_megasas_scsi_req_alloc_failed(
-                mfi_frame_desc[cmd->frame->header.frame_cmd],
-                cmd->frame->header.target_id, cmd->frame->header.lun_id);
-        megasas_write_sense(cmd, SENSE_CODE(NO_SENSE));
-        cmd->frame->header.scsi_status = BUSY;
-        s->event_count++;
-        return MFI_STAT_SCSI_DONE_WITH_ERROR;
-    }
-
-    is_write = (cmd->req->cmd.mode == SCSI_XFER_TO_DEV);
-    len = megasas_enqueue_req(cmd, is_write);
-    if (len > 0) {
-        if (is_write) {
-            trace_megasas_scsi_write_start(cmd->index, len);
-        } else {
-            trace_megasas_scsi_read_start(cmd->index, len);
-        }
-    } else {
-        trace_megasas_scsi_nodata(cmd->index);
-    }
-    return MFI_STAT_INVALID_STATUS;
-}
-
-static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd)
-{
-    uint32_t lba_count, lba_start_hi, lba_start_lo;
-    uint64_t lba_start;
-    bool is_write = (cmd->frame->header.frame_cmd == MFI_CMD_LD_WRITE);
-    uint8_t cdb[16];
-    int len;
-    struct SCSIDevice *sdev = NULL;
-
-    lba_count = le32_to_cpu(cmd->frame->io.header.data_len);
-    lba_start_lo = le32_to_cpu(cmd->frame->io.lba_lo);
-    lba_start_hi = le32_to_cpu(cmd->frame->io.lba_hi);
-    lba_start = ((uint64_t)lba_start_hi << 32) | lba_start_lo;
-
-    if (cmd->frame->header.target_id < s->fw_luns) {
-        sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id,
-                                cmd->frame->header.lun_id);
-    }
-
-    trace_megasas_handle_io(cmd->index,
-                            mfi_frame_desc[cmd->frame->header.frame_cmd],
-                            cmd->frame->header.target_id,
-                            cmd->frame->header.lun_id,
-                            (unsigned long)lba_start, (unsigned long)lba_count);
-    if (!sdev) {
-        trace_megasas_io_target_not_present(cmd->index,
-            mfi_frame_desc[cmd->frame->header.frame_cmd],
-            cmd->frame->header.target_id, cmd->frame->header.lun_id);
-        return MFI_STAT_DEVICE_NOT_FOUND;
-    }
-
-    if (cmd->frame->header.cdb_len > 16) {
-        trace_megasas_scsi_invalid_cdb_len(
-            mfi_frame_desc[cmd->frame->header.frame_cmd], 1,
-            cmd->frame->header.target_id, cmd->frame->header.lun_id,
-            cmd->frame->header.cdb_len);
-        megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE));
-        cmd->frame->header.scsi_status = CHECK_CONDITION;
-        s->event_count++;
-        return MFI_STAT_SCSI_DONE_WITH_ERROR;
-    }
-
-    cmd->iov_size = lba_count * sdev->blocksize;
-    if (megasas_map_sgl(s, cmd, &cmd->frame->io.sgl)) {
-        megasas_write_sense(cmd, SENSE_CODE(TARGET_FAILURE));
-        cmd->frame->header.scsi_status = CHECK_CONDITION;
-        s->event_count++;
-        return MFI_STAT_SCSI_DONE_WITH_ERROR;
-    }
-
-    megasas_encode_lba(cdb, lba_start, lba_count, is_write);
-    cmd->req = scsi_req_new(sdev, cmd->index,
-                            cmd->frame->header.lun_id, cdb, cmd);
-    if (!cmd->req) {
-        trace_megasas_scsi_req_alloc_failed(
-            mfi_frame_desc[cmd->frame->header.frame_cmd],
-            cmd->frame->header.target_id, cmd->frame->header.lun_id);
-        megasas_write_sense(cmd, SENSE_CODE(NO_SENSE));
-        cmd->frame->header.scsi_status = BUSY;
-        s->event_count++;
-        return MFI_STAT_SCSI_DONE_WITH_ERROR;
-    }
-    len = megasas_enqueue_req(cmd, is_write);
-    if (len > 0) {
-        if (is_write) {
-            trace_megasas_io_write_start(cmd->index, lba_start, lba_count, len);
-        } else {
-            trace_megasas_io_read_start(cmd->index, lba_start, lba_count, len);
-        }
-    }
-    return MFI_STAT_INVALID_STATUS;
-}
-
-static int megasas_finish_internal_command(MegasasCmd *cmd,
-                                           SCSIRequest *req, size_t resid)
-{
-    int retval = MFI_STAT_INVALID_CMD;
-
-    if (cmd->frame->header.frame_cmd == MFI_CMD_DCMD) {
-        cmd->iov_size -= resid;
-        retval = megasas_finish_internal_dcmd(cmd, req);
-    }
-    return retval;
-}
-
-static QEMUSGList *megasas_get_sg_list(SCSIRequest *req)
-{
-    MegasasCmd *cmd = req->hba_private;
-
-    if (cmd->frame->header.frame_cmd == MFI_CMD_DCMD) {
-        return NULL;
-    } else {
-        return &cmd->qsg;
-    }
-}
-
-static void megasas_xfer_complete(SCSIRequest *req, uint32_t len)
-{
-    MegasasCmd *cmd = req->hba_private;
-    uint8_t *buf;
-    uint32_t opcode;
-
-    trace_megasas_io_complete(cmd->index, len);
-
-    if (cmd->frame->header.frame_cmd != MFI_CMD_DCMD) {
-        scsi_req_continue(req);
-        return;
-    }
-
-    buf = scsi_req_get_buf(req);
-    opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
-    if (opcode == MFI_DCMD_PD_GET_INFO && cmd->iov_buf) {
-        struct mfi_pd_info *info = cmd->iov_buf;
-
-        if (info->inquiry_data[0] == 0x7f) {
-            memset(info->inquiry_data, 0, sizeof(info->inquiry_data));
-            memcpy(info->inquiry_data, buf, len);
-        } else if (info->vpd_page83[0] == 0x7f) {
-            memset(info->vpd_page83, 0, sizeof(info->vpd_page83));
-            memcpy(info->vpd_page83, buf, len);
-        }
-        scsi_req_continue(req);
-    } else if (opcode == MFI_DCMD_LD_GET_INFO) {
-        struct mfi_ld_info *info = cmd->iov_buf;
-
-        if (cmd->iov_buf) {
-            memcpy(info->vpd_page83, buf, sizeof(info->vpd_page83));
-            scsi_req_continue(req);
-        }
-    }
-}
-
-static void megasas_command_complete(SCSIRequest *req, uint32_t status,
-                                     size_t resid)
-{
-    MegasasCmd *cmd = req->hba_private;
-    uint8_t cmd_status = MFI_STAT_OK;
-
-    trace_megasas_command_complete(cmd->index, status, resid);
-
-    if (cmd->req != req) {
-        /*
-         * Internal command complete
-         */
-        cmd_status = megasas_finish_internal_command(cmd, req, resid);
-        if (cmd_status == MFI_STAT_INVALID_STATUS) {
-            return;
-        }
-    } else {
-        req->status = status;
-        trace_megasas_scsi_complete(cmd->index, req->status,
-                                    cmd->iov_size, req->cmd.xfer);
-        if (req->status != GOOD) {
-            cmd_status = MFI_STAT_SCSI_DONE_WITH_ERROR;
-        }
-        if (req->status == CHECK_CONDITION) {
-            megasas_copy_sense(cmd);
-        }
-
-        megasas_unmap_sgl(cmd);
-        cmd->frame->header.scsi_status = req->status;
-        scsi_req_unref(cmd->req);
-        cmd->req = NULL;
-    }
-    cmd->frame->header.cmd_status = cmd_status;
-    megasas_complete_frame(cmd->state, cmd->context);
-}
-
-static void megasas_command_cancel(SCSIRequest *req)
-{
-    MegasasCmd *cmd = req->hba_private;
-
-    if (cmd) {
-        megasas_abort_command(cmd);
-    } else {
-        scsi_req_unref(req);
-    }
-}
-
-static int megasas_handle_abort(MegasasState *s, MegasasCmd *cmd)
-{
-    uint64_t abort_ctx = le64_to_cpu(cmd->frame->abort.abort_context);
-    hwaddr abort_addr, addr_hi, addr_lo;
-    MegasasCmd *abort_cmd;
-
-    addr_hi = le32_to_cpu(cmd->frame->abort.abort_mfi_addr_hi);
-    addr_lo = le32_to_cpu(cmd->frame->abort.abort_mfi_addr_lo);
-    abort_addr = ((uint64_t)addr_hi << 32) | addr_lo;
-
-    abort_cmd = megasas_lookup_frame(s, abort_addr);
-    if (!abort_cmd) {
-        trace_megasas_abort_no_cmd(cmd->index, abort_ctx);
-        s->event_count++;
-        return MFI_STAT_OK;
-    }
-    if (!megasas_use_queue64(s)) {
-        abort_ctx &= (uint64_t)0xFFFFFFFF;
-    }
-    if (abort_cmd->context != abort_ctx) {
-        trace_megasas_abort_invalid_context(cmd->index, abort_cmd->index,
-                                            abort_cmd->context);
-        s->event_count++;
-        return MFI_STAT_ABORT_NOT_POSSIBLE;
-    }
-    trace_megasas_abort_frame(cmd->index, abort_cmd->index);
-    megasas_abort_command(abort_cmd);
-    if (!s->event_cmd || abort_cmd != s->event_cmd) {
-        s->event_cmd = NULL;
-    }
-    s->event_count++;
-    return MFI_STAT_OK;
-}
-
-static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr,
-                                 uint32_t frame_count)
-{
-    uint8_t frame_status = MFI_STAT_INVALID_CMD;
-    uint64_t frame_context;
-    MegasasCmd *cmd;
-
-    /*
-     * Always read 64bit context, top bits will be
-     * masked out if required in megasas_enqueue_frame()
-     */
-    frame_context = megasas_frame_get_context(frame_addr);
-
-    cmd = megasas_enqueue_frame(s, frame_addr, frame_context, frame_count);
-    if (!cmd) {
-        /* reply queue full */
-        trace_megasas_frame_busy(frame_addr);
-        megasas_frame_set_scsi_status(frame_addr, BUSY);
-        megasas_frame_set_cmd_status(frame_addr, MFI_STAT_SCSI_DONE_WITH_ERROR);
-        megasas_complete_frame(s, frame_context);
-        s->event_count++;
-        return;
-    }
-    switch (cmd->frame->header.frame_cmd) {
-    case MFI_CMD_INIT:
-        frame_status = megasas_init_firmware(s, cmd);
-        break;
-    case MFI_CMD_DCMD:
-        frame_status = megasas_handle_dcmd(s, cmd);
-        break;
-    case MFI_CMD_ABORT:
-        frame_status = megasas_handle_abort(s, cmd);
-        break;
-    case MFI_CMD_PD_SCSI_IO:
-        frame_status = megasas_handle_scsi(s, cmd, 0);
-        break;
-    case MFI_CMD_LD_SCSI_IO:
-        frame_status = megasas_handle_scsi(s, cmd, 1);
-        break;
-    case MFI_CMD_LD_READ:
-    case MFI_CMD_LD_WRITE:
-        frame_status = megasas_handle_io(s, cmd);
-        break;
-    default:
-        trace_megasas_unhandled_frame_cmd(cmd->index,
-                                          cmd->frame->header.frame_cmd);
-        s->event_count++;
-        break;
-    }
-    if (frame_status != MFI_STAT_INVALID_STATUS) {
-        if (cmd->frame) {
-            cmd->frame->header.cmd_status = frame_status;
-        } else {
-            megasas_frame_set_cmd_status(frame_addr, frame_status);
-        }
-        megasas_complete_frame(s, cmd->context);
-    }
-}
-
-static uint64_t megasas_mmio_read(void *opaque, hwaddr addr,
-                                  unsigned size)
-{
-    MegasasState *s = opaque;
-    uint32_t retval = 0;
-
-    switch (addr) {
-    case MFI_IDB:
-        retval = 0;
-        break;
-    case MFI_OMSG0:
-    case MFI_OSP0:
-        retval = (megasas_use_msix(s) ? MFI_FWSTATE_MSIX_SUPPORTED : 0) |
-            (s->fw_state & MFI_FWSTATE_MASK) |
-            ((s->fw_sge & 0xff) << 16) |
-            (s->fw_cmds & 0xFFFF);
-        break;
-    case MFI_OSTS:
-        if (megasas_intr_enabled(s) && s->doorbell) {
-            retval = MFI_1078_RM | 1;
-        }
-        break;
-    case MFI_OMSK:
-        retval = s->intr_mask;
-        break;
-    case MFI_ODCR0:
-        retval = s->doorbell;
-        break;
-    default:
-        trace_megasas_mmio_invalid_readl(addr);
-        break;
-    }
-    trace_megasas_mmio_readl(addr, retval);
-    return retval;
-}
-
-static void megasas_mmio_write(void *opaque, hwaddr addr,
-                               uint64_t val, unsigned size)
-{
-    MegasasState *s = opaque;
-    uint64_t frame_addr;
-    uint32_t frame_count;
-    int i;
-
-    trace_megasas_mmio_writel(addr, val);
-    switch (addr) {
-    case MFI_IDB:
-        if (val & MFI_FWINIT_ABORT) {
-            /* Abort all pending cmds */
-            for (i = 0; i < s->fw_cmds; i++) {
-                megasas_abort_command(&s->frames[i]);
-            }
-        }
-        if (val & MFI_FWINIT_READY) {
-            /* move to FW READY */
-            megasas_soft_reset(s);
-        }
-        if (val & MFI_FWINIT_MFIMODE) {
-            /* discard MFIs */
-        }
-        break;
-    case MFI_OMSK:
-        s->intr_mask = val;
-        if (!megasas_intr_enabled(s) && !msix_enabled(&s->dev)) {
-            trace_megasas_irq_lower();
-            qemu_irq_lower(s->dev.irq[0]);
-        }
-        if (megasas_intr_enabled(s)) {
-            trace_megasas_intr_enabled();
-        } else {
-            trace_megasas_intr_disabled();
-        }
-        break;
-    case MFI_ODCR0:
-        s->doorbell = 0;
-        if (s->producer_pa && megasas_intr_enabled(s)) {
-            /* Update reply queue pointer */
-            trace_megasas_qf_update(s->reply_queue_head, s->busy);
-            stl_le_phys(s->producer_pa, s->reply_queue_head);
-            if (!msix_enabled(&s->dev)) {
-                trace_megasas_irq_lower();
-                qemu_irq_lower(s->dev.irq[0]);
-            }
-        }
-        break;
-    case MFI_IQPH:
-        /* Received high 32 bits of a 64 bit MFI frame address */
-        s->frame_hi = val;
-        break;
-    case MFI_IQPL:
-        /* Received low 32 bits of a 64 bit MFI frame address */
-    case MFI_IQP:
-        /* Received 32 bit MFI frame address */
-        frame_addr = (val & ~0x1F);
-        /* Add possible 64 bit offset */
-        frame_addr |= ((uint64_t)s->frame_hi << 32);
-        s->frame_hi = 0;
-        frame_count = (val >> 1) & 0xF;
-        megasas_handle_frame(s, frame_addr, frame_count);
-        break;
-    default:
-        trace_megasas_mmio_invalid_writel(addr, val);
-        break;
-    }
-}
-
-static const MemoryRegionOps megasas_mmio_ops = {
-    .read = megasas_mmio_read,
-    .write = megasas_mmio_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .impl = {
-        .min_access_size = 8,
-        .max_access_size = 8,
-    }
-};
-
-static uint64_t megasas_port_read(void *opaque, hwaddr addr,
-                                  unsigned size)
-{
-    return megasas_mmio_read(opaque, addr & 0xff, size);
-}
-
-static void megasas_port_write(void *opaque, hwaddr addr,
-                               uint64_t val, unsigned size)
-{
-    megasas_mmio_write(opaque, addr & 0xff, val, size);
-}
-
-static const MemoryRegionOps megasas_port_ops = {
-    .read = megasas_port_read,
-    .write = megasas_port_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .impl = {
-        .min_access_size = 4,
-        .max_access_size = 4,
-    }
-};
-
-static uint64_t megasas_queue_read(void *opaque, hwaddr addr,
-                                   unsigned size)
-{
-    return 0;
-}
-
-static const MemoryRegionOps megasas_queue_ops = {
-    .read = megasas_queue_read,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .impl = {
-        .min_access_size = 8,
-        .max_access_size = 8,
-    }
-};
-
-static void megasas_soft_reset(MegasasState *s)
-{
-    int i;
-    MegasasCmd *cmd;
-
-    trace_megasas_reset();
-    for (i = 0; i < s->fw_cmds; i++) {
-        cmd = &s->frames[i];
-        megasas_abort_command(cmd);
-    }
-    megasas_reset_frames(s);
-    s->reply_queue_len = s->fw_cmds;
-    s->reply_queue_pa = 0;
-    s->consumer_pa = 0;
-    s->producer_pa = 0;
-    s->fw_state = MFI_FWSTATE_READY;
-    s->doorbell = 0;
-    s->intr_mask = MEGASAS_INTR_DISABLED_MASK;
-    s->frame_hi = 0;
-    s->flags &= ~MEGASAS_MASK_USE_QUEUE64;
-    s->event_count++;
-    s->boot_event = s->event_count;
-}
-
-static void megasas_scsi_reset(DeviceState *dev)
-{
-    MegasasState *s = DO_UPCAST(MegasasState, dev.qdev, dev);
-
-    megasas_soft_reset(s);
-}
-
-static const VMStateDescription vmstate_megasas = {
-    .name = "megasas",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .fields      = (VMStateField[]) {
-        VMSTATE_PCI_DEVICE(dev, MegasasState),
-
-        VMSTATE_INT32(fw_state, MegasasState),
-        VMSTATE_INT32(intr_mask, MegasasState),
-        VMSTATE_INT32(doorbell, MegasasState),
-        VMSTATE_UINT64(reply_queue_pa, MegasasState),
-        VMSTATE_UINT64(consumer_pa, MegasasState),
-        VMSTATE_UINT64(producer_pa, MegasasState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void megasas_scsi_uninit(PCIDevice *d)
-{
-    MegasasState *s = DO_UPCAST(MegasasState, dev, d);
-
-#ifdef USE_MSIX
-    msix_uninit(&s->dev, &s->mmio_io);
-#endif
-    memory_region_destroy(&s->mmio_io);
-    memory_region_destroy(&s->port_io);
-    memory_region_destroy(&s->queue_io);
-}
-
-static const struct SCSIBusInfo megasas_scsi_info = {
-    .tcq = true,
-    .max_target = MFI_MAX_LD,
-    .max_lun = 255,
-
-    .transfer_data = megasas_xfer_complete,
-    .get_sg_list = megasas_get_sg_list,
-    .complete = megasas_command_complete,
-    .cancel = megasas_command_cancel,
-};
-
-static int megasas_scsi_init(PCIDevice *dev)
-{
-    MegasasState *s = DO_UPCAST(MegasasState, dev, dev);
-    uint8_t *pci_conf;
-    int i, bar_type;
-
-    pci_conf = s->dev.config;
-
-    /* PCI latency timer = 0 */
-    pci_conf[PCI_LATENCY_TIMER] = 0;
-    /* Interrupt pin 1 */
-    pci_conf[PCI_INTERRUPT_PIN] = 0x01;
-
-    memory_region_init_io(&s->mmio_io, &megasas_mmio_ops, s,
-                          "megasas-mmio", 0x4000);
-    memory_region_init_io(&s->port_io, &megasas_port_ops, s,
-                          "megasas-io", 256);
-    memory_region_init_io(&s->queue_io, &megasas_queue_ops, s,
-                          "megasas-queue", 0x40000);
-
-#ifdef USE_MSIX
-    /* MSI-X support is currently broken */
-    if (megasas_use_msix(s) &&
-        msix_init(&s->dev, 15, &s->mmio_io, 0, 0x2000)) {
-        s->flags &= ~MEGASAS_MASK_USE_MSIX;
-    }
-#else
-    s->flags &= ~MEGASAS_MASK_USE_MSIX;
-#endif
-
-    bar_type = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64;
-    pci_register_bar(&s->dev, 0, bar_type, &s->mmio_io);
-    pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_IO, &s->port_io);
-    pci_register_bar(&s->dev, 3, bar_type, &s->queue_io);
-
-    if (megasas_use_msix(s)) {
-        msix_vector_use(&s->dev, 0);
-    }
-
-    if (!s->sas_addr) {
-        s->sas_addr = ((NAA_LOCALLY_ASSIGNED_ID << 24) |
-                       IEEE_COMPANY_LOCALLY_ASSIGNED) << 36;
-        s->sas_addr |= (pci_bus_num(dev->bus) << 16);
-        s->sas_addr |= (PCI_SLOT(dev->devfn) << 8);
-        s->sas_addr |= PCI_FUNC(dev->devfn);
-    }
-    if (!s->hba_serial) {
-       s->hba_serial = g_strdup(MEGASAS_HBA_SERIAL);
-    }
-    if (s->fw_sge >= MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE) {
-        s->fw_sge = MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE;
-    } else if (s->fw_sge >= 128 - MFI_PASS_FRAME_SIZE) {
-        s->fw_sge = 128 - MFI_PASS_FRAME_SIZE;
-    } else {
-        s->fw_sge = 64 - MFI_PASS_FRAME_SIZE;
-    }
-    if (s->fw_cmds > MEGASAS_MAX_FRAMES) {
-        s->fw_cmds = MEGASAS_MAX_FRAMES;
-    }
-    trace_megasas_init(s->fw_sge, s->fw_cmds,
-                       megasas_use_msix(s) ? "MSI-X" : "INTx",
-                       megasas_is_jbod(s) ? "jbod" : "raid");
-    s->fw_luns = (MFI_MAX_LD > MAX_SCSI_DEVS) ?
-        MAX_SCSI_DEVS : MFI_MAX_LD;
-    s->producer_pa = 0;
-    s->consumer_pa = 0;
-    for (i = 0; i < s->fw_cmds; i++) {
-        s->frames[i].index = i;
-        s->frames[i].context = -1;
-        s->frames[i].pa = 0;
-        s->frames[i].state = s;
-    }
-
-    scsi_bus_new(&s->bus, &dev->qdev, &megasas_scsi_info);
-    scsi_bus_legacy_handle_cmdline(&s->bus);
-    return 0;
-}
-
-static Property megasas_properties[] = {
-    DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge,
-                       MEGASAS_DEFAULT_SGE),
-    DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds,
-                       MEGASAS_DEFAULT_FRAMES),
-    DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial),
-    DEFINE_PROP_HEX64("sas_address", MegasasState, sas_addr, 0),
-#ifdef USE_MSIX
-    DEFINE_PROP_BIT("use_msix", MegasasState, flags,
-                    MEGASAS_FLAG_USE_MSIX, false),
-#endif
-    DEFINE_PROP_BIT("use_jbod", MegasasState, flags,
-                    MEGASAS_FLAG_USE_JBOD, false),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void megasas_class_init(ObjectClass *oc, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(oc);
-    PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
-
-    pc->init = megasas_scsi_init;
-    pc->exit = megasas_scsi_uninit;
-    pc->vendor_id = PCI_VENDOR_ID_LSI_LOGIC;
-    pc->device_id = PCI_DEVICE_ID_LSI_SAS1078;
-    pc->subsystem_vendor_id = PCI_VENDOR_ID_LSI_LOGIC;
-    pc->subsystem_id = 0x1013;
-    pc->class_id = PCI_CLASS_STORAGE_RAID;
-    dc->props = megasas_properties;
-    dc->reset = megasas_scsi_reset;
-    dc->vmsd = &vmstate_megasas;
-    dc->desc = "LSI MegaRAID SAS 1078";
-}
-
-static const TypeInfo megasas_info = {
-    .name  = "megasas",
-    .parent = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(MegasasState),
-    .class_init = megasas_class_init,
-};
-
-static void megasas_register_types(void)
-{
-    type_register_static(&megasas_info);
-}
-
-type_init(megasas_register_types)
diff --git a/hw/mipsnet.c b/hw/mipsnet.c
deleted file mode 100644 (file)
index ac6193a..0000000
+++ /dev/null
@@ -1,284 +0,0 @@
-#include "hw/hw.h"
-#include "net/net.h"
-#include "trace.h"
-#include "hw/sysbus.h"
-
-/* MIPSnet register offsets */
-
-#define MIPSNET_DEV_ID         0x00
-#define MIPSNET_BUSY           0x08
-#define MIPSNET_RX_DATA_COUNT  0x0c
-#define MIPSNET_TX_DATA_COUNT  0x10
-#define MIPSNET_INT_CTL                0x14
-# define MIPSNET_INTCTL_TXDONE         0x00000001
-# define MIPSNET_INTCTL_RXDONE         0x00000002
-# define MIPSNET_INTCTL_TESTBIT                0x80000000
-#define MIPSNET_INTERRUPT_INFO 0x18
-#define MIPSNET_RX_DATA_BUFFER 0x1c
-#define MIPSNET_TX_DATA_BUFFER 0x20
-
-#define MAX_ETH_FRAME_SIZE     1514
-
-typedef struct MIPSnetState {
-    SysBusDevice busdev;
-
-    uint32_t busy;
-    uint32_t rx_count;
-    uint32_t rx_read;
-    uint32_t tx_count;
-    uint32_t tx_written;
-    uint32_t intctl;
-    uint8_t rx_buffer[MAX_ETH_FRAME_SIZE];
-    uint8_t tx_buffer[MAX_ETH_FRAME_SIZE];
-    MemoryRegion io;
-    qemu_irq irq;
-    NICState *nic;
-    NICConf conf;
-} MIPSnetState;
-
-static void mipsnet_reset(MIPSnetState *s)
-{
-    s->busy = 1;
-    s->rx_count = 0;
-    s->rx_read = 0;
-    s->tx_count = 0;
-    s->tx_written = 0;
-    s->intctl = 0;
-    memset(s->rx_buffer, 0, MAX_ETH_FRAME_SIZE);
-    memset(s->tx_buffer, 0, MAX_ETH_FRAME_SIZE);
-}
-
-static void mipsnet_update_irq(MIPSnetState *s)
-{
-    int isr = !!s->intctl;
-    trace_mipsnet_irq(isr, s->intctl);
-    qemu_set_irq(s->irq, isr);
-}
-
-static int mipsnet_buffer_full(MIPSnetState *s)
-{
-    if (s->rx_count >= MAX_ETH_FRAME_SIZE)
-        return 1;
-    return 0;
-}
-
-static int mipsnet_can_receive(NetClientState *nc)
-{
-    MIPSnetState *s = qemu_get_nic_opaque(nc);
-
-    if (s->busy)
-        return 0;
-    return !mipsnet_buffer_full(s);
-}
-
-static ssize_t mipsnet_receive(NetClientState *nc, const uint8_t *buf, size_t size)
-{
-    MIPSnetState *s = qemu_get_nic_opaque(nc);
-
-    trace_mipsnet_receive(size);
-    if (!mipsnet_can_receive(nc))
-        return -1;
-
-    s->busy = 1;
-
-    /* Just accept everything. */
-
-    /* Write packet data. */
-    memcpy(s->rx_buffer, buf, size);
-
-    s->rx_count = size;
-    s->rx_read = 0;
-
-    /* Now we can signal we have received something. */
-    s->intctl |= MIPSNET_INTCTL_RXDONE;
-    mipsnet_update_irq(s);
-
-    return size;
-}
-
-static uint64_t mipsnet_ioport_read(void *opaque, hwaddr addr,
-                                    unsigned int size)
-{
-    MIPSnetState *s = opaque;
-    int ret = 0;
-
-    addr &= 0x3f;
-    switch (addr) {
-    case MIPSNET_DEV_ID:
-       ret = be32_to_cpu(0x4d495053);          /* MIPS */
-        break;
-    case MIPSNET_DEV_ID + 4:
-       ret = be32_to_cpu(0x4e455430);          /* NET0 */
-        break;
-    case MIPSNET_BUSY:
-       ret = s->busy;
-        break;
-    case MIPSNET_RX_DATA_COUNT:
-       ret = s->rx_count;
-        break;
-    case MIPSNET_TX_DATA_COUNT:
-       ret = s->tx_count;
-        break;
-    case MIPSNET_INT_CTL:
-       ret = s->intctl;
-        s->intctl &= ~MIPSNET_INTCTL_TESTBIT;
-        break;
-    case MIPSNET_INTERRUPT_INFO:
-        /* XXX: This seems to be a per-VPE interrupt number. */
-       ret = 0;
-        break;
-    case MIPSNET_RX_DATA_BUFFER:
-        if (s->rx_count) {
-            s->rx_count--;
-            ret = s->rx_buffer[s->rx_read++];
-        }
-        break;
-    /* Reads as zero. */
-    case MIPSNET_TX_DATA_BUFFER:
-    default:
-        break;
-    }
-    trace_mipsnet_read(addr, ret);
-    return ret;
-}
-
-static void mipsnet_ioport_write(void *opaque, hwaddr addr,
-                                 uint64_t val, unsigned int size)
-{
-    MIPSnetState *s = opaque;
-
-    addr &= 0x3f;
-    trace_mipsnet_write(addr, val);
-    switch (addr) {
-    case MIPSNET_TX_DATA_COUNT:
-       s->tx_count = (val <= MAX_ETH_FRAME_SIZE) ? val : 0;
-        s->tx_written = 0;
-        break;
-    case MIPSNET_INT_CTL:
-        if (val & MIPSNET_INTCTL_TXDONE) {
-            s->intctl &= ~MIPSNET_INTCTL_TXDONE;
-        } else if (val & MIPSNET_INTCTL_RXDONE) {
-            s->intctl &= ~MIPSNET_INTCTL_RXDONE;
-        } else if (val & MIPSNET_INTCTL_TESTBIT) {
-            mipsnet_reset(s);
-            s->intctl |= MIPSNET_INTCTL_TESTBIT;
-        } else if (!val) {
-            /* ACK testbit interrupt, flag was cleared on read. */
-        }
-        s->busy = !!s->intctl;
-        mipsnet_update_irq(s);
-        break;
-    case MIPSNET_TX_DATA_BUFFER:
-        s->tx_buffer[s->tx_written++] = val;
-        if (s->tx_written == s->tx_count) {
-            /* Send buffer. */
-            trace_mipsnet_send(s->tx_count);
-            qemu_send_packet(qemu_get_queue(s->nic), s->tx_buffer, s->tx_count);
-            s->tx_count = s->tx_written = 0;
-            s->intctl |= MIPSNET_INTCTL_TXDONE;
-            s->busy = 1;
-            mipsnet_update_irq(s);
-        }
-        break;
-    /* Read-only registers */
-    case MIPSNET_DEV_ID:
-    case MIPSNET_BUSY:
-    case MIPSNET_RX_DATA_COUNT:
-    case MIPSNET_INTERRUPT_INFO:
-    case MIPSNET_RX_DATA_BUFFER:
-    default:
-        break;
-    }
-}
-
-static const VMStateDescription vmstate_mipsnet = {
-    .name = "mipsnet",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT32(busy, MIPSnetState),
-        VMSTATE_UINT32(rx_count, MIPSnetState),
-        VMSTATE_UINT32(rx_read, MIPSnetState),
-        VMSTATE_UINT32(tx_count, MIPSnetState),
-        VMSTATE_UINT32(tx_written, MIPSnetState),
-        VMSTATE_UINT32(intctl, MIPSnetState),
-        VMSTATE_BUFFER(rx_buffer, MIPSnetState),
-        VMSTATE_BUFFER(tx_buffer, MIPSnetState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void mipsnet_cleanup(NetClientState *nc)
-{
-    MIPSnetState *s = qemu_get_nic_opaque(nc);
-
-    s->nic = NULL;
-}
-
-static NetClientInfo net_mipsnet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
-    .size = sizeof(NICState),
-    .can_receive = mipsnet_can_receive,
-    .receive = mipsnet_receive,
-    .cleanup = mipsnet_cleanup,
-};
-
-static const MemoryRegionOps mipsnet_ioport_ops = {
-    .read = mipsnet_ioport_read,
-    .write = mipsnet_ioport_write,
-    .impl.min_access_size = 1,
-    .impl.max_access_size = 4,
-};
-
-static int mipsnet_sysbus_init(SysBusDevice *dev)
-{
-    MIPSnetState *s = DO_UPCAST(MIPSnetState, busdev, dev);
-
-    memory_region_init_io(&s->io, &mipsnet_ioport_ops, s, "mipsnet-io", 36);
-    sysbus_init_mmio(dev, &s->io);
-    sysbus_init_irq(dev, &s->irq);
-
-    s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf,
-                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
-    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
-
-    return 0;
-}
-
-static void mipsnet_sysbus_reset(DeviceState *dev)
-{
-    MIPSnetState *s = DO_UPCAST(MIPSnetState, busdev.qdev, dev);
-    mipsnet_reset(s);
-}
-
-static Property mipsnet_properties[] = {
-    DEFINE_NIC_PROPERTIES(MIPSnetState, conf),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void mipsnet_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = mipsnet_sysbus_init;
-    dc->desc = "MIPS Simulator network device";
-    dc->reset = mipsnet_sysbus_reset;
-    dc->vmsd = &vmstate_mipsnet;
-    dc->props = mipsnet_properties;
-}
-
-static const TypeInfo mipsnet_info = {
-    .name          = "mipsnet",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(MIPSnetState),
-    .class_init    = mipsnet_class_init,
-};
-
-static void mipsnet_register_types(void)
-{
-    type_register_static(&mipsnet_info);
-}
-
-type_init(mipsnet_register_types)
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..009b1d9f019381a7c36e80b0a7ad4052faea44c4 100644 (file)
@@ -0,0 +1,11 @@
+common-obj-$(CONFIG_APPLESMC) += applesmc.o
+common-obj-$(CONFIG_MAX111X) += max111x.o
+common-obj-$(CONFIG_TMP105) += tmp105.o
+
+# ARM devices
+common-obj-$(CONFIG_PL310) += arm_l2x0.o
+
+# PKUnity SoC devices
+common-obj-$(CONFIG_PUV3) += puv3_pm.o
+
+common-obj-$(CONFIG_MACIO) += macio/
diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c
new file mode 100644 (file)
index 0000000..c29558b
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ *  Apple SMC controller
+ *
+ *  Copyright (c) 2007 Alexander Graf
+ *
+ *  Authors: Alexander Graf <agraf@suse.de>
+ *           Susanne Graf <suse@csgraf.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * *****************************************************************
+ *
+ * In all Intel-based Apple hardware there is an SMC chip to control the
+ * backlight, fans and several other generic device parameters. It also
+ * contains the magic keys used to dongle Mac OS X to the device.
+ *
+ * This driver was mostly created by looking at the Linux AppleSMC driver
+ * implementation and does not support IRQ.
+ *
+ */
+
+#include "hw/hw.h"
+#include "hw/isa/isa.h"
+#include "ui/console.h"
+#include "qemu/timer.h"
+
+/* #define DEBUG_SMC */
+
+#define APPLESMC_DEFAULT_IOBASE        0x300
+/* data port used by Apple SMC */
+#define APPLESMC_DATA_PORT             0x0
+/* command/status port used by Apple SMC */
+#define APPLESMC_CMD_PORT              0x4
+#define APPLESMC_NR_PORTS              32
+#define APPLESMC_MAX_DATA_LENGTH       32
+
+#define APPLESMC_READ_CMD              0x10
+#define APPLESMC_WRITE_CMD             0x11
+#define APPLESMC_GET_KEY_BY_INDEX_CMD  0x12
+#define APPLESMC_GET_KEY_TYPE_CMD      0x13
+
+#ifdef DEBUG_SMC
+#define smc_debug(...) fprintf(stderr, "AppleSMC: " __VA_ARGS__)
+#else
+#define smc_debug(...) do { } while(0)
+#endif
+
+static char default_osk[64] = "This is a dummy key. Enter the real key "
+                              "using the -osk parameter";
+
+struct AppleSMCData {
+    uint8_t len;
+    const char *key;
+    const char *data;
+    QLIST_ENTRY(AppleSMCData) node;
+};
+
+struct AppleSMCStatus {
+    ISADevice dev;
+    uint32_t iobase;
+    uint8_t cmd;
+    uint8_t status;
+    uint8_t key[4];
+    uint8_t read_pos;
+    uint8_t data_len;
+    uint8_t data_pos;
+    uint8_t data[255];
+    uint8_t charactic[4];
+    char *osk;
+    QLIST_HEAD(, AppleSMCData) data_def;
+};
+
+static void applesmc_io_cmd_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+    struct AppleSMCStatus *s = opaque;
+
+    smc_debug("CMD Write B: %#x = %#x\n", addr, val);
+    switch(val) {
+        case APPLESMC_READ_CMD:
+            s->status = 0x0c;
+            break;
+    }
+    s->cmd = val;
+    s->read_pos = 0;
+    s->data_pos = 0;
+}
+
+static void applesmc_fill_data(struct AppleSMCStatus *s)
+{
+    struct AppleSMCData *d;
+
+    QLIST_FOREACH(d, &s->data_def, node) {
+        if (!memcmp(d->key, s->key, 4)) {
+            smc_debug("Key matched (%s Len=%d Data=%s)\n", d->key,
+                      d->len, d->data);
+            memcpy(s->data, d->data, d->len);
+            return;
+        }
+    }
+}
+
+static void applesmc_io_data_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+    struct AppleSMCStatus *s = opaque;
+
+    smc_debug("DATA Write B: %#x = %#x\n", addr, val);
+    switch(s->cmd) {
+        case APPLESMC_READ_CMD:
+            if(s->read_pos < 4) {
+                s->key[s->read_pos] = val;
+                s->status = 0x04;
+            } else if(s->read_pos == 4) {
+                s->data_len = val;
+                s->status = 0x05;
+                s->data_pos = 0;
+                smc_debug("Key = %c%c%c%c Len = %d\n", s->key[0],
+                          s->key[1], s->key[2], s->key[3], val);
+                applesmc_fill_data(s);
+            }
+            s->read_pos++;
+            break;
+    }
+}
+
+static uint32_t applesmc_io_data_readb(void *opaque, uint32_t addr1)
+{
+    struct AppleSMCStatus *s = opaque;
+    uint8_t retval = 0;
+
+    switch(s->cmd) {
+        case APPLESMC_READ_CMD:
+            if(s->data_pos < s->data_len) {
+                retval = s->data[s->data_pos];
+                smc_debug("READ_DATA[%d] = %#hhx\n", s->data_pos,
+                          retval);
+                s->data_pos++;
+                if(s->data_pos == s->data_len) {
+                    s->status = 0x00;
+                    smc_debug("EOF\n");
+                } else
+                    s->status = 0x05;
+            }
+    }
+    smc_debug("DATA Read b: %#x = %#x\n", addr1, retval);
+
+    return retval;
+}
+
+static uint32_t applesmc_io_cmd_readb(void *opaque, uint32_t addr1)
+{
+    struct AppleSMCStatus *s = opaque;
+
+    smc_debug("CMD Read B: %#x\n", addr1);
+    return s->status;
+}
+
+static void applesmc_add_key(struct AppleSMCStatus *s, const char *key,
+                             int len, const char *data)
+{
+    struct AppleSMCData *def;
+
+    def = g_malloc0(sizeof(struct AppleSMCData));
+    def->key = key;
+    def->len = len;
+    def->data = data;
+
+    QLIST_INSERT_HEAD(&s->data_def, def, node);
+}
+
+static void qdev_applesmc_isa_reset(DeviceState *dev)
+{
+    struct AppleSMCStatus *s = DO_UPCAST(struct AppleSMCStatus, dev.qdev, dev);
+    struct AppleSMCData *d, *next;
+
+    /* Remove existing entries */
+    QLIST_FOREACH_SAFE(d, &s->data_def, node, next) {
+        QLIST_REMOVE(d, node);
+    }
+
+    applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03");
+    applesmc_add_key(s, "OSK0", 32, s->osk);
+    applesmc_add_key(s, "OSK1", 32, s->osk + 32);
+    applesmc_add_key(s, "NATJ", 1, "\0");
+    applesmc_add_key(s, "MSSP", 1, "\0");
+    applesmc_add_key(s, "MSSD", 1, "\0x3");
+}
+
+static int applesmc_isa_init(ISADevice *dev)
+{
+    struct AppleSMCStatus *s = DO_UPCAST(struct AppleSMCStatus, dev, dev);
+
+    register_ioport_read(s->iobase + APPLESMC_DATA_PORT, 4, 1,
+                         applesmc_io_data_readb, s);
+    register_ioport_read(s->iobase + APPLESMC_CMD_PORT, 4, 1,
+                         applesmc_io_cmd_readb, s);
+    register_ioport_write(s->iobase + APPLESMC_DATA_PORT, 4, 1,
+                          applesmc_io_data_writeb, s);
+    register_ioport_write(s->iobase + APPLESMC_CMD_PORT, 4, 1,
+                          applesmc_io_cmd_writeb, s);
+
+    if (!s->osk || (strlen(s->osk) != 64)) {
+        fprintf(stderr, "WARNING: Using AppleSMC with invalid key\n");
+        s->osk = default_osk;
+    }
+
+    QLIST_INIT(&s->data_def);
+    qdev_applesmc_isa_reset(&dev->qdev);
+
+    return 0;
+}
+
+static Property applesmc_isa_properties[] = {
+    DEFINE_PROP_HEX32("iobase", struct AppleSMCStatus, iobase,
+                      APPLESMC_DEFAULT_IOBASE),
+    DEFINE_PROP_STRING("osk", struct AppleSMCStatus, osk),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void qdev_applesmc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+    ic->init = applesmc_isa_init;
+    dc->reset = qdev_applesmc_isa_reset;
+    dc->props = applesmc_isa_properties;
+}
+
+static const TypeInfo applesmc_isa_info = {
+    .name          = "isa-applesmc",
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(struct AppleSMCStatus),
+    .class_init    = qdev_applesmc_class_init,
+};
+
+static void applesmc_register_types(void)
+{
+    type_register_static(&applesmc_isa_info);
+}
+
+type_init(applesmc_register_types)
diff --git a/hw/misc/arm_l2x0.c b/hw/misc/arm_l2x0.c
new file mode 100644 (file)
index 0000000..eb4427d
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * ARM dummy L210, L220, PL310 cache controller.
+ *
+ * Copyright (c) 2010-2012 Calxeda
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or any later version, as published by the Free Software
+ * Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "hw/sysbus.h"
+
+/* L2C-310 r3p2 */
+#define CACHE_ID 0x410000c8
+
+typedef struct l2x0_state {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    uint32_t cache_type;
+    uint32_t ctrl;
+    uint32_t aux_ctrl;
+    uint32_t data_ctrl;
+    uint32_t tag_ctrl;
+    uint32_t filter_start;
+    uint32_t filter_end;
+} l2x0_state;
+
+static const VMStateDescription vmstate_l2x0 = {
+    .name = "l2x0",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(ctrl, l2x0_state),
+        VMSTATE_UINT32(aux_ctrl, l2x0_state),
+        VMSTATE_UINT32(data_ctrl, l2x0_state),
+        VMSTATE_UINT32(tag_ctrl, l2x0_state),
+        VMSTATE_UINT32(filter_start, l2x0_state),
+        VMSTATE_UINT32(filter_end, l2x0_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+
+static uint64_t l2x0_priv_read(void *opaque, hwaddr offset,
+                               unsigned size)
+{
+    uint32_t cache_data;
+    l2x0_state *s = (l2x0_state *)opaque;
+    offset &= 0xfff;
+    if (offset >= 0x730 && offset < 0x800) {
+        return 0; /* cache ops complete */
+    }
+    switch (offset) {
+    case 0:
+        return CACHE_ID;
+    case 0x4:
+        /* aux_ctrl values affect cache_type values */
+        cache_data = (s->aux_ctrl & (7 << 17)) >> 15;
+        cache_data |= (s->aux_ctrl & (1 << 16)) >> 16;
+        return s->cache_type |= (cache_data << 18) | (cache_data << 6);
+    case 0x100:
+        return s->ctrl;
+    case 0x104:
+        return s->aux_ctrl;
+    case 0x108:
+        return s->tag_ctrl;
+    case 0x10C:
+        return s->data_ctrl;
+    case 0xC00:
+        return s->filter_start;
+    case 0xC04:
+        return s->filter_end;
+    case 0xF40:
+        return 0;
+    case 0xF60:
+        return 0;
+    case 0xF80:
+        return 0;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "l2x0_priv_read: Bad offset %x\n", (int)offset);
+        break;
+    }
+    return 0;
+}
+
+static void l2x0_priv_write(void *opaque, hwaddr offset,
+                            uint64_t value, unsigned size)
+{
+    l2x0_state *s = (l2x0_state *)opaque;
+    offset &= 0xfff;
+    if (offset >= 0x730 && offset < 0x800) {
+        /* ignore */
+        return;
+    }
+    switch (offset) {
+    case 0x100:
+        s->ctrl = value & 1;
+        break;
+    case 0x104:
+        s->aux_ctrl = value;
+        break;
+    case 0x108:
+        s->tag_ctrl = value;
+        break;
+    case 0x10C:
+        s->data_ctrl = value;
+        break;
+    case 0xC00:
+        s->filter_start = value;
+        break;
+    case 0xC04:
+        s->filter_end = value;
+        break;
+    case 0xF40:
+        return;
+    case 0xF60:
+        return;
+    case 0xF80:
+        return;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "l2x0_priv_write: Bad offset %x\n", (int)offset);
+        break;
+    }
+}
+
+static void l2x0_priv_reset(DeviceState *dev)
+{
+    l2x0_state *s = DO_UPCAST(l2x0_state, busdev.qdev, dev);
+
+    s->ctrl = 0;
+    s->aux_ctrl = 0x02020000;
+    s->tag_ctrl = 0;
+    s->data_ctrl = 0;
+    s->filter_start = 0;
+    s->filter_end = 0;
+}
+
+static const MemoryRegionOps l2x0_mem_ops = {
+    .read = l2x0_priv_read,
+    .write = l2x0_priv_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+ };
+
+static int l2x0_priv_init(SysBusDevice *dev)
+{
+    l2x0_state *s = FROM_SYSBUS(l2x0_state, dev);
+
+    memory_region_init_io(&s->iomem, &l2x0_mem_ops, s, "l2x0_cc", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    return 0;
+}
+
+static Property l2x0_properties[] = {
+    DEFINE_PROP_UINT32("cache-type", l2x0_state, cache_type, 0x1c100100),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void l2x0_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    k->init = l2x0_priv_init;
+    dc->vmsd = &vmstate_l2x0;
+    dc->no_user = 1;
+    dc->props = l2x0_properties;
+    dc->reset = l2x0_priv_reset;
+}
+
+static const TypeInfo l2x0_info = {
+    .name = "l2x0",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(l2x0_state),
+    .class_init = l2x0_class_init,
+};
+
+static void l2x0_register_types(void)
+{
+    type_register_static(&l2x0_info);
+}
+
+type_init(l2x0_register_types)
diff --git a/hw/misc/macio/Makefile.objs b/hw/misc/macio/Makefile.objs
new file mode 100644 (file)
index 0000000..ef7ac24
--- /dev/null
@@ -0,0 +1,3 @@
+common-obj-y += macio.o
+common-obj-$(CONFIG_CUDA) += cuda.o
+common-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o
diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c
new file mode 100644 (file)
index 0000000..f797796
--- /dev/null
@@ -0,0 +1,740 @@
+/*
+ * QEMU PowerMac CUDA device support
+ *
+ * Copyright (c) 2004-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/ppc/mac.h"
+#include "hw/input/adb.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+
+/* XXX: implement all timer modes */
+
+/* debug CUDA */
+//#define DEBUG_CUDA
+
+/* debug CUDA packets */
+//#define DEBUG_CUDA_PACKET
+
+#ifdef DEBUG_CUDA
+#define CUDA_DPRINTF(fmt, ...)                                  \
+    do { printf("CUDA: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define CUDA_DPRINTF(fmt, ...)
+#endif
+
+/* Bits in B data register: all active low */
+#define TREQ           0x08            /* Transfer request (input) */
+#define TACK           0x10            /* Transfer acknowledge (output) */
+#define TIP            0x20            /* Transfer in progress (output) */
+
+/* Bits in ACR */
+#define SR_CTRL                0x1c            /* Shift register control bits */
+#define SR_EXT         0x0c            /* Shift on external clock */
+#define SR_OUT         0x10            /* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET                0x80            /* set bits in IER */
+#define IER_CLR                0               /* clear bits in IER */
+#define SR_INT         0x04            /* Shift register full/empty */
+#define T1_INT          0x40            /* Timer 1 interrupt */
+#define T2_INT          0x20            /* Timer 2 interrupt */
+
+/* Bits in ACR */
+#define T1MODE          0xc0            /* Timer 1 mode */
+#define T1MODE_CONT     0x40            /*  continuous interrupts */
+
+/* commands (1st byte) */
+#define ADB_PACKET     0
+#define CUDA_PACKET    1
+#define ERROR_PACKET   2
+#define TIMER_PACKET   3
+#define POWER_PACKET   4
+#define MACIIC_PACKET  5
+#define PMU_PACKET     6
+
+
+/* CUDA commands (2nd byte) */
+#define CUDA_WARM_START                        0x0
+#define CUDA_AUTOPOLL                  0x1
+#define CUDA_GET_6805_ADDR             0x2
+#define CUDA_GET_TIME                  0x3
+#define CUDA_GET_PRAM                  0x7
+#define CUDA_SET_6805_ADDR             0x8
+#define CUDA_SET_TIME                  0x9
+#define CUDA_POWERDOWN                 0xa
+#define CUDA_POWERUP_TIME              0xb
+#define CUDA_SET_PRAM                  0xc
+#define CUDA_MS_RESET                  0xd
+#define CUDA_SEND_DFAC                 0xe
+#define CUDA_BATTERY_SWAP_SENSE                0x10
+#define CUDA_RESET_SYSTEM              0x11
+#define CUDA_SET_IPL                   0x12
+#define CUDA_FILE_SERVER_FLAG          0x13
+#define CUDA_SET_AUTO_RATE             0x14
+#define CUDA_GET_AUTO_RATE             0x16
+#define CUDA_SET_DEVICE_LIST           0x19
+#define CUDA_GET_DEVICE_LIST           0x1a
+#define CUDA_SET_ONE_SECOND_MODE       0x1b
+#define CUDA_SET_POWER_MESSAGES                0x21
+#define CUDA_GET_SET_IIC               0x22
+#define CUDA_WAKEUP                    0x23
+#define CUDA_TIMER_TICKLE              0x24
+#define CUDA_COMBINED_FORMAT_IIC       0x25
+
+#define CUDA_TIMER_FREQ (4700000 / 6)
+#define CUDA_ADB_POLL_FREQ 50
+
+/* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */
+#define RTC_OFFSET                      2082844800
+
+static void cuda_update(CUDAState *s);
+static void cuda_receive_packet_from_host(CUDAState *s,
+                                          const uint8_t *data, int len);
+static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
+                              int64_t current_time);
+
+static void cuda_update_irq(CUDAState *s)
+{
+    if (s->ifr & s->ier & (SR_INT | T1_INT)) {
+        qemu_irq_raise(s->irq);
+    } else {
+        qemu_irq_lower(s->irq);
+    }
+}
+
+static unsigned int get_counter(CUDATimer *s)
+{
+    int64_t d;
+    unsigned int counter;
+
+    d = muldiv64(qemu_get_clock_ns(vm_clock) - s->load_time,
+                 CUDA_TIMER_FREQ, get_ticks_per_sec());
+    if (s->index == 0) {
+        /* the timer goes down from latch to -1 (period of latch + 2) */
+        if (d <= (s->counter_value + 1)) {
+            counter = (s->counter_value - d) & 0xffff;
+        } else {
+            counter = (d - (s->counter_value + 1)) % (s->latch + 2);
+            counter = (s->latch - counter) & 0xffff;
+        }
+    } else {
+        counter = (s->counter_value - d) & 0xffff;
+    }
+    return counter;
+}
+
+static void set_counter(CUDAState *s, CUDATimer *ti, unsigned int val)
+{
+    CUDA_DPRINTF("T%d.counter=%d\n", 1 + (ti->timer == NULL), val);
+    ti->load_time = qemu_get_clock_ns(vm_clock);
+    ti->counter_value = val;
+    cuda_timer_update(s, ti, ti->load_time);
+}
+
+static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time)
+{
+    int64_t d, next_time;
+    unsigned int counter;
+
+    /* current counter value */
+    d = muldiv64(current_time - s->load_time,
+                 CUDA_TIMER_FREQ, get_ticks_per_sec());
+    /* the timer goes down from latch to -1 (period of latch + 2) */
+    if (d <= (s->counter_value + 1)) {
+        counter = (s->counter_value - d) & 0xffff;
+    } else {
+        counter = (d - (s->counter_value + 1)) % (s->latch + 2);
+        counter = (s->latch - counter) & 0xffff;
+    }
+
+    /* Note: we consider the irq is raised on 0 */
+    if (counter == 0xffff) {
+        next_time = d + s->latch + 1;
+    } else if (counter == 0) {
+        next_time = d + s->latch + 2;
+    } else {
+        next_time = d + counter;
+    }
+    CUDA_DPRINTF("latch=%d counter=%" PRId64 " delta_next=%" PRId64 "\n",
+                 s->latch, d, next_time - d);
+    next_time = muldiv64(next_time, get_ticks_per_sec(), CUDA_TIMER_FREQ) +
+        s->load_time;
+    if (next_time <= current_time)
+        next_time = current_time + 1;
+    return next_time;
+}
+
+static void cuda_timer_update(CUDAState *s, CUDATimer *ti,
+                              int64_t current_time)
+{
+    if (!ti->timer)
+        return;
+    if ((s->acr & T1MODE) != T1MODE_CONT) {
+        qemu_del_timer(ti->timer);
+    } else {
+        ti->next_irq_time = get_next_irq_time(ti, current_time);
+        qemu_mod_timer(ti->timer, ti->next_irq_time);
+    }
+}
+
+static void cuda_timer1(void *opaque)
+{
+    CUDAState *s = opaque;
+    CUDATimer *ti = &s->timers[0];
+
+    cuda_timer_update(s, ti, ti->next_irq_time);
+    s->ifr |= T1_INT;
+    cuda_update_irq(s);
+}
+
+static uint32_t cuda_readb(void *opaque, hwaddr addr)
+{
+    CUDAState *s = opaque;
+    uint32_t val;
+
+    addr = (addr >> 9) & 0xf;
+    switch(addr) {
+    case 0:
+        val = s->b;
+        break;
+    case 1:
+        val = s->a;
+        break;
+    case 2:
+        val = s->dirb;
+        break;
+    case 3:
+        val = s->dira;
+        break;
+    case 4:
+        val = get_counter(&s->timers[0]) & 0xff;
+        s->ifr &= ~T1_INT;
+        cuda_update_irq(s);
+        break;
+    case 5:
+        val = get_counter(&s->timers[0]) >> 8;
+        cuda_update_irq(s);
+        break;
+    case 6:
+        val = s->timers[0].latch & 0xff;
+        break;
+    case 7:
+        /* XXX: check this */
+        val = (s->timers[0].latch >> 8) & 0xff;
+        break;
+    case 8:
+        val = get_counter(&s->timers[1]) & 0xff;
+        s->ifr &= ~T2_INT;
+        break;
+    case 9:
+        val = get_counter(&s->timers[1]) >> 8;
+        break;
+    case 10:
+        val = s->sr;
+        s->ifr &= ~SR_INT;
+        cuda_update_irq(s);
+        break;
+    case 11:
+        val = s->acr;
+        break;
+    case 12:
+        val = s->pcr;
+        break;
+    case 13:
+        val = s->ifr;
+        if (s->ifr & s->ier)
+            val |= 0x80;
+        break;
+    case 14:
+        val = s->ier | 0x80;
+        break;
+    default:
+    case 15:
+        val = s->anh;
+        break;
+    }
+    if (addr != 13 || val != 0) {
+        CUDA_DPRINTF("read: reg=0x%x val=%02x\n", (int)addr, val);
+    }
+
+    return val;
+}
+
+static void cuda_writeb(void *opaque, hwaddr addr, uint32_t val)
+{
+    CUDAState *s = opaque;
+
+    addr = (addr >> 9) & 0xf;
+    CUDA_DPRINTF("write: reg=0x%x val=%02x\n", (int)addr, val);
+
+    switch(addr) {
+    case 0:
+        s->b = val;
+        cuda_update(s);
+        break;
+    case 1:
+        s->a = val;
+        break;
+    case 2:
+        s->dirb = val;
+        break;
+    case 3:
+        s->dira = val;
+        break;
+    case 4:
+        s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
+        cuda_timer_update(s, &s->timers[0], qemu_get_clock_ns(vm_clock));
+        break;
+    case 5:
+        s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
+        s->ifr &= ~T1_INT;
+        set_counter(s, &s->timers[0], s->timers[0].latch);
+        break;
+    case 6:
+        s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
+        cuda_timer_update(s, &s->timers[0], qemu_get_clock_ns(vm_clock));
+        break;
+    case 7:
+        s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
+        s->ifr &= ~T1_INT;
+        cuda_timer_update(s, &s->timers[0], qemu_get_clock_ns(vm_clock));
+        break;
+    case 8:
+        s->timers[1].latch = val;
+        set_counter(s, &s->timers[1], val);
+        break;
+    case 9:
+        set_counter(s, &s->timers[1], (val << 8) | s->timers[1].latch);
+        break;
+    case 10:
+        s->sr = val;
+        break;
+    case 11:
+        s->acr = val;
+        cuda_timer_update(s, &s->timers[0], qemu_get_clock_ns(vm_clock));
+        cuda_update(s);
+        break;
+    case 12:
+        s->pcr = val;
+        break;
+    case 13:
+        /* reset bits */
+        s->ifr &= ~val;
+        cuda_update_irq(s);
+        break;
+    case 14:
+        if (val & IER_SET) {
+            /* set bits */
+            s->ier |= val & 0x7f;
+        } else {
+            /* reset bits */
+            s->ier &= ~val;
+        }
+        cuda_update_irq(s);
+        break;
+    default:
+    case 15:
+        s->anh = val;
+        break;
+    }
+}
+
+/* NOTE: TIP and TREQ are negated */
+static void cuda_update(CUDAState *s)
+{
+    int packet_received, len;
+
+    packet_received = 0;
+    if (!(s->b & TIP)) {
+        /* transfer requested from host */
+
+        if (s->acr & SR_OUT) {
+            /* data output */
+            if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
+                if (s->data_out_index < sizeof(s->data_out)) {
+                    CUDA_DPRINTF("send: %02x\n", s->sr);
+                    s->data_out[s->data_out_index++] = s->sr;
+                    s->ifr |= SR_INT;
+                    cuda_update_irq(s);
+                }
+            }
+        } else {
+            if (s->data_in_index < s->data_in_size) {
+                /* data input */
+                if ((s->b & (TACK | TIP)) != (s->last_b & (TACK | TIP))) {
+                    s->sr = s->data_in[s->data_in_index++];
+                    CUDA_DPRINTF("recv: %02x\n", s->sr);
+                    /* indicate end of transfer */
+                    if (s->data_in_index >= s->data_in_size) {
+                        s->b = (s->b | TREQ);
+                    }
+                    s->ifr |= SR_INT;
+                    cuda_update_irq(s);
+                }
+            }
+        }
+    } else {
+        /* no transfer requested: handle sync case */
+        if ((s->last_b & TIP) && (s->b & TACK) != (s->last_b & TACK)) {
+            /* update TREQ state each time TACK change state */
+            if (s->b & TACK)
+                s->b = (s->b | TREQ);
+            else
+                s->b = (s->b & ~TREQ);
+            s->ifr |= SR_INT;
+            cuda_update_irq(s);
+        } else {
+            if (!(s->last_b & TIP)) {
+                /* handle end of host to cuda transfer */
+                packet_received = (s->data_out_index > 0);
+                /* always an IRQ at the end of transfer */
+                s->ifr |= SR_INT;
+                cuda_update_irq(s);
+            }
+            /* signal if there is data to read */
+            if (s->data_in_index < s->data_in_size) {
+                s->b = (s->b & ~TREQ);
+            }
+        }
+    }
+
+    s->last_acr = s->acr;
+    s->last_b = s->b;
+
+    /* NOTE: cuda_receive_packet_from_host() can call cuda_update()
+       recursively */
+    if (packet_received) {
+        len = s->data_out_index;
+        s->data_out_index = 0;
+        cuda_receive_packet_from_host(s, s->data_out, len);
+    }
+}
+
+static void cuda_send_packet_to_host(CUDAState *s,
+                                     const uint8_t *data, int len)
+{
+#ifdef DEBUG_CUDA_PACKET
+    {
+        int i;
+        printf("cuda_send_packet_to_host:\n");
+        for(i = 0; i < len; i++)
+            printf(" %02x", data[i]);
+        printf("\n");
+    }
+#endif
+    memcpy(s->data_in, data, len);
+    s->data_in_size = len;
+    s->data_in_index = 0;
+    cuda_update(s);
+    s->ifr |= SR_INT;
+    cuda_update_irq(s);
+}
+
+static void cuda_adb_poll(void *opaque)
+{
+    CUDAState *s = opaque;
+    uint8_t obuf[ADB_MAX_OUT_LEN + 2];
+    int olen;
+
+    olen = adb_poll(&s->adb_bus, obuf + 2);
+    if (olen > 0) {
+        obuf[0] = ADB_PACKET;
+        obuf[1] = 0x40; /* polled data */
+        cuda_send_packet_to_host(s, obuf, olen + 2);
+    }
+    qemu_mod_timer(s->adb_poll_timer,
+                   qemu_get_clock_ns(vm_clock) +
+                   (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ));
+}
+
+static void cuda_receive_packet(CUDAState *s,
+                                const uint8_t *data, int len)
+{
+    uint8_t obuf[16];
+    int autopoll;
+    uint32_t ti;
+
+    switch(data[0]) {
+    case CUDA_AUTOPOLL:
+        autopoll = (data[1] != 0);
+        if (autopoll != s->autopoll) {
+            s->autopoll = autopoll;
+            if (autopoll) {
+                qemu_mod_timer(s->adb_poll_timer,
+                               qemu_get_clock_ns(vm_clock) +
+                               (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ));
+            } else {
+                qemu_del_timer(s->adb_poll_timer);
+            }
+        }
+        obuf[0] = CUDA_PACKET;
+        obuf[1] = data[1];
+        cuda_send_packet_to_host(s, obuf, 2);
+        break;
+    case CUDA_SET_TIME:
+        ti = (((uint32_t)data[1]) << 24) + (((uint32_t)data[2]) << 16) + (((uint32_t)data[3]) << 8) + data[4];
+        s->tick_offset = ti - (qemu_get_clock_ns(vm_clock) / get_ticks_per_sec());
+        obuf[0] = CUDA_PACKET;
+        obuf[1] = 0;
+        obuf[2] = 0;
+        cuda_send_packet_to_host(s, obuf, 3);
+        break;
+    case CUDA_GET_TIME:
+        ti = s->tick_offset + (qemu_get_clock_ns(vm_clock) / get_ticks_per_sec());
+        obuf[0] = CUDA_PACKET;
+        obuf[1] = 0;
+        obuf[2] = 0;
+        obuf[3] = ti >> 24;
+        obuf[4] = ti >> 16;
+        obuf[5] = ti >> 8;
+        obuf[6] = ti;
+        cuda_send_packet_to_host(s, obuf, 7);
+        break;
+    case CUDA_FILE_SERVER_FLAG:
+    case CUDA_SET_DEVICE_LIST:
+    case CUDA_SET_AUTO_RATE:
+    case CUDA_SET_POWER_MESSAGES:
+        obuf[0] = CUDA_PACKET;
+        obuf[1] = 0;
+        cuda_send_packet_to_host(s, obuf, 2);
+        break;
+    case CUDA_POWERDOWN:
+        obuf[0] = CUDA_PACKET;
+        obuf[1] = 0;
+        cuda_send_packet_to_host(s, obuf, 2);
+        qemu_system_shutdown_request();
+        break;
+    case CUDA_RESET_SYSTEM:
+        obuf[0] = CUDA_PACKET;
+        obuf[1] = 0;
+        cuda_send_packet_to_host(s, obuf, 2);
+        qemu_system_reset_request();
+        break;
+    default:
+        break;
+    }
+}
+
+static void cuda_receive_packet_from_host(CUDAState *s,
+                                          const uint8_t *data, int len)
+{
+#ifdef DEBUG_CUDA_PACKET
+    {
+        int i;
+        printf("cuda_receive_packet_from_host:\n");
+        for(i = 0; i < len; i++)
+            printf(" %02x", data[i]);
+        printf("\n");
+    }
+#endif
+    switch(data[0]) {
+    case ADB_PACKET:
+        {
+            uint8_t obuf[ADB_MAX_OUT_LEN + 2];
+            int olen;
+            olen = adb_request(&s->adb_bus, obuf + 2, data + 1, len - 1);
+            if (olen > 0) {
+                obuf[0] = ADB_PACKET;
+                obuf[1] = 0x00;
+            } else {
+                /* error */
+                obuf[0] = ADB_PACKET;
+                obuf[1] = -olen;
+                olen = 0;
+            }
+            cuda_send_packet_to_host(s, obuf, olen + 2);
+        }
+        break;
+    case CUDA_PACKET:
+        cuda_receive_packet(s, data + 1, len - 1);
+        break;
+    }
+}
+
+static void cuda_writew (void *opaque, hwaddr addr, uint32_t value)
+{
+}
+
+static void cuda_writel (void *opaque, hwaddr addr, uint32_t value)
+{
+}
+
+static uint32_t cuda_readw (void *opaque, hwaddr addr)
+{
+    return 0;
+}
+
+static uint32_t cuda_readl (void *opaque, hwaddr addr)
+{
+    return 0;
+}
+
+static const MemoryRegionOps cuda_ops = {
+    .old_mmio = {
+        .write = {
+            cuda_writeb,
+            cuda_writew,
+            cuda_writel,
+        },
+        .read = {
+            cuda_readb,
+            cuda_readw,
+            cuda_readl,
+        },
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static bool cuda_timer_exist(void *opaque, int version_id)
+{
+    CUDATimer *s = opaque;
+
+    return s->timer != NULL;
+}
+
+static const VMStateDescription vmstate_cuda_timer = {
+    .name = "cuda_timer",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT16(latch, CUDATimer),
+        VMSTATE_UINT16(counter_value, CUDATimer),
+        VMSTATE_INT64(load_time, CUDATimer),
+        VMSTATE_INT64(next_irq_time, CUDATimer),
+        VMSTATE_TIMER_TEST(timer, CUDATimer, cuda_timer_exist),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_cuda = {
+    .name = "cuda",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(a, CUDAState),
+        VMSTATE_UINT8(b, CUDAState),
+        VMSTATE_UINT8(dira, CUDAState),
+        VMSTATE_UINT8(dirb, CUDAState),
+        VMSTATE_UINT8(sr, CUDAState),
+        VMSTATE_UINT8(acr, CUDAState),
+        VMSTATE_UINT8(pcr, CUDAState),
+        VMSTATE_UINT8(ifr, CUDAState),
+        VMSTATE_UINT8(ier, CUDAState),
+        VMSTATE_UINT8(anh, CUDAState),
+        VMSTATE_INT32(data_in_size, CUDAState),
+        VMSTATE_INT32(data_in_index, CUDAState),
+        VMSTATE_INT32(data_out_index, CUDAState),
+        VMSTATE_UINT8(autopoll, CUDAState),
+        VMSTATE_BUFFER(data_in, CUDAState),
+        VMSTATE_BUFFER(data_out, CUDAState),
+        VMSTATE_UINT32(tick_offset, CUDAState),
+        VMSTATE_STRUCT_ARRAY(timers, CUDAState, 2, 1,
+                             vmstate_cuda_timer, CUDATimer),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void cuda_reset(DeviceState *dev)
+{
+    CUDAState *s = CUDA(dev);
+
+    s->b = 0;
+    s->a = 0;
+    s->dirb = 0;
+    s->dira = 0;
+    s->sr = 0;
+    s->acr = 0;
+    s->pcr = 0;
+    s->ifr = 0;
+    s->ier = 0;
+    //    s->ier = T1_INT | SR_INT;
+    s->anh = 0;
+    s->data_in_size = 0;
+    s->data_in_index = 0;
+    s->data_out_index = 0;
+    s->autopoll = 0;
+
+    s->timers[0].latch = 0xffff;
+    set_counter(s, &s->timers[0], 0xffff);
+
+    s->timers[1].latch = 0;
+    set_counter(s, &s->timers[1], 0xffff);
+}
+
+static void cuda_realizefn(DeviceState *dev, Error **errp)
+{
+    CUDAState *s = CUDA(dev);
+    struct tm tm;
+
+    s->timers[0].timer = qemu_new_timer_ns(vm_clock, cuda_timer1, s);
+
+    qemu_get_timedate(&tm, 0);
+    s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET;
+
+    s->adb_poll_timer = qemu_new_timer_ns(vm_clock, cuda_adb_poll, s);
+}
+
+static void cuda_initfn(Object *obj)
+{
+    SysBusDevice *d = SYS_BUS_DEVICE(obj);
+    CUDAState *s = CUDA(obj);
+    int i;
+
+    memory_region_init_io(&s->mem, &cuda_ops, s, "cuda", 0x2000);
+    sysbus_init_mmio(d, &s->mem);
+    sysbus_init_irq(d, &s->irq);
+
+    for (i = 0; i < ARRAY_SIZE(s->timers); i++) {
+        s->timers[i].index = i;
+    }
+
+    qbus_create_inplace((BusState *)&s->adb_bus, TYPE_ADB_BUS, DEVICE(obj),
+                        "adb.0");
+}
+
+static void cuda_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->realize = cuda_realizefn;
+    dc->reset = cuda_reset;
+    dc->vmsd = &vmstate_cuda;
+}
+
+static const TypeInfo cuda_type_info = {
+    .name = TYPE_CUDA,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(CUDAState),
+    .instance_init = cuda_initfn,
+    .class_init = cuda_class_init,
+};
+
+static void cuda_register_types(void)
+{
+    type_register_static(&cuda_type_info);
+}
+
+type_init(cuda_register_types)
diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c
new file mode 100644 (file)
index 0000000..a2363bb
--- /dev/null
@@ -0,0 +1,859 @@
+/*
+ * PowerMac descriptor-based DMA emulation
+ *
+ * Copyright (c) 2005-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ * Copyright (c) 2009 Laurent Vivier
+ *
+ * some parts from linux-2.6.28, arch/powerpc/include/asm/dbdma.h
+ *
+ *   Definitions for using the Apple Descriptor-Based DMA controller
+ *   in Power Macintosh computers.
+ *
+ *   Copyright (C) 1996 Paul Mackerras.
+ *
+ * some parts from mol 0.9.71
+ *
+ *   Descriptor based DMA emulation
+ *
+ *   Copyright (C) 1998-2004 Samuel Rydh (samuel@ibrium.se)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/isa/isa.h"
+#include "hw/ppc/mac_dbdma.h"
+#include "qemu/main-loop.h"
+
+/* debug DBDMA */
+//#define DEBUG_DBDMA
+
+#ifdef DEBUG_DBDMA
+#define DBDMA_DPRINTF(fmt, ...)                                 \
+    do { printf("DBDMA: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DBDMA_DPRINTF(fmt, ...)
+#endif
+
+/*
+ */
+
+/*
+ * DBDMA control/status registers.  All little-endian.
+ */
+
+#define DBDMA_CONTROL         0x00
+#define DBDMA_STATUS          0x01
+#define DBDMA_CMDPTR_HI       0x02
+#define DBDMA_CMDPTR_LO       0x03
+#define DBDMA_INTR_SEL        0x04
+#define DBDMA_BRANCH_SEL      0x05
+#define DBDMA_WAIT_SEL        0x06
+#define DBDMA_XFER_MODE       0x07
+#define DBDMA_DATA2PTR_HI     0x08
+#define DBDMA_DATA2PTR_LO     0x09
+#define DBDMA_RES1            0x0A
+#define DBDMA_ADDRESS_HI      0x0B
+#define DBDMA_BRANCH_ADDR_HI  0x0C
+#define DBDMA_RES2            0x0D
+#define DBDMA_RES3            0x0E
+#define DBDMA_RES4            0x0F
+
+#define DBDMA_REGS            16
+#define DBDMA_SIZE            (DBDMA_REGS * sizeof(uint32_t))
+
+#define DBDMA_CHANNEL_SHIFT   7
+#define DBDMA_CHANNEL_SIZE    (1 << DBDMA_CHANNEL_SHIFT)
+
+#define DBDMA_CHANNELS        (0x1000 >> DBDMA_CHANNEL_SHIFT)
+
+/* Bits in control and status registers */
+
+#define RUN    0x8000
+#define PAUSE  0x4000
+#define FLUSH  0x2000
+#define WAKE   0x1000
+#define DEAD   0x0800
+#define ACTIVE 0x0400
+#define BT     0x0100
+#define DEVSTAT        0x00ff
+
+/*
+ * DBDMA command structure.  These fields are all little-endian!
+ */
+
+typedef struct dbdma_cmd {
+    uint16_t req_count;          /* requested byte transfer count */
+    uint16_t command;    /* command word (has bit-fields) */
+    uint32_t phy_addr;   /* physical data address */
+    uint32_t cmd_dep;    /* command-dependent field */
+    uint16_t res_count;          /* residual count after completion */
+    uint16_t xfer_status; /* transfer status */
+} dbdma_cmd;
+
+/* DBDMA command values in command field */
+
+#define COMMAND_MASK    0xf000
+#define OUTPUT_MORE    0x0000  /* transfer memory data to stream */
+#define OUTPUT_LAST    0x1000  /* ditto followed by end marker */
+#define INPUT_MORE     0x2000  /* transfer stream data to memory */
+#define INPUT_LAST     0x3000  /* ditto, expect end marker */
+#define STORE_WORD     0x4000  /* write word (4 bytes) to device reg */
+#define LOAD_WORD      0x5000  /* read word (4 bytes) from device reg */
+#define DBDMA_NOP      0x6000  /* do nothing */
+#define DBDMA_STOP     0x7000  /* suspend processing */
+
+/* Key values in command field */
+
+#define KEY_MASK        0x0700
+#define KEY_STREAM0    0x0000  /* usual data stream */
+#define KEY_STREAM1    0x0100  /* control/status stream */
+#define KEY_STREAM2    0x0200  /* device-dependent stream */
+#define KEY_STREAM3    0x0300  /* device-dependent stream */
+#define KEY_STREAM4    0x0400  /* reserved */
+#define KEY_REGS       0x0500  /* device register space */
+#define KEY_SYSTEM     0x0600  /* system memory-mapped space */
+#define KEY_DEVICE     0x0700  /* device memory-mapped space */
+
+/* Interrupt control values in command field */
+
+#define INTR_MASK       0x0030
+#define INTR_NEVER     0x0000  /* don't interrupt */
+#define INTR_IFSET     0x0010  /* intr if condition bit is 1 */
+#define INTR_IFCLR     0x0020  /* intr if condition bit is 0 */
+#define INTR_ALWAYS    0x0030  /* always interrupt */
+
+/* Branch control values in command field */
+
+#define BR_MASK         0x000c
+#define BR_NEVER       0x0000  /* don't branch */
+#define BR_IFSET       0x0004  /* branch if condition bit is 1 */
+#define BR_IFCLR       0x0008  /* branch if condition bit is 0 */
+#define BR_ALWAYS      0x000c  /* always branch */
+
+/* Wait control values in command field */
+
+#define WAIT_MASK       0x0003
+#define WAIT_NEVER     0x0000  /* don't wait */
+#define WAIT_IFSET     0x0001  /* wait if condition bit is 1 */
+#define WAIT_IFCLR     0x0002  /* wait if condition bit is 0 */
+#define WAIT_ALWAYS    0x0003  /* always wait */
+
+typedef struct DBDMA_channel {
+    int channel;
+    uint32_t regs[DBDMA_REGS];
+    qemu_irq irq;
+    DBDMA_io io;
+    DBDMA_rw rw;
+    DBDMA_flush flush;
+    dbdma_cmd current;
+    int processing;
+} DBDMA_channel;
+
+typedef struct {
+    MemoryRegion mem;
+    DBDMA_channel channels[DBDMA_CHANNELS];
+} DBDMAState;
+
+#ifdef DEBUG_DBDMA
+static void dump_dbdma_cmd(dbdma_cmd *cmd)
+{
+    printf("dbdma_cmd %p\n", cmd);
+    printf("    req_count 0x%04x\n", le16_to_cpu(cmd->req_count));
+    printf("    command 0x%04x\n", le16_to_cpu(cmd->command));
+    printf("    phy_addr 0x%08x\n", le32_to_cpu(cmd->phy_addr));
+    printf("    cmd_dep 0x%08x\n", le32_to_cpu(cmd->cmd_dep));
+    printf("    res_count 0x%04x\n", le16_to_cpu(cmd->res_count));
+    printf("    xfer_status 0x%04x\n", le16_to_cpu(cmd->xfer_status));
+}
+#else
+static void dump_dbdma_cmd(dbdma_cmd *cmd)
+{
+}
+#endif
+static void dbdma_cmdptr_load(DBDMA_channel *ch)
+{
+    DBDMA_DPRINTF("dbdma_cmdptr_load 0x%08x\n",
+                  ch->regs[DBDMA_CMDPTR_LO]);
+    cpu_physical_memory_read(ch->regs[DBDMA_CMDPTR_LO],
+                             (uint8_t*)&ch->current, sizeof(dbdma_cmd));
+}
+
+static void dbdma_cmdptr_save(DBDMA_channel *ch)
+{
+    DBDMA_DPRINTF("dbdma_cmdptr_save 0x%08x\n",
+                  ch->regs[DBDMA_CMDPTR_LO]);
+    DBDMA_DPRINTF("xfer_status 0x%08x res_count 0x%04x\n",
+                  le16_to_cpu(ch->current.xfer_status),
+                  le16_to_cpu(ch->current.res_count));
+    cpu_physical_memory_write(ch->regs[DBDMA_CMDPTR_LO],
+                              (uint8_t*)&ch->current, sizeof(dbdma_cmd));
+}
+
+static void kill_channel(DBDMA_channel *ch)
+{
+    DBDMA_DPRINTF("kill_channel\n");
+
+    ch->regs[DBDMA_STATUS] |= DEAD;
+    ch->regs[DBDMA_STATUS] &= ~ACTIVE;
+
+    qemu_irq_raise(ch->irq);
+}
+
+static void conditional_interrupt(DBDMA_channel *ch)
+{
+    dbdma_cmd *current = &ch->current;
+    uint16_t intr;
+    uint16_t sel_mask, sel_value;
+    uint32_t status;
+    int cond;
+
+    DBDMA_DPRINTF("conditional_interrupt\n");
+
+    intr = le16_to_cpu(current->command) & INTR_MASK;
+
+    switch(intr) {
+    case INTR_NEVER:  /* don't interrupt */
+        return;
+    case INTR_ALWAYS: /* always interrupt */
+        qemu_irq_raise(ch->irq);
+        return;
+    }
+
+    status = ch->regs[DBDMA_STATUS] & DEVSTAT;
+
+    sel_mask = (ch->regs[DBDMA_INTR_SEL] >> 16) & 0x0f;
+    sel_value = ch->regs[DBDMA_INTR_SEL] & 0x0f;
+
+    cond = (status & sel_mask) == (sel_value & sel_mask);
+
+    switch(intr) {
+    case INTR_IFSET:  /* intr if condition bit is 1 */
+        if (cond)
+            qemu_irq_raise(ch->irq);
+        return;
+    case INTR_IFCLR:  /* intr if condition bit is 0 */
+        if (!cond)
+            qemu_irq_raise(ch->irq);
+        return;
+    }
+}
+
+static int conditional_wait(DBDMA_channel *ch)
+{
+    dbdma_cmd *current = &ch->current;
+    uint16_t wait;
+    uint16_t sel_mask, sel_value;
+    uint32_t status;
+    int cond;
+
+    DBDMA_DPRINTF("conditional_wait\n");
+
+    wait = le16_to_cpu(current->command) & WAIT_MASK;
+
+    switch(wait) {
+    case WAIT_NEVER:  /* don't wait */
+        return 0;
+    case WAIT_ALWAYS: /* always wait */
+        return 1;
+    }
+
+    status = ch->regs[DBDMA_STATUS] & DEVSTAT;
+
+    sel_mask = (ch->regs[DBDMA_WAIT_SEL] >> 16) & 0x0f;
+    sel_value = ch->regs[DBDMA_WAIT_SEL] & 0x0f;
+
+    cond = (status & sel_mask) == (sel_value & sel_mask);
+
+    switch(wait) {
+    case WAIT_IFSET:  /* wait if condition bit is 1 */
+        if (cond)
+            return 1;
+        return 0;
+    case WAIT_IFCLR:  /* wait if condition bit is 0 */
+        if (!cond)
+            return 1;
+        return 0;
+    }
+    return 0;
+}
+
+static void next(DBDMA_channel *ch)
+{
+    uint32_t cp;
+
+    ch->regs[DBDMA_STATUS] &= ~BT;
+
+    cp = ch->regs[DBDMA_CMDPTR_LO];
+    ch->regs[DBDMA_CMDPTR_LO] = cp + sizeof(dbdma_cmd);
+    dbdma_cmdptr_load(ch);
+}
+
+static void branch(DBDMA_channel *ch)
+{
+    dbdma_cmd *current = &ch->current;
+
+    ch->regs[DBDMA_CMDPTR_LO] = current->cmd_dep;
+    ch->regs[DBDMA_STATUS] |= BT;
+    dbdma_cmdptr_load(ch);
+}
+
+static void conditional_branch(DBDMA_channel *ch)
+{
+    dbdma_cmd *current = &ch->current;
+    uint16_t br;
+    uint16_t sel_mask, sel_value;
+    uint32_t status;
+    int cond;
+
+    DBDMA_DPRINTF("conditional_branch\n");
+
+    /* check if we must branch */
+
+    br = le16_to_cpu(current->command) & BR_MASK;
+
+    switch(br) {
+    case BR_NEVER:  /* don't branch */
+        next(ch);
+        return;
+    case BR_ALWAYS: /* always branch */
+        branch(ch);
+        return;
+    }
+
+    status = ch->regs[DBDMA_STATUS] & DEVSTAT;
+
+    sel_mask = (ch->regs[DBDMA_BRANCH_SEL] >> 16) & 0x0f;
+    sel_value = ch->regs[DBDMA_BRANCH_SEL] & 0x0f;
+
+    cond = (status & sel_mask) == (sel_value & sel_mask);
+
+    switch(br) {
+    case BR_IFSET:  /* branch if condition bit is 1 */
+        if (cond)
+            branch(ch);
+        else
+            next(ch);
+        return;
+    case BR_IFCLR:  /* branch if condition bit is 0 */
+        if (!cond)
+            branch(ch);
+        else
+            next(ch);
+        return;
+    }
+}
+
+static QEMUBH *dbdma_bh;
+static void channel_run(DBDMA_channel *ch);
+
+static void dbdma_end(DBDMA_io *io)
+{
+    DBDMA_channel *ch = io->channel;
+    dbdma_cmd *current = &ch->current;
+
+    if (conditional_wait(ch))
+        goto wait;
+
+    current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]);
+    current->res_count = cpu_to_le16(io->len);
+    dbdma_cmdptr_save(ch);
+    if (io->is_last)
+        ch->regs[DBDMA_STATUS] &= ~FLUSH;
+
+    conditional_interrupt(ch);
+    conditional_branch(ch);
+
+wait:
+    ch->processing = 0;
+    if ((ch->regs[DBDMA_STATUS] & RUN) &&
+        (ch->regs[DBDMA_STATUS] & ACTIVE))
+        channel_run(ch);
+}
+
+static void start_output(DBDMA_channel *ch, int key, uint32_t addr,
+                        uint16_t req_count, int is_last)
+{
+    DBDMA_DPRINTF("start_output\n");
+
+    /* KEY_REGS, KEY_DEVICE and KEY_STREAM
+     * are not implemented in the mac-io chip
+     */
+
+    DBDMA_DPRINTF("addr 0x%x key 0x%x\n", addr, key);
+    if (!addr || key > KEY_STREAM3) {
+        kill_channel(ch);
+        return;
+    }
+
+    ch->io.addr = addr;
+    ch->io.len = req_count;
+    ch->io.is_last = is_last;
+    ch->io.dma_end = dbdma_end;
+    ch->io.is_dma_out = 1;
+    ch->processing = 1;
+    if (ch->rw) {
+        ch->rw(&ch->io);
+    }
+}
+
+static void start_input(DBDMA_channel *ch, int key, uint32_t addr,
+                       uint16_t req_count, int is_last)
+{
+    DBDMA_DPRINTF("start_input\n");
+
+    /* KEY_REGS, KEY_DEVICE and KEY_STREAM
+     * are not implemented in the mac-io chip
+     */
+
+    if (!addr || key > KEY_STREAM3) {
+        kill_channel(ch);
+        return;
+    }
+
+    ch->io.addr = addr;
+    ch->io.len = req_count;
+    ch->io.is_last = is_last;
+    ch->io.dma_end = dbdma_end;
+    ch->io.is_dma_out = 0;
+    ch->processing = 1;
+    if (ch->rw) {
+        ch->rw(&ch->io);
+    }
+}
+
+static void load_word(DBDMA_channel *ch, int key, uint32_t addr,
+                     uint16_t len)
+{
+    dbdma_cmd *current = &ch->current;
+    uint32_t val;
+
+    DBDMA_DPRINTF("load_word\n");
+
+    /* only implements KEY_SYSTEM */
+
+    if (key != KEY_SYSTEM) {
+        printf("DBDMA: LOAD_WORD, unimplemented key %x\n", key);
+        kill_channel(ch);
+        return;
+    }
+
+    cpu_physical_memory_read(addr, (uint8_t*)&val, len);
+
+    if (len == 2)
+        val = (val << 16) | (current->cmd_dep & 0x0000ffff);
+    else if (len == 1)
+        val = (val << 24) | (current->cmd_dep & 0x00ffffff);
+
+    current->cmd_dep = val;
+
+    if (conditional_wait(ch))
+        goto wait;
+
+    current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]);
+    dbdma_cmdptr_save(ch);
+    ch->regs[DBDMA_STATUS] &= ~FLUSH;
+
+    conditional_interrupt(ch);
+    next(ch);
+
+wait:
+    qemu_bh_schedule(dbdma_bh);
+}
+
+static void store_word(DBDMA_channel *ch, int key, uint32_t addr,
+                      uint16_t len)
+{
+    dbdma_cmd *current = &ch->current;
+    uint32_t val;
+
+    DBDMA_DPRINTF("store_word\n");
+
+    /* only implements KEY_SYSTEM */
+
+    if (key != KEY_SYSTEM) {
+        printf("DBDMA: STORE_WORD, unimplemented key %x\n", key);
+        kill_channel(ch);
+        return;
+    }
+
+    val = current->cmd_dep;
+    if (len == 2)
+        val >>= 16;
+    else if (len == 1)
+        val >>= 24;
+
+    cpu_physical_memory_write(addr, (uint8_t*)&val, len);
+
+    if (conditional_wait(ch))
+        goto wait;
+
+    current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]);
+    dbdma_cmdptr_save(ch);
+    ch->regs[DBDMA_STATUS] &= ~FLUSH;
+
+    conditional_interrupt(ch);
+    next(ch);
+
+wait:
+    qemu_bh_schedule(dbdma_bh);
+}
+
+static void nop(DBDMA_channel *ch)
+{
+    dbdma_cmd *current = &ch->current;
+
+    if (conditional_wait(ch))
+        goto wait;
+
+    current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]);
+    dbdma_cmdptr_save(ch);
+
+    conditional_interrupt(ch);
+    conditional_branch(ch);
+
+wait:
+    qemu_bh_schedule(dbdma_bh);
+}
+
+static void stop(DBDMA_channel *ch)
+{
+    ch->regs[DBDMA_STATUS] &= ~(ACTIVE|DEAD|FLUSH);
+
+    /* the stop command does not increment command pointer */
+}
+
+static void channel_run(DBDMA_channel *ch)
+{
+    dbdma_cmd *current = &ch->current;
+    uint16_t cmd, key;
+    uint16_t req_count;
+    uint32_t phy_addr;
+
+    DBDMA_DPRINTF("channel_run\n");
+    dump_dbdma_cmd(current);
+
+    /* clear WAKE flag at command fetch */
+
+    ch->regs[DBDMA_STATUS] &= ~WAKE;
+
+    cmd = le16_to_cpu(current->command) & COMMAND_MASK;
+
+    switch (cmd) {
+    case DBDMA_NOP:
+        nop(ch);
+       return;
+
+    case DBDMA_STOP:
+        stop(ch);
+       return;
+    }
+
+    key = le16_to_cpu(current->command) & 0x0700;
+    req_count = le16_to_cpu(current->req_count);
+    phy_addr = le32_to_cpu(current->phy_addr);
+
+    if (key == KEY_STREAM4) {
+        printf("command %x, invalid key 4\n", cmd);
+        kill_channel(ch);
+        return;
+    }
+
+    switch (cmd) {
+    case OUTPUT_MORE:
+        start_output(ch, key, phy_addr, req_count, 0);
+       return;
+
+    case OUTPUT_LAST:
+        start_output(ch, key, phy_addr, req_count, 1);
+       return;
+
+    case INPUT_MORE:
+        start_input(ch, key, phy_addr, req_count, 0);
+       return;
+
+    case INPUT_LAST:
+        start_input(ch, key, phy_addr, req_count, 1);
+       return;
+    }
+
+    if (key < KEY_REGS) {
+        printf("command %x, invalid key %x\n", cmd, key);
+        key = KEY_SYSTEM;
+    }
+
+    /* for LOAD_WORD and STORE_WORD, req_count is on 3 bits
+     * and BRANCH is invalid
+     */
+
+    req_count = req_count & 0x0007;
+    if (req_count & 0x4) {
+        req_count = 4;
+        phy_addr &= ~3;
+    } else if (req_count & 0x2) {
+        req_count = 2;
+        phy_addr &= ~1;
+    } else
+        req_count = 1;
+
+    switch (cmd) {
+    case LOAD_WORD:
+        load_word(ch, key, phy_addr, req_count);
+       return;
+
+    case STORE_WORD:
+        store_word(ch, key, phy_addr, req_count);
+       return;
+    }
+}
+
+static void DBDMA_run(DBDMAState *s)
+{
+    int channel;
+
+    for (channel = 0; channel < DBDMA_CHANNELS; channel++) {
+        DBDMA_channel *ch = &s->channels[channel];
+        uint32_t status = ch->regs[DBDMA_STATUS];
+        if (!ch->processing && (status & RUN) && (status & ACTIVE)) {
+            channel_run(ch);
+        }
+    }
+}
+
+static void DBDMA_run_bh(void *opaque)
+{
+    DBDMAState *s = opaque;
+
+    DBDMA_DPRINTF("DBDMA_run_bh\n");
+
+    DBDMA_run(s);
+}
+
+void DBDMA_register_channel(void *dbdma, int nchan, qemu_irq irq,
+                            DBDMA_rw rw, DBDMA_flush flush,
+                            void *opaque)
+{
+    DBDMAState *s = dbdma;
+    DBDMA_channel *ch = &s->channels[nchan];
+
+    DBDMA_DPRINTF("DBDMA_register_channel 0x%x\n", nchan);
+
+    ch->irq = irq;
+    ch->channel = nchan;
+    ch->rw = rw;
+    ch->flush = flush;
+    ch->io.opaque = opaque;
+    ch->io.channel = ch;
+}
+
+static void
+dbdma_control_write(DBDMA_channel *ch)
+{
+    uint16_t mask, value;
+    uint32_t status;
+
+    mask = (ch->regs[DBDMA_CONTROL] >> 16) & 0xffff;
+    value = ch->regs[DBDMA_CONTROL] & 0xffff;
+
+    value &= (RUN | PAUSE | FLUSH | WAKE | DEVSTAT);
+
+    status = ch->regs[DBDMA_STATUS];
+
+    status = (value & mask) | (status & ~mask);
+
+    if (status & WAKE)
+        status |= ACTIVE;
+    if (status & RUN) {
+        status |= ACTIVE;
+        status &= ~DEAD;
+    }
+    if (status & PAUSE)
+        status &= ~ACTIVE;
+    if ((ch->regs[DBDMA_STATUS] & RUN) && !(status & RUN)) {
+        /* RUN is cleared */
+        status &= ~(ACTIVE|DEAD);
+        if ((status & FLUSH) && ch->flush) {
+            ch->flush(&ch->io);
+            status &= ~FLUSH;
+        }
+    }
+
+    DBDMA_DPRINTF("    status 0x%08x\n", status);
+
+    ch->regs[DBDMA_STATUS] = status;
+
+    if (status & ACTIVE)
+        qemu_bh_schedule(dbdma_bh);
+    if ((status & FLUSH) && ch->flush)
+        ch->flush(&ch->io);
+}
+
+static void dbdma_write(void *opaque, hwaddr addr,
+                        uint64_t value, unsigned size)
+{
+    int channel = addr >> DBDMA_CHANNEL_SHIFT;
+    DBDMAState *s = opaque;
+    DBDMA_channel *ch = &s->channels[channel];
+    int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2;
+
+    DBDMA_DPRINTF("writel 0x" TARGET_FMT_plx " <= 0x%08x\n", addr, value);
+    DBDMA_DPRINTF("channel 0x%x reg 0x%x\n",
+                  (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg);
+
+    /* cmdptr cannot be modified if channel is RUN or ACTIVE */
+
+    if (reg == DBDMA_CMDPTR_LO &&
+        (ch->regs[DBDMA_STATUS] & (RUN | ACTIVE)))
+       return;
+
+    ch->regs[reg] = value;
+
+    switch(reg) {
+    case DBDMA_CONTROL:
+        dbdma_control_write(ch);
+        break;
+    case DBDMA_CMDPTR_LO:
+        /* 16-byte aligned */
+        ch->regs[DBDMA_CMDPTR_LO] &= ~0xf;
+        dbdma_cmdptr_load(ch);
+        break;
+    case DBDMA_STATUS:
+    case DBDMA_INTR_SEL:
+    case DBDMA_BRANCH_SEL:
+    case DBDMA_WAIT_SEL:
+        /* nothing to do */
+        break;
+    case DBDMA_XFER_MODE:
+    case DBDMA_CMDPTR_HI:
+    case DBDMA_DATA2PTR_HI:
+    case DBDMA_DATA2PTR_LO:
+    case DBDMA_ADDRESS_HI:
+    case DBDMA_BRANCH_ADDR_HI:
+    case DBDMA_RES1:
+    case DBDMA_RES2:
+    case DBDMA_RES3:
+    case DBDMA_RES4:
+        /* unused */
+        break;
+    }
+}
+
+static uint64_t dbdma_read(void *opaque, hwaddr addr,
+                           unsigned size)
+{
+    uint32_t value;
+    int channel = addr >> DBDMA_CHANNEL_SHIFT;
+    DBDMAState *s = opaque;
+    DBDMA_channel *ch = &s->channels[channel];
+    int reg = (addr - (channel << DBDMA_CHANNEL_SHIFT)) >> 2;
+
+    value = ch->regs[reg];
+
+    DBDMA_DPRINTF("readl 0x" TARGET_FMT_plx " => 0x%08x\n", addr, value);
+    DBDMA_DPRINTF("channel 0x%x reg 0x%x\n",
+                  (uint32_t)addr >> DBDMA_CHANNEL_SHIFT, reg);
+
+    switch(reg) {
+    case DBDMA_CONTROL:
+        value = 0;
+        break;
+    case DBDMA_STATUS:
+    case DBDMA_CMDPTR_LO:
+    case DBDMA_INTR_SEL:
+    case DBDMA_BRANCH_SEL:
+    case DBDMA_WAIT_SEL:
+        /* nothing to do */
+        break;
+    case DBDMA_XFER_MODE:
+    case DBDMA_CMDPTR_HI:
+    case DBDMA_DATA2PTR_HI:
+    case DBDMA_DATA2PTR_LO:
+    case DBDMA_ADDRESS_HI:
+    case DBDMA_BRANCH_ADDR_HI:
+        /* unused */
+        value = 0;
+        break;
+    case DBDMA_RES1:
+    case DBDMA_RES2:
+    case DBDMA_RES3:
+    case DBDMA_RES4:
+        /* reserved */
+        break;
+    }
+
+    return value;
+}
+
+static const MemoryRegionOps dbdma_ops = {
+    .read = dbdma_read,
+    .write = dbdma_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static const VMStateDescription vmstate_dbdma_channel = {
+    .name = "dbdma_channel",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, struct DBDMA_channel, DBDMA_REGS),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_dbdma = {
+    .name = "dbdma",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields      = (VMStateField[]) {
+        VMSTATE_STRUCT_ARRAY(channels, DBDMAState, DBDMA_CHANNELS, 1,
+                             vmstate_dbdma_channel, DBDMA_channel),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void dbdma_reset(void *opaque)
+{
+    DBDMAState *s = opaque;
+    int i;
+
+    for (i = 0; i < DBDMA_CHANNELS; i++)
+        memset(s->channels[i].regs, 0, DBDMA_SIZE);
+}
+
+void* DBDMA_init (MemoryRegion **dbdma_mem)
+{
+    DBDMAState *s;
+
+    s = g_malloc0(sizeof(DBDMAState));
+
+    memory_region_init_io(&s->mem, &dbdma_ops, s, "dbdma", 0x1000);
+    *dbdma_mem = &s->mem;
+    vmstate_register(NULL, -1, &vmstate_dbdma, s);
+    qemu_register_reset(dbdma_reset, s);
+
+    dbdma_bh = qemu_bh_new(DBDMA_run_bh, s);
+
+    return s;
+}
diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c
new file mode 100644 (file)
index 0000000..2f389dd
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * PowerMac MacIO device emulation
+ *
+ * Copyright (c) 2005-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/ppc/mac.h"
+#include "hw/pci/pci.h"
+#include "hw/ppc/mac_dbdma.h"
+#include "hw/char/escc.h"
+
+#define TYPE_MACIO "macio"
+#define MACIO(obj) OBJECT_CHECK(MacIOState, (obj), TYPE_MACIO)
+
+typedef struct MacIOState
+{
+    /*< private >*/
+    PCIDevice parent;
+    /*< public >*/
+
+    MemoryRegion bar;
+    CUDAState cuda;
+    void *dbdma;
+    MemoryRegion *pic_mem;
+    MemoryRegion *escc_mem;
+} MacIOState;
+
+#define OLDWORLD_MACIO(obj) \
+    OBJECT_CHECK(OldWorldMacIOState, (obj), TYPE_OLDWORLD_MACIO)
+
+typedef struct OldWorldMacIOState {
+    /*< private >*/
+    MacIOState parent_obj;
+    /*< public >*/
+
+    qemu_irq irqs[3];
+
+    MacIONVRAMState nvram;
+    MACIOIDEState ide;
+} OldWorldMacIOState;
+
+#define NEWWORLD_MACIO(obj) \
+    OBJECT_CHECK(NewWorldMacIOState, (obj), TYPE_NEWWORLD_MACIO)
+
+typedef struct NewWorldMacIOState {
+    /*< private >*/
+    MacIOState parent_obj;
+    /*< public >*/
+    qemu_irq irqs[5];
+    MACIOIDEState ide[2];
+} NewWorldMacIOState;
+
+static void macio_bar_setup(MacIOState *macio_state)
+{
+    MemoryRegion *bar = &macio_state->bar;
+
+    if (macio_state->escc_mem) {
+        memory_region_add_subregion(bar, 0x13000, macio_state->escc_mem);
+    }
+}
+
+static int macio_common_initfn(PCIDevice *d)
+{
+    MacIOState *s = MACIO(d);
+    SysBusDevice *sysbus_dev;
+    int ret;
+
+    d->config[0x3d] = 0x01; // interrupt on pin 1
+
+    ret = qdev_init(DEVICE(&s->cuda));
+    if (ret < 0) {
+        return ret;
+    }
+    sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
+    memory_region_add_subregion(&s->bar, 0x16000,
+                                sysbus_mmio_get_region(sysbus_dev, 0));
+
+    macio_bar_setup(s);
+    pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar);
+
+    return 0;
+}
+
+static int macio_oldworld_initfn(PCIDevice *d)
+{
+    MacIOState *s = MACIO(d);
+    OldWorldMacIOState *os = OLDWORLD_MACIO(d);
+    SysBusDevice *sysbus_dev;
+    int ret = macio_common_initfn(d);
+    if (ret < 0) {
+        return ret;
+    }
+
+    sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
+    sysbus_connect_irq(sysbus_dev, 0, os->irqs[0]);
+
+    ret = qdev_init(DEVICE(&os->nvram));
+    if (ret < 0) {
+        return ret;
+    }
+    sysbus_dev = SYS_BUS_DEVICE(&os->nvram);
+    memory_region_add_subregion(&s->bar, 0x60000,
+                                sysbus_mmio_get_region(sysbus_dev, 0));
+    pmac_format_nvram_partition(&os->nvram, os->nvram.size);
+
+    if (s->pic_mem) {
+        /* Heathrow PIC */
+        memory_region_add_subregion(&s->bar, 0x00000, s->pic_mem);
+    }
+
+    sysbus_dev = SYS_BUS_DEVICE(&os->ide);
+    sysbus_connect_irq(sysbus_dev, 0, os->irqs[1]);
+    sysbus_connect_irq(sysbus_dev, 1, os->irqs[2]);
+    macio_ide_register_dma(&os->ide, s->dbdma, 0x16);
+    ret = qdev_init(DEVICE(&os->ide));
+    if (ret < 0) {
+        return ret;
+    }
+
+    return 0;
+}
+
+static void macio_oldworld_init(Object *obj)
+{
+    MacIOState *s = MACIO(obj);
+    OldWorldMacIOState *os = OLDWORLD_MACIO(obj);
+    DeviceState *dev;
+
+    qdev_init_gpio_out(DEVICE(obj), os->irqs, ARRAY_SIZE(os->irqs));
+
+    object_initialize(&os->nvram, TYPE_MACIO_NVRAM);
+    dev = DEVICE(&os->nvram);
+    qdev_prop_set_uint32(dev, "size", 0x2000);
+    qdev_prop_set_uint32(dev, "it_shift", 4);
+
+    object_initialize(&os->ide, TYPE_MACIO_IDE);
+    qdev_set_parent_bus(DEVICE(&os->ide), sysbus_get_default());
+    memory_region_add_subregion(&s->bar, 0x1f000 + (1 * 0x1000), &os->ide.mem);
+    object_property_add_child(obj, "ide", OBJECT(&os->ide), NULL);
+}
+
+static int macio_newworld_initfn(PCIDevice *d)
+{
+    MacIOState *s = MACIO(d);
+    NewWorldMacIOState *ns = NEWWORLD_MACIO(d);
+    SysBusDevice *sysbus_dev;
+    int ret = macio_common_initfn(d);
+    if (ret < 0) {
+        return ret;
+    }
+
+    sysbus_dev = SYS_BUS_DEVICE(&s->cuda);
+    sysbus_connect_irq(sysbus_dev, 0, ns->irqs[0]);
+
+    if (s->pic_mem) {
+        /* OpenPIC */
+        memory_region_add_subregion(&s->bar, 0x40000, s->pic_mem);
+    }
+
+    sysbus_dev = SYS_BUS_DEVICE(&ns->ide[0]);
+    sysbus_connect_irq(sysbus_dev, 0, ns->irqs[1]);
+    sysbus_connect_irq(sysbus_dev, 1, ns->irqs[2]);
+    macio_ide_register_dma(&ns->ide[0], s->dbdma, 0x16);
+    ret = qdev_init(DEVICE(&ns->ide[0]));
+    if (ret < 0) {
+        return ret;
+    }
+
+    sysbus_dev = SYS_BUS_DEVICE(&ns->ide[1]);
+    sysbus_connect_irq(sysbus_dev, 0, ns->irqs[3]);
+    sysbus_connect_irq(sysbus_dev, 1, ns->irqs[4]);
+    macio_ide_register_dma(&ns->ide[1], s->dbdma, 0x1a);
+    ret = qdev_init(DEVICE(&ns->ide[1]));
+    if (ret < 0) {
+        return ret;
+    }
+
+    return 0;
+}
+
+static void macio_newworld_init(Object *obj)
+{
+    MacIOState *s = MACIO(obj);
+    NewWorldMacIOState *ns = NEWWORLD_MACIO(obj);
+    int i;
+    gchar *name;
+
+    qdev_init_gpio_out(DEVICE(obj), ns->irqs, ARRAY_SIZE(ns->irqs));
+
+    for (i = 0; i < 2; i++) {
+        object_initialize(&ns->ide[i], TYPE_MACIO_IDE);
+        qdev_set_parent_bus(DEVICE(&ns->ide[i]), sysbus_get_default());
+        memory_region_add_subregion(&s->bar, 0x1f000 + ((i + 1) * 0x1000),
+                                    &ns->ide[i].mem);
+        name = g_strdup_printf("ide[%i]", i);
+        object_property_add_child(obj, name, OBJECT(&ns->ide[i]), NULL);
+        g_free(name);
+    }
+}
+
+static void macio_instance_init(Object *obj)
+{
+    MacIOState *s = MACIO(obj);
+    MemoryRegion *dbdma_mem;
+
+    memory_region_init(&s->bar, "macio", 0x80000);
+
+    object_initialize(&s->cuda, TYPE_CUDA);
+    qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default());
+    object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL);
+
+    s->dbdma = DBDMA_init(&dbdma_mem);
+    memory_region_add_subregion(&s->bar, 0x08000, dbdma_mem);
+}
+
+static void macio_oldworld_class_init(ObjectClass *oc, void *data)
+{
+    PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc);
+
+    pdc->init = macio_oldworld_initfn;
+    pdc->device_id = PCI_DEVICE_ID_APPLE_343S1201;
+}
+
+static void macio_newworld_class_init(ObjectClass *oc, void *data)
+{
+    PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc);
+
+    pdc->init = macio_newworld_initfn;
+    pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL;
+}
+
+static void macio_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->vendor_id = PCI_VENDOR_ID_APPLE;
+    k->class_id = PCI_CLASS_OTHERS << 8;
+}
+
+static const TypeInfo macio_oldworld_type_info = {
+    .name          = TYPE_OLDWORLD_MACIO,
+    .parent        = TYPE_MACIO,
+    .instance_size = sizeof(OldWorldMacIOState),
+    .instance_init = macio_oldworld_init,
+    .class_init    = macio_oldworld_class_init,
+};
+
+static const TypeInfo macio_newworld_type_info = {
+    .name          = TYPE_NEWWORLD_MACIO,
+    .parent        = TYPE_MACIO,
+    .instance_size = sizeof(NewWorldMacIOState),
+    .instance_init = macio_newworld_init,
+    .class_init    = macio_newworld_class_init,
+};
+
+static const TypeInfo macio_type_info = {
+    .name          = TYPE_MACIO,
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(MacIOState),
+    .instance_init = macio_instance_init,
+    .abstract      = true,
+    .class_init    = macio_class_init,
+};
+
+static void macio_register_types(void)
+{
+    type_register_static(&macio_type_info);
+    type_register_static(&macio_oldworld_type_info);
+    type_register_static(&macio_newworld_type_info);
+}
+
+type_init(macio_register_types)
+
+void macio_init(PCIDevice *d,
+                MemoryRegion *pic_mem,
+                MemoryRegion *escc_mem)
+{
+    MacIOState *macio_state = MACIO(d);
+
+    macio_state->pic_mem = pic_mem;
+    macio_state->escc_mem = escc_mem;
+    /* Note: this code is strongly inspirated from the corresponding code
+       in PearPC */
+
+    qdev_init_nofail(DEVICE(d));
+}
diff --git a/hw/misc/max111x.c b/hw/misc/max111x.c
new file mode 100644 (file)
index 0000000..d477ecd
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Maxim MAX1110/1111 ADC chip emulation.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GNU GPLv2.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "hw/ssi.h"
+
+typedef struct {
+    SSISlave ssidev;
+    qemu_irq interrupt;
+    uint8_t tb1, rb2, rb3;
+    int cycle;
+
+    uint8_t input[8];
+    int inputs, com;
+} MAX111xState;
+
+/* Control-byte bitfields */
+#define CB_PD0         (1 << 0)
+#define CB_PD1         (1 << 1)
+#define CB_SGL         (1 << 2)
+#define CB_UNI         (1 << 3)
+#define CB_SEL0                (1 << 4)
+#define CB_SEL1                (1 << 5)
+#define CB_SEL2                (1 << 6)
+#define CB_START       (1 << 7)
+
+#define CHANNEL_NUM(v, b0, b1, b2)     \
+                       ((((v) >> (2 + (b0))) & 4) |    \
+                        (((v) >> (3 + (b1))) & 2) |    \
+                        (((v) >> (4 + (b2))) & 1))
+
+static uint32_t max111x_read(MAX111xState *s)
+{
+    if (!s->tb1)
+        return 0;
+
+    switch (s->cycle ++) {
+    case 1:
+        return s->rb2;
+    case 2:
+        return s->rb3;
+    }
+
+    return 0;
+}
+
+/* Interpret a control-byte */
+static void max111x_write(MAX111xState *s, uint32_t value)
+{
+    int measure, chan;
+
+    /* Ignore the value if START bit is zero */
+    if (!(value & CB_START))
+        return;
+
+    s->cycle = 0;
+
+    if (!(value & CB_PD1)) {
+        s->tb1 = 0;
+        return;
+    }
+
+    s->tb1 = value;
+
+    if (s->inputs == 8)
+        chan = CHANNEL_NUM(value, 1, 0, 2);
+    else
+        chan = CHANNEL_NUM(value & ~CB_SEL0, 0, 1, 2);
+
+    if (value & CB_SGL)
+        measure = s->input[chan] - s->com;
+    else
+        measure = s->input[chan] - s->input[chan ^ 1];
+
+    if (!(value & CB_UNI))
+        measure ^= 0x80;
+
+    s->rb2 = (measure >> 2) & 0x3f;
+    s->rb3 = (measure << 6) & 0xc0;
+
+    /* FIXME: When should the IRQ be lowered?  */
+    qemu_irq_raise(s->interrupt);
+}
+
+static uint32_t max111x_transfer(SSISlave *dev, uint32_t value)
+{
+    MAX111xState *s = FROM_SSI_SLAVE(MAX111xState, dev);
+    max111x_write(s, value);
+    return max111x_read(s);
+}
+
+static const VMStateDescription vmstate_max111x = {
+    .name = "max111x",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_SSI_SLAVE(ssidev, MAX111xState),
+        VMSTATE_UINT8(tb1, MAX111xState),
+        VMSTATE_UINT8(rb2, MAX111xState),
+        VMSTATE_UINT8(rb3, MAX111xState),
+        VMSTATE_INT32_EQUAL(inputs, MAX111xState),
+        VMSTATE_INT32(com, MAX111xState),
+        VMSTATE_ARRAY_INT32_UNSAFE(input, MAX111xState, inputs,
+                                   vmstate_info_uint8, uint8_t),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int max111x_init(SSISlave *dev, int inputs)
+{
+    MAX111xState *s = FROM_SSI_SLAVE(MAX111xState, dev);
+
+    qdev_init_gpio_out(&dev->qdev, &s->interrupt, 1);
+
+    s->inputs = inputs;
+    /* TODO: add a user interface for setting these */
+    s->input[0] = 0xf0;
+    s->input[1] = 0xe0;
+    s->input[2] = 0xd0;
+    s->input[3] = 0xc0;
+    s->input[4] = 0xb0;
+    s->input[5] = 0xa0;
+    s->input[6] = 0x90;
+    s->input[7] = 0x80;
+    s->com = 0;
+
+    vmstate_register(&dev->qdev, -1, &vmstate_max111x, s);
+    return 0;
+}
+
+static int max1110_init(SSISlave *dev)
+{
+    return max111x_init(dev, 8);
+}
+
+static int max1111_init(SSISlave *dev)
+{
+    return max111x_init(dev, 4);
+}
+
+void max111x_set_input(DeviceState *dev, int line, uint8_t value)
+{
+    MAX111xState *s = FROM_SSI_SLAVE(MAX111xState, SSI_SLAVE_FROM_QDEV(dev));
+    assert(line >= 0 && line < s->inputs);
+    s->input[line] = value;
+}
+
+static void max1110_class_init(ObjectClass *klass, void *data)
+{
+    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
+
+    k->init = max1110_init;
+    k->transfer = max111x_transfer;
+}
+
+static const TypeInfo max1110_info = {
+    .name          = "max1110",
+    .parent        = TYPE_SSI_SLAVE,
+    .instance_size = sizeof(MAX111xState),
+    .class_init    = max1110_class_init,
+};
+
+static void max1111_class_init(ObjectClass *klass, void *data)
+{
+    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
+
+    k->init = max1111_init;
+    k->transfer = max111x_transfer;
+}
+
+static const TypeInfo max1111_info = {
+    .name          = "max1111",
+    .parent        = TYPE_SSI_SLAVE,
+    .instance_size = sizeof(MAX111xState),
+    .class_init    = max1111_class_init,
+};
+
+static void max111x_register_types(void)
+{
+    type_register_static(&max1110_info);
+    type_register_static(&max1111_info);
+}
+
+type_init(max111x_register_types)
diff --git a/hw/misc/puv3_pm.c b/hw/misc/puv3_pm.c
new file mode 100644 (file)
index 0000000..0aacdc2
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Power Management device simulation in PKUnity SoC
+ *
+ * Copyright (C) 2010-2012 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or any later version.
+ * See the COPYING file in the top-level directory.
+ */
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+
+#undef DEBUG_PUV3
+#include "hw/unicore32/puv3.h"
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+
+    uint32_t reg_PMCR;
+    uint32_t reg_PCGR;
+    uint32_t reg_PLL_SYS_CFG;
+    uint32_t reg_PLL_DDR_CFG;
+    uint32_t reg_PLL_VGA_CFG;
+    uint32_t reg_DIVCFG;
+} PUV3PMState;
+
+static uint64_t puv3_pm_read(void *opaque, hwaddr offset,
+        unsigned size)
+{
+    PUV3PMState *s = opaque;
+    uint32_t ret = 0;
+
+    switch (offset) {
+    case 0x14:
+        ret = s->reg_PCGR;
+        break;
+    case 0x18:
+        ret = s->reg_PLL_SYS_CFG;
+        break;
+    case 0x1c:
+        ret = s->reg_PLL_DDR_CFG;
+        break;
+    case 0x20:
+        ret = s->reg_PLL_VGA_CFG;
+        break;
+    case 0x24:
+        ret = s->reg_DIVCFG;
+        break;
+    case 0x28: /* PLL SYS STATUS */
+        ret = 0x00002401;
+        break;
+    case 0x2c: /* PLL DDR STATUS */
+        ret = 0x00100c00;
+        break;
+    case 0x30: /* PLL VGA STATUS */
+        ret = 0x00003801;
+        break;
+    case 0x34: /* DIV STATUS */
+        ret = 0x22f52015;
+        break;
+    case 0x38: /* SW RESET */
+        ret = 0x0;
+        break;
+    case 0x44: /* PLL DFC DONE */
+        ret = 0x7;
+        break;
+    default:
+        DPRINTF("Bad offset 0x%x\n", offset);
+    }
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
+
+    return ret;
+}
+
+static void puv3_pm_write(void *opaque, hwaddr offset,
+        uint64_t value, unsigned size)
+{
+    PUV3PMState *s = opaque;
+
+    switch (offset) {
+    case 0x0:
+        s->reg_PMCR = value;
+        break;
+    case 0x14:
+        s->reg_PCGR = value;
+        break;
+    case 0x18:
+        s->reg_PLL_SYS_CFG = value;
+        break;
+    case 0x1c:
+        s->reg_PLL_DDR_CFG = value;
+        break;
+    case 0x20:
+        s->reg_PLL_VGA_CFG = value;
+        break;
+    case 0x24:
+    case 0x38:
+        break;
+    default:
+        DPRINTF("Bad offset 0x%x\n", offset);
+    }
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
+}
+
+static const MemoryRegionOps puv3_pm_ops = {
+    .read = puv3_pm_read,
+    .write = puv3_pm_write,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int puv3_pm_init(SysBusDevice *dev)
+{
+    PUV3PMState *s = FROM_SYSBUS(PUV3PMState, dev);
+
+    s->reg_PCGR = 0x0;
+
+    memory_region_init_io(&s->iomem, &puv3_pm_ops, s, "puv3_pm",
+            PUV3_REGS_OFFSET);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static void puv3_pm_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = puv3_pm_init;
+}
+
+static const TypeInfo puv3_pm_info = {
+    .name = "puv3_pm",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(PUV3PMState),
+    .class_init = puv3_pm_class_init,
+};
+
+static void puv3_pm_register_type(void)
+{
+    type_register_static(&puv3_pm_info);
+}
+
+type_init(puv3_pm_register_type)
diff --git a/hw/misc/tmp105.c b/hw/misc/tmp105.c
new file mode 100644 (file)
index 0000000..21a27a6
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * Texas Instruments TMP105 temperature sensor.
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "hw/i2c/i2c.h"
+#include "hw/tmp105.h"
+#include "qapi/visitor.h"
+
+static void tmp105_interrupt_update(TMP105State *s)
+{
+    qemu_set_irq(s->pin, s->alarm ^ ((~s->config >> 2) & 1));  /* POL */
+}
+
+static void tmp105_alarm_update(TMP105State *s)
+{
+    if ((s->config >> 0) & 1) {                                        /* SD */
+        if ((s->config >> 7) & 1)                              /* OS */
+            s->config &= ~(1 << 7);                            /* OS */
+        else
+            return;
+    }
+
+    if ((s->config >> 1) & 1) {                                        /* TM */
+        if (s->temperature >= s->limit[1])
+            s->alarm = 1;
+        else if (s->temperature < s->limit[0])
+            s->alarm = 1;
+    } else {
+        if (s->temperature >= s->limit[1])
+            s->alarm = 1;
+        else if (s->temperature < s->limit[0])
+            s->alarm = 0;
+    }
+
+    tmp105_interrupt_update(s);
+}
+
+static void tmp105_get_temperature(Object *obj, Visitor *v, void *opaque,
+                                   const char *name, Error **errp)
+{
+    TMP105State *s = TMP105(obj);
+    int64_t value = s->temperature;
+
+    visit_type_int(v, &value, name, errp);
+}
+
+/* Units are 0.001 centigrades relative to 0 C.  */
+static void tmp105_set_temperature(Object *obj, Visitor *v, void *opaque,
+                                   const char *name, Error **errp)
+{
+    TMP105State *s = TMP105(obj);
+    int64_t temp;
+
+    visit_type_int(v, &temp, name, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+    if (temp >= 128000 || temp < -128000) {
+        error_setg(errp, "value %" PRId64 ".%03" PRIu64 " Â°C is out of range",
+                   temp / 1000, temp % 1000);
+        return;
+    }
+
+    s->temperature = ((int16_t) (temp * 0x800 / 128000)) << 4;
+
+    tmp105_alarm_update(s);
+}
+
+static const int tmp105_faultq[4] = { 1, 2, 4, 6 };
+
+static void tmp105_read(TMP105State *s)
+{
+    s->len = 0;
+
+    if ((s->config >> 1) & 1) {                                        /* TM */
+        s->alarm = 0;
+        tmp105_interrupt_update(s);
+    }
+
+    switch (s->pointer & 3) {
+    case TMP105_REG_TEMPERATURE:
+        s->buf[s->len ++] = (((uint16_t) s->temperature) >> 8);
+        s->buf[s->len ++] = (((uint16_t) s->temperature) >> 0) &
+                (0xf0 << ((~s->config >> 5) & 3));             /* R */
+        break;
+
+    case TMP105_REG_CONFIG:
+        s->buf[s->len ++] = s->config;
+        break;
+
+    case TMP105_REG_T_LOW:
+        s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 8;
+        s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 0;
+        break;
+
+    case TMP105_REG_T_HIGH:
+        s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 8;
+        s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 0;
+        break;
+    }
+}
+
+static void tmp105_write(TMP105State *s)
+{
+    switch (s->pointer & 3) {
+    case TMP105_REG_TEMPERATURE:
+        break;
+
+    case TMP105_REG_CONFIG:
+        if (s->buf[0] & ~s->config & (1 << 0))                 /* SD */
+            printf("%s: TMP105 shutdown\n", __FUNCTION__);
+        s->config = s->buf[0];
+        s->faults = tmp105_faultq[(s->config >> 3) & 3];       /* F */
+        tmp105_alarm_update(s);
+        break;
+
+    case TMP105_REG_T_LOW:
+    case TMP105_REG_T_HIGH:
+        if (s->len >= 3)
+            s->limit[s->pointer & 1] = (int16_t)
+                    ((((uint16_t) s->buf[0]) << 8) | s->buf[1]);
+        tmp105_alarm_update(s);
+        break;
+    }
+}
+
+static int tmp105_rx(I2CSlave *i2c)
+{
+    TMP105State *s = TMP105(i2c);
+
+    if (s->len < 2) {
+        return s->buf[s->len ++];
+    } else {
+        return 0xff;
+    }
+}
+
+static int tmp105_tx(I2CSlave *i2c, uint8_t data)
+{
+    TMP105State *s = TMP105(i2c);
+
+    if (s->len == 0) {
+        s->pointer = data;
+        s->len++;
+    } else {
+        if (s->len <= 2) {
+            s->buf[s->len - 1] = data;
+        }
+        s->len++;
+        tmp105_write(s);
+    }
+
+    return 0;
+}
+
+static void tmp105_event(I2CSlave *i2c, enum i2c_event event)
+{
+    TMP105State *s = TMP105(i2c);
+
+    if (event == I2C_START_RECV) {
+        tmp105_read(s);
+    }
+
+    s->len = 0;
+}
+
+static int tmp105_post_load(void *opaque, int version_id)
+{
+    TMP105State *s = opaque;
+
+    s->faults = tmp105_faultq[(s->config >> 3) & 3];           /* F */
+
+    tmp105_interrupt_update(s);
+    return 0;
+}
+
+static const VMStateDescription vmstate_tmp105 = {
+    .name = "TMP105",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .post_load = tmp105_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT8(len, TMP105State),
+        VMSTATE_UINT8_ARRAY(buf, TMP105State, 2),
+        VMSTATE_UINT8(pointer, TMP105State),
+        VMSTATE_UINT8(config, TMP105State),
+        VMSTATE_INT16(temperature, TMP105State),
+        VMSTATE_INT16_ARRAY(limit, TMP105State, 2),
+        VMSTATE_UINT8(alarm, TMP105State),
+        VMSTATE_I2C_SLAVE(i2c, TMP105State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void tmp105_reset(I2CSlave *i2c)
+{
+    TMP105State *s = TMP105(i2c);
+
+    s->temperature = 0;
+    s->pointer = 0;
+    s->config = 0;
+    s->faults = tmp105_faultq[(s->config >> 3) & 3];
+    s->alarm = 0;
+
+    tmp105_interrupt_update(s);
+}
+
+static int tmp105_init(I2CSlave *i2c)
+{
+    TMP105State *s = TMP105(i2c);
+
+    qdev_init_gpio_out(&i2c->qdev, &s->pin, 1);
+
+    tmp105_reset(&s->i2c);
+
+    return 0;
+}
+
+static void tmp105_initfn(Object *obj)
+{
+    object_property_add(obj, "temperature", "int",
+                        tmp105_get_temperature,
+                        tmp105_set_temperature, NULL, NULL, NULL);
+}
+
+static void tmp105_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+    k->init = tmp105_init;
+    k->event = tmp105_event;
+    k->recv = tmp105_rx;
+    k->send = tmp105_tx;
+    dc->vmsd = &vmstate_tmp105;
+}
+
+static const TypeInfo tmp105_info = {
+    .name          = TYPE_TMP105,
+    .parent        = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(TMP105State),
+    .instance_init = tmp105_initfn,
+    .class_init    = tmp105_class_init,
+};
+
+static void tmp105_register_types(void)
+{
+    type_register_static(&tmp105_info);
+}
+
+type_init(tmp105_register_types)
diff --git a/hw/nand.c b/hw/nand.c
deleted file mode 100644 (file)
index 087ca14..0000000
--- a/hw/nand.c
+++ /dev/null
@@ -1,791 +0,0 @@
-/*
- * Flash NAND memory emulation.  Based on "16M x 8 Bit NAND Flash
- * Memory" datasheet for the KM29U128AT / K9F2808U0A chips from
- * Samsung Electronic.
- *
- * Copyright (c) 2006 Openedhand Ltd.
- * Written by Andrzej Zaborowski <balrog@zabor.org>
- *
- * Support for additional features based on "MT29F2G16ABCWP 2Gx16"
- * datasheet from Micron Technology and "NAND02G-B2C" datasheet
- * from ST Microelectronics.
- *
- * This code is licensed under the GNU GPL v2.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#ifndef NAND_IO
-
-# include "hw/hw.h"
-# include "hw/block/flash.h"
-# include "sysemu/blockdev.h"
-# include "hw/sysbus.h"
-#include "qemu/error-report.h"
-
-# define NAND_CMD_READ0                0x00
-# define NAND_CMD_READ1                0x01
-# define NAND_CMD_READ2                0x50
-# define NAND_CMD_LPREAD2      0x30
-# define NAND_CMD_NOSERIALREAD2        0x35
-# define NAND_CMD_RANDOMREAD1  0x05
-# define NAND_CMD_RANDOMREAD2  0xe0
-# define NAND_CMD_READID       0x90
-# define NAND_CMD_RESET                0xff
-# define NAND_CMD_PAGEPROGRAM1 0x80
-# define NAND_CMD_PAGEPROGRAM2 0x10
-# define NAND_CMD_CACHEPROGRAM2        0x15
-# define NAND_CMD_BLOCKERASE1  0x60
-# define NAND_CMD_BLOCKERASE2  0xd0
-# define NAND_CMD_READSTATUS   0x70
-# define NAND_CMD_COPYBACKPRG1 0x85
-
-# define NAND_IOSTATUS_ERROR   (1 << 0)
-# define NAND_IOSTATUS_PLANE0  (1 << 1)
-# define NAND_IOSTATUS_PLANE1  (1 << 2)
-# define NAND_IOSTATUS_PLANE2  (1 << 3)
-# define NAND_IOSTATUS_PLANE3  (1 << 4)
-# define NAND_IOSTATUS_READY    (1 << 6)
-# define NAND_IOSTATUS_UNPROTCT        (1 << 7)
-
-# define MAX_PAGE              0x800
-# define MAX_OOB               0x40
-
-typedef struct NANDFlashState NANDFlashState;
-struct NANDFlashState {
-    SysBusDevice busdev;
-    uint8_t manf_id, chip_id;
-    uint8_t buswidth; /* in BYTES */
-    int size, pages;
-    int page_shift, oob_shift, erase_shift, addr_shift;
-    uint8_t *storage;
-    BlockDriverState *bdrv;
-    int mem_oob;
-
-    uint8_t cle, ale, ce, wp, gnd;
-
-    uint8_t io[MAX_PAGE + MAX_OOB + 0x400];
-    uint8_t *ioaddr;
-    int iolen;
-
-    uint32_t cmd;
-    uint64_t addr;
-    int addrlen;
-    int status;
-    int offset;
-
-    void (*blk_write)(NANDFlashState *s);
-    void (*blk_erase)(NANDFlashState *s);
-    void (*blk_load)(NANDFlashState *s, uint64_t addr, int offset);
-
-    uint32_t ioaddr_vmstate;
-};
-
-static void mem_and(uint8_t *dest, const uint8_t *src, size_t n)
-{
-    /* Like memcpy() but we logical-AND the data into the destination */
-    int i;
-    for (i = 0; i < n; i++) {
-        dest[i] &= src[i];
-    }
-}
-
-# define NAND_NO_AUTOINCR      0x00000001
-# define NAND_BUSWIDTH_16      0x00000002
-# define NAND_NO_PADDING       0x00000004
-# define NAND_CACHEPRG         0x00000008
-# define NAND_COPYBACK         0x00000010
-# define NAND_IS_AND           0x00000020
-# define NAND_4PAGE_ARRAY      0x00000040
-# define NAND_NO_READRDY       0x00000100
-# define NAND_SAMSUNG_LP       (NAND_NO_PADDING | NAND_COPYBACK)
-
-# define NAND_IO
-
-# define PAGE(addr)            ((addr) >> ADDR_SHIFT)
-# define PAGE_START(page)      (PAGE(page) * (PAGE_SIZE + OOB_SIZE))
-# define PAGE_MASK             ((1 << ADDR_SHIFT) - 1)
-# define OOB_SHIFT             (PAGE_SHIFT - 5)
-# define OOB_SIZE              (1 << OOB_SHIFT)
-# define SECTOR(addr)          ((addr) >> (9 + ADDR_SHIFT - PAGE_SHIFT))
-# define SECTOR_OFFSET(addr)   ((addr) & ((511 >> PAGE_SHIFT) << 8))
-
-# define PAGE_SIZE             256
-# define PAGE_SHIFT            8
-# define PAGE_SECTORS          1
-# define ADDR_SHIFT            8
-# include "nand.c"
-# define PAGE_SIZE             512
-# define PAGE_SHIFT            9
-# define PAGE_SECTORS          1
-# define ADDR_SHIFT            8
-# include "nand.c"
-# define PAGE_SIZE             2048
-# define PAGE_SHIFT            11
-# define PAGE_SECTORS          4
-# define ADDR_SHIFT            16
-# include "nand.c"
-
-/* Information based on Linux drivers/mtd/nand/nand_ids.c */
-static const struct {
-    int size;
-    int width;
-    int page_shift;
-    int erase_shift;
-    uint32_t options;
-} nand_flash_ids[0x100] = {
-    [0 ... 0xff] = { 0 },
-
-    [0x6e] = { 1,      8,      8, 4, 0 },
-    [0x64] = { 2,      8,      8, 4, 0 },
-    [0x6b] = { 4,      8,      9, 4, 0 },
-    [0xe8] = { 1,      8,      8, 4, 0 },
-    [0xec] = { 1,      8,      8, 4, 0 },
-    [0xea] = { 2,      8,      8, 4, 0 },
-    [0xd5] = { 4,      8,      9, 4, 0 },
-    [0xe3] = { 4,      8,      9, 4, 0 },
-    [0xe5] = { 4,      8,      9, 4, 0 },
-    [0xd6] = { 8,      8,      9, 4, 0 },
-
-    [0x39] = { 8,      8,      9, 4, 0 },
-    [0xe6] = { 8,      8,      9, 4, 0 },
-    [0x49] = { 8,      16,     9, 4, NAND_BUSWIDTH_16 },
-    [0x59] = { 8,      16,     9, 4, NAND_BUSWIDTH_16 },
-
-    [0x33] = { 16,     8,      9, 5, 0 },
-    [0x73] = { 16,     8,      9, 5, 0 },
-    [0x43] = { 16,     16,     9, 5, NAND_BUSWIDTH_16 },
-    [0x53] = { 16,     16,     9, 5, NAND_BUSWIDTH_16 },
-
-    [0x35] = { 32,     8,      9, 5, 0 },
-    [0x75] = { 32,     8,      9, 5, 0 },
-    [0x45] = { 32,     16,     9, 5, NAND_BUSWIDTH_16 },
-    [0x55] = { 32,     16,     9, 5, NAND_BUSWIDTH_16 },
-
-    [0x36] = { 64,     8,      9, 5, 0 },
-    [0x76] = { 64,     8,      9, 5, 0 },
-    [0x46] = { 64,     16,     9, 5, NAND_BUSWIDTH_16 },
-    [0x56] = { 64,     16,     9, 5, NAND_BUSWIDTH_16 },
-
-    [0x78] = { 128,    8,      9, 5, 0 },
-    [0x39] = { 128,    8,      9, 5, 0 },
-    [0x79] = { 128,    8,      9, 5, 0 },
-    [0x72] = { 128,    16,     9, 5, NAND_BUSWIDTH_16 },
-    [0x49] = { 128,    16,     9, 5, NAND_BUSWIDTH_16 },
-    [0x74] = { 128,    16,     9, 5, NAND_BUSWIDTH_16 },
-    [0x59] = { 128,    16,     9, 5, NAND_BUSWIDTH_16 },
-
-    [0x71] = { 256,    8,      9, 5, 0 },
-
-    /*
-     * These are the new chips with large page size. The pagesize and the
-     * erasesize is determined from the extended id bytes
-     */
-# define LP_OPTIONS    (NAND_SAMSUNG_LP | NAND_NO_READRDY | NAND_NO_AUTOINCR)
-# define LP_OPTIONS16  (LP_OPTIONS | NAND_BUSWIDTH_16)
-
-    /* 512 Megabit */
-    [0xa2] = { 64,     8,      0, 0, LP_OPTIONS },
-    [0xf2] = { 64,     8,      0, 0, LP_OPTIONS },
-    [0xb2] = { 64,     16,     0, 0, LP_OPTIONS16 },
-    [0xc2] = { 64,     16,     0, 0, LP_OPTIONS16 },
-
-    /* 1 Gigabit */
-    [0xa1] = { 128,    8,      0, 0, LP_OPTIONS },
-    [0xf1] = { 128,    8,      0, 0, LP_OPTIONS },
-    [0xb1] = { 128,    16,     0, 0, LP_OPTIONS16 },
-    [0xc1] = { 128,    16,     0, 0, LP_OPTIONS16 },
-
-    /* 2 Gigabit */
-    [0xaa] = { 256,    8,      0, 0, LP_OPTIONS },
-    [0xda] = { 256,    8,      0, 0, LP_OPTIONS },
-    [0xba] = { 256,    16,     0, 0, LP_OPTIONS16 },
-    [0xca] = { 256,    16,     0, 0, LP_OPTIONS16 },
-
-    /* 4 Gigabit */
-    [0xac] = { 512,    8,      0, 0, LP_OPTIONS },
-    [0xdc] = { 512,    8,      0, 0, LP_OPTIONS },
-    [0xbc] = { 512,    16,     0, 0, LP_OPTIONS16 },
-    [0xcc] = { 512,    16,     0, 0, LP_OPTIONS16 },
-
-    /* 8 Gigabit */
-    [0xa3] = { 1024,   8,      0, 0, LP_OPTIONS },
-    [0xd3] = { 1024,   8,      0, 0, LP_OPTIONS },
-    [0xb3] = { 1024,   16,     0, 0, LP_OPTIONS16 },
-    [0xc3] = { 1024,   16,     0, 0, LP_OPTIONS16 },
-
-    /* 16 Gigabit */
-    [0xa5] = { 2048,   8,      0, 0, LP_OPTIONS },
-    [0xd5] = { 2048,   8,      0, 0, LP_OPTIONS },
-    [0xb5] = { 2048,   16,     0, 0, LP_OPTIONS16 },
-    [0xc5] = { 2048,   16,     0, 0, LP_OPTIONS16 },
-};
-
-static void nand_reset(DeviceState *dev)
-{
-    NANDFlashState *s = FROM_SYSBUS(NANDFlashState, SYS_BUS_DEVICE(dev));
-    s->cmd = NAND_CMD_READ0;
-    s->addr = 0;
-    s->addrlen = 0;
-    s->iolen = 0;
-    s->offset = 0;
-    s->status &= NAND_IOSTATUS_UNPROTCT;
-    s->status |= NAND_IOSTATUS_READY;
-}
-
-static inline void nand_pushio_byte(NANDFlashState *s, uint8_t value)
-{
-    s->ioaddr[s->iolen++] = value;
-    for (value = s->buswidth; --value;) {
-        s->ioaddr[s->iolen++] = 0;
-    }
-}
-
-static void nand_command(NANDFlashState *s)
-{
-    unsigned int offset;
-    switch (s->cmd) {
-    case NAND_CMD_READ0:
-        s->iolen = 0;
-        break;
-
-    case NAND_CMD_READID:
-        s->ioaddr = s->io;
-        s->iolen = 0;
-        nand_pushio_byte(s, s->manf_id);
-        nand_pushio_byte(s, s->chip_id);
-        nand_pushio_byte(s, 'Q'); /* Don't-care byte (often 0xa5) */
-        if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
-            /* Page Size, Block Size, Spare Size; bit 6 indicates
-             * 8 vs 16 bit width NAND.
-             */
-            nand_pushio_byte(s, (s->buswidth == 2) ? 0x55 : 0x15);
-        } else {
-            nand_pushio_byte(s, 0xc0); /* Multi-plane */
-        }
-        break;
-
-    case NAND_CMD_RANDOMREAD2:
-    case NAND_CMD_NOSERIALREAD2:
-        if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP))
-            break;
-        offset = s->addr & ((1 << s->addr_shift) - 1);
-        s->blk_load(s, s->addr, offset);
-        if (s->gnd)
-            s->iolen = (1 << s->page_shift) - offset;
-        else
-            s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset;
-        break;
-
-    case NAND_CMD_RESET:
-        nand_reset(&s->busdev.qdev);
-        break;
-
-    case NAND_CMD_PAGEPROGRAM1:
-        s->ioaddr = s->io;
-        s->iolen = 0;
-        break;
-
-    case NAND_CMD_PAGEPROGRAM2:
-        if (s->wp) {
-            s->blk_write(s);
-        }
-        break;
-
-    case NAND_CMD_BLOCKERASE1:
-        break;
-
-    case NAND_CMD_BLOCKERASE2:
-        s->addr &= (1ull << s->addrlen * 8) - 1;
-        if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)
-            s->addr <<= 16;
-        else
-            s->addr <<= 8;
-
-        if (s->wp) {
-            s->blk_erase(s);
-        }
-        break;
-
-    case NAND_CMD_READSTATUS:
-        s->ioaddr = s->io;
-        s->iolen = 0;
-        nand_pushio_byte(s, s->status);
-        break;
-
-    default:
-        printf("%s: Unknown NAND command 0x%02x\n", __FUNCTION__, s->cmd);
-    }
-}
-
-static void nand_pre_save(void *opaque)
-{
-    NANDFlashState *s = opaque;
-
-    s->ioaddr_vmstate = s->ioaddr - s->io;
-}
-
-static int nand_post_load(void *opaque, int version_id)
-{
-    NANDFlashState *s = opaque;
-
-    if (s->ioaddr_vmstate > sizeof(s->io)) {
-        return -EINVAL;
-    }
-    s->ioaddr = s->io + s->ioaddr_vmstate;
-
-    return 0;
-}
-
-static const VMStateDescription vmstate_nand = {
-    .name = "nand",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .pre_save = nand_pre_save,
-    .post_load = nand_post_load,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT8(cle, NANDFlashState),
-        VMSTATE_UINT8(ale, NANDFlashState),
-        VMSTATE_UINT8(ce, NANDFlashState),
-        VMSTATE_UINT8(wp, NANDFlashState),
-        VMSTATE_UINT8(gnd, NANDFlashState),
-        VMSTATE_BUFFER(io, NANDFlashState),
-        VMSTATE_UINT32(ioaddr_vmstate, NANDFlashState),
-        VMSTATE_INT32(iolen, NANDFlashState),
-        VMSTATE_UINT32(cmd, NANDFlashState),
-        VMSTATE_UINT64(addr, NANDFlashState),
-        VMSTATE_INT32(addrlen, NANDFlashState),
-        VMSTATE_INT32(status, NANDFlashState),
-        VMSTATE_INT32(offset, NANDFlashState),
-        /* XXX: do we want to save s->storage too? */
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int nand_device_init(SysBusDevice *dev)
-{
-    int pagesize;
-    NANDFlashState *s = FROM_SYSBUS(NANDFlashState, dev);
-
-    s->buswidth = nand_flash_ids[s->chip_id].width >> 3;
-    s->size = nand_flash_ids[s->chip_id].size << 20;
-    if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
-        s->page_shift = 11;
-        s->erase_shift = 6;
-    } else {
-        s->page_shift = nand_flash_ids[s->chip_id].page_shift;
-        s->erase_shift = nand_flash_ids[s->chip_id].erase_shift;
-    }
-
-    switch (1 << s->page_shift) {
-    case 256:
-        nand_init_256(s);
-        break;
-    case 512:
-        nand_init_512(s);
-        break;
-    case 2048:
-        nand_init_2048(s);
-        break;
-    default:
-        error_report("Unsupported NAND block size");
-        return -1;
-    }
-
-    pagesize = 1 << s->oob_shift;
-    s->mem_oob = 1;
-    if (s->bdrv) {
-        if (bdrv_is_read_only(s->bdrv)) {
-            error_report("Can't use a read-only drive");
-            return -1;
-        }
-        if (bdrv_getlength(s->bdrv) >=
-                (s->pages << s->page_shift) + (s->pages << s->oob_shift)) {
-            pagesize = 0;
-            s->mem_oob = 0;
-        }
-    } else {
-        pagesize += 1 << s->page_shift;
-    }
-    if (pagesize) {
-        s->storage = (uint8_t *) memset(g_malloc(s->pages * pagesize),
-                        0xff, s->pages * pagesize);
-    }
-    /* Give s->ioaddr a sane value in case we save state before it is used. */
-    s->ioaddr = s->io;
-
-    return 0;
-}
-
-static Property nand_properties[] = {
-    DEFINE_PROP_UINT8("manufacturer_id", NANDFlashState, manf_id, 0),
-    DEFINE_PROP_UINT8("chip_id", NANDFlashState, chip_id, 0),
-    DEFINE_PROP_DRIVE("drive", NANDFlashState, bdrv),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void nand_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = nand_device_init;
-    dc->reset = nand_reset;
-    dc->vmsd = &vmstate_nand;
-    dc->props = nand_properties;
-}
-
-static const TypeInfo nand_info = {
-    .name          = "nand",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(NANDFlashState),
-    .class_init    = nand_class_init,
-};
-
-static void nand_register_types(void)
-{
-    type_register_static(&nand_info);
-}
-
-/*
- * Chip inputs are CLE, ALE, CE, WP, GND and eight I/O pins.  Chip
- * outputs are R/B and eight I/O pins.
- *
- * CE, WP and R/B are active low.
- */
-void nand_setpins(DeviceState *dev, uint8_t cle, uint8_t ale,
-                  uint8_t ce, uint8_t wp, uint8_t gnd)
-{
-    NANDFlashState *s = (NANDFlashState *) dev;
-    s->cle = cle;
-    s->ale = ale;
-    s->ce = ce;
-    s->wp = wp;
-    s->gnd = gnd;
-    if (wp)
-        s->status |= NAND_IOSTATUS_UNPROTCT;
-    else
-        s->status &= ~NAND_IOSTATUS_UNPROTCT;
-}
-
-void nand_getpins(DeviceState *dev, int *rb)
-{
-    *rb = 1;
-}
-
-void nand_setio(DeviceState *dev, uint32_t value)
-{
-    int i;
-    NANDFlashState *s = (NANDFlashState *) dev;
-    if (!s->ce && s->cle) {
-        if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) {
-            if (s->cmd == NAND_CMD_READ0 && value == NAND_CMD_LPREAD2)
-                return;
-            if (value == NAND_CMD_RANDOMREAD1) {
-                s->addr &= ~((1 << s->addr_shift) - 1);
-                s->addrlen = 0;
-                return;
-            }
-        }
-        if (value == NAND_CMD_READ0)
-            s->offset = 0;
-       else if (value == NAND_CMD_READ1) {
-            s->offset = 0x100;
-            value = NAND_CMD_READ0;
-        }
-       else if (value == NAND_CMD_READ2) {
-            s->offset = 1 << s->page_shift;
-            value = NAND_CMD_READ0;
-        }
-
-        s->cmd = value;
-
-        if (s->cmd == NAND_CMD_READSTATUS ||
-                s->cmd == NAND_CMD_PAGEPROGRAM2 ||
-                s->cmd == NAND_CMD_BLOCKERASE1 ||
-                s->cmd == NAND_CMD_BLOCKERASE2 ||
-                s->cmd == NAND_CMD_NOSERIALREAD2 ||
-                s->cmd == NAND_CMD_RANDOMREAD2 ||
-                s->cmd == NAND_CMD_RESET)
-            nand_command(s);
-
-        if (s->cmd != NAND_CMD_RANDOMREAD2) {
-            s->addrlen = 0;
-        }
-    }
-
-    if (s->ale) {
-        unsigned int shift = s->addrlen * 8;
-        unsigned int mask = ~(0xff << shift);
-        unsigned int v = value << shift;
-
-        s->addr = (s->addr & mask) | v;
-        s->addrlen ++;
-
-        switch (s->addrlen) {
-        case 1:
-            if (s->cmd == NAND_CMD_READID) {
-                nand_command(s);
-            }
-            break;
-        case 2: /* fix cache address as a byte address */
-            s->addr <<= (s->buswidth - 1);
-            break;
-        case 3:
-            if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
-                    (s->cmd == NAND_CMD_READ0 ||
-                     s->cmd == NAND_CMD_PAGEPROGRAM1)) {
-                nand_command(s);
-            }
-            break;
-        case 4:
-            if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
-                    nand_flash_ids[s->chip_id].size < 256 && /* 1Gb or less */
-                    (s->cmd == NAND_CMD_READ0 ||
-                     s->cmd == NAND_CMD_PAGEPROGRAM1)) {
-                nand_command(s);
-            }
-            break;
-        case 5:
-            if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) &&
-                    nand_flash_ids[s->chip_id].size >= 256 && /* 2Gb or more */
-                    (s->cmd == NAND_CMD_READ0 ||
-                     s->cmd == NAND_CMD_PAGEPROGRAM1)) {
-                nand_command(s);
-            }
-            break;
-        default:
-            break;
-        }
-    }
-
-    if (!s->cle && !s->ale && s->cmd == NAND_CMD_PAGEPROGRAM1) {
-        if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift)) {
-            for (i = s->buswidth; i--; value >>= 8) {
-                s->io[s->iolen ++] = (uint8_t) (value & 0xff);
-            }
-        }
-    } else if (!s->cle && !s->ale && s->cmd == NAND_CMD_COPYBACKPRG1) {
-        if ((s->addr & ((1 << s->addr_shift) - 1)) <
-                (1 << s->page_shift) + (1 << s->oob_shift)) {
-            for (i = s->buswidth; i--; s->addr++, value >>= 8) {
-                s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] =
-                    (uint8_t) (value & 0xff);
-            }
-        }
-    }
-}
-
-uint32_t nand_getio(DeviceState *dev)
-{
-    int offset;
-    uint32_t x = 0;
-    NANDFlashState *s = (NANDFlashState *) dev;
-
-    /* Allow sequential reading */
-    if (!s->iolen && s->cmd == NAND_CMD_READ0) {
-        offset = (int) (s->addr & ((1 << s->addr_shift) - 1)) + s->offset;
-        s->offset = 0;
-
-        s->blk_load(s, s->addr, offset);
-        if (s->gnd)
-            s->iolen = (1 << s->page_shift) - offset;
-        else
-            s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset;
-    }
-
-    if (s->ce || s->iolen <= 0)
-        return 0;
-
-    for (offset = s->buswidth; offset--;) {
-        x |= s->ioaddr[offset] << (offset << 3);
-    }
-    /* after receiving READ STATUS command all subsequent reads will
-     * return the status register value until another command is issued
-     */
-    if (s->cmd != NAND_CMD_READSTATUS) {
-        s->addr   += s->buswidth;
-        s->ioaddr += s->buswidth;
-        s->iolen  -= s->buswidth;
-    }
-    return x;
-}
-
-uint32_t nand_getbuswidth(DeviceState *dev)
-{
-    NANDFlashState *s = (NANDFlashState *) dev;
-    return s->buswidth << 3;
-}
-
-DeviceState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id)
-{
-    DeviceState *dev;
-
-    if (nand_flash_ids[chip_id].size == 0) {
-        hw_error("%s: Unsupported NAND chip ID.\n", __FUNCTION__);
-    }
-    dev = qdev_create(NULL, "nand");
-    qdev_prop_set_uint8(dev, "manufacturer_id", manf_id);
-    qdev_prop_set_uint8(dev, "chip_id", chip_id);
-    if (bdrv) {
-        qdev_prop_set_drive_nofail(dev, "drive", bdrv);
-    }
-
-    qdev_init_nofail(dev);
-    return dev;
-}
-
-type_init(nand_register_types)
-
-#else
-
-/* Program a single page */
-static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s)
-{
-    uint64_t off, page, sector, soff;
-    uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200];
-    if (PAGE(s->addr) >= s->pages)
-        return;
-
-    if (!s->bdrv) {
-        mem_and(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) +
-                        s->offset, s->io, s->iolen);
-    } else if (s->mem_oob) {
-        sector = SECTOR(s->addr);
-        off = (s->addr & PAGE_MASK) + s->offset;
-        soff = SECTOR_OFFSET(s->addr);
-        if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS) < 0) {
-            printf("%s: read error in sector %" PRIu64 "\n", __func__, sector);
-            return;
-        }
-
-        mem_and(iobuf + (soff | off), s->io, MIN(s->iolen, PAGE_SIZE - off));
-        if (off + s->iolen > PAGE_SIZE) {
-            page = PAGE(s->addr);
-            mem_and(s->storage + (page << OOB_SHIFT), s->io + PAGE_SIZE - off,
-                            MIN(OOB_SIZE, off + s->iolen - PAGE_SIZE));
-        }
-
-        if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS) < 0) {
-            printf("%s: write error in sector %" PRIu64 "\n", __func__, sector);
-        }
-    } else {
-        off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset;
-        sector = off >> 9;
-        soff = off & 0x1ff;
-        if (bdrv_read(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) < 0) {
-            printf("%s: read error in sector %" PRIu64 "\n", __func__, sector);
-            return;
-        }
-
-        mem_and(iobuf + soff, s->io, s->iolen);
-
-        if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) < 0) {
-            printf("%s: write error in sector %" PRIu64 "\n", __func__, sector);
-        }
-    }
-    s->offset = 0;
-}
-
-/* Erase a single block */
-static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s)
-{
-    uint64_t i, page, addr;
-    uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, };
-    addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1);
-
-    if (PAGE(addr) >= s->pages)
-        return;
-
-    if (!s->bdrv) {
-        memset(s->storage + PAGE_START(addr),
-                        0xff, (PAGE_SIZE + OOB_SIZE) << s->erase_shift);
-    } else if (s->mem_oob) {
-        memset(s->storage + (PAGE(addr) << OOB_SHIFT),
-                        0xff, OOB_SIZE << s->erase_shift);
-        i = SECTOR(addr);
-        page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift));
-        for (; i < page; i ++)
-            if (bdrv_write(s->bdrv, i, iobuf, 1) < 0) {
-                printf("%s: write error in sector %" PRIu64 "\n", __func__, i);
-            }
-    } else {
-        addr = PAGE_START(addr);
-        page = addr >> 9;
-        if (bdrv_read(s->bdrv, page, iobuf, 1) < 0) {
-            printf("%s: read error in sector %" PRIu64 "\n", __func__, page);
-        }
-        memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1);
-        if (bdrv_write(s->bdrv, page, iobuf, 1) < 0) {
-            printf("%s: write error in sector %" PRIu64 "\n", __func__, page);
-        }
-
-        memset(iobuf, 0xff, 0x200);
-        i = (addr & ~0x1ff) + 0x200;
-        for (addr += ((PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200;
-                        i < addr; i += 0x200)
-            if (bdrv_write(s->bdrv, i >> 9, iobuf, 1) < 0) {
-                printf("%s: write error in sector %" PRIu64 "\n",
-                       __func__, i >> 9);
-            }
-
-        page = i >> 9;
-        if (bdrv_read(s->bdrv, page, iobuf, 1) < 0) {
-            printf("%s: read error in sector %" PRIu64 "\n", __func__, page);
-        }
-        memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1);
-        if (bdrv_write(s->bdrv, page, iobuf, 1) < 0) {
-            printf("%s: write error in sector %" PRIu64 "\n", __func__, page);
-        }
-    }
-}
-
-static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s,
-                uint64_t addr, int offset)
-{
-    if (PAGE(addr) >= s->pages)
-        return;
-
-    if (s->bdrv) {
-        if (s->mem_oob) {
-            if (bdrv_read(s->bdrv, SECTOR(addr), s->io, PAGE_SECTORS) < 0) {
-                printf("%s: read error in sector %" PRIu64 "\n",
-                                __func__, SECTOR(addr));
-            }
-            memcpy(s->io + SECTOR_OFFSET(s->addr) + PAGE_SIZE,
-                            s->storage + (PAGE(s->addr) << OOB_SHIFT),
-                            OOB_SIZE);
-            s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset;
-        } else {
-            if (bdrv_read(s->bdrv, PAGE_START(addr) >> 9,
-                                    s->io, (PAGE_SECTORS + 2)) < 0) {
-                printf("%s: read error in sector %" PRIu64 "\n",
-                                __func__, PAGE_START(addr) >> 9);
-            }
-            s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset;
-        }
-    } else {
-        memcpy(s->io, s->storage + PAGE_START(s->addr) +
-                        offset, PAGE_SIZE + OOB_SIZE - offset);
-        s->ioaddr = s->io;
-    }
-}
-
-static void glue(nand_init_, PAGE_SIZE)(NANDFlashState *s)
-{
-    s->oob_shift = PAGE_SHIFT - 5;
-    s->pages = s->size >> PAGE_SHIFT;
-    s->addr_shift = ADDR_SHIFT;
-
-    s->blk_erase = glue(nand_blk_erase_, PAGE_SIZE);
-    s->blk_write = glue(nand_blk_write_, PAGE_SIZE);
-    s->blk_load = glue(nand_blk_load_, PAGE_SIZE);
-}
-
-# undef PAGE_SIZE
-# undef PAGE_SHIFT
-# undef PAGE_SECTORS
-# undef ADDR_SHIFT
-#endif /* NAND_IO */
diff --git a/hw/ne2000-isa.c b/hw/ne2000-isa.c
deleted file mode 100644 (file)
index e4c10db..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * QEMU NE2000 emulation -- isa bus windup
- *
- * Copyright (c) 2003-2004 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/i386/pc.h"
-#include "hw/isa/isa.h"
-#include "hw/qdev.h"
-#include "net/net.h"
-#include "hw/ne2000.h"
-#include "exec/address-spaces.h"
-
-typedef struct ISANE2000State {
-    ISADevice dev;
-    uint32_t iobase;
-    uint32_t isairq;
-    NE2000State ne2000;
-} ISANE2000State;
-
-static void isa_ne2000_cleanup(NetClientState *nc)
-{
-    NE2000State *s = qemu_get_nic_opaque(nc);
-
-    s->nic = NULL;
-}
-
-static NetClientInfo net_ne2000_isa_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
-    .size = sizeof(NICState),
-    .can_receive = ne2000_can_receive,
-    .receive = ne2000_receive,
-    .cleanup = isa_ne2000_cleanup,
-};
-
-static const VMStateDescription vmstate_isa_ne2000 = {
-    .name = "ne2000",
-    .version_id = 2,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .fields      = (VMStateField []) {
-        VMSTATE_STRUCT(ne2000, ISANE2000State, 0, vmstate_ne2000, NE2000State),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int isa_ne2000_initfn(ISADevice *dev)
-{
-    ISANE2000State *isa = DO_UPCAST(ISANE2000State, dev, dev);
-    NE2000State *s = &isa->ne2000;
-
-    ne2000_setup_io(s, 0x20);
-    isa_register_ioport(dev, &s->io, isa->iobase);
-
-    isa_init_irq(dev, &s->irq, isa->isairq);
-
-    qemu_macaddr_default_if_unset(&s->c.macaddr);
-    ne2000_reset(s);
-
-    s->nic = qemu_new_nic(&net_ne2000_isa_info, &s->c,
-                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
-    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a);
-
-    return 0;
-}
-
-static Property ne2000_isa_properties[] = {
-    DEFINE_PROP_HEX32("iobase", ISANE2000State, iobase, 0x300),
-    DEFINE_PROP_UINT32("irq",   ISANE2000State, isairq, 9),
-    DEFINE_NIC_PROPERTIES(ISANE2000State, ne2000.c),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void isa_ne2000_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
-    ic->init = isa_ne2000_initfn;
-    dc->props = ne2000_isa_properties;
-}
-
-static const TypeInfo ne2000_isa_info = {
-    .name          = "ne2k_isa",
-    .parent        = TYPE_ISA_DEVICE,
-    .instance_size = sizeof(ISANE2000State),
-    .class_init    = isa_ne2000_class_initfn,
-};
-
-static void ne2000_isa_register_types(void)
-{
-    type_register_static(&ne2000_isa_info);
-}
-
-type_init(ne2000_isa_register_types)
diff --git a/hw/ne2000.c b/hw/ne2000.c
deleted file mode 100644 (file)
index 7f45831..0000000
+++ /dev/null
@@ -1,789 +0,0 @@
-/*
- * QEMU NE2000 emulation
- *
- * Copyright (c) 2003-2004 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/pci/pci.h"
-#include "net/net.h"
-#include "hw/ne2000.h"
-#include "hw/loader.h"
-#include "sysemu/sysemu.h"
-
-/* debug NE2000 card */
-//#define DEBUG_NE2000
-
-#define MAX_ETH_FRAME_SIZE 1514
-
-#define E8390_CMD      0x00  /* The command register (for all pages) */
-/* Page 0 register offsets. */
-#define EN0_CLDALO     0x01    /* Low byte of current local dma addr  RD */
-#define EN0_STARTPG    0x01    /* Starting page of ring bfr WR */
-#define EN0_CLDAHI     0x02    /* High byte of current local dma addr  RD */
-#define EN0_STOPPG     0x02    /* Ending page +1 of ring bfr WR */
-#define EN0_BOUNDARY   0x03    /* Boundary page of ring bfr RD WR */
-#define EN0_TSR                0x04    /* Transmit status reg RD */
-#define EN0_TPSR       0x04    /* Transmit starting page WR */
-#define EN0_NCR                0x05    /* Number of collision reg RD */
-#define EN0_TCNTLO     0x05    /* Low  byte of tx byte count WR */
-#define EN0_FIFO       0x06    /* FIFO RD */
-#define EN0_TCNTHI     0x06    /* High byte of tx byte count WR */
-#define EN0_ISR                0x07    /* Interrupt status reg RD WR */
-#define EN0_CRDALO     0x08    /* low byte of current remote dma address RD */
-#define EN0_RSARLO     0x08    /* Remote start address reg 0 */
-#define EN0_CRDAHI     0x09    /* high byte, current remote dma address RD */
-#define EN0_RSARHI     0x09    /* Remote start address reg 1 */
-#define EN0_RCNTLO     0x0a    /* Remote byte count reg WR */
-#define EN0_RTL8029ID0 0x0a    /* Realtek ID byte #1 RD */
-#define EN0_RCNTHI     0x0b    /* Remote byte count reg WR */
-#define EN0_RTL8029ID1 0x0b    /* Realtek ID byte #2 RD */
-#define EN0_RSR                0x0c    /* rx status reg RD */
-#define EN0_RXCR       0x0c    /* RX configuration reg WR */
-#define EN0_TXCR       0x0d    /* TX configuration reg WR */
-#define EN0_COUNTER0   0x0d    /* Rcv alignment error counter RD */
-#define EN0_DCFG       0x0e    /* Data configuration reg WR */
-#define EN0_COUNTER1   0x0e    /* Rcv CRC error counter RD */
-#define EN0_IMR                0x0f    /* Interrupt mask reg WR */
-#define EN0_COUNTER2   0x0f    /* Rcv missed frame error counter RD */
-
-#define EN1_PHYS        0x11
-#define EN1_CURPAG      0x17
-#define EN1_MULT        0x18
-
-#define EN2_STARTPG    0x21    /* Starting page of ring bfr RD */
-#define EN2_STOPPG     0x22    /* Ending page +1 of ring bfr RD */
-
-#define EN3_CONFIG0    0x33
-#define EN3_CONFIG1    0x34
-#define EN3_CONFIG2    0x35
-#define EN3_CONFIG3    0x36
-
-/*  Register accessed at EN_CMD, the 8390 base addr.  */
-#define E8390_STOP     0x01    /* Stop and reset the chip */
-#define E8390_START    0x02    /* Start the chip, clear reset */
-#define E8390_TRANS    0x04    /* Transmit a frame */
-#define E8390_RREAD    0x08    /* Remote read */
-#define E8390_RWRITE   0x10    /* Remote write  */
-#define E8390_NODMA    0x20    /* Remote DMA */
-#define E8390_PAGE0    0x00    /* Select page chip registers */
-#define E8390_PAGE1    0x40    /* using the two high-order bits */
-#define E8390_PAGE2    0x80    /* Page 3 is invalid. */
-
-/* Bits in EN0_ISR - Interrupt status register */
-#define ENISR_RX       0x01    /* Receiver, no error */
-#define ENISR_TX       0x02    /* Transmitter, no error */
-#define ENISR_RX_ERR   0x04    /* Receiver, with error */
-#define ENISR_TX_ERR   0x08    /* Transmitter, with error */
-#define ENISR_OVER     0x10    /* Receiver overwrote the ring */
-#define ENISR_COUNTERS 0x20    /* Counters need emptying */
-#define ENISR_RDC      0x40    /* remote dma complete */
-#define ENISR_RESET    0x80    /* Reset completed */
-#define ENISR_ALL      0x3f    /* Interrupts we will enable */
-
-/* Bits in received packet status byte and EN0_RSR*/
-#define ENRSR_RXOK     0x01    /* Received a good packet */
-#define ENRSR_CRC      0x02    /* CRC error */
-#define ENRSR_FAE      0x04    /* frame alignment error */
-#define ENRSR_FO       0x08    /* FIFO overrun */
-#define ENRSR_MPA      0x10    /* missed pkt */
-#define ENRSR_PHY      0x20    /* physical/multicast address */
-#define ENRSR_DIS      0x40    /* receiver disable. set in monitor mode */
-#define ENRSR_DEF      0x80    /* deferring */
-
-/* Transmitted packet status, EN0_TSR. */
-#define ENTSR_PTX 0x01 /* Packet transmitted without error */
-#define ENTSR_ND  0x02 /* The transmit wasn't deferred. */
-#define ENTSR_COL 0x04 /* The transmit collided at least once. */
-#define ENTSR_ABT 0x08  /* The transmit collided 16 times, and was deferred. */
-#define ENTSR_CRS 0x10 /* The carrier sense was lost. */
-#define ENTSR_FU  0x20  /* A "FIFO underrun" occurred during transmit. */
-#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */
-#define ENTSR_OWC 0x80  /* There was an out-of-window collision. */
-
-typedef struct PCINE2000State {
-    PCIDevice dev;
-    NE2000State ne2000;
-} PCINE2000State;
-
-void ne2000_reset(NE2000State *s)
-{
-    int i;
-
-    s->isr = ENISR_RESET;
-    memcpy(s->mem, &s->c.macaddr, 6);
-    s->mem[14] = 0x57;
-    s->mem[15] = 0x57;
-
-    /* duplicate prom data */
-    for(i = 15;i >= 0; i--) {
-        s->mem[2 * i] = s->mem[i];
-        s->mem[2 * i + 1] = s->mem[i];
-    }
-}
-
-static void ne2000_update_irq(NE2000State *s)
-{
-    int isr;
-    isr = (s->isr & s->imr) & 0x7f;
-#if defined(DEBUG_NE2000)
-    printf("NE2000: Set IRQ to %d (%02x %02x)\n",
-          isr ? 1 : 0, s->isr, s->imr);
-#endif
-    qemu_set_irq(s->irq, (isr != 0));
-}
-
-static int ne2000_buffer_full(NE2000State *s)
-{
-    int avail, index, boundary;
-
-    index = s->curpag << 8;
-    boundary = s->boundary << 8;
-    if (index < boundary)
-        avail = boundary - index;
-    else
-        avail = (s->stop - s->start) - (index - boundary);
-    if (avail < (MAX_ETH_FRAME_SIZE + 4))
-        return 1;
-    return 0;
-}
-
-int ne2000_can_receive(NetClientState *nc)
-{
-    NE2000State *s = qemu_get_nic_opaque(nc);
-
-    if (s->cmd & E8390_STOP)
-        return 1;
-    return !ne2000_buffer_full(s);
-}
-
-#define MIN_BUF_SIZE 60
-
-ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
-{
-    NE2000State *s = qemu_get_nic_opaque(nc);
-    int size = size_;
-    uint8_t *p;
-    unsigned int total_len, next, avail, len, index, mcast_idx;
-    uint8_t buf1[60];
-    static const uint8_t broadcast_macaddr[6] =
-        { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-
-#if defined(DEBUG_NE2000)
-    printf("NE2000: received len=%d\n", size);
-#endif
-
-    if (s->cmd & E8390_STOP || ne2000_buffer_full(s))
-        return -1;
-
-    /* XXX: check this */
-    if (s->rxcr & 0x10) {
-        /* promiscuous: receive all */
-    } else {
-        if (!memcmp(buf,  broadcast_macaddr, 6)) {
-            /* broadcast address */
-            if (!(s->rxcr & 0x04))
-                return size;
-        } else if (buf[0] & 0x01) {
-            /* multicast */
-            if (!(s->rxcr & 0x08))
-                return size;
-            mcast_idx = compute_mcast_idx(buf);
-            if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))))
-                return size;
-        } else if (s->mem[0] == buf[0] &&
-                   s->mem[2] == buf[1] &&
-                   s->mem[4] == buf[2] &&
-                   s->mem[6] == buf[3] &&
-                   s->mem[8] == buf[4] &&
-                   s->mem[10] == buf[5]) {
-            /* match */
-        } else {
-            return size;
-        }
-    }
-
-
-    /* if too small buffer, then expand it */
-    if (size < MIN_BUF_SIZE) {
-        memcpy(buf1, buf, size);
-        memset(buf1 + size, 0, MIN_BUF_SIZE - size);
-        buf = buf1;
-        size = MIN_BUF_SIZE;
-    }
-
-    index = s->curpag << 8;
-    /* 4 bytes for header */
-    total_len = size + 4;
-    /* address for next packet (4 bytes for CRC) */
-    next = index + ((total_len + 4 + 255) & ~0xff);
-    if (next >= s->stop)
-        next -= (s->stop - s->start);
-    /* prepare packet header */
-    p = s->mem + index;
-    s->rsr = ENRSR_RXOK; /* receive status */
-    /* XXX: check this */
-    if (buf[0] & 0x01)
-        s->rsr |= ENRSR_PHY;
-    p[0] = s->rsr;
-    p[1] = next >> 8;
-    p[2] = total_len;
-    p[3] = total_len >> 8;
-    index += 4;
-
-    /* write packet data */
-    while (size > 0) {
-        if (index <= s->stop)
-            avail = s->stop - index;
-        else
-            avail = 0;
-        len = size;
-        if (len > avail)
-            len = avail;
-        memcpy(s->mem + index, buf, len);
-        buf += len;
-        index += len;
-        if (index == s->stop)
-            index = s->start;
-        size -= len;
-    }
-    s->curpag = next >> 8;
-
-    /* now we can signal we have received something */
-    s->isr |= ENISR_RX;
-    ne2000_update_irq(s);
-
-    return size_;
-}
-
-static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val)
-{
-    NE2000State *s = opaque;
-    int offset, page, index;
-
-    addr &= 0xf;
-#ifdef DEBUG_NE2000
-    printf("NE2000: write addr=0x%x val=0x%02x\n", addr, val);
-#endif
-    if (addr == E8390_CMD) {
-        /* control register */
-        s->cmd = val;
-        if (!(val & E8390_STOP)) { /* START bit makes no sense on RTL8029... */
-            s->isr &= ~ENISR_RESET;
-            /* test specific case: zero length transfer */
-            if ((val & (E8390_RREAD | E8390_RWRITE)) &&
-                s->rcnt == 0) {
-                s->isr |= ENISR_RDC;
-                ne2000_update_irq(s);
-            }
-            if (val & E8390_TRANS) {
-                index = (s->tpsr << 8);
-                /* XXX: next 2 lines are a hack to make netware 3.11 work */
-                if (index >= NE2000_PMEM_END)
-                    index -= NE2000_PMEM_SIZE;
-                /* fail safe: check range on the transmitted length  */
-                if (index + s->tcnt <= NE2000_PMEM_END) {
-                    qemu_send_packet(qemu_get_queue(s->nic), s->mem + index,
-                                     s->tcnt);
-                }
-                /* signal end of transfer */
-                s->tsr = ENTSR_PTX;
-                s->isr |= ENISR_TX;
-                s->cmd &= ~E8390_TRANS;
-                ne2000_update_irq(s);
-            }
-        }
-    } else {
-        page = s->cmd >> 6;
-        offset = addr | (page << 4);
-        switch(offset) {
-        case EN0_STARTPG:
-            s->start = val << 8;
-            break;
-        case EN0_STOPPG:
-            s->stop = val << 8;
-            break;
-        case EN0_BOUNDARY:
-            s->boundary = val;
-            break;
-        case EN0_IMR:
-            s->imr = val;
-            ne2000_update_irq(s);
-            break;
-        case EN0_TPSR:
-            s->tpsr = val;
-            break;
-        case EN0_TCNTLO:
-            s->tcnt = (s->tcnt & 0xff00) | val;
-            break;
-        case EN0_TCNTHI:
-            s->tcnt = (s->tcnt & 0x00ff) | (val << 8);
-            break;
-        case EN0_RSARLO:
-            s->rsar = (s->rsar & 0xff00) | val;
-            break;
-        case EN0_RSARHI:
-            s->rsar = (s->rsar & 0x00ff) | (val << 8);
-            break;
-        case EN0_RCNTLO:
-            s->rcnt = (s->rcnt & 0xff00) | val;
-            break;
-        case EN0_RCNTHI:
-            s->rcnt = (s->rcnt & 0x00ff) | (val << 8);
-            break;
-        case EN0_RXCR:
-            s->rxcr = val;
-            break;
-        case EN0_DCFG:
-            s->dcfg = val;
-            break;
-        case EN0_ISR:
-            s->isr &= ~(val & 0x7f);
-            ne2000_update_irq(s);
-            break;
-        case EN1_PHYS ... EN1_PHYS + 5:
-            s->phys[offset - EN1_PHYS] = val;
-            break;
-        case EN1_CURPAG:
-            s->curpag = val;
-            break;
-        case EN1_MULT ... EN1_MULT + 7:
-            s->mult[offset - EN1_MULT] = val;
-            break;
-        }
-    }
-}
-
-static uint32_t ne2000_ioport_read(void *opaque, uint32_t addr)
-{
-    NE2000State *s = opaque;
-    int offset, page, ret;
-
-    addr &= 0xf;
-    if (addr == E8390_CMD) {
-        ret = s->cmd;
-    } else {
-        page = s->cmd >> 6;
-        offset = addr | (page << 4);
-        switch(offset) {
-        case EN0_TSR:
-            ret = s->tsr;
-            break;
-        case EN0_BOUNDARY:
-            ret = s->boundary;
-            break;
-        case EN0_ISR:
-            ret = s->isr;
-            break;
-       case EN0_RSARLO:
-           ret = s->rsar & 0x00ff;
-           break;
-       case EN0_RSARHI:
-           ret = s->rsar >> 8;
-           break;
-        case EN1_PHYS ... EN1_PHYS + 5:
-            ret = s->phys[offset - EN1_PHYS];
-            break;
-        case EN1_CURPAG:
-            ret = s->curpag;
-            break;
-        case EN1_MULT ... EN1_MULT + 7:
-            ret = s->mult[offset - EN1_MULT];
-            break;
-        case EN0_RSR:
-            ret = s->rsr;
-            break;
-        case EN2_STARTPG:
-            ret = s->start >> 8;
-            break;
-        case EN2_STOPPG:
-            ret = s->stop >> 8;
-            break;
-       case EN0_RTL8029ID0:
-           ret = 0x50;
-           break;
-       case EN0_RTL8029ID1:
-           ret = 0x43;
-           break;
-       case EN3_CONFIG0:
-           ret = 0;            /* 10baseT media */
-           break;
-       case EN3_CONFIG2:
-           ret = 0x40;         /* 10baseT active */
-           break;
-       case EN3_CONFIG3:
-           ret = 0x40;         /* Full duplex */
-           break;
-        default:
-            ret = 0x00;
-            break;
-        }
-    }
-#ifdef DEBUG_NE2000
-    printf("NE2000: read addr=0x%x val=%02x\n", addr, ret);
-#endif
-    return ret;
-}
-
-static inline void ne2000_mem_writeb(NE2000State *s, uint32_t addr,
-                                     uint32_t val)
-{
-    if (addr < 32 ||
-        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
-        s->mem[addr] = val;
-    }
-}
-
-static inline void ne2000_mem_writew(NE2000State *s, uint32_t addr,
-                                     uint32_t val)
-{
-    addr &= ~1; /* XXX: check exact behaviour if not even */
-    if (addr < 32 ||
-        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
-        *(uint16_t *)(s->mem + addr) = cpu_to_le16(val);
-    }
-}
-
-static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr,
-                                     uint32_t val)
-{
-    addr &= ~1; /* XXX: check exact behaviour if not even */
-    if (addr < 32 ||
-        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
-        cpu_to_le32wu((uint32_t *)(s->mem + addr), val);
-    }
-}
-
-static inline uint32_t ne2000_mem_readb(NE2000State *s, uint32_t addr)
-{
-    if (addr < 32 ||
-        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
-        return s->mem[addr];
-    } else {
-        return 0xff;
-    }
-}
-
-static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr)
-{
-    addr &= ~1; /* XXX: check exact behaviour if not even */
-    if (addr < 32 ||
-        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
-        return le16_to_cpu(*(uint16_t *)(s->mem + addr));
-    } else {
-        return 0xffff;
-    }
-}
-
-static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr)
-{
-    addr &= ~1; /* XXX: check exact behaviour if not even */
-    if (addr < 32 ||
-        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
-        return le32_to_cpupu((uint32_t *)(s->mem + addr));
-    } else {
-        return 0xffffffff;
-    }
-}
-
-static inline void ne2000_dma_update(NE2000State *s, int len)
-{
-    s->rsar += len;
-    /* wrap */
-    /* XXX: check what to do if rsar > stop */
-    if (s->rsar == s->stop)
-        s->rsar = s->start;
-
-    if (s->rcnt <= len) {
-        s->rcnt = 0;
-        /* signal end of transfer */
-        s->isr |= ENISR_RDC;
-        ne2000_update_irq(s);
-    } else {
-        s->rcnt -= len;
-    }
-}
-
-static void ne2000_asic_ioport_write(void *opaque, uint32_t addr, uint32_t val)
-{
-    NE2000State *s = opaque;
-
-#ifdef DEBUG_NE2000
-    printf("NE2000: asic write val=0x%04x\n", val);
-#endif
-    if (s->rcnt == 0)
-        return;
-    if (s->dcfg & 0x01) {
-        /* 16 bit access */
-        ne2000_mem_writew(s, s->rsar, val);
-        ne2000_dma_update(s, 2);
-    } else {
-        /* 8 bit access */
-        ne2000_mem_writeb(s, s->rsar, val);
-        ne2000_dma_update(s, 1);
-    }
-}
-
-static uint32_t ne2000_asic_ioport_read(void *opaque, uint32_t addr)
-{
-    NE2000State *s = opaque;
-    int ret;
-
-    if (s->dcfg & 0x01) {
-        /* 16 bit access */
-        ret = ne2000_mem_readw(s, s->rsar);
-        ne2000_dma_update(s, 2);
-    } else {
-        /* 8 bit access */
-        ret = ne2000_mem_readb(s, s->rsar);
-        ne2000_dma_update(s, 1);
-    }
-#ifdef DEBUG_NE2000
-    printf("NE2000: asic read val=0x%04x\n", ret);
-#endif
-    return ret;
-}
-
-static void ne2000_asic_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
-{
-    NE2000State *s = opaque;
-
-#ifdef DEBUG_NE2000
-    printf("NE2000: asic writel val=0x%04x\n", val);
-#endif
-    if (s->rcnt == 0)
-        return;
-    /* 32 bit access */
-    ne2000_mem_writel(s, s->rsar, val);
-    ne2000_dma_update(s, 4);
-}
-
-static uint32_t ne2000_asic_ioport_readl(void *opaque, uint32_t addr)
-{
-    NE2000State *s = opaque;
-    int ret;
-
-    /* 32 bit access */
-    ret = ne2000_mem_readl(s, s->rsar);
-    ne2000_dma_update(s, 4);
-#ifdef DEBUG_NE2000
-    printf("NE2000: asic readl val=0x%04x\n", ret);
-#endif
-    return ret;
-}
-
-static void ne2000_reset_ioport_write(void *opaque, uint32_t addr, uint32_t val)
-{
-    /* nothing to do (end of reset pulse) */
-}
-
-static uint32_t ne2000_reset_ioport_read(void *opaque, uint32_t addr)
-{
-    NE2000State *s = opaque;
-    ne2000_reset(s);
-    return 0;
-}
-
-static int ne2000_post_load(void* opaque, int version_id)
-{
-    NE2000State* s = opaque;
-
-    if (version_id < 2) {
-        s->rxcr = 0x0c;
-    }
-    return 0;
-}
-
-const VMStateDescription vmstate_ne2000 = {
-    .name = "ne2000",
-    .version_id = 2,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .post_load = ne2000_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT8_V(rxcr, NE2000State, 2),
-        VMSTATE_UINT8(cmd, NE2000State),
-        VMSTATE_UINT32(start, NE2000State),
-        VMSTATE_UINT32(stop, NE2000State),
-        VMSTATE_UINT8(boundary, NE2000State),
-        VMSTATE_UINT8(tsr, NE2000State),
-        VMSTATE_UINT8(tpsr, NE2000State),
-        VMSTATE_UINT16(tcnt, NE2000State),
-        VMSTATE_UINT16(rcnt, NE2000State),
-        VMSTATE_UINT32(rsar, NE2000State),
-        VMSTATE_UINT8(rsr, NE2000State),
-        VMSTATE_UINT8(isr, NE2000State),
-        VMSTATE_UINT8(dcfg, NE2000State),
-        VMSTATE_UINT8(imr, NE2000State),
-        VMSTATE_BUFFER(phys, NE2000State),
-        VMSTATE_UINT8(curpag, NE2000State),
-        VMSTATE_BUFFER(mult, NE2000State),
-        VMSTATE_UNUSED(4), /* was irq */
-        VMSTATE_BUFFER(mem, NE2000State),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_pci_ne2000 = {
-    .name = "ne2000",
-    .version_id = 3,
-    .minimum_version_id = 3,
-    .minimum_version_id_old = 3,
-    .fields      = (VMStateField []) {
-        VMSTATE_PCI_DEVICE(dev, PCINE2000State),
-        VMSTATE_STRUCT(ne2000, PCINE2000State, 0, vmstate_ne2000, NE2000State),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static uint64_t ne2000_read(void *opaque, hwaddr addr,
-                            unsigned size)
-{
-    NE2000State *s = opaque;
-
-    if (addr < 0x10 && size == 1) {
-        return ne2000_ioport_read(s, addr);
-    } else if (addr == 0x10) {
-        if (size <= 2) {
-            return ne2000_asic_ioport_read(s, addr);
-        } else {
-            return ne2000_asic_ioport_readl(s, addr);
-        }
-    } else if (addr == 0x1f && size == 1) {
-        return ne2000_reset_ioport_read(s, addr);
-    }
-    return ((uint64_t)1 << (size * 8)) - 1;
-}
-
-static void ne2000_write(void *opaque, hwaddr addr,
-                         uint64_t data, unsigned size)
-{
-    NE2000State *s = opaque;
-
-    if (addr < 0x10 && size == 1) {
-        ne2000_ioport_write(s, addr, data);
-    } else if (addr == 0x10) {
-        if (size <= 2) {
-            ne2000_asic_ioport_write(s, addr, data);
-        } else {
-            ne2000_asic_ioport_writel(s, addr, data);
-        }
-    } else if (addr == 0x1f && size == 1) {
-        ne2000_reset_ioport_write(s, addr, data);
-    }
-}
-
-static const MemoryRegionOps ne2000_ops = {
-    .read = ne2000_read,
-    .write = ne2000_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-/***********************************************************/
-/* PCI NE2000 definitions */
-
-void ne2000_setup_io(NE2000State *s, unsigned size)
-{
-    memory_region_init_io(&s->io, &ne2000_ops, s, "ne2000", size);
-}
-
-static void ne2000_cleanup(NetClientState *nc)
-{
-    NE2000State *s = qemu_get_nic_opaque(nc);
-
-    s->nic = NULL;
-}
-
-static NetClientInfo net_ne2000_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
-    .size = sizeof(NICState),
-    .can_receive = ne2000_can_receive,
-    .receive = ne2000_receive,
-    .cleanup = ne2000_cleanup,
-};
-
-static int pci_ne2000_init(PCIDevice *pci_dev)
-{
-    PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev);
-    NE2000State *s;
-    uint8_t *pci_conf;
-
-    pci_conf = d->dev.config;
-    pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */
-
-    s = &d->ne2000;
-    ne2000_setup_io(s, 0x100);
-    pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io);
-    s->irq = d->dev.irq[0];
-
-    qemu_macaddr_default_if_unset(&s->c.macaddr);
-    ne2000_reset(s);
-
-    s->nic = qemu_new_nic(&net_ne2000_info, &s->c,
-                          object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s);
-    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a);
-
-    add_boot_device_path(s->c.bootindex, &pci_dev->qdev, "/ethernet-phy@0");
-
-    return 0;
-}
-
-static void pci_ne2000_exit(PCIDevice *pci_dev)
-{
-    PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev);
-    NE2000State *s = &d->ne2000;
-
-    memory_region_destroy(&s->io);
-    qemu_del_nic(s->nic);
-}
-
-static Property ne2000_properties[] = {
-    DEFINE_NIC_PROPERTIES(PCINE2000State, ne2000.c),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void ne2000_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = pci_ne2000_init;
-    k->exit = pci_ne2000_exit;
-    k->romfile = "efi-ne2k_pci.rom",
-    k->vendor_id = PCI_VENDOR_ID_REALTEK;
-    k->device_id = PCI_DEVICE_ID_REALTEK_8029;
-    k->class_id = PCI_CLASS_NETWORK_ETHERNET;
-    dc->vmsd = &vmstate_pci_ne2000;
-    dc->props = ne2000_properties;
-}
-
-static const TypeInfo ne2000_info = {
-    .name          = "ne2k_pci",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCINE2000State),
-    .class_init    = ne2000_class_init,
-};
-
-static void ne2000_register_types(void)
-{
-    type_register_static(&ne2000_info);
-}
-
-type_init(ne2000_register_types)
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ad91293fe4acf2d1d4667d768822baa3e0644e78 100644 (file)
@@ -0,0 +1,22 @@
+common-obj-$(CONFIG_DP8393X) += dp8393x.o
+common-obj-$(CONFIG_XEN_BACKEND) += xen_nic.o
+
+# PCI network cards
+common-obj-$(CONFIG_NE2000_PCI) += ne2000.o
+common-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o
+common-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o
+common-obj-$(CONFIG_PCNET_COMMON) += pcnet.o
+common-obj-$(CONFIG_E1000_PCI) += e1000.o
+common-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o
+common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet_tx_pkt.o vmxnet_rx_pkt.o
+common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet3.o
+
+common-obj-$(CONFIG_SMC91C111) += smc91c111.o
+common-obj-$(CONFIG_LAN9118) += lan9118.o
+common-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o
+common-obj-$(CONFIG_OPENCORES_ETH) += opencores_eth.o
+common-obj-$(CONFIG_XGMAC) += xgmac.o
+common-obj-$(CONFIG_MIPSNET) += mipsnet.o
+common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
+
+common-obj-$(CONFIG_CADENCE) += cadence_gem.o
diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c
new file mode 100644 (file)
index 0000000..e177057
--- /dev/null
@@ -0,0 +1,1219 @@
+/*
+ * QEMU Xilinx GEM emulation
+ *
+ * Copyright (c) 2011 Xilinx, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <zlib.h> /* For crc32 */
+
+#include "hw/sysbus.h"
+#include "net/net.h"
+#include "net/checksum.h"
+
+#ifdef CADENCE_GEM_ERR_DEBUG
+#define DB_PRINT(...) do { \
+    fprintf(stderr,  ": %s: ", __func__); \
+    fprintf(stderr, ## __VA_ARGS__); \
+    } while (0);
+#else
+    #define DB_PRINT(...)
+#endif
+
+#define GEM_NWCTRL        (0x00000000/4) /* Network Control reg */
+#define GEM_NWCFG         (0x00000004/4) /* Network Config reg */
+#define GEM_NWSTATUS      (0x00000008/4) /* Network Status reg */
+#define GEM_USERIO        (0x0000000C/4) /* User IO reg */
+#define GEM_DMACFG        (0x00000010/4) /* DMA Control reg */
+#define GEM_TXSTATUS      (0x00000014/4) /* TX Status reg */
+#define GEM_RXQBASE       (0x00000018/4) /* RX Q Base address reg */
+#define GEM_TXQBASE       (0x0000001C/4) /* TX Q Base address reg */
+#define GEM_RXSTATUS      (0x00000020/4) /* RX Status reg */
+#define GEM_ISR           (0x00000024/4) /* Interrupt Status reg */
+#define GEM_IER           (0x00000028/4) /* Interrupt Enable reg */
+#define GEM_IDR           (0x0000002C/4) /* Interrupt Disable reg */
+#define GEM_IMR           (0x00000030/4) /* Interrupt Mask reg */
+#define GEM_PHYMNTNC      (0x00000034/4) /* Phy Maintaince reg */
+#define GEM_RXPAUSE       (0x00000038/4) /* RX Pause Time reg */
+#define GEM_TXPAUSE       (0x0000003C/4) /* TX Pause Time reg */
+#define GEM_TXPARTIALSF   (0x00000040/4) /* TX Partial Store and Forward */
+#define GEM_RXPARTIALSF   (0x00000044/4) /* RX Partial Store and Forward */
+#define GEM_HASHLO        (0x00000080/4) /* Hash Low address reg */
+#define GEM_HASHHI        (0x00000084/4) /* Hash High address reg */
+#define GEM_SPADDR1LO     (0x00000088/4) /* Specific addr 1 low reg */
+#define GEM_SPADDR1HI     (0x0000008C/4) /* Specific addr 1 high reg */
+#define GEM_SPADDR2LO     (0x00000090/4) /* Specific addr 2 low reg */
+#define GEM_SPADDR2HI     (0x00000094/4) /* Specific addr 2 high reg */
+#define GEM_SPADDR3LO     (0x00000098/4) /* Specific addr 3 low reg */
+#define GEM_SPADDR3HI     (0x0000009C/4) /* Specific addr 3 high reg */
+#define GEM_SPADDR4LO     (0x000000A0/4) /* Specific addr 4 low reg */
+#define GEM_SPADDR4HI     (0x000000A4/4) /* Specific addr 4 high reg */
+#define GEM_TIDMATCH1     (0x000000A8/4) /* Type ID1 Match reg */
+#define GEM_TIDMATCH2     (0x000000AC/4) /* Type ID2 Match reg */
+#define GEM_TIDMATCH3     (0x000000B0/4) /* Type ID3 Match reg */
+#define GEM_TIDMATCH4     (0x000000B4/4) /* Type ID4 Match reg */
+#define GEM_WOLAN         (0x000000B8/4) /* Wake on LAN reg */
+#define GEM_IPGSTRETCH    (0x000000BC/4) /* IPG Stretch reg */
+#define GEM_SVLAN         (0x000000C0/4) /* Stacked VLAN reg */
+#define GEM_MODID         (0x000000FC/4) /* Module ID reg */
+#define GEM_OCTTXLO       (0x00000100/4) /* Octects transmitted Low reg */
+#define GEM_OCTTXHI       (0x00000104/4) /* Octects transmitted High reg */
+#define GEM_TXCNT         (0x00000108/4) /* Error-free Frames transmitted */
+#define GEM_TXBCNT        (0x0000010C/4) /* Error-free Broadcast Frames */
+#define GEM_TXMCNT        (0x00000110/4) /* Error-free Multicast Frame */
+#define GEM_TXPAUSECNT    (0x00000114/4) /* Pause Frames Transmitted */
+#define GEM_TX64CNT       (0x00000118/4) /* Error-free 64 TX */
+#define GEM_TX65CNT       (0x0000011C/4) /* Error-free 65-127 TX */
+#define GEM_TX128CNT      (0x00000120/4) /* Error-free 128-255 TX */
+#define GEM_TX256CNT      (0x00000124/4) /* Error-free 256-511 */
+#define GEM_TX512CNT      (0x00000128/4) /* Error-free 512-1023 TX */
+#define GEM_TX1024CNT     (0x0000012C/4) /* Error-free 1024-1518 TX */
+#define GEM_TX1519CNT     (0x00000130/4) /* Error-free larger than 1519 TX */
+#define GEM_TXURUNCNT     (0x00000134/4) /* TX under run error counter */
+#define GEM_SINGLECOLLCNT (0x00000138/4) /* Single Collision Frames */
+#define GEM_MULTCOLLCNT   (0x0000013C/4) /* Multiple Collision Frames */
+#define GEM_EXCESSCOLLCNT (0x00000140/4) /* Excessive Collision Frames */
+#define GEM_LATECOLLCNT   (0x00000144/4) /* Late Collision Frames */
+#define GEM_DEFERTXCNT    (0x00000148/4) /* Deferred Transmission Frames */
+#define GEM_CSENSECNT     (0x0000014C/4) /* Carrier Sense Error Counter */
+#define GEM_OCTRXLO       (0x00000150/4) /* Octects Received register Low */
+#define GEM_OCTRXHI       (0x00000154/4) /* Octects Received register High */
+#define GEM_RXCNT         (0x00000158/4) /* Error-free Frames Received */
+#define GEM_RXBROADCNT    (0x0000015C/4) /* Error-free Broadcast Frames RX */
+#define GEM_RXMULTICNT    (0x00000160/4) /* Error-free Multicast Frames RX */
+#define GEM_RXPAUSECNT    (0x00000164/4) /* Pause Frames Received Counter */
+#define GEM_RX64CNT       (0x00000168/4) /* Error-free 64 byte Frames RX */
+#define GEM_RX65CNT       (0x0000016C/4) /* Error-free 65-127B Frames RX */
+#define GEM_RX128CNT      (0x00000170/4) /* Error-free 128-255B Frames RX */
+#define GEM_RX256CNT      (0x00000174/4) /* Error-free 256-512B Frames RX */
+#define GEM_RX512CNT      (0x00000178/4) /* Error-free 512-1023B Frames RX */
+#define GEM_RX1024CNT     (0x0000017C/4) /* Error-free 1024-1518B Frames RX */
+#define GEM_RX1519CNT     (0x00000180/4) /* Error-free 1519-max Frames RX */
+#define GEM_RXUNDERCNT    (0x00000184/4) /* Undersize Frames Received */
+#define GEM_RXOVERCNT     (0x00000188/4) /* Oversize Frames Received */
+#define GEM_RXJABCNT      (0x0000018C/4) /* Jabbers Received Counter */
+#define GEM_RXFCSCNT      (0x00000190/4) /* Frame Check seq. Error Counter */
+#define GEM_RXLENERRCNT   (0x00000194/4) /* Length Field Error Counter */
+#define GEM_RXSYMERRCNT   (0x00000198/4) /* Symbol Error Counter */
+#define GEM_RXALIGNERRCNT (0x0000019C/4) /* Alignment Error Counter */
+#define GEM_RXRSCERRCNT   (0x000001A0/4) /* Receive Resource Error Counter */
+#define GEM_RXORUNCNT     (0x000001A4/4) /* Receive Overrun Counter */
+#define GEM_RXIPCSERRCNT  (0x000001A8/4) /* IP header Checksum Error Counter */
+#define GEM_RXTCPCCNT     (0x000001AC/4) /* TCP Checksum Error Counter */
+#define GEM_RXUDPCCNT     (0x000001B0/4) /* UDP Checksum Error Counter */
+
+#define GEM_1588S         (0x000001D0/4) /* 1588 Timer Seconds */
+#define GEM_1588NS        (0x000001D4/4) /* 1588 Timer Nanoseconds */
+#define GEM_1588ADJ       (0x000001D8/4) /* 1588 Timer Adjust */
+#define GEM_1588INC       (0x000001DC/4) /* 1588 Timer Increment */
+#define GEM_PTPETXS       (0x000001E0/4) /* PTP Event Frame Transmitted (s) */
+#define GEM_PTPETXNS      (0x000001E4/4) /* PTP Event Frame Transmitted (ns) */
+#define GEM_PTPERXS       (0x000001E8/4) /* PTP Event Frame Received (s) */
+#define GEM_PTPERXNS      (0x000001EC/4) /* PTP Event Frame Received (ns) */
+#define GEM_PTPPTXS       (0x000001E0/4) /* PTP Peer Frame Transmitted (s) */
+#define GEM_PTPPTXNS      (0x000001E4/4) /* PTP Peer Frame Transmitted (ns) */
+#define GEM_PTPPRXS       (0x000001E8/4) /* PTP Peer Frame Received (s) */
+#define GEM_PTPPRXNS      (0x000001EC/4) /* PTP Peer Frame Received (ns) */
+
+/* Design Configuration Registers */
+#define GEM_DESCONF       (0x00000280/4)
+#define GEM_DESCONF2      (0x00000284/4)
+#define GEM_DESCONF3      (0x00000288/4)
+#define GEM_DESCONF4      (0x0000028C/4)
+#define GEM_DESCONF5      (0x00000290/4)
+#define GEM_DESCONF6      (0x00000294/4)
+#define GEM_DESCONF7      (0x00000298/4)
+
+#define GEM_MAXREG        (0x00000640/4) /* Last valid GEM address */
+
+/*****************************************/
+#define GEM_NWCTRL_TXSTART     0x00000200 /* Transmit Enable */
+#define GEM_NWCTRL_TXENA       0x00000008 /* Transmit Enable */
+#define GEM_NWCTRL_RXENA       0x00000004 /* Receive Enable */
+#define GEM_NWCTRL_LOCALLOOP   0x00000002 /* Local Loopback */
+
+#define GEM_NWCFG_STRIP_FCS    0x00020000 /* Strip FCS field */
+#define GEM_NWCFG_LERR_DISC    0x00010000 /* Discard RX frames with lenth err */
+#define GEM_NWCFG_BUFF_OFST_M  0x0000C000 /* Receive buffer offset mask */
+#define GEM_NWCFG_BUFF_OFST_S  14         /* Receive buffer offset shift */
+#define GEM_NWCFG_UCAST_HASH   0x00000080 /* accept unicast if hash match */
+#define GEM_NWCFG_MCAST_HASH   0x00000040 /* accept multicast if hash match */
+#define GEM_NWCFG_BCAST_REJ    0x00000020 /* Reject broadcast packets */
+#define GEM_NWCFG_PROMISC      0x00000010 /* Accept all packets */
+
+#define GEM_DMACFG_RBUFSZ_M    0x007F0000 /* DMA RX Buffer Size mask */
+#define GEM_DMACFG_RBUFSZ_S    16         /* DMA RX Buffer Size shift */
+#define GEM_DMACFG_RBUFSZ_MUL  64         /* DMA RX Buffer Size multiplier */
+#define GEM_DMACFG_TXCSUM_OFFL 0x00000800 /* Transmit checksum offload */
+
+#define GEM_TXSTATUS_TXCMPL    0x00000020 /* Transmit Complete */
+#define GEM_TXSTATUS_USED      0x00000001 /* sw owned descriptor encountered */
+
+#define GEM_RXSTATUS_FRMRCVD   0x00000002 /* Frame received */
+#define GEM_RXSTATUS_NOBUF     0x00000001 /* Buffer unavailable */
+
+/* GEM_ISR GEM_IER GEM_IDR GEM_IMR */
+#define GEM_INT_TXCMPL        0x00000080 /* Transmit Complete */
+#define GEM_INT_TXUSED         0x00000008
+#define GEM_INT_RXUSED         0x00000004
+#define GEM_INT_RXCMPL        0x00000002
+
+#define GEM_PHYMNTNC_OP_R      0x20000000 /* read operation */
+#define GEM_PHYMNTNC_OP_W      0x10000000 /* write operation */
+#define GEM_PHYMNTNC_ADDR      0x0F800000 /* Address bits */
+#define GEM_PHYMNTNC_ADDR_SHFT 23
+#define GEM_PHYMNTNC_REG       0x007C0000 /* register bits */
+#define GEM_PHYMNTNC_REG_SHIFT 18
+
+/* Marvell PHY definitions */
+#define BOARD_PHY_ADDRESS    23 /* PHY address we will emulate a device at */
+
+#define PHY_REG_CONTROL      0
+#define PHY_REG_STATUS       1
+#define PHY_REG_PHYID1       2
+#define PHY_REG_PHYID2       3
+#define PHY_REG_ANEGADV      4
+#define PHY_REG_LINKPABIL    5
+#define PHY_REG_ANEGEXP      6
+#define PHY_REG_NEXTP        7
+#define PHY_REG_LINKPNEXTP   8
+#define PHY_REG_100BTCTRL    9
+#define PHY_REG_1000BTSTAT   10
+#define PHY_REG_EXTSTAT      15
+#define PHY_REG_PHYSPCFC_CTL 16
+#define PHY_REG_PHYSPCFC_ST  17
+#define PHY_REG_INT_EN       18
+#define PHY_REG_INT_ST       19
+#define PHY_REG_EXT_PHYSPCFC_CTL  20
+#define PHY_REG_RXERR        21
+#define PHY_REG_EACD         22
+#define PHY_REG_LED          24
+#define PHY_REG_LED_OVRD     25
+#define PHY_REG_EXT_PHYSPCFC_CTL2 26
+#define PHY_REG_EXT_PHYSPCFC_ST   27
+#define PHY_REG_CABLE_DIAG   28
+
+#define PHY_REG_CONTROL_RST  0x8000
+#define PHY_REG_CONTROL_LOOP 0x4000
+#define PHY_REG_CONTROL_ANEG 0x1000
+
+#define PHY_REG_STATUS_LINK     0x0004
+#define PHY_REG_STATUS_ANEGCMPL 0x0020
+
+#define PHY_REG_INT_ST_ANEGCMPL 0x0800
+#define PHY_REG_INT_ST_LINKC    0x0400
+#define PHY_REG_INT_ST_ENERGY   0x0010
+
+/***********************************************************************/
+#define GEM_RX_REJECT  1
+#define GEM_RX_ACCEPT  0
+
+/***********************************************************************/
+
+#define DESC_1_USED 0x80000000
+#define DESC_1_LENGTH 0x00001FFF
+
+#define DESC_1_TX_WRAP 0x40000000
+#define DESC_1_TX_LAST 0x00008000
+
+#define DESC_0_RX_WRAP 0x00000002
+#define DESC_0_RX_OWNERSHIP 0x00000001
+
+#define DESC_1_RX_SOF 0x00004000
+#define DESC_1_RX_EOF 0x00008000
+
+static inline unsigned tx_desc_get_buffer(unsigned *desc)
+{
+    return desc[0];
+}
+
+static inline unsigned tx_desc_get_used(unsigned *desc)
+{
+    return (desc[1] & DESC_1_USED) ? 1 : 0;
+}
+
+static inline void tx_desc_set_used(unsigned *desc)
+{
+    desc[1] |= DESC_1_USED;
+}
+
+static inline unsigned tx_desc_get_wrap(unsigned *desc)
+{
+    return (desc[1] & DESC_1_TX_WRAP) ? 1 : 0;
+}
+
+static inline unsigned tx_desc_get_last(unsigned *desc)
+{
+    return (desc[1] & DESC_1_TX_LAST) ? 1 : 0;
+}
+
+static inline unsigned tx_desc_get_length(unsigned *desc)
+{
+    return desc[1] & DESC_1_LENGTH;
+}
+
+static inline void print_gem_tx_desc(unsigned *desc)
+{
+    DB_PRINT("TXDESC:\n");
+    DB_PRINT("bufaddr: 0x%08x\n", *desc);
+    DB_PRINT("used_hw: %d\n", tx_desc_get_used(desc));
+    DB_PRINT("wrap:    %d\n", tx_desc_get_wrap(desc));
+    DB_PRINT("last:    %d\n", tx_desc_get_last(desc));
+    DB_PRINT("length:  %d\n", tx_desc_get_length(desc));
+}
+
+static inline unsigned rx_desc_get_buffer(unsigned *desc)
+{
+    return desc[0] & ~0x3UL;
+}
+
+static inline unsigned rx_desc_get_wrap(unsigned *desc)
+{
+    return desc[0] & DESC_0_RX_WRAP ? 1 : 0;
+}
+
+static inline unsigned rx_desc_get_ownership(unsigned *desc)
+{
+    return desc[0] & DESC_0_RX_OWNERSHIP ? 1 : 0;
+}
+
+static inline void rx_desc_set_ownership(unsigned *desc)
+{
+    desc[0] |= DESC_0_RX_OWNERSHIP;
+}
+
+static inline void rx_desc_set_sof(unsigned *desc)
+{
+    desc[1] |= DESC_1_RX_SOF;
+}
+
+static inline void rx_desc_set_eof(unsigned *desc)
+{
+    desc[1] |= DESC_1_RX_EOF;
+}
+
+static inline void rx_desc_set_length(unsigned *desc, unsigned len)
+{
+    desc[1] &= ~DESC_1_LENGTH;
+    desc[1] |= len;
+}
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    NICState *nic;
+    NICConf conf;
+    qemu_irq irq;
+
+    /* GEM registers backing store */
+    uint32_t regs[GEM_MAXREG];
+    /* Mask of register bits which are write only */
+    uint32_t regs_wo[GEM_MAXREG];
+    /* Mask of register bits which are read only */
+    uint32_t regs_ro[GEM_MAXREG];
+    /* Mask of register bits which are clear on read */
+    uint32_t regs_rtc[GEM_MAXREG];
+    /* Mask of register bits which are write 1 to clear */
+    uint32_t regs_w1c[GEM_MAXREG];
+
+    /* PHY registers backing store */
+    uint16_t phy_regs[32];
+
+    uint8_t phy_loop; /* Are we in phy loopback? */
+
+    /* The current DMA descriptor pointers */
+    uint32_t rx_desc_addr;
+    uint32_t tx_desc_addr;
+
+} GemState;
+
+/* The broadcast MAC address: 0xFFFFFFFFFFFF */
+const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+/*
+ * gem_init_register_masks:
+ * One time initialization.
+ * Set masks to identify which register bits have magical clear properties
+ */
+static void gem_init_register_masks(GemState *s)
+{
+    /* Mask of register bits which are read only*/
+    memset(&s->regs_ro[0], 0, sizeof(s->regs_ro));
+    s->regs_ro[GEM_NWCTRL]   = 0xFFF80000;
+    s->regs_ro[GEM_NWSTATUS] = 0xFFFFFFFF;
+    s->regs_ro[GEM_DMACFG]   = 0xFE00F000;
+    s->regs_ro[GEM_TXSTATUS] = 0xFFFFFE08;
+    s->regs_ro[GEM_RXQBASE]  = 0x00000003;
+    s->regs_ro[GEM_TXQBASE]  = 0x00000003;
+    s->regs_ro[GEM_RXSTATUS] = 0xFFFFFFF0;
+    s->regs_ro[GEM_ISR]      = 0xFFFFFFFF;
+    s->regs_ro[GEM_IMR]      = 0xFFFFFFFF;
+    s->regs_ro[GEM_MODID]    = 0xFFFFFFFF;
+
+    /* Mask of register bits which are clear on read */
+    memset(&s->regs_rtc[0], 0, sizeof(s->regs_rtc));
+    s->regs_rtc[GEM_ISR]      = 0xFFFFFFFF;
+
+    /* Mask of register bits which are write 1 to clear */
+    memset(&s->regs_w1c[0], 0, sizeof(s->regs_w1c));
+    s->regs_w1c[GEM_TXSTATUS] = 0x000001F7;
+    s->regs_w1c[GEM_RXSTATUS] = 0x0000000F;
+
+    /* Mask of register bits which are write only */
+    memset(&s->regs_wo[0], 0, sizeof(s->regs_wo));
+    s->regs_wo[GEM_NWCTRL]   = 0x00073E60;
+    s->regs_wo[GEM_IER]      = 0x07FFFFFF;
+    s->regs_wo[GEM_IDR]      = 0x07FFFFFF;
+}
+
+/*
+ * phy_update_link:
+ * Make the emulated PHY link state match the QEMU "interface" state.
+ */
+static void phy_update_link(GemState *s)
+{
+    DB_PRINT("down %d\n", qemu_get_queue(s->nic)->link_down);
+
+    /* Autonegotiation status mirrors link status.  */
+    if (qemu_get_queue(s->nic)->link_down) {
+        s->phy_regs[PHY_REG_STATUS] &= ~(PHY_REG_STATUS_ANEGCMPL |
+                                         PHY_REG_STATUS_LINK);
+        s->phy_regs[PHY_REG_INT_ST] |= PHY_REG_INT_ST_LINKC;
+    } else {
+        s->phy_regs[PHY_REG_STATUS] |= (PHY_REG_STATUS_ANEGCMPL |
+                                         PHY_REG_STATUS_LINK);
+        s->phy_regs[PHY_REG_INT_ST] |= (PHY_REG_INT_ST_LINKC |
+                                        PHY_REG_INT_ST_ANEGCMPL |
+                                        PHY_REG_INT_ST_ENERGY);
+    }
+}
+
+static int gem_can_receive(NetClientState *nc)
+{
+    GemState *s;
+
+    s = qemu_get_nic_opaque(nc);
+
+    DB_PRINT("\n");
+
+    /* Do nothing if receive is not enabled. */
+    if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_RXENA)) {
+        return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * gem_update_int_status:
+ * Raise or lower interrupt based on current status.
+ */
+static void gem_update_int_status(GemState *s)
+{
+    if (s->regs[GEM_ISR]) {
+        DB_PRINT("asserting int. (0x%08x)\n", s->regs[GEM_ISR]);
+        qemu_set_irq(s->irq, 1);
+    }
+}
+
+/*
+ * gem_receive_updatestats:
+ * Increment receive statistics.
+ */
+static void gem_receive_updatestats(GemState *s, const uint8_t *packet,
+                                    unsigned bytes)
+{
+    uint64_t octets;
+
+    /* Total octets (bytes) received */
+    octets = ((uint64_t)(s->regs[GEM_OCTRXLO]) << 32) |
+             s->regs[GEM_OCTRXHI];
+    octets += bytes;
+    s->regs[GEM_OCTRXLO] = octets >> 32;
+    s->regs[GEM_OCTRXHI] = octets;
+
+    /* Error-free Frames received */
+    s->regs[GEM_RXCNT]++;
+
+    /* Error-free Broadcast Frames counter */
+    if (!memcmp(packet, broadcast_addr, 6)) {
+        s->regs[GEM_RXBROADCNT]++;
+    }
+
+    /* Error-free Multicast Frames counter */
+    if (packet[0] == 0x01) {
+        s->regs[GEM_RXMULTICNT]++;
+    }
+
+    if (bytes <= 64) {
+        s->regs[GEM_RX64CNT]++;
+    } else if (bytes <= 127) {
+        s->regs[GEM_RX65CNT]++;
+    } else if (bytes <= 255) {
+        s->regs[GEM_RX128CNT]++;
+    } else if (bytes <= 511) {
+        s->regs[GEM_RX256CNT]++;
+    } else if (bytes <= 1023) {
+        s->regs[GEM_RX512CNT]++;
+    } else if (bytes <= 1518) {
+        s->regs[GEM_RX1024CNT]++;
+    } else {
+        s->regs[GEM_RX1519CNT]++;
+    }
+}
+
+/*
+ * Get the MAC Address bit from the specified position
+ */
+static unsigned get_bit(const uint8_t *mac, unsigned bit)
+{
+    unsigned byte;
+
+    byte = mac[bit / 8];
+    byte >>= (bit & 0x7);
+    byte &= 1;
+
+    return byte;
+}
+
+/*
+ * Calculate a GEM MAC Address hash index
+ */
+static unsigned calc_mac_hash(const uint8_t *mac)
+{
+    int index_bit, mac_bit;
+    unsigned hash_index;
+
+    hash_index = 0;
+    mac_bit = 5;
+    for (index_bit = 5; index_bit >= 0; index_bit--) {
+        hash_index |= (get_bit(mac,  mac_bit) ^
+                               get_bit(mac, mac_bit + 6) ^
+                               get_bit(mac, mac_bit + 12) ^
+                               get_bit(mac, mac_bit + 18) ^
+                               get_bit(mac, mac_bit + 24) ^
+                               get_bit(mac, mac_bit + 30) ^
+                               get_bit(mac, mac_bit + 36) ^
+                               get_bit(mac, mac_bit + 42)) << index_bit;
+        mac_bit--;
+    }
+
+    return hash_index;
+}
+
+/*
+ * gem_mac_address_filter:
+ * Accept or reject this destination address?
+ * Returns:
+ * GEM_RX_REJECT: reject
+ * GEM_RX_ACCEPT: accept
+ */
+static int gem_mac_address_filter(GemState *s, const uint8_t *packet)
+{
+    uint8_t *gem_spaddr;
+    int i;
+
+    /* Promiscuous mode? */
+    if (s->regs[GEM_NWCFG] & GEM_NWCFG_PROMISC) {
+        return GEM_RX_ACCEPT;
+    }
+
+    if (!memcmp(packet, broadcast_addr, 6)) {
+        /* Reject broadcast packets? */
+        if (s->regs[GEM_NWCFG] & GEM_NWCFG_BCAST_REJ) {
+            return GEM_RX_REJECT;
+        }
+        return GEM_RX_ACCEPT;
+    }
+
+    /* Accept packets -w- hash match? */
+    if ((packet[0] == 0x01 && (s->regs[GEM_NWCFG] & GEM_NWCFG_MCAST_HASH)) ||
+        (packet[0] != 0x01 && (s->regs[GEM_NWCFG] & GEM_NWCFG_UCAST_HASH))) {
+        unsigned hash_index;
+
+        hash_index = calc_mac_hash(packet);
+        if (hash_index < 32) {
+            if (s->regs[GEM_HASHLO] & (1<<hash_index)) {
+                return GEM_RX_ACCEPT;
+            }
+        } else {
+            hash_index -= 32;
+            if (s->regs[GEM_HASHHI] & (1<<hash_index)) {
+                return GEM_RX_ACCEPT;
+            }
+        }
+    }
+
+    /* Check all 4 specific addresses */
+    gem_spaddr = (uint8_t *)&(s->regs[GEM_SPADDR1LO]);
+    for (i = 0; i < 4; i++) {
+        if (!memcmp(packet, gem_spaddr, 6)) {
+            return GEM_RX_ACCEPT;
+        }
+
+        gem_spaddr += 8;
+    }
+
+    /* No address match; reject the packet */
+    return GEM_RX_REJECT;
+}
+
+/*
+ * gem_receive:
+ * Fit a packet handed to us by QEMU into the receive descriptor ring.
+ */
+static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+    unsigned    desc[2];
+    hwaddr packet_desc_addr, last_desc_addr;
+    GemState *s;
+    unsigned   rxbufsize, bytes_to_copy;
+    unsigned   rxbuf_offset;
+    uint8_t    rxbuf[2048];
+    uint8_t   *rxbuf_ptr;
+
+    s = qemu_get_nic_opaque(nc);
+
+    /* Do nothing if receive is not enabled. */
+    if (!gem_can_receive(nc)) {
+        return -1;
+    }
+
+    /* Is this destination MAC address "for us" ? */
+    if (gem_mac_address_filter(s, buf) == GEM_RX_REJECT) {
+        return -1;
+    }
+
+    /* Discard packets with receive length error enabled ? */
+    if (s->regs[GEM_NWCFG] & GEM_NWCFG_LERR_DISC) {
+        unsigned type_len;
+
+        /* Fish the ethertype / length field out of the RX packet */
+        type_len = buf[12] << 8 | buf[13];
+        /* It is a length field, not an ethertype */
+        if (type_len < 0x600) {
+            if (size < type_len) {
+                /* discard */
+                return -1;
+            }
+        }
+    }
+
+    /*
+     * Determine configured receive buffer offset (probably 0)
+     */
+    rxbuf_offset = (s->regs[GEM_NWCFG] & GEM_NWCFG_BUFF_OFST_M) >>
+                   GEM_NWCFG_BUFF_OFST_S;
+
+    /* The configure size of each receive buffer.  Determines how many
+     * buffers needed to hold this packet.
+     */
+    rxbufsize = ((s->regs[GEM_DMACFG] & GEM_DMACFG_RBUFSZ_M) >>
+                 GEM_DMACFG_RBUFSZ_S) * GEM_DMACFG_RBUFSZ_MUL;
+    bytes_to_copy = size;
+
+    /* Strip of FCS field ? (usually yes) */
+    if (s->regs[GEM_NWCFG] & GEM_NWCFG_STRIP_FCS) {
+        rxbuf_ptr = (void *)buf;
+    } else {
+        unsigned crc_val;
+        int      crc_offset;
+
+        /* The application wants the FCS field, which QEMU does not provide.
+         * We must try and caclculate one.
+         */
+
+        memcpy(rxbuf, buf, size);
+        memset(rxbuf + size, 0, sizeof(rxbuf) - size);
+        rxbuf_ptr = rxbuf;
+        crc_val = cpu_to_le32(crc32(0, rxbuf, MAX(size, 60)));
+        if (size < 60) {
+            crc_offset = 60;
+        } else {
+            crc_offset = size;
+        }
+        memcpy(rxbuf + crc_offset, &crc_val, sizeof(crc_val));
+
+        bytes_to_copy += 4;
+        size += 4;
+    }
+
+    /* Pad to minimum length */
+    if (size < 64) {
+        size = 64;
+    }
+
+    DB_PRINT("config bufsize: %d packet size: %ld\n", rxbufsize, size);
+
+    packet_desc_addr = s->rx_desc_addr;
+    while (1) {
+        DB_PRINT("read descriptor 0x%x\n", (unsigned)packet_desc_addr);
+        /* read current descriptor */
+        cpu_physical_memory_read(packet_desc_addr,
+                                 (uint8_t *)&desc[0], sizeof(desc));
+
+        /* Descriptor owned by software ? */
+        if (rx_desc_get_ownership(desc) == 1) {
+            DB_PRINT("descriptor 0x%x owned by sw.\n",
+                     (unsigned)packet_desc_addr);
+            s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_NOBUF;
+            s->regs[GEM_ISR] |= GEM_INT_RXUSED & ~(s->regs[GEM_IMR]);
+            /* Handle interrupt consequences */
+            gem_update_int_status(s);
+            return -1;
+        }
+
+        DB_PRINT("copy %d bytes to 0x%x\n", MIN(bytes_to_copy, rxbufsize),
+                rx_desc_get_buffer(desc));
+
+        /*
+         * Let's have QEMU lend a helping hand.
+         */
+        if (rx_desc_get_buffer(desc) == 0) {
+            DB_PRINT("Invalid RX buffer (NULL) for descriptor 0x%x\n",
+                     (unsigned)packet_desc_addr);
+            break;
+        }
+
+        /* Copy packet data to emulated DMA buffer */
+        cpu_physical_memory_write(rx_desc_get_buffer(desc) + rxbuf_offset,
+                                  rxbuf_ptr, MIN(bytes_to_copy, rxbufsize));
+        bytes_to_copy -= MIN(bytes_to_copy, rxbufsize);
+        rxbuf_ptr += MIN(bytes_to_copy, rxbufsize);
+        if (bytes_to_copy == 0) {
+            break;
+        }
+
+        /* Next descriptor */
+        if (rx_desc_get_wrap(desc)) {
+            packet_desc_addr = s->regs[GEM_RXQBASE];
+        } else {
+            packet_desc_addr += 8;
+        }
+    }
+
+    DB_PRINT("set length: %ld, EOF on descriptor 0x%x\n", size,
+            (unsigned)packet_desc_addr);
+
+    /* Update last descriptor with EOF and total length */
+    rx_desc_set_eof(desc);
+    rx_desc_set_length(desc, size);
+    cpu_physical_memory_write(packet_desc_addr,
+                              (uint8_t *)&desc[0], sizeof(desc));
+
+    /* Advance RX packet descriptor Q */
+    last_desc_addr = packet_desc_addr;
+    packet_desc_addr = s->rx_desc_addr;
+    s->rx_desc_addr = last_desc_addr;
+    if (rx_desc_get_wrap(desc)) {
+        s->rx_desc_addr = s->regs[GEM_RXQBASE];
+        DB_PRINT("wrapping RX descriptor list\n");
+    } else {
+        DB_PRINT("incrementing RX descriptor list\n");
+        s->rx_desc_addr += 8;
+    }
+
+    DB_PRINT("set SOF, OWN on descriptor 0x%08x\n", (unsigned)packet_desc_addr);
+
+    /* Count it */
+    gem_receive_updatestats(s, buf, size);
+
+    /* Update first descriptor (which could also be the last) */
+    /* read descriptor */
+    cpu_physical_memory_read(packet_desc_addr,
+                             (uint8_t *)&desc[0], sizeof(desc));
+    rx_desc_set_sof(desc);
+    rx_desc_set_ownership(desc);
+    cpu_physical_memory_write(packet_desc_addr,
+                              (uint8_t *)&desc[0], sizeof(desc));
+
+    s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_FRMRCVD;
+    s->regs[GEM_ISR] |= GEM_INT_RXCMPL & ~(s->regs[GEM_IMR]);
+
+    /* Handle interrupt consequences */
+    gem_update_int_status(s);
+
+    return size;
+}
+
+/*
+ * gem_transmit_updatestats:
+ * Increment transmit statistics.
+ */
+static void gem_transmit_updatestats(GemState *s, const uint8_t *packet,
+                                     unsigned bytes)
+{
+    uint64_t octets;
+
+    /* Total octets (bytes) transmitted */
+    octets = ((uint64_t)(s->regs[GEM_OCTTXLO]) << 32) |
+             s->regs[GEM_OCTTXHI];
+    octets += bytes;
+    s->regs[GEM_OCTTXLO] = octets >> 32;
+    s->regs[GEM_OCTTXHI] = octets;
+
+    /* Error-free Frames transmitted */
+    s->regs[GEM_TXCNT]++;
+
+    /* Error-free Broadcast Frames counter */
+    if (!memcmp(packet, broadcast_addr, 6)) {
+        s->regs[GEM_TXBCNT]++;
+    }
+
+    /* Error-free Multicast Frames counter */
+    if (packet[0] == 0x01) {
+        s->regs[GEM_TXMCNT]++;
+    }
+
+    if (bytes <= 64) {
+        s->regs[GEM_TX64CNT]++;
+    } else if (bytes <= 127) {
+        s->regs[GEM_TX65CNT]++;
+    } else if (bytes <= 255) {
+        s->regs[GEM_TX128CNT]++;
+    } else if (bytes <= 511) {
+        s->regs[GEM_TX256CNT]++;
+    } else if (bytes <= 1023) {
+        s->regs[GEM_TX512CNT]++;
+    } else if (bytes <= 1518) {
+        s->regs[GEM_TX1024CNT]++;
+    } else {
+        s->regs[GEM_TX1519CNT]++;
+    }
+}
+
+/*
+ * gem_transmit:
+ * Fish packets out of the descriptor ring and feed them to QEMU
+ */
+static void gem_transmit(GemState *s)
+{
+    unsigned    desc[2];
+    hwaddr packet_desc_addr;
+    uint8_t     tx_packet[2048];
+    uint8_t     *p;
+    unsigned    total_bytes;
+
+    /* Do nothing if transmit is not enabled. */
+    if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_TXENA)) {
+        return;
+    }
+
+    DB_PRINT("\n");
+
+    /* The packet we will hand off to qemu.
+     * Packets scattered across multiple descriptors are gathered to this
+     * one contiguous buffer first.
+     */
+    p = tx_packet;
+    total_bytes = 0;
+
+    /* read current descriptor */
+    packet_desc_addr = s->tx_desc_addr;
+    cpu_physical_memory_read(packet_desc_addr,
+                             (uint8_t *)&desc[0], sizeof(desc));
+    /* Handle all descriptors owned by hardware */
+    while (tx_desc_get_used(desc) == 0) {
+
+        /* Do nothing if transmit is not enabled. */
+        if (!(s->regs[GEM_NWCTRL] & GEM_NWCTRL_TXENA)) {
+            return;
+        }
+        print_gem_tx_desc(desc);
+
+        /* The real hardware would eat this (and possibly crash).
+         * For QEMU let's lend a helping hand.
+         */
+        if ((tx_desc_get_buffer(desc) == 0) ||
+            (tx_desc_get_length(desc) == 0)) {
+            DB_PRINT("Invalid TX descriptor @ 0x%x\n",
+                     (unsigned)packet_desc_addr);
+            break;
+        }
+
+        /* Gather this fragment of the packet from "dma memory" to our contig.
+         * buffer.
+         */
+        cpu_physical_memory_read(tx_desc_get_buffer(desc), p,
+                                 tx_desc_get_length(desc));
+        p += tx_desc_get_length(desc);
+        total_bytes += tx_desc_get_length(desc);
+
+        /* Last descriptor for this packet; hand the whole thing off */
+        if (tx_desc_get_last(desc)) {
+            /* Modify the 1st descriptor of this packet to be owned by
+             * the processor.
+             */
+            cpu_physical_memory_read(s->tx_desc_addr,
+                                     (uint8_t *)&desc[0], sizeof(desc));
+            tx_desc_set_used(desc);
+            cpu_physical_memory_write(s->tx_desc_addr,
+                                      (uint8_t *)&desc[0], sizeof(desc));
+            /* Advance the hardare current descriptor past this packet */
+            if (tx_desc_get_wrap(desc)) {
+                s->tx_desc_addr = s->regs[GEM_TXQBASE];
+            } else {
+                s->tx_desc_addr = packet_desc_addr + 8;
+            }
+            DB_PRINT("TX descriptor next: 0x%08x\n", s->tx_desc_addr);
+
+            s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_TXCMPL;
+            s->regs[GEM_ISR] |= GEM_INT_TXCMPL & ~(s->regs[GEM_IMR]);
+
+            /* Handle interrupt consequences */
+            gem_update_int_status(s);
+
+            /* Is checksum offload enabled? */
+            if (s->regs[GEM_DMACFG] & GEM_DMACFG_TXCSUM_OFFL) {
+                net_checksum_calculate(tx_packet, total_bytes);
+            }
+
+            /* Update MAC statistics */
+            gem_transmit_updatestats(s, tx_packet, total_bytes);
+
+            /* Send the packet somewhere */
+            if (s->phy_loop) {
+                gem_receive(qemu_get_queue(s->nic), tx_packet, total_bytes);
+            } else {
+                qemu_send_packet(qemu_get_queue(s->nic), tx_packet,
+                                 total_bytes);
+            }
+
+            /* Prepare for next packet */
+            p = tx_packet;
+            total_bytes = 0;
+        }
+
+        /* read next descriptor */
+        if (tx_desc_get_wrap(desc)) {
+            packet_desc_addr = s->regs[GEM_TXQBASE];
+        } else {
+            packet_desc_addr += 8;
+        }
+        cpu_physical_memory_read(packet_desc_addr,
+                                 (uint8_t *)&desc[0], sizeof(desc));
+    }
+
+    if (tx_desc_get_used(desc)) {
+        s->regs[GEM_TXSTATUS] |= GEM_TXSTATUS_USED;
+        s->regs[GEM_ISR] |= GEM_INT_TXUSED & ~(s->regs[GEM_IMR]);
+        gem_update_int_status(s);
+    }
+}
+
+static void gem_phy_reset(GemState *s)
+{
+    memset(&s->phy_regs[0], 0, sizeof(s->phy_regs));
+    s->phy_regs[PHY_REG_CONTROL] = 0x1140;
+    s->phy_regs[PHY_REG_STATUS] = 0x7969;
+    s->phy_regs[PHY_REG_PHYID1] = 0x0141;
+    s->phy_regs[PHY_REG_PHYID2] = 0x0CC2;
+    s->phy_regs[PHY_REG_ANEGADV] = 0x01E1;
+    s->phy_regs[PHY_REG_LINKPABIL] = 0xCDE1;
+    s->phy_regs[PHY_REG_ANEGEXP] = 0x000F;
+    s->phy_regs[PHY_REG_NEXTP] = 0x2001;
+    s->phy_regs[PHY_REG_LINKPNEXTP] = 0x40E6;
+    s->phy_regs[PHY_REG_100BTCTRL] = 0x0300;
+    s->phy_regs[PHY_REG_1000BTSTAT] = 0x7C00;
+    s->phy_regs[PHY_REG_EXTSTAT] = 0x3000;
+    s->phy_regs[PHY_REG_PHYSPCFC_CTL] = 0x0078;
+    s->phy_regs[PHY_REG_PHYSPCFC_ST] = 0xBC00;
+    s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL] = 0x0C60;
+    s->phy_regs[PHY_REG_LED] = 0x4100;
+    s->phy_regs[PHY_REG_EXT_PHYSPCFC_CTL2] = 0x000A;
+    s->phy_regs[PHY_REG_EXT_PHYSPCFC_ST] = 0x848B;
+
+    phy_update_link(s);
+}
+
+static void gem_reset(DeviceState *d)
+{
+    GemState *s = FROM_SYSBUS(GemState, SYS_BUS_DEVICE(d));
+
+    DB_PRINT("\n");
+
+    /* Set post reset register values */
+    memset(&s->regs[0], 0, sizeof(s->regs));
+    s->regs[GEM_NWCFG] = 0x00080000;
+    s->regs[GEM_NWSTATUS] = 0x00000006;
+    s->regs[GEM_DMACFG] = 0x00020784;
+    s->regs[GEM_IMR] = 0x07ffffff;
+    s->regs[GEM_TXPAUSE] = 0x0000ffff;
+    s->regs[GEM_TXPARTIALSF] = 0x000003ff;
+    s->regs[GEM_RXPARTIALSF] = 0x000003ff;
+    s->regs[GEM_MODID] = 0x00020118;
+    s->regs[GEM_DESCONF] = 0x02500111;
+    s->regs[GEM_DESCONF2] = 0x2ab13fff;
+    s->regs[GEM_DESCONF5] = 0x002f2145;
+    s->regs[GEM_DESCONF6] = 0x00000200;
+
+    gem_phy_reset(s);
+
+    gem_update_int_status(s);
+}
+
+static uint16_t gem_phy_read(GemState *s, unsigned reg_num)
+{
+    DB_PRINT("reg: %d value: 0x%04x\n", reg_num, s->phy_regs[reg_num]);
+    return s->phy_regs[reg_num];
+}
+
+static void gem_phy_write(GemState *s, unsigned reg_num, uint16_t val)
+{
+    DB_PRINT("reg: %d value: 0x%04x\n", reg_num, val);
+
+    switch (reg_num) {
+    case PHY_REG_CONTROL:
+        if (val & PHY_REG_CONTROL_RST) {
+            /* Phy reset */
+            gem_phy_reset(s);
+            val &= ~(PHY_REG_CONTROL_RST | PHY_REG_CONTROL_LOOP);
+            s->phy_loop = 0;
+        }
+        if (val & PHY_REG_CONTROL_ANEG) {
+            /* Complete autonegotiation immediately */
+            val &= ~PHY_REG_CONTROL_ANEG;
+            s->phy_regs[PHY_REG_STATUS] |= PHY_REG_STATUS_ANEGCMPL;
+        }
+        if (val & PHY_REG_CONTROL_LOOP) {
+            DB_PRINT("PHY placed in loopback\n");
+            s->phy_loop = 1;
+        } else {
+            s->phy_loop = 0;
+        }
+        break;
+    }
+    s->phy_regs[reg_num] = val;
+}
+
+/*
+ * gem_read32:
+ * Read a GEM register.
+ */
+static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size)
+{
+    GemState *s;
+    uint32_t retval;
+
+    s = (GemState *)opaque;
+
+    offset >>= 2;
+    retval = s->regs[offset];
+
+    DB_PRINT("offset: 0x%04x read: 0x%08x\n", (unsigned)offset*4, retval);
+
+    switch (offset) {
+    case GEM_ISR:
+        DB_PRINT("lowering irq on ISR read\n");
+        qemu_set_irq(s->irq, 0);
+        break;
+    case GEM_PHYMNTNC:
+        if (retval & GEM_PHYMNTNC_OP_R) {
+            uint32_t phy_addr, reg_num;
+
+            phy_addr = (retval & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT;
+            if (phy_addr == BOARD_PHY_ADDRESS) {
+                reg_num = (retval & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT;
+                retval &= 0xFFFF0000;
+                retval |= gem_phy_read(s, reg_num);
+            } else {
+                retval |= 0xFFFF; /* No device at this address */
+            }
+        }
+        break;
+    }
+
+    /* Squash read to clear bits */
+    s->regs[offset] &= ~(s->regs_rtc[offset]);
+
+    /* Do not provide write only bits */
+    retval &= ~(s->regs_wo[offset]);
+
+    DB_PRINT("0x%08x\n", retval);
+    return retval;
+}
+
+/*
+ * gem_write32:
+ * Write a GEM register.
+ */
+static void gem_write(void *opaque, hwaddr offset, uint64_t val,
+        unsigned size)
+{
+    GemState *s = (GemState *)opaque;
+    uint32_t readonly;
+
+    DB_PRINT("offset: 0x%04x write: 0x%08x ", (unsigned)offset, (unsigned)val);
+    offset >>= 2;
+
+    /* Squash bits which are read only in write value */
+    val &= ~(s->regs_ro[offset]);
+    /* Preserve (only) bits which are read only in register */
+    readonly = s->regs[offset];
+    readonly &= s->regs_ro[offset];
+
+    /* Squash bits which are write 1 to clear */
+    val &= ~(s->regs_w1c[offset] & val);
+
+    /* Copy register write to backing store */
+    s->regs[offset] = val | readonly;
+
+    /* Handle register write side effects */
+    switch (offset) {
+    case GEM_NWCTRL:
+        if (val & GEM_NWCTRL_TXSTART) {
+            gem_transmit(s);
+        }
+        if (!(val & GEM_NWCTRL_TXENA)) {
+            /* Reset to start of Q when transmit disabled. */
+            s->tx_desc_addr = s->regs[GEM_TXQBASE];
+        }
+        if (val & GEM_NWCTRL_RXENA) {
+            qemu_flush_queued_packets(qemu_get_queue(s->nic));
+        }
+        break;
+
+    case GEM_TXSTATUS:
+        gem_update_int_status(s);
+        break;
+    case GEM_RXQBASE:
+        s->rx_desc_addr = val;
+        break;
+    case GEM_TXQBASE:
+        s->tx_desc_addr = val;
+        break;
+    case GEM_RXSTATUS:
+        gem_update_int_status(s);
+        break;
+    case GEM_IER:
+        s->regs[GEM_IMR] &= ~val;
+        gem_update_int_status(s);
+        break;
+    case GEM_IDR:
+        s->regs[GEM_IMR] |= val;
+        gem_update_int_status(s);
+        break;
+    case GEM_PHYMNTNC:
+        if (val & GEM_PHYMNTNC_OP_W) {
+            uint32_t phy_addr, reg_num;
+
+            phy_addr = (val & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT;
+            if (phy_addr == BOARD_PHY_ADDRESS) {
+                reg_num = (val & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT;
+                gem_phy_write(s, reg_num, val);
+            }
+        }
+        break;
+    }
+
+    DB_PRINT("newval: 0x%08x\n", s->regs[offset]);
+}
+
+static const MemoryRegionOps gem_ops = {
+    .read = gem_read,
+    .write = gem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void gem_cleanup(NetClientState *nc)
+{
+    GemState *s = qemu_get_nic_opaque(nc);
+
+    DB_PRINT("\n");
+    s->nic = NULL;
+}
+
+static void gem_set_link(NetClientState *nc)
+{
+    DB_PRINT("\n");
+    phy_update_link(qemu_get_nic_opaque(nc));
+}
+
+static NetClientInfo net_gem_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = gem_can_receive,
+    .receive = gem_receive,
+    .cleanup = gem_cleanup,
+    .link_status_changed = gem_set_link,
+};
+
+static int gem_init(SysBusDevice *dev)
+{
+    GemState *s;
+
+    DB_PRINT("\n");
+
+    s = FROM_SYSBUS(GemState, dev);
+    gem_init_register_masks(s);
+    memory_region_init_io(&s->iomem, &gem_ops, s, "enet", sizeof(s->regs));
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+
+    s->nic = qemu_new_nic(&net_gem_info, &s->conf,
+            object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_cadence_gem = {
+    .name = "cadence_gem",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, GemState, GEM_MAXREG),
+        VMSTATE_UINT16_ARRAY(phy_regs, GemState, 32),
+        VMSTATE_UINT8(phy_loop, GemState),
+        VMSTATE_UINT32(rx_desc_addr, GemState),
+        VMSTATE_UINT32(tx_desc_addr, GemState),
+    }
+};
+
+static Property gem_properties[] = {
+    DEFINE_NIC_PROPERTIES(GemState, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void gem_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = gem_init;
+    dc->props = gem_properties;
+    dc->vmsd = &vmstate_cadence_gem;
+    dc->reset = gem_reset;
+}
+
+static const TypeInfo gem_info = {
+    .class_init = gem_class_init,
+    .name  = "cadence_gem",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(GemState),
+};
+
+static void gem_register_types(void)
+{
+    type_register_static(&gem_info);
+}
+
+type_init(gem_register_types)
diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c
new file mode 100644 (file)
index 0000000..2289f08
--- /dev/null
@@ -0,0 +1,914 @@
+/*
+ * QEMU NS SONIC DP8393x netcard
+ *
+ * Copyright (c) 2008-2009 Herve Poussineau
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "qemu/timer.h"
+#include "net/net.h"
+#include "hw/mips/mips.h"
+
+//#define DEBUG_SONIC
+
+/* Calculate CRCs properly on Rx packets */
+#define SONIC_CALCULATE_RXCRC
+
+#if defined(SONIC_CALCULATE_RXCRC)
+/* For crc32 */
+#include <zlib.h>
+#endif
+
+#ifdef DEBUG_SONIC
+#define DPRINTF(fmt, ...) \
+do { printf("sonic: " fmt , ##  __VA_ARGS__); } while (0)
+static const char* reg_names[] = {
+    "CR", "DCR", "RCR", "TCR", "IMR", "ISR", "UTDA", "CTDA",
+    "TPS", "TFC", "TSA0", "TSA1", "TFS", "URDA", "CRDA", "CRBA0",
+    "CRBA1", "RBWC0", "RBWC1", "EOBC", "URRA", "RSA", "REA", "RRP",
+    "RWP", "TRBA0", "TRBA1", "0x1b", "0x1c", "0x1d", "0x1e", "LLFA",
+    "TTDA", "CEP", "CAP2", "CAP1", "CAP0", "CE", "CDP", "CDC",
+    "SR", "WT0", "WT1", "RSC", "CRCT", "FAET", "MPT", "MDT",
+    "0x30", "0x31", "0x32", "0x33", "0x34", "0x35", "0x36", "0x37",
+    "0x38", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "DCR2" };
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#define SONIC_ERROR(fmt, ...) \
+do { printf("sonic ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
+
+#define SONIC_CR     0x00
+#define SONIC_DCR    0x01
+#define SONIC_RCR    0x02
+#define SONIC_TCR    0x03
+#define SONIC_IMR    0x04
+#define SONIC_ISR    0x05
+#define SONIC_UTDA   0x06
+#define SONIC_CTDA   0x07
+#define SONIC_TPS    0x08
+#define SONIC_TFC    0x09
+#define SONIC_TSA0   0x0a
+#define SONIC_TSA1   0x0b
+#define SONIC_TFS    0x0c
+#define SONIC_URDA   0x0d
+#define SONIC_CRDA   0x0e
+#define SONIC_CRBA0  0x0f
+#define SONIC_CRBA1  0x10
+#define SONIC_RBWC0  0x11
+#define SONIC_RBWC1  0x12
+#define SONIC_EOBC   0x13
+#define SONIC_URRA   0x14
+#define SONIC_RSA    0x15
+#define SONIC_REA    0x16
+#define SONIC_RRP    0x17
+#define SONIC_RWP    0x18
+#define SONIC_TRBA0  0x19
+#define SONIC_TRBA1  0x1a
+#define SONIC_LLFA   0x1f
+#define SONIC_TTDA   0x20
+#define SONIC_CEP    0x21
+#define SONIC_CAP2   0x22
+#define SONIC_CAP1   0x23
+#define SONIC_CAP0   0x24
+#define SONIC_CE     0x25
+#define SONIC_CDP    0x26
+#define SONIC_CDC    0x27
+#define SONIC_SR     0x28
+#define SONIC_WT0    0x29
+#define SONIC_WT1    0x2a
+#define SONIC_RSC    0x2b
+#define SONIC_CRCT   0x2c
+#define SONIC_FAET   0x2d
+#define SONIC_MPT    0x2e
+#define SONIC_MDT    0x2f
+#define SONIC_DCR2   0x3f
+
+#define SONIC_CR_HTX     0x0001
+#define SONIC_CR_TXP     0x0002
+#define SONIC_CR_RXDIS   0x0004
+#define SONIC_CR_RXEN    0x0008
+#define SONIC_CR_STP     0x0010
+#define SONIC_CR_ST      0x0020
+#define SONIC_CR_RST     0x0080
+#define SONIC_CR_RRRA    0x0100
+#define SONIC_CR_LCAM    0x0200
+#define SONIC_CR_MASK    0x03bf
+
+#define SONIC_DCR_DW     0x0020
+#define SONIC_DCR_LBR    0x2000
+#define SONIC_DCR_EXBUS  0x8000
+
+#define SONIC_RCR_PRX    0x0001
+#define SONIC_RCR_LBK    0x0002
+#define SONIC_RCR_FAER   0x0004
+#define SONIC_RCR_CRCR   0x0008
+#define SONIC_RCR_CRS    0x0020
+#define SONIC_RCR_LPKT   0x0040
+#define SONIC_RCR_BC     0x0080
+#define SONIC_RCR_MC     0x0100
+#define SONIC_RCR_LB0    0x0200
+#define SONIC_RCR_LB1    0x0400
+#define SONIC_RCR_AMC    0x0800
+#define SONIC_RCR_PRO    0x1000
+#define SONIC_RCR_BRD    0x2000
+#define SONIC_RCR_RNT    0x4000
+
+#define SONIC_TCR_PTX    0x0001
+#define SONIC_TCR_BCM    0x0002
+#define SONIC_TCR_FU     0x0004
+#define SONIC_TCR_EXC    0x0040
+#define SONIC_TCR_CRSL   0x0080
+#define SONIC_TCR_NCRS   0x0100
+#define SONIC_TCR_EXD    0x0400
+#define SONIC_TCR_CRCI   0x2000
+#define SONIC_TCR_PINT   0x8000
+
+#define SONIC_ISR_RBE    0x0020
+#define SONIC_ISR_RDE    0x0040
+#define SONIC_ISR_TC     0x0080
+#define SONIC_ISR_TXDN   0x0200
+#define SONIC_ISR_PKTRX  0x0400
+#define SONIC_ISR_PINT   0x0800
+#define SONIC_ISR_LCD    0x1000
+
+typedef struct dp8393xState {
+    /* Hardware */
+    int it_shift;
+    qemu_irq irq;
+#ifdef DEBUG_SONIC
+    int irq_level;
+#endif
+    QEMUTimer *watchdog;
+    int64_t wt_last_update;
+    NICConf conf;
+    NICState *nic;
+    MemoryRegion *address_space;
+    MemoryRegion mmio;
+
+    /* Registers */
+    uint8_t cam[16][6];
+    uint16_t regs[0x40];
+
+    /* Temporaries */
+    uint8_t tx_buffer[0x10000];
+    int loopback_packet;
+
+    /* Memory access */
+    void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write);
+    void* mem_opaque;
+} dp8393xState;
+
+static void dp8393x_update_irq(dp8393xState *s)
+{
+    int level = (s->regs[SONIC_IMR] & s->regs[SONIC_ISR]) ? 1 : 0;
+
+#ifdef DEBUG_SONIC
+    if (level != s->irq_level) {
+        s->irq_level = level;
+        if (level) {
+            DPRINTF("raise irq, isr is 0x%04x\n", s->regs[SONIC_ISR]);
+        } else {
+            DPRINTF("lower irq\n");
+        }
+    }
+#endif
+
+    qemu_set_irq(s->irq, level);
+}
+
+static void do_load_cam(dp8393xState *s)
+{
+    uint16_t data[8];
+    int width, size;
+    uint16_t index = 0;
+
+    width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
+    size = sizeof(uint16_t) * 4 * width;
+
+    while (s->regs[SONIC_CDC] & 0x1f) {
+        /* Fill current entry */
+        s->memory_rw(s->mem_opaque,
+            (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
+            (uint8_t *)data, size, 0);
+        s->cam[index][0] = data[1 * width] & 0xff;
+        s->cam[index][1] = data[1 * width] >> 8;
+        s->cam[index][2] = data[2 * width] & 0xff;
+        s->cam[index][3] = data[2 * width] >> 8;
+        s->cam[index][4] = data[3 * width] & 0xff;
+        s->cam[index][5] = data[3 * width] >> 8;
+        DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index,
+            s->cam[index][0], s->cam[index][1], s->cam[index][2],
+            s->cam[index][3], s->cam[index][4], s->cam[index][5]);
+        /* Move to next entry */
+        s->regs[SONIC_CDC]--;
+        s->regs[SONIC_CDP] += size;
+        index++;
+    }
+
+    /* Read CAM enable */
+    s->memory_rw(s->mem_opaque,
+        (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP],
+        (uint8_t *)data, size, 0);
+    s->regs[SONIC_CE] = data[0 * width];
+    DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]);
+
+    /* Done */
+    s->regs[SONIC_CR] &= ~SONIC_CR_LCAM;
+    s->regs[SONIC_ISR] |= SONIC_ISR_LCD;
+    dp8393x_update_irq(s);
+}
+
+static void do_read_rra(dp8393xState *s)
+{
+    uint16_t data[8];
+    int width, size;
+
+    /* Read memory */
+    width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
+    size = sizeof(uint16_t) * 4 * width;
+    s->memory_rw(s->mem_opaque,
+        (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP],
+        (uint8_t *)data, size, 0);
+
+    /* Update SONIC registers */
+    s->regs[SONIC_CRBA0] = data[0 * width];
+    s->regs[SONIC_CRBA1] = data[1 * width];
+    s->regs[SONIC_RBWC0] = data[2 * width];
+    s->regs[SONIC_RBWC1] = data[3 * width];
+    DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n",
+        s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1],
+        s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]);
+
+    /* Go to next entry */
+    s->regs[SONIC_RRP] += size;
+
+    /* Handle wrap */
+    if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) {
+        s->regs[SONIC_RRP] = s->regs[SONIC_RSA];
+    }
+
+    /* Check resource exhaustion */
+    if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP])
+    {
+        s->regs[SONIC_ISR] |= SONIC_ISR_RBE;
+        dp8393x_update_irq(s);
+    }
+
+    /* Done */
+    s->regs[SONIC_CR] &= ~SONIC_CR_RRRA;
+}
+
+static void do_software_reset(dp8393xState *s)
+{
+    qemu_del_timer(s->watchdog);
+
+    s->regs[SONIC_CR] &= ~(SONIC_CR_LCAM | SONIC_CR_RRRA | SONIC_CR_TXP | SONIC_CR_HTX);
+    s->regs[SONIC_CR] |= SONIC_CR_RST | SONIC_CR_RXDIS;
+}
+
+static void set_next_tick(dp8393xState *s)
+{
+    uint32_t ticks;
+    int64_t delay;
+
+    if (s->regs[SONIC_CR] & SONIC_CR_STP) {
+        qemu_del_timer(s->watchdog);
+        return;
+    }
+
+    ticks = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
+    s->wt_last_update = qemu_get_clock_ns(vm_clock);
+    delay = get_ticks_per_sec() * ticks / 5000000;
+    qemu_mod_timer(s->watchdog, s->wt_last_update + delay);
+}
+
+static void update_wt_regs(dp8393xState *s)
+{
+    int64_t elapsed;
+    uint32_t val;
+
+    if (s->regs[SONIC_CR] & SONIC_CR_STP) {
+        qemu_del_timer(s->watchdog);
+        return;
+    }
+
+    elapsed = s->wt_last_update - qemu_get_clock_ns(vm_clock);
+    val = s->regs[SONIC_WT1] << 16 | s->regs[SONIC_WT0];
+    val -= elapsed / 5000000;
+    s->regs[SONIC_WT1] = (val >> 16) & 0xffff;
+    s->regs[SONIC_WT0] = (val >> 0)  & 0xffff;
+    set_next_tick(s);
+
+}
+
+static void do_start_timer(dp8393xState *s)
+{
+    s->regs[SONIC_CR] &= ~SONIC_CR_STP;
+    set_next_tick(s);
+}
+
+static void do_stop_timer(dp8393xState *s)
+{
+    s->regs[SONIC_CR] &= ~SONIC_CR_ST;
+    update_wt_regs(s);
+}
+
+static void do_receiver_enable(dp8393xState *s)
+{
+    s->regs[SONIC_CR] &= ~SONIC_CR_RXDIS;
+}
+
+static void do_receiver_disable(dp8393xState *s)
+{
+    s->regs[SONIC_CR] &= ~SONIC_CR_RXEN;
+}
+
+static void do_transmit_packets(dp8393xState *s)
+{
+    NetClientState *nc = qemu_get_queue(s->nic);
+    uint16_t data[12];
+    int width, size;
+    int tx_len, len;
+    uint16_t i;
+
+    width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
+
+    while (1) {
+        /* Read memory */
+        DPRINTF("Transmit packet at %08x\n",
+                (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_CTDA]);
+        size = sizeof(uint16_t) * 6 * width;
+        s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
+        s->memory_rw(s->mem_opaque,
+            ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * width,
+            (uint8_t *)data, size, 0);
+        tx_len = 0;
+
+        /* Update registers */
+        s->regs[SONIC_TCR] = data[0 * width] & 0xf000;
+        s->regs[SONIC_TPS] = data[1 * width];
+        s->regs[SONIC_TFC] = data[2 * width];
+        s->regs[SONIC_TSA0] = data[3 * width];
+        s->regs[SONIC_TSA1] = data[4 * width];
+        s->regs[SONIC_TFS] = data[5 * width];
+
+        /* Handle programmable interrupt */
+        if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
+            s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
+        } else {
+            s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
+        }
+
+        for (i = 0; i < s->regs[SONIC_TFC]; ) {
+            /* Append fragment */
+            len = s->regs[SONIC_TFS];
+            if (tx_len + len > sizeof(s->tx_buffer)) {
+                len = sizeof(s->tx_buffer) - tx_len;
+            }
+            s->memory_rw(s->mem_opaque,
+                (s->regs[SONIC_TSA1] << 16) | s->regs[SONIC_TSA0],
+                &s->tx_buffer[tx_len], len, 0);
+            tx_len += len;
+
+            i++;
+            if (i != s->regs[SONIC_TFC]) {
+                /* Read next fragment details */
+                size = sizeof(uint16_t) * 3 * width;
+                s->memory_rw(s->mem_opaque,
+                    ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * i) * width,
+                    (uint8_t *)data, size, 0);
+                s->regs[SONIC_TSA0] = data[0 * width];
+                s->regs[SONIC_TSA1] = data[1 * width];
+                s->regs[SONIC_TFS] = data[2 * width];
+            }
+        }
+
+        /* Handle Ethernet checksum */
+        if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
+            /* Don't append FCS there, to look like slirp packets
+             * which don't have one */
+        } else {
+            /* Remove existing FCS */
+            tx_len -= 4;
+        }
+
+        if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
+            /* Loopback */
+            s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
+            if (nc->info->can_receive(nc)) {
+                s->loopback_packet = 1;
+                nc->info->receive(nc, s->tx_buffer, tx_len);
+            }
+        } else {
+            /* Transmit packet */
+            qemu_send_packet(nc, s->tx_buffer, tx_len);
+        }
+        s->regs[SONIC_TCR] |= SONIC_TCR_PTX;
+
+        /* Write status */
+        data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */
+        size = sizeof(uint16_t) * width;
+        s->memory_rw(s->mem_opaque,
+            (s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA],
+            (uint8_t *)data, size, 1);
+
+        if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
+            /* Read footer of packet */
+            size = sizeof(uint16_t) * width;
+            s->memory_rw(s->mem_opaque,
+                ((s->regs[SONIC_UTDA] << 16) | s->regs[SONIC_TTDA]) + sizeof(uint16_t) * (4 + 3 * s->regs[SONIC_TFC]) * width,
+                (uint8_t *)data, size, 0);
+            s->regs[SONIC_CTDA] = data[0 * width] & ~0x1;
+            if (data[0 * width] & 0x1) {
+                /* EOL detected */
+                break;
+            }
+        }
+    }
+
+    /* Done */
+    s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
+    s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
+    dp8393x_update_irq(s);
+}
+
+static void do_halt_transmission(dp8393xState *s)
+{
+    /* Nothing to do */
+}
+
+static void do_command(dp8393xState *s, uint16_t command)
+{
+    if ((s->regs[SONIC_CR] & SONIC_CR_RST) && !(command & SONIC_CR_RST)) {
+        s->regs[SONIC_CR] &= ~SONIC_CR_RST;
+        return;
+    }
+
+    s->regs[SONIC_CR] |= (command & SONIC_CR_MASK);
+
+    if (command & SONIC_CR_HTX)
+        do_halt_transmission(s);
+    if (command & SONIC_CR_TXP)
+        do_transmit_packets(s);
+    if (command & SONIC_CR_RXDIS)
+        do_receiver_disable(s);
+    if (command & SONIC_CR_RXEN)
+        do_receiver_enable(s);
+    if (command & SONIC_CR_STP)
+        do_stop_timer(s);
+    if (command & SONIC_CR_ST)
+        do_start_timer(s);
+    if (command & SONIC_CR_RST)
+        do_software_reset(s);
+    if (command & SONIC_CR_RRRA)
+        do_read_rra(s);
+    if (command & SONIC_CR_LCAM)
+        do_load_cam(s);
+}
+
+static uint16_t read_register(dp8393xState *s, int reg)
+{
+    uint16_t val = 0;
+
+    switch (reg) {
+        /* Update data before reading it */
+        case SONIC_WT0:
+        case SONIC_WT1:
+            update_wt_regs(s);
+            val = s->regs[reg];
+            break;
+        /* Accept read to some registers only when in reset mode */
+        case SONIC_CAP2:
+        case SONIC_CAP1:
+        case SONIC_CAP0:
+            if (s->regs[SONIC_CR] & SONIC_CR_RST) {
+                val = s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg) + 1] << 8;
+                val |= s->cam[s->regs[SONIC_CEP] & 0xf][2* (SONIC_CAP0 - reg)];
+            }
+            break;
+        /* All other registers have no special contrainst */
+        default:
+            val = s->regs[reg];
+    }
+
+    DPRINTF("read 0x%04x from reg %s\n", val, reg_names[reg]);
+
+    return val;
+}
+
+static void write_register(dp8393xState *s, int reg, uint16_t val)
+{
+    DPRINTF("write 0x%04x to reg %s\n", val, reg_names[reg]);
+
+    switch (reg) {
+        /* Command register */
+        case SONIC_CR:
+            do_command(s, val);
+            break;
+        /* Prevent write to read-only registers */
+        case SONIC_CAP2:
+        case SONIC_CAP1:
+        case SONIC_CAP0:
+        case SONIC_SR:
+        case SONIC_MDT:
+            DPRINTF("writing to reg %d invalid\n", reg);
+            break;
+        /* Accept write to some registers only when in reset mode */
+        case SONIC_DCR:
+            if (s->regs[SONIC_CR] & SONIC_CR_RST) {
+                s->regs[reg] = val & 0xbfff;
+            } else {
+                DPRINTF("writing to DCR invalid\n");
+            }
+            break;
+        case SONIC_DCR2:
+            if (s->regs[SONIC_CR] & SONIC_CR_RST) {
+                s->regs[reg] = val & 0xf017;
+            } else {
+                DPRINTF("writing to DCR2 invalid\n");
+            }
+            break;
+        /* 12 lower bytes are Read Only */
+        case SONIC_TCR:
+            s->regs[reg] = val & 0xf000;
+            break;
+        /* 9 lower bytes are Read Only */
+        case SONIC_RCR:
+            s->regs[reg] = val & 0xffe0;
+            break;
+        /* Ignore most significant bit */
+        case SONIC_IMR:
+            s->regs[reg] = val & 0x7fff;
+            dp8393x_update_irq(s);
+            break;
+        /* Clear bits by writing 1 to them */
+        case SONIC_ISR:
+            val &= s->regs[reg];
+            s->regs[reg] &= ~val;
+            if (val & SONIC_ISR_RBE) {
+                do_read_rra(s);
+            }
+            dp8393x_update_irq(s);
+            break;
+        /* Ignore least significant bit */
+        case SONIC_RSA:
+        case SONIC_REA:
+        case SONIC_RRP:
+        case SONIC_RWP:
+            s->regs[reg] = val & 0xfffe;
+            break;
+        /* Invert written value for some registers */
+        case SONIC_CRCT:
+        case SONIC_FAET:
+        case SONIC_MPT:
+            s->regs[reg] = val ^ 0xffff;
+            break;
+        /* All other registers have no special contrainst */
+        default:
+            s->regs[reg] = val;
+    }
+
+    if (reg == SONIC_WT0 || reg == SONIC_WT1) {
+        set_next_tick(s);
+    }
+}
+
+static void dp8393x_watchdog(void *opaque)
+{
+    dp8393xState *s = opaque;
+
+    if (s->regs[SONIC_CR] & SONIC_CR_STP) {
+        return;
+    }
+
+    s->regs[SONIC_WT1] = 0xffff;
+    s->regs[SONIC_WT0] = 0xffff;
+    set_next_tick(s);
+
+    /* Signal underflow */
+    s->regs[SONIC_ISR] |= SONIC_ISR_TC;
+    dp8393x_update_irq(s);
+}
+
+static uint32_t dp8393x_readw(void *opaque, hwaddr addr)
+{
+    dp8393xState *s = opaque;
+    int reg;
+
+    if ((addr & ((1 << s->it_shift) - 1)) != 0) {
+        return 0;
+    }
+
+    reg = addr >> s->it_shift;
+    return read_register(s, reg);
+}
+
+static uint32_t dp8393x_readb(void *opaque, hwaddr addr)
+{
+    uint16_t v = dp8393x_readw(opaque, addr & ~0x1);
+    return (v >> (8 * (addr & 0x1))) & 0xff;
+}
+
+static uint32_t dp8393x_readl(void *opaque, hwaddr addr)
+{
+    uint32_t v;
+    v = dp8393x_readw(opaque, addr);
+    v |= dp8393x_readw(opaque, addr + 2) << 16;
+    return v;
+}
+
+static void dp8393x_writew(void *opaque, hwaddr addr, uint32_t val)
+{
+    dp8393xState *s = opaque;
+    int reg;
+
+    if ((addr & ((1 << s->it_shift) - 1)) != 0) {
+        return;
+    }
+
+    reg = addr >> s->it_shift;
+
+    write_register(s, reg, (uint16_t)val);
+}
+
+static void dp8393x_writeb(void *opaque, hwaddr addr, uint32_t val)
+{
+    uint16_t old_val = dp8393x_readw(opaque, addr & ~0x1);
+
+    switch (addr & 3) {
+    case 0:
+        val = val | (old_val & 0xff00);
+        break;
+    case 1:
+        val = (val << 8) | (old_val & 0x00ff);
+        break;
+    }
+    dp8393x_writew(opaque, addr & ~0x1, val);
+}
+
+static void dp8393x_writel(void *opaque, hwaddr addr, uint32_t val)
+{
+    dp8393x_writew(opaque, addr, val & 0xffff);
+    dp8393x_writew(opaque, addr + 2, (val >> 16) & 0xffff);
+}
+
+static const MemoryRegionOps dp8393x_ops = {
+    .old_mmio = {
+        .read = { dp8393x_readb, dp8393x_readw, dp8393x_readl, },
+        .write = { dp8393x_writeb, dp8393x_writew, dp8393x_writel, },
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int nic_can_receive(NetClientState *nc)
+{
+    dp8393xState *s = qemu_get_nic_opaque(nc);
+
+    if (!(s->regs[SONIC_CR] & SONIC_CR_RXEN))
+        return 0;
+    if (s->regs[SONIC_ISR] & SONIC_ISR_RBE)
+        return 0;
+    return 1;
+}
+
+static int receive_filter(dp8393xState *s, const uint8_t * buf, int size)
+{
+    static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+    int i;
+
+    /* Check for runt packet (remember that checksum is not there) */
+    if (size < 64 - 4) {
+        return (s->regs[SONIC_RCR] & SONIC_RCR_RNT) ? 0 : -1;
+    }
+
+    /* Check promiscuous mode */
+    if ((s->regs[SONIC_RCR] & SONIC_RCR_PRO) && (buf[0] & 1) == 0) {
+        return 0;
+    }
+
+    /* Check multicast packets */
+    if ((s->regs[SONIC_RCR] & SONIC_RCR_AMC) && (buf[0] & 1) == 1) {
+        return SONIC_RCR_MC;
+    }
+
+    /* Check broadcast */
+    if ((s->regs[SONIC_RCR] & SONIC_RCR_BRD) && !memcmp(buf, bcast, sizeof(bcast))) {
+        return SONIC_RCR_BC;
+    }
+
+    /* Check CAM */
+    for (i = 0; i < 16; i++) {
+        if (s->regs[SONIC_CE] & (1 << i)) {
+             /* Entry enabled */
+             if (!memcmp(buf, s->cam[i], sizeof(s->cam[i]))) {
+                 return 0;
+             }
+        }
+    }
+
+    return -1;
+}
+
+static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
+{
+    dp8393xState *s = qemu_get_nic_opaque(nc);
+    uint16_t data[10];
+    int packet_type;
+    uint32_t available, address;
+    int width, rx_len = size;
+    uint32_t checksum;
+
+    width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;
+
+    s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER |
+        SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC);
+
+    packet_type = receive_filter(s, buf, size);
+    if (packet_type < 0) {
+        DPRINTF("packet not for netcard\n");
+        return -1;
+    }
+
+    /* XXX: Check byte ordering */
+
+    /* Check for EOL */
+    if (s->regs[SONIC_LLFA] & 0x1) {
+        /* Are we still in resource exhaustion? */
+        size = sizeof(uint16_t) * 1 * width;
+        address = ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width;
+        s->memory_rw(s->mem_opaque, address, (uint8_t*)data, size, 0);
+        if (data[0 * width] & 0x1) {
+            /* Still EOL ; stop reception */
+            return -1;
+        } else {
+            s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
+        }
+    }
+
+    /* Save current position */
+    s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1];
+    s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0];
+
+    /* Calculate the ethernet checksum */
+#ifdef SONIC_CALCULATE_RXCRC
+    checksum = cpu_to_le32(crc32(0, buf, rx_len));
+#else
+    checksum = 0;
+#endif
+
+    /* Put packet into RBA */
+    DPRINTF("Receive packet at %08x\n", (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0]);
+    address = (s->regs[SONIC_CRBA1] << 16) | s->regs[SONIC_CRBA0];
+    s->memory_rw(s->mem_opaque, address, (uint8_t*)buf, rx_len, 1);
+    address += rx_len;
+    s->memory_rw(s->mem_opaque, address, (uint8_t*)&checksum, 4, 1);
+    rx_len += 4;
+    s->regs[SONIC_CRBA1] = address >> 16;
+    s->regs[SONIC_CRBA0] = address & 0xffff;
+    available = (s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0];
+    available -= rx_len / 2;
+    s->regs[SONIC_RBWC1] = available >> 16;
+    s->regs[SONIC_RBWC0] = available & 0xffff;
+
+    /* Update status */
+    if (((s->regs[SONIC_RBWC1] << 16) | s->regs[SONIC_RBWC0]) < s->regs[SONIC_EOBC]) {
+        s->regs[SONIC_RCR] |= SONIC_RCR_LPKT;
+    }
+    s->regs[SONIC_RCR] |= packet_type;
+    s->regs[SONIC_RCR] |= SONIC_RCR_PRX;
+    if (s->loopback_packet) {
+        s->regs[SONIC_RCR] |= SONIC_RCR_LBK;
+        s->loopback_packet = 0;
+    }
+
+    /* Write status to memory */
+    DPRINTF("Write status at %08x\n", (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]);
+    data[0 * width] = s->regs[SONIC_RCR]; /* status */
+    data[1 * width] = rx_len; /* byte count */
+    data[2 * width] = s->regs[SONIC_TRBA0]; /* pkt_ptr0 */
+    data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */
+    data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */
+    size = sizeof(uint16_t) * 5 * width;
+    s->memory_rw(s->mem_opaque, (s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA], (uint8_t *)data, size, 1);
+
+    /* Move to next descriptor */
+    size = sizeof(uint16_t) * width;
+    s->memory_rw(s->mem_opaque,
+        ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 5 * width,
+        (uint8_t *)data, size, 0);
+    s->regs[SONIC_LLFA] = data[0 * width];
+    if (s->regs[SONIC_LLFA] & 0x1) {
+        /* EOL detected */
+        s->regs[SONIC_ISR] |= SONIC_ISR_RDE;
+    } else {
+        data[0 * width] = 0; /* in_use */
+        s->memory_rw(s->mem_opaque,
+            ((s->regs[SONIC_URDA] << 16) | s->regs[SONIC_CRDA]) + sizeof(uint16_t) * 6 * width,
+            (uint8_t *)data, size, 1);
+        s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA];
+        s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX;
+        s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff);
+
+        if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) {
+            /* Read next RRA */
+            do_read_rra(s);
+        }
+    }
+
+    /* Done */
+    dp8393x_update_irq(s);
+
+    return size;
+}
+
+static void nic_reset(void *opaque)
+{
+    dp8393xState *s = opaque;
+    qemu_del_timer(s->watchdog);
+
+    s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS;
+    s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR);
+    s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT);
+    s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX;
+    s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM;
+    s->regs[SONIC_IMR] = 0;
+    s->regs[SONIC_ISR] = 0;
+    s->regs[SONIC_DCR2] = 0;
+    s->regs[SONIC_EOBC] = 0x02F8;
+    s->regs[SONIC_RSC] = 0;
+    s->regs[SONIC_CE] = 0;
+    s->regs[SONIC_RSC] = 0;
+
+    /* Network cable is connected */
+    s->regs[SONIC_RCR] |= SONIC_RCR_CRS;
+
+    dp8393x_update_irq(s);
+}
+
+static void nic_cleanup(NetClientState *nc)
+{
+    dp8393xState *s = qemu_get_nic_opaque(nc);
+
+    memory_region_del_subregion(s->address_space, &s->mmio);
+    memory_region_destroy(&s->mmio);
+
+    qemu_del_timer(s->watchdog);
+    qemu_free_timer(s->watchdog);
+
+    g_free(s);
+}
+
+static NetClientInfo net_dp83932_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = nic_can_receive,
+    .receive = nic_receive,
+    .cleanup = nic_cleanup,
+};
+
+void dp83932_init(NICInfo *nd, hwaddr base, int it_shift,
+                  MemoryRegion *address_space,
+                  qemu_irq irq, void* mem_opaque,
+                  void (*memory_rw)(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write))
+{
+    dp8393xState *s;
+
+    qemu_check_nic_model(nd, "dp83932");
+
+    s = g_malloc0(sizeof(dp8393xState));
+
+    s->address_space = address_space;
+    s->mem_opaque = mem_opaque;
+    s->memory_rw = memory_rw;
+    s->it_shift = it_shift;
+    s->irq = irq;
+    s->watchdog = qemu_new_timer_ns(vm_clock, dp8393x_watchdog, s);
+    s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */
+
+    s->conf.macaddr = nd->macaddr;
+    s->conf.peers.ncs[0] = nd->netdev;
+
+    s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, nd->model, nd->name, s);
+
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+    qemu_register_reset(nic_reset, s);
+    nic_reset(s);
+
+    memory_region_init_io(&s->mmio, &dp8393x_ops, s,
+                          "dp8393x", 0x40 << it_shift);
+    memory_region_add_subregion(address_space, base, &s->mmio);
+}
diff --git a/hw/net/e1000.c b/hw/net/e1000.c
new file mode 100644 (file)
index 0000000..3f18041
--- /dev/null
@@ -0,0 +1,1404 @@
+/*
+ * QEMU e1000 emulation
+ *
+ * Software developer's manual:
+ * http://download.intel.com/design/network/manuals/8254x_GBe_SDM.pdf
+ *
+ * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc.
+ * Copyright (c) 2008 Qumranet
+ * Based on work done by:
+ * Copyright (c) 2007 Dan Aloni
+ * Copyright (c) 2004 Antony T Curtis
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "net/net.h"
+#include "net/checksum.h"
+#include "hw/loader.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/dma.h"
+
+#include "hw/e1000_hw.h"
+
+#define E1000_DEBUG
+
+#ifdef E1000_DEBUG
+enum {
+    DEBUG_GENERAL,     DEBUG_IO,       DEBUG_MMIO,     DEBUG_INTERRUPT,
+    DEBUG_RX,          DEBUG_TX,       DEBUG_MDIC,     DEBUG_EEPROM,
+    DEBUG_UNKNOWN,     DEBUG_TXSUM,    DEBUG_TXERR,    DEBUG_RXERR,
+    DEBUG_RXFILTER,     DEBUG_PHY,      DEBUG_NOTYET,
+};
+#define DBGBIT(x)      (1<<DEBUG_##x)
+static int debugflags = DBGBIT(TXERR) | DBGBIT(GENERAL);
+
+#define        DBGOUT(what, fmt, ...) do { \
+    if (debugflags & DBGBIT(what)) \
+        fprintf(stderr, "e1000: " fmt, ## __VA_ARGS__); \
+    } while (0)
+#else
+#define        DBGOUT(what, fmt, ...) do {} while (0)
+#endif
+
+#define IOPORT_SIZE       0x40
+#define PNPMMIO_SIZE      0x20000
+#define MIN_BUF_SIZE      60 /* Min. octets in an ethernet frame sans FCS */
+
+/* this is the size past which hardware will drop packets when setting LPE=0 */
+#define MAXIMUM_ETHERNET_VLAN_SIZE 1522
+/* this is the size past which hardware will drop packets when setting LPE=1 */
+#define MAXIMUM_ETHERNET_LPE_SIZE 16384
+
+/*
+ * HW models:
+ *  E1000_DEV_ID_82540EM works with Windows and Linux
+ *  E1000_DEV_ID_82573L OK with windoze and Linux 2.6.22,
+ *     appears to perform better than 82540EM, but breaks with Linux 2.6.18
+ *  E1000_DEV_ID_82544GC_COPPER appears to work; not well tested
+ *  Others never tested
+ */
+enum { E1000_DEVID = E1000_DEV_ID_82540EM };
+
+/*
+ * May need to specify additional MAC-to-PHY entries --
+ * Intel's Windows driver refuses to initialize unless they match
+ */
+enum {
+    PHY_ID2_INIT = E1000_DEVID == E1000_DEV_ID_82573L ?                0xcc2 :
+                   E1000_DEVID == E1000_DEV_ID_82544GC_COPPER ?        0xc30 :
+                   /* default to E1000_DEV_ID_82540EM */       0xc20
+};
+
+typedef struct E1000State_st {
+    PCIDevice dev;
+    NICState *nic;
+    NICConf conf;
+    MemoryRegion mmio;
+    MemoryRegion io;
+
+    uint32_t mac_reg[0x8000];
+    uint16_t phy_reg[0x20];
+    uint16_t eeprom_data[64];
+
+    uint32_t rxbuf_size;
+    uint32_t rxbuf_min_shift;
+    struct e1000_tx {
+        unsigned char header[256];
+        unsigned char vlan_header[4];
+        /* Fields vlan and data must not be reordered or separated. */
+        unsigned char vlan[4];
+        unsigned char data[0x10000];
+        uint16_t size;
+        unsigned char sum_needed;
+        unsigned char vlan_needed;
+        uint8_t ipcss;
+        uint8_t ipcso;
+        uint16_t ipcse;
+        uint8_t tucss;
+        uint8_t tucso;
+        uint16_t tucse;
+        uint8_t hdr_len;
+        uint16_t mss;
+        uint32_t paylen;
+        uint16_t tso_frames;
+        char tse;
+        int8_t ip;
+        int8_t tcp;
+        char cptse;     // current packet tse bit
+    } tx;
+
+    struct {
+        uint32_t val_in;       // shifted in from guest driver
+        uint16_t bitnum_in;
+        uint16_t bitnum_out;
+        uint16_t reading;
+        uint32_t old_eecd;
+    } eecd_state;
+
+    QEMUTimer *autoneg_timer;
+
+/* Compatibility flags for migration to/from qemu 1.3.0 and older */
+#define E1000_FLAG_AUTONEG_BIT 0
+#define E1000_FLAG_AUTONEG (1 << E1000_FLAG_AUTONEG_BIT)
+    uint32_t compat_flags;
+} E1000State;
+
+#define        defreg(x)       x = (E1000_##x>>2)
+enum {
+    defreg(CTRL),      defreg(EECD),   defreg(EERD),   defreg(GPRC),
+    defreg(GPTC),      defreg(ICR),    defreg(ICS),    defreg(IMC),
+    defreg(IMS),       defreg(LEDCTL), defreg(MANC),   defreg(MDIC),
+    defreg(MPC),       defreg(PBA),    defreg(RCTL),   defreg(RDBAH),
+    defreg(RDBAL),     defreg(RDH),    defreg(RDLEN),  defreg(RDT),
+    defreg(STATUS),    defreg(SWSM),   defreg(TCTL),   defreg(TDBAH),
+    defreg(TDBAL),     defreg(TDH),    defreg(TDLEN),  defreg(TDT),
+    defreg(TORH),      defreg(TORL),   defreg(TOTH),   defreg(TOTL),
+    defreg(TPR),       defreg(TPT),    defreg(TXDCTL), defreg(WUFC),
+    defreg(RA),                defreg(MTA),    defreg(CRCERRS),defreg(VFTA),
+    defreg(VET),
+};
+
+static void
+e1000_link_down(E1000State *s)
+{
+    s->mac_reg[STATUS] &= ~E1000_STATUS_LU;
+    s->phy_reg[PHY_STATUS] &= ~MII_SR_LINK_STATUS;
+}
+
+static void
+e1000_link_up(E1000State *s)
+{
+    s->mac_reg[STATUS] |= E1000_STATUS_LU;
+    s->phy_reg[PHY_STATUS] |= MII_SR_LINK_STATUS;
+}
+
+static void
+set_phy_ctrl(E1000State *s, int index, uint16_t val)
+{
+    /*
+     * QEMU 1.3 does not support link auto-negotiation emulation, so if we
+     * migrate during auto negotiation, after migration the link will be
+     * down.
+     */
+    if (!(s->compat_flags & E1000_FLAG_AUTONEG)) {
+        return;
+    }
+    if ((val & MII_CR_AUTO_NEG_EN) && (val & MII_CR_RESTART_AUTO_NEG)) {
+        e1000_link_down(s);
+        s->phy_reg[PHY_STATUS] &= ~MII_SR_AUTONEG_COMPLETE;
+        DBGOUT(PHY, "Start link auto negotiation\n");
+        qemu_mod_timer(s->autoneg_timer, qemu_get_clock_ms(vm_clock) + 500);
+    }
+}
+
+static void
+e1000_autoneg_timer(void *opaque)
+{
+    E1000State *s = opaque;
+    if (!qemu_get_queue(s->nic)->link_down) {
+        e1000_link_up(s);
+    }
+    s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE;
+    DBGOUT(PHY, "Auto negotiation is completed\n");
+}
+
+static void (*phyreg_writeops[])(E1000State *, int, uint16_t) = {
+    [PHY_CTRL] = set_phy_ctrl,
+};
+
+enum { NPHYWRITEOPS = ARRAY_SIZE(phyreg_writeops) };
+
+enum { PHY_R = 1, PHY_W = 2, PHY_RW = PHY_R | PHY_W };
+static const char phy_regcap[0x20] = {
+    [PHY_STATUS] = PHY_R,      [M88E1000_EXT_PHY_SPEC_CTRL] = PHY_RW,
+    [PHY_ID1] = PHY_R,         [M88E1000_PHY_SPEC_CTRL] = PHY_RW,
+    [PHY_CTRL] = PHY_RW,       [PHY_1000T_CTRL] = PHY_RW,
+    [PHY_LP_ABILITY] = PHY_R,  [PHY_1000T_STATUS] = PHY_R,
+    [PHY_AUTONEG_ADV] = PHY_RW,        [M88E1000_RX_ERR_CNTR] = PHY_R,
+    [PHY_ID2] = PHY_R,         [M88E1000_PHY_SPEC_STATUS] = PHY_R
+};
+
+static const uint16_t phy_reg_init[] = {
+    [PHY_CTRL] = 0x1140,
+    [PHY_STATUS] = 0x794d, /* link initially up with not completed autoneg */
+    [PHY_ID1] = 0x141,                         [PHY_ID2] = PHY_ID2_INIT,
+    [PHY_1000T_CTRL] = 0x0e00,                 [M88E1000_PHY_SPEC_CTRL] = 0x360,
+    [M88E1000_EXT_PHY_SPEC_CTRL] = 0x0d60,     [PHY_AUTONEG_ADV] = 0xde1,
+    [PHY_LP_ABILITY] = 0x1e0,                  [PHY_1000T_STATUS] = 0x3c00,
+    [M88E1000_PHY_SPEC_STATUS] = 0xac00,
+};
+
+static const uint32_t mac_reg_init[] = {
+    [PBA] =     0x00100030,
+    [LEDCTL] =  0x602,
+    [CTRL] =    E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN0 |
+                E1000_CTRL_SPD_1000 | E1000_CTRL_SLU,
+    [STATUS] =  0x80000000 | E1000_STATUS_GIO_MASTER_ENABLE |
+                E1000_STATUS_ASDV | E1000_STATUS_MTXCKOK |
+                E1000_STATUS_SPEED_1000 | E1000_STATUS_FD |
+                E1000_STATUS_LU,
+    [MANC] =    E1000_MANC_EN_MNG2HOST | E1000_MANC_RCV_TCO_EN |
+                E1000_MANC_ARP_EN | E1000_MANC_0298_EN |
+                E1000_MANC_RMCP_EN,
+};
+
+static void
+set_interrupt_cause(E1000State *s, int index, uint32_t val)
+{
+    if (val && (E1000_DEVID >= E1000_DEV_ID_82547EI_MOBILE)) {
+        /* Only for 8257x */
+        val |= E1000_ICR_INT_ASSERTED;
+    }
+    s->mac_reg[ICR] = val;
+
+    /*
+     * Make sure ICR and ICS registers have the same value.
+     * The spec says that the ICS register is write-only.  However in practice,
+     * on real hardware ICS is readable, and for reads it has the same value as
+     * ICR (except that ICS does not have the clear on read behaviour of ICR).
+     *
+     * The VxWorks PRO/1000 driver uses this behaviour.
+     */
+    s->mac_reg[ICS] = val;
+
+    qemu_set_irq(s->dev.irq[0], (s->mac_reg[IMS] & s->mac_reg[ICR]) != 0);
+}
+
+static void
+set_ics(E1000State *s, int index, uint32_t val)
+{
+    DBGOUT(INTERRUPT, "set_ics %x, ICR %x, IMR %x\n", val, s->mac_reg[ICR],
+        s->mac_reg[IMS]);
+    set_interrupt_cause(s, 0, val | s->mac_reg[ICR]);
+}
+
+static int
+rxbufsize(uint32_t v)
+{
+    v &= E1000_RCTL_BSEX | E1000_RCTL_SZ_16384 | E1000_RCTL_SZ_8192 |
+         E1000_RCTL_SZ_4096 | E1000_RCTL_SZ_2048 | E1000_RCTL_SZ_1024 |
+         E1000_RCTL_SZ_512 | E1000_RCTL_SZ_256;
+    switch (v) {
+    case E1000_RCTL_BSEX | E1000_RCTL_SZ_16384:
+        return 16384;
+    case E1000_RCTL_BSEX | E1000_RCTL_SZ_8192:
+        return 8192;
+    case E1000_RCTL_BSEX | E1000_RCTL_SZ_4096:
+        return 4096;
+    case E1000_RCTL_SZ_1024:
+        return 1024;
+    case E1000_RCTL_SZ_512:
+        return 512;
+    case E1000_RCTL_SZ_256:
+        return 256;
+    }
+    return 2048;
+}
+
+static void e1000_reset(void *opaque)
+{
+    E1000State *d = opaque;
+    uint8_t *macaddr = d->conf.macaddr.a;
+    int i;
+
+    qemu_del_timer(d->autoneg_timer);
+    memset(d->phy_reg, 0, sizeof d->phy_reg);
+    memmove(d->phy_reg, phy_reg_init, sizeof phy_reg_init);
+    memset(d->mac_reg, 0, sizeof d->mac_reg);
+    memmove(d->mac_reg, mac_reg_init, sizeof mac_reg_init);
+    d->rxbuf_min_shift = 1;
+    memset(&d->tx, 0, sizeof d->tx);
+
+    if (qemu_get_queue(d->nic)->link_down) {
+        e1000_link_down(d);
+    }
+
+    /* Some guests expect pre-initialized RAH/RAL (AddrValid flag + MACaddr) */
+    d->mac_reg[RA] = 0;
+    d->mac_reg[RA + 1] = E1000_RAH_AV;
+    for (i = 0; i < 4; i++) {
+        d->mac_reg[RA] |= macaddr[i] << (8 * i);
+        d->mac_reg[RA + 1] |= (i < 2) ? macaddr[i + 4] << (8 * i) : 0;
+    }
+}
+
+static void
+set_ctrl(E1000State *s, int index, uint32_t val)
+{
+    /* RST is self clearing */
+    s->mac_reg[CTRL] = val & ~E1000_CTRL_RST;
+}
+
+static void
+set_rx_control(E1000State *s, int index, uint32_t val)
+{
+    s->mac_reg[RCTL] = val;
+    s->rxbuf_size = rxbufsize(val);
+    s->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1;
+    DBGOUT(RX, "RCTL: %d, mac_reg[RCTL] = 0x%x\n", s->mac_reg[RDT],
+           s->mac_reg[RCTL]);
+    qemu_flush_queued_packets(qemu_get_queue(s->nic));
+}
+
+static void
+set_mdic(E1000State *s, int index, uint32_t val)
+{
+    uint32_t data = val & E1000_MDIC_DATA_MASK;
+    uint32_t addr = ((val & E1000_MDIC_REG_MASK) >> E1000_MDIC_REG_SHIFT);
+
+    if ((val & E1000_MDIC_PHY_MASK) >> E1000_MDIC_PHY_SHIFT != 1) // phy #
+        val = s->mac_reg[MDIC] | E1000_MDIC_ERROR;
+    else if (val & E1000_MDIC_OP_READ) {
+        DBGOUT(MDIC, "MDIC read reg 0x%x\n", addr);
+        if (!(phy_regcap[addr] & PHY_R)) {
+            DBGOUT(MDIC, "MDIC read reg %x unhandled\n", addr);
+            val |= E1000_MDIC_ERROR;
+        } else
+            val = (val ^ data) | s->phy_reg[addr];
+    } else if (val & E1000_MDIC_OP_WRITE) {
+        DBGOUT(MDIC, "MDIC write reg 0x%x, value 0x%x\n", addr, data);
+        if (!(phy_regcap[addr] & PHY_W)) {
+            DBGOUT(MDIC, "MDIC write reg %x unhandled\n", addr);
+            val |= E1000_MDIC_ERROR;
+        } else {
+            if (addr < NPHYWRITEOPS && phyreg_writeops[addr]) {
+                phyreg_writeops[addr](s, index, data);
+            }
+            s->phy_reg[addr] = data;
+        }
+    }
+    s->mac_reg[MDIC] = val | E1000_MDIC_READY;
+
+    if (val & E1000_MDIC_INT_EN) {
+        set_ics(s, 0, E1000_ICR_MDAC);
+    }
+}
+
+static uint32_t
+get_eecd(E1000State *s, int index)
+{
+    uint32_t ret = E1000_EECD_PRES|E1000_EECD_GNT | s->eecd_state.old_eecd;
+
+    DBGOUT(EEPROM, "reading eeprom bit %d (reading %d)\n",
+           s->eecd_state.bitnum_out, s->eecd_state.reading);
+    if (!s->eecd_state.reading ||
+        ((s->eeprom_data[(s->eecd_state.bitnum_out >> 4) & 0x3f] >>
+          ((s->eecd_state.bitnum_out & 0xf) ^ 0xf))) & 1)
+        ret |= E1000_EECD_DO;
+    return ret;
+}
+
+static void
+set_eecd(E1000State *s, int index, uint32_t val)
+{
+    uint32_t oldval = s->eecd_state.old_eecd;
+
+    s->eecd_state.old_eecd = val & (E1000_EECD_SK | E1000_EECD_CS |
+            E1000_EECD_DI|E1000_EECD_FWE_MASK|E1000_EECD_REQ);
+    if (!(E1000_EECD_CS & val))                        // CS inactive; nothing to do
+       return;
+    if (E1000_EECD_CS & (val ^ oldval)) {      // CS rise edge; reset state
+       s->eecd_state.val_in = 0;
+       s->eecd_state.bitnum_in = 0;
+       s->eecd_state.bitnum_out = 0;
+       s->eecd_state.reading = 0;
+    }
+    if (!(E1000_EECD_SK & (val ^ oldval)))     // no clock edge
+        return;
+    if (!(E1000_EECD_SK & val)) {              // falling edge
+        s->eecd_state.bitnum_out++;
+        return;
+    }
+    s->eecd_state.val_in <<= 1;
+    if (val & E1000_EECD_DI)
+        s->eecd_state.val_in |= 1;
+    if (++s->eecd_state.bitnum_in == 9 && !s->eecd_state.reading) {
+        s->eecd_state.bitnum_out = ((s->eecd_state.val_in & 0x3f)<<4)-1;
+        s->eecd_state.reading = (((s->eecd_state.val_in >> 6) & 7) ==
+            EEPROM_READ_OPCODE_MICROWIRE);
+    }
+    DBGOUT(EEPROM, "eeprom bitnum in %d out %d, reading %d\n",
+           s->eecd_state.bitnum_in, s->eecd_state.bitnum_out,
+           s->eecd_state.reading);
+}
+
+static uint32_t
+flash_eerd_read(E1000State *s, int x)
+{
+    unsigned int index, r = s->mac_reg[EERD] & ~E1000_EEPROM_RW_REG_START;
+
+    if ((s->mac_reg[EERD] & E1000_EEPROM_RW_REG_START) == 0)
+        return (s->mac_reg[EERD]);
+
+    if ((index = r >> E1000_EEPROM_RW_ADDR_SHIFT) > EEPROM_CHECKSUM_REG)
+        return (E1000_EEPROM_RW_REG_DONE | r);
+
+    return ((s->eeprom_data[index] << E1000_EEPROM_RW_REG_DATA) |
+           E1000_EEPROM_RW_REG_DONE | r);
+}
+
+static void
+putsum(uint8_t *data, uint32_t n, uint32_t sloc, uint32_t css, uint32_t cse)
+{
+    uint32_t sum;
+
+    if (cse && cse < n)
+        n = cse + 1;
+    if (sloc < n-1) {
+        sum = net_checksum_add(n-css, data+css);
+        cpu_to_be16wu((uint16_t *)(data + sloc),
+                      net_checksum_finish(sum));
+    }
+}
+
+static inline int
+vlan_enabled(E1000State *s)
+{
+    return ((s->mac_reg[CTRL] & E1000_CTRL_VME) != 0);
+}
+
+static inline int
+vlan_rx_filter_enabled(E1000State *s)
+{
+    return ((s->mac_reg[RCTL] & E1000_RCTL_VFE) != 0);
+}
+
+static inline int
+is_vlan_packet(E1000State *s, const uint8_t *buf)
+{
+    return (be16_to_cpup((uint16_t *)(buf + 12)) ==
+                le16_to_cpup((uint16_t *)(s->mac_reg + VET)));
+}
+
+static inline int
+is_vlan_txd(uint32_t txd_lower)
+{
+    return ((txd_lower & E1000_TXD_CMD_VLE) != 0);
+}
+
+/* FCS aka Ethernet CRC-32. We don't get it from backends and can't
+ * fill it in, just pad descriptor length by 4 bytes unless guest
+ * told us to strip it off the packet. */
+static inline int
+fcs_len(E1000State *s)
+{
+    return (s->mac_reg[RCTL] & E1000_RCTL_SECRC) ? 0 : 4;
+}
+
+static void
+e1000_send_packet(E1000State *s, const uint8_t *buf, int size)
+{
+    NetClientState *nc = qemu_get_queue(s->nic);
+    if (s->phy_reg[PHY_CTRL] & MII_CR_LOOPBACK) {
+        nc->info->receive(nc, buf, size);
+    } else {
+        qemu_send_packet(nc, buf, size);
+    }
+}
+
+static void
+xmit_seg(E1000State *s)
+{
+    uint16_t len, *sp;
+    unsigned int frames = s->tx.tso_frames, css, sofar, n;
+    struct e1000_tx *tp = &s->tx;
+
+    if (tp->tse && tp->cptse) {
+        css = tp->ipcss;
+        DBGOUT(TXSUM, "frames %d size %d ipcss %d\n",
+               frames, tp->size, css);
+        if (tp->ip) {          // IPv4
+            cpu_to_be16wu((uint16_t *)(tp->data+css+2),
+                          tp->size - css);
+            cpu_to_be16wu((uint16_t *)(tp->data+css+4),
+                          be16_to_cpup((uint16_t *)(tp->data+css+4))+frames);
+        } else                 // IPv6
+            cpu_to_be16wu((uint16_t *)(tp->data+css+4),
+                          tp->size - css);
+        css = tp->tucss;
+        len = tp->size - css;
+        DBGOUT(TXSUM, "tcp %d tucss %d len %d\n", tp->tcp, css, len);
+        if (tp->tcp) {
+            sofar = frames * tp->mss;
+            cpu_to_be32wu((uint32_t *)(tp->data+css+4),        // seq
+                be32_to_cpupu((uint32_t *)(tp->data+css+4))+sofar);
+            if (tp->paylen - sofar > tp->mss)
+                tp->data[css + 13] &= ~9;              // PSH, FIN
+        } else // UDP
+            cpu_to_be16wu((uint16_t *)(tp->data+css+4), len);
+        if (tp->sum_needed & E1000_TXD_POPTS_TXSM) {
+            unsigned int phsum;
+            // add pseudo-header length before checksum calculation
+            sp = (uint16_t *)(tp->data + tp->tucso);
+            phsum = be16_to_cpup(sp) + len;
+            phsum = (phsum >> 16) + (phsum & 0xffff);
+            cpu_to_be16wu(sp, phsum);
+        }
+        tp->tso_frames++;
+    }
+
+    if (tp->sum_needed & E1000_TXD_POPTS_TXSM)
+        putsum(tp->data, tp->size, tp->tucso, tp->tucss, tp->tucse);
+    if (tp->sum_needed & E1000_TXD_POPTS_IXSM)
+        putsum(tp->data, tp->size, tp->ipcso, tp->ipcss, tp->ipcse);
+    if (tp->vlan_needed) {
+        memmove(tp->vlan, tp->data, 4);
+        memmove(tp->data, tp->data + 4, 8);
+        memcpy(tp->data + 8, tp->vlan_header, 4);
+        e1000_send_packet(s, tp->vlan, tp->size + 4);
+    } else
+        e1000_send_packet(s, tp->data, tp->size);
+    s->mac_reg[TPT]++;
+    s->mac_reg[GPTC]++;
+    n = s->mac_reg[TOTL];
+    if ((s->mac_reg[TOTL] += s->tx.size) < n)
+        s->mac_reg[TOTH]++;
+}
+
+static void
+process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
+{
+    uint32_t txd_lower = le32_to_cpu(dp->lower.data);
+    uint32_t dtype = txd_lower & (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D);
+    unsigned int split_size = txd_lower & 0xffff, bytes, sz, op;
+    unsigned int msh = 0xfffff, hdr = 0;
+    uint64_t addr;
+    struct e1000_context_desc *xp = (struct e1000_context_desc *)dp;
+    struct e1000_tx *tp = &s->tx;
+
+    if (dtype == E1000_TXD_CMD_DEXT) { // context descriptor
+        op = le32_to_cpu(xp->cmd_and_length);
+        tp->ipcss = xp->lower_setup.ip_fields.ipcss;
+        tp->ipcso = xp->lower_setup.ip_fields.ipcso;
+        tp->ipcse = le16_to_cpu(xp->lower_setup.ip_fields.ipcse);
+        tp->tucss = xp->upper_setup.tcp_fields.tucss;
+        tp->tucso = xp->upper_setup.tcp_fields.tucso;
+        tp->tucse = le16_to_cpu(xp->upper_setup.tcp_fields.tucse);
+        tp->paylen = op & 0xfffff;
+        tp->hdr_len = xp->tcp_seg_setup.fields.hdr_len;
+        tp->mss = le16_to_cpu(xp->tcp_seg_setup.fields.mss);
+        tp->ip = (op & E1000_TXD_CMD_IP) ? 1 : 0;
+        tp->tcp = (op & E1000_TXD_CMD_TCP) ? 1 : 0;
+        tp->tse = (op & E1000_TXD_CMD_TSE) ? 1 : 0;
+        tp->tso_frames = 0;
+        if (tp->tucso == 0) {  // this is probably wrong
+            DBGOUT(TXSUM, "TCP/UDP: cso 0!\n");
+            tp->tucso = tp->tucss + (tp->tcp ? 16 : 6);
+        }
+        return;
+    } else if (dtype == (E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D)) {
+        // data descriptor
+        if (tp->size == 0) {
+            tp->sum_needed = le32_to_cpu(dp->upper.data) >> 8;
+        }
+        tp->cptse = ( txd_lower & E1000_TXD_CMD_TSE ) ? 1 : 0;
+    } else {
+        // legacy descriptor
+        tp->cptse = 0;
+    }
+
+    if (vlan_enabled(s) && is_vlan_txd(txd_lower) &&
+        (tp->cptse || txd_lower & E1000_TXD_CMD_EOP)) {
+        tp->vlan_needed = 1;
+        cpu_to_be16wu((uint16_t *)(tp->vlan_header),
+                      le16_to_cpup((uint16_t *)(s->mac_reg + VET)));
+        cpu_to_be16wu((uint16_t *)(tp->vlan_header + 2),
+                      le16_to_cpu(dp->upper.fields.special));
+    }
+        
+    addr = le64_to_cpu(dp->buffer_addr);
+    if (tp->tse && tp->cptse) {
+        hdr = tp->hdr_len;
+        msh = hdr + tp->mss;
+        do {
+            bytes = split_size;
+            if (tp->size + bytes > msh)
+                bytes = msh - tp->size;
+
+            bytes = MIN(sizeof(tp->data) - tp->size, bytes);
+            pci_dma_read(&s->dev, addr, tp->data + tp->size, bytes);
+            if ((sz = tp->size + bytes) >= hdr && tp->size < hdr)
+                memmove(tp->header, tp->data, hdr);
+            tp->size = sz;
+            addr += bytes;
+            if (sz == msh) {
+                xmit_seg(s);
+                memmove(tp->data, tp->header, hdr);
+                tp->size = hdr;
+            }
+        } while (split_size -= bytes);
+    } else if (!tp->tse && tp->cptse) {
+        // context descriptor TSE is not set, while data descriptor TSE is set
+        DBGOUT(TXERR, "TCP segmentation error\n");
+    } else {
+        split_size = MIN(sizeof(tp->data) - tp->size, split_size);
+        pci_dma_read(&s->dev, addr, tp->data + tp->size, split_size);
+        tp->size += split_size;
+    }
+
+    if (!(txd_lower & E1000_TXD_CMD_EOP))
+        return;
+    if (!(tp->tse && tp->cptse && tp->size < hdr))
+        xmit_seg(s);
+    tp->tso_frames = 0;
+    tp->sum_needed = 0;
+    tp->vlan_needed = 0;
+    tp->size = 0;
+    tp->cptse = 0;
+}
+
+static uint32_t
+txdesc_writeback(E1000State *s, dma_addr_t base, struct e1000_tx_desc *dp)
+{
+    uint32_t txd_upper, txd_lower = le32_to_cpu(dp->lower.data);
+
+    if (!(txd_lower & (E1000_TXD_CMD_RS|E1000_TXD_CMD_RPS)))
+        return 0;
+    txd_upper = (le32_to_cpu(dp->upper.data) | E1000_TXD_STAT_DD) &
+                ~(E1000_TXD_STAT_EC | E1000_TXD_STAT_LC | E1000_TXD_STAT_TU);
+    dp->upper.data = cpu_to_le32(txd_upper);
+    pci_dma_write(&s->dev, base + ((char *)&dp->upper - (char *)dp),
+                  &dp->upper, sizeof(dp->upper));
+    return E1000_ICR_TXDW;
+}
+
+static uint64_t tx_desc_base(E1000State *s)
+{
+    uint64_t bah = s->mac_reg[TDBAH];
+    uint64_t bal = s->mac_reg[TDBAL] & ~0xf;
+
+    return (bah << 32) + bal;
+}
+
+static void
+start_xmit(E1000State *s)
+{
+    dma_addr_t base;
+    struct e1000_tx_desc desc;
+    uint32_t tdh_start = s->mac_reg[TDH], cause = E1000_ICS_TXQE;
+
+    if (!(s->mac_reg[TCTL] & E1000_TCTL_EN)) {
+        DBGOUT(TX, "tx disabled\n");
+        return;
+    }
+
+    while (s->mac_reg[TDH] != s->mac_reg[TDT]) {
+        base = tx_desc_base(s) +
+               sizeof(struct e1000_tx_desc) * s->mac_reg[TDH];
+        pci_dma_read(&s->dev, base, &desc, sizeof(desc));
+
+        DBGOUT(TX, "index %d: %p : %x %x\n", s->mac_reg[TDH],
+               (void *)(intptr_t)desc.buffer_addr, desc.lower.data,
+               desc.upper.data);
+
+        process_tx_desc(s, &desc);
+        cause |= txdesc_writeback(s, base, &desc);
+
+        if (++s->mac_reg[TDH] * sizeof(desc) >= s->mac_reg[TDLEN])
+            s->mac_reg[TDH] = 0;
+        /*
+         * the following could happen only if guest sw assigns
+         * bogus values to TDT/TDLEN.
+         * there's nothing too intelligent we could do about this.
+         */
+        if (s->mac_reg[TDH] == tdh_start) {
+            DBGOUT(TXERR, "TDH wraparound @%x, TDT %x, TDLEN %x\n",
+                   tdh_start, s->mac_reg[TDT], s->mac_reg[TDLEN]);
+            break;
+        }
+    }
+    set_ics(s, 0, cause);
+}
+
+static int
+receive_filter(E1000State *s, const uint8_t *buf, int size)
+{
+    static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+    static const int mta_shift[] = {4, 3, 2, 0};
+    uint32_t f, rctl = s->mac_reg[RCTL], ra[2], *rp;
+
+    if (is_vlan_packet(s, buf) && vlan_rx_filter_enabled(s)) {
+        uint16_t vid = be16_to_cpup((uint16_t *)(buf + 14));
+        uint32_t vfta = le32_to_cpup((uint32_t *)(s->mac_reg + VFTA) +
+                                     ((vid >> 5) & 0x7f));
+        if ((vfta & (1 << (vid & 0x1f))) == 0)
+            return 0;
+    }
+
+    if (rctl & E1000_RCTL_UPE)                 // promiscuous
+        return 1;
+
+    if ((buf[0] & 1) && (rctl & E1000_RCTL_MPE))       // promiscuous mcast
+        return 1;
+
+    if ((rctl & E1000_RCTL_BAM) && !memcmp(buf, bcast, sizeof bcast))
+        return 1;
+
+    for (rp = s->mac_reg + RA; rp < s->mac_reg + RA + 32; rp += 2) {
+        if (!(rp[1] & E1000_RAH_AV))
+            continue;
+        ra[0] = cpu_to_le32(rp[0]);
+        ra[1] = cpu_to_le32(rp[1]);
+        if (!memcmp(buf, (uint8_t *)ra, 6)) {
+            DBGOUT(RXFILTER,
+                   "unicast match[%d]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+                   (int)(rp - s->mac_reg - RA)/2,
+                   buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+            return 1;
+        }
+    }
+    DBGOUT(RXFILTER, "unicast mismatch: %02x:%02x:%02x:%02x:%02x:%02x\n",
+           buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+
+    f = mta_shift[(rctl >> E1000_RCTL_MO_SHIFT) & 3];
+    f = (((buf[5] << 8) | buf[4]) >> f) & 0xfff;
+    if (s->mac_reg[MTA + (f >> 5)] & (1 << (f & 0x1f)))
+        return 1;
+    DBGOUT(RXFILTER,
+           "dropping, inexact filter mismatch: %02x:%02x:%02x:%02x:%02x:%02x MO %d MTA[%d] %x\n",
+           buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
+           (rctl >> E1000_RCTL_MO_SHIFT) & 3, f >> 5,
+           s->mac_reg[MTA + (f >> 5)]);
+
+    return 0;
+}
+
+static void
+e1000_set_link_status(NetClientState *nc)
+{
+    E1000State *s = qemu_get_nic_opaque(nc);
+    uint32_t old_status = s->mac_reg[STATUS];
+
+    if (nc->link_down) {
+        e1000_link_down(s);
+    } else {
+        e1000_link_up(s);
+    }
+
+    if (s->mac_reg[STATUS] != old_status)
+        set_ics(s, 0, E1000_ICR_LSC);
+}
+
+static bool e1000_has_rxbufs(E1000State *s, size_t total_size)
+{
+    int bufs;
+    /* Fast-path short packets */
+    if (total_size <= s->rxbuf_size) {
+        return s->mac_reg[RDH] != s->mac_reg[RDT];
+    }
+    if (s->mac_reg[RDH] < s->mac_reg[RDT]) {
+        bufs = s->mac_reg[RDT] - s->mac_reg[RDH];
+    } else if (s->mac_reg[RDH] > s->mac_reg[RDT]) {
+        bufs = s->mac_reg[RDLEN] /  sizeof(struct e1000_rx_desc) +
+            s->mac_reg[RDT] - s->mac_reg[RDH];
+    } else {
+        return false;
+    }
+    return total_size <= bufs * s->rxbuf_size;
+}
+
+static int
+e1000_can_receive(NetClientState *nc)
+{
+    E1000State *s = qemu_get_nic_opaque(nc);
+
+    return (s->mac_reg[STATUS] & E1000_STATUS_LU) &&
+        (s->mac_reg[RCTL] & E1000_RCTL_EN) && e1000_has_rxbufs(s, 1);
+}
+
+static uint64_t rx_desc_base(E1000State *s)
+{
+    uint64_t bah = s->mac_reg[RDBAH];
+    uint64_t bal = s->mac_reg[RDBAL] & ~0xf;
+
+    return (bah << 32) + bal;
+}
+
+static ssize_t
+e1000_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+    E1000State *s = qemu_get_nic_opaque(nc);
+    struct e1000_rx_desc desc;
+    dma_addr_t base;
+    unsigned int n, rdt;
+    uint32_t rdh_start;
+    uint16_t vlan_special = 0;
+    uint8_t vlan_status = 0, vlan_offset = 0;
+    uint8_t min_buf[MIN_BUF_SIZE];
+    size_t desc_offset;
+    size_t desc_size;
+    size_t total_size;
+
+    if (!(s->mac_reg[STATUS] & E1000_STATUS_LU)) {
+        return -1;
+    }
+
+    if (!(s->mac_reg[RCTL] & E1000_RCTL_EN)) {
+        return -1;
+    }
+
+    /* Pad to minimum Ethernet frame length */
+    if (size < sizeof(min_buf)) {
+        memcpy(min_buf, buf, size);
+        memset(&min_buf[size], 0, sizeof(min_buf) - size);
+        buf = min_buf;
+        size = sizeof(min_buf);
+    }
+
+    /* Discard oversized packets if !LPE and !SBP. */
+    if ((size > MAXIMUM_ETHERNET_LPE_SIZE ||
+        (size > MAXIMUM_ETHERNET_VLAN_SIZE
+        && !(s->mac_reg[RCTL] & E1000_RCTL_LPE)))
+        && !(s->mac_reg[RCTL] & E1000_RCTL_SBP)) {
+        return size;
+    }
+
+    if (!receive_filter(s, buf, size))
+        return size;
+
+    if (vlan_enabled(s) && is_vlan_packet(s, buf)) {
+        vlan_special = cpu_to_le16(be16_to_cpup((uint16_t *)(buf + 14)));
+        memmove((uint8_t *)buf + 4, buf, 12);
+        vlan_status = E1000_RXD_STAT_VP;
+        vlan_offset = 4;
+        size -= 4;
+    }
+
+    rdh_start = s->mac_reg[RDH];
+    desc_offset = 0;
+    total_size = size + fcs_len(s);
+    if (!e1000_has_rxbufs(s, total_size)) {
+            set_ics(s, 0, E1000_ICS_RXO);
+            return -1;
+    }
+    do {
+        desc_size = total_size - desc_offset;
+        if (desc_size > s->rxbuf_size) {
+            desc_size = s->rxbuf_size;
+        }
+        base = rx_desc_base(s) + sizeof(desc) * s->mac_reg[RDH];
+        pci_dma_read(&s->dev, base, &desc, sizeof(desc));
+        desc.special = vlan_special;
+        desc.status |= (vlan_status | E1000_RXD_STAT_DD);
+        if (desc.buffer_addr) {
+            if (desc_offset < size) {
+                size_t copy_size = size - desc_offset;
+                if (copy_size > s->rxbuf_size) {
+                    copy_size = s->rxbuf_size;
+                }
+                pci_dma_write(&s->dev, le64_to_cpu(desc.buffer_addr),
+                              buf + desc_offset + vlan_offset, copy_size);
+            }
+            desc_offset += desc_size;
+            desc.length = cpu_to_le16(desc_size);
+            if (desc_offset >= total_size) {
+                desc.status |= E1000_RXD_STAT_EOP | E1000_RXD_STAT_IXSM;
+            } else {
+                /* Guest zeroing out status is not a hardware requirement.
+                   Clear EOP in case guest didn't do it. */
+                desc.status &= ~E1000_RXD_STAT_EOP;
+            }
+        } else { // as per intel docs; skip descriptors with null buf addr
+            DBGOUT(RX, "Null RX descriptor!!\n");
+        }
+        pci_dma_write(&s->dev, base, &desc, sizeof(desc));
+
+        if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN])
+            s->mac_reg[RDH] = 0;
+        /* see comment in start_xmit; same here */
+        if (s->mac_reg[RDH] == rdh_start) {
+            DBGOUT(RXERR, "RDH wraparound @%x, RDT %x, RDLEN %x\n",
+                   rdh_start, s->mac_reg[RDT], s->mac_reg[RDLEN]);
+            set_ics(s, 0, E1000_ICS_RXO);
+            return -1;
+        }
+    } while (desc_offset < total_size);
+
+    s->mac_reg[GPRC]++;
+    s->mac_reg[TPR]++;
+    /* TOR - Total Octets Received:
+     * This register includes bytes received in a packet from the <Destination
+     * Address> field through the <CRC> field, inclusively.
+     */
+    n = s->mac_reg[TORL] + size + /* Always include FCS length. */ 4;
+    if (n < s->mac_reg[TORL])
+        s->mac_reg[TORH]++;
+    s->mac_reg[TORL] = n;
+
+    n = E1000_ICS_RXT0;
+    if ((rdt = s->mac_reg[RDT]) < s->mac_reg[RDH])
+        rdt += s->mac_reg[RDLEN] / sizeof(desc);
+    if (((rdt - s->mac_reg[RDH]) * sizeof(desc)) <= s->mac_reg[RDLEN] >>
+        s->rxbuf_min_shift)
+        n |= E1000_ICS_RXDMT0;
+
+    set_ics(s, 0, n);
+
+    return size;
+}
+
+static uint32_t
+mac_readreg(E1000State *s, int index)
+{
+    return s->mac_reg[index];
+}
+
+static uint32_t
+mac_icr_read(E1000State *s, int index)
+{
+    uint32_t ret = s->mac_reg[ICR];
+
+    DBGOUT(INTERRUPT, "ICR read: %x\n", ret);
+    set_interrupt_cause(s, 0, 0);
+    return ret;
+}
+
+static uint32_t
+mac_read_clr4(E1000State *s, int index)
+{
+    uint32_t ret = s->mac_reg[index];
+
+    s->mac_reg[index] = 0;
+    return ret;
+}
+
+static uint32_t
+mac_read_clr8(E1000State *s, int index)
+{
+    uint32_t ret = s->mac_reg[index];
+
+    s->mac_reg[index] = 0;
+    s->mac_reg[index-1] = 0;
+    return ret;
+}
+
+static void
+mac_writereg(E1000State *s, int index, uint32_t val)
+{
+    s->mac_reg[index] = val;
+}
+
+static void
+set_rdt(E1000State *s, int index, uint32_t val)
+{
+    s->mac_reg[index] = val & 0xffff;
+    if (e1000_has_rxbufs(s, 1)) {
+        qemu_flush_queued_packets(qemu_get_queue(s->nic));
+    }
+}
+
+static void
+set_16bit(E1000State *s, int index, uint32_t val)
+{
+    s->mac_reg[index] = val & 0xffff;
+}
+
+static void
+set_dlen(E1000State *s, int index, uint32_t val)
+{
+    s->mac_reg[index] = val & 0xfff80;
+}
+
+static void
+set_tctl(E1000State *s, int index, uint32_t val)
+{
+    s->mac_reg[index] = val;
+    s->mac_reg[TDT] &= 0xffff;
+    start_xmit(s);
+}
+
+static void
+set_icr(E1000State *s, int index, uint32_t val)
+{
+    DBGOUT(INTERRUPT, "set_icr %x\n", val);
+    set_interrupt_cause(s, 0, s->mac_reg[ICR] & ~val);
+}
+
+static void
+set_imc(E1000State *s, int index, uint32_t val)
+{
+    s->mac_reg[IMS] &= ~val;
+    set_ics(s, 0, 0);
+}
+
+static void
+set_ims(E1000State *s, int index, uint32_t val)
+{
+    s->mac_reg[IMS] |= val;
+    set_ics(s, 0, 0);
+}
+
+#define getreg(x)      [x] = mac_readreg
+static uint32_t (*macreg_readops[])(E1000State *, int) = {
+    getreg(PBA),       getreg(RCTL),   getreg(TDH),    getreg(TXDCTL),
+    getreg(WUFC),      getreg(TDT),    getreg(CTRL),   getreg(LEDCTL),
+    getreg(MANC),      getreg(MDIC),   getreg(SWSM),   getreg(STATUS),
+    getreg(TORL),      getreg(TOTL),   getreg(IMS),    getreg(TCTL),
+    getreg(RDH),       getreg(RDT),    getreg(VET),    getreg(ICS),
+    getreg(TDBAL),     getreg(TDBAH),  getreg(RDBAH),  getreg(RDBAL),
+    getreg(TDLEN),     getreg(RDLEN),
+
+    [TOTH] = mac_read_clr8,    [TORH] = mac_read_clr8, [GPRC] = mac_read_clr4,
+    [GPTC] = mac_read_clr4,    [TPR] = mac_read_clr4,  [TPT] = mac_read_clr4,
+    [ICR] = mac_icr_read,      [EECD] = get_eecd,      [EERD] = flash_eerd_read,
+    [CRCERRS ... MPC] = &mac_readreg,
+    [RA ... RA+31] = &mac_readreg,
+    [MTA ... MTA+127] = &mac_readreg,
+    [VFTA ... VFTA+127] = &mac_readreg,
+};
+enum { NREADOPS = ARRAY_SIZE(macreg_readops) };
+
+#define putreg(x)      [x] = mac_writereg
+static void (*macreg_writeops[])(E1000State *, int, uint32_t) = {
+    putreg(PBA),       putreg(EERD),   putreg(SWSM),   putreg(WUFC),
+    putreg(TDBAL),     putreg(TDBAH),  putreg(TXDCTL), putreg(RDBAH),
+    putreg(RDBAL),     putreg(LEDCTL), putreg(VET),
+    [TDLEN] = set_dlen,        [RDLEN] = set_dlen,     [TCTL] = set_tctl,
+    [TDT] = set_tctl,  [MDIC] = set_mdic,      [ICS] = set_ics,
+    [TDH] = set_16bit, [RDH] = set_16bit,      [RDT] = set_rdt,
+    [IMC] = set_imc,   [IMS] = set_ims,        [ICR] = set_icr,
+    [EECD] = set_eecd, [RCTL] = set_rx_control, [CTRL] = set_ctrl,
+    [RA ... RA+31] = &mac_writereg,
+    [MTA ... MTA+127] = &mac_writereg,
+    [VFTA ... VFTA+127] = &mac_writereg,
+};
+
+enum { NWRITEOPS = ARRAY_SIZE(macreg_writeops) };
+
+static void
+e1000_mmio_write(void *opaque, hwaddr addr, uint64_t val,
+                 unsigned size)
+{
+    E1000State *s = opaque;
+    unsigned int index = (addr & 0x1ffff) >> 2;
+
+    if (index < NWRITEOPS && macreg_writeops[index]) {
+        macreg_writeops[index](s, index, val);
+    } else if (index < NREADOPS && macreg_readops[index]) {
+        DBGOUT(MMIO, "e1000_mmio_writel RO %x: 0x%04"PRIx64"\n", index<<2, val);
+    } else {
+        DBGOUT(UNKNOWN, "MMIO unknown write addr=0x%08x,val=0x%08"PRIx64"\n",
+               index<<2, val);
+    }
+}
+
+static uint64_t
+e1000_mmio_read(void *opaque, hwaddr addr, unsigned size)
+{
+    E1000State *s = opaque;
+    unsigned int index = (addr & 0x1ffff) >> 2;
+
+    if (index < NREADOPS && macreg_readops[index])
+    {
+        return macreg_readops[index](s, index);
+    }
+    DBGOUT(UNKNOWN, "MMIO unknown read addr=0x%08x\n", index<<2);
+    return 0;
+}
+
+static const MemoryRegionOps e1000_mmio_ops = {
+    .read = e1000_mmio_read,
+    .write = e1000_mmio_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static uint64_t e1000_io_read(void *opaque, hwaddr addr,
+                              unsigned size)
+{
+    E1000State *s = opaque;
+
+    (void)s;
+    return 0;
+}
+
+static void e1000_io_write(void *opaque, hwaddr addr,
+                           uint64_t val, unsigned size)
+{
+    E1000State *s = opaque;
+
+    (void)s;
+}
+
+static const MemoryRegionOps e1000_io_ops = {
+    .read = e1000_io_read,
+    .write = e1000_io_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static bool is_version_1(void *opaque, int version_id)
+{
+    return version_id == 1;
+}
+
+static void e1000_pre_save(void *opaque)
+{
+    E1000State *s = opaque;
+    NetClientState *nc = qemu_get_queue(s->nic);
+
+    if (!(s->compat_flags & E1000_FLAG_AUTONEG)) {
+        return;
+    }
+
+    /*
+     * If link is down and auto-negotiation is ongoing, complete
+     * auto-negotiation immediately.  This allows is to look at
+     * MII_SR_AUTONEG_COMPLETE to infer link status on load.
+     */
+    if (nc->link_down &&
+        s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN &&
+        s->phy_reg[PHY_CTRL] & MII_CR_RESTART_AUTO_NEG) {
+         s->phy_reg[PHY_STATUS] |= MII_SR_AUTONEG_COMPLETE;
+    }
+}
+
+static int e1000_post_load(void *opaque, int version_id)
+{
+    E1000State *s = opaque;
+    NetClientState *nc = qemu_get_queue(s->nic);
+
+    /* nc.link_down can't be migrated, so infer link_down according
+     * to link status bit in mac_reg[STATUS].
+     * Alternatively, restart link negotiation if it was in progress. */
+    nc->link_down = (s->mac_reg[STATUS] & E1000_STATUS_LU) == 0;
+
+    if (!(s->compat_flags & E1000_FLAG_AUTONEG)) {
+        return 0;
+    }
+
+    if (s->phy_reg[PHY_CTRL] & MII_CR_AUTO_NEG_EN &&
+        s->phy_reg[PHY_CTRL] & MII_CR_RESTART_AUTO_NEG &&
+        !(s->phy_reg[PHY_STATUS] & MII_SR_AUTONEG_COMPLETE)) {
+        nc->link_down = false;
+        qemu_mod_timer(s->autoneg_timer, qemu_get_clock_ms(vm_clock) + 500);
+    }
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_e1000 = {
+    .name = "e1000",
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_save = e1000_pre_save,
+    .post_load = e1000_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_PCI_DEVICE(dev, E1000State),
+        VMSTATE_UNUSED_TEST(is_version_1, 4), /* was instance id */
+        VMSTATE_UNUSED(4), /* Was mmio_base.  */
+        VMSTATE_UINT32(rxbuf_size, E1000State),
+        VMSTATE_UINT32(rxbuf_min_shift, E1000State),
+        VMSTATE_UINT32(eecd_state.val_in, E1000State),
+        VMSTATE_UINT16(eecd_state.bitnum_in, E1000State),
+        VMSTATE_UINT16(eecd_state.bitnum_out, E1000State),
+        VMSTATE_UINT16(eecd_state.reading, E1000State),
+        VMSTATE_UINT32(eecd_state.old_eecd, E1000State),
+        VMSTATE_UINT8(tx.ipcss, E1000State),
+        VMSTATE_UINT8(tx.ipcso, E1000State),
+        VMSTATE_UINT16(tx.ipcse, E1000State),
+        VMSTATE_UINT8(tx.tucss, E1000State),
+        VMSTATE_UINT8(tx.tucso, E1000State),
+        VMSTATE_UINT16(tx.tucse, E1000State),
+        VMSTATE_UINT32(tx.paylen, E1000State),
+        VMSTATE_UINT8(tx.hdr_len, E1000State),
+        VMSTATE_UINT16(tx.mss, E1000State),
+        VMSTATE_UINT16(tx.size, E1000State),
+        VMSTATE_UINT16(tx.tso_frames, E1000State),
+        VMSTATE_UINT8(tx.sum_needed, E1000State),
+        VMSTATE_INT8(tx.ip, E1000State),
+        VMSTATE_INT8(tx.tcp, E1000State),
+        VMSTATE_BUFFER(tx.header, E1000State),
+        VMSTATE_BUFFER(tx.data, E1000State),
+        VMSTATE_UINT16_ARRAY(eeprom_data, E1000State, 64),
+        VMSTATE_UINT16_ARRAY(phy_reg, E1000State, 0x20),
+        VMSTATE_UINT32(mac_reg[CTRL], E1000State),
+        VMSTATE_UINT32(mac_reg[EECD], E1000State),
+        VMSTATE_UINT32(mac_reg[EERD], E1000State),
+        VMSTATE_UINT32(mac_reg[GPRC], E1000State),
+        VMSTATE_UINT32(mac_reg[GPTC], E1000State),
+        VMSTATE_UINT32(mac_reg[ICR], E1000State),
+        VMSTATE_UINT32(mac_reg[ICS], E1000State),
+        VMSTATE_UINT32(mac_reg[IMC], E1000State),
+        VMSTATE_UINT32(mac_reg[IMS], E1000State),
+        VMSTATE_UINT32(mac_reg[LEDCTL], E1000State),
+        VMSTATE_UINT32(mac_reg[MANC], E1000State),
+        VMSTATE_UINT32(mac_reg[MDIC], E1000State),
+        VMSTATE_UINT32(mac_reg[MPC], E1000State),
+        VMSTATE_UINT32(mac_reg[PBA], E1000State),
+        VMSTATE_UINT32(mac_reg[RCTL], E1000State),
+        VMSTATE_UINT32(mac_reg[RDBAH], E1000State),
+        VMSTATE_UINT32(mac_reg[RDBAL], E1000State),
+        VMSTATE_UINT32(mac_reg[RDH], E1000State),
+        VMSTATE_UINT32(mac_reg[RDLEN], E1000State),
+        VMSTATE_UINT32(mac_reg[RDT], E1000State),
+        VMSTATE_UINT32(mac_reg[STATUS], E1000State),
+        VMSTATE_UINT32(mac_reg[SWSM], E1000State),
+        VMSTATE_UINT32(mac_reg[TCTL], E1000State),
+        VMSTATE_UINT32(mac_reg[TDBAH], E1000State),
+        VMSTATE_UINT32(mac_reg[TDBAL], E1000State),
+        VMSTATE_UINT32(mac_reg[TDH], E1000State),
+        VMSTATE_UINT32(mac_reg[TDLEN], E1000State),
+        VMSTATE_UINT32(mac_reg[TDT], E1000State),
+        VMSTATE_UINT32(mac_reg[TORH], E1000State),
+        VMSTATE_UINT32(mac_reg[TORL], E1000State),
+        VMSTATE_UINT32(mac_reg[TOTH], E1000State),
+        VMSTATE_UINT32(mac_reg[TOTL], E1000State),
+        VMSTATE_UINT32(mac_reg[TPR], E1000State),
+        VMSTATE_UINT32(mac_reg[TPT], E1000State),
+        VMSTATE_UINT32(mac_reg[TXDCTL], E1000State),
+        VMSTATE_UINT32(mac_reg[WUFC], E1000State),
+        VMSTATE_UINT32(mac_reg[VET], E1000State),
+        VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, RA, 32),
+        VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, MTA, 128),
+        VMSTATE_UINT32_SUB_ARRAY(mac_reg, E1000State, VFTA, 128),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const uint16_t e1000_eeprom_template[64] = {
+    0x0000, 0x0000, 0x0000, 0x0000,      0xffff, 0x0000,      0x0000, 0x0000,
+    0x3000, 0x1000, 0x6403, E1000_DEVID, 0x8086, E1000_DEVID, 0x8086, 0x3040,
+    0x0008, 0x2000, 0x7e14, 0x0048,      0x1000, 0x00d8,      0x0000, 0x2700,
+    0x6cc9, 0x3150, 0x0722, 0x040b,      0x0984, 0x0000,      0xc000, 0x0706,
+    0x1008, 0x0000, 0x0f04, 0x7fff,      0x4d01, 0xffff,      0xffff, 0xffff,
+    0xffff, 0xffff, 0xffff, 0xffff,      0xffff, 0xffff,      0xffff, 0xffff,
+    0x0100, 0x4000, 0x121c, 0xffff,      0xffff, 0xffff,      0xffff, 0xffff,
+    0xffff, 0xffff, 0xffff, 0xffff,      0xffff, 0xffff,      0xffff, 0x0000,
+};
+
+/* PCI interface */
+
+static void
+e1000_mmio_setup(E1000State *d)
+{
+    int i;
+    const uint32_t excluded_regs[] = {
+        E1000_MDIC, E1000_ICR, E1000_ICS, E1000_IMS,
+        E1000_IMC, E1000_TCTL, E1000_TDT, PNPMMIO_SIZE
+    };
+
+    memory_region_init_io(&d->mmio, &e1000_mmio_ops, d, "e1000-mmio",
+                          PNPMMIO_SIZE);
+    memory_region_add_coalescing(&d->mmio, 0, excluded_regs[0]);
+    for (i = 0; excluded_regs[i] != PNPMMIO_SIZE; i++)
+        memory_region_add_coalescing(&d->mmio, excluded_regs[i] + 4,
+                                     excluded_regs[i+1] - excluded_regs[i] - 4);
+    memory_region_init_io(&d->io, &e1000_io_ops, d, "e1000-io", IOPORT_SIZE);
+}
+
+static void
+e1000_cleanup(NetClientState *nc)
+{
+    E1000State *s = qemu_get_nic_opaque(nc);
+
+    s->nic = NULL;
+}
+
+static void
+pci_e1000_uninit(PCIDevice *dev)
+{
+    E1000State *d = DO_UPCAST(E1000State, dev, dev);
+
+    qemu_del_timer(d->autoneg_timer);
+    qemu_free_timer(d->autoneg_timer);
+    memory_region_destroy(&d->mmio);
+    memory_region_destroy(&d->io);
+    qemu_del_nic(d->nic);
+}
+
+static NetClientInfo net_e1000_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = e1000_can_receive,
+    .receive = e1000_receive,
+    .cleanup = e1000_cleanup,
+    .link_status_changed = e1000_set_link_status,
+};
+
+static int pci_e1000_init(PCIDevice *pci_dev)
+{
+    E1000State *d = DO_UPCAST(E1000State, dev, pci_dev);
+    uint8_t *pci_conf;
+    uint16_t checksum = 0;
+    int i;
+    uint8_t *macaddr;
+
+    pci_conf = d->dev.config;
+
+    /* TODO: RST# value should be 0, PCI spec 6.2.4 */
+    pci_conf[PCI_CACHE_LINE_SIZE] = 0x10;
+
+    pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */
+
+    e1000_mmio_setup(d);
+
+    pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
+
+    pci_register_bar(&d->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->io);
+
+    memmove(d->eeprom_data, e1000_eeprom_template,
+        sizeof e1000_eeprom_template);
+    qemu_macaddr_default_if_unset(&d->conf.macaddr);
+    macaddr = d->conf.macaddr.a;
+    for (i = 0; i < 3; i++)
+        d->eeprom_data[i] = (macaddr[2*i+1]<<8) | macaddr[2*i];
+    for (i = 0; i < EEPROM_CHECKSUM_REG; i++)
+        checksum += d->eeprom_data[i];
+    checksum = (uint16_t) EEPROM_SUM - checksum;
+    d->eeprom_data[EEPROM_CHECKSUM_REG] = checksum;
+
+    d->nic = qemu_new_nic(&net_e1000_info, &d->conf,
+                          object_get_typename(OBJECT(d)), d->dev.qdev.id, d);
+
+    qemu_format_nic_info_str(qemu_get_queue(d->nic), macaddr);
+
+    add_boot_device_path(d->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0");
+
+    d->autoneg_timer = qemu_new_timer_ms(vm_clock, e1000_autoneg_timer, d);
+
+    return 0;
+}
+
+static void qdev_e1000_reset(DeviceState *dev)
+{
+    E1000State *d = DO_UPCAST(E1000State, dev.qdev, dev);
+    e1000_reset(d);
+}
+
+static Property e1000_properties[] = {
+    DEFINE_NIC_PROPERTIES(E1000State, conf),
+    DEFINE_PROP_BIT("autonegotiation", E1000State,
+                    compat_flags, E1000_FLAG_AUTONEG_BIT, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void e1000_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = pci_e1000_init;
+    k->exit = pci_e1000_uninit;
+    k->romfile = "efi-e1000.rom";
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = E1000_DEVID;
+    k->revision = 0x03;
+    k->class_id = PCI_CLASS_NETWORK_ETHERNET;
+    dc->desc = "Intel Gigabit Ethernet";
+    dc->reset = qdev_e1000_reset;
+    dc->vmsd = &vmstate_e1000;
+    dc->props = e1000_properties;
+}
+
+static const TypeInfo e1000_info = {
+    .name          = "e1000",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(E1000State),
+    .class_init    = e1000_class_init,
+};
+
+static void e1000_register_types(void)
+{
+    type_register_static(&e1000_info);
+}
+
+type_init(e1000_register_types)
diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
new file mode 100644 (file)
index 0000000..dc99ea6
--- /dev/null
@@ -0,0 +1,2115 @@
+/*
+ * QEMU i8255x (PRO100) emulation
+ *
+ * Copyright (C) 2006-2011 Stefan Weil
+ *
+ * Portions of the code are copies from grub / etherboot eepro100.c
+ * and linux e100.c.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) version 3 or any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Tested features (i82559):
+ *      PXE boot (i386 guest, i386 / mips / mipsel / ppc host) ok
+ *      Linux networking (i386) ok
+ *
+ * Untested:
+ *      Windows networking
+ *
+ * References:
+ *
+ * Intel 8255x 10/100 Mbps Ethernet Controller Family
+ * Open Source Software Developer Manual
+ *
+ * TODO:
+ *      * PHY emulation should be separated from nic emulation.
+ *        Most nic emulations could share the same phy code.
+ *      * i82550 is untested. It is programmed like the i82559.
+ *      * i82562 is untested. It is programmed like the i82559.
+ *      * Power management (i82558 and later) is not implemented.
+ *      * Wake-on-LAN is not implemented.
+ */
+
+#include <stddef.h>             /* offsetof */
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "net/net.h"
+#include "hw/nvram/eeprom93xx.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/dma.h"
+
+/* QEMU sends frames smaller than 60 bytes to ethernet nics.
+ * Such frames are rejected by real nics and their emulations.
+ * To avoid this behaviour, other nic emulations pad received
+ * frames. The following definition enables this padding for
+ * eepro100, too. We keep the define around in case it might
+ * become useful the future if the core networking is ever
+ * changed to pad short packets itself. */
+#define CONFIG_PAD_RECEIVED_FRAMES
+
+#define KiB 1024
+
+/* Debug EEPRO100 card. */
+#if 0
+# define DEBUG_EEPRO100
+#endif
+
+#ifdef DEBUG_EEPRO100
+#define logout(fmt, ...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ## __VA_ARGS__)
+#else
+#define logout(fmt, ...) ((void)0)
+#endif
+
+/* Set flags to 0 to disable debug output. */
+#define INT     1       /* interrupt related actions */
+#define MDI     1       /* mdi related actions */
+#define OTHER   1
+#define RXTX    1
+#define EEPROM  1       /* eeprom related actions */
+
+#define TRACE(flag, command) ((flag) ? (command) : (void)0)
+
+#define missing(text) fprintf(stderr, "eepro100: feature is missing in this emulation: " text "\n")
+
+#define MAX_ETH_FRAME_SIZE 1514
+
+/* This driver supports several different devices which are declared here. */
+#define i82550          0x82550
+#define i82551          0x82551
+#define i82557A         0x82557a
+#define i82557B         0x82557b
+#define i82557C         0x82557c
+#define i82558A         0x82558a
+#define i82558B         0x82558b
+#define i82559A         0x82559a
+#define i82559B         0x82559b
+#define i82559C         0x82559c
+#define i82559ER        0x82559e
+#define i82562          0x82562
+#define i82801          0x82801
+
+/* Use 64 word EEPROM. TODO: could be a runtime option. */
+#define EEPROM_SIZE     64
+
+#define PCI_MEM_SIZE            (4 * KiB)
+#define PCI_IO_SIZE             64
+#define PCI_FLASH_SIZE          (128 * KiB)
+
+#define BIT(n) (1 << (n))
+#define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m)
+
+/* The SCB accepts the following controls for the Tx and Rx units: */
+#define  CU_NOP         0x0000  /* No operation. */
+#define  CU_START       0x0010  /* CU start. */
+#define  CU_RESUME      0x0020  /* CU resume. */
+#define  CU_STATSADDR   0x0040  /* Load dump counters address. */
+#define  CU_SHOWSTATS   0x0050  /* Dump statistical counters. */
+#define  CU_CMD_BASE    0x0060  /* Load CU base address. */
+#define  CU_DUMPSTATS   0x0070  /* Dump and reset statistical counters. */
+#define  CU_SRESUME     0x00a0  /* CU static resume. */
+
+#define  RU_NOP         0x0000
+#define  RX_START       0x0001
+#define  RX_RESUME      0x0002
+#define  RU_ABORT       0x0004
+#define  RX_ADDR_LOAD   0x0006
+#define  RX_RESUMENR    0x0007
+#define INT_MASK        0x0100
+#define DRVR_INT        0x0200  /* Driver generated interrupt. */
+
+typedef struct {
+    const char *name;
+    const char *desc;
+    uint16_t device_id;
+    uint8_t revision;
+    uint16_t subsystem_vendor_id;
+    uint16_t subsystem_id;
+
+    uint32_t device;
+    uint8_t stats_size;
+    bool has_extended_tcb_support;
+    bool power_management;
+} E100PCIDeviceInfo;
+
+/* Offsets to the various registers.
+   All accesses need not be longword aligned. */
+typedef enum {
+    SCBStatus = 0,              /* Status Word. */
+    SCBAck = 1,
+    SCBCmd = 2,                 /* Rx/Command Unit command and status. */
+    SCBIntmask = 3,
+    SCBPointer = 4,             /* General purpose pointer. */
+    SCBPort = 8,                /* Misc. commands and operands.  */
+    SCBflash = 12,              /* Flash memory control. */
+    SCBeeprom = 14,             /* EEPROM control. */
+    SCBCtrlMDI = 16,            /* MDI interface control. */
+    SCBEarlyRx = 20,            /* Early receive byte count. */
+    SCBFlow = 24,               /* Flow Control. */
+    SCBpmdr = 27,               /* Power Management Driver. */
+    SCBgctrl = 28,              /* General Control. */
+    SCBgstat = 29,              /* General Status. */
+} E100RegisterOffset;
+
+/* A speedo3 transmit buffer descriptor with two buffers... */
+typedef struct {
+    uint16_t status;
+    uint16_t command;
+    uint32_t link;              /* void * */
+    uint32_t tbd_array_addr;    /* transmit buffer descriptor array address. */
+    uint16_t tcb_bytes;         /* transmit command block byte count (in lower 14 bits */
+    uint8_t tx_threshold;       /* transmit threshold */
+    uint8_t tbd_count;          /* TBD number */
+#if 0
+    /* This constitutes two "TBD" entries: hdr and data */
+    uint32_t tx_buf_addr0;  /* void *, header of frame to be transmitted.  */
+    int32_t  tx_buf_size0;  /* Length of Tx hdr. */
+    uint32_t tx_buf_addr1;  /* void *, data to be transmitted.  */
+    int32_t  tx_buf_size1;  /* Length of Tx data. */
+#endif
+} eepro100_tx_t;
+
+/* Receive frame descriptor. */
+typedef struct {
+    int16_t status;
+    uint16_t command;
+    uint32_t link;              /* struct RxFD * */
+    uint32_t rx_buf_addr;       /* void * */
+    uint16_t count;
+    uint16_t size;
+    /* Ethernet frame data follows. */
+} eepro100_rx_t;
+
+typedef enum {
+    COMMAND_EL = BIT(15),
+    COMMAND_S = BIT(14),
+    COMMAND_I = BIT(13),
+    COMMAND_NC = BIT(4),
+    COMMAND_SF = BIT(3),
+    COMMAND_CMD = BITS(2, 0),
+} scb_command_bit;
+
+typedef enum {
+    STATUS_C = BIT(15),
+    STATUS_OK = BIT(13),
+} scb_status_bit;
+
+typedef struct {
+    uint32_t tx_good_frames, tx_max_collisions, tx_late_collisions,
+             tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions,
+             tx_multiple_collisions, tx_total_collisions;
+    uint32_t rx_good_frames, rx_crc_errors, rx_alignment_errors,
+             rx_resource_errors, rx_overrun_errors, rx_cdt_errors,
+             rx_short_frame_errors;
+    uint32_t fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported;
+    uint16_t xmt_tco_frames, rcv_tco_frames;
+    /* TODO: i82559 has six reserved statistics but a total of 24 dwords. */
+    uint32_t reserved[4];
+} eepro100_stats_t;
+
+typedef enum {
+    cu_idle = 0,
+    cu_suspended = 1,
+    cu_active = 2,
+    cu_lpq_active = 2,
+    cu_hqp_active = 3
+} cu_state_t;
+
+typedef enum {
+    ru_idle = 0,
+    ru_suspended = 1,
+    ru_no_resources = 2,
+    ru_ready = 4
+} ru_state_t;
+
+typedef struct {
+    PCIDevice dev;
+    /* Hash register (multicast mask array, multiple individual addresses). */
+    uint8_t mult[8];
+    MemoryRegion mmio_bar;
+    MemoryRegion io_bar;
+    MemoryRegion flash_bar;
+    NICState *nic;
+    NICConf conf;
+    uint8_t scb_stat;           /* SCB stat/ack byte */
+    uint8_t int_stat;           /* PCI interrupt status */
+    /* region must not be saved by nic_save. */
+    uint16_t mdimem[32];
+    eeprom_t *eeprom;
+    uint32_t device;            /* device variant */
+    /* (cu_base + cu_offset) address the next command block in the command block list. */
+    uint32_t cu_base;           /* CU base address */
+    uint32_t cu_offset;         /* CU address offset */
+    /* (ru_base + ru_offset) address the RFD in the Receive Frame Area. */
+    uint32_t ru_base;           /* RU base address */
+    uint32_t ru_offset;         /* RU address offset */
+    uint32_t statsaddr;         /* pointer to eepro100_stats_t */
+
+    /* Temporary status information (no need to save these values),
+     * used while processing CU commands. */
+    eepro100_tx_t tx;           /* transmit buffer descriptor */
+    uint32_t cb_address;        /* = cu_base + cu_offset */
+
+    /* Statistical counters. Also used for wake-up packet (i82559). */
+    eepro100_stats_t statistics;
+
+    /* Data in mem is always in the byte order of the controller (le).
+     * It must be dword aligned to allow direct access to 32 bit values. */
+    uint8_t mem[PCI_MEM_SIZE] __attribute__((aligned(8)));
+
+    /* Configuration bytes. */
+    uint8_t configuration[22];
+
+    /* vmstate for each particular nic */
+    VMStateDescription *vmstate;
+
+    /* Quasi static device properties (no need to save them). */
+    uint16_t stats_size;
+    bool has_extended_tcb_support;
+} EEPRO100State;
+
+/* Word indices in EEPROM. */
+typedef enum {
+    EEPROM_CNFG_MDIX  = 0x03,
+    EEPROM_ID         = 0x05,
+    EEPROM_PHY_ID     = 0x06,
+    EEPROM_VENDOR_ID  = 0x0c,
+    EEPROM_CONFIG_ASF = 0x0d,
+    EEPROM_DEVICE_ID  = 0x23,
+    EEPROM_SMBUS_ADDR = 0x90,
+} EEPROMOffset;
+
+/* Bit values for EEPROM ID word. */
+typedef enum {
+    EEPROM_ID_MDM = BIT(0),     /* Modem */
+    EEPROM_ID_STB = BIT(1),     /* Standby Enable */
+    EEPROM_ID_WMR = BIT(2),     /* ??? */
+    EEPROM_ID_WOL = BIT(5),     /* Wake on LAN */
+    EEPROM_ID_DPD = BIT(6),     /* Deep Power Down */
+    EEPROM_ID_ALT = BIT(7),     /* */
+    /* BITS(10, 8) device revision */
+    EEPROM_ID_BD = BIT(11),     /* boot disable */
+    EEPROM_ID_ID = BIT(13),     /* id bit */
+    /* BITS(15, 14) signature */
+    EEPROM_ID_VALID = BIT(14),  /* signature for valid eeprom */
+} eeprom_id_bit;
+
+/* Default values for MDI (PHY) registers */
+static const uint16_t eepro100_mdi_default[] = {
+    /* MDI Registers 0 - 6, 7 */
+    0x3000, 0x780d, 0x02a8, 0x0154, 0x05e1, 0x0000, 0x0000, 0x0000,
+    /* MDI Registers 8 - 15 */
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* MDI Registers 16 - 31 */
+    0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+/* Readonly mask for MDI (PHY) registers */
+static const uint16_t eepro100_mdi_mask[] = {
+    0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+    0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+#define POLYNOMIAL 0x04c11db6
+
+static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s);
+
+/* From FreeBSD (locally modified). */
+static unsigned e100_compute_mcast_idx(const uint8_t *ep)
+{
+    uint32_t crc;
+    int carry, i, j;
+    uint8_t b;
+
+    crc = 0xffffffff;
+    for (i = 0; i < 6; i++) {
+        b = *ep++;
+        for (j = 0; j < 8; j++) {
+            carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
+            crc <<= 1;
+            b >>= 1;
+            if (carry) {
+                crc = ((crc ^ POLYNOMIAL) | carry);
+            }
+        }
+    }
+    return (crc & BITS(7, 2)) >> 2;
+}
+
+/* Read a 16 bit control/status (CSR) register. */
+static uint16_t e100_read_reg2(EEPRO100State *s, E100RegisterOffset addr)
+{
+    assert(!((uintptr_t)&s->mem[addr] & 1));
+    return le16_to_cpup((uint16_t *)&s->mem[addr]);
+}
+
+/* Read a 32 bit control/status (CSR) register. */
+static uint32_t e100_read_reg4(EEPRO100State *s, E100RegisterOffset addr)
+{
+    assert(!((uintptr_t)&s->mem[addr] & 3));
+    return le32_to_cpup((uint32_t *)&s->mem[addr]);
+}
+
+/* Write a 16 bit control/status (CSR) register. */
+static void e100_write_reg2(EEPRO100State *s, E100RegisterOffset addr,
+                            uint16_t val)
+{
+    assert(!((uintptr_t)&s->mem[addr] & 1));
+    cpu_to_le16w((uint16_t *)&s->mem[addr], val);
+}
+
+/* Read a 32 bit control/status (CSR) register. */
+static void e100_write_reg4(EEPRO100State *s, E100RegisterOffset addr,
+                            uint32_t val)
+{
+    assert(!((uintptr_t)&s->mem[addr] & 3));
+    cpu_to_le32w((uint32_t *)&s->mem[addr], val);
+}
+
+#if defined(DEBUG_EEPRO100)
+static const char *nic_dump(const uint8_t * buf, unsigned size)
+{
+    static char dump[3 * 16 + 1];
+    char *p = &dump[0];
+    if (size > 16) {
+        size = 16;
+    }
+    while (size-- > 0) {
+        p += sprintf(p, " %02x", *buf++);
+    }
+    return dump;
+}
+#endif                          /* DEBUG_EEPRO100 */
+
+enum scb_stat_ack {
+    stat_ack_not_ours = 0x00,
+    stat_ack_sw_gen = 0x04,
+    stat_ack_rnr = 0x10,
+    stat_ack_cu_idle = 0x20,
+    stat_ack_frame_rx = 0x40,
+    stat_ack_cu_cmd_done = 0x80,
+    stat_ack_not_present = 0xFF,
+    stat_ack_rx = (stat_ack_sw_gen | stat_ack_rnr | stat_ack_frame_rx),
+    stat_ack_tx = (stat_ack_cu_idle | stat_ack_cu_cmd_done),
+};
+
+static void disable_interrupt(EEPRO100State * s)
+{
+    if (s->int_stat) {
+        TRACE(INT, logout("interrupt disabled\n"));
+        qemu_irq_lower(s->dev.irq[0]);
+        s->int_stat = 0;
+    }
+}
+
+static void enable_interrupt(EEPRO100State * s)
+{
+    if (!s->int_stat) {
+        TRACE(INT, logout("interrupt enabled\n"));
+        qemu_irq_raise(s->dev.irq[0]);
+        s->int_stat = 1;
+    }
+}
+
+static void eepro100_acknowledge(EEPRO100State * s)
+{
+    s->scb_stat &= ~s->mem[SCBAck];
+    s->mem[SCBAck] = s->scb_stat;
+    if (s->scb_stat == 0) {
+        disable_interrupt(s);
+    }
+}
+
+static void eepro100_interrupt(EEPRO100State * s, uint8_t status)
+{
+    uint8_t mask = ~s->mem[SCBIntmask];
+    s->mem[SCBAck] |= status;
+    status = s->scb_stat = s->mem[SCBAck];
+    status &= (mask | 0x0f);
+#if 0
+    status &= (~s->mem[SCBIntmask] | 0x0xf);
+#endif
+    if (status && (mask & 0x01)) {
+        /* SCB mask and SCB Bit M do not disable interrupt. */
+        enable_interrupt(s);
+    } else if (s->int_stat) {
+        disable_interrupt(s);
+    }
+}
+
+static void eepro100_cx_interrupt(EEPRO100State * s)
+{
+    /* CU completed action command. */
+    /* Transmit not ok (82557 only, not in emulation). */
+    eepro100_interrupt(s, 0x80);
+}
+
+static void eepro100_cna_interrupt(EEPRO100State * s)
+{
+    /* CU left the active state. */
+    eepro100_interrupt(s, 0x20);
+}
+
+static void eepro100_fr_interrupt(EEPRO100State * s)
+{
+    /* RU received a complete frame. */
+    eepro100_interrupt(s, 0x40);
+}
+
+static void eepro100_rnr_interrupt(EEPRO100State * s)
+{
+    /* RU is not ready. */
+    eepro100_interrupt(s, 0x10);
+}
+
+static void eepro100_mdi_interrupt(EEPRO100State * s)
+{
+    /* MDI completed read or write cycle. */
+    eepro100_interrupt(s, 0x08);
+}
+
+static void eepro100_swi_interrupt(EEPRO100State * s)
+{
+    /* Software has requested an interrupt. */
+    eepro100_interrupt(s, 0x04);
+}
+
+#if 0
+static void eepro100_fcp_interrupt(EEPRO100State * s)
+{
+    /* Flow control pause interrupt (82558 and later). */
+    eepro100_interrupt(s, 0x01);
+}
+#endif
+
+static void e100_pci_reset(EEPRO100State * s)
+{
+    E100PCIDeviceInfo *info = eepro100_get_class(s);
+    uint32_t device = s->device;
+    uint8_t *pci_conf = s->dev.config;
+
+    TRACE(OTHER, logout("%p\n", s));
+
+    /* PCI Status */
+    pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM |
+                                        PCI_STATUS_FAST_BACK);
+    /* PCI Latency Timer */
+    pci_set_byte(pci_conf + PCI_LATENCY_TIMER, 0x20);   /* latency timer = 32 clocks */
+    /* Capability Pointer is set by PCI framework. */
+    /* Interrupt Line */
+    /* Interrupt Pin */
+    pci_set_byte(pci_conf + PCI_INTERRUPT_PIN, 1);      /* interrupt pin A */
+    /* Minimum Grant */
+    pci_set_byte(pci_conf + PCI_MIN_GNT, 0x08);
+    /* Maximum Latency */
+    pci_set_byte(pci_conf + PCI_MAX_LAT, 0x18);
+
+    s->stats_size = info->stats_size;
+    s->has_extended_tcb_support = info->has_extended_tcb_support;
+
+    switch (device) {
+    case i82550:
+    case i82551:
+    case i82557A:
+    case i82557B:
+    case i82557C:
+    case i82558A:
+    case i82558B:
+    case i82559A:
+    case i82559B:
+    case i82559ER:
+    case i82562:
+    case i82801:
+    case i82559C:
+        break;
+    default:
+        logout("Device %X is undefined!\n", device);
+    }
+
+    /* Standard TxCB. */
+    s->configuration[6] |= BIT(4);
+
+    /* Standard statistical counters. */
+    s->configuration[6] |= BIT(5);
+
+    if (s->stats_size == 80) {
+        /* TODO: check TCO Statistical Counters bit. Documentation not clear. */
+        if (s->configuration[6] & BIT(2)) {
+            /* TCO statistical counters. */
+            assert(s->configuration[6] & BIT(5));
+        } else {
+            if (s->configuration[6] & BIT(5)) {
+                /* No extended statistical counters, i82557 compatible. */
+                s->stats_size = 64;
+            } else {
+                /* i82558 compatible. */
+                s->stats_size = 76;
+            }
+        }
+    } else {
+        if (s->configuration[6] & BIT(5)) {
+            /* No extended statistical counters. */
+            s->stats_size = 64;
+        }
+    }
+    assert(s->stats_size > 0 && s->stats_size <= sizeof(s->statistics));
+
+    if (info->power_management) {
+        /* Power Management Capabilities */
+        int cfg_offset = 0xdc;
+        int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM,
+                                   cfg_offset, PCI_PM_SIZEOF);
+        assert(r >= 0);
+        pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21);
+#if 0 /* TODO: replace dummy code for power management emulation. */
+        /* TODO: Power Management Control / Status. */
+        pci_set_word(pci_conf + cfg_offset + PCI_PM_CTRL, 0x0000);
+        /* TODO: Ethernet Power Consumption Registers (i82559 and later). */
+        pci_set_byte(pci_conf + cfg_offset + PCI_PM_PPB_EXTENSIONS, 0x0000);
+#endif
+    }
+
+#if EEPROM_SIZE > 0
+    if (device == i82557C || device == i82558B || device == i82559C) {
+        /*
+        TODO: get vendor id from EEPROM for i82557C or later.
+        TODO: get device id from EEPROM for i82557C or later.
+        TODO: status bit 4 can be disabled by EEPROM for i82558, i82559.
+        TODO: header type is determined by EEPROM for i82559.
+        TODO: get subsystem id from EEPROM for i82557C or later.
+        TODO: get subsystem vendor id from EEPROM for i82557C or later.
+        TODO: exp. rom baddr depends on a bit in EEPROM for i82558 or later.
+        TODO: capability pointer depends on EEPROM for i82558.
+        */
+        logout("Get device id and revision from EEPROM!!!\n");
+    }
+#endif /* EEPROM_SIZE > 0 */
+}
+
+static void nic_selective_reset(EEPRO100State * s)
+{
+    size_t i;
+    uint16_t *eeprom_contents = eeprom93xx_data(s->eeprom);
+#if 0
+    eeprom93xx_reset(s->eeprom);
+#endif
+    memcpy(eeprom_contents, s->conf.macaddr.a, 6);
+    eeprom_contents[EEPROM_ID] = EEPROM_ID_VALID;
+    if (s->device == i82557B || s->device == i82557C)
+        eeprom_contents[5] = 0x0100;
+    eeprom_contents[EEPROM_PHY_ID] = 1;
+    uint16_t sum = 0;
+    for (i = 0; i < EEPROM_SIZE - 1; i++) {
+        sum += eeprom_contents[i];
+    }
+    eeprom_contents[EEPROM_SIZE - 1] = 0xbaba - sum;
+    TRACE(EEPROM, logout("checksum=0x%04x\n", eeprom_contents[EEPROM_SIZE - 1]));
+
+    memset(s->mem, 0, sizeof(s->mem));
+    e100_write_reg4(s, SCBCtrlMDI, BIT(21));
+
+    assert(sizeof(s->mdimem) == sizeof(eepro100_mdi_default));
+    memcpy(&s->mdimem[0], &eepro100_mdi_default[0], sizeof(s->mdimem));
+}
+
+static void nic_reset(void *opaque)
+{
+    EEPRO100State *s = opaque;
+    TRACE(OTHER, logout("%p\n", s));
+    /* TODO: Clearing of hash register for selective reset, too? */
+    memset(&s->mult[0], 0, sizeof(s->mult));
+    nic_selective_reset(s);
+}
+
+#if defined(DEBUG_EEPRO100)
+static const char * const e100_reg[PCI_IO_SIZE / 4] = {
+    "Command/Status",
+    "General Pointer",
+    "Port",
+    "EEPROM/Flash Control",
+    "MDI Control",
+    "Receive DMA Byte Count",
+    "Flow Control",
+    "General Status/Control"
+};
+
+static char *regname(uint32_t addr)
+{
+    static char buf[32];
+    if (addr < PCI_IO_SIZE) {
+        const char *r = e100_reg[addr / 4];
+        if (r != 0) {
+            snprintf(buf, sizeof(buf), "%s+%u", r, addr % 4);
+        } else {
+            snprintf(buf, sizeof(buf), "0x%02x", addr);
+        }
+    } else {
+        snprintf(buf, sizeof(buf), "??? 0x%08x", addr);
+    }
+    return buf;
+}
+#endif                          /* DEBUG_EEPRO100 */
+
+/*****************************************************************************
+ *
+ * Command emulation.
+ *
+ ****************************************************************************/
+
+#if 0
+static uint16_t eepro100_read_command(EEPRO100State * s)
+{
+    uint16_t val = 0xffff;
+    TRACE(OTHER, logout("val=0x%04x\n", val));
+    return val;
+}
+#endif
+
+/* Commands that can be put in a command list entry. */
+enum commands {
+    CmdNOp = 0,
+    CmdIASetup = 1,
+    CmdConfigure = 2,
+    CmdMulticastList = 3,
+    CmdTx = 4,
+    CmdTDR = 5,                 /* load microcode */
+    CmdDump = 6,
+    CmdDiagnose = 7,
+
+    /* And some extra flags: */
+    CmdSuspend = 0x4000,        /* Suspend after completion. */
+    CmdIntr = 0x2000,           /* Interrupt after completion. */
+    CmdTxFlex = 0x0008,         /* Use "Flexible mode" for CmdTx command. */
+};
+
+static cu_state_t get_cu_state(EEPRO100State * s)
+{
+    return ((s->mem[SCBStatus] & BITS(7, 6)) >> 6);
+}
+
+static void set_cu_state(EEPRO100State * s, cu_state_t state)
+{
+    s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(7, 6)) + (state << 6);
+}
+
+static ru_state_t get_ru_state(EEPRO100State * s)
+{
+    return ((s->mem[SCBStatus] & BITS(5, 2)) >> 2);
+}
+
+static void set_ru_state(EEPRO100State * s, ru_state_t state)
+{
+    s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(5, 2)) + (state << 2);
+}
+
+static void dump_statistics(EEPRO100State * s)
+{
+    /* Dump statistical data. Most data is never changed by the emulation
+     * and always 0, so we first just copy the whole block and then those
+     * values which really matter.
+     * Number of data should check configuration!!!
+     */
+    pci_dma_write(&s->dev, s->statsaddr, &s->statistics, s->stats_size);
+    stl_le_pci_dma(&s->dev, s->statsaddr + 0,
+                   s->statistics.tx_good_frames);
+    stl_le_pci_dma(&s->dev, s->statsaddr + 36,
+                   s->statistics.rx_good_frames);
+    stl_le_pci_dma(&s->dev, s->statsaddr + 48,
+                   s->statistics.rx_resource_errors);
+    stl_le_pci_dma(&s->dev, s->statsaddr + 60,
+                   s->statistics.rx_short_frame_errors);
+#if 0
+    stw_le_pci_dma(&s->dev, s->statsaddr + 76, s->statistics.xmt_tco_frames);
+    stw_le_pci_dma(&s->dev, s->statsaddr + 78, s->statistics.rcv_tco_frames);
+    missing("CU dump statistical counters");
+#endif
+}
+
+static void read_cb(EEPRO100State *s)
+{
+    pci_dma_read(&s->dev, s->cb_address, &s->tx, sizeof(s->tx));
+    s->tx.status = le16_to_cpu(s->tx.status);
+    s->tx.command = le16_to_cpu(s->tx.command);
+    s->tx.link = le32_to_cpu(s->tx.link);
+    s->tx.tbd_array_addr = le32_to_cpu(s->tx.tbd_array_addr);
+    s->tx.tcb_bytes = le16_to_cpu(s->tx.tcb_bytes);
+}
+
+static void tx_command(EEPRO100State *s)
+{
+    uint32_t tbd_array = le32_to_cpu(s->tx.tbd_array_addr);
+    uint16_t tcb_bytes = (le16_to_cpu(s->tx.tcb_bytes) & 0x3fff);
+    /* Sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes. */
+    uint8_t buf[2600];
+    uint16_t size = 0;
+    uint32_t tbd_address = s->cb_address + 0x10;
+    TRACE(RXTX, logout
+        ("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n",
+         tbd_array, tcb_bytes, s->tx.tbd_count));
+
+    if (tcb_bytes > 2600) {
+        logout("TCB byte count too large, using 2600\n");
+        tcb_bytes = 2600;
+    }
+    if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) {
+        logout
+            ("illegal values of TBD array address and TCB byte count!\n");
+    }
+    assert(tcb_bytes <= sizeof(buf));
+    while (size < tcb_bytes) {
+        uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address);
+        uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4);
+#if 0
+        uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6);
+#endif
+        tbd_address += 8;
+        TRACE(RXTX, logout
+            ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n",
+             tx_buffer_address, tx_buffer_size));
+        tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
+        pci_dma_read(&s->dev, tx_buffer_address, &buf[size], tx_buffer_size);
+        size += tx_buffer_size;
+    }
+    if (tbd_array == 0xffffffff) {
+        /* Simplified mode. Was already handled by code above. */
+    } else {
+        /* Flexible mode. */
+        uint8_t tbd_count = 0;
+        if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) {
+            /* Extended Flexible TCB. */
+            for (; tbd_count < 2; tbd_count++) {
+                uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev,
+                                                            tbd_address);
+                uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev,
+                                                          tbd_address + 4);
+                uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev,
+                                                        tbd_address + 6);
+                tbd_address += 8;
+                TRACE(RXTX, logout
+                    ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n",
+                     tx_buffer_address, tx_buffer_size));
+                tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
+                pci_dma_read(&s->dev, tx_buffer_address,
+                             &buf[size], tx_buffer_size);
+                size += tx_buffer_size;
+                if (tx_buffer_el & 1) {
+                    break;
+                }
+            }
+        }
+        tbd_address = tbd_array;
+        for (; tbd_count < s->tx.tbd_count; tbd_count++) {
+            uint32_t tx_buffer_address = ldl_le_pci_dma(&s->dev, tbd_address);
+            uint16_t tx_buffer_size = lduw_le_pci_dma(&s->dev, tbd_address + 4);
+            uint16_t tx_buffer_el = lduw_le_pci_dma(&s->dev, tbd_address + 6);
+            tbd_address += 8;
+            TRACE(RXTX, logout
+                ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n",
+                 tx_buffer_address, tx_buffer_size));
+            tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size);
+            pci_dma_read(&s->dev, tx_buffer_address,
+                         &buf[size], tx_buffer_size);
+            size += tx_buffer_size;
+            if (tx_buffer_el & 1) {
+                break;
+            }
+        }
+    }
+    TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size)));
+    qemu_send_packet(qemu_get_queue(s->nic), buf, size);
+    s->statistics.tx_good_frames++;
+    /* Transmit with bad status would raise an CX/TNO interrupt.
+     * (82557 only). Emulation never has bad status. */
+#if 0
+    eepro100_cx_interrupt(s);
+#endif
+}
+
+static void set_multicast_list(EEPRO100State *s)
+{
+    uint16_t multicast_count = s->tx.tbd_array_addr & BITS(13, 0);
+    uint16_t i;
+    memset(&s->mult[0], 0, sizeof(s->mult));
+    TRACE(OTHER, logout("multicast list, multicast count = %u\n", multicast_count));
+    for (i = 0; i < multicast_count; i += 6) {
+        uint8_t multicast_addr[6];
+        pci_dma_read(&s->dev, s->cb_address + 10 + i, multicast_addr, 6);
+        TRACE(OTHER, logout("multicast entry %s\n", nic_dump(multicast_addr, 6)));
+        unsigned mcast_idx = e100_compute_mcast_idx(multicast_addr);
+        assert(mcast_idx < 64);
+        s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7));
+    }
+}
+
+static void action_command(EEPRO100State *s)
+{
+    for (;;) {
+        bool bit_el;
+        bool bit_s;
+        bool bit_i;
+        bool bit_nc;
+        uint16_t ok_status = STATUS_OK;
+        s->cb_address = s->cu_base + s->cu_offset;
+        read_cb(s);
+        bit_el = ((s->tx.command & COMMAND_EL) != 0);
+        bit_s = ((s->tx.command & COMMAND_S) != 0);
+        bit_i = ((s->tx.command & COMMAND_I) != 0);
+        bit_nc = ((s->tx.command & COMMAND_NC) != 0);
+#if 0
+        bool bit_sf = ((s->tx.command & COMMAND_SF) != 0);
+#endif
+        s->cu_offset = s->tx.link;
+        TRACE(OTHER,
+              logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n",
+                     s->tx.status, s->tx.command, s->tx.link));
+        switch (s->tx.command & COMMAND_CMD) {
+        case CmdNOp:
+            /* Do nothing. */
+            break;
+        case CmdIASetup:
+            pci_dma_read(&s->dev, s->cb_address + 8, &s->conf.macaddr.a[0], 6);
+            TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6)));
+            break;
+        case CmdConfigure:
+            pci_dma_read(&s->dev, s->cb_address + 8,
+                         &s->configuration[0], sizeof(s->configuration));
+            TRACE(OTHER, logout("configuration: %s\n",
+                                nic_dump(&s->configuration[0], 16)));
+            TRACE(OTHER, logout("configuration: %s\n",
+                                nic_dump(&s->configuration[16],
+                                ARRAY_SIZE(s->configuration) - 16)));
+            if (s->configuration[20] & BIT(6)) {
+                TRACE(OTHER, logout("Multiple IA bit\n"));
+            }
+            break;
+        case CmdMulticastList:
+            set_multicast_list(s);
+            break;
+        case CmdTx:
+            if (bit_nc) {
+                missing("CmdTx: NC = 0");
+                ok_status = 0;
+                break;
+            }
+            tx_command(s);
+            break;
+        case CmdTDR:
+            TRACE(OTHER, logout("load microcode\n"));
+            /* Starting with offset 8, the command contains
+             * 64 dwords microcode which we just ignore here. */
+            break;
+        case CmdDiagnose:
+            TRACE(OTHER, logout("diagnose\n"));
+            /* Make sure error flag is not set. */
+            s->tx.status = 0;
+            break;
+        default:
+            missing("undefined command");
+            ok_status = 0;
+            break;
+        }
+        /* Write new status. */
+        stw_le_pci_dma(&s->dev, s->cb_address,
+                       s->tx.status | ok_status | STATUS_C);
+        if (bit_i) {
+            /* CU completed action. */
+            eepro100_cx_interrupt(s);
+        }
+        if (bit_el) {
+            /* CU becomes idle. Terminate command loop. */
+            set_cu_state(s, cu_idle);
+            eepro100_cna_interrupt(s);
+            break;
+        } else if (bit_s) {
+            /* CU becomes suspended. Terminate command loop. */
+            set_cu_state(s, cu_suspended);
+            eepro100_cna_interrupt(s);
+            break;
+        } else {
+            /* More entries in list. */
+            TRACE(OTHER, logout("CU list with at least one more entry\n"));
+        }
+    }
+    TRACE(OTHER, logout("CU list empty\n"));
+    /* List is empty. Now CU is idle or suspended. */
+}
+
+static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
+{
+    cu_state_t cu_state;
+    switch (val) {
+    case CU_NOP:
+        /* No operation. */
+        break;
+    case CU_START:
+        cu_state = get_cu_state(s);
+        if (cu_state != cu_idle && cu_state != cu_suspended) {
+            /* Intel documentation says that CU must be idle or suspended
+             * for the CU start command. */
+            logout("unexpected CU state is %u\n", cu_state);
+        }
+        set_cu_state(s, cu_active);
+        s->cu_offset = e100_read_reg4(s, SCBPointer);
+        action_command(s);
+        break;
+    case CU_RESUME:
+        if (get_cu_state(s) != cu_suspended) {
+            logout("bad CU resume from CU state %u\n", get_cu_state(s));
+            /* Workaround for bad Linux eepro100 driver which resumes
+             * from idle state. */
+#if 0
+            missing("cu resume");
+#endif
+            set_cu_state(s, cu_suspended);
+        }
+        if (get_cu_state(s) == cu_suspended) {
+            TRACE(OTHER, logout("CU resuming\n"));
+            set_cu_state(s, cu_active);
+            action_command(s);
+        }
+        break;
+    case CU_STATSADDR:
+        /* Load dump counters address. */
+        s->statsaddr = e100_read_reg4(s, SCBPointer);
+        TRACE(OTHER, logout("val=0x%02x (dump counters address)\n", val));
+        if (s->statsaddr & 3) {
+            /* Memory must be Dword aligned. */
+            logout("unaligned dump counters address\n");
+            /* Handling of misaligned addresses is undefined.
+             * Here we align the address by ignoring the lower bits. */
+            /* TODO: Test unaligned dump counter address on real hardware. */
+            s->statsaddr &= ~3;
+        }
+        break;
+    case CU_SHOWSTATS:
+        /* Dump statistical counters. */
+        TRACE(OTHER, logout("val=0x%02x (dump stats)\n", val));
+        dump_statistics(s);
+        stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa005);
+        break;
+    case CU_CMD_BASE:
+        /* Load CU base. */
+        TRACE(OTHER, logout("val=0x%02x (CU base address)\n", val));
+        s->cu_base = e100_read_reg4(s, SCBPointer);
+        break;
+    case CU_DUMPSTATS:
+        /* Dump and reset statistical counters. */
+        TRACE(OTHER, logout("val=0x%02x (dump stats and reset)\n", val));
+        dump_statistics(s);
+        stl_le_pci_dma(&s->dev, s->statsaddr + s->stats_size, 0xa007);
+        memset(&s->statistics, 0, sizeof(s->statistics));
+        break;
+    case CU_SRESUME:
+        /* CU static resume. */
+        missing("CU static resume");
+        break;
+    default:
+        missing("Undefined CU command");
+    }
+}
+
+static void eepro100_ru_command(EEPRO100State * s, uint8_t val)
+{
+    switch (val) {
+    case RU_NOP:
+        /* No operation. */
+        break;
+    case RX_START:
+        /* RU start. */
+        if (get_ru_state(s) != ru_idle) {
+            logout("RU state is %u, should be %u\n", get_ru_state(s), ru_idle);
+#if 0
+            assert(!"wrong RU state");
+#endif
+        }
+        set_ru_state(s, ru_ready);
+        s->ru_offset = e100_read_reg4(s, SCBPointer);
+        qemu_flush_queued_packets(qemu_get_queue(s->nic));
+        TRACE(OTHER, logout("val=0x%02x (rx start)\n", val));
+        break;
+    case RX_RESUME:
+        /* Restart RU. */
+        if (get_ru_state(s) != ru_suspended) {
+            logout("RU state is %u, should be %u\n", get_ru_state(s),
+                   ru_suspended);
+#if 0
+            assert(!"wrong RU state");
+#endif
+        }
+        set_ru_state(s, ru_ready);
+        break;
+    case RU_ABORT:
+        /* RU abort. */
+        if (get_ru_state(s) == ru_ready) {
+            eepro100_rnr_interrupt(s);
+        }
+        set_ru_state(s, ru_idle);
+        break;
+    case RX_ADDR_LOAD:
+        /* Load RU base. */
+        TRACE(OTHER, logout("val=0x%02x (RU base address)\n", val));
+        s->ru_base = e100_read_reg4(s, SCBPointer);
+        break;
+    default:
+        logout("val=0x%02x (undefined RU command)\n", val);
+        missing("Undefined SU command");
+    }
+}
+
+static void eepro100_write_command(EEPRO100State * s, uint8_t val)
+{
+    eepro100_ru_command(s, val & 0x0f);
+    eepro100_cu_command(s, val & 0xf0);
+    if ((val) == 0) {
+        TRACE(OTHER, logout("val=0x%02x\n", val));
+    }
+    /* Clear command byte after command was accepted. */
+    s->mem[SCBCmd] = 0;
+}
+
+/*****************************************************************************
+ *
+ * EEPROM emulation.
+ *
+ ****************************************************************************/
+
+#define EEPROM_CS       0x02
+#define EEPROM_SK       0x01
+#define EEPROM_DI       0x04
+#define EEPROM_DO       0x08
+
+static uint16_t eepro100_read_eeprom(EEPRO100State * s)
+{
+    uint16_t val = e100_read_reg2(s, SCBeeprom);
+    if (eeprom93xx_read(s->eeprom)) {
+        val |= EEPROM_DO;
+    } else {
+        val &= ~EEPROM_DO;
+    }
+    TRACE(EEPROM, logout("val=0x%04x\n", val));
+    return val;
+}
+
+static void eepro100_write_eeprom(eeprom_t * eeprom, uint8_t val)
+{
+    TRACE(EEPROM, logout("val=0x%02x\n", val));
+
+    /* mask unwritable bits */
+#if 0
+    val = SET_MASKED(val, 0x31, eeprom->value);
+#endif
+
+    int eecs = ((val & EEPROM_CS) != 0);
+    int eesk = ((val & EEPROM_SK) != 0);
+    int eedi = ((val & EEPROM_DI) != 0);
+    eeprom93xx_write(eeprom, eecs, eesk, eedi);
+}
+
+/*****************************************************************************
+ *
+ * MDI emulation.
+ *
+ ****************************************************************************/
+
+#if defined(DEBUG_EEPRO100)
+static const char * const mdi_op_name[] = {
+    "opcode 0",
+    "write",
+    "read",
+    "opcode 3"
+};
+
+static const char * const mdi_reg_name[] = {
+    "Control",
+    "Status",
+    "PHY Identification (Word 1)",
+    "PHY Identification (Word 2)",
+    "Auto-Negotiation Advertisement",
+    "Auto-Negotiation Link Partner Ability",
+    "Auto-Negotiation Expansion"
+};
+
+static const char *reg2name(uint8_t reg)
+{
+    static char buffer[10];
+    const char *p = buffer;
+    if (reg < ARRAY_SIZE(mdi_reg_name)) {
+        p = mdi_reg_name[reg];
+    } else {
+        snprintf(buffer, sizeof(buffer), "reg=0x%02x", reg);
+    }
+    return p;
+}
+#endif                          /* DEBUG_EEPRO100 */
+
+static uint32_t eepro100_read_mdi(EEPRO100State * s)
+{
+    uint32_t val = e100_read_reg4(s, SCBCtrlMDI);
+
+#ifdef DEBUG_EEPRO100
+    uint8_t raiseint = (val & BIT(29)) >> 29;
+    uint8_t opcode = (val & BITS(27, 26)) >> 26;
+    uint8_t phy = (val & BITS(25, 21)) >> 21;
+    uint8_t reg = (val & BITS(20, 16)) >> 16;
+    uint16_t data = (val & BITS(15, 0));
+#endif
+    /* Emulation takes no time to finish MDI transaction. */
+    val |= BIT(28);
+    TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
+                      val, raiseint, mdi_op_name[opcode], phy,
+                      reg2name(reg), data));
+    return val;
+}
+
+static void eepro100_write_mdi(EEPRO100State *s)
+{
+    uint32_t val = e100_read_reg4(s, SCBCtrlMDI);
+    uint8_t raiseint = (val & BIT(29)) >> 29;
+    uint8_t opcode = (val & BITS(27, 26)) >> 26;
+    uint8_t phy = (val & BITS(25, 21)) >> 21;
+    uint8_t reg = (val & BITS(20, 16)) >> 16;
+    uint16_t data = (val & BITS(15, 0));
+    TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
+          val, raiseint, mdi_op_name[opcode], phy, reg2name(reg), data));
+    if (phy != 1) {
+        /* Unsupported PHY address. */
+#if 0
+        logout("phy must be 1 but is %u\n", phy);
+#endif
+        data = 0;
+    } else if (opcode != 1 && opcode != 2) {
+        /* Unsupported opcode. */
+        logout("opcode must be 1 or 2 but is %u\n", opcode);
+        data = 0;
+    } else if (reg > 6) {
+        /* Unsupported register. */
+        logout("register must be 0...6 but is %u\n", reg);
+        data = 0;
+    } else {
+        TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
+                          val, raiseint, mdi_op_name[opcode], phy,
+                          reg2name(reg), data));
+        if (opcode == 1) {
+            /* MDI write */
+            switch (reg) {
+            case 0:            /* Control Register */
+                if (data & 0x8000) {
+                    /* Reset status and control registers to default. */
+                    s->mdimem[0] = eepro100_mdi_default[0];
+                    s->mdimem[1] = eepro100_mdi_default[1];
+                    data = s->mdimem[reg];
+                } else {
+                    /* Restart Auto Configuration = Normal Operation */
+                    data &= ~0x0200;
+                }
+                break;
+            case 1:            /* Status Register */
+                missing("not writable");
+                data = s->mdimem[reg];
+                break;
+            case 2:            /* PHY Identification Register (Word 1) */
+            case 3:            /* PHY Identification Register (Word 2) */
+                missing("not implemented");
+                break;
+            case 4:            /* Auto-Negotiation Advertisement Register */
+            case 5:            /* Auto-Negotiation Link Partner Ability Register */
+                break;
+            case 6:            /* Auto-Negotiation Expansion Register */
+            default:
+                missing("not implemented");
+            }
+            s->mdimem[reg] = data;
+        } else if (opcode == 2) {
+            /* MDI read */
+            switch (reg) {
+            case 0:            /* Control Register */
+                if (data & 0x8000) {
+                    /* Reset status and control registers to default. */
+                    s->mdimem[0] = eepro100_mdi_default[0];
+                    s->mdimem[1] = eepro100_mdi_default[1];
+                }
+                break;
+            case 1:            /* Status Register */
+                s->mdimem[reg] |= 0x0020;
+                break;
+            case 2:            /* PHY Identification Register (Word 1) */
+            case 3:            /* PHY Identification Register (Word 2) */
+            case 4:            /* Auto-Negotiation Advertisement Register */
+                break;
+            case 5:            /* Auto-Negotiation Link Partner Ability Register */
+                s->mdimem[reg] = 0x41fe;
+                break;
+            case 6:            /* Auto-Negotiation Expansion Register */
+                s->mdimem[reg] = 0x0001;
+                break;
+            }
+            data = s->mdimem[reg];
+        }
+        /* Emulation takes no time to finish MDI transaction.
+         * Set MDI bit in SCB status register. */
+        s->mem[SCBAck] |= 0x08;
+        val |= BIT(28);
+        if (raiseint) {
+            eepro100_mdi_interrupt(s);
+        }
+    }
+    val = (val & 0xffff0000) + data;
+    e100_write_reg4(s, SCBCtrlMDI, val);
+}
+
+/*****************************************************************************
+ *
+ * Port emulation.
+ *
+ ****************************************************************************/
+
+#define PORT_SOFTWARE_RESET     0
+#define PORT_SELFTEST           1
+#define PORT_SELECTIVE_RESET    2
+#define PORT_DUMP               3
+#define PORT_SELECTION_MASK     3
+
+typedef struct {
+    uint32_t st_sign;           /* Self Test Signature */
+    uint32_t st_result;         /* Self Test Results */
+} eepro100_selftest_t;
+
+static uint32_t eepro100_read_port(EEPRO100State * s)
+{
+    return 0;
+}
+
+static void eepro100_write_port(EEPRO100State *s)
+{
+    uint32_t val = e100_read_reg4(s, SCBPort);
+    uint32_t address = (val & ~PORT_SELECTION_MASK);
+    uint8_t selection = (val & PORT_SELECTION_MASK);
+    switch (selection) {
+    case PORT_SOFTWARE_RESET:
+        nic_reset(s);
+        break;
+    case PORT_SELFTEST:
+        TRACE(OTHER, logout("selftest address=0x%08x\n", address));
+        eepro100_selftest_t data;
+        pci_dma_read(&s->dev, address, (uint8_t *) &data, sizeof(data));
+        data.st_sign = 0xffffffff;
+        data.st_result = 0;
+        pci_dma_write(&s->dev, address, (uint8_t *) &data, sizeof(data));
+        break;
+    case PORT_SELECTIVE_RESET:
+        TRACE(OTHER, logout("selective reset, selftest address=0x%08x\n", address));
+        nic_selective_reset(s);
+        break;
+    default:
+        logout("val=0x%08x\n", val);
+        missing("unknown port selection");
+    }
+}
+
+/*****************************************************************************
+ *
+ * General hardware emulation.
+ *
+ ****************************************************************************/
+
+static uint8_t eepro100_read1(EEPRO100State * s, uint32_t addr)
+{
+    uint8_t val = 0;
+    if (addr <= sizeof(s->mem) - sizeof(val)) {
+        val = s->mem[addr];
+    }
+
+    switch (addr) {
+    case SCBStatus:
+    case SCBAck:
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+        break;
+    case SCBCmd:
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+#if 0
+        val = eepro100_read_command(s);
+#endif
+        break;
+    case SCBIntmask:
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+        break;
+    case SCBPort + 3:
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+        break;
+    case SCBeeprom:
+        val = eepro100_read_eeprom(s);
+        break;
+    case SCBCtrlMDI:
+    case SCBCtrlMDI + 1:
+    case SCBCtrlMDI + 2:
+    case SCBCtrlMDI + 3:
+        val = (uint8_t)(eepro100_read_mdi(s) >> (8 * (addr & 3)));
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+        break;
+    case SCBpmdr:       /* Power Management Driver Register */
+        val = 0;
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+        break;
+    case SCBgctrl:      /* General Control Register */
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+        break;
+    case SCBgstat:      /* General Status Register */
+        /* 100 Mbps full duplex, valid link */
+        val = 0x07;
+        TRACE(OTHER, logout("addr=General Status val=%02x\n", val));
+        break;
+    default:
+        logout("addr=%s val=0x%02x\n", regname(addr), val);
+        missing("unknown byte read");
+    }
+    return val;
+}
+
+static uint16_t eepro100_read2(EEPRO100State * s, uint32_t addr)
+{
+    uint16_t val = 0;
+    if (addr <= sizeof(s->mem) - sizeof(val)) {
+        val = e100_read_reg2(s, addr);
+    }
+
+    switch (addr) {
+    case SCBStatus:
+    case SCBCmd:
+        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
+        break;
+    case SCBeeprom:
+        val = eepro100_read_eeprom(s);
+        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
+        break;
+    case SCBCtrlMDI:
+    case SCBCtrlMDI + 2:
+        val = (uint16_t)(eepro100_read_mdi(s) >> (8 * (addr & 3)));
+        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
+        break;
+    default:
+        logout("addr=%s val=0x%04x\n", regname(addr), val);
+        missing("unknown word read");
+    }
+    return val;
+}
+
+static uint32_t eepro100_read4(EEPRO100State * s, uint32_t addr)
+{
+    uint32_t val = 0;
+    if (addr <= sizeof(s->mem) - sizeof(val)) {
+        val = e100_read_reg4(s, addr);
+    }
+
+    switch (addr) {
+    case SCBStatus:
+        TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
+        break;
+    case SCBPointer:
+        TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
+        break;
+    case SCBPort:
+        val = eepro100_read_port(s);
+        TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
+        break;
+    case SCBflash:
+        val = eepro100_read_eeprom(s);
+        TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
+        break;
+    case SCBCtrlMDI:
+        val = eepro100_read_mdi(s);
+        break;
+    default:
+        logout("addr=%s val=0x%08x\n", regname(addr), val);
+        missing("unknown longword read");
+    }
+    return val;
+}
+
+static void eepro100_write1(EEPRO100State * s, uint32_t addr, uint8_t val)
+{
+    /* SCBStatus is readonly. */
+    if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
+        s->mem[addr] = val;
+    }
+
+    switch (addr) {
+    case SCBStatus:
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+        break;
+    case SCBAck:
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+        eepro100_acknowledge(s);
+        break;
+    case SCBCmd:
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+        eepro100_write_command(s, val);
+        break;
+    case SCBIntmask:
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+        if (val & BIT(1)) {
+            eepro100_swi_interrupt(s);
+        }
+        eepro100_interrupt(s, 0);
+        break;
+    case SCBPointer:
+    case SCBPointer + 1:
+    case SCBPointer + 2:
+    case SCBPointer + 3:
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+        break;
+    case SCBPort:
+    case SCBPort + 1:
+    case SCBPort + 2:
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+        break;
+    case SCBPort + 3:
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+        eepro100_write_port(s);
+        break;
+    case SCBFlow:       /* does not exist on 82557 */
+    case SCBFlow + 1:
+    case SCBFlow + 2:
+    case SCBpmdr:       /* does not exist on 82557 */
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+        break;
+    case SCBeeprom:
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+        eepro100_write_eeprom(s->eeprom, val);
+        break;
+    case SCBCtrlMDI:
+    case SCBCtrlMDI + 1:
+    case SCBCtrlMDI + 2:
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+        break;
+    case SCBCtrlMDI + 3:
+        TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
+        eepro100_write_mdi(s);
+        break;
+    default:
+        logout("addr=%s val=0x%02x\n", regname(addr), val);
+        missing("unknown byte write");
+    }
+}
+
+static void eepro100_write2(EEPRO100State * s, uint32_t addr, uint16_t val)
+{
+    /* SCBStatus is readonly. */
+    if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) {
+        e100_write_reg2(s, addr, val);
+    }
+
+    switch (addr) {
+    case SCBStatus:
+        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
+        s->mem[SCBAck] = (val >> 8);
+        eepro100_acknowledge(s);
+        break;
+    case SCBCmd:
+        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
+        eepro100_write_command(s, val);
+        eepro100_write1(s, SCBIntmask, val >> 8);
+        break;
+    case SCBPointer:
+    case SCBPointer + 2:
+        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
+        break;
+    case SCBPort:
+        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
+        break;
+    case SCBPort + 2:
+        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
+        eepro100_write_port(s);
+        break;
+    case SCBeeprom:
+        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
+        eepro100_write_eeprom(s->eeprom, val);
+        break;
+    case SCBCtrlMDI:
+        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
+        break;
+    case SCBCtrlMDI + 2:
+        TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
+        eepro100_write_mdi(s);
+        break;
+    default:
+        logout("addr=%s val=0x%04x\n", regname(addr), val);
+        missing("unknown word write");
+    }
+}
+
+static void eepro100_write4(EEPRO100State * s, uint32_t addr, uint32_t val)
+{
+    if (addr <= sizeof(s->mem) - sizeof(val)) {
+        e100_write_reg4(s, addr, val);
+    }
+
+    switch (addr) {
+    case SCBPointer:
+        TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
+        break;
+    case SCBPort:
+        TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
+        eepro100_write_port(s);
+        break;
+    case SCBflash:
+        TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
+        val = val >> 16;
+        eepro100_write_eeprom(s->eeprom, val);
+        break;
+    case SCBCtrlMDI:
+        TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
+        eepro100_write_mdi(s);
+        break;
+    default:
+        logout("addr=%s val=0x%08x\n", regname(addr), val);
+        missing("unknown longword write");
+    }
+}
+
+static uint64_t eepro100_read(void *opaque, hwaddr addr,
+                              unsigned size)
+{
+    EEPRO100State *s = opaque;
+
+    switch (size) {
+    case 1: return eepro100_read1(s, addr);
+    case 2: return eepro100_read2(s, addr);
+    case 4: return eepro100_read4(s, addr);
+    default: abort();
+    }
+}
+
+static void eepro100_write(void *opaque, hwaddr addr,
+                           uint64_t data, unsigned size)
+{
+    EEPRO100State *s = opaque;
+
+    switch (size) {
+    case 1:
+        eepro100_write1(s, addr, data);
+        break;
+    case 2:
+        eepro100_write2(s, addr, data);
+        break;
+    case 4:
+        eepro100_write4(s, addr, data);
+        break;
+    default:
+        abort();
+    }
+}
+
+static const MemoryRegionOps eepro100_ops = {
+    .read = eepro100_read,
+    .write = eepro100_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static int nic_can_receive(NetClientState *nc)
+{
+    EEPRO100State *s = qemu_get_nic_opaque(nc);
+    TRACE(RXTX, logout("%p\n", s));
+    return get_ru_state(s) == ru_ready;
+#if 0
+    return !eepro100_buffer_full(s);
+#endif
+}
+
+static ssize_t nic_receive(NetClientState *nc, const uint8_t * buf, size_t size)
+{
+    /* TODO:
+     * - Magic packets should set bit 30 in power management driver register.
+     * - Interesting packets should set bit 29 in power management driver register.
+     */
+    EEPRO100State *s = qemu_get_nic_opaque(nc);
+    uint16_t rfd_status = 0xa000;
+#if defined(CONFIG_PAD_RECEIVED_FRAMES)
+    uint8_t min_buf[60];
+#endif
+    static const uint8_t broadcast_macaddr[6] =
+        { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+#if defined(CONFIG_PAD_RECEIVED_FRAMES)
+    /* Pad to minimum Ethernet frame length */
+    if (size < sizeof(min_buf)) {
+        memcpy(min_buf, buf, size);
+        memset(&min_buf[size], 0, sizeof(min_buf) - size);
+        buf = min_buf;
+        size = sizeof(min_buf);
+    }
+#endif
+
+    if (s->configuration[8] & 0x80) {
+        /* CSMA is disabled. */
+        logout("%p received while CSMA is disabled\n", s);
+        return -1;
+#if !defined(CONFIG_PAD_RECEIVED_FRAMES)
+    } else if (size < 64 && (s->configuration[7] & BIT(0))) {
+        /* Short frame and configuration byte 7/0 (discard short receive) set:
+         * Short frame is discarded */
+        logout("%p received short frame (%zu byte)\n", s, size);
+        s->statistics.rx_short_frame_errors++;
+        return -1;
+#endif
+    } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & BIT(3))) {
+        /* Long frame and configuration byte 18/3 (long receive ok) not set:
+         * Long frames are discarded. */
+        logout("%p received long frame (%zu byte), ignored\n", s, size);
+        return -1;
+    } else if (memcmp(buf, s->conf.macaddr.a, 6) == 0) {       /* !!! */
+        /* Frame matches individual address. */
+        /* TODO: check configuration byte 15/4 (ignore U/L). */
+        TRACE(RXTX, logout("%p received frame for me, len=%zu\n", s, size));
+    } else if (memcmp(buf, broadcast_macaddr, 6) == 0) {
+        /* Broadcast frame. */
+        TRACE(RXTX, logout("%p received broadcast, len=%zu\n", s, size));
+        rfd_status |= 0x0002;
+    } else if (buf[0] & 0x01) {
+        /* Multicast frame. */
+        TRACE(RXTX, logout("%p received multicast, len=%zu,%s\n", s, size, nic_dump(buf, size)));
+        if (s->configuration[21] & BIT(3)) {
+          /* Multicast all bit is set, receive all multicast frames. */
+        } else {
+          unsigned mcast_idx = e100_compute_mcast_idx(buf);
+          assert(mcast_idx < 64);
+          if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
+            /* Multicast frame is allowed in hash table. */
+          } else if (s->configuration[15] & BIT(0)) {
+              /* Promiscuous: receive all. */
+              rfd_status |= 0x0004;
+          } else {
+              TRACE(RXTX, logout("%p multicast ignored\n", s));
+              return -1;
+          }
+        }
+        /* TODO: Next not for promiscuous mode? */
+        rfd_status |= 0x0002;
+    } else if (s->configuration[15] & BIT(0)) {
+        /* Promiscuous: receive all. */
+        TRACE(RXTX, logout("%p received frame in promiscuous mode, len=%zu\n", s, size));
+        rfd_status |= 0x0004;
+    } else if (s->configuration[20] & BIT(6)) {
+        /* Multiple IA bit set. */
+        unsigned mcast_idx = compute_mcast_idx(buf);
+        assert(mcast_idx < 64);
+        if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) {
+            TRACE(RXTX, logout("%p accepted, multiple IA bit set\n", s));
+        } else {
+            TRACE(RXTX, logout("%p frame ignored, multiple IA bit set\n", s));
+            return -1;
+        }
+    } else {
+        TRACE(RXTX, logout("%p received frame, ignored, len=%zu,%s\n", s, size,
+              nic_dump(buf, size)));
+        return size;
+    }
+
+    if (get_ru_state(s) != ru_ready) {
+        /* No resources available. */
+        logout("no resources, state=%u\n", get_ru_state(s));
+        /* TODO: RNR interrupt only at first failed frame? */
+        eepro100_rnr_interrupt(s);
+        s->statistics.rx_resource_errors++;
+#if 0
+        assert(!"no resources");
+#endif
+        return -1;
+    }
+    /* !!! */
+    eepro100_rx_t rx;
+    pci_dma_read(&s->dev, s->ru_base + s->ru_offset,
+                 &rx, sizeof(eepro100_rx_t));
+    uint16_t rfd_command = le16_to_cpu(rx.command);
+    uint16_t rfd_size = le16_to_cpu(rx.size);
+
+    if (size > rfd_size) {
+        logout("Receive buffer (%" PRId16 " bytes) too small for data "
+            "(%zu bytes); data truncated\n", rfd_size, size);
+        size = rfd_size;
+    }
+#if !defined(CONFIG_PAD_RECEIVED_FRAMES)
+    if (size < 64) {
+        rfd_status |= 0x0080;
+    }
+#endif
+    TRACE(OTHER, logout("command 0x%04x, link 0x%08x, addr 0x%08x, size %u\n",
+          rfd_command, rx.link, rx.rx_buf_addr, rfd_size));
+    stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset +
+                offsetof(eepro100_rx_t, status), rfd_status);
+    stw_le_pci_dma(&s->dev, s->ru_base + s->ru_offset +
+                offsetof(eepro100_rx_t, count), size);
+    /* Early receive interrupt not supported. */
+#if 0
+    eepro100_er_interrupt(s);
+#endif
+    /* Receive CRC Transfer not supported. */
+    if (s->configuration[18] & BIT(2)) {
+        missing("Receive CRC Transfer");
+        return -1;
+    }
+    /* TODO: check stripping enable bit. */
+#if 0
+    assert(!(s->configuration[17] & BIT(0)));
+#endif
+    pci_dma_write(&s->dev, s->ru_base + s->ru_offset +
+                  sizeof(eepro100_rx_t), buf, size);
+    s->statistics.rx_good_frames++;
+    eepro100_fr_interrupt(s);
+    s->ru_offset = le32_to_cpu(rx.link);
+    if (rfd_command & COMMAND_EL) {
+        /* EL bit is set, so this was the last frame. */
+        logout("receive: Running out of frames\n");
+        set_ru_state(s, ru_no_resources);
+        eepro100_rnr_interrupt(s);
+    }
+    if (rfd_command & COMMAND_S) {
+        /* S bit is set. */
+        set_ru_state(s, ru_suspended);
+    }
+    return size;
+}
+
+static const VMStateDescription vmstate_eepro100 = {
+    .version_id = 3,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields      = (VMStateField []) {
+        VMSTATE_PCI_DEVICE(dev, EEPRO100State),
+        VMSTATE_UNUSED(32),
+        VMSTATE_BUFFER(mult, EEPRO100State),
+        VMSTATE_BUFFER(mem, EEPRO100State),
+        /* Save all members of struct between scb_stat and mem. */
+        VMSTATE_UINT8(scb_stat, EEPRO100State),
+        VMSTATE_UINT8(int_stat, EEPRO100State),
+        VMSTATE_UNUSED(3*4),
+        VMSTATE_MACADDR(conf.macaddr, EEPRO100State),
+        VMSTATE_UNUSED(19*4),
+        VMSTATE_UINT16_ARRAY(mdimem, EEPRO100State, 32),
+        /* The eeprom should be saved and restored by its own routines. */
+        VMSTATE_UINT32(device, EEPRO100State),
+        /* TODO check device. */
+        VMSTATE_UINT32(cu_base, EEPRO100State),
+        VMSTATE_UINT32(cu_offset, EEPRO100State),
+        VMSTATE_UINT32(ru_base, EEPRO100State),
+        VMSTATE_UINT32(ru_offset, EEPRO100State),
+        VMSTATE_UINT32(statsaddr, EEPRO100State),
+        /* Save eepro100_stats_t statistics. */
+        VMSTATE_UINT32(statistics.tx_good_frames, EEPRO100State),
+        VMSTATE_UINT32(statistics.tx_max_collisions, EEPRO100State),
+        VMSTATE_UINT32(statistics.tx_late_collisions, EEPRO100State),
+        VMSTATE_UINT32(statistics.tx_underruns, EEPRO100State),
+        VMSTATE_UINT32(statistics.tx_lost_crs, EEPRO100State),
+        VMSTATE_UINT32(statistics.tx_deferred, EEPRO100State),
+        VMSTATE_UINT32(statistics.tx_single_collisions, EEPRO100State),
+        VMSTATE_UINT32(statistics.tx_multiple_collisions, EEPRO100State),
+        VMSTATE_UINT32(statistics.tx_total_collisions, EEPRO100State),
+        VMSTATE_UINT32(statistics.rx_good_frames, EEPRO100State),
+        VMSTATE_UINT32(statistics.rx_crc_errors, EEPRO100State),
+        VMSTATE_UINT32(statistics.rx_alignment_errors, EEPRO100State),
+        VMSTATE_UINT32(statistics.rx_resource_errors, EEPRO100State),
+        VMSTATE_UINT32(statistics.rx_overrun_errors, EEPRO100State),
+        VMSTATE_UINT32(statistics.rx_cdt_errors, EEPRO100State),
+        VMSTATE_UINT32(statistics.rx_short_frame_errors, EEPRO100State),
+        VMSTATE_UINT32(statistics.fc_xmt_pause, EEPRO100State),
+        VMSTATE_UINT32(statistics.fc_rcv_pause, EEPRO100State),
+        VMSTATE_UINT32(statistics.fc_rcv_unsupported, EEPRO100State),
+        VMSTATE_UINT16(statistics.xmt_tco_frames, EEPRO100State),
+        VMSTATE_UINT16(statistics.rcv_tco_frames, EEPRO100State),
+        /* Configuration bytes. */
+        VMSTATE_BUFFER(configuration, EEPRO100State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void nic_cleanup(NetClientState *nc)
+{
+    EEPRO100State *s = qemu_get_nic_opaque(nc);
+
+    s->nic = NULL;
+}
+
+static void pci_nic_uninit(PCIDevice *pci_dev)
+{
+    EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
+
+    memory_region_destroy(&s->mmio_bar);
+    memory_region_destroy(&s->io_bar);
+    memory_region_destroy(&s->flash_bar);
+    vmstate_unregister(&pci_dev->qdev, s->vmstate, s);
+    eeprom93xx_free(&pci_dev->qdev, s->eeprom);
+    qemu_del_nic(s->nic);
+}
+
+static NetClientInfo net_eepro100_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = nic_can_receive,
+    .receive = nic_receive,
+    .cleanup = nic_cleanup,
+};
+
+static int e100_nic_init(PCIDevice *pci_dev)
+{
+    EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev);
+    E100PCIDeviceInfo *info = eepro100_get_class(s);
+
+    TRACE(OTHER, logout("\n"));
+
+    s->device = info->device;
+
+    e100_pci_reset(s);
+
+    /* Add 64 * 2 EEPROM. i82557 and i82558 support a 64 word EEPROM,
+     * i82559 and later support 64 or 256 word EEPROM. */
+    s->eeprom = eeprom93xx_new(&pci_dev->qdev, EEPROM_SIZE);
+
+    /* Handler for memory-mapped I/O */
+    memory_region_init_io(&s->mmio_bar, &eepro100_ops, s, "eepro100-mmio",
+                          PCI_MEM_SIZE);
+    pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->mmio_bar);
+    memory_region_init_io(&s->io_bar, &eepro100_ops, s, "eepro100-io",
+                          PCI_IO_SIZE);
+    pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
+    /* FIXME: flash aliases to mmio?! */
+    memory_region_init_io(&s->flash_bar, &eepro100_ops, s, "eepro100-flash",
+                          PCI_FLASH_SIZE);
+    pci_register_bar(&s->dev, 2, 0, &s->flash_bar);
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+    logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6));
+
+    nic_reset(s);
+
+    s->nic = qemu_new_nic(&net_eepro100_info, &s->conf,
+                          object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s);
+
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+    TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str));
+
+    qemu_register_reset(nic_reset, s);
+
+    s->vmstate = g_malloc(sizeof(vmstate_eepro100));
+    memcpy(s->vmstate, &vmstate_eepro100, sizeof(vmstate_eepro100));
+    s->vmstate->name = qemu_get_queue(s->nic)->model;
+    vmstate_register(&pci_dev->qdev, -1, s->vmstate, s);
+
+    add_boot_device_path(s->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0");
+
+    return 0;
+}
+
+static E100PCIDeviceInfo e100_devices[] = {
+    {
+        .name = "i82550",
+        .desc = "Intel i82550 Ethernet",
+        .device = i82550,
+        /* TODO: check device id. */
+        .device_id = PCI_DEVICE_ID_INTEL_82551IT,
+        /* Revision ID: 0x0c, 0x0d, 0x0e. */
+        .revision = 0x0e,
+        /* TODO: check size of statistical counters. */
+        .stats_size = 80,
+        /* TODO: check extended tcb support. */
+        .has_extended_tcb_support = true,
+        .power_management = true,
+    },{
+        .name = "i82551",
+        .desc = "Intel i82551 Ethernet",
+        .device = i82551,
+        .device_id = PCI_DEVICE_ID_INTEL_82551IT,
+        /* Revision ID: 0x0f, 0x10. */
+        .revision = 0x0f,
+        /* TODO: check size of statistical counters. */
+        .stats_size = 80,
+        .has_extended_tcb_support = true,
+        .power_management = true,
+    },{
+        .name = "i82557a",
+        .desc = "Intel i82557A Ethernet",
+        .device = i82557A,
+        .device_id = PCI_DEVICE_ID_INTEL_82557,
+        .revision = 0x01,
+        .power_management = false,
+    },{
+        .name = "i82557b",
+        .desc = "Intel i82557B Ethernet",
+        .device = i82557B,
+        .device_id = PCI_DEVICE_ID_INTEL_82557,
+        .revision = 0x02,
+        .power_management = false,
+    },{
+        .name = "i82557c",
+        .desc = "Intel i82557C Ethernet",
+        .device = i82557C,
+        .device_id = PCI_DEVICE_ID_INTEL_82557,
+        .revision = 0x03,
+        .power_management = false,
+    },{
+        .name = "i82558a",
+        .desc = "Intel i82558A Ethernet",
+        .device = i82558A,
+        .device_id = PCI_DEVICE_ID_INTEL_82557,
+        .revision = 0x04,
+        .stats_size = 76,
+        .has_extended_tcb_support = true,
+        .power_management = true,
+    },{
+        .name = "i82558b",
+        .desc = "Intel i82558B Ethernet",
+        .device = i82558B,
+        .device_id = PCI_DEVICE_ID_INTEL_82557,
+        .revision = 0x05,
+        .stats_size = 76,
+        .has_extended_tcb_support = true,
+        .power_management = true,
+    },{
+        .name = "i82559a",
+        .desc = "Intel i82559A Ethernet",
+        .device = i82559A,
+        .device_id = PCI_DEVICE_ID_INTEL_82557,
+        .revision = 0x06,
+        .stats_size = 80,
+        .has_extended_tcb_support = true,
+        .power_management = true,
+    },{
+        .name = "i82559b",
+        .desc = "Intel i82559B Ethernet",
+        .device = i82559B,
+        .device_id = PCI_DEVICE_ID_INTEL_82557,
+        .revision = 0x07,
+        .stats_size = 80,
+        .has_extended_tcb_support = true,
+        .power_management = true,
+    },{
+        .name = "i82559c",
+        .desc = "Intel i82559C Ethernet",
+        .device = i82559C,
+        .device_id = PCI_DEVICE_ID_INTEL_82557,
+#if 0
+        .revision = 0x08,
+#endif
+        /* TODO: Windows wants revision id 0x0c. */
+        .revision = 0x0c,
+#if EEPROM_SIZE > 0
+        .subsystem_vendor_id = PCI_VENDOR_ID_INTEL,
+        .subsystem_id = 0x0040,
+#endif
+        .stats_size = 80,
+        .has_extended_tcb_support = true,
+        .power_management = true,
+    },{
+        .name = "i82559er",
+        .desc = "Intel i82559ER Ethernet",
+        .device = i82559ER,
+        .device_id = PCI_DEVICE_ID_INTEL_82551IT,
+        .revision = 0x09,
+        .stats_size = 80,
+        .has_extended_tcb_support = true,
+        .power_management = true,
+    },{
+        .name = "i82562",
+        .desc = "Intel i82562 Ethernet",
+        .device = i82562,
+        /* TODO: check device id. */
+        .device_id = PCI_DEVICE_ID_INTEL_82551IT,
+        /* TODO: wrong revision id. */
+        .revision = 0x0e,
+        .stats_size = 80,
+        .has_extended_tcb_support = true,
+        .power_management = true,
+    },{
+        /* Toshiba Tecra 8200. */
+        .name = "i82801",
+        .desc = "Intel i82801 Ethernet",
+        .device = i82801,
+        .device_id = 0x2449,
+        .revision = 0x03,
+        .stats_size = 80,
+        .has_extended_tcb_support = true,
+        .power_management = true,
+    }
+};
+
+static E100PCIDeviceInfo *eepro100_get_class_by_name(const char *typename)
+{
+    E100PCIDeviceInfo *info = NULL;
+    int i;
+
+    /* This is admittedly awkward but also temporary.  QOM allows for
+     * parameterized typing and for subclassing both of which would suitable
+     * handle what's going on here.  But class_data is already being used as
+     * a stop-gap hack to allow incremental qdev conversion so we cannot use it
+     * right now.  Once we merge the final QOM series, we can come back here and
+     * do this in a much more elegant fashion.
+     */
+    for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
+        if (strcmp(e100_devices[i].name, typename) == 0) {
+            info = &e100_devices[i];
+            break;
+        }
+    }
+    assert(info != NULL);
+
+    return info;
+}
+
+static E100PCIDeviceInfo *eepro100_get_class(EEPRO100State *s)
+{
+    return eepro100_get_class_by_name(object_get_typename(OBJECT(s)));
+}
+
+static Property e100_properties[] = {
+    DEFINE_NIC_PROPERTIES(EEPRO100State, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void eepro100_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+    E100PCIDeviceInfo *info;
+
+    info = eepro100_get_class_by_name(object_class_get_name(klass));
+
+    dc->props = e100_properties;
+    dc->desc = info->desc;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->class_id = PCI_CLASS_NETWORK_ETHERNET;
+    k->romfile = "pxe-eepro100.rom";
+    k->init = e100_nic_init;
+    k->exit = pci_nic_uninit;
+    k->device_id = info->device_id;
+    k->revision = info->revision;
+    k->subsystem_vendor_id = info->subsystem_vendor_id;
+    k->subsystem_id = info->subsystem_id;
+}
+
+static void eepro100_register_types(void)
+{
+    size_t i;
+    for (i = 0; i < ARRAY_SIZE(e100_devices); i++) {
+        TypeInfo type_info = {};
+        E100PCIDeviceInfo *info = &e100_devices[i];
+
+        type_info.name = info->name;
+        type_info.parent = TYPE_PCI_DEVICE;
+        type_info.class_init = eepro100_class_init;
+        type_info.instance_size = sizeof(EEPRO100State);
+        
+        type_register(&type_info);
+    }
+}
+
+type_init(eepro100_register_types)
diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c
new file mode 100644 (file)
index 0000000..04cf267
--- /dev/null
@@ -0,0 +1,1399 @@
+/*
+ * SMSC LAN9118 Ethernet interface emulation
+ *
+ * Copyright (c) 2009 CodeSourcery, LLC.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GNU GPL v2
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "hw/sysbus.h"
+#include "net/net.h"
+#include "hw/arm/devices.h"
+#include "sysemu/sysemu.h"
+#include "hw/ptimer.h"
+/* For crc32 */
+#include <zlib.h>
+
+//#define DEBUG_LAN9118
+
+#ifdef DEBUG_LAN9118
+#define DPRINTF(fmt, ...) \
+do { printf("lan9118: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { hw_error("lan9118: error: " fmt , ## __VA_ARGS__);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+#define CSR_ID_REV      0x50
+#define CSR_IRQ_CFG     0x54
+#define CSR_INT_STS     0x58
+#define CSR_INT_EN      0x5c
+#define CSR_BYTE_TEST   0x64
+#define CSR_FIFO_INT    0x68
+#define CSR_RX_CFG      0x6c
+#define CSR_TX_CFG      0x70
+#define CSR_HW_CFG      0x74
+#define CSR_RX_DP_CTRL  0x78
+#define CSR_RX_FIFO_INF 0x7c
+#define CSR_TX_FIFO_INF 0x80
+#define CSR_PMT_CTRL    0x84
+#define CSR_GPIO_CFG    0x88
+#define CSR_GPT_CFG     0x8c
+#define CSR_GPT_CNT     0x90
+#define CSR_WORD_SWAP   0x98
+#define CSR_FREE_RUN    0x9c
+#define CSR_RX_DROP     0xa0
+#define CSR_MAC_CSR_CMD 0xa4
+#define CSR_MAC_CSR_DATA 0xa8
+#define CSR_AFC_CFG     0xac
+#define CSR_E2P_CMD     0xb0
+#define CSR_E2P_DATA    0xb4
+
+/* IRQ_CFG */
+#define IRQ_INT         0x00001000
+#define IRQ_EN          0x00000100
+#define IRQ_POL         0x00000010
+#define IRQ_TYPE        0x00000001
+
+/* INT_STS/INT_EN */
+#define SW_INT          0x80000000
+#define TXSTOP_INT      0x02000000
+#define RXSTOP_INT      0x01000000
+#define RXDFH_INT       0x00800000
+#define TX_IOC_INT      0x00200000
+#define RXD_INT         0x00100000
+#define GPT_INT         0x00080000
+#define PHY_INT         0x00040000
+#define PME_INT         0x00020000
+#define TXSO_INT        0x00010000
+#define RWT_INT         0x00008000
+#define RXE_INT         0x00004000
+#define TXE_INT         0x00002000
+#define TDFU_INT        0x00000800
+#define TDFO_INT        0x00000400
+#define TDFA_INT        0x00000200
+#define TSFF_INT        0x00000100
+#define TSFL_INT        0x00000080
+#define RXDF_INT        0x00000040
+#define RDFL_INT        0x00000020
+#define RSFF_INT        0x00000010
+#define RSFL_INT        0x00000008
+#define GPIO2_INT       0x00000004
+#define GPIO1_INT       0x00000002
+#define GPIO0_INT       0x00000001
+#define RESERVED_INT    0x7c001000
+
+#define MAC_CR          1
+#define MAC_ADDRH       2
+#define MAC_ADDRL       3
+#define MAC_HASHH       4
+#define MAC_HASHL       5
+#define MAC_MII_ACC     6
+#define MAC_MII_DATA    7
+#define MAC_FLOW        8
+#define MAC_VLAN1       9 /* TODO */
+#define MAC_VLAN2       10 /* TODO */
+#define MAC_WUFF        11 /* TODO */
+#define MAC_WUCSR       12 /* TODO */
+
+#define MAC_CR_RXALL    0x80000000
+#define MAC_CR_RCVOWN   0x00800000
+#define MAC_CR_LOOPBK   0x00200000
+#define MAC_CR_FDPX     0x00100000
+#define MAC_CR_MCPAS    0x00080000
+#define MAC_CR_PRMS     0x00040000
+#define MAC_CR_INVFILT  0x00020000
+#define MAC_CR_PASSBAD  0x00010000
+#define MAC_CR_HO       0x00008000
+#define MAC_CR_HPFILT   0x00002000
+#define MAC_CR_LCOLL    0x00001000
+#define MAC_CR_BCAST    0x00000800
+#define MAC_CR_DISRTY   0x00000400
+#define MAC_CR_PADSTR   0x00000100
+#define MAC_CR_BOLMT    0x000000c0
+#define MAC_CR_DFCHK    0x00000020
+#define MAC_CR_TXEN     0x00000008
+#define MAC_CR_RXEN     0x00000004
+#define MAC_CR_RESERVED 0x7f404213
+
+#define PHY_INT_ENERGYON            0x80
+#define PHY_INT_AUTONEG_COMPLETE    0x40
+#define PHY_INT_FAULT               0x20
+#define PHY_INT_DOWN                0x10
+#define PHY_INT_AUTONEG_LP          0x08
+#define PHY_INT_PARFAULT            0x04
+#define PHY_INT_AUTONEG_PAGE        0x02
+
+#define GPT_TIMER_EN    0x20000000
+
+enum tx_state {
+    TX_IDLE,
+    TX_B,
+    TX_DATA
+};
+
+typedef struct {
+    /* state is a tx_state but we can't put enums in VMStateDescriptions. */
+    uint32_t state;
+    uint32_t cmd_a;
+    uint32_t cmd_b;
+    int32_t buffer_size;
+    int32_t offset;
+    int32_t pad;
+    int32_t fifo_used;
+    int32_t len;
+    uint8_t data[2048];
+} LAN9118Packet;
+
+static const VMStateDescription vmstate_lan9118_packet = {
+    .name = "lan9118_packet",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(state, LAN9118Packet),
+        VMSTATE_UINT32(cmd_a, LAN9118Packet),
+        VMSTATE_UINT32(cmd_b, LAN9118Packet),
+        VMSTATE_INT32(buffer_size, LAN9118Packet),
+        VMSTATE_INT32(offset, LAN9118Packet),
+        VMSTATE_INT32(pad, LAN9118Packet),
+        VMSTATE_INT32(fifo_used, LAN9118Packet),
+        VMSTATE_INT32(len, LAN9118Packet),
+        VMSTATE_UINT8_ARRAY(data, LAN9118Packet, 2048),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+typedef struct {
+    SysBusDevice busdev;
+    NICState *nic;
+    NICConf conf;
+    qemu_irq irq;
+    MemoryRegion mmio;
+    ptimer_state *timer;
+
+    uint32_t irq_cfg;
+    uint32_t int_sts;
+    uint32_t int_en;
+    uint32_t fifo_int;
+    uint32_t rx_cfg;
+    uint32_t tx_cfg;
+    uint32_t hw_cfg;
+    uint32_t pmt_ctrl;
+    uint32_t gpio_cfg;
+    uint32_t gpt_cfg;
+    uint32_t word_swap;
+    uint32_t free_timer_start;
+    uint32_t mac_cmd;
+    uint32_t mac_data;
+    uint32_t afc_cfg;
+    uint32_t e2p_cmd;
+    uint32_t e2p_data;
+
+    uint32_t mac_cr;
+    uint32_t mac_hashh;
+    uint32_t mac_hashl;
+    uint32_t mac_mii_acc;
+    uint32_t mac_mii_data;
+    uint32_t mac_flow;
+
+    uint32_t phy_status;
+    uint32_t phy_control;
+    uint32_t phy_advertise;
+    uint32_t phy_int;
+    uint32_t phy_int_mask;
+
+    int32_t eeprom_writable;
+    uint8_t eeprom[128];
+
+    int32_t tx_fifo_size;
+    LAN9118Packet *txp;
+    LAN9118Packet tx_packet;
+
+    int32_t tx_status_fifo_used;
+    int32_t tx_status_fifo_head;
+    uint32_t tx_status_fifo[512];
+
+    int32_t rx_status_fifo_size;
+    int32_t rx_status_fifo_used;
+    int32_t rx_status_fifo_head;
+    uint32_t rx_status_fifo[896];
+    int32_t rx_fifo_size;
+    int32_t rx_fifo_used;
+    int32_t rx_fifo_head;
+    uint32_t rx_fifo[3360];
+    int32_t rx_packet_size_head;
+    int32_t rx_packet_size_tail;
+    int32_t rx_packet_size[1024];
+
+    int32_t rxp_offset;
+    int32_t rxp_size;
+    int32_t rxp_pad;
+
+    uint32_t write_word_prev_offset;
+    uint32_t write_word_n;
+    uint16_t write_word_l;
+    uint16_t write_word_h;
+    uint32_t read_word_prev_offset;
+    uint32_t read_word_n;
+    uint32_t read_long;
+
+    uint32_t mode_16bit;
+} lan9118_state;
+
+static const VMStateDescription vmstate_lan9118 = {
+    .name = "lan9118",
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_PTIMER(timer, lan9118_state),
+        VMSTATE_UINT32(irq_cfg, lan9118_state),
+        VMSTATE_UINT32(int_sts, lan9118_state),
+        VMSTATE_UINT32(int_en, lan9118_state),
+        VMSTATE_UINT32(fifo_int, lan9118_state),
+        VMSTATE_UINT32(rx_cfg, lan9118_state),
+        VMSTATE_UINT32(tx_cfg, lan9118_state),
+        VMSTATE_UINT32(hw_cfg, lan9118_state),
+        VMSTATE_UINT32(pmt_ctrl, lan9118_state),
+        VMSTATE_UINT32(gpio_cfg, lan9118_state),
+        VMSTATE_UINT32(gpt_cfg, lan9118_state),
+        VMSTATE_UINT32(word_swap, lan9118_state),
+        VMSTATE_UINT32(free_timer_start, lan9118_state),
+        VMSTATE_UINT32(mac_cmd, lan9118_state),
+        VMSTATE_UINT32(mac_data, lan9118_state),
+        VMSTATE_UINT32(afc_cfg, lan9118_state),
+        VMSTATE_UINT32(e2p_cmd, lan9118_state),
+        VMSTATE_UINT32(e2p_data, lan9118_state),
+        VMSTATE_UINT32(mac_cr, lan9118_state),
+        VMSTATE_UINT32(mac_hashh, lan9118_state),
+        VMSTATE_UINT32(mac_hashl, lan9118_state),
+        VMSTATE_UINT32(mac_mii_acc, lan9118_state),
+        VMSTATE_UINT32(mac_mii_data, lan9118_state),
+        VMSTATE_UINT32(mac_flow, lan9118_state),
+        VMSTATE_UINT32(phy_status, lan9118_state),
+        VMSTATE_UINT32(phy_control, lan9118_state),
+        VMSTATE_UINT32(phy_advertise, lan9118_state),
+        VMSTATE_UINT32(phy_int, lan9118_state),
+        VMSTATE_UINT32(phy_int_mask, lan9118_state),
+        VMSTATE_INT32(eeprom_writable, lan9118_state),
+        VMSTATE_UINT8_ARRAY(eeprom, lan9118_state, 128),
+        VMSTATE_INT32(tx_fifo_size, lan9118_state),
+        /* txp always points at tx_packet so need not be saved */
+        VMSTATE_STRUCT(tx_packet, lan9118_state, 0,
+                       vmstate_lan9118_packet, LAN9118Packet),
+        VMSTATE_INT32(tx_status_fifo_used, lan9118_state),
+        VMSTATE_INT32(tx_status_fifo_head, lan9118_state),
+        VMSTATE_UINT32_ARRAY(tx_status_fifo, lan9118_state, 512),
+        VMSTATE_INT32(rx_status_fifo_size, lan9118_state),
+        VMSTATE_INT32(rx_status_fifo_used, lan9118_state),
+        VMSTATE_INT32(rx_status_fifo_head, lan9118_state),
+        VMSTATE_UINT32_ARRAY(rx_status_fifo, lan9118_state, 896),
+        VMSTATE_INT32(rx_fifo_size, lan9118_state),
+        VMSTATE_INT32(rx_fifo_used, lan9118_state),
+        VMSTATE_INT32(rx_fifo_head, lan9118_state),
+        VMSTATE_UINT32_ARRAY(rx_fifo, lan9118_state, 3360),
+        VMSTATE_INT32(rx_packet_size_head, lan9118_state),
+        VMSTATE_INT32(rx_packet_size_tail, lan9118_state),
+        VMSTATE_INT32_ARRAY(rx_packet_size, lan9118_state, 1024),
+        VMSTATE_INT32(rxp_offset, lan9118_state),
+        VMSTATE_INT32(rxp_size, lan9118_state),
+        VMSTATE_INT32(rxp_pad, lan9118_state),
+        VMSTATE_UINT32_V(write_word_prev_offset, lan9118_state, 2),
+        VMSTATE_UINT32_V(write_word_n, lan9118_state, 2),
+        VMSTATE_UINT16_V(write_word_l, lan9118_state, 2),
+        VMSTATE_UINT16_V(write_word_h, lan9118_state, 2),
+        VMSTATE_UINT32_V(read_word_prev_offset, lan9118_state, 2),
+        VMSTATE_UINT32_V(read_word_n, lan9118_state, 2),
+        VMSTATE_UINT32_V(read_long, lan9118_state, 2),
+        VMSTATE_UINT32_V(mode_16bit, lan9118_state, 2),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void lan9118_update(lan9118_state *s)
+{
+    int level;
+
+    /* TODO: Implement FIFO level IRQs.  */
+    level = (s->int_sts & s->int_en) != 0;
+    if (level) {
+        s->irq_cfg |= IRQ_INT;
+    } else {
+        s->irq_cfg &= ~IRQ_INT;
+    }
+    if ((s->irq_cfg & IRQ_EN) == 0) {
+        level = 0;
+    }
+    if ((s->irq_cfg & (IRQ_TYPE | IRQ_POL)) != (IRQ_TYPE | IRQ_POL)) {
+        /* Interrupt is active low unless we're configured as
+         * active-high polarity, push-pull type.
+         */
+        level = !level;
+    }
+    qemu_set_irq(s->irq, level);
+}
+
+static void lan9118_mac_changed(lan9118_state *s)
+{
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+}
+
+static void lan9118_reload_eeprom(lan9118_state *s)
+{
+    int i;
+    if (s->eeprom[0] != 0xa5) {
+        s->e2p_cmd &= ~0x10;
+        DPRINTF("MACADDR load failed\n");
+        return;
+    }
+    for (i = 0; i < 6; i++) {
+        s->conf.macaddr.a[i] = s->eeprom[i + 1];
+    }
+    s->e2p_cmd |= 0x10;
+    DPRINTF("MACADDR loaded from eeprom\n");
+    lan9118_mac_changed(s);
+}
+
+static void phy_update_irq(lan9118_state *s)
+{
+    if (s->phy_int & s->phy_int_mask) {
+        s->int_sts |= PHY_INT;
+    } else {
+        s->int_sts &= ~PHY_INT;
+    }
+    lan9118_update(s);
+}
+
+static void phy_update_link(lan9118_state *s)
+{
+    /* Autonegotiation status mirrors link status.  */
+    if (qemu_get_queue(s->nic)->link_down) {
+        s->phy_status &= ~0x0024;
+        s->phy_int |= PHY_INT_DOWN;
+    } else {
+        s->phy_status |= 0x0024;
+        s->phy_int |= PHY_INT_ENERGYON;
+        s->phy_int |= PHY_INT_AUTONEG_COMPLETE;
+    }
+    phy_update_irq(s);
+}
+
+static void lan9118_set_link(NetClientState *nc)
+{
+    phy_update_link(qemu_get_nic_opaque(nc));
+}
+
+static void phy_reset(lan9118_state *s)
+{
+    s->phy_status = 0x7809;
+    s->phy_control = 0x3000;
+    s->phy_advertise = 0x01e1;
+    s->phy_int_mask = 0;
+    s->phy_int = 0;
+    phy_update_link(s);
+}
+
+static void lan9118_reset(DeviceState *d)
+{
+    lan9118_state *s = FROM_SYSBUS(lan9118_state, SYS_BUS_DEVICE(d));
+    s->irq_cfg &= (IRQ_TYPE | IRQ_POL);
+    s->int_sts = 0;
+    s->int_en = 0;
+    s->fifo_int = 0x48000000;
+    s->rx_cfg = 0;
+    s->tx_cfg = 0;
+    s->hw_cfg = s->mode_16bit ? 0x00050000 : 0x00050004;
+    s->pmt_ctrl &= 0x45;
+    s->gpio_cfg = 0;
+    s->txp->fifo_used = 0;
+    s->txp->state = TX_IDLE;
+    s->txp->cmd_a = 0xffffffffu;
+    s->txp->cmd_b = 0xffffffffu;
+    s->txp->len = 0;
+    s->txp->fifo_used = 0;
+    s->tx_fifo_size = 4608;
+    s->tx_status_fifo_used = 0;
+    s->rx_status_fifo_size = 704;
+    s->rx_fifo_size = 2640;
+    s->rx_fifo_used = 0;
+    s->rx_status_fifo_size = 176;
+    s->rx_status_fifo_used = 0;
+    s->rxp_offset = 0;
+    s->rxp_size = 0;
+    s->rxp_pad = 0;
+    s->rx_packet_size_tail = s->rx_packet_size_head;
+    s->rx_packet_size[s->rx_packet_size_head] = 0;
+    s->mac_cmd = 0;
+    s->mac_data = 0;
+    s->afc_cfg = 0;
+    s->e2p_cmd = 0;
+    s->e2p_data = 0;
+    s->free_timer_start = qemu_get_clock_ns(vm_clock) / 40;
+
+    ptimer_stop(s->timer);
+    ptimer_set_count(s->timer, 0xffff);
+    s->gpt_cfg = 0xffff;
+
+    s->mac_cr = MAC_CR_PRMS;
+    s->mac_hashh = 0;
+    s->mac_hashl = 0;
+    s->mac_mii_acc = 0;
+    s->mac_mii_data = 0;
+    s->mac_flow = 0;
+
+    s->read_word_n = 0;
+    s->write_word_n = 0;
+
+    phy_reset(s);
+
+    s->eeprom_writable = 0;
+    lan9118_reload_eeprom(s);
+}
+
+static int lan9118_can_receive(NetClientState *nc)
+{
+    return 1;
+}
+
+static void rx_fifo_push(lan9118_state *s, uint32_t val)
+{
+    int fifo_pos;
+    fifo_pos = s->rx_fifo_head + s->rx_fifo_used;
+    if (fifo_pos >= s->rx_fifo_size)
+      fifo_pos -= s->rx_fifo_size;
+    s->rx_fifo[fifo_pos] = val;
+    s->rx_fifo_used++;
+}
+
+/* Return nonzero if the packet is accepted by the filter.  */
+static int lan9118_filter(lan9118_state *s, const uint8_t *addr)
+{
+    int multicast;
+    uint32_t hash;
+
+    if (s->mac_cr & MAC_CR_PRMS) {
+        return 1;
+    }
+    if (addr[0] == 0xff && addr[1] == 0xff && addr[2] == 0xff &&
+        addr[3] == 0xff && addr[4] == 0xff && addr[5] == 0xff) {
+        return (s->mac_cr & MAC_CR_BCAST) == 0;
+    }
+
+    multicast = addr[0] & 1;
+    if (multicast &&s->mac_cr & MAC_CR_MCPAS) {
+        return 1;
+    }
+    if (multicast ? (s->mac_cr & MAC_CR_HPFILT) == 0
+                  : (s->mac_cr & MAC_CR_HO) == 0) {
+        /* Exact matching.  */
+        hash = memcmp(addr, s->conf.macaddr.a, 6);
+        if (s->mac_cr & MAC_CR_INVFILT) {
+            return hash != 0;
+        } else {
+            return hash == 0;
+        }
+    } else {
+        /* Hash matching  */
+        hash = compute_mcast_idx(addr);
+        if (hash & 0x20) {
+            return (s->mac_hashh >> (hash & 0x1f)) & 1;
+        } else {
+            return (s->mac_hashl >> (hash & 0x1f)) & 1;
+        }
+    }
+}
+
+static ssize_t lan9118_receive(NetClientState *nc, const uint8_t *buf,
+                               size_t size)
+{
+    lan9118_state *s = qemu_get_nic_opaque(nc);
+    int fifo_len;
+    int offset;
+    int src_pos;
+    int n;
+    int filter;
+    uint32_t val;
+    uint32_t crc;
+    uint32_t status;
+
+    if ((s->mac_cr & MAC_CR_RXEN) == 0) {
+        return -1;
+    }
+
+    if (size >= 2048 || size < 14) {
+        return -1;
+    }
+
+    /* TODO: Implement FIFO overflow notification.  */
+    if (s->rx_status_fifo_used == s->rx_status_fifo_size) {
+        return -1;
+    }
+
+    filter = lan9118_filter(s, buf);
+    if (!filter && (s->mac_cr & MAC_CR_RXALL) == 0) {
+        return size;
+    }
+
+    offset = (s->rx_cfg >> 8) & 0x1f;
+    n = offset & 3;
+    fifo_len = (size + n + 3) >> 2;
+    /* Add a word for the CRC.  */
+    fifo_len++;
+    if (s->rx_fifo_size - s->rx_fifo_used < fifo_len) {
+        return -1;
+    }
+
+    DPRINTF("Got packet len:%d fifo:%d filter:%s\n",
+            (int)size, fifo_len, filter ? "pass" : "fail");
+    val = 0;
+    crc = bswap32(crc32(~0, buf, size));
+    for (src_pos = 0; src_pos < size; src_pos++) {
+        val = (val >> 8) | ((uint32_t)buf[src_pos] << 24);
+        n++;
+        if (n == 4) {
+            n = 0;
+            rx_fifo_push(s, val);
+            val = 0;
+        }
+    }
+    if (n) {
+        val >>= ((4 - n) * 8);
+        val |= crc << (n * 8);
+        rx_fifo_push(s, val);
+        val = crc >> ((4 - n) * 8);
+        rx_fifo_push(s, val);
+    } else {
+        rx_fifo_push(s, crc);
+    }
+    n = s->rx_status_fifo_head + s->rx_status_fifo_used;
+    if (n >= s->rx_status_fifo_size) {
+        n -= s->rx_status_fifo_size;
+    }
+    s->rx_packet_size[s->rx_packet_size_tail] = fifo_len;
+    s->rx_packet_size_tail = (s->rx_packet_size_tail + 1023) & 1023;
+    s->rx_status_fifo_used++;
+
+    status = (size + 4) << 16;
+    if (buf[0] == 0xff && buf[1] == 0xff && buf[2] == 0xff &&
+        buf[3] == 0xff && buf[4] == 0xff && buf[5] == 0xff) {
+        status |= 0x00002000;
+    } else if (buf[0] & 1) {
+        status |= 0x00000400;
+    }
+    if (!filter) {
+        status |= 0x40000000;
+    }
+    s->rx_status_fifo[n] = status;
+
+    if (s->rx_status_fifo_used > (s->fifo_int & 0xff)) {
+        s->int_sts |= RSFL_INT;
+    }
+    lan9118_update(s);
+
+    return size;
+}
+
+static uint32_t rx_fifo_pop(lan9118_state *s)
+{
+    int n;
+    uint32_t val;
+
+    if (s->rxp_size == 0 && s->rxp_pad == 0) {
+        s->rxp_size = s->rx_packet_size[s->rx_packet_size_head];
+        s->rx_packet_size[s->rx_packet_size_head] = 0;
+        if (s->rxp_size != 0) {
+            s->rx_packet_size_head = (s->rx_packet_size_head + 1023) & 1023;
+            s->rxp_offset = (s->rx_cfg >> 10) & 7;
+            n = s->rxp_offset + s->rxp_size;
+            switch (s->rx_cfg >> 30) {
+            case 1:
+                n = (-n) & 3;
+                break;
+            case 2:
+                n = (-n) & 7;
+                break;
+            default:
+                n = 0;
+                break;
+            }
+            s->rxp_pad = n;
+            DPRINTF("Pop packet size:%d offset:%d pad: %d\n",
+                    s->rxp_size, s->rxp_offset, s->rxp_pad);
+        }
+    }
+    if (s->rxp_offset > 0) {
+        s->rxp_offset--;
+        val = 0;
+    } else if (s->rxp_size > 0) {
+        s->rxp_size--;
+        val = s->rx_fifo[s->rx_fifo_head++];
+        if (s->rx_fifo_head >= s->rx_fifo_size) {
+            s->rx_fifo_head -= s->rx_fifo_size;
+        }
+        s->rx_fifo_used--;
+    } else if (s->rxp_pad > 0) {
+        s->rxp_pad--;
+        val =  0;
+    } else {
+        DPRINTF("RX underflow\n");
+        s->int_sts |= RXE_INT;
+        val =  0;
+    }
+    lan9118_update(s);
+    return val;
+}
+
+static void do_tx_packet(lan9118_state *s)
+{
+    int n;
+    uint32_t status;
+
+    /* FIXME: Honor TX disable, and allow queueing of packets.  */
+    if (s->phy_control & 0x4000)  {
+        /* This assumes the receive routine doesn't touch the VLANClient.  */
+        lan9118_receive(qemu_get_queue(s->nic), s->txp->data, s->txp->len);
+    } else {
+        qemu_send_packet(qemu_get_queue(s->nic), s->txp->data, s->txp->len);
+    }
+    s->txp->fifo_used = 0;
+
+    if (s->tx_status_fifo_used == 512) {
+        /* Status FIFO full */
+        return;
+    }
+    /* Add entry to status FIFO.  */
+    status = s->txp->cmd_b & 0xffff0000u;
+    DPRINTF("Sent packet tag:%04x len %d\n", status >> 16, s->txp->len);
+    n = (s->tx_status_fifo_head + s->tx_status_fifo_used) & 511;
+    s->tx_status_fifo[n] = status;
+    s->tx_status_fifo_used++;
+    if (s->tx_status_fifo_used == 512) {
+        s->int_sts |= TSFF_INT;
+        /* TODO: Stop transmission.  */
+    }
+}
+
+static uint32_t rx_status_fifo_pop(lan9118_state *s)
+{
+    uint32_t val;
+
+    val = s->rx_status_fifo[s->rx_status_fifo_head];
+    if (s->rx_status_fifo_used != 0) {
+        s->rx_status_fifo_used--;
+        s->rx_status_fifo_head++;
+        if (s->rx_status_fifo_head >= s->rx_status_fifo_size) {
+            s->rx_status_fifo_head -= s->rx_status_fifo_size;
+        }
+        /* ??? What value should be returned when the FIFO is empty?  */
+        DPRINTF("RX status pop 0x%08x\n", val);
+    }
+    return val;
+}
+
+static uint32_t tx_status_fifo_pop(lan9118_state *s)
+{
+    uint32_t val;
+
+    val = s->tx_status_fifo[s->tx_status_fifo_head];
+    if (s->tx_status_fifo_used != 0) {
+        s->tx_status_fifo_used--;
+        s->tx_status_fifo_head = (s->tx_status_fifo_head + 1) & 511;
+        /* ??? What value should be returned when the FIFO is empty?  */
+    }
+    return val;
+}
+
+static void tx_fifo_push(lan9118_state *s, uint32_t val)
+{
+    int n;
+
+    if (s->txp->fifo_used == s->tx_fifo_size) {
+        s->int_sts |= TDFO_INT;
+        return;
+    }
+    switch (s->txp->state) {
+    case TX_IDLE:
+        s->txp->cmd_a = val & 0x831f37ff;
+        s->txp->fifo_used++;
+        s->txp->state = TX_B;
+        break;
+    case TX_B:
+        if (s->txp->cmd_a & 0x2000) {
+            /* First segment */
+            s->txp->cmd_b = val;
+            s->txp->fifo_used++;
+            s->txp->buffer_size = s->txp->cmd_a & 0x7ff;
+            s->txp->offset = (s->txp->cmd_a >> 16) & 0x1f;
+            /* End alignment does not include command words.  */
+            n = (s->txp->buffer_size + s->txp->offset + 3) >> 2;
+            switch ((n >> 24) & 3) {
+            case 1:
+                n = (-n) & 3;
+                break;
+            case 2:
+                n = (-n) & 7;
+                break;
+            default:
+                n = 0;
+            }
+            s->txp->pad = n;
+            s->txp->len = 0;
+        }
+        DPRINTF("Block len:%d offset:%d pad:%d cmd %08x\n",
+                s->txp->buffer_size, s->txp->offset, s->txp->pad,
+                s->txp->cmd_a);
+        s->txp->state = TX_DATA;
+        break;
+    case TX_DATA:
+        if (s->txp->offset >= 4) {
+            s->txp->offset -= 4;
+            break;
+        }
+        if (s->txp->buffer_size <= 0 && s->txp->pad != 0) {
+            s->txp->pad--;
+        } else {
+            n = 4;
+            while (s->txp->offset) {
+                val >>= 8;
+                n--;
+                s->txp->offset--;
+            }
+            /* Documentation is somewhat unclear on the ordering of bytes
+               in FIFO words.  Empirical results show it to be little-endian.
+               */
+            /* TODO: FIFO overflow checking.  */
+            while (n--) {
+                s->txp->data[s->txp->len] = val & 0xff;
+                s->txp->len++;
+                val >>= 8;
+                s->txp->buffer_size--;
+            }
+            s->txp->fifo_used++;
+        }
+        if (s->txp->buffer_size <= 0 && s->txp->pad == 0) {
+            if (s->txp->cmd_a & 0x1000) {
+                do_tx_packet(s);
+            }
+            if (s->txp->cmd_a & 0x80000000) {
+                s->int_sts |= TX_IOC_INT;
+            }
+            s->txp->state = TX_IDLE;
+        }
+        break;
+    }
+}
+
+static uint32_t do_phy_read(lan9118_state *s, int reg)
+{
+    uint32_t val;
+
+    switch (reg) {
+    case 0: /* Basic Control */
+        return s->phy_control;
+    case 1: /* Basic Status */
+        return s->phy_status;
+    case 2: /* ID1 */
+        return 0x0007;
+    case 3: /* ID2 */
+        return 0xc0d1;
+    case 4: /* Auto-neg advertisement */
+        return s->phy_advertise;
+    case 5: /* Auto-neg Link Partner Ability */
+        return 0x0f71;
+    case 6: /* Auto-neg Expansion */
+        return 1;
+        /* TODO 17, 18, 27, 29, 30, 31 */
+    case 29: /* Interrupt source.  */
+        val = s->phy_int;
+        s->phy_int = 0;
+        phy_update_irq(s);
+        return val;
+    case 30: /* Interrupt mask */
+        return s->phy_int_mask;
+    default:
+        BADF("PHY read reg %d\n", reg);
+        return 0;
+    }
+}
+
+static void do_phy_write(lan9118_state *s, int reg, uint32_t val)
+{
+    switch (reg) {
+    case 0: /* Basic Control */
+        if (val & 0x8000) {
+            phy_reset(s);
+            break;
+        }
+        s->phy_control = val & 0x7980;
+        /* Complete autonegotiation immediately.  */
+        if (val & 0x1000) {
+            s->phy_status |= 0x0020;
+        }
+        break;
+    case 4: /* Auto-neg advertisement */
+        s->phy_advertise = (val & 0x2d7f) | 0x80;
+        break;
+        /* TODO 17, 18, 27, 31 */
+    case 30: /* Interrupt mask */
+        s->phy_int_mask = val & 0xff;
+        phy_update_irq(s);
+        break;
+    default:
+        BADF("PHY write reg %d = 0x%04x\n", reg, val);
+    }
+}
+
+static void do_mac_write(lan9118_state *s, int reg, uint32_t val)
+{
+    switch (reg) {
+    case MAC_CR:
+        if ((s->mac_cr & MAC_CR_RXEN) != 0 && (val & MAC_CR_RXEN) == 0) {
+            s->int_sts |= RXSTOP_INT;
+        }
+        s->mac_cr = val & ~MAC_CR_RESERVED;
+        DPRINTF("MAC_CR: %08x\n", val);
+        break;
+    case MAC_ADDRH:
+        s->conf.macaddr.a[4] = val & 0xff;
+        s->conf.macaddr.a[5] = (val >> 8) & 0xff;
+        lan9118_mac_changed(s);
+        break;
+    case MAC_ADDRL:
+        s->conf.macaddr.a[0] = val & 0xff;
+        s->conf.macaddr.a[1] = (val >> 8) & 0xff;
+        s->conf.macaddr.a[2] = (val >> 16) & 0xff;
+        s->conf.macaddr.a[3] = (val >> 24) & 0xff;
+        lan9118_mac_changed(s);
+        break;
+    case MAC_HASHH:
+        s->mac_hashh = val;
+        break;
+    case MAC_HASHL:
+        s->mac_hashl = val;
+        break;
+    case MAC_MII_ACC:
+        s->mac_mii_acc = val & 0xffc2;
+        if (val & 2) {
+            DPRINTF("PHY write %d = 0x%04x\n",
+                    (val >> 6) & 0x1f, s->mac_mii_data);
+            do_phy_write(s, (val >> 6) & 0x1f, s->mac_mii_data);
+        } else {
+            s->mac_mii_data = do_phy_read(s, (val >> 6) & 0x1f);
+            DPRINTF("PHY read %d = 0x%04x\n",
+                    (val >> 6) & 0x1f, s->mac_mii_data);
+        }
+        break;
+    case MAC_MII_DATA:
+        s->mac_mii_data = val & 0xffff;
+        break;
+    case MAC_FLOW:
+        s->mac_flow = val & 0xffff0000;
+        break;
+    case MAC_VLAN1:
+        /* Writing to this register changes a condition for
+         * FrameTooLong bit in rx_status.  Since we do not set
+         * FrameTooLong anyway, just ignore write to this.
+         */
+        break;
+    default:
+        hw_error("lan9118: Unimplemented MAC register write: %d = 0x%x\n",
+                 s->mac_cmd & 0xf, val);
+    }
+}
+
+static uint32_t do_mac_read(lan9118_state *s, int reg)
+{
+    switch (reg) {
+    case MAC_CR:
+        return s->mac_cr;
+    case MAC_ADDRH:
+        return s->conf.macaddr.a[4] | (s->conf.macaddr.a[5] << 8);
+    case MAC_ADDRL:
+        return s->conf.macaddr.a[0] | (s->conf.macaddr.a[1] << 8)
+               | (s->conf.macaddr.a[2] << 16) | (s->conf.macaddr.a[3] << 24);
+    case MAC_HASHH:
+        return s->mac_hashh;
+        break;
+    case MAC_HASHL:
+        return s->mac_hashl;
+        break;
+    case MAC_MII_ACC:
+        return s->mac_mii_acc;
+    case MAC_MII_DATA:
+        return s->mac_mii_data;
+    case MAC_FLOW:
+        return s->mac_flow;
+    default:
+        hw_error("lan9118: Unimplemented MAC register read: %d\n",
+                 s->mac_cmd & 0xf);
+    }
+}
+
+static void lan9118_eeprom_cmd(lan9118_state *s, int cmd, int addr)
+{
+    s->e2p_cmd = (s->e2p_cmd & 0x10) | (cmd << 28) | addr;
+    switch (cmd) {
+    case 0:
+        s->e2p_data = s->eeprom[addr];
+        DPRINTF("EEPROM Read %d = 0x%02x\n", addr, s->e2p_data);
+        break;
+    case 1:
+        s->eeprom_writable = 0;
+        DPRINTF("EEPROM Write Disable\n");
+        break;
+    case 2: /* EWEN */
+        s->eeprom_writable = 1;
+        DPRINTF("EEPROM Write Enable\n");
+        break;
+    case 3: /* WRITE */
+        if (s->eeprom_writable) {
+            s->eeprom[addr] &= s->e2p_data;
+            DPRINTF("EEPROM Write %d = 0x%02x\n", addr, s->e2p_data);
+        } else {
+            DPRINTF("EEPROM Write %d (ignored)\n", addr);
+        }
+        break;
+    case 4: /* WRAL */
+        if (s->eeprom_writable) {
+            for (addr = 0; addr < 128; addr++) {
+                s->eeprom[addr] &= s->e2p_data;
+            }
+            DPRINTF("EEPROM Write All 0x%02x\n", s->e2p_data);
+        } else {
+            DPRINTF("EEPROM Write All (ignored)\n");
+        }
+        break;
+    case 5: /* ERASE */
+        if (s->eeprom_writable) {
+            s->eeprom[addr] = 0xff;
+            DPRINTF("EEPROM Erase %d\n", addr);
+        } else {
+            DPRINTF("EEPROM Erase %d (ignored)\n", addr);
+        }
+        break;
+    case 6: /* ERAL */
+        if (s->eeprom_writable) {
+            memset(s->eeprom, 0xff, 128);
+            DPRINTF("EEPROM Erase All\n");
+        } else {
+            DPRINTF("EEPROM Erase All (ignored)\n");
+        }
+        break;
+    case 7: /* RELOAD */
+        lan9118_reload_eeprom(s);
+        break;
+    }
+}
+
+static void lan9118_tick(void *opaque)
+{
+    lan9118_state *s = (lan9118_state *)opaque;
+    if (s->int_en & GPT_INT) {
+        s->int_sts |= GPT_INT;
+    }
+    lan9118_update(s);
+}
+
+static void lan9118_writel(void *opaque, hwaddr offset,
+                           uint64_t val, unsigned size)
+{
+    lan9118_state *s = (lan9118_state *)opaque;
+    offset &= 0xff;
+
+    //DPRINTF("Write reg 0x%02x = 0x%08x\n", (int)offset, val);
+    if (offset >= 0x20 && offset < 0x40) {
+        /* TX FIFO */
+        tx_fifo_push(s, val);
+        return;
+    }
+    switch (offset) {
+    case CSR_IRQ_CFG:
+        /* TODO: Implement interrupt deassertion intervals.  */
+        val &= (IRQ_EN | IRQ_POL | IRQ_TYPE);
+        s->irq_cfg = (s->irq_cfg & IRQ_INT) | val;
+        break;
+    case CSR_INT_STS:
+        s->int_sts &= ~val;
+        break;
+    case CSR_INT_EN:
+        s->int_en = val & ~RESERVED_INT;
+        s->int_sts |= val & SW_INT;
+        break;
+    case CSR_FIFO_INT:
+        DPRINTF("FIFO INT levels %08x\n", val);
+        s->fifo_int = val;
+        break;
+    case CSR_RX_CFG:
+        if (val & 0x8000) {
+            /* RX_DUMP */
+            s->rx_fifo_used = 0;
+            s->rx_status_fifo_used = 0;
+            s->rx_packet_size_tail = s->rx_packet_size_head;
+            s->rx_packet_size[s->rx_packet_size_head] = 0;
+        }
+        s->rx_cfg = val & 0xcfff1ff0;
+        break;
+    case CSR_TX_CFG:
+        if (val & 0x8000) {
+            s->tx_status_fifo_used = 0;
+        }
+        if (val & 0x4000) {
+            s->txp->state = TX_IDLE;
+            s->txp->fifo_used = 0;
+            s->txp->cmd_a = 0xffffffff;
+        }
+        s->tx_cfg = val & 6;
+        break;
+    case CSR_HW_CFG:
+        if (val & 1) {
+            /* SRST */
+            lan9118_reset(&s->busdev.qdev);
+        } else {
+            s->hw_cfg = (val & 0x003f300) | (s->hw_cfg & 0x4);
+        }
+        break;
+    case CSR_RX_DP_CTRL:
+        if (val & 0x80000000) {
+            /* Skip forward to next packet.  */
+            s->rxp_pad = 0;
+            s->rxp_offset = 0;
+            if (s->rxp_size == 0) {
+                /* Pop a word to start the next packet.  */
+                rx_fifo_pop(s);
+                s->rxp_pad = 0;
+                s->rxp_offset = 0;
+            }
+            s->rx_fifo_head += s->rxp_size;
+            if (s->rx_fifo_head >= s->rx_fifo_size) {
+                s->rx_fifo_head -= s->rx_fifo_size;
+            }
+        }
+        break;
+    case CSR_PMT_CTRL:
+        if (val & 0x400) {
+            phy_reset(s);
+        }
+        s->pmt_ctrl &= ~0x34e;
+        s->pmt_ctrl |= (val & 0x34e);
+        break;
+    case CSR_GPIO_CFG:
+        /* Probably just enabling LEDs.  */
+        s->gpio_cfg = val & 0x7777071f;
+        break;
+    case CSR_GPT_CFG:
+        if ((s->gpt_cfg ^ val) & GPT_TIMER_EN) {
+            if (val & GPT_TIMER_EN) {
+                ptimer_set_count(s->timer, val & 0xffff);
+                ptimer_run(s->timer, 0);
+            } else {
+                ptimer_stop(s->timer);
+                ptimer_set_count(s->timer, 0xffff);
+            }
+        }
+        s->gpt_cfg = val & (GPT_TIMER_EN | 0xffff);
+        break;
+    case CSR_WORD_SWAP:
+        /* Ignored because we're in 32-bit mode.  */
+        s->word_swap = val;
+        break;
+    case CSR_MAC_CSR_CMD:
+        s->mac_cmd = val & 0x4000000f;
+        if (val & 0x80000000) {
+            if (val & 0x40000000) {
+                s->mac_data = do_mac_read(s, val & 0xf);
+                DPRINTF("MAC read %d = 0x%08x\n", val & 0xf, s->mac_data);
+            } else {
+                DPRINTF("MAC write %d = 0x%08x\n", val & 0xf, s->mac_data);
+                do_mac_write(s, val & 0xf, s->mac_data);
+            }
+        }
+        break;
+    case CSR_MAC_CSR_DATA:
+        s->mac_data = val;
+        break;
+    case CSR_AFC_CFG:
+        s->afc_cfg = val & 0x00ffffff;
+        break;
+    case CSR_E2P_CMD:
+        lan9118_eeprom_cmd(s, (val >> 28) & 7, val & 0x7f);
+        break;
+    case CSR_E2P_DATA:
+        s->e2p_data = val & 0xff;
+        break;
+
+    default:
+        hw_error("lan9118_write: Bad reg 0x%x = %x\n", (int)offset, (int)val);
+        break;
+    }
+    lan9118_update(s);
+}
+
+static void lan9118_writew(void *opaque, hwaddr offset,
+                           uint32_t val)
+{
+    lan9118_state *s = (lan9118_state *)opaque;
+    offset &= 0xff;
+
+    if (s->write_word_prev_offset != (offset & ~0x3)) {
+        /* New offset, reset word counter */
+        s->write_word_n = 0;
+        s->write_word_prev_offset = offset & ~0x3;
+    }
+
+    if (offset & 0x2) {
+        s->write_word_h = val;
+    } else {
+        s->write_word_l = val;
+    }
+
+    //DPRINTF("Writew reg 0x%02x = 0x%08x\n", (int)offset, val);
+    s->write_word_n++;
+    if (s->write_word_n == 2) {
+        s->write_word_n = 0;
+        lan9118_writel(s, offset & ~3, s->write_word_l +
+                (s->write_word_h << 16), 4);
+    }
+}
+
+static void lan9118_16bit_mode_write(void *opaque, hwaddr offset,
+                                     uint64_t val, unsigned size)
+{
+    switch (size) {
+    case 2:
+        lan9118_writew(opaque, offset, (uint32_t)val);
+        return;
+    case 4:
+        lan9118_writel(opaque, offset, val, size);
+        return;
+    }
+
+    hw_error("lan9118_write: Bad size 0x%x\n", size);
+}
+
+static uint64_t lan9118_readl(void *opaque, hwaddr offset,
+                              unsigned size)
+{
+    lan9118_state *s = (lan9118_state *)opaque;
+
+    //DPRINTF("Read reg 0x%02x\n", (int)offset);
+    if (offset < 0x20) {
+        /* RX FIFO */
+        return rx_fifo_pop(s);
+    }
+    switch (offset) {
+    case 0x40:
+        return rx_status_fifo_pop(s);
+    case 0x44:
+        return s->rx_status_fifo[s->tx_status_fifo_head];
+    case 0x48:
+        return tx_status_fifo_pop(s);
+    case 0x4c:
+        return s->tx_status_fifo[s->tx_status_fifo_head];
+    case CSR_ID_REV:
+        return 0x01180001;
+    case CSR_IRQ_CFG:
+        return s->irq_cfg;
+    case CSR_INT_STS:
+        return s->int_sts;
+    case CSR_INT_EN:
+        return s->int_en;
+    case CSR_BYTE_TEST:
+        return 0x87654321;
+    case CSR_FIFO_INT:
+        return s->fifo_int;
+    case CSR_RX_CFG:
+        return s->rx_cfg;
+    case CSR_TX_CFG:
+        return s->tx_cfg;
+    case CSR_HW_CFG:
+        return s->hw_cfg;
+    case CSR_RX_DP_CTRL:
+        return 0;
+    case CSR_RX_FIFO_INF:
+        return (s->rx_status_fifo_used << 16) | (s->rx_fifo_used << 2);
+    case CSR_TX_FIFO_INF:
+        return (s->tx_status_fifo_used << 16)
+               | (s->tx_fifo_size - s->txp->fifo_used);
+    case CSR_PMT_CTRL:
+        return s->pmt_ctrl;
+    case CSR_GPIO_CFG:
+        return s->gpio_cfg;
+    case CSR_GPT_CFG:
+        return s->gpt_cfg;
+    case CSR_GPT_CNT:
+        return ptimer_get_count(s->timer);
+    case CSR_WORD_SWAP:
+        return s->word_swap;
+    case CSR_FREE_RUN:
+        return (qemu_get_clock_ns(vm_clock) / 40) - s->free_timer_start;
+    case CSR_RX_DROP:
+        /* TODO: Implement dropped frames counter.  */
+        return 0;
+    case CSR_MAC_CSR_CMD:
+        return s->mac_cmd;
+    case CSR_MAC_CSR_DATA:
+        return s->mac_data;
+    case CSR_AFC_CFG:
+        return s->afc_cfg;
+    case CSR_E2P_CMD:
+        return s->e2p_cmd;
+    case CSR_E2P_DATA:
+        return s->e2p_data;
+    }
+    hw_error("lan9118_read: Bad reg 0x%x\n", (int)offset);
+    return 0;
+}
+
+static uint32_t lan9118_readw(void *opaque, hwaddr offset)
+{
+    lan9118_state *s = (lan9118_state *)opaque;
+    uint32_t val;
+
+    if (s->read_word_prev_offset != (offset & ~0x3)) {
+        /* New offset, reset word counter */
+        s->read_word_n = 0;
+        s->read_word_prev_offset = offset & ~0x3;
+    }
+
+    s->read_word_n++;
+    if (s->read_word_n == 1) {
+        s->read_long = lan9118_readl(s, offset & ~3, 4);
+    } else {
+        s->read_word_n = 0;
+    }
+
+    if (offset & 2) {
+        val = s->read_long >> 16;
+    } else {
+        val = s->read_long & 0xFFFF;
+    }
+
+    //DPRINTF("Readw reg 0x%02x, val 0x%x\n", (int)offset, val);
+    return val;
+}
+
+static uint64_t lan9118_16bit_mode_read(void *opaque, hwaddr offset,
+                                        unsigned size)
+{
+    switch (size) {
+    case 2:
+        return lan9118_readw(opaque, offset);
+    case 4:
+        return lan9118_readl(opaque, offset, size);
+    }
+
+    hw_error("lan9118_read: Bad size 0x%x\n", size);
+    return 0;
+}
+
+static const MemoryRegionOps lan9118_mem_ops = {
+    .read = lan9118_readl,
+    .write = lan9118_writel,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const MemoryRegionOps lan9118_16bit_mem_ops = {
+    .read = lan9118_16bit_mode_read,
+    .write = lan9118_16bit_mode_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void lan9118_cleanup(NetClientState *nc)
+{
+    lan9118_state *s = qemu_get_nic_opaque(nc);
+
+    s->nic = NULL;
+}
+
+static NetClientInfo net_lan9118_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = lan9118_can_receive,
+    .receive = lan9118_receive,
+    .cleanup = lan9118_cleanup,
+    .link_status_changed = lan9118_set_link,
+};
+
+static int lan9118_init1(SysBusDevice *dev)
+{
+    lan9118_state *s = FROM_SYSBUS(lan9118_state, dev);
+    QEMUBH *bh;
+    int i;
+    const MemoryRegionOps *mem_ops =
+            s->mode_16bit ? &lan9118_16bit_mem_ops : &lan9118_mem_ops;
+
+    memory_region_init_io(&s->mmio, mem_ops, s, "lan9118-mmio", 0x100);
+    sysbus_init_mmio(dev, &s->mmio);
+    sysbus_init_irq(dev, &s->irq);
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+
+    s->nic = qemu_new_nic(&net_lan9118_info, &s->conf,
+                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+    s->eeprom[0] = 0xa5;
+    for (i = 0; i < 6; i++) {
+        s->eeprom[i + 1] = s->conf.macaddr.a[i];
+    }
+    s->pmt_ctrl = 1;
+    s->txp = &s->tx_packet;
+
+    bh = qemu_bh_new(lan9118_tick, s);
+    s->timer = ptimer_init(bh);
+    ptimer_set_freq(s->timer, 10000);
+    ptimer_set_limit(s->timer, 0xffff, 1);
+
+    return 0;
+}
+
+static Property lan9118_properties[] = {
+    DEFINE_NIC_PROPERTIES(lan9118_state, conf),
+    DEFINE_PROP_UINT32("mode_16bit", lan9118_state, mode_16bit, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void lan9118_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = lan9118_init1;
+    dc->reset = lan9118_reset;
+    dc->props = lan9118_properties;
+    dc->vmsd = &vmstate_lan9118;
+}
+
+static const TypeInfo lan9118_info = {
+    .name          = "lan9118",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(lan9118_state),
+    .class_init    = lan9118_class_init,
+};
+
+static void lan9118_register_types(void)
+{
+    type_register_static(&lan9118_info);
+}
+
+/* Legacy helper function.  Should go away when machine config files are
+   implemented.  */
+void lan9118_init(NICInfo *nd, uint32_t base, qemu_irq irq)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    qemu_check_nic_model(nd, "lan9118");
+    dev = qdev_create(NULL, "lan9118");
+    qdev_set_nic_properties(dev, nd);
+    qdev_init_nofail(dev);
+    s = SYS_BUS_DEVICE(dev);
+    sysbus_mmio_map(s, 0, base);
+    sysbus_connect_irq(s, 0, irq);
+}
+
+type_init(lan9118_register_types)
diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c
new file mode 100644 (file)
index 0000000..ac6193a
--- /dev/null
@@ -0,0 +1,284 @@
+#include "hw/hw.h"
+#include "net/net.h"
+#include "trace.h"
+#include "hw/sysbus.h"
+
+/* MIPSnet register offsets */
+
+#define MIPSNET_DEV_ID         0x00
+#define MIPSNET_BUSY           0x08
+#define MIPSNET_RX_DATA_COUNT  0x0c
+#define MIPSNET_TX_DATA_COUNT  0x10
+#define MIPSNET_INT_CTL                0x14
+# define MIPSNET_INTCTL_TXDONE         0x00000001
+# define MIPSNET_INTCTL_RXDONE         0x00000002
+# define MIPSNET_INTCTL_TESTBIT                0x80000000
+#define MIPSNET_INTERRUPT_INFO 0x18
+#define MIPSNET_RX_DATA_BUFFER 0x1c
+#define MIPSNET_TX_DATA_BUFFER 0x20
+
+#define MAX_ETH_FRAME_SIZE     1514
+
+typedef struct MIPSnetState {
+    SysBusDevice busdev;
+
+    uint32_t busy;
+    uint32_t rx_count;
+    uint32_t rx_read;
+    uint32_t tx_count;
+    uint32_t tx_written;
+    uint32_t intctl;
+    uint8_t rx_buffer[MAX_ETH_FRAME_SIZE];
+    uint8_t tx_buffer[MAX_ETH_FRAME_SIZE];
+    MemoryRegion io;
+    qemu_irq irq;
+    NICState *nic;
+    NICConf conf;
+} MIPSnetState;
+
+static void mipsnet_reset(MIPSnetState *s)
+{
+    s->busy = 1;
+    s->rx_count = 0;
+    s->rx_read = 0;
+    s->tx_count = 0;
+    s->tx_written = 0;
+    s->intctl = 0;
+    memset(s->rx_buffer, 0, MAX_ETH_FRAME_SIZE);
+    memset(s->tx_buffer, 0, MAX_ETH_FRAME_SIZE);
+}
+
+static void mipsnet_update_irq(MIPSnetState *s)
+{
+    int isr = !!s->intctl;
+    trace_mipsnet_irq(isr, s->intctl);
+    qemu_set_irq(s->irq, isr);
+}
+
+static int mipsnet_buffer_full(MIPSnetState *s)
+{
+    if (s->rx_count >= MAX_ETH_FRAME_SIZE)
+        return 1;
+    return 0;
+}
+
+static int mipsnet_can_receive(NetClientState *nc)
+{
+    MIPSnetState *s = qemu_get_nic_opaque(nc);
+
+    if (s->busy)
+        return 0;
+    return !mipsnet_buffer_full(s);
+}
+
+static ssize_t mipsnet_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+    MIPSnetState *s = qemu_get_nic_opaque(nc);
+
+    trace_mipsnet_receive(size);
+    if (!mipsnet_can_receive(nc))
+        return -1;
+
+    s->busy = 1;
+
+    /* Just accept everything. */
+
+    /* Write packet data. */
+    memcpy(s->rx_buffer, buf, size);
+
+    s->rx_count = size;
+    s->rx_read = 0;
+
+    /* Now we can signal we have received something. */
+    s->intctl |= MIPSNET_INTCTL_RXDONE;
+    mipsnet_update_irq(s);
+
+    return size;
+}
+
+static uint64_t mipsnet_ioport_read(void *opaque, hwaddr addr,
+                                    unsigned int size)
+{
+    MIPSnetState *s = opaque;
+    int ret = 0;
+
+    addr &= 0x3f;
+    switch (addr) {
+    case MIPSNET_DEV_ID:
+       ret = be32_to_cpu(0x4d495053);          /* MIPS */
+        break;
+    case MIPSNET_DEV_ID + 4:
+       ret = be32_to_cpu(0x4e455430);          /* NET0 */
+        break;
+    case MIPSNET_BUSY:
+       ret = s->busy;
+        break;
+    case MIPSNET_RX_DATA_COUNT:
+       ret = s->rx_count;
+        break;
+    case MIPSNET_TX_DATA_COUNT:
+       ret = s->tx_count;
+        break;
+    case MIPSNET_INT_CTL:
+       ret = s->intctl;
+        s->intctl &= ~MIPSNET_INTCTL_TESTBIT;
+        break;
+    case MIPSNET_INTERRUPT_INFO:
+        /* XXX: This seems to be a per-VPE interrupt number. */
+       ret = 0;
+        break;
+    case MIPSNET_RX_DATA_BUFFER:
+        if (s->rx_count) {
+            s->rx_count--;
+            ret = s->rx_buffer[s->rx_read++];
+        }
+        break;
+    /* Reads as zero. */
+    case MIPSNET_TX_DATA_BUFFER:
+    default:
+        break;
+    }
+    trace_mipsnet_read(addr, ret);
+    return ret;
+}
+
+static void mipsnet_ioport_write(void *opaque, hwaddr addr,
+                                 uint64_t val, unsigned int size)
+{
+    MIPSnetState *s = opaque;
+
+    addr &= 0x3f;
+    trace_mipsnet_write(addr, val);
+    switch (addr) {
+    case MIPSNET_TX_DATA_COUNT:
+       s->tx_count = (val <= MAX_ETH_FRAME_SIZE) ? val : 0;
+        s->tx_written = 0;
+        break;
+    case MIPSNET_INT_CTL:
+        if (val & MIPSNET_INTCTL_TXDONE) {
+            s->intctl &= ~MIPSNET_INTCTL_TXDONE;
+        } else if (val & MIPSNET_INTCTL_RXDONE) {
+            s->intctl &= ~MIPSNET_INTCTL_RXDONE;
+        } else if (val & MIPSNET_INTCTL_TESTBIT) {
+            mipsnet_reset(s);
+            s->intctl |= MIPSNET_INTCTL_TESTBIT;
+        } else if (!val) {
+            /* ACK testbit interrupt, flag was cleared on read. */
+        }
+        s->busy = !!s->intctl;
+        mipsnet_update_irq(s);
+        break;
+    case MIPSNET_TX_DATA_BUFFER:
+        s->tx_buffer[s->tx_written++] = val;
+        if (s->tx_written == s->tx_count) {
+            /* Send buffer. */
+            trace_mipsnet_send(s->tx_count);
+            qemu_send_packet(qemu_get_queue(s->nic), s->tx_buffer, s->tx_count);
+            s->tx_count = s->tx_written = 0;
+            s->intctl |= MIPSNET_INTCTL_TXDONE;
+            s->busy = 1;
+            mipsnet_update_irq(s);
+        }
+        break;
+    /* Read-only registers */
+    case MIPSNET_DEV_ID:
+    case MIPSNET_BUSY:
+    case MIPSNET_RX_DATA_COUNT:
+    case MIPSNET_INTERRUPT_INFO:
+    case MIPSNET_RX_DATA_BUFFER:
+    default:
+        break;
+    }
+}
+
+static const VMStateDescription vmstate_mipsnet = {
+    .name = "mipsnet",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32(busy, MIPSnetState),
+        VMSTATE_UINT32(rx_count, MIPSnetState),
+        VMSTATE_UINT32(rx_read, MIPSnetState),
+        VMSTATE_UINT32(tx_count, MIPSnetState),
+        VMSTATE_UINT32(tx_written, MIPSnetState),
+        VMSTATE_UINT32(intctl, MIPSnetState),
+        VMSTATE_BUFFER(rx_buffer, MIPSnetState),
+        VMSTATE_BUFFER(tx_buffer, MIPSnetState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void mipsnet_cleanup(NetClientState *nc)
+{
+    MIPSnetState *s = qemu_get_nic_opaque(nc);
+
+    s->nic = NULL;
+}
+
+static NetClientInfo net_mipsnet_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = mipsnet_can_receive,
+    .receive = mipsnet_receive,
+    .cleanup = mipsnet_cleanup,
+};
+
+static const MemoryRegionOps mipsnet_ioport_ops = {
+    .read = mipsnet_ioport_read,
+    .write = mipsnet_ioport_write,
+    .impl.min_access_size = 1,
+    .impl.max_access_size = 4,
+};
+
+static int mipsnet_sysbus_init(SysBusDevice *dev)
+{
+    MIPSnetState *s = DO_UPCAST(MIPSnetState, busdev, dev);
+
+    memory_region_init_io(&s->io, &mipsnet_ioport_ops, s, "mipsnet-io", 36);
+    sysbus_init_mmio(dev, &s->io);
+    sysbus_init_irq(dev, &s->irq);
+
+    s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf,
+                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+
+    return 0;
+}
+
+static void mipsnet_sysbus_reset(DeviceState *dev)
+{
+    MIPSnetState *s = DO_UPCAST(MIPSnetState, busdev.qdev, dev);
+    mipsnet_reset(s);
+}
+
+static Property mipsnet_properties[] = {
+    DEFINE_NIC_PROPERTIES(MIPSnetState, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mipsnet_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = mipsnet_sysbus_init;
+    dc->desc = "MIPS Simulator network device";
+    dc->reset = mipsnet_sysbus_reset;
+    dc->vmsd = &vmstate_mipsnet;
+    dc->props = mipsnet_properties;
+}
+
+static const TypeInfo mipsnet_info = {
+    .name          = "mipsnet",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(MIPSnetState),
+    .class_init    = mipsnet_class_init,
+};
+
+static void mipsnet_register_types(void)
+{
+    type_register_static(&mipsnet_info);
+}
+
+type_init(mipsnet_register_types)
diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c
new file mode 100644 (file)
index 0000000..e4c10db
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * QEMU NE2000 emulation -- isa bus windup
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "hw/isa/isa.h"
+#include "hw/qdev.h"
+#include "net/net.h"
+#include "hw/ne2000.h"
+#include "exec/address-spaces.h"
+
+typedef struct ISANE2000State {
+    ISADevice dev;
+    uint32_t iobase;
+    uint32_t isairq;
+    NE2000State ne2000;
+} ISANE2000State;
+
+static void isa_ne2000_cleanup(NetClientState *nc)
+{
+    NE2000State *s = qemu_get_nic_opaque(nc);
+
+    s->nic = NULL;
+}
+
+static NetClientInfo net_ne2000_isa_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = ne2000_can_receive,
+    .receive = ne2000_receive,
+    .cleanup = isa_ne2000_cleanup,
+};
+
+static const VMStateDescription vmstate_isa_ne2000 = {
+    .name = "ne2000",
+    .version_id = 2,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields      = (VMStateField []) {
+        VMSTATE_STRUCT(ne2000, ISANE2000State, 0, vmstate_ne2000, NE2000State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int isa_ne2000_initfn(ISADevice *dev)
+{
+    ISANE2000State *isa = DO_UPCAST(ISANE2000State, dev, dev);
+    NE2000State *s = &isa->ne2000;
+
+    ne2000_setup_io(s, 0x20);
+    isa_register_ioport(dev, &s->io, isa->iobase);
+
+    isa_init_irq(dev, &s->irq, isa->isairq);
+
+    qemu_macaddr_default_if_unset(&s->c.macaddr);
+    ne2000_reset(s);
+
+    s->nic = qemu_new_nic(&net_ne2000_isa_info, &s->c,
+                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a);
+
+    return 0;
+}
+
+static Property ne2000_isa_properties[] = {
+    DEFINE_PROP_HEX32("iobase", ISANE2000State, iobase, 0x300),
+    DEFINE_PROP_UINT32("irq",   ISANE2000State, isairq, 9),
+    DEFINE_NIC_PROPERTIES(ISANE2000State, ne2000.c),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void isa_ne2000_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+    ic->init = isa_ne2000_initfn;
+    dc->props = ne2000_isa_properties;
+}
+
+static const TypeInfo ne2000_isa_info = {
+    .name          = "ne2k_isa",
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(ISANE2000State),
+    .class_init    = isa_ne2000_class_initfn,
+};
+
+static void ne2000_isa_register_types(void)
+{
+    type_register_static(&ne2000_isa_info);
+}
+
+type_init(ne2000_isa_register_types)
diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c
new file mode 100644 (file)
index 0000000..7f45831
--- /dev/null
@@ -0,0 +1,789 @@
+/*
+ * QEMU NE2000 emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "net/net.h"
+#include "hw/ne2000.h"
+#include "hw/loader.h"
+#include "sysemu/sysemu.h"
+
+/* debug NE2000 card */
+//#define DEBUG_NE2000
+
+#define MAX_ETH_FRAME_SIZE 1514
+
+#define E8390_CMD      0x00  /* The command register (for all pages) */
+/* Page 0 register offsets. */
+#define EN0_CLDALO     0x01    /* Low byte of current local dma addr  RD */
+#define EN0_STARTPG    0x01    /* Starting page of ring bfr WR */
+#define EN0_CLDAHI     0x02    /* High byte of current local dma addr  RD */
+#define EN0_STOPPG     0x02    /* Ending page +1 of ring bfr WR */
+#define EN0_BOUNDARY   0x03    /* Boundary page of ring bfr RD WR */
+#define EN0_TSR                0x04    /* Transmit status reg RD */
+#define EN0_TPSR       0x04    /* Transmit starting page WR */
+#define EN0_NCR                0x05    /* Number of collision reg RD */
+#define EN0_TCNTLO     0x05    /* Low  byte of tx byte count WR */
+#define EN0_FIFO       0x06    /* FIFO RD */
+#define EN0_TCNTHI     0x06    /* High byte of tx byte count WR */
+#define EN0_ISR                0x07    /* Interrupt status reg RD WR */
+#define EN0_CRDALO     0x08    /* low byte of current remote dma address RD */
+#define EN0_RSARLO     0x08    /* Remote start address reg 0 */
+#define EN0_CRDAHI     0x09    /* high byte, current remote dma address RD */
+#define EN0_RSARHI     0x09    /* Remote start address reg 1 */
+#define EN0_RCNTLO     0x0a    /* Remote byte count reg WR */
+#define EN0_RTL8029ID0 0x0a    /* Realtek ID byte #1 RD */
+#define EN0_RCNTHI     0x0b    /* Remote byte count reg WR */
+#define EN0_RTL8029ID1 0x0b    /* Realtek ID byte #2 RD */
+#define EN0_RSR                0x0c    /* rx status reg RD */
+#define EN0_RXCR       0x0c    /* RX configuration reg WR */
+#define EN0_TXCR       0x0d    /* TX configuration reg WR */
+#define EN0_COUNTER0   0x0d    /* Rcv alignment error counter RD */
+#define EN0_DCFG       0x0e    /* Data configuration reg WR */
+#define EN0_COUNTER1   0x0e    /* Rcv CRC error counter RD */
+#define EN0_IMR                0x0f    /* Interrupt mask reg WR */
+#define EN0_COUNTER2   0x0f    /* Rcv missed frame error counter RD */
+
+#define EN1_PHYS        0x11
+#define EN1_CURPAG      0x17
+#define EN1_MULT        0x18
+
+#define EN2_STARTPG    0x21    /* Starting page of ring bfr RD */
+#define EN2_STOPPG     0x22    /* Ending page +1 of ring bfr RD */
+
+#define EN3_CONFIG0    0x33
+#define EN3_CONFIG1    0x34
+#define EN3_CONFIG2    0x35
+#define EN3_CONFIG3    0x36
+
+/*  Register accessed at EN_CMD, the 8390 base addr.  */
+#define E8390_STOP     0x01    /* Stop and reset the chip */
+#define E8390_START    0x02    /* Start the chip, clear reset */
+#define E8390_TRANS    0x04    /* Transmit a frame */
+#define E8390_RREAD    0x08    /* Remote read */
+#define E8390_RWRITE   0x10    /* Remote write  */
+#define E8390_NODMA    0x20    /* Remote DMA */
+#define E8390_PAGE0    0x00    /* Select page chip registers */
+#define E8390_PAGE1    0x40    /* using the two high-order bits */
+#define E8390_PAGE2    0x80    /* Page 3 is invalid. */
+
+/* Bits in EN0_ISR - Interrupt status register */
+#define ENISR_RX       0x01    /* Receiver, no error */
+#define ENISR_TX       0x02    /* Transmitter, no error */
+#define ENISR_RX_ERR   0x04    /* Receiver, with error */
+#define ENISR_TX_ERR   0x08    /* Transmitter, with error */
+#define ENISR_OVER     0x10    /* Receiver overwrote the ring */
+#define ENISR_COUNTERS 0x20    /* Counters need emptying */
+#define ENISR_RDC      0x40    /* remote dma complete */
+#define ENISR_RESET    0x80    /* Reset completed */
+#define ENISR_ALL      0x3f    /* Interrupts we will enable */
+
+/* Bits in received packet status byte and EN0_RSR*/
+#define ENRSR_RXOK     0x01    /* Received a good packet */
+#define ENRSR_CRC      0x02    /* CRC error */
+#define ENRSR_FAE      0x04    /* frame alignment error */
+#define ENRSR_FO       0x08    /* FIFO overrun */
+#define ENRSR_MPA      0x10    /* missed pkt */
+#define ENRSR_PHY      0x20    /* physical/multicast address */
+#define ENRSR_DIS      0x40    /* receiver disable. set in monitor mode */
+#define ENRSR_DEF      0x80    /* deferring */
+
+/* Transmitted packet status, EN0_TSR. */
+#define ENTSR_PTX 0x01 /* Packet transmitted without error */
+#define ENTSR_ND  0x02 /* The transmit wasn't deferred. */
+#define ENTSR_COL 0x04 /* The transmit collided at least once. */
+#define ENTSR_ABT 0x08  /* The transmit collided 16 times, and was deferred. */
+#define ENTSR_CRS 0x10 /* The carrier sense was lost. */
+#define ENTSR_FU  0x20  /* A "FIFO underrun" occurred during transmit. */
+#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */
+#define ENTSR_OWC 0x80  /* There was an out-of-window collision. */
+
+typedef struct PCINE2000State {
+    PCIDevice dev;
+    NE2000State ne2000;
+} PCINE2000State;
+
+void ne2000_reset(NE2000State *s)
+{
+    int i;
+
+    s->isr = ENISR_RESET;
+    memcpy(s->mem, &s->c.macaddr, 6);
+    s->mem[14] = 0x57;
+    s->mem[15] = 0x57;
+
+    /* duplicate prom data */
+    for(i = 15;i >= 0; i--) {
+        s->mem[2 * i] = s->mem[i];
+        s->mem[2 * i + 1] = s->mem[i];
+    }
+}
+
+static void ne2000_update_irq(NE2000State *s)
+{
+    int isr;
+    isr = (s->isr & s->imr) & 0x7f;
+#if defined(DEBUG_NE2000)
+    printf("NE2000: Set IRQ to %d (%02x %02x)\n",
+          isr ? 1 : 0, s->isr, s->imr);
+#endif
+    qemu_set_irq(s->irq, (isr != 0));
+}
+
+static int ne2000_buffer_full(NE2000State *s)
+{
+    int avail, index, boundary;
+
+    index = s->curpag << 8;
+    boundary = s->boundary << 8;
+    if (index < boundary)
+        avail = boundary - index;
+    else
+        avail = (s->stop - s->start) - (index - boundary);
+    if (avail < (MAX_ETH_FRAME_SIZE + 4))
+        return 1;
+    return 0;
+}
+
+int ne2000_can_receive(NetClientState *nc)
+{
+    NE2000State *s = qemu_get_nic_opaque(nc);
+
+    if (s->cmd & E8390_STOP)
+        return 1;
+    return !ne2000_buffer_full(s);
+}
+
+#define MIN_BUF_SIZE 60
+
+ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
+{
+    NE2000State *s = qemu_get_nic_opaque(nc);
+    int size = size_;
+    uint8_t *p;
+    unsigned int total_len, next, avail, len, index, mcast_idx;
+    uint8_t buf1[60];
+    static const uint8_t broadcast_macaddr[6] =
+        { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+#if defined(DEBUG_NE2000)
+    printf("NE2000: received len=%d\n", size);
+#endif
+
+    if (s->cmd & E8390_STOP || ne2000_buffer_full(s))
+        return -1;
+
+    /* XXX: check this */
+    if (s->rxcr & 0x10) {
+        /* promiscuous: receive all */
+    } else {
+        if (!memcmp(buf,  broadcast_macaddr, 6)) {
+            /* broadcast address */
+            if (!(s->rxcr & 0x04))
+                return size;
+        } else if (buf[0] & 0x01) {
+            /* multicast */
+            if (!(s->rxcr & 0x08))
+                return size;
+            mcast_idx = compute_mcast_idx(buf);
+            if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))))
+                return size;
+        } else if (s->mem[0] == buf[0] &&
+                   s->mem[2] == buf[1] &&
+                   s->mem[4] == buf[2] &&
+                   s->mem[6] == buf[3] &&
+                   s->mem[8] == buf[4] &&
+                   s->mem[10] == buf[5]) {
+            /* match */
+        } else {
+            return size;
+        }
+    }
+
+
+    /* if too small buffer, then expand it */
+    if (size < MIN_BUF_SIZE) {
+        memcpy(buf1, buf, size);
+        memset(buf1 + size, 0, MIN_BUF_SIZE - size);
+        buf = buf1;
+        size = MIN_BUF_SIZE;
+    }
+
+    index = s->curpag << 8;
+    /* 4 bytes for header */
+    total_len = size + 4;
+    /* address for next packet (4 bytes for CRC) */
+    next = index + ((total_len + 4 + 255) & ~0xff);
+    if (next >= s->stop)
+        next -= (s->stop - s->start);
+    /* prepare packet header */
+    p = s->mem + index;
+    s->rsr = ENRSR_RXOK; /* receive status */
+    /* XXX: check this */
+    if (buf[0] & 0x01)
+        s->rsr |= ENRSR_PHY;
+    p[0] = s->rsr;
+    p[1] = next >> 8;
+    p[2] = total_len;
+    p[3] = total_len >> 8;
+    index += 4;
+
+    /* write packet data */
+    while (size > 0) {
+        if (index <= s->stop)
+            avail = s->stop - index;
+        else
+            avail = 0;
+        len = size;
+        if (len > avail)
+            len = avail;
+        memcpy(s->mem + index, buf, len);
+        buf += len;
+        index += len;
+        if (index == s->stop)
+            index = s->start;
+        size -= len;
+    }
+    s->curpag = next >> 8;
+
+    /* now we can signal we have received something */
+    s->isr |= ENISR_RX;
+    ne2000_update_irq(s);
+
+    return size_;
+}
+
+static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    NE2000State *s = opaque;
+    int offset, page, index;
+
+    addr &= 0xf;
+#ifdef DEBUG_NE2000
+    printf("NE2000: write addr=0x%x val=0x%02x\n", addr, val);
+#endif
+    if (addr == E8390_CMD) {
+        /* control register */
+        s->cmd = val;
+        if (!(val & E8390_STOP)) { /* START bit makes no sense on RTL8029... */
+            s->isr &= ~ENISR_RESET;
+            /* test specific case: zero length transfer */
+            if ((val & (E8390_RREAD | E8390_RWRITE)) &&
+                s->rcnt == 0) {
+                s->isr |= ENISR_RDC;
+                ne2000_update_irq(s);
+            }
+            if (val & E8390_TRANS) {
+                index = (s->tpsr << 8);
+                /* XXX: next 2 lines are a hack to make netware 3.11 work */
+                if (index >= NE2000_PMEM_END)
+                    index -= NE2000_PMEM_SIZE;
+                /* fail safe: check range on the transmitted length  */
+                if (index + s->tcnt <= NE2000_PMEM_END) {
+                    qemu_send_packet(qemu_get_queue(s->nic), s->mem + index,
+                                     s->tcnt);
+                }
+                /* signal end of transfer */
+                s->tsr = ENTSR_PTX;
+                s->isr |= ENISR_TX;
+                s->cmd &= ~E8390_TRANS;
+                ne2000_update_irq(s);
+            }
+        }
+    } else {
+        page = s->cmd >> 6;
+        offset = addr | (page << 4);
+        switch(offset) {
+        case EN0_STARTPG:
+            s->start = val << 8;
+            break;
+        case EN0_STOPPG:
+            s->stop = val << 8;
+            break;
+        case EN0_BOUNDARY:
+            s->boundary = val;
+            break;
+        case EN0_IMR:
+            s->imr = val;
+            ne2000_update_irq(s);
+            break;
+        case EN0_TPSR:
+            s->tpsr = val;
+            break;
+        case EN0_TCNTLO:
+            s->tcnt = (s->tcnt & 0xff00) | val;
+            break;
+        case EN0_TCNTHI:
+            s->tcnt = (s->tcnt & 0x00ff) | (val << 8);
+            break;
+        case EN0_RSARLO:
+            s->rsar = (s->rsar & 0xff00) | val;
+            break;
+        case EN0_RSARHI:
+            s->rsar = (s->rsar & 0x00ff) | (val << 8);
+            break;
+        case EN0_RCNTLO:
+            s->rcnt = (s->rcnt & 0xff00) | val;
+            break;
+        case EN0_RCNTHI:
+            s->rcnt = (s->rcnt & 0x00ff) | (val << 8);
+            break;
+        case EN0_RXCR:
+            s->rxcr = val;
+            break;
+        case EN0_DCFG:
+            s->dcfg = val;
+            break;
+        case EN0_ISR:
+            s->isr &= ~(val & 0x7f);
+            ne2000_update_irq(s);
+            break;
+        case EN1_PHYS ... EN1_PHYS + 5:
+            s->phys[offset - EN1_PHYS] = val;
+            break;
+        case EN1_CURPAG:
+            s->curpag = val;
+            break;
+        case EN1_MULT ... EN1_MULT + 7:
+            s->mult[offset - EN1_MULT] = val;
+            break;
+        }
+    }
+}
+
+static uint32_t ne2000_ioport_read(void *opaque, uint32_t addr)
+{
+    NE2000State *s = opaque;
+    int offset, page, ret;
+
+    addr &= 0xf;
+    if (addr == E8390_CMD) {
+        ret = s->cmd;
+    } else {
+        page = s->cmd >> 6;
+        offset = addr | (page << 4);
+        switch(offset) {
+        case EN0_TSR:
+            ret = s->tsr;
+            break;
+        case EN0_BOUNDARY:
+            ret = s->boundary;
+            break;
+        case EN0_ISR:
+            ret = s->isr;
+            break;
+       case EN0_RSARLO:
+           ret = s->rsar & 0x00ff;
+           break;
+       case EN0_RSARHI:
+           ret = s->rsar >> 8;
+           break;
+        case EN1_PHYS ... EN1_PHYS + 5:
+            ret = s->phys[offset - EN1_PHYS];
+            break;
+        case EN1_CURPAG:
+            ret = s->curpag;
+            break;
+        case EN1_MULT ... EN1_MULT + 7:
+            ret = s->mult[offset - EN1_MULT];
+            break;
+        case EN0_RSR:
+            ret = s->rsr;
+            break;
+        case EN2_STARTPG:
+            ret = s->start >> 8;
+            break;
+        case EN2_STOPPG:
+            ret = s->stop >> 8;
+            break;
+       case EN0_RTL8029ID0:
+           ret = 0x50;
+           break;
+       case EN0_RTL8029ID1:
+           ret = 0x43;
+           break;
+       case EN3_CONFIG0:
+           ret = 0;            /* 10baseT media */
+           break;
+       case EN3_CONFIG2:
+           ret = 0x40;         /* 10baseT active */
+           break;
+       case EN3_CONFIG3:
+           ret = 0x40;         /* Full duplex */
+           break;
+        default:
+            ret = 0x00;
+            break;
+        }
+    }
+#ifdef DEBUG_NE2000
+    printf("NE2000: read addr=0x%x val=%02x\n", addr, ret);
+#endif
+    return ret;
+}
+
+static inline void ne2000_mem_writeb(NE2000State *s, uint32_t addr,
+                                     uint32_t val)
+{
+    if (addr < 32 ||
+        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+        s->mem[addr] = val;
+    }
+}
+
+static inline void ne2000_mem_writew(NE2000State *s, uint32_t addr,
+                                     uint32_t val)
+{
+    addr &= ~1; /* XXX: check exact behaviour if not even */
+    if (addr < 32 ||
+        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+        *(uint16_t *)(s->mem + addr) = cpu_to_le16(val);
+    }
+}
+
+static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr,
+                                     uint32_t val)
+{
+    addr &= ~1; /* XXX: check exact behaviour if not even */
+    if (addr < 32 ||
+        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+        cpu_to_le32wu((uint32_t *)(s->mem + addr), val);
+    }
+}
+
+static inline uint32_t ne2000_mem_readb(NE2000State *s, uint32_t addr)
+{
+    if (addr < 32 ||
+        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+        return s->mem[addr];
+    } else {
+        return 0xff;
+    }
+}
+
+static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr)
+{
+    addr &= ~1; /* XXX: check exact behaviour if not even */
+    if (addr < 32 ||
+        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+        return le16_to_cpu(*(uint16_t *)(s->mem + addr));
+    } else {
+        return 0xffff;
+    }
+}
+
+static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr)
+{
+    addr &= ~1; /* XXX: check exact behaviour if not even */
+    if (addr < 32 ||
+        (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) {
+        return le32_to_cpupu((uint32_t *)(s->mem + addr));
+    } else {
+        return 0xffffffff;
+    }
+}
+
+static inline void ne2000_dma_update(NE2000State *s, int len)
+{
+    s->rsar += len;
+    /* wrap */
+    /* XXX: check what to do if rsar > stop */
+    if (s->rsar == s->stop)
+        s->rsar = s->start;
+
+    if (s->rcnt <= len) {
+        s->rcnt = 0;
+        /* signal end of transfer */
+        s->isr |= ENISR_RDC;
+        ne2000_update_irq(s);
+    } else {
+        s->rcnt -= len;
+    }
+}
+
+static void ne2000_asic_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    NE2000State *s = opaque;
+
+#ifdef DEBUG_NE2000
+    printf("NE2000: asic write val=0x%04x\n", val);
+#endif
+    if (s->rcnt == 0)
+        return;
+    if (s->dcfg & 0x01) {
+        /* 16 bit access */
+        ne2000_mem_writew(s, s->rsar, val);
+        ne2000_dma_update(s, 2);
+    } else {
+        /* 8 bit access */
+        ne2000_mem_writeb(s, s->rsar, val);
+        ne2000_dma_update(s, 1);
+    }
+}
+
+static uint32_t ne2000_asic_ioport_read(void *opaque, uint32_t addr)
+{
+    NE2000State *s = opaque;
+    int ret;
+
+    if (s->dcfg & 0x01) {
+        /* 16 bit access */
+        ret = ne2000_mem_readw(s, s->rsar);
+        ne2000_dma_update(s, 2);
+    } else {
+        /* 8 bit access */
+        ret = ne2000_mem_readb(s, s->rsar);
+        ne2000_dma_update(s, 1);
+    }
+#ifdef DEBUG_NE2000
+    printf("NE2000: asic read val=0x%04x\n", ret);
+#endif
+    return ret;
+}
+
+static void ne2000_asic_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+    NE2000State *s = opaque;
+
+#ifdef DEBUG_NE2000
+    printf("NE2000: asic writel val=0x%04x\n", val);
+#endif
+    if (s->rcnt == 0)
+        return;
+    /* 32 bit access */
+    ne2000_mem_writel(s, s->rsar, val);
+    ne2000_dma_update(s, 4);
+}
+
+static uint32_t ne2000_asic_ioport_readl(void *opaque, uint32_t addr)
+{
+    NE2000State *s = opaque;
+    int ret;
+
+    /* 32 bit access */
+    ret = ne2000_mem_readl(s, s->rsar);
+    ne2000_dma_update(s, 4);
+#ifdef DEBUG_NE2000
+    printf("NE2000: asic readl val=0x%04x\n", ret);
+#endif
+    return ret;
+}
+
+static void ne2000_reset_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    /* nothing to do (end of reset pulse) */
+}
+
+static uint32_t ne2000_reset_ioport_read(void *opaque, uint32_t addr)
+{
+    NE2000State *s = opaque;
+    ne2000_reset(s);
+    return 0;
+}
+
+static int ne2000_post_load(void* opaque, int version_id)
+{
+    NE2000State* s = opaque;
+
+    if (version_id < 2) {
+        s->rxcr = 0x0c;
+    }
+    return 0;
+}
+
+const VMStateDescription vmstate_ne2000 = {
+    .name = "ne2000",
+    .version_id = 2,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .post_load = ne2000_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT8_V(rxcr, NE2000State, 2),
+        VMSTATE_UINT8(cmd, NE2000State),
+        VMSTATE_UINT32(start, NE2000State),
+        VMSTATE_UINT32(stop, NE2000State),
+        VMSTATE_UINT8(boundary, NE2000State),
+        VMSTATE_UINT8(tsr, NE2000State),
+        VMSTATE_UINT8(tpsr, NE2000State),
+        VMSTATE_UINT16(tcnt, NE2000State),
+        VMSTATE_UINT16(rcnt, NE2000State),
+        VMSTATE_UINT32(rsar, NE2000State),
+        VMSTATE_UINT8(rsr, NE2000State),
+        VMSTATE_UINT8(isr, NE2000State),
+        VMSTATE_UINT8(dcfg, NE2000State),
+        VMSTATE_UINT8(imr, NE2000State),
+        VMSTATE_BUFFER(phys, NE2000State),
+        VMSTATE_UINT8(curpag, NE2000State),
+        VMSTATE_BUFFER(mult, NE2000State),
+        VMSTATE_UNUSED(4), /* was irq */
+        VMSTATE_BUFFER(mem, NE2000State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_pci_ne2000 = {
+    .name = "ne2000",
+    .version_id = 3,
+    .minimum_version_id = 3,
+    .minimum_version_id_old = 3,
+    .fields      = (VMStateField []) {
+        VMSTATE_PCI_DEVICE(dev, PCINE2000State),
+        VMSTATE_STRUCT(ne2000, PCINE2000State, 0, vmstate_ne2000, NE2000State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static uint64_t ne2000_read(void *opaque, hwaddr addr,
+                            unsigned size)
+{
+    NE2000State *s = opaque;
+
+    if (addr < 0x10 && size == 1) {
+        return ne2000_ioport_read(s, addr);
+    } else if (addr == 0x10) {
+        if (size <= 2) {
+            return ne2000_asic_ioport_read(s, addr);
+        } else {
+            return ne2000_asic_ioport_readl(s, addr);
+        }
+    } else if (addr == 0x1f && size == 1) {
+        return ne2000_reset_ioport_read(s, addr);
+    }
+    return ((uint64_t)1 << (size * 8)) - 1;
+}
+
+static void ne2000_write(void *opaque, hwaddr addr,
+                         uint64_t data, unsigned size)
+{
+    NE2000State *s = opaque;
+
+    if (addr < 0x10 && size == 1) {
+        ne2000_ioport_write(s, addr, data);
+    } else if (addr == 0x10) {
+        if (size <= 2) {
+            ne2000_asic_ioport_write(s, addr, data);
+        } else {
+            ne2000_asic_ioport_writel(s, addr, data);
+        }
+    } else if (addr == 0x1f && size == 1) {
+        ne2000_reset_ioport_write(s, addr, data);
+    }
+}
+
+static const MemoryRegionOps ne2000_ops = {
+    .read = ne2000_read,
+    .write = ne2000_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+/***********************************************************/
+/* PCI NE2000 definitions */
+
+void ne2000_setup_io(NE2000State *s, unsigned size)
+{
+    memory_region_init_io(&s->io, &ne2000_ops, s, "ne2000", size);
+}
+
+static void ne2000_cleanup(NetClientState *nc)
+{
+    NE2000State *s = qemu_get_nic_opaque(nc);
+
+    s->nic = NULL;
+}
+
+static NetClientInfo net_ne2000_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = ne2000_can_receive,
+    .receive = ne2000_receive,
+    .cleanup = ne2000_cleanup,
+};
+
+static int pci_ne2000_init(PCIDevice *pci_dev)
+{
+    PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev);
+    NE2000State *s;
+    uint8_t *pci_conf;
+
+    pci_conf = d->dev.config;
+    pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */
+
+    s = &d->ne2000;
+    ne2000_setup_io(s, 0x100);
+    pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io);
+    s->irq = d->dev.irq[0];
+
+    qemu_macaddr_default_if_unset(&s->c.macaddr);
+    ne2000_reset(s);
+
+    s->nic = qemu_new_nic(&net_ne2000_info, &s->c,
+                          object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s);
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a);
+
+    add_boot_device_path(s->c.bootindex, &pci_dev->qdev, "/ethernet-phy@0");
+
+    return 0;
+}
+
+static void pci_ne2000_exit(PCIDevice *pci_dev)
+{
+    PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev);
+    NE2000State *s = &d->ne2000;
+
+    memory_region_destroy(&s->io);
+    qemu_del_nic(s->nic);
+}
+
+static Property ne2000_properties[] = {
+    DEFINE_NIC_PROPERTIES(PCINE2000State, ne2000.c),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ne2000_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = pci_ne2000_init;
+    k->exit = pci_ne2000_exit;
+    k->romfile = "efi-ne2k_pci.rom",
+    k->vendor_id = PCI_VENDOR_ID_REALTEK;
+    k->device_id = PCI_DEVICE_ID_REALTEK_8029;
+    k->class_id = PCI_CLASS_NETWORK_ETHERNET;
+    dc->vmsd = &vmstate_pci_ne2000;
+    dc->props = ne2000_properties;
+}
+
+static const TypeInfo ne2000_info = {
+    .name          = "ne2k_pci",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCINE2000State),
+    .class_init    = ne2000_class_init,
+};
+
+static void ne2000_register_types(void)
+{
+    type_register_static(&ne2000_info);
+}
+
+type_init(ne2000_register_types)
diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c
new file mode 100644 (file)
index 0000000..be64bf2
--- /dev/null
@@ -0,0 +1,733 @@
+/*
+ * OpenCores Ethernet MAC 10/100 + subset of
+ * National Semiconductors DP83848C 10/100 PHY
+ *
+ * http://opencores.org/svnget,ethmac?file=%2Ftrunk%2F%2Fdoc%2Feth_speci.pdf
+ * http://cache.national.com/ds/DP/DP83848C.pdf
+ *
+ * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the Open Source and Linux Lab nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "net/net.h"
+#include "sysemu/sysemu.h"
+#include "trace.h"
+
+/* RECSMALL is not used because it breaks tap networking in linux:
+ * incoming ARP responses are too short
+ */
+#undef USE_RECSMALL
+
+#define GET_FIELD(v, field) (((v) & (field)) >> (field ## _LBN))
+#define GET_REGBIT(s, reg, field) ((s)->regs[reg] & (reg ## _ ## field))
+#define GET_REGFIELD(s, reg, field) \
+    GET_FIELD((s)->regs[reg], reg ## _ ## field)
+
+#define SET_FIELD(v, field, data) \
+    ((v) = (((v) & ~(field)) | (((data) << (field ## _LBN)) & (field))))
+#define SET_REGFIELD(s, reg, field, data) \
+    SET_FIELD((s)->regs[reg], reg ## _ ## field, data)
+
+/* PHY MII registers */
+enum {
+    MII_BMCR,
+    MII_BMSR,
+    MII_PHYIDR1,
+    MII_PHYIDR2,
+    MII_ANAR,
+    MII_ANLPAR,
+    MII_REG_MAX = 16,
+};
+
+typedef struct Mii {
+    uint16_t regs[MII_REG_MAX];
+    bool link_ok;
+} Mii;
+
+static void mii_set_link(Mii *s, bool link_ok)
+{
+    if (link_ok) {
+        s->regs[MII_BMSR] |= 0x4;
+        s->regs[MII_ANLPAR] |= 0x01e1;
+    } else {
+        s->regs[MII_BMSR] &= ~0x4;
+        s->regs[MII_ANLPAR] &= 0x01ff;
+    }
+    s->link_ok = link_ok;
+}
+
+static void mii_reset(Mii *s)
+{
+    memset(s->regs, 0, sizeof(s->regs));
+    s->regs[MII_BMCR] = 0x1000;
+    s->regs[MII_BMSR] = 0x7848; /* no ext regs */
+    s->regs[MII_PHYIDR1] = 0x2000;
+    s->regs[MII_PHYIDR2] = 0x5c90;
+    s->regs[MII_ANAR] = 0x01e1;
+    mii_set_link(s, s->link_ok);
+}
+
+static void mii_ro(Mii *s, uint16_t v)
+{
+}
+
+static void mii_write_bmcr(Mii *s, uint16_t v)
+{
+    if (v & 0x8000) {
+        mii_reset(s);
+    } else {
+        s->regs[MII_BMCR] = v;
+    }
+}
+
+static void mii_write_host(Mii *s, unsigned idx, uint16_t v)
+{
+    static void (*reg_write[MII_REG_MAX])(Mii *s, uint16_t v) = {
+        [MII_BMCR] = mii_write_bmcr,
+        [MII_BMSR] = mii_ro,
+        [MII_PHYIDR1] = mii_ro,
+        [MII_PHYIDR2] = mii_ro,
+    };
+
+    if (idx < MII_REG_MAX) {
+        trace_open_eth_mii_write(idx, v);
+        if (reg_write[idx]) {
+            reg_write[idx](s, v);
+        } else {
+            s->regs[idx] = v;
+        }
+    }
+}
+
+static uint16_t mii_read_host(Mii *s, unsigned idx)
+{
+    trace_open_eth_mii_read(idx, s->regs[idx]);
+    return s->regs[idx];
+}
+
+/* OpenCores Ethernet registers */
+enum {
+    MODER,
+    INT_SOURCE,
+    INT_MASK,
+    IPGT,
+    IPGR1,
+    IPGR2,
+    PACKETLEN,
+    COLLCONF,
+    TX_BD_NUM,
+    CTRLMODER,
+    MIIMODER,
+    MIICOMMAND,
+    MIIADDRESS,
+    MIITX_DATA,
+    MIIRX_DATA,
+    MIISTATUS,
+    MAC_ADDR0,
+    MAC_ADDR1,
+    HASH0,
+    HASH1,
+    TXCTRL,
+    REG_MAX,
+};
+
+enum {
+    MODER_RECSMALL = 0x10000,
+    MODER_PAD = 0x8000,
+    MODER_HUGEN = 0x4000,
+    MODER_RST = 0x800,
+    MODER_LOOPBCK = 0x80,
+    MODER_PRO = 0x20,
+    MODER_IAM = 0x10,
+    MODER_BRO = 0x8,
+    MODER_TXEN = 0x2,
+    MODER_RXEN = 0x1,
+};
+
+enum {
+    INT_SOURCE_RXB = 0x4,
+    INT_SOURCE_TXB = 0x1,
+};
+
+enum {
+    PACKETLEN_MINFL = 0xffff0000,
+    PACKETLEN_MINFL_LBN = 16,
+    PACKETLEN_MAXFL = 0xffff,
+    PACKETLEN_MAXFL_LBN = 0,
+};
+
+enum {
+    MIICOMMAND_WCTRLDATA = 0x4,
+    MIICOMMAND_RSTAT = 0x2,
+    MIICOMMAND_SCANSTAT = 0x1,
+};
+
+enum {
+    MIIADDRESS_RGAD = 0x1f00,
+    MIIADDRESS_RGAD_LBN = 8,
+    MIIADDRESS_FIAD = 0x1f,
+    MIIADDRESS_FIAD_LBN = 0,
+};
+
+enum {
+    MIITX_DATA_CTRLDATA = 0xffff,
+    MIITX_DATA_CTRLDATA_LBN = 0,
+};
+
+enum {
+    MIIRX_DATA_PRSD = 0xffff,
+    MIIRX_DATA_PRSD_LBN = 0,
+};
+
+enum {
+    MIISTATUS_LINKFAIL = 0x1,
+    MIISTATUS_LINKFAIL_LBN = 0,
+};
+
+enum {
+    MAC_ADDR0_BYTE2 = 0xff000000,
+    MAC_ADDR0_BYTE2_LBN = 24,
+    MAC_ADDR0_BYTE3 = 0xff0000,
+    MAC_ADDR0_BYTE3_LBN = 16,
+    MAC_ADDR0_BYTE4 = 0xff00,
+    MAC_ADDR0_BYTE4_LBN = 8,
+    MAC_ADDR0_BYTE5 = 0xff,
+    MAC_ADDR0_BYTE5_LBN = 0,
+};
+
+enum {
+    MAC_ADDR1_BYTE0 = 0xff00,
+    MAC_ADDR1_BYTE0_LBN = 8,
+    MAC_ADDR1_BYTE1 = 0xff,
+    MAC_ADDR1_BYTE1_LBN = 0,
+};
+
+enum {
+    TXD_LEN = 0xffff0000,
+    TXD_LEN_LBN = 16,
+    TXD_RD = 0x8000,
+    TXD_IRQ = 0x4000,
+    TXD_WR = 0x2000,
+    TXD_PAD = 0x1000,
+    TXD_CRC = 0x800,
+    TXD_UR = 0x100,
+    TXD_RTRY = 0xf0,
+    TXD_RTRY_LBN = 4,
+    TXD_RL = 0x8,
+    TXD_LC = 0x4,
+    TXD_DF = 0x2,
+    TXD_CS = 0x1,
+};
+
+enum {
+    RXD_LEN = 0xffff0000,
+    RXD_LEN_LBN = 16,
+    RXD_E = 0x8000,
+    RXD_IRQ = 0x4000,
+    RXD_WRAP = 0x2000,
+    RXD_CF = 0x100,
+    RXD_M = 0x80,
+    RXD_OR = 0x40,
+    RXD_IS = 0x20,
+    RXD_DN = 0x10,
+    RXD_TL = 0x8,
+    RXD_SF = 0x4,
+    RXD_CRC = 0x2,
+    RXD_LC = 0x1,
+};
+
+typedef struct desc {
+    uint32_t len_flags;
+    uint32_t buf_ptr;
+} desc;
+
+#define DEFAULT_PHY 1
+
+typedef struct OpenEthState {
+    SysBusDevice dev;
+    NICState *nic;
+    NICConf conf;
+    MemoryRegion reg_io;
+    MemoryRegion desc_io;
+    qemu_irq irq;
+
+    Mii mii;
+    uint32_t regs[REG_MAX];
+    unsigned tx_desc;
+    unsigned rx_desc;
+    desc desc[128];
+} OpenEthState;
+
+static desc *rx_desc(OpenEthState *s)
+{
+    return s->desc + s->rx_desc;
+}
+
+static desc *tx_desc(OpenEthState *s)
+{
+    return s->desc + s->tx_desc;
+}
+
+static void open_eth_update_irq(OpenEthState *s,
+        uint32_t old, uint32_t new)
+{
+    if (!old != !new) {
+        trace_open_eth_update_irq(new);
+        qemu_set_irq(s->irq, new);
+    }
+}
+
+static void open_eth_int_source_write(OpenEthState *s,
+        uint32_t val)
+{
+    uint32_t old_val = s->regs[INT_SOURCE];
+
+    s->regs[INT_SOURCE] = val;
+    open_eth_update_irq(s, old_val & s->regs[INT_MASK],
+            s->regs[INT_SOURCE] & s->regs[INT_MASK]);
+}
+
+static void open_eth_set_link_status(NetClientState *nc)
+{
+    OpenEthState *s = qemu_get_nic_opaque(nc);
+
+    if (GET_REGBIT(s, MIICOMMAND, SCANSTAT)) {
+        SET_REGFIELD(s, MIISTATUS, LINKFAIL, nc->link_down);
+    }
+    mii_set_link(&s->mii, !nc->link_down);
+}
+
+static void open_eth_reset(void *opaque)
+{
+    OpenEthState *s = opaque;
+
+    memset(s->regs, 0, sizeof(s->regs));
+    s->regs[MODER] = 0xa000;
+    s->regs[IPGT] = 0x12;
+    s->regs[IPGR1] = 0xc;
+    s->regs[IPGR2] = 0x12;
+    s->regs[PACKETLEN] = 0x400600;
+    s->regs[COLLCONF] = 0xf003f;
+    s->regs[TX_BD_NUM] = 0x40;
+    s->regs[MIIMODER] = 0x64;
+
+    s->tx_desc = 0;
+    s->rx_desc = 0x40;
+
+    mii_reset(&s->mii);
+    open_eth_set_link_status(qemu_get_queue(s->nic));
+}
+
+static int open_eth_can_receive(NetClientState *nc)
+{
+    OpenEthState *s = qemu_get_nic_opaque(nc);
+
+    return GET_REGBIT(s, MODER, RXEN) &&
+        (s->regs[TX_BD_NUM] < 0x80) &&
+        (rx_desc(s)->len_flags & RXD_E);
+}
+
+static ssize_t open_eth_receive(NetClientState *nc,
+        const uint8_t *buf, size_t size)
+{
+    OpenEthState *s = qemu_get_nic_opaque(nc);
+    size_t maxfl = GET_REGFIELD(s, PACKETLEN, MAXFL);
+    size_t minfl = GET_REGFIELD(s, PACKETLEN, MINFL);
+    size_t fcsl = 4;
+    bool miss = true;
+
+    trace_open_eth_receive((unsigned)size);
+
+    if (size >= 6) {
+        static const uint8_t bcast_addr[] = {
+            0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+        };
+        if (memcmp(buf, bcast_addr, sizeof(bcast_addr)) == 0) {
+            miss = GET_REGBIT(s, MODER, BRO);
+        } else if ((buf[0] & 0x1) || GET_REGBIT(s, MODER, IAM)) {
+            unsigned mcast_idx = compute_mcast_idx(buf);
+            miss = !(s->regs[HASH0 + mcast_idx / 32] &
+                    (1 << (mcast_idx % 32)));
+            trace_open_eth_receive_mcast(
+                    mcast_idx, s->regs[HASH0], s->regs[HASH1]);
+        } else {
+            miss = GET_REGFIELD(s, MAC_ADDR1, BYTE0) != buf[0] ||
+                GET_REGFIELD(s, MAC_ADDR1, BYTE1) != buf[1] ||
+                GET_REGFIELD(s, MAC_ADDR0, BYTE2) != buf[2] ||
+                GET_REGFIELD(s, MAC_ADDR0, BYTE3) != buf[3] ||
+                GET_REGFIELD(s, MAC_ADDR0, BYTE4) != buf[4] ||
+                GET_REGFIELD(s, MAC_ADDR0, BYTE5) != buf[5];
+        }
+    }
+
+    if (miss && !GET_REGBIT(s, MODER, PRO)) {
+        trace_open_eth_receive_reject();
+        return size;
+    }
+
+#ifdef USE_RECSMALL
+    if (GET_REGBIT(s, MODER, RECSMALL) || size >= minfl) {
+#else
+    {
+#endif
+        static const uint8_t zero[64] = {0};
+        desc *desc = rx_desc(s);
+        size_t copy_size = GET_REGBIT(s, MODER, HUGEN) ? 65536 : maxfl;
+
+        desc->len_flags &= ~(RXD_CF | RXD_M | RXD_OR |
+                RXD_IS | RXD_DN | RXD_TL | RXD_SF | RXD_CRC | RXD_LC);
+
+        if (copy_size > size) {
+            copy_size = size;
+        } else {
+            fcsl = 0;
+        }
+        if (miss) {
+            desc->len_flags |= RXD_M;
+        }
+        if (GET_REGBIT(s, MODER, HUGEN) && size > maxfl) {
+            desc->len_flags |= RXD_TL;
+        }
+#ifdef USE_RECSMALL
+        if (size < minfl) {
+            desc->len_flags |= RXD_SF;
+        }
+#endif
+
+        cpu_physical_memory_write(desc->buf_ptr, buf, copy_size);
+
+        if (GET_REGBIT(s, MODER, PAD) && copy_size < minfl) {
+            if (minfl - copy_size > fcsl) {
+                fcsl = 0;
+            } else {
+                fcsl -= minfl - copy_size;
+            }
+            while (copy_size < minfl) {
+                size_t zero_sz = minfl - copy_size < sizeof(zero) ?
+                    minfl - copy_size : sizeof(zero);
+
+                cpu_physical_memory_write(desc->buf_ptr + copy_size,
+                        zero, zero_sz);
+                copy_size += zero_sz;
+            }
+        }
+
+        /* There's no FCS in the frames handed to us by the QEMU, zero fill it.
+         * Don't do it if the frame is cut at the MAXFL or padded with 4 or
+         * more bytes to the MINFL.
+         */
+        cpu_physical_memory_write(desc->buf_ptr + copy_size, zero, fcsl);
+        copy_size += fcsl;
+
+        SET_FIELD(desc->len_flags, RXD_LEN, copy_size);
+
+        if ((desc->len_flags & RXD_WRAP) || s->rx_desc == 0x7f) {
+            s->rx_desc = s->regs[TX_BD_NUM];
+        } else {
+            ++s->rx_desc;
+        }
+        desc->len_flags &= ~RXD_E;
+
+        trace_open_eth_receive_desc(desc->buf_ptr, desc->len_flags);
+
+        if (desc->len_flags & RXD_IRQ) {
+            open_eth_int_source_write(s,
+                    s->regs[INT_SOURCE] | INT_SOURCE_RXB);
+        }
+    }
+    return size;
+}
+
+static void open_eth_cleanup(NetClientState *nc)
+{
+}
+
+static NetClientInfo net_open_eth_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = open_eth_can_receive,
+    .receive = open_eth_receive,
+    .cleanup = open_eth_cleanup,
+    .link_status_changed = open_eth_set_link_status,
+};
+
+static void open_eth_start_xmit(OpenEthState *s, desc *tx)
+{
+    uint8_t buf[65536];
+    unsigned len = GET_FIELD(tx->len_flags, TXD_LEN);
+    unsigned tx_len = len;
+
+    if ((tx->len_flags & TXD_PAD) &&
+            tx_len < GET_REGFIELD(s, PACKETLEN, MINFL)) {
+        tx_len = GET_REGFIELD(s, PACKETLEN, MINFL);
+    }
+    if (!GET_REGBIT(s, MODER, HUGEN) &&
+            tx_len > GET_REGFIELD(s, PACKETLEN, MAXFL)) {
+        tx_len = GET_REGFIELD(s, PACKETLEN, MAXFL);
+    }
+
+    trace_open_eth_start_xmit(tx->buf_ptr, len, tx_len);
+
+    if (len > tx_len) {
+        len = tx_len;
+    }
+    cpu_physical_memory_read(tx->buf_ptr, buf, len);
+    if (tx_len > len) {
+        memset(buf + len, 0, tx_len - len);
+    }
+    qemu_send_packet(qemu_get_queue(s->nic), buf, tx_len);
+
+    if (tx->len_flags & TXD_WR) {
+        s->tx_desc = 0;
+    } else {
+        ++s->tx_desc;
+        if (s->tx_desc >= s->regs[TX_BD_NUM]) {
+            s->tx_desc = 0;
+        }
+    }
+    tx->len_flags &= ~(TXD_RD | TXD_UR |
+            TXD_RTRY | TXD_RL | TXD_LC | TXD_DF | TXD_CS);
+    if (tx->len_flags & TXD_IRQ) {
+        open_eth_int_source_write(s, s->regs[INT_SOURCE] | INT_SOURCE_TXB);
+    }
+
+}
+
+static void open_eth_check_start_xmit(OpenEthState *s)
+{
+    desc *tx = tx_desc(s);
+    if (GET_REGBIT(s, MODER, TXEN) && s->regs[TX_BD_NUM] > 0 &&
+            (tx->len_flags & TXD_RD) &&
+            GET_FIELD(tx->len_flags, TXD_LEN) > 4) {
+        open_eth_start_xmit(s, tx);
+    }
+}
+
+static uint64_t open_eth_reg_read(void *opaque,
+        hwaddr addr, unsigned int size)
+{
+    static uint32_t (*reg_read[REG_MAX])(OpenEthState *s) = {
+    };
+    OpenEthState *s = opaque;
+    unsigned idx = addr / 4;
+    uint64_t v = 0;
+
+    if (idx < REG_MAX) {
+        if (reg_read[idx]) {
+            v = reg_read[idx](s);
+        } else {
+            v = s->regs[idx];
+        }
+    }
+    trace_open_eth_reg_read((uint32_t)addr, (uint32_t)v);
+    return v;
+}
+
+static void open_eth_ro(OpenEthState *s, uint32_t val)
+{
+}
+
+static void open_eth_moder_host_write(OpenEthState *s, uint32_t val)
+{
+    uint32_t set = val & ~s->regs[MODER];
+
+    if (set & MODER_RST) {
+        open_eth_reset(s);
+    }
+
+    s->regs[MODER] = val;
+
+    if (set & MODER_RXEN) {
+        s->rx_desc = s->regs[TX_BD_NUM];
+    }
+    if (set & MODER_TXEN) {
+        s->tx_desc = 0;
+        open_eth_check_start_xmit(s);
+    }
+}
+
+static void open_eth_int_source_host_write(OpenEthState *s, uint32_t val)
+{
+    uint32_t old = s->regs[INT_SOURCE];
+
+    s->regs[INT_SOURCE] &= ~val;
+    open_eth_update_irq(s, old & s->regs[INT_MASK],
+            s->regs[INT_SOURCE] & s->regs[INT_MASK]);
+}
+
+static void open_eth_int_mask_host_write(OpenEthState *s, uint32_t val)
+{
+    uint32_t old = s->regs[INT_MASK];
+
+    s->regs[INT_MASK] = val;
+    open_eth_update_irq(s, s->regs[INT_SOURCE] & old,
+            s->regs[INT_SOURCE] & s->regs[INT_MASK]);
+}
+
+static void open_eth_mii_command_host_write(OpenEthState *s, uint32_t val)
+{
+    unsigned fiad = GET_REGFIELD(s, MIIADDRESS, FIAD);
+    unsigned rgad = GET_REGFIELD(s, MIIADDRESS, RGAD);
+
+    if (val & MIICOMMAND_WCTRLDATA) {
+        if (fiad == DEFAULT_PHY) {
+            mii_write_host(&s->mii, rgad,
+                    GET_REGFIELD(s, MIITX_DATA, CTRLDATA));
+        }
+    }
+    if (val & MIICOMMAND_RSTAT) {
+        if (fiad == DEFAULT_PHY) {
+            SET_REGFIELD(s, MIIRX_DATA, PRSD,
+                    mii_read_host(&s->mii, rgad));
+        } else {
+            s->regs[MIIRX_DATA] = 0xffff;
+        }
+        SET_REGFIELD(s, MIISTATUS, LINKFAIL, qemu_get_queue(s->nic)->link_down);
+    }
+}
+
+static void open_eth_mii_tx_host_write(OpenEthState *s, uint32_t val)
+{
+    SET_REGFIELD(s, MIITX_DATA, CTRLDATA, val);
+    if (GET_REGFIELD(s, MIIADDRESS, FIAD) == DEFAULT_PHY) {
+        mii_write_host(&s->mii, GET_REGFIELD(s, MIIADDRESS, RGAD),
+                GET_REGFIELD(s, MIITX_DATA, CTRLDATA));
+    }
+}
+
+static void open_eth_reg_write(void *opaque,
+        hwaddr addr, uint64_t val, unsigned int size)
+{
+    static void (*reg_write[REG_MAX])(OpenEthState *s, uint32_t val) = {
+        [MODER] = open_eth_moder_host_write,
+        [INT_SOURCE] = open_eth_int_source_host_write,
+        [INT_MASK] = open_eth_int_mask_host_write,
+        [MIICOMMAND] = open_eth_mii_command_host_write,
+        [MIITX_DATA] = open_eth_mii_tx_host_write,
+        [MIISTATUS] = open_eth_ro,
+    };
+    OpenEthState *s = opaque;
+    unsigned idx = addr / 4;
+
+    if (idx < REG_MAX) {
+        trace_open_eth_reg_write((uint32_t)addr, (uint32_t)val);
+        if (reg_write[idx]) {
+            reg_write[idx](s, val);
+        } else {
+            s->regs[idx] = val;
+        }
+    }
+}
+
+static uint64_t open_eth_desc_read(void *opaque,
+        hwaddr addr, unsigned int size)
+{
+    OpenEthState *s = opaque;
+    uint64_t v = 0;
+
+    addr &= 0x3ff;
+    memcpy(&v, (uint8_t *)s->desc + addr, size);
+    trace_open_eth_desc_read((uint32_t)addr, (uint32_t)v);
+    return v;
+}
+
+static void open_eth_desc_write(void *opaque,
+        hwaddr addr, uint64_t val, unsigned int size)
+{
+    OpenEthState *s = opaque;
+
+    addr &= 0x3ff;
+    trace_open_eth_desc_write((uint32_t)addr, (uint32_t)val);
+    memcpy((uint8_t *)s->desc + addr, &val, size);
+    open_eth_check_start_xmit(s);
+}
+
+
+static const MemoryRegionOps open_eth_reg_ops = {
+    .read = open_eth_reg_read,
+    .write = open_eth_reg_write,
+};
+
+static const MemoryRegionOps open_eth_desc_ops = {
+    .read = open_eth_desc_read,
+    .write = open_eth_desc_write,
+};
+
+static int sysbus_open_eth_init(SysBusDevice *dev)
+{
+    OpenEthState *s = DO_UPCAST(OpenEthState, dev, dev);
+
+    memory_region_init_io(&s->reg_io, &open_eth_reg_ops, s,
+            "open_eth.regs", 0x54);
+    sysbus_init_mmio(dev, &s->reg_io);
+
+    memory_region_init_io(&s->desc_io, &open_eth_desc_ops, s,
+            "open_eth.desc", 0x400);
+    sysbus_init_mmio(dev, &s->desc_io);
+
+    sysbus_init_irq(dev, &s->irq);
+
+    s->nic = qemu_new_nic(&net_open_eth_info, &s->conf,
+                          object_get_typename(OBJECT(s)), s->dev.qdev.id, s);
+    return 0;
+}
+
+static void qdev_open_eth_reset(DeviceState *dev)
+{
+    OpenEthState *d = DO_UPCAST(OpenEthState, dev.qdev, dev);
+    open_eth_reset(d);
+}
+
+static Property open_eth_properties[] = {
+    DEFINE_NIC_PROPERTIES(OpenEthState, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void open_eth_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = sysbus_open_eth_init;
+    dc->desc = "Opencores 10/100 Mbit Ethernet";
+    dc->reset = qdev_open_eth_reset;
+    dc->props = open_eth_properties;
+}
+
+static const TypeInfo open_eth_info = {
+    .name          = "open_eth",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(OpenEthState),
+    .class_init    = open_eth_class_init,
+};
+
+static void open_eth_register_types(void)
+{
+    type_register_static(&open_eth_info);
+}
+
+type_init(open_eth_register_types)
diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c
new file mode 100644 (file)
index 0000000..61af57e
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * QEMU AMD PC-Net II (Am79C970A) PCI emulation
+ *
+ * Copyright (c) 2004 Antony T Curtis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* This software was written to be compatible with the specification:
+ * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet
+ * AMD Publication# 19436  Rev:E  Amendment/0  Issue Date: June 2000
+ */
+
+#include "hw/pci/pci.h"
+#include "net/net.h"
+#include "hw/loader.h"
+#include "qemu/timer.h"
+#include "sysemu/dma.h"
+
+#include "hw/pcnet.h"
+
+//#define PCNET_DEBUG
+//#define PCNET_DEBUG_IO
+//#define PCNET_DEBUG_BCR
+//#define PCNET_DEBUG_CSR
+//#define PCNET_DEBUG_RMD
+//#define PCNET_DEBUG_TMD
+//#define PCNET_DEBUG_MATCH
+
+
+typedef struct {
+    PCIDevice pci_dev;
+    PCNetState state;
+    MemoryRegion io_bar;
+} PCIPCNetState;
+
+static void pcnet_aprom_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+    PCNetState *s = opaque;
+#ifdef PCNET_DEBUG
+    printf("pcnet_aprom_writeb addr=0x%08x val=0x%02x\n", addr, val);
+#endif
+    if (BCR_APROMWE(s)) {
+        s->prom[addr & 15] = val;
+    }
+}
+
+static uint32_t pcnet_aprom_readb(void *opaque, uint32_t addr)
+{
+    PCNetState *s = opaque;
+    uint32_t val = s->prom[addr & 15];
+#ifdef PCNET_DEBUG
+    printf("pcnet_aprom_readb addr=0x%08x val=0x%02x\n", addr, val);
+#endif
+    return val;
+}
+
+static uint64_t pcnet_ioport_read(void *opaque, hwaddr addr,
+                                  unsigned size)
+{
+    PCNetState *d = opaque;
+
+    if (addr < 0x10) {
+        if (!BCR_DWIO(d) && size == 1) {
+            return pcnet_aprom_readb(d, addr);
+        } else if (!BCR_DWIO(d) && (addr & 1) == 0 && size == 2) {
+            return pcnet_aprom_readb(d, addr) |
+                   (pcnet_aprom_readb(d, addr + 1) << 8);
+        } else if (BCR_DWIO(d) && (addr & 3) == 0 && size == 4) {
+            return pcnet_aprom_readb(d, addr) |
+                   (pcnet_aprom_readb(d, addr + 1) << 8) |
+                   (pcnet_aprom_readb(d, addr + 2) << 16) |
+                   (pcnet_aprom_readb(d, addr + 3) << 24);
+        }
+    } else {
+        if (size == 2) {
+            return pcnet_ioport_readw(d, addr);
+        } else if (size == 4) {
+            return pcnet_ioport_readl(d, addr);
+        }
+    }
+    return ((uint64_t)1 << (size * 8)) - 1;
+}
+
+static void pcnet_ioport_write(void *opaque, hwaddr addr,
+                               uint64_t data, unsigned size)
+{
+    PCNetState *d = opaque;
+
+    if (addr < 0x10) {
+        if (!BCR_DWIO(d) && size == 1) {
+            pcnet_aprom_writeb(d, addr, data);
+        } else if (!BCR_DWIO(d) && (addr & 1) == 0 && size == 2) {
+            pcnet_aprom_writeb(d, addr, data & 0xff);
+            pcnet_aprom_writeb(d, addr + 1, data >> 8);
+        } else if (BCR_DWIO(d) && (addr & 3) == 0 && size == 4) {
+            pcnet_aprom_writeb(d, addr, data & 0xff);
+            pcnet_aprom_writeb(d, addr + 1, (data >> 8) & 0xff);
+            pcnet_aprom_writeb(d, addr + 2, (data >> 16) & 0xff);
+            pcnet_aprom_writeb(d, addr + 3, data >> 24);
+        }
+    } else {
+        if (size == 2) {
+            pcnet_ioport_writew(d, addr, data);
+        } else if (size == 4) {
+            pcnet_ioport_writel(d, addr, data);
+        }
+    }
+}
+
+static const MemoryRegionOps pcnet_io_ops = {
+    .read = pcnet_ioport_read,
+    .write = pcnet_ioport_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void pcnet_mmio_writeb(void *opaque, hwaddr addr, uint32_t val)
+{
+    PCNetState *d = opaque;
+#ifdef PCNET_DEBUG_IO
+    printf("pcnet_mmio_writeb addr=0x" TARGET_FMT_plx" val=0x%02x\n", addr,
+           val);
+#endif
+    if (!(addr & 0x10))
+        pcnet_aprom_writeb(d, addr & 0x0f, val);
+}
+
+static uint32_t pcnet_mmio_readb(void *opaque, hwaddr addr)
+{
+    PCNetState *d = opaque;
+    uint32_t val = -1;
+    if (!(addr & 0x10))
+        val = pcnet_aprom_readb(d, addr & 0x0f);
+#ifdef PCNET_DEBUG_IO
+    printf("pcnet_mmio_readb addr=0x" TARGET_FMT_plx " val=0x%02x\n", addr,
+           val & 0xff);
+#endif
+    return val;
+}
+
+static void pcnet_mmio_writew(void *opaque, hwaddr addr, uint32_t val)
+{
+    PCNetState *d = opaque;
+#ifdef PCNET_DEBUG_IO
+    printf("pcnet_mmio_writew addr=0x" TARGET_FMT_plx " val=0x%04x\n", addr,
+           val);
+#endif
+    if (addr & 0x10)
+        pcnet_ioport_writew(d, addr & 0x0f, val);
+    else {
+        addr &= 0x0f;
+        pcnet_aprom_writeb(d, addr, val & 0xff);
+        pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8);
+    }
+}
+
+static uint32_t pcnet_mmio_readw(void *opaque, hwaddr addr)
+{
+    PCNetState *d = opaque;
+    uint32_t val = -1;
+    if (addr & 0x10)
+        val = pcnet_ioport_readw(d, addr & 0x0f);
+    else {
+        addr &= 0x0f;
+        val = pcnet_aprom_readb(d, addr+1);
+        val <<= 8;
+        val |= pcnet_aprom_readb(d, addr);
+    }
+#ifdef PCNET_DEBUG_IO
+    printf("pcnet_mmio_readw addr=0x" TARGET_FMT_plx" val = 0x%04x\n", addr,
+           val & 0xffff);
+#endif
+    return val;
+}
+
+static void pcnet_mmio_writel(void *opaque, hwaddr addr, uint32_t val)
+{
+    PCNetState *d = opaque;
+#ifdef PCNET_DEBUG_IO
+    printf("pcnet_mmio_writel addr=0x" TARGET_FMT_plx" val=0x%08x\n", addr,
+           val);
+#endif
+    if (addr & 0x10)
+        pcnet_ioport_writel(d, addr & 0x0f, val);
+    else {
+        addr &= 0x0f;
+        pcnet_aprom_writeb(d, addr, val & 0xff);
+        pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8);
+        pcnet_aprom_writeb(d, addr+2, (val & 0xff0000) >> 16);
+        pcnet_aprom_writeb(d, addr+3, (val & 0xff000000) >> 24);
+    }
+}
+
+static uint32_t pcnet_mmio_readl(void *opaque, hwaddr addr)
+{
+    PCNetState *d = opaque;
+    uint32_t val;
+    if (addr & 0x10)
+        val = pcnet_ioport_readl(d, addr & 0x0f);
+    else {
+        addr &= 0x0f;
+        val = pcnet_aprom_readb(d, addr+3);
+        val <<= 8;
+        val |= pcnet_aprom_readb(d, addr+2);
+        val <<= 8;
+        val |= pcnet_aprom_readb(d, addr+1);
+        val <<= 8;
+        val |= pcnet_aprom_readb(d, addr);
+    }
+#ifdef PCNET_DEBUG_IO
+    printf("pcnet_mmio_readl addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr,
+           val);
+#endif
+    return val;
+}
+
+static const VMStateDescription vmstate_pci_pcnet = {
+    .name = "pcnet",
+    .version_id = 3,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields      = (VMStateField []) {
+        VMSTATE_PCI_DEVICE(pci_dev, PCIPCNetState),
+        VMSTATE_STRUCT(state, PCIPCNetState, 0, vmstate_pcnet, PCNetState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/* PCI interface */
+
+static const MemoryRegionOps pcnet_mmio_ops = {
+    .old_mmio = {
+        .read = { pcnet_mmio_readb, pcnet_mmio_readw, pcnet_mmio_readl },
+        .write = { pcnet_mmio_writeb, pcnet_mmio_writew, pcnet_mmio_writel },
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void pci_physical_memory_write(void *dma_opaque, hwaddr addr,
+                                      uint8_t *buf, int len, int do_bswap)
+{
+    pci_dma_write(dma_opaque, addr, buf, len);
+}
+
+static void pci_physical_memory_read(void *dma_opaque, hwaddr addr,
+                                     uint8_t *buf, int len, int do_bswap)
+{
+    pci_dma_read(dma_opaque, addr, buf, len);
+}
+
+static void pci_pcnet_cleanup(NetClientState *nc)
+{
+    PCNetState *d = qemu_get_nic_opaque(nc);
+
+    pcnet_common_cleanup(d);
+}
+
+static void pci_pcnet_uninit(PCIDevice *dev)
+{
+    PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, dev);
+
+    memory_region_destroy(&d->state.mmio);
+    memory_region_destroy(&d->io_bar);
+    qemu_del_timer(d->state.poll_timer);
+    qemu_free_timer(d->state.poll_timer);
+    qemu_del_nic(d->state.nic);
+}
+
+static NetClientInfo net_pci_pcnet_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = pcnet_can_receive,
+    .receive = pcnet_receive,
+    .link_status_changed = pcnet_set_link_status,
+    .cleanup = pci_pcnet_cleanup,
+};
+
+static int pci_pcnet_init(PCIDevice *pci_dev)
+{
+    PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, pci_dev);
+    PCNetState *s = &d->state;
+    uint8_t *pci_conf;
+
+#if 0
+    printf("sizeof(RMD)=%d, sizeof(TMD)=%d\n",
+        sizeof(struct pcnet_RMD), sizeof(struct pcnet_TMD));
+#endif
+
+    pci_conf = pci_dev->config;
+
+    pci_set_word(pci_conf + PCI_STATUS,
+                 PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM);
+
+    pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, 0x0);
+    pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, 0x0);
+
+    pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */
+    pci_conf[PCI_MIN_GNT] = 0x06;
+    pci_conf[PCI_MAX_LAT] = 0xff;
+
+    /* Handler for memory-mapped I/O */
+    memory_region_init_io(&d->state.mmio, &pcnet_mmio_ops, s, "pcnet-mmio",
+                          PCNET_PNPMMIO_SIZE);
+
+    memory_region_init_io(&d->io_bar, &pcnet_io_ops, s, "pcnet-io",
+                          PCNET_IOPORT_SIZE);
+    pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->io_bar);
+
+    pci_register_bar(pci_dev, 1, 0, &s->mmio);
+
+    s->irq = pci_dev->irq[0];
+    s->phys_mem_read = pci_physical_memory_read;
+    s->phys_mem_write = pci_physical_memory_write;
+    s->dma_opaque = pci_dev;
+
+    return pcnet_common_init(&pci_dev->qdev, s, &net_pci_pcnet_info);
+}
+
+static void pci_reset(DeviceState *dev)
+{
+    PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev.qdev, dev);
+
+    pcnet_h_reset(&d->state);
+}
+
+static Property pcnet_properties[] = {
+    DEFINE_NIC_PROPERTIES(PCIPCNetState, state.conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pcnet_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = pci_pcnet_init;
+    k->exit = pci_pcnet_uninit;
+    k->romfile = "efi-pcnet.rom",
+    k->vendor_id = PCI_VENDOR_ID_AMD;
+    k->device_id = PCI_DEVICE_ID_AMD_LANCE;
+    k->revision = 0x10;
+    k->class_id = PCI_CLASS_NETWORK_ETHERNET;
+    dc->reset = pci_reset;
+    dc->vmsd = &vmstate_pci_pcnet;
+    dc->props = pcnet_properties;
+}
+
+static const TypeInfo pcnet_info = {
+    .name          = "pcnet",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIPCNetState),
+    .class_init    = pcnet_class_init,
+};
+
+static void pci_pcnet_register_types(void)
+{
+    type_register_static(&pcnet_info);
+}
+
+type_init(pci_pcnet_register_types)
diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c
new file mode 100644 (file)
index 0000000..b0b462b
--- /dev/null
@@ -0,0 +1,1768 @@
+/*
+ * QEMU AMD PC-Net II (Am79C970A) emulation
+ *
+ * Copyright (c) 2004 Antony T Curtis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* This software was written to be compatible with the specification:
+ * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet
+ * AMD Publication# 19436  Rev:E  Amendment/0  Issue Date: June 2000
+ */
+
+/*
+ * On Sparc32, this is the Lance (Am7990) part of chip STP2000 (Master I/O), also
+ * produced as NCR89C100. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
+ * and
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt
+ */
+
+#include "hw/qdev.h"
+#include "net/net.h"
+#include "qemu/timer.h"
+#include "qemu/sockets.h"
+#include "sysemu/sysemu.h"
+
+#include "hw/pcnet.h"
+
+//#define PCNET_DEBUG
+//#define PCNET_DEBUG_IO
+//#define PCNET_DEBUG_BCR
+//#define PCNET_DEBUG_CSR
+//#define PCNET_DEBUG_RMD
+//#define PCNET_DEBUG_TMD
+//#define PCNET_DEBUG_MATCH
+
+
+struct qemu_ether_header {
+    uint8_t ether_dhost[6];
+    uint8_t ether_shost[6];
+    uint16_t ether_type;
+};
+
+#define CSR_INIT(S)      !!(((S)->csr[0])&0x0001)
+#define CSR_STRT(S)      !!(((S)->csr[0])&0x0002)
+#define CSR_STOP(S)      !!(((S)->csr[0])&0x0004)
+#define CSR_TDMD(S)      !!(((S)->csr[0])&0x0008)
+#define CSR_TXON(S)      !!(((S)->csr[0])&0x0010)
+#define CSR_RXON(S)      !!(((S)->csr[0])&0x0020)
+#define CSR_INEA(S)      !!(((S)->csr[0])&0x0040)
+#define CSR_BSWP(S)      !!(((S)->csr[3])&0x0004)
+#define CSR_LAPPEN(S)    !!(((S)->csr[3])&0x0020)
+#define CSR_DXSUFLO(S)   !!(((S)->csr[3])&0x0040)
+#define CSR_ASTRP_RCV(S) !!(((S)->csr[4])&0x0800)
+#define CSR_DPOLL(S)     !!(((S)->csr[4])&0x1000)
+#define CSR_SPND(S)      !!(((S)->csr[5])&0x0001)
+#define CSR_LTINTEN(S)   !!(((S)->csr[5])&0x4000)
+#define CSR_TOKINTD(S)   !!(((S)->csr[5])&0x8000)
+#define CSR_DRX(S)       !!(((S)->csr[15])&0x0001)
+#define CSR_DTX(S)       !!(((S)->csr[15])&0x0002)
+#define CSR_LOOP(S)      !!(((S)->csr[15])&0x0004)
+#define CSR_DXMTFCS(S)   !!(((S)->csr[15])&0x0008)
+#define CSR_INTL(S)      !!(((S)->csr[15])&0x0040)
+#define CSR_DRCVPA(S)    !!(((S)->csr[15])&0x2000)
+#define CSR_DRCVBC(S)    !!(((S)->csr[15])&0x4000)
+#define CSR_PROM(S)      !!(((S)->csr[15])&0x8000)
+
+#define CSR_CRBC(S)      ((S)->csr[40])
+#define CSR_CRST(S)      ((S)->csr[41])
+#define CSR_CXBC(S)      ((S)->csr[42])
+#define CSR_CXST(S)      ((S)->csr[43])
+#define CSR_NRBC(S)      ((S)->csr[44])
+#define CSR_NRST(S)      ((S)->csr[45])
+#define CSR_POLL(S)      ((S)->csr[46])
+#define CSR_PINT(S)      ((S)->csr[47])
+#define CSR_RCVRC(S)     ((S)->csr[72])
+#define CSR_XMTRC(S)     ((S)->csr[74])
+#define CSR_RCVRL(S)     ((S)->csr[76])
+#define CSR_XMTRL(S)     ((S)->csr[78])
+#define CSR_MISSC(S)     ((S)->csr[112])
+
+#define CSR_IADR(S)      ((S)->csr[ 1] | ((uint32_t)(S)->csr[ 2] << 16))
+#define CSR_CRBA(S)      ((S)->csr[18] | ((uint32_t)(S)->csr[19] << 16))
+#define CSR_CXBA(S)      ((S)->csr[20] | ((uint32_t)(S)->csr[21] << 16))
+#define CSR_NRBA(S)      ((S)->csr[22] | ((uint32_t)(S)->csr[23] << 16))
+#define CSR_BADR(S)      ((S)->csr[24] | ((uint32_t)(S)->csr[25] << 16))
+#define CSR_NRDA(S)      ((S)->csr[26] | ((uint32_t)(S)->csr[27] << 16))
+#define CSR_CRDA(S)      ((S)->csr[28] | ((uint32_t)(S)->csr[29] << 16))
+#define CSR_BADX(S)      ((S)->csr[30] | ((uint32_t)(S)->csr[31] << 16))
+#define CSR_NXDA(S)      ((S)->csr[32] | ((uint32_t)(S)->csr[33] << 16))
+#define CSR_CXDA(S)      ((S)->csr[34] | ((uint32_t)(S)->csr[35] << 16))
+#define CSR_NNRD(S)      ((S)->csr[36] | ((uint32_t)(S)->csr[37] << 16))
+#define CSR_NNXD(S)      ((S)->csr[38] | ((uint32_t)(S)->csr[39] << 16))
+#define CSR_PXDA(S)      ((S)->csr[60] | ((uint32_t)(S)->csr[61] << 16))
+#define CSR_NXBA(S)      ((S)->csr[64] | ((uint32_t)(S)->csr[65] << 16))
+
+#define PHYSADDR(S,A) \
+  (BCR_SSIZE32(S) ? (A) : (A) | ((0xff00 & (uint32_t)(S)->csr[2])<<16))
+
+struct pcnet_initblk16 {
+    uint16_t mode;
+    uint16_t padr[3];
+    uint16_t ladrf[4];
+    uint32_t rdra;
+    uint32_t tdra;
+};
+
+struct pcnet_initblk32 {
+    uint16_t mode;
+    uint8_t rlen;
+    uint8_t tlen;
+    uint16_t padr[3];
+    uint16_t _res;
+    uint16_t ladrf[4];
+    uint32_t rdra;
+    uint32_t tdra;
+};
+
+struct pcnet_TMD {
+    uint32_t tbadr;
+    int16_t length;
+    int16_t status;
+    uint32_t misc;
+    uint32_t res;
+};
+
+#define TMDL_BCNT_MASK  0x0fff
+#define TMDL_BCNT_SH    0
+#define TMDL_ONES_MASK  0xf000
+#define TMDL_ONES_SH    12
+
+#define TMDS_BPE_MASK   0x0080
+#define TMDS_BPE_SH     7
+#define TMDS_ENP_MASK   0x0100
+#define TMDS_ENP_SH     8
+#define TMDS_STP_MASK   0x0200
+#define TMDS_STP_SH     9
+#define TMDS_DEF_MASK   0x0400
+#define TMDS_DEF_SH     10
+#define TMDS_ONE_MASK   0x0800
+#define TMDS_ONE_SH     11
+#define TMDS_LTINT_MASK 0x1000
+#define TMDS_LTINT_SH   12
+#define TMDS_NOFCS_MASK 0x2000
+#define TMDS_NOFCS_SH   13
+#define TMDS_ADDFCS_MASK TMDS_NOFCS_MASK
+#define TMDS_ADDFCS_SH  TMDS_NOFCS_SH
+#define TMDS_ERR_MASK   0x4000
+#define TMDS_ERR_SH     14
+#define TMDS_OWN_MASK   0x8000
+#define TMDS_OWN_SH     15
+
+#define TMDM_TRC_MASK   0x0000000f
+#define TMDM_TRC_SH     0
+#define TMDM_TDR_MASK   0x03ff0000
+#define TMDM_TDR_SH     16
+#define TMDM_RTRY_MASK  0x04000000
+#define TMDM_RTRY_SH    26
+#define TMDM_LCAR_MASK  0x08000000
+#define TMDM_LCAR_SH    27
+#define TMDM_LCOL_MASK  0x10000000
+#define TMDM_LCOL_SH    28
+#define TMDM_EXDEF_MASK 0x20000000
+#define TMDM_EXDEF_SH   29
+#define TMDM_UFLO_MASK  0x40000000
+#define TMDM_UFLO_SH    30
+#define TMDM_BUFF_MASK  0x80000000
+#define TMDM_BUFF_SH    31
+
+struct pcnet_RMD {
+    uint32_t rbadr;
+    int16_t buf_length;
+    int16_t status;
+    uint32_t msg_length;
+    uint32_t res;
+};
+
+#define RMDL_BCNT_MASK  0x0fff
+#define RMDL_BCNT_SH    0
+#define RMDL_ONES_MASK  0xf000
+#define RMDL_ONES_SH    12
+
+#define RMDS_BAM_MASK   0x0010
+#define RMDS_BAM_SH     4
+#define RMDS_LFAM_MASK  0x0020
+#define RMDS_LFAM_SH    5
+#define RMDS_PAM_MASK   0x0040
+#define RMDS_PAM_SH     6
+#define RMDS_BPE_MASK   0x0080
+#define RMDS_BPE_SH     7
+#define RMDS_ENP_MASK   0x0100
+#define RMDS_ENP_SH     8
+#define RMDS_STP_MASK   0x0200
+#define RMDS_STP_SH     9
+#define RMDS_BUFF_MASK  0x0400
+#define RMDS_BUFF_SH    10
+#define RMDS_CRC_MASK   0x0800
+#define RMDS_CRC_SH     11
+#define RMDS_OFLO_MASK  0x1000
+#define RMDS_OFLO_SH    12
+#define RMDS_FRAM_MASK  0x2000
+#define RMDS_FRAM_SH    13
+#define RMDS_ERR_MASK   0x4000
+#define RMDS_ERR_SH     14
+#define RMDS_OWN_MASK   0x8000
+#define RMDS_OWN_SH     15
+
+#define RMDM_MCNT_MASK  0x00000fff
+#define RMDM_MCNT_SH    0
+#define RMDM_ZEROS_MASK 0x0000f000
+#define RMDM_ZEROS_SH   12
+#define RMDM_RPC_MASK   0x00ff0000
+#define RMDM_RPC_SH     16
+#define RMDM_RCC_MASK   0xff000000
+#define RMDM_RCC_SH     24
+
+#define SET_FIELD(regp, name, field, value)             \
+  (*(regp) = (*(regp) & ~(name ## _ ## field ## _MASK)) \
+             | ((value) << name ## _ ## field ## _SH))
+
+#define GET_FIELD(reg, name, field)                     \
+  (((reg) & name ## _ ## field ## _MASK) >> name ## _ ## field ## _SH)
+
+#define PRINT_TMD(T) printf(                            \
+        "TMD0 : TBADR=0x%08x\n"                         \
+        "TMD1 : OWN=%d, ERR=%d, FCS=%d, LTI=%d, "       \
+        "ONE=%d, DEF=%d, STP=%d, ENP=%d,\n"             \
+        "       BPE=%d, BCNT=%d\n"                      \
+        "TMD2 : BUF=%d, UFL=%d, EXD=%d, LCO=%d, "       \
+        "LCA=%d, RTR=%d,\n"                             \
+        "       TDR=%d, TRC=%d\n",                      \
+        (T)->tbadr,                                     \
+        GET_FIELD((T)->status, TMDS, OWN),              \
+        GET_FIELD((T)->status, TMDS, ERR),              \
+        GET_FIELD((T)->status, TMDS, NOFCS),            \
+        GET_FIELD((T)->status, TMDS, LTINT),            \
+        GET_FIELD((T)->status, TMDS, ONE),              \
+        GET_FIELD((T)->status, TMDS, DEF),              \
+        GET_FIELD((T)->status, TMDS, STP),              \
+        GET_FIELD((T)->status, TMDS, ENP),              \
+        GET_FIELD((T)->status, TMDS, BPE),              \
+        4096-GET_FIELD((T)->length, TMDL, BCNT),        \
+        GET_FIELD((T)->misc, TMDM, BUFF),               \
+        GET_FIELD((T)->misc, TMDM, UFLO),               \
+        GET_FIELD((T)->misc, TMDM, EXDEF),              \
+        GET_FIELD((T)->misc, TMDM, LCOL),               \
+        GET_FIELD((T)->misc, TMDM, LCAR),               \
+        GET_FIELD((T)->misc, TMDM, RTRY),               \
+        GET_FIELD((T)->misc, TMDM, TDR),                \
+        GET_FIELD((T)->misc, TMDM, TRC))
+
+#define PRINT_RMD(R) printf(                            \
+        "RMD0 : RBADR=0x%08x\n"                         \
+        "RMD1 : OWN=%d, ERR=%d, FRAM=%d, OFLO=%d, "     \
+        "CRC=%d, BUFF=%d, STP=%d, ENP=%d,\n       "     \
+        "BPE=%d, PAM=%d, LAFM=%d, BAM=%d, ONES=%d, BCNT=%d\n" \
+        "RMD2 : RCC=%d, RPC=%d, MCNT=%d, ZEROS=%d\n",   \
+        (R)->rbadr,                                     \
+        GET_FIELD((R)->status, RMDS, OWN),              \
+        GET_FIELD((R)->status, RMDS, ERR),              \
+        GET_FIELD((R)->status, RMDS, FRAM),             \
+        GET_FIELD((R)->status, RMDS, OFLO),             \
+        GET_FIELD((R)->status, RMDS, CRC),              \
+        GET_FIELD((R)->status, RMDS, BUFF),             \
+        GET_FIELD((R)->status, RMDS, STP),              \
+        GET_FIELD((R)->status, RMDS, ENP),              \
+        GET_FIELD((R)->status, RMDS, BPE),              \
+        GET_FIELD((R)->status, RMDS, PAM),              \
+        GET_FIELD((R)->status, RMDS, LFAM),             \
+        GET_FIELD((R)->status, RMDS, BAM),              \
+        GET_FIELD((R)->buf_length, RMDL, ONES),         \
+        4096-GET_FIELD((R)->buf_length, RMDL, BCNT),    \
+        GET_FIELD((R)->msg_length, RMDM, RCC),          \
+        GET_FIELD((R)->msg_length, RMDM, RPC),          \
+        GET_FIELD((R)->msg_length, RMDM, MCNT),         \
+        GET_FIELD((R)->msg_length, RMDM, ZEROS))
+
+static inline void pcnet_tmd_load(PCNetState *s, struct pcnet_TMD *tmd,
+                                  hwaddr addr)
+{
+    if (!BCR_SSIZE32(s)) {
+        struct {
+            uint32_t tbadr;
+            int16_t length;
+            int16_t status;
+       } xda;
+        s->phys_mem_read(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0);
+        tmd->tbadr = le32_to_cpu(xda.tbadr) & 0xffffff;
+        tmd->length = le16_to_cpu(xda.length);
+        tmd->status = (le32_to_cpu(xda.tbadr) >> 16) & 0xff00;
+        tmd->misc = le16_to_cpu(xda.status) << 16;
+        tmd->res = 0;
+    } else {
+        s->phys_mem_read(s->dma_opaque, addr, (void *)tmd, sizeof(*tmd), 0);
+        le32_to_cpus(&tmd->tbadr);
+        le16_to_cpus((uint16_t *)&tmd->length);
+        le16_to_cpus((uint16_t *)&tmd->status);
+        le32_to_cpus(&tmd->misc);
+        le32_to_cpus(&tmd->res);
+        if (BCR_SWSTYLE(s) == 3) {
+            uint32_t tmp = tmd->tbadr;
+            tmd->tbadr = tmd->misc;
+            tmd->misc = tmp;
+        }
+    }
+}
+
+static inline void pcnet_tmd_store(PCNetState *s, const struct pcnet_TMD *tmd,
+                                   hwaddr addr)
+{
+    if (!BCR_SSIZE32(s)) {
+        struct {
+            uint32_t tbadr;
+            int16_t length;
+            int16_t status;
+        } xda;
+        xda.tbadr = cpu_to_le32((tmd->tbadr & 0xffffff) |
+                                ((tmd->status & 0xff00) << 16));
+        xda.length = cpu_to_le16(tmd->length);
+        xda.status = cpu_to_le16(tmd->misc >> 16);
+        s->phys_mem_write(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0);
+    } else {
+        struct {
+            uint32_t tbadr;
+            int16_t length;
+            int16_t status;
+            uint32_t misc;
+            uint32_t res;
+        } xda;
+        xda.tbadr = cpu_to_le32(tmd->tbadr);
+        xda.length = cpu_to_le16(tmd->length);
+        xda.status = cpu_to_le16(tmd->status);
+        xda.misc = cpu_to_le32(tmd->misc);
+        xda.res = cpu_to_le32(tmd->res);
+        if (BCR_SWSTYLE(s) == 3) {
+            uint32_t tmp = xda.tbadr;
+            xda.tbadr = xda.misc;
+            xda.misc = tmp;
+        }
+        s->phys_mem_write(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0);
+    }
+}
+
+static inline void pcnet_rmd_load(PCNetState *s, struct pcnet_RMD *rmd,
+                                  hwaddr addr)
+{
+    if (!BCR_SSIZE32(s)) {
+        struct {
+            uint32_t rbadr;
+            int16_t buf_length;
+            int16_t msg_length;
+       } rda;
+        s->phys_mem_read(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0);
+        rmd->rbadr = le32_to_cpu(rda.rbadr) & 0xffffff;
+        rmd->buf_length = le16_to_cpu(rda.buf_length);
+        rmd->status = (le32_to_cpu(rda.rbadr) >> 16) & 0xff00;
+        rmd->msg_length = le16_to_cpu(rda.msg_length);
+        rmd->res = 0;
+    } else {
+        s->phys_mem_read(s->dma_opaque, addr, (void *)rmd, sizeof(*rmd), 0);
+        le32_to_cpus(&rmd->rbadr);
+        le16_to_cpus((uint16_t *)&rmd->buf_length);
+        le16_to_cpus((uint16_t *)&rmd->status);
+        le32_to_cpus(&rmd->msg_length);
+        le32_to_cpus(&rmd->res);
+        if (BCR_SWSTYLE(s) == 3) {
+            uint32_t tmp = rmd->rbadr;
+            rmd->rbadr = rmd->msg_length;
+            rmd->msg_length = tmp;
+        }
+    }
+}
+
+static inline void pcnet_rmd_store(PCNetState *s, struct pcnet_RMD *rmd,
+                                   hwaddr addr)
+{
+    if (!BCR_SSIZE32(s)) {
+        struct {
+            uint32_t rbadr;
+            int16_t buf_length;
+            int16_t msg_length;
+        } rda;
+        rda.rbadr = cpu_to_le32((rmd->rbadr & 0xffffff) |
+                                ((rmd->status & 0xff00) << 16));
+        rda.buf_length = cpu_to_le16(rmd->buf_length);
+        rda.msg_length = cpu_to_le16(rmd->msg_length);
+        s->phys_mem_write(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0);
+    } else {
+        struct {
+            uint32_t rbadr;
+            int16_t buf_length;
+            int16_t status;
+            uint32_t msg_length;
+            uint32_t res;
+        } rda;
+        rda.rbadr = cpu_to_le32(rmd->rbadr);
+        rda.buf_length = cpu_to_le16(rmd->buf_length);
+        rda.status = cpu_to_le16(rmd->status);
+        rda.msg_length = cpu_to_le32(rmd->msg_length);
+        rda.res = cpu_to_le32(rmd->res);
+        if (BCR_SWSTYLE(s) == 3) {
+            uint32_t tmp = rda.rbadr;
+            rda.rbadr = rda.msg_length;
+            rda.msg_length = tmp;
+        }
+        s->phys_mem_write(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0);
+    }
+}
+
+
+#define TMDLOAD(TMD,ADDR) pcnet_tmd_load(s,TMD,ADDR)
+
+#define TMDSTORE(TMD,ADDR) pcnet_tmd_store(s,TMD,ADDR)
+
+#define RMDLOAD(RMD,ADDR) pcnet_rmd_load(s,RMD,ADDR)
+
+#define RMDSTORE(RMD,ADDR) pcnet_rmd_store(s,RMD,ADDR)
+
+#if 1
+
+#define CHECK_RMD(ADDR,RES) do {                \
+    struct pcnet_RMD rmd;                       \
+    RMDLOAD(&rmd,(ADDR));                       \
+    (RES) |= (GET_FIELD(rmd.buf_length, RMDL, ONES) != 15) \
+          || (GET_FIELD(rmd.msg_length, RMDM, ZEROS) != 0); \
+} while (0)
+
+#define CHECK_TMD(ADDR,RES) do {                \
+    struct pcnet_TMD tmd;                       \
+    TMDLOAD(&tmd,(ADDR));                       \
+    (RES) |= (GET_FIELD(tmd.length, TMDL, ONES) != 15); \
+} while (0)
+
+#else
+
+#define CHECK_RMD(ADDR,RES) do {                \
+    switch (BCR_SWSTYLE(s)) {                   \
+    case 0x00:                                  \
+        do {                                    \
+            uint16_t rda[4];                    \
+            s->phys_mem_read(s->dma_opaque, (ADDR), \
+                (void *)&rda[0], sizeof(rda), 0); \
+            (RES) |= (rda[2] & 0xf000)!=0xf000; \
+            (RES) |= (rda[3] & 0xf000)!=0x0000; \
+        } while (0);                            \
+        break;                                  \
+    case 0x01:                                  \
+    case 0x02:                                  \
+        do {                                    \
+            uint32_t rda[4];                    \
+            s->phys_mem_read(s->dma_opaque, (ADDR), \
+                (void *)&rda[0], sizeof(rda), 0); \
+            (RES) |= (rda[1] & 0x0000f000L)!=0x0000f000L; \
+            (RES) |= (rda[2] & 0x0000f000L)!=0x00000000L; \
+        } while (0);                            \
+        break;                                  \
+    case 0x03:                                  \
+        do {                                    \
+            uint32_t rda[4];                    \
+            s->phys_mem_read(s->dma_opaque, (ADDR), \
+                (void *)&rda[0], sizeof(rda), 0); \
+            (RES) |= (rda[0] & 0x0000f000L)!=0x00000000L; \
+            (RES) |= (rda[1] & 0x0000f000L)!=0x0000f000L; \
+        } while (0);                            \
+        break;                                  \
+    }                                           \
+} while (0)
+
+#define CHECK_TMD(ADDR,RES) do {                \
+    switch (BCR_SWSTYLE(s)) {                   \
+    case 0x00:                                  \
+        do {                                    \
+            uint16_t xda[4];                    \
+            s->phys_mem_read(s->dma_opaque, (ADDR), \
+                (void *)&xda[0], sizeof(xda), 0); \
+            (RES) |= (xda[2] & 0xf000)!=0xf000; \
+        } while (0);                            \
+        break;                                  \
+    case 0x01:                                  \
+    case 0x02:                                  \
+    case 0x03:                                  \
+        do {                                    \
+            uint32_t xda[4];                    \
+            s->phys_mem_read(s->dma_opaque, (ADDR), \
+                (void *)&xda[0], sizeof(xda), 0); \
+            (RES) |= (xda[1] & 0x0000f000L)!=0x0000f000L; \
+        } while (0);                            \
+        break;                                  \
+    }                                           \
+} while (0)
+
+#endif
+
+#define PRINT_PKTHDR(BUF) do {                  \
+    struct qemu_ether_header *hdr = (void *)(BUF); \
+    printf("packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, " \
+           "shost=%02x:%02x:%02x:%02x:%02x:%02x, " \
+           "type=0x%04x\n",                     \
+           hdr->ether_dhost[0],hdr->ether_dhost[1],hdr->ether_dhost[2], \
+           hdr->ether_dhost[3],hdr->ether_dhost[4],hdr->ether_dhost[5], \
+           hdr->ether_shost[0],hdr->ether_shost[1],hdr->ether_shost[2], \
+           hdr->ether_shost[3],hdr->ether_shost[4],hdr->ether_shost[5], \
+           be16_to_cpu(hdr->ether_type));       \
+} while (0)
+
+#define MULTICAST_FILTER_LEN 8
+
+static inline uint32_t lnc_mchash(const uint8_t *ether_addr)
+{
+#define LNC_POLYNOMIAL          0xEDB88320UL
+    uint32_t crc = 0xFFFFFFFF;
+    int idx, bit;
+    uint8_t data;
+
+    for (idx = 0; idx < 6; idx++) {
+        for (data = *ether_addr++, bit = 0; bit < MULTICAST_FILTER_LEN; bit++) {
+            crc = (crc >> 1) ^ (((crc ^ data) & 1) ? LNC_POLYNOMIAL : 0);
+            data >>= 1;
+        }
+    }
+    return crc;
+#undef LNC_POLYNOMIAL
+}
+
+#define CRC(crc, ch)    (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff])
+
+/* generated using the AUTODIN II polynomial
+ *     x^32 + x^26 + x^23 + x^22 + x^16 +
+ *     x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
+ */
+static const uint32_t crctab[256] = {
+       0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+       0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+       0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+       0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+       0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+       0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+       0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+       0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+       0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+       0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+       0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+       0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+       0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+       0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+       0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+       0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+       0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+       0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+       0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+       0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+       0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+       0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+       0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+       0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+       0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+       0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+       0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+       0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+       0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+       0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+       0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+       0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+       0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+       0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+       0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+       0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+       0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+       0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+       0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+       0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+       0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+       0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+       0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+       0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+       0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+       0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+       0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+       0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+       0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+       0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+       0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+       0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+       0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+       0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+       0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+       0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+       0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+       0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+       0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+       0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+       0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+       0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+       0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+       0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+};
+
+static inline int padr_match(PCNetState *s, const uint8_t *buf, int size)
+{
+    struct qemu_ether_header *hdr = (void *)buf;
+    uint8_t padr[6] = {
+        s->csr[12] & 0xff, s->csr[12] >> 8,
+        s->csr[13] & 0xff, s->csr[13] >> 8,
+        s->csr[14] & 0xff, s->csr[14] >> 8
+    };
+    int result = (!CSR_DRCVPA(s)) && !memcmp(hdr->ether_dhost, padr, 6);
+#ifdef PCNET_DEBUG_MATCH
+    printf("packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, "
+           "padr=%02x:%02x:%02x:%02x:%02x:%02x\n",
+           hdr->ether_dhost[0],hdr->ether_dhost[1],hdr->ether_dhost[2],
+           hdr->ether_dhost[3],hdr->ether_dhost[4],hdr->ether_dhost[5],
+           padr[0],padr[1],padr[2],padr[3],padr[4],padr[5]);
+    printf("padr_match result=%d\n", result);
+#endif
+    return result;
+}
+
+static inline int padr_bcast(PCNetState *s, const uint8_t *buf, int size)
+{
+    static const uint8_t BCAST[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+    struct qemu_ether_header *hdr = (void *)buf;
+    int result = !CSR_DRCVBC(s) && !memcmp(hdr->ether_dhost, BCAST, 6);
+#ifdef PCNET_DEBUG_MATCH
+    printf("padr_bcast result=%d\n", result);
+#endif
+    return result;
+}
+
+static inline int ladr_match(PCNetState *s, const uint8_t *buf, int size)
+{
+    struct qemu_ether_header *hdr = (void *)buf;
+    if ((*(hdr->ether_dhost)&0x01) &&
+        ((uint64_t *)&s->csr[8])[0] != 0LL) {
+        uint8_t ladr[8] = {
+            s->csr[8] & 0xff, s->csr[8] >> 8,
+            s->csr[9] & 0xff, s->csr[9] >> 8,
+            s->csr[10] & 0xff, s->csr[10] >> 8,
+            s->csr[11] & 0xff, s->csr[11] >> 8
+        };
+        int index = lnc_mchash(hdr->ether_dhost) >> 26;
+        return !!(ladr[index >> 3] & (1 << (index & 7)));
+    }
+    return 0;
+}
+
+static inline hwaddr pcnet_rdra_addr(PCNetState *s, int idx)
+{
+    while (idx < 1) idx += CSR_RCVRL(s);
+    return s->rdra + ((CSR_RCVRL(s) - idx) * (BCR_SWSTYLE(s) ? 16 : 8));
+}
+
+static inline int64_t pcnet_get_next_poll_time(PCNetState *s, int64_t current_time)
+{
+    int64_t next_time = current_time +
+        muldiv64(65536 - (CSR_SPND(s) ? 0 : CSR_POLL(s)),
+                 get_ticks_per_sec(), 33000000L);
+    if (next_time <= current_time)
+        next_time = current_time + 1;
+    return next_time;
+}
+
+static void pcnet_poll(PCNetState *s);
+static void pcnet_poll_timer(void *opaque);
+
+static uint32_t pcnet_csr_readw(PCNetState *s, uint32_t rap);
+static void pcnet_csr_writew(PCNetState *s, uint32_t rap, uint32_t new_value);
+static void pcnet_bcr_writew(PCNetState *s, uint32_t rap, uint32_t val);
+
+static void pcnet_s_reset(PCNetState *s)
+{
+#ifdef PCNET_DEBUG
+    printf("pcnet_s_reset\n");
+#endif
+
+    s->rdra = 0;
+    s->tdra = 0;
+    s->rap = 0;
+
+    s->bcr[BCR_BSBC] &= ~0x0080;
+
+    s->csr[0]   = 0x0004;
+    s->csr[3]   = 0x0000;
+    s->csr[4]   = 0x0115;
+    s->csr[5]   = 0x0000;
+    s->csr[6]   = 0x0000;
+    s->csr[8]   = 0;
+    s->csr[9]   = 0;
+    s->csr[10]  = 0;
+    s->csr[11]  = 0;
+    s->csr[12]  = le16_to_cpu(((uint16_t *)&s->prom[0])[0]);
+    s->csr[13]  = le16_to_cpu(((uint16_t *)&s->prom[0])[1]);
+    s->csr[14]  = le16_to_cpu(((uint16_t *)&s->prom[0])[2]);
+    s->csr[15] &= 0x21c4;
+    s->csr[72]  = 1;
+    s->csr[74]  = 1;
+    s->csr[76]  = 1;
+    s->csr[78]  = 1;
+    s->csr[80]  = 0x1410;
+    s->csr[88]  = 0x1003;
+    s->csr[89]  = 0x0262;
+    s->csr[94]  = 0x0000;
+    s->csr[100] = 0x0200;
+    s->csr[103] = 0x0105;
+    s->csr[103] = 0x0105;
+    s->csr[112] = 0x0000;
+    s->csr[114] = 0x0000;
+    s->csr[122] = 0x0000;
+    s->csr[124] = 0x0000;
+
+    s->tx_busy = 0;
+}
+
+static void pcnet_update_irq(PCNetState *s)
+{
+    int isr = 0;
+    s->csr[0] &= ~0x0080;
+
+#if 1
+    if (((s->csr[0] & ~s->csr[3]) & 0x5f00) ||
+        (((s->csr[4]>>1) & ~s->csr[4]) & 0x0115) ||
+        (((s->csr[5]>>1) & s->csr[5]) & 0x0048))
+#else
+    if ((!(s->csr[3] & 0x4000) && !!(s->csr[0] & 0x4000)) /* BABL */ ||
+        (!(s->csr[3] & 0x1000) && !!(s->csr[0] & 0x1000)) /* MISS */ ||
+        (!(s->csr[3] & 0x0100) && !!(s->csr[0] & 0x0100)) /* IDON */ ||
+        (!(s->csr[3] & 0x0200) && !!(s->csr[0] & 0x0200)) /* TINT */ ||
+        (!(s->csr[3] & 0x0400) && !!(s->csr[0] & 0x0400)) /* RINT */ ||
+        (!(s->csr[3] & 0x0800) && !!(s->csr[0] & 0x0800)) /* MERR */ ||
+        (!(s->csr[4] & 0x0001) && !!(s->csr[4] & 0x0002)) /* JAB */ ||
+        (!(s->csr[4] & 0x0004) && !!(s->csr[4] & 0x0008)) /* TXSTRT */ ||
+        (!(s->csr[4] & 0x0010) && !!(s->csr[4] & 0x0020)) /* RCVO */ ||
+        (!(s->csr[4] & 0x0100) && !!(s->csr[4] & 0x0200)) /* MFCO */ ||
+        (!!(s->csr[5] & 0x0040) && !!(s->csr[5] & 0x0080)) /* EXDINT */ ||
+        (!!(s->csr[5] & 0x0008) && !!(s->csr[5] & 0x0010)) /* MPINT */)
+#endif
+    {
+
+        isr = CSR_INEA(s);
+        s->csr[0] |= 0x0080;
+    }
+
+    if (!!(s->csr[4] & 0x0080) && CSR_INEA(s)) { /* UINT */
+        s->csr[4] &= ~0x0080;
+        s->csr[4] |= 0x0040;
+        s->csr[0] |= 0x0080;
+        isr = 1;
+#ifdef PCNET_DEBUG
+        printf("pcnet user int\n");
+#endif
+    }
+
+#if 1
+    if (((s->csr[5]>>1) & s->csr[5]) & 0x0500)
+#else
+    if ((!!(s->csr[5] & 0x0400) && !!(s->csr[5] & 0x0800)) /* SINT */ ||
+        (!!(s->csr[5] & 0x0100) && !!(s->csr[5] & 0x0200)) /* SLPINT */ )
+#endif
+    {
+        isr = 1;
+        s->csr[0] |= 0x0080;
+    }
+
+    if (isr != s->isr) {
+#ifdef PCNET_DEBUG
+        printf("pcnet: INTA=%d\n", isr);
+#endif
+    }
+    qemu_set_irq(s->irq, isr);
+    s->isr = isr;
+}
+
+static void pcnet_init(PCNetState *s)
+{
+    int rlen, tlen;
+    uint16_t padr[3], ladrf[4], mode;
+    uint32_t rdra, tdra;
+
+#ifdef PCNET_DEBUG
+    printf("pcnet_init init_addr=0x%08x\n", PHYSADDR(s,CSR_IADR(s)));
+#endif
+
+    if (BCR_SSIZE32(s)) {
+        struct pcnet_initblk32 initblk;
+        s->phys_mem_read(s->dma_opaque, PHYSADDR(s,CSR_IADR(s)),
+                (uint8_t *)&initblk, sizeof(initblk), 0);
+        mode = le16_to_cpu(initblk.mode);
+        rlen = initblk.rlen >> 4;
+        tlen = initblk.tlen >> 4;
+       ladrf[0] = le16_to_cpu(initblk.ladrf[0]);
+       ladrf[1] = le16_to_cpu(initblk.ladrf[1]);
+       ladrf[2] = le16_to_cpu(initblk.ladrf[2]);
+       ladrf[3] = le16_to_cpu(initblk.ladrf[3]);
+       padr[0] = le16_to_cpu(initblk.padr[0]);
+       padr[1] = le16_to_cpu(initblk.padr[1]);
+       padr[2] = le16_to_cpu(initblk.padr[2]);
+        rdra = le32_to_cpu(initblk.rdra);
+        tdra = le32_to_cpu(initblk.tdra);
+    } else {
+        struct pcnet_initblk16 initblk;
+        s->phys_mem_read(s->dma_opaque, PHYSADDR(s,CSR_IADR(s)),
+                (uint8_t *)&initblk, sizeof(initblk), 0);
+        mode = le16_to_cpu(initblk.mode);
+       ladrf[0] = le16_to_cpu(initblk.ladrf[0]);
+       ladrf[1] = le16_to_cpu(initblk.ladrf[1]);
+       ladrf[2] = le16_to_cpu(initblk.ladrf[2]);
+       ladrf[3] = le16_to_cpu(initblk.ladrf[3]);
+       padr[0] = le16_to_cpu(initblk.padr[0]);
+       padr[1] = le16_to_cpu(initblk.padr[1]);
+       padr[2] = le16_to_cpu(initblk.padr[2]);
+        rdra = le32_to_cpu(initblk.rdra);
+        tdra = le32_to_cpu(initblk.tdra);
+        rlen = rdra >> 29;
+        tlen = tdra >> 29;
+        rdra &= 0x00ffffff;
+        tdra &= 0x00ffffff;
+    }
+
+#if defined(PCNET_DEBUG)
+    printf("rlen=%d tlen=%d\n", rlen, tlen);
+#endif
+
+    CSR_RCVRL(s) = (rlen < 9) ? (1 << rlen) : 512;
+    CSR_XMTRL(s) = (tlen < 9) ? (1 << tlen) : 512;
+    s->csr[ 6] = (tlen << 12) | (rlen << 8);
+    s->csr[15] = mode;
+    s->csr[ 8] = ladrf[0];
+    s->csr[ 9] = ladrf[1];
+    s->csr[10] = ladrf[2];
+    s->csr[11] = ladrf[3];
+    s->csr[12] = padr[0];
+    s->csr[13] = padr[1];
+    s->csr[14] = padr[2];
+    s->rdra = PHYSADDR(s, rdra);
+    s->tdra = PHYSADDR(s, tdra);
+
+    CSR_RCVRC(s) = CSR_RCVRL(s);
+    CSR_XMTRC(s) = CSR_XMTRL(s);
+
+#ifdef PCNET_DEBUG
+    printf("pcnet ss32=%d rdra=0x%08x[%d] tdra=0x%08x[%d]\n",
+        BCR_SSIZE32(s),
+        s->rdra, CSR_RCVRL(s), s->tdra, CSR_XMTRL(s));
+#endif
+
+    s->csr[0] |= 0x0101;
+    s->csr[0] &= ~0x0004;       /* clear STOP bit */
+}
+
+static void pcnet_start(PCNetState *s)
+{
+#ifdef PCNET_DEBUG
+    printf("pcnet_start\n");
+#endif
+
+    if (!CSR_DTX(s))
+        s->csr[0] |= 0x0010;    /* set TXON */
+
+    if (!CSR_DRX(s))
+        s->csr[0] |= 0x0020;    /* set RXON */
+
+    s->csr[0] &= ~0x0004;       /* clear STOP bit */
+    s->csr[0] |= 0x0002;
+    pcnet_poll_timer(s);
+}
+
+static void pcnet_stop(PCNetState *s)
+{
+#ifdef PCNET_DEBUG
+    printf("pcnet_stop\n");
+#endif
+    s->csr[0] &= ~0xffeb;
+    s->csr[0] |= 0x0014;
+    s->csr[4] &= ~0x02c2;
+    s->csr[5] &= ~0x0011;
+    pcnet_poll_timer(s);
+}
+
+static void pcnet_rdte_poll(PCNetState *s)
+{
+    s->csr[28] = s->csr[29] = 0;
+    if (s->rdra) {
+        int bad = 0;
+#if 1
+        hwaddr crda = pcnet_rdra_addr(s, CSR_RCVRC(s));
+        hwaddr nrda = pcnet_rdra_addr(s, -1 + CSR_RCVRC(s));
+        hwaddr nnrd = pcnet_rdra_addr(s, -2 + CSR_RCVRC(s));
+#else
+        hwaddr crda = s->rdra +
+            (CSR_RCVRL(s) - CSR_RCVRC(s)) *
+            (BCR_SWSTYLE(s) ? 16 : 8 );
+        int nrdc = CSR_RCVRC(s)<=1 ? CSR_RCVRL(s) : CSR_RCVRC(s)-1;
+        hwaddr nrda = s->rdra +
+            (CSR_RCVRL(s) - nrdc) *
+            (BCR_SWSTYLE(s) ? 16 : 8 );
+        int nnrc = nrdc<=1 ? CSR_RCVRL(s) : nrdc-1;
+        hwaddr nnrd = s->rdra +
+            (CSR_RCVRL(s) - nnrc) *
+            (BCR_SWSTYLE(s) ? 16 : 8 );
+#endif
+
+        CHECK_RMD(crda, bad);
+        if (!bad) {
+            CHECK_RMD(nrda, bad);
+            if (bad || (nrda == crda)) nrda = 0;
+            CHECK_RMD(nnrd, bad);
+            if (bad || (nnrd == crda)) nnrd = 0;
+
+            s->csr[28] = crda & 0xffff;
+            s->csr[29] = crda >> 16;
+            s->csr[26] = nrda & 0xffff;
+            s->csr[27] = nrda >> 16;
+            s->csr[36] = nnrd & 0xffff;
+            s->csr[37] = nnrd >> 16;
+#ifdef PCNET_DEBUG
+            if (bad) {
+                printf("pcnet: BAD RMD RECORDS AFTER 0x" TARGET_FMT_plx "\n",
+                       crda);
+            }
+        } else {
+            printf("pcnet: BAD RMD RDA=0x" TARGET_FMT_plx "\n",
+                   crda);
+#endif
+        }
+    }
+
+    if (CSR_CRDA(s)) {
+        struct pcnet_RMD rmd;
+        RMDLOAD(&rmd, PHYSADDR(s,CSR_CRDA(s)));
+        CSR_CRBC(s) = GET_FIELD(rmd.buf_length, RMDL, BCNT);
+        CSR_CRST(s) = rmd.status;
+#ifdef PCNET_DEBUG_RMD_X
+        printf("CRDA=0x%08x CRST=0x%04x RCVRC=%d RMDL=0x%04x RMDS=0x%04x RMDM=0x%08x\n",
+                PHYSADDR(s,CSR_CRDA(s)), CSR_CRST(s), CSR_RCVRC(s),
+                rmd.buf_length, rmd.status, rmd.msg_length);
+        PRINT_RMD(&rmd);
+#endif
+    } else {
+        CSR_CRBC(s) = CSR_CRST(s) = 0;
+    }
+
+    if (CSR_NRDA(s)) {
+        struct pcnet_RMD rmd;
+        RMDLOAD(&rmd, PHYSADDR(s,CSR_NRDA(s)));
+        CSR_NRBC(s) = GET_FIELD(rmd.buf_length, RMDL, BCNT);
+        CSR_NRST(s) = rmd.status;
+    } else {
+        CSR_NRBC(s) = CSR_NRST(s) = 0;
+    }
+
+}
+
+static int pcnet_tdte_poll(PCNetState *s)
+{
+    s->csr[34] = s->csr[35] = 0;
+    if (s->tdra) {
+        hwaddr cxda = s->tdra +
+            (CSR_XMTRL(s) - CSR_XMTRC(s)) *
+            (BCR_SWSTYLE(s) ? 16 : 8);
+        int bad = 0;
+        CHECK_TMD(cxda, bad);
+        if (!bad) {
+            if (CSR_CXDA(s) != cxda) {
+                s->csr[60] = s->csr[34];
+                s->csr[61] = s->csr[35];
+                s->csr[62] = CSR_CXBC(s);
+                s->csr[63] = CSR_CXST(s);
+            }
+            s->csr[34] = cxda & 0xffff;
+            s->csr[35] = cxda >> 16;
+#ifdef PCNET_DEBUG_X
+            printf("pcnet: BAD TMD XDA=0x%08x\n", cxda);
+#endif
+        }
+    }
+
+    if (CSR_CXDA(s)) {
+        struct pcnet_TMD tmd;
+
+        TMDLOAD(&tmd, PHYSADDR(s,CSR_CXDA(s)));
+
+        CSR_CXBC(s) = GET_FIELD(tmd.length, TMDL, BCNT);
+        CSR_CXST(s) = tmd.status;
+    } else {
+        CSR_CXBC(s) = CSR_CXST(s) = 0;
+    }
+
+    return !!(CSR_CXST(s) & 0x8000);
+}
+
+int pcnet_can_receive(NetClientState *nc)
+{
+    PCNetState *s = qemu_get_nic_opaque(nc);
+    if (CSR_STOP(s) || CSR_SPND(s))
+        return 0;
+
+    return sizeof(s->buffer)-16;
+}
+
+#define MIN_BUF_SIZE 60
+
+ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
+{
+    PCNetState *s = qemu_get_nic_opaque(nc);
+    int is_padr = 0, is_bcast = 0, is_ladr = 0;
+    uint8_t buf1[60];
+    int remaining;
+    int crc_err = 0;
+    int size = size_;
+
+    if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size ||
+        (CSR_LOOP(s) && !s->looptest)) {
+        return -1;
+    }
+#ifdef PCNET_DEBUG
+    printf("pcnet_receive size=%d\n", size);
+#endif
+
+    /* if too small buffer, then expand it */
+    if (size < MIN_BUF_SIZE) {
+        memcpy(buf1, buf, size);
+        memset(buf1 + size, 0, MIN_BUF_SIZE - size);
+        buf = buf1;
+        size = MIN_BUF_SIZE;
+    }
+
+    if (CSR_PROM(s)
+        || (is_padr=padr_match(s, buf, size))
+        || (is_bcast=padr_bcast(s, buf, size))
+        || (is_ladr=ladr_match(s, buf, size))) {
+
+        pcnet_rdte_poll(s);
+
+        if (!(CSR_CRST(s) & 0x8000) && s->rdra) {
+            struct pcnet_RMD rmd;
+            int rcvrc = CSR_RCVRC(s)-1,i;
+            hwaddr nrda;
+            for (i = CSR_RCVRL(s)-1; i > 0; i--, rcvrc--) {
+                if (rcvrc <= 1)
+                    rcvrc = CSR_RCVRL(s);
+                nrda = s->rdra +
+                    (CSR_RCVRL(s) - rcvrc) *
+                    (BCR_SWSTYLE(s) ? 16 : 8 );
+                RMDLOAD(&rmd, nrda);
+                if (GET_FIELD(rmd.status, RMDS, OWN)) {
+#ifdef PCNET_DEBUG_RMD
+                    printf("pcnet - scan buffer: RCVRC=%d PREV_RCVRC=%d\n",
+                                rcvrc, CSR_RCVRC(s));
+#endif
+                    CSR_RCVRC(s) = rcvrc;
+                    pcnet_rdte_poll(s);
+                    break;
+                }
+            }
+        }
+
+        if (!(CSR_CRST(s) & 0x8000)) {
+#ifdef PCNET_DEBUG_RMD
+            printf("pcnet - no buffer: RCVRC=%d\n", CSR_RCVRC(s));
+#endif
+            s->csr[0] |= 0x1000; /* Set MISS flag */
+            CSR_MISSC(s)++;
+        } else {
+            uint8_t *src = s->buffer;
+            hwaddr crda = CSR_CRDA(s);
+            struct pcnet_RMD rmd;
+            int pktcount = 0;
+
+            if (!s->looptest) {
+                memcpy(src, buf, size);
+                /* no need to compute the CRC */
+                src[size] = 0;
+                src[size + 1] = 0;
+                src[size + 2] = 0;
+                src[size + 3] = 0;
+                size += 4;
+            } else if (s->looptest == PCNET_LOOPTEST_CRC ||
+                       !CSR_DXMTFCS(s) || size < MIN_BUF_SIZE+4) {
+                uint32_t fcs = ~0;
+                uint8_t *p = src;
+
+                while (p != &src[size])
+                    CRC(fcs, *p++);
+                *(uint32_t *)p = htonl(fcs);
+                size += 4;
+            } else {
+                uint32_t fcs = ~0;
+                uint8_t *p = src;
+
+                while (p != &src[size-4])
+                    CRC(fcs, *p++);
+                crc_err = (*(uint32_t *)p != htonl(fcs));
+            }
+
+#ifdef PCNET_DEBUG_MATCH
+            PRINT_PKTHDR(buf);
+#endif
+
+            RMDLOAD(&rmd, PHYSADDR(s,crda));
+            /*if (!CSR_LAPPEN(s))*/
+                SET_FIELD(&rmd.status, RMDS, STP, 1);
+
+#define PCNET_RECV_STORE() do {                                 \
+    int count = MIN(4096 - GET_FIELD(rmd.buf_length, RMDL, BCNT),remaining); \
+    hwaddr rbadr = PHYSADDR(s, rmd.rbadr);          \
+    s->phys_mem_write(s->dma_opaque, rbadr, src, count, CSR_BSWP(s)); \
+    src += count; remaining -= count;                           \
+    SET_FIELD(&rmd.status, RMDS, OWN, 0);                       \
+    RMDSTORE(&rmd, PHYSADDR(s,crda));                           \
+    pktcount++;                                                 \
+} while (0)
+
+            remaining = size;
+            PCNET_RECV_STORE();
+            if ((remaining > 0) && CSR_NRDA(s)) {
+                hwaddr nrda = CSR_NRDA(s);
+#ifdef PCNET_DEBUG_RMD
+                PRINT_RMD(&rmd);
+#endif
+                RMDLOAD(&rmd, PHYSADDR(s,nrda));
+                if (GET_FIELD(rmd.status, RMDS, OWN)) {
+                    crda = nrda;
+                    PCNET_RECV_STORE();
+#ifdef PCNET_DEBUG_RMD
+                    PRINT_RMD(&rmd);
+#endif
+                    if ((remaining > 0) && (nrda=CSR_NNRD(s))) {
+                        RMDLOAD(&rmd, PHYSADDR(s,nrda));
+                        if (GET_FIELD(rmd.status, RMDS, OWN)) {
+                            crda = nrda;
+                            PCNET_RECV_STORE();
+                        }
+                    }
+                }
+            }
+
+#undef PCNET_RECV_STORE
+
+            RMDLOAD(&rmd, PHYSADDR(s,crda));
+            if (remaining == 0) {
+                SET_FIELD(&rmd.msg_length, RMDM, MCNT, size);
+                SET_FIELD(&rmd.status, RMDS, ENP, 1);
+                SET_FIELD(&rmd.status, RMDS, PAM, !CSR_PROM(s) && is_padr);
+                SET_FIELD(&rmd.status, RMDS, LFAM, !CSR_PROM(s) && is_ladr);
+                SET_FIELD(&rmd.status, RMDS, BAM, !CSR_PROM(s) && is_bcast);
+                if (crc_err) {
+                    SET_FIELD(&rmd.status, RMDS, CRC, 1);
+                    SET_FIELD(&rmd.status, RMDS, ERR, 1);
+                }
+            } else {
+                SET_FIELD(&rmd.status, RMDS, OFLO, 1);
+                SET_FIELD(&rmd.status, RMDS, BUFF, 1);
+                SET_FIELD(&rmd.status, RMDS, ERR, 1);
+            }
+            RMDSTORE(&rmd, PHYSADDR(s,crda));
+            s->csr[0] |= 0x0400;
+
+#ifdef PCNET_DEBUG
+            printf("RCVRC=%d CRDA=0x%08x BLKS=%d\n",
+                CSR_RCVRC(s), PHYSADDR(s,CSR_CRDA(s)), pktcount);
+#endif
+#ifdef PCNET_DEBUG_RMD
+            PRINT_RMD(&rmd);
+#endif
+
+            while (pktcount--) {
+                if (CSR_RCVRC(s) <= 1)
+                    CSR_RCVRC(s) = CSR_RCVRL(s);
+                else
+                    CSR_RCVRC(s)--;
+            }
+
+            pcnet_rdte_poll(s);
+
+        }
+    }
+
+    pcnet_poll(s);
+    pcnet_update_irq(s);
+
+    return size_;
+}
+
+void pcnet_set_link_status(NetClientState *nc)
+{
+    PCNetState *d = qemu_get_nic_opaque(nc);
+
+    d->lnkst = nc->link_down ? 0 : 0x40;
+}
+
+static void pcnet_transmit(PCNetState *s)
+{
+    hwaddr xmit_cxda = 0;
+    int count = CSR_XMTRL(s)-1;
+    int add_crc = 0;
+
+    s->xmit_pos = -1;
+
+    if (!CSR_TXON(s)) {
+        s->csr[0] &= ~0x0008;
+        return;
+    }
+
+    s->tx_busy = 1;
+
+    txagain:
+    if (pcnet_tdte_poll(s)) {
+        struct pcnet_TMD tmd;
+
+        TMDLOAD(&tmd, PHYSADDR(s,CSR_CXDA(s)));
+
+#ifdef PCNET_DEBUG_TMD
+        printf("  TMDLOAD 0x%08x\n", PHYSADDR(s,CSR_CXDA(s)));
+        PRINT_TMD(&tmd);
+#endif
+        if (GET_FIELD(tmd.status, TMDS, STP)) {
+            s->xmit_pos = 0;
+            xmit_cxda = PHYSADDR(s,CSR_CXDA(s));
+            if (BCR_SWSTYLE(s) != 1)
+                add_crc = GET_FIELD(tmd.status, TMDS, ADDFCS);
+        }
+        if (s->lnkst == 0 &&
+            (!CSR_LOOP(s) || (!CSR_INTL(s) && !BCR_TMAULOOP(s)))) {
+            SET_FIELD(&tmd.misc, TMDM, LCAR, 1);
+            SET_FIELD(&tmd.status, TMDS, ERR, 1);
+            SET_FIELD(&tmd.status, TMDS, OWN, 0);
+            s->csr[0] |= 0xa000; /* ERR | CERR */
+            s->xmit_pos = -1;
+            goto txdone;
+        }
+        if (!GET_FIELD(tmd.status, TMDS, ENP)) {
+            int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);
+            s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr),
+                             s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s));
+            s->xmit_pos += bcnt;
+        } else if (s->xmit_pos >= 0) {
+            int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);
+            s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr),
+                             s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s));
+            s->xmit_pos += bcnt;
+#ifdef PCNET_DEBUG
+            printf("pcnet_transmit size=%d\n", s->xmit_pos);
+#endif
+            if (CSR_LOOP(s)) {
+                if (BCR_SWSTYLE(s) == 1)
+                    add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS);
+                s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC;
+                pcnet_receive(qemu_get_queue(s->nic), s->buffer, s->xmit_pos);
+                s->looptest = 0;
+            } else
+                if (s->nic)
+                    qemu_send_packet(qemu_get_queue(s->nic), s->buffer,
+                                     s->xmit_pos);
+
+            s->csr[0] &= ~0x0008;   /* clear TDMD */
+            s->csr[4] |= 0x0004;    /* set TXSTRT */
+            s->xmit_pos = -1;
+        }
+
+    txdone:
+        SET_FIELD(&tmd.status, TMDS, OWN, 0);
+        TMDSTORE(&tmd, PHYSADDR(s,CSR_CXDA(s)));
+        if (!CSR_TOKINTD(s) || (CSR_LTINTEN(s) && GET_FIELD(tmd.status, TMDS, LTINT)))
+            s->csr[0] |= 0x0200;    /* set TINT */
+
+        if (CSR_XMTRC(s)<=1)
+            CSR_XMTRC(s) = CSR_XMTRL(s);
+        else
+            CSR_XMTRC(s)--;
+        if (count--)
+            goto txagain;
+
+    } else
+    if (s->xmit_pos >= 0) {
+        struct pcnet_TMD tmd;
+        TMDLOAD(&tmd, xmit_cxda);
+        SET_FIELD(&tmd.misc, TMDM, BUFF, 1);
+        SET_FIELD(&tmd.misc, TMDM, UFLO, 1);
+        SET_FIELD(&tmd.status, TMDS, ERR, 1);
+        SET_FIELD(&tmd.status, TMDS, OWN, 0);
+        TMDSTORE(&tmd, xmit_cxda);
+        s->csr[0] |= 0x0200;    /* set TINT */
+        if (!CSR_DXSUFLO(s)) {
+            s->csr[0] &= ~0x0010;
+        } else
+        if (count--)
+          goto txagain;
+    }
+
+    s->tx_busy = 0;
+}
+
+static void pcnet_poll(PCNetState *s)
+{
+    if (CSR_RXON(s)) {
+        pcnet_rdte_poll(s);
+    }
+
+    if (CSR_TDMD(s) ||
+        (CSR_TXON(s) && !CSR_DPOLL(s) && pcnet_tdte_poll(s)))
+    {
+        /* prevent recursion */
+        if (s->tx_busy)
+            return;
+
+        pcnet_transmit(s);
+    }
+}
+
+static void pcnet_poll_timer(void *opaque)
+{
+    PCNetState *s = opaque;
+
+    qemu_del_timer(s->poll_timer);
+
+    if (CSR_TDMD(s)) {
+        pcnet_transmit(s);
+    }
+
+    pcnet_update_irq(s);
+
+    if (!CSR_STOP(s) && !CSR_SPND(s) && !CSR_DPOLL(s)) {
+        uint64_t now = qemu_get_clock_ns(vm_clock) * 33;
+        if (!s->timer || !now)
+            s->timer = now;
+        else {
+            uint64_t t = now - s->timer + CSR_POLL(s);
+            if (t > 0xffffLL) {
+                pcnet_poll(s);
+                CSR_POLL(s) = CSR_PINT(s);
+            } else
+                CSR_POLL(s) = t;
+        }
+        qemu_mod_timer(s->poll_timer,
+            pcnet_get_next_poll_time(s,qemu_get_clock_ns(vm_clock)));
+    }
+}
+
+
+static void pcnet_csr_writew(PCNetState *s, uint32_t rap, uint32_t new_value)
+{
+    uint16_t val = new_value;
+#ifdef PCNET_DEBUG_CSR
+    printf("pcnet_csr_writew rap=%d val=0x%04x\n", rap, val);
+#endif
+    switch (rap) {
+    case 0:
+        s->csr[0] &= ~(val & 0x7f00); /* Clear any interrupt flags */
+
+        s->csr[0] = (s->csr[0] & ~0x0040) | (val & 0x0048);
+
+        val = (val & 0x007f) | (s->csr[0] & 0x7f00);
+
+        /* IFF STOP, STRT and INIT are set, clear STRT and INIT */
+        if ((val&7) == 7)
+          val &= ~3;
+
+        if (!CSR_STOP(s) && (val & 4))
+            pcnet_stop(s);
+
+        if (!CSR_INIT(s) && (val & 1))
+            pcnet_init(s);
+
+        if (!CSR_STRT(s) && (val & 2))
+            pcnet_start(s);
+
+        if (CSR_TDMD(s))
+            pcnet_transmit(s);
+
+        return;
+    case 1:
+    case 2:
+    case 8:
+    case 9:
+    case 10:
+    case 11:
+    case 12:
+    case 13:
+    case 14:
+    case 15:
+    case 18: /* CRBAL */
+    case 19: /* CRBAU */
+    case 20: /* CXBAL */
+    case 21: /* CXBAU */
+    case 22: /* NRBAU */
+    case 23: /* NRBAU */
+    case 24:
+    case 25:
+    case 26:
+    case 27:
+    case 28:
+    case 29:
+    case 30:
+    case 31:
+    case 32:
+    case 33:
+    case 34:
+    case 35:
+    case 36:
+    case 37:
+    case 38:
+    case 39:
+    case 40: /* CRBC */
+    case 41:
+    case 42: /* CXBC */
+    case 43:
+    case 44:
+    case 45:
+    case 46: /* POLL */
+    case 47: /* POLLINT */
+    case 72:
+    case 74:
+    case 76: /* RCVRL */
+    case 78: /* XMTRL */
+    case 112:
+       if (CSR_STOP(s) || CSR_SPND(s))
+           break;
+       return;
+    case 3:
+        break;
+    case 4:
+        s->csr[4] &= ~(val & 0x026a);
+        val &= ~0x026a; val |= s->csr[4] & 0x026a;
+        break;
+    case 5:
+        s->csr[5] &= ~(val & 0x0a90);
+        val &= ~0x0a90; val |= s->csr[5] & 0x0a90;
+        break;
+    case 16:
+        pcnet_csr_writew(s,1,val);
+        return;
+    case 17:
+        pcnet_csr_writew(s,2,val);
+        return;
+    case 58:
+        pcnet_bcr_writew(s,BCR_SWS,val);
+        break;
+    default:
+        return;
+    }
+    s->csr[rap] = val;
+}
+
+static uint32_t pcnet_csr_readw(PCNetState *s, uint32_t rap)
+{
+    uint32_t val;
+    switch (rap) {
+    case 0:
+        pcnet_update_irq(s);
+        val = s->csr[0];
+        val |= (val & 0x7800) ? 0x8000 : 0;
+        break;
+    case 16:
+        return pcnet_csr_readw(s,1);
+    case 17:
+        return pcnet_csr_readw(s,2);
+    case 58:
+        return pcnet_bcr_readw(s,BCR_SWS);
+    case 88:
+        val = s->csr[89];
+        val <<= 16;
+        val |= s->csr[88];
+        break;
+    default:
+        val = s->csr[rap];
+    }
+#ifdef PCNET_DEBUG_CSR
+    printf("pcnet_csr_readw rap=%d val=0x%04x\n", rap, val);
+#endif
+    return val;
+}
+
+static void pcnet_bcr_writew(PCNetState *s, uint32_t rap, uint32_t val)
+{
+    rap &= 127;
+#ifdef PCNET_DEBUG_BCR
+    printf("pcnet_bcr_writew rap=%d val=0x%04x\n", rap, val);
+#endif
+    switch (rap) {
+    case BCR_SWS:
+        if (!(CSR_STOP(s) || CSR_SPND(s)))
+            return;
+        val &= ~0x0300;
+        switch (val & 0x00ff) {
+        case 0:
+            val |= 0x0200;
+            break;
+        case 1:
+            val |= 0x0100;
+            break;
+        case 2:
+        case 3:
+            val |= 0x0300;
+            break;
+        default:
+            printf("Bad SWSTYLE=0x%02x\n", val & 0xff);
+            val = 0x0200;
+            break;
+        }
+#ifdef PCNET_DEBUG
+       printf("BCR_SWS=0x%04x\n", val);
+#endif
+        /* fall through */
+    case BCR_LNKST:
+    case BCR_LED1:
+    case BCR_LED2:
+    case BCR_LED3:
+    case BCR_MC:
+    case BCR_FDC:
+    case BCR_BSBC:
+    case BCR_EECAS:
+    case BCR_PLAT:
+        s->bcr[rap] = val;
+        break;
+    default:
+        break;
+    }
+}
+
+uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap)
+{
+    uint32_t val;
+    rap &= 127;
+    switch (rap) {
+    case BCR_LNKST:
+    case BCR_LED1:
+    case BCR_LED2:
+    case BCR_LED3:
+        val = s->bcr[rap] & ~0x8000;
+        val |= (val & 0x017f & s->lnkst) ? 0x8000 : 0;
+        break;
+    default:
+        val = rap < 32 ? s->bcr[rap] : 0;
+        break;
+    }
+#ifdef PCNET_DEBUG_BCR
+    printf("pcnet_bcr_readw rap=%d val=0x%04x\n", rap, val);
+#endif
+    return val;
+}
+
+void pcnet_h_reset(void *opaque)
+{
+    PCNetState *s = opaque;
+
+    s->bcr[BCR_MSRDA] = 0x0005;
+    s->bcr[BCR_MSWRA] = 0x0005;
+    s->bcr[BCR_MC   ] = 0x0002;
+    s->bcr[BCR_LNKST] = 0x00c0;
+    s->bcr[BCR_LED1 ] = 0x0084;
+    s->bcr[BCR_LED2 ] = 0x0088;
+    s->bcr[BCR_LED3 ] = 0x0090;
+    s->bcr[BCR_FDC  ] = 0x0000;
+    s->bcr[BCR_BSBC ] = 0x9001;
+    s->bcr[BCR_EECAS] = 0x0002;
+    s->bcr[BCR_SWS  ] = 0x0200;
+    s->bcr[BCR_PLAT ] = 0xff06;
+
+    pcnet_s_reset(s);
+    pcnet_update_irq(s);
+    pcnet_poll_timer(s);
+}
+
+void pcnet_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+    PCNetState *s = opaque;
+    pcnet_poll_timer(s);
+#ifdef PCNET_DEBUG_IO
+    printf("pcnet_ioport_writew addr=0x%08x val=0x%04x\n", addr, val);
+#endif
+    if (!BCR_DWIO(s)) {
+        switch (addr & 0x0f) {
+        case 0x00: /* RDP */
+            pcnet_csr_writew(s, s->rap, val);
+            break;
+        case 0x02:
+            s->rap = val & 0x7f;
+            break;
+        case 0x06:
+            pcnet_bcr_writew(s, s->rap, val);
+            break;
+        }
+    }
+    pcnet_update_irq(s);
+}
+
+uint32_t pcnet_ioport_readw(void *opaque, uint32_t addr)
+{
+    PCNetState *s = opaque;
+    uint32_t val = -1;
+    pcnet_poll_timer(s);
+    if (!BCR_DWIO(s)) {
+        switch (addr & 0x0f) {
+        case 0x00: /* RDP */
+            val = pcnet_csr_readw(s, s->rap);
+            break;
+        case 0x02:
+            val = s->rap;
+            break;
+        case 0x04:
+            pcnet_s_reset(s);
+            val = 0;
+            break;
+        case 0x06:
+            val = pcnet_bcr_readw(s, s->rap);
+            break;
+        }
+    }
+    pcnet_update_irq(s);
+#ifdef PCNET_DEBUG_IO
+    printf("pcnet_ioport_readw addr=0x%08x val=0x%04x\n", addr, val & 0xffff);
+#endif
+    return val;
+}
+
+void pcnet_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
+{
+    PCNetState *s = opaque;
+    pcnet_poll_timer(s);
+#ifdef PCNET_DEBUG_IO
+    printf("pcnet_ioport_writel addr=0x%08x val=0x%08x\n", addr, val);
+#endif
+    if (BCR_DWIO(s)) {
+        switch (addr & 0x0f) {
+        case 0x00: /* RDP */
+            pcnet_csr_writew(s, s->rap, val & 0xffff);
+            break;
+        case 0x04:
+            s->rap = val & 0x7f;
+            break;
+        case 0x0c:
+            pcnet_bcr_writew(s, s->rap, val & 0xffff);
+            break;
+        }
+    } else
+    if ((addr & 0x0f) == 0) {
+        /* switch device to dword i/o mode */
+        pcnet_bcr_writew(s, BCR_BSBC, pcnet_bcr_readw(s, BCR_BSBC) | 0x0080);
+#ifdef PCNET_DEBUG_IO
+        printf("device switched into dword i/o mode\n");
+#endif
+    }
+    pcnet_update_irq(s);
+}
+
+uint32_t pcnet_ioport_readl(void *opaque, uint32_t addr)
+{
+    PCNetState *s = opaque;
+    uint32_t val = -1;
+    pcnet_poll_timer(s);
+    if (BCR_DWIO(s)) {
+        switch (addr & 0x0f) {
+        case 0x00: /* RDP */
+            val = pcnet_csr_readw(s, s->rap);
+            break;
+        case 0x04:
+            val = s->rap;
+            break;
+        case 0x08:
+            pcnet_s_reset(s);
+            val = 0;
+            break;
+        case 0x0c:
+            val = pcnet_bcr_readw(s, s->rap);
+            break;
+        }
+    }
+    pcnet_update_irq(s);
+#ifdef PCNET_DEBUG_IO
+    printf("pcnet_ioport_readl addr=0x%08x val=0x%08x\n", addr, val);
+#endif
+    return val;
+}
+
+static bool is_version_2(void *opaque, int version_id)
+{
+    return version_id == 2;
+}
+
+const VMStateDescription vmstate_pcnet = {
+    .name = "pcnet",
+    .version_id = 3,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields      = (VMStateField []) {
+        VMSTATE_INT32(rap, PCNetState),
+        VMSTATE_INT32(isr, PCNetState),
+        VMSTATE_INT32(lnkst, PCNetState),
+        VMSTATE_UINT32(rdra, PCNetState),
+        VMSTATE_UINT32(tdra, PCNetState),
+        VMSTATE_BUFFER(prom, PCNetState),
+        VMSTATE_UINT16_ARRAY(csr, PCNetState, 128),
+        VMSTATE_UINT16_ARRAY(bcr, PCNetState, 32),
+        VMSTATE_UINT64(timer, PCNetState),
+        VMSTATE_INT32(xmit_pos, PCNetState),
+        VMSTATE_BUFFER(buffer, PCNetState),
+        VMSTATE_UNUSED_TEST(is_version_2, 4),
+        VMSTATE_INT32(tx_busy, PCNetState),
+        VMSTATE_TIMER(poll_timer, PCNetState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+void pcnet_common_cleanup(PCNetState *d)
+{
+    d->nic = NULL;
+}
+
+int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info)
+{
+    int i;
+    uint16_t checksum;
+
+    s->poll_timer = qemu_new_timer_ns(vm_clock, pcnet_poll_timer, s);
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+    s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, s);
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+
+    add_boot_device_path(s->conf.bootindex, dev, "/ethernet-phy@0");
+
+    /* Initialize the PROM */
+
+    /*
+      Datasheet: http://pdfdata.datasheetsite.com/web/24528/AM79C970A.pdf
+      page 95
+    */
+    memcpy(s->prom, s->conf.macaddr.a, 6);
+    /* Reserved Location: must be 00h */
+    s->prom[6] = s->prom[7] = 0x00;
+    /* Reserved Location: must be 00h */
+    s->prom[8] = 0x00;
+    /* Hardware ID: must be 11h if compatibility to AMD drivers is desired */
+    s->prom[9] = 0x11;
+    /* User programmable space, init with 0 */
+    s->prom[10] = s->prom[11] = 0x00;
+    /* LSByte of two-byte checksum, which is the sum of bytes 00h-0Bh
+       and bytes 0Eh and 0Fh, must therefore be initialized with 0! */
+    s->prom[12] = s->prom[13] = 0x00;
+    /* Must be ASCII W (57h) if compatibility to AMD
+       driver software is desired */
+    s->prom[14] = s->prom[15] = 0x57;
+
+    for (i = 0, checksum = 0; i < 16; i++) {
+        checksum += s->prom[i];
+    }
+    *(uint16_t *)&s->prom[12] = cpu_to_le16(checksum);
+
+    s->lnkst = 0x40; /* initial link state: up */
+
+    return 0;
+}
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
new file mode 100644 (file)
index 0000000..9369507
--- /dev/null
@@ -0,0 +1,3555 @@
+/**
+ * QEMU RTL8139 emulation
+ *
+ * Copyright (c) 2006 Igor Kovalenko
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+
+ * Modifications:
+ *  2006-Jan-28  Mark Malakanov :   TSAD and CSCR implementation (for Windows driver)
+ *
+ *  2006-Apr-28  Juergen Lock   :   EEPROM emulation changes for FreeBSD driver
+ *                                  HW revision ID changes for FreeBSD driver
+ *
+ *  2006-Jul-01  Igor Kovalenko :   Implemented loopback mode for FreeBSD driver
+ *                                  Corrected packet transfer reassembly routine for 8139C+ mode
+ *                                  Rearranged debugging print statements
+ *                                  Implemented PCI timer interrupt (disabled by default)
+ *                                  Implemented Tally Counters, increased VM load/save version
+ *                                  Implemented IP/TCP/UDP checksum task offloading
+ *
+ *  2006-Jul-04  Igor Kovalenko :   Implemented TCP segmentation offloading
+ *                                  Fixed MTU=1500 for produced ethernet frames
+ *
+ *  2006-Jul-09  Igor Kovalenko :   Fixed TCP header length calculation while processing
+ *                                  segmentation offloading
+ *                                  Removed slirp.h dependency
+ *                                  Added rx/tx buffer reset when enabling rx/tx operation
+ *
+ *  2010-Feb-04  Frediano Ziglio:   Rewrote timer support using QEMU timer only
+ *                                  when strictly needed (required for for
+ *                                  Darwin)
+ *  2011-Mar-22  Benjamin Poirier:  Implemented VLAN offloading
+ */
+
+/* For crc32 */
+#include <zlib.h>
+
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "sysemu/dma.h"
+#include "qemu/timer.h"
+#include "net/net.h"
+#include "hw/loader.h"
+#include "sysemu/sysemu.h"
+#include "qemu/iov.h"
+
+/* debug RTL8139 card */
+//#define DEBUG_RTL8139 1
+
+#define PCI_FREQUENCY 33000000L
+
+#define SET_MASKED(input, mask, curr) \
+    ( ( (input) & ~(mask) ) | ( (curr) & (mask) ) )
+
+/* arg % size for size which is a power of 2 */
+#define MOD2(input, size) \
+    ( ( input ) & ( size - 1 )  )
+
+#define ETHER_ADDR_LEN 6
+#define ETHER_TYPE_LEN 2
+#define ETH_HLEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN)
+#define ETH_P_IP    0x0800      /* Internet Protocol packet */
+#define ETH_P_8021Q 0x8100      /* 802.1Q VLAN Extended Header  */
+#define ETH_MTU     1500
+
+#define VLAN_TCI_LEN 2
+#define VLAN_HLEN (ETHER_TYPE_LEN + VLAN_TCI_LEN)
+
+#if defined (DEBUG_RTL8139)
+#  define DPRINTF(fmt, ...) \
+    do { fprintf(stderr, "RTL8139: " fmt, ## __VA_ARGS__); } while (0)
+#else
+static inline GCC_FMT_ATTR(1, 2) int DPRINTF(const char *fmt, ...)
+{
+    return 0;
+}
+#endif
+
+/* Symbolic offsets to registers. */
+enum RTL8139_registers {
+    MAC0 = 0,        /* Ethernet hardware address. */
+    MAR0 = 8,        /* Multicast filter. */
+    TxStatus0 = 0x10,/* Transmit status (Four 32bit registers). C mode only */
+                     /* Dump Tally Conter control register(64bit). C+ mode only */
+    TxAddr0 = 0x20,  /* Tx descriptors (also four 32bit). */
+    RxBuf = 0x30,
+    ChipCmd = 0x37,
+    RxBufPtr = 0x38,
+    RxBufAddr = 0x3A,
+    IntrMask = 0x3C,
+    IntrStatus = 0x3E,
+    TxConfig = 0x40,
+    RxConfig = 0x44,
+    Timer = 0x48,        /* A general-purpose counter. */
+    RxMissed = 0x4C,    /* 24 bits valid, write clears. */
+    Cfg9346 = 0x50,
+    Config0 = 0x51,
+    Config1 = 0x52,
+    FlashReg = 0x54,
+    MediaStatus = 0x58,
+    Config3 = 0x59,
+    Config4 = 0x5A,        /* absent on RTL-8139A */
+    HltClk = 0x5B,
+    MultiIntr = 0x5C,
+    PCIRevisionID = 0x5E,
+    TxSummary = 0x60, /* TSAD register. Transmit Status of All Descriptors*/
+    BasicModeCtrl = 0x62,
+    BasicModeStatus = 0x64,
+    NWayAdvert = 0x66,
+    NWayLPAR = 0x68,
+    NWayExpansion = 0x6A,
+    /* Undocumented registers, but required for proper operation. */
+    FIFOTMS = 0x70,        /* FIFO Control and test. */
+    CSCR = 0x74,        /* Chip Status and Configuration Register. */
+    PARA78 = 0x78,
+    PARA7c = 0x7c,        /* Magic transceiver parameter register. */
+    Config5 = 0xD8,        /* absent on RTL-8139A */
+    /* C+ mode */
+    TxPoll        = 0xD9,    /* Tell chip to check Tx descriptors for work */
+    RxMaxSize    = 0xDA, /* Max size of an Rx packet (8169 only) */
+    CpCmd        = 0xE0, /* C+ Command register (C+ mode only) */
+    IntrMitigate    = 0xE2,    /* rx/tx interrupt mitigation control */
+    RxRingAddrLO    = 0xE4, /* 64-bit start addr of Rx ring */
+    RxRingAddrHI    = 0xE8, /* 64-bit start addr of Rx ring */
+    TxThresh    = 0xEC, /* Early Tx threshold */
+};
+
+enum ClearBitMasks {
+    MultiIntrClear = 0xF000,
+    ChipCmdClear = 0xE2,
+    Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1),
+};
+
+enum ChipCmdBits {
+    CmdReset = 0x10,
+    CmdRxEnb = 0x08,
+    CmdTxEnb = 0x04,
+    RxBufEmpty = 0x01,
+};
+
+/* C+ mode */
+enum CplusCmdBits {
+    CPlusRxVLAN   = 0x0040, /* enable receive VLAN detagging */
+    CPlusRxChkSum = 0x0020, /* enable receive checksum offloading */
+    CPlusRxEnb    = 0x0002,
+    CPlusTxEnb    = 0x0001,
+};
+
+/* Interrupt register bits, using my own meaningful names. */
+enum IntrStatusBits {
+    PCIErr = 0x8000,
+    PCSTimeout = 0x4000,
+    RxFIFOOver = 0x40,
+    RxUnderrun = 0x20, /* Packet Underrun / Link Change */
+    RxOverflow = 0x10,
+    TxErr = 0x08,
+    TxOK = 0x04,
+    RxErr = 0x02,
+    RxOK = 0x01,
+
+    RxAckBits = RxFIFOOver | RxOverflow | RxOK,
+};
+
+enum TxStatusBits {
+    TxHostOwns = 0x2000,
+    TxUnderrun = 0x4000,
+    TxStatOK = 0x8000,
+    TxOutOfWindow = 0x20000000,
+    TxAborted = 0x40000000,
+    TxCarrierLost = 0x80000000,
+};
+enum RxStatusBits {
+    RxMulticast = 0x8000,
+    RxPhysical = 0x4000,
+    RxBroadcast = 0x2000,
+    RxBadSymbol = 0x0020,
+    RxRunt = 0x0010,
+    RxTooLong = 0x0008,
+    RxCRCErr = 0x0004,
+    RxBadAlign = 0x0002,
+    RxStatusOK = 0x0001,
+};
+
+/* Bits in RxConfig. */
+enum rx_mode_bits {
+    AcceptErr = 0x20,
+    AcceptRunt = 0x10,
+    AcceptBroadcast = 0x08,
+    AcceptMulticast = 0x04,
+    AcceptMyPhys = 0x02,
+    AcceptAllPhys = 0x01,
+};
+
+/* Bits in TxConfig. */
+enum tx_config_bits {
+
+        /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */
+        TxIFGShift = 24,
+        TxIFG84 = (0 << TxIFGShift),    /* 8.4us / 840ns (10 / 100Mbps) */
+        TxIFG88 = (1 << TxIFGShift),    /* 8.8us / 880ns (10 / 100Mbps) */
+        TxIFG92 = (2 << TxIFGShift),    /* 9.2us / 920ns (10 / 100Mbps) */
+        TxIFG96 = (3 << TxIFGShift),    /* 9.6us / 960ns (10 / 100Mbps) */
+
+    TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
+    TxCRC = (1 << 16),    /* DISABLE appending CRC to end of Tx packets */
+    TxClearAbt = (1 << 0),    /* Clear abort (WO) */
+    TxDMAShift = 8,        /* DMA burst value (0-7) is shifted this many bits */
+    TxRetryShift = 4,    /* TXRR value (0-15) is shifted this many bits */
+
+    TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
+};
+
+
+/* Transmit Status of All Descriptors (TSAD) Register */
+enum TSAD_bits {
+ TSAD_TOK3 = 1<<15, // TOK bit of Descriptor 3
+ TSAD_TOK2 = 1<<14, // TOK bit of Descriptor 2
+ TSAD_TOK1 = 1<<13, // TOK bit of Descriptor 1
+ TSAD_TOK0 = 1<<12, // TOK bit of Descriptor 0
+ TSAD_TUN3 = 1<<11, // TUN bit of Descriptor 3
+ TSAD_TUN2 = 1<<10, // TUN bit of Descriptor 2
+ TSAD_TUN1 = 1<<9, // TUN bit of Descriptor 1
+ TSAD_TUN0 = 1<<8, // TUN bit of Descriptor 0
+ TSAD_TABT3 = 1<<07, // TABT bit of Descriptor 3
+ TSAD_TABT2 = 1<<06, // TABT bit of Descriptor 2
+ TSAD_TABT1 = 1<<05, // TABT bit of Descriptor 1
+ TSAD_TABT0 = 1<<04, // TABT bit of Descriptor 0
+ TSAD_OWN3 = 1<<03, // OWN bit of Descriptor 3
+ TSAD_OWN2 = 1<<02, // OWN bit of Descriptor 2
+ TSAD_OWN1 = 1<<01, // OWN bit of Descriptor 1
+ TSAD_OWN0 = 1<<00, // OWN bit of Descriptor 0
+};
+
+
+/* Bits in Config1 */
+enum Config1Bits {
+    Cfg1_PM_Enable = 0x01,
+    Cfg1_VPD_Enable = 0x02,
+    Cfg1_PIO = 0x04,
+    Cfg1_MMIO = 0x08,
+    LWAKE = 0x10,        /* not on 8139, 8139A */
+    Cfg1_Driver_Load = 0x20,
+    Cfg1_LED0 = 0x40,
+    Cfg1_LED1 = 0x80,
+    SLEEP = (1 << 1),    /* only on 8139, 8139A */
+    PWRDN = (1 << 0),    /* only on 8139, 8139A */
+};
+
+/* Bits in Config3 */
+enum Config3Bits {
+    Cfg3_FBtBEn    = (1 << 0), /* 1 = Fast Back to Back */
+    Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */
+    Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */
+    Cfg3_CardB_En  = (1 << 3), /* 1 = enable CardBus registers */
+    Cfg3_LinkUp    = (1 << 4), /* 1 = wake up on link up */
+    Cfg3_Magic     = (1 << 5), /* 1 = wake up on Magic Packet (tm) */
+    Cfg3_PARM_En   = (1 << 6), /* 0 = software can set twister parameters */
+    Cfg3_GNTSel    = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */
+};
+
+/* Bits in Config4 */
+enum Config4Bits {
+    LWPTN = (1 << 2),    /* not on 8139, 8139A */
+};
+
+/* Bits in Config5 */
+enum Config5Bits {
+    Cfg5_PME_STS     = (1 << 0), /* 1 = PCI reset resets PME_Status */
+    Cfg5_LANWake     = (1 << 1), /* 1 = enable LANWake signal */
+    Cfg5_LDPS        = (1 << 2), /* 0 = save power when link is down */
+    Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */
+    Cfg5_UWF         = (1 << 4), /* 1 = accept unicast wakeup frame */
+    Cfg5_MWF         = (1 << 5), /* 1 = accept multicast wakeup frame */
+    Cfg5_BWF         = (1 << 6), /* 1 = accept broadcast wakeup frame */
+};
+
+enum RxConfigBits {
+    /* rx fifo threshold */
+    RxCfgFIFOShift = 13,
+    RxCfgFIFONone = (7 << RxCfgFIFOShift),
+
+    /* Max DMA burst */
+    RxCfgDMAShift = 8,
+    RxCfgDMAUnlimited = (7 << RxCfgDMAShift),
+
+    /* rx ring buffer length */
+    RxCfgRcv8K = 0,
+    RxCfgRcv16K = (1 << 11),
+    RxCfgRcv32K = (1 << 12),
+    RxCfgRcv64K = (1 << 11) | (1 << 12),
+
+    /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */
+    RxNoWrap = (1 << 7),
+};
+
+/* Twister tuning parameters from RealTek.
+   Completely undocumented, but required to tune bad links on some boards. */
+/*
+enum CSCRBits {
+    CSCR_LinkOKBit = 0x0400,
+    CSCR_LinkChangeBit = 0x0800,
+    CSCR_LinkStatusBits = 0x0f000,
+    CSCR_LinkDownOffCmd = 0x003c0,
+    CSCR_LinkDownCmd = 0x0f3c0,
+*/
+enum CSCRBits {
+    CSCR_Testfun = 1<<15, /* 1 = Auto-neg speeds up internal timer, WO, def 0 */
+    CSCR_LD  = 1<<9,  /* Active low TPI link disable signal. When low, TPI still transmits link pulses and TPI stays in good link state. def 1*/
+    CSCR_HEART_BIT = 1<<8,  /* 1 = HEART BEAT enable, 0 = HEART BEAT disable. HEART BEAT function is only valid in 10Mbps mode. def 1*/
+    CSCR_JBEN = 1<<7,  /* 1 = enable jabber function. 0 = disable jabber function, def 1*/
+    CSCR_F_LINK_100 = 1<<6, /* Used to login force good link in 100Mbps for diagnostic purposes. 1 = DISABLE, 0 = ENABLE. def 1*/
+    CSCR_F_Connect  = 1<<5,  /* Assertion of this bit forces the disconnect function to be bypassed. def 0*/
+    CSCR_Con_status = 1<<3, /* This bit indicates the status of the connection. 1 = valid connected link detected; 0 = disconnected link detected. RO def 0*/
+    CSCR_Con_status_En = 1<<2, /* Assertion of this bit configures LED1 pin to indicate connection status. def 0*/
+    CSCR_PASS_SCR = 1<<0, /* Bypass Scramble, def 0*/
+};
+
+enum Cfg9346Bits {
+    Cfg9346_Normal = 0x00,
+    Cfg9346_Autoload = 0x40,
+    Cfg9346_Programming = 0x80,
+    Cfg9346_ConfigWrite = 0xC0,
+};
+
+typedef enum {
+    CH_8139 = 0,
+    CH_8139_K,
+    CH_8139A,
+    CH_8139A_G,
+    CH_8139B,
+    CH_8130,
+    CH_8139C,
+    CH_8100,
+    CH_8100B_8139D,
+    CH_8101,
+} chip_t;
+
+enum chip_flags {
+    HasHltClk = (1 << 0),
+    HasLWake = (1 << 1),
+};
+
+#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \
+    (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22)
+#define HW_REVID_MASK    HW_REVID(1, 1, 1, 1, 1, 1, 1)
+
+#define RTL8139_PCI_REVID_8139      0x10
+#define RTL8139_PCI_REVID_8139CPLUS 0x20
+
+#define RTL8139_PCI_REVID           RTL8139_PCI_REVID_8139CPLUS
+
+/* Size is 64 * 16bit words */
+#define EEPROM_9346_ADDR_BITS 6
+#define EEPROM_9346_SIZE  (1 << EEPROM_9346_ADDR_BITS)
+#define EEPROM_9346_ADDR_MASK (EEPROM_9346_SIZE - 1)
+
+enum Chip9346Operation
+{
+    Chip9346_op_mask = 0xc0,          /* 10 zzzzzz */
+    Chip9346_op_read = 0x80,          /* 10 AAAAAA */
+    Chip9346_op_write = 0x40,         /* 01 AAAAAA D(15)..D(0) */
+    Chip9346_op_ext_mask = 0xf0,      /* 11 zzzzzz */
+    Chip9346_op_write_enable = 0x30,  /* 00 11zzzz */
+    Chip9346_op_write_all = 0x10,     /* 00 01zzzz */
+    Chip9346_op_write_disable = 0x00, /* 00 00zzzz */
+};
+
+enum Chip9346Mode
+{
+    Chip9346_none = 0,
+    Chip9346_enter_command_mode,
+    Chip9346_read_command,
+    Chip9346_data_read,      /* from output register */
+    Chip9346_data_write,     /* to input register, then to contents at specified address */
+    Chip9346_data_write_all, /* to input register, then filling contents */
+};
+
+typedef struct EEprom9346
+{
+    uint16_t contents[EEPROM_9346_SIZE];
+    int      mode;
+    uint32_t tick;
+    uint8_t  address;
+    uint16_t input;
+    uint16_t output;
+
+    uint8_t eecs;
+    uint8_t eesk;
+    uint8_t eedi;
+    uint8_t eedo;
+} EEprom9346;
+
+typedef struct RTL8139TallyCounters
+{
+    /* Tally counters */
+    uint64_t   TxOk;
+    uint64_t   RxOk;
+    uint64_t   TxERR;
+    uint32_t   RxERR;
+    uint16_t   MissPkt;
+    uint16_t   FAE;
+    uint32_t   Tx1Col;
+    uint32_t   TxMCol;
+    uint64_t   RxOkPhy;
+    uint64_t   RxOkBrd;
+    uint32_t   RxOkMul;
+    uint16_t   TxAbt;
+    uint16_t   TxUndrn;
+} RTL8139TallyCounters;
+
+/* Clears all tally counters */
+static void RTL8139TallyCounters_clear(RTL8139TallyCounters* counters);
+
+typedef struct RTL8139State {
+    PCIDevice dev;
+    uint8_t phys[8]; /* mac address */
+    uint8_t mult[8]; /* multicast mask array */
+
+    uint32_t TxStatus[4]; /* TxStatus0 in C mode*/ /* also DTCCR[0] and DTCCR[1] in C+ mode */
+    uint32_t TxAddr[4];   /* TxAddr0 */
+    uint32_t RxBuf;       /* Receive buffer */
+    uint32_t RxBufferSize;/* internal variable, receive ring buffer size in C mode */
+    uint32_t RxBufPtr;
+    uint32_t RxBufAddr;
+
+    uint16_t IntrStatus;
+    uint16_t IntrMask;
+
+    uint32_t TxConfig;
+    uint32_t RxConfig;
+    uint32_t RxMissed;
+
+    uint16_t CSCR;
+
+    uint8_t  Cfg9346;
+    uint8_t  Config0;
+    uint8_t  Config1;
+    uint8_t  Config3;
+    uint8_t  Config4;
+    uint8_t  Config5;
+
+    uint8_t  clock_enabled;
+    uint8_t  bChipCmdState;
+
+    uint16_t MultiIntr;
+
+    uint16_t BasicModeCtrl;
+    uint16_t BasicModeStatus;
+    uint16_t NWayAdvert;
+    uint16_t NWayLPAR;
+    uint16_t NWayExpansion;
+
+    uint16_t CpCmd;
+    uint8_t  TxThresh;
+
+    NICState *nic;
+    NICConf conf;
+
+    /* C ring mode */
+    uint32_t   currTxDesc;
+
+    /* C+ mode */
+    uint32_t   cplus_enabled;
+
+    uint32_t   currCPlusRxDesc;
+    uint32_t   currCPlusTxDesc;
+
+    uint32_t   RxRingAddrLO;
+    uint32_t   RxRingAddrHI;
+
+    EEprom9346 eeprom;
+
+    uint32_t   TCTR;
+    uint32_t   TimerInt;
+    int64_t    TCTR_base;
+
+    /* Tally counters */
+    RTL8139TallyCounters tally_counters;
+
+    /* Non-persistent data */
+    uint8_t   *cplus_txbuffer;
+    int        cplus_txbuffer_len;
+    int        cplus_txbuffer_offset;
+
+    /* PCI interrupt timer */
+    QEMUTimer *timer;
+    int64_t TimerExpire;
+
+    MemoryRegion bar_io;
+    MemoryRegion bar_mem;
+
+    /* Support migration to/from old versions */
+    int rtl8139_mmio_io_addr_dummy;
+} RTL8139State;
+
+/* Writes tally counters to memory via DMA */
+static void RTL8139TallyCounters_dma_write(RTL8139State *s, dma_addr_t tc_addr);
+
+static void rtl8139_set_next_tctr_time(RTL8139State *s, int64_t current_time);
+
+static void prom9346_decode_command(EEprom9346 *eeprom, uint8_t command)
+{
+    DPRINTF("eeprom command 0x%02x\n", command);
+
+    switch (command & Chip9346_op_mask)
+    {
+        case Chip9346_op_read:
+        {
+            eeprom->address = command & EEPROM_9346_ADDR_MASK;
+            eeprom->output = eeprom->contents[eeprom->address];
+            eeprom->eedo = 0;
+            eeprom->tick = 0;
+            eeprom->mode = Chip9346_data_read;
+            DPRINTF("eeprom read from address 0x%02x data=0x%04x\n",
+                eeprom->address, eeprom->output);
+        }
+        break;
+
+        case Chip9346_op_write:
+        {
+            eeprom->address = command & EEPROM_9346_ADDR_MASK;
+            eeprom->input = 0;
+            eeprom->tick = 0;
+            eeprom->mode = Chip9346_none; /* Chip9346_data_write */
+            DPRINTF("eeprom begin write to address 0x%02x\n",
+                eeprom->address);
+        }
+        break;
+        default:
+            eeprom->mode = Chip9346_none;
+            switch (command & Chip9346_op_ext_mask)
+            {
+                case Chip9346_op_write_enable:
+                    DPRINTF("eeprom write enabled\n");
+                    break;
+                case Chip9346_op_write_all:
+                    DPRINTF("eeprom begin write all\n");
+                    break;
+                case Chip9346_op_write_disable:
+                    DPRINTF("eeprom write disabled\n");
+                    break;
+            }
+            break;
+    }
+}
+
+static void prom9346_shift_clock(EEprom9346 *eeprom)
+{
+    int bit = eeprom->eedi?1:0;
+
+    ++ eeprom->tick;
+
+    DPRINTF("eeprom: tick %d eedi=%d eedo=%d\n", eeprom->tick, eeprom->eedi,
+        eeprom->eedo);
+
+    switch (eeprom->mode)
+    {
+        case Chip9346_enter_command_mode:
+            if (bit)
+            {
+                eeprom->mode = Chip9346_read_command;
+                eeprom->tick = 0;
+                eeprom->input = 0;
+                DPRINTF("eeprom: +++ synchronized, begin command read\n");
+            }
+            break;
+
+        case Chip9346_read_command:
+            eeprom->input = (eeprom->input << 1) | (bit & 1);
+            if (eeprom->tick == 8)
+            {
+                prom9346_decode_command(eeprom, eeprom->input & 0xff);
+            }
+            break;
+
+        case Chip9346_data_read:
+            eeprom->eedo = (eeprom->output & 0x8000)?1:0;
+            eeprom->output <<= 1;
+            if (eeprom->tick == 16)
+            {
+#if 1
+        // the FreeBSD drivers (rl and re) don't explicitly toggle
+        // CS between reads (or does setting Cfg9346 to 0 count too?),
+        // so we need to enter wait-for-command state here
+                eeprom->mode = Chip9346_enter_command_mode;
+                eeprom->input = 0;
+                eeprom->tick = 0;
+
+                DPRINTF("eeprom: +++ end of read, awaiting next command\n");
+#else
+        // original behaviour
+                ++eeprom->address;
+                eeprom->address &= EEPROM_9346_ADDR_MASK;
+                eeprom->output = eeprom->contents[eeprom->address];
+                eeprom->tick = 0;
+
+                DPRINTF("eeprom: +++ read next address 0x%02x data=0x%04x\n",
+                    eeprom->address, eeprom->output);
+#endif
+            }
+            break;
+
+        case Chip9346_data_write:
+            eeprom->input = (eeprom->input << 1) | (bit & 1);
+            if (eeprom->tick == 16)
+            {
+                DPRINTF("eeprom write to address 0x%02x data=0x%04x\n",
+                    eeprom->address, eeprom->input);
+
+                eeprom->contents[eeprom->address] = eeprom->input;
+                eeprom->mode = Chip9346_none; /* waiting for next command after CS cycle */
+                eeprom->tick = 0;
+                eeprom->input = 0;
+            }
+            break;
+
+        case Chip9346_data_write_all:
+            eeprom->input = (eeprom->input << 1) | (bit & 1);
+            if (eeprom->tick == 16)
+            {
+                int i;
+                for (i = 0; i < EEPROM_9346_SIZE; i++)
+                {
+                    eeprom->contents[i] = eeprom->input;
+                }
+                DPRINTF("eeprom filled with data=0x%04x\n", eeprom->input);
+
+                eeprom->mode = Chip9346_enter_command_mode;
+                eeprom->tick = 0;
+                eeprom->input = 0;
+            }
+            break;
+
+        default:
+            break;
+    }
+}
+
+static int prom9346_get_wire(RTL8139State *s)
+{
+    EEprom9346 *eeprom = &s->eeprom;
+    if (!eeprom->eecs)
+        return 0;
+
+    return eeprom->eedo;
+}
+
+/* FIXME: This should be merged into/replaced by eeprom93xx.c.  */
+static void prom9346_set_wire(RTL8139State *s, int eecs, int eesk, int eedi)
+{
+    EEprom9346 *eeprom = &s->eeprom;
+    uint8_t old_eecs = eeprom->eecs;
+    uint8_t old_eesk = eeprom->eesk;
+
+    eeprom->eecs = eecs;
+    eeprom->eesk = eesk;
+    eeprom->eedi = eedi;
+
+    DPRINTF("eeprom: +++ wires CS=%d SK=%d DI=%d DO=%d\n", eeprom->eecs,
+        eeprom->eesk, eeprom->eedi, eeprom->eedo);
+
+    if (!old_eecs && eecs)
+    {
+        /* Synchronize start */
+        eeprom->tick = 0;
+        eeprom->input = 0;
+        eeprom->output = 0;
+        eeprom->mode = Chip9346_enter_command_mode;
+
+        DPRINTF("=== eeprom: begin access, enter command mode\n");
+    }
+
+    if (!eecs)
+    {
+        DPRINTF("=== eeprom: end access\n");
+        return;
+    }
+
+    if (!old_eesk && eesk)
+    {
+        /* SK front rules */
+        prom9346_shift_clock(eeprom);
+    }
+}
+
+static void rtl8139_update_irq(RTL8139State *s)
+{
+    int isr;
+    isr = (s->IntrStatus & s->IntrMask) & 0xffff;
+
+    DPRINTF("Set IRQ to %d (%04x %04x)\n", isr ? 1 : 0, s->IntrStatus,
+        s->IntrMask);
+
+    qemu_set_irq(s->dev.irq[0], (isr != 0));
+}
+
+static int rtl8139_RxWrap(RTL8139State *s)
+{
+    /* wrapping enabled; assume 1.5k more buffer space if size < 65536 */
+    return (s->RxConfig & (1 << 7));
+}
+
+static int rtl8139_receiver_enabled(RTL8139State *s)
+{
+    return s->bChipCmdState & CmdRxEnb;
+}
+
+static int rtl8139_transmitter_enabled(RTL8139State *s)
+{
+    return s->bChipCmdState & CmdTxEnb;
+}
+
+static int rtl8139_cp_receiver_enabled(RTL8139State *s)
+{
+    return s->CpCmd & CPlusRxEnb;
+}
+
+static int rtl8139_cp_transmitter_enabled(RTL8139State *s)
+{
+    return s->CpCmd & CPlusTxEnb;
+}
+
+static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size)
+{
+    if (s->RxBufAddr + size > s->RxBufferSize)
+    {
+        int wrapped = MOD2(s->RxBufAddr + size, s->RxBufferSize);
+
+        /* write packet data */
+        if (wrapped && !(s->RxBufferSize < 65536 && rtl8139_RxWrap(s)))
+        {
+            DPRINTF(">>> rx packet wrapped in buffer at %d\n", size - wrapped);
+
+            if (size > wrapped)
+            {
+                pci_dma_write(&s->dev, s->RxBuf + s->RxBufAddr,
+                              buf, size-wrapped);
+            }
+
+            /* reset buffer pointer */
+            s->RxBufAddr = 0;
+
+            pci_dma_write(&s->dev, s->RxBuf + s->RxBufAddr,
+                          buf + (size-wrapped), wrapped);
+
+            s->RxBufAddr = wrapped;
+
+            return;
+        }
+    }
+
+    /* non-wrapping path or overwrapping enabled */
+    pci_dma_write(&s->dev, s->RxBuf + s->RxBufAddr, buf, size);
+
+    s->RxBufAddr += size;
+}
+
+#define MIN_BUF_SIZE 60
+static inline dma_addr_t rtl8139_addr64(uint32_t low, uint32_t high)
+{
+    return low | ((uint64_t)high << 32);
+}
+
+/* Workaround for buggy guest driver such as linux who allocates rx
+ * rings after the receiver were enabled. */
+static bool rtl8139_cp_rx_valid(RTL8139State *s)
+{
+    return !(s->RxRingAddrLO == 0 && s->RxRingAddrHI == 0);
+}
+
+static int rtl8139_can_receive(NetClientState *nc)
+{
+    RTL8139State *s = qemu_get_nic_opaque(nc);
+    int avail;
+
+    /* Receive (drop) packets if card is disabled.  */
+    if (!s->clock_enabled)
+      return 1;
+    if (!rtl8139_receiver_enabled(s))
+      return 1;
+
+    if (rtl8139_cp_receiver_enabled(s) && rtl8139_cp_rx_valid(s)) {
+        /* ??? Flow control not implemented in c+ mode.
+           This is a hack to work around slirp deficiencies anyway.  */
+        return 1;
+    } else {
+        avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr,
+                     s->RxBufferSize);
+        return (avail == 0 || avail >= 1514 || (s->IntrMask & RxOverflow));
+    }
+}
+
+static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t size_, int do_interrupt)
+{
+    RTL8139State *s = qemu_get_nic_opaque(nc);
+    /* size is the length of the buffer passed to the driver */
+    int size = size_;
+    const uint8_t *dot1q_buf = NULL;
+
+    uint32_t packet_header = 0;
+
+    uint8_t buf1[MIN_BUF_SIZE + VLAN_HLEN];
+    static const uint8_t broadcast_macaddr[6] =
+        { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+    DPRINTF(">>> received len=%d\n", size);
+
+    /* test if board clock is stopped */
+    if (!s->clock_enabled)
+    {
+        DPRINTF("stopped ==========================\n");
+        return -1;
+    }
+
+    /* first check if receiver is enabled */
+
+    if (!rtl8139_receiver_enabled(s))
+    {
+        DPRINTF("receiver disabled ================\n");
+        return -1;
+    }
+
+    /* XXX: check this */
+    if (s->RxConfig & AcceptAllPhys) {
+        /* promiscuous: receive all */
+        DPRINTF(">>> packet received in promiscuous mode\n");
+
+    } else {
+        if (!memcmp(buf,  broadcast_macaddr, 6)) {
+            /* broadcast address */
+            if (!(s->RxConfig & AcceptBroadcast))
+            {
+                DPRINTF(">>> broadcast packet rejected\n");
+
+                /* update tally counter */
+                ++s->tally_counters.RxERR;
+
+                return size;
+            }
+
+            packet_header |= RxBroadcast;
+
+            DPRINTF(">>> broadcast packet received\n");
+
+            /* update tally counter */
+            ++s->tally_counters.RxOkBrd;
+
+        } else if (buf[0] & 0x01) {
+            /* multicast */
+            if (!(s->RxConfig & AcceptMulticast))
+            {
+                DPRINTF(">>> multicast packet rejected\n");
+
+                /* update tally counter */
+                ++s->tally_counters.RxERR;
+
+                return size;
+            }
+
+            int mcast_idx = compute_mcast_idx(buf);
+
+            if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))))
+            {
+                DPRINTF(">>> multicast address mismatch\n");
+
+                /* update tally counter */
+                ++s->tally_counters.RxERR;
+
+                return size;
+            }
+
+            packet_header |= RxMulticast;
+
+            DPRINTF(">>> multicast packet received\n");
+
+            /* update tally counter */
+            ++s->tally_counters.RxOkMul;
+
+        } else if (s->phys[0] == buf[0] &&
+                   s->phys[1] == buf[1] &&
+                   s->phys[2] == buf[2] &&
+                   s->phys[3] == buf[3] &&
+                   s->phys[4] == buf[4] &&
+                   s->phys[5] == buf[5]) {
+            /* match */
+            if (!(s->RxConfig & AcceptMyPhys))
+            {
+                DPRINTF(">>> rejecting physical address matching packet\n");
+
+                /* update tally counter */
+                ++s->tally_counters.RxERR;
+
+                return size;
+            }
+
+            packet_header |= RxPhysical;
+
+            DPRINTF(">>> physical address matching packet received\n");
+
+            /* update tally counter */
+            ++s->tally_counters.RxOkPhy;
+
+        } else {
+
+            DPRINTF(">>> unknown packet\n");
+
+            /* update tally counter */
+            ++s->tally_counters.RxERR;
+
+            return size;
+        }
+    }
+
+    /* if too small buffer, then expand it
+     * Include some tailroom in case a vlan tag is later removed. */
+    if (size < MIN_BUF_SIZE + VLAN_HLEN) {
+        memcpy(buf1, buf, size);
+        memset(buf1 + size, 0, MIN_BUF_SIZE + VLAN_HLEN - size);
+        buf = buf1;
+        if (size < MIN_BUF_SIZE) {
+            size = MIN_BUF_SIZE;
+        }
+    }
+
+    if (rtl8139_cp_receiver_enabled(s))
+    {
+        if (!rtl8139_cp_rx_valid(s)) {
+            return size;
+        }
+
+        DPRINTF("in C+ Rx mode ================\n");
+
+        /* begin C+ receiver mode */
+
+/* w0 ownership flag */
+#define CP_RX_OWN (1<<31)
+/* w0 end of ring flag */
+#define CP_RX_EOR (1<<30)
+/* w0 bits 0...12 : buffer size */
+#define CP_RX_BUFFER_SIZE_MASK ((1<<13) - 1)
+/* w1 tag available flag */
+#define CP_RX_TAVA (1<<16)
+/* w1 bits 0...15 : VLAN tag */
+#define CP_RX_VLAN_TAG_MASK ((1<<16) - 1)
+/* w2 low  32bit of Rx buffer ptr */
+/* w3 high 32bit of Rx buffer ptr */
+
+        int descriptor = s->currCPlusRxDesc;
+        dma_addr_t cplus_rx_ring_desc;
+
+        cplus_rx_ring_desc = rtl8139_addr64(s->RxRingAddrLO, s->RxRingAddrHI);
+        cplus_rx_ring_desc += 16 * descriptor;
+
+        DPRINTF("+++ C+ mode reading RX descriptor %d from host memory at "
+            "%08x %08x = "DMA_ADDR_FMT"\n", descriptor, s->RxRingAddrHI,
+            s->RxRingAddrLO, cplus_rx_ring_desc);
+
+        uint32_t val, rxdw0,rxdw1,rxbufLO,rxbufHI;
+
+        pci_dma_read(&s->dev, cplus_rx_ring_desc, &val, 4);
+        rxdw0 = le32_to_cpu(val);
+        pci_dma_read(&s->dev, cplus_rx_ring_desc+4, &val, 4);
+        rxdw1 = le32_to_cpu(val);
+        pci_dma_read(&s->dev, cplus_rx_ring_desc+8, &val, 4);
+        rxbufLO = le32_to_cpu(val);
+        pci_dma_read(&s->dev, cplus_rx_ring_desc+12, &val, 4);
+        rxbufHI = le32_to_cpu(val);
+
+        DPRINTF("+++ C+ mode RX descriptor %d %08x %08x %08x %08x\n",
+            descriptor, rxdw0, rxdw1, rxbufLO, rxbufHI);
+
+        if (!(rxdw0 & CP_RX_OWN))
+        {
+            DPRINTF("C+ Rx mode : descriptor %d is owned by host\n",
+                descriptor);
+
+            s->IntrStatus |= RxOverflow;
+            ++s->RxMissed;
+
+            /* update tally counter */
+            ++s->tally_counters.RxERR;
+            ++s->tally_counters.MissPkt;
+
+            rtl8139_update_irq(s);
+            return size_;
+        }
+
+        uint32_t rx_space = rxdw0 & CP_RX_BUFFER_SIZE_MASK;
+
+        /* write VLAN info to descriptor variables. */
+        if (s->CpCmd & CPlusRxVLAN && be16_to_cpup((uint16_t *)
+                &buf[ETHER_ADDR_LEN * 2]) == ETH_P_8021Q) {
+            dot1q_buf = &buf[ETHER_ADDR_LEN * 2];
+            size -= VLAN_HLEN;
+            /* if too small buffer, use the tailroom added duing expansion */
+            if (size < MIN_BUF_SIZE) {
+                size = MIN_BUF_SIZE;
+            }
+
+            rxdw1 &= ~CP_RX_VLAN_TAG_MASK;
+            /* BE + ~le_to_cpu()~ + cpu_to_le() = BE */
+            rxdw1 |= CP_RX_TAVA | le16_to_cpup((uint16_t *)
+                &dot1q_buf[ETHER_TYPE_LEN]);
+
+            DPRINTF("C+ Rx mode : extracted vlan tag with tci: ""%u\n",
+                be16_to_cpup((uint16_t *)&dot1q_buf[ETHER_TYPE_LEN]));
+        } else {
+            /* reset VLAN tag flag */
+            rxdw1 &= ~CP_RX_TAVA;
+        }
+
+        /* TODO: scatter the packet over available receive ring descriptors space */
+
+        if (size+4 > rx_space)
+        {
+            DPRINTF("C+ Rx mode : descriptor %d size %d received %d + 4\n",
+                descriptor, rx_space, size);
+
+            s->IntrStatus |= RxOverflow;
+            ++s->RxMissed;
+
+            /* update tally counter */
+            ++s->tally_counters.RxERR;
+            ++s->tally_counters.MissPkt;
+
+            rtl8139_update_irq(s);
+            return size_;
+        }
+
+        dma_addr_t rx_addr = rtl8139_addr64(rxbufLO, rxbufHI);
+
+        /* receive/copy to target memory */
+        if (dot1q_buf) {
+            pci_dma_write(&s->dev, rx_addr, buf, 2 * ETHER_ADDR_LEN);
+            pci_dma_write(&s->dev, rx_addr + 2 * ETHER_ADDR_LEN,
+                          buf + 2 * ETHER_ADDR_LEN + VLAN_HLEN,
+                          size - 2 * ETHER_ADDR_LEN);
+        } else {
+            pci_dma_write(&s->dev, rx_addr, buf, size);
+        }
+
+        if (s->CpCmd & CPlusRxChkSum)
+        {
+            /* do some packet checksumming */
+        }
+
+        /* write checksum */
+        val = cpu_to_le32(crc32(0, buf, size_));
+        pci_dma_write(&s->dev, rx_addr+size, (uint8_t *)&val, 4);
+
+/* first segment of received packet flag */
+#define CP_RX_STATUS_FS (1<<29)
+/* last segment of received packet flag */
+#define CP_RX_STATUS_LS (1<<28)
+/* multicast packet flag */
+#define CP_RX_STATUS_MAR (1<<26)
+/* physical-matching packet flag */
+#define CP_RX_STATUS_PAM (1<<25)
+/* broadcast packet flag */
+#define CP_RX_STATUS_BAR (1<<24)
+/* runt packet flag */
+#define CP_RX_STATUS_RUNT (1<<19)
+/* crc error flag */
+#define CP_RX_STATUS_CRC (1<<18)
+/* IP checksum error flag */
+#define CP_RX_STATUS_IPF (1<<15)
+/* UDP checksum error flag */
+#define CP_RX_STATUS_UDPF (1<<14)
+/* TCP checksum error flag */
+#define CP_RX_STATUS_TCPF (1<<13)
+
+        /* transfer ownership to target */
+        rxdw0 &= ~CP_RX_OWN;
+
+        /* set first segment bit */
+        rxdw0 |= CP_RX_STATUS_FS;
+
+        /* set last segment bit */
+        rxdw0 |= CP_RX_STATUS_LS;
+
+        /* set received packet type flags */
+        if (packet_header & RxBroadcast)
+            rxdw0 |= CP_RX_STATUS_BAR;
+        if (packet_header & RxMulticast)
+            rxdw0 |= CP_RX_STATUS_MAR;
+        if (packet_header & RxPhysical)
+            rxdw0 |= CP_RX_STATUS_PAM;
+
+        /* set received size */
+        rxdw0 &= ~CP_RX_BUFFER_SIZE_MASK;
+        rxdw0 |= (size+4);
+
+        /* update ring data */
+        val = cpu_to_le32(rxdw0);
+        pci_dma_write(&s->dev, cplus_rx_ring_desc, (uint8_t *)&val, 4);
+        val = cpu_to_le32(rxdw1);
+        pci_dma_write(&s->dev, cplus_rx_ring_desc+4, (uint8_t *)&val, 4);
+
+        /* update tally counter */
+        ++s->tally_counters.RxOk;
+
+        /* seek to next Rx descriptor */
+        if (rxdw0 & CP_RX_EOR)
+        {
+            s->currCPlusRxDesc = 0;
+        }
+        else
+        {
+            ++s->currCPlusRxDesc;
+        }
+
+        DPRINTF("done C+ Rx mode ----------------\n");
+
+    }
+    else
+    {
+        DPRINTF("in ring Rx mode ================\n");
+
+        /* begin ring receiver mode */
+        int avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr, s->RxBufferSize);
+
+        /* if receiver buffer is empty then avail == 0 */
+
+        if (avail != 0 && size + 8 >= avail)
+        {
+            DPRINTF("rx overflow: rx buffer length %d head 0x%04x "
+                "read 0x%04x === available 0x%04x need 0x%04x\n",
+                s->RxBufferSize, s->RxBufAddr, s->RxBufPtr, avail, size + 8);
+
+            s->IntrStatus |= RxOverflow;
+            ++s->RxMissed;
+            rtl8139_update_irq(s);
+            return size_;
+        }
+
+        packet_header |= RxStatusOK;
+
+        packet_header |= (((size+4) << 16) & 0xffff0000);
+
+        /* write header */
+        uint32_t val = cpu_to_le32(packet_header);
+
+        rtl8139_write_buffer(s, (uint8_t *)&val, 4);
+
+        rtl8139_write_buffer(s, buf, size);
+
+        /* write checksum */
+        val = cpu_to_le32(crc32(0, buf, size));
+        rtl8139_write_buffer(s, (uint8_t *)&val, 4);
+
+        /* correct buffer write pointer */
+        s->RxBufAddr = MOD2((s->RxBufAddr + 3) & ~0x3, s->RxBufferSize);
+
+        /* now we can signal we have received something */
+
+        DPRINTF("received: rx buffer length %d head 0x%04x read 0x%04x\n",
+            s->RxBufferSize, s->RxBufAddr, s->RxBufPtr);
+    }
+
+    s->IntrStatus |= RxOK;
+
+    if (do_interrupt)
+    {
+        rtl8139_update_irq(s);
+    }
+
+    return size_;
+}
+
+static ssize_t rtl8139_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+    return rtl8139_do_receive(nc, buf, size, 1);
+}
+
+static void rtl8139_reset_rxring(RTL8139State *s, uint32_t bufferSize)
+{
+    s->RxBufferSize = bufferSize;
+    s->RxBufPtr  = 0;
+    s->RxBufAddr = 0;
+}
+
+static void rtl8139_reset(DeviceState *d)
+{
+    RTL8139State *s = container_of(d, RTL8139State, dev.qdev);
+    int i;
+
+    /* restore MAC address */
+    memcpy(s->phys, s->conf.macaddr.a, 6);
+
+    /* reset interrupt mask */
+    s->IntrStatus = 0;
+    s->IntrMask = 0;
+
+    rtl8139_update_irq(s);
+
+    /* mark all status registers as owned by host */
+    for (i = 0; i < 4; ++i)
+    {
+        s->TxStatus[i] = TxHostOwns;
+    }
+
+    s->currTxDesc = 0;
+    s->currCPlusRxDesc = 0;
+    s->currCPlusTxDesc = 0;
+
+    s->RxRingAddrLO = 0;
+    s->RxRingAddrHI = 0;
+
+    s->RxBuf = 0;
+
+    rtl8139_reset_rxring(s, 8192);
+
+    /* ACK the reset */
+    s->TxConfig = 0;
+
+#if 0
+//    s->TxConfig |= HW_REVID(1, 0, 0, 0, 0, 0, 0); // RTL-8139  HasHltClk
+    s->clock_enabled = 0;
+#else
+    s->TxConfig |= HW_REVID(1, 1, 1, 0, 1, 1, 0); // RTL-8139C+ HasLWake
+    s->clock_enabled = 1;
+#endif
+
+    s->bChipCmdState = CmdReset; /* RxBufEmpty bit is calculated on read from ChipCmd */;
+
+    /* set initial state data */
+    s->Config0 = 0x0; /* No boot ROM */
+    s->Config1 = 0xC; /* IO mapped and MEM mapped registers available */
+    s->Config3 = 0x1; /* fast back-to-back compatible */
+    s->Config5 = 0x0;
+
+    s->CSCR = CSCR_F_LINK_100 | CSCR_HEART_BIT | CSCR_LD;
+
+    s->CpCmd   = 0x0; /* reset C+ mode */
+    s->cplus_enabled = 0;
+
+
+//    s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation
+//    s->BasicModeCtrl = 0x2100; // 100Mbps, full duplex
+    s->BasicModeCtrl = 0x1000; // autonegotiation
+
+    s->BasicModeStatus  = 0x7809;
+    //s->BasicModeStatus |= 0x0040; /* UTP medium */
+    s->BasicModeStatus |= 0x0020; /* autonegotiation completed */
+    /* preserve link state */
+    s->BasicModeStatus |= qemu_get_queue(s->nic)->link_down ? 0 : 0x04;
+
+    s->NWayAdvert    = 0x05e1; /* all modes, full duplex */
+    s->NWayLPAR      = 0x05e1; /* all modes, full duplex */
+    s->NWayExpansion = 0x0001; /* autonegotiation supported */
+
+    /* also reset timer and disable timer interrupt */
+    s->TCTR = 0;
+    s->TimerInt = 0;
+    s->TCTR_base = 0;
+
+    /* reset tally counters */
+    RTL8139TallyCounters_clear(&s->tally_counters);
+}
+
+static void RTL8139TallyCounters_clear(RTL8139TallyCounters* counters)
+{
+    counters->TxOk = 0;
+    counters->RxOk = 0;
+    counters->TxERR = 0;
+    counters->RxERR = 0;
+    counters->MissPkt = 0;
+    counters->FAE = 0;
+    counters->Tx1Col = 0;
+    counters->TxMCol = 0;
+    counters->RxOkPhy = 0;
+    counters->RxOkBrd = 0;
+    counters->RxOkMul = 0;
+    counters->TxAbt = 0;
+    counters->TxUndrn = 0;
+}
+
+static void RTL8139TallyCounters_dma_write(RTL8139State *s, dma_addr_t tc_addr)
+{
+    RTL8139TallyCounters *tally_counters = &s->tally_counters;
+    uint16_t val16;
+    uint32_t val32;
+    uint64_t val64;
+
+    val64 = cpu_to_le64(tally_counters->TxOk);
+    pci_dma_write(&s->dev, tc_addr + 0,     (uint8_t *)&val64, 8);
+
+    val64 = cpu_to_le64(tally_counters->RxOk);
+    pci_dma_write(&s->dev, tc_addr + 8,     (uint8_t *)&val64, 8);
+
+    val64 = cpu_to_le64(tally_counters->TxERR);
+    pci_dma_write(&s->dev, tc_addr + 16,    (uint8_t *)&val64, 8);
+
+    val32 = cpu_to_le32(tally_counters->RxERR);
+    pci_dma_write(&s->dev, tc_addr + 24,    (uint8_t *)&val32, 4);
+
+    val16 = cpu_to_le16(tally_counters->MissPkt);
+    pci_dma_write(&s->dev, tc_addr + 28,    (uint8_t *)&val16, 2);
+
+    val16 = cpu_to_le16(tally_counters->FAE);
+    pci_dma_write(&s->dev, tc_addr + 30,    (uint8_t *)&val16, 2);
+
+    val32 = cpu_to_le32(tally_counters->Tx1Col);
+    pci_dma_write(&s->dev, tc_addr + 32,    (uint8_t *)&val32, 4);
+
+    val32 = cpu_to_le32(tally_counters->TxMCol);
+    pci_dma_write(&s->dev, tc_addr + 36,    (uint8_t *)&val32, 4);
+
+    val64 = cpu_to_le64(tally_counters->RxOkPhy);
+    pci_dma_write(&s->dev, tc_addr + 40,    (uint8_t *)&val64, 8);
+
+    val64 = cpu_to_le64(tally_counters->RxOkBrd);
+    pci_dma_write(&s->dev, tc_addr + 48,    (uint8_t *)&val64, 8);
+
+    val32 = cpu_to_le32(tally_counters->RxOkMul);
+    pci_dma_write(&s->dev, tc_addr + 56,    (uint8_t *)&val32, 4);
+
+    val16 = cpu_to_le16(tally_counters->TxAbt);
+    pci_dma_write(&s->dev, tc_addr + 60,    (uint8_t *)&val16, 2);
+
+    val16 = cpu_to_le16(tally_counters->TxUndrn);
+    pci_dma_write(&s->dev, tc_addr + 62,    (uint8_t *)&val16, 2);
+}
+
+/* Loads values of tally counters from VM state file */
+
+static const VMStateDescription vmstate_tally_counters = {
+    .name = "tally_counters",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT64(TxOk, RTL8139TallyCounters),
+        VMSTATE_UINT64(RxOk, RTL8139TallyCounters),
+        VMSTATE_UINT64(TxERR, RTL8139TallyCounters),
+        VMSTATE_UINT32(RxERR, RTL8139TallyCounters),
+        VMSTATE_UINT16(MissPkt, RTL8139TallyCounters),
+        VMSTATE_UINT16(FAE, RTL8139TallyCounters),
+        VMSTATE_UINT32(Tx1Col, RTL8139TallyCounters),
+        VMSTATE_UINT32(TxMCol, RTL8139TallyCounters),
+        VMSTATE_UINT64(RxOkPhy, RTL8139TallyCounters),
+        VMSTATE_UINT64(RxOkBrd, RTL8139TallyCounters),
+        VMSTATE_UINT16(TxAbt, RTL8139TallyCounters),
+        VMSTATE_UINT16(TxUndrn, RTL8139TallyCounters),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void rtl8139_ChipCmd_write(RTL8139State *s, uint32_t val)
+{
+    val &= 0xff;
+
+    DPRINTF("ChipCmd write val=0x%08x\n", val);
+
+    if (val & CmdReset)
+    {
+        DPRINTF("ChipCmd reset\n");
+        rtl8139_reset(&s->dev.qdev);
+    }
+    if (val & CmdRxEnb)
+    {
+        DPRINTF("ChipCmd enable receiver\n");
+
+        s->currCPlusRxDesc = 0;
+    }
+    if (val & CmdTxEnb)
+    {
+        DPRINTF("ChipCmd enable transmitter\n");
+
+        s->currCPlusTxDesc = 0;
+    }
+
+    /* mask unwritable bits */
+    val = SET_MASKED(val, 0xe3, s->bChipCmdState);
+
+    /* Deassert reset pin before next read */
+    val &= ~CmdReset;
+
+    s->bChipCmdState = val;
+}
+
+static int rtl8139_RxBufferEmpty(RTL8139State *s)
+{
+    int unread = MOD2(s->RxBufferSize + s->RxBufAddr - s->RxBufPtr, s->RxBufferSize);
+
+    if (unread != 0)
+    {
+        DPRINTF("receiver buffer data available 0x%04x\n", unread);
+        return 0;
+    }
+
+    DPRINTF("receiver buffer is empty\n");
+
+    return 1;
+}
+
+static uint32_t rtl8139_ChipCmd_read(RTL8139State *s)
+{
+    uint32_t ret = s->bChipCmdState;
+
+    if (rtl8139_RxBufferEmpty(s))
+        ret |= RxBufEmpty;
+
+    DPRINTF("ChipCmd read val=0x%04x\n", ret);
+
+    return ret;
+}
+
+static void rtl8139_CpCmd_write(RTL8139State *s, uint32_t val)
+{
+    val &= 0xffff;
+
+    DPRINTF("C+ command register write(w) val=0x%04x\n", val);
+
+    s->cplus_enabled = 1;
+
+    /* mask unwritable bits */
+    val = SET_MASKED(val, 0xff84, s->CpCmd);
+
+    s->CpCmd = val;
+}
+
+static uint32_t rtl8139_CpCmd_read(RTL8139State *s)
+{
+    uint32_t ret = s->CpCmd;
+
+    DPRINTF("C+ command register read(w) val=0x%04x\n", ret);
+
+    return ret;
+}
+
+static void rtl8139_IntrMitigate_write(RTL8139State *s, uint32_t val)
+{
+    DPRINTF("C+ IntrMitigate register write(w) val=0x%04x\n", val);
+}
+
+static uint32_t rtl8139_IntrMitigate_read(RTL8139State *s)
+{
+    uint32_t ret = 0;
+
+    DPRINTF("C+ IntrMitigate register read(w) val=0x%04x\n", ret);
+
+    return ret;
+}
+
+static int rtl8139_config_writable(RTL8139State *s)
+{
+    if ((s->Cfg9346 & Chip9346_op_mask) == Cfg9346_ConfigWrite)
+    {
+        return 1;
+    }
+
+    DPRINTF("Configuration registers are write-protected\n");
+
+    return 0;
+}
+
+static void rtl8139_BasicModeCtrl_write(RTL8139State *s, uint32_t val)
+{
+    val &= 0xffff;
+
+    DPRINTF("BasicModeCtrl register write(w) val=0x%04x\n", val);
+
+    /* mask unwritable bits */
+    uint32_t mask = 0x4cff;
+
+    if (1 || !rtl8139_config_writable(s))
+    {
+        /* Speed setting and autonegotiation enable bits are read-only */
+        mask |= 0x3000;
+        /* Duplex mode setting is read-only */
+        mask |= 0x0100;
+    }
+
+    val = SET_MASKED(val, mask, s->BasicModeCtrl);
+
+    s->BasicModeCtrl = val;
+}
+
+static uint32_t rtl8139_BasicModeCtrl_read(RTL8139State *s)
+{
+    uint32_t ret = s->BasicModeCtrl;
+
+    DPRINTF("BasicModeCtrl register read(w) val=0x%04x\n", ret);
+
+    return ret;
+}
+
+static void rtl8139_BasicModeStatus_write(RTL8139State *s, uint32_t val)
+{
+    val &= 0xffff;
+
+    DPRINTF("BasicModeStatus register write(w) val=0x%04x\n", val);
+
+    /* mask unwritable bits */
+    val = SET_MASKED(val, 0xff3f, s->BasicModeStatus);
+
+    s->BasicModeStatus = val;
+}
+
+static uint32_t rtl8139_BasicModeStatus_read(RTL8139State *s)
+{
+    uint32_t ret = s->BasicModeStatus;
+
+    DPRINTF("BasicModeStatus register read(w) val=0x%04x\n", ret);
+
+    return ret;
+}
+
+static void rtl8139_Cfg9346_write(RTL8139State *s, uint32_t val)
+{
+    val &= 0xff;
+
+    DPRINTF("Cfg9346 write val=0x%02x\n", val);
+
+    /* mask unwritable bits */
+    val = SET_MASKED(val, 0x31, s->Cfg9346);
+
+    uint32_t opmode = val & 0xc0;
+    uint32_t eeprom_val = val & 0xf;
+
+    if (opmode == 0x80) {
+        /* eeprom access */
+        int eecs = (eeprom_val & 0x08)?1:0;
+        int eesk = (eeprom_val & 0x04)?1:0;
+        int eedi = (eeprom_val & 0x02)?1:0;
+        prom9346_set_wire(s, eecs, eesk, eedi);
+    } else if (opmode == 0x40) {
+        /* Reset.  */
+        val = 0;
+        rtl8139_reset(&s->dev.qdev);
+    }
+
+    s->Cfg9346 = val;
+}
+
+static uint32_t rtl8139_Cfg9346_read(RTL8139State *s)
+{
+    uint32_t ret = s->Cfg9346;
+
+    uint32_t opmode = ret & 0xc0;
+
+    if (opmode == 0x80)
+    {
+        /* eeprom access */
+        int eedo = prom9346_get_wire(s);
+        if (eedo)
+        {
+            ret |=  0x01;
+        }
+        else
+        {
+            ret &= ~0x01;
+        }
+    }
+
+    DPRINTF("Cfg9346 read val=0x%02x\n", ret);
+
+    return ret;
+}
+
+static void rtl8139_Config0_write(RTL8139State *s, uint32_t val)
+{
+    val &= 0xff;
+
+    DPRINTF("Config0 write val=0x%02x\n", val);
+
+    if (!rtl8139_config_writable(s)) {
+        return;
+    }
+
+    /* mask unwritable bits */
+    val = SET_MASKED(val, 0xf8, s->Config0);
+
+    s->Config0 = val;
+}
+
+static uint32_t rtl8139_Config0_read(RTL8139State *s)
+{
+    uint32_t ret = s->Config0;
+
+    DPRINTF("Config0 read val=0x%02x\n", ret);
+
+    return ret;
+}
+
+static void rtl8139_Config1_write(RTL8139State *s, uint32_t val)
+{
+    val &= 0xff;
+
+    DPRINTF("Config1 write val=0x%02x\n", val);
+
+    if (!rtl8139_config_writable(s)) {
+        return;
+    }
+
+    /* mask unwritable bits */
+    val = SET_MASKED(val, 0xC, s->Config1);
+
+    s->Config1 = val;
+}
+
+static uint32_t rtl8139_Config1_read(RTL8139State *s)
+{
+    uint32_t ret = s->Config1;
+
+    DPRINTF("Config1 read val=0x%02x\n", ret);
+
+    return ret;
+}
+
+static void rtl8139_Config3_write(RTL8139State *s, uint32_t val)
+{
+    val &= 0xff;
+
+    DPRINTF("Config3 write val=0x%02x\n", val);
+
+    if (!rtl8139_config_writable(s)) {
+        return;
+    }
+
+    /* mask unwritable bits */
+    val = SET_MASKED(val, 0x8F, s->Config3);
+
+    s->Config3 = val;
+}
+
+static uint32_t rtl8139_Config3_read(RTL8139State *s)
+{
+    uint32_t ret = s->Config3;
+
+    DPRINTF("Config3 read val=0x%02x\n", ret);
+
+    return ret;
+}
+
+static void rtl8139_Config4_write(RTL8139State *s, uint32_t val)
+{
+    val &= 0xff;
+
+    DPRINTF("Config4 write val=0x%02x\n", val);
+
+    if (!rtl8139_config_writable(s)) {
+        return;
+    }
+
+    /* mask unwritable bits */
+    val = SET_MASKED(val, 0x0a, s->Config4);
+
+    s->Config4 = val;
+}
+
+static uint32_t rtl8139_Config4_read(RTL8139State *s)
+{
+    uint32_t ret = s->Config4;
+
+    DPRINTF("Config4 read val=0x%02x\n", ret);
+
+    return ret;
+}
+
+static void rtl8139_Config5_write(RTL8139State *s, uint32_t val)
+{
+    val &= 0xff;
+
+    DPRINTF("Config5 write val=0x%02x\n", val);
+
+    /* mask unwritable bits */
+    val = SET_MASKED(val, 0x80, s->Config5);
+
+    s->Config5 = val;
+}
+
+static uint32_t rtl8139_Config5_read(RTL8139State *s)
+{
+    uint32_t ret = s->Config5;
+
+    DPRINTF("Config5 read val=0x%02x\n", ret);
+
+    return ret;
+}
+
+static void rtl8139_TxConfig_write(RTL8139State *s, uint32_t val)
+{
+    if (!rtl8139_transmitter_enabled(s))
+    {
+        DPRINTF("transmitter disabled; no TxConfig write val=0x%08x\n", val);
+        return;
+    }
+
+    DPRINTF("TxConfig write val=0x%08x\n", val);
+
+    val = SET_MASKED(val, TxVersionMask | 0x8070f80f, s->TxConfig);
+
+    s->TxConfig = val;
+}
+
+static void rtl8139_TxConfig_writeb(RTL8139State *s, uint32_t val)
+{
+    DPRINTF("RTL8139C TxConfig via write(b) val=0x%02x\n", val);
+
+    uint32_t tc = s->TxConfig;
+    tc &= 0xFFFFFF00;
+    tc |= (val & 0x000000FF);
+    rtl8139_TxConfig_write(s, tc);
+}
+
+static uint32_t rtl8139_TxConfig_read(RTL8139State *s)
+{
+    uint32_t ret = s->TxConfig;
+
+    DPRINTF("TxConfig read val=0x%04x\n", ret);
+
+    return ret;
+}
+
+static void rtl8139_RxConfig_write(RTL8139State *s, uint32_t val)
+{
+    DPRINTF("RxConfig write val=0x%08x\n", val);
+
+    /* mask unwritable bits */
+    val = SET_MASKED(val, 0xf0fc0040, s->RxConfig);
+
+    s->RxConfig = val;
+
+    /* reset buffer size and read/write pointers */
+    rtl8139_reset_rxring(s, 8192 << ((s->RxConfig >> 11) & 0x3));
+
+    DPRINTF("RxConfig write reset buffer size to %d\n", s->RxBufferSize);
+}
+
+static uint32_t rtl8139_RxConfig_read(RTL8139State *s)
+{
+    uint32_t ret = s->RxConfig;
+
+    DPRINTF("RxConfig read val=0x%08x\n", ret);
+
+    return ret;
+}
+
+static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size,
+    int do_interrupt, const uint8_t *dot1q_buf)
+{
+    struct iovec *iov = NULL;
+
+    if (!size)
+    {
+        DPRINTF("+++ empty ethernet frame\n");
+        return;
+    }
+
+    if (dot1q_buf && size >= ETHER_ADDR_LEN * 2) {
+        iov = (struct iovec[3]) {
+            { .iov_base = buf, .iov_len = ETHER_ADDR_LEN * 2 },
+            { .iov_base = (void *) dot1q_buf, .iov_len = VLAN_HLEN },
+            { .iov_base = buf + ETHER_ADDR_LEN * 2,
+                .iov_len = size - ETHER_ADDR_LEN * 2 },
+        };
+    }
+
+    if (TxLoopBack == (s->TxConfig & TxLoopBack))
+    {
+        size_t buf2_size;
+        uint8_t *buf2;
+
+        if (iov) {
+            buf2_size = iov_size(iov, 3);
+            buf2 = g_malloc(buf2_size);
+            iov_to_buf(iov, 3, 0, buf2, buf2_size);
+            buf = buf2;
+        }
+
+        DPRINTF("+++ transmit loopback mode\n");
+        rtl8139_do_receive(qemu_get_queue(s->nic), buf, size, do_interrupt);
+
+        if (iov) {
+            g_free(buf2);
+        }
+    }
+    else
+    {
+        if (iov) {
+            qemu_sendv_packet(qemu_get_queue(s->nic), iov, 3);
+        } else {
+            qemu_send_packet(qemu_get_queue(s->nic), buf, size);
+        }
+    }
+}
+
+static int rtl8139_transmit_one(RTL8139State *s, int descriptor)
+{
+    if (!rtl8139_transmitter_enabled(s))
+    {
+        DPRINTF("+++ cannot transmit from descriptor %d: transmitter "
+            "disabled\n", descriptor);
+        return 0;
+    }
+
+    if (s->TxStatus[descriptor] & TxHostOwns)
+    {
+        DPRINTF("+++ cannot transmit from descriptor %d: owned by host "
+            "(%08x)\n", descriptor, s->TxStatus[descriptor]);
+        return 0;
+    }
+
+    DPRINTF("+++ transmitting from descriptor %d\n", descriptor);
+
+    int txsize = s->TxStatus[descriptor] & 0x1fff;
+    uint8_t txbuffer[0x2000];
+
+    DPRINTF("+++ transmit reading %d bytes from host memory at 0x%08x\n",
+        txsize, s->TxAddr[descriptor]);
+
+    pci_dma_read(&s->dev, s->TxAddr[descriptor], txbuffer, txsize);
+
+    /* Mark descriptor as transferred */
+    s->TxStatus[descriptor] |= TxHostOwns;
+    s->TxStatus[descriptor] |= TxStatOK;
+
+    rtl8139_transfer_frame(s, txbuffer, txsize, 0, NULL);
+
+    DPRINTF("+++ transmitted %d bytes from descriptor %d\n", txsize,
+        descriptor);
+
+    /* update interrupt */
+    s->IntrStatus |= TxOK;
+    rtl8139_update_irq(s);
+
+    return 1;
+}
+
+/* structures and macros for task offloading */
+typedef struct ip_header
+{
+    uint8_t  ip_ver_len;    /* version and header length */
+    uint8_t  ip_tos;        /* type of service */
+    uint16_t ip_len;        /* total length */
+    uint16_t ip_id;         /* identification */
+    uint16_t ip_off;        /* fragment offset field */
+    uint8_t  ip_ttl;        /* time to live */
+    uint8_t  ip_p;          /* protocol */
+    uint16_t ip_sum;        /* checksum */
+    uint32_t ip_src,ip_dst; /* source and dest address */
+} ip_header;
+
+#define IP_HEADER_VERSION_4 4
+#define IP_HEADER_VERSION(ip) ((ip->ip_ver_len >> 4)&0xf)
+#define IP_HEADER_LENGTH(ip) (((ip->ip_ver_len)&0xf) << 2)
+
+typedef struct tcp_header
+{
+    uint16_t th_sport;         /* source port */
+    uint16_t th_dport;         /* destination port */
+    uint32_t th_seq;                   /* sequence number */
+    uint32_t th_ack;                   /* acknowledgement number */
+    uint16_t th_offset_flags; /* data offset, reserved 6 bits, TCP protocol flags */
+    uint16_t th_win;                   /* window */
+    uint16_t th_sum;                   /* checksum */
+    uint16_t th_urp;                   /* urgent pointer */
+} tcp_header;
+
+typedef struct udp_header
+{
+    uint16_t uh_sport; /* source port */
+    uint16_t uh_dport; /* destination port */
+    uint16_t uh_ulen;  /* udp length */
+    uint16_t uh_sum;   /* udp checksum */
+} udp_header;
+
+typedef struct ip_pseudo_header
+{
+    uint32_t ip_src;
+    uint32_t ip_dst;
+    uint8_t  zeros;
+    uint8_t  ip_proto;
+    uint16_t ip_payload;
+} ip_pseudo_header;
+
+#define IP_PROTO_TCP 6
+#define IP_PROTO_UDP 17
+
+#define TCP_HEADER_DATA_OFFSET(tcp) (((be16_to_cpu(tcp->th_offset_flags) >> 12)&0xf) << 2)
+#define TCP_FLAGS_ONLY(flags) ((flags)&0x3f)
+#define TCP_HEADER_FLAGS(tcp) TCP_FLAGS_ONLY(be16_to_cpu(tcp->th_offset_flags))
+
+#define TCP_HEADER_CLEAR_FLAGS(tcp, off) ((tcp)->th_offset_flags &= cpu_to_be16(~TCP_FLAGS_ONLY(off)))
+
+#define TCP_FLAG_FIN  0x01
+#define TCP_FLAG_PUSH 0x08
+
+/* produces ones' complement sum of data */
+static uint16_t ones_complement_sum(uint8_t *data, size_t len)
+{
+    uint32_t result = 0;
+
+    for (; len > 1; data+=2, len-=2)
+    {
+        result += *(uint16_t*)data;
+    }
+
+    /* add the remainder byte */
+    if (len)
+    {
+        uint8_t odd[2] = {*data, 0};
+        result += *(uint16_t*)odd;
+    }
+
+    while (result>>16)
+        result = (result & 0xffff) + (result >> 16);
+
+    return result;
+}
+
+static uint16_t ip_checksum(void *data, size_t len)
+{
+    return ~ones_complement_sum((uint8_t*)data, len);
+}
+
+static int rtl8139_cplus_transmit_one(RTL8139State *s)
+{
+    if (!rtl8139_transmitter_enabled(s))
+    {
+        DPRINTF("+++ C+ mode: transmitter disabled\n");
+        return 0;
+    }
+
+    if (!rtl8139_cp_transmitter_enabled(s))
+    {
+        DPRINTF("+++ C+ mode: C+ transmitter disabled\n");
+        return 0 ;
+    }
+
+    int descriptor = s->currCPlusTxDesc;
+
+    dma_addr_t cplus_tx_ring_desc = rtl8139_addr64(s->TxAddr[0], s->TxAddr[1]);
+
+    /* Normal priority ring */
+    cplus_tx_ring_desc += 16 * descriptor;
+
+    DPRINTF("+++ C+ mode reading TX descriptor %d from host memory at "
+        "%08x %08x = 0x"DMA_ADDR_FMT"\n", descriptor, s->TxAddr[1],
+        s->TxAddr[0], cplus_tx_ring_desc);
+
+    uint32_t val, txdw0,txdw1,txbufLO,txbufHI;
+
+    pci_dma_read(&s->dev, cplus_tx_ring_desc,    (uint8_t *)&val, 4);
+    txdw0 = le32_to_cpu(val);
+    pci_dma_read(&s->dev, cplus_tx_ring_desc+4,  (uint8_t *)&val, 4);
+    txdw1 = le32_to_cpu(val);
+    pci_dma_read(&s->dev, cplus_tx_ring_desc+8,  (uint8_t *)&val, 4);
+    txbufLO = le32_to_cpu(val);
+    pci_dma_read(&s->dev, cplus_tx_ring_desc+12, (uint8_t *)&val, 4);
+    txbufHI = le32_to_cpu(val);
+
+    DPRINTF("+++ C+ mode TX descriptor %d %08x %08x %08x %08x\n", descriptor,
+        txdw0, txdw1, txbufLO, txbufHI);
+
+/* w0 ownership flag */
+#define CP_TX_OWN (1<<31)
+/* w0 end of ring flag */
+#define CP_TX_EOR (1<<30)
+/* first segment of received packet flag */
+#define CP_TX_FS (1<<29)
+/* last segment of received packet flag */
+#define CP_TX_LS (1<<28)
+/* large send packet flag */
+#define CP_TX_LGSEN (1<<27)
+/* large send MSS mask, bits 16...25 */
+#define CP_TC_LGSEN_MSS_MASK ((1 << 12) - 1)
+
+/* IP checksum offload flag */
+#define CP_TX_IPCS (1<<18)
+/* UDP checksum offload flag */
+#define CP_TX_UDPCS (1<<17)
+/* TCP checksum offload flag */
+#define CP_TX_TCPCS (1<<16)
+
+/* w0 bits 0...15 : buffer size */
+#define CP_TX_BUFFER_SIZE (1<<16)
+#define CP_TX_BUFFER_SIZE_MASK (CP_TX_BUFFER_SIZE - 1)
+/* w1 add tag flag */
+#define CP_TX_TAGC (1<<17)
+/* w1 bits 0...15 : VLAN tag (big endian) */
+#define CP_TX_VLAN_TAG_MASK ((1<<16) - 1)
+/* w2 low  32bit of Rx buffer ptr */
+/* w3 high 32bit of Rx buffer ptr */
+
+/* set after transmission */
+/* FIFO underrun flag */
+#define CP_TX_STATUS_UNF (1<<25)
+/* transmit error summary flag, valid if set any of three below */
+#define CP_TX_STATUS_TES (1<<23)
+/* out-of-window collision flag */
+#define CP_TX_STATUS_OWC (1<<22)
+/* link failure flag */
+#define CP_TX_STATUS_LNKF (1<<21)
+/* excessive collisions flag */
+#define CP_TX_STATUS_EXC (1<<20)
+
+    if (!(txdw0 & CP_TX_OWN))
+    {
+        DPRINTF("C+ Tx mode : descriptor %d is owned by host\n", descriptor);
+        return 0 ;
+    }
+
+    DPRINTF("+++ C+ Tx mode : transmitting from descriptor %d\n", descriptor);
+
+    if (txdw0 & CP_TX_FS)
+    {
+        DPRINTF("+++ C+ Tx mode : descriptor %d is first segment "
+            "descriptor\n", descriptor);
+
+        /* reset internal buffer offset */
+        s->cplus_txbuffer_offset = 0;
+    }
+
+    int txsize = txdw0 & CP_TX_BUFFER_SIZE_MASK;
+    dma_addr_t tx_addr = rtl8139_addr64(txbufLO, txbufHI);
+
+    /* make sure we have enough space to assemble the packet */
+    if (!s->cplus_txbuffer)
+    {
+        s->cplus_txbuffer_len = CP_TX_BUFFER_SIZE;
+        s->cplus_txbuffer = g_malloc(s->cplus_txbuffer_len);
+        s->cplus_txbuffer_offset = 0;
+
+        DPRINTF("+++ C+ mode transmission buffer allocated space %d\n",
+            s->cplus_txbuffer_len);
+    }
+
+    if (s->cplus_txbuffer_offset + txsize >= s->cplus_txbuffer_len)
+    {
+        /* The spec didn't tell the maximum size, stick to CP_TX_BUFFER_SIZE */
+        txsize = s->cplus_txbuffer_len - s->cplus_txbuffer_offset;
+        DPRINTF("+++ C+ mode transmission buffer overrun, truncated descriptor"
+                "length to %d\n", txsize);
+    }
+
+    if (!s->cplus_txbuffer)
+    {
+        /* out of memory */
+
+        DPRINTF("+++ C+ mode transmiter failed to reallocate %d bytes\n",
+            s->cplus_txbuffer_len);
+
+        /* update tally counter */
+        ++s->tally_counters.TxERR;
+        ++s->tally_counters.TxAbt;
+
+        return 0;
+    }
+
+    /* append more data to the packet */
+
+    DPRINTF("+++ C+ mode transmit reading %d bytes from host memory at "
+            DMA_ADDR_FMT" to offset %d\n", txsize, tx_addr,
+            s->cplus_txbuffer_offset);
+
+    pci_dma_read(&s->dev, tx_addr,
+                 s->cplus_txbuffer + s->cplus_txbuffer_offset, txsize);
+    s->cplus_txbuffer_offset += txsize;
+
+    /* seek to next Rx descriptor */
+    if (txdw0 & CP_TX_EOR)
+    {
+        s->currCPlusTxDesc = 0;
+    }
+    else
+    {
+        ++s->currCPlusTxDesc;
+        if (s->currCPlusTxDesc >= 64)
+            s->currCPlusTxDesc = 0;
+    }
+
+    /* transfer ownership to target */
+    txdw0 &= ~CP_RX_OWN;
+
+    /* reset error indicator bits */
+    txdw0 &= ~CP_TX_STATUS_UNF;
+    txdw0 &= ~CP_TX_STATUS_TES;
+    txdw0 &= ~CP_TX_STATUS_OWC;
+    txdw0 &= ~CP_TX_STATUS_LNKF;
+    txdw0 &= ~CP_TX_STATUS_EXC;
+
+    /* update ring data */
+    val = cpu_to_le32(txdw0);
+    pci_dma_write(&s->dev, cplus_tx_ring_desc, (uint8_t *)&val, 4);
+
+    /* Now decide if descriptor being processed is holding the last segment of packet */
+    if (txdw0 & CP_TX_LS)
+    {
+        uint8_t dot1q_buffer_space[VLAN_HLEN];
+        uint16_t *dot1q_buffer;
+
+        DPRINTF("+++ C+ Tx mode : descriptor %d is last segment descriptor\n",
+            descriptor);
+
+        /* can transfer fully assembled packet */
+
+        uint8_t *saved_buffer  = s->cplus_txbuffer;
+        int      saved_size    = s->cplus_txbuffer_offset;
+        int      saved_buffer_len = s->cplus_txbuffer_len;
+
+        /* create vlan tag */
+        if (txdw1 & CP_TX_TAGC) {
+            /* the vlan tag is in BE byte order in the descriptor
+             * BE + le_to_cpu() + ~swap()~ = cpu */
+            DPRINTF("+++ C+ Tx mode : inserting vlan tag with ""tci: %u\n",
+                bswap16(txdw1 & CP_TX_VLAN_TAG_MASK));
+
+            dot1q_buffer = (uint16_t *) dot1q_buffer_space;
+            dot1q_buffer[0] = cpu_to_be16(ETH_P_8021Q);
+            /* BE + le_to_cpu() + ~cpu_to_le()~ = BE */
+            dot1q_buffer[1] = cpu_to_le16(txdw1 & CP_TX_VLAN_TAG_MASK);
+        } else {
+            dot1q_buffer = NULL;
+        }
+
+        /* reset the card space to protect from recursive call */
+        s->cplus_txbuffer = NULL;
+        s->cplus_txbuffer_offset = 0;
+        s->cplus_txbuffer_len = 0;
+
+        if (txdw0 & (CP_TX_IPCS | CP_TX_UDPCS | CP_TX_TCPCS | CP_TX_LGSEN))
+        {
+            DPRINTF("+++ C+ mode offloaded task checksum\n");
+
+            /* ip packet header */
+            ip_header *ip = NULL;
+            int hlen = 0;
+            uint8_t  ip_protocol = 0;
+            uint16_t ip_data_len = 0;
+
+            uint8_t *eth_payload_data = NULL;
+            size_t   eth_payload_len  = 0;
+
+            int proto = be16_to_cpu(*(uint16_t *)(saved_buffer + 12));
+            if (proto == ETH_P_IP)
+            {
+                DPRINTF("+++ C+ mode has IP packet\n");
+
+                /* not aligned */
+                eth_payload_data = saved_buffer + ETH_HLEN;
+                eth_payload_len  = saved_size   - ETH_HLEN;
+
+                ip = (ip_header*)eth_payload_data;
+
+                if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
+                    DPRINTF("+++ C+ mode packet has bad IP version %d "
+                        "expected %d\n", IP_HEADER_VERSION(ip),
+                        IP_HEADER_VERSION_4);
+                    ip = NULL;
+                } else {
+                    hlen = IP_HEADER_LENGTH(ip);
+                    ip_protocol = ip->ip_p;
+                    ip_data_len = be16_to_cpu(ip->ip_len) - hlen;
+                }
+            }
+
+            if (ip)
+            {
+                if (txdw0 & CP_TX_IPCS)
+                {
+                    DPRINTF("+++ C+ mode need IP checksum\n");
+
+                    if (hlen<sizeof(ip_header) || hlen>eth_payload_len) {/* min header length */
+                        /* bad packet header len */
+                        /* or packet too short */
+                    }
+                    else
+                    {
+                        ip->ip_sum = 0;
+                        ip->ip_sum = ip_checksum(ip, hlen);
+                        DPRINTF("+++ C+ mode IP header len=%d checksum=%04x\n",
+                            hlen, ip->ip_sum);
+                    }
+                }
+
+                if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP)
+                {
+                    int large_send_mss = (txdw0 >> 16) & CP_TC_LGSEN_MSS_MASK;
+
+                    DPRINTF("+++ C+ mode offloaded task TSO MTU=%d IP data %d "
+                        "frame data %d specified MSS=%d\n", ETH_MTU,
+                        ip_data_len, saved_size - ETH_HLEN, large_send_mss);
+
+                    int tcp_send_offset = 0;
+                    int send_count = 0;
+
+                    /* maximum IP header length is 60 bytes */
+                    uint8_t saved_ip_header[60];
+
+                    /* save IP header template; data area is used in tcp checksum calculation */
+                    memcpy(saved_ip_header, eth_payload_data, hlen);
+
+                    /* a placeholder for checksum calculation routine in tcp case */
+                    uint8_t *data_to_checksum     = eth_payload_data + hlen - 12;
+                    //                    size_t   data_to_checksum_len = eth_payload_len  - hlen + 12;
+
+                    /* pointer to TCP header */
+                    tcp_header *p_tcp_hdr = (tcp_header*)(eth_payload_data + hlen);
+
+                    int tcp_hlen = TCP_HEADER_DATA_OFFSET(p_tcp_hdr);
+
+                    /* ETH_MTU = ip header len + tcp header len + payload */
+                    int tcp_data_len = ip_data_len - tcp_hlen;
+                    int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen;
+
+                    DPRINTF("+++ C+ mode TSO IP data len %d TCP hlen %d TCP "
+                        "data len %d TCP chunk size %d\n", ip_data_len,
+                        tcp_hlen, tcp_data_len, tcp_chunk_size);
+
+                    /* note the cycle below overwrites IP header data,
+                       but restores it from saved_ip_header before sending packet */
+
+                    int is_last_frame = 0;
+
+                    for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += tcp_chunk_size)
+                    {
+                        uint16_t chunk_size = tcp_chunk_size;
+
+                        /* check if this is the last frame */
+                        if (tcp_send_offset + tcp_chunk_size >= tcp_data_len)
+                        {
+                            is_last_frame = 1;
+                            chunk_size = tcp_data_len - tcp_send_offset;
+                        }
+
+                        DPRINTF("+++ C+ mode TSO TCP seqno %08x\n",
+                            be32_to_cpu(p_tcp_hdr->th_seq));
+
+                        /* add 4 TCP pseudoheader fields */
+                        /* copy IP source and destination fields */
+                        memcpy(data_to_checksum, saved_ip_header + 12, 8);
+
+                        DPRINTF("+++ C+ mode TSO calculating TCP checksum for "
+                            "packet with %d bytes data\n", tcp_hlen +
+                            chunk_size);
+
+                        if (tcp_send_offset)
+                        {
+                            memcpy((uint8_t*)p_tcp_hdr + tcp_hlen, (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size);
+                        }
+
+                        /* keep PUSH and FIN flags only for the last frame */
+                        if (!is_last_frame)
+                        {
+                            TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TCP_FLAG_PUSH|TCP_FLAG_FIN);
+                        }
+
+                        /* recalculate TCP checksum */
+                        ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
+                        p_tcpip_hdr->zeros      = 0;
+                        p_tcpip_hdr->ip_proto   = IP_PROTO_TCP;
+                        p_tcpip_hdr->ip_payload = cpu_to_be16(tcp_hlen + chunk_size);
+
+                        p_tcp_hdr->th_sum = 0;
+
+                        int tcp_checksum = ip_checksum(data_to_checksum, tcp_hlen + chunk_size + 12);
+                        DPRINTF("+++ C+ mode TSO TCP checksum %04x\n",
+                            tcp_checksum);
+
+                        p_tcp_hdr->th_sum = tcp_checksum;
+
+                        /* restore IP header */
+                        memcpy(eth_payload_data, saved_ip_header, hlen);
+
+                        /* set IP data length and recalculate IP checksum */
+                        ip->ip_len = cpu_to_be16(hlen + tcp_hlen + chunk_size);
+
+                        /* increment IP id for subsequent frames */
+                        ip->ip_id = cpu_to_be16(tcp_send_offset/tcp_chunk_size + be16_to_cpu(ip->ip_id));
+
+                        ip->ip_sum = 0;
+                        ip->ip_sum = ip_checksum(eth_payload_data, hlen);
+                        DPRINTF("+++ C+ mode TSO IP header len=%d "
+                            "checksum=%04x\n", hlen, ip->ip_sum);
+
+                        int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size;
+                        DPRINTF("+++ C+ mode TSO transferring packet size "
+                            "%d\n", tso_send_size);
+                        rtl8139_transfer_frame(s, saved_buffer, tso_send_size,
+                            0, (uint8_t *) dot1q_buffer);
+
+                        /* add transferred count to TCP sequence number */
+                        p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq));
+                        ++send_count;
+                    }
+
+                    /* Stop sending this frame */
+                    saved_size = 0;
+                }
+                else if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS))
+                {
+                    DPRINTF("+++ C+ mode need TCP or UDP checksum\n");
+
+                    /* maximum IP header length is 60 bytes */
+                    uint8_t saved_ip_header[60];
+                    memcpy(saved_ip_header, eth_payload_data, hlen);
+
+                    uint8_t *data_to_checksum     = eth_payload_data + hlen - 12;
+                    //                    size_t   data_to_checksum_len = eth_payload_len  - hlen + 12;
+
+                    /* add 4 TCP pseudoheader fields */
+                    /* copy IP source and destination fields */
+                    memcpy(data_to_checksum, saved_ip_header + 12, 8);
+
+                    if ((txdw0 & CP_TX_TCPCS) && ip_protocol == IP_PROTO_TCP)
+                    {
+                        DPRINTF("+++ C+ mode calculating TCP checksum for "
+                            "packet with %d bytes data\n", ip_data_len);
+
+                        ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
+                        p_tcpip_hdr->zeros      = 0;
+                        p_tcpip_hdr->ip_proto   = IP_PROTO_TCP;
+                        p_tcpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
+
+                        tcp_header* p_tcp_hdr = (tcp_header *) (data_to_checksum+12);
+
+                        p_tcp_hdr->th_sum = 0;
+
+                        int tcp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
+                        DPRINTF("+++ C+ mode TCP checksum %04x\n",
+                            tcp_checksum);
+
+                        p_tcp_hdr->th_sum = tcp_checksum;
+                    }
+                    else if ((txdw0 & CP_TX_UDPCS) && ip_protocol == IP_PROTO_UDP)
+                    {
+                        DPRINTF("+++ C+ mode calculating UDP checksum for "
+                            "packet with %d bytes data\n", ip_data_len);
+
+                        ip_pseudo_header *p_udpip_hdr = (ip_pseudo_header *)data_to_checksum;
+                        p_udpip_hdr->zeros      = 0;
+                        p_udpip_hdr->ip_proto   = IP_PROTO_UDP;
+                        p_udpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
+
+                        udp_header *p_udp_hdr = (udp_header *) (data_to_checksum+12);
+
+                        p_udp_hdr->uh_sum = 0;
+
+                        int udp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
+                        DPRINTF("+++ C+ mode UDP checksum %04x\n",
+                            udp_checksum);
+
+                        p_udp_hdr->uh_sum = udp_checksum;
+                    }
+
+                    /* restore IP header */
+                    memcpy(eth_payload_data, saved_ip_header, hlen);
+                }
+            }
+        }
+
+        /* update tally counter */
+        ++s->tally_counters.TxOk;
+
+        DPRINTF("+++ C+ mode transmitting %d bytes packet\n", saved_size);
+
+        rtl8139_transfer_frame(s, saved_buffer, saved_size, 1,
+            (uint8_t *) dot1q_buffer);
+
+        /* restore card space if there was no recursion and reset offset */
+        if (!s->cplus_txbuffer)
+        {
+            s->cplus_txbuffer        = saved_buffer;
+            s->cplus_txbuffer_len    = saved_buffer_len;
+            s->cplus_txbuffer_offset = 0;
+        }
+        else
+        {
+            g_free(saved_buffer);
+        }
+    }
+    else
+    {
+        DPRINTF("+++ C+ mode transmission continue to next descriptor\n");
+    }
+
+    return 1;
+}
+
+static void rtl8139_cplus_transmit(RTL8139State *s)
+{
+    int txcount = 0;
+
+    while (rtl8139_cplus_transmit_one(s))
+    {
+        ++txcount;
+    }
+
+    /* Mark transfer completed */
+    if (!txcount)
+    {
+        DPRINTF("C+ mode : transmitter queue stalled, current TxDesc = %d\n",
+            s->currCPlusTxDesc);
+    }
+    else
+    {
+        /* update interrupt status */
+        s->IntrStatus |= TxOK;
+        rtl8139_update_irq(s);
+    }
+}
+
+static void rtl8139_transmit(RTL8139State *s)
+{
+    int descriptor = s->currTxDesc, txcount = 0;
+
+    /*while*/
+    if (rtl8139_transmit_one(s, descriptor))
+    {
+        ++s->currTxDesc;
+        s->currTxDesc %= 4;
+        ++txcount;
+    }
+
+    /* Mark transfer completed */
+    if (!txcount)
+    {
+        DPRINTF("transmitter queue stalled, current TxDesc = %d\n",
+            s->currTxDesc);
+    }
+}
+
+static void rtl8139_TxStatus_write(RTL8139State *s, uint32_t txRegOffset, uint32_t val)
+{
+
+    int descriptor = txRegOffset/4;
+
+    /* handle C+ transmit mode register configuration */
+
+    if (s->cplus_enabled)
+    {
+        DPRINTF("RTL8139C+ DTCCR write offset=0x%x val=0x%08x "
+            "descriptor=%d\n", txRegOffset, val, descriptor);
+
+        /* handle Dump Tally Counters command */
+        s->TxStatus[descriptor] = val;
+
+        if (descriptor == 0 && (val & 0x8))
+        {
+            hwaddr tc_addr = rtl8139_addr64(s->TxStatus[0] & ~0x3f, s->TxStatus[1]);
+
+            /* dump tally counters to specified memory location */
+            RTL8139TallyCounters_dma_write(s, tc_addr);
+
+            /* mark dump completed */
+            s->TxStatus[0] &= ~0x8;
+        }
+
+        return;
+    }
+
+    DPRINTF("TxStatus write offset=0x%x val=0x%08x descriptor=%d\n",
+        txRegOffset, val, descriptor);
+
+    /* mask only reserved bits */
+    val &= ~0xff00c000; /* these bits are reset on write */
+    val = SET_MASKED(val, 0x00c00000, s->TxStatus[descriptor]);
+
+    s->TxStatus[descriptor] = val;
+
+    /* attempt to start transmission */
+    rtl8139_transmit(s);
+}
+
+static uint32_t rtl8139_TxStatus_TxAddr_read(RTL8139State *s, uint32_t regs[],
+                                             uint32_t base, uint8_t addr,
+                                             int size)
+{
+    uint32_t reg = (addr - base) / 4;
+    uint32_t offset = addr & 0x3;
+    uint32_t ret = 0;
+
+    if (addr & (size - 1)) {
+        DPRINTF("not implemented read for TxStatus/TxAddr "
+                "addr=0x%x size=0x%x\n", addr, size);
+        return ret;
+    }
+
+    switch (size) {
+    case 1: /* fall through */
+    case 2: /* fall through */
+    case 4:
+        ret = (regs[reg] >> offset * 8) & (((uint64_t)1 << (size * 8)) - 1);
+        DPRINTF("TxStatus/TxAddr[%d] read addr=0x%x size=0x%x val=0x%08x\n",
+                reg, addr, size, ret);
+        break;
+    default:
+        DPRINTF("unsupported size 0x%x of TxStatus/TxAddr reading\n", size);
+        break;
+    }
+
+    return ret;
+}
+
+static uint16_t rtl8139_TSAD_read(RTL8139State *s)
+{
+    uint16_t ret = 0;
+
+    /* Simulate TSAD, it is read only anyway */
+
+    ret = ((s->TxStatus[3] & TxStatOK  )?TSAD_TOK3:0)
+         |((s->TxStatus[2] & TxStatOK  )?TSAD_TOK2:0)
+         |((s->TxStatus[1] & TxStatOK  )?TSAD_TOK1:0)
+         |((s->TxStatus[0] & TxStatOK  )?TSAD_TOK0:0)
+
+         |((s->TxStatus[3] & TxUnderrun)?TSAD_TUN3:0)
+         |((s->TxStatus[2] & TxUnderrun)?TSAD_TUN2:0)
+         |((s->TxStatus[1] & TxUnderrun)?TSAD_TUN1:0)
+         |((s->TxStatus[0] & TxUnderrun)?TSAD_TUN0:0)
+
+         |((s->TxStatus[3] & TxAborted )?TSAD_TABT3:0)
+         |((s->TxStatus[2] & TxAborted )?TSAD_TABT2:0)
+         |((s->TxStatus[1] & TxAborted )?TSAD_TABT1:0)
+         |((s->TxStatus[0] & TxAborted )?TSAD_TABT0:0)
+
+         |((s->TxStatus[3] & TxHostOwns )?TSAD_OWN3:0)
+         |((s->TxStatus[2] & TxHostOwns )?TSAD_OWN2:0)
+         |((s->TxStatus[1] & TxHostOwns )?TSAD_OWN1:0)
+         |((s->TxStatus[0] & TxHostOwns )?TSAD_OWN0:0) ;
+
+
+    DPRINTF("TSAD read val=0x%04x\n", ret);
+
+    return ret;
+}
+
+static uint16_t rtl8139_CSCR_read(RTL8139State *s)
+{
+    uint16_t ret = s->CSCR;
+
+    DPRINTF("CSCR read val=0x%04x\n", ret);
+
+    return ret;
+}
+
+static void rtl8139_TxAddr_write(RTL8139State *s, uint32_t txAddrOffset, uint32_t val)
+{
+    DPRINTF("TxAddr write offset=0x%x val=0x%08x\n", txAddrOffset, val);
+
+    s->TxAddr[txAddrOffset/4] = val;
+}
+
+static uint32_t rtl8139_TxAddr_read(RTL8139State *s, uint32_t txAddrOffset)
+{
+    uint32_t ret = s->TxAddr[txAddrOffset/4];
+
+    DPRINTF("TxAddr read offset=0x%x val=0x%08x\n", txAddrOffset, ret);
+
+    return ret;
+}
+
+static void rtl8139_RxBufPtr_write(RTL8139State *s, uint32_t val)
+{
+    DPRINTF("RxBufPtr write val=0x%04x\n", val);
+
+    /* this value is off by 16 */
+    s->RxBufPtr = MOD2(val + 0x10, s->RxBufferSize);
+
+    DPRINTF(" CAPR write: rx buffer length %d head 0x%04x read 0x%04x\n",
+        s->RxBufferSize, s->RxBufAddr, s->RxBufPtr);
+}
+
+static uint32_t rtl8139_RxBufPtr_read(RTL8139State *s)
+{
+    /* this value is off by 16 */
+    uint32_t ret = s->RxBufPtr - 0x10;
+
+    DPRINTF("RxBufPtr read val=0x%04x\n", ret);
+
+    return ret;
+}
+
+static uint32_t rtl8139_RxBufAddr_read(RTL8139State *s)
+{
+    /* this value is NOT off by 16 */
+    uint32_t ret = s->RxBufAddr;
+
+    DPRINTF("RxBufAddr read val=0x%04x\n", ret);
+
+    return ret;
+}
+
+static void rtl8139_RxBuf_write(RTL8139State *s, uint32_t val)
+{
+    DPRINTF("RxBuf write val=0x%08x\n", val);
+
+    s->RxBuf = val;
+
+    /* may need to reset rxring here */
+}
+
+static uint32_t rtl8139_RxBuf_read(RTL8139State *s)
+{
+    uint32_t ret = s->RxBuf;
+
+    DPRINTF("RxBuf read val=0x%08x\n", ret);
+
+    return ret;
+}
+
+static void rtl8139_IntrMask_write(RTL8139State *s, uint32_t val)
+{
+    DPRINTF("IntrMask write(w) val=0x%04x\n", val);
+
+    /* mask unwritable bits */
+    val = SET_MASKED(val, 0x1e00, s->IntrMask);
+
+    s->IntrMask = val;
+
+    rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock));
+    rtl8139_update_irq(s);
+
+}
+
+static uint32_t rtl8139_IntrMask_read(RTL8139State *s)
+{
+    uint32_t ret = s->IntrMask;
+
+    DPRINTF("IntrMask read(w) val=0x%04x\n", ret);
+
+    return ret;
+}
+
+static void rtl8139_IntrStatus_write(RTL8139State *s, uint32_t val)
+{
+    DPRINTF("IntrStatus write(w) val=0x%04x\n", val);
+
+#if 0
+
+    /* writing to ISR has no effect */
+
+    return;
+
+#else
+    uint16_t newStatus = s->IntrStatus & ~val;
+
+    /* mask unwritable bits */
+    newStatus = SET_MASKED(newStatus, 0x1e00, s->IntrStatus);
+
+    /* writing 1 to interrupt status register bit clears it */
+    s->IntrStatus = 0;
+    rtl8139_update_irq(s);
+
+    s->IntrStatus = newStatus;
+    /*
+     * Computing if we miss an interrupt here is not that correct but
+     * considered that we should have had already an interrupt
+     * and probably emulated is slower is better to assume this resetting was
+     * done before testing on previous rtl8139_update_irq lead to IRQ losing
+     */
+    rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock));
+    rtl8139_update_irq(s);
+
+#endif
+}
+
+static uint32_t rtl8139_IntrStatus_read(RTL8139State *s)
+{
+    rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock));
+
+    uint32_t ret = s->IntrStatus;
+
+    DPRINTF("IntrStatus read(w) val=0x%04x\n", ret);
+
+#if 0
+
+    /* reading ISR clears all interrupts */
+    s->IntrStatus = 0;
+
+    rtl8139_update_irq(s);
+
+#endif
+
+    return ret;
+}
+
+static void rtl8139_MultiIntr_write(RTL8139State *s, uint32_t val)
+{
+    DPRINTF("MultiIntr write(w) val=0x%04x\n", val);
+
+    /* mask unwritable bits */
+    val = SET_MASKED(val, 0xf000, s->MultiIntr);
+
+    s->MultiIntr = val;
+}
+
+static uint32_t rtl8139_MultiIntr_read(RTL8139State *s)
+{
+    uint32_t ret = s->MultiIntr;
+
+    DPRINTF("MultiIntr read(w) val=0x%04x\n", ret);
+
+    return ret;
+}
+
+static void rtl8139_io_writeb(void *opaque, uint8_t addr, uint32_t val)
+{
+    RTL8139State *s = opaque;
+
+    switch (addr)
+    {
+        case MAC0 ... MAC0+5:
+            s->phys[addr - MAC0] = val;
+            break;
+        case MAC0+6 ... MAC0+7:
+            /* reserved */
+            break;
+        case MAR0 ... MAR0+7:
+            s->mult[addr - MAR0] = val;
+            break;
+        case ChipCmd:
+            rtl8139_ChipCmd_write(s, val);
+            break;
+        case Cfg9346:
+            rtl8139_Cfg9346_write(s, val);
+            break;
+        case TxConfig: /* windows driver sometimes writes using byte-lenth call */
+            rtl8139_TxConfig_writeb(s, val);
+            break;
+        case Config0:
+            rtl8139_Config0_write(s, val);
+            break;
+        case Config1:
+            rtl8139_Config1_write(s, val);
+            break;
+        case Config3:
+            rtl8139_Config3_write(s, val);
+            break;
+        case Config4:
+            rtl8139_Config4_write(s, val);
+            break;
+        case Config5:
+            rtl8139_Config5_write(s, val);
+            break;
+        case MediaStatus:
+            /* ignore */
+            DPRINTF("not implemented write(b) to MediaStatus val=0x%02x\n",
+                val);
+            break;
+
+        case HltClk:
+            DPRINTF("HltClk write val=0x%08x\n", val);
+            if (val == 'R')
+            {
+                s->clock_enabled = 1;
+            }
+            else if (val == 'H')
+            {
+                s->clock_enabled = 0;
+            }
+            break;
+
+        case TxThresh:
+            DPRINTF("C+ TxThresh write(b) val=0x%02x\n", val);
+            s->TxThresh = val;
+            break;
+
+        case TxPoll:
+            DPRINTF("C+ TxPoll write(b) val=0x%02x\n", val);
+            if (val & (1 << 7))
+            {
+                DPRINTF("C+ TxPoll high priority transmission (not "
+                    "implemented)\n");
+                //rtl8139_cplus_transmit(s);
+            }
+            if (val & (1 << 6))
+            {
+                DPRINTF("C+ TxPoll normal priority transmission\n");
+                rtl8139_cplus_transmit(s);
+            }
+
+            break;
+
+        default:
+            DPRINTF("not implemented write(b) addr=0x%x val=0x%02x\n", addr,
+                val);
+            break;
+    }
+}
+
+static void rtl8139_io_writew(void *opaque, uint8_t addr, uint32_t val)
+{
+    RTL8139State *s = opaque;
+
+    switch (addr)
+    {
+        case IntrMask:
+            rtl8139_IntrMask_write(s, val);
+            break;
+
+        case IntrStatus:
+            rtl8139_IntrStatus_write(s, val);
+            break;
+
+        case MultiIntr:
+            rtl8139_MultiIntr_write(s, val);
+            break;
+
+        case RxBufPtr:
+            rtl8139_RxBufPtr_write(s, val);
+            break;
+
+        case BasicModeCtrl:
+            rtl8139_BasicModeCtrl_write(s, val);
+            break;
+        case BasicModeStatus:
+            rtl8139_BasicModeStatus_write(s, val);
+            break;
+        case NWayAdvert:
+            DPRINTF("NWayAdvert write(w) val=0x%04x\n", val);
+            s->NWayAdvert = val;
+            break;
+        case NWayLPAR:
+            DPRINTF("forbidden NWayLPAR write(w) val=0x%04x\n", val);
+            break;
+        case NWayExpansion:
+            DPRINTF("NWayExpansion write(w) val=0x%04x\n", val);
+            s->NWayExpansion = val;
+            break;
+
+        case CpCmd:
+            rtl8139_CpCmd_write(s, val);
+            break;
+
+        case IntrMitigate:
+            rtl8139_IntrMitigate_write(s, val);
+            break;
+
+        default:
+            DPRINTF("ioport write(w) addr=0x%x val=0x%04x via write(b)\n",
+                addr, val);
+
+            rtl8139_io_writeb(opaque, addr, val & 0xff);
+            rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+            break;
+    }
+}
+
+static void rtl8139_set_next_tctr_time(RTL8139State *s, int64_t current_time)
+{
+    int64_t pci_time, next_time;
+    uint32_t low_pci;
+
+    DPRINTF("entered rtl8139_set_next_tctr_time\n");
+
+    if (s->TimerExpire && current_time >= s->TimerExpire) {
+        s->IntrStatus |= PCSTimeout;
+        rtl8139_update_irq(s);
+    }
+
+    /* Set QEMU timer only if needed that is
+     * - TimerInt <> 0 (we have a timer)
+     * - mask = 1 (we want an interrupt timer)
+     * - irq = 0  (irq is not already active)
+     * If any of above change we need to compute timer again
+     * Also we must check if timer is passed without QEMU timer
+     */
+    s->TimerExpire = 0;
+    if (!s->TimerInt) {
+        return;
+    }
+
+    pci_time = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY,
+                                get_ticks_per_sec());
+    low_pci = pci_time & 0xffffffff;
+    pci_time = pci_time - low_pci + s->TimerInt;
+    if (low_pci >= s->TimerInt) {
+        pci_time += 0x100000000LL;
+    }
+    next_time = s->TCTR_base + muldiv64(pci_time, get_ticks_per_sec(),
+                                                PCI_FREQUENCY);
+    s->TimerExpire = next_time;
+
+    if ((s->IntrMask & PCSTimeout) != 0 && (s->IntrStatus & PCSTimeout) == 0) {
+        qemu_mod_timer(s->timer, next_time);
+    }
+}
+
+static void rtl8139_io_writel(void *opaque, uint8_t addr, uint32_t val)
+{
+    RTL8139State *s = opaque;
+
+    switch (addr)
+    {
+        case RxMissed:
+            DPRINTF("RxMissed clearing on write\n");
+            s->RxMissed = 0;
+            break;
+
+        case TxConfig:
+            rtl8139_TxConfig_write(s, val);
+            break;
+
+        case RxConfig:
+            rtl8139_RxConfig_write(s, val);
+            break;
+
+        case TxStatus0 ... TxStatus0+4*4-1:
+            rtl8139_TxStatus_write(s, addr-TxStatus0, val);
+            break;
+
+        case TxAddr0 ... TxAddr0+4*4-1:
+            rtl8139_TxAddr_write(s, addr-TxAddr0, val);
+            break;
+
+        case RxBuf:
+            rtl8139_RxBuf_write(s, val);
+            break;
+
+        case RxRingAddrLO:
+            DPRINTF("C+ RxRing low bits write val=0x%08x\n", val);
+            s->RxRingAddrLO = val;
+            break;
+
+        case RxRingAddrHI:
+            DPRINTF("C+ RxRing high bits write val=0x%08x\n", val);
+            s->RxRingAddrHI = val;
+            break;
+
+        case Timer:
+            DPRINTF("TCTR Timer reset on write\n");
+            s->TCTR_base = qemu_get_clock_ns(vm_clock);
+            rtl8139_set_next_tctr_time(s, s->TCTR_base);
+            break;
+
+        case FlashReg:
+            DPRINTF("FlashReg TimerInt write val=0x%08x\n", val);
+            if (s->TimerInt != val) {
+                s->TimerInt = val;
+                rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock));
+            }
+            break;
+
+        default:
+            DPRINTF("ioport write(l) addr=0x%x val=0x%08x via write(b)\n",
+                addr, val);
+            rtl8139_io_writeb(opaque, addr, val & 0xff);
+            rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+            rtl8139_io_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+            rtl8139_io_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+            break;
+    }
+}
+
+static uint32_t rtl8139_io_readb(void *opaque, uint8_t addr)
+{
+    RTL8139State *s = opaque;
+    int ret;
+
+    switch (addr)
+    {
+        case MAC0 ... MAC0+5:
+            ret = s->phys[addr - MAC0];
+            break;
+        case MAC0+6 ... MAC0+7:
+            ret = 0;
+            break;
+        case MAR0 ... MAR0+7:
+            ret = s->mult[addr - MAR0];
+            break;
+        case TxStatus0 ... TxStatus0+4*4-1:
+            ret = rtl8139_TxStatus_TxAddr_read(s, s->TxStatus, TxStatus0,
+                                               addr, 1);
+            break;
+        case ChipCmd:
+            ret = rtl8139_ChipCmd_read(s);
+            break;
+        case Cfg9346:
+            ret = rtl8139_Cfg9346_read(s);
+            break;
+        case Config0:
+            ret = rtl8139_Config0_read(s);
+            break;
+        case Config1:
+            ret = rtl8139_Config1_read(s);
+            break;
+        case Config3:
+            ret = rtl8139_Config3_read(s);
+            break;
+        case Config4:
+            ret = rtl8139_Config4_read(s);
+            break;
+        case Config5:
+            ret = rtl8139_Config5_read(s);
+            break;
+
+        case MediaStatus:
+            /* The LinkDown bit of MediaStatus is inverse with link status */
+            ret = 0xd0 | (~s->BasicModeStatus & 0x04);
+            DPRINTF("MediaStatus read 0x%x\n", ret);
+            break;
+
+        case HltClk:
+            ret = s->clock_enabled;
+            DPRINTF("HltClk read 0x%x\n", ret);
+            break;
+
+        case PCIRevisionID:
+            ret = RTL8139_PCI_REVID;
+            DPRINTF("PCI Revision ID read 0x%x\n", ret);
+            break;
+
+        case TxThresh:
+            ret = s->TxThresh;
+            DPRINTF("C+ TxThresh read(b) val=0x%02x\n", ret);
+            break;
+
+        case 0x43: /* Part of TxConfig register. Windows driver tries to read it */
+            ret = s->TxConfig >> 24;
+            DPRINTF("RTL8139C TxConfig at 0x43 read(b) val=0x%02x\n", ret);
+            break;
+
+        default:
+            DPRINTF("not implemented read(b) addr=0x%x\n", addr);
+            ret = 0;
+            break;
+    }
+
+    return ret;
+}
+
+static uint32_t rtl8139_io_readw(void *opaque, uint8_t addr)
+{
+    RTL8139State *s = opaque;
+    uint32_t ret;
+
+    switch (addr)
+    {
+        case TxAddr0 ... TxAddr0+4*4-1:
+            ret = rtl8139_TxStatus_TxAddr_read(s, s->TxAddr, TxAddr0, addr, 2);
+            break;
+        case IntrMask:
+            ret = rtl8139_IntrMask_read(s);
+            break;
+
+        case IntrStatus:
+            ret = rtl8139_IntrStatus_read(s);
+            break;
+
+        case MultiIntr:
+            ret = rtl8139_MultiIntr_read(s);
+            break;
+
+        case RxBufPtr:
+            ret = rtl8139_RxBufPtr_read(s);
+            break;
+
+        case RxBufAddr:
+            ret = rtl8139_RxBufAddr_read(s);
+            break;
+
+        case BasicModeCtrl:
+            ret = rtl8139_BasicModeCtrl_read(s);
+            break;
+        case BasicModeStatus:
+            ret = rtl8139_BasicModeStatus_read(s);
+            break;
+        case NWayAdvert:
+            ret = s->NWayAdvert;
+            DPRINTF("NWayAdvert read(w) val=0x%04x\n", ret);
+            break;
+        case NWayLPAR:
+            ret = s->NWayLPAR;
+            DPRINTF("NWayLPAR read(w) val=0x%04x\n", ret);
+            break;
+        case NWayExpansion:
+            ret = s->NWayExpansion;
+            DPRINTF("NWayExpansion read(w) val=0x%04x\n", ret);
+            break;
+
+        case CpCmd:
+            ret = rtl8139_CpCmd_read(s);
+            break;
+
+        case IntrMitigate:
+            ret = rtl8139_IntrMitigate_read(s);
+            break;
+
+        case TxSummary:
+            ret = rtl8139_TSAD_read(s);
+            break;
+
+        case CSCR:
+            ret = rtl8139_CSCR_read(s);
+            break;
+
+        default:
+            DPRINTF("ioport read(w) addr=0x%x via read(b)\n", addr);
+
+            ret  = rtl8139_io_readb(opaque, addr);
+            ret |= rtl8139_io_readb(opaque, addr + 1) << 8;
+
+            DPRINTF("ioport read(w) addr=0x%x val=0x%04x\n", addr, ret);
+            break;
+    }
+
+    return ret;
+}
+
+static uint32_t rtl8139_io_readl(void *opaque, uint8_t addr)
+{
+    RTL8139State *s = opaque;
+    uint32_t ret;
+
+    switch (addr)
+    {
+        case RxMissed:
+            ret = s->RxMissed;
+
+            DPRINTF("RxMissed read val=0x%08x\n", ret);
+            break;
+
+        case TxConfig:
+            ret = rtl8139_TxConfig_read(s);
+            break;
+
+        case RxConfig:
+            ret = rtl8139_RxConfig_read(s);
+            break;
+
+        case TxStatus0 ... TxStatus0+4*4-1:
+            ret = rtl8139_TxStatus_TxAddr_read(s, s->TxStatus, TxStatus0,
+                                               addr, 4);
+            break;
+
+        case TxAddr0 ... TxAddr0+4*4-1:
+            ret = rtl8139_TxAddr_read(s, addr-TxAddr0);
+            break;
+
+        case RxBuf:
+            ret = rtl8139_RxBuf_read(s);
+            break;
+
+        case RxRingAddrLO:
+            ret = s->RxRingAddrLO;
+            DPRINTF("C+ RxRing low bits read val=0x%08x\n", ret);
+            break;
+
+        case RxRingAddrHI:
+            ret = s->RxRingAddrHI;
+            DPRINTF("C+ RxRing high bits read val=0x%08x\n", ret);
+            break;
+
+        case Timer:
+            ret = muldiv64(qemu_get_clock_ns(vm_clock) - s->TCTR_base,
+                           PCI_FREQUENCY, get_ticks_per_sec());
+            DPRINTF("TCTR Timer read val=0x%08x\n", ret);
+            break;
+
+        case FlashReg:
+            ret = s->TimerInt;
+            DPRINTF("FlashReg TimerInt read val=0x%08x\n", ret);
+            break;
+
+        default:
+            DPRINTF("ioport read(l) addr=0x%x via read(b)\n", addr);
+
+            ret  = rtl8139_io_readb(opaque, addr);
+            ret |= rtl8139_io_readb(opaque, addr + 1) << 8;
+            ret |= rtl8139_io_readb(opaque, addr + 2) << 16;
+            ret |= rtl8139_io_readb(opaque, addr + 3) << 24;
+
+            DPRINTF("read(l) addr=0x%x val=%08x\n", addr, ret);
+            break;
+    }
+
+    return ret;
+}
+
+/* */
+
+static void rtl8139_mmio_writeb(void *opaque, hwaddr addr, uint32_t val)
+{
+    rtl8139_io_writeb(opaque, addr & 0xFF, val);
+}
+
+static void rtl8139_mmio_writew(void *opaque, hwaddr addr, uint32_t val)
+{
+    rtl8139_io_writew(opaque, addr & 0xFF, val);
+}
+
+static void rtl8139_mmio_writel(void *opaque, hwaddr addr, uint32_t val)
+{
+    rtl8139_io_writel(opaque, addr & 0xFF, val);
+}
+
+static uint32_t rtl8139_mmio_readb(void *opaque, hwaddr addr)
+{
+    return rtl8139_io_readb(opaque, addr & 0xFF);
+}
+
+static uint32_t rtl8139_mmio_readw(void *opaque, hwaddr addr)
+{
+    uint32_t val = rtl8139_io_readw(opaque, addr & 0xFF);
+    return val;
+}
+
+static uint32_t rtl8139_mmio_readl(void *opaque, hwaddr addr)
+{
+    uint32_t val = rtl8139_io_readl(opaque, addr & 0xFF);
+    return val;
+}
+
+static int rtl8139_post_load(void *opaque, int version_id)
+{
+    RTL8139State* s = opaque;
+    rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock));
+    if (version_id < 4) {
+        s->cplus_enabled = s->CpCmd != 0;
+    }
+
+    /* nc.link_down can't be migrated, so infer link_down according
+     * to link status bit in BasicModeStatus */
+    qemu_get_queue(s->nic)->link_down = (s->BasicModeStatus & 0x04) == 0;
+
+    return 0;
+}
+
+static bool rtl8139_hotplug_ready_needed(void *opaque)
+{
+    return qdev_machine_modified();
+}
+
+static const VMStateDescription vmstate_rtl8139_hotplug_ready ={
+    .name = "rtl8139/hotplug_ready",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void rtl8139_pre_save(void *opaque)
+{
+    RTL8139State* s = opaque;
+    int64_t current_time = qemu_get_clock_ns(vm_clock);
+
+    /* set IntrStatus correctly */
+    rtl8139_set_next_tctr_time(s, current_time);
+    s->TCTR = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY,
+                       get_ticks_per_sec());
+    s->rtl8139_mmio_io_addr_dummy = 0;
+}
+
+static const VMStateDescription vmstate_rtl8139 = {
+    .name = "rtl8139",
+    .version_id = 4,
+    .minimum_version_id = 3,
+    .minimum_version_id_old = 3,
+    .post_load = rtl8139_post_load,
+    .pre_save  = rtl8139_pre_save,
+    .fields      = (VMStateField []) {
+        VMSTATE_PCI_DEVICE(dev, RTL8139State),
+        VMSTATE_PARTIAL_BUFFER(phys, RTL8139State, 6),
+        VMSTATE_BUFFER(mult, RTL8139State),
+        VMSTATE_UINT32_ARRAY(TxStatus, RTL8139State, 4),
+        VMSTATE_UINT32_ARRAY(TxAddr, RTL8139State, 4),
+
+        VMSTATE_UINT32(RxBuf, RTL8139State),
+        VMSTATE_UINT32(RxBufferSize, RTL8139State),
+        VMSTATE_UINT32(RxBufPtr, RTL8139State),
+        VMSTATE_UINT32(RxBufAddr, RTL8139State),
+
+        VMSTATE_UINT16(IntrStatus, RTL8139State),
+        VMSTATE_UINT16(IntrMask, RTL8139State),
+
+        VMSTATE_UINT32(TxConfig, RTL8139State),
+        VMSTATE_UINT32(RxConfig, RTL8139State),
+        VMSTATE_UINT32(RxMissed, RTL8139State),
+        VMSTATE_UINT16(CSCR, RTL8139State),
+
+        VMSTATE_UINT8(Cfg9346, RTL8139State),
+        VMSTATE_UINT8(Config0, RTL8139State),
+        VMSTATE_UINT8(Config1, RTL8139State),
+        VMSTATE_UINT8(Config3, RTL8139State),
+        VMSTATE_UINT8(Config4, RTL8139State),
+        VMSTATE_UINT8(Config5, RTL8139State),
+
+        VMSTATE_UINT8(clock_enabled, RTL8139State),
+        VMSTATE_UINT8(bChipCmdState, RTL8139State),
+
+        VMSTATE_UINT16(MultiIntr, RTL8139State),
+
+        VMSTATE_UINT16(BasicModeCtrl, RTL8139State),
+        VMSTATE_UINT16(BasicModeStatus, RTL8139State),
+        VMSTATE_UINT16(NWayAdvert, RTL8139State),
+        VMSTATE_UINT16(NWayLPAR, RTL8139State),
+        VMSTATE_UINT16(NWayExpansion, RTL8139State),
+
+        VMSTATE_UINT16(CpCmd, RTL8139State),
+        VMSTATE_UINT8(TxThresh, RTL8139State),
+
+        VMSTATE_UNUSED(4),
+        VMSTATE_MACADDR(conf.macaddr, RTL8139State),
+        VMSTATE_INT32(rtl8139_mmio_io_addr_dummy, RTL8139State),
+
+        VMSTATE_UINT32(currTxDesc, RTL8139State),
+        VMSTATE_UINT32(currCPlusRxDesc, RTL8139State),
+        VMSTATE_UINT32(currCPlusTxDesc, RTL8139State),
+        VMSTATE_UINT32(RxRingAddrLO, RTL8139State),
+        VMSTATE_UINT32(RxRingAddrHI, RTL8139State),
+
+        VMSTATE_UINT16_ARRAY(eeprom.contents, RTL8139State, EEPROM_9346_SIZE),
+        VMSTATE_INT32(eeprom.mode, RTL8139State),
+        VMSTATE_UINT32(eeprom.tick, RTL8139State),
+        VMSTATE_UINT8(eeprom.address, RTL8139State),
+        VMSTATE_UINT16(eeprom.input, RTL8139State),
+        VMSTATE_UINT16(eeprom.output, RTL8139State),
+
+        VMSTATE_UINT8(eeprom.eecs, RTL8139State),
+        VMSTATE_UINT8(eeprom.eesk, RTL8139State),
+        VMSTATE_UINT8(eeprom.eedi, RTL8139State),
+        VMSTATE_UINT8(eeprom.eedo, RTL8139State),
+
+        VMSTATE_UINT32(TCTR, RTL8139State),
+        VMSTATE_UINT32(TimerInt, RTL8139State),
+        VMSTATE_INT64(TCTR_base, RTL8139State),
+
+        VMSTATE_STRUCT(tally_counters, RTL8139State, 0,
+                       vmstate_tally_counters, RTL8139TallyCounters),
+
+        VMSTATE_UINT32_V(cplus_enabled, RTL8139State, 4),
+        VMSTATE_END_OF_LIST()
+    },
+    .subsections = (VMStateSubsection []) {
+        {
+            .vmsd = &vmstate_rtl8139_hotplug_ready,
+            .needed = rtl8139_hotplug_ready_needed,
+        }, {
+            /* empty */
+        }
+    }
+};
+
+/***********************************************************/
+/* PCI RTL8139 definitions */
+
+static void rtl8139_ioport_write(void *opaque, hwaddr addr,
+                                 uint64_t val, unsigned size)
+{
+    switch (size) {
+    case 1:
+        rtl8139_io_writeb(opaque, addr, val);
+        break;
+    case 2:
+        rtl8139_io_writew(opaque, addr, val);
+        break;
+    case 4:
+        rtl8139_io_writel(opaque, addr, val);
+        break;
+    }
+}
+
+static uint64_t rtl8139_ioport_read(void *opaque, hwaddr addr,
+                                    unsigned size)
+{
+    switch (size) {
+    case 1:
+        return rtl8139_io_readb(opaque, addr);
+    case 2:
+        return rtl8139_io_readw(opaque, addr);
+    case 4:
+        return rtl8139_io_readl(opaque, addr);
+    }
+
+    return -1;
+}
+
+static const MemoryRegionOps rtl8139_io_ops = {
+    .read = rtl8139_ioport_read,
+    .write = rtl8139_ioport_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static const MemoryRegionOps rtl8139_mmio_ops = {
+    .old_mmio = {
+        .read = {
+            rtl8139_mmio_readb,
+            rtl8139_mmio_readw,
+            rtl8139_mmio_readl,
+        },
+        .write = {
+            rtl8139_mmio_writeb,
+            rtl8139_mmio_writew,
+            rtl8139_mmio_writel,
+        },
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void rtl8139_timer(void *opaque)
+{
+    RTL8139State *s = opaque;
+
+    if (!s->clock_enabled)
+    {
+        DPRINTF(">>> timer: clock is not running\n");
+        return;
+    }
+
+    s->IntrStatus |= PCSTimeout;
+    rtl8139_update_irq(s);
+    rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock));
+}
+
+static void rtl8139_cleanup(NetClientState *nc)
+{
+    RTL8139State *s = qemu_get_nic_opaque(nc);
+
+    s->nic = NULL;
+}
+
+static void pci_rtl8139_uninit(PCIDevice *dev)
+{
+    RTL8139State *s = DO_UPCAST(RTL8139State, dev, dev);
+
+    memory_region_destroy(&s->bar_io);
+    memory_region_destroy(&s->bar_mem);
+    if (s->cplus_txbuffer) {
+        g_free(s->cplus_txbuffer);
+        s->cplus_txbuffer = NULL;
+    }
+    qemu_del_timer(s->timer);
+    qemu_free_timer(s->timer);
+    qemu_del_nic(s->nic);
+}
+
+static void rtl8139_set_link_status(NetClientState *nc)
+{
+    RTL8139State *s = qemu_get_nic_opaque(nc);
+
+    if (nc->link_down) {
+        s->BasicModeStatus &= ~0x04;
+    } else {
+        s->BasicModeStatus |= 0x04;
+    }
+
+    s->IntrStatus |= RxUnderrun;
+    rtl8139_update_irq(s);
+}
+
+static NetClientInfo net_rtl8139_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = rtl8139_can_receive,
+    .receive = rtl8139_receive,
+    .cleanup = rtl8139_cleanup,
+    .link_status_changed = rtl8139_set_link_status,
+};
+
+static int pci_rtl8139_init(PCIDevice *dev)
+{
+    RTL8139State * s = DO_UPCAST(RTL8139State, dev, dev);
+    uint8_t *pci_conf;
+
+    pci_conf = s->dev.config;
+    pci_conf[PCI_INTERRUPT_PIN] = 1;    /* interrupt pin A */
+    /* TODO: start of capability list, but no capability
+     * list bit in status register, and offset 0xdc seems unused. */
+    pci_conf[PCI_CAPABILITY_LIST] = 0xdc;
+
+    memory_region_init_io(&s->bar_io, &rtl8139_io_ops, s, "rtl8139", 0x100);
+    memory_region_init_io(&s->bar_mem, &rtl8139_mmio_ops, s, "rtl8139", 0x100);
+    pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->bar_io);
+    pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar_mem);
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+
+    /* prepare eeprom */
+    s->eeprom.contents[0] = 0x8129;
+#if 1
+    /* PCI vendor and device ID should be mirrored here */
+    s->eeprom.contents[1] = PCI_VENDOR_ID_REALTEK;
+    s->eeprom.contents[2] = PCI_DEVICE_ID_REALTEK_8139;
+#endif
+    s->eeprom.contents[7] = s->conf.macaddr.a[0] | s->conf.macaddr.a[1] << 8;
+    s->eeprom.contents[8] = s->conf.macaddr.a[2] | s->conf.macaddr.a[3] << 8;
+    s->eeprom.contents[9] = s->conf.macaddr.a[4] | s->conf.macaddr.a[5] << 8;
+
+    s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf,
+                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+
+    s->cplus_txbuffer = NULL;
+    s->cplus_txbuffer_len = 0;
+    s->cplus_txbuffer_offset = 0;
+
+    s->TimerExpire = 0;
+    s->timer = qemu_new_timer_ns(vm_clock, rtl8139_timer, s);
+    rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock));
+
+    add_boot_device_path(s->conf.bootindex, &dev->qdev, "/ethernet-phy@0");
+
+    return 0;
+}
+
+static Property rtl8139_properties[] = {
+    DEFINE_NIC_PROPERTIES(RTL8139State, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void rtl8139_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = pci_rtl8139_init;
+    k->exit = pci_rtl8139_uninit;
+    k->romfile = "efi-rtl8139.rom";
+    k->vendor_id = PCI_VENDOR_ID_REALTEK;
+    k->device_id = PCI_DEVICE_ID_REALTEK_8139;
+    k->revision = RTL8139_PCI_REVID; /* >=0x20 is for 8139C+ */
+    k->class_id = PCI_CLASS_NETWORK_ETHERNET;
+    dc->reset = rtl8139_reset;
+    dc->vmsd = &vmstate_rtl8139;
+    dc->props = rtl8139_properties;
+}
+
+static const TypeInfo rtl8139_info = {
+    .name          = "rtl8139",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(RTL8139State),
+    .class_init    = rtl8139_class_init,
+};
+
+static void rtl8139_register_types(void)
+{
+    type_register_static(&rtl8139_info);
+}
+
+type_init(rtl8139_register_types)
diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c
new file mode 100644 (file)
index 0000000..f659256
--- /dev/null
@@ -0,0 +1,806 @@
+/*
+ * SMSC 91C111 Ethernet interface emulation
+ *
+ * Copyright (c) 2005 CodeSourcery, LLC.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL
+ */
+
+#include "hw/sysbus.h"
+#include "net/net.h"
+#include "hw/arm/devices.h"
+/* For crc32 */
+#include <zlib.h>
+
+/* Number of 2k memory pages available.  */
+#define NUM_PACKETS 4
+
+typedef struct {
+    SysBusDevice busdev;
+    NICState *nic;
+    NICConf conf;
+    uint16_t tcr;
+    uint16_t rcr;
+    uint16_t cr;
+    uint16_t ctr;
+    uint16_t gpr;
+    uint16_t ptr;
+    uint16_t ercv;
+    qemu_irq irq;
+    int bank;
+    int packet_num;
+    int tx_alloc;
+    /* Bitmask of allocated packets.  */
+    int allocated;
+    int tx_fifo_len;
+    int tx_fifo[NUM_PACKETS];
+    int rx_fifo_len;
+    int rx_fifo[NUM_PACKETS];
+    int tx_fifo_done_len;
+    int tx_fifo_done[NUM_PACKETS];
+    /* Packet buffer memory.  */
+    uint8_t data[NUM_PACKETS][2048];
+    uint8_t int_level;
+    uint8_t int_mask;
+    MemoryRegion mmio;
+} smc91c111_state;
+
+static const VMStateDescription vmstate_smc91c111 = {
+    .name = "smc91c111",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT16(tcr, smc91c111_state),
+        VMSTATE_UINT16(rcr, smc91c111_state),
+        VMSTATE_UINT16(cr, smc91c111_state),
+        VMSTATE_UINT16(ctr, smc91c111_state),
+        VMSTATE_UINT16(gpr, smc91c111_state),
+        VMSTATE_UINT16(ptr, smc91c111_state),
+        VMSTATE_UINT16(ercv, smc91c111_state),
+        VMSTATE_INT32(bank, smc91c111_state),
+        VMSTATE_INT32(packet_num, smc91c111_state),
+        VMSTATE_INT32(tx_alloc, smc91c111_state),
+        VMSTATE_INT32(allocated, smc91c111_state),
+        VMSTATE_INT32(tx_fifo_len, smc91c111_state),
+        VMSTATE_INT32_ARRAY(tx_fifo, smc91c111_state, NUM_PACKETS),
+        VMSTATE_INT32(rx_fifo_len, smc91c111_state),
+        VMSTATE_INT32_ARRAY(rx_fifo, smc91c111_state, NUM_PACKETS),
+        VMSTATE_INT32(tx_fifo_done_len, smc91c111_state),
+        VMSTATE_INT32_ARRAY(tx_fifo_done, smc91c111_state, NUM_PACKETS),
+        VMSTATE_BUFFER_UNSAFE(data, smc91c111_state, 0, NUM_PACKETS * 2048),
+        VMSTATE_UINT8(int_level, smc91c111_state),
+        VMSTATE_UINT8(int_mask, smc91c111_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+#define RCR_SOFT_RST  0x8000
+#define RCR_STRIP_CRC 0x0200
+#define RCR_RXEN      0x0100
+
+#define TCR_EPH_LOOP  0x2000
+#define TCR_NOCRC     0x0100
+#define TCR_PAD_EN    0x0080
+#define TCR_FORCOL    0x0004
+#define TCR_LOOP      0x0002
+#define TCR_TXEN      0x0001
+
+#define INT_MD        0x80
+#define INT_ERCV      0x40
+#define INT_EPH       0x20
+#define INT_RX_OVRN   0x10
+#define INT_ALLOC     0x08
+#define INT_TX_EMPTY  0x04
+#define INT_TX        0x02
+#define INT_RCV       0x01
+
+#define CTR_AUTO_RELEASE  0x0800
+#define CTR_RELOAD        0x0002
+#define CTR_STORE         0x0001
+
+#define RS_ALGNERR      0x8000
+#define RS_BRODCAST     0x4000
+#define RS_BADCRC       0x2000
+#define RS_ODDFRAME     0x1000
+#define RS_TOOLONG      0x0800
+#define RS_TOOSHORT     0x0400
+#define RS_MULTICAST    0x0001
+
+/* Update interrupt status.  */
+static void smc91c111_update(smc91c111_state *s)
+{
+    int level;
+
+    if (s->tx_fifo_len == 0)
+        s->int_level |= INT_TX_EMPTY;
+    if (s->tx_fifo_done_len != 0)
+        s->int_level |= INT_TX;
+    level = (s->int_level & s->int_mask) != 0;
+    qemu_set_irq(s->irq, level);
+}
+
+/* Try to allocate a packet.  Returns 0x80 on failure.  */
+static int smc91c111_allocate_packet(smc91c111_state *s)
+{
+    int i;
+    if (s->allocated == (1 << NUM_PACKETS) - 1) {
+        return 0x80;
+    }
+
+    for (i = 0; i < NUM_PACKETS; i++) {
+        if ((s->allocated & (1 << i)) == 0)
+            break;
+    }
+    s->allocated |= 1 << i;
+    return i;
+}
+
+
+/* Process a pending TX allocate.  */
+static void smc91c111_tx_alloc(smc91c111_state *s)
+{
+    s->tx_alloc = smc91c111_allocate_packet(s);
+    if (s->tx_alloc == 0x80)
+        return;
+    s->int_level |= INT_ALLOC;
+    smc91c111_update(s);
+}
+
+/* Remove and item from the RX FIFO.  */
+static void smc91c111_pop_rx_fifo(smc91c111_state *s)
+{
+    int i;
+
+    s->rx_fifo_len--;
+    if (s->rx_fifo_len) {
+        for (i = 0; i < s->rx_fifo_len; i++)
+            s->rx_fifo[i] = s->rx_fifo[i + 1];
+        s->int_level |= INT_RCV;
+    } else {
+        s->int_level &= ~INT_RCV;
+    }
+    smc91c111_update(s);
+}
+
+/* Remove an item from the TX completion FIFO.  */
+static void smc91c111_pop_tx_fifo_done(smc91c111_state *s)
+{
+    int i;
+
+    if (s->tx_fifo_done_len == 0)
+        return;
+    s->tx_fifo_done_len--;
+    for (i = 0; i < s->tx_fifo_done_len; i++)
+        s->tx_fifo_done[i] = s->tx_fifo_done[i + 1];
+}
+
+/* Release the memory allocated to a packet.  */
+static void smc91c111_release_packet(smc91c111_state *s, int packet)
+{
+    s->allocated &= ~(1 << packet);
+    if (s->tx_alloc == 0x80)
+        smc91c111_tx_alloc(s);
+}
+
+/* Flush the TX FIFO.  */
+static void smc91c111_do_tx(smc91c111_state *s)
+{
+    int i;
+    int len;
+    int control;
+    int packetnum;
+    uint8_t *p;
+
+    if ((s->tcr & TCR_TXEN) == 0)
+        return;
+    if (s->tx_fifo_len == 0)
+        return;
+    for (i = 0; i < s->tx_fifo_len; i++) {
+        packetnum = s->tx_fifo[i];
+        p = &s->data[packetnum][0];
+        /* Set status word.  */
+        *(p++) = 0x01;
+        *(p++) = 0x40;
+        len = *(p++);
+        len |= ((int)*(p++)) << 8;
+        len -= 6;
+        control = p[len + 1];
+        if (control & 0x20)
+            len++;
+        /* ??? This overwrites the data following the buffer.
+           Don't know what real hardware does.  */
+        if (len < 64 && (s->tcr & TCR_PAD_EN)) {
+            memset(p + len, 0, 64 - len);
+            len = 64;
+        }
+#if 0
+        {
+            int add_crc;
+
+            /* The card is supposed to append the CRC to the frame.
+               However none of the other network traffic has the CRC
+               appended.  Suspect this is low level ethernet detail we
+               don't need to worry about.  */
+            add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0;
+            if (add_crc) {
+                uint32_t crc;
+
+                crc = crc32(~0, p, len);
+                memcpy(p + len, &crc, 4);
+                len += 4;
+            }
+        }
+#endif
+        if (s->ctr & CTR_AUTO_RELEASE)
+            /* Race?  */
+            smc91c111_release_packet(s, packetnum);
+        else if (s->tx_fifo_done_len < NUM_PACKETS)
+            s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum;
+        qemu_send_packet(qemu_get_queue(s->nic), p, len);
+    }
+    s->tx_fifo_len = 0;
+    smc91c111_update(s);
+}
+
+/* Add a packet to the TX FIFO.  */
+static void smc91c111_queue_tx(smc91c111_state *s, int packet)
+{
+    if (s->tx_fifo_len == NUM_PACKETS)
+        return;
+    s->tx_fifo[s->tx_fifo_len++] = packet;
+    smc91c111_do_tx(s);
+}
+
+static void smc91c111_reset(DeviceState *dev)
+{
+    smc91c111_state *s = FROM_SYSBUS(smc91c111_state, SYS_BUS_DEVICE(dev));
+    s->bank = 0;
+    s->tx_fifo_len = 0;
+    s->tx_fifo_done_len = 0;
+    s->rx_fifo_len = 0;
+    s->allocated = 0;
+    s->packet_num = 0;
+    s->tx_alloc = 0;
+    s->tcr = 0;
+    s->rcr = 0;
+    s->cr = 0xa0b1;
+    s->ctr = 0x1210;
+    s->ptr = 0;
+    s->ercv = 0x1f;
+    s->int_level = INT_TX_EMPTY;
+    s->int_mask = 0;
+    smc91c111_update(s);
+}
+
+#define SET_LOW(name, val) s->name = (s->name & 0xff00) | val
+#define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8)
+
+static void smc91c111_writeb(void *opaque, hwaddr offset,
+                             uint32_t value)
+{
+    smc91c111_state *s = (smc91c111_state *)opaque;
+
+    offset = offset & 0xf;
+    if (offset == 14) {
+        s->bank = value;
+        return;
+    }
+    if (offset == 15)
+        return;
+    switch (s->bank) {
+    case 0:
+        switch (offset) {
+        case 0: /* TCR */
+            SET_LOW(tcr, value);
+            return;
+        case 1:
+            SET_HIGH(tcr, value);
+            return;
+        case 4: /* RCR */
+            SET_LOW(rcr, value);
+            return;
+        case 5:
+            SET_HIGH(rcr, value);
+            if (s->rcr & RCR_SOFT_RST)
+                smc91c111_reset(&s->busdev.qdev);
+            return;
+        case 10: case 11: /* RPCR */
+            /* Ignored */
+            return;
+        case 12: case 13: /* Reserved */
+            return;
+        }
+        break;
+
+    case 1:
+        switch (offset) {
+        case 0: /* CONFIG */
+            SET_LOW(cr, value);
+            return;
+        case 1:
+            SET_HIGH(cr,value);
+            return;
+        case 2: case 3: /* BASE */
+        case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
+            /* Not implemented.  */
+            return;
+        case 10: /* Genral Purpose */
+            SET_LOW(gpr, value);
+            return;
+        case 11:
+            SET_HIGH(gpr, value);
+            return;
+        case 12: /* Control */
+            if (value & 1)
+                fprintf(stderr, "smc91c111:EEPROM store not implemented\n");
+            if (value & 2)
+                fprintf(stderr, "smc91c111:EEPROM reload not implemented\n");
+            value &= ~3;
+            SET_LOW(ctr, value);
+            return;
+        case 13:
+            SET_HIGH(ctr, value);
+            return;
+        }
+        break;
+
+    case 2:
+        switch (offset) {
+        case 0: /* MMU Command */
+            switch (value >> 5) {
+            case 0: /* no-op */
+                break;
+            case 1: /* Allocate for TX.  */
+                s->tx_alloc = 0x80;
+                s->int_level &= ~INT_ALLOC;
+                smc91c111_update(s);
+                smc91c111_tx_alloc(s);
+                break;
+            case 2: /* Reset MMU.  */
+                s->allocated = 0;
+                s->tx_fifo_len = 0;
+                s->tx_fifo_done_len = 0;
+                s->rx_fifo_len = 0;
+                s->tx_alloc = 0;
+                break;
+            case 3: /* Remove from RX FIFO.  */
+                smc91c111_pop_rx_fifo(s);
+                break;
+            case 4: /* Remove from RX FIFO and release.  */
+                if (s->rx_fifo_len > 0) {
+                    smc91c111_release_packet(s, s->rx_fifo[0]);
+                }
+                smc91c111_pop_rx_fifo(s);
+                break;
+            case 5: /* Release.  */
+                smc91c111_release_packet(s, s->packet_num);
+                break;
+            case 6: /* Add to TX FIFO.  */
+                smc91c111_queue_tx(s, s->packet_num);
+                break;
+            case 7: /* Reset TX FIFO.  */
+                s->tx_fifo_len = 0;
+                s->tx_fifo_done_len = 0;
+                break;
+            }
+            return;
+        case 1:
+            /* Ignore.  */
+            return;
+        case 2: /* Packet Number Register */
+            s->packet_num = value;
+            return;
+        case 3: case 4: case 5:
+            /* Should be readonly, but linux writes to them anyway. Ignore.  */
+            return;
+        case 6: /* Pointer */
+            SET_LOW(ptr, value);
+            return;
+        case 7:
+            SET_HIGH(ptr, value);
+            return;
+        case 8: case 9: case 10: case 11: /* Data */
+            {
+                int p;
+                int n;
+
+                if (s->ptr & 0x8000)
+                    n = s->rx_fifo[0];
+                else
+                    n = s->packet_num;
+                p = s->ptr & 0x07ff;
+                if (s->ptr & 0x4000) {
+                    s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff);
+                } else {
+                    p += (offset & 3);
+                }
+                s->data[n][p] = value;
+            }
+            return;
+        case 12: /* Interrupt ACK.  */
+            s->int_level &= ~(value & 0xd6);
+            if (value & INT_TX)
+                smc91c111_pop_tx_fifo_done(s);
+            smc91c111_update(s);
+            return;
+        case 13: /* Interrupt mask.  */
+            s->int_mask = value;
+            smc91c111_update(s);
+            return;
+        }
+        break;
+
+    case 3:
+        switch (offset) {
+        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+            /* Multicast table.  */
+            /* Not implemented.  */
+            return;
+        case 8: case 9: /* Management Interface.  */
+            /* Not implemented.  */
+            return;
+        case 12: /* Early receive.  */
+            s->ercv = value & 0x1f;
+            return;
+        case 13:
+            /* Ignore.  */
+            return;
+        }
+        break;
+    }
+    hw_error("smc91c111_write: Bad reg %d:%x\n", s->bank, (int)offset);
+}
+
+static uint32_t smc91c111_readb(void *opaque, hwaddr offset)
+{
+    smc91c111_state *s = (smc91c111_state *)opaque;
+
+    offset = offset & 0xf;
+    if (offset == 14) {
+        return s->bank;
+    }
+    if (offset == 15)
+        return 0x33;
+    switch (s->bank) {
+    case 0:
+        switch (offset) {
+        case 0: /* TCR */
+            return s->tcr & 0xff;
+        case 1:
+            return s->tcr >> 8;
+        case 2: /* EPH Status */
+            return 0;
+        case 3:
+            return 0x40;
+        case 4: /* RCR */
+            return s->rcr & 0xff;
+        case 5:
+            return s->rcr >> 8;
+        case 6: /* Counter */
+        case 7:
+            /* Not implemented.  */
+            return 0;
+        case 8: /* Memory size.  */
+            return NUM_PACKETS;
+        case 9: /* Free memory available.  */
+            {
+                int i;
+                int n;
+                n = 0;
+                for (i = 0; i < NUM_PACKETS; i++) {
+                    if (s->allocated & (1 << i))
+                        n++;
+                }
+                return n;
+            }
+        case 10: case 11: /* RPCR */
+            /* Not implemented.  */
+            return 0;
+        case 12: case 13: /* Reserved */
+            return 0;
+        }
+        break;
+
+    case 1:
+        switch (offset) {
+        case 0: /* CONFIG */
+            return s->cr & 0xff;
+        case 1:
+            return s->cr >> 8;
+        case 2: case 3: /* BASE */
+            /* Not implemented.  */
+            return 0;
+        case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
+            return s->conf.macaddr.a[offset - 4];
+        case 10: /* General Purpose */
+            return s->gpr & 0xff;
+        case 11:
+            return s->gpr >> 8;
+        case 12: /* Control */
+            return s->ctr & 0xff;
+        case 13:
+            return s->ctr >> 8;
+        }
+        break;
+
+    case 2:
+        switch (offset) {
+        case 0: case 1: /* MMUCR Busy bit.  */
+            return 0;
+        case 2: /* Packet Number.  */
+            return s->packet_num;
+        case 3: /* Allocation Result.  */
+            return s->tx_alloc;
+        case 4: /* TX FIFO */
+            if (s->tx_fifo_done_len == 0)
+                return 0x80;
+            else
+                return s->tx_fifo_done[0];
+        case 5: /* RX FIFO */
+            if (s->rx_fifo_len == 0)
+                return 0x80;
+            else
+                return s->rx_fifo[0];
+        case 6: /* Pointer */
+            return s->ptr & 0xff;
+        case 7:
+            return (s->ptr >> 8) & 0xf7;
+        case 8: case 9: case 10: case 11: /* Data */
+            {
+                int p;
+                int n;
+
+                if (s->ptr & 0x8000)
+                    n = s->rx_fifo[0];
+                else
+                    n = s->packet_num;
+                p = s->ptr & 0x07ff;
+                if (s->ptr & 0x4000) {
+                    s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff);
+                } else {
+                    p += (offset & 3);
+                }
+                return s->data[n][p];
+            }
+        case 12: /* Interrupt status.  */
+            return s->int_level;
+        case 13: /* Interrupt mask.  */
+            return s->int_mask;
+        }
+        break;
+
+    case 3:
+        switch (offset) {
+        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+            /* Multicast table.  */
+            /* Not implemented.  */
+            return 0;
+        case 8: /* Management Interface.  */
+            /* Not implemented.  */
+            return 0x30;
+        case 9:
+            return 0x33;
+        case 10: /* Revision.  */
+            return 0x91;
+        case 11:
+            return 0x33;
+        case 12:
+            return s->ercv;
+        case 13:
+            return 0;
+        }
+        break;
+    }
+    hw_error("smc91c111_read: Bad reg %d:%x\n", s->bank, (int)offset);
+    return 0;
+}
+
+static void smc91c111_writew(void *opaque, hwaddr offset,
+                             uint32_t value)
+{
+    smc91c111_writeb(opaque, offset, value & 0xff);
+    smc91c111_writeb(opaque, offset + 1, value >> 8);
+}
+
+static void smc91c111_writel(void *opaque, hwaddr offset,
+                             uint32_t value)
+{
+    /* 32-bit writes to offset 0xc only actually write to the bank select
+       register (offset 0xe)  */
+    if (offset != 0xc)
+        smc91c111_writew(opaque, offset, value & 0xffff);
+    smc91c111_writew(opaque, offset + 2, value >> 16);
+}
+
+static uint32_t smc91c111_readw(void *opaque, hwaddr offset)
+{
+    uint32_t val;
+    val = smc91c111_readb(opaque, offset);
+    val |= smc91c111_readb(opaque, offset + 1) << 8;
+    return val;
+}
+
+static uint32_t smc91c111_readl(void *opaque, hwaddr offset)
+{
+    uint32_t val;
+    val = smc91c111_readw(opaque, offset);
+    val |= smc91c111_readw(opaque, offset + 2) << 16;
+    return val;
+}
+
+static int smc91c111_can_receive(NetClientState *nc)
+{
+    smc91c111_state *s = qemu_get_nic_opaque(nc);
+
+    if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
+        return 1;
+    if (s->allocated == (1 << NUM_PACKETS) - 1)
+        return 0;
+    return 1;
+}
+
+static ssize_t smc91c111_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+    smc91c111_state *s = qemu_get_nic_opaque(nc);
+    int status;
+    int packetsize;
+    uint32_t crc;
+    int packetnum;
+    uint8_t *p;
+
+    if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
+        return -1;
+    /* Short packets are padded with zeros.  Receiving a packet
+       < 64 bytes long is considered an error condition.  */
+    if (size < 64)
+        packetsize = 64;
+    else
+        packetsize = (size & ~1);
+    packetsize += 6;
+    crc = (s->rcr & RCR_STRIP_CRC) == 0;
+    if (crc)
+        packetsize += 4;
+    /* TODO: Flag overrun and receive errors.  */
+    if (packetsize > 2048)
+        return -1;
+    packetnum = smc91c111_allocate_packet(s);
+    if (packetnum == 0x80)
+        return -1;
+    s->rx_fifo[s->rx_fifo_len++] = packetnum;
+
+    p = &s->data[packetnum][0];
+    /* ??? Multicast packets?  */
+    status = 0;
+    if (size > 1518)
+        status |= RS_TOOLONG;
+    if (size & 1)
+        status |= RS_ODDFRAME;
+    *(p++) = status & 0xff;
+    *(p++) = status >> 8;
+    *(p++) = packetsize & 0xff;
+    *(p++) = packetsize >> 8;
+    memcpy(p, buf, size & ~1);
+    p += (size & ~1);
+    /* Pad short packets.  */
+    if (size < 64) {
+        int pad;
+
+        if (size & 1)
+            *(p++) = buf[size - 1];
+        pad = 64 - size;
+        memset(p, 0, pad);
+        p += pad;
+        size = 64;
+    }
+    /* It's not clear if the CRC should go before or after the last byte in
+       odd sized packets.  Linux disables the CRC, so that's no help.
+       The pictures in the documentation show the CRC aligned on a 16-bit
+       boundary before the last odd byte, so that's what we do.  */
+    if (crc) {
+        crc = crc32(~0, buf, size);
+        *(p++) = crc & 0xff; crc >>= 8;
+        *(p++) = crc & 0xff; crc >>= 8;
+        *(p++) = crc & 0xff; crc >>= 8;
+        *(p++) = crc & 0xff;
+    }
+    if (size & 1) {
+        *(p++) = buf[size - 1];
+        *p = 0x60;
+    } else {
+        *(p++) = 0;
+        *p = 0x40;
+    }
+    /* TODO: Raise early RX interrupt?  */
+    s->int_level |= INT_RCV;
+    smc91c111_update(s);
+
+    return size;
+}
+
+static const MemoryRegionOps smc91c111_mem_ops = {
+    /* The special case for 32 bit writes to 0xc means we can't just
+     * set .impl.min/max_access_size to 1, unfortunately
+     */
+    .old_mmio = {
+        .read = { smc91c111_readb, smc91c111_readw, smc91c111_readl, },
+        .write = { smc91c111_writeb, smc91c111_writew, smc91c111_writel, },
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void smc91c111_cleanup(NetClientState *nc)
+{
+    smc91c111_state *s = qemu_get_nic_opaque(nc);
+
+    s->nic = NULL;
+}
+
+static NetClientInfo net_smc91c111_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = smc91c111_can_receive,
+    .receive = smc91c111_receive,
+    .cleanup = smc91c111_cleanup,
+};
+
+static int smc91c111_init1(SysBusDevice *dev)
+{
+    smc91c111_state *s = FROM_SYSBUS(smc91c111_state, dev);
+    memory_region_init_io(&s->mmio, &smc91c111_mem_ops, s,
+                          "smc91c111-mmio", 16);
+    sysbus_init_mmio(dev, &s->mmio);
+    sysbus_init_irq(dev, &s->irq);
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+    s->nic = qemu_new_nic(&net_smc91c111_info, &s->conf,
+                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+    /* ??? Save/restore.  */
+    return 0;
+}
+
+static Property smc91c111_properties[] = {
+    DEFINE_NIC_PROPERTIES(smc91c111_state, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void smc91c111_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = smc91c111_init1;
+    dc->reset = smc91c111_reset;
+    dc->vmsd = &vmstate_smc91c111;
+    dc->props = smc91c111_properties;
+}
+
+static const TypeInfo smc91c111_info = {
+    .name          = "smc91c111",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(smc91c111_state),
+    .class_init    = smc91c111_class_init,
+};
+
+static void smc91c111_register_types(void)
+{
+    type_register_static(&smc91c111_info);
+}
+
+/* Legacy helper function.  Should go away when machine config files are
+   implemented.  */
+void smc91c111_init(NICInfo *nd, uint32_t base, qemu_irq irq)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    qemu_check_nic_model(nd, "smc91c111");
+    dev = qdev_create(NULL, "smc91c111");
+    qdev_set_nic_properties(dev, nd);
+    qdev_init_nofail(dev);
+    s = SYS_BUS_DEVICE(dev);
+    sysbus_mmio_map(s, 0, base);
+    sysbus_connect_irq(s, 0, irq);
+}
+
+type_init(smc91c111_register_types)
diff --git a/hw/net/vmware_utils.h b/hw/net/vmware_utils.h
new file mode 100644 (file)
index 0000000..5307e2c
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * QEMU VMWARE paravirtual devices - auxiliary code
+ *
+ * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
+ *
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
+ *
+ * Authors:
+ * Dmitry Fleytman <dmitry@daynix.com>
+ * Yan Vugenfirer <yan@daynix.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef VMWARE_UTILS_H
+#define VMWARE_UTILS_H
+
+#include "qemu/range.h"
+
+#ifndef VMW_SHPRN
+#define VMW_SHPRN(fmt, ...) do {} while (0)
+#endif
+
+/*
+ * Shared memory access functions with byte swap support
+ * Each function contains printout for reverse-engineering needs
+ *
+ */
+static inline void
+vmw_shmem_read(hwaddr addr, void *buf, int len)
+{
+    VMW_SHPRN("SHMEM r: %" PRIx64 ", len: %d to %p", addr, len, buf);
+    cpu_physical_memory_read(addr, buf, len);
+}
+
+static inline void
+vmw_shmem_write(hwaddr addr, void *buf, int len)
+{
+    VMW_SHPRN("SHMEM w: %" PRIx64 ", len: %d to %p", addr, len, buf);
+    cpu_physical_memory_write(addr, buf, len);
+}
+
+static inline void
+vmw_shmem_rw(hwaddr addr, void *buf, int len, int is_write)
+{
+    VMW_SHPRN("SHMEM r/w: %" PRIx64 ", len: %d (to %p), is write: %d",
+              addr, len, buf, is_write);
+
+    cpu_physical_memory_rw(addr, buf, len, is_write);
+}
+
+static inline void
+vmw_shmem_set(hwaddr addr, uint8 val, int len)
+{
+    int i;
+    VMW_SHPRN("SHMEM set: %" PRIx64 ", len: %d (value 0x%X)", addr, len, val);
+
+    for (i = 0; i < len; i++) {
+        cpu_physical_memory_write(addr + i, &val, 1);
+    }
+}
+
+static inline uint32_t
+vmw_shmem_ld8(hwaddr addr)
+{
+    uint8_t res = ldub_phys(addr);
+    VMW_SHPRN("SHMEM load8: %" PRIx64 " (value 0x%X)", addr, res);
+    return res;
+}
+
+static inline void
+vmw_shmem_st8(hwaddr addr, uint8_t value)
+{
+    VMW_SHPRN("SHMEM store8: %" PRIx64 " (value 0x%X)", addr, value);
+    stb_phys(addr, value);
+}
+
+static inline uint32_t
+vmw_shmem_ld16(hwaddr addr)
+{
+    uint16_t res = lduw_le_phys(addr);
+    VMW_SHPRN("SHMEM load16: %" PRIx64 " (value 0x%X)", addr, res);
+    return res;
+}
+
+static inline void
+vmw_shmem_st16(hwaddr addr, uint16_t value)
+{
+    VMW_SHPRN("SHMEM store16: %" PRIx64 " (value 0x%X)", addr, value);
+    stw_le_phys(addr, value);
+}
+
+static inline uint32_t
+vmw_shmem_ld32(hwaddr addr)
+{
+    uint32_t res = ldl_le_phys(addr);
+    VMW_SHPRN("SHMEM load32: %" PRIx64 " (value 0x%X)", addr, res);
+    return res;
+}
+
+static inline void
+vmw_shmem_st32(hwaddr addr, uint32_t value)
+{
+    VMW_SHPRN("SHMEM store32: %" PRIx64 " (value 0x%X)", addr, value);
+    stl_le_phys(addr, value);
+}
+
+static inline uint64_t
+vmw_shmem_ld64(hwaddr addr)
+{
+    uint64_t res = ldq_le_phys(addr);
+    VMW_SHPRN("SHMEM load64: %" PRIx64 " (value %" PRIx64 ")", addr, res);
+    return res;
+}
+
+static inline void
+vmw_shmem_st64(hwaddr addr, uint64_t value)
+{
+    VMW_SHPRN("SHMEM store64: %" PRIx64 " (value %" PRIx64 ")", addr, value);
+    stq_le_phys(addr, value);
+}
+
+/* Macros for simplification of operations on array-style registers */
+
+/*
+ * Whether <addr> lies inside of array-style register defined by <base>,
+ * number of elements (<cnt>) and element size (<regsize>)
+ *
+*/
+#define VMW_IS_MULTIREG_ADDR(addr, base, cnt, regsize)                 \
+    range_covers_byte(base, cnt * regsize, addr)
+
+/*
+ * Returns index of given register (<addr>) in array-style register defined by
+ * <base> and element size (<regsize>)
+ *
+*/
+#define VMW_MULTIREG_IDX_BY_ADDR(addr, base, regsize)                  \
+    (((addr) - (base)) / (regsize))
+
+#endif
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
new file mode 100644 (file)
index 0000000..5916624
--- /dev/null
@@ -0,0 +1,2460 @@
+/*
+ * QEMU VMWARE VMXNET3 paravirtual NIC
+ *
+ * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
+ *
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
+ *
+ * Authors:
+ * Dmitry Fleytman <dmitry@daynix.com>
+ * Tamir Shomer <tamirs@daynix.com>
+ * Yan Vugenfirer <yan@daynix.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "net/net.h"
+#include "net/tap.h"
+#include "net/checksum.h"
+#include "sysemu/sysemu.h"
+#include "qemu-common.h"
+#include "qemu/bswap.h"
+#include "hw/pci/msix.h"
+#include "hw/pci/msi.h"
+
+#include "vmxnet3.h"
+#include "vmxnet_debug.h"
+#include "vmware_utils.h"
+#include "vmxnet_tx_pkt.h"
+#include "vmxnet_rx_pkt.h"
+
+#define PCI_DEVICE_ID_VMWARE_VMXNET3_REVISION 0x1
+#define VMXNET3_MSIX_BAR_SIZE 0x2000
+
+#define VMXNET3_BAR0_IDX      (0)
+#define VMXNET3_BAR1_IDX      (1)
+#define VMXNET3_MSIX_BAR_IDX  (2)
+
+#define VMXNET3_OFF_MSIX_TABLE (0x000)
+#define VMXNET3_OFF_MSIX_PBA   (0x800)
+
+/* Link speed in Mbps should be shifted by 16 */
+#define VMXNET3_LINK_SPEED      (1000 << 16)
+
+/* Link status: 1 - up, 0 - down. */
+#define VMXNET3_LINK_STATUS_UP  0x1
+
+/* Least significant bit should be set for revision and version */
+#define VMXNET3_DEVICE_VERSION    0x1
+#define VMXNET3_DEVICE_REVISION   0x1
+
+/* Macros for rings descriptors access */
+#define VMXNET3_READ_TX_QUEUE_DESCR8(dpa, field) \
+    (vmw_shmem_ld8(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field)))
+
+#define VMXNET3_WRITE_TX_QUEUE_DESCR8(dpa, field, value) \
+    (vmw_shmem_st8(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field, value)))
+
+#define VMXNET3_READ_TX_QUEUE_DESCR32(dpa, field) \
+    (vmw_shmem_ld32(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field)))
+
+#define VMXNET3_WRITE_TX_QUEUE_DESCR32(dpa, field, value) \
+    (vmw_shmem_st32(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field), value))
+
+#define VMXNET3_READ_TX_QUEUE_DESCR64(dpa, field) \
+    (vmw_shmem_ld64(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field)))
+
+#define VMXNET3_WRITE_TX_QUEUE_DESCR64(dpa, field, value) \
+    (vmw_shmem_st64(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field), value))
+
+#define VMXNET3_READ_RX_QUEUE_DESCR64(dpa, field) \
+    (vmw_shmem_ld64(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field)))
+
+#define VMXNET3_READ_RX_QUEUE_DESCR32(dpa, field) \
+    (vmw_shmem_ld32(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field)))
+
+#define VMXNET3_WRITE_RX_QUEUE_DESCR64(dpa, field, value) \
+    (vmw_shmem_st64(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field), value))
+
+#define VMXNET3_WRITE_RX_QUEUE_DESCR8(dpa, field, value) \
+    (vmw_shmem_st8(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field), value))
+
+/* Macros for guest driver shared area access */
+#define VMXNET3_READ_DRV_SHARED64(shpa, field) \
+    (vmw_shmem_ld64(shpa + offsetof(struct Vmxnet3_DriverShared, field)))
+
+#define VMXNET3_READ_DRV_SHARED32(shpa, field) \
+    (vmw_shmem_ld32(shpa + offsetof(struct Vmxnet3_DriverShared, field)))
+
+#define VMXNET3_WRITE_DRV_SHARED32(shpa, field, val) \
+    (vmw_shmem_st32(shpa + offsetof(struct Vmxnet3_DriverShared, field), val))
+
+#define VMXNET3_READ_DRV_SHARED16(shpa, field) \
+    (vmw_shmem_ld16(shpa + offsetof(struct Vmxnet3_DriverShared, field)))
+
+#define VMXNET3_READ_DRV_SHARED8(shpa, field) \
+    (vmw_shmem_ld8(shpa + offsetof(struct Vmxnet3_DriverShared, field)))
+
+#define VMXNET3_READ_DRV_SHARED(shpa, field, b, l) \
+    (vmw_shmem_read(shpa + offsetof(struct Vmxnet3_DriverShared, field), b, l))
+
+#define VMXNET_FLAG_IS_SET(field, flag) (((field) & (flag)) == (flag))
+
+#define TYPE_VMXNET3 "vmxnet3"
+#define VMXNET3(obj) OBJECT_CHECK(VMXNET3State, (obj), TYPE_VMXNET3)
+
+/* Cyclic ring abstraction */
+typedef struct {
+    hwaddr pa;
+    size_t size;
+    size_t cell_size;
+    size_t next;
+    uint8_t gen;
+} Vmxnet3Ring;
+
+static inline void vmxnet3_ring_init(Vmxnet3Ring *ring,
+                                     hwaddr pa,
+                                     size_t size,
+                                     size_t cell_size,
+                                     bool zero_region)
+{
+    ring->pa = pa;
+    ring->size = size;
+    ring->cell_size = cell_size;
+    ring->gen = VMXNET3_INIT_GEN;
+    ring->next = 0;
+
+    if (zero_region) {
+        vmw_shmem_set(pa, 0, size * cell_size);
+    }
+}
+
+#define VMXNET3_RING_DUMP(macro, ring_name, ridx, r)                         \
+    macro("%s#%d: base %" PRIx64 " size %lu cell_size %lu gen %d next %lu",  \
+          (ring_name), (ridx),                                               \
+          (r)->pa, (r)->size, (r)->cell_size, (r)->gen, (r)->next)
+
+static inline void vmxnet3_ring_inc(Vmxnet3Ring *ring)
+{
+    if (++ring->next >= ring->size) {
+        ring->next = 0;
+        ring->gen ^= 1;
+    }
+}
+
+static inline void vmxnet3_ring_dec(Vmxnet3Ring *ring)
+{
+    if (ring->next-- == 0) {
+        ring->next = ring->size - 1;
+        ring->gen ^= 1;
+    }
+}
+
+static inline hwaddr vmxnet3_ring_curr_cell_pa(Vmxnet3Ring *ring)
+{
+    return ring->pa + ring->next * ring->cell_size;
+}
+
+static inline void vmxnet3_ring_read_curr_cell(Vmxnet3Ring *ring, void *buff)
+{
+    vmw_shmem_read(vmxnet3_ring_curr_cell_pa(ring), buff, ring->cell_size);
+}
+
+static inline void vmxnet3_ring_write_curr_cell(Vmxnet3Ring *ring, void *buff)
+{
+    vmw_shmem_write(vmxnet3_ring_curr_cell_pa(ring), buff, ring->cell_size);
+}
+
+static inline size_t vmxnet3_ring_curr_cell_idx(Vmxnet3Ring *ring)
+{
+    return ring->next;
+}
+
+static inline uint8_t vmxnet3_ring_curr_gen(Vmxnet3Ring *ring)
+{
+    return ring->gen;
+}
+
+/* Debug trace-related functions */
+static inline void
+vmxnet3_dump_tx_descr(struct Vmxnet3_TxDesc *descr)
+{
+    VMW_PKPRN("TX DESCR: "
+              "addr %" PRIx64 ", len: %d, gen: %d, rsvd: %d, "
+              "dtype: %d, ext1: %d, msscof: %d, hlen: %d, om: %d, "
+              "eop: %d, cq: %d, ext2: %d, ti: %d, tci: %d",
+              le64_to_cpu(descr->addr), descr->len, descr->gen, descr->rsvd,
+              descr->dtype, descr->ext1, descr->msscof, descr->hlen, descr->om,
+              descr->eop, descr->cq, descr->ext2, descr->ti, descr->tci);
+}
+
+static inline void
+vmxnet3_dump_virt_hdr(struct virtio_net_hdr *vhdr)
+{
+    VMW_PKPRN("VHDR: flags 0x%x, gso_type: 0x%x, hdr_len: %d, gso_size: %d, "
+              "csum_start: %d, csum_offset: %d",
+              vhdr->flags, vhdr->gso_type, vhdr->hdr_len, vhdr->gso_size,
+              vhdr->csum_start, vhdr->csum_offset);
+}
+
+static inline void
+vmxnet3_dump_rx_descr(struct Vmxnet3_RxDesc *descr)
+{
+    VMW_PKPRN("RX DESCR: addr %" PRIx64 ", len: %d, gen: %d, rsvd: %d, "
+              "dtype: %d, ext1: %d, btype: %d",
+              le64_to_cpu(descr->addr), descr->len, descr->gen,
+              descr->rsvd, descr->dtype, descr->ext1, descr->btype);
+}
+
+/* Device state and helper functions */
+#define VMXNET3_RX_RINGS_PER_QUEUE (2)
+
+typedef struct {
+    Vmxnet3Ring tx_ring;
+    Vmxnet3Ring comp_ring;
+
+    uint8_t intr_idx;
+    hwaddr tx_stats_pa;
+    struct UPT1_TxStats txq_stats;
+} Vmxnet3TxqDescr;
+
+typedef struct {
+    Vmxnet3Ring rx_ring[VMXNET3_RX_RINGS_PER_QUEUE];
+    Vmxnet3Ring comp_ring;
+    uint8_t intr_idx;
+    hwaddr rx_stats_pa;
+    struct UPT1_RxStats rxq_stats;
+} Vmxnet3RxqDescr;
+
+typedef struct {
+    bool is_masked;
+    bool is_pending;
+    bool is_asserted;
+} Vmxnet3IntState;
+
+typedef struct {
+        PCIDevice parent_obj;
+        NICState *nic;
+        NICConf conf;
+        MemoryRegion bar0;
+        MemoryRegion bar1;
+        MemoryRegion msix_bar;
+
+        Vmxnet3RxqDescr rxq_descr[VMXNET3_DEVICE_MAX_RX_QUEUES];
+        Vmxnet3TxqDescr txq_descr[VMXNET3_DEVICE_MAX_TX_QUEUES];
+
+        /* Whether MSI-X support was installed successfully */
+        bool msix_used;
+        /* Whether MSI support was installed successfully */
+        bool msi_used;
+        hwaddr drv_shmem;
+        hwaddr temp_shared_guest_driver_memory;
+
+        uint8_t txq_num;
+
+        /* This boolean tells whether RX packet being indicated has to */
+        /* be split into head and body chunks from different RX rings  */
+        bool rx_packets_compound;
+
+        bool rx_vlan_stripping;
+        bool lro_supported;
+
+        uint8_t rxq_num;
+
+        /* Network MTU */
+        uint32_t mtu;
+
+        /* Maximum number of fragments for indicated TX packets */
+        uint32_t max_tx_frags;
+
+        /* Maximum number of fragments for indicated RX packets */
+        uint16_t max_rx_frags;
+
+        /* Index for events interrupt */
+        uint8_t event_int_idx;
+
+        /* Whether automatic interrupts masking enabled */
+        bool auto_int_masking;
+
+        bool peer_has_vhdr;
+
+        /* TX packets to QEMU interface */
+        struct VmxnetTxPkt *tx_pkt;
+        uint32_t offload_mode;
+        uint32_t cso_or_gso_size;
+        uint16_t tci;
+        bool needs_vlan;
+
+        struct VmxnetRxPkt *rx_pkt;
+
+        bool tx_sop;
+        bool skip_current_tx_pkt;
+
+        uint32_t device_active;
+        uint32_t last_command;
+
+        uint32_t link_status_and_speed;
+
+        Vmxnet3IntState interrupt_states[VMXNET3_MAX_INTRS];
+
+        uint32_t temp_mac;   /* To store the low part first */
+
+        MACAddr perm_mac;
+        uint32_t vlan_table[VMXNET3_VFT_SIZE];
+        uint32_t rx_mode;
+        MACAddr *mcast_list;
+        uint32_t mcast_list_len;
+        uint32_t mcast_list_buff_size; /* needed for live migration. */
+} VMXNET3State;
+
+/* Interrupt management */
+
+/*
+ *This function returns sign whether interrupt line is in asserted state
+ * This depends on the type of interrupt used. For INTX interrupt line will
+ * be asserted until explicit deassertion, for MSI(X) interrupt line will
+ * be deasserted automatically due to notification semantics of the MSI(X)
+ * interrupts
+ */
+static bool _vmxnet3_assert_interrupt_line(VMXNET3State *s, uint32_t int_idx)
+{
+    PCIDevice *d = PCI_DEVICE(s);
+
+    if (s->msix_used && msix_enabled(d)) {
+        VMW_IRPRN("Sending MSI-X notification for vector %u", int_idx);
+        msix_notify(d, int_idx);
+        return false;
+    }
+    if (s->msi_used && msi_enabled(d)) {
+        VMW_IRPRN("Sending MSI notification for vector %u", int_idx);
+        msi_notify(d, int_idx);
+        return false;
+    }
+
+    VMW_IRPRN("Asserting line for interrupt %u", int_idx);
+    qemu_set_irq(d->irq[int_idx], 1);
+    return true;
+}
+
+static void _vmxnet3_deassert_interrupt_line(VMXNET3State *s, int lidx)
+{
+    PCIDevice *d = PCI_DEVICE(s);
+
+    /*
+     * This function should never be called for MSI(X) interrupts
+     * because deassertion never required for message interrupts
+     */
+    assert(!s->msix_used || !msix_enabled(d));
+    /*
+     * This function should never be called for MSI(X) interrupts
+     * because deassertion never required for message interrupts
+     */
+    assert(!s->msi_used || !msi_enabled(d));
+
+    VMW_IRPRN("Deasserting line for interrupt %u", lidx);
+    qemu_set_irq(d->irq[lidx], 0);
+}
+
+static void vmxnet3_update_interrupt_line_state(VMXNET3State *s, int lidx)
+{
+    if (!s->interrupt_states[lidx].is_pending &&
+       s->interrupt_states[lidx].is_asserted) {
+        VMW_IRPRN("New interrupt line state for index %d is DOWN", lidx);
+        _vmxnet3_deassert_interrupt_line(s, lidx);
+        s->interrupt_states[lidx].is_asserted = false;
+        return;
+    }
+
+    if (s->interrupt_states[lidx].is_pending &&
+       !s->interrupt_states[lidx].is_masked &&
+       !s->interrupt_states[lidx].is_asserted) {
+        VMW_IRPRN("New interrupt line state for index %d is UP", lidx);
+        s->interrupt_states[lidx].is_asserted =
+            _vmxnet3_assert_interrupt_line(s, lidx);
+        s->interrupt_states[lidx].is_pending = false;
+        return;
+    }
+}
+
+static void vmxnet3_trigger_interrupt(VMXNET3State *s, int lidx)
+{
+    PCIDevice *d = PCI_DEVICE(s);
+    s->interrupt_states[lidx].is_pending = true;
+    vmxnet3_update_interrupt_line_state(s, lidx);
+
+    if (s->msix_used && msix_enabled(d) && s->auto_int_masking) {
+        goto do_automask;
+    }
+
+    if (s->msi_used && msi_enabled(d) && s->auto_int_masking) {
+        goto do_automask;
+    }
+
+    return;
+
+do_automask:
+    s->interrupt_states[lidx].is_masked = true;
+    vmxnet3_update_interrupt_line_state(s, lidx);
+}
+
+static bool vmxnet3_interrupt_asserted(VMXNET3State *s, int lidx)
+{
+    return s->interrupt_states[lidx].is_asserted;
+}
+
+static void vmxnet3_clear_interrupt(VMXNET3State *s, int int_idx)
+{
+    s->interrupt_states[int_idx].is_pending = false;
+    if (s->auto_int_masking) {
+        s->interrupt_states[int_idx].is_masked = true;
+    }
+    vmxnet3_update_interrupt_line_state(s, int_idx);
+}
+
+static void
+vmxnet3_on_interrupt_mask_changed(VMXNET3State *s, int lidx, bool is_masked)
+{
+    s->interrupt_states[lidx].is_masked = is_masked;
+    vmxnet3_update_interrupt_line_state(s, lidx);
+}
+
+static bool vmxnet3_verify_driver_magic(hwaddr dshmem)
+{
+    return (VMXNET3_READ_DRV_SHARED32(dshmem, magic) == VMXNET3_REV1_MAGIC);
+}
+
+#define VMXNET3_GET_BYTE(x, byte_num) (((x) >> (byte_num)*8) & 0xFF)
+#define VMXNET3_MAKE_BYTE(byte_num, val) \
+    (((uint32_t)((val) & 0xFF)) << (byte_num)*8)
+
+static void vmxnet3_set_variable_mac(VMXNET3State *s, uint32_t h, uint32_t l)
+{
+    s->conf.macaddr.a[0] = VMXNET3_GET_BYTE(l,  0);
+    s->conf.macaddr.a[1] = VMXNET3_GET_BYTE(l,  1);
+    s->conf.macaddr.a[2] = VMXNET3_GET_BYTE(l,  2);
+    s->conf.macaddr.a[3] = VMXNET3_GET_BYTE(l,  3);
+    s->conf.macaddr.a[4] = VMXNET3_GET_BYTE(h, 0);
+    s->conf.macaddr.a[5] = VMXNET3_GET_BYTE(h, 1);
+
+    VMW_CFPRN("Variable MAC: " VMXNET_MF, VMXNET_MA(s->conf.macaddr.a));
+
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+}
+
+static uint64_t vmxnet3_get_mac_low(MACAddr *addr)
+{
+    return VMXNET3_MAKE_BYTE(0, addr->a[0]) |
+           VMXNET3_MAKE_BYTE(1, addr->a[1]) |
+           VMXNET3_MAKE_BYTE(2, addr->a[2]) |
+           VMXNET3_MAKE_BYTE(3, addr->a[3]);
+}
+
+static uint64_t vmxnet3_get_mac_high(MACAddr *addr)
+{
+    return VMXNET3_MAKE_BYTE(0, addr->a[4]) |
+           VMXNET3_MAKE_BYTE(1, addr->a[5]);
+}
+
+static void
+vmxnet3_inc_tx_consumption_counter(VMXNET3State *s, int qidx)
+{
+    vmxnet3_ring_inc(&s->txq_descr[qidx].tx_ring);
+}
+
+static inline void
+vmxnet3_inc_rx_consumption_counter(VMXNET3State *s, int qidx, int ridx)
+{
+    vmxnet3_ring_inc(&s->rxq_descr[qidx].rx_ring[ridx]);
+}
+
+static inline void
+vmxnet3_inc_tx_completion_counter(VMXNET3State *s, int qidx)
+{
+    vmxnet3_ring_inc(&s->txq_descr[qidx].comp_ring);
+}
+
+static void
+vmxnet3_inc_rx_completion_counter(VMXNET3State *s, int qidx)
+{
+    vmxnet3_ring_inc(&s->rxq_descr[qidx].comp_ring);
+}
+
+static void
+vmxnet3_dec_rx_completion_counter(VMXNET3State *s, int qidx)
+{
+    vmxnet3_ring_dec(&s->rxq_descr[qidx].comp_ring);
+}
+
+static void vmxnet3_complete_packet(VMXNET3State *s, int qidx, uint32 tx_ridx)
+{
+    struct Vmxnet3_TxCompDesc txcq_descr;
+
+    VMXNET3_RING_DUMP(VMW_RIPRN, "TXC", qidx, &s->txq_descr[qidx].comp_ring);
+
+    txcq_descr.txdIdx = tx_ridx;
+    txcq_descr.gen = vmxnet3_ring_curr_gen(&s->txq_descr[qidx].comp_ring);
+
+    vmxnet3_ring_write_curr_cell(&s->txq_descr[qidx].comp_ring, &txcq_descr);
+
+    /* Flush changes in TX descriptor before changing the counter value */
+    smp_wmb();
+
+    vmxnet3_inc_tx_completion_counter(s, qidx);
+    vmxnet3_trigger_interrupt(s, s->txq_descr[qidx].intr_idx);
+}
+
+static bool
+vmxnet3_setup_tx_offloads(VMXNET3State *s)
+{
+    switch (s->offload_mode) {
+    case VMXNET3_OM_NONE:
+        vmxnet_tx_pkt_build_vheader(s->tx_pkt, false, false, 0);
+        break;
+
+    case VMXNET3_OM_CSUM:
+        vmxnet_tx_pkt_build_vheader(s->tx_pkt, false, true, 0);
+        VMW_PKPRN("L4 CSO requested\n");
+        break;
+
+    case VMXNET3_OM_TSO:
+        vmxnet_tx_pkt_build_vheader(s->tx_pkt, true, true,
+            s->cso_or_gso_size);
+        vmxnet_tx_pkt_update_ip_checksums(s->tx_pkt);
+        VMW_PKPRN("GSO offload requested.");
+        break;
+
+    default:
+        assert(false);
+        return false;
+    }
+
+    return true;
+}
+
+static void
+vmxnet3_tx_retrieve_metadata(VMXNET3State *s,
+                             const struct Vmxnet3_TxDesc *txd)
+{
+    s->offload_mode = txd->om;
+    s->cso_or_gso_size = txd->msscof;
+    s->tci = txd->tci;
+    s->needs_vlan = txd->ti;
+}
+
+typedef enum {
+    VMXNET3_PKT_STATUS_OK,
+    VMXNET3_PKT_STATUS_ERROR,
+    VMXNET3_PKT_STATUS_DISCARD,/* only for tx */
+    VMXNET3_PKT_STATUS_OUT_OF_BUF /* only for rx */
+} Vmxnet3PktStatus;
+
+static void
+vmxnet3_on_tx_done_update_stats(VMXNET3State *s, int qidx,
+    Vmxnet3PktStatus status)
+{
+    size_t tot_len = vmxnet_tx_pkt_get_total_len(s->tx_pkt);
+    struct UPT1_TxStats *stats = &s->txq_descr[qidx].txq_stats;
+
+    switch (status) {
+    case VMXNET3_PKT_STATUS_OK:
+        switch (vmxnet_tx_pkt_get_packet_type(s->tx_pkt)) {
+        case ETH_PKT_BCAST:
+            stats->bcastPktsTxOK++;
+            stats->bcastBytesTxOK += tot_len;
+            break;
+        case ETH_PKT_MCAST:
+            stats->mcastPktsTxOK++;
+            stats->mcastBytesTxOK += tot_len;
+            break;
+        case ETH_PKT_UCAST:
+            stats->ucastPktsTxOK++;
+            stats->ucastBytesTxOK += tot_len;
+            break;
+        default:
+            assert(false);
+        }
+
+        if (s->offload_mode == VMXNET3_OM_TSO) {
+            /*
+             * According to VMWARE headers this statistic is a number
+             * of packets after segmentation but since we don't have
+             * this information in QEMU model, the best we can do is to
+             * provide number of non-segmented packets
+             */
+            stats->TSOPktsTxOK++;
+            stats->TSOBytesTxOK += tot_len;
+        }
+        break;
+
+    case VMXNET3_PKT_STATUS_DISCARD:
+        stats->pktsTxDiscard++;
+        break;
+
+    case VMXNET3_PKT_STATUS_ERROR:
+        stats->pktsTxError++;
+        break;
+
+    default:
+        assert(false);
+    }
+}
+
+static void
+vmxnet3_on_rx_done_update_stats(VMXNET3State *s,
+                                int qidx,
+                                Vmxnet3PktStatus status)
+{
+    struct UPT1_RxStats *stats = &s->rxq_descr[qidx].rxq_stats;
+    size_t tot_len = vmxnet_rx_pkt_get_total_len(s->rx_pkt);
+
+    switch (status) {
+    case VMXNET3_PKT_STATUS_OUT_OF_BUF:
+        stats->pktsRxOutOfBuf++;
+        break;
+
+    case VMXNET3_PKT_STATUS_ERROR:
+        stats->pktsRxError++;
+        break;
+    case VMXNET3_PKT_STATUS_OK:
+        switch (vmxnet_rx_pkt_get_packet_type(s->rx_pkt)) {
+        case ETH_PKT_BCAST:
+            stats->bcastPktsRxOK++;
+            stats->bcastBytesRxOK += tot_len;
+            break;
+        case ETH_PKT_MCAST:
+            stats->mcastPktsRxOK++;
+            stats->mcastBytesRxOK += tot_len;
+            break;
+        case ETH_PKT_UCAST:
+            stats->ucastPktsRxOK++;
+            stats->ucastBytesRxOK += tot_len;
+            break;
+        default:
+            assert(false);
+        }
+
+        if (tot_len > s->mtu) {
+            stats->LROPktsRxOK++;
+            stats->LROBytesRxOK += tot_len;
+        }
+        break;
+    default:
+        assert(false);
+    }
+}
+
+static inline bool
+vmxnet3_pop_next_tx_descr(VMXNET3State *s,
+                          int qidx,
+                          struct Vmxnet3_TxDesc *txd,
+                          uint32_t *descr_idx)
+{
+    Vmxnet3Ring *ring = &s->txq_descr[qidx].tx_ring;
+
+    vmxnet3_ring_read_curr_cell(ring, txd);
+    if (txd->gen == vmxnet3_ring_curr_gen(ring)) {
+        /* Only read after generation field verification */
+        smp_rmb();
+        /* Re-read to be sure we got the latest version */
+        vmxnet3_ring_read_curr_cell(ring, txd);
+        VMXNET3_RING_DUMP(VMW_RIPRN, "TX", qidx, ring);
+        *descr_idx = vmxnet3_ring_curr_cell_idx(ring);
+        vmxnet3_inc_tx_consumption_counter(s, qidx);
+        return true;
+    }
+
+    return false;
+}
+
+static bool
+vmxnet3_send_packet(VMXNET3State *s, uint32_t qidx)
+{
+    Vmxnet3PktStatus status = VMXNET3_PKT_STATUS_OK;
+
+    if (!vmxnet3_setup_tx_offloads(s)) {
+        status = VMXNET3_PKT_STATUS_ERROR;
+        goto func_exit;
+    }
+
+    /* debug prints */
+    vmxnet3_dump_virt_hdr(vmxnet_tx_pkt_get_vhdr(s->tx_pkt));
+    vmxnet_tx_pkt_dump(s->tx_pkt);
+
+    if (!vmxnet_tx_pkt_send(s->tx_pkt, qemu_get_queue(s->nic))) {
+        status = VMXNET3_PKT_STATUS_DISCARD;
+        goto func_exit;
+    }
+
+func_exit:
+    vmxnet3_on_tx_done_update_stats(s, qidx, status);
+    return (status == VMXNET3_PKT_STATUS_OK);
+}
+
+static void vmxnet3_process_tx_queue(VMXNET3State *s, int qidx)
+{
+    struct Vmxnet3_TxDesc txd;
+    uint32_t txd_idx;
+    uint32_t data_len;
+    hwaddr data_pa;
+
+    for (;;) {
+        if (!vmxnet3_pop_next_tx_descr(s, qidx, &txd, &txd_idx)) {
+            break;
+        }
+
+        vmxnet3_dump_tx_descr(&txd);
+
+        if (!s->skip_current_tx_pkt) {
+            data_len = (txd.len > 0) ? txd.len : VMXNET3_MAX_TX_BUF_SIZE;
+            data_pa = le64_to_cpu(txd.addr);
+
+            if (!vmxnet_tx_pkt_add_raw_fragment(s->tx_pkt,
+                                                data_pa,
+                                                data_len)) {
+                s->skip_current_tx_pkt = true;
+            }
+        }
+
+        if (s->tx_sop) {
+            vmxnet3_tx_retrieve_metadata(s, &txd);
+            s->tx_sop = false;
+        }
+
+        if (txd.eop) {
+            if (!s->skip_current_tx_pkt) {
+                vmxnet_tx_pkt_parse(s->tx_pkt);
+
+                if (s->needs_vlan) {
+                    vmxnet_tx_pkt_setup_vlan_header(s->tx_pkt, s->tci);
+                }
+
+                vmxnet3_send_packet(s, qidx);
+            } else {
+                vmxnet3_on_tx_done_update_stats(s, qidx,
+                                                VMXNET3_PKT_STATUS_ERROR);
+            }
+
+            vmxnet3_complete_packet(s, qidx, txd_idx);
+            s->tx_sop = true;
+            s->skip_current_tx_pkt = false;
+            vmxnet_tx_pkt_reset(s->tx_pkt);
+        }
+    }
+}
+
+static inline void
+vmxnet3_read_next_rx_descr(VMXNET3State *s, int qidx, int ridx,
+                           struct Vmxnet3_RxDesc *dbuf, uint32_t *didx)
+{
+    Vmxnet3Ring *ring = &s->rxq_descr[qidx].rx_ring[ridx];
+    *didx = vmxnet3_ring_curr_cell_idx(ring);
+    vmxnet3_ring_read_curr_cell(ring, dbuf);
+}
+
+static inline uint8_t
+vmxnet3_get_rx_ring_gen(VMXNET3State *s, int qidx, int ridx)
+{
+    return s->rxq_descr[qidx].rx_ring[ridx].gen;
+}
+
+static inline hwaddr
+vmxnet3_pop_rxc_descr(VMXNET3State *s, int qidx, uint32_t *descr_gen)
+{
+    uint8_t ring_gen;
+    struct Vmxnet3_RxCompDesc rxcd;
+
+    hwaddr daddr =
+        vmxnet3_ring_curr_cell_pa(&s->rxq_descr[qidx].comp_ring);
+
+    cpu_physical_memory_read(daddr, &rxcd, sizeof(struct Vmxnet3_RxCompDesc));
+    ring_gen = vmxnet3_ring_curr_gen(&s->rxq_descr[qidx].comp_ring);
+
+    if (rxcd.gen != ring_gen) {
+        *descr_gen = ring_gen;
+        vmxnet3_inc_rx_completion_counter(s, qidx);
+        return daddr;
+    }
+
+    return 0;
+}
+
+static inline void
+vmxnet3_revert_rxc_descr(VMXNET3State *s, int qidx)
+{
+    vmxnet3_dec_rx_completion_counter(s, qidx);
+}
+
+#define RXQ_IDX      (0)
+#define RX_HEAD_BODY_RING (0)
+#define RX_BODY_ONLY_RING (1)
+
+static bool
+vmxnet3_get_next_head_rx_descr(VMXNET3State *s,
+                               struct Vmxnet3_RxDesc *descr_buf,
+                               uint32_t *descr_idx,
+                               uint32_t *ridx)
+{
+    for (;;) {
+        uint32_t ring_gen;
+        vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING,
+                                   descr_buf, descr_idx);
+
+        /* If no more free descriptors - return */
+        ring_gen = vmxnet3_get_rx_ring_gen(s, RXQ_IDX, RX_HEAD_BODY_RING);
+        if (descr_buf->gen != ring_gen) {
+            return false;
+        }
+
+        /* Only read after generation field verification */
+        smp_rmb();
+        /* Re-read to be sure we got the latest version */
+        vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING,
+                                   descr_buf, descr_idx);
+
+        /* Mark current descriptor as used/skipped */
+        vmxnet3_inc_rx_consumption_counter(s, RXQ_IDX, RX_HEAD_BODY_RING);
+
+        /* If this is what we are looking for - return */
+        if (descr_buf->btype == VMXNET3_RXD_BTYPE_HEAD) {
+            *ridx = RX_HEAD_BODY_RING;
+            return true;
+        }
+    }
+}
+
+static bool
+vmxnet3_get_next_body_rx_descr(VMXNET3State *s,
+                               struct Vmxnet3_RxDesc *d,
+                               uint32_t *didx,
+                               uint32_t *ridx)
+{
+    vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING, d, didx);
+
+    /* Try to find corresponding descriptor in head/body ring */
+    if (d->gen == vmxnet3_get_rx_ring_gen(s, RXQ_IDX, RX_HEAD_BODY_RING)) {
+        /* Only read after generation field verification */
+        smp_rmb();
+        /* Re-read to be sure we got the latest version */
+        vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING, d, didx);
+        if (d->btype == VMXNET3_RXD_BTYPE_BODY) {
+            vmxnet3_inc_rx_consumption_counter(s, RXQ_IDX, RX_HEAD_BODY_RING);
+            *ridx = RX_HEAD_BODY_RING;
+            return true;
+        }
+    }
+
+    /*
+     * If there is no free descriptors on head/body ring or next free
+     * descriptor is a head descriptor switch to body only ring
+     */
+    vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_BODY_ONLY_RING, d, didx);
+
+    /* If no more free descriptors - return */
+    if (d->gen == vmxnet3_get_rx_ring_gen(s, RXQ_IDX, RX_BODY_ONLY_RING)) {
+        /* Only read after generation field verification */
+        smp_rmb();
+        /* Re-read to be sure we got the latest version */
+        vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_BODY_ONLY_RING, d, didx);
+        assert(d->btype == VMXNET3_RXD_BTYPE_BODY);
+        *ridx = RX_BODY_ONLY_RING;
+        vmxnet3_inc_rx_consumption_counter(s, RXQ_IDX, RX_BODY_ONLY_RING);
+        return true;
+    }
+
+    return false;
+}
+
+static inline bool
+vmxnet3_get_next_rx_descr(VMXNET3State *s, bool is_head,
+                          struct Vmxnet3_RxDesc *descr_buf,
+                          uint32_t *descr_idx,
+                          uint32_t *ridx)
+{
+    if (is_head || !s->rx_packets_compound) {
+        return vmxnet3_get_next_head_rx_descr(s, descr_buf, descr_idx, ridx);
+    } else {
+        return vmxnet3_get_next_body_rx_descr(s, descr_buf, descr_idx, ridx);
+    }
+}
+
+static void vmxnet3_rx_update_descr(struct VmxnetRxPkt *pkt,
+    struct Vmxnet3_RxCompDesc *rxcd)
+{
+    int csum_ok, is_gso;
+    bool isip4, isip6, istcp, isudp;
+    struct virtio_net_hdr *vhdr;
+    uint8_t offload_type;
+
+    if (vmxnet_rx_pkt_is_vlan_stripped(pkt)) {
+        rxcd->ts = 1;
+        rxcd->tci = vmxnet_rx_pkt_get_vlan_tag(pkt);
+    }
+
+    if (!vmxnet_rx_pkt_has_virt_hdr(pkt)) {
+        goto nocsum;
+    }
+
+    vhdr = vmxnet_rx_pkt_get_vhdr(pkt);
+    /*
+     * Checksum is valid when lower level tell so or when lower level
+     * requires checksum offload telling that packet produced/bridged
+     * locally and did travel over network after last checksum calculation
+     * or production
+     */
+    csum_ok = VMXNET_FLAG_IS_SET(vhdr->flags, VIRTIO_NET_HDR_F_DATA_VALID) ||
+              VMXNET_FLAG_IS_SET(vhdr->flags, VIRTIO_NET_HDR_F_NEEDS_CSUM);
+
+    offload_type = vhdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN;
+    is_gso = (offload_type != VIRTIO_NET_HDR_GSO_NONE) ? 1 : 0;
+
+    if (!csum_ok && !is_gso) {
+        goto nocsum;
+    }
+
+    vmxnet_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp);
+    if ((!istcp && !isudp) || (!isip4 && !isip6)) {
+        goto nocsum;
+    }
+
+    rxcd->cnc = 0;
+    rxcd->v4 = isip4 ? 1 : 0;
+    rxcd->v6 = isip6 ? 1 : 0;
+    rxcd->tcp = istcp ? 1 : 0;
+    rxcd->udp = isudp ? 1 : 0;
+    rxcd->fcs = rxcd->tuc = rxcd->ipc = 1;
+    return;
+
+nocsum:
+    rxcd->cnc = 1;
+    return;
+}
+
+static void
+vmxnet3_physical_memory_writev(const struct iovec *iov,
+                               size_t start_iov_off,
+                               hwaddr target_addr,
+                               size_t bytes_to_copy)
+{
+    size_t curr_off = 0;
+    size_t copied = 0;
+
+    while (bytes_to_copy) {
+        if (start_iov_off < (curr_off + iov->iov_len)) {
+            size_t chunk_len =
+                MIN((curr_off + iov->iov_len) - start_iov_off, bytes_to_copy);
+
+            cpu_physical_memory_write(target_addr + copied,
+                                      iov->iov_base + start_iov_off - curr_off,
+                                      chunk_len);
+
+            copied += chunk_len;
+            start_iov_off += chunk_len;
+            curr_off = start_iov_off;
+            bytes_to_copy -= chunk_len;
+        } else {
+            curr_off += iov->iov_len;
+        }
+        iov++;
+    }
+}
+
+static bool
+vmxnet3_indicate_packet(VMXNET3State *s)
+{
+    struct Vmxnet3_RxDesc rxd;
+    bool is_head = true;
+    uint32_t rxd_idx;
+    uint32_t rx_ridx = 0;
+
+    struct Vmxnet3_RxCompDesc rxcd;
+    uint32_t new_rxcd_gen = VMXNET3_INIT_GEN;
+    hwaddr new_rxcd_pa = 0;
+    hwaddr ready_rxcd_pa = 0;
+    struct iovec *data = vmxnet_rx_pkt_get_iovec(s->rx_pkt);
+    size_t bytes_copied = 0;
+    size_t bytes_left = vmxnet_rx_pkt_get_total_len(s->rx_pkt);
+    uint16_t num_frags = 0;
+    size_t chunk_size;
+
+    vmxnet_rx_pkt_dump(s->rx_pkt);
+
+    while (bytes_left > 0) {
+
+        /* cannot add more frags to packet */
+        if (num_frags == s->max_rx_frags) {
+            break;
+        }
+
+        new_rxcd_pa = vmxnet3_pop_rxc_descr(s, RXQ_IDX, &new_rxcd_gen);
+        if (!new_rxcd_pa) {
+            break;
+        }
+
+        if (!vmxnet3_get_next_rx_descr(s, is_head, &rxd, &rxd_idx, &rx_ridx)) {
+            break;
+        }
+
+        chunk_size = MIN(bytes_left, rxd.len);
+        vmxnet3_physical_memory_writev(data, bytes_copied,
+                                       le64_to_cpu(rxd.addr), chunk_size);
+        bytes_copied += chunk_size;
+        bytes_left -= chunk_size;
+
+        vmxnet3_dump_rx_descr(&rxd);
+
+        if (0 != ready_rxcd_pa) {
+            cpu_physical_memory_write(ready_rxcd_pa, &rxcd, sizeof(rxcd));
+        }
+
+        memset(&rxcd, 0, sizeof(struct Vmxnet3_RxCompDesc));
+        rxcd.rxdIdx = rxd_idx;
+        rxcd.len = chunk_size;
+        rxcd.sop = is_head;
+        rxcd.gen = new_rxcd_gen;
+        rxcd.rqID = RXQ_IDX + rx_ridx * s->rxq_num;
+
+        if (0 == bytes_left) {
+            vmxnet3_rx_update_descr(s->rx_pkt, &rxcd);
+        }
+
+        VMW_RIPRN("RX Completion descriptor: rxRing: %lu rxIdx %lu len %lu "
+                  "sop %d csum_correct %lu",
+                  (unsigned long) rx_ridx,
+                  (unsigned long) rxcd.rxdIdx,
+                  (unsigned long) rxcd.len,
+                  (int) rxcd.sop,
+                  (unsigned long) rxcd.tuc);
+
+        is_head = false;
+        ready_rxcd_pa = new_rxcd_pa;
+        new_rxcd_pa = 0;
+    }
+
+    if (0 != ready_rxcd_pa) {
+        rxcd.eop = 1;
+        rxcd.err = (0 != bytes_left);
+        cpu_physical_memory_write(ready_rxcd_pa, &rxcd, sizeof(rxcd));
+
+        /* Flush RX descriptor changes */
+        smp_wmb();
+    }
+
+    if (0 != new_rxcd_pa) {
+        vmxnet3_revert_rxc_descr(s, RXQ_IDX);
+    }
+
+    vmxnet3_trigger_interrupt(s, s->rxq_descr[RXQ_IDX].intr_idx);
+
+    if (bytes_left == 0) {
+        vmxnet3_on_rx_done_update_stats(s, RXQ_IDX, VMXNET3_PKT_STATUS_OK);
+        return true;
+    } else if (num_frags == s->max_rx_frags) {
+        vmxnet3_on_rx_done_update_stats(s, RXQ_IDX, VMXNET3_PKT_STATUS_ERROR);
+        return false;
+    } else {
+        vmxnet3_on_rx_done_update_stats(s, RXQ_IDX,
+                                        VMXNET3_PKT_STATUS_OUT_OF_BUF);
+        return false;
+    }
+}
+
+static void
+vmxnet3_io_bar0_write(void *opaque, hwaddr addr,
+                      uint64_t val, unsigned size)
+{
+    VMXNET3State *s = opaque;
+
+    if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_TXPROD,
+                        VMXNET3_DEVICE_MAX_TX_QUEUES, VMXNET3_REG_ALIGN)) {
+        int tx_queue_idx =
+            VMW_MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_TXPROD,
+                                     VMXNET3_REG_ALIGN);
+        assert(tx_queue_idx <= s->txq_num);
+        vmxnet3_process_tx_queue(s, tx_queue_idx);
+        return;
+    }
+
+    if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_IMR,
+                        VMXNET3_MAX_INTRS, VMXNET3_REG_ALIGN)) {
+        int l = VMW_MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_IMR,
+                                         VMXNET3_REG_ALIGN);
+
+        VMW_CBPRN("Interrupt mask for line %d written: 0x%" PRIx64, l, val);
+
+        vmxnet3_on_interrupt_mask_changed(s, l, val);
+        return;
+    }
+
+    if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_RXPROD,
+                        VMXNET3_DEVICE_MAX_RX_QUEUES, VMXNET3_REG_ALIGN) ||
+       VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_RXPROD2,
+                        VMXNET3_DEVICE_MAX_RX_QUEUES, VMXNET3_REG_ALIGN)) {
+        return;
+    }
+
+    VMW_WRPRN("BAR0 unknown write [%" PRIx64 "] = %" PRIx64 ", size %d",
+              (uint64_t) addr, val, size);
+}
+
+static uint64_t
+vmxnet3_io_bar0_read(void *opaque, hwaddr addr, unsigned size)
+{
+    if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_IMR,
+                        VMXNET3_MAX_INTRS, VMXNET3_REG_ALIGN)) {
+        assert(false);
+    }
+
+    VMW_CBPRN("BAR0 unknown read [%" PRIx64 "], size %d", addr, size);
+    return 0;
+}
+
+static void vmxnet3_reset_interrupt_states(VMXNET3State *s)
+{
+    int i;
+    for (i = 0; i < ARRAY_SIZE(s->interrupt_states); i++) {
+        s->interrupt_states[i].is_asserted = false;
+        s->interrupt_states[i].is_pending = false;
+        s->interrupt_states[i].is_masked = true;
+    }
+}
+
+static void vmxnet3_reset_mac(VMXNET3State *s)
+{
+    memcpy(&s->conf.macaddr.a, &s->perm_mac.a, sizeof(s->perm_mac.a));
+    VMW_CFPRN("MAC address set to: " VMXNET_MF, VMXNET_MA(s->conf.macaddr.a));
+}
+
+static void vmxnet3_deactivate_device(VMXNET3State *s)
+{
+    VMW_CBPRN("Deactivating vmxnet3...");
+    s->device_active = false;
+}
+
+static void vmxnet3_reset(VMXNET3State *s)
+{
+    VMW_CBPRN("Resetting vmxnet3...");
+
+    vmxnet3_deactivate_device(s);
+    vmxnet3_reset_interrupt_states(s);
+    vmxnet_tx_pkt_reset(s->tx_pkt);
+    s->drv_shmem = 0;
+    s->tx_sop = true;
+    s->skip_current_tx_pkt = false;
+}
+
+static void vmxnet3_update_rx_mode(VMXNET3State *s)
+{
+    s->rx_mode = VMXNET3_READ_DRV_SHARED32(s->drv_shmem,
+                                           devRead.rxFilterConf.rxMode);
+    VMW_CFPRN("RX mode: 0x%08X", s->rx_mode);
+}
+
+static void vmxnet3_update_vlan_filters(VMXNET3State *s)
+{
+    int i;
+
+    /* Copy configuration from shared memory */
+    VMXNET3_READ_DRV_SHARED(s->drv_shmem,
+                            devRead.rxFilterConf.vfTable,
+                            s->vlan_table,
+                            sizeof(s->vlan_table));
+
+    /* Invert byte order when needed */
+    for (i = 0; i < ARRAY_SIZE(s->vlan_table); i++) {
+        s->vlan_table[i] = le32_to_cpu(s->vlan_table[i]);
+    }
+
+    /* Dump configuration for debugging purposes */
+    VMW_CFPRN("Configured VLANs:");
+    for (i = 0; i < sizeof(s->vlan_table) * 8; i++) {
+        if (VMXNET3_VFTABLE_ENTRY_IS_SET(s->vlan_table, i)) {
+            VMW_CFPRN("\tVLAN %d is present", i);
+        }
+    }
+}
+
+static void vmxnet3_update_mcast_filters(VMXNET3State *s)
+{
+    uint16_t list_bytes =
+        VMXNET3_READ_DRV_SHARED16(s->drv_shmem,
+                                  devRead.rxFilterConf.mfTableLen);
+
+    s->mcast_list_len = list_bytes / sizeof(s->mcast_list[0]);
+
+    s->mcast_list = g_realloc(s->mcast_list, list_bytes);
+    if (NULL == s->mcast_list) {
+        if (0 == s->mcast_list_len) {
+            VMW_CFPRN("Current multicast list is empty");
+        } else {
+            VMW_ERPRN("Failed to allocate multicast list of %d elements",
+                      s->mcast_list_len);
+        }
+        s->mcast_list_len = 0;
+    } else {
+        int i;
+        hwaddr mcast_list_pa =
+            VMXNET3_READ_DRV_SHARED64(s->drv_shmem,
+                                      devRead.rxFilterConf.mfTablePA);
+
+        cpu_physical_memory_read(mcast_list_pa, s->mcast_list, list_bytes);
+        VMW_CFPRN("Current multicast list len is %d:", s->mcast_list_len);
+        for (i = 0; i < s->mcast_list_len; i++) {
+            VMW_CFPRN("\t" VMXNET_MF, VMXNET_MA(s->mcast_list[i].a));
+        }
+    }
+}
+
+static void vmxnet3_setup_rx_filtering(VMXNET3State *s)
+{
+    vmxnet3_update_rx_mode(s);
+    vmxnet3_update_vlan_filters(s);
+    vmxnet3_update_mcast_filters(s);
+}
+
+static uint32_t vmxnet3_get_interrupt_config(VMXNET3State *s)
+{
+    uint32_t interrupt_mode = VMXNET3_IT_AUTO | (VMXNET3_IMM_AUTO << 2);
+    VMW_CFPRN("Interrupt config is 0x%X", interrupt_mode);
+    return interrupt_mode;
+}
+
+static void vmxnet3_fill_stats(VMXNET3State *s)
+{
+    int i;
+    for (i = 0; i < s->txq_num; i++) {
+        cpu_physical_memory_write(s->txq_descr[i].tx_stats_pa,
+                                  &s->txq_descr[i].txq_stats,
+                                  sizeof(s->txq_descr[i].txq_stats));
+    }
+
+    for (i = 0; i < s->rxq_num; i++) {
+        cpu_physical_memory_write(s->rxq_descr[i].rx_stats_pa,
+                                  &s->rxq_descr[i].rxq_stats,
+                                  sizeof(s->rxq_descr[i].rxq_stats));
+    }
+}
+
+static void vmxnet3_adjust_by_guest_type(VMXNET3State *s)
+{
+    struct Vmxnet3_GOSInfo gos;
+
+    VMXNET3_READ_DRV_SHARED(s->drv_shmem, devRead.misc.driverInfo.gos,
+                            &gos, sizeof(gos));
+    s->rx_packets_compound =
+        (gos.gosType == VMXNET3_GOS_TYPE_WIN) ? false : true;
+
+    VMW_CFPRN("Guest type specifics: RXCOMPOUND: %d", s->rx_packets_compound);
+}
+
+static void
+vmxnet3_dump_conf_descr(const char *name,
+                        struct Vmxnet3_VariableLenConfDesc *pm_descr)
+{
+    VMW_CFPRN("%s descriptor dump: Version %u, Length %u",
+              name, pm_descr->confVer, pm_descr->confLen);
+
+};
+
+static void vmxnet3_update_pm_state(VMXNET3State *s)
+{
+    struct Vmxnet3_VariableLenConfDesc pm_descr;
+
+    pm_descr.confLen =
+        VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.pmConfDesc.confLen);
+    pm_descr.confVer =
+        VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.pmConfDesc.confVer);
+    pm_descr.confPA =
+        VMXNET3_READ_DRV_SHARED64(s->drv_shmem, devRead.pmConfDesc.confPA);
+
+    vmxnet3_dump_conf_descr("PM State", &pm_descr);
+}
+
+static void vmxnet3_update_features(VMXNET3State *s)
+{
+    uint32_t guest_features;
+    int rxcso_supported;
+
+    guest_features = VMXNET3_READ_DRV_SHARED32(s->drv_shmem,
+                                               devRead.misc.uptFeatures);
+
+    rxcso_supported = VMXNET_FLAG_IS_SET(guest_features, UPT1_F_RXCSUM);
+    s->rx_vlan_stripping = VMXNET_FLAG_IS_SET(guest_features, UPT1_F_RXVLAN);
+    s->lro_supported = VMXNET_FLAG_IS_SET(guest_features, UPT1_F_LRO);
+
+    VMW_CFPRN("Features configuration: LRO: %d, RXCSUM: %d, VLANSTRIP: %d",
+              s->lro_supported, rxcso_supported,
+              s->rx_vlan_stripping);
+    if (s->peer_has_vhdr) {
+        tap_set_offload(qemu_get_queue(s->nic)->peer,
+                        rxcso_supported,
+                        s->lro_supported,
+                        s->lro_supported,
+                        0,
+                        0);
+    }
+}
+
+static void vmxnet3_activate_device(VMXNET3State *s)
+{
+    int i;
+    static const uint32_t VMXNET3_DEF_TX_THRESHOLD = 1;
+    hwaddr qdescr_table_pa;
+    uint64_t pa;
+    uint32_t size;
+
+    /* Verify configuration consistency */
+    if (!vmxnet3_verify_driver_magic(s->drv_shmem)) {
+        VMW_ERPRN("Device configuration received from driver is invalid");
+        return;
+    }
+
+    vmxnet3_adjust_by_guest_type(s);
+    vmxnet3_update_features(s);
+    vmxnet3_update_pm_state(s);
+    vmxnet3_setup_rx_filtering(s);
+    /* Cache fields from shared memory */
+    s->mtu = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.misc.mtu);
+    VMW_CFPRN("MTU is %u", s->mtu);
+
+    s->max_rx_frags =
+        VMXNET3_READ_DRV_SHARED16(s->drv_shmem, devRead.misc.maxNumRxSG);
+
+    VMW_CFPRN("Max RX fragments is %u", s->max_rx_frags);
+
+    s->event_int_idx =
+        VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.intrConf.eventIntrIdx);
+    VMW_CFPRN("Events interrupt line is %u", s->event_int_idx);
+
+    s->auto_int_masking =
+        VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.intrConf.autoMask);
+    VMW_CFPRN("Automatic interrupt masking is %d", (int)s->auto_int_masking);
+
+    s->txq_num =
+        VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.misc.numTxQueues);
+    s->rxq_num =
+        VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.misc.numRxQueues);
+
+    VMW_CFPRN("Number of TX/RX queues %u/%u", s->txq_num, s->rxq_num);
+    assert(s->txq_num <= VMXNET3_DEVICE_MAX_TX_QUEUES);
+
+    qdescr_table_pa =
+        VMXNET3_READ_DRV_SHARED64(s->drv_shmem, devRead.misc.queueDescPA);
+    VMW_CFPRN("TX queues descriptors table is at 0x%" PRIx64, qdescr_table_pa);
+
+    /*
+     * Worst-case scenario is a packet that holds all TX rings space so
+     * we calculate total size of all TX rings for max TX fragments number
+     */
+    s->max_tx_frags = 0;
+
+    /* TX queues */
+    for (i = 0; i < s->txq_num; i++) {
+        hwaddr qdescr_pa =
+            qdescr_table_pa + i * sizeof(struct Vmxnet3_TxQueueDesc);
+
+        /* Read interrupt number for this TX queue */
+        s->txq_descr[i].intr_idx =
+            VMXNET3_READ_TX_QUEUE_DESCR8(qdescr_pa, conf.intrIdx);
+
+        VMW_CFPRN("TX Queue %d interrupt: %d", i, s->txq_descr[i].intr_idx);
+
+        /* Read rings memory locations for TX queues */
+        pa = VMXNET3_READ_TX_QUEUE_DESCR64(qdescr_pa, conf.txRingBasePA);
+        size = VMXNET3_READ_TX_QUEUE_DESCR32(qdescr_pa, conf.txRingSize);
+
+        vmxnet3_ring_init(&s->txq_descr[i].tx_ring, pa, size,
+                          sizeof(struct Vmxnet3_TxDesc), false);
+        VMXNET3_RING_DUMP(VMW_CFPRN, "TX", i, &s->txq_descr[i].tx_ring);
+
+        s->max_tx_frags += size;
+
+        /* TXC ring */
+        pa = VMXNET3_READ_TX_QUEUE_DESCR64(qdescr_pa, conf.compRingBasePA);
+        size = VMXNET3_READ_TX_QUEUE_DESCR32(qdescr_pa, conf.compRingSize);
+        vmxnet3_ring_init(&s->txq_descr[i].comp_ring, pa, size,
+                          sizeof(struct Vmxnet3_TxCompDesc), true);
+        VMXNET3_RING_DUMP(VMW_CFPRN, "TXC", i, &s->txq_descr[i].comp_ring);
+
+        s->txq_descr[i].tx_stats_pa =
+            qdescr_pa + offsetof(struct Vmxnet3_TxQueueDesc, stats);
+
+        memset(&s->txq_descr[i].txq_stats, 0,
+               sizeof(s->txq_descr[i].txq_stats));
+
+        /* Fill device-managed parameters for queues */
+        VMXNET3_WRITE_TX_QUEUE_DESCR32(qdescr_pa,
+                                       ctrl.txThreshold,
+                                       VMXNET3_DEF_TX_THRESHOLD);
+    }
+
+    /* Preallocate TX packet wrapper */
+    VMW_CFPRN("Max TX fragments is %u", s->max_tx_frags);
+    vmxnet_tx_pkt_init(&s->tx_pkt, s->max_tx_frags, s->peer_has_vhdr);
+    vmxnet_rx_pkt_init(&s->rx_pkt, s->peer_has_vhdr);
+
+    /* Read rings memory locations for RX queues */
+    for (i = 0; i < s->rxq_num; i++) {
+        int j;
+        hwaddr qd_pa =
+            qdescr_table_pa + s->txq_num * sizeof(struct Vmxnet3_TxQueueDesc) +
+            i * sizeof(struct Vmxnet3_RxQueueDesc);
+
+        /* Read interrupt number for this RX queue */
+        s->rxq_descr[i].intr_idx =
+            VMXNET3_READ_TX_QUEUE_DESCR8(qd_pa, conf.intrIdx);
+
+        VMW_CFPRN("RX Queue %d interrupt: %d", i, s->rxq_descr[i].intr_idx);
+
+        /* Read rings memory locations */
+        for (j = 0; j < VMXNET3_RX_RINGS_PER_QUEUE; j++) {
+            /* RX rings */
+            pa = VMXNET3_READ_RX_QUEUE_DESCR64(qd_pa, conf.rxRingBasePA[j]);
+            size = VMXNET3_READ_RX_QUEUE_DESCR32(qd_pa, conf.rxRingSize[j]);
+            vmxnet3_ring_init(&s->rxq_descr[i].rx_ring[j], pa, size,
+                              sizeof(struct Vmxnet3_RxDesc), false);
+            VMW_CFPRN("RX queue %d:%d: Base: %" PRIx64 ", Size: %d",
+                      i, j, pa, size);
+        }
+
+        /* RXC ring */
+        pa = VMXNET3_READ_RX_QUEUE_DESCR64(qd_pa, conf.compRingBasePA);
+        size = VMXNET3_READ_RX_QUEUE_DESCR32(qd_pa, conf.compRingSize);
+        vmxnet3_ring_init(&s->rxq_descr[i].comp_ring, pa, size,
+                          sizeof(struct Vmxnet3_RxCompDesc), true);
+        VMW_CFPRN("RXC queue %d: Base: %" PRIx64 ", Size: %d", i, pa, size);
+
+        s->rxq_descr[i].rx_stats_pa =
+            qd_pa + offsetof(struct Vmxnet3_RxQueueDesc, stats);
+        memset(&s->rxq_descr[i].rxq_stats, 0,
+               sizeof(s->rxq_descr[i].rxq_stats));
+    }
+
+    /* Make sure everything is in place before device activation */
+    smp_wmb();
+
+    vmxnet3_reset_mac(s);
+
+    s->device_active = true;
+}
+
+static void vmxnet3_handle_command(VMXNET3State *s, uint64_t cmd)
+{
+    s->last_command = cmd;
+
+    switch (cmd) {
+    case VMXNET3_CMD_GET_PERM_MAC_HI:
+        VMW_CBPRN("Set: Get upper part of permanent MAC");
+        break;
+
+    case VMXNET3_CMD_GET_PERM_MAC_LO:
+        VMW_CBPRN("Set: Get lower part of permanent MAC");
+        break;
+
+    case VMXNET3_CMD_GET_STATS:
+        VMW_CBPRN("Set: Get device statistics");
+        vmxnet3_fill_stats(s);
+        break;
+
+    case VMXNET3_CMD_ACTIVATE_DEV:
+        VMW_CBPRN("Set: Activating vmxnet3 device");
+        vmxnet3_activate_device(s);
+        break;
+
+    case VMXNET3_CMD_UPDATE_RX_MODE:
+        VMW_CBPRN("Set: Update rx mode");
+        vmxnet3_update_rx_mode(s);
+        break;
+
+    case VMXNET3_CMD_UPDATE_VLAN_FILTERS:
+        VMW_CBPRN("Set: Update VLAN filters");
+        vmxnet3_update_vlan_filters(s);
+        break;
+
+    case VMXNET3_CMD_UPDATE_MAC_FILTERS:
+        VMW_CBPRN("Set: Update MAC filters");
+        vmxnet3_update_mcast_filters(s);
+        break;
+
+    case VMXNET3_CMD_UPDATE_FEATURE:
+        VMW_CBPRN("Set: Update features");
+        vmxnet3_update_features(s);
+        break;
+
+    case VMXNET3_CMD_UPDATE_PMCFG:
+        VMW_CBPRN("Set: Update power management config");
+        vmxnet3_update_pm_state(s);
+        break;
+
+    case VMXNET3_CMD_GET_LINK:
+        VMW_CBPRN("Set: Get link");
+        break;
+
+    case VMXNET3_CMD_RESET_DEV:
+        VMW_CBPRN("Set: Reset device");
+        vmxnet3_reset(s);
+        break;
+
+    case VMXNET3_CMD_QUIESCE_DEV:
+        VMW_CBPRN("Set: VMXNET3_CMD_QUIESCE_DEV - pause the device");
+        vmxnet3_deactivate_device(s);
+        break;
+
+    case VMXNET3_CMD_GET_CONF_INTR:
+        VMW_CBPRN("Set: VMXNET3_CMD_GET_CONF_INTR - interrupt configuration");
+        break;
+
+    default:
+        VMW_CBPRN("Received unknown command: %" PRIx64, cmd);
+        break;
+    }
+}
+
+static uint64_t vmxnet3_get_command_status(VMXNET3State *s)
+{
+    uint64_t ret;
+
+    switch (s->last_command) {
+    case VMXNET3_CMD_ACTIVATE_DEV:
+        ret = (s->device_active) ? 0 : -1;
+        VMW_CFPRN("Device active: %" PRIx64, ret);
+        break;
+
+    case VMXNET3_CMD_GET_LINK:
+        ret = s->link_status_and_speed;
+        VMW_CFPRN("Link and speed: %" PRIx64, ret);
+        break;
+
+    case VMXNET3_CMD_GET_PERM_MAC_LO:
+        ret = vmxnet3_get_mac_low(&s->perm_mac);
+        break;
+
+    case VMXNET3_CMD_GET_PERM_MAC_HI:
+        ret = vmxnet3_get_mac_high(&s->perm_mac);
+        break;
+
+    case VMXNET3_CMD_GET_CONF_INTR:
+        ret = vmxnet3_get_interrupt_config(s);
+        break;
+
+    default:
+        VMW_WRPRN("Received request for unknown command: %x", s->last_command);
+        ret = -1;
+        break;
+    }
+
+    return ret;
+}
+
+static void vmxnet3_set_events(VMXNET3State *s, uint32_t val)
+{
+    uint32_t events;
+
+    VMW_CBPRN("Setting events: 0x%x", val);
+    events = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, ecr) | val;
+    VMXNET3_WRITE_DRV_SHARED32(s->drv_shmem, ecr, events);
+}
+
+static void vmxnet3_ack_events(VMXNET3State *s, uint32_t val)
+{
+    uint32_t events;
+
+    VMW_CBPRN("Clearing events: 0x%x", val);
+    events = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, ecr) & ~val;
+    VMXNET3_WRITE_DRV_SHARED32(s->drv_shmem, ecr, events);
+}
+
+static void
+vmxnet3_io_bar1_write(void *opaque,
+                      hwaddr addr,
+                      uint64_t val,
+                      unsigned size)
+{
+    VMXNET3State *s = opaque;
+
+    switch (addr) {
+    /* Vmxnet3 Revision Report Selection */
+    case VMXNET3_REG_VRRS:
+        VMW_CBPRN("Write BAR1 [VMXNET3_REG_VRRS] = %" PRIx64 ", size %d",
+                  val, size);
+        break;
+
+    /* UPT Version Report Selection */
+    case VMXNET3_REG_UVRS:
+        VMW_CBPRN("Write BAR1 [VMXNET3_REG_UVRS] = %" PRIx64 ", size %d",
+                  val, size);
+        break;
+
+    /* Driver Shared Address Low */
+    case VMXNET3_REG_DSAL:
+        VMW_CBPRN("Write BAR1 [VMXNET3_REG_DSAL] = %" PRIx64 ", size %d",
+                  val, size);
+        /*
+         * Guest driver will first write the low part of the shared
+         * memory address. We save it to temp variable and set the
+         * shared address only after we get the high part
+         */
+        if (0 == val) {
+            s->device_active = false;
+        }
+        s->temp_shared_guest_driver_memory = val;
+        s->drv_shmem = 0;
+        break;
+
+    /* Driver Shared Address High */
+    case VMXNET3_REG_DSAH:
+        VMW_CBPRN("Write BAR1 [VMXNET3_REG_DSAH] = %" PRIx64 ", size %d",
+                  val, size);
+        /*
+         * Set the shared memory between guest driver and device.
+         * We already should have low address part.
+         */
+        s->drv_shmem = s->temp_shared_guest_driver_memory | (val << 32);
+        break;
+
+    /* Command */
+    case VMXNET3_REG_CMD:
+        VMW_CBPRN("Write BAR1 [VMXNET3_REG_CMD] = %" PRIx64 ", size %d",
+                  val, size);
+        vmxnet3_handle_command(s, val);
+        break;
+
+    /* MAC Address Low */
+    case VMXNET3_REG_MACL:
+        VMW_CBPRN("Write BAR1 [VMXNET3_REG_MACL] = %" PRIx64 ", size %d",
+                  val, size);
+        s->temp_mac = val;
+        break;
+
+    /* MAC Address High */
+    case VMXNET3_REG_MACH:
+        VMW_CBPRN("Write BAR1 [VMXNET3_REG_MACH] = %" PRIx64 ", size %d",
+                  val, size);
+        vmxnet3_set_variable_mac(s, val, s->temp_mac);
+        break;
+
+    /* Interrupt Cause Register */
+    case VMXNET3_REG_ICR:
+        VMW_CBPRN("Write BAR1 [VMXNET3_REG_ICR] = %" PRIx64 ", size %d",
+                  val, size);
+        assert(false);
+        break;
+
+    /* Event Cause Register */
+    case VMXNET3_REG_ECR:
+        VMW_CBPRN("Write BAR1 [VMXNET3_REG_ECR] = %" PRIx64 ", size %d",
+                  val, size);
+        vmxnet3_ack_events(s, val);
+        break;
+
+    default:
+        VMW_CBPRN("Unknown Write to BAR1 [%" PRIx64 "] = %" PRIx64 ", size %d",
+                  addr, val, size);
+        break;
+    }
+}
+
+static uint64_t
+vmxnet3_io_bar1_read(void *opaque, hwaddr addr, unsigned size)
+{
+        VMXNET3State *s = opaque;
+        uint64_t ret = 0;
+
+        switch (addr) {
+        /* Vmxnet3 Revision Report Selection */
+        case VMXNET3_REG_VRRS:
+            VMW_CBPRN("Read BAR1 [VMXNET3_REG_VRRS], size %d", size);
+            ret = VMXNET3_DEVICE_REVISION;
+            break;
+
+        /* UPT Version Report Selection */
+        case VMXNET3_REG_UVRS:
+            VMW_CBPRN("Read BAR1 [VMXNET3_REG_UVRS], size %d", size);
+            ret = VMXNET3_DEVICE_VERSION;
+            break;
+
+        /* Command */
+        case VMXNET3_REG_CMD:
+            VMW_CBPRN("Read BAR1 [VMXNET3_REG_CMD], size %d", size);
+            ret = vmxnet3_get_command_status(s);
+            break;
+
+        /* MAC Address Low */
+        case VMXNET3_REG_MACL:
+            VMW_CBPRN("Read BAR1 [VMXNET3_REG_MACL], size %d", size);
+            ret = vmxnet3_get_mac_low(&s->conf.macaddr);
+            break;
+
+        /* MAC Address High */
+        case VMXNET3_REG_MACH:
+            VMW_CBPRN("Read BAR1 [VMXNET3_REG_MACH], size %d", size);
+            ret = vmxnet3_get_mac_high(&s->conf.macaddr);
+            break;
+
+        /*
+         * Interrupt Cause Register
+         * Used for legacy interrupts only so interrupt index always 0
+         */
+        case VMXNET3_REG_ICR:
+            VMW_CBPRN("Read BAR1 [VMXNET3_REG_ICR], size %d", size);
+            if (vmxnet3_interrupt_asserted(s, 0)) {
+                vmxnet3_clear_interrupt(s, 0);
+                ret = true;
+            } else {
+                ret = false;
+            }
+            break;
+
+        default:
+            VMW_CBPRN("Unknow read BAR1[%" PRIx64 "], %d bytes", addr, size);
+            break;
+        }
+
+        return ret;
+}
+
+static int
+vmxnet3_can_receive(NetClientState *nc)
+{
+    VMXNET3State *s = qemu_get_nic_opaque(nc);
+    return s->device_active &&
+           VMXNET_FLAG_IS_SET(s->link_status_and_speed, VMXNET3_LINK_STATUS_UP);
+}
+
+static inline bool
+vmxnet3_is_registered_vlan(VMXNET3State *s, const void *data)
+{
+    uint16_t vlan_tag = eth_get_pkt_tci(data) & VLAN_VID_MASK;
+    if (IS_SPECIAL_VLAN_ID(vlan_tag)) {
+        return true;
+    }
+
+    return VMXNET3_VFTABLE_ENTRY_IS_SET(s->vlan_table, vlan_tag);
+}
+
+static bool
+vmxnet3_is_allowed_mcast_group(VMXNET3State *s, const uint8_t *group_mac)
+{
+    int i;
+    for (i = 0; i < s->mcast_list_len; i++) {
+        if (!memcmp(group_mac, s->mcast_list[i].a, sizeof(s->mcast_list[i]))) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static bool
+vmxnet3_rx_filter_may_indicate(VMXNET3State *s, const void *data,
+    size_t size)
+{
+    struct eth_header *ehdr = PKT_GET_ETH_HDR(data);
+
+    if (VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_PROMISC)) {
+        return true;
+    }
+
+    if (!vmxnet3_is_registered_vlan(s, data)) {
+        return false;
+    }
+
+    switch (vmxnet_rx_pkt_get_packet_type(s->rx_pkt)) {
+    case ETH_PKT_UCAST:
+        if (!VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_UCAST)) {
+            return false;
+        }
+        if (memcmp(s->conf.macaddr.a, ehdr->h_dest, ETH_ALEN)) {
+            return false;
+        }
+        break;
+
+    case ETH_PKT_BCAST:
+        if (!VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_BCAST)) {
+            return false;
+        }
+        break;
+
+    case ETH_PKT_MCAST:
+        if (VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_ALL_MULTI)) {
+            return true;
+        }
+        if (!VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_MCAST)) {
+            return false;
+        }
+        if (!vmxnet3_is_allowed_mcast_group(s, ehdr->h_dest)) {
+            return false;
+        }
+        break;
+
+    default:
+        assert(false);
+    }
+
+    return true;
+}
+
+static ssize_t
+vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+    VMXNET3State *s = qemu_get_nic_opaque(nc);
+    size_t bytes_indicated;
+
+    if (!vmxnet3_can_receive(nc)) {
+        VMW_PKPRN("Cannot receive now");
+        return -1;
+    }
+
+    if (s->peer_has_vhdr) {
+        vmxnet_rx_pkt_set_vhdr(s->rx_pkt, (struct virtio_net_hdr *)buf);
+        buf += sizeof(struct virtio_net_hdr);
+        size -= sizeof(struct virtio_net_hdr);
+    }
+
+    vmxnet_rx_pkt_set_packet_type(s->rx_pkt,
+        get_eth_packet_type(PKT_GET_ETH_HDR(buf)));
+
+    if (vmxnet3_rx_filter_may_indicate(s, buf, size)) {
+        vmxnet_rx_pkt_attach_data(s->rx_pkt, buf, size, s->rx_vlan_stripping);
+        bytes_indicated = vmxnet3_indicate_packet(s) ? size : -1;
+        if (bytes_indicated < size) {
+            VMW_PKPRN("RX: %lu of %lu bytes indicated", bytes_indicated, size);
+        }
+    } else {
+        VMW_PKPRN("Packet dropped by RX filter");
+        bytes_indicated = size;
+    }
+
+    assert(size > 0);
+    assert(bytes_indicated != 0);
+    return bytes_indicated;
+}
+
+static void vmxnet3_cleanup(NetClientState *nc)
+{
+    VMXNET3State *s = qemu_get_nic_opaque(nc);
+    s->nic = NULL;
+}
+
+static void vmxnet3_set_link_status(NetClientState *nc)
+{
+    VMXNET3State *s = qemu_get_nic_opaque(nc);
+
+    if (nc->link_down) {
+        s->link_status_and_speed &= ~VMXNET3_LINK_STATUS_UP;
+    } else {
+        s->link_status_and_speed |= VMXNET3_LINK_STATUS_UP;
+    }
+
+    vmxnet3_set_events(s, VMXNET3_ECR_LINK);
+    vmxnet3_trigger_interrupt(s, s->event_int_idx);
+}
+
+static NetClientInfo net_vmxnet3_info = {
+        .type = NET_CLIENT_OPTIONS_KIND_NIC,
+        .size = sizeof(NICState),
+        .can_receive = vmxnet3_can_receive,
+        .receive = vmxnet3_receive,
+        .cleanup = vmxnet3_cleanup,
+        .link_status_changed = vmxnet3_set_link_status,
+};
+
+static bool vmxnet3_peer_has_vnet_hdr(VMXNET3State *s)
+{
+    NetClientState *peer = qemu_get_queue(s->nic)->peer;
+
+    if ((NULL != peer)                              &&
+        (peer->info->type == NET_CLIENT_OPTIONS_KIND_TAP)   &&
+        tap_has_vnet_hdr(peer)) {
+        return true;
+    }
+
+    VMW_WRPRN("Peer has no virtio extension. Task offloads will be emulated.");
+    return false;
+}
+
+static void vmxnet3_net_uninit(VMXNET3State *s)
+{
+    g_free(s->mcast_list);
+    vmxnet_tx_pkt_reset(s->tx_pkt);
+    vmxnet_tx_pkt_uninit(s->tx_pkt);
+    vmxnet_rx_pkt_uninit(s->rx_pkt);
+    qemu_del_net_client(qemu_get_queue(s->nic));
+}
+
+static void vmxnet3_net_init(VMXNET3State *s)
+{
+    DeviceState *d = DEVICE(s);
+
+    VMW_CBPRN("vmxnet3_net_init called...");
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+
+    /* Windows guest will query the address that was set on init */
+    memcpy(&s->perm_mac.a, &s->conf.macaddr.a, sizeof(s->perm_mac.a));
+
+    s->mcast_list = NULL;
+    s->mcast_list_len = 0;
+
+    s->link_status_and_speed = VMXNET3_LINK_SPEED | VMXNET3_LINK_STATUS_UP;
+
+    VMW_CFPRN("Permanent MAC: " MAC_FMT, MAC_ARG(s->perm_mac.a));
+
+    s->nic = qemu_new_nic(&net_vmxnet3_info, &s->conf,
+                          object_get_typename(OBJECT(s)),
+                          d->id, s);
+
+    s->peer_has_vhdr = vmxnet3_peer_has_vnet_hdr(s);
+    s->tx_sop = true;
+    s->skip_current_tx_pkt = false;
+    s->tx_pkt = NULL;
+    s->rx_pkt = NULL;
+    s->rx_vlan_stripping = false;
+    s->lro_supported = false;
+
+    if (s->peer_has_vhdr) {
+        tap_set_vnet_hdr_len(qemu_get_queue(s->nic)->peer,
+            sizeof(struct virtio_net_hdr));
+
+        tap_using_vnet_hdr(qemu_get_queue(s->nic)->peer, 1);
+    }
+
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+}
+
+static void
+vmxnet3_unuse_msix_vectors(VMXNET3State *s, int num_vectors)
+{
+    PCIDevice *d = PCI_DEVICE(s);
+    int i;
+    for (i = 0; i < num_vectors; i++) {
+        msix_vector_unuse(d, i);
+    }
+}
+
+static bool
+vmxnet3_use_msix_vectors(VMXNET3State *s, int num_vectors)
+{
+    PCIDevice *d = PCI_DEVICE(s);
+    int i;
+    for (i = 0; i < num_vectors; i++) {
+        int res = msix_vector_use(d, i);
+        if (0 > res) {
+            VMW_WRPRN("Failed to use MSI-X vector %d, error %d", i, res);
+            vmxnet3_unuse_msix_vectors(s, i);
+            return false;
+        }
+    }
+    return true;
+}
+
+static bool
+vmxnet3_init_msix(VMXNET3State *s)
+{
+    PCIDevice *d = PCI_DEVICE(s);
+    int res = msix_init(d, VMXNET3_MAX_INTRS,
+                        &s->msix_bar,
+                        VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_TABLE,
+                        &s->msix_bar,
+                        VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_PBA,
+                        0);
+
+    if (0 > res) {
+        VMW_WRPRN("Failed to initialize MSI-X, error %d", res);
+        s->msix_used = false;
+    } else {
+        if (!vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS)) {
+            VMW_WRPRN("Failed to use MSI-X vectors, error %d", res);
+            msix_uninit(d, &s->msix_bar, &s->msix_bar);
+            s->msix_used = false;
+        } else {
+            s->msix_used = true;
+        }
+    }
+    return s->msix_used;
+}
+
+static void
+vmxnet3_cleanup_msix(VMXNET3State *s)
+{
+    PCIDevice *d = PCI_DEVICE(s);
+
+    if (s->msix_used) {
+        msix_vector_unuse(d, VMXNET3_MAX_INTRS);
+        msix_uninit(d, &s->msix_bar, &s->msix_bar);
+    }
+}
+
+#define VMXNET3_MSI_NUM_VECTORS   (1)
+#define VMXNET3_MSI_OFFSET        (0x50)
+#define VMXNET3_USE_64BIT         (true)
+#define VMXNET3_PER_VECTOR_MASK   (false)
+
+static bool
+vmxnet3_init_msi(VMXNET3State *s)
+{
+    PCIDevice *d = PCI_DEVICE(s);
+    int res;
+
+    res = msi_init(d, VMXNET3_MSI_OFFSET, VMXNET3_MSI_NUM_VECTORS,
+                   VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK);
+    if (0 > res) {
+        VMW_WRPRN("Failed to initialize MSI, error %d", res);
+        s->msi_used = false;
+    } else {
+        s->msi_used = true;
+    }
+
+    return s->msi_used;
+}
+
+static void
+vmxnet3_cleanup_msi(VMXNET3State *s)
+{
+    PCIDevice *d = PCI_DEVICE(s);
+
+    if (s->msi_used) {
+        msi_uninit(d);
+    }
+}
+
+static void
+vmxnet3_msix_save(QEMUFile *f, void *opaque)
+{
+    PCIDevice *d = PCI_DEVICE(opaque);
+    msix_save(d, f);
+}
+
+static int
+vmxnet3_msix_load(QEMUFile *f, void *opaque, int version_id)
+{
+    PCIDevice *d = PCI_DEVICE(opaque);
+    msix_load(d, f);
+    return 0;
+}
+
+static const MemoryRegionOps b0_ops = {
+    .read = vmxnet3_io_bar0_read,
+    .write = vmxnet3_io_bar0_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+            .min_access_size = 4,
+            .max_access_size = 4,
+    },
+};
+
+static const MemoryRegionOps b1_ops = {
+    .read = vmxnet3_io_bar1_read,
+    .write = vmxnet3_io_bar1_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+            .min_access_size = 4,
+            .max_access_size = 4,
+    },
+};
+
+static int vmxnet3_pci_init(PCIDevice *pci_dev)
+{
+    DeviceState *dev = DEVICE(pci_dev);
+    VMXNET3State *s = VMXNET3(pci_dev);
+
+    VMW_CBPRN("Starting init...");
+
+    memory_region_init_io(&s->bar0, &b0_ops, s,
+                          "vmxnet3-b0", VMXNET3_PT_REG_SIZE);
+    pci_register_bar(pci_dev, VMXNET3_BAR0_IDX,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0);
+
+    memory_region_init_io(&s->bar1, &b1_ops, s,
+                          "vmxnet3-b1", VMXNET3_VD_REG_SIZE);
+    pci_register_bar(pci_dev, VMXNET3_BAR1_IDX,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar1);
+
+    memory_region_init(&s->msix_bar, "vmxnet3-msix-bar",
+                       VMXNET3_MSIX_BAR_SIZE);
+    pci_register_bar(pci_dev, VMXNET3_MSIX_BAR_IDX,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, &s->msix_bar);
+
+    vmxnet3_reset_interrupt_states(s);
+
+    /* Interrupt pin A */
+    pci_dev->config[PCI_INTERRUPT_PIN] = 0x01;
+
+    if (!vmxnet3_init_msix(s)) {
+        VMW_WRPRN("Failed to initialize MSI-X, configuration is inconsistent.");
+    }
+
+    if (!vmxnet3_init_msi(s)) {
+        VMW_WRPRN("Failed to initialize MSI, configuration is inconsistent.");
+    }
+
+    vmxnet3_net_init(s);
+
+    register_savevm(dev, "vmxnet3-msix", -1, 1,
+                    vmxnet3_msix_save, vmxnet3_msix_load, s);
+
+    add_boot_device_path(s->conf.bootindex, dev, "/ethernet-phy@0");
+
+    return 0;
+}
+
+
+static void vmxnet3_pci_uninit(PCIDevice *pci_dev)
+{
+    DeviceState *dev = DEVICE(pci_dev);
+    VMXNET3State *s = VMXNET3(pci_dev);
+
+    VMW_CBPRN("Starting uninit...");
+
+    unregister_savevm(dev, "vmxnet3-msix", s);
+
+    vmxnet3_net_uninit(s);
+
+    vmxnet3_cleanup_msix(s);
+
+    vmxnet3_cleanup_msi(s);
+
+    memory_region_destroy(&s->bar0);
+    memory_region_destroy(&s->bar1);
+    memory_region_destroy(&s->msix_bar);
+}
+
+static void vmxnet3_qdev_reset(DeviceState *dev)
+{
+    PCIDevice *d = PCI_DEVICE(dev);
+    VMXNET3State *s = VMXNET3(d);
+
+    VMW_CBPRN("Starting QDEV reset...");
+    vmxnet3_reset(s);
+}
+
+static bool vmxnet3_mc_list_needed(void *opaque)
+{
+    return true;
+}
+
+static int vmxnet3_mcast_list_pre_load(void *opaque)
+{
+    VMXNET3State *s = opaque;
+
+    s->mcast_list = g_malloc(s->mcast_list_buff_size);
+
+    return 0;
+}
+
+
+static void vmxnet3_pre_save(void *opaque)
+{
+    VMXNET3State *s = opaque;
+
+    s->mcast_list_buff_size = s->mcast_list_len * sizeof(MACAddr);
+}
+
+static const VMStateDescription vmxstate_vmxnet3_mcast_list = {
+    .name = "vmxnet3/mcast_list",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_load = vmxnet3_mcast_list_pre_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_VBUFFER_UINT32(mcast_list, VMXNET3State, 0, NULL, 0,
+            mcast_list_buff_size),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void vmxnet3_get_ring_from_file(QEMUFile *f, Vmxnet3Ring *r)
+{
+    r->pa = qemu_get_be64(f);
+    r->size = qemu_get_be32(f);
+    r->cell_size = qemu_get_be32(f);
+    r->next = qemu_get_be32(f);
+    r->gen = qemu_get_byte(f);
+}
+
+static void vmxnet3_put_ring_to_file(QEMUFile *f, Vmxnet3Ring *r)
+{
+    qemu_put_be64(f, r->pa);
+    qemu_put_be32(f, r->size);
+    qemu_put_be32(f, r->cell_size);
+    qemu_put_be32(f, r->next);
+    qemu_put_byte(f, r->gen);
+}
+
+static void vmxnet3_get_tx_stats_from_file(QEMUFile *f,
+    struct UPT1_TxStats *tx_stat)
+{
+    tx_stat->TSOPktsTxOK = qemu_get_be64(f);
+    tx_stat->TSOBytesTxOK = qemu_get_be64(f);
+    tx_stat->ucastPktsTxOK = qemu_get_be64(f);
+    tx_stat->ucastBytesTxOK = qemu_get_be64(f);
+    tx_stat->mcastPktsTxOK = qemu_get_be64(f);
+    tx_stat->mcastBytesTxOK = qemu_get_be64(f);
+    tx_stat->bcastPktsTxOK = qemu_get_be64(f);
+    tx_stat->bcastBytesTxOK = qemu_get_be64(f);
+    tx_stat->pktsTxError = qemu_get_be64(f);
+    tx_stat->pktsTxDiscard = qemu_get_be64(f);
+}
+
+static void vmxnet3_put_tx_stats_to_file(QEMUFile *f,
+    struct UPT1_TxStats *tx_stat)
+{
+    qemu_put_be64(f, tx_stat->TSOPktsTxOK);
+    qemu_put_be64(f, tx_stat->TSOBytesTxOK);
+    qemu_put_be64(f, tx_stat->ucastPktsTxOK);
+    qemu_put_be64(f, tx_stat->ucastBytesTxOK);
+    qemu_put_be64(f, tx_stat->mcastPktsTxOK);
+    qemu_put_be64(f, tx_stat->mcastBytesTxOK);
+    qemu_put_be64(f, tx_stat->bcastPktsTxOK);
+    qemu_put_be64(f, tx_stat->bcastBytesTxOK);
+    qemu_put_be64(f, tx_stat->pktsTxError);
+    qemu_put_be64(f, tx_stat->pktsTxDiscard);
+}
+
+static int vmxnet3_get_txq_descr(QEMUFile *f, void *pv, size_t size)
+{
+    Vmxnet3TxqDescr *r = pv;
+
+    vmxnet3_get_ring_from_file(f, &r->tx_ring);
+    vmxnet3_get_ring_from_file(f, &r->comp_ring);
+    r->intr_idx = qemu_get_byte(f);
+    r->tx_stats_pa = qemu_get_be64(f);
+
+    vmxnet3_get_tx_stats_from_file(f, &r->txq_stats);
+
+    return 0;
+}
+
+static void vmxnet3_put_txq_descr(QEMUFile *f, void *pv, size_t size)
+{
+    Vmxnet3TxqDescr *r = pv;
+
+    vmxnet3_put_ring_to_file(f, &r->tx_ring);
+    vmxnet3_put_ring_to_file(f, &r->comp_ring);
+    qemu_put_byte(f, r->intr_idx);
+    qemu_put_be64(f, r->tx_stats_pa);
+    vmxnet3_put_tx_stats_to_file(f, &r->txq_stats);
+}
+
+const VMStateInfo txq_descr_info = {
+    .name = "txq_descr",
+    .get = vmxnet3_get_txq_descr,
+    .put = vmxnet3_put_txq_descr
+};
+
+static void vmxnet3_get_rx_stats_from_file(QEMUFile *f,
+    struct UPT1_RxStats *rx_stat)
+{
+    rx_stat->LROPktsRxOK = qemu_get_be64(f);
+    rx_stat->LROBytesRxOK = qemu_get_be64(f);
+    rx_stat->ucastPktsRxOK = qemu_get_be64(f);
+    rx_stat->ucastBytesRxOK = qemu_get_be64(f);
+    rx_stat->mcastPktsRxOK = qemu_get_be64(f);
+    rx_stat->mcastBytesRxOK = qemu_get_be64(f);
+    rx_stat->bcastPktsRxOK = qemu_get_be64(f);
+    rx_stat->bcastBytesRxOK = qemu_get_be64(f);
+    rx_stat->pktsRxOutOfBuf = qemu_get_be64(f);
+    rx_stat->pktsRxError = qemu_get_be64(f);
+}
+
+static void vmxnet3_put_rx_stats_to_file(QEMUFile *f,
+    struct UPT1_RxStats *rx_stat)
+{
+    qemu_put_be64(f, rx_stat->LROPktsRxOK);
+    qemu_put_be64(f, rx_stat->LROBytesRxOK);
+    qemu_put_be64(f, rx_stat->ucastPktsRxOK);
+    qemu_put_be64(f, rx_stat->ucastBytesRxOK);
+    qemu_put_be64(f, rx_stat->mcastPktsRxOK);
+    qemu_put_be64(f, rx_stat->mcastBytesRxOK);
+    qemu_put_be64(f, rx_stat->bcastPktsRxOK);
+    qemu_put_be64(f, rx_stat->bcastBytesRxOK);
+    qemu_put_be64(f, rx_stat->pktsRxOutOfBuf);
+    qemu_put_be64(f, rx_stat->pktsRxError);
+}
+
+static int vmxnet3_get_rxq_descr(QEMUFile *f, void *pv, size_t size)
+{
+    Vmxnet3RxqDescr *r = pv;
+    int i;
+
+    for (i = 0; i < VMXNET3_RX_RINGS_PER_QUEUE; i++) {
+        vmxnet3_get_ring_from_file(f, &r->rx_ring[i]);
+    }
+
+    vmxnet3_get_ring_from_file(f, &r->comp_ring);
+    r->intr_idx = qemu_get_byte(f);
+    r->rx_stats_pa = qemu_get_be64(f);
+
+    vmxnet3_get_rx_stats_from_file(f, &r->rxq_stats);
+
+    return 0;
+}
+
+static void vmxnet3_put_rxq_descr(QEMUFile *f, void *pv, size_t size)
+{
+    Vmxnet3RxqDescr *r = pv;
+    int i;
+
+    for (i = 0; i < VMXNET3_RX_RINGS_PER_QUEUE; i++) {
+        vmxnet3_put_ring_to_file(f, &r->rx_ring[i]);
+    }
+
+    vmxnet3_put_ring_to_file(f, &r->comp_ring);
+    qemu_put_byte(f, r->intr_idx);
+    qemu_put_be64(f, r->rx_stats_pa);
+    vmxnet3_put_rx_stats_to_file(f, &r->rxq_stats);
+}
+
+static int vmxnet3_post_load(void *opaque, int version_id)
+{
+    VMXNET3State *s = opaque;
+    PCIDevice *d = PCI_DEVICE(s);
+
+    vmxnet_tx_pkt_init(&s->tx_pkt, s->max_tx_frags, s->peer_has_vhdr);
+    vmxnet_rx_pkt_init(&s->rx_pkt, s->peer_has_vhdr);
+
+    if (s->msix_used) {
+        if  (!vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS)) {
+            VMW_WRPRN("Failed to re-use MSI-X vectors");
+            msix_uninit(d, &s->msix_bar, &s->msix_bar);
+            s->msix_used = false;
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+const VMStateInfo rxq_descr_info = {
+    .name = "rxq_descr",
+    .get = vmxnet3_get_rxq_descr,
+    .put = vmxnet3_put_rxq_descr
+};
+
+static int vmxnet3_get_int_state(QEMUFile *f, void *pv, size_t size)
+{
+    Vmxnet3IntState *r = pv;
+
+    r->is_masked = qemu_get_byte(f);
+    r->is_pending = qemu_get_byte(f);
+    r->is_asserted = qemu_get_byte(f);
+
+    return 0;
+}
+
+static void vmxnet3_put_int_state(QEMUFile *f, void *pv, size_t size)
+{
+    Vmxnet3IntState *r = pv;
+
+    qemu_put_byte(f, r->is_masked);
+    qemu_put_byte(f, r->is_pending);
+    qemu_put_byte(f, r->is_asserted);
+}
+
+const VMStateInfo int_state_info = {
+    .name = "int_state",
+    .get = vmxnet3_get_int_state,
+    .put = vmxnet3_put_int_state
+};
+
+static const VMStateDescription vmstate_vmxnet3 = {
+    .name = "vmxnet3",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_save = vmxnet3_pre_save,
+    .post_load = vmxnet3_post_load,
+    .fields      = (VMStateField[]) {
+            VMSTATE_PCI_DEVICE(parent_obj, VMXNET3State),
+            VMSTATE_BOOL(rx_packets_compound, VMXNET3State),
+            VMSTATE_BOOL(rx_vlan_stripping, VMXNET3State),
+            VMSTATE_BOOL(lro_supported, VMXNET3State),
+            VMSTATE_UINT32(rx_mode, VMXNET3State),
+            VMSTATE_UINT32(mcast_list_len, VMXNET3State),
+            VMSTATE_UINT32(mcast_list_buff_size, VMXNET3State),
+            VMSTATE_UINT32_ARRAY(vlan_table, VMXNET3State, VMXNET3_VFT_SIZE),
+            VMSTATE_UINT32(mtu, VMXNET3State),
+            VMSTATE_UINT16(max_rx_frags, VMXNET3State),
+            VMSTATE_UINT32(max_tx_frags, VMXNET3State),
+            VMSTATE_UINT8(event_int_idx, VMXNET3State),
+            VMSTATE_BOOL(auto_int_masking, VMXNET3State),
+            VMSTATE_UINT8(txq_num, VMXNET3State),
+            VMSTATE_UINT8(rxq_num, VMXNET3State),
+            VMSTATE_UINT32(device_active, VMXNET3State),
+            VMSTATE_UINT32(last_command, VMXNET3State),
+            VMSTATE_UINT32(link_status_and_speed, VMXNET3State),
+            VMSTATE_UINT32(temp_mac, VMXNET3State),
+            VMSTATE_UINT64(drv_shmem, VMXNET3State),
+            VMSTATE_UINT64(temp_shared_guest_driver_memory, VMXNET3State),
+
+            VMSTATE_ARRAY(txq_descr, VMXNET3State,
+                VMXNET3_DEVICE_MAX_TX_QUEUES, 0, txq_descr_info,
+                Vmxnet3TxqDescr),
+            VMSTATE_ARRAY(rxq_descr, VMXNET3State,
+                VMXNET3_DEVICE_MAX_RX_QUEUES, 0, rxq_descr_info,
+                Vmxnet3RxqDescr),
+            VMSTATE_ARRAY(interrupt_states, VMXNET3State, VMXNET3_MAX_INTRS,
+                0, int_state_info, Vmxnet3IntState),
+
+            VMSTATE_END_OF_LIST()
+    },
+    .subsections = (VMStateSubsection[]) {
+        {
+            .vmsd = &vmxstate_vmxnet3_mcast_list,
+            .needed = vmxnet3_mc_list_needed
+        },
+        {
+            /* empty element. */
+        }
+    }
+};
+
+static void
+vmxnet3_write_config(PCIDevice *pci_dev, uint32_t addr, uint32_t val, int len)
+{
+    pci_default_write_config(pci_dev, addr, val, len);
+    msix_write_config(pci_dev, addr, val, len);
+    msi_write_config(pci_dev, addr, val, len);
+}
+
+static Property vmxnet3_properties[] = {
+    DEFINE_NIC_PROPERTIES(VMXNET3State, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vmxnet3_class_init(ObjectClass *class, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(class);
+    PCIDeviceClass *c = PCI_DEVICE_CLASS(class);
+
+    c->init = vmxnet3_pci_init;
+    c->exit = vmxnet3_pci_uninit;
+    c->vendor_id = PCI_VENDOR_ID_VMWARE;
+    c->device_id = PCI_DEVICE_ID_VMWARE_VMXNET3;
+    c->revision = PCI_DEVICE_ID_VMWARE_VMXNET3_REVISION;
+    c->class_id = PCI_CLASS_NETWORK_ETHERNET;
+    c->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE;
+    c->subsystem_id = PCI_DEVICE_ID_VMWARE_VMXNET3;
+    c->config_write = vmxnet3_write_config,
+    dc->desc = "VMWare Paravirtualized Ethernet v3";
+    dc->reset = vmxnet3_qdev_reset;
+    dc->vmsd = &vmstate_vmxnet3;
+    dc->props = vmxnet3_properties;
+}
+
+static const TypeInfo vmxnet3_info = {
+    .name          = TYPE_VMXNET3,
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(VMXNET3State),
+    .class_init    = vmxnet3_class_init,
+};
+
+static void vmxnet3_register_types(void)
+{
+    VMW_CBPRN("vmxnet3_register_types called...");
+    type_register_static(&vmxnet3_info);
+}
+
+type_init(vmxnet3_register_types)
diff --git a/hw/net/vmxnet3.h b/hw/net/vmxnet3.h
new file mode 100644 (file)
index 0000000..7db0c8f
--- /dev/null
@@ -0,0 +1,760 @@
+/*
+ * QEMU VMWARE VMXNET3 paravirtual NIC interface definitions
+ *
+ * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
+ *
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
+ *
+ * Authors:
+ * Dmitry Fleytman <dmitry@daynix.com>
+ * Tamir Shomer <tamirs@daynix.com>
+ * Yan Vugenfirer <yan@daynix.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef _QEMU_VMXNET3_H
+#define _QEMU_VMXNET3_H
+
+#define VMXNET3_DEVICE_MAX_TX_QUEUES 8
+#define VMXNET3_DEVICE_MAX_RX_QUEUES 8   /* Keep this value as a power of 2 */
+
+/*
+ * VMWARE headers we got from Linux kernel do not fully comply QEMU coding
+ * standards in sense of types and defines used.
+ * Since we didn't want to change VMWARE code, following set of typedefs
+ * and defines needed to compile these headers with QEMU introduced.
+ */
+#define u64     uint64_t
+#define u32     uint32_t
+#define u16     uint16_t
+#define u8      uint8_t
+#define __le16  uint16_t
+#define __le32  uint32_t
+#define __le64  uint64_t
+#define __packed QEMU_PACKED
+
+#if defined(HOST_WORDS_BIGENDIAN)
+#define const_cpu_to_le64(x) bswap_64(x)
+#define __BIG_ENDIAN_BITFIELD
+#else
+#define const_cpu_to_le64(x) (x)
+#endif
+
+/*
+ * Following is an interface definition for
+ * VMXNET3 device as provided by VMWARE
+ * See original copyright from Linux kernel v3.2.8
+ * header file drivers/net/vmxnet3/vmxnet3_defs.h below.
+ */
+
+/*
+ * Linux driver for VMware's vmxnet3 ethernet NIC.
+ *
+ * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Maintained by: Shreyas Bhatewara <pv-drivers@vmware.com>
+ *
+ */
+
+struct UPT1_TxStats {
+    u64            TSOPktsTxOK;  /* TSO pkts post-segmentation */
+    u64            TSOBytesTxOK;
+    u64            ucastPktsTxOK;
+    u64            ucastBytesTxOK;
+    u64            mcastPktsTxOK;
+    u64            mcastBytesTxOK;
+    u64            bcastPktsTxOK;
+    u64            bcastBytesTxOK;
+    u64            pktsTxError;
+    u64            pktsTxDiscard;
+};
+
+struct UPT1_RxStats {
+    u64            LROPktsRxOK;    /* LRO pkts */
+    u64            LROBytesRxOK;   /* bytes from LRO pkts */
+    /* the following counters are for pkts from the wire, i.e., pre-LRO */
+    u64            ucastPktsRxOK;
+    u64            ucastBytesRxOK;
+    u64            mcastPktsRxOK;
+    u64            mcastBytesRxOK;
+    u64            bcastPktsRxOK;
+    u64            bcastBytesRxOK;
+    u64            pktsRxOutOfBuf;
+    u64            pktsRxError;
+};
+
+/* interrupt moderation level */
+enum {
+    UPT1_IML_NONE        = 0, /* no interrupt moderation */
+    UPT1_IML_HIGHEST    = 7, /* least intr generated */
+    UPT1_IML_ADAPTIVE    = 8, /* adpative intr moderation */
+};
+/* values for UPT1_RSSConf.hashFunc */
+enum {
+    UPT1_RSS_HASH_TYPE_NONE      = 0x0,
+    UPT1_RSS_HASH_TYPE_IPV4      = 0x01,
+    UPT1_RSS_HASH_TYPE_TCP_IPV4  = 0x02,
+    UPT1_RSS_HASH_TYPE_IPV6      = 0x04,
+    UPT1_RSS_HASH_TYPE_TCP_IPV6  = 0x08,
+};
+
+enum {
+    UPT1_RSS_HASH_FUNC_NONE      = 0x0,
+    UPT1_RSS_HASH_FUNC_TOEPLITZ  = 0x01,
+};
+
+#define UPT1_RSS_MAX_KEY_SIZE        40
+#define UPT1_RSS_MAX_IND_TABLE_SIZE  128
+
+struct UPT1_RSSConf {
+    u16            hashType;
+    u16            hashFunc;
+    u16            hashKeySize;
+    u16            indTableSize;
+    u8            hashKey[UPT1_RSS_MAX_KEY_SIZE];
+    u8            indTable[UPT1_RSS_MAX_IND_TABLE_SIZE];
+};
+
+/* features */
+enum {
+    UPT1_F_RXCSUM        = const_cpu_to_le64(0x0001), /* rx csum verification */
+    UPT1_F_RSS        = const_cpu_to_le64(0x0002),
+    UPT1_F_RXVLAN        = const_cpu_to_le64(0x0004), /* VLAN tag stripping */
+    UPT1_F_LRO        = const_cpu_to_le64(0x0008),
+};
+
+/* all registers are 32 bit wide */
+/* BAR 1 */
+enum {
+    VMXNET3_REG_VRRS    = 0x0,    /* Vmxnet3 Revision Report Selection */
+    VMXNET3_REG_UVRS    = 0x8,    /* UPT Version Report Selection */
+    VMXNET3_REG_DSAL    = 0x10,    /* Driver Shared Address Low */
+    VMXNET3_REG_DSAH    = 0x18,    /* Driver Shared Address High */
+    VMXNET3_REG_CMD        = 0x20,    /* Command */
+    VMXNET3_REG_MACL    = 0x28,    /* MAC Address Low */
+    VMXNET3_REG_MACH    = 0x30,    /* MAC Address High */
+    VMXNET3_REG_ICR        = 0x38,    /* Interrupt Cause Register */
+    VMXNET3_REG_ECR        = 0x40    /* Event Cause Register */
+};
+
+/* BAR 0 */
+enum {
+    VMXNET3_REG_IMR        = 0x0,     /* Interrupt Mask Register */
+    VMXNET3_REG_TXPROD    = 0x600, /* Tx Producer Index */
+    VMXNET3_REG_RXPROD    = 0x800, /* Rx Producer Index for ring 1 */
+    VMXNET3_REG_RXPROD2    = 0xA00     /* Rx Producer Index for ring 2 */
+};
+
+#define VMXNET3_PT_REG_SIZE     4096    /* BAR 0 */
+#define VMXNET3_VD_REG_SIZE     4096    /* BAR 1 */
+
+#define VMXNET3_REG_ALIGN       8    /* All registers are 8-byte aligned. */
+#define VMXNET3_REG_ALIGN_MASK  0x7
+
+/* I/O Mapped access to registers */
+#define VMXNET3_IO_TYPE_PT              0
+#define VMXNET3_IO_TYPE_VD              1
+#define VMXNET3_IO_ADDR(type, reg)      (((type) << 24) | ((reg) & 0xFFFFFF))
+#define VMXNET3_IO_TYPE(addr)           ((addr) >> 24)
+#define VMXNET3_IO_REG(addr)            ((addr) & 0xFFFFFF)
+
+enum {
+    VMXNET3_CMD_FIRST_SET = 0xCAFE0000,
+    VMXNET3_CMD_ACTIVATE_DEV = VMXNET3_CMD_FIRST_SET, /* 0xCAFE0000 */
+    VMXNET3_CMD_QUIESCE_DEV,                          /* 0xCAFE0001 */
+    VMXNET3_CMD_RESET_DEV,                            /* 0xCAFE0002 */
+    VMXNET3_CMD_UPDATE_RX_MODE,                       /* 0xCAFE0003 */
+    VMXNET3_CMD_UPDATE_MAC_FILTERS,                   /* 0xCAFE0004 */
+    VMXNET3_CMD_UPDATE_VLAN_FILTERS,                  /* 0xCAFE0005 */
+    VMXNET3_CMD_UPDATE_RSSIDT,                        /* 0xCAFE0006 */
+    VMXNET3_CMD_UPDATE_IML,                           /* 0xCAFE0007 */
+    VMXNET3_CMD_UPDATE_PMCFG,                         /* 0xCAFE0008 */
+    VMXNET3_CMD_UPDATE_FEATURE,                       /* 0xCAFE0009 */
+    VMXNET3_CMD_LOAD_PLUGIN,                          /* 0xCAFE000A */
+
+    VMXNET3_CMD_FIRST_GET = 0xF00D0000,
+    VMXNET3_CMD_GET_QUEUE_STATUS = VMXNET3_CMD_FIRST_GET, /* 0xF00D0000 */
+    VMXNET3_CMD_GET_STATS,                                /* 0xF00D0001 */
+    VMXNET3_CMD_GET_LINK,                                 /* 0xF00D0002 */
+    VMXNET3_CMD_GET_PERM_MAC_LO,                          /* 0xF00D0003 */
+    VMXNET3_CMD_GET_PERM_MAC_HI,                          /* 0xF00D0004 */
+    VMXNET3_CMD_GET_DID_LO,                               /* 0xF00D0005 */
+    VMXNET3_CMD_GET_DID_HI,                               /* 0xF00D0006 */
+    VMXNET3_CMD_GET_DEV_EXTRA_INFO,                       /* 0xF00D0007 */
+    VMXNET3_CMD_GET_CONF_INTR                             /* 0xF00D0008 */
+};
+
+/*
+ *    Little Endian layout of bitfields -
+ *    Byte 0 :    7.....len.....0
+ *    Byte 1 :    rsvd gen 13.len.8
+ *    Byte 2 :     5.msscof.0 ext1  dtype
+ *    Byte 3 :     13...msscof...6
+ *
+ *    Big Endian layout of bitfields -
+ *    Byte 0:        13...msscof...6
+ *    Byte 1 :     5.msscof.0 ext1  dtype
+ *    Byte 2 :    rsvd gen 13.len.8
+ *    Byte 3 :    7.....len.....0
+ *
+ *    Thus, le32_to_cpu on the dword will allow the big endian driver to read
+ *    the bit fields correctly. And cpu_to_le32 will convert bitfields
+ *    bit fields written by big endian driver to format required by device.
+ */
+
+struct Vmxnet3_TxDesc {
+    __le64 addr;
+
+#ifdef __BIG_ENDIAN_BITFIELD
+    u32 msscof:14;  /* MSS, checksum offset, flags */
+    u32 ext1:1;
+    u32 dtype:1;    /* descriptor type */
+    u32 rsvd:1;
+    u32 gen:1;      /* generation bit */
+    u32 len:14;
+#else
+    u32 len:14;
+    u32 gen:1;      /* generation bit */
+    u32 rsvd:1;
+    u32 dtype:1;    /* descriptor type */
+    u32 ext1:1;
+    u32 msscof:14;  /* MSS, checksum offset, flags */
+#endif  /* __BIG_ENDIAN_BITFIELD */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+    u32 tci:16;     /* Tag to Insert */
+    u32 ti:1;       /* VLAN Tag Insertion */
+    u32 ext2:1;
+    u32 cq:1;       /* completion request */
+    u32 eop:1;      /* End Of Packet */
+    u32 om:2;       /* offload mode */
+    u32 hlen:10;    /* header len */
+#else
+    u32 hlen:10;    /* header len */
+    u32 om:2;       /* offload mode */
+    u32 eop:1;      /* End Of Packet */
+    u32 cq:1;       /* completion request */
+    u32 ext2:1;
+    u32 ti:1;       /* VLAN Tag Insertion */
+    u32 tci:16;     /* Tag to Insert */
+#endif  /* __BIG_ENDIAN_BITFIELD */
+};
+
+/* TxDesc.OM values */
+#define VMXNET3_OM_NONE        0
+#define VMXNET3_OM_CSUM        2
+#define VMXNET3_OM_TSO        3
+
+/* fields in TxDesc we access w/o using bit fields */
+#define VMXNET3_TXD_EOP_SHIFT    12
+#define VMXNET3_TXD_CQ_SHIFT    13
+#define VMXNET3_TXD_GEN_SHIFT    14
+#define VMXNET3_TXD_EOP_DWORD_SHIFT 3
+#define VMXNET3_TXD_GEN_DWORD_SHIFT 2
+
+#define VMXNET3_TXD_CQ        (1 << VMXNET3_TXD_CQ_SHIFT)
+#define VMXNET3_TXD_EOP        (1 << VMXNET3_TXD_EOP_SHIFT)
+#define VMXNET3_TXD_GEN        (1 << VMXNET3_TXD_GEN_SHIFT)
+
+#define VMXNET3_HDR_COPY_SIZE   128
+
+
+struct Vmxnet3_TxDataDesc {
+    u8        data[VMXNET3_HDR_COPY_SIZE];
+};
+
+#define VMXNET3_TCD_GEN_SHIFT    31
+#define VMXNET3_TCD_GEN_SIZE    1
+#define VMXNET3_TCD_TXIDX_SHIFT    0
+#define VMXNET3_TCD_TXIDX_SIZE    12
+#define VMXNET3_TCD_GEN_DWORD_SHIFT    3
+
+struct Vmxnet3_TxCompDesc {
+    u32        txdIdx:12;    /* Index of the EOP TxDesc */
+    u32        ext1:20;
+
+    __le32        ext2;
+    __le32        ext3;
+
+    u32        rsvd:24;
+    u32        type:7;       /* completion type */
+    u32        gen:1;        /* generation bit */
+};
+
+struct Vmxnet3_RxDesc {
+    __le64        addr;
+
+#ifdef __BIG_ENDIAN_BITFIELD
+    u32        gen:1;        /* Generation bit */
+    u32        rsvd:15;
+    u32        dtype:1;      /* Descriptor type */
+    u32        btype:1;      /* Buffer Type */
+    u32        len:14;
+#else
+    u32        len:14;
+    u32        btype:1;      /* Buffer Type */
+    u32        dtype:1;      /* Descriptor type */
+    u32        rsvd:15;
+    u32        gen:1;        /* Generation bit */
+#endif
+    u32        ext1;
+};
+
+/* values of RXD.BTYPE */
+#define VMXNET3_RXD_BTYPE_HEAD   0    /* head only */
+#define VMXNET3_RXD_BTYPE_BODY   1    /* body only */
+
+/* fields in RxDesc we access w/o using bit fields */
+#define VMXNET3_RXD_BTYPE_SHIFT  14
+#define VMXNET3_RXD_GEN_SHIFT    31
+
+struct Vmxnet3_RxCompDesc {
+#ifdef __BIG_ENDIAN_BITFIELD
+    u32        ext2:1;
+    u32        cnc:1;        /* Checksum Not Calculated */
+    u32        rssType:4;    /* RSS hash type used */
+    u32        rqID:10;      /* rx queue/ring ID */
+    u32        sop:1;        /* Start of Packet */
+    u32        eop:1;        /* End of Packet */
+    u32        ext1:2;
+    u32        rxdIdx:12;    /* Index of the RxDesc */
+#else
+    u32        rxdIdx:12;    /* Index of the RxDesc */
+    u32        ext1:2;
+    u32        eop:1;        /* End of Packet */
+    u32        sop:1;        /* Start of Packet */
+    u32        rqID:10;      /* rx queue/ring ID */
+    u32        rssType:4;    /* RSS hash type used */
+    u32        cnc:1;        /* Checksum Not Calculated */
+    u32        ext2:1;
+#endif  /* __BIG_ENDIAN_BITFIELD */
+
+    __le32        rssHash;      /* RSS hash value */
+
+#ifdef __BIG_ENDIAN_BITFIELD
+    u32        tci:16;       /* Tag stripped */
+    u32        ts:1;         /* Tag is stripped */
+    u32        err:1;        /* Error */
+    u32        len:14;       /* data length */
+#else
+    u32        len:14;       /* data length */
+    u32        err:1;        /* Error */
+    u32        ts:1;         /* Tag is stripped */
+    u32        tci:16;       /* Tag stripped */
+#endif  /* __BIG_ENDIAN_BITFIELD */
+
+
+#ifdef __BIG_ENDIAN_BITFIELD
+    u32        gen:1;        /* generation bit */
+    u32        type:7;       /* completion type */
+    u32        fcs:1;        /* Frame CRC correct */
+    u32        frg:1;        /* IP Fragment */
+    u32        v4:1;         /* IPv4 */
+    u32        v6:1;         /* IPv6 */
+    u32        ipc:1;        /* IP Checksum Correct */
+    u32        tcp:1;        /* TCP packet */
+    u32        udp:1;        /* UDP packet */
+    u32        tuc:1;        /* TCP/UDP Checksum Correct */
+    u32        csum:16;
+#else
+    u32        csum:16;
+    u32        tuc:1;        /* TCP/UDP Checksum Correct */
+    u32        udp:1;        /* UDP packet */
+    u32        tcp:1;        /* TCP packet */
+    u32        ipc:1;        /* IP Checksum Correct */
+    u32        v6:1;         /* IPv6 */
+    u32        v4:1;         /* IPv4 */
+    u32        frg:1;        /* IP Fragment */
+    u32        fcs:1;        /* Frame CRC correct */
+    u32        type:7;       /* completion type */
+    u32        gen:1;        /* generation bit */
+#endif  /* __BIG_ENDIAN_BITFIELD */
+};
+
+/* fields in RxCompDesc we access via Vmxnet3_GenericDesc.dword[3] */
+#define VMXNET3_RCD_TUC_SHIFT    16
+#define VMXNET3_RCD_IPC_SHIFT    19
+
+/* fields in RxCompDesc we access via Vmxnet3_GenericDesc.qword[1] */
+#define VMXNET3_RCD_TYPE_SHIFT    56
+#define VMXNET3_RCD_GEN_SHIFT    63
+
+/* csum OK for TCP/UDP pkts over IP */
+#define VMXNET3_RCD_CSUM_OK (1 << VMXNET3_RCD_TUC_SHIFT | \
+                     1 << VMXNET3_RCD_IPC_SHIFT)
+#define VMXNET3_TXD_GEN_SIZE 1
+#define VMXNET3_TXD_EOP_SIZE 1
+
+/* value of RxCompDesc.rssType */
+enum {
+    VMXNET3_RCD_RSS_TYPE_NONE     = 0,
+    VMXNET3_RCD_RSS_TYPE_IPV4     = 1,
+    VMXNET3_RCD_RSS_TYPE_TCPIPV4  = 2,
+    VMXNET3_RCD_RSS_TYPE_IPV6     = 3,
+    VMXNET3_RCD_RSS_TYPE_TCPIPV6  = 4,
+};
+
+
+/* a union for accessing all cmd/completion descriptors */
+union Vmxnet3_GenericDesc {
+    __le64                qword[2];
+    __le32                dword[4];
+    __le16                word[8];
+    struct Vmxnet3_TxDesc        txd;
+    struct Vmxnet3_RxDesc        rxd;
+    struct Vmxnet3_TxCompDesc    tcd;
+    struct Vmxnet3_RxCompDesc    rcd;
+};
+
+#define VMXNET3_INIT_GEN       1
+
+/* Max size of a single tx buffer */
+#define VMXNET3_MAX_TX_BUF_SIZE  (1 << 14)
+
+/* # of tx desc needed for a tx buffer size */
+#define VMXNET3_TXD_NEEDED(size) (((size) + VMXNET3_MAX_TX_BUF_SIZE - 1) / \
+                    VMXNET3_MAX_TX_BUF_SIZE)
+
+/* max # of tx descs for a non-tso pkt */
+#define VMXNET3_MAX_TXD_PER_PKT 16
+
+/* Max size of a single rx buffer */
+#define VMXNET3_MAX_RX_BUF_SIZE  ((1 << 14) - 1)
+/* Minimum size of a type 0 buffer */
+#define VMXNET3_MIN_T0_BUF_SIZE  128
+#define VMXNET3_MAX_CSUM_OFFSET  1024
+
+/* Ring base address alignment */
+#define VMXNET3_RING_BA_ALIGN   512
+#define VMXNET3_RING_BA_MASK    (VMXNET3_RING_BA_ALIGN - 1)
+
+/* Ring size must be a multiple of 32 */
+#define VMXNET3_RING_SIZE_ALIGN 32
+#define VMXNET3_RING_SIZE_MASK  (VMXNET3_RING_SIZE_ALIGN - 1)
+
+/* Max ring size */
+#define VMXNET3_TX_RING_MAX_SIZE   4096
+#define VMXNET3_TC_RING_MAX_SIZE   4096
+#define VMXNET3_RX_RING_MAX_SIZE   4096
+#define VMXNET3_RC_RING_MAX_SIZE   8192
+
+/* a list of reasons for queue stop */
+
+enum {
+ VMXNET3_ERR_NOEOP        = 0x80000000, /* cannot find the EOP desc of a pkt */
+ VMXNET3_ERR_TXD_REUSE    = 0x80000001, /* reuse TxDesc before tx completion */
+ VMXNET3_ERR_BIG_PKT      = 0x80000002, /* too many TxDesc for a pkt */
+ VMXNET3_ERR_DESC_NOT_SPT = 0x80000003, /* descriptor type not supported */
+ VMXNET3_ERR_SMALL_BUF    = 0x80000004, /* type 0 buffer too small */
+ VMXNET3_ERR_STRESS       = 0x80000005, /* stress option firing in vmkernel */
+ VMXNET3_ERR_SWITCH       = 0x80000006, /* mode switch failure */
+ VMXNET3_ERR_TXD_INVALID  = 0x80000007, /* invalid TxDesc */
+};
+
+/* completion descriptor types */
+#define VMXNET3_CDTYPE_TXCOMP      0    /* Tx Completion Descriptor */
+#define VMXNET3_CDTYPE_RXCOMP      3    /* Rx Completion Descriptor */
+
+enum {
+    VMXNET3_GOS_BITS_UNK    = 0,   /* unknown */
+    VMXNET3_GOS_BITS_32     = 1,
+    VMXNET3_GOS_BITS_64     = 2,
+};
+
+#define VMXNET3_GOS_TYPE_UNK        0 /* unknown */
+#define VMXNET3_GOS_TYPE_LINUX      1
+#define VMXNET3_GOS_TYPE_WIN        2
+#define VMXNET3_GOS_TYPE_SOLARIS    3
+#define VMXNET3_GOS_TYPE_FREEBSD    4
+#define VMXNET3_GOS_TYPE_PXE        5
+
+struct Vmxnet3_GOSInfo {
+#ifdef __BIG_ENDIAN_BITFIELD
+    u32        gosMisc:10;    /* other info about gos */
+    u32        gosVer:16;     /* gos version */
+    u32        gosType:4;     /* which guest */
+    u32        gosBits:2;    /* 32-bit or 64-bit? */
+#else
+    u32        gosBits:2;     /* 32-bit or 64-bit? */
+    u32        gosType:4;     /* which guest */
+    u32        gosVer:16;     /* gos version */
+    u32        gosMisc:10;    /* other info about gos */
+#endif  /* __BIG_ENDIAN_BITFIELD */
+};
+
+struct Vmxnet3_DriverInfo {
+    __le32                version;
+    struct Vmxnet3_GOSInfo        gos;
+    __le32                vmxnet3RevSpt;
+    __le32                uptVerSpt;
+};
+
+
+#define VMXNET3_REV1_MAGIC  0xbabefee1
+
+/*
+ * QueueDescPA must be 128 bytes aligned. It points to an array of
+ * Vmxnet3_TxQueueDesc followed by an array of Vmxnet3_RxQueueDesc.
+ * The number of Vmxnet3_TxQueueDesc/Vmxnet3_RxQueueDesc are specified by
+ * Vmxnet3_MiscConf.numTxQueues/numRxQueues, respectively.
+ */
+#define VMXNET3_QUEUE_DESC_ALIGN  128
+
+
+struct Vmxnet3_MiscConf {
+    struct Vmxnet3_DriverInfo driverInfo;
+    __le64        uptFeatures;
+    __le64        ddPA;         /* driver data PA */
+    __le64        queueDescPA;  /* queue descriptor table PA */
+    __le32        ddLen;        /* driver data len */
+    __le32        queueDescLen; /* queue desc. table len in bytes */
+    __le32        mtu;
+    __le16        maxNumRxSG;
+    u8        numTxQueues;
+    u8        numRxQueues;
+    __le32        reserved[4];
+};
+
+
+struct Vmxnet3_TxQueueConf {
+    __le64        txRingBasePA;
+    __le64        dataRingBasePA;
+    __le64        compRingBasePA;
+    __le64        ddPA;         /* driver data */
+    __le64        reserved;
+    __le32        txRingSize;   /* # of tx desc */
+    __le32        dataRingSize; /* # of data desc */
+    __le32        compRingSize; /* # of comp desc */
+    __le32        ddLen;        /* size of driver data */
+    u8        intrIdx;
+    u8        _pad[7];
+};
+
+
+struct Vmxnet3_RxQueueConf {
+    __le64        rxRingBasePA[2];
+    __le64        compRingBasePA;
+    __le64        ddPA;            /* driver data */
+    __le64        reserved;
+    __le32        rxRingSize[2];   /* # of rx desc */
+    __le32        compRingSize;    /* # of rx comp desc */
+    __le32        ddLen;           /* size of driver data */
+    u8        intrIdx;
+    u8        _pad[7];
+};
+
+
+enum vmxnet3_intr_mask_mode {
+    VMXNET3_IMM_AUTO   = 0,
+    VMXNET3_IMM_ACTIVE = 1,
+    VMXNET3_IMM_LAZY   = 2
+};
+
+enum vmxnet3_intr_type {
+    VMXNET3_IT_AUTO = 0,
+    VMXNET3_IT_INTX = 1,
+    VMXNET3_IT_MSI  = 2,
+    VMXNET3_IT_MSIX = 3
+};
+
+#define VMXNET3_MAX_TX_QUEUES  8
+#define VMXNET3_MAX_RX_QUEUES  16
+/* addition 1 for events */
+#define VMXNET3_MAX_INTRS      25
+
+/* value of intrCtrl */
+#define VMXNET3_IC_DISABLE_ALL  0x1   /* bit 0 */
+
+
+struct Vmxnet3_IntrConf {
+    bool        autoMask;
+    u8        numIntrs;      /* # of interrupts */
+    u8        eventIntrIdx;
+    u8        modLevels[VMXNET3_MAX_INTRS];    /* moderation level for
+                             * each intr */
+    __le32        intrCtrl;
+    __le32        reserved[2];
+};
+
+/* one bit per VLAN ID, the size is in the units of u32 */
+#define VMXNET3_VFT_SIZE  (4096/(sizeof(uint32_t)*8))
+
+
+struct Vmxnet3_QueueStatus {
+    bool        stopped;
+    u8        _pad[3];
+    __le32        error;
+};
+
+
+struct Vmxnet3_TxQueueCtrl {
+    __le32        txNumDeferred;
+    __le32        txThreshold;
+    __le64        reserved;
+};
+
+
+struct Vmxnet3_RxQueueCtrl {
+    bool        updateRxProd;
+    u8        _pad[7];
+    __le64        reserved;
+};
+
+enum {
+    VMXNET3_RXM_UCAST     = 0x01,  /* unicast only */
+    VMXNET3_RXM_MCAST     = 0x02,  /* multicast passing the filters */
+    VMXNET3_RXM_BCAST     = 0x04,  /* broadcast only */
+    VMXNET3_RXM_ALL_MULTI = 0x08,  /* all multicast */
+    VMXNET3_RXM_PROMISC   = 0x10  /* promiscuous */
+};
+
+struct Vmxnet3_RxFilterConf {
+    __le32        rxMode;       /* VMXNET3_RXM_xxx */
+    __le16        mfTableLen;   /* size of the multicast filter table */
+    __le16        _pad1;
+    __le64        mfTablePA;    /* PA of the multicast filters table */
+    __le32        vfTable[VMXNET3_VFT_SIZE]; /* vlan filter */
+};
+
+
+#define VMXNET3_PM_MAX_FILTERS        6
+#define VMXNET3_PM_MAX_PATTERN_SIZE   128
+#define VMXNET3_PM_MAX_MASK_SIZE      (VMXNET3_PM_MAX_PATTERN_SIZE / 8)
+
+#define VMXNET3_PM_WAKEUP_MAGIC  cpu_to_le16(0x01)  /* wake up on magic pkts */
+#define VMXNET3_PM_WAKEUP_FILTER cpu_to_le16(0x02)  /* wake up on pkts matching
+                                                     * filters */
+
+
+struct Vmxnet3_PM_PktFilter {
+    u8        maskSize;
+    u8        patternSize;
+    u8        mask[VMXNET3_PM_MAX_MASK_SIZE];
+    u8        pattern[VMXNET3_PM_MAX_PATTERN_SIZE];
+    u8        pad[6];
+};
+
+
+struct Vmxnet3_PMConf {
+    __le16        wakeUpEvents;  /* VMXNET3_PM_WAKEUP_xxx */
+    u8        numFilters;
+    u8        pad[5];
+    struct Vmxnet3_PM_PktFilter filters[VMXNET3_PM_MAX_FILTERS];
+};
+
+
+struct Vmxnet3_VariableLenConfDesc {
+    __le32        confVer;
+    __le32        confLen;
+    __le64        confPA;
+};
+
+
+struct Vmxnet3_TxQueueDesc {
+    struct Vmxnet3_TxQueueCtrl        ctrl;
+    struct Vmxnet3_TxQueueConf        conf;
+
+    /* Driver read after a GET command */
+    struct Vmxnet3_QueueStatus        status;
+    struct UPT1_TxStats            stats;
+    u8                    _pad[88]; /* 128 aligned */
+};
+
+
+struct Vmxnet3_RxQueueDesc {
+    struct Vmxnet3_RxQueueCtrl        ctrl;
+    struct Vmxnet3_RxQueueConf        conf;
+    /* Driver read after a GET commad */
+    struct Vmxnet3_QueueStatus        status;
+    struct UPT1_RxStats            stats;
+    u8                      __pad[88]; /* 128 aligned */
+};
+
+
+struct Vmxnet3_DSDevRead {
+    /* read-only region for device, read by dev in response to a SET cmd */
+    struct Vmxnet3_MiscConf            misc;
+    struct Vmxnet3_IntrConf            intrConf;
+    struct Vmxnet3_RxFilterConf        rxFilterConf;
+    struct Vmxnet3_VariableLenConfDesc    rssConfDesc;
+    struct Vmxnet3_VariableLenConfDesc    pmConfDesc;
+    struct Vmxnet3_VariableLenConfDesc    pluginConfDesc;
+};
+
+/* All structures in DriverShared are padded to multiples of 8 bytes */
+struct Vmxnet3_DriverShared {
+    __le32              magic;
+    /* make devRead start at 64bit boundaries */
+    __le32              pad;
+    struct Vmxnet3_DSDevRead    devRead;
+    __le32              ecr;
+    __le32              reserved[5];
+};
+
+
+#define VMXNET3_ECR_RQERR       (1 << 0)
+#define VMXNET3_ECR_TQERR       (1 << 1)
+#define VMXNET3_ECR_LINK        (1 << 2)
+#define VMXNET3_ECR_DIC         (1 << 3)
+#define VMXNET3_ECR_DEBUG       (1 << 4)
+
+/* flip the gen bit of a ring */
+#define VMXNET3_FLIP_RING_GEN(gen) ((gen) = (gen) ^ 0x1)
+
+/* only use this if moving the idx won't affect the gen bit */
+#define VMXNET3_INC_RING_IDX_ONLY(idx, ring_size) \
+    do {\
+        (idx)++;\
+        if (unlikely((idx) == (ring_size))) {\
+            (idx) = 0;\
+        } \
+    } while (0)
+
+#define VMXNET3_SET_VFTABLE_ENTRY(vfTable, vid) \
+    (vfTable[vid >> 5] |= (1 << (vid & 31)))
+#define VMXNET3_CLEAR_VFTABLE_ENTRY(vfTable, vid) \
+    (vfTable[vid >> 5] &= ~(1 << (vid & 31)))
+
+#define VMXNET3_VFTABLE_ENTRY_IS_SET(vfTable, vid) \
+    ((vfTable[vid >> 5] & (1 << (vid & 31))) != 0)
+
+#define VMXNET3_MAX_MTU     9000
+#define VMXNET3_MIN_MTU     60
+
+#define VMXNET3_LINK_UP         (10000 << 16 | 1)    /* 10 Gbps, up */
+#define VMXNET3_LINK_DOWN       0
+
+#undef u64
+#undef u32
+#undef u16
+#undef u8
+#undef __le16
+#undef __le32
+#undef __le64
+#undef __packed
+#undef const_cpu_to_le64
+#if defined(HOST_WORDS_BIGENDIAN)
+#undef __BIG_ENDIAN_BITFIELD
+#endif
+
+#endif
diff --git a/hw/net/vmxnet_debug.h b/hw/net/vmxnet_debug.h
new file mode 100644 (file)
index 0000000..96dae0f
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * QEMU VMWARE VMXNET* paravirtual NICs - debugging facilities
+ *
+ * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
+ *
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
+ *
+ * Authors:
+ * Dmitry Fleytman <dmitry@daynix.com>
+ * Tamir Shomer <tamirs@daynix.com>
+ * Yan Vugenfirer <yan@daynix.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef _QEMU_VMXNET_DEBUG_H
+#define _QEMU_VMXNET_DEBUG_H
+
+#define VMXNET_DEVICE_NAME "vmxnet3"
+
+/* #define VMXNET_DEBUG_CB */
+#define VMXNET_DEBUG_WARNINGS
+#define VMXNET_DEBUG_ERRORS
+/* #define VMXNET_DEBUG_INTERRUPTS */
+/* #define VMXNET_DEBUG_CONFIG */
+/* #define VMXNET_DEBUG_RINGS */
+/* #define VMXNET_DEBUG_PACKETS */
+/* #define VMXNET_DEBUG_SHMEM_ACCESS */
+
+#ifdef VMXNET_DEBUG_SHMEM_ACCESS
+#define VMW_SHPRN(fmt, ...)                                                   \
+    do {                                                                      \
+        printf("[%s][SH][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__,       \
+            ## __VA_ARGS__);                                                  \
+    } while (0)
+#else
+#define VMW_SHPRN(fmt, ...) do {} while (0)
+#endif
+
+#ifdef VMXNET_DEBUG_CB
+#define VMW_CBPRN(fmt, ...)                                                   \
+    do {                                                                      \
+        printf("[%s][CB][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__,       \
+            ## __VA_ARGS__);                                                  \
+    } while (0)
+#else
+#define VMW_CBPRN(fmt, ...) do {} while (0)
+#endif
+
+#ifdef VMXNET_DEBUG_PACKETS
+#define VMW_PKPRN(fmt, ...)                                                   \
+    do {                                                                      \
+        printf("[%s][PK][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__,       \
+            ## __VA_ARGS__);                                                  \
+    } while (0)
+#else
+#define VMW_PKPRN(fmt, ...) do {} while (0)
+#endif
+
+#ifdef VMXNET_DEBUG_WARNINGS
+#define VMW_WRPRN(fmt, ...)                                                   \
+    do {                                                                      \
+        printf("[%s][WR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__,       \
+            ## __VA_ARGS__);                                                  \
+    } while (0)
+#else
+#define VMW_WRPRN(fmt, ...) do {} while (0)
+#endif
+
+#ifdef VMXNET_DEBUG_ERRORS
+#define VMW_ERPRN(fmt, ...)                                                   \
+    do {                                                                      \
+        printf("[%s][ER][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__,       \
+            ## __VA_ARGS__);                                                  \
+    } while (0)
+#else
+#define VMW_ERPRN(fmt, ...) do {} while (0)
+#endif
+
+#ifdef VMXNET_DEBUG_INTERRUPTS
+#define VMW_IRPRN(fmt, ...)                                                   \
+    do {                                                                      \
+        printf("[%s][IR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__,       \
+            ## __VA_ARGS__);                                                  \
+    } while (0)
+#else
+#define VMW_IRPRN(fmt, ...) do {} while (0)
+#endif
+
+#ifdef VMXNET_DEBUG_CONFIG
+#define VMW_CFPRN(fmt, ...)                                                   \
+    do {                                                                      \
+        printf("[%s][CF][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__,       \
+            ## __VA_ARGS__);                                                  \
+    } while (0)
+#else
+#define VMW_CFPRN(fmt, ...) do {} while (0)
+#endif
+
+#ifdef VMXNET_DEBUG_RINGS
+#define VMW_RIPRN(fmt, ...)                                                   \
+    do {                                                                      \
+        printf("[%s][RI][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__,       \
+            ## __VA_ARGS__);                                                  \
+    } while (0)
+#else
+#define VMW_RIPRN(fmt, ...) do {} while (0)
+#endif
+
+#define VMXNET_MF       "%02X:%02X:%02X:%02X:%02X:%02X"
+#define VMXNET_MA(a)    (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+
+#endif /* _QEMU_VMXNET3_DEBUG_H  */
diff --git a/hw/net/vmxnet_rx_pkt.c b/hw/net/vmxnet_rx_pkt.c
new file mode 100644 (file)
index 0000000..a40e346
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * QEMU VMWARE VMXNET* paravirtual NICs - RX packets abstractions
+ *
+ * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
+ *
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
+ *
+ * Authors:
+ * Dmitry Fleytman <dmitry@daynix.com>
+ * Tamir Shomer <tamirs@daynix.com>
+ * Yan Vugenfirer <yan@daynix.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "vmxnet_rx_pkt.h"
+#include "net/eth.h"
+#include "qemu-common.h"
+#include "qemu/iov.h"
+#include "net/checksum.h"
+#include "net/tap.h"
+
+/*
+ * RX packet may contain up to 2 fragments - rebuilt eth header
+ * in case of VLAN tag stripping
+ * and payload received from QEMU - in any case
+ */
+#define VMXNET_MAX_RX_PACKET_FRAGMENTS (2)
+
+struct VmxnetRxPkt {
+    struct virtio_net_hdr virt_hdr;
+    uint8_t ehdr_buf[ETH_MAX_L2_HDR_LEN];
+    struct iovec vec[VMXNET_MAX_RX_PACKET_FRAGMENTS];
+    uint16_t vec_len;
+    uint32_t tot_len;
+    uint16_t tci;
+    bool vlan_stripped;
+    bool has_virt_hdr;
+    eth_pkt_types_e packet_type;
+
+    /* Analysis results */
+    bool isip4;
+    bool isip6;
+    bool isudp;
+    bool istcp;
+};
+
+void vmxnet_rx_pkt_init(struct VmxnetRxPkt **pkt, bool has_virt_hdr)
+{
+    struct VmxnetRxPkt *p = g_malloc0(sizeof *p);
+    p->has_virt_hdr = has_virt_hdr;
+    *pkt = p;
+}
+
+void vmxnet_rx_pkt_uninit(struct VmxnetRxPkt *pkt)
+{
+    g_free(pkt);
+}
+
+struct virtio_net_hdr *vmxnet_rx_pkt_get_vhdr(struct VmxnetRxPkt *pkt)
+{
+    assert(pkt);
+    return &pkt->virt_hdr;
+}
+
+void vmxnet_rx_pkt_attach_data(struct VmxnetRxPkt *pkt, const void *data,
+                               size_t len, bool strip_vlan)
+{
+    uint16_t tci = 0;
+    uint16_t ploff;
+    assert(pkt);
+    pkt->vlan_stripped = false;
+
+    if (strip_vlan) {
+        pkt->vlan_stripped = eth_strip_vlan(data, pkt->ehdr_buf, &ploff, &tci);
+    }
+
+    if (pkt->vlan_stripped) {
+        pkt->vec[0].iov_base = pkt->ehdr_buf;
+        pkt->vec[0].iov_len = ploff - sizeof(struct vlan_header);
+        pkt->vec[1].iov_base = (uint8_t *) data + ploff;
+        pkt->vec[1].iov_len = len - ploff;
+        pkt->vec_len = 2;
+        pkt->tot_len = len - ploff + sizeof(struct eth_header);
+    } else {
+        pkt->vec[0].iov_base = (void *)data;
+        pkt->vec[0].iov_len = len;
+        pkt->vec_len = 1;
+        pkt->tot_len = len;
+    }
+
+    pkt->tci = tci;
+
+    eth_get_protocols(data, len, &pkt->isip4, &pkt->isip6,
+        &pkt->isudp, &pkt->istcp);
+}
+
+void vmxnet_rx_pkt_dump(struct VmxnetRxPkt *pkt)
+{
+#ifdef VMXNET_RX_PKT_DEBUG
+    VmxnetRxPkt *pkt = (VmxnetRxPkt *)pkt;
+    assert(pkt);
+
+    printf("RX PKT: tot_len: %d, vlan_stripped: %d, vlan_tag: %d\n",
+              pkt->tot_len, pkt->vlan_stripped, pkt->tci);
+#endif
+}
+
+void vmxnet_rx_pkt_set_packet_type(struct VmxnetRxPkt *pkt,
+    eth_pkt_types_e packet_type)
+{
+    assert(pkt);
+
+    pkt->packet_type = packet_type;
+
+}
+
+eth_pkt_types_e vmxnet_rx_pkt_get_packet_type(struct VmxnetRxPkt *pkt)
+{
+    assert(pkt);
+
+    return pkt->packet_type;
+}
+
+size_t vmxnet_rx_pkt_get_total_len(struct VmxnetRxPkt *pkt)
+{
+    assert(pkt);
+
+    return pkt->tot_len;
+}
+
+void vmxnet_rx_pkt_get_protocols(struct VmxnetRxPkt *pkt,
+                                 bool *isip4, bool *isip6,
+                                 bool *isudp, bool *istcp)
+{
+    assert(pkt);
+
+    *isip4 = pkt->isip4;
+    *isip6 = pkt->isip6;
+    *isudp = pkt->isudp;
+    *istcp = pkt->istcp;
+}
+
+struct iovec *vmxnet_rx_pkt_get_iovec(struct VmxnetRxPkt *pkt)
+{
+    assert(pkt);
+
+    return pkt->vec;
+}
+
+void vmxnet_rx_pkt_set_vhdr(struct VmxnetRxPkt *pkt,
+                            struct virtio_net_hdr *vhdr)
+{
+    assert(pkt);
+
+    memcpy(&pkt->virt_hdr, vhdr, sizeof pkt->virt_hdr);
+}
+
+bool vmxnet_rx_pkt_is_vlan_stripped(struct VmxnetRxPkt *pkt)
+{
+    assert(pkt);
+
+    return pkt->vlan_stripped;
+}
+
+bool vmxnet_rx_pkt_has_virt_hdr(struct VmxnetRxPkt *pkt)
+{
+    assert(pkt);
+
+    return pkt->has_virt_hdr;
+}
+
+uint16_t vmxnet_rx_pkt_get_num_frags(struct VmxnetRxPkt *pkt)
+{
+    assert(pkt);
+
+    return pkt->vec_len;
+}
+
+uint16_t vmxnet_rx_pkt_get_vlan_tag(struct VmxnetRxPkt *pkt)
+{
+    assert(pkt);
+
+    return pkt->tci;
+}
diff --git a/hw/net/vmxnet_rx_pkt.h b/hw/net/vmxnet_rx_pkt.h
new file mode 100644 (file)
index 0000000..6b2c60e
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * QEMU VMWARE VMXNET* paravirtual NICs - RX packets abstraction
+ *
+ * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
+ *
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
+ *
+ * Authors:
+ * Dmitry Fleytman <dmitry@daynix.com>
+ * Tamir Shomer <tamirs@daynix.com>
+ * Yan Vugenfirer <yan@daynix.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef VMXNET_RX_PKT_H
+#define VMXNET_RX_PKT_H
+
+#include "stdint.h"
+#include "stdbool.h"
+#include "net/eth.h"
+
+/* defines to enable packet dump functions */
+/*#define VMXNET_RX_PKT_DEBUG*/
+
+struct VmxnetRxPkt;
+
+/**
+ * Clean all rx packet resources
+ *
+ * @pkt:            packet
+ *
+ */
+void vmxnet_rx_pkt_uninit(struct VmxnetRxPkt *pkt);
+
+/**
+ * Init function for rx packet functionality
+ *
+ * @pkt:            packet pointer
+ * @has_virt_hdr:   device uses virtio header
+ *
+ */
+void vmxnet_rx_pkt_init(struct VmxnetRxPkt **pkt, bool has_virt_hdr);
+
+/**
+ * returns total length of data attached to rx context
+ *
+ * @pkt:            packet
+ *
+ * Return:  nothing
+ *
+ */
+size_t vmxnet_rx_pkt_get_total_len(struct VmxnetRxPkt *pkt);
+
+/**
+ * fetches packet analysis results
+ *
+ * @pkt:            packet
+ * @isip4:          whether the packet given is IPv4
+ * @isip6:          whether the packet given is IPv6
+ * @isudp:          whether the packet given is UDP
+ * @istcp:          whether the packet given is TCP
+ *
+ */
+void vmxnet_rx_pkt_get_protocols(struct VmxnetRxPkt *pkt,
+                                 bool *isip4, bool *isip6,
+                                 bool *isudp, bool *istcp);
+
+/**
+ * returns virtio header stored in rx context
+ *
+ * @pkt:            packet
+ * @ret:            virtio header
+ *
+ */
+struct virtio_net_hdr *vmxnet_rx_pkt_get_vhdr(struct VmxnetRxPkt *pkt);
+
+/**
+ * returns packet type
+ *
+ * @pkt:            packet
+ * @ret:            packet type
+ *
+ */
+eth_pkt_types_e vmxnet_rx_pkt_get_packet_type(struct VmxnetRxPkt *pkt);
+
+/**
+ * returns vlan tag
+ *
+ * @pkt:            packet
+ * @ret:            VLAN tag
+ *
+ */
+uint16_t vmxnet_rx_pkt_get_vlan_tag(struct VmxnetRxPkt *pkt);
+
+/**
+ * tells whether vlan was stripped from the packet
+ *
+ * @pkt:            packet
+ * @ret:            VLAN stripped sign
+ *
+ */
+bool vmxnet_rx_pkt_is_vlan_stripped(struct VmxnetRxPkt *pkt);
+
+/**
+ * notifies caller if the packet has virtio header
+ *
+ * @pkt:            packet
+ * @ret:            true if packet has virtio header, false otherwize
+ *
+ */
+bool vmxnet_rx_pkt_has_virt_hdr(struct VmxnetRxPkt *pkt);
+
+/**
+ * returns number of frags attached to the packet
+ *
+ * @pkt:            packet
+ * @ret:            number of frags
+ *
+ */
+uint16_t vmxnet_rx_pkt_get_num_frags(struct VmxnetRxPkt *pkt);
+
+/**
+ * attach data to rx packet
+ *
+ * @pkt:            packet
+ * @data:           pointer to the data buffer
+ * @len:            data length
+ * @strip_vlan:     should the module strip vlan from data
+ *
+ */
+void vmxnet_rx_pkt_attach_data(struct VmxnetRxPkt *pkt, const void *data,
+    size_t len, bool strip_vlan);
+
+/**
+ * returns io vector that holds the attached data
+ *
+ * @pkt:            packet
+ * @ret:            pointer to IOVec
+ *
+ */
+struct iovec *vmxnet_rx_pkt_get_iovec(struct VmxnetRxPkt *pkt);
+
+/**
+ * prints rx packet data if debug is enabled
+ *
+ * @pkt:            packet
+ *
+ */
+void vmxnet_rx_pkt_dump(struct VmxnetRxPkt *pkt);
+
+/**
+ * copy passed vhdr data to packet context
+ *
+ * @pkt:            packet
+ * @vhdr:           VHDR buffer
+ *
+ */
+void vmxnet_rx_pkt_set_vhdr(struct VmxnetRxPkt *pkt,
+    struct virtio_net_hdr *vhdr);
+
+/**
+ * save packet type in packet context
+ *
+ * @pkt:            packet
+ * @packet_type:    the packet type
+ *
+ */
+void vmxnet_rx_pkt_set_packet_type(struct VmxnetRxPkt *pkt,
+    eth_pkt_types_e packet_type);
+
+#endif
diff --git a/hw/net/vmxnet_tx_pkt.c b/hw/net/vmxnet_tx_pkt.c
new file mode 100644 (file)
index 0000000..b1e795b
--- /dev/null
@@ -0,0 +1,567 @@
+/*
+ * QEMU VMWARE VMXNET* paravirtual NICs - TX packets abstractions
+ *
+ * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
+ *
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
+ *
+ * Authors:
+ * Dmitry Fleytman <dmitry@daynix.com>
+ * Tamir Shomer <tamirs@daynix.com>
+ * Yan Vugenfirer <yan@daynix.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "vmxnet_tx_pkt.h"
+#include "net/eth.h"
+#include "qemu-common.h"
+#include "qemu/iov.h"
+#include "net/checksum.h"
+#include "net/tap.h"
+#include "net/net.h"
+#include "exec/cpu-common.h"
+
+enum {
+    VMXNET_TX_PKT_VHDR_FRAG = 0,
+    VMXNET_TX_PKT_L2HDR_FRAG,
+    VMXNET_TX_PKT_L3HDR_FRAG,
+    VMXNET_TX_PKT_PL_START_FRAG
+};
+
+/* TX packet private context */
+struct VmxnetTxPkt {
+    struct virtio_net_hdr virt_hdr;
+    bool has_virt_hdr;
+
+    struct iovec *raw;
+    uint32_t raw_frags;
+    uint32_t max_raw_frags;
+
+    struct iovec *vec;
+
+    uint8_t l2_hdr[ETH_MAX_L2_HDR_LEN];
+
+    uint32_t payload_len;
+
+    uint32_t payload_frags;
+    uint32_t max_payload_frags;
+
+    uint16_t hdr_len;
+    eth_pkt_types_e packet_type;
+    uint8_t l4proto;
+};
+
+void vmxnet_tx_pkt_init(struct VmxnetTxPkt **pkt, uint32_t max_frags,
+    bool has_virt_hdr)
+{
+    struct VmxnetTxPkt *p = g_malloc0(sizeof *p);
+
+    p->vec = g_malloc((sizeof *p->vec) *
+        (max_frags + VMXNET_TX_PKT_PL_START_FRAG));
+
+    p->raw = g_malloc((sizeof *p->raw) * max_frags);
+
+    p->max_payload_frags = max_frags;
+    p->max_raw_frags = max_frags;
+    p->has_virt_hdr = has_virt_hdr;
+    p->vec[VMXNET_TX_PKT_VHDR_FRAG].iov_base = &p->virt_hdr;
+    p->vec[VMXNET_TX_PKT_VHDR_FRAG].iov_len =
+        p->has_virt_hdr ? sizeof p->virt_hdr : 0;
+    p->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base = &p->l2_hdr;
+    p->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base = NULL;
+    p->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len = 0;
+
+    *pkt = p;
+}
+
+void vmxnet_tx_pkt_uninit(struct VmxnetTxPkt *pkt)
+{
+    if (pkt) {
+        g_free(pkt->vec);
+        g_free(pkt->raw);
+        g_free(pkt);
+    }
+}
+
+void vmxnet_tx_pkt_update_ip_checksums(struct VmxnetTxPkt *pkt)
+{
+    uint16_t csum;
+    uint32_t ph_raw_csum;
+    assert(pkt);
+    uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN;
+    struct ip_header *ip_hdr;
+
+    if (VIRTIO_NET_HDR_GSO_TCPV4 != gso_type &&
+        VIRTIO_NET_HDR_GSO_UDP != gso_type) {
+        return;
+    }
+
+    ip_hdr = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base;
+
+    if (pkt->payload_len + pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len >
+        ETH_MAX_IP_DGRAM_LEN) {
+        return;
+    }
+
+    ip_hdr->ip_len = cpu_to_be16(pkt->payload_len +
+        pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len);
+
+    /* Calculate IP header checksum                    */
+    ip_hdr->ip_sum = 0;
+    csum = net_raw_checksum((uint8_t *)ip_hdr,
+        pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len);
+    ip_hdr->ip_sum = cpu_to_be16(csum);
+
+    /* Calculate IP pseudo header checksum             */
+    ph_raw_csum = eth_calc_pseudo_hdr_csum(ip_hdr, pkt->payload_len);
+    csum = cpu_to_be16(~net_checksum_finish(ph_raw_csum));
+    iov_from_buf(&pkt->vec[VMXNET_TX_PKT_PL_START_FRAG], pkt->payload_frags,
+                 pkt->virt_hdr.csum_offset, &csum, sizeof(csum));
+}
+
+static void vmxnet_tx_pkt_calculate_hdr_len(struct VmxnetTxPkt *pkt)
+{
+    pkt->hdr_len = pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len +
+        pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len;
+}
+
+static bool vmxnet_tx_pkt_parse_headers(struct VmxnetTxPkt *pkt)
+{
+    struct iovec *l2_hdr, *l3_hdr;
+    size_t bytes_read;
+    size_t full_ip6hdr_len;
+    uint16_t l3_proto;
+
+    assert(pkt);
+
+    l2_hdr = &pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG];
+    l3_hdr = &pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG];
+
+    bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 0, l2_hdr->iov_base,
+                            ETH_MAX_L2_HDR_LEN);
+    if (bytes_read < ETH_MAX_L2_HDR_LEN) {
+        l2_hdr->iov_len = 0;
+        return false;
+    } else {
+        l2_hdr->iov_len = eth_get_l2_hdr_length(l2_hdr->iov_base);
+    }
+
+    l3_proto = eth_get_l3_proto(l2_hdr->iov_base, l2_hdr->iov_len);
+
+    switch (l3_proto) {
+    case ETH_P_IP:
+        l3_hdr->iov_base = g_malloc(ETH_MAX_IP4_HDR_LEN);
+
+        bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len,
+                                l3_hdr->iov_base, sizeof(struct ip_header));
+
+        if (bytes_read < sizeof(struct ip_header)) {
+            l3_hdr->iov_len = 0;
+            return false;
+        }
+
+        l3_hdr->iov_len = IP_HDR_GET_LEN(l3_hdr->iov_base);
+        pkt->l4proto = ((struct ip_header *) l3_hdr->iov_base)->ip_p;
+
+        /* copy optional IPv4 header data */
+        bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags,
+                                l2_hdr->iov_len + sizeof(struct ip_header),
+                                l3_hdr->iov_base + sizeof(struct ip_header),
+                                l3_hdr->iov_len - sizeof(struct ip_header));
+        if (bytes_read < l3_hdr->iov_len - sizeof(struct ip_header)) {
+            l3_hdr->iov_len = 0;
+            return false;
+        }
+        break;
+
+    case ETH_P_IPV6:
+        if (!eth_parse_ipv6_hdr(pkt->raw, pkt->raw_frags, l2_hdr->iov_len,
+                               &pkt->l4proto, &full_ip6hdr_len)) {
+            l3_hdr->iov_len = 0;
+            return false;
+        }
+
+        l3_hdr->iov_base = g_malloc(full_ip6hdr_len);
+
+        bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len,
+                                l3_hdr->iov_base, full_ip6hdr_len);
+
+        if (bytes_read < full_ip6hdr_len) {
+            l3_hdr->iov_len = 0;
+            return false;
+        } else {
+            l3_hdr->iov_len = full_ip6hdr_len;
+        }
+        break;
+
+    default:
+        l3_hdr->iov_len = 0;
+        break;
+    }
+
+    vmxnet_tx_pkt_calculate_hdr_len(pkt);
+    pkt->packet_type = get_eth_packet_type(l2_hdr->iov_base);
+    return true;
+}
+
+static bool vmxnet_tx_pkt_rebuild_payload(struct VmxnetTxPkt *pkt)
+{
+    size_t payload_len = iov_size(pkt->raw, pkt->raw_frags) - pkt->hdr_len;
+
+    pkt->payload_frags = iov_copy(&pkt->vec[VMXNET_TX_PKT_PL_START_FRAG],
+                                pkt->max_payload_frags,
+                                pkt->raw, pkt->raw_frags,
+                                pkt->hdr_len, payload_len);
+
+    if (pkt->payload_frags != (uint32_t) -1) {
+        pkt->payload_len = payload_len;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool vmxnet_tx_pkt_parse(struct VmxnetTxPkt *pkt)
+{
+    return vmxnet_tx_pkt_parse_headers(pkt) &&
+           vmxnet_tx_pkt_rebuild_payload(pkt);
+}
+
+struct virtio_net_hdr *vmxnet_tx_pkt_get_vhdr(struct VmxnetTxPkt *pkt)
+{
+    assert(pkt);
+    return &pkt->virt_hdr;
+}
+
+static uint8_t vmxnet_tx_pkt_get_gso_type(struct VmxnetTxPkt *pkt,
+                                          bool tso_enable)
+{
+    uint8_t rc = VIRTIO_NET_HDR_GSO_NONE;
+    uint16_t l3_proto;
+
+    l3_proto = eth_get_l3_proto(pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base,
+        pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len);
+
+    if (!tso_enable) {
+        goto func_exit;
+    }
+
+    rc = eth_get_gso_type(l3_proto, pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base,
+                          pkt->l4proto);
+
+func_exit:
+    return rc;
+}
+
+void vmxnet_tx_pkt_build_vheader(struct VmxnetTxPkt *pkt, bool tso_enable,
+    bool csum_enable, uint32_t gso_size)
+{
+    struct tcp_hdr l4hdr;
+    assert(pkt);
+
+    /* csum has to be enabled if tso is. */
+    assert(csum_enable || !tso_enable);
+
+    pkt->virt_hdr.gso_type = vmxnet_tx_pkt_get_gso_type(pkt, tso_enable);
+
+    switch (pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
+    case VIRTIO_NET_HDR_GSO_NONE:
+        pkt->virt_hdr.hdr_len = 0;
+        pkt->virt_hdr.gso_size = 0;
+        break;
+
+    case VIRTIO_NET_HDR_GSO_UDP:
+        pkt->virt_hdr.gso_size = IP_FRAG_ALIGN_SIZE(gso_size);
+        pkt->virt_hdr.hdr_len = pkt->hdr_len + sizeof(struct udp_header);
+        break;
+
+    case VIRTIO_NET_HDR_GSO_TCPV4:
+    case VIRTIO_NET_HDR_GSO_TCPV6:
+        iov_to_buf(&pkt->vec[VMXNET_TX_PKT_PL_START_FRAG], pkt->payload_frags,
+                   0, &l4hdr, sizeof(l4hdr));
+        pkt->virt_hdr.hdr_len = pkt->hdr_len + l4hdr.th_off * sizeof(uint32_t);
+        pkt->virt_hdr.gso_size = IP_FRAG_ALIGN_SIZE(gso_size);
+        break;
+
+    default:
+        assert(false);
+    }
+
+    if (csum_enable) {
+        switch (pkt->l4proto) {
+        case IP_PROTO_TCP:
+            pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
+            pkt->virt_hdr.csum_start = pkt->hdr_len;
+            pkt->virt_hdr.csum_offset = offsetof(struct tcp_hdr, th_sum);
+            break;
+        case IP_PROTO_UDP:
+            pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
+            pkt->virt_hdr.csum_start = pkt->hdr_len;
+            pkt->virt_hdr.csum_offset = offsetof(struct udp_hdr, uh_sum);
+            break;
+        default:
+            break;
+        }
+    }
+}
+
+void vmxnet_tx_pkt_setup_vlan_header(struct VmxnetTxPkt *pkt, uint16_t vlan)
+{
+    bool is_new;
+    assert(pkt);
+
+    eth_setup_vlan_headers(pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base,
+        vlan, &is_new);
+
+    /* update l2hdrlen */
+    if (is_new) {
+        pkt->hdr_len += sizeof(struct vlan_header);
+        pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len +=
+            sizeof(struct vlan_header);
+    }
+}
+
+bool vmxnet_tx_pkt_add_raw_fragment(struct VmxnetTxPkt *pkt, hwaddr pa,
+    size_t len)
+{
+    hwaddr mapped_len = 0;
+    struct iovec *ventry;
+    assert(pkt);
+    assert(pkt->max_raw_frags > pkt->raw_frags);
+
+    if (!len) {
+        return true;
+     }
+
+    ventry = &pkt->raw[pkt->raw_frags];
+    mapped_len = len;
+
+    ventry->iov_base = cpu_physical_memory_map(pa, &mapped_len, false);
+    ventry->iov_len = mapped_len;
+    pkt->raw_frags += !!ventry->iov_base;
+
+    if ((ventry->iov_base == NULL) || (len != mapped_len)) {
+        return false;
+    }
+
+    return true;
+}
+
+eth_pkt_types_e vmxnet_tx_pkt_get_packet_type(struct VmxnetTxPkt *pkt)
+{
+    assert(pkt);
+
+    return pkt->packet_type;
+}
+
+size_t vmxnet_tx_pkt_get_total_len(struct VmxnetTxPkt *pkt)
+{
+    assert(pkt);
+
+    return pkt->hdr_len + pkt->payload_len;
+}
+
+void vmxnet_tx_pkt_dump(struct VmxnetTxPkt *pkt)
+{
+#ifdef VMXNET_TX_PKT_DEBUG
+    assert(pkt);
+
+    printf("TX PKT: hdr_len: %d, pkt_type: 0x%X, l2hdr_len: %lu, "
+        "l3hdr_len: %lu, payload_len: %u\n", pkt->hdr_len, pkt->packet_type,
+        pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len,
+        pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len, pkt->payload_len);
+#endif
+}
+
+void vmxnet_tx_pkt_reset(struct VmxnetTxPkt *pkt)
+{
+    int i;
+
+    /* no assert, as reset can be called before tx_pkt_init */
+    if (!pkt) {
+        return;
+    }
+
+    memset(&pkt->virt_hdr, 0, sizeof(pkt->virt_hdr));
+
+    g_free(pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base);
+    pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base = NULL;
+
+    assert(pkt->vec);
+    for (i = VMXNET_TX_PKT_L2HDR_FRAG;
+         i < pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG; i++) {
+        pkt->vec[i].iov_len = 0;
+    }
+    pkt->payload_len = 0;
+    pkt->payload_frags = 0;
+
+    assert(pkt->raw);
+    for (i = 0; i < pkt->raw_frags; i++) {
+        assert(pkt->raw[i].iov_base);
+        cpu_physical_memory_unmap(pkt->raw[i].iov_base, pkt->raw[i].iov_len,
+                                  false, pkt->raw[i].iov_len);
+        pkt->raw[i].iov_len = 0;
+    }
+    pkt->raw_frags = 0;
+
+    pkt->hdr_len = 0;
+    pkt->packet_type = 0;
+    pkt->l4proto = 0;
+}
+
+static void vmxnet_tx_pkt_do_sw_csum(struct VmxnetTxPkt *pkt)
+{
+    struct iovec *iov = &pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG];
+    uint32_t csum_cntr;
+    uint16_t csum = 0;
+    /* num of iovec without vhdr */
+    uint32_t iov_len = pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG - 1;
+    uint16_t csl;
+    struct ip_header *iphdr;
+    size_t csum_offset = pkt->virt_hdr.csum_start + pkt->virt_hdr.csum_offset;
+
+    /* Put zero to checksum field */
+    iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum);
+
+    /* Calculate L4 TCP/UDP checksum */
+    csl = pkt->payload_len;
+
+    /* data checksum */
+    csum_cntr =
+        net_checksum_add_iov(iov, iov_len, pkt->virt_hdr.csum_start, csl);
+    /* add pseudo header to csum */
+    iphdr = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base;
+    csum_cntr += eth_calc_pseudo_hdr_csum(iphdr, csl);
+
+    /* Put the checksum obtained into the packet */
+    csum = cpu_to_be16(net_checksum_finish(csum_cntr));
+    iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum);
+}
+
+enum {
+    VMXNET_TX_PKT_FRAGMENT_L2_HDR_POS = 0,
+    VMXNET_TX_PKT_FRAGMENT_L3_HDR_POS,
+    VMXNET_TX_PKT_FRAGMENT_HEADER_NUM
+};
+
+#define VMXNET_MAX_FRAG_SG_LIST (64)
+
+static size_t vmxnet_tx_pkt_fetch_fragment(struct VmxnetTxPkt *pkt,
+    int *src_idx, size_t *src_offset, struct iovec *dst, int *dst_idx)
+{
+    size_t fetched = 0;
+    struct iovec *src = pkt->vec;
+
+    *dst_idx = VMXNET_TX_PKT_FRAGMENT_HEADER_NUM;
+
+    while (fetched < pkt->virt_hdr.gso_size) {
+
+        /* no more place in fragment iov */
+        if (*dst_idx == VMXNET_MAX_FRAG_SG_LIST) {
+            break;
+        }
+
+        /* no more data in iovec */
+        if (*src_idx == (pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG)) {
+            break;
+        }
+
+
+        dst[*dst_idx].iov_base = src[*src_idx].iov_base + *src_offset;
+        dst[*dst_idx].iov_len = MIN(src[*src_idx].iov_len - *src_offset,
+            pkt->virt_hdr.gso_size - fetched);
+
+        *src_offset += dst[*dst_idx].iov_len;
+        fetched += dst[*dst_idx].iov_len;
+
+        if (*src_offset == src[*src_idx].iov_len) {
+            *src_offset = 0;
+            (*src_idx)++;
+        }
+
+        (*dst_idx)++;
+    }
+
+    return fetched;
+}
+
+static bool vmxnet_tx_pkt_do_sw_fragmentation(struct VmxnetTxPkt *pkt,
+    NetClientState *nc)
+{
+    struct iovec fragment[VMXNET_MAX_FRAG_SG_LIST];
+    size_t fragment_len = 0;
+    bool more_frags = false;
+
+    /* some pointers for shorter code */
+    void *l2_iov_base, *l3_iov_base;
+    size_t l2_iov_len, l3_iov_len;
+    int src_idx =  VMXNET_TX_PKT_PL_START_FRAG, dst_idx;
+    size_t src_offset = 0;
+    size_t fragment_offset = 0;
+
+    l2_iov_base = pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base;
+    l2_iov_len = pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len;
+    l3_iov_base = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base;
+    l3_iov_len = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len;
+
+    /* Copy headers */
+    fragment[VMXNET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_base = l2_iov_base;
+    fragment[VMXNET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_len = l2_iov_len;
+    fragment[VMXNET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_base = l3_iov_base;
+    fragment[VMXNET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_len = l3_iov_len;
+
+
+    /* Put as much data as possible and send */
+    do {
+        fragment_len = vmxnet_tx_pkt_fetch_fragment(pkt, &src_idx, &src_offset,
+            fragment, &dst_idx);
+
+        more_frags = (fragment_offset + fragment_len < pkt->payload_len);
+
+        eth_setup_ip4_fragmentation(l2_iov_base, l2_iov_len, l3_iov_base,
+            l3_iov_len, fragment_len, fragment_offset, more_frags);
+
+        eth_fix_ip4_checksum(l3_iov_base, l3_iov_len);
+
+        qemu_sendv_packet(nc, fragment, dst_idx);
+
+        fragment_offset += fragment_len;
+
+    } while (more_frags);
+
+    return true;
+}
+
+bool vmxnet_tx_pkt_send(struct VmxnetTxPkt *pkt, NetClientState *nc)
+{
+    assert(pkt);
+
+    if (!pkt->has_virt_hdr &&
+        pkt->virt_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
+        vmxnet_tx_pkt_do_sw_csum(pkt);
+    }
+
+    /*
+     * Since underlying infrastructure does not support IP datagrams longer
+     * than 64K we should drop such packets and don't even try to send
+     */
+    if (VIRTIO_NET_HDR_GSO_NONE != pkt->virt_hdr.gso_type) {
+        if (pkt->payload_len >
+            ETH_MAX_IP_DGRAM_LEN -
+            pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len) {
+            return false;
+        }
+    }
+
+    if (pkt->has_virt_hdr ||
+        pkt->virt_hdr.gso_type == VIRTIO_NET_HDR_GSO_NONE) {
+        qemu_sendv_packet(nc, pkt->vec,
+            pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG);
+        return true;
+    }
+
+    return vmxnet_tx_pkt_do_sw_fragmentation(pkt, nc);
+}
diff --git a/hw/net/vmxnet_tx_pkt.h b/hw/net/vmxnet_tx_pkt.h
new file mode 100644 (file)
index 0000000..57121a6
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * QEMU VMWARE VMXNET* paravirtual NICs - TX packets abstraction
+ *
+ * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
+ *
+ * Developed by Daynix Computing LTD (http://www.daynix.com)
+ *
+ * Authors:
+ * Dmitry Fleytman <dmitry@daynix.com>
+ * Tamir Shomer <tamirs@daynix.com>
+ * Yan Vugenfirer <yan@daynix.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef VMXNET_TX_PKT_H
+#define VMXNET_TX_PKT_H
+
+#include "stdint.h"
+#include "stdbool.h"
+#include "net/eth.h"
+#include "exec/hwaddr.h"
+
+/* define to enable packet dump functions */
+/*#define VMXNET_TX_PKT_DEBUG*/
+
+struct VmxnetTxPkt;
+
+/**
+ * Init function for tx packet functionality
+ *
+ * @pkt:            packet pointer
+ * @max_frags:      max tx ip fragments
+ * @has_virt_hdr:   device uses virtio header.
+ */
+void vmxnet_tx_pkt_init(struct VmxnetTxPkt **pkt, uint32_t max_frags,
+    bool has_virt_hdr);
+
+/**
+ * Clean all tx packet resources.
+ *
+ * @pkt:            packet.
+ */
+void vmxnet_tx_pkt_uninit(struct VmxnetTxPkt *pkt);
+
+/**
+ * get virtio header
+ *
+ * @pkt:            packet
+ * @ret:            virtio header
+ */
+struct virtio_net_hdr *vmxnet_tx_pkt_get_vhdr(struct VmxnetTxPkt *pkt);
+
+/**
+ * build virtio header (will be stored in module context)
+ *
+ * @pkt:            packet
+ * @tso_enable:     TSO enabled
+ * @csum_enable:    CSO enabled
+ * @gso_size:       MSS size for TSO
+ *
+ */
+void vmxnet_tx_pkt_build_vheader(struct VmxnetTxPkt *pkt, bool tso_enable,
+    bool csum_enable, uint32_t gso_size);
+
+/**
+ * updates vlan tag, and adds vlan header in case it is missing
+ *
+ * @pkt:            packet
+ * @vlan:           VLAN tag
+ *
+ */
+void vmxnet_tx_pkt_setup_vlan_header(struct VmxnetTxPkt *pkt, uint16_t vlan);
+
+/**
+ * populate data fragment into pkt context.
+ *
+ * @pkt:            packet
+ * @pa:             physical address of fragment
+ * @len:            length of fragment
+ *
+ */
+bool vmxnet_tx_pkt_add_raw_fragment(struct VmxnetTxPkt *pkt, hwaddr pa,
+    size_t len);
+
+/**
+ * fix ip header fields and calculate checksums needed.
+ *
+ * @pkt:            packet
+ *
+ */
+void vmxnet_tx_pkt_update_ip_checksums(struct VmxnetTxPkt *pkt);
+
+/**
+ * get length of all populated data.
+ *
+ * @pkt:            packet
+ * @ret:            total data length
+ *
+ */
+size_t vmxnet_tx_pkt_get_total_len(struct VmxnetTxPkt *pkt);
+
+/**
+ * get packet type
+ *
+ * @pkt:            packet
+ * @ret:            packet type
+ *
+ */
+eth_pkt_types_e vmxnet_tx_pkt_get_packet_type(struct VmxnetTxPkt *pkt);
+
+/**
+ * prints packet data if debug is enabled
+ *
+ * @pkt:            packet
+ *
+ */
+void vmxnet_tx_pkt_dump(struct VmxnetTxPkt *pkt);
+
+/**
+ * reset tx packet private context (needed to be called between packets)
+ *
+ * @pkt:            packet
+ *
+ */
+void vmxnet_tx_pkt_reset(struct VmxnetTxPkt *pkt);
+
+/**
+ * Send packet to qemu. handles sw offloads if vhdr is not supported.
+ *
+ * @pkt:            packet
+ * @nc:             NetClientState
+ * @ret:            operation result
+ *
+ */
+bool vmxnet_tx_pkt_send(struct VmxnetTxPkt *pkt, NetClientState *nc);
+
+/**
+ * parse raw packet data and analyze offload requirements.
+ *
+ * @pkt:            packet
+ *
+ */
+bool vmxnet_tx_pkt_parse(struct VmxnetTxPkt *pkt);
+
+#endif
diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c
new file mode 100644 (file)
index 0000000..63918ae
--- /dev/null
@@ -0,0 +1,439 @@
+/*
+ *  xen paravirt network card backend
+ *
+ *  (c) Gerd Hoffmann <kraxel@redhat.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; under version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *  Contributions after 2012-01-13 are licensed under the terms of the
+ *  GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <inttypes.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include "hw/hw.h"
+#include "net/net.h"
+#include "net/checksum.h"
+#include "net/util.h"
+#include "hw/xen/xen_backend.h"
+
+#include <xen/io/netif.h>
+
+/* ------------------------------------------------------------- */
+
+struct XenNetDev {
+    struct XenDevice      xendev;  /* must be first */
+    char                  *mac;
+    int                   tx_work;
+    int                   tx_ring_ref;
+    int                   rx_ring_ref;
+    struct netif_tx_sring *txs;
+    struct netif_rx_sring *rxs;
+    netif_tx_back_ring_t  tx_ring;
+    netif_rx_back_ring_t  rx_ring;
+    NICConf               conf;
+    NICState              *nic;
+};
+
+/* ------------------------------------------------------------- */
+
+static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, int8_t st)
+{
+    RING_IDX i = netdev->tx_ring.rsp_prod_pvt;
+    netif_tx_response_t *resp;
+    int notify;
+
+    resp = RING_GET_RESPONSE(&netdev->tx_ring, i);
+    resp->id     = txp->id;
+    resp->status = st;
+
+#if 0
+    if (txp->flags & NETTXF_extra_info) {
+        RING_GET_RESPONSE(&netdev->tx_ring, ++i)->status = NETIF_RSP_NULL;
+    }
+#endif
+
+    netdev->tx_ring.rsp_prod_pvt = ++i;
+    RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify);
+    if (notify) {
+        xen_be_send_notify(&netdev->xendev);
+    }
+
+    if (i == netdev->tx_ring.req_cons) {
+        int more_to_do;
+        RING_FINAL_CHECK_FOR_REQUESTS(&netdev->tx_ring, more_to_do);
+        if (more_to_do) {
+            netdev->tx_work++;
+        }
+    }
+}
+
+static void net_tx_error(struct XenNetDev *netdev, netif_tx_request_t *txp, RING_IDX end)
+{
+#if 0
+    /*
+     * Hmm, why netback fails everything in the ring?
+     * Should we do that even when not supporting SG and TSO?
+     */
+    RING_IDX cons = netdev->tx_ring.req_cons;
+
+    do {
+        make_tx_response(netif, txp, NETIF_RSP_ERROR);
+        if (cons >= end) {
+            break;
+        }
+        txp = RING_GET_REQUEST(&netdev->tx_ring, cons++);
+    } while (1);
+    netdev->tx_ring.req_cons = cons;
+    netif_schedule_work(netif);
+    netif_put(netif);
+#else
+    net_tx_response(netdev, txp, NETIF_RSP_ERROR);
+#endif
+}
+
+static void net_tx_packets(struct XenNetDev *netdev)
+{
+    netif_tx_request_t txreq;
+    RING_IDX rc, rp;
+    void *page;
+    void *tmpbuf = NULL;
+
+    for (;;) {
+        rc = netdev->tx_ring.req_cons;
+        rp = netdev->tx_ring.sring->req_prod;
+        xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
+
+        while ((rc != rp)) {
+            if (RING_REQUEST_CONS_OVERFLOW(&netdev->tx_ring, rc)) {
+                break;
+            }
+            memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq));
+            netdev->tx_ring.req_cons = ++rc;
+
+#if 1
+            /* should not happen in theory, we don't announce the *
+             * feature-{sg,gso,whatelse} flags in xenstore (yet?) */
+            if (txreq.flags & NETTXF_extra_info) {
+                xen_be_printf(&netdev->xendev, 0, "FIXME: extra info flag\n");
+                net_tx_error(netdev, &txreq, rc);
+                continue;
+            }
+            if (txreq.flags & NETTXF_more_data) {
+                xen_be_printf(&netdev->xendev, 0, "FIXME: more data flag\n");
+                net_tx_error(netdev, &txreq, rc);
+                continue;
+            }
+#endif
+
+            if (txreq.size < 14) {
+                xen_be_printf(&netdev->xendev, 0, "bad packet size: %d\n", txreq.size);
+                net_tx_error(netdev, &txreq, rc);
+                continue;
+            }
+
+            if ((txreq.offset + txreq.size) > XC_PAGE_SIZE) {
+                xen_be_printf(&netdev->xendev, 0, "error: page crossing\n");
+                net_tx_error(netdev, &txreq, rc);
+                continue;
+            }
+
+            xen_be_printf(&netdev->xendev, 3, "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n",
+                          txreq.gref, txreq.offset, txreq.size, txreq.flags,
+                          (txreq.flags & NETTXF_csum_blank)     ? " csum_blank"     : "",
+                          (txreq.flags & NETTXF_data_validated) ? " data_validated" : "",
+                          (txreq.flags & NETTXF_more_data)      ? " more_data"      : "",
+                          (txreq.flags & NETTXF_extra_info)     ? " extra_info"     : "");
+
+            page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev,
+                                           netdev->xendev.dom,
+                                           txreq.gref, PROT_READ);
+            if (page == NULL) {
+                xen_be_printf(&netdev->xendev, 0, "error: tx gref dereference failed (%d)\n",
+                              txreq.gref);
+                net_tx_error(netdev, &txreq, rc);
+                continue;
+            }
+            if (txreq.flags & NETTXF_csum_blank) {
+                /* have read-only mapping -> can't fill checksum in-place */
+                if (!tmpbuf) {
+                    tmpbuf = g_malloc(XC_PAGE_SIZE);
+                }
+                memcpy(tmpbuf, page + txreq.offset, txreq.size);
+                net_checksum_calculate(tmpbuf, txreq.size);
+                qemu_send_packet(qemu_get_queue(netdev->nic), tmpbuf,
+                                 txreq.size);
+            } else {
+                qemu_send_packet(qemu_get_queue(netdev->nic),
+                                 page + txreq.offset, txreq.size);
+            }
+            xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1);
+            net_tx_response(netdev, &txreq, NETIF_RSP_OKAY);
+        }
+        if (!netdev->tx_work) {
+            break;
+        }
+        netdev->tx_work = 0;
+    }
+    g_free(tmpbuf);
+}
+
+/* ------------------------------------------------------------- */
+
+static void net_rx_response(struct XenNetDev *netdev,
+                            netif_rx_request_t *req, int8_t st,
+                            uint16_t offset, uint16_t size,
+                            uint16_t flags)
+{
+    RING_IDX i = netdev->rx_ring.rsp_prod_pvt;
+    netif_rx_response_t *resp;
+    int notify;
+
+    resp = RING_GET_RESPONSE(&netdev->rx_ring, i);
+    resp->offset     = offset;
+    resp->flags      = flags;
+    resp->id         = req->id;
+    resp->status     = (int16_t)size;
+    if (st < 0) {
+        resp->status = (int16_t)st;
+    }
+
+    xen_be_printf(&netdev->xendev, 3, "rx response: idx %d, status %d, flags 0x%x\n",
+                  i, resp->status, resp->flags);
+
+    netdev->rx_ring.rsp_prod_pvt = ++i;
+    RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify);
+    if (notify) {
+        xen_be_send_notify(&netdev->xendev);
+    }
+}
+
+#define NET_IP_ALIGN 2
+
+static int net_rx_ok(NetClientState *nc)
+{
+    struct XenNetDev *netdev = qemu_get_nic_opaque(nc);
+    RING_IDX rc, rp;
+
+    if (netdev->xendev.be_state != XenbusStateConnected) {
+        return 0;
+    }
+
+    rc = netdev->rx_ring.req_cons;
+    rp = netdev->rx_ring.sring->req_prod;
+    xen_rmb();
+
+    if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) {
+        xen_be_printf(&netdev->xendev, 2, "%s: no rx buffers (%d/%d)\n",
+                      __FUNCTION__, rc, rp);
+        return 0;
+    }
+    return 1;
+}
+
+static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+    struct XenNetDev *netdev = qemu_get_nic_opaque(nc);
+    netif_rx_request_t rxreq;
+    RING_IDX rc, rp;
+    void *page;
+
+    if (netdev->xendev.be_state != XenbusStateConnected) {
+        return -1;
+    }
+
+    rc = netdev->rx_ring.req_cons;
+    rp = netdev->rx_ring.sring->req_prod;
+    xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
+
+    if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) {
+        xen_be_printf(&netdev->xendev, 2, "no buffer, drop packet\n");
+        return -1;
+    }
+    if (size > XC_PAGE_SIZE - NET_IP_ALIGN) {
+        xen_be_printf(&netdev->xendev, 0, "packet too big (%lu > %ld)",
+                      (unsigned long)size, XC_PAGE_SIZE - NET_IP_ALIGN);
+        return -1;
+    }
+
+    memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq));
+    netdev->rx_ring.req_cons = ++rc;
+
+    page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev,
+                                   netdev->xendev.dom,
+                                   rxreq.gref, PROT_WRITE);
+    if (page == NULL) {
+        xen_be_printf(&netdev->xendev, 0, "error: rx gref dereference failed (%d)\n",
+                      rxreq.gref);
+        net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0);
+        return -1;
+    }
+    memcpy(page + NET_IP_ALIGN, buf, size);
+    xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1);
+    net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0);
+
+    return size;
+}
+
+/* ------------------------------------------------------------- */
+
+static NetClientInfo net_xen_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = net_rx_ok,
+    .receive = net_rx_packet,
+};
+
+static int net_init(struct XenDevice *xendev)
+{
+    struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
+
+    /* read xenstore entries */
+    if (netdev->mac == NULL) {
+        netdev->mac = xenstore_read_be_str(&netdev->xendev, "mac");
+    }
+
+    /* do we have all we need? */
+    if (netdev->mac == NULL) {
+        return -1;
+    }
+
+    if (net_parse_macaddr(netdev->conf.macaddr.a, netdev->mac) < 0) {
+        return -1;
+    }
+
+    netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf,
+                               "xen", NULL, netdev);
+
+    snprintf(qemu_get_queue(netdev->nic)->info_str,
+             sizeof(qemu_get_queue(netdev->nic)->info_str),
+             "nic: xenbus vif macaddr=%s", netdev->mac);
+
+    /* fill info */
+    xenstore_write_be_int(&netdev->xendev, "feature-rx-copy", 1);
+    xenstore_write_be_int(&netdev->xendev, "feature-rx-flip", 0);
+
+    return 0;
+}
+
+static int net_connect(struct XenDevice *xendev)
+{
+    struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
+    int rx_copy;
+
+    if (xenstore_read_fe_int(&netdev->xendev, "tx-ring-ref",
+                             &netdev->tx_ring_ref) == -1) {
+        return -1;
+    }
+    if (xenstore_read_fe_int(&netdev->xendev, "rx-ring-ref",
+                             &netdev->rx_ring_ref) == -1) {
+        return 1;
+    }
+    if (xenstore_read_fe_int(&netdev->xendev, "event-channel",
+                             &netdev->xendev.remote_port) == -1) {
+        return -1;
+    }
+
+    if (xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy) == -1) {
+        rx_copy = 0;
+    }
+    if (rx_copy == 0) {
+        xen_be_printf(&netdev->xendev, 0, "frontend doesn't support rx-copy.\n");
+        return -1;
+    }
+
+    netdev->txs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev,
+                                          netdev->xendev.dom,
+                                          netdev->tx_ring_ref,
+                                          PROT_READ | PROT_WRITE);
+    netdev->rxs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev,
+                                          netdev->xendev.dom,
+                                          netdev->rx_ring_ref,
+                                          PROT_READ | PROT_WRITE);
+    if (!netdev->txs || !netdev->rxs) {
+        return -1;
+    }
+    BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XC_PAGE_SIZE);
+    BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XC_PAGE_SIZE);
+
+    xen_be_bind_evtchn(&netdev->xendev);
+
+    xen_be_printf(&netdev->xendev, 1, "ok: tx-ring-ref %d, rx-ring-ref %d, "
+                  "remote port %d, local port %d\n",
+                  netdev->tx_ring_ref, netdev->rx_ring_ref,
+                  netdev->xendev.remote_port, netdev->xendev.local_port);
+
+    net_tx_packets(netdev);
+    return 0;
+}
+
+static void net_disconnect(struct XenDevice *xendev)
+{
+    struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
+
+    xen_be_unbind_evtchn(&netdev->xendev);
+
+    if (netdev->txs) {
+        xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1);
+        netdev->txs = NULL;
+    }
+    if (netdev->rxs) {
+        xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1);
+        netdev->rxs = NULL;
+    }
+    if (netdev->nic) {
+        qemu_del_nic(netdev->nic);
+        netdev->nic = NULL;
+    }
+}
+
+static void net_event(struct XenDevice *xendev)
+{
+    struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
+    net_tx_packets(netdev);
+    qemu_flush_queued_packets(qemu_get_queue(netdev->nic));
+}
+
+static int net_free(struct XenDevice *xendev)
+{
+    struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
+
+    g_free(netdev->mac);
+    return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+struct XenDevOps xen_netdev_ops = {
+    .size       = sizeof(struct XenNetDev),
+    .flags      = DEVOPS_FLAG_NEED_GNTDEV,
+    .init       = net_init,
+    .initialise    = net_connect,
+    .event      = net_event,
+    .disconnect = net_disconnect,
+    .free       = net_free,
+};
diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c
new file mode 100644 (file)
index 0000000..5275f48
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * QEMU model of XGMAC Ethernet.
+ *
+ * derived from the Xilinx AXI-Ethernet by Edgar E. Iglesias.
+ *
+ * Copyright (c) 2011 Calxeda, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/sysbus.h"
+#include "char/char.h"
+#include "qemu/log.h"
+#include "net/net.h"
+#include "net/checksum.h"
+
+#ifdef DEBUG_XGMAC
+#define DEBUGF_BRK(message, args...) do { \
+                                         fprintf(stderr, (message), ## args); \
+                                     } while (0)
+#else
+#define DEBUGF_BRK(message, args...) do { } while (0)
+#endif
+
+#define XGMAC_CONTROL           0x00000000   /* MAC Configuration */
+#define XGMAC_FRAME_FILTER      0x00000001   /* MAC Frame Filter */
+#define XGMAC_FLOW_CTRL         0x00000006   /* MAC Flow Control */
+#define XGMAC_VLAN_TAG          0x00000007   /* VLAN Tags */
+#define XGMAC_VERSION           0x00000008   /* Version */
+/* VLAN tag for insertion or replacement into tx frames */
+#define XGMAC_VLAN_INCL         0x00000009
+#define XGMAC_LPI_CTRL          0x0000000a   /* LPI Control and Status */
+#define XGMAC_LPI_TIMER         0x0000000b   /* LPI Timers Control */
+#define XGMAC_TX_PACE           0x0000000c   /* Transmit Pace and Stretch */
+#define XGMAC_VLAN_HASH         0x0000000d   /* VLAN Hash Table */
+#define XGMAC_DEBUG             0x0000000e   /* Debug */
+#define XGMAC_INT_STATUS        0x0000000f   /* Interrupt and Control */
+/* HASH table registers */
+#define XGMAC_HASH(n)           ((0x00000300/4) + (n))
+#define XGMAC_NUM_HASH          16
+/* Operation Mode */
+#define XGMAC_OPMODE            (0x00000400/4)
+/* Remote Wake-Up Frame Filter */
+#define XGMAC_REMOTE_WAKE       (0x00000700/4)
+/* PMT Control and Status */
+#define XGMAC_PMT               (0x00000704/4)
+
+#define XGMAC_ADDR_HIGH(reg)    (0x00000010+((reg) * 2))
+#define XGMAC_ADDR_LOW(reg)     (0x00000011+((reg) * 2))
+
+#define DMA_BUS_MODE            0x000003c0   /* Bus Mode */
+#define DMA_XMT_POLL_DEMAND     0x000003c1   /* Transmit Poll Demand */
+#define DMA_RCV_POLL_DEMAND     0x000003c2   /* Received Poll Demand */
+#define DMA_RCV_BASE_ADDR       0x000003c3   /* Receive List Base */
+#define DMA_TX_BASE_ADDR        0x000003c4   /* Transmit List Base */
+#define DMA_STATUS              0x000003c5   /* Status Register */
+#define DMA_CONTROL             0x000003c6   /* Ctrl (Operational Mode) */
+#define DMA_INTR_ENA            0x000003c7   /* Interrupt Enable */
+#define DMA_MISSED_FRAME_CTR    0x000003c8   /* Missed Frame Counter */
+/* Receive Interrupt Watchdog Timer */
+#define DMA_RI_WATCHDOG_TIMER   0x000003c9
+#define DMA_AXI_BUS             0x000003ca   /* AXI Bus Mode */
+#define DMA_AXI_STATUS          0x000003cb   /* AXI Status */
+#define DMA_CUR_TX_DESC_ADDR    0x000003d2   /* Current Host Tx Descriptor */
+#define DMA_CUR_RX_DESC_ADDR    0x000003d3   /* Current Host Rx Descriptor */
+#define DMA_CUR_TX_BUF_ADDR     0x000003d4   /* Current Host Tx Buffer */
+#define DMA_CUR_RX_BUF_ADDR     0x000003d5   /* Current Host Rx Buffer */
+#define DMA_HW_FEATURE          0x000003d6   /* Enabled Hardware Features */
+
+/* DMA Status register defines */
+#define DMA_STATUS_GMI          0x08000000   /* MMC interrupt */
+#define DMA_STATUS_GLI          0x04000000   /* GMAC Line interface int */
+#define DMA_STATUS_EB_MASK      0x00380000   /* Error Bits Mask */
+#define DMA_STATUS_EB_TX_ABORT  0x00080000   /* Error Bits - TX Abort */
+#define DMA_STATUS_EB_RX_ABORT  0x00100000   /* Error Bits - RX Abort */
+#define DMA_STATUS_TS_MASK      0x00700000   /* Transmit Process State */
+#define DMA_STATUS_TS_SHIFT     20
+#define DMA_STATUS_RS_MASK      0x000e0000   /* Receive Process State */
+#define DMA_STATUS_RS_SHIFT     17
+#define DMA_STATUS_NIS          0x00010000   /* Normal Interrupt Summary */
+#define DMA_STATUS_AIS          0x00008000   /* Abnormal Interrupt Summary */
+#define DMA_STATUS_ERI          0x00004000   /* Early Receive Interrupt */
+#define DMA_STATUS_FBI          0x00002000   /* Fatal Bus Error Interrupt */
+#define DMA_STATUS_ETI          0x00000400   /* Early Transmit Interrupt */
+#define DMA_STATUS_RWT          0x00000200   /* Receive Watchdog Timeout */
+#define DMA_STATUS_RPS          0x00000100   /* Receive Process Stopped */
+#define DMA_STATUS_RU           0x00000080   /* Receive Buffer Unavailable */
+#define DMA_STATUS_RI           0x00000040   /* Receive Interrupt */
+#define DMA_STATUS_UNF          0x00000020   /* Transmit Underflow */
+#define DMA_STATUS_OVF          0x00000010   /* Receive Overflow */
+#define DMA_STATUS_TJT          0x00000008   /* Transmit Jabber Timeout */
+#define DMA_STATUS_TU           0x00000004   /* Transmit Buffer Unavailable */
+#define DMA_STATUS_TPS          0x00000002   /* Transmit Process Stopped */
+#define DMA_STATUS_TI           0x00000001   /* Transmit Interrupt */
+
+/* DMA Control register defines */
+#define DMA_CONTROL_ST          0x00002000   /* Start/Stop Transmission */
+#define DMA_CONTROL_SR          0x00000002   /* Start/Stop Receive */
+#define DMA_CONTROL_DFF         0x01000000   /* Disable flush of rx frames */
+
+struct desc {
+    uint32_t ctl_stat;
+    uint16_t buffer1_size;
+    uint16_t buffer2_size;
+    uint32_t buffer1_addr;
+    uint32_t buffer2_addr;
+    uint32_t ext_stat;
+    uint32_t res[3];
+};
+
+#define R_MAX 0x400
+
+typedef struct RxTxStats {
+    uint64_t rx_bytes;
+    uint64_t tx_bytes;
+
+    uint64_t rx;
+    uint64_t rx_bcast;
+    uint64_t rx_mcast;
+} RxTxStats;
+
+typedef struct XgmacState {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    qemu_irq sbd_irq;
+    qemu_irq pmt_irq;
+    qemu_irq mci_irq;
+    NICState *nic;
+    NICConf conf;
+
+    struct RxTxStats stats;
+    uint32_t regs[R_MAX];
+} XgmacState;
+
+const VMStateDescription vmstate_rxtx_stats = {
+    .name = "xgmac_stats",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT64(rx_bytes, RxTxStats),
+        VMSTATE_UINT64(tx_bytes, RxTxStats),
+        VMSTATE_UINT64(rx, RxTxStats),
+        VMSTATE_UINT64(rx_bcast, RxTxStats),
+        VMSTATE_UINT64(rx_mcast, RxTxStats),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_xgmac = {
+    .name = "xgmac",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT(stats, XgmacState, 0, vmstate_rxtx_stats, RxTxStats),
+        VMSTATE_UINT32_ARRAY(regs, XgmacState, R_MAX),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void xgmac_read_desc(struct XgmacState *s, struct desc *d, int rx)
+{
+    uint32_t addr = rx ? s->regs[DMA_CUR_RX_DESC_ADDR] :
+        s->regs[DMA_CUR_TX_DESC_ADDR];
+    cpu_physical_memory_read(addr, d, sizeof(*d));
+}
+
+static void xgmac_write_desc(struct XgmacState *s, struct desc *d, int rx)
+{
+    int reg = rx ? DMA_CUR_RX_DESC_ADDR : DMA_CUR_TX_DESC_ADDR;
+    uint32_t addr = s->regs[reg];
+
+    if (!rx && (d->ctl_stat & 0x00200000)) {
+        s->regs[reg] = s->regs[DMA_TX_BASE_ADDR];
+    } else if (rx && (d->buffer1_size & 0x8000)) {
+        s->regs[reg] = s->regs[DMA_RCV_BASE_ADDR];
+    } else {
+        s->regs[reg] += sizeof(*d);
+    }
+    cpu_physical_memory_write(addr, d, sizeof(*d));
+}
+
+static void xgmac_enet_send(struct XgmacState *s)
+{
+    struct desc bd;
+    int frame_size;
+    int len;
+    uint8_t frame[8192];
+    uint8_t *ptr;
+
+    ptr = frame;
+    frame_size = 0;
+    while (1) {
+        xgmac_read_desc(s, &bd, 0);
+        if ((bd.ctl_stat & 0x80000000) == 0) {
+            /* Run out of descriptors to transmit.  */
+            break;
+        }
+        len = (bd.buffer1_size & 0xfff) + (bd.buffer2_size & 0xfff);
+
+        if ((bd.buffer1_size & 0xfff) > 2048) {
+            DEBUGF_BRK("qemu:%s:ERROR...ERROR...ERROR... -- "
+                        "xgmac buffer 1 len on send > 2048 (0x%x)\n",
+                         __func__, bd.buffer1_size & 0xfff);
+        }
+        if ((bd.buffer2_size & 0xfff) != 0) {
+            DEBUGF_BRK("qemu:%s:ERROR...ERROR...ERROR... -- "
+                        "xgmac buffer 2 len on send != 0 (0x%x)\n",
+                        __func__, bd.buffer2_size & 0xfff);
+        }
+        if (len >= sizeof(frame)) {
+            DEBUGF_BRK("qemu:%s: buffer overflow %d read into %zu "
+                        "buffer\n" , __func__, len, sizeof(frame));
+            DEBUGF_BRK("qemu:%s: buffer1.size=%d; buffer2.size=%d\n",
+                        __func__, bd.buffer1_size, bd.buffer2_size);
+        }
+
+        cpu_physical_memory_read(bd.buffer1_addr, ptr, len);
+        ptr += len;
+        frame_size += len;
+        if (bd.ctl_stat & 0x20000000) {
+            /* Last buffer in frame.  */
+            qemu_send_packet(qemu_get_queue(s->nic), frame, len);
+            ptr = frame;
+            frame_size = 0;
+            s->regs[DMA_STATUS] |= DMA_STATUS_TI | DMA_STATUS_NIS;
+        }
+        bd.ctl_stat &= ~0x80000000;
+        /* Write back the modified descriptor.  */
+        xgmac_write_desc(s, &bd, 0);
+    }
+}
+
+static void enet_update_irq(struct XgmacState *s)
+{
+    int stat = s->regs[DMA_STATUS] & s->regs[DMA_INTR_ENA];
+    qemu_set_irq(s->sbd_irq, !!stat);
+}
+
+static uint64_t enet_read(void *opaque, hwaddr addr, unsigned size)
+{
+    struct XgmacState *s = opaque;
+    uint64_t r = 0;
+    addr >>= 2;
+
+    switch (addr) {
+    case XGMAC_VERSION:
+        r = 0x1012;
+        break;
+    default:
+        if (addr < ARRAY_SIZE(s->regs)) {
+            r = s->regs[addr];
+        }
+        break;
+    }
+    return r;
+}
+
+static void enet_write(void *opaque, hwaddr addr,
+                       uint64_t value, unsigned size)
+{
+    struct XgmacState *s = opaque;
+
+    addr >>= 2;
+    switch (addr) {
+    case DMA_BUS_MODE:
+        s->regs[DMA_BUS_MODE] = value & ~0x1;
+        break;
+    case DMA_XMT_POLL_DEMAND:
+        xgmac_enet_send(s);
+        break;
+    case DMA_STATUS:
+        s->regs[DMA_STATUS] = s->regs[DMA_STATUS] & ~value;
+        break;
+    case DMA_RCV_BASE_ADDR:
+        s->regs[DMA_RCV_BASE_ADDR] = s->regs[DMA_CUR_RX_DESC_ADDR] = value;
+        break;
+    case DMA_TX_BASE_ADDR:
+        s->regs[DMA_TX_BASE_ADDR] = s->regs[DMA_CUR_TX_DESC_ADDR] = value;
+        break;
+    default:
+        if (addr < ARRAY_SIZE(s->regs)) {
+            s->regs[addr] = value;
+        }
+        break;
+    }
+    enet_update_irq(s);
+}
+
+static const MemoryRegionOps enet_mem_ops = {
+    .read = enet_read,
+    .write = enet_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static int eth_can_rx(NetClientState *nc)
+{
+    struct XgmacState *s = qemu_get_nic_opaque(nc);
+
+    /* RX enabled?  */
+    return s->regs[DMA_CONTROL] & DMA_CONTROL_SR;
+}
+
+static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+    struct XgmacState *s = qemu_get_nic_opaque(nc);
+    static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff,
+                                              0xff, 0xff, 0xff};
+    int unicast, broadcast, multicast;
+    struct desc bd;
+    ssize_t ret;
+
+    unicast = ~buf[0] & 0x1;
+    broadcast = memcmp(buf, sa_bcast, 6) == 0;
+    multicast = !unicast && !broadcast;
+    if (size < 12) {
+        s->regs[DMA_STATUS] |= DMA_STATUS_RI | DMA_STATUS_NIS;
+        ret = -1;
+        goto out;
+    }
+
+    xgmac_read_desc(s, &bd, 1);
+    if ((bd.ctl_stat & 0x80000000) == 0) {
+        s->regs[DMA_STATUS] |= DMA_STATUS_RU | DMA_STATUS_AIS;
+        ret = size;
+        goto out;
+    }
+
+    cpu_physical_memory_write(bd.buffer1_addr, buf, size);
+
+    /* Add in the 4 bytes for crc (the real hw returns length incl crc) */
+    size += 4;
+    bd.ctl_stat = (size << 16) | 0x300;
+    xgmac_write_desc(s, &bd, 1);
+
+    s->stats.rx_bytes += size;
+    s->stats.rx++;
+    if (multicast) {
+        s->stats.rx_mcast++;
+    } else if (broadcast) {
+        s->stats.rx_bcast++;
+    }
+
+    s->regs[DMA_STATUS] |= DMA_STATUS_RI | DMA_STATUS_NIS;
+    ret = size;
+
+out:
+    enet_update_irq(s);
+    return ret;
+}
+
+static void eth_cleanup(NetClientState *nc)
+{
+    struct XgmacState *s = qemu_get_nic_opaque(nc);
+    s->nic = NULL;
+}
+
+static NetClientInfo net_xgmac_enet_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = eth_can_rx,
+    .receive = eth_rx,
+    .cleanup = eth_cleanup,
+};
+
+static int xgmac_enet_init(SysBusDevice *dev)
+{
+    struct XgmacState *s = FROM_SYSBUS(typeof(*s), dev);
+
+    memory_region_init_io(&s->iomem, &enet_mem_ops, s, "xgmac", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->sbd_irq);
+    sysbus_init_irq(dev, &s->pmt_irq);
+    sysbus_init_irq(dev, &s->mci_irq);
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+    s->nic = qemu_new_nic(&net_xgmac_enet_info, &s->conf,
+                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+
+    s->regs[XGMAC_ADDR_HIGH(0)] = (s->conf.macaddr.a[5] << 8) |
+                                   s->conf.macaddr.a[4];
+    s->regs[XGMAC_ADDR_LOW(0)] = (s->conf.macaddr.a[3] << 24) |
+                                 (s->conf.macaddr.a[2] << 16) |
+                                 (s->conf.macaddr.a[1] << 8) |
+                                  s->conf.macaddr.a[0];
+
+    return 0;
+}
+
+static Property xgmac_properties[] = {
+    DEFINE_NIC_PROPERTIES(struct XgmacState, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xgmac_enet_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    sbc->init = xgmac_enet_init;
+    dc->vmsd = &vmstate_xgmac;
+    dc->props = xgmac_properties;
+}
+
+static const TypeInfo xgmac_enet_info = {
+    .name          = "xgmac",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(struct XgmacState),
+    .class_init    = xgmac_enet_class_init,
+};
+
+static void xgmac_enet_register_types(void)
+{
+    type_register_static(&xgmac_enet_info);
+}
+
+type_init(xgmac_enet_register_types)
diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
new file mode 100644 (file)
index 0000000..07c4bad
--- /dev/null
@@ -0,0 +1,918 @@
+/*
+ * QEMU model of Xilinx AXI-Ethernet.
+ *
+ * Copyright (c) 2011 Edgar E. Iglesias.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/sysbus.h"
+#include "qemu/log.h"
+#include "net/net.h"
+#include "net/checksum.h"
+#include "qapi/qmp/qerror.h"
+
+#include "hw/stream.h"
+
+#define DPHY(x)
+
+/* Advertisement control register. */
+#define ADVERTISE_10HALF        0x0020  /* Try for 10mbps half-duplex  */
+#define ADVERTISE_10FULL        0x0040  /* Try for 10mbps full-duplex  */
+#define ADVERTISE_100HALF       0x0080  /* Try for 100mbps half-duplex */
+#define ADVERTISE_100FULL       0x0100  /* Try for 100mbps full-duplex */
+
+struct PHY {
+    uint32_t regs[32];
+
+    int link;
+
+    unsigned int (*read)(struct PHY *phy, unsigned int req);
+    void (*write)(struct PHY *phy, unsigned int req,
+                  unsigned int data);
+};
+
+static unsigned int tdk_read(struct PHY *phy, unsigned int req)
+{
+    int regnum;
+    unsigned r = 0;
+
+    regnum = req & 0x1f;
+
+    switch (regnum) {
+        case 1:
+            if (!phy->link) {
+                break;
+            }
+            /* MR1.  */
+            /* Speeds and modes.  */
+            r |= (1 << 13) | (1 << 14);
+            r |= (1 << 11) | (1 << 12);
+            r |= (1 << 5); /* Autoneg complete.  */
+            r |= (1 << 3); /* Autoneg able.  */
+            r |= (1 << 2); /* link.  */
+            r |= (1 << 1); /* link.  */
+            break;
+        case 5:
+            /* Link partner ability.
+               We are kind; always agree with whatever best mode
+               the guest advertises.  */
+            r = 1 << 14; /* Success.  */
+            /* Copy advertised modes.  */
+            r |= phy->regs[4] & (15 << 5);
+            /* Autoneg support.  */
+            r |= 1;
+            break;
+        case 17:
+            /* Marvel PHY on many xilinx boards.  */
+            r = 0x8000; /* 1000Mb  */
+            break;
+        case 18:
+            {
+                /* Diagnostics reg.  */
+                int duplex = 0;
+                int speed_100 = 0;
+
+                if (!phy->link) {
+                    break;
+                }
+
+                /* Are we advertising 100 half or 100 duplex ? */
+                speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF);
+                speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL);
+
+                /* Are we advertising 10 duplex or 100 duplex ? */
+                duplex = !!(phy->regs[4] & ADVERTISE_100FULL);
+                duplex |= !!(phy->regs[4] & ADVERTISE_10FULL);
+                r = (speed_100 << 10) | (duplex << 11);
+            }
+            break;
+
+        default:
+            r = phy->regs[regnum];
+            break;
+    }
+    DPHY(qemu_log("\n%s %x = reg[%d]\n", __func__, r, regnum));
+    return r;
+}
+
+static void
+tdk_write(struct PHY *phy, unsigned int req, unsigned int data)
+{
+    int regnum;
+
+    regnum = req & 0x1f;
+    DPHY(qemu_log("%s reg[%d] = %x\n", __func__, regnum, data));
+    switch (regnum) {
+        default:
+            phy->regs[regnum] = data;
+            break;
+    }
+}
+
+static void
+tdk_init(struct PHY *phy)
+{
+    phy->regs[0] = 0x3100;
+    /* PHY Id.  */
+    phy->regs[2] = 0x0300;
+    phy->regs[3] = 0xe400;
+    /* Autonegotiation advertisement reg.  */
+    phy->regs[4] = 0x01E1;
+    phy->link = 1;
+
+    phy->read = tdk_read;
+    phy->write = tdk_write;
+}
+
+struct MDIOBus {
+    /* bus.  */
+    int mdc;
+    int mdio;
+
+    /* decoder.  */
+    enum {
+        PREAMBLE,
+        SOF,
+        OPC,
+        ADDR,
+        REQ,
+        TURNAROUND,
+        DATA
+    } state;
+    unsigned int drive;
+
+    unsigned int cnt;
+    unsigned int addr;
+    unsigned int opc;
+    unsigned int req;
+    unsigned int data;
+
+    struct PHY *devs[32];
+};
+
+static void
+mdio_attach(struct MDIOBus *bus, struct PHY *phy, unsigned int addr)
+{
+    bus->devs[addr & 0x1f] = phy;
+}
+
+#ifdef USE_THIS_DEAD_CODE
+static void
+mdio_detach(struct MDIOBus *bus, struct PHY *phy, unsigned int addr)
+{
+    bus->devs[addr & 0x1f] = NULL;
+}
+#endif
+
+static uint16_t mdio_read_req(struct MDIOBus *bus, unsigned int addr,
+                  unsigned int reg)
+{
+    struct PHY *phy;
+    uint16_t data;
+
+    phy = bus->devs[addr];
+    if (phy && phy->read) {
+        data = phy->read(phy, reg);
+    } else {
+        data = 0xffff;
+    }
+    DPHY(qemu_log("%s addr=%d reg=%d data=%x\n", __func__, addr, reg, data));
+    return data;
+}
+
+static void mdio_write_req(struct MDIOBus *bus, unsigned int addr,
+               unsigned int reg, uint16_t data)
+{
+    struct PHY *phy;
+
+    DPHY(qemu_log("%s addr=%d reg=%d data=%x\n", __func__, addr, reg, data));
+    phy = bus->devs[addr];
+    if (phy && phy->write) {
+        phy->write(phy, reg, data);
+    }
+}
+
+#define DENET(x)
+
+#define R_RAF      (0x000 / 4)
+enum {
+    RAF_MCAST_REJ = (1 << 1),
+    RAF_BCAST_REJ = (1 << 2),
+    RAF_EMCF_EN = (1 << 12),
+    RAF_NEWFUNC_EN = (1 << 11)
+};
+
+#define R_IS       (0x00C / 4)
+enum {
+    IS_HARD_ACCESS_COMPLETE = 1,
+    IS_AUTONEG = (1 << 1),
+    IS_RX_COMPLETE = (1 << 2),
+    IS_RX_REJECT = (1 << 3),
+    IS_TX_COMPLETE = (1 << 5),
+    IS_RX_DCM_LOCK = (1 << 6),
+    IS_MGM_RDY = (1 << 7),
+    IS_PHY_RST_DONE = (1 << 8),
+};
+
+#define R_IP       (0x010 / 4)
+#define R_IE       (0x014 / 4)
+#define R_UAWL     (0x020 / 4)
+#define R_UAWU     (0x024 / 4)
+#define R_PPST     (0x030 / 4)
+enum {
+    PPST_LINKSTATUS = (1 << 0),
+    PPST_PHY_LINKSTATUS = (1 << 7),
+};
+
+#define R_STATS_RX_BYTESL (0x200 / 4)
+#define R_STATS_RX_BYTESH (0x204 / 4)
+#define R_STATS_TX_BYTESL (0x208 / 4)
+#define R_STATS_TX_BYTESH (0x20C / 4)
+#define R_STATS_RXL       (0x290 / 4)
+#define R_STATS_RXH       (0x294 / 4)
+#define R_STATS_RX_BCASTL (0x2a0 / 4)
+#define R_STATS_RX_BCASTH (0x2a4 / 4)
+#define R_STATS_RX_MCASTL (0x2a8 / 4)
+#define R_STATS_RX_MCASTH (0x2ac / 4)
+
+#define R_RCW0     (0x400 / 4)
+#define R_RCW1     (0x404 / 4)
+enum {
+    RCW1_VLAN = (1 << 27),
+    RCW1_RX   = (1 << 28),
+    RCW1_FCS  = (1 << 29),
+    RCW1_JUM  = (1 << 30),
+    RCW1_RST  = (1 << 31),
+};
+
+#define R_TC       (0x408 / 4)
+enum {
+    TC_VLAN = (1 << 27),
+    TC_TX   = (1 << 28),
+    TC_FCS  = (1 << 29),
+    TC_JUM  = (1 << 30),
+    TC_RST  = (1 << 31),
+};
+
+#define R_EMMC     (0x410 / 4)
+enum {
+    EMMC_LINKSPEED_10MB = (0 << 30),
+    EMMC_LINKSPEED_100MB = (1 << 30),
+    EMMC_LINKSPEED_1000MB = (2 << 30),
+};
+
+#define R_PHYC     (0x414 / 4)
+
+#define R_MC       (0x500 / 4)
+#define MC_EN      (1 << 6)
+
+#define R_MCR      (0x504 / 4)
+#define R_MWD      (0x508 / 4)
+#define R_MRD      (0x50c / 4)
+#define R_MIS      (0x600 / 4)
+#define R_MIP      (0x620 / 4)
+#define R_MIE      (0x640 / 4)
+#define R_MIC      (0x640 / 4)
+
+#define R_UAW0     (0x700 / 4)
+#define R_UAW1     (0x704 / 4)
+#define R_FMI      (0x708 / 4)
+#define R_AF0      (0x710 / 4)
+#define R_AF1      (0x714 / 4)
+#define R_MAX      (0x34 / 4)
+
+/* Indirect registers.  */
+struct TEMAC  {
+    struct MDIOBus mdio_bus;
+    struct PHY phy;
+
+    void *parent;
+};
+
+struct XilinxAXIEnet {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    qemu_irq irq;
+    StreamSlave *tx_dev;
+    NICState *nic;
+    NICConf conf;
+
+
+    uint32_t c_rxmem;
+    uint32_t c_txmem;
+    uint32_t c_phyaddr;
+
+    struct TEMAC TEMAC;
+
+    /* MII regs.  */
+    union {
+        uint32_t regs[4];
+        struct {
+            uint32_t mc;
+            uint32_t mcr;
+            uint32_t mwd;
+            uint32_t mrd;
+        };
+    } mii;
+
+    struct {
+        uint64_t rx_bytes;
+        uint64_t tx_bytes;
+
+        uint64_t rx;
+        uint64_t rx_bcast;
+        uint64_t rx_mcast;
+    } stats;
+
+    /* Receive configuration words.  */
+    uint32_t rcw[2];
+    /* Transmit config.  */
+    uint32_t tc;
+    uint32_t emmc;
+    uint32_t phyc;
+
+    /* Unicast Address Word.  */
+    uint32_t uaw[2];
+    /* Unicast address filter used with extended mcast.  */
+    uint32_t ext_uaw[2];
+    uint32_t fmi;
+
+    uint32_t regs[R_MAX];
+
+    /* Multicast filter addrs.  */
+    uint32_t maddr[4][2];
+    /* 32K x 1 lookup filter.  */
+    uint32_t ext_mtable[1024];
+
+
+    uint8_t *rxmem;
+};
+
+static void axienet_rx_reset(struct XilinxAXIEnet *s)
+{
+    s->rcw[1] = RCW1_JUM | RCW1_FCS | RCW1_RX | RCW1_VLAN;
+}
+
+static void axienet_tx_reset(struct XilinxAXIEnet *s)
+{
+    s->tc = TC_JUM | TC_TX | TC_VLAN;
+}
+
+static inline int axienet_rx_resetting(struct XilinxAXIEnet *s)
+{
+    return s->rcw[1] & RCW1_RST;
+}
+
+static inline int axienet_rx_enabled(struct XilinxAXIEnet *s)
+{
+    return s->rcw[1] & RCW1_RX;
+}
+
+static inline int axienet_extmcf_enabled(struct XilinxAXIEnet *s)
+{
+    return !!(s->regs[R_RAF] & RAF_EMCF_EN);
+}
+
+static inline int axienet_newfunc_enabled(struct XilinxAXIEnet *s)
+{
+    return !!(s->regs[R_RAF] & RAF_NEWFUNC_EN);
+}
+
+static void axienet_reset(struct XilinxAXIEnet *s)
+{
+    axienet_rx_reset(s);
+    axienet_tx_reset(s);
+
+    s->regs[R_PPST] = PPST_LINKSTATUS | PPST_PHY_LINKSTATUS;
+    s->regs[R_IS] = IS_AUTONEG | IS_RX_DCM_LOCK | IS_MGM_RDY | IS_PHY_RST_DONE;
+
+    s->emmc = EMMC_LINKSPEED_100MB;
+}
+
+static void enet_update_irq(struct XilinxAXIEnet *s)
+{
+    s->regs[R_IP] = s->regs[R_IS] & s->regs[R_IE];
+    qemu_set_irq(s->irq, !!s->regs[R_IP]);
+}
+
+static uint64_t enet_read(void *opaque, hwaddr addr, unsigned size)
+{
+    struct XilinxAXIEnet *s = opaque;
+    uint32_t r = 0;
+    addr >>= 2;
+
+    switch (addr) {
+        case R_RCW0:
+        case R_RCW1:
+            r = s->rcw[addr & 1];
+            break;
+
+        case R_TC:
+            r = s->tc;
+            break;
+
+        case R_EMMC:
+            r = s->emmc;
+            break;
+
+        case R_PHYC:
+            r = s->phyc;
+            break;
+
+        case R_MCR:
+            r = s->mii.regs[addr & 3] | (1 << 7); /* Always ready.  */
+            break;
+
+        case R_STATS_RX_BYTESL:
+        case R_STATS_RX_BYTESH:
+            r = s->stats.rx_bytes >> (32 * (addr & 1));
+            break;
+
+        case R_STATS_TX_BYTESL:
+        case R_STATS_TX_BYTESH:
+            r = s->stats.tx_bytes >> (32 * (addr & 1));
+            break;
+
+        case R_STATS_RXL:
+        case R_STATS_RXH:
+            r = s->stats.rx >> (32 * (addr & 1));
+            break;
+        case R_STATS_RX_BCASTL:
+        case R_STATS_RX_BCASTH:
+            r = s->stats.rx_bcast >> (32 * (addr & 1));
+            break;
+        case R_STATS_RX_MCASTL:
+        case R_STATS_RX_MCASTH:
+            r = s->stats.rx_mcast >> (32 * (addr & 1));
+            break;
+
+        case R_MC:
+        case R_MWD:
+        case R_MRD:
+            r = s->mii.regs[addr & 3];
+            break;
+
+        case R_UAW0:
+        case R_UAW1:
+            r = s->uaw[addr & 1];
+            break;
+
+        case R_UAWU:
+        case R_UAWL:
+            r = s->ext_uaw[addr & 1];
+            break;
+
+        case R_FMI:
+            r = s->fmi;
+            break;
+
+        case R_AF0:
+        case R_AF1:
+            r = s->maddr[s->fmi & 3][addr & 1];
+            break;
+
+        case 0x8000 ... 0x83ff:
+            r = s->ext_mtable[addr - 0x8000];
+            break;
+
+        default:
+            if (addr < ARRAY_SIZE(s->regs)) {
+                r = s->regs[addr];
+            }
+            DENET(qemu_log("%s addr=" TARGET_FMT_plx " v=%x\n",
+                            __func__, addr * 4, r));
+            break;
+    }
+    return r;
+}
+
+static void enet_write(void *opaque, hwaddr addr,
+                       uint64_t value, unsigned size)
+{
+    struct XilinxAXIEnet *s = opaque;
+    struct TEMAC *t = &s->TEMAC;
+
+    addr >>= 2;
+    switch (addr) {
+        case R_RCW0:
+        case R_RCW1:
+            s->rcw[addr & 1] = value;
+            if ((addr & 1) && value & RCW1_RST) {
+                axienet_rx_reset(s);
+            } else {
+                qemu_flush_queued_packets(qemu_get_queue(s->nic));
+            }
+            break;
+
+        case R_TC:
+            s->tc = value;
+            if (value & TC_RST) {
+                axienet_tx_reset(s);
+            }
+            break;
+
+        case R_EMMC:
+            s->emmc = value;
+            break;
+
+        case R_PHYC:
+            s->phyc = value;
+            break;
+
+        case R_MC:
+             value &= ((1 < 7) - 1);
+
+             /* Enable the MII.  */
+             if (value & MC_EN) {
+                 unsigned int miiclkdiv = value & ((1 << 6) - 1);
+                 if (!miiclkdiv) {
+                     qemu_log("AXIENET: MDIO enabled but MDIOCLK is zero!\n");
+                 }
+             }
+             s->mii.mc = value;
+             break;
+
+        case R_MCR: {
+             unsigned int phyaddr = (value >> 24) & 0x1f;
+             unsigned int regaddr = (value >> 16) & 0x1f;
+             unsigned int op = (value >> 14) & 3;
+             unsigned int initiate = (value >> 11) & 1;
+
+             if (initiate) {
+                 if (op == 1) {
+                     mdio_write_req(&t->mdio_bus, phyaddr, regaddr, s->mii.mwd);
+                 } else if (op == 2) {
+                     s->mii.mrd = mdio_read_req(&t->mdio_bus, phyaddr, regaddr);
+                 } else {
+                     qemu_log("AXIENET: invalid MDIOBus OP=%d\n", op);
+                 }
+             }
+             s->mii.mcr = value;
+             break;
+        }
+
+        case R_MWD:
+        case R_MRD:
+             s->mii.regs[addr & 3] = value;
+             break;
+
+
+        case R_UAW0:
+        case R_UAW1:
+            s->uaw[addr & 1] = value;
+            break;
+
+        case R_UAWL:
+        case R_UAWU:
+            s->ext_uaw[addr & 1] = value;
+            break;
+
+        case R_FMI:
+            s->fmi = value;
+            break;
+
+        case R_AF0:
+        case R_AF1:
+            s->maddr[s->fmi & 3][addr & 1] = value;
+            break;
+
+        case R_IS:
+            s->regs[addr] &= ~value;
+            break;
+
+        case 0x8000 ... 0x83ff:
+            s->ext_mtable[addr - 0x8000] = value;
+            break;
+
+        default:
+            DENET(qemu_log("%s addr=" TARGET_FMT_plx " v=%x\n",
+                           __func__, addr * 4, (unsigned)value));
+            if (addr < ARRAY_SIZE(s->regs)) {
+                s->regs[addr] = value;
+            }
+            break;
+    }
+    enet_update_irq(s);
+}
+
+static const MemoryRegionOps enet_ops = {
+    .read = enet_read,
+    .write = enet_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static int eth_can_rx(NetClientState *nc)
+{
+    struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
+
+    /* RX enabled?  */
+    return !axienet_rx_resetting(s) && axienet_rx_enabled(s);
+}
+
+static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1)
+{
+    int match = 1;
+
+    if (memcmp(buf, &f0, 4)) {
+        match = 0;
+    }
+
+    if (buf[4] != (f1 & 0xff) || buf[5] != ((f1 >> 8) & 0xff)) {
+        match = 0;
+    }
+
+    return match;
+}
+
+static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+    struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
+    static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff,
+                                              0xff, 0xff, 0xff};
+    static const unsigned char sa_ipmcast[3] = {0x01, 0x00, 0x52};
+    uint32_t app[6] = {0};
+    int promisc = s->fmi & (1 << 31);
+    int unicast, broadcast, multicast, ip_multicast = 0;
+    uint32_t csum32;
+    uint16_t csum16;
+    int i;
+
+    DENET(qemu_log("%s: %zd bytes\n", __func__, size));
+
+    unicast = ~buf[0] & 0x1;
+    broadcast = memcmp(buf, sa_bcast, 6) == 0;
+    multicast = !unicast && !broadcast;
+    if (multicast && (memcmp(sa_ipmcast, buf, sizeof sa_ipmcast) == 0)) {
+        ip_multicast = 1;
+    }
+
+    /* Jumbo or vlan sizes ?  */
+    if (!(s->rcw[1] & RCW1_JUM)) {
+        if (size > 1518 && size <= 1522 && !(s->rcw[1] & RCW1_VLAN)) {
+            return size;
+        }
+    }
+
+    /* Basic Address filters.  If you want to use the extended filters
+       you'll generally have to place the ethernet mac into promiscuous mode
+       to avoid the basic filtering from dropping most frames.  */
+    if (!promisc) {
+        if (unicast) {
+            if (!enet_match_addr(buf, s->uaw[0], s->uaw[1])) {
+                return size;
+            }
+        } else {
+            if (broadcast) {
+                /* Broadcast.  */
+                if (s->regs[R_RAF] & RAF_BCAST_REJ) {
+                    return size;
+                }
+            } else {
+                int drop = 1;
+
+                /* Multicast.  */
+                if (s->regs[R_RAF] & RAF_MCAST_REJ) {
+                    return size;
+                }
+
+                for (i = 0; i < 4; i++) {
+                    if (enet_match_addr(buf, s->maddr[i][0], s->maddr[i][1])) {
+                        drop = 0;
+                        break;
+                    }
+                }
+
+                if (drop) {
+                    return size;
+                }
+            }
+        }
+    }
+
+    /* Extended mcast filtering enabled?  */
+    if (axienet_newfunc_enabled(s) && axienet_extmcf_enabled(s)) {
+        if (unicast) {
+            if (!enet_match_addr(buf, s->ext_uaw[0], s->ext_uaw[1])) {
+                return size;
+            }
+        } else {
+            if (broadcast) {
+                /* Broadcast. ???  */
+                if (s->regs[R_RAF] & RAF_BCAST_REJ) {
+                    return size;
+                }
+            } else {
+                int idx, bit;
+
+                /* Multicast.  */
+                if (!memcmp(buf, sa_ipmcast, 3)) {
+                    return size;
+                }
+
+                idx  = (buf[4] & 0x7f) << 8;
+                idx |= buf[5];
+
+                bit = 1 << (idx & 0x1f);
+                idx >>= 5;
+
+                if (!(s->ext_mtable[idx] & bit)) {
+                    return size;
+                }
+            }
+        }
+    }
+
+    if (size < 12) {
+        s->regs[R_IS] |= IS_RX_REJECT;
+        enet_update_irq(s);
+        return -1;
+    }
+
+    if (size > (s->c_rxmem - 4)) {
+        size = s->c_rxmem - 4;
+    }
+
+    memcpy(s->rxmem, buf, size);
+    memset(s->rxmem + size, 0, 4); /* Clear the FCS.  */
+
+    if (s->rcw[1] & RCW1_FCS) {
+        size += 4; /* fcs is inband.  */
+    }
+
+    app[0] = 5 << 28;
+    csum32 = net_checksum_add(size - 14, (uint8_t *)s->rxmem + 14);
+    /* Fold it once.  */
+    csum32 = (csum32 & 0xffff) + (csum32 >> 16);
+    /* And twice to get rid of possible carries.  */
+    csum16 = (csum32 & 0xffff) + (csum32 >> 16);
+    app[3] = csum16;
+    app[4] = size & 0xffff;
+
+    s->stats.rx_bytes += size;
+    s->stats.rx++;
+    if (multicast) {
+        s->stats.rx_mcast++;
+        app[2] |= 1 | (ip_multicast << 1);
+    } else if (broadcast) {
+        s->stats.rx_bcast++;
+        app[2] |= 1 << 3;
+    }
+
+    /* Good frame.  */
+    app[2] |= 1 << 6;
+
+    stream_push(s->tx_dev, (void *)s->rxmem, size, app);
+
+    s->regs[R_IS] |= IS_RX_COMPLETE;
+    enet_update_irq(s);
+    return size;
+}
+
+static void eth_cleanup(NetClientState *nc)
+{
+    /* FIXME.  */
+    struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
+    g_free(s->rxmem);
+    g_free(s);
+}
+
+static void
+axienet_stream_push(StreamSlave *obj, uint8_t *buf, size_t size, uint32_t *hdr)
+{
+    struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj));
+
+    /* TX enable ?  */
+    if (!(s->tc & TC_TX)) {
+        return;
+    }
+
+    /* Jumbo or vlan sizes ?  */
+    if (!(s->tc & TC_JUM)) {
+        if (size > 1518 && size <= 1522 && !(s->tc & TC_VLAN)) {
+            return;
+        }
+    }
+
+    if (hdr[0] & 1) {
+        unsigned int start_off = hdr[1] >> 16;
+        unsigned int write_off = hdr[1] & 0xffff;
+        uint32_t tmp_csum;
+        uint16_t csum;
+
+        tmp_csum = net_checksum_add(size - start_off,
+                                    (uint8_t *)buf + start_off);
+        /* Accumulate the seed.  */
+        tmp_csum += hdr[2] & 0xffff;
+
+        /* Fold the 32bit partial checksum.  */
+        csum = net_checksum_finish(tmp_csum);
+
+        /* Writeback.  */
+        buf[write_off] = csum >> 8;
+        buf[write_off + 1] = csum & 0xff;
+    }
+
+    qemu_send_packet(qemu_get_queue(s->nic), buf, size);
+
+    s->stats.tx_bytes += size;
+    s->regs[R_IS] |= IS_TX_COMPLETE;
+    enet_update_irq(s);
+}
+
+static NetClientInfo net_xilinx_enet_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = eth_can_rx,
+    .receive = eth_rx,
+    .cleanup = eth_cleanup,
+};
+
+static int xilinx_enet_init(SysBusDevice *dev)
+{
+    struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), dev);
+
+    sysbus_init_irq(dev, &s->irq);
+
+    memory_region_init_io(&s->iomem, &enet_ops, s, "enet", 0x40000);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+    s->nic = qemu_new_nic(&net_xilinx_enet_info, &s->conf,
+                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+
+    tdk_init(&s->TEMAC.phy);
+    mdio_attach(&s->TEMAC.mdio_bus, &s->TEMAC.phy, s->c_phyaddr);
+
+    s->TEMAC.parent = s;
+
+    s->rxmem = g_malloc(s->c_rxmem);
+    axienet_reset(s);
+
+    return 0;
+}
+
+static void xilinx_enet_initfn(Object *obj)
+{
+    struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj));
+    Error *errp = NULL;
+
+    object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
+                             (Object **) &s->tx_dev, &errp);
+    assert_no_error(errp);
+}
+
+static Property xilinx_enet_properties[] = {
+    DEFINE_PROP_UINT32("phyaddr", struct XilinxAXIEnet, c_phyaddr, 7),
+    DEFINE_PROP_UINT32("rxmem", struct XilinxAXIEnet, c_rxmem, 0x1000),
+    DEFINE_PROP_UINT32("txmem", struct XilinxAXIEnet, c_txmem, 0x1000),
+    DEFINE_NIC_PROPERTIES(struct XilinxAXIEnet, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xilinx_enet_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
+
+    k->init = xilinx_enet_init;
+    dc->props = xilinx_enet_properties;
+    ssc->push = axienet_stream_push;
+}
+
+static const TypeInfo xilinx_enet_info = {
+    .name          = "xlnx.axi-ethernet",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(struct XilinxAXIEnet),
+    .class_init    = xilinx_enet_class_init,
+    .instance_init = xilinx_enet_initfn,
+    .interfaces = (InterfaceInfo[]) {
+            { TYPE_STREAM_SLAVE },
+            { }
+    }
+};
+
+static void xilinx_enet_register_types(void)
+{
+    type_register_static(&xilinx_enet_info);
+}
+
+type_init(xilinx_enet_register_types)
diff --git a/hw/null-machine.c b/hw/null-machine.c
deleted file mode 100644 (file)
index bdf109f..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Empty machine
- *
- * Copyright IBM, Corp. 2012
- *
- * Authors:
- *  Anthony Liguori   <aliguori@us.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- *
- */
-
-#include "qemu-common.h"
-#include "hw/hw.h"
-#include "hw/boards.h"
-
-static void machine_none_init(QEMUMachineInitArgs *args)
-{
-}
-
-static QEMUMachine machine_none = {
-    .name = "none",
-    .desc = "empty machine",
-    .init = machine_none_init,
-    .max_cpus = 0,
-    DEFAULT_MACHINE_OPTIONS,
-};
-
-static void register_machines(void)
-{
-    qemu_register_machine(&machine_none);
-}
-
-machine_init(register_machines);
-
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..80fb1b0441b0d100eb64c13a0ff0f804ea160815 100644 (file)
@@ -0,0 +1,4 @@
+common-obj-$(CONFIG_DS1225Y) += ds1225y.o
+common-obj-y += eeprom93xx.o
+common-obj-y += fw_cfg.o
+common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o
diff --git a/hw/nvram/ds1225y.c b/hw/nvram/ds1225y.c
new file mode 100644 (file)
index 0000000..488f1d7
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * QEMU NVRAM emulation for DS1225Y chip
+ *
+ * Copyright (c) 2007-2008 Hervé Poussineau
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/sysbus.h"
+#include "trace.h"
+
+typedef struct {
+    DeviceState qdev;
+    MemoryRegion iomem;
+    uint32_t chip_size;
+    char *filename;
+    FILE *file;
+    uint8_t *contents;
+} NvRamState;
+
+static uint64_t nvram_read(void *opaque, hwaddr addr, unsigned size)
+{
+    NvRamState *s = opaque;
+    uint32_t val;
+
+    val = s->contents[addr];
+    trace_nvram_read(addr, val);
+    return val;
+}
+
+static void nvram_write(void *opaque, hwaddr addr, uint64_t val,
+                        unsigned size)
+{
+    NvRamState *s = opaque;
+
+    val &= 0xff;
+    trace_nvram_write(addr, s->contents[addr], val);
+
+    s->contents[addr] = val;
+    if (s->file) {
+        fseek(s->file, addr, SEEK_SET);
+        fputc(val, s->file);
+        fflush(s->file);
+    }
+}
+
+static const MemoryRegionOps nvram_ops = {
+    .read = nvram_read,
+    .write = nvram_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static int nvram_post_load(void *opaque, int version_id)
+{
+    NvRamState *s = opaque;
+
+    /* Close file, as filename may has changed in load/store process */
+    if (s->file) {
+        fclose(s->file);
+    }
+
+    /* Write back nvram contents */
+    s->file = fopen(s->filename, "wb");
+    if (s->file) {
+        /* Write back contents, as 'wb' mode cleaned the file */
+        if (fwrite(s->contents, s->chip_size, 1, s->file) != 1) {
+            printf("nvram_post_load: short write\n");
+        }
+        fflush(s->file);
+    }
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_nvram = {
+    .name = "nvram",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .post_load = nvram_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_VARRAY_UINT32(contents, NvRamState, chip_size, 0,
+                              vmstate_info_uint8, uint8_t),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+typedef struct {
+    SysBusDevice busdev;
+    NvRamState nvram;
+} SysBusNvRamState;
+
+static int nvram_sysbus_initfn(SysBusDevice *dev)
+{
+    NvRamState *s = &FROM_SYSBUS(SysBusNvRamState, dev)->nvram;
+    FILE *file;
+
+    s->contents = g_malloc0(s->chip_size);
+
+    memory_region_init_io(&s->iomem, &nvram_ops, s, "nvram", s->chip_size);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    /* Read current file */
+    file = fopen(s->filename, "rb");
+    if (file) {
+        /* Read nvram contents */
+        if (fread(s->contents, s->chip_size, 1, file) != 1) {
+            printf("nvram_sysbus_initfn: short read\n");
+        }
+        fclose(file);
+    }
+    nvram_post_load(s, 0);
+
+    return 0;
+}
+
+static Property nvram_sysbus_properties[] = {
+    DEFINE_PROP_UINT32("size", SysBusNvRamState, nvram.chip_size, 0x2000),
+    DEFINE_PROP_STRING("filename", SysBusNvRamState, nvram.filename),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void nvram_sysbus_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = nvram_sysbus_initfn;
+    dc->vmsd = &vmstate_nvram;
+    dc->props = nvram_sysbus_properties;
+}
+
+static const TypeInfo nvram_sysbus_info = {
+    .name          = "ds1225y",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SysBusNvRamState),
+    .class_init    = nvram_sysbus_class_init,
+};
+
+static void nvram_register_types(void)
+{
+    type_register_static(&nvram_sysbus_info);
+}
+
+type_init(nvram_register_types)
diff --git a/hw/nvram/eeprom93xx.c b/hw/nvram/eeprom93xx.c
new file mode 100644 (file)
index 0000000..08f4df5
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * QEMU EEPROM 93xx emulation
+ *
+ * Copyright (c) 2006-2007 Stefan Weil
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Emulation for serial EEPROMs:
+ * NMC93C06 256-Bit (16 x 16)
+ * NMC93C46 1024-Bit (64 x 16)
+ * NMC93C56 2028 Bit (128 x 16)
+ * NMC93C66 4096 Bit (256 x 16)
+ * Compatible devices include FM93C46 and others.
+ *
+ * Other drivers use these interface functions:
+ * eeprom93xx_new   - add a new EEPROM (with 16, 64 or 256 words)
+ * eeprom93xx_free  - destroy EEPROM
+ * eeprom93xx_read  - read data from the EEPROM
+ * eeprom93xx_write - write data to the EEPROM
+ * eeprom93xx_data  - get EEPROM data array for external manipulation
+ *
+ * Todo list:
+ * - No emulation of EEPROM timings.
+ */
+
+#include "hw/hw.h"
+#include "hw/nvram/eeprom93xx.h"
+
+/* Debug EEPROM emulation. */
+//~ #define DEBUG_EEPROM
+
+#ifdef DEBUG_EEPROM
+#define logout(fmt, ...) fprintf(stderr, "EEPROM\t%-24s" fmt, __func__, ## __VA_ARGS__)
+#else
+#define logout(fmt, ...) ((void)0)
+#endif
+
+#define EEPROM_INSTANCE  0
+#define OLD_EEPROM_VERSION 20061112
+#define EEPROM_VERSION (OLD_EEPROM_VERSION + 1)
+
+#if 0
+typedef enum {
+  eeprom_read  = 0x80,   /* read register xx */
+  eeprom_write = 0x40,   /* write register xx */
+  eeprom_erase = 0xc0,   /* erase register xx */
+  eeprom_ewen  = 0x30,   /* erase / write enable */
+  eeprom_ewds  = 0x00,   /* erase / write disable */
+  eeprom_eral  = 0x20,   /* erase all registers */
+  eeprom_wral  = 0x10,   /* write all registers */
+  eeprom_amask = 0x0f,
+  eeprom_imask = 0xf0
+} eeprom_instruction_t;
+#endif
+
+#ifdef DEBUG_EEPROM
+static const char *opstring[] = {
+  "extended", "write", "read", "erase"
+};
+#endif
+
+struct _eeprom_t {
+    uint8_t  tick;
+    uint8_t  address;
+    uint8_t  command;
+    uint8_t  writable;
+
+    uint8_t eecs;
+    uint8_t eesk;
+    uint8_t eedo;
+
+    uint8_t  addrbits;
+    uint16_t size;
+    uint16_t data;
+    uint16_t contents[0];
+};
+
+/* Code for saving and restoring of EEPROM state. */
+
+/* Restore an uint16_t from an uint8_t
+   This is a Big hack, but it is how the old state did it.
+ */
+
+static int get_uint16_from_uint8(QEMUFile *f, void *pv, size_t size)
+{
+    uint16_t *v = pv;
+    *v = qemu_get_ubyte(f);
+    return 0;
+}
+
+static void put_unused(QEMUFile *f, void *pv, size_t size)
+{
+    fprintf(stderr, "uint16_from_uint8 is used only for backwards compatibility.\n");
+    fprintf(stderr, "Never should be used to write a new state.\n");
+    exit(0);
+}
+
+static const VMStateInfo vmstate_hack_uint16_from_uint8 = {
+    .name = "uint16_from_uint8",
+    .get  = get_uint16_from_uint8,
+    .put  = put_unused,
+};
+
+#define VMSTATE_UINT16_HACK_TEST(_f, _s, _t)                           \
+    VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_hack_uint16_from_uint8, uint16_t)
+
+static bool is_old_eeprom_version(void *opaque, int version_id)
+{
+    return version_id == OLD_EEPROM_VERSION;
+}
+
+static const VMStateDescription vmstate_eeprom = {
+    .name = "eeprom",
+    .version_id = EEPROM_VERSION,
+    .minimum_version_id = OLD_EEPROM_VERSION,
+    .minimum_version_id_old = OLD_EEPROM_VERSION,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT8(tick, eeprom_t),
+        VMSTATE_UINT8(address, eeprom_t),
+        VMSTATE_UINT8(command, eeprom_t),
+        VMSTATE_UINT8(writable, eeprom_t),
+
+        VMSTATE_UINT8(eecs, eeprom_t),
+        VMSTATE_UINT8(eesk, eeprom_t),
+        VMSTATE_UINT8(eedo, eeprom_t),
+
+        VMSTATE_UINT8(addrbits, eeprom_t),
+        VMSTATE_UINT16_HACK_TEST(size, eeprom_t, is_old_eeprom_version),
+        VMSTATE_UNUSED_TEST(is_old_eeprom_version, 1),
+        VMSTATE_UINT16_EQUAL_V(size, eeprom_t, EEPROM_VERSION),
+        VMSTATE_UINT16(data, eeprom_t),
+        VMSTATE_VARRAY_UINT16_UNSAFE(contents, eeprom_t, size, 0,
+                                     vmstate_info_uint16, uint16_t),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+void eeprom93xx_write(eeprom_t *eeprom, int eecs, int eesk, int eedi)
+{
+    uint8_t tick = eeprom->tick;
+    uint8_t eedo = eeprom->eedo;
+    uint16_t address = eeprom->address;
+    uint8_t command = eeprom->command;
+
+    logout("CS=%u SK=%u DI=%u DO=%u, tick = %u\n",
+           eecs, eesk, eedi, eedo, tick);
+
+    if (! eeprom->eecs && eecs) {
+        /* Start chip select cycle. */
+        logout("Cycle start, waiting for 1st start bit (0)\n");
+        tick = 0;
+        command = 0x0;
+        address = 0x0;
+    } else if (eeprom->eecs && ! eecs) {
+        /* End chip select cycle. This triggers write / erase. */
+        if (eeprom->writable) {
+            uint8_t subcommand = address >> (eeprom->addrbits - 2);
+            if (command == 0 && subcommand == 2) {
+                /* Erase all. */
+                for (address = 0; address < eeprom->size; address++) {
+                    eeprom->contents[address] = 0xffff;
+                }
+            } else if (command == 3) {
+                /* Erase word. */
+                eeprom->contents[address] = 0xffff;
+            } else if (tick >= 2 + 2 + eeprom->addrbits + 16) {
+                if (command == 1) {
+                    /* Write word. */
+                    eeprom->contents[address] &= eeprom->data;
+                } else if (command == 0 && subcommand == 1) {
+                    /* Write all. */
+                    for (address = 0; address < eeprom->size; address++) {
+                        eeprom->contents[address] &= eeprom->data;
+                    }
+                }
+            }
+        }
+        /* Output DO is tristate, read results in 1. */
+        eedo = 1;
+    } else if (eecs && ! eeprom->eesk && eesk) {
+        /* Raising edge of clock shifts data in. */
+        if (tick == 0) {
+            /* Wait for 1st start bit. */
+            if (eedi == 0) {
+                logout("Got correct 1st start bit, waiting for 2nd start bit (1)\n");
+                tick++;
+            } else {
+                logout("wrong 1st start bit (is 1, should be 0)\n");
+                tick = 2;
+                //~ assert(!"wrong start bit");
+            }
+        } else if (tick == 1) {
+            /* Wait for 2nd start bit. */
+            if (eedi != 0) {
+                logout("Got correct 2nd start bit, getting command + address\n");
+                tick++;
+            } else {
+                logout("1st start bit is longer than needed\n");
+            }
+        } else if (tick < 2 + 2) {
+            /* Got 2 start bits, transfer 2 opcode bits. */
+            tick++;
+            command <<= 1;
+            if (eedi) {
+                command += 1;
+            }
+        } else if (tick < 2 + 2 + eeprom->addrbits) {
+            /* Got 2 start bits and 2 opcode bits, transfer all address bits. */
+            tick++;
+            address = ((address << 1) | eedi);
+            if (tick == 2 + 2 + eeprom->addrbits) {
+                logout("%s command, address = 0x%02x (value 0x%04x)\n",
+                       opstring[command], address, eeprom->contents[address]);
+                if (command == 2) {
+                    eedo = 0;
+                }
+                address = address % eeprom->size;
+                if (command == 0) {
+                    /* Command code in upper 2 bits of address. */
+                    switch (address >> (eeprom->addrbits - 2)) {
+                        case 0:
+                            logout("write disable command\n");
+                            eeprom->writable = 0;
+                            break;
+                        case 1:
+                            logout("write all command\n");
+                            break;
+                        case 2:
+                            logout("erase all command\n");
+                            break;
+                        case 3:
+                            logout("write enable command\n");
+                            eeprom->writable = 1;
+                            break;
+                    }
+                } else {
+                    /* Read, write or erase word. */
+                    eeprom->data = eeprom->contents[address];
+                }
+            }
+        } else if (tick < 2 + 2 + eeprom->addrbits + 16) {
+            /* Transfer 16 data bits. */
+            tick++;
+            if (command == 2) {
+                /* Read word. */
+                eedo = ((eeprom->data & 0x8000) != 0);
+            }
+            eeprom->data <<= 1;
+            eeprom->data += eedi;
+        } else {
+            logout("additional unneeded tick, not processed\n");
+        }
+    }
+    /* Save status of EEPROM. */
+    eeprom->tick = tick;
+    eeprom->eecs = eecs;
+    eeprom->eesk = eesk;
+    eeprom->eedo = eedo;
+    eeprom->address = address;
+    eeprom->command = command;
+}
+
+uint16_t eeprom93xx_read(eeprom_t *eeprom)
+{
+    /* Return status of pin DO (0 or 1). */
+    logout("CS=%u DO=%u\n", eeprom->eecs, eeprom->eedo);
+    return (eeprom->eedo);
+}
+
+#if 0
+void eeprom93xx_reset(eeprom_t *eeprom)
+{
+    /* prepare eeprom */
+    logout("eeprom = 0x%p\n", eeprom);
+    eeprom->tick = 0;
+    eeprom->command = 0;
+}
+#endif
+
+eeprom_t *eeprom93xx_new(DeviceState *dev, uint16_t nwords)
+{
+    /* Add a new EEPROM (with 16, 64 or 256 words). */
+    eeprom_t *eeprom;
+    uint8_t addrbits;
+
+    switch (nwords) {
+        case 16:
+        case 64:
+            addrbits = 6;
+            break;
+        case 128:
+        case 256:
+            addrbits = 8;
+            break;
+        default:
+            assert(!"Unsupported EEPROM size, fallback to 64 words!");
+            nwords = 64;
+            addrbits = 6;
+    }
+
+    eeprom = (eeprom_t *)g_malloc0(sizeof(*eeprom) + nwords * 2);
+    eeprom->size = nwords;
+    eeprom->addrbits = addrbits;
+    /* Output DO is tristate, read results in 1. */
+    eeprom->eedo = 1;
+    logout("eeprom = 0x%p, nwords = %u\n", eeprom, nwords);
+    vmstate_register(dev, 0, &vmstate_eeprom, eeprom);
+    return eeprom;
+}
+
+void eeprom93xx_free(DeviceState *dev, eeprom_t *eeprom)
+{
+    /* Destroy EEPROM. */
+    logout("eeprom = 0x%p\n", eeprom);
+    vmstate_unregister(dev, &vmstate_eeprom, eeprom);
+    g_free(eeprom);
+}
+
+uint16_t *eeprom93xx_data(eeprom_t *eeprom)
+{
+    /* Get EEPROM data array. */
+    return &eeprom->contents[0];
+}
+
+/* eof */
diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c
new file mode 100644 (file)
index 0000000..97bba87
--- /dev/null
@@ -0,0 +1,574 @@
+/*
+ * QEMU Firmware configuration device emulation
+ *
+ * Copyright (c) 2008 Gleb Natapov
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "sysemu/sysemu.h"
+#include "hw/isa/isa.h"
+#include "hw/nvram/fw_cfg.h"
+#include "hw/sysbus.h"
+#include "trace.h"
+#include "qemu/error-report.h"
+#include "qemu/config-file.h"
+
+#define FW_CFG_SIZE 2
+#define FW_CFG_DATA_SIZE 1
+
+typedef struct FWCfgEntry {
+    uint32_t len;
+    uint8_t *data;
+    void *callback_opaque;
+    FWCfgCallback callback;
+} FWCfgEntry;
+
+struct FWCfgState {
+    SysBusDevice busdev;
+    MemoryRegion ctl_iomem, data_iomem, comb_iomem;
+    uint32_t ctl_iobase, data_iobase;
+    FWCfgEntry entries[2][FW_CFG_MAX_ENTRY];
+    FWCfgFiles *files;
+    uint16_t cur_entry;
+    uint32_t cur_offset;
+    Notifier machine_ready;
+};
+
+#define JPG_FILE 0
+#define BMP_FILE 1
+
+static char *read_splashfile(char *filename, size_t *file_sizep,
+                             int *file_typep)
+{
+    GError *err = NULL;
+    gboolean res;
+    gchar *content;
+    int file_type;
+    unsigned int filehead;
+    int bmp_bpp;
+
+    res = g_file_get_contents(filename, &content, file_sizep, &err);
+    if (res == FALSE) {
+        error_report("failed to read splash file '%s'", filename);
+        g_error_free(err);
+        return NULL;
+    }
+
+    /* check file size */
+    if (*file_sizep < 30) {
+        goto error;
+    }
+
+    /* check magic ID */
+    filehead = ((content[0] & 0xff) + (content[1] << 8)) & 0xffff;
+    if (filehead == 0xd8ff) {
+        file_type = JPG_FILE;
+    } else if (filehead == 0x4d42) {
+        file_type = BMP_FILE;
+    } else {
+        goto error;
+    }
+
+    /* check BMP bpp */
+    if (file_type == BMP_FILE) {
+        bmp_bpp = (content[28] + (content[29] << 8)) & 0xffff;
+        if (bmp_bpp != 24) {
+            goto error;
+        }
+    }
+
+    /* return values */
+    *file_typep = file_type;
+
+    return content;
+
+error:
+    error_report("splash file '%s' format not recognized; must be JPEG "
+                 "or 24 bit BMP", filename);
+    g_free(content);
+    return NULL;
+}
+
+static void fw_cfg_bootsplash(FWCfgState *s)
+{
+    int boot_splash_time = -1;
+    const char *boot_splash_filename = NULL;
+    char *p;
+    char *filename, *file_data;
+    size_t file_size;
+    int file_type;
+    const char *temp;
+
+    /* get user configuration */
+    QemuOptsList *plist = qemu_find_opts("boot-opts");
+    QemuOpts *opts = QTAILQ_FIRST(&plist->head);
+    if (opts != NULL) {
+        temp = qemu_opt_get(opts, "splash");
+        if (temp != NULL) {
+            boot_splash_filename = temp;
+        }
+        temp = qemu_opt_get(opts, "splash-time");
+        if (temp != NULL) {
+            p = (char *)temp;
+            boot_splash_time = strtol(p, (char **)&p, 10);
+        }
+    }
+
+    /* insert splash time if user configurated */
+    if (boot_splash_time >= 0) {
+        /* validate the input */
+        if (boot_splash_time > 0xffff) {
+            error_report("splash time is big than 65535, force it to 65535.");
+            boot_splash_time = 0xffff;
+        }
+        /* use little endian format */
+        qemu_extra_params_fw[0] = (uint8_t)(boot_splash_time & 0xff);
+        qemu_extra_params_fw[1] = (uint8_t)((boot_splash_time >> 8) & 0xff);
+        fw_cfg_add_file(s, "etc/boot-menu-wait", qemu_extra_params_fw, 2);
+    }
+
+    /* insert splash file if user configurated */
+    if (boot_splash_filename != NULL) {
+        filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, boot_splash_filename);
+        if (filename == NULL) {
+            error_report("failed to find file '%s'.", boot_splash_filename);
+            return;
+        }
+
+        /* loading file data */
+        file_data = read_splashfile(filename, &file_size, &file_type);
+        if (file_data == NULL) {
+            g_free(filename);
+            return;
+        }
+        if (boot_splash_filedata != NULL) {
+            g_free(boot_splash_filedata);
+        }
+        boot_splash_filedata = (uint8_t *)file_data;
+        boot_splash_filedata_size = file_size;
+
+        /* insert data */
+        if (file_type == JPG_FILE) {
+            fw_cfg_add_file(s, "bootsplash.jpg",
+                    boot_splash_filedata, boot_splash_filedata_size);
+        } else {
+            fw_cfg_add_file(s, "bootsplash.bmp",
+                    boot_splash_filedata, boot_splash_filedata_size);
+        }
+        g_free(filename);
+    }
+}
+
+static void fw_cfg_reboot(FWCfgState *s)
+{
+    int reboot_timeout = -1;
+    char *p;
+    const char *temp;
+
+    /* get user configuration */
+    QemuOptsList *plist = qemu_find_opts("boot-opts");
+    QemuOpts *opts = QTAILQ_FIRST(&plist->head);
+    if (opts != NULL) {
+        temp = qemu_opt_get(opts, "reboot-timeout");
+        if (temp != NULL) {
+            p = (char *)temp;
+            reboot_timeout = strtol(p, (char **)&p, 10);
+        }
+    }
+    /* validate the input */
+    if (reboot_timeout > 0xffff) {
+        error_report("reboot timeout is larger than 65535, force it to 65535.");
+        reboot_timeout = 0xffff;
+    }
+    fw_cfg_add_file(s, "etc/boot-fail-wait", g_memdup(&reboot_timeout, 4), 4);
+}
+
+static void fw_cfg_write(FWCfgState *s, uint8_t value)
+{
+    int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
+    FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
+
+    trace_fw_cfg_write(s, value);
+
+    if (s->cur_entry & FW_CFG_WRITE_CHANNEL && e->callback &&
+        s->cur_offset < e->len) {
+        e->data[s->cur_offset++] = value;
+        if (s->cur_offset == e->len) {
+            e->callback(e->callback_opaque, e->data);
+            s->cur_offset = 0;
+        }
+    }
+}
+
+static int fw_cfg_select(FWCfgState *s, uint16_t key)
+{
+    int ret;
+
+    s->cur_offset = 0;
+    if ((key & FW_CFG_ENTRY_MASK) >= FW_CFG_MAX_ENTRY) {
+        s->cur_entry = FW_CFG_INVALID;
+        ret = 0;
+    } else {
+        s->cur_entry = key;
+        ret = 1;
+    }
+
+    trace_fw_cfg_select(s, key, ret);
+    return ret;
+}
+
+static uint8_t fw_cfg_read(FWCfgState *s)
+{
+    int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
+    FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
+    uint8_t ret;
+
+    if (s->cur_entry == FW_CFG_INVALID || !e->data || s->cur_offset >= e->len)
+        ret = 0;
+    else
+        ret = e->data[s->cur_offset++];
+
+    trace_fw_cfg_read(s, ret);
+    return ret;
+}
+
+static uint64_t fw_cfg_data_mem_read(void *opaque, hwaddr addr,
+                                     unsigned size)
+{
+    return fw_cfg_read(opaque);
+}
+
+static void fw_cfg_data_mem_write(void *opaque, hwaddr addr,
+                                  uint64_t value, unsigned size)
+{
+    fw_cfg_write(opaque, (uint8_t)value);
+}
+
+static void fw_cfg_ctl_mem_write(void *opaque, hwaddr addr,
+                                 uint64_t value, unsigned size)
+{
+    fw_cfg_select(opaque, (uint16_t)value);
+}
+
+static bool fw_cfg_ctl_mem_valid(void *opaque, hwaddr addr,
+                                 unsigned size, bool is_write)
+{
+    return is_write && size == 2;
+}
+
+static uint64_t fw_cfg_comb_read(void *opaque, hwaddr addr,
+                                 unsigned size)
+{
+    return fw_cfg_read(opaque);
+}
+
+static void fw_cfg_comb_write(void *opaque, hwaddr addr,
+                              uint64_t value, unsigned size)
+{
+    switch (size) {
+    case 1:
+        fw_cfg_write(opaque, (uint8_t)value);
+        break;
+    case 2:
+        fw_cfg_select(opaque, (uint16_t)value);
+        break;
+    }
+}
+
+static bool fw_cfg_comb_valid(void *opaque, hwaddr addr,
+                                  unsigned size, bool is_write)
+{
+    return (size == 1) || (is_write && size == 2);
+}
+
+static const MemoryRegionOps fw_cfg_ctl_mem_ops = {
+    .write = fw_cfg_ctl_mem_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid.accepts = fw_cfg_ctl_mem_valid,
+};
+
+static const MemoryRegionOps fw_cfg_data_mem_ops = {
+    .read = fw_cfg_data_mem_read,
+    .write = fw_cfg_data_mem_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static const MemoryRegionOps fw_cfg_comb_mem_ops = {
+    .read = fw_cfg_comb_read,
+    .write = fw_cfg_comb_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid.accepts = fw_cfg_comb_valid,
+};
+
+static void fw_cfg_reset(DeviceState *d)
+{
+    FWCfgState *s = DO_UPCAST(FWCfgState, busdev.qdev, d);
+
+    fw_cfg_select(s, 0);
+}
+
+/* Save restore 32 bit int as uint16_t
+   This is a Big hack, but it is how the old state did it.
+   Or we broke compatibility in the state, or we can't use struct tm
+ */
+
+static int get_uint32_as_uint16(QEMUFile *f, void *pv, size_t size)
+{
+    uint32_t *v = pv;
+    *v = qemu_get_be16(f);
+    return 0;
+}
+
+static void put_unused(QEMUFile *f, void *pv, size_t size)
+{
+    fprintf(stderr, "uint32_as_uint16 is only used for backward compatibility.\n");
+    fprintf(stderr, "This functions shouldn't be called.\n");
+}
+
+static const VMStateInfo vmstate_hack_uint32_as_uint16 = {
+    .name = "int32_as_uint16",
+    .get  = get_uint32_as_uint16,
+    .put  = put_unused,
+};
+
+#define VMSTATE_UINT16_HACK(_f, _s, _t)                                    \
+    VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_hack_uint32_as_uint16, uint32_t)
+
+
+static bool is_version_1(void *opaque, int version_id)
+{
+    return version_id == 1;
+}
+
+static const VMStateDescription vmstate_fw_cfg = {
+    .name = "fw_cfg",
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT16(cur_entry, FWCfgState),
+        VMSTATE_UINT16_HACK(cur_offset, FWCfgState, is_version_1),
+        VMSTATE_UINT32_V(cur_offset, FWCfgState, 2),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len)
+{
+    int arch = !!(key & FW_CFG_ARCH_LOCAL);
+
+    key &= FW_CFG_ENTRY_MASK;
+
+    assert(key < FW_CFG_MAX_ENTRY && len < UINT32_MAX);
+
+    s->entries[arch][key].data = data;
+    s->entries[arch][key].len = (uint32_t)len;
+}
+
+void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value)
+{
+    size_t sz = strlen(value) + 1;
+
+    return fw_cfg_add_bytes(s, key, g_memdup(value, sz), sz);
+}
+
+void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value)
+{
+    uint16_t *copy;
+
+    copy = g_malloc(sizeof(value));
+    *copy = cpu_to_le16(value);
+    fw_cfg_add_bytes(s, key, copy, sizeof(value));
+}
+
+void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value)
+{
+    uint32_t *copy;
+
+    copy = g_malloc(sizeof(value));
+    *copy = cpu_to_le32(value);
+    fw_cfg_add_bytes(s, key, copy, sizeof(value));
+}
+
+void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value)
+{
+    uint64_t *copy;
+
+    copy = g_malloc(sizeof(value));
+    *copy = cpu_to_le64(value);
+    fw_cfg_add_bytes(s, key, copy, sizeof(value));
+}
+
+void fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback,
+                         void *callback_opaque, void *data, size_t len)
+{
+    int arch = !!(key & FW_CFG_ARCH_LOCAL);
+
+    assert(key & FW_CFG_WRITE_CHANNEL);
+
+    key &= FW_CFG_ENTRY_MASK;
+
+    assert(key < FW_CFG_MAX_ENTRY && len <= UINT32_MAX);
+
+    s->entries[arch][key].data = data;
+    s->entries[arch][key].len = (uint32_t)len;
+    s->entries[arch][key].callback_opaque = callback_opaque;
+    s->entries[arch][key].callback = callback;
+}
+
+void fw_cfg_add_file(FWCfgState *s,  const char *filename,
+                     void *data, size_t len)
+{
+    int i, index;
+    size_t dsize;
+
+    if (!s->files) {
+        dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * FW_CFG_FILE_SLOTS;
+        s->files = g_malloc0(dsize);
+        fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, s->files, dsize);
+    }
+
+    index = be32_to_cpu(s->files->count);
+    assert(index < FW_CFG_FILE_SLOTS);
+
+    fw_cfg_add_bytes(s, FW_CFG_FILE_FIRST + index, data, len);
+
+    pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name),
+            filename);
+    for (i = 0; i < index; i++) {
+        if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
+            trace_fw_cfg_add_file_dupe(s, s->files->f[index].name);
+            return;
+        }
+    }
+
+    s->files->f[index].size   = cpu_to_be32(len);
+    s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index);
+    trace_fw_cfg_add_file(s, index, s->files->f[index].name, len);
+
+    s->files->count = cpu_to_be32(index+1);
+}
+
+static void fw_cfg_machine_ready(struct Notifier *n, void *data)
+{
+    size_t len;
+    FWCfgState *s = container_of(n, FWCfgState, machine_ready);
+    char *bootindex = get_boot_devices_list(&len);
+
+    fw_cfg_add_file(s, "bootorder", (uint8_t*)bootindex, len);
+}
+
+FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,
+                        hwaddr ctl_addr, hwaddr data_addr)
+{
+    DeviceState *dev;
+    SysBusDevice *d;
+    FWCfgState *s;
+
+    dev = qdev_create(NULL, "fw_cfg");
+    qdev_prop_set_uint32(dev, "ctl_iobase", ctl_port);
+    qdev_prop_set_uint32(dev, "data_iobase", data_port);
+    qdev_init_nofail(dev);
+    d = SYS_BUS_DEVICE(dev);
+
+    s = DO_UPCAST(FWCfgState, busdev.qdev, dev);
+
+    if (ctl_addr) {
+        sysbus_mmio_map(d, 0, ctl_addr);
+    }
+    if (data_addr) {
+        sysbus_mmio_map(d, 1, data_addr);
+    }
+    fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4);
+    fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16);
+    fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)(display_type == DT_NOGRAPHIC));
+    fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
+    fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu);
+    fw_cfg_bootsplash(s);
+    fw_cfg_reboot(s);
+
+    s->machine_ready.notify = fw_cfg_machine_ready;
+    qemu_add_machine_init_done_notifier(&s->machine_ready);
+
+    return s;
+}
+
+static int fw_cfg_init1(SysBusDevice *dev)
+{
+    FWCfgState *s = FROM_SYSBUS(FWCfgState, dev);
+
+    memory_region_init_io(&s->ctl_iomem, &fw_cfg_ctl_mem_ops, s,
+                          "fwcfg.ctl", FW_CFG_SIZE);
+    sysbus_init_mmio(dev, &s->ctl_iomem);
+    memory_region_init_io(&s->data_iomem, &fw_cfg_data_mem_ops, s,
+                          "fwcfg.data", FW_CFG_DATA_SIZE);
+    sysbus_init_mmio(dev, &s->data_iomem);
+    /* In case ctl and data overlap: */
+    memory_region_init_io(&s->comb_iomem, &fw_cfg_comb_mem_ops, s,
+                          "fwcfg", FW_CFG_SIZE);
+
+    if (s->ctl_iobase + 1 == s->data_iobase) {
+        sysbus_add_io(dev, s->ctl_iobase, &s->comb_iomem);
+    } else {
+        if (s->ctl_iobase) {
+            sysbus_add_io(dev, s->ctl_iobase, &s->ctl_iomem);
+        }
+        if (s->data_iobase) {
+            sysbus_add_io(dev, s->data_iobase, &s->data_iomem);
+        }
+    }
+    return 0;
+}
+
+static Property fw_cfg_properties[] = {
+    DEFINE_PROP_HEX32("ctl_iobase", FWCfgState, ctl_iobase, -1),
+    DEFINE_PROP_HEX32("data_iobase", FWCfgState, data_iobase, -1),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void fw_cfg_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = fw_cfg_init1;
+    dc->no_user = 1;
+    dc->reset = fw_cfg_reset;
+    dc->vmsd = &vmstate_fw_cfg;
+    dc->props = fw_cfg_properties;
+}
+
+static const TypeInfo fw_cfg_info = {
+    .name          = "fw_cfg",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(FWCfgState),
+    .class_init    = fw_cfg_class_init,
+};
+
+static void fw_cfg_register_types(void)
+{
+    type_register_static(&fw_cfg_info);
+}
+
+type_init(fw_cfg_register_types)
diff --git a/hw/nvram/mac_nvram.c b/hw/nvram/mac_nvram.c
new file mode 100644 (file)
index 0000000..5223330
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * PowerMac NVRAM emulation
+ *
+ * Copyright (c) 2005-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/sparc/firmware_abi.h"
+#include "sysemu/sysemu.h"
+#include "hw/ppc/mac.h"
+
+/* debug NVR */
+//#define DEBUG_NVR
+
+#ifdef DEBUG_NVR
+#define NVR_DPRINTF(fmt, ...)                                   \
+    do { printf("NVR: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define NVR_DPRINTF(fmt, ...)
+#endif
+
+#define DEF_SYSTEM_SIZE 0xc10
+
+/* Direct access to NVRAM */
+uint8_t macio_nvram_read(MacIONVRAMState *s, uint32_t addr)
+{
+    uint32_t ret;
+
+    if (addr < s->size) {
+        ret = s->data[addr];
+    } else {
+        ret = -1;
+    }
+    NVR_DPRINTF("read addr %04" PRIx32 " val %" PRIx8 "\n", addr, ret);
+
+    return ret;
+}
+
+void macio_nvram_write(MacIONVRAMState *s, uint32_t addr, uint8_t val)
+{
+    NVR_DPRINTF("write addr %04" PRIx32 " val %" PRIx8 "\n", addr, val);
+    if (addr < s->size) {
+        s->data[addr] = val;
+    }
+}
+
+/* macio style NVRAM device */
+static void macio_nvram_writeb(void *opaque, hwaddr addr,
+                               uint64_t value, unsigned size)
+{
+    MacIONVRAMState *s = opaque;
+
+    addr = (addr >> s->it_shift) & (s->size - 1);
+    s->data[addr] = value;
+    NVR_DPRINTF("writeb addr %04" PHYS_PRIx " val %" PRIx64 "\n", addr, value);
+}
+
+static uint64_t macio_nvram_readb(void *opaque, hwaddr addr,
+                                  unsigned size)
+{
+    MacIONVRAMState *s = opaque;
+    uint32_t value;
+
+    addr = (addr >> s->it_shift) & (s->size - 1);
+    value = s->data[addr];
+    NVR_DPRINTF("readb addr %04x val %x\n", (int)addr, value);
+
+    return value;
+}
+
+static const MemoryRegionOps macio_nvram_ops = {
+    .read = macio_nvram_readb,
+    .write = macio_nvram_writeb,
+    .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static const VMStateDescription vmstate_macio_nvram = {
+    .name = "macio_nvram",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_VBUFFER_UINT32(data, MacIONVRAMState, 0, NULL, 0, size),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+
+static void macio_nvram_reset(DeviceState *dev)
+{
+}
+
+static void macio_nvram_realizefn(DeviceState *dev, Error **errp)
+{
+    SysBusDevice *d = SYS_BUS_DEVICE(dev);
+    MacIONVRAMState *s = MACIO_NVRAM(dev);
+
+    s->data = g_malloc0(s->size);
+
+    memory_region_init_io(&s->mem, &macio_nvram_ops, s, "macio-nvram",
+                          s->size << s->it_shift);
+    sysbus_init_mmio(d, &s->mem);
+}
+
+static void macio_nvram_unrealizefn(DeviceState *dev, Error **errp)
+{
+    MacIONVRAMState *s = MACIO_NVRAM(dev);
+
+    g_free(s->data);
+}
+
+static Property macio_nvram_properties[] = {
+    DEFINE_PROP_UINT32("size", MacIONVRAMState, size, 0),
+    DEFINE_PROP_UINT32("it_shift", MacIONVRAMState, it_shift, 0),
+    DEFINE_PROP_END_OF_LIST()
+};
+
+static void macio_nvram_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->realize = macio_nvram_realizefn;
+    dc->unrealize = macio_nvram_unrealizefn;
+    dc->reset = macio_nvram_reset;
+    dc->vmsd = &vmstate_macio_nvram;
+    dc->props = macio_nvram_properties;
+}
+
+static const TypeInfo macio_nvram_type_info = {
+    .name = TYPE_MACIO_NVRAM,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(MacIONVRAMState),
+    .class_init = macio_nvram_class_init,
+};
+
+static void macio_nvram_register_types(void)
+{
+    type_register_static(&macio_nvram_type_info);
+}
+
+/* Set up a system OpenBIOS NVRAM partition */
+void pmac_format_nvram_partition (MacIONVRAMState *nvr, int len)
+{
+    unsigned int i;
+    uint32_t start = 0, end;
+    struct OpenBIOS_nvpart_v1 *part_header;
+
+    // OpenBIOS nvram variables
+    // Variable partition
+    part_header = (struct OpenBIOS_nvpart_v1 *)nvr->data;
+    part_header->signature = OPENBIOS_PART_SYSTEM;
+    pstrcpy(part_header->name, sizeof(part_header->name), "system");
+
+    end = start + sizeof(struct OpenBIOS_nvpart_v1);
+    for (i = 0; i < nb_prom_envs; i++)
+        end = OpenBIOS_set_var(nvr->data, end, prom_envs[i]);
+
+    // End marker
+    nvr->data[end++] = '\0';
+
+    end = start + ((end - start + 15) & ~15);
+    /* XXX: OpenBIOS is not able to grow up a partition. Leave some space for
+       new variables. */
+    if (end < DEF_SYSTEM_SIZE)
+        end = DEF_SYSTEM_SIZE;
+    OpenBIOS_finish_partition(part_header, end - start);
+
+    // free partition
+    start = end;
+    part_header = (struct OpenBIOS_nvpart_v1 *)&nvr->data[start];
+    part_header->signature = OPENBIOS_PART_FREE;
+    pstrcpy(part_header->name, sizeof(part_header->name), "free");
+
+    end = len;
+    OpenBIOS_finish_partition(part_header, end - start);
+}
+
+type_init(macio_nvram_register_types)
diff --git a/hw/opencores_eth.c b/hw/opencores_eth.c
deleted file mode 100644 (file)
index be64bf2..0000000
+++ /dev/null
@@ -1,733 +0,0 @@
-/*
- * OpenCores Ethernet MAC 10/100 + subset of
- * National Semiconductors DP83848C 10/100 PHY
- *
- * http://opencores.org/svnget,ethmac?file=%2Ftrunk%2F%2Fdoc%2Feth_speci.pdf
- * http://cache.national.com/ds/DP/DP83848C.pdf
- *
- * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of the Open Source and Linux Lab nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-#include "net/net.h"
-#include "sysemu/sysemu.h"
-#include "trace.h"
-
-/* RECSMALL is not used because it breaks tap networking in linux:
- * incoming ARP responses are too short
- */
-#undef USE_RECSMALL
-
-#define GET_FIELD(v, field) (((v) & (field)) >> (field ## _LBN))
-#define GET_REGBIT(s, reg, field) ((s)->regs[reg] & (reg ## _ ## field))
-#define GET_REGFIELD(s, reg, field) \
-    GET_FIELD((s)->regs[reg], reg ## _ ## field)
-
-#define SET_FIELD(v, field, data) \
-    ((v) = (((v) & ~(field)) | (((data) << (field ## _LBN)) & (field))))
-#define SET_REGFIELD(s, reg, field, data) \
-    SET_FIELD((s)->regs[reg], reg ## _ ## field, data)
-
-/* PHY MII registers */
-enum {
-    MII_BMCR,
-    MII_BMSR,
-    MII_PHYIDR1,
-    MII_PHYIDR2,
-    MII_ANAR,
-    MII_ANLPAR,
-    MII_REG_MAX = 16,
-};
-
-typedef struct Mii {
-    uint16_t regs[MII_REG_MAX];
-    bool link_ok;
-} Mii;
-
-static void mii_set_link(Mii *s, bool link_ok)
-{
-    if (link_ok) {
-        s->regs[MII_BMSR] |= 0x4;
-        s->regs[MII_ANLPAR] |= 0x01e1;
-    } else {
-        s->regs[MII_BMSR] &= ~0x4;
-        s->regs[MII_ANLPAR] &= 0x01ff;
-    }
-    s->link_ok = link_ok;
-}
-
-static void mii_reset(Mii *s)
-{
-    memset(s->regs, 0, sizeof(s->regs));
-    s->regs[MII_BMCR] = 0x1000;
-    s->regs[MII_BMSR] = 0x7848; /* no ext regs */
-    s->regs[MII_PHYIDR1] = 0x2000;
-    s->regs[MII_PHYIDR2] = 0x5c90;
-    s->regs[MII_ANAR] = 0x01e1;
-    mii_set_link(s, s->link_ok);
-}
-
-static void mii_ro(Mii *s, uint16_t v)
-{
-}
-
-static void mii_write_bmcr(Mii *s, uint16_t v)
-{
-    if (v & 0x8000) {
-        mii_reset(s);
-    } else {
-        s->regs[MII_BMCR] = v;
-    }
-}
-
-static void mii_write_host(Mii *s, unsigned idx, uint16_t v)
-{
-    static void (*reg_write[MII_REG_MAX])(Mii *s, uint16_t v) = {
-        [MII_BMCR] = mii_write_bmcr,
-        [MII_BMSR] = mii_ro,
-        [MII_PHYIDR1] = mii_ro,
-        [MII_PHYIDR2] = mii_ro,
-    };
-
-    if (idx < MII_REG_MAX) {
-        trace_open_eth_mii_write(idx, v);
-        if (reg_write[idx]) {
-            reg_write[idx](s, v);
-        } else {
-            s->regs[idx] = v;
-        }
-    }
-}
-
-static uint16_t mii_read_host(Mii *s, unsigned idx)
-{
-    trace_open_eth_mii_read(idx, s->regs[idx]);
-    return s->regs[idx];
-}
-
-/* OpenCores Ethernet registers */
-enum {
-    MODER,
-    INT_SOURCE,
-    INT_MASK,
-    IPGT,
-    IPGR1,
-    IPGR2,
-    PACKETLEN,
-    COLLCONF,
-    TX_BD_NUM,
-    CTRLMODER,
-    MIIMODER,
-    MIICOMMAND,
-    MIIADDRESS,
-    MIITX_DATA,
-    MIIRX_DATA,
-    MIISTATUS,
-    MAC_ADDR0,
-    MAC_ADDR1,
-    HASH0,
-    HASH1,
-    TXCTRL,
-    REG_MAX,
-};
-
-enum {
-    MODER_RECSMALL = 0x10000,
-    MODER_PAD = 0x8000,
-    MODER_HUGEN = 0x4000,
-    MODER_RST = 0x800,
-    MODER_LOOPBCK = 0x80,
-    MODER_PRO = 0x20,
-    MODER_IAM = 0x10,
-    MODER_BRO = 0x8,
-    MODER_TXEN = 0x2,
-    MODER_RXEN = 0x1,
-};
-
-enum {
-    INT_SOURCE_RXB = 0x4,
-    INT_SOURCE_TXB = 0x1,
-};
-
-enum {
-    PACKETLEN_MINFL = 0xffff0000,
-    PACKETLEN_MINFL_LBN = 16,
-    PACKETLEN_MAXFL = 0xffff,
-    PACKETLEN_MAXFL_LBN = 0,
-};
-
-enum {
-    MIICOMMAND_WCTRLDATA = 0x4,
-    MIICOMMAND_RSTAT = 0x2,
-    MIICOMMAND_SCANSTAT = 0x1,
-};
-
-enum {
-    MIIADDRESS_RGAD = 0x1f00,
-    MIIADDRESS_RGAD_LBN = 8,
-    MIIADDRESS_FIAD = 0x1f,
-    MIIADDRESS_FIAD_LBN = 0,
-};
-
-enum {
-    MIITX_DATA_CTRLDATA = 0xffff,
-    MIITX_DATA_CTRLDATA_LBN = 0,
-};
-
-enum {
-    MIIRX_DATA_PRSD = 0xffff,
-    MIIRX_DATA_PRSD_LBN = 0,
-};
-
-enum {
-    MIISTATUS_LINKFAIL = 0x1,
-    MIISTATUS_LINKFAIL_LBN = 0,
-};
-
-enum {
-    MAC_ADDR0_BYTE2 = 0xff000000,
-    MAC_ADDR0_BYTE2_LBN = 24,
-    MAC_ADDR0_BYTE3 = 0xff0000,
-    MAC_ADDR0_BYTE3_LBN = 16,
-    MAC_ADDR0_BYTE4 = 0xff00,
-    MAC_ADDR0_BYTE4_LBN = 8,
-    MAC_ADDR0_BYTE5 = 0xff,
-    MAC_ADDR0_BYTE5_LBN = 0,
-};
-
-enum {
-    MAC_ADDR1_BYTE0 = 0xff00,
-    MAC_ADDR1_BYTE0_LBN = 8,
-    MAC_ADDR1_BYTE1 = 0xff,
-    MAC_ADDR1_BYTE1_LBN = 0,
-};
-
-enum {
-    TXD_LEN = 0xffff0000,
-    TXD_LEN_LBN = 16,
-    TXD_RD = 0x8000,
-    TXD_IRQ = 0x4000,
-    TXD_WR = 0x2000,
-    TXD_PAD = 0x1000,
-    TXD_CRC = 0x800,
-    TXD_UR = 0x100,
-    TXD_RTRY = 0xf0,
-    TXD_RTRY_LBN = 4,
-    TXD_RL = 0x8,
-    TXD_LC = 0x4,
-    TXD_DF = 0x2,
-    TXD_CS = 0x1,
-};
-
-enum {
-    RXD_LEN = 0xffff0000,
-    RXD_LEN_LBN = 16,
-    RXD_E = 0x8000,
-    RXD_IRQ = 0x4000,
-    RXD_WRAP = 0x2000,
-    RXD_CF = 0x100,
-    RXD_M = 0x80,
-    RXD_OR = 0x40,
-    RXD_IS = 0x20,
-    RXD_DN = 0x10,
-    RXD_TL = 0x8,
-    RXD_SF = 0x4,
-    RXD_CRC = 0x2,
-    RXD_LC = 0x1,
-};
-
-typedef struct desc {
-    uint32_t len_flags;
-    uint32_t buf_ptr;
-} desc;
-
-#define DEFAULT_PHY 1
-
-typedef struct OpenEthState {
-    SysBusDevice dev;
-    NICState *nic;
-    NICConf conf;
-    MemoryRegion reg_io;
-    MemoryRegion desc_io;
-    qemu_irq irq;
-
-    Mii mii;
-    uint32_t regs[REG_MAX];
-    unsigned tx_desc;
-    unsigned rx_desc;
-    desc desc[128];
-} OpenEthState;
-
-static desc *rx_desc(OpenEthState *s)
-{
-    return s->desc + s->rx_desc;
-}
-
-static desc *tx_desc(OpenEthState *s)
-{
-    return s->desc + s->tx_desc;
-}
-
-static void open_eth_update_irq(OpenEthState *s,
-        uint32_t old, uint32_t new)
-{
-    if (!old != !new) {
-        trace_open_eth_update_irq(new);
-        qemu_set_irq(s->irq, new);
-    }
-}
-
-static void open_eth_int_source_write(OpenEthState *s,
-        uint32_t val)
-{
-    uint32_t old_val = s->regs[INT_SOURCE];
-
-    s->regs[INT_SOURCE] = val;
-    open_eth_update_irq(s, old_val & s->regs[INT_MASK],
-            s->regs[INT_SOURCE] & s->regs[INT_MASK]);
-}
-
-static void open_eth_set_link_status(NetClientState *nc)
-{
-    OpenEthState *s = qemu_get_nic_opaque(nc);
-
-    if (GET_REGBIT(s, MIICOMMAND, SCANSTAT)) {
-        SET_REGFIELD(s, MIISTATUS, LINKFAIL, nc->link_down);
-    }
-    mii_set_link(&s->mii, !nc->link_down);
-}
-
-static void open_eth_reset(void *opaque)
-{
-    OpenEthState *s = opaque;
-
-    memset(s->regs, 0, sizeof(s->regs));
-    s->regs[MODER] = 0xa000;
-    s->regs[IPGT] = 0x12;
-    s->regs[IPGR1] = 0xc;
-    s->regs[IPGR2] = 0x12;
-    s->regs[PACKETLEN] = 0x400600;
-    s->regs[COLLCONF] = 0xf003f;
-    s->regs[TX_BD_NUM] = 0x40;
-    s->regs[MIIMODER] = 0x64;
-
-    s->tx_desc = 0;
-    s->rx_desc = 0x40;
-
-    mii_reset(&s->mii);
-    open_eth_set_link_status(qemu_get_queue(s->nic));
-}
-
-static int open_eth_can_receive(NetClientState *nc)
-{
-    OpenEthState *s = qemu_get_nic_opaque(nc);
-
-    return GET_REGBIT(s, MODER, RXEN) &&
-        (s->regs[TX_BD_NUM] < 0x80) &&
-        (rx_desc(s)->len_flags & RXD_E);
-}
-
-static ssize_t open_eth_receive(NetClientState *nc,
-        const uint8_t *buf, size_t size)
-{
-    OpenEthState *s = qemu_get_nic_opaque(nc);
-    size_t maxfl = GET_REGFIELD(s, PACKETLEN, MAXFL);
-    size_t minfl = GET_REGFIELD(s, PACKETLEN, MINFL);
-    size_t fcsl = 4;
-    bool miss = true;
-
-    trace_open_eth_receive((unsigned)size);
-
-    if (size >= 6) {
-        static const uint8_t bcast_addr[] = {
-            0xff, 0xff, 0xff, 0xff, 0xff, 0xff
-        };
-        if (memcmp(buf, bcast_addr, sizeof(bcast_addr)) == 0) {
-            miss = GET_REGBIT(s, MODER, BRO);
-        } else if ((buf[0] & 0x1) || GET_REGBIT(s, MODER, IAM)) {
-            unsigned mcast_idx = compute_mcast_idx(buf);
-            miss = !(s->regs[HASH0 + mcast_idx / 32] &
-                    (1 << (mcast_idx % 32)));
-            trace_open_eth_receive_mcast(
-                    mcast_idx, s->regs[HASH0], s->regs[HASH1]);
-        } else {
-            miss = GET_REGFIELD(s, MAC_ADDR1, BYTE0) != buf[0] ||
-                GET_REGFIELD(s, MAC_ADDR1, BYTE1) != buf[1] ||
-                GET_REGFIELD(s, MAC_ADDR0, BYTE2) != buf[2] ||
-                GET_REGFIELD(s, MAC_ADDR0, BYTE3) != buf[3] ||
-                GET_REGFIELD(s, MAC_ADDR0, BYTE4) != buf[4] ||
-                GET_REGFIELD(s, MAC_ADDR0, BYTE5) != buf[5];
-        }
-    }
-
-    if (miss && !GET_REGBIT(s, MODER, PRO)) {
-        trace_open_eth_receive_reject();
-        return size;
-    }
-
-#ifdef USE_RECSMALL
-    if (GET_REGBIT(s, MODER, RECSMALL) || size >= minfl) {
-#else
-    {
-#endif
-        static const uint8_t zero[64] = {0};
-        desc *desc = rx_desc(s);
-        size_t copy_size = GET_REGBIT(s, MODER, HUGEN) ? 65536 : maxfl;
-
-        desc->len_flags &= ~(RXD_CF | RXD_M | RXD_OR |
-                RXD_IS | RXD_DN | RXD_TL | RXD_SF | RXD_CRC | RXD_LC);
-
-        if (copy_size > size) {
-            copy_size = size;
-        } else {
-            fcsl = 0;
-        }
-        if (miss) {
-            desc->len_flags |= RXD_M;
-        }
-        if (GET_REGBIT(s, MODER, HUGEN) && size > maxfl) {
-            desc->len_flags |= RXD_TL;
-        }
-#ifdef USE_RECSMALL
-        if (size < minfl) {
-            desc->len_flags |= RXD_SF;
-        }
-#endif
-
-        cpu_physical_memory_write(desc->buf_ptr, buf, copy_size);
-
-        if (GET_REGBIT(s, MODER, PAD) && copy_size < minfl) {
-            if (minfl - copy_size > fcsl) {
-                fcsl = 0;
-            } else {
-                fcsl -= minfl - copy_size;
-            }
-            while (copy_size < minfl) {
-                size_t zero_sz = minfl - copy_size < sizeof(zero) ?
-                    minfl - copy_size : sizeof(zero);
-
-                cpu_physical_memory_write(desc->buf_ptr + copy_size,
-                        zero, zero_sz);
-                copy_size += zero_sz;
-            }
-        }
-
-        /* There's no FCS in the frames handed to us by the QEMU, zero fill it.
-         * Don't do it if the frame is cut at the MAXFL or padded with 4 or
-         * more bytes to the MINFL.
-         */
-        cpu_physical_memory_write(desc->buf_ptr + copy_size, zero, fcsl);
-        copy_size += fcsl;
-
-        SET_FIELD(desc->len_flags, RXD_LEN, copy_size);
-
-        if ((desc->len_flags & RXD_WRAP) || s->rx_desc == 0x7f) {
-            s->rx_desc = s->regs[TX_BD_NUM];
-        } else {
-            ++s->rx_desc;
-        }
-        desc->len_flags &= ~RXD_E;
-
-        trace_open_eth_receive_desc(desc->buf_ptr, desc->len_flags);
-
-        if (desc->len_flags & RXD_IRQ) {
-            open_eth_int_source_write(s,
-                    s->regs[INT_SOURCE] | INT_SOURCE_RXB);
-        }
-    }
-    return size;
-}
-
-static void open_eth_cleanup(NetClientState *nc)
-{
-}
-
-static NetClientInfo net_open_eth_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
-    .size = sizeof(NICState),
-    .can_receive = open_eth_can_receive,
-    .receive = open_eth_receive,
-    .cleanup = open_eth_cleanup,
-    .link_status_changed = open_eth_set_link_status,
-};
-
-static void open_eth_start_xmit(OpenEthState *s, desc *tx)
-{
-    uint8_t buf[65536];
-    unsigned len = GET_FIELD(tx->len_flags, TXD_LEN);
-    unsigned tx_len = len;
-
-    if ((tx->len_flags & TXD_PAD) &&
-            tx_len < GET_REGFIELD(s, PACKETLEN, MINFL)) {
-        tx_len = GET_REGFIELD(s, PACKETLEN, MINFL);
-    }
-    if (!GET_REGBIT(s, MODER, HUGEN) &&
-            tx_len > GET_REGFIELD(s, PACKETLEN, MAXFL)) {
-        tx_len = GET_REGFIELD(s, PACKETLEN, MAXFL);
-    }
-
-    trace_open_eth_start_xmit(tx->buf_ptr, len, tx_len);
-
-    if (len > tx_len) {
-        len = tx_len;
-    }
-    cpu_physical_memory_read(tx->buf_ptr, buf, len);
-    if (tx_len > len) {
-        memset(buf + len, 0, tx_len - len);
-    }
-    qemu_send_packet(qemu_get_queue(s->nic), buf, tx_len);
-
-    if (tx->len_flags & TXD_WR) {
-        s->tx_desc = 0;
-    } else {
-        ++s->tx_desc;
-        if (s->tx_desc >= s->regs[TX_BD_NUM]) {
-            s->tx_desc = 0;
-        }
-    }
-    tx->len_flags &= ~(TXD_RD | TXD_UR |
-            TXD_RTRY | TXD_RL | TXD_LC | TXD_DF | TXD_CS);
-    if (tx->len_flags & TXD_IRQ) {
-        open_eth_int_source_write(s, s->regs[INT_SOURCE] | INT_SOURCE_TXB);
-    }
-
-}
-
-static void open_eth_check_start_xmit(OpenEthState *s)
-{
-    desc *tx = tx_desc(s);
-    if (GET_REGBIT(s, MODER, TXEN) && s->regs[TX_BD_NUM] > 0 &&
-            (tx->len_flags & TXD_RD) &&
-            GET_FIELD(tx->len_flags, TXD_LEN) > 4) {
-        open_eth_start_xmit(s, tx);
-    }
-}
-
-static uint64_t open_eth_reg_read(void *opaque,
-        hwaddr addr, unsigned int size)
-{
-    static uint32_t (*reg_read[REG_MAX])(OpenEthState *s) = {
-    };
-    OpenEthState *s = opaque;
-    unsigned idx = addr / 4;
-    uint64_t v = 0;
-
-    if (idx < REG_MAX) {
-        if (reg_read[idx]) {
-            v = reg_read[idx](s);
-        } else {
-            v = s->regs[idx];
-        }
-    }
-    trace_open_eth_reg_read((uint32_t)addr, (uint32_t)v);
-    return v;
-}
-
-static void open_eth_ro(OpenEthState *s, uint32_t val)
-{
-}
-
-static void open_eth_moder_host_write(OpenEthState *s, uint32_t val)
-{
-    uint32_t set = val & ~s->regs[MODER];
-
-    if (set & MODER_RST) {
-        open_eth_reset(s);
-    }
-
-    s->regs[MODER] = val;
-
-    if (set & MODER_RXEN) {
-        s->rx_desc = s->regs[TX_BD_NUM];
-    }
-    if (set & MODER_TXEN) {
-        s->tx_desc = 0;
-        open_eth_check_start_xmit(s);
-    }
-}
-
-static void open_eth_int_source_host_write(OpenEthState *s, uint32_t val)
-{
-    uint32_t old = s->regs[INT_SOURCE];
-
-    s->regs[INT_SOURCE] &= ~val;
-    open_eth_update_irq(s, old & s->regs[INT_MASK],
-            s->regs[INT_SOURCE] & s->regs[INT_MASK]);
-}
-
-static void open_eth_int_mask_host_write(OpenEthState *s, uint32_t val)
-{
-    uint32_t old = s->regs[INT_MASK];
-
-    s->regs[INT_MASK] = val;
-    open_eth_update_irq(s, s->regs[INT_SOURCE] & old,
-            s->regs[INT_SOURCE] & s->regs[INT_MASK]);
-}
-
-static void open_eth_mii_command_host_write(OpenEthState *s, uint32_t val)
-{
-    unsigned fiad = GET_REGFIELD(s, MIIADDRESS, FIAD);
-    unsigned rgad = GET_REGFIELD(s, MIIADDRESS, RGAD);
-
-    if (val & MIICOMMAND_WCTRLDATA) {
-        if (fiad == DEFAULT_PHY) {
-            mii_write_host(&s->mii, rgad,
-                    GET_REGFIELD(s, MIITX_DATA, CTRLDATA));
-        }
-    }
-    if (val & MIICOMMAND_RSTAT) {
-        if (fiad == DEFAULT_PHY) {
-            SET_REGFIELD(s, MIIRX_DATA, PRSD,
-                    mii_read_host(&s->mii, rgad));
-        } else {
-            s->regs[MIIRX_DATA] = 0xffff;
-        }
-        SET_REGFIELD(s, MIISTATUS, LINKFAIL, qemu_get_queue(s->nic)->link_down);
-    }
-}
-
-static void open_eth_mii_tx_host_write(OpenEthState *s, uint32_t val)
-{
-    SET_REGFIELD(s, MIITX_DATA, CTRLDATA, val);
-    if (GET_REGFIELD(s, MIIADDRESS, FIAD) == DEFAULT_PHY) {
-        mii_write_host(&s->mii, GET_REGFIELD(s, MIIADDRESS, RGAD),
-                GET_REGFIELD(s, MIITX_DATA, CTRLDATA));
-    }
-}
-
-static void open_eth_reg_write(void *opaque,
-        hwaddr addr, uint64_t val, unsigned int size)
-{
-    static void (*reg_write[REG_MAX])(OpenEthState *s, uint32_t val) = {
-        [MODER] = open_eth_moder_host_write,
-        [INT_SOURCE] = open_eth_int_source_host_write,
-        [INT_MASK] = open_eth_int_mask_host_write,
-        [MIICOMMAND] = open_eth_mii_command_host_write,
-        [MIITX_DATA] = open_eth_mii_tx_host_write,
-        [MIISTATUS] = open_eth_ro,
-    };
-    OpenEthState *s = opaque;
-    unsigned idx = addr / 4;
-
-    if (idx < REG_MAX) {
-        trace_open_eth_reg_write((uint32_t)addr, (uint32_t)val);
-        if (reg_write[idx]) {
-            reg_write[idx](s, val);
-        } else {
-            s->regs[idx] = val;
-        }
-    }
-}
-
-static uint64_t open_eth_desc_read(void *opaque,
-        hwaddr addr, unsigned int size)
-{
-    OpenEthState *s = opaque;
-    uint64_t v = 0;
-
-    addr &= 0x3ff;
-    memcpy(&v, (uint8_t *)s->desc + addr, size);
-    trace_open_eth_desc_read((uint32_t)addr, (uint32_t)v);
-    return v;
-}
-
-static void open_eth_desc_write(void *opaque,
-        hwaddr addr, uint64_t val, unsigned int size)
-{
-    OpenEthState *s = opaque;
-
-    addr &= 0x3ff;
-    trace_open_eth_desc_write((uint32_t)addr, (uint32_t)val);
-    memcpy((uint8_t *)s->desc + addr, &val, size);
-    open_eth_check_start_xmit(s);
-}
-
-
-static const MemoryRegionOps open_eth_reg_ops = {
-    .read = open_eth_reg_read,
-    .write = open_eth_reg_write,
-};
-
-static const MemoryRegionOps open_eth_desc_ops = {
-    .read = open_eth_desc_read,
-    .write = open_eth_desc_write,
-};
-
-static int sysbus_open_eth_init(SysBusDevice *dev)
-{
-    OpenEthState *s = DO_UPCAST(OpenEthState, dev, dev);
-
-    memory_region_init_io(&s->reg_io, &open_eth_reg_ops, s,
-            "open_eth.regs", 0x54);
-    sysbus_init_mmio(dev, &s->reg_io);
-
-    memory_region_init_io(&s->desc_io, &open_eth_desc_ops, s,
-            "open_eth.desc", 0x400);
-    sysbus_init_mmio(dev, &s->desc_io);
-
-    sysbus_init_irq(dev, &s->irq);
-
-    s->nic = qemu_new_nic(&net_open_eth_info, &s->conf,
-                          object_get_typename(OBJECT(s)), s->dev.qdev.id, s);
-    return 0;
-}
-
-static void qdev_open_eth_reset(DeviceState *dev)
-{
-    OpenEthState *d = DO_UPCAST(OpenEthState, dev.qdev, dev);
-    open_eth_reset(d);
-}
-
-static Property open_eth_properties[] = {
-    DEFINE_NIC_PROPERTIES(OpenEthState, conf),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void open_eth_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = sysbus_open_eth_init;
-    dc->desc = "Opencores 10/100 Mbit Ethernet";
-    dc->reset = qdev_open_eth_reset;
-    dc->props = open_eth_properties;
-}
-
-static const TypeInfo open_eth_info = {
-    .name          = "open_eth",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(OpenEthState),
-    .class_init    = open_eth_class_init,
-};
-
-static void open_eth_register_types(void)
-{
-    type_register_static(&open_eth_info);
-}
-
-type_init(open_eth_register_types)
diff --git a/hw/pam.c b/hw/pam.c
deleted file mode 100644 (file)
index 7181bd6..0000000
--- a/hw/pam.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * QEMU i440FX/PIIX3 PCI Bridge Emulation
- *
- * Copyright (c) 2006 Fabrice Bellard
- * Copyright (c) 2011 Isaku Yamahata <yamahata at valinux co jp>
- *                    VA Linux Systems Japan K.K.
- * Copyright (c) 2012 Jason Baron <jbaron@redhat.com>
- *
- * Split out from piix_pci.c
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "sysemu/sysemu.h"
-#include "hw/pci-host/pam.h"
-
-void smram_update(MemoryRegion *smram_region, uint8_t smram,
-                  uint8_t smm_enabled)
-{
-    bool smram_enabled;
-
-    smram_enabled = ((smm_enabled && (smram & SMRAM_G_SMRAME)) ||
-                        (smram & SMRAM_D_OPEN));
-    memory_region_set_enabled(smram_region, !smram_enabled);
-}
-
-void smram_set_smm(uint8_t *host_smm_enabled, int smm, uint8_t smram,
-                   MemoryRegion *smram_region)
-{
-    uint8_t smm_enabled = (smm != 0);
-    if (*host_smm_enabled != smm_enabled) {
-        *host_smm_enabled = smm_enabled;
-        smram_update(smram_region, smram, *host_smm_enabled);
-    }
-}
-
-void init_pam(MemoryRegion *ram_memory, MemoryRegion *system_memory,
-              MemoryRegion *pci_address_space, PAMMemoryRegion *mem,
-              uint32_t start, uint32_t size)
-{
-    int i;
-
-    /* RAM */
-    memory_region_init_alias(&mem->alias[3], "pam-ram", ram_memory,
-                             start, size);
-    /* ROM (XXX: not quite correct) */
-    memory_region_init_alias(&mem->alias[1], "pam-rom", ram_memory,
-                             start, size);
-    memory_region_set_readonly(&mem->alias[1], true);
-
-    /* XXX: should distinguish read/write cases */
-    memory_region_init_alias(&mem->alias[0], "pam-pci", pci_address_space,
-                             start, size);
-    memory_region_init_alias(&mem->alias[2], "pam-pci", pci_address_space,
-                             start, size);
-
-    for (i = 0; i < 4; ++i) {
-        memory_region_set_enabled(&mem->alias[i], false);
-        memory_region_add_subregion_overlap(system_memory, start,
-                                            &mem->alias[i], 1);
-    }
-    mem->current = 0;
-}
-
-void pam_update(PAMMemoryRegion *pam, int idx, uint8_t val)
-{
-    assert(0 <= idx && idx <= 12);
-
-    memory_region_set_enabled(&pam->alias[pam->current], false);
-    pam->current = (val >> ((!(idx & 1)) * 4)) & PAM_ATTR_MASK;
-    memory_region_set_enabled(&pam->alias[pam->current], true);
-}
diff --git a/hw/parallel.c b/hw/parallel.c
deleted file mode 100644 (file)
index 863a6fb..0000000
+++ /dev/null
@@ -1,614 +0,0 @@
-/*
- * QEMU Parallel PORT emulation
- *
- * Copyright (c) 2003-2005 Fabrice Bellard
- * Copyright (c) 2007 Marko Kohtala
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "char/char.h"
-#include "hw/isa/isa.h"
-#include "hw/i386/pc.h"
-#include "sysemu/sysemu.h"
-
-//#define DEBUG_PARALLEL
-
-#ifdef DEBUG_PARALLEL
-#define pdebug(fmt, ...) printf("pp: " fmt, ## __VA_ARGS__)
-#else
-#define pdebug(fmt, ...) ((void)0)
-#endif
-
-#define PARA_REG_DATA 0
-#define PARA_REG_STS 1
-#define PARA_REG_CTR 2
-#define PARA_REG_EPP_ADDR 3
-#define PARA_REG_EPP_DATA 4
-
-/*
- * These are the definitions for the Printer Status Register
- */
-#define PARA_STS_BUSY  0x80    /* Busy complement */
-#define PARA_STS_ACK   0x40    /* Acknowledge */
-#define PARA_STS_PAPER 0x20    /* Out of paper */
-#define PARA_STS_ONLINE        0x10    /* Online */
-#define PARA_STS_ERROR 0x08    /* Error complement */
-#define PARA_STS_TMOUT 0x01    /* EPP timeout */
-
-/*
- * These are the definitions for the Printer Control Register
- */
-#define PARA_CTR_DIR   0x20    /* Direction (1=read, 0=write) */
-#define PARA_CTR_INTEN 0x10    /* IRQ Enable */
-#define PARA_CTR_SELECT        0x08    /* Select In complement */
-#define PARA_CTR_INIT  0x04    /* Initialize Printer complement */
-#define PARA_CTR_AUTOLF        0x02    /* Auto linefeed complement */
-#define PARA_CTR_STROBE        0x01    /* Strobe complement */
-
-#define PARA_CTR_SIGNAL (PARA_CTR_SELECT|PARA_CTR_INIT|PARA_CTR_AUTOLF|PARA_CTR_STROBE)
-
-typedef struct ParallelState {
-    MemoryRegion iomem;
-    uint8_t dataw;
-    uint8_t datar;
-    uint8_t status;
-    uint8_t control;
-    qemu_irq irq;
-    int irq_pending;
-    CharDriverState *chr;
-    int hw_driver;
-    int epp_timeout;
-    uint32_t last_read_offset; /* For debugging */
-    /* Memory-mapped interface */
-    int it_shift;
-} ParallelState;
-
-typedef struct ISAParallelState {
-    ISADevice dev;
-    uint32_t index;
-    uint32_t iobase;
-    uint32_t isairq;
-    ParallelState state;
-} ISAParallelState;
-
-static void parallel_update_irq(ParallelState *s)
-{
-    if (s->irq_pending)
-        qemu_irq_raise(s->irq);
-    else
-        qemu_irq_lower(s->irq);
-}
-
-static void
-parallel_ioport_write_sw(void *opaque, uint32_t addr, uint32_t val)
-{
-    ParallelState *s = opaque;
-
-    pdebug("write addr=0x%02x val=0x%02x\n", addr, val);
-
-    addr &= 7;
-    switch(addr) {
-    case PARA_REG_DATA:
-        s->dataw = val;
-        parallel_update_irq(s);
-        break;
-    case PARA_REG_CTR:
-        val |= 0xc0;
-        if ((val & PARA_CTR_INIT) == 0 ) {
-            s->status = PARA_STS_BUSY;
-            s->status |= PARA_STS_ACK;
-            s->status |= PARA_STS_ONLINE;
-            s->status |= PARA_STS_ERROR;
-        }
-        else if (val & PARA_CTR_SELECT) {
-            if (val & PARA_CTR_STROBE) {
-                s->status &= ~PARA_STS_BUSY;
-                if ((s->control & PARA_CTR_STROBE) == 0)
-                    qemu_chr_fe_write(s->chr, &s->dataw, 1);
-            } else {
-                if (s->control & PARA_CTR_INTEN) {
-                    s->irq_pending = 1;
-                }
-            }
-        }
-        parallel_update_irq(s);
-        s->control = val;
-        break;
-    }
-}
-
-static void parallel_ioport_write_hw(void *opaque, uint32_t addr, uint32_t val)
-{
-    ParallelState *s = opaque;
-    uint8_t parm = val;
-    int dir;
-
-    /* Sometimes programs do several writes for timing purposes on old
-       HW. Take care not to waste time on writes that do nothing. */
-
-    s->last_read_offset = ~0U;
-
-    addr &= 7;
-    switch(addr) {
-    case PARA_REG_DATA:
-        if (s->dataw == val)
-            return;
-        pdebug("wd%02x\n", val);
-        qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_WRITE_DATA, &parm);
-        s->dataw = val;
-        break;
-    case PARA_REG_STS:
-        pdebug("ws%02x\n", val);
-        if (val & PARA_STS_TMOUT)
-            s->epp_timeout = 0;
-        break;
-    case PARA_REG_CTR:
-        val |= 0xc0;
-        if (s->control == val)
-            return;
-        pdebug("wc%02x\n", val);
-
-        if ((val & PARA_CTR_DIR) != (s->control & PARA_CTR_DIR)) {
-            if (val & PARA_CTR_DIR) {
-                dir = 1;
-            } else {
-                dir = 0;
-            }
-            qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_DATA_DIR, &dir);
-            parm &= ~PARA_CTR_DIR;
-        }
-
-        qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_WRITE_CONTROL, &parm);
-        s->control = val;
-        break;
-    case PARA_REG_EPP_ADDR:
-        if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT)
-            /* Controls not correct for EPP address cycle, so do nothing */
-            pdebug("wa%02x s\n", val);
-        else {
-            struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 };
-            if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE_ADDR, &ioarg)) {
-                s->epp_timeout = 1;
-                pdebug("wa%02x t\n", val);
-            }
-            else
-                pdebug("wa%02x\n", val);
-        }
-        break;
-    case PARA_REG_EPP_DATA:
-        if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT)
-            /* Controls not correct for EPP data cycle, so do nothing */
-            pdebug("we%02x s\n", val);
-        else {
-            struct ParallelIOArg ioarg = { .buffer = &parm, .count = 1 };
-            if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg)) {
-                s->epp_timeout = 1;
-                pdebug("we%02x t\n", val);
-            }
-            else
-                pdebug("we%02x\n", val);
-        }
-        break;
-    }
-}
-
-static void
-parallel_ioport_eppdata_write_hw2(void *opaque, uint32_t addr, uint32_t val)
-{
-    ParallelState *s = opaque;
-    uint16_t eppdata = cpu_to_le16(val);
-    int err;
-    struct ParallelIOArg ioarg = {
-        .buffer = &eppdata, .count = sizeof(eppdata)
-    };
-    if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) {
-        /* Controls not correct for EPP data cycle, so do nothing */
-        pdebug("we%04x s\n", val);
-        return;
-    }
-    err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg);
-    if (err) {
-        s->epp_timeout = 1;
-        pdebug("we%04x t\n", val);
-    }
-    else
-        pdebug("we%04x\n", val);
-}
-
-static void
-parallel_ioport_eppdata_write_hw4(void *opaque, uint32_t addr, uint32_t val)
-{
-    ParallelState *s = opaque;
-    uint32_t eppdata = cpu_to_le32(val);
-    int err;
-    struct ParallelIOArg ioarg = {
-        .buffer = &eppdata, .count = sizeof(eppdata)
-    };
-    if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) {
-        /* Controls not correct for EPP data cycle, so do nothing */
-        pdebug("we%08x s\n", val);
-        return;
-    }
-    err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_WRITE, &ioarg);
-    if (err) {
-        s->epp_timeout = 1;
-        pdebug("we%08x t\n", val);
-    }
-    else
-        pdebug("we%08x\n", val);
-}
-
-static uint32_t parallel_ioport_read_sw(void *opaque, uint32_t addr)
-{
-    ParallelState *s = opaque;
-    uint32_t ret = 0xff;
-
-    addr &= 7;
-    switch(addr) {
-    case PARA_REG_DATA:
-        if (s->control & PARA_CTR_DIR)
-            ret = s->datar;
-        else
-            ret = s->dataw;
-        break;
-    case PARA_REG_STS:
-        ret = s->status;
-        s->irq_pending = 0;
-        if ((s->status & PARA_STS_BUSY) == 0 && (s->control & PARA_CTR_STROBE) == 0) {
-            /* XXX Fixme: wait 5 microseconds */
-            if (s->status & PARA_STS_ACK)
-                s->status &= ~PARA_STS_ACK;
-            else {
-                /* XXX Fixme: wait 5 microseconds */
-                s->status |= PARA_STS_ACK;
-                s->status |= PARA_STS_BUSY;
-            }
-        }
-        parallel_update_irq(s);
-        break;
-    case PARA_REG_CTR:
-        ret = s->control;
-        break;
-    }
-    pdebug("read addr=0x%02x val=0x%02x\n", addr, ret);
-    return ret;
-}
-
-static uint32_t parallel_ioport_read_hw(void *opaque, uint32_t addr)
-{
-    ParallelState *s = opaque;
-    uint8_t ret = 0xff;
-    addr &= 7;
-    switch(addr) {
-    case PARA_REG_DATA:
-        qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_DATA, &ret);
-        if (s->last_read_offset != addr || s->datar != ret)
-            pdebug("rd%02x\n", ret);
-        s->datar = ret;
-        break;
-    case PARA_REG_STS:
-        qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &ret);
-        ret &= ~PARA_STS_TMOUT;
-        if (s->epp_timeout)
-            ret |= PARA_STS_TMOUT;
-        if (s->last_read_offset != addr || s->status != ret)
-            pdebug("rs%02x\n", ret);
-        s->status = ret;
-        break;
-    case PARA_REG_CTR:
-        /* s->control has some bits fixed to 1. It is zero only when
-           it has not been yet written to.  */
-        if (s->control == 0) {
-            qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_CONTROL, &ret);
-            if (s->last_read_offset != addr)
-                pdebug("rc%02x\n", ret);
-            s->control = ret;
-        }
-        else {
-            ret = s->control;
-            if (s->last_read_offset != addr)
-                pdebug("rc%02x\n", ret);
-        }
-        break;
-    case PARA_REG_EPP_ADDR:
-        if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT))
-            /* Controls not correct for EPP addr cycle, so do nothing */
-            pdebug("ra%02x s\n", ret);
-        else {
-            struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 };
-            if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ_ADDR, &ioarg)) {
-                s->epp_timeout = 1;
-                pdebug("ra%02x t\n", ret);
-            }
-            else
-                pdebug("ra%02x\n", ret);
-        }
-        break;
-    case PARA_REG_EPP_DATA:
-        if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT))
-            /* Controls not correct for EPP data cycle, so do nothing */
-            pdebug("re%02x s\n", ret);
-        else {
-            struct ParallelIOArg ioarg = { .buffer = &ret, .count = 1 };
-            if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg)) {
-                s->epp_timeout = 1;
-                pdebug("re%02x t\n", ret);
-            }
-            else
-                pdebug("re%02x\n", ret);
-        }
-        break;
-    }
-    s->last_read_offset = addr;
-    return ret;
-}
-
-static uint32_t
-parallel_ioport_eppdata_read_hw2(void *opaque, uint32_t addr)
-{
-    ParallelState *s = opaque;
-    uint32_t ret;
-    uint16_t eppdata = ~0;
-    int err;
-    struct ParallelIOArg ioarg = {
-        .buffer = &eppdata, .count = sizeof(eppdata)
-    };
-    if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) {
-        /* Controls not correct for EPP data cycle, so do nothing */
-        pdebug("re%04x s\n", eppdata);
-        return eppdata;
-    }
-    err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg);
-    ret = le16_to_cpu(eppdata);
-
-    if (err) {
-        s->epp_timeout = 1;
-        pdebug("re%04x t\n", ret);
-    }
-    else
-        pdebug("re%04x\n", ret);
-    return ret;
-}
-
-static uint32_t
-parallel_ioport_eppdata_read_hw4(void *opaque, uint32_t addr)
-{
-    ParallelState *s = opaque;
-    uint32_t ret;
-    uint32_t eppdata = ~0U;
-    int err;
-    struct ParallelIOArg ioarg = {
-        .buffer = &eppdata, .count = sizeof(eppdata)
-    };
-    if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != (PARA_CTR_DIR|PARA_CTR_INIT)) {
-        /* Controls not correct for EPP data cycle, so do nothing */
-        pdebug("re%08x s\n", eppdata);
-        return eppdata;
-    }
-    err = qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_EPP_READ, &ioarg);
-    ret = le32_to_cpu(eppdata);
-
-    if (err) {
-        s->epp_timeout = 1;
-        pdebug("re%08x t\n", ret);
-    }
-    else
-        pdebug("re%08x\n", ret);
-    return ret;
-}
-
-static void parallel_ioport_ecp_write(void *opaque, uint32_t addr, uint32_t val)
-{
-    pdebug("wecp%d=%02x\n", addr & 7, val);
-}
-
-static uint32_t parallel_ioport_ecp_read(void *opaque, uint32_t addr)
-{
-    uint8_t ret = 0xff;
-
-    pdebug("recp%d:%02x\n", addr & 7, ret);
-    return ret;
-}
-
-static void parallel_reset(void *opaque)
-{
-    ParallelState *s = opaque;
-
-    s->datar = ~0;
-    s->dataw = ~0;
-    s->status = PARA_STS_BUSY;
-    s->status |= PARA_STS_ACK;
-    s->status |= PARA_STS_ONLINE;
-    s->status |= PARA_STS_ERROR;
-    s->status |= PARA_STS_TMOUT;
-    s->control = PARA_CTR_SELECT;
-    s->control |= PARA_CTR_INIT;
-    s->control |= 0xc0;
-    s->irq_pending = 0;
-    s->hw_driver = 0;
-    s->epp_timeout = 0;
-    s->last_read_offset = ~0U;
-}
-
-static const int isa_parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
-
-static const MemoryRegionPortio isa_parallel_portio_hw_list[] = {
-    { 0, 8, 1,
-      .read = parallel_ioport_read_hw,
-      .write = parallel_ioport_write_hw },
-    { 4, 1, 2,
-      .read = parallel_ioport_eppdata_read_hw2,
-      .write = parallel_ioport_eppdata_write_hw2 },
-    { 4, 1, 4,
-      .read = parallel_ioport_eppdata_read_hw4,
-      .write = parallel_ioport_eppdata_write_hw4 },
-    { 0x400, 8, 1,
-      .read = parallel_ioport_ecp_read,
-      .write = parallel_ioport_ecp_write },
-    PORTIO_END_OF_LIST(),
-};
-
-static const MemoryRegionPortio isa_parallel_portio_sw_list[] = {
-    { 0, 8, 1,
-      .read = parallel_ioport_read_sw,
-      .write = parallel_ioport_write_sw },
-    PORTIO_END_OF_LIST(),
-};
-
-static int parallel_isa_initfn(ISADevice *dev)
-{
-    static int index;
-    ISAParallelState *isa = DO_UPCAST(ISAParallelState, dev, dev);
-    ParallelState *s = &isa->state;
-    int base;
-    uint8_t dummy;
-
-    if (!s->chr) {
-        fprintf(stderr, "Can't create parallel device, empty char device\n");
-        exit(1);
-    }
-
-    if (isa->index == -1)
-        isa->index = index;
-    if (isa->index >= MAX_PARALLEL_PORTS)
-        return -1;
-    if (isa->iobase == -1)
-        isa->iobase = isa_parallel_io[isa->index];
-    index++;
-
-    base = isa->iobase;
-    isa_init_irq(dev, &s->irq, isa->isairq);
-    qemu_register_reset(parallel_reset, s);
-
-    if (qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_PP_READ_STATUS, &dummy) == 0) {
-        s->hw_driver = 1;
-        s->status = dummy;
-    }
-
-    isa_register_portio_list(dev, base,
-                             (s->hw_driver
-                              ? &isa_parallel_portio_hw_list[0]
-                              : &isa_parallel_portio_sw_list[0]),
-                             s, "parallel");
-    return 0;
-}
-
-/* Memory mapped interface */
-static uint32_t parallel_mm_readb (void *opaque, hwaddr addr)
-{
-    ParallelState *s = opaque;
-
-    return parallel_ioport_read_sw(s, addr >> s->it_shift) & 0xFF;
-}
-
-static void parallel_mm_writeb (void *opaque,
-                                hwaddr addr, uint32_t value)
-{
-    ParallelState *s = opaque;
-
-    parallel_ioport_write_sw(s, addr >> s->it_shift, value & 0xFF);
-}
-
-static uint32_t parallel_mm_readw (void *opaque, hwaddr addr)
-{
-    ParallelState *s = opaque;
-
-    return parallel_ioport_read_sw(s, addr >> s->it_shift) & 0xFFFF;
-}
-
-static void parallel_mm_writew (void *opaque,
-                                hwaddr addr, uint32_t value)
-{
-    ParallelState *s = opaque;
-
-    parallel_ioport_write_sw(s, addr >> s->it_shift, value & 0xFFFF);
-}
-
-static uint32_t parallel_mm_readl (void *opaque, hwaddr addr)
-{
-    ParallelState *s = opaque;
-
-    return parallel_ioport_read_sw(s, addr >> s->it_shift);
-}
-
-static void parallel_mm_writel (void *opaque,
-                                hwaddr addr, uint32_t value)
-{
-    ParallelState *s = opaque;
-
-    parallel_ioport_write_sw(s, addr >> s->it_shift, value);
-}
-
-static const MemoryRegionOps parallel_mm_ops = {
-    .old_mmio = {
-        .read = { parallel_mm_readb, parallel_mm_readw, parallel_mm_readl },
-        .write = { parallel_mm_writeb, parallel_mm_writew, parallel_mm_writel },
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-/* If fd is zero, it means that the parallel device uses the console */
-bool parallel_mm_init(MemoryRegion *address_space,
-                      hwaddr base, int it_shift, qemu_irq irq,
-                      CharDriverState *chr)
-{
-    ParallelState *s;
-
-    s = g_malloc0(sizeof(ParallelState));
-    s->irq = irq;
-    s->chr = chr;
-    s->it_shift = it_shift;
-    qemu_register_reset(parallel_reset, s);
-
-    memory_region_init_io(&s->iomem, &parallel_mm_ops, s,
-                          "parallel", 8 << it_shift);
-    memory_region_add_subregion(address_space, base, &s->iomem);
-    return true;
-}
-
-static Property parallel_isa_properties[] = {
-    DEFINE_PROP_UINT32("index", ISAParallelState, index,   -1),
-    DEFINE_PROP_HEX32("iobase", ISAParallelState, iobase,  -1),
-    DEFINE_PROP_UINT32("irq",   ISAParallelState, isairq,  7),
-    DEFINE_PROP_CHR("chardev",  ISAParallelState, state.chr),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void parallel_isa_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
-    ic->init = parallel_isa_initfn;
-    dc->props = parallel_isa_properties;
-}
-
-static const TypeInfo parallel_isa_info = {
-    .name          = "isa-parallel",
-    .parent        = TYPE_ISA_DEVICE,
-    .instance_size = sizeof(ISAParallelState),
-    .class_init    = parallel_isa_class_initfn,
-};
-
-static void parallel_register_types(void)
-{
-    type_register_static(&parallel_isa_info);
-}
-
-type_init(parallel_register_types)
diff --git a/hw/pc87312.c b/hw/pc87312.c
deleted file mode 100644 (file)
index 9f5e185..0000000
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * QEMU National Semiconductor PC87312 (Super I/O)
- *
- * Copyright (c) 2010-2012 Herve Poussineau
- * Copyright (c) 2011-2012 Andreas Färber
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/isa/pc87312.h"
-#include "qemu/error-report.h"
-#include "sysemu/blockdev.h"
-#include "sysemu/sysemu.h"
-#include "char/char.h"
-#include "trace.h"
-
-
-#define REG_FER 0
-#define REG_FAR 1
-#define REG_PTR 2
-
-#define FER_PARALLEL_EN   0x01
-#define FER_UART1_EN      0x02
-#define FER_UART2_EN      0x04
-#define FER_FDC_EN        0x08
-#define FER_FDC_4         0x10
-#define FER_FDC_ADDR      0x20
-#define FER_IDE_EN        0x40
-#define FER_IDE_ADDR      0x80
-
-#define FAR_PARALLEL_ADDR 0x03
-#define FAR_UART1_ADDR    0x0C
-#define FAR_UART2_ADDR    0x30
-#define FAR_UART_3_4      0xC0
-
-#define PTR_POWER_DOWN    0x01
-#define PTR_CLOCK_DOWN    0x02
-#define PTR_PWDN          0x04
-#define PTR_IRQ_5_7       0x08
-#define PTR_UART1_TEST    0x10
-#define PTR_UART2_TEST    0x20
-#define PTR_LOCK_CONF     0x40
-#define PTR_EPP_MODE      0x80
-
-
-/* Parallel port */
-
-static inline bool is_parallel_enabled(PC87312State *s)
-{
-    return s->regs[REG_FER] & FER_PARALLEL_EN;
-}
-
-static const uint32_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 };
-
-static inline uint32_t get_parallel_iobase(PC87312State *s)
-{
-    return parallel_base[s->regs[REG_FAR] & FAR_PARALLEL_ADDR];
-}
-
-static const uint32_t parallel_irq[] = { 5, 7, 5, 0 };
-
-static inline uint32_t get_parallel_irq(PC87312State *s)
-{
-    int idx;
-    idx = (s->regs[REG_FAR] & FAR_PARALLEL_ADDR);
-    if (idx == 0) {
-        return (s->regs[REG_PTR] & PTR_IRQ_5_7) ? 7 : 5;
-    } else {
-        return parallel_irq[idx];
-    }
-}
-
-static inline bool is_parallel_epp(PC87312State *s)
-{
-    return s->regs[REG_PTR] & PTR_EPP_MODE;
-}
-
-
-/* UARTs */
-
-static const uint32_t uart_base[2][4] = {
-    { 0x3e8, 0x338, 0x2e8, 0x220 },
-    { 0x2e8, 0x238, 0x2e0, 0x228 }
-};
-
-static inline uint32_t get_uart_iobase(PC87312State *s, int i)
-{
-    int idx;
-    idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
-    if (idx == 0) {
-        return 0x3f8;
-    } else if (idx == 1) {
-        return 0x2f8;
-    } else {
-        return uart_base[idx & 1][(s->regs[REG_FAR] & FAR_UART_3_4) >> 6];
-    }
-}
-
-static inline uint32_t get_uart_irq(PC87312State *s, int i)
-{
-    int idx;
-    idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
-    return (idx & 1) ? 3 : 4;
-}
-
-static inline bool is_uart_enabled(PC87312State *s, int i)
-{
-    return s->regs[REG_FER] & (FER_UART1_EN << i);
-}
-
-
-/* Floppy controller */
-
-static inline bool is_fdc_enabled(PC87312State *s)
-{
-    return s->regs[REG_FER] & FER_FDC_EN;
-}
-
-static inline uint32_t get_fdc_iobase(PC87312State *s)
-{
-    return (s->regs[REG_FER] & FER_FDC_ADDR) ? 0x370 : 0x3f0;
-}
-
-
-/* IDE controller */
-
-static inline bool is_ide_enabled(PC87312State *s)
-{
-    return s->regs[REG_FER] & FER_IDE_EN;
-}
-
-static inline uint32_t get_ide_iobase(PC87312State *s)
-{
-    return (s->regs[REG_FER] & FER_IDE_ADDR) ? 0x170 : 0x1f0;
-}
-
-
-static void reconfigure_devices(PC87312State *s)
-{
-    error_report("pc87312: unsupported device reconfiguration (%02x %02x %02x)",
-                 s->regs[REG_FER], s->regs[REG_FAR], s->regs[REG_PTR]);
-}
-
-static void pc87312_soft_reset(PC87312State *s)
-{
-    static const uint8_t fer_init[] = {
-        0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4b, 0x4b,
-        0x4b, 0x4b, 0x4b, 0x4b, 0x0f, 0x0f, 0x0f, 0x0f,
-        0x49, 0x49, 0x49, 0x49, 0x07, 0x07, 0x07, 0x07,
-        0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x08, 0x00,
-    };
-    static const uint8_t far_init[] = {
-        0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x00, 0x01,
-        0x01, 0x09, 0x08, 0x08, 0x10, 0x11, 0x39, 0x24,
-        0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x39, 0x24,
-        0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x10, 0x10,
-    };
-    static const uint8_t ptr_init[] = {
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
-    };
-
-    s->read_id_step = 0;
-    s->selected_index = REG_FER;
-
-    s->regs[REG_FER] = fer_init[s->config & 0x1f];
-    s->regs[REG_FAR] = far_init[s->config & 0x1f];
-    s->regs[REG_PTR] = ptr_init[s->config & 0x1f];
-}
-
-static void pc87312_hard_reset(PC87312State *s)
-{
-    pc87312_soft_reset(s);
-}
-
-static void pc87312_io_write(void *opaque, hwaddr addr, uint64_t val,
-                             unsigned int size)
-{
-    PC87312State *s = opaque;
-
-    trace_pc87312_io_write(addr, val);
-
-    if ((addr & 1) == 0) {
-        /* Index register */
-        s->read_id_step = 2;
-        s->selected_index = val;
-    } else {
-        /* Data register */
-        if (s->selected_index < 3) {
-            s->regs[s->selected_index] = val;
-            reconfigure_devices(s);
-        }
-    }
-}
-
-static uint64_t pc87312_io_read(void *opaque, hwaddr addr, unsigned int size)
-{
-    PC87312State *s = opaque;
-    uint32_t val;
-
-    if ((addr & 1) == 0) {
-        /* Index register */
-        if (s->read_id_step++ == 0) {
-            val = 0x88;
-        } else if (s->read_id_step++ == 1) {
-            val = 0;
-        } else {
-            val = s->selected_index;
-        }
-    } else {
-        /* Data register */
-        if (s->selected_index < 3) {
-            val = s->regs[s->selected_index];
-        } else {
-            /* Invalid selected index */
-            val = 0;
-        }
-    }
-
-    trace_pc87312_io_read(addr, val);
-    return val;
-}
-
-static const MemoryRegionOps pc87312_io_ops = {
-    .read  = pc87312_io_read,
-    .write = pc87312_io_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .valid = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-static int pc87312_post_load(void *opaque, int version_id)
-{
-    PC87312State *s = opaque;
-
-    reconfigure_devices(s);
-    return 0;
-}
-
-static void pc87312_reset(DeviceState *d)
-{
-    PC87312State *s = PC87312(d);
-
-    pc87312_soft_reset(s);
-}
-
-static int pc87312_init(ISADevice *dev)
-{
-    PC87312State *s;
-    DeviceState *d;
-    ISADevice *isa;
-    ISABus *bus;
-    CharDriverState *chr;
-    DriveInfo *drive;
-    char name[5];
-    int i;
-
-    s = PC87312(dev);
-    bus = isa_bus_from_device(dev);
-    pc87312_hard_reset(s);
-    isa_register_ioport(dev, &s->io, s->iobase);
-
-    if (is_parallel_enabled(s)) {
-        chr = parallel_hds[0];
-        if (chr == NULL) {
-            chr = qemu_chr_new("par0", "null", NULL);
-        }
-        isa = isa_create(bus, "isa-parallel");
-        d = DEVICE(isa);
-        qdev_prop_set_uint32(d, "index", 0);
-        qdev_prop_set_uint32(d, "iobase", get_parallel_iobase(s));
-        qdev_prop_set_uint32(d, "irq", get_parallel_irq(s));
-        qdev_prop_set_chr(d, "chardev", chr);
-        qdev_init_nofail(d);
-        s->parallel.dev = isa;
-        trace_pc87312_info_parallel(get_parallel_iobase(s),
-                                    get_parallel_irq(s));
-    }
-
-    for (i = 0; i < 2; i++) {
-        if (is_uart_enabled(s, i)) {
-            chr = serial_hds[i];
-            if (chr == NULL) {
-                snprintf(name, sizeof(name), "ser%d", i);
-                chr = qemu_chr_new(name, "null", NULL);
-            }
-            isa = isa_create(bus, "isa-serial");
-            d = DEVICE(isa);
-            qdev_prop_set_uint32(d, "index", i);
-            qdev_prop_set_uint32(d, "iobase", get_uart_iobase(s, i));
-            qdev_prop_set_uint32(d, "irq", get_uart_irq(s, i));
-            qdev_prop_set_chr(d, "chardev", chr);
-            qdev_init_nofail(d);
-            s->uart[i].dev = isa;
-            trace_pc87312_info_serial(i, get_uart_iobase(s, i),
-                                      get_uart_irq(s, i));
-        }
-    }
-
-    if (is_fdc_enabled(s)) {
-        isa = isa_create(bus, "isa-fdc");
-        d = DEVICE(isa);
-        qdev_prop_set_uint32(d, "iobase", get_fdc_iobase(s));
-        qdev_prop_set_uint32(d, "irq", 6);
-        drive = drive_get(IF_FLOPPY, 0, 0);
-        if (drive != NULL) {
-            qdev_prop_set_drive_nofail(d, "driveA", drive->bdrv);
-        }
-        drive = drive_get(IF_FLOPPY, 0, 1);
-        if (drive != NULL) {
-            qdev_prop_set_drive_nofail(d, "driveB", drive->bdrv);
-        }
-        qdev_init_nofail(d);
-        s->fdc.dev = isa;
-        trace_pc87312_info_floppy(get_fdc_iobase(s));
-    }
-
-    if (is_ide_enabled(s)) {
-        isa = isa_create(bus, "isa-ide");
-        d = DEVICE(isa);
-        qdev_prop_set_uint32(d, "iobase", get_ide_iobase(s));
-        qdev_prop_set_uint32(d, "iobase2", get_ide_iobase(s) + 0x206);
-        qdev_prop_set_uint32(d, "irq", 14);
-        qdev_init_nofail(d);
-        s->ide.dev = isa;
-        trace_pc87312_info_ide(get_ide_iobase(s));
-    }
-
-    return 0;
-}
-
-static void pc87312_initfn(Object *obj)
-{
-    PC87312State *s = PC87312(obj);
-
-    memory_region_init_io(&s->io, &pc87312_io_ops, s, "pc87312", 2);
-}
-
-static const VMStateDescription vmstate_pc87312 = {
-    .name = "pc87312",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .post_load = pc87312_post_load,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT8(read_id_step, PC87312State),
-        VMSTATE_UINT8(selected_index, PC87312State),
-        VMSTATE_UINT8_ARRAY(regs, PC87312State, 3),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property pc87312_properties[] = {
-    DEFINE_PROP_HEX32("iobase", PC87312State, iobase, 0x398),
-    DEFINE_PROP_UINT8("config", PC87312State, config, 1),
-    DEFINE_PROP_END_OF_LIST()
-};
-
-static void pc87312_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
-
-    ic->init = pc87312_init;
-    dc->reset = pc87312_reset;
-    dc->vmsd = &vmstate_pc87312;
-    dc->props = pc87312_properties;
-}
-
-static const TypeInfo pc87312_type_info = {
-    .name          = TYPE_PC87312,
-    .parent        = TYPE_ISA_DEVICE,
-    .instance_size = sizeof(PC87312State),
-    .instance_init = pc87312_initfn,
-    .class_init    = pc87312_class_init,
-};
-
-static void pc87312_register_types(void)
-{
-    type_register_static(&pc87312_type_info);
-}
-
-type_init(pc87312_register_types)
index 1cd6cde2ee05713e0849e11167acca6bf0b0bb79..aac5f65e9d99052513bb02d46f9b72f39a964cff 100644 (file)
@@ -4,6 +4,8 @@ common-obj-$(CONFIG_PCI) += shpc.o
 common-obj-$(CONFIG_PCI) += slotid_cap.o
 common-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o
 common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o
-common-obj-$(CONFIG_NO_PCI) += pci-stub.o
 
+common-obj-$(CONFIG_NO_PCI) += pci-stub.o
 common-obj-$(CONFIG_ALL) += pci-stub.o
+
+common-obj-$(CONFIG_PCI) += bridge/ host/
diff --git a/hw/pci/bridge/Makefile.objs b/hw/pci/bridge/Makefile.objs
new file mode 100644 (file)
index 0000000..5dd92d2
--- /dev/null
@@ -0,0 +1,3 @@
+common-obj-y += pci_bridge_dev.o
+common-obj-y += ioh3420.o xio3130_upstream.o xio3130_downstream.o
+common-obj-y += i82801b11.o
diff --git a/hw/pci/bridge/i82801b11.c b/hw/pci/bridge/i82801b11.c
new file mode 100644 (file)
index 0000000..5807a92
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+/*
+ * QEMU i82801b11 dmi-to-pci Bridge Emulation
+ *
+ *  Copyright (c) 2009, 2010, 2011
+ *                Isaku Yamahata <yamahata at valinux co jp>
+ *                VA Linux Systems Japan K.K.
+ *  Copyright (C) 2012 Jason Baron <jbaron@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "hw/pci/pci.h"
+#include "hw/i386/ich9.h"
+
+
+/*****************************************************************************/
+/* ICH9 DMI-to-PCI bridge */
+#define I82801ba_SSVID_OFFSET   0x50
+#define I82801ba_SSVID_SVID     0
+#define I82801ba_SSVID_SSID     0
+
+typedef struct I82801b11Bridge {
+    PCIBridge br;
+} I82801b11Bridge;
+
+static int i82801b11_bridge_initfn(PCIDevice *d)
+{
+    int rc;
+
+    rc = pci_bridge_initfn(d, TYPE_PCI_BUS);
+    if (rc < 0) {
+        return rc;
+    }
+
+    rc = pci_bridge_ssvid_init(d, I82801ba_SSVID_OFFSET,
+                               I82801ba_SSVID_SVID, I82801ba_SSVID_SSID);
+    if (rc < 0) {
+        goto err_bridge;
+    }
+    pci_config_set_prog_interface(d->config, PCI_CLASS_BRDIGE_PCI_INF_SUB);
+    return 0;
+
+err_bridge:
+    pci_bridge_exitfn(d);
+
+    return rc;
+}
+
+static void i82801b11_bridge_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->is_bridge = 1;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_82801BA_11;
+    k->revision = ICH9_D2P_A2_REVISION;
+    k->init = i82801b11_bridge_initfn;
+}
+
+static const TypeInfo i82801b11_bridge_info = {
+    .name          = "i82801b11-bridge",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(I82801b11Bridge),
+    .class_init    = i82801b11_bridge_class_init,
+};
+
+PCIBus *ich9_d2pbr_init(PCIBus *bus, int devfn, int sec_bus)
+{
+    PCIDevice *d;
+    PCIBridge *br;
+    char buf[16];
+    DeviceState *qdev;
+
+    d = pci_create_multifunction(bus, devfn, true, "i82801b11-bridge");
+    if (!d) {
+        return NULL;
+    }
+    br = DO_UPCAST(PCIBridge, dev, d);
+    qdev = &br->dev.qdev;
+
+    snprintf(buf, sizeof(buf), "pci.%d", sec_bus);
+    pci_bridge_map_irq(br, buf, pci_swizzle_map_irq_fn);
+    qdev_init_nofail(qdev);
+
+    return pci_bridge_get_sec_bus(br);
+}
+
+static void d2pbr_register(void)
+{
+    type_register_static(&i82801b11_bridge_info);
+}
+
+type_init(d2pbr_register);
diff --git a/hw/pci/bridge/ioh3420.c b/hw/pci/bridge/ioh3420.c
new file mode 100644 (file)
index 0000000..5cff61e
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * ioh3420.c
+ * Intel X58 north bridge IOH
+ * PCI Express root port device id 3420
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/pci/pci_ids.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/pcie.h"
+#include "hw/ioh3420.h"
+
+#define PCI_DEVICE_ID_IOH_EPORT         0x3420  /* D0:F0 express mode */
+#define PCI_DEVICE_ID_IOH_REV           0x2
+#define IOH_EP_SSVID_OFFSET             0x40
+#define IOH_EP_SSVID_SVID               PCI_VENDOR_ID_INTEL
+#define IOH_EP_SSVID_SSID               0
+#define IOH_EP_MSI_OFFSET               0x60
+#define IOH_EP_MSI_SUPPORTED_FLAGS      PCI_MSI_FLAGS_MASKBIT
+#define IOH_EP_MSI_NR_VECTOR            2
+#define IOH_EP_EXP_OFFSET               0x90
+#define IOH_EP_AER_OFFSET               0x100
+
+/*
+ * If two MSI vector are allocated, Advanced Error Interrupt Message Number
+ * is 1. otherwise 0.
+ * 17.12.5.10 RPERRSTS,  32:27 bit Advanced Error Interrupt Message Number.
+ */
+static uint8_t ioh3420_aer_vector(const PCIDevice *d)
+{
+    switch (msi_nr_vectors_allocated(d)) {
+    case 1:
+        return 0;
+    case 2:
+        return 1;
+    case 4:
+    case 8:
+    case 16:
+    case 32:
+    default:
+        break;
+    }
+    abort();
+    return 0;
+}
+
+static void ioh3420_aer_vector_update(PCIDevice *d)
+{
+    pcie_aer_root_set_vector(d, ioh3420_aer_vector(d));
+}
+
+static void ioh3420_write_config(PCIDevice *d,
+                                   uint32_t address, uint32_t val, int len)
+{
+    uint32_t root_cmd =
+        pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_ROOT_COMMAND);
+
+    pci_bridge_write_config(d, address, val, len);
+    ioh3420_aer_vector_update(d);
+    pcie_cap_slot_write_config(d, address, val, len);
+    pcie_aer_write_config(d, address, val, len);
+    pcie_aer_root_write_config(d, address, val, len, root_cmd);
+}
+
+static void ioh3420_reset(DeviceState *qdev)
+{
+    PCIDevice *d = PCI_DEVICE(qdev);
+
+    ioh3420_aer_vector_update(d);
+    pcie_cap_root_reset(d);
+    pcie_cap_deverr_reset(d);
+    pcie_cap_slot_reset(d);
+    pcie_aer_root_reset(d);
+    pci_bridge_reset(qdev);
+    pci_bridge_disable_base_limit(d);
+}
+
+static int ioh3420_initfn(PCIDevice *d)
+{
+    PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
+    PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
+    PCIESlot *s = DO_UPCAST(PCIESlot, port, p);
+    int rc;
+
+    rc = pci_bridge_initfn(d, TYPE_PCIE_BUS);
+    if (rc < 0) {
+        return rc;
+    }
+
+    pcie_port_init_reg(d);
+
+    rc = pci_bridge_ssvid_init(d, IOH_EP_SSVID_OFFSET,
+                               IOH_EP_SSVID_SVID, IOH_EP_SSVID_SSID);
+    if (rc < 0) {
+        goto err_bridge;
+    }
+    rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR,
+                  IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
+                  IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
+    if (rc < 0) {
+        goto err_bridge;
+    }
+    rc = pcie_cap_init(d, IOH_EP_EXP_OFFSET, PCI_EXP_TYPE_ROOT_PORT, p->port);
+    if (rc < 0) {
+        goto err_msi;
+    }
+    pcie_cap_deverr_init(d);
+    pcie_cap_slot_init(d, s->slot);
+    pcie_chassis_create(s->chassis);
+    rc = pcie_chassis_add_slot(s);
+    if (rc < 0) {
+        goto err_pcie_cap;
+    }
+    pcie_cap_root_init(d);
+    rc = pcie_aer_init(d, IOH_EP_AER_OFFSET);
+    if (rc < 0) {
+        goto err;
+    }
+    pcie_aer_root_init(d);
+    ioh3420_aer_vector_update(d);
+    return 0;
+
+err:
+    pcie_chassis_del_slot(s);
+err_pcie_cap:
+    pcie_cap_exit(d);
+err_msi:
+    msi_uninit(d);
+err_bridge:
+    pci_bridge_exitfn(d);
+    return rc;
+}
+
+static void ioh3420_exitfn(PCIDevice *d)
+{
+    PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
+    PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
+    PCIESlot *s = DO_UPCAST(PCIESlot, port, p);
+
+    pcie_aer_exit(d);
+    pcie_chassis_del_slot(s);
+    pcie_cap_exit(d);
+    msi_uninit(d);
+    pci_bridge_exitfn(d);
+}
+
+PCIESlot *ioh3420_init(PCIBus *bus, int devfn, bool multifunction,
+                         const char *bus_name, pci_map_irq_fn map_irq,
+                         uint8_t port, uint8_t chassis, uint16_t slot)
+{
+    PCIDevice *d;
+    PCIBridge *br;
+    DeviceState *qdev;
+
+    d = pci_create_multifunction(bus, devfn, multifunction, "ioh3420");
+    if (!d) {
+        return NULL;
+    }
+    br = DO_UPCAST(PCIBridge, dev, d);
+
+    qdev = &br->dev.qdev;
+    pci_bridge_map_irq(br, bus_name, map_irq);
+    qdev_prop_set_uint8(qdev, "port", port);
+    qdev_prop_set_uint8(qdev, "chassis", chassis);
+    qdev_prop_set_uint16(qdev, "slot", slot);
+    qdev_init_nofail(qdev);
+
+    return DO_UPCAST(PCIESlot, port, DO_UPCAST(PCIEPort, br, br));
+}
+
+static const VMStateDescription vmstate_ioh3420 = {
+    .name = "ioh-3240-express-root-port",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = pcie_cap_slot_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCIE_DEVICE(port.br.dev, PCIESlot),
+        VMSTATE_STRUCT(port.br.dev.exp.aer_log, PCIESlot, 0,
+                       vmstate_pcie_aer_log, PCIEAERLog),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property ioh3420_properties[] = {
+    DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0),
+    DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0),
+    DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0),
+    DEFINE_PROP_UINT16("aer_log_max", PCIESlot,
+    port.br.dev.exp.aer_log.log_max,
+    PCIE_AER_LOG_MAX_DEFAULT),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ioh3420_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->is_express = 1;
+    k->is_bridge = 1;
+    k->config_write = ioh3420_write_config;
+    k->init = ioh3420_initfn;
+    k->exit = ioh3420_exitfn;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_IOH_EPORT;
+    k->revision = PCI_DEVICE_ID_IOH_REV;
+    dc->desc = "Intel IOH device id 3420 PCIE Root Port";
+    dc->reset = ioh3420_reset;
+    dc->vmsd = &vmstate_ioh3420;
+    dc->props = ioh3420_properties;
+}
+
+static const TypeInfo ioh3420_info = {
+    .name          = "ioh3420",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIESlot),
+    .class_init    = ioh3420_class_init,
+};
+
+static void ioh3420_register_types(void)
+{
+    type_register_static(&ioh3420_info);
+}
+
+type_init(ioh3420_register_types)
+
+/*
+ * Local variables:
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 8
+ *  indent-tab-mode: nil
+ * End:
+ */
diff --git a/hw/pci/bridge/pci_bridge_dev.c b/hw/pci/bridge/pci_bridge_dev.c
new file mode 100644 (file)
index 0000000..971b432
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Standard PCI Bridge Device
+ *
+ * Copyright (c) 2011 Red Hat Inc. Author: Michael S. Tsirkin <mst@redhat.com>
+ *
+ * http://www.pcisig.com/specifications/conventional/pci_to_pci_bridge_architecture/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci/pci_ids.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/shpc.h"
+#include "hw/pci/slotid_cap.h"
+#include "exec/memory.h"
+#include "hw/pci/pci_bus.h"
+
+struct PCIBridgeDev {
+    PCIBridge bridge;
+    MemoryRegion bar;
+    uint8_t chassis_nr;
+#define PCI_BRIDGE_DEV_F_MSI_REQ 0
+    uint32_t flags;
+};
+typedef struct PCIBridgeDev PCIBridgeDev;
+
+static int pci_bridge_dev_initfn(PCIDevice *dev)
+{
+    PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev);
+    PCIBridgeDev *bridge_dev = DO_UPCAST(PCIBridgeDev, bridge, br);
+    int err;
+
+    err = pci_bridge_initfn(dev, TYPE_PCI_BUS);
+    if (err) {
+        goto bridge_error;
+    }
+    memory_region_init(&bridge_dev->bar, "shpc-bar", shpc_bar_size(dev));
+    err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0);
+    if (err) {
+        goto shpc_error;
+    }
+    err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0);
+    if (err) {
+        goto slotid_error;
+    }
+    if ((bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_MSI_REQ)) &&
+        msi_supported) {
+        err = msi_init(dev, 0, 1, true, true);
+        if (err < 0) {
+            goto msi_error;
+        }
+    }
+    /* TODO: spec recommends using 64 bit prefetcheable BAR.
+     * Check whether that works well. */
+    pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY |
+                    PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar);
+    dev->config[PCI_INTERRUPT_PIN] = 0x1;
+    return 0;
+msi_error:
+    slotid_cap_cleanup(dev);
+slotid_error:
+    shpc_cleanup(dev, &bridge_dev->bar);
+shpc_error:
+    memory_region_destroy(&bridge_dev->bar);
+    pci_bridge_exitfn(dev);
+bridge_error:
+    return err;
+}
+
+static void pci_bridge_dev_exitfn(PCIDevice *dev)
+{
+    PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev);
+    PCIBridgeDev *bridge_dev = DO_UPCAST(PCIBridgeDev, bridge, br);
+    if (msi_present(dev)) {
+        msi_uninit(dev);
+    }
+    slotid_cap_cleanup(dev);
+    shpc_cleanup(dev, &bridge_dev->bar);
+    memory_region_destroy(&bridge_dev->bar);
+    pci_bridge_exitfn(dev);
+}
+
+static void pci_bridge_dev_write_config(PCIDevice *d,
+                                        uint32_t address, uint32_t val, int len)
+{
+    pci_bridge_write_config(d, address, val, len);
+    if (msi_present(d)) {
+        msi_write_config(d, address, val, len);
+    }
+    shpc_cap_write_config(d, address, val, len);
+}
+
+static void qdev_pci_bridge_dev_reset(DeviceState *qdev)
+{
+    PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev);
+
+    pci_bridge_reset(qdev);
+    shpc_reset(dev);
+}
+
+static Property pci_bridge_dev_properties[] = {
+                    /* Note: 0 is not a legal chassis number. */
+    DEFINE_PROP_UINT8("chassis_nr", PCIBridgeDev, chassis_nr, 0),
+    DEFINE_PROP_BIT("msi", PCIBridgeDev, flags, PCI_BRIDGE_DEV_F_MSI_REQ, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription pci_bridge_dev_vmstate = {
+    .name = "pci_bridge",
+    .fields = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(bridge.dev, PCIBridgeDev),
+        SHPC_VMSTATE(bridge.dev.shpc, PCIBridgeDev),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void pci_bridge_dev_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+    k->init = pci_bridge_dev_initfn;
+    k->exit = pci_bridge_dev_exitfn;
+    k->config_write = pci_bridge_dev_write_config;
+    k->vendor_id = PCI_VENDOR_ID_REDHAT;
+    k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE;
+    k->class_id = PCI_CLASS_BRIDGE_PCI;
+    k->is_bridge = 1,
+    dc->desc = "Standard PCI Bridge";
+    dc->reset = qdev_pci_bridge_dev_reset;
+    dc->props = pci_bridge_dev_properties;
+    dc->vmsd = &pci_bridge_dev_vmstate;
+}
+
+static const TypeInfo pci_bridge_dev_info = {
+    .name = "pci-bridge",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIBridgeDev),
+    .class_init = pci_bridge_dev_class_init,
+};
+
+static void pci_bridge_dev_register(void)
+{
+    type_register_static(&pci_bridge_dev_info);
+}
+
+type_init(pci_bridge_dev_register);
diff --git a/hw/pci/bridge/xio3130_downstream.c b/hw/pci/bridge/xio3130_downstream.c
new file mode 100644 (file)
index 0000000..b868f56
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * x3130_downstream.c
+ * TI X3130 pci express downstream port switch
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/pci/pci_ids.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/pcie.h"
+#include "hw/xio3130_downstream.h"
+
+#define PCI_DEVICE_ID_TI_XIO3130D       0x8233  /* downstream port */
+#define XIO3130_REVISION                0x1
+#define XIO3130_MSI_OFFSET              0x70
+#define XIO3130_MSI_SUPPORTED_FLAGS     PCI_MSI_FLAGS_64BIT
+#define XIO3130_MSI_NR_VECTOR           1
+#define XIO3130_SSVID_OFFSET            0x80
+#define XIO3130_SSVID_SVID              0
+#define XIO3130_SSVID_SSID              0
+#define XIO3130_EXP_OFFSET              0x90
+#define XIO3130_AER_OFFSET              0x100
+
+static void xio3130_downstream_write_config(PCIDevice *d, uint32_t address,
+                                         uint32_t val, int len)
+{
+    pci_bridge_write_config(d, address, val, len);
+    pcie_cap_flr_write_config(d, address, val, len);
+    pcie_cap_slot_write_config(d, address, val, len);
+    pcie_aer_write_config(d, address, val, len);
+}
+
+static void xio3130_downstream_reset(DeviceState *qdev)
+{
+    PCIDevice *d = PCI_DEVICE(qdev);
+
+    pcie_cap_deverr_reset(d);
+    pcie_cap_slot_reset(d);
+    pcie_cap_ari_reset(d);
+    pci_bridge_reset(qdev);
+}
+
+static int xio3130_downstream_initfn(PCIDevice *d)
+{
+    PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
+    PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
+    PCIESlot *s = DO_UPCAST(PCIESlot, port, p);
+    int rc;
+
+    rc = pci_bridge_initfn(d, TYPE_PCIE_BUS);
+    if (rc < 0) {
+        return rc;
+    }
+
+    pcie_port_init_reg(d);
+
+    rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
+                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
+                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
+    if (rc < 0) {
+        goto err_bridge;
+    }
+    rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET,
+                               XIO3130_SSVID_SVID, XIO3130_SSVID_SSID);
+    if (rc < 0) {
+        goto err_bridge;
+    }
+    rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_DOWNSTREAM,
+                       p->port);
+    if (rc < 0) {
+        goto err_msi;
+    }
+    pcie_cap_flr_init(d);
+    pcie_cap_deverr_init(d);
+    pcie_cap_slot_init(d, s->slot);
+    pcie_chassis_create(s->chassis);
+    rc = pcie_chassis_add_slot(s);
+    if (rc < 0) {
+        goto err_pcie_cap;
+    }
+    pcie_cap_ari_init(d);
+    rc = pcie_aer_init(d, XIO3130_AER_OFFSET);
+    if (rc < 0) {
+        goto err;
+    }
+
+    return 0;
+
+err:
+    pcie_chassis_del_slot(s);
+err_pcie_cap:
+    pcie_cap_exit(d);
+err_msi:
+    msi_uninit(d);
+err_bridge:
+    pci_bridge_exitfn(d);
+    return rc;
+}
+
+static void xio3130_downstream_exitfn(PCIDevice *d)
+{
+    PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
+    PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
+    PCIESlot *s = DO_UPCAST(PCIESlot, port, p);
+
+    pcie_aer_exit(d);
+    pcie_chassis_del_slot(s);
+    pcie_cap_exit(d);
+    msi_uninit(d);
+    pci_bridge_exitfn(d);
+}
+
+PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction,
+                                  const char *bus_name, pci_map_irq_fn map_irq,
+                                  uint8_t port, uint8_t chassis,
+                                  uint16_t slot)
+{
+    PCIDevice *d;
+    PCIBridge *br;
+    DeviceState *qdev;
+
+    d = pci_create_multifunction(bus, devfn, multifunction,
+                                 "xio3130-downstream");
+    if (!d) {
+        return NULL;
+    }
+    br = DO_UPCAST(PCIBridge, dev, d);
+
+    qdev = &br->dev.qdev;
+    pci_bridge_map_irq(br, bus_name, map_irq);
+    qdev_prop_set_uint8(qdev, "port", port);
+    qdev_prop_set_uint8(qdev, "chassis", chassis);
+    qdev_prop_set_uint16(qdev, "slot", slot);
+    qdev_init_nofail(qdev);
+
+    return DO_UPCAST(PCIESlot, port, DO_UPCAST(PCIEPort, br, br));
+}
+
+static const VMStateDescription vmstate_xio3130_downstream = {
+    .name = "xio3130-express-downstream-port",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load = pcie_cap_slot_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCIE_DEVICE(port.br.dev, PCIESlot),
+        VMSTATE_STRUCT(port.br.dev.exp.aer_log, PCIESlot, 0,
+                       vmstate_pcie_aer_log, PCIEAERLog),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property xio3130_downstream_properties[] = {
+    DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0),
+    DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0),
+    DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0),
+    DEFINE_PROP_UINT16("aer_log_max", PCIESlot,
+    port.br.dev.exp.aer_log.log_max,
+    PCIE_AER_LOG_MAX_DEFAULT),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xio3130_downstream_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->is_express = 1;
+    k->is_bridge = 1;
+    k->config_write = xio3130_downstream_write_config;
+    k->init = xio3130_downstream_initfn;
+    k->exit = xio3130_downstream_exitfn;
+    k->vendor_id = PCI_VENDOR_ID_TI;
+    k->device_id = PCI_DEVICE_ID_TI_XIO3130D;
+    k->revision = XIO3130_REVISION;
+    dc->desc = "TI X3130 Downstream Port of PCI Express Switch";
+    dc->reset = xio3130_downstream_reset;
+    dc->vmsd = &vmstate_xio3130_downstream;
+    dc->props = xio3130_downstream_properties;
+}
+
+static const TypeInfo xio3130_downstream_info = {
+    .name          = "xio3130-downstream",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIESlot),
+    .class_init    = xio3130_downstream_class_init,
+};
+
+static void xio3130_downstream_register_types(void)
+{
+    type_register_static(&xio3130_downstream_info);
+}
+
+type_init(xio3130_downstream_register_types)
+
+/*
+ * Local variables:
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 8
+ *  indent-tab-mode: nil
+ * End:
+ */
diff --git a/hw/pci/bridge/xio3130_upstream.c b/hw/pci/bridge/xio3130_upstream.c
new file mode 100644 (file)
index 0000000..cd5d97d
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * xio3130_upstream.c
+ * TI X3130 pci express upstream port switch
+ *
+ * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/pci/pci_ids.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/pcie.h"
+#include "hw/xio3130_upstream.h"
+
+#define PCI_DEVICE_ID_TI_XIO3130U       0x8232  /* upstream port */
+#define XIO3130_REVISION                0x2
+#define XIO3130_MSI_OFFSET              0x70
+#define XIO3130_MSI_SUPPORTED_FLAGS     PCI_MSI_FLAGS_64BIT
+#define XIO3130_MSI_NR_VECTOR           1
+#define XIO3130_SSVID_OFFSET            0x80
+#define XIO3130_SSVID_SVID              0
+#define XIO3130_SSVID_SSID              0
+#define XIO3130_EXP_OFFSET              0x90
+#define XIO3130_AER_OFFSET              0x100
+
+static void xio3130_upstream_write_config(PCIDevice *d, uint32_t address,
+                                          uint32_t val, int len)
+{
+    pci_bridge_write_config(d, address, val, len);
+    pcie_cap_flr_write_config(d, address, val, len);
+    pcie_aer_write_config(d, address, val, len);
+}
+
+static void xio3130_upstream_reset(DeviceState *qdev)
+{
+    PCIDevice *d = PCI_DEVICE(qdev);
+
+    pci_bridge_reset(qdev);
+    pcie_cap_deverr_reset(d);
+}
+
+static int xio3130_upstream_initfn(PCIDevice *d)
+{
+    PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
+    PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
+    int rc;
+
+    rc = pci_bridge_initfn(d, TYPE_PCIE_BUS);
+    if (rc < 0) {
+        return rc;
+    }
+
+    pcie_port_init_reg(d);
+
+    rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
+                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
+                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
+    if (rc < 0) {
+        goto err_bridge;
+    }
+    rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET,
+                               XIO3130_SSVID_SVID, XIO3130_SSVID_SSID);
+    if (rc < 0) {
+        goto err_bridge;
+    }
+    rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_UPSTREAM,
+                       p->port);
+    if (rc < 0) {
+        goto err_msi;
+    }
+    pcie_cap_flr_init(d);
+    pcie_cap_deverr_init(d);
+    rc = pcie_aer_init(d, XIO3130_AER_OFFSET);
+    if (rc < 0) {
+        goto err;
+    }
+
+    return 0;
+
+err:
+    pcie_cap_exit(d);
+err_msi:
+    msi_uninit(d);
+err_bridge:
+    pci_bridge_exitfn(d);
+    return rc;
+}
+
+static void xio3130_upstream_exitfn(PCIDevice *d)
+{
+    pcie_aer_exit(d);
+    pcie_cap_exit(d);
+    msi_uninit(d);
+    pci_bridge_exitfn(d);
+}
+
+PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction,
+                             const char *bus_name, pci_map_irq_fn map_irq,
+                             uint8_t port)
+{
+    PCIDevice *d;
+    PCIBridge *br;
+    DeviceState *qdev;
+
+    d = pci_create_multifunction(bus, devfn, multifunction, "x3130-upstream");
+    if (!d) {
+        return NULL;
+    }
+    br = DO_UPCAST(PCIBridge, dev, d);
+
+    qdev = &br->dev.qdev;
+    pci_bridge_map_irq(br, bus_name, map_irq);
+    qdev_prop_set_uint8(qdev, "port", port);
+    qdev_init_nofail(qdev);
+
+    return DO_UPCAST(PCIEPort, br, br);
+}
+
+static const VMStateDescription vmstate_xio3130_upstream = {
+    .name = "xio3130-express-upstream-port",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCIE_DEVICE(br.dev, PCIEPort),
+        VMSTATE_STRUCT(br.dev.exp.aer_log, PCIEPort, 0, vmstate_pcie_aer_log,
+                       PCIEAERLog),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property xio3130_upstream_properties[] = {
+    DEFINE_PROP_UINT8("port", PCIEPort, port, 0),
+    DEFINE_PROP_UINT16("aer_log_max", PCIEPort, br.dev.exp.aer_log.log_max,
+    PCIE_AER_LOG_MAX_DEFAULT),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xio3130_upstream_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->is_express = 1;
+    k->is_bridge = 1;
+    k->config_write = xio3130_upstream_write_config;
+    k->init = xio3130_upstream_initfn;
+    k->exit = xio3130_upstream_exitfn;
+    k->vendor_id = PCI_VENDOR_ID_TI;
+    k->device_id = PCI_DEVICE_ID_TI_XIO3130U;
+    k->revision = XIO3130_REVISION;
+    dc->desc = "TI X3130 Upstream Port of PCI Express Switch";
+    dc->reset = xio3130_upstream_reset;
+    dc->vmsd = &vmstate_xio3130_upstream;
+    dc->props = xio3130_upstream_properties;
+}
+
+static const TypeInfo xio3130_upstream_info = {
+    .name          = "x3130-upstream",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIEPort),
+    .class_init    = xio3130_upstream_class_init,
+};
+
+static void xio3130_upstream_register_types(void)
+{
+    type_register_static(&xio3130_upstream_info);
+}
+
+type_init(xio3130_upstream_register_types)
+
+
+/*
+ * Local variables:
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 8
+ *  indent-tab-mode: nil
+ * End:
+ */
diff --git a/hw/pci/host/Makefile.objs b/hw/pci/host/Makefile.objs
new file mode 100644 (file)
index 0000000..e1d6cce
--- /dev/null
@@ -0,0 +1,13 @@
+common-obj-y += pam.o
+
+# PPC devices
+common-obj-$(CONFIG_PREP_PCI) += prep.o
+common-obj-$(CONFIG_GRACKLE_PCI) += grackle.o
+# NewWorld PowerMac
+common-obj-$(CONFIG_UNIN_PCI) += uninorth.o
+common-obj-$(CONFIG_DEC_PCI) += dec.o
+# PowerPC E500 boards
+common-obj-$(CONFIG_PPCE500_PCI) += ppce500.o
+
+# ARM devices
+common-obj-$(CONFIG_VERSATILE_PCI) += versatile.o
diff --git a/hw/pci/host/dec.c b/hw/pci/host/dec.c
new file mode 100644 (file)
index 0000000..6ec3d22
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * QEMU DEC 21154 PCI bridge
+ *
+ * Copyright (c) 2006-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/dec_pci.h"
+#include "hw/sysbus.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_host.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci/pci_bus.h"
+
+/* debug DEC */
+//#define DEBUG_DEC
+
+#ifdef DEBUG_DEC
+#define DEC_DPRINTF(fmt, ...)                               \
+    do { printf("DEC: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DEC_DPRINTF(fmt, ...)
+#endif
+
+#define DEC_21154(obj) OBJECT_CHECK(DECState, (obj), TYPE_DEC_21154)
+
+typedef struct DECState {
+    PCIHostState parent_obj;
+} DECState;
+
+static int dec_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+    return irq_num;
+}
+
+static int dec_pci_bridge_initfn(PCIDevice *pci_dev)
+{
+    return pci_bridge_initfn(pci_dev, TYPE_PCI_BUS);
+}
+
+static void dec_21154_pci_bridge_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = dec_pci_bridge_initfn;
+    k->exit = pci_bridge_exitfn;
+    k->vendor_id = PCI_VENDOR_ID_DEC;
+    k->device_id = PCI_DEVICE_ID_DEC_21154;
+    k->config_write = pci_bridge_write_config;
+    k->is_bridge = 1;
+    dc->desc = "DEC 21154 PCI-PCI bridge";
+    dc->reset = pci_bridge_reset;
+    dc->vmsd = &vmstate_pci_device;
+}
+
+static const TypeInfo dec_21154_pci_bridge_info = {
+    .name          = "dec-21154-p2p-bridge",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIBridge),
+    .class_init    = dec_21154_pci_bridge_class_init,
+};
+
+PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn)
+{
+    PCIDevice *dev;
+    PCIBridge *br;
+
+    dev = pci_create_multifunction(parent_bus, devfn, false,
+                                   "dec-21154-p2p-bridge");
+    br = DO_UPCAST(PCIBridge, dev, dev);
+    pci_bridge_map_irq(br, "DEC 21154 PCI-PCI bridge", dec_map_irq);
+    qdev_init_nofail(&dev->qdev);
+    return pci_bridge_get_sec_bus(br);
+}
+
+static int pci_dec_21154_device_init(SysBusDevice *dev)
+{
+    PCIHostState *phb;
+
+    phb = PCI_HOST_BRIDGE(dev);
+
+    memory_region_init_io(&phb->conf_mem, &pci_host_conf_le_ops,
+                          dev, "pci-conf-idx", 0x1000);
+    memory_region_init_io(&phb->data_mem, &pci_host_data_le_ops,
+                          dev, "pci-data-idx", 0x1000);
+    sysbus_init_mmio(dev, &phb->conf_mem);
+    sysbus_init_mmio(dev, &phb->data_mem);
+    return 0;
+}
+
+static int dec_21154_pci_host_init(PCIDevice *d)
+{
+    /* PCI2PCI bridge same values as PearPC - check this */
+    return 0;
+}
+
+static void dec_21154_pci_host_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = dec_21154_pci_host_init;
+    k->vendor_id = PCI_VENDOR_ID_DEC;
+    k->device_id = PCI_DEVICE_ID_DEC_21154;
+    k->revision = 0x02;
+    k->class_id = PCI_CLASS_BRIDGE_PCI;
+    k->is_bridge = 1;
+}
+
+static const TypeInfo dec_21154_pci_host_info = {
+    .name          = "dec-21154",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIDevice),
+    .class_init    = dec_21154_pci_host_class_init,
+};
+
+static void pci_dec_21154_device_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = pci_dec_21154_device_init;
+}
+
+static const TypeInfo pci_dec_21154_device_info = {
+    .name          = TYPE_DEC_21154,
+    .parent        = TYPE_PCI_HOST_BRIDGE,
+    .instance_size = sizeof(DECState),
+    .class_init    = pci_dec_21154_device_class_init,
+};
+
+static void dec_register_types(void)
+{
+    type_register_static(&pci_dec_21154_device_info);
+    type_register_static(&dec_21154_pci_host_info);
+    type_register_static(&dec_21154_pci_bridge_info);
+}
+
+type_init(dec_register_types)
diff --git a/hw/pci/host/grackle.c b/hw/pci/host/grackle.c
new file mode 100644 (file)
index 0000000..69344d9
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * QEMU Grackle PCI host (heathrow OldWorld PowerMac)
+ *
+ * Copyright (c) 2006-2007 Fabrice Bellard
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/pci/pci_host.h"
+#include "hw/ppc/mac.h"
+#include "hw/pci/pci.h"
+
+/* debug Grackle */
+//#define DEBUG_GRACKLE
+
+#ifdef DEBUG_GRACKLE
+#define GRACKLE_DPRINTF(fmt, ...)                               \
+    do { printf("GRACKLE: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define GRACKLE_DPRINTF(fmt, ...)
+#endif
+
+#define GRACKLE_PCI_HOST_BRIDGE(obj) \
+    OBJECT_CHECK(GrackleState, (obj), TYPE_GRACKLE_PCI_HOST_BRIDGE)
+
+typedef struct GrackleState {
+    PCIHostState parent_obj;
+
+    MemoryRegion pci_mmio;
+    MemoryRegion pci_hole;
+} GrackleState;
+
+/* Don't know if this matches real hardware, but it agrees with OHW.  */
+static int pci_grackle_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+    return (irq_num + (pci_dev->devfn >> 3)) & 3;
+}
+
+static void pci_grackle_set_irq(void *opaque, int irq_num, int level)
+{
+    qemu_irq *pic = opaque;
+
+    GRACKLE_DPRINTF("set_irq num %d level %d\n", irq_num, level);
+    qemu_set_irq(pic[irq_num + 0x15], level);
+}
+
+PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic,
+                         MemoryRegion *address_space_mem,
+                         MemoryRegion *address_space_io)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+    PCIHostState *phb;
+    GrackleState *d;
+
+    dev = qdev_create(NULL, TYPE_GRACKLE_PCI_HOST_BRIDGE);
+    qdev_init_nofail(dev);
+    s = SYS_BUS_DEVICE(dev);
+    phb = PCI_HOST_BRIDGE(dev);
+    d = GRACKLE_PCI_HOST_BRIDGE(dev);
+
+    memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL);
+    memory_region_init_alias(&d->pci_hole, "pci-hole", &d->pci_mmio,
+                             0x80000000ULL, 0x7e000000ULL);
+    memory_region_add_subregion(address_space_mem, 0x80000000ULL,
+                                &d->pci_hole);
+
+    phb->bus = pci_register_bus(dev, "pci",
+                                pci_grackle_set_irq,
+                                pci_grackle_map_irq,
+                                pic,
+                                &d->pci_mmio,
+                                address_space_io,
+                                0, 4, TYPE_PCI_BUS);
+
+    pci_create_simple(phb->bus, 0, "grackle");
+
+    sysbus_mmio_map(s, 0, base);
+    sysbus_mmio_map(s, 1, base + 0x00200000);
+
+    return phb->bus;
+}
+
+static int pci_grackle_init_device(SysBusDevice *dev)
+{
+    PCIHostState *phb;
+
+    phb = PCI_HOST_BRIDGE(dev);
+
+    memory_region_init_io(&phb->conf_mem, &pci_host_conf_le_ops,
+                          dev, "pci-conf-idx", 0x1000);
+    memory_region_init_io(&phb->data_mem, &pci_host_data_le_ops,
+                          dev, "pci-data-idx", 0x1000);
+    sysbus_init_mmio(dev, &phb->conf_mem);
+    sysbus_init_mmio(dev, &phb->data_mem);
+
+    return 0;
+}
+
+static int grackle_pci_host_init(PCIDevice *d)
+{
+    d->config[0x09] = 0x01;
+    return 0;
+}
+
+static void grackle_pci_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    k->init      = grackle_pci_host_init;
+    k->vendor_id = PCI_VENDOR_ID_MOTOROLA;
+    k->device_id = PCI_DEVICE_ID_MOTOROLA_MPC106;
+    k->revision  = 0x00;
+    k->class_id  = PCI_CLASS_BRIDGE_HOST;
+    dc->no_user = 1;
+}
+
+static const TypeInfo grackle_pci_info = {
+    .name          = "grackle",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIDevice),
+    .class_init = grackle_pci_class_init,
+};
+
+static void pci_grackle_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    k->init = pci_grackle_init_device;
+    dc->no_user = 1;
+}
+
+static const TypeInfo grackle_pci_host_info = {
+    .name          = TYPE_GRACKLE_PCI_HOST_BRIDGE,
+    .parent        = TYPE_PCI_HOST_BRIDGE,
+    .instance_size = sizeof(GrackleState),
+    .class_init    = pci_grackle_class_init,
+};
+
+static void grackle_register_types(void)
+{
+    type_register_static(&grackle_pci_info);
+    type_register_static(&grackle_pci_host_info);
+}
+
+type_init(grackle_register_types)
diff --git a/hw/pci/host/pam.c b/hw/pci/host/pam.c
new file mode 100644 (file)
index 0000000..7181bd6
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * QEMU i440FX/PIIX3 PCI Bridge Emulation
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ * Copyright (c) 2011 Isaku Yamahata <yamahata at valinux co jp>
+ *                    VA Linux Systems Japan K.K.
+ * Copyright (c) 2012 Jason Baron <jbaron@redhat.com>
+ *
+ * Split out from piix_pci.c
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "sysemu/sysemu.h"
+#include "hw/pci-host/pam.h"
+
+void smram_update(MemoryRegion *smram_region, uint8_t smram,
+                  uint8_t smm_enabled)
+{
+    bool smram_enabled;
+
+    smram_enabled = ((smm_enabled && (smram & SMRAM_G_SMRAME)) ||
+                        (smram & SMRAM_D_OPEN));
+    memory_region_set_enabled(smram_region, !smram_enabled);
+}
+
+void smram_set_smm(uint8_t *host_smm_enabled, int smm, uint8_t smram,
+                   MemoryRegion *smram_region)
+{
+    uint8_t smm_enabled = (smm != 0);
+    if (*host_smm_enabled != smm_enabled) {
+        *host_smm_enabled = smm_enabled;
+        smram_update(smram_region, smram, *host_smm_enabled);
+    }
+}
+
+void init_pam(MemoryRegion *ram_memory, MemoryRegion *system_memory,
+              MemoryRegion *pci_address_space, PAMMemoryRegion *mem,
+              uint32_t start, uint32_t size)
+{
+    int i;
+
+    /* RAM */
+    memory_region_init_alias(&mem->alias[3], "pam-ram", ram_memory,
+                             start, size);
+    /* ROM (XXX: not quite correct) */
+    memory_region_init_alias(&mem->alias[1], "pam-rom", ram_memory,
+                             start, size);
+    memory_region_set_readonly(&mem->alias[1], true);
+
+    /* XXX: should distinguish read/write cases */
+    memory_region_init_alias(&mem->alias[0], "pam-pci", pci_address_space,
+                             start, size);
+    memory_region_init_alias(&mem->alias[2], "pam-pci", pci_address_space,
+                             start, size);
+
+    for (i = 0; i < 4; ++i) {
+        memory_region_set_enabled(&mem->alias[i], false);
+        memory_region_add_subregion_overlap(system_memory, start,
+                                            &mem->alias[i], 1);
+    }
+    mem->current = 0;
+}
+
+void pam_update(PAMMemoryRegion *pam, int idx, uint8_t val)
+{
+    assert(0 <= idx && idx <= 12);
+
+    memory_region_set_enabled(&pam->alias[pam->current], false);
+    pam->current = (val >> ((!(idx & 1)) * 4)) & PAM_ATTR_MASK;
+    memory_region_set_enabled(&pam->alias[pam->current], true);
+}
diff --git a/hw/pci/host/ppce500.c b/hw/pci/host/ppce500.c
new file mode 100644 (file)
index 0000000..5e7ad94
--- /dev/null
@@ -0,0 +1,427 @@
+/*
+ * QEMU PowerPC E500 embedded processors pci controller emulation
+ *
+ * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Yu Liu,     <yu.liu@freescale.com>
+ *
+ * This file is derived from hw/ppc4xx_pci.c,
+ * the copyright for that material belongs to the original owners.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of  the GNU General  Public License as published by
+ * the Free Software Foundation;  either version 2 of the  License, or
+ * (at your option) any later version.
+ */
+
+#include "hw/hw.h"
+#include "hw/ppc/e500-ccsr.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_host.h"
+#include "qemu/bswap.h"
+#include "hw/pci-host/ppce500.h"
+
+#ifdef DEBUG_PCI
+#define pci_debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
+#else
+#define pci_debug(fmt, ...)
+#endif
+
+#define PCIE500_CFGADDR       0x0
+#define PCIE500_CFGDATA       0x4
+#define PCIE500_REG_BASE      0xC00
+#define PCIE500_ALL_SIZE      0x1000
+#define PCIE500_REG_SIZE      (PCIE500_ALL_SIZE - PCIE500_REG_BASE)
+
+#define PCIE500_PCI_IOLEN     0x10000ULL
+
+#define PPCE500_PCI_CONFIG_ADDR         0x0
+#define PPCE500_PCI_CONFIG_DATA         0x4
+#define PPCE500_PCI_INTACK              0x8
+
+#define PPCE500_PCI_OW1                 (0xC20 - PCIE500_REG_BASE)
+#define PPCE500_PCI_OW2                 (0xC40 - PCIE500_REG_BASE)
+#define PPCE500_PCI_OW3                 (0xC60 - PCIE500_REG_BASE)
+#define PPCE500_PCI_OW4                 (0xC80 - PCIE500_REG_BASE)
+#define PPCE500_PCI_IW3                 (0xDA0 - PCIE500_REG_BASE)
+#define PPCE500_PCI_IW2                 (0xDC0 - PCIE500_REG_BASE)
+#define PPCE500_PCI_IW1                 (0xDE0 - PCIE500_REG_BASE)
+
+#define PPCE500_PCI_GASKET_TIMR         (0xE20 - PCIE500_REG_BASE)
+
+#define PCI_POTAR               0x0
+#define PCI_POTEAR              0x4
+#define PCI_POWBAR              0x8
+#define PCI_POWAR               0x10
+
+#define PCI_PITAR               0x0
+#define PCI_PIWBAR              0x8
+#define PCI_PIWBEAR             0xC
+#define PCI_PIWAR               0x10
+
+#define PPCE500_PCI_NR_POBS     5
+#define PPCE500_PCI_NR_PIBS     3
+
+struct  pci_outbound {
+    uint32_t potar;
+    uint32_t potear;
+    uint32_t powbar;
+    uint32_t powar;
+};
+
+struct pci_inbound {
+    uint32_t pitar;
+    uint32_t piwbar;
+    uint32_t piwbear;
+    uint32_t piwar;
+};
+
+#define TYPE_PPC_E500_PCI_HOST_BRIDGE "e500-pcihost"
+
+#define PPC_E500_PCI_HOST_BRIDGE(obj) \
+    OBJECT_CHECK(PPCE500PCIState, (obj), TYPE_PPC_E500_PCI_HOST_BRIDGE)
+
+struct PPCE500PCIState {
+    PCIHostState parent_obj;
+
+    struct pci_outbound pob[PPCE500_PCI_NR_POBS];
+    struct pci_inbound pib[PPCE500_PCI_NR_PIBS];
+    uint32_t gasket_time;
+    qemu_irq irq[4];
+    uint32_t first_slot;
+    /* mmio maps */
+    MemoryRegion container;
+    MemoryRegion iomem;
+    MemoryRegion pio;
+};
+
+#define TYPE_PPC_E500_PCI_BRIDGE "e500-host-bridge"
+#define PPC_E500_PCI_BRIDGE(obj) \
+    OBJECT_CHECK(PPCE500PCIBridgeState, (obj), TYPE_PPC_E500_PCI_BRIDGE)
+
+struct PPCE500PCIBridgeState {
+    /*< private >*/
+    PCIDevice parent;
+    /*< public >*/
+
+    MemoryRegion bar0;
+};
+
+typedef struct PPCE500PCIBridgeState PPCE500PCIBridgeState;
+typedef struct PPCE500PCIState PPCE500PCIState;
+
+static uint64_t pci_reg_read4(void *opaque, hwaddr addr,
+                              unsigned size)
+{
+    PPCE500PCIState *pci = opaque;
+    unsigned long win;
+    uint32_t value = 0;
+    int idx;
+
+    win = addr & 0xfe0;
+
+    switch (win) {
+    case PPCE500_PCI_OW1:
+    case PPCE500_PCI_OW2:
+    case PPCE500_PCI_OW3:
+    case PPCE500_PCI_OW4:
+        idx = (addr >> 5) & 0x7;
+        switch (addr & 0xC) {
+        case PCI_POTAR:
+            value = pci->pob[idx].potar;
+            break;
+        case PCI_POTEAR:
+            value = pci->pob[idx].potear;
+            break;
+        case PCI_POWBAR:
+            value = pci->pob[idx].powbar;
+            break;
+        case PCI_POWAR:
+            value = pci->pob[idx].powar;
+            break;
+        default:
+            break;
+        }
+        break;
+
+    case PPCE500_PCI_IW3:
+    case PPCE500_PCI_IW2:
+    case PPCE500_PCI_IW1:
+        idx = ((addr >> 5) & 0x3) - 1;
+        switch (addr & 0xC) {
+        case PCI_PITAR:
+            value = pci->pib[idx].pitar;
+            break;
+        case PCI_PIWBAR:
+            value = pci->pib[idx].piwbar;
+            break;
+        case PCI_PIWBEAR:
+            value = pci->pib[idx].piwbear;
+            break;
+        case PCI_PIWAR:
+            value = pci->pib[idx].piwar;
+            break;
+        default:
+            break;
+        };
+        break;
+
+    case PPCE500_PCI_GASKET_TIMR:
+        value = pci->gasket_time;
+        break;
+
+    default:
+        break;
+    }
+
+    pci_debug("%s: win:%lx(addr:" TARGET_FMT_plx ") -> value:%x\n", __func__,
+              win, addr, value);
+    return value;
+}
+
+static void pci_reg_write4(void *opaque, hwaddr addr,
+                           uint64_t value, unsigned size)
+{
+    PPCE500PCIState *pci = opaque;
+    unsigned long win;
+    int idx;
+
+    win = addr & 0xfe0;
+
+    pci_debug("%s: value:%x -> win:%lx(addr:" TARGET_FMT_plx ")\n",
+              __func__, (unsigned)value, win, addr);
+
+    switch (win) {
+    case PPCE500_PCI_OW1:
+    case PPCE500_PCI_OW2:
+    case PPCE500_PCI_OW3:
+    case PPCE500_PCI_OW4:
+        idx = (addr >> 5) & 0x7;
+        switch (addr & 0xC) {
+        case PCI_POTAR:
+            pci->pob[idx].potar = value;
+            break;
+        case PCI_POTEAR:
+            pci->pob[idx].potear = value;
+            break;
+        case PCI_POWBAR:
+            pci->pob[idx].powbar = value;
+            break;
+        case PCI_POWAR:
+            pci->pob[idx].powar = value;
+            break;
+        default:
+            break;
+        };
+        break;
+
+    case PPCE500_PCI_IW3:
+    case PPCE500_PCI_IW2:
+    case PPCE500_PCI_IW1:
+        idx = ((addr >> 5) & 0x3) - 1;
+        switch (addr & 0xC) {
+        case PCI_PITAR:
+            pci->pib[idx].pitar = value;
+            break;
+        case PCI_PIWBAR:
+            pci->pib[idx].piwbar = value;
+            break;
+        case PCI_PIWBEAR:
+            pci->pib[idx].piwbear = value;
+            break;
+        case PCI_PIWAR:
+            pci->pib[idx].piwar = value;
+            break;
+        default:
+            break;
+        };
+        break;
+
+    case PPCE500_PCI_GASKET_TIMR:
+        pci->gasket_time = value;
+        break;
+
+    default:
+        break;
+    };
+}
+
+static const MemoryRegionOps e500_pci_reg_ops = {
+    .read = pci_reg_read4,
+    .write = pci_reg_write4,
+    .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static int mpc85xx_pci_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+    int devno = pci_dev->devfn >> 3;
+    int ret;
+
+    ret = ppce500_pci_map_irq_slot(devno, irq_num);
+
+    pci_debug("%s: devfn %x irq %d -> %d  devno:%x\n", __func__,
+           pci_dev->devfn, irq_num, ret, devno);
+
+    return ret;
+}
+
+static void mpc85xx_pci_set_irq(void *opaque, int irq_num, int level)
+{
+    qemu_irq *pic = opaque;
+
+    pci_debug("%s: PCI irq %d, level:%d\n", __func__, irq_num, level);
+
+    qemu_set_irq(pic[irq_num], level);
+}
+
+static const VMStateDescription vmstate_pci_outbound = {
+    .name = "pci_outbound",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32(potar, struct pci_outbound),
+        VMSTATE_UINT32(potear, struct pci_outbound),
+        VMSTATE_UINT32(powbar, struct pci_outbound),
+        VMSTATE_UINT32(powar, struct pci_outbound),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_pci_inbound = {
+    .name = "pci_inbound",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32(pitar, struct pci_inbound),
+        VMSTATE_UINT32(piwbar, struct pci_inbound),
+        VMSTATE_UINT32(piwbear, struct pci_inbound),
+        VMSTATE_UINT32(piwar, struct pci_inbound),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_ppce500_pci = {
+    .name = "ppce500_pci",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_STRUCT_ARRAY(pob, PPCE500PCIState, PPCE500_PCI_NR_POBS, 1,
+                             vmstate_pci_outbound, struct pci_outbound),
+        VMSTATE_STRUCT_ARRAY(pib, PPCE500PCIState, PPCE500_PCI_NR_PIBS, 1,
+                             vmstate_pci_outbound, struct pci_inbound),
+        VMSTATE_UINT32(gasket_time, PPCE500PCIState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+#include "exec/address-spaces.h"
+
+static int e500_pcihost_bridge_initfn(PCIDevice *d)
+{
+    PPCE500PCIBridgeState *b = PPC_E500_PCI_BRIDGE(d);
+    PPCE500CCSRState *ccsr = CCSR(container_get(qdev_get_machine(),
+                                  "/e500-ccsr"));
+
+    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_PCI);
+    d->config[PCI_HEADER_TYPE] =
+        (d->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) |
+        PCI_HEADER_TYPE_BRIDGE;
+
+    memory_region_init_alias(&b->bar0, "e500-pci-bar0", &ccsr->ccsr_space,
+                             0, int128_get64(ccsr->ccsr_space.size));
+    pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &b->bar0);
+
+    return 0;
+}
+
+static int e500_pcihost_initfn(SysBusDevice *dev)
+{
+    PCIHostState *h;
+    PPCE500PCIState *s;
+    PCIBus *b;
+    int i;
+    MemoryRegion *address_space_mem = get_system_memory();
+
+    h = PCI_HOST_BRIDGE(dev);
+    s = PPC_E500_PCI_HOST_BRIDGE(dev);
+
+    for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
+        sysbus_init_irq(dev, &s->irq[i]);
+    }
+
+    memory_region_init(&s->pio, "pci-pio", PCIE500_PCI_IOLEN);
+
+    b = pci_register_bus(DEVICE(dev), NULL, mpc85xx_pci_set_irq,
+                         mpc85xx_pci_map_irq, s->irq, address_space_mem,
+                         &s->pio, PCI_DEVFN(s->first_slot, 0), 4, TYPE_PCI_BUS);
+    h->bus = b;
+
+    pci_create_simple(b, 0, "e500-host-bridge");
+
+    memory_region_init(&s->container, "pci-container", PCIE500_ALL_SIZE);
+    memory_region_init_io(&h->conf_mem, &pci_host_conf_be_ops, h,
+                          "pci-conf-idx", 4);
+    memory_region_init_io(&h->data_mem, &pci_host_data_le_ops, h,
+                          "pci-conf-data", 4);
+    memory_region_init_io(&s->iomem, &e500_pci_reg_ops, s,
+                          "pci.reg", PCIE500_REG_SIZE);
+    memory_region_add_subregion(&s->container, PCIE500_CFGADDR, &h->conf_mem);
+    memory_region_add_subregion(&s->container, PCIE500_CFGDATA, &h->data_mem);
+    memory_region_add_subregion(&s->container, PCIE500_REG_BASE, &s->iomem);
+    sysbus_init_mmio(dev, &s->container);
+    sysbus_init_mmio(dev, &s->pio);
+
+    return 0;
+}
+
+static void e500_host_bridge_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = e500_pcihost_bridge_initfn;
+    k->vendor_id = PCI_VENDOR_ID_FREESCALE;
+    k->device_id = PCI_DEVICE_ID_MPC8533E;
+    k->class_id = PCI_CLASS_PROCESSOR_POWERPC;
+    dc->desc = "Host bridge";
+}
+
+static const TypeInfo e500_host_bridge_info = {
+    .name          = "e500-host-bridge",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PPCE500PCIBridgeState),
+    .class_init    = e500_host_bridge_class_init,
+};
+
+static Property pcihost_properties[] = {
+    DEFINE_PROP_UINT32("first_slot", PPCE500PCIState, first_slot, 0x11),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void e500_pcihost_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = e500_pcihost_initfn;
+    dc->props = pcihost_properties;
+    dc->vmsd = &vmstate_ppce500_pci;
+}
+
+static const TypeInfo e500_pcihost_info = {
+    .name          = TYPE_PPC_E500_PCI_HOST_BRIDGE,
+    .parent        = TYPE_PCI_HOST_BRIDGE,
+    .instance_size = sizeof(PPCE500PCIState),
+    .class_init    = e500_pcihost_class_init,
+};
+
+static void e500_pci_register_types(void)
+{
+    type_register_static(&e500_pcihost_info);
+    type_register_static(&e500_host_bridge_info);
+}
+
+type_init(e500_pci_register_types)
diff --git a/hw/pci/host/prep.c b/hw/pci/host/prep.c
new file mode 100644 (file)
index 0000000..6130253
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * QEMU PREP PCI host
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ * Copyright (c) 2011-2013 Andreas Färber
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_bus.h"
+#include "hw/pci/pci_host.h"
+#include "hw/i386/pc.h"
+#include "exec/address-spaces.h"
+
+#define TYPE_RAVEN_PCI_DEVICE "raven"
+#define TYPE_RAVEN_PCI_HOST_BRIDGE "raven-pcihost"
+
+#define RAVEN_PCI_DEVICE(obj) \
+    OBJECT_CHECK(RavenPCIState, (obj), TYPE_RAVEN_PCI_DEVICE)
+
+typedef struct RavenPCIState {
+    PCIDevice dev;
+} RavenPCIState;
+
+#define RAVEN_PCI_HOST_BRIDGE(obj) \
+    OBJECT_CHECK(PREPPCIState, (obj), TYPE_RAVEN_PCI_HOST_BRIDGE)
+
+typedef struct PRePPCIState {
+    PCIHostState parent_obj;
+
+    MemoryRegion intack;
+    qemu_irq irq[4];
+    PCIBus pci_bus;
+    RavenPCIState pci_dev;
+} PREPPCIState;
+
+static inline uint32_t PPC_PCIIO_config(hwaddr addr)
+{
+    int i;
+
+    for (i = 0; i < 11; i++) {
+        if ((addr & (1 << (11 + i))) != 0) {
+            break;
+        }
+    }
+    return (addr & 0x7ff) |  (i << 11);
+}
+
+static void ppc_pci_io_write(void *opaque, hwaddr addr,
+                             uint64_t val, unsigned int size)
+{
+    PREPPCIState *s = opaque;
+    PCIHostState *phb = PCI_HOST_BRIDGE(s);
+    pci_data_write(phb->bus, PPC_PCIIO_config(addr), val, size);
+}
+
+static uint64_t ppc_pci_io_read(void *opaque, hwaddr addr,
+                                unsigned int size)
+{
+    PREPPCIState *s = opaque;
+    PCIHostState *phb = PCI_HOST_BRIDGE(s);
+    return pci_data_read(phb->bus, PPC_PCIIO_config(addr), size);
+}
+
+static const MemoryRegionOps PPC_PCIIO_ops = {
+    .read = ppc_pci_io_read,
+    .write = ppc_pci_io_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t ppc_intack_read(void *opaque, hwaddr addr,
+                                unsigned int size)
+{
+    return pic_read_irq(isa_pic);
+}
+
+static const MemoryRegionOps PPC_intack_ops = {
+    .read = ppc_intack_read,
+    .valid = {
+        .max_access_size = 1,
+    },
+};
+
+static int prep_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+    return (irq_num + (pci_dev->devfn >> 3)) & 1;
+}
+
+static void prep_set_irq(void *opaque, int irq_num, int level)
+{
+    qemu_irq *pic = opaque;
+
+    qemu_set_irq(pic[irq_num] , level);
+}
+
+static void raven_pcihost_realizefn(DeviceState *d, Error **errp)
+{
+    SysBusDevice *dev = SYS_BUS_DEVICE(d);
+    PCIHostState *h = PCI_HOST_BRIDGE(dev);
+    PREPPCIState *s = RAVEN_PCI_HOST_BRIDGE(dev);
+    MemoryRegion *address_space_mem = get_system_memory();
+    int i;
+
+    for (i = 0; i < 4; i++) {
+        sysbus_init_irq(dev, &s->irq[i]);
+    }
+
+    pci_bus_irqs(&s->pci_bus, prep_set_irq, prep_map_irq, s->irq, 4);
+
+    memory_region_init_io(&h->conf_mem, &pci_host_conf_be_ops, s,
+                          "pci-conf-idx", 1);
+    sysbus_add_io(dev, 0xcf8, &h->conf_mem);
+    sysbus_init_ioports(&h->busdev, 0xcf8, 1);
+
+    memory_region_init_io(&h->data_mem, &pci_host_data_be_ops, s,
+                          "pci-conf-data", 1);
+    sysbus_add_io(dev, 0xcfc, &h->data_mem);
+    sysbus_init_ioports(&h->busdev, 0xcfc, 1);
+
+    memory_region_init_io(&h->mmcfg, &PPC_PCIIO_ops, s, "pciio", 0x00400000);
+    memory_region_add_subregion(address_space_mem, 0x80800000, &h->mmcfg);
+
+    memory_region_init_io(&s->intack, &PPC_intack_ops, s, "pci-intack", 1);
+    memory_region_add_subregion(address_space_mem, 0xbffffff0, &s->intack);
+
+    /* TODO Remove once realize propagates to child devices. */
+    object_property_set_bool(OBJECT(&s->pci_dev), true, "realized", errp);
+}
+
+static void raven_pcihost_initfn(Object *obj)
+{
+    PCIHostState *h = PCI_HOST_BRIDGE(obj);
+    PREPPCIState *s = RAVEN_PCI_HOST_BRIDGE(obj);
+    MemoryRegion *address_space_mem = get_system_memory();
+    MemoryRegion *address_space_io = get_system_io();
+    DeviceState *pci_dev;
+
+    pci_bus_new_inplace(&s->pci_bus, DEVICE(obj), NULL,
+                        address_space_mem, address_space_io, 0, TYPE_PCI_BUS);
+    h->bus = &s->pci_bus;
+
+    object_initialize(&s->pci_dev, TYPE_RAVEN_PCI_DEVICE);
+    pci_dev = DEVICE(&s->pci_dev);
+    qdev_set_parent_bus(pci_dev, BUS(&s->pci_bus));
+    object_property_set_int(OBJECT(&s->pci_dev), PCI_DEVFN(0, 0), "addr",
+                            NULL);
+    qdev_prop_set_bit(pci_dev, "multifunction", false);
+}
+
+static int raven_init(PCIDevice *d)
+{
+    d->config[0x0C] = 0x08; // cache_line_size
+    d->config[0x0D] = 0x10; // latency_timer
+    d->config[0x34] = 0x00; // capabilities_pointer
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_raven = {
+    .name = "raven",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(dev, RavenPCIState),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static void raven_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    k->init = raven_init;
+    k->vendor_id = PCI_VENDOR_ID_MOTOROLA;
+    k->device_id = PCI_DEVICE_ID_MOTOROLA_RAVEN;
+    k->revision = 0x00;
+    k->class_id = PCI_CLASS_BRIDGE_HOST;
+    dc->desc = "PReP Host Bridge - Motorola Raven";
+    dc->vmsd = &vmstate_raven;
+    dc->no_user = 1;
+}
+
+static const TypeInfo raven_info = {
+    .name = TYPE_RAVEN_PCI_DEVICE,
+    .parent = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(RavenPCIState),
+    .class_init = raven_class_init,
+};
+
+static void raven_pcihost_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = raven_pcihost_realizefn;
+    dc->fw_name = "pci";
+    dc->no_user = 1;
+}
+
+static const TypeInfo raven_pcihost_info = {
+    .name = TYPE_RAVEN_PCI_HOST_BRIDGE,
+    .parent = TYPE_PCI_HOST_BRIDGE,
+    .instance_size = sizeof(PREPPCIState),
+    .instance_init = raven_pcihost_initfn,
+    .class_init = raven_pcihost_class_init,
+};
+
+static void raven_register_types(void)
+{
+    type_register_static(&raven_pcihost_info);
+    type_register_static(&raven_info);
+}
+
+type_init(raven_register_types)
diff --git a/hw/pci/host/uninorth.c b/hw/pci/host/uninorth.c
new file mode 100644 (file)
index 0000000..fff235d
--- /dev/null
@@ -0,0 +1,492 @@
+/*
+ * QEMU Uninorth PCI host (for all Mac99 and newer machines)
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/ppc/mac.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_host.h"
+
+/* debug UniNorth */
+//#define DEBUG_UNIN
+
+#ifdef DEBUG_UNIN
+#define UNIN_DPRINTF(fmt, ...)                                  \
+    do { printf("UNIN: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define UNIN_DPRINTF(fmt, ...)
+#endif
+
+static const int unin_irq_line[] = { 0x1b, 0x1c, 0x1d, 0x1e };
+
+#define TYPE_UNI_NORTH_PCI_HOST_BRIDGE "uni-north-pci-pcihost"
+#define TYPE_UNI_NORTH_AGP_HOST_BRIDGE "uni-north-agp-pcihost"
+#define TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE "uni-north-internal-pci-pcihost"
+#define TYPE_U3_AGP_HOST_BRIDGE "u3-agp-pcihost"
+
+#define UNI_NORTH_PCI_HOST_BRIDGE(obj) \
+    OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_PCI_HOST_BRIDGE)
+#define UNI_NORTH_AGP_HOST_BRIDGE(obj) \
+    OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_AGP_HOST_BRIDGE)
+#define UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(obj) \
+    OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE)
+#define U3_AGP_HOST_BRIDGE(obj) \
+    OBJECT_CHECK(UNINState, (obj), TYPE_U3_AGP_HOST_BRIDGE)
+
+typedef struct UNINState {
+    PCIHostState parent_obj;
+
+    MemoryRegion pci_mmio;
+    MemoryRegion pci_hole;
+} UNINState;
+
+static int pci_unin_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+    int retval;
+    int devfn = pci_dev->devfn & 0x00FFFFFF;
+
+    retval = (((devfn >> 11) & 0x1F) + irq_num) & 3;
+
+    return retval;
+}
+
+static void pci_unin_set_irq(void *opaque, int irq_num, int level)
+{
+    qemu_irq *pic = opaque;
+
+    UNIN_DPRINTF("%s: setting INT %d = %d\n", __func__,
+                 unin_irq_line[irq_num], level);
+    qemu_set_irq(pic[unin_irq_line[irq_num]], level);
+}
+
+static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr)
+{
+    uint32_t retval;
+
+    if (reg & (1u << 31)) {
+        /* XXX OpenBIOS compatibility hack */
+        retval = reg | (addr & 3);
+    } else if (reg & 1) {
+        /* CFA1 style */
+        retval = (reg & ~7u) | (addr & 7);
+    } else {
+        uint32_t slot, func;
+
+        /* Grab CFA0 style values */
+        slot = ffs(reg & 0xfffff800) - 1;
+        func = (reg >> 8) & 7;
+
+        /* ... and then convert them to x86 format */
+        /* config pointer */
+        retval = (reg & (0xff - 7)) | (addr & 7);
+        /* slot */
+        retval |= slot << 11;
+        /* fn */
+        retval |= func << 8;
+    }
+
+
+    UNIN_DPRINTF("Converted config space accessor %08x/%08x -> %08x\n",
+                 reg, addr, retval);
+
+    return retval;
+}
+
+static void unin_data_write(void *opaque, hwaddr addr,
+                            uint64_t val, unsigned len)
+{
+    UNINState *s = opaque;
+    PCIHostState *phb = PCI_HOST_BRIDGE(s);
+    UNIN_DPRINTF("write addr %" TARGET_FMT_plx " len %d val %"PRIx64"\n",
+                 addr, len, val);
+    pci_data_write(phb->bus,
+                   unin_get_config_reg(phb->config_reg, addr),
+                   val, len);
+}
+
+static uint64_t unin_data_read(void *opaque, hwaddr addr,
+                               unsigned len)
+{
+    UNINState *s = opaque;
+    PCIHostState *phb = PCI_HOST_BRIDGE(s);
+    uint32_t val;
+
+    val = pci_data_read(phb->bus,
+                        unin_get_config_reg(phb->config_reg, addr),
+                        len);
+    UNIN_DPRINTF("read addr %" TARGET_FMT_plx " len %d val %x\n",
+                 addr, len, val);
+    return val;
+}
+
+static const MemoryRegionOps unin_data_ops = {
+    .read = unin_data_read,
+    .write = unin_data_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static int pci_unin_main_init_device(SysBusDevice *dev)
+{
+    PCIHostState *h;
+
+    /* Use values found on a real PowerMac */
+    /* Uninorth main bus */
+    h = PCI_HOST_BRIDGE(dev);
+
+    memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops,
+                          dev, "pci-conf-idx", 0x1000);
+    memory_region_init_io(&h->data_mem, &unin_data_ops, dev,
+                          "pci-conf-data", 0x1000);
+    sysbus_init_mmio(dev, &h->conf_mem);
+    sysbus_init_mmio(dev, &h->data_mem);
+
+    return 0;
+}
+
+
+static int pci_u3_agp_init_device(SysBusDevice *dev)
+{
+    PCIHostState *h;
+
+    /* Uninorth U3 AGP bus */
+    h = PCI_HOST_BRIDGE(dev);
+
+    memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops,
+                          dev, "pci-conf-idx", 0x1000);
+    memory_region_init_io(&h->data_mem, &unin_data_ops, dev,
+                          "pci-conf-data", 0x1000);
+    sysbus_init_mmio(dev, &h->conf_mem);
+    sysbus_init_mmio(dev, &h->data_mem);
+
+    return 0;
+}
+
+static int pci_unin_agp_init_device(SysBusDevice *dev)
+{
+    PCIHostState *h;
+
+    /* Uninorth AGP bus */
+    h = PCI_HOST_BRIDGE(dev);
+
+    memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops,
+                          dev, "pci-conf-idx", 0x1000);
+    memory_region_init_io(&h->data_mem, &pci_host_data_le_ops,
+                          dev, "pci-conf-data", 0x1000);
+    sysbus_init_mmio(dev, &h->conf_mem);
+    sysbus_init_mmio(dev, &h->data_mem);
+    return 0;
+}
+
+static int pci_unin_internal_init_device(SysBusDevice *dev)
+{
+    PCIHostState *h;
+
+    /* Uninorth internal bus */
+    h = PCI_HOST_BRIDGE(dev);
+
+    memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops,
+                          dev, "pci-conf-idx", 0x1000);
+    memory_region_init_io(&h->data_mem, &pci_host_data_le_ops,
+                          dev, "pci-conf-data", 0x1000);
+    sysbus_init_mmio(dev, &h->conf_mem);
+    sysbus_init_mmio(dev, &h->data_mem);
+    return 0;
+}
+
+PCIBus *pci_pmac_init(qemu_irq *pic,
+                      MemoryRegion *address_space_mem,
+                      MemoryRegion *address_space_io)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+    PCIHostState *h;
+    UNINState *d;
+
+    /* Use values found on a real PowerMac */
+    /* Uninorth main bus */
+    dev = qdev_create(NULL, TYPE_UNI_NORTH_PCI_HOST_BRIDGE);
+    qdev_init_nofail(dev);
+    s = SYS_BUS_DEVICE(dev);
+    h = PCI_HOST_BRIDGE(s);
+    d = UNI_NORTH_PCI_HOST_BRIDGE(dev);
+    memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL);
+    memory_region_init_alias(&d->pci_hole, "pci-hole", &d->pci_mmio,
+                             0x80000000ULL, 0x70000000ULL);
+    memory_region_add_subregion(address_space_mem, 0x80000000ULL,
+                                &d->pci_hole);
+
+    h->bus = pci_register_bus(dev, "pci",
+                              pci_unin_set_irq, pci_unin_map_irq,
+                              pic,
+                              &d->pci_mmio,
+                              address_space_io,
+                              PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS);
+
+#if 0
+    pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north");
+#endif
+
+    sysbus_mmio_map(s, 0, 0xf2800000);
+    sysbus_mmio_map(s, 1, 0xf2c00000);
+
+    /* DEC 21154 bridge */
+#if 0
+    /* XXX: not activated as PPC BIOS doesn't handle multiple buses properly */
+    pci_create_simple(h->bus, PCI_DEVFN(12, 0), "dec-21154");
+#endif
+
+    /* Uninorth AGP bus */
+    pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-agp");
+    dev = qdev_create(NULL, TYPE_UNI_NORTH_AGP_HOST_BRIDGE);
+    qdev_init_nofail(dev);
+    s = SYS_BUS_DEVICE(dev);
+    sysbus_mmio_map(s, 0, 0xf0800000);
+    sysbus_mmio_map(s, 1, 0xf0c00000);
+
+    /* Uninorth internal bus */
+#if 0
+    /* XXX: not needed for now */
+    pci_create_simple(h->bus, PCI_DEVFN(14, 0),
+                      "uni-north-internal-pci");
+    dev = qdev_create(NULL, TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE);
+    qdev_init_nofail(dev);
+    s = SYS_BUS_DEVICE(dev);
+    sysbus_mmio_map(s, 0, 0xf4800000);
+    sysbus_mmio_map(s, 1, 0xf4c00000);
+#endif
+
+    return h->bus;
+}
+
+PCIBus *pci_pmac_u3_init(qemu_irq *pic,
+                         MemoryRegion *address_space_mem,
+                         MemoryRegion *address_space_io)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+    PCIHostState *h;
+    UNINState *d;
+
+    /* Uninorth AGP bus */
+
+    dev = qdev_create(NULL, TYPE_U3_AGP_HOST_BRIDGE);
+    qdev_init_nofail(dev);
+    s = SYS_BUS_DEVICE(dev);
+    h = PCI_HOST_BRIDGE(dev);
+    d = U3_AGP_HOST_BRIDGE(dev);
+
+    memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL);
+    memory_region_init_alias(&d->pci_hole, "pci-hole", &d->pci_mmio,
+                             0x80000000ULL, 0x70000000ULL);
+    memory_region_add_subregion(address_space_mem, 0x80000000ULL,
+                                &d->pci_hole);
+
+    h->bus = pci_register_bus(dev, "pci",
+                              pci_unin_set_irq, pci_unin_map_irq,
+                              pic,
+                              &d->pci_mmio,
+                              address_space_io,
+                              PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS);
+
+    sysbus_mmio_map(s, 0, 0xf0800000);
+    sysbus_mmio_map(s, 1, 0xf0c00000);
+
+    pci_create_simple(h->bus, 11 << 3, "u3-agp");
+
+    return h->bus;
+}
+
+static int unin_main_pci_host_init(PCIDevice *d)
+{
+    d->config[0x0C] = 0x08; // cache_line_size
+    d->config[0x0D] = 0x10; // latency_timer
+    d->config[0x34] = 0x00; // capabilities_pointer
+    return 0;
+}
+
+static int unin_agp_pci_host_init(PCIDevice *d)
+{
+    d->config[0x0C] = 0x08; // cache_line_size
+    d->config[0x0D] = 0x10; // latency_timer
+    //    d->config[0x34] = 0x80; // capabilities_pointer
+    return 0;
+}
+
+static int u3_agp_pci_host_init(PCIDevice *d)
+{
+    /* cache line size */
+    d->config[0x0C] = 0x08;
+    /* latency timer */
+    d->config[0x0D] = 0x10;
+    return 0;
+}
+
+static int unin_internal_pci_host_init(PCIDevice *d)
+{
+    d->config[0x0C] = 0x08; // cache_line_size
+    d->config[0x0D] = 0x10; // latency_timer
+    d->config[0x34] = 0x00; // capabilities_pointer
+    return 0;
+}
+
+static void unin_main_pci_host_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init      = unin_main_pci_host_init;
+    k->vendor_id = PCI_VENDOR_ID_APPLE;
+    k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_PCI;
+    k->revision  = 0x00;
+    k->class_id  = PCI_CLASS_BRIDGE_HOST;
+}
+
+static const TypeInfo unin_main_pci_host_info = {
+    .name = "uni-north-pci",
+    .parent = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIDevice),
+    .class_init = unin_main_pci_host_class_init,
+};
+
+static void u3_agp_pci_host_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init      = u3_agp_pci_host_init;
+    k->vendor_id = PCI_VENDOR_ID_APPLE;
+    k->device_id = PCI_DEVICE_ID_APPLE_U3_AGP;
+    k->revision  = 0x00;
+    k->class_id  = PCI_CLASS_BRIDGE_HOST;
+}
+
+static const TypeInfo u3_agp_pci_host_info = {
+    .name = "u3-agp",
+    .parent = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIDevice),
+    .class_init = u3_agp_pci_host_class_init,
+};
+
+static void unin_agp_pci_host_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init      = unin_agp_pci_host_init;
+    k->vendor_id = PCI_VENDOR_ID_APPLE;
+    k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_AGP;
+    k->revision  = 0x00;
+    k->class_id  = PCI_CLASS_BRIDGE_HOST;
+}
+
+static const TypeInfo unin_agp_pci_host_info = {
+    .name = "uni-north-agp",
+    .parent = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIDevice),
+    .class_init = unin_agp_pci_host_class_init,
+};
+
+static void unin_internal_pci_host_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init      = unin_internal_pci_host_init;
+    k->vendor_id = PCI_VENDOR_ID_APPLE;
+    k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_I_PCI;
+    k->revision  = 0x00;
+    k->class_id  = PCI_CLASS_BRIDGE_HOST;
+}
+
+static const TypeInfo unin_internal_pci_host_info = {
+    .name = "uni-north-internal-pci",
+    .parent = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIDevice),
+    .class_init = unin_internal_pci_host_class_init,
+};
+
+static void pci_unin_main_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sbc->init = pci_unin_main_init_device;
+}
+
+static const TypeInfo pci_unin_main_info = {
+    .name          = TYPE_UNI_NORTH_PCI_HOST_BRIDGE,
+    .parent        = TYPE_PCI_HOST_BRIDGE,
+    .instance_size = sizeof(UNINState),
+    .class_init    = pci_unin_main_class_init,
+};
+
+static void pci_u3_agp_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sbc->init = pci_u3_agp_init_device;
+}
+
+static const TypeInfo pci_u3_agp_info = {
+    .name          = TYPE_U3_AGP_HOST_BRIDGE,
+    .parent        = TYPE_PCI_HOST_BRIDGE,
+    .instance_size = sizeof(UNINState),
+    .class_init    = pci_u3_agp_class_init,
+};
+
+static void pci_unin_agp_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sbc->init = pci_unin_agp_init_device;
+}
+
+static const TypeInfo pci_unin_agp_info = {
+    .name          = TYPE_UNI_NORTH_AGP_HOST_BRIDGE,
+    .parent        = TYPE_PCI_HOST_BRIDGE,
+    .instance_size = sizeof(UNINState),
+    .class_init    = pci_unin_agp_class_init,
+};
+
+static void pci_unin_internal_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sbc->init = pci_unin_internal_init_device;
+}
+
+static const TypeInfo pci_unin_internal_info = {
+    .name          = TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE,
+    .parent        = TYPE_PCI_HOST_BRIDGE,
+    .instance_size = sizeof(UNINState),
+    .class_init    = pci_unin_internal_class_init,
+};
+
+static void unin_register_types(void)
+{
+    type_register_static(&unin_main_pci_host_info);
+    type_register_static(&u3_agp_pci_host_info);
+    type_register_static(&unin_agp_pci_host_info);
+    type_register_static(&unin_internal_pci_host_info);
+
+    type_register_static(&pci_unin_main_info);
+    type_register_static(&pci_u3_agp_info);
+    type_register_static(&pci_unin_agp_info);
+    type_register_static(&pci_unin_internal_info);
+}
+
+type_init(unin_register_types)
diff --git a/hw/pci/host/versatile.c b/hw/pci/host/versatile.c
new file mode 100644 (file)
index 0000000..d67ca79
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * ARM Versatile/PB PCI host controller
+ *
+ * Copyright (c) 2006-2009 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the LGPL.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_host.h"
+#include "exec/address-spaces.h"
+
+typedef struct {
+    SysBusDevice busdev;
+    qemu_irq irq[4];
+    int realview;
+    MemoryRegion mem_config;
+    MemoryRegion mem_config2;
+    MemoryRegion isa;
+} PCIVPBState;
+
+static inline uint32_t vpb_pci_config_addr(hwaddr addr)
+{
+    return addr & 0xffffff;
+}
+
+static void pci_vpb_config_write(void *opaque, hwaddr addr,
+                                 uint64_t val, unsigned size)
+{
+    pci_data_write(opaque, vpb_pci_config_addr(addr), val, size);
+}
+
+static uint64_t pci_vpb_config_read(void *opaque, hwaddr addr,
+                                    unsigned size)
+{
+    uint32_t val;
+    val = pci_data_read(opaque, vpb_pci_config_addr(addr), size);
+    return val;
+}
+
+static const MemoryRegionOps pci_vpb_config_ops = {
+    .read = pci_vpb_config_read,
+    .write = pci_vpb_config_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int pci_vpb_map_irq(PCIDevice *d, int irq_num)
+{
+    return irq_num;
+}
+
+static void pci_vpb_set_irq(void *opaque, int irq_num, int level)
+{
+    qemu_irq *pic = opaque;
+
+    qemu_set_irq(pic[irq_num], level);
+}
+
+static int pci_vpb_init(SysBusDevice *dev)
+{
+    PCIVPBState *s = FROM_SYSBUS(PCIVPBState, dev);
+    PCIBus *bus;
+    int i;
+
+    for (i = 0; i < 4; i++) {
+        sysbus_init_irq(dev, &s->irq[i]);
+    }
+    bus = pci_register_bus(&dev->qdev, "pci",
+                           pci_vpb_set_irq, pci_vpb_map_irq, s->irq,
+                           get_system_memory(), get_system_io(),
+                           PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS);
+
+    /* ??? Register memory space.  */
+
+    /* Our memory regions are:
+     * 0 : PCI self config window
+     * 1 : PCI config window
+     * 2 : PCI IO window (realview_pci only)
+     */
+    memory_region_init_io(&s->mem_config, &pci_vpb_config_ops, bus,
+                          "pci-vpb-selfconfig", 0x1000000);
+    sysbus_init_mmio(dev, &s->mem_config);
+    memory_region_init_io(&s->mem_config2, &pci_vpb_config_ops, bus,
+                          "pci-vpb-config", 0x1000000);
+    sysbus_init_mmio(dev, &s->mem_config2);
+    if (s->realview) {
+        isa_mmio_setup(&s->isa, 0x0100000);
+        sysbus_init_mmio(dev, &s->isa);
+    }
+
+    pci_create_simple(bus, -1, "versatile_pci_host");
+    return 0;
+}
+
+static int pci_realview_init(SysBusDevice *dev)
+{
+    PCIVPBState *s = FROM_SYSBUS(PCIVPBState, dev);
+    s->realview = 1;
+    return pci_vpb_init(dev);
+}
+
+static int versatile_pci_host_init(PCIDevice *d)
+{
+    pci_set_word(d->config + PCI_STATUS,
+                PCI_STATUS_66MHZ | PCI_STATUS_DEVSEL_MEDIUM);
+    pci_set_byte(d->config + PCI_LATENCY_TIMER, 0x10);
+    return 0;
+}
+
+static void versatile_pci_host_class_init(ObjectClass *klass, void *data)
+{
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = versatile_pci_host_init;
+    k->vendor_id = PCI_VENDOR_ID_XILINX;
+    k->device_id = PCI_DEVICE_ID_XILINX_XC2VP30;
+    k->class_id = PCI_CLASS_PROCESSOR_CO;
+}
+
+static const TypeInfo versatile_pci_host_info = {
+    .name          = "versatile_pci_host",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIDevice),
+    .class_init    = versatile_pci_host_class_init,
+};
+
+static void pci_vpb_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = pci_vpb_init;
+}
+
+static const TypeInfo pci_vpb_info = {
+    .name          = "versatile_pci",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(PCIVPBState),
+    .class_init    = pci_vpb_class_init,
+};
+
+static void pci_realview_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = pci_realview_init;
+}
+
+static const TypeInfo pci_realview_info = {
+    .name          = "realview_pci",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(PCIVPBState),
+    .class_init    = pci_realview_class_init,
+};
+
+static void versatile_pci_register_types(void)
+{
+    type_register_static(&pci_vpb_info);
+    type_register_static(&pci_realview_info);
+    type_register_static(&versatile_pci_host_info);
+}
+
+type_init(versatile_pci_register_types)
diff --git a/hw/pci_bridge_dev.c b/hw/pci_bridge_dev.c
deleted file mode 100644 (file)
index 971b432..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Standard PCI Bridge Device
- *
- * Copyright (c) 2011 Red Hat Inc. Author: Michael S. Tsirkin <mst@redhat.com>
- *
- * http://www.pcisig.com/specifications/conventional/pci_to_pci_bridge_architecture/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/pci/pci_bridge.h"
-#include "hw/pci/pci_ids.h"
-#include "hw/pci/msi.h"
-#include "hw/pci/shpc.h"
-#include "hw/pci/slotid_cap.h"
-#include "exec/memory.h"
-#include "hw/pci/pci_bus.h"
-
-struct PCIBridgeDev {
-    PCIBridge bridge;
-    MemoryRegion bar;
-    uint8_t chassis_nr;
-#define PCI_BRIDGE_DEV_F_MSI_REQ 0
-    uint32_t flags;
-};
-typedef struct PCIBridgeDev PCIBridgeDev;
-
-static int pci_bridge_dev_initfn(PCIDevice *dev)
-{
-    PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev);
-    PCIBridgeDev *bridge_dev = DO_UPCAST(PCIBridgeDev, bridge, br);
-    int err;
-
-    err = pci_bridge_initfn(dev, TYPE_PCI_BUS);
-    if (err) {
-        goto bridge_error;
-    }
-    memory_region_init(&bridge_dev->bar, "shpc-bar", shpc_bar_size(dev));
-    err = shpc_init(dev, &br->sec_bus, &bridge_dev->bar, 0);
-    if (err) {
-        goto shpc_error;
-    }
-    err = slotid_cap_init(dev, 0, bridge_dev->chassis_nr, 0);
-    if (err) {
-        goto slotid_error;
-    }
-    if ((bridge_dev->flags & (1 << PCI_BRIDGE_DEV_F_MSI_REQ)) &&
-        msi_supported) {
-        err = msi_init(dev, 0, 1, true, true);
-        if (err < 0) {
-            goto msi_error;
-        }
-    }
-    /* TODO: spec recommends using 64 bit prefetcheable BAR.
-     * Check whether that works well. */
-    pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY |
-                    PCI_BASE_ADDRESS_MEM_TYPE_64, &bridge_dev->bar);
-    dev->config[PCI_INTERRUPT_PIN] = 0x1;
-    return 0;
-msi_error:
-    slotid_cap_cleanup(dev);
-slotid_error:
-    shpc_cleanup(dev, &bridge_dev->bar);
-shpc_error:
-    memory_region_destroy(&bridge_dev->bar);
-    pci_bridge_exitfn(dev);
-bridge_error:
-    return err;
-}
-
-static void pci_bridge_dev_exitfn(PCIDevice *dev)
-{
-    PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev);
-    PCIBridgeDev *bridge_dev = DO_UPCAST(PCIBridgeDev, bridge, br);
-    if (msi_present(dev)) {
-        msi_uninit(dev);
-    }
-    slotid_cap_cleanup(dev);
-    shpc_cleanup(dev, &bridge_dev->bar);
-    memory_region_destroy(&bridge_dev->bar);
-    pci_bridge_exitfn(dev);
-}
-
-static void pci_bridge_dev_write_config(PCIDevice *d,
-                                        uint32_t address, uint32_t val, int len)
-{
-    pci_bridge_write_config(d, address, val, len);
-    if (msi_present(d)) {
-        msi_write_config(d, address, val, len);
-    }
-    shpc_cap_write_config(d, address, val, len);
-}
-
-static void qdev_pci_bridge_dev_reset(DeviceState *qdev)
-{
-    PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev);
-
-    pci_bridge_reset(qdev);
-    shpc_reset(dev);
-}
-
-static Property pci_bridge_dev_properties[] = {
-                    /* Note: 0 is not a legal chassis number. */
-    DEFINE_PROP_UINT8("chassis_nr", PCIBridgeDev, chassis_nr, 0),
-    DEFINE_PROP_BIT("msi", PCIBridgeDev, flags, PCI_BRIDGE_DEV_F_MSI_REQ, true),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static const VMStateDescription pci_bridge_dev_vmstate = {
-    .name = "pci_bridge",
-    .fields = (VMStateField[]) {
-        VMSTATE_PCI_DEVICE(bridge.dev, PCIBridgeDev),
-        SHPC_VMSTATE(bridge.dev.shpc, PCIBridgeDev),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void pci_bridge_dev_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-    k->init = pci_bridge_dev_initfn;
-    k->exit = pci_bridge_dev_exitfn;
-    k->config_write = pci_bridge_dev_write_config;
-    k->vendor_id = PCI_VENDOR_ID_REDHAT;
-    k->device_id = PCI_DEVICE_ID_REDHAT_BRIDGE;
-    k->class_id = PCI_CLASS_BRIDGE_PCI;
-    k->is_bridge = 1,
-    dc->desc = "Standard PCI Bridge";
-    dc->reset = qdev_pci_bridge_dev_reset;
-    dc->props = pci_bridge_dev_properties;
-    dc->vmsd = &pci_bridge_dev_vmstate;
-}
-
-static const TypeInfo pci_bridge_dev_info = {
-    .name = "pci-bridge",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIBridgeDev),
-    .class_init = pci_bridge_dev_class_init,
-};
-
-static void pci_bridge_dev_register(void)
-{
-    type_register_static(&pci_bridge_dev_info);
-}
-
-type_init(pci_bridge_dev_register);
diff --git a/hw/pckbd.c b/hw/pckbd.c
deleted file mode 100644 (file)
index 08ceb9f..0000000
+++ /dev/null
@@ -1,527 +0,0 @@
-/*
- * QEMU PC keyboard emulation
- *
- * Copyright (c) 2003 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/isa/isa.h"
-#include "hw/i386/pc.h"
-#include "hw/input/ps2.h"
-#include "sysemu/sysemu.h"
-
-/* debug PC keyboard */
-//#define DEBUG_KBD
-#ifdef DEBUG_KBD
-#define DPRINTF(fmt, ...)                                       \
-    do { printf("KBD: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...)
-#endif
-
-/*     Keyboard Controller Commands */
-#define KBD_CCMD_READ_MODE     0x20    /* Read mode bits */
-#define KBD_CCMD_WRITE_MODE    0x60    /* Write mode bits */
-#define KBD_CCMD_GET_VERSION   0xA1    /* Get controller version */
-#define KBD_CCMD_MOUSE_DISABLE 0xA7    /* Disable mouse interface */
-#define KBD_CCMD_MOUSE_ENABLE  0xA8    /* Enable mouse interface */
-#define KBD_CCMD_TEST_MOUSE    0xA9    /* Mouse interface test */
-#define KBD_CCMD_SELF_TEST     0xAA    /* Controller self test */
-#define KBD_CCMD_KBD_TEST      0xAB    /* Keyboard interface test */
-#define KBD_CCMD_KBD_DISABLE   0xAD    /* Keyboard interface disable */
-#define KBD_CCMD_KBD_ENABLE    0xAE    /* Keyboard interface enable */
-#define KBD_CCMD_READ_INPORT    0xC0    /* read input port */
-#define KBD_CCMD_READ_OUTPORT  0xD0    /* read output port */
-#define KBD_CCMD_WRITE_OUTPORT 0xD1    /* write output port */
-#define KBD_CCMD_WRITE_OBUF    0xD2
-#define KBD_CCMD_WRITE_AUX_OBUF        0xD3    /* Write to output buffer as if
-                                          initiated by the auxiliary device */
-#define KBD_CCMD_WRITE_MOUSE   0xD4    /* Write the following byte to the mouse */
-#define KBD_CCMD_DISABLE_A20    0xDD    /* HP vectra only ? */
-#define KBD_CCMD_ENABLE_A20     0xDF    /* HP vectra only ? */
-#define KBD_CCMD_PULSE_BITS_3_0 0xF0    /* Pulse bits 3-0 of the output port P2. */
-#define KBD_CCMD_RESET          0xFE    /* Pulse bit 0 of the output port P2 = CPU reset. */
-#define KBD_CCMD_NO_OP          0xFF    /* Pulse no bits of the output port P2. */
-
-/* Keyboard Commands */
-#define KBD_CMD_SET_LEDS       0xED    /* Set keyboard leds */
-#define KBD_CMD_ECHO           0xEE
-#define KBD_CMD_GET_ID                 0xF2    /* get keyboard ID */
-#define KBD_CMD_SET_RATE       0xF3    /* Set typematic rate */
-#define KBD_CMD_ENABLE         0xF4    /* Enable scanning */
-#define KBD_CMD_RESET_DISABLE  0xF5    /* reset and disable scanning */
-#define KBD_CMD_RESET_ENABLE           0xF6    /* reset and enable scanning */
-#define KBD_CMD_RESET          0xFF    /* Reset */
-
-/* Keyboard Replies */
-#define KBD_REPLY_POR          0xAA    /* Power on reset */
-#define KBD_REPLY_ACK          0xFA    /* Command ACK */
-#define KBD_REPLY_RESEND       0xFE    /* Command NACK, send the cmd again */
-
-/* Status Register Bits */
-#define KBD_STAT_OBF           0x01    /* Keyboard output buffer full */
-#define KBD_STAT_IBF           0x02    /* Keyboard input buffer full */
-#define KBD_STAT_SELFTEST      0x04    /* Self test successful */
-#define KBD_STAT_CMD           0x08    /* Last write was a command write (0=data) */
-#define KBD_STAT_UNLOCKED      0x10    /* Zero if keyboard locked */
-#define KBD_STAT_MOUSE_OBF     0x20    /* Mouse output buffer full */
-#define KBD_STAT_GTO           0x40    /* General receive/xmit timeout */
-#define KBD_STAT_PERR          0x80    /* Parity error */
-
-/* Controller Mode Register Bits */
-#define KBD_MODE_KBD_INT       0x01    /* Keyboard data generate IRQ1 */
-#define KBD_MODE_MOUSE_INT     0x02    /* Mouse data generate IRQ12 */
-#define KBD_MODE_SYS           0x04    /* The system flag (?) */
-#define KBD_MODE_NO_KEYLOCK    0x08    /* The keylock doesn't affect the keyboard if set */
-#define KBD_MODE_DISABLE_KBD   0x10    /* Disable keyboard interface */
-#define KBD_MODE_DISABLE_MOUSE 0x20    /* Disable mouse interface */
-#define KBD_MODE_KCC           0x40    /* Scan code conversion to PC format */
-#define KBD_MODE_RFU           0x80
-
-/* Output Port Bits */
-#define KBD_OUT_RESET           0x01    /* 1=normal mode, 0=reset */
-#define KBD_OUT_A20             0x02    /* x86 only */
-#define KBD_OUT_OBF             0x10    /* Keyboard output buffer full */
-#define KBD_OUT_MOUSE_OBF       0x20    /* Mouse output buffer full */
-
-/* Mouse Commands */
-#define AUX_SET_SCALE11                0xE6    /* Set 1:1 scaling */
-#define AUX_SET_SCALE21                0xE7    /* Set 2:1 scaling */
-#define AUX_SET_RES            0xE8    /* Set resolution */
-#define AUX_GET_SCALE          0xE9    /* Get scaling factor */
-#define AUX_SET_STREAM         0xEA    /* Set stream mode */
-#define AUX_POLL               0xEB    /* Poll */
-#define AUX_RESET_WRAP         0xEC    /* Reset wrap mode */
-#define AUX_SET_WRAP           0xEE    /* Set wrap mode */
-#define AUX_SET_REMOTE         0xF0    /* Set remote mode */
-#define AUX_GET_TYPE           0xF2    /* Get type */
-#define AUX_SET_SAMPLE         0xF3    /* Set sample rate */
-#define AUX_ENABLE_DEV         0xF4    /* Enable aux device */
-#define AUX_DISABLE_DEV                0xF5    /* Disable aux device */
-#define AUX_SET_DEFAULT                0xF6
-#define AUX_RESET              0xFF    /* Reset aux device */
-#define AUX_ACK                        0xFA    /* Command byte ACK. */
-
-#define MOUSE_STATUS_REMOTE     0x40
-#define MOUSE_STATUS_ENABLED    0x20
-#define MOUSE_STATUS_SCALE21    0x10
-
-#define KBD_PENDING_KBD         1
-#define KBD_PENDING_AUX         2
-
-typedef struct KBDState {
-    uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
-    uint8_t status;
-    uint8_t mode;
-    uint8_t outport;
-    /* Bitmask of devices with data available.  */
-    uint8_t pending;
-    void *kbd;
-    void *mouse;
-
-    qemu_irq irq_kbd;
-    qemu_irq irq_mouse;
-    qemu_irq *a20_out;
-    hwaddr mask;
-} KBDState;
-
-/* update irq and KBD_STAT_[MOUSE_]OBF */
-/* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be
-   incorrect, but it avoids having to simulate exact delays */
-static void kbd_update_irq(KBDState *s)
-{
-    int irq_kbd_level, irq_mouse_level;
-
-    irq_kbd_level = 0;
-    irq_mouse_level = 0;
-    s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
-    s->outport &= ~(KBD_OUT_OBF | KBD_OUT_MOUSE_OBF);
-    if (s->pending) {
-        s->status |= KBD_STAT_OBF;
-        s->outport |= KBD_OUT_OBF;
-        /* kbd data takes priority over aux data.  */
-        if (s->pending == KBD_PENDING_AUX) {
-            s->status |= KBD_STAT_MOUSE_OBF;
-            s->outport |= KBD_OUT_MOUSE_OBF;
-            if (s->mode & KBD_MODE_MOUSE_INT)
-                irq_mouse_level = 1;
-        } else {
-            if ((s->mode & KBD_MODE_KBD_INT) &&
-                !(s->mode & KBD_MODE_DISABLE_KBD))
-                irq_kbd_level = 1;
-        }
-    }
-    qemu_set_irq(s->irq_kbd, irq_kbd_level);
-    qemu_set_irq(s->irq_mouse, irq_mouse_level);
-}
-
-static void kbd_update_kbd_irq(void *opaque, int level)
-{
-    KBDState *s = (KBDState *)opaque;
-
-    if (level)
-        s->pending |= KBD_PENDING_KBD;
-    else
-        s->pending &= ~KBD_PENDING_KBD;
-    kbd_update_irq(s);
-}
-
-static void kbd_update_aux_irq(void *opaque, int level)
-{
-    KBDState *s = (KBDState *)opaque;
-
-    if (level)
-        s->pending |= KBD_PENDING_AUX;
-    else
-        s->pending &= ~KBD_PENDING_AUX;
-    kbd_update_irq(s);
-}
-
-static uint64_t kbd_read_status(void *opaque, hwaddr addr,
-                                unsigned size)
-{
-    KBDState *s = opaque;
-    int val;
-    val = s->status;
-    DPRINTF("kbd: read status=0x%02x\n", val);
-    return val;
-}
-
-static void kbd_queue(KBDState *s, int b, int aux)
-{
-    if (aux)
-        ps2_queue(s->mouse, b);
-    else
-        ps2_queue(s->kbd, b);
-}
-
-static void outport_write(KBDState *s, uint32_t val)
-{
-    DPRINTF("kbd: write outport=0x%02x\n", val);
-    s->outport = val;
-    if (s->a20_out) {
-        qemu_set_irq(*s->a20_out, (val >> 1) & 1);
-    }
-    if (!(val & 1)) {
-        qemu_system_reset_request();
-    }
-}
-
-static void kbd_write_command(void *opaque, hwaddr addr,
-                              uint64_t val, unsigned size)
-{
-    KBDState *s = opaque;
-
-    DPRINTF("kbd: write cmd=0x%02x\n", val);
-
-    /* Bits 3-0 of the output port P2 of the keyboard controller may be pulsed
-     * low for approximately 6 micro seconds. Bits 3-0 of the KBD_CCMD_PULSE
-     * command specify the output port bits to be pulsed.
-     * 0: Bit should be pulsed. 1: Bit should not be modified.
-     * The only useful version of this command is pulsing bit 0,
-     * which does a CPU reset.
-     */
-    if((val & KBD_CCMD_PULSE_BITS_3_0) == KBD_CCMD_PULSE_BITS_3_0) {
-        if(!(val & 1))
-            val = KBD_CCMD_RESET;
-        else
-            val = KBD_CCMD_NO_OP;
-    }
-
-    switch(val) {
-    case KBD_CCMD_READ_MODE:
-        kbd_queue(s, s->mode, 0);
-        break;
-    case KBD_CCMD_WRITE_MODE:
-    case KBD_CCMD_WRITE_OBUF:
-    case KBD_CCMD_WRITE_AUX_OBUF:
-    case KBD_CCMD_WRITE_MOUSE:
-    case KBD_CCMD_WRITE_OUTPORT:
-        s->write_cmd = val;
-        break;
-    case KBD_CCMD_MOUSE_DISABLE:
-        s->mode |= KBD_MODE_DISABLE_MOUSE;
-        break;
-    case KBD_CCMD_MOUSE_ENABLE:
-        s->mode &= ~KBD_MODE_DISABLE_MOUSE;
-        break;
-    case KBD_CCMD_TEST_MOUSE:
-        kbd_queue(s, 0x00, 0);
-        break;
-    case KBD_CCMD_SELF_TEST:
-        s->status |= KBD_STAT_SELFTEST;
-        kbd_queue(s, 0x55, 0);
-        break;
-    case KBD_CCMD_KBD_TEST:
-        kbd_queue(s, 0x00, 0);
-        break;
-    case KBD_CCMD_KBD_DISABLE:
-        s->mode |= KBD_MODE_DISABLE_KBD;
-        kbd_update_irq(s);
-        break;
-    case KBD_CCMD_KBD_ENABLE:
-        s->mode &= ~KBD_MODE_DISABLE_KBD;
-        kbd_update_irq(s);
-        break;
-    case KBD_CCMD_READ_INPORT:
-        kbd_queue(s, 0x00, 0);
-        break;
-    case KBD_CCMD_READ_OUTPORT:
-        kbd_queue(s, s->outport, 0);
-        break;
-    case KBD_CCMD_ENABLE_A20:
-        if (s->a20_out) {
-            qemu_irq_raise(*s->a20_out);
-        }
-        s->outport |= KBD_OUT_A20;
-        break;
-    case KBD_CCMD_DISABLE_A20:
-        if (s->a20_out) {
-            qemu_irq_lower(*s->a20_out);
-        }
-        s->outport &= ~KBD_OUT_A20;
-        break;
-    case KBD_CCMD_RESET:
-        qemu_system_reset_request();
-        break;
-    case KBD_CCMD_NO_OP:
-        /* ignore that */
-        break;
-    default:
-        fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", (int)val);
-        break;
-    }
-}
-
-static uint64_t kbd_read_data(void *opaque, hwaddr addr,
-                              unsigned size)
-{
-    KBDState *s = opaque;
-    uint32_t val;
-
-    if (s->pending == KBD_PENDING_AUX)
-        val = ps2_read_data(s->mouse);
-    else
-        val = ps2_read_data(s->kbd);
-
-    DPRINTF("kbd: read data=0x%02x\n", val);
-    return val;
-}
-
-static void kbd_write_data(void *opaque, hwaddr addr,
-                           uint64_t val, unsigned size)
-{
-    KBDState *s = opaque;
-
-    DPRINTF("kbd: write data=0x%02x\n", val);
-
-    switch(s->write_cmd) {
-    case 0:
-        ps2_write_keyboard(s->kbd, val);
-        break;
-    case KBD_CCMD_WRITE_MODE:
-        s->mode = val;
-        ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0);
-        /* ??? */
-        kbd_update_irq(s);
-        break;
-    case KBD_CCMD_WRITE_OBUF:
-        kbd_queue(s, val, 0);
-        break;
-    case KBD_CCMD_WRITE_AUX_OBUF:
-        kbd_queue(s, val, 1);
-        break;
-    case KBD_CCMD_WRITE_OUTPORT:
-        outport_write(s, val);
-        break;
-    case KBD_CCMD_WRITE_MOUSE:
-        ps2_write_mouse(s->mouse, val);
-        break;
-    default:
-        break;
-    }
-    s->write_cmd = 0;
-}
-
-static void kbd_reset(void *opaque)
-{
-    KBDState *s = opaque;
-
-    s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
-    s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
-    s->outport = KBD_OUT_RESET | KBD_OUT_A20;
-}
-
-static const VMStateDescription vmstate_kbd = {
-    .name = "pckbd",
-    .version_id = 3,
-    .minimum_version_id = 3,
-    .minimum_version_id_old = 3,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT8(write_cmd, KBDState),
-        VMSTATE_UINT8(status, KBDState),
-        VMSTATE_UINT8(mode, KBDState),
-        VMSTATE_UINT8(pending, KBDState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-/* Memory mapped interface */
-static uint32_t kbd_mm_readb (void *opaque, hwaddr addr)
-{
-    KBDState *s = opaque;
-
-    if (addr & s->mask)
-        return kbd_read_status(s, 0, 1) & 0xff;
-    else
-        return kbd_read_data(s, 0, 1) & 0xff;
-}
-
-static void kbd_mm_writeb (void *opaque, hwaddr addr, uint32_t value)
-{
-    KBDState *s = opaque;
-
-    if (addr & s->mask)
-        kbd_write_command(s, 0, value & 0xff, 1);
-    else
-        kbd_write_data(s, 0, value & 0xff, 1);
-}
-
-static const MemoryRegionOps i8042_mmio_ops = {
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .old_mmio = {
-        .read = { kbd_mm_readb, kbd_mm_readb, kbd_mm_readb },
-        .write = { kbd_mm_writeb, kbd_mm_writeb, kbd_mm_writeb },
-    },
-};
-
-void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
-                   MemoryRegion *region, ram_addr_t size,
-                   hwaddr mask)
-{
-    KBDState *s = g_malloc0(sizeof(KBDState));
-
-    s->irq_kbd = kbd_irq;
-    s->irq_mouse = mouse_irq;
-    s->mask = mask;
-
-    vmstate_register(NULL, 0, &vmstate_kbd, s);
-
-    memory_region_init_io(region, &i8042_mmio_ops, s, "i8042", size);
-
-    s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
-    s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
-    qemu_register_reset(kbd_reset, s);
-}
-
-typedef struct ISAKBDState {
-    ISADevice dev;
-    KBDState kbd;
-    MemoryRegion io[2];
-} ISAKBDState;
-
-void i8042_isa_mouse_fake_event(void *opaque)
-{
-    ISADevice *dev = opaque;
-    KBDState *s = &(DO_UPCAST(ISAKBDState, dev, dev)->kbd);
-
-    ps2_mouse_fake_event(s->mouse);
-}
-
-void i8042_setup_a20_line(ISADevice *dev, qemu_irq *a20_out)
-{
-    KBDState *s = &(DO_UPCAST(ISAKBDState, dev, dev)->kbd);
-
-    s->a20_out = a20_out;
-}
-
-static const VMStateDescription vmstate_kbd_isa = {
-    .name = "pckbd",
-    .version_id = 3,
-    .minimum_version_id = 3,
-    .minimum_version_id_old = 3,
-    .fields      = (VMStateField []) {
-        VMSTATE_STRUCT(kbd, ISAKBDState, 0, vmstate_kbd, KBDState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const MemoryRegionOps i8042_data_ops = {
-    .read = kbd_read_data,
-    .write = kbd_write_data,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static const MemoryRegionOps i8042_cmd_ops = {
-    .read = kbd_read_status,
-    .write = kbd_write_command,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static int i8042_initfn(ISADevice *dev)
-{
-    ISAKBDState *isa_s = DO_UPCAST(ISAKBDState, dev, dev);
-    KBDState *s = &isa_s->kbd;
-
-    isa_init_irq(dev, &s->irq_kbd, 1);
-    isa_init_irq(dev, &s->irq_mouse, 12);
-
-    memory_region_init_io(isa_s->io + 0, &i8042_data_ops, s, "i8042-data", 1);
-    isa_register_ioport(dev, isa_s->io + 0, 0x60);
-
-    memory_region_init_io(isa_s->io + 1, &i8042_cmd_ops, s, "i8042-cmd", 1);
-    isa_register_ioport(dev, isa_s->io + 1, 0x64);
-
-    s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
-    s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
-    qemu_register_reset(kbd_reset, s);
-    return 0;
-}
-
-static void i8042_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
-    ic->init = i8042_initfn;
-    dc->no_user = 1;
-    dc->vmsd = &vmstate_kbd_isa;
-}
-
-static const TypeInfo i8042_info = {
-    .name          = "i8042",
-    .parent        = TYPE_ISA_DEVICE,
-    .instance_size = sizeof(ISAKBDState),
-    .class_init    = i8042_class_initfn,
-};
-
-static void i8042_register_types(void)
-{
-    type_register_static(&i8042_info);
-}
-
-type_init(i8042_register_types)
diff --git a/hw/pcnet-pci.c b/hw/pcnet-pci.c
deleted file mode 100644 (file)
index 61af57e..0000000
+++ /dev/null
@@ -1,376 +0,0 @@
-/*
- * QEMU AMD PC-Net II (Am79C970A) PCI emulation
- *
- * Copyright (c) 2004 Antony T Curtis
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-/* This software was written to be compatible with the specification:
- * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet
- * AMD Publication# 19436  Rev:E  Amendment/0  Issue Date: June 2000
- */
-
-#include "hw/pci/pci.h"
-#include "net/net.h"
-#include "hw/loader.h"
-#include "qemu/timer.h"
-#include "sysemu/dma.h"
-
-#include "hw/pcnet.h"
-
-//#define PCNET_DEBUG
-//#define PCNET_DEBUG_IO
-//#define PCNET_DEBUG_BCR
-//#define PCNET_DEBUG_CSR
-//#define PCNET_DEBUG_RMD
-//#define PCNET_DEBUG_TMD
-//#define PCNET_DEBUG_MATCH
-
-
-typedef struct {
-    PCIDevice pci_dev;
-    PCNetState state;
-    MemoryRegion io_bar;
-} PCIPCNetState;
-
-static void pcnet_aprom_writeb(void *opaque, uint32_t addr, uint32_t val)
-{
-    PCNetState *s = opaque;
-#ifdef PCNET_DEBUG
-    printf("pcnet_aprom_writeb addr=0x%08x val=0x%02x\n", addr, val);
-#endif
-    if (BCR_APROMWE(s)) {
-        s->prom[addr & 15] = val;
-    }
-}
-
-static uint32_t pcnet_aprom_readb(void *opaque, uint32_t addr)
-{
-    PCNetState *s = opaque;
-    uint32_t val = s->prom[addr & 15];
-#ifdef PCNET_DEBUG
-    printf("pcnet_aprom_readb addr=0x%08x val=0x%02x\n", addr, val);
-#endif
-    return val;
-}
-
-static uint64_t pcnet_ioport_read(void *opaque, hwaddr addr,
-                                  unsigned size)
-{
-    PCNetState *d = opaque;
-
-    if (addr < 0x10) {
-        if (!BCR_DWIO(d) && size == 1) {
-            return pcnet_aprom_readb(d, addr);
-        } else if (!BCR_DWIO(d) && (addr & 1) == 0 && size == 2) {
-            return pcnet_aprom_readb(d, addr) |
-                   (pcnet_aprom_readb(d, addr + 1) << 8);
-        } else if (BCR_DWIO(d) && (addr & 3) == 0 && size == 4) {
-            return pcnet_aprom_readb(d, addr) |
-                   (pcnet_aprom_readb(d, addr + 1) << 8) |
-                   (pcnet_aprom_readb(d, addr + 2) << 16) |
-                   (pcnet_aprom_readb(d, addr + 3) << 24);
-        }
-    } else {
-        if (size == 2) {
-            return pcnet_ioport_readw(d, addr);
-        } else if (size == 4) {
-            return pcnet_ioport_readl(d, addr);
-        }
-    }
-    return ((uint64_t)1 << (size * 8)) - 1;
-}
-
-static void pcnet_ioport_write(void *opaque, hwaddr addr,
-                               uint64_t data, unsigned size)
-{
-    PCNetState *d = opaque;
-
-    if (addr < 0x10) {
-        if (!BCR_DWIO(d) && size == 1) {
-            pcnet_aprom_writeb(d, addr, data);
-        } else if (!BCR_DWIO(d) && (addr & 1) == 0 && size == 2) {
-            pcnet_aprom_writeb(d, addr, data & 0xff);
-            pcnet_aprom_writeb(d, addr + 1, data >> 8);
-        } else if (BCR_DWIO(d) && (addr & 3) == 0 && size == 4) {
-            pcnet_aprom_writeb(d, addr, data & 0xff);
-            pcnet_aprom_writeb(d, addr + 1, (data >> 8) & 0xff);
-            pcnet_aprom_writeb(d, addr + 2, (data >> 16) & 0xff);
-            pcnet_aprom_writeb(d, addr + 3, data >> 24);
-        }
-    } else {
-        if (size == 2) {
-            pcnet_ioport_writew(d, addr, data);
-        } else if (size == 4) {
-            pcnet_ioport_writel(d, addr, data);
-        }
-    }
-}
-
-static const MemoryRegionOps pcnet_io_ops = {
-    .read = pcnet_ioport_read,
-    .write = pcnet_ioport_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void pcnet_mmio_writeb(void *opaque, hwaddr addr, uint32_t val)
-{
-    PCNetState *d = opaque;
-#ifdef PCNET_DEBUG_IO
-    printf("pcnet_mmio_writeb addr=0x" TARGET_FMT_plx" val=0x%02x\n", addr,
-           val);
-#endif
-    if (!(addr & 0x10))
-        pcnet_aprom_writeb(d, addr & 0x0f, val);
-}
-
-static uint32_t pcnet_mmio_readb(void *opaque, hwaddr addr)
-{
-    PCNetState *d = opaque;
-    uint32_t val = -1;
-    if (!(addr & 0x10))
-        val = pcnet_aprom_readb(d, addr & 0x0f);
-#ifdef PCNET_DEBUG_IO
-    printf("pcnet_mmio_readb addr=0x" TARGET_FMT_plx " val=0x%02x\n", addr,
-           val & 0xff);
-#endif
-    return val;
-}
-
-static void pcnet_mmio_writew(void *opaque, hwaddr addr, uint32_t val)
-{
-    PCNetState *d = opaque;
-#ifdef PCNET_DEBUG_IO
-    printf("pcnet_mmio_writew addr=0x" TARGET_FMT_plx " val=0x%04x\n", addr,
-           val);
-#endif
-    if (addr & 0x10)
-        pcnet_ioport_writew(d, addr & 0x0f, val);
-    else {
-        addr &= 0x0f;
-        pcnet_aprom_writeb(d, addr, val & 0xff);
-        pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8);
-    }
-}
-
-static uint32_t pcnet_mmio_readw(void *opaque, hwaddr addr)
-{
-    PCNetState *d = opaque;
-    uint32_t val = -1;
-    if (addr & 0x10)
-        val = pcnet_ioport_readw(d, addr & 0x0f);
-    else {
-        addr &= 0x0f;
-        val = pcnet_aprom_readb(d, addr+1);
-        val <<= 8;
-        val |= pcnet_aprom_readb(d, addr);
-    }
-#ifdef PCNET_DEBUG_IO
-    printf("pcnet_mmio_readw addr=0x" TARGET_FMT_plx" val = 0x%04x\n", addr,
-           val & 0xffff);
-#endif
-    return val;
-}
-
-static void pcnet_mmio_writel(void *opaque, hwaddr addr, uint32_t val)
-{
-    PCNetState *d = opaque;
-#ifdef PCNET_DEBUG_IO
-    printf("pcnet_mmio_writel addr=0x" TARGET_FMT_plx" val=0x%08x\n", addr,
-           val);
-#endif
-    if (addr & 0x10)
-        pcnet_ioport_writel(d, addr & 0x0f, val);
-    else {
-        addr &= 0x0f;
-        pcnet_aprom_writeb(d, addr, val & 0xff);
-        pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8);
-        pcnet_aprom_writeb(d, addr+2, (val & 0xff0000) >> 16);
-        pcnet_aprom_writeb(d, addr+3, (val & 0xff000000) >> 24);
-    }
-}
-
-static uint32_t pcnet_mmio_readl(void *opaque, hwaddr addr)
-{
-    PCNetState *d = opaque;
-    uint32_t val;
-    if (addr & 0x10)
-        val = pcnet_ioport_readl(d, addr & 0x0f);
-    else {
-        addr &= 0x0f;
-        val = pcnet_aprom_readb(d, addr+3);
-        val <<= 8;
-        val |= pcnet_aprom_readb(d, addr+2);
-        val <<= 8;
-        val |= pcnet_aprom_readb(d, addr+1);
-        val <<= 8;
-        val |= pcnet_aprom_readb(d, addr);
-    }
-#ifdef PCNET_DEBUG_IO
-    printf("pcnet_mmio_readl addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr,
-           val);
-#endif
-    return val;
-}
-
-static const VMStateDescription vmstate_pci_pcnet = {
-    .name = "pcnet",
-    .version_id = 3,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .fields      = (VMStateField []) {
-        VMSTATE_PCI_DEVICE(pci_dev, PCIPCNetState),
-        VMSTATE_STRUCT(state, PCIPCNetState, 0, vmstate_pcnet, PCNetState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-/* PCI interface */
-
-static const MemoryRegionOps pcnet_mmio_ops = {
-    .old_mmio = {
-        .read = { pcnet_mmio_readb, pcnet_mmio_readw, pcnet_mmio_readl },
-        .write = { pcnet_mmio_writeb, pcnet_mmio_writew, pcnet_mmio_writel },
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void pci_physical_memory_write(void *dma_opaque, hwaddr addr,
-                                      uint8_t *buf, int len, int do_bswap)
-{
-    pci_dma_write(dma_opaque, addr, buf, len);
-}
-
-static void pci_physical_memory_read(void *dma_opaque, hwaddr addr,
-                                     uint8_t *buf, int len, int do_bswap)
-{
-    pci_dma_read(dma_opaque, addr, buf, len);
-}
-
-static void pci_pcnet_cleanup(NetClientState *nc)
-{
-    PCNetState *d = qemu_get_nic_opaque(nc);
-
-    pcnet_common_cleanup(d);
-}
-
-static void pci_pcnet_uninit(PCIDevice *dev)
-{
-    PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, dev);
-
-    memory_region_destroy(&d->state.mmio);
-    memory_region_destroy(&d->io_bar);
-    qemu_del_timer(d->state.poll_timer);
-    qemu_free_timer(d->state.poll_timer);
-    qemu_del_nic(d->state.nic);
-}
-
-static NetClientInfo net_pci_pcnet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
-    .size = sizeof(NICState),
-    .can_receive = pcnet_can_receive,
-    .receive = pcnet_receive,
-    .link_status_changed = pcnet_set_link_status,
-    .cleanup = pci_pcnet_cleanup,
-};
-
-static int pci_pcnet_init(PCIDevice *pci_dev)
-{
-    PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, pci_dev);
-    PCNetState *s = &d->state;
-    uint8_t *pci_conf;
-
-#if 0
-    printf("sizeof(RMD)=%d, sizeof(TMD)=%d\n",
-        sizeof(struct pcnet_RMD), sizeof(struct pcnet_TMD));
-#endif
-
-    pci_conf = pci_dev->config;
-
-    pci_set_word(pci_conf + PCI_STATUS,
-                 PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM);
-
-    pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, 0x0);
-    pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, 0x0);
-
-    pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin A */
-    pci_conf[PCI_MIN_GNT] = 0x06;
-    pci_conf[PCI_MAX_LAT] = 0xff;
-
-    /* Handler for memory-mapped I/O */
-    memory_region_init_io(&d->state.mmio, &pcnet_mmio_ops, s, "pcnet-mmio",
-                          PCNET_PNPMMIO_SIZE);
-
-    memory_region_init_io(&d->io_bar, &pcnet_io_ops, s, "pcnet-io",
-                          PCNET_IOPORT_SIZE);
-    pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->io_bar);
-
-    pci_register_bar(pci_dev, 1, 0, &s->mmio);
-
-    s->irq = pci_dev->irq[0];
-    s->phys_mem_read = pci_physical_memory_read;
-    s->phys_mem_write = pci_physical_memory_write;
-    s->dma_opaque = pci_dev;
-
-    return pcnet_common_init(&pci_dev->qdev, s, &net_pci_pcnet_info);
-}
-
-static void pci_reset(DeviceState *dev)
-{
-    PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev.qdev, dev);
-
-    pcnet_h_reset(&d->state);
-}
-
-static Property pcnet_properties[] = {
-    DEFINE_NIC_PROPERTIES(PCIPCNetState, state.conf),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void pcnet_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = pci_pcnet_init;
-    k->exit = pci_pcnet_uninit;
-    k->romfile = "efi-pcnet.rom",
-    k->vendor_id = PCI_VENDOR_ID_AMD;
-    k->device_id = PCI_DEVICE_ID_AMD_LANCE;
-    k->revision = 0x10;
-    k->class_id = PCI_CLASS_NETWORK_ETHERNET;
-    dc->reset = pci_reset;
-    dc->vmsd = &vmstate_pci_pcnet;
-    dc->props = pcnet_properties;
-}
-
-static const TypeInfo pcnet_info = {
-    .name          = "pcnet",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIPCNetState),
-    .class_init    = pcnet_class_init,
-};
-
-static void pci_pcnet_register_types(void)
-{
-    type_register_static(&pcnet_info);
-}
-
-type_init(pci_pcnet_register_types)
diff --git a/hw/pcnet.c b/hw/pcnet.c
deleted file mode 100644 (file)
index b0b462b..0000000
+++ /dev/null
@@ -1,1768 +0,0 @@
-/*
- * QEMU AMD PC-Net II (Am79C970A) emulation
- *
- * Copyright (c) 2004 Antony T Curtis
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-/* This software was written to be compatible with the specification:
- * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet
- * AMD Publication# 19436  Rev:E  Amendment/0  Issue Date: June 2000
- */
-
-/*
- * On Sparc32, this is the Lance (Am7990) part of chip STP2000 (Master I/O), also
- * produced as NCR89C100. See
- * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
- * and
- * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt
- */
-
-#include "hw/qdev.h"
-#include "net/net.h"
-#include "qemu/timer.h"
-#include "qemu/sockets.h"
-#include "sysemu/sysemu.h"
-
-#include "hw/pcnet.h"
-
-//#define PCNET_DEBUG
-//#define PCNET_DEBUG_IO
-//#define PCNET_DEBUG_BCR
-//#define PCNET_DEBUG_CSR
-//#define PCNET_DEBUG_RMD
-//#define PCNET_DEBUG_TMD
-//#define PCNET_DEBUG_MATCH
-
-
-struct qemu_ether_header {
-    uint8_t ether_dhost[6];
-    uint8_t ether_shost[6];
-    uint16_t ether_type;
-};
-
-#define CSR_INIT(S)      !!(((S)->csr[0])&0x0001)
-#define CSR_STRT(S)      !!(((S)->csr[0])&0x0002)
-#define CSR_STOP(S)      !!(((S)->csr[0])&0x0004)
-#define CSR_TDMD(S)      !!(((S)->csr[0])&0x0008)
-#define CSR_TXON(S)      !!(((S)->csr[0])&0x0010)
-#define CSR_RXON(S)      !!(((S)->csr[0])&0x0020)
-#define CSR_INEA(S)      !!(((S)->csr[0])&0x0040)
-#define CSR_BSWP(S)      !!(((S)->csr[3])&0x0004)
-#define CSR_LAPPEN(S)    !!(((S)->csr[3])&0x0020)
-#define CSR_DXSUFLO(S)   !!(((S)->csr[3])&0x0040)
-#define CSR_ASTRP_RCV(S) !!(((S)->csr[4])&0x0800)
-#define CSR_DPOLL(S)     !!(((S)->csr[4])&0x1000)
-#define CSR_SPND(S)      !!(((S)->csr[5])&0x0001)
-#define CSR_LTINTEN(S)   !!(((S)->csr[5])&0x4000)
-#define CSR_TOKINTD(S)   !!(((S)->csr[5])&0x8000)
-#define CSR_DRX(S)       !!(((S)->csr[15])&0x0001)
-#define CSR_DTX(S)       !!(((S)->csr[15])&0x0002)
-#define CSR_LOOP(S)      !!(((S)->csr[15])&0x0004)
-#define CSR_DXMTFCS(S)   !!(((S)->csr[15])&0x0008)
-#define CSR_INTL(S)      !!(((S)->csr[15])&0x0040)
-#define CSR_DRCVPA(S)    !!(((S)->csr[15])&0x2000)
-#define CSR_DRCVBC(S)    !!(((S)->csr[15])&0x4000)
-#define CSR_PROM(S)      !!(((S)->csr[15])&0x8000)
-
-#define CSR_CRBC(S)      ((S)->csr[40])
-#define CSR_CRST(S)      ((S)->csr[41])
-#define CSR_CXBC(S)      ((S)->csr[42])
-#define CSR_CXST(S)      ((S)->csr[43])
-#define CSR_NRBC(S)      ((S)->csr[44])
-#define CSR_NRST(S)      ((S)->csr[45])
-#define CSR_POLL(S)      ((S)->csr[46])
-#define CSR_PINT(S)      ((S)->csr[47])
-#define CSR_RCVRC(S)     ((S)->csr[72])
-#define CSR_XMTRC(S)     ((S)->csr[74])
-#define CSR_RCVRL(S)     ((S)->csr[76])
-#define CSR_XMTRL(S)     ((S)->csr[78])
-#define CSR_MISSC(S)     ((S)->csr[112])
-
-#define CSR_IADR(S)      ((S)->csr[ 1] | ((uint32_t)(S)->csr[ 2] << 16))
-#define CSR_CRBA(S)      ((S)->csr[18] | ((uint32_t)(S)->csr[19] << 16))
-#define CSR_CXBA(S)      ((S)->csr[20] | ((uint32_t)(S)->csr[21] << 16))
-#define CSR_NRBA(S)      ((S)->csr[22] | ((uint32_t)(S)->csr[23] << 16))
-#define CSR_BADR(S)      ((S)->csr[24] | ((uint32_t)(S)->csr[25] << 16))
-#define CSR_NRDA(S)      ((S)->csr[26] | ((uint32_t)(S)->csr[27] << 16))
-#define CSR_CRDA(S)      ((S)->csr[28] | ((uint32_t)(S)->csr[29] << 16))
-#define CSR_BADX(S)      ((S)->csr[30] | ((uint32_t)(S)->csr[31] << 16))
-#define CSR_NXDA(S)      ((S)->csr[32] | ((uint32_t)(S)->csr[33] << 16))
-#define CSR_CXDA(S)      ((S)->csr[34] | ((uint32_t)(S)->csr[35] << 16))
-#define CSR_NNRD(S)      ((S)->csr[36] | ((uint32_t)(S)->csr[37] << 16))
-#define CSR_NNXD(S)      ((S)->csr[38] | ((uint32_t)(S)->csr[39] << 16))
-#define CSR_PXDA(S)      ((S)->csr[60] | ((uint32_t)(S)->csr[61] << 16))
-#define CSR_NXBA(S)      ((S)->csr[64] | ((uint32_t)(S)->csr[65] << 16))
-
-#define PHYSADDR(S,A) \
-  (BCR_SSIZE32(S) ? (A) : (A) | ((0xff00 & (uint32_t)(S)->csr[2])<<16))
-
-struct pcnet_initblk16 {
-    uint16_t mode;
-    uint16_t padr[3];
-    uint16_t ladrf[4];
-    uint32_t rdra;
-    uint32_t tdra;
-};
-
-struct pcnet_initblk32 {
-    uint16_t mode;
-    uint8_t rlen;
-    uint8_t tlen;
-    uint16_t padr[3];
-    uint16_t _res;
-    uint16_t ladrf[4];
-    uint32_t rdra;
-    uint32_t tdra;
-};
-
-struct pcnet_TMD {
-    uint32_t tbadr;
-    int16_t length;
-    int16_t status;
-    uint32_t misc;
-    uint32_t res;
-};
-
-#define TMDL_BCNT_MASK  0x0fff
-#define TMDL_BCNT_SH    0
-#define TMDL_ONES_MASK  0xf000
-#define TMDL_ONES_SH    12
-
-#define TMDS_BPE_MASK   0x0080
-#define TMDS_BPE_SH     7
-#define TMDS_ENP_MASK   0x0100
-#define TMDS_ENP_SH     8
-#define TMDS_STP_MASK   0x0200
-#define TMDS_STP_SH     9
-#define TMDS_DEF_MASK   0x0400
-#define TMDS_DEF_SH     10
-#define TMDS_ONE_MASK   0x0800
-#define TMDS_ONE_SH     11
-#define TMDS_LTINT_MASK 0x1000
-#define TMDS_LTINT_SH   12
-#define TMDS_NOFCS_MASK 0x2000
-#define TMDS_NOFCS_SH   13
-#define TMDS_ADDFCS_MASK TMDS_NOFCS_MASK
-#define TMDS_ADDFCS_SH  TMDS_NOFCS_SH
-#define TMDS_ERR_MASK   0x4000
-#define TMDS_ERR_SH     14
-#define TMDS_OWN_MASK   0x8000
-#define TMDS_OWN_SH     15
-
-#define TMDM_TRC_MASK   0x0000000f
-#define TMDM_TRC_SH     0
-#define TMDM_TDR_MASK   0x03ff0000
-#define TMDM_TDR_SH     16
-#define TMDM_RTRY_MASK  0x04000000
-#define TMDM_RTRY_SH    26
-#define TMDM_LCAR_MASK  0x08000000
-#define TMDM_LCAR_SH    27
-#define TMDM_LCOL_MASK  0x10000000
-#define TMDM_LCOL_SH    28
-#define TMDM_EXDEF_MASK 0x20000000
-#define TMDM_EXDEF_SH   29
-#define TMDM_UFLO_MASK  0x40000000
-#define TMDM_UFLO_SH    30
-#define TMDM_BUFF_MASK  0x80000000
-#define TMDM_BUFF_SH    31
-
-struct pcnet_RMD {
-    uint32_t rbadr;
-    int16_t buf_length;
-    int16_t status;
-    uint32_t msg_length;
-    uint32_t res;
-};
-
-#define RMDL_BCNT_MASK  0x0fff
-#define RMDL_BCNT_SH    0
-#define RMDL_ONES_MASK  0xf000
-#define RMDL_ONES_SH    12
-
-#define RMDS_BAM_MASK   0x0010
-#define RMDS_BAM_SH     4
-#define RMDS_LFAM_MASK  0x0020
-#define RMDS_LFAM_SH    5
-#define RMDS_PAM_MASK   0x0040
-#define RMDS_PAM_SH     6
-#define RMDS_BPE_MASK   0x0080
-#define RMDS_BPE_SH     7
-#define RMDS_ENP_MASK   0x0100
-#define RMDS_ENP_SH     8
-#define RMDS_STP_MASK   0x0200
-#define RMDS_STP_SH     9
-#define RMDS_BUFF_MASK  0x0400
-#define RMDS_BUFF_SH    10
-#define RMDS_CRC_MASK   0x0800
-#define RMDS_CRC_SH     11
-#define RMDS_OFLO_MASK  0x1000
-#define RMDS_OFLO_SH    12
-#define RMDS_FRAM_MASK  0x2000
-#define RMDS_FRAM_SH    13
-#define RMDS_ERR_MASK   0x4000
-#define RMDS_ERR_SH     14
-#define RMDS_OWN_MASK   0x8000
-#define RMDS_OWN_SH     15
-
-#define RMDM_MCNT_MASK  0x00000fff
-#define RMDM_MCNT_SH    0
-#define RMDM_ZEROS_MASK 0x0000f000
-#define RMDM_ZEROS_SH   12
-#define RMDM_RPC_MASK   0x00ff0000
-#define RMDM_RPC_SH     16
-#define RMDM_RCC_MASK   0xff000000
-#define RMDM_RCC_SH     24
-
-#define SET_FIELD(regp, name, field, value)             \
-  (*(regp) = (*(regp) & ~(name ## _ ## field ## _MASK)) \
-             | ((value) << name ## _ ## field ## _SH))
-
-#define GET_FIELD(reg, name, field)                     \
-  (((reg) & name ## _ ## field ## _MASK) >> name ## _ ## field ## _SH)
-
-#define PRINT_TMD(T) printf(                            \
-        "TMD0 : TBADR=0x%08x\n"                         \
-        "TMD1 : OWN=%d, ERR=%d, FCS=%d, LTI=%d, "       \
-        "ONE=%d, DEF=%d, STP=%d, ENP=%d,\n"             \
-        "       BPE=%d, BCNT=%d\n"                      \
-        "TMD2 : BUF=%d, UFL=%d, EXD=%d, LCO=%d, "       \
-        "LCA=%d, RTR=%d,\n"                             \
-        "       TDR=%d, TRC=%d\n",                      \
-        (T)->tbadr,                                     \
-        GET_FIELD((T)->status, TMDS, OWN),              \
-        GET_FIELD((T)->status, TMDS, ERR),              \
-        GET_FIELD((T)->status, TMDS, NOFCS),            \
-        GET_FIELD((T)->status, TMDS, LTINT),            \
-        GET_FIELD((T)->status, TMDS, ONE),              \
-        GET_FIELD((T)->status, TMDS, DEF),              \
-        GET_FIELD((T)->status, TMDS, STP),              \
-        GET_FIELD((T)->status, TMDS, ENP),              \
-        GET_FIELD((T)->status, TMDS, BPE),              \
-        4096-GET_FIELD((T)->length, TMDL, BCNT),        \
-        GET_FIELD((T)->misc, TMDM, BUFF),               \
-        GET_FIELD((T)->misc, TMDM, UFLO),               \
-        GET_FIELD((T)->misc, TMDM, EXDEF),              \
-        GET_FIELD((T)->misc, TMDM, LCOL),               \
-        GET_FIELD((T)->misc, TMDM, LCAR),               \
-        GET_FIELD((T)->misc, TMDM, RTRY),               \
-        GET_FIELD((T)->misc, TMDM, TDR),                \
-        GET_FIELD((T)->misc, TMDM, TRC))
-
-#define PRINT_RMD(R) printf(                            \
-        "RMD0 : RBADR=0x%08x\n"                         \
-        "RMD1 : OWN=%d, ERR=%d, FRAM=%d, OFLO=%d, "     \
-        "CRC=%d, BUFF=%d, STP=%d, ENP=%d,\n       "     \
-        "BPE=%d, PAM=%d, LAFM=%d, BAM=%d, ONES=%d, BCNT=%d\n" \
-        "RMD2 : RCC=%d, RPC=%d, MCNT=%d, ZEROS=%d\n",   \
-        (R)->rbadr,                                     \
-        GET_FIELD((R)->status, RMDS, OWN),              \
-        GET_FIELD((R)->status, RMDS, ERR),              \
-        GET_FIELD((R)->status, RMDS, FRAM),             \
-        GET_FIELD((R)->status, RMDS, OFLO),             \
-        GET_FIELD((R)->status, RMDS, CRC),              \
-        GET_FIELD((R)->status, RMDS, BUFF),             \
-        GET_FIELD((R)->status, RMDS, STP),              \
-        GET_FIELD((R)->status, RMDS, ENP),              \
-        GET_FIELD((R)->status, RMDS, BPE),              \
-        GET_FIELD((R)->status, RMDS, PAM),              \
-        GET_FIELD((R)->status, RMDS, LFAM),             \
-        GET_FIELD((R)->status, RMDS, BAM),              \
-        GET_FIELD((R)->buf_length, RMDL, ONES),         \
-        4096-GET_FIELD((R)->buf_length, RMDL, BCNT),    \
-        GET_FIELD((R)->msg_length, RMDM, RCC),          \
-        GET_FIELD((R)->msg_length, RMDM, RPC),          \
-        GET_FIELD((R)->msg_length, RMDM, MCNT),         \
-        GET_FIELD((R)->msg_length, RMDM, ZEROS))
-
-static inline void pcnet_tmd_load(PCNetState *s, struct pcnet_TMD *tmd,
-                                  hwaddr addr)
-{
-    if (!BCR_SSIZE32(s)) {
-        struct {
-            uint32_t tbadr;
-            int16_t length;
-            int16_t status;
-       } xda;
-        s->phys_mem_read(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0);
-        tmd->tbadr = le32_to_cpu(xda.tbadr) & 0xffffff;
-        tmd->length = le16_to_cpu(xda.length);
-        tmd->status = (le32_to_cpu(xda.tbadr) >> 16) & 0xff00;
-        tmd->misc = le16_to_cpu(xda.status) << 16;
-        tmd->res = 0;
-    } else {
-        s->phys_mem_read(s->dma_opaque, addr, (void *)tmd, sizeof(*tmd), 0);
-        le32_to_cpus(&tmd->tbadr);
-        le16_to_cpus((uint16_t *)&tmd->length);
-        le16_to_cpus((uint16_t *)&tmd->status);
-        le32_to_cpus(&tmd->misc);
-        le32_to_cpus(&tmd->res);
-        if (BCR_SWSTYLE(s) == 3) {
-            uint32_t tmp = tmd->tbadr;
-            tmd->tbadr = tmd->misc;
-            tmd->misc = tmp;
-        }
-    }
-}
-
-static inline void pcnet_tmd_store(PCNetState *s, const struct pcnet_TMD *tmd,
-                                   hwaddr addr)
-{
-    if (!BCR_SSIZE32(s)) {
-        struct {
-            uint32_t tbadr;
-            int16_t length;
-            int16_t status;
-        } xda;
-        xda.tbadr = cpu_to_le32((tmd->tbadr & 0xffffff) |
-                                ((tmd->status & 0xff00) << 16));
-        xda.length = cpu_to_le16(tmd->length);
-        xda.status = cpu_to_le16(tmd->misc >> 16);
-        s->phys_mem_write(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0);
-    } else {
-        struct {
-            uint32_t tbadr;
-            int16_t length;
-            int16_t status;
-            uint32_t misc;
-            uint32_t res;
-        } xda;
-        xda.tbadr = cpu_to_le32(tmd->tbadr);
-        xda.length = cpu_to_le16(tmd->length);
-        xda.status = cpu_to_le16(tmd->status);
-        xda.misc = cpu_to_le32(tmd->misc);
-        xda.res = cpu_to_le32(tmd->res);
-        if (BCR_SWSTYLE(s) == 3) {
-            uint32_t tmp = xda.tbadr;
-            xda.tbadr = xda.misc;
-            xda.misc = tmp;
-        }
-        s->phys_mem_write(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0);
-    }
-}
-
-static inline void pcnet_rmd_load(PCNetState *s, struct pcnet_RMD *rmd,
-                                  hwaddr addr)
-{
-    if (!BCR_SSIZE32(s)) {
-        struct {
-            uint32_t rbadr;
-            int16_t buf_length;
-            int16_t msg_length;
-       } rda;
-        s->phys_mem_read(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0);
-        rmd->rbadr = le32_to_cpu(rda.rbadr) & 0xffffff;
-        rmd->buf_length = le16_to_cpu(rda.buf_length);
-        rmd->status = (le32_to_cpu(rda.rbadr) >> 16) & 0xff00;
-        rmd->msg_length = le16_to_cpu(rda.msg_length);
-        rmd->res = 0;
-    } else {
-        s->phys_mem_read(s->dma_opaque, addr, (void *)rmd, sizeof(*rmd), 0);
-        le32_to_cpus(&rmd->rbadr);
-        le16_to_cpus((uint16_t *)&rmd->buf_length);
-        le16_to_cpus((uint16_t *)&rmd->status);
-        le32_to_cpus(&rmd->msg_length);
-        le32_to_cpus(&rmd->res);
-        if (BCR_SWSTYLE(s) == 3) {
-            uint32_t tmp = rmd->rbadr;
-            rmd->rbadr = rmd->msg_length;
-            rmd->msg_length = tmp;
-        }
-    }
-}
-
-static inline void pcnet_rmd_store(PCNetState *s, struct pcnet_RMD *rmd,
-                                   hwaddr addr)
-{
-    if (!BCR_SSIZE32(s)) {
-        struct {
-            uint32_t rbadr;
-            int16_t buf_length;
-            int16_t msg_length;
-        } rda;
-        rda.rbadr = cpu_to_le32((rmd->rbadr & 0xffffff) |
-                                ((rmd->status & 0xff00) << 16));
-        rda.buf_length = cpu_to_le16(rmd->buf_length);
-        rda.msg_length = cpu_to_le16(rmd->msg_length);
-        s->phys_mem_write(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0);
-    } else {
-        struct {
-            uint32_t rbadr;
-            int16_t buf_length;
-            int16_t status;
-            uint32_t msg_length;
-            uint32_t res;
-        } rda;
-        rda.rbadr = cpu_to_le32(rmd->rbadr);
-        rda.buf_length = cpu_to_le16(rmd->buf_length);
-        rda.status = cpu_to_le16(rmd->status);
-        rda.msg_length = cpu_to_le32(rmd->msg_length);
-        rda.res = cpu_to_le32(rmd->res);
-        if (BCR_SWSTYLE(s) == 3) {
-            uint32_t tmp = rda.rbadr;
-            rda.rbadr = rda.msg_length;
-            rda.msg_length = tmp;
-        }
-        s->phys_mem_write(s->dma_opaque, addr, (void *)&rda, sizeof(rda), 0);
-    }
-}
-
-
-#define TMDLOAD(TMD,ADDR) pcnet_tmd_load(s,TMD,ADDR)
-
-#define TMDSTORE(TMD,ADDR) pcnet_tmd_store(s,TMD,ADDR)
-
-#define RMDLOAD(RMD,ADDR) pcnet_rmd_load(s,RMD,ADDR)
-
-#define RMDSTORE(RMD,ADDR) pcnet_rmd_store(s,RMD,ADDR)
-
-#if 1
-
-#define CHECK_RMD(ADDR,RES) do {                \
-    struct pcnet_RMD rmd;                       \
-    RMDLOAD(&rmd,(ADDR));                       \
-    (RES) |= (GET_FIELD(rmd.buf_length, RMDL, ONES) != 15) \
-          || (GET_FIELD(rmd.msg_length, RMDM, ZEROS) != 0); \
-} while (0)
-
-#define CHECK_TMD(ADDR,RES) do {                \
-    struct pcnet_TMD tmd;                       \
-    TMDLOAD(&tmd,(ADDR));                       \
-    (RES) |= (GET_FIELD(tmd.length, TMDL, ONES) != 15); \
-} while (0)
-
-#else
-
-#define CHECK_RMD(ADDR,RES) do {                \
-    switch (BCR_SWSTYLE(s)) {                   \
-    case 0x00:                                  \
-        do {                                    \
-            uint16_t rda[4];                    \
-            s->phys_mem_read(s->dma_opaque, (ADDR), \
-                (void *)&rda[0], sizeof(rda), 0); \
-            (RES) |= (rda[2] & 0xf000)!=0xf000; \
-            (RES) |= (rda[3] & 0xf000)!=0x0000; \
-        } while (0);                            \
-        break;                                  \
-    case 0x01:                                  \
-    case 0x02:                                  \
-        do {                                    \
-            uint32_t rda[4];                    \
-            s->phys_mem_read(s->dma_opaque, (ADDR), \
-                (void *)&rda[0], sizeof(rda), 0); \
-            (RES) |= (rda[1] & 0x0000f000L)!=0x0000f000L; \
-            (RES) |= (rda[2] & 0x0000f000L)!=0x00000000L; \
-        } while (0);                            \
-        break;                                  \
-    case 0x03:                                  \
-        do {                                    \
-            uint32_t rda[4];                    \
-            s->phys_mem_read(s->dma_opaque, (ADDR), \
-                (void *)&rda[0], sizeof(rda), 0); \
-            (RES) |= (rda[0] & 0x0000f000L)!=0x00000000L; \
-            (RES) |= (rda[1] & 0x0000f000L)!=0x0000f000L; \
-        } while (0);                            \
-        break;                                  \
-    }                                           \
-} while (0)
-
-#define CHECK_TMD(ADDR,RES) do {                \
-    switch (BCR_SWSTYLE(s)) {                   \
-    case 0x00:                                  \
-        do {                                    \
-            uint16_t xda[4];                    \
-            s->phys_mem_read(s->dma_opaque, (ADDR), \
-                (void *)&xda[0], sizeof(xda), 0); \
-            (RES) |= (xda[2] & 0xf000)!=0xf000; \
-        } while (0);                            \
-        break;                                  \
-    case 0x01:                                  \
-    case 0x02:                                  \
-    case 0x03:                                  \
-        do {                                    \
-            uint32_t xda[4];                    \
-            s->phys_mem_read(s->dma_opaque, (ADDR), \
-                (void *)&xda[0], sizeof(xda), 0); \
-            (RES) |= (xda[1] & 0x0000f000L)!=0x0000f000L; \
-        } while (0);                            \
-        break;                                  \
-    }                                           \
-} while (0)
-
-#endif
-
-#define PRINT_PKTHDR(BUF) do {                  \
-    struct qemu_ether_header *hdr = (void *)(BUF); \
-    printf("packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, " \
-           "shost=%02x:%02x:%02x:%02x:%02x:%02x, " \
-           "type=0x%04x\n",                     \
-           hdr->ether_dhost[0],hdr->ether_dhost[1],hdr->ether_dhost[2], \
-           hdr->ether_dhost[3],hdr->ether_dhost[4],hdr->ether_dhost[5], \
-           hdr->ether_shost[0],hdr->ether_shost[1],hdr->ether_shost[2], \
-           hdr->ether_shost[3],hdr->ether_shost[4],hdr->ether_shost[5], \
-           be16_to_cpu(hdr->ether_type));       \
-} while (0)
-
-#define MULTICAST_FILTER_LEN 8
-
-static inline uint32_t lnc_mchash(const uint8_t *ether_addr)
-{
-#define LNC_POLYNOMIAL          0xEDB88320UL
-    uint32_t crc = 0xFFFFFFFF;
-    int idx, bit;
-    uint8_t data;
-
-    for (idx = 0; idx < 6; idx++) {
-        for (data = *ether_addr++, bit = 0; bit < MULTICAST_FILTER_LEN; bit++) {
-            crc = (crc >> 1) ^ (((crc ^ data) & 1) ? LNC_POLYNOMIAL : 0);
-            data >>= 1;
-        }
-    }
-    return crc;
-#undef LNC_POLYNOMIAL
-}
-
-#define CRC(crc, ch)    (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff])
-
-/* generated using the AUTODIN II polynomial
- *     x^32 + x^26 + x^23 + x^22 + x^16 +
- *     x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
- */
-static const uint32_t crctab[256] = {
-       0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
-       0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
-       0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
-       0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
-       0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
-       0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
-       0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
-       0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
-       0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
-       0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
-       0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
-       0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
-       0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
-       0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
-       0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
-       0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
-       0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
-       0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
-       0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
-       0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
-       0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
-       0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
-       0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
-       0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
-       0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
-       0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
-       0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
-       0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
-       0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
-       0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
-       0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
-       0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
-       0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
-       0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
-       0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
-       0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
-       0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
-       0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
-       0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
-       0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
-       0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
-       0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
-       0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
-       0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
-       0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
-       0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
-       0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
-       0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
-       0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
-       0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
-       0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
-       0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
-       0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
-       0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
-       0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
-       0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
-       0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
-       0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
-       0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
-       0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
-       0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
-       0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
-       0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
-       0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
-};
-
-static inline int padr_match(PCNetState *s, const uint8_t *buf, int size)
-{
-    struct qemu_ether_header *hdr = (void *)buf;
-    uint8_t padr[6] = {
-        s->csr[12] & 0xff, s->csr[12] >> 8,
-        s->csr[13] & 0xff, s->csr[13] >> 8,
-        s->csr[14] & 0xff, s->csr[14] >> 8
-    };
-    int result = (!CSR_DRCVPA(s)) && !memcmp(hdr->ether_dhost, padr, 6);
-#ifdef PCNET_DEBUG_MATCH
-    printf("packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, "
-           "padr=%02x:%02x:%02x:%02x:%02x:%02x\n",
-           hdr->ether_dhost[0],hdr->ether_dhost[1],hdr->ether_dhost[2],
-           hdr->ether_dhost[3],hdr->ether_dhost[4],hdr->ether_dhost[5],
-           padr[0],padr[1],padr[2],padr[3],padr[4],padr[5]);
-    printf("padr_match result=%d\n", result);
-#endif
-    return result;
-}
-
-static inline int padr_bcast(PCNetState *s, const uint8_t *buf, int size)
-{
-    static const uint8_t BCAST[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-    struct qemu_ether_header *hdr = (void *)buf;
-    int result = !CSR_DRCVBC(s) && !memcmp(hdr->ether_dhost, BCAST, 6);
-#ifdef PCNET_DEBUG_MATCH
-    printf("padr_bcast result=%d\n", result);
-#endif
-    return result;
-}
-
-static inline int ladr_match(PCNetState *s, const uint8_t *buf, int size)
-{
-    struct qemu_ether_header *hdr = (void *)buf;
-    if ((*(hdr->ether_dhost)&0x01) &&
-        ((uint64_t *)&s->csr[8])[0] != 0LL) {
-        uint8_t ladr[8] = {
-            s->csr[8] & 0xff, s->csr[8] >> 8,
-            s->csr[9] & 0xff, s->csr[9] >> 8,
-            s->csr[10] & 0xff, s->csr[10] >> 8,
-            s->csr[11] & 0xff, s->csr[11] >> 8
-        };
-        int index = lnc_mchash(hdr->ether_dhost) >> 26;
-        return !!(ladr[index >> 3] & (1 << (index & 7)));
-    }
-    return 0;
-}
-
-static inline hwaddr pcnet_rdra_addr(PCNetState *s, int idx)
-{
-    while (idx < 1) idx += CSR_RCVRL(s);
-    return s->rdra + ((CSR_RCVRL(s) - idx) * (BCR_SWSTYLE(s) ? 16 : 8));
-}
-
-static inline int64_t pcnet_get_next_poll_time(PCNetState *s, int64_t current_time)
-{
-    int64_t next_time = current_time +
-        muldiv64(65536 - (CSR_SPND(s) ? 0 : CSR_POLL(s)),
-                 get_ticks_per_sec(), 33000000L);
-    if (next_time <= current_time)
-        next_time = current_time + 1;
-    return next_time;
-}
-
-static void pcnet_poll(PCNetState *s);
-static void pcnet_poll_timer(void *opaque);
-
-static uint32_t pcnet_csr_readw(PCNetState *s, uint32_t rap);
-static void pcnet_csr_writew(PCNetState *s, uint32_t rap, uint32_t new_value);
-static void pcnet_bcr_writew(PCNetState *s, uint32_t rap, uint32_t val);
-
-static void pcnet_s_reset(PCNetState *s)
-{
-#ifdef PCNET_DEBUG
-    printf("pcnet_s_reset\n");
-#endif
-
-    s->rdra = 0;
-    s->tdra = 0;
-    s->rap = 0;
-
-    s->bcr[BCR_BSBC] &= ~0x0080;
-
-    s->csr[0]   = 0x0004;
-    s->csr[3]   = 0x0000;
-    s->csr[4]   = 0x0115;
-    s->csr[5]   = 0x0000;
-    s->csr[6]   = 0x0000;
-    s->csr[8]   = 0;
-    s->csr[9]   = 0;
-    s->csr[10]  = 0;
-    s->csr[11]  = 0;
-    s->csr[12]  = le16_to_cpu(((uint16_t *)&s->prom[0])[0]);
-    s->csr[13]  = le16_to_cpu(((uint16_t *)&s->prom[0])[1]);
-    s->csr[14]  = le16_to_cpu(((uint16_t *)&s->prom[0])[2]);
-    s->csr[15] &= 0x21c4;
-    s->csr[72]  = 1;
-    s->csr[74]  = 1;
-    s->csr[76]  = 1;
-    s->csr[78]  = 1;
-    s->csr[80]  = 0x1410;
-    s->csr[88]  = 0x1003;
-    s->csr[89]  = 0x0262;
-    s->csr[94]  = 0x0000;
-    s->csr[100] = 0x0200;
-    s->csr[103] = 0x0105;
-    s->csr[103] = 0x0105;
-    s->csr[112] = 0x0000;
-    s->csr[114] = 0x0000;
-    s->csr[122] = 0x0000;
-    s->csr[124] = 0x0000;
-
-    s->tx_busy = 0;
-}
-
-static void pcnet_update_irq(PCNetState *s)
-{
-    int isr = 0;
-    s->csr[0] &= ~0x0080;
-
-#if 1
-    if (((s->csr[0] & ~s->csr[3]) & 0x5f00) ||
-        (((s->csr[4]>>1) & ~s->csr[4]) & 0x0115) ||
-        (((s->csr[5]>>1) & s->csr[5]) & 0x0048))
-#else
-    if ((!(s->csr[3] & 0x4000) && !!(s->csr[0] & 0x4000)) /* BABL */ ||
-        (!(s->csr[3] & 0x1000) && !!(s->csr[0] & 0x1000)) /* MISS */ ||
-        (!(s->csr[3] & 0x0100) && !!(s->csr[0] & 0x0100)) /* IDON */ ||
-        (!(s->csr[3] & 0x0200) && !!(s->csr[0] & 0x0200)) /* TINT */ ||
-        (!(s->csr[3] & 0x0400) && !!(s->csr[0] & 0x0400)) /* RINT */ ||
-        (!(s->csr[3] & 0x0800) && !!(s->csr[0] & 0x0800)) /* MERR */ ||
-        (!(s->csr[4] & 0x0001) && !!(s->csr[4] & 0x0002)) /* JAB */ ||
-        (!(s->csr[4] & 0x0004) && !!(s->csr[4] & 0x0008)) /* TXSTRT */ ||
-        (!(s->csr[4] & 0x0010) && !!(s->csr[4] & 0x0020)) /* RCVO */ ||
-        (!(s->csr[4] & 0x0100) && !!(s->csr[4] & 0x0200)) /* MFCO */ ||
-        (!!(s->csr[5] & 0x0040) && !!(s->csr[5] & 0x0080)) /* EXDINT */ ||
-        (!!(s->csr[5] & 0x0008) && !!(s->csr[5] & 0x0010)) /* MPINT */)
-#endif
-    {
-
-        isr = CSR_INEA(s);
-        s->csr[0] |= 0x0080;
-    }
-
-    if (!!(s->csr[4] & 0x0080) && CSR_INEA(s)) { /* UINT */
-        s->csr[4] &= ~0x0080;
-        s->csr[4] |= 0x0040;
-        s->csr[0] |= 0x0080;
-        isr = 1;
-#ifdef PCNET_DEBUG
-        printf("pcnet user int\n");
-#endif
-    }
-
-#if 1
-    if (((s->csr[5]>>1) & s->csr[5]) & 0x0500)
-#else
-    if ((!!(s->csr[5] & 0x0400) && !!(s->csr[5] & 0x0800)) /* SINT */ ||
-        (!!(s->csr[5] & 0x0100) && !!(s->csr[5] & 0x0200)) /* SLPINT */ )
-#endif
-    {
-        isr = 1;
-        s->csr[0] |= 0x0080;
-    }
-
-    if (isr != s->isr) {
-#ifdef PCNET_DEBUG
-        printf("pcnet: INTA=%d\n", isr);
-#endif
-    }
-    qemu_set_irq(s->irq, isr);
-    s->isr = isr;
-}
-
-static void pcnet_init(PCNetState *s)
-{
-    int rlen, tlen;
-    uint16_t padr[3], ladrf[4], mode;
-    uint32_t rdra, tdra;
-
-#ifdef PCNET_DEBUG
-    printf("pcnet_init init_addr=0x%08x\n", PHYSADDR(s,CSR_IADR(s)));
-#endif
-
-    if (BCR_SSIZE32(s)) {
-        struct pcnet_initblk32 initblk;
-        s->phys_mem_read(s->dma_opaque, PHYSADDR(s,CSR_IADR(s)),
-                (uint8_t *)&initblk, sizeof(initblk), 0);
-        mode = le16_to_cpu(initblk.mode);
-        rlen = initblk.rlen >> 4;
-        tlen = initblk.tlen >> 4;
-       ladrf[0] = le16_to_cpu(initblk.ladrf[0]);
-       ladrf[1] = le16_to_cpu(initblk.ladrf[1]);
-       ladrf[2] = le16_to_cpu(initblk.ladrf[2]);
-       ladrf[3] = le16_to_cpu(initblk.ladrf[3]);
-       padr[0] = le16_to_cpu(initblk.padr[0]);
-       padr[1] = le16_to_cpu(initblk.padr[1]);
-       padr[2] = le16_to_cpu(initblk.padr[2]);
-        rdra = le32_to_cpu(initblk.rdra);
-        tdra = le32_to_cpu(initblk.tdra);
-    } else {
-        struct pcnet_initblk16 initblk;
-        s->phys_mem_read(s->dma_opaque, PHYSADDR(s,CSR_IADR(s)),
-                (uint8_t *)&initblk, sizeof(initblk), 0);
-        mode = le16_to_cpu(initblk.mode);
-       ladrf[0] = le16_to_cpu(initblk.ladrf[0]);
-       ladrf[1] = le16_to_cpu(initblk.ladrf[1]);
-       ladrf[2] = le16_to_cpu(initblk.ladrf[2]);
-       ladrf[3] = le16_to_cpu(initblk.ladrf[3]);
-       padr[0] = le16_to_cpu(initblk.padr[0]);
-       padr[1] = le16_to_cpu(initblk.padr[1]);
-       padr[2] = le16_to_cpu(initblk.padr[2]);
-        rdra = le32_to_cpu(initblk.rdra);
-        tdra = le32_to_cpu(initblk.tdra);
-        rlen = rdra >> 29;
-        tlen = tdra >> 29;
-        rdra &= 0x00ffffff;
-        tdra &= 0x00ffffff;
-    }
-
-#if defined(PCNET_DEBUG)
-    printf("rlen=%d tlen=%d\n", rlen, tlen);
-#endif
-
-    CSR_RCVRL(s) = (rlen < 9) ? (1 << rlen) : 512;
-    CSR_XMTRL(s) = (tlen < 9) ? (1 << tlen) : 512;
-    s->csr[ 6] = (tlen << 12) | (rlen << 8);
-    s->csr[15] = mode;
-    s->csr[ 8] = ladrf[0];
-    s->csr[ 9] = ladrf[1];
-    s->csr[10] = ladrf[2];
-    s->csr[11] = ladrf[3];
-    s->csr[12] = padr[0];
-    s->csr[13] = padr[1];
-    s->csr[14] = padr[2];
-    s->rdra = PHYSADDR(s, rdra);
-    s->tdra = PHYSADDR(s, tdra);
-
-    CSR_RCVRC(s) = CSR_RCVRL(s);
-    CSR_XMTRC(s) = CSR_XMTRL(s);
-
-#ifdef PCNET_DEBUG
-    printf("pcnet ss32=%d rdra=0x%08x[%d] tdra=0x%08x[%d]\n",
-        BCR_SSIZE32(s),
-        s->rdra, CSR_RCVRL(s), s->tdra, CSR_XMTRL(s));
-#endif
-
-    s->csr[0] |= 0x0101;
-    s->csr[0] &= ~0x0004;       /* clear STOP bit */
-}
-
-static void pcnet_start(PCNetState *s)
-{
-#ifdef PCNET_DEBUG
-    printf("pcnet_start\n");
-#endif
-
-    if (!CSR_DTX(s))
-        s->csr[0] |= 0x0010;    /* set TXON */
-
-    if (!CSR_DRX(s))
-        s->csr[0] |= 0x0020;    /* set RXON */
-
-    s->csr[0] &= ~0x0004;       /* clear STOP bit */
-    s->csr[0] |= 0x0002;
-    pcnet_poll_timer(s);
-}
-
-static void pcnet_stop(PCNetState *s)
-{
-#ifdef PCNET_DEBUG
-    printf("pcnet_stop\n");
-#endif
-    s->csr[0] &= ~0xffeb;
-    s->csr[0] |= 0x0014;
-    s->csr[4] &= ~0x02c2;
-    s->csr[5] &= ~0x0011;
-    pcnet_poll_timer(s);
-}
-
-static void pcnet_rdte_poll(PCNetState *s)
-{
-    s->csr[28] = s->csr[29] = 0;
-    if (s->rdra) {
-        int bad = 0;
-#if 1
-        hwaddr crda = pcnet_rdra_addr(s, CSR_RCVRC(s));
-        hwaddr nrda = pcnet_rdra_addr(s, -1 + CSR_RCVRC(s));
-        hwaddr nnrd = pcnet_rdra_addr(s, -2 + CSR_RCVRC(s));
-#else
-        hwaddr crda = s->rdra +
-            (CSR_RCVRL(s) - CSR_RCVRC(s)) *
-            (BCR_SWSTYLE(s) ? 16 : 8 );
-        int nrdc = CSR_RCVRC(s)<=1 ? CSR_RCVRL(s) : CSR_RCVRC(s)-1;
-        hwaddr nrda = s->rdra +
-            (CSR_RCVRL(s) - nrdc) *
-            (BCR_SWSTYLE(s) ? 16 : 8 );
-        int nnrc = nrdc<=1 ? CSR_RCVRL(s) : nrdc-1;
-        hwaddr nnrd = s->rdra +
-            (CSR_RCVRL(s) - nnrc) *
-            (BCR_SWSTYLE(s) ? 16 : 8 );
-#endif
-
-        CHECK_RMD(crda, bad);
-        if (!bad) {
-            CHECK_RMD(nrda, bad);
-            if (bad || (nrda == crda)) nrda = 0;
-            CHECK_RMD(nnrd, bad);
-            if (bad || (nnrd == crda)) nnrd = 0;
-
-            s->csr[28] = crda & 0xffff;
-            s->csr[29] = crda >> 16;
-            s->csr[26] = nrda & 0xffff;
-            s->csr[27] = nrda >> 16;
-            s->csr[36] = nnrd & 0xffff;
-            s->csr[37] = nnrd >> 16;
-#ifdef PCNET_DEBUG
-            if (bad) {
-                printf("pcnet: BAD RMD RECORDS AFTER 0x" TARGET_FMT_plx "\n",
-                       crda);
-            }
-        } else {
-            printf("pcnet: BAD RMD RDA=0x" TARGET_FMT_plx "\n",
-                   crda);
-#endif
-        }
-    }
-
-    if (CSR_CRDA(s)) {
-        struct pcnet_RMD rmd;
-        RMDLOAD(&rmd, PHYSADDR(s,CSR_CRDA(s)));
-        CSR_CRBC(s) = GET_FIELD(rmd.buf_length, RMDL, BCNT);
-        CSR_CRST(s) = rmd.status;
-#ifdef PCNET_DEBUG_RMD_X
-        printf("CRDA=0x%08x CRST=0x%04x RCVRC=%d RMDL=0x%04x RMDS=0x%04x RMDM=0x%08x\n",
-                PHYSADDR(s,CSR_CRDA(s)), CSR_CRST(s), CSR_RCVRC(s),
-                rmd.buf_length, rmd.status, rmd.msg_length);
-        PRINT_RMD(&rmd);
-#endif
-    } else {
-        CSR_CRBC(s) = CSR_CRST(s) = 0;
-    }
-
-    if (CSR_NRDA(s)) {
-        struct pcnet_RMD rmd;
-        RMDLOAD(&rmd, PHYSADDR(s,CSR_NRDA(s)));
-        CSR_NRBC(s) = GET_FIELD(rmd.buf_length, RMDL, BCNT);
-        CSR_NRST(s) = rmd.status;
-    } else {
-        CSR_NRBC(s) = CSR_NRST(s) = 0;
-    }
-
-}
-
-static int pcnet_tdte_poll(PCNetState *s)
-{
-    s->csr[34] = s->csr[35] = 0;
-    if (s->tdra) {
-        hwaddr cxda = s->tdra +
-            (CSR_XMTRL(s) - CSR_XMTRC(s)) *
-            (BCR_SWSTYLE(s) ? 16 : 8);
-        int bad = 0;
-        CHECK_TMD(cxda, bad);
-        if (!bad) {
-            if (CSR_CXDA(s) != cxda) {
-                s->csr[60] = s->csr[34];
-                s->csr[61] = s->csr[35];
-                s->csr[62] = CSR_CXBC(s);
-                s->csr[63] = CSR_CXST(s);
-            }
-            s->csr[34] = cxda & 0xffff;
-            s->csr[35] = cxda >> 16;
-#ifdef PCNET_DEBUG_X
-            printf("pcnet: BAD TMD XDA=0x%08x\n", cxda);
-#endif
-        }
-    }
-
-    if (CSR_CXDA(s)) {
-        struct pcnet_TMD tmd;
-
-        TMDLOAD(&tmd, PHYSADDR(s,CSR_CXDA(s)));
-
-        CSR_CXBC(s) = GET_FIELD(tmd.length, TMDL, BCNT);
-        CSR_CXST(s) = tmd.status;
-    } else {
-        CSR_CXBC(s) = CSR_CXST(s) = 0;
-    }
-
-    return !!(CSR_CXST(s) & 0x8000);
-}
-
-int pcnet_can_receive(NetClientState *nc)
-{
-    PCNetState *s = qemu_get_nic_opaque(nc);
-    if (CSR_STOP(s) || CSR_SPND(s))
-        return 0;
-
-    return sizeof(s->buffer)-16;
-}
-
-#define MIN_BUF_SIZE 60
-
-ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
-{
-    PCNetState *s = qemu_get_nic_opaque(nc);
-    int is_padr = 0, is_bcast = 0, is_ladr = 0;
-    uint8_t buf1[60];
-    int remaining;
-    int crc_err = 0;
-    int size = size_;
-
-    if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size ||
-        (CSR_LOOP(s) && !s->looptest)) {
-        return -1;
-    }
-#ifdef PCNET_DEBUG
-    printf("pcnet_receive size=%d\n", size);
-#endif
-
-    /* if too small buffer, then expand it */
-    if (size < MIN_BUF_SIZE) {
-        memcpy(buf1, buf, size);
-        memset(buf1 + size, 0, MIN_BUF_SIZE - size);
-        buf = buf1;
-        size = MIN_BUF_SIZE;
-    }
-
-    if (CSR_PROM(s)
-        || (is_padr=padr_match(s, buf, size))
-        || (is_bcast=padr_bcast(s, buf, size))
-        || (is_ladr=ladr_match(s, buf, size))) {
-
-        pcnet_rdte_poll(s);
-
-        if (!(CSR_CRST(s) & 0x8000) && s->rdra) {
-            struct pcnet_RMD rmd;
-            int rcvrc = CSR_RCVRC(s)-1,i;
-            hwaddr nrda;
-            for (i = CSR_RCVRL(s)-1; i > 0; i--, rcvrc--) {
-                if (rcvrc <= 1)
-                    rcvrc = CSR_RCVRL(s);
-                nrda = s->rdra +
-                    (CSR_RCVRL(s) - rcvrc) *
-                    (BCR_SWSTYLE(s) ? 16 : 8 );
-                RMDLOAD(&rmd, nrda);
-                if (GET_FIELD(rmd.status, RMDS, OWN)) {
-#ifdef PCNET_DEBUG_RMD
-                    printf("pcnet - scan buffer: RCVRC=%d PREV_RCVRC=%d\n",
-                                rcvrc, CSR_RCVRC(s));
-#endif
-                    CSR_RCVRC(s) = rcvrc;
-                    pcnet_rdte_poll(s);
-                    break;
-                }
-            }
-        }
-
-        if (!(CSR_CRST(s) & 0x8000)) {
-#ifdef PCNET_DEBUG_RMD
-            printf("pcnet - no buffer: RCVRC=%d\n", CSR_RCVRC(s));
-#endif
-            s->csr[0] |= 0x1000; /* Set MISS flag */
-            CSR_MISSC(s)++;
-        } else {
-            uint8_t *src = s->buffer;
-            hwaddr crda = CSR_CRDA(s);
-            struct pcnet_RMD rmd;
-            int pktcount = 0;
-
-            if (!s->looptest) {
-                memcpy(src, buf, size);
-                /* no need to compute the CRC */
-                src[size] = 0;
-                src[size + 1] = 0;
-                src[size + 2] = 0;
-                src[size + 3] = 0;
-                size += 4;
-            } else if (s->looptest == PCNET_LOOPTEST_CRC ||
-                       !CSR_DXMTFCS(s) || size < MIN_BUF_SIZE+4) {
-                uint32_t fcs = ~0;
-                uint8_t *p = src;
-
-                while (p != &src[size])
-                    CRC(fcs, *p++);
-                *(uint32_t *)p = htonl(fcs);
-                size += 4;
-            } else {
-                uint32_t fcs = ~0;
-                uint8_t *p = src;
-
-                while (p != &src[size-4])
-                    CRC(fcs, *p++);
-                crc_err = (*(uint32_t *)p != htonl(fcs));
-            }
-
-#ifdef PCNET_DEBUG_MATCH
-            PRINT_PKTHDR(buf);
-#endif
-
-            RMDLOAD(&rmd, PHYSADDR(s,crda));
-            /*if (!CSR_LAPPEN(s))*/
-                SET_FIELD(&rmd.status, RMDS, STP, 1);
-
-#define PCNET_RECV_STORE() do {                                 \
-    int count = MIN(4096 - GET_FIELD(rmd.buf_length, RMDL, BCNT),remaining); \
-    hwaddr rbadr = PHYSADDR(s, rmd.rbadr);          \
-    s->phys_mem_write(s->dma_opaque, rbadr, src, count, CSR_BSWP(s)); \
-    src += count; remaining -= count;                           \
-    SET_FIELD(&rmd.status, RMDS, OWN, 0);                       \
-    RMDSTORE(&rmd, PHYSADDR(s,crda));                           \
-    pktcount++;                                                 \
-} while (0)
-
-            remaining = size;
-            PCNET_RECV_STORE();
-            if ((remaining > 0) && CSR_NRDA(s)) {
-                hwaddr nrda = CSR_NRDA(s);
-#ifdef PCNET_DEBUG_RMD
-                PRINT_RMD(&rmd);
-#endif
-                RMDLOAD(&rmd, PHYSADDR(s,nrda));
-                if (GET_FIELD(rmd.status, RMDS, OWN)) {
-                    crda = nrda;
-                    PCNET_RECV_STORE();
-#ifdef PCNET_DEBUG_RMD
-                    PRINT_RMD(&rmd);
-#endif
-                    if ((remaining > 0) && (nrda=CSR_NNRD(s))) {
-                        RMDLOAD(&rmd, PHYSADDR(s,nrda));
-                        if (GET_FIELD(rmd.status, RMDS, OWN)) {
-                            crda = nrda;
-                            PCNET_RECV_STORE();
-                        }
-                    }
-                }
-            }
-
-#undef PCNET_RECV_STORE
-
-            RMDLOAD(&rmd, PHYSADDR(s,crda));
-            if (remaining == 0) {
-                SET_FIELD(&rmd.msg_length, RMDM, MCNT, size);
-                SET_FIELD(&rmd.status, RMDS, ENP, 1);
-                SET_FIELD(&rmd.status, RMDS, PAM, !CSR_PROM(s) && is_padr);
-                SET_FIELD(&rmd.status, RMDS, LFAM, !CSR_PROM(s) && is_ladr);
-                SET_FIELD(&rmd.status, RMDS, BAM, !CSR_PROM(s) && is_bcast);
-                if (crc_err) {
-                    SET_FIELD(&rmd.status, RMDS, CRC, 1);
-                    SET_FIELD(&rmd.status, RMDS, ERR, 1);
-                }
-            } else {
-                SET_FIELD(&rmd.status, RMDS, OFLO, 1);
-                SET_FIELD(&rmd.status, RMDS, BUFF, 1);
-                SET_FIELD(&rmd.status, RMDS, ERR, 1);
-            }
-            RMDSTORE(&rmd, PHYSADDR(s,crda));
-            s->csr[0] |= 0x0400;
-
-#ifdef PCNET_DEBUG
-            printf("RCVRC=%d CRDA=0x%08x BLKS=%d\n",
-                CSR_RCVRC(s), PHYSADDR(s,CSR_CRDA(s)), pktcount);
-#endif
-#ifdef PCNET_DEBUG_RMD
-            PRINT_RMD(&rmd);
-#endif
-
-            while (pktcount--) {
-                if (CSR_RCVRC(s) <= 1)
-                    CSR_RCVRC(s) = CSR_RCVRL(s);
-                else
-                    CSR_RCVRC(s)--;
-            }
-
-            pcnet_rdte_poll(s);
-
-        }
-    }
-
-    pcnet_poll(s);
-    pcnet_update_irq(s);
-
-    return size_;
-}
-
-void pcnet_set_link_status(NetClientState *nc)
-{
-    PCNetState *d = qemu_get_nic_opaque(nc);
-
-    d->lnkst = nc->link_down ? 0 : 0x40;
-}
-
-static void pcnet_transmit(PCNetState *s)
-{
-    hwaddr xmit_cxda = 0;
-    int count = CSR_XMTRL(s)-1;
-    int add_crc = 0;
-
-    s->xmit_pos = -1;
-
-    if (!CSR_TXON(s)) {
-        s->csr[0] &= ~0x0008;
-        return;
-    }
-
-    s->tx_busy = 1;
-
-    txagain:
-    if (pcnet_tdte_poll(s)) {
-        struct pcnet_TMD tmd;
-
-        TMDLOAD(&tmd, PHYSADDR(s,CSR_CXDA(s)));
-
-#ifdef PCNET_DEBUG_TMD
-        printf("  TMDLOAD 0x%08x\n", PHYSADDR(s,CSR_CXDA(s)));
-        PRINT_TMD(&tmd);
-#endif
-        if (GET_FIELD(tmd.status, TMDS, STP)) {
-            s->xmit_pos = 0;
-            xmit_cxda = PHYSADDR(s,CSR_CXDA(s));
-            if (BCR_SWSTYLE(s) != 1)
-                add_crc = GET_FIELD(tmd.status, TMDS, ADDFCS);
-        }
-        if (s->lnkst == 0 &&
-            (!CSR_LOOP(s) || (!CSR_INTL(s) && !BCR_TMAULOOP(s)))) {
-            SET_FIELD(&tmd.misc, TMDM, LCAR, 1);
-            SET_FIELD(&tmd.status, TMDS, ERR, 1);
-            SET_FIELD(&tmd.status, TMDS, OWN, 0);
-            s->csr[0] |= 0xa000; /* ERR | CERR */
-            s->xmit_pos = -1;
-            goto txdone;
-        }
-        if (!GET_FIELD(tmd.status, TMDS, ENP)) {
-            int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);
-            s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr),
-                             s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s));
-            s->xmit_pos += bcnt;
-        } else if (s->xmit_pos >= 0) {
-            int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT);
-            s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr),
-                             s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s));
-            s->xmit_pos += bcnt;
-#ifdef PCNET_DEBUG
-            printf("pcnet_transmit size=%d\n", s->xmit_pos);
-#endif
-            if (CSR_LOOP(s)) {
-                if (BCR_SWSTYLE(s) == 1)
-                    add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS);
-                s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC;
-                pcnet_receive(qemu_get_queue(s->nic), s->buffer, s->xmit_pos);
-                s->looptest = 0;
-            } else
-                if (s->nic)
-                    qemu_send_packet(qemu_get_queue(s->nic), s->buffer,
-                                     s->xmit_pos);
-
-            s->csr[0] &= ~0x0008;   /* clear TDMD */
-            s->csr[4] |= 0x0004;    /* set TXSTRT */
-            s->xmit_pos = -1;
-        }
-
-    txdone:
-        SET_FIELD(&tmd.status, TMDS, OWN, 0);
-        TMDSTORE(&tmd, PHYSADDR(s,CSR_CXDA(s)));
-        if (!CSR_TOKINTD(s) || (CSR_LTINTEN(s) && GET_FIELD(tmd.status, TMDS, LTINT)))
-            s->csr[0] |= 0x0200;    /* set TINT */
-
-        if (CSR_XMTRC(s)<=1)
-            CSR_XMTRC(s) = CSR_XMTRL(s);
-        else
-            CSR_XMTRC(s)--;
-        if (count--)
-            goto txagain;
-
-    } else
-    if (s->xmit_pos >= 0) {
-        struct pcnet_TMD tmd;
-        TMDLOAD(&tmd, xmit_cxda);
-        SET_FIELD(&tmd.misc, TMDM, BUFF, 1);
-        SET_FIELD(&tmd.misc, TMDM, UFLO, 1);
-        SET_FIELD(&tmd.status, TMDS, ERR, 1);
-        SET_FIELD(&tmd.status, TMDS, OWN, 0);
-        TMDSTORE(&tmd, xmit_cxda);
-        s->csr[0] |= 0x0200;    /* set TINT */
-        if (!CSR_DXSUFLO(s)) {
-            s->csr[0] &= ~0x0010;
-        } else
-        if (count--)
-          goto txagain;
-    }
-
-    s->tx_busy = 0;
-}
-
-static void pcnet_poll(PCNetState *s)
-{
-    if (CSR_RXON(s)) {
-        pcnet_rdte_poll(s);
-    }
-
-    if (CSR_TDMD(s) ||
-        (CSR_TXON(s) && !CSR_DPOLL(s) && pcnet_tdte_poll(s)))
-    {
-        /* prevent recursion */
-        if (s->tx_busy)
-            return;
-
-        pcnet_transmit(s);
-    }
-}
-
-static void pcnet_poll_timer(void *opaque)
-{
-    PCNetState *s = opaque;
-
-    qemu_del_timer(s->poll_timer);
-
-    if (CSR_TDMD(s)) {
-        pcnet_transmit(s);
-    }
-
-    pcnet_update_irq(s);
-
-    if (!CSR_STOP(s) && !CSR_SPND(s) && !CSR_DPOLL(s)) {
-        uint64_t now = qemu_get_clock_ns(vm_clock) * 33;
-        if (!s->timer || !now)
-            s->timer = now;
-        else {
-            uint64_t t = now - s->timer + CSR_POLL(s);
-            if (t > 0xffffLL) {
-                pcnet_poll(s);
-                CSR_POLL(s) = CSR_PINT(s);
-            } else
-                CSR_POLL(s) = t;
-        }
-        qemu_mod_timer(s->poll_timer,
-            pcnet_get_next_poll_time(s,qemu_get_clock_ns(vm_clock)));
-    }
-}
-
-
-static void pcnet_csr_writew(PCNetState *s, uint32_t rap, uint32_t new_value)
-{
-    uint16_t val = new_value;
-#ifdef PCNET_DEBUG_CSR
-    printf("pcnet_csr_writew rap=%d val=0x%04x\n", rap, val);
-#endif
-    switch (rap) {
-    case 0:
-        s->csr[0] &= ~(val & 0x7f00); /* Clear any interrupt flags */
-
-        s->csr[0] = (s->csr[0] & ~0x0040) | (val & 0x0048);
-
-        val = (val & 0x007f) | (s->csr[0] & 0x7f00);
-
-        /* IFF STOP, STRT and INIT are set, clear STRT and INIT */
-        if ((val&7) == 7)
-          val &= ~3;
-
-        if (!CSR_STOP(s) && (val & 4))
-            pcnet_stop(s);
-
-        if (!CSR_INIT(s) && (val & 1))
-            pcnet_init(s);
-
-        if (!CSR_STRT(s) && (val & 2))
-            pcnet_start(s);
-
-        if (CSR_TDMD(s))
-            pcnet_transmit(s);
-
-        return;
-    case 1:
-    case 2:
-    case 8:
-    case 9:
-    case 10:
-    case 11:
-    case 12:
-    case 13:
-    case 14:
-    case 15:
-    case 18: /* CRBAL */
-    case 19: /* CRBAU */
-    case 20: /* CXBAL */
-    case 21: /* CXBAU */
-    case 22: /* NRBAU */
-    case 23: /* NRBAU */
-    case 24:
-    case 25:
-    case 26:
-    case 27:
-    case 28:
-    case 29:
-    case 30:
-    case 31:
-    case 32:
-    case 33:
-    case 34:
-    case 35:
-    case 36:
-    case 37:
-    case 38:
-    case 39:
-    case 40: /* CRBC */
-    case 41:
-    case 42: /* CXBC */
-    case 43:
-    case 44:
-    case 45:
-    case 46: /* POLL */
-    case 47: /* POLLINT */
-    case 72:
-    case 74:
-    case 76: /* RCVRL */
-    case 78: /* XMTRL */
-    case 112:
-       if (CSR_STOP(s) || CSR_SPND(s))
-           break;
-       return;
-    case 3:
-        break;
-    case 4:
-        s->csr[4] &= ~(val & 0x026a);
-        val &= ~0x026a; val |= s->csr[4] & 0x026a;
-        break;
-    case 5:
-        s->csr[5] &= ~(val & 0x0a90);
-        val &= ~0x0a90; val |= s->csr[5] & 0x0a90;
-        break;
-    case 16:
-        pcnet_csr_writew(s,1,val);
-        return;
-    case 17:
-        pcnet_csr_writew(s,2,val);
-        return;
-    case 58:
-        pcnet_bcr_writew(s,BCR_SWS,val);
-        break;
-    default:
-        return;
-    }
-    s->csr[rap] = val;
-}
-
-static uint32_t pcnet_csr_readw(PCNetState *s, uint32_t rap)
-{
-    uint32_t val;
-    switch (rap) {
-    case 0:
-        pcnet_update_irq(s);
-        val = s->csr[0];
-        val |= (val & 0x7800) ? 0x8000 : 0;
-        break;
-    case 16:
-        return pcnet_csr_readw(s,1);
-    case 17:
-        return pcnet_csr_readw(s,2);
-    case 58:
-        return pcnet_bcr_readw(s,BCR_SWS);
-    case 88:
-        val = s->csr[89];
-        val <<= 16;
-        val |= s->csr[88];
-        break;
-    default:
-        val = s->csr[rap];
-    }
-#ifdef PCNET_DEBUG_CSR
-    printf("pcnet_csr_readw rap=%d val=0x%04x\n", rap, val);
-#endif
-    return val;
-}
-
-static void pcnet_bcr_writew(PCNetState *s, uint32_t rap, uint32_t val)
-{
-    rap &= 127;
-#ifdef PCNET_DEBUG_BCR
-    printf("pcnet_bcr_writew rap=%d val=0x%04x\n", rap, val);
-#endif
-    switch (rap) {
-    case BCR_SWS:
-        if (!(CSR_STOP(s) || CSR_SPND(s)))
-            return;
-        val &= ~0x0300;
-        switch (val & 0x00ff) {
-        case 0:
-            val |= 0x0200;
-            break;
-        case 1:
-            val |= 0x0100;
-            break;
-        case 2:
-        case 3:
-            val |= 0x0300;
-            break;
-        default:
-            printf("Bad SWSTYLE=0x%02x\n", val & 0xff);
-            val = 0x0200;
-            break;
-        }
-#ifdef PCNET_DEBUG
-       printf("BCR_SWS=0x%04x\n", val);
-#endif
-        /* fall through */
-    case BCR_LNKST:
-    case BCR_LED1:
-    case BCR_LED2:
-    case BCR_LED3:
-    case BCR_MC:
-    case BCR_FDC:
-    case BCR_BSBC:
-    case BCR_EECAS:
-    case BCR_PLAT:
-        s->bcr[rap] = val;
-        break;
-    default:
-        break;
-    }
-}
-
-uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap)
-{
-    uint32_t val;
-    rap &= 127;
-    switch (rap) {
-    case BCR_LNKST:
-    case BCR_LED1:
-    case BCR_LED2:
-    case BCR_LED3:
-        val = s->bcr[rap] & ~0x8000;
-        val |= (val & 0x017f & s->lnkst) ? 0x8000 : 0;
-        break;
-    default:
-        val = rap < 32 ? s->bcr[rap] : 0;
-        break;
-    }
-#ifdef PCNET_DEBUG_BCR
-    printf("pcnet_bcr_readw rap=%d val=0x%04x\n", rap, val);
-#endif
-    return val;
-}
-
-void pcnet_h_reset(void *opaque)
-{
-    PCNetState *s = opaque;
-
-    s->bcr[BCR_MSRDA] = 0x0005;
-    s->bcr[BCR_MSWRA] = 0x0005;
-    s->bcr[BCR_MC   ] = 0x0002;
-    s->bcr[BCR_LNKST] = 0x00c0;
-    s->bcr[BCR_LED1 ] = 0x0084;
-    s->bcr[BCR_LED2 ] = 0x0088;
-    s->bcr[BCR_LED3 ] = 0x0090;
-    s->bcr[BCR_FDC  ] = 0x0000;
-    s->bcr[BCR_BSBC ] = 0x9001;
-    s->bcr[BCR_EECAS] = 0x0002;
-    s->bcr[BCR_SWS  ] = 0x0200;
-    s->bcr[BCR_PLAT ] = 0xff06;
-
-    pcnet_s_reset(s);
-    pcnet_update_irq(s);
-    pcnet_poll_timer(s);
-}
-
-void pcnet_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
-{
-    PCNetState *s = opaque;
-    pcnet_poll_timer(s);
-#ifdef PCNET_DEBUG_IO
-    printf("pcnet_ioport_writew addr=0x%08x val=0x%04x\n", addr, val);
-#endif
-    if (!BCR_DWIO(s)) {
-        switch (addr & 0x0f) {
-        case 0x00: /* RDP */
-            pcnet_csr_writew(s, s->rap, val);
-            break;
-        case 0x02:
-            s->rap = val & 0x7f;
-            break;
-        case 0x06:
-            pcnet_bcr_writew(s, s->rap, val);
-            break;
-        }
-    }
-    pcnet_update_irq(s);
-}
-
-uint32_t pcnet_ioport_readw(void *opaque, uint32_t addr)
-{
-    PCNetState *s = opaque;
-    uint32_t val = -1;
-    pcnet_poll_timer(s);
-    if (!BCR_DWIO(s)) {
-        switch (addr & 0x0f) {
-        case 0x00: /* RDP */
-            val = pcnet_csr_readw(s, s->rap);
-            break;
-        case 0x02:
-            val = s->rap;
-            break;
-        case 0x04:
-            pcnet_s_reset(s);
-            val = 0;
-            break;
-        case 0x06:
-            val = pcnet_bcr_readw(s, s->rap);
-            break;
-        }
-    }
-    pcnet_update_irq(s);
-#ifdef PCNET_DEBUG_IO
-    printf("pcnet_ioport_readw addr=0x%08x val=0x%04x\n", addr, val & 0xffff);
-#endif
-    return val;
-}
-
-void pcnet_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
-{
-    PCNetState *s = opaque;
-    pcnet_poll_timer(s);
-#ifdef PCNET_DEBUG_IO
-    printf("pcnet_ioport_writel addr=0x%08x val=0x%08x\n", addr, val);
-#endif
-    if (BCR_DWIO(s)) {
-        switch (addr & 0x0f) {
-        case 0x00: /* RDP */
-            pcnet_csr_writew(s, s->rap, val & 0xffff);
-            break;
-        case 0x04:
-            s->rap = val & 0x7f;
-            break;
-        case 0x0c:
-            pcnet_bcr_writew(s, s->rap, val & 0xffff);
-            break;
-        }
-    } else
-    if ((addr & 0x0f) == 0) {
-        /* switch device to dword i/o mode */
-        pcnet_bcr_writew(s, BCR_BSBC, pcnet_bcr_readw(s, BCR_BSBC) | 0x0080);
-#ifdef PCNET_DEBUG_IO
-        printf("device switched into dword i/o mode\n");
-#endif
-    }
-    pcnet_update_irq(s);
-}
-
-uint32_t pcnet_ioport_readl(void *opaque, uint32_t addr)
-{
-    PCNetState *s = opaque;
-    uint32_t val = -1;
-    pcnet_poll_timer(s);
-    if (BCR_DWIO(s)) {
-        switch (addr & 0x0f) {
-        case 0x00: /* RDP */
-            val = pcnet_csr_readw(s, s->rap);
-            break;
-        case 0x04:
-            val = s->rap;
-            break;
-        case 0x08:
-            pcnet_s_reset(s);
-            val = 0;
-            break;
-        case 0x0c:
-            val = pcnet_bcr_readw(s, s->rap);
-            break;
-        }
-    }
-    pcnet_update_irq(s);
-#ifdef PCNET_DEBUG_IO
-    printf("pcnet_ioport_readl addr=0x%08x val=0x%08x\n", addr, val);
-#endif
-    return val;
-}
-
-static bool is_version_2(void *opaque, int version_id)
-{
-    return version_id == 2;
-}
-
-const VMStateDescription vmstate_pcnet = {
-    .name = "pcnet",
-    .version_id = 3,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .fields      = (VMStateField []) {
-        VMSTATE_INT32(rap, PCNetState),
-        VMSTATE_INT32(isr, PCNetState),
-        VMSTATE_INT32(lnkst, PCNetState),
-        VMSTATE_UINT32(rdra, PCNetState),
-        VMSTATE_UINT32(tdra, PCNetState),
-        VMSTATE_BUFFER(prom, PCNetState),
-        VMSTATE_UINT16_ARRAY(csr, PCNetState, 128),
-        VMSTATE_UINT16_ARRAY(bcr, PCNetState, 32),
-        VMSTATE_UINT64(timer, PCNetState),
-        VMSTATE_INT32(xmit_pos, PCNetState),
-        VMSTATE_BUFFER(buffer, PCNetState),
-        VMSTATE_UNUSED_TEST(is_version_2, 4),
-        VMSTATE_INT32(tx_busy, PCNetState),
-        VMSTATE_TIMER(poll_timer, PCNetState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-void pcnet_common_cleanup(PCNetState *d)
-{
-    d->nic = NULL;
-}
-
-int pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info)
-{
-    int i;
-    uint16_t checksum;
-
-    s->poll_timer = qemu_new_timer_ns(vm_clock, pcnet_poll_timer, s);
-
-    qemu_macaddr_default_if_unset(&s->conf.macaddr);
-    s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, s);
-    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
-
-    add_boot_device_path(s->conf.bootindex, dev, "/ethernet-phy@0");
-
-    /* Initialize the PROM */
-
-    /*
-      Datasheet: http://pdfdata.datasheetsite.com/web/24528/AM79C970A.pdf
-      page 95
-    */
-    memcpy(s->prom, s->conf.macaddr.a, 6);
-    /* Reserved Location: must be 00h */
-    s->prom[6] = s->prom[7] = 0x00;
-    /* Reserved Location: must be 00h */
-    s->prom[8] = 0x00;
-    /* Hardware ID: must be 11h if compatibility to AMD drivers is desired */
-    s->prom[9] = 0x11;
-    /* User programmable space, init with 0 */
-    s->prom[10] = s->prom[11] = 0x00;
-    /* LSByte of two-byte checksum, which is the sum of bytes 00h-0Bh
-       and bytes 0Eh and 0Fh, must therefore be initialized with 0! */
-    s->prom[12] = s->prom[13] = 0x00;
-    /* Must be ASCII W (57h) if compatibility to AMD
-       driver software is desired */
-    s->prom[14] = s->prom[15] = 0x57;
-
-    for (i = 0, checksum = 0; i < 16; i++) {
-        checksum += s->prom[i];
-    }
-    *(uint16_t *)&s->prom[12] = cpu_to_le16(checksum);
-
-    s->lnkst = 0x40; /* initial link state: up */
-
-    return 0;
-}
diff --git a/hw/pcspk.c b/hw/pcspk.c
deleted file mode 100644 (file)
index 34e0df7..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * QEMU PC speaker emulation
- *
- * Copyright (c) 2006 Joachim Henke
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/hw.h"
-#include "hw/i386/pc.h"
-#include "hw/isa/isa.h"
-#include "audio/audio.h"
-#include "qemu/timer.h"
-#include "hw/timer/i8254.h"
-#include "hw/audio/pcspk.h"
-
-#define PCSPK_BUF_LEN 1792
-#define PCSPK_SAMPLE_RATE 32000
-#define PCSPK_MAX_FREQ (PCSPK_SAMPLE_RATE >> 1)
-#define PCSPK_MIN_COUNT ((PIT_FREQ + PCSPK_MAX_FREQ - 1) / PCSPK_MAX_FREQ)
-
-typedef struct {
-    ISADevice dev;
-    MemoryRegion ioport;
-    uint32_t iobase;
-    uint8_t sample_buf[PCSPK_BUF_LEN];
-    QEMUSoundCard card;
-    SWVoiceOut *voice;
-    void *pit;
-    unsigned int pit_count;
-    unsigned int samples;
-    unsigned int play_pos;
-    int data_on;
-    int dummy_refresh_clock;
-} PCSpkState;
-
-static const char *s_spk = "pcspk";
-static PCSpkState *pcspk_state;
-
-static inline void generate_samples(PCSpkState *s)
-{
-    unsigned int i;
-
-    if (s->pit_count) {
-        const uint32_t m = PCSPK_SAMPLE_RATE * s->pit_count;
-        const uint32_t n = ((uint64_t)PIT_FREQ << 32) / m;
-
-        /* multiple of wavelength for gapless looping */
-        s->samples = (PCSPK_BUF_LEN * PIT_FREQ / m * m / (PIT_FREQ >> 1) + 1) >> 1;
-        for (i = 0; i < s->samples; ++i)
-            s->sample_buf[i] = (64 & (n * i >> 25)) - 32;
-    } else {
-        s->samples = PCSPK_BUF_LEN;
-        for (i = 0; i < PCSPK_BUF_LEN; ++i)
-            s->sample_buf[i] = 128; /* silence */
-    }
-}
-
-static void pcspk_callback(void *opaque, int free)
-{
-    PCSpkState *s = opaque;
-    PITChannelInfo ch;
-    unsigned int n;
-
-    pit_get_channel_info(s->pit, 2, &ch);
-
-    if (ch.mode != 3) {
-        return;
-    }
-
-    n = ch.initial_count;
-    /* avoid frequencies that are not reproducible with sample rate */
-    if (n < PCSPK_MIN_COUNT)
-        n = 0;
-
-    if (s->pit_count != n) {
-        s->pit_count = n;
-        s->play_pos = 0;
-        generate_samples(s);
-    }
-
-    while (free > 0) {
-        n = audio_MIN(s->samples - s->play_pos, (unsigned int)free);
-        n = AUD_write(s->voice, &s->sample_buf[s->play_pos], n);
-        if (!n)
-            break;
-        s->play_pos = (s->play_pos + n) % s->samples;
-        free -= n;
-    }
-}
-
-int pcspk_audio_init(ISABus *bus)
-{
-    PCSpkState *s = pcspk_state;
-    struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUD_FMT_U8, 0};
-
-    AUD_register_card(s_spk, &s->card);
-
-    s->voice = AUD_open_out(&s->card, s->voice, s_spk, s, pcspk_callback, &as);
-    if (!s->voice) {
-        AUD_log(s_spk, "Could not open voice\n");
-        return -1;
-    }
-
-    return 0;
-}
-
-static uint64_t pcspk_io_read(void *opaque, hwaddr addr,
-                              unsigned size)
-{
-    PCSpkState *s = opaque;
-    PITChannelInfo ch;
-
-    pit_get_channel_info(s->pit, 2, &ch);
-
-    s->dummy_refresh_clock ^= (1 << 4);
-
-    return ch.gate | (s->data_on << 1) | s->dummy_refresh_clock |
-       (ch.out << 5);
-}
-
-static void pcspk_io_write(void *opaque, hwaddr addr, uint64_t val,
-                           unsigned size)
-{
-    PCSpkState *s = opaque;
-    const int gate = val & 1;
-
-    s->data_on = (val >> 1) & 1;
-    pit_set_gate(s->pit, 2, gate);
-    if (s->voice) {
-        if (gate) /* restart */
-            s->play_pos = 0;
-        AUD_set_active_out(s->voice, gate & s->data_on);
-    }
-}
-
-static const MemoryRegionOps pcspk_io_ops = {
-    .read = pcspk_io_read,
-    .write = pcspk_io_write,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-};
-
-static int pcspk_initfn(ISADevice *dev)
-{
-    PCSpkState *s = DO_UPCAST(PCSpkState, dev, dev);
-
-    memory_region_init_io(&s->ioport, &pcspk_io_ops, s, "elcr", 1);
-    isa_register_ioport(dev, &s->ioport, s->iobase);
-
-    pcspk_state = s;
-
-    return 0;
-}
-
-static Property pcspk_properties[] = {
-    DEFINE_PROP_HEX32("iobase", PCSpkState, iobase,  -1),
-    DEFINE_PROP_PTR("pit", PCSpkState, pit),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void pcspk_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
-
-    ic->init = pcspk_initfn;
-    dc->no_user = 1;
-    dc->props = pcspk_properties;
-}
-
-static const TypeInfo pcspk_info = {
-    .name           = "isa-pcspk",
-    .parent         = TYPE_ISA_DEVICE,
-    .instance_size  = sizeof(PCSpkState),
-    .class_init     = pcspk_class_initfn,
-};
-
-static void pcspk_register(void)
-{
-    type_register_static(&pcspk_info);
-}
-type_init(pcspk_register)
diff --git a/hw/pflash_cfi01.c b/hw/pflash_cfi01.c
deleted file mode 100644 (file)
index 3ff20e0..0000000
+++ /dev/null
@@ -1,769 +0,0 @@
-/*
- *  CFI parallel flash with Intel command set emulation
- *
- *  Copyright (c) 2006 Thorsten Zitterell
- *  Copyright (c) 2005 Jocelyn Mayer
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * For now, this code can emulate flashes of 1, 2 or 4 bytes width.
- * Supported commands/modes are:
- * - flash read
- * - flash write
- * - flash ID read
- * - sector erase
- * - CFI queries
- *
- * It does not support timings
- * It does not support flash interleaving
- * It does not implement software data protection as found in many real chips
- * It does not implement erase suspend/resume commands
- * It does not implement multiple sectors erase
- *
- * It does not implement much more ...
- */
-
-#include "hw/hw.h"
-#include "hw/block/flash.h"
-#include "block/block.h"
-#include "qemu/timer.h"
-#include "exec/address-spaces.h"
-#include "qemu/host-utils.h"
-#include "hw/sysbus.h"
-
-#define PFLASH_BUG(fmt, ...) \
-do { \
-    fprintf(stderr, "PFLASH: Possible BUG - " fmt, ## __VA_ARGS__); \
-    exit(1); \
-} while(0)
-
-/* #define PFLASH_DEBUG */
-#ifdef PFLASH_DEBUG
-#define DPRINTF(fmt, ...)                                   \
-do {                                                        \
-    fprintf(stderr, "PFLASH: " fmt , ## __VA_ARGS__);       \
-} while (0)
-#else
-#define DPRINTF(fmt, ...) do { } while (0)
-#endif
-
-struct pflash_t {
-    SysBusDevice busdev;
-    BlockDriverState *bs;
-    uint32_t nb_blocs;
-    uint64_t sector_len;
-    uint8_t width;
-    uint8_t be;
-    uint8_t wcycle; /* if 0, the flash is read normally */
-    int ro;
-    uint8_t cmd;
-    uint8_t status;
-    uint16_t ident0;
-    uint16_t ident1;
-    uint16_t ident2;
-    uint16_t ident3;
-    uint8_t cfi_len;
-    uint8_t cfi_table[0x52];
-    uint64_t counter;
-    unsigned int writeblock_size;
-    QEMUTimer *timer;
-    MemoryRegion mem;
-    char *name;
-    void *storage;
-};
-
-static const VMStateDescription vmstate_pflash = {
-    .name = "pflash_cfi01",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT8(wcycle, pflash_t),
-        VMSTATE_UINT8(cmd, pflash_t),
-        VMSTATE_UINT8(status, pflash_t),
-        VMSTATE_UINT64(counter, pflash_t),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void pflash_timer (void *opaque)
-{
-    pflash_t *pfl = opaque;
-
-    DPRINTF("%s: command %02x done\n", __func__, pfl->cmd);
-    /* Reset flash */
-    pfl->status ^= 0x80;
-    memory_region_rom_device_set_readable(&pfl->mem, true);
-    pfl->wcycle = 0;
-    pfl->cmd = 0;
-}
-
-static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
-                             int width, int be)
-{
-    hwaddr boff;
-    uint32_t ret;
-    uint8_t *p;
-
-    ret = -1;
-    boff = offset & 0xFF; /* why this here ?? */
-
-    if (pfl->width == 2)
-        boff = boff >> 1;
-    else if (pfl->width == 4)
-        boff = boff >> 2;
-
-#if 0
-    DPRINTF("%s: reading offset " TARGET_FMT_plx " under cmd %02x width %d\n",
-            __func__, offset, pfl->cmd, width);
-#endif
-    switch (pfl->cmd) {
-    default:
-        /* This should never happen : reset state & treat it as a read */
-        DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd);
-        pfl->wcycle = 0;
-        pfl->cmd = 0;
-        /* fall through to read code */
-    case 0x00:
-        /* Flash area read */
-        p = pfl->storage;
-        switch (width) {
-        case 1:
-            ret = p[offset];
-            DPRINTF("%s: data offset " TARGET_FMT_plx " %02x\n",
-                    __func__, offset, ret);
-            break;
-        case 2:
-            if (be) {
-                ret = p[offset] << 8;
-                ret |= p[offset + 1];
-            } else {
-                ret = p[offset];
-                ret |= p[offset + 1] << 8;
-            }
-            DPRINTF("%s: data offset " TARGET_FMT_plx " %04x\n",
-                    __func__, offset, ret);
-            break;
-        case 4:
-            if (be) {
-                ret = p[offset] << 24;
-                ret |= p[offset + 1] << 16;
-                ret |= p[offset + 2] << 8;
-                ret |= p[offset + 3];
-            } else {
-                ret = p[offset];
-                ret |= p[offset + 1] << 8;
-                ret |= p[offset + 2] << 16;
-                ret |= p[offset + 3] << 24;
-            }
-            DPRINTF("%s: data offset " TARGET_FMT_plx " %08x\n",
-                    __func__, offset, ret);
-            break;
-        default:
-            DPRINTF("BUG in %s\n", __func__);
-        }
-
-        break;
-    case 0x10: /* Single byte program */
-    case 0x20: /* Block erase */
-    case 0x28: /* Block erase */
-    case 0x40: /* single byte program */
-    case 0x50: /* Clear status register */
-    case 0x60: /* Block /un)lock */
-    case 0x70: /* Status Register */
-    case 0xe8: /* Write block */
-        /* Status register read */
-        ret = pfl->status;
-        DPRINTF("%s: status %x\n", __func__, ret);
-        break;
-    case 0x90:
-        switch (boff) {
-        case 0:
-            ret = pfl->ident0 << 8 | pfl->ident1;
-            DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret);
-            break;
-        case 1:
-            ret = pfl->ident2 << 8 | pfl->ident3;
-            DPRINTF("%s: Device ID Code %04x\n", __func__, ret);
-            break;
-        default:
-            DPRINTF("%s: Read Device Information boff=%x\n", __func__,
-                    (unsigned)boff);
-            ret = 0;
-            break;
-        }
-        break;
-    case 0x98: /* Query mode */
-        if (boff > pfl->cfi_len)
-            ret = 0;
-        else
-            ret = pfl->cfi_table[boff];
-        break;
-    }
-    return ret;
-}
-
-/* update flash content on disk */
-static void pflash_update(pflash_t *pfl, int offset,
-                          int size)
-{
-    int offset_end;
-    if (pfl->bs) {
-        offset_end = offset + size;
-        /* round to sectors */
-        offset = offset >> 9;
-        offset_end = (offset_end + 511) >> 9;
-        bdrv_write(pfl->bs, offset, pfl->storage + (offset << 9),
-                   offset_end - offset);
-    }
-}
-
-static inline void pflash_data_write(pflash_t *pfl, hwaddr offset,
-                                     uint32_t value, int width, int be)
-{
-    uint8_t *p = pfl->storage;
-
-    DPRINTF("%s: block write offset " TARGET_FMT_plx
-            " value %x counter %016" PRIx64 "\n",
-            __func__, offset, value, pfl->counter);
-    switch (width) {
-    case 1:
-        p[offset] = value;
-        break;
-    case 2:
-        if (be) {
-            p[offset] = value >> 8;
-            p[offset + 1] = value;
-        } else {
-            p[offset] = value;
-            p[offset + 1] = value >> 8;
-        }
-        break;
-    case 4:
-        if (be) {
-            p[offset] = value >> 24;
-            p[offset + 1] = value >> 16;
-            p[offset + 2] = value >> 8;
-            p[offset + 3] = value;
-        } else {
-            p[offset] = value;
-            p[offset + 1] = value >> 8;
-            p[offset + 2] = value >> 16;
-            p[offset + 3] = value >> 24;
-        }
-        break;
-    }
-
-}
-
-static void pflash_write(pflash_t *pfl, hwaddr offset,
-                         uint32_t value, int width, int be)
-{
-    uint8_t *p;
-    uint8_t cmd;
-
-    cmd = value;
-
-    DPRINTF("%s: writing offset " TARGET_FMT_plx " value %08x width %d wcycle 0x%x\n",
-            __func__, offset, value, width, pfl->wcycle);
-
-    if (!pfl->wcycle) {
-        /* Set the device in I/O access mode */
-        memory_region_rom_device_set_readable(&pfl->mem, false);
-    }
-
-    switch (pfl->wcycle) {
-    case 0:
-        /* read mode */
-        switch (cmd) {
-        case 0x00: /* ??? */
-            goto reset_flash;
-        case 0x10: /* Single Byte Program */
-        case 0x40: /* Single Byte Program */
-            DPRINTF("%s: Single Byte Program\n", __func__);
-            break;
-        case 0x20: /* Block erase */
-            p = pfl->storage;
-            offset &= ~(pfl->sector_len - 1);
-
-            DPRINTF("%s: block erase at " TARGET_FMT_plx " bytes %x\n",
-                    __func__, offset, (unsigned)pfl->sector_len);
-
-            if (!pfl->ro) {
-                memset(p + offset, 0xff, pfl->sector_len);
-                pflash_update(pfl, offset, pfl->sector_len);
-            } else {
-                pfl->status |= 0x20; /* Block erase error */
-            }
-            pfl->status |= 0x80; /* Ready! */
-            break;
-        case 0x50: /* Clear status bits */
-            DPRINTF("%s: Clear status bits\n", __func__);
-            pfl->status = 0x0;
-            goto reset_flash;
-        case 0x60: /* Block (un)lock */
-            DPRINTF("%s: Block unlock\n", __func__);
-            break;
-        case 0x70: /* Status Register */
-            DPRINTF("%s: Read status register\n", __func__);
-            pfl->cmd = cmd;
-            return;
-        case 0x90: /* Read Device ID */
-            DPRINTF("%s: Read Device information\n", __func__);
-            pfl->cmd = cmd;
-            return;
-        case 0x98: /* CFI query */
-            DPRINTF("%s: CFI query\n", __func__);
-            break;
-        case 0xe8: /* Write to buffer */
-            DPRINTF("%s: Write to buffer\n", __func__);
-            pfl->status |= 0x80; /* Ready! */
-            break;
-        case 0xf0: /* Probe for AMD flash */
-            DPRINTF("%s: Probe for AMD flash\n", __func__);
-            goto reset_flash;
-        case 0xff: /* Read array mode */
-            DPRINTF("%s: Read array mode\n", __func__);
-            goto reset_flash;
-        default:
-            goto error_flash;
-        }
-        pfl->wcycle++;
-        pfl->cmd = cmd;
-        break;
-    case 1:
-        switch (pfl->cmd) {
-        case 0x10: /* Single Byte Program */
-        case 0x40: /* Single Byte Program */
-            DPRINTF("%s: Single Byte Program\n", __func__);
-            if (!pfl->ro) {
-                pflash_data_write(pfl, offset, value, width, be);
-                pflash_update(pfl, offset, width);
-            } else {
-                pfl->status |= 0x10; /* Programming error */
-            }
-            pfl->status |= 0x80; /* Ready! */
-            pfl->wcycle = 0;
-        break;
-        case 0x20: /* Block erase */
-        case 0x28:
-            if (cmd == 0xd0) { /* confirm */
-                pfl->wcycle = 0;
-                pfl->status |= 0x80;
-            } else if (cmd == 0xff) { /* read array mode */
-                goto reset_flash;
-            } else
-                goto error_flash;
-
-            break;
-        case 0xe8:
-            DPRINTF("%s: block write of %x bytes\n", __func__, value);
-            pfl->counter = value;
-            pfl->wcycle++;
-            break;
-        case 0x60:
-            if (cmd == 0xd0) {
-                pfl->wcycle = 0;
-                pfl->status |= 0x80;
-            } else if (cmd == 0x01) {
-                pfl->wcycle = 0;
-                pfl->status |= 0x80;
-            } else if (cmd == 0xff) {
-                goto reset_flash;
-            } else {
-                DPRINTF("%s: Unknown (un)locking command\n", __func__);
-                goto reset_flash;
-            }
-            break;
-        case 0x98:
-            if (cmd == 0xff) {
-                goto reset_flash;
-            } else {
-                DPRINTF("%s: leaving query mode\n", __func__);
-            }
-            break;
-        default:
-            goto error_flash;
-        }
-        break;
-    case 2:
-        switch (pfl->cmd) {
-        case 0xe8: /* Block write */
-            if (!pfl->ro) {
-                pflash_data_write(pfl, offset, value, width, be);
-            } else {
-                pfl->status |= 0x10; /* Programming error */
-            }
-
-            pfl->status |= 0x80;
-
-            if (!pfl->counter) {
-                hwaddr mask = pfl->writeblock_size - 1;
-                mask = ~mask;
-
-                DPRINTF("%s: block write finished\n", __func__);
-                pfl->wcycle++;
-                if (!pfl->ro) {
-                    /* Flush the entire write buffer onto backing storage.  */
-                    pflash_update(pfl, offset & mask, pfl->writeblock_size);
-                } else {
-                    pfl->status |= 0x10; /* Programming error */
-                }
-            }
-
-            pfl->counter--;
-            break;
-        default:
-            goto error_flash;
-        }
-        break;
-    case 3: /* Confirm mode */
-        switch (pfl->cmd) {
-        case 0xe8: /* Block write */
-            if (cmd == 0xd0) {
-                pfl->wcycle = 0;
-                pfl->status |= 0x80;
-            } else {
-                DPRINTF("%s: unknown command for \"write block\"\n", __func__);
-                PFLASH_BUG("Write block confirm");
-                goto reset_flash;
-            }
-            break;
-        default:
-            goto error_flash;
-        }
-        break;
-    default:
-        /* Should never happen */
-        DPRINTF("%s: invalid write state\n",  __func__);
-        goto reset_flash;
-    }
-    return;
-
- error_flash:
-    qemu_log_mask(LOG_UNIMP, "%s: Unimplemented flash cmd sequence "
-                  "(offset " TARGET_FMT_plx ", wcycle 0x%x cmd 0x%x value 0x%x)"
-                  "\n", __func__, offset, pfl->wcycle, pfl->cmd, value);
-
- reset_flash:
-    memory_region_rom_device_set_readable(&pfl->mem, true);
-
-    pfl->wcycle = 0;
-    pfl->cmd = 0;
-}
-
-
-static uint32_t pflash_readb_be(void *opaque, hwaddr addr)
-{
-    return pflash_read(opaque, addr, 1, 1);
-}
-
-static uint32_t pflash_readb_le(void *opaque, hwaddr addr)
-{
-    return pflash_read(opaque, addr, 1, 0);
-}
-
-static uint32_t pflash_readw_be(void *opaque, hwaddr addr)
-{
-    pflash_t *pfl = opaque;
-
-    return pflash_read(pfl, addr, 2, 1);
-}
-
-static uint32_t pflash_readw_le(void *opaque, hwaddr addr)
-{
-    pflash_t *pfl = opaque;
-
-    return pflash_read(pfl, addr, 2, 0);
-}
-
-static uint32_t pflash_readl_be(void *opaque, hwaddr addr)
-{
-    pflash_t *pfl = opaque;
-
-    return pflash_read(pfl, addr, 4, 1);
-}
-
-static uint32_t pflash_readl_le(void *opaque, hwaddr addr)
-{
-    pflash_t *pfl = opaque;
-
-    return pflash_read(pfl, addr, 4, 0);
-}
-
-static void pflash_writeb_be(void *opaque, hwaddr addr,
-                             uint32_t value)
-{
-    pflash_write(opaque, addr, value, 1, 1);
-}
-
-static void pflash_writeb_le(void *opaque, hwaddr addr,
-                             uint32_t value)
-{
-    pflash_write(opaque, addr, value, 1, 0);
-}
-
-static void pflash_writew_be(void *opaque, hwaddr addr,
-                             uint32_t value)
-{
-    pflash_t *pfl = opaque;
-
-    pflash_write(pfl, addr, value, 2, 1);
-}
-
-static void pflash_writew_le(void *opaque, hwaddr addr,
-                             uint32_t value)
-{
-    pflash_t *pfl = opaque;
-
-    pflash_write(pfl, addr, value, 2, 0);
-}
-
-static void pflash_writel_be(void *opaque, hwaddr addr,
-                             uint32_t value)
-{
-    pflash_t *pfl = opaque;
-
-    pflash_write(pfl, addr, value, 4, 1);
-}
-
-static void pflash_writel_le(void *opaque, hwaddr addr,
-                             uint32_t value)
-{
-    pflash_t *pfl = opaque;
-
-    pflash_write(pfl, addr, value, 4, 0);
-}
-
-static const MemoryRegionOps pflash_cfi01_ops_be = {
-    .old_mmio = {
-        .read = { pflash_readb_be, pflash_readw_be, pflash_readl_be, },
-        .write = { pflash_writeb_be, pflash_writew_be, pflash_writel_be, },
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static const MemoryRegionOps pflash_cfi01_ops_le = {
-    .old_mmio = {
-        .read = { pflash_readb_le, pflash_readw_le, pflash_readl_le, },
-        .write = { pflash_writeb_le, pflash_writew_le, pflash_writel_le, },
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int pflash_cfi01_init(SysBusDevice *dev)
-{
-    pflash_t *pfl = FROM_SYSBUS(typeof(*pfl), dev);
-    uint64_t total_len;
-    int ret;
-
-    total_len = pfl->sector_len * pfl->nb_blocs;
-
-    /* XXX: to be fixed */
-#if 0
-    if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) &&
-        total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024))
-        return NULL;
-#endif
-
-    memory_region_init_rom_device(
-        &pfl->mem, pfl->be ? &pflash_cfi01_ops_be : &pflash_cfi01_ops_le, pfl,
-        pfl->name, total_len);
-    vmstate_register_ram(&pfl->mem, DEVICE(pfl));
-    pfl->storage = memory_region_get_ram_ptr(&pfl->mem);
-    sysbus_init_mmio(dev, &pfl->mem);
-
-    if (pfl->bs) {
-        /* read the initial flash content */
-        ret = bdrv_read(pfl->bs, 0, pfl->storage, total_len >> 9);
-
-        if (ret < 0) {
-            vmstate_unregister_ram(&pfl->mem, DEVICE(pfl));
-            memory_region_destroy(&pfl->mem);
-            return 1;
-        }
-    }
-
-    if (pfl->bs) {
-        pfl->ro = bdrv_is_read_only(pfl->bs);
-    } else {
-        pfl->ro = 0;
-    }
-
-    pfl->timer = qemu_new_timer_ns(vm_clock, pflash_timer, pfl);
-    pfl->wcycle = 0;
-    pfl->cmd = 0;
-    pfl->status = 0;
-    /* Hardcoded CFI table */
-    pfl->cfi_len = 0x52;
-    /* Standard "QRY" string */
-    pfl->cfi_table[0x10] = 'Q';
-    pfl->cfi_table[0x11] = 'R';
-    pfl->cfi_table[0x12] = 'Y';
-    /* Command set (Intel) */
-    pfl->cfi_table[0x13] = 0x01;
-    pfl->cfi_table[0x14] = 0x00;
-    /* Primary extended table address (none) */
-    pfl->cfi_table[0x15] = 0x31;
-    pfl->cfi_table[0x16] = 0x00;
-    /* Alternate command set (none) */
-    pfl->cfi_table[0x17] = 0x00;
-    pfl->cfi_table[0x18] = 0x00;
-    /* Alternate extended table (none) */
-    pfl->cfi_table[0x19] = 0x00;
-    pfl->cfi_table[0x1A] = 0x00;
-    /* Vcc min */
-    pfl->cfi_table[0x1B] = 0x45;
-    /* Vcc max */
-    pfl->cfi_table[0x1C] = 0x55;
-    /* Vpp min (no Vpp pin) */
-    pfl->cfi_table[0x1D] = 0x00;
-    /* Vpp max (no Vpp pin) */
-    pfl->cfi_table[0x1E] = 0x00;
-    /* Reserved */
-    pfl->cfi_table[0x1F] = 0x07;
-    /* Timeout for min size buffer write */
-    pfl->cfi_table[0x20] = 0x07;
-    /* Typical timeout for block erase */
-    pfl->cfi_table[0x21] = 0x0a;
-    /* Typical timeout for full chip erase (4096 ms) */
-    pfl->cfi_table[0x22] = 0x00;
-    /* Reserved */
-    pfl->cfi_table[0x23] = 0x04;
-    /* Max timeout for buffer write */
-    pfl->cfi_table[0x24] = 0x04;
-    /* Max timeout for block erase */
-    pfl->cfi_table[0x25] = 0x04;
-    /* Max timeout for chip erase */
-    pfl->cfi_table[0x26] = 0x00;
-    /* Device size */
-    pfl->cfi_table[0x27] = ctz32(total_len); // + 1;
-    /* Flash device interface (8 & 16 bits) */
-    pfl->cfi_table[0x28] = 0x02;
-    pfl->cfi_table[0x29] = 0x00;
-    /* Max number of bytes in multi-bytes write */
-    if (pfl->width == 1) {
-        pfl->cfi_table[0x2A] = 0x08;
-    } else {
-        pfl->cfi_table[0x2A] = 0x0B;
-    }
-    pfl->writeblock_size = 1 << pfl->cfi_table[0x2A];
-
-    pfl->cfi_table[0x2B] = 0x00;
-    /* Number of erase block regions (uniform) */
-    pfl->cfi_table[0x2C] = 0x01;
-    /* Erase block region 1 */
-    pfl->cfi_table[0x2D] = pfl->nb_blocs - 1;
-    pfl->cfi_table[0x2E] = (pfl->nb_blocs - 1) >> 8;
-    pfl->cfi_table[0x2F] = pfl->sector_len >> 8;
-    pfl->cfi_table[0x30] = pfl->sector_len >> 16;
-
-    /* Extended */
-    pfl->cfi_table[0x31] = 'P';
-    pfl->cfi_table[0x32] = 'R';
-    pfl->cfi_table[0x33] = 'I';
-
-    pfl->cfi_table[0x34] = '1';
-    pfl->cfi_table[0x35] = '0';
-
-    pfl->cfi_table[0x36] = 0x00;
-    pfl->cfi_table[0x37] = 0x00;
-    pfl->cfi_table[0x38] = 0x00;
-    pfl->cfi_table[0x39] = 0x00;
-
-    pfl->cfi_table[0x3a] = 0x00;
-
-    pfl->cfi_table[0x3b] = 0x00;
-    pfl->cfi_table[0x3c] = 0x00;
-
-    pfl->cfi_table[0x3f] = 0x01; /* Number of protection fields */
-
-    return 0;
-}
-
-static Property pflash_cfi01_properties[] = {
-    DEFINE_PROP_DRIVE("drive", struct pflash_t, bs),
-    DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0),
-    DEFINE_PROP_UINT64("sector-length", struct pflash_t, sector_len, 0),
-    DEFINE_PROP_UINT8("width", struct pflash_t, width, 0),
-    DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0),
-    DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0),
-    DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0),
-    DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0),
-    DEFINE_PROP_UINT16("id3", struct pflash_t, ident3, 0),
-    DEFINE_PROP_STRING("name", struct pflash_t, name),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void pflash_cfi01_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = pflash_cfi01_init;
-    dc->props = pflash_cfi01_properties;
-    dc->vmsd = &vmstate_pflash;
-}
-
-
-static const TypeInfo pflash_cfi01_info = {
-    .name           = "cfi.pflash01",
-    .parent         = TYPE_SYS_BUS_DEVICE,
-    .instance_size  = sizeof(struct pflash_t),
-    .class_init     = pflash_cfi01_class_init,
-};
-
-static void pflash_cfi01_register_types(void)
-{
-    type_register_static(&pflash_cfi01_info);
-}
-
-type_init(pflash_cfi01_register_types)
-
-pflash_t *pflash_cfi01_register(hwaddr base,
-                                DeviceState *qdev, const char *name,
-                                hwaddr size,
-                                BlockDriverState *bs,
-                                uint32_t sector_len, int nb_blocs, int width,
-                                uint16_t id0, uint16_t id1,
-                                uint16_t id2, uint16_t id3, int be)
-{
-    DeviceState *dev = qdev_create(NULL, "cfi.pflash01");
-    SysBusDevice *busdev = SYS_BUS_DEVICE(dev);
-    pflash_t *pfl = (pflash_t *)object_dynamic_cast(OBJECT(dev),
-                                                    "cfi.pflash01");
-
-    if (bs && qdev_prop_set_drive(dev, "drive", bs)) {
-        abort();
-    }
-    qdev_prop_set_uint32(dev, "num-blocks", nb_blocs);
-    qdev_prop_set_uint64(dev, "sector-length", sector_len);
-    qdev_prop_set_uint8(dev, "width", width);
-    qdev_prop_set_uint8(dev, "big-endian", !!be);
-    qdev_prop_set_uint16(dev, "id0", id0);
-    qdev_prop_set_uint16(dev, "id1", id1);
-    qdev_prop_set_uint16(dev, "id2", id2);
-    qdev_prop_set_uint16(dev, "id3", id3);
-    qdev_prop_set_string(dev, "name", name);
-    qdev_init_nofail(dev);
-
-    sysbus_mmio_map(busdev, 0, base);
-    return pfl;
-}
-
-MemoryRegion *pflash_cfi01_get_memory(pflash_t *fl)
-{
-    return &fl->mem;
-}
diff --git a/hw/pflash_cfi02.c b/hw/pflash_cfi02.c
deleted file mode 100644 (file)
index 9a7fa70..0000000
+++ /dev/null
@@ -1,787 +0,0 @@
-/*
- *  CFI parallel flash with AMD command set emulation
- *
- *  Copyright (c) 2005 Jocelyn Mayer
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * For now, this code can emulate flashes of 1, 2 or 4 bytes width.
- * Supported commands/modes are:
- * - flash read
- * - flash write
- * - flash ID read
- * - sector erase
- * - chip erase
- * - unlock bypass command
- * - CFI queries
- *
- * It does not support flash interleaving.
- * It does not implement boot blocs with reduced size
- * It does not implement software data protection as found in many real chips
- * It does not implement erase suspend/resume commands
- * It does not implement multiple sectors erase
- */
-
-#include "hw/hw.h"
-#include "hw/block/flash.h"
-#include "qemu/timer.h"
-#include "block/block.h"
-#include "exec/address-spaces.h"
-#include "qemu/host-utils.h"
-#include "hw/sysbus.h"
-
-//#define PFLASH_DEBUG
-#ifdef PFLASH_DEBUG
-#define DPRINTF(fmt, ...)                                  \
-do {                                                       \
-    fprintf(stderr "PFLASH: " fmt , ## __VA_ARGS__);       \
-} while (0)
-#else
-#define DPRINTF(fmt, ...) do { } while (0)
-#endif
-
-#define PFLASH_LAZY_ROMD_THRESHOLD 42
-
-struct pflash_t {
-    SysBusDevice busdev;
-    BlockDriverState *bs;
-    uint32_t sector_len;
-    uint32_t nb_blocs;
-    uint32_t chip_len;
-    uint8_t mappings;
-    uint8_t width;
-    uint8_t be;
-    int wcycle; /* if 0, the flash is read normally */
-    int bypass;
-    int ro;
-    uint8_t cmd;
-    uint8_t status;
-    /* FIXME: implement array device properties */
-    uint16_t ident0;
-    uint16_t ident1;
-    uint16_t ident2;
-    uint16_t ident3;
-    uint16_t unlock_addr0;
-    uint16_t unlock_addr1;
-    uint8_t cfi_len;
-    uint8_t cfi_table[0x52];
-    QEMUTimer *timer;
-    /* The device replicates the flash memory across its memory space.  Emulate
-     * that by having a container (.mem) filled with an array of aliases
-     * (.mem_mappings) pointing to the flash memory (.orig_mem).
-     */
-    MemoryRegion mem;
-    MemoryRegion *mem_mappings;    /* array; one per mapping */
-    MemoryRegion orig_mem;
-    int rom_mode;
-    int read_counter; /* used for lazy switch-back to rom mode */
-    char *name;
-    void *storage;
-};
-
-/*
- * Set up replicated mappings of the same region.
- */
-static void pflash_setup_mappings(pflash_t *pfl)
-{
-    unsigned i;
-    hwaddr size = memory_region_size(&pfl->orig_mem);
-
-    memory_region_init(&pfl->mem, "pflash", pfl->mappings * size);
-    pfl->mem_mappings = g_new(MemoryRegion, pfl->mappings);
-    for (i = 0; i < pfl->mappings; ++i) {
-        memory_region_init_alias(&pfl->mem_mappings[i], "pflash-alias",
-                                 &pfl->orig_mem, 0, size);
-        memory_region_add_subregion(&pfl->mem, i * size, &pfl->mem_mappings[i]);
-    }
-}
-
-static void pflash_register_memory(pflash_t *pfl, int rom_mode)
-{
-    memory_region_rom_device_set_readable(&pfl->orig_mem, rom_mode);
-    pfl->rom_mode = rom_mode;
-}
-
-static void pflash_timer (void *opaque)
-{
-    pflash_t *pfl = opaque;
-
-    DPRINTF("%s: command %02x done\n", __func__, pfl->cmd);
-    /* Reset flash */
-    pfl->status ^= 0x80;
-    if (pfl->bypass) {
-        pfl->wcycle = 2;
-    } else {
-        pflash_register_memory(pfl, 1);
-        pfl->wcycle = 0;
-    }
-    pfl->cmd = 0;
-}
-
-static uint32_t pflash_read (pflash_t *pfl, hwaddr offset,
-                             int width, int be)
-{
-    hwaddr boff;
-    uint32_t ret;
-    uint8_t *p;
-
-    DPRINTF("%s: offset " TARGET_FMT_plx "\n", __func__, offset);
-    ret = -1;
-    /* Lazy reset to ROMD mode after a certain amount of read accesses */
-    if (!pfl->rom_mode && pfl->wcycle == 0 &&
-        ++pfl->read_counter > PFLASH_LAZY_ROMD_THRESHOLD) {
-        pflash_register_memory(pfl, 1);
-    }
-    offset &= pfl->chip_len - 1;
-    boff = offset & 0xFF;
-    if (pfl->width == 2)
-        boff = boff >> 1;
-    else if (pfl->width == 4)
-        boff = boff >> 2;
-    switch (pfl->cmd) {
-    default:
-        /* This should never happen : reset state & treat it as a read*/
-        DPRINTF("%s: unknown command state: %x\n", __func__, pfl->cmd);
-        pfl->wcycle = 0;
-        pfl->cmd = 0;
-        /* fall through to the read code */
-    case 0x80:
-        /* We accept reads during second unlock sequence... */
-    case 0x00:
-    flash_read:
-        /* Flash area read */
-        p = pfl->storage;
-        switch (width) {
-        case 1:
-            ret = p[offset];
-//            DPRINTF("%s: data offset %08x %02x\n", __func__, offset, ret);
-            break;
-        case 2:
-            if (be) {
-                ret = p[offset] << 8;
-                ret |= p[offset + 1];
-            } else {
-                ret = p[offset];
-                ret |= p[offset + 1] << 8;
-            }
-//            DPRINTF("%s: data offset %08x %04x\n", __func__, offset, ret);
-            break;
-        case 4:
-            if (be) {
-                ret = p[offset] << 24;
-                ret |= p[offset + 1] << 16;
-                ret |= p[offset + 2] << 8;
-                ret |= p[offset + 3];
-            } else {
-                ret = p[offset];
-                ret |= p[offset + 1] << 8;
-                ret |= p[offset + 2] << 16;
-                ret |= p[offset + 3] << 24;
-            }
-//            DPRINTF("%s: data offset %08x %08x\n", __func__, offset, ret);
-            break;
-        }
-        break;
-    case 0x90:
-        /* flash ID read */
-        switch (boff) {
-        case 0x00:
-        case 0x01:
-            ret = boff & 0x01 ? pfl->ident1 : pfl->ident0;
-            break;
-        case 0x02:
-            ret = 0x00; /* Pretend all sectors are unprotected */
-            break;
-        case 0x0E:
-        case 0x0F:
-            ret = boff & 0x01 ? pfl->ident3 : pfl->ident2;
-            if (ret == (uint8_t)-1) {
-                goto flash_read;
-            }
-            break;
-        default:
-            goto flash_read;
-        }
-        DPRINTF("%s: ID " TARGET_FMT_plx " %x\n", __func__, boff, ret);
-        break;
-    case 0xA0:
-    case 0x10:
-    case 0x30:
-        /* Status register read */
-        ret = pfl->status;
-        DPRINTF("%s: status %x\n", __func__, ret);
-        /* Toggle bit 6 */
-        pfl->status ^= 0x40;
-        break;
-    case 0x98:
-        /* CFI query mode */
-        if (boff > pfl->cfi_len)
-            ret = 0;
-        else
-            ret = pfl->cfi_table[boff];
-        break;
-    }
-
-    return ret;
-}
-
-/* update flash content on disk */
-static void pflash_update(pflash_t *pfl, int offset,
-                          int size)
-{
-    int offset_end;
-    if (pfl->bs) {
-        offset_end = offset + size;
-        /* round to sectors */
-        offset = offset >> 9;
-        offset_end = (offset_end + 511) >> 9;
-        bdrv_write(pfl->bs, offset, pfl->storage + (offset << 9),
-                   offset_end - offset);
-    }
-}
-
-static void pflash_write (pflash_t *pfl, hwaddr offset,
-                          uint32_t value, int width, int be)
-{
-    hwaddr boff;
-    uint8_t *p;
-    uint8_t cmd;
-
-    cmd = value;
-    if (pfl->cmd != 0xA0 && cmd == 0xF0) {
-#if 0
-        DPRINTF("%s: flash reset asked (%02x %02x)\n",
-                __func__, pfl->cmd, cmd);
-#endif
-        goto reset_flash;
-    }
-    DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d %d\n", __func__,
-            offset, value, width, pfl->wcycle);
-    offset &= pfl->chip_len - 1;
-
-    DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d\n", __func__,
-            offset, value, width);
-    boff = offset & (pfl->sector_len - 1);
-    if (pfl->width == 2)
-        boff = boff >> 1;
-    else if (pfl->width == 4)
-        boff = boff >> 2;
-    switch (pfl->wcycle) {
-    case 0:
-        /* Set the device in I/O access mode if required */
-        if (pfl->rom_mode)
-            pflash_register_memory(pfl, 0);
-        pfl->read_counter = 0;
-        /* We're in read mode */
-    check_unlock0:
-        if (boff == 0x55 && cmd == 0x98) {
-        enter_CFI_mode:
-            /* Enter CFI query mode */
-            pfl->wcycle = 7;
-            pfl->cmd = 0x98;
-            return;
-        }
-        if (boff != pfl->unlock_addr0 || cmd != 0xAA) {
-            DPRINTF("%s: unlock0 failed " TARGET_FMT_plx " %02x %04x\n",
-                    __func__, boff, cmd, pfl->unlock_addr0);
-            goto reset_flash;
-        }
-        DPRINTF("%s: unlock sequence started\n", __func__);
-        break;
-    case 1:
-        /* We started an unlock sequence */
-    check_unlock1:
-        if (boff != pfl->unlock_addr1 || cmd != 0x55) {
-            DPRINTF("%s: unlock1 failed " TARGET_FMT_plx " %02x\n", __func__,
-                    boff, cmd);
-            goto reset_flash;
-        }
-        DPRINTF("%s: unlock sequence done\n", __func__);
-        break;
-    case 2:
-        /* We finished an unlock sequence */
-        if (!pfl->bypass && boff != pfl->unlock_addr0) {
-            DPRINTF("%s: command failed " TARGET_FMT_plx " %02x\n", __func__,
-                    boff, cmd);
-            goto reset_flash;
-        }
-        switch (cmd) {
-        case 0x20:
-            pfl->bypass = 1;
-            goto do_bypass;
-        case 0x80:
-        case 0x90:
-        case 0xA0:
-            pfl->cmd = cmd;
-            DPRINTF("%s: starting command %02x\n", __func__, cmd);
-            break;
-        default:
-            DPRINTF("%s: unknown command %02x\n", __func__, cmd);
-            goto reset_flash;
-        }
-        break;
-    case 3:
-        switch (pfl->cmd) {
-        case 0x80:
-            /* We need another unlock sequence */
-            goto check_unlock0;
-        case 0xA0:
-            DPRINTF("%s: write data offset " TARGET_FMT_plx " %08x %d\n",
-                    __func__, offset, value, width);
-            p = pfl->storage;
-            if (!pfl->ro) {
-                switch (width) {
-                case 1:
-                    p[offset] &= value;
-                    pflash_update(pfl, offset, 1);
-                    break;
-                case 2:
-                    if (be) {
-                        p[offset] &= value >> 8;
-                        p[offset + 1] &= value;
-                    } else {
-                        p[offset] &= value;
-                        p[offset + 1] &= value >> 8;
-                    }
-                    pflash_update(pfl, offset, 2);
-                    break;
-                case 4:
-                    if (be) {
-                        p[offset] &= value >> 24;
-                        p[offset + 1] &= value >> 16;
-                        p[offset + 2] &= value >> 8;
-                        p[offset + 3] &= value;
-                    } else {
-                        p[offset] &= value;
-                        p[offset + 1] &= value >> 8;
-                        p[offset + 2] &= value >> 16;
-                        p[offset + 3] &= value >> 24;
-                    }
-                    pflash_update(pfl, offset, 4);
-                    break;
-                }
-            }
-            pfl->status = 0x00 | ~(value & 0x80);
-            /* Let's pretend write is immediate */
-            if (pfl->bypass)
-                goto do_bypass;
-            goto reset_flash;
-        case 0x90:
-            if (pfl->bypass && cmd == 0x00) {
-                /* Unlock bypass reset */
-                goto reset_flash;
-            }
-            /* We can enter CFI query mode from autoselect mode */
-            if (boff == 0x55 && cmd == 0x98)
-                goto enter_CFI_mode;
-            /* No break here */
-        default:
-            DPRINTF("%s: invalid write for command %02x\n",
-                    __func__, pfl->cmd);
-            goto reset_flash;
-        }
-    case 4:
-        switch (pfl->cmd) {
-        case 0xA0:
-            /* Ignore writes while flash data write is occurring */
-            /* As we suppose write is immediate, this should never happen */
-            return;
-        case 0x80:
-            goto check_unlock1;
-        default:
-            /* Should never happen */
-            DPRINTF("%s: invalid command state %02x (wc 4)\n",
-                    __func__, pfl->cmd);
-            goto reset_flash;
-        }
-        break;
-    case 5:
-        switch (cmd) {
-        case 0x10:
-            if (boff != pfl->unlock_addr0) {
-                DPRINTF("%s: chip erase: invalid address " TARGET_FMT_plx "\n",
-                        __func__, offset);
-                goto reset_flash;
-            }
-            /* Chip erase */
-            DPRINTF("%s: start chip erase\n", __func__);
-            if (!pfl->ro) {
-                memset(pfl->storage, 0xFF, pfl->chip_len);
-                pflash_update(pfl, 0, pfl->chip_len);
-            }
-            pfl->status = 0x00;
-            /* Let's wait 5 seconds before chip erase is done */
-            qemu_mod_timer(pfl->timer,
-                           qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() * 5));
-            break;
-        case 0x30:
-            /* Sector erase */
-            p = pfl->storage;
-            offset &= ~(pfl->sector_len - 1);
-            DPRINTF("%s: start sector erase at " TARGET_FMT_plx "\n", __func__,
-                    offset);
-            if (!pfl->ro) {
-                memset(p + offset, 0xFF, pfl->sector_len);
-                pflash_update(pfl, offset, pfl->sector_len);
-            }
-            pfl->status = 0x00;
-            /* Let's wait 1/2 second before sector erase is done */
-            qemu_mod_timer(pfl->timer,
-                           qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() / 2));
-            break;
-        default:
-            DPRINTF("%s: invalid command %02x (wc 5)\n", __func__, cmd);
-            goto reset_flash;
-        }
-        pfl->cmd = cmd;
-        break;
-    case 6:
-        switch (pfl->cmd) {
-        case 0x10:
-            /* Ignore writes during chip erase */
-            return;
-        case 0x30:
-            /* Ignore writes during sector erase */
-            return;
-        default:
-            /* Should never happen */
-            DPRINTF("%s: invalid command state %02x (wc 6)\n",
-                    __func__, pfl->cmd);
-            goto reset_flash;
-        }
-        break;
-    case 7: /* Special value for CFI queries */
-        DPRINTF("%s: invalid write in CFI query mode\n", __func__);
-        goto reset_flash;
-    default:
-        /* Should never happen */
-        DPRINTF("%s: invalid write state (wc 7)\n",  __func__);
-        goto reset_flash;
-    }
-    pfl->wcycle++;
-
-    return;
-
-    /* Reset flash */
- reset_flash:
-    pfl->bypass = 0;
-    pfl->wcycle = 0;
-    pfl->cmd = 0;
-    return;
-
- do_bypass:
-    pfl->wcycle = 2;
-    pfl->cmd = 0;
-}
-
-
-static uint32_t pflash_readb_be(void *opaque, hwaddr addr)
-{
-    return pflash_read(opaque, addr, 1, 1);
-}
-
-static uint32_t pflash_readb_le(void *opaque, hwaddr addr)
-{
-    return pflash_read(opaque, addr, 1, 0);
-}
-
-static uint32_t pflash_readw_be(void *opaque, hwaddr addr)
-{
-    pflash_t *pfl = opaque;
-
-    return pflash_read(pfl, addr, 2, 1);
-}
-
-static uint32_t pflash_readw_le(void *opaque, hwaddr addr)
-{
-    pflash_t *pfl = opaque;
-
-    return pflash_read(pfl, addr, 2, 0);
-}
-
-static uint32_t pflash_readl_be(void *opaque, hwaddr addr)
-{
-    pflash_t *pfl = opaque;
-
-    return pflash_read(pfl, addr, 4, 1);
-}
-
-static uint32_t pflash_readl_le(void *opaque, hwaddr addr)
-{
-    pflash_t *pfl = opaque;
-
-    return pflash_read(pfl, addr, 4, 0);
-}
-
-static void pflash_writeb_be(void *opaque, hwaddr addr,
-                             uint32_t value)
-{
-    pflash_write(opaque, addr, value, 1, 1);
-}
-
-static void pflash_writeb_le(void *opaque, hwaddr addr,
-                             uint32_t value)
-{
-    pflash_write(opaque, addr, value, 1, 0);
-}
-
-static void pflash_writew_be(void *opaque, hwaddr addr,
-                             uint32_t value)
-{
-    pflash_t *pfl = opaque;
-
-    pflash_write(pfl, addr, value, 2, 1);
-}
-
-static void pflash_writew_le(void *opaque, hwaddr addr,
-                             uint32_t value)
-{
-    pflash_t *pfl = opaque;
-
-    pflash_write(pfl, addr, value, 2, 0);
-}
-
-static void pflash_writel_be(void *opaque, hwaddr addr,
-                             uint32_t value)
-{
-    pflash_t *pfl = opaque;
-
-    pflash_write(pfl, addr, value, 4, 1);
-}
-
-static void pflash_writel_le(void *opaque, hwaddr addr,
-                             uint32_t value)
-{
-    pflash_t *pfl = opaque;
-
-    pflash_write(pfl, addr, value, 4, 0);
-}
-
-static const MemoryRegionOps pflash_cfi02_ops_be = {
-    .old_mmio = {
-        .read = { pflash_readb_be, pflash_readw_be, pflash_readl_be, },
-        .write = { pflash_writeb_be, pflash_writew_be, pflash_writel_be, },
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static const MemoryRegionOps pflash_cfi02_ops_le = {
-    .old_mmio = {
-        .read = { pflash_readb_le, pflash_readw_le, pflash_readl_le, },
-        .write = { pflash_writeb_le, pflash_writew_le, pflash_writel_le, },
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int pflash_cfi02_init(SysBusDevice *dev)
-{
-    pflash_t *pfl = FROM_SYSBUS(typeof(*pfl), dev);
-    uint32_t chip_len;
-    int ret;
-
-    chip_len = pfl->sector_len * pfl->nb_blocs;
-    /* XXX: to be fixed */
-#if 0
-    if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) &&
-        total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024))
-        return NULL;
-#endif
-
-    memory_region_init_rom_device(&pfl->orig_mem, pfl->be ?
-                                  &pflash_cfi02_ops_be : &pflash_cfi02_ops_le,
-                                  pfl, pfl->name, chip_len);
-    vmstate_register_ram(&pfl->orig_mem, DEVICE(pfl));
-    pfl->storage = memory_region_get_ram_ptr(&pfl->orig_mem);
-    pfl->chip_len = chip_len;
-    if (pfl->bs) {
-        /* read the initial flash content */
-        ret = bdrv_read(pfl->bs, 0, pfl->storage, chip_len >> 9);
-        if (ret < 0) {
-            g_free(pfl);
-            return 1;
-        }
-    }
-
-    pflash_setup_mappings(pfl);
-    pfl->rom_mode = 1;
-    sysbus_init_mmio(dev, &pfl->mem);
-
-    if (pfl->bs) {
-        pfl->ro = bdrv_is_read_only(pfl->bs);
-    } else {
-        pfl->ro = 0;
-    }
-
-    pfl->timer = qemu_new_timer_ns(vm_clock, pflash_timer, pfl);
-    pfl->wcycle = 0;
-    pfl->cmd = 0;
-    pfl->status = 0;
-    /* Hardcoded CFI table (mostly from SG29 Spansion flash) */
-    pfl->cfi_len = 0x52;
-    /* Standard "QRY" string */
-    pfl->cfi_table[0x10] = 'Q';
-    pfl->cfi_table[0x11] = 'R';
-    pfl->cfi_table[0x12] = 'Y';
-    /* Command set (AMD/Fujitsu) */
-    pfl->cfi_table[0x13] = 0x02;
-    pfl->cfi_table[0x14] = 0x00;
-    /* Primary extended table address */
-    pfl->cfi_table[0x15] = 0x31;
-    pfl->cfi_table[0x16] = 0x00;
-    /* Alternate command set (none) */
-    pfl->cfi_table[0x17] = 0x00;
-    pfl->cfi_table[0x18] = 0x00;
-    /* Alternate extended table (none) */
-    pfl->cfi_table[0x19] = 0x00;
-    pfl->cfi_table[0x1A] = 0x00;
-    /* Vcc min */
-    pfl->cfi_table[0x1B] = 0x27;
-    /* Vcc max */
-    pfl->cfi_table[0x1C] = 0x36;
-    /* Vpp min (no Vpp pin) */
-    pfl->cfi_table[0x1D] = 0x00;
-    /* Vpp max (no Vpp pin) */
-    pfl->cfi_table[0x1E] = 0x00;
-    /* Reserved */
-    pfl->cfi_table[0x1F] = 0x07;
-    /* Timeout for min size buffer write (NA) */
-    pfl->cfi_table[0x20] = 0x00;
-    /* Typical timeout for block erase (512 ms) */
-    pfl->cfi_table[0x21] = 0x09;
-    /* Typical timeout for full chip erase (4096 ms) */
-    pfl->cfi_table[0x22] = 0x0C;
-    /* Reserved */
-    pfl->cfi_table[0x23] = 0x01;
-    /* Max timeout for buffer write (NA) */
-    pfl->cfi_table[0x24] = 0x00;
-    /* Max timeout for block erase */
-    pfl->cfi_table[0x25] = 0x0A;
-    /* Max timeout for chip erase */
-    pfl->cfi_table[0x26] = 0x0D;
-    /* Device size */
-    pfl->cfi_table[0x27] = ctz32(chip_len);
-    /* Flash device interface (8 & 16 bits) */
-    pfl->cfi_table[0x28] = 0x02;
-    pfl->cfi_table[0x29] = 0x00;
-    /* Max number of bytes in multi-bytes write */
-    /* XXX: disable buffered write as it's not supported */
-    //    pfl->cfi_table[0x2A] = 0x05;
-    pfl->cfi_table[0x2A] = 0x00;
-    pfl->cfi_table[0x2B] = 0x00;
-    /* Number of erase block regions (uniform) */
-    pfl->cfi_table[0x2C] = 0x01;
-    /* Erase block region 1 */
-    pfl->cfi_table[0x2D] = pfl->nb_blocs - 1;
-    pfl->cfi_table[0x2E] = (pfl->nb_blocs - 1) >> 8;
-    pfl->cfi_table[0x2F] = pfl->sector_len >> 8;
-    pfl->cfi_table[0x30] = pfl->sector_len >> 16;
-
-    /* Extended */
-    pfl->cfi_table[0x31] = 'P';
-    pfl->cfi_table[0x32] = 'R';
-    pfl->cfi_table[0x33] = 'I';
-
-    pfl->cfi_table[0x34] = '1';
-    pfl->cfi_table[0x35] = '0';
-
-    pfl->cfi_table[0x36] = 0x00;
-    pfl->cfi_table[0x37] = 0x00;
-    pfl->cfi_table[0x38] = 0x00;
-    pfl->cfi_table[0x39] = 0x00;
-
-    pfl->cfi_table[0x3a] = 0x00;
-
-    pfl->cfi_table[0x3b] = 0x00;
-    pfl->cfi_table[0x3c] = 0x00;
-
-    return 0;
-}
-
-static Property pflash_cfi02_properties[] = {
-    DEFINE_PROP_DRIVE("drive", struct pflash_t, bs),
-    DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0),
-    DEFINE_PROP_UINT32("sector-length", struct pflash_t, sector_len, 0),
-    DEFINE_PROP_UINT8("width", struct pflash_t, width, 0),
-    DEFINE_PROP_UINT8("mappings", struct pflash_t, mappings, 0),
-    DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0),
-    DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0),
-    DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0),
-    DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0),
-    DEFINE_PROP_UINT16("id3", struct pflash_t, ident3, 0),
-    DEFINE_PROP_UINT16("unlock-addr0", struct pflash_t, unlock_addr0, 0),
-    DEFINE_PROP_UINT16("unlock-addr1", struct pflash_t, unlock_addr1, 0),
-    DEFINE_PROP_STRING("name", struct pflash_t, name),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void pflash_cfi02_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = pflash_cfi02_init;
-    dc->props = pflash_cfi02_properties;
-}
-
-static const TypeInfo pflash_cfi02_info = {
-    .name           = "cfi.pflash02",
-    .parent         = TYPE_SYS_BUS_DEVICE,
-    .instance_size  = sizeof(struct pflash_t),
-    .class_init     = pflash_cfi02_class_init,
-};
-
-static void pflash_cfi02_register_types(void)
-{
-    type_register_static(&pflash_cfi02_info);
-}
-
-type_init(pflash_cfi02_register_types)
-
-pflash_t *pflash_cfi02_register(hwaddr base,
-                                DeviceState *qdev, const char *name,
-                                hwaddr size,
-                                BlockDriverState *bs, uint32_t sector_len,
-                                int nb_blocs, int nb_mappings, int width,
-                                uint16_t id0, uint16_t id1,
-                                uint16_t id2, uint16_t id3,
-                                uint16_t unlock_addr0, uint16_t unlock_addr1,
-                                int be)
-{
-    DeviceState *dev = qdev_create(NULL, "cfi.pflash02");
-    SysBusDevice *busdev = SYS_BUS_DEVICE(dev);
-    pflash_t *pfl = (pflash_t *)object_dynamic_cast(OBJECT(dev),
-                                                    "cfi.pflash02");
-
-    if (bs && qdev_prop_set_drive(dev, "drive", bs)) {
-        abort();
-    }
-    qdev_prop_set_uint32(dev, "num-blocks", nb_blocs);
-    qdev_prop_set_uint32(dev, "sector-length", sector_len);
-    qdev_prop_set_uint8(dev, "width", width);
-    qdev_prop_set_uint8(dev, "mappings", nb_mappings);
-    qdev_prop_set_uint8(dev, "big-endian", !!be);
-    qdev_prop_set_uint16(dev, "id0", id0);
-    qdev_prop_set_uint16(dev, "id1", id1);
-    qdev_prop_set_uint16(dev, "id2", id2);
-    qdev_prop_set_uint16(dev, "id3", id3);
-    qdev_prop_set_uint16(dev, "unlock-addr0", unlock_addr0);
-    qdev_prop_set_uint16(dev, "unlock-addr1", unlock_addr1);
-    qdev_prop_set_string(dev, "name", name);
-    qdev_init_nofail(dev);
-
-    sysbus_mmio_map(busdev, 0, base);
-    return pfl;
-}
diff --git a/hw/piix4.c b/hw/piix4.c
deleted file mode 100644 (file)
index d750413..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * QEMU PIIX4 PCI Bridge Emulation
- *
- * Copyright (c) 2006 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/hw.h"
-#include "hw/i386/pc.h"
-#include "hw/pci/pci.h"
-#include "hw/isa/isa.h"
-#include "hw/sysbus.h"
-
-PCIDevice *piix4_dev;
-
-typedef struct PIIX4State {
-    PCIDevice dev;
-} PIIX4State;
-
-static void piix4_reset(void *opaque)
-{
-    PIIX4State *d = opaque;
-    uint8_t *pci_conf = d->dev.config;
-
-    pci_conf[0x04] = 0x07; // master, memory and I/O
-    pci_conf[0x05] = 0x00;
-    pci_conf[0x06] = 0x00;
-    pci_conf[0x07] = 0x02; // PCI_status_devsel_medium
-    pci_conf[0x4c] = 0x4d;
-    pci_conf[0x4e] = 0x03;
-    pci_conf[0x4f] = 0x00;
-    pci_conf[0x60] = 0x0a; // PCI A -> IRQ 10
-    pci_conf[0x61] = 0x0a; // PCI B -> IRQ 10
-    pci_conf[0x62] = 0x0b; // PCI C -> IRQ 11
-    pci_conf[0x63] = 0x0b; // PCI D -> IRQ 11
-    pci_conf[0x69] = 0x02;
-    pci_conf[0x70] = 0x80;
-    pci_conf[0x76] = 0x0c;
-    pci_conf[0x77] = 0x0c;
-    pci_conf[0x78] = 0x02;
-    pci_conf[0x79] = 0x00;
-    pci_conf[0x80] = 0x00;
-    pci_conf[0x82] = 0x00;
-    pci_conf[0xa0] = 0x08;
-    pci_conf[0xa2] = 0x00;
-    pci_conf[0xa3] = 0x00;
-    pci_conf[0xa4] = 0x00;
-    pci_conf[0xa5] = 0x00;
-    pci_conf[0xa6] = 0x00;
-    pci_conf[0xa7] = 0x00;
-    pci_conf[0xa8] = 0x0f;
-    pci_conf[0xaa] = 0x00;
-    pci_conf[0xab] = 0x00;
-    pci_conf[0xac] = 0x00;
-    pci_conf[0xae] = 0x00;
-}
-
-static const VMStateDescription vmstate_piix4 = {
-    .name = "PIIX4",
-    .version_id = 2,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .fields      = (VMStateField[]) {
-        VMSTATE_PCI_DEVICE(dev, PIIX4State),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int piix4_initfn(PCIDevice *dev)
-{
-    PIIX4State *d = DO_UPCAST(PIIX4State, dev, dev);
-
-    isa_bus_new(&d->dev.qdev, pci_address_space_io(dev));
-    piix4_dev = &d->dev;
-    qemu_register_reset(piix4_reset, d);
-    return 0;
-}
-
-int piix4_init(PCIBus *bus, ISABus **isa_bus, int devfn)
-{
-    PCIDevice *d;
-
-    d = pci_create_simple_multifunction(bus, devfn, true, "PIIX4");
-    *isa_bus = DO_UPCAST(ISABus, qbus, qdev_get_child_bus(&d->qdev, "isa.0"));
-    return d->devfn;
-}
-
-static void piix4_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->no_hotplug = 1;
-    k->init = piix4_initfn;
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->device_id = PCI_DEVICE_ID_INTEL_82371AB_0;
-    k->class_id = PCI_CLASS_BRIDGE_ISA;
-    dc->desc = "ISA bridge";
-    dc->no_user = 1;
-    dc->vmsd = &vmstate_piix4;
-}
-
-static const TypeInfo piix4_info = {
-    .name          = "PIIX4",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PIIX4State),
-    .class_init    = piix4_class_init,
-};
-
-static void piix4_register_types(void)
-{
-    type_register_static(&piix4_info);
-}
-
-type_init(piix4_register_types)
diff --git a/hw/pl011.c b/hw/pl011.c
deleted file mode 100644 (file)
index 332d5b9..0000000
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * Arm PrimeCell PL011 UART
- *
- * Copyright (c) 2006 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the GPL.
- */
-
-#include "hw/sysbus.h"
-#include "char/char.h"
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    uint32_t readbuff;
-    uint32_t flags;
-    uint32_t lcr;
-    uint32_t cr;
-    uint32_t dmacr;
-    uint32_t int_enabled;
-    uint32_t int_level;
-    uint32_t read_fifo[16];
-    uint32_t ilpr;
-    uint32_t ibrd;
-    uint32_t fbrd;
-    uint32_t ifl;
-    int read_pos;
-    int read_count;
-    int read_trigger;
-    CharDriverState *chr;
-    qemu_irq irq;
-    const unsigned char *id;
-} pl011_state;
-
-#define PL011_INT_TX 0x20
-#define PL011_INT_RX 0x10
-
-#define PL011_FLAG_TXFE 0x80
-#define PL011_FLAG_RXFF 0x40
-#define PL011_FLAG_TXFF 0x20
-#define PL011_FLAG_RXFE 0x10
-
-static const unsigned char pl011_id_arm[8] =
-  { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
-static const unsigned char pl011_id_luminary[8] =
-  { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
-
-static void pl011_update(pl011_state *s)
-{
-    uint32_t flags;
-
-    flags = s->int_level & s->int_enabled;
-    qemu_set_irq(s->irq, flags != 0);
-}
-
-static uint64_t pl011_read(void *opaque, hwaddr offset,
-                           unsigned size)
-{
-    pl011_state *s = (pl011_state *)opaque;
-    uint32_t c;
-
-    if (offset >= 0xfe0 && offset < 0x1000) {
-        return s->id[(offset - 0xfe0) >> 2];
-    }
-    switch (offset >> 2) {
-    case 0: /* UARTDR */
-        s->flags &= ~PL011_FLAG_RXFF;
-        c = s->read_fifo[s->read_pos];
-        if (s->read_count > 0) {
-            s->read_count--;
-            if (++s->read_pos == 16)
-                s->read_pos = 0;
-        }
-        if (s->read_count == 0) {
-            s->flags |= PL011_FLAG_RXFE;
-        }
-        if (s->read_count == s->read_trigger - 1)
-            s->int_level &= ~ PL011_INT_RX;
-        pl011_update(s);
-        if (s->chr) {
-            qemu_chr_accept_input(s->chr);
-        }
-        return c;
-    case 1: /* UARTCR */
-        return 0;
-    case 6: /* UARTFR */
-        return s->flags;
-    case 8: /* UARTILPR */
-        return s->ilpr;
-    case 9: /* UARTIBRD */
-        return s->ibrd;
-    case 10: /* UARTFBRD */
-        return s->fbrd;
-    case 11: /* UARTLCR_H */
-        return s->lcr;
-    case 12: /* UARTCR */
-        return s->cr;
-    case 13: /* UARTIFLS */
-        return s->ifl;
-    case 14: /* UARTIMSC */
-        return s->int_enabled;
-    case 15: /* UARTRIS */
-        return s->int_level;
-    case 16: /* UARTMIS */
-        return s->int_level & s->int_enabled;
-    case 18: /* UARTDMACR */
-        return s->dmacr;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl011_read: Bad offset %x\n", (int)offset);
-        return 0;
-    }
-}
-
-static void pl011_set_read_trigger(pl011_state *s)
-{
-#if 0
-    /* The docs say the RX interrupt is triggered when the FIFO exceeds
-       the threshold.  However linux only reads the FIFO in response to an
-       interrupt.  Triggering the interrupt when the FIFO is non-empty seems
-       to make things work.  */
-    if (s->lcr & 0x10)
-        s->read_trigger = (s->ifl >> 1) & 0x1c;
-    else
-#endif
-        s->read_trigger = 1;
-}
-
-static void pl011_write(void *opaque, hwaddr offset,
-                        uint64_t value, unsigned size)
-{
-    pl011_state *s = (pl011_state *)opaque;
-    unsigned char ch;
-
-    switch (offset >> 2) {
-    case 0: /* UARTDR */
-        /* ??? Check if transmitter is enabled.  */
-        ch = value;
-        if (s->chr)
-            qemu_chr_fe_write(s->chr, &ch, 1);
-        s->int_level |= PL011_INT_TX;
-        pl011_update(s);
-        break;
-    case 1: /* UARTCR */
-        s->cr = value;
-        break;
-    case 6: /* UARTFR */
-        /* Writes to Flag register are ignored.  */
-        break;
-    case 8: /* UARTUARTILPR */
-        s->ilpr = value;
-        break;
-    case 9: /* UARTIBRD */
-        s->ibrd = value;
-        break;
-    case 10: /* UARTFBRD */
-        s->fbrd = value;
-        break;
-    case 11: /* UARTLCR_H */
-        s->lcr = value;
-        pl011_set_read_trigger(s);
-        break;
-    case 12: /* UARTCR */
-        /* ??? Need to implement the enable and loopback bits.  */
-        s->cr = value;
-        break;
-    case 13: /* UARTIFS */
-        s->ifl = value;
-        pl011_set_read_trigger(s);
-        break;
-    case 14: /* UARTIMSC */
-        s->int_enabled = value;
-        pl011_update(s);
-        break;
-    case 17: /* UARTICR */
-        s->int_level &= ~value;
-        pl011_update(s);
-        break;
-    case 18: /* UARTDMACR */
-        s->dmacr = value;
-        if (value & 3) {
-            qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n");
-        }
-        break;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl011_write: Bad offset %x\n", (int)offset);
-    }
-}
-
-static int pl011_can_receive(void *opaque)
-{
-    pl011_state *s = (pl011_state *)opaque;
-
-    if (s->lcr & 0x10)
-        return s->read_count < 16;
-    else
-        return s->read_count < 1;
-}
-
-static void pl011_put_fifo(void *opaque, uint32_t value)
-{
-    pl011_state *s = (pl011_state *)opaque;
-    int slot;
-
-    slot = s->read_pos + s->read_count;
-    if (slot >= 16)
-        slot -= 16;
-    s->read_fifo[slot] = value;
-    s->read_count++;
-    s->flags &= ~PL011_FLAG_RXFE;
-    if (s->cr & 0x10 || s->read_count == 16) {
-        s->flags |= PL011_FLAG_RXFF;
-    }
-    if (s->read_count == s->read_trigger) {
-        s->int_level |= PL011_INT_RX;
-        pl011_update(s);
-    }
-}
-
-static void pl011_receive(void *opaque, const uint8_t *buf, int size)
-{
-    pl011_put_fifo(opaque, *buf);
-}
-
-static void pl011_event(void *opaque, int event)
-{
-    if (event == CHR_EVENT_BREAK)
-        pl011_put_fifo(opaque, 0x400);
-}
-
-static const MemoryRegionOps pl011_ops = {
-    .read = pl011_read,
-    .write = pl011_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static const VMStateDescription vmstate_pl011 = {
-    .name = "pl011",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT32(readbuff, pl011_state),
-        VMSTATE_UINT32(flags, pl011_state),
-        VMSTATE_UINT32(lcr, pl011_state),
-        VMSTATE_UINT32(cr, pl011_state),
-        VMSTATE_UINT32(dmacr, pl011_state),
-        VMSTATE_UINT32(int_enabled, pl011_state),
-        VMSTATE_UINT32(int_level, pl011_state),
-        VMSTATE_UINT32_ARRAY(read_fifo, pl011_state, 16),
-        VMSTATE_UINT32(ilpr, pl011_state),
-        VMSTATE_UINT32(ibrd, pl011_state),
-        VMSTATE_UINT32(fbrd, pl011_state),
-        VMSTATE_UINT32(ifl, pl011_state),
-        VMSTATE_INT32(read_pos, pl011_state),
-        VMSTATE_INT32(read_count, pl011_state),
-        VMSTATE_INT32(read_trigger, pl011_state),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int pl011_init(SysBusDevice *dev, const unsigned char *id)
-{
-    pl011_state *s = FROM_SYSBUS(pl011_state, dev);
-
-    memory_region_init_io(&s->iomem, &pl011_ops, s, "pl011", 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-    sysbus_init_irq(dev, &s->irq);
-    s->id = id;
-    s->chr = qemu_char_get_next_serial();
-
-    s->read_trigger = 1;
-    s->ifl = 0x12;
-    s->cr = 0x300;
-    s->flags = 0x90;
-    if (s->chr) {
-        qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive,
-                              pl011_event, s);
-    }
-    vmstate_register(&dev->qdev, -1, &vmstate_pl011, s);
-    return 0;
-}
-
-static int pl011_arm_init(SysBusDevice *dev)
-{
-    return pl011_init(dev, pl011_id_arm);
-}
-
-static int pl011_luminary_init(SysBusDevice *dev)
-{
-    return pl011_init(dev, pl011_id_luminary);
-}
-
-static void pl011_arm_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sdc->init = pl011_arm_init;
-}
-
-static const TypeInfo pl011_arm_info = {
-    .name          = "pl011",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(pl011_state),
-    .class_init    = pl011_arm_class_init,
-};
-
-static void pl011_luminary_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sdc->init = pl011_luminary_init;
-}
-
-static const TypeInfo pl011_luminary_info = {
-    .name          = "pl011_luminary",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(pl011_state),
-    .class_init    = pl011_luminary_class_init,
-};
-
-static void pl011_register_types(void)
-{
-    type_register_static(&pl011_arm_info);
-    type_register_static(&pl011_luminary_info);
-}
-
-type_init(pl011_register_types)
diff --git a/hw/pl022.c b/hw/pl022.c
deleted file mode 100644 (file)
index 536c216..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Arm PrimeCell PL022 Synchronous Serial Port
- *
- * Copyright (c) 2007 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the GPL.
- */
-
-#include "hw/sysbus.h"
-#include "hw/ssi.h"
-
-//#define DEBUG_PL022 1
-
-#ifdef DEBUG_PL022
-#define DPRINTF(fmt, ...) \
-do { printf("pl022: " fmt , ## __VA_ARGS__); } while (0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__);} while (0)
-#endif
-
-#define PL022_CR1_LBM 0x01
-#define PL022_CR1_SSE 0x02
-#define PL022_CR1_MS  0x04
-#define PL022_CR1_SDO 0x08
-
-#define PL022_SR_TFE  0x01
-#define PL022_SR_TNF  0x02
-#define PL022_SR_RNE  0x04
-#define PL022_SR_RFF  0x08
-#define PL022_SR_BSY  0x10
-
-#define PL022_INT_ROR 0x01
-#define PL022_INT_RT  0x04
-#define PL022_INT_RX  0x04
-#define PL022_INT_TX  0x08
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    uint32_t cr0;
-    uint32_t cr1;
-    uint32_t bitmask;
-    uint32_t sr;
-    uint32_t cpsr;
-    uint32_t is;
-    uint32_t im;
-    /* The FIFO head points to the next empty entry.  */
-    int tx_fifo_head;
-    int rx_fifo_head;
-    int tx_fifo_len;
-    int rx_fifo_len;
-    uint16_t tx_fifo[8];
-    uint16_t rx_fifo[8];
-    qemu_irq irq;
-    SSIBus *ssi;
-} pl022_state;
-
-static const unsigned char pl022_id[8] =
-  { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
-
-static void pl022_update(pl022_state *s)
-{
-    s->sr = 0;
-    if (s->tx_fifo_len == 0)
-        s->sr |= PL022_SR_TFE;
-    if (s->tx_fifo_len != 8)
-        s->sr |= PL022_SR_TNF;
-    if (s->rx_fifo_len != 0)
-        s->sr |= PL022_SR_RNE;
-    if (s->rx_fifo_len == 8)
-        s->sr |= PL022_SR_RFF;
-    if (s->tx_fifo_len)
-        s->sr |= PL022_SR_BSY;
-    s->is = 0;
-    if (s->rx_fifo_len >= 4)
-        s->is |= PL022_INT_RX;
-    if (s->tx_fifo_len <= 4)
-        s->is |= PL022_INT_TX;
-
-    qemu_set_irq(s->irq, (s->is & s->im) != 0);
-}
-
-static void pl022_xfer(pl022_state *s)
-{
-    int i;
-    int o;
-    int val;
-
-    if ((s->cr1 & PL022_CR1_SSE) == 0) {
-        pl022_update(s);
-        DPRINTF("Disabled\n");
-        return;
-    }
-
-    DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len);
-    i = (s->tx_fifo_head - s->tx_fifo_len) & 7;
-    o = s->rx_fifo_head;
-    /* ??? We do not emulate the line speed.
-       This may break some applications.  The are two problematic cases:
-        (a) A driver feeds data into the TX FIFO until it is full,
-         and only then drains the RX FIFO.  On real hardware the CPU can
-         feed data fast enough that the RX fifo never gets chance to overflow.
-        (b) A driver transmits data, deliberately allowing the RX FIFO to
-         overflow because it ignores the RX data anyway.
-
-       We choose to support (a) by stalling the transmit engine if it would
-       cause the RX FIFO to overflow.  In practice much transmit-only code
-       falls into (a) because it flushes the RX FIFO to determine when
-       the transfer has completed.  */
-    while (s->tx_fifo_len && s->rx_fifo_len < 8) {
-        DPRINTF("xfer\n");
-        val = s->tx_fifo[i];
-        if (s->cr1 & PL022_CR1_LBM) {
-            /* Loopback mode.  */
-        } else {
-            val = ssi_transfer(s->ssi, val);
-        }
-        s->rx_fifo[o] = val & s->bitmask;
-        i = (i + 1) & 7;
-        o = (o + 1) & 7;
-        s->tx_fifo_len--;
-        s->rx_fifo_len++;
-    }
-    s->rx_fifo_head = o;
-    pl022_update(s);
-}
-
-static uint64_t pl022_read(void *opaque, hwaddr offset,
-                           unsigned size)
-{
-    pl022_state *s = (pl022_state *)opaque;
-    int val;
-
-    if (offset >= 0xfe0 && offset < 0x1000) {
-        return pl022_id[(offset - 0xfe0) >> 2];
-    }
-    switch (offset) {
-    case 0x00: /* CR0 */
-      return s->cr0;
-    case 0x04: /* CR1 */
-      return s->cr1;
-    case 0x08: /* DR */
-        if (s->rx_fifo_len) {
-            val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7];
-            DPRINTF("RX %02x\n", val);
-            s->rx_fifo_len--;
-            pl022_xfer(s);
-        } else {
-            val = 0;
-        }
-        return val;
-    case 0x0c: /* SR */
-        return s->sr;
-    case 0x10: /* CPSR */
-        return s->cpsr;
-    case 0x14: /* IMSC */
-        return s->im;
-    case 0x18: /* RIS */
-        return s->is;
-    case 0x1c: /* MIS */
-        return s->im & s->is;
-    case 0x20: /* DMACR */
-        /* Not implemented.  */
-        return 0;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl022_read: Bad offset %x\n", (int)offset);
-        return 0;
-    }
-}
-
-static void pl022_write(void *opaque, hwaddr offset,
-                        uint64_t value, unsigned size)
-{
-    pl022_state *s = (pl022_state *)opaque;
-
-    switch (offset) {
-    case 0x00: /* CR0 */
-        s->cr0 = value;
-        /* Clock rate and format are ignored.  */
-        s->bitmask = (1 << ((value & 15) + 1)) - 1;
-        break;
-    case 0x04: /* CR1 */
-        s->cr1 = value;
-        if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
-                   == (PL022_CR1_MS | PL022_CR1_SSE)) {
-            BADF("SPI slave mode not implemented\n");
-        }
-        pl022_xfer(s);
-        break;
-    case 0x08: /* DR */
-        if (s->tx_fifo_len < 8) {
-            DPRINTF("TX %02x\n", (unsigned)value);
-            s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
-            s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
-            s->tx_fifo_len++;
-            pl022_xfer(s);
-        }
-        break;
-    case 0x10: /* CPSR */
-        /* Prescaler.  Ignored.  */
-        s->cpsr = value & 0xff;
-        break;
-    case 0x14: /* IMSC */
-        s->im = value;
-        pl022_update(s);
-        break;
-    case 0x20: /* DMACR */
-        if (value) {
-            qemu_log_mask(LOG_UNIMP, "pl022: DMA not implemented\n");
-        }
-        break;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl022_write: Bad offset %x\n", (int)offset);
-    }
-}
-
-static void pl022_reset(pl022_state *s)
-{
-    s->rx_fifo_len = 0;
-    s->tx_fifo_len = 0;
-    s->im = 0;
-    s->is = PL022_INT_TX;
-    s->sr = PL022_SR_TFE | PL022_SR_TNF;
-}
-
-static const MemoryRegionOps pl022_ops = {
-    .read = pl022_read,
-    .write = pl022_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static const VMStateDescription vmstate_pl022 = {
-    .name = "pl022_ssp",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT32(cr0, pl022_state),
-        VMSTATE_UINT32(cr1, pl022_state),
-        VMSTATE_UINT32(bitmask, pl022_state),
-        VMSTATE_UINT32(sr, pl022_state),
-        VMSTATE_UINT32(cpsr, pl022_state),
-        VMSTATE_UINT32(is, pl022_state),
-        VMSTATE_UINT32(im, pl022_state),
-        VMSTATE_INT32(tx_fifo_head, pl022_state),
-        VMSTATE_INT32(rx_fifo_head, pl022_state),
-        VMSTATE_INT32(tx_fifo_len, pl022_state),
-        VMSTATE_INT32(rx_fifo_len, pl022_state),
-        VMSTATE_UINT16(tx_fifo[0], pl022_state),
-        VMSTATE_UINT16(rx_fifo[0], pl022_state),
-        VMSTATE_UINT16(tx_fifo[1], pl022_state),
-        VMSTATE_UINT16(rx_fifo[1], pl022_state),
-        VMSTATE_UINT16(tx_fifo[2], pl022_state),
-        VMSTATE_UINT16(rx_fifo[2], pl022_state),
-        VMSTATE_UINT16(tx_fifo[3], pl022_state),
-        VMSTATE_UINT16(rx_fifo[3], pl022_state),
-        VMSTATE_UINT16(tx_fifo[4], pl022_state),
-        VMSTATE_UINT16(rx_fifo[4], pl022_state),
-        VMSTATE_UINT16(tx_fifo[5], pl022_state),
-        VMSTATE_UINT16(rx_fifo[5], pl022_state),
-        VMSTATE_UINT16(tx_fifo[6], pl022_state),
-        VMSTATE_UINT16(rx_fifo[6], pl022_state),
-        VMSTATE_UINT16(tx_fifo[7], pl022_state),
-        VMSTATE_UINT16(rx_fifo[7], pl022_state),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int pl022_init(SysBusDevice *dev)
-{
-    pl022_state *s = FROM_SYSBUS(pl022_state, dev);
-
-    memory_region_init_io(&s->iomem, &pl022_ops, s, "pl022", 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-    sysbus_init_irq(dev, &s->irq);
-    s->ssi = ssi_create_bus(&dev->qdev, "ssi");
-    pl022_reset(s);
-    vmstate_register(&dev->qdev, -1, &vmstate_pl022, s);
-    return 0;
-}
-
-static void pl022_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sdc->init = pl022_init;
-}
-
-static const TypeInfo pl022_info = {
-    .name          = "pl022",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(pl022_state),
-    .class_init    = pl022_class_init,
-};
-
-static void pl022_register_types(void)
-{
-    type_register_static(&pl022_info);
-}
-
-type_init(pl022_register_types)
diff --git a/hw/pl031.c b/hw/pl031.c
deleted file mode 100644 (file)
index 764940b..0000000
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * ARM AMBA PrimeCell PL031 RTC
- *
- * Copyright (c) 2007 CodeSourcery
- *
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "hw/sysbus.h"
-#include "qemu/timer.h"
-#include "sysemu/sysemu.h"
-
-//#define DEBUG_PL031
-
-#ifdef DEBUG_PL031
-#define DPRINTF(fmt, ...) \
-do { printf("pl031: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#endif
-
-#define RTC_DR      0x00    /* Data read register */
-#define RTC_MR      0x04    /* Match register */
-#define RTC_LR      0x08    /* Data load register */
-#define RTC_CR      0x0c    /* Control register */
-#define RTC_IMSC    0x10    /* Interrupt mask and set register */
-#define RTC_RIS     0x14    /* Raw interrupt status register */
-#define RTC_MIS     0x18    /* Masked interrupt status register */
-#define RTC_ICR     0x1c    /* Interrupt clear register */
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    QEMUTimer *timer;
-    qemu_irq irq;
-
-    /* Needed to preserve the tick_count across migration, even if the
-     * absolute value of the rtc_clock is different on the source and
-     * destination.
-     */
-    uint32_t tick_offset_vmstate;
-    uint32_t tick_offset;
-
-    uint32_t mr;
-    uint32_t lr;
-    uint32_t cr;
-    uint32_t im;
-    uint32_t is;
-} pl031_state;
-
-static const unsigned char pl031_id[] = {
-    0x31, 0x10, 0x14, 0x00,         /* Device ID        */
-    0x0d, 0xf0, 0x05, 0xb1          /* Cell ID      */
-};
-
-static void pl031_update(pl031_state *s)
-{
-    qemu_set_irq(s->irq, s->is & s->im);
-}
-
-static void pl031_interrupt(void * opaque)
-{
-    pl031_state *s = (pl031_state *)opaque;
-
-    s->is = 1;
-    DPRINTF("Alarm raised\n");
-    pl031_update(s);
-}
-
-static uint32_t pl031_get_count(pl031_state *s)
-{
-    int64_t now = qemu_get_clock_ns(rtc_clock);
-    return s->tick_offset + now / get_ticks_per_sec();
-}
-
-static void pl031_set_alarm(pl031_state *s)
-{
-    uint32_t ticks;
-
-    /* The timer wraps around.  This subtraction also wraps in the same way,
-       and gives correct results when alarm < now_ticks.  */
-    ticks = s->mr - pl031_get_count(s);
-    DPRINTF("Alarm set in %ud ticks\n", ticks);
-    if (ticks == 0) {
-        qemu_del_timer(s->timer);
-        pl031_interrupt(s);
-    } else {
-        int64_t now = qemu_get_clock_ns(rtc_clock);
-        qemu_mod_timer(s->timer, now + (int64_t)ticks * get_ticks_per_sec());
-    }
-}
-
-static uint64_t pl031_read(void *opaque, hwaddr offset,
-                           unsigned size)
-{
-    pl031_state *s = (pl031_state *)opaque;
-
-    if (offset >= 0xfe0  &&  offset < 0x1000)
-        return pl031_id[(offset - 0xfe0) >> 2];
-
-    switch (offset) {
-    case RTC_DR:
-        return pl031_get_count(s);
-    case RTC_MR:
-        return s->mr;
-    case RTC_IMSC:
-        return s->im;
-    case RTC_RIS:
-        return s->is;
-    case RTC_LR:
-        return s->lr;
-    case RTC_CR:
-        /* RTC is permanently enabled.  */
-        return 1;
-    case RTC_MIS:
-        return s->is & s->im;
-    case RTC_ICR:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl031: read of write-only register at offset 0x%x\n",
-                      (int)offset);
-        break;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl031_read: Bad offset 0x%x\n", (int)offset);
-        break;
-    }
-
-    return 0;
-}
-
-static void pl031_write(void * opaque, hwaddr offset,
-                        uint64_t value, unsigned size)
-{
-    pl031_state *s = (pl031_state *)opaque;
-
-
-    switch (offset) {
-    case RTC_LR:
-        s->tick_offset += value - pl031_get_count(s);
-        pl031_set_alarm(s);
-        break;
-    case RTC_MR:
-        s->mr = value;
-        pl031_set_alarm(s);
-        break;
-    case RTC_IMSC:
-        s->im = value & 1;
-        DPRINTF("Interrupt mask %d\n", s->im);
-        pl031_update(s);
-        break;
-    case RTC_ICR:
-        /* The PL031 documentation (DDI0224B) states that the interrupt is
-           cleared when bit 0 of the written value is set.  However the
-           arm926e documentation (DDI0287B) states that the interrupt is
-           cleared when any value is written.  */
-        DPRINTF("Interrupt cleared");
-        s->is = 0;
-        pl031_update(s);
-        break;
-    case RTC_CR:
-        /* Written value is ignored.  */
-        break;
-
-    case RTC_DR:
-    case RTC_MIS:
-    case RTC_RIS:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl031: write to read-only register at offset 0x%x\n",
-                      (int)offset);
-        break;
-
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl031_write: Bad offset 0x%x\n", (int)offset);
-        break;
-    }
-}
-
-static const MemoryRegionOps pl031_ops = {
-    .read = pl031_read,
-    .write = pl031_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int pl031_init(SysBusDevice *dev)
-{
-    pl031_state *s = FROM_SYSBUS(pl031_state, dev);
-    struct tm tm;
-
-    memory_region_init_io(&s->iomem, &pl031_ops, s, "pl031", 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-
-    sysbus_init_irq(dev, &s->irq);
-    qemu_get_timedate(&tm, 0);
-    s->tick_offset = mktimegm(&tm) - qemu_get_clock_ns(rtc_clock) / get_ticks_per_sec();
-
-    s->timer = qemu_new_timer_ns(rtc_clock, pl031_interrupt, s);
-    return 0;
-}
-
-static void pl031_pre_save(void *opaque)
-{
-    pl031_state *s = opaque;
-
-    /* tick_offset is base_time - rtc_clock base time.  Instead, we want to
-     * store the base time relative to the vm_clock for backwards-compatibility.  */
-    int64_t delta = qemu_get_clock_ns(rtc_clock) - qemu_get_clock_ns(vm_clock);
-    s->tick_offset_vmstate = s->tick_offset + delta / get_ticks_per_sec();
-}
-
-static int pl031_post_load(void *opaque, int version_id)
-{
-    pl031_state *s = opaque;
-
-    int64_t delta = qemu_get_clock_ns(rtc_clock) - qemu_get_clock_ns(vm_clock);
-    s->tick_offset = s->tick_offset_vmstate - delta / get_ticks_per_sec();
-    pl031_set_alarm(s);
-    return 0;
-}
-
-static const VMStateDescription vmstate_pl031 = {
-    .name = "pl031",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .pre_save = pl031_pre_save,
-    .post_load = pl031_post_load,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32(tick_offset_vmstate, pl031_state),
-        VMSTATE_UINT32(mr, pl031_state),
-        VMSTATE_UINT32(lr, pl031_state),
-        VMSTATE_UINT32(cr, pl031_state),
-        VMSTATE_UINT32(im, pl031_state),
-        VMSTATE_UINT32(is, pl031_state),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void pl031_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = pl031_init;
-    dc->no_user = 1;
-    dc->vmsd = &vmstate_pl031;
-}
-
-static const TypeInfo pl031_info = {
-    .name          = "pl031",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(pl031_state),
-    .class_init    = pl031_class_init,
-};
-
-static void pl031_register_types(void)
-{
-    type_register_static(&pl031_info);
-}
-
-type_init(pl031_register_types)
diff --git a/hw/pl041.c b/hw/pl041.c
deleted file mode 100644 (file)
index 92dddc2..0000000
+++ /dev/null
@@ -1,647 +0,0 @@
-/*
- * Arm PrimeCell PL041 Advanced Audio Codec Interface
- *
- * Copyright (c) 2011
- * Written by Mathieu Sonet - www.elasticsheep.com
- *
- * This code is licensed under the GPL.
- *
- * *****************************************************************
- *
- * This driver emulates the ARM AACI interface
- * connected to a LM4549 codec.
- *
- * Limitations:
- * - Supports only a playback on one channel (Versatile/Vexpress)
- * - Supports only one TX FIFO in compact-mode or non-compact mode.
- * - Supports playback of 12, 16, 18 and 20 bits samples.
- * - Record is not supported.
- * - The PL041 is hardwired to a LM4549 codec.
- *
- */
-
-#include "hw/sysbus.h"
-
-#include "hw/pl041.h"
-#include "hw/lm4549.h"
-
-#if 0
-#define PL041_DEBUG_LEVEL 1
-#endif
-
-#if defined(PL041_DEBUG_LEVEL) && (PL041_DEBUG_LEVEL >= 1)
-#define DBG_L1(fmt, ...) \
-do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DBG_L1(fmt, ...) \
-do { } while (0)
-#endif
-
-#if defined(PL041_DEBUG_LEVEL) && (PL041_DEBUG_LEVEL >= 2)
-#define DBG_L2(fmt, ...) \
-do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DBG_L2(fmt, ...) \
-do { } while (0)
-#endif
-
-
-#define MAX_FIFO_DEPTH      (1024)
-#define DEFAULT_FIFO_DEPTH  (8)
-
-#define SLOT1_RW    (1 << 19)
-
-/* This FIFO only stores 20-bit samples on 32-bit words.
-   So its level is independent of the selected mode */
-typedef struct {
-    uint32_t level;
-    uint32_t data[MAX_FIFO_DEPTH];
-} pl041_fifo;
-
-typedef struct {
-    pl041_fifo tx_fifo;
-    uint8_t tx_enabled;
-    uint8_t tx_compact_mode;
-    uint8_t tx_sample_size;
-
-    pl041_fifo rx_fifo;
-    uint8_t rx_enabled;
-    uint8_t rx_compact_mode;
-    uint8_t rx_sample_size;
-} pl041_channel;
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    qemu_irq irq;
-
-    uint32_t fifo_depth; /* FIFO depth in non-compact mode */
-
-    pl041_regfile regs;
-    pl041_channel fifo1;
-    lm4549_state codec;
-} pl041_state;
-
-
-static const unsigned char pl041_default_id[8] = {
-    0x41, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1
-};
-
-#if defined(PL041_DEBUG_LEVEL)
-#define REGISTER(name, offset) #name,
-static const char *pl041_regs_name[] = {
-    #include "pl041.hx"
-};
-#undef REGISTER
-#endif
-
-
-#if defined(PL041_DEBUG_LEVEL)
-static const char *get_reg_name(hwaddr offset)
-{
-    if (offset <= PL041_dr1_7) {
-        return pl041_regs_name[offset >> 2];
-    }
-
-    return "unknown";
-}
-#endif
-
-static uint8_t pl041_compute_periphid3(pl041_state *s)
-{
-    uint8_t id3 = 1; /* One channel */
-
-    /* Add the fifo depth information */
-    switch (s->fifo_depth) {
-    case 8:
-        id3 |= 0 << 3;
-        break;
-    case 32:
-        id3 |= 1 << 3;
-        break;
-    case 64:
-        id3 |= 2 << 3;
-        break;
-    case 128:
-        id3 |= 3 << 3;
-        break;
-    case 256:
-        id3 |= 4 << 3;
-        break;
-    case 512:
-        id3 |= 5 << 3;
-        break;
-    case 1024:
-        id3 |= 6 << 3;
-        break;
-    case 2048:
-        id3 |= 7 << 3;
-        break;
-    }
-
-    return id3;
-}
-
-static void pl041_reset(pl041_state *s)
-{
-    DBG_L1("pl041_reset\n");
-
-    memset(&s->regs, 0x00, sizeof(pl041_regfile));
-
-    s->regs.slfr = SL1TXEMPTY | SL2TXEMPTY | SL12TXEMPTY;
-    s->regs.sr1 = TXFE | RXFE | TXHE;
-    s->regs.isr1 = 0;
-
-    memset(&s->fifo1, 0x00, sizeof(s->fifo1));
-}
-
-
-static void pl041_fifo1_write(pl041_state *s, uint32_t value)
-{
-    pl041_channel *channel = &s->fifo1;
-    pl041_fifo *fifo = &s->fifo1.tx_fifo;
-
-    /* Push the value in the FIFO */
-    if (channel->tx_compact_mode == 0) {
-        /* Non-compact mode */
-
-        if (fifo->level < s->fifo_depth) {
-            /* Pad the value with 0 to obtain a 20-bit sample */
-            switch (channel->tx_sample_size) {
-            case 12:
-                value = (value << 8) & 0xFFFFF;
-                break;
-            case 16:
-                value = (value << 4) & 0xFFFFF;
-                break;
-            case 18:
-                value = (value << 2) & 0xFFFFF;
-                break;
-            case 20:
-            default:
-                break;
-            }
-
-            /* Store the sample in the FIFO */
-            fifo->data[fifo->level++] = value;
-        }
-#if defined(PL041_DEBUG_LEVEL)
-        else {
-            DBG_L1("fifo1 write: overrun\n");
-        }
-#endif
-    } else {
-        /* Compact mode */
-
-        if ((fifo->level + 2) < s->fifo_depth) {
-            uint32_t i = 0;
-            uint32_t sample = 0;
-
-            for (i = 0; i < 2; i++) {
-                sample = value & 0xFFFF;
-                value = value >> 16;
-
-                /* Pad each sample with 0 to obtain a 20-bit sample */
-                switch (channel->tx_sample_size) {
-                case 12:
-                    sample = sample << 8;
-                    break;
-                case 16:
-                default:
-                    sample = sample << 4;
-                    break;
-                }
-
-                /* Store the sample in the FIFO */
-                fifo->data[fifo->level++] = sample;
-            }
-        }
-#if defined(PL041_DEBUG_LEVEL)
-        else {
-            DBG_L1("fifo1 write: overrun\n");
-        }
-#endif
-    }
-
-    /* Update the status register */
-    if (fifo->level > 0) {
-        s->regs.sr1 &= ~(TXUNDERRUN | TXFE);
-    }
-
-    if (fifo->level >= (s->fifo_depth / 2)) {
-        s->regs.sr1 &= ~TXHE;
-    }
-
-    if (fifo->level >= s->fifo_depth) {
-        s->regs.sr1 |= TXFF;
-    }
-
-    DBG_L2("fifo1_push sr1 = 0x%08x\n", s->regs.sr1);
-}
-
-static void pl041_fifo1_transmit(pl041_state *s)
-{
-    pl041_channel *channel = &s->fifo1;
-    pl041_fifo *fifo = &s->fifo1.tx_fifo;
-    uint32_t slots = s->regs.txcr1 & TXSLOT_MASK;
-    uint32_t written_samples;
-
-    /* Check if FIFO1 transmit is enabled */
-    if ((channel->tx_enabled) && (slots & (TXSLOT3 | TXSLOT4))) {
-        if (fifo->level >= (s->fifo_depth / 2)) {
-            int i;
-
-            DBG_L1("Transfer FIFO level = %i\n", fifo->level);
-
-            /* Try to transfer the whole FIFO */
-            for (i = 0; i < (fifo->level / 2); i++) {
-                uint32_t left = fifo->data[i * 2];
-                uint32_t right = fifo->data[i * 2 + 1];
-
-                 /* Transmit two 20-bit samples to the codec */
-                if (lm4549_write_samples(&s->codec, left, right) == 0) {
-                    DBG_L1("Codec buffer full\n");
-                    break;
-                }
-            }
-
-            written_samples = i * 2;
-            if (written_samples > 0) {
-                /* Update the FIFO level */
-                fifo->level -= written_samples;
-
-                /* Move back the pending samples to the start of the FIFO */
-                for (i = 0; i < fifo->level; i++) {
-                    fifo->data[i] = fifo->data[written_samples + i];
-                }
-
-                /* Update the status register */
-                s->regs.sr1 &= ~TXFF;
-
-                if (fifo->level <= (s->fifo_depth / 2)) {
-                    s->regs.sr1 |= TXHE;
-                }
-
-                if (fifo->level == 0) {
-                    s->regs.sr1 |= TXFE | TXUNDERRUN;
-                    DBG_L1("Empty FIFO\n");
-                }
-            }
-        }
-    }
-}
-
-static void pl041_isr1_update(pl041_state *s)
-{
-    /* Update ISR1 */
-    if (s->regs.sr1 & TXUNDERRUN) {
-        s->regs.isr1 |= URINTR;
-    } else {
-        s->regs.isr1 &= ~URINTR;
-    }
-
-    if (s->regs.sr1 & TXHE) {
-        s->regs.isr1 |= TXINTR;
-    } else {
-        s->regs.isr1 &= ~TXINTR;
-    }
-
-    if (!(s->regs.sr1 & TXBUSY) && (s->regs.sr1 & TXFE)) {
-        s->regs.isr1 |= TXCINTR;
-    } else {
-        s->regs.isr1 &= ~TXCINTR;
-    }
-
-    /* Update the irq state */
-    qemu_set_irq(s->irq, ((s->regs.isr1 & s->regs.ie1) > 0) ? 1 : 0);
-    DBG_L2("Set interrupt sr1 = 0x%08x isr1 = 0x%08x masked = 0x%08x\n",
-           s->regs.sr1, s->regs.isr1, s->regs.isr1 & s->regs.ie1);
-}
-
-static void pl041_request_data(void *opaque)
-{
-    pl041_state *s = (pl041_state *)opaque;
-
-    /* Trigger pending transfers */
-    pl041_fifo1_transmit(s);
-    pl041_isr1_update(s);
-}
-
-static uint64_t pl041_read(void *opaque, hwaddr offset,
-                                unsigned size)
-{
-    pl041_state *s = (pl041_state *)opaque;
-    int value;
-
-    if ((offset >= PL041_periphid0) && (offset <= PL041_pcellid3)) {
-        if (offset == PL041_periphid3) {
-            value = pl041_compute_periphid3(s);
-        } else {
-            value = pl041_default_id[(offset - PL041_periphid0) >> 2];
-        }
-
-        DBG_L1("pl041_read [0x%08x] => 0x%08x\n", offset, value);
-        return value;
-    } else if (offset <= PL041_dr4_7) {
-        value = *((uint32_t *)&s->regs + (offset >> 2));
-    } else {
-        DBG_L1("pl041_read: Reserved offset %x\n", (int)offset);
-        return 0;
-    }
-
-    switch (offset) {
-    case PL041_allints:
-        value = s->regs.isr1 & 0x7F;
-        break;
-    }
-
-    DBG_L1("pl041_read [0x%08x] %s => 0x%08x\n", offset,
-           get_reg_name(offset), value);
-
-    return value;
-}
-
-static void pl041_write(void *opaque, hwaddr offset,
-                             uint64_t value, unsigned size)
-{
-    pl041_state *s = (pl041_state *)opaque;
-    uint16_t control, data;
-    uint32_t result;
-
-    DBG_L1("pl041_write [0x%08x] %s <= 0x%08x\n", offset,
-           get_reg_name(offset), (unsigned int)value);
-
-    /* Write the register */
-    if (offset <= PL041_dr4_7) {
-        *((uint32_t *)&s->regs + (offset >> 2)) = value;
-    } else {
-        DBG_L1("pl041_write: Reserved offset %x\n", (int)offset);
-        return;
-    }
-
-    /* Execute the actions */
-    switch (offset) {
-    case PL041_txcr1:
-    {
-        pl041_channel *channel = &s->fifo1;
-
-        uint32_t txen = s->regs.txcr1 & TXEN;
-        uint32_t tsize = (s->regs.txcr1 & TSIZE_MASK) >> TSIZE_MASK_BIT;
-        uint32_t compact_mode = (s->regs.txcr1 & TXCOMPACT) ? 1 : 0;
-#if defined(PL041_DEBUG_LEVEL)
-        uint32_t slots = (s->regs.txcr1 & TXSLOT_MASK) >> TXSLOT_MASK_BIT;
-        uint32_t txfen = (s->regs.txcr1 & TXFEN) > 0 ? 1 : 0;
-#endif
-
-        DBG_L1("=> txen = %i slots = 0x%01x tsize = %i compact = %i "
-               "txfen = %i\n", txen, slots,  tsize, compact_mode, txfen);
-
-        channel->tx_enabled = txen;
-        channel->tx_compact_mode = compact_mode;
-
-        switch (tsize) {
-        case 0:
-            channel->tx_sample_size = 16;
-            break;
-        case 1:
-            channel->tx_sample_size = 18;
-            break;
-        case 2:
-            channel->tx_sample_size = 20;
-            break;
-        case 3:
-            channel->tx_sample_size = 12;
-            break;
-        }
-
-        DBG_L1("TX enabled = %i\n", channel->tx_enabled);
-        DBG_L1("TX compact mode = %i\n", channel->tx_compact_mode);
-        DBG_L1("TX sample width = %i\n", channel->tx_sample_size);
-
-        /* Check if compact mode is allowed with selected tsize */
-        if (channel->tx_compact_mode == 1) {
-            if ((channel->tx_sample_size == 18) ||
-                (channel->tx_sample_size == 20)) {
-                channel->tx_compact_mode = 0;
-                DBG_L1("Compact mode not allowed with 18/20-bit sample size\n");
-            }
-        }
-
-        break;
-    }
-    case PL041_sl1tx:
-        s->regs.slfr &= ~SL1TXEMPTY;
-
-        control = (s->regs.sl1tx >> 12) & 0x7F;
-        data = (s->regs.sl2tx >> 4) & 0xFFFF;
-
-        if ((s->regs.sl1tx & SLOT1_RW) == 0) {
-            /* Write operation */
-            lm4549_write(&s->codec, control, data);
-        } else {
-            /* Read operation */
-            result = lm4549_read(&s->codec, control);
-
-            /* Store the returned value */
-            s->regs.sl1rx = s->regs.sl1tx & ~SLOT1_RW;
-            s->regs.sl2rx = result << 4;
-
-            s->regs.slfr &= ~(SL1RXBUSY | SL2RXBUSY);
-            s->regs.slfr |= SL1RXVALID | SL2RXVALID;
-        }
-        break;
-
-    case PL041_sl2tx:
-        s->regs.sl2tx = value;
-        s->regs.slfr &= ~SL2TXEMPTY;
-        break;
-
-    case PL041_intclr:
-        DBG_L1("=> Clear interrupt intclr = 0x%08x isr1 = 0x%08x\n",
-               s->regs.intclr, s->regs.isr1);
-
-        if (s->regs.intclr & TXUEC1) {
-            s->regs.sr1 &= ~TXUNDERRUN;
-        }
-        break;
-
-    case PL041_maincr:
-    {
-#if defined(PL041_DEBUG_LEVEL)
-        char debug[] = " AACIFE  SL1RXEN  SL1TXEN";
-        if (!(value & AACIFE)) {
-            debug[0] = '!';
-        }
-        if (!(value & SL1RXEN)) {
-            debug[8] = '!';
-        }
-        if (!(value & SL1TXEN)) {
-            debug[17] = '!';
-        }
-        DBG_L1("%s\n", debug);
-#endif
-
-        if ((s->regs.maincr & AACIFE) == 0) {
-            pl041_reset(s);
-        }
-        break;
-    }
-
-    case PL041_dr1_0:
-    case PL041_dr1_1:
-    case PL041_dr1_2:
-    case PL041_dr1_3:
-        pl041_fifo1_write(s, value);
-        break;
-    }
-
-    /* Transmit the FIFO content */
-    pl041_fifo1_transmit(s);
-
-    /* Update the ISR1 register */
-    pl041_isr1_update(s);
-}
-
-static void pl041_device_reset(DeviceState *d)
-{
-    pl041_state *s = DO_UPCAST(pl041_state, busdev.qdev, d);
-
-    pl041_reset(s);
-}
-
-static const MemoryRegionOps pl041_ops = {
-    .read = pl041_read,
-    .write = pl041_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int pl041_init(SysBusDevice *dev)
-{
-    pl041_state *s = FROM_SYSBUS(pl041_state, dev);
-
-    DBG_L1("pl041_init 0x%08x\n", (uint32_t)s);
-
-    /* Check the device properties */
-    switch (s->fifo_depth) {
-    case 8:
-    case 32:
-    case 64:
-    case 128:
-    case 256:
-    case 512:
-    case 1024:
-    case 2048:
-        break;
-    case 16:
-    default:
-        /* NC FIFO depth of 16 is not allowed because its id bits in
-           AACIPERIPHID3 overlap with the id for the default NC FIFO depth */
-        qemu_log_mask(LOG_UNIMP,
-                      "pl041: unsupported non-compact fifo depth [%i]\n",
-                      s->fifo_depth);
-        return -1;
-    }
-
-    /* Connect the device to the sysbus */
-    memory_region_init_io(&s->iomem, &pl041_ops, s, "pl041", 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-    sysbus_init_irq(dev, &s->irq);
-
-    /* Init the codec */
-    lm4549_init(&s->codec, &pl041_request_data, (void *)s);
-
-    return 0;
-}
-
-static const VMStateDescription vmstate_pl041_regfile = {
-    .name = "pl041_regfile",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-#define REGISTER(name, offset) VMSTATE_UINT32(name, pl041_regfile),
-        #include "pl041.hx"
-#undef REGISTER
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_pl041_fifo = {
-    .name = "pl041_fifo",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT32(level, pl041_fifo),
-        VMSTATE_UINT32_ARRAY(data, pl041_fifo, MAX_FIFO_DEPTH),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_pl041_channel = {
-    .name = "pl041_channel",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_STRUCT(tx_fifo, pl041_channel, 0,
-                       vmstate_pl041_fifo, pl041_fifo),
-        VMSTATE_UINT8(tx_enabled, pl041_channel),
-        VMSTATE_UINT8(tx_compact_mode, pl041_channel),
-        VMSTATE_UINT8(tx_sample_size, pl041_channel),
-        VMSTATE_STRUCT(rx_fifo, pl041_channel, 0,
-                       vmstate_pl041_fifo, pl041_fifo),
-        VMSTATE_UINT8(rx_enabled, pl041_channel),
-        VMSTATE_UINT8(rx_compact_mode, pl041_channel),
-        VMSTATE_UINT8(rx_sample_size, pl041_channel),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_pl041 = {
-    .name = "pl041",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32(fifo_depth, pl041_state),
-        VMSTATE_STRUCT(regs, pl041_state, 0,
-                       vmstate_pl041_regfile, pl041_regfile),
-        VMSTATE_STRUCT(fifo1, pl041_state, 0,
-                       vmstate_pl041_channel, pl041_channel),
-        VMSTATE_STRUCT(codec, pl041_state, 0,
-                       vmstate_lm4549_state, lm4549_state),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property pl041_device_properties[] = {
-    /* Non-compact FIFO depth property */
-    DEFINE_PROP_UINT32("nc_fifo_depth", pl041_state, fifo_depth, DEFAULT_FIFO_DEPTH),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void pl041_device_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = pl041_init;
-    dc->no_user = 1;
-    dc->reset = pl041_device_reset;
-    dc->vmsd = &vmstate_pl041;
-    dc->props = pl041_device_properties;
-}
-
-static const TypeInfo pl041_device_info = {
-    .name          = "pl041",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(pl041_state),
-    .class_init    = pl041_device_class_init,
-};
-
-static void pl041_register_types(void)
-{
-    type_register_static(&pl041_device_info);
-}
-
-type_init(pl041_register_types)
diff --git a/hw/pl041.hx b/hw/pl041.hx
deleted file mode 100644 (file)
index dd7188c..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Arm PrimeCell PL041 Advanced Audio Codec Interface
- *
- * Copyright (c) 2011
- * Written by Mathieu Sonet - www.elasticsheep.com
- *
- * This code is licensed under the GPL.
- *
- * *****************************************************************
- */
-
-/* PL041 register file description */
-
-REGISTER( rxcr1,   0x00 )
-REGISTER( txcr1,   0x04 )
-REGISTER( sr1,     0x08 )
-REGISTER( isr1,    0x0C )
-REGISTER( ie1,     0x10 )
-REGISTER( rxcr2,   0x14 )
-REGISTER( txcr2,   0x18 )
-REGISTER( sr2,     0x1C )
-REGISTER( isr2,    0x20 )
-REGISTER( ie2,     0x24 )
-REGISTER( rxcr3,   0x28 )
-REGISTER( txcr3,   0x2C )
-REGISTER( sr3,     0x30 )
-REGISTER( isr3,    0x34 )
-REGISTER( ie3,     0x38 )
-REGISTER( rxcr4,   0x3C )
-REGISTER( txcr4,   0x40 )
-REGISTER( sr4,     0x44 )
-REGISTER( isr4,    0x48 )
-REGISTER( ie4,     0x4C )
-REGISTER( sl1rx,   0x50 )
-REGISTER( sl1tx,   0x54 )
-REGISTER( sl2rx,   0x58 )
-REGISTER( sl2tx,   0x5C )
-REGISTER( sl12rx,  0x60 )
-REGISTER( sl12tx,  0x64 )
-REGISTER( slfr,    0x68 )
-REGISTER( slistat, 0x6C )
-REGISTER( slien,   0x70 )
-REGISTER( intclr,  0x74 )
-REGISTER( maincr,  0x78 )
-REGISTER( reset,   0x7C )
-REGISTER( sync,    0x80 )
-REGISTER( allints, 0x84 )
-REGISTER( mainfr,  0x88 )
-REGISTER( unused,  0x8C )
-REGISTER( dr1_0,   0x90 )
-REGISTER( dr1_1,   0x94 )
-REGISTER( dr1_2,   0x98 )
-REGISTER( dr1_3,   0x9C )
-REGISTER( dr1_4,   0xA0 )
-REGISTER( dr1_5,   0xA4 )
-REGISTER( dr1_6,   0xA8 )
-REGISTER( dr1_7,   0xAC )
-REGISTER( dr2_0,   0xB0 )
-REGISTER( dr2_1,   0xB4 )
-REGISTER( dr2_2,   0xB8 )
-REGISTER( dr2_3,   0xBC )
-REGISTER( dr2_4,   0xC0 )
-REGISTER( dr2_5,   0xC4 )
-REGISTER( dr2_6,   0xC8 )
-REGISTER( dr2_7,   0xCC )
-REGISTER( dr3_0,   0xD0 )
-REGISTER( dr3_1,   0xD4 )
-REGISTER( dr3_2,   0xD8 )
-REGISTER( dr3_3,   0xDC )
-REGISTER( dr3_4,   0xE0 )
-REGISTER( dr3_5,   0xE4 )
-REGISTER( dr3_6,   0xE8 )
-REGISTER( dr3_7,   0xEC )
-REGISTER( dr4_0,   0xF0 )
-REGISTER( dr4_1,   0xF4 )
-REGISTER( dr4_2,   0xF8 )
-REGISTER( dr4_3,   0xFC )
-REGISTER( dr4_4,   0x100 )
-REGISTER( dr4_5,   0x104 )
-REGISTER( dr4_6,   0x108 )
-REGISTER( dr4_7,   0x10C )
diff --git a/hw/pl050.c b/hw/pl050.c
deleted file mode 100644 (file)
index 7dd8a59..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Arm PrimeCell PL050 Keyboard / Mouse Interface
- *
- * Copyright (c) 2006-2007 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the GPL.
- */
-
-#include "hw/sysbus.h"
-#include "hw/input/ps2.h"
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    void *dev;
-    uint32_t cr;
-    uint32_t clk;
-    uint32_t last;
-    int pending;
-    qemu_irq irq;
-    int is_mouse;
-} pl050_state;
-
-static const VMStateDescription vmstate_pl050 = {
-    .name = "pl050",
-    .version_id = 2,
-    .minimum_version_id = 2,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32(cr, pl050_state),
-        VMSTATE_UINT32(clk, pl050_state),
-        VMSTATE_UINT32(last, pl050_state),
-        VMSTATE_INT32(pending, pl050_state),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-#define PL050_TXEMPTY         (1 << 6)
-#define PL050_TXBUSY          (1 << 5)
-#define PL050_RXFULL          (1 << 4)
-#define PL050_RXBUSY          (1 << 3)
-#define PL050_RXPARITY        (1 << 2)
-#define PL050_KMIC            (1 << 1)
-#define PL050_KMID            (1 << 0)
-
-static const unsigned char pl050_id[] =
-{ 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
-
-static void pl050_update(void *opaque, int level)
-{
-    pl050_state *s = (pl050_state *)opaque;
-    int raise;
-
-    s->pending = level;
-    raise = (s->pending && (s->cr & 0x10) != 0)
-            || (s->cr & 0x08) != 0;
-    qemu_set_irq(s->irq, raise);
-}
-
-static uint64_t pl050_read(void *opaque, hwaddr offset,
-                           unsigned size)
-{
-    pl050_state *s = (pl050_state *)opaque;
-    if (offset >= 0xfe0 && offset < 0x1000)
-        return pl050_id[(offset - 0xfe0) >> 2];
-
-    switch (offset >> 2) {
-    case 0: /* KMICR */
-        return s->cr;
-    case 1: /* KMISTAT */
-        {
-            uint8_t val;
-            uint32_t stat;
-
-            val = s->last;
-            val = val ^ (val >> 4);
-            val = val ^ (val >> 2);
-            val = (val ^ (val >> 1)) & 1;
-
-            stat = PL050_TXEMPTY;
-            if (val)
-                stat |= PL050_RXPARITY;
-            if (s->pending)
-                stat |= PL050_RXFULL;
-
-            return stat;
-        }
-    case 2: /* KMIDATA */
-        if (s->pending)
-            s->last = ps2_read_data(s->dev);
-        return s->last;
-    case 3: /* KMICLKDIV */
-        return s->clk;
-    case 4: /* KMIIR */
-        return s->pending | 2;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl050_read: Bad offset %x\n", (int)offset);
-        return 0;
-    }
-}
-
-static void pl050_write(void *opaque, hwaddr offset,
-                        uint64_t value, unsigned size)
-{
-    pl050_state *s = (pl050_state *)opaque;
-    switch (offset >> 2) {
-    case 0: /* KMICR */
-        s->cr = value;
-        pl050_update(s, s->pending);
-        /* ??? Need to implement the enable/disable bit.  */
-        break;
-    case 2: /* KMIDATA */
-        /* ??? This should toggle the TX interrupt line.  */
-        /* ??? This means kbd/mouse can block each other.  */
-        if (s->is_mouse) {
-            ps2_write_mouse(s->dev, value);
-        } else {
-            ps2_write_keyboard(s->dev, value);
-        }
-        break;
-    case 3: /* KMICLKDIV */
-        s->clk = value;
-        return;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl050_write: Bad offset %x\n", (int)offset);
-    }
-}
-static const MemoryRegionOps pl050_ops = {
-    .read = pl050_read,
-    .write = pl050_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int pl050_init(SysBusDevice *dev, int is_mouse)
-{
-    pl050_state *s = FROM_SYSBUS(pl050_state, dev);
-
-    memory_region_init_io(&s->iomem, &pl050_ops, s, "pl050", 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-    sysbus_init_irq(dev, &s->irq);
-    s->is_mouse = is_mouse;
-    if (s->is_mouse)
-        s->dev = ps2_mouse_init(pl050_update, s);
-    else
-        s->dev = ps2_kbd_init(pl050_update, s);
-    return 0;
-}
-
-static int pl050_init_keyboard(SysBusDevice *dev)
-{
-    return pl050_init(dev, 0);
-}
-
-static int pl050_init_mouse(SysBusDevice *dev)
-{
-    return pl050_init(dev, 1);
-}
-
-static void pl050_kbd_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = pl050_init_keyboard;
-    dc->vmsd = &vmstate_pl050;
-}
-
-static const TypeInfo pl050_kbd_info = {
-    .name          = "pl050_keyboard",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(pl050_state),
-    .class_init    = pl050_kbd_class_init,
-};
-
-static void pl050_mouse_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = pl050_init_mouse;
-    dc->vmsd = &vmstate_pl050;
-}
-
-static const TypeInfo pl050_mouse_info = {
-    .name          = "pl050_mouse",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(pl050_state),
-    .class_init    = pl050_mouse_class_init,
-};
-
-static void pl050_register_types(void)
-{
-    type_register_static(&pl050_kbd_info);
-    type_register_static(&pl050_mouse_info);
-}
-
-type_init(pl050_register_types)
diff --git a/hw/pl061.c b/hw/pl061.c
deleted file mode 100644 (file)
index 74bc109..0000000
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * Arm PrimeCell PL061 General Purpose IO with additional
- * Luminary Micro Stellaris bits.
- *
- * Copyright (c) 2007 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the GPL.
- */
-
-#include "hw/sysbus.h"
-
-//#define DEBUG_PL061 1
-
-#ifdef DEBUG_PL061
-#define DPRINTF(fmt, ...) \
-do { printf("pl061: " fmt , ## __VA_ARGS__); } while (0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "pl061: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "pl061: error: " fmt , ## __VA_ARGS__);} while (0)
-#endif
-
-static const uint8_t pl061_id[12] =
-  { 0x00, 0x00, 0x00, 0x00, 0x61, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
-static const uint8_t pl061_id_luminary[12] =
-  { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    uint32_t locked;
-    uint32_t data;
-    uint32_t old_data;
-    uint32_t dir;
-    uint32_t isense;
-    uint32_t ibe;
-    uint32_t iev;
-    uint32_t im;
-    uint32_t istate;
-    uint32_t afsel;
-    uint32_t dr2r;
-    uint32_t dr4r;
-    uint32_t dr8r;
-    uint32_t odr;
-    uint32_t pur;
-    uint32_t pdr;
-    uint32_t slr;
-    uint32_t den;
-    uint32_t cr;
-    uint32_t float_high;
-    uint32_t amsel;
-    qemu_irq irq;
-    qemu_irq out[8];
-    const unsigned char *id;
-} pl061_state;
-
-static const VMStateDescription vmstate_pl061 = {
-    .name = "pl061",
-    .version_id = 2,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32(locked, pl061_state),
-        VMSTATE_UINT32(data, pl061_state),
-        VMSTATE_UINT32(old_data, pl061_state),
-        VMSTATE_UINT32(dir, pl061_state),
-        VMSTATE_UINT32(isense, pl061_state),
-        VMSTATE_UINT32(ibe, pl061_state),
-        VMSTATE_UINT32(iev, pl061_state),
-        VMSTATE_UINT32(im, pl061_state),
-        VMSTATE_UINT32(istate, pl061_state),
-        VMSTATE_UINT32(afsel, pl061_state),
-        VMSTATE_UINT32(dr2r, pl061_state),
-        VMSTATE_UINT32(dr4r, pl061_state),
-        VMSTATE_UINT32(dr8r, pl061_state),
-        VMSTATE_UINT32(odr, pl061_state),
-        VMSTATE_UINT32(pur, pl061_state),
-        VMSTATE_UINT32(pdr, pl061_state),
-        VMSTATE_UINT32(slr, pl061_state),
-        VMSTATE_UINT32(den, pl061_state),
-        VMSTATE_UINT32(cr, pl061_state),
-        VMSTATE_UINT32(float_high, pl061_state),
-        VMSTATE_UINT32_V(amsel, pl061_state, 2),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void pl061_update(pl061_state *s)
-{
-    uint8_t changed;
-    uint8_t mask;
-    uint8_t out;
-    int i;
-
-    /* Outputs float high.  */
-    /* FIXME: This is board dependent.  */
-    out = (s->data & s->dir) | ~s->dir;
-    changed = s->old_data ^ out;
-    if (!changed)
-        return;
-
-    s->old_data = out;
-    for (i = 0; i < 8; i++) {
-        mask = 1 << i;
-        if (changed & mask) {
-            DPRINTF("Set output %d = %d\n", i, (out & mask) != 0);
-            qemu_set_irq(s->out[i], (out & mask) != 0);
-        }
-    }
-
-    /* FIXME: Implement input interrupts.  */
-}
-
-static uint64_t pl061_read(void *opaque, hwaddr offset,
-                           unsigned size)
-{
-    pl061_state *s = (pl061_state *)opaque;
-
-    if (offset >= 0xfd0 && offset < 0x1000) {
-        return s->id[(offset - 0xfd0) >> 2];
-    }
-    if (offset < 0x400) {
-        return s->data & (offset >> 2);
-    }
-    switch (offset) {
-    case 0x400: /* Direction */
-        return s->dir;
-    case 0x404: /* Interrupt sense */
-        return s->isense;
-    case 0x408: /* Interrupt both edges */
-        return s->ibe;
-    case 0x40c: /* Interrupt event */
-        return s->iev;
-    case 0x410: /* Interrupt mask */
-        return s->im;
-    case 0x414: /* Raw interrupt status */
-        return s->istate;
-    case 0x418: /* Masked interrupt status */
-        return s->istate | s->im;
-    case 0x420: /* Alternate function select */
-        return s->afsel;
-    case 0x500: /* 2mA drive */
-        return s->dr2r;
-    case 0x504: /* 4mA drive */
-        return s->dr4r;
-    case 0x508: /* 8mA drive */
-        return s->dr8r;
-    case 0x50c: /* Open drain */
-        return s->odr;
-    case 0x510: /* Pull-up */
-        return s->pur;
-    case 0x514: /* Pull-down */
-        return s->pdr;
-    case 0x518: /* Slew rate control */
-        return s->slr;
-    case 0x51c: /* Digital enable */
-        return s->den;
-    case 0x520: /* Lock */
-        return s->locked;
-    case 0x524: /* Commit */
-        return s->cr;
-    case 0x528: /* Analog mode select */
-        return s->amsel;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl061_read: Bad offset %x\n", (int)offset);
-        return 0;
-    }
-}
-
-static void pl061_write(void *opaque, hwaddr offset,
-                        uint64_t value, unsigned size)
-{
-    pl061_state *s = (pl061_state *)opaque;
-    uint8_t mask;
-
-    if (offset < 0x400) {
-        mask = (offset >> 2) & s->dir;
-        s->data = (s->data & ~mask) | (value & mask);
-        pl061_update(s);
-        return;
-    }
-    switch (offset) {
-    case 0x400: /* Direction */
-        s->dir = value & 0xff;
-        break;
-    case 0x404: /* Interrupt sense */
-        s->isense = value & 0xff;
-        break;
-    case 0x408: /* Interrupt both edges */
-        s->ibe = value & 0xff;
-        break;
-    case 0x40c: /* Interrupt event */
-        s->iev = value & 0xff;
-        break;
-    case 0x410: /* Interrupt mask */
-        s->im = value & 0xff;
-        break;
-    case 0x41c: /* Interrupt clear */
-        s->istate &= ~value;
-        break;
-    case 0x420: /* Alternate function select */
-        mask = s->cr;
-        s->afsel = (s->afsel & ~mask) | (value & mask);
-        break;
-    case 0x500: /* 2mA drive */
-        s->dr2r = value & 0xff;
-        break;
-    case 0x504: /* 4mA drive */
-        s->dr4r = value & 0xff;
-        break;
-    case 0x508: /* 8mA drive */
-        s->dr8r = value & 0xff;
-        break;
-    case 0x50c: /* Open drain */
-        s->odr = value & 0xff;
-        break;
-    case 0x510: /* Pull-up */
-        s->pur = value & 0xff;
-        break;
-    case 0x514: /* Pull-down */
-        s->pdr = value & 0xff;
-        break;
-    case 0x518: /* Slew rate control */
-        s->slr = value & 0xff;
-        break;
-    case 0x51c: /* Digital enable */
-        s->den = value & 0xff;
-        break;
-    case 0x520: /* Lock */
-        s->locked = (value != 0xacce551);
-        break;
-    case 0x524: /* Commit */
-        if (!s->locked)
-            s->cr = value & 0xff;
-        break;
-    case 0x528:
-        s->amsel = value & 0xff;
-        break;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl061_write: Bad offset %x\n", (int)offset);
-    }
-    pl061_update(s);
-}
-
-static void pl061_reset(pl061_state *s)
-{
-  s->locked = 1;
-  s->cr = 0xff;
-}
-
-static void pl061_set_irq(void * opaque, int irq, int level)
-{
-    pl061_state *s = (pl061_state *)opaque;
-    uint8_t mask;
-
-    mask = 1 << irq;
-    if ((s->dir & mask) == 0) {
-        s->data &= ~mask;
-        if (level)
-            s->data |= mask;
-        pl061_update(s);
-    }
-}
-
-static const MemoryRegionOps pl061_ops = {
-    .read = pl061_read,
-    .write = pl061_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int pl061_init(SysBusDevice *dev, const unsigned char *id)
-{
-    pl061_state *s = FROM_SYSBUS(pl061_state, dev);
-    s->id = id;
-    memory_region_init_io(&s->iomem, &pl061_ops, s, "pl061", 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-    sysbus_init_irq(dev, &s->irq);
-    qdev_init_gpio_in(&dev->qdev, pl061_set_irq, 8);
-    qdev_init_gpio_out(&dev->qdev, s->out, 8);
-    pl061_reset(s);
-    return 0;
-}
-
-static int pl061_init_luminary(SysBusDevice *dev)
-{
-    return pl061_init(dev, pl061_id_luminary);
-}
-
-static int pl061_init_arm(SysBusDevice *dev)
-{
-    return pl061_init(dev, pl061_id);
-}
-
-static void pl061_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = pl061_init_arm;
-    dc->vmsd = &vmstate_pl061;
-}
-
-static const TypeInfo pl061_info = {
-    .name          = "pl061",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(pl061_state),
-    .class_init    = pl061_class_init,
-};
-
-static void pl061_luminary_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = pl061_init_luminary;
-    dc->vmsd = &vmstate_pl061;
-}
-
-static const TypeInfo pl061_luminary_info = {
-    .name          = "pl061_luminary",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(pl061_state),
-    .class_init    = pl061_luminary_class_init,
-};
-
-static void pl061_register_types(void)
-{
-    type_register_static(&pl061_info);
-    type_register_static(&pl061_luminary_info);
-}
-
-type_init(pl061_register_types)
diff --git a/hw/pl080.c b/hw/pl080.c
deleted file mode 100644 (file)
index 00b66b4..0000000
+++ /dev/null
@@ -1,421 +0,0 @@
-/*
- * Arm PrimeCell PL080/PL081 DMA controller
- *
- * Copyright (c) 2006 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the GPL.
- */
-
-#include "hw/sysbus.h"
-
-#define PL080_MAX_CHANNELS 8
-#define PL080_CONF_E    0x1
-#define PL080_CONF_M1   0x2
-#define PL080_CONF_M2   0x4
-
-#define PL080_CCONF_H   0x40000
-#define PL080_CCONF_A   0x20000
-#define PL080_CCONF_L   0x10000
-#define PL080_CCONF_ITC 0x08000
-#define PL080_CCONF_IE  0x04000
-#define PL080_CCONF_E   0x00001
-
-#define PL080_CCTRL_I   0x80000000
-#define PL080_CCTRL_DI  0x08000000
-#define PL080_CCTRL_SI  0x04000000
-#define PL080_CCTRL_D   0x02000000
-#define PL080_CCTRL_S   0x01000000
-
-typedef struct {
-    uint32_t src;
-    uint32_t dest;
-    uint32_t lli;
-    uint32_t ctrl;
-    uint32_t conf;
-} pl080_channel;
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    uint8_t tc_int;
-    uint8_t tc_mask;
-    uint8_t err_int;
-    uint8_t err_mask;
-    uint32_t conf;
-    uint32_t sync;
-    uint32_t req_single;
-    uint32_t req_burst;
-    pl080_channel chan[PL080_MAX_CHANNELS];
-    int nchannels;
-    /* Flag to avoid recursive DMA invocations.  */
-    int running;
-    qemu_irq irq;
-} pl080_state;
-
-static const VMStateDescription vmstate_pl080_channel = {
-    .name = "pl080_channel",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32(src, pl080_channel),
-        VMSTATE_UINT32(dest, pl080_channel),
-        VMSTATE_UINT32(lli, pl080_channel),
-        VMSTATE_UINT32(ctrl, pl080_channel),
-        VMSTATE_UINT32(conf, pl080_channel),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_pl080 = {
-    .name = "pl080",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT8(tc_int, pl080_state),
-        VMSTATE_UINT8(tc_mask, pl080_state),
-        VMSTATE_UINT8(err_int, pl080_state),
-        VMSTATE_UINT8(err_mask, pl080_state),
-        VMSTATE_UINT32(conf, pl080_state),
-        VMSTATE_UINT32(sync, pl080_state),
-        VMSTATE_UINT32(req_single, pl080_state),
-        VMSTATE_UINT32(req_burst, pl080_state),
-        VMSTATE_UINT8(tc_int, pl080_state),
-        VMSTATE_UINT8(tc_int, pl080_state),
-        VMSTATE_UINT8(tc_int, pl080_state),
-        VMSTATE_STRUCT_ARRAY(chan, pl080_state, PL080_MAX_CHANNELS,
-                             1, vmstate_pl080_channel, pl080_channel),
-        VMSTATE_INT32(running, pl080_state),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const unsigned char pl080_id[] =
-{ 0x80, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 };
-
-static const unsigned char pl081_id[] =
-{ 0x81, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 };
-
-static void pl080_update(pl080_state *s)
-{
-    if ((s->tc_int & s->tc_mask)
-            || (s->err_int & s->err_mask))
-        qemu_irq_raise(s->irq);
-    else
-        qemu_irq_lower(s->irq);
-}
-
-static void pl080_run(pl080_state *s)
-{
-    int c;
-    int flow;
-    pl080_channel *ch;
-    int swidth;
-    int dwidth;
-    int xsize;
-    int n;
-    int src_id;
-    int dest_id;
-    int size;
-    uint8_t buff[4];
-    uint32_t req;
-
-    s->tc_mask = 0;
-    for (c = 0; c < s->nchannels; c++) {
-        if (s->chan[c].conf & PL080_CCONF_ITC)
-            s->tc_mask |= 1 << c;
-        if (s->chan[c].conf & PL080_CCONF_IE)
-            s->err_mask |= 1 << c;
-    }
-
-    if ((s->conf & PL080_CONF_E) == 0)
-        return;
-
-hw_error("DMA active\n");
-    /* If we are already in the middle of a DMA operation then indicate that
-       there may be new DMA requests and return immediately.  */
-    if (s->running) {
-        s->running++;
-        return;
-    }
-    s->running = 1;
-    while (s->running) {
-        for (c = 0; c < s->nchannels; c++) {
-            ch = &s->chan[c];
-again:
-            /* Test if thiws channel has any pending DMA requests.  */
-            if ((ch->conf & (PL080_CCONF_H | PL080_CCONF_E))
-                    != PL080_CCONF_E)
-                continue;
-            flow = (ch->conf >> 11) & 7;
-            if (flow >= 4) {
-                hw_error(
-                    "pl080_run: Peripheral flow control not implemented\n");
-            }
-            src_id = (ch->conf >> 1) & 0x1f;
-            dest_id = (ch->conf >> 6) & 0x1f;
-            size = ch->ctrl & 0xfff;
-            req = s->req_single | s->req_burst;
-            switch (flow) {
-            case 0:
-                break;
-            case 1:
-                if ((req & (1u << dest_id)) == 0)
-                    size = 0;
-                break;
-            case 2:
-                if ((req & (1u << src_id)) == 0)
-                    size = 0;
-                break;
-            case 3:
-                if ((req & (1u << src_id)) == 0
-                        || (req & (1u << dest_id)) == 0)
-                    size = 0;
-                break;
-            }
-            if (!size)
-                continue;
-
-            /* Transfer one element.  */
-            /* ??? Should transfer multiple elements for a burst request.  */
-            /* ??? Unclear what the proper behavior is when source and
-               destination widths are different.  */
-            swidth = 1 << ((ch->ctrl >> 18) & 7);
-            dwidth = 1 << ((ch->ctrl >> 21) & 7);
-            for (n = 0; n < dwidth; n+= swidth) {
-                cpu_physical_memory_read(ch->src, buff + n, swidth);
-                if (ch->ctrl & PL080_CCTRL_SI)
-                    ch->src += swidth;
-            }
-            xsize = (dwidth < swidth) ? swidth : dwidth;
-            /* ??? This may pad the value incorrectly for dwidth < 32.  */
-            for (n = 0; n < xsize; n += dwidth) {
-                cpu_physical_memory_write(ch->dest + n, buff + n, dwidth);
-                if (ch->ctrl & PL080_CCTRL_DI)
-                    ch->dest += swidth;
-            }
-
-            size--;
-            ch->ctrl = (ch->ctrl & 0xfffff000) | size;
-            if (size == 0) {
-                /* Transfer complete.  */
-                if (ch->lli) {
-                    ch->src = ldl_le_phys(ch->lli);
-                    ch->dest = ldl_le_phys(ch->lli + 4);
-                    ch->ctrl = ldl_le_phys(ch->lli + 12);
-                    ch->lli = ldl_le_phys(ch->lli + 8);
-                } else {
-                    ch->conf &= ~PL080_CCONF_E;
-                }
-                if (ch->ctrl & PL080_CCTRL_I) {
-                    s->tc_int |= 1 << c;
-                }
-            }
-            goto again;
-        }
-        if (--s->running)
-            s->running = 1;
-    }
-}
-
-static uint64_t pl080_read(void *opaque, hwaddr offset,
-                           unsigned size)
-{
-    pl080_state *s = (pl080_state *)opaque;
-    uint32_t i;
-    uint32_t mask;
-
-    if (offset >= 0xfe0 && offset < 0x1000) {
-        if (s->nchannels == 8) {
-            return pl080_id[(offset - 0xfe0) >> 2];
-        } else {
-            return pl081_id[(offset - 0xfe0) >> 2];
-        }
-    }
-    if (offset >= 0x100 && offset < 0x200) {
-        i = (offset & 0xe0) >> 5;
-        if (i >= s->nchannels)
-            goto bad_offset;
-        switch (offset >> 2) {
-        case 0: /* SrcAddr */
-            return s->chan[i].src;
-        case 1: /* DestAddr */
-            return s->chan[i].dest;
-        case 2: /* LLI */
-            return s->chan[i].lli;
-        case 3: /* Control */
-            return s->chan[i].ctrl;
-        case 4: /* Configuration */
-            return s->chan[i].conf;
-        default:
-            goto bad_offset;
-        }
-    }
-    switch (offset >> 2) {
-    case 0: /* IntStatus */
-        return (s->tc_int & s->tc_mask) | (s->err_int & s->err_mask);
-    case 1: /* IntTCStatus */
-        return (s->tc_int & s->tc_mask);
-    case 3: /* IntErrorStatus */
-        return (s->err_int & s->err_mask);
-    case 5: /* RawIntTCStatus */
-        return s->tc_int;
-    case 6: /* RawIntErrorStatus */
-        return s->err_int;
-    case 7: /* EnbldChns */
-        mask = 0;
-        for (i = 0; i < s->nchannels; i++) {
-            if (s->chan[i].conf & PL080_CCONF_E)
-                mask |= 1 << i;
-        }
-        return mask;
-    case 8: /* SoftBReq */
-    case 9: /* SoftSReq */
-    case 10: /* SoftLBReq */
-    case 11: /* SoftLSReq */
-        /* ??? Implement these. */
-        return 0;
-    case 12: /* Configuration */
-        return s->conf;
-    case 13: /* Sync */
-        return s->sync;
-    default:
-    bad_offset:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl080_read: Bad offset %x\n", (int)offset);
-        return 0;
-    }
-}
-
-static void pl080_write(void *opaque, hwaddr offset,
-                        uint64_t value, unsigned size)
-{
-    pl080_state *s = (pl080_state *)opaque;
-    int i;
-
-    if (offset >= 0x100 && offset < 0x200) {
-        i = (offset & 0xe0) >> 5;
-        if (i >= s->nchannels)
-            goto bad_offset;
-        switch (offset >> 2) {
-        case 0: /* SrcAddr */
-            s->chan[i].src = value;
-            break;
-        case 1: /* DestAddr */
-            s->chan[i].dest = value;
-            break;
-        case 2: /* LLI */
-            s->chan[i].lli = value;
-            break;
-        case 3: /* Control */
-            s->chan[i].ctrl = value;
-            break;
-        case 4: /* Configuration */
-            s->chan[i].conf = value;
-            pl080_run(s);
-            break;
-        }
-    }
-    switch (offset >> 2) {
-    case 2: /* IntTCClear */
-        s->tc_int &= ~value;
-        break;
-    case 4: /* IntErrorClear */
-        s->err_int &= ~value;
-        break;
-    case 8: /* SoftBReq */
-    case 9: /* SoftSReq */
-    case 10: /* SoftLBReq */
-    case 11: /* SoftLSReq */
-        /* ??? Implement these.  */
-        qemu_log_mask(LOG_UNIMP, "pl080_write: Soft DMA not implemented\n");
-        break;
-    case 12: /* Configuration */
-        s->conf = value;
-        if (s->conf & (PL080_CONF_M1 | PL080_CONF_M1)) {
-            qemu_log_mask(LOG_UNIMP,
-                          "pl080_write: Big-endian DMA not implemented\n");
-        }
-        pl080_run(s);
-        break;
-    case 13: /* Sync */
-        s->sync = value;
-        break;
-    default:
-    bad_offset:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl080_write: Bad offset %x\n", (int)offset);
-    }
-    pl080_update(s);
-}
-
-static const MemoryRegionOps pl080_ops = {
-    .read = pl080_read,
-    .write = pl080_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int pl08x_init(SysBusDevice *dev, int nchannels)
-{
-    pl080_state *s = FROM_SYSBUS(pl080_state, dev);
-
-    memory_region_init_io(&s->iomem, &pl080_ops, s, "pl080", 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-    sysbus_init_irq(dev, &s->irq);
-    s->nchannels = nchannels;
-    return 0;
-}
-
-static int pl080_init(SysBusDevice *dev)
-{
-    return pl08x_init(dev, 8);
-}
-
-static int pl081_init(SysBusDevice *dev)
-{
-    return pl08x_init(dev, 2);
-}
-
-static void pl080_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = pl080_init;
-    dc->no_user = 1;
-    dc->vmsd = &vmstate_pl080;
-}
-
-static const TypeInfo pl080_info = {
-    .name          = "pl080",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(pl080_state),
-    .class_init    = pl080_class_init,
-};
-
-static void pl081_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = pl081_init;
-    dc->no_user = 1;
-    dc->vmsd = &vmstate_pl080;
-}
-
-static const TypeInfo pl081_info = {
-    .name          = "pl081",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(pl080_state),
-    .class_init    = pl081_class_init,
-};
-
-/* The PL080 and PL081 are the same except for the number of channels
-   they implement (8 and 2 respectively).  */
-static void pl080_register_types(void)
-{
-    type_register_static(&pl080_info);
-    type_register_static(&pl081_info);
-}
-
-type_init(pl080_register_types)
diff --git a/hw/pl110.c b/hw/pl110.c
deleted file mode 100644 (file)
index fbef675..0000000
+++ /dev/null
@@ -1,533 +0,0 @@
-/*
- * Arm PrimeCell PL110 Color LCD Controller
- *
- * Copyright (c) 2005-2009 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the GNU LGPL
- */
-
-#include "hw/sysbus.h"
-#include "ui/console.h"
-#include "hw/framebuffer.h"
-#include "ui/pixel_ops.h"
-
-#define PL110_CR_EN   0x001
-#define PL110_CR_BGR  0x100
-#define PL110_CR_BEBO 0x200
-#define PL110_CR_BEPO 0x400
-#define PL110_CR_PWR  0x800
-
-enum pl110_bppmode
-{
-    BPP_1,
-    BPP_2,
-    BPP_4,
-    BPP_8,
-    BPP_16,
-    BPP_32,
-    BPP_16_565, /* PL111 only */
-    BPP_12      /* PL111 only */
-};
-
-
-/* The Versatile/PB uses a slightly modified PL110 controller.  */
-enum pl110_version
-{
-    PL110,
-    PL110_VERSATILE,
-    PL111
-};
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    QemuConsole *con;
-
-    int version;
-    uint32_t timing[4];
-    uint32_t cr;
-    uint32_t upbase;
-    uint32_t lpbase;
-    uint32_t int_status;
-    uint32_t int_mask;
-    int cols;
-    int rows;
-    enum pl110_bppmode bpp;
-    int invalidate;
-    uint32_t mux_ctrl;
-    uint32_t palette[256];
-    uint32_t raw_palette[128];
-    qemu_irq irq;
-} pl110_state;
-
-static int vmstate_pl110_post_load(void *opaque, int version_id);
-
-static const VMStateDescription vmstate_pl110 = {
-    .name = "pl110",
-    .version_id = 2,
-    .minimum_version_id = 1,
-    .post_load = vmstate_pl110_post_load,
-    .fields = (VMStateField[]) {
-        VMSTATE_INT32(version, pl110_state),
-        VMSTATE_UINT32_ARRAY(timing, pl110_state, 4),
-        VMSTATE_UINT32(cr, pl110_state),
-        VMSTATE_UINT32(upbase, pl110_state),
-        VMSTATE_UINT32(lpbase, pl110_state),
-        VMSTATE_UINT32(int_status, pl110_state),
-        VMSTATE_UINT32(int_mask, pl110_state),
-        VMSTATE_INT32(cols, pl110_state),
-        VMSTATE_INT32(rows, pl110_state),
-        VMSTATE_UINT32(bpp, pl110_state),
-        VMSTATE_INT32(invalidate, pl110_state),
-        VMSTATE_UINT32_ARRAY(palette, pl110_state, 256),
-        VMSTATE_UINT32_ARRAY(raw_palette, pl110_state, 128),
-        VMSTATE_UINT32_V(mux_ctrl, pl110_state, 2),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const unsigned char pl110_id[] =
-{ 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
-
-/* The Arm documentation (DDI0224C) says the CLDC on the Versatile board
-   has a different ID.  However Linux only looks for the normal ID.  */
-#if 0
-static const unsigned char pl110_versatile_id[] =
-{ 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
-#else
-#define pl110_versatile_id pl110_id
-#endif
-
-static const unsigned char pl111_id[] = {
-    0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1
-};
-
-/* Indexed by pl110_version */
-static const unsigned char *idregs[] = {
-    pl110_id,
-    pl110_versatile_id,
-    pl111_id
-};
-
-#define BITS 8
-#include "hw/pl110_template.h"
-#define BITS 15
-#include "hw/pl110_template.h"
-#define BITS 16
-#include "hw/pl110_template.h"
-#define BITS 24
-#include "hw/pl110_template.h"
-#define BITS 32
-#include "hw/pl110_template.h"
-
-static int pl110_enabled(pl110_state *s)
-{
-  return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
-}
-
-static void pl110_update_display(void *opaque)
-{
-    pl110_state *s = (pl110_state *)opaque;
-    DisplaySurface *surface = qemu_console_surface(s->con);
-    drawfn* fntable;
-    drawfn fn;
-    int dest_width;
-    int src_width;
-    int bpp_offset;
-    int first;
-    int last;
-
-    if (!pl110_enabled(s))
-        return;
-
-    switch (surface_bits_per_pixel(surface)) {
-    case 0:
-        return;
-    case 8:
-        fntable = pl110_draw_fn_8;
-        dest_width = 1;
-        break;
-    case 15:
-        fntable = pl110_draw_fn_15;
-        dest_width = 2;
-        break;
-    case 16:
-        fntable = pl110_draw_fn_16;
-        dest_width = 2;
-        break;
-    case 24:
-        fntable = pl110_draw_fn_24;
-        dest_width = 3;
-        break;
-    case 32:
-        fntable = pl110_draw_fn_32;
-        dest_width = 4;
-        break;
-    default:
-        fprintf(stderr, "pl110: Bad color depth\n");
-        exit(1);
-    }
-    if (s->cr & PL110_CR_BGR)
-        bpp_offset = 0;
-    else
-        bpp_offset = 24;
-
-    if ((s->version != PL111) && (s->bpp == BPP_16)) {
-        /* The PL110's native 16 bit mode is 5551; however
-         * most boards with a PL110 implement an external
-         * mux which allows bits to be reshuffled to give
-         * 565 format. The mux is typically controlled by
-         * an external system register.
-         * This is controlled by a GPIO input pin
-         * so boards can wire it up to their register.
-         *
-         * The PL111 straightforwardly implements both
-         * 5551 and 565 under control of the bpp field
-         * in the LCDControl register.
-         */
-        switch (s->mux_ctrl) {
-        case 3: /* 565 BGR */
-            bpp_offset = (BPP_16_565 - BPP_16);
-            break;
-        case 1: /* 5551 */
-            break;
-        case 0: /* 888; also if we have loaded vmstate from an old version */
-        case 2: /* 565 RGB */
-        default:
-            /* treat as 565 but honour BGR bit */
-            bpp_offset += (BPP_16_565 - BPP_16);
-            break;
-        }
-    }
-
-    if (s->cr & PL110_CR_BEBO)
-        fn = fntable[s->bpp + 8 + bpp_offset];
-    else if (s->cr & PL110_CR_BEPO)
-        fn = fntable[s->bpp + 16 + bpp_offset];
-    else
-        fn = fntable[s->bpp + bpp_offset];
-
-    src_width = s->cols;
-    switch (s->bpp) {
-    case BPP_1:
-        src_width >>= 3;
-        break;
-    case BPP_2:
-        src_width >>= 2;
-        break;
-    case BPP_4:
-        src_width >>= 1;
-        break;
-    case BPP_8:
-        break;
-    case BPP_16:
-    case BPP_16_565:
-    case BPP_12:
-        src_width <<= 1;
-        break;
-    case BPP_32:
-        src_width <<= 2;
-        break;
-    }
-    dest_width *= s->cols;
-    first = 0;
-    framebuffer_update_display(surface, sysbus_address_space(&s->busdev),
-                               s->upbase, s->cols, s->rows,
-                               src_width, dest_width, 0,
-                               s->invalidate,
-                               fn, s->palette,
-                               &first, &last);
-    if (first >= 0) {
-        dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1);
-    }
-    s->invalidate = 0;
-}
-
-static void pl110_invalidate_display(void * opaque)
-{
-    pl110_state *s = (pl110_state *)opaque;
-    s->invalidate = 1;
-    if (pl110_enabled(s)) {
-        qemu_console_resize(s->con, s->cols, s->rows);
-    }
-}
-
-static void pl110_update_palette(pl110_state *s, int n)
-{
-    DisplaySurface *surface = qemu_console_surface(s->con);
-    int i;
-    uint32_t raw;
-    unsigned int r, g, b;
-
-    raw = s->raw_palette[n];
-    n <<= 1;
-    for (i = 0; i < 2; i++) {
-        r = (raw & 0x1f) << 3;
-        raw >>= 5;
-        g = (raw & 0x1f) << 3;
-        raw >>= 5;
-        b = (raw & 0x1f) << 3;
-        /* The I bit is ignored.  */
-        raw >>= 6;
-        switch (surface_bits_per_pixel(surface)) {
-        case 8:
-            s->palette[n] = rgb_to_pixel8(r, g, b);
-            break;
-        case 15:
-            s->palette[n] = rgb_to_pixel15(r, g, b);
-            break;
-        case 16:
-            s->palette[n] = rgb_to_pixel16(r, g, b);
-            break;
-        case 24:
-        case 32:
-            s->palette[n] = rgb_to_pixel32(r, g, b);
-            break;
-        }
-        n++;
-    }
-}
-
-static void pl110_resize(pl110_state *s, int width, int height)
-{
-    if (width != s->cols || height != s->rows) {
-        if (pl110_enabled(s)) {
-            qemu_console_resize(s->con, width, height);
-        }
-    }
-    s->cols = width;
-    s->rows = height;
-}
-
-/* Update interrupts.  */
-static void pl110_update(pl110_state *s)
-{
-  /* TODO: Implement interrupts.  */
-}
-
-static uint64_t pl110_read(void *opaque, hwaddr offset,
-                           unsigned size)
-{
-    pl110_state *s = (pl110_state *)opaque;
-
-    if (offset >= 0xfe0 && offset < 0x1000) {
-        return idregs[s->version][(offset - 0xfe0) >> 2];
-    }
-    if (offset >= 0x200 && offset < 0x400) {
-        return s->raw_palette[(offset - 0x200) >> 2];
-    }
-    switch (offset >> 2) {
-    case 0: /* LCDTiming0 */
-        return s->timing[0];
-    case 1: /* LCDTiming1 */
-        return s->timing[1];
-    case 2: /* LCDTiming2 */
-        return s->timing[2];
-    case 3: /* LCDTiming3 */
-        return s->timing[3];
-    case 4: /* LCDUPBASE */
-        return s->upbase;
-    case 5: /* LCDLPBASE */
-        return s->lpbase;
-    case 6: /* LCDIMSC */
-        if (s->version != PL110) {
-            return s->cr;
-        }
-        return s->int_mask;
-    case 7: /* LCDControl */
-        if (s->version != PL110) {
-            return s->int_mask;
-        }
-        return s->cr;
-    case 8: /* LCDRIS */
-        return s->int_status;
-    case 9: /* LCDMIS */
-        return s->int_status & s->int_mask;
-    case 11: /* LCDUPCURR */
-        /* TODO: Implement vertical refresh.  */
-        return s->upbase;
-    case 12: /* LCDLPCURR */
-        return s->lpbase;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl110_read: Bad offset %x\n", (int)offset);
-        return 0;
-    }
-}
-
-static void pl110_write(void *opaque, hwaddr offset,
-                        uint64_t val, unsigned size)
-{
-    pl110_state *s = (pl110_state *)opaque;
-    int n;
-
-    /* For simplicity invalidate the display whenever a control register
-       is written to.  */
-    s->invalidate = 1;
-    if (offset >= 0x200 && offset < 0x400) {
-        /* Palette.  */
-        n = (offset - 0x200) >> 2;
-        s->raw_palette[(offset - 0x200) >> 2] = val;
-        pl110_update_palette(s, n);
-        return;
-    }
-    switch (offset >> 2) {
-    case 0: /* LCDTiming0 */
-        s->timing[0] = val;
-        n = ((val & 0xfc) + 4) * 4;
-        pl110_resize(s, n, s->rows);
-        break;
-    case 1: /* LCDTiming1 */
-        s->timing[1] = val;
-        n = (val & 0x3ff) + 1;
-        pl110_resize(s, s->cols, n);
-        break;
-    case 2: /* LCDTiming2 */
-        s->timing[2] = val;
-        break;
-    case 3: /* LCDTiming3 */
-        s->timing[3] = val;
-        break;
-    case 4: /* LCDUPBASE */
-        s->upbase = val;
-        break;
-    case 5: /* LCDLPBASE */
-        s->lpbase = val;
-        break;
-    case 6: /* LCDIMSC */
-        if (s->version != PL110) {
-            goto control;
-        }
-    imsc:
-        s->int_mask = val;
-        pl110_update(s);
-        break;
-    case 7: /* LCDControl */
-        if (s->version != PL110) {
-            goto imsc;
-        }
-    control:
-        s->cr = val;
-        s->bpp = (val >> 1) & 7;
-        if (pl110_enabled(s)) {
-            qemu_console_resize(s->con, s->cols, s->rows);
-        }
-        break;
-    case 10: /* LCDICR */
-        s->int_status &= ~val;
-        pl110_update(s);
-        break;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl110_write: Bad offset %x\n", (int)offset);
-    }
-}
-
-static const MemoryRegionOps pl110_ops = {
-    .read = pl110_read,
-    .write = pl110_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void pl110_mux_ctrl_set(void *opaque, int line, int level)
-{
-    pl110_state *s = (pl110_state *)opaque;
-    s->mux_ctrl = level;
-}
-
-static int vmstate_pl110_post_load(void *opaque, int version_id)
-{
-    pl110_state *s = opaque;
-    /* Make sure we redraw, and at the right size */
-    pl110_invalidate_display(s);
-    return 0;
-}
-
-static int pl110_init(SysBusDevice *dev)
-{
-    pl110_state *s = FROM_SYSBUS(pl110_state, dev);
-
-    memory_region_init_io(&s->iomem, &pl110_ops, s, "pl110", 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-    sysbus_init_irq(dev, &s->irq);
-    qdev_init_gpio_in(&s->busdev.qdev, pl110_mux_ctrl_set, 1);
-    s->con = graphic_console_init(pl110_update_display,
-                                  pl110_invalidate_display,
-                                  NULL, NULL, s);
-    return 0;
-}
-
-static int pl110_versatile_init(SysBusDevice *dev)
-{
-    pl110_state *s = FROM_SYSBUS(pl110_state, dev);
-    s->version = PL110_VERSATILE;
-    return pl110_init(dev);
-}
-
-static int pl111_init(SysBusDevice *dev)
-{
-    pl110_state *s = FROM_SYSBUS(pl110_state, dev);
-    s->version = PL111;
-    return pl110_init(dev);
-}
-
-static void pl110_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = pl110_init;
-    dc->no_user = 1;
-    dc->vmsd = &vmstate_pl110;
-}
-
-static const TypeInfo pl110_info = {
-    .name          = "pl110",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(pl110_state),
-    .class_init    = pl110_class_init,
-};
-
-static void pl110_versatile_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = pl110_versatile_init;
-    dc->no_user = 1;
-    dc->vmsd = &vmstate_pl110;
-}
-
-static const TypeInfo pl110_versatile_info = {
-    .name          = "pl110_versatile",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(pl110_state),
-    .class_init    = pl110_versatile_class_init,
-};
-
-static void pl111_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = pl111_init;
-    dc->no_user = 1;
-    dc->vmsd = &vmstate_pl110;
-}
-
-static const TypeInfo pl111_info = {
-    .name          = "pl111",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(pl110_state),
-    .class_init    = pl111_class_init,
-};
-
-static void pl110_register_types(void)
-{
-    type_register_static(&pl110_info);
-    type_register_static(&pl110_versatile_info);
-    type_register_static(&pl111_info);
-}
-
-type_init(pl110_register_types)
diff --git a/hw/pl181.c b/hw/pl181.c
deleted file mode 100644 (file)
index 2527296..0000000
+++ /dev/null
@@ -1,515 +0,0 @@
-/*
- * Arm PrimeCell PL181 MultiMedia Card Interface
- *
- * Copyright (c) 2007 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the GPL.
- */
-
-#include "sysemu/blockdev.h"
-#include "hw/sysbus.h"
-#include "hw/sd.h"
-
-//#define DEBUG_PL181 1
-
-#ifdef DEBUG_PL181
-#define DPRINTF(fmt, ...) \
-do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#endif
-
-#define PL181_FIFO_LEN 16
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    SDState *card;
-    uint32_t clock;
-    uint32_t power;
-    uint32_t cmdarg;
-    uint32_t cmd;
-    uint32_t datatimer;
-    uint32_t datalength;
-    uint32_t respcmd;
-    uint32_t response[4];
-    uint32_t datactrl;
-    uint32_t datacnt;
-    uint32_t status;
-    uint32_t mask[2];
-    int32_t fifo_pos;
-    int32_t fifo_len;
-    /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
-       while it is reading the FIFO.  We hack around this be defering
-       subsequent transfers until after the driver polls the status word.
-       http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
-     */
-    int32_t linux_hack;
-    uint32_t fifo[PL181_FIFO_LEN];
-    qemu_irq irq[2];
-    /* GPIO outputs for 'card is readonly' and 'card inserted' */
-    qemu_irq cardstatus[2];
-} pl181_state;
-
-static const VMStateDescription vmstate_pl181 = {
-    .name = "pl181",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32(clock, pl181_state),
-        VMSTATE_UINT32(power, pl181_state),
-        VMSTATE_UINT32(cmdarg, pl181_state),
-        VMSTATE_UINT32(cmd, pl181_state),
-        VMSTATE_UINT32(datatimer, pl181_state),
-        VMSTATE_UINT32(datalength, pl181_state),
-        VMSTATE_UINT32(respcmd, pl181_state),
-        VMSTATE_UINT32_ARRAY(response, pl181_state, 4),
-        VMSTATE_UINT32(datactrl, pl181_state),
-        VMSTATE_UINT32(datacnt, pl181_state),
-        VMSTATE_UINT32(status, pl181_state),
-        VMSTATE_UINT32_ARRAY(mask, pl181_state, 2),
-        VMSTATE_INT32(fifo_pos, pl181_state),
-        VMSTATE_INT32(fifo_len, pl181_state),
-        VMSTATE_INT32(linux_hack, pl181_state),
-        VMSTATE_UINT32_ARRAY(fifo, pl181_state, PL181_FIFO_LEN),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-#define PL181_CMD_INDEX     0x3f
-#define PL181_CMD_RESPONSE  (1 << 6)
-#define PL181_CMD_LONGRESP  (1 << 7)
-#define PL181_CMD_INTERRUPT (1 << 8)
-#define PL181_CMD_PENDING   (1 << 9)
-#define PL181_CMD_ENABLE    (1 << 10)
-
-#define PL181_DATA_ENABLE             (1 << 0)
-#define PL181_DATA_DIRECTION          (1 << 1)
-#define PL181_DATA_MODE               (1 << 2)
-#define PL181_DATA_DMAENABLE          (1 << 3)
-
-#define PL181_STATUS_CMDCRCFAIL       (1 << 0)
-#define PL181_STATUS_DATACRCFAIL      (1 << 1)
-#define PL181_STATUS_CMDTIMEOUT       (1 << 2)
-#define PL181_STATUS_DATATIMEOUT      (1 << 3)
-#define PL181_STATUS_TXUNDERRUN       (1 << 4)
-#define PL181_STATUS_RXOVERRUN        (1 << 5)
-#define PL181_STATUS_CMDRESPEND       (1 << 6)
-#define PL181_STATUS_CMDSENT          (1 << 7)
-#define PL181_STATUS_DATAEND          (1 << 8)
-#define PL181_STATUS_DATABLOCKEND     (1 << 10)
-#define PL181_STATUS_CMDACTIVE        (1 << 11)
-#define PL181_STATUS_TXACTIVE         (1 << 12)
-#define PL181_STATUS_RXACTIVE         (1 << 13)
-#define PL181_STATUS_TXFIFOHALFEMPTY  (1 << 14)
-#define PL181_STATUS_RXFIFOHALFFULL   (1 << 15)
-#define PL181_STATUS_TXFIFOFULL       (1 << 16)
-#define PL181_STATUS_RXFIFOFULL       (1 << 17)
-#define PL181_STATUS_TXFIFOEMPTY      (1 << 18)
-#define PL181_STATUS_RXFIFOEMPTY      (1 << 19)
-#define PL181_STATUS_TXDATAAVLBL      (1 << 20)
-#define PL181_STATUS_RXDATAAVLBL      (1 << 21)
-
-#define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
-                             |PL181_STATUS_TXFIFOHALFEMPTY \
-                             |PL181_STATUS_TXFIFOFULL \
-                             |PL181_STATUS_TXFIFOEMPTY \
-                             |PL181_STATUS_TXDATAAVLBL)
-#define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
-                             |PL181_STATUS_RXFIFOHALFFULL \
-                             |PL181_STATUS_RXFIFOFULL \
-                             |PL181_STATUS_RXFIFOEMPTY \
-                             |PL181_STATUS_RXDATAAVLBL)
-
-static const unsigned char pl181_id[] =
-{ 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
-
-static void pl181_update(pl181_state *s)
-{
-    int i;
-    for (i = 0; i < 2; i++) {
-        qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
-    }
-}
-
-static void pl181_fifo_push(pl181_state *s, uint32_t value)
-{
-    int n;
-
-    if (s->fifo_len == PL181_FIFO_LEN) {
-        fprintf(stderr, "pl181: FIFO overflow\n");
-        return;
-    }
-    n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
-    s->fifo_len++;
-    s->fifo[n] = value;
-    DPRINTF("FIFO push %08x\n", (int)value);
-}
-
-static uint32_t pl181_fifo_pop(pl181_state *s)
-{
-    uint32_t value;
-
-    if (s->fifo_len == 0) {
-        fprintf(stderr, "pl181: FIFO underflow\n");
-        return 0;
-    }
-    value = s->fifo[s->fifo_pos];
-    s->fifo_len--;
-    s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
-    DPRINTF("FIFO pop %08x\n", (int)value);
-    return value;
-}
-
-static void pl181_send_command(pl181_state *s)
-{
-    SDRequest request;
-    uint8_t response[16];
-    int rlen;
-
-    request.cmd = s->cmd & PL181_CMD_INDEX;
-    request.arg = s->cmdarg;
-    DPRINTF("Command %d %08x\n", request.cmd, request.arg);
-    rlen = sd_do_command(s->card, &request, response);
-    if (rlen < 0)
-        goto error;
-    if (s->cmd & PL181_CMD_RESPONSE) {
-#define RWORD(n) ((response[n] << 24) | (response[n + 1] << 16) \
-                  | (response[n + 2] << 8) | response[n + 3])
-        if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
-            goto error;
-        if (rlen != 4 && rlen != 16)
-            goto error;
-        s->response[0] = RWORD(0);
-        if (rlen == 4) {
-            s->response[1] = s->response[2] = s->response[3] = 0;
-        } else {
-            s->response[1] = RWORD(4);
-            s->response[2] = RWORD(8);
-            s->response[3] = RWORD(12) & ~1;
-        }
-        DPRINTF("Response received\n");
-        s->status |= PL181_STATUS_CMDRESPEND;
-#undef RWORD
-    } else {
-        DPRINTF("Command sent\n");
-        s->status |= PL181_STATUS_CMDSENT;
-    }
-    return;
-
-error:
-    DPRINTF("Timeout\n");
-    s->status |= PL181_STATUS_CMDTIMEOUT;
-}
-
-/* Transfer data between the card and the FIFO.  This is complicated by
-   the FIFO holding 32-bit words and the card taking data in single byte
-   chunks.  FIFO bytes are transferred in little-endian order.  */
-
-static void pl181_fifo_run(pl181_state *s)
-{
-    uint32_t bits;
-    uint32_t value = 0;
-    int n;
-    int is_read;
-
-    is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
-    if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card))
-            && !s->linux_hack) {
-        if (is_read) {
-            n = 0;
-            while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
-                value |= (uint32_t)sd_read_data(s->card) << (n * 8);
-                s->datacnt--;
-                n++;
-                if (n == 4) {
-                    pl181_fifo_push(s, value);
-                    n = 0;
-                    value = 0;
-                }
-            }
-            if (n != 0) {
-                pl181_fifo_push(s, value);
-            }
-        } else { /* write */
-            n = 0;
-            while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
-                if (n == 0) {
-                    value = pl181_fifo_pop(s);
-                    n = 4;
-                }
-                n--;
-                s->datacnt--;
-                sd_write_data(s->card, value & 0xff);
-                value >>= 8;
-            }
-        }
-    }
-    s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
-    if (s->datacnt == 0) {
-        s->status |= PL181_STATUS_DATAEND;
-        /* HACK: */
-        s->status |= PL181_STATUS_DATABLOCKEND;
-        DPRINTF("Transfer Complete\n");
-    }
-    if (s->datacnt == 0 && s->fifo_len == 0) {
-        s->datactrl &= ~PL181_DATA_ENABLE;
-        DPRINTF("Data engine idle\n");
-    } else {
-        /* Update FIFO bits.  */
-        bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
-        if (s->fifo_len == 0) {
-            bits |= PL181_STATUS_TXFIFOEMPTY;
-            bits |= PL181_STATUS_RXFIFOEMPTY;
-        } else {
-            bits |= PL181_STATUS_TXDATAAVLBL;
-            bits |= PL181_STATUS_RXDATAAVLBL;
-        }
-        if (s->fifo_len == 16) {
-            bits |= PL181_STATUS_TXFIFOFULL;
-            bits |= PL181_STATUS_RXFIFOFULL;
-        }
-        if (s->fifo_len <= 8) {
-            bits |= PL181_STATUS_TXFIFOHALFEMPTY;
-        }
-        if (s->fifo_len >= 8) {
-            bits |= PL181_STATUS_RXFIFOHALFFULL;
-        }
-        if (s->datactrl & PL181_DATA_DIRECTION) {
-            bits &= PL181_STATUS_RX_FIFO;
-        } else {
-            bits &= PL181_STATUS_TX_FIFO;
-        }
-        s->status |= bits;
-    }
-}
-
-static uint64_t pl181_read(void *opaque, hwaddr offset,
-                           unsigned size)
-{
-    pl181_state *s = (pl181_state *)opaque;
-    uint32_t tmp;
-
-    if (offset >= 0xfe0 && offset < 0x1000) {
-        return pl181_id[(offset - 0xfe0) >> 2];
-    }
-    switch (offset) {
-    case 0x00: /* Power */
-        return s->power;
-    case 0x04: /* Clock */
-        return s->clock;
-    case 0x08: /* Argument */
-        return s->cmdarg;
-    case 0x0c: /* Command */
-        return s->cmd;
-    case 0x10: /* RespCmd */
-        return s->respcmd;
-    case 0x14: /* Response0 */
-        return s->response[0];
-    case 0x18: /* Response1 */
-        return s->response[1];
-    case 0x1c: /* Response2 */
-        return s->response[2];
-    case 0x20: /* Response3 */
-        return s->response[3];
-    case 0x24: /* DataTimer */
-        return s->datatimer;
-    case 0x28: /* DataLength */
-        return s->datalength;
-    case 0x2c: /* DataCtrl */
-        return s->datactrl;
-    case 0x30: /* DataCnt */
-        return s->datacnt;
-    case 0x34: /* Status */
-        tmp = s->status;
-        if (s->linux_hack) {
-            s->linux_hack = 0;
-            pl181_fifo_run(s);
-            pl181_update(s);
-        }
-        return tmp;
-    case 0x3c: /* Mask0 */
-        return s->mask[0];
-    case 0x40: /* Mask1 */
-        return s->mask[1];
-    case 0x48: /* FifoCnt */
-        /* The documentation is somewhat vague about exactly what FifoCnt
-           does.  On real hardware it appears to be when decrememnted
-           when a word is transferred between the FIFO and the serial
-           data engine.  DataCnt is decremented after each byte is
-           transferred between the serial engine and the card.
-           We don't emulate this level of detail, so both can be the same.  */
-        tmp = (s->datacnt + 3) >> 2;
-        if (s->linux_hack) {
-            s->linux_hack = 0;
-            pl181_fifo_run(s);
-            pl181_update(s);
-        }
-        return tmp;
-    case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
-    case 0x90: case 0x94: case 0x98: case 0x9c:
-    case 0xa0: case 0xa4: case 0xa8: case 0xac:
-    case 0xb0: case 0xb4: case 0xb8: case 0xbc:
-        if (s->fifo_len == 0) {
-            qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n");
-            return 0;
-        } else {
-            uint32_t value;
-            value = pl181_fifo_pop(s);
-            s->linux_hack = 1;
-            pl181_fifo_run(s);
-            pl181_update(s);
-            return value;
-        }
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl181_read: Bad offset %x\n", (int)offset);
-        return 0;
-    }
-}
-
-static void pl181_write(void *opaque, hwaddr offset,
-                        uint64_t value, unsigned size)
-{
-    pl181_state *s = (pl181_state *)opaque;
-
-    switch (offset) {
-    case 0x00: /* Power */
-        s->power = value & 0xff;
-        break;
-    case 0x04: /* Clock */
-        s->clock = value & 0xff;
-        break;
-    case 0x08: /* Argument */
-        s->cmdarg = value;
-        break;
-    case 0x0c: /* Command */
-        s->cmd = value;
-        if (s->cmd & PL181_CMD_ENABLE) {
-            if (s->cmd & PL181_CMD_INTERRUPT) {
-                qemu_log_mask(LOG_UNIMP,
-                              "pl181: Interrupt mode not implemented\n");
-            } if (s->cmd & PL181_CMD_PENDING) {
-                qemu_log_mask(LOG_UNIMP,
-                              "pl181: Pending commands not implemented\n");
-            } else {
-                pl181_send_command(s);
-                pl181_fifo_run(s);
-            }
-            /* The command has completed one way or the other.  */
-            s->cmd &= ~PL181_CMD_ENABLE;
-        }
-        break;
-    case 0x24: /* DataTimer */
-        s->datatimer = value;
-        break;
-    case 0x28: /* DataLength */
-        s->datalength = value & 0xffff;
-        break;
-    case 0x2c: /* DataCtrl */
-        s->datactrl = value & 0xff;
-        if (value & PL181_DATA_ENABLE) {
-            s->datacnt = s->datalength;
-            pl181_fifo_run(s);
-        }
-        break;
-    case 0x38: /* Clear */
-        s->status &= ~(value & 0x7ff);
-        break;
-    case 0x3c: /* Mask0 */
-        s->mask[0] = value;
-        break;
-    case 0x40: /* Mask1 */
-        s->mask[1] = value;
-        break;
-    case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
-    case 0x90: case 0x94: case 0x98: case 0x9c:
-    case 0xa0: case 0xa4: case 0xa8: case 0xac:
-    case 0xb0: case 0xb4: case 0xb8: case 0xbc:
-        if (s->datacnt == 0) {
-            qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n");
-        } else {
-            pl181_fifo_push(s, value);
-            pl181_fifo_run(s);
-        }
-        break;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl181_write: Bad offset %x\n", (int)offset);
-    }
-    pl181_update(s);
-}
-
-static const MemoryRegionOps pl181_ops = {
-    .read = pl181_read,
-    .write = pl181_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void pl181_reset(DeviceState *d)
-{
-    pl181_state *s = DO_UPCAST(pl181_state, busdev.qdev, d);
-
-    s->power = 0;
-    s->cmdarg = 0;
-    s->cmd = 0;
-    s->datatimer = 0;
-    s->datalength = 0;
-    s->respcmd = 0;
-    s->response[0] = 0;
-    s->response[1] = 0;
-    s->response[2] = 0;
-    s->response[3] = 0;
-    s->datatimer = 0;
-    s->datalength = 0;
-    s->datactrl = 0;
-    s->datacnt = 0;
-    s->status = 0;
-    s->linux_hack = 0;
-    s->mask[0] = 0;
-    s->mask[1] = 0;
-
-    /* We can assume our GPIO outputs have been wired up now */
-    sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]);
-}
-
-static int pl181_init(SysBusDevice *dev)
-{
-    pl181_state *s = FROM_SYSBUS(pl181_state, dev);
-    DriveInfo *dinfo;
-
-    memory_region_init_io(&s->iomem, &pl181_ops, s, "pl181", 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-    sysbus_init_irq(dev, &s->irq[0]);
-    sysbus_init_irq(dev, &s->irq[1]);
-    qdev_init_gpio_out(&s->busdev.qdev, s->cardstatus, 2);
-    dinfo = drive_get_next(IF_SD);
-    s->card = sd_init(dinfo ? dinfo->bdrv : NULL, 0);
-    return 0;
-}
-
-static void pl181_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-    DeviceClass *k = DEVICE_CLASS(klass);
-
-    sdc->init = pl181_init;
-    k->vmsd = &vmstate_pl181;
-    k->reset = pl181_reset;
-    k->no_user = 1;
-}
-
-static const TypeInfo pl181_info = {
-    .name          = "pl181",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(pl181_state),
-    .class_init    = pl181_class_init,
-};
-
-static void pl181_register_types(void)
-{
-    type_register_static(&pl181_info);
-}
-
-type_init(pl181_register_types)
diff --git a/hw/pl190.c b/hw/pl190.c
deleted file mode 100644 (file)
index 9610673..0000000
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Arm PrimeCell PL190 Vector Interrupt Controller
- *
- * Copyright (c) 2006 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the GPL.
- */
-
-#include "hw/sysbus.h"
-
-/* The number of virtual priority levels.  16 user vectors plus the
-   unvectored IRQ.  Chained interrupts would require an additional level
-   if implemented.  */
-
-#define PL190_NUM_PRIO 17
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    uint32_t level;
-    uint32_t soft_level;
-    uint32_t irq_enable;
-    uint32_t fiq_select;
-    uint8_t vect_control[16];
-    uint32_t vect_addr[PL190_NUM_PRIO];
-    /* Mask containing interrupts with higher priority than this one.  */
-    uint32_t prio_mask[PL190_NUM_PRIO + 1];
-    int protected;
-    /* Current priority level.  */
-    int priority;
-    int prev_prio[PL190_NUM_PRIO];
-    qemu_irq irq;
-    qemu_irq fiq;
-} pl190_state;
-
-static const unsigned char pl190_id[] =
-{ 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 };
-
-static inline uint32_t pl190_irq_level(pl190_state *s)
-{
-    return (s->level | s->soft_level) & s->irq_enable & ~s->fiq_select;
-}
-
-/* Update interrupts.  */
-static void pl190_update(pl190_state *s)
-{
-    uint32_t level = pl190_irq_level(s);
-    int set;
-
-    set = (level & s->prio_mask[s->priority]) != 0;
-    qemu_set_irq(s->irq, set);
-    set = ((s->level | s->soft_level) & s->fiq_select) != 0;
-    qemu_set_irq(s->fiq, set);
-}
-
-static void pl190_set_irq(void *opaque, int irq, int level)
-{
-    pl190_state *s = (pl190_state *)opaque;
-
-    if (level)
-        s->level |= 1u << irq;
-    else
-        s->level &= ~(1u << irq);
-    pl190_update(s);
-}
-
-static void pl190_update_vectors(pl190_state *s)
-{
-    uint32_t mask;
-    int i;
-    int n;
-
-    mask = 0;
-    for (i = 0; i < 16; i++)
-      {
-        s->prio_mask[i] = mask;
-        if (s->vect_control[i] & 0x20)
-          {
-            n = s->vect_control[i] & 0x1f;
-            mask |= 1 << n;
-          }
-      }
-    s->prio_mask[16] = mask;
-    pl190_update(s);
-}
-
-static uint64_t pl190_read(void *opaque, hwaddr offset,
-                           unsigned size)
-{
-    pl190_state *s = (pl190_state *)opaque;
-    int i;
-
-    if (offset >= 0xfe0 && offset < 0x1000) {
-        return pl190_id[(offset - 0xfe0) >> 2];
-    }
-    if (offset >= 0x100 && offset < 0x140) {
-        return s->vect_addr[(offset - 0x100) >> 2];
-    }
-    if (offset >= 0x200 && offset < 0x240) {
-        return s->vect_control[(offset - 0x200) >> 2];
-    }
-    switch (offset >> 2) {
-    case 0: /* IRQSTATUS */
-        return pl190_irq_level(s);
-    case 1: /* FIQSATUS */
-        return (s->level | s->soft_level) & s->fiq_select;
-    case 2: /* RAWINTR */
-        return s->level | s->soft_level;
-    case 3: /* INTSELECT */
-        return s->fiq_select;
-    case 4: /* INTENABLE */
-        return s->irq_enable;
-    case 6: /* SOFTINT */
-        return s->soft_level;
-    case 8: /* PROTECTION */
-        return s->protected;
-    case 12: /* VECTADDR */
-        /* Read vector address at the start of an ISR.  Increases the
-         * current priority level to that of the current interrupt.
-         *
-         * Since an enabled interrupt X at priority P causes prio_mask[Y]
-         * to have bit X set for all Y > P, this loop will stop with
-         * i == the priority of the highest priority set interrupt.
-         */
-        for (i = 0; i < s->priority; i++) {
-            if ((s->level | s->soft_level) & s->prio_mask[i + 1]) {
-                break;
-            }
-        }
-
-        /* Reading this value with no pending interrupts is undefined.
-           We return the default address.  */
-        if (i == PL190_NUM_PRIO)
-          return s->vect_addr[16];
-        if (i < s->priority)
-          {
-            s->prev_prio[i] = s->priority;
-            s->priority = i;
-            pl190_update(s);
-          }
-        return s->vect_addr[s->priority];
-    case 13: /* DEFVECTADDR */
-        return s->vect_addr[16];
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "pl190_read: Bad offset %x\n", (int)offset);
-        return 0;
-    }
-}
-
-static void pl190_write(void *opaque, hwaddr offset,
-                        uint64_t val, unsigned size)
-{
-    pl190_state *s = (pl190_state *)opaque;
-
-    if (offset >= 0x100 && offset < 0x140) {
-        s->vect_addr[(offset - 0x100) >> 2] = val;
-        pl190_update_vectors(s);
-        return;
-    }
-    if (offset >= 0x200 && offset < 0x240) {
-        s->vect_control[(offset - 0x200) >> 2] = val;
-        pl190_update_vectors(s);
-        return;
-    }
-    switch (offset >> 2) {
-    case 0: /* SELECT */
-        /* This is a readonly register, but linux tries to write to it
-           anyway.  Ignore the write.  */
-        break;
-    case 3: /* INTSELECT */
-        s->fiq_select = val;
-        break;
-    case 4: /* INTENABLE */
-        s->irq_enable |= val;
-        break;
-    case 5: /* INTENCLEAR */
-        s->irq_enable &= ~val;
-        break;
-    case 6: /* SOFTINT */
-        s->soft_level |= val;
-        break;
-    case 7: /* SOFTINTCLEAR */
-        s->soft_level &= ~val;
-        break;
-    case 8: /* PROTECTION */
-        /* TODO: Protection (supervisor only access) is not implemented.  */
-        s->protected = val & 1;
-        break;
-    case 12: /* VECTADDR */
-        /* Restore the previous priority level.  The value written is
-           ignored.  */
-        if (s->priority < PL190_NUM_PRIO)
-            s->priority = s->prev_prio[s->priority];
-        break;
-    case 13: /* DEFVECTADDR */
-        s->vect_addr[16] = val;
-        break;
-    case 0xc0: /* ITCR */
-        if (val) {
-            qemu_log_mask(LOG_UNIMP, "pl190: Test mode not implemented\n");
-        }
-        break;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                     "pl190_write: Bad offset %x\n", (int)offset);
-        return;
-    }
-    pl190_update(s);
-}
-
-static const MemoryRegionOps pl190_ops = {
-    .read = pl190_read,
-    .write = pl190_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void pl190_reset(DeviceState *d)
-{
-  pl190_state *s = DO_UPCAST(pl190_state, busdev.qdev, d);
-  int i;
-
-  for (i = 0; i < 16; i++)
-    {
-      s->vect_addr[i] = 0;
-      s->vect_control[i] = 0;
-    }
-  s->vect_addr[16] = 0;
-  s->prio_mask[17] = 0xffffffff;
-  s->priority = PL190_NUM_PRIO;
-  pl190_update_vectors(s);
-}
-
-static int pl190_init(SysBusDevice *dev)
-{
-    pl190_state *s = FROM_SYSBUS(pl190_state, dev);
-
-    memory_region_init_io(&s->iomem, &pl190_ops, s, "pl190", 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-    qdev_init_gpio_in(&dev->qdev, pl190_set_irq, 32);
-    sysbus_init_irq(dev, &s->irq);
-    sysbus_init_irq(dev, &s->fiq);
-    return 0;
-}
-
-static const VMStateDescription vmstate_pl190 = {
-    .name = "pl190",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32(level, pl190_state),
-        VMSTATE_UINT32(soft_level, pl190_state),
-        VMSTATE_UINT32(irq_enable, pl190_state),
-        VMSTATE_UINT32(fiq_select, pl190_state),
-        VMSTATE_UINT8_ARRAY(vect_control, pl190_state, 16),
-        VMSTATE_UINT32_ARRAY(vect_addr, pl190_state, PL190_NUM_PRIO),
-        VMSTATE_UINT32_ARRAY(prio_mask, pl190_state, PL190_NUM_PRIO+1),
-        VMSTATE_INT32(protected, pl190_state),
-        VMSTATE_INT32(priority, pl190_state),
-        VMSTATE_INT32_ARRAY(prev_prio, pl190_state, PL190_NUM_PRIO),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void pl190_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = pl190_init;
-    dc->no_user = 1;
-    dc->reset = pl190_reset;
-    dc->vmsd = &vmstate_pl190;
-}
-
-static const TypeInfo pl190_info = {
-    .name          = "pl190",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(pl190_state),
-    .class_init    = pl190_class_init,
-};
-
-static void pl190_register_types(void)
-{
-    type_register_static(&pl190_info);
-}
-
-type_init(pl190_register_types)
diff --git a/hw/pl330.c b/hw/pl330.c
deleted file mode 100644 (file)
index 8b33138..0000000
+++ /dev/null
@@ -1,1653 +0,0 @@
-/*
- * ARM PrimeCell PL330 DMA Controller
- *
- * Copyright (c) 2009 Samsung Electronics.
- * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
- * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
- * Copyright (c) 2012 PetaLogix Pty Ltd.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; version 2 or later.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/sysbus.h"
-#include "qemu/timer.h"
-#include "sysemu/dma.h"
-
-#ifndef PL330_ERR_DEBUG
-#define PL330_ERR_DEBUG 0
-#endif
-
-#define DB_PRINT_L(lvl, fmt, args...) do {\
-    if (PL330_ERR_DEBUG >= lvl) {\
-        fprintf(stderr, "PL330: %s:" fmt, __func__, ## args);\
-    } \
-} while (0);
-
-#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
-
-#define PL330_PERIPH_NUM            32
-#define PL330_MAX_BURST_LEN         128
-#define PL330_INSN_MAXSIZE          6
-
-#define PL330_FIFO_OK               0
-#define PL330_FIFO_STALL            1
-#define PL330_FIFO_ERR              (-1)
-
-#define PL330_FAULT_UNDEF_INSTR             (1 <<  0)
-#define PL330_FAULT_OPERAND_INVALID         (1 <<  1)
-#define PL330_FAULT_DMAGO_ERR               (1 <<  4)
-#define PL330_FAULT_EVENT_ERR               (1 <<  5)
-#define PL330_FAULT_CH_PERIPH_ERR           (1 <<  6)
-#define PL330_FAULT_CH_RDWR_ERR             (1 <<  7)
-#define PL330_FAULT_ST_DATA_UNAVAILABLE     (1 << 12)
-#define PL330_FAULT_FIFOEMPTY_ERR           (1 << 13)
-#define PL330_FAULT_INSTR_FETCH_ERR         (1 << 16)
-#define PL330_FAULT_DATA_WRITE_ERR          (1 << 17)
-#define PL330_FAULT_DATA_READ_ERR           (1 << 18)
-#define PL330_FAULT_DBG_INSTR               (1 << 30)
-#define PL330_FAULT_LOCKUP_ERR              (1 << 31)
-
-#define PL330_UNTAGGED              0xff
-
-#define PL330_SINGLE                0x0
-#define PL330_BURST                 0x1
-
-#define PL330_WATCHDOG_LIMIT        1024
-
-/* IOMEM mapped registers */
-#define PL330_REG_DSR               0x000
-#define PL330_REG_DPC               0x004
-#define PL330_REG_INTEN             0x020
-#define PL330_REG_INT_EVENT_RIS     0x024
-#define PL330_REG_INTMIS            0x028
-#define PL330_REG_INTCLR            0x02C
-#define PL330_REG_FSRD              0x030
-#define PL330_REG_FSRC              0x034
-#define PL330_REG_FTRD              0x038
-#define PL330_REG_FTR_BASE          0x040
-#define PL330_REG_CSR_BASE          0x100
-#define PL330_REG_CPC_BASE          0x104
-#define PL330_REG_CHANCTRL          0x400
-#define PL330_REG_DBGSTATUS         0xD00
-#define PL330_REG_DBGCMD            0xD04
-#define PL330_REG_DBGINST0          0xD08
-#define PL330_REG_DBGINST1          0xD0C
-#define PL330_REG_CR0_BASE          0xE00
-#define PL330_REG_PERIPH_ID         0xFE0
-
-#define PL330_IOMEM_SIZE    0x1000
-
-#define CFG_BOOT_ADDR 2
-#define CFG_INS 3
-#define CFG_PNS 4
-#define CFG_CRD 5
-
-static const uint32_t pl330_id[] = {
-    0x30, 0x13, 0x24, 0x00, 0x0D, 0xF0, 0x05, 0xB1
-};
-
-/* DMA channel states as they are described in PL330 Technical Reference Manual
- * Most of them will not be used in emulation.
- */
-typedef enum  {
-    pl330_chan_stopped = 0,
-    pl330_chan_executing = 1,
-    pl330_chan_cache_miss = 2,
-    pl330_chan_updating_pc = 3,
-    pl330_chan_waiting_event = 4,
-    pl330_chan_at_barrier = 5,
-    pl330_chan_queue_busy = 6,
-    pl330_chan_waiting_periph = 7,
-    pl330_chan_killing = 8,
-    pl330_chan_completing = 9,
-    pl330_chan_fault_completing = 14,
-    pl330_chan_fault = 15,
-} PL330ChanState;
-
-typedef struct PL330State PL330State;
-
-typedef struct PL330Chan {
-    uint32_t src;
-    uint32_t dst;
-    uint32_t pc;
-    uint32_t control;
-    uint32_t status;
-    uint32_t lc[2];
-    uint32_t fault_type;
-    uint32_t watchdog_timer;
-
-    bool ns;
-    uint8_t request_flag;
-    uint8_t wakeup;
-    uint8_t wfp_sbp;
-
-    uint8_t state;
-    uint8_t stall;
-
-    bool is_manager;
-    PL330State *parent;
-    uint8_t tag;
-} PL330Chan;
-
-static const VMStateDescription vmstate_pl330_chan = {
-    .name = "pl330_chan",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32(src, PL330Chan),
-        VMSTATE_UINT32(dst, PL330Chan),
-        VMSTATE_UINT32(pc, PL330Chan),
-        VMSTATE_UINT32(control, PL330Chan),
-        VMSTATE_UINT32(status, PL330Chan),
-        VMSTATE_UINT32_ARRAY(lc, PL330Chan, 2),
-        VMSTATE_UINT32(fault_type, PL330Chan),
-        VMSTATE_UINT32(watchdog_timer, PL330Chan),
-        VMSTATE_BOOL(ns, PL330Chan),
-        VMSTATE_UINT8(request_flag, PL330Chan),
-        VMSTATE_UINT8(wakeup, PL330Chan),
-        VMSTATE_UINT8(wfp_sbp, PL330Chan),
-        VMSTATE_UINT8(state, PL330Chan),
-        VMSTATE_UINT8(stall, PL330Chan),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-typedef struct PL330Fifo {
-    uint8_t *buf;
-    uint8_t *tag;
-    uint32_t head;
-    uint32_t num;
-    uint32_t buf_size;
-} PL330Fifo;
-
-static const VMStateDescription vmstate_pl330_fifo = {
-    .name = "pl330_chan",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_VBUFFER_UINT32(buf, PL330Fifo, 1, NULL, 0, buf_size),
-        VMSTATE_VBUFFER_UINT32(tag, PL330Fifo, 1, NULL, 0, buf_size),
-        VMSTATE_UINT32(head, PL330Fifo),
-        VMSTATE_UINT32(num, PL330Fifo),
-        VMSTATE_UINT32(buf_size, PL330Fifo),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-typedef struct PL330QueueEntry {
-    uint32_t addr;
-    uint32_t len;
-    uint8_t n;
-    bool inc;
-    bool z;
-    uint8_t tag;
-    uint8_t seqn;
-} PL330QueueEntry;
-
-static const VMStateDescription vmstate_pl330_queue_entry = {
-    .name = "pl330_queue_entry",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32(addr, PL330QueueEntry),
-        VMSTATE_UINT32(len, PL330QueueEntry),
-        VMSTATE_UINT8(n, PL330QueueEntry),
-        VMSTATE_BOOL(inc, PL330QueueEntry),
-        VMSTATE_BOOL(z, PL330QueueEntry),
-        VMSTATE_UINT8(tag, PL330QueueEntry),
-        VMSTATE_UINT8(seqn, PL330QueueEntry),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-typedef struct PL330Queue {
-    PL330State *parent;
-    PL330QueueEntry *queue;
-    uint32_t queue_size;
-} PL330Queue;
-
-static const VMStateDescription vmstate_pl330_queue = {
-    .name = "pl330_queue",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_STRUCT_VARRAY_UINT32(queue, PL330Queue, queue_size, 1,
-                                 vmstate_pl330_queue_entry, PL330QueueEntry),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-struct PL330State {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    qemu_irq irq_abort;
-    qemu_irq *irq;
-
-    /* Config registers. cfg[5] = CfgDn. */
-    uint32_t cfg[6];
-#define EVENT_SEC_STATE 3
-#define PERIPH_SEC_STATE 4
-    /* cfg 0 bits and pieces */
-    uint32_t num_chnls;
-    uint8_t num_periph_req;
-    uint8_t num_events;
-    uint8_t mgr_ns_at_rst;
-    /* cfg 1 bits and pieces */
-    uint8_t i_cache_len;
-    uint8_t num_i_cache_lines;
-    /* CRD bits and pieces */
-    uint8_t data_width;
-    uint8_t wr_cap;
-    uint8_t wr_q_dep;
-    uint8_t rd_cap;
-    uint8_t rd_q_dep;
-    uint16_t data_buffer_dep;
-
-    PL330Chan manager;
-    PL330Chan *chan;
-    PL330Fifo fifo;
-    PL330Queue read_queue;
-    PL330Queue write_queue;
-    uint8_t *lo_seqn;
-    uint8_t *hi_seqn;
-    QEMUTimer *timer; /* is used for restore dma. */
-
-    uint32_t inten;
-    uint32_t int_status;
-    uint32_t ev_status;
-    uint32_t dbg[2];
-    uint8_t debug_status;
-    uint8_t num_faulting;
-    uint8_t periph_busy[PL330_PERIPH_NUM];
-
-};
-
-#define TYPE_PL330 "pl330"
-#define PL330(obj) OBJECT_CHECK(PL330State, (obj), TYPE_PL330)
-
-static const VMStateDescription vmstate_pl330 = {
-    .name = "pl330",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_STRUCT(manager, PL330State, 0, vmstate_pl330_chan, PL330Chan),
-        VMSTATE_STRUCT_VARRAY_UINT32(chan, PL330State, num_chnls, 0,
-                                     vmstate_pl330_chan, PL330Chan),
-        VMSTATE_VBUFFER_UINT32(lo_seqn, PL330State, 1, NULL, 0, num_chnls),
-        VMSTATE_VBUFFER_UINT32(hi_seqn, PL330State, 1, NULL, 0, num_chnls),
-        VMSTATE_STRUCT(fifo, PL330State, 0, vmstate_pl330_fifo, PL330Fifo),
-        VMSTATE_STRUCT(read_queue, PL330State, 0, vmstate_pl330_queue,
-                       PL330Queue),
-        VMSTATE_STRUCT(write_queue, PL330State, 0, vmstate_pl330_queue,
-                       PL330Queue),
-        VMSTATE_TIMER(timer, PL330State),
-        VMSTATE_UINT32(inten, PL330State),
-        VMSTATE_UINT32(int_status, PL330State),
-        VMSTATE_UINT32(ev_status, PL330State),
-        VMSTATE_UINT32_ARRAY(dbg, PL330State, 2),
-        VMSTATE_UINT8(debug_status, PL330State),
-        VMSTATE_UINT8(num_faulting, PL330State),
-        VMSTATE_UINT8_ARRAY(periph_busy, PL330State, PL330_PERIPH_NUM),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-typedef struct PL330InsnDesc {
-    /* OPCODE of the instruction */
-    uint8_t opcode;
-    /* Mask so we can select several sibling instructions, such as
-       DMALD, DMALDS and DMALDB */
-    uint8_t opmask;
-    /* Size of instruction in bytes */
-    uint8_t size;
-    /* Interpreter */
-    void (*exec)(PL330Chan *, uint8_t opcode, uint8_t *args, int len);
-} PL330InsnDesc;
-
-
-/* MFIFO Implementation
- *
- * MFIFO is implemented as a cyclic buffer of BUF_SIZE size. Tagged bytes are
- * stored in this buffer. Data is stored in BUF field, tags - in the
- * corresponding array elements of TAG field.
- */
-
-/* Initialize queue. */
-
-static void pl330_fifo_init(PL330Fifo *s, uint32_t size)
-{
-    s->buf = g_malloc0(size);
-    s->tag = g_malloc0(size);
-    s->buf_size = size;
-}
-
-/* Cyclic increment */
-
-static inline int pl330_fifo_inc(PL330Fifo *s, int x)
-{
-    return (x + 1) % s->buf_size;
-}
-
-/* Number of empty bytes in MFIFO */
-
-static inline int pl330_fifo_num_free(PL330Fifo *s)
-{
-    return s->buf_size - s->num;
-}
-
-/* Push LEN bytes of data stored in BUF to MFIFO and tag it with TAG.
- * Zero returned on success, PL330_FIFO_STALL if there is no enough free
- * space in MFIFO to store requested amount of data. If push was unsuccessful
- * no data is stored to MFIFO.
- */
-
-static int pl330_fifo_push(PL330Fifo *s, uint8_t *buf, int len, uint8_t tag)
-{
-    int i;
-
-    if (s->buf_size - s->num < len) {
-        return PL330_FIFO_STALL;
-    }
-    for (i = 0; i < len; i++) {
-        int push_idx = (s->head + s->num + i) % s->buf_size;
-        s->buf[push_idx] = buf[i];
-        s->tag[push_idx] = tag;
-    }
-    s->num += len;
-    return PL330_FIFO_OK;
-}
-
-/* Get LEN bytes of data from MFIFO and store it to BUF. Tag value of each
- * byte is verified. Zero returned on success, PL330_FIFO_ERR on tag mismatch
- * and PL330_FIFO_STALL if there is no enough data in MFIFO. If get was
- * unsuccessful no data is removed from MFIFO.
- */
-
-static int pl330_fifo_get(PL330Fifo *s, uint8_t *buf, int len, uint8_t tag)
-{
-    int i;
-
-    if (s->num < len) {
-        return PL330_FIFO_STALL;
-    }
-    for (i = 0; i < len; i++) {
-        if (s->tag[s->head] == tag) {
-            int get_idx = (s->head + i) % s->buf_size;
-            buf[i] = s->buf[get_idx];
-        } else { /* Tag mismatch - Rollback transaction */
-            return PL330_FIFO_ERR;
-        }
-    }
-    s->head = (s->head + len) % s->buf_size;
-    s->num -= len;
-    return PL330_FIFO_OK;
-}
-
-/* Reset MFIFO. This completely erases all data in it. */
-
-static inline void pl330_fifo_reset(PL330Fifo *s)
-{
-    s->head = 0;
-    s->num = 0;
-}
-
-/* Return tag of the first byte stored in MFIFO. If MFIFO is empty
- * PL330_UNTAGGED is returned.
- */
-
-static inline uint8_t pl330_fifo_tag(PL330Fifo *s)
-{
-    return (!s->num) ? PL330_UNTAGGED : s->tag[s->head];
-}
-
-/* Returns non-zero if tag TAG is present in fifo or zero otherwise */
-
-static int pl330_fifo_has_tag(PL330Fifo *s, uint8_t tag)
-{
-    int i, n;
-
-    i = s->head;
-    for (n = 0; n < s->num; n++) {
-        if (s->tag[i] == tag) {
-            return 1;
-        }
-        i = pl330_fifo_inc(s, i);
-    }
-    return 0;
-}
-
-/* Remove all entry tagged with TAG from MFIFO */
-
-static void pl330_fifo_tagged_remove(PL330Fifo *s, uint8_t tag)
-{
-    int i, t, n;
-
-    t = i = s->head;
-    for (n = 0; n < s->num; n++) {
-        if (s->tag[i] != tag) {
-            s->buf[t] = s->buf[i];
-            s->tag[t] = s->tag[i];
-            t = pl330_fifo_inc(s, t);
-        } else {
-            s->num = s->num - 1;
-        }
-        i = pl330_fifo_inc(s, i);
-    }
-}
-
-/* Read-Write Queue implementation
- *
- * A Read-Write Queue stores up to QUEUE_SIZE instructions (loads or stores).
- * Each instruction is described by source (for loads) or destination (for
- * stores) address ADDR, width of data to be loaded/stored LEN, number of
- * stores/loads to be performed N, INC bit, Z bit and TAG to identify channel
- * this instruction belongs to. Queue does not store any information about
- * nature of the instruction: is it load or store. PL330 has different queues
- * for loads and stores so this is already known at the top level where it
- * matters.
- *
- * Queue works as FIFO for instructions with equivalent tags, but can issue
- * instructions with different tags in arbitrary order. SEQN field attached to
- * each instruction helps to achieve this. For each TAG queue contains
- * instructions with consecutive SEQN values ranging from LO_SEQN[TAG] to
- * HI_SEQN[TAG]-1 inclusive. SEQN is 8-bit unsigned integer, so SEQN=255 is
- * followed by SEQN=0.
- *
- * Z bit indicates that zeroes should be stored. No MFIFO fetches are performed
- * in this case.
- */
-
-static void pl330_queue_reset(PL330Queue *s)
-{
-    int i;
-
-    for (i = 0; i < s->queue_size; i++) {
-        s->queue[i].tag = PL330_UNTAGGED;
-    }
-}
-
-/* Initialize queue */
-static void pl330_queue_init(PL330Queue *s, int size, PL330State *parent)
-{
-    s->parent = parent;
-    s->queue = g_new0(PL330QueueEntry, size);
-    s->queue_size = size;
-}
-
-/* Returns pointer to an empty slot or NULL if queue is full */
-static PL330QueueEntry *pl330_queue_find_empty(PL330Queue *s)
-{
-    int i;
-
-    for (i = 0; i < s->queue_size; i++) {
-        if (s->queue[i].tag == PL330_UNTAGGED) {
-            return &s->queue[i];
-        }
-    }
-    return NULL;
-}
-
-/* Put instruction in queue.
- * Return value:
- * - zero - OK
- * - non-zero - queue is full
- */
-
-static int pl330_queue_put_insn(PL330Queue *s, uint32_t addr,
-                                int len, int n, bool inc, bool z, uint8_t tag)
-{
-    PL330QueueEntry *entry = pl330_queue_find_empty(s);
-
-    if (!entry) {
-        return 1;
-    }
-    entry->tag = tag;
-    entry->addr = addr;
-    entry->len = len;
-    entry->n = n;
-    entry->z = z;
-    entry->inc = inc;
-    entry->seqn = s->parent->hi_seqn[tag];
-    s->parent->hi_seqn[tag]++;
-    return 0;
-}
-
-/* Returns a pointer to queue slot containing instruction which satisfies
- *  following conditions:
- *   - it has valid tag value (not PL330_UNTAGGED)
- *   - if enforce_seq is set it has to be issuable without violating queue
- *     logic (see above)
- *   - if TAG argument is not PL330_UNTAGGED this instruction has tag value
- *     equivalent to the argument TAG value.
- *  If such instruction cannot be found NULL is returned.
- */
-
-static PL330QueueEntry *pl330_queue_find_insn(PL330Queue *s, uint8_t tag,
-                                              bool enforce_seq)
-{
-    int i;
-
-    for (i = 0; i < s->queue_size; i++) {
-        if (s->queue[i].tag != PL330_UNTAGGED) {
-            if ((!enforce_seq ||
-                    s->queue[i].seqn == s->parent->lo_seqn[s->queue[i].tag]) &&
-                    (s->queue[i].tag == tag || tag == PL330_UNTAGGED ||
-                    s->queue[i].z)) {
-                return &s->queue[i];
-            }
-        }
-    }
-    return NULL;
-}
-
-/* Removes instruction from queue. */
-
-static inline void pl330_queue_remove_insn(PL330Queue *s, PL330QueueEntry *e)
-{
-    s->parent->lo_seqn[e->tag]++;
-    e->tag = PL330_UNTAGGED;
-}
-
-/* Removes all instructions tagged with TAG from queue. */
-
-static inline void pl330_queue_remove_tagged(PL330Queue *s, uint8_t tag)
-{
-    int i;
-
-    for (i = 0; i < s->queue_size; i++) {
-        if (s->queue[i].tag == tag) {
-            s->queue[i].tag = PL330_UNTAGGED;
-        }
-    }
-}
-
-/* DMA instruction execution engine */
-
-/* Moves DMA channel to the FAULT state and updates it's status. */
-
-static inline void pl330_fault(PL330Chan *ch, uint32_t flags)
-{
-    DB_PRINT("ch: %p, flags: %x\n", ch, flags);
-    ch->fault_type |= flags;
-    if (ch->state == pl330_chan_fault) {
-        return;
-    }
-    ch->state = pl330_chan_fault;
-    ch->parent->num_faulting++;
-    if (ch->parent->num_faulting == 1) {
-        DB_PRINT("abort interrupt raised\n");
-        qemu_irq_raise(ch->parent->irq_abort);
-    }
-}
-
-/*
- * For information about instructions see PL330 Technical Reference Manual.
- *
- * Arguments:
- *   CH - channel executing the instruction
- *   OPCODE - opcode
- *   ARGS - array of 8-bit arguments
- *   LEN - number of elements in ARGS array
- */
-
-static void pl330_dmaaddh(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
-{
-    uint16_t im = (((uint16_t)args[1]) << 8) | ((uint16_t)args[0]);
-    uint8_t ra = (opcode >> 1) & 1;
-
-    if (ch->is_manager) {
-        pl330_fault(ch, PL330_FAULT_UNDEF_INSTR);
-        return;
-    }
-    if (ra) {
-        ch->dst += im;
-    } else {
-        ch->src += im;
-    }
-}
-
-static void pl330_dmaend(PL330Chan *ch, uint8_t opcode,
-                         uint8_t *args, int len)
-{
-    PL330State *s = ch->parent;
-
-    if (ch->state == pl330_chan_executing && !ch->is_manager) {
-        /* Wait for all transfers to complete */
-        if (pl330_fifo_has_tag(&s->fifo, ch->tag) ||
-            pl330_queue_find_insn(&s->read_queue, ch->tag, false) != NULL ||
-            pl330_queue_find_insn(&s->write_queue, ch->tag, false) != NULL) {
-
-            ch->stall = 1;
-            return;
-        }
-    }
-    DB_PRINT("DMA ending!\n");
-    pl330_fifo_tagged_remove(&s->fifo, ch->tag);
-    pl330_queue_remove_tagged(&s->read_queue, ch->tag);
-    pl330_queue_remove_tagged(&s->write_queue, ch->tag);
-    ch->state = pl330_chan_stopped;
-}
-
-static void pl330_dmaflushp(PL330Chan *ch, uint8_t opcode,
-                                            uint8_t *args, int len)
-{
-    uint8_t periph_id;
-
-    if (args[0] & 7) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    periph_id = (args[0] >> 3) & 0x1f;
-    if (periph_id >= ch->parent->num_periph_req) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) {
-        pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR);
-        return;
-    }
-    /* Do nothing */
-}
-
-static void pl330_dmago(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
-{
-    uint8_t chan_id;
-    uint8_t ns;
-    uint32_t pc;
-    PL330Chan *s;
-
-    DB_PRINT("\n");
-
-    if (!ch->is_manager) {
-        pl330_fault(ch, PL330_FAULT_UNDEF_INSTR);
-        return;
-    }
-    ns = !!(opcode & 2);
-    chan_id = args[0] & 7;
-    if ((args[0] >> 3)) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    if (chan_id >= ch->parent->num_chnls) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    pc = (((uint32_t)args[4]) << 24) | (((uint32_t)args[3]) << 16) |
-         (((uint32_t)args[2]) << 8)  | (((uint32_t)args[1]));
-    if (ch->parent->chan[chan_id].state != pl330_chan_stopped) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    if (ch->ns && !ns) {
-        pl330_fault(ch, PL330_FAULT_DMAGO_ERR);
-        return;
-    }
-    s = &ch->parent->chan[chan_id];
-    s->ns = ns;
-    s->pc = pc;
-    s->state = pl330_chan_executing;
-}
-
-static void pl330_dmald(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
-{
-    uint8_t bs = opcode & 3;
-    uint32_t size, num;
-    bool inc;
-
-    if (bs == 2) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    if ((bs == 1 && ch->request_flag == PL330_BURST) ||
-        (bs == 3 && ch->request_flag == PL330_SINGLE)) {
-        /* Perform NOP */
-        return;
-    }
-    if (bs == 1 && ch->request_flag == PL330_SINGLE) {
-        num = 1;
-    } else {
-        num = ((ch->control >> 4) & 0xf) + 1;
-    }
-    size = (uint32_t)1 << ((ch->control >> 1) & 0x7);
-    inc = !!(ch->control & 1);
-    ch->stall = pl330_queue_put_insn(&ch->parent->read_queue, ch->src,
-                                    size, num, inc, 0, ch->tag);
-    if (!ch->stall) {
-        DB_PRINT("channel:%d address:%08x size:%d num:%d %c\n",
-                 ch->tag, ch->src, size, num, inc ? 'Y' : 'N');
-        ch->src += inc ? size * num - (ch->src & (size - 1)) : 0;
-    }
-}
-
-static void pl330_dmaldp(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
-{
-    uint8_t periph_id;
-
-    if (args[0] & 7) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    periph_id = (args[0] >> 3) & 0x1f;
-    if (periph_id >= ch->parent->num_periph_req) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) {
-        pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR);
-        return;
-    }
-    pl330_dmald(ch, opcode, args, len);
-}
-
-static void pl330_dmalp(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
-{
-    uint8_t lc = (opcode & 2) >> 1;
-
-    ch->lc[lc] = args[0];
-}
-
-static void pl330_dmakill(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
-{
-    if (ch->state == pl330_chan_fault ||
-        ch->state == pl330_chan_fault_completing) {
-        /* This is the only way for a channel to leave the faulting state */
-        ch->fault_type = 0;
-        ch->parent->num_faulting--;
-        if (ch->parent->num_faulting == 0) {
-            DB_PRINT("abort interrupt lowered\n");
-            qemu_irq_lower(ch->parent->irq_abort);
-        }
-    }
-    ch->state = pl330_chan_killing;
-    pl330_fifo_tagged_remove(&ch->parent->fifo, ch->tag);
-    pl330_queue_remove_tagged(&ch->parent->read_queue, ch->tag);
-    pl330_queue_remove_tagged(&ch->parent->write_queue, ch->tag);
-    ch->state = pl330_chan_stopped;
-}
-
-static void pl330_dmalpend(PL330Chan *ch, uint8_t opcode,
-                                    uint8_t *args, int len)
-{
-    uint8_t nf = (opcode & 0x10) >> 4;
-    uint8_t bs = opcode & 3;
-    uint8_t lc = (opcode & 4) >> 2;
-
-    if (bs == 2) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    if ((bs == 1 && ch->request_flag == PL330_BURST) ||
-        (bs == 3 && ch->request_flag == PL330_SINGLE)) {
-        /* Perform NOP */
-        return;
-    }
-    if (!nf || ch->lc[lc]) {
-        if (nf) {
-            ch->lc[lc]--;
-        }
-        DB_PRINT("loop reiteration\n");
-        ch->pc -= args[0];
-        ch->pc -= len + 1;
-        /* "ch->pc -= args[0] + len + 1" is incorrect when args[0] == 256 */
-    } else {
-        DB_PRINT("loop fallthrough\n");
-    }
-}
-
-
-static void pl330_dmamov(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
-{
-    uint8_t rd = args[0] & 7;
-    uint32_t im;
-
-    if ((args[0] >> 3)) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    im = (((uint32_t)args[4]) << 24) | (((uint32_t)args[3]) << 16) |
-         (((uint32_t)args[2]) << 8)  | (((uint32_t)args[1]));
-    switch (rd) {
-    case 0:
-        ch->src = im;
-        break;
-    case 1:
-        ch->control = im;
-        break;
-    case 2:
-        ch->dst = im;
-        break;
-    default:
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-}
-
-static void pl330_dmanop(PL330Chan *ch, uint8_t opcode,
-                         uint8_t *args, int len)
-{
-    /* NOP is NOP. */
-}
-
-static void pl330_dmarmb(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
-{
-   if (pl330_queue_find_insn(&ch->parent->read_queue, ch->tag, false)) {
-        ch->state = pl330_chan_at_barrier;
-        ch->stall = 1;
-        return;
-    } else {
-        ch->state = pl330_chan_executing;
-    }
-}
-
-static void pl330_dmasev(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
-{
-    uint8_t ev_id;
-
-    if (args[0] & 7) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    ev_id = (args[0] >> 3) & 0x1f;
-    if (ev_id >= ch->parent->num_events) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    if (ch->ns && !(ch->parent->cfg[CFG_INS] & (1 << ev_id))) {
-        pl330_fault(ch, PL330_FAULT_EVENT_ERR);
-        return;
-    }
-    if (ch->parent->inten & (1 << ev_id)) {
-        ch->parent->int_status |= (1 << ev_id);
-        DB_PRINT("event interrupt raised %d\n", ev_id);
-        qemu_irq_raise(ch->parent->irq[ev_id]);
-    }
-    ch->parent->ev_status |= (1 << ev_id);
-}
-
-static void pl330_dmast(PL330Chan *ch, uint8_t opcode, uint8_t *args, int len)
-{
-    uint8_t bs = opcode & 3;
-    uint32_t size, num;
-    bool inc;
-
-    if (bs == 2) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    if ((bs == 1 && ch->request_flag == PL330_BURST) ||
-        (bs == 3 && ch->request_flag == PL330_SINGLE)) {
-        /* Perform NOP */
-        return;
-    }
-    num = ((ch->control >> 18) & 0xf) + 1;
-    size = (uint32_t)1 << ((ch->control >> 15) & 0x7);
-    inc = !!((ch->control >> 14) & 1);
-    ch->stall = pl330_queue_put_insn(&ch->parent->write_queue, ch->dst,
-                                    size, num, inc, 0, ch->tag);
-    if (!ch->stall) {
-        DB_PRINT("channel:%d address:%08x size:%d num:%d %c\n",
-                 ch->tag, ch->dst, size, num, inc ? 'Y' : 'N');
-        ch->dst += inc ? size * num - (ch->dst & (size - 1)) : 0;
-    }
-}
-
-static void pl330_dmastp(PL330Chan *ch, uint8_t opcode,
-                         uint8_t *args, int len)
-{
-    uint8_t periph_id;
-
-    if (args[0] & 7) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    periph_id = (args[0] >> 3) & 0x1f;
-    if (periph_id >= ch->parent->num_periph_req) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) {
-        pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR);
-        return;
-    }
-    pl330_dmast(ch, opcode, args, len);
-}
-
-static void pl330_dmastz(PL330Chan *ch, uint8_t opcode,
-                         uint8_t *args, int len)
-{
-    uint32_t size, num;
-    bool inc;
-
-    num = ((ch->control >> 18) & 0xf) + 1;
-    size = (uint32_t)1 << ((ch->control >> 15) & 0x7);
-    inc = !!((ch->control >> 14) & 1);
-    ch->stall = pl330_queue_put_insn(&ch->parent->write_queue, ch->dst,
-                                    size, num, inc, 1, ch->tag);
-    if (inc) {
-        ch->dst += size * num;
-    }
-}
-
-static void pl330_dmawfe(PL330Chan *ch, uint8_t opcode,
-                         uint8_t *args, int len)
-{
-    uint8_t ev_id;
-    int i;
-
-    if (args[0] & 5) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    ev_id = (args[0] >> 3) & 0x1f;
-    if (ev_id >= ch->parent->num_events) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    if (ch->ns && !(ch->parent->cfg[CFG_INS] & (1 << ev_id))) {
-        pl330_fault(ch, PL330_FAULT_EVENT_ERR);
-        return;
-    }
-    ch->wakeup = ev_id;
-    ch->state = pl330_chan_waiting_event;
-    if (~ch->parent->inten & ch->parent->ev_status & 1 << ev_id) {
-        ch->state = pl330_chan_executing;
-        /* If anyone else is currently waiting on the same event, let them
-         * clear the ev_status so they pick up event as well
-         */
-        for (i = 0; i < ch->parent->num_chnls; ++i) {
-            PL330Chan *peer = &ch->parent->chan[i];
-            if (peer->state == pl330_chan_waiting_event &&
-                    peer->wakeup == ev_id) {
-                return;
-            }
-        }
-        ch->parent->ev_status &= ~(1 << ev_id);
-    } else {
-        ch->stall = 1;
-    }
-}
-
-static void pl330_dmawfp(PL330Chan *ch, uint8_t opcode,
-                         uint8_t *args, int len)
-{
-    uint8_t bs = opcode & 3;
-    uint8_t periph_id;
-
-    if (args[0] & 7) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    periph_id = (args[0] >> 3) & 0x1f;
-    if (periph_id >= ch->parent->num_periph_req) {
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-    if (ch->ns && !(ch->parent->cfg[CFG_PNS] & (1 << periph_id))) {
-        pl330_fault(ch, PL330_FAULT_CH_PERIPH_ERR);
-        return;
-    }
-    switch (bs) {
-    case 0: /* S */
-        ch->request_flag = PL330_SINGLE;
-        ch->wfp_sbp = 0;
-        break;
-    case 1: /* P */
-        ch->request_flag = PL330_BURST;
-        ch->wfp_sbp = 2;
-        break;
-    case 2: /* B */
-        ch->request_flag = PL330_BURST;
-        ch->wfp_sbp = 1;
-        break;
-    default:
-        pl330_fault(ch, PL330_FAULT_OPERAND_INVALID);
-        return;
-    }
-
-    if (ch->parent->periph_busy[periph_id]) {
-        ch->state = pl330_chan_waiting_periph;
-        ch->stall = 1;
-    } else if (ch->state == pl330_chan_waiting_periph) {
-        ch->state = pl330_chan_executing;
-    }
-}
-
-static void pl330_dmawmb(PL330Chan *ch, uint8_t opcode,
-                         uint8_t *args, int len)
-{
-    if (pl330_queue_find_insn(&ch->parent->write_queue, ch->tag, false)) {
-        ch->state = pl330_chan_at_barrier;
-        ch->stall = 1;
-        return;
-    } else {
-        ch->state = pl330_chan_executing;
-    }
-}
-
-/* NULL terminated array of the instruction descriptions. */
-static const PL330InsnDesc insn_desc[] = {
-    { .opcode = 0x54, .opmask = 0xFD, .size = 3, .exec = pl330_dmaaddh, },
-    { .opcode = 0x00, .opmask = 0xFF, .size = 1, .exec = pl330_dmaend, },
-    { .opcode = 0x35, .opmask = 0xFF, .size = 2, .exec = pl330_dmaflushp, },
-    { .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago, },
-    { .opcode = 0x04, .opmask = 0xFC, .size = 1, .exec = pl330_dmald, },
-    { .opcode = 0x25, .opmask = 0xFD, .size = 2, .exec = pl330_dmaldp, },
-    { .opcode = 0x20, .opmask = 0xFD, .size = 2, .exec = pl330_dmalp, },
-    /* dmastp  must be before dmalpend in this list, because their maps
-     * are overlapping
-     */
-    { .opcode = 0x29, .opmask = 0xFD, .size = 2, .exec = pl330_dmastp, },
-    { .opcode = 0x28, .opmask = 0xE8, .size = 2, .exec = pl330_dmalpend, },
-    { .opcode = 0x01, .opmask = 0xFF, .size = 1, .exec = pl330_dmakill, },
-    { .opcode = 0xBC, .opmask = 0xFF, .size = 6, .exec = pl330_dmamov, },
-    { .opcode = 0x18, .opmask = 0xFF, .size = 1, .exec = pl330_dmanop, },
-    { .opcode = 0x12, .opmask = 0xFF, .size = 1, .exec = pl330_dmarmb, },
-    { .opcode = 0x34, .opmask = 0xFF, .size = 2, .exec = pl330_dmasev, },
-    { .opcode = 0x08, .opmask = 0xFC, .size = 1, .exec = pl330_dmast, },
-    { .opcode = 0x0C, .opmask = 0xFF, .size = 1, .exec = pl330_dmastz, },
-    { .opcode = 0x36, .opmask = 0xFF, .size = 2, .exec = pl330_dmawfe, },
-    { .opcode = 0x30, .opmask = 0xFC, .size = 2, .exec = pl330_dmawfp, },
-    { .opcode = 0x13, .opmask = 0xFF, .size = 1, .exec = pl330_dmawmb, },
-    { .opcode = 0x00, .opmask = 0x00, .size = 0, .exec = NULL, }
-};
-
-/* Instructions which can be issued via debug registers. */
-static const PL330InsnDesc debug_insn_desc[] = {
-    { .opcode = 0xA0, .opmask = 0xFD, .size = 6, .exec = pl330_dmago, },
-    { .opcode = 0x01, .opmask = 0xFF, .size = 1, .exec = pl330_dmakill, },
-    { .opcode = 0x34, .opmask = 0xFF, .size = 2, .exec = pl330_dmasev, },
-    { .opcode = 0x00, .opmask = 0x00, .size = 0, .exec = NULL, }
-};
-
-static inline const PL330InsnDesc *pl330_fetch_insn(PL330Chan *ch)
-{
-    uint8_t opcode;
-    int i;
-
-    dma_memory_read(&dma_context_memory, ch->pc, &opcode, 1);
-    for (i = 0; insn_desc[i].size; i++) {
-        if ((opcode & insn_desc[i].opmask) == insn_desc[i].opcode) {
-            return &insn_desc[i];
-        }
-    }
-    return NULL;
-}
-
-static inline void pl330_exec_insn(PL330Chan *ch, const PL330InsnDesc *insn)
-{
-    uint8_t buf[PL330_INSN_MAXSIZE];
-
-    assert(insn->size <= PL330_INSN_MAXSIZE);
-    dma_memory_read(&dma_context_memory, ch->pc, buf, insn->size);
-    insn->exec(ch, buf[0], &buf[1], insn->size - 1);
-}
-
-static inline void pl330_update_pc(PL330Chan *ch,
-                                   const PL330InsnDesc *insn)
-{
-    ch->pc += insn->size;
-}
-
-/* Try to execute current instruction in channel CH. Number of executed
-   instructions is returned (0 or 1). */
-static int pl330_chan_exec(PL330Chan *ch)
-{
-    const PL330InsnDesc *insn;
-
-    if (ch->state != pl330_chan_executing &&
-            ch->state != pl330_chan_waiting_periph &&
-            ch->state != pl330_chan_at_barrier &&
-            ch->state != pl330_chan_waiting_event) {
-        DB_PRINT("%d\n", ch->state);
-        return 0;
-    }
-    ch->stall = 0;
-    insn = pl330_fetch_insn(ch);
-    if (!insn) {
-        DB_PRINT("pl330 undefined instruction\n");
-        pl330_fault(ch, PL330_FAULT_UNDEF_INSTR);
-        return 0;
-    }
-    pl330_exec_insn(ch, insn);
-    if (!ch->stall) {
-        pl330_update_pc(ch, insn);
-        ch->watchdog_timer = 0;
-        return 1;
-    /* WDT only active in exec state */
-    } else if (ch->state == pl330_chan_executing) {
-        ch->watchdog_timer++;
-        if (ch->watchdog_timer >= PL330_WATCHDOG_LIMIT) {
-            pl330_fault(ch, PL330_FAULT_LOCKUP_ERR);
-        }
-    }
-    return 0;
-}
-
-/* Try to execute 1 instruction in each channel, one instruction from read
-   queue and one instruction from write queue. Number of successfully executed
-   instructions is returned. */
-static int pl330_exec_cycle(PL330Chan *channel)
-{
-    PL330State *s = channel->parent;
-    PL330QueueEntry *q;
-    int i;
-    int num_exec = 0;
-    int fifo_res = 0;
-    uint8_t buf[PL330_MAX_BURST_LEN];
-
-    /* Execute one instruction in each channel */
-    num_exec += pl330_chan_exec(channel);
-
-    /* Execute one instruction from read queue */
-    q = pl330_queue_find_insn(&s->read_queue, PL330_UNTAGGED, true);
-    if (q != NULL && q->len <= pl330_fifo_num_free(&s->fifo)) {
-        int len = q->len - (q->addr & (q->len - 1));
-
-        dma_memory_read(&dma_context_memory, q->addr, buf, len);
-        if (PL330_ERR_DEBUG > 1) {
-            DB_PRINT("PL330 read from memory @%08x (size = %08x):\n",
-                      q->addr, len);
-            hexdump((char *)buf, stderr, "", len);
-        }
-        fifo_res = pl330_fifo_push(&s->fifo, buf, len, q->tag);
-        if (fifo_res == PL330_FIFO_OK) {
-            if (q->inc) {
-                q->addr += len;
-            }
-            q->n--;
-            if (!q->n) {
-                pl330_queue_remove_insn(&s->read_queue, q);
-            }
-            num_exec++;
-        }
-    }
-
-    /* Execute one instruction from write queue. */
-    q = pl330_queue_find_insn(&s->write_queue, pl330_fifo_tag(&s->fifo), true);
-    if (q != NULL) {
-        int len = q->len - (q->addr & (q->len - 1));
-
-        if (q->z) {
-            for (i = 0; i < len; i++) {
-                buf[i] = 0;
-            }
-        } else {
-            fifo_res = pl330_fifo_get(&s->fifo, buf, len, q->tag);
-        }
-        if (fifo_res == PL330_FIFO_OK || q->z) {
-            dma_memory_write(&dma_context_memory, q->addr, buf, len);
-            if (PL330_ERR_DEBUG > 1) {
-                DB_PRINT("PL330 read from memory @%08x (size = %08x):\n",
-                         q->addr, len);
-                hexdump((char *)buf, stderr, "", len);
-            }
-            if (q->inc) {
-                q->addr += len;
-            }
-            num_exec++;
-        } else if (fifo_res == PL330_FIFO_STALL) {
-            pl330_fault(&channel->parent->chan[q->tag],
-                                PL330_FAULT_FIFOEMPTY_ERR);
-        }
-        q->n--;
-        if (!q->n) {
-            pl330_queue_remove_insn(&s->write_queue, q);
-        }
-    }
-
-    return num_exec;
-}
-
-static int pl330_exec_channel(PL330Chan *channel)
-{
-    int insr_exec = 0;
-
-    /* TODO: Is it all right to execute everything or should we do per-cycle
-       simulation? */
-    while (pl330_exec_cycle(channel)) {
-        insr_exec++;
-    }
-
-    /* Detect deadlock */
-    if (channel->state == pl330_chan_executing) {
-        pl330_fault(channel, PL330_FAULT_LOCKUP_ERR);
-    }
-    /* Situation when one of the queues has deadlocked but all channels
-     * have finished their programs should be impossible.
-     */
-
-    return insr_exec;
-}
-
-static inline void pl330_exec(PL330State *s)
-{
-    DB_PRINT("\n");
-    int i, insr_exec;
-    do {
-        insr_exec = pl330_exec_channel(&s->manager);
-
-        for (i = 0; i < s->num_chnls; i++) {
-            insr_exec += pl330_exec_channel(&s->chan[i]);
-        }
-    } while (insr_exec);
-}
-
-static void pl330_exec_cycle_timer(void *opaque)
-{
-    PL330State *s = (PL330State *)opaque;
-    pl330_exec(s);
-}
-
-/* Stop or restore dma operations */
-
-static void pl330_dma_stop_irq(void *opaque, int irq, int level)
-{
-    PL330State *s = (PL330State *)opaque;
-
-    if (s->periph_busy[irq] != level) {
-        s->periph_busy[irq] = level;
-        qemu_mod_timer(s->timer, qemu_get_clock_ns(vm_clock));
-    }
-}
-
-static void pl330_debug_exec(PL330State *s)
-{
-    uint8_t args[5];
-    uint8_t opcode;
-    uint8_t chan_id;
-    int i;
-    PL330Chan *ch;
-    const PL330InsnDesc *insn;
-
-    s->debug_status = 1;
-    chan_id = (s->dbg[0] >>  8) & 0x07;
-    opcode  = (s->dbg[0] >> 16) & 0xff;
-    args[0] = (s->dbg[0] >> 24) & 0xff;
-    args[1] = (s->dbg[1] >>  0) & 0xff;
-    args[2] = (s->dbg[1] >>  8) & 0xff;
-    args[3] = (s->dbg[1] >> 16) & 0xff;
-    args[4] = (s->dbg[1] >> 24) & 0xff;
-    DB_PRINT("chan id: %d\n", chan_id);
-    if (s->dbg[0] & 1) {
-        ch = &s->chan[chan_id];
-    } else {
-        ch = &s->manager;
-    }
-    insn = NULL;
-    for (i = 0; debug_insn_desc[i].size; i++) {
-        if ((opcode & debug_insn_desc[i].opmask) == debug_insn_desc[i].opcode) {
-            insn = &debug_insn_desc[i];
-        }
-    }
-    if (!insn) {
-        pl330_fault(ch, PL330_FAULT_UNDEF_INSTR | PL330_FAULT_DBG_INSTR);
-        return ;
-    }
-    ch->stall = 0;
-    insn->exec(ch, opcode, args, insn->size - 1);
-    if (ch->fault_type) {
-        ch->fault_type |= PL330_FAULT_DBG_INSTR;
-    }
-    if (ch->stall) {
-        qemu_log_mask(LOG_UNIMP, "pl330: stall of debug instruction not "
-                      "implemented\n");
-    }
-    s->debug_status = 0;
-}
-
-/* IOMEM mapped registers */
-
-static void pl330_iomem_write(void *opaque, hwaddr offset,
-                              uint64_t value, unsigned size)
-{
-    PL330State *s = (PL330State *) opaque;
-    uint32_t i;
-
-    DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)value);
-
-    switch (offset) {
-    case PL330_REG_INTEN:
-        s->inten = value;
-        break;
-    case PL330_REG_INTCLR:
-        for (i = 0; i < s->num_events; i++) {
-            if (s->int_status & s->inten & value & (1 << i)) {
-                DB_PRINT("event interrupt lowered %d\n", i);
-                qemu_irq_lower(s->irq[i]);
-            }
-        }
-        s->ev_status &= ~(value & s->inten);
-        s->int_status &= ~(value & s->inten);
-        break;
-    case PL330_REG_DBGCMD:
-        if ((value & 3) == 0) {
-            pl330_debug_exec(s);
-            pl330_exec(s);
-        } else {
-            qemu_log_mask(LOG_GUEST_ERROR, "pl330: write of illegal value %u "
-                          "for offset " TARGET_FMT_plx "\n", (unsigned)value,
-                          offset);
-        }
-        break;
-    case PL330_REG_DBGINST0:
-        DB_PRINT("s->dbg[0] = %08x\n", (unsigned)value);
-        s->dbg[0] = value;
-        break;
-    case PL330_REG_DBGINST1:
-        DB_PRINT("s->dbg[1] = %08x\n", (unsigned)value);
-        s->dbg[1] = value;
-        break;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad write offset " TARGET_FMT_plx
-                      "\n", offset);
-        break;
-    }
-}
-
-static inline uint32_t pl330_iomem_read_imp(void *opaque,
-        hwaddr offset)
-{
-    PL330State *s = (PL330State *)opaque;
-    int chan_id;
-    int i;
-    uint32_t res;
-
-    if (offset >= PL330_REG_PERIPH_ID && offset < PL330_REG_PERIPH_ID + 32) {
-        return pl330_id[(offset - PL330_REG_PERIPH_ID) >> 2];
-    }
-    if (offset >= PL330_REG_CR0_BASE && offset < PL330_REG_CR0_BASE + 24) {
-        return s->cfg[(offset - PL330_REG_CR0_BASE) >> 2];
-    }
-    if (offset >= PL330_REG_CHANCTRL && offset < PL330_REG_DBGSTATUS) {
-        offset -= PL330_REG_CHANCTRL;
-        chan_id = offset >> 5;
-        if (chan_id >= s->num_chnls) {
-            qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
-                          TARGET_FMT_plx "\n", offset);
-            return 0;
-        }
-        switch (offset & 0x1f) {
-        case 0x00:
-            return s->chan[chan_id].src;
-        case 0x04:
-            return s->chan[chan_id].dst;
-        case 0x08:
-            return s->chan[chan_id].control;
-        case 0x0C:
-            return s->chan[chan_id].lc[0];
-        case 0x10:
-            return s->chan[chan_id].lc[1];
-        default:
-            qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
-                          TARGET_FMT_plx "\n", offset);
-            return 0;
-        }
-    }
-    if (offset >= PL330_REG_CSR_BASE && offset < 0x400) {
-        offset -= PL330_REG_CSR_BASE;
-        chan_id = offset >> 3;
-        if (chan_id >= s->num_chnls) {
-            qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
-                          TARGET_FMT_plx "\n", offset);
-            return 0;
-        }
-        switch ((offset >> 2) & 1) {
-        case 0x0:
-            res = (s->chan[chan_id].ns << 21) |
-                    (s->chan[chan_id].wakeup << 4) |
-                    (s->chan[chan_id].state) |
-                    (s->chan[chan_id].wfp_sbp << 14);
-            return res;
-        case 0x1:
-            return s->chan[chan_id].pc;
-        default:
-            qemu_log_mask(LOG_GUEST_ERROR, "pl330: read error\n");
-            return 0;
-        }
-    }
-    if (offset >= PL330_REG_FTR_BASE && offset < 0x100) {
-        offset -= PL330_REG_FTR_BASE;
-        chan_id = offset >> 2;
-        if (chan_id >= s->num_chnls) {
-            qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
-                          TARGET_FMT_plx "\n", offset);
-            return 0;
-        }
-        return s->chan[chan_id].fault_type;
-    }
-    switch (offset) {
-    case PL330_REG_DSR:
-        return (s->manager.ns << 9) | (s->manager.wakeup << 4) |
-            (s->manager.state & 0xf);
-    case PL330_REG_DPC:
-        return s->manager.pc;
-    case PL330_REG_INTEN:
-        return s->inten;
-    case PL330_REG_INT_EVENT_RIS:
-        return s->ev_status;
-    case PL330_REG_INTMIS:
-        return s->int_status;
-    case PL330_REG_INTCLR:
-        /* Documentation says that we can't read this register
-         * but linux kernel does it
-         */
-        return 0;
-    case PL330_REG_FSRD:
-        return s->manager.state ? 1 : 0;
-    case PL330_REG_FSRC:
-        res = 0;
-        for (i = 0; i < s->num_chnls; i++) {
-            if (s->chan[i].state == pl330_chan_fault ||
-                s->chan[i].state == pl330_chan_fault_completing) {
-                res |= 1 << i;
-            }
-        }
-        return res;
-    case PL330_REG_FTRD:
-        return s->manager.fault_type;
-    case PL330_REG_DBGSTATUS:
-        return s->debug_status;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
-                      TARGET_FMT_plx "\n", offset);
-    }
-    return 0;
-}
-
-static uint64_t pl330_iomem_read(void *opaque, hwaddr offset,
-        unsigned size)
-{
-    int ret = pl330_iomem_read_imp(opaque, offset);
-    DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, ret);
-    return ret;
-}
-
-static const MemoryRegionOps pl330_ops = {
-    .read = pl330_iomem_read,
-    .write = pl330_iomem_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .impl = {
-        .min_access_size = 4,
-        .max_access_size = 4,
-    }
-};
-
-/* Controller logic and initialization */
-
-static void pl330_chan_reset(PL330Chan *ch)
-{
-    ch->src = 0;
-    ch->dst = 0;
-    ch->pc = 0;
-    ch->state = pl330_chan_stopped;
-    ch->watchdog_timer = 0;
-    ch->stall = 0;
-    ch->control = 0;
-    ch->status = 0;
-    ch->fault_type = 0;
-}
-
-static void pl330_reset(DeviceState *d)
-{
-    int i;
-    PL330State *s = PL330(d);
-
-    s->inten = 0;
-    s->int_status = 0;
-    s->ev_status = 0;
-    s->debug_status = 0;
-    s->num_faulting = 0;
-    s->manager.ns = s->mgr_ns_at_rst;
-    pl330_fifo_reset(&s->fifo);
-    pl330_queue_reset(&s->read_queue);
-    pl330_queue_reset(&s->write_queue);
-
-    for (i = 0; i < s->num_chnls; i++) {
-        pl330_chan_reset(&s->chan[i]);
-    }
-    for (i = 0; i < s->num_periph_req; i++) {
-        s->periph_busy[i] = 0;
-    }
-
-    qemu_del_timer(s->timer);
-}
-
-static void pl330_realize(DeviceState *dev, Error **errp)
-{
-    int i;
-    PL330State *s = PL330(dev);
-
-    sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq_abort);
-    memory_region_init_io(&s->iomem, &pl330_ops, s, "dma", PL330_IOMEM_SIZE);
-    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
-
-    s->timer = qemu_new_timer_ns(vm_clock, pl330_exec_cycle_timer, s);
-
-    s->cfg[0] = (s->mgr_ns_at_rst ? 0x4 : 0) |
-                (s->num_periph_req > 0 ? 1 : 0) |
-                ((s->num_chnls - 1) & 0x7) << 4 |
-                ((s->num_periph_req - 1) & 0x1f) << 12 |
-                ((s->num_events - 1) & 0x1f) << 17;
-
-    switch (s->i_cache_len) {
-    case (4):
-        s->cfg[1] |= 2;
-        break;
-    case (8):
-        s->cfg[1] |= 3;
-        break;
-    case (16):
-        s->cfg[1] |= 4;
-        break;
-    case (32):
-        s->cfg[1] |= 5;
-        break;
-    default:
-        error_setg(errp, "Bad value for i-cache_len property: %d\n",
-                   s->i_cache_len);
-        return;
-    }
-    s->cfg[1] |= ((s->num_i_cache_lines - 1) & 0xf) << 4;
-
-    s->chan = g_new0(PL330Chan, s->num_chnls);
-    s->hi_seqn = g_new0(uint8_t, s->num_chnls);
-    s->lo_seqn = g_new0(uint8_t, s->num_chnls);
-    for (i = 0; i < s->num_chnls; i++) {
-        s->chan[i].parent = s;
-        s->chan[i].tag = (uint8_t)i;
-    }
-    s->manager.parent = s;
-    s->manager.tag = s->num_chnls;
-    s->manager.is_manager = true;
-
-    s->irq = g_new0(qemu_irq, s->num_events);
-    for (i = 0; i < s->num_events; i++) {
-        sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]);
-    }
-
-    qdev_init_gpio_in(dev, pl330_dma_stop_irq, PL330_PERIPH_NUM);
-
-    switch (s->data_width) {
-    case (32):
-        s->cfg[CFG_CRD] |= 0x2;
-        break;
-    case (64):
-        s->cfg[CFG_CRD] |= 0x3;
-        break;
-    case (128):
-        s->cfg[CFG_CRD] |= 0x4;
-        break;
-    default:
-        error_setg(errp, "Bad value for data_width property: %d\n",
-                   s->data_width);
-        return;
-    }
-
-    s->cfg[CFG_CRD] |= ((s->wr_cap - 1) & 0x7) << 4 |
-                    ((s->wr_q_dep - 1) & 0xf) << 8 |
-                    ((s->rd_cap - 1) & 0x7) << 12 |
-                    ((s->rd_q_dep - 1) & 0xf) << 16 |
-                    ((s->data_buffer_dep - 1) & 0x1ff) << 20;
-
-    pl330_queue_init(&s->read_queue, s->rd_q_dep, s);
-    pl330_queue_init(&s->write_queue, s->wr_q_dep, s);
-    pl330_fifo_init(&s->fifo, s->data_buffer_dep);
-}
-
-static Property pl330_properties[] = {
-    /* CR0 */
-    DEFINE_PROP_UINT32("num_chnls", PL330State, num_chnls, 8),
-    DEFINE_PROP_UINT8("num_periph_req", PL330State, num_periph_req, 4),
-    DEFINE_PROP_UINT8("num_events", PL330State, num_events, 16),
-    DEFINE_PROP_UINT8("mgr_ns_at_rst", PL330State, mgr_ns_at_rst, 0),
-    /* CR1 */
-    DEFINE_PROP_UINT8("i-cache_len", PL330State, i_cache_len, 4),
-    DEFINE_PROP_UINT8("num_i-cache_lines", PL330State, num_i_cache_lines, 8),
-    /* CR2-4 */
-    DEFINE_PROP_UINT32("boot_addr", PL330State, cfg[CFG_BOOT_ADDR], 0),
-    DEFINE_PROP_UINT32("INS", PL330State, cfg[CFG_INS], 0),
-    DEFINE_PROP_UINT32("PNS", PL330State, cfg[CFG_PNS], 0),
-    /* CRD */
-    DEFINE_PROP_UINT8("data_width", PL330State, data_width, 64),
-    DEFINE_PROP_UINT8("wr_cap", PL330State, wr_cap, 8),
-    DEFINE_PROP_UINT8("wr_q_dep", PL330State, wr_q_dep, 16),
-    DEFINE_PROP_UINT8("rd_cap", PL330State, rd_cap, 8),
-    DEFINE_PROP_UINT8("rd_q_dep", PL330State, rd_q_dep, 16),
-    DEFINE_PROP_UINT16("data_buffer_dep", PL330State, data_buffer_dep, 256),
-
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void pl330_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    dc->realize = pl330_realize;
-    dc->reset = pl330_reset;
-    dc->props = pl330_properties;
-    dc->vmsd = &vmstate_pl330;
-}
-
-static const TypeInfo pl330_type_info = {
-    .name           = TYPE_PL330,
-    .parent         = TYPE_SYS_BUS_DEVICE,
-    .instance_size  = sizeof(PL330State),
-    .class_init      = pl330_class_init,
-};
-
-static void pl330_register_types(void)
-{
-    type_register_static(&pl330_type_info);
-}
-
-type_init(pl330_register_types)
diff --git a/hw/pm_smbus.c b/hw/pm_smbus.c
deleted file mode 100644 (file)
index 0b5bb89..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * PC SMBus implementation
- * splitted from acpi.c
- *
- * Copyright (c) 2006 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see
- * <http://www.gnu.org/licenses/>.
- */
-#include "hw/hw.h"
-#include "hw/i386/pc.h"
-#include "hw/i2c/pm_smbus.h"
-#include "hw/i2c/smbus.h"
-
-/* no save/load? */
-
-#define SMBHSTSTS       0x00
-#define SMBHSTCNT       0x02
-#define SMBHSTCMD       0x03
-#define SMBHSTADD       0x04
-#define SMBHSTDAT0      0x05
-#define SMBHSTDAT1      0x06
-#define SMBBLKDAT       0x07
-
-//#define DEBUG
-
-#ifdef DEBUG
-# define SMBUS_DPRINTF(format, ...)     printf(format, ## __VA_ARGS__)
-#else
-# define SMBUS_DPRINTF(format, ...)     do { } while (0)
-#endif
-
-
-static void smb_transaction(PMSMBus *s)
-{
-    uint8_t prot = (s->smb_ctl >> 2) & 0x07;
-    uint8_t read = s->smb_addr & 0x01;
-    uint8_t cmd = s->smb_cmd;
-    uint8_t addr = s->smb_addr >> 1;
-    i2c_bus *bus = s->smbus;
-
-    SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot);
-    switch(prot) {
-    case 0x0:
-        smbus_quick_command(bus, addr, read);
-        break;
-    case 0x1:
-        if (read) {
-            s->smb_data0 = smbus_receive_byte(bus, addr);
-        } else {
-            smbus_send_byte(bus, addr, cmd);
-        }
-        break;
-    case 0x2:
-        if (read) {
-            s->smb_data0 = smbus_read_byte(bus, addr, cmd);
-        } else {
-            smbus_write_byte(bus, addr, cmd, s->smb_data0);
-        }
-        break;
-    case 0x3:
-        if (read) {
-            uint16_t val;
-            val = smbus_read_word(bus, addr, cmd);
-            s->smb_data0 = val;
-            s->smb_data1 = val >> 8;
-        } else {
-            smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0);
-        }
-        break;
-    case 0x5:
-        if (read) {
-            s->smb_data0 = smbus_read_block(bus, addr, cmd, s->smb_data);
-        } else {
-            smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0);
-        }
-        break;
-    default:
-        goto error;
-    }
-    return;
-
-  error:
-    s->smb_stat |= 0x04;
-}
-
-static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
-                              unsigned width)
-{
-    PMSMBus *s = opaque;
-
-    SMBUS_DPRINTF("SMB writeb port=0x%04x val=0x%02x\n", addr, val);
-    switch(addr) {
-    case SMBHSTSTS:
-        s->smb_stat = 0;
-        s->smb_index = 0;
-        break;
-    case SMBHSTCNT:
-        s->smb_ctl = val;
-        if (val & 0x40)
-            smb_transaction(s);
-        break;
-    case SMBHSTCMD:
-        s->smb_cmd = val;
-        break;
-    case SMBHSTADD:
-        s->smb_addr = val;
-        break;
-    case SMBHSTDAT0:
-        s->smb_data0 = val;
-        break;
-    case SMBHSTDAT1:
-        s->smb_data1 = val;
-        break;
-    case SMBBLKDAT:
-        s->smb_data[s->smb_index++] = val;
-        if (s->smb_index > 31)
-            s->smb_index = 0;
-        break;
-    default:
-        break;
-    }
-}
-
-static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width)
-{
-    PMSMBus *s = opaque;
-    uint32_t val;
-
-    switch(addr) {
-    case SMBHSTSTS:
-        val = s->smb_stat;
-        break;
-    case SMBHSTCNT:
-        s->smb_index = 0;
-        val = s->smb_ctl & 0x1f;
-        break;
-    case SMBHSTCMD:
-        val = s->smb_cmd;
-        break;
-    case SMBHSTADD:
-        val = s->smb_addr;
-        break;
-    case SMBHSTDAT0:
-        val = s->smb_data0;
-        break;
-    case SMBHSTDAT1:
-        val = s->smb_data1;
-        break;
-    case SMBBLKDAT:
-        val = s->smb_data[s->smb_index++];
-        if (s->smb_index > 31)
-            s->smb_index = 0;
-        break;
-    default:
-        val = 0;
-        break;
-    }
-    SMBUS_DPRINTF("SMB readb port=0x%04x val=0x%02x\n", addr, val);
-    return val;
-}
-
-static const MemoryRegionOps pm_smbus_ops = {
-    .read = smb_ioport_readb,
-    .write = smb_ioport_writeb,
-    .valid.min_access_size = 1,
-    .valid.max_access_size = 1,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-void pm_smbus_init(DeviceState *parent, PMSMBus *smb)
-{
-    smb->smbus = i2c_init_bus(parent, "i2c");
-    memory_region_init_io(&smb->io, &pm_smbus_ops, smb, "pm-smbus", 64);
-}
diff --git a/hw/ppce500_pci.c b/hw/ppce500_pci.c
deleted file mode 100644 (file)
index 5e7ad94..0000000
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- * QEMU PowerPC E500 embedded processors pci controller emulation
- *
- * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved.
- *
- * Author: Yu Liu,     <yu.liu@freescale.com>
- *
- * This file is derived from hw/ppc4xx_pci.c,
- * the copyright for that material belongs to the original owners.
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of  the GNU General  Public License as published by
- * the Free Software Foundation;  either version 2 of the  License, or
- * (at your option) any later version.
- */
-
-#include "hw/hw.h"
-#include "hw/ppc/e500-ccsr.h"
-#include "hw/pci/pci.h"
-#include "hw/pci/pci_host.h"
-#include "qemu/bswap.h"
-#include "hw/pci-host/ppce500.h"
-
-#ifdef DEBUG_PCI
-#define pci_debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
-#else
-#define pci_debug(fmt, ...)
-#endif
-
-#define PCIE500_CFGADDR       0x0
-#define PCIE500_CFGDATA       0x4
-#define PCIE500_REG_BASE      0xC00
-#define PCIE500_ALL_SIZE      0x1000
-#define PCIE500_REG_SIZE      (PCIE500_ALL_SIZE - PCIE500_REG_BASE)
-
-#define PCIE500_PCI_IOLEN     0x10000ULL
-
-#define PPCE500_PCI_CONFIG_ADDR         0x0
-#define PPCE500_PCI_CONFIG_DATA         0x4
-#define PPCE500_PCI_INTACK              0x8
-
-#define PPCE500_PCI_OW1                 (0xC20 - PCIE500_REG_BASE)
-#define PPCE500_PCI_OW2                 (0xC40 - PCIE500_REG_BASE)
-#define PPCE500_PCI_OW3                 (0xC60 - PCIE500_REG_BASE)
-#define PPCE500_PCI_OW4                 (0xC80 - PCIE500_REG_BASE)
-#define PPCE500_PCI_IW3                 (0xDA0 - PCIE500_REG_BASE)
-#define PPCE500_PCI_IW2                 (0xDC0 - PCIE500_REG_BASE)
-#define PPCE500_PCI_IW1                 (0xDE0 - PCIE500_REG_BASE)
-
-#define PPCE500_PCI_GASKET_TIMR         (0xE20 - PCIE500_REG_BASE)
-
-#define PCI_POTAR               0x0
-#define PCI_POTEAR              0x4
-#define PCI_POWBAR              0x8
-#define PCI_POWAR               0x10
-
-#define PCI_PITAR               0x0
-#define PCI_PIWBAR              0x8
-#define PCI_PIWBEAR             0xC
-#define PCI_PIWAR               0x10
-
-#define PPCE500_PCI_NR_POBS     5
-#define PPCE500_PCI_NR_PIBS     3
-
-struct  pci_outbound {
-    uint32_t potar;
-    uint32_t potear;
-    uint32_t powbar;
-    uint32_t powar;
-};
-
-struct pci_inbound {
-    uint32_t pitar;
-    uint32_t piwbar;
-    uint32_t piwbear;
-    uint32_t piwar;
-};
-
-#define TYPE_PPC_E500_PCI_HOST_BRIDGE "e500-pcihost"
-
-#define PPC_E500_PCI_HOST_BRIDGE(obj) \
-    OBJECT_CHECK(PPCE500PCIState, (obj), TYPE_PPC_E500_PCI_HOST_BRIDGE)
-
-struct PPCE500PCIState {
-    PCIHostState parent_obj;
-
-    struct pci_outbound pob[PPCE500_PCI_NR_POBS];
-    struct pci_inbound pib[PPCE500_PCI_NR_PIBS];
-    uint32_t gasket_time;
-    qemu_irq irq[4];
-    uint32_t first_slot;
-    /* mmio maps */
-    MemoryRegion container;
-    MemoryRegion iomem;
-    MemoryRegion pio;
-};
-
-#define TYPE_PPC_E500_PCI_BRIDGE "e500-host-bridge"
-#define PPC_E500_PCI_BRIDGE(obj) \
-    OBJECT_CHECK(PPCE500PCIBridgeState, (obj), TYPE_PPC_E500_PCI_BRIDGE)
-
-struct PPCE500PCIBridgeState {
-    /*< private >*/
-    PCIDevice parent;
-    /*< public >*/
-
-    MemoryRegion bar0;
-};
-
-typedef struct PPCE500PCIBridgeState PPCE500PCIBridgeState;
-typedef struct PPCE500PCIState PPCE500PCIState;
-
-static uint64_t pci_reg_read4(void *opaque, hwaddr addr,
-                              unsigned size)
-{
-    PPCE500PCIState *pci = opaque;
-    unsigned long win;
-    uint32_t value = 0;
-    int idx;
-
-    win = addr & 0xfe0;
-
-    switch (win) {
-    case PPCE500_PCI_OW1:
-    case PPCE500_PCI_OW2:
-    case PPCE500_PCI_OW3:
-    case PPCE500_PCI_OW4:
-        idx = (addr >> 5) & 0x7;
-        switch (addr & 0xC) {
-        case PCI_POTAR:
-            value = pci->pob[idx].potar;
-            break;
-        case PCI_POTEAR:
-            value = pci->pob[idx].potear;
-            break;
-        case PCI_POWBAR:
-            value = pci->pob[idx].powbar;
-            break;
-        case PCI_POWAR:
-            value = pci->pob[idx].powar;
-            break;
-        default:
-            break;
-        }
-        break;
-
-    case PPCE500_PCI_IW3:
-    case PPCE500_PCI_IW2:
-    case PPCE500_PCI_IW1:
-        idx = ((addr >> 5) & 0x3) - 1;
-        switch (addr & 0xC) {
-        case PCI_PITAR:
-            value = pci->pib[idx].pitar;
-            break;
-        case PCI_PIWBAR:
-            value = pci->pib[idx].piwbar;
-            break;
-        case PCI_PIWBEAR:
-            value = pci->pib[idx].piwbear;
-            break;
-        case PCI_PIWAR:
-            value = pci->pib[idx].piwar;
-            break;
-        default:
-            break;
-        };
-        break;
-
-    case PPCE500_PCI_GASKET_TIMR:
-        value = pci->gasket_time;
-        break;
-
-    default:
-        break;
-    }
-
-    pci_debug("%s: win:%lx(addr:" TARGET_FMT_plx ") -> value:%x\n", __func__,
-              win, addr, value);
-    return value;
-}
-
-static void pci_reg_write4(void *opaque, hwaddr addr,
-                           uint64_t value, unsigned size)
-{
-    PPCE500PCIState *pci = opaque;
-    unsigned long win;
-    int idx;
-
-    win = addr & 0xfe0;
-
-    pci_debug("%s: value:%x -> win:%lx(addr:" TARGET_FMT_plx ")\n",
-              __func__, (unsigned)value, win, addr);
-
-    switch (win) {
-    case PPCE500_PCI_OW1:
-    case PPCE500_PCI_OW2:
-    case PPCE500_PCI_OW3:
-    case PPCE500_PCI_OW4:
-        idx = (addr >> 5) & 0x7;
-        switch (addr & 0xC) {
-        case PCI_POTAR:
-            pci->pob[idx].potar = value;
-            break;
-        case PCI_POTEAR:
-            pci->pob[idx].potear = value;
-            break;
-        case PCI_POWBAR:
-            pci->pob[idx].powbar = value;
-            break;
-        case PCI_POWAR:
-            pci->pob[idx].powar = value;
-            break;
-        default:
-            break;
-        };
-        break;
-
-    case PPCE500_PCI_IW3:
-    case PPCE500_PCI_IW2:
-    case PPCE500_PCI_IW1:
-        idx = ((addr >> 5) & 0x3) - 1;
-        switch (addr & 0xC) {
-        case PCI_PITAR:
-            pci->pib[idx].pitar = value;
-            break;
-        case PCI_PIWBAR:
-            pci->pib[idx].piwbar = value;
-            break;
-        case PCI_PIWBEAR:
-            pci->pib[idx].piwbear = value;
-            break;
-        case PCI_PIWAR:
-            pci->pib[idx].piwar = value;
-            break;
-        default:
-            break;
-        };
-        break;
-
-    case PPCE500_PCI_GASKET_TIMR:
-        pci->gasket_time = value;
-        break;
-
-    default:
-        break;
-    };
-}
-
-static const MemoryRegionOps e500_pci_reg_ops = {
-    .read = pci_reg_read4,
-    .write = pci_reg_write4,
-    .endianness = DEVICE_BIG_ENDIAN,
-};
-
-static int mpc85xx_pci_map_irq(PCIDevice *pci_dev, int irq_num)
-{
-    int devno = pci_dev->devfn >> 3;
-    int ret;
-
-    ret = ppce500_pci_map_irq_slot(devno, irq_num);
-
-    pci_debug("%s: devfn %x irq %d -> %d  devno:%x\n", __func__,
-           pci_dev->devfn, irq_num, ret, devno);
-
-    return ret;
-}
-
-static void mpc85xx_pci_set_irq(void *opaque, int irq_num, int level)
-{
-    qemu_irq *pic = opaque;
-
-    pci_debug("%s: PCI irq %d, level:%d\n", __func__, irq_num, level);
-
-    qemu_set_irq(pic[irq_num], level);
-}
-
-static const VMStateDescription vmstate_pci_outbound = {
-    .name = "pci_outbound",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT32(potar, struct pci_outbound),
-        VMSTATE_UINT32(potear, struct pci_outbound),
-        VMSTATE_UINT32(powbar, struct pci_outbound),
-        VMSTATE_UINT32(powar, struct pci_outbound),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_pci_inbound = {
-    .name = "pci_inbound",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT32(pitar, struct pci_inbound),
-        VMSTATE_UINT32(piwbar, struct pci_inbound),
-        VMSTATE_UINT32(piwbear, struct pci_inbound),
-        VMSTATE_UINT32(piwar, struct pci_inbound),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_ppce500_pci = {
-    .name = "ppce500_pci",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_STRUCT_ARRAY(pob, PPCE500PCIState, PPCE500_PCI_NR_POBS, 1,
-                             vmstate_pci_outbound, struct pci_outbound),
-        VMSTATE_STRUCT_ARRAY(pib, PPCE500PCIState, PPCE500_PCI_NR_PIBS, 1,
-                             vmstate_pci_outbound, struct pci_inbound),
-        VMSTATE_UINT32(gasket_time, PPCE500PCIState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-#include "exec/address-spaces.h"
-
-static int e500_pcihost_bridge_initfn(PCIDevice *d)
-{
-    PPCE500PCIBridgeState *b = PPC_E500_PCI_BRIDGE(d);
-    PPCE500CCSRState *ccsr = CCSR(container_get(qdev_get_machine(),
-                                  "/e500-ccsr"));
-
-    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_PCI);
-    d->config[PCI_HEADER_TYPE] =
-        (d->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) |
-        PCI_HEADER_TYPE_BRIDGE;
-
-    memory_region_init_alias(&b->bar0, "e500-pci-bar0", &ccsr->ccsr_space,
-                             0, int128_get64(ccsr->ccsr_space.size));
-    pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &b->bar0);
-
-    return 0;
-}
-
-static int e500_pcihost_initfn(SysBusDevice *dev)
-{
-    PCIHostState *h;
-    PPCE500PCIState *s;
-    PCIBus *b;
-    int i;
-    MemoryRegion *address_space_mem = get_system_memory();
-
-    h = PCI_HOST_BRIDGE(dev);
-    s = PPC_E500_PCI_HOST_BRIDGE(dev);
-
-    for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
-        sysbus_init_irq(dev, &s->irq[i]);
-    }
-
-    memory_region_init(&s->pio, "pci-pio", PCIE500_PCI_IOLEN);
-
-    b = pci_register_bus(DEVICE(dev), NULL, mpc85xx_pci_set_irq,
-                         mpc85xx_pci_map_irq, s->irq, address_space_mem,
-                         &s->pio, PCI_DEVFN(s->first_slot, 0), 4, TYPE_PCI_BUS);
-    h->bus = b;
-
-    pci_create_simple(b, 0, "e500-host-bridge");
-
-    memory_region_init(&s->container, "pci-container", PCIE500_ALL_SIZE);
-    memory_region_init_io(&h->conf_mem, &pci_host_conf_be_ops, h,
-                          "pci-conf-idx", 4);
-    memory_region_init_io(&h->data_mem, &pci_host_data_le_ops, h,
-                          "pci-conf-data", 4);
-    memory_region_init_io(&s->iomem, &e500_pci_reg_ops, s,
-                          "pci.reg", PCIE500_REG_SIZE);
-    memory_region_add_subregion(&s->container, PCIE500_CFGADDR, &h->conf_mem);
-    memory_region_add_subregion(&s->container, PCIE500_CFGDATA, &h->data_mem);
-    memory_region_add_subregion(&s->container, PCIE500_REG_BASE, &s->iomem);
-    sysbus_init_mmio(dev, &s->container);
-    sysbus_init_mmio(dev, &s->pio);
-
-    return 0;
-}
-
-static void e500_host_bridge_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = e500_pcihost_bridge_initfn;
-    k->vendor_id = PCI_VENDOR_ID_FREESCALE;
-    k->device_id = PCI_DEVICE_ID_MPC8533E;
-    k->class_id = PCI_CLASS_PROCESSOR_POWERPC;
-    dc->desc = "Host bridge";
-}
-
-static const TypeInfo e500_host_bridge_info = {
-    .name          = "e500-host-bridge",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PPCE500PCIBridgeState),
-    .class_init    = e500_host_bridge_class_init,
-};
-
-static Property pcihost_properties[] = {
-    DEFINE_PROP_UINT32("first_slot", PPCE500PCIState, first_slot, 0x11),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void e500_pcihost_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = e500_pcihost_initfn;
-    dc->props = pcihost_properties;
-    dc->vmsd = &vmstate_ppce500_pci;
-}
-
-static const TypeInfo e500_pcihost_info = {
-    .name          = TYPE_PPC_E500_PCI_HOST_BRIDGE,
-    .parent        = TYPE_PCI_HOST_BRIDGE,
-    .instance_size = sizeof(PPCE500PCIState),
-    .class_init    = e500_pcihost_class_init,
-};
-
-static void e500_pci_register_types(void)
-{
-    type_register_static(&e500_pcihost_info);
-    type_register_static(&e500_host_bridge_info);
-}
-
-type_init(e500_pci_register_types)
diff --git a/hw/prep_pci.c b/hw/prep_pci.c
deleted file mode 100644 (file)
index 6130253..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * QEMU PREP PCI host
- *
- * Copyright (c) 2006 Fabrice Bellard
- * Copyright (c) 2011-2013 Andreas Färber
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/hw.h"
-#include "hw/pci/pci.h"
-#include "hw/pci/pci_bus.h"
-#include "hw/pci/pci_host.h"
-#include "hw/i386/pc.h"
-#include "exec/address-spaces.h"
-
-#define TYPE_RAVEN_PCI_DEVICE "raven"
-#define TYPE_RAVEN_PCI_HOST_BRIDGE "raven-pcihost"
-
-#define RAVEN_PCI_DEVICE(obj) \
-    OBJECT_CHECK(RavenPCIState, (obj), TYPE_RAVEN_PCI_DEVICE)
-
-typedef struct RavenPCIState {
-    PCIDevice dev;
-} RavenPCIState;
-
-#define RAVEN_PCI_HOST_BRIDGE(obj) \
-    OBJECT_CHECK(PREPPCIState, (obj), TYPE_RAVEN_PCI_HOST_BRIDGE)
-
-typedef struct PRePPCIState {
-    PCIHostState parent_obj;
-
-    MemoryRegion intack;
-    qemu_irq irq[4];
-    PCIBus pci_bus;
-    RavenPCIState pci_dev;
-} PREPPCIState;
-
-static inline uint32_t PPC_PCIIO_config(hwaddr addr)
-{
-    int i;
-
-    for (i = 0; i < 11; i++) {
-        if ((addr & (1 << (11 + i))) != 0) {
-            break;
-        }
-    }
-    return (addr & 0x7ff) |  (i << 11);
-}
-
-static void ppc_pci_io_write(void *opaque, hwaddr addr,
-                             uint64_t val, unsigned int size)
-{
-    PREPPCIState *s = opaque;
-    PCIHostState *phb = PCI_HOST_BRIDGE(s);
-    pci_data_write(phb->bus, PPC_PCIIO_config(addr), val, size);
-}
-
-static uint64_t ppc_pci_io_read(void *opaque, hwaddr addr,
-                                unsigned int size)
-{
-    PREPPCIState *s = opaque;
-    PCIHostState *phb = PCI_HOST_BRIDGE(s);
-    return pci_data_read(phb->bus, PPC_PCIIO_config(addr), size);
-}
-
-static const MemoryRegionOps PPC_PCIIO_ops = {
-    .read = ppc_pci_io_read,
-    .write = ppc_pci_io_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static uint64_t ppc_intack_read(void *opaque, hwaddr addr,
-                                unsigned int size)
-{
-    return pic_read_irq(isa_pic);
-}
-
-static const MemoryRegionOps PPC_intack_ops = {
-    .read = ppc_intack_read,
-    .valid = {
-        .max_access_size = 1,
-    },
-};
-
-static int prep_map_irq(PCIDevice *pci_dev, int irq_num)
-{
-    return (irq_num + (pci_dev->devfn >> 3)) & 1;
-}
-
-static void prep_set_irq(void *opaque, int irq_num, int level)
-{
-    qemu_irq *pic = opaque;
-
-    qemu_set_irq(pic[irq_num] , level);
-}
-
-static void raven_pcihost_realizefn(DeviceState *d, Error **errp)
-{
-    SysBusDevice *dev = SYS_BUS_DEVICE(d);
-    PCIHostState *h = PCI_HOST_BRIDGE(dev);
-    PREPPCIState *s = RAVEN_PCI_HOST_BRIDGE(dev);
-    MemoryRegion *address_space_mem = get_system_memory();
-    int i;
-
-    for (i = 0; i < 4; i++) {
-        sysbus_init_irq(dev, &s->irq[i]);
-    }
-
-    pci_bus_irqs(&s->pci_bus, prep_set_irq, prep_map_irq, s->irq, 4);
-
-    memory_region_init_io(&h->conf_mem, &pci_host_conf_be_ops, s,
-                          "pci-conf-idx", 1);
-    sysbus_add_io(dev, 0xcf8, &h->conf_mem);
-    sysbus_init_ioports(&h->busdev, 0xcf8, 1);
-
-    memory_region_init_io(&h->data_mem, &pci_host_data_be_ops, s,
-                          "pci-conf-data", 1);
-    sysbus_add_io(dev, 0xcfc, &h->data_mem);
-    sysbus_init_ioports(&h->busdev, 0xcfc, 1);
-
-    memory_region_init_io(&h->mmcfg, &PPC_PCIIO_ops, s, "pciio", 0x00400000);
-    memory_region_add_subregion(address_space_mem, 0x80800000, &h->mmcfg);
-
-    memory_region_init_io(&s->intack, &PPC_intack_ops, s, "pci-intack", 1);
-    memory_region_add_subregion(address_space_mem, 0xbffffff0, &s->intack);
-
-    /* TODO Remove once realize propagates to child devices. */
-    object_property_set_bool(OBJECT(&s->pci_dev), true, "realized", errp);
-}
-
-static void raven_pcihost_initfn(Object *obj)
-{
-    PCIHostState *h = PCI_HOST_BRIDGE(obj);
-    PREPPCIState *s = RAVEN_PCI_HOST_BRIDGE(obj);
-    MemoryRegion *address_space_mem = get_system_memory();
-    MemoryRegion *address_space_io = get_system_io();
-    DeviceState *pci_dev;
-
-    pci_bus_new_inplace(&s->pci_bus, DEVICE(obj), NULL,
-                        address_space_mem, address_space_io, 0, TYPE_PCI_BUS);
-    h->bus = &s->pci_bus;
-
-    object_initialize(&s->pci_dev, TYPE_RAVEN_PCI_DEVICE);
-    pci_dev = DEVICE(&s->pci_dev);
-    qdev_set_parent_bus(pci_dev, BUS(&s->pci_bus));
-    object_property_set_int(OBJECT(&s->pci_dev), PCI_DEVFN(0, 0), "addr",
-                            NULL);
-    qdev_prop_set_bit(pci_dev, "multifunction", false);
-}
-
-static int raven_init(PCIDevice *d)
-{
-    d->config[0x0C] = 0x08; // cache_line_size
-    d->config[0x0D] = 0x10; // latency_timer
-    d->config[0x34] = 0x00; // capabilities_pointer
-
-    return 0;
-}
-
-static const VMStateDescription vmstate_raven = {
-    .name = "raven",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .fields = (VMStateField[]) {
-        VMSTATE_PCI_DEVICE(dev, RavenPCIState),
-        VMSTATE_END_OF_LIST()
-    },
-};
-
-static void raven_class_init(ObjectClass *klass, void *data)
-{
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    k->init = raven_init;
-    k->vendor_id = PCI_VENDOR_ID_MOTOROLA;
-    k->device_id = PCI_DEVICE_ID_MOTOROLA_RAVEN;
-    k->revision = 0x00;
-    k->class_id = PCI_CLASS_BRIDGE_HOST;
-    dc->desc = "PReP Host Bridge - Motorola Raven";
-    dc->vmsd = &vmstate_raven;
-    dc->no_user = 1;
-}
-
-static const TypeInfo raven_info = {
-    .name = TYPE_RAVEN_PCI_DEVICE,
-    .parent = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(RavenPCIState),
-    .class_init = raven_class_init,
-};
-
-static void raven_pcihost_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    dc->realize = raven_pcihost_realizefn;
-    dc->fw_name = "pci";
-    dc->no_user = 1;
-}
-
-static const TypeInfo raven_pcihost_info = {
-    .name = TYPE_RAVEN_PCI_HOST_BRIDGE,
-    .parent = TYPE_PCI_HOST_BRIDGE,
-    .instance_size = sizeof(PREPPCIState),
-    .instance_init = raven_pcihost_initfn,
-    .class_init = raven_pcihost_class_init,
-};
-
-static void raven_register_types(void)
-{
-    type_register_static(&raven_pcihost_info);
-    type_register_static(&raven_info);
-}
-
-type_init(raven_register_types)
diff --git a/hw/ps2.c b/hw/ps2.c
deleted file mode 100644 (file)
index 3412079..0000000
--- a/hw/ps2.c
+++ /dev/null
@@ -1,676 +0,0 @@
-/*
- * QEMU PS/2 keyboard/mouse emulation
- *
- * Copyright (c) 2003 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/input/ps2.h"
-#include "ui/console.h"
-#include "sysemu/sysemu.h"
-
-/* debug PC keyboard */
-//#define DEBUG_KBD
-
-/* debug PC keyboard : only mouse */
-//#define DEBUG_MOUSE
-
-/* Keyboard Commands */
-#define KBD_CMD_SET_LEDS       0xED    /* Set keyboard leds */
-#define KBD_CMD_ECHO           0xEE
-#define KBD_CMD_SCANCODE       0xF0    /* Get/set scancode set */
-#define KBD_CMD_GET_ID                 0xF2    /* get keyboard ID */
-#define KBD_CMD_SET_RATE       0xF3    /* Set typematic rate */
-#define KBD_CMD_ENABLE         0xF4    /* Enable scanning */
-#define KBD_CMD_RESET_DISABLE  0xF5    /* reset and disable scanning */
-#define KBD_CMD_RESET_ENABLE           0xF6    /* reset and enable scanning */
-#define KBD_CMD_RESET          0xFF    /* Reset */
-
-/* Keyboard Replies */
-#define KBD_REPLY_POR          0xAA    /* Power on reset */
-#define KBD_REPLY_ID           0xAB    /* Keyboard ID */
-#define KBD_REPLY_ACK          0xFA    /* Command ACK */
-#define KBD_REPLY_RESEND       0xFE    /* Command NACK, send the cmd again */
-
-/* Mouse Commands */
-#define AUX_SET_SCALE11                0xE6    /* Set 1:1 scaling */
-#define AUX_SET_SCALE21                0xE7    /* Set 2:1 scaling */
-#define AUX_SET_RES            0xE8    /* Set resolution */
-#define AUX_GET_SCALE          0xE9    /* Get scaling factor */
-#define AUX_SET_STREAM         0xEA    /* Set stream mode */
-#define AUX_POLL               0xEB    /* Poll */
-#define AUX_RESET_WRAP         0xEC    /* Reset wrap mode */
-#define AUX_SET_WRAP           0xEE    /* Set wrap mode */
-#define AUX_SET_REMOTE         0xF0    /* Set remote mode */
-#define AUX_GET_TYPE           0xF2    /* Get type */
-#define AUX_SET_SAMPLE         0xF3    /* Set sample rate */
-#define AUX_ENABLE_DEV         0xF4    /* Enable aux device */
-#define AUX_DISABLE_DEV                0xF5    /* Disable aux device */
-#define AUX_SET_DEFAULT                0xF6
-#define AUX_RESET              0xFF    /* Reset aux device */
-#define AUX_ACK                        0xFA    /* Command byte ACK. */
-
-#define MOUSE_STATUS_REMOTE     0x40
-#define MOUSE_STATUS_ENABLED    0x20
-#define MOUSE_STATUS_SCALE21    0x10
-
-#define PS2_QUEUE_SIZE 256
-
-typedef struct {
-    uint8_t data[PS2_QUEUE_SIZE];
-    int rptr, wptr, count;
-} PS2Queue;
-
-typedef struct {
-    PS2Queue queue;
-    int32_t write_cmd;
-    void (*update_irq)(void *, int);
-    void *update_arg;
-} PS2State;
-
-typedef struct {
-    PS2State common;
-    int scan_enabled;
-    /* QEMU uses translated PC scancodes internally.  To avoid multiple
-       conversions we do the translation (if any) in the PS/2 emulation
-       not the keyboard controller.  */
-    int translate;
-    int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */
-    int ledstate;
-} PS2KbdState;
-
-typedef struct {
-    PS2State common;
-    uint8_t mouse_status;
-    uint8_t mouse_resolution;
-    uint8_t mouse_sample_rate;
-    uint8_t mouse_wrap;
-    uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
-    uint8_t mouse_detect_state;
-    int mouse_dx; /* current values, needed for 'poll' mode */
-    int mouse_dy;
-    int mouse_dz;
-    uint8_t mouse_buttons;
-} PS2MouseState;
-
-/* Table to convert from PC scancodes to raw scancodes.  */
-static const unsigned char ps2_raw_keycode[128] = {
-  0, 118,  22,  30,  38,  37,  46,  54,  61,  62,  70,  69,  78,  85, 102,  13,
- 21,  29,  36,  45,  44,  53,  60,  67,  68,  77,  84,  91,  90,  20,  28,  27,
- 35,  43,  52,  51,  59,  66,  75,  76,  82,  14,  18,  93,  26,  34,  33,  42,
- 50,  49,  58,  65,  73,  74,  89, 124,  17,  41,  88,   5,   6,   4,  12,   3,
- 11,   2,  10,   1,   9, 119, 126, 108, 117, 125, 123, 107, 115, 116, 121, 105,
-114, 122, 112, 113, 127,  96,  97, 120,   7,  15,  23,  31,  39,  47,  55,  63,
- 71,  79,  86,  94,   8,  16,  24,  32,  40,  48,  56,  64,  72,  80,  87, 111,
- 19,  25,  57,  81,  83,  92,  95,  98,  99, 100, 101, 103, 104, 106, 109, 110
-};
-static const unsigned char ps2_raw_keycode_set3[128] = {
-  0,   8,  22,  30,  38,  37,  46,  54,  61,  62,  70,  69,  78,  85, 102,  13,
- 21,  29,  36,  45,  44,  53,  60,  67,  68,  77,  84,  91,  90,  17,  28,  27,
- 35,  43,  52,  51,  59,  66,  75,  76,  82,  14,  18,  92,  26,  34,  33,  42,
- 50,  49,  58,  65,  73,  74,  89, 126,  25,  41,  20,   7,  15,  23,  31,  39,
- 47,   2,  63,  71,  79, 118,  95, 108, 117, 125, 132, 107, 115, 116, 124, 105,
-114, 122, 112, 113, 127,  96,  97,  86,  94,  15,  23,  31,  39,  47,  55,  63,
- 71,  79,  86,  94,   8,  16,  24,  32,  40,  48,  56,  64,  72,  80,  87, 111,
- 19,  25,  57,  81,  83,  92,  95,  98,  99, 100, 101, 103, 104, 106, 109, 110
-};
-
-void ps2_queue(void *opaque, int b)
-{
-    PS2State *s = (PS2State *)opaque;
-    PS2Queue *q = &s->queue;
-
-    if (q->count >= PS2_QUEUE_SIZE)
-        return;
-    q->data[q->wptr] = b;
-    if (++q->wptr == PS2_QUEUE_SIZE)
-        q->wptr = 0;
-    q->count++;
-    s->update_irq(s->update_arg, 1);
-}
-
-/*
-   keycode is expressed as follow:
-   bit 7    - 0 key pressed, 1 = key released
-   bits 6-0 - translated scancode set 2
- */
-static void ps2_put_keycode(void *opaque, int keycode)
-{
-    PS2KbdState *s = opaque;
-
-    qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
-    /* XXX: add support for scancode set 1 */
-    if (!s->translate && keycode < 0xe0 && s->scancode_set > 1) {
-        if (keycode & 0x80) {
-            ps2_queue(&s->common, 0xf0);
-        }
-        if (s->scancode_set == 2) {
-            keycode = ps2_raw_keycode[keycode & 0x7f];
-        } else if (s->scancode_set == 3) {
-            keycode = ps2_raw_keycode_set3[keycode & 0x7f];
-        }
-      }
-    ps2_queue(&s->common, keycode);
-}
-
-uint32_t ps2_read_data(void *opaque)
-{
-    PS2State *s = (PS2State *)opaque;
-    PS2Queue *q;
-    int val, index;
-
-    q = &s->queue;
-    if (q->count == 0) {
-        /* NOTE: if no data left, we return the last keyboard one
-           (needed for EMM386) */
-        /* XXX: need a timer to do things correctly */
-        index = q->rptr - 1;
-        if (index < 0)
-            index = PS2_QUEUE_SIZE - 1;
-        val = q->data[index];
-    } else {
-        val = q->data[q->rptr];
-        if (++q->rptr == PS2_QUEUE_SIZE)
-            q->rptr = 0;
-        q->count--;
-        /* reading deasserts IRQ */
-        s->update_irq(s->update_arg, 0);
-        /* reassert IRQs if data left */
-        s->update_irq(s->update_arg, q->count != 0);
-    }
-    return val;
-}
-
-static void ps2_set_ledstate(PS2KbdState *s, int ledstate)
-{
-    s->ledstate = ledstate;
-    kbd_put_ledstate(ledstate);
-}
-
-static void ps2_reset_keyboard(PS2KbdState *s)
-{
-    s->scan_enabled = 1;
-    s->scancode_set = 2;
-    ps2_set_ledstate(s, 0);
-}
-
-void ps2_write_keyboard(void *opaque, int val)
-{
-    PS2KbdState *s = (PS2KbdState *)opaque;
-
-    switch(s->common.write_cmd) {
-    default:
-    case -1:
-        switch(val) {
-        case 0x00:
-            ps2_queue(&s->common, KBD_REPLY_ACK);
-            break;
-        case 0x05:
-            ps2_queue(&s->common, KBD_REPLY_RESEND);
-            break;
-        case KBD_CMD_GET_ID:
-            ps2_queue(&s->common, KBD_REPLY_ACK);
-            /* We emulate a MF2 AT keyboard here */
-            ps2_queue(&s->common, KBD_REPLY_ID);
-            if (s->translate)
-                ps2_queue(&s->common, 0x41);
-            else
-                ps2_queue(&s->common, 0x83);
-            break;
-        case KBD_CMD_ECHO:
-            ps2_queue(&s->common, KBD_CMD_ECHO);
-            break;
-        case KBD_CMD_ENABLE:
-            s->scan_enabled = 1;
-            ps2_queue(&s->common, KBD_REPLY_ACK);
-            break;
-        case KBD_CMD_SCANCODE:
-        case KBD_CMD_SET_LEDS:
-        case KBD_CMD_SET_RATE:
-            s->common.write_cmd = val;
-            ps2_queue(&s->common, KBD_REPLY_ACK);
-            break;
-        case KBD_CMD_RESET_DISABLE:
-            ps2_reset_keyboard(s);
-            s->scan_enabled = 0;
-            ps2_queue(&s->common, KBD_REPLY_ACK);
-            break;
-        case KBD_CMD_RESET_ENABLE:
-            ps2_reset_keyboard(s);
-            s->scan_enabled = 1;
-            ps2_queue(&s->common, KBD_REPLY_ACK);
-            break;
-        case KBD_CMD_RESET:
-            ps2_reset_keyboard(s);
-            ps2_queue(&s->common, KBD_REPLY_ACK);
-            ps2_queue(&s->common, KBD_REPLY_POR);
-            break;
-        default:
-            ps2_queue(&s->common, KBD_REPLY_ACK);
-            break;
-        }
-        break;
-    case KBD_CMD_SCANCODE:
-        if (val == 0) {
-            if (s->scancode_set == 1)
-                ps2_put_keycode(s, 0x43);
-            else if (s->scancode_set == 2)
-                ps2_put_keycode(s, 0x41);
-            else if (s->scancode_set == 3)
-                ps2_put_keycode(s, 0x3f);
-        } else {
-            if (val >= 1 && val <= 3)
-                s->scancode_set = val;
-            ps2_queue(&s->common, KBD_REPLY_ACK);
-        }
-        s->common.write_cmd = -1;
-        break;
-    case KBD_CMD_SET_LEDS:
-        ps2_set_ledstate(s, val);
-        ps2_queue(&s->common, KBD_REPLY_ACK);
-        s->common.write_cmd = -1;
-        break;
-    case KBD_CMD_SET_RATE:
-        ps2_queue(&s->common, KBD_REPLY_ACK);
-        s->common.write_cmd = -1;
-        break;
-    }
-}
-
-/* Set the scancode translation mode.
-   0 = raw scancodes.
-   1 = translated scancodes (used by qemu internally).  */
-
-void ps2_keyboard_set_translation(void *opaque, int mode)
-{
-    PS2KbdState *s = (PS2KbdState *)opaque;
-    s->translate = mode;
-}
-
-static void ps2_mouse_send_packet(PS2MouseState *s)
-{
-    unsigned int b;
-    int dx1, dy1, dz1;
-
-    dx1 = s->mouse_dx;
-    dy1 = s->mouse_dy;
-    dz1 = s->mouse_dz;
-    /* XXX: increase range to 8 bits ? */
-    if (dx1 > 127)
-        dx1 = 127;
-    else if (dx1 < -127)
-        dx1 = -127;
-    if (dy1 > 127)
-        dy1 = 127;
-    else if (dy1 < -127)
-        dy1 = -127;
-    b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
-    ps2_queue(&s->common, b);
-    ps2_queue(&s->common, dx1 & 0xff);
-    ps2_queue(&s->common, dy1 & 0xff);
-    /* extra byte for IMPS/2 or IMEX */
-    switch(s->mouse_type) {
-    default:
-        break;
-    case 3:
-        if (dz1 > 127)
-            dz1 = 127;
-        else if (dz1 < -127)
-                dz1 = -127;
-        ps2_queue(&s->common, dz1 & 0xff);
-        break;
-    case 4:
-        if (dz1 > 7)
-            dz1 = 7;
-        else if (dz1 < -7)
-            dz1 = -7;
-        b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
-        ps2_queue(&s->common, b);
-        break;
-    }
-
-    /* update deltas */
-    s->mouse_dx -= dx1;
-    s->mouse_dy -= dy1;
-    s->mouse_dz -= dz1;
-}
-
-static void ps2_mouse_event(void *opaque,
-                            int dx, int dy, int dz, int buttons_state)
-{
-    PS2MouseState *s = opaque;
-
-    /* check if deltas are recorded when disabled */
-    if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
-        return;
-
-    s->mouse_dx += dx;
-    s->mouse_dy -= dy;
-    s->mouse_dz += dz;
-    /* XXX: SDL sometimes generates nul events: we delete them */
-    if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 &&
-        s->mouse_buttons == buttons_state)
-       return;
-    s->mouse_buttons = buttons_state;
-
-    if (buttons_state) {
-        qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
-    }
-
-    if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
-        (s->common.queue.count < (PS2_QUEUE_SIZE - 16))) {
-        for(;;) {
-            /* if not remote, send event. Multiple events are sent if
-               too big deltas */
-            ps2_mouse_send_packet(s);
-            if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
-                break;
-        }
-    }
-}
-
-void ps2_mouse_fake_event(void *opaque)
-{
-    ps2_mouse_event(opaque, 1, 0, 0, 0);
-}
-
-void ps2_write_mouse(void *opaque, int val)
-{
-    PS2MouseState *s = (PS2MouseState *)opaque;
-#ifdef DEBUG_MOUSE
-    printf("kbd: write mouse 0x%02x\n", val);
-#endif
-    switch(s->common.write_cmd) {
-    default:
-    case -1:
-        /* mouse command */
-        if (s->mouse_wrap) {
-            if (val == AUX_RESET_WRAP) {
-                s->mouse_wrap = 0;
-                ps2_queue(&s->common, AUX_ACK);
-                return;
-            } else if (val != AUX_RESET) {
-                ps2_queue(&s->common, val);
-                return;
-            }
-        }
-        switch(val) {
-        case AUX_SET_SCALE11:
-            s->mouse_status &= ~MOUSE_STATUS_SCALE21;
-            ps2_queue(&s->common, AUX_ACK);
-            break;
-        case AUX_SET_SCALE21:
-            s->mouse_status |= MOUSE_STATUS_SCALE21;
-            ps2_queue(&s->common, AUX_ACK);
-            break;
-        case AUX_SET_STREAM:
-            s->mouse_status &= ~MOUSE_STATUS_REMOTE;
-            ps2_queue(&s->common, AUX_ACK);
-            break;
-        case AUX_SET_WRAP:
-            s->mouse_wrap = 1;
-            ps2_queue(&s->common, AUX_ACK);
-            break;
-        case AUX_SET_REMOTE:
-            s->mouse_status |= MOUSE_STATUS_REMOTE;
-            ps2_queue(&s->common, AUX_ACK);
-            break;
-        case AUX_GET_TYPE:
-            ps2_queue(&s->common, AUX_ACK);
-            ps2_queue(&s->common, s->mouse_type);
-            break;
-        case AUX_SET_RES:
-        case AUX_SET_SAMPLE:
-            s->common.write_cmd = val;
-            ps2_queue(&s->common, AUX_ACK);
-            break;
-        case AUX_GET_SCALE:
-            ps2_queue(&s->common, AUX_ACK);
-            ps2_queue(&s->common, s->mouse_status);
-            ps2_queue(&s->common, s->mouse_resolution);
-            ps2_queue(&s->common, s->mouse_sample_rate);
-            break;
-        case AUX_POLL:
-            ps2_queue(&s->common, AUX_ACK);
-            ps2_mouse_send_packet(s);
-            break;
-        case AUX_ENABLE_DEV:
-            s->mouse_status |= MOUSE_STATUS_ENABLED;
-            ps2_queue(&s->common, AUX_ACK);
-            break;
-        case AUX_DISABLE_DEV:
-            s->mouse_status &= ~MOUSE_STATUS_ENABLED;
-            ps2_queue(&s->common, AUX_ACK);
-            break;
-        case AUX_SET_DEFAULT:
-            s->mouse_sample_rate = 100;
-            s->mouse_resolution = 2;
-            s->mouse_status = 0;
-            ps2_queue(&s->common, AUX_ACK);
-            break;
-        case AUX_RESET:
-            s->mouse_sample_rate = 100;
-            s->mouse_resolution = 2;
-            s->mouse_status = 0;
-            s->mouse_type = 0;
-            ps2_queue(&s->common, AUX_ACK);
-            ps2_queue(&s->common, 0xaa);
-            ps2_queue(&s->common, s->mouse_type);
-            break;
-        default:
-            break;
-        }
-        break;
-    case AUX_SET_SAMPLE:
-        s->mouse_sample_rate = val;
-        /* detect IMPS/2 or IMEX */
-        switch(s->mouse_detect_state) {
-        default:
-        case 0:
-            if (val == 200)
-                s->mouse_detect_state = 1;
-            break;
-        case 1:
-            if (val == 100)
-                s->mouse_detect_state = 2;
-            else if (val == 200)
-                s->mouse_detect_state = 3;
-            else
-                s->mouse_detect_state = 0;
-            break;
-        case 2:
-            if (val == 80)
-                s->mouse_type = 3; /* IMPS/2 */
-            s->mouse_detect_state = 0;
-            break;
-        case 3:
-            if (val == 80)
-                s->mouse_type = 4; /* IMEX */
-            s->mouse_detect_state = 0;
-            break;
-        }
-        ps2_queue(&s->common, AUX_ACK);
-        s->common.write_cmd = -1;
-        break;
-    case AUX_SET_RES:
-        s->mouse_resolution = val;
-        ps2_queue(&s->common, AUX_ACK);
-        s->common.write_cmd = -1;
-        break;
-    }
-}
-
-static void ps2_common_reset(PS2State *s)
-{
-    PS2Queue *q;
-    s->write_cmd = -1;
-    q = &s->queue;
-    q->rptr = 0;
-    q->wptr = 0;
-    q->count = 0;
-    s->update_irq(s->update_arg, 0);
-}
-
-static void ps2_kbd_reset(void *opaque)
-{
-    PS2KbdState *s = (PS2KbdState *) opaque;
-
-    ps2_common_reset(&s->common);
-    s->scan_enabled = 0;
-    s->translate = 0;
-    s->scancode_set = 0;
-}
-
-static void ps2_mouse_reset(void *opaque)
-{
-    PS2MouseState *s = (PS2MouseState *) opaque;
-
-    ps2_common_reset(&s->common);
-    s->mouse_status = 0;
-    s->mouse_resolution = 0;
-    s->mouse_sample_rate = 0;
-    s->mouse_wrap = 0;
-    s->mouse_type = 0;
-    s->mouse_detect_state = 0;
-    s->mouse_dx = 0;
-    s->mouse_dy = 0;
-    s->mouse_dz = 0;
-    s->mouse_buttons = 0;
-}
-
-static const VMStateDescription vmstate_ps2_common = {
-    .name = "PS2 Common State",
-    .version_id = 3,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .fields      = (VMStateField []) {
-        VMSTATE_INT32(write_cmd, PS2State),
-        VMSTATE_INT32(queue.rptr, PS2State),
-        VMSTATE_INT32(queue.wptr, PS2State),
-        VMSTATE_INT32(queue.count, PS2State),
-        VMSTATE_BUFFER(queue.data, PS2State),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static bool ps2_keyboard_ledstate_needed(void *opaque)
-{
-    PS2KbdState *s = opaque;
-
-    return s->ledstate != 0; /* 0 is default state */
-}
-
-static int ps2_kbd_ledstate_post_load(void *opaque, int version_id)
-{
-    PS2KbdState *s = opaque;
-
-    kbd_put_ledstate(s->ledstate);
-    return 0;
-}
-
-static const VMStateDescription vmstate_ps2_keyboard_ledstate = {
-    .name = "ps2kbd/ledstate",
-    .version_id = 3,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .post_load = ps2_kbd_ledstate_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_INT32(ledstate, PS2KbdState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int ps2_kbd_post_load(void* opaque, int version_id)
-{
-    PS2KbdState *s = (PS2KbdState*)opaque;
-
-    if (version_id == 2)
-        s->scancode_set=2;
-    return 0;
-}
-
-static const VMStateDescription vmstate_ps2_keyboard = {
-    .name = "ps2kbd",
-    .version_id = 3,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .post_load = ps2_kbd_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_STRUCT(common, PS2KbdState, 0, vmstate_ps2_common, PS2State),
-        VMSTATE_INT32(scan_enabled, PS2KbdState),
-        VMSTATE_INT32(translate, PS2KbdState),
-        VMSTATE_INT32_V(scancode_set, PS2KbdState,3),
-        VMSTATE_END_OF_LIST()
-    },
-    .subsections = (VMStateSubsection []) {
-        {
-            .vmsd = &vmstate_ps2_keyboard_ledstate,
-            .needed = ps2_keyboard_ledstate_needed,
-        }, {
-            /* empty */
-        }
-    }
-};
-
-static const VMStateDescription vmstate_ps2_mouse = {
-    .name = "ps2mouse",
-    .version_id = 2,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .fields      = (VMStateField []) {
-        VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State),
-        VMSTATE_UINT8(mouse_status, PS2MouseState),
-        VMSTATE_UINT8(mouse_resolution, PS2MouseState),
-        VMSTATE_UINT8(mouse_sample_rate, PS2MouseState),
-        VMSTATE_UINT8(mouse_wrap, PS2MouseState),
-        VMSTATE_UINT8(mouse_type, PS2MouseState),
-        VMSTATE_UINT8(mouse_detect_state, PS2MouseState),
-        VMSTATE_INT32(mouse_dx, PS2MouseState),
-        VMSTATE_INT32(mouse_dy, PS2MouseState),
-        VMSTATE_INT32(mouse_dz, PS2MouseState),
-        VMSTATE_UINT8(mouse_buttons, PS2MouseState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
-{
-    PS2KbdState *s = (PS2KbdState *)g_malloc0(sizeof(PS2KbdState));
-
-    s->common.update_irq = update_irq;
-    s->common.update_arg = update_arg;
-    s->scancode_set = 2;
-    vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s);
-    qemu_add_kbd_event_handler(ps2_put_keycode, s);
-    qemu_register_reset(ps2_kbd_reset, s);
-    return s;
-}
-
-void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
-{
-    PS2MouseState *s = (PS2MouseState *)g_malloc0(sizeof(PS2MouseState));
-
-    s->common.update_irq = update_irq;
-    s->common.update_arg = update_arg;
-    vmstate_register(NULL, 0, &vmstate_ps2_mouse, s);
-    qemu_add_mouse_event_handler(ps2_mouse_event, s, 0, "QEMU PS/2 Mouse");
-    qemu_register_reset(ps2_mouse_reset, s);
-    return s;
-}
diff --git a/hw/ptimer.c b/hw/ptimer.c
deleted file mode 100644 (file)
index 4bc96c9..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * General purpose implementation of a simple periodic countdown timer.
- *
- * Copyright (c) 2007 CodeSourcery.
- *
- * This code is licensed under the GNU LGPL.
- */
-#include "hw/hw.h"
-#include "qemu/timer.h"
-#include "hw/ptimer.h"
-#include "qemu/host-utils.h"
-
-struct ptimer_state
-{
-    uint8_t enabled; /* 0 = disabled, 1 = periodic, 2 = oneshot.  */
-    uint64_t limit;
-    uint64_t delta;
-    uint32_t period_frac;
-    int64_t period;
-    int64_t last_event;
-    int64_t next_event;
-    QEMUBH *bh;
-    QEMUTimer *timer;
-};
-
-/* Use a bottom-half routine to avoid reentrancy issues.  */
-static void ptimer_trigger(ptimer_state *s)
-{
-    if (s->bh) {
-        qemu_bh_schedule(s->bh);
-    }
-}
-
-static void ptimer_reload(ptimer_state *s)
-{
-    if (s->delta == 0) {
-        ptimer_trigger(s);
-        s->delta = s->limit;
-    }
-    if (s->delta == 0 || s->period == 0) {
-        fprintf(stderr, "Timer with period zero, disabling\n");
-        s->enabled = 0;
-        return;
-    }
-
-    s->last_event = s->next_event;
-    s->next_event = s->last_event + s->delta * s->period;
-    if (s->period_frac) {
-        s->next_event += ((int64_t)s->period_frac * s->delta) >> 32;
-    }
-    qemu_mod_timer(s->timer, s->next_event);
-}
-
-static void ptimer_tick(void *opaque)
-{
-    ptimer_state *s = (ptimer_state *)opaque;
-    ptimer_trigger(s);
-    s->delta = 0;
-    if (s->enabled == 2) {
-        s->enabled = 0;
-    } else {
-        ptimer_reload(s);
-    }
-}
-
-uint64_t ptimer_get_count(ptimer_state *s)
-{
-    int64_t now;
-    uint64_t counter;
-
-    if (s->enabled) {
-        now = qemu_get_clock_ns(vm_clock);
-        /* Figure out the current counter value.  */
-        if (now - s->next_event > 0
-            || s->period == 0) {
-            /* Prevent timer underflowing if it should already have
-               triggered.  */
-            counter = 0;
-        } else {
-            uint64_t rem;
-            uint64_t div;
-            int clz1, clz2;
-            int shift;
-
-            /* We need to divide time by period, where time is stored in
-               rem (64-bit integer) and period is stored in period/period_frac
-               (64.32 fixed point).
-              
-               Doing full precision division is hard, so scale values and
-               do a 64-bit division.  The result should be rounded down,
-               so that the rounding error never causes the timer to go
-               backwards.
-            */
-
-            rem = s->next_event - now;
-            div = s->period;
-
-            clz1 = clz64(rem);
-            clz2 = clz64(div);
-            shift = clz1 < clz2 ? clz1 : clz2;
-
-            rem <<= shift;
-            div <<= shift;
-            if (shift >= 32) {
-                div |= ((uint64_t)s->period_frac << (shift - 32));
-            } else {
-                if (shift != 0)
-                    div |= (s->period_frac >> (32 - shift));
-                /* Look at remaining bits of period_frac and round div up if 
-                   necessary.  */
-                if ((uint32_t)(s->period_frac << shift))
-                    div += 1;
-            }
-            counter = rem / div;
-        }
-    } else {
-        counter = s->delta;
-    }
-    return counter;
-}
-
-void ptimer_set_count(ptimer_state *s, uint64_t count)
-{
-    s->delta = count;
-    if (s->enabled) {
-        s->next_event = qemu_get_clock_ns(vm_clock);
-        ptimer_reload(s);
-    }
-}
-
-void ptimer_run(ptimer_state *s, int oneshot)
-{
-    if (s->enabled) {
-        return;
-    }
-    if (s->period == 0) {
-        fprintf(stderr, "Timer with period zero, disabling\n");
-        return;
-    }
-    s->enabled = oneshot ? 2 : 1;
-    s->next_event = qemu_get_clock_ns(vm_clock);
-    ptimer_reload(s);
-}
-
-/* Pause a timer.  Note that this may cause it to "lose" time, even if it
-   is immediately restarted.  */
-void ptimer_stop(ptimer_state *s)
-{
-    if (!s->enabled)
-        return;
-
-    s->delta = ptimer_get_count(s);
-    qemu_del_timer(s->timer);
-    s->enabled = 0;
-}
-
-/* Set counter increment interval in nanoseconds.  */
-void ptimer_set_period(ptimer_state *s, int64_t period)
-{
-    s->period = period;
-    s->period_frac = 0;
-    if (s->enabled) {
-        s->next_event = qemu_get_clock_ns(vm_clock);
-        ptimer_reload(s);
-    }
-}
-
-/* Set counter frequency in Hz.  */
-void ptimer_set_freq(ptimer_state *s, uint32_t freq)
-{
-    s->period = 1000000000ll / freq;
-    s->period_frac = (1000000000ll << 32) / freq;
-    if (s->enabled) {
-        s->next_event = qemu_get_clock_ns(vm_clock);
-        ptimer_reload(s);
-    }
-}
-
-/* Set the initial countdown value.  If reload is nonzero then also set
-   count = limit.  */
-void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload)
-{
-    /*
-     * Artificially limit timeout rate to something
-     * achievable under QEMU.  Otherwise, QEMU spends all
-     * its time generating timer interrupts, and there
-     * is no forward progress.
-     * About ten microseconds is the fastest that really works
-     * on the current generation of host machines.
-     */
-
-    if (limit * s->period < 10000 && s->period) {
-        limit = 10000 / s->period;
-    }
-
-    s->limit = limit;
-    if (reload)
-        s->delta = limit;
-    if (s->enabled && reload) {
-        s->next_event = qemu_get_clock_ns(vm_clock);
-        ptimer_reload(s);
-    }
-}
-
-const VMStateDescription vmstate_ptimer = {
-    .name = "ptimer",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT8(enabled, ptimer_state),
-        VMSTATE_UINT64(limit, ptimer_state),
-        VMSTATE_UINT64(delta, ptimer_state),
-        VMSTATE_UINT32(period_frac, ptimer_state),
-        VMSTATE_INT64(period, ptimer_state),
-        VMSTATE_INT64(last_event, ptimer_state),
-        VMSTATE_INT64(next_event, ptimer_state),
-        VMSTATE_TIMER(timer, ptimer_state),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-ptimer_state *ptimer_init(QEMUBH *bh)
-{
-    ptimer_state *s;
-
-    s = (ptimer_state *)g_malloc0(sizeof(ptimer_state));
-    s->bh = bh;
-    s->timer = qemu_new_timer_ns(vm_clock, ptimer_tick, s);
-    return s;
-}
diff --git a/hw/puv3_dma.c b/hw/puv3_dma.c
deleted file mode 100644 (file)
index 32844b5..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * DMA device simulation in PKUnity SoC
- *
- * Copyright (C) 2010-2012 Guan Xuetao
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation, or any later version.
- * See the COPYING file in the top-level directory.
- */
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-
-#undef DEBUG_PUV3
-#include "hw/unicore32/puv3.h"
-
-#define PUV3_DMA_CH_NR          (6)
-#define PUV3_DMA_CH_MASK        (0xff)
-#define PUV3_DMA_CH(offset)     ((offset) >> 8)
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    uint32_t reg_CFG[PUV3_DMA_CH_NR];
-} PUV3DMAState;
-
-static uint64_t puv3_dma_read(void *opaque, hwaddr offset,
-        unsigned size)
-{
-    PUV3DMAState *s = opaque;
-    uint32_t ret = 0;
-
-    assert(PUV3_DMA_CH(offset) < PUV3_DMA_CH_NR);
-
-    switch (offset & PUV3_DMA_CH_MASK) {
-    case 0x10:
-        ret = s->reg_CFG[PUV3_DMA_CH(offset)];
-        break;
-    default:
-        DPRINTF("Bad offset 0x%x\n", offset);
-    }
-    DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
-
-    return ret;
-}
-
-static void puv3_dma_write(void *opaque, hwaddr offset,
-        uint64_t value, unsigned size)
-{
-    PUV3DMAState *s = opaque;
-
-    assert(PUV3_DMA_CH(offset) < PUV3_DMA_CH_NR);
-
-    switch (offset & PUV3_DMA_CH_MASK) {
-    case 0x10:
-        s->reg_CFG[PUV3_DMA_CH(offset)] = value;
-        break;
-    default:
-        DPRINTF("Bad offset 0x%x\n", offset);
-    }
-    DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
-}
-
-static const MemoryRegionOps puv3_dma_ops = {
-    .read = puv3_dma_read,
-    .write = puv3_dma_write,
-    .impl = {
-        .min_access_size = 4,
-        .max_access_size = 4,
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int puv3_dma_init(SysBusDevice *dev)
-{
-    PUV3DMAState *s = FROM_SYSBUS(PUV3DMAState, dev);
-    int i;
-
-    for (i = 0; i < PUV3_DMA_CH_NR; i++) {
-        s->reg_CFG[i] = 0x0;
-    }
-
-    memory_region_init_io(&s->iomem, &puv3_dma_ops, s, "puv3_dma",
-            PUV3_REGS_OFFSET);
-    sysbus_init_mmio(dev, &s->iomem);
-
-    return 0;
-}
-
-static void puv3_dma_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sdc->init = puv3_dma_init;
-}
-
-static const TypeInfo puv3_dma_info = {
-    .name = "puv3_dma",
-    .parent = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(PUV3DMAState),
-    .class_init = puv3_dma_class_init,
-};
-
-static void puv3_dma_register_type(void)
-{
-    type_register_static(&puv3_dma_info);
-}
-
-type_init(puv3_dma_register_type)
diff --git a/hw/puv3_gpio.c b/hw/puv3_gpio.c
deleted file mode 100644 (file)
index 5bab97e..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * GPIO device simulation in PKUnity SoC
- *
- * Copyright (C) 2010-2012 Guan Xuetao
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation, or any later version.
- * See the COPYING file in the top-level directory.
- */
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-
-#undef DEBUG_PUV3
-#include "hw/unicore32/puv3.h"
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    qemu_irq irq[9];
-
-    uint32_t reg_GPLR;
-    uint32_t reg_GPDR;
-    uint32_t reg_GPIR;
-} PUV3GPIOState;
-
-static uint64_t puv3_gpio_read(void *opaque, hwaddr offset,
-        unsigned size)
-{
-    PUV3GPIOState *s = opaque;
-    uint32_t ret = 0;
-
-    switch (offset) {
-    case 0x00:
-        ret = s->reg_GPLR;
-        break;
-    case 0x04:
-        ret = s->reg_GPDR;
-        break;
-    case 0x20:
-        ret = s->reg_GPIR;
-        break;
-    default:
-        DPRINTF("Bad offset 0x%x\n", offset);
-    }
-    DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
-
-    return ret;
-}
-
-static void puv3_gpio_write(void *opaque, hwaddr offset,
-        uint64_t value, unsigned size)
-{
-    PUV3GPIOState *s = opaque;
-
-    DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
-    switch (offset) {
-    case 0x04:
-        s->reg_GPDR = value;
-        break;
-    case 0x08:
-        if (s->reg_GPDR & value) {
-            s->reg_GPLR |= value;
-        } else {
-            DPRINTF("Write gpio input port error!");
-        }
-        break;
-    case 0x0c:
-        if (s->reg_GPDR & value) {
-            s->reg_GPLR &= ~value;
-        } else {
-            DPRINTF("Write gpio input port error!");
-        }
-        break;
-    case 0x10: /* GRER */
-    case 0x14: /* GFER */
-    case 0x18: /* GEDR */
-        break;
-    case 0x20: /* GPIR */
-        s->reg_GPIR = value;
-        break;
-    default:
-        DPRINTF("Bad offset 0x%x\n", offset);
-    }
-}
-
-static const MemoryRegionOps puv3_gpio_ops = {
-    .read = puv3_gpio_read,
-    .write = puv3_gpio_write,
-    .impl = {
-        .min_access_size = 4,
-        .max_access_size = 4,
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int puv3_gpio_init(SysBusDevice *dev)
-{
-    PUV3GPIOState *s = FROM_SYSBUS(PUV3GPIOState, dev);
-
-    s->reg_GPLR = 0;
-    s->reg_GPDR = 0;
-
-    /* FIXME: these irqs not handled yet */
-    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW0]);
-    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW1]);
-    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW2]);
-    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW3]);
-    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW4]);
-    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW5]);
-    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW6]);
-    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW7]);
-    sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOHIGH]);
-
-    memory_region_init_io(&s->iomem, &puv3_gpio_ops, s, "puv3_gpio",
-            PUV3_REGS_OFFSET);
-    sysbus_init_mmio(dev, &s->iomem);
-
-    return 0;
-}
-
-static void puv3_gpio_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sdc->init = puv3_gpio_init;
-}
-
-static const TypeInfo puv3_gpio_info = {
-    .name = "puv3_gpio",
-    .parent = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(PUV3GPIOState),
-    .class_init = puv3_gpio_class_init,
-};
-
-static void puv3_gpio_register_type(void)
-{
-    type_register_static(&puv3_gpio_info);
-}
-
-type_init(puv3_gpio_register_type)
diff --git a/hw/puv3_intc.c b/hw/puv3_intc.c
deleted file mode 100644 (file)
index 0cd5e9e..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * INTC device simulation in PKUnity SoC
- *
- * Copyright (C) 2010-2012 Guan Xuetao
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation, or any later version.
- * See the COPYING file in the top-level directory.
- */
-#include "hw/sysbus.h"
-
-#undef DEBUG_PUV3
-#include "hw/unicore32/puv3.h"
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    qemu_irq parent_irq;
-
-    uint32_t reg_ICMR;
-    uint32_t reg_ICPR;
-} PUV3INTCState;
-
-/* Update interrupt status after enabled or pending bits have been changed.  */
-static void puv3_intc_update(PUV3INTCState *s)
-{
-    if (s->reg_ICMR & s->reg_ICPR) {
-        qemu_irq_raise(s->parent_irq);
-    } else {
-        qemu_irq_lower(s->parent_irq);
-    }
-}
-
-/* Process a change in an external INTC input. */
-static void puv3_intc_handler(void *opaque, int irq, int level)
-{
-    PUV3INTCState *s = opaque;
-
-    DPRINTF("irq 0x%x, level 0x%x\n", irq, level);
-    if (level) {
-        s->reg_ICPR |= (1 << irq);
-    } else {
-        s->reg_ICPR &= ~(1 << irq);
-    }
-    puv3_intc_update(s);
-}
-
-static uint64_t puv3_intc_read(void *opaque, hwaddr offset,
-        unsigned size)
-{
-    PUV3INTCState *s = opaque;
-    uint32_t ret = 0;
-
-    switch (offset) {
-    case 0x04: /* INTC_ICMR */
-        ret = s->reg_ICMR;
-        break;
-    case 0x0c: /* INTC_ICIP */
-        ret = s->reg_ICPR; /* the same value with ICPR */
-        break;
-    default:
-        DPRINTF("Bad offset %x\n", (int)offset);
-    }
-    DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
-    return ret;
-}
-
-static void puv3_intc_write(void *opaque, hwaddr offset,
-        uint64_t value, unsigned size)
-{
-    PUV3INTCState *s = opaque;
-
-    DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
-    switch (offset) {
-    case 0x00: /* INTC_ICLR */
-    case 0x14: /* INTC_ICCR */
-        break;
-    case 0x04: /* INTC_ICMR */
-        s->reg_ICMR = value;
-        break;
-    default:
-        DPRINTF("Bad offset 0x%x\n", (int)offset);
-        return;
-    }
-    puv3_intc_update(s);
-}
-
-static const MemoryRegionOps puv3_intc_ops = {
-    .read = puv3_intc_read,
-    .write = puv3_intc_write,
-    .impl = {
-        .min_access_size = 4,
-        .max_access_size = 4,
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int puv3_intc_init(SysBusDevice *dev)
-{
-    PUV3INTCState *s = FROM_SYSBUS(PUV3INTCState, dev);
-
-    qdev_init_gpio_in(&s->busdev.qdev, puv3_intc_handler, PUV3_IRQS_NR);
-    sysbus_init_irq(&s->busdev, &s->parent_irq);
-
-    s->reg_ICMR = 0;
-    s->reg_ICPR = 0;
-
-    memory_region_init_io(&s->iomem, &puv3_intc_ops, s, "puv3_intc",
-            PUV3_REGS_OFFSET);
-    sysbus_init_mmio(dev, &s->iomem);
-
-    return 0;
-}
-
-static void puv3_intc_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sdc->init = puv3_intc_init;
-}
-
-static const TypeInfo puv3_intc_info = {
-    .name = "puv3_intc",
-    .parent = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(PUV3INTCState),
-    .class_init = puv3_intc_class_init,
-};
-
-static void puv3_intc_register_type(void)
-{
-    type_register_static(&puv3_intc_info);
-}
-
-type_init(puv3_intc_register_type)
diff --git a/hw/puv3_ost.c b/hw/puv3_ost.c
deleted file mode 100644 (file)
index 0c3d827..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * OSTimer device simulation in PKUnity SoC
- *
- * Copyright (C) 2010-2012 Guan Xuetao
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation, or any later version.
- * See the COPYING file in the top-level directory.
- */
-#include "hw/sysbus.h"
-#include "hw/ptimer.h"
-
-#undef DEBUG_PUV3
-#include "hw/unicore32/puv3.h"
-
-/* puv3 ostimer implementation. */
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    QEMUBH *bh;
-    qemu_irq irq;
-    ptimer_state *ptimer;
-
-    uint32_t reg_OSMR0;
-    uint32_t reg_OSCR;
-    uint32_t reg_OSSR;
-    uint32_t reg_OIER;
-} PUV3OSTState;
-
-static uint64_t puv3_ost_read(void *opaque, hwaddr offset,
-        unsigned size)
-{
-    PUV3OSTState *s = opaque;
-    uint32_t ret = 0;
-
-    switch (offset) {
-    case 0x10: /* Counter Register */
-        ret = s->reg_OSMR0 - (uint32_t)ptimer_get_count(s->ptimer);
-        break;
-    case 0x14: /* Status Register */
-        ret = s->reg_OSSR;
-        break;
-    case 0x1c: /* Interrupt Enable Register */
-        ret = s->reg_OIER;
-        break;
-    default:
-        DPRINTF("Bad offset %x\n", (int)offset);
-    }
-    DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
-    return ret;
-}
-
-static void puv3_ost_write(void *opaque, hwaddr offset,
-        uint64_t value, unsigned size)
-{
-    PUV3OSTState *s = opaque;
-
-    DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
-    switch (offset) {
-    case 0x00: /* Match Register 0 */
-        s->reg_OSMR0 = value;
-        if (s->reg_OSMR0 > s->reg_OSCR) {
-            ptimer_set_count(s->ptimer, s->reg_OSMR0 - s->reg_OSCR);
-        } else {
-            ptimer_set_count(s->ptimer, s->reg_OSMR0 +
-                    (0xffffffff - s->reg_OSCR));
-        }
-        ptimer_run(s->ptimer, 2);
-        break;
-    case 0x14: /* Status Register */
-        assert(value == 0);
-        if (s->reg_OSSR) {
-            s->reg_OSSR = value;
-            qemu_irq_lower(s->irq);
-        }
-        break;
-    case 0x1c: /* Interrupt Enable Register */
-        s->reg_OIER = value;
-        break;
-    default:
-        DPRINTF("Bad offset %x\n", (int)offset);
-    }
-}
-
-static const MemoryRegionOps puv3_ost_ops = {
-    .read = puv3_ost_read,
-    .write = puv3_ost_write,
-    .impl = {
-        .min_access_size = 4,
-        .max_access_size = 4,
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void puv3_ost_tick(void *opaque)
-{
-    PUV3OSTState *s = opaque;
-
-    DPRINTF("ost hit when ptimer counter from 0x%x to 0x%x!\n",
-            s->reg_OSCR, s->reg_OSMR0);
-
-    s->reg_OSCR = s->reg_OSMR0;
-    if (s->reg_OIER) {
-        s->reg_OSSR = 1;
-        qemu_irq_raise(s->irq);
-    }
-}
-
-static int puv3_ost_init(SysBusDevice *dev)
-{
-    PUV3OSTState *s = FROM_SYSBUS(PUV3OSTState, dev);
-
-    s->reg_OIER = 0;
-    s->reg_OSSR = 0;
-    s->reg_OSMR0 = 0;
-    s->reg_OSCR = 0;
-
-    sysbus_init_irq(dev, &s->irq);
-
-    s->bh = qemu_bh_new(puv3_ost_tick, s);
-    s->ptimer = ptimer_init(s->bh);
-    ptimer_set_freq(s->ptimer, 50 * 1000 * 1000);
-
-    memory_region_init_io(&s->iomem, &puv3_ost_ops, s, "puv3_ost",
-            PUV3_REGS_OFFSET);
-    sysbus_init_mmio(dev, &s->iomem);
-
-    return 0;
-}
-
-static void puv3_ost_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sdc->init = puv3_ost_init;
-}
-
-static const TypeInfo puv3_ost_info = {
-    .name = "puv3_ost",
-    .parent = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(PUV3OSTState),
-    .class_init = puv3_ost_class_init,
-};
-
-static void puv3_ost_register_type(void)
-{
-    type_register_static(&puv3_ost_info);
-}
-
-type_init(puv3_ost_register_type)
diff --git a/hw/puv3_pm.c b/hw/puv3_pm.c
deleted file mode 100644 (file)
index 0aacdc2..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Power Management device simulation in PKUnity SoC
- *
- * Copyright (C) 2010-2012 Guan Xuetao
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation, or any later version.
- * See the COPYING file in the top-level directory.
- */
-#include "hw/hw.h"
-#include "hw/sysbus.h"
-
-#undef DEBUG_PUV3
-#include "hw/unicore32/puv3.h"
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-
-    uint32_t reg_PMCR;
-    uint32_t reg_PCGR;
-    uint32_t reg_PLL_SYS_CFG;
-    uint32_t reg_PLL_DDR_CFG;
-    uint32_t reg_PLL_VGA_CFG;
-    uint32_t reg_DIVCFG;
-} PUV3PMState;
-
-static uint64_t puv3_pm_read(void *opaque, hwaddr offset,
-        unsigned size)
-{
-    PUV3PMState *s = opaque;
-    uint32_t ret = 0;
-
-    switch (offset) {
-    case 0x14:
-        ret = s->reg_PCGR;
-        break;
-    case 0x18:
-        ret = s->reg_PLL_SYS_CFG;
-        break;
-    case 0x1c:
-        ret = s->reg_PLL_DDR_CFG;
-        break;
-    case 0x20:
-        ret = s->reg_PLL_VGA_CFG;
-        break;
-    case 0x24:
-        ret = s->reg_DIVCFG;
-        break;
-    case 0x28: /* PLL SYS STATUS */
-        ret = 0x00002401;
-        break;
-    case 0x2c: /* PLL DDR STATUS */
-        ret = 0x00100c00;
-        break;
-    case 0x30: /* PLL VGA STATUS */
-        ret = 0x00003801;
-        break;
-    case 0x34: /* DIV STATUS */
-        ret = 0x22f52015;
-        break;
-    case 0x38: /* SW RESET */
-        ret = 0x0;
-        break;
-    case 0x44: /* PLL DFC DONE */
-        ret = 0x7;
-        break;
-    default:
-        DPRINTF("Bad offset 0x%x\n", offset);
-    }
-    DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
-
-    return ret;
-}
-
-static void puv3_pm_write(void *opaque, hwaddr offset,
-        uint64_t value, unsigned size)
-{
-    PUV3PMState *s = opaque;
-
-    switch (offset) {
-    case 0x0:
-        s->reg_PMCR = value;
-        break;
-    case 0x14:
-        s->reg_PCGR = value;
-        break;
-    case 0x18:
-        s->reg_PLL_SYS_CFG = value;
-        break;
-    case 0x1c:
-        s->reg_PLL_DDR_CFG = value;
-        break;
-    case 0x20:
-        s->reg_PLL_VGA_CFG = value;
-        break;
-    case 0x24:
-    case 0x38:
-        break;
-    default:
-        DPRINTF("Bad offset 0x%x\n", offset);
-    }
-    DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
-}
-
-static const MemoryRegionOps puv3_pm_ops = {
-    .read = puv3_pm_read,
-    .write = puv3_pm_write,
-    .impl = {
-        .min_access_size = 4,
-        .max_access_size = 4,
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int puv3_pm_init(SysBusDevice *dev)
-{
-    PUV3PMState *s = FROM_SYSBUS(PUV3PMState, dev);
-
-    s->reg_PCGR = 0x0;
-
-    memory_region_init_io(&s->iomem, &puv3_pm_ops, s, "puv3_pm",
-            PUV3_REGS_OFFSET);
-    sysbus_init_mmio(dev, &s->iomem);
-
-    return 0;
-}
-
-static void puv3_pm_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sdc->init = puv3_pm_init;
-}
-
-static const TypeInfo puv3_pm_info = {
-    .name = "puv3_pm",
-    .parent = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(PUV3PMState),
-    .class_init = puv3_pm_class_init,
-};
-
-static void puv3_pm_register_type(void)
-{
-    type_register_static(&puv3_pm_info);
-}
-
-type_init(puv3_pm_register_type)
diff --git a/hw/qdev-addr.c b/hw/qdev-addr.c
deleted file mode 100644 (file)
index 80a38bb..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#include "hw/qdev.h"
-#include "hw/qdev-addr.h"
-#include "exec/hwaddr.h"
-#include "qapi/qmp/qerror.h"
-#include "qapi/visitor.h"
-
-/* --- target physical address --- */
-
-static int parse_taddr(DeviceState *dev, Property *prop, const char *str)
-{
-    hwaddr *ptr = qdev_get_prop_ptr(dev, prop);
-
-    *ptr = strtoull(str, NULL, 16);
-    return 0;
-}
-
-static int print_taddr(DeviceState *dev, Property *prop, char *dest, size_t len)
-{
-    hwaddr *ptr = qdev_get_prop_ptr(dev, prop);
-    return snprintf(dest, len, "0x" TARGET_FMT_plx, *ptr);
-}
-
-static void get_taddr(Object *obj, Visitor *v, void *opaque,
-                      const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    hwaddr *ptr = qdev_get_prop_ptr(dev, prop);
-    int64_t value;
-
-    value = *ptr;
-    visit_type_int64(v, &value, name, errp);
-}
-
-static void set_taddr(Object *obj, Visitor *v, void *opaque,
-                      const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    hwaddr *ptr = qdev_get_prop_ptr(dev, prop);
-    Error *local_err = NULL;
-    int64_t value;
-
-    if (dev->realized) {
-        qdev_prop_set_after_realize(dev, name, errp);
-        return;
-    }
-
-    visit_type_int64(v, &value, name, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
-    }
-    if ((uint64_t)value <= (uint64_t) ~(hwaddr)0) {
-        *ptr = value;
-    } else {
-        error_set(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE,
-                  dev->id?:"", name, value, (uint64_t) 0,
-                  (uint64_t) ~(hwaddr)0);
-    }
-}
-
-
-PropertyInfo qdev_prop_taddr = {
-    .name  = "taddr",
-    .parse = parse_taddr,
-    .print = print_taddr,
-    .get   = get_taddr,
-    .set   = set_taddr,
-};
-
-void qdev_prop_set_taddr(DeviceState *dev, const char *name, hwaddr value)
-{
-    Error *errp = NULL;
-    object_property_set_int(OBJECT(dev), value, name, &errp);
-    assert(!errp);
-
-}
diff --git a/hw/qdev-properties-system.c b/hw/qdev-properties-system.c
deleted file mode 100644 (file)
index 8c2e152..0000000
+++ /dev/null
@@ -1,391 +0,0 @@
-/*
- * qdev property parsing and global properties
- * (parts specific for qemu-system-*)
- *
- * This file is based on code from hw/qdev-properties.c from
- * commit 074a86fccd185616469dfcdc0e157f438aebba18,
- * Copyright (c) Gerd Hoffmann <kraxel@redhat.com> and other contributors.
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#include "net/net.h"
-#include "hw/qdev.h"
-#include "qapi/qmp/qerror.h"
-#include "sysemu/blockdev.h"
-#include "hw/block/block.h"
-#include "net/hub.h"
-#include "qapi/visitor.h"
-#include "char/char.h"
-
-static void get_pointer(Object *obj, Visitor *v, Property *prop,
-                        const char *(*print)(void *ptr),
-                        const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    void **ptr = qdev_get_prop_ptr(dev, prop);
-    char *p;
-
-    p = (char *) (*ptr ? print(*ptr) : "");
-    visit_type_str(v, &p, name, errp);
-}
-
-static void set_pointer(Object *obj, Visitor *v, Property *prop,
-                        int (*parse)(DeviceState *dev, const char *str,
-                                     void **ptr),
-                        const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Error *local_err = NULL;
-    void **ptr = qdev_get_prop_ptr(dev, prop);
-    char *str;
-    int ret;
-
-    if (dev->realized) {
-        qdev_prop_set_after_realize(dev, name, errp);
-        return;
-    }
-
-    visit_type_str(v, &str, name, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
-    }
-    if (!*str) {
-        g_free(str);
-        *ptr = NULL;
-        return;
-    }
-    ret = parse(dev, str, ptr);
-    error_set_from_qdev_prop_error(errp, ret, dev, prop, str);
-    g_free(str);
-}
-
-/* --- drive --- */
-
-static int parse_drive(DeviceState *dev, const char *str, void **ptr)
-{
-    BlockDriverState *bs;
-
-    bs = bdrv_find(str);
-    if (bs == NULL) {
-        return -ENOENT;
-    }
-    if (bdrv_attach_dev(bs, dev) < 0) {
-        return -EEXIST;
-    }
-    *ptr = bs;
-    return 0;
-}
-
-static void release_drive(Object *obj, const char *name, void *opaque)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    BlockDriverState **ptr = qdev_get_prop_ptr(dev, prop);
-
-    if (*ptr) {
-        bdrv_detach_dev(*ptr, dev);
-        blockdev_auto_del(*ptr);
-    }
-}
-
-static const char *print_drive(void *ptr)
-{
-    return bdrv_get_device_name(ptr);
-}
-
-static void get_drive(Object *obj, Visitor *v, void *opaque,
-                      const char *name, Error **errp)
-{
-    get_pointer(obj, v, opaque, print_drive, name, errp);
-}
-
-static void set_drive(Object *obj, Visitor *v, void *opaque,
-                      const char *name, Error **errp)
-{
-    set_pointer(obj, v, opaque, parse_drive, name, errp);
-}
-
-PropertyInfo qdev_prop_drive = {
-    .name  = "drive",
-    .get   = get_drive,
-    .set   = set_drive,
-    .release = release_drive,
-};
-
-/* --- character device --- */
-
-static int parse_chr(DeviceState *dev, const char *str, void **ptr)
-{
-    CharDriverState *chr = qemu_chr_find(str);
-    if (chr == NULL) {
-        return -ENOENT;
-    }
-    if (qemu_chr_fe_claim(chr) != 0) {
-        return -EEXIST;
-    }
-    *ptr = chr;
-    return 0;
-}
-
-static void release_chr(Object *obj, const char *name, void *opaque)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    CharDriverState **ptr = qdev_get_prop_ptr(dev, prop);
-    CharDriverState *chr = *ptr;
-
-    if (chr) {
-        qemu_chr_add_handlers(chr, NULL, NULL, NULL, NULL);
-        qemu_chr_fe_release(chr);
-    }
-}
-
-
-static const char *print_chr(void *ptr)
-{
-    CharDriverState *chr = ptr;
-
-    return chr->label ? chr->label : "";
-}
-
-static void get_chr(Object *obj, Visitor *v, void *opaque,
-                    const char *name, Error **errp)
-{
-    get_pointer(obj, v, opaque, print_chr, name, errp);
-}
-
-static void set_chr(Object *obj, Visitor *v, void *opaque,
-                    const char *name, Error **errp)
-{
-    set_pointer(obj, v, opaque, parse_chr, name, errp);
-}
-
-PropertyInfo qdev_prop_chr = {
-    .name  = "chr",
-    .get   = get_chr,
-    .set   = set_chr,
-    .release = release_chr,
-};
-
-/* --- netdev device --- */
-
-static int parse_netdev(DeviceState *dev, const char *str, void **ptr)
-{
-    NICPeers *peers_ptr = (NICPeers *)ptr;
-    NICConf *conf = container_of(peers_ptr, NICConf, peers);
-    NetClientState **ncs = peers_ptr->ncs;
-    NetClientState *peers[MAX_QUEUE_NUM];
-    int queues, i = 0;
-    int ret;
-
-    queues = qemu_find_net_clients_except(str, peers,
-                                          NET_CLIENT_OPTIONS_KIND_NIC,
-                                          MAX_QUEUE_NUM);
-    if (queues == 0) {
-        ret = -ENOENT;
-        goto err;
-    }
-
-    if (queues > MAX_QUEUE_NUM) {
-        ret = -E2BIG;
-        goto err;
-    }
-
-    for (i = 0; i < queues; i++) {
-        if (peers[i] == NULL) {
-            ret = -ENOENT;
-            goto err;
-        }
-
-        if (peers[i]->peer) {
-            ret = -EEXIST;
-            goto err;
-        }
-
-        ncs[i] = peers[i];
-        ncs[i]->queue_index = i;
-    }
-
-    conf->queues = queues;
-
-    return 0;
-
-err:
-    return ret;
-}
-
-static const char *print_netdev(void *ptr)
-{
-    NetClientState *netdev = ptr;
-
-    return netdev->name ? netdev->name : "";
-}
-
-static void get_netdev(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
-{
-    get_pointer(obj, v, opaque, print_netdev, name, errp);
-}
-
-static void set_netdev(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
-{
-    set_pointer(obj, v, opaque, parse_netdev, name, errp);
-}
-
-PropertyInfo qdev_prop_netdev = {
-    .name  = "netdev",
-    .get   = get_netdev,
-    .set   = set_netdev,
-};
-
-/* --- vlan --- */
-
-static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len)
-{
-    NetClientState **ptr = qdev_get_prop_ptr(dev, prop);
-
-    if (*ptr) {
-        int id;
-        if (!net_hub_id_for_client(*ptr, &id)) {
-            return snprintf(dest, len, "%d", id);
-        }
-    }
-
-    return snprintf(dest, len, "<null>");
-}
-
-static void get_vlan(Object *obj, Visitor *v, void *opaque,
-                     const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    NetClientState **ptr = qdev_get_prop_ptr(dev, prop);
-    int32_t id = -1;
-
-    if (*ptr) {
-        int hub_id;
-        if (!net_hub_id_for_client(*ptr, &hub_id)) {
-            id = hub_id;
-        }
-    }
-
-    visit_type_int32(v, &id, name, errp);
-}
-
-static void set_vlan(Object *obj, Visitor *v, void *opaque,
-                     const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop);
-    NetClientState **ptr = &peers_ptr->ncs[0];
-    Error *local_err = NULL;
-    int32_t id;
-    NetClientState *hubport;
-
-    if (dev->realized) {
-        qdev_prop_set_after_realize(dev, name, errp);
-        return;
-    }
-
-    visit_type_int32(v, &id, name, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
-    }
-    if (id == -1) {
-        *ptr = NULL;
-        return;
-    }
-
-    hubport = net_hub_port_find(id);
-    if (!hubport) {
-        error_set(errp, QERR_INVALID_PARAMETER_VALUE,
-                  name, prop->info->name);
-        return;
-    }
-    *ptr = hubport;
-}
-
-PropertyInfo qdev_prop_vlan = {
-    .name  = "vlan",
-    .print = print_vlan,
-    .get   = get_vlan,
-    .set   = set_vlan,
-};
-
-int qdev_prop_set_drive(DeviceState *dev, const char *name,
-                        BlockDriverState *value)
-{
-    Error *errp = NULL;
-    const char *bdrv_name = value ? bdrv_get_device_name(value) : "";
-    object_property_set_str(OBJECT(dev), bdrv_name,
-                            name, &errp);
-    if (errp) {
-        qerror_report_err(errp);
-        error_free(errp);
-        return -1;
-    }
-    return 0;
-}
-
-void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name,
-                                BlockDriverState *value)
-{
-    if (qdev_prop_set_drive(dev, name, value) < 0) {
-        exit(1);
-    }
-}
-void qdev_prop_set_chr(DeviceState *dev, const char *name,
-                       CharDriverState *value)
-{
-    Error *errp = NULL;
-    assert(!value || value->label);
-    object_property_set_str(OBJECT(dev),
-                            value ? value->label : "", name, &errp);
-    assert_no_error(errp);
-}
-
-void qdev_prop_set_netdev(DeviceState *dev, const char *name,
-                          NetClientState *value)
-{
-    Error *errp = NULL;
-    assert(!value || value->name);
-    object_property_set_str(OBJECT(dev),
-                            value ? value->name : "", name, &errp);
-    assert_no_error(errp);
-}
-
-void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
-{
-    qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a);
-    if (nd->netdev) {
-        qdev_prop_set_netdev(dev, "netdev", nd->netdev);
-    }
-    if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED &&
-        object_property_find(OBJECT(dev), "vectors", NULL)) {
-        qdev_prop_set_uint32(dev, "vectors", nd->nvectors);
-    }
-    nd->instantiated = 1;
-}
-
-static int qdev_add_one_global(QemuOpts *opts, void *opaque)
-{
-    GlobalProperty *g;
-
-    g = g_malloc0(sizeof(*g));
-    g->driver   = qemu_opt_get(opts, "driver");
-    g->property = qemu_opt_get(opts, "property");
-    g->value    = qemu_opt_get(opts, "value");
-    qdev_prop_register_global(g);
-    return 0;
-}
-
-void qemu_add_globals(void)
-{
-    qemu_opts_foreach(qemu_find_opts("global"), qdev_add_one_global, NULL, 0);
-}
diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c
deleted file mode 100644 (file)
index 9a0872d..0000000
+++ /dev/null
@@ -1,1092 +0,0 @@
-#include "net/net.h"
-#include "hw/qdev.h"
-#include "qapi/qmp/qerror.h"
-#include "sysemu/blockdev.h"
-#include "hw/block/block.h"
-#include "net/hub.h"
-#include "qapi/visitor.h"
-#include "char/char.h"
-
-void qdev_prop_set_after_realize(DeviceState *dev, const char *name,
-                                  Error **errp)
-{
-    if (dev->id) {
-        error_setg(errp, "Attempt to set property '%s' on device '%s' "
-                   "(type '%s') after it was realized", name, dev->id,
-                   object_get_typename(OBJECT(dev)));
-    } else {
-        error_setg(errp, "Attempt to set property '%s' on anonymous device "
-                   "(type '%s') after it was realized", name,
-                   object_get_typename(OBJECT(dev)));
-    }
-}
-
-void *qdev_get_prop_ptr(DeviceState *dev, Property *prop)
-{
-    void *ptr = dev;
-    ptr += prop->offset;
-    return ptr;
-}
-
-static void get_enum(Object *obj, Visitor *v, void *opaque,
-                     const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    int *ptr = qdev_get_prop_ptr(dev, prop);
-
-    visit_type_enum(v, ptr, prop->info->enum_table,
-                    prop->info->name, prop->name, errp);
-}
-
-static void set_enum(Object *obj, Visitor *v, void *opaque,
-                     const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    int *ptr = qdev_get_prop_ptr(dev, prop);
-
-    if (dev->realized) {
-        qdev_prop_set_after_realize(dev, name, errp);
-        return;
-    }
-
-    visit_type_enum(v, ptr, prop->info->enum_table,
-                    prop->info->name, prop->name, errp);
-}
-
-/* Bit */
-
-static uint32_t qdev_get_prop_mask(Property *prop)
-{
-    assert(prop->info == &qdev_prop_bit);
-    return 0x1 << prop->bitnr;
-}
-
-static void bit_prop_set(DeviceState *dev, Property *props, bool val)
-{
-    uint32_t *p = qdev_get_prop_ptr(dev, props);
-    uint32_t mask = qdev_get_prop_mask(props);
-    if (val) {
-        *p |= mask;
-    } else {
-        *p &= ~mask;
-    }
-}
-
-static int print_bit(DeviceState *dev, Property *prop, char *dest, size_t len)
-{
-    uint32_t *p = qdev_get_prop_ptr(dev, prop);
-    return snprintf(dest, len, (*p & qdev_get_prop_mask(prop)) ? "on" : "off");
-}
-
-static void get_bit(Object *obj, Visitor *v, void *opaque,
-                    const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    uint32_t *p = qdev_get_prop_ptr(dev, prop);
-    bool value = (*p & qdev_get_prop_mask(prop)) != 0;
-
-    visit_type_bool(v, &value, name, errp);
-}
-
-static void set_bit(Object *obj, Visitor *v, void *opaque,
-                    const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    Error *local_err = NULL;
-    bool value;
-
-    if (dev->realized) {
-        qdev_prop_set_after_realize(dev, name, errp);
-        return;
-    }
-
-    visit_type_bool(v, &value, name, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
-    }
-    bit_prop_set(dev, prop, value);
-}
-
-PropertyInfo qdev_prop_bit = {
-    .name  = "boolean",
-    .legacy_name  = "on/off",
-    .print = print_bit,
-    .get   = get_bit,
-    .set   = set_bit,
-};
-
-/* --- 8bit integer --- */
-
-static void get_uint8(Object *obj, Visitor *v, void *opaque,
-                      const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
-
-    visit_type_uint8(v, ptr, name, errp);
-}
-
-static void set_uint8(Object *obj, Visitor *v, void *opaque,
-                      const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
-
-    if (dev->realized) {
-        qdev_prop_set_after_realize(dev, name, errp);
-        return;
-    }
-
-    visit_type_uint8(v, ptr, name, errp);
-}
-
-PropertyInfo qdev_prop_uint8 = {
-    .name  = "uint8",
-    .get   = get_uint8,
-    .set   = set_uint8,
-};
-
-/* --- 8bit hex value --- */
-
-static int parse_hex8(DeviceState *dev, Property *prop, const char *str)
-{
-    uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
-    char *end;
-
-    if (str[0] != '0' || str[1] != 'x') {
-        return -EINVAL;
-    }
-
-    *ptr = strtoul(str, &end, 16);
-    if ((*end != '\0') || (end == str)) {
-        return -EINVAL;
-    }
-
-    return 0;
-}
-
-static int print_hex8(DeviceState *dev, Property *prop, char *dest, size_t len)
-{
-    uint8_t *ptr = qdev_get_prop_ptr(dev, prop);
-    return snprintf(dest, len, "0x%" PRIx8, *ptr);
-}
-
-PropertyInfo qdev_prop_hex8 = {
-    .name  = "uint8",
-    .legacy_name  = "hex8",
-    .parse = parse_hex8,
-    .print = print_hex8,
-    .get   = get_uint8,
-    .set   = set_uint8,
-};
-
-/* --- 16bit integer --- */
-
-static void get_uint16(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    uint16_t *ptr = qdev_get_prop_ptr(dev, prop);
-
-    visit_type_uint16(v, ptr, name, errp);
-}
-
-static void set_uint16(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    uint16_t *ptr = qdev_get_prop_ptr(dev, prop);
-
-    if (dev->realized) {
-        qdev_prop_set_after_realize(dev, name, errp);
-        return;
-    }
-
-    visit_type_uint16(v, ptr, name, errp);
-}
-
-PropertyInfo qdev_prop_uint16 = {
-    .name  = "uint16",
-    .get   = get_uint16,
-    .set   = set_uint16,
-};
-
-/* --- 32bit integer --- */
-
-static void get_uint32(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
-
-    visit_type_uint32(v, ptr, name, errp);
-}
-
-static void set_uint32(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
-
-    if (dev->realized) {
-        qdev_prop_set_after_realize(dev, name, errp);
-        return;
-    }
-
-    visit_type_uint32(v, ptr, name, errp);
-}
-
-static void get_int32(Object *obj, Visitor *v, void *opaque,
-                      const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    int32_t *ptr = qdev_get_prop_ptr(dev, prop);
-
-    visit_type_int32(v, ptr, name, errp);
-}
-
-static void set_int32(Object *obj, Visitor *v, void *opaque,
-                      const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    int32_t *ptr = qdev_get_prop_ptr(dev, prop);
-
-    if (dev->realized) {
-        qdev_prop_set_after_realize(dev, name, errp);
-        return;
-    }
-
-    visit_type_int32(v, ptr, name, errp);
-}
-
-PropertyInfo qdev_prop_uint32 = {
-    .name  = "uint32",
-    .get   = get_uint32,
-    .set   = set_uint32,
-};
-
-PropertyInfo qdev_prop_int32 = {
-    .name  = "int32",
-    .get   = get_int32,
-    .set   = set_int32,
-};
-
-/* --- 32bit hex value --- */
-
-static int parse_hex32(DeviceState *dev, Property *prop, const char *str)
-{
-    uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
-    char *end;
-
-    if (str[0] != '0' || str[1] != 'x') {
-        return -EINVAL;
-    }
-
-    *ptr = strtoul(str, &end, 16);
-    if ((*end != '\0') || (end == str)) {
-        return -EINVAL;
-    }
-
-    return 0;
-}
-
-static int print_hex32(DeviceState *dev, Property *prop, char *dest, size_t len)
-{
-    uint32_t *ptr = qdev_get_prop_ptr(dev, prop);
-    return snprintf(dest, len, "0x%" PRIx32, *ptr);
-}
-
-PropertyInfo qdev_prop_hex32 = {
-    .name  = "uint32",
-    .legacy_name  = "hex32",
-    .parse = parse_hex32,
-    .print = print_hex32,
-    .get   = get_uint32,
-    .set   = set_uint32,
-};
-
-/* --- 64bit integer --- */
-
-static void get_uint64(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
-
-    visit_type_uint64(v, ptr, name, errp);
-}
-
-static void set_uint64(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
-
-    if (dev->realized) {
-        qdev_prop_set_after_realize(dev, name, errp);
-        return;
-    }
-
-    visit_type_uint64(v, ptr, name, errp);
-}
-
-PropertyInfo qdev_prop_uint64 = {
-    .name  = "uint64",
-    .get   = get_uint64,
-    .set   = set_uint64,
-};
-
-/* --- 64bit hex value --- */
-
-static int parse_hex64(DeviceState *dev, Property *prop, const char *str)
-{
-    uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
-    char *end;
-
-    if (str[0] != '0' || str[1] != 'x') {
-        return -EINVAL;
-    }
-
-    *ptr = strtoull(str, &end, 16);
-    if ((*end != '\0') || (end == str)) {
-        return -EINVAL;
-    }
-
-    return 0;
-}
-
-static int print_hex64(DeviceState *dev, Property *prop, char *dest, size_t len)
-{
-    uint64_t *ptr = qdev_get_prop_ptr(dev, prop);
-    return snprintf(dest, len, "0x%" PRIx64, *ptr);
-}
-
-PropertyInfo qdev_prop_hex64 = {
-    .name  = "uint64",
-    .legacy_name  = "hex64",
-    .parse = parse_hex64,
-    .print = print_hex64,
-    .get   = get_uint64,
-    .set   = set_uint64,
-};
-
-/* --- string --- */
-
-static void release_string(Object *obj, const char *name, void *opaque)
-{
-    Property *prop = opaque;
-    g_free(*(char **)qdev_get_prop_ptr(DEVICE(obj), prop));
-}
-
-static int print_string(DeviceState *dev, Property *prop, char *dest,
-                        size_t len)
-{
-    char **ptr = qdev_get_prop_ptr(dev, prop);
-    if (!*ptr) {
-        return snprintf(dest, len, "<null>");
-    }
-    return snprintf(dest, len, "\"%s\"", *ptr);
-}
-
-static void get_string(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    char **ptr = qdev_get_prop_ptr(dev, prop);
-
-    if (!*ptr) {
-        char *str = (char *)"";
-        visit_type_str(v, &str, name, errp);
-    } else {
-        visit_type_str(v, ptr, name, errp);
-    }
-}
-
-static void set_string(Object *obj, Visitor *v, void *opaque,
-                       const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    char **ptr = qdev_get_prop_ptr(dev, prop);
-    Error *local_err = NULL;
-    char *str;
-
-    if (dev->realized) {
-        qdev_prop_set_after_realize(dev, name, errp);
-        return;
-    }
-
-    visit_type_str(v, &str, name, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
-    }
-    if (*ptr) {
-        g_free(*ptr);
-    }
-    *ptr = str;
-}
-
-PropertyInfo qdev_prop_string = {
-    .name  = "string",
-    .print = print_string,
-    .release = release_string,
-    .get   = get_string,
-    .set   = set_string,
-};
-
-/* --- pointer --- */
-
-/* Not a proper property, just for dirty hacks.  TODO Remove it!  */
-PropertyInfo qdev_prop_ptr = {
-    .name  = "ptr",
-};
-
-/* --- mac address --- */
-
-/*
- * accepted syntax versions:
- *   01:02:03:04:05:06
- *   01-02-03-04-05-06
- */
-static void get_mac(Object *obj, Visitor *v, void *opaque,
-                    const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    MACAddr *mac = qdev_get_prop_ptr(dev, prop);
-    char buffer[2 * 6 + 5 + 1];
-    char *p = buffer;
-
-    snprintf(buffer, sizeof(buffer), "%02x:%02x:%02x:%02x:%02x:%02x",
-             mac->a[0], mac->a[1], mac->a[2],
-             mac->a[3], mac->a[4], mac->a[5]);
-
-    visit_type_str(v, &p, name, errp);
-}
-
-static void set_mac(Object *obj, Visitor *v, void *opaque,
-                    const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    MACAddr *mac = qdev_get_prop_ptr(dev, prop);
-    Error *local_err = NULL;
-    int i, pos;
-    char *str, *p;
-
-    if (dev->realized) {
-        qdev_prop_set_after_realize(dev, name, errp);
-        return;
-    }
-
-    visit_type_str(v, &str, name, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
-    }
-
-    for (i = 0, pos = 0; i < 6; i++, pos += 3) {
-        if (!qemu_isxdigit(str[pos])) {
-            goto inval;
-        }
-        if (!qemu_isxdigit(str[pos+1])) {
-            goto inval;
-        }
-        if (i == 5) {
-            if (str[pos+2] != '\0') {
-                goto inval;
-            }
-        } else {
-            if (str[pos+2] != ':' && str[pos+2] != '-') {
-                goto inval;
-            }
-        }
-        mac->a[i] = strtol(str+pos, &p, 16);
-    }
-    g_free(str);
-    return;
-
-inval:
-    error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str);
-    g_free(str);
-}
-
-PropertyInfo qdev_prop_macaddr = {
-    .name  = "macaddr",
-    .get   = get_mac,
-    .set   = set_mac,
-};
-
-/* --- lost tick policy --- */
-
-static const char *lost_tick_policy_table[LOST_TICK_MAX+1] = {
-    [LOST_TICK_DISCARD] = "discard",
-    [LOST_TICK_DELAY] = "delay",
-    [LOST_TICK_MERGE] = "merge",
-    [LOST_TICK_SLEW] = "slew",
-    [LOST_TICK_MAX] = NULL,
-};
-
-QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int));
-
-PropertyInfo qdev_prop_losttickpolicy = {
-    .name  = "LostTickPolicy",
-    .enum_table  = lost_tick_policy_table,
-    .get   = get_enum,
-    .set   = set_enum,
-};
-
-/* --- BIOS CHS translation */
-
-static const char *bios_chs_trans_table[] = {
-    [BIOS_ATA_TRANSLATION_AUTO] = "auto",
-    [BIOS_ATA_TRANSLATION_NONE] = "none",
-    [BIOS_ATA_TRANSLATION_LBA]  = "lba",
-};
-
-PropertyInfo qdev_prop_bios_chs_trans = {
-    .name = "bios-chs-trans",
-    .enum_table = bios_chs_trans_table,
-    .get = get_enum,
-    .set = set_enum,
-};
-
-/* --- pci address --- */
-
-/*
- * bus-local address, i.e. "$slot" or "$slot.$fn"
- */
-static void set_pci_devfn(Object *obj, Visitor *v, void *opaque,
-                          const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    int32_t value, *ptr = qdev_get_prop_ptr(dev, prop);
-    unsigned int slot, fn, n;
-    Error *local_err = NULL;
-    char *str;
-
-    if (dev->realized) {
-        qdev_prop_set_after_realize(dev, name, errp);
-        return;
-    }
-
-    visit_type_str(v, &str, name, &local_err);
-    if (local_err) {
-        error_free(local_err);
-        local_err = NULL;
-        visit_type_int32(v, &value, name, &local_err);
-        if (local_err) {
-            error_propagate(errp, local_err);
-        } else if (value < -1 || value > 255) {
-            error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
-                      "pci_devfn");
-        } else {
-            *ptr = value;
-        }
-        return;
-    }
-
-    if (sscanf(str, "%x.%x%n", &slot, &fn, &n) != 2) {
-        fn = 0;
-        if (sscanf(str, "%x%n", &slot, &n) != 1) {
-            goto invalid;
-        }
-    }
-    if (str[n] != '\0' || fn > 7 || slot > 31) {
-        goto invalid;
-    }
-    *ptr = slot << 3 | fn;
-    g_free(str);
-    return;
-
-invalid:
-    error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str);
-    g_free(str);
-}
-
-static int print_pci_devfn(DeviceState *dev, Property *prop, char *dest,
-                           size_t len)
-{
-    int32_t *ptr = qdev_get_prop_ptr(dev, prop);
-
-    if (*ptr == -1) {
-        return snprintf(dest, len, "<unset>");
-    } else {
-        return snprintf(dest, len, "%02x.%x", *ptr >> 3, *ptr & 7);
-    }
-}
-
-PropertyInfo qdev_prop_pci_devfn = {
-    .name  = "int32",
-    .legacy_name  = "pci-devfn",
-    .print = print_pci_devfn,
-    .get   = get_int32,
-    .set   = set_pci_devfn,
-};
-
-/* --- blocksize --- */
-
-static void set_blocksize(Object *obj, Visitor *v, void *opaque,
-                          const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    uint16_t value, *ptr = qdev_get_prop_ptr(dev, prop);
-    Error *local_err = NULL;
-    const int64_t min = 512;
-    const int64_t max = 32768;
-
-    if (dev->realized) {
-        qdev_prop_set_after_realize(dev, name, errp);
-        return;
-    }
-
-    visit_type_uint16(v, &value, name, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
-    }
-    if (value < min || value > max) {
-        error_set(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE,
-                  dev->id?:"", name, (int64_t)value, min, max);
-        return;
-    }
-
-    /* We rely on power-of-2 blocksizes for bitmasks */
-    if ((value & (value - 1)) != 0) {
-        error_set(errp, QERR_PROPERTY_VALUE_NOT_POWER_OF_2,
-                  dev->id?:"", name, (int64_t)value);
-        return;
-    }
-
-    *ptr = value;
-}
-
-PropertyInfo qdev_prop_blocksize = {
-    .name  = "blocksize",
-    .get   = get_uint16,
-    .set   = set_blocksize,
-};
-
-/* --- pci host address --- */
-
-static void get_pci_host_devaddr(Object *obj, Visitor *v, void *opaque,
-                                 const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    PCIHostDeviceAddress *addr = qdev_get_prop_ptr(dev, prop);
-    char buffer[] = "xxxx:xx:xx.x";
-    char *p = buffer;
-    int rc = 0;
-
-    rc = snprintf(buffer, sizeof(buffer), "%04x:%02x:%02x.%d",
-                  addr->domain, addr->bus, addr->slot, addr->function);
-    assert(rc == sizeof(buffer) - 1);
-
-    visit_type_str(v, &p, name, errp);
-}
-
-/*
- * Parse [<domain>:]<bus>:<slot>.<func>
- *   if <domain> is not supplied, it's assumed to be 0.
- */
-static void set_pci_host_devaddr(Object *obj, Visitor *v, void *opaque,
-                                 const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    PCIHostDeviceAddress *addr = qdev_get_prop_ptr(dev, prop);
-    Error *local_err = NULL;
-    char *str, *p;
-    char *e;
-    unsigned long val;
-    unsigned long dom = 0, bus = 0;
-    unsigned int slot = 0, func = 0;
-
-    if (dev->realized) {
-        qdev_prop_set_after_realize(dev, name, errp);
-        return;
-    }
-
-    visit_type_str(v, &str, name, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
-    }
-
-    p = str;
-    val = strtoul(p, &e, 16);
-    if (e == p || *e != ':') {
-        goto inval;
-    }
-    bus = val;
-
-    p = e + 1;
-    val = strtoul(p, &e, 16);
-    if (e == p) {
-        goto inval;
-    }
-    if (*e == ':') {
-        dom = bus;
-        bus = val;
-        p = e + 1;
-        val = strtoul(p, &e, 16);
-        if (e == p) {
-            goto inval;
-        }
-    }
-    slot = val;
-
-    if (*e != '.') {
-        goto inval;
-    }
-    p = e + 1;
-    val = strtoul(p, &e, 10);
-    if (e == p) {
-        goto inval;
-    }
-    func = val;
-
-    if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7) {
-        goto inval;
-    }
-
-    if (*e) {
-        goto inval;
-    }
-
-    addr->domain = dom;
-    addr->bus = bus;
-    addr->slot = slot;
-    addr->function = func;
-
-    g_free(str);
-    return;
-
-inval:
-    error_set_from_qdev_prop_error(errp, EINVAL, dev, prop, str);
-    g_free(str);
-}
-
-PropertyInfo qdev_prop_pci_host_devaddr = {
-    .name = "pci-host-devaddr",
-    .get = get_pci_host_devaddr,
-    .set = set_pci_host_devaddr,
-};
-
-/* --- support for array properties --- */
-
-/* Used as an opaque for the object properties we add for each
- * array element. Note that the struct Property must be first
- * in the struct so that a pointer to this works as the opaque
- * for the underlying element's property hooks as well as for
- * our own release callback.
- */
-typedef struct {
-    struct Property prop;
-    char *propname;
-    ObjectPropertyRelease *release;
-} ArrayElementProperty;
-
-/* object property release callback for array element properties:
- * we call the underlying element's property release hook, and
- * then free the memory we allocated when we added the property.
- */
-static void array_element_release(Object *obj, const char *name, void *opaque)
-{
-    ArrayElementProperty *p = opaque;
-    if (p->release) {
-        p->release(obj, name, opaque);
-    }
-    g_free(p->propname);
-    g_free(p);
-}
-
-static void set_prop_arraylen(Object *obj, Visitor *v, void *opaque,
-                              const char *name, Error **errp)
-{
-    /* Setter for the property which defines the length of a
-     * variable-sized property array. As well as actually setting the
-     * array-length field in the device struct, we have to create the
-     * array itself and dynamically add the corresponding properties.
-     */
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    uint32_t *alenptr = qdev_get_prop_ptr(dev, prop);
-    void **arrayptr = (void *)dev + prop->arrayoffset;
-    void *eltptr;
-    const char *arrayname;
-    int i;
-
-    if (dev->realized) {
-        qdev_prop_set_after_realize(dev, name, errp);
-        return;
-    }
-    if (*alenptr) {
-        error_setg(errp, "array size property %s may not be set more than once",
-                   name);
-        return;
-    }
-    visit_type_uint32(v, alenptr, name, errp);
-    if (error_is_set(errp)) {
-        return;
-    }
-    if (!*alenptr) {
-        return;
-    }
-
-    /* DEFINE_PROP_ARRAY guarantees that name should start with this prefix;
-     * strip it off so we can get the name of the array itself.
-     */
-    assert(strncmp(name, PROP_ARRAY_LEN_PREFIX,
-                   strlen(PROP_ARRAY_LEN_PREFIX)) == 0);
-    arrayname = name + strlen(PROP_ARRAY_LEN_PREFIX);
-
-    /* Note that it is the responsibility of the individual device's deinit
-     * to free the array proper.
-     */
-    *arrayptr = eltptr = g_malloc0(*alenptr * prop->arrayfieldsize);
-    for (i = 0; i < *alenptr; i++, eltptr += prop->arrayfieldsize) {
-        char *propname = g_strdup_printf("%s[%d]", arrayname, i);
-        ArrayElementProperty *arrayprop = g_new0(ArrayElementProperty, 1);
-        arrayprop->release = prop->arrayinfo->release;
-        arrayprop->propname = propname;
-        arrayprop->prop.info = prop->arrayinfo;
-        arrayprop->prop.name = propname;
-        /* This ugly piece of pointer arithmetic sets up the offset so
-         * that when the underlying get/set hooks call qdev_get_prop_ptr
-         * they get the right answer despite the array element not actually
-         * being inside the device struct.
-         */
-        arrayprop->prop.offset = eltptr - (void *)dev;
-        assert(qdev_get_prop_ptr(dev, &arrayprop->prop) == eltptr);
-        object_property_add(obj, propname,
-                            arrayprop->prop.info->name,
-                            arrayprop->prop.info->get,
-                            arrayprop->prop.info->set,
-                            array_element_release,
-                            arrayprop, errp);
-        if (error_is_set(errp)) {
-            return;
-        }
-    }
-}
-
-PropertyInfo qdev_prop_arraylen = {
-    .name = "uint32",
-    .get = get_uint32,
-    .set = set_prop_arraylen,
-};
-
-/* --- public helpers --- */
-
-static Property *qdev_prop_walk(Property *props, const char *name)
-{
-    if (!props) {
-        return NULL;
-    }
-    while (props->name) {
-        if (strcmp(props->name, name) == 0) {
-            return props;
-        }
-        props++;
-    }
-    return NULL;
-}
-
-static Property *qdev_prop_find(DeviceState *dev, const char *name)
-{
-    ObjectClass *class;
-    Property *prop;
-
-    /* device properties */
-    class = object_get_class(OBJECT(dev));
-    do {
-        prop = qdev_prop_walk(DEVICE_CLASS(class)->props, name);
-        if (prop) {
-            return prop;
-        }
-        class = object_class_get_parent(class);
-    } while (class != object_class_by_name(TYPE_DEVICE));
-
-    return NULL;
-}
-
-void error_set_from_qdev_prop_error(Error **errp, int ret, DeviceState *dev,
-                                    Property *prop, const char *value)
-{
-    switch (ret) {
-    case -EEXIST:
-        error_set(errp, QERR_PROPERTY_VALUE_IN_USE,
-                  object_get_typename(OBJECT(dev)), prop->name, value);
-        break;
-    default:
-    case -EINVAL:
-        error_set(errp, QERR_PROPERTY_VALUE_BAD,
-                  object_get_typename(OBJECT(dev)), prop->name, value);
-        break;
-    case -ENOENT:
-        error_set(errp, QERR_PROPERTY_VALUE_NOT_FOUND,
-                  object_get_typename(OBJECT(dev)), prop->name, value);
-        break;
-    case 0:
-        break;
-    }
-}
-
-int qdev_prop_parse(DeviceState *dev, const char *name, const char *value)
-{
-    char *legacy_name;
-    Error *err = NULL;
-
-    legacy_name = g_strdup_printf("legacy-%s", name);
-    if (object_property_get_type(OBJECT(dev), legacy_name, NULL)) {
-        object_property_parse(OBJECT(dev), value, legacy_name, &err);
-    } else {
-        object_property_parse(OBJECT(dev), value, name, &err);
-    }
-    g_free(legacy_name);
-
-    if (err) {
-        qerror_report_err(err);
-        error_free(err);
-        return -1;
-    }
-    return 0;
-}
-
-void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value)
-{
-    Error *errp = NULL;
-    object_property_set_bool(OBJECT(dev), value, name, &errp);
-    assert_no_error(errp);
-}
-
-void qdev_prop_set_uint8(DeviceState *dev, const char *name, uint8_t value)
-{
-    Error *errp = NULL;
-    object_property_set_int(OBJECT(dev), value, name, &errp);
-    assert_no_error(errp);
-}
-
-void qdev_prop_set_uint16(DeviceState *dev, const char *name, uint16_t value)
-{
-    Error *errp = NULL;
-    object_property_set_int(OBJECT(dev), value, name, &errp);
-    assert_no_error(errp);
-}
-
-void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value)
-{
-    Error *errp = NULL;
-    object_property_set_int(OBJECT(dev), value, name, &errp);
-    assert_no_error(errp);
-}
-
-void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value)
-{
-    Error *errp = NULL;
-    object_property_set_int(OBJECT(dev), value, name, &errp);
-    assert_no_error(errp);
-}
-
-void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value)
-{
-    Error *errp = NULL;
-    object_property_set_int(OBJECT(dev), value, name, &errp);
-    assert_no_error(errp);
-}
-
-void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value)
-{
-    Error *errp = NULL;
-    object_property_set_str(OBJECT(dev), value, name, &errp);
-    assert_no_error(errp);
-}
-
-void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value)
-{
-    Error *errp = NULL;
-    char str[2 * 6 + 5 + 1];
-    snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x",
-             value[0], value[1], value[2], value[3], value[4], value[5]);
-
-    object_property_set_str(OBJECT(dev), str, name, &errp);
-    assert_no_error(errp);
-}
-
-void qdev_prop_set_enum(DeviceState *dev, const char *name, int value)
-{
-    Property *prop;
-    Error *errp = NULL;
-
-    prop = qdev_prop_find(dev, name);
-    object_property_set_str(OBJECT(dev), prop->info->enum_table[value],
-                            name, &errp);
-    assert_no_error(errp);
-}
-
-void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value)
-{
-    Property *prop;
-    void **ptr;
-
-    prop = qdev_prop_find(dev, name);
-    assert(prop && prop->info == &qdev_prop_ptr);
-    ptr = qdev_get_prop_ptr(dev, prop);
-    *ptr = value;
-}
-
-static QTAILQ_HEAD(, GlobalProperty) global_props =
-        QTAILQ_HEAD_INITIALIZER(global_props);
-
-void qdev_prop_register_global(GlobalProperty *prop)
-{
-    QTAILQ_INSERT_TAIL(&global_props, prop, next);
-}
-
-void qdev_prop_register_global_list(GlobalProperty *props)
-{
-    int i;
-
-    for (i = 0; props[i].driver != NULL; i++) {
-        qdev_prop_register_global(props+i);
-    }
-}
-
-void qdev_prop_set_globals(DeviceState *dev)
-{
-    ObjectClass *class = object_get_class(OBJECT(dev));
-
-    do {
-        GlobalProperty *prop;
-        QTAILQ_FOREACH(prop, &global_props, next) {
-            if (strcmp(object_class_get_name(class), prop->driver) != 0) {
-                continue;
-            }
-            if (qdev_prop_parse(dev, prop->property, prop->value) != 0) {
-                exit(1);
-            }
-        }
-        class = object_class_get_parent(class);
-    } while (class);
-}
diff --git a/hw/qdev.c b/hw/qdev.c
deleted file mode 100644 (file)
index e2bb37d..0000000
--- a/hw/qdev.c
+++ /dev/null
@@ -1,882 +0,0 @@
-/*
- *  Dynamic device configuration and creation.
- *
- *  Copyright (c) 2009 CodeSourcery
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-/* The theory here is that it should be possible to create a machine without
-   knowledge of specific devices.  Historically board init routines have
-   passed a bunch of arguments to each device, requiring the board know
-   exactly which device it is dealing with.  This file provides an abstract
-   API for device configuration and initialization.  Devices will generally
-   inherit from a particular bus (e.g. PCI or I2C) rather than
-   this API directly.  */
-
-#include "hw/qdev.h"
-#include "sysemu/sysemu.h"
-#include "qapi/error.h"
-#include "qapi/qmp/qerror.h"
-#include "qapi/visitor.h"
-#include "qapi/qmp/qjson.h"
-#include "monitor/monitor.h"
-
-int qdev_hotplug = 0;
-static bool qdev_hot_added = false;
-static bool qdev_hot_removed = false;
-
-const VMStateDescription *qdev_get_vmsd(DeviceState *dev)
-{
-    DeviceClass *dc = DEVICE_GET_CLASS(dev);
-    return dc->vmsd;
-}
-
-const char *qdev_fw_name(DeviceState *dev)
-{
-    DeviceClass *dc = DEVICE_GET_CLASS(dev);
-
-    if (dc->fw_name) {
-        return dc->fw_name;
-    }
-
-    return object_get_typename(OBJECT(dev));
-}
-
-static void qdev_property_add_legacy(DeviceState *dev, Property *prop,
-                                     Error **errp);
-
-static void bus_remove_child(BusState *bus, DeviceState *child)
-{
-    BusChild *kid;
-
-    QTAILQ_FOREACH(kid, &bus->children, sibling) {
-        if (kid->child == child) {
-            char name[32];
-
-            snprintf(name, sizeof(name), "child[%d]", kid->index);
-            QTAILQ_REMOVE(&bus->children, kid, sibling);
-
-            /* This gives back ownership of kid->child back to us.  */
-            object_property_del(OBJECT(bus), name, NULL);
-            object_unref(OBJECT(kid->child));
-            g_free(kid);
-            return;
-        }
-    }
-}
-
-static void bus_add_child(BusState *bus, DeviceState *child)
-{
-    char name[32];
-    BusChild *kid = g_malloc0(sizeof(*kid));
-
-    if (qdev_hotplug) {
-        assert(bus->allow_hotplug);
-    }
-
-    kid->index = bus->max_index++;
-    kid->child = child;
-    object_ref(OBJECT(kid->child));
-
-    QTAILQ_INSERT_HEAD(&bus->children, kid, sibling);
-
-    /* This transfers ownership of kid->child to the property.  */
-    snprintf(name, sizeof(name), "child[%d]", kid->index);
-    object_property_add_link(OBJECT(bus), name,
-                             object_get_typename(OBJECT(child)),
-                             (Object **)&kid->child,
-                             NULL);
-}
-
-void qdev_set_parent_bus(DeviceState *dev, BusState *bus)
-{
-    dev->parent_bus = bus;
-    object_ref(OBJECT(bus));
-    bus_add_child(bus, dev);
-}
-
-/* Create a new device.  This only initializes the device state structure
-   and allows properties to be set.  qdev_init should be called to
-   initialize the actual device emulation.  */
-DeviceState *qdev_create(BusState *bus, const char *name)
-{
-    DeviceState *dev;
-
-    dev = qdev_try_create(bus, name);
-    if (!dev) {
-        if (bus) {
-            error_report("Unknown device '%s' for bus '%s'", name,
-                         object_get_typename(OBJECT(bus)));
-        } else {
-            error_report("Unknown device '%s' for default sysbus", name);
-        }
-        abort();
-    }
-
-    return dev;
-}
-
-DeviceState *qdev_try_create(BusState *bus, const char *type)
-{
-    DeviceState *dev;
-
-    if (object_class_by_name(type) == NULL) {
-        return NULL;
-    }
-    dev = DEVICE(object_new(type));
-    if (!dev) {
-        return NULL;
-    }
-
-    if (!bus) {
-        bus = sysbus_get_default();
-    }
-
-    qdev_set_parent_bus(dev, bus);
-    object_unref(OBJECT(dev));
-    return dev;
-}
-
-/* Initialize a device.  Device properties should be set before calling
-   this function.  IRQs and MMIO regions should be connected/mapped after
-   calling this function.
-   On failure, destroy the device and return negative value.
-   Return 0 on success.  */
-int qdev_init(DeviceState *dev)
-{
-    Error *local_err = NULL;
-
-    assert(!dev->realized);
-
-    object_property_set_bool(OBJECT(dev), true, "realized", &local_err);
-    if (local_err != NULL) {
-        error_free(local_err);
-        qdev_free(dev);
-        return -1;
-    }
-    return 0;
-}
-
-static void device_realize(DeviceState *dev, Error **err)
-{
-    DeviceClass *dc = DEVICE_GET_CLASS(dev);
-
-    if (dc->init) {
-        int rc = dc->init(dev);
-        if (rc < 0) {
-            error_setg(err, "Device initialization failed.");
-            return;
-        }
-    }
-}
-
-void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,
-                                 int required_for_version)
-{
-    assert(!dev->realized);
-    dev->instance_id_alias = alias_id;
-    dev->alias_required_for_version = required_for_version;
-}
-
-void qdev_unplug(DeviceState *dev, Error **errp)
-{
-    DeviceClass *dc = DEVICE_GET_CLASS(dev);
-
-    if (!dev->parent_bus->allow_hotplug) {
-        error_set(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name);
-        return;
-    }
-    assert(dc->unplug != NULL);
-
-    qdev_hot_removed = true;
-
-    if (dc->unplug(dev) < 0) {
-        error_set(errp, QERR_UNDEFINED_ERROR);
-        return;
-    }
-}
-
-static int qdev_reset_one(DeviceState *dev, void *opaque)
-{
-    device_reset(dev);
-
-    return 0;
-}
-
-static int qbus_reset_one(BusState *bus, void *opaque)
-{
-    BusClass *bc = BUS_GET_CLASS(bus);
-    if (bc->reset) {
-        return bc->reset(bus);
-    }
-    return 0;
-}
-
-void qdev_reset_all(DeviceState *dev)
-{
-    qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL);
-}
-
-void qbus_reset_all(BusState *bus)
-{
-    qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL);
-}
-
-void qbus_reset_all_fn(void *opaque)
-{
-    BusState *bus = opaque;
-    qbus_reset_all(bus);
-}
-
-/* can be used as ->unplug() callback for the simple cases */
-int qdev_simple_unplug_cb(DeviceState *dev)
-{
-    /* just zap it */
-    qdev_free(dev);
-    return 0;
-}
-
-
-/* Like qdev_init(), but terminate program via error_report() instead of
-   returning an error value.  This is okay during machine creation.
-   Don't use for hotplug, because there callers need to recover from
-   failure.  Exception: if you know the device's init() callback can't
-   fail, then qdev_init_nofail() can't fail either, and is therefore
-   usable even then.  But relying on the device implementation that
-   way is somewhat unclean, and best avoided.  */
-void qdev_init_nofail(DeviceState *dev)
-{
-    const char *typename = object_get_typename(OBJECT(dev));
-
-    if (qdev_init(dev) < 0) {
-        error_report("Initialization of device %s failed", typename);
-        exit(1);
-    }
-}
-
-/* Unlink device from bus and free the structure.  */
-void qdev_free(DeviceState *dev)
-{
-    object_unparent(OBJECT(dev));
-}
-
-void qdev_machine_creation_done(void)
-{
-    /*
-     * ok, initial machine setup is done, starting from now we can
-     * only create hotpluggable devices
-     */
-    qdev_hotplug = 1;
-}
-
-bool qdev_machine_modified(void)
-{
-    return qdev_hot_added || qdev_hot_removed;
-}
-
-BusState *qdev_get_parent_bus(DeviceState *dev)
-{
-    return dev->parent_bus;
-}
-
-void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
-{
-    dev->gpio_in = qemu_extend_irqs(dev->gpio_in, dev->num_gpio_in, handler,
-                                        dev, n);
-    dev->num_gpio_in += n;
-}
-
-void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
-{
-    assert(dev->num_gpio_out == 0);
-    dev->num_gpio_out = n;
-    dev->gpio_out = pins;
-}
-
-qemu_irq qdev_get_gpio_in(DeviceState *dev, int n)
-{
-    assert(n >= 0 && n < dev->num_gpio_in);
-    return dev->gpio_in[n];
-}
-
-void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin)
-{
-    assert(n >= 0 && n < dev->num_gpio_out);
-    dev->gpio_out[n] = pin;
-}
-
-BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
-{
-    BusState *bus;
-
-    QLIST_FOREACH(bus, &dev->child_bus, sibling) {
-        if (strcmp(name, bus->name) == 0) {
-            return bus;
-        }
-    }
-    return NULL;
-}
-
-int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
-                       qbus_walkerfn *busfn, void *opaque)
-{
-    BusChild *kid;
-    int err;
-
-    if (busfn) {
-        err = busfn(bus, opaque);
-        if (err) {
-            return err;
-        }
-    }
-
-    QTAILQ_FOREACH(kid, &bus->children, sibling) {
-        err = qdev_walk_children(kid->child, devfn, busfn, opaque);
-        if (err < 0) {
-            return err;
-        }
-    }
-
-    return 0;
-}
-
-int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
-                       qbus_walkerfn *busfn, void *opaque)
-{
-    BusState *bus;
-    int err;
-
-    if (devfn) {
-        err = devfn(dev, opaque);
-        if (err) {
-            return err;
-        }
-    }
-
-    QLIST_FOREACH(bus, &dev->child_bus, sibling) {
-        err = qbus_walk_children(bus, devfn, busfn, opaque);
-        if (err < 0) {
-            return err;
-        }
-    }
-
-    return 0;
-}
-
-DeviceState *qdev_find_recursive(BusState *bus, const char *id)
-{
-    BusChild *kid;
-    DeviceState *ret;
-    BusState *child;
-
-    QTAILQ_FOREACH(kid, &bus->children, sibling) {
-        DeviceState *dev = kid->child;
-
-        if (dev->id && strcmp(dev->id, id) == 0) {
-            return dev;
-        }
-
-        QLIST_FOREACH(child, &dev->child_bus, sibling) {
-            ret = qdev_find_recursive(child, id);
-            if (ret) {
-                return ret;
-            }
-        }
-    }
-    return NULL;
-}
-
-static void qbus_realize(BusState *bus, DeviceState *parent, const char *name)
-{
-    const char *typename = object_get_typename(OBJECT(bus));
-    char *buf;
-    int i,len;
-
-    bus->parent = parent;
-
-    if (name) {
-        bus->name = g_strdup(name);
-    } else if (bus->parent && bus->parent->id) {
-        /* parent device has id -> use it for bus name */
-        len = strlen(bus->parent->id) + 16;
-        buf = g_malloc(len);
-        snprintf(buf, len, "%s.%d", bus->parent->id, bus->parent->num_child_bus);
-        bus->name = buf;
-    } else {
-        /* no id -> use lowercase bus type for bus name */
-        len = strlen(typename) + 16;
-        buf = g_malloc(len);
-        len = snprintf(buf, len, "%s.%d", typename,
-                       bus->parent ? bus->parent->num_child_bus : 0);
-        for (i = 0; i < len; i++)
-            buf[i] = qemu_tolower(buf[i]);
-        bus->name = buf;
-    }
-
-    if (bus->parent) {
-        QLIST_INSERT_HEAD(&bus->parent->child_bus, bus, sibling);
-        bus->parent->num_child_bus++;
-        object_property_add_child(OBJECT(bus->parent), bus->name, OBJECT(bus), NULL);
-        object_unref(OBJECT(bus));
-    } else if (bus != sysbus_get_default()) {
-        /* TODO: once all bus devices are qdevified,
-           only reset handler for main_system_bus should be registered here. */
-        qemu_register_reset(qbus_reset_all_fn, bus);
-    }
-}
-
-static void bus_unparent(Object *obj)
-{
-    BusState *bus = BUS(obj);
-    BusChild *kid;
-
-    while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) {
-        DeviceState *dev = kid->child;
-        qdev_free(dev);
-    }
-    if (bus->parent) {
-        QLIST_REMOVE(bus, sibling);
-        bus->parent->num_child_bus--;
-        bus->parent = NULL;
-    } else {
-        assert(bus != sysbus_get_default()); /* main_system_bus is never freed */
-        qemu_unregister_reset(qbus_reset_all_fn, bus);
-    }
-}
-
-void qbus_create_inplace(void *bus, const char *typename,
-                         DeviceState *parent, const char *name)
-{
-    object_initialize(bus, typename);
-    qbus_realize(bus, parent, name);
-}
-
-BusState *qbus_create(const char *typename, DeviceState *parent, const char *name)
-{
-    BusState *bus;
-
-    bus = BUS(object_new(typename));
-    qbus_realize(bus, parent, name);
-
-    return bus;
-}
-
-void qbus_free(BusState *bus)
-{
-    object_unparent(OBJECT(bus));
-}
-
-static char *bus_get_fw_dev_path(BusState *bus, DeviceState *dev)
-{
-    BusClass *bc = BUS_GET_CLASS(bus);
-
-    if (bc->get_fw_dev_path) {
-        return bc->get_fw_dev_path(dev);
-    }
-
-    return NULL;
-}
-
-static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size)
-{
-    int l = 0;
-
-    if (dev && dev->parent_bus) {
-        char *d;
-        l = qdev_get_fw_dev_path_helper(dev->parent_bus->parent, p, size);
-        d = bus_get_fw_dev_path(dev->parent_bus, dev);
-        if (d) {
-            l += snprintf(p + l, size - l, "%s", d);
-            g_free(d);
-        } else {
-            l += snprintf(p + l, size - l, "%s", object_get_typename(OBJECT(dev)));
-        }
-    }
-    l += snprintf(p + l , size - l, "/");
-
-    return l;
-}
-
-char* qdev_get_fw_dev_path(DeviceState *dev)
-{
-    char path[128];
-    int l;
-
-    l = qdev_get_fw_dev_path_helper(dev, path, 128);
-
-    path[l-1] = '\0';
-
-    return g_strdup(path);
-}
-
-char *qdev_get_dev_path(DeviceState *dev)
-{
-    BusClass *bc;
-
-    if (!dev || !dev->parent_bus) {
-        return NULL;
-    }
-
-    bc = BUS_GET_CLASS(dev->parent_bus);
-    if (bc->get_dev_path) {
-        return bc->get_dev_path(dev);
-    }
-
-    return NULL;
-}
-
-/**
- * Legacy property handling
- */
-
-static void qdev_get_legacy_property(Object *obj, Visitor *v, void *opaque,
-                                     const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-
-    char buffer[1024];
-    char *ptr = buffer;
-
-    prop->info->print(dev, prop, buffer, sizeof(buffer));
-    visit_type_str(v, &ptr, name, errp);
-}
-
-static void qdev_set_legacy_property(Object *obj, Visitor *v, void *opaque,
-                                     const char *name, Error **errp)
-{
-    DeviceState *dev = DEVICE(obj);
-    Property *prop = opaque;
-    Error *local_err = NULL;
-    char *ptr = NULL;
-    int ret;
-
-    if (dev->realized) {
-        qdev_prop_set_after_realize(dev, name, errp);
-        return;
-    }
-
-    visit_type_str(v, &ptr, name, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
-    }
-
-    ret = prop->info->parse(dev, prop, ptr);
-    error_set_from_qdev_prop_error(errp, ret, dev, prop, ptr);
-    g_free(ptr);
-}
-
-/**
- * @qdev_add_legacy_property - adds a legacy property
- *
- * Do not use this is new code!  Properties added through this interface will
- * be given names and types in the "legacy" namespace.
- *
- * Legacy properties are string versions of other OOM properties.  The format
- * of the string depends on the property type.
- */
-void qdev_property_add_legacy(DeviceState *dev, Property *prop,
-                              Error **errp)
-{
-    gchar *name, *type;
-
-    /* Register pointer properties as legacy properties */
-    if (!prop->info->print && !prop->info->parse &&
-        (prop->info->set || prop->info->get)) {
-        return;
-    }
-
-    name = g_strdup_printf("legacy-%s", prop->name);
-    type = g_strdup_printf("legacy<%s>",
-                           prop->info->legacy_name ?: prop->info->name);
-
-    object_property_add(OBJECT(dev), name, type,
-                        prop->info->print ? qdev_get_legacy_property : prop->info->get,
-                        prop->info->parse ? qdev_set_legacy_property : prop->info->set,
-                        NULL,
-                        prop, errp);
-
-    g_free(type);
-    g_free(name);
-}
-
-/**
- * @qdev_property_add_static - add a @Property to a device.
- *
- * Static properties access data in a struct.  The actual type of the
- * property and the field depends on the property type.
- */
-void qdev_property_add_static(DeviceState *dev, Property *prop,
-                              Error **errp)
-{
-    Error *local_err = NULL;
-    Object *obj = OBJECT(dev);
-
-    /*
-     * TODO qdev_prop_ptr does not have getters or setters.  It must
-     * go now that it can be replaced with links.  The test should be
-     * removed along with it: all static properties are read/write.
-     */
-    if (!prop->info->get && !prop->info->set) {
-        return;
-    }
-
-    object_property_add(obj, prop->name, prop->info->name,
-                        prop->info->get, prop->info->set,
-                        prop->info->release,
-                        prop, &local_err);
-
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
-    }
-    if (prop->qtype == QTYPE_NONE) {
-        return;
-    }
-
-    if (prop->qtype == QTYPE_QBOOL) {
-        object_property_set_bool(obj, prop->defval, prop->name, &local_err);
-    } else if (prop->info->enum_table) {
-        object_property_set_str(obj, prop->info->enum_table[prop->defval],
-                                prop->name, &local_err);
-    } else if (prop->qtype == QTYPE_QINT) {
-        object_property_set_int(obj, prop->defval, prop->name, &local_err);
-    }
-    assert_no_error(local_err);
-}
-
-static bool device_get_realized(Object *obj, Error **err)
-{
-    DeviceState *dev = DEVICE(obj);
-    return dev->realized;
-}
-
-static void device_set_realized(Object *obj, bool value, Error **err)
-{
-    DeviceState *dev = DEVICE(obj);
-    DeviceClass *dc = DEVICE_GET_CLASS(dev);
-    Error *local_err = NULL;
-
-    if (value && !dev->realized) {
-        if (dc->realize) {
-            dc->realize(dev, &local_err);
-        }
-
-        if (!obj->parent && local_err == NULL) {
-            static int unattached_count;
-            gchar *name = g_strdup_printf("device[%d]", unattached_count++);
-
-            object_property_add_child(container_get(qdev_get_machine(),
-                                                    "/unattached"),
-                                      name, obj, &local_err);
-            g_free(name);
-        }
-
-        if (qdev_get_vmsd(dev) && local_err == NULL) {
-            vmstate_register_with_alias_id(dev, -1, qdev_get_vmsd(dev), dev,
-                                           dev->instance_id_alias,
-                                           dev->alias_required_for_version);
-        }
-        if (dev->hotplugged && local_err == NULL) {
-            device_reset(dev);
-        }
-    } else if (!value && dev->realized) {
-        if (dc->unrealize) {
-            dc->unrealize(dev, &local_err);
-        }
-    }
-
-    if (local_err != NULL) {
-        error_propagate(err, local_err);
-        return;
-    }
-
-    dev->realized = value;
-}
-
-static void device_initfn(Object *obj)
-{
-    DeviceState *dev = DEVICE(obj);
-    ObjectClass *class;
-    Property *prop;
-    Error *err = NULL;
-
-    if (qdev_hotplug) {
-        dev->hotplugged = 1;
-        qdev_hot_added = true;
-    }
-
-    dev->instance_id_alias = -1;
-    dev->realized = false;
-
-    object_property_add_bool(obj, "realized",
-                             device_get_realized, device_set_realized, NULL);
-
-    class = object_get_class(OBJECT(dev));
-    do {
-        for (prop = DEVICE_CLASS(class)->props; prop && prop->name; prop++) {
-            qdev_property_add_legacy(dev, prop, &err);
-            assert_no_error(err);
-            qdev_property_add_static(dev, prop, &err);
-            assert_no_error(err);
-        }
-        class = object_class_get_parent(class);
-    } while (class != object_class_by_name(TYPE_DEVICE));
-    qdev_prop_set_globals(dev);
-
-    object_property_add_link(OBJECT(dev), "parent_bus", TYPE_BUS,
-                             (Object **)&dev->parent_bus, &err);
-    assert_no_error(err);
-}
-
-/* Unlink device from bus and free the structure.  */
-static void device_finalize(Object *obj)
-{
-    DeviceState *dev = DEVICE(obj);
-    if (dev->opts) {
-        qemu_opts_del(dev->opts);
-    }
-}
-
-static void device_class_base_init(ObjectClass *class, void *data)
-{
-    DeviceClass *klass = DEVICE_CLASS(class);
-
-    /* We explicitly look up properties in the superclasses,
-     * so do not propagate them to the subclasses.
-     */
-    klass->props = NULL;
-}
-
-static void device_unparent(Object *obj)
-{
-    DeviceState *dev = DEVICE(obj);
-    DeviceClass *dc = DEVICE_GET_CLASS(dev);
-    BusState *bus;
-    QObject *event_data;
-    bool have_realized = dev->realized;
-
-    while (dev->num_child_bus) {
-        bus = QLIST_FIRST(&dev->child_bus);
-        qbus_free(bus);
-    }
-    if (dev->realized) {
-        if (qdev_get_vmsd(dev)) {
-            vmstate_unregister(dev, qdev_get_vmsd(dev), dev);
-        }
-        if (dc->exit) {
-            dc->exit(dev);
-        }
-    }
-    if (dev->parent_bus) {
-        bus_remove_child(dev->parent_bus, dev);
-        object_unref(OBJECT(dev->parent_bus));
-        dev->parent_bus = NULL;
-    }
-
-    /* Only send event if the device had been completely realized */
-    if (have_realized) {
-        gchar *path = object_get_canonical_path(OBJECT(dev));
-
-        if (dev->id) {
-            event_data = qobject_from_jsonf("{ 'device': %s, 'path': %s }",
-                                            dev->id, path);
-        } else {
-            event_data = qobject_from_jsonf("{ 'path': %s }", path);
-        }
-        monitor_protocol_event(QEVENT_DEVICE_DELETED, event_data);
-        qobject_decref(event_data);
-        g_free(path);
-    }
-}
-
-static void device_class_init(ObjectClass *class, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(class);
-
-    class->unparent = device_unparent;
-    dc->realize = device_realize;
-}
-
-void device_reset(DeviceState *dev)
-{
-    DeviceClass *klass = DEVICE_GET_CLASS(dev);
-
-    if (klass->reset) {
-        klass->reset(dev);
-    }
-}
-
-Object *qdev_get_machine(void)
-{
-    static Object *dev;
-
-    if (dev == NULL) {
-        dev = container_get(object_get_root(), "/machine");
-    }
-
-    return dev;
-}
-
-static const TypeInfo device_type_info = {
-    .name = TYPE_DEVICE,
-    .parent = TYPE_OBJECT,
-    .instance_size = sizeof(DeviceState),
-    .instance_init = device_initfn,
-    .instance_finalize = device_finalize,
-    .class_base_init = device_class_base_init,
-    .class_init = device_class_init,
-    .abstract = true,
-    .class_size = sizeof(DeviceClass),
-};
-
-static void qbus_initfn(Object *obj)
-{
-    BusState *bus = BUS(obj);
-
-    QTAILQ_INIT(&bus->children);
-}
-
-static void bus_class_init(ObjectClass *class, void *data)
-{
-    class->unparent = bus_unparent;
-}
-
-static void qbus_finalize(Object *obj)
-{
-    BusState *bus = BUS(obj);
-
-    g_free((char *)bus->name);
-}
-
-static const TypeInfo bus_info = {
-    .name = TYPE_BUS,
-    .parent = TYPE_OBJECT,
-    .instance_size = sizeof(BusState),
-    .abstract = true,
-    .class_size = sizeof(BusClass),
-    .instance_init = qbus_initfn,
-    .instance_finalize = qbus_finalize,
-    .class_init = bus_class_init,
-};
-
-static void qdev_register_types(void)
-{
-    type_register_static(&bus_info);
-    type_register_static(&device_type_info);
-}
-
-type_init(qdev_register_types)
diff --git a/hw/rc4030.c b/hw/rc4030.c
deleted file mode 100644 (file)
index 03f92f1..0000000
+++ /dev/null
@@ -1,825 +0,0 @@
-/*
- * QEMU JAZZ RC4030 chipset
- *
- * Copyright (c) 2007-2009 Herve Poussineau
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/hw.h"
-#include "hw/mips/mips.h"
-#include "qemu/timer.h"
-
-/********************************************************/
-/* debug rc4030 */
-
-//#define DEBUG_RC4030
-//#define DEBUG_RC4030_DMA
-
-#ifdef DEBUG_RC4030
-#define DPRINTF(fmt, ...) \
-do { printf("rc4030: " fmt , ## __VA_ARGS__); } while (0)
-static const char* irq_names[] = { "parallel", "floppy", "sound", "video",
-            "network", "scsi", "keyboard", "mouse", "serial0", "serial1" };
-#else
-#define DPRINTF(fmt, ...)
-#endif
-
-#define RC4030_ERROR(fmt, ...) \
-do { fprintf(stderr, "rc4030 ERROR: %s: " fmt, __func__ , ## __VA_ARGS__); } while (0)
-
-/********************************************************/
-/* rc4030 emulation                                     */
-
-typedef struct dma_pagetable_entry {
-    int32_t frame;
-    int32_t owner;
-} QEMU_PACKED dma_pagetable_entry;
-
-#define DMA_PAGESIZE    4096
-#define DMA_REG_ENABLE  1
-#define DMA_REG_COUNT   2
-#define DMA_REG_ADDRESS 3
-
-#define DMA_FLAG_ENABLE     0x0001
-#define DMA_FLAG_MEM_TO_DEV 0x0002
-#define DMA_FLAG_TC_INTR    0x0100
-#define DMA_FLAG_MEM_INTR   0x0200
-#define DMA_FLAG_ADDR_INTR  0x0400
-
-typedef struct rc4030State
-{
-    uint32_t config; /* 0x0000: RC4030 config register */
-    uint32_t revision; /* 0x0008: RC4030 Revision register */
-    uint32_t invalid_address_register; /* 0x0010: Invalid Address register */
-
-    /* DMA */
-    uint32_t dma_regs[8][4];
-    uint32_t dma_tl_base; /* 0x0018: DMA transl. table base */
-    uint32_t dma_tl_limit; /* 0x0020: DMA transl. table limit */
-
-    /* cache */
-    uint32_t cache_maint; /* 0x0030: Cache Maintenance */
-    uint32_t remote_failed_address; /* 0x0038: Remote Failed Address */
-    uint32_t memory_failed_address; /* 0x0040: Memory Failed Address */
-    uint32_t cache_ptag; /* 0x0048: I/O Cache Physical Tag */
-    uint32_t cache_ltag; /* 0x0050: I/O Cache Logical Tag */
-    uint32_t cache_bmask; /* 0x0058: I/O Cache Byte Mask */
-
-    uint32_t nmi_interrupt; /* 0x0200: interrupt source */
-    uint32_t offset210;
-    uint32_t nvram_protect; /* 0x0220: NV ram protect register */
-    uint32_t rem_speed[16];
-    uint32_t imr_jazz; /* Local bus int enable mask */
-    uint32_t isr_jazz; /* Local bus int source */
-
-    /* timer */
-    QEMUTimer *periodic_timer;
-    uint32_t itr; /* Interval timer reload */
-
-    qemu_irq timer_irq;
-    qemu_irq jazz_bus_irq;
-
-    MemoryRegion iomem_chipset;
-    MemoryRegion iomem_jazzio;
-} rc4030State;
-
-static void set_next_tick(rc4030State *s)
-{
-    qemu_irq_lower(s->timer_irq);
-    uint32_t tm_hz;
-
-    tm_hz = 1000 / (s->itr + 1);
-
-    qemu_mod_timer(s->periodic_timer, qemu_get_clock_ns(vm_clock) +
-                   get_ticks_per_sec() / tm_hz);
-}
-
-/* called for accesses to rc4030 */
-static uint32_t rc4030_readl(void *opaque, hwaddr addr)
-{
-    rc4030State *s = opaque;
-    uint32_t val;
-
-    addr &= 0x3fff;
-    switch (addr & ~0x3) {
-    /* Global config register */
-    case 0x0000:
-        val = s->config;
-        break;
-    /* Revision register */
-    case 0x0008:
-        val = s->revision;
-        break;
-    /* Invalid Address register */
-    case 0x0010:
-        val = s->invalid_address_register;
-        break;
-    /* DMA transl. table base */
-    case 0x0018:
-        val = s->dma_tl_base;
-        break;
-    /* DMA transl. table limit */
-    case 0x0020:
-        val = s->dma_tl_limit;
-        break;
-    /* Remote Failed Address */
-    case 0x0038:
-        val = s->remote_failed_address;
-        break;
-    /* Memory Failed Address */
-    case 0x0040:
-        val = s->memory_failed_address;
-        break;
-    /* I/O Cache Byte Mask */
-    case 0x0058:
-        val = s->cache_bmask;
-        /* HACK */
-        if (s->cache_bmask == (uint32_t)-1)
-            s->cache_bmask = 0;
-        break;
-    /* Remote Speed Registers */
-    case 0x0070:
-    case 0x0078:
-    case 0x0080:
-    case 0x0088:
-    case 0x0090:
-    case 0x0098:
-    case 0x00a0:
-    case 0x00a8:
-    case 0x00b0:
-    case 0x00b8:
-    case 0x00c0:
-    case 0x00c8:
-    case 0x00d0:
-    case 0x00d8:
-    case 0x00e0:
-    case 0x00e8:
-        val = s->rem_speed[(addr - 0x0070) >> 3];
-        break;
-    /* DMA channel base address */
-    case 0x0100:
-    case 0x0108:
-    case 0x0110:
-    case 0x0118:
-    case 0x0120:
-    case 0x0128:
-    case 0x0130:
-    case 0x0138:
-    case 0x0140:
-    case 0x0148:
-    case 0x0150:
-    case 0x0158:
-    case 0x0160:
-    case 0x0168:
-    case 0x0170:
-    case 0x0178:
-    case 0x0180:
-    case 0x0188:
-    case 0x0190:
-    case 0x0198:
-    case 0x01a0:
-    case 0x01a8:
-    case 0x01b0:
-    case 0x01b8:
-    case 0x01c0:
-    case 0x01c8:
-    case 0x01d0:
-    case 0x01d8:
-    case 0x01e0:
-    case 0x01e8:
-    case 0x01f0:
-    case 0x01f8:
-        {
-            int entry = (addr - 0x0100) >> 5;
-            int idx = (addr & 0x1f) >> 3;
-            val = s->dma_regs[entry][idx];
-        }
-        break;
-    /* Interrupt source */
-    case 0x0200:
-        val = s->nmi_interrupt;
-        break;
-    /* Error type */
-    case 0x0208:
-        val = 0;
-        break;
-    /* Offset 0x0210 */
-    case 0x0210:
-        val = s->offset210;
-        break;
-    /* NV ram protect register */
-    case 0x0220:
-        val = s->nvram_protect;
-        break;
-    /* Interval timer count */
-    case 0x0230:
-        val = 0;
-        qemu_irq_lower(s->timer_irq);
-        break;
-    /* EISA interrupt */
-    case 0x0238:
-        val = 7; /* FIXME: should be read from EISA controller */
-        break;
-    default:
-        RC4030_ERROR("invalid read [" TARGET_FMT_plx "]\n", addr);
-        val = 0;
-        break;
-    }
-
-    if ((addr & ~3) != 0x230) {
-        DPRINTF("read 0x%02x at " TARGET_FMT_plx "\n", val, addr);
-    }
-
-    return val;
-}
-
-static uint32_t rc4030_readw(void *opaque, hwaddr addr)
-{
-    uint32_t v = rc4030_readl(opaque, addr & ~0x3);
-    if (addr & 0x2)
-        return v >> 16;
-    else
-        return v & 0xffff;
-}
-
-static uint32_t rc4030_readb(void *opaque, hwaddr addr)
-{
-    uint32_t v = rc4030_readl(opaque, addr & ~0x3);
-    return (v >> (8 * (addr & 0x3))) & 0xff;
-}
-
-static void rc4030_writel(void *opaque, hwaddr addr, uint32_t val)
-{
-    rc4030State *s = opaque;
-    addr &= 0x3fff;
-
-    DPRINTF("write 0x%02x at " TARGET_FMT_plx "\n", val, addr);
-
-    switch (addr & ~0x3) {
-    /* Global config register */
-    case 0x0000:
-        s->config = val;
-        break;
-    /* DMA transl. table base */
-    case 0x0018:
-        s->dma_tl_base = val;
-        break;
-    /* DMA transl. table limit */
-    case 0x0020:
-        s->dma_tl_limit = val;
-        break;
-    /* DMA transl. table invalidated */
-    case 0x0028:
-        break;
-    /* Cache Maintenance */
-    case 0x0030:
-        s->cache_maint = val;
-        break;
-    /* I/O Cache Physical Tag */
-    case 0x0048:
-        s->cache_ptag = val;
-        break;
-    /* I/O Cache Logical Tag */
-    case 0x0050:
-        s->cache_ltag = val;
-        break;
-    /* I/O Cache Byte Mask */
-    case 0x0058:
-        s->cache_bmask |= val; /* HACK */
-        break;
-    /* I/O Cache Buffer Window */
-    case 0x0060:
-        /* HACK */
-        if (s->cache_ltag == 0x80000001 && s->cache_bmask == 0xf0f0f0f) {
-            hwaddr dest = s->cache_ptag & ~0x1;
-            dest += (s->cache_maint & 0x3) << 3;
-            cpu_physical_memory_write(dest, &val, 4);
-        }
-        break;
-    /* Remote Speed Registers */
-    case 0x0070:
-    case 0x0078:
-    case 0x0080:
-    case 0x0088:
-    case 0x0090:
-    case 0x0098:
-    case 0x00a0:
-    case 0x00a8:
-    case 0x00b0:
-    case 0x00b8:
-    case 0x00c0:
-    case 0x00c8:
-    case 0x00d0:
-    case 0x00d8:
-    case 0x00e0:
-    case 0x00e8:
-        s->rem_speed[(addr - 0x0070) >> 3] = val;
-        break;
-    /* DMA channel base address */
-    case 0x0100:
-    case 0x0108:
-    case 0x0110:
-    case 0x0118:
-    case 0x0120:
-    case 0x0128:
-    case 0x0130:
-    case 0x0138:
-    case 0x0140:
-    case 0x0148:
-    case 0x0150:
-    case 0x0158:
-    case 0x0160:
-    case 0x0168:
-    case 0x0170:
-    case 0x0178:
-    case 0x0180:
-    case 0x0188:
-    case 0x0190:
-    case 0x0198:
-    case 0x01a0:
-    case 0x01a8:
-    case 0x01b0:
-    case 0x01b8:
-    case 0x01c0:
-    case 0x01c8:
-    case 0x01d0:
-    case 0x01d8:
-    case 0x01e0:
-    case 0x01e8:
-    case 0x01f0:
-    case 0x01f8:
-        {
-            int entry = (addr - 0x0100) >> 5;
-            int idx = (addr & 0x1f) >> 3;
-            s->dma_regs[entry][idx] = val;
-        }
-        break;
-    /* Offset 0x0210 */
-    case 0x0210:
-        s->offset210 = val;
-        break;
-    /* Interval timer reload */
-    case 0x0228:
-        s->itr = val;
-        qemu_irq_lower(s->timer_irq);
-        set_next_tick(s);
-        break;
-    /* EISA interrupt */
-    case 0x0238:
-        break;
-    default:
-        RC4030_ERROR("invalid write of 0x%02x at [" TARGET_FMT_plx "]\n", val, addr);
-        break;
-    }
-}
-
-static void rc4030_writew(void *opaque, hwaddr addr, uint32_t val)
-{
-    uint32_t old_val = rc4030_readl(opaque, addr & ~0x3);
-
-    if (addr & 0x2)
-        val = (val << 16) | (old_val & 0x0000ffff);
-    else
-        val = val | (old_val & 0xffff0000);
-    rc4030_writel(opaque, addr & ~0x3, val);
-}
-
-static void rc4030_writeb(void *opaque, hwaddr addr, uint32_t val)
-{
-    uint32_t old_val = rc4030_readl(opaque, addr & ~0x3);
-
-    switch (addr & 3) {
-    case 0:
-        val = val | (old_val & 0xffffff00);
-        break;
-    case 1:
-        val = (val << 8) | (old_val & 0xffff00ff);
-        break;
-    case 2:
-        val = (val << 16) | (old_val & 0xff00ffff);
-        break;
-    case 3:
-        val = (val << 24) | (old_val & 0x00ffffff);
-        break;
-    }
-    rc4030_writel(opaque, addr & ~0x3, val);
-}
-
-static const MemoryRegionOps rc4030_ops = {
-    .old_mmio = {
-        .read = { rc4030_readb, rc4030_readw, rc4030_readl, },
-        .write = { rc4030_writeb, rc4030_writew, rc4030_writel, },
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void update_jazz_irq(rc4030State *s)
-{
-    uint16_t pending;
-
-    pending = s->isr_jazz & s->imr_jazz;
-
-#ifdef DEBUG_RC4030
-    if (s->isr_jazz != 0) {
-        uint32_t irq = 0;
-        DPRINTF("pending irqs:");
-        for (irq = 0; irq < ARRAY_SIZE(irq_names); irq++) {
-            if (s->isr_jazz & (1 << irq)) {
-                printf(" %s", irq_names[irq]);
-                if (!(s->imr_jazz & (1 << irq))) {
-                    printf("(ignored)");
-                }
-            }
-        }
-        printf("\n");
-    }
-#endif
-
-    if (pending != 0)
-        qemu_irq_raise(s->jazz_bus_irq);
-    else
-        qemu_irq_lower(s->jazz_bus_irq);
-}
-
-static void rc4030_irq_jazz_request(void *opaque, int irq, int level)
-{
-    rc4030State *s = opaque;
-
-    if (level) {
-        s->isr_jazz |= 1 << irq;
-    } else {
-        s->isr_jazz &= ~(1 << irq);
-    }
-
-    update_jazz_irq(s);
-}
-
-static void rc4030_periodic_timer(void *opaque)
-{
-    rc4030State *s = opaque;
-
-    set_next_tick(s);
-    qemu_irq_raise(s->timer_irq);
-}
-
-static uint32_t jazzio_readw(void *opaque, hwaddr addr)
-{
-    rc4030State *s = opaque;
-    uint32_t val;
-    uint32_t irq;
-    addr &= 0xfff;
-
-    switch (addr) {
-    /* Local bus int source */
-    case 0x00: {
-        uint32_t pending = s->isr_jazz & s->imr_jazz;
-        val = 0;
-        irq = 0;
-        while (pending) {
-            if (pending & 1) {
-                DPRINTF("returning irq %s\n", irq_names[irq]);
-                val = (irq + 1) << 2;
-                break;
-            }
-            irq++;
-            pending >>= 1;
-        }
-        break;
-    }
-    /* Local bus int enable mask */
-    case 0x02:
-        val = s->imr_jazz;
-        break;
-    default:
-        RC4030_ERROR("(jazz io controller) invalid read [" TARGET_FMT_plx "]\n", addr);
-        val = 0;
-    }
-
-    DPRINTF("(jazz io controller) read 0x%04x at " TARGET_FMT_plx "\n", val, addr);
-
-    return val;
-}
-
-static uint32_t jazzio_readb(void *opaque, hwaddr addr)
-{
-    uint32_t v;
-    v = jazzio_readw(opaque, addr & ~0x1);
-    return (v >> (8 * (addr & 0x1))) & 0xff;
-}
-
-static uint32_t jazzio_readl(void *opaque, hwaddr addr)
-{
-    uint32_t v;
-    v = jazzio_readw(opaque, addr);
-    v |= jazzio_readw(opaque, addr + 2) << 16;
-    return v;
-}
-
-static void jazzio_writew(void *opaque, hwaddr addr, uint32_t val)
-{
-    rc4030State *s = opaque;
-    addr &= 0xfff;
-
-    DPRINTF("(jazz io controller) write 0x%04x at " TARGET_FMT_plx "\n", val, addr);
-
-    switch (addr) {
-    /* Local bus int enable mask */
-    case 0x02:
-        s->imr_jazz = val;
-        update_jazz_irq(s);
-        break;
-    default:
-        RC4030_ERROR("(jazz io controller) invalid write of 0x%04x at [" TARGET_FMT_plx "]\n", val, addr);
-        break;
-    }
-}
-
-static void jazzio_writeb(void *opaque, hwaddr addr, uint32_t val)
-{
-    uint32_t old_val = jazzio_readw(opaque, addr & ~0x1);
-
-    switch (addr & 1) {
-    case 0:
-        val = val | (old_val & 0xff00);
-        break;
-    case 1:
-        val = (val << 8) | (old_val & 0x00ff);
-        break;
-    }
-    jazzio_writew(opaque, addr & ~0x1, val);
-}
-
-static void jazzio_writel(void *opaque, hwaddr addr, uint32_t val)
-{
-    jazzio_writew(opaque, addr, val & 0xffff);
-    jazzio_writew(opaque, addr + 2, (val >> 16) & 0xffff);
-}
-
-static const MemoryRegionOps jazzio_ops = {
-    .old_mmio = {
-        .read = { jazzio_readb, jazzio_readw, jazzio_readl, },
-        .write = { jazzio_writeb, jazzio_writew, jazzio_writel, },
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void rc4030_reset(void *opaque)
-{
-    rc4030State *s = opaque;
-    int i;
-
-    s->config = 0x410; /* some boards seem to accept 0x104 too */
-    s->revision = 1;
-    s->invalid_address_register = 0;
-
-    memset(s->dma_regs, 0, sizeof(s->dma_regs));
-    s->dma_tl_base = s->dma_tl_limit = 0;
-
-    s->remote_failed_address = s->memory_failed_address = 0;
-    s->cache_maint = 0;
-    s->cache_ptag = s->cache_ltag = 0;
-    s->cache_bmask = 0;
-
-    s->offset210 = 0x18186;
-    s->nvram_protect = 7;
-    for (i = 0; i < 15; i++)
-        s->rem_speed[i] = 7;
-    s->imr_jazz = 0x10; /* XXX: required by firmware, but why? */
-    s->isr_jazz = 0;
-
-    s->itr = 0;
-
-    qemu_irq_lower(s->timer_irq);
-    qemu_irq_lower(s->jazz_bus_irq);
-}
-
-static int rc4030_load(QEMUFile *f, void *opaque, int version_id)
-{
-    rc4030State* s = opaque;
-    int i, j;
-
-    if (version_id != 2)
-        return -EINVAL;
-
-    s->config = qemu_get_be32(f);
-    s->invalid_address_register = qemu_get_be32(f);
-    for (i = 0; i < 8; i++)
-        for (j = 0; j < 4; j++)
-            s->dma_regs[i][j] = qemu_get_be32(f);
-    s->dma_tl_base = qemu_get_be32(f);
-    s->dma_tl_limit = qemu_get_be32(f);
-    s->cache_maint = qemu_get_be32(f);
-    s->remote_failed_address = qemu_get_be32(f);
-    s->memory_failed_address = qemu_get_be32(f);
-    s->cache_ptag = qemu_get_be32(f);
-    s->cache_ltag = qemu_get_be32(f);
-    s->cache_bmask = qemu_get_be32(f);
-    s->offset210 = qemu_get_be32(f);
-    s->nvram_protect = qemu_get_be32(f);
-    for (i = 0; i < 15; i++)
-        s->rem_speed[i] = qemu_get_be32(f);
-    s->imr_jazz = qemu_get_be32(f);
-    s->isr_jazz = qemu_get_be32(f);
-    s->itr = qemu_get_be32(f);
-
-    set_next_tick(s);
-    update_jazz_irq(s);
-
-    return 0;
-}
-
-static void rc4030_save(QEMUFile *f, void *opaque)
-{
-    rc4030State* s = opaque;
-    int i, j;
-
-    qemu_put_be32(f, s->config);
-    qemu_put_be32(f, s->invalid_address_register);
-    for (i = 0; i < 8; i++)
-        for (j = 0; j < 4; j++)
-            qemu_put_be32(f, s->dma_regs[i][j]);
-    qemu_put_be32(f, s->dma_tl_base);
-    qemu_put_be32(f, s->dma_tl_limit);
-    qemu_put_be32(f, s->cache_maint);
-    qemu_put_be32(f, s->remote_failed_address);
-    qemu_put_be32(f, s->memory_failed_address);
-    qemu_put_be32(f, s->cache_ptag);
-    qemu_put_be32(f, s->cache_ltag);
-    qemu_put_be32(f, s->cache_bmask);
-    qemu_put_be32(f, s->offset210);
-    qemu_put_be32(f, s->nvram_protect);
-    for (i = 0; i < 15; i++)
-        qemu_put_be32(f, s->rem_speed[i]);
-    qemu_put_be32(f, s->imr_jazz);
-    qemu_put_be32(f, s->isr_jazz);
-    qemu_put_be32(f, s->itr);
-}
-
-void rc4030_dma_memory_rw(void *opaque, hwaddr addr, uint8_t *buf, int len, int is_write)
-{
-    rc4030State *s = opaque;
-    hwaddr entry_addr;
-    hwaddr phys_addr;
-    dma_pagetable_entry entry;
-    int index;
-    int ncpy, i;
-
-    i = 0;
-    for (;;) {
-        if (i == len) {
-            break;
-        }
-
-        ncpy = DMA_PAGESIZE - (addr & (DMA_PAGESIZE - 1));
-        if (ncpy > len - i)
-            ncpy = len - i;
-
-        /* Get DMA translation table entry */
-        index = addr / DMA_PAGESIZE;
-        if (index >= s->dma_tl_limit / sizeof(dma_pagetable_entry)) {
-            break;
-        }
-        entry_addr = s->dma_tl_base + index * sizeof(dma_pagetable_entry);
-        /* XXX: not sure. should we really use only lowest bits? */
-        entry_addr &= 0x7fffffff;
-        cpu_physical_memory_read(entry_addr, &entry, sizeof(entry));
-
-        /* Read/write data at right place */
-        phys_addr = entry.frame + (addr & (DMA_PAGESIZE - 1));
-        cpu_physical_memory_rw(phys_addr, &buf[i], ncpy, is_write);
-
-        i += ncpy;
-        addr += ncpy;
-    }
-}
-
-static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_write)
-{
-    rc4030State *s = opaque;
-    hwaddr dma_addr;
-    int dev_to_mem;
-
-    s->dma_regs[n][DMA_REG_ENABLE] &= ~(DMA_FLAG_TC_INTR | DMA_FLAG_MEM_INTR | DMA_FLAG_ADDR_INTR);
-
-    /* Check DMA channel consistency */
-    dev_to_mem = (s->dma_regs[n][DMA_REG_ENABLE] & DMA_FLAG_MEM_TO_DEV) ? 0 : 1;
-    if (!(s->dma_regs[n][DMA_REG_ENABLE] & DMA_FLAG_ENABLE) ||
-        (is_write != dev_to_mem)) {
-        s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_MEM_INTR;
-        s->nmi_interrupt |= 1 << n;
-        return;
-    }
-
-    /* Get start address and len */
-    if (len > s->dma_regs[n][DMA_REG_COUNT])
-        len = s->dma_regs[n][DMA_REG_COUNT];
-    dma_addr = s->dma_regs[n][DMA_REG_ADDRESS];
-
-    /* Read/write data at right place */
-    rc4030_dma_memory_rw(opaque, dma_addr, buf, len, is_write);
-
-    s->dma_regs[n][DMA_REG_ENABLE] |= DMA_FLAG_TC_INTR;
-    s->dma_regs[n][DMA_REG_COUNT] -= len;
-
-#ifdef DEBUG_RC4030_DMA
-    {
-        int i, j;
-        printf("rc4030 dma: Copying %d bytes %s host %p\n",
-            len, is_write ? "from" : "to", buf);
-        for (i = 0; i < len; i += 16) {
-            int n = 16;
-            if (n > len - i) {
-                n = len - i;
-            }
-            for (j = 0; j < n; j++)
-                printf("%02x ", buf[i + j]);
-            while (j++ < 16)
-                printf("   ");
-            printf("| ");
-            for (j = 0; j < n; j++)
-                printf("%c", isprint(buf[i + j]) ? buf[i + j] : '.');
-            printf("\n");
-        }
-    }
-#endif
-}
-
-struct rc4030DMAState {
-    void *opaque;
-    int n;
-};
-
-void rc4030_dma_read(void *dma, uint8_t *buf, int len)
-{
-    rc4030_dma s = dma;
-    rc4030_do_dma(s->opaque, s->n, buf, len, 0);
-}
-
-void rc4030_dma_write(void *dma, uint8_t *buf, int len)
-{
-    rc4030_dma s = dma;
-    rc4030_do_dma(s->opaque, s->n, buf, len, 1);
-}
-
-static rc4030_dma *rc4030_allocate_dmas(void *opaque, int n)
-{
-    rc4030_dma *s;
-    struct rc4030DMAState *p;
-    int i;
-
-    s = (rc4030_dma *)g_malloc0(sizeof(rc4030_dma) * n);
-    p = (struct rc4030DMAState *)g_malloc0(sizeof(struct rc4030DMAState) * n);
-    for (i = 0; i < n; i++) {
-        p->opaque = opaque;
-        p->n = i;
-        s[i] = p;
-        p++;
-    }
-    return s;
-}
-
-void *rc4030_init(qemu_irq timer, qemu_irq jazz_bus,
-                  qemu_irq **irqs, rc4030_dma **dmas,
-                  MemoryRegion *sysmem)
-{
-    rc4030State *s;
-
-    s = g_malloc0(sizeof(rc4030State));
-
-    *irqs = qemu_allocate_irqs(rc4030_irq_jazz_request, s, 16);
-    *dmas = rc4030_allocate_dmas(s, 4);
-
-    s->periodic_timer = qemu_new_timer_ns(vm_clock, rc4030_periodic_timer, s);
-    s->timer_irq = timer;
-    s->jazz_bus_irq = jazz_bus;
-
-    qemu_register_reset(rc4030_reset, s);
-    register_savevm(NULL, "rc4030", 0, 2, rc4030_save, rc4030_load, s);
-    rc4030_reset(s);
-
-    memory_region_init_io(&s->iomem_chipset, &rc4030_ops, s,
-                          "rc4030.chipset", 0x300);
-    memory_region_add_subregion(sysmem, 0x80000000, &s->iomem_chipset);
-    memory_region_init_io(&s->iomem_jazzio, &jazzio_ops, s,
-                          "rc4030.jazzio", 0x00001000);
-    memory_region_add_subregion(sysmem, 0xf0000000, &s->iomem_jazzio);
-
-    return s;
-}
diff --git a/hw/rtl8139.c b/hw/rtl8139.c
deleted file mode 100644 (file)
index 9369507..0000000
+++ /dev/null
@@ -1,3555 +0,0 @@
-/**
- * QEMU RTL8139 emulation
- *
- * Copyright (c) 2006 Igor Kovalenko
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
-
- * Modifications:
- *  2006-Jan-28  Mark Malakanov :   TSAD and CSCR implementation (for Windows driver)
- *
- *  2006-Apr-28  Juergen Lock   :   EEPROM emulation changes for FreeBSD driver
- *                                  HW revision ID changes for FreeBSD driver
- *
- *  2006-Jul-01  Igor Kovalenko :   Implemented loopback mode for FreeBSD driver
- *                                  Corrected packet transfer reassembly routine for 8139C+ mode
- *                                  Rearranged debugging print statements
- *                                  Implemented PCI timer interrupt (disabled by default)
- *                                  Implemented Tally Counters, increased VM load/save version
- *                                  Implemented IP/TCP/UDP checksum task offloading
- *
- *  2006-Jul-04  Igor Kovalenko :   Implemented TCP segmentation offloading
- *                                  Fixed MTU=1500 for produced ethernet frames
- *
- *  2006-Jul-09  Igor Kovalenko :   Fixed TCP header length calculation while processing
- *                                  segmentation offloading
- *                                  Removed slirp.h dependency
- *                                  Added rx/tx buffer reset when enabling rx/tx operation
- *
- *  2010-Feb-04  Frediano Ziglio:   Rewrote timer support using QEMU timer only
- *                                  when strictly needed (required for for
- *                                  Darwin)
- *  2011-Mar-22  Benjamin Poirier:  Implemented VLAN offloading
- */
-
-/* For crc32 */
-#include <zlib.h>
-
-#include "hw/hw.h"
-#include "hw/pci/pci.h"
-#include "sysemu/dma.h"
-#include "qemu/timer.h"
-#include "net/net.h"
-#include "hw/loader.h"
-#include "sysemu/sysemu.h"
-#include "qemu/iov.h"
-
-/* debug RTL8139 card */
-//#define DEBUG_RTL8139 1
-
-#define PCI_FREQUENCY 33000000L
-
-#define SET_MASKED(input, mask, curr) \
-    ( ( (input) & ~(mask) ) | ( (curr) & (mask) ) )
-
-/* arg % size for size which is a power of 2 */
-#define MOD2(input, size) \
-    ( ( input ) & ( size - 1 )  )
-
-#define ETHER_ADDR_LEN 6
-#define ETHER_TYPE_LEN 2
-#define ETH_HLEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN)
-#define ETH_P_IP    0x0800      /* Internet Protocol packet */
-#define ETH_P_8021Q 0x8100      /* 802.1Q VLAN Extended Header  */
-#define ETH_MTU     1500
-
-#define VLAN_TCI_LEN 2
-#define VLAN_HLEN (ETHER_TYPE_LEN + VLAN_TCI_LEN)
-
-#if defined (DEBUG_RTL8139)
-#  define DPRINTF(fmt, ...) \
-    do { fprintf(stderr, "RTL8139: " fmt, ## __VA_ARGS__); } while (0)
-#else
-static inline GCC_FMT_ATTR(1, 2) int DPRINTF(const char *fmt, ...)
-{
-    return 0;
-}
-#endif
-
-/* Symbolic offsets to registers. */
-enum RTL8139_registers {
-    MAC0 = 0,        /* Ethernet hardware address. */
-    MAR0 = 8,        /* Multicast filter. */
-    TxStatus0 = 0x10,/* Transmit status (Four 32bit registers). C mode only */
-                     /* Dump Tally Conter control register(64bit). C+ mode only */
-    TxAddr0 = 0x20,  /* Tx descriptors (also four 32bit). */
-    RxBuf = 0x30,
-    ChipCmd = 0x37,
-    RxBufPtr = 0x38,
-    RxBufAddr = 0x3A,
-    IntrMask = 0x3C,
-    IntrStatus = 0x3E,
-    TxConfig = 0x40,
-    RxConfig = 0x44,
-    Timer = 0x48,        /* A general-purpose counter. */
-    RxMissed = 0x4C,    /* 24 bits valid, write clears. */
-    Cfg9346 = 0x50,
-    Config0 = 0x51,
-    Config1 = 0x52,
-    FlashReg = 0x54,
-    MediaStatus = 0x58,
-    Config3 = 0x59,
-    Config4 = 0x5A,        /* absent on RTL-8139A */
-    HltClk = 0x5B,
-    MultiIntr = 0x5C,
-    PCIRevisionID = 0x5E,
-    TxSummary = 0x60, /* TSAD register. Transmit Status of All Descriptors*/
-    BasicModeCtrl = 0x62,
-    BasicModeStatus = 0x64,
-    NWayAdvert = 0x66,
-    NWayLPAR = 0x68,
-    NWayExpansion = 0x6A,
-    /* Undocumented registers, but required for proper operation. */
-    FIFOTMS = 0x70,        /* FIFO Control and test. */
-    CSCR = 0x74,        /* Chip Status and Configuration Register. */
-    PARA78 = 0x78,
-    PARA7c = 0x7c,        /* Magic transceiver parameter register. */
-    Config5 = 0xD8,        /* absent on RTL-8139A */
-    /* C+ mode */
-    TxPoll        = 0xD9,    /* Tell chip to check Tx descriptors for work */
-    RxMaxSize    = 0xDA, /* Max size of an Rx packet (8169 only) */
-    CpCmd        = 0xE0, /* C+ Command register (C+ mode only) */
-    IntrMitigate    = 0xE2,    /* rx/tx interrupt mitigation control */
-    RxRingAddrLO    = 0xE4, /* 64-bit start addr of Rx ring */
-    RxRingAddrHI    = 0xE8, /* 64-bit start addr of Rx ring */
-    TxThresh    = 0xEC, /* Early Tx threshold */
-};
-
-enum ClearBitMasks {
-    MultiIntrClear = 0xF000,
-    ChipCmdClear = 0xE2,
-    Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1),
-};
-
-enum ChipCmdBits {
-    CmdReset = 0x10,
-    CmdRxEnb = 0x08,
-    CmdTxEnb = 0x04,
-    RxBufEmpty = 0x01,
-};
-
-/* C+ mode */
-enum CplusCmdBits {
-    CPlusRxVLAN   = 0x0040, /* enable receive VLAN detagging */
-    CPlusRxChkSum = 0x0020, /* enable receive checksum offloading */
-    CPlusRxEnb    = 0x0002,
-    CPlusTxEnb    = 0x0001,
-};
-
-/* Interrupt register bits, using my own meaningful names. */
-enum IntrStatusBits {
-    PCIErr = 0x8000,
-    PCSTimeout = 0x4000,
-    RxFIFOOver = 0x40,
-    RxUnderrun = 0x20, /* Packet Underrun / Link Change */
-    RxOverflow = 0x10,
-    TxErr = 0x08,
-    TxOK = 0x04,
-    RxErr = 0x02,
-    RxOK = 0x01,
-
-    RxAckBits = RxFIFOOver | RxOverflow | RxOK,
-};
-
-enum TxStatusBits {
-    TxHostOwns = 0x2000,
-    TxUnderrun = 0x4000,
-    TxStatOK = 0x8000,
-    TxOutOfWindow = 0x20000000,
-    TxAborted = 0x40000000,
-    TxCarrierLost = 0x80000000,
-};
-enum RxStatusBits {
-    RxMulticast = 0x8000,
-    RxPhysical = 0x4000,
-    RxBroadcast = 0x2000,
-    RxBadSymbol = 0x0020,
-    RxRunt = 0x0010,
-    RxTooLong = 0x0008,
-    RxCRCErr = 0x0004,
-    RxBadAlign = 0x0002,
-    RxStatusOK = 0x0001,
-};
-
-/* Bits in RxConfig. */
-enum rx_mode_bits {
-    AcceptErr = 0x20,
-    AcceptRunt = 0x10,
-    AcceptBroadcast = 0x08,
-    AcceptMulticast = 0x04,
-    AcceptMyPhys = 0x02,
-    AcceptAllPhys = 0x01,
-};
-
-/* Bits in TxConfig. */
-enum tx_config_bits {
-
-        /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */
-        TxIFGShift = 24,
-        TxIFG84 = (0 << TxIFGShift),    /* 8.4us / 840ns (10 / 100Mbps) */
-        TxIFG88 = (1 << TxIFGShift),    /* 8.8us / 880ns (10 / 100Mbps) */
-        TxIFG92 = (2 << TxIFGShift),    /* 9.2us / 920ns (10 / 100Mbps) */
-        TxIFG96 = (3 << TxIFGShift),    /* 9.6us / 960ns (10 / 100Mbps) */
-
-    TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */
-    TxCRC = (1 << 16),    /* DISABLE appending CRC to end of Tx packets */
-    TxClearAbt = (1 << 0),    /* Clear abort (WO) */
-    TxDMAShift = 8,        /* DMA burst value (0-7) is shifted this many bits */
-    TxRetryShift = 4,    /* TXRR value (0-15) is shifted this many bits */
-
-    TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */
-};
-
-
-/* Transmit Status of All Descriptors (TSAD) Register */
-enum TSAD_bits {
- TSAD_TOK3 = 1<<15, // TOK bit of Descriptor 3
- TSAD_TOK2 = 1<<14, // TOK bit of Descriptor 2
- TSAD_TOK1 = 1<<13, // TOK bit of Descriptor 1
- TSAD_TOK0 = 1<<12, // TOK bit of Descriptor 0
- TSAD_TUN3 = 1<<11, // TUN bit of Descriptor 3
- TSAD_TUN2 = 1<<10, // TUN bit of Descriptor 2
- TSAD_TUN1 = 1<<9, // TUN bit of Descriptor 1
- TSAD_TUN0 = 1<<8, // TUN bit of Descriptor 0
- TSAD_TABT3 = 1<<07, // TABT bit of Descriptor 3
- TSAD_TABT2 = 1<<06, // TABT bit of Descriptor 2
- TSAD_TABT1 = 1<<05, // TABT bit of Descriptor 1
- TSAD_TABT0 = 1<<04, // TABT bit of Descriptor 0
- TSAD_OWN3 = 1<<03, // OWN bit of Descriptor 3
- TSAD_OWN2 = 1<<02, // OWN bit of Descriptor 2
- TSAD_OWN1 = 1<<01, // OWN bit of Descriptor 1
- TSAD_OWN0 = 1<<00, // OWN bit of Descriptor 0
-};
-
-
-/* Bits in Config1 */
-enum Config1Bits {
-    Cfg1_PM_Enable = 0x01,
-    Cfg1_VPD_Enable = 0x02,
-    Cfg1_PIO = 0x04,
-    Cfg1_MMIO = 0x08,
-    LWAKE = 0x10,        /* not on 8139, 8139A */
-    Cfg1_Driver_Load = 0x20,
-    Cfg1_LED0 = 0x40,
-    Cfg1_LED1 = 0x80,
-    SLEEP = (1 << 1),    /* only on 8139, 8139A */
-    PWRDN = (1 << 0),    /* only on 8139, 8139A */
-};
-
-/* Bits in Config3 */
-enum Config3Bits {
-    Cfg3_FBtBEn    = (1 << 0), /* 1 = Fast Back to Back */
-    Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */
-    Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */
-    Cfg3_CardB_En  = (1 << 3), /* 1 = enable CardBus registers */
-    Cfg3_LinkUp    = (1 << 4), /* 1 = wake up on link up */
-    Cfg3_Magic     = (1 << 5), /* 1 = wake up on Magic Packet (tm) */
-    Cfg3_PARM_En   = (1 << 6), /* 0 = software can set twister parameters */
-    Cfg3_GNTSel    = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */
-};
-
-/* Bits in Config4 */
-enum Config4Bits {
-    LWPTN = (1 << 2),    /* not on 8139, 8139A */
-};
-
-/* Bits in Config5 */
-enum Config5Bits {
-    Cfg5_PME_STS     = (1 << 0), /* 1 = PCI reset resets PME_Status */
-    Cfg5_LANWake     = (1 << 1), /* 1 = enable LANWake signal */
-    Cfg5_LDPS        = (1 << 2), /* 0 = save power when link is down */
-    Cfg5_FIFOAddrPtr = (1 << 3), /* Realtek internal SRAM testing */
-    Cfg5_UWF         = (1 << 4), /* 1 = accept unicast wakeup frame */
-    Cfg5_MWF         = (1 << 5), /* 1 = accept multicast wakeup frame */
-    Cfg5_BWF         = (1 << 6), /* 1 = accept broadcast wakeup frame */
-};
-
-enum RxConfigBits {
-    /* rx fifo threshold */
-    RxCfgFIFOShift = 13,
-    RxCfgFIFONone = (7 << RxCfgFIFOShift),
-
-    /* Max DMA burst */
-    RxCfgDMAShift = 8,
-    RxCfgDMAUnlimited = (7 << RxCfgDMAShift),
-
-    /* rx ring buffer length */
-    RxCfgRcv8K = 0,
-    RxCfgRcv16K = (1 << 11),
-    RxCfgRcv32K = (1 << 12),
-    RxCfgRcv64K = (1 << 11) | (1 << 12),
-
-    /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */
-    RxNoWrap = (1 << 7),
-};
-
-/* Twister tuning parameters from RealTek.
-   Completely undocumented, but required to tune bad links on some boards. */
-/*
-enum CSCRBits {
-    CSCR_LinkOKBit = 0x0400,
-    CSCR_LinkChangeBit = 0x0800,
-    CSCR_LinkStatusBits = 0x0f000,
-    CSCR_LinkDownOffCmd = 0x003c0,
-    CSCR_LinkDownCmd = 0x0f3c0,
-*/
-enum CSCRBits {
-    CSCR_Testfun = 1<<15, /* 1 = Auto-neg speeds up internal timer, WO, def 0 */
-    CSCR_LD  = 1<<9,  /* Active low TPI link disable signal. When low, TPI still transmits link pulses and TPI stays in good link state. def 1*/
-    CSCR_HEART_BIT = 1<<8,  /* 1 = HEART BEAT enable, 0 = HEART BEAT disable. HEART BEAT function is only valid in 10Mbps mode. def 1*/
-    CSCR_JBEN = 1<<7,  /* 1 = enable jabber function. 0 = disable jabber function, def 1*/
-    CSCR_F_LINK_100 = 1<<6, /* Used to login force good link in 100Mbps for diagnostic purposes. 1 = DISABLE, 0 = ENABLE. def 1*/
-    CSCR_F_Connect  = 1<<5,  /* Assertion of this bit forces the disconnect function to be bypassed. def 0*/
-    CSCR_Con_status = 1<<3, /* This bit indicates the status of the connection. 1 = valid connected link detected; 0 = disconnected link detected. RO def 0*/
-    CSCR_Con_status_En = 1<<2, /* Assertion of this bit configures LED1 pin to indicate connection status. def 0*/
-    CSCR_PASS_SCR = 1<<0, /* Bypass Scramble, def 0*/
-};
-
-enum Cfg9346Bits {
-    Cfg9346_Normal = 0x00,
-    Cfg9346_Autoload = 0x40,
-    Cfg9346_Programming = 0x80,
-    Cfg9346_ConfigWrite = 0xC0,
-};
-
-typedef enum {
-    CH_8139 = 0,
-    CH_8139_K,
-    CH_8139A,
-    CH_8139A_G,
-    CH_8139B,
-    CH_8130,
-    CH_8139C,
-    CH_8100,
-    CH_8100B_8139D,
-    CH_8101,
-} chip_t;
-
-enum chip_flags {
-    HasHltClk = (1 << 0),
-    HasLWake = (1 << 1),
-};
-
-#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \
-    (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22)
-#define HW_REVID_MASK    HW_REVID(1, 1, 1, 1, 1, 1, 1)
-
-#define RTL8139_PCI_REVID_8139      0x10
-#define RTL8139_PCI_REVID_8139CPLUS 0x20
-
-#define RTL8139_PCI_REVID           RTL8139_PCI_REVID_8139CPLUS
-
-/* Size is 64 * 16bit words */
-#define EEPROM_9346_ADDR_BITS 6
-#define EEPROM_9346_SIZE  (1 << EEPROM_9346_ADDR_BITS)
-#define EEPROM_9346_ADDR_MASK (EEPROM_9346_SIZE - 1)
-
-enum Chip9346Operation
-{
-    Chip9346_op_mask = 0xc0,          /* 10 zzzzzz */
-    Chip9346_op_read = 0x80,          /* 10 AAAAAA */
-    Chip9346_op_write = 0x40,         /* 01 AAAAAA D(15)..D(0) */
-    Chip9346_op_ext_mask = 0xf0,      /* 11 zzzzzz */
-    Chip9346_op_write_enable = 0x30,  /* 00 11zzzz */
-    Chip9346_op_write_all = 0x10,     /* 00 01zzzz */
-    Chip9346_op_write_disable = 0x00, /* 00 00zzzz */
-};
-
-enum Chip9346Mode
-{
-    Chip9346_none = 0,
-    Chip9346_enter_command_mode,
-    Chip9346_read_command,
-    Chip9346_data_read,      /* from output register */
-    Chip9346_data_write,     /* to input register, then to contents at specified address */
-    Chip9346_data_write_all, /* to input register, then filling contents */
-};
-
-typedef struct EEprom9346
-{
-    uint16_t contents[EEPROM_9346_SIZE];
-    int      mode;
-    uint32_t tick;
-    uint8_t  address;
-    uint16_t input;
-    uint16_t output;
-
-    uint8_t eecs;
-    uint8_t eesk;
-    uint8_t eedi;
-    uint8_t eedo;
-} EEprom9346;
-
-typedef struct RTL8139TallyCounters
-{
-    /* Tally counters */
-    uint64_t   TxOk;
-    uint64_t   RxOk;
-    uint64_t   TxERR;
-    uint32_t   RxERR;
-    uint16_t   MissPkt;
-    uint16_t   FAE;
-    uint32_t   Tx1Col;
-    uint32_t   TxMCol;
-    uint64_t   RxOkPhy;
-    uint64_t   RxOkBrd;
-    uint32_t   RxOkMul;
-    uint16_t   TxAbt;
-    uint16_t   TxUndrn;
-} RTL8139TallyCounters;
-
-/* Clears all tally counters */
-static void RTL8139TallyCounters_clear(RTL8139TallyCounters* counters);
-
-typedef struct RTL8139State {
-    PCIDevice dev;
-    uint8_t phys[8]; /* mac address */
-    uint8_t mult[8]; /* multicast mask array */
-
-    uint32_t TxStatus[4]; /* TxStatus0 in C mode*/ /* also DTCCR[0] and DTCCR[1] in C+ mode */
-    uint32_t TxAddr[4];   /* TxAddr0 */
-    uint32_t RxBuf;       /* Receive buffer */
-    uint32_t RxBufferSize;/* internal variable, receive ring buffer size in C mode */
-    uint32_t RxBufPtr;
-    uint32_t RxBufAddr;
-
-    uint16_t IntrStatus;
-    uint16_t IntrMask;
-
-    uint32_t TxConfig;
-    uint32_t RxConfig;
-    uint32_t RxMissed;
-
-    uint16_t CSCR;
-
-    uint8_t  Cfg9346;
-    uint8_t  Config0;
-    uint8_t  Config1;
-    uint8_t  Config3;
-    uint8_t  Config4;
-    uint8_t  Config5;
-
-    uint8_t  clock_enabled;
-    uint8_t  bChipCmdState;
-
-    uint16_t MultiIntr;
-
-    uint16_t BasicModeCtrl;
-    uint16_t BasicModeStatus;
-    uint16_t NWayAdvert;
-    uint16_t NWayLPAR;
-    uint16_t NWayExpansion;
-
-    uint16_t CpCmd;
-    uint8_t  TxThresh;
-
-    NICState *nic;
-    NICConf conf;
-
-    /* C ring mode */
-    uint32_t   currTxDesc;
-
-    /* C+ mode */
-    uint32_t   cplus_enabled;
-
-    uint32_t   currCPlusRxDesc;
-    uint32_t   currCPlusTxDesc;
-
-    uint32_t   RxRingAddrLO;
-    uint32_t   RxRingAddrHI;
-
-    EEprom9346 eeprom;
-
-    uint32_t   TCTR;
-    uint32_t   TimerInt;
-    int64_t    TCTR_base;
-
-    /* Tally counters */
-    RTL8139TallyCounters tally_counters;
-
-    /* Non-persistent data */
-    uint8_t   *cplus_txbuffer;
-    int        cplus_txbuffer_len;
-    int        cplus_txbuffer_offset;
-
-    /* PCI interrupt timer */
-    QEMUTimer *timer;
-    int64_t TimerExpire;
-
-    MemoryRegion bar_io;
-    MemoryRegion bar_mem;
-
-    /* Support migration to/from old versions */
-    int rtl8139_mmio_io_addr_dummy;
-} RTL8139State;
-
-/* Writes tally counters to memory via DMA */
-static void RTL8139TallyCounters_dma_write(RTL8139State *s, dma_addr_t tc_addr);
-
-static void rtl8139_set_next_tctr_time(RTL8139State *s, int64_t current_time);
-
-static void prom9346_decode_command(EEprom9346 *eeprom, uint8_t command)
-{
-    DPRINTF("eeprom command 0x%02x\n", command);
-
-    switch (command & Chip9346_op_mask)
-    {
-        case Chip9346_op_read:
-        {
-            eeprom->address = command & EEPROM_9346_ADDR_MASK;
-            eeprom->output = eeprom->contents[eeprom->address];
-            eeprom->eedo = 0;
-            eeprom->tick = 0;
-            eeprom->mode = Chip9346_data_read;
-            DPRINTF("eeprom read from address 0x%02x data=0x%04x\n",
-                eeprom->address, eeprom->output);
-        }
-        break;
-
-        case Chip9346_op_write:
-        {
-            eeprom->address = command & EEPROM_9346_ADDR_MASK;
-            eeprom->input = 0;
-            eeprom->tick = 0;
-            eeprom->mode = Chip9346_none; /* Chip9346_data_write */
-            DPRINTF("eeprom begin write to address 0x%02x\n",
-                eeprom->address);
-        }
-        break;
-        default:
-            eeprom->mode = Chip9346_none;
-            switch (command & Chip9346_op_ext_mask)
-            {
-                case Chip9346_op_write_enable:
-                    DPRINTF("eeprom write enabled\n");
-                    break;
-                case Chip9346_op_write_all:
-                    DPRINTF("eeprom begin write all\n");
-                    break;
-                case Chip9346_op_write_disable:
-                    DPRINTF("eeprom write disabled\n");
-                    break;
-            }
-            break;
-    }
-}
-
-static void prom9346_shift_clock(EEprom9346 *eeprom)
-{
-    int bit = eeprom->eedi?1:0;
-
-    ++ eeprom->tick;
-
-    DPRINTF("eeprom: tick %d eedi=%d eedo=%d\n", eeprom->tick, eeprom->eedi,
-        eeprom->eedo);
-
-    switch (eeprom->mode)
-    {
-        case Chip9346_enter_command_mode:
-            if (bit)
-            {
-                eeprom->mode = Chip9346_read_command;
-                eeprom->tick = 0;
-                eeprom->input = 0;
-                DPRINTF("eeprom: +++ synchronized, begin command read\n");
-            }
-            break;
-
-        case Chip9346_read_command:
-            eeprom->input = (eeprom->input << 1) | (bit & 1);
-            if (eeprom->tick == 8)
-            {
-                prom9346_decode_command(eeprom, eeprom->input & 0xff);
-            }
-            break;
-
-        case Chip9346_data_read:
-            eeprom->eedo = (eeprom->output & 0x8000)?1:0;
-            eeprom->output <<= 1;
-            if (eeprom->tick == 16)
-            {
-#if 1
-        // the FreeBSD drivers (rl and re) don't explicitly toggle
-        // CS between reads (or does setting Cfg9346 to 0 count too?),
-        // so we need to enter wait-for-command state here
-                eeprom->mode = Chip9346_enter_command_mode;
-                eeprom->input = 0;
-                eeprom->tick = 0;
-
-                DPRINTF("eeprom: +++ end of read, awaiting next command\n");
-#else
-        // original behaviour
-                ++eeprom->address;
-                eeprom->address &= EEPROM_9346_ADDR_MASK;
-                eeprom->output = eeprom->contents[eeprom->address];
-                eeprom->tick = 0;
-
-                DPRINTF("eeprom: +++ read next address 0x%02x data=0x%04x\n",
-                    eeprom->address, eeprom->output);
-#endif
-            }
-            break;
-
-        case Chip9346_data_write:
-            eeprom->input = (eeprom->input << 1) | (bit & 1);
-            if (eeprom->tick == 16)
-            {
-                DPRINTF("eeprom write to address 0x%02x data=0x%04x\n",
-                    eeprom->address, eeprom->input);
-
-                eeprom->contents[eeprom->address] = eeprom->input;
-                eeprom->mode = Chip9346_none; /* waiting for next command after CS cycle */
-                eeprom->tick = 0;
-                eeprom->input = 0;
-            }
-            break;
-
-        case Chip9346_data_write_all:
-            eeprom->input = (eeprom->input << 1) | (bit & 1);
-            if (eeprom->tick == 16)
-            {
-                int i;
-                for (i = 0; i < EEPROM_9346_SIZE; i++)
-                {
-                    eeprom->contents[i] = eeprom->input;
-                }
-                DPRINTF("eeprom filled with data=0x%04x\n", eeprom->input);
-
-                eeprom->mode = Chip9346_enter_command_mode;
-                eeprom->tick = 0;
-                eeprom->input = 0;
-            }
-            break;
-
-        default:
-            break;
-    }
-}
-
-static int prom9346_get_wire(RTL8139State *s)
-{
-    EEprom9346 *eeprom = &s->eeprom;
-    if (!eeprom->eecs)
-        return 0;
-
-    return eeprom->eedo;
-}
-
-/* FIXME: This should be merged into/replaced by eeprom93xx.c.  */
-static void prom9346_set_wire(RTL8139State *s, int eecs, int eesk, int eedi)
-{
-    EEprom9346 *eeprom = &s->eeprom;
-    uint8_t old_eecs = eeprom->eecs;
-    uint8_t old_eesk = eeprom->eesk;
-
-    eeprom->eecs = eecs;
-    eeprom->eesk = eesk;
-    eeprom->eedi = eedi;
-
-    DPRINTF("eeprom: +++ wires CS=%d SK=%d DI=%d DO=%d\n", eeprom->eecs,
-        eeprom->eesk, eeprom->eedi, eeprom->eedo);
-
-    if (!old_eecs && eecs)
-    {
-        /* Synchronize start */
-        eeprom->tick = 0;
-        eeprom->input = 0;
-        eeprom->output = 0;
-        eeprom->mode = Chip9346_enter_command_mode;
-
-        DPRINTF("=== eeprom: begin access, enter command mode\n");
-    }
-
-    if (!eecs)
-    {
-        DPRINTF("=== eeprom: end access\n");
-        return;
-    }
-
-    if (!old_eesk && eesk)
-    {
-        /* SK front rules */
-        prom9346_shift_clock(eeprom);
-    }
-}
-
-static void rtl8139_update_irq(RTL8139State *s)
-{
-    int isr;
-    isr = (s->IntrStatus & s->IntrMask) & 0xffff;
-
-    DPRINTF("Set IRQ to %d (%04x %04x)\n", isr ? 1 : 0, s->IntrStatus,
-        s->IntrMask);
-
-    qemu_set_irq(s->dev.irq[0], (isr != 0));
-}
-
-static int rtl8139_RxWrap(RTL8139State *s)
-{
-    /* wrapping enabled; assume 1.5k more buffer space if size < 65536 */
-    return (s->RxConfig & (1 << 7));
-}
-
-static int rtl8139_receiver_enabled(RTL8139State *s)
-{
-    return s->bChipCmdState & CmdRxEnb;
-}
-
-static int rtl8139_transmitter_enabled(RTL8139State *s)
-{
-    return s->bChipCmdState & CmdTxEnb;
-}
-
-static int rtl8139_cp_receiver_enabled(RTL8139State *s)
-{
-    return s->CpCmd & CPlusRxEnb;
-}
-
-static int rtl8139_cp_transmitter_enabled(RTL8139State *s)
-{
-    return s->CpCmd & CPlusTxEnb;
-}
-
-static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size)
-{
-    if (s->RxBufAddr + size > s->RxBufferSize)
-    {
-        int wrapped = MOD2(s->RxBufAddr + size, s->RxBufferSize);
-
-        /* write packet data */
-        if (wrapped && !(s->RxBufferSize < 65536 && rtl8139_RxWrap(s)))
-        {
-            DPRINTF(">>> rx packet wrapped in buffer at %d\n", size - wrapped);
-
-            if (size > wrapped)
-            {
-                pci_dma_write(&s->dev, s->RxBuf + s->RxBufAddr,
-                              buf, size-wrapped);
-            }
-
-            /* reset buffer pointer */
-            s->RxBufAddr = 0;
-
-            pci_dma_write(&s->dev, s->RxBuf + s->RxBufAddr,
-                          buf + (size-wrapped), wrapped);
-
-            s->RxBufAddr = wrapped;
-
-            return;
-        }
-    }
-
-    /* non-wrapping path or overwrapping enabled */
-    pci_dma_write(&s->dev, s->RxBuf + s->RxBufAddr, buf, size);
-
-    s->RxBufAddr += size;
-}
-
-#define MIN_BUF_SIZE 60
-static inline dma_addr_t rtl8139_addr64(uint32_t low, uint32_t high)
-{
-    return low | ((uint64_t)high << 32);
-}
-
-/* Workaround for buggy guest driver such as linux who allocates rx
- * rings after the receiver were enabled. */
-static bool rtl8139_cp_rx_valid(RTL8139State *s)
-{
-    return !(s->RxRingAddrLO == 0 && s->RxRingAddrHI == 0);
-}
-
-static int rtl8139_can_receive(NetClientState *nc)
-{
-    RTL8139State *s = qemu_get_nic_opaque(nc);
-    int avail;
-
-    /* Receive (drop) packets if card is disabled.  */
-    if (!s->clock_enabled)
-      return 1;
-    if (!rtl8139_receiver_enabled(s))
-      return 1;
-
-    if (rtl8139_cp_receiver_enabled(s) && rtl8139_cp_rx_valid(s)) {
-        /* ??? Flow control not implemented in c+ mode.
-           This is a hack to work around slirp deficiencies anyway.  */
-        return 1;
-    } else {
-        avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr,
-                     s->RxBufferSize);
-        return (avail == 0 || avail >= 1514 || (s->IntrMask & RxOverflow));
-    }
-}
-
-static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t size_, int do_interrupt)
-{
-    RTL8139State *s = qemu_get_nic_opaque(nc);
-    /* size is the length of the buffer passed to the driver */
-    int size = size_;
-    const uint8_t *dot1q_buf = NULL;
-
-    uint32_t packet_header = 0;
-
-    uint8_t buf1[MIN_BUF_SIZE + VLAN_HLEN];
-    static const uint8_t broadcast_macaddr[6] =
-        { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-
-    DPRINTF(">>> received len=%d\n", size);
-
-    /* test if board clock is stopped */
-    if (!s->clock_enabled)
-    {
-        DPRINTF("stopped ==========================\n");
-        return -1;
-    }
-
-    /* first check if receiver is enabled */
-
-    if (!rtl8139_receiver_enabled(s))
-    {
-        DPRINTF("receiver disabled ================\n");
-        return -1;
-    }
-
-    /* XXX: check this */
-    if (s->RxConfig & AcceptAllPhys) {
-        /* promiscuous: receive all */
-        DPRINTF(">>> packet received in promiscuous mode\n");
-
-    } else {
-        if (!memcmp(buf,  broadcast_macaddr, 6)) {
-            /* broadcast address */
-            if (!(s->RxConfig & AcceptBroadcast))
-            {
-                DPRINTF(">>> broadcast packet rejected\n");
-
-                /* update tally counter */
-                ++s->tally_counters.RxERR;
-
-                return size;
-            }
-
-            packet_header |= RxBroadcast;
-
-            DPRINTF(">>> broadcast packet received\n");
-
-            /* update tally counter */
-            ++s->tally_counters.RxOkBrd;
-
-        } else if (buf[0] & 0x01) {
-            /* multicast */
-            if (!(s->RxConfig & AcceptMulticast))
-            {
-                DPRINTF(">>> multicast packet rejected\n");
-
-                /* update tally counter */
-                ++s->tally_counters.RxERR;
-
-                return size;
-            }
-
-            int mcast_idx = compute_mcast_idx(buf);
-
-            if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))))
-            {
-                DPRINTF(">>> multicast address mismatch\n");
-
-                /* update tally counter */
-                ++s->tally_counters.RxERR;
-
-                return size;
-            }
-
-            packet_header |= RxMulticast;
-
-            DPRINTF(">>> multicast packet received\n");
-
-            /* update tally counter */
-            ++s->tally_counters.RxOkMul;
-
-        } else if (s->phys[0] == buf[0] &&
-                   s->phys[1] == buf[1] &&
-                   s->phys[2] == buf[2] &&
-                   s->phys[3] == buf[3] &&
-                   s->phys[4] == buf[4] &&
-                   s->phys[5] == buf[5]) {
-            /* match */
-            if (!(s->RxConfig & AcceptMyPhys))
-            {
-                DPRINTF(">>> rejecting physical address matching packet\n");
-
-                /* update tally counter */
-                ++s->tally_counters.RxERR;
-
-                return size;
-            }
-
-            packet_header |= RxPhysical;
-
-            DPRINTF(">>> physical address matching packet received\n");
-
-            /* update tally counter */
-            ++s->tally_counters.RxOkPhy;
-
-        } else {
-
-            DPRINTF(">>> unknown packet\n");
-
-            /* update tally counter */
-            ++s->tally_counters.RxERR;
-
-            return size;
-        }
-    }
-
-    /* if too small buffer, then expand it
-     * Include some tailroom in case a vlan tag is later removed. */
-    if (size < MIN_BUF_SIZE + VLAN_HLEN) {
-        memcpy(buf1, buf, size);
-        memset(buf1 + size, 0, MIN_BUF_SIZE + VLAN_HLEN - size);
-        buf = buf1;
-        if (size < MIN_BUF_SIZE) {
-            size = MIN_BUF_SIZE;
-        }
-    }
-
-    if (rtl8139_cp_receiver_enabled(s))
-    {
-        if (!rtl8139_cp_rx_valid(s)) {
-            return size;
-        }
-
-        DPRINTF("in C+ Rx mode ================\n");
-
-        /* begin C+ receiver mode */
-
-/* w0 ownership flag */
-#define CP_RX_OWN (1<<31)
-/* w0 end of ring flag */
-#define CP_RX_EOR (1<<30)
-/* w0 bits 0...12 : buffer size */
-#define CP_RX_BUFFER_SIZE_MASK ((1<<13) - 1)
-/* w1 tag available flag */
-#define CP_RX_TAVA (1<<16)
-/* w1 bits 0...15 : VLAN tag */
-#define CP_RX_VLAN_TAG_MASK ((1<<16) - 1)
-/* w2 low  32bit of Rx buffer ptr */
-/* w3 high 32bit of Rx buffer ptr */
-
-        int descriptor = s->currCPlusRxDesc;
-        dma_addr_t cplus_rx_ring_desc;
-
-        cplus_rx_ring_desc = rtl8139_addr64(s->RxRingAddrLO, s->RxRingAddrHI);
-        cplus_rx_ring_desc += 16 * descriptor;
-
-        DPRINTF("+++ C+ mode reading RX descriptor %d from host memory at "
-            "%08x %08x = "DMA_ADDR_FMT"\n", descriptor, s->RxRingAddrHI,
-            s->RxRingAddrLO, cplus_rx_ring_desc);
-
-        uint32_t val, rxdw0,rxdw1,rxbufLO,rxbufHI;
-
-        pci_dma_read(&s->dev, cplus_rx_ring_desc, &val, 4);
-        rxdw0 = le32_to_cpu(val);
-        pci_dma_read(&s->dev, cplus_rx_ring_desc+4, &val, 4);
-        rxdw1 = le32_to_cpu(val);
-        pci_dma_read(&s->dev, cplus_rx_ring_desc+8, &val, 4);
-        rxbufLO = le32_to_cpu(val);
-        pci_dma_read(&s->dev, cplus_rx_ring_desc+12, &val, 4);
-        rxbufHI = le32_to_cpu(val);
-
-        DPRINTF("+++ C+ mode RX descriptor %d %08x %08x %08x %08x\n",
-            descriptor, rxdw0, rxdw1, rxbufLO, rxbufHI);
-
-        if (!(rxdw0 & CP_RX_OWN))
-        {
-            DPRINTF("C+ Rx mode : descriptor %d is owned by host\n",
-                descriptor);
-
-            s->IntrStatus |= RxOverflow;
-            ++s->RxMissed;
-
-            /* update tally counter */
-            ++s->tally_counters.RxERR;
-            ++s->tally_counters.MissPkt;
-
-            rtl8139_update_irq(s);
-            return size_;
-        }
-
-        uint32_t rx_space = rxdw0 & CP_RX_BUFFER_SIZE_MASK;
-
-        /* write VLAN info to descriptor variables. */
-        if (s->CpCmd & CPlusRxVLAN && be16_to_cpup((uint16_t *)
-                &buf[ETHER_ADDR_LEN * 2]) == ETH_P_8021Q) {
-            dot1q_buf = &buf[ETHER_ADDR_LEN * 2];
-            size -= VLAN_HLEN;
-            /* if too small buffer, use the tailroom added duing expansion */
-            if (size < MIN_BUF_SIZE) {
-                size = MIN_BUF_SIZE;
-            }
-
-            rxdw1 &= ~CP_RX_VLAN_TAG_MASK;
-            /* BE + ~le_to_cpu()~ + cpu_to_le() = BE */
-            rxdw1 |= CP_RX_TAVA | le16_to_cpup((uint16_t *)
-                &dot1q_buf[ETHER_TYPE_LEN]);
-
-            DPRINTF("C+ Rx mode : extracted vlan tag with tci: ""%u\n",
-                be16_to_cpup((uint16_t *)&dot1q_buf[ETHER_TYPE_LEN]));
-        } else {
-            /* reset VLAN tag flag */
-            rxdw1 &= ~CP_RX_TAVA;
-        }
-
-        /* TODO: scatter the packet over available receive ring descriptors space */
-
-        if (size+4 > rx_space)
-        {
-            DPRINTF("C+ Rx mode : descriptor %d size %d received %d + 4\n",
-                descriptor, rx_space, size);
-
-            s->IntrStatus |= RxOverflow;
-            ++s->RxMissed;
-
-            /* update tally counter */
-            ++s->tally_counters.RxERR;
-            ++s->tally_counters.MissPkt;
-
-            rtl8139_update_irq(s);
-            return size_;
-        }
-
-        dma_addr_t rx_addr = rtl8139_addr64(rxbufLO, rxbufHI);
-
-        /* receive/copy to target memory */
-        if (dot1q_buf) {
-            pci_dma_write(&s->dev, rx_addr, buf, 2 * ETHER_ADDR_LEN);
-            pci_dma_write(&s->dev, rx_addr + 2 * ETHER_ADDR_LEN,
-                          buf + 2 * ETHER_ADDR_LEN + VLAN_HLEN,
-                          size - 2 * ETHER_ADDR_LEN);
-        } else {
-            pci_dma_write(&s->dev, rx_addr, buf, size);
-        }
-
-        if (s->CpCmd & CPlusRxChkSum)
-        {
-            /* do some packet checksumming */
-        }
-
-        /* write checksum */
-        val = cpu_to_le32(crc32(0, buf, size_));
-        pci_dma_write(&s->dev, rx_addr+size, (uint8_t *)&val, 4);
-
-/* first segment of received packet flag */
-#define CP_RX_STATUS_FS (1<<29)
-/* last segment of received packet flag */
-#define CP_RX_STATUS_LS (1<<28)
-/* multicast packet flag */
-#define CP_RX_STATUS_MAR (1<<26)
-/* physical-matching packet flag */
-#define CP_RX_STATUS_PAM (1<<25)
-/* broadcast packet flag */
-#define CP_RX_STATUS_BAR (1<<24)
-/* runt packet flag */
-#define CP_RX_STATUS_RUNT (1<<19)
-/* crc error flag */
-#define CP_RX_STATUS_CRC (1<<18)
-/* IP checksum error flag */
-#define CP_RX_STATUS_IPF (1<<15)
-/* UDP checksum error flag */
-#define CP_RX_STATUS_UDPF (1<<14)
-/* TCP checksum error flag */
-#define CP_RX_STATUS_TCPF (1<<13)
-
-        /* transfer ownership to target */
-        rxdw0 &= ~CP_RX_OWN;
-
-        /* set first segment bit */
-        rxdw0 |= CP_RX_STATUS_FS;
-
-        /* set last segment bit */
-        rxdw0 |= CP_RX_STATUS_LS;
-
-        /* set received packet type flags */
-        if (packet_header & RxBroadcast)
-            rxdw0 |= CP_RX_STATUS_BAR;
-        if (packet_header & RxMulticast)
-            rxdw0 |= CP_RX_STATUS_MAR;
-        if (packet_header & RxPhysical)
-            rxdw0 |= CP_RX_STATUS_PAM;
-
-        /* set received size */
-        rxdw0 &= ~CP_RX_BUFFER_SIZE_MASK;
-        rxdw0 |= (size+4);
-
-        /* update ring data */
-        val = cpu_to_le32(rxdw0);
-        pci_dma_write(&s->dev, cplus_rx_ring_desc, (uint8_t *)&val, 4);
-        val = cpu_to_le32(rxdw1);
-        pci_dma_write(&s->dev, cplus_rx_ring_desc+4, (uint8_t *)&val, 4);
-
-        /* update tally counter */
-        ++s->tally_counters.RxOk;
-
-        /* seek to next Rx descriptor */
-        if (rxdw0 & CP_RX_EOR)
-        {
-            s->currCPlusRxDesc = 0;
-        }
-        else
-        {
-            ++s->currCPlusRxDesc;
-        }
-
-        DPRINTF("done C+ Rx mode ----------------\n");
-
-    }
-    else
-    {
-        DPRINTF("in ring Rx mode ================\n");
-
-        /* begin ring receiver mode */
-        int avail = MOD2(s->RxBufferSize + s->RxBufPtr - s->RxBufAddr, s->RxBufferSize);
-
-        /* if receiver buffer is empty then avail == 0 */
-
-        if (avail != 0 && size + 8 >= avail)
-        {
-            DPRINTF("rx overflow: rx buffer length %d head 0x%04x "
-                "read 0x%04x === available 0x%04x need 0x%04x\n",
-                s->RxBufferSize, s->RxBufAddr, s->RxBufPtr, avail, size + 8);
-
-            s->IntrStatus |= RxOverflow;
-            ++s->RxMissed;
-            rtl8139_update_irq(s);
-            return size_;
-        }
-
-        packet_header |= RxStatusOK;
-
-        packet_header |= (((size+4) << 16) & 0xffff0000);
-
-        /* write header */
-        uint32_t val = cpu_to_le32(packet_header);
-
-        rtl8139_write_buffer(s, (uint8_t *)&val, 4);
-
-        rtl8139_write_buffer(s, buf, size);
-
-        /* write checksum */
-        val = cpu_to_le32(crc32(0, buf, size));
-        rtl8139_write_buffer(s, (uint8_t *)&val, 4);
-
-        /* correct buffer write pointer */
-        s->RxBufAddr = MOD2((s->RxBufAddr + 3) & ~0x3, s->RxBufferSize);
-
-        /* now we can signal we have received something */
-
-        DPRINTF("received: rx buffer length %d head 0x%04x read 0x%04x\n",
-            s->RxBufferSize, s->RxBufAddr, s->RxBufPtr);
-    }
-
-    s->IntrStatus |= RxOK;
-
-    if (do_interrupt)
-    {
-        rtl8139_update_irq(s);
-    }
-
-    return size_;
-}
-
-static ssize_t rtl8139_receive(NetClientState *nc, const uint8_t *buf, size_t size)
-{
-    return rtl8139_do_receive(nc, buf, size, 1);
-}
-
-static void rtl8139_reset_rxring(RTL8139State *s, uint32_t bufferSize)
-{
-    s->RxBufferSize = bufferSize;
-    s->RxBufPtr  = 0;
-    s->RxBufAddr = 0;
-}
-
-static void rtl8139_reset(DeviceState *d)
-{
-    RTL8139State *s = container_of(d, RTL8139State, dev.qdev);
-    int i;
-
-    /* restore MAC address */
-    memcpy(s->phys, s->conf.macaddr.a, 6);
-
-    /* reset interrupt mask */
-    s->IntrStatus = 0;
-    s->IntrMask = 0;
-
-    rtl8139_update_irq(s);
-
-    /* mark all status registers as owned by host */
-    for (i = 0; i < 4; ++i)
-    {
-        s->TxStatus[i] = TxHostOwns;
-    }
-
-    s->currTxDesc = 0;
-    s->currCPlusRxDesc = 0;
-    s->currCPlusTxDesc = 0;
-
-    s->RxRingAddrLO = 0;
-    s->RxRingAddrHI = 0;
-
-    s->RxBuf = 0;
-
-    rtl8139_reset_rxring(s, 8192);
-
-    /* ACK the reset */
-    s->TxConfig = 0;
-
-#if 0
-//    s->TxConfig |= HW_REVID(1, 0, 0, 0, 0, 0, 0); // RTL-8139  HasHltClk
-    s->clock_enabled = 0;
-#else
-    s->TxConfig |= HW_REVID(1, 1, 1, 0, 1, 1, 0); // RTL-8139C+ HasLWake
-    s->clock_enabled = 1;
-#endif
-
-    s->bChipCmdState = CmdReset; /* RxBufEmpty bit is calculated on read from ChipCmd */;
-
-    /* set initial state data */
-    s->Config0 = 0x0; /* No boot ROM */
-    s->Config1 = 0xC; /* IO mapped and MEM mapped registers available */
-    s->Config3 = 0x1; /* fast back-to-back compatible */
-    s->Config5 = 0x0;
-
-    s->CSCR = CSCR_F_LINK_100 | CSCR_HEART_BIT | CSCR_LD;
-
-    s->CpCmd   = 0x0; /* reset C+ mode */
-    s->cplus_enabled = 0;
-
-
-//    s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation
-//    s->BasicModeCtrl = 0x2100; // 100Mbps, full duplex
-    s->BasicModeCtrl = 0x1000; // autonegotiation
-
-    s->BasicModeStatus  = 0x7809;
-    //s->BasicModeStatus |= 0x0040; /* UTP medium */
-    s->BasicModeStatus |= 0x0020; /* autonegotiation completed */
-    /* preserve link state */
-    s->BasicModeStatus |= qemu_get_queue(s->nic)->link_down ? 0 : 0x04;
-
-    s->NWayAdvert    = 0x05e1; /* all modes, full duplex */
-    s->NWayLPAR      = 0x05e1; /* all modes, full duplex */
-    s->NWayExpansion = 0x0001; /* autonegotiation supported */
-
-    /* also reset timer and disable timer interrupt */
-    s->TCTR = 0;
-    s->TimerInt = 0;
-    s->TCTR_base = 0;
-
-    /* reset tally counters */
-    RTL8139TallyCounters_clear(&s->tally_counters);
-}
-
-static void RTL8139TallyCounters_clear(RTL8139TallyCounters* counters)
-{
-    counters->TxOk = 0;
-    counters->RxOk = 0;
-    counters->TxERR = 0;
-    counters->RxERR = 0;
-    counters->MissPkt = 0;
-    counters->FAE = 0;
-    counters->Tx1Col = 0;
-    counters->TxMCol = 0;
-    counters->RxOkPhy = 0;
-    counters->RxOkBrd = 0;
-    counters->RxOkMul = 0;
-    counters->TxAbt = 0;
-    counters->TxUndrn = 0;
-}
-
-static void RTL8139TallyCounters_dma_write(RTL8139State *s, dma_addr_t tc_addr)
-{
-    RTL8139TallyCounters *tally_counters = &s->tally_counters;
-    uint16_t val16;
-    uint32_t val32;
-    uint64_t val64;
-
-    val64 = cpu_to_le64(tally_counters->TxOk);
-    pci_dma_write(&s->dev, tc_addr + 0,     (uint8_t *)&val64, 8);
-
-    val64 = cpu_to_le64(tally_counters->RxOk);
-    pci_dma_write(&s->dev, tc_addr + 8,     (uint8_t *)&val64, 8);
-
-    val64 = cpu_to_le64(tally_counters->TxERR);
-    pci_dma_write(&s->dev, tc_addr + 16,    (uint8_t *)&val64, 8);
-
-    val32 = cpu_to_le32(tally_counters->RxERR);
-    pci_dma_write(&s->dev, tc_addr + 24,    (uint8_t *)&val32, 4);
-
-    val16 = cpu_to_le16(tally_counters->MissPkt);
-    pci_dma_write(&s->dev, tc_addr + 28,    (uint8_t *)&val16, 2);
-
-    val16 = cpu_to_le16(tally_counters->FAE);
-    pci_dma_write(&s->dev, tc_addr + 30,    (uint8_t *)&val16, 2);
-
-    val32 = cpu_to_le32(tally_counters->Tx1Col);
-    pci_dma_write(&s->dev, tc_addr + 32,    (uint8_t *)&val32, 4);
-
-    val32 = cpu_to_le32(tally_counters->TxMCol);
-    pci_dma_write(&s->dev, tc_addr + 36,    (uint8_t *)&val32, 4);
-
-    val64 = cpu_to_le64(tally_counters->RxOkPhy);
-    pci_dma_write(&s->dev, tc_addr + 40,    (uint8_t *)&val64, 8);
-
-    val64 = cpu_to_le64(tally_counters->RxOkBrd);
-    pci_dma_write(&s->dev, tc_addr + 48,    (uint8_t *)&val64, 8);
-
-    val32 = cpu_to_le32(tally_counters->RxOkMul);
-    pci_dma_write(&s->dev, tc_addr + 56,    (uint8_t *)&val32, 4);
-
-    val16 = cpu_to_le16(tally_counters->TxAbt);
-    pci_dma_write(&s->dev, tc_addr + 60,    (uint8_t *)&val16, 2);
-
-    val16 = cpu_to_le16(tally_counters->TxUndrn);
-    pci_dma_write(&s->dev, tc_addr + 62,    (uint8_t *)&val16, 2);
-}
-
-/* Loads values of tally counters from VM state file */
-
-static const VMStateDescription vmstate_tally_counters = {
-    .name = "tally_counters",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT64(TxOk, RTL8139TallyCounters),
-        VMSTATE_UINT64(RxOk, RTL8139TallyCounters),
-        VMSTATE_UINT64(TxERR, RTL8139TallyCounters),
-        VMSTATE_UINT32(RxERR, RTL8139TallyCounters),
-        VMSTATE_UINT16(MissPkt, RTL8139TallyCounters),
-        VMSTATE_UINT16(FAE, RTL8139TallyCounters),
-        VMSTATE_UINT32(Tx1Col, RTL8139TallyCounters),
-        VMSTATE_UINT32(TxMCol, RTL8139TallyCounters),
-        VMSTATE_UINT64(RxOkPhy, RTL8139TallyCounters),
-        VMSTATE_UINT64(RxOkBrd, RTL8139TallyCounters),
-        VMSTATE_UINT16(TxAbt, RTL8139TallyCounters),
-        VMSTATE_UINT16(TxUndrn, RTL8139TallyCounters),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void rtl8139_ChipCmd_write(RTL8139State *s, uint32_t val)
-{
-    val &= 0xff;
-
-    DPRINTF("ChipCmd write val=0x%08x\n", val);
-
-    if (val & CmdReset)
-    {
-        DPRINTF("ChipCmd reset\n");
-        rtl8139_reset(&s->dev.qdev);
-    }
-    if (val & CmdRxEnb)
-    {
-        DPRINTF("ChipCmd enable receiver\n");
-
-        s->currCPlusRxDesc = 0;
-    }
-    if (val & CmdTxEnb)
-    {
-        DPRINTF("ChipCmd enable transmitter\n");
-
-        s->currCPlusTxDesc = 0;
-    }
-
-    /* mask unwritable bits */
-    val = SET_MASKED(val, 0xe3, s->bChipCmdState);
-
-    /* Deassert reset pin before next read */
-    val &= ~CmdReset;
-
-    s->bChipCmdState = val;
-}
-
-static int rtl8139_RxBufferEmpty(RTL8139State *s)
-{
-    int unread = MOD2(s->RxBufferSize + s->RxBufAddr - s->RxBufPtr, s->RxBufferSize);
-
-    if (unread != 0)
-    {
-        DPRINTF("receiver buffer data available 0x%04x\n", unread);
-        return 0;
-    }
-
-    DPRINTF("receiver buffer is empty\n");
-
-    return 1;
-}
-
-static uint32_t rtl8139_ChipCmd_read(RTL8139State *s)
-{
-    uint32_t ret = s->bChipCmdState;
-
-    if (rtl8139_RxBufferEmpty(s))
-        ret |= RxBufEmpty;
-
-    DPRINTF("ChipCmd read val=0x%04x\n", ret);
-
-    return ret;
-}
-
-static void rtl8139_CpCmd_write(RTL8139State *s, uint32_t val)
-{
-    val &= 0xffff;
-
-    DPRINTF("C+ command register write(w) val=0x%04x\n", val);
-
-    s->cplus_enabled = 1;
-
-    /* mask unwritable bits */
-    val = SET_MASKED(val, 0xff84, s->CpCmd);
-
-    s->CpCmd = val;
-}
-
-static uint32_t rtl8139_CpCmd_read(RTL8139State *s)
-{
-    uint32_t ret = s->CpCmd;
-
-    DPRINTF("C+ command register read(w) val=0x%04x\n", ret);
-
-    return ret;
-}
-
-static void rtl8139_IntrMitigate_write(RTL8139State *s, uint32_t val)
-{
-    DPRINTF("C+ IntrMitigate register write(w) val=0x%04x\n", val);
-}
-
-static uint32_t rtl8139_IntrMitigate_read(RTL8139State *s)
-{
-    uint32_t ret = 0;
-
-    DPRINTF("C+ IntrMitigate register read(w) val=0x%04x\n", ret);
-
-    return ret;
-}
-
-static int rtl8139_config_writable(RTL8139State *s)
-{
-    if ((s->Cfg9346 & Chip9346_op_mask) == Cfg9346_ConfigWrite)
-    {
-        return 1;
-    }
-
-    DPRINTF("Configuration registers are write-protected\n");
-
-    return 0;
-}
-
-static void rtl8139_BasicModeCtrl_write(RTL8139State *s, uint32_t val)
-{
-    val &= 0xffff;
-
-    DPRINTF("BasicModeCtrl register write(w) val=0x%04x\n", val);
-
-    /* mask unwritable bits */
-    uint32_t mask = 0x4cff;
-
-    if (1 || !rtl8139_config_writable(s))
-    {
-        /* Speed setting and autonegotiation enable bits are read-only */
-        mask |= 0x3000;
-        /* Duplex mode setting is read-only */
-        mask |= 0x0100;
-    }
-
-    val = SET_MASKED(val, mask, s->BasicModeCtrl);
-
-    s->BasicModeCtrl = val;
-}
-
-static uint32_t rtl8139_BasicModeCtrl_read(RTL8139State *s)
-{
-    uint32_t ret = s->BasicModeCtrl;
-
-    DPRINTF("BasicModeCtrl register read(w) val=0x%04x\n", ret);
-
-    return ret;
-}
-
-static void rtl8139_BasicModeStatus_write(RTL8139State *s, uint32_t val)
-{
-    val &= 0xffff;
-
-    DPRINTF("BasicModeStatus register write(w) val=0x%04x\n", val);
-
-    /* mask unwritable bits */
-    val = SET_MASKED(val, 0xff3f, s->BasicModeStatus);
-
-    s->BasicModeStatus = val;
-}
-
-static uint32_t rtl8139_BasicModeStatus_read(RTL8139State *s)
-{
-    uint32_t ret = s->BasicModeStatus;
-
-    DPRINTF("BasicModeStatus register read(w) val=0x%04x\n", ret);
-
-    return ret;
-}
-
-static void rtl8139_Cfg9346_write(RTL8139State *s, uint32_t val)
-{
-    val &= 0xff;
-
-    DPRINTF("Cfg9346 write val=0x%02x\n", val);
-
-    /* mask unwritable bits */
-    val = SET_MASKED(val, 0x31, s->Cfg9346);
-
-    uint32_t opmode = val & 0xc0;
-    uint32_t eeprom_val = val & 0xf;
-
-    if (opmode == 0x80) {
-        /* eeprom access */
-        int eecs = (eeprom_val & 0x08)?1:0;
-        int eesk = (eeprom_val & 0x04)?1:0;
-        int eedi = (eeprom_val & 0x02)?1:0;
-        prom9346_set_wire(s, eecs, eesk, eedi);
-    } else if (opmode == 0x40) {
-        /* Reset.  */
-        val = 0;
-        rtl8139_reset(&s->dev.qdev);
-    }
-
-    s->Cfg9346 = val;
-}
-
-static uint32_t rtl8139_Cfg9346_read(RTL8139State *s)
-{
-    uint32_t ret = s->Cfg9346;
-
-    uint32_t opmode = ret & 0xc0;
-
-    if (opmode == 0x80)
-    {
-        /* eeprom access */
-        int eedo = prom9346_get_wire(s);
-        if (eedo)
-        {
-            ret |=  0x01;
-        }
-        else
-        {
-            ret &= ~0x01;
-        }
-    }
-
-    DPRINTF("Cfg9346 read val=0x%02x\n", ret);
-
-    return ret;
-}
-
-static void rtl8139_Config0_write(RTL8139State *s, uint32_t val)
-{
-    val &= 0xff;
-
-    DPRINTF("Config0 write val=0x%02x\n", val);
-
-    if (!rtl8139_config_writable(s)) {
-        return;
-    }
-
-    /* mask unwritable bits */
-    val = SET_MASKED(val, 0xf8, s->Config0);
-
-    s->Config0 = val;
-}
-
-static uint32_t rtl8139_Config0_read(RTL8139State *s)
-{
-    uint32_t ret = s->Config0;
-
-    DPRINTF("Config0 read val=0x%02x\n", ret);
-
-    return ret;
-}
-
-static void rtl8139_Config1_write(RTL8139State *s, uint32_t val)
-{
-    val &= 0xff;
-
-    DPRINTF("Config1 write val=0x%02x\n", val);
-
-    if (!rtl8139_config_writable(s)) {
-        return;
-    }
-
-    /* mask unwritable bits */
-    val = SET_MASKED(val, 0xC, s->Config1);
-
-    s->Config1 = val;
-}
-
-static uint32_t rtl8139_Config1_read(RTL8139State *s)
-{
-    uint32_t ret = s->Config1;
-
-    DPRINTF("Config1 read val=0x%02x\n", ret);
-
-    return ret;
-}
-
-static void rtl8139_Config3_write(RTL8139State *s, uint32_t val)
-{
-    val &= 0xff;
-
-    DPRINTF("Config3 write val=0x%02x\n", val);
-
-    if (!rtl8139_config_writable(s)) {
-        return;
-    }
-
-    /* mask unwritable bits */
-    val = SET_MASKED(val, 0x8F, s->Config3);
-
-    s->Config3 = val;
-}
-
-static uint32_t rtl8139_Config3_read(RTL8139State *s)
-{
-    uint32_t ret = s->Config3;
-
-    DPRINTF("Config3 read val=0x%02x\n", ret);
-
-    return ret;
-}
-
-static void rtl8139_Config4_write(RTL8139State *s, uint32_t val)
-{
-    val &= 0xff;
-
-    DPRINTF("Config4 write val=0x%02x\n", val);
-
-    if (!rtl8139_config_writable(s)) {
-        return;
-    }
-
-    /* mask unwritable bits */
-    val = SET_MASKED(val, 0x0a, s->Config4);
-
-    s->Config4 = val;
-}
-
-static uint32_t rtl8139_Config4_read(RTL8139State *s)
-{
-    uint32_t ret = s->Config4;
-
-    DPRINTF("Config4 read val=0x%02x\n", ret);
-
-    return ret;
-}
-
-static void rtl8139_Config5_write(RTL8139State *s, uint32_t val)
-{
-    val &= 0xff;
-
-    DPRINTF("Config5 write val=0x%02x\n", val);
-
-    /* mask unwritable bits */
-    val = SET_MASKED(val, 0x80, s->Config5);
-
-    s->Config5 = val;
-}
-
-static uint32_t rtl8139_Config5_read(RTL8139State *s)
-{
-    uint32_t ret = s->Config5;
-
-    DPRINTF("Config5 read val=0x%02x\n", ret);
-
-    return ret;
-}
-
-static void rtl8139_TxConfig_write(RTL8139State *s, uint32_t val)
-{
-    if (!rtl8139_transmitter_enabled(s))
-    {
-        DPRINTF("transmitter disabled; no TxConfig write val=0x%08x\n", val);
-        return;
-    }
-
-    DPRINTF("TxConfig write val=0x%08x\n", val);
-
-    val = SET_MASKED(val, TxVersionMask | 0x8070f80f, s->TxConfig);
-
-    s->TxConfig = val;
-}
-
-static void rtl8139_TxConfig_writeb(RTL8139State *s, uint32_t val)
-{
-    DPRINTF("RTL8139C TxConfig via write(b) val=0x%02x\n", val);
-
-    uint32_t tc = s->TxConfig;
-    tc &= 0xFFFFFF00;
-    tc |= (val & 0x000000FF);
-    rtl8139_TxConfig_write(s, tc);
-}
-
-static uint32_t rtl8139_TxConfig_read(RTL8139State *s)
-{
-    uint32_t ret = s->TxConfig;
-
-    DPRINTF("TxConfig read val=0x%04x\n", ret);
-
-    return ret;
-}
-
-static void rtl8139_RxConfig_write(RTL8139State *s, uint32_t val)
-{
-    DPRINTF("RxConfig write val=0x%08x\n", val);
-
-    /* mask unwritable bits */
-    val = SET_MASKED(val, 0xf0fc0040, s->RxConfig);
-
-    s->RxConfig = val;
-
-    /* reset buffer size and read/write pointers */
-    rtl8139_reset_rxring(s, 8192 << ((s->RxConfig >> 11) & 0x3));
-
-    DPRINTF("RxConfig write reset buffer size to %d\n", s->RxBufferSize);
-}
-
-static uint32_t rtl8139_RxConfig_read(RTL8139State *s)
-{
-    uint32_t ret = s->RxConfig;
-
-    DPRINTF("RxConfig read val=0x%08x\n", ret);
-
-    return ret;
-}
-
-static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size,
-    int do_interrupt, const uint8_t *dot1q_buf)
-{
-    struct iovec *iov = NULL;
-
-    if (!size)
-    {
-        DPRINTF("+++ empty ethernet frame\n");
-        return;
-    }
-
-    if (dot1q_buf && size >= ETHER_ADDR_LEN * 2) {
-        iov = (struct iovec[3]) {
-            { .iov_base = buf, .iov_len = ETHER_ADDR_LEN * 2 },
-            { .iov_base = (void *) dot1q_buf, .iov_len = VLAN_HLEN },
-            { .iov_base = buf + ETHER_ADDR_LEN * 2,
-                .iov_len = size - ETHER_ADDR_LEN * 2 },
-        };
-    }
-
-    if (TxLoopBack == (s->TxConfig & TxLoopBack))
-    {
-        size_t buf2_size;
-        uint8_t *buf2;
-
-        if (iov) {
-            buf2_size = iov_size(iov, 3);
-            buf2 = g_malloc(buf2_size);
-            iov_to_buf(iov, 3, 0, buf2, buf2_size);
-            buf = buf2;
-        }
-
-        DPRINTF("+++ transmit loopback mode\n");
-        rtl8139_do_receive(qemu_get_queue(s->nic), buf, size, do_interrupt);
-
-        if (iov) {
-            g_free(buf2);
-        }
-    }
-    else
-    {
-        if (iov) {
-            qemu_sendv_packet(qemu_get_queue(s->nic), iov, 3);
-        } else {
-            qemu_send_packet(qemu_get_queue(s->nic), buf, size);
-        }
-    }
-}
-
-static int rtl8139_transmit_one(RTL8139State *s, int descriptor)
-{
-    if (!rtl8139_transmitter_enabled(s))
-    {
-        DPRINTF("+++ cannot transmit from descriptor %d: transmitter "
-            "disabled\n", descriptor);
-        return 0;
-    }
-
-    if (s->TxStatus[descriptor] & TxHostOwns)
-    {
-        DPRINTF("+++ cannot transmit from descriptor %d: owned by host "
-            "(%08x)\n", descriptor, s->TxStatus[descriptor]);
-        return 0;
-    }
-
-    DPRINTF("+++ transmitting from descriptor %d\n", descriptor);
-
-    int txsize = s->TxStatus[descriptor] & 0x1fff;
-    uint8_t txbuffer[0x2000];
-
-    DPRINTF("+++ transmit reading %d bytes from host memory at 0x%08x\n",
-        txsize, s->TxAddr[descriptor]);
-
-    pci_dma_read(&s->dev, s->TxAddr[descriptor], txbuffer, txsize);
-
-    /* Mark descriptor as transferred */
-    s->TxStatus[descriptor] |= TxHostOwns;
-    s->TxStatus[descriptor] |= TxStatOK;
-
-    rtl8139_transfer_frame(s, txbuffer, txsize, 0, NULL);
-
-    DPRINTF("+++ transmitted %d bytes from descriptor %d\n", txsize,
-        descriptor);
-
-    /* update interrupt */
-    s->IntrStatus |= TxOK;
-    rtl8139_update_irq(s);
-
-    return 1;
-}
-
-/* structures and macros for task offloading */
-typedef struct ip_header
-{
-    uint8_t  ip_ver_len;    /* version and header length */
-    uint8_t  ip_tos;        /* type of service */
-    uint16_t ip_len;        /* total length */
-    uint16_t ip_id;         /* identification */
-    uint16_t ip_off;        /* fragment offset field */
-    uint8_t  ip_ttl;        /* time to live */
-    uint8_t  ip_p;          /* protocol */
-    uint16_t ip_sum;        /* checksum */
-    uint32_t ip_src,ip_dst; /* source and dest address */
-} ip_header;
-
-#define IP_HEADER_VERSION_4 4
-#define IP_HEADER_VERSION(ip) ((ip->ip_ver_len >> 4)&0xf)
-#define IP_HEADER_LENGTH(ip) (((ip->ip_ver_len)&0xf) << 2)
-
-typedef struct tcp_header
-{
-    uint16_t th_sport;         /* source port */
-    uint16_t th_dport;         /* destination port */
-    uint32_t th_seq;                   /* sequence number */
-    uint32_t th_ack;                   /* acknowledgement number */
-    uint16_t th_offset_flags; /* data offset, reserved 6 bits, TCP protocol flags */
-    uint16_t th_win;                   /* window */
-    uint16_t th_sum;                   /* checksum */
-    uint16_t th_urp;                   /* urgent pointer */
-} tcp_header;
-
-typedef struct udp_header
-{
-    uint16_t uh_sport; /* source port */
-    uint16_t uh_dport; /* destination port */
-    uint16_t uh_ulen;  /* udp length */
-    uint16_t uh_sum;   /* udp checksum */
-} udp_header;
-
-typedef struct ip_pseudo_header
-{
-    uint32_t ip_src;
-    uint32_t ip_dst;
-    uint8_t  zeros;
-    uint8_t  ip_proto;
-    uint16_t ip_payload;
-} ip_pseudo_header;
-
-#define IP_PROTO_TCP 6
-#define IP_PROTO_UDP 17
-
-#define TCP_HEADER_DATA_OFFSET(tcp) (((be16_to_cpu(tcp->th_offset_flags) >> 12)&0xf) << 2)
-#define TCP_FLAGS_ONLY(flags) ((flags)&0x3f)
-#define TCP_HEADER_FLAGS(tcp) TCP_FLAGS_ONLY(be16_to_cpu(tcp->th_offset_flags))
-
-#define TCP_HEADER_CLEAR_FLAGS(tcp, off) ((tcp)->th_offset_flags &= cpu_to_be16(~TCP_FLAGS_ONLY(off)))
-
-#define TCP_FLAG_FIN  0x01
-#define TCP_FLAG_PUSH 0x08
-
-/* produces ones' complement sum of data */
-static uint16_t ones_complement_sum(uint8_t *data, size_t len)
-{
-    uint32_t result = 0;
-
-    for (; len > 1; data+=2, len-=2)
-    {
-        result += *(uint16_t*)data;
-    }
-
-    /* add the remainder byte */
-    if (len)
-    {
-        uint8_t odd[2] = {*data, 0};
-        result += *(uint16_t*)odd;
-    }
-
-    while (result>>16)
-        result = (result & 0xffff) + (result >> 16);
-
-    return result;
-}
-
-static uint16_t ip_checksum(void *data, size_t len)
-{
-    return ~ones_complement_sum((uint8_t*)data, len);
-}
-
-static int rtl8139_cplus_transmit_one(RTL8139State *s)
-{
-    if (!rtl8139_transmitter_enabled(s))
-    {
-        DPRINTF("+++ C+ mode: transmitter disabled\n");
-        return 0;
-    }
-
-    if (!rtl8139_cp_transmitter_enabled(s))
-    {
-        DPRINTF("+++ C+ mode: C+ transmitter disabled\n");
-        return 0 ;
-    }
-
-    int descriptor = s->currCPlusTxDesc;
-
-    dma_addr_t cplus_tx_ring_desc = rtl8139_addr64(s->TxAddr[0], s->TxAddr[1]);
-
-    /* Normal priority ring */
-    cplus_tx_ring_desc += 16 * descriptor;
-
-    DPRINTF("+++ C+ mode reading TX descriptor %d from host memory at "
-        "%08x %08x = 0x"DMA_ADDR_FMT"\n", descriptor, s->TxAddr[1],
-        s->TxAddr[0], cplus_tx_ring_desc);
-
-    uint32_t val, txdw0,txdw1,txbufLO,txbufHI;
-
-    pci_dma_read(&s->dev, cplus_tx_ring_desc,    (uint8_t *)&val, 4);
-    txdw0 = le32_to_cpu(val);
-    pci_dma_read(&s->dev, cplus_tx_ring_desc+4,  (uint8_t *)&val, 4);
-    txdw1 = le32_to_cpu(val);
-    pci_dma_read(&s->dev, cplus_tx_ring_desc+8,  (uint8_t *)&val, 4);
-    txbufLO = le32_to_cpu(val);
-    pci_dma_read(&s->dev, cplus_tx_ring_desc+12, (uint8_t *)&val, 4);
-    txbufHI = le32_to_cpu(val);
-
-    DPRINTF("+++ C+ mode TX descriptor %d %08x %08x %08x %08x\n", descriptor,
-        txdw0, txdw1, txbufLO, txbufHI);
-
-/* w0 ownership flag */
-#define CP_TX_OWN (1<<31)
-/* w0 end of ring flag */
-#define CP_TX_EOR (1<<30)
-/* first segment of received packet flag */
-#define CP_TX_FS (1<<29)
-/* last segment of received packet flag */
-#define CP_TX_LS (1<<28)
-/* large send packet flag */
-#define CP_TX_LGSEN (1<<27)
-/* large send MSS mask, bits 16...25 */
-#define CP_TC_LGSEN_MSS_MASK ((1 << 12) - 1)
-
-/* IP checksum offload flag */
-#define CP_TX_IPCS (1<<18)
-/* UDP checksum offload flag */
-#define CP_TX_UDPCS (1<<17)
-/* TCP checksum offload flag */
-#define CP_TX_TCPCS (1<<16)
-
-/* w0 bits 0...15 : buffer size */
-#define CP_TX_BUFFER_SIZE (1<<16)
-#define CP_TX_BUFFER_SIZE_MASK (CP_TX_BUFFER_SIZE - 1)
-/* w1 add tag flag */
-#define CP_TX_TAGC (1<<17)
-/* w1 bits 0...15 : VLAN tag (big endian) */
-#define CP_TX_VLAN_TAG_MASK ((1<<16) - 1)
-/* w2 low  32bit of Rx buffer ptr */
-/* w3 high 32bit of Rx buffer ptr */
-
-/* set after transmission */
-/* FIFO underrun flag */
-#define CP_TX_STATUS_UNF (1<<25)
-/* transmit error summary flag, valid if set any of three below */
-#define CP_TX_STATUS_TES (1<<23)
-/* out-of-window collision flag */
-#define CP_TX_STATUS_OWC (1<<22)
-/* link failure flag */
-#define CP_TX_STATUS_LNKF (1<<21)
-/* excessive collisions flag */
-#define CP_TX_STATUS_EXC (1<<20)
-
-    if (!(txdw0 & CP_TX_OWN))
-    {
-        DPRINTF("C+ Tx mode : descriptor %d is owned by host\n", descriptor);
-        return 0 ;
-    }
-
-    DPRINTF("+++ C+ Tx mode : transmitting from descriptor %d\n", descriptor);
-
-    if (txdw0 & CP_TX_FS)
-    {
-        DPRINTF("+++ C+ Tx mode : descriptor %d is first segment "
-            "descriptor\n", descriptor);
-
-        /* reset internal buffer offset */
-        s->cplus_txbuffer_offset = 0;
-    }
-
-    int txsize = txdw0 & CP_TX_BUFFER_SIZE_MASK;
-    dma_addr_t tx_addr = rtl8139_addr64(txbufLO, txbufHI);
-
-    /* make sure we have enough space to assemble the packet */
-    if (!s->cplus_txbuffer)
-    {
-        s->cplus_txbuffer_len = CP_TX_BUFFER_SIZE;
-        s->cplus_txbuffer = g_malloc(s->cplus_txbuffer_len);
-        s->cplus_txbuffer_offset = 0;
-
-        DPRINTF("+++ C+ mode transmission buffer allocated space %d\n",
-            s->cplus_txbuffer_len);
-    }
-
-    if (s->cplus_txbuffer_offset + txsize >= s->cplus_txbuffer_len)
-    {
-        /* The spec didn't tell the maximum size, stick to CP_TX_BUFFER_SIZE */
-        txsize = s->cplus_txbuffer_len - s->cplus_txbuffer_offset;
-        DPRINTF("+++ C+ mode transmission buffer overrun, truncated descriptor"
-                "length to %d\n", txsize);
-    }
-
-    if (!s->cplus_txbuffer)
-    {
-        /* out of memory */
-
-        DPRINTF("+++ C+ mode transmiter failed to reallocate %d bytes\n",
-            s->cplus_txbuffer_len);
-
-        /* update tally counter */
-        ++s->tally_counters.TxERR;
-        ++s->tally_counters.TxAbt;
-
-        return 0;
-    }
-
-    /* append more data to the packet */
-
-    DPRINTF("+++ C+ mode transmit reading %d bytes from host memory at "
-            DMA_ADDR_FMT" to offset %d\n", txsize, tx_addr,
-            s->cplus_txbuffer_offset);
-
-    pci_dma_read(&s->dev, tx_addr,
-                 s->cplus_txbuffer + s->cplus_txbuffer_offset, txsize);
-    s->cplus_txbuffer_offset += txsize;
-
-    /* seek to next Rx descriptor */
-    if (txdw0 & CP_TX_EOR)
-    {
-        s->currCPlusTxDesc = 0;
-    }
-    else
-    {
-        ++s->currCPlusTxDesc;
-        if (s->currCPlusTxDesc >= 64)
-            s->currCPlusTxDesc = 0;
-    }
-
-    /* transfer ownership to target */
-    txdw0 &= ~CP_RX_OWN;
-
-    /* reset error indicator bits */
-    txdw0 &= ~CP_TX_STATUS_UNF;
-    txdw0 &= ~CP_TX_STATUS_TES;
-    txdw0 &= ~CP_TX_STATUS_OWC;
-    txdw0 &= ~CP_TX_STATUS_LNKF;
-    txdw0 &= ~CP_TX_STATUS_EXC;
-
-    /* update ring data */
-    val = cpu_to_le32(txdw0);
-    pci_dma_write(&s->dev, cplus_tx_ring_desc, (uint8_t *)&val, 4);
-
-    /* Now decide if descriptor being processed is holding the last segment of packet */
-    if (txdw0 & CP_TX_LS)
-    {
-        uint8_t dot1q_buffer_space[VLAN_HLEN];
-        uint16_t *dot1q_buffer;
-
-        DPRINTF("+++ C+ Tx mode : descriptor %d is last segment descriptor\n",
-            descriptor);
-
-        /* can transfer fully assembled packet */
-
-        uint8_t *saved_buffer  = s->cplus_txbuffer;
-        int      saved_size    = s->cplus_txbuffer_offset;
-        int      saved_buffer_len = s->cplus_txbuffer_len;
-
-        /* create vlan tag */
-        if (txdw1 & CP_TX_TAGC) {
-            /* the vlan tag is in BE byte order in the descriptor
-             * BE + le_to_cpu() + ~swap()~ = cpu */
-            DPRINTF("+++ C+ Tx mode : inserting vlan tag with ""tci: %u\n",
-                bswap16(txdw1 & CP_TX_VLAN_TAG_MASK));
-
-            dot1q_buffer = (uint16_t *) dot1q_buffer_space;
-            dot1q_buffer[0] = cpu_to_be16(ETH_P_8021Q);
-            /* BE + le_to_cpu() + ~cpu_to_le()~ = BE */
-            dot1q_buffer[1] = cpu_to_le16(txdw1 & CP_TX_VLAN_TAG_MASK);
-        } else {
-            dot1q_buffer = NULL;
-        }
-
-        /* reset the card space to protect from recursive call */
-        s->cplus_txbuffer = NULL;
-        s->cplus_txbuffer_offset = 0;
-        s->cplus_txbuffer_len = 0;
-
-        if (txdw0 & (CP_TX_IPCS | CP_TX_UDPCS | CP_TX_TCPCS | CP_TX_LGSEN))
-        {
-            DPRINTF("+++ C+ mode offloaded task checksum\n");
-
-            /* ip packet header */
-            ip_header *ip = NULL;
-            int hlen = 0;
-            uint8_t  ip_protocol = 0;
-            uint16_t ip_data_len = 0;
-
-            uint8_t *eth_payload_data = NULL;
-            size_t   eth_payload_len  = 0;
-
-            int proto = be16_to_cpu(*(uint16_t *)(saved_buffer + 12));
-            if (proto == ETH_P_IP)
-            {
-                DPRINTF("+++ C+ mode has IP packet\n");
-
-                /* not aligned */
-                eth_payload_data = saved_buffer + ETH_HLEN;
-                eth_payload_len  = saved_size   - ETH_HLEN;
-
-                ip = (ip_header*)eth_payload_data;
-
-                if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
-                    DPRINTF("+++ C+ mode packet has bad IP version %d "
-                        "expected %d\n", IP_HEADER_VERSION(ip),
-                        IP_HEADER_VERSION_4);
-                    ip = NULL;
-                } else {
-                    hlen = IP_HEADER_LENGTH(ip);
-                    ip_protocol = ip->ip_p;
-                    ip_data_len = be16_to_cpu(ip->ip_len) - hlen;
-                }
-            }
-
-            if (ip)
-            {
-                if (txdw0 & CP_TX_IPCS)
-                {
-                    DPRINTF("+++ C+ mode need IP checksum\n");
-
-                    if (hlen<sizeof(ip_header) || hlen>eth_payload_len) {/* min header length */
-                        /* bad packet header len */
-                        /* or packet too short */
-                    }
-                    else
-                    {
-                        ip->ip_sum = 0;
-                        ip->ip_sum = ip_checksum(ip, hlen);
-                        DPRINTF("+++ C+ mode IP header len=%d checksum=%04x\n",
-                            hlen, ip->ip_sum);
-                    }
-                }
-
-                if ((txdw0 & CP_TX_LGSEN) && ip_protocol == IP_PROTO_TCP)
-                {
-                    int large_send_mss = (txdw0 >> 16) & CP_TC_LGSEN_MSS_MASK;
-
-                    DPRINTF("+++ C+ mode offloaded task TSO MTU=%d IP data %d "
-                        "frame data %d specified MSS=%d\n", ETH_MTU,
-                        ip_data_len, saved_size - ETH_HLEN, large_send_mss);
-
-                    int tcp_send_offset = 0;
-                    int send_count = 0;
-
-                    /* maximum IP header length is 60 bytes */
-                    uint8_t saved_ip_header[60];
-
-                    /* save IP header template; data area is used in tcp checksum calculation */
-                    memcpy(saved_ip_header, eth_payload_data, hlen);
-
-                    /* a placeholder for checksum calculation routine in tcp case */
-                    uint8_t *data_to_checksum     = eth_payload_data + hlen - 12;
-                    //                    size_t   data_to_checksum_len = eth_payload_len  - hlen + 12;
-
-                    /* pointer to TCP header */
-                    tcp_header *p_tcp_hdr = (tcp_header*)(eth_payload_data + hlen);
-
-                    int tcp_hlen = TCP_HEADER_DATA_OFFSET(p_tcp_hdr);
-
-                    /* ETH_MTU = ip header len + tcp header len + payload */
-                    int tcp_data_len = ip_data_len - tcp_hlen;
-                    int tcp_chunk_size = ETH_MTU - hlen - tcp_hlen;
-
-                    DPRINTF("+++ C+ mode TSO IP data len %d TCP hlen %d TCP "
-                        "data len %d TCP chunk size %d\n", ip_data_len,
-                        tcp_hlen, tcp_data_len, tcp_chunk_size);
-
-                    /* note the cycle below overwrites IP header data,
-                       but restores it from saved_ip_header before sending packet */
-
-                    int is_last_frame = 0;
-
-                    for (tcp_send_offset = 0; tcp_send_offset < tcp_data_len; tcp_send_offset += tcp_chunk_size)
-                    {
-                        uint16_t chunk_size = tcp_chunk_size;
-
-                        /* check if this is the last frame */
-                        if (tcp_send_offset + tcp_chunk_size >= tcp_data_len)
-                        {
-                            is_last_frame = 1;
-                            chunk_size = tcp_data_len - tcp_send_offset;
-                        }
-
-                        DPRINTF("+++ C+ mode TSO TCP seqno %08x\n",
-                            be32_to_cpu(p_tcp_hdr->th_seq));
-
-                        /* add 4 TCP pseudoheader fields */
-                        /* copy IP source and destination fields */
-                        memcpy(data_to_checksum, saved_ip_header + 12, 8);
-
-                        DPRINTF("+++ C+ mode TSO calculating TCP checksum for "
-                            "packet with %d bytes data\n", tcp_hlen +
-                            chunk_size);
-
-                        if (tcp_send_offset)
-                        {
-                            memcpy((uint8_t*)p_tcp_hdr + tcp_hlen, (uint8_t*)p_tcp_hdr + tcp_hlen + tcp_send_offset, chunk_size);
-                        }
-
-                        /* keep PUSH and FIN flags only for the last frame */
-                        if (!is_last_frame)
-                        {
-                            TCP_HEADER_CLEAR_FLAGS(p_tcp_hdr, TCP_FLAG_PUSH|TCP_FLAG_FIN);
-                        }
-
-                        /* recalculate TCP checksum */
-                        ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
-                        p_tcpip_hdr->zeros      = 0;
-                        p_tcpip_hdr->ip_proto   = IP_PROTO_TCP;
-                        p_tcpip_hdr->ip_payload = cpu_to_be16(tcp_hlen + chunk_size);
-
-                        p_tcp_hdr->th_sum = 0;
-
-                        int tcp_checksum = ip_checksum(data_to_checksum, tcp_hlen + chunk_size + 12);
-                        DPRINTF("+++ C+ mode TSO TCP checksum %04x\n",
-                            tcp_checksum);
-
-                        p_tcp_hdr->th_sum = tcp_checksum;
-
-                        /* restore IP header */
-                        memcpy(eth_payload_data, saved_ip_header, hlen);
-
-                        /* set IP data length and recalculate IP checksum */
-                        ip->ip_len = cpu_to_be16(hlen + tcp_hlen + chunk_size);
-
-                        /* increment IP id for subsequent frames */
-                        ip->ip_id = cpu_to_be16(tcp_send_offset/tcp_chunk_size + be16_to_cpu(ip->ip_id));
-
-                        ip->ip_sum = 0;
-                        ip->ip_sum = ip_checksum(eth_payload_data, hlen);
-                        DPRINTF("+++ C+ mode TSO IP header len=%d "
-                            "checksum=%04x\n", hlen, ip->ip_sum);
-
-                        int tso_send_size = ETH_HLEN + hlen + tcp_hlen + chunk_size;
-                        DPRINTF("+++ C+ mode TSO transferring packet size "
-                            "%d\n", tso_send_size);
-                        rtl8139_transfer_frame(s, saved_buffer, tso_send_size,
-                            0, (uint8_t *) dot1q_buffer);
-
-                        /* add transferred count to TCP sequence number */
-                        p_tcp_hdr->th_seq = cpu_to_be32(chunk_size + be32_to_cpu(p_tcp_hdr->th_seq));
-                        ++send_count;
-                    }
-
-                    /* Stop sending this frame */
-                    saved_size = 0;
-                }
-                else if (txdw0 & (CP_TX_TCPCS|CP_TX_UDPCS))
-                {
-                    DPRINTF("+++ C+ mode need TCP or UDP checksum\n");
-
-                    /* maximum IP header length is 60 bytes */
-                    uint8_t saved_ip_header[60];
-                    memcpy(saved_ip_header, eth_payload_data, hlen);
-
-                    uint8_t *data_to_checksum     = eth_payload_data + hlen - 12;
-                    //                    size_t   data_to_checksum_len = eth_payload_len  - hlen + 12;
-
-                    /* add 4 TCP pseudoheader fields */
-                    /* copy IP source and destination fields */
-                    memcpy(data_to_checksum, saved_ip_header + 12, 8);
-
-                    if ((txdw0 & CP_TX_TCPCS) && ip_protocol == IP_PROTO_TCP)
-                    {
-                        DPRINTF("+++ C+ mode calculating TCP checksum for "
-                            "packet with %d bytes data\n", ip_data_len);
-
-                        ip_pseudo_header *p_tcpip_hdr = (ip_pseudo_header *)data_to_checksum;
-                        p_tcpip_hdr->zeros      = 0;
-                        p_tcpip_hdr->ip_proto   = IP_PROTO_TCP;
-                        p_tcpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
-
-                        tcp_header* p_tcp_hdr = (tcp_header *) (data_to_checksum+12);
-
-                        p_tcp_hdr->th_sum = 0;
-
-                        int tcp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
-                        DPRINTF("+++ C+ mode TCP checksum %04x\n",
-                            tcp_checksum);
-
-                        p_tcp_hdr->th_sum = tcp_checksum;
-                    }
-                    else if ((txdw0 & CP_TX_UDPCS) && ip_protocol == IP_PROTO_UDP)
-                    {
-                        DPRINTF("+++ C+ mode calculating UDP checksum for "
-                            "packet with %d bytes data\n", ip_data_len);
-
-                        ip_pseudo_header *p_udpip_hdr = (ip_pseudo_header *)data_to_checksum;
-                        p_udpip_hdr->zeros      = 0;
-                        p_udpip_hdr->ip_proto   = IP_PROTO_UDP;
-                        p_udpip_hdr->ip_payload = cpu_to_be16(ip_data_len);
-
-                        udp_header *p_udp_hdr = (udp_header *) (data_to_checksum+12);
-
-                        p_udp_hdr->uh_sum = 0;
-
-                        int udp_checksum = ip_checksum(data_to_checksum, ip_data_len + 12);
-                        DPRINTF("+++ C+ mode UDP checksum %04x\n",
-                            udp_checksum);
-
-                        p_udp_hdr->uh_sum = udp_checksum;
-                    }
-
-                    /* restore IP header */
-                    memcpy(eth_payload_data, saved_ip_header, hlen);
-                }
-            }
-        }
-
-        /* update tally counter */
-        ++s->tally_counters.TxOk;
-
-        DPRINTF("+++ C+ mode transmitting %d bytes packet\n", saved_size);
-
-        rtl8139_transfer_frame(s, saved_buffer, saved_size, 1,
-            (uint8_t *) dot1q_buffer);
-
-        /* restore card space if there was no recursion and reset offset */
-        if (!s->cplus_txbuffer)
-        {
-            s->cplus_txbuffer        = saved_buffer;
-            s->cplus_txbuffer_len    = saved_buffer_len;
-            s->cplus_txbuffer_offset = 0;
-        }
-        else
-        {
-            g_free(saved_buffer);
-        }
-    }
-    else
-    {
-        DPRINTF("+++ C+ mode transmission continue to next descriptor\n");
-    }
-
-    return 1;
-}
-
-static void rtl8139_cplus_transmit(RTL8139State *s)
-{
-    int txcount = 0;
-
-    while (rtl8139_cplus_transmit_one(s))
-    {
-        ++txcount;
-    }
-
-    /* Mark transfer completed */
-    if (!txcount)
-    {
-        DPRINTF("C+ mode : transmitter queue stalled, current TxDesc = %d\n",
-            s->currCPlusTxDesc);
-    }
-    else
-    {
-        /* update interrupt status */
-        s->IntrStatus |= TxOK;
-        rtl8139_update_irq(s);
-    }
-}
-
-static void rtl8139_transmit(RTL8139State *s)
-{
-    int descriptor = s->currTxDesc, txcount = 0;
-
-    /*while*/
-    if (rtl8139_transmit_one(s, descriptor))
-    {
-        ++s->currTxDesc;
-        s->currTxDesc %= 4;
-        ++txcount;
-    }
-
-    /* Mark transfer completed */
-    if (!txcount)
-    {
-        DPRINTF("transmitter queue stalled, current TxDesc = %d\n",
-            s->currTxDesc);
-    }
-}
-
-static void rtl8139_TxStatus_write(RTL8139State *s, uint32_t txRegOffset, uint32_t val)
-{
-
-    int descriptor = txRegOffset/4;
-
-    /* handle C+ transmit mode register configuration */
-
-    if (s->cplus_enabled)
-    {
-        DPRINTF("RTL8139C+ DTCCR write offset=0x%x val=0x%08x "
-            "descriptor=%d\n", txRegOffset, val, descriptor);
-
-        /* handle Dump Tally Counters command */
-        s->TxStatus[descriptor] = val;
-
-        if (descriptor == 0 && (val & 0x8))
-        {
-            hwaddr tc_addr = rtl8139_addr64(s->TxStatus[0] & ~0x3f, s->TxStatus[1]);
-
-            /* dump tally counters to specified memory location */
-            RTL8139TallyCounters_dma_write(s, tc_addr);
-
-            /* mark dump completed */
-            s->TxStatus[0] &= ~0x8;
-        }
-
-        return;
-    }
-
-    DPRINTF("TxStatus write offset=0x%x val=0x%08x descriptor=%d\n",
-        txRegOffset, val, descriptor);
-
-    /* mask only reserved bits */
-    val &= ~0xff00c000; /* these bits are reset on write */
-    val = SET_MASKED(val, 0x00c00000, s->TxStatus[descriptor]);
-
-    s->TxStatus[descriptor] = val;
-
-    /* attempt to start transmission */
-    rtl8139_transmit(s);
-}
-
-static uint32_t rtl8139_TxStatus_TxAddr_read(RTL8139State *s, uint32_t regs[],
-                                             uint32_t base, uint8_t addr,
-                                             int size)
-{
-    uint32_t reg = (addr - base) / 4;
-    uint32_t offset = addr & 0x3;
-    uint32_t ret = 0;
-
-    if (addr & (size - 1)) {
-        DPRINTF("not implemented read for TxStatus/TxAddr "
-                "addr=0x%x size=0x%x\n", addr, size);
-        return ret;
-    }
-
-    switch (size) {
-    case 1: /* fall through */
-    case 2: /* fall through */
-    case 4:
-        ret = (regs[reg] >> offset * 8) & (((uint64_t)1 << (size * 8)) - 1);
-        DPRINTF("TxStatus/TxAddr[%d] read addr=0x%x size=0x%x val=0x%08x\n",
-                reg, addr, size, ret);
-        break;
-    default:
-        DPRINTF("unsupported size 0x%x of TxStatus/TxAddr reading\n", size);
-        break;
-    }
-
-    return ret;
-}
-
-static uint16_t rtl8139_TSAD_read(RTL8139State *s)
-{
-    uint16_t ret = 0;
-
-    /* Simulate TSAD, it is read only anyway */
-
-    ret = ((s->TxStatus[3] & TxStatOK  )?TSAD_TOK3:0)
-         |((s->TxStatus[2] & TxStatOK  )?TSAD_TOK2:0)
-         |((s->TxStatus[1] & TxStatOK  )?TSAD_TOK1:0)
-         |((s->TxStatus[0] & TxStatOK  )?TSAD_TOK0:0)
-
-         |((s->TxStatus[3] & TxUnderrun)?TSAD_TUN3:0)
-         |((s->TxStatus[2] & TxUnderrun)?TSAD_TUN2:0)
-         |((s->TxStatus[1] & TxUnderrun)?TSAD_TUN1:0)
-         |((s->TxStatus[0] & TxUnderrun)?TSAD_TUN0:0)
-
-         |((s->TxStatus[3] & TxAborted )?TSAD_TABT3:0)
-         |((s->TxStatus[2] & TxAborted )?TSAD_TABT2:0)
-         |((s->TxStatus[1] & TxAborted )?TSAD_TABT1:0)
-         |((s->TxStatus[0] & TxAborted )?TSAD_TABT0:0)
-
-         |((s->TxStatus[3] & TxHostOwns )?TSAD_OWN3:0)
-         |((s->TxStatus[2] & TxHostOwns )?TSAD_OWN2:0)
-         |((s->TxStatus[1] & TxHostOwns )?TSAD_OWN1:0)
-         |((s->TxStatus[0] & TxHostOwns )?TSAD_OWN0:0) ;
-
-
-    DPRINTF("TSAD read val=0x%04x\n", ret);
-
-    return ret;
-}
-
-static uint16_t rtl8139_CSCR_read(RTL8139State *s)
-{
-    uint16_t ret = s->CSCR;
-
-    DPRINTF("CSCR read val=0x%04x\n", ret);
-
-    return ret;
-}
-
-static void rtl8139_TxAddr_write(RTL8139State *s, uint32_t txAddrOffset, uint32_t val)
-{
-    DPRINTF("TxAddr write offset=0x%x val=0x%08x\n", txAddrOffset, val);
-
-    s->TxAddr[txAddrOffset/4] = val;
-}
-
-static uint32_t rtl8139_TxAddr_read(RTL8139State *s, uint32_t txAddrOffset)
-{
-    uint32_t ret = s->TxAddr[txAddrOffset/4];
-
-    DPRINTF("TxAddr read offset=0x%x val=0x%08x\n", txAddrOffset, ret);
-
-    return ret;
-}
-
-static void rtl8139_RxBufPtr_write(RTL8139State *s, uint32_t val)
-{
-    DPRINTF("RxBufPtr write val=0x%04x\n", val);
-
-    /* this value is off by 16 */
-    s->RxBufPtr = MOD2(val + 0x10, s->RxBufferSize);
-
-    DPRINTF(" CAPR write: rx buffer length %d head 0x%04x read 0x%04x\n",
-        s->RxBufferSize, s->RxBufAddr, s->RxBufPtr);
-}
-
-static uint32_t rtl8139_RxBufPtr_read(RTL8139State *s)
-{
-    /* this value is off by 16 */
-    uint32_t ret = s->RxBufPtr - 0x10;
-
-    DPRINTF("RxBufPtr read val=0x%04x\n", ret);
-
-    return ret;
-}
-
-static uint32_t rtl8139_RxBufAddr_read(RTL8139State *s)
-{
-    /* this value is NOT off by 16 */
-    uint32_t ret = s->RxBufAddr;
-
-    DPRINTF("RxBufAddr read val=0x%04x\n", ret);
-
-    return ret;
-}
-
-static void rtl8139_RxBuf_write(RTL8139State *s, uint32_t val)
-{
-    DPRINTF("RxBuf write val=0x%08x\n", val);
-
-    s->RxBuf = val;
-
-    /* may need to reset rxring here */
-}
-
-static uint32_t rtl8139_RxBuf_read(RTL8139State *s)
-{
-    uint32_t ret = s->RxBuf;
-
-    DPRINTF("RxBuf read val=0x%08x\n", ret);
-
-    return ret;
-}
-
-static void rtl8139_IntrMask_write(RTL8139State *s, uint32_t val)
-{
-    DPRINTF("IntrMask write(w) val=0x%04x\n", val);
-
-    /* mask unwritable bits */
-    val = SET_MASKED(val, 0x1e00, s->IntrMask);
-
-    s->IntrMask = val;
-
-    rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock));
-    rtl8139_update_irq(s);
-
-}
-
-static uint32_t rtl8139_IntrMask_read(RTL8139State *s)
-{
-    uint32_t ret = s->IntrMask;
-
-    DPRINTF("IntrMask read(w) val=0x%04x\n", ret);
-
-    return ret;
-}
-
-static void rtl8139_IntrStatus_write(RTL8139State *s, uint32_t val)
-{
-    DPRINTF("IntrStatus write(w) val=0x%04x\n", val);
-
-#if 0
-
-    /* writing to ISR has no effect */
-
-    return;
-
-#else
-    uint16_t newStatus = s->IntrStatus & ~val;
-
-    /* mask unwritable bits */
-    newStatus = SET_MASKED(newStatus, 0x1e00, s->IntrStatus);
-
-    /* writing 1 to interrupt status register bit clears it */
-    s->IntrStatus = 0;
-    rtl8139_update_irq(s);
-
-    s->IntrStatus = newStatus;
-    /*
-     * Computing if we miss an interrupt here is not that correct but
-     * considered that we should have had already an interrupt
-     * and probably emulated is slower is better to assume this resetting was
-     * done before testing on previous rtl8139_update_irq lead to IRQ losing
-     */
-    rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock));
-    rtl8139_update_irq(s);
-
-#endif
-}
-
-static uint32_t rtl8139_IntrStatus_read(RTL8139State *s)
-{
-    rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock));
-
-    uint32_t ret = s->IntrStatus;
-
-    DPRINTF("IntrStatus read(w) val=0x%04x\n", ret);
-
-#if 0
-
-    /* reading ISR clears all interrupts */
-    s->IntrStatus = 0;
-
-    rtl8139_update_irq(s);
-
-#endif
-
-    return ret;
-}
-
-static void rtl8139_MultiIntr_write(RTL8139State *s, uint32_t val)
-{
-    DPRINTF("MultiIntr write(w) val=0x%04x\n", val);
-
-    /* mask unwritable bits */
-    val = SET_MASKED(val, 0xf000, s->MultiIntr);
-
-    s->MultiIntr = val;
-}
-
-static uint32_t rtl8139_MultiIntr_read(RTL8139State *s)
-{
-    uint32_t ret = s->MultiIntr;
-
-    DPRINTF("MultiIntr read(w) val=0x%04x\n", ret);
-
-    return ret;
-}
-
-static void rtl8139_io_writeb(void *opaque, uint8_t addr, uint32_t val)
-{
-    RTL8139State *s = opaque;
-
-    switch (addr)
-    {
-        case MAC0 ... MAC0+5:
-            s->phys[addr - MAC0] = val;
-            break;
-        case MAC0+6 ... MAC0+7:
-            /* reserved */
-            break;
-        case MAR0 ... MAR0+7:
-            s->mult[addr - MAR0] = val;
-            break;
-        case ChipCmd:
-            rtl8139_ChipCmd_write(s, val);
-            break;
-        case Cfg9346:
-            rtl8139_Cfg9346_write(s, val);
-            break;
-        case TxConfig: /* windows driver sometimes writes using byte-lenth call */
-            rtl8139_TxConfig_writeb(s, val);
-            break;
-        case Config0:
-            rtl8139_Config0_write(s, val);
-            break;
-        case Config1:
-            rtl8139_Config1_write(s, val);
-            break;
-        case Config3:
-            rtl8139_Config3_write(s, val);
-            break;
-        case Config4:
-            rtl8139_Config4_write(s, val);
-            break;
-        case Config5:
-            rtl8139_Config5_write(s, val);
-            break;
-        case MediaStatus:
-            /* ignore */
-            DPRINTF("not implemented write(b) to MediaStatus val=0x%02x\n",
-                val);
-            break;
-
-        case HltClk:
-            DPRINTF("HltClk write val=0x%08x\n", val);
-            if (val == 'R')
-            {
-                s->clock_enabled = 1;
-            }
-            else if (val == 'H')
-            {
-                s->clock_enabled = 0;
-            }
-            break;
-
-        case TxThresh:
-            DPRINTF("C+ TxThresh write(b) val=0x%02x\n", val);
-            s->TxThresh = val;
-            break;
-
-        case TxPoll:
-            DPRINTF("C+ TxPoll write(b) val=0x%02x\n", val);
-            if (val & (1 << 7))
-            {
-                DPRINTF("C+ TxPoll high priority transmission (not "
-                    "implemented)\n");
-                //rtl8139_cplus_transmit(s);
-            }
-            if (val & (1 << 6))
-            {
-                DPRINTF("C+ TxPoll normal priority transmission\n");
-                rtl8139_cplus_transmit(s);
-            }
-
-            break;
-
-        default:
-            DPRINTF("not implemented write(b) addr=0x%x val=0x%02x\n", addr,
-                val);
-            break;
-    }
-}
-
-static void rtl8139_io_writew(void *opaque, uint8_t addr, uint32_t val)
-{
-    RTL8139State *s = opaque;
-
-    switch (addr)
-    {
-        case IntrMask:
-            rtl8139_IntrMask_write(s, val);
-            break;
-
-        case IntrStatus:
-            rtl8139_IntrStatus_write(s, val);
-            break;
-
-        case MultiIntr:
-            rtl8139_MultiIntr_write(s, val);
-            break;
-
-        case RxBufPtr:
-            rtl8139_RxBufPtr_write(s, val);
-            break;
-
-        case BasicModeCtrl:
-            rtl8139_BasicModeCtrl_write(s, val);
-            break;
-        case BasicModeStatus:
-            rtl8139_BasicModeStatus_write(s, val);
-            break;
-        case NWayAdvert:
-            DPRINTF("NWayAdvert write(w) val=0x%04x\n", val);
-            s->NWayAdvert = val;
-            break;
-        case NWayLPAR:
-            DPRINTF("forbidden NWayLPAR write(w) val=0x%04x\n", val);
-            break;
-        case NWayExpansion:
-            DPRINTF("NWayExpansion write(w) val=0x%04x\n", val);
-            s->NWayExpansion = val;
-            break;
-
-        case CpCmd:
-            rtl8139_CpCmd_write(s, val);
-            break;
-
-        case IntrMitigate:
-            rtl8139_IntrMitigate_write(s, val);
-            break;
-
-        default:
-            DPRINTF("ioport write(w) addr=0x%x val=0x%04x via write(b)\n",
-                addr, val);
-
-            rtl8139_io_writeb(opaque, addr, val & 0xff);
-            rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff);
-            break;
-    }
-}
-
-static void rtl8139_set_next_tctr_time(RTL8139State *s, int64_t current_time)
-{
-    int64_t pci_time, next_time;
-    uint32_t low_pci;
-
-    DPRINTF("entered rtl8139_set_next_tctr_time\n");
-
-    if (s->TimerExpire && current_time >= s->TimerExpire) {
-        s->IntrStatus |= PCSTimeout;
-        rtl8139_update_irq(s);
-    }
-
-    /* Set QEMU timer only if needed that is
-     * - TimerInt <> 0 (we have a timer)
-     * - mask = 1 (we want an interrupt timer)
-     * - irq = 0  (irq is not already active)
-     * If any of above change we need to compute timer again
-     * Also we must check if timer is passed without QEMU timer
-     */
-    s->TimerExpire = 0;
-    if (!s->TimerInt) {
-        return;
-    }
-
-    pci_time = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY,
-                                get_ticks_per_sec());
-    low_pci = pci_time & 0xffffffff;
-    pci_time = pci_time - low_pci + s->TimerInt;
-    if (low_pci >= s->TimerInt) {
-        pci_time += 0x100000000LL;
-    }
-    next_time = s->TCTR_base + muldiv64(pci_time, get_ticks_per_sec(),
-                                                PCI_FREQUENCY);
-    s->TimerExpire = next_time;
-
-    if ((s->IntrMask & PCSTimeout) != 0 && (s->IntrStatus & PCSTimeout) == 0) {
-        qemu_mod_timer(s->timer, next_time);
-    }
-}
-
-static void rtl8139_io_writel(void *opaque, uint8_t addr, uint32_t val)
-{
-    RTL8139State *s = opaque;
-
-    switch (addr)
-    {
-        case RxMissed:
-            DPRINTF("RxMissed clearing on write\n");
-            s->RxMissed = 0;
-            break;
-
-        case TxConfig:
-            rtl8139_TxConfig_write(s, val);
-            break;
-
-        case RxConfig:
-            rtl8139_RxConfig_write(s, val);
-            break;
-
-        case TxStatus0 ... TxStatus0+4*4-1:
-            rtl8139_TxStatus_write(s, addr-TxStatus0, val);
-            break;
-
-        case TxAddr0 ... TxAddr0+4*4-1:
-            rtl8139_TxAddr_write(s, addr-TxAddr0, val);
-            break;
-
-        case RxBuf:
-            rtl8139_RxBuf_write(s, val);
-            break;
-
-        case RxRingAddrLO:
-            DPRINTF("C+ RxRing low bits write val=0x%08x\n", val);
-            s->RxRingAddrLO = val;
-            break;
-
-        case RxRingAddrHI:
-            DPRINTF("C+ RxRing high bits write val=0x%08x\n", val);
-            s->RxRingAddrHI = val;
-            break;
-
-        case Timer:
-            DPRINTF("TCTR Timer reset on write\n");
-            s->TCTR_base = qemu_get_clock_ns(vm_clock);
-            rtl8139_set_next_tctr_time(s, s->TCTR_base);
-            break;
-
-        case FlashReg:
-            DPRINTF("FlashReg TimerInt write val=0x%08x\n", val);
-            if (s->TimerInt != val) {
-                s->TimerInt = val;
-                rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock));
-            }
-            break;
-
-        default:
-            DPRINTF("ioport write(l) addr=0x%x val=0x%08x via write(b)\n",
-                addr, val);
-            rtl8139_io_writeb(opaque, addr, val & 0xff);
-            rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff);
-            rtl8139_io_writeb(opaque, addr + 2, (val >> 16) & 0xff);
-            rtl8139_io_writeb(opaque, addr + 3, (val >> 24) & 0xff);
-            break;
-    }
-}
-
-static uint32_t rtl8139_io_readb(void *opaque, uint8_t addr)
-{
-    RTL8139State *s = opaque;
-    int ret;
-
-    switch (addr)
-    {
-        case MAC0 ... MAC0+5:
-            ret = s->phys[addr - MAC0];
-            break;
-        case MAC0+6 ... MAC0+7:
-            ret = 0;
-            break;
-        case MAR0 ... MAR0+7:
-            ret = s->mult[addr - MAR0];
-            break;
-        case TxStatus0 ... TxStatus0+4*4-1:
-            ret = rtl8139_TxStatus_TxAddr_read(s, s->TxStatus, TxStatus0,
-                                               addr, 1);
-            break;
-        case ChipCmd:
-            ret = rtl8139_ChipCmd_read(s);
-            break;
-        case Cfg9346:
-            ret = rtl8139_Cfg9346_read(s);
-            break;
-        case Config0:
-            ret = rtl8139_Config0_read(s);
-            break;
-        case Config1:
-            ret = rtl8139_Config1_read(s);
-            break;
-        case Config3:
-            ret = rtl8139_Config3_read(s);
-            break;
-        case Config4:
-            ret = rtl8139_Config4_read(s);
-            break;
-        case Config5:
-            ret = rtl8139_Config5_read(s);
-            break;
-
-        case MediaStatus:
-            /* The LinkDown bit of MediaStatus is inverse with link status */
-            ret = 0xd0 | (~s->BasicModeStatus & 0x04);
-            DPRINTF("MediaStatus read 0x%x\n", ret);
-            break;
-
-        case HltClk:
-            ret = s->clock_enabled;
-            DPRINTF("HltClk read 0x%x\n", ret);
-            break;
-
-        case PCIRevisionID:
-            ret = RTL8139_PCI_REVID;
-            DPRINTF("PCI Revision ID read 0x%x\n", ret);
-            break;
-
-        case TxThresh:
-            ret = s->TxThresh;
-            DPRINTF("C+ TxThresh read(b) val=0x%02x\n", ret);
-            break;
-
-        case 0x43: /* Part of TxConfig register. Windows driver tries to read it */
-            ret = s->TxConfig >> 24;
-            DPRINTF("RTL8139C TxConfig at 0x43 read(b) val=0x%02x\n", ret);
-            break;
-
-        default:
-            DPRINTF("not implemented read(b) addr=0x%x\n", addr);
-            ret = 0;
-            break;
-    }
-
-    return ret;
-}
-
-static uint32_t rtl8139_io_readw(void *opaque, uint8_t addr)
-{
-    RTL8139State *s = opaque;
-    uint32_t ret;
-
-    switch (addr)
-    {
-        case TxAddr0 ... TxAddr0+4*4-1:
-            ret = rtl8139_TxStatus_TxAddr_read(s, s->TxAddr, TxAddr0, addr, 2);
-            break;
-        case IntrMask:
-            ret = rtl8139_IntrMask_read(s);
-            break;
-
-        case IntrStatus:
-            ret = rtl8139_IntrStatus_read(s);
-            break;
-
-        case MultiIntr:
-            ret = rtl8139_MultiIntr_read(s);
-            break;
-
-        case RxBufPtr:
-            ret = rtl8139_RxBufPtr_read(s);
-            break;
-
-        case RxBufAddr:
-            ret = rtl8139_RxBufAddr_read(s);
-            break;
-
-        case BasicModeCtrl:
-            ret = rtl8139_BasicModeCtrl_read(s);
-            break;
-        case BasicModeStatus:
-            ret = rtl8139_BasicModeStatus_read(s);
-            break;
-        case NWayAdvert:
-            ret = s->NWayAdvert;
-            DPRINTF("NWayAdvert read(w) val=0x%04x\n", ret);
-            break;
-        case NWayLPAR:
-            ret = s->NWayLPAR;
-            DPRINTF("NWayLPAR read(w) val=0x%04x\n", ret);
-            break;
-        case NWayExpansion:
-            ret = s->NWayExpansion;
-            DPRINTF("NWayExpansion read(w) val=0x%04x\n", ret);
-            break;
-
-        case CpCmd:
-            ret = rtl8139_CpCmd_read(s);
-            break;
-
-        case IntrMitigate:
-            ret = rtl8139_IntrMitigate_read(s);
-            break;
-
-        case TxSummary:
-            ret = rtl8139_TSAD_read(s);
-            break;
-
-        case CSCR:
-            ret = rtl8139_CSCR_read(s);
-            break;
-
-        default:
-            DPRINTF("ioport read(w) addr=0x%x via read(b)\n", addr);
-
-            ret  = rtl8139_io_readb(opaque, addr);
-            ret |= rtl8139_io_readb(opaque, addr + 1) << 8;
-
-            DPRINTF("ioport read(w) addr=0x%x val=0x%04x\n", addr, ret);
-            break;
-    }
-
-    return ret;
-}
-
-static uint32_t rtl8139_io_readl(void *opaque, uint8_t addr)
-{
-    RTL8139State *s = opaque;
-    uint32_t ret;
-
-    switch (addr)
-    {
-        case RxMissed:
-            ret = s->RxMissed;
-
-            DPRINTF("RxMissed read val=0x%08x\n", ret);
-            break;
-
-        case TxConfig:
-            ret = rtl8139_TxConfig_read(s);
-            break;
-
-        case RxConfig:
-            ret = rtl8139_RxConfig_read(s);
-            break;
-
-        case TxStatus0 ... TxStatus0+4*4-1:
-            ret = rtl8139_TxStatus_TxAddr_read(s, s->TxStatus, TxStatus0,
-                                               addr, 4);
-            break;
-
-        case TxAddr0 ... TxAddr0+4*4-1:
-            ret = rtl8139_TxAddr_read(s, addr-TxAddr0);
-            break;
-
-        case RxBuf:
-            ret = rtl8139_RxBuf_read(s);
-            break;
-
-        case RxRingAddrLO:
-            ret = s->RxRingAddrLO;
-            DPRINTF("C+ RxRing low bits read val=0x%08x\n", ret);
-            break;
-
-        case RxRingAddrHI:
-            ret = s->RxRingAddrHI;
-            DPRINTF("C+ RxRing high bits read val=0x%08x\n", ret);
-            break;
-
-        case Timer:
-            ret = muldiv64(qemu_get_clock_ns(vm_clock) - s->TCTR_base,
-                           PCI_FREQUENCY, get_ticks_per_sec());
-            DPRINTF("TCTR Timer read val=0x%08x\n", ret);
-            break;
-
-        case FlashReg:
-            ret = s->TimerInt;
-            DPRINTF("FlashReg TimerInt read val=0x%08x\n", ret);
-            break;
-
-        default:
-            DPRINTF("ioport read(l) addr=0x%x via read(b)\n", addr);
-
-            ret  = rtl8139_io_readb(opaque, addr);
-            ret |= rtl8139_io_readb(opaque, addr + 1) << 8;
-            ret |= rtl8139_io_readb(opaque, addr + 2) << 16;
-            ret |= rtl8139_io_readb(opaque, addr + 3) << 24;
-
-            DPRINTF("read(l) addr=0x%x val=%08x\n", addr, ret);
-            break;
-    }
-
-    return ret;
-}
-
-/* */
-
-static void rtl8139_mmio_writeb(void *opaque, hwaddr addr, uint32_t val)
-{
-    rtl8139_io_writeb(opaque, addr & 0xFF, val);
-}
-
-static void rtl8139_mmio_writew(void *opaque, hwaddr addr, uint32_t val)
-{
-    rtl8139_io_writew(opaque, addr & 0xFF, val);
-}
-
-static void rtl8139_mmio_writel(void *opaque, hwaddr addr, uint32_t val)
-{
-    rtl8139_io_writel(opaque, addr & 0xFF, val);
-}
-
-static uint32_t rtl8139_mmio_readb(void *opaque, hwaddr addr)
-{
-    return rtl8139_io_readb(opaque, addr & 0xFF);
-}
-
-static uint32_t rtl8139_mmio_readw(void *opaque, hwaddr addr)
-{
-    uint32_t val = rtl8139_io_readw(opaque, addr & 0xFF);
-    return val;
-}
-
-static uint32_t rtl8139_mmio_readl(void *opaque, hwaddr addr)
-{
-    uint32_t val = rtl8139_io_readl(opaque, addr & 0xFF);
-    return val;
-}
-
-static int rtl8139_post_load(void *opaque, int version_id)
-{
-    RTL8139State* s = opaque;
-    rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock));
-    if (version_id < 4) {
-        s->cplus_enabled = s->CpCmd != 0;
-    }
-
-    /* nc.link_down can't be migrated, so infer link_down according
-     * to link status bit in BasicModeStatus */
-    qemu_get_queue(s->nic)->link_down = (s->BasicModeStatus & 0x04) == 0;
-
-    return 0;
-}
-
-static bool rtl8139_hotplug_ready_needed(void *opaque)
-{
-    return qdev_machine_modified();
-}
-
-static const VMStateDescription vmstate_rtl8139_hotplug_ready ={
-    .name = "rtl8139/hotplug_ready",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField []) {
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void rtl8139_pre_save(void *opaque)
-{
-    RTL8139State* s = opaque;
-    int64_t current_time = qemu_get_clock_ns(vm_clock);
-
-    /* set IntrStatus correctly */
-    rtl8139_set_next_tctr_time(s, current_time);
-    s->TCTR = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY,
-                       get_ticks_per_sec());
-    s->rtl8139_mmio_io_addr_dummy = 0;
-}
-
-static const VMStateDescription vmstate_rtl8139 = {
-    .name = "rtl8139",
-    .version_id = 4,
-    .minimum_version_id = 3,
-    .minimum_version_id_old = 3,
-    .post_load = rtl8139_post_load,
-    .pre_save  = rtl8139_pre_save,
-    .fields      = (VMStateField []) {
-        VMSTATE_PCI_DEVICE(dev, RTL8139State),
-        VMSTATE_PARTIAL_BUFFER(phys, RTL8139State, 6),
-        VMSTATE_BUFFER(mult, RTL8139State),
-        VMSTATE_UINT32_ARRAY(TxStatus, RTL8139State, 4),
-        VMSTATE_UINT32_ARRAY(TxAddr, RTL8139State, 4),
-
-        VMSTATE_UINT32(RxBuf, RTL8139State),
-        VMSTATE_UINT32(RxBufferSize, RTL8139State),
-        VMSTATE_UINT32(RxBufPtr, RTL8139State),
-        VMSTATE_UINT32(RxBufAddr, RTL8139State),
-
-        VMSTATE_UINT16(IntrStatus, RTL8139State),
-        VMSTATE_UINT16(IntrMask, RTL8139State),
-
-        VMSTATE_UINT32(TxConfig, RTL8139State),
-        VMSTATE_UINT32(RxConfig, RTL8139State),
-        VMSTATE_UINT32(RxMissed, RTL8139State),
-        VMSTATE_UINT16(CSCR, RTL8139State),
-
-        VMSTATE_UINT8(Cfg9346, RTL8139State),
-        VMSTATE_UINT8(Config0, RTL8139State),
-        VMSTATE_UINT8(Config1, RTL8139State),
-        VMSTATE_UINT8(Config3, RTL8139State),
-        VMSTATE_UINT8(Config4, RTL8139State),
-        VMSTATE_UINT8(Config5, RTL8139State),
-
-        VMSTATE_UINT8(clock_enabled, RTL8139State),
-        VMSTATE_UINT8(bChipCmdState, RTL8139State),
-
-        VMSTATE_UINT16(MultiIntr, RTL8139State),
-
-        VMSTATE_UINT16(BasicModeCtrl, RTL8139State),
-        VMSTATE_UINT16(BasicModeStatus, RTL8139State),
-        VMSTATE_UINT16(NWayAdvert, RTL8139State),
-        VMSTATE_UINT16(NWayLPAR, RTL8139State),
-        VMSTATE_UINT16(NWayExpansion, RTL8139State),
-
-        VMSTATE_UINT16(CpCmd, RTL8139State),
-        VMSTATE_UINT8(TxThresh, RTL8139State),
-
-        VMSTATE_UNUSED(4),
-        VMSTATE_MACADDR(conf.macaddr, RTL8139State),
-        VMSTATE_INT32(rtl8139_mmio_io_addr_dummy, RTL8139State),
-
-        VMSTATE_UINT32(currTxDesc, RTL8139State),
-        VMSTATE_UINT32(currCPlusRxDesc, RTL8139State),
-        VMSTATE_UINT32(currCPlusTxDesc, RTL8139State),
-        VMSTATE_UINT32(RxRingAddrLO, RTL8139State),
-        VMSTATE_UINT32(RxRingAddrHI, RTL8139State),
-
-        VMSTATE_UINT16_ARRAY(eeprom.contents, RTL8139State, EEPROM_9346_SIZE),
-        VMSTATE_INT32(eeprom.mode, RTL8139State),
-        VMSTATE_UINT32(eeprom.tick, RTL8139State),
-        VMSTATE_UINT8(eeprom.address, RTL8139State),
-        VMSTATE_UINT16(eeprom.input, RTL8139State),
-        VMSTATE_UINT16(eeprom.output, RTL8139State),
-
-        VMSTATE_UINT8(eeprom.eecs, RTL8139State),
-        VMSTATE_UINT8(eeprom.eesk, RTL8139State),
-        VMSTATE_UINT8(eeprom.eedi, RTL8139State),
-        VMSTATE_UINT8(eeprom.eedo, RTL8139State),
-
-        VMSTATE_UINT32(TCTR, RTL8139State),
-        VMSTATE_UINT32(TimerInt, RTL8139State),
-        VMSTATE_INT64(TCTR_base, RTL8139State),
-
-        VMSTATE_STRUCT(tally_counters, RTL8139State, 0,
-                       vmstate_tally_counters, RTL8139TallyCounters),
-
-        VMSTATE_UINT32_V(cplus_enabled, RTL8139State, 4),
-        VMSTATE_END_OF_LIST()
-    },
-    .subsections = (VMStateSubsection []) {
-        {
-            .vmsd = &vmstate_rtl8139_hotplug_ready,
-            .needed = rtl8139_hotplug_ready_needed,
-        }, {
-            /* empty */
-        }
-    }
-};
-
-/***********************************************************/
-/* PCI RTL8139 definitions */
-
-static void rtl8139_ioport_write(void *opaque, hwaddr addr,
-                                 uint64_t val, unsigned size)
-{
-    switch (size) {
-    case 1:
-        rtl8139_io_writeb(opaque, addr, val);
-        break;
-    case 2:
-        rtl8139_io_writew(opaque, addr, val);
-        break;
-    case 4:
-        rtl8139_io_writel(opaque, addr, val);
-        break;
-    }
-}
-
-static uint64_t rtl8139_ioport_read(void *opaque, hwaddr addr,
-                                    unsigned size)
-{
-    switch (size) {
-    case 1:
-        return rtl8139_io_readb(opaque, addr);
-    case 2:
-        return rtl8139_io_readw(opaque, addr);
-    case 4:
-        return rtl8139_io_readl(opaque, addr);
-    }
-
-    return -1;
-}
-
-static const MemoryRegionOps rtl8139_io_ops = {
-    .read = rtl8139_ioport_read,
-    .write = rtl8139_ioport_write,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 4,
-    },
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static const MemoryRegionOps rtl8139_mmio_ops = {
-    .old_mmio = {
-        .read = {
-            rtl8139_mmio_readb,
-            rtl8139_mmio_readw,
-            rtl8139_mmio_readl,
-        },
-        .write = {
-            rtl8139_mmio_writeb,
-            rtl8139_mmio_writew,
-            rtl8139_mmio_writel,
-        },
-    },
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void rtl8139_timer(void *opaque)
-{
-    RTL8139State *s = opaque;
-
-    if (!s->clock_enabled)
-    {
-        DPRINTF(">>> timer: clock is not running\n");
-        return;
-    }
-
-    s->IntrStatus |= PCSTimeout;
-    rtl8139_update_irq(s);
-    rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock));
-}
-
-static void rtl8139_cleanup(NetClientState *nc)
-{
-    RTL8139State *s = qemu_get_nic_opaque(nc);
-
-    s->nic = NULL;
-}
-
-static void pci_rtl8139_uninit(PCIDevice *dev)
-{
-    RTL8139State *s = DO_UPCAST(RTL8139State, dev, dev);
-
-    memory_region_destroy(&s->bar_io);
-    memory_region_destroy(&s->bar_mem);
-    if (s->cplus_txbuffer) {
-        g_free(s->cplus_txbuffer);
-        s->cplus_txbuffer = NULL;
-    }
-    qemu_del_timer(s->timer);
-    qemu_free_timer(s->timer);
-    qemu_del_nic(s->nic);
-}
-
-static void rtl8139_set_link_status(NetClientState *nc)
-{
-    RTL8139State *s = qemu_get_nic_opaque(nc);
-
-    if (nc->link_down) {
-        s->BasicModeStatus &= ~0x04;
-    } else {
-        s->BasicModeStatus |= 0x04;
-    }
-
-    s->IntrStatus |= RxUnderrun;
-    rtl8139_update_irq(s);
-}
-
-static NetClientInfo net_rtl8139_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
-    .size = sizeof(NICState),
-    .can_receive = rtl8139_can_receive,
-    .receive = rtl8139_receive,
-    .cleanup = rtl8139_cleanup,
-    .link_status_changed = rtl8139_set_link_status,
-};
-
-static int pci_rtl8139_init(PCIDevice *dev)
-{
-    RTL8139State * s = DO_UPCAST(RTL8139State, dev, dev);
-    uint8_t *pci_conf;
-
-    pci_conf = s->dev.config;
-    pci_conf[PCI_INTERRUPT_PIN] = 1;    /* interrupt pin A */
-    /* TODO: start of capability list, but no capability
-     * list bit in status register, and offset 0xdc seems unused. */
-    pci_conf[PCI_CAPABILITY_LIST] = 0xdc;
-
-    memory_region_init_io(&s->bar_io, &rtl8139_io_ops, s, "rtl8139", 0x100);
-    memory_region_init_io(&s->bar_mem, &rtl8139_mmio_ops, s, "rtl8139", 0x100);
-    pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->bar_io);
-    pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar_mem);
-
-    qemu_macaddr_default_if_unset(&s->conf.macaddr);
-
-    /* prepare eeprom */
-    s->eeprom.contents[0] = 0x8129;
-#if 1
-    /* PCI vendor and device ID should be mirrored here */
-    s->eeprom.contents[1] = PCI_VENDOR_ID_REALTEK;
-    s->eeprom.contents[2] = PCI_DEVICE_ID_REALTEK_8139;
-#endif
-    s->eeprom.contents[7] = s->conf.macaddr.a[0] | s->conf.macaddr.a[1] << 8;
-    s->eeprom.contents[8] = s->conf.macaddr.a[2] | s->conf.macaddr.a[3] << 8;
-    s->eeprom.contents[9] = s->conf.macaddr.a[4] | s->conf.macaddr.a[5] << 8;
-
-    s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf,
-                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
-    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
-
-    s->cplus_txbuffer = NULL;
-    s->cplus_txbuffer_len = 0;
-    s->cplus_txbuffer_offset = 0;
-
-    s->TimerExpire = 0;
-    s->timer = qemu_new_timer_ns(vm_clock, rtl8139_timer, s);
-    rtl8139_set_next_tctr_time(s, qemu_get_clock_ns(vm_clock));
-
-    add_boot_device_path(s->conf.bootindex, &dev->qdev, "/ethernet-phy@0");
-
-    return 0;
-}
-
-static Property rtl8139_properties[] = {
-    DEFINE_NIC_PROPERTIES(RTL8139State, conf),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void rtl8139_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = pci_rtl8139_init;
-    k->exit = pci_rtl8139_uninit;
-    k->romfile = "efi-rtl8139.rom";
-    k->vendor_id = PCI_VENDOR_ID_REALTEK;
-    k->device_id = PCI_DEVICE_ID_REALTEK_8139;
-    k->revision = RTL8139_PCI_REVID; /* >=0x20 is for 8139C+ */
-    k->class_id = PCI_CLASS_NETWORK_ETHERNET;
-    dc->reset = rtl8139_reset;
-    dc->vmsd = &vmstate_rtl8139;
-    dc->props = rtl8139_properties;
-}
-
-static const TypeInfo rtl8139_info = {
-    .name          = "rtl8139",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(RTL8139State),
-    .class_init    = rtl8139_class_init,
-};
-
-static void rtl8139_register_types(void)
-{
-    type_register_static(&rtl8139_info);
-}
-
-type_init(rtl8139_register_types)
diff --git a/hw/sb16.c b/hw/sb16.c
deleted file mode 100644 (file)
index 783b6b4..0000000
--- a/hw/sb16.c
+++ /dev/null
@@ -1,1424 +0,0 @@
-/*
- * QEMU Soundblaster 16 emulation
- *
- * Copyright (c) 2003-2005 Vassili Karpov (malc)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/audio/audio.h"
-#include "audio/audio.h"
-#include "hw/isa/isa.h"
-#include "hw/qdev.h"
-#include "qemu/timer.h"
-#include "qemu/host-utils.h"
-
-#define dolog(...) AUD_log ("sb16", __VA_ARGS__)
-
-/* #define DEBUG */
-/* #define DEBUG_SB16_MOST */
-
-#ifdef DEBUG
-#define ldebug(...) dolog (__VA_ARGS__)
-#else
-#define ldebug(...)
-#endif
-
-#define IO_READ_PROTO(name)                             \
-    uint32_t name (void *opaque, uint32_t nport)
-#define IO_WRITE_PROTO(name)                                    \
-    void name (void *opaque, uint32_t nport, uint32_t val)
-
-static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
-
-typedef struct SB16State {
-    ISADevice dev;
-    QEMUSoundCard card;
-    qemu_irq pic;
-    uint32_t irq;
-    uint32_t dma;
-    uint32_t hdma;
-    uint32_t port;
-    uint32_t ver;
-
-    int in_index;
-    int out_data_len;
-    int fmt_stereo;
-    int fmt_signed;
-    int fmt_bits;
-    audfmt_e fmt;
-    int dma_auto;
-    int block_size;
-    int fifo;
-    int freq;
-    int time_const;
-    int speaker;
-    int needed_bytes;
-    int cmd;
-    int use_hdma;
-    int highspeed;
-    int can_write;
-
-    int v2x6;
-
-    uint8_t csp_param;
-    uint8_t csp_value;
-    uint8_t csp_mode;
-    uint8_t csp_regs[256];
-    uint8_t csp_index;
-    uint8_t csp_reg83[4];
-    int csp_reg83r;
-    int csp_reg83w;
-
-    uint8_t in2_data[10];
-    uint8_t out_data[50];
-    uint8_t test_reg;
-    uint8_t last_read_byte;
-    int nzero;
-
-    int left_till_irq;
-
-    int dma_running;
-    int bytes_per_second;
-    int align;
-    int audio_free;
-    SWVoiceOut *voice;
-
-    QEMUTimer *aux_ts;
-    /* mixer state */
-    int mixer_nreg;
-    uint8_t mixer_regs[256];
-} SB16State;
-
-static void SB_audio_callback (void *opaque, int free);
-
-static int magic_of_irq (int irq)
-{
-    switch (irq) {
-    case 5:
-        return 2;
-    case 7:
-        return 4;
-    case 9:
-        return 1;
-    case 10:
-        return 8;
-    default:
-        dolog ("bad irq %d\n", irq);
-        return 2;
-    }
-}
-
-static int irq_of_magic (int magic)
-{
-    switch (magic) {
-    case 1:
-        return 9;
-    case 2:
-        return 5;
-    case 4:
-        return 7;
-    case 8:
-        return 10;
-    default:
-        dolog ("bad irq magic %d\n", magic);
-        return -1;
-    }
-}
-
-#if 0
-static void log_dsp (SB16State *dsp)
-{
-    ldebug ("%s:%s:%d:%s:dmasize=%d:freq=%d:const=%d:speaker=%d\n",
-            dsp->fmt_stereo ? "Stereo" : "Mono",
-            dsp->fmt_signed ? "Signed" : "Unsigned",
-            dsp->fmt_bits,
-            dsp->dma_auto ? "Auto" : "Single",
-            dsp->block_size,
-            dsp->freq,
-            dsp->time_const,
-            dsp->speaker);
-}
-#endif
-
-static void speaker (SB16State *s, int on)
-{
-    s->speaker = on;
-    /* AUD_enable (s->voice, on); */
-}
-
-static void control (SB16State *s, int hold)
-{
-    int dma = s->use_hdma ? s->hdma : s->dma;
-    s->dma_running = hold;
-
-    ldebug ("hold %d high %d dma %d\n", hold, s->use_hdma, dma);
-
-    if (hold) {
-        DMA_hold_DREQ (dma);
-        AUD_set_active_out (s->voice, 1);
-    }
-    else {
-        DMA_release_DREQ (dma);
-        AUD_set_active_out (s->voice, 0);
-    }
-}
-
-static void aux_timer (void *opaque)
-{
-    SB16State *s = opaque;
-    s->can_write = 1;
-    qemu_irq_raise (s->pic);
-}
-
-#define DMA8_AUTO 1
-#define DMA8_HIGH 2
-
-static void continue_dma8 (SB16State *s)
-{
-    if (s->freq > 0) {
-        struct audsettings as;
-
-        s->audio_free = 0;
-
-        as.freq = s->freq;
-        as.nchannels = 1 << s->fmt_stereo;
-        as.fmt = s->fmt;
-        as.endianness = 0;
-
-        s->voice = AUD_open_out (
-            &s->card,
-            s->voice,
-            "sb16",
-            s,
-            SB_audio_callback,
-            &as
-            );
-    }
-
-    control (s, 1);
-}
-
-static void dma_cmd8 (SB16State *s, int mask, int dma_len)
-{
-    s->fmt = AUD_FMT_U8;
-    s->use_hdma = 0;
-    s->fmt_bits = 8;
-    s->fmt_signed = 0;
-    s->fmt_stereo = (s->mixer_regs[0x0e] & 2) != 0;
-    if (-1 == s->time_const) {
-        if (s->freq <= 0)
-            s->freq = 11025;
-    }
-    else {
-        int tmp = (256 - s->time_const);
-        s->freq = (1000000 + (tmp / 2)) / tmp;
-    }
-
-    if (dma_len != -1) {
-        s->block_size = dma_len << s->fmt_stereo;
-    }
-    else {
-        /* This is apparently the only way to make both Act1/PL
-           and SecondReality/FC work
-
-           Act1 sets block size via command 0x48 and it's an odd number
-           SR does the same with even number
-           Both use stereo, and Creatives own documentation states that
-           0x48 sets block size in bytes less one.. go figure */
-        s->block_size &= ~s->fmt_stereo;
-    }
-
-    s->freq >>= s->fmt_stereo;
-    s->left_till_irq = s->block_size;
-    s->bytes_per_second = (s->freq << s->fmt_stereo);
-    /* s->highspeed = (mask & DMA8_HIGH) != 0; */
-    s->dma_auto = (mask & DMA8_AUTO) != 0;
-    s->align = (1 << s->fmt_stereo) - 1;
-
-    if (s->block_size & s->align) {
-        dolog ("warning: misaligned block size %d, alignment %d\n",
-               s->block_size, s->align + 1);
-    }
-
-    ldebug ("freq %d, stereo %d, sign %d, bits %d, "
-            "dma %d, auto %d, fifo %d, high %d\n",
-            s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
-            s->block_size, s->dma_auto, s->fifo, s->highspeed);
-
-    continue_dma8 (s);
-    speaker (s, 1);
-}
-
-static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len)
-{
-    s->use_hdma = cmd < 0xc0;
-    s->fifo = (cmd >> 1) & 1;
-    s->dma_auto = (cmd >> 2) & 1;
-    s->fmt_signed = (d0 >> 4) & 1;
-    s->fmt_stereo = (d0 >> 5) & 1;
-
-    switch (cmd >> 4) {
-    case 11:
-        s->fmt_bits = 16;
-        break;
-
-    case 12:
-        s->fmt_bits = 8;
-        break;
-    }
-
-    if (-1 != s->time_const) {
-#if 1
-        int tmp = 256 - s->time_const;
-        s->freq = (1000000 + (tmp / 2)) / tmp;
-#else
-        /* s->freq = 1000000 / ((255 - s->time_const) << s->fmt_stereo); */
-        s->freq = 1000000 / ((255 - s->time_const));
-#endif
-        s->time_const = -1;
-    }
-
-    s->block_size = dma_len + 1;
-    s->block_size <<= (s->fmt_bits == 16);
-    if (!s->dma_auto) {
-        /* It is clear that for DOOM and auto-init this value
-           shouldn't take stereo into account, while Miles Sound Systems
-           setsound.exe with single transfer mode wouldn't work without it
-           wonders of SB16 yet again */
-        s->block_size <<= s->fmt_stereo;
-    }
-
-    ldebug ("freq %d, stereo %d, sign %d, bits %d, "
-            "dma %d, auto %d, fifo %d, high %d\n",
-            s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
-            s->block_size, s->dma_auto, s->fifo, s->highspeed);
-
-    if (16 == s->fmt_bits) {
-        if (s->fmt_signed) {
-            s->fmt = AUD_FMT_S16;
-        }
-        else {
-            s->fmt = AUD_FMT_U16;
-        }
-    }
-    else {
-        if (s->fmt_signed) {
-            s->fmt = AUD_FMT_S8;
-        }
-        else {
-            s->fmt = AUD_FMT_U8;
-        }
-    }
-
-    s->left_till_irq = s->block_size;
-
-    s->bytes_per_second = (s->freq << s->fmt_stereo) << (s->fmt_bits == 16);
-    s->highspeed = 0;
-    s->align = (1 << (s->fmt_stereo + (s->fmt_bits == 16))) - 1;
-    if (s->block_size & s->align) {
-        dolog ("warning: misaligned block size %d, alignment %d\n",
-               s->block_size, s->align + 1);
-    }
-
-    if (s->freq) {
-        struct audsettings as;
-
-        s->audio_free = 0;
-
-        as.freq = s->freq;
-        as.nchannels = 1 << s->fmt_stereo;
-        as.fmt = s->fmt;
-        as.endianness = 0;
-
-        s->voice = AUD_open_out (
-            &s->card,
-            s->voice,
-            "sb16",
-            s,
-            SB_audio_callback,
-            &as
-            );
-    }
-
-    control (s, 1);
-    speaker (s, 1);
-}
-
-static inline void dsp_out_data (SB16State *s, uint8_t val)
-{
-    ldebug ("outdata %#x\n", val);
-    if ((size_t) s->out_data_len < sizeof (s->out_data)) {
-        s->out_data[s->out_data_len++] = val;
-    }
-}
-
-static inline uint8_t dsp_get_data (SB16State *s)
-{
-    if (s->in_index) {
-        return s->in2_data[--s->in_index];
-    }
-    else {
-        dolog ("buffer underflow\n");
-        return 0;
-    }
-}
-
-static void command (SB16State *s, uint8_t cmd)
-{
-    ldebug ("command %#x\n", cmd);
-
-    if (cmd > 0xaf && cmd < 0xd0) {
-        if (cmd & 8) {
-            dolog ("ADC not yet supported (command %#x)\n", cmd);
-        }
-
-        switch (cmd >> 4) {
-        case 11:
-        case 12:
-            break;
-        default:
-            dolog ("%#x wrong bits\n", cmd);
-        }
-        s->needed_bytes = 3;
-    }
-    else {
-        s->needed_bytes = 0;
-
-        switch (cmd) {
-        case 0x03:
-            dsp_out_data (s, 0x10); /* s->csp_param); */
-            goto warn;
-
-        case 0x04:
-            s->needed_bytes = 1;
-            goto warn;
-
-        case 0x05:
-            s->needed_bytes = 2;
-            goto warn;
-
-        case 0x08:
-            /* __asm__ ("int3"); */
-            goto warn;
-
-        case 0x0e:
-            s->needed_bytes = 2;
-            goto warn;
-
-        case 0x09:
-            dsp_out_data (s, 0xf8);
-            goto warn;
-
-        case 0x0f:
-            s->needed_bytes = 1;
-            goto warn;
-
-        case 0x10:
-            s->needed_bytes = 1;
-            goto warn;
-
-        case 0x14:
-            s->needed_bytes = 2;
-            s->block_size = 0;
-            break;
-
-        case 0x1c:              /* Auto-Initialize DMA DAC, 8-bit */
-            dma_cmd8 (s, DMA8_AUTO, -1);
-            break;
-
-        case 0x20:              /* Direct ADC, Juice/PL */
-            dsp_out_data (s, 0xff);
-            goto warn;
-
-        case 0x35:
-            dolog ("0x35 - MIDI command not implemented\n");
-            break;
-
-        case 0x40:
-            s->freq = -1;
-            s->time_const = -1;
-            s->needed_bytes = 1;
-            break;
-
-        case 0x41:
-            s->freq = -1;
-            s->time_const = -1;
-            s->needed_bytes = 2;
-            break;
-
-        case 0x42:
-            s->freq = -1;
-            s->time_const = -1;
-            s->needed_bytes = 2;
-            goto warn;
-
-        case 0x45:
-            dsp_out_data (s, 0xaa);
-            goto warn;
-
-        case 0x47:                /* Continue Auto-Initialize DMA 16bit */
-            break;
-
-        case 0x48:
-            s->needed_bytes = 2;
-            break;
-
-        case 0x74:
-            s->needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */
-            dolog ("0x75 - DMA DAC, 4-bit ADPCM not implemented\n");
-            break;
-
-        case 0x75:              /* DMA DAC, 4-bit ADPCM Reference */
-            s->needed_bytes = 2;
-            dolog ("0x74 - DMA DAC, 4-bit ADPCM Reference not implemented\n");
-            break;
-
-        case 0x76:              /* DMA DAC, 2.6-bit ADPCM */
-            s->needed_bytes = 2;
-            dolog ("0x74 - DMA DAC, 2.6-bit ADPCM not implemented\n");
-            break;
-
-        case 0x77:              /* DMA DAC, 2.6-bit ADPCM Reference */
-            s->needed_bytes = 2;
-            dolog ("0x74 - DMA DAC, 2.6-bit ADPCM Reference not implemented\n");
-            break;
-
-        case 0x7d:
-            dolog ("0x7d - Autio-Initialize DMA DAC, 4-bit ADPCM Reference\n");
-            dolog ("not implemented\n");
-            break;
-
-        case 0x7f:
-            dolog (
-                "0x7d - Autio-Initialize DMA DAC, 2.6-bit ADPCM Reference\n"
-                );
-            dolog ("not implemented\n");
-            break;
-
-        case 0x80:
-            s->needed_bytes = 2;
-            break;
-
-        case 0x90:
-        case 0x91:
-            dma_cmd8 (s, ((cmd & 1) == 0) | DMA8_HIGH, -1);
-            break;
-
-        case 0xd0:              /* halt DMA operation. 8bit */
-            control (s, 0);
-            break;
-
-        case 0xd1:              /* speaker on */
-            speaker (s, 1);
-            break;
-
-        case 0xd3:              /* speaker off */
-            speaker (s, 0);
-            break;
-
-        case 0xd4:              /* continue DMA operation. 8bit */
-            /* KQ6 (or maybe Sierras audblst.drv in general) resets
-               the frequency between halt/continue */
-            continue_dma8 (s);
-            break;
-
-        case 0xd5:              /* halt DMA operation. 16bit */
-            control (s, 0);
-            break;
-
-        case 0xd6:              /* continue DMA operation. 16bit */
-            control (s, 1);
-            break;
-
-        case 0xd9:              /* exit auto-init DMA after this block. 16bit */
-            s->dma_auto = 0;
-            break;
-
-        case 0xda:              /* exit auto-init DMA after this block. 8bit */
-            s->dma_auto = 0;
-            break;
-
-        case 0xe0:              /* DSP identification */
-            s->needed_bytes = 1;
-            break;
-
-        case 0xe1:
-            dsp_out_data (s, s->ver & 0xff);
-            dsp_out_data (s, s->ver >> 8);
-            break;
-
-        case 0xe2:
-            s->needed_bytes = 1;
-            goto warn;
-
-        case 0xe3:
-            {
-                int i;
-                for (i = sizeof (e3) - 1; i >= 0; --i)
-                    dsp_out_data (s, e3[i]);
-            }
-            break;
-
-        case 0xe4:              /* write test reg */
-            s->needed_bytes = 1;
-            break;
-
-        case 0xe7:
-            dolog ("Attempt to probe for ESS (0xe7)?\n");
-            break;
-
-        case 0xe8:              /* read test reg */
-            dsp_out_data (s, s->test_reg);
-            break;
-
-        case 0xf2:
-        case 0xf3:
-            dsp_out_data (s, 0xaa);
-            s->mixer_regs[0x82] |= (cmd == 0xf2) ? 1 : 2;
-            qemu_irq_raise (s->pic);
-            break;
-
-        case 0xf9:
-            s->needed_bytes = 1;
-            goto warn;
-
-        case 0xfa:
-            dsp_out_data (s, 0);
-            goto warn;
-
-        case 0xfc:              /* FIXME */
-            dsp_out_data (s, 0);
-            goto warn;
-
-        default:
-            dolog ("Unrecognized command %#x\n", cmd);
-            break;
-        }
-    }
-
-    if (!s->needed_bytes) {
-        ldebug ("\n");
-    }
-
- exit:
-    if (!s->needed_bytes) {
-        s->cmd = -1;
-    }
-    else {
-        s->cmd = cmd;
-    }
-    return;
-
- warn:
-    dolog ("warning: command %#x,%d is not truly understood yet\n",
-           cmd, s->needed_bytes);
-    goto exit;
-
-}
-
-static uint16_t dsp_get_lohi (SB16State *s)
-{
-    uint8_t hi = dsp_get_data (s);
-    uint8_t lo = dsp_get_data (s);
-    return (hi << 8) | lo;
-}
-
-static uint16_t dsp_get_hilo (SB16State *s)
-{
-    uint8_t lo = dsp_get_data (s);
-    uint8_t hi = dsp_get_data (s);
-    return (hi << 8) | lo;
-}
-
-static void complete (SB16State *s)
-{
-    int d0, d1, d2;
-    ldebug ("complete command %#x, in_index %d, needed_bytes %d\n",
-            s->cmd, s->in_index, s->needed_bytes);
-
-    if (s->cmd > 0xaf && s->cmd < 0xd0) {
-        d2 = dsp_get_data (s);
-        d1 = dsp_get_data (s);
-        d0 = dsp_get_data (s);
-
-        if (s->cmd & 8) {
-            dolog ("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n",
-                   s->cmd, d0, d1, d2);
-        }
-        else {
-            ldebug ("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n",
-                    s->cmd, d0, d1, d2);
-            dma_cmd (s, s->cmd, d0, d1 + (d2 << 8));
-        }
-    }
-    else {
-        switch (s->cmd) {
-        case 0x04:
-            s->csp_mode = dsp_get_data (s);
-            s->csp_reg83r = 0;
-            s->csp_reg83w = 0;
-            ldebug ("CSP command 0x04: mode=%#x\n", s->csp_mode);
-            break;
-
-        case 0x05:
-            s->csp_param = dsp_get_data (s);
-            s->csp_value = dsp_get_data (s);
-            ldebug ("CSP command 0x05: param=%#x value=%#x\n",
-                    s->csp_param,
-                    s->csp_value);
-            break;
-
-        case 0x0e:
-            d0 = dsp_get_data (s);
-            d1 = dsp_get_data (s);
-            ldebug ("write CSP register %d <- %#x\n", d1, d0);
-            if (d1 == 0x83) {
-                ldebug ("0x83[%d] <- %#x\n", s->csp_reg83r, d0);
-                s->csp_reg83[s->csp_reg83r % 4] = d0;
-                s->csp_reg83r += 1;
-            }
-            else {
-                s->csp_regs[d1] = d0;
-            }
-            break;
-
-        case 0x0f:
-            d0 = dsp_get_data (s);
-            ldebug ("read CSP register %#x -> %#x, mode=%#x\n",
-                    d0, s->csp_regs[d0], s->csp_mode);
-            if (d0 == 0x83) {
-                ldebug ("0x83[%d] -> %#x\n",
-                        s->csp_reg83w,
-                        s->csp_reg83[s->csp_reg83w % 4]);
-                dsp_out_data (s, s->csp_reg83[s->csp_reg83w % 4]);
-                s->csp_reg83w += 1;
-            }
-            else {
-                dsp_out_data (s, s->csp_regs[d0]);
-            }
-            break;
-
-        case 0x10:
-            d0 = dsp_get_data (s);
-            dolog ("cmd 0x10 d0=%#x\n", d0);
-            break;
-
-        case 0x14:
-            dma_cmd8 (s, 0, dsp_get_lohi (s) + 1);
-            break;
-
-        case 0x40:
-            s->time_const = dsp_get_data (s);
-            ldebug ("set time const %d\n", s->time_const);
-            break;
-
-        case 0x42:              /* FT2 sets output freq with this, go figure */
-#if 0
-            dolog ("cmd 0x42 might not do what it think it should\n");
-#endif
-        case 0x41:
-            s->freq = dsp_get_hilo (s);
-            ldebug ("set freq %d\n", s->freq);
-            break;
-
-        case 0x48:
-            s->block_size = dsp_get_lohi (s) + 1;
-            ldebug ("set dma block len %d\n", s->block_size);
-            break;
-
-        case 0x74:
-        case 0x75:
-        case 0x76:
-        case 0x77:
-            /* ADPCM stuff, ignore */
-            break;
-
-        case 0x80:
-            {
-                int freq, samples, bytes;
-                int64_t ticks;
-
-                freq = s->freq > 0 ? s->freq : 11025;
-                samples = dsp_get_lohi (s) + 1;
-                bytes = samples << s->fmt_stereo << (s->fmt_bits == 16);
-                ticks = muldiv64 (bytes, get_ticks_per_sec (), freq);
-                if (ticks < get_ticks_per_sec () / 1024) {
-                    qemu_irq_raise (s->pic);
-                }
-                else {
-                    if (s->aux_ts) {
-                        qemu_mod_timer (
-                            s->aux_ts,
-                            qemu_get_clock_ns (vm_clock) + ticks
-                            );
-                    }
-                }
-                ldebug ("mix silence %d %d %" PRId64 "\n", samples, bytes, ticks);
-            }
-            break;
-
-        case 0xe0:
-            d0 = dsp_get_data (s);
-            s->out_data_len = 0;
-            ldebug ("E0 data = %#x\n", d0);
-            dsp_out_data (s, ~d0);
-            break;
-
-        case 0xe2:
-#ifdef DEBUG
-            d0 = dsp_get_data (s);
-            dolog ("E2 = %#x\n", d0);
-#endif
-            break;
-
-        case 0xe4:
-            s->test_reg = dsp_get_data (s);
-            break;
-
-        case 0xf9:
-            d0 = dsp_get_data (s);
-            ldebug ("command 0xf9 with %#x\n", d0);
-            switch (d0) {
-            case 0x0e:
-                dsp_out_data (s, 0xff);
-                break;
-
-            case 0x0f:
-                dsp_out_data (s, 0x07);
-                break;
-
-            case 0x37:
-                dsp_out_data (s, 0x38);
-                break;
-
-            default:
-                dsp_out_data (s, 0x00);
-                break;
-            }
-            break;
-
-        default:
-            dolog ("complete: unrecognized command %#x\n", s->cmd);
-            return;
-        }
-    }
-
-    ldebug ("\n");
-    s->cmd = -1;
-}
-
-static void legacy_reset (SB16State *s)
-{
-    struct audsettings as;
-
-    s->freq = 11025;
-    s->fmt_signed = 0;
-    s->fmt_bits = 8;
-    s->fmt_stereo = 0;
-
-    as.freq = s->freq;
-    as.nchannels = 1;
-    as.fmt = AUD_FMT_U8;
-    as.endianness = 0;
-
-    s->voice = AUD_open_out (
-        &s->card,
-        s->voice,
-        "sb16",
-        s,
-        SB_audio_callback,
-        &as
-        );
-
-    /* Not sure about that... */
-    /* AUD_set_active_out (s->voice, 1); */
-}
-
-static void reset (SB16State *s)
-{
-    qemu_irq_lower (s->pic);
-    if (s->dma_auto) {
-        qemu_irq_raise (s->pic);
-        qemu_irq_lower (s->pic);
-    }
-
-    s->mixer_regs[0x82] = 0;
-    s->dma_auto = 0;
-    s->in_index = 0;
-    s->out_data_len = 0;
-    s->left_till_irq = 0;
-    s->needed_bytes = 0;
-    s->block_size = -1;
-    s->nzero = 0;
-    s->highspeed = 0;
-    s->v2x6 = 0;
-    s->cmd = -1;
-
-    dsp_out_data (s, 0xaa);
-    speaker (s, 0);
-    control (s, 0);
-    legacy_reset (s);
-}
-
-static IO_WRITE_PROTO (dsp_write)
-{
-    SB16State *s = opaque;
-    int iport;
-
-    iport = nport - s->port;
-
-    ldebug ("write %#x <- %#x\n", nport, val);
-    switch (iport) {
-    case 0x06:
-        switch (val) {
-        case 0x00:
-            if (s->v2x6 == 1) {
-                reset (s);
-            }
-            s->v2x6 = 0;
-            break;
-
-        case 0x01:
-        case 0x03:              /* FreeBSD kludge */
-            s->v2x6 = 1;
-            break;
-
-        case 0xc6:
-            s->v2x6 = 0;        /* Prince of Persia, csp.sys, diagnose.exe */
-            break;
-
-        case 0xb8:              /* Panic */
-            reset (s);
-            break;
-
-        case 0x39:
-            dsp_out_data (s, 0x38);
-            reset (s);
-            s->v2x6 = 0x39;
-            break;
-
-        default:
-            s->v2x6 = val;
-            break;
-        }
-        break;
-
-    case 0x0c:                  /* write data or command | write status */
-/*         if (s->highspeed) */
-/*             break; */
-
-        if (0 == s->needed_bytes) {
-            command (s, val);
-#if 0
-            if (0 == s->needed_bytes) {
-                log_dsp (s);
-            }
-#endif
-        }
-        else {
-            if (s->in_index == sizeof (s->in2_data)) {
-                dolog ("in data overrun\n");
-            }
-            else {
-                s->in2_data[s->in_index++] = val;
-                if (s->in_index == s->needed_bytes) {
-                    s->needed_bytes = 0;
-                    complete (s);
-#if 0
-                    log_dsp (s);
-#endif
-                }
-            }
-        }
-        break;
-
-    default:
-        ldebug ("(nport=%#x, val=%#x)\n", nport, val);
-        break;
-    }
-}
-
-static IO_READ_PROTO (dsp_read)
-{
-    SB16State *s = opaque;
-    int iport, retval, ack = 0;
-
-    iport = nport - s->port;
-
-    switch (iport) {
-    case 0x06:                  /* reset */
-        retval = 0xff;
-        break;
-
-    case 0x0a:                  /* read data */
-        if (s->out_data_len) {
-            retval = s->out_data[--s->out_data_len];
-            s->last_read_byte = retval;
-        }
-        else {
-            if (s->cmd != -1) {
-                dolog ("empty output buffer for command %#x\n",
-                       s->cmd);
-            }
-            retval = s->last_read_byte;
-            /* goto error; */
-        }
-        break;
-
-    case 0x0c:                  /* 0 can write */
-        retval = s->can_write ? 0 : 0x80;
-        break;
-
-    case 0x0d:                  /* timer interrupt clear */
-        /* dolog ("timer interrupt clear\n"); */
-        retval = 0;
-        break;
-
-    case 0x0e:                  /* data available status | irq 8 ack */
-        retval = (!s->out_data_len || s->highspeed) ? 0 : 0x80;
-        if (s->mixer_regs[0x82] & 1) {
-            ack = 1;
-            s->mixer_regs[0x82] &= 1;
-            qemu_irq_lower (s->pic);
-        }
-        break;
-
-    case 0x0f:                  /* irq 16 ack */
-        retval = 0xff;
-        if (s->mixer_regs[0x82] & 2) {
-            ack = 1;
-            s->mixer_regs[0x82] &= 2;
-            qemu_irq_lower (s->pic);
-        }
-        break;
-
-    default:
-        goto error;
-    }
-
-    if (!ack) {
-        ldebug ("read %#x -> %#x\n", nport, retval);
-    }
-
-    return retval;
-
- error:
-    dolog ("warning: dsp_read %#x error\n", nport);
-    return 0xff;
-}
-
-static void reset_mixer (SB16State *s)
-{
-    int i;
-
-    memset (s->mixer_regs, 0xff, 0x7f);
-    memset (s->mixer_regs + 0x83, 0xff, sizeof (s->mixer_regs) - 0x83);
-
-    s->mixer_regs[0x02] = 4;    /* master volume 3bits */
-    s->mixer_regs[0x06] = 4;    /* MIDI volume 3bits */
-    s->mixer_regs[0x08] = 0;    /* CD volume 3bits */
-    s->mixer_regs[0x0a] = 0;    /* voice volume 2bits */
-
-    /* d5=input filt, d3=lowpass filt, d1,d2=input source */
-    s->mixer_regs[0x0c] = 0;
-
-    /* d5=output filt, d1=stereo switch */
-    s->mixer_regs[0x0e] = 0;
-
-    /* voice volume L d5,d7, R d1,d3 */
-    s->mixer_regs[0x04] = (4 << 5) | (4 << 1);
-    /* master ... */
-    s->mixer_regs[0x22] = (4 << 5) | (4 << 1);
-    /* MIDI ... */
-    s->mixer_regs[0x26] = (4 << 5) | (4 << 1);
-
-    for (i = 0x30; i < 0x48; i++) {
-        s->mixer_regs[i] = 0x20;
-    }
-}
-
-static IO_WRITE_PROTO (mixer_write_indexb)
-{
-    SB16State *s = opaque;
-    (void) nport;
-    s->mixer_nreg = val;
-}
-
-static IO_WRITE_PROTO (mixer_write_datab)
-{
-    SB16State *s = opaque;
-
-    (void) nport;
-    ldebug ("mixer_write [%#x] <- %#x\n", s->mixer_nreg, val);
-
-    switch (s->mixer_nreg) {
-    case 0x00:
-        reset_mixer (s);
-        break;
-
-    case 0x80:
-        {
-            int irq = irq_of_magic (val);
-            ldebug ("setting irq to %d (val=%#x)\n", irq, val);
-            if (irq > 0) {
-                s->irq = irq;
-            }
-        }
-        break;
-
-    case 0x81:
-        {
-            int dma, hdma;
-
-            dma = ctz32 (val & 0xf);
-            hdma = ctz32 (val & 0xf0);
-            if (dma != s->dma || hdma != s->hdma) {
-                dolog (
-                    "attempt to change DMA "
-                    "8bit %d(%d), 16bit %d(%d) (val=%#x)\n",
-                    dma, s->dma, hdma, s->hdma, val);
-            }
-#if 0
-            s->dma = dma;
-            s->hdma = hdma;
-#endif
-        }
-        break;
-
-    case 0x82:
-        dolog ("attempt to write into IRQ status register (val=%#x)\n",
-               val);
-        return;
-
-    default:
-        if (s->mixer_nreg >= 0x80) {
-            ldebug ("attempt to write mixer[%#x] <- %#x\n", s->mixer_nreg, val);
-        }
-        break;
-    }
-
-    s->mixer_regs[s->mixer_nreg] = val;
-}
-
-static IO_WRITE_PROTO (mixer_write_indexw)
-{
-    mixer_write_indexb (opaque, nport, val & 0xff);
-    mixer_write_datab (opaque, nport, (val >> 8) & 0xff);
-}
-
-static IO_READ_PROTO (mixer_read)
-{
-    SB16State *s = opaque;
-
-    (void) nport;
-#ifndef DEBUG_SB16_MOST
-    if (s->mixer_nreg != 0x82) {
-        ldebug ("mixer_read[%#x] -> %#x\n",
-                s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
-    }
-#else
-    ldebug ("mixer_read[%#x] -> %#x\n",
-            s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
-#endif
-    return s->mixer_regs[s->mixer_nreg];
-}
-
-static int write_audio (SB16State *s, int nchan, int dma_pos,
-                        int dma_len, int len)
-{
-    int temp, net;
-    uint8_t tmpbuf[4096];
-
-    temp = len;
-    net = 0;
-
-    while (temp) {
-        int left = dma_len - dma_pos;
-        int copied;
-        size_t to_copy;
-
-        to_copy = audio_MIN (temp, left);
-        if (to_copy > sizeof (tmpbuf)) {
-            to_copy = sizeof (tmpbuf);
-        }
-
-        copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy);
-        copied = AUD_write (s->voice, tmpbuf, copied);
-
-        temp -= copied;
-        dma_pos = (dma_pos + copied) % dma_len;
-        net += copied;
-
-        if (!copied) {
-            break;
-        }
-    }
-
-    return net;
-}
-
-static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
-{
-    SB16State *s = opaque;
-    int till, copy, written, free;
-
-    if (s->block_size <= 0) {
-        dolog ("invalid block size=%d nchan=%d dma_pos=%d dma_len=%d\n",
-               s->block_size, nchan, dma_pos, dma_len);
-        return dma_pos;
-    }
-
-    if (s->left_till_irq < 0) {
-        s->left_till_irq = s->block_size;
-    }
-
-    if (s->voice) {
-        free = s->audio_free & ~s->align;
-        if ((free <= 0) || !dma_len) {
-            return dma_pos;
-        }
-    }
-    else {
-        free = dma_len;
-    }
-
-    copy = free;
-    till = s->left_till_irq;
-
-#ifdef DEBUG_SB16_MOST
-    dolog ("pos:%06d %d till:%d len:%d\n",
-           dma_pos, free, till, dma_len);
-#endif
-
-    if (till <= copy) {
-        if (0 == s->dma_auto) {
-            copy = till;
-        }
-    }
-
-    written = write_audio (s, nchan, dma_pos, dma_len, copy);
-    dma_pos = (dma_pos + written) % dma_len;
-    s->left_till_irq -= written;
-
-    if (s->left_till_irq <= 0) {
-        s->mixer_regs[0x82] |= (nchan & 4) ? 2 : 1;
-        qemu_irq_raise (s->pic);
-        if (0 == s->dma_auto) {
-            control (s, 0);
-            speaker (s, 0);
-        }
-    }
-
-#ifdef DEBUG_SB16_MOST
-    ldebug ("pos %5d free %5d size %5d till % 5d copy %5d written %5d size %5d\n",
-            dma_pos, free, dma_len, s->left_till_irq, copy, written,
-            s->block_size);
-#endif
-
-    while (s->left_till_irq <= 0) {
-        s->left_till_irq = s->block_size + s->left_till_irq;
-    }
-
-    return dma_pos;
-}
-
-static void SB_audio_callback (void *opaque, int free)
-{
-    SB16State *s = opaque;
-    s->audio_free = free;
-}
-
-static int sb16_post_load (void *opaque, int version_id)
-{
-    SB16State *s = opaque;
-
-    if (s->voice) {
-        AUD_close_out (&s->card, s->voice);
-        s->voice = NULL;
-    }
-
-    if (s->dma_running) {
-        if (s->freq) {
-            struct audsettings as;
-
-            s->audio_free = 0;
-
-            as.freq = s->freq;
-            as.nchannels = 1 << s->fmt_stereo;
-            as.fmt = s->fmt;
-            as.endianness = 0;
-
-            s->voice = AUD_open_out (
-                &s->card,
-                s->voice,
-                "sb16",
-                s,
-                SB_audio_callback,
-                &as
-                );
-        }
-
-        control (s, 1);
-        speaker (s, s->speaker);
-    }
-    return 0;
-}
-
-static const VMStateDescription vmstate_sb16 = {
-    .name = "sb16",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .post_load = sb16_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT32 (irq, SB16State),
-        VMSTATE_UINT32 (dma, SB16State),
-        VMSTATE_UINT32 (hdma, SB16State),
-        VMSTATE_UINT32 (port, SB16State),
-        VMSTATE_UINT32 (ver, SB16State),
-        VMSTATE_INT32 (in_index, SB16State),
-        VMSTATE_INT32 (out_data_len, SB16State),
-        VMSTATE_INT32 (fmt_stereo, SB16State),
-        VMSTATE_INT32 (fmt_signed, SB16State),
-        VMSTATE_INT32 (fmt_bits, SB16State),
-        VMSTATE_UINT32 (fmt, SB16State),
-        VMSTATE_INT32 (dma_auto, SB16State),
-        VMSTATE_INT32 (block_size, SB16State),
-        VMSTATE_INT32 (fifo, SB16State),
-        VMSTATE_INT32 (freq, SB16State),
-        VMSTATE_INT32 (time_const, SB16State),
-        VMSTATE_INT32 (speaker, SB16State),
-        VMSTATE_INT32 (needed_bytes, SB16State),
-        VMSTATE_INT32 (cmd, SB16State),
-        VMSTATE_INT32 (use_hdma, SB16State),
-        VMSTATE_INT32 (highspeed, SB16State),
-        VMSTATE_INT32 (can_write, SB16State),
-        VMSTATE_INT32 (v2x6, SB16State),
-
-        VMSTATE_UINT8 (csp_param, SB16State),
-        VMSTATE_UINT8 (csp_value, SB16State),
-        VMSTATE_UINT8 (csp_mode, SB16State),
-        VMSTATE_UINT8 (csp_param, SB16State),
-        VMSTATE_BUFFER (csp_regs, SB16State),
-        VMSTATE_UINT8 (csp_index, SB16State),
-        VMSTATE_BUFFER (csp_reg83, SB16State),
-        VMSTATE_INT32 (csp_reg83r, SB16State),
-        VMSTATE_INT32 (csp_reg83w, SB16State),
-
-        VMSTATE_BUFFER (in2_data, SB16State),
-        VMSTATE_BUFFER (out_data, SB16State),
-        VMSTATE_UINT8 (test_reg, SB16State),
-        VMSTATE_UINT8 (last_read_byte, SB16State),
-
-        VMSTATE_INT32 (nzero, SB16State),
-        VMSTATE_INT32 (left_till_irq, SB16State),
-        VMSTATE_INT32 (dma_running, SB16State),
-        VMSTATE_INT32 (bytes_per_second, SB16State),
-        VMSTATE_INT32 (align, SB16State),
-
-        VMSTATE_INT32 (mixer_nreg, SB16State),
-        VMSTATE_BUFFER (mixer_regs, SB16State),
-
-        VMSTATE_END_OF_LIST ()
-    }
-};
-
-static const MemoryRegionPortio sb16_ioport_list[] = {
-    {  4, 1, 1, .write = mixer_write_indexb },
-    {  4, 1, 2, .write = mixer_write_indexw },
-    {  5, 1, 1, .read = mixer_read, .write = mixer_write_datab },
-    {  6, 1, 1, .read = dsp_read, .write = dsp_write },
-    { 10, 1, 1, .read = dsp_read },
-    { 12, 1, 1, .write = dsp_write },
-    { 12, 4, 1, .read = dsp_read },
-    PORTIO_END_OF_LIST (),
-};
-
-
-static int sb16_initfn (ISADevice *dev)
-{
-    SB16State *s;
-
-    s = DO_UPCAST (SB16State, dev, dev);
-
-    s->cmd = -1;
-    isa_init_irq (dev, &s->pic, s->irq);
-
-    s->mixer_regs[0x80] = magic_of_irq (s->irq);
-    s->mixer_regs[0x81] = (1 << s->dma) | (1 << s->hdma);
-    s->mixer_regs[0x82] = 2 << 5;
-
-    s->csp_regs[5] = 1;
-    s->csp_regs[9] = 0xf8;
-
-    reset_mixer (s);
-    s->aux_ts = qemu_new_timer_ns (vm_clock, aux_timer, s);
-    if (!s->aux_ts) {
-        dolog ("warning: Could not create auxiliary timer\n");
-    }
-
-    isa_register_portio_list (dev, s->port, sb16_ioport_list, s, "sb16");
-
-    DMA_register_channel (s->hdma, SB_read_DMA, s);
-    DMA_register_channel (s->dma, SB_read_DMA, s);
-    s->can_write = 1;
-
-    AUD_register_card ("sb16", &s->card);
-    return 0;
-}
-
-int SB16_init (ISABus *bus)
-{
-    isa_create_simple (bus, "sb16");
-    return 0;
-}
-
-static Property sb16_properties[] = {
-    DEFINE_PROP_HEX32  ("version", SB16State, ver,  0x0405), /* 4.5 */
-    DEFINE_PROP_HEX32  ("iobase",  SB16State, port, 0x220),
-    DEFINE_PROP_UINT32 ("irq",     SB16State, irq,  5),
-    DEFINE_PROP_UINT32 ("dma",     SB16State, dma,  1),
-    DEFINE_PROP_UINT32 ("dma16",   SB16State, hdma, 5),
-    DEFINE_PROP_END_OF_LIST (),
-};
-
-static void sb16_class_initfn (ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS (klass);
-    ISADeviceClass *ic = ISA_DEVICE_CLASS (klass);
-    ic->init = sb16_initfn;
-    dc->desc = "Creative Sound Blaster 16";
-    dc->vmsd = &vmstate_sb16;
-    dc->props = sb16_properties;
-}
-
-static const TypeInfo sb16_info = {
-    .name          = "sb16",
-    .parent        = TYPE_ISA_DEVICE,
-    .instance_size = sizeof (SB16State),
-    .class_init    = sb16_class_initfn,
-};
-
-static void sb16_register_types (void)
-{
-    type_register_static (&sb16_info);
-}
-
-type_init (sb16_register_types)
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
deleted file mode 100644 (file)
index 6239ee1..0000000
+++ /dev/null
@@ -1,1889 +0,0 @@
-#include "hw/hw.h"
-#include "qemu/error-report.h"
-#include "hw/scsi/scsi.h"
-#include "block/scsi.h"
-#include "hw/qdev.h"
-#include "sysemu/blockdev.h"
-#include "trace.h"
-#include "sysemu/dma.h"
-
-static char *scsibus_get_dev_path(DeviceState *dev);
-static char *scsibus_get_fw_dev_path(DeviceState *dev);
-static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf);
-static void scsi_req_dequeue(SCSIRequest *req);
-
-static Property scsi_props[] = {
-    DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0),
-    DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1),
-    DEFINE_PROP_UINT32("lun", SCSIDevice, lun, -1),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void scsi_bus_class_init(ObjectClass *klass, void *data)
-{
-    BusClass *k = BUS_CLASS(klass);
-
-    k->get_dev_path = scsibus_get_dev_path;
-    k->get_fw_dev_path = scsibus_get_fw_dev_path;
-}
-
-static const TypeInfo scsi_bus_info = {
-    .name = TYPE_SCSI_BUS,
-    .parent = TYPE_BUS,
-    .instance_size = sizeof(SCSIBus),
-    .class_init = scsi_bus_class_init,
-};
-static int next_scsi_bus;
-
-static int scsi_device_init(SCSIDevice *s)
-{
-    SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s);
-    if (sc->init) {
-        return sc->init(s);
-    }
-    return 0;
-}
-
-static void scsi_device_destroy(SCSIDevice *s)
-{
-    SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s);
-    if (sc->destroy) {
-        sc->destroy(s);
-    }
-}
-
-static SCSIRequest *scsi_device_alloc_req(SCSIDevice *s, uint32_t tag, uint32_t lun,
-                                          uint8_t *buf, void *hba_private)
-{
-    SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s);
-    if (sc->alloc_req) {
-        return sc->alloc_req(s, tag, lun, buf, hba_private);
-    }
-
-    return NULL;
-}
-
-static void scsi_device_unit_attention_reported(SCSIDevice *s)
-{
-    SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s);
-    if (sc->unit_attention_reported) {
-        sc->unit_attention_reported(s);
-    }
-}
-
-/* Create a scsi bus, and attach devices to it.  */
-void scsi_bus_new(SCSIBus *bus, DeviceState *host, const SCSIBusInfo *info)
-{
-    qbus_create_inplace(&bus->qbus, TYPE_SCSI_BUS, host, NULL);
-    bus->busnr = next_scsi_bus++;
-    bus->info = info;
-    bus->qbus.allow_hotplug = 1;
-}
-
-static void scsi_dma_restart_bh(void *opaque)
-{
-    SCSIDevice *s = opaque;
-    SCSIRequest *req, *next;
-
-    qemu_bh_delete(s->bh);
-    s->bh = NULL;
-
-    QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) {
-        scsi_req_ref(req);
-        if (req->retry) {
-            req->retry = false;
-            switch (req->cmd.mode) {
-            case SCSI_XFER_FROM_DEV:
-            case SCSI_XFER_TO_DEV:
-                scsi_req_continue(req);
-                break;
-            case SCSI_XFER_NONE:
-                assert(!req->sg);
-                scsi_req_dequeue(req);
-                scsi_req_enqueue(req);
-                break;
-            }
-        }
-        scsi_req_unref(req);
-    }
-}
-
-void scsi_req_retry(SCSIRequest *req)
-{
-    /* No need to save a reference, because scsi_dma_restart_bh just
-     * looks at the request list.  */
-    req->retry = true;
-}
-
-static void scsi_dma_restart_cb(void *opaque, int running, RunState state)
-{
-    SCSIDevice *s = opaque;
-
-    if (!running) {
-        return;
-    }
-    if (!s->bh) {
-        s->bh = qemu_bh_new(scsi_dma_restart_bh, s);
-        qemu_bh_schedule(s->bh);
-    }
-}
-
-static int scsi_qdev_init(DeviceState *qdev)
-{
-    SCSIDevice *dev = SCSI_DEVICE(qdev);
-    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
-    SCSIDevice *d;
-    int rc = -1;
-
-    if (dev->channel > bus->info->max_channel) {
-        error_report("bad scsi channel id: %d", dev->channel);
-        goto err;
-    }
-    if (dev->id != -1 && dev->id > bus->info->max_target) {
-        error_report("bad scsi device id: %d", dev->id);
-        goto err;
-    }
-    if (dev->lun != -1 && dev->lun > bus->info->max_lun) {
-        error_report("bad scsi device lun: %d", dev->lun);
-        goto err;
-    }
-
-    if (dev->id == -1) {
-        int id = -1;
-        if (dev->lun == -1) {
-            dev->lun = 0;
-        }
-        do {
-            d = scsi_device_find(bus, dev->channel, ++id, dev->lun);
-        } while (d && d->lun == dev->lun && id < bus->info->max_target);
-        if (d && d->lun == dev->lun) {
-            error_report("no free target");
-            goto err;
-        }
-        dev->id = id;
-    } else if (dev->lun == -1) {
-        int lun = -1;
-        do {
-            d = scsi_device_find(bus, dev->channel, dev->id, ++lun);
-        } while (d && d->lun == lun && lun < bus->info->max_lun);
-        if (d && d->lun == lun) {
-            error_report("no free lun");
-            goto err;
-        }
-        dev->lun = lun;
-    } else {
-        d = scsi_device_find(bus, dev->channel, dev->id, dev->lun);
-        assert(d);
-        if (d->lun == dev->lun && dev != d) {
-            qdev_free(&d->qdev);
-        }
-    }
-
-    QTAILQ_INIT(&dev->requests);
-    rc = scsi_device_init(dev);
-    if (rc == 0) {
-        dev->vmsentry = qemu_add_vm_change_state_handler(scsi_dma_restart_cb,
-                                                         dev);
-    }
-
-    if (bus->info->hotplug) {
-        bus->info->hotplug(bus, dev);
-    }
-
-err:
-    return rc;
-}
-
-static int scsi_qdev_exit(DeviceState *qdev)
-{
-    SCSIDevice *dev = SCSI_DEVICE(qdev);
-
-    if (dev->vmsentry) {
-        qemu_del_vm_change_state_handler(dev->vmsentry);
-    }
-    scsi_device_destroy(dev);
-    return 0;
-}
-
-/* handle legacy '-drive if=scsi,...' cmd line args */
-SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
-                                      int unit, bool removable, int bootindex,
-                                      const char *serial)
-{
-    const char *driver;
-    DeviceState *dev;
-
-    driver = bdrv_is_sg(bdrv) ? "scsi-generic" : "scsi-disk";
-    dev = qdev_create(&bus->qbus, driver);
-    qdev_prop_set_uint32(dev, "scsi-id", unit);
-    if (bootindex >= 0) {
-        qdev_prop_set_int32(dev, "bootindex", bootindex);
-    }
-    if (object_property_find(OBJECT(dev), "removable", NULL)) {
-        qdev_prop_set_bit(dev, "removable", removable);
-    }
-    if (serial) {
-        qdev_prop_set_string(dev, "serial", serial);
-    }
-    if (qdev_prop_set_drive(dev, "drive", bdrv) < 0) {
-        qdev_free(dev);
-        return NULL;
-    }
-    if (qdev_init(dev) < 0)
-        return NULL;
-    return SCSI_DEVICE(dev);
-}
-
-int scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
-{
-    Location loc;
-    DriveInfo *dinfo;
-    int res = 0, unit;
-
-    loc_push_none(&loc);
-    for (unit = 0; unit <= bus->info->max_target; unit++) {
-        dinfo = drive_get(IF_SCSI, bus->busnr, unit);
-        if (dinfo == NULL) {
-            continue;
-        }
-        qemu_opts_loc_restore(dinfo->opts);
-        if (!scsi_bus_legacy_add_drive(bus, dinfo->bdrv, unit, false, -1, NULL)) {
-            res = -1;
-            break;
-        }
-    }
-    loc_pop(&loc);
-    return res;
-}
-
-static int32_t scsi_invalid_field(SCSIRequest *req, uint8_t *buf)
-{
-    scsi_req_build_sense(req, SENSE_CODE(INVALID_FIELD));
-    scsi_req_complete(req, CHECK_CONDITION);
-    return 0;
-}
-
-static const struct SCSIReqOps reqops_invalid_field = {
-    .size         = sizeof(SCSIRequest),
-    .send_command = scsi_invalid_field
-};
-
-/* SCSIReqOps implementation for invalid commands.  */
-
-static int32_t scsi_invalid_command(SCSIRequest *req, uint8_t *buf)
-{
-    scsi_req_build_sense(req, SENSE_CODE(INVALID_OPCODE));
-    scsi_req_complete(req, CHECK_CONDITION);
-    return 0;
-}
-
-static const struct SCSIReqOps reqops_invalid_opcode = {
-    .size         = sizeof(SCSIRequest),
-    .send_command = scsi_invalid_command
-};
-
-/* SCSIReqOps implementation for unit attention conditions.  */
-
-static int32_t scsi_unit_attention(SCSIRequest *req, uint8_t *buf)
-{
-    if (req->dev->unit_attention.key == UNIT_ATTENTION) {
-        scsi_req_build_sense(req, req->dev->unit_attention);
-    } else if (req->bus->unit_attention.key == UNIT_ATTENTION) {
-        scsi_req_build_sense(req, req->bus->unit_attention);
-    }
-    scsi_req_complete(req, CHECK_CONDITION);
-    return 0;
-}
-
-static const struct SCSIReqOps reqops_unit_attention = {
-    .size         = sizeof(SCSIRequest),
-    .send_command = scsi_unit_attention
-};
-
-/* SCSIReqOps implementation for REPORT LUNS and for commands sent to
-   an invalid LUN.  */
-
-typedef struct SCSITargetReq SCSITargetReq;
-
-struct SCSITargetReq {
-    SCSIRequest req;
-    int len;
-    uint8_t buf[2056];
-};
-
-static void store_lun(uint8_t *outbuf, int lun)
-{
-    if (lun < 256) {
-        outbuf[1] = lun;
-        return;
-    }
-    outbuf[1] = (lun & 255);
-    outbuf[0] = (lun >> 8) | 0x40;
-}
-
-static bool scsi_target_emulate_report_luns(SCSITargetReq *r)
-{
-    BusChild *kid;
-    int i, len, n;
-    int channel, id;
-    bool found_lun0;
-
-    if (r->req.cmd.xfer < 16) {
-        return false;
-    }
-    if (r->req.cmd.buf[2] > 2) {
-        return false;
-    }
-    channel = r->req.dev->channel;
-    id = r->req.dev->id;
-    found_lun0 = false;
-    n = 0;
-    QTAILQ_FOREACH(kid, &r->req.bus->qbus.children, sibling) {
-        DeviceState *qdev = kid->child;
-        SCSIDevice *dev = SCSI_DEVICE(qdev);
-
-        if (dev->channel == channel && dev->id == id) {
-            if (dev->lun == 0) {
-                found_lun0 = true;
-            }
-            n += 8;
-        }
-    }
-    if (!found_lun0) {
-        n += 8;
-    }
-    len = MIN(n + 8, r->req.cmd.xfer & ~7);
-    if (len > sizeof(r->buf)) {
-        /* TODO: > 256 LUNs? */
-        return false;
-    }
-
-    memset(r->buf, 0, len);
-    stl_be_p(&r->buf, n);
-    i = found_lun0 ? 8 : 16;
-    QTAILQ_FOREACH(kid, &r->req.bus->qbus.children, sibling) {
-        DeviceState *qdev = kid->child;
-        SCSIDevice *dev = SCSI_DEVICE(qdev);
-
-        if (dev->channel == channel && dev->id == id) {
-            store_lun(&r->buf[i], dev->lun);
-            i += 8;
-        }
-    }
-    assert(i == n + 8);
-    r->len = len;
-    return true;
-}
-
-static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
-{
-    assert(r->req.dev->lun != r->req.lun);
-    if (r->req.cmd.buf[1] & 0x2) {
-        /* Command support data - optional, not implemented */
-        return false;
-    }
-
-    if (r->req.cmd.buf[1] & 0x1) {
-        /* Vital product data */
-        uint8_t page_code = r->req.cmd.buf[2];
-        r->buf[r->len++] = page_code ; /* this page */
-        r->buf[r->len++] = 0x00;
-
-        switch (page_code) {
-        case 0x00: /* Supported page codes, mandatory */
-        {
-            int pages;
-            pages = r->len++;
-            r->buf[r->len++] = 0x00; /* list of supported pages (this page) */
-            r->buf[pages] = r->len - pages - 1; /* number of pages */
-            break;
-        }
-        default:
-            return false;
-        }
-        /* done with EVPD */
-        assert(r->len < sizeof(r->buf));
-        r->len = MIN(r->req.cmd.xfer, r->len);
-        return true;
-    }
-
-    /* Standard INQUIRY data */
-    if (r->req.cmd.buf[2] != 0) {
-        return false;
-    }
-
-    /* PAGE CODE == 0 */
-    r->len = MIN(r->req.cmd.xfer, 36);
-    memset(r->buf, 0, r->len);
-    if (r->req.lun != 0) {
-        r->buf[0] = TYPE_NO_LUN;
-    } else {
-        r->buf[0] = TYPE_NOT_PRESENT | TYPE_INACTIVE;
-        r->buf[2] = 5; /* Version */
-        r->buf[3] = 2 | 0x10; /* HiSup, response data format */
-        r->buf[4] = r->len - 5; /* Additional Length = (Len - 1) - 4 */
-        r->buf[7] = 0x10 | (r->req.bus->info->tcq ? 0x02 : 0); /* Sync, TCQ.  */
-        memcpy(&r->buf[8], "QEMU    ", 8);
-        memcpy(&r->buf[16], "QEMU TARGET     ", 16);
-        pstrcpy((char *) &r->buf[32], 4, qemu_get_version());
-    }
-    return true;
-}
-
-static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf)
-{
-    SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
-
-    switch (buf[0]) {
-    case REPORT_LUNS:
-        if (!scsi_target_emulate_report_luns(r)) {
-            goto illegal_request;
-        }
-        break;
-    case INQUIRY:
-        if (!scsi_target_emulate_inquiry(r)) {
-            goto illegal_request;
-        }
-        break;
-    case REQUEST_SENSE:
-        r->len = scsi_device_get_sense(r->req.dev, r->buf,
-                                       MIN(req->cmd.xfer, sizeof r->buf),
-                                       (req->cmd.buf[1] & 1) == 0);
-        if (r->req.dev->sense_is_ua) {
-            scsi_device_unit_attention_reported(req->dev);
-            r->req.dev->sense_len = 0;
-            r->req.dev->sense_is_ua = false;
-        }
-        break;
-    default:
-        scsi_req_build_sense(req, SENSE_CODE(LUN_NOT_SUPPORTED));
-        scsi_req_complete(req, CHECK_CONDITION);
-        return 0;
-    illegal_request:
-        scsi_req_build_sense(req, SENSE_CODE(INVALID_FIELD));
-        scsi_req_complete(req, CHECK_CONDITION);
-        return 0;
-    }
-
-    if (!r->len) {
-        scsi_req_complete(req, GOOD);
-    }
-    return r->len;
-}
-
-static void scsi_target_read_data(SCSIRequest *req)
-{
-    SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
-    uint32_t n;
-
-    n = r->len;
-    if (n > 0) {
-        r->len = 0;
-        scsi_req_data(&r->req, n);
-    } else {
-        scsi_req_complete(&r->req, GOOD);
-    }
-}
-
-static uint8_t *scsi_target_get_buf(SCSIRequest *req)
-{
-    SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
-
-    return r->buf;
-}
-
-static const struct SCSIReqOps reqops_target_command = {
-    .size         = sizeof(SCSITargetReq),
-    .send_command = scsi_target_send_command,
-    .read_data    = scsi_target_read_data,
-    .get_buf      = scsi_target_get_buf,
-};
-
-
-SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d,
-                            uint32_t tag, uint32_t lun, void *hba_private)
-{
-    SCSIRequest *req;
-
-    req = g_malloc0(reqops->size);
-    req->refcount = 1;
-    req->bus = scsi_bus_from_device(d);
-    req->dev = d;
-    req->tag = tag;
-    req->lun = lun;
-    req->hba_private = hba_private;
-    req->status = -1;
-    req->sense_len = 0;
-    req->ops = reqops;
-    trace_scsi_req_alloc(req->dev->id, req->lun, req->tag);
-    return req;
-}
-
-SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
-                          uint8_t *buf, void *hba_private)
-{
-    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus);
-    SCSIRequest *req;
-    SCSICommand cmd;
-
-    if (scsi_req_parse(&cmd, d, buf) != 0) {
-        trace_scsi_req_parse_bad(d->id, lun, tag, buf[0]);
-        req = scsi_req_alloc(&reqops_invalid_opcode, d, tag, lun, hba_private);
-    } else {
-        trace_scsi_req_parsed(d->id, lun, tag, buf[0],
-                              cmd.mode, cmd.xfer);
-        if (cmd.lba != -1) {
-            trace_scsi_req_parsed_lba(d->id, lun, tag, buf[0],
-                                      cmd.lba);
-        }
-
-        if (cmd.xfer > INT32_MAX) {
-            req = scsi_req_alloc(&reqops_invalid_field, d, tag, lun, hba_private);
-        } else if ((d->unit_attention.key == UNIT_ATTENTION ||
-                   bus->unit_attention.key == UNIT_ATTENTION) &&
-                  (buf[0] != INQUIRY &&
-                   buf[0] != REPORT_LUNS &&
-                   buf[0] != GET_CONFIGURATION &&
-                   buf[0] != GET_EVENT_STATUS_NOTIFICATION &&
-
-                   /*
-                    * If we already have a pending unit attention condition,
-                    * report this one before triggering another one.
-                    */
-                   !(buf[0] == REQUEST_SENSE && d->sense_is_ua))) {
-            req = scsi_req_alloc(&reqops_unit_attention, d, tag, lun,
-                                 hba_private);
-        } else if (lun != d->lun ||
-                   buf[0] == REPORT_LUNS ||
-                   (buf[0] == REQUEST_SENSE && d->sense_len)) {
-            req = scsi_req_alloc(&reqops_target_command, d, tag, lun,
-                                 hba_private);
-        } else {
-            req = scsi_device_alloc_req(d, tag, lun, buf, hba_private);
-        }
-    }
-
-    req->cmd = cmd;
-    req->resid = req->cmd.xfer;
-
-    switch (buf[0]) {
-    case INQUIRY:
-        trace_scsi_inquiry(d->id, lun, tag, cmd.buf[1], cmd.buf[2]);
-        break;
-    case TEST_UNIT_READY:
-        trace_scsi_test_unit_ready(d->id, lun, tag);
-        break;
-    case REPORT_LUNS:
-        trace_scsi_report_luns(d->id, lun, tag);
-        break;
-    case REQUEST_SENSE:
-        trace_scsi_request_sense(d->id, lun, tag);
-        break;
-    default:
-        break;
-    }
-
-    return req;
-}
-
-uint8_t *scsi_req_get_buf(SCSIRequest *req)
-{
-    return req->ops->get_buf(req);
-}
-
-static void scsi_clear_unit_attention(SCSIRequest *req)
-{
-    SCSISense *ua;
-    if (req->dev->unit_attention.key != UNIT_ATTENTION &&
-        req->bus->unit_attention.key != UNIT_ATTENTION) {
-        return;
-    }
-
-    /*
-     * If an INQUIRY command enters the enabled command state,
-     * the device server shall [not] clear any unit attention condition;
-     * See also MMC-6, paragraphs 6.5 and 6.6.2.
-     */
-    if (req->cmd.buf[0] == INQUIRY ||
-        req->cmd.buf[0] == GET_CONFIGURATION ||
-        req->cmd.buf[0] == GET_EVENT_STATUS_NOTIFICATION) {
-        return;
-    }
-
-    if (req->dev->unit_attention.key == UNIT_ATTENTION) {
-        ua = &req->dev->unit_attention;
-    } else {
-        ua = &req->bus->unit_attention;
-    }
-
-    /*
-     * If a REPORT LUNS command enters the enabled command state, [...]
-     * the device server shall clear any pending unit attention condition
-     * with an additional sense code of REPORTED LUNS DATA HAS CHANGED.
-     */
-    if (req->cmd.buf[0] == REPORT_LUNS &&
-        !(ua->asc == SENSE_CODE(REPORTED_LUNS_CHANGED).asc &&
-          ua->ascq == SENSE_CODE(REPORTED_LUNS_CHANGED).ascq)) {
-        return;
-    }
-
-    *ua = SENSE_CODE(NO_SENSE);
-}
-
-int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len)
-{
-    int ret;
-
-    assert(len >= 14);
-    if (!req->sense_len) {
-        return 0;
-    }
-
-    ret = scsi_build_sense(req->sense, req->sense_len, buf, len, true);
-
-    /*
-     * FIXME: clearing unit attention conditions upon autosense should be done
-     * only if the UA_INTLCK_CTRL field in the Control mode page is set to 00b
-     * (SAM-5, 5.14).
-     *
-     * We assume UA_INTLCK_CTRL to be 00b for HBAs that support autosense, and
-     * 10b for HBAs that do not support it (do not call scsi_req_get_sense).
-     * Here we handle unit attention clearing for UA_INTLCK_CTRL == 00b.
-     */
-    if (req->dev->sense_is_ua) {
-        scsi_device_unit_attention_reported(req->dev);
-        req->dev->sense_len = 0;
-        req->dev->sense_is_ua = false;
-    }
-    return ret;
-}
-
-int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed)
-{
-    return scsi_build_sense(dev->sense, dev->sense_len, buf, len, fixed);
-}
-
-void scsi_req_build_sense(SCSIRequest *req, SCSISense sense)
-{
-    trace_scsi_req_build_sense(req->dev->id, req->lun, req->tag,
-                               sense.key, sense.asc, sense.ascq);
-    memset(req->sense, 0, 18);
-    req->sense[0] = 0x70;
-    req->sense[2] = sense.key;
-    req->sense[7] = 10;
-    req->sense[12] = sense.asc;
-    req->sense[13] = sense.ascq;
-    req->sense_len = 18;
-}
-
-static void scsi_req_enqueue_internal(SCSIRequest *req)
-{
-    assert(!req->enqueued);
-    scsi_req_ref(req);
-    if (req->bus->info->get_sg_list) {
-        req->sg = req->bus->info->get_sg_list(req);
-    } else {
-        req->sg = NULL;
-    }
-    req->enqueued = true;
-    QTAILQ_INSERT_TAIL(&req->dev->requests, req, next);
-}
-
-int32_t scsi_req_enqueue(SCSIRequest *req)
-{
-    int32_t rc;
-
-    assert(!req->retry);
-    scsi_req_enqueue_internal(req);
-    scsi_req_ref(req);
-    rc = req->ops->send_command(req, req->cmd.buf);
-    scsi_req_unref(req);
-    return rc;
-}
-
-static void scsi_req_dequeue(SCSIRequest *req)
-{
-    trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag);
-    req->retry = false;
-    if (req->enqueued) {
-        QTAILQ_REMOVE(&req->dev->requests, req, next);
-        req->enqueued = false;
-        scsi_req_unref(req);
-    }
-}
-
-static int scsi_get_performance_length(int num_desc, int type, int data_type)
-{
-    /* MMC-6, paragraph 6.7.  */
-    switch (type) {
-    case 0:
-        if ((data_type & 3) == 0) {
-            /* Each descriptor is as in Table 295 - Nominal performance.  */
-            return 16 * num_desc + 8;
-        } else {
-            /* Each descriptor is as in Table 296 - Exceptions.  */
-            return 6 * num_desc + 8;
-        }
-    case 1:
-    case 4:
-    case 5:
-        return 8 * num_desc + 8;
-    case 2:
-        return 2048 * num_desc + 8;
-    case 3:
-        return 16 * num_desc + 8;
-    default:
-        return 8;
-    }
-}
-
-static int ata_passthrough_xfer_unit(SCSIDevice *dev, uint8_t *buf)
-{
-    int byte_block = (buf[2] >> 2) & 0x1;
-    int type = (buf[2] >> 4) & 0x1;
-    int xfer_unit;
-
-    if (byte_block) {
-        if (type) {
-            xfer_unit = dev->blocksize;
-        } else {
-            xfer_unit = 512;
-        }
-    } else {
-        xfer_unit = 1;
-    }
-
-    return xfer_unit;
-}
-
-static int ata_passthrough_12_xfer_size(SCSIDevice *dev, uint8_t *buf)
-{
-    int length = buf[2] & 0x3;
-    int xfer;
-    int unit = ata_passthrough_xfer_unit(dev, buf);
-
-    switch (length) {
-    case 0:
-    case 3: /* USB-specific.  */
-    default:
-        xfer = 0;
-        break;
-    case 1:
-        xfer = buf[3];
-        break;
-    case 2:
-        xfer = buf[4];
-        break;
-    }
-
-    return xfer * unit;
-}
-
-static int ata_passthrough_16_xfer_size(SCSIDevice *dev, uint8_t *buf)
-{
-    int extend = buf[1] & 0x1;
-    int length = buf[2] & 0x3;
-    int xfer;
-    int unit = ata_passthrough_xfer_unit(dev, buf);
-
-    switch (length) {
-    case 0:
-    case 3: /* USB-specific.  */
-    default:
-        xfer = 0;
-        break;
-    case 1:
-        xfer = buf[4];
-        xfer |= (extend ? buf[3] << 8 : 0);
-        break;
-    case 2:
-        xfer = buf[6];
-        xfer |= (extend ? buf[5] << 8 : 0);
-        break;
-    }
-
-    return xfer * unit;
-}
-
-uint32_t scsi_data_cdb_length(uint8_t *buf)
-{
-    if ((buf[0] >> 5) == 0 && buf[4] == 0) {
-        return 256;
-    } else {
-        return scsi_cdb_length(buf);
-    }
-}
-
-uint32_t scsi_cdb_length(uint8_t *buf)
-{
-    switch (buf[0] >> 5) {
-    case 0:
-        return buf[4];
-        break;
-    case 1:
-    case 2:
-        return lduw_be_p(&buf[7]);
-        break;
-    case 4:
-        return ldl_be_p(&buf[10]) & 0xffffffffULL;
-        break;
-    case 5:
-        return ldl_be_p(&buf[6]) & 0xffffffffULL;
-        break;
-    default:
-        return -1;
-    }
-}
-
-static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
-{
-    cmd->xfer = scsi_cdb_length(buf);
-    switch (buf[0]) {
-    case TEST_UNIT_READY:
-    case REWIND:
-    case START_STOP:
-    case SET_CAPACITY:
-    case WRITE_FILEMARKS:
-    case WRITE_FILEMARKS_16:
-    case SPACE:
-    case RESERVE:
-    case RELEASE:
-    case ERASE:
-    case ALLOW_MEDIUM_REMOVAL:
-    case VERIFY_10:
-    case SEEK_10:
-    case SYNCHRONIZE_CACHE:
-    case SYNCHRONIZE_CACHE_16:
-    case LOCATE_16:
-    case LOCK_UNLOCK_CACHE:
-    case SET_CD_SPEED:
-    case SET_LIMITS:
-    case WRITE_LONG_10:
-    case UPDATE_BLOCK:
-    case RESERVE_TRACK:
-    case SET_READ_AHEAD:
-    case PRE_FETCH:
-    case PRE_FETCH_16:
-    case ALLOW_OVERWRITE:
-        cmd->xfer = 0;
-        break;
-    case MODE_SENSE:
-        break;
-    case WRITE_SAME_10:
-    case WRITE_SAME_16:
-        cmd->xfer = dev->blocksize;
-        break;
-    case READ_CAPACITY_10:
-        cmd->xfer = 8;
-        break;
-    case READ_BLOCK_LIMITS:
-        cmd->xfer = 6;
-        break;
-    case SEND_VOLUME_TAG:
-        /* GPCMD_SET_STREAMING from multimedia commands.  */
-        if (dev->type == TYPE_ROM) {
-            cmd->xfer = buf[10] | (buf[9] << 8);
-        } else {
-            cmd->xfer = buf[9] | (buf[8] << 8);
-        }
-        break;
-    case WRITE_6:
-        /* length 0 means 256 blocks */
-        if (cmd->xfer == 0) {
-            cmd->xfer = 256;
-        }
-    case WRITE_10:
-    case WRITE_VERIFY_10:
-    case WRITE_12:
-    case WRITE_VERIFY_12:
-    case WRITE_16:
-    case WRITE_VERIFY_16:
-        cmd->xfer *= dev->blocksize;
-        break;
-    case READ_6:
-    case READ_REVERSE:
-        /* length 0 means 256 blocks */
-        if (cmd->xfer == 0) {
-            cmd->xfer = 256;
-        }
-    case READ_10:
-    case RECOVER_BUFFERED_DATA:
-    case READ_12:
-    case READ_16:
-        cmd->xfer *= dev->blocksize;
-        break;
-    case FORMAT_UNIT:
-        /* MMC mandates the parameter list to be 12-bytes long.  Parameters
-         * for block devices are restricted to the header right now.  */
-        if (dev->type == TYPE_ROM && (buf[1] & 16)) {
-            cmd->xfer = 12;
-        } else {
-            cmd->xfer = (buf[1] & 16) == 0 ? 0 : (buf[1] & 32 ? 8 : 4);
-        }
-        break;
-    case INQUIRY:
-    case RECEIVE_DIAGNOSTIC:
-    case SEND_DIAGNOSTIC:
-        cmd->xfer = buf[4] | (buf[3] << 8);
-        break;
-    case READ_CD:
-    case READ_BUFFER:
-    case WRITE_BUFFER:
-    case SEND_CUE_SHEET:
-        cmd->xfer = buf[8] | (buf[7] << 8) | (buf[6] << 16);
-        break;
-    case PERSISTENT_RESERVE_OUT:
-        cmd->xfer = ldl_be_p(&buf[5]) & 0xffffffffULL;
-        break;
-    case ERASE_12:
-        if (dev->type == TYPE_ROM) {
-            /* MMC command GET PERFORMANCE.  */
-            cmd->xfer = scsi_get_performance_length(buf[9] | (buf[8] << 8),
-                                                    buf[10], buf[1] & 0x1f);
-        }
-        break;
-    case MECHANISM_STATUS:
-    case READ_DVD_STRUCTURE:
-    case SEND_DVD_STRUCTURE:
-    case MAINTENANCE_OUT:
-    case MAINTENANCE_IN:
-        if (dev->type == TYPE_ROM) {
-            /* GPCMD_REPORT_KEY and GPCMD_SEND_KEY from multi media commands */
-            cmd->xfer = buf[9] | (buf[8] << 8);
-        }
-        break;
-    case ATA_PASSTHROUGH_12:
-        if (dev->type == TYPE_ROM) {
-            /* BLANK command of MMC */
-            cmd->xfer = 0;
-        } else {
-            cmd->xfer = ata_passthrough_12_xfer_size(dev, buf);
-        }
-        break;
-    case ATA_PASSTHROUGH_16:
-        cmd->xfer = ata_passthrough_16_xfer_size(dev, buf);
-        break;
-    }
-    return 0;
-}
-
-static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
-{
-    switch (buf[0]) {
-    /* stream commands */
-    case ERASE_12:
-    case ERASE_16:
-        cmd->xfer = 0;
-        break;
-    case READ_6:
-    case READ_REVERSE:
-    case RECOVER_BUFFERED_DATA:
-    case WRITE_6:
-        cmd->xfer = buf[4] | (buf[3] << 8) | (buf[2] << 16);
-        if (buf[1] & 0x01) { /* fixed */
-            cmd->xfer *= dev->blocksize;
-        }
-        break;
-    case READ_16:
-    case READ_REVERSE_16:
-    case VERIFY_16:
-    case WRITE_16:
-        cmd->xfer = buf[14] | (buf[13] << 8) | (buf[12] << 16);
-        if (buf[1] & 0x01) { /* fixed */
-            cmd->xfer *= dev->blocksize;
-        }
-        break;
-    case REWIND:
-    case LOAD_UNLOAD:
-        cmd->xfer = 0;
-        break;
-    case SPACE_16:
-        cmd->xfer = buf[13] | (buf[12] << 8);
-        break;
-    case READ_POSITION:
-        switch (buf[1] & 0x1f) /* operation code */ {
-        case SHORT_FORM_BLOCK_ID:
-        case SHORT_FORM_VENDOR_SPECIFIC:
-            cmd->xfer = 20;
-            break;
-        case LONG_FORM:
-            cmd->xfer = 32;
-            break;
-        case EXTENDED_FORM:
-            cmd->xfer = buf[8] | (buf[7] << 8);
-            break;
-        default:
-            return -1;
-        }
-
-        break;
-    case FORMAT_UNIT:
-        cmd->xfer = buf[4] | (buf[3] << 8);
-        break;
-    /* generic commands */
-    default:
-        return scsi_req_length(cmd, dev, buf);
-    }
-    return 0;
-}
-
-static int scsi_req_medium_changer_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
-{
-    switch (buf[0]) {
-    /* medium changer commands */
-    case EXCHANGE_MEDIUM:
-    case INITIALIZE_ELEMENT_STATUS:
-    case INITIALIZE_ELEMENT_STATUS_WITH_RANGE:
-    case MOVE_MEDIUM:
-    case POSITION_TO_ELEMENT:
-        cmd->xfer = 0;
-        break;
-    case READ_ELEMENT_STATUS:
-        cmd->xfer = buf[9] | (buf[8] << 8) | (buf[7] << 16);
-        break;
-
-    /* generic commands */
-    default:
-        return scsi_req_length(cmd, dev, buf);
-    }
-    return 0;
-}
-
-
-static void scsi_cmd_xfer_mode(SCSICommand *cmd)
-{
-    if (!cmd->xfer) {
-        cmd->mode = SCSI_XFER_NONE;
-        return;
-    }
-    switch (cmd->buf[0]) {
-    case WRITE_6:
-    case WRITE_10:
-    case WRITE_VERIFY_10:
-    case WRITE_12:
-    case WRITE_VERIFY_12:
-    case WRITE_16:
-    case WRITE_VERIFY_16:
-    case COPY:
-    case COPY_VERIFY:
-    case COMPARE:
-    case CHANGE_DEFINITION:
-    case LOG_SELECT:
-    case MODE_SELECT:
-    case MODE_SELECT_10:
-    case SEND_DIAGNOSTIC:
-    case WRITE_BUFFER:
-    case FORMAT_UNIT:
-    case REASSIGN_BLOCKS:
-    case SEARCH_EQUAL:
-    case SEARCH_HIGH:
-    case SEARCH_LOW:
-    case UPDATE_BLOCK:
-    case WRITE_LONG_10:
-    case WRITE_SAME_10:
-    case WRITE_SAME_16:
-    case UNMAP:
-    case SEARCH_HIGH_12:
-    case SEARCH_EQUAL_12:
-    case SEARCH_LOW_12:
-    case MEDIUM_SCAN:
-    case SEND_VOLUME_TAG:
-    case SEND_CUE_SHEET:
-    case SEND_DVD_STRUCTURE:
-    case PERSISTENT_RESERVE_OUT:
-    case MAINTENANCE_OUT:
-        cmd->mode = SCSI_XFER_TO_DEV;
-        break;
-    case ATA_PASSTHROUGH_12:
-    case ATA_PASSTHROUGH_16:
-        /* T_DIR */
-        cmd->mode = (cmd->buf[2] & 0x8) ?
-                   SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV;
-        break;
-    default:
-        cmd->mode = SCSI_XFER_FROM_DEV;
-        break;
-    }
-}
-
-static uint64_t scsi_cmd_lba(SCSICommand *cmd)
-{
-    uint8_t *buf = cmd->buf;
-    uint64_t lba;
-
-    switch (buf[0] >> 5) {
-    case 0:
-        lba = ldl_be_p(&buf[0]) & 0x1fffff;
-        break;
-    case 1:
-    case 2:
-    case 5:
-        lba = ldl_be_p(&buf[2]) & 0xffffffffULL;
-        break;
-    case 4:
-        lba = ldq_be_p(&buf[2]);
-        break;
-    default:
-        lba = -1;
-
-    }
-    return lba;
-}
-
-int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
-{
-    int rc;
-
-    switch (buf[0] >> 5) {
-    case 0:
-        cmd->len = 6;
-        break;
-    case 1:
-    case 2:
-        cmd->len = 10;
-        break;
-    case 4:
-        cmd->len = 16;
-        break;
-    case 5:
-        cmd->len = 12;
-        break;
-    default:
-        return -1;
-    }
-
-    switch (dev->type) {
-    case TYPE_TAPE:
-        rc = scsi_req_stream_length(cmd, dev, buf);
-        break;
-    case TYPE_MEDIUM_CHANGER:
-        rc = scsi_req_medium_changer_length(cmd, dev, buf);
-        break;
-    default:
-        rc = scsi_req_length(cmd, dev, buf);
-        break;
-    }
-
-    if (rc != 0)
-        return rc;
-
-    memcpy(cmd->buf, buf, cmd->len);
-    scsi_cmd_xfer_mode(cmd);
-    cmd->lba = scsi_cmd_lba(cmd);
-    return 0;
-}
-
-void scsi_device_report_change(SCSIDevice *dev, SCSISense sense)
-{
-    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
-
-    scsi_device_set_ua(dev, sense);
-    if (bus->info->change) {
-        bus->info->change(bus, dev, sense);
-    }
-}
-
-/*
- * Predefined sense codes
- */
-
-/* No sense data available */
-const struct SCSISense sense_code_NO_SENSE = {
-    .key = NO_SENSE , .asc = 0x00 , .ascq = 0x00
-};
-
-/* LUN not ready, Manual intervention required */
-const struct SCSISense sense_code_LUN_NOT_READY = {
-    .key = NOT_READY, .asc = 0x04, .ascq = 0x03
-};
-
-/* LUN not ready, Medium not present */
-const struct SCSISense sense_code_NO_MEDIUM = {
-    .key = NOT_READY, .asc = 0x3a, .ascq = 0x00
-};
-
-/* LUN not ready, medium removal prevented */
-const struct SCSISense sense_code_NOT_READY_REMOVAL_PREVENTED = {
-    .key = NOT_READY, .asc = 0x53, .ascq = 0x02
-};
-
-/* Hardware error, internal target failure */
-const struct SCSISense sense_code_TARGET_FAILURE = {
-    .key = HARDWARE_ERROR, .asc = 0x44, .ascq = 0x00
-};
-
-/* Illegal request, invalid command operation code */
-const struct SCSISense sense_code_INVALID_OPCODE = {
-    .key = ILLEGAL_REQUEST, .asc = 0x20, .ascq = 0x00
-};
-
-/* Illegal request, LBA out of range */
-const struct SCSISense sense_code_LBA_OUT_OF_RANGE = {
-    .key = ILLEGAL_REQUEST, .asc = 0x21, .ascq = 0x00
-};
-
-/* Illegal request, Invalid field in CDB */
-const struct SCSISense sense_code_INVALID_FIELD = {
-    .key = ILLEGAL_REQUEST, .asc = 0x24, .ascq = 0x00
-};
-
-/* Illegal request, Invalid field in parameter list */
-const struct SCSISense sense_code_INVALID_PARAM = {
-    .key = ILLEGAL_REQUEST, .asc = 0x26, .ascq = 0x00
-};
-
-/* Illegal request, Parameter list length error */
-const struct SCSISense sense_code_INVALID_PARAM_LEN = {
-    .key = ILLEGAL_REQUEST, .asc = 0x1a, .ascq = 0x00
-};
-
-/* Illegal request, LUN not supported */
-const struct SCSISense sense_code_LUN_NOT_SUPPORTED = {
-    .key = ILLEGAL_REQUEST, .asc = 0x25, .ascq = 0x00
-};
-
-/* Illegal request, Saving parameters not supported */
-const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED = {
-    .key = ILLEGAL_REQUEST, .asc = 0x39, .ascq = 0x00
-};
-
-/* Illegal request, Incompatible medium installed */
-const struct SCSISense sense_code_INCOMPATIBLE_FORMAT = {
-    .key = ILLEGAL_REQUEST, .asc = 0x30, .ascq = 0x00
-};
-
-/* Illegal request, medium removal prevented */
-const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED = {
-    .key = ILLEGAL_REQUEST, .asc = 0x53, .ascq = 0x02
-};
-
-/* Command aborted, I/O process terminated */
-const struct SCSISense sense_code_IO_ERROR = {
-    .key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06
-};
-
-/* Command aborted, I_T Nexus loss occurred */
-const struct SCSISense sense_code_I_T_NEXUS_LOSS = {
-    .key = ABORTED_COMMAND, .asc = 0x29, .ascq = 0x07
-};
-
-/* Command aborted, Logical Unit failure */
-const struct SCSISense sense_code_LUN_FAILURE = {
-    .key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01
-};
-
-/* Unit attention, Capacity data has changed */
-const struct SCSISense sense_code_CAPACITY_CHANGED = {
-    .key = UNIT_ATTENTION, .asc = 0x2a, .ascq = 0x09
-};
-
-/* Unit attention, Power on, reset or bus device reset occurred */
-const struct SCSISense sense_code_RESET = {
-    .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x00
-};
-
-/* Unit attention, No medium */
-const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM = {
-    .key = UNIT_ATTENTION, .asc = 0x3a, .ascq = 0x00
-};
-
-/* Unit attention, Medium may have changed */
-const struct SCSISense sense_code_MEDIUM_CHANGED = {
-    .key = UNIT_ATTENTION, .asc = 0x28, .ascq = 0x00
-};
-
-/* Unit attention, Reported LUNs data has changed */
-const struct SCSISense sense_code_REPORTED_LUNS_CHANGED = {
-    .key = UNIT_ATTENTION, .asc = 0x3f, .ascq = 0x0e
-};
-
-/* Unit attention, Device internal reset */
-const struct SCSISense sense_code_DEVICE_INTERNAL_RESET = {
-    .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x04
-};
-
-/* Data Protection, Write Protected */
-const struct SCSISense sense_code_WRITE_PROTECTED = {
-    .key = DATA_PROTECT, .asc = 0x27, .ascq = 0x00
-};
-
-/*
- * scsi_build_sense
- *
- * Convert between fixed and descriptor sense buffers
- */
-int scsi_build_sense(uint8_t *in_buf, int in_len,
-                     uint8_t *buf, int len, bool fixed)
-{
-    bool fixed_in;
-    SCSISense sense;
-    if (!fixed && len < 8) {
-        return 0;
-    }
-
-    if (in_len == 0) {
-        sense.key = NO_SENSE;
-        sense.asc = 0;
-        sense.ascq = 0;
-    } else {
-        fixed_in = (in_buf[0] & 2) == 0;
-
-        if (fixed == fixed_in) {
-            memcpy(buf, in_buf, MIN(len, in_len));
-            return MIN(len, in_len);
-        }
-
-        if (fixed_in) {
-            sense.key = in_buf[2];
-            sense.asc = in_buf[12];
-            sense.ascq = in_buf[13];
-        } else {
-            sense.key = in_buf[1];
-            sense.asc = in_buf[2];
-            sense.ascq = in_buf[3];
-        }
-    }
-
-    memset(buf, 0, len);
-    if (fixed) {
-        /* Return fixed format sense buffer */
-        buf[0] = 0x70;
-        buf[2] = sense.key;
-        buf[7] = 10;
-        buf[12] = sense.asc;
-        buf[13] = sense.ascq;
-        return MIN(len, 18);
-    } else {
-        /* Return descriptor format sense buffer */
-        buf[0] = 0x72;
-        buf[1] = sense.key;
-        buf[2] = sense.asc;
-        buf[3] = sense.ascq;
-        return 8;
-    }
-}
-
-static const char *scsi_command_name(uint8_t cmd)
-{
-    static const char *names[] = {
-        [ TEST_UNIT_READY          ] = "TEST_UNIT_READY",
-        [ REWIND                   ] = "REWIND",
-        [ REQUEST_SENSE            ] = "REQUEST_SENSE",
-        [ FORMAT_UNIT              ] = "FORMAT_UNIT",
-        [ READ_BLOCK_LIMITS        ] = "READ_BLOCK_LIMITS",
-        [ REASSIGN_BLOCKS          ] = "REASSIGN_BLOCKS/INITIALIZE ELEMENT STATUS",
-        /* LOAD_UNLOAD and INITIALIZE_ELEMENT_STATUS use the same operation code */
-        [ READ_6                   ] = "READ_6",
-        [ WRITE_6                  ] = "WRITE_6",
-        [ SET_CAPACITY             ] = "SET_CAPACITY",
-        [ READ_REVERSE             ] = "READ_REVERSE",
-        [ WRITE_FILEMARKS          ] = "WRITE_FILEMARKS",
-        [ SPACE                    ] = "SPACE",
-        [ INQUIRY                  ] = "INQUIRY",
-        [ RECOVER_BUFFERED_DATA    ] = "RECOVER_BUFFERED_DATA",
-        [ MAINTENANCE_IN           ] = "MAINTENANCE_IN",
-        [ MAINTENANCE_OUT          ] = "MAINTENANCE_OUT",
-        [ MODE_SELECT              ] = "MODE_SELECT",
-        [ RESERVE                  ] = "RESERVE",
-        [ RELEASE                  ] = "RELEASE",
-        [ COPY                     ] = "COPY",
-        [ ERASE                    ] = "ERASE",
-        [ MODE_SENSE               ] = "MODE_SENSE",
-        [ START_STOP               ] = "START_STOP/LOAD_UNLOAD",
-        /* LOAD_UNLOAD and START_STOP use the same operation code */
-        [ RECEIVE_DIAGNOSTIC       ] = "RECEIVE_DIAGNOSTIC",
-        [ SEND_DIAGNOSTIC          ] = "SEND_DIAGNOSTIC",
-        [ ALLOW_MEDIUM_REMOVAL     ] = "ALLOW_MEDIUM_REMOVAL",
-        [ READ_CAPACITY_10         ] = "READ_CAPACITY_10",
-        [ READ_10                  ] = "READ_10",
-        [ WRITE_10                 ] = "WRITE_10",
-        [ SEEK_10                  ] = "SEEK_10/POSITION_TO_ELEMENT",
-        /* SEEK_10 and POSITION_TO_ELEMENT use the same operation code */
-        [ WRITE_VERIFY_10          ] = "WRITE_VERIFY_10",
-        [ VERIFY_10                ] = "VERIFY_10",
-        [ SEARCH_HIGH              ] = "SEARCH_HIGH",
-        [ SEARCH_EQUAL             ] = "SEARCH_EQUAL",
-        [ SEARCH_LOW               ] = "SEARCH_LOW",
-        [ SET_LIMITS               ] = "SET_LIMITS",
-        [ PRE_FETCH                ] = "PRE_FETCH/READ_POSITION",
-        /* READ_POSITION and PRE_FETCH use the same operation code */
-        [ SYNCHRONIZE_CACHE        ] = "SYNCHRONIZE_CACHE",
-        [ LOCK_UNLOCK_CACHE        ] = "LOCK_UNLOCK_CACHE",
-        [ READ_DEFECT_DATA         ] = "READ_DEFECT_DATA/INITIALIZE_ELEMENT_STATUS_WITH_RANGE",
-        /* READ_DEFECT_DATA and INITIALIZE_ELEMENT_STATUS_WITH_RANGE use the same operation code */
-        [ MEDIUM_SCAN              ] = "MEDIUM_SCAN",
-        [ COMPARE                  ] = "COMPARE",
-        [ COPY_VERIFY              ] = "COPY_VERIFY",
-        [ WRITE_BUFFER             ] = "WRITE_BUFFER",
-        [ READ_BUFFER              ] = "READ_BUFFER",
-        [ UPDATE_BLOCK             ] = "UPDATE_BLOCK",
-        [ READ_LONG_10             ] = "READ_LONG_10",
-        [ WRITE_LONG_10            ] = "WRITE_LONG_10",
-        [ CHANGE_DEFINITION        ] = "CHANGE_DEFINITION",
-        [ WRITE_SAME_10            ] = "WRITE_SAME_10",
-        [ UNMAP                    ] = "UNMAP",
-        [ READ_TOC                 ] = "READ_TOC",
-        [ REPORT_DENSITY_SUPPORT   ] = "REPORT_DENSITY_SUPPORT",
-        [ SANITIZE                 ] = "SANITIZE",
-        [ GET_CONFIGURATION        ] = "GET_CONFIGURATION",
-        [ LOG_SELECT               ] = "LOG_SELECT",
-        [ LOG_SENSE                ] = "LOG_SENSE",
-        [ MODE_SELECT_10           ] = "MODE_SELECT_10",
-        [ RESERVE_10               ] = "RESERVE_10",
-        [ RELEASE_10               ] = "RELEASE_10",
-        [ MODE_SENSE_10            ] = "MODE_SENSE_10",
-        [ PERSISTENT_RESERVE_IN    ] = "PERSISTENT_RESERVE_IN",
-        [ PERSISTENT_RESERVE_OUT   ] = "PERSISTENT_RESERVE_OUT",
-        [ WRITE_FILEMARKS_16       ] = "WRITE_FILEMARKS_16",
-        [ EXTENDED_COPY            ] = "EXTENDED_COPY",
-        [ ATA_PASSTHROUGH_16       ] = "ATA_PASSTHROUGH_16",
-        [ ACCESS_CONTROL_IN        ] = "ACCESS_CONTROL_IN",
-        [ ACCESS_CONTROL_OUT       ] = "ACCESS_CONTROL_OUT",
-        [ READ_16                  ] = "READ_16",
-        [ COMPARE_AND_WRITE        ] = "COMPARE_AND_WRITE",
-        [ WRITE_16                 ] = "WRITE_16",
-        [ WRITE_VERIFY_16          ] = "WRITE_VERIFY_16",
-        [ VERIFY_16                ] = "VERIFY_16",
-        [ PRE_FETCH_16             ] = "PRE_FETCH_16",
-        [ SYNCHRONIZE_CACHE_16     ] = "SPACE_16/SYNCHRONIZE_CACHE_16",
-        /* SPACE_16 and SYNCHRONIZE_CACHE_16 use the same operation code */
-        [ LOCATE_16                ] = "LOCATE_16",
-        [ WRITE_SAME_16            ] = "ERASE_16/WRITE_SAME_16",
-        /* ERASE_16 and WRITE_SAME_16 use the same operation code */
-        [ SERVICE_ACTION_IN_16     ] = "SERVICE_ACTION_IN_16",
-        [ WRITE_LONG_16            ] = "WRITE_LONG_16",
-        [ REPORT_LUNS              ] = "REPORT_LUNS",
-        [ ATA_PASSTHROUGH_12       ] = "BLANK/ATA_PASSTHROUGH_12",
-        [ MOVE_MEDIUM              ] = "MOVE_MEDIUM",
-        [ EXCHANGE_MEDIUM          ] = "EXCHANGE MEDIUM",
-        [ READ_12                  ] = "READ_12",
-        [ WRITE_12                 ] = "WRITE_12",
-        [ ERASE_12                 ] = "ERASE_12/GET_PERFORMANCE",
-        /* ERASE_12 and GET_PERFORMANCE use the same operation code */
-        [ SERVICE_ACTION_IN_12     ] = "SERVICE_ACTION_IN_12",
-        [ WRITE_VERIFY_12          ] = "WRITE_VERIFY_12",
-        [ VERIFY_12                ] = "VERIFY_12",
-        [ SEARCH_HIGH_12           ] = "SEARCH_HIGH_12",
-        [ SEARCH_EQUAL_12          ] = "SEARCH_EQUAL_12",
-        [ SEARCH_LOW_12            ] = "SEARCH_LOW_12",
-        [ READ_ELEMENT_STATUS      ] = "READ_ELEMENT_STATUS",
-        [ SEND_VOLUME_TAG          ] = "SEND_VOLUME_TAG/SET_STREAMING",
-        /* SEND_VOLUME_TAG and SET_STREAMING use the same operation code */
-        [ READ_CD                  ] = "READ_CD",
-        [ READ_DEFECT_DATA_12      ] = "READ_DEFECT_DATA_12",
-        [ READ_DVD_STRUCTURE       ] = "READ_DVD_STRUCTURE",
-        [ RESERVE_TRACK            ] = "RESERVE_TRACK",
-        [ SEND_CUE_SHEET           ] = "SEND_CUE_SHEET",
-        [ SEND_DVD_STRUCTURE       ] = "SEND_DVD_STRUCTURE",
-        [ SET_CD_SPEED             ] = "SET_CD_SPEED",
-        [ SET_READ_AHEAD           ] = "SET_READ_AHEAD",
-        [ ALLOW_OVERWRITE          ] = "ALLOW_OVERWRITE",
-        [ MECHANISM_STATUS         ] = "MECHANISM_STATUS",
-    };
-
-    if (cmd >= ARRAY_SIZE(names) || names[cmd] == NULL)
-        return "*UNKNOWN*";
-    return names[cmd];
-}
-
-SCSIRequest *scsi_req_ref(SCSIRequest *req)
-{
-    assert(req->refcount > 0);
-    req->refcount++;
-    return req;
-}
-
-void scsi_req_unref(SCSIRequest *req)
-{
-    assert(req->refcount > 0);
-    if (--req->refcount == 0) {
-        SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, req->dev->qdev.parent_bus);
-        if (bus->info->free_request && req->hba_private) {
-            bus->info->free_request(bus, req->hba_private);
-        }
-        if (req->ops->free_req) {
-            req->ops->free_req(req);
-        }
-        g_free(req);
-    }
-}
-
-/* Tell the device that we finished processing this chunk of I/O.  It
-   will start the next chunk or complete the command.  */
-void scsi_req_continue(SCSIRequest *req)
-{
-    if (req->io_canceled) {
-        trace_scsi_req_continue_canceled(req->dev->id, req->lun, req->tag);
-        return;
-    }
-    trace_scsi_req_continue(req->dev->id, req->lun, req->tag);
-    if (req->cmd.mode == SCSI_XFER_TO_DEV) {
-        req->ops->write_data(req);
-    } else {
-        req->ops->read_data(req);
-    }
-}
-
-/* Called by the devices when data is ready for the HBA.  The HBA should
-   start a DMA operation to read or fill the device's data buffer.
-   Once it completes, calling scsi_req_continue will restart I/O.  */
-void scsi_req_data(SCSIRequest *req, int len)
-{
-    uint8_t *buf;
-    if (req->io_canceled) {
-        trace_scsi_req_data_canceled(req->dev->id, req->lun, req->tag, len);
-        return;
-    }
-    trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
-    assert(req->cmd.mode != SCSI_XFER_NONE);
-    if (!req->sg) {
-        req->resid -= len;
-        req->bus->info->transfer_data(req, len);
-        return;
-    }
-
-    /* If the device calls scsi_req_data and the HBA specified a
-     * scatter/gather list, the transfer has to happen in a single
-     * step.  */
-    assert(!req->dma_started);
-    req->dma_started = true;
-
-    buf = scsi_req_get_buf(req);
-    if (req->cmd.mode == SCSI_XFER_FROM_DEV) {
-        req->resid = dma_buf_read(buf, len, req->sg);
-    } else {
-        req->resid = dma_buf_write(buf, len, req->sg);
-    }
-    scsi_req_continue(req);
-}
-
-void scsi_req_print(SCSIRequest *req)
-{
-    FILE *fp = stderr;
-    int i;
-
-    fprintf(fp, "[%s id=%d] %s",
-            req->dev->qdev.parent_bus->name,
-            req->dev->id,
-            scsi_command_name(req->cmd.buf[0]));
-    for (i = 1; i < req->cmd.len; i++) {
-        fprintf(fp, " 0x%02x", req->cmd.buf[i]);
-    }
-    switch (req->cmd.mode) {
-    case SCSI_XFER_NONE:
-        fprintf(fp, " - none\n");
-        break;
-    case SCSI_XFER_FROM_DEV:
-        fprintf(fp, " - from-dev len=%zd\n", req->cmd.xfer);
-        break;
-    case SCSI_XFER_TO_DEV:
-        fprintf(fp, " - to-dev len=%zd\n", req->cmd.xfer);
-        break;
-    default:
-        fprintf(fp, " - Oops\n");
-        break;
-    }
-}
-
-void scsi_req_complete(SCSIRequest *req, int status)
-{
-    assert(req->status == -1);
-    req->status = status;
-
-    assert(req->sense_len <= sizeof(req->sense));
-    if (status == GOOD) {
-        req->sense_len = 0;
-    }
-
-    if (req->sense_len) {
-        memcpy(req->dev->sense, req->sense, req->sense_len);
-        req->dev->sense_len = req->sense_len;
-        req->dev->sense_is_ua = (req->ops == &reqops_unit_attention);
-    } else {
-        req->dev->sense_len = 0;
-        req->dev->sense_is_ua = false;
-    }
-
-    /*
-     * Unit attention state is now stored in the device's sense buffer
-     * if the HBA didn't do autosense.  Clear the pending unit attention
-     * flags.
-     */
-    scsi_clear_unit_attention(req);
-
-    scsi_req_ref(req);
-    scsi_req_dequeue(req);
-    req->bus->info->complete(req, req->status, req->resid);
-    scsi_req_unref(req);
-}
-
-void scsi_req_cancel(SCSIRequest *req)
-{
-    trace_scsi_req_cancel(req->dev->id, req->lun, req->tag);
-    if (!req->enqueued) {
-        return;
-    }
-    scsi_req_ref(req);
-    scsi_req_dequeue(req);
-    req->io_canceled = true;
-    if (req->ops->cancel_io) {
-        req->ops->cancel_io(req);
-    }
-    if (req->bus->info->cancel) {
-        req->bus->info->cancel(req);
-    }
-    scsi_req_unref(req);
-}
-
-void scsi_req_abort(SCSIRequest *req, int status)
-{
-    if (!req->enqueued) {
-        return;
-    }
-    scsi_req_ref(req);
-    scsi_req_dequeue(req);
-    req->io_canceled = true;
-    if (req->ops->cancel_io) {
-        req->ops->cancel_io(req);
-    }
-    scsi_req_complete(req, status);
-    scsi_req_unref(req);
-}
-
-static int scsi_ua_precedence(SCSISense sense)
-{
-    if (sense.key != UNIT_ATTENTION) {
-        return INT_MAX;
-    }
-    if (sense.asc == 0x29 && sense.ascq == 0x04) {
-        /* DEVICE INTERNAL RESET goes with POWER ON OCCURRED */
-        return 1;
-    } else if (sense.asc == 0x3F && sense.ascq == 0x01) {
-        /* MICROCODE HAS BEEN CHANGED goes with SCSI BUS RESET OCCURRED */
-        return 2;
-    } else if (sense.asc == 0x29 && (sense.ascq == 0x05 || sense.ascq == 0x06)) {
-        /* These two go with "all others". */
-        ;
-    } else if (sense.asc == 0x29 && sense.ascq <= 0x07) {
-        /* POWER ON, RESET OR BUS DEVICE RESET OCCURRED = 0
-         * POWER ON OCCURRED = 1
-         * SCSI BUS RESET OCCURRED = 2
-         * BUS DEVICE RESET FUNCTION OCCURRED = 3
-         * I_T NEXUS LOSS OCCURRED = 7
-         */
-        return sense.ascq;
-    } else if (sense.asc == 0x2F && sense.ascq == 0x01) {
-        /* COMMANDS CLEARED BY POWER LOSS NOTIFICATION  */
-        return 8;
-    }
-    return (sense.asc << 8) | sense.ascq;
-}
-
-void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense)
-{
-    int prec1, prec2;
-    if (sense.key != UNIT_ATTENTION) {
-        return;
-    }
-    trace_scsi_device_set_ua(sdev->id, sdev->lun, sense.key,
-                             sense.asc, sense.ascq);
-
-    /*
-     * Override a pre-existing unit attention condition, except for a more
-     * important reset condition.
-    */
-    prec1 = scsi_ua_precedence(sdev->unit_attention);
-    prec2 = scsi_ua_precedence(sense);
-    if (prec2 < prec1) {
-        sdev->unit_attention = sense;
-    }
-}
-
-void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense)
-{
-    SCSIRequest *req;
-
-    while (!QTAILQ_EMPTY(&sdev->requests)) {
-        req = QTAILQ_FIRST(&sdev->requests);
-        scsi_req_cancel(req);
-    }
-
-    scsi_device_set_ua(sdev, sense);
-}
-
-static char *scsibus_get_dev_path(DeviceState *dev)
-{
-    SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev);
-    DeviceState *hba = dev->parent_bus->parent;
-    char *id;
-    char *path;
-
-    id = qdev_get_dev_path(hba);
-    if (id) {
-        path = g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun);
-    } else {
-        path = g_strdup_printf("%d:%d:%d", d->channel, d->id, d->lun);
-    }
-    g_free(id);
-    return path;
-}
-
-static char *scsibus_get_fw_dev_path(DeviceState *dev)
-{
-    SCSIDevice *d = SCSI_DEVICE(dev);
-    return g_strdup_printf("channel@%x/%s@%x,%x", d->channel,
-                           qdev_fw_name(dev), d->id, d->lun);
-}
-
-SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun)
-{
-    BusChild *kid;
-    SCSIDevice *target_dev = NULL;
-
-    QTAILQ_FOREACH_REVERSE(kid, &bus->qbus.children, ChildrenHead, sibling) {
-        DeviceState *qdev = kid->child;
-        SCSIDevice *dev = SCSI_DEVICE(qdev);
-
-        if (dev->channel == channel && dev->id == id) {
-            if (dev->lun == lun) {
-                return dev;
-            }
-            target_dev = dev;
-        }
-    }
-    return target_dev;
-}
-
-/* SCSI request list.  For simplicity, pv points to the whole device */
-
-static void put_scsi_requests(QEMUFile *f, void *pv, size_t size)
-{
-    SCSIDevice *s = pv;
-    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus);
-    SCSIRequest *req;
-
-    QTAILQ_FOREACH(req, &s->requests, next) {
-        assert(!req->io_canceled);
-        assert(req->status == -1);
-        assert(req->enqueued);
-
-        qemu_put_sbyte(f, req->retry ? 1 : 2);
-        qemu_put_buffer(f, req->cmd.buf, sizeof(req->cmd.buf));
-        qemu_put_be32s(f, &req->tag);
-        qemu_put_be32s(f, &req->lun);
-        if (bus->info->save_request) {
-            bus->info->save_request(f, req);
-        }
-        if (req->ops->save_request) {
-            req->ops->save_request(f, req);
-        }
-    }
-    qemu_put_sbyte(f, 0);
-}
-
-static int get_scsi_requests(QEMUFile *f, void *pv, size_t size)
-{
-    SCSIDevice *s = pv;
-    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus);
-    int8_t sbyte;
-
-    while ((sbyte = qemu_get_sbyte(f)) > 0) {
-        uint8_t buf[SCSI_CMD_BUF_SIZE];
-        uint32_t tag;
-        uint32_t lun;
-        SCSIRequest *req;
-
-        qemu_get_buffer(f, buf, sizeof(buf));
-        qemu_get_be32s(f, &tag);
-        qemu_get_be32s(f, &lun);
-        req = scsi_req_new(s, tag, lun, buf, NULL);
-        req->retry = (sbyte == 1);
-        if (bus->info->load_request) {
-            req->hba_private = bus->info->load_request(f, req);
-        }
-        if (req->ops->load_request) {
-            req->ops->load_request(f, req);
-        }
-
-        /* Just restart it later.  */
-        scsi_req_enqueue_internal(req);
-
-        /* At this point, the request will be kept alive by the reference
-         * added by scsi_req_enqueue_internal, so we can release our reference.
-         * The HBA of course will add its own reference in the load_request
-         * callback if it needs to hold on the SCSIRequest.
-         */
-        scsi_req_unref(req);
-    }
-
-    return 0;
-}
-
-static int scsi_qdev_unplug(DeviceState *qdev)
-{
-    SCSIDevice *dev = SCSI_DEVICE(qdev);
-    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
-
-    if (bus->info->hot_unplug) {
-        bus->info->hot_unplug(bus, dev);
-    }
-    return qdev_simple_unplug_cb(qdev);
-}
-
-static const VMStateInfo vmstate_info_scsi_requests = {
-    .name = "scsi-requests",
-    .get  = get_scsi_requests,
-    .put  = put_scsi_requests,
-};
-
-const VMStateDescription vmstate_scsi_device = {
-    .name = "SCSIDevice",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT8(unit_attention.key, SCSIDevice),
-        VMSTATE_UINT8(unit_attention.asc, SCSIDevice),
-        VMSTATE_UINT8(unit_attention.ascq, SCSIDevice),
-        VMSTATE_BOOL(sense_is_ua, SCSIDevice),
-        VMSTATE_UINT8_ARRAY(sense, SCSIDevice, SCSI_SENSE_BUF_SIZE),
-        VMSTATE_UINT32(sense_len, SCSIDevice),
-        {
-            .name         = "requests",
-            .version_id   = 0,
-            .field_exists = NULL,
-            .size         = 0,   /* ouch */
-            .info         = &vmstate_info_scsi_requests,
-            .flags        = VMS_SINGLE,
-            .offset       = 0,
-        },
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void scsi_device_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *k = DEVICE_CLASS(klass);
-    k->bus_type = TYPE_SCSI_BUS;
-    k->init     = scsi_qdev_init;
-    k->unplug   = scsi_qdev_unplug;
-    k->exit     = scsi_qdev_exit;
-    k->props    = scsi_props;
-}
-
-static const TypeInfo scsi_device_type_info = {
-    .name = TYPE_SCSI_DEVICE,
-    .parent = TYPE_DEVICE,
-    .instance_size = sizeof(SCSIDevice),
-    .abstract = true,
-    .class_size = sizeof(SCSIDeviceClass),
-    .class_init = scsi_device_class_init,
-};
-
-static void scsi_register_types(void)
-{
-    type_register_static(&scsi_bus_info);
-    type_register_static(&scsi_device_type_info);
-}
-
-type_init(scsi_register_types)
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
deleted file mode 100644 (file)
index f52bd11..0000000
+++ /dev/null
@@ -1,2526 +0,0 @@
-/*
- * SCSI Device emulation
- *
- * Copyright (c) 2006 CodeSourcery.
- * Based on code by Fabrice Bellard
- *
- * Written by Paul Brook
- * Modifications:
- *  2009-Dec-12 Artyom Tarasenko : implemented stamdard inquiry for the case
- *                                 when the allocation length of CDB is smaller
- *                                 than 36.
- *  2009-Oct-13 Artyom Tarasenko : implemented the block descriptor in the
- *                                 MODE SENSE response.
- *
- * This code is licensed under the LGPL.
- *
- * Note that this file only handles the SCSI architecture model and device
- * commands.  Emulation of interface/link layer protocols is handled by
- * the host adapter emulator.
- */
-
-//#define DEBUG_SCSI
-
-#ifdef DEBUG_SCSI
-#define DPRINTF(fmt, ...) \
-do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#endif
-
-#include "qemu-common.h"
-#include "qemu/error-report.h"
-#include "hw/scsi/scsi.h"
-#include "block/scsi.h"
-#include "sysemu/sysemu.h"
-#include "sysemu/blockdev.h"
-#include "hw/block/block.h"
-#include "sysemu/dma.h"
-
-#ifdef __linux
-#include <scsi/sg.h>
-#endif
-
-#define SCSI_DMA_BUF_SIZE           131072
-#define SCSI_MAX_INQUIRY_LEN        256
-#define SCSI_MAX_MODE_LEN           256
-
-#define DEFAULT_DISCARD_GRANULARITY 4096
-
-typedef struct SCSIDiskState SCSIDiskState;
-
-typedef struct SCSIDiskReq {
-    SCSIRequest req;
-    /* Both sector and sector_count are in terms of qemu 512 byte blocks.  */
-    uint64_t sector;
-    uint32_t sector_count;
-    uint32_t buflen;
-    bool started;
-    struct iovec iov;
-    QEMUIOVector qiov;
-    BlockAcctCookie acct;
-} SCSIDiskReq;
-
-#define SCSI_DISK_F_REMOVABLE   0
-#define SCSI_DISK_F_DPOFUA      1
-
-struct SCSIDiskState
-{
-    SCSIDevice qdev;
-    uint32_t features;
-    bool media_changed;
-    bool media_event;
-    bool eject_request;
-    uint64_t wwn;
-    QEMUBH *bh;
-    char *version;
-    char *serial;
-    char *vendor;
-    char *product;
-    bool tray_open;
-    bool tray_locked;
-};
-
-static int scsi_handle_rw_error(SCSIDiskReq *r, int error);
-
-static void scsi_free_request(SCSIRequest *req)
-{
-    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
-
-    qemu_vfree(r->iov.iov_base);
-}
-
-/* Helper function for command completion with sense.  */
-static void scsi_check_condition(SCSIDiskReq *r, SCSISense sense)
-{
-    DPRINTF("Command complete tag=0x%x sense=%d/%d/%d\n",
-            r->req.tag, sense.key, sense.asc, sense.ascq);
-    scsi_req_build_sense(&r->req, sense);
-    scsi_req_complete(&r->req, CHECK_CONDITION);
-}
-
-/* Cancel a pending data transfer.  */
-static void scsi_cancel_io(SCSIRequest *req)
-{
-    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
-
-    DPRINTF("Cancel tag=0x%x\n", req->tag);
-    if (r->req.aiocb) {
-        bdrv_aio_cancel(r->req.aiocb);
-
-        /* This reference was left in by scsi_*_data.  We take ownership of
-         * it the moment scsi_req_cancel is called, independent of whether
-         * bdrv_aio_cancel completes the request or not.  */
-        scsi_req_unref(&r->req);
-    }
-    r->req.aiocb = NULL;
-}
-
-static uint32_t scsi_init_iovec(SCSIDiskReq *r, size_t size)
-{
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
-
-    if (!r->iov.iov_base) {
-        r->buflen = size;
-        r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen);
-    }
-    r->iov.iov_len = MIN(r->sector_count * 512, r->buflen);
-    qemu_iovec_init_external(&r->qiov, &r->iov, 1);
-    return r->qiov.size / 512;
-}
-
-static void scsi_disk_save_request(QEMUFile *f, SCSIRequest *req)
-{
-    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
-
-    qemu_put_be64s(f, &r->sector);
-    qemu_put_be32s(f, &r->sector_count);
-    qemu_put_be32s(f, &r->buflen);
-    if (r->buflen) {
-        if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
-            qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len);
-        } else if (!req->retry) {
-            uint32_t len = r->iov.iov_len;
-            qemu_put_be32s(f, &len);
-            qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len);
-        }
-    }
-}
-
-static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req)
-{
-    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
-
-    qemu_get_be64s(f, &r->sector);
-    qemu_get_be32s(f, &r->sector_count);
-    qemu_get_be32s(f, &r->buflen);
-    if (r->buflen) {
-        scsi_init_iovec(r, r->buflen);
-        if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
-            qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len);
-        } else if (!r->req.retry) {
-            uint32_t len;
-            qemu_get_be32s(f, &len);
-            r->iov.iov_len = len;
-            assert(r->iov.iov_len <= r->buflen);
-            qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len);
-        }
-    }
-
-    qemu_iovec_init_external(&r->qiov, &r->iov, 1);
-}
-
-static void scsi_aio_complete(void *opaque, int ret)
-{
-    SCSIDiskReq *r = (SCSIDiskReq *)opaque;
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
-
-    assert(r->req.aiocb != NULL);
-    r->req.aiocb = NULL;
-    bdrv_acct_done(s->qdev.conf.bs, &r->acct);
-    if (r->req.io_canceled) {
-        goto done;
-    }
-
-    if (ret < 0) {
-        if (scsi_handle_rw_error(r, -ret)) {
-            goto done;
-        }
-    }
-
-    scsi_req_complete(&r->req, GOOD);
-
-done:
-    if (!r->req.io_canceled) {
-        scsi_req_unref(&r->req);
-    }
-}
-
-static bool scsi_is_cmd_fua(SCSICommand *cmd)
-{
-    switch (cmd->buf[0]) {
-    case READ_10:
-    case READ_12:
-    case READ_16:
-    case WRITE_10:
-    case WRITE_12:
-    case WRITE_16:
-        return (cmd->buf[1] & 8) != 0;
-
-    case VERIFY_10:
-    case VERIFY_12:
-    case VERIFY_16:
-    case WRITE_VERIFY_10:
-    case WRITE_VERIFY_12:
-    case WRITE_VERIFY_16:
-        return true;
-
-    case READ_6:
-    case WRITE_6:
-    default:
-        return false;
-    }
-}
-
-static void scsi_write_do_fua(SCSIDiskReq *r)
-{
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
-
-    if (r->req.io_canceled) {
-        goto done;
-    }
-
-    if (scsi_is_cmd_fua(&r->req.cmd)) {
-        bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
-        r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_aio_complete, r);
-        return;
-    }
-
-    scsi_req_complete(&r->req, GOOD);
-
-done:
-    if (!r->req.io_canceled) {
-        scsi_req_unref(&r->req);
-    }
-}
-
-static void scsi_dma_complete(void *opaque, int ret)
-{
-    SCSIDiskReq *r = (SCSIDiskReq *)opaque;
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
-
-    assert(r->req.aiocb != NULL);
-    r->req.aiocb = NULL;
-    bdrv_acct_done(s->qdev.conf.bs, &r->acct);
-    if (r->req.io_canceled) {
-        goto done;
-    }
-
-    if (ret < 0) {
-        if (scsi_handle_rw_error(r, -ret)) {
-            goto done;
-        }
-    }
-
-    r->sector += r->sector_count;
-    r->sector_count = 0;
-    if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
-        scsi_write_do_fua(r);
-        return;
-    } else {
-        scsi_req_complete(&r->req, GOOD);
-    }
-
-done:
-    if (!r->req.io_canceled) {
-        scsi_req_unref(&r->req);
-    }
-}
-
-static void scsi_read_complete(void * opaque, int ret)
-{
-    SCSIDiskReq *r = (SCSIDiskReq *)opaque;
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
-    int n;
-
-    assert(r->req.aiocb != NULL);
-    r->req.aiocb = NULL;
-    bdrv_acct_done(s->qdev.conf.bs, &r->acct);
-    if (r->req.io_canceled) {
-        goto done;
-    }
-
-    if (ret < 0) {
-        if (scsi_handle_rw_error(r, -ret)) {
-            goto done;
-        }
-    }
-
-    DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->qiov.size);
-
-    n = r->qiov.size / 512;
-    r->sector += n;
-    r->sector_count -= n;
-    scsi_req_data(&r->req, r->qiov.size);
-
-done:
-    if (!r->req.io_canceled) {
-        scsi_req_unref(&r->req);
-    }
-}
-
-/* Actually issue a read to the block device.  */
-static void scsi_do_read(void *opaque, int ret)
-{
-    SCSIDiskReq *r = opaque;
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
-    uint32_t n;
-
-    if (r->req.aiocb != NULL) {
-        r->req.aiocb = NULL;
-        bdrv_acct_done(s->qdev.conf.bs, &r->acct);
-    }
-    if (r->req.io_canceled) {
-        goto done;
-    }
-
-    if (ret < 0) {
-        if (scsi_handle_rw_error(r, -ret)) {
-            goto done;
-        }
-    }
-
-    /* The request is used as the AIO opaque value, so add a ref.  */
-    scsi_req_ref(&r->req);
-
-    if (r->req.sg) {
-        dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ);
-        r->req.resid -= r->req.sg->size;
-        r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector,
-                                     scsi_dma_complete, r);
-    } else {
-        n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
-        bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
-        r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
-                                      scsi_read_complete, r);
-    }
-
-done:
-    if (!r->req.io_canceled) {
-        scsi_req_unref(&r->req);
-    }
-}
-
-/* Read more data from scsi device into buffer.  */
-static void scsi_read_data(SCSIRequest *req)
-{
-    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
-    bool first;
-
-    DPRINTF("Read sector_count=%d\n", r->sector_count);
-    if (r->sector_count == 0) {
-        /* This also clears the sense buffer for REQUEST SENSE.  */
-        scsi_req_complete(&r->req, GOOD);
-        return;
-    }
-
-    /* No data transfer may already be in progress */
-    assert(r->req.aiocb == NULL);
-
-    /* The request is used as the AIO opaque value, so add a ref.  */
-    scsi_req_ref(&r->req);
-    if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
-        DPRINTF("Data transfer direction invalid\n");
-        scsi_read_complete(r, -EINVAL);
-        return;
-    }
-
-    if (s->tray_open) {
-        scsi_read_complete(r, -ENOMEDIUM);
-        return;
-    }
-
-    first = !r->started;
-    r->started = true;
-    if (first && scsi_is_cmd_fua(&r->req.cmd)) {
-        bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
-        r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_do_read, r);
-    } else {
-        scsi_do_read(r, 0);
-    }
-}
-
-/*
- * scsi_handle_rw_error has two return values.  0 means that the error
- * must be ignored, 1 means that the error has been processed and the
- * caller should not do anything else for this request.  Note that
- * scsi_handle_rw_error always manages its reference counts, independent
- * of the return value.
- */
-static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
-{
-    bool is_read = (r->req.cmd.xfer == SCSI_XFER_FROM_DEV);
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
-    BlockErrorAction action = bdrv_get_error_action(s->qdev.conf.bs, is_read, error);
-
-    if (action == BDRV_ACTION_REPORT) {
-        switch (error) {
-        case ENOMEDIUM:
-            scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
-            break;
-        case ENOMEM:
-            scsi_check_condition(r, SENSE_CODE(TARGET_FAILURE));
-            break;
-        case EINVAL:
-            scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
-            break;
-        default:
-            scsi_check_condition(r, SENSE_CODE(IO_ERROR));
-            break;
-        }
-    }
-    bdrv_error_action(s->qdev.conf.bs, action, is_read, error);
-    if (action == BDRV_ACTION_STOP) {
-        scsi_req_retry(&r->req);
-    }
-    return action != BDRV_ACTION_IGNORE;
-}
-
-static void scsi_write_complete(void * opaque, int ret)
-{
-    SCSIDiskReq *r = (SCSIDiskReq *)opaque;
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
-    uint32_t n;
-
-    if (r->req.aiocb != NULL) {
-        r->req.aiocb = NULL;
-        bdrv_acct_done(s->qdev.conf.bs, &r->acct);
-    }
-    if (r->req.io_canceled) {
-        goto done;
-    }
-
-    if (ret < 0) {
-        if (scsi_handle_rw_error(r, -ret)) {
-            goto done;
-        }
-    }
-
-    n = r->qiov.size / 512;
-    r->sector += n;
-    r->sector_count -= n;
-    if (r->sector_count == 0) {
-        scsi_write_do_fua(r);
-        return;
-    } else {
-        scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
-        DPRINTF("Write complete tag=0x%x more=%zd\n", r->req.tag, r->qiov.size);
-        scsi_req_data(&r->req, r->qiov.size);
-    }
-
-done:
-    if (!r->req.io_canceled) {
-        scsi_req_unref(&r->req);
-    }
-}
-
-static void scsi_write_data(SCSIRequest *req)
-{
-    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
-    uint32_t n;
-
-    /* No data transfer may already be in progress */
-    assert(r->req.aiocb == NULL);
-
-    /* The request is used as the AIO opaque value, so add a ref.  */
-    scsi_req_ref(&r->req);
-    if (r->req.cmd.mode != SCSI_XFER_TO_DEV) {
-        DPRINTF("Data transfer direction invalid\n");
-        scsi_write_complete(r, -EINVAL);
-        return;
-    }
-
-    if (!r->req.sg && !r->qiov.size) {
-        /* Called for the first time.  Ask the driver to send us more data.  */
-        r->started = true;
-        scsi_write_complete(r, 0);
-        return;
-    }
-    if (s->tray_open) {
-        scsi_write_complete(r, -ENOMEDIUM);
-        return;
-    }
-
-    if (r->req.cmd.buf[0] == VERIFY_10 || r->req.cmd.buf[0] == VERIFY_12 ||
-        r->req.cmd.buf[0] == VERIFY_16) {
-        if (r->req.sg) {
-            scsi_dma_complete(r, 0);
-        } else {
-            scsi_write_complete(r, 0);
-        }
-        return;
-    }
-
-    if (r->req.sg) {
-        dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_WRITE);
-        r->req.resid -= r->req.sg->size;
-        r->req.aiocb = dma_bdrv_write(s->qdev.conf.bs, r->req.sg, r->sector,
-                                      scsi_dma_complete, r);
-    } else {
-        n = r->qiov.size / 512;
-        bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_WRITE);
-        r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, r->sector, &r->qiov, n,
-                                       scsi_write_complete, r);
-    }
-}
-
-/* Return a pointer to the data buffer.  */
-static uint8_t *scsi_get_buf(SCSIRequest *req)
-{
-    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
-
-    return (uint8_t *)r->iov.iov_base;
-}
-
-static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
-{
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
-    int buflen = 0;
-    int start;
-
-    if (req->cmd.buf[1] & 0x1) {
-        /* Vital product data */
-        uint8_t page_code = req->cmd.buf[2];
-
-        outbuf[buflen++] = s->qdev.type & 0x1f;
-        outbuf[buflen++] = page_code ; // this page
-        outbuf[buflen++] = 0x00;
-        outbuf[buflen++] = 0x00;
-        start = buflen;
-
-        switch (page_code) {
-        case 0x00: /* Supported page codes, mandatory */
-        {
-            DPRINTF("Inquiry EVPD[Supported pages] "
-                    "buffer size %zd\n", req->cmd.xfer);
-            outbuf[buflen++] = 0x00; // list of supported pages (this page)
-            if (s->serial) {
-                outbuf[buflen++] = 0x80; // unit serial number
-            }
-            outbuf[buflen++] = 0x83; // device identification
-            if (s->qdev.type == TYPE_DISK) {
-                outbuf[buflen++] = 0xb0; // block limits
-                outbuf[buflen++] = 0xb2; // thin provisioning
-            }
-            break;
-        }
-        case 0x80: /* Device serial number, optional */
-        {
-            int l;
-
-            if (!s->serial) {
-                DPRINTF("Inquiry (EVPD[Serial number] not supported\n");
-                return -1;
-            }
-
-            l = strlen(s->serial);
-            if (l > 20) {
-                l = 20;
-            }
-
-            DPRINTF("Inquiry EVPD[Serial number] "
-                    "buffer size %zd\n", req->cmd.xfer);
-            memcpy(outbuf+buflen, s->serial, l);
-            buflen += l;
-            break;
-        }
-
-        case 0x83: /* Device identification page, mandatory */
-        {
-            const char *str = s->serial ?: bdrv_get_device_name(s->qdev.conf.bs);
-            int max_len = s->serial ? 20 : 255 - 8;
-            int id_len = strlen(str);
-
-            if (id_len > max_len) {
-                id_len = max_len;
-            }
-            DPRINTF("Inquiry EVPD[Device identification] "
-                    "buffer size %zd\n", req->cmd.xfer);
-
-            outbuf[buflen++] = 0x2; // ASCII
-            outbuf[buflen++] = 0;   // not officially assigned
-            outbuf[buflen++] = 0;   // reserved
-            outbuf[buflen++] = id_len; // length of data following
-            memcpy(outbuf+buflen, str, id_len);
-            buflen += id_len;
-
-            if (s->wwn) {
-                outbuf[buflen++] = 0x1; // Binary
-                outbuf[buflen++] = 0x3; // NAA
-                outbuf[buflen++] = 0;   // reserved
-                outbuf[buflen++] = 8;
-                stq_be_p(&outbuf[buflen], s->wwn);
-                buflen += 8;
-            }
-            break;
-        }
-        case 0xb0: /* block limits */
-        {
-            unsigned int unmap_sectors =
-                    s->qdev.conf.discard_granularity / s->qdev.blocksize;
-            unsigned int min_io_size =
-                    s->qdev.conf.min_io_size / s->qdev.blocksize;
-            unsigned int opt_io_size =
-                    s->qdev.conf.opt_io_size / s->qdev.blocksize;
-
-            if (s->qdev.type == TYPE_ROM) {
-                DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n",
-                        page_code);
-                return -1;
-            }
-            /* required VPD size with unmap support */
-            buflen = 0x40;
-            memset(outbuf + 4, 0, buflen - 4);
-
-            /* optimal transfer length granularity */
-            outbuf[6] = (min_io_size >> 8) & 0xff;
-            outbuf[7] = min_io_size & 0xff;
-
-            /* optimal transfer length */
-            outbuf[12] = (opt_io_size >> 24) & 0xff;
-            outbuf[13] = (opt_io_size >> 16) & 0xff;
-            outbuf[14] = (opt_io_size >> 8) & 0xff;
-            outbuf[15] = opt_io_size & 0xff;
-
-            /* optimal unmap granularity */
-            outbuf[28] = (unmap_sectors >> 24) & 0xff;
-            outbuf[29] = (unmap_sectors >> 16) & 0xff;
-            outbuf[30] = (unmap_sectors >> 8) & 0xff;
-            outbuf[31] = unmap_sectors & 0xff;
-            break;
-        }
-        case 0xb2: /* thin provisioning */
-        {
-            buflen = 8;
-            outbuf[4] = 0;
-            outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */
-            outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1;
-            outbuf[7] = 0;
-            break;
-        }
-        default:
-            return -1;
-        }
-        /* done with EVPD */
-        assert(buflen - start <= 255);
-        outbuf[start - 1] = buflen - start;
-        return buflen;
-    }
-
-    /* Standard INQUIRY data */
-    if (req->cmd.buf[2] != 0) {
-        return -1;
-    }
-
-    /* PAGE CODE == 0 */
-    buflen = req->cmd.xfer;
-    if (buflen > SCSI_MAX_INQUIRY_LEN) {
-        buflen = SCSI_MAX_INQUIRY_LEN;
-    }
-
-    outbuf[0] = s->qdev.type & 0x1f;
-    outbuf[1] = (s->features & (1 << SCSI_DISK_F_REMOVABLE)) ? 0x80 : 0;
-
-    strpadcpy((char *) &outbuf[16], 16, s->product, ' ');
-    strpadcpy((char *) &outbuf[8], 8, s->vendor, ' ');
-
-    memset(&outbuf[32], 0, 4);
-    memcpy(&outbuf[32], s->version, MIN(4, strlen(s->version)));
-    /*
-     * We claim conformance to SPC-3, which is required for guests
-     * to ask for modern features like READ CAPACITY(16) or the
-     * block characteristics VPD page by default.  Not all of SPC-3
-     * is actually implemented, but we're good enough.
-     */
-    outbuf[2] = 5;
-    outbuf[3] = 2 | 0x10; /* Format 2, HiSup */
-
-    if (buflen > 36) {
-        outbuf[4] = buflen - 5; /* Additional Length = (Len - 1) - 4 */
-    } else {
-        /* If the allocation length of CDB is too small,
-               the additional length is not adjusted */
-        outbuf[4] = 36 - 5;
-    }
-
-    /* Sync data transfer and TCQ.  */
-    outbuf[7] = 0x10 | (req->bus->info->tcq ? 0x02 : 0);
-    return buflen;
-}
-
-static inline bool media_is_dvd(SCSIDiskState *s)
-{
-    uint64_t nb_sectors;
-    if (s->qdev.type != TYPE_ROM) {
-        return false;
-    }
-    if (!bdrv_is_inserted(s->qdev.conf.bs)) {
-        return false;
-    }
-    bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
-    return nb_sectors > CD_MAX_SECTORS;
-}
-
-static inline bool media_is_cd(SCSIDiskState *s)
-{
-    uint64_t nb_sectors;
-    if (s->qdev.type != TYPE_ROM) {
-        return false;
-    }
-    if (!bdrv_is_inserted(s->qdev.conf.bs)) {
-        return false;
-    }
-    bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
-    return nb_sectors <= CD_MAX_SECTORS;
-}
-
-static int scsi_read_disc_information(SCSIDiskState *s, SCSIDiskReq *r,
-                                      uint8_t *outbuf)
-{
-    uint8_t type = r->req.cmd.buf[1] & 7;
-
-    if (s->qdev.type != TYPE_ROM) {
-        return -1;
-    }
-
-    /* Types 1/2 are only defined for Blu-Ray.  */
-    if (type != 0) {
-        scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
-        return -1;
-    }
-
-    memset(outbuf, 0, 34);
-    outbuf[1] = 32;
-    outbuf[2] = 0xe; /* last session complete, disc finalized */
-    outbuf[3] = 1;   /* first track on disc */
-    outbuf[4] = 1;   /* # of sessions */
-    outbuf[5] = 1;   /* first track of last session */
-    outbuf[6] = 1;   /* last track of last session */
-    outbuf[7] = 0x20; /* unrestricted use */
-    outbuf[8] = 0x00; /* CD-ROM or DVD-ROM */
-    /* 9-10-11: most significant byte corresponding bytes 4-5-6 */
-    /* 12-23: not meaningful for CD-ROM or DVD-ROM */
-    /* 24-31: disc bar code */
-    /* 32: disc application code */
-    /* 33: number of OPC tables */
-
-    return 34;
-}
-
-static int scsi_read_dvd_structure(SCSIDiskState *s, SCSIDiskReq *r,
-                                   uint8_t *outbuf)
-{
-    static const int rds_caps_size[5] = {
-        [0] = 2048 + 4,
-        [1] = 4 + 4,
-        [3] = 188 + 4,
-        [4] = 2048 + 4,
-    };
-
-    uint8_t media = r->req.cmd.buf[1];
-    uint8_t layer = r->req.cmd.buf[6];
-    uint8_t format = r->req.cmd.buf[7];
-    int size = -1;
-
-    if (s->qdev.type != TYPE_ROM) {
-        return -1;
-    }
-    if (media != 0) {
-        scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
-        return -1;
-    }
-
-    if (format != 0xff) {
-        if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
-            scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
-            return -1;
-        }
-        if (media_is_cd(s)) {
-            scsi_check_condition(r, SENSE_CODE(INCOMPATIBLE_FORMAT));
-            return -1;
-        }
-        if (format >= ARRAY_SIZE(rds_caps_size)) {
-            return -1;
-        }
-        size = rds_caps_size[format];
-        memset(outbuf, 0, size);
-    }
-
-    switch (format) {
-    case 0x00: {
-        /* Physical format information */
-        uint64_t nb_sectors;
-        if (layer != 0) {
-            goto fail;
-        }
-        bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
-
-        outbuf[4] = 1;   /* DVD-ROM, part version 1 */
-        outbuf[5] = 0xf; /* 120mm disc, minimum rate unspecified */
-        outbuf[6] = 1;   /* one layer, read-only (per MMC-2 spec) */
-        outbuf[7] = 0;   /* default densities */
-
-        stl_be_p(&outbuf[12], (nb_sectors >> 2) - 1); /* end sector */
-        stl_be_p(&outbuf[16], (nb_sectors >> 2) - 1); /* l0 end sector */
-        break;
-    }
-
-    case 0x01: /* DVD copyright information, all zeros */
-        break;
-
-    case 0x03: /* BCA information - invalid field for no BCA info */
-        return -1;
-
-    case 0x04: /* DVD disc manufacturing information, all zeros */
-        break;
-
-    case 0xff: { /* List capabilities */
-        int i;
-        size = 4;
-        for (i = 0; i < ARRAY_SIZE(rds_caps_size); i++) {
-            if (!rds_caps_size[i]) {
-                continue;
-            }
-            outbuf[size] = i;
-            outbuf[size + 1] = 0x40; /* Not writable, readable */
-            stw_be_p(&outbuf[size + 2], rds_caps_size[i]);
-            size += 4;
-        }
-        break;
-     }
-
-    default:
-        return -1;
-    }
-
-    /* Size of buffer, not including 2 byte size field */
-    stw_be_p(outbuf, size - 2);
-    return size;
-
-fail:
-    return -1;
-}
-
-static int scsi_event_status_media(SCSIDiskState *s, uint8_t *outbuf)
-{
-    uint8_t event_code, media_status;
-
-    media_status = 0;
-    if (s->tray_open) {
-        media_status = MS_TRAY_OPEN;
-    } else if (bdrv_is_inserted(s->qdev.conf.bs)) {
-        media_status = MS_MEDIA_PRESENT;
-    }
-
-    /* Event notification descriptor */
-    event_code = MEC_NO_CHANGE;
-    if (media_status != MS_TRAY_OPEN) {
-        if (s->media_event) {
-            event_code = MEC_NEW_MEDIA;
-            s->media_event = false;
-        } else if (s->eject_request) {
-            event_code = MEC_EJECT_REQUESTED;
-            s->eject_request = false;
-        }
-    }
-
-    outbuf[0] = event_code;
-    outbuf[1] = media_status;
-
-    /* These fields are reserved, just clear them. */
-    outbuf[2] = 0;
-    outbuf[3] = 0;
-    return 4;
-}
-
-static int scsi_get_event_status_notification(SCSIDiskState *s, SCSIDiskReq *r,
-                                              uint8_t *outbuf)
-{
-    int size;
-    uint8_t *buf = r->req.cmd.buf;
-    uint8_t notification_class_request = buf[4];
-    if (s->qdev.type != TYPE_ROM) {
-        return -1;
-    }
-    if ((buf[1] & 1) == 0) {
-        /* asynchronous */
-        return -1;
-    }
-
-    size = 4;
-    outbuf[0] = outbuf[1] = 0;
-    outbuf[3] = 1 << GESN_MEDIA; /* supported events */
-    if (notification_class_request & (1 << GESN_MEDIA)) {
-        outbuf[2] = GESN_MEDIA;
-        size += scsi_event_status_media(s, &outbuf[size]);
-    } else {
-        outbuf[2] = 0x80;
-    }
-    stw_be_p(outbuf, size - 4);
-    return size;
-}
-
-static int scsi_get_configuration(SCSIDiskState *s, uint8_t *outbuf)
-{
-    int current;
-
-    if (s->qdev.type != TYPE_ROM) {
-        return -1;
-    }
-    current = media_is_dvd(s) ? MMC_PROFILE_DVD_ROM : MMC_PROFILE_CD_ROM;
-    memset(outbuf, 0, 40);
-    stl_be_p(&outbuf[0], 36); /* Bytes after the data length field */
-    stw_be_p(&outbuf[6], current);
-    /* outbuf[8] - outbuf[19]: Feature 0 - Profile list */
-    outbuf[10] = 0x03; /* persistent, current */
-    outbuf[11] = 8; /* two profiles */
-    stw_be_p(&outbuf[12], MMC_PROFILE_DVD_ROM);
-    outbuf[14] = (current == MMC_PROFILE_DVD_ROM);
-    stw_be_p(&outbuf[16], MMC_PROFILE_CD_ROM);
-    outbuf[18] = (current == MMC_PROFILE_CD_ROM);
-    /* outbuf[20] - outbuf[31]: Feature 1 - Core feature */
-    stw_be_p(&outbuf[20], 1);
-    outbuf[22] = 0x08 | 0x03; /* version 2, persistent, current */
-    outbuf[23] = 8;
-    stl_be_p(&outbuf[24], 1); /* SCSI */
-    outbuf[28] = 1; /* DBE = 1, mandatory */
-    /* outbuf[32] - outbuf[39]: Feature 3 - Removable media feature */
-    stw_be_p(&outbuf[32], 3);
-    outbuf[34] = 0x08 | 0x03; /* version 2, persistent, current */
-    outbuf[35] = 4;
-    outbuf[36] = 0x39; /* tray, load=1, eject=1, unlocked at powerup, lock=1 */
-    /* TODO: Random readable, CD read, DVD read, drive serial number,
-       power management */
-    return 40;
-}
-
-static int scsi_emulate_mechanism_status(SCSIDiskState *s, uint8_t *outbuf)
-{
-    if (s->qdev.type != TYPE_ROM) {
-        return -1;
-    }
-    memset(outbuf, 0, 8);
-    outbuf[5] = 1; /* CD-ROM */
-    return 8;
-}
-
-static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf,
-                           int page_control)
-{
-    static const int mode_sense_valid[0x3f] = {
-        [MODE_PAGE_HD_GEOMETRY]            = (1 << TYPE_DISK),
-        [MODE_PAGE_FLEXIBLE_DISK_GEOMETRY] = (1 << TYPE_DISK),
-        [MODE_PAGE_CACHING]                = (1 << TYPE_DISK) | (1 << TYPE_ROM),
-        [MODE_PAGE_R_W_ERROR]              = (1 << TYPE_DISK) | (1 << TYPE_ROM),
-        [MODE_PAGE_AUDIO_CTL]              = (1 << TYPE_ROM),
-        [MODE_PAGE_CAPABILITIES]           = (1 << TYPE_ROM),
-    };
-
-    uint8_t *p = *p_outbuf + 2;
-    int length;
-
-    if ((mode_sense_valid[page] & (1 << s->qdev.type)) == 0) {
-        return -1;
-    }
-
-    /*
-     * If Changeable Values are requested, a mask denoting those mode parameters
-     * that are changeable shall be returned. As we currently don't support
-     * parameter changes via MODE_SELECT all bits are returned set to zero.
-     * The buffer was already menset to zero by the caller of this function.
-     *
-     * The offsets here are off by two compared to the descriptions in the
-     * SCSI specs, because those include a 2-byte header.  This is unfortunate,
-     * but it is done so that offsets are consistent within our implementation
-     * of MODE SENSE and MODE SELECT.  MODE SELECT has to deal with both
-     * 2-byte and 4-byte headers.
-     */
-    switch (page) {
-    case MODE_PAGE_HD_GEOMETRY:
-        length = 0x16;
-        if (page_control == 1) { /* Changeable Values */
-            break;
-        }
-        /* if a geometry hint is available, use it */
-        p[0] = (s->qdev.conf.cyls >> 16) & 0xff;
-        p[1] = (s->qdev.conf.cyls >> 8) & 0xff;
-        p[2] = s->qdev.conf.cyls & 0xff;
-        p[3] = s->qdev.conf.heads & 0xff;
-        /* Write precomp start cylinder, disabled */
-        p[4] = (s->qdev.conf.cyls >> 16) & 0xff;
-        p[5] = (s->qdev.conf.cyls >> 8) & 0xff;
-        p[6] = s->qdev.conf.cyls & 0xff;
-        /* Reduced current start cylinder, disabled */
-        p[7] = (s->qdev.conf.cyls >> 16) & 0xff;
-        p[8] = (s->qdev.conf.cyls >> 8) & 0xff;
-        p[9] = s->qdev.conf.cyls & 0xff;
-        /* Device step rate [ns], 200ns */
-        p[10] = 0;
-        p[11] = 200;
-        /* Landing zone cylinder */
-        p[12] = 0xff;
-        p[13] =  0xff;
-        p[14] = 0xff;
-        /* Medium rotation rate [rpm], 5400 rpm */
-        p[18] = (5400 >> 8) & 0xff;
-        p[19] = 5400 & 0xff;
-        break;
-
-    case MODE_PAGE_FLEXIBLE_DISK_GEOMETRY:
-        length = 0x1e;
-        if (page_control == 1) { /* Changeable Values */
-            break;
-        }
-        /* Transfer rate [kbit/s], 5Mbit/s */
-        p[0] = 5000 >> 8;
-        p[1] = 5000 & 0xff;
-        /* if a geometry hint is available, use it */
-        p[2] = s->qdev.conf.heads & 0xff;
-        p[3] = s->qdev.conf.secs & 0xff;
-        p[4] = s->qdev.blocksize >> 8;
-        p[6] = (s->qdev.conf.cyls >> 8) & 0xff;
-        p[7] = s->qdev.conf.cyls & 0xff;
-        /* Write precomp start cylinder, disabled */
-        p[8] = (s->qdev.conf.cyls >> 8) & 0xff;
-        p[9] = s->qdev.conf.cyls & 0xff;
-        /* Reduced current start cylinder, disabled */
-        p[10] = (s->qdev.conf.cyls >> 8) & 0xff;
-        p[11] = s->qdev.conf.cyls & 0xff;
-        /* Device step rate [100us], 100us */
-        p[12] = 0;
-        p[13] = 1;
-        /* Device step pulse width [us], 1us */
-        p[14] = 1;
-        /* Device head settle delay [100us], 100us */
-        p[15] = 0;
-        p[16] = 1;
-        /* Motor on delay [0.1s], 0.1s */
-        p[17] = 1;
-        /* Motor off delay [0.1s], 0.1s */
-        p[18] = 1;
-        /* Medium rotation rate [rpm], 5400 rpm */
-        p[26] = (5400 >> 8) & 0xff;
-        p[27] = 5400 & 0xff;
-        break;
-
-    case MODE_PAGE_CACHING:
-        length = 0x12;
-        if (page_control == 1 || /* Changeable Values */
-            bdrv_enable_write_cache(s->qdev.conf.bs)) {
-            p[0] = 4; /* WCE */
-        }
-        break;
-
-    case MODE_PAGE_R_W_ERROR:
-        length = 10;
-        if (page_control == 1) { /* Changeable Values */
-            break;
-        }
-        p[0] = 0x80; /* Automatic Write Reallocation Enabled */
-        if (s->qdev.type == TYPE_ROM) {
-            p[1] = 0x20; /* Read Retry Count */
-        }
-        break;
-
-    case MODE_PAGE_AUDIO_CTL:
-        length = 14;
-        break;
-
-    case MODE_PAGE_CAPABILITIES:
-        length = 0x14;
-        if (page_control == 1) { /* Changeable Values */
-            break;
-        }
-
-        p[0] = 0x3b; /* CD-R & CD-RW read */
-        p[1] = 0; /* Writing not supported */
-        p[2] = 0x7f; /* Audio, composite, digital out,
-                        mode 2 form 1&2, multi session */
-        p[3] = 0xff; /* CD DA, DA accurate, RW supported,
-                        RW corrected, C2 errors, ISRC,
-                        UPC, Bar code */
-        p[4] = 0x2d | (s->tray_locked ? 2 : 0);
-        /* Locking supported, jumper present, eject, tray */
-        p[5] = 0; /* no volume & mute control, no
-                     changer */
-        p[6] = (50 * 176) >> 8; /* 50x read speed */
-        p[7] = (50 * 176) & 0xff;
-        p[8] = 2 >> 8; /* Two volume levels */
-        p[9] = 2 & 0xff;
-        p[10] = 2048 >> 8; /* 2M buffer */
-        p[11] = 2048 & 0xff;
-        p[12] = (16 * 176) >> 8; /* 16x read speed current */
-        p[13] = (16 * 176) & 0xff;
-        p[16] = (16 * 176) >> 8; /* 16x write speed */
-        p[17] = (16 * 176) & 0xff;
-        p[18] = (16 * 176) >> 8; /* 16x write speed current */
-        p[19] = (16 * 176) & 0xff;
-        break;
-
-    default:
-        return -1;
-    }
-
-    assert(length < 256);
-    (*p_outbuf)[0] = page;
-    (*p_outbuf)[1] = length;
-    *p_outbuf += length + 2;
-    return length + 2;
-}
-
-static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
-{
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
-    uint64_t nb_sectors;
-    bool dbd;
-    int page, buflen, ret, page_control;
-    uint8_t *p;
-    uint8_t dev_specific_param;
-
-    dbd = (r->req.cmd.buf[1] & 0x8) != 0;
-    page = r->req.cmd.buf[2] & 0x3f;
-    page_control = (r->req.cmd.buf[2] & 0xc0) >> 6;
-    DPRINTF("Mode Sense(%d) (page %d, xfer %zd, page_control %d)\n",
-        (r->req.cmd.buf[0] == MODE_SENSE) ? 6 : 10, page, r->req.cmd.xfer, page_control);
-    memset(outbuf, 0, r->req.cmd.xfer);
-    p = outbuf;
-
-    if (s->qdev.type == TYPE_DISK) {
-        dev_specific_param = s->features & (1 << SCSI_DISK_F_DPOFUA) ? 0x10 : 0;
-        if (bdrv_is_read_only(s->qdev.conf.bs)) {
-            dev_specific_param |= 0x80; /* Readonly.  */
-        }
-    } else {
-        /* MMC prescribes that CD/DVD drives have no block descriptors,
-         * and defines no device-specific parameter.  */
-        dev_specific_param = 0x00;
-        dbd = true;
-    }
-
-    if (r->req.cmd.buf[0] == MODE_SENSE) {
-        p[1] = 0; /* Default media type.  */
-        p[2] = dev_specific_param;
-        p[3] = 0; /* Block descriptor length.  */
-        p += 4;
-    } else { /* MODE_SENSE_10 */
-        p[2] = 0; /* Default media type.  */
-        p[3] = dev_specific_param;
-        p[6] = p[7] = 0; /* Block descriptor length.  */
-        p += 8;
-    }
-
-    bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
-    if (!dbd && nb_sectors) {
-        if (r->req.cmd.buf[0] == MODE_SENSE) {
-            outbuf[3] = 8; /* Block descriptor length  */
-        } else { /* MODE_SENSE_10 */
-            outbuf[7] = 8; /* Block descriptor length  */
-        }
-        nb_sectors /= (s->qdev.blocksize / 512);
-        if (nb_sectors > 0xffffff) {
-            nb_sectors = 0;
-        }
-        p[0] = 0; /* media density code */
-        p[1] = (nb_sectors >> 16) & 0xff;
-        p[2] = (nb_sectors >> 8) & 0xff;
-        p[3] = nb_sectors & 0xff;
-        p[4] = 0; /* reserved */
-        p[5] = 0; /* bytes 5-7 are the sector size in bytes */
-        p[6] = s->qdev.blocksize >> 8;
-        p[7] = 0;
-        p += 8;
-    }
-
-    if (page_control == 3) {
-        /* Saved Values */
-        scsi_check_condition(r, SENSE_CODE(SAVING_PARAMS_NOT_SUPPORTED));
-        return -1;
-    }
-
-    if (page == 0x3f) {
-        for (page = 0; page <= 0x3e; page++) {
-            mode_sense_page(s, page, &p, page_control);
-        }
-    } else {
-        ret = mode_sense_page(s, page, &p, page_control);
-        if (ret == -1) {
-            return -1;
-        }
-    }
-
-    buflen = p - outbuf;
-    /*
-     * The mode data length field specifies the length in bytes of the
-     * following data that is available to be transferred. The mode data
-     * length does not include itself.
-     */
-    if (r->req.cmd.buf[0] == MODE_SENSE) {
-        outbuf[0] = buflen - 1;
-    } else { /* MODE_SENSE_10 */
-        outbuf[0] = ((buflen - 2) >> 8) & 0xff;
-        outbuf[1] = (buflen - 2) & 0xff;
-    }
-    return buflen;
-}
-
-static int scsi_disk_emulate_read_toc(SCSIRequest *req, uint8_t *outbuf)
-{
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
-    int start_track, format, msf, toclen;
-    uint64_t nb_sectors;
-
-    msf = req->cmd.buf[1] & 2;
-    format = req->cmd.buf[2] & 0xf;
-    start_track = req->cmd.buf[6];
-    bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
-    DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
-    nb_sectors /= s->qdev.blocksize / 512;
-    switch (format) {
-    case 0:
-        toclen = cdrom_read_toc(nb_sectors, outbuf, msf, start_track);
-        break;
-    case 1:
-        /* multi session : only a single session defined */
-        toclen = 12;
-        memset(outbuf, 0, 12);
-        outbuf[1] = 0x0a;
-        outbuf[2] = 0x01;
-        outbuf[3] = 0x01;
-        break;
-    case 2:
-        toclen = cdrom_read_toc_raw(nb_sectors, outbuf, msf, start_track);
-        break;
-    default:
-        return -1;
-    }
-    return toclen;
-}
-
-static int scsi_disk_emulate_start_stop(SCSIDiskReq *r)
-{
-    SCSIRequest *req = &r->req;
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
-    bool start = req->cmd.buf[4] & 1;
-    bool loej = req->cmd.buf[4] & 2; /* load on start, eject on !start */
-    int pwrcnd = req->cmd.buf[4] & 0xf0;
-
-    if (pwrcnd) {
-        /* eject/load only happens for power condition == 0 */
-        return 0;
-    }
-
-    if ((s->features & (1 << SCSI_DISK_F_REMOVABLE)) && loej) {
-        if (!start && !s->tray_open && s->tray_locked) {
-            scsi_check_condition(r,
-                                 bdrv_is_inserted(s->qdev.conf.bs)
-                                 ? SENSE_CODE(ILLEGAL_REQ_REMOVAL_PREVENTED)
-                                 : SENSE_CODE(NOT_READY_REMOVAL_PREVENTED));
-            return -1;
-        }
-
-        if (s->tray_open != !start) {
-            bdrv_eject(s->qdev.conf.bs, !start);
-            s->tray_open = !start;
-        }
-    }
-    return 0;
-}
-
-static void scsi_disk_emulate_read_data(SCSIRequest *req)
-{
-    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
-    int buflen = r->iov.iov_len;
-
-    if (buflen) {
-        DPRINTF("Read buf_len=%d\n", buflen);
-        r->iov.iov_len = 0;
-        r->started = true;
-        scsi_req_data(&r->req, buflen);
-        return;
-    }
-
-    /* This also clears the sense buffer for REQUEST SENSE.  */
-    scsi_req_complete(&r->req, GOOD);
-}
-
-static int scsi_disk_check_mode_select(SCSIDiskState *s, int page,
-                                       uint8_t *inbuf, int inlen)
-{
-    uint8_t mode_current[SCSI_MAX_MODE_LEN];
-    uint8_t mode_changeable[SCSI_MAX_MODE_LEN];
-    uint8_t *p;
-    int len, expected_len, changeable_len, i;
-
-    /* The input buffer does not include the page header, so it is
-     * off by 2 bytes.
-     */
-    expected_len = inlen + 2;
-    if (expected_len > SCSI_MAX_MODE_LEN) {
-        return -1;
-    }
-
-    p = mode_current;
-    memset(mode_current, 0, inlen + 2);
-    len = mode_sense_page(s, page, &p, 0);
-    if (len < 0 || len != expected_len) {
-        return -1;
-    }
-
-    p = mode_changeable;
-    memset(mode_changeable, 0, inlen + 2);
-    changeable_len = mode_sense_page(s, page, &p, 1);
-    assert(changeable_len == len);
-
-    /* Check that unchangeable bits are the same as what MODE SENSE
-     * would return.
-     */
-    for (i = 2; i < len; i++) {
-        if (((mode_current[i] ^ inbuf[i - 2]) & ~mode_changeable[i]) != 0) {
-            return -1;
-        }
-    }
-    return 0;
-}
-
-static void scsi_disk_apply_mode_select(SCSIDiskState *s, int page, uint8_t *p)
-{
-    switch (page) {
-    case MODE_PAGE_CACHING:
-        bdrv_set_enable_write_cache(s->qdev.conf.bs, (p[0] & 4) != 0);
-        break;
-
-    default:
-        break;
-    }
-}
-
-static int mode_select_pages(SCSIDiskReq *r, uint8_t *p, int len, bool change)
-{
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
-
-    while (len > 0) {
-        int page, subpage, page_len;
-
-        /* Parse both possible formats for the mode page headers.  */
-        page = p[0] & 0x3f;
-        if (p[0] & 0x40) {
-            if (len < 4) {
-                goto invalid_param_len;
-            }
-            subpage = p[1];
-            page_len = lduw_be_p(&p[2]);
-            p += 4;
-            len -= 4;
-        } else {
-            if (len < 2) {
-                goto invalid_param_len;
-            }
-            subpage = 0;
-            page_len = p[1];
-            p += 2;
-            len -= 2;
-        }
-
-        if (subpage) {
-            goto invalid_param;
-        }
-        if (page_len > len) {
-            goto invalid_param_len;
-        }
-
-        if (!change) {
-            if (scsi_disk_check_mode_select(s, page, p, page_len) < 0) {
-                goto invalid_param;
-            }
-        } else {
-            scsi_disk_apply_mode_select(s, page, p);
-        }
-
-        p += page_len;
-        len -= page_len;
-    }
-    return 0;
-
-invalid_param:
-    scsi_check_condition(r, SENSE_CODE(INVALID_PARAM));
-    return -1;
-
-invalid_param_len:
-    scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN));
-    return -1;
-}
-
-static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf)
-{
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
-    uint8_t *p = inbuf;
-    int cmd = r->req.cmd.buf[0];
-    int len = r->req.cmd.xfer;
-    int hdr_len = (cmd == MODE_SELECT ? 4 : 8);
-    int bd_len;
-    int pass;
-
-    /* We only support PF=1, SP=0.  */
-    if ((r->req.cmd.buf[1] & 0x11) != 0x10) {
-        goto invalid_field;
-    }
-
-    if (len < hdr_len) {
-        goto invalid_param_len;
-    }
-
-    bd_len = (cmd == MODE_SELECT ? p[3] : lduw_be_p(&p[6]));
-    len -= hdr_len;
-    p += hdr_len;
-    if (len < bd_len) {
-        goto invalid_param_len;
-    }
-    if (bd_len != 0 && bd_len != 8) {
-        goto invalid_param;
-    }
-
-    len -= bd_len;
-    p += bd_len;
-
-    /* Ensure no change is made if there is an error!  */
-    for (pass = 0; pass < 2; pass++) {
-        if (mode_select_pages(r, p, len, pass == 1) < 0) {
-            assert(pass == 0);
-            return;
-        }
-    }
-    if (!bdrv_enable_write_cache(s->qdev.conf.bs)) {
-        /* The request is used as the AIO opaque value, so add a ref.  */
-        scsi_req_ref(&r->req);
-        bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
-        r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_aio_complete, r);
-        return;
-    }
-
-    scsi_req_complete(&r->req, GOOD);
-    return;
-
-invalid_param:
-    scsi_check_condition(r, SENSE_CODE(INVALID_PARAM));
-    return;
-
-invalid_param_len:
-    scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN));
-    return;
-
-invalid_field:
-    scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
-}
-
-static inline bool check_lba_range(SCSIDiskState *s,
-                                   uint64_t sector_num, uint32_t nb_sectors)
-{
-    /*
-     * The first line tests that no overflow happens when computing the last
-     * sector.  The second line tests that the last accessed sector is in
-     * range.
-     *
-     * Careful, the computations should not underflow for nb_sectors == 0,
-     * and a 0-block read to the first LBA beyond the end of device is
-     * valid.
-     */
-    return (sector_num <= sector_num + nb_sectors &&
-            sector_num + nb_sectors <= s->qdev.max_lba + 1);
-}
-
-typedef struct UnmapCBData {
-    SCSIDiskReq *r;
-    uint8_t *inbuf;
-    int count;
-} UnmapCBData;
-
-static void scsi_unmap_complete(void *opaque, int ret)
-{
-    UnmapCBData *data = opaque;
-    SCSIDiskReq *r = data->r;
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
-    uint64_t sector_num;
-    uint32_t nb_sectors;
-
-    r->req.aiocb = NULL;
-    if (r->req.io_canceled) {
-        goto done;
-    }
-
-    if (ret < 0) {
-        if (scsi_handle_rw_error(r, -ret)) {
-            goto done;
-        }
-    }
-
-    if (data->count > 0) {
-        sector_num = ldq_be_p(&data->inbuf[0]);
-        nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL;
-        if (!check_lba_range(s, sector_num, nb_sectors)) {
-            scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
-            goto done;
-        }
-
-        r->req.aiocb = bdrv_aio_discard(s->qdev.conf.bs,
-                                        sector_num * (s->qdev.blocksize / 512),
-                                        nb_sectors * (s->qdev.blocksize / 512),
-                                        scsi_unmap_complete, data);
-        data->count--;
-        data->inbuf += 16;
-        return;
-    }
-
-    scsi_req_complete(&r->req, GOOD);
-
-done:
-    if (!r->req.io_canceled) {
-        scsi_req_unref(&r->req);
-    }
-    g_free(data);
-}
-
-static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
-{
-    uint8_t *p = inbuf;
-    int len = r->req.cmd.xfer;
-    UnmapCBData *data;
-
-    if (len < 8) {
-        goto invalid_param_len;
-    }
-    if (len < lduw_be_p(&p[0]) + 2) {
-        goto invalid_param_len;
-    }
-    if (len < lduw_be_p(&p[2]) + 8) {
-        goto invalid_param_len;
-    }
-    if (lduw_be_p(&p[2]) & 15) {
-        goto invalid_param_len;
-    }
-
-    data = g_new0(UnmapCBData, 1);
-    data->r = r;
-    data->inbuf = &p[8];
-    data->count = lduw_be_p(&p[2]) >> 4;
-
-    /* The matching unref is in scsi_unmap_complete, before data is freed.  */
-    scsi_req_ref(&r->req);
-    scsi_unmap_complete(data, 0);
-    return;
-
-invalid_param_len:
-    scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN));
-}
-
-static void scsi_disk_emulate_write_data(SCSIRequest *req)
-{
-    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
-
-    if (r->iov.iov_len) {
-        int buflen = r->iov.iov_len;
-        DPRINTF("Write buf_len=%d\n", buflen);
-        r->iov.iov_len = 0;
-        scsi_req_data(&r->req, buflen);
-        return;
-    }
-
-    switch (req->cmd.buf[0]) {
-    case MODE_SELECT:
-    case MODE_SELECT_10:
-        /* This also clears the sense buffer for REQUEST SENSE.  */
-        scsi_disk_emulate_mode_select(r, r->iov.iov_base);
-        break;
-
-    case UNMAP:
-        scsi_disk_emulate_unmap(r, r->iov.iov_base);
-        break;
-
-    default:
-        abort();
-    }
-}
-
-static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
-{
-    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
-    uint64_t nb_sectors;
-    uint8_t *outbuf;
-    int buflen;
-
-    switch (req->cmd.buf[0]) {
-    case INQUIRY:
-    case MODE_SENSE:
-    case MODE_SENSE_10:
-    case RESERVE:
-    case RESERVE_10:
-    case RELEASE:
-    case RELEASE_10:
-    case START_STOP:
-    case ALLOW_MEDIUM_REMOVAL:
-    case GET_CONFIGURATION:
-    case GET_EVENT_STATUS_NOTIFICATION:
-    case MECHANISM_STATUS:
-    case REQUEST_SENSE:
-        break;
-
-    default:
-        if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
-            scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
-            return 0;
-        }
-        break;
-    }
-
-    /*
-     * FIXME: we shouldn't return anything bigger than 4k, but the code
-     * requires the buffer to be as big as req->cmd.xfer in several
-     * places.  So, do not allow CDBs with a very large ALLOCATION
-     * LENGTH.  The real fix would be to modify scsi_read_data and
-     * dma_buf_read, so that they return data beyond the buflen
-     * as all zeros.
-     */
-    if (req->cmd.xfer > 65536) {
-        goto illegal_request;
-    }
-    r->buflen = MAX(4096, req->cmd.xfer);
-
-    if (!r->iov.iov_base) {
-        r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen);
-    }
-
-    buflen = req->cmd.xfer;
-    outbuf = r->iov.iov_base;
-    memset(outbuf, 0, r->buflen);
-    switch (req->cmd.buf[0]) {
-    case TEST_UNIT_READY:
-        assert(!s->tray_open && bdrv_is_inserted(s->qdev.conf.bs));
-        break;
-    case INQUIRY:
-        buflen = scsi_disk_emulate_inquiry(req, outbuf);
-        if (buflen < 0) {
-            goto illegal_request;
-        }
-        break;
-    case MODE_SENSE:
-    case MODE_SENSE_10:
-        buflen = scsi_disk_emulate_mode_sense(r, outbuf);
-        if (buflen < 0) {
-            goto illegal_request;
-        }
-        break;
-    case READ_TOC:
-        buflen = scsi_disk_emulate_read_toc(req, outbuf);
-        if (buflen < 0) {
-            goto illegal_request;
-        }
-        break;
-    case RESERVE:
-        if (req->cmd.buf[1] & 1) {
-            goto illegal_request;
-        }
-        break;
-    case RESERVE_10:
-        if (req->cmd.buf[1] & 3) {
-            goto illegal_request;
-        }
-        break;
-    case RELEASE:
-        if (req->cmd.buf[1] & 1) {
-            goto illegal_request;
-        }
-        break;
-    case RELEASE_10:
-        if (req->cmd.buf[1] & 3) {
-            goto illegal_request;
-        }
-        break;
-    case START_STOP:
-        if (scsi_disk_emulate_start_stop(r) < 0) {
-            return 0;
-        }
-        break;
-    case ALLOW_MEDIUM_REMOVAL:
-        s->tray_locked = req->cmd.buf[4] & 1;
-        bdrv_lock_medium(s->qdev.conf.bs, req->cmd.buf[4] & 1);
-        break;
-    case READ_CAPACITY_10:
-        /* The normal LEN field for this command is zero.  */
-        memset(outbuf, 0, 8);
-        bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
-        if (!nb_sectors) {
-            scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY));
-            return 0;
-        }
-        if ((req->cmd.buf[8] & 1) == 0 && req->cmd.lba) {
-            goto illegal_request;
-        }
-        nb_sectors /= s->qdev.blocksize / 512;
-        /* Returned value is the address of the last sector.  */
-        nb_sectors--;
-        /* Remember the new size for read/write sanity checking. */
-        s->qdev.max_lba = nb_sectors;
-        /* Clip to 2TB, instead of returning capacity modulo 2TB. */
-        if (nb_sectors > UINT32_MAX) {
-            nb_sectors = UINT32_MAX;
-        }
-        outbuf[0] = (nb_sectors >> 24) & 0xff;
-        outbuf[1] = (nb_sectors >> 16) & 0xff;
-        outbuf[2] = (nb_sectors >> 8) & 0xff;
-        outbuf[3] = nb_sectors & 0xff;
-        outbuf[4] = 0;
-        outbuf[5] = 0;
-        outbuf[6] = s->qdev.blocksize >> 8;
-        outbuf[7] = 0;
-        break;
-    case REQUEST_SENSE:
-        /* Just return "NO SENSE".  */
-        buflen = scsi_build_sense(NULL, 0, outbuf, r->buflen,
-                                  (req->cmd.buf[1] & 1) == 0);
-        if (buflen < 0) {
-            goto illegal_request;
-        }
-        break;
-    case MECHANISM_STATUS:
-        buflen = scsi_emulate_mechanism_status(s, outbuf);
-        if (buflen < 0) {
-            goto illegal_request;
-        }
-        break;
-    case GET_CONFIGURATION:
-        buflen = scsi_get_configuration(s, outbuf);
-        if (buflen < 0) {
-            goto illegal_request;
-        }
-        break;
-    case GET_EVENT_STATUS_NOTIFICATION:
-        buflen = scsi_get_event_status_notification(s, r, outbuf);
-        if (buflen < 0) {
-            goto illegal_request;
-        }
-        break;
-    case READ_DISC_INFORMATION:
-        buflen = scsi_read_disc_information(s, r, outbuf);
-        if (buflen < 0) {
-            goto illegal_request;
-        }
-        break;
-    case READ_DVD_STRUCTURE:
-        buflen = scsi_read_dvd_structure(s, r, outbuf);
-        if (buflen < 0) {
-            goto illegal_request;
-        }
-        break;
-    case SERVICE_ACTION_IN_16:
-        /* Service Action In subcommands. */
-        if ((req->cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
-            DPRINTF("SAI READ CAPACITY(16)\n");
-            memset(outbuf, 0, req->cmd.xfer);
-            bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
-            if (!nb_sectors) {
-                scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY));
-                return 0;
-            }
-            if ((req->cmd.buf[14] & 1) == 0 && req->cmd.lba) {
-                goto illegal_request;
-            }
-            nb_sectors /= s->qdev.blocksize / 512;
-            /* Returned value is the address of the last sector.  */
-            nb_sectors--;
-            /* Remember the new size for read/write sanity checking. */
-            s->qdev.max_lba = nb_sectors;
-            outbuf[0] = (nb_sectors >> 56) & 0xff;
-            outbuf[1] = (nb_sectors >> 48) & 0xff;
-            outbuf[2] = (nb_sectors >> 40) & 0xff;
-            outbuf[3] = (nb_sectors >> 32) & 0xff;
-            outbuf[4] = (nb_sectors >> 24) & 0xff;
-            outbuf[5] = (nb_sectors >> 16) & 0xff;
-            outbuf[6] = (nb_sectors >> 8) & 0xff;
-            outbuf[7] = nb_sectors & 0xff;
-            outbuf[8] = 0;
-            outbuf[9] = 0;
-            outbuf[10] = s->qdev.blocksize >> 8;
-            outbuf[11] = 0;
-            outbuf[12] = 0;
-            outbuf[13] = get_physical_block_exp(&s->qdev.conf);
-
-            /* set TPE bit if the format supports discard */
-            if (s->qdev.conf.discard_granularity) {
-                outbuf[14] = 0x80;
-            }
-
-            /* Protection, exponent and lowest lba field left blank. */
-            break;
-        }
-        DPRINTF("Unsupported Service Action In\n");
-        goto illegal_request;
-    case SYNCHRONIZE_CACHE:
-        /* The request is used as the AIO opaque value, so add a ref.  */
-        scsi_req_ref(&r->req);
-        bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
-        r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_aio_complete, r);
-        return 0;
-    case SEEK_10:
-        DPRINTF("Seek(10) (sector %" PRId64 ")\n", r->req.cmd.lba);
-        if (r->req.cmd.lba > s->qdev.max_lba) {
-            goto illegal_lba;
-        }
-        break;
-    case MODE_SELECT:
-        DPRINTF("Mode Select(6) (len %lu)\n", (long)r->req.cmd.xfer);
-        break;
-    case MODE_SELECT_10:
-        DPRINTF("Mode Select(10) (len %lu)\n", (long)r->req.cmd.xfer);
-        break;
-    case UNMAP:
-        DPRINTF("Unmap (len %lu)\n", (long)r->req.cmd.xfer);
-        break;
-    case WRITE_SAME_10:
-    case WRITE_SAME_16:
-        nb_sectors = scsi_data_cdb_length(r->req.cmd.buf);
-        if (bdrv_is_read_only(s->qdev.conf.bs)) {
-            scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
-            return 0;
-        }
-        if (!check_lba_range(s, r->req.cmd.lba, nb_sectors)) {
-            goto illegal_lba;
-        }
-
-        /*
-         * We only support WRITE SAME with the unmap bit set for now.
-         */
-        if (!(req->cmd.buf[1] & 0x8)) {
-            goto illegal_request;
-        }
-
-        /* The request is used as the AIO opaque value, so add a ref.  */
-        scsi_req_ref(&r->req);
-        r->req.aiocb = bdrv_aio_discard(s->qdev.conf.bs,
-                                        r->req.cmd.lba * (s->qdev.blocksize / 512),
-                                        nb_sectors * (s->qdev.blocksize / 512),
-                                        scsi_aio_complete, r);
-        return 0;
-    default:
-        DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
-        scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
-        return 0;
-    }
-    assert(!r->req.aiocb);
-    r->iov.iov_len = MIN(r->buflen, req->cmd.xfer);
-    if (r->iov.iov_len == 0) {
-        scsi_req_complete(&r->req, GOOD);
-    }
-    if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
-        assert(r->iov.iov_len == req->cmd.xfer);
-        return -r->iov.iov_len;
-    } else {
-        return r->iov.iov_len;
-    }
-
-illegal_request:
-    if (r->req.status == -1) {
-        scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
-    }
-    return 0;
-
-illegal_lba:
-    scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
-    return 0;
-}
-
-/* Execute a scsi command.  Returns the length of the data expected by the
-   command.  This will be Positive for data transfers from the device
-   (eg. disk reads), negative for transfers to the device (eg. disk writes),
-   and zero if the command does not transfer any data.  */
-
-static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
-{
-    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
-    uint32_t len;
-    uint8_t command;
-
-    command = buf[0];
-
-    if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
-        scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
-        return 0;
-    }
-
-    len = scsi_data_cdb_length(r->req.cmd.buf);
-    switch (command) {
-    case READ_6:
-    case READ_10:
-    case READ_12:
-    case READ_16:
-        DPRINTF("Read (sector %" PRId64 ", count %u)\n", r->req.cmd.lba, len);
-        if (r->req.cmd.buf[1] & 0xe0) {
-            goto illegal_request;
-        }
-        if (!check_lba_range(s, r->req.cmd.lba, len)) {
-            goto illegal_lba;
-        }
-        r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512);
-        r->sector_count = len * (s->qdev.blocksize / 512);
-        break;
-    case WRITE_6:
-    case WRITE_10:
-    case WRITE_12:
-    case WRITE_16:
-    case WRITE_VERIFY_10:
-    case WRITE_VERIFY_12:
-    case WRITE_VERIFY_16:
-        if (bdrv_is_read_only(s->qdev.conf.bs)) {
-            scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
-            return 0;
-        }
-        /* fallthrough */
-    case VERIFY_10:
-    case VERIFY_12:
-    case VERIFY_16:
-        DPRINTF("Write %s(sector %" PRId64 ", count %u)\n",
-                (command & 0xe) == 0xe ? "And Verify " : "",
-                r->req.cmd.lba, len);
-        if (r->req.cmd.buf[1] & 0xe0) {
-            goto illegal_request;
-        }
-        if (!check_lba_range(s, r->req.cmd.lba, len)) {
-            goto illegal_lba;
-        }
-        r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512);
-        r->sector_count = len * (s->qdev.blocksize / 512);
-        break;
-    default:
-        abort();
-    illegal_request:
-        scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
-        return 0;
-    illegal_lba:
-        scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
-        return 0;
-    }
-    if (r->sector_count == 0) {
-        scsi_req_complete(&r->req, GOOD);
-    }
-    assert(r->iov.iov_len == 0);
-    if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
-        return -r->sector_count * 512;
-    } else {
-        return r->sector_count * 512;
-    }
-}
-
-static void scsi_disk_reset(DeviceState *dev)
-{
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev);
-    uint64_t nb_sectors;
-
-    scsi_device_purge_requests(&s->qdev, SENSE_CODE(RESET));
-
-    bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
-    nb_sectors /= s->qdev.blocksize / 512;
-    if (nb_sectors) {
-        nb_sectors--;
-    }
-    s->qdev.max_lba = nb_sectors;
-}
-
-static void scsi_destroy(SCSIDevice *dev)
-{
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
-
-    scsi_device_purge_requests(&s->qdev, SENSE_CODE(NO_SENSE));
-    blockdev_mark_auto_del(s->qdev.conf.bs);
-}
-
-static void scsi_disk_resize_cb(void *opaque)
-{
-    SCSIDiskState *s = opaque;
-
-    /* SPC lists this sense code as available only for
-     * direct-access devices.
-     */
-    if (s->qdev.type == TYPE_DISK) {
-        scsi_device_report_change(&s->qdev, SENSE_CODE(CAPACITY_CHANGED));
-    }
-}
-
-static void scsi_cd_change_media_cb(void *opaque, bool load)
-{
-    SCSIDiskState *s = opaque;
-
-    /*
-     * When a CD gets changed, we have to report an ejected state and
-     * then a loaded state to guests so that they detect tray
-     * open/close and media change events.  Guests that do not use
-     * GET_EVENT_STATUS_NOTIFICATION to detect such tray open/close
-     * states rely on this behavior.
-     *
-     * media_changed governs the state machine used for unit attention
-     * report.  media_event is used by GET EVENT STATUS NOTIFICATION.
-     */
-    s->media_changed = load;
-    s->tray_open = !load;
-    scsi_device_set_ua(&s->qdev, SENSE_CODE(UNIT_ATTENTION_NO_MEDIUM));
-    s->media_event = true;
-    s->eject_request = false;
-}
-
-static void scsi_cd_eject_request_cb(void *opaque, bool force)
-{
-    SCSIDiskState *s = opaque;
-
-    s->eject_request = true;
-    if (force) {
-        s->tray_locked = false;
-    }
-}
-
-static bool scsi_cd_is_tray_open(void *opaque)
-{
-    return ((SCSIDiskState *)opaque)->tray_open;
-}
-
-static bool scsi_cd_is_medium_locked(void *opaque)
-{
-    return ((SCSIDiskState *)opaque)->tray_locked;
-}
-
-static const BlockDevOps scsi_disk_removable_block_ops = {
-    .change_media_cb = scsi_cd_change_media_cb,
-    .eject_request_cb = scsi_cd_eject_request_cb,
-    .is_tray_open = scsi_cd_is_tray_open,
-    .is_medium_locked = scsi_cd_is_medium_locked,
-
-    .resize_cb = scsi_disk_resize_cb,
-};
-
-static const BlockDevOps scsi_disk_block_ops = {
-    .resize_cb = scsi_disk_resize_cb,
-};
-
-static void scsi_disk_unit_attention_reported(SCSIDevice *dev)
-{
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
-    if (s->media_changed) {
-        s->media_changed = false;
-        scsi_device_set_ua(&s->qdev, SENSE_CODE(MEDIUM_CHANGED));
-    }
-}
-
-static int scsi_initfn(SCSIDevice *dev)
-{
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
-
-    if (!s->qdev.conf.bs) {
-        error_report("drive property not set");
-        return -1;
-    }
-
-    if (!(s->features & (1 << SCSI_DISK_F_REMOVABLE)) &&
-        !bdrv_is_inserted(s->qdev.conf.bs)) {
-        error_report("Device needs media, but drive is empty");
-        return -1;
-    }
-
-    blkconf_serial(&s->qdev.conf, &s->serial);
-    if (dev->type == TYPE_DISK
-        && blkconf_geometry(&dev->conf, NULL, 65535, 255, 255) < 0) {
-        return -1;
-    }
-
-    if (s->qdev.conf.discard_granularity == -1) {
-        s->qdev.conf.discard_granularity =
-            MAX(s->qdev.conf.logical_block_size, DEFAULT_DISCARD_GRANULARITY);
-    }
-
-    if (!s->version) {
-        s->version = g_strdup(qemu_get_version());
-    }
-    if (!s->vendor) {
-        s->vendor = g_strdup("QEMU");
-    }
-
-    if (bdrv_is_sg(s->qdev.conf.bs)) {
-        error_report("unwanted /dev/sg*");
-        return -1;
-    }
-
-    if (s->features & (1 << SCSI_DISK_F_REMOVABLE)) {
-        bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_disk_removable_block_ops, s);
-    } else {
-        bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_disk_block_ops, s);
-    }
-    bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize);
-
-    bdrv_iostatus_enable(s->qdev.conf.bs);
-    add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, NULL);
-    return 0;
-}
-
-static int scsi_hd_initfn(SCSIDevice *dev)
-{
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
-    s->qdev.blocksize = s->qdev.conf.logical_block_size;
-    s->qdev.type = TYPE_DISK;
-    if (!s->product) {
-        s->product = g_strdup("QEMU HARDDISK");
-    }
-    return scsi_initfn(&s->qdev);
-}
-
-static int scsi_cd_initfn(SCSIDevice *dev)
-{
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
-    s->qdev.blocksize = 2048;
-    s->qdev.type = TYPE_ROM;
-    s->features |= 1 << SCSI_DISK_F_REMOVABLE;
-    if (!s->product) {
-        s->product = g_strdup("QEMU CD-ROM");
-    }
-    return scsi_initfn(&s->qdev);
-}
-
-static int scsi_disk_initfn(SCSIDevice *dev)
-{
-    DriveInfo *dinfo;
-
-    if (!dev->conf.bs) {
-        return scsi_initfn(dev);  /* ... and die there */
-    }
-
-    dinfo = drive_get_by_blockdev(dev->conf.bs);
-    if (dinfo->media_cd) {
-        return scsi_cd_initfn(dev);
-    } else {
-        return scsi_hd_initfn(dev);
-    }
-}
-
-static const SCSIReqOps scsi_disk_emulate_reqops = {
-    .size         = sizeof(SCSIDiskReq),
-    .free_req     = scsi_free_request,
-    .send_command = scsi_disk_emulate_command,
-    .read_data    = scsi_disk_emulate_read_data,
-    .write_data   = scsi_disk_emulate_write_data,
-    .get_buf      = scsi_get_buf,
-};
-
-static const SCSIReqOps scsi_disk_dma_reqops = {
-    .size         = sizeof(SCSIDiskReq),
-    .free_req     = scsi_free_request,
-    .send_command = scsi_disk_dma_command,
-    .read_data    = scsi_read_data,
-    .write_data   = scsi_write_data,
-    .cancel_io    = scsi_cancel_io,
-    .get_buf      = scsi_get_buf,
-    .load_request = scsi_disk_load_request,
-    .save_request = scsi_disk_save_request,
-};
-
-static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = {
-    [TEST_UNIT_READY]                 = &scsi_disk_emulate_reqops,
-    [INQUIRY]                         = &scsi_disk_emulate_reqops,
-    [MODE_SENSE]                      = &scsi_disk_emulate_reqops,
-    [MODE_SENSE_10]                   = &scsi_disk_emulate_reqops,
-    [START_STOP]                      = &scsi_disk_emulate_reqops,
-    [ALLOW_MEDIUM_REMOVAL]            = &scsi_disk_emulate_reqops,
-    [READ_CAPACITY_10]                = &scsi_disk_emulate_reqops,
-    [READ_TOC]                        = &scsi_disk_emulate_reqops,
-    [READ_DVD_STRUCTURE]              = &scsi_disk_emulate_reqops,
-    [READ_DISC_INFORMATION]           = &scsi_disk_emulate_reqops,
-    [GET_CONFIGURATION]               = &scsi_disk_emulate_reqops,
-    [GET_EVENT_STATUS_NOTIFICATION]   = &scsi_disk_emulate_reqops,
-    [MECHANISM_STATUS]                = &scsi_disk_emulate_reqops,
-    [SERVICE_ACTION_IN_16]            = &scsi_disk_emulate_reqops,
-    [REQUEST_SENSE]                   = &scsi_disk_emulate_reqops,
-    [SYNCHRONIZE_CACHE]               = &scsi_disk_emulate_reqops,
-    [SEEK_10]                         = &scsi_disk_emulate_reqops,
-    [MODE_SELECT]                     = &scsi_disk_emulate_reqops,
-    [MODE_SELECT_10]                  = &scsi_disk_emulate_reqops,
-    [UNMAP]                           = &scsi_disk_emulate_reqops,
-    [WRITE_SAME_10]                   = &scsi_disk_emulate_reqops,
-    [WRITE_SAME_16]                   = &scsi_disk_emulate_reqops,
-
-    [READ_6]                          = &scsi_disk_dma_reqops,
-    [READ_10]                         = &scsi_disk_dma_reqops,
-    [READ_12]                         = &scsi_disk_dma_reqops,
-    [READ_16]                         = &scsi_disk_dma_reqops,
-    [VERIFY_10]                       = &scsi_disk_dma_reqops,
-    [VERIFY_12]                       = &scsi_disk_dma_reqops,
-    [VERIFY_16]                       = &scsi_disk_dma_reqops,
-    [WRITE_6]                         = &scsi_disk_dma_reqops,
-    [WRITE_10]                        = &scsi_disk_dma_reqops,
-    [WRITE_12]                        = &scsi_disk_dma_reqops,
-    [WRITE_16]                        = &scsi_disk_dma_reqops,
-    [WRITE_VERIFY_10]                 = &scsi_disk_dma_reqops,
-    [WRITE_VERIFY_12]                 = &scsi_disk_dma_reqops,
-    [WRITE_VERIFY_16]                 = &scsi_disk_dma_reqops,
-};
-
-static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
-                                     uint8_t *buf, void *hba_private)
-{
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
-    SCSIRequest *req;
-    const SCSIReqOps *ops;
-    uint8_t command;
-
-    command = buf[0];
-    ops = scsi_disk_reqops_dispatch[command];
-    if (!ops) {
-        ops = &scsi_disk_emulate_reqops;
-    }
-    req = scsi_req_alloc(ops, &s->qdev, tag, lun, hba_private);
-
-#ifdef DEBUG_SCSI
-    DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]);
-    {
-        int i;
-        for (i = 1; i < req->cmd.len; i++) {
-            printf(" 0x%02x", buf[i]);
-        }
-        printf("\n");
-    }
-#endif
-
-    return req;
-}
-
-#ifdef __linux__
-static int get_device_type(SCSIDiskState *s)
-{
-    BlockDriverState *bdrv = s->qdev.conf.bs;
-    uint8_t cmd[16];
-    uint8_t buf[36];
-    uint8_t sensebuf[8];
-    sg_io_hdr_t io_header;
-    int ret;
-
-    memset(cmd, 0, sizeof(cmd));
-    memset(buf, 0, sizeof(buf));
-    cmd[0] = INQUIRY;
-    cmd[4] = sizeof(buf);
-
-    memset(&io_header, 0, sizeof(io_header));
-    io_header.interface_id = 'S';
-    io_header.dxfer_direction = SG_DXFER_FROM_DEV;
-    io_header.dxfer_len = sizeof(buf);
-    io_header.dxferp = buf;
-    io_header.cmdp = cmd;
-    io_header.cmd_len = sizeof(cmd);
-    io_header.mx_sb_len = sizeof(sensebuf);
-    io_header.sbp = sensebuf;
-    io_header.timeout = 6000; /* XXX */
-
-    ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
-    if (ret < 0 || io_header.driver_status || io_header.host_status) {
-        return -1;
-    }
-    s->qdev.type = buf[0];
-    if (buf[1] & 0x80) {
-        s->features |= 1 << SCSI_DISK_F_REMOVABLE;
-    }
-    return 0;
-}
-
-static int scsi_block_initfn(SCSIDevice *dev)
-{
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
-    int sg_version;
-    int rc;
-
-    if (!s->qdev.conf.bs) {
-        error_report("scsi-block: drive property not set");
-        return -1;
-    }
-
-    /* check we are using a driver managing SG_IO (version 3 and after) */
-    if (bdrv_ioctl(s->qdev.conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0 ||
-        sg_version < 30000) {
-        error_report("scsi-block: scsi generic interface too old");
-        return -1;
-    }
-
-    /* get device type from INQUIRY data */
-    rc = get_device_type(s);
-    if (rc < 0) {
-        error_report("scsi-block: INQUIRY failed");
-        return -1;
-    }
-
-    /* Make a guess for the block size, we'll fix it when the guest sends.
-     * READ CAPACITY.  If they don't, they likely would assume these sizes
-     * anyway. (TODO: check in /sys).
-     */
-    if (s->qdev.type == TYPE_ROM || s->qdev.type == TYPE_WORM) {
-        s->qdev.blocksize = 2048;
-    } else {
-        s->qdev.blocksize = 512;
-    }
-    return scsi_initfn(&s->qdev);
-}
-
-static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
-                                           uint32_t lun, uint8_t *buf,
-                                           void *hba_private)
-{
-    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
-
-    switch (buf[0]) {
-    case READ_6:
-    case READ_10:
-    case READ_12:
-    case READ_16:
-    case VERIFY_10:
-    case VERIFY_12:
-    case VERIFY_16:
-    case WRITE_6:
-    case WRITE_10:
-    case WRITE_12:
-    case WRITE_16:
-    case WRITE_VERIFY_10:
-    case WRITE_VERIFY_12:
-    case WRITE_VERIFY_16:
-        /* If we are not using O_DIRECT, we might read stale data from the
-        * host cache if writes were made using other commands than these
-        * ones (such as WRITE SAME or EXTENDED COPY, etc.).  So, without
-        * O_DIRECT everything must go through SG_IO.
-         */
-        if (bdrv_get_flags(s->qdev.conf.bs) & BDRV_O_NOCACHE) {
-            break;
-        }
-
-        /* MMC writing cannot be done via pread/pwrite, because it sometimes
-         * involves writing beyond the maximum LBA or to negative LBA (lead-in).
-         * And once you do these writes, reading from the block device is
-         * unreliable, too.  It is even possible that reads deliver random data
-         * from the host page cache (this is probably a Linux bug).
-         *
-         * We might use scsi_disk_dma_reqops as long as no writing commands are
-         * seen, but performance usually isn't paramount on optical media.  So,
-         * just make scsi-block operate the same as scsi-generic for them.
-         */
-        if (s->qdev.type != TYPE_ROM) {
-            return scsi_req_alloc(&scsi_disk_dma_reqops, &s->qdev, tag, lun,
-                                  hba_private);
-        }
-    }
-
-    return scsi_req_alloc(&scsi_generic_req_ops, &s->qdev, tag, lun,
-                          hba_private);
-}
-#endif
-
-#define DEFINE_SCSI_DISK_PROPERTIES()                                \
-    DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf),               \
-    DEFINE_PROP_STRING("ver", SCSIDiskState, version),               \
-    DEFINE_PROP_STRING("serial", SCSIDiskState, serial),             \
-    DEFINE_PROP_STRING("vendor", SCSIDiskState, vendor),             \
-    DEFINE_PROP_STRING("product", SCSIDiskState, product)
-
-static Property scsi_hd_properties[] = {
-    DEFINE_SCSI_DISK_PROPERTIES(),
-    DEFINE_PROP_BIT("removable", SCSIDiskState, features,
-                    SCSI_DISK_F_REMOVABLE, false),
-    DEFINE_PROP_BIT("dpofua", SCSIDiskState, features,
-                    SCSI_DISK_F_DPOFUA, false),
-    DEFINE_PROP_HEX64("wwn", SCSIDiskState, wwn, 0),
-    DEFINE_BLOCK_CHS_PROPERTIES(SCSIDiskState, qdev.conf),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static const VMStateDescription vmstate_scsi_disk_state = {
-    .name = "scsi-disk",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_SCSI_DEVICE(qdev, SCSIDiskState),
-        VMSTATE_BOOL(media_changed, SCSIDiskState),
-        VMSTATE_BOOL(media_event, SCSIDiskState),
-        VMSTATE_BOOL(eject_request, SCSIDiskState),
-        VMSTATE_BOOL(tray_open, SCSIDiskState),
-        VMSTATE_BOOL(tray_locked, SCSIDiskState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void scsi_hd_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
-
-    sc->init         = scsi_hd_initfn;
-    sc->destroy      = scsi_destroy;
-    sc->alloc_req    = scsi_new_request;
-    sc->unit_attention_reported = scsi_disk_unit_attention_reported;
-    dc->fw_name = "disk";
-    dc->desc = "virtual SCSI disk";
-    dc->reset = scsi_disk_reset;
-    dc->props = scsi_hd_properties;
-    dc->vmsd  = &vmstate_scsi_disk_state;
-}
-
-static const TypeInfo scsi_hd_info = {
-    .name          = "scsi-hd",
-    .parent        = TYPE_SCSI_DEVICE,
-    .instance_size = sizeof(SCSIDiskState),
-    .class_init    = scsi_hd_class_initfn,
-};
-
-static Property scsi_cd_properties[] = {
-    DEFINE_SCSI_DISK_PROPERTIES(),
-    DEFINE_PROP_HEX64("wwn", SCSIDiskState, wwn, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void scsi_cd_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
-
-    sc->init         = scsi_cd_initfn;
-    sc->destroy      = scsi_destroy;
-    sc->alloc_req    = scsi_new_request;
-    sc->unit_attention_reported = scsi_disk_unit_attention_reported;
-    dc->fw_name = "disk";
-    dc->desc = "virtual SCSI CD-ROM";
-    dc->reset = scsi_disk_reset;
-    dc->props = scsi_cd_properties;
-    dc->vmsd  = &vmstate_scsi_disk_state;
-}
-
-static const TypeInfo scsi_cd_info = {
-    .name          = "scsi-cd",
-    .parent        = TYPE_SCSI_DEVICE,
-    .instance_size = sizeof(SCSIDiskState),
-    .class_init    = scsi_cd_class_initfn,
-};
-
-#ifdef __linux__
-static Property scsi_block_properties[] = {
-    DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.bs),
-    DEFINE_PROP_INT32("bootindex", SCSIDiskState, qdev.conf.bootindex, -1),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void scsi_block_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
-
-    sc->init         = scsi_block_initfn;
-    sc->destroy      = scsi_destroy;
-    sc->alloc_req    = scsi_block_new_request;
-    dc->fw_name = "disk";
-    dc->desc = "SCSI block device passthrough";
-    dc->reset = scsi_disk_reset;
-    dc->props = scsi_block_properties;
-    dc->vmsd  = &vmstate_scsi_disk_state;
-}
-
-static const TypeInfo scsi_block_info = {
-    .name          = "scsi-block",
-    .parent        = TYPE_SCSI_DEVICE,
-    .instance_size = sizeof(SCSIDiskState),
-    .class_init    = scsi_block_class_initfn,
-};
-#endif
-
-static Property scsi_disk_properties[] = {
-    DEFINE_SCSI_DISK_PROPERTIES(),
-    DEFINE_PROP_BIT("removable", SCSIDiskState, features,
-                    SCSI_DISK_F_REMOVABLE, false),
-    DEFINE_PROP_BIT("dpofua", SCSIDiskState, features,
-                    SCSI_DISK_F_DPOFUA, false),
-    DEFINE_PROP_HEX64("wwn", SCSIDiskState, wwn, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void scsi_disk_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
-
-    sc->init         = scsi_disk_initfn;
-    sc->destroy      = scsi_destroy;
-    sc->alloc_req    = scsi_new_request;
-    sc->unit_attention_reported = scsi_disk_unit_attention_reported;
-    dc->fw_name = "disk";
-    dc->desc = "virtual SCSI disk or CD-ROM (legacy)";
-    dc->reset = scsi_disk_reset;
-    dc->props = scsi_disk_properties;
-    dc->vmsd  = &vmstate_scsi_disk_state;
-}
-
-static const TypeInfo scsi_disk_info = {
-    .name          = "scsi-disk",
-    .parent        = TYPE_SCSI_DEVICE,
-    .instance_size = sizeof(SCSIDiskState),
-    .class_init    = scsi_disk_class_initfn,
-};
-
-static void scsi_disk_register_types(void)
-{
-    type_register_static(&scsi_hd_info);
-    type_register_static(&scsi_cd_info);
-#ifdef __linux__
-    type_register_static(&scsi_block_info);
-#endif
-    type_register_static(&scsi_disk_info);
-}
-
-type_init(scsi_disk_register_types)
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
deleted file mode 100644 (file)
index 2a9a561..0000000
+++ /dev/null
@@ -1,516 +0,0 @@
-/*
- * Generic SCSI Device support
- *
- * Copyright (c) 2007 Bull S.A.S.
- * Based on code by Paul Brook
- * Based on code by Fabrice Bellard
- *
- * Written by Laurent Vivier <Laurent.Vivier@bull.net>
- *
- * This code is licensed under the LGPL.
- *
- */
-
-#include "qemu-common.h"
-#include "qemu/error-report.h"
-#include "hw/scsi/scsi.h"
-#include "sysemu/blockdev.h"
-
-#ifdef __linux__
-
-//#define DEBUG_SCSI
-
-#ifdef DEBUG_SCSI
-#define DPRINTF(fmt, ...) \
-do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#endif
-
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
-
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <scsi/sg.h>
-#include "block/scsi.h"
-
-#define SCSI_SENSE_BUF_SIZE 96
-
-#define SG_ERR_DRIVER_TIMEOUT  0x06
-#define SG_ERR_DRIVER_SENSE    0x08
-
-#define SG_ERR_DID_OK          0x00
-#define SG_ERR_DID_NO_CONNECT  0x01
-#define SG_ERR_DID_BUS_BUSY    0x02
-#define SG_ERR_DID_TIME_OUT    0x03
-
-#ifndef MAX_UINT
-#define MAX_UINT ((unsigned int)-1)
-#endif
-
-typedef struct SCSIGenericReq {
-    SCSIRequest req;
-    uint8_t *buf;
-    int buflen;
-    int len;
-    sg_io_hdr_t io_header;
-} SCSIGenericReq;
-
-static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req)
-{
-    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
-
-    qemu_put_sbe32s(f, &r->buflen);
-    if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
-        assert(!r->req.sg);
-        qemu_put_buffer(f, r->buf, r->req.cmd.xfer);
-    }
-}
-
-static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req)
-{
-    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
-
-    qemu_get_sbe32s(f, &r->buflen);
-    if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
-        assert(!r->req.sg);
-        qemu_get_buffer(f, r->buf, r->req.cmd.xfer);
-    }
-}
-
-static void scsi_free_request(SCSIRequest *req)
-{
-    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
-
-    g_free(r->buf);
-}
-
-/* Helper function for command completion.  */
-static void scsi_command_complete(void *opaque, int ret)
-{
-    int status;
-    SCSIGenericReq *r = (SCSIGenericReq *)opaque;
-
-    r->req.aiocb = NULL;
-    if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
-        r->req.sense_len = r->io_header.sb_len_wr;
-    }
-
-    if (ret != 0) {
-        switch (ret) {
-        case -EDOM:
-            status = TASK_SET_FULL;
-            break;
-        case -ENOMEM:
-            status = CHECK_CONDITION;
-            scsi_req_build_sense(&r->req, SENSE_CODE(TARGET_FAILURE));
-            break;
-        default:
-            status = CHECK_CONDITION;
-            scsi_req_build_sense(&r->req, SENSE_CODE(IO_ERROR));
-            break;
-        }
-    } else {
-        if (r->io_header.host_status == SG_ERR_DID_NO_CONNECT ||
-            r->io_header.host_status == SG_ERR_DID_BUS_BUSY ||
-            r->io_header.host_status == SG_ERR_DID_TIME_OUT ||
-            (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) {
-            status = BUSY;
-            BADF("Driver Timeout\n");
-        } else if (r->io_header.host_status) {
-            status = CHECK_CONDITION;
-            scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS));
-        } else if (r->io_header.status) {
-            status = r->io_header.status;
-        } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
-            status = CHECK_CONDITION;
-        } else {
-            status = GOOD;
-        }
-    }
-    DPRINTF("Command complete 0x%p tag=0x%x status=%d\n",
-            r, r->req.tag, status);
-
-    scsi_req_complete(&r->req, status);
-    if (!r->req.io_canceled) {
-        scsi_req_unref(&r->req);
-    }
-}
-
-/* Cancel a pending data transfer.  */
-static void scsi_cancel_io(SCSIRequest *req)
-{
-    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
-
-    DPRINTF("Cancel tag=0x%x\n", req->tag);
-    if (r->req.aiocb) {
-        bdrv_aio_cancel(r->req.aiocb);
-
-        /* This reference was left in by scsi_*_data.  We take ownership of
-         * it independent of whether bdrv_aio_cancel completes the request
-         * or not.  */
-        scsi_req_unref(&r->req);
-    }
-    r->req.aiocb = NULL;
-}
-
-static int execute_command(BlockDriverState *bdrv,
-                           SCSIGenericReq *r, int direction,
-                          BlockDriverCompletionFunc *complete)
-{
-    r->io_header.interface_id = 'S';
-    r->io_header.dxfer_direction = direction;
-    r->io_header.dxferp = r->buf;
-    r->io_header.dxfer_len = r->buflen;
-    r->io_header.cmdp = r->req.cmd.buf;
-    r->io_header.cmd_len = r->req.cmd.len;
-    r->io_header.mx_sb_len = sizeof(r->req.sense);
-    r->io_header.sbp = r->req.sense;
-    r->io_header.timeout = MAX_UINT;
-    r->io_header.usr_ptr = r;
-    r->io_header.flags |= SG_FLAG_DIRECT_IO;
-
-    r->req.aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r);
-
-    return 0;
-}
-
-static void scsi_read_complete(void * opaque, int ret)
-{
-    SCSIGenericReq *r = (SCSIGenericReq *)opaque;
-    SCSIDevice *s = r->req.dev;
-    int len;
-
-    r->req.aiocb = NULL;
-    if (ret) {
-        DPRINTF("IO error ret %d\n", ret);
-        scsi_command_complete(r, ret);
-        return;
-    }
-    len = r->io_header.dxfer_len - r->io_header.resid;
-    DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len);
-
-    r->len = -1;
-    if (len == 0) {
-        scsi_command_complete(r, 0);
-    } else {
-        /* Snoop READ CAPACITY output to set the blocksize.  */
-        if (r->req.cmd.buf[0] == READ_CAPACITY_10) {
-            s->blocksize = ldl_be_p(&r->buf[4]);
-            s->max_lba = ldl_be_p(&r->buf[0]);
-        } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 &&
-                   (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
-            s->blocksize = ldl_be_p(&r->buf[8]);
-            s->max_lba = ldq_be_p(&r->buf[0]);
-        }
-        bdrv_set_buffer_alignment(s->conf.bs, s->blocksize);
-
-        scsi_req_data(&r->req, len);
-        if (!r->req.io_canceled) {
-            scsi_req_unref(&r->req);
-        }
-    }
-}
-
-/* Read more data from scsi device into buffer.  */
-static void scsi_read_data(SCSIRequest *req)
-{
-    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
-    SCSIDevice *s = r->req.dev;
-    int ret;
-
-    DPRINTF("scsi_read_data 0x%x\n", req->tag);
-
-    /* The request is used as the AIO opaque value, so add a ref.  */
-    scsi_req_ref(&r->req);
-    if (r->len == -1) {
-        scsi_command_complete(r, 0);
-        return;
-    }
-
-    ret = execute_command(s->conf.bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
-    if (ret < 0) {
-        scsi_command_complete(r, ret);
-    }
-}
-
-static void scsi_write_complete(void * opaque, int ret)
-{
-    SCSIGenericReq *r = (SCSIGenericReq *)opaque;
-    SCSIDevice *s = r->req.dev;
-
-    DPRINTF("scsi_write_complete() ret = %d\n", ret);
-    r->req.aiocb = NULL;
-    if (ret) {
-        DPRINTF("IO error\n");
-        scsi_command_complete(r, ret);
-        return;
-    }
-
-    if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 &&
-        s->type == TYPE_TAPE) {
-        s->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
-        DPRINTF("block size %d\n", s->blocksize);
-    }
-
-    scsi_command_complete(r, ret);
-}
-
-/* Write data to a scsi device.  Returns nonzero on failure.
-   The transfer may complete asynchronously.  */
-static void scsi_write_data(SCSIRequest *req)
-{
-    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
-    SCSIDevice *s = r->req.dev;
-    int ret;
-
-    DPRINTF("scsi_write_data 0x%x\n", req->tag);
-    if (r->len == 0) {
-        r->len = r->buflen;
-        scsi_req_data(&r->req, r->len);
-        return;
-    }
-
-    /* The request is used as the AIO opaque value, so add a ref.  */
-    scsi_req_ref(&r->req);
-    ret = execute_command(s->conf.bs, r, SG_DXFER_TO_DEV, scsi_write_complete);
-    if (ret < 0) {
-        scsi_command_complete(r, ret);
-    }
-}
-
-/* Return a pointer to the data buffer.  */
-static uint8_t *scsi_get_buf(SCSIRequest *req)
-{
-    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
-
-    return r->buf;
-}
-
-/* Execute a scsi command.  Returns the length of the data expected by the
-   command.  This will be Positive for data transfers from the device
-   (eg. disk reads), negative for transfers to the device (eg. disk writes),
-   and zero if the command does not transfer any data.  */
-
-static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
-{
-    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
-    SCSIDevice *s = r->req.dev;
-    int ret;
-
-    DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x", lun, tag,
-            r->req.cmd.xfer, cmd[0]);
-
-#ifdef DEBUG_SCSI
-    {
-        int i;
-        for (i = 1; i < r->req.cmd.len; i++) {
-            printf(" 0x%02x", cmd[i]);
-        }
-        printf("\n");
-    }
-#endif
-
-    if (r->req.cmd.xfer == 0) {
-        if (r->buf != NULL)
-            g_free(r->buf);
-        r->buflen = 0;
-        r->buf = NULL;
-        /* The request is used as the AIO opaque value, so add a ref.  */
-        scsi_req_ref(&r->req);
-        ret = execute_command(s->conf.bs, r, SG_DXFER_NONE, scsi_command_complete);
-        if (ret < 0) {
-            scsi_command_complete(r, ret);
-            return 0;
-        }
-        return 0;
-    }
-
-    if (r->buflen != r->req.cmd.xfer) {
-        if (r->buf != NULL)
-            g_free(r->buf);
-        r->buf = g_malloc(r->req.cmd.xfer);
-        r->buflen = r->req.cmd.xfer;
-    }
-
-    memset(r->buf, 0, r->buflen);
-    r->len = r->req.cmd.xfer;
-    if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
-        r->len = 0;
-        return -r->req.cmd.xfer;
-    } else {
-        return r->req.cmd.xfer;
-    }
-}
-
-static int get_stream_blocksize(BlockDriverState *bdrv)
-{
-    uint8_t cmd[6];
-    uint8_t buf[12];
-    uint8_t sensebuf[8];
-    sg_io_hdr_t io_header;
-    int ret;
-
-    memset(cmd, 0, sizeof(cmd));
-    memset(buf, 0, sizeof(buf));
-    cmd[0] = MODE_SENSE;
-    cmd[4] = sizeof(buf);
-
-    memset(&io_header, 0, sizeof(io_header));
-    io_header.interface_id = 'S';
-    io_header.dxfer_direction = SG_DXFER_FROM_DEV;
-    io_header.dxfer_len = sizeof(buf);
-    io_header.dxferp = buf;
-    io_header.cmdp = cmd;
-    io_header.cmd_len = sizeof(cmd);
-    io_header.mx_sb_len = sizeof(sensebuf);
-    io_header.sbp = sensebuf;
-    io_header.timeout = 6000; /* XXX */
-
-    ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
-    if (ret < 0 || io_header.driver_status || io_header.host_status) {
-        return -1;
-    }
-    return (buf[9] << 16) | (buf[10] << 8) | buf[11];
-}
-
-static void scsi_generic_reset(DeviceState *dev)
-{
-    SCSIDevice *s = SCSI_DEVICE(dev);
-
-    scsi_device_purge_requests(s, SENSE_CODE(RESET));
-}
-
-static void scsi_destroy(SCSIDevice *s)
-{
-    scsi_device_purge_requests(s, SENSE_CODE(NO_SENSE));
-    blockdev_mark_auto_del(s->conf.bs);
-}
-
-static int scsi_generic_initfn(SCSIDevice *s)
-{
-    int sg_version;
-    struct sg_scsi_id scsiid;
-
-    if (!s->conf.bs) {
-        error_report("drive property not set");
-        return -1;
-    }
-
-    if (bdrv_get_on_error(s->conf.bs, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
-        error_report("Device doesn't support drive option werror");
-        return -1;
-    }
-    if (bdrv_get_on_error(s->conf.bs, 1) != BLOCKDEV_ON_ERROR_REPORT) {
-        error_report("Device doesn't support drive option rerror");
-        return -1;
-    }
-
-    /* check we are using a driver managing SG_IO (version 3 and after */
-    if (bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0) {
-        error_report("scsi generic interface not supported");
-        return -1;
-    }
-    if (sg_version < 30000) {
-        error_report("scsi generic interface too old");
-        return -1;
-    }
-
-    /* get LUN of the /dev/sg? */
-    if (bdrv_ioctl(s->conf.bs, SG_GET_SCSI_ID, &scsiid)) {
-        error_report("SG_GET_SCSI_ID ioctl failed");
-        return -1;
-    }
-
-    /* define device state */
-    s->type = scsiid.scsi_type;
-    DPRINTF("device type %d\n", s->type);
-    if (s->type == TYPE_DISK || s->type == TYPE_ROM) {
-        add_boot_device_path(s->conf.bootindex, &s->qdev, NULL);
-    }
-
-    switch (s->type) {
-    case TYPE_TAPE:
-        s->blocksize = get_stream_blocksize(s->conf.bs);
-        if (s->blocksize == -1) {
-            s->blocksize = 0;
-        }
-        break;
-
-        /* Make a guess for block devices, we'll fix it when the guest sends.
-         * READ CAPACITY.  If they don't, they likely would assume these sizes
-         * anyway. (TODO: they could also send MODE SENSE).
-         */
-    case TYPE_ROM:
-    case TYPE_WORM:
-        s->blocksize = 2048;
-        break;
-    default:
-        s->blocksize = 512;
-        break;
-    }
-
-    DPRINTF("block size %d\n", s->blocksize);
-    return 0;
-}
-
-const SCSIReqOps scsi_generic_req_ops = {
-    .size         = sizeof(SCSIGenericReq),
-    .free_req     = scsi_free_request,
-    .send_command = scsi_send_command,
-    .read_data    = scsi_read_data,
-    .write_data   = scsi_write_data,
-    .cancel_io    = scsi_cancel_io,
-    .get_buf      = scsi_get_buf,
-    .load_request = scsi_generic_load_request,
-    .save_request = scsi_generic_save_request,
-};
-
-static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
-                                     uint8_t *buf, void *hba_private)
-{
-    SCSIRequest *req;
-
-    req = scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private);
-    return req;
-}
-
-static Property scsi_generic_properties[] = {
-    DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.bs),
-    DEFINE_PROP_INT32("bootindex", SCSIDevice, conf.bootindex, -1),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void scsi_generic_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
-
-    sc->init         = scsi_generic_initfn;
-    sc->destroy      = scsi_destroy;
-    sc->alloc_req    = scsi_new_request;
-    dc->fw_name = "disk";
-    dc->desc = "pass through generic scsi device (/dev/sg*)";
-    dc->reset = scsi_generic_reset;
-    dc->props = scsi_generic_properties;
-    dc->vmsd  = &vmstate_scsi_device;
-}
-
-static const TypeInfo scsi_generic_info = {
-    .name          = "scsi-generic",
-    .parent        = TYPE_SCSI_DEVICE,
-    .instance_size = sizeof(SCSIDevice),
-    .class_init    = scsi_generic_class_initfn,
-};
-
-static void scsi_generic_register_types(void)
-{
-    type_register_static(&scsi_generic_info);
-}
-
-type_init(scsi_generic_register_types)
-
-#endif /* __linux__ */
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6a56504068d4fa6f8844f6e73b5db39f937930f9 100644 (file)
@@ -0,0 +1,6 @@
+common-obj-y += scsi-disk.o
+common-obj-y += scsi-generic.o scsi-bus.o
+common-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o
+common-obj-$(CONFIG_MEGASAS_SCSI_PCI) += megasas.o
+common-obj-$(CONFIG_ESP) += esp.o
+common-obj-$(CONFIG_ESP_PCI) += esp-pci.o
diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c
new file mode 100644 (file)
index 0000000..3ca5c8c
--- /dev/null
@@ -0,0 +1,518 @@
+/*
+ * QEMU ESP/NCR53C9x emulation
+ *
+ * Copyright (c) 2005-2006 Fabrice Bellard
+ * Copyright (c) 2012 Herve Poussineau
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/pci/pci.h"
+#include "hw/nvram/eeprom93xx.h"
+#include "hw/scsi/esp.h"
+#include "trace.h"
+#include "qemu/log.h"
+
+#define TYPE_AM53C974_DEVICE "am53c974"
+
+#define DMA_CMD   0x0
+#define DMA_STC   0x1
+#define DMA_SPA   0x2
+#define DMA_WBC   0x3
+#define DMA_WAC   0x4
+#define DMA_STAT  0x5
+#define DMA_SMDLA 0x6
+#define DMA_WMAC  0x7
+
+#define DMA_CMD_MASK   0x03
+#define DMA_CMD_DIAG   0x04
+#define DMA_CMD_MDL    0x10
+#define DMA_CMD_INTE_P 0x20
+#define DMA_CMD_INTE_D 0x40
+#define DMA_CMD_DIR    0x80
+
+#define DMA_STAT_PWDN    0x01
+#define DMA_STAT_ERROR   0x02
+#define DMA_STAT_ABORT   0x04
+#define DMA_STAT_DONE    0x08
+#define DMA_STAT_SCSIINT 0x10
+#define DMA_STAT_BCMBLT  0x20
+
+#define SBAC_STATUS 0x1000
+
+typedef struct PCIESPState {
+    PCIDevice dev;
+    MemoryRegion io;
+    uint32_t dma_regs[8];
+    uint32_t sbac;
+    ESPState esp;
+} PCIESPState;
+
+static void esp_pci_handle_idle(PCIESPState *pci, uint32_t val)
+{
+    trace_esp_pci_dma_idle(val);
+    esp_dma_enable(&pci->esp, 0, 0);
+}
+
+static void esp_pci_handle_blast(PCIESPState *pci, uint32_t val)
+{
+    trace_esp_pci_dma_blast(val);
+    qemu_log_mask(LOG_UNIMP, "am53c974: cmd BLAST not implemented\n");
+}
+
+static void esp_pci_handle_abort(PCIESPState *pci, uint32_t val)
+{
+    trace_esp_pci_dma_abort(val);
+    if (pci->esp.current_req) {
+        scsi_req_cancel(pci->esp.current_req);
+    }
+}
+
+static void esp_pci_handle_start(PCIESPState *pci, uint32_t val)
+{
+    trace_esp_pci_dma_start(val);
+
+    pci->dma_regs[DMA_WBC] = pci->dma_regs[DMA_STC];
+    pci->dma_regs[DMA_WAC] = pci->dma_regs[DMA_SPA];
+    pci->dma_regs[DMA_WMAC] = pci->dma_regs[DMA_SMDLA];
+
+    pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_BCMBLT | DMA_STAT_SCSIINT
+                               | DMA_STAT_DONE | DMA_STAT_ABORT
+                               | DMA_STAT_ERROR | DMA_STAT_PWDN);
+
+    esp_dma_enable(&pci->esp, 0, 1);
+}
+
+static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val)
+{
+    trace_esp_pci_dma_write(saddr, pci->dma_regs[saddr], val);
+    switch (saddr) {
+    case DMA_CMD:
+        pci->dma_regs[saddr] = val;
+        switch (val & DMA_CMD_MASK) {
+        case 0x0: /* IDLE */
+            esp_pci_handle_idle(pci, val);
+            break;
+        case 0x1: /* BLAST */
+            esp_pci_handle_blast(pci, val);
+            break;
+        case 0x2: /* ABORT */
+            esp_pci_handle_abort(pci, val);
+            break;
+        case 0x3: /* START */
+            esp_pci_handle_start(pci, val);
+            break;
+        default: /* can't happen */
+            abort();
+        }
+        break;
+    case DMA_STC:
+    case DMA_SPA:
+    case DMA_SMDLA:
+        pci->dma_regs[saddr] = val;
+        break;
+    case DMA_STAT:
+        if (!(pci->sbac & SBAC_STATUS)) {
+            /* clear some bits on write */
+            uint32_t mask = DMA_STAT_ERROR | DMA_STAT_ABORT | DMA_STAT_DONE;
+            pci->dma_regs[DMA_STAT] &= ~(val & mask);
+        }
+        break;
+    default:
+        trace_esp_pci_error_invalid_write_dma(val, saddr);
+        return;
+    }
+}
+
+static uint32_t esp_pci_dma_read(PCIESPState *pci, uint32_t saddr)
+{
+    uint32_t val;
+
+    val = pci->dma_regs[saddr];
+    if (saddr == DMA_STAT) {
+        if (pci->esp.rregs[ESP_RSTAT] & STAT_INT) {
+            val |= DMA_STAT_SCSIINT;
+        }
+        if (pci->sbac & SBAC_STATUS) {
+            pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_ERROR | DMA_STAT_ABORT |
+                                         DMA_STAT_DONE);
+        }
+    }
+
+    trace_esp_pci_dma_read(saddr, val);
+    return val;
+}
+
+static void esp_pci_io_write(void *opaque, hwaddr addr,
+                             uint64_t val, unsigned int size)
+{
+    PCIESPState *pci = opaque;
+
+    if (size < 4 || addr & 3) {
+        /* need to upgrade request: we only support 4-bytes accesses */
+        uint32_t current = 0, mask;
+        int shift;
+
+        if (addr < 0x40) {
+            current = pci->esp.wregs[addr >> 2];
+        } else if (addr < 0x60) {
+            current = pci->dma_regs[(addr - 0x40) >> 2];
+        } else if (addr < 0x74) {
+            current = pci->sbac;
+        }
+
+        shift = (4 - size) * 8;
+        mask = (~(uint32_t)0 << shift) >> shift;
+
+        shift = ((4 - (addr & 3)) & 3) * 8;
+        val <<= shift;
+        val |= current & ~(mask << shift);
+        addr &= ~3;
+        size = 4;
+    }
+
+    if (addr < 0x40) {
+        /* SCSI core reg */
+        esp_reg_write(&pci->esp, addr >> 2, val);
+    } else if (addr < 0x60) {
+        /* PCI DMA CCB */
+        esp_pci_dma_write(pci, (addr - 0x40) >> 2, val);
+    } else if (addr == 0x70) {
+        /* DMA SCSI Bus and control */
+        trace_esp_pci_sbac_write(pci->sbac, val);
+        pci->sbac = val;
+    } else {
+        trace_esp_pci_error_invalid_write((int)addr);
+    }
+}
+
+static uint64_t esp_pci_io_read(void *opaque, hwaddr addr,
+                                unsigned int size)
+{
+    PCIESPState *pci = opaque;
+    uint32_t ret;
+
+    if (addr < 0x40) {
+        /* SCSI core reg */
+        ret = esp_reg_read(&pci->esp, addr >> 2);
+    } else if (addr < 0x60) {
+        /* PCI DMA CCB */
+        ret = esp_pci_dma_read(pci, (addr - 0x40) >> 2);
+    } else if (addr == 0x70) {
+        /* DMA SCSI Bus and control */
+        trace_esp_pci_sbac_read(pci->sbac);
+        ret = pci->sbac;
+    } else {
+        /* Invalid region */
+        trace_esp_pci_error_invalid_read((int)addr);
+        ret = 0;
+    }
+
+    /* give only requested data */
+    ret >>= (addr & 3) * 8;
+    ret &= ~(~(uint64_t)0 << (8 * size));
+
+    return ret;
+}
+
+static void esp_pci_dma_memory_rw(PCIESPState *pci, uint8_t *buf, int len,
+                                  DMADirection dir)
+{
+    dma_addr_t addr;
+    DMADirection expected_dir;
+
+    if (pci->dma_regs[DMA_CMD] & DMA_CMD_DIR) {
+        expected_dir = DMA_DIRECTION_FROM_DEVICE;
+    } else {
+        expected_dir = DMA_DIRECTION_TO_DEVICE;
+    }
+
+    if (dir != expected_dir) {
+        trace_esp_pci_error_invalid_dma_direction();
+        return;
+    }
+
+    if (pci->dma_regs[DMA_STAT] & DMA_CMD_MDL) {
+        qemu_log_mask(LOG_UNIMP, "am53c974: MDL transfer not implemented\n");
+    }
+
+    addr = pci->dma_regs[DMA_SPA];
+    if (pci->dma_regs[DMA_WBC] < len) {
+        len = pci->dma_regs[DMA_WBC];
+    }
+
+    pci_dma_rw(&pci->dev, addr, buf, len, dir);
+
+    /* update status registers */
+    pci->dma_regs[DMA_WBC] -= len;
+    pci->dma_regs[DMA_WAC] += len;
+}
+
+static void esp_pci_dma_memory_read(void *opaque, uint8_t *buf, int len)
+{
+    PCIESPState *pci = opaque;
+    esp_pci_dma_memory_rw(pci, buf, len, DMA_DIRECTION_TO_DEVICE);
+}
+
+static void esp_pci_dma_memory_write(void *opaque, uint8_t *buf, int len)
+{
+    PCIESPState *pci = opaque;
+    esp_pci_dma_memory_rw(pci, buf, len, DMA_DIRECTION_FROM_DEVICE);
+}
+
+static const MemoryRegionOps esp_pci_io_ops = {
+    .read = esp_pci_io_read,
+    .write = esp_pci_io_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+};
+
+static void esp_pci_hard_reset(DeviceState *dev)
+{
+    PCIESPState *pci = DO_UPCAST(PCIESPState, dev.qdev, dev);
+    esp_hard_reset(&pci->esp);
+    pci->dma_regs[DMA_CMD] &= ~(DMA_CMD_DIR | DMA_CMD_INTE_D | DMA_CMD_INTE_P
+                              | DMA_CMD_MDL | DMA_CMD_DIAG | DMA_CMD_MASK);
+    pci->dma_regs[DMA_WBC] &= ~0xffff;
+    pci->dma_regs[DMA_WAC] = 0xffffffff;
+    pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_BCMBLT | DMA_STAT_SCSIINT
+                               | DMA_STAT_DONE | DMA_STAT_ABORT
+                               | DMA_STAT_ERROR);
+    pci->dma_regs[DMA_WMAC] = 0xfffffffd;
+}
+
+static const VMStateDescription vmstate_esp_pci_scsi = {
+    .name = "pciespscsi",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(dev, PCIESPState),
+        VMSTATE_BUFFER_UNSAFE(dma_regs, PCIESPState, 0, 8 * sizeof(uint32_t)),
+        VMSTATE_STRUCT(esp, PCIESPState, 0, vmstate_esp, ESPState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void esp_pci_command_complete(SCSIRequest *req, uint32_t status,
+                                     size_t resid)
+{
+    ESPState *s = req->hba_private;
+    PCIESPState *pci = container_of(s, PCIESPState, esp);
+
+    esp_command_complete(req, status, resid);
+    pci->dma_regs[DMA_WBC] = 0;
+    pci->dma_regs[DMA_STAT] |= DMA_STAT_DONE;
+}
+
+static const struct SCSIBusInfo esp_pci_scsi_info = {
+    .tcq = false,
+    .max_target = ESP_MAX_DEVS,
+    .max_lun = 7,
+
+    .transfer_data = esp_transfer_data,
+    .complete = esp_pci_command_complete,
+    .cancel = esp_request_cancelled,
+};
+
+static int esp_pci_scsi_init(PCIDevice *dev)
+{
+    PCIESPState *pci = DO_UPCAST(PCIESPState, dev, dev);
+    ESPState *s = &pci->esp;
+    uint8_t *pci_conf;
+
+    pci_conf = pci->dev.config;
+
+    /* Interrupt pin A */
+    pci_conf[PCI_INTERRUPT_PIN] = 0x01;
+
+    s->dma_memory_read = esp_pci_dma_memory_read;
+    s->dma_memory_write = esp_pci_dma_memory_write;
+    s->dma_opaque = pci;
+    s->chip_id = TCHI_AM53C974;
+    memory_region_init_io(&pci->io, &esp_pci_io_ops, pci, "esp-io", 0x80);
+
+    pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->io);
+    s->irq = pci->dev.irq[0];
+
+    scsi_bus_new(&s->bus, &dev->qdev, &esp_pci_scsi_info);
+    if (!dev->qdev.hotplugged) {
+        return scsi_bus_legacy_handle_cmdline(&s->bus);
+    }
+    return 0;
+}
+
+static void esp_pci_scsi_uninit(PCIDevice *d)
+{
+    PCIESPState *pci = DO_UPCAST(PCIESPState, dev, d);
+
+    memory_region_destroy(&pci->io);
+}
+
+static void esp_pci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = esp_pci_scsi_init;
+    k->exit = esp_pci_scsi_uninit;
+    k->vendor_id = PCI_VENDOR_ID_AMD;
+    k->device_id = PCI_DEVICE_ID_AMD_SCSI;
+    k->revision = 0x10;
+    k->class_id = PCI_CLASS_STORAGE_SCSI;
+    dc->desc = "AMD Am53c974 PCscsi-PCI SCSI adapter";
+    dc->reset = esp_pci_hard_reset;
+    dc->vmsd = &vmstate_esp_pci_scsi;
+}
+
+static const TypeInfo esp_pci_info = {
+    .name = TYPE_AM53C974_DEVICE,
+    .parent = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(PCIESPState),
+    .class_init = esp_pci_class_init,
+};
+
+typedef struct {
+    PCIESPState pci;
+    eeprom_t *eeprom;
+} DC390State;
+
+#define TYPE_DC390_DEVICE "dc390"
+#define DC390(obj) \
+    OBJECT_CHECK(DC390State, obj, TYPE_DC390_DEVICE)
+
+#define EE_ADAPT_SCSI_ID 64
+#define EE_MODE2         65
+#define EE_DELAY         66
+#define EE_TAG_CMD_NUM   67
+#define EE_ADAPT_OPTIONS 68
+#define EE_BOOT_SCSI_ID  69
+#define EE_BOOT_SCSI_LUN 70
+#define EE_CHKSUM1       126
+#define EE_CHKSUM2       127
+
+#define EE_ADAPT_OPTION_F6_F8_AT_BOOT   0x01
+#define EE_ADAPT_OPTION_BOOT_FROM_CDROM 0x02
+#define EE_ADAPT_OPTION_INT13           0x04
+#define EE_ADAPT_OPTION_SCAM_SUPPORT    0x08
+
+
+static uint32_t dc390_read_config(PCIDevice *dev, uint32_t addr, int l)
+{
+    DC390State *pci = DC390(dev);
+    uint32_t val;
+
+    val = pci_default_read_config(dev, addr, l);
+
+    if (addr == 0x00 && l == 1) {
+        /* First byte of address space is AND-ed with EEPROM DO line */
+        if (!eeprom93xx_read(pci->eeprom)) {
+            val &= ~0xff;
+        }
+    }
+
+    return val;
+}
+
+static void dc390_write_config(PCIDevice *dev,
+                               uint32_t addr, uint32_t val, int l)
+{
+    DC390State *pci = DC390(dev);
+    if (addr == 0x80) {
+        /* EEPROM write */
+        int eesk = val & 0x80 ? 1 : 0;
+        int eedi = val & 0x40 ? 1 : 0;
+        eeprom93xx_write(pci->eeprom, 1, eesk, eedi);
+    } else if (addr == 0xc0) {
+        /* EEPROM CS low */
+        eeprom93xx_write(pci->eeprom, 0, 0, 0);
+    } else {
+        pci_default_write_config(dev, addr, val, l);
+    }
+}
+
+static int dc390_scsi_init(PCIDevice *dev)
+{
+    DC390State *pci = DC390(dev);
+    uint8_t *contents;
+    uint16_t chksum = 0;
+    int i, ret;
+
+    /* init base class */
+    ret = esp_pci_scsi_init(dev);
+    if (ret < 0) {
+        return ret;
+    }
+
+    /* EEPROM */
+    pci->eeprom = eeprom93xx_new(DEVICE(dev), 64);
+
+    /* set default eeprom values */
+    contents = (uint8_t *)eeprom93xx_data(pci->eeprom);
+
+    for (i = 0; i < 16; i++) {
+        contents[i * 2] = 0x57;
+        contents[i * 2 + 1] = 0x00;
+    }
+    contents[EE_ADAPT_SCSI_ID] = 7;
+    contents[EE_MODE2] = 0x0f;
+    contents[EE_TAG_CMD_NUM] = 0x04;
+    contents[EE_ADAPT_OPTIONS] = EE_ADAPT_OPTION_F6_F8_AT_BOOT
+                               | EE_ADAPT_OPTION_BOOT_FROM_CDROM
+                               | EE_ADAPT_OPTION_INT13;
+
+    /* update eeprom checksum */
+    for (i = 0; i < EE_CHKSUM1; i += 2) {
+        chksum += contents[i] + (((uint16_t)contents[i + 1]) << 8);
+    }
+    chksum = 0x1234 - chksum;
+    contents[EE_CHKSUM1] = chksum & 0xff;
+    contents[EE_CHKSUM2] = chksum >> 8;
+
+    return 0;
+}
+
+static void dc390_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = dc390_scsi_init;
+    k->config_read = dc390_read_config;
+    k->config_write = dc390_write_config;
+    dc->desc = "Tekram DC-390 SCSI adapter";
+}
+
+static const TypeInfo dc390_info = {
+    .name = "dc390",
+    .parent = TYPE_AM53C974_DEVICE,
+    .instance_size = sizeof(DC390State),
+    .class_init = dc390_class_init,
+};
+
+static void esp_pci_register_types(void)
+{
+    type_register_static(&esp_pci_info);
+    type_register_static(&dc390_info);
+}
+
+type_init(esp_pci_register_types)
diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
new file mode 100644 (file)
index 0000000..17adbec
--- /dev/null
@@ -0,0 +1,727 @@
+/*
+ * QEMU ESP/NCR53C9x emulation
+ *
+ * Copyright (c) 2005-2006 Fabrice Bellard
+ * Copyright (c) 2012 Herve Poussineau
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/scsi/esp.h"
+#include "trace.h"
+#include "qemu/log.h"
+
+/*
+ * On Sparc32, this is the ESP (NCR53C90) part of chip STP2000 (Master I/O),
+ * also produced as NCR89C100. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
+ * and
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR53C9X.txt
+ */
+
+static void esp_raise_irq(ESPState *s)
+{
+    if (!(s->rregs[ESP_RSTAT] & STAT_INT)) {
+        s->rregs[ESP_RSTAT] |= STAT_INT;
+        qemu_irq_raise(s->irq);
+        trace_esp_raise_irq();
+    }
+}
+
+static void esp_lower_irq(ESPState *s)
+{
+    if (s->rregs[ESP_RSTAT] & STAT_INT) {
+        s->rregs[ESP_RSTAT] &= ~STAT_INT;
+        qemu_irq_lower(s->irq);
+        trace_esp_lower_irq();
+    }
+}
+
+void esp_dma_enable(ESPState *s, int irq, int level)
+{
+    if (level) {
+        s->dma_enabled = 1;
+        trace_esp_dma_enable();
+        if (s->dma_cb) {
+            s->dma_cb(s);
+            s->dma_cb = NULL;
+        }
+    } else {
+        trace_esp_dma_disable();
+        s->dma_enabled = 0;
+    }
+}
+
+void esp_request_cancelled(SCSIRequest *req)
+{
+    ESPState *s = req->hba_private;
+
+    if (req == s->current_req) {
+        scsi_req_unref(s->current_req);
+        s->current_req = NULL;
+        s->current_dev = NULL;
+    }
+}
+
+static uint32_t get_cmd(ESPState *s, uint8_t *buf)
+{
+    uint32_t dmalen;
+    int target;
+
+    target = s->wregs[ESP_WBUSID] & BUSID_DID;
+    if (s->dma) {
+        dmalen = s->rregs[ESP_TCLO];
+        dmalen |= s->rregs[ESP_TCMID] << 8;
+        dmalen |= s->rregs[ESP_TCHI] << 16;
+        s->dma_memory_read(s->dma_opaque, buf, dmalen);
+    } else {
+        dmalen = s->ti_size;
+        memcpy(buf, s->ti_buf, dmalen);
+        buf[0] = buf[2] >> 5;
+    }
+    trace_esp_get_cmd(dmalen, target);
+
+    s->ti_size = 0;
+    s->ti_rptr = 0;
+    s->ti_wptr = 0;
+
+    if (s->current_req) {
+        /* Started a new command before the old one finished.  Cancel it.  */
+        scsi_req_cancel(s->current_req);
+        s->async_len = 0;
+    }
+
+    s->current_dev = scsi_device_find(&s->bus, 0, target, 0);
+    if (!s->current_dev) {
+        // No such drive
+        s->rregs[ESP_RSTAT] = 0;
+        s->rregs[ESP_RINTR] = INTR_DC;
+        s->rregs[ESP_RSEQ] = SEQ_0;
+        esp_raise_irq(s);
+        return 0;
+    }
+    return dmalen;
+}
+
+static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid)
+{
+    int32_t datalen;
+    int lun;
+    SCSIDevice *current_lun;
+
+    trace_esp_do_busid_cmd(busid);
+    lun = busid & 7;
+    current_lun = scsi_device_find(&s->bus, 0, s->current_dev->id, lun);
+    s->current_req = scsi_req_new(current_lun, 0, lun, buf, s);
+    datalen = scsi_req_enqueue(s->current_req);
+    s->ti_size = datalen;
+    if (datalen != 0) {
+        s->rregs[ESP_RSTAT] = STAT_TC;
+        s->dma_left = 0;
+        s->dma_counter = 0;
+        if (datalen > 0) {
+            s->rregs[ESP_RSTAT] |= STAT_DI;
+        } else {
+            s->rregs[ESP_RSTAT] |= STAT_DO;
+        }
+        scsi_req_continue(s->current_req);
+    }
+    s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
+    s->rregs[ESP_RSEQ] = SEQ_CD;
+    esp_raise_irq(s);
+}
+
+static void do_cmd(ESPState *s, uint8_t *buf)
+{
+    uint8_t busid = buf[0];
+
+    do_busid_cmd(s, &buf[1], busid);
+}
+
+static void handle_satn(ESPState *s)
+{
+    uint8_t buf[32];
+    int len;
+
+    if (s->dma && !s->dma_enabled) {
+        s->dma_cb = handle_satn;
+        return;
+    }
+    len = get_cmd(s, buf);
+    if (len)
+        do_cmd(s, buf);
+}
+
+static void handle_s_without_atn(ESPState *s)
+{
+    uint8_t buf[32];
+    int len;
+
+    if (s->dma && !s->dma_enabled) {
+        s->dma_cb = handle_s_without_atn;
+        return;
+    }
+    len = get_cmd(s, buf);
+    if (len) {
+        do_busid_cmd(s, buf, 0);
+    }
+}
+
+static void handle_satn_stop(ESPState *s)
+{
+    if (s->dma && !s->dma_enabled) {
+        s->dma_cb = handle_satn_stop;
+        return;
+    }
+    s->cmdlen = get_cmd(s, s->cmdbuf);
+    if (s->cmdlen) {
+        trace_esp_handle_satn_stop(s->cmdlen);
+        s->do_cmd = 1;
+        s->rregs[ESP_RSTAT] = STAT_TC | STAT_CD;
+        s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
+        s->rregs[ESP_RSEQ] = SEQ_CD;
+        esp_raise_irq(s);
+    }
+}
+
+static void write_response(ESPState *s)
+{
+    trace_esp_write_response(s->status);
+    s->ti_buf[0] = s->status;
+    s->ti_buf[1] = 0;
+    if (s->dma) {
+        s->dma_memory_write(s->dma_opaque, s->ti_buf, 2);
+        s->rregs[ESP_RSTAT] = STAT_TC | STAT_ST;
+        s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
+        s->rregs[ESP_RSEQ] = SEQ_CD;
+    } else {
+        s->ti_size = 2;
+        s->ti_rptr = 0;
+        s->ti_wptr = 0;
+        s->rregs[ESP_RFLAGS] = 2;
+    }
+    esp_raise_irq(s);
+}
+
+static void esp_dma_done(ESPState *s)
+{
+    s->rregs[ESP_RSTAT] |= STAT_TC;
+    s->rregs[ESP_RINTR] = INTR_BS;
+    s->rregs[ESP_RSEQ] = 0;
+    s->rregs[ESP_RFLAGS] = 0;
+    s->rregs[ESP_TCLO] = 0;
+    s->rregs[ESP_TCMID] = 0;
+    s->rregs[ESP_TCHI] = 0;
+    esp_raise_irq(s);
+}
+
+static void esp_do_dma(ESPState *s)
+{
+    uint32_t len;
+    int to_device;
+
+    to_device = (s->ti_size < 0);
+    len = s->dma_left;
+    if (s->do_cmd) {
+        trace_esp_do_dma(s->cmdlen, len);
+        s->dma_memory_read(s->dma_opaque, &s->cmdbuf[s->cmdlen], len);
+        s->ti_size = 0;
+        s->cmdlen = 0;
+        s->do_cmd = 0;
+        do_cmd(s, s->cmdbuf);
+        return;
+    }
+    if (s->async_len == 0) {
+        /* Defer until data is available.  */
+        return;
+    }
+    if (len > s->async_len) {
+        len = s->async_len;
+    }
+    if (to_device) {
+        s->dma_memory_read(s->dma_opaque, s->async_buf, len);
+    } else {
+        s->dma_memory_write(s->dma_opaque, s->async_buf, len);
+    }
+    s->dma_left -= len;
+    s->async_buf += len;
+    s->async_len -= len;
+    if (to_device)
+        s->ti_size += len;
+    else
+        s->ti_size -= len;
+    if (s->async_len == 0) {
+        scsi_req_continue(s->current_req);
+        /* If there is still data to be read from the device then
+           complete the DMA operation immediately.  Otherwise defer
+           until the scsi layer has completed.  */
+        if (to_device || s->dma_left != 0 || s->ti_size == 0) {
+            return;
+        }
+    }
+
+    /* Partially filled a scsi buffer. Complete immediately.  */
+    esp_dma_done(s);
+}
+
+void esp_command_complete(SCSIRequest *req, uint32_t status,
+                                 size_t resid)
+{
+    ESPState *s = req->hba_private;
+
+    trace_esp_command_complete();
+    if (s->ti_size != 0) {
+        trace_esp_command_complete_unexpected();
+    }
+    s->ti_size = 0;
+    s->dma_left = 0;
+    s->async_len = 0;
+    if (status) {
+        trace_esp_command_complete_fail();
+    }
+    s->status = status;
+    s->rregs[ESP_RSTAT] = STAT_ST;
+    esp_dma_done(s);
+    if (s->current_req) {
+        scsi_req_unref(s->current_req);
+        s->current_req = NULL;
+        s->current_dev = NULL;
+    }
+}
+
+void esp_transfer_data(SCSIRequest *req, uint32_t len)
+{
+    ESPState *s = req->hba_private;
+
+    trace_esp_transfer_data(s->dma_left, s->ti_size);
+    s->async_len = len;
+    s->async_buf = scsi_req_get_buf(req);
+    if (s->dma_left) {
+        esp_do_dma(s);
+    } else if (s->dma_counter != 0 && s->ti_size <= 0) {
+        /* If this was the last part of a DMA transfer then the
+           completion interrupt is deferred to here.  */
+        esp_dma_done(s);
+    }
+}
+
+static void handle_ti(ESPState *s)
+{
+    uint32_t dmalen, minlen;
+
+    if (s->dma && !s->dma_enabled) {
+        s->dma_cb = handle_ti;
+        return;
+    }
+
+    dmalen = s->rregs[ESP_TCLO];
+    dmalen |= s->rregs[ESP_TCMID] << 8;
+    dmalen |= s->rregs[ESP_TCHI] << 16;
+    if (dmalen==0) {
+      dmalen=0x10000;
+    }
+    s->dma_counter = dmalen;
+
+    if (s->do_cmd)
+        minlen = (dmalen < 32) ? dmalen : 32;
+    else if (s->ti_size < 0)
+        minlen = (dmalen < -s->ti_size) ? dmalen : -s->ti_size;
+    else
+        minlen = (dmalen < s->ti_size) ? dmalen : s->ti_size;
+    trace_esp_handle_ti(minlen);
+    if (s->dma) {
+        s->dma_left = minlen;
+        s->rregs[ESP_RSTAT] &= ~STAT_TC;
+        esp_do_dma(s);
+    } else if (s->do_cmd) {
+        trace_esp_handle_ti_cmd(s->cmdlen);
+        s->ti_size = 0;
+        s->cmdlen = 0;
+        s->do_cmd = 0;
+        do_cmd(s, s->cmdbuf);
+        return;
+    }
+}
+
+void esp_hard_reset(ESPState *s)
+{
+    memset(s->rregs, 0, ESP_REGS);
+    memset(s->wregs, 0, ESP_REGS);
+    s->rregs[ESP_TCHI] = s->chip_id;
+    s->ti_size = 0;
+    s->ti_rptr = 0;
+    s->ti_wptr = 0;
+    s->dma = 0;
+    s->do_cmd = 0;
+    s->dma_cb = NULL;
+
+    s->rregs[ESP_CFG1] = 7;
+}
+
+static void esp_soft_reset(ESPState *s)
+{
+    qemu_irq_lower(s->irq);
+    esp_hard_reset(s);
+}
+
+static void parent_esp_reset(ESPState *s, int irq, int level)
+{
+    if (level) {
+        esp_soft_reset(s);
+    }
+}
+
+uint64_t esp_reg_read(ESPState *s, uint32_t saddr)
+{
+    uint32_t old_val;
+
+    trace_esp_mem_readb(saddr, s->rregs[saddr]);
+    switch (saddr) {
+    case ESP_FIFO:
+        if (s->ti_size > 0) {
+            s->ti_size--;
+            if ((s->rregs[ESP_RSTAT] & STAT_PIO_MASK) == 0) {
+                /* Data out.  */
+                qemu_log_mask(LOG_UNIMP,
+                              "esp: PIO data read not implemented\n");
+                s->rregs[ESP_FIFO] = 0;
+            } else {
+                s->rregs[ESP_FIFO] = s->ti_buf[s->ti_rptr++];
+            }
+            esp_raise_irq(s);
+        }
+        if (s->ti_size == 0) {
+            s->ti_rptr = 0;
+            s->ti_wptr = 0;
+        }
+        break;
+    case ESP_RINTR:
+        /* Clear sequence step, interrupt register and all status bits
+           except TC */
+        old_val = s->rregs[ESP_RINTR];
+        s->rregs[ESP_RINTR] = 0;
+        s->rregs[ESP_RSTAT] &= ~STAT_TC;
+        s->rregs[ESP_RSEQ] = SEQ_CD;
+        esp_lower_irq(s);
+
+        return old_val;
+    default:
+        break;
+    }
+    return s->rregs[saddr];
+}
+
+void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val)
+{
+    trace_esp_mem_writeb(saddr, s->wregs[saddr], val);
+    switch (saddr) {
+    case ESP_TCLO:
+    case ESP_TCMID:
+    case ESP_TCHI:
+        s->rregs[ESP_RSTAT] &= ~STAT_TC;
+        break;
+    case ESP_FIFO:
+        if (s->do_cmd) {
+            s->cmdbuf[s->cmdlen++] = val & 0xff;
+        } else if (s->ti_size == TI_BUFSZ - 1) {
+            trace_esp_error_fifo_overrun();
+        } else {
+            s->ti_size++;
+            s->ti_buf[s->ti_wptr++] = val & 0xff;
+        }
+        break;
+    case ESP_CMD:
+        s->rregs[saddr] = val;
+        if (val & CMD_DMA) {
+            s->dma = 1;
+            /* Reload DMA counter.  */
+            s->rregs[ESP_TCLO] = s->wregs[ESP_TCLO];
+            s->rregs[ESP_TCMID] = s->wregs[ESP_TCMID];
+            s->rregs[ESP_TCHI] = s->wregs[ESP_TCHI];
+        } else {
+            s->dma = 0;
+        }
+        switch(val & CMD_CMD) {
+        case CMD_NOP:
+            trace_esp_mem_writeb_cmd_nop(val);
+            break;
+        case CMD_FLUSH:
+            trace_esp_mem_writeb_cmd_flush(val);
+            //s->ti_size = 0;
+            s->rregs[ESP_RINTR] = INTR_FC;
+            s->rregs[ESP_RSEQ] = 0;
+            s->rregs[ESP_RFLAGS] = 0;
+            break;
+        case CMD_RESET:
+            trace_esp_mem_writeb_cmd_reset(val);
+            esp_soft_reset(s);
+            break;
+        case CMD_BUSRESET:
+            trace_esp_mem_writeb_cmd_bus_reset(val);
+            s->rregs[ESP_RINTR] = INTR_RST;
+            if (!(s->wregs[ESP_CFG1] & CFG1_RESREPT)) {
+                esp_raise_irq(s);
+            }
+            break;
+        case CMD_TI:
+            handle_ti(s);
+            break;
+        case CMD_ICCS:
+            trace_esp_mem_writeb_cmd_iccs(val);
+            write_response(s);
+            s->rregs[ESP_RINTR] = INTR_FC;
+            s->rregs[ESP_RSTAT] |= STAT_MI;
+            break;
+        case CMD_MSGACC:
+            trace_esp_mem_writeb_cmd_msgacc(val);
+            s->rregs[ESP_RINTR] = INTR_DC;
+            s->rregs[ESP_RSEQ] = 0;
+            s->rregs[ESP_RFLAGS] = 0;
+            esp_raise_irq(s);
+            break;
+        case CMD_PAD:
+            trace_esp_mem_writeb_cmd_pad(val);
+            s->rregs[ESP_RSTAT] = STAT_TC;
+            s->rregs[ESP_RINTR] = INTR_FC;
+            s->rregs[ESP_RSEQ] = 0;
+            break;
+        case CMD_SATN:
+            trace_esp_mem_writeb_cmd_satn(val);
+            break;
+        case CMD_RSTATN:
+            trace_esp_mem_writeb_cmd_rstatn(val);
+            break;
+        case CMD_SEL:
+            trace_esp_mem_writeb_cmd_sel(val);
+            handle_s_without_atn(s);
+            break;
+        case CMD_SELATN:
+            trace_esp_mem_writeb_cmd_selatn(val);
+            handle_satn(s);
+            break;
+        case CMD_SELATNS:
+            trace_esp_mem_writeb_cmd_selatns(val);
+            handle_satn_stop(s);
+            break;
+        case CMD_ENSEL:
+            trace_esp_mem_writeb_cmd_ensel(val);
+            s->rregs[ESP_RINTR] = 0;
+            break;
+        case CMD_DISSEL:
+            trace_esp_mem_writeb_cmd_dissel(val);
+            s->rregs[ESP_RINTR] = 0;
+            esp_raise_irq(s);
+            break;
+        default:
+            trace_esp_error_unhandled_command(val);
+            break;
+        }
+        break;
+    case ESP_WBUSID ... ESP_WSYNO:
+        break;
+    case ESP_CFG1:
+    case ESP_CFG2: case ESP_CFG3:
+    case ESP_RES3: case ESP_RES4:
+        s->rregs[saddr] = val;
+        break;
+    case ESP_WCCF ... ESP_WTEST:
+        break;
+    default:
+        trace_esp_error_invalid_write(val, saddr);
+        return;
+    }
+    s->wregs[saddr] = val;
+}
+
+static bool esp_mem_accepts(void *opaque, hwaddr addr,
+                            unsigned size, bool is_write)
+{
+    return (size == 1) || (is_write && size == 4);
+}
+
+const VMStateDescription vmstate_esp = {
+    .name ="esp",
+    .version_id = 3,
+    .minimum_version_id = 3,
+    .minimum_version_id_old = 3,
+    .fields      = (VMStateField []) {
+        VMSTATE_BUFFER(rregs, ESPState),
+        VMSTATE_BUFFER(wregs, ESPState),
+        VMSTATE_INT32(ti_size, ESPState),
+        VMSTATE_UINT32(ti_rptr, ESPState),
+        VMSTATE_UINT32(ti_wptr, ESPState),
+        VMSTATE_BUFFER(ti_buf, ESPState),
+        VMSTATE_UINT32(status, ESPState),
+        VMSTATE_UINT32(dma, ESPState),
+        VMSTATE_BUFFER(cmdbuf, ESPState),
+        VMSTATE_UINT32(cmdlen, ESPState),
+        VMSTATE_UINT32(do_cmd, ESPState),
+        VMSTATE_UINT32(dma_left, ESPState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    uint32_t it_shift;
+    ESPState esp;
+} SysBusESPState;
+
+static void sysbus_esp_mem_write(void *opaque, hwaddr addr,
+                                 uint64_t val, unsigned int size)
+{
+    SysBusESPState *sysbus = opaque;
+    uint32_t saddr;
+
+    saddr = addr >> sysbus->it_shift;
+    esp_reg_write(&sysbus->esp, saddr, val);
+}
+
+static uint64_t sysbus_esp_mem_read(void *opaque, hwaddr addr,
+                                    unsigned int size)
+{
+    SysBusESPState *sysbus = opaque;
+    uint32_t saddr;
+
+    saddr = addr >> sysbus->it_shift;
+    return esp_reg_read(&sysbus->esp, saddr);
+}
+
+static const MemoryRegionOps sysbus_esp_mem_ops = {
+    .read = sysbus_esp_mem_read,
+    .write = sysbus_esp_mem_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid.accepts = esp_mem_accepts,
+};
+
+void esp_init(hwaddr espaddr, int it_shift,
+              ESPDMAMemoryReadWriteFunc dma_memory_read,
+              ESPDMAMemoryReadWriteFunc dma_memory_write,
+              void *dma_opaque, qemu_irq irq, qemu_irq *reset,
+              qemu_irq *dma_enable)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+    SysBusESPState *sysbus;
+    ESPState *esp;
+
+    dev = qdev_create(NULL, "esp");
+    sysbus = DO_UPCAST(SysBusESPState, busdev.qdev, dev);
+    esp = &sysbus->esp;
+    esp->dma_memory_read = dma_memory_read;
+    esp->dma_memory_write = dma_memory_write;
+    esp->dma_opaque = dma_opaque;
+    sysbus->it_shift = it_shift;
+    /* XXX for now until rc4030 has been changed to use DMA enable signal */
+    esp->dma_enabled = 1;
+    qdev_init_nofail(dev);
+    s = SYS_BUS_DEVICE(dev);
+    sysbus_connect_irq(s, 0, irq);
+    sysbus_mmio_map(s, 0, espaddr);
+    *reset = qdev_get_gpio_in(dev, 0);
+    *dma_enable = qdev_get_gpio_in(dev, 1);
+}
+
+static const struct SCSIBusInfo esp_scsi_info = {
+    .tcq = false,
+    .max_target = ESP_MAX_DEVS,
+    .max_lun = 7,
+
+    .transfer_data = esp_transfer_data,
+    .complete = esp_command_complete,
+    .cancel = esp_request_cancelled
+};
+
+static void sysbus_esp_gpio_demux(void *opaque, int irq, int level)
+{
+    DeviceState *d = opaque;
+    SysBusESPState *sysbus = container_of(d, SysBusESPState, busdev.qdev);
+    ESPState *s = &sysbus->esp;
+
+    switch (irq) {
+    case 0:
+        parent_esp_reset(s, irq, level);
+        break;
+    case 1:
+        esp_dma_enable(opaque, irq, level);
+        break;
+    }
+}
+
+static int sysbus_esp_init(SysBusDevice *dev)
+{
+    SysBusESPState *sysbus = FROM_SYSBUS(SysBusESPState, dev);
+    ESPState *s = &sysbus->esp;
+
+    sysbus_init_irq(dev, &s->irq);
+    assert(sysbus->it_shift != -1);
+
+    s->chip_id = TCHI_FAS100A;
+    memory_region_init_io(&sysbus->iomem, &sysbus_esp_mem_ops, sysbus,
+                          "esp", ESP_REGS << sysbus->it_shift);
+    sysbus_init_mmio(dev, &sysbus->iomem);
+
+    qdev_init_gpio_in(&dev->qdev, sysbus_esp_gpio_demux, 2);
+
+    scsi_bus_new(&s->bus, &dev->qdev, &esp_scsi_info);
+    return scsi_bus_legacy_handle_cmdline(&s->bus);
+}
+
+static void sysbus_esp_hard_reset(DeviceState *dev)
+{
+    SysBusESPState *sysbus = DO_UPCAST(SysBusESPState, busdev.qdev, dev);
+    esp_hard_reset(&sysbus->esp);
+}
+
+static const VMStateDescription vmstate_sysbus_esp_scsi = {
+    .name = "sysbusespscsi",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT(esp, SysBusESPState, 0, vmstate_esp, ESPState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void sysbus_esp_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = sysbus_esp_init;
+    dc->reset = sysbus_esp_hard_reset;
+    dc->vmsd = &vmstate_sysbus_esp_scsi;
+}
+
+static const TypeInfo sysbus_esp_info = {
+    .name          = "esp",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SysBusESPState),
+    .class_init    = sysbus_esp_class_init,
+};
+
+static void esp_register_types(void)
+{
+    type_register_static(&sysbus_esp_info);
+}
+
+type_init(esp_register_types)
diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c
new file mode 100644 (file)
index 0000000..c601b29
--- /dev/null
@@ -0,0 +1,2136 @@
+/*
+ * QEMU LSI53C895A SCSI Host Bus Adapter emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the LGPL.
+ */
+
+/* ??? Need to check if the {read,write}[wl] routines work properly on
+   big-endian targets.  */
+
+#include <assert.h>
+
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "hw/scsi/scsi.h"
+#include "sysemu/dma.h"
+
+//#define DEBUG_LSI
+//#define DEBUG_LSI_REG
+
+#ifdef DEBUG_LSI
+#define DPRINTF(fmt, ...) \
+do { printf("lsi_scsi: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+#define LSI_MAX_DEVS 7
+
+#define LSI_SCNTL0_TRG    0x01
+#define LSI_SCNTL0_AAP    0x02
+#define LSI_SCNTL0_EPC    0x08
+#define LSI_SCNTL0_WATN   0x10
+#define LSI_SCNTL0_START  0x20
+
+#define LSI_SCNTL1_SST    0x01
+#define LSI_SCNTL1_IARB   0x02
+#define LSI_SCNTL1_AESP   0x04
+#define LSI_SCNTL1_RST    0x08
+#define LSI_SCNTL1_CON    0x10
+#define LSI_SCNTL1_DHP    0x20
+#define LSI_SCNTL1_ADB    0x40
+#define LSI_SCNTL1_EXC    0x80
+
+#define LSI_SCNTL2_WSR    0x01
+#define LSI_SCNTL2_VUE0   0x02
+#define LSI_SCNTL2_VUE1   0x04
+#define LSI_SCNTL2_WSS    0x08
+#define LSI_SCNTL2_SLPHBEN 0x10
+#define LSI_SCNTL2_SLPMD  0x20
+#define LSI_SCNTL2_CHM    0x40
+#define LSI_SCNTL2_SDU    0x80
+
+#define LSI_ISTAT0_DIP    0x01
+#define LSI_ISTAT0_SIP    0x02
+#define LSI_ISTAT0_INTF   0x04
+#define LSI_ISTAT0_CON    0x08
+#define LSI_ISTAT0_SEM    0x10
+#define LSI_ISTAT0_SIGP   0x20
+#define LSI_ISTAT0_SRST   0x40
+#define LSI_ISTAT0_ABRT   0x80
+
+#define LSI_ISTAT1_SI     0x01
+#define LSI_ISTAT1_SRUN   0x02
+#define LSI_ISTAT1_FLSH   0x04
+
+#define LSI_SSTAT0_SDP0   0x01
+#define LSI_SSTAT0_RST    0x02
+#define LSI_SSTAT0_WOA    0x04
+#define LSI_SSTAT0_LOA    0x08
+#define LSI_SSTAT0_AIP    0x10
+#define LSI_SSTAT0_OLF    0x20
+#define LSI_SSTAT0_ORF    0x40
+#define LSI_SSTAT0_ILF    0x80
+
+#define LSI_SIST0_PAR     0x01
+#define LSI_SIST0_RST     0x02
+#define LSI_SIST0_UDC     0x04
+#define LSI_SIST0_SGE     0x08
+#define LSI_SIST0_RSL     0x10
+#define LSI_SIST0_SEL     0x20
+#define LSI_SIST0_CMP     0x40
+#define LSI_SIST0_MA      0x80
+
+#define LSI_SIST1_HTH     0x01
+#define LSI_SIST1_GEN     0x02
+#define LSI_SIST1_STO     0x04
+#define LSI_SIST1_SBMC    0x10
+
+#define LSI_SOCL_IO       0x01
+#define LSI_SOCL_CD       0x02
+#define LSI_SOCL_MSG      0x04
+#define LSI_SOCL_ATN      0x08
+#define LSI_SOCL_SEL      0x10
+#define LSI_SOCL_BSY      0x20
+#define LSI_SOCL_ACK      0x40
+#define LSI_SOCL_REQ      0x80
+
+#define LSI_DSTAT_IID     0x01
+#define LSI_DSTAT_SIR     0x04
+#define LSI_DSTAT_SSI     0x08
+#define LSI_DSTAT_ABRT    0x10
+#define LSI_DSTAT_BF      0x20
+#define LSI_DSTAT_MDPE    0x40
+#define LSI_DSTAT_DFE     0x80
+
+#define LSI_DCNTL_COM     0x01
+#define LSI_DCNTL_IRQD    0x02
+#define LSI_DCNTL_STD     0x04
+#define LSI_DCNTL_IRQM    0x08
+#define LSI_DCNTL_SSM     0x10
+#define LSI_DCNTL_PFEN    0x20
+#define LSI_DCNTL_PFF     0x40
+#define LSI_DCNTL_CLSE    0x80
+
+#define LSI_DMODE_MAN     0x01
+#define LSI_DMODE_BOF     0x02
+#define LSI_DMODE_ERMP    0x04
+#define LSI_DMODE_ERL     0x08
+#define LSI_DMODE_DIOM    0x10
+#define LSI_DMODE_SIOM    0x20
+
+#define LSI_CTEST2_DACK   0x01
+#define LSI_CTEST2_DREQ   0x02
+#define LSI_CTEST2_TEOP   0x04
+#define LSI_CTEST2_PCICIE 0x08
+#define LSI_CTEST2_CM     0x10
+#define LSI_CTEST2_CIO    0x20
+#define LSI_CTEST2_SIGP   0x40
+#define LSI_CTEST2_DDIR   0x80
+
+#define LSI_CTEST5_BL2    0x04
+#define LSI_CTEST5_DDIR   0x08
+#define LSI_CTEST5_MASR   0x10
+#define LSI_CTEST5_DFSN   0x20
+#define LSI_CTEST5_BBCK   0x40
+#define LSI_CTEST5_ADCK   0x80
+
+#define LSI_CCNTL0_DILS   0x01
+#define LSI_CCNTL0_DISFC  0x10
+#define LSI_CCNTL0_ENNDJ  0x20
+#define LSI_CCNTL0_PMJCTL 0x40
+#define LSI_CCNTL0_ENPMJ  0x80
+
+#define LSI_CCNTL1_EN64DBMV  0x01
+#define LSI_CCNTL1_EN64TIBMV 0x02
+#define LSI_CCNTL1_64TIMOD   0x04
+#define LSI_CCNTL1_DDAC      0x08
+#define LSI_CCNTL1_ZMOD      0x80
+
+/* Enable Response to Reselection */
+#define LSI_SCID_RRE      0x60
+
+#define LSI_CCNTL1_40BIT (LSI_CCNTL1_EN64TIBMV|LSI_CCNTL1_64TIMOD)
+
+#define PHASE_DO          0
+#define PHASE_DI          1
+#define PHASE_CMD         2
+#define PHASE_ST          3
+#define PHASE_MO          6
+#define PHASE_MI          7
+#define PHASE_MASK        7
+
+/* Maximum length of MSG IN data.  */
+#define LSI_MAX_MSGIN_LEN 8
+
+/* Flag set if this is a tagged command.  */
+#define LSI_TAG_VALID     (1 << 16)
+
+typedef struct lsi_request {
+    SCSIRequest *req;
+    uint32_t tag;
+    uint32_t dma_len;
+    uint8_t *dma_buf;
+    uint32_t pending;
+    int out;
+    QTAILQ_ENTRY(lsi_request) next;
+} lsi_request;
+
+typedef struct {
+    PCIDevice dev;
+    MemoryRegion mmio_io;
+    MemoryRegion ram_io;
+    MemoryRegion io_io;
+
+    int carry; /* ??? Should this be an a visible register somewhere?  */
+    int status;
+    /* Action to take at the end of a MSG IN phase.
+       0 = COMMAND, 1 = disconnect, 2 = DATA OUT, 3 = DATA IN.  */
+    int msg_action;
+    int msg_len;
+    uint8_t msg[LSI_MAX_MSGIN_LEN];
+    /* 0 if SCRIPTS are running or stopped.
+     * 1 if a Wait Reselect instruction has been issued.
+     * 2 if processing DMA from lsi_execute_script.
+     * 3 if a DMA operation is in progress.  */
+    int waiting;
+    SCSIBus bus;
+    int current_lun;
+    /* The tag is a combination of the device ID and the SCSI tag.  */
+    uint32_t select_tag;
+    int command_complete;
+    QTAILQ_HEAD(, lsi_request) queue;
+    lsi_request *current;
+
+    uint32_t dsa;
+    uint32_t temp;
+    uint32_t dnad;
+    uint32_t dbc;
+    uint8_t istat0;
+    uint8_t istat1;
+    uint8_t dcmd;
+    uint8_t dstat;
+    uint8_t dien;
+    uint8_t sist0;
+    uint8_t sist1;
+    uint8_t sien0;
+    uint8_t sien1;
+    uint8_t mbox0;
+    uint8_t mbox1;
+    uint8_t dfifo;
+    uint8_t ctest2;
+    uint8_t ctest3;
+    uint8_t ctest4;
+    uint8_t ctest5;
+    uint8_t ccntl0;
+    uint8_t ccntl1;
+    uint32_t dsp;
+    uint32_t dsps;
+    uint8_t dmode;
+    uint8_t dcntl;
+    uint8_t scntl0;
+    uint8_t scntl1;
+    uint8_t scntl2;
+    uint8_t scntl3;
+    uint8_t sstat0;
+    uint8_t sstat1;
+    uint8_t scid;
+    uint8_t sxfer;
+    uint8_t socl;
+    uint8_t sdid;
+    uint8_t ssid;
+    uint8_t sfbr;
+    uint8_t stest1;
+    uint8_t stest2;
+    uint8_t stest3;
+    uint8_t sidl;
+    uint8_t stime0;
+    uint8_t respid0;
+    uint8_t respid1;
+    uint32_t mmrs;
+    uint32_t mmws;
+    uint32_t sfs;
+    uint32_t drs;
+    uint32_t sbms;
+    uint32_t dbms;
+    uint32_t dnad64;
+    uint32_t pmjad1;
+    uint32_t pmjad2;
+    uint32_t rbc;
+    uint32_t ua;
+    uint32_t ia;
+    uint32_t sbc;
+    uint32_t csbc;
+    uint32_t scratch[18]; /* SCRATCHA-SCRATCHR */
+    uint8_t sbr;
+
+    /* Script ram is stored as 32-bit words in host byteorder.  */
+    uint32_t script_ram[2048];
+} LSIState;
+
+static inline int lsi_irq_on_rsl(LSIState *s)
+{
+    return (s->sien0 & LSI_SIST0_RSL) && (s->scid & LSI_SCID_RRE);
+}
+
+static void lsi_soft_reset(LSIState *s)
+{
+    DPRINTF("Reset\n");
+    s->carry = 0;
+
+    s->msg_action = 0;
+    s->msg_len = 0;
+    s->waiting = 0;
+    s->dsa = 0;
+    s->dnad = 0;
+    s->dbc = 0;
+    s->temp = 0;
+    memset(s->scratch, 0, sizeof(s->scratch));
+    s->istat0 = 0;
+    s->istat1 = 0;
+    s->dcmd = 0x40;
+    s->dstat = LSI_DSTAT_DFE;
+    s->dien = 0;
+    s->sist0 = 0;
+    s->sist1 = 0;
+    s->sien0 = 0;
+    s->sien1 = 0;
+    s->mbox0 = 0;
+    s->mbox1 = 0;
+    s->dfifo = 0;
+    s->ctest2 = LSI_CTEST2_DACK;
+    s->ctest3 = 0;
+    s->ctest4 = 0;
+    s->ctest5 = 0;
+    s->ccntl0 = 0;
+    s->ccntl1 = 0;
+    s->dsp = 0;
+    s->dsps = 0;
+    s->dmode = 0;
+    s->dcntl = 0;
+    s->scntl0 = 0xc0;
+    s->scntl1 = 0;
+    s->scntl2 = 0;
+    s->scntl3 = 0;
+    s->sstat0 = 0;
+    s->sstat1 = 0;
+    s->scid = 7;
+    s->sxfer = 0;
+    s->socl = 0;
+    s->sdid = 0;
+    s->ssid = 0;
+    s->stest1 = 0;
+    s->stest2 = 0;
+    s->stest3 = 0;
+    s->sidl = 0;
+    s->stime0 = 0;
+    s->respid0 = 0x80;
+    s->respid1 = 0;
+    s->mmrs = 0;
+    s->mmws = 0;
+    s->sfs = 0;
+    s->drs = 0;
+    s->sbms = 0;
+    s->dbms = 0;
+    s->dnad64 = 0;
+    s->pmjad1 = 0;
+    s->pmjad2 = 0;
+    s->rbc = 0;
+    s->ua = 0;
+    s->ia = 0;
+    s->sbc = 0;
+    s->csbc = 0;
+    s->sbr = 0;
+    assert(QTAILQ_EMPTY(&s->queue));
+    assert(!s->current);
+}
+
+static int lsi_dma_40bit(LSIState *s)
+{
+    if ((s->ccntl1 & LSI_CCNTL1_40BIT) == LSI_CCNTL1_40BIT)
+        return 1;
+    return 0;
+}
+
+static int lsi_dma_ti64bit(LSIState *s)
+{
+    if ((s->ccntl1 & LSI_CCNTL1_EN64TIBMV) == LSI_CCNTL1_EN64TIBMV)
+        return 1;
+    return 0;
+}
+
+static int lsi_dma_64bit(LSIState *s)
+{
+    if ((s->ccntl1 & LSI_CCNTL1_EN64DBMV) == LSI_CCNTL1_EN64DBMV)
+        return 1;
+    return 0;
+}
+
+static uint8_t lsi_reg_readb(LSIState *s, int offset);
+static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val);
+static void lsi_execute_script(LSIState *s);
+static void lsi_reselect(LSIState *s, lsi_request *p);
+
+static inline uint32_t read_dword(LSIState *s, uint32_t addr)
+{
+    uint32_t buf;
+
+    pci_dma_read(&s->dev, addr, &buf, 4);
+    return cpu_to_le32(buf);
+}
+
+static void lsi_stop_script(LSIState *s)
+{
+    s->istat1 &= ~LSI_ISTAT1_SRUN;
+}
+
+static void lsi_update_irq(LSIState *s)
+{
+    int level;
+    static int last_level;
+    lsi_request *p;
+
+    /* It's unclear whether the DIP/SIP bits should be cleared when the
+       Interrupt Status Registers are cleared or when istat0 is read.
+       We currently do the formwer, which seems to work.  */
+    level = 0;
+    if (s->dstat) {
+        if (s->dstat & s->dien)
+            level = 1;
+        s->istat0 |= LSI_ISTAT0_DIP;
+    } else {
+        s->istat0 &= ~LSI_ISTAT0_DIP;
+    }
+
+    if (s->sist0 || s->sist1) {
+        if ((s->sist0 & s->sien0) || (s->sist1 & s->sien1))
+            level = 1;
+        s->istat0 |= LSI_ISTAT0_SIP;
+    } else {
+        s->istat0 &= ~LSI_ISTAT0_SIP;
+    }
+    if (s->istat0 & LSI_ISTAT0_INTF)
+        level = 1;
+
+    if (level != last_level) {
+        DPRINTF("Update IRQ level %d dstat %02x sist %02x%02x\n",
+                level, s->dstat, s->sist1, s->sist0);
+        last_level = level;
+    }
+    qemu_set_irq(s->dev.irq[0], level);
+
+    if (!level && lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON)) {
+        DPRINTF("Handled IRQs & disconnected, looking for pending "
+                "processes\n");
+        QTAILQ_FOREACH(p, &s->queue, next) {
+            if (p->pending) {
+                lsi_reselect(s, p);
+                break;
+            }
+        }
+    }
+}
+
+/* Stop SCRIPTS execution and raise a SCSI interrupt.  */
+static void lsi_script_scsi_interrupt(LSIState *s, int stat0, int stat1)
+{
+    uint32_t mask0;
+    uint32_t mask1;
+
+    DPRINTF("SCSI Interrupt 0x%02x%02x prev 0x%02x%02x\n",
+            stat1, stat0, s->sist1, s->sist0);
+    s->sist0 |= stat0;
+    s->sist1 |= stat1;
+    /* Stop processor on fatal or unmasked interrupt.  As a special hack
+       we don't stop processing when raising STO.  Instead continue
+       execution and stop at the next insn that accesses the SCSI bus.  */
+    mask0 = s->sien0 | ~(LSI_SIST0_CMP | LSI_SIST0_SEL | LSI_SIST0_RSL);
+    mask1 = s->sien1 | ~(LSI_SIST1_GEN | LSI_SIST1_HTH);
+    mask1 &= ~LSI_SIST1_STO;
+    if (s->sist0 & mask0 || s->sist1 & mask1) {
+        lsi_stop_script(s);
+    }
+    lsi_update_irq(s);
+}
+
+/* Stop SCRIPTS execution and raise a DMA interrupt.  */
+static void lsi_script_dma_interrupt(LSIState *s, int stat)
+{
+    DPRINTF("DMA Interrupt 0x%x prev 0x%x\n", stat, s->dstat);
+    s->dstat |= stat;
+    lsi_update_irq(s);
+    lsi_stop_script(s);
+}
+
+static inline void lsi_set_phase(LSIState *s, int phase)
+{
+    s->sstat1 = (s->sstat1 & ~PHASE_MASK) | phase;
+}
+
+static void lsi_bad_phase(LSIState *s, int out, int new_phase)
+{
+    /* Trigger a phase mismatch.  */
+    if (s->ccntl0 & LSI_CCNTL0_ENPMJ) {
+        if ((s->ccntl0 & LSI_CCNTL0_PMJCTL)) {
+            s->dsp = out ? s->pmjad1 : s->pmjad2;
+        } else {
+            s->dsp = (s->scntl2 & LSI_SCNTL2_WSR ? s->pmjad2 : s->pmjad1);
+        }
+        DPRINTF("Data phase mismatch jump to %08x\n", s->dsp);
+    } else {
+        DPRINTF("Phase mismatch interrupt\n");
+        lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0);
+        lsi_stop_script(s);
+    }
+    lsi_set_phase(s, new_phase);
+}
+
+
+/* Resume SCRIPTS execution after a DMA operation.  */
+static void lsi_resume_script(LSIState *s)
+{
+    if (s->waiting != 2) {
+        s->waiting = 0;
+        lsi_execute_script(s);
+    } else {
+        s->waiting = 0;
+    }
+}
+
+static void lsi_disconnect(LSIState *s)
+{
+    s->scntl1 &= ~LSI_SCNTL1_CON;
+    s->sstat1 &= ~PHASE_MASK;
+}
+
+static void lsi_bad_selection(LSIState *s, uint32_t id)
+{
+    DPRINTF("Selected absent target %d\n", id);
+    lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO);
+    lsi_disconnect(s);
+}
+
+/* Initiate a SCSI layer data transfer.  */
+static void lsi_do_dma(LSIState *s, int out)
+{
+    uint32_t count;
+    dma_addr_t addr;
+    SCSIDevice *dev;
+
+    assert(s->current);
+    if (!s->current->dma_len) {
+        /* Wait until data is available.  */
+        DPRINTF("DMA no data available\n");
+        return;
+    }
+
+    dev = s->current->req->dev;
+    assert(dev);
+
+    count = s->dbc;
+    if (count > s->current->dma_len)
+        count = s->current->dma_len;
+
+    addr = s->dnad;
+    /* both 40 and Table Indirect 64-bit DMAs store upper bits in dnad64 */
+    if (lsi_dma_40bit(s) || lsi_dma_ti64bit(s))
+        addr |= ((uint64_t)s->dnad64 << 32);
+    else if (s->dbms)
+        addr |= ((uint64_t)s->dbms << 32);
+    else if (s->sbms)
+        addr |= ((uint64_t)s->sbms << 32);
+
+    DPRINTF("DMA addr=0x" DMA_ADDR_FMT " len=%d\n", addr, count);
+    s->csbc += count;
+    s->dnad += count;
+    s->dbc -= count;
+     if (s->current->dma_buf == NULL) {
+        s->current->dma_buf = scsi_req_get_buf(s->current->req);
+    }
+    /* ??? Set SFBR to first data byte.  */
+    if (out) {
+        pci_dma_read(&s->dev, addr, s->current->dma_buf, count);
+    } else {
+        pci_dma_write(&s->dev, addr, s->current->dma_buf, count);
+    }
+    s->current->dma_len -= count;
+    if (s->current->dma_len == 0) {
+        s->current->dma_buf = NULL;
+        scsi_req_continue(s->current->req);
+    } else {
+        s->current->dma_buf += count;
+        lsi_resume_script(s);
+    }
+}
+
+
+/* Add a command to the queue.  */
+static void lsi_queue_command(LSIState *s)
+{
+    lsi_request *p = s->current;
+
+    DPRINTF("Queueing tag=0x%x\n", p->tag);
+    assert(s->current != NULL);
+    assert(s->current->dma_len == 0);
+    QTAILQ_INSERT_TAIL(&s->queue, s->current, next);
+    s->current = NULL;
+
+    p->pending = 0;
+    p->out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
+}
+
+/* Queue a byte for a MSG IN phase.  */
+static void lsi_add_msg_byte(LSIState *s, uint8_t data)
+{
+    if (s->msg_len >= LSI_MAX_MSGIN_LEN) {
+        BADF("MSG IN data too long\n");
+    } else {
+        DPRINTF("MSG IN 0x%02x\n", data);
+        s->msg[s->msg_len++] = data;
+    }
+}
+
+/* Perform reselection to continue a command.  */
+static void lsi_reselect(LSIState *s, lsi_request *p)
+{
+    int id;
+
+    assert(s->current == NULL);
+    QTAILQ_REMOVE(&s->queue, p, next);
+    s->current = p;
+
+    id = (p->tag >> 8) & 0xf;
+    s->ssid = id | 0x80;
+    /* LSI53C700 Family Compatibility, see LSI53C895A 4-73 */
+    if (!(s->dcntl & LSI_DCNTL_COM)) {
+        s->sfbr = 1 << (id & 0x7);
+    }
+    DPRINTF("Reselected target %d\n", id);
+    s->scntl1 |= LSI_SCNTL1_CON;
+    lsi_set_phase(s, PHASE_MI);
+    s->msg_action = p->out ? 2 : 3;
+    s->current->dma_len = p->pending;
+    lsi_add_msg_byte(s, 0x80);
+    if (s->current->tag & LSI_TAG_VALID) {
+        lsi_add_msg_byte(s, 0x20);
+        lsi_add_msg_byte(s, p->tag & 0xff);
+    }
+
+    if (lsi_irq_on_rsl(s)) {
+        lsi_script_scsi_interrupt(s, LSI_SIST0_RSL, 0);
+    }
+}
+
+static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag)
+{
+    lsi_request *p;
+
+    QTAILQ_FOREACH(p, &s->queue, next) {
+        if (p->tag == tag) {
+            return p;
+        }
+    }
+
+    return NULL;
+}
+
+static void lsi_request_free(LSIState *s, lsi_request *p)
+{
+    if (p == s->current) {
+        s->current = NULL;
+    } else {
+        QTAILQ_REMOVE(&s->queue, p, next);
+    }
+    g_free(p);
+}
+
+static void lsi_request_cancelled(SCSIRequest *req)
+{
+    LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
+    lsi_request *p = req->hba_private;
+
+    req->hba_private = NULL;
+    lsi_request_free(s, p);
+    scsi_req_unref(req);
+}
+
+/* Record that data is available for a queued command.  Returns zero if
+   the device was reselected, nonzero if the IO is deferred.  */
+static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len)
+{
+    lsi_request *p = req->hba_private;
+
+    if (p->pending) {
+        BADF("Multiple IO pending for request %p\n", p);
+    }
+    p->pending = len;
+    /* Reselect if waiting for it, or if reselection triggers an IRQ
+       and the bus is free.
+       Since no interrupt stacking is implemented in the emulation, it
+       is also required that there are no pending interrupts waiting
+       for service from the device driver. */
+    if (s->waiting == 1 ||
+        (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON) &&
+         !(s->istat0 & (LSI_ISTAT0_SIP | LSI_ISTAT0_DIP)))) {
+        /* Reselect device.  */
+        lsi_reselect(s, p);
+        return 0;
+    } else {
+        DPRINTF("Queueing IO tag=0x%x\n", p->tag);
+        p->pending = len;
+        return 1;
+    }
+}
+
+ /* Callback to indicate that the SCSI layer has completed a command.  */
+static void lsi_command_complete(SCSIRequest *req, uint32_t status, size_t resid)
+{
+    LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
+    int out;
+
+    out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
+    DPRINTF("Command complete status=%d\n", (int)status);
+    s->status = status;
+    s->command_complete = 2;
+    if (s->waiting && s->dbc != 0) {
+        /* Raise phase mismatch for short transfers.  */
+        lsi_bad_phase(s, out, PHASE_ST);
+    } else {
+        lsi_set_phase(s, PHASE_ST);
+    }
+
+    if (req->hba_private == s->current) {
+        req->hba_private = NULL;
+        lsi_request_free(s, s->current);
+        scsi_req_unref(req);
+    }
+    lsi_resume_script(s);
+}
+
+ /* Callback to indicate that the SCSI layer has completed a transfer.  */
+static void lsi_transfer_data(SCSIRequest *req, uint32_t len)
+{
+    LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
+    int out;
+
+    assert(req->hba_private);
+    if (s->waiting == 1 || req->hba_private != s->current ||
+        (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) {
+        if (lsi_queue_req(s, req, len)) {
+            return;
+        }
+    }
+
+    out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
+
+    /* host adapter (re)connected */
+    DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, len);
+    s->current->dma_len = len;
+    s->command_complete = 1;
+    if (s->waiting) {
+        if (s->waiting == 1 || s->dbc == 0) {
+            lsi_resume_script(s);
+        } else {
+            lsi_do_dma(s, out);
+        }
+    }
+}
+
+static void lsi_do_command(LSIState *s)
+{
+    SCSIDevice *dev;
+    uint8_t buf[16];
+    uint32_t id;
+    int n;
+
+    DPRINTF("Send command len=%d\n", s->dbc);
+    if (s->dbc > 16)
+        s->dbc = 16;
+    pci_dma_read(&s->dev, s->dnad, buf, s->dbc);
+    s->sfbr = buf[0];
+    s->command_complete = 0;
+
+    id = (s->select_tag >> 8) & 0xf;
+    dev = scsi_device_find(&s->bus, 0, id, s->current_lun);
+    if (!dev) {
+        lsi_bad_selection(s, id);
+        return;
+    }
+
+    assert(s->current == NULL);
+    s->current = g_malloc0(sizeof(lsi_request));
+    s->current->tag = s->select_tag;
+    s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun, buf,
+                                   s->current);
+
+    n = scsi_req_enqueue(s->current->req);
+    if (n) {
+        if (n > 0) {
+            lsi_set_phase(s, PHASE_DI);
+        } else if (n < 0) {
+            lsi_set_phase(s, PHASE_DO);
+        }
+        scsi_req_continue(s->current->req);
+    }
+    if (!s->command_complete) {
+        if (n) {
+            /* Command did not complete immediately so disconnect.  */
+            lsi_add_msg_byte(s, 2); /* SAVE DATA POINTER */
+            lsi_add_msg_byte(s, 4); /* DISCONNECT */
+            /* wait data */
+            lsi_set_phase(s, PHASE_MI);
+            s->msg_action = 1;
+            lsi_queue_command(s);
+        } else {
+            /* wait command complete */
+            lsi_set_phase(s, PHASE_DI);
+        }
+    }
+}
+
+static void lsi_do_status(LSIState *s)
+{
+    uint8_t status;
+    DPRINTF("Get status len=%d status=%d\n", s->dbc, s->status);
+    if (s->dbc != 1)
+        BADF("Bad Status move\n");
+    s->dbc = 1;
+    status = s->status;
+    s->sfbr = status;
+    pci_dma_write(&s->dev, s->dnad, &status, 1);
+    lsi_set_phase(s, PHASE_MI);
+    s->msg_action = 1;
+    lsi_add_msg_byte(s, 0); /* COMMAND COMPLETE */
+}
+
+static void lsi_do_msgin(LSIState *s)
+{
+    int len;
+    DPRINTF("Message in len=%d/%d\n", s->dbc, s->msg_len);
+    s->sfbr = s->msg[0];
+    len = s->msg_len;
+    if (len > s->dbc)
+        len = s->dbc;
+    pci_dma_write(&s->dev, s->dnad, s->msg, len);
+    /* Linux drivers rely on the last byte being in the SIDL.  */
+    s->sidl = s->msg[len - 1];
+    s->msg_len -= len;
+    if (s->msg_len) {
+        memmove(s->msg, s->msg + len, s->msg_len);
+    } else {
+        /* ??? Check if ATN (not yet implemented) is asserted and maybe
+           switch to PHASE_MO.  */
+        switch (s->msg_action) {
+        case 0:
+            lsi_set_phase(s, PHASE_CMD);
+            break;
+        case 1:
+            lsi_disconnect(s);
+            break;
+        case 2:
+            lsi_set_phase(s, PHASE_DO);
+            break;
+        case 3:
+            lsi_set_phase(s, PHASE_DI);
+            break;
+        default:
+            abort();
+        }
+    }
+}
+
+/* Read the next byte during a MSGOUT phase.  */
+static uint8_t lsi_get_msgbyte(LSIState *s)
+{
+    uint8_t data;
+    pci_dma_read(&s->dev, s->dnad, &data, 1);
+    s->dnad++;
+    s->dbc--;
+    return data;
+}
+
+/* Skip the next n bytes during a MSGOUT phase. */
+static void lsi_skip_msgbytes(LSIState *s, unsigned int n)
+{
+    s->dnad += n;
+    s->dbc  -= n;
+}
+
+static void lsi_do_msgout(LSIState *s)
+{
+    uint8_t msg;
+    int len;
+    uint32_t current_tag;
+    lsi_request *current_req, *p, *p_next;
+
+    if (s->current) {
+        current_tag = s->current->tag;
+        current_req = s->current;
+    } else {
+        current_tag = s->select_tag;
+        current_req = lsi_find_by_tag(s, current_tag);
+    }
+
+    DPRINTF("MSG out len=%d\n", s->dbc);
+    while (s->dbc) {
+        msg = lsi_get_msgbyte(s);
+        s->sfbr = msg;
+
+        switch (msg) {
+        case 0x04:
+            DPRINTF("MSG: Disconnect\n");
+            lsi_disconnect(s);
+            break;
+        case 0x08:
+            DPRINTF("MSG: No Operation\n");
+            lsi_set_phase(s, PHASE_CMD);
+            break;
+        case 0x01:
+            len = lsi_get_msgbyte(s);
+            msg = lsi_get_msgbyte(s);
+            (void)len; /* avoid a warning about unused variable*/
+            DPRINTF("Extended message 0x%x (len %d)\n", msg, len);
+            switch (msg) {
+            case 1:
+                DPRINTF("SDTR (ignored)\n");
+                lsi_skip_msgbytes(s, 2);
+                break;
+            case 3:
+                DPRINTF("WDTR (ignored)\n");
+                lsi_skip_msgbytes(s, 1);
+                break;
+            default:
+                goto bad;
+            }
+            break;
+        case 0x20: /* SIMPLE queue */
+            s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID;
+            DPRINTF("SIMPLE queue tag=0x%x\n", s->select_tag & 0xff);
+            break;
+        case 0x21: /* HEAD of queue */
+            BADF("HEAD queue not implemented\n");
+            s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID;
+            break;
+        case 0x22: /* ORDERED queue */
+            BADF("ORDERED queue not implemented\n");
+            s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID;
+            break;
+        case 0x0d:
+            /* The ABORT TAG message clears the current I/O process only. */
+            DPRINTF("MSG: ABORT TAG tag=0x%x\n", current_tag);
+            if (current_req) {
+                scsi_req_cancel(current_req->req);
+            }
+            lsi_disconnect(s);
+            break;
+        case 0x06:
+        case 0x0e:
+        case 0x0c:
+            /* The ABORT message clears all I/O processes for the selecting
+               initiator on the specified logical unit of the target. */
+            if (msg == 0x06) {
+                DPRINTF("MSG: ABORT tag=0x%x\n", current_tag);
+            }
+            /* The CLEAR QUEUE message clears all I/O processes for all
+               initiators on the specified logical unit of the target. */
+            if (msg == 0x0e) {
+                DPRINTF("MSG: CLEAR QUEUE tag=0x%x\n", current_tag);
+            }
+            /* The BUS DEVICE RESET message clears all I/O processes for all
+               initiators on all logical units of the target. */
+            if (msg == 0x0c) {
+                DPRINTF("MSG: BUS DEVICE RESET tag=0x%x\n", current_tag);
+            }
+
+            /* clear the current I/O process */
+            if (s->current) {
+                scsi_req_cancel(s->current->req);
+            }
+
+            /* As the current implemented devices scsi_disk and scsi_generic
+               only support one LUN, we don't need to keep track of LUNs.
+               Clearing I/O processes for other initiators could be possible
+               for scsi_generic by sending a SG_SCSI_RESET to the /dev/sgX
+               device, but this is currently not implemented (and seems not
+               to be really necessary). So let's simply clear all queued
+               commands for the current device: */
+            QTAILQ_FOREACH_SAFE(p, &s->queue, next, p_next) {
+                if ((p->tag & 0x0000ff00) == (current_tag & 0x0000ff00)) {
+                    scsi_req_cancel(p->req);
+                }
+            }
+
+            lsi_disconnect(s);
+            break;
+        default:
+            if ((msg & 0x80) == 0) {
+                goto bad;
+            }
+            s->current_lun = msg & 7;
+            DPRINTF("Select LUN %d\n", s->current_lun);
+            lsi_set_phase(s, PHASE_CMD);
+            break;
+        }
+    }
+    return;
+bad:
+    BADF("Unimplemented message 0x%02x\n", msg);
+    lsi_set_phase(s, PHASE_MI);
+    lsi_add_msg_byte(s, 7); /* MESSAGE REJECT */
+    s->msg_action = 0;
+}
+
+/* Sign extend a 24-bit value.  */
+static inline int32_t sxt24(int32_t n)
+{
+    return (n << 8) >> 8;
+}
+
+#define LSI_BUF_SIZE 4096
+static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count)
+{
+    int n;
+    uint8_t buf[LSI_BUF_SIZE];
+
+    DPRINTF("memcpy dest 0x%08x src 0x%08x count %d\n", dest, src, count);
+    while (count) {
+        n = (count > LSI_BUF_SIZE) ? LSI_BUF_SIZE : count;
+        pci_dma_read(&s->dev, src, buf, n);
+        pci_dma_write(&s->dev, dest, buf, n);
+        src += n;
+        dest += n;
+        count -= n;
+    }
+}
+
+static void lsi_wait_reselect(LSIState *s)
+{
+    lsi_request *p;
+
+    DPRINTF("Wait Reselect\n");
+
+    QTAILQ_FOREACH(p, &s->queue, next) {
+        if (p->pending) {
+            lsi_reselect(s, p);
+            break;
+        }
+    }
+    if (s->current == NULL) {
+        s->waiting = 1;
+    }
+}
+
+static void lsi_execute_script(LSIState *s)
+{
+    uint32_t insn;
+    uint32_t addr, addr_high;
+    int opcode;
+    int insn_processed = 0;
+
+    s->istat1 |= LSI_ISTAT1_SRUN;
+again:
+    insn_processed++;
+    insn = read_dword(s, s->dsp);
+    if (!insn) {
+        /* If we receive an empty opcode increment the DSP by 4 bytes
+           instead of 8 and execute the next opcode at that location */
+        s->dsp += 4;
+        goto again;
+    }
+    addr = read_dword(s, s->dsp + 4);
+    addr_high = 0;
+    DPRINTF("SCRIPTS dsp=%08x opcode %08x arg %08x\n", s->dsp, insn, addr);
+    s->dsps = addr;
+    s->dcmd = insn >> 24;
+    s->dsp += 8;
+    switch (insn >> 30) {
+    case 0: /* Block move.  */
+        if (s->sist1 & LSI_SIST1_STO) {
+            DPRINTF("Delayed select timeout\n");
+            lsi_stop_script(s);
+            break;
+        }
+        s->dbc = insn & 0xffffff;
+        s->rbc = s->dbc;
+        /* ??? Set ESA.  */
+        s->ia = s->dsp - 8;
+        if (insn & (1 << 29)) {
+            /* Indirect addressing.  */
+            addr = read_dword(s, addr);
+        } else if (insn & (1 << 28)) {
+            uint32_t buf[2];
+            int32_t offset;
+            /* Table indirect addressing.  */
+
+            /* 32-bit Table indirect */
+            offset = sxt24(addr);
+            pci_dma_read(&s->dev, s->dsa + offset, buf, 8);
+            /* byte count is stored in bits 0:23 only */
+            s->dbc = cpu_to_le32(buf[0]) & 0xffffff;
+            s->rbc = s->dbc;
+            addr = cpu_to_le32(buf[1]);
+
+            /* 40-bit DMA, upper addr bits [39:32] stored in first DWORD of
+             * table, bits [31:24] */
+            if (lsi_dma_40bit(s))
+                addr_high = cpu_to_le32(buf[0]) >> 24;
+            else if (lsi_dma_ti64bit(s)) {
+                int selector = (cpu_to_le32(buf[0]) >> 24) & 0x1f;
+                switch (selector) {
+                case 0 ... 0x0f:
+                    /* offset index into scratch registers since
+                     * TI64 mode can use registers C to R */
+                    addr_high = s->scratch[2 + selector];
+                    break;
+                case 0x10:
+                    addr_high = s->mmrs;
+                    break;
+                case 0x11:
+                    addr_high = s->mmws;
+                    break;
+                case 0x12:
+                    addr_high = s->sfs;
+                    break;
+                case 0x13:
+                    addr_high = s->drs;
+                    break;
+                case 0x14:
+                    addr_high = s->sbms;
+                    break;
+                case 0x15:
+                    addr_high = s->dbms;
+                    break;
+                default:
+                    BADF("Illegal selector specified (0x%x > 0x15)"
+                         " for 64-bit DMA block move", selector);
+                    break;
+                }
+            }
+        } else if (lsi_dma_64bit(s)) {
+            /* fetch a 3rd dword if 64-bit direct move is enabled and
+               only if we're not doing table indirect or indirect addressing */
+            s->dbms = read_dword(s, s->dsp);
+            s->dsp += 4;
+            s->ia = s->dsp - 12;
+        }
+        if ((s->sstat1 & PHASE_MASK) != ((insn >> 24) & 7)) {
+            DPRINTF("Wrong phase got %d expected %d\n",
+                    s->sstat1 & PHASE_MASK, (insn >> 24) & 7);
+            lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0);
+            break;
+        }
+        s->dnad = addr;
+        s->dnad64 = addr_high;
+        switch (s->sstat1 & 0x7) {
+        case PHASE_DO:
+            s->waiting = 2;
+            lsi_do_dma(s, 1);
+            if (s->waiting)
+                s->waiting = 3;
+            break;
+        case PHASE_DI:
+            s->waiting = 2;
+            lsi_do_dma(s, 0);
+            if (s->waiting)
+                s->waiting = 3;
+            break;
+        case PHASE_CMD:
+            lsi_do_command(s);
+            break;
+        case PHASE_ST:
+            lsi_do_status(s);
+            break;
+        case PHASE_MO:
+            lsi_do_msgout(s);
+            break;
+        case PHASE_MI:
+            lsi_do_msgin(s);
+            break;
+        default:
+            BADF("Unimplemented phase %d\n", s->sstat1 & PHASE_MASK);
+            exit(1);
+        }
+        s->dfifo = s->dbc & 0xff;
+        s->ctest5 = (s->ctest5 & 0xfc) | ((s->dbc >> 8) & 3);
+        s->sbc = s->dbc;
+        s->rbc -= s->dbc;
+        s->ua = addr + s->dbc;
+        break;
+
+    case 1: /* IO or Read/Write instruction.  */
+        opcode = (insn >> 27) & 7;
+        if (opcode < 5) {
+            uint32_t id;
+
+            if (insn & (1 << 25)) {
+                id = read_dword(s, s->dsa + sxt24(insn));
+            } else {
+                id = insn;
+            }
+            id = (id >> 16) & 0xf;
+            if (insn & (1 << 26)) {
+                addr = s->dsp + sxt24(addr);
+            }
+            s->dnad = addr;
+            switch (opcode) {
+            case 0: /* Select */
+                s->sdid = id;
+                if (s->scntl1 & LSI_SCNTL1_CON) {
+                    DPRINTF("Already reselected, jumping to alternative address\n");
+                    s->dsp = s->dnad;
+                    break;
+                }
+                s->sstat0 |= LSI_SSTAT0_WOA;
+                s->scntl1 &= ~LSI_SCNTL1_IARB;
+                if (!scsi_device_find(&s->bus, 0, id, 0)) {
+                    lsi_bad_selection(s, id);
+                    break;
+                }
+                DPRINTF("Selected target %d%s\n",
+                        id, insn & (1 << 3) ? " ATN" : "");
+                /* ??? Linux drivers compain when this is set.  Maybe
+                   it only applies in low-level mode (unimplemented).
+                lsi_script_scsi_interrupt(s, LSI_SIST0_CMP, 0); */
+                s->select_tag = id << 8;
+                s->scntl1 |= LSI_SCNTL1_CON;
+                if (insn & (1 << 3)) {
+                    s->socl |= LSI_SOCL_ATN;
+                }
+                lsi_set_phase(s, PHASE_MO);
+                break;
+            case 1: /* Disconnect */
+                DPRINTF("Wait Disconnect\n");
+                s->scntl1 &= ~LSI_SCNTL1_CON;
+                break;
+            case 2: /* Wait Reselect */
+                if (!lsi_irq_on_rsl(s)) {
+                    lsi_wait_reselect(s);
+                }
+                break;
+            case 3: /* Set */
+                DPRINTF("Set%s%s%s%s\n",
+                        insn & (1 << 3) ? " ATN" : "",
+                        insn & (1 << 6) ? " ACK" : "",
+                        insn & (1 << 9) ? " TM" : "",
+                        insn & (1 << 10) ? " CC" : "");
+                if (insn & (1 << 3)) {
+                    s->socl |= LSI_SOCL_ATN;
+                    lsi_set_phase(s, PHASE_MO);
+                }
+                if (insn & (1 << 9)) {
+                    BADF("Target mode not implemented\n");
+                    exit(1);
+                }
+                if (insn & (1 << 10))
+                    s->carry = 1;
+                break;
+            case 4: /* Clear */
+                DPRINTF("Clear%s%s%s%s\n",
+                        insn & (1 << 3) ? " ATN" : "",
+                        insn & (1 << 6) ? " ACK" : "",
+                        insn & (1 << 9) ? " TM" : "",
+                        insn & (1 << 10) ? " CC" : "");
+                if (insn & (1 << 3)) {
+                    s->socl &= ~LSI_SOCL_ATN;
+                }
+                if (insn & (1 << 10))
+                    s->carry = 0;
+                break;
+            }
+        } else {
+            uint8_t op0;
+            uint8_t op1;
+            uint8_t data8;
+            int reg;
+            int operator;
+#ifdef DEBUG_LSI
+            static const char *opcode_names[3] =
+                {"Write", "Read", "Read-Modify-Write"};
+            static const char *operator_names[8] =
+                {"MOV", "SHL", "OR", "XOR", "AND", "SHR", "ADD", "ADC"};
+#endif
+
+            reg = ((insn >> 16) & 0x7f) | (insn & 0x80);
+            data8 = (insn >> 8) & 0xff;
+            opcode = (insn >> 27) & 7;
+            operator = (insn >> 24) & 7;
+            DPRINTF("%s reg 0x%x %s data8=0x%02x sfbr=0x%02x%s\n",
+                    opcode_names[opcode - 5], reg,
+                    operator_names[operator], data8, s->sfbr,
+                    (insn & (1 << 23)) ? " SFBR" : "");
+            op0 = op1 = 0;
+            switch (opcode) {
+            case 5: /* From SFBR */
+                op0 = s->sfbr;
+                op1 = data8;
+                break;
+            case 6: /* To SFBR */
+                if (operator)
+                    op0 = lsi_reg_readb(s, reg);
+                op1 = data8;
+                break;
+            case 7: /* Read-modify-write */
+                if (operator)
+                    op0 = lsi_reg_readb(s, reg);
+                if (insn & (1 << 23)) {
+                    op1 = s->sfbr;
+                } else {
+                    op1 = data8;
+                }
+                break;
+            }
+
+            switch (operator) {
+            case 0: /* move */
+                op0 = op1;
+                break;
+            case 1: /* Shift left */
+                op1 = op0 >> 7;
+                op0 = (op0 << 1) | s->carry;
+                s->carry = op1;
+                break;
+            case 2: /* OR */
+                op0 |= op1;
+                break;
+            case 3: /* XOR */
+                op0 ^= op1;
+                break;
+            case 4: /* AND */
+                op0 &= op1;
+                break;
+            case 5: /* SHR */
+                op1 = op0 & 1;
+                op0 = (op0 >> 1) | (s->carry << 7);
+                s->carry = op1;
+                break;
+            case 6: /* ADD */
+                op0 += op1;
+                s->carry = op0 < op1;
+                break;
+            case 7: /* ADC */
+                op0 += op1 + s->carry;
+                if (s->carry)
+                    s->carry = op0 <= op1;
+                else
+                    s->carry = op0 < op1;
+                break;
+            }
+
+            switch (opcode) {
+            case 5: /* From SFBR */
+            case 7: /* Read-modify-write */
+                lsi_reg_writeb(s, reg, op0);
+                break;
+            case 6: /* To SFBR */
+                s->sfbr = op0;
+                break;
+            }
+        }
+        break;
+
+    case 2: /* Transfer Control.  */
+        {
+            int cond;
+            int jmp;
+
+            if ((insn & 0x002e0000) == 0) {
+                DPRINTF("NOP\n");
+                break;
+            }
+            if (s->sist1 & LSI_SIST1_STO) {
+                DPRINTF("Delayed select timeout\n");
+                lsi_stop_script(s);
+                break;
+            }
+            cond = jmp = (insn & (1 << 19)) != 0;
+            if (cond == jmp && (insn & (1 << 21))) {
+                DPRINTF("Compare carry %d\n", s->carry == jmp);
+                cond = s->carry != 0;
+            }
+            if (cond == jmp && (insn & (1 << 17))) {
+                DPRINTF("Compare phase %d %c= %d\n",
+                        (s->sstat1 & PHASE_MASK),
+                        jmp ? '=' : '!',
+                        ((insn >> 24) & 7));
+                cond = (s->sstat1 & PHASE_MASK) == ((insn >> 24) & 7);
+            }
+            if (cond == jmp && (insn & (1 << 18))) {
+                uint8_t mask;
+
+                mask = (~insn >> 8) & 0xff;
+                DPRINTF("Compare data 0x%x & 0x%x %c= 0x%x\n",
+                        s->sfbr, mask, jmp ? '=' : '!', insn & mask);
+                cond = (s->sfbr & mask) == (insn & mask);
+            }
+            if (cond == jmp) {
+                if (insn & (1 << 23)) {
+                    /* Relative address.  */
+                    addr = s->dsp + sxt24(addr);
+                }
+                switch ((insn >> 27) & 7) {
+                case 0: /* Jump */
+                    DPRINTF("Jump to 0x%08x\n", addr);
+                    s->dsp = addr;
+                    break;
+                case 1: /* Call */
+                    DPRINTF("Call 0x%08x\n", addr);
+                    s->temp = s->dsp;
+                    s->dsp = addr;
+                    break;
+                case 2: /* Return */
+                    DPRINTF("Return to 0x%08x\n", s->temp);
+                    s->dsp = s->temp;
+                    break;
+                case 3: /* Interrupt */
+                    DPRINTF("Interrupt 0x%08x\n", s->dsps);
+                    if ((insn & (1 << 20)) != 0) {
+                        s->istat0 |= LSI_ISTAT0_INTF;
+                        lsi_update_irq(s);
+                    } else {
+                        lsi_script_dma_interrupt(s, LSI_DSTAT_SIR);
+                    }
+                    break;
+                default:
+                    DPRINTF("Illegal transfer control\n");
+                    lsi_script_dma_interrupt(s, LSI_DSTAT_IID);
+                    break;
+                }
+            } else {
+                DPRINTF("Control condition failed\n");
+            }
+        }
+        break;
+
+    case 3:
+        if ((insn & (1 << 29)) == 0) {
+            /* Memory move.  */
+            uint32_t dest;
+            /* ??? The docs imply the destination address is loaded into
+               the TEMP register.  However the Linux drivers rely on
+               the value being presrved.  */
+            dest = read_dword(s, s->dsp);
+            s->dsp += 4;
+            lsi_memcpy(s, dest, addr, insn & 0xffffff);
+        } else {
+            uint8_t data[7];
+            int reg;
+            int n;
+            int i;
+
+            if (insn & (1 << 28)) {
+                addr = s->dsa + sxt24(addr);
+            }
+            n = (insn & 7);
+            reg = (insn >> 16) & 0xff;
+            if (insn & (1 << 24)) {
+                pci_dma_read(&s->dev, addr, data, n);
+                DPRINTF("Load reg 0x%x size %d addr 0x%08x = %08x\n", reg, n,
+                        addr, *(int *)data);
+                for (i = 0; i < n; i++) {
+                    lsi_reg_writeb(s, reg + i, data[i]);
+                }
+            } else {
+                DPRINTF("Store reg 0x%x size %d addr 0x%08x\n", reg, n, addr);
+                for (i = 0; i < n; i++) {
+                    data[i] = lsi_reg_readb(s, reg + i);
+                }
+                pci_dma_write(&s->dev, addr, data, n);
+            }
+        }
+    }
+    if (insn_processed > 10000 && !s->waiting) {
+        /* Some windows drivers make the device spin waiting for a memory
+           location to change.  If we have been executed a lot of code then
+           assume this is the case and force an unexpected device disconnect.
+           This is apparently sufficient to beat the drivers into submission.
+         */
+        if (!(s->sien0 & LSI_SIST0_UDC))
+            fprintf(stderr, "inf. loop with UDC masked\n");
+        lsi_script_scsi_interrupt(s, LSI_SIST0_UDC, 0);
+        lsi_disconnect(s);
+    } else if (s->istat1 & LSI_ISTAT1_SRUN && !s->waiting) {
+        if (s->dcntl & LSI_DCNTL_SSM) {
+            lsi_script_dma_interrupt(s, LSI_DSTAT_SSI);
+        } else {
+            goto again;
+        }
+    }
+    DPRINTF("SCRIPTS execution stopped\n");
+}
+
+static uint8_t lsi_reg_readb(LSIState *s, int offset)
+{
+    uint8_t tmp;
+#define CASE_GET_REG24(name, addr) \
+    case addr: return s->name & 0xff; \
+    case addr + 1: return (s->name >> 8) & 0xff; \
+    case addr + 2: return (s->name >> 16) & 0xff;
+
+#define CASE_GET_REG32(name, addr) \
+    case addr: return s->name & 0xff; \
+    case addr + 1: return (s->name >> 8) & 0xff; \
+    case addr + 2: return (s->name >> 16) & 0xff; \
+    case addr + 3: return (s->name >> 24) & 0xff;
+
+#ifdef DEBUG_LSI_REG
+    DPRINTF("Read reg %x\n", offset);
+#endif
+    switch (offset) {
+    case 0x00: /* SCNTL0 */
+        return s->scntl0;
+    case 0x01: /* SCNTL1 */
+        return s->scntl1;
+    case 0x02: /* SCNTL2 */
+        return s->scntl2;
+    case 0x03: /* SCNTL3 */
+        return s->scntl3;
+    case 0x04: /* SCID */
+        return s->scid;
+    case 0x05: /* SXFER */
+        return s->sxfer;
+    case 0x06: /* SDID */
+        return s->sdid;
+    case 0x07: /* GPREG0 */
+        return 0x7f;
+    case 0x08: /* Revision ID */
+        return 0x00;
+    case 0xa: /* SSID */
+        return s->ssid;
+    case 0xb: /* SBCL */
+        /* ??? This is not correct. However it's (hopefully) only
+           used for diagnostics, so should be ok.  */
+        return 0;
+    case 0xc: /* DSTAT */
+        tmp = s->dstat | 0x80;
+        if ((s->istat0 & LSI_ISTAT0_INTF) == 0)
+            s->dstat = 0;
+        lsi_update_irq(s);
+        return tmp;
+    case 0x0d: /* SSTAT0 */
+        return s->sstat0;
+    case 0x0e: /* SSTAT1 */
+        return s->sstat1;
+    case 0x0f: /* SSTAT2 */
+        return s->scntl1 & LSI_SCNTL1_CON ? 0 : 2;
+    CASE_GET_REG32(dsa, 0x10)
+    case 0x14: /* ISTAT0 */
+        return s->istat0;
+    case 0x15: /* ISTAT1 */
+        return s->istat1;
+    case 0x16: /* MBOX0 */
+        return s->mbox0;
+    case 0x17: /* MBOX1 */
+        return s->mbox1;
+    case 0x18: /* CTEST0 */
+        return 0xff;
+    case 0x19: /* CTEST1 */
+        return 0;
+    case 0x1a: /* CTEST2 */
+        tmp = s->ctest2 | LSI_CTEST2_DACK | LSI_CTEST2_CM;
+        if (s->istat0 & LSI_ISTAT0_SIGP) {
+            s->istat0 &= ~LSI_ISTAT0_SIGP;
+            tmp |= LSI_CTEST2_SIGP;
+        }
+        return tmp;
+    case 0x1b: /* CTEST3 */
+        return s->ctest3;
+    CASE_GET_REG32(temp, 0x1c)
+    case 0x20: /* DFIFO */
+        return 0;
+    case 0x21: /* CTEST4 */
+        return s->ctest4;
+    case 0x22: /* CTEST5 */
+        return s->ctest5;
+    case 0x23: /* CTEST6 */
+         return 0;
+    CASE_GET_REG24(dbc, 0x24)
+    case 0x27: /* DCMD */
+        return s->dcmd;
+    CASE_GET_REG32(dnad, 0x28)
+    CASE_GET_REG32(dsp, 0x2c)
+    CASE_GET_REG32(dsps, 0x30)
+    CASE_GET_REG32(scratch[0], 0x34)
+    case 0x38: /* DMODE */
+        return s->dmode;
+    case 0x39: /* DIEN */
+        return s->dien;
+    case 0x3a: /* SBR */
+        return s->sbr;
+    case 0x3b: /* DCNTL */
+        return s->dcntl;
+    case 0x40: /* SIEN0 */
+        return s->sien0;
+    case 0x41: /* SIEN1 */
+        return s->sien1;
+    case 0x42: /* SIST0 */
+        tmp = s->sist0;
+        s->sist0 = 0;
+        lsi_update_irq(s);
+        return tmp;
+    case 0x43: /* SIST1 */
+        tmp = s->sist1;
+        s->sist1 = 0;
+        lsi_update_irq(s);
+        return tmp;
+    case 0x46: /* MACNTL */
+        return 0x0f;
+    case 0x47: /* GPCNTL0 */
+        return 0x0f;
+    case 0x48: /* STIME0 */
+        return s->stime0;
+    case 0x4a: /* RESPID0 */
+        return s->respid0;
+    case 0x4b: /* RESPID1 */
+        return s->respid1;
+    case 0x4d: /* STEST1 */
+        return s->stest1;
+    case 0x4e: /* STEST2 */
+        return s->stest2;
+    case 0x4f: /* STEST3 */
+        return s->stest3;
+    case 0x50: /* SIDL */
+        /* This is needed by the linux drivers.  We currently only update it
+           during the MSG IN phase.  */
+        return s->sidl;
+    case 0x52: /* STEST4 */
+        return 0xe0;
+    case 0x56: /* CCNTL0 */
+        return s->ccntl0;
+    case 0x57: /* CCNTL1 */
+        return s->ccntl1;
+    case 0x58: /* SBDL */
+        /* Some drivers peek at the data bus during the MSG IN phase.  */
+        if ((s->sstat1 & PHASE_MASK) == PHASE_MI)
+            return s->msg[0];
+        return 0;
+    case 0x59: /* SBDL high */
+        return 0;
+    CASE_GET_REG32(mmrs, 0xa0)
+    CASE_GET_REG32(mmws, 0xa4)
+    CASE_GET_REG32(sfs, 0xa8)
+    CASE_GET_REG32(drs, 0xac)
+    CASE_GET_REG32(sbms, 0xb0)
+    CASE_GET_REG32(dbms, 0xb4)
+    CASE_GET_REG32(dnad64, 0xb8)
+    CASE_GET_REG32(pmjad1, 0xc0)
+    CASE_GET_REG32(pmjad2, 0xc4)
+    CASE_GET_REG32(rbc, 0xc8)
+    CASE_GET_REG32(ua, 0xcc)
+    CASE_GET_REG32(ia, 0xd4)
+    CASE_GET_REG32(sbc, 0xd8)
+    CASE_GET_REG32(csbc, 0xdc)
+    }
+    if (offset >= 0x5c && offset < 0xa0) {
+        int n;
+        int shift;
+        n = (offset - 0x58) >> 2;
+        shift = (offset & 3) * 8;
+        return (s->scratch[n] >> shift) & 0xff;
+    }
+    BADF("readb 0x%x\n", offset);
+    exit(1);
+#undef CASE_GET_REG24
+#undef CASE_GET_REG32
+}
+
+static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
+{
+#define CASE_SET_REG24(name, addr) \
+    case addr    : s->name &= 0xffffff00; s->name |= val;       break; \
+    case addr + 1: s->name &= 0xffff00ff; s->name |= val << 8;  break; \
+    case addr + 2: s->name &= 0xff00ffff; s->name |= val << 16; break;
+
+#define CASE_SET_REG32(name, addr) \
+    case addr    : s->name &= 0xffffff00; s->name |= val;       break; \
+    case addr + 1: s->name &= 0xffff00ff; s->name |= val << 8;  break; \
+    case addr + 2: s->name &= 0xff00ffff; s->name |= val << 16; break; \
+    case addr + 3: s->name &= 0x00ffffff; s->name |= val << 24; break;
+
+#ifdef DEBUG_LSI_REG
+    DPRINTF("Write reg %x = %02x\n", offset, val);
+#endif
+    switch (offset) {
+    case 0x00: /* SCNTL0 */
+        s->scntl0 = val;
+        if (val & LSI_SCNTL0_START) {
+            BADF("Start sequence not implemented\n");
+        }
+        break;
+    case 0x01: /* SCNTL1 */
+        s->scntl1 = val & ~LSI_SCNTL1_SST;
+        if (val & LSI_SCNTL1_IARB) {
+            BADF("Immediate Arbritration not implemented\n");
+        }
+        if (val & LSI_SCNTL1_RST) {
+            if (!(s->sstat0 & LSI_SSTAT0_RST)) {
+                qbus_reset_all(&s->bus.qbus);
+                s->sstat0 |= LSI_SSTAT0_RST;
+                lsi_script_scsi_interrupt(s, LSI_SIST0_RST, 0);
+            }
+        } else {
+            s->sstat0 &= ~LSI_SSTAT0_RST;
+        }
+        break;
+    case 0x02: /* SCNTL2 */
+        val &= ~(LSI_SCNTL2_WSR | LSI_SCNTL2_WSS);
+        s->scntl2 = val;
+        break;
+    case 0x03: /* SCNTL3 */
+        s->scntl3 = val;
+        break;
+    case 0x04: /* SCID */
+        s->scid = val;
+        break;
+    case 0x05: /* SXFER */
+        s->sxfer = val;
+        break;
+    case 0x06: /* SDID */
+        if ((val & 0xf) != (s->ssid & 0xf))
+            BADF("Destination ID does not match SSID\n");
+        s->sdid = val & 0xf;
+        break;
+    case 0x07: /* GPREG0 */
+        break;
+    case 0x08: /* SFBR */
+        /* The CPU is not allowed to write to this register.  However the
+           SCRIPTS register move instructions are.  */
+        s->sfbr = val;
+        break;
+    case 0x0a: case 0x0b:
+        /* Openserver writes to these readonly registers on startup */
+       return;
+    case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+        /* Linux writes to these readonly registers on startup.  */
+        return;
+    CASE_SET_REG32(dsa, 0x10)
+    case 0x14: /* ISTAT0 */
+        s->istat0 = (s->istat0 & 0x0f) | (val & 0xf0);
+        if (val & LSI_ISTAT0_ABRT) {
+            lsi_script_dma_interrupt(s, LSI_DSTAT_ABRT);
+        }
+        if (val & LSI_ISTAT0_INTF) {
+            s->istat0 &= ~LSI_ISTAT0_INTF;
+            lsi_update_irq(s);
+        }
+        if (s->waiting == 1 && val & LSI_ISTAT0_SIGP) {
+            DPRINTF("Woken by SIGP\n");
+            s->waiting = 0;
+            s->dsp = s->dnad;
+            lsi_execute_script(s);
+        }
+        if (val & LSI_ISTAT0_SRST) {
+            qdev_reset_all(&s->dev.qdev);
+        }
+        break;
+    case 0x16: /* MBOX0 */
+        s->mbox0 = val;
+        break;
+    case 0x17: /* MBOX1 */
+        s->mbox1 = val;
+        break;
+    case 0x1a: /* CTEST2 */
+       s->ctest2 = val & LSI_CTEST2_PCICIE;
+       break;
+    case 0x1b: /* CTEST3 */
+        s->ctest3 = val & 0x0f;
+        break;
+    CASE_SET_REG32(temp, 0x1c)
+    case 0x21: /* CTEST4 */
+        if (val & 7) {
+           BADF("Unimplemented CTEST4-FBL 0x%x\n", val);
+        }
+        s->ctest4 = val;
+        break;
+    case 0x22: /* CTEST5 */
+        if (val & (LSI_CTEST5_ADCK | LSI_CTEST5_BBCK)) {
+            BADF("CTEST5 DMA increment not implemented\n");
+        }
+        s->ctest5 = val;
+        break;
+    CASE_SET_REG24(dbc, 0x24)
+    CASE_SET_REG32(dnad, 0x28)
+    case 0x2c: /* DSP[0:7] */
+        s->dsp &= 0xffffff00;
+        s->dsp |= val;
+        break;
+    case 0x2d: /* DSP[8:15] */
+        s->dsp &= 0xffff00ff;
+        s->dsp |= val << 8;
+        break;
+    case 0x2e: /* DSP[16:23] */
+        s->dsp &= 0xff00ffff;
+        s->dsp |= val << 16;
+        break;
+    case 0x2f: /* DSP[24:31] */
+        s->dsp &= 0x00ffffff;
+        s->dsp |= val << 24;
+        if ((s->dmode & LSI_DMODE_MAN) == 0
+            && (s->istat1 & LSI_ISTAT1_SRUN) == 0)
+            lsi_execute_script(s);
+        break;
+    CASE_SET_REG32(dsps, 0x30)
+    CASE_SET_REG32(scratch[0], 0x34)
+    case 0x38: /* DMODE */
+        if (val & (LSI_DMODE_SIOM | LSI_DMODE_DIOM)) {
+            BADF("IO mappings not implemented\n");
+        }
+        s->dmode = val;
+        break;
+    case 0x39: /* DIEN */
+        s->dien = val;
+        lsi_update_irq(s);
+        break;
+    case 0x3a: /* SBR */
+        s->sbr = val;
+        break;
+    case 0x3b: /* DCNTL */
+        s->dcntl = val & ~(LSI_DCNTL_PFF | LSI_DCNTL_STD);
+        if ((val & LSI_DCNTL_STD) && (s->istat1 & LSI_ISTAT1_SRUN) == 0)
+            lsi_execute_script(s);
+        break;
+    case 0x40: /* SIEN0 */
+        s->sien0 = val;
+        lsi_update_irq(s);
+        break;
+    case 0x41: /* SIEN1 */
+        s->sien1 = val;
+        lsi_update_irq(s);
+        break;
+    case 0x47: /* GPCNTL0 */
+        break;
+    case 0x48: /* STIME0 */
+        s->stime0 = val;
+        break;
+    case 0x49: /* STIME1 */
+        if (val & 0xf) {
+            DPRINTF("General purpose timer not implemented\n");
+            /* ??? Raising the interrupt immediately seems to be sufficient
+               to keep the FreeBSD driver happy.  */
+            lsi_script_scsi_interrupt(s, 0, LSI_SIST1_GEN);
+        }
+        break;
+    case 0x4a: /* RESPID0 */
+        s->respid0 = val;
+        break;
+    case 0x4b: /* RESPID1 */
+        s->respid1 = val;
+        break;
+    case 0x4d: /* STEST1 */
+        s->stest1 = val;
+        break;
+    case 0x4e: /* STEST2 */
+        if (val & 1) {
+            BADF("Low level mode not implemented\n");
+        }
+        s->stest2 = val;
+        break;
+    case 0x4f: /* STEST3 */
+        if (val & 0x41) {
+            BADF("SCSI FIFO test mode not implemented\n");
+        }
+        s->stest3 = val;
+        break;
+    case 0x56: /* CCNTL0 */
+        s->ccntl0 = val;
+        break;
+    case 0x57: /* CCNTL1 */
+        s->ccntl1 = val;
+        break;
+    CASE_SET_REG32(mmrs, 0xa0)
+    CASE_SET_REG32(mmws, 0xa4)
+    CASE_SET_REG32(sfs, 0xa8)
+    CASE_SET_REG32(drs, 0xac)
+    CASE_SET_REG32(sbms, 0xb0)
+    CASE_SET_REG32(dbms, 0xb4)
+    CASE_SET_REG32(dnad64, 0xb8)
+    CASE_SET_REG32(pmjad1, 0xc0)
+    CASE_SET_REG32(pmjad2, 0xc4)
+    CASE_SET_REG32(rbc, 0xc8)
+    CASE_SET_REG32(ua, 0xcc)
+    CASE_SET_REG32(ia, 0xd4)
+    CASE_SET_REG32(sbc, 0xd8)
+    CASE_SET_REG32(csbc, 0xdc)
+    default:
+        if (offset >= 0x5c && offset < 0xa0) {
+            int n;
+            int shift;
+            n = (offset - 0x58) >> 2;
+            shift = (offset & 3) * 8;
+            s->scratch[n] &= ~(0xff << shift);
+            s->scratch[n] |= (val & 0xff) << shift;
+        } else {
+            BADF("Unhandled writeb 0x%x = 0x%x\n", offset, val);
+        }
+    }
+#undef CASE_SET_REG24
+#undef CASE_SET_REG32
+}
+
+static void lsi_mmio_write(void *opaque, hwaddr addr,
+                           uint64_t val, unsigned size)
+{
+    LSIState *s = opaque;
+
+    lsi_reg_writeb(s, addr & 0xff, val);
+}
+
+static uint64_t lsi_mmio_read(void *opaque, hwaddr addr,
+                              unsigned size)
+{
+    LSIState *s = opaque;
+
+    return lsi_reg_readb(s, addr & 0xff);
+}
+
+static const MemoryRegionOps lsi_mmio_ops = {
+    .read = lsi_mmio_read,
+    .write = lsi_mmio_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static void lsi_ram_write(void *opaque, hwaddr addr,
+                          uint64_t val, unsigned size)
+{
+    LSIState *s = opaque;
+    uint32_t newval;
+    uint32_t mask;
+    int shift;
+
+    newval = s->script_ram[addr >> 2];
+    shift = (addr & 3) * 8;
+    mask = ((uint64_t)1 << (size * 8)) - 1;
+    newval &= ~(mask << shift);
+    newval |= val << shift;
+    s->script_ram[addr >> 2] = newval;
+}
+
+static uint64_t lsi_ram_read(void *opaque, hwaddr addr,
+                             unsigned size)
+{
+    LSIState *s = opaque;
+    uint32_t val;
+    uint32_t mask;
+
+    val = s->script_ram[addr >> 2];
+    mask = ((uint64_t)1 << (size * 8)) - 1;
+    val >>= (addr & 3) * 8;
+    return val & mask;
+}
+
+static const MemoryRegionOps lsi_ram_ops = {
+    .read = lsi_ram_read,
+    .write = lsi_ram_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static uint64_t lsi_io_read(void *opaque, hwaddr addr,
+                            unsigned size)
+{
+    LSIState *s = opaque;
+    return lsi_reg_readb(s, addr & 0xff);
+}
+
+static void lsi_io_write(void *opaque, hwaddr addr,
+                         uint64_t val, unsigned size)
+{
+    LSIState *s = opaque;
+    lsi_reg_writeb(s, addr & 0xff, val);
+}
+
+static const MemoryRegionOps lsi_io_ops = {
+    .read = lsi_io_read,
+    .write = lsi_io_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static void lsi_scsi_reset(DeviceState *dev)
+{
+    LSIState *s = DO_UPCAST(LSIState, dev.qdev, dev);
+
+    lsi_soft_reset(s);
+}
+
+static void lsi_pre_save(void *opaque)
+{
+    LSIState *s = opaque;
+
+    if (s->current) {
+        assert(s->current->dma_buf == NULL);
+        assert(s->current->dma_len == 0);
+    }
+    assert(QTAILQ_EMPTY(&s->queue));
+}
+
+static const VMStateDescription vmstate_lsi_scsi = {
+    .name = "lsiscsi",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .pre_save = lsi_pre_save,
+    .fields      = (VMStateField []) {
+        VMSTATE_PCI_DEVICE(dev, LSIState),
+
+        VMSTATE_INT32(carry, LSIState),
+        VMSTATE_INT32(status, LSIState),
+        VMSTATE_INT32(msg_action, LSIState),
+        VMSTATE_INT32(msg_len, LSIState),
+        VMSTATE_BUFFER(msg, LSIState),
+        VMSTATE_INT32(waiting, LSIState),
+
+        VMSTATE_UINT32(dsa, LSIState),
+        VMSTATE_UINT32(temp, LSIState),
+        VMSTATE_UINT32(dnad, LSIState),
+        VMSTATE_UINT32(dbc, LSIState),
+        VMSTATE_UINT8(istat0, LSIState),
+        VMSTATE_UINT8(istat1, LSIState),
+        VMSTATE_UINT8(dcmd, LSIState),
+        VMSTATE_UINT8(dstat, LSIState),
+        VMSTATE_UINT8(dien, LSIState),
+        VMSTATE_UINT8(sist0, LSIState),
+        VMSTATE_UINT8(sist1, LSIState),
+        VMSTATE_UINT8(sien0, LSIState),
+        VMSTATE_UINT8(sien1, LSIState),
+        VMSTATE_UINT8(mbox0, LSIState),
+        VMSTATE_UINT8(mbox1, LSIState),
+        VMSTATE_UINT8(dfifo, LSIState),
+        VMSTATE_UINT8(ctest2, LSIState),
+        VMSTATE_UINT8(ctest3, LSIState),
+        VMSTATE_UINT8(ctest4, LSIState),
+        VMSTATE_UINT8(ctest5, LSIState),
+        VMSTATE_UINT8(ccntl0, LSIState),
+        VMSTATE_UINT8(ccntl1, LSIState),
+        VMSTATE_UINT32(dsp, LSIState),
+        VMSTATE_UINT32(dsps, LSIState),
+        VMSTATE_UINT8(dmode, LSIState),
+        VMSTATE_UINT8(dcntl, LSIState),
+        VMSTATE_UINT8(scntl0, LSIState),
+        VMSTATE_UINT8(scntl1, LSIState),
+        VMSTATE_UINT8(scntl2, LSIState),
+        VMSTATE_UINT8(scntl3, LSIState),
+        VMSTATE_UINT8(sstat0, LSIState),
+        VMSTATE_UINT8(sstat1, LSIState),
+        VMSTATE_UINT8(scid, LSIState),
+        VMSTATE_UINT8(sxfer, LSIState),
+        VMSTATE_UINT8(socl, LSIState),
+        VMSTATE_UINT8(sdid, LSIState),
+        VMSTATE_UINT8(ssid, LSIState),
+        VMSTATE_UINT8(sfbr, LSIState),
+        VMSTATE_UINT8(stest1, LSIState),
+        VMSTATE_UINT8(stest2, LSIState),
+        VMSTATE_UINT8(stest3, LSIState),
+        VMSTATE_UINT8(sidl, LSIState),
+        VMSTATE_UINT8(stime0, LSIState),
+        VMSTATE_UINT8(respid0, LSIState),
+        VMSTATE_UINT8(respid1, LSIState),
+        VMSTATE_UINT32(mmrs, LSIState),
+        VMSTATE_UINT32(mmws, LSIState),
+        VMSTATE_UINT32(sfs, LSIState),
+        VMSTATE_UINT32(drs, LSIState),
+        VMSTATE_UINT32(sbms, LSIState),
+        VMSTATE_UINT32(dbms, LSIState),
+        VMSTATE_UINT32(dnad64, LSIState),
+        VMSTATE_UINT32(pmjad1, LSIState),
+        VMSTATE_UINT32(pmjad2, LSIState),
+        VMSTATE_UINT32(rbc, LSIState),
+        VMSTATE_UINT32(ua, LSIState),
+        VMSTATE_UINT32(ia, LSIState),
+        VMSTATE_UINT32(sbc, LSIState),
+        VMSTATE_UINT32(csbc, LSIState),
+        VMSTATE_BUFFER_UNSAFE(scratch, LSIState, 0, 18 * sizeof(uint32_t)),
+        VMSTATE_UINT8(sbr, LSIState),
+
+        VMSTATE_BUFFER_UNSAFE(script_ram, LSIState, 0, 2048 * sizeof(uint32_t)),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void lsi_scsi_uninit(PCIDevice *d)
+{
+    LSIState *s = DO_UPCAST(LSIState, dev, d);
+
+    memory_region_destroy(&s->mmio_io);
+    memory_region_destroy(&s->ram_io);
+    memory_region_destroy(&s->io_io);
+}
+
+static const struct SCSIBusInfo lsi_scsi_info = {
+    .tcq = true,
+    .max_target = LSI_MAX_DEVS,
+    .max_lun = 0,  /* LUN support is buggy */
+
+    .transfer_data = lsi_transfer_data,
+    .complete = lsi_command_complete,
+    .cancel = lsi_request_cancelled
+};
+
+static int lsi_scsi_init(PCIDevice *dev)
+{
+    LSIState *s = DO_UPCAST(LSIState, dev, dev);
+    uint8_t *pci_conf;
+
+    pci_conf = s->dev.config;
+
+    /* PCI latency timer = 255 */
+    pci_conf[PCI_LATENCY_TIMER] = 0xff;
+    /* Interrupt pin A */
+    pci_conf[PCI_INTERRUPT_PIN] = 0x01;
+
+    memory_region_init_io(&s->mmio_io, &lsi_mmio_ops, s, "lsi-mmio", 0x400);
+    memory_region_init_io(&s->ram_io, &lsi_ram_ops, s, "lsi-ram", 0x2000);
+    memory_region_init_io(&s->io_io, &lsi_io_ops, s, "lsi-io", 256);
+
+    pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_io);
+    pci_register_bar(&s->dev, 1, 0, &s->mmio_io);
+    pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ram_io);
+    QTAILQ_INIT(&s->queue);
+
+    scsi_bus_new(&s->bus, &dev->qdev, &lsi_scsi_info);
+    if (!dev->qdev.hotplugged) {
+        return scsi_bus_legacy_handle_cmdline(&s->bus);
+    }
+    return 0;
+}
+
+static void lsi_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = lsi_scsi_init;
+    k->exit = lsi_scsi_uninit;
+    k->vendor_id = PCI_VENDOR_ID_LSI_LOGIC;
+    k->device_id = PCI_DEVICE_ID_LSI_53C895A;
+    k->class_id = PCI_CLASS_STORAGE_SCSI;
+    k->subsystem_id = 0x1000;
+    dc->reset = lsi_scsi_reset;
+    dc->vmsd = &vmstate_lsi_scsi;
+}
+
+static const TypeInfo lsi_info = {
+    .name          = "lsi53c895a",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(LSIState),
+    .class_init    = lsi_class_init,
+};
+
+static void lsi53c895a_register_types(void)
+{
+    type_register_static(&lsi_info);
+}
+
+type_init(lsi53c895a_register_types)
diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c
new file mode 100644 (file)
index 0000000..f46f800
--- /dev/null
@@ -0,0 +1,2213 @@
+/*
+ * QEMU MegaRAID SAS 8708EM2 Host Bus Adapter emulation
+ * Based on the linux driver code at drivers/scsi/megaraid
+ *
+ * Copyright (c) 2009-2012 Hannes Reinecke, SUSE Labs
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "sysemu/dma.h"
+#include "hw/pci/msix.h"
+#include "qemu/iov.h"
+#include "hw/scsi/scsi.h"
+#include "block/scsi.h"
+#include "trace.h"
+
+#include "hw/mfi.h"
+
+#define MEGASAS_VERSION "1.70"
+#define MEGASAS_MAX_FRAMES 2048         /* Firmware limit at 65535 */
+#define MEGASAS_DEFAULT_FRAMES 1000     /* Windows requires this */
+#define MEGASAS_MAX_SGE 128             /* Firmware limit */
+#define MEGASAS_DEFAULT_SGE 80
+#define MEGASAS_MAX_SECTORS 0xFFFF      /* No real limit */
+#define MEGASAS_MAX_ARRAYS 128
+
+#define MEGASAS_HBA_SERIAL "QEMU123456"
+#define NAA_LOCALLY_ASSIGNED_ID 0x3ULL
+#define IEEE_COMPANY_LOCALLY_ASSIGNED 0x525400
+
+#define MEGASAS_FLAG_USE_JBOD      0
+#define MEGASAS_MASK_USE_JBOD      (1 << MEGASAS_FLAG_USE_JBOD)
+#define MEGASAS_FLAG_USE_MSIX      1
+#define MEGASAS_MASK_USE_MSIX      (1 << MEGASAS_FLAG_USE_MSIX)
+#define MEGASAS_FLAG_USE_QUEUE64   2
+#define MEGASAS_MASK_USE_QUEUE64   (1 << MEGASAS_FLAG_USE_QUEUE64)
+
+static const char *mfi_frame_desc[] = {
+    "MFI init", "LD Read", "LD Write", "LD SCSI", "PD SCSI",
+    "MFI Doorbell", "MFI Abort", "MFI SMP", "MFI Stop"};
+
+typedef struct MegasasCmd {
+    uint32_t index;
+    uint16_t flags;
+    uint16_t count;
+    uint64_t context;
+
+    hwaddr pa;
+    hwaddr pa_size;
+    union mfi_frame *frame;
+    SCSIRequest *req;
+    QEMUSGList qsg;
+    void *iov_buf;
+    size_t iov_size;
+    size_t iov_offset;
+    struct MegasasState *state;
+} MegasasCmd;
+
+typedef struct MegasasState {
+    PCIDevice dev;
+    MemoryRegion mmio_io;
+    MemoryRegion port_io;
+    MemoryRegion queue_io;
+    uint32_t frame_hi;
+
+    int fw_state;
+    uint32_t fw_sge;
+    uint32_t fw_cmds;
+    uint32_t flags;
+    int fw_luns;
+    int intr_mask;
+    int doorbell;
+    int busy;
+
+    MegasasCmd *event_cmd;
+    int event_locale;
+    int event_class;
+    int event_count;
+    int shutdown_event;
+    int boot_event;
+
+    uint64_t sas_addr;
+    char *hba_serial;
+
+    uint64_t reply_queue_pa;
+    void *reply_queue;
+    int reply_queue_len;
+    int reply_queue_head;
+    int reply_queue_tail;
+    uint64_t consumer_pa;
+    uint64_t producer_pa;
+
+    MegasasCmd frames[MEGASAS_MAX_FRAMES];
+
+    SCSIBus bus;
+} MegasasState;
+
+#define MEGASAS_INTR_DISABLED_MASK 0xFFFFFFFF
+
+static bool megasas_intr_enabled(MegasasState *s)
+{
+    if ((s->intr_mask & MEGASAS_INTR_DISABLED_MASK) !=
+        MEGASAS_INTR_DISABLED_MASK) {
+        return true;
+    }
+    return false;
+}
+
+static bool megasas_use_queue64(MegasasState *s)
+{
+    return s->flags & MEGASAS_MASK_USE_QUEUE64;
+}
+
+static bool megasas_use_msix(MegasasState *s)
+{
+    return s->flags & MEGASAS_MASK_USE_MSIX;
+}
+
+static bool megasas_is_jbod(MegasasState *s)
+{
+    return s->flags & MEGASAS_MASK_USE_JBOD;
+}
+
+static void megasas_frame_set_cmd_status(unsigned long frame, uint8_t v)
+{
+    stb_phys(frame + offsetof(struct mfi_frame_header, cmd_status), v);
+}
+
+static void megasas_frame_set_scsi_status(unsigned long frame, uint8_t v)
+{
+    stb_phys(frame + offsetof(struct mfi_frame_header, scsi_status), v);
+}
+
+/*
+ * Context is considered opaque, but the HBA firmware is running
+ * in little endian mode. So convert it to little endian, too.
+ */
+static uint64_t megasas_frame_get_context(unsigned long frame)
+{
+    return ldq_le_phys(frame + offsetof(struct mfi_frame_header, context));
+}
+
+static bool megasas_frame_is_ieee_sgl(MegasasCmd *cmd)
+{
+    return cmd->flags & MFI_FRAME_IEEE_SGL;
+}
+
+static bool megasas_frame_is_sgl64(MegasasCmd *cmd)
+{
+    return cmd->flags & MFI_FRAME_SGL64;
+}
+
+static bool megasas_frame_is_sense64(MegasasCmd *cmd)
+{
+    return cmd->flags & MFI_FRAME_SENSE64;
+}
+
+static uint64_t megasas_sgl_get_addr(MegasasCmd *cmd,
+                                     union mfi_sgl *sgl)
+{
+    uint64_t addr;
+
+    if (megasas_frame_is_ieee_sgl(cmd)) {
+        addr = le64_to_cpu(sgl->sg_skinny->addr);
+    } else if (megasas_frame_is_sgl64(cmd)) {
+        addr = le64_to_cpu(sgl->sg64->addr);
+    } else {
+        addr = le32_to_cpu(sgl->sg32->addr);
+    }
+    return addr;
+}
+
+static uint32_t megasas_sgl_get_len(MegasasCmd *cmd,
+                                    union mfi_sgl *sgl)
+{
+    uint32_t len;
+
+    if (megasas_frame_is_ieee_sgl(cmd)) {
+        len = le32_to_cpu(sgl->sg_skinny->len);
+    } else if (megasas_frame_is_sgl64(cmd)) {
+        len = le32_to_cpu(sgl->sg64->len);
+    } else {
+        len = le32_to_cpu(sgl->sg32->len);
+    }
+    return len;
+}
+
+static union mfi_sgl *megasas_sgl_next(MegasasCmd *cmd,
+                                       union mfi_sgl *sgl)
+{
+    uint8_t *next = (uint8_t *)sgl;
+
+    if (megasas_frame_is_ieee_sgl(cmd)) {
+        next += sizeof(struct mfi_sg_skinny);
+    } else if (megasas_frame_is_sgl64(cmd)) {
+        next += sizeof(struct mfi_sg64);
+    } else {
+        next += sizeof(struct mfi_sg32);
+    }
+
+    if (next >= (uint8_t *)cmd->frame + cmd->pa_size) {
+        return NULL;
+    }
+    return (union mfi_sgl *)next;
+}
+
+static void megasas_soft_reset(MegasasState *s);
+
+static int megasas_map_sgl(MegasasState *s, MegasasCmd *cmd, union mfi_sgl *sgl)
+{
+    int i;
+    int iov_count = 0;
+    size_t iov_size = 0;
+
+    cmd->flags = le16_to_cpu(cmd->frame->header.flags);
+    iov_count = cmd->frame->header.sge_count;
+    if (iov_count > MEGASAS_MAX_SGE) {
+        trace_megasas_iovec_sgl_overflow(cmd->index, iov_count,
+                                         MEGASAS_MAX_SGE);
+        return iov_count;
+    }
+    qemu_sglist_init(&cmd->qsg, iov_count, pci_dma_context(&s->dev));
+    for (i = 0; i < iov_count; i++) {
+        dma_addr_t iov_pa, iov_size_p;
+
+        if (!sgl) {
+            trace_megasas_iovec_sgl_underflow(cmd->index, i);
+            goto unmap;
+        }
+        iov_pa = megasas_sgl_get_addr(cmd, sgl);
+        iov_size_p = megasas_sgl_get_len(cmd, sgl);
+        if (!iov_pa || !iov_size_p) {
+            trace_megasas_iovec_sgl_invalid(cmd->index, i,
+                                            iov_pa, iov_size_p);
+            goto unmap;
+        }
+        qemu_sglist_add(&cmd->qsg, iov_pa, iov_size_p);
+        sgl = megasas_sgl_next(cmd, sgl);
+        iov_size += (size_t)iov_size_p;
+    }
+    if (cmd->iov_size > iov_size) {
+        trace_megasas_iovec_overflow(cmd->index, iov_size, cmd->iov_size);
+    } else if (cmd->iov_size < iov_size) {
+        trace_megasas_iovec_underflow(cmd->iov_size, iov_size, cmd->iov_size);
+    }
+    cmd->iov_offset = 0;
+    return 0;
+unmap:
+    qemu_sglist_destroy(&cmd->qsg);
+    return iov_count - i;
+}
+
+static void megasas_unmap_sgl(MegasasCmd *cmd)
+{
+    qemu_sglist_destroy(&cmd->qsg);
+    cmd->iov_offset = 0;
+}
+
+/*
+ * passthrough sense and io sense are at the same offset
+ */
+static int megasas_build_sense(MegasasCmd *cmd, uint8_t *sense_ptr,
+    uint8_t sense_len)
+{
+    uint32_t pa_hi = 0, pa_lo;
+    hwaddr pa;
+
+    if (sense_len > cmd->frame->header.sense_len) {
+        sense_len = cmd->frame->header.sense_len;
+    }
+    if (sense_len) {
+        pa_lo = le32_to_cpu(cmd->frame->pass.sense_addr_lo);
+        if (megasas_frame_is_sense64(cmd)) {
+            pa_hi = le32_to_cpu(cmd->frame->pass.sense_addr_hi);
+        }
+        pa = ((uint64_t) pa_hi << 32) | pa_lo;
+        cpu_physical_memory_write(pa, sense_ptr, sense_len);
+        cmd->frame->header.sense_len = sense_len;
+    }
+    return sense_len;
+}
+
+static void megasas_write_sense(MegasasCmd *cmd, SCSISense sense)
+{
+    uint8_t sense_buf[SCSI_SENSE_BUF_SIZE];
+    uint8_t sense_len = 18;
+
+    memset(sense_buf, 0, sense_len);
+    sense_buf[0] = 0xf0;
+    sense_buf[2] = sense.key;
+    sense_buf[7] = 10;
+    sense_buf[12] = sense.asc;
+    sense_buf[13] = sense.ascq;
+    megasas_build_sense(cmd, sense_buf, sense_len);
+}
+
+static void megasas_copy_sense(MegasasCmd *cmd)
+{
+    uint8_t sense_buf[SCSI_SENSE_BUF_SIZE];
+    uint8_t sense_len;
+
+    sense_len = scsi_req_get_sense(cmd->req, sense_buf,
+                                   SCSI_SENSE_BUF_SIZE);
+    megasas_build_sense(cmd, sense_buf, sense_len);
+}
+
+/*
+ * Format an INQUIRY CDB
+ */
+static int megasas_setup_inquiry(uint8_t *cdb, int pg, int len)
+{
+    memset(cdb, 0, 6);
+    cdb[0] = INQUIRY;
+    if (pg > 0) {
+        cdb[1] = 0x1;
+        cdb[2] = pg;
+    }
+    cdb[3] = (len >> 8) & 0xff;
+    cdb[4] = (len & 0xff);
+    return len;
+}
+
+/*
+ * Encode lba and len into a READ_16/WRITE_16 CDB
+ */
+static void megasas_encode_lba(uint8_t *cdb, uint64_t lba,
+                               uint32_t len, bool is_write)
+{
+    memset(cdb, 0x0, 16);
+    if (is_write) {
+        cdb[0] = WRITE_16;
+    } else {
+        cdb[0] = READ_16;
+    }
+    cdb[2] = (lba >> 56) & 0xff;
+    cdb[3] = (lba >> 48) & 0xff;
+    cdb[4] = (lba >> 40) & 0xff;
+    cdb[5] = (lba >> 32) & 0xff;
+    cdb[6] = (lba >> 24) & 0xff;
+    cdb[7] = (lba >> 16) & 0xff;
+    cdb[8] = (lba >> 8) & 0xff;
+    cdb[9] = (lba) & 0xff;
+    cdb[10] = (len >> 24) & 0xff;
+    cdb[11] = (len >> 16) & 0xff;
+    cdb[12] = (len >> 8) & 0xff;
+    cdb[13] = (len) & 0xff;
+}
+
+/*
+ * Utility functions
+ */
+static uint64_t megasas_fw_time(void)
+{
+    struct tm curtime;
+    uint64_t bcd_time;
+
+    qemu_get_timedate(&curtime, 0);
+    bcd_time = ((uint64_t)curtime.tm_sec & 0xff) << 48 |
+        ((uint64_t)curtime.tm_min & 0xff)  << 40 |
+        ((uint64_t)curtime.tm_hour & 0xff) << 32 |
+        ((uint64_t)curtime.tm_mday & 0xff) << 24 |
+        ((uint64_t)curtime.tm_mon & 0xff)  << 16 |
+        ((uint64_t)(curtime.tm_year + 1900) & 0xffff);
+
+    return bcd_time;
+}
+
+/*
+ * Default disk sata address
+ * 0x1221 is the magic number as
+ * present in real hardware,
+ * so use it here, too.
+ */
+static uint64_t megasas_get_sata_addr(uint16_t id)
+{
+    uint64_t addr = (0x1221ULL << 48);
+    return addr & (id << 24);
+}
+
+/*
+ * Frame handling
+ */
+static int megasas_next_index(MegasasState *s, int index, int limit)
+{
+    index++;
+    if (index == limit) {
+        index = 0;
+    }
+    return index;
+}
+
+static MegasasCmd *megasas_lookup_frame(MegasasState *s,
+    hwaddr frame)
+{
+    MegasasCmd *cmd = NULL;
+    int num = 0, index;
+
+    index = s->reply_queue_head;
+
+    while (num < s->fw_cmds) {
+        if (s->frames[index].pa && s->frames[index].pa == frame) {
+            cmd = &s->frames[index];
+            break;
+        }
+        index = megasas_next_index(s, index, s->fw_cmds);
+        num++;
+    }
+
+    return cmd;
+}
+
+static MegasasCmd *megasas_next_frame(MegasasState *s,
+    hwaddr frame)
+{
+    MegasasCmd *cmd = NULL;
+    int num = 0, index;
+
+    cmd = megasas_lookup_frame(s, frame);
+    if (cmd) {
+        trace_megasas_qf_found(cmd->index, cmd->pa);
+        return cmd;
+    }
+    index = s->reply_queue_head;
+    num = 0;
+    while (num < s->fw_cmds) {
+        if (!s->frames[index].pa) {
+            cmd = &s->frames[index];
+            break;
+        }
+        index = megasas_next_index(s, index, s->fw_cmds);
+        num++;
+    }
+    if (!cmd) {
+        trace_megasas_qf_failed(frame);
+    }
+    trace_megasas_qf_new(index, cmd);
+    return cmd;
+}
+
+static MegasasCmd *megasas_enqueue_frame(MegasasState *s,
+    hwaddr frame, uint64_t context, int count)
+{
+    MegasasCmd *cmd = NULL;
+    int frame_size = MFI_FRAME_SIZE * 16;
+    hwaddr frame_size_p = frame_size;
+
+    cmd = megasas_next_frame(s, frame);
+    /* All frames busy */
+    if (!cmd) {
+        return NULL;
+    }
+    if (!cmd->pa) {
+        cmd->pa = frame;
+        /* Map all possible frames */
+        cmd->frame = cpu_physical_memory_map(frame, &frame_size_p, 0);
+        if (frame_size_p != frame_size) {
+            trace_megasas_qf_map_failed(cmd->index, (unsigned long)frame);
+            if (cmd->frame) {
+                cpu_physical_memory_unmap(cmd->frame, frame_size_p, 0, 0);
+                cmd->frame = NULL;
+                cmd->pa = 0;
+            }
+            s->event_count++;
+            return NULL;
+        }
+        cmd->pa_size = frame_size_p;
+        cmd->context = context;
+        if (!megasas_use_queue64(s)) {
+            cmd->context &= (uint64_t)0xFFFFFFFF;
+        }
+    }
+    cmd->count = count;
+    s->busy++;
+
+    trace_megasas_qf_enqueue(cmd->index, cmd->count, cmd->context,
+                             s->reply_queue_head, s->busy);
+
+    return cmd;
+}
+
+static void megasas_complete_frame(MegasasState *s, uint64_t context)
+{
+    int tail, queue_offset;
+
+    /* Decrement busy count */
+    s->busy--;
+
+    if (s->reply_queue_pa) {
+        /*
+         * Put command on the reply queue.
+         * Context is opaque, but emulation is running in
+         * little endian. So convert it.
+         */
+        tail = s->reply_queue_head;
+        if (megasas_use_queue64(s)) {
+            queue_offset = tail * sizeof(uint64_t);
+            stq_le_phys(s->reply_queue_pa + queue_offset, context);
+        } else {
+            queue_offset = tail * sizeof(uint32_t);
+            stl_le_phys(s->reply_queue_pa + queue_offset, context);
+        }
+        s->reply_queue_head = megasas_next_index(s, tail, s->fw_cmds);
+        trace_megasas_qf_complete(context, tail, queue_offset,
+                                  s->busy, s->doorbell);
+    }
+
+    if (megasas_intr_enabled(s)) {
+        /* Notify HBA */
+        s->doorbell++;
+        if (s->doorbell == 1) {
+            if (msix_enabled(&s->dev)) {
+                trace_megasas_msix_raise(0);
+                msix_notify(&s->dev, 0);
+            } else {
+                trace_megasas_irq_raise();
+                qemu_irq_raise(s->dev.irq[0]);
+            }
+        }
+    } else {
+        trace_megasas_qf_complete_noirq(context);
+    }
+}
+
+static void megasas_reset_frames(MegasasState *s)
+{
+    int i;
+    MegasasCmd *cmd;
+
+    for (i = 0; i < s->fw_cmds; i++) {
+        cmd = &s->frames[i];
+        if (cmd->pa) {
+            cpu_physical_memory_unmap(cmd->frame, cmd->pa_size, 0, 0);
+            cmd->frame = NULL;
+            cmd->pa = 0;
+        }
+    }
+}
+
+static void megasas_abort_command(MegasasCmd *cmd)
+{
+    if (cmd->req) {
+        scsi_req_cancel(cmd->req);
+        cmd->req = NULL;
+    }
+}
+
+static int megasas_init_firmware(MegasasState *s, MegasasCmd *cmd)
+{
+    uint32_t pa_hi, pa_lo;
+    hwaddr iq_pa, initq_size;
+    struct mfi_init_qinfo *initq;
+    uint32_t flags;
+    int ret = MFI_STAT_OK;
+
+    pa_lo = le32_to_cpu(cmd->frame->init.qinfo_new_addr_lo);
+    pa_hi = le32_to_cpu(cmd->frame->init.qinfo_new_addr_hi);
+    iq_pa = (((uint64_t) pa_hi << 32) | pa_lo);
+    trace_megasas_init_firmware((uint64_t)iq_pa);
+    initq_size = sizeof(*initq);
+    initq = cpu_physical_memory_map(iq_pa, &initq_size, 0);
+    if (!initq || initq_size != sizeof(*initq)) {
+        trace_megasas_initq_map_failed(cmd->index);
+        s->event_count++;
+        ret = MFI_STAT_MEMORY_NOT_AVAILABLE;
+        goto out;
+    }
+    s->reply_queue_len = le32_to_cpu(initq->rq_entries) & 0xFFFF;
+    if (s->reply_queue_len > s->fw_cmds) {
+        trace_megasas_initq_mismatch(s->reply_queue_len, s->fw_cmds);
+        s->event_count++;
+        ret = MFI_STAT_INVALID_PARAMETER;
+        goto out;
+    }
+    pa_lo = le32_to_cpu(initq->rq_addr_lo);
+    pa_hi = le32_to_cpu(initq->rq_addr_hi);
+    s->reply_queue_pa = ((uint64_t) pa_hi << 32) | pa_lo;
+    pa_lo = le32_to_cpu(initq->ci_addr_lo);
+    pa_hi = le32_to_cpu(initq->ci_addr_hi);
+    s->consumer_pa = ((uint64_t) pa_hi << 32) | pa_lo;
+    pa_lo = le32_to_cpu(initq->pi_addr_lo);
+    pa_hi = le32_to_cpu(initq->pi_addr_hi);
+    s->producer_pa = ((uint64_t) pa_hi << 32) | pa_lo;
+    s->reply_queue_head = ldl_le_phys(s->producer_pa);
+    s->reply_queue_tail = ldl_le_phys(s->consumer_pa);
+    flags = le32_to_cpu(initq->flags);
+    if (flags & MFI_QUEUE_FLAG_CONTEXT64) {
+        s->flags |= MEGASAS_MASK_USE_QUEUE64;
+    }
+    trace_megasas_init_queue((unsigned long)s->reply_queue_pa,
+                             s->reply_queue_len, s->reply_queue_head,
+                             s->reply_queue_tail, flags);
+    megasas_reset_frames(s);
+    s->fw_state = MFI_FWSTATE_OPERATIONAL;
+out:
+    if (initq) {
+        cpu_physical_memory_unmap(initq, initq_size, 0, 0);
+    }
+    return ret;
+}
+
+static int megasas_map_dcmd(MegasasState *s, MegasasCmd *cmd)
+{
+    dma_addr_t iov_pa, iov_size;
+
+    cmd->flags = le16_to_cpu(cmd->frame->header.flags);
+    if (!cmd->frame->header.sge_count) {
+        trace_megasas_dcmd_zero_sge(cmd->index);
+        cmd->iov_size = 0;
+        return 0;
+    } else if (cmd->frame->header.sge_count > 1) {
+        trace_megasas_dcmd_invalid_sge(cmd->index,
+                                       cmd->frame->header.sge_count);
+        cmd->iov_size = 0;
+        return -1;
+    }
+    iov_pa = megasas_sgl_get_addr(cmd, &cmd->frame->dcmd.sgl);
+    iov_size = megasas_sgl_get_len(cmd, &cmd->frame->dcmd.sgl);
+    qemu_sglist_init(&cmd->qsg, 1, pci_dma_context(&s->dev));
+    qemu_sglist_add(&cmd->qsg, iov_pa, iov_size);
+    cmd->iov_size = iov_size;
+    return cmd->iov_size;
+}
+
+static void megasas_finish_dcmd(MegasasCmd *cmd, uint32_t iov_size)
+{
+    trace_megasas_finish_dcmd(cmd->index, iov_size);
+
+    if (cmd->frame->header.sge_count) {
+        qemu_sglist_destroy(&cmd->qsg);
+    }
+    if (iov_size > cmd->iov_size) {
+        if (megasas_frame_is_ieee_sgl(cmd)) {
+            cmd->frame->dcmd.sgl.sg_skinny->len = cpu_to_le32(iov_size);
+        } else if (megasas_frame_is_sgl64(cmd)) {
+            cmd->frame->dcmd.sgl.sg64->len = cpu_to_le32(iov_size);
+        } else {
+            cmd->frame->dcmd.sgl.sg32->len = cpu_to_le32(iov_size);
+        }
+    }
+    cmd->iov_size = 0;
+}
+
+static int megasas_ctrl_get_info(MegasasState *s, MegasasCmd *cmd)
+{
+    struct mfi_ctrl_info info;
+    size_t dcmd_size = sizeof(info);
+    BusChild *kid;
+    int num_ld_disks = 0;
+    uint16_t sdev_id;
+
+    memset(&info, 0x0, cmd->iov_size);
+    if (cmd->iov_size < dcmd_size) {
+        trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
+                                            dcmd_size);
+        return MFI_STAT_INVALID_PARAMETER;
+    }
+
+    info.pci.vendor = cpu_to_le16(PCI_VENDOR_ID_LSI_LOGIC);
+    info.pci.device = cpu_to_le16(PCI_DEVICE_ID_LSI_SAS1078);
+    info.pci.subvendor = cpu_to_le16(PCI_VENDOR_ID_LSI_LOGIC);
+    info.pci.subdevice = cpu_to_le16(0x1013);
+
+    /*
+     * For some reason the firmware supports
+     * only up to 8 device ports.
+     * Despite supporting a far larger number
+     * of devices for the physical devices.
+     * So just display the first 8 devices
+     * in the device port list, independent
+     * of how many logical devices are actually
+     * present.
+     */
+    info.host.type = MFI_INFO_HOST_PCIE;
+    info.device.type = MFI_INFO_DEV_SAS3G;
+    info.device.port_count = 8;
+    QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
+        SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+
+        if (num_ld_disks < 8) {
+            sdev_id = ((sdev->id & 0xFF) >> 8) | (sdev->lun & 0xFF);
+            info.device.port_addr[num_ld_disks] =
+                cpu_to_le64(megasas_get_sata_addr(sdev_id));
+        }
+        num_ld_disks++;
+    }
+
+    memcpy(info.product_name, "MegaRAID SAS 8708EM2", 20);
+    snprintf(info.serial_number, 32, "%s", s->hba_serial);
+    snprintf(info.package_version, 0x60, "%s-QEMU", QEMU_VERSION);
+    memcpy(info.image_component[0].name, "APP", 3);
+    memcpy(info.image_component[0].version, MEGASAS_VERSION "-QEMU", 9);
+    memcpy(info.image_component[0].build_date, __DATE__, 11);
+    memcpy(info.image_component[0].build_time, __TIME__, 8);
+    info.image_component_count = 1;
+    if (s->dev.has_rom) {
+        uint8_t biosver[32];
+        uint8_t *ptr;
+
+        ptr = memory_region_get_ram_ptr(&s->dev.rom);
+        memcpy(biosver, ptr + 0x41, 31);
+        qemu_put_ram_ptr(ptr);
+        memcpy(info.image_component[1].name, "BIOS", 4);
+        memcpy(info.image_component[1].version, biosver,
+               strlen((const char *)biosver));
+        info.image_component_count++;
+    }
+    info.current_fw_time = cpu_to_le32(megasas_fw_time());
+    info.max_arms = 32;
+    info.max_spans = 8;
+    info.max_arrays = MEGASAS_MAX_ARRAYS;
+    info.max_lds = s->fw_luns;
+    info.max_cmds = cpu_to_le16(s->fw_cmds);
+    info.max_sg_elements = cpu_to_le16(s->fw_sge);
+    info.max_request_size = cpu_to_le32(MEGASAS_MAX_SECTORS);
+    info.lds_present = cpu_to_le16(num_ld_disks);
+    info.pd_present = cpu_to_le16(num_ld_disks);
+    info.pd_disks_present = cpu_to_le16(num_ld_disks);
+    info.hw_present = cpu_to_le32(MFI_INFO_HW_NVRAM |
+                                   MFI_INFO_HW_MEM |
+                                   MFI_INFO_HW_FLASH);
+    info.memory_size = cpu_to_le16(512);
+    info.nvram_size = cpu_to_le16(32);
+    info.flash_size = cpu_to_le16(16);
+    info.raid_levels = cpu_to_le32(MFI_INFO_RAID_0);
+    info.adapter_ops = cpu_to_le32(MFI_INFO_AOPS_RBLD_RATE |
+                                    MFI_INFO_AOPS_SELF_DIAGNOSTIC |
+                                    MFI_INFO_AOPS_MIXED_ARRAY);
+    info.ld_ops = cpu_to_le32(MFI_INFO_LDOPS_DISK_CACHE_POLICY |
+                               MFI_INFO_LDOPS_ACCESS_POLICY |
+                               MFI_INFO_LDOPS_IO_POLICY |
+                               MFI_INFO_LDOPS_WRITE_POLICY |
+                               MFI_INFO_LDOPS_READ_POLICY);
+    info.max_strips_per_io = cpu_to_le16(s->fw_sge);
+    info.stripe_sz_ops.min = 3;
+    info.stripe_sz_ops.max = ffs(MEGASAS_MAX_SECTORS + 1) - 1;
+    info.properties.pred_fail_poll_interval = cpu_to_le16(300);
+    info.properties.intr_throttle_cnt = cpu_to_le16(16);
+    info.properties.intr_throttle_timeout = cpu_to_le16(50);
+    info.properties.rebuild_rate = 30;
+    info.properties.patrol_read_rate = 30;
+    info.properties.bgi_rate = 30;
+    info.properties.cc_rate = 30;
+    info.properties.recon_rate = 30;
+    info.properties.cache_flush_interval = 4;
+    info.properties.spinup_drv_cnt = 2;
+    info.properties.spinup_delay = 6;
+    info.properties.ecc_bucket_size = 15;
+    info.properties.ecc_bucket_leak_rate = cpu_to_le16(1440);
+    info.properties.expose_encl_devices = 1;
+    info.properties.OnOffProperties = cpu_to_le32(MFI_CTRL_PROP_EnableJBOD);
+    info.pd_ops = cpu_to_le32(MFI_INFO_PDOPS_FORCE_ONLINE |
+                               MFI_INFO_PDOPS_FORCE_OFFLINE);
+    info.pd_mix_support = cpu_to_le32(MFI_INFO_PDMIX_SAS |
+                                       MFI_INFO_PDMIX_SATA |
+                                       MFI_INFO_PDMIX_LD);
+
+    cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg);
+    return MFI_STAT_OK;
+}
+
+static int megasas_mfc_get_defaults(MegasasState *s, MegasasCmd *cmd)
+{
+    struct mfi_defaults info;
+    size_t dcmd_size = sizeof(struct mfi_defaults);
+
+    memset(&info, 0x0, dcmd_size);
+    if (cmd->iov_size < dcmd_size) {
+        trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
+                                            dcmd_size);
+        return MFI_STAT_INVALID_PARAMETER;
+    }
+
+    info.sas_addr = cpu_to_le64(s->sas_addr);
+    info.stripe_size = 3;
+    info.flush_time = 4;
+    info.background_rate = 30;
+    info.allow_mix_in_enclosure = 1;
+    info.allow_mix_in_ld = 1;
+    info.direct_pd_mapping = 1;
+    /* Enable for BIOS support */
+    info.bios_enumerate_lds = 1;
+    info.disable_ctrl_r = 1;
+    info.expose_enclosure_devices = 1;
+    info.disable_preboot_cli = 1;
+    info.cluster_disable = 1;
+
+    cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg);
+    return MFI_STAT_OK;
+}
+
+static int megasas_dcmd_get_bios_info(MegasasState *s, MegasasCmd *cmd)
+{
+    struct mfi_bios_data info;
+    size_t dcmd_size = sizeof(info);
+
+    memset(&info, 0x0, dcmd_size);
+    if (cmd->iov_size < dcmd_size) {
+        trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
+                                            dcmd_size);
+        return MFI_STAT_INVALID_PARAMETER;
+    }
+    info.continue_on_error = 1;
+    info.verbose = 1;
+    if (megasas_is_jbod(s)) {
+        info.expose_all_drives = 1;
+    }
+
+    cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg);
+    return MFI_STAT_OK;
+}
+
+static int megasas_dcmd_get_fw_time(MegasasState *s, MegasasCmd *cmd)
+{
+    uint64_t fw_time;
+    size_t dcmd_size = sizeof(fw_time);
+
+    fw_time = cpu_to_le64(megasas_fw_time());
+
+    cmd->iov_size -= dma_buf_read((uint8_t *)&fw_time, dcmd_size, &cmd->qsg);
+    return MFI_STAT_OK;
+}
+
+static int megasas_dcmd_set_fw_time(MegasasState *s, MegasasCmd *cmd)
+{
+    uint64_t fw_time;
+
+    /* This is a dummy; setting of firmware time is not allowed */
+    memcpy(&fw_time, cmd->frame->dcmd.mbox, sizeof(fw_time));
+
+    trace_megasas_dcmd_set_fw_time(cmd->index, fw_time);
+    fw_time = cpu_to_le64(megasas_fw_time());
+    return MFI_STAT_OK;
+}
+
+static int megasas_event_info(MegasasState *s, MegasasCmd *cmd)
+{
+    struct mfi_evt_log_state info;
+    size_t dcmd_size = sizeof(info);
+
+    memset(&info, 0, dcmd_size);
+
+    info.newest_seq_num = cpu_to_le32(s->event_count);
+    info.shutdown_seq_num = cpu_to_le32(s->shutdown_event);
+    info.boot_seq_num = cpu_to_le32(s->boot_event);
+
+    cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg);
+    return MFI_STAT_OK;
+}
+
+static int megasas_event_wait(MegasasState *s, MegasasCmd *cmd)
+{
+    union mfi_evt event;
+
+    if (cmd->iov_size < sizeof(struct mfi_evt_detail)) {
+        trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
+                                            sizeof(struct mfi_evt_detail));
+        return MFI_STAT_INVALID_PARAMETER;
+    }
+    s->event_count = cpu_to_le32(cmd->frame->dcmd.mbox[0]);
+    event.word = cpu_to_le32(cmd->frame->dcmd.mbox[4]);
+    s->event_locale = event.members.locale;
+    s->event_class = event.members.class;
+    s->event_cmd = cmd;
+    /* Decrease busy count; event frame doesn't count here */
+    s->busy--;
+    cmd->iov_size = sizeof(struct mfi_evt_detail);
+    return MFI_STAT_INVALID_STATUS;
+}
+
+static int megasas_dcmd_pd_get_list(MegasasState *s, MegasasCmd *cmd)
+{
+    struct mfi_pd_list info;
+    size_t dcmd_size = sizeof(info);
+    BusChild *kid;
+    uint32_t offset, dcmd_limit, num_pd_disks = 0, max_pd_disks;
+    uint16_t sdev_id;
+
+    memset(&info, 0, dcmd_size);
+    offset = 8;
+    dcmd_limit = offset + sizeof(struct mfi_pd_address);
+    if (cmd->iov_size < dcmd_limit) {
+        trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
+                                            dcmd_limit);
+        return MFI_STAT_INVALID_PARAMETER;
+    }
+
+    max_pd_disks = (cmd->iov_size - offset) / sizeof(struct mfi_pd_address);
+    if (max_pd_disks > s->fw_luns) {
+        max_pd_disks = s->fw_luns;
+    }
+
+    QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
+        SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+
+        sdev_id = ((sdev->id & 0xFF) >> 8) | (sdev->lun & 0xFF);
+        info.addr[num_pd_disks].device_id = cpu_to_le16(sdev_id);
+        info.addr[num_pd_disks].encl_device_id = 0xFFFF;
+        info.addr[num_pd_disks].encl_index = 0;
+        info.addr[num_pd_disks].slot_number = (sdev->id & 0xFF);
+        info.addr[num_pd_disks].scsi_dev_type = sdev->type;
+        info.addr[num_pd_disks].connect_port_bitmap = 0x1;
+        info.addr[num_pd_disks].sas_addr[0] =
+            cpu_to_le64(megasas_get_sata_addr(sdev_id));
+        num_pd_disks++;
+        offset += sizeof(struct mfi_pd_address);
+    }
+    trace_megasas_dcmd_pd_get_list(cmd->index, num_pd_disks,
+                                   max_pd_disks, offset);
+
+    info.size = cpu_to_le32(offset);
+    info.count = cpu_to_le32(num_pd_disks);
+
+    cmd->iov_size -= dma_buf_read((uint8_t *)&info, offset, &cmd->qsg);
+    return MFI_STAT_OK;
+}
+
+static int megasas_dcmd_pd_list_query(MegasasState *s, MegasasCmd *cmd)
+{
+    uint16_t flags;
+
+    /* mbox0 contains flags */
+    flags = le16_to_cpu(cmd->frame->dcmd.mbox[0]);
+    trace_megasas_dcmd_pd_list_query(cmd->index, flags);
+    if (flags == MR_PD_QUERY_TYPE_ALL ||
+        megasas_is_jbod(s)) {
+        return megasas_dcmd_pd_get_list(s, cmd);
+    }
+
+    return MFI_STAT_OK;
+}
+
+static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun,
+                                      MegasasCmd *cmd)
+{
+    struct mfi_pd_info *info = cmd->iov_buf;
+    size_t dcmd_size = sizeof(struct mfi_pd_info);
+    BlockConf *conf = &sdev->conf;
+    uint64_t pd_size;
+    uint16_t sdev_id = ((sdev->id & 0xFF) >> 8) | (lun & 0xFF);
+    uint8_t cmdbuf[6];
+    SCSIRequest *req;
+    size_t len, resid;
+
+    if (!cmd->iov_buf) {
+        cmd->iov_buf = g_malloc(dcmd_size);
+        memset(cmd->iov_buf, 0, dcmd_size);
+        info = cmd->iov_buf;
+        info->inquiry_data[0] = 0x7f; /* Force PQual 0x3, PType 0x1f */
+        info->vpd_page83[0] = 0x7f;
+        megasas_setup_inquiry(cmdbuf, 0, sizeof(info->inquiry_data));
+        req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
+        if (!req) {
+            trace_megasas_dcmd_req_alloc_failed(cmd->index,
+                                                "PD get info std inquiry");
+            g_free(cmd->iov_buf);
+            cmd->iov_buf = NULL;
+            return MFI_STAT_FLASH_ALLOC_FAIL;
+        }
+        trace_megasas_dcmd_internal_submit(cmd->index,
+                                           "PD get info std inquiry", lun);
+        len = scsi_req_enqueue(req);
+        if (len > 0) {
+            cmd->iov_size = len;
+            scsi_req_continue(req);
+        }
+        return MFI_STAT_INVALID_STATUS;
+    } else if (info->inquiry_data[0] != 0x7f && info->vpd_page83[0] == 0x7f) {
+        megasas_setup_inquiry(cmdbuf, 0x83, sizeof(info->vpd_page83));
+        req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd);
+        if (!req) {
+            trace_megasas_dcmd_req_alloc_failed(cmd->index,
+                                                "PD get info vpd inquiry");
+            return MFI_STAT_FLASH_ALLOC_FAIL;
+        }
+        trace_megasas_dcmd_internal_submit(cmd->index,
+                                           "PD get info vpd inquiry", lun);
+        len = scsi_req_enqueue(req);
+        if (len > 0) {
+            cmd->iov_size = len;
+            scsi_req_continue(req);
+        }
+        return MFI_STAT_INVALID_STATUS;
+    }
+    /* Finished, set FW state */
+    if ((info->inquiry_data[0] >> 5) == 0) {
+        if (megasas_is_jbod(cmd->state)) {
+            info->fw_state = cpu_to_le16(MFI_PD_STATE_SYSTEM);
+        } else {
+            info->fw_state = cpu_to_le16(MFI_PD_STATE_ONLINE);
+        }
+    } else {
+        info->fw_state = cpu_to_le16(MFI_PD_STATE_OFFLINE);
+    }
+
+    info->ref.v.device_id = cpu_to_le16(sdev_id);
+    info->state.ddf.pd_type = cpu_to_le16(MFI_PD_DDF_TYPE_IN_VD|
+                                          MFI_PD_DDF_TYPE_INTF_SAS);
+    bdrv_get_geometry(conf->bs, &pd_size);
+    info->raw_size = cpu_to_le64(pd_size);
+    info->non_coerced_size = cpu_to_le64(pd_size);
+    info->coerced_size = cpu_to_le64(pd_size);
+    info->encl_device_id = 0xFFFF;
+    info->slot_number = (sdev->id & 0xFF);
+    info->path_info.count = 1;
+    info->path_info.sas_addr[0] =
+        cpu_to_le64(megasas_get_sata_addr(sdev_id));
+    info->connected_port_bitmap = 0x1;
+    info->device_speed = 1;
+    info->link_speed = 1;
+    resid = dma_buf_read(cmd->iov_buf, dcmd_size, &cmd->qsg);
+    g_free(cmd->iov_buf);
+    cmd->iov_size = dcmd_size - resid;
+    cmd->iov_buf = NULL;
+    return MFI_STAT_OK;
+}
+
+static int megasas_dcmd_pd_get_info(MegasasState *s, MegasasCmd *cmd)
+{
+    size_t dcmd_size = sizeof(struct mfi_pd_info);
+    uint16_t pd_id;
+    SCSIDevice *sdev = NULL;
+    int retval = MFI_STAT_DEVICE_NOT_FOUND;
+
+    if (cmd->iov_size < dcmd_size) {
+        return MFI_STAT_INVALID_PARAMETER;
+    }
+
+    /* mbox0 has the ID */
+    pd_id = le16_to_cpu(cmd->frame->dcmd.mbox[0]);
+    sdev = scsi_device_find(&s->bus, 0, pd_id, 0);
+    trace_megasas_dcmd_pd_get_info(cmd->index, pd_id);
+
+    if (sdev) {
+        /* Submit inquiry */
+        retval = megasas_pd_get_info_submit(sdev, pd_id, cmd);
+    }
+
+    return retval;
+}
+
+static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd)
+{
+    struct mfi_ld_list info;
+    size_t dcmd_size = sizeof(info), resid;
+    uint32_t num_ld_disks = 0, max_ld_disks = s->fw_luns;
+    uint64_t ld_size;
+    BusChild *kid;
+
+    memset(&info, 0, dcmd_size);
+    if (cmd->iov_size < dcmd_size) {
+        trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
+                                            dcmd_size);
+        return MFI_STAT_INVALID_PARAMETER;
+    }
+
+    if (megasas_is_jbod(s)) {
+        max_ld_disks = 0;
+    }
+    QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
+        SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+        BlockConf *conf = &sdev->conf;
+
+        if (num_ld_disks >= max_ld_disks) {
+            break;
+        }
+        /* Logical device size is in blocks */
+        bdrv_get_geometry(conf->bs, &ld_size);
+        info.ld_list[num_ld_disks].ld.v.target_id = sdev->id;
+        info.ld_list[num_ld_disks].ld.v.lun_id = sdev->lun;
+        info.ld_list[num_ld_disks].state = MFI_LD_STATE_OPTIMAL;
+        info.ld_list[num_ld_disks].size = cpu_to_le64(ld_size);
+        num_ld_disks++;
+    }
+    info.ld_count = cpu_to_le32(num_ld_disks);
+    trace_megasas_dcmd_ld_get_list(cmd->index, num_ld_disks, max_ld_disks);
+
+    resid = dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg);
+    cmd->iov_size = dcmd_size - resid;
+    return MFI_STAT_OK;
+}
+
+static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
+                                      MegasasCmd *cmd)
+{
+    struct mfi_ld_info *info = cmd->iov_buf;
+    size_t dcmd_size = sizeof(struct mfi_ld_info);
+    uint8_t cdb[6];
+    SCSIRequest *req;
+    ssize_t len, resid;
+    BlockConf *conf = &sdev->conf;
+    uint16_t sdev_id = ((sdev->id & 0xFF) >> 8) | (lun & 0xFF);
+    uint64_t ld_size;
+
+    if (!cmd->iov_buf) {
+        cmd->iov_buf = g_malloc(dcmd_size);
+        memset(cmd->iov_buf, 0x0, dcmd_size);
+        info = cmd->iov_buf;
+        megasas_setup_inquiry(cdb, 0x83, sizeof(info->vpd_page83));
+        req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd);
+        if (!req) {
+            trace_megasas_dcmd_req_alloc_failed(cmd->index,
+                                                "LD get info vpd inquiry");
+            g_free(cmd->iov_buf);
+            cmd->iov_buf = NULL;
+            return MFI_STAT_FLASH_ALLOC_FAIL;
+        }
+        trace_megasas_dcmd_internal_submit(cmd->index,
+                                           "LD get info vpd inquiry", lun);
+        len = scsi_req_enqueue(req);
+        if (len > 0) {
+            cmd->iov_size = len;
+            scsi_req_continue(req);
+        }
+        return MFI_STAT_INVALID_STATUS;
+    }
+
+    info->ld_config.params.state = MFI_LD_STATE_OPTIMAL;
+    info->ld_config.properties.ld.v.target_id = lun;
+    info->ld_config.params.stripe_size = 3;
+    info->ld_config.params.num_drives = 1;
+    info->ld_config.params.is_consistent = 1;
+    /* Logical device size is in blocks */
+    bdrv_get_geometry(conf->bs, &ld_size);
+    info->size = cpu_to_le64(ld_size);
+    memset(info->ld_config.span, 0, sizeof(info->ld_config.span));
+    info->ld_config.span[0].start_block = 0;
+    info->ld_config.span[0].num_blocks = info->size;
+    info->ld_config.span[0].array_ref = cpu_to_le16(sdev_id);
+
+    resid = dma_buf_read(cmd->iov_buf, dcmd_size, &cmd->qsg);
+    g_free(cmd->iov_buf);
+    cmd->iov_size = dcmd_size - resid;
+    cmd->iov_buf = NULL;
+    return MFI_STAT_OK;
+}
+
+static int megasas_dcmd_ld_get_info(MegasasState *s, MegasasCmd *cmd)
+{
+    struct mfi_ld_info info;
+    size_t dcmd_size = sizeof(info);
+    uint16_t ld_id;
+    uint32_t max_ld_disks = s->fw_luns;
+    SCSIDevice *sdev = NULL;
+    int retval = MFI_STAT_DEVICE_NOT_FOUND;
+
+    if (cmd->iov_size < dcmd_size) {
+        return MFI_STAT_INVALID_PARAMETER;
+    }
+
+    /* mbox0 has the ID */
+    ld_id = le16_to_cpu(cmd->frame->dcmd.mbox[0]);
+    trace_megasas_dcmd_ld_get_info(cmd->index, ld_id);
+
+    if (megasas_is_jbod(s)) {
+        return MFI_STAT_DEVICE_NOT_FOUND;
+    }
+
+    if (ld_id < max_ld_disks) {
+        sdev = scsi_device_find(&s->bus, 0, ld_id, 0);
+    }
+
+    if (sdev) {
+        retval = megasas_ld_get_info_submit(sdev, ld_id, cmd);
+    }
+
+    return retval;
+}
+
+static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd)
+{
+    uint8_t data[4096];
+    struct mfi_config_data *info;
+    int num_pd_disks = 0, array_offset, ld_offset;
+    BusChild *kid;
+
+    if (cmd->iov_size > 4096) {
+        return MFI_STAT_INVALID_PARAMETER;
+    }
+
+    QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
+        num_pd_disks++;
+    }
+    info = (struct mfi_config_data *)&data;
+    /*
+     * Array mapping:
+     * - One array per SCSI device
+     * - One logical drive per SCSI device
+     *   spanning the entire device
+     */
+    info->array_count = num_pd_disks;
+    info->array_size = sizeof(struct mfi_array) * num_pd_disks;
+    info->log_drv_count = num_pd_disks;
+    info->log_drv_size = sizeof(struct mfi_ld_config) * num_pd_disks;
+    info->spares_count = 0;
+    info->spares_size = sizeof(struct mfi_spare);
+    info->size = sizeof(struct mfi_config_data) + info->array_size +
+        info->log_drv_size;
+    if (info->size > 4096) {
+        return MFI_STAT_INVALID_PARAMETER;
+    }
+
+    array_offset = sizeof(struct mfi_config_data);
+    ld_offset = array_offset + sizeof(struct mfi_array) * num_pd_disks;
+
+    QTAILQ_FOREACH(kid, &s->bus.qbus.children, sibling) {
+        SCSIDevice *sdev = DO_UPCAST(SCSIDevice, qdev, kid->child);
+        BlockConf *conf = &sdev->conf;
+        uint16_t sdev_id = ((sdev->id & 0xFF) >> 8) | (sdev->lun & 0xFF);
+        struct mfi_array *array;
+        struct mfi_ld_config *ld;
+        uint64_t pd_size;
+        int i;
+
+        array = (struct mfi_array *)(data + array_offset);
+        bdrv_get_geometry(conf->bs, &pd_size);
+        array->size = cpu_to_le64(pd_size);
+        array->num_drives = 1;
+        array->array_ref = cpu_to_le16(sdev_id);
+        array->pd[0].ref.v.device_id = cpu_to_le16(sdev_id);
+        array->pd[0].ref.v.seq_num = 0;
+        array->pd[0].fw_state = MFI_PD_STATE_ONLINE;
+        array->pd[0].encl.pd = 0xFF;
+        array->pd[0].encl.slot = (sdev->id & 0xFF);
+        for (i = 1; i < MFI_MAX_ROW_SIZE; i++) {
+            array->pd[i].ref.v.device_id = 0xFFFF;
+            array->pd[i].ref.v.seq_num = 0;
+            array->pd[i].fw_state = MFI_PD_STATE_UNCONFIGURED_GOOD;
+            array->pd[i].encl.pd = 0xFF;
+            array->pd[i].encl.slot = 0xFF;
+        }
+        array_offset += sizeof(struct mfi_array);
+        ld = (struct mfi_ld_config *)(data + ld_offset);
+        memset(ld, 0, sizeof(struct mfi_ld_config));
+        ld->properties.ld.v.target_id = (sdev->id & 0xFF);
+        ld->properties.default_cache_policy = MR_LD_CACHE_READ_AHEAD |
+            MR_LD_CACHE_READ_ADAPTIVE;
+        ld->properties.current_cache_policy = MR_LD_CACHE_READ_AHEAD |
+            MR_LD_CACHE_READ_ADAPTIVE;
+        ld->params.state = MFI_LD_STATE_OPTIMAL;
+        ld->params.stripe_size = 3;
+        ld->params.num_drives = 1;
+        ld->params.span_depth = 1;
+        ld->params.is_consistent = 1;
+        ld->span[0].start_block = 0;
+        ld->span[0].num_blocks = cpu_to_le64(pd_size);
+        ld->span[0].array_ref = cpu_to_le16(sdev_id);
+        ld_offset += sizeof(struct mfi_ld_config);
+    }
+
+    cmd->iov_size -= dma_buf_read((uint8_t *)data, info->size, &cmd->qsg);
+    return MFI_STAT_OK;
+}
+
+static int megasas_dcmd_get_properties(MegasasState *s, MegasasCmd *cmd)
+{
+    struct mfi_ctrl_props info;
+    size_t dcmd_size = sizeof(info);
+
+    memset(&info, 0x0, dcmd_size);
+    if (cmd->iov_size < dcmd_size) {
+        trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
+                                            dcmd_size);
+        return MFI_STAT_INVALID_PARAMETER;
+    }
+    info.pred_fail_poll_interval = cpu_to_le16(300);
+    info.intr_throttle_cnt = cpu_to_le16(16);
+    info.intr_throttle_timeout = cpu_to_le16(50);
+    info.rebuild_rate = 30;
+    info.patrol_read_rate = 30;
+    info.bgi_rate = 30;
+    info.cc_rate = 30;
+    info.recon_rate = 30;
+    info.cache_flush_interval = 4;
+    info.spinup_drv_cnt = 2;
+    info.spinup_delay = 6;
+    info.ecc_bucket_size = 15;
+    info.ecc_bucket_leak_rate = cpu_to_le16(1440);
+    info.expose_encl_devices = 1;
+
+    cmd->iov_size -= dma_buf_read((uint8_t *)&info, dcmd_size, &cmd->qsg);
+    return MFI_STAT_OK;
+}
+
+static int megasas_cache_flush(MegasasState *s, MegasasCmd *cmd)
+{
+    bdrv_drain_all();
+    return MFI_STAT_OK;
+}
+
+static int megasas_ctrl_shutdown(MegasasState *s, MegasasCmd *cmd)
+{
+    s->fw_state = MFI_FWSTATE_READY;
+    return MFI_STAT_OK;
+}
+
+static int megasas_cluster_reset_ld(MegasasState *s, MegasasCmd *cmd)
+{
+    return MFI_STAT_INVALID_DCMD;
+}
+
+static int megasas_dcmd_set_properties(MegasasState *s, MegasasCmd *cmd)
+{
+    struct mfi_ctrl_props info;
+    size_t dcmd_size = sizeof(info);
+
+    if (cmd->iov_size < dcmd_size) {
+        trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size,
+                                            dcmd_size);
+        return MFI_STAT_INVALID_PARAMETER;
+    }
+    dma_buf_write((uint8_t *)&info, cmd->iov_size, &cmd->qsg);
+    trace_megasas_dcmd_unsupported(cmd->index, cmd->iov_size);
+    return MFI_STAT_OK;
+}
+
+static int megasas_dcmd_dummy(MegasasState *s, MegasasCmd *cmd)
+{
+    trace_megasas_dcmd_dummy(cmd->index, cmd->iov_size);
+    return MFI_STAT_OK;
+}
+
+static const struct dcmd_cmd_tbl_t {
+    int opcode;
+    const char *desc;
+    int (*func)(MegasasState *s, MegasasCmd *cmd);
+} dcmd_cmd_tbl[] = {
+    { MFI_DCMD_CTRL_MFI_HOST_MEM_ALLOC, "CTRL_HOST_MEM_ALLOC",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_CTRL_GET_INFO, "CTRL_GET_INFO",
+      megasas_ctrl_get_info },
+    { MFI_DCMD_CTRL_GET_PROPERTIES, "CTRL_GET_PROPERTIES",
+      megasas_dcmd_get_properties },
+    { MFI_DCMD_CTRL_SET_PROPERTIES, "CTRL_SET_PROPERTIES",
+      megasas_dcmd_set_properties },
+    { MFI_DCMD_CTRL_ALARM_GET, "CTRL_ALARM_GET",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_CTRL_ALARM_ENABLE, "CTRL_ALARM_ENABLE",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_CTRL_ALARM_DISABLE, "CTRL_ALARM_DISABLE",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_CTRL_ALARM_SILENCE, "CTRL_ALARM_SILENCE",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_CTRL_ALARM_TEST, "CTRL_ALARM_TEST",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_CTRL_EVENT_GETINFO, "CTRL_EVENT_GETINFO",
+      megasas_event_info },
+    { MFI_DCMD_CTRL_EVENT_GET, "CTRL_EVENT_GET",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_CTRL_EVENT_WAIT, "CTRL_EVENT_WAIT",
+      megasas_event_wait },
+    { MFI_DCMD_CTRL_SHUTDOWN, "CTRL_SHUTDOWN",
+      megasas_ctrl_shutdown },
+    { MFI_DCMD_HIBERNATE_STANDBY, "CTRL_STANDBY",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_CTRL_GET_TIME, "CTRL_GET_TIME",
+      megasas_dcmd_get_fw_time },
+    { MFI_DCMD_CTRL_SET_TIME, "CTRL_SET_TIME",
+      megasas_dcmd_set_fw_time },
+    { MFI_DCMD_CTRL_BIOS_DATA_GET, "CTRL_BIOS_DATA_GET",
+      megasas_dcmd_get_bios_info },
+    { MFI_DCMD_CTRL_FACTORY_DEFAULTS, "CTRL_FACTORY_DEFAULTS",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_CTRL_MFC_DEFAULTS_GET, "CTRL_MFC_DEFAULTS_GET",
+      megasas_mfc_get_defaults },
+    { MFI_DCMD_CTRL_MFC_DEFAULTS_SET, "CTRL_MFC_DEFAULTS_SET",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_CTRL_CACHE_FLUSH, "CTRL_CACHE_FLUSH",
+      megasas_cache_flush },
+    { MFI_DCMD_PD_GET_LIST, "PD_GET_LIST",
+      megasas_dcmd_pd_get_list },
+    { MFI_DCMD_PD_LIST_QUERY, "PD_LIST_QUERY",
+      megasas_dcmd_pd_list_query },
+    { MFI_DCMD_PD_GET_INFO, "PD_GET_INFO",
+      megasas_dcmd_pd_get_info },
+    { MFI_DCMD_PD_STATE_SET, "PD_STATE_SET",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_PD_REBUILD, "PD_REBUILD",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_PD_BLINK, "PD_BLINK",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_PD_UNBLINK, "PD_UNBLINK",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_LD_GET_LIST, "LD_GET_LIST",
+      megasas_dcmd_ld_get_list},
+    { MFI_DCMD_LD_GET_INFO, "LD_GET_INFO",
+      megasas_dcmd_ld_get_info },
+    { MFI_DCMD_LD_GET_PROP, "LD_GET_PROP",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_LD_SET_PROP, "LD_SET_PROP",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_LD_DELETE, "LD_DELETE",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_CFG_READ, "CFG_READ",
+      megasas_dcmd_cfg_read },
+    { MFI_DCMD_CFG_ADD, "CFG_ADD",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_CFG_CLEAR, "CFG_CLEAR",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_CFG_FOREIGN_READ, "CFG_FOREIGN_READ",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_CFG_FOREIGN_IMPORT, "CFG_FOREIGN_IMPORT",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_BBU_STATUS, "BBU_STATUS",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_BBU_CAPACITY_INFO, "BBU_CAPACITY_INFO",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_BBU_DESIGN_INFO, "BBU_DESIGN_INFO",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_BBU_PROP_GET, "BBU_PROP_GET",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_CLUSTER, "CLUSTER",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_CLUSTER_RESET_ALL, "CLUSTER_RESET_ALL",
+      megasas_dcmd_dummy },
+    { MFI_DCMD_CLUSTER_RESET_LD, "CLUSTER_RESET_LD",
+      megasas_cluster_reset_ld },
+    { -1, NULL, NULL }
+};
+
+static int megasas_handle_dcmd(MegasasState *s, MegasasCmd *cmd)
+{
+    int opcode, len;
+    int retval = 0;
+    const struct dcmd_cmd_tbl_t *cmdptr = dcmd_cmd_tbl;
+
+    opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
+    trace_megasas_handle_dcmd(cmd->index, opcode);
+    len = megasas_map_dcmd(s, cmd);
+    if (len < 0) {
+        return MFI_STAT_MEMORY_NOT_AVAILABLE;
+    }
+    while (cmdptr->opcode != -1 && cmdptr->opcode != opcode) {
+        cmdptr++;
+    }
+    if (cmdptr->opcode == -1) {
+        trace_megasas_dcmd_unhandled(cmd->index, opcode, len);
+        retval = megasas_dcmd_dummy(s, cmd);
+    } else {
+        trace_megasas_dcmd_enter(cmd->index, cmdptr->desc, len);
+        retval = cmdptr->func(s, cmd);
+    }
+    if (retval != MFI_STAT_INVALID_STATUS) {
+        megasas_finish_dcmd(cmd, len);
+    }
+    return retval;
+}
+
+static int megasas_finish_internal_dcmd(MegasasCmd *cmd,
+                                        SCSIRequest *req)
+{
+    int opcode;
+    int retval = MFI_STAT_OK;
+    int lun = req->lun;
+
+    opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
+    scsi_req_unref(req);
+    trace_megasas_dcmd_internal_finish(cmd->index, opcode, lun);
+    switch (opcode) {
+    case MFI_DCMD_PD_GET_INFO:
+        retval = megasas_pd_get_info_submit(req->dev, lun, cmd);
+        break;
+    case MFI_DCMD_LD_GET_INFO:
+        retval = megasas_ld_get_info_submit(req->dev, lun, cmd);
+        break;
+    default:
+        trace_megasas_dcmd_internal_invalid(cmd->index, opcode);
+        retval = MFI_STAT_INVALID_DCMD;
+        break;
+    }
+    if (retval != MFI_STAT_INVALID_STATUS) {
+        megasas_finish_dcmd(cmd, cmd->iov_size);
+    }
+    return retval;
+}
+
+static int megasas_enqueue_req(MegasasCmd *cmd, bool is_write)
+{
+    int len;
+
+    len = scsi_req_enqueue(cmd->req);
+    if (len < 0) {
+        len = -len;
+    }
+    if (len > 0) {
+        if (len > cmd->iov_size) {
+            if (is_write) {
+                trace_megasas_iov_write_overflow(cmd->index, len,
+                                                 cmd->iov_size);
+            } else {
+                trace_megasas_iov_read_overflow(cmd->index, len,
+                                                cmd->iov_size);
+            }
+        }
+        if (len < cmd->iov_size) {
+            if (is_write) {
+                trace_megasas_iov_write_underflow(cmd->index, len,
+                                                  cmd->iov_size);
+            } else {
+                trace_megasas_iov_read_underflow(cmd->index, len,
+                                                 cmd->iov_size);
+            }
+            cmd->iov_size = len;
+        }
+        scsi_req_continue(cmd->req);
+    }
+    return len;
+}
+
+static int megasas_handle_scsi(MegasasState *s, MegasasCmd *cmd,
+                               bool is_logical)
+{
+    uint8_t *cdb;
+    int len;
+    bool is_write;
+    struct SCSIDevice *sdev = NULL;
+
+    cdb = cmd->frame->pass.cdb;
+
+    if (cmd->frame->header.target_id < s->fw_luns) {
+        sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id,
+                                cmd->frame->header.lun_id);
+    }
+    cmd->iov_size = le32_to_cpu(cmd->frame->header.data_len);
+    trace_megasas_handle_scsi(mfi_frame_desc[cmd->frame->header.frame_cmd],
+                              is_logical, cmd->frame->header.target_id,
+                              cmd->frame->header.lun_id, sdev, cmd->iov_size);
+
+    if (!sdev || (megasas_is_jbod(s) && is_logical)) {
+        trace_megasas_scsi_target_not_present(
+            mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical,
+            cmd->frame->header.target_id, cmd->frame->header.lun_id);
+        return MFI_STAT_DEVICE_NOT_FOUND;
+    }
+
+    if (cmd->frame->header.cdb_len > 16) {
+        trace_megasas_scsi_invalid_cdb_len(
+                mfi_frame_desc[cmd->frame->header.frame_cmd], is_logical,
+                cmd->frame->header.target_id, cmd->frame->header.lun_id,
+                cmd->frame->header.cdb_len);
+        megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE));
+        cmd->frame->header.scsi_status = CHECK_CONDITION;
+        s->event_count++;
+        return MFI_STAT_SCSI_DONE_WITH_ERROR;
+    }
+
+    if (megasas_map_sgl(s, cmd, &cmd->frame->pass.sgl)) {
+        megasas_write_sense(cmd, SENSE_CODE(TARGET_FAILURE));
+        cmd->frame->header.scsi_status = CHECK_CONDITION;
+        s->event_count++;
+        return MFI_STAT_SCSI_DONE_WITH_ERROR;
+    }
+
+    cmd->req = scsi_req_new(sdev, cmd->index,
+                            cmd->frame->header.lun_id, cdb, cmd);
+    if (!cmd->req) {
+        trace_megasas_scsi_req_alloc_failed(
+                mfi_frame_desc[cmd->frame->header.frame_cmd],
+                cmd->frame->header.target_id, cmd->frame->header.lun_id);
+        megasas_write_sense(cmd, SENSE_CODE(NO_SENSE));
+        cmd->frame->header.scsi_status = BUSY;
+        s->event_count++;
+        return MFI_STAT_SCSI_DONE_WITH_ERROR;
+    }
+
+    is_write = (cmd->req->cmd.mode == SCSI_XFER_TO_DEV);
+    len = megasas_enqueue_req(cmd, is_write);
+    if (len > 0) {
+        if (is_write) {
+            trace_megasas_scsi_write_start(cmd->index, len);
+        } else {
+            trace_megasas_scsi_read_start(cmd->index, len);
+        }
+    } else {
+        trace_megasas_scsi_nodata(cmd->index);
+    }
+    return MFI_STAT_INVALID_STATUS;
+}
+
+static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd)
+{
+    uint32_t lba_count, lba_start_hi, lba_start_lo;
+    uint64_t lba_start;
+    bool is_write = (cmd->frame->header.frame_cmd == MFI_CMD_LD_WRITE);
+    uint8_t cdb[16];
+    int len;
+    struct SCSIDevice *sdev = NULL;
+
+    lba_count = le32_to_cpu(cmd->frame->io.header.data_len);
+    lba_start_lo = le32_to_cpu(cmd->frame->io.lba_lo);
+    lba_start_hi = le32_to_cpu(cmd->frame->io.lba_hi);
+    lba_start = ((uint64_t)lba_start_hi << 32) | lba_start_lo;
+
+    if (cmd->frame->header.target_id < s->fw_luns) {
+        sdev = scsi_device_find(&s->bus, 0, cmd->frame->header.target_id,
+                                cmd->frame->header.lun_id);
+    }
+
+    trace_megasas_handle_io(cmd->index,
+                            mfi_frame_desc[cmd->frame->header.frame_cmd],
+                            cmd->frame->header.target_id,
+                            cmd->frame->header.lun_id,
+                            (unsigned long)lba_start, (unsigned long)lba_count);
+    if (!sdev) {
+        trace_megasas_io_target_not_present(cmd->index,
+            mfi_frame_desc[cmd->frame->header.frame_cmd],
+            cmd->frame->header.target_id, cmd->frame->header.lun_id);
+        return MFI_STAT_DEVICE_NOT_FOUND;
+    }
+
+    if (cmd->frame->header.cdb_len > 16) {
+        trace_megasas_scsi_invalid_cdb_len(
+            mfi_frame_desc[cmd->frame->header.frame_cmd], 1,
+            cmd->frame->header.target_id, cmd->frame->header.lun_id,
+            cmd->frame->header.cdb_len);
+        megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE));
+        cmd->frame->header.scsi_status = CHECK_CONDITION;
+        s->event_count++;
+        return MFI_STAT_SCSI_DONE_WITH_ERROR;
+    }
+
+    cmd->iov_size = lba_count * sdev->blocksize;
+    if (megasas_map_sgl(s, cmd, &cmd->frame->io.sgl)) {
+        megasas_write_sense(cmd, SENSE_CODE(TARGET_FAILURE));
+        cmd->frame->header.scsi_status = CHECK_CONDITION;
+        s->event_count++;
+        return MFI_STAT_SCSI_DONE_WITH_ERROR;
+    }
+
+    megasas_encode_lba(cdb, lba_start, lba_count, is_write);
+    cmd->req = scsi_req_new(sdev, cmd->index,
+                            cmd->frame->header.lun_id, cdb, cmd);
+    if (!cmd->req) {
+        trace_megasas_scsi_req_alloc_failed(
+            mfi_frame_desc[cmd->frame->header.frame_cmd],
+            cmd->frame->header.target_id, cmd->frame->header.lun_id);
+        megasas_write_sense(cmd, SENSE_CODE(NO_SENSE));
+        cmd->frame->header.scsi_status = BUSY;
+        s->event_count++;
+        return MFI_STAT_SCSI_DONE_WITH_ERROR;
+    }
+    len = megasas_enqueue_req(cmd, is_write);
+    if (len > 0) {
+        if (is_write) {
+            trace_megasas_io_write_start(cmd->index, lba_start, lba_count, len);
+        } else {
+            trace_megasas_io_read_start(cmd->index, lba_start, lba_count, len);
+        }
+    }
+    return MFI_STAT_INVALID_STATUS;
+}
+
+static int megasas_finish_internal_command(MegasasCmd *cmd,
+                                           SCSIRequest *req, size_t resid)
+{
+    int retval = MFI_STAT_INVALID_CMD;
+
+    if (cmd->frame->header.frame_cmd == MFI_CMD_DCMD) {
+        cmd->iov_size -= resid;
+        retval = megasas_finish_internal_dcmd(cmd, req);
+    }
+    return retval;
+}
+
+static QEMUSGList *megasas_get_sg_list(SCSIRequest *req)
+{
+    MegasasCmd *cmd = req->hba_private;
+
+    if (cmd->frame->header.frame_cmd == MFI_CMD_DCMD) {
+        return NULL;
+    } else {
+        return &cmd->qsg;
+    }
+}
+
+static void megasas_xfer_complete(SCSIRequest *req, uint32_t len)
+{
+    MegasasCmd *cmd = req->hba_private;
+    uint8_t *buf;
+    uint32_t opcode;
+
+    trace_megasas_io_complete(cmd->index, len);
+
+    if (cmd->frame->header.frame_cmd != MFI_CMD_DCMD) {
+        scsi_req_continue(req);
+        return;
+    }
+
+    buf = scsi_req_get_buf(req);
+    opcode = le32_to_cpu(cmd->frame->dcmd.opcode);
+    if (opcode == MFI_DCMD_PD_GET_INFO && cmd->iov_buf) {
+        struct mfi_pd_info *info = cmd->iov_buf;
+
+        if (info->inquiry_data[0] == 0x7f) {
+            memset(info->inquiry_data, 0, sizeof(info->inquiry_data));
+            memcpy(info->inquiry_data, buf, len);
+        } else if (info->vpd_page83[0] == 0x7f) {
+            memset(info->vpd_page83, 0, sizeof(info->vpd_page83));
+            memcpy(info->vpd_page83, buf, len);
+        }
+        scsi_req_continue(req);
+    } else if (opcode == MFI_DCMD_LD_GET_INFO) {
+        struct mfi_ld_info *info = cmd->iov_buf;
+
+        if (cmd->iov_buf) {
+            memcpy(info->vpd_page83, buf, sizeof(info->vpd_page83));
+            scsi_req_continue(req);
+        }
+    }
+}
+
+static void megasas_command_complete(SCSIRequest *req, uint32_t status,
+                                     size_t resid)
+{
+    MegasasCmd *cmd = req->hba_private;
+    uint8_t cmd_status = MFI_STAT_OK;
+
+    trace_megasas_command_complete(cmd->index, status, resid);
+
+    if (cmd->req != req) {
+        /*
+         * Internal command complete
+         */
+        cmd_status = megasas_finish_internal_command(cmd, req, resid);
+        if (cmd_status == MFI_STAT_INVALID_STATUS) {
+            return;
+        }
+    } else {
+        req->status = status;
+        trace_megasas_scsi_complete(cmd->index, req->status,
+                                    cmd->iov_size, req->cmd.xfer);
+        if (req->status != GOOD) {
+            cmd_status = MFI_STAT_SCSI_DONE_WITH_ERROR;
+        }
+        if (req->status == CHECK_CONDITION) {
+            megasas_copy_sense(cmd);
+        }
+
+        megasas_unmap_sgl(cmd);
+        cmd->frame->header.scsi_status = req->status;
+        scsi_req_unref(cmd->req);
+        cmd->req = NULL;
+    }
+    cmd->frame->header.cmd_status = cmd_status;
+    megasas_complete_frame(cmd->state, cmd->context);
+}
+
+static void megasas_command_cancel(SCSIRequest *req)
+{
+    MegasasCmd *cmd = req->hba_private;
+
+    if (cmd) {
+        megasas_abort_command(cmd);
+    } else {
+        scsi_req_unref(req);
+    }
+}
+
+static int megasas_handle_abort(MegasasState *s, MegasasCmd *cmd)
+{
+    uint64_t abort_ctx = le64_to_cpu(cmd->frame->abort.abort_context);
+    hwaddr abort_addr, addr_hi, addr_lo;
+    MegasasCmd *abort_cmd;
+
+    addr_hi = le32_to_cpu(cmd->frame->abort.abort_mfi_addr_hi);
+    addr_lo = le32_to_cpu(cmd->frame->abort.abort_mfi_addr_lo);
+    abort_addr = ((uint64_t)addr_hi << 32) | addr_lo;
+
+    abort_cmd = megasas_lookup_frame(s, abort_addr);
+    if (!abort_cmd) {
+        trace_megasas_abort_no_cmd(cmd->index, abort_ctx);
+        s->event_count++;
+        return MFI_STAT_OK;
+    }
+    if (!megasas_use_queue64(s)) {
+        abort_ctx &= (uint64_t)0xFFFFFFFF;
+    }
+    if (abort_cmd->context != abort_ctx) {
+        trace_megasas_abort_invalid_context(cmd->index, abort_cmd->index,
+                                            abort_cmd->context);
+        s->event_count++;
+        return MFI_STAT_ABORT_NOT_POSSIBLE;
+    }
+    trace_megasas_abort_frame(cmd->index, abort_cmd->index);
+    megasas_abort_command(abort_cmd);
+    if (!s->event_cmd || abort_cmd != s->event_cmd) {
+        s->event_cmd = NULL;
+    }
+    s->event_count++;
+    return MFI_STAT_OK;
+}
+
+static void megasas_handle_frame(MegasasState *s, uint64_t frame_addr,
+                                 uint32_t frame_count)
+{
+    uint8_t frame_status = MFI_STAT_INVALID_CMD;
+    uint64_t frame_context;
+    MegasasCmd *cmd;
+
+    /*
+     * Always read 64bit context, top bits will be
+     * masked out if required in megasas_enqueue_frame()
+     */
+    frame_context = megasas_frame_get_context(frame_addr);
+
+    cmd = megasas_enqueue_frame(s, frame_addr, frame_context, frame_count);
+    if (!cmd) {
+        /* reply queue full */
+        trace_megasas_frame_busy(frame_addr);
+        megasas_frame_set_scsi_status(frame_addr, BUSY);
+        megasas_frame_set_cmd_status(frame_addr, MFI_STAT_SCSI_DONE_WITH_ERROR);
+        megasas_complete_frame(s, frame_context);
+        s->event_count++;
+        return;
+    }
+    switch (cmd->frame->header.frame_cmd) {
+    case MFI_CMD_INIT:
+        frame_status = megasas_init_firmware(s, cmd);
+        break;
+    case MFI_CMD_DCMD:
+        frame_status = megasas_handle_dcmd(s, cmd);
+        break;
+    case MFI_CMD_ABORT:
+        frame_status = megasas_handle_abort(s, cmd);
+        break;
+    case MFI_CMD_PD_SCSI_IO:
+        frame_status = megasas_handle_scsi(s, cmd, 0);
+        break;
+    case MFI_CMD_LD_SCSI_IO:
+        frame_status = megasas_handle_scsi(s, cmd, 1);
+        break;
+    case MFI_CMD_LD_READ:
+    case MFI_CMD_LD_WRITE:
+        frame_status = megasas_handle_io(s, cmd);
+        break;
+    default:
+        trace_megasas_unhandled_frame_cmd(cmd->index,
+                                          cmd->frame->header.frame_cmd);
+        s->event_count++;
+        break;
+    }
+    if (frame_status != MFI_STAT_INVALID_STATUS) {
+        if (cmd->frame) {
+            cmd->frame->header.cmd_status = frame_status;
+        } else {
+            megasas_frame_set_cmd_status(frame_addr, frame_status);
+        }
+        megasas_complete_frame(s, cmd->context);
+    }
+}
+
+static uint64_t megasas_mmio_read(void *opaque, hwaddr addr,
+                                  unsigned size)
+{
+    MegasasState *s = opaque;
+    uint32_t retval = 0;
+
+    switch (addr) {
+    case MFI_IDB:
+        retval = 0;
+        break;
+    case MFI_OMSG0:
+    case MFI_OSP0:
+        retval = (megasas_use_msix(s) ? MFI_FWSTATE_MSIX_SUPPORTED : 0) |
+            (s->fw_state & MFI_FWSTATE_MASK) |
+            ((s->fw_sge & 0xff) << 16) |
+            (s->fw_cmds & 0xFFFF);
+        break;
+    case MFI_OSTS:
+        if (megasas_intr_enabled(s) && s->doorbell) {
+            retval = MFI_1078_RM | 1;
+        }
+        break;
+    case MFI_OMSK:
+        retval = s->intr_mask;
+        break;
+    case MFI_ODCR0:
+        retval = s->doorbell;
+        break;
+    default:
+        trace_megasas_mmio_invalid_readl(addr);
+        break;
+    }
+    trace_megasas_mmio_readl(addr, retval);
+    return retval;
+}
+
+static void megasas_mmio_write(void *opaque, hwaddr addr,
+                               uint64_t val, unsigned size)
+{
+    MegasasState *s = opaque;
+    uint64_t frame_addr;
+    uint32_t frame_count;
+    int i;
+
+    trace_megasas_mmio_writel(addr, val);
+    switch (addr) {
+    case MFI_IDB:
+        if (val & MFI_FWINIT_ABORT) {
+            /* Abort all pending cmds */
+            for (i = 0; i < s->fw_cmds; i++) {
+                megasas_abort_command(&s->frames[i]);
+            }
+        }
+        if (val & MFI_FWINIT_READY) {
+            /* move to FW READY */
+            megasas_soft_reset(s);
+        }
+        if (val & MFI_FWINIT_MFIMODE) {
+            /* discard MFIs */
+        }
+        break;
+    case MFI_OMSK:
+        s->intr_mask = val;
+        if (!megasas_intr_enabled(s) && !msix_enabled(&s->dev)) {
+            trace_megasas_irq_lower();
+            qemu_irq_lower(s->dev.irq[0]);
+        }
+        if (megasas_intr_enabled(s)) {
+            trace_megasas_intr_enabled();
+        } else {
+            trace_megasas_intr_disabled();
+        }
+        break;
+    case MFI_ODCR0:
+        s->doorbell = 0;
+        if (s->producer_pa && megasas_intr_enabled(s)) {
+            /* Update reply queue pointer */
+            trace_megasas_qf_update(s->reply_queue_head, s->busy);
+            stl_le_phys(s->producer_pa, s->reply_queue_head);
+            if (!msix_enabled(&s->dev)) {
+                trace_megasas_irq_lower();
+                qemu_irq_lower(s->dev.irq[0]);
+            }
+        }
+        break;
+    case MFI_IQPH:
+        /* Received high 32 bits of a 64 bit MFI frame address */
+        s->frame_hi = val;
+        break;
+    case MFI_IQPL:
+        /* Received low 32 bits of a 64 bit MFI frame address */
+    case MFI_IQP:
+        /* Received 32 bit MFI frame address */
+        frame_addr = (val & ~0x1F);
+        /* Add possible 64 bit offset */
+        frame_addr |= ((uint64_t)s->frame_hi << 32);
+        s->frame_hi = 0;
+        frame_count = (val >> 1) & 0xF;
+        megasas_handle_frame(s, frame_addr, frame_count);
+        break;
+    default:
+        trace_megasas_mmio_invalid_writel(addr, val);
+        break;
+    }
+}
+
+static const MemoryRegionOps megasas_mmio_ops = {
+    .read = megasas_mmio_read,
+    .write = megasas_mmio_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 8,
+        .max_access_size = 8,
+    }
+};
+
+static uint64_t megasas_port_read(void *opaque, hwaddr addr,
+                                  unsigned size)
+{
+    return megasas_mmio_read(opaque, addr & 0xff, size);
+}
+
+static void megasas_port_write(void *opaque, hwaddr addr,
+                               uint64_t val, unsigned size)
+{
+    megasas_mmio_write(opaque, addr & 0xff, val, size);
+}
+
+static const MemoryRegionOps megasas_port_ops = {
+    .read = megasas_port_read,
+    .write = megasas_port_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    }
+};
+
+static uint64_t megasas_queue_read(void *opaque, hwaddr addr,
+                                   unsigned size)
+{
+    return 0;
+}
+
+static const MemoryRegionOps megasas_queue_ops = {
+    .read = megasas_queue_read,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 8,
+        .max_access_size = 8,
+    }
+};
+
+static void megasas_soft_reset(MegasasState *s)
+{
+    int i;
+    MegasasCmd *cmd;
+
+    trace_megasas_reset();
+    for (i = 0; i < s->fw_cmds; i++) {
+        cmd = &s->frames[i];
+        megasas_abort_command(cmd);
+    }
+    megasas_reset_frames(s);
+    s->reply_queue_len = s->fw_cmds;
+    s->reply_queue_pa = 0;
+    s->consumer_pa = 0;
+    s->producer_pa = 0;
+    s->fw_state = MFI_FWSTATE_READY;
+    s->doorbell = 0;
+    s->intr_mask = MEGASAS_INTR_DISABLED_MASK;
+    s->frame_hi = 0;
+    s->flags &= ~MEGASAS_MASK_USE_QUEUE64;
+    s->event_count++;
+    s->boot_event = s->event_count;
+}
+
+static void megasas_scsi_reset(DeviceState *dev)
+{
+    MegasasState *s = DO_UPCAST(MegasasState, dev.qdev, dev);
+
+    megasas_soft_reset(s);
+}
+
+static const VMStateDescription vmstate_megasas = {
+    .name = "megasas",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields      = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(dev, MegasasState),
+
+        VMSTATE_INT32(fw_state, MegasasState),
+        VMSTATE_INT32(intr_mask, MegasasState),
+        VMSTATE_INT32(doorbell, MegasasState),
+        VMSTATE_UINT64(reply_queue_pa, MegasasState),
+        VMSTATE_UINT64(consumer_pa, MegasasState),
+        VMSTATE_UINT64(producer_pa, MegasasState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void megasas_scsi_uninit(PCIDevice *d)
+{
+    MegasasState *s = DO_UPCAST(MegasasState, dev, d);
+
+#ifdef USE_MSIX
+    msix_uninit(&s->dev, &s->mmio_io);
+#endif
+    memory_region_destroy(&s->mmio_io);
+    memory_region_destroy(&s->port_io);
+    memory_region_destroy(&s->queue_io);
+}
+
+static const struct SCSIBusInfo megasas_scsi_info = {
+    .tcq = true,
+    .max_target = MFI_MAX_LD,
+    .max_lun = 255,
+
+    .transfer_data = megasas_xfer_complete,
+    .get_sg_list = megasas_get_sg_list,
+    .complete = megasas_command_complete,
+    .cancel = megasas_command_cancel,
+};
+
+static int megasas_scsi_init(PCIDevice *dev)
+{
+    MegasasState *s = DO_UPCAST(MegasasState, dev, dev);
+    uint8_t *pci_conf;
+    int i, bar_type;
+
+    pci_conf = s->dev.config;
+
+    /* PCI latency timer = 0 */
+    pci_conf[PCI_LATENCY_TIMER] = 0;
+    /* Interrupt pin 1 */
+    pci_conf[PCI_INTERRUPT_PIN] = 0x01;
+
+    memory_region_init_io(&s->mmio_io, &megasas_mmio_ops, s,
+                          "megasas-mmio", 0x4000);
+    memory_region_init_io(&s->port_io, &megasas_port_ops, s,
+                          "megasas-io", 256);
+    memory_region_init_io(&s->queue_io, &megasas_queue_ops, s,
+                          "megasas-queue", 0x40000);
+
+#ifdef USE_MSIX
+    /* MSI-X support is currently broken */
+    if (megasas_use_msix(s) &&
+        msix_init(&s->dev, 15, &s->mmio_io, 0, 0x2000)) {
+        s->flags &= ~MEGASAS_MASK_USE_MSIX;
+    }
+#else
+    s->flags &= ~MEGASAS_MASK_USE_MSIX;
+#endif
+
+    bar_type = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64;
+    pci_register_bar(&s->dev, 0, bar_type, &s->mmio_io);
+    pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_IO, &s->port_io);
+    pci_register_bar(&s->dev, 3, bar_type, &s->queue_io);
+
+    if (megasas_use_msix(s)) {
+        msix_vector_use(&s->dev, 0);
+    }
+
+    if (!s->sas_addr) {
+        s->sas_addr = ((NAA_LOCALLY_ASSIGNED_ID << 24) |
+                       IEEE_COMPANY_LOCALLY_ASSIGNED) << 36;
+        s->sas_addr |= (pci_bus_num(dev->bus) << 16);
+        s->sas_addr |= (PCI_SLOT(dev->devfn) << 8);
+        s->sas_addr |= PCI_FUNC(dev->devfn);
+    }
+    if (!s->hba_serial) {
+       s->hba_serial = g_strdup(MEGASAS_HBA_SERIAL);
+    }
+    if (s->fw_sge >= MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE) {
+        s->fw_sge = MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE;
+    } else if (s->fw_sge >= 128 - MFI_PASS_FRAME_SIZE) {
+        s->fw_sge = 128 - MFI_PASS_FRAME_SIZE;
+    } else {
+        s->fw_sge = 64 - MFI_PASS_FRAME_SIZE;
+    }
+    if (s->fw_cmds > MEGASAS_MAX_FRAMES) {
+        s->fw_cmds = MEGASAS_MAX_FRAMES;
+    }
+    trace_megasas_init(s->fw_sge, s->fw_cmds,
+                       megasas_use_msix(s) ? "MSI-X" : "INTx",
+                       megasas_is_jbod(s) ? "jbod" : "raid");
+    s->fw_luns = (MFI_MAX_LD > MAX_SCSI_DEVS) ?
+        MAX_SCSI_DEVS : MFI_MAX_LD;
+    s->producer_pa = 0;
+    s->consumer_pa = 0;
+    for (i = 0; i < s->fw_cmds; i++) {
+        s->frames[i].index = i;
+        s->frames[i].context = -1;
+        s->frames[i].pa = 0;
+        s->frames[i].state = s;
+    }
+
+    scsi_bus_new(&s->bus, &dev->qdev, &megasas_scsi_info);
+    scsi_bus_legacy_handle_cmdline(&s->bus);
+    return 0;
+}
+
+static Property megasas_properties[] = {
+    DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge,
+                       MEGASAS_DEFAULT_SGE),
+    DEFINE_PROP_UINT32("max_cmds", MegasasState, fw_cmds,
+                       MEGASAS_DEFAULT_FRAMES),
+    DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial),
+    DEFINE_PROP_HEX64("sas_address", MegasasState, sas_addr, 0),
+#ifdef USE_MSIX
+    DEFINE_PROP_BIT("use_msix", MegasasState, flags,
+                    MEGASAS_FLAG_USE_MSIX, false),
+#endif
+    DEFINE_PROP_BIT("use_jbod", MegasasState, flags,
+                    MEGASAS_FLAG_USE_JBOD, false),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void megasas_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
+
+    pc->init = megasas_scsi_init;
+    pc->exit = megasas_scsi_uninit;
+    pc->vendor_id = PCI_VENDOR_ID_LSI_LOGIC;
+    pc->device_id = PCI_DEVICE_ID_LSI_SAS1078;
+    pc->subsystem_vendor_id = PCI_VENDOR_ID_LSI_LOGIC;
+    pc->subsystem_id = 0x1013;
+    pc->class_id = PCI_CLASS_STORAGE_RAID;
+    dc->props = megasas_properties;
+    dc->reset = megasas_scsi_reset;
+    dc->vmsd = &vmstate_megasas;
+    dc->desc = "LSI MegaRAID SAS 1078";
+}
+
+static const TypeInfo megasas_info = {
+    .name  = "megasas",
+    .parent = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(MegasasState),
+    .class_init = megasas_class_init,
+};
+
+static void megasas_register_types(void)
+{
+    type_register_static(&megasas_info);
+}
+
+type_init(megasas_register_types)
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
new file mode 100644 (file)
index 0000000..6239ee1
--- /dev/null
@@ -0,0 +1,1889 @@
+#include "hw/hw.h"
+#include "qemu/error-report.h"
+#include "hw/scsi/scsi.h"
+#include "block/scsi.h"
+#include "hw/qdev.h"
+#include "sysemu/blockdev.h"
+#include "trace.h"
+#include "sysemu/dma.h"
+
+static char *scsibus_get_dev_path(DeviceState *dev);
+static char *scsibus_get_fw_dev_path(DeviceState *dev);
+static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf);
+static void scsi_req_dequeue(SCSIRequest *req);
+
+static Property scsi_props[] = {
+    DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0),
+    DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1),
+    DEFINE_PROP_UINT32("lun", SCSIDevice, lun, -1),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void scsi_bus_class_init(ObjectClass *klass, void *data)
+{
+    BusClass *k = BUS_CLASS(klass);
+
+    k->get_dev_path = scsibus_get_dev_path;
+    k->get_fw_dev_path = scsibus_get_fw_dev_path;
+}
+
+static const TypeInfo scsi_bus_info = {
+    .name = TYPE_SCSI_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(SCSIBus),
+    .class_init = scsi_bus_class_init,
+};
+static int next_scsi_bus;
+
+static int scsi_device_init(SCSIDevice *s)
+{
+    SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s);
+    if (sc->init) {
+        return sc->init(s);
+    }
+    return 0;
+}
+
+static void scsi_device_destroy(SCSIDevice *s)
+{
+    SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s);
+    if (sc->destroy) {
+        sc->destroy(s);
+    }
+}
+
+static SCSIRequest *scsi_device_alloc_req(SCSIDevice *s, uint32_t tag, uint32_t lun,
+                                          uint8_t *buf, void *hba_private)
+{
+    SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s);
+    if (sc->alloc_req) {
+        return sc->alloc_req(s, tag, lun, buf, hba_private);
+    }
+
+    return NULL;
+}
+
+static void scsi_device_unit_attention_reported(SCSIDevice *s)
+{
+    SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(s);
+    if (sc->unit_attention_reported) {
+        sc->unit_attention_reported(s);
+    }
+}
+
+/* Create a scsi bus, and attach devices to it.  */
+void scsi_bus_new(SCSIBus *bus, DeviceState *host, const SCSIBusInfo *info)
+{
+    qbus_create_inplace(&bus->qbus, TYPE_SCSI_BUS, host, NULL);
+    bus->busnr = next_scsi_bus++;
+    bus->info = info;
+    bus->qbus.allow_hotplug = 1;
+}
+
+static void scsi_dma_restart_bh(void *opaque)
+{
+    SCSIDevice *s = opaque;
+    SCSIRequest *req, *next;
+
+    qemu_bh_delete(s->bh);
+    s->bh = NULL;
+
+    QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) {
+        scsi_req_ref(req);
+        if (req->retry) {
+            req->retry = false;
+            switch (req->cmd.mode) {
+            case SCSI_XFER_FROM_DEV:
+            case SCSI_XFER_TO_DEV:
+                scsi_req_continue(req);
+                break;
+            case SCSI_XFER_NONE:
+                assert(!req->sg);
+                scsi_req_dequeue(req);
+                scsi_req_enqueue(req);
+                break;
+            }
+        }
+        scsi_req_unref(req);
+    }
+}
+
+void scsi_req_retry(SCSIRequest *req)
+{
+    /* No need to save a reference, because scsi_dma_restart_bh just
+     * looks at the request list.  */
+    req->retry = true;
+}
+
+static void scsi_dma_restart_cb(void *opaque, int running, RunState state)
+{
+    SCSIDevice *s = opaque;
+
+    if (!running) {
+        return;
+    }
+    if (!s->bh) {
+        s->bh = qemu_bh_new(scsi_dma_restart_bh, s);
+        qemu_bh_schedule(s->bh);
+    }
+}
+
+static int scsi_qdev_init(DeviceState *qdev)
+{
+    SCSIDevice *dev = SCSI_DEVICE(qdev);
+    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
+    SCSIDevice *d;
+    int rc = -1;
+
+    if (dev->channel > bus->info->max_channel) {
+        error_report("bad scsi channel id: %d", dev->channel);
+        goto err;
+    }
+    if (dev->id != -1 && dev->id > bus->info->max_target) {
+        error_report("bad scsi device id: %d", dev->id);
+        goto err;
+    }
+    if (dev->lun != -1 && dev->lun > bus->info->max_lun) {
+        error_report("bad scsi device lun: %d", dev->lun);
+        goto err;
+    }
+
+    if (dev->id == -1) {
+        int id = -1;
+        if (dev->lun == -1) {
+            dev->lun = 0;
+        }
+        do {
+            d = scsi_device_find(bus, dev->channel, ++id, dev->lun);
+        } while (d && d->lun == dev->lun && id < bus->info->max_target);
+        if (d && d->lun == dev->lun) {
+            error_report("no free target");
+            goto err;
+        }
+        dev->id = id;
+    } else if (dev->lun == -1) {
+        int lun = -1;
+        do {
+            d = scsi_device_find(bus, dev->channel, dev->id, ++lun);
+        } while (d && d->lun == lun && lun < bus->info->max_lun);
+        if (d && d->lun == lun) {
+            error_report("no free lun");
+            goto err;
+        }
+        dev->lun = lun;
+    } else {
+        d = scsi_device_find(bus, dev->channel, dev->id, dev->lun);
+        assert(d);
+        if (d->lun == dev->lun && dev != d) {
+            qdev_free(&d->qdev);
+        }
+    }
+
+    QTAILQ_INIT(&dev->requests);
+    rc = scsi_device_init(dev);
+    if (rc == 0) {
+        dev->vmsentry = qemu_add_vm_change_state_handler(scsi_dma_restart_cb,
+                                                         dev);
+    }
+
+    if (bus->info->hotplug) {
+        bus->info->hotplug(bus, dev);
+    }
+
+err:
+    return rc;
+}
+
+static int scsi_qdev_exit(DeviceState *qdev)
+{
+    SCSIDevice *dev = SCSI_DEVICE(qdev);
+
+    if (dev->vmsentry) {
+        qemu_del_vm_change_state_handler(dev->vmsentry);
+    }
+    scsi_device_destroy(dev);
+    return 0;
+}
+
+/* handle legacy '-drive if=scsi,...' cmd line args */
+SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
+                                      int unit, bool removable, int bootindex,
+                                      const char *serial)
+{
+    const char *driver;
+    DeviceState *dev;
+
+    driver = bdrv_is_sg(bdrv) ? "scsi-generic" : "scsi-disk";
+    dev = qdev_create(&bus->qbus, driver);
+    qdev_prop_set_uint32(dev, "scsi-id", unit);
+    if (bootindex >= 0) {
+        qdev_prop_set_int32(dev, "bootindex", bootindex);
+    }
+    if (object_property_find(OBJECT(dev), "removable", NULL)) {
+        qdev_prop_set_bit(dev, "removable", removable);
+    }
+    if (serial) {
+        qdev_prop_set_string(dev, "serial", serial);
+    }
+    if (qdev_prop_set_drive(dev, "drive", bdrv) < 0) {
+        qdev_free(dev);
+        return NULL;
+    }
+    if (qdev_init(dev) < 0)
+        return NULL;
+    return SCSI_DEVICE(dev);
+}
+
+int scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
+{
+    Location loc;
+    DriveInfo *dinfo;
+    int res = 0, unit;
+
+    loc_push_none(&loc);
+    for (unit = 0; unit <= bus->info->max_target; unit++) {
+        dinfo = drive_get(IF_SCSI, bus->busnr, unit);
+        if (dinfo == NULL) {
+            continue;
+        }
+        qemu_opts_loc_restore(dinfo->opts);
+        if (!scsi_bus_legacy_add_drive(bus, dinfo->bdrv, unit, false, -1, NULL)) {
+            res = -1;
+            break;
+        }
+    }
+    loc_pop(&loc);
+    return res;
+}
+
+static int32_t scsi_invalid_field(SCSIRequest *req, uint8_t *buf)
+{
+    scsi_req_build_sense(req, SENSE_CODE(INVALID_FIELD));
+    scsi_req_complete(req, CHECK_CONDITION);
+    return 0;
+}
+
+static const struct SCSIReqOps reqops_invalid_field = {
+    .size         = sizeof(SCSIRequest),
+    .send_command = scsi_invalid_field
+};
+
+/* SCSIReqOps implementation for invalid commands.  */
+
+static int32_t scsi_invalid_command(SCSIRequest *req, uint8_t *buf)
+{
+    scsi_req_build_sense(req, SENSE_CODE(INVALID_OPCODE));
+    scsi_req_complete(req, CHECK_CONDITION);
+    return 0;
+}
+
+static const struct SCSIReqOps reqops_invalid_opcode = {
+    .size         = sizeof(SCSIRequest),
+    .send_command = scsi_invalid_command
+};
+
+/* SCSIReqOps implementation for unit attention conditions.  */
+
+static int32_t scsi_unit_attention(SCSIRequest *req, uint8_t *buf)
+{
+    if (req->dev->unit_attention.key == UNIT_ATTENTION) {
+        scsi_req_build_sense(req, req->dev->unit_attention);
+    } else if (req->bus->unit_attention.key == UNIT_ATTENTION) {
+        scsi_req_build_sense(req, req->bus->unit_attention);
+    }
+    scsi_req_complete(req, CHECK_CONDITION);
+    return 0;
+}
+
+static const struct SCSIReqOps reqops_unit_attention = {
+    .size         = sizeof(SCSIRequest),
+    .send_command = scsi_unit_attention
+};
+
+/* SCSIReqOps implementation for REPORT LUNS and for commands sent to
+   an invalid LUN.  */
+
+typedef struct SCSITargetReq SCSITargetReq;
+
+struct SCSITargetReq {
+    SCSIRequest req;
+    int len;
+    uint8_t buf[2056];
+};
+
+static void store_lun(uint8_t *outbuf, int lun)
+{
+    if (lun < 256) {
+        outbuf[1] = lun;
+        return;
+    }
+    outbuf[1] = (lun & 255);
+    outbuf[0] = (lun >> 8) | 0x40;
+}
+
+static bool scsi_target_emulate_report_luns(SCSITargetReq *r)
+{
+    BusChild *kid;
+    int i, len, n;
+    int channel, id;
+    bool found_lun0;
+
+    if (r->req.cmd.xfer < 16) {
+        return false;
+    }
+    if (r->req.cmd.buf[2] > 2) {
+        return false;
+    }
+    channel = r->req.dev->channel;
+    id = r->req.dev->id;
+    found_lun0 = false;
+    n = 0;
+    QTAILQ_FOREACH(kid, &r->req.bus->qbus.children, sibling) {
+        DeviceState *qdev = kid->child;
+        SCSIDevice *dev = SCSI_DEVICE(qdev);
+
+        if (dev->channel == channel && dev->id == id) {
+            if (dev->lun == 0) {
+                found_lun0 = true;
+            }
+            n += 8;
+        }
+    }
+    if (!found_lun0) {
+        n += 8;
+    }
+    len = MIN(n + 8, r->req.cmd.xfer & ~7);
+    if (len > sizeof(r->buf)) {
+        /* TODO: > 256 LUNs? */
+        return false;
+    }
+
+    memset(r->buf, 0, len);
+    stl_be_p(&r->buf, n);
+    i = found_lun0 ? 8 : 16;
+    QTAILQ_FOREACH(kid, &r->req.bus->qbus.children, sibling) {
+        DeviceState *qdev = kid->child;
+        SCSIDevice *dev = SCSI_DEVICE(qdev);
+
+        if (dev->channel == channel && dev->id == id) {
+            store_lun(&r->buf[i], dev->lun);
+            i += 8;
+        }
+    }
+    assert(i == n + 8);
+    r->len = len;
+    return true;
+}
+
+static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
+{
+    assert(r->req.dev->lun != r->req.lun);
+    if (r->req.cmd.buf[1] & 0x2) {
+        /* Command support data - optional, not implemented */
+        return false;
+    }
+
+    if (r->req.cmd.buf[1] & 0x1) {
+        /* Vital product data */
+        uint8_t page_code = r->req.cmd.buf[2];
+        r->buf[r->len++] = page_code ; /* this page */
+        r->buf[r->len++] = 0x00;
+
+        switch (page_code) {
+        case 0x00: /* Supported page codes, mandatory */
+        {
+            int pages;
+            pages = r->len++;
+            r->buf[r->len++] = 0x00; /* list of supported pages (this page) */
+            r->buf[pages] = r->len - pages - 1; /* number of pages */
+            break;
+        }
+        default:
+            return false;
+        }
+        /* done with EVPD */
+        assert(r->len < sizeof(r->buf));
+        r->len = MIN(r->req.cmd.xfer, r->len);
+        return true;
+    }
+
+    /* Standard INQUIRY data */
+    if (r->req.cmd.buf[2] != 0) {
+        return false;
+    }
+
+    /* PAGE CODE == 0 */
+    r->len = MIN(r->req.cmd.xfer, 36);
+    memset(r->buf, 0, r->len);
+    if (r->req.lun != 0) {
+        r->buf[0] = TYPE_NO_LUN;
+    } else {
+        r->buf[0] = TYPE_NOT_PRESENT | TYPE_INACTIVE;
+        r->buf[2] = 5; /* Version */
+        r->buf[3] = 2 | 0x10; /* HiSup, response data format */
+        r->buf[4] = r->len - 5; /* Additional Length = (Len - 1) - 4 */
+        r->buf[7] = 0x10 | (r->req.bus->info->tcq ? 0x02 : 0); /* Sync, TCQ.  */
+        memcpy(&r->buf[8], "QEMU    ", 8);
+        memcpy(&r->buf[16], "QEMU TARGET     ", 16);
+        pstrcpy((char *) &r->buf[32], 4, qemu_get_version());
+    }
+    return true;
+}
+
+static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf)
+{
+    SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
+
+    switch (buf[0]) {
+    case REPORT_LUNS:
+        if (!scsi_target_emulate_report_luns(r)) {
+            goto illegal_request;
+        }
+        break;
+    case INQUIRY:
+        if (!scsi_target_emulate_inquiry(r)) {
+            goto illegal_request;
+        }
+        break;
+    case REQUEST_SENSE:
+        r->len = scsi_device_get_sense(r->req.dev, r->buf,
+                                       MIN(req->cmd.xfer, sizeof r->buf),
+                                       (req->cmd.buf[1] & 1) == 0);
+        if (r->req.dev->sense_is_ua) {
+            scsi_device_unit_attention_reported(req->dev);
+            r->req.dev->sense_len = 0;
+            r->req.dev->sense_is_ua = false;
+        }
+        break;
+    default:
+        scsi_req_build_sense(req, SENSE_CODE(LUN_NOT_SUPPORTED));
+        scsi_req_complete(req, CHECK_CONDITION);
+        return 0;
+    illegal_request:
+        scsi_req_build_sense(req, SENSE_CODE(INVALID_FIELD));
+        scsi_req_complete(req, CHECK_CONDITION);
+        return 0;
+    }
+
+    if (!r->len) {
+        scsi_req_complete(req, GOOD);
+    }
+    return r->len;
+}
+
+static void scsi_target_read_data(SCSIRequest *req)
+{
+    SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
+    uint32_t n;
+
+    n = r->len;
+    if (n > 0) {
+        r->len = 0;
+        scsi_req_data(&r->req, n);
+    } else {
+        scsi_req_complete(&r->req, GOOD);
+    }
+}
+
+static uint8_t *scsi_target_get_buf(SCSIRequest *req)
+{
+    SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req);
+
+    return r->buf;
+}
+
+static const struct SCSIReqOps reqops_target_command = {
+    .size         = sizeof(SCSITargetReq),
+    .send_command = scsi_target_send_command,
+    .read_data    = scsi_target_read_data,
+    .get_buf      = scsi_target_get_buf,
+};
+
+
+SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d,
+                            uint32_t tag, uint32_t lun, void *hba_private)
+{
+    SCSIRequest *req;
+
+    req = g_malloc0(reqops->size);
+    req->refcount = 1;
+    req->bus = scsi_bus_from_device(d);
+    req->dev = d;
+    req->tag = tag;
+    req->lun = lun;
+    req->hba_private = hba_private;
+    req->status = -1;
+    req->sense_len = 0;
+    req->ops = reqops;
+    trace_scsi_req_alloc(req->dev->id, req->lun, req->tag);
+    return req;
+}
+
+SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
+                          uint8_t *buf, void *hba_private)
+{
+    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus);
+    SCSIRequest *req;
+    SCSICommand cmd;
+
+    if (scsi_req_parse(&cmd, d, buf) != 0) {
+        trace_scsi_req_parse_bad(d->id, lun, tag, buf[0]);
+        req = scsi_req_alloc(&reqops_invalid_opcode, d, tag, lun, hba_private);
+    } else {
+        trace_scsi_req_parsed(d->id, lun, tag, buf[0],
+                              cmd.mode, cmd.xfer);
+        if (cmd.lba != -1) {
+            trace_scsi_req_parsed_lba(d->id, lun, tag, buf[0],
+                                      cmd.lba);
+        }
+
+        if (cmd.xfer > INT32_MAX) {
+            req = scsi_req_alloc(&reqops_invalid_field, d, tag, lun, hba_private);
+        } else if ((d->unit_attention.key == UNIT_ATTENTION ||
+                   bus->unit_attention.key == UNIT_ATTENTION) &&
+                  (buf[0] != INQUIRY &&
+                   buf[0] != REPORT_LUNS &&
+                   buf[0] != GET_CONFIGURATION &&
+                   buf[0] != GET_EVENT_STATUS_NOTIFICATION &&
+
+                   /*
+                    * If we already have a pending unit attention condition,
+                    * report this one before triggering another one.
+                    */
+                   !(buf[0] == REQUEST_SENSE && d->sense_is_ua))) {
+            req = scsi_req_alloc(&reqops_unit_attention, d, tag, lun,
+                                 hba_private);
+        } else if (lun != d->lun ||
+                   buf[0] == REPORT_LUNS ||
+                   (buf[0] == REQUEST_SENSE && d->sense_len)) {
+            req = scsi_req_alloc(&reqops_target_command, d, tag, lun,
+                                 hba_private);
+        } else {
+            req = scsi_device_alloc_req(d, tag, lun, buf, hba_private);
+        }
+    }
+
+    req->cmd = cmd;
+    req->resid = req->cmd.xfer;
+
+    switch (buf[0]) {
+    case INQUIRY:
+        trace_scsi_inquiry(d->id, lun, tag, cmd.buf[1], cmd.buf[2]);
+        break;
+    case TEST_UNIT_READY:
+        trace_scsi_test_unit_ready(d->id, lun, tag);
+        break;
+    case REPORT_LUNS:
+        trace_scsi_report_luns(d->id, lun, tag);
+        break;
+    case REQUEST_SENSE:
+        trace_scsi_request_sense(d->id, lun, tag);
+        break;
+    default:
+        break;
+    }
+
+    return req;
+}
+
+uint8_t *scsi_req_get_buf(SCSIRequest *req)
+{
+    return req->ops->get_buf(req);
+}
+
+static void scsi_clear_unit_attention(SCSIRequest *req)
+{
+    SCSISense *ua;
+    if (req->dev->unit_attention.key != UNIT_ATTENTION &&
+        req->bus->unit_attention.key != UNIT_ATTENTION) {
+        return;
+    }
+
+    /*
+     * If an INQUIRY command enters the enabled command state,
+     * the device server shall [not] clear any unit attention condition;
+     * See also MMC-6, paragraphs 6.5 and 6.6.2.
+     */
+    if (req->cmd.buf[0] == INQUIRY ||
+        req->cmd.buf[0] == GET_CONFIGURATION ||
+        req->cmd.buf[0] == GET_EVENT_STATUS_NOTIFICATION) {
+        return;
+    }
+
+    if (req->dev->unit_attention.key == UNIT_ATTENTION) {
+        ua = &req->dev->unit_attention;
+    } else {
+        ua = &req->bus->unit_attention;
+    }
+
+    /*
+     * If a REPORT LUNS command enters the enabled command state, [...]
+     * the device server shall clear any pending unit attention condition
+     * with an additional sense code of REPORTED LUNS DATA HAS CHANGED.
+     */
+    if (req->cmd.buf[0] == REPORT_LUNS &&
+        !(ua->asc == SENSE_CODE(REPORTED_LUNS_CHANGED).asc &&
+          ua->ascq == SENSE_CODE(REPORTED_LUNS_CHANGED).ascq)) {
+        return;
+    }
+
+    *ua = SENSE_CODE(NO_SENSE);
+}
+
+int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len)
+{
+    int ret;
+
+    assert(len >= 14);
+    if (!req->sense_len) {
+        return 0;
+    }
+
+    ret = scsi_build_sense(req->sense, req->sense_len, buf, len, true);
+
+    /*
+     * FIXME: clearing unit attention conditions upon autosense should be done
+     * only if the UA_INTLCK_CTRL field in the Control mode page is set to 00b
+     * (SAM-5, 5.14).
+     *
+     * We assume UA_INTLCK_CTRL to be 00b for HBAs that support autosense, and
+     * 10b for HBAs that do not support it (do not call scsi_req_get_sense).
+     * Here we handle unit attention clearing for UA_INTLCK_CTRL == 00b.
+     */
+    if (req->dev->sense_is_ua) {
+        scsi_device_unit_attention_reported(req->dev);
+        req->dev->sense_len = 0;
+        req->dev->sense_is_ua = false;
+    }
+    return ret;
+}
+
+int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed)
+{
+    return scsi_build_sense(dev->sense, dev->sense_len, buf, len, fixed);
+}
+
+void scsi_req_build_sense(SCSIRequest *req, SCSISense sense)
+{
+    trace_scsi_req_build_sense(req->dev->id, req->lun, req->tag,
+                               sense.key, sense.asc, sense.ascq);
+    memset(req->sense, 0, 18);
+    req->sense[0] = 0x70;
+    req->sense[2] = sense.key;
+    req->sense[7] = 10;
+    req->sense[12] = sense.asc;
+    req->sense[13] = sense.ascq;
+    req->sense_len = 18;
+}
+
+static void scsi_req_enqueue_internal(SCSIRequest *req)
+{
+    assert(!req->enqueued);
+    scsi_req_ref(req);
+    if (req->bus->info->get_sg_list) {
+        req->sg = req->bus->info->get_sg_list(req);
+    } else {
+        req->sg = NULL;
+    }
+    req->enqueued = true;
+    QTAILQ_INSERT_TAIL(&req->dev->requests, req, next);
+}
+
+int32_t scsi_req_enqueue(SCSIRequest *req)
+{
+    int32_t rc;
+
+    assert(!req->retry);
+    scsi_req_enqueue_internal(req);
+    scsi_req_ref(req);
+    rc = req->ops->send_command(req, req->cmd.buf);
+    scsi_req_unref(req);
+    return rc;
+}
+
+static void scsi_req_dequeue(SCSIRequest *req)
+{
+    trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag);
+    req->retry = false;
+    if (req->enqueued) {
+        QTAILQ_REMOVE(&req->dev->requests, req, next);
+        req->enqueued = false;
+        scsi_req_unref(req);
+    }
+}
+
+static int scsi_get_performance_length(int num_desc, int type, int data_type)
+{
+    /* MMC-6, paragraph 6.7.  */
+    switch (type) {
+    case 0:
+        if ((data_type & 3) == 0) {
+            /* Each descriptor is as in Table 295 - Nominal performance.  */
+            return 16 * num_desc + 8;
+        } else {
+            /* Each descriptor is as in Table 296 - Exceptions.  */
+            return 6 * num_desc + 8;
+        }
+    case 1:
+    case 4:
+    case 5:
+        return 8 * num_desc + 8;
+    case 2:
+        return 2048 * num_desc + 8;
+    case 3:
+        return 16 * num_desc + 8;
+    default:
+        return 8;
+    }
+}
+
+static int ata_passthrough_xfer_unit(SCSIDevice *dev, uint8_t *buf)
+{
+    int byte_block = (buf[2] >> 2) & 0x1;
+    int type = (buf[2] >> 4) & 0x1;
+    int xfer_unit;
+
+    if (byte_block) {
+        if (type) {
+            xfer_unit = dev->blocksize;
+        } else {
+            xfer_unit = 512;
+        }
+    } else {
+        xfer_unit = 1;
+    }
+
+    return xfer_unit;
+}
+
+static int ata_passthrough_12_xfer_size(SCSIDevice *dev, uint8_t *buf)
+{
+    int length = buf[2] & 0x3;
+    int xfer;
+    int unit = ata_passthrough_xfer_unit(dev, buf);
+
+    switch (length) {
+    case 0:
+    case 3: /* USB-specific.  */
+    default:
+        xfer = 0;
+        break;
+    case 1:
+        xfer = buf[3];
+        break;
+    case 2:
+        xfer = buf[4];
+        break;
+    }
+
+    return xfer * unit;
+}
+
+static int ata_passthrough_16_xfer_size(SCSIDevice *dev, uint8_t *buf)
+{
+    int extend = buf[1] & 0x1;
+    int length = buf[2] & 0x3;
+    int xfer;
+    int unit = ata_passthrough_xfer_unit(dev, buf);
+
+    switch (length) {
+    case 0:
+    case 3: /* USB-specific.  */
+    default:
+        xfer = 0;
+        break;
+    case 1:
+        xfer = buf[4];
+        xfer |= (extend ? buf[3] << 8 : 0);
+        break;
+    case 2:
+        xfer = buf[6];
+        xfer |= (extend ? buf[5] << 8 : 0);
+        break;
+    }
+
+    return xfer * unit;
+}
+
+uint32_t scsi_data_cdb_length(uint8_t *buf)
+{
+    if ((buf[0] >> 5) == 0 && buf[4] == 0) {
+        return 256;
+    } else {
+        return scsi_cdb_length(buf);
+    }
+}
+
+uint32_t scsi_cdb_length(uint8_t *buf)
+{
+    switch (buf[0] >> 5) {
+    case 0:
+        return buf[4];
+        break;
+    case 1:
+    case 2:
+        return lduw_be_p(&buf[7]);
+        break;
+    case 4:
+        return ldl_be_p(&buf[10]) & 0xffffffffULL;
+        break;
+    case 5:
+        return ldl_be_p(&buf[6]) & 0xffffffffULL;
+        break;
+    default:
+        return -1;
+    }
+}
+
+static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
+{
+    cmd->xfer = scsi_cdb_length(buf);
+    switch (buf[0]) {
+    case TEST_UNIT_READY:
+    case REWIND:
+    case START_STOP:
+    case SET_CAPACITY:
+    case WRITE_FILEMARKS:
+    case WRITE_FILEMARKS_16:
+    case SPACE:
+    case RESERVE:
+    case RELEASE:
+    case ERASE:
+    case ALLOW_MEDIUM_REMOVAL:
+    case VERIFY_10:
+    case SEEK_10:
+    case SYNCHRONIZE_CACHE:
+    case SYNCHRONIZE_CACHE_16:
+    case LOCATE_16:
+    case LOCK_UNLOCK_CACHE:
+    case SET_CD_SPEED:
+    case SET_LIMITS:
+    case WRITE_LONG_10:
+    case UPDATE_BLOCK:
+    case RESERVE_TRACK:
+    case SET_READ_AHEAD:
+    case PRE_FETCH:
+    case PRE_FETCH_16:
+    case ALLOW_OVERWRITE:
+        cmd->xfer = 0;
+        break;
+    case MODE_SENSE:
+        break;
+    case WRITE_SAME_10:
+    case WRITE_SAME_16:
+        cmd->xfer = dev->blocksize;
+        break;
+    case READ_CAPACITY_10:
+        cmd->xfer = 8;
+        break;
+    case READ_BLOCK_LIMITS:
+        cmd->xfer = 6;
+        break;
+    case SEND_VOLUME_TAG:
+        /* GPCMD_SET_STREAMING from multimedia commands.  */
+        if (dev->type == TYPE_ROM) {
+            cmd->xfer = buf[10] | (buf[9] << 8);
+        } else {
+            cmd->xfer = buf[9] | (buf[8] << 8);
+        }
+        break;
+    case WRITE_6:
+        /* length 0 means 256 blocks */
+        if (cmd->xfer == 0) {
+            cmd->xfer = 256;
+        }
+    case WRITE_10:
+    case WRITE_VERIFY_10:
+    case WRITE_12:
+    case WRITE_VERIFY_12:
+    case WRITE_16:
+    case WRITE_VERIFY_16:
+        cmd->xfer *= dev->blocksize;
+        break;
+    case READ_6:
+    case READ_REVERSE:
+        /* length 0 means 256 blocks */
+        if (cmd->xfer == 0) {
+            cmd->xfer = 256;
+        }
+    case READ_10:
+    case RECOVER_BUFFERED_DATA:
+    case READ_12:
+    case READ_16:
+        cmd->xfer *= dev->blocksize;
+        break;
+    case FORMAT_UNIT:
+        /* MMC mandates the parameter list to be 12-bytes long.  Parameters
+         * for block devices are restricted to the header right now.  */
+        if (dev->type == TYPE_ROM && (buf[1] & 16)) {
+            cmd->xfer = 12;
+        } else {
+            cmd->xfer = (buf[1] & 16) == 0 ? 0 : (buf[1] & 32 ? 8 : 4);
+        }
+        break;
+    case INQUIRY:
+    case RECEIVE_DIAGNOSTIC:
+    case SEND_DIAGNOSTIC:
+        cmd->xfer = buf[4] | (buf[3] << 8);
+        break;
+    case READ_CD:
+    case READ_BUFFER:
+    case WRITE_BUFFER:
+    case SEND_CUE_SHEET:
+        cmd->xfer = buf[8] | (buf[7] << 8) | (buf[6] << 16);
+        break;
+    case PERSISTENT_RESERVE_OUT:
+        cmd->xfer = ldl_be_p(&buf[5]) & 0xffffffffULL;
+        break;
+    case ERASE_12:
+        if (dev->type == TYPE_ROM) {
+            /* MMC command GET PERFORMANCE.  */
+            cmd->xfer = scsi_get_performance_length(buf[9] | (buf[8] << 8),
+                                                    buf[10], buf[1] & 0x1f);
+        }
+        break;
+    case MECHANISM_STATUS:
+    case READ_DVD_STRUCTURE:
+    case SEND_DVD_STRUCTURE:
+    case MAINTENANCE_OUT:
+    case MAINTENANCE_IN:
+        if (dev->type == TYPE_ROM) {
+            /* GPCMD_REPORT_KEY and GPCMD_SEND_KEY from multi media commands */
+            cmd->xfer = buf[9] | (buf[8] << 8);
+        }
+        break;
+    case ATA_PASSTHROUGH_12:
+        if (dev->type == TYPE_ROM) {
+            /* BLANK command of MMC */
+            cmd->xfer = 0;
+        } else {
+            cmd->xfer = ata_passthrough_12_xfer_size(dev, buf);
+        }
+        break;
+    case ATA_PASSTHROUGH_16:
+        cmd->xfer = ata_passthrough_16_xfer_size(dev, buf);
+        break;
+    }
+    return 0;
+}
+
+static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
+{
+    switch (buf[0]) {
+    /* stream commands */
+    case ERASE_12:
+    case ERASE_16:
+        cmd->xfer = 0;
+        break;
+    case READ_6:
+    case READ_REVERSE:
+    case RECOVER_BUFFERED_DATA:
+    case WRITE_6:
+        cmd->xfer = buf[4] | (buf[3] << 8) | (buf[2] << 16);
+        if (buf[1] & 0x01) { /* fixed */
+            cmd->xfer *= dev->blocksize;
+        }
+        break;
+    case READ_16:
+    case READ_REVERSE_16:
+    case VERIFY_16:
+    case WRITE_16:
+        cmd->xfer = buf[14] | (buf[13] << 8) | (buf[12] << 16);
+        if (buf[1] & 0x01) { /* fixed */
+            cmd->xfer *= dev->blocksize;
+        }
+        break;
+    case REWIND:
+    case LOAD_UNLOAD:
+        cmd->xfer = 0;
+        break;
+    case SPACE_16:
+        cmd->xfer = buf[13] | (buf[12] << 8);
+        break;
+    case READ_POSITION:
+        switch (buf[1] & 0x1f) /* operation code */ {
+        case SHORT_FORM_BLOCK_ID:
+        case SHORT_FORM_VENDOR_SPECIFIC:
+            cmd->xfer = 20;
+            break;
+        case LONG_FORM:
+            cmd->xfer = 32;
+            break;
+        case EXTENDED_FORM:
+            cmd->xfer = buf[8] | (buf[7] << 8);
+            break;
+        default:
+            return -1;
+        }
+
+        break;
+    case FORMAT_UNIT:
+        cmd->xfer = buf[4] | (buf[3] << 8);
+        break;
+    /* generic commands */
+    default:
+        return scsi_req_length(cmd, dev, buf);
+    }
+    return 0;
+}
+
+static int scsi_req_medium_changer_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
+{
+    switch (buf[0]) {
+    /* medium changer commands */
+    case EXCHANGE_MEDIUM:
+    case INITIALIZE_ELEMENT_STATUS:
+    case INITIALIZE_ELEMENT_STATUS_WITH_RANGE:
+    case MOVE_MEDIUM:
+    case POSITION_TO_ELEMENT:
+        cmd->xfer = 0;
+        break;
+    case READ_ELEMENT_STATUS:
+        cmd->xfer = buf[9] | (buf[8] << 8) | (buf[7] << 16);
+        break;
+
+    /* generic commands */
+    default:
+        return scsi_req_length(cmd, dev, buf);
+    }
+    return 0;
+}
+
+
+static void scsi_cmd_xfer_mode(SCSICommand *cmd)
+{
+    if (!cmd->xfer) {
+        cmd->mode = SCSI_XFER_NONE;
+        return;
+    }
+    switch (cmd->buf[0]) {
+    case WRITE_6:
+    case WRITE_10:
+    case WRITE_VERIFY_10:
+    case WRITE_12:
+    case WRITE_VERIFY_12:
+    case WRITE_16:
+    case WRITE_VERIFY_16:
+    case COPY:
+    case COPY_VERIFY:
+    case COMPARE:
+    case CHANGE_DEFINITION:
+    case LOG_SELECT:
+    case MODE_SELECT:
+    case MODE_SELECT_10:
+    case SEND_DIAGNOSTIC:
+    case WRITE_BUFFER:
+    case FORMAT_UNIT:
+    case REASSIGN_BLOCKS:
+    case SEARCH_EQUAL:
+    case SEARCH_HIGH:
+    case SEARCH_LOW:
+    case UPDATE_BLOCK:
+    case WRITE_LONG_10:
+    case WRITE_SAME_10:
+    case WRITE_SAME_16:
+    case UNMAP:
+    case SEARCH_HIGH_12:
+    case SEARCH_EQUAL_12:
+    case SEARCH_LOW_12:
+    case MEDIUM_SCAN:
+    case SEND_VOLUME_TAG:
+    case SEND_CUE_SHEET:
+    case SEND_DVD_STRUCTURE:
+    case PERSISTENT_RESERVE_OUT:
+    case MAINTENANCE_OUT:
+        cmd->mode = SCSI_XFER_TO_DEV;
+        break;
+    case ATA_PASSTHROUGH_12:
+    case ATA_PASSTHROUGH_16:
+        /* T_DIR */
+        cmd->mode = (cmd->buf[2] & 0x8) ?
+                   SCSI_XFER_FROM_DEV : SCSI_XFER_TO_DEV;
+        break;
+    default:
+        cmd->mode = SCSI_XFER_FROM_DEV;
+        break;
+    }
+}
+
+static uint64_t scsi_cmd_lba(SCSICommand *cmd)
+{
+    uint8_t *buf = cmd->buf;
+    uint64_t lba;
+
+    switch (buf[0] >> 5) {
+    case 0:
+        lba = ldl_be_p(&buf[0]) & 0x1fffff;
+        break;
+    case 1:
+    case 2:
+    case 5:
+        lba = ldl_be_p(&buf[2]) & 0xffffffffULL;
+        break;
+    case 4:
+        lba = ldq_be_p(&buf[2]);
+        break;
+    default:
+        lba = -1;
+
+    }
+    return lba;
+}
+
+int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
+{
+    int rc;
+
+    switch (buf[0] >> 5) {
+    case 0:
+        cmd->len = 6;
+        break;
+    case 1:
+    case 2:
+        cmd->len = 10;
+        break;
+    case 4:
+        cmd->len = 16;
+        break;
+    case 5:
+        cmd->len = 12;
+        break;
+    default:
+        return -1;
+    }
+
+    switch (dev->type) {
+    case TYPE_TAPE:
+        rc = scsi_req_stream_length(cmd, dev, buf);
+        break;
+    case TYPE_MEDIUM_CHANGER:
+        rc = scsi_req_medium_changer_length(cmd, dev, buf);
+        break;
+    default:
+        rc = scsi_req_length(cmd, dev, buf);
+        break;
+    }
+
+    if (rc != 0)
+        return rc;
+
+    memcpy(cmd->buf, buf, cmd->len);
+    scsi_cmd_xfer_mode(cmd);
+    cmd->lba = scsi_cmd_lba(cmd);
+    return 0;
+}
+
+void scsi_device_report_change(SCSIDevice *dev, SCSISense sense)
+{
+    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
+
+    scsi_device_set_ua(dev, sense);
+    if (bus->info->change) {
+        bus->info->change(bus, dev, sense);
+    }
+}
+
+/*
+ * Predefined sense codes
+ */
+
+/* No sense data available */
+const struct SCSISense sense_code_NO_SENSE = {
+    .key = NO_SENSE , .asc = 0x00 , .ascq = 0x00
+};
+
+/* LUN not ready, Manual intervention required */
+const struct SCSISense sense_code_LUN_NOT_READY = {
+    .key = NOT_READY, .asc = 0x04, .ascq = 0x03
+};
+
+/* LUN not ready, Medium not present */
+const struct SCSISense sense_code_NO_MEDIUM = {
+    .key = NOT_READY, .asc = 0x3a, .ascq = 0x00
+};
+
+/* LUN not ready, medium removal prevented */
+const struct SCSISense sense_code_NOT_READY_REMOVAL_PREVENTED = {
+    .key = NOT_READY, .asc = 0x53, .ascq = 0x02
+};
+
+/* Hardware error, internal target failure */
+const struct SCSISense sense_code_TARGET_FAILURE = {
+    .key = HARDWARE_ERROR, .asc = 0x44, .ascq = 0x00
+};
+
+/* Illegal request, invalid command operation code */
+const struct SCSISense sense_code_INVALID_OPCODE = {
+    .key = ILLEGAL_REQUEST, .asc = 0x20, .ascq = 0x00
+};
+
+/* Illegal request, LBA out of range */
+const struct SCSISense sense_code_LBA_OUT_OF_RANGE = {
+    .key = ILLEGAL_REQUEST, .asc = 0x21, .ascq = 0x00
+};
+
+/* Illegal request, Invalid field in CDB */
+const struct SCSISense sense_code_INVALID_FIELD = {
+    .key = ILLEGAL_REQUEST, .asc = 0x24, .ascq = 0x00
+};
+
+/* Illegal request, Invalid field in parameter list */
+const struct SCSISense sense_code_INVALID_PARAM = {
+    .key = ILLEGAL_REQUEST, .asc = 0x26, .ascq = 0x00
+};
+
+/* Illegal request, Parameter list length error */
+const struct SCSISense sense_code_INVALID_PARAM_LEN = {
+    .key = ILLEGAL_REQUEST, .asc = 0x1a, .ascq = 0x00
+};
+
+/* Illegal request, LUN not supported */
+const struct SCSISense sense_code_LUN_NOT_SUPPORTED = {
+    .key = ILLEGAL_REQUEST, .asc = 0x25, .ascq = 0x00
+};
+
+/* Illegal request, Saving parameters not supported */
+const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED = {
+    .key = ILLEGAL_REQUEST, .asc = 0x39, .ascq = 0x00
+};
+
+/* Illegal request, Incompatible medium installed */
+const struct SCSISense sense_code_INCOMPATIBLE_FORMAT = {
+    .key = ILLEGAL_REQUEST, .asc = 0x30, .ascq = 0x00
+};
+
+/* Illegal request, medium removal prevented */
+const struct SCSISense sense_code_ILLEGAL_REQ_REMOVAL_PREVENTED = {
+    .key = ILLEGAL_REQUEST, .asc = 0x53, .ascq = 0x02
+};
+
+/* Command aborted, I/O process terminated */
+const struct SCSISense sense_code_IO_ERROR = {
+    .key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06
+};
+
+/* Command aborted, I_T Nexus loss occurred */
+const struct SCSISense sense_code_I_T_NEXUS_LOSS = {
+    .key = ABORTED_COMMAND, .asc = 0x29, .ascq = 0x07
+};
+
+/* Command aborted, Logical Unit failure */
+const struct SCSISense sense_code_LUN_FAILURE = {
+    .key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01
+};
+
+/* Unit attention, Capacity data has changed */
+const struct SCSISense sense_code_CAPACITY_CHANGED = {
+    .key = UNIT_ATTENTION, .asc = 0x2a, .ascq = 0x09
+};
+
+/* Unit attention, Power on, reset or bus device reset occurred */
+const struct SCSISense sense_code_RESET = {
+    .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x00
+};
+
+/* Unit attention, No medium */
+const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM = {
+    .key = UNIT_ATTENTION, .asc = 0x3a, .ascq = 0x00
+};
+
+/* Unit attention, Medium may have changed */
+const struct SCSISense sense_code_MEDIUM_CHANGED = {
+    .key = UNIT_ATTENTION, .asc = 0x28, .ascq = 0x00
+};
+
+/* Unit attention, Reported LUNs data has changed */
+const struct SCSISense sense_code_REPORTED_LUNS_CHANGED = {
+    .key = UNIT_ATTENTION, .asc = 0x3f, .ascq = 0x0e
+};
+
+/* Unit attention, Device internal reset */
+const struct SCSISense sense_code_DEVICE_INTERNAL_RESET = {
+    .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x04
+};
+
+/* Data Protection, Write Protected */
+const struct SCSISense sense_code_WRITE_PROTECTED = {
+    .key = DATA_PROTECT, .asc = 0x27, .ascq = 0x00
+};
+
+/*
+ * scsi_build_sense
+ *
+ * Convert between fixed and descriptor sense buffers
+ */
+int scsi_build_sense(uint8_t *in_buf, int in_len,
+                     uint8_t *buf, int len, bool fixed)
+{
+    bool fixed_in;
+    SCSISense sense;
+    if (!fixed && len < 8) {
+        return 0;
+    }
+
+    if (in_len == 0) {
+        sense.key = NO_SENSE;
+        sense.asc = 0;
+        sense.ascq = 0;
+    } else {
+        fixed_in = (in_buf[0] & 2) == 0;
+
+        if (fixed == fixed_in) {
+            memcpy(buf, in_buf, MIN(len, in_len));
+            return MIN(len, in_len);
+        }
+
+        if (fixed_in) {
+            sense.key = in_buf[2];
+            sense.asc = in_buf[12];
+            sense.ascq = in_buf[13];
+        } else {
+            sense.key = in_buf[1];
+            sense.asc = in_buf[2];
+            sense.ascq = in_buf[3];
+        }
+    }
+
+    memset(buf, 0, len);
+    if (fixed) {
+        /* Return fixed format sense buffer */
+        buf[0] = 0x70;
+        buf[2] = sense.key;
+        buf[7] = 10;
+        buf[12] = sense.asc;
+        buf[13] = sense.ascq;
+        return MIN(len, 18);
+    } else {
+        /* Return descriptor format sense buffer */
+        buf[0] = 0x72;
+        buf[1] = sense.key;
+        buf[2] = sense.asc;
+        buf[3] = sense.ascq;
+        return 8;
+    }
+}
+
+static const char *scsi_command_name(uint8_t cmd)
+{
+    static const char *names[] = {
+        [ TEST_UNIT_READY          ] = "TEST_UNIT_READY",
+        [ REWIND                   ] = "REWIND",
+        [ REQUEST_SENSE            ] = "REQUEST_SENSE",
+        [ FORMAT_UNIT              ] = "FORMAT_UNIT",
+        [ READ_BLOCK_LIMITS        ] = "READ_BLOCK_LIMITS",
+        [ REASSIGN_BLOCKS          ] = "REASSIGN_BLOCKS/INITIALIZE ELEMENT STATUS",
+        /* LOAD_UNLOAD and INITIALIZE_ELEMENT_STATUS use the same operation code */
+        [ READ_6                   ] = "READ_6",
+        [ WRITE_6                  ] = "WRITE_6",
+        [ SET_CAPACITY             ] = "SET_CAPACITY",
+        [ READ_REVERSE             ] = "READ_REVERSE",
+        [ WRITE_FILEMARKS          ] = "WRITE_FILEMARKS",
+        [ SPACE                    ] = "SPACE",
+        [ INQUIRY                  ] = "INQUIRY",
+        [ RECOVER_BUFFERED_DATA    ] = "RECOVER_BUFFERED_DATA",
+        [ MAINTENANCE_IN           ] = "MAINTENANCE_IN",
+        [ MAINTENANCE_OUT          ] = "MAINTENANCE_OUT",
+        [ MODE_SELECT              ] = "MODE_SELECT",
+        [ RESERVE                  ] = "RESERVE",
+        [ RELEASE                  ] = "RELEASE",
+        [ COPY                     ] = "COPY",
+        [ ERASE                    ] = "ERASE",
+        [ MODE_SENSE               ] = "MODE_SENSE",
+        [ START_STOP               ] = "START_STOP/LOAD_UNLOAD",
+        /* LOAD_UNLOAD and START_STOP use the same operation code */
+        [ RECEIVE_DIAGNOSTIC       ] = "RECEIVE_DIAGNOSTIC",
+        [ SEND_DIAGNOSTIC          ] = "SEND_DIAGNOSTIC",
+        [ ALLOW_MEDIUM_REMOVAL     ] = "ALLOW_MEDIUM_REMOVAL",
+        [ READ_CAPACITY_10         ] = "READ_CAPACITY_10",
+        [ READ_10                  ] = "READ_10",
+        [ WRITE_10                 ] = "WRITE_10",
+        [ SEEK_10                  ] = "SEEK_10/POSITION_TO_ELEMENT",
+        /* SEEK_10 and POSITION_TO_ELEMENT use the same operation code */
+        [ WRITE_VERIFY_10          ] = "WRITE_VERIFY_10",
+        [ VERIFY_10                ] = "VERIFY_10",
+        [ SEARCH_HIGH              ] = "SEARCH_HIGH",
+        [ SEARCH_EQUAL             ] = "SEARCH_EQUAL",
+        [ SEARCH_LOW               ] = "SEARCH_LOW",
+        [ SET_LIMITS               ] = "SET_LIMITS",
+        [ PRE_FETCH                ] = "PRE_FETCH/READ_POSITION",
+        /* READ_POSITION and PRE_FETCH use the same operation code */
+        [ SYNCHRONIZE_CACHE        ] = "SYNCHRONIZE_CACHE",
+        [ LOCK_UNLOCK_CACHE        ] = "LOCK_UNLOCK_CACHE",
+        [ READ_DEFECT_DATA         ] = "READ_DEFECT_DATA/INITIALIZE_ELEMENT_STATUS_WITH_RANGE",
+        /* READ_DEFECT_DATA and INITIALIZE_ELEMENT_STATUS_WITH_RANGE use the same operation code */
+        [ MEDIUM_SCAN              ] = "MEDIUM_SCAN",
+        [ COMPARE                  ] = "COMPARE",
+        [ COPY_VERIFY              ] = "COPY_VERIFY",
+        [ WRITE_BUFFER             ] = "WRITE_BUFFER",
+        [ READ_BUFFER              ] = "READ_BUFFER",
+        [ UPDATE_BLOCK             ] = "UPDATE_BLOCK",
+        [ READ_LONG_10             ] = "READ_LONG_10",
+        [ WRITE_LONG_10            ] = "WRITE_LONG_10",
+        [ CHANGE_DEFINITION        ] = "CHANGE_DEFINITION",
+        [ WRITE_SAME_10            ] = "WRITE_SAME_10",
+        [ UNMAP                    ] = "UNMAP",
+        [ READ_TOC                 ] = "READ_TOC",
+        [ REPORT_DENSITY_SUPPORT   ] = "REPORT_DENSITY_SUPPORT",
+        [ SANITIZE                 ] = "SANITIZE",
+        [ GET_CONFIGURATION        ] = "GET_CONFIGURATION",
+        [ LOG_SELECT               ] = "LOG_SELECT",
+        [ LOG_SENSE                ] = "LOG_SENSE",
+        [ MODE_SELECT_10           ] = "MODE_SELECT_10",
+        [ RESERVE_10               ] = "RESERVE_10",
+        [ RELEASE_10               ] = "RELEASE_10",
+        [ MODE_SENSE_10            ] = "MODE_SENSE_10",
+        [ PERSISTENT_RESERVE_IN    ] = "PERSISTENT_RESERVE_IN",
+        [ PERSISTENT_RESERVE_OUT   ] = "PERSISTENT_RESERVE_OUT",
+        [ WRITE_FILEMARKS_16       ] = "WRITE_FILEMARKS_16",
+        [ EXTENDED_COPY            ] = "EXTENDED_COPY",
+        [ ATA_PASSTHROUGH_16       ] = "ATA_PASSTHROUGH_16",
+        [ ACCESS_CONTROL_IN        ] = "ACCESS_CONTROL_IN",
+        [ ACCESS_CONTROL_OUT       ] = "ACCESS_CONTROL_OUT",
+        [ READ_16                  ] = "READ_16",
+        [ COMPARE_AND_WRITE        ] = "COMPARE_AND_WRITE",
+        [ WRITE_16                 ] = "WRITE_16",
+        [ WRITE_VERIFY_16          ] = "WRITE_VERIFY_16",
+        [ VERIFY_16                ] = "VERIFY_16",
+        [ PRE_FETCH_16             ] = "PRE_FETCH_16",
+        [ SYNCHRONIZE_CACHE_16     ] = "SPACE_16/SYNCHRONIZE_CACHE_16",
+        /* SPACE_16 and SYNCHRONIZE_CACHE_16 use the same operation code */
+        [ LOCATE_16                ] = "LOCATE_16",
+        [ WRITE_SAME_16            ] = "ERASE_16/WRITE_SAME_16",
+        /* ERASE_16 and WRITE_SAME_16 use the same operation code */
+        [ SERVICE_ACTION_IN_16     ] = "SERVICE_ACTION_IN_16",
+        [ WRITE_LONG_16            ] = "WRITE_LONG_16",
+        [ REPORT_LUNS              ] = "REPORT_LUNS",
+        [ ATA_PASSTHROUGH_12       ] = "BLANK/ATA_PASSTHROUGH_12",
+        [ MOVE_MEDIUM              ] = "MOVE_MEDIUM",
+        [ EXCHANGE_MEDIUM          ] = "EXCHANGE MEDIUM",
+        [ READ_12                  ] = "READ_12",
+        [ WRITE_12                 ] = "WRITE_12",
+        [ ERASE_12                 ] = "ERASE_12/GET_PERFORMANCE",
+        /* ERASE_12 and GET_PERFORMANCE use the same operation code */
+        [ SERVICE_ACTION_IN_12     ] = "SERVICE_ACTION_IN_12",
+        [ WRITE_VERIFY_12          ] = "WRITE_VERIFY_12",
+        [ VERIFY_12                ] = "VERIFY_12",
+        [ SEARCH_HIGH_12           ] = "SEARCH_HIGH_12",
+        [ SEARCH_EQUAL_12          ] = "SEARCH_EQUAL_12",
+        [ SEARCH_LOW_12            ] = "SEARCH_LOW_12",
+        [ READ_ELEMENT_STATUS      ] = "READ_ELEMENT_STATUS",
+        [ SEND_VOLUME_TAG          ] = "SEND_VOLUME_TAG/SET_STREAMING",
+        /* SEND_VOLUME_TAG and SET_STREAMING use the same operation code */
+        [ READ_CD                  ] = "READ_CD",
+        [ READ_DEFECT_DATA_12      ] = "READ_DEFECT_DATA_12",
+        [ READ_DVD_STRUCTURE       ] = "READ_DVD_STRUCTURE",
+        [ RESERVE_TRACK            ] = "RESERVE_TRACK",
+        [ SEND_CUE_SHEET           ] = "SEND_CUE_SHEET",
+        [ SEND_DVD_STRUCTURE       ] = "SEND_DVD_STRUCTURE",
+        [ SET_CD_SPEED             ] = "SET_CD_SPEED",
+        [ SET_READ_AHEAD           ] = "SET_READ_AHEAD",
+        [ ALLOW_OVERWRITE          ] = "ALLOW_OVERWRITE",
+        [ MECHANISM_STATUS         ] = "MECHANISM_STATUS",
+    };
+
+    if (cmd >= ARRAY_SIZE(names) || names[cmd] == NULL)
+        return "*UNKNOWN*";
+    return names[cmd];
+}
+
+SCSIRequest *scsi_req_ref(SCSIRequest *req)
+{
+    assert(req->refcount > 0);
+    req->refcount++;
+    return req;
+}
+
+void scsi_req_unref(SCSIRequest *req)
+{
+    assert(req->refcount > 0);
+    if (--req->refcount == 0) {
+        SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, req->dev->qdev.parent_bus);
+        if (bus->info->free_request && req->hba_private) {
+            bus->info->free_request(bus, req->hba_private);
+        }
+        if (req->ops->free_req) {
+            req->ops->free_req(req);
+        }
+        g_free(req);
+    }
+}
+
+/* Tell the device that we finished processing this chunk of I/O.  It
+   will start the next chunk or complete the command.  */
+void scsi_req_continue(SCSIRequest *req)
+{
+    if (req->io_canceled) {
+        trace_scsi_req_continue_canceled(req->dev->id, req->lun, req->tag);
+        return;
+    }
+    trace_scsi_req_continue(req->dev->id, req->lun, req->tag);
+    if (req->cmd.mode == SCSI_XFER_TO_DEV) {
+        req->ops->write_data(req);
+    } else {
+        req->ops->read_data(req);
+    }
+}
+
+/* Called by the devices when data is ready for the HBA.  The HBA should
+   start a DMA operation to read or fill the device's data buffer.
+   Once it completes, calling scsi_req_continue will restart I/O.  */
+void scsi_req_data(SCSIRequest *req, int len)
+{
+    uint8_t *buf;
+    if (req->io_canceled) {
+        trace_scsi_req_data_canceled(req->dev->id, req->lun, req->tag, len);
+        return;
+    }
+    trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
+    assert(req->cmd.mode != SCSI_XFER_NONE);
+    if (!req->sg) {
+        req->resid -= len;
+        req->bus->info->transfer_data(req, len);
+        return;
+    }
+
+    /* If the device calls scsi_req_data and the HBA specified a
+     * scatter/gather list, the transfer has to happen in a single
+     * step.  */
+    assert(!req->dma_started);
+    req->dma_started = true;
+
+    buf = scsi_req_get_buf(req);
+    if (req->cmd.mode == SCSI_XFER_FROM_DEV) {
+        req->resid = dma_buf_read(buf, len, req->sg);
+    } else {
+        req->resid = dma_buf_write(buf, len, req->sg);
+    }
+    scsi_req_continue(req);
+}
+
+void scsi_req_print(SCSIRequest *req)
+{
+    FILE *fp = stderr;
+    int i;
+
+    fprintf(fp, "[%s id=%d] %s",
+            req->dev->qdev.parent_bus->name,
+            req->dev->id,
+            scsi_command_name(req->cmd.buf[0]));
+    for (i = 1; i < req->cmd.len; i++) {
+        fprintf(fp, " 0x%02x", req->cmd.buf[i]);
+    }
+    switch (req->cmd.mode) {
+    case SCSI_XFER_NONE:
+        fprintf(fp, " - none\n");
+        break;
+    case SCSI_XFER_FROM_DEV:
+        fprintf(fp, " - from-dev len=%zd\n", req->cmd.xfer);
+        break;
+    case SCSI_XFER_TO_DEV:
+        fprintf(fp, " - to-dev len=%zd\n", req->cmd.xfer);
+        break;
+    default:
+        fprintf(fp, " - Oops\n");
+        break;
+    }
+}
+
+void scsi_req_complete(SCSIRequest *req, int status)
+{
+    assert(req->status == -1);
+    req->status = status;
+
+    assert(req->sense_len <= sizeof(req->sense));
+    if (status == GOOD) {
+        req->sense_len = 0;
+    }
+
+    if (req->sense_len) {
+        memcpy(req->dev->sense, req->sense, req->sense_len);
+        req->dev->sense_len = req->sense_len;
+        req->dev->sense_is_ua = (req->ops == &reqops_unit_attention);
+    } else {
+        req->dev->sense_len = 0;
+        req->dev->sense_is_ua = false;
+    }
+
+    /*
+     * Unit attention state is now stored in the device's sense buffer
+     * if the HBA didn't do autosense.  Clear the pending unit attention
+     * flags.
+     */
+    scsi_clear_unit_attention(req);
+
+    scsi_req_ref(req);
+    scsi_req_dequeue(req);
+    req->bus->info->complete(req, req->status, req->resid);
+    scsi_req_unref(req);
+}
+
+void scsi_req_cancel(SCSIRequest *req)
+{
+    trace_scsi_req_cancel(req->dev->id, req->lun, req->tag);
+    if (!req->enqueued) {
+        return;
+    }
+    scsi_req_ref(req);
+    scsi_req_dequeue(req);
+    req->io_canceled = true;
+    if (req->ops->cancel_io) {
+        req->ops->cancel_io(req);
+    }
+    if (req->bus->info->cancel) {
+        req->bus->info->cancel(req);
+    }
+    scsi_req_unref(req);
+}
+
+void scsi_req_abort(SCSIRequest *req, int status)
+{
+    if (!req->enqueued) {
+        return;
+    }
+    scsi_req_ref(req);
+    scsi_req_dequeue(req);
+    req->io_canceled = true;
+    if (req->ops->cancel_io) {
+        req->ops->cancel_io(req);
+    }
+    scsi_req_complete(req, status);
+    scsi_req_unref(req);
+}
+
+static int scsi_ua_precedence(SCSISense sense)
+{
+    if (sense.key != UNIT_ATTENTION) {
+        return INT_MAX;
+    }
+    if (sense.asc == 0x29 && sense.ascq == 0x04) {
+        /* DEVICE INTERNAL RESET goes with POWER ON OCCURRED */
+        return 1;
+    } else if (sense.asc == 0x3F && sense.ascq == 0x01) {
+        /* MICROCODE HAS BEEN CHANGED goes with SCSI BUS RESET OCCURRED */
+        return 2;
+    } else if (sense.asc == 0x29 && (sense.ascq == 0x05 || sense.ascq == 0x06)) {
+        /* These two go with "all others". */
+        ;
+    } else if (sense.asc == 0x29 && sense.ascq <= 0x07) {
+        /* POWER ON, RESET OR BUS DEVICE RESET OCCURRED = 0
+         * POWER ON OCCURRED = 1
+         * SCSI BUS RESET OCCURRED = 2
+         * BUS DEVICE RESET FUNCTION OCCURRED = 3
+         * I_T NEXUS LOSS OCCURRED = 7
+         */
+        return sense.ascq;
+    } else if (sense.asc == 0x2F && sense.ascq == 0x01) {
+        /* COMMANDS CLEARED BY POWER LOSS NOTIFICATION  */
+        return 8;
+    }
+    return (sense.asc << 8) | sense.ascq;
+}
+
+void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense)
+{
+    int prec1, prec2;
+    if (sense.key != UNIT_ATTENTION) {
+        return;
+    }
+    trace_scsi_device_set_ua(sdev->id, sdev->lun, sense.key,
+                             sense.asc, sense.ascq);
+
+    /*
+     * Override a pre-existing unit attention condition, except for a more
+     * important reset condition.
+    */
+    prec1 = scsi_ua_precedence(sdev->unit_attention);
+    prec2 = scsi_ua_precedence(sense);
+    if (prec2 < prec1) {
+        sdev->unit_attention = sense;
+    }
+}
+
+void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense)
+{
+    SCSIRequest *req;
+
+    while (!QTAILQ_EMPTY(&sdev->requests)) {
+        req = QTAILQ_FIRST(&sdev->requests);
+        scsi_req_cancel(req);
+    }
+
+    scsi_device_set_ua(sdev, sense);
+}
+
+static char *scsibus_get_dev_path(DeviceState *dev)
+{
+    SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev);
+    DeviceState *hba = dev->parent_bus->parent;
+    char *id;
+    char *path;
+
+    id = qdev_get_dev_path(hba);
+    if (id) {
+        path = g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun);
+    } else {
+        path = g_strdup_printf("%d:%d:%d", d->channel, d->id, d->lun);
+    }
+    g_free(id);
+    return path;
+}
+
+static char *scsibus_get_fw_dev_path(DeviceState *dev)
+{
+    SCSIDevice *d = SCSI_DEVICE(dev);
+    return g_strdup_printf("channel@%x/%s@%x,%x", d->channel,
+                           qdev_fw_name(dev), d->id, d->lun);
+}
+
+SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun)
+{
+    BusChild *kid;
+    SCSIDevice *target_dev = NULL;
+
+    QTAILQ_FOREACH_REVERSE(kid, &bus->qbus.children, ChildrenHead, sibling) {
+        DeviceState *qdev = kid->child;
+        SCSIDevice *dev = SCSI_DEVICE(qdev);
+
+        if (dev->channel == channel && dev->id == id) {
+            if (dev->lun == lun) {
+                return dev;
+            }
+            target_dev = dev;
+        }
+    }
+    return target_dev;
+}
+
+/* SCSI request list.  For simplicity, pv points to the whole device */
+
+static void put_scsi_requests(QEMUFile *f, void *pv, size_t size)
+{
+    SCSIDevice *s = pv;
+    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus);
+    SCSIRequest *req;
+
+    QTAILQ_FOREACH(req, &s->requests, next) {
+        assert(!req->io_canceled);
+        assert(req->status == -1);
+        assert(req->enqueued);
+
+        qemu_put_sbyte(f, req->retry ? 1 : 2);
+        qemu_put_buffer(f, req->cmd.buf, sizeof(req->cmd.buf));
+        qemu_put_be32s(f, &req->tag);
+        qemu_put_be32s(f, &req->lun);
+        if (bus->info->save_request) {
+            bus->info->save_request(f, req);
+        }
+        if (req->ops->save_request) {
+            req->ops->save_request(f, req);
+        }
+    }
+    qemu_put_sbyte(f, 0);
+}
+
+static int get_scsi_requests(QEMUFile *f, void *pv, size_t size)
+{
+    SCSIDevice *s = pv;
+    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus);
+    int8_t sbyte;
+
+    while ((sbyte = qemu_get_sbyte(f)) > 0) {
+        uint8_t buf[SCSI_CMD_BUF_SIZE];
+        uint32_t tag;
+        uint32_t lun;
+        SCSIRequest *req;
+
+        qemu_get_buffer(f, buf, sizeof(buf));
+        qemu_get_be32s(f, &tag);
+        qemu_get_be32s(f, &lun);
+        req = scsi_req_new(s, tag, lun, buf, NULL);
+        req->retry = (sbyte == 1);
+        if (bus->info->load_request) {
+            req->hba_private = bus->info->load_request(f, req);
+        }
+        if (req->ops->load_request) {
+            req->ops->load_request(f, req);
+        }
+
+        /* Just restart it later.  */
+        scsi_req_enqueue_internal(req);
+
+        /* At this point, the request will be kept alive by the reference
+         * added by scsi_req_enqueue_internal, so we can release our reference.
+         * The HBA of course will add its own reference in the load_request
+         * callback if it needs to hold on the SCSIRequest.
+         */
+        scsi_req_unref(req);
+    }
+
+    return 0;
+}
+
+static int scsi_qdev_unplug(DeviceState *qdev)
+{
+    SCSIDevice *dev = SCSI_DEVICE(qdev);
+    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
+
+    if (bus->info->hot_unplug) {
+        bus->info->hot_unplug(bus, dev);
+    }
+    return qdev_simple_unplug_cb(qdev);
+}
+
+static const VMStateInfo vmstate_info_scsi_requests = {
+    .name = "scsi-requests",
+    .get  = get_scsi_requests,
+    .put  = put_scsi_requests,
+};
+
+const VMStateDescription vmstate_scsi_device = {
+    .name = "SCSIDevice",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(unit_attention.key, SCSIDevice),
+        VMSTATE_UINT8(unit_attention.asc, SCSIDevice),
+        VMSTATE_UINT8(unit_attention.ascq, SCSIDevice),
+        VMSTATE_BOOL(sense_is_ua, SCSIDevice),
+        VMSTATE_UINT8_ARRAY(sense, SCSIDevice, SCSI_SENSE_BUF_SIZE),
+        VMSTATE_UINT32(sense_len, SCSIDevice),
+        {
+            .name         = "requests",
+            .version_id   = 0,
+            .field_exists = NULL,
+            .size         = 0,   /* ouch */
+            .info         = &vmstate_info_scsi_requests,
+            .flags        = VMS_SINGLE,
+            .offset       = 0,
+        },
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void scsi_device_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *k = DEVICE_CLASS(klass);
+    k->bus_type = TYPE_SCSI_BUS;
+    k->init     = scsi_qdev_init;
+    k->unplug   = scsi_qdev_unplug;
+    k->exit     = scsi_qdev_exit;
+    k->props    = scsi_props;
+}
+
+static const TypeInfo scsi_device_type_info = {
+    .name = TYPE_SCSI_DEVICE,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(SCSIDevice),
+    .abstract = true,
+    .class_size = sizeof(SCSIDeviceClass),
+    .class_init = scsi_device_class_init,
+};
+
+static void scsi_register_types(void)
+{
+    type_register_static(&scsi_bus_info);
+    type_register_static(&scsi_device_type_info);
+}
+
+type_init(scsi_register_types)
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
new file mode 100644 (file)
index 0000000..f52bd11
--- /dev/null
@@ -0,0 +1,2526 @@
+/*
+ * SCSI Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Based on code by Fabrice Bellard
+ *
+ * Written by Paul Brook
+ * Modifications:
+ *  2009-Dec-12 Artyom Tarasenko : implemented stamdard inquiry for the case
+ *                                 when the allocation length of CDB is smaller
+ *                                 than 36.
+ *  2009-Oct-13 Artyom Tarasenko : implemented the block descriptor in the
+ *                                 MODE SENSE response.
+ *
+ * This code is licensed under the LGPL.
+ *
+ * Note that this file only handles the SCSI architecture model and device
+ * commands.  Emulation of interface/link layer protocols is handled by
+ * the host adapter emulator.
+ */
+
+//#define DEBUG_SCSI
+
+#ifdef DEBUG_SCSI
+#define DPRINTF(fmt, ...) \
+do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+#include "qemu-common.h"
+#include "qemu/error-report.h"
+#include "hw/scsi/scsi.h"
+#include "block/scsi.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/blockdev.h"
+#include "hw/block/block.h"
+#include "sysemu/dma.h"
+
+#ifdef __linux
+#include <scsi/sg.h>
+#endif
+
+#define SCSI_DMA_BUF_SIZE           131072
+#define SCSI_MAX_INQUIRY_LEN        256
+#define SCSI_MAX_MODE_LEN           256
+
+#define DEFAULT_DISCARD_GRANULARITY 4096
+
+typedef struct SCSIDiskState SCSIDiskState;
+
+typedef struct SCSIDiskReq {
+    SCSIRequest req;
+    /* Both sector and sector_count are in terms of qemu 512 byte blocks.  */
+    uint64_t sector;
+    uint32_t sector_count;
+    uint32_t buflen;
+    bool started;
+    struct iovec iov;
+    QEMUIOVector qiov;
+    BlockAcctCookie acct;
+} SCSIDiskReq;
+
+#define SCSI_DISK_F_REMOVABLE   0
+#define SCSI_DISK_F_DPOFUA      1
+
+struct SCSIDiskState
+{
+    SCSIDevice qdev;
+    uint32_t features;
+    bool media_changed;
+    bool media_event;
+    bool eject_request;
+    uint64_t wwn;
+    QEMUBH *bh;
+    char *version;
+    char *serial;
+    char *vendor;
+    char *product;
+    bool tray_open;
+    bool tray_locked;
+};
+
+static int scsi_handle_rw_error(SCSIDiskReq *r, int error);
+
+static void scsi_free_request(SCSIRequest *req)
+{
+    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
+    qemu_vfree(r->iov.iov_base);
+}
+
+/* Helper function for command completion with sense.  */
+static void scsi_check_condition(SCSIDiskReq *r, SCSISense sense)
+{
+    DPRINTF("Command complete tag=0x%x sense=%d/%d/%d\n",
+            r->req.tag, sense.key, sense.asc, sense.ascq);
+    scsi_req_build_sense(&r->req, sense);
+    scsi_req_complete(&r->req, CHECK_CONDITION);
+}
+
+/* Cancel a pending data transfer.  */
+static void scsi_cancel_io(SCSIRequest *req)
+{
+    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
+    DPRINTF("Cancel tag=0x%x\n", req->tag);
+    if (r->req.aiocb) {
+        bdrv_aio_cancel(r->req.aiocb);
+
+        /* This reference was left in by scsi_*_data.  We take ownership of
+         * it the moment scsi_req_cancel is called, independent of whether
+         * bdrv_aio_cancel completes the request or not.  */
+        scsi_req_unref(&r->req);
+    }
+    r->req.aiocb = NULL;
+}
+
+static uint32_t scsi_init_iovec(SCSIDiskReq *r, size_t size)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+    if (!r->iov.iov_base) {
+        r->buflen = size;
+        r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen);
+    }
+    r->iov.iov_len = MIN(r->sector_count * 512, r->buflen);
+    qemu_iovec_init_external(&r->qiov, &r->iov, 1);
+    return r->qiov.size / 512;
+}
+
+static void scsi_disk_save_request(QEMUFile *f, SCSIRequest *req)
+{
+    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
+    qemu_put_be64s(f, &r->sector);
+    qemu_put_be32s(f, &r->sector_count);
+    qemu_put_be32s(f, &r->buflen);
+    if (r->buflen) {
+        if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+            qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len);
+        } else if (!req->retry) {
+            uint32_t len = r->iov.iov_len;
+            qemu_put_be32s(f, &len);
+            qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len);
+        }
+    }
+}
+
+static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req)
+{
+    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
+    qemu_get_be64s(f, &r->sector);
+    qemu_get_be32s(f, &r->sector_count);
+    qemu_get_be32s(f, &r->buflen);
+    if (r->buflen) {
+        scsi_init_iovec(r, r->buflen);
+        if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+            qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len);
+        } else if (!r->req.retry) {
+            uint32_t len;
+            qemu_get_be32s(f, &len);
+            r->iov.iov_len = len;
+            assert(r->iov.iov_len <= r->buflen);
+            qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len);
+        }
+    }
+
+    qemu_iovec_init_external(&r->qiov, &r->iov, 1);
+}
+
+static void scsi_aio_complete(void *opaque, int ret)
+{
+    SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+    assert(r->req.aiocb != NULL);
+    r->req.aiocb = NULL;
+    bdrv_acct_done(s->qdev.conf.bs, &r->acct);
+    if (r->req.io_canceled) {
+        goto done;
+    }
+
+    if (ret < 0) {
+        if (scsi_handle_rw_error(r, -ret)) {
+            goto done;
+        }
+    }
+
+    scsi_req_complete(&r->req, GOOD);
+
+done:
+    if (!r->req.io_canceled) {
+        scsi_req_unref(&r->req);
+    }
+}
+
+static bool scsi_is_cmd_fua(SCSICommand *cmd)
+{
+    switch (cmd->buf[0]) {
+    case READ_10:
+    case READ_12:
+    case READ_16:
+    case WRITE_10:
+    case WRITE_12:
+    case WRITE_16:
+        return (cmd->buf[1] & 8) != 0;
+
+    case VERIFY_10:
+    case VERIFY_12:
+    case VERIFY_16:
+    case WRITE_VERIFY_10:
+    case WRITE_VERIFY_12:
+    case WRITE_VERIFY_16:
+        return true;
+
+    case READ_6:
+    case WRITE_6:
+    default:
+        return false;
+    }
+}
+
+static void scsi_write_do_fua(SCSIDiskReq *r)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+    if (r->req.io_canceled) {
+        goto done;
+    }
+
+    if (scsi_is_cmd_fua(&r->req.cmd)) {
+        bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
+        r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_aio_complete, r);
+        return;
+    }
+
+    scsi_req_complete(&r->req, GOOD);
+
+done:
+    if (!r->req.io_canceled) {
+        scsi_req_unref(&r->req);
+    }
+}
+
+static void scsi_dma_complete(void *opaque, int ret)
+{
+    SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+    assert(r->req.aiocb != NULL);
+    r->req.aiocb = NULL;
+    bdrv_acct_done(s->qdev.conf.bs, &r->acct);
+    if (r->req.io_canceled) {
+        goto done;
+    }
+
+    if (ret < 0) {
+        if (scsi_handle_rw_error(r, -ret)) {
+            goto done;
+        }
+    }
+
+    r->sector += r->sector_count;
+    r->sector_count = 0;
+    if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+        scsi_write_do_fua(r);
+        return;
+    } else {
+        scsi_req_complete(&r->req, GOOD);
+    }
+
+done:
+    if (!r->req.io_canceled) {
+        scsi_req_unref(&r->req);
+    }
+}
+
+static void scsi_read_complete(void * opaque, int ret)
+{
+    SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+    int n;
+
+    assert(r->req.aiocb != NULL);
+    r->req.aiocb = NULL;
+    bdrv_acct_done(s->qdev.conf.bs, &r->acct);
+    if (r->req.io_canceled) {
+        goto done;
+    }
+
+    if (ret < 0) {
+        if (scsi_handle_rw_error(r, -ret)) {
+            goto done;
+        }
+    }
+
+    DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->qiov.size);
+
+    n = r->qiov.size / 512;
+    r->sector += n;
+    r->sector_count -= n;
+    scsi_req_data(&r->req, r->qiov.size);
+
+done:
+    if (!r->req.io_canceled) {
+        scsi_req_unref(&r->req);
+    }
+}
+
+/* Actually issue a read to the block device.  */
+static void scsi_do_read(void *opaque, int ret)
+{
+    SCSIDiskReq *r = opaque;
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+    uint32_t n;
+
+    if (r->req.aiocb != NULL) {
+        r->req.aiocb = NULL;
+        bdrv_acct_done(s->qdev.conf.bs, &r->acct);
+    }
+    if (r->req.io_canceled) {
+        goto done;
+    }
+
+    if (ret < 0) {
+        if (scsi_handle_rw_error(r, -ret)) {
+            goto done;
+        }
+    }
+
+    /* The request is used as the AIO opaque value, so add a ref.  */
+    scsi_req_ref(&r->req);
+
+    if (r->req.sg) {
+        dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ);
+        r->req.resid -= r->req.sg->size;
+        r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector,
+                                     scsi_dma_complete, r);
+    } else {
+        n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
+        bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
+        r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
+                                      scsi_read_complete, r);
+    }
+
+done:
+    if (!r->req.io_canceled) {
+        scsi_req_unref(&r->req);
+    }
+}
+
+/* Read more data from scsi device into buffer.  */
+static void scsi_read_data(SCSIRequest *req)
+{
+    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+    bool first;
+
+    DPRINTF("Read sector_count=%d\n", r->sector_count);
+    if (r->sector_count == 0) {
+        /* This also clears the sense buffer for REQUEST SENSE.  */
+        scsi_req_complete(&r->req, GOOD);
+        return;
+    }
+
+    /* No data transfer may already be in progress */
+    assert(r->req.aiocb == NULL);
+
+    /* The request is used as the AIO opaque value, so add a ref.  */
+    scsi_req_ref(&r->req);
+    if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+        DPRINTF("Data transfer direction invalid\n");
+        scsi_read_complete(r, -EINVAL);
+        return;
+    }
+
+    if (s->tray_open) {
+        scsi_read_complete(r, -ENOMEDIUM);
+        return;
+    }
+
+    first = !r->started;
+    r->started = true;
+    if (first && scsi_is_cmd_fua(&r->req.cmd)) {
+        bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
+        r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_do_read, r);
+    } else {
+        scsi_do_read(r, 0);
+    }
+}
+
+/*
+ * scsi_handle_rw_error has two return values.  0 means that the error
+ * must be ignored, 1 means that the error has been processed and the
+ * caller should not do anything else for this request.  Note that
+ * scsi_handle_rw_error always manages its reference counts, independent
+ * of the return value.
+ */
+static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
+{
+    bool is_read = (r->req.cmd.xfer == SCSI_XFER_FROM_DEV);
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+    BlockErrorAction action = bdrv_get_error_action(s->qdev.conf.bs, is_read, error);
+
+    if (action == BDRV_ACTION_REPORT) {
+        switch (error) {
+        case ENOMEDIUM:
+            scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
+            break;
+        case ENOMEM:
+            scsi_check_condition(r, SENSE_CODE(TARGET_FAILURE));
+            break;
+        case EINVAL:
+            scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
+            break;
+        default:
+            scsi_check_condition(r, SENSE_CODE(IO_ERROR));
+            break;
+        }
+    }
+    bdrv_error_action(s->qdev.conf.bs, action, is_read, error);
+    if (action == BDRV_ACTION_STOP) {
+        scsi_req_retry(&r->req);
+    }
+    return action != BDRV_ACTION_IGNORE;
+}
+
+static void scsi_write_complete(void * opaque, int ret)
+{
+    SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+    uint32_t n;
+
+    if (r->req.aiocb != NULL) {
+        r->req.aiocb = NULL;
+        bdrv_acct_done(s->qdev.conf.bs, &r->acct);
+    }
+    if (r->req.io_canceled) {
+        goto done;
+    }
+
+    if (ret < 0) {
+        if (scsi_handle_rw_error(r, -ret)) {
+            goto done;
+        }
+    }
+
+    n = r->qiov.size / 512;
+    r->sector += n;
+    r->sector_count -= n;
+    if (r->sector_count == 0) {
+        scsi_write_do_fua(r);
+        return;
+    } else {
+        scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
+        DPRINTF("Write complete tag=0x%x more=%zd\n", r->req.tag, r->qiov.size);
+        scsi_req_data(&r->req, r->qiov.size);
+    }
+
+done:
+    if (!r->req.io_canceled) {
+        scsi_req_unref(&r->req);
+    }
+}
+
+static void scsi_write_data(SCSIRequest *req)
+{
+    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+    uint32_t n;
+
+    /* No data transfer may already be in progress */
+    assert(r->req.aiocb == NULL);
+
+    /* The request is used as the AIO opaque value, so add a ref.  */
+    scsi_req_ref(&r->req);
+    if (r->req.cmd.mode != SCSI_XFER_TO_DEV) {
+        DPRINTF("Data transfer direction invalid\n");
+        scsi_write_complete(r, -EINVAL);
+        return;
+    }
+
+    if (!r->req.sg && !r->qiov.size) {
+        /* Called for the first time.  Ask the driver to send us more data.  */
+        r->started = true;
+        scsi_write_complete(r, 0);
+        return;
+    }
+    if (s->tray_open) {
+        scsi_write_complete(r, -ENOMEDIUM);
+        return;
+    }
+
+    if (r->req.cmd.buf[0] == VERIFY_10 || r->req.cmd.buf[0] == VERIFY_12 ||
+        r->req.cmd.buf[0] == VERIFY_16) {
+        if (r->req.sg) {
+            scsi_dma_complete(r, 0);
+        } else {
+            scsi_write_complete(r, 0);
+        }
+        return;
+    }
+
+    if (r->req.sg) {
+        dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_WRITE);
+        r->req.resid -= r->req.sg->size;
+        r->req.aiocb = dma_bdrv_write(s->qdev.conf.bs, r->req.sg, r->sector,
+                                      scsi_dma_complete, r);
+    } else {
+        n = r->qiov.size / 512;
+        bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_WRITE);
+        r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, r->sector, &r->qiov, n,
+                                       scsi_write_complete, r);
+    }
+}
+
+/* Return a pointer to the data buffer.  */
+static uint8_t *scsi_get_buf(SCSIRequest *req)
+{
+    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
+    return (uint8_t *)r->iov.iov_base;
+}
+
+static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
+    int buflen = 0;
+    int start;
+
+    if (req->cmd.buf[1] & 0x1) {
+        /* Vital product data */
+        uint8_t page_code = req->cmd.buf[2];
+
+        outbuf[buflen++] = s->qdev.type & 0x1f;
+        outbuf[buflen++] = page_code ; // this page
+        outbuf[buflen++] = 0x00;
+        outbuf[buflen++] = 0x00;
+        start = buflen;
+
+        switch (page_code) {
+        case 0x00: /* Supported page codes, mandatory */
+        {
+            DPRINTF("Inquiry EVPD[Supported pages] "
+                    "buffer size %zd\n", req->cmd.xfer);
+            outbuf[buflen++] = 0x00; // list of supported pages (this page)
+            if (s->serial) {
+                outbuf[buflen++] = 0x80; // unit serial number
+            }
+            outbuf[buflen++] = 0x83; // device identification
+            if (s->qdev.type == TYPE_DISK) {
+                outbuf[buflen++] = 0xb0; // block limits
+                outbuf[buflen++] = 0xb2; // thin provisioning
+            }
+            break;
+        }
+        case 0x80: /* Device serial number, optional */
+        {
+            int l;
+
+            if (!s->serial) {
+                DPRINTF("Inquiry (EVPD[Serial number] not supported\n");
+                return -1;
+            }
+
+            l = strlen(s->serial);
+            if (l > 20) {
+                l = 20;
+            }
+
+            DPRINTF("Inquiry EVPD[Serial number] "
+                    "buffer size %zd\n", req->cmd.xfer);
+            memcpy(outbuf+buflen, s->serial, l);
+            buflen += l;
+            break;
+        }
+
+        case 0x83: /* Device identification page, mandatory */
+        {
+            const char *str = s->serial ?: bdrv_get_device_name(s->qdev.conf.bs);
+            int max_len = s->serial ? 20 : 255 - 8;
+            int id_len = strlen(str);
+
+            if (id_len > max_len) {
+                id_len = max_len;
+            }
+            DPRINTF("Inquiry EVPD[Device identification] "
+                    "buffer size %zd\n", req->cmd.xfer);
+
+            outbuf[buflen++] = 0x2; // ASCII
+            outbuf[buflen++] = 0;   // not officially assigned
+            outbuf[buflen++] = 0;   // reserved
+            outbuf[buflen++] = id_len; // length of data following
+            memcpy(outbuf+buflen, str, id_len);
+            buflen += id_len;
+
+            if (s->wwn) {
+                outbuf[buflen++] = 0x1; // Binary
+                outbuf[buflen++] = 0x3; // NAA
+                outbuf[buflen++] = 0;   // reserved
+                outbuf[buflen++] = 8;
+                stq_be_p(&outbuf[buflen], s->wwn);
+                buflen += 8;
+            }
+            break;
+        }
+        case 0xb0: /* block limits */
+        {
+            unsigned int unmap_sectors =
+                    s->qdev.conf.discard_granularity / s->qdev.blocksize;
+            unsigned int min_io_size =
+                    s->qdev.conf.min_io_size / s->qdev.blocksize;
+            unsigned int opt_io_size =
+                    s->qdev.conf.opt_io_size / s->qdev.blocksize;
+
+            if (s->qdev.type == TYPE_ROM) {
+                DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n",
+                        page_code);
+                return -1;
+            }
+            /* required VPD size with unmap support */
+            buflen = 0x40;
+            memset(outbuf + 4, 0, buflen - 4);
+
+            /* optimal transfer length granularity */
+            outbuf[6] = (min_io_size >> 8) & 0xff;
+            outbuf[7] = min_io_size & 0xff;
+
+            /* optimal transfer length */
+            outbuf[12] = (opt_io_size >> 24) & 0xff;
+            outbuf[13] = (opt_io_size >> 16) & 0xff;
+            outbuf[14] = (opt_io_size >> 8) & 0xff;
+            outbuf[15] = opt_io_size & 0xff;
+
+            /* optimal unmap granularity */
+            outbuf[28] = (unmap_sectors >> 24) & 0xff;
+            outbuf[29] = (unmap_sectors >> 16) & 0xff;
+            outbuf[30] = (unmap_sectors >> 8) & 0xff;
+            outbuf[31] = unmap_sectors & 0xff;
+            break;
+        }
+        case 0xb2: /* thin provisioning */
+        {
+            buflen = 8;
+            outbuf[4] = 0;
+            outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */
+            outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1;
+            outbuf[7] = 0;
+            break;
+        }
+        default:
+            return -1;
+        }
+        /* done with EVPD */
+        assert(buflen - start <= 255);
+        outbuf[start - 1] = buflen - start;
+        return buflen;
+    }
+
+    /* Standard INQUIRY data */
+    if (req->cmd.buf[2] != 0) {
+        return -1;
+    }
+
+    /* PAGE CODE == 0 */
+    buflen = req->cmd.xfer;
+    if (buflen > SCSI_MAX_INQUIRY_LEN) {
+        buflen = SCSI_MAX_INQUIRY_LEN;
+    }
+
+    outbuf[0] = s->qdev.type & 0x1f;
+    outbuf[1] = (s->features & (1 << SCSI_DISK_F_REMOVABLE)) ? 0x80 : 0;
+
+    strpadcpy((char *) &outbuf[16], 16, s->product, ' ');
+    strpadcpy((char *) &outbuf[8], 8, s->vendor, ' ');
+
+    memset(&outbuf[32], 0, 4);
+    memcpy(&outbuf[32], s->version, MIN(4, strlen(s->version)));
+    /*
+     * We claim conformance to SPC-3, which is required for guests
+     * to ask for modern features like READ CAPACITY(16) or the
+     * block characteristics VPD page by default.  Not all of SPC-3
+     * is actually implemented, but we're good enough.
+     */
+    outbuf[2] = 5;
+    outbuf[3] = 2 | 0x10; /* Format 2, HiSup */
+
+    if (buflen > 36) {
+        outbuf[4] = buflen - 5; /* Additional Length = (Len - 1) - 4 */
+    } else {
+        /* If the allocation length of CDB is too small,
+               the additional length is not adjusted */
+        outbuf[4] = 36 - 5;
+    }
+
+    /* Sync data transfer and TCQ.  */
+    outbuf[7] = 0x10 | (req->bus->info->tcq ? 0x02 : 0);
+    return buflen;
+}
+
+static inline bool media_is_dvd(SCSIDiskState *s)
+{
+    uint64_t nb_sectors;
+    if (s->qdev.type != TYPE_ROM) {
+        return false;
+    }
+    if (!bdrv_is_inserted(s->qdev.conf.bs)) {
+        return false;
+    }
+    bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+    return nb_sectors > CD_MAX_SECTORS;
+}
+
+static inline bool media_is_cd(SCSIDiskState *s)
+{
+    uint64_t nb_sectors;
+    if (s->qdev.type != TYPE_ROM) {
+        return false;
+    }
+    if (!bdrv_is_inserted(s->qdev.conf.bs)) {
+        return false;
+    }
+    bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+    return nb_sectors <= CD_MAX_SECTORS;
+}
+
+static int scsi_read_disc_information(SCSIDiskState *s, SCSIDiskReq *r,
+                                      uint8_t *outbuf)
+{
+    uint8_t type = r->req.cmd.buf[1] & 7;
+
+    if (s->qdev.type != TYPE_ROM) {
+        return -1;
+    }
+
+    /* Types 1/2 are only defined for Blu-Ray.  */
+    if (type != 0) {
+        scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
+        return -1;
+    }
+
+    memset(outbuf, 0, 34);
+    outbuf[1] = 32;
+    outbuf[2] = 0xe; /* last session complete, disc finalized */
+    outbuf[3] = 1;   /* first track on disc */
+    outbuf[4] = 1;   /* # of sessions */
+    outbuf[5] = 1;   /* first track of last session */
+    outbuf[6] = 1;   /* last track of last session */
+    outbuf[7] = 0x20; /* unrestricted use */
+    outbuf[8] = 0x00; /* CD-ROM or DVD-ROM */
+    /* 9-10-11: most significant byte corresponding bytes 4-5-6 */
+    /* 12-23: not meaningful for CD-ROM or DVD-ROM */
+    /* 24-31: disc bar code */
+    /* 32: disc application code */
+    /* 33: number of OPC tables */
+
+    return 34;
+}
+
+static int scsi_read_dvd_structure(SCSIDiskState *s, SCSIDiskReq *r,
+                                   uint8_t *outbuf)
+{
+    static const int rds_caps_size[5] = {
+        [0] = 2048 + 4,
+        [1] = 4 + 4,
+        [3] = 188 + 4,
+        [4] = 2048 + 4,
+    };
+
+    uint8_t media = r->req.cmd.buf[1];
+    uint8_t layer = r->req.cmd.buf[6];
+    uint8_t format = r->req.cmd.buf[7];
+    int size = -1;
+
+    if (s->qdev.type != TYPE_ROM) {
+        return -1;
+    }
+    if (media != 0) {
+        scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
+        return -1;
+    }
+
+    if (format != 0xff) {
+        if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
+            scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
+            return -1;
+        }
+        if (media_is_cd(s)) {
+            scsi_check_condition(r, SENSE_CODE(INCOMPATIBLE_FORMAT));
+            return -1;
+        }
+        if (format >= ARRAY_SIZE(rds_caps_size)) {
+            return -1;
+        }
+        size = rds_caps_size[format];
+        memset(outbuf, 0, size);
+    }
+
+    switch (format) {
+    case 0x00: {
+        /* Physical format information */
+        uint64_t nb_sectors;
+        if (layer != 0) {
+            goto fail;
+        }
+        bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+
+        outbuf[4] = 1;   /* DVD-ROM, part version 1 */
+        outbuf[5] = 0xf; /* 120mm disc, minimum rate unspecified */
+        outbuf[6] = 1;   /* one layer, read-only (per MMC-2 spec) */
+        outbuf[7] = 0;   /* default densities */
+
+        stl_be_p(&outbuf[12], (nb_sectors >> 2) - 1); /* end sector */
+        stl_be_p(&outbuf[16], (nb_sectors >> 2) - 1); /* l0 end sector */
+        break;
+    }
+
+    case 0x01: /* DVD copyright information, all zeros */
+        break;
+
+    case 0x03: /* BCA information - invalid field for no BCA info */
+        return -1;
+
+    case 0x04: /* DVD disc manufacturing information, all zeros */
+        break;
+
+    case 0xff: { /* List capabilities */
+        int i;
+        size = 4;
+        for (i = 0; i < ARRAY_SIZE(rds_caps_size); i++) {
+            if (!rds_caps_size[i]) {
+                continue;
+            }
+            outbuf[size] = i;
+            outbuf[size + 1] = 0x40; /* Not writable, readable */
+            stw_be_p(&outbuf[size + 2], rds_caps_size[i]);
+            size += 4;
+        }
+        break;
+     }
+
+    default:
+        return -1;
+    }
+
+    /* Size of buffer, not including 2 byte size field */
+    stw_be_p(outbuf, size - 2);
+    return size;
+
+fail:
+    return -1;
+}
+
+static int scsi_event_status_media(SCSIDiskState *s, uint8_t *outbuf)
+{
+    uint8_t event_code, media_status;
+
+    media_status = 0;
+    if (s->tray_open) {
+        media_status = MS_TRAY_OPEN;
+    } else if (bdrv_is_inserted(s->qdev.conf.bs)) {
+        media_status = MS_MEDIA_PRESENT;
+    }
+
+    /* Event notification descriptor */
+    event_code = MEC_NO_CHANGE;
+    if (media_status != MS_TRAY_OPEN) {
+        if (s->media_event) {
+            event_code = MEC_NEW_MEDIA;
+            s->media_event = false;
+        } else if (s->eject_request) {
+            event_code = MEC_EJECT_REQUESTED;
+            s->eject_request = false;
+        }
+    }
+
+    outbuf[0] = event_code;
+    outbuf[1] = media_status;
+
+    /* These fields are reserved, just clear them. */
+    outbuf[2] = 0;
+    outbuf[3] = 0;
+    return 4;
+}
+
+static int scsi_get_event_status_notification(SCSIDiskState *s, SCSIDiskReq *r,
+                                              uint8_t *outbuf)
+{
+    int size;
+    uint8_t *buf = r->req.cmd.buf;
+    uint8_t notification_class_request = buf[4];
+    if (s->qdev.type != TYPE_ROM) {
+        return -1;
+    }
+    if ((buf[1] & 1) == 0) {
+        /* asynchronous */
+        return -1;
+    }
+
+    size = 4;
+    outbuf[0] = outbuf[1] = 0;
+    outbuf[3] = 1 << GESN_MEDIA; /* supported events */
+    if (notification_class_request & (1 << GESN_MEDIA)) {
+        outbuf[2] = GESN_MEDIA;
+        size += scsi_event_status_media(s, &outbuf[size]);
+    } else {
+        outbuf[2] = 0x80;
+    }
+    stw_be_p(outbuf, size - 4);
+    return size;
+}
+
+static int scsi_get_configuration(SCSIDiskState *s, uint8_t *outbuf)
+{
+    int current;
+
+    if (s->qdev.type != TYPE_ROM) {
+        return -1;
+    }
+    current = media_is_dvd(s) ? MMC_PROFILE_DVD_ROM : MMC_PROFILE_CD_ROM;
+    memset(outbuf, 0, 40);
+    stl_be_p(&outbuf[0], 36); /* Bytes after the data length field */
+    stw_be_p(&outbuf[6], current);
+    /* outbuf[8] - outbuf[19]: Feature 0 - Profile list */
+    outbuf[10] = 0x03; /* persistent, current */
+    outbuf[11] = 8; /* two profiles */
+    stw_be_p(&outbuf[12], MMC_PROFILE_DVD_ROM);
+    outbuf[14] = (current == MMC_PROFILE_DVD_ROM);
+    stw_be_p(&outbuf[16], MMC_PROFILE_CD_ROM);
+    outbuf[18] = (current == MMC_PROFILE_CD_ROM);
+    /* outbuf[20] - outbuf[31]: Feature 1 - Core feature */
+    stw_be_p(&outbuf[20], 1);
+    outbuf[22] = 0x08 | 0x03; /* version 2, persistent, current */
+    outbuf[23] = 8;
+    stl_be_p(&outbuf[24], 1); /* SCSI */
+    outbuf[28] = 1; /* DBE = 1, mandatory */
+    /* outbuf[32] - outbuf[39]: Feature 3 - Removable media feature */
+    stw_be_p(&outbuf[32], 3);
+    outbuf[34] = 0x08 | 0x03; /* version 2, persistent, current */
+    outbuf[35] = 4;
+    outbuf[36] = 0x39; /* tray, load=1, eject=1, unlocked at powerup, lock=1 */
+    /* TODO: Random readable, CD read, DVD read, drive serial number,
+       power management */
+    return 40;
+}
+
+static int scsi_emulate_mechanism_status(SCSIDiskState *s, uint8_t *outbuf)
+{
+    if (s->qdev.type != TYPE_ROM) {
+        return -1;
+    }
+    memset(outbuf, 0, 8);
+    outbuf[5] = 1; /* CD-ROM */
+    return 8;
+}
+
+static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf,
+                           int page_control)
+{
+    static const int mode_sense_valid[0x3f] = {
+        [MODE_PAGE_HD_GEOMETRY]            = (1 << TYPE_DISK),
+        [MODE_PAGE_FLEXIBLE_DISK_GEOMETRY] = (1 << TYPE_DISK),
+        [MODE_PAGE_CACHING]                = (1 << TYPE_DISK) | (1 << TYPE_ROM),
+        [MODE_PAGE_R_W_ERROR]              = (1 << TYPE_DISK) | (1 << TYPE_ROM),
+        [MODE_PAGE_AUDIO_CTL]              = (1 << TYPE_ROM),
+        [MODE_PAGE_CAPABILITIES]           = (1 << TYPE_ROM),
+    };
+
+    uint8_t *p = *p_outbuf + 2;
+    int length;
+
+    if ((mode_sense_valid[page] & (1 << s->qdev.type)) == 0) {
+        return -1;
+    }
+
+    /*
+     * If Changeable Values are requested, a mask denoting those mode parameters
+     * that are changeable shall be returned. As we currently don't support
+     * parameter changes via MODE_SELECT all bits are returned set to zero.
+     * The buffer was already menset to zero by the caller of this function.
+     *
+     * The offsets here are off by two compared to the descriptions in the
+     * SCSI specs, because those include a 2-byte header.  This is unfortunate,
+     * but it is done so that offsets are consistent within our implementation
+     * of MODE SENSE and MODE SELECT.  MODE SELECT has to deal with both
+     * 2-byte and 4-byte headers.
+     */
+    switch (page) {
+    case MODE_PAGE_HD_GEOMETRY:
+        length = 0x16;
+        if (page_control == 1) { /* Changeable Values */
+            break;
+        }
+        /* if a geometry hint is available, use it */
+        p[0] = (s->qdev.conf.cyls >> 16) & 0xff;
+        p[1] = (s->qdev.conf.cyls >> 8) & 0xff;
+        p[2] = s->qdev.conf.cyls & 0xff;
+        p[3] = s->qdev.conf.heads & 0xff;
+        /* Write precomp start cylinder, disabled */
+        p[4] = (s->qdev.conf.cyls >> 16) & 0xff;
+        p[5] = (s->qdev.conf.cyls >> 8) & 0xff;
+        p[6] = s->qdev.conf.cyls & 0xff;
+        /* Reduced current start cylinder, disabled */
+        p[7] = (s->qdev.conf.cyls >> 16) & 0xff;
+        p[8] = (s->qdev.conf.cyls >> 8) & 0xff;
+        p[9] = s->qdev.conf.cyls & 0xff;
+        /* Device step rate [ns], 200ns */
+        p[10] = 0;
+        p[11] = 200;
+        /* Landing zone cylinder */
+        p[12] = 0xff;
+        p[13] =  0xff;
+        p[14] = 0xff;
+        /* Medium rotation rate [rpm], 5400 rpm */
+        p[18] = (5400 >> 8) & 0xff;
+        p[19] = 5400 & 0xff;
+        break;
+
+    case MODE_PAGE_FLEXIBLE_DISK_GEOMETRY:
+        length = 0x1e;
+        if (page_control == 1) { /* Changeable Values */
+            break;
+        }
+        /* Transfer rate [kbit/s], 5Mbit/s */
+        p[0] = 5000 >> 8;
+        p[1] = 5000 & 0xff;
+        /* if a geometry hint is available, use it */
+        p[2] = s->qdev.conf.heads & 0xff;
+        p[3] = s->qdev.conf.secs & 0xff;
+        p[4] = s->qdev.blocksize >> 8;
+        p[6] = (s->qdev.conf.cyls >> 8) & 0xff;
+        p[7] = s->qdev.conf.cyls & 0xff;
+        /* Write precomp start cylinder, disabled */
+        p[8] = (s->qdev.conf.cyls >> 8) & 0xff;
+        p[9] = s->qdev.conf.cyls & 0xff;
+        /* Reduced current start cylinder, disabled */
+        p[10] = (s->qdev.conf.cyls >> 8) & 0xff;
+        p[11] = s->qdev.conf.cyls & 0xff;
+        /* Device step rate [100us], 100us */
+        p[12] = 0;
+        p[13] = 1;
+        /* Device step pulse width [us], 1us */
+        p[14] = 1;
+        /* Device head settle delay [100us], 100us */
+        p[15] = 0;
+        p[16] = 1;
+        /* Motor on delay [0.1s], 0.1s */
+        p[17] = 1;
+        /* Motor off delay [0.1s], 0.1s */
+        p[18] = 1;
+        /* Medium rotation rate [rpm], 5400 rpm */
+        p[26] = (5400 >> 8) & 0xff;
+        p[27] = 5400 & 0xff;
+        break;
+
+    case MODE_PAGE_CACHING:
+        length = 0x12;
+        if (page_control == 1 || /* Changeable Values */
+            bdrv_enable_write_cache(s->qdev.conf.bs)) {
+            p[0] = 4; /* WCE */
+        }
+        break;
+
+    case MODE_PAGE_R_W_ERROR:
+        length = 10;
+        if (page_control == 1) { /* Changeable Values */
+            break;
+        }
+        p[0] = 0x80; /* Automatic Write Reallocation Enabled */
+        if (s->qdev.type == TYPE_ROM) {
+            p[1] = 0x20; /* Read Retry Count */
+        }
+        break;
+
+    case MODE_PAGE_AUDIO_CTL:
+        length = 14;
+        break;
+
+    case MODE_PAGE_CAPABILITIES:
+        length = 0x14;
+        if (page_control == 1) { /* Changeable Values */
+            break;
+        }
+
+        p[0] = 0x3b; /* CD-R & CD-RW read */
+        p[1] = 0; /* Writing not supported */
+        p[2] = 0x7f; /* Audio, composite, digital out,
+                        mode 2 form 1&2, multi session */
+        p[3] = 0xff; /* CD DA, DA accurate, RW supported,
+                        RW corrected, C2 errors, ISRC,
+                        UPC, Bar code */
+        p[4] = 0x2d | (s->tray_locked ? 2 : 0);
+        /* Locking supported, jumper present, eject, tray */
+        p[5] = 0; /* no volume & mute control, no
+                     changer */
+        p[6] = (50 * 176) >> 8; /* 50x read speed */
+        p[7] = (50 * 176) & 0xff;
+        p[8] = 2 >> 8; /* Two volume levels */
+        p[9] = 2 & 0xff;
+        p[10] = 2048 >> 8; /* 2M buffer */
+        p[11] = 2048 & 0xff;
+        p[12] = (16 * 176) >> 8; /* 16x read speed current */
+        p[13] = (16 * 176) & 0xff;
+        p[16] = (16 * 176) >> 8; /* 16x write speed */
+        p[17] = (16 * 176) & 0xff;
+        p[18] = (16 * 176) >> 8; /* 16x write speed current */
+        p[19] = (16 * 176) & 0xff;
+        break;
+
+    default:
+        return -1;
+    }
+
+    assert(length < 256);
+    (*p_outbuf)[0] = page;
+    (*p_outbuf)[1] = length;
+    *p_outbuf += length + 2;
+    return length + 2;
+}
+
+static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+    uint64_t nb_sectors;
+    bool dbd;
+    int page, buflen, ret, page_control;
+    uint8_t *p;
+    uint8_t dev_specific_param;
+
+    dbd = (r->req.cmd.buf[1] & 0x8) != 0;
+    page = r->req.cmd.buf[2] & 0x3f;
+    page_control = (r->req.cmd.buf[2] & 0xc0) >> 6;
+    DPRINTF("Mode Sense(%d) (page %d, xfer %zd, page_control %d)\n",
+        (r->req.cmd.buf[0] == MODE_SENSE) ? 6 : 10, page, r->req.cmd.xfer, page_control);
+    memset(outbuf, 0, r->req.cmd.xfer);
+    p = outbuf;
+
+    if (s->qdev.type == TYPE_DISK) {
+        dev_specific_param = s->features & (1 << SCSI_DISK_F_DPOFUA) ? 0x10 : 0;
+        if (bdrv_is_read_only(s->qdev.conf.bs)) {
+            dev_specific_param |= 0x80; /* Readonly.  */
+        }
+    } else {
+        /* MMC prescribes that CD/DVD drives have no block descriptors,
+         * and defines no device-specific parameter.  */
+        dev_specific_param = 0x00;
+        dbd = true;
+    }
+
+    if (r->req.cmd.buf[0] == MODE_SENSE) {
+        p[1] = 0; /* Default media type.  */
+        p[2] = dev_specific_param;
+        p[3] = 0; /* Block descriptor length.  */
+        p += 4;
+    } else { /* MODE_SENSE_10 */
+        p[2] = 0; /* Default media type.  */
+        p[3] = dev_specific_param;
+        p[6] = p[7] = 0; /* Block descriptor length.  */
+        p += 8;
+    }
+
+    bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+    if (!dbd && nb_sectors) {
+        if (r->req.cmd.buf[0] == MODE_SENSE) {
+            outbuf[3] = 8; /* Block descriptor length  */
+        } else { /* MODE_SENSE_10 */
+            outbuf[7] = 8; /* Block descriptor length  */
+        }
+        nb_sectors /= (s->qdev.blocksize / 512);
+        if (nb_sectors > 0xffffff) {
+            nb_sectors = 0;
+        }
+        p[0] = 0; /* media density code */
+        p[1] = (nb_sectors >> 16) & 0xff;
+        p[2] = (nb_sectors >> 8) & 0xff;
+        p[3] = nb_sectors & 0xff;
+        p[4] = 0; /* reserved */
+        p[5] = 0; /* bytes 5-7 are the sector size in bytes */
+        p[6] = s->qdev.blocksize >> 8;
+        p[7] = 0;
+        p += 8;
+    }
+
+    if (page_control == 3) {
+        /* Saved Values */
+        scsi_check_condition(r, SENSE_CODE(SAVING_PARAMS_NOT_SUPPORTED));
+        return -1;
+    }
+
+    if (page == 0x3f) {
+        for (page = 0; page <= 0x3e; page++) {
+            mode_sense_page(s, page, &p, page_control);
+        }
+    } else {
+        ret = mode_sense_page(s, page, &p, page_control);
+        if (ret == -1) {
+            return -1;
+        }
+    }
+
+    buflen = p - outbuf;
+    /*
+     * The mode data length field specifies the length in bytes of the
+     * following data that is available to be transferred. The mode data
+     * length does not include itself.
+     */
+    if (r->req.cmd.buf[0] == MODE_SENSE) {
+        outbuf[0] = buflen - 1;
+    } else { /* MODE_SENSE_10 */
+        outbuf[0] = ((buflen - 2) >> 8) & 0xff;
+        outbuf[1] = (buflen - 2) & 0xff;
+    }
+    return buflen;
+}
+
+static int scsi_disk_emulate_read_toc(SCSIRequest *req, uint8_t *outbuf)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
+    int start_track, format, msf, toclen;
+    uint64_t nb_sectors;
+
+    msf = req->cmd.buf[1] & 2;
+    format = req->cmd.buf[2] & 0xf;
+    start_track = req->cmd.buf[6];
+    bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+    DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
+    nb_sectors /= s->qdev.blocksize / 512;
+    switch (format) {
+    case 0:
+        toclen = cdrom_read_toc(nb_sectors, outbuf, msf, start_track);
+        break;
+    case 1:
+        /* multi session : only a single session defined */
+        toclen = 12;
+        memset(outbuf, 0, 12);
+        outbuf[1] = 0x0a;
+        outbuf[2] = 0x01;
+        outbuf[3] = 0x01;
+        break;
+    case 2:
+        toclen = cdrom_read_toc_raw(nb_sectors, outbuf, msf, start_track);
+        break;
+    default:
+        return -1;
+    }
+    return toclen;
+}
+
+static int scsi_disk_emulate_start_stop(SCSIDiskReq *r)
+{
+    SCSIRequest *req = &r->req;
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
+    bool start = req->cmd.buf[4] & 1;
+    bool loej = req->cmd.buf[4] & 2; /* load on start, eject on !start */
+    int pwrcnd = req->cmd.buf[4] & 0xf0;
+
+    if (pwrcnd) {
+        /* eject/load only happens for power condition == 0 */
+        return 0;
+    }
+
+    if ((s->features & (1 << SCSI_DISK_F_REMOVABLE)) && loej) {
+        if (!start && !s->tray_open && s->tray_locked) {
+            scsi_check_condition(r,
+                                 bdrv_is_inserted(s->qdev.conf.bs)
+                                 ? SENSE_CODE(ILLEGAL_REQ_REMOVAL_PREVENTED)
+                                 : SENSE_CODE(NOT_READY_REMOVAL_PREVENTED));
+            return -1;
+        }
+
+        if (s->tray_open != !start) {
+            bdrv_eject(s->qdev.conf.bs, !start);
+            s->tray_open = !start;
+        }
+    }
+    return 0;
+}
+
+static void scsi_disk_emulate_read_data(SCSIRequest *req)
+{
+    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+    int buflen = r->iov.iov_len;
+
+    if (buflen) {
+        DPRINTF("Read buf_len=%d\n", buflen);
+        r->iov.iov_len = 0;
+        r->started = true;
+        scsi_req_data(&r->req, buflen);
+        return;
+    }
+
+    /* This also clears the sense buffer for REQUEST SENSE.  */
+    scsi_req_complete(&r->req, GOOD);
+}
+
+static int scsi_disk_check_mode_select(SCSIDiskState *s, int page,
+                                       uint8_t *inbuf, int inlen)
+{
+    uint8_t mode_current[SCSI_MAX_MODE_LEN];
+    uint8_t mode_changeable[SCSI_MAX_MODE_LEN];
+    uint8_t *p;
+    int len, expected_len, changeable_len, i;
+
+    /* The input buffer does not include the page header, so it is
+     * off by 2 bytes.
+     */
+    expected_len = inlen + 2;
+    if (expected_len > SCSI_MAX_MODE_LEN) {
+        return -1;
+    }
+
+    p = mode_current;
+    memset(mode_current, 0, inlen + 2);
+    len = mode_sense_page(s, page, &p, 0);
+    if (len < 0 || len != expected_len) {
+        return -1;
+    }
+
+    p = mode_changeable;
+    memset(mode_changeable, 0, inlen + 2);
+    changeable_len = mode_sense_page(s, page, &p, 1);
+    assert(changeable_len == len);
+
+    /* Check that unchangeable bits are the same as what MODE SENSE
+     * would return.
+     */
+    for (i = 2; i < len; i++) {
+        if (((mode_current[i] ^ inbuf[i - 2]) & ~mode_changeable[i]) != 0) {
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static void scsi_disk_apply_mode_select(SCSIDiskState *s, int page, uint8_t *p)
+{
+    switch (page) {
+    case MODE_PAGE_CACHING:
+        bdrv_set_enable_write_cache(s->qdev.conf.bs, (p[0] & 4) != 0);
+        break;
+
+    default:
+        break;
+    }
+}
+
+static int mode_select_pages(SCSIDiskReq *r, uint8_t *p, int len, bool change)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+    while (len > 0) {
+        int page, subpage, page_len;
+
+        /* Parse both possible formats for the mode page headers.  */
+        page = p[0] & 0x3f;
+        if (p[0] & 0x40) {
+            if (len < 4) {
+                goto invalid_param_len;
+            }
+            subpage = p[1];
+            page_len = lduw_be_p(&p[2]);
+            p += 4;
+            len -= 4;
+        } else {
+            if (len < 2) {
+                goto invalid_param_len;
+            }
+            subpage = 0;
+            page_len = p[1];
+            p += 2;
+            len -= 2;
+        }
+
+        if (subpage) {
+            goto invalid_param;
+        }
+        if (page_len > len) {
+            goto invalid_param_len;
+        }
+
+        if (!change) {
+            if (scsi_disk_check_mode_select(s, page, p, page_len) < 0) {
+                goto invalid_param;
+            }
+        } else {
+            scsi_disk_apply_mode_select(s, page, p);
+        }
+
+        p += page_len;
+        len -= page_len;
+    }
+    return 0;
+
+invalid_param:
+    scsi_check_condition(r, SENSE_CODE(INVALID_PARAM));
+    return -1;
+
+invalid_param_len:
+    scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN));
+    return -1;
+}
+
+static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+    uint8_t *p = inbuf;
+    int cmd = r->req.cmd.buf[0];
+    int len = r->req.cmd.xfer;
+    int hdr_len = (cmd == MODE_SELECT ? 4 : 8);
+    int bd_len;
+    int pass;
+
+    /* We only support PF=1, SP=0.  */
+    if ((r->req.cmd.buf[1] & 0x11) != 0x10) {
+        goto invalid_field;
+    }
+
+    if (len < hdr_len) {
+        goto invalid_param_len;
+    }
+
+    bd_len = (cmd == MODE_SELECT ? p[3] : lduw_be_p(&p[6]));
+    len -= hdr_len;
+    p += hdr_len;
+    if (len < bd_len) {
+        goto invalid_param_len;
+    }
+    if (bd_len != 0 && bd_len != 8) {
+        goto invalid_param;
+    }
+
+    len -= bd_len;
+    p += bd_len;
+
+    /* Ensure no change is made if there is an error!  */
+    for (pass = 0; pass < 2; pass++) {
+        if (mode_select_pages(r, p, len, pass == 1) < 0) {
+            assert(pass == 0);
+            return;
+        }
+    }
+    if (!bdrv_enable_write_cache(s->qdev.conf.bs)) {
+        /* The request is used as the AIO opaque value, so add a ref.  */
+        scsi_req_ref(&r->req);
+        bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
+        r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_aio_complete, r);
+        return;
+    }
+
+    scsi_req_complete(&r->req, GOOD);
+    return;
+
+invalid_param:
+    scsi_check_condition(r, SENSE_CODE(INVALID_PARAM));
+    return;
+
+invalid_param_len:
+    scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN));
+    return;
+
+invalid_field:
+    scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
+}
+
+static inline bool check_lba_range(SCSIDiskState *s,
+                                   uint64_t sector_num, uint32_t nb_sectors)
+{
+    /*
+     * The first line tests that no overflow happens when computing the last
+     * sector.  The second line tests that the last accessed sector is in
+     * range.
+     *
+     * Careful, the computations should not underflow for nb_sectors == 0,
+     * and a 0-block read to the first LBA beyond the end of device is
+     * valid.
+     */
+    return (sector_num <= sector_num + nb_sectors &&
+            sector_num + nb_sectors <= s->qdev.max_lba + 1);
+}
+
+typedef struct UnmapCBData {
+    SCSIDiskReq *r;
+    uint8_t *inbuf;
+    int count;
+} UnmapCBData;
+
+static void scsi_unmap_complete(void *opaque, int ret)
+{
+    UnmapCBData *data = opaque;
+    SCSIDiskReq *r = data->r;
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+    uint64_t sector_num;
+    uint32_t nb_sectors;
+
+    r->req.aiocb = NULL;
+    if (r->req.io_canceled) {
+        goto done;
+    }
+
+    if (ret < 0) {
+        if (scsi_handle_rw_error(r, -ret)) {
+            goto done;
+        }
+    }
+
+    if (data->count > 0) {
+        sector_num = ldq_be_p(&data->inbuf[0]);
+        nb_sectors = ldl_be_p(&data->inbuf[8]) & 0xffffffffULL;
+        if (!check_lba_range(s, sector_num, nb_sectors)) {
+            scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
+            goto done;
+        }
+
+        r->req.aiocb = bdrv_aio_discard(s->qdev.conf.bs,
+                                        sector_num * (s->qdev.blocksize / 512),
+                                        nb_sectors * (s->qdev.blocksize / 512),
+                                        scsi_unmap_complete, data);
+        data->count--;
+        data->inbuf += 16;
+        return;
+    }
+
+    scsi_req_complete(&r->req, GOOD);
+
+done:
+    if (!r->req.io_canceled) {
+        scsi_req_unref(&r->req);
+    }
+    g_free(data);
+}
+
+static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
+{
+    uint8_t *p = inbuf;
+    int len = r->req.cmd.xfer;
+    UnmapCBData *data;
+
+    if (len < 8) {
+        goto invalid_param_len;
+    }
+    if (len < lduw_be_p(&p[0]) + 2) {
+        goto invalid_param_len;
+    }
+    if (len < lduw_be_p(&p[2]) + 8) {
+        goto invalid_param_len;
+    }
+    if (lduw_be_p(&p[2]) & 15) {
+        goto invalid_param_len;
+    }
+
+    data = g_new0(UnmapCBData, 1);
+    data->r = r;
+    data->inbuf = &p[8];
+    data->count = lduw_be_p(&p[2]) >> 4;
+
+    /* The matching unref is in scsi_unmap_complete, before data is freed.  */
+    scsi_req_ref(&r->req);
+    scsi_unmap_complete(data, 0);
+    return;
+
+invalid_param_len:
+    scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN));
+}
+
+static void scsi_disk_emulate_write_data(SCSIRequest *req)
+{
+    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
+    if (r->iov.iov_len) {
+        int buflen = r->iov.iov_len;
+        DPRINTF("Write buf_len=%d\n", buflen);
+        r->iov.iov_len = 0;
+        scsi_req_data(&r->req, buflen);
+        return;
+    }
+
+    switch (req->cmd.buf[0]) {
+    case MODE_SELECT:
+    case MODE_SELECT_10:
+        /* This also clears the sense buffer for REQUEST SENSE.  */
+        scsi_disk_emulate_mode_select(r, r->iov.iov_base);
+        break;
+
+    case UNMAP:
+        scsi_disk_emulate_unmap(r, r->iov.iov_base);
+        break;
+
+    default:
+        abort();
+    }
+}
+
+static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
+{
+    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
+    uint64_t nb_sectors;
+    uint8_t *outbuf;
+    int buflen;
+
+    switch (req->cmd.buf[0]) {
+    case INQUIRY:
+    case MODE_SENSE:
+    case MODE_SENSE_10:
+    case RESERVE:
+    case RESERVE_10:
+    case RELEASE:
+    case RELEASE_10:
+    case START_STOP:
+    case ALLOW_MEDIUM_REMOVAL:
+    case GET_CONFIGURATION:
+    case GET_EVENT_STATUS_NOTIFICATION:
+    case MECHANISM_STATUS:
+    case REQUEST_SENSE:
+        break;
+
+    default:
+        if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
+            scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
+            return 0;
+        }
+        break;
+    }
+
+    /*
+     * FIXME: we shouldn't return anything bigger than 4k, but the code
+     * requires the buffer to be as big as req->cmd.xfer in several
+     * places.  So, do not allow CDBs with a very large ALLOCATION
+     * LENGTH.  The real fix would be to modify scsi_read_data and
+     * dma_buf_read, so that they return data beyond the buflen
+     * as all zeros.
+     */
+    if (req->cmd.xfer > 65536) {
+        goto illegal_request;
+    }
+    r->buflen = MAX(4096, req->cmd.xfer);
+
+    if (!r->iov.iov_base) {
+        r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen);
+    }
+
+    buflen = req->cmd.xfer;
+    outbuf = r->iov.iov_base;
+    memset(outbuf, 0, r->buflen);
+    switch (req->cmd.buf[0]) {
+    case TEST_UNIT_READY:
+        assert(!s->tray_open && bdrv_is_inserted(s->qdev.conf.bs));
+        break;
+    case INQUIRY:
+        buflen = scsi_disk_emulate_inquiry(req, outbuf);
+        if (buflen < 0) {
+            goto illegal_request;
+        }
+        break;
+    case MODE_SENSE:
+    case MODE_SENSE_10:
+        buflen = scsi_disk_emulate_mode_sense(r, outbuf);
+        if (buflen < 0) {
+            goto illegal_request;
+        }
+        break;
+    case READ_TOC:
+        buflen = scsi_disk_emulate_read_toc(req, outbuf);
+        if (buflen < 0) {
+            goto illegal_request;
+        }
+        break;
+    case RESERVE:
+        if (req->cmd.buf[1] & 1) {
+            goto illegal_request;
+        }
+        break;
+    case RESERVE_10:
+        if (req->cmd.buf[1] & 3) {
+            goto illegal_request;
+        }
+        break;
+    case RELEASE:
+        if (req->cmd.buf[1] & 1) {
+            goto illegal_request;
+        }
+        break;
+    case RELEASE_10:
+        if (req->cmd.buf[1] & 3) {
+            goto illegal_request;
+        }
+        break;
+    case START_STOP:
+        if (scsi_disk_emulate_start_stop(r) < 0) {
+            return 0;
+        }
+        break;
+    case ALLOW_MEDIUM_REMOVAL:
+        s->tray_locked = req->cmd.buf[4] & 1;
+        bdrv_lock_medium(s->qdev.conf.bs, req->cmd.buf[4] & 1);
+        break;
+    case READ_CAPACITY_10:
+        /* The normal LEN field for this command is zero.  */
+        memset(outbuf, 0, 8);
+        bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+        if (!nb_sectors) {
+            scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY));
+            return 0;
+        }
+        if ((req->cmd.buf[8] & 1) == 0 && req->cmd.lba) {
+            goto illegal_request;
+        }
+        nb_sectors /= s->qdev.blocksize / 512;
+        /* Returned value is the address of the last sector.  */
+        nb_sectors--;
+        /* Remember the new size for read/write sanity checking. */
+        s->qdev.max_lba = nb_sectors;
+        /* Clip to 2TB, instead of returning capacity modulo 2TB. */
+        if (nb_sectors > UINT32_MAX) {
+            nb_sectors = UINT32_MAX;
+        }
+        outbuf[0] = (nb_sectors >> 24) & 0xff;
+        outbuf[1] = (nb_sectors >> 16) & 0xff;
+        outbuf[2] = (nb_sectors >> 8) & 0xff;
+        outbuf[3] = nb_sectors & 0xff;
+        outbuf[4] = 0;
+        outbuf[5] = 0;
+        outbuf[6] = s->qdev.blocksize >> 8;
+        outbuf[7] = 0;
+        break;
+    case REQUEST_SENSE:
+        /* Just return "NO SENSE".  */
+        buflen = scsi_build_sense(NULL, 0, outbuf, r->buflen,
+                                  (req->cmd.buf[1] & 1) == 0);
+        if (buflen < 0) {
+            goto illegal_request;
+        }
+        break;
+    case MECHANISM_STATUS:
+        buflen = scsi_emulate_mechanism_status(s, outbuf);
+        if (buflen < 0) {
+            goto illegal_request;
+        }
+        break;
+    case GET_CONFIGURATION:
+        buflen = scsi_get_configuration(s, outbuf);
+        if (buflen < 0) {
+            goto illegal_request;
+        }
+        break;
+    case GET_EVENT_STATUS_NOTIFICATION:
+        buflen = scsi_get_event_status_notification(s, r, outbuf);
+        if (buflen < 0) {
+            goto illegal_request;
+        }
+        break;
+    case READ_DISC_INFORMATION:
+        buflen = scsi_read_disc_information(s, r, outbuf);
+        if (buflen < 0) {
+            goto illegal_request;
+        }
+        break;
+    case READ_DVD_STRUCTURE:
+        buflen = scsi_read_dvd_structure(s, r, outbuf);
+        if (buflen < 0) {
+            goto illegal_request;
+        }
+        break;
+    case SERVICE_ACTION_IN_16:
+        /* Service Action In subcommands. */
+        if ((req->cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
+            DPRINTF("SAI READ CAPACITY(16)\n");
+            memset(outbuf, 0, req->cmd.xfer);
+            bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+            if (!nb_sectors) {
+                scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY));
+                return 0;
+            }
+            if ((req->cmd.buf[14] & 1) == 0 && req->cmd.lba) {
+                goto illegal_request;
+            }
+            nb_sectors /= s->qdev.blocksize / 512;
+            /* Returned value is the address of the last sector.  */
+            nb_sectors--;
+            /* Remember the new size for read/write sanity checking. */
+            s->qdev.max_lba = nb_sectors;
+            outbuf[0] = (nb_sectors >> 56) & 0xff;
+            outbuf[1] = (nb_sectors >> 48) & 0xff;
+            outbuf[2] = (nb_sectors >> 40) & 0xff;
+            outbuf[3] = (nb_sectors >> 32) & 0xff;
+            outbuf[4] = (nb_sectors >> 24) & 0xff;
+            outbuf[5] = (nb_sectors >> 16) & 0xff;
+            outbuf[6] = (nb_sectors >> 8) & 0xff;
+            outbuf[7] = nb_sectors & 0xff;
+            outbuf[8] = 0;
+            outbuf[9] = 0;
+            outbuf[10] = s->qdev.blocksize >> 8;
+            outbuf[11] = 0;
+            outbuf[12] = 0;
+            outbuf[13] = get_physical_block_exp(&s->qdev.conf);
+
+            /* set TPE bit if the format supports discard */
+            if (s->qdev.conf.discard_granularity) {
+                outbuf[14] = 0x80;
+            }
+
+            /* Protection, exponent and lowest lba field left blank. */
+            break;
+        }
+        DPRINTF("Unsupported Service Action In\n");
+        goto illegal_request;
+    case SYNCHRONIZE_CACHE:
+        /* The request is used as the AIO opaque value, so add a ref.  */
+        scsi_req_ref(&r->req);
+        bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
+        r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_aio_complete, r);
+        return 0;
+    case SEEK_10:
+        DPRINTF("Seek(10) (sector %" PRId64 ")\n", r->req.cmd.lba);
+        if (r->req.cmd.lba > s->qdev.max_lba) {
+            goto illegal_lba;
+        }
+        break;
+    case MODE_SELECT:
+        DPRINTF("Mode Select(6) (len %lu)\n", (long)r->req.cmd.xfer);
+        break;
+    case MODE_SELECT_10:
+        DPRINTF("Mode Select(10) (len %lu)\n", (long)r->req.cmd.xfer);
+        break;
+    case UNMAP:
+        DPRINTF("Unmap (len %lu)\n", (long)r->req.cmd.xfer);
+        break;
+    case WRITE_SAME_10:
+    case WRITE_SAME_16:
+        nb_sectors = scsi_data_cdb_length(r->req.cmd.buf);
+        if (bdrv_is_read_only(s->qdev.conf.bs)) {
+            scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
+            return 0;
+        }
+        if (!check_lba_range(s, r->req.cmd.lba, nb_sectors)) {
+            goto illegal_lba;
+        }
+
+        /*
+         * We only support WRITE SAME with the unmap bit set for now.
+         */
+        if (!(req->cmd.buf[1] & 0x8)) {
+            goto illegal_request;
+        }
+
+        /* The request is used as the AIO opaque value, so add a ref.  */
+        scsi_req_ref(&r->req);
+        r->req.aiocb = bdrv_aio_discard(s->qdev.conf.bs,
+                                        r->req.cmd.lba * (s->qdev.blocksize / 512),
+                                        nb_sectors * (s->qdev.blocksize / 512),
+                                        scsi_aio_complete, r);
+        return 0;
+    default:
+        DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
+        scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
+        return 0;
+    }
+    assert(!r->req.aiocb);
+    r->iov.iov_len = MIN(r->buflen, req->cmd.xfer);
+    if (r->iov.iov_len == 0) {
+        scsi_req_complete(&r->req, GOOD);
+    }
+    if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+        assert(r->iov.iov_len == req->cmd.xfer);
+        return -r->iov.iov_len;
+    } else {
+        return r->iov.iov_len;
+    }
+
+illegal_request:
+    if (r->req.status == -1) {
+        scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
+    }
+    return 0;
+
+illegal_lba:
+    scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
+    return 0;
+}
+
+/* Execute a scsi command.  Returns the length of the data expected by the
+   command.  This will be Positive for data transfers from the device
+   (eg. disk reads), negative for transfers to the device (eg. disk writes),
+   and zero if the command does not transfer any data.  */
+
+static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
+{
+    SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
+    uint32_t len;
+    uint8_t command;
+
+    command = buf[0];
+
+    if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
+        scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
+        return 0;
+    }
+
+    len = scsi_data_cdb_length(r->req.cmd.buf);
+    switch (command) {
+    case READ_6:
+    case READ_10:
+    case READ_12:
+    case READ_16:
+        DPRINTF("Read (sector %" PRId64 ", count %u)\n", r->req.cmd.lba, len);
+        if (r->req.cmd.buf[1] & 0xe0) {
+            goto illegal_request;
+        }
+        if (!check_lba_range(s, r->req.cmd.lba, len)) {
+            goto illegal_lba;
+        }
+        r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512);
+        r->sector_count = len * (s->qdev.blocksize / 512);
+        break;
+    case WRITE_6:
+    case WRITE_10:
+    case WRITE_12:
+    case WRITE_16:
+    case WRITE_VERIFY_10:
+    case WRITE_VERIFY_12:
+    case WRITE_VERIFY_16:
+        if (bdrv_is_read_only(s->qdev.conf.bs)) {
+            scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
+            return 0;
+        }
+        /* fallthrough */
+    case VERIFY_10:
+    case VERIFY_12:
+    case VERIFY_16:
+        DPRINTF("Write %s(sector %" PRId64 ", count %u)\n",
+                (command & 0xe) == 0xe ? "And Verify " : "",
+                r->req.cmd.lba, len);
+        if (r->req.cmd.buf[1] & 0xe0) {
+            goto illegal_request;
+        }
+        if (!check_lba_range(s, r->req.cmd.lba, len)) {
+            goto illegal_lba;
+        }
+        r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512);
+        r->sector_count = len * (s->qdev.blocksize / 512);
+        break;
+    default:
+        abort();
+    illegal_request:
+        scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
+        return 0;
+    illegal_lba:
+        scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
+        return 0;
+    }
+    if (r->sector_count == 0) {
+        scsi_req_complete(&r->req, GOOD);
+    }
+    assert(r->iov.iov_len == 0);
+    if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+        return -r->sector_count * 512;
+    } else {
+        return r->sector_count * 512;
+    }
+}
+
+static void scsi_disk_reset(DeviceState *dev)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev);
+    uint64_t nb_sectors;
+
+    scsi_device_purge_requests(&s->qdev, SENSE_CODE(RESET));
+
+    bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+    nb_sectors /= s->qdev.blocksize / 512;
+    if (nb_sectors) {
+        nb_sectors--;
+    }
+    s->qdev.max_lba = nb_sectors;
+}
+
+static void scsi_destroy(SCSIDevice *dev)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+
+    scsi_device_purge_requests(&s->qdev, SENSE_CODE(NO_SENSE));
+    blockdev_mark_auto_del(s->qdev.conf.bs);
+}
+
+static void scsi_disk_resize_cb(void *opaque)
+{
+    SCSIDiskState *s = opaque;
+
+    /* SPC lists this sense code as available only for
+     * direct-access devices.
+     */
+    if (s->qdev.type == TYPE_DISK) {
+        scsi_device_report_change(&s->qdev, SENSE_CODE(CAPACITY_CHANGED));
+    }
+}
+
+static void scsi_cd_change_media_cb(void *opaque, bool load)
+{
+    SCSIDiskState *s = opaque;
+
+    /*
+     * When a CD gets changed, we have to report an ejected state and
+     * then a loaded state to guests so that they detect tray
+     * open/close and media change events.  Guests that do not use
+     * GET_EVENT_STATUS_NOTIFICATION to detect such tray open/close
+     * states rely on this behavior.
+     *
+     * media_changed governs the state machine used for unit attention
+     * report.  media_event is used by GET EVENT STATUS NOTIFICATION.
+     */
+    s->media_changed = load;
+    s->tray_open = !load;
+    scsi_device_set_ua(&s->qdev, SENSE_CODE(UNIT_ATTENTION_NO_MEDIUM));
+    s->media_event = true;
+    s->eject_request = false;
+}
+
+static void scsi_cd_eject_request_cb(void *opaque, bool force)
+{
+    SCSIDiskState *s = opaque;
+
+    s->eject_request = true;
+    if (force) {
+        s->tray_locked = false;
+    }
+}
+
+static bool scsi_cd_is_tray_open(void *opaque)
+{
+    return ((SCSIDiskState *)opaque)->tray_open;
+}
+
+static bool scsi_cd_is_medium_locked(void *opaque)
+{
+    return ((SCSIDiskState *)opaque)->tray_locked;
+}
+
+static const BlockDevOps scsi_disk_removable_block_ops = {
+    .change_media_cb = scsi_cd_change_media_cb,
+    .eject_request_cb = scsi_cd_eject_request_cb,
+    .is_tray_open = scsi_cd_is_tray_open,
+    .is_medium_locked = scsi_cd_is_medium_locked,
+
+    .resize_cb = scsi_disk_resize_cb,
+};
+
+static const BlockDevOps scsi_disk_block_ops = {
+    .resize_cb = scsi_disk_resize_cb,
+};
+
+static void scsi_disk_unit_attention_reported(SCSIDevice *dev)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+    if (s->media_changed) {
+        s->media_changed = false;
+        scsi_device_set_ua(&s->qdev, SENSE_CODE(MEDIUM_CHANGED));
+    }
+}
+
+static int scsi_initfn(SCSIDevice *dev)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+
+    if (!s->qdev.conf.bs) {
+        error_report("drive property not set");
+        return -1;
+    }
+
+    if (!(s->features & (1 << SCSI_DISK_F_REMOVABLE)) &&
+        !bdrv_is_inserted(s->qdev.conf.bs)) {
+        error_report("Device needs media, but drive is empty");
+        return -1;
+    }
+
+    blkconf_serial(&s->qdev.conf, &s->serial);
+    if (dev->type == TYPE_DISK
+        && blkconf_geometry(&dev->conf, NULL, 65535, 255, 255) < 0) {
+        return -1;
+    }
+
+    if (s->qdev.conf.discard_granularity == -1) {
+        s->qdev.conf.discard_granularity =
+            MAX(s->qdev.conf.logical_block_size, DEFAULT_DISCARD_GRANULARITY);
+    }
+
+    if (!s->version) {
+        s->version = g_strdup(qemu_get_version());
+    }
+    if (!s->vendor) {
+        s->vendor = g_strdup("QEMU");
+    }
+
+    if (bdrv_is_sg(s->qdev.conf.bs)) {
+        error_report("unwanted /dev/sg*");
+        return -1;
+    }
+
+    if (s->features & (1 << SCSI_DISK_F_REMOVABLE)) {
+        bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_disk_removable_block_ops, s);
+    } else {
+        bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_disk_block_ops, s);
+    }
+    bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize);
+
+    bdrv_iostatus_enable(s->qdev.conf.bs);
+    add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, NULL);
+    return 0;
+}
+
+static int scsi_hd_initfn(SCSIDevice *dev)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+    s->qdev.blocksize = s->qdev.conf.logical_block_size;
+    s->qdev.type = TYPE_DISK;
+    if (!s->product) {
+        s->product = g_strdup("QEMU HARDDISK");
+    }
+    return scsi_initfn(&s->qdev);
+}
+
+static int scsi_cd_initfn(SCSIDevice *dev)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+    s->qdev.blocksize = 2048;
+    s->qdev.type = TYPE_ROM;
+    s->features |= 1 << SCSI_DISK_F_REMOVABLE;
+    if (!s->product) {
+        s->product = g_strdup("QEMU CD-ROM");
+    }
+    return scsi_initfn(&s->qdev);
+}
+
+static int scsi_disk_initfn(SCSIDevice *dev)
+{
+    DriveInfo *dinfo;
+
+    if (!dev->conf.bs) {
+        return scsi_initfn(dev);  /* ... and die there */
+    }
+
+    dinfo = drive_get_by_blockdev(dev->conf.bs);
+    if (dinfo->media_cd) {
+        return scsi_cd_initfn(dev);
+    } else {
+        return scsi_hd_initfn(dev);
+    }
+}
+
+static const SCSIReqOps scsi_disk_emulate_reqops = {
+    .size         = sizeof(SCSIDiskReq),
+    .free_req     = scsi_free_request,
+    .send_command = scsi_disk_emulate_command,
+    .read_data    = scsi_disk_emulate_read_data,
+    .write_data   = scsi_disk_emulate_write_data,
+    .get_buf      = scsi_get_buf,
+};
+
+static const SCSIReqOps scsi_disk_dma_reqops = {
+    .size         = sizeof(SCSIDiskReq),
+    .free_req     = scsi_free_request,
+    .send_command = scsi_disk_dma_command,
+    .read_data    = scsi_read_data,
+    .write_data   = scsi_write_data,
+    .cancel_io    = scsi_cancel_io,
+    .get_buf      = scsi_get_buf,
+    .load_request = scsi_disk_load_request,
+    .save_request = scsi_disk_save_request,
+};
+
+static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = {
+    [TEST_UNIT_READY]                 = &scsi_disk_emulate_reqops,
+    [INQUIRY]                         = &scsi_disk_emulate_reqops,
+    [MODE_SENSE]                      = &scsi_disk_emulate_reqops,
+    [MODE_SENSE_10]                   = &scsi_disk_emulate_reqops,
+    [START_STOP]                      = &scsi_disk_emulate_reqops,
+    [ALLOW_MEDIUM_REMOVAL]            = &scsi_disk_emulate_reqops,
+    [READ_CAPACITY_10]                = &scsi_disk_emulate_reqops,
+    [READ_TOC]                        = &scsi_disk_emulate_reqops,
+    [READ_DVD_STRUCTURE]              = &scsi_disk_emulate_reqops,
+    [READ_DISC_INFORMATION]           = &scsi_disk_emulate_reqops,
+    [GET_CONFIGURATION]               = &scsi_disk_emulate_reqops,
+    [GET_EVENT_STATUS_NOTIFICATION]   = &scsi_disk_emulate_reqops,
+    [MECHANISM_STATUS]                = &scsi_disk_emulate_reqops,
+    [SERVICE_ACTION_IN_16]            = &scsi_disk_emulate_reqops,
+    [REQUEST_SENSE]                   = &scsi_disk_emulate_reqops,
+    [SYNCHRONIZE_CACHE]               = &scsi_disk_emulate_reqops,
+    [SEEK_10]                         = &scsi_disk_emulate_reqops,
+    [MODE_SELECT]                     = &scsi_disk_emulate_reqops,
+    [MODE_SELECT_10]                  = &scsi_disk_emulate_reqops,
+    [UNMAP]                           = &scsi_disk_emulate_reqops,
+    [WRITE_SAME_10]                   = &scsi_disk_emulate_reqops,
+    [WRITE_SAME_16]                   = &scsi_disk_emulate_reqops,
+
+    [READ_6]                          = &scsi_disk_dma_reqops,
+    [READ_10]                         = &scsi_disk_dma_reqops,
+    [READ_12]                         = &scsi_disk_dma_reqops,
+    [READ_16]                         = &scsi_disk_dma_reqops,
+    [VERIFY_10]                       = &scsi_disk_dma_reqops,
+    [VERIFY_12]                       = &scsi_disk_dma_reqops,
+    [VERIFY_16]                       = &scsi_disk_dma_reqops,
+    [WRITE_6]                         = &scsi_disk_dma_reqops,
+    [WRITE_10]                        = &scsi_disk_dma_reqops,
+    [WRITE_12]                        = &scsi_disk_dma_reqops,
+    [WRITE_16]                        = &scsi_disk_dma_reqops,
+    [WRITE_VERIFY_10]                 = &scsi_disk_dma_reqops,
+    [WRITE_VERIFY_12]                 = &scsi_disk_dma_reqops,
+    [WRITE_VERIFY_16]                 = &scsi_disk_dma_reqops,
+};
+
+static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
+                                     uint8_t *buf, void *hba_private)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
+    SCSIRequest *req;
+    const SCSIReqOps *ops;
+    uint8_t command;
+
+    command = buf[0];
+    ops = scsi_disk_reqops_dispatch[command];
+    if (!ops) {
+        ops = &scsi_disk_emulate_reqops;
+    }
+    req = scsi_req_alloc(ops, &s->qdev, tag, lun, hba_private);
+
+#ifdef DEBUG_SCSI
+    DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]);
+    {
+        int i;
+        for (i = 1; i < req->cmd.len; i++) {
+            printf(" 0x%02x", buf[i]);
+        }
+        printf("\n");
+    }
+#endif
+
+    return req;
+}
+
+#ifdef __linux__
+static int get_device_type(SCSIDiskState *s)
+{
+    BlockDriverState *bdrv = s->qdev.conf.bs;
+    uint8_t cmd[16];
+    uint8_t buf[36];
+    uint8_t sensebuf[8];
+    sg_io_hdr_t io_header;
+    int ret;
+
+    memset(cmd, 0, sizeof(cmd));
+    memset(buf, 0, sizeof(buf));
+    cmd[0] = INQUIRY;
+    cmd[4] = sizeof(buf);
+
+    memset(&io_header, 0, sizeof(io_header));
+    io_header.interface_id = 'S';
+    io_header.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_header.dxfer_len = sizeof(buf);
+    io_header.dxferp = buf;
+    io_header.cmdp = cmd;
+    io_header.cmd_len = sizeof(cmd);
+    io_header.mx_sb_len = sizeof(sensebuf);
+    io_header.sbp = sensebuf;
+    io_header.timeout = 6000; /* XXX */
+
+    ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
+    if (ret < 0 || io_header.driver_status || io_header.host_status) {
+        return -1;
+    }
+    s->qdev.type = buf[0];
+    if (buf[1] & 0x80) {
+        s->features |= 1 << SCSI_DISK_F_REMOVABLE;
+    }
+    return 0;
+}
+
+static int scsi_block_initfn(SCSIDevice *dev)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+    int sg_version;
+    int rc;
+
+    if (!s->qdev.conf.bs) {
+        error_report("scsi-block: drive property not set");
+        return -1;
+    }
+
+    /* check we are using a driver managing SG_IO (version 3 and after) */
+    if (bdrv_ioctl(s->qdev.conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0 ||
+        sg_version < 30000) {
+        error_report("scsi-block: scsi generic interface too old");
+        return -1;
+    }
+
+    /* get device type from INQUIRY data */
+    rc = get_device_type(s);
+    if (rc < 0) {
+        error_report("scsi-block: INQUIRY failed");
+        return -1;
+    }
+
+    /* Make a guess for the block size, we'll fix it when the guest sends.
+     * READ CAPACITY.  If they don't, they likely would assume these sizes
+     * anyway. (TODO: check in /sys).
+     */
+    if (s->qdev.type == TYPE_ROM || s->qdev.type == TYPE_WORM) {
+        s->qdev.blocksize = 2048;
+    } else {
+        s->qdev.blocksize = 512;
+    }
+    return scsi_initfn(&s->qdev);
+}
+
+static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
+                                           uint32_t lun, uint8_t *buf,
+                                           void *hba_private)
+{
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
+
+    switch (buf[0]) {
+    case READ_6:
+    case READ_10:
+    case READ_12:
+    case READ_16:
+    case VERIFY_10:
+    case VERIFY_12:
+    case VERIFY_16:
+    case WRITE_6:
+    case WRITE_10:
+    case WRITE_12:
+    case WRITE_16:
+    case WRITE_VERIFY_10:
+    case WRITE_VERIFY_12:
+    case WRITE_VERIFY_16:
+        /* If we are not using O_DIRECT, we might read stale data from the
+        * host cache if writes were made using other commands than these
+        * ones (such as WRITE SAME or EXTENDED COPY, etc.).  So, without
+        * O_DIRECT everything must go through SG_IO.
+         */
+        if (bdrv_get_flags(s->qdev.conf.bs) & BDRV_O_NOCACHE) {
+            break;
+        }
+
+        /* MMC writing cannot be done via pread/pwrite, because it sometimes
+         * involves writing beyond the maximum LBA or to negative LBA (lead-in).
+         * And once you do these writes, reading from the block device is
+         * unreliable, too.  It is even possible that reads deliver random data
+         * from the host page cache (this is probably a Linux bug).
+         *
+         * We might use scsi_disk_dma_reqops as long as no writing commands are
+         * seen, but performance usually isn't paramount on optical media.  So,
+         * just make scsi-block operate the same as scsi-generic for them.
+         */
+        if (s->qdev.type != TYPE_ROM) {
+            return scsi_req_alloc(&scsi_disk_dma_reqops, &s->qdev, tag, lun,
+                                  hba_private);
+        }
+    }
+
+    return scsi_req_alloc(&scsi_generic_req_ops, &s->qdev, tag, lun,
+                          hba_private);
+}
+#endif
+
+#define DEFINE_SCSI_DISK_PROPERTIES()                                \
+    DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf),               \
+    DEFINE_PROP_STRING("ver", SCSIDiskState, version),               \
+    DEFINE_PROP_STRING("serial", SCSIDiskState, serial),             \
+    DEFINE_PROP_STRING("vendor", SCSIDiskState, vendor),             \
+    DEFINE_PROP_STRING("product", SCSIDiskState, product)
+
+static Property scsi_hd_properties[] = {
+    DEFINE_SCSI_DISK_PROPERTIES(),
+    DEFINE_PROP_BIT("removable", SCSIDiskState, features,
+                    SCSI_DISK_F_REMOVABLE, false),
+    DEFINE_PROP_BIT("dpofua", SCSIDiskState, features,
+                    SCSI_DISK_F_DPOFUA, false),
+    DEFINE_PROP_HEX64("wwn", SCSIDiskState, wwn, 0),
+    DEFINE_BLOCK_CHS_PROPERTIES(SCSIDiskState, qdev.conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription vmstate_scsi_disk_state = {
+    .name = "scsi-disk",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_SCSI_DEVICE(qdev, SCSIDiskState),
+        VMSTATE_BOOL(media_changed, SCSIDiskState),
+        VMSTATE_BOOL(media_event, SCSIDiskState),
+        VMSTATE_BOOL(eject_request, SCSIDiskState),
+        VMSTATE_BOOL(tray_open, SCSIDiskState),
+        VMSTATE_BOOL(tray_locked, SCSIDiskState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void scsi_hd_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
+
+    sc->init         = scsi_hd_initfn;
+    sc->destroy      = scsi_destroy;
+    sc->alloc_req    = scsi_new_request;
+    sc->unit_attention_reported = scsi_disk_unit_attention_reported;
+    dc->fw_name = "disk";
+    dc->desc = "virtual SCSI disk";
+    dc->reset = scsi_disk_reset;
+    dc->props = scsi_hd_properties;
+    dc->vmsd  = &vmstate_scsi_disk_state;
+}
+
+static const TypeInfo scsi_hd_info = {
+    .name          = "scsi-hd",
+    .parent        = TYPE_SCSI_DEVICE,
+    .instance_size = sizeof(SCSIDiskState),
+    .class_init    = scsi_hd_class_initfn,
+};
+
+static Property scsi_cd_properties[] = {
+    DEFINE_SCSI_DISK_PROPERTIES(),
+    DEFINE_PROP_HEX64("wwn", SCSIDiskState, wwn, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void scsi_cd_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
+
+    sc->init         = scsi_cd_initfn;
+    sc->destroy      = scsi_destroy;
+    sc->alloc_req    = scsi_new_request;
+    sc->unit_attention_reported = scsi_disk_unit_attention_reported;
+    dc->fw_name = "disk";
+    dc->desc = "virtual SCSI CD-ROM";
+    dc->reset = scsi_disk_reset;
+    dc->props = scsi_cd_properties;
+    dc->vmsd  = &vmstate_scsi_disk_state;
+}
+
+static const TypeInfo scsi_cd_info = {
+    .name          = "scsi-cd",
+    .parent        = TYPE_SCSI_DEVICE,
+    .instance_size = sizeof(SCSIDiskState),
+    .class_init    = scsi_cd_class_initfn,
+};
+
+#ifdef __linux__
+static Property scsi_block_properties[] = {
+    DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.bs),
+    DEFINE_PROP_INT32("bootindex", SCSIDiskState, qdev.conf.bootindex, -1),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void scsi_block_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
+
+    sc->init         = scsi_block_initfn;
+    sc->destroy      = scsi_destroy;
+    sc->alloc_req    = scsi_block_new_request;
+    dc->fw_name = "disk";
+    dc->desc = "SCSI block device passthrough";
+    dc->reset = scsi_disk_reset;
+    dc->props = scsi_block_properties;
+    dc->vmsd  = &vmstate_scsi_disk_state;
+}
+
+static const TypeInfo scsi_block_info = {
+    .name          = "scsi-block",
+    .parent        = TYPE_SCSI_DEVICE,
+    .instance_size = sizeof(SCSIDiskState),
+    .class_init    = scsi_block_class_initfn,
+};
+#endif
+
+static Property scsi_disk_properties[] = {
+    DEFINE_SCSI_DISK_PROPERTIES(),
+    DEFINE_PROP_BIT("removable", SCSIDiskState, features,
+                    SCSI_DISK_F_REMOVABLE, false),
+    DEFINE_PROP_BIT("dpofua", SCSIDiskState, features,
+                    SCSI_DISK_F_DPOFUA, false),
+    DEFINE_PROP_HEX64("wwn", SCSIDiskState, wwn, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void scsi_disk_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
+
+    sc->init         = scsi_disk_initfn;
+    sc->destroy      = scsi_destroy;
+    sc->alloc_req    = scsi_new_request;
+    sc->unit_attention_reported = scsi_disk_unit_attention_reported;
+    dc->fw_name = "disk";
+    dc->desc = "virtual SCSI disk or CD-ROM (legacy)";
+    dc->reset = scsi_disk_reset;
+    dc->props = scsi_disk_properties;
+    dc->vmsd  = &vmstate_scsi_disk_state;
+}
+
+static const TypeInfo scsi_disk_info = {
+    .name          = "scsi-disk",
+    .parent        = TYPE_SCSI_DEVICE,
+    .instance_size = sizeof(SCSIDiskState),
+    .class_init    = scsi_disk_class_initfn,
+};
+
+static void scsi_disk_register_types(void)
+{
+    type_register_static(&scsi_hd_info);
+    type_register_static(&scsi_cd_info);
+#ifdef __linux__
+    type_register_static(&scsi_block_info);
+#endif
+    type_register_static(&scsi_disk_info);
+}
+
+type_init(scsi_disk_register_types)
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
new file mode 100644 (file)
index 0000000..2a9a561
--- /dev/null
@@ -0,0 +1,516 @@
+/*
+ * Generic SCSI Device support
+ *
+ * Copyright (c) 2007 Bull S.A.S.
+ * Based on code by Paul Brook
+ * Based on code by Fabrice Bellard
+ *
+ * Written by Laurent Vivier <Laurent.Vivier@bull.net>
+ *
+ * This code is licensed under the LGPL.
+ *
+ */
+
+#include "qemu-common.h"
+#include "qemu/error-report.h"
+#include "hw/scsi/scsi.h"
+#include "sysemu/blockdev.h"
+
+#ifdef __linux__
+
+//#define DEBUG_SCSI
+
+#ifdef DEBUG_SCSI
+#define DPRINTF(fmt, ...) \
+do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <scsi/sg.h>
+#include "block/scsi.h"
+
+#define SCSI_SENSE_BUF_SIZE 96
+
+#define SG_ERR_DRIVER_TIMEOUT  0x06
+#define SG_ERR_DRIVER_SENSE    0x08
+
+#define SG_ERR_DID_OK          0x00
+#define SG_ERR_DID_NO_CONNECT  0x01
+#define SG_ERR_DID_BUS_BUSY    0x02
+#define SG_ERR_DID_TIME_OUT    0x03
+
+#ifndef MAX_UINT
+#define MAX_UINT ((unsigned int)-1)
+#endif
+
+typedef struct SCSIGenericReq {
+    SCSIRequest req;
+    uint8_t *buf;
+    int buflen;
+    int len;
+    sg_io_hdr_t io_header;
+} SCSIGenericReq;
+
+static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req)
+{
+    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+
+    qemu_put_sbe32s(f, &r->buflen);
+    if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+        assert(!r->req.sg);
+        qemu_put_buffer(f, r->buf, r->req.cmd.xfer);
+    }
+}
+
+static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req)
+{
+    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+
+    qemu_get_sbe32s(f, &r->buflen);
+    if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+        assert(!r->req.sg);
+        qemu_get_buffer(f, r->buf, r->req.cmd.xfer);
+    }
+}
+
+static void scsi_free_request(SCSIRequest *req)
+{
+    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+
+    g_free(r->buf);
+}
+
+/* Helper function for command completion.  */
+static void scsi_command_complete(void *opaque, int ret)
+{
+    int status;
+    SCSIGenericReq *r = (SCSIGenericReq *)opaque;
+
+    r->req.aiocb = NULL;
+    if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
+        r->req.sense_len = r->io_header.sb_len_wr;
+    }
+
+    if (ret != 0) {
+        switch (ret) {
+        case -EDOM:
+            status = TASK_SET_FULL;
+            break;
+        case -ENOMEM:
+            status = CHECK_CONDITION;
+            scsi_req_build_sense(&r->req, SENSE_CODE(TARGET_FAILURE));
+            break;
+        default:
+            status = CHECK_CONDITION;
+            scsi_req_build_sense(&r->req, SENSE_CODE(IO_ERROR));
+            break;
+        }
+    } else {
+        if (r->io_header.host_status == SG_ERR_DID_NO_CONNECT ||
+            r->io_header.host_status == SG_ERR_DID_BUS_BUSY ||
+            r->io_header.host_status == SG_ERR_DID_TIME_OUT ||
+            (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) {
+            status = BUSY;
+            BADF("Driver Timeout\n");
+        } else if (r->io_header.host_status) {
+            status = CHECK_CONDITION;
+            scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS));
+        } else if (r->io_header.status) {
+            status = r->io_header.status;
+        } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
+            status = CHECK_CONDITION;
+        } else {
+            status = GOOD;
+        }
+    }
+    DPRINTF("Command complete 0x%p tag=0x%x status=%d\n",
+            r, r->req.tag, status);
+
+    scsi_req_complete(&r->req, status);
+    if (!r->req.io_canceled) {
+        scsi_req_unref(&r->req);
+    }
+}
+
+/* Cancel a pending data transfer.  */
+static void scsi_cancel_io(SCSIRequest *req)
+{
+    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+
+    DPRINTF("Cancel tag=0x%x\n", req->tag);
+    if (r->req.aiocb) {
+        bdrv_aio_cancel(r->req.aiocb);
+
+        /* This reference was left in by scsi_*_data.  We take ownership of
+         * it independent of whether bdrv_aio_cancel completes the request
+         * or not.  */
+        scsi_req_unref(&r->req);
+    }
+    r->req.aiocb = NULL;
+}
+
+static int execute_command(BlockDriverState *bdrv,
+                           SCSIGenericReq *r, int direction,
+                          BlockDriverCompletionFunc *complete)
+{
+    r->io_header.interface_id = 'S';
+    r->io_header.dxfer_direction = direction;
+    r->io_header.dxferp = r->buf;
+    r->io_header.dxfer_len = r->buflen;
+    r->io_header.cmdp = r->req.cmd.buf;
+    r->io_header.cmd_len = r->req.cmd.len;
+    r->io_header.mx_sb_len = sizeof(r->req.sense);
+    r->io_header.sbp = r->req.sense;
+    r->io_header.timeout = MAX_UINT;
+    r->io_header.usr_ptr = r;
+    r->io_header.flags |= SG_FLAG_DIRECT_IO;
+
+    r->req.aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r);
+
+    return 0;
+}
+
+static void scsi_read_complete(void * opaque, int ret)
+{
+    SCSIGenericReq *r = (SCSIGenericReq *)opaque;
+    SCSIDevice *s = r->req.dev;
+    int len;
+
+    r->req.aiocb = NULL;
+    if (ret) {
+        DPRINTF("IO error ret %d\n", ret);
+        scsi_command_complete(r, ret);
+        return;
+    }
+    len = r->io_header.dxfer_len - r->io_header.resid;
+    DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len);
+
+    r->len = -1;
+    if (len == 0) {
+        scsi_command_complete(r, 0);
+    } else {
+        /* Snoop READ CAPACITY output to set the blocksize.  */
+        if (r->req.cmd.buf[0] == READ_CAPACITY_10) {
+            s->blocksize = ldl_be_p(&r->buf[4]);
+            s->max_lba = ldl_be_p(&r->buf[0]);
+        } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 &&
+                   (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
+            s->blocksize = ldl_be_p(&r->buf[8]);
+            s->max_lba = ldq_be_p(&r->buf[0]);
+        }
+        bdrv_set_buffer_alignment(s->conf.bs, s->blocksize);
+
+        scsi_req_data(&r->req, len);
+        if (!r->req.io_canceled) {
+            scsi_req_unref(&r->req);
+        }
+    }
+}
+
+/* Read more data from scsi device into buffer.  */
+static void scsi_read_data(SCSIRequest *req)
+{
+    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+    SCSIDevice *s = r->req.dev;
+    int ret;
+
+    DPRINTF("scsi_read_data 0x%x\n", req->tag);
+
+    /* The request is used as the AIO opaque value, so add a ref.  */
+    scsi_req_ref(&r->req);
+    if (r->len == -1) {
+        scsi_command_complete(r, 0);
+        return;
+    }
+
+    ret = execute_command(s->conf.bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
+    if (ret < 0) {
+        scsi_command_complete(r, ret);
+    }
+}
+
+static void scsi_write_complete(void * opaque, int ret)
+{
+    SCSIGenericReq *r = (SCSIGenericReq *)opaque;
+    SCSIDevice *s = r->req.dev;
+
+    DPRINTF("scsi_write_complete() ret = %d\n", ret);
+    r->req.aiocb = NULL;
+    if (ret) {
+        DPRINTF("IO error\n");
+        scsi_command_complete(r, ret);
+        return;
+    }
+
+    if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 &&
+        s->type == TYPE_TAPE) {
+        s->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
+        DPRINTF("block size %d\n", s->blocksize);
+    }
+
+    scsi_command_complete(r, ret);
+}
+
+/* Write data to a scsi device.  Returns nonzero on failure.
+   The transfer may complete asynchronously.  */
+static void scsi_write_data(SCSIRequest *req)
+{
+    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+    SCSIDevice *s = r->req.dev;
+    int ret;
+
+    DPRINTF("scsi_write_data 0x%x\n", req->tag);
+    if (r->len == 0) {
+        r->len = r->buflen;
+        scsi_req_data(&r->req, r->len);
+        return;
+    }
+
+    /* The request is used as the AIO opaque value, so add a ref.  */
+    scsi_req_ref(&r->req);
+    ret = execute_command(s->conf.bs, r, SG_DXFER_TO_DEV, scsi_write_complete);
+    if (ret < 0) {
+        scsi_command_complete(r, ret);
+    }
+}
+
+/* Return a pointer to the data buffer.  */
+static uint8_t *scsi_get_buf(SCSIRequest *req)
+{
+    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+
+    return r->buf;
+}
+
+/* Execute a scsi command.  Returns the length of the data expected by the
+   command.  This will be Positive for data transfers from the device
+   (eg. disk reads), negative for transfers to the device (eg. disk writes),
+   and zero if the command does not transfer any data.  */
+
+static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
+{
+    SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+    SCSIDevice *s = r->req.dev;
+    int ret;
+
+    DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x", lun, tag,
+            r->req.cmd.xfer, cmd[0]);
+
+#ifdef DEBUG_SCSI
+    {
+        int i;
+        for (i = 1; i < r->req.cmd.len; i++) {
+            printf(" 0x%02x", cmd[i]);
+        }
+        printf("\n");
+    }
+#endif
+
+    if (r->req.cmd.xfer == 0) {
+        if (r->buf != NULL)
+            g_free(r->buf);
+        r->buflen = 0;
+        r->buf = NULL;
+        /* The request is used as the AIO opaque value, so add a ref.  */
+        scsi_req_ref(&r->req);
+        ret = execute_command(s->conf.bs, r, SG_DXFER_NONE, scsi_command_complete);
+        if (ret < 0) {
+            scsi_command_complete(r, ret);
+            return 0;
+        }
+        return 0;
+    }
+
+    if (r->buflen != r->req.cmd.xfer) {
+        if (r->buf != NULL)
+            g_free(r->buf);
+        r->buf = g_malloc(r->req.cmd.xfer);
+        r->buflen = r->req.cmd.xfer;
+    }
+
+    memset(r->buf, 0, r->buflen);
+    r->len = r->req.cmd.xfer;
+    if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+        r->len = 0;
+        return -r->req.cmd.xfer;
+    } else {
+        return r->req.cmd.xfer;
+    }
+}
+
+static int get_stream_blocksize(BlockDriverState *bdrv)
+{
+    uint8_t cmd[6];
+    uint8_t buf[12];
+    uint8_t sensebuf[8];
+    sg_io_hdr_t io_header;
+    int ret;
+
+    memset(cmd, 0, sizeof(cmd));
+    memset(buf, 0, sizeof(buf));
+    cmd[0] = MODE_SENSE;
+    cmd[4] = sizeof(buf);
+
+    memset(&io_header, 0, sizeof(io_header));
+    io_header.interface_id = 'S';
+    io_header.dxfer_direction = SG_DXFER_FROM_DEV;
+    io_header.dxfer_len = sizeof(buf);
+    io_header.dxferp = buf;
+    io_header.cmdp = cmd;
+    io_header.cmd_len = sizeof(cmd);
+    io_header.mx_sb_len = sizeof(sensebuf);
+    io_header.sbp = sensebuf;
+    io_header.timeout = 6000; /* XXX */
+
+    ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
+    if (ret < 0 || io_header.driver_status || io_header.host_status) {
+        return -1;
+    }
+    return (buf[9] << 16) | (buf[10] << 8) | buf[11];
+}
+
+static void scsi_generic_reset(DeviceState *dev)
+{
+    SCSIDevice *s = SCSI_DEVICE(dev);
+
+    scsi_device_purge_requests(s, SENSE_CODE(RESET));
+}
+
+static void scsi_destroy(SCSIDevice *s)
+{
+    scsi_device_purge_requests(s, SENSE_CODE(NO_SENSE));
+    blockdev_mark_auto_del(s->conf.bs);
+}
+
+static int scsi_generic_initfn(SCSIDevice *s)
+{
+    int sg_version;
+    struct sg_scsi_id scsiid;
+
+    if (!s->conf.bs) {
+        error_report("drive property not set");
+        return -1;
+    }
+
+    if (bdrv_get_on_error(s->conf.bs, 0) != BLOCKDEV_ON_ERROR_ENOSPC) {
+        error_report("Device doesn't support drive option werror");
+        return -1;
+    }
+    if (bdrv_get_on_error(s->conf.bs, 1) != BLOCKDEV_ON_ERROR_REPORT) {
+        error_report("Device doesn't support drive option rerror");
+        return -1;
+    }
+
+    /* check we are using a driver managing SG_IO (version 3 and after */
+    if (bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0) {
+        error_report("scsi generic interface not supported");
+        return -1;
+    }
+    if (sg_version < 30000) {
+        error_report("scsi generic interface too old");
+        return -1;
+    }
+
+    /* get LUN of the /dev/sg? */
+    if (bdrv_ioctl(s->conf.bs, SG_GET_SCSI_ID, &scsiid)) {
+        error_report("SG_GET_SCSI_ID ioctl failed");
+        return -1;
+    }
+
+    /* define device state */
+    s->type = scsiid.scsi_type;
+    DPRINTF("device type %d\n", s->type);
+    if (s->type == TYPE_DISK || s->type == TYPE_ROM) {
+        add_boot_device_path(s->conf.bootindex, &s->qdev, NULL);
+    }
+
+    switch (s->type) {
+    case TYPE_TAPE:
+        s->blocksize = get_stream_blocksize(s->conf.bs);
+        if (s->blocksize == -1) {
+            s->blocksize = 0;
+        }
+        break;
+
+        /* Make a guess for block devices, we'll fix it when the guest sends.
+         * READ CAPACITY.  If they don't, they likely would assume these sizes
+         * anyway. (TODO: they could also send MODE SENSE).
+         */
+    case TYPE_ROM:
+    case TYPE_WORM:
+        s->blocksize = 2048;
+        break;
+    default:
+        s->blocksize = 512;
+        break;
+    }
+
+    DPRINTF("block size %d\n", s->blocksize);
+    return 0;
+}
+
+const SCSIReqOps scsi_generic_req_ops = {
+    .size         = sizeof(SCSIGenericReq),
+    .free_req     = scsi_free_request,
+    .send_command = scsi_send_command,
+    .read_data    = scsi_read_data,
+    .write_data   = scsi_write_data,
+    .cancel_io    = scsi_cancel_io,
+    .get_buf      = scsi_get_buf,
+    .load_request = scsi_generic_load_request,
+    .save_request = scsi_generic_save_request,
+};
+
+static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
+                                     uint8_t *buf, void *hba_private)
+{
+    SCSIRequest *req;
+
+    req = scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private);
+    return req;
+}
+
+static Property scsi_generic_properties[] = {
+    DEFINE_PROP_DRIVE("drive", SCSIDevice, conf.bs),
+    DEFINE_PROP_INT32("bootindex", SCSIDevice, conf.bootindex, -1),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void scsi_generic_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
+
+    sc->init         = scsi_generic_initfn;
+    sc->destroy      = scsi_destroy;
+    sc->alloc_req    = scsi_new_request;
+    dc->fw_name = "disk";
+    dc->desc = "pass through generic scsi device (/dev/sg*)";
+    dc->reset = scsi_generic_reset;
+    dc->props = scsi_generic_properties;
+    dc->vmsd  = &vmstate_scsi_device;
+}
+
+static const TypeInfo scsi_generic_info = {
+    .name          = "scsi-generic",
+    .parent        = TYPE_SCSI_DEVICE,
+    .instance_size = sizeof(SCSIDevice),
+    .class_init    = scsi_generic_class_initfn,
+};
+
+static void scsi_generic_register_types(void)
+{
+    type_register_static(&scsi_generic_info);
+}
+
+type_init(scsi_generic_register_types)
+
+#endif /* __linux__ */
diff --git a/hw/sd.c b/hw/sd.c
deleted file mode 100644 (file)
index 66c4014..0000000
--- a/hw/sd.c
+++ /dev/null
@@ -1,1764 +0,0 @@
-/*
- * SD Memory Card emulation as defined in the "SD Memory Card Physical
- * layer specification, Version 1.10."
- *
- * Copyright (c) 2006 Andrzej Zaborowski  <balrog@zabor.org>
- * Copyright (c) 2007 CodeSourcery
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "hw/hw.h"
-#include "block/block.h"
-#include "hw/sd.h"
-#include "qemu/bitmap.h"
-
-//#define DEBUG_SD 1
-
-#ifdef DEBUG_SD
-#define DPRINTF(fmt, ...) \
-do { fprintf(stderr, "SD: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#endif
-
-typedef enum {
-    sd_r0 = 0,    /* no response */
-    sd_r1,        /* normal response command */
-    sd_r2_i,      /* CID register */
-    sd_r2_s,      /* CSD register */
-    sd_r3,        /* OCR register */
-    sd_r6 = 6,    /* Published RCA response */
-    sd_r7,        /* Operating voltage */
-    sd_r1b = -1,
-    sd_illegal = -2,
-} sd_rsp_type_t;
-
-enum SDCardModes {
-    sd_inactive,
-    sd_card_identification_mode,
-    sd_data_transfer_mode,
-};
-
-enum SDCardStates {
-    sd_inactive_state = -1,
-    sd_idle_state = 0,
-    sd_ready_state,
-    sd_identification_state,
-    sd_standby_state,
-    sd_transfer_state,
-    sd_sendingdata_state,
-    sd_receivingdata_state,
-    sd_programming_state,
-    sd_disconnect_state,
-};
-
-struct SDState {
-    uint32_t mode;    /* current card mode, one of SDCardModes */
-    int32_t state;    /* current card state, one of SDCardStates */
-    uint32_t ocr;
-    uint8_t scr[8];
-    uint8_t cid[16];
-    uint8_t csd[16];
-    uint16_t rca;
-    uint32_t card_status;
-    uint8_t sd_status[64];
-    uint32_t vhs;
-    bool wp_switch;
-    unsigned long *wp_groups;
-    int32_t wpgrps_size;
-    uint64_t size;
-    uint32_t blk_len;
-    uint32_t erase_start;
-    uint32_t erase_end;
-    uint8_t pwd[16];
-    uint32_t pwd_len;
-    uint8_t function_group[6];
-
-    bool spi;
-    uint8_t current_cmd;
-    /* True if we will handle the next command as an ACMD. Note that this does
-     * *not* track the APP_CMD status bit!
-     */
-    bool expecting_acmd;
-    uint32_t blk_written;
-    uint64_t data_start;
-    uint32_t data_offset;
-    uint8_t data[512];
-    qemu_irq readonly_cb;
-    qemu_irq inserted_cb;
-    BlockDriverState *bdrv;
-    uint8_t *buf;
-
-    bool enable;
-};
-
-static void sd_set_mode(SDState *sd)
-{
-    switch (sd->state) {
-    case sd_inactive_state:
-        sd->mode = sd_inactive;
-        break;
-
-    case sd_idle_state:
-    case sd_ready_state:
-    case sd_identification_state:
-        sd->mode = sd_card_identification_mode;
-        break;
-
-    case sd_standby_state:
-    case sd_transfer_state:
-    case sd_sendingdata_state:
-    case sd_receivingdata_state:
-    case sd_programming_state:
-    case sd_disconnect_state:
-        sd->mode = sd_data_transfer_mode;
-        break;
-    }
-}
-
-static const sd_cmd_type_t sd_cmd_type[64] = {
-    sd_bc,   sd_none, sd_bcr,  sd_bcr,  sd_none, sd_none, sd_none, sd_ac,
-    sd_bcr,  sd_ac,   sd_ac,   sd_adtc, sd_ac,   sd_ac,   sd_none, sd_ac,
-    sd_ac,   sd_adtc, sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none,
-    sd_adtc, sd_adtc, sd_adtc, sd_adtc, sd_ac,   sd_ac,   sd_adtc, sd_none,
-    sd_ac,   sd_ac,   sd_none, sd_none, sd_none, sd_none, sd_ac,   sd_none,
-    sd_none, sd_none, sd_bc,   sd_none, sd_none, sd_none, sd_none, sd_none,
-    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_ac,
-    sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
-};
-
-static const sd_cmd_type_t sd_acmd_type[64] = {
-    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_ac,   sd_none,
-    sd_none, sd_none, sd_none, sd_none, sd_none, sd_adtc, sd_none, sd_none,
-    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_adtc, sd_ac,
-    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
-    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
-    sd_none, sd_bcr,  sd_ac,   sd_none, sd_none, sd_none, sd_none, sd_none,
-    sd_none, sd_none, sd_none, sd_adtc, sd_none, sd_none, sd_none, sd_none,
-    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
-};
-
-static const int sd_cmd_class[64] = {
-    0,  0,  0,  0,  0,  9, 10,  0,  0,  0,  0,  1,  0,  0,  0,  0,
-    2,  2,  2,  2,  3,  3,  3,  3,  4,  4,  4,  4,  6,  6,  6,  6,
-    5,  5, 10, 10, 10, 10,  5,  9,  9,  9,  7,  7,  7,  7,  7,  7,
-    7,  7, 10,  7,  9,  9,  9,  8,  8, 10,  8,  8,  8,  8,  8,  8,
-};
-
-static uint8_t sd_crc7(void *message, size_t width)
-{
-    int i, bit;
-    uint8_t shift_reg = 0x00;
-    uint8_t *msg = (uint8_t *) message;
-
-    for (i = 0; i < width; i ++, msg ++)
-        for (bit = 7; bit >= 0; bit --) {
-            shift_reg <<= 1;
-            if ((shift_reg >> 7) ^ ((*msg >> bit) & 1))
-                shift_reg ^= 0x89;
-        }
-
-    return shift_reg;
-}
-
-static uint16_t sd_crc16(void *message, size_t width)
-{
-    int i, bit;
-    uint16_t shift_reg = 0x0000;
-    uint16_t *msg = (uint16_t *) message;
-    width <<= 1;
-
-    for (i = 0; i < width; i ++, msg ++)
-        for (bit = 15; bit >= 0; bit --) {
-            shift_reg <<= 1;
-            if ((shift_reg >> 15) ^ ((*msg >> bit) & 1))
-                shift_reg ^= 0x1011;
-        }
-
-    return shift_reg;
-}
-
-static void sd_set_ocr(SDState *sd)
-{
-    /* All voltages OK, card power-up OK, Standard Capacity SD Memory Card */
-    sd->ocr = 0x80ffff00;
-}
-
-static void sd_set_scr(SDState *sd)
-{
-    sd->scr[0] = 0x00;         /* SCR Structure */
-    sd->scr[1] = 0x2f;         /* SD Security Support */
-    sd->scr[2] = 0x00;
-    sd->scr[3] = 0x00;
-    sd->scr[4] = 0x00;
-    sd->scr[5] = 0x00;
-    sd->scr[6] = 0x00;
-    sd->scr[7] = 0x00;
-}
-
-#define MID    0xaa
-#define OID    "XY"
-#define PNM    "QEMU!"
-#define PRV    0x01
-#define MDT_YR 2006
-#define MDT_MON        2
-
-static void sd_set_cid(SDState *sd)
-{
-    sd->cid[0] = MID;          /* Fake card manufacturer ID (MID) */
-    sd->cid[1] = OID[0];       /* OEM/Application ID (OID) */
-    sd->cid[2] = OID[1];
-    sd->cid[3] = PNM[0];       /* Fake product name (PNM) */
-    sd->cid[4] = PNM[1];
-    sd->cid[5] = PNM[2];
-    sd->cid[6] = PNM[3];
-    sd->cid[7] = PNM[4];
-    sd->cid[8] = PRV;          /* Fake product revision (PRV) */
-    sd->cid[9] = 0xde;         /* Fake serial number (PSN) */
-    sd->cid[10] = 0xad;
-    sd->cid[11] = 0xbe;
-    sd->cid[12] = 0xef;
-    sd->cid[13] = 0x00 |       /* Manufacture date (MDT) */
-        ((MDT_YR - 2000) / 10);
-    sd->cid[14] = ((MDT_YR % 10) << 4) | MDT_MON;
-    sd->cid[15] = (sd_crc7(sd->cid, 15) << 1) | 1;
-}
-
-#define HWBLOCK_SHIFT  9                       /* 512 bytes */
-#define SECTOR_SHIFT   5                       /* 16 kilobytes */
-#define WPGROUP_SHIFT  7                       /* 2 megs */
-#define CMULT_SHIFT    9                       /* 512 times HWBLOCK_SIZE */
-#define WPGROUP_SIZE   (1 << (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT))
-
-static const uint8_t sd_csd_rw_mask[16] = {
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfe,
-};
-
-static void sd_set_csd(SDState *sd, uint64_t size)
-{
-    uint32_t csize = (size >> (CMULT_SHIFT + HWBLOCK_SHIFT)) - 1;
-    uint32_t sectsize = (1 << (SECTOR_SHIFT + 1)) - 1;
-    uint32_t wpsize = (1 << (WPGROUP_SHIFT + 1)) - 1;
-
-    if (size <= 0x40000000) {  /* Standard Capacity SD */
-        sd->csd[0] = 0x00;     /* CSD structure */
-        sd->csd[1] = 0x26;     /* Data read access-time-1 */
-        sd->csd[2] = 0x00;     /* Data read access-time-2 */
-        sd->csd[3] = 0x5a;     /* Max. data transfer rate */
-        sd->csd[4] = 0x5f;     /* Card Command Classes */
-        sd->csd[5] = 0x50 |    /* Max. read data block length */
-            HWBLOCK_SHIFT;
-        sd->csd[6] = 0xe0 |    /* Partial block for read allowed */
-            ((csize >> 10) & 0x03);
-        sd->csd[7] = 0x00 |    /* Device size */
-            ((csize >> 2) & 0xff);
-        sd->csd[8] = 0x3f |    /* Max. read current */
-            ((csize << 6) & 0xc0);
-        sd->csd[9] = 0xfc |    /* Max. write current */
-            ((CMULT_SHIFT - 2) >> 1);
-        sd->csd[10] = 0x40 |   /* Erase sector size */
-            (((CMULT_SHIFT - 2) << 7) & 0x80) | (sectsize >> 1);
-        sd->csd[11] = 0x00 |   /* Write protect group size */
-            ((sectsize << 7) & 0x80) | wpsize;
-        sd->csd[12] = 0x90 |   /* Write speed factor */
-            (HWBLOCK_SHIFT >> 2);
-        sd->csd[13] = 0x20 |   /* Max. write data block length */
-            ((HWBLOCK_SHIFT << 6) & 0xc0);
-        sd->csd[14] = 0x00;    /* File format group */
-        sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1;
-    } else {                   /* SDHC */
-        size /= 512 * 1024;
-        size -= 1;
-        sd->csd[0] = 0x40;
-        sd->csd[1] = 0x0e;
-        sd->csd[2] = 0x00;
-        sd->csd[3] = 0x32;
-        sd->csd[4] = 0x5b;
-        sd->csd[5] = 0x59;
-        sd->csd[6] = 0x00;
-        sd->csd[7] = (size >> 16) & 0xff;
-        sd->csd[8] = (size >> 8) & 0xff;
-        sd->csd[9] = (size & 0xff);
-        sd->csd[10] = 0x7f;
-        sd->csd[11] = 0x80;
-        sd->csd[12] = 0x0a;
-        sd->csd[13] = 0x40;
-        sd->csd[14] = 0x00;
-        sd->csd[15] = 0x00;
-        sd->ocr |= 1 << 30;    /* High Capacity SD Memort Card */
-    }
-}
-
-static void sd_set_rca(SDState *sd)
-{
-    sd->rca += 0x4567;
-}
-
-/* Card status bits, split by clear condition:
- * A : According to the card current state
- * B : Always related to the previous command
- * C : Cleared by read
- */
-#define CARD_STATUS_A  0x02004100
-#define CARD_STATUS_B  0x00c01e00
-#define CARD_STATUS_C  0xfd39a028
-
-static void sd_set_cardstatus(SDState *sd)
-{
-    sd->card_status = 0x00000100;
-}
-
-static void sd_set_sdstatus(SDState *sd)
-{
-    memset(sd->sd_status, 0, 64);
-}
-
-static int sd_req_crc_validate(SDRequest *req)
-{
-    uint8_t buffer[5];
-    buffer[0] = 0x40 | req->cmd;
-    buffer[1] = (req->arg >> 24) & 0xff;
-    buffer[2] = (req->arg >> 16) & 0xff;
-    buffer[3] = (req->arg >> 8) & 0xff;
-    buffer[4] = (req->arg >> 0) & 0xff;
-    return 0;
-    return sd_crc7(buffer, 5) != req->crc;     /* TODO */
-}
-
-static void sd_response_r1_make(SDState *sd, uint8_t *response)
-{
-    uint32_t status = sd->card_status;
-    /* Clear the "clear on read" status bits */
-    sd->card_status &= ~CARD_STATUS_C;
-
-    response[0] = (status >> 24) & 0xff;
-    response[1] = (status >> 16) & 0xff;
-    response[2] = (status >> 8) & 0xff;
-    response[3] = (status >> 0) & 0xff;
-}
-
-static void sd_response_r3_make(SDState *sd, uint8_t *response)
-{
-    response[0] = (sd->ocr >> 24) & 0xff;
-    response[1] = (sd->ocr >> 16) & 0xff;
-    response[2] = (sd->ocr >> 8) & 0xff;
-    response[3] = (sd->ocr >> 0) & 0xff;
-}
-
-static void sd_response_r6_make(SDState *sd, uint8_t *response)
-{
-    uint16_t arg;
-    uint16_t status;
-
-    arg = sd->rca;
-    status = ((sd->card_status >> 8) & 0xc000) |
-             ((sd->card_status >> 6) & 0x2000) |
-              (sd->card_status & 0x1fff);
-    sd->card_status &= ~(CARD_STATUS_C & 0xc81fff);
-
-    response[0] = (arg >> 8) & 0xff;
-    response[1] = arg & 0xff;
-    response[2] = (status >> 8) & 0xff;
-    response[3] = status & 0xff;
-}
-
-static void sd_response_r7_make(SDState *sd, uint8_t *response)
-{
-    response[0] = (sd->vhs >> 24) & 0xff;
-    response[1] = (sd->vhs >> 16) & 0xff;
-    response[2] = (sd->vhs >>  8) & 0xff;
-    response[3] = (sd->vhs >>  0) & 0xff;
-}
-
-static inline uint64_t sd_addr_to_wpnum(uint64_t addr)
-{
-    return addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT);
-}
-
-static void sd_reset(SDState *sd, BlockDriverState *bdrv)
-{
-    uint64_t size;
-    uint64_t sect;
-
-    if (bdrv) {
-        bdrv_get_geometry(bdrv, &sect);
-    } else {
-        sect = 0;
-    }
-    size = sect << 9;
-
-    sect = sd_addr_to_wpnum(size) + 1;
-
-    sd->state = sd_idle_state;
-    sd->rca = 0x0000;
-    sd_set_ocr(sd);
-    sd_set_scr(sd);
-    sd_set_cid(sd);
-    sd_set_csd(sd, size);
-    sd_set_cardstatus(sd);
-    sd_set_sdstatus(sd);
-
-    sd->bdrv = bdrv;
-
-    if (sd->wp_groups)
-        g_free(sd->wp_groups);
-    sd->wp_switch = bdrv ? bdrv_is_read_only(bdrv) : false;
-    sd->wpgrps_size = sect;
-    sd->wp_groups = bitmap_new(sd->wpgrps_size);
-    memset(sd->function_group, 0, sizeof(sd->function_group));
-    sd->erase_start = 0;
-    sd->erase_end = 0;
-    sd->size = size;
-    sd->blk_len = 0x200;
-    sd->pwd_len = 0;
-    sd->expecting_acmd = false;
-}
-
-static void sd_cardchange(void *opaque, bool load)
-{
-    SDState *sd = opaque;
-
-    qemu_set_irq(sd->inserted_cb, bdrv_is_inserted(sd->bdrv));
-    if (bdrv_is_inserted(sd->bdrv)) {
-        sd_reset(sd, sd->bdrv);
-        qemu_set_irq(sd->readonly_cb, sd->wp_switch);
-    }
-}
-
-static const BlockDevOps sd_block_ops = {
-    .change_media_cb = sd_cardchange,
-};
-
-static const VMStateDescription sd_vmstate = {
-    .name = "sd-card",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_UINT32(mode, SDState),
-        VMSTATE_INT32(state, SDState),
-        VMSTATE_UINT8_ARRAY(cid, SDState, 16),
-        VMSTATE_UINT8_ARRAY(csd, SDState, 16),
-        VMSTATE_UINT16(rca, SDState),
-        VMSTATE_UINT32(card_status, SDState),
-        VMSTATE_PARTIAL_BUFFER(sd_status, SDState, 1),
-        VMSTATE_UINT32(vhs, SDState),
-        VMSTATE_BITMAP(wp_groups, SDState, 0, wpgrps_size),
-        VMSTATE_UINT32(blk_len, SDState),
-        VMSTATE_UINT32(erase_start, SDState),
-        VMSTATE_UINT32(erase_end, SDState),
-        VMSTATE_UINT8_ARRAY(pwd, SDState, 16),
-        VMSTATE_UINT32(pwd_len, SDState),
-        VMSTATE_UINT8_ARRAY(function_group, SDState, 6),
-        VMSTATE_UINT8(current_cmd, SDState),
-        VMSTATE_BOOL(expecting_acmd, SDState),
-        VMSTATE_UINT32(blk_written, SDState),
-        VMSTATE_UINT64(data_start, SDState),
-        VMSTATE_UINT32(data_offset, SDState),
-        VMSTATE_UINT8_ARRAY(data, SDState, 512),
-        VMSTATE_BUFFER_POINTER_UNSAFE(buf, SDState, 1, 512),
-        VMSTATE_BOOL(enable, SDState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-/* We do not model the chip select pin, so allow the board to select
-   whether card should be in SSI or MMC/SD mode.  It is also up to the
-   board to ensure that ssi transfers only occur when the chip select
-   is asserted.  */
-SDState *sd_init(BlockDriverState *bs, bool is_spi)
-{
-    SDState *sd;
-
-    sd = (SDState *) g_malloc0(sizeof(SDState));
-    sd->buf = qemu_blockalign(bs, 512);
-    sd->spi = is_spi;
-    sd->enable = true;
-    sd_reset(sd, bs);
-    if (sd->bdrv) {
-        bdrv_attach_dev_nofail(sd->bdrv, sd);
-        bdrv_set_dev_ops(sd->bdrv, &sd_block_ops, sd);
-    }
-    vmstate_register(NULL, -1, &sd_vmstate, sd);
-    return sd;
-}
-
-void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert)
-{
-    sd->readonly_cb = readonly;
-    sd->inserted_cb = insert;
-    qemu_set_irq(readonly, sd->bdrv ? bdrv_is_read_only(sd->bdrv) : 0);
-    qemu_set_irq(insert, sd->bdrv ? bdrv_is_inserted(sd->bdrv) : 0);
-}
-
-static void sd_erase(SDState *sd)
-{
-    int i;
-    uint64_t erase_start = sd->erase_start;
-    uint64_t erase_end = sd->erase_end;
-
-    if (!sd->erase_start || !sd->erase_end) {
-        sd->card_status |= ERASE_SEQ_ERROR;
-        return;
-    }
-
-    if (extract32(sd->ocr, OCR_CCS_BITN, 1)) {
-        /* High capacity memory card: erase units are 512 byte blocks */
-        erase_start *= 512;
-        erase_end *= 512;
-    }
-
-    erase_start = sd_addr_to_wpnum(erase_start);
-    erase_end = sd_addr_to_wpnum(erase_end);
-    sd->erase_start = 0;
-    sd->erase_end = 0;
-    sd->csd[14] |= 0x40;
-
-    for (i = erase_start; i <= erase_end; i++) {
-        if (test_bit(i, sd->wp_groups)) {
-            sd->card_status |= WP_ERASE_SKIP;
-        }
-    }
-}
-
-static uint32_t sd_wpbits(SDState *sd, uint64_t addr)
-{
-    uint32_t i, wpnum;
-    uint32_t ret = 0;
-
-    wpnum = sd_addr_to_wpnum(addr);
-
-    for (i = 0; i < 32; i++, wpnum++, addr += WPGROUP_SIZE) {
-        if (addr < sd->size && test_bit(wpnum, sd->wp_groups)) {
-            ret |= (1 << i);
-        }
-    }
-
-    return ret;
-}
-
-static void sd_function_switch(SDState *sd, uint32_t arg)
-{
-    int i, mode, new_func, crc;
-    mode = !!(arg & 0x80000000);
-
-    sd->data[0] = 0x00;                /* Maximum current consumption */
-    sd->data[1] = 0x01;
-    sd->data[2] = 0x80;                /* Supported group 6 functions */
-    sd->data[3] = 0x01;
-    sd->data[4] = 0x80;                /* Supported group 5 functions */
-    sd->data[5] = 0x01;
-    sd->data[6] = 0x80;                /* Supported group 4 functions */
-    sd->data[7] = 0x01;
-    sd->data[8] = 0x80;                /* Supported group 3 functions */
-    sd->data[9] = 0x01;
-    sd->data[10] = 0x80;       /* Supported group 2 functions */
-    sd->data[11] = 0x43;
-    sd->data[12] = 0x80;       /* Supported group 1 functions */
-    sd->data[13] = 0x03;
-    for (i = 0; i < 6; i ++) {
-        new_func = (arg >> (i * 4)) & 0x0f;
-        if (mode && new_func != 0x0f)
-            sd->function_group[i] = new_func;
-        sd->data[14 + (i >> 1)] = new_func << ((i * 4) & 4);
-    }
-    memset(&sd->data[17], 0, 47);
-    crc = sd_crc16(sd->data, 64);
-    sd->data[65] = crc >> 8;
-    sd->data[66] = crc & 0xff;
-}
-
-static inline bool sd_wp_addr(SDState *sd, uint64_t addr)
-{
-    return test_bit(sd_addr_to_wpnum(addr), sd->wp_groups);
-}
-
-static void sd_lock_command(SDState *sd)
-{
-    int erase, lock, clr_pwd, set_pwd, pwd_len;
-    erase = !!(sd->data[0] & 0x08);
-    lock = sd->data[0] & 0x04;
-    clr_pwd = sd->data[0] & 0x02;
-    set_pwd = sd->data[0] & 0x01;
-
-    if (sd->blk_len > 1)
-        pwd_len = sd->data[1];
-    else
-        pwd_len = 0;
-
-    if (erase) {
-        if (!(sd->card_status & CARD_IS_LOCKED) || sd->blk_len > 1 ||
-                        set_pwd || clr_pwd || lock || sd->wp_switch ||
-                        (sd->csd[14] & 0x20)) {
-            sd->card_status |= LOCK_UNLOCK_FAILED;
-            return;
-        }
-        bitmap_zero(sd->wp_groups, sd->wpgrps_size);
-        sd->csd[14] &= ~0x10;
-        sd->card_status &= ~CARD_IS_LOCKED;
-        sd->pwd_len = 0;
-        /* Erasing the entire card here! */
-        fprintf(stderr, "SD: Card force-erased by CMD42\n");
-        return;
-    }
-
-    if (sd->blk_len < 2 + pwd_len ||
-                    pwd_len <= sd->pwd_len ||
-                    pwd_len > sd->pwd_len + 16) {
-        sd->card_status |= LOCK_UNLOCK_FAILED;
-        return;
-    }
-
-    if (sd->pwd_len && memcmp(sd->pwd, sd->data + 2, sd->pwd_len)) {
-        sd->card_status |= LOCK_UNLOCK_FAILED;
-        return;
-    }
-
-    pwd_len -= sd->pwd_len;
-    if ((pwd_len && !set_pwd) ||
-                    (clr_pwd && (set_pwd || lock)) ||
-                    (lock && !sd->pwd_len && !set_pwd) ||
-                    (!set_pwd && !clr_pwd &&
-                     (((sd->card_status & CARD_IS_LOCKED) && lock) ||
-                      (!(sd->card_status & CARD_IS_LOCKED) && !lock)))) {
-        sd->card_status |= LOCK_UNLOCK_FAILED;
-        return;
-    }
-
-    if (set_pwd) {
-        memcpy(sd->pwd, sd->data + 2 + sd->pwd_len, pwd_len);
-        sd->pwd_len = pwd_len;
-    }
-
-    if (clr_pwd) {
-        sd->pwd_len = 0;
-    }
-
-    if (lock)
-        sd->card_status |= CARD_IS_LOCKED;
-    else
-        sd->card_status &= ~CARD_IS_LOCKED;
-}
-
-static sd_rsp_type_t sd_normal_command(SDState *sd,
-                                       SDRequest req)
-{
-    uint32_t rca = 0x0000;
-    uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg;
-
-    /* Not interpreting this as an app command */
-    sd->card_status &= ~APP_CMD;
-
-    if (sd_cmd_type[req.cmd] == sd_ac || sd_cmd_type[req.cmd] == sd_adtc)
-        rca = req.arg >> 16;
-
-    DPRINTF("CMD%d 0x%08x state %d\n", req.cmd, req.arg, sd->state);
-    switch (req.cmd) {
-    /* Basic commands (Class 0 and Class 1) */
-    case 0:    /* CMD0:   GO_IDLE_STATE */
-        switch (sd->state) {
-        case sd_inactive_state:
-            return sd->spi ? sd_r1 : sd_r0;
-
-        default:
-            sd->state = sd_idle_state;
-            sd_reset(sd, sd->bdrv);
-            return sd->spi ? sd_r1 : sd_r0;
-        }
-        break;
-
-    case 1:    /* CMD1:   SEND_OP_CMD */
-        if (!sd->spi)
-            goto bad_cmd;
-
-        sd->state = sd_transfer_state;
-        return sd_r1;
-
-    case 2:    /* CMD2:   ALL_SEND_CID */
-        if (sd->spi)
-            goto bad_cmd;
-        switch (sd->state) {
-        case sd_ready_state:
-            sd->state = sd_identification_state;
-            return sd_r2_i;
-
-        default:
-            break;
-        }
-        break;
-
-    case 3:    /* CMD3:   SEND_RELATIVE_ADDR */
-        if (sd->spi)
-            goto bad_cmd;
-        switch (sd->state) {
-        case sd_identification_state:
-        case sd_standby_state:
-            sd->state = sd_standby_state;
-            sd_set_rca(sd);
-            return sd_r6;
-
-        default:
-            break;
-        }
-        break;
-
-    case 4:    /* CMD4:   SEND_DSR */
-        if (sd->spi)
-            goto bad_cmd;
-        switch (sd->state) {
-        case sd_standby_state:
-            break;
-
-        default:
-            break;
-        }
-        break;
-
-    case 5: /* CMD5: reserved for SDIO cards */
-        return sd_illegal;
-
-    case 6:    /* CMD6:   SWITCH_FUNCTION */
-        if (sd->spi)
-            goto bad_cmd;
-        switch (sd->mode) {
-        case sd_data_transfer_mode:
-            sd_function_switch(sd, req.arg);
-            sd->state = sd_sendingdata_state;
-            sd->data_start = 0;
-            sd->data_offset = 0;
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    case 7:    /* CMD7:   SELECT/DESELECT_CARD */
-        if (sd->spi)
-            goto bad_cmd;
-        switch (sd->state) {
-        case sd_standby_state:
-            if (sd->rca != rca)
-                return sd_r0;
-
-            sd->state = sd_transfer_state;
-            return sd_r1b;
-
-        case sd_transfer_state:
-        case sd_sendingdata_state:
-            if (sd->rca == rca)
-                break;
-
-            sd->state = sd_standby_state;
-            return sd_r1b;
-
-        case sd_disconnect_state:
-            if (sd->rca != rca)
-                return sd_r0;
-
-            sd->state = sd_programming_state;
-            return sd_r1b;
-
-        case sd_programming_state:
-            if (sd->rca == rca)
-                break;
-
-            sd->state = sd_disconnect_state;
-            return sd_r1b;
-
-        default:
-            break;
-        }
-        break;
-
-    case 8:    /* CMD8:   SEND_IF_COND */
-        /* Physical Layer Specification Version 2.00 command */
-        switch (sd->state) {
-        case sd_idle_state:
-            sd->vhs = 0;
-
-            /* No response if not exactly one VHS bit is set.  */
-            if (!(req.arg >> 8) || (req.arg >> ffs(req.arg & ~0xff)))
-                return sd->spi ? sd_r7 : sd_r0;
-
-            /* Accept.  */
-            sd->vhs = req.arg;
-            return sd_r7;
-
-        default:
-            break;
-        }
-        break;
-
-    case 9:    /* CMD9:   SEND_CSD */
-        switch (sd->state) {
-        case sd_standby_state:
-            if (sd->rca != rca)
-                return sd_r0;
-
-            return sd_r2_s;
-
-        case sd_transfer_state:
-            if (!sd->spi)
-                break;
-            sd->state = sd_sendingdata_state;
-            memcpy(sd->data, sd->csd, 16);
-            sd->data_start = addr;
-            sd->data_offset = 0;
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    case 10:   /* CMD10:  SEND_CID */
-        switch (sd->state) {
-        case sd_standby_state:
-            if (sd->rca != rca)
-                return sd_r0;
-
-            return sd_r2_i;
-
-        case sd_transfer_state:
-            if (!sd->spi)
-                break;
-            sd->state = sd_sendingdata_state;
-            memcpy(sd->data, sd->cid, 16);
-            sd->data_start = addr;
-            sd->data_offset = 0;
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    case 11:   /* CMD11:  READ_DAT_UNTIL_STOP */
-        if (sd->spi)
-            goto bad_cmd;
-        switch (sd->state) {
-        case sd_transfer_state:
-            sd->state = sd_sendingdata_state;
-            sd->data_start = req.arg;
-            sd->data_offset = 0;
-
-            if (sd->data_start + sd->blk_len > sd->size)
-                sd->card_status |= ADDRESS_ERROR;
-            return sd_r0;
-
-        default:
-            break;
-        }
-        break;
-
-    case 12:   /* CMD12:  STOP_TRANSMISSION */
-        switch (sd->state) {
-        case sd_sendingdata_state:
-            sd->state = sd_transfer_state;
-            return sd_r1b;
-
-        case sd_receivingdata_state:
-            sd->state = sd_programming_state;
-            /* Bzzzzzzztt .... Operation complete.  */
-            sd->state = sd_transfer_state;
-            return sd_r1b;
-
-        default:
-            break;
-        }
-        break;
-
-    case 13:   /* CMD13:  SEND_STATUS */
-        switch (sd->mode) {
-        case sd_data_transfer_mode:
-            if (sd->rca != rca)
-                return sd_r0;
-
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    case 15:   /* CMD15:  GO_INACTIVE_STATE */
-        if (sd->spi)
-            goto bad_cmd;
-        switch (sd->mode) {
-        case sd_data_transfer_mode:
-            if (sd->rca != rca)
-                return sd_r0;
-
-            sd->state = sd_inactive_state;
-            return sd_r0;
-
-        default:
-            break;
-        }
-        break;
-
-    /* Block read commands (Classs 2) */
-    case 16:   /* CMD16:  SET_BLOCKLEN */
-        switch (sd->state) {
-        case sd_transfer_state:
-            if (req.arg > (1 << HWBLOCK_SHIFT))
-                sd->card_status |= BLOCK_LEN_ERROR;
-            else
-                sd->blk_len = req.arg;
-
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    case 17:   /* CMD17:  READ_SINGLE_BLOCK */
-        switch (sd->state) {
-        case sd_transfer_state:
-            sd->state = sd_sendingdata_state;
-            sd->data_start = addr;
-            sd->data_offset = 0;
-
-            if (sd->data_start + sd->blk_len > sd->size)
-                sd->card_status |= ADDRESS_ERROR;
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    case 18:   /* CMD18:  READ_MULTIPLE_BLOCK */
-        switch (sd->state) {
-        case sd_transfer_state:
-            sd->state = sd_sendingdata_state;
-            sd->data_start = addr;
-            sd->data_offset = 0;
-
-            if (sd->data_start + sd->blk_len > sd->size)
-                sd->card_status |= ADDRESS_ERROR;
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    /* Block write commands (Class 4) */
-    case 24:   /* CMD24:  WRITE_SINGLE_BLOCK */
-        if (sd->spi)
-            goto unimplemented_cmd;
-        switch (sd->state) {
-        case sd_transfer_state:
-            /* Writing in SPI mode not implemented.  */
-            if (sd->spi)
-                break;
-            sd->state = sd_receivingdata_state;
-            sd->data_start = addr;
-            sd->data_offset = 0;
-            sd->blk_written = 0;
-
-            if (sd->data_start + sd->blk_len > sd->size)
-                sd->card_status |= ADDRESS_ERROR;
-            if (sd_wp_addr(sd, sd->data_start))
-                sd->card_status |= WP_VIOLATION;
-            if (sd->csd[14] & 0x30)
-                sd->card_status |= WP_VIOLATION;
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    case 25:   /* CMD25:  WRITE_MULTIPLE_BLOCK */
-        if (sd->spi)
-            goto unimplemented_cmd;
-        switch (sd->state) {
-        case sd_transfer_state:
-            /* Writing in SPI mode not implemented.  */
-            if (sd->spi)
-                break;
-            sd->state = sd_receivingdata_state;
-            sd->data_start = addr;
-            sd->data_offset = 0;
-            sd->blk_written = 0;
-
-            if (sd->data_start + sd->blk_len > sd->size)
-                sd->card_status |= ADDRESS_ERROR;
-            if (sd_wp_addr(sd, sd->data_start))
-                sd->card_status |= WP_VIOLATION;
-            if (sd->csd[14] & 0x30)
-                sd->card_status |= WP_VIOLATION;
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    case 26:   /* CMD26:  PROGRAM_CID */
-        if (sd->spi)
-            goto bad_cmd;
-        switch (sd->state) {
-        case sd_transfer_state:
-            sd->state = sd_receivingdata_state;
-            sd->data_start = 0;
-            sd->data_offset = 0;
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    case 27:   /* CMD27:  PROGRAM_CSD */
-        if (sd->spi)
-            goto unimplemented_cmd;
-        switch (sd->state) {
-        case sd_transfer_state:
-            sd->state = sd_receivingdata_state;
-            sd->data_start = 0;
-            sd->data_offset = 0;
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    /* Write protection (Class 6) */
-    case 28:   /* CMD28:  SET_WRITE_PROT */
-        switch (sd->state) {
-        case sd_transfer_state:
-            if (addr >= sd->size) {
-                sd->card_status |= ADDRESS_ERROR;
-                return sd_r1b;
-            }
-
-            sd->state = sd_programming_state;
-            set_bit(sd_addr_to_wpnum(addr), sd->wp_groups);
-            /* Bzzzzzzztt .... Operation complete.  */
-            sd->state = sd_transfer_state;
-            return sd_r1b;
-
-        default:
-            break;
-        }
-        break;
-
-    case 29:   /* CMD29:  CLR_WRITE_PROT */
-        switch (sd->state) {
-        case sd_transfer_state:
-            if (addr >= sd->size) {
-                sd->card_status |= ADDRESS_ERROR;
-                return sd_r1b;
-            }
-
-            sd->state = sd_programming_state;
-            clear_bit(sd_addr_to_wpnum(addr), sd->wp_groups);
-            /* Bzzzzzzztt .... Operation complete.  */
-            sd->state = sd_transfer_state;
-            return sd_r1b;
-
-        default:
-            break;
-        }
-        break;
-
-    case 30:   /* CMD30:  SEND_WRITE_PROT */
-        switch (sd->state) {
-        case sd_transfer_state:
-            sd->state = sd_sendingdata_state;
-            *(uint32_t *) sd->data = sd_wpbits(sd, req.arg);
-            sd->data_start = addr;
-            sd->data_offset = 0;
-            return sd_r1b;
-
-        default:
-            break;
-        }
-        break;
-
-    /* Erase commands (Class 5) */
-    case 32:   /* CMD32:  ERASE_WR_BLK_START */
-        switch (sd->state) {
-        case sd_transfer_state:
-            sd->erase_start = req.arg;
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    case 33:   /* CMD33:  ERASE_WR_BLK_END */
-        switch (sd->state) {
-        case sd_transfer_state:
-            sd->erase_end = req.arg;
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    case 38:   /* CMD38:  ERASE */
-        switch (sd->state) {
-        case sd_transfer_state:
-            if (sd->csd[14] & 0x30) {
-                sd->card_status |= WP_VIOLATION;
-                return sd_r1b;
-            }
-
-            sd->state = sd_programming_state;
-            sd_erase(sd);
-            /* Bzzzzzzztt .... Operation complete.  */
-            sd->state = sd_transfer_state;
-            return sd_r1b;
-
-        default:
-            break;
-        }
-        break;
-
-    /* Lock card commands (Class 7) */
-    case 42:   /* CMD42:  LOCK_UNLOCK */
-        if (sd->spi)
-            goto unimplemented_cmd;
-        switch (sd->state) {
-        case sd_transfer_state:
-            sd->state = sd_receivingdata_state;
-            sd->data_start = 0;
-            sd->data_offset = 0;
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    case 52:
-    case 53:
-        /* CMD52, CMD53: reserved for SDIO cards
-         * (see the SDIO Simplified Specification V2.0)
-         * Handle as illegal command but do not complain
-         * on stderr, as some OSes may use these in their
-         * probing for presence of an SDIO card.
-         */
-        return sd_illegal;
-
-    /* Application specific commands (Class 8) */
-    case 55:   /* CMD55:  APP_CMD */
-        if (sd->rca != rca)
-            return sd_r0;
-
-        sd->expecting_acmd = true;
-        sd->card_status |= APP_CMD;
-        return sd_r1;
-
-    case 56:   /* CMD56:  GEN_CMD */
-        fprintf(stderr, "SD: GEN_CMD 0x%08x\n", req.arg);
-
-        switch (sd->state) {
-        case sd_transfer_state:
-            sd->data_offset = 0;
-            if (req.arg & 1)
-                sd->state = sd_sendingdata_state;
-            else
-                sd->state = sd_receivingdata_state;
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    default:
-    bad_cmd:
-        fprintf(stderr, "SD: Unknown CMD%i\n", req.cmd);
-        return sd_illegal;
-
-    unimplemented_cmd:
-        /* Commands that are recognised but not yet implemented in SPI mode.  */
-        fprintf(stderr, "SD: CMD%i not implemented in SPI mode\n", req.cmd);
-        return sd_illegal;
-    }
-
-    fprintf(stderr, "SD: CMD%i in a wrong state\n", req.cmd);
-    return sd_illegal;
-}
-
-static sd_rsp_type_t sd_app_command(SDState *sd,
-                                    SDRequest req)
-{
-    DPRINTF("ACMD%d 0x%08x\n", req.cmd, req.arg);
-    sd->card_status |= APP_CMD;
-    switch (req.cmd) {
-    case 6:    /* ACMD6:  SET_BUS_WIDTH */
-        switch (sd->state) {
-        case sd_transfer_state:
-            sd->sd_status[0] &= 0x3f;
-            sd->sd_status[0] |= (req.arg & 0x03) << 6;
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    case 13:   /* ACMD13: SD_STATUS */
-        switch (sd->state) {
-        case sd_transfer_state:
-            sd->state = sd_sendingdata_state;
-            sd->data_start = 0;
-            sd->data_offset = 0;
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    case 22:   /* ACMD22: SEND_NUM_WR_BLOCKS */
-        switch (sd->state) {
-        case sd_transfer_state:
-            *(uint32_t *) sd->data = sd->blk_written;
-
-            sd->state = sd_sendingdata_state;
-            sd->data_start = 0;
-            sd->data_offset = 0;
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    case 23:   /* ACMD23: SET_WR_BLK_ERASE_COUNT */
-        switch (sd->state) {
-        case sd_transfer_state:
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    case 41:   /* ACMD41: SD_APP_OP_COND */
-        if (sd->spi) {
-            /* SEND_OP_CMD */
-            sd->state = sd_transfer_state;
-            return sd_r1;
-        }
-        switch (sd->state) {
-        case sd_idle_state:
-            /* We accept any voltage.  10000 V is nothing.  */
-            if (req.arg)
-                sd->state = sd_ready_state;
-
-            return sd_r3;
-
-        default:
-            break;
-        }
-        break;
-
-    case 42:   /* ACMD42: SET_CLR_CARD_DETECT */
-        switch (sd->state) {
-        case sd_transfer_state:
-            /* Bringing in the 50KOhm pull-up resistor... Done.  */
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    case 51:   /* ACMD51: SEND_SCR */
-        switch (sd->state) {
-        case sd_transfer_state:
-            sd->state = sd_sendingdata_state;
-            sd->data_start = 0;
-            sd->data_offset = 0;
-            return sd_r1;
-
-        default:
-            break;
-        }
-        break;
-
-    default:
-        /* Fall back to standard commands.  */
-        return sd_normal_command(sd, req);
-    }
-
-    fprintf(stderr, "SD: ACMD%i in a wrong state\n", req.cmd);
-    return sd_illegal;
-}
-
-static int cmd_valid_while_locked(SDState *sd, SDRequest *req)
-{
-    /* Valid commands in locked state:
-     * basic class (0)
-     * lock card class (7)
-     * CMD16
-     * implicitly, the ACMD prefix CMD55
-     * ACMD41 and ACMD42
-     * Anything else provokes an "illegal command" response.
-     */
-    if (sd->expecting_acmd) {
-        return req->cmd == 41 || req->cmd == 42;
-    }
-    if (req->cmd == 16 || req->cmd == 55) {
-        return 1;
-    }
-    return sd_cmd_class[req->cmd] == 0 || sd_cmd_class[req->cmd] == 7;
-}
-
-int sd_do_command(SDState *sd, SDRequest *req,
-                  uint8_t *response) {
-    int last_state;
-    sd_rsp_type_t rtype;
-    int rsplen;
-
-    if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv) || !sd->enable) {
-        return 0;
-    }
-
-    if (sd_req_crc_validate(req)) {
-        sd->card_status |= COM_CRC_ERROR;
-        rtype = sd_illegal;
-        goto send_response;
-    }
-
-    if (sd->card_status & CARD_IS_LOCKED) {
-        if (!cmd_valid_while_locked(sd, req)) {
-            sd->card_status |= ILLEGAL_COMMAND;
-            sd->expecting_acmd = false;
-            fprintf(stderr, "SD: Card is locked\n");
-            rtype = sd_illegal;
-            goto send_response;
-        }
-    }
-
-    last_state = sd->state;
-    sd_set_mode(sd);
-
-    if (sd->expecting_acmd) {
-        sd->expecting_acmd = false;
-        rtype = sd_app_command(sd, *req);
-    } else {
-        rtype = sd_normal_command(sd, *req);
-    }
-
-    if (rtype == sd_illegal) {
-        sd->card_status |= ILLEGAL_COMMAND;
-    } else {
-        /* Valid command, we can update the 'state before command' bits.
-         * (Do this now so they appear in r1 responses.)
-         */
-        sd->current_cmd = req->cmd;
-        sd->card_status &= ~CURRENT_STATE;
-        sd->card_status |= (last_state << 9);
-    }
-
-send_response:
-    switch (rtype) {
-    case sd_r1:
-    case sd_r1b:
-        sd_response_r1_make(sd, response);
-        rsplen = 4;
-        break;
-
-    case sd_r2_i:
-        memcpy(response, sd->cid, sizeof(sd->cid));
-        rsplen = 16;
-        break;
-
-    case sd_r2_s:
-        memcpy(response, sd->csd, sizeof(sd->csd));
-        rsplen = 16;
-        break;
-
-    case sd_r3:
-        sd_response_r3_make(sd, response);
-        rsplen = 4;
-        break;
-
-    case sd_r6:
-        sd_response_r6_make(sd, response);
-        rsplen = 4;
-        break;
-
-    case sd_r7:
-        sd_response_r7_make(sd, response);
-        rsplen = 4;
-        break;
-
-    case sd_r0:
-    case sd_illegal:
-    default:
-        rsplen = 0;
-        break;
-    }
-
-    if (rtype != sd_illegal) {
-        /* Clear the "clear on valid command" status bits now we've
-         * sent any response
-         */
-        sd->card_status &= ~CARD_STATUS_B;
-    }
-
-#ifdef DEBUG_SD
-    if (rsplen) {
-        int i;
-        DPRINTF("Response:");
-        for (i = 0; i < rsplen; i++)
-            fprintf(stderr, " %02x", response[i]);
-        fprintf(stderr, " state %d\n", sd->state);
-    } else {
-        DPRINTF("No response %d\n", sd->state);
-    }
-#endif
-
-    return rsplen;
-}
-
-static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len)
-{
-    uint64_t end = addr + len;
-
-    DPRINTF("sd_blk_read: addr = 0x%08llx, len = %d\n",
-            (unsigned long long) addr, len);
-    if (!sd->bdrv || bdrv_read(sd->bdrv, addr >> 9, sd->buf, 1) < 0) {
-        fprintf(stderr, "sd_blk_read: read error on host side\n");
-        return;
-    }
-
-    if (end > (addr & ~511) + 512) {
-        memcpy(sd->data, sd->buf + (addr & 511), 512 - (addr & 511));
-
-        if (bdrv_read(sd->bdrv, end >> 9, sd->buf, 1) < 0) {
-            fprintf(stderr, "sd_blk_read: read error on host side\n");
-            return;
-        }
-        memcpy(sd->data + 512 - (addr & 511), sd->buf, end & 511);
-    } else
-        memcpy(sd->data, sd->buf + (addr & 511), len);
-}
-
-static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len)
-{
-    uint64_t end = addr + len;
-
-    if ((addr & 511) || len < 512)
-        if (!sd->bdrv || bdrv_read(sd->bdrv, addr >> 9, sd->buf, 1) < 0) {
-            fprintf(stderr, "sd_blk_write: read error on host side\n");
-            return;
-        }
-
-    if (end > (addr & ~511) + 512) {
-        memcpy(sd->buf + (addr & 511), sd->data, 512 - (addr & 511));
-        if (bdrv_write(sd->bdrv, addr >> 9, sd->buf, 1) < 0) {
-            fprintf(stderr, "sd_blk_write: write error on host side\n");
-            return;
-        }
-
-        if (bdrv_read(sd->bdrv, end >> 9, sd->buf, 1) < 0) {
-            fprintf(stderr, "sd_blk_write: read error on host side\n");
-            return;
-        }
-        memcpy(sd->buf, sd->data + 512 - (addr & 511), end & 511);
-        if (bdrv_write(sd->bdrv, end >> 9, sd->buf, 1) < 0) {
-            fprintf(stderr, "sd_blk_write: write error on host side\n");
-        }
-    } else {
-        memcpy(sd->buf + (addr & 511), sd->data, len);
-        if (!sd->bdrv || bdrv_write(sd->bdrv, addr >> 9, sd->buf, 1) < 0) {
-            fprintf(stderr, "sd_blk_write: write error on host side\n");
-        }
-    }
-}
-
-#define BLK_READ_BLOCK(a, len) sd_blk_read(sd, a, len)
-#define BLK_WRITE_BLOCK(a, len)        sd_blk_write(sd, a, len)
-#define APP_READ_BLOCK(a, len) memset(sd->data, 0xec, len)
-#define APP_WRITE_BLOCK(a, len)
-
-void sd_write_data(SDState *sd, uint8_t value)
-{
-    int i;
-
-    if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv) || !sd->enable)
-        return;
-
-    if (sd->state != sd_receivingdata_state) {
-        fprintf(stderr, "sd_write_data: not in Receiving-Data state\n");
-        return;
-    }
-
-    if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
-        return;
-
-    switch (sd->current_cmd) {
-    case 24:   /* CMD24:  WRITE_SINGLE_BLOCK */
-        sd->data[sd->data_offset ++] = value;
-        if (sd->data_offset >= sd->blk_len) {
-            /* TODO: Check CRC before committing */
-            sd->state = sd_programming_state;
-            BLK_WRITE_BLOCK(sd->data_start, sd->data_offset);
-            sd->blk_written ++;
-            sd->csd[14] |= 0x40;
-            /* Bzzzzzzztt .... Operation complete.  */
-            sd->state = sd_transfer_state;
-        }
-        break;
-
-    case 25:   /* CMD25:  WRITE_MULTIPLE_BLOCK */
-        if (sd->data_offset == 0) {
-            /* Start of the block - lets check the address is valid */
-            if (sd->data_start + sd->blk_len > sd->size) {
-                sd->card_status |= ADDRESS_ERROR;
-                break;
-            }
-            if (sd_wp_addr(sd, sd->data_start)) {
-                sd->card_status |= WP_VIOLATION;
-                break;
-            }
-        }
-        sd->data[sd->data_offset++] = value;
-        if (sd->data_offset >= sd->blk_len) {
-            /* TODO: Check CRC before committing */
-            sd->state = sd_programming_state;
-            BLK_WRITE_BLOCK(sd->data_start, sd->data_offset);
-            sd->blk_written++;
-            sd->data_start += sd->blk_len;
-            sd->data_offset = 0;
-            sd->csd[14] |= 0x40;
-
-            /* Bzzzzzzztt .... Operation complete.  */
-            sd->state = sd_receivingdata_state;
-        }
-        break;
-
-    case 26:   /* CMD26:  PROGRAM_CID */
-        sd->data[sd->data_offset ++] = value;
-        if (sd->data_offset >= sizeof(sd->cid)) {
-            /* TODO: Check CRC before committing */
-            sd->state = sd_programming_state;
-            for (i = 0; i < sizeof(sd->cid); i ++)
-                if ((sd->cid[i] | 0x00) != sd->data[i])
-                    sd->card_status |= CID_CSD_OVERWRITE;
-
-            if (!(sd->card_status & CID_CSD_OVERWRITE))
-                for (i = 0; i < sizeof(sd->cid); i ++) {
-                    sd->cid[i] |= 0x00;
-                    sd->cid[i] &= sd->data[i];
-                }
-            /* Bzzzzzzztt .... Operation complete.  */
-            sd->state = sd_transfer_state;
-        }
-        break;
-
-    case 27:   /* CMD27:  PROGRAM_CSD */
-        sd->data[sd->data_offset ++] = value;
-        if (sd->data_offset >= sizeof(sd->csd)) {
-            /* TODO: Check CRC before committing */
-            sd->state = sd_programming_state;
-            for (i = 0; i < sizeof(sd->csd); i ++)
-                if ((sd->csd[i] | sd_csd_rw_mask[i]) !=
-                    (sd->data[i] | sd_csd_rw_mask[i]))
-                    sd->card_status |= CID_CSD_OVERWRITE;
-
-            /* Copy flag (OTP) & Permanent write protect */
-            if (sd->csd[14] & ~sd->data[14] & 0x60)
-                sd->card_status |= CID_CSD_OVERWRITE;
-
-            if (!(sd->card_status & CID_CSD_OVERWRITE))
-                for (i = 0; i < sizeof(sd->csd); i ++) {
-                    sd->csd[i] |= sd_csd_rw_mask[i];
-                    sd->csd[i] &= sd->data[i];
-                }
-            /* Bzzzzzzztt .... Operation complete.  */
-            sd->state = sd_transfer_state;
-        }
-        break;
-
-    case 42:   /* CMD42:  LOCK_UNLOCK */
-        sd->data[sd->data_offset ++] = value;
-        if (sd->data_offset >= sd->blk_len) {
-            /* TODO: Check CRC before committing */
-            sd->state = sd_programming_state;
-            sd_lock_command(sd);
-            /* Bzzzzzzztt .... Operation complete.  */
-            sd->state = sd_transfer_state;
-        }
-        break;
-
-    case 56:   /* CMD56:  GEN_CMD */
-        sd->data[sd->data_offset ++] = value;
-        if (sd->data_offset >= sd->blk_len) {
-            APP_WRITE_BLOCK(sd->data_start, sd->data_offset);
-            sd->state = sd_transfer_state;
-        }
-        break;
-
-    default:
-        fprintf(stderr, "sd_write_data: unknown command\n");
-        break;
-    }
-}
-
-uint8_t sd_read_data(SDState *sd)
-{
-    /* TODO: Append CRCs */
-    uint8_t ret;
-    int io_len;
-
-    if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv) || !sd->enable)
-        return 0x00;
-
-    if (sd->state != sd_sendingdata_state) {
-        fprintf(stderr, "sd_read_data: not in Sending-Data state\n");
-        return 0x00;
-    }
-
-    if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
-        return 0x00;
-
-    io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len;
-
-    switch (sd->current_cmd) {
-    case 6:    /* CMD6:   SWITCH_FUNCTION */
-        ret = sd->data[sd->data_offset ++];
-
-        if (sd->data_offset >= 64)
-            sd->state = sd_transfer_state;
-        break;
-
-    case 9:    /* CMD9:   SEND_CSD */
-    case 10:   /* CMD10:  SEND_CID */
-        ret = sd->data[sd->data_offset ++];
-
-        if (sd->data_offset >= 16)
-            sd->state = sd_transfer_state;
-        break;
-
-    case 11:   /* CMD11:  READ_DAT_UNTIL_STOP */
-        if (sd->data_offset == 0)
-            BLK_READ_BLOCK(sd->data_start, io_len);
-        ret = sd->data[sd->data_offset ++];
-
-        if (sd->data_offset >= io_len) {
-            sd->data_start += io_len;
-            sd->data_offset = 0;
-            if (sd->data_start + io_len > sd->size) {
-                sd->card_status |= ADDRESS_ERROR;
-                break;
-            }
-        }
-        break;
-
-    case 13:   /* ACMD13: SD_STATUS */
-        ret = sd->sd_status[sd->data_offset ++];
-
-        if (sd->data_offset >= sizeof(sd->sd_status))
-            sd->state = sd_transfer_state;
-        break;
-
-    case 17:   /* CMD17:  READ_SINGLE_BLOCK */
-        if (sd->data_offset == 0)
-            BLK_READ_BLOCK(sd->data_start, io_len);
-        ret = sd->data[sd->data_offset ++];
-
-        if (sd->data_offset >= io_len)
-            sd->state = sd_transfer_state;
-        break;
-
-    case 18:   /* CMD18:  READ_MULTIPLE_BLOCK */
-        if (sd->data_offset == 0)
-            BLK_READ_BLOCK(sd->data_start, io_len);
-        ret = sd->data[sd->data_offset ++];
-
-        if (sd->data_offset >= io_len) {
-            sd->data_start += io_len;
-            sd->data_offset = 0;
-            if (sd->data_start + io_len > sd->size) {
-                sd->card_status |= ADDRESS_ERROR;
-                break;
-            }
-        }
-        break;
-
-    case 22:   /* ACMD22: SEND_NUM_WR_BLOCKS */
-        ret = sd->data[sd->data_offset ++];
-
-        if (sd->data_offset >= 4)
-            sd->state = sd_transfer_state;
-        break;
-
-    case 30:   /* CMD30:  SEND_WRITE_PROT */
-        ret = sd->data[sd->data_offset ++];
-
-        if (sd->data_offset >= 4)
-            sd->state = sd_transfer_state;
-        break;
-
-    case 51:   /* ACMD51: SEND_SCR */
-        ret = sd->scr[sd->data_offset ++];
-
-        if (sd->data_offset >= sizeof(sd->scr))
-            sd->state = sd_transfer_state;
-        break;
-
-    case 56:   /* CMD56:  GEN_CMD */
-        if (sd->data_offset == 0)
-            APP_READ_BLOCK(sd->data_start, sd->blk_len);
-        ret = sd->data[sd->data_offset ++];
-
-        if (sd->data_offset >= sd->blk_len)
-            sd->state = sd_transfer_state;
-        break;
-
-    default:
-        fprintf(stderr, "sd_read_data: unknown command\n");
-        return 0x00;
-    }
-
-    return ret;
-}
-
-bool sd_data_ready(SDState *sd)
-{
-    return sd->state == sd_sendingdata_state;
-}
-
-void sd_enable(SDState *sd, bool enable)
-{
-    sd->enable = enable;
-}
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8acce025180c75696ce870bfc0c06364dd3192d9 100644 (file)
@@ -0,0 +1,4 @@
+common-obj-$(CONFIG_PL181) += pl181.o
+common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
+common-obj-$(CONFIG_SD) += sd.o
+common-obj-$(CONFIG_SDHCI) += sdhci.o
diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c
new file mode 100644 (file)
index 0000000..2527296
--- /dev/null
@@ -0,0 +1,515 @@
+/*
+ * Arm PrimeCell PL181 MultiMedia Card Interface
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "sysemu/blockdev.h"
+#include "hw/sysbus.h"
+#include "hw/sd.h"
+
+//#define DEBUG_PL181 1
+
+#ifdef DEBUG_PL181
+#define DPRINTF(fmt, ...) \
+do { printf("pl181: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+#define PL181_FIFO_LEN 16
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    SDState *card;
+    uint32_t clock;
+    uint32_t power;
+    uint32_t cmdarg;
+    uint32_t cmd;
+    uint32_t datatimer;
+    uint32_t datalength;
+    uint32_t respcmd;
+    uint32_t response[4];
+    uint32_t datactrl;
+    uint32_t datacnt;
+    uint32_t status;
+    uint32_t mask[2];
+    int32_t fifo_pos;
+    int32_t fifo_len;
+    /* The linux 2.6.21 driver is buggy, and misbehaves if new data arrives
+       while it is reading the FIFO.  We hack around this be defering
+       subsequent transfers until after the driver polls the status word.
+       http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4446/1
+     */
+    int32_t linux_hack;
+    uint32_t fifo[PL181_FIFO_LEN];
+    qemu_irq irq[2];
+    /* GPIO outputs for 'card is readonly' and 'card inserted' */
+    qemu_irq cardstatus[2];
+} pl181_state;
+
+static const VMStateDescription vmstate_pl181 = {
+    .name = "pl181",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(clock, pl181_state),
+        VMSTATE_UINT32(power, pl181_state),
+        VMSTATE_UINT32(cmdarg, pl181_state),
+        VMSTATE_UINT32(cmd, pl181_state),
+        VMSTATE_UINT32(datatimer, pl181_state),
+        VMSTATE_UINT32(datalength, pl181_state),
+        VMSTATE_UINT32(respcmd, pl181_state),
+        VMSTATE_UINT32_ARRAY(response, pl181_state, 4),
+        VMSTATE_UINT32(datactrl, pl181_state),
+        VMSTATE_UINT32(datacnt, pl181_state),
+        VMSTATE_UINT32(status, pl181_state),
+        VMSTATE_UINT32_ARRAY(mask, pl181_state, 2),
+        VMSTATE_INT32(fifo_pos, pl181_state),
+        VMSTATE_INT32(fifo_len, pl181_state),
+        VMSTATE_INT32(linux_hack, pl181_state),
+        VMSTATE_UINT32_ARRAY(fifo, pl181_state, PL181_FIFO_LEN),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+#define PL181_CMD_INDEX     0x3f
+#define PL181_CMD_RESPONSE  (1 << 6)
+#define PL181_CMD_LONGRESP  (1 << 7)
+#define PL181_CMD_INTERRUPT (1 << 8)
+#define PL181_CMD_PENDING   (1 << 9)
+#define PL181_CMD_ENABLE    (1 << 10)
+
+#define PL181_DATA_ENABLE             (1 << 0)
+#define PL181_DATA_DIRECTION          (1 << 1)
+#define PL181_DATA_MODE               (1 << 2)
+#define PL181_DATA_DMAENABLE          (1 << 3)
+
+#define PL181_STATUS_CMDCRCFAIL       (1 << 0)
+#define PL181_STATUS_DATACRCFAIL      (1 << 1)
+#define PL181_STATUS_CMDTIMEOUT       (1 << 2)
+#define PL181_STATUS_DATATIMEOUT      (1 << 3)
+#define PL181_STATUS_TXUNDERRUN       (1 << 4)
+#define PL181_STATUS_RXOVERRUN        (1 << 5)
+#define PL181_STATUS_CMDRESPEND       (1 << 6)
+#define PL181_STATUS_CMDSENT          (1 << 7)
+#define PL181_STATUS_DATAEND          (1 << 8)
+#define PL181_STATUS_DATABLOCKEND     (1 << 10)
+#define PL181_STATUS_CMDACTIVE        (1 << 11)
+#define PL181_STATUS_TXACTIVE         (1 << 12)
+#define PL181_STATUS_RXACTIVE         (1 << 13)
+#define PL181_STATUS_TXFIFOHALFEMPTY  (1 << 14)
+#define PL181_STATUS_RXFIFOHALFFULL   (1 << 15)
+#define PL181_STATUS_TXFIFOFULL       (1 << 16)
+#define PL181_STATUS_RXFIFOFULL       (1 << 17)
+#define PL181_STATUS_TXFIFOEMPTY      (1 << 18)
+#define PL181_STATUS_RXFIFOEMPTY      (1 << 19)
+#define PL181_STATUS_TXDATAAVLBL      (1 << 20)
+#define PL181_STATUS_RXDATAAVLBL      (1 << 21)
+
+#define PL181_STATUS_TX_FIFO (PL181_STATUS_TXACTIVE \
+                             |PL181_STATUS_TXFIFOHALFEMPTY \
+                             |PL181_STATUS_TXFIFOFULL \
+                             |PL181_STATUS_TXFIFOEMPTY \
+                             |PL181_STATUS_TXDATAAVLBL)
+#define PL181_STATUS_RX_FIFO (PL181_STATUS_RXACTIVE \
+                             |PL181_STATUS_RXFIFOHALFFULL \
+                             |PL181_STATUS_RXFIFOFULL \
+                             |PL181_STATUS_RXFIFOEMPTY \
+                             |PL181_STATUS_RXDATAAVLBL)
+
+static const unsigned char pl181_id[] =
+{ 0x81, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl181_update(pl181_state *s)
+{
+    int i;
+    for (i = 0; i < 2; i++) {
+        qemu_set_irq(s->irq[i], (s->status & s->mask[i]) != 0);
+    }
+}
+
+static void pl181_fifo_push(pl181_state *s, uint32_t value)
+{
+    int n;
+
+    if (s->fifo_len == PL181_FIFO_LEN) {
+        fprintf(stderr, "pl181: FIFO overflow\n");
+        return;
+    }
+    n = (s->fifo_pos + s->fifo_len) & (PL181_FIFO_LEN - 1);
+    s->fifo_len++;
+    s->fifo[n] = value;
+    DPRINTF("FIFO push %08x\n", (int)value);
+}
+
+static uint32_t pl181_fifo_pop(pl181_state *s)
+{
+    uint32_t value;
+
+    if (s->fifo_len == 0) {
+        fprintf(stderr, "pl181: FIFO underflow\n");
+        return 0;
+    }
+    value = s->fifo[s->fifo_pos];
+    s->fifo_len--;
+    s->fifo_pos = (s->fifo_pos + 1) & (PL181_FIFO_LEN - 1);
+    DPRINTF("FIFO pop %08x\n", (int)value);
+    return value;
+}
+
+static void pl181_send_command(pl181_state *s)
+{
+    SDRequest request;
+    uint8_t response[16];
+    int rlen;
+
+    request.cmd = s->cmd & PL181_CMD_INDEX;
+    request.arg = s->cmdarg;
+    DPRINTF("Command %d %08x\n", request.cmd, request.arg);
+    rlen = sd_do_command(s->card, &request, response);
+    if (rlen < 0)
+        goto error;
+    if (s->cmd & PL181_CMD_RESPONSE) {
+#define RWORD(n) ((response[n] << 24) | (response[n + 1] << 16) \
+                  | (response[n + 2] << 8) | response[n + 3])
+        if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP)))
+            goto error;
+        if (rlen != 4 && rlen != 16)
+            goto error;
+        s->response[0] = RWORD(0);
+        if (rlen == 4) {
+            s->response[1] = s->response[2] = s->response[3] = 0;
+        } else {
+            s->response[1] = RWORD(4);
+            s->response[2] = RWORD(8);
+            s->response[3] = RWORD(12) & ~1;
+        }
+        DPRINTF("Response received\n");
+        s->status |= PL181_STATUS_CMDRESPEND;
+#undef RWORD
+    } else {
+        DPRINTF("Command sent\n");
+        s->status |= PL181_STATUS_CMDSENT;
+    }
+    return;
+
+error:
+    DPRINTF("Timeout\n");
+    s->status |= PL181_STATUS_CMDTIMEOUT;
+}
+
+/* Transfer data between the card and the FIFO.  This is complicated by
+   the FIFO holding 32-bit words and the card taking data in single byte
+   chunks.  FIFO bytes are transferred in little-endian order.  */
+
+static void pl181_fifo_run(pl181_state *s)
+{
+    uint32_t bits;
+    uint32_t value = 0;
+    int n;
+    int is_read;
+
+    is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0;
+    if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card))
+            && !s->linux_hack) {
+        if (is_read) {
+            n = 0;
+            while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) {
+                value |= (uint32_t)sd_read_data(s->card) << (n * 8);
+                s->datacnt--;
+                n++;
+                if (n == 4) {
+                    pl181_fifo_push(s, value);
+                    n = 0;
+                    value = 0;
+                }
+            }
+            if (n != 0) {
+                pl181_fifo_push(s, value);
+            }
+        } else { /* write */
+            n = 0;
+            while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) {
+                if (n == 0) {
+                    value = pl181_fifo_pop(s);
+                    n = 4;
+                }
+                n--;
+                s->datacnt--;
+                sd_write_data(s->card, value & 0xff);
+                value >>= 8;
+            }
+        }
+    }
+    s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO);
+    if (s->datacnt == 0) {
+        s->status |= PL181_STATUS_DATAEND;
+        /* HACK: */
+        s->status |= PL181_STATUS_DATABLOCKEND;
+        DPRINTF("Transfer Complete\n");
+    }
+    if (s->datacnt == 0 && s->fifo_len == 0) {
+        s->datactrl &= ~PL181_DATA_ENABLE;
+        DPRINTF("Data engine idle\n");
+    } else {
+        /* Update FIFO bits.  */
+        bits = PL181_STATUS_TXACTIVE | PL181_STATUS_RXACTIVE;
+        if (s->fifo_len == 0) {
+            bits |= PL181_STATUS_TXFIFOEMPTY;
+            bits |= PL181_STATUS_RXFIFOEMPTY;
+        } else {
+            bits |= PL181_STATUS_TXDATAAVLBL;
+            bits |= PL181_STATUS_RXDATAAVLBL;
+        }
+        if (s->fifo_len == 16) {
+            bits |= PL181_STATUS_TXFIFOFULL;
+            bits |= PL181_STATUS_RXFIFOFULL;
+        }
+        if (s->fifo_len <= 8) {
+            bits |= PL181_STATUS_TXFIFOHALFEMPTY;
+        }
+        if (s->fifo_len >= 8) {
+            bits |= PL181_STATUS_RXFIFOHALFFULL;
+        }
+        if (s->datactrl & PL181_DATA_DIRECTION) {
+            bits &= PL181_STATUS_RX_FIFO;
+        } else {
+            bits &= PL181_STATUS_TX_FIFO;
+        }
+        s->status |= bits;
+    }
+}
+
+static uint64_t pl181_read(void *opaque, hwaddr offset,
+                           unsigned size)
+{
+    pl181_state *s = (pl181_state *)opaque;
+    uint32_t tmp;
+
+    if (offset >= 0xfe0 && offset < 0x1000) {
+        return pl181_id[(offset - 0xfe0) >> 2];
+    }
+    switch (offset) {
+    case 0x00: /* Power */
+        return s->power;
+    case 0x04: /* Clock */
+        return s->clock;
+    case 0x08: /* Argument */
+        return s->cmdarg;
+    case 0x0c: /* Command */
+        return s->cmd;
+    case 0x10: /* RespCmd */
+        return s->respcmd;
+    case 0x14: /* Response0 */
+        return s->response[0];
+    case 0x18: /* Response1 */
+        return s->response[1];
+    case 0x1c: /* Response2 */
+        return s->response[2];
+    case 0x20: /* Response3 */
+        return s->response[3];
+    case 0x24: /* DataTimer */
+        return s->datatimer;
+    case 0x28: /* DataLength */
+        return s->datalength;
+    case 0x2c: /* DataCtrl */
+        return s->datactrl;
+    case 0x30: /* DataCnt */
+        return s->datacnt;
+    case 0x34: /* Status */
+        tmp = s->status;
+        if (s->linux_hack) {
+            s->linux_hack = 0;
+            pl181_fifo_run(s);
+            pl181_update(s);
+        }
+        return tmp;
+    case 0x3c: /* Mask0 */
+        return s->mask[0];
+    case 0x40: /* Mask1 */
+        return s->mask[1];
+    case 0x48: /* FifoCnt */
+        /* The documentation is somewhat vague about exactly what FifoCnt
+           does.  On real hardware it appears to be when decrememnted
+           when a word is transferred between the FIFO and the serial
+           data engine.  DataCnt is decremented after each byte is
+           transferred between the serial engine and the card.
+           We don't emulate this level of detail, so both can be the same.  */
+        tmp = (s->datacnt + 3) >> 2;
+        if (s->linux_hack) {
+            s->linux_hack = 0;
+            pl181_fifo_run(s);
+            pl181_update(s);
+        }
+        return tmp;
+    case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
+    case 0x90: case 0x94: case 0x98: case 0x9c:
+    case 0xa0: case 0xa4: case 0xa8: case 0xac:
+    case 0xb0: case 0xb4: case 0xb8: case 0xbc:
+        if (s->fifo_len == 0) {
+            qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO read\n");
+            return 0;
+        } else {
+            uint32_t value;
+            value = pl181_fifo_pop(s);
+            s->linux_hack = 1;
+            pl181_fifo_run(s);
+            pl181_update(s);
+            return value;
+        }
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl181_read: Bad offset %x\n", (int)offset);
+        return 0;
+    }
+}
+
+static void pl181_write(void *opaque, hwaddr offset,
+                        uint64_t value, unsigned size)
+{
+    pl181_state *s = (pl181_state *)opaque;
+
+    switch (offset) {
+    case 0x00: /* Power */
+        s->power = value & 0xff;
+        break;
+    case 0x04: /* Clock */
+        s->clock = value & 0xff;
+        break;
+    case 0x08: /* Argument */
+        s->cmdarg = value;
+        break;
+    case 0x0c: /* Command */
+        s->cmd = value;
+        if (s->cmd & PL181_CMD_ENABLE) {
+            if (s->cmd & PL181_CMD_INTERRUPT) {
+                qemu_log_mask(LOG_UNIMP,
+                              "pl181: Interrupt mode not implemented\n");
+            } if (s->cmd & PL181_CMD_PENDING) {
+                qemu_log_mask(LOG_UNIMP,
+                              "pl181: Pending commands not implemented\n");
+            } else {
+                pl181_send_command(s);
+                pl181_fifo_run(s);
+            }
+            /* The command has completed one way or the other.  */
+            s->cmd &= ~PL181_CMD_ENABLE;
+        }
+        break;
+    case 0x24: /* DataTimer */
+        s->datatimer = value;
+        break;
+    case 0x28: /* DataLength */
+        s->datalength = value & 0xffff;
+        break;
+    case 0x2c: /* DataCtrl */
+        s->datactrl = value & 0xff;
+        if (value & PL181_DATA_ENABLE) {
+            s->datacnt = s->datalength;
+            pl181_fifo_run(s);
+        }
+        break;
+    case 0x38: /* Clear */
+        s->status &= ~(value & 0x7ff);
+        break;
+    case 0x3c: /* Mask0 */
+        s->mask[0] = value;
+        break;
+    case 0x40: /* Mask1 */
+        s->mask[1] = value;
+        break;
+    case 0x80: case 0x84: case 0x88: case 0x8c: /* FifoData */
+    case 0x90: case 0x94: case 0x98: case 0x9c:
+    case 0xa0: case 0xa4: case 0xa8: case 0xac:
+    case 0xb0: case 0xb4: case 0xb8: case 0xbc:
+        if (s->datacnt == 0) {
+            qemu_log_mask(LOG_GUEST_ERROR, "pl181: Unexpected FIFO write\n");
+        } else {
+            pl181_fifo_push(s, value);
+            pl181_fifo_run(s);
+        }
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl181_write: Bad offset %x\n", (int)offset);
+    }
+    pl181_update(s);
+}
+
+static const MemoryRegionOps pl181_ops = {
+    .read = pl181_read,
+    .write = pl181_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void pl181_reset(DeviceState *d)
+{
+    pl181_state *s = DO_UPCAST(pl181_state, busdev.qdev, d);
+
+    s->power = 0;
+    s->cmdarg = 0;
+    s->cmd = 0;
+    s->datatimer = 0;
+    s->datalength = 0;
+    s->respcmd = 0;
+    s->response[0] = 0;
+    s->response[1] = 0;
+    s->response[2] = 0;
+    s->response[3] = 0;
+    s->datatimer = 0;
+    s->datalength = 0;
+    s->datactrl = 0;
+    s->datacnt = 0;
+    s->status = 0;
+    s->linux_hack = 0;
+    s->mask[0] = 0;
+    s->mask[1] = 0;
+
+    /* We can assume our GPIO outputs have been wired up now */
+    sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]);
+}
+
+static int pl181_init(SysBusDevice *dev)
+{
+    pl181_state *s = FROM_SYSBUS(pl181_state, dev);
+    DriveInfo *dinfo;
+
+    memory_region_init_io(&s->iomem, &pl181_ops, s, "pl181", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq[0]);
+    sysbus_init_irq(dev, &s->irq[1]);
+    qdev_init_gpio_out(&s->busdev.qdev, s->cardstatus, 2);
+    dinfo = drive_get_next(IF_SD);
+    s->card = sd_init(dinfo ? dinfo->bdrv : NULL, 0);
+    return 0;
+}
+
+static void pl181_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *k = DEVICE_CLASS(klass);
+
+    sdc->init = pl181_init;
+    k->vmsd = &vmstate_pl181;
+    k->reset = pl181_reset;
+    k->no_user = 1;
+}
+
+static const TypeInfo pl181_info = {
+    .name          = "pl181",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl181_state),
+    .class_init    = pl181_class_init,
+};
+
+static void pl181_register_types(void)
+{
+    type_register_static(&pl181_info);
+}
+
+type_init(pl181_register_types)
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
new file mode 100644 (file)
index 0000000..66c4014
--- /dev/null
@@ -0,0 +1,1764 @@
+/*
+ * SD Memory Card emulation as defined in the "SD Memory Card Physical
+ * layer specification, Version 1.10."
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski  <balrog@zabor.org>
+ * Copyright (c) 2007 CodeSourcery
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "hw/hw.h"
+#include "block/block.h"
+#include "hw/sd.h"
+#include "qemu/bitmap.h"
+
+//#define DEBUG_SD 1
+
+#ifdef DEBUG_SD
+#define DPRINTF(fmt, ...) \
+do { fprintf(stderr, "SD: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+typedef enum {
+    sd_r0 = 0,    /* no response */
+    sd_r1,        /* normal response command */
+    sd_r2_i,      /* CID register */
+    sd_r2_s,      /* CSD register */
+    sd_r3,        /* OCR register */
+    sd_r6 = 6,    /* Published RCA response */
+    sd_r7,        /* Operating voltage */
+    sd_r1b = -1,
+    sd_illegal = -2,
+} sd_rsp_type_t;
+
+enum SDCardModes {
+    sd_inactive,
+    sd_card_identification_mode,
+    sd_data_transfer_mode,
+};
+
+enum SDCardStates {
+    sd_inactive_state = -1,
+    sd_idle_state = 0,
+    sd_ready_state,
+    sd_identification_state,
+    sd_standby_state,
+    sd_transfer_state,
+    sd_sendingdata_state,
+    sd_receivingdata_state,
+    sd_programming_state,
+    sd_disconnect_state,
+};
+
+struct SDState {
+    uint32_t mode;    /* current card mode, one of SDCardModes */
+    int32_t state;    /* current card state, one of SDCardStates */
+    uint32_t ocr;
+    uint8_t scr[8];
+    uint8_t cid[16];
+    uint8_t csd[16];
+    uint16_t rca;
+    uint32_t card_status;
+    uint8_t sd_status[64];
+    uint32_t vhs;
+    bool wp_switch;
+    unsigned long *wp_groups;
+    int32_t wpgrps_size;
+    uint64_t size;
+    uint32_t blk_len;
+    uint32_t erase_start;
+    uint32_t erase_end;
+    uint8_t pwd[16];
+    uint32_t pwd_len;
+    uint8_t function_group[6];
+
+    bool spi;
+    uint8_t current_cmd;
+    /* True if we will handle the next command as an ACMD. Note that this does
+     * *not* track the APP_CMD status bit!
+     */
+    bool expecting_acmd;
+    uint32_t blk_written;
+    uint64_t data_start;
+    uint32_t data_offset;
+    uint8_t data[512];
+    qemu_irq readonly_cb;
+    qemu_irq inserted_cb;
+    BlockDriverState *bdrv;
+    uint8_t *buf;
+
+    bool enable;
+};
+
+static void sd_set_mode(SDState *sd)
+{
+    switch (sd->state) {
+    case sd_inactive_state:
+        sd->mode = sd_inactive;
+        break;
+
+    case sd_idle_state:
+    case sd_ready_state:
+    case sd_identification_state:
+        sd->mode = sd_card_identification_mode;
+        break;
+
+    case sd_standby_state:
+    case sd_transfer_state:
+    case sd_sendingdata_state:
+    case sd_receivingdata_state:
+    case sd_programming_state:
+    case sd_disconnect_state:
+        sd->mode = sd_data_transfer_mode;
+        break;
+    }
+}
+
+static const sd_cmd_type_t sd_cmd_type[64] = {
+    sd_bc,   sd_none, sd_bcr,  sd_bcr,  sd_none, sd_none, sd_none, sd_ac,
+    sd_bcr,  sd_ac,   sd_ac,   sd_adtc, sd_ac,   sd_ac,   sd_none, sd_ac,
+    sd_ac,   sd_adtc, sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none,
+    sd_adtc, sd_adtc, sd_adtc, sd_adtc, sd_ac,   sd_ac,   sd_adtc, sd_none,
+    sd_ac,   sd_ac,   sd_none, sd_none, sd_none, sd_none, sd_ac,   sd_none,
+    sd_none, sd_none, sd_bc,   sd_none, sd_none, sd_none, sd_none, sd_none,
+    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_ac,
+    sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
+};
+
+static const sd_cmd_type_t sd_acmd_type[64] = {
+    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_ac,   sd_none,
+    sd_none, sd_none, sd_none, sd_none, sd_none, sd_adtc, sd_none, sd_none,
+    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_adtc, sd_ac,
+    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
+    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
+    sd_none, sd_bcr,  sd_ac,   sd_none, sd_none, sd_none, sd_none, sd_none,
+    sd_none, sd_none, sd_none, sd_adtc, sd_none, sd_none, sd_none, sd_none,
+    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
+};
+
+static const int sd_cmd_class[64] = {
+    0,  0,  0,  0,  0,  9, 10,  0,  0,  0,  0,  1,  0,  0,  0,  0,
+    2,  2,  2,  2,  3,  3,  3,  3,  4,  4,  4,  4,  6,  6,  6,  6,
+    5,  5, 10, 10, 10, 10,  5,  9,  9,  9,  7,  7,  7,  7,  7,  7,
+    7,  7, 10,  7,  9,  9,  9,  8,  8, 10,  8,  8,  8,  8,  8,  8,
+};
+
+static uint8_t sd_crc7(void *message, size_t width)
+{
+    int i, bit;
+    uint8_t shift_reg = 0x00;
+    uint8_t *msg = (uint8_t *) message;
+
+    for (i = 0; i < width; i ++, msg ++)
+        for (bit = 7; bit >= 0; bit --) {
+            shift_reg <<= 1;
+            if ((shift_reg >> 7) ^ ((*msg >> bit) & 1))
+                shift_reg ^= 0x89;
+        }
+
+    return shift_reg;
+}
+
+static uint16_t sd_crc16(void *message, size_t width)
+{
+    int i, bit;
+    uint16_t shift_reg = 0x0000;
+    uint16_t *msg = (uint16_t *) message;
+    width <<= 1;
+
+    for (i = 0; i < width; i ++, msg ++)
+        for (bit = 15; bit >= 0; bit --) {
+            shift_reg <<= 1;
+            if ((shift_reg >> 15) ^ ((*msg >> bit) & 1))
+                shift_reg ^= 0x1011;
+        }
+
+    return shift_reg;
+}
+
+static void sd_set_ocr(SDState *sd)
+{
+    /* All voltages OK, card power-up OK, Standard Capacity SD Memory Card */
+    sd->ocr = 0x80ffff00;
+}
+
+static void sd_set_scr(SDState *sd)
+{
+    sd->scr[0] = 0x00;         /* SCR Structure */
+    sd->scr[1] = 0x2f;         /* SD Security Support */
+    sd->scr[2] = 0x00;
+    sd->scr[3] = 0x00;
+    sd->scr[4] = 0x00;
+    sd->scr[5] = 0x00;
+    sd->scr[6] = 0x00;
+    sd->scr[7] = 0x00;
+}
+
+#define MID    0xaa
+#define OID    "XY"
+#define PNM    "QEMU!"
+#define PRV    0x01
+#define MDT_YR 2006
+#define MDT_MON        2
+
+static void sd_set_cid(SDState *sd)
+{
+    sd->cid[0] = MID;          /* Fake card manufacturer ID (MID) */
+    sd->cid[1] = OID[0];       /* OEM/Application ID (OID) */
+    sd->cid[2] = OID[1];
+    sd->cid[3] = PNM[0];       /* Fake product name (PNM) */
+    sd->cid[4] = PNM[1];
+    sd->cid[5] = PNM[2];
+    sd->cid[6] = PNM[3];
+    sd->cid[7] = PNM[4];
+    sd->cid[8] = PRV;          /* Fake product revision (PRV) */
+    sd->cid[9] = 0xde;         /* Fake serial number (PSN) */
+    sd->cid[10] = 0xad;
+    sd->cid[11] = 0xbe;
+    sd->cid[12] = 0xef;
+    sd->cid[13] = 0x00 |       /* Manufacture date (MDT) */
+        ((MDT_YR - 2000) / 10);
+    sd->cid[14] = ((MDT_YR % 10) << 4) | MDT_MON;
+    sd->cid[15] = (sd_crc7(sd->cid, 15) << 1) | 1;
+}
+
+#define HWBLOCK_SHIFT  9                       /* 512 bytes */
+#define SECTOR_SHIFT   5                       /* 16 kilobytes */
+#define WPGROUP_SHIFT  7                       /* 2 megs */
+#define CMULT_SHIFT    9                       /* 512 times HWBLOCK_SIZE */
+#define WPGROUP_SIZE   (1 << (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT))
+
+static const uint8_t sd_csd_rw_mask[16] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfe,
+};
+
+static void sd_set_csd(SDState *sd, uint64_t size)
+{
+    uint32_t csize = (size >> (CMULT_SHIFT + HWBLOCK_SHIFT)) - 1;
+    uint32_t sectsize = (1 << (SECTOR_SHIFT + 1)) - 1;
+    uint32_t wpsize = (1 << (WPGROUP_SHIFT + 1)) - 1;
+
+    if (size <= 0x40000000) {  /* Standard Capacity SD */
+        sd->csd[0] = 0x00;     /* CSD structure */
+        sd->csd[1] = 0x26;     /* Data read access-time-1 */
+        sd->csd[2] = 0x00;     /* Data read access-time-2 */
+        sd->csd[3] = 0x5a;     /* Max. data transfer rate */
+        sd->csd[4] = 0x5f;     /* Card Command Classes */
+        sd->csd[5] = 0x50 |    /* Max. read data block length */
+            HWBLOCK_SHIFT;
+        sd->csd[6] = 0xe0 |    /* Partial block for read allowed */
+            ((csize >> 10) & 0x03);
+        sd->csd[7] = 0x00 |    /* Device size */
+            ((csize >> 2) & 0xff);
+        sd->csd[8] = 0x3f |    /* Max. read current */
+            ((csize << 6) & 0xc0);
+        sd->csd[9] = 0xfc |    /* Max. write current */
+            ((CMULT_SHIFT - 2) >> 1);
+        sd->csd[10] = 0x40 |   /* Erase sector size */
+            (((CMULT_SHIFT - 2) << 7) & 0x80) | (sectsize >> 1);
+        sd->csd[11] = 0x00 |   /* Write protect group size */
+            ((sectsize << 7) & 0x80) | wpsize;
+        sd->csd[12] = 0x90 |   /* Write speed factor */
+            (HWBLOCK_SHIFT >> 2);
+        sd->csd[13] = 0x20 |   /* Max. write data block length */
+            ((HWBLOCK_SHIFT << 6) & 0xc0);
+        sd->csd[14] = 0x00;    /* File format group */
+        sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1;
+    } else {                   /* SDHC */
+        size /= 512 * 1024;
+        size -= 1;
+        sd->csd[0] = 0x40;
+        sd->csd[1] = 0x0e;
+        sd->csd[2] = 0x00;
+        sd->csd[3] = 0x32;
+        sd->csd[4] = 0x5b;
+        sd->csd[5] = 0x59;
+        sd->csd[6] = 0x00;
+        sd->csd[7] = (size >> 16) & 0xff;
+        sd->csd[8] = (size >> 8) & 0xff;
+        sd->csd[9] = (size & 0xff);
+        sd->csd[10] = 0x7f;
+        sd->csd[11] = 0x80;
+        sd->csd[12] = 0x0a;
+        sd->csd[13] = 0x40;
+        sd->csd[14] = 0x00;
+        sd->csd[15] = 0x00;
+        sd->ocr |= 1 << 30;    /* High Capacity SD Memort Card */
+    }
+}
+
+static void sd_set_rca(SDState *sd)
+{
+    sd->rca += 0x4567;
+}
+
+/* Card status bits, split by clear condition:
+ * A : According to the card current state
+ * B : Always related to the previous command
+ * C : Cleared by read
+ */
+#define CARD_STATUS_A  0x02004100
+#define CARD_STATUS_B  0x00c01e00
+#define CARD_STATUS_C  0xfd39a028
+
+static void sd_set_cardstatus(SDState *sd)
+{
+    sd->card_status = 0x00000100;
+}
+
+static void sd_set_sdstatus(SDState *sd)
+{
+    memset(sd->sd_status, 0, 64);
+}
+
+static int sd_req_crc_validate(SDRequest *req)
+{
+    uint8_t buffer[5];
+    buffer[0] = 0x40 | req->cmd;
+    buffer[1] = (req->arg >> 24) & 0xff;
+    buffer[2] = (req->arg >> 16) & 0xff;
+    buffer[3] = (req->arg >> 8) & 0xff;
+    buffer[4] = (req->arg >> 0) & 0xff;
+    return 0;
+    return sd_crc7(buffer, 5) != req->crc;     /* TODO */
+}
+
+static void sd_response_r1_make(SDState *sd, uint8_t *response)
+{
+    uint32_t status = sd->card_status;
+    /* Clear the "clear on read" status bits */
+    sd->card_status &= ~CARD_STATUS_C;
+
+    response[0] = (status >> 24) & 0xff;
+    response[1] = (status >> 16) & 0xff;
+    response[2] = (status >> 8) & 0xff;
+    response[3] = (status >> 0) & 0xff;
+}
+
+static void sd_response_r3_make(SDState *sd, uint8_t *response)
+{
+    response[0] = (sd->ocr >> 24) & 0xff;
+    response[1] = (sd->ocr >> 16) & 0xff;
+    response[2] = (sd->ocr >> 8) & 0xff;
+    response[3] = (sd->ocr >> 0) & 0xff;
+}
+
+static void sd_response_r6_make(SDState *sd, uint8_t *response)
+{
+    uint16_t arg;
+    uint16_t status;
+
+    arg = sd->rca;
+    status = ((sd->card_status >> 8) & 0xc000) |
+             ((sd->card_status >> 6) & 0x2000) |
+              (sd->card_status & 0x1fff);
+    sd->card_status &= ~(CARD_STATUS_C & 0xc81fff);
+
+    response[0] = (arg >> 8) & 0xff;
+    response[1] = arg & 0xff;
+    response[2] = (status >> 8) & 0xff;
+    response[3] = status & 0xff;
+}
+
+static void sd_response_r7_make(SDState *sd, uint8_t *response)
+{
+    response[0] = (sd->vhs >> 24) & 0xff;
+    response[1] = (sd->vhs >> 16) & 0xff;
+    response[2] = (sd->vhs >>  8) & 0xff;
+    response[3] = (sd->vhs >>  0) & 0xff;
+}
+
+static inline uint64_t sd_addr_to_wpnum(uint64_t addr)
+{
+    return addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT);
+}
+
+static void sd_reset(SDState *sd, BlockDriverState *bdrv)
+{
+    uint64_t size;
+    uint64_t sect;
+
+    if (bdrv) {
+        bdrv_get_geometry(bdrv, &sect);
+    } else {
+        sect = 0;
+    }
+    size = sect << 9;
+
+    sect = sd_addr_to_wpnum(size) + 1;
+
+    sd->state = sd_idle_state;
+    sd->rca = 0x0000;
+    sd_set_ocr(sd);
+    sd_set_scr(sd);
+    sd_set_cid(sd);
+    sd_set_csd(sd, size);
+    sd_set_cardstatus(sd);
+    sd_set_sdstatus(sd);
+
+    sd->bdrv = bdrv;
+
+    if (sd->wp_groups)
+        g_free(sd->wp_groups);
+    sd->wp_switch = bdrv ? bdrv_is_read_only(bdrv) : false;
+    sd->wpgrps_size = sect;
+    sd->wp_groups = bitmap_new(sd->wpgrps_size);
+    memset(sd->function_group, 0, sizeof(sd->function_group));
+    sd->erase_start = 0;
+    sd->erase_end = 0;
+    sd->size = size;
+    sd->blk_len = 0x200;
+    sd->pwd_len = 0;
+    sd->expecting_acmd = false;
+}
+
+static void sd_cardchange(void *opaque, bool load)
+{
+    SDState *sd = opaque;
+
+    qemu_set_irq(sd->inserted_cb, bdrv_is_inserted(sd->bdrv));
+    if (bdrv_is_inserted(sd->bdrv)) {
+        sd_reset(sd, sd->bdrv);
+        qemu_set_irq(sd->readonly_cb, sd->wp_switch);
+    }
+}
+
+static const BlockDevOps sd_block_ops = {
+    .change_media_cb = sd_cardchange,
+};
+
+static const VMStateDescription sd_vmstate = {
+    .name = "sd-card",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(mode, SDState),
+        VMSTATE_INT32(state, SDState),
+        VMSTATE_UINT8_ARRAY(cid, SDState, 16),
+        VMSTATE_UINT8_ARRAY(csd, SDState, 16),
+        VMSTATE_UINT16(rca, SDState),
+        VMSTATE_UINT32(card_status, SDState),
+        VMSTATE_PARTIAL_BUFFER(sd_status, SDState, 1),
+        VMSTATE_UINT32(vhs, SDState),
+        VMSTATE_BITMAP(wp_groups, SDState, 0, wpgrps_size),
+        VMSTATE_UINT32(blk_len, SDState),
+        VMSTATE_UINT32(erase_start, SDState),
+        VMSTATE_UINT32(erase_end, SDState),
+        VMSTATE_UINT8_ARRAY(pwd, SDState, 16),
+        VMSTATE_UINT32(pwd_len, SDState),
+        VMSTATE_UINT8_ARRAY(function_group, SDState, 6),
+        VMSTATE_UINT8(current_cmd, SDState),
+        VMSTATE_BOOL(expecting_acmd, SDState),
+        VMSTATE_UINT32(blk_written, SDState),
+        VMSTATE_UINT64(data_start, SDState),
+        VMSTATE_UINT32(data_offset, SDState),
+        VMSTATE_UINT8_ARRAY(data, SDState, 512),
+        VMSTATE_BUFFER_POINTER_UNSAFE(buf, SDState, 1, 512),
+        VMSTATE_BOOL(enable, SDState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/* We do not model the chip select pin, so allow the board to select
+   whether card should be in SSI or MMC/SD mode.  It is also up to the
+   board to ensure that ssi transfers only occur when the chip select
+   is asserted.  */
+SDState *sd_init(BlockDriverState *bs, bool is_spi)
+{
+    SDState *sd;
+
+    sd = (SDState *) g_malloc0(sizeof(SDState));
+    sd->buf = qemu_blockalign(bs, 512);
+    sd->spi = is_spi;
+    sd->enable = true;
+    sd_reset(sd, bs);
+    if (sd->bdrv) {
+        bdrv_attach_dev_nofail(sd->bdrv, sd);
+        bdrv_set_dev_ops(sd->bdrv, &sd_block_ops, sd);
+    }
+    vmstate_register(NULL, -1, &sd_vmstate, sd);
+    return sd;
+}
+
+void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert)
+{
+    sd->readonly_cb = readonly;
+    sd->inserted_cb = insert;
+    qemu_set_irq(readonly, sd->bdrv ? bdrv_is_read_only(sd->bdrv) : 0);
+    qemu_set_irq(insert, sd->bdrv ? bdrv_is_inserted(sd->bdrv) : 0);
+}
+
+static void sd_erase(SDState *sd)
+{
+    int i;
+    uint64_t erase_start = sd->erase_start;
+    uint64_t erase_end = sd->erase_end;
+
+    if (!sd->erase_start || !sd->erase_end) {
+        sd->card_status |= ERASE_SEQ_ERROR;
+        return;
+    }
+
+    if (extract32(sd->ocr, OCR_CCS_BITN, 1)) {
+        /* High capacity memory card: erase units are 512 byte blocks */
+        erase_start *= 512;
+        erase_end *= 512;
+    }
+
+    erase_start = sd_addr_to_wpnum(erase_start);
+    erase_end = sd_addr_to_wpnum(erase_end);
+    sd->erase_start = 0;
+    sd->erase_end = 0;
+    sd->csd[14] |= 0x40;
+
+    for (i = erase_start; i <= erase_end; i++) {
+        if (test_bit(i, sd->wp_groups)) {
+            sd->card_status |= WP_ERASE_SKIP;
+        }
+    }
+}
+
+static uint32_t sd_wpbits(SDState *sd, uint64_t addr)
+{
+    uint32_t i, wpnum;
+    uint32_t ret = 0;
+
+    wpnum = sd_addr_to_wpnum(addr);
+
+    for (i = 0; i < 32; i++, wpnum++, addr += WPGROUP_SIZE) {
+        if (addr < sd->size && test_bit(wpnum, sd->wp_groups)) {
+            ret |= (1 << i);
+        }
+    }
+
+    return ret;
+}
+
+static void sd_function_switch(SDState *sd, uint32_t arg)
+{
+    int i, mode, new_func, crc;
+    mode = !!(arg & 0x80000000);
+
+    sd->data[0] = 0x00;                /* Maximum current consumption */
+    sd->data[1] = 0x01;
+    sd->data[2] = 0x80;                /* Supported group 6 functions */
+    sd->data[3] = 0x01;
+    sd->data[4] = 0x80;                /* Supported group 5 functions */
+    sd->data[5] = 0x01;
+    sd->data[6] = 0x80;                /* Supported group 4 functions */
+    sd->data[7] = 0x01;
+    sd->data[8] = 0x80;                /* Supported group 3 functions */
+    sd->data[9] = 0x01;
+    sd->data[10] = 0x80;       /* Supported group 2 functions */
+    sd->data[11] = 0x43;
+    sd->data[12] = 0x80;       /* Supported group 1 functions */
+    sd->data[13] = 0x03;
+    for (i = 0; i < 6; i ++) {
+        new_func = (arg >> (i * 4)) & 0x0f;
+        if (mode && new_func != 0x0f)
+            sd->function_group[i] = new_func;
+        sd->data[14 + (i >> 1)] = new_func << ((i * 4) & 4);
+    }
+    memset(&sd->data[17], 0, 47);
+    crc = sd_crc16(sd->data, 64);
+    sd->data[65] = crc >> 8;
+    sd->data[66] = crc & 0xff;
+}
+
+static inline bool sd_wp_addr(SDState *sd, uint64_t addr)
+{
+    return test_bit(sd_addr_to_wpnum(addr), sd->wp_groups);
+}
+
+static void sd_lock_command(SDState *sd)
+{
+    int erase, lock, clr_pwd, set_pwd, pwd_len;
+    erase = !!(sd->data[0] & 0x08);
+    lock = sd->data[0] & 0x04;
+    clr_pwd = sd->data[0] & 0x02;
+    set_pwd = sd->data[0] & 0x01;
+
+    if (sd->blk_len > 1)
+        pwd_len = sd->data[1];
+    else
+        pwd_len = 0;
+
+    if (erase) {
+        if (!(sd->card_status & CARD_IS_LOCKED) || sd->blk_len > 1 ||
+                        set_pwd || clr_pwd || lock || sd->wp_switch ||
+                        (sd->csd[14] & 0x20)) {
+            sd->card_status |= LOCK_UNLOCK_FAILED;
+            return;
+        }
+        bitmap_zero(sd->wp_groups, sd->wpgrps_size);
+        sd->csd[14] &= ~0x10;
+        sd->card_status &= ~CARD_IS_LOCKED;
+        sd->pwd_len = 0;
+        /* Erasing the entire card here! */
+        fprintf(stderr, "SD: Card force-erased by CMD42\n");
+        return;
+    }
+
+    if (sd->blk_len < 2 + pwd_len ||
+                    pwd_len <= sd->pwd_len ||
+                    pwd_len > sd->pwd_len + 16) {
+        sd->card_status |= LOCK_UNLOCK_FAILED;
+        return;
+    }
+
+    if (sd->pwd_len && memcmp(sd->pwd, sd->data + 2, sd->pwd_len)) {
+        sd->card_status |= LOCK_UNLOCK_FAILED;
+        return;
+    }
+
+    pwd_len -= sd->pwd_len;
+    if ((pwd_len && !set_pwd) ||
+                    (clr_pwd && (set_pwd || lock)) ||
+                    (lock && !sd->pwd_len && !set_pwd) ||
+                    (!set_pwd && !clr_pwd &&
+                     (((sd->card_status & CARD_IS_LOCKED) && lock) ||
+                      (!(sd->card_status & CARD_IS_LOCKED) && !lock)))) {
+        sd->card_status |= LOCK_UNLOCK_FAILED;
+        return;
+    }
+
+    if (set_pwd) {
+        memcpy(sd->pwd, sd->data + 2 + sd->pwd_len, pwd_len);
+        sd->pwd_len = pwd_len;
+    }
+
+    if (clr_pwd) {
+        sd->pwd_len = 0;
+    }
+
+    if (lock)
+        sd->card_status |= CARD_IS_LOCKED;
+    else
+        sd->card_status &= ~CARD_IS_LOCKED;
+}
+
+static sd_rsp_type_t sd_normal_command(SDState *sd,
+                                       SDRequest req)
+{
+    uint32_t rca = 0x0000;
+    uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg;
+
+    /* Not interpreting this as an app command */
+    sd->card_status &= ~APP_CMD;
+
+    if (sd_cmd_type[req.cmd] == sd_ac || sd_cmd_type[req.cmd] == sd_adtc)
+        rca = req.arg >> 16;
+
+    DPRINTF("CMD%d 0x%08x state %d\n", req.cmd, req.arg, sd->state);
+    switch (req.cmd) {
+    /* Basic commands (Class 0 and Class 1) */
+    case 0:    /* CMD0:   GO_IDLE_STATE */
+        switch (sd->state) {
+        case sd_inactive_state:
+            return sd->spi ? sd_r1 : sd_r0;
+
+        default:
+            sd->state = sd_idle_state;
+            sd_reset(sd, sd->bdrv);
+            return sd->spi ? sd_r1 : sd_r0;
+        }
+        break;
+
+    case 1:    /* CMD1:   SEND_OP_CMD */
+        if (!sd->spi)
+            goto bad_cmd;
+
+        sd->state = sd_transfer_state;
+        return sd_r1;
+
+    case 2:    /* CMD2:   ALL_SEND_CID */
+        if (sd->spi)
+            goto bad_cmd;
+        switch (sd->state) {
+        case sd_ready_state:
+            sd->state = sd_identification_state;
+            return sd_r2_i;
+
+        default:
+            break;
+        }
+        break;
+
+    case 3:    /* CMD3:   SEND_RELATIVE_ADDR */
+        if (sd->spi)
+            goto bad_cmd;
+        switch (sd->state) {
+        case sd_identification_state:
+        case sd_standby_state:
+            sd->state = sd_standby_state;
+            sd_set_rca(sd);
+            return sd_r6;
+
+        default:
+            break;
+        }
+        break;
+
+    case 4:    /* CMD4:   SEND_DSR */
+        if (sd->spi)
+            goto bad_cmd;
+        switch (sd->state) {
+        case sd_standby_state:
+            break;
+
+        default:
+            break;
+        }
+        break;
+
+    case 5: /* CMD5: reserved for SDIO cards */
+        return sd_illegal;
+
+    case 6:    /* CMD6:   SWITCH_FUNCTION */
+        if (sd->spi)
+            goto bad_cmd;
+        switch (sd->mode) {
+        case sd_data_transfer_mode:
+            sd_function_switch(sd, req.arg);
+            sd->state = sd_sendingdata_state;
+            sd->data_start = 0;
+            sd->data_offset = 0;
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    case 7:    /* CMD7:   SELECT/DESELECT_CARD */
+        if (sd->spi)
+            goto bad_cmd;
+        switch (sd->state) {
+        case sd_standby_state:
+            if (sd->rca != rca)
+                return sd_r0;
+
+            sd->state = sd_transfer_state;
+            return sd_r1b;
+
+        case sd_transfer_state:
+        case sd_sendingdata_state:
+            if (sd->rca == rca)
+                break;
+
+            sd->state = sd_standby_state;
+            return sd_r1b;
+
+        case sd_disconnect_state:
+            if (sd->rca != rca)
+                return sd_r0;
+
+            sd->state = sd_programming_state;
+            return sd_r1b;
+
+        case sd_programming_state:
+            if (sd->rca == rca)
+                break;
+
+            sd->state = sd_disconnect_state;
+            return sd_r1b;
+
+        default:
+            break;
+        }
+        break;
+
+    case 8:    /* CMD8:   SEND_IF_COND */
+        /* Physical Layer Specification Version 2.00 command */
+        switch (sd->state) {
+        case sd_idle_state:
+            sd->vhs = 0;
+
+            /* No response if not exactly one VHS bit is set.  */
+            if (!(req.arg >> 8) || (req.arg >> ffs(req.arg & ~0xff)))
+                return sd->spi ? sd_r7 : sd_r0;
+
+            /* Accept.  */
+            sd->vhs = req.arg;
+            return sd_r7;
+
+        default:
+            break;
+        }
+        break;
+
+    case 9:    /* CMD9:   SEND_CSD */
+        switch (sd->state) {
+        case sd_standby_state:
+            if (sd->rca != rca)
+                return sd_r0;
+
+            return sd_r2_s;
+
+        case sd_transfer_state:
+            if (!sd->spi)
+                break;
+            sd->state = sd_sendingdata_state;
+            memcpy(sd->data, sd->csd, 16);
+            sd->data_start = addr;
+            sd->data_offset = 0;
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    case 10:   /* CMD10:  SEND_CID */
+        switch (sd->state) {
+        case sd_standby_state:
+            if (sd->rca != rca)
+                return sd_r0;
+
+            return sd_r2_i;
+
+        case sd_transfer_state:
+            if (!sd->spi)
+                break;
+            sd->state = sd_sendingdata_state;
+            memcpy(sd->data, sd->cid, 16);
+            sd->data_start = addr;
+            sd->data_offset = 0;
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    case 11:   /* CMD11:  READ_DAT_UNTIL_STOP */
+        if (sd->spi)
+            goto bad_cmd;
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->state = sd_sendingdata_state;
+            sd->data_start = req.arg;
+            sd->data_offset = 0;
+
+            if (sd->data_start + sd->blk_len > sd->size)
+                sd->card_status |= ADDRESS_ERROR;
+            return sd_r0;
+
+        default:
+            break;
+        }
+        break;
+
+    case 12:   /* CMD12:  STOP_TRANSMISSION */
+        switch (sd->state) {
+        case sd_sendingdata_state:
+            sd->state = sd_transfer_state;
+            return sd_r1b;
+
+        case sd_receivingdata_state:
+            sd->state = sd_programming_state;
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_transfer_state;
+            return sd_r1b;
+
+        default:
+            break;
+        }
+        break;
+
+    case 13:   /* CMD13:  SEND_STATUS */
+        switch (sd->mode) {
+        case sd_data_transfer_mode:
+            if (sd->rca != rca)
+                return sd_r0;
+
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    case 15:   /* CMD15:  GO_INACTIVE_STATE */
+        if (sd->spi)
+            goto bad_cmd;
+        switch (sd->mode) {
+        case sd_data_transfer_mode:
+            if (sd->rca != rca)
+                return sd_r0;
+
+            sd->state = sd_inactive_state;
+            return sd_r0;
+
+        default:
+            break;
+        }
+        break;
+
+    /* Block read commands (Classs 2) */
+    case 16:   /* CMD16:  SET_BLOCKLEN */
+        switch (sd->state) {
+        case sd_transfer_state:
+            if (req.arg > (1 << HWBLOCK_SHIFT))
+                sd->card_status |= BLOCK_LEN_ERROR;
+            else
+                sd->blk_len = req.arg;
+
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    case 17:   /* CMD17:  READ_SINGLE_BLOCK */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->state = sd_sendingdata_state;
+            sd->data_start = addr;
+            sd->data_offset = 0;
+
+            if (sd->data_start + sd->blk_len > sd->size)
+                sd->card_status |= ADDRESS_ERROR;
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    case 18:   /* CMD18:  READ_MULTIPLE_BLOCK */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->state = sd_sendingdata_state;
+            sd->data_start = addr;
+            sd->data_offset = 0;
+
+            if (sd->data_start + sd->blk_len > sd->size)
+                sd->card_status |= ADDRESS_ERROR;
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    /* Block write commands (Class 4) */
+    case 24:   /* CMD24:  WRITE_SINGLE_BLOCK */
+        if (sd->spi)
+            goto unimplemented_cmd;
+        switch (sd->state) {
+        case sd_transfer_state:
+            /* Writing in SPI mode not implemented.  */
+            if (sd->spi)
+                break;
+            sd->state = sd_receivingdata_state;
+            sd->data_start = addr;
+            sd->data_offset = 0;
+            sd->blk_written = 0;
+
+            if (sd->data_start + sd->blk_len > sd->size)
+                sd->card_status |= ADDRESS_ERROR;
+            if (sd_wp_addr(sd, sd->data_start))
+                sd->card_status |= WP_VIOLATION;
+            if (sd->csd[14] & 0x30)
+                sd->card_status |= WP_VIOLATION;
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    case 25:   /* CMD25:  WRITE_MULTIPLE_BLOCK */
+        if (sd->spi)
+            goto unimplemented_cmd;
+        switch (sd->state) {
+        case sd_transfer_state:
+            /* Writing in SPI mode not implemented.  */
+            if (sd->spi)
+                break;
+            sd->state = sd_receivingdata_state;
+            sd->data_start = addr;
+            sd->data_offset = 0;
+            sd->blk_written = 0;
+
+            if (sd->data_start + sd->blk_len > sd->size)
+                sd->card_status |= ADDRESS_ERROR;
+            if (sd_wp_addr(sd, sd->data_start))
+                sd->card_status |= WP_VIOLATION;
+            if (sd->csd[14] & 0x30)
+                sd->card_status |= WP_VIOLATION;
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    case 26:   /* CMD26:  PROGRAM_CID */
+        if (sd->spi)
+            goto bad_cmd;
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->state = sd_receivingdata_state;
+            sd->data_start = 0;
+            sd->data_offset = 0;
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    case 27:   /* CMD27:  PROGRAM_CSD */
+        if (sd->spi)
+            goto unimplemented_cmd;
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->state = sd_receivingdata_state;
+            sd->data_start = 0;
+            sd->data_offset = 0;
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    /* Write protection (Class 6) */
+    case 28:   /* CMD28:  SET_WRITE_PROT */
+        switch (sd->state) {
+        case sd_transfer_state:
+            if (addr >= sd->size) {
+                sd->card_status |= ADDRESS_ERROR;
+                return sd_r1b;
+            }
+
+            sd->state = sd_programming_state;
+            set_bit(sd_addr_to_wpnum(addr), sd->wp_groups);
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_transfer_state;
+            return sd_r1b;
+
+        default:
+            break;
+        }
+        break;
+
+    case 29:   /* CMD29:  CLR_WRITE_PROT */
+        switch (sd->state) {
+        case sd_transfer_state:
+            if (addr >= sd->size) {
+                sd->card_status |= ADDRESS_ERROR;
+                return sd_r1b;
+            }
+
+            sd->state = sd_programming_state;
+            clear_bit(sd_addr_to_wpnum(addr), sd->wp_groups);
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_transfer_state;
+            return sd_r1b;
+
+        default:
+            break;
+        }
+        break;
+
+    case 30:   /* CMD30:  SEND_WRITE_PROT */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->state = sd_sendingdata_state;
+            *(uint32_t *) sd->data = sd_wpbits(sd, req.arg);
+            sd->data_start = addr;
+            sd->data_offset = 0;
+            return sd_r1b;
+
+        default:
+            break;
+        }
+        break;
+
+    /* Erase commands (Class 5) */
+    case 32:   /* CMD32:  ERASE_WR_BLK_START */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->erase_start = req.arg;
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    case 33:   /* CMD33:  ERASE_WR_BLK_END */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->erase_end = req.arg;
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    case 38:   /* CMD38:  ERASE */
+        switch (sd->state) {
+        case sd_transfer_state:
+            if (sd->csd[14] & 0x30) {
+                sd->card_status |= WP_VIOLATION;
+                return sd_r1b;
+            }
+
+            sd->state = sd_programming_state;
+            sd_erase(sd);
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_transfer_state;
+            return sd_r1b;
+
+        default:
+            break;
+        }
+        break;
+
+    /* Lock card commands (Class 7) */
+    case 42:   /* CMD42:  LOCK_UNLOCK */
+        if (sd->spi)
+            goto unimplemented_cmd;
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->state = sd_receivingdata_state;
+            sd->data_start = 0;
+            sd->data_offset = 0;
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    case 52:
+    case 53:
+        /* CMD52, CMD53: reserved for SDIO cards
+         * (see the SDIO Simplified Specification V2.0)
+         * Handle as illegal command but do not complain
+         * on stderr, as some OSes may use these in their
+         * probing for presence of an SDIO card.
+         */
+        return sd_illegal;
+
+    /* Application specific commands (Class 8) */
+    case 55:   /* CMD55:  APP_CMD */
+        if (sd->rca != rca)
+            return sd_r0;
+
+        sd->expecting_acmd = true;
+        sd->card_status |= APP_CMD;
+        return sd_r1;
+
+    case 56:   /* CMD56:  GEN_CMD */
+        fprintf(stderr, "SD: GEN_CMD 0x%08x\n", req.arg);
+
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->data_offset = 0;
+            if (req.arg & 1)
+                sd->state = sd_sendingdata_state;
+            else
+                sd->state = sd_receivingdata_state;
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    default:
+    bad_cmd:
+        fprintf(stderr, "SD: Unknown CMD%i\n", req.cmd);
+        return sd_illegal;
+
+    unimplemented_cmd:
+        /* Commands that are recognised but not yet implemented in SPI mode.  */
+        fprintf(stderr, "SD: CMD%i not implemented in SPI mode\n", req.cmd);
+        return sd_illegal;
+    }
+
+    fprintf(stderr, "SD: CMD%i in a wrong state\n", req.cmd);
+    return sd_illegal;
+}
+
+static sd_rsp_type_t sd_app_command(SDState *sd,
+                                    SDRequest req)
+{
+    DPRINTF("ACMD%d 0x%08x\n", req.cmd, req.arg);
+    sd->card_status |= APP_CMD;
+    switch (req.cmd) {
+    case 6:    /* ACMD6:  SET_BUS_WIDTH */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->sd_status[0] &= 0x3f;
+            sd->sd_status[0] |= (req.arg & 0x03) << 6;
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    case 13:   /* ACMD13: SD_STATUS */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->state = sd_sendingdata_state;
+            sd->data_start = 0;
+            sd->data_offset = 0;
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    case 22:   /* ACMD22: SEND_NUM_WR_BLOCKS */
+        switch (sd->state) {
+        case sd_transfer_state:
+            *(uint32_t *) sd->data = sd->blk_written;
+
+            sd->state = sd_sendingdata_state;
+            sd->data_start = 0;
+            sd->data_offset = 0;
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    case 23:   /* ACMD23: SET_WR_BLK_ERASE_COUNT */
+        switch (sd->state) {
+        case sd_transfer_state:
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    case 41:   /* ACMD41: SD_APP_OP_COND */
+        if (sd->spi) {
+            /* SEND_OP_CMD */
+            sd->state = sd_transfer_state;
+            return sd_r1;
+        }
+        switch (sd->state) {
+        case sd_idle_state:
+            /* We accept any voltage.  10000 V is nothing.  */
+            if (req.arg)
+                sd->state = sd_ready_state;
+
+            return sd_r3;
+
+        default:
+            break;
+        }
+        break;
+
+    case 42:   /* ACMD42: SET_CLR_CARD_DETECT */
+        switch (sd->state) {
+        case sd_transfer_state:
+            /* Bringing in the 50KOhm pull-up resistor... Done.  */
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    case 51:   /* ACMD51: SEND_SCR */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->state = sd_sendingdata_state;
+            sd->data_start = 0;
+            sd->data_offset = 0;
+            return sd_r1;
+
+        default:
+            break;
+        }
+        break;
+
+    default:
+        /* Fall back to standard commands.  */
+        return sd_normal_command(sd, req);
+    }
+
+    fprintf(stderr, "SD: ACMD%i in a wrong state\n", req.cmd);
+    return sd_illegal;
+}
+
+static int cmd_valid_while_locked(SDState *sd, SDRequest *req)
+{
+    /* Valid commands in locked state:
+     * basic class (0)
+     * lock card class (7)
+     * CMD16
+     * implicitly, the ACMD prefix CMD55
+     * ACMD41 and ACMD42
+     * Anything else provokes an "illegal command" response.
+     */
+    if (sd->expecting_acmd) {
+        return req->cmd == 41 || req->cmd == 42;
+    }
+    if (req->cmd == 16 || req->cmd == 55) {
+        return 1;
+    }
+    return sd_cmd_class[req->cmd] == 0 || sd_cmd_class[req->cmd] == 7;
+}
+
+int sd_do_command(SDState *sd, SDRequest *req,
+                  uint8_t *response) {
+    int last_state;
+    sd_rsp_type_t rtype;
+    int rsplen;
+
+    if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv) || !sd->enable) {
+        return 0;
+    }
+
+    if (sd_req_crc_validate(req)) {
+        sd->card_status |= COM_CRC_ERROR;
+        rtype = sd_illegal;
+        goto send_response;
+    }
+
+    if (sd->card_status & CARD_IS_LOCKED) {
+        if (!cmd_valid_while_locked(sd, req)) {
+            sd->card_status |= ILLEGAL_COMMAND;
+            sd->expecting_acmd = false;
+            fprintf(stderr, "SD: Card is locked\n");
+            rtype = sd_illegal;
+            goto send_response;
+        }
+    }
+
+    last_state = sd->state;
+    sd_set_mode(sd);
+
+    if (sd->expecting_acmd) {
+        sd->expecting_acmd = false;
+        rtype = sd_app_command(sd, *req);
+    } else {
+        rtype = sd_normal_command(sd, *req);
+    }
+
+    if (rtype == sd_illegal) {
+        sd->card_status |= ILLEGAL_COMMAND;
+    } else {
+        /* Valid command, we can update the 'state before command' bits.
+         * (Do this now so they appear in r1 responses.)
+         */
+        sd->current_cmd = req->cmd;
+        sd->card_status &= ~CURRENT_STATE;
+        sd->card_status |= (last_state << 9);
+    }
+
+send_response:
+    switch (rtype) {
+    case sd_r1:
+    case sd_r1b:
+        sd_response_r1_make(sd, response);
+        rsplen = 4;
+        break;
+
+    case sd_r2_i:
+        memcpy(response, sd->cid, sizeof(sd->cid));
+        rsplen = 16;
+        break;
+
+    case sd_r2_s:
+        memcpy(response, sd->csd, sizeof(sd->csd));
+        rsplen = 16;
+        break;
+
+    case sd_r3:
+        sd_response_r3_make(sd, response);
+        rsplen = 4;
+        break;
+
+    case sd_r6:
+        sd_response_r6_make(sd, response);
+        rsplen = 4;
+        break;
+
+    case sd_r7:
+        sd_response_r7_make(sd, response);
+        rsplen = 4;
+        break;
+
+    case sd_r0:
+    case sd_illegal:
+    default:
+        rsplen = 0;
+        break;
+    }
+
+    if (rtype != sd_illegal) {
+        /* Clear the "clear on valid command" status bits now we've
+         * sent any response
+         */
+        sd->card_status &= ~CARD_STATUS_B;
+    }
+
+#ifdef DEBUG_SD
+    if (rsplen) {
+        int i;
+        DPRINTF("Response:");
+        for (i = 0; i < rsplen; i++)
+            fprintf(stderr, " %02x", response[i]);
+        fprintf(stderr, " state %d\n", sd->state);
+    } else {
+        DPRINTF("No response %d\n", sd->state);
+    }
+#endif
+
+    return rsplen;
+}
+
+static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len)
+{
+    uint64_t end = addr + len;
+
+    DPRINTF("sd_blk_read: addr = 0x%08llx, len = %d\n",
+            (unsigned long long) addr, len);
+    if (!sd->bdrv || bdrv_read(sd->bdrv, addr >> 9, sd->buf, 1) < 0) {
+        fprintf(stderr, "sd_blk_read: read error on host side\n");
+        return;
+    }
+
+    if (end > (addr & ~511) + 512) {
+        memcpy(sd->data, sd->buf + (addr & 511), 512 - (addr & 511));
+
+        if (bdrv_read(sd->bdrv, end >> 9, sd->buf, 1) < 0) {
+            fprintf(stderr, "sd_blk_read: read error on host side\n");
+            return;
+        }
+        memcpy(sd->data + 512 - (addr & 511), sd->buf, end & 511);
+    } else
+        memcpy(sd->data, sd->buf + (addr & 511), len);
+}
+
+static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len)
+{
+    uint64_t end = addr + len;
+
+    if ((addr & 511) || len < 512)
+        if (!sd->bdrv || bdrv_read(sd->bdrv, addr >> 9, sd->buf, 1) < 0) {
+            fprintf(stderr, "sd_blk_write: read error on host side\n");
+            return;
+        }
+
+    if (end > (addr & ~511) + 512) {
+        memcpy(sd->buf + (addr & 511), sd->data, 512 - (addr & 511));
+        if (bdrv_write(sd->bdrv, addr >> 9, sd->buf, 1) < 0) {
+            fprintf(stderr, "sd_blk_write: write error on host side\n");
+            return;
+        }
+
+        if (bdrv_read(sd->bdrv, end >> 9, sd->buf, 1) < 0) {
+            fprintf(stderr, "sd_blk_write: read error on host side\n");
+            return;
+        }
+        memcpy(sd->buf, sd->data + 512 - (addr & 511), end & 511);
+        if (bdrv_write(sd->bdrv, end >> 9, sd->buf, 1) < 0) {
+            fprintf(stderr, "sd_blk_write: write error on host side\n");
+        }
+    } else {
+        memcpy(sd->buf + (addr & 511), sd->data, len);
+        if (!sd->bdrv || bdrv_write(sd->bdrv, addr >> 9, sd->buf, 1) < 0) {
+            fprintf(stderr, "sd_blk_write: write error on host side\n");
+        }
+    }
+}
+
+#define BLK_READ_BLOCK(a, len) sd_blk_read(sd, a, len)
+#define BLK_WRITE_BLOCK(a, len)        sd_blk_write(sd, a, len)
+#define APP_READ_BLOCK(a, len) memset(sd->data, 0xec, len)
+#define APP_WRITE_BLOCK(a, len)
+
+void sd_write_data(SDState *sd, uint8_t value)
+{
+    int i;
+
+    if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv) || !sd->enable)
+        return;
+
+    if (sd->state != sd_receivingdata_state) {
+        fprintf(stderr, "sd_write_data: not in Receiving-Data state\n");
+        return;
+    }
+
+    if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
+        return;
+
+    switch (sd->current_cmd) {
+    case 24:   /* CMD24:  WRITE_SINGLE_BLOCK */
+        sd->data[sd->data_offset ++] = value;
+        if (sd->data_offset >= sd->blk_len) {
+            /* TODO: Check CRC before committing */
+            sd->state = sd_programming_state;
+            BLK_WRITE_BLOCK(sd->data_start, sd->data_offset);
+            sd->blk_written ++;
+            sd->csd[14] |= 0x40;
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_transfer_state;
+        }
+        break;
+
+    case 25:   /* CMD25:  WRITE_MULTIPLE_BLOCK */
+        if (sd->data_offset == 0) {
+            /* Start of the block - lets check the address is valid */
+            if (sd->data_start + sd->blk_len > sd->size) {
+                sd->card_status |= ADDRESS_ERROR;
+                break;
+            }
+            if (sd_wp_addr(sd, sd->data_start)) {
+                sd->card_status |= WP_VIOLATION;
+                break;
+            }
+        }
+        sd->data[sd->data_offset++] = value;
+        if (sd->data_offset >= sd->blk_len) {
+            /* TODO: Check CRC before committing */
+            sd->state = sd_programming_state;
+            BLK_WRITE_BLOCK(sd->data_start, sd->data_offset);
+            sd->blk_written++;
+            sd->data_start += sd->blk_len;
+            sd->data_offset = 0;
+            sd->csd[14] |= 0x40;
+
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_receivingdata_state;
+        }
+        break;
+
+    case 26:   /* CMD26:  PROGRAM_CID */
+        sd->data[sd->data_offset ++] = value;
+        if (sd->data_offset >= sizeof(sd->cid)) {
+            /* TODO: Check CRC before committing */
+            sd->state = sd_programming_state;
+            for (i = 0; i < sizeof(sd->cid); i ++)
+                if ((sd->cid[i] | 0x00) != sd->data[i])
+                    sd->card_status |= CID_CSD_OVERWRITE;
+
+            if (!(sd->card_status & CID_CSD_OVERWRITE))
+                for (i = 0; i < sizeof(sd->cid); i ++) {
+                    sd->cid[i] |= 0x00;
+                    sd->cid[i] &= sd->data[i];
+                }
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_transfer_state;
+        }
+        break;
+
+    case 27:   /* CMD27:  PROGRAM_CSD */
+        sd->data[sd->data_offset ++] = value;
+        if (sd->data_offset >= sizeof(sd->csd)) {
+            /* TODO: Check CRC before committing */
+            sd->state = sd_programming_state;
+            for (i = 0; i < sizeof(sd->csd); i ++)
+                if ((sd->csd[i] | sd_csd_rw_mask[i]) !=
+                    (sd->data[i] | sd_csd_rw_mask[i]))
+                    sd->card_status |= CID_CSD_OVERWRITE;
+
+            /* Copy flag (OTP) & Permanent write protect */
+            if (sd->csd[14] & ~sd->data[14] & 0x60)
+                sd->card_status |= CID_CSD_OVERWRITE;
+
+            if (!(sd->card_status & CID_CSD_OVERWRITE))
+                for (i = 0; i < sizeof(sd->csd); i ++) {
+                    sd->csd[i] |= sd_csd_rw_mask[i];
+                    sd->csd[i] &= sd->data[i];
+                }
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_transfer_state;
+        }
+        break;
+
+    case 42:   /* CMD42:  LOCK_UNLOCK */
+        sd->data[sd->data_offset ++] = value;
+        if (sd->data_offset >= sd->blk_len) {
+            /* TODO: Check CRC before committing */
+            sd->state = sd_programming_state;
+            sd_lock_command(sd);
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_transfer_state;
+        }
+        break;
+
+    case 56:   /* CMD56:  GEN_CMD */
+        sd->data[sd->data_offset ++] = value;
+        if (sd->data_offset >= sd->blk_len) {
+            APP_WRITE_BLOCK(sd->data_start, sd->data_offset);
+            sd->state = sd_transfer_state;
+        }
+        break;
+
+    default:
+        fprintf(stderr, "sd_write_data: unknown command\n");
+        break;
+    }
+}
+
+uint8_t sd_read_data(SDState *sd)
+{
+    /* TODO: Append CRCs */
+    uint8_t ret;
+    int io_len;
+
+    if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv) || !sd->enable)
+        return 0x00;
+
+    if (sd->state != sd_sendingdata_state) {
+        fprintf(stderr, "sd_read_data: not in Sending-Data state\n");
+        return 0x00;
+    }
+
+    if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
+        return 0x00;
+
+    io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len;
+
+    switch (sd->current_cmd) {
+    case 6:    /* CMD6:   SWITCH_FUNCTION */
+        ret = sd->data[sd->data_offset ++];
+
+        if (sd->data_offset >= 64)
+            sd->state = sd_transfer_state;
+        break;
+
+    case 9:    /* CMD9:   SEND_CSD */
+    case 10:   /* CMD10:  SEND_CID */
+        ret = sd->data[sd->data_offset ++];
+
+        if (sd->data_offset >= 16)
+            sd->state = sd_transfer_state;
+        break;
+
+    case 11:   /* CMD11:  READ_DAT_UNTIL_STOP */
+        if (sd->data_offset == 0)
+            BLK_READ_BLOCK(sd->data_start, io_len);
+        ret = sd->data[sd->data_offset ++];
+
+        if (sd->data_offset >= io_len) {
+            sd->data_start += io_len;
+            sd->data_offset = 0;
+            if (sd->data_start + io_len > sd->size) {
+                sd->card_status |= ADDRESS_ERROR;
+                break;
+            }
+        }
+        break;
+
+    case 13:   /* ACMD13: SD_STATUS */
+        ret = sd->sd_status[sd->data_offset ++];
+
+        if (sd->data_offset >= sizeof(sd->sd_status))
+            sd->state = sd_transfer_state;
+        break;
+
+    case 17:   /* CMD17:  READ_SINGLE_BLOCK */
+        if (sd->data_offset == 0)
+            BLK_READ_BLOCK(sd->data_start, io_len);
+        ret = sd->data[sd->data_offset ++];
+
+        if (sd->data_offset >= io_len)
+            sd->state = sd_transfer_state;
+        break;
+
+    case 18:   /* CMD18:  READ_MULTIPLE_BLOCK */
+        if (sd->data_offset == 0)
+            BLK_READ_BLOCK(sd->data_start, io_len);
+        ret = sd->data[sd->data_offset ++];
+
+        if (sd->data_offset >= io_len) {
+            sd->data_start += io_len;
+            sd->data_offset = 0;
+            if (sd->data_start + io_len > sd->size) {
+                sd->card_status |= ADDRESS_ERROR;
+                break;
+            }
+        }
+        break;
+
+    case 22:   /* ACMD22: SEND_NUM_WR_BLOCKS */
+        ret = sd->data[sd->data_offset ++];
+
+        if (sd->data_offset >= 4)
+            sd->state = sd_transfer_state;
+        break;
+
+    case 30:   /* CMD30:  SEND_WRITE_PROT */
+        ret = sd->data[sd->data_offset ++];
+
+        if (sd->data_offset >= 4)
+            sd->state = sd_transfer_state;
+        break;
+
+    case 51:   /* ACMD51: SEND_SCR */
+        ret = sd->scr[sd->data_offset ++];
+
+        if (sd->data_offset >= sizeof(sd->scr))
+            sd->state = sd_transfer_state;
+        break;
+
+    case 56:   /* CMD56:  GEN_CMD */
+        if (sd->data_offset == 0)
+            APP_READ_BLOCK(sd->data_start, sd->blk_len);
+        ret = sd->data[sd->data_offset ++];
+
+        if (sd->data_offset >= sd->blk_len)
+            sd->state = sd_transfer_state;
+        break;
+
+    default:
+        fprintf(stderr, "sd_read_data: unknown command\n");
+        return 0x00;
+    }
+
+    return ret;
+}
+
+bool sd_data_ready(SDState *sd)
+{
+    return sd->state == sd_sendingdata_state;
+}
+
+void sd_enable(SDState *sd, bool enable)
+{
+    sd->enable = enable;
+}
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
new file mode 100644 (file)
index 0000000..4a29e6c
--- /dev/null
@@ -0,0 +1,1300 @@
+/*
+ * SD Association Host Standard Specification v2.0 controller emulation
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Mitsyanko Igor <i.mitsyanko@samsung.com>
+ * Peter A.G. Crosthwaite <peter.crosthwaite@petalogix.com>
+ *
+ * Based on MMC controller for Samsung S5PC1xx-based board emulation
+ * by Alexey Merkulov and Vladimir Monakhov.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "sysemu/blockdev.h"
+#include "sysemu/dma.h"
+#include "qemu/timer.h"
+#include "block/block_int.h"
+#include "qemu/bitops.h"
+
+#include "hw/sdhci.h"
+
+/* host controller debug messages */
+#ifndef SDHC_DEBUG
+#define SDHC_DEBUG                        0
+#endif
+
+#if SDHC_DEBUG == 0
+    #define DPRINT_L1(fmt, args...)       do { } while (0)
+    #define DPRINT_L2(fmt, args...)       do { } while (0)
+    #define ERRPRINT(fmt, args...)        do { } while (0)
+#elif SDHC_DEBUG == 1
+    #define DPRINT_L1(fmt, args...)       \
+        do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0)
+    #define DPRINT_L2(fmt, args...)       do { } while (0)
+    #define ERRPRINT(fmt, args...)        \
+        do {fprintf(stderr, "QEMU SDHC ERROR: "fmt, ## args); } while (0)
+#else
+    #define DPRINT_L1(fmt, args...)       \
+        do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0)
+    #define DPRINT_L2(fmt, args...)       \
+        do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0)
+    #define ERRPRINT(fmt, args...)        \
+        do {fprintf(stderr, "QEMU SDHC ERROR: "fmt, ## args); } while (0)
+#endif
+
+/* Default SD/MMC host controller features information, which will be
+ * presented in CAPABILITIES register of generic SD host controller at reset.
+ * If not stated otherwise:
+ * 0 - not supported, 1 - supported, other - prohibited.
+ */
+#define SDHC_CAPAB_64BITBUS       0ul        /* 64-bit System Bus Support */
+#define SDHC_CAPAB_18V            1ul        /* Voltage support 1.8v */
+#define SDHC_CAPAB_30V            0ul        /* Voltage support 3.0v */
+#define SDHC_CAPAB_33V            1ul        /* Voltage support 3.3v */
+#define SDHC_CAPAB_SUSPRESUME     0ul        /* Suspend/resume support */
+#define SDHC_CAPAB_SDMA           1ul        /* SDMA support */
+#define SDHC_CAPAB_HIGHSPEED      1ul        /* High speed support */
+#define SDHC_CAPAB_ADMA1          1ul        /* ADMA1 support */
+#define SDHC_CAPAB_ADMA2          1ul        /* ADMA2 support */
+/* Maximum host controller R/W buffers size
+ * Possible values: 512, 1024, 2048 bytes */
+#define SDHC_CAPAB_MAXBLOCKLENGTH 512ul
+/* Maximum clock frequency for SDclock in MHz
+ * value in range 10-63 MHz, 0 - not defined */
+#define SDHC_CAPAB_BASECLKFREQ    0ul
+#define SDHC_CAPAB_TOUNIT         1ul  /* Timeout clock unit 0 - kHz, 1 - MHz */
+/* Timeout clock frequency 1-63, 0 - not defined */
+#define SDHC_CAPAB_TOCLKFREQ      0ul
+
+/* Now check all parameters and calculate CAPABILITIES REGISTER value */
+#if SDHC_CAPAB_64BITBUS > 1 || SDHC_CAPAB_18V > 1 || SDHC_CAPAB_30V > 1 ||     \
+    SDHC_CAPAB_33V > 1 || SDHC_CAPAB_SUSPRESUME > 1 || SDHC_CAPAB_SDMA > 1 ||  \
+    SDHC_CAPAB_HIGHSPEED > 1 || SDHC_CAPAB_ADMA2 > 1 || SDHC_CAPAB_ADMA1 > 1 ||\
+    SDHC_CAPAB_TOUNIT > 1
+#error Capabilities features can have value 0 or 1 only!
+#endif
+
+#if SDHC_CAPAB_MAXBLOCKLENGTH == 512
+#define MAX_BLOCK_LENGTH 0ul
+#elif SDHC_CAPAB_MAXBLOCKLENGTH == 1024
+#define MAX_BLOCK_LENGTH 1ul
+#elif SDHC_CAPAB_MAXBLOCKLENGTH == 2048
+#define MAX_BLOCK_LENGTH 2ul
+#else
+#error Max host controller block size can have value 512, 1024 or 2048 only!
+#endif
+
+#if (SDHC_CAPAB_BASECLKFREQ > 0 && SDHC_CAPAB_BASECLKFREQ < 10) || \
+    SDHC_CAPAB_BASECLKFREQ > 63
+#error SDclock frequency can have value in range 0, 10-63 only!
+#endif
+
+#if SDHC_CAPAB_TOCLKFREQ > 63
+#error Timeout clock frequency can have value in range 0-63 only!
+#endif
+
+#define SDHC_CAPAB_REG_DEFAULT                                 \
+   ((SDHC_CAPAB_64BITBUS << 28) | (SDHC_CAPAB_18V << 26) |     \
+    (SDHC_CAPAB_30V << 25) | (SDHC_CAPAB_33V << 24) |          \
+    (SDHC_CAPAB_SUSPRESUME << 23) | (SDHC_CAPAB_SDMA << 22) |  \
+    (SDHC_CAPAB_HIGHSPEED << 21) | (SDHC_CAPAB_ADMA1 << 20) |  \
+    (SDHC_CAPAB_ADMA2 << 19) | (MAX_BLOCK_LENGTH << 16) |      \
+    (SDHC_CAPAB_BASECLKFREQ << 8) | (SDHC_CAPAB_TOUNIT << 7) | \
+    (SDHC_CAPAB_TOCLKFREQ))
+
+#define MASKED_WRITE(reg, mask, val)  (reg = (reg & (mask)) | (val))
+
+static uint8_t sdhci_slotint(SDHCIState *s)
+{
+    return (s->norintsts & s->norintsigen) || (s->errintsts & s->errintsigen) ||
+         ((s->norintsts & SDHC_NIS_INSERT) && (s->wakcon & SDHC_WKUP_ON_INS)) ||
+         ((s->norintsts & SDHC_NIS_REMOVE) && (s->wakcon & SDHC_WKUP_ON_RMV));
+}
+
+static inline void sdhci_update_irq(SDHCIState *s)
+{
+    qemu_set_irq(s->irq, sdhci_slotint(s));
+}
+
+static void sdhci_raise_insertion_irq(void *opaque)
+{
+    SDHCIState *s = (SDHCIState *)opaque;
+
+    if (s->norintsts & SDHC_NIS_REMOVE) {
+        qemu_mod_timer(s->insert_timer,
+                       qemu_get_clock_ns(vm_clock) + SDHC_INSERTION_DELAY);
+    } else {
+        s->prnsts = 0x1ff0000;
+        if (s->norintstsen & SDHC_NISEN_INSERT) {
+            s->norintsts |= SDHC_NIS_INSERT;
+        }
+        sdhci_update_irq(s);
+    }
+}
+
+static void sdhci_insert_eject_cb(void *opaque, int irq, int level)
+{
+    SDHCIState *s = (SDHCIState *)opaque;
+    DPRINT_L1("Card state changed: %s!\n", level ? "insert" : "eject");
+
+    if ((s->norintsts & SDHC_NIS_REMOVE) && level) {
+        /* Give target some time to notice card ejection */
+        qemu_mod_timer(s->insert_timer,
+                       qemu_get_clock_ns(vm_clock) + SDHC_INSERTION_DELAY);
+    } else {
+        if (level) {
+            s->prnsts = 0x1ff0000;
+            if (s->norintstsen & SDHC_NISEN_INSERT) {
+                s->norintsts |= SDHC_NIS_INSERT;
+            }
+        } else {
+            s->prnsts = 0x1fa0000;
+            s->pwrcon &= ~SDHC_POWER_ON;
+            s->clkcon &= ~SDHC_CLOCK_SDCLK_EN;
+            if (s->norintstsen & SDHC_NISEN_REMOVE) {
+                s->norintsts |= SDHC_NIS_REMOVE;
+            }
+        }
+        sdhci_update_irq(s);
+    }
+}
+
+static void sdhci_card_readonly_cb(void *opaque, int irq, int level)
+{
+    SDHCIState *s = (SDHCIState *)opaque;
+
+    if (level) {
+        s->prnsts &= ~SDHC_WRITE_PROTECT;
+    } else {
+        /* Write enabled */
+        s->prnsts |= SDHC_WRITE_PROTECT;
+    }
+}
+
+static void sdhci_reset(SDHCIState *s)
+{
+    qemu_del_timer(s->insert_timer);
+    qemu_del_timer(s->transfer_timer);
+    /* Set all registers to 0. Capabilities registers are not cleared
+     * and assumed to always preserve their value, given to them during
+     * initialization */
+    memset(&s->sdmasysad, 0, (uintptr_t)&s->capareg - (uintptr_t)&s->sdmasysad);
+
+    sd_set_cb(s->card, s->ro_cb, s->eject_cb);
+    s->data_count = 0;
+    s->stopped_state = sdhc_not_stopped;
+}
+
+static void sdhci_do_data_transfer(void *opaque)
+{
+    SDHCIState *s = (SDHCIState *)opaque;
+
+    SDHCI_GET_CLASS(s)->data_transfer(s);
+}
+
+static void sdhci_send_command(SDHCIState *s)
+{
+    SDRequest request;
+    uint8_t response[16];
+    int rlen;
+
+    s->errintsts = 0;
+    s->acmd12errsts = 0;
+    request.cmd = s->cmdreg >> 8;
+    request.arg = s->argument;
+    DPRINT_L1("sending CMD%u ARG[0x%08x]\n", request.cmd, request.arg);
+    rlen = sd_do_command(s->card, &request, response);
+
+    if (s->cmdreg & SDHC_CMD_RESPONSE) {
+        if (rlen == 4) {
+            s->rspreg[0] = (response[0] << 24) | (response[1] << 16) |
+                           (response[2] << 8)  |  response[3];
+            s->rspreg[1] = s->rspreg[2] = s->rspreg[3] = 0;
+            DPRINT_L1("Response: RSPREG[31..0]=0x%08x\n", s->rspreg[0]);
+        } else if (rlen == 16) {
+            s->rspreg[0] = (response[11] << 24) | (response[12] << 16) |
+                           (response[13] << 8) |  response[14];
+            s->rspreg[1] = (response[7] << 24) | (response[8] << 16) |
+                           (response[9] << 8)  |  response[10];
+            s->rspreg[2] = (response[3] << 24) | (response[4] << 16) |
+                           (response[5] << 8)  |  response[6];
+            s->rspreg[3] = (response[0] << 16) | (response[1] << 8) |
+                            response[2];
+            DPRINT_L1("Response received:\n RSPREG[127..96]=0x%08x, RSPREG[95.."
+                  "64]=0x%08x,\n RSPREG[63..32]=0x%08x, RSPREG[31..0]=0x%08x\n",
+                  s->rspreg[3], s->rspreg[2], s->rspreg[1], s->rspreg[0]);
+        } else {
+            ERRPRINT("Timeout waiting for command response\n");
+            if (s->errintstsen & SDHC_EISEN_CMDTIMEOUT) {
+                s->errintsts |= SDHC_EIS_CMDTIMEOUT;
+                s->norintsts |= SDHC_NIS_ERR;
+            }
+        }
+
+        if ((s->norintstsen & SDHC_NISEN_TRSCMP) &&
+            (s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY) {
+            s->norintsts |= SDHC_NIS_TRSCMP;
+        }
+    } else if (rlen != 0 && (s->errintstsen & SDHC_EISEN_CMDIDX)) {
+        s->errintsts |= SDHC_EIS_CMDIDX;
+        s->norintsts |= SDHC_NIS_ERR;
+    }
+
+    if (s->norintstsen & SDHC_NISEN_CMDCMP) {
+        s->norintsts |= SDHC_NIS_CMDCMP;
+    }
+
+    sdhci_update_irq(s);
+
+    if (s->blksize && (s->cmdreg & SDHC_CMD_DATA_PRESENT)) {
+        sdhci_do_data_transfer(s);
+    }
+}
+
+static void sdhci_end_transfer(SDHCIState *s)
+{
+    /* Automatically send CMD12 to stop transfer if AutoCMD12 enabled */
+    if ((s->trnmod & SDHC_TRNS_ACMD12) != 0) {
+        SDRequest request;
+        uint8_t response[16];
+
+        request.cmd = 0x0C;
+        request.arg = 0;
+        DPRINT_L1("Automatically issue CMD%d %08x\n", request.cmd, request.arg);
+        sd_do_command(s->card, &request, response);
+        /* Auto CMD12 response goes to the upper Response register */
+        s->rspreg[3] = (response[0] << 24) | (response[1] << 16) |
+                (response[2] << 8) | response[3];
+    }
+
+    s->prnsts &= ~(SDHC_DOING_READ | SDHC_DOING_WRITE |
+            SDHC_DAT_LINE_ACTIVE | SDHC_DATA_INHIBIT |
+            SDHC_SPACE_AVAILABLE | SDHC_DATA_AVAILABLE);
+
+    if (s->norintstsen & SDHC_NISEN_TRSCMP) {
+        s->norintsts |= SDHC_NIS_TRSCMP;
+    }
+
+    sdhci_update_irq(s);
+}
+
+/*
+ * Programmed i/o data transfer
+ */
+
+/* Fill host controller's read buffer with BLKSIZE bytes of data from card */
+static void sdhci_read_block_from_card(SDHCIState *s)
+{
+    int index = 0;
+
+    if ((s->trnmod & SDHC_TRNS_MULTI) &&
+            (s->trnmod & SDHC_TRNS_BLK_CNT_EN) && (s->blkcnt == 0)) {
+        return;
+    }
+
+    for (index = 0; index < (s->blksize & 0x0fff); index++) {
+        s->fifo_buffer[index] = sd_read_data(s->card);
+    }
+
+    /* New data now available for READ through Buffer Port Register */
+    s->prnsts |= SDHC_DATA_AVAILABLE;
+    if (s->norintstsen & SDHC_NISEN_RBUFRDY) {
+        s->norintsts |= SDHC_NIS_RBUFRDY;
+    }
+
+    /* Clear DAT line active status if that was the last block */
+    if ((s->trnmod & SDHC_TRNS_MULTI) == 0 ||
+            ((s->trnmod & SDHC_TRNS_MULTI) && s->blkcnt == 1)) {
+        s->prnsts &= ~SDHC_DAT_LINE_ACTIVE;
+    }
+
+    /* If stop at block gap request was set and it's not the last block of
+     * data - generate Block Event interrupt */
+    if (s->stopped_state == sdhc_gap_read && (s->trnmod & SDHC_TRNS_MULTI) &&
+            s->blkcnt != 1)    {
+        s->prnsts &= ~SDHC_DAT_LINE_ACTIVE;
+        if (s->norintstsen & SDHC_EISEN_BLKGAP) {
+            s->norintsts |= SDHC_EIS_BLKGAP;
+        }
+    }
+
+    sdhci_update_irq(s);
+}
+
+/* Read @size byte of data from host controller @s BUFFER DATA PORT register */
+static uint32_t sdhci_read_dataport(SDHCIState *s, unsigned size)
+{
+    uint32_t value = 0;
+    int i;
+
+    /* first check that a valid data exists in host controller input buffer */
+    if ((s->prnsts & SDHC_DATA_AVAILABLE) == 0) {
+        ERRPRINT("Trying to read from empty buffer\n");
+        return 0;
+    }
+
+    for (i = 0; i < size; i++) {
+        value |= s->fifo_buffer[s->data_count] << i * 8;
+        s->data_count++;
+        /* check if we've read all valid data (blksize bytes) from buffer */
+        if ((s->data_count) >= (s->blksize & 0x0fff)) {
+            DPRINT_L2("All %u bytes of data have been read from input buffer\n",
+                    s->data_count);
+            s->prnsts &= ~SDHC_DATA_AVAILABLE; /* no more data in a buffer */
+            s->data_count = 0;  /* next buff read must start at position [0] */
+
+            if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
+                s->blkcnt--;
+            }
+
+            /* if that was the last block of data */
+            if ((s->trnmod & SDHC_TRNS_MULTI) == 0 ||
+                ((s->trnmod & SDHC_TRNS_BLK_CNT_EN) && (s->blkcnt == 0)) ||
+                 /* stop at gap request */
+                (s->stopped_state == sdhc_gap_read &&
+                 !(s->prnsts & SDHC_DAT_LINE_ACTIVE))) {
+                SDHCI_GET_CLASS(s)->end_data_transfer(s);
+            } else { /* if there are more data, read next block from card */
+                SDHCI_GET_CLASS(s)->read_block_from_card(s);
+            }
+            break;
+        }
+    }
+
+    return value;
+}
+
+/* Write data from host controller FIFO to card */
+static void sdhci_write_block_to_card(SDHCIState *s)
+{
+    int index = 0;
+
+    if (s->prnsts & SDHC_SPACE_AVAILABLE) {
+        if (s->norintstsen & SDHC_NISEN_WBUFRDY) {
+            s->norintsts |= SDHC_NIS_WBUFRDY;
+        }
+        sdhci_update_irq(s);
+        return;
+    }
+
+    if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
+        if (s->blkcnt == 0) {
+            return;
+        } else {
+            s->blkcnt--;
+        }
+    }
+
+    for (index = 0; index < (s->blksize & 0x0fff); index++) {
+        sd_write_data(s->card, s->fifo_buffer[index]);
+    }
+
+    /* Next data can be written through BUFFER DATORT register */
+    s->prnsts |= SDHC_SPACE_AVAILABLE;
+    if (s->norintstsen & SDHC_NISEN_WBUFRDY) {
+        s->norintsts |= SDHC_NIS_WBUFRDY;
+    }
+
+    /* Finish transfer if that was the last block of data */
+    if ((s->trnmod & SDHC_TRNS_MULTI) == 0 ||
+            ((s->trnmod & SDHC_TRNS_MULTI) &&
+            (s->trnmod & SDHC_TRNS_BLK_CNT_EN) && (s->blkcnt == 0))) {
+        SDHCI_GET_CLASS(s)->end_data_transfer(s);
+    }
+
+    /* Generate Block Gap Event if requested and if not the last block */
+    if (s->stopped_state == sdhc_gap_write && (s->trnmod & SDHC_TRNS_MULTI) &&
+            s->blkcnt > 0) {
+        s->prnsts &= ~SDHC_DOING_WRITE;
+        if (s->norintstsen & SDHC_EISEN_BLKGAP) {
+            s->norintsts |= SDHC_EIS_BLKGAP;
+        }
+        SDHCI_GET_CLASS(s)->end_data_transfer(s);
+    }
+
+    sdhci_update_irq(s);
+}
+
+/* Write @size bytes of @value data to host controller @s Buffer Data Port
+ * register */
+static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size)
+{
+    unsigned i;
+
+    /* Check that there is free space left in a buffer */
+    if (!(s->prnsts & SDHC_SPACE_AVAILABLE)) {
+        ERRPRINT("Can't write to data buffer: buffer full\n");
+        return;
+    }
+
+    for (i = 0; i < size; i++) {
+        s->fifo_buffer[s->data_count] = value & 0xFF;
+        s->data_count++;
+        value >>= 8;
+        if (s->data_count >= (s->blksize & 0x0fff)) {
+            DPRINT_L2("write buffer filled with %u bytes of data\n",
+                    s->data_count);
+            s->data_count = 0;
+            s->prnsts &= ~SDHC_SPACE_AVAILABLE;
+            if (s->prnsts & SDHC_DOING_WRITE) {
+                SDHCI_GET_CLASS(s)->write_block_to_card(s);
+            }
+        }
+    }
+}
+
+/*
+ * Single DMA data transfer
+ */
+
+/* Multi block SDMA transfer */
+static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
+{
+    bool page_aligned = false;
+    unsigned int n, begin;
+    const uint16_t block_size = s->blksize & 0x0fff;
+    uint32_t boundary_chk = 1 << (((s->blksize & 0xf000) >> 12) + 12);
+    uint32_t boundary_count = boundary_chk - (s->sdmasysad % boundary_chk);
+
+    /* XXX: Some sd/mmc drivers (for example, u-boot-slp) do not account for
+     * possible stop at page boundary if initial address is not page aligned,
+     * allow them to work properly */
+    if ((s->sdmasysad % boundary_chk) == 0) {
+        page_aligned = true;
+    }
+
+    if (s->trnmod & SDHC_TRNS_READ) {
+        s->prnsts |= SDHC_DOING_READ | SDHC_DATA_INHIBIT |
+                SDHC_DAT_LINE_ACTIVE;
+        while (s->blkcnt) {
+            if (s->data_count == 0) {
+                for (n = 0; n < block_size; n++) {
+                    s->fifo_buffer[n] = sd_read_data(s->card);
+                }
+            }
+            begin = s->data_count;
+            if (((boundary_count + begin) < block_size) && page_aligned) {
+                s->data_count = boundary_count + begin;
+                boundary_count = 0;
+             } else {
+                s->data_count = block_size;
+                boundary_count -= block_size - begin;
+                if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
+                    s->blkcnt--;
+                }
+            }
+            dma_memory_write(&dma_context_memory, s->sdmasysad,
+                             &s->fifo_buffer[begin], s->data_count - begin);
+            s->sdmasysad += s->data_count - begin;
+            if (s->data_count == block_size) {
+                s->data_count = 0;
+            }
+            if (page_aligned && boundary_count == 0) {
+                break;
+            }
+        }
+    } else {
+        s->prnsts |= SDHC_DOING_WRITE | SDHC_DATA_INHIBIT |
+                SDHC_DAT_LINE_ACTIVE;
+        while (s->blkcnt) {
+            begin = s->data_count;
+            if (((boundary_count + begin) < block_size) && page_aligned) {
+                s->data_count = boundary_count + begin;
+                boundary_count = 0;
+             } else {
+                s->data_count = block_size;
+                boundary_count -= block_size - begin;
+            }
+            dma_memory_read(&dma_context_memory, s->sdmasysad,
+                            &s->fifo_buffer[begin], s->data_count);
+            s->sdmasysad += s->data_count - begin;
+            if (s->data_count == block_size) {
+                for (n = 0; n < block_size; n++) {
+                    sd_write_data(s->card, s->fifo_buffer[n]);
+                }
+                s->data_count = 0;
+                if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
+                    s->blkcnt--;
+                }
+            }
+            if (page_aligned && boundary_count == 0) {
+                break;
+            }
+        }
+    }
+
+    if (s->blkcnt == 0) {
+        SDHCI_GET_CLASS(s)->end_data_transfer(s);
+    } else {
+        if (s->norintstsen & SDHC_NISEN_DMA) {
+            s->norintsts |= SDHC_NIS_DMA;
+        }
+        sdhci_update_irq(s);
+    }
+}
+
+/* single block SDMA transfer */
+
+static void sdhci_sdma_transfer_single_block(SDHCIState *s)
+{
+    int n;
+    uint32_t datacnt = s->blksize & 0x0fff;
+
+    if (s->trnmod & SDHC_TRNS_READ) {
+        for (n = 0; n < datacnt; n++) {
+            s->fifo_buffer[n] = sd_read_data(s->card);
+        }
+        dma_memory_write(&dma_context_memory, s->sdmasysad, s->fifo_buffer,
+                         datacnt);
+    } else {
+        dma_memory_read(&dma_context_memory, s->sdmasysad, s->fifo_buffer,
+                        datacnt);
+        for (n = 0; n < datacnt; n++) {
+            sd_write_data(s->card, s->fifo_buffer[n]);
+        }
+    }
+
+    if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
+        s->blkcnt--;
+    }
+
+    SDHCI_GET_CLASS(s)->end_data_transfer(s);
+}
+
+typedef struct ADMADescr {
+    hwaddr addr;
+    uint16_t length;
+    uint8_t attr;
+    uint8_t incr;
+} ADMADescr;
+
+static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
+{
+    uint32_t adma1 = 0;
+    uint64_t adma2 = 0;
+    hwaddr entry_addr = (hwaddr)s->admasysaddr;
+    switch (SDHC_DMA_TYPE(s->hostctl)) {
+    case SDHC_CTRL_ADMA2_32:
+        dma_memory_read(&dma_context_memory, entry_addr, (uint8_t *)&adma2,
+                        sizeof(adma2));
+        adma2 = le64_to_cpu(adma2);
+        /* The spec does not specify endianness of descriptor table.
+         * We currently assume that it is LE.
+         */
+        dscr->addr = (hwaddr)extract64(adma2, 32, 32) & ~0x3ull;
+        dscr->length = (uint16_t)extract64(adma2, 16, 16);
+        dscr->attr = (uint8_t)extract64(adma2, 0, 7);
+        dscr->incr = 8;
+        break;
+    case SDHC_CTRL_ADMA1_32:
+        dma_memory_read(&dma_context_memory, entry_addr, (uint8_t *)&adma1,
+                        sizeof(adma1));
+        adma1 = le32_to_cpu(adma1);
+        dscr->addr = (hwaddr)(adma1 & 0xFFFFF000);
+        dscr->attr = (uint8_t)extract32(adma1, 0, 7);
+        dscr->incr = 4;
+        if ((dscr->attr & SDHC_ADMA_ATTR_ACT_MASK) == SDHC_ADMA_ATTR_SET_LEN) {
+            dscr->length = (uint16_t)extract32(adma1, 12, 16);
+        } else {
+            dscr->length = 4096;
+        }
+        break;
+    case SDHC_CTRL_ADMA2_64:
+        dma_memory_read(&dma_context_memory, entry_addr,
+                        (uint8_t *)(&dscr->attr), 1);
+        dma_memory_read(&dma_context_memory, entry_addr + 2,
+                        (uint8_t *)(&dscr->length), 2);
+        dscr->length = le16_to_cpu(dscr->length);
+        dma_memory_read(&dma_context_memory, entry_addr + 4,
+                        (uint8_t *)(&dscr->addr), 8);
+        dscr->attr = le64_to_cpu(dscr->attr);
+        dscr->attr &= 0xfffffff8;
+        dscr->incr = 12;
+        break;
+    }
+}
+
+/* Advanced DMA data transfer */
+
+static void sdhci_do_adma(SDHCIState *s)
+{
+    unsigned int n, begin, length;
+    const uint16_t block_size = s->blksize & 0x0fff;
+    ADMADescr dscr;
+    int i;
+
+    for (i = 0; i < SDHC_ADMA_DESCS_PER_DELAY; ++i) {
+        s->admaerr &= ~SDHC_ADMAERR_LENGTH_MISMATCH;
+
+        get_adma_description(s, &dscr);
+        DPRINT_L2("ADMA loop: addr=" TARGET_FMT_plx ", len=%d, attr=%x\n",
+                dscr.addr, dscr.length, dscr.attr);
+
+        if ((dscr.attr & SDHC_ADMA_ATTR_VALID) == 0) {
+            /* Indicate that error occurred in ST_FDS state */
+            s->admaerr &= ~SDHC_ADMAERR_STATE_MASK;
+            s->admaerr |= SDHC_ADMAERR_STATE_ST_FDS;
+
+            /* Generate ADMA error interrupt */
+            if (s->errintstsen & SDHC_EISEN_ADMAERR) {
+                s->errintsts |= SDHC_EIS_ADMAERR;
+                s->norintsts |= SDHC_NIS_ERR;
+            }
+
+            sdhci_update_irq(s);
+            return;
+        }
+
+        length = dscr.length ? dscr.length : 65536;
+
+        switch (dscr.attr & SDHC_ADMA_ATTR_ACT_MASK) {
+        case SDHC_ADMA_ATTR_ACT_TRAN:  /* data transfer */
+
+            if (s->trnmod & SDHC_TRNS_READ) {
+                while (length) {
+                    if (s->data_count == 0) {
+                        for (n = 0; n < block_size; n++) {
+                            s->fifo_buffer[n] = sd_read_data(s->card);
+                        }
+                    }
+                    begin = s->data_count;
+                    if ((length + begin) < block_size) {
+                        s->data_count = length + begin;
+                        length = 0;
+                     } else {
+                        s->data_count = block_size;
+                        length -= block_size - begin;
+                    }
+                    dma_memory_write(&dma_context_memory, dscr.addr,
+                                     &s->fifo_buffer[begin],
+                                     s->data_count - begin);
+                    dscr.addr += s->data_count - begin;
+                    if (s->data_count == block_size) {
+                        s->data_count = 0;
+                        if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
+                            s->blkcnt--;
+                            if (s->blkcnt == 0) {
+                                break;
+                            }
+                        }
+                    }
+                }
+            } else {
+                while (length) {
+                    begin = s->data_count;
+                    if ((length + begin) < block_size) {
+                        s->data_count = length + begin;
+                        length = 0;
+                     } else {
+                        s->data_count = block_size;
+                        length -= block_size - begin;
+                    }
+                    dma_memory_read(&dma_context_memory, dscr.addr,
+                                    &s->fifo_buffer[begin], s->data_count);
+                    dscr.addr += s->data_count - begin;
+                    if (s->data_count == block_size) {
+                        for (n = 0; n < block_size; n++) {
+                            sd_write_data(s->card, s->fifo_buffer[n]);
+                        }
+                        s->data_count = 0;
+                        if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
+                            s->blkcnt--;
+                            if (s->blkcnt == 0) {
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+            s->admasysaddr += dscr.incr;
+            break;
+        case SDHC_ADMA_ATTR_ACT_LINK:   /* link to next descriptor table */
+            s->admasysaddr = dscr.addr;
+            DPRINT_L1("ADMA link: admasysaddr=0x%lx\n", s->admasysaddr);
+            break;
+        default:
+            s->admasysaddr += dscr.incr;
+            break;
+        }
+
+        /* ADMA transfer terminates if blkcnt == 0 or by END attribute */
+        if (((s->trnmod & SDHC_TRNS_BLK_CNT_EN) &&
+                    (s->blkcnt == 0)) || (dscr.attr & SDHC_ADMA_ATTR_END)) {
+            DPRINT_L2("ADMA transfer completed\n");
+            if (length || ((dscr.attr & SDHC_ADMA_ATTR_END) &&
+                (s->trnmod & SDHC_TRNS_BLK_CNT_EN) &&
+                s->blkcnt != 0)) {
+                ERRPRINT("SD/MMC host ADMA length mismatch\n");
+                s->admaerr |= SDHC_ADMAERR_LENGTH_MISMATCH |
+                        SDHC_ADMAERR_STATE_ST_TFR;
+                if (s->errintstsen & SDHC_EISEN_ADMAERR) {
+                    ERRPRINT("Set ADMA error flag\n");
+                    s->errintsts |= SDHC_EIS_ADMAERR;
+                    s->norintsts |= SDHC_NIS_ERR;
+                }
+
+                sdhci_update_irq(s);
+            }
+            SDHCI_GET_CLASS(s)->end_data_transfer(s);
+            return;
+        }
+
+        if (dscr.attr & SDHC_ADMA_ATTR_INT) {
+            DPRINT_L1("ADMA interrupt: admasysaddr=0x%lx\n", s->admasysaddr);
+            if (s->norintstsen & SDHC_NISEN_DMA) {
+                s->norintsts |= SDHC_NIS_DMA;
+            }
+
+            sdhci_update_irq(s);
+            return;
+        }
+    }
+
+    /* we have unfinished business - reschedule to continue ADMA */
+    qemu_mod_timer(s->transfer_timer,
+                   qemu_get_clock_ns(vm_clock) + SDHC_TRANSFER_DELAY);
+}
+
+/* Perform data transfer according to controller configuration */
+
+static void sdhci_data_transfer(SDHCIState *s)
+{
+    SDHCIClass *k = SDHCI_GET_CLASS(s);
+    s->data_count = 0;
+
+    if (s->trnmod & SDHC_TRNS_DMA) {
+        switch (SDHC_DMA_TYPE(s->hostctl)) {
+        case SDHC_CTRL_SDMA:
+            if ((s->trnmod & SDHC_TRNS_MULTI) &&
+                    (!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || s->blkcnt == 0)) {
+                break;
+            }
+
+            if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) {
+                k->do_sdma_single(s);
+            } else {
+                k->do_sdma_multi(s);
+            }
+
+            break;
+        case SDHC_CTRL_ADMA1_32:
+            if (!(s->capareg & SDHC_CAN_DO_ADMA1)) {
+                ERRPRINT("ADMA1 not supported\n");
+                break;
+            }
+
+            k->do_adma(s);
+            break;
+        case SDHC_CTRL_ADMA2_32:
+            if (!(s->capareg & SDHC_CAN_DO_ADMA2)) {
+                ERRPRINT("ADMA2 not supported\n");
+                break;
+            }
+
+            k->do_adma(s);
+            break;
+        case SDHC_CTRL_ADMA2_64:
+            if (!(s->capareg & SDHC_CAN_DO_ADMA2) ||
+                    !(s->capareg & SDHC_64_BIT_BUS_SUPPORT)) {
+                ERRPRINT("64 bit ADMA not supported\n");
+                break;
+            }
+
+            k->do_adma(s);
+            break;
+        default:
+            ERRPRINT("Unsupported DMA type\n");
+            break;
+        }
+    } else {
+        if ((s->trnmod & SDHC_TRNS_READ) && sd_data_ready(s->card)) {
+            s->prnsts |= SDHC_DOING_READ | SDHC_DATA_INHIBIT |
+                    SDHC_DAT_LINE_ACTIVE;
+            SDHCI_GET_CLASS(s)->read_block_from_card(s);
+        } else {
+            s->prnsts |= SDHC_DOING_WRITE | SDHC_DAT_LINE_ACTIVE |
+                    SDHC_SPACE_AVAILABLE | SDHC_DATA_INHIBIT;
+            SDHCI_GET_CLASS(s)->write_block_to_card(s);
+        }
+    }
+}
+
+static bool sdhci_can_issue_command(SDHCIState *s)
+{
+    if (!SDHC_CLOCK_IS_ON(s->clkcon) || !(s->pwrcon & SDHC_POWER_ON) ||
+        (((s->prnsts & SDHC_DATA_INHIBIT) || s->stopped_state) &&
+        ((s->cmdreg & SDHC_CMD_DATA_PRESENT) ||
+        ((s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY &&
+        !(SDHC_COMMAND_TYPE(s->cmdreg) == SDHC_CMD_ABORT))))) {
+        return false;
+    }
+
+    return true;
+}
+
+/* The Buffer Data Port register must be accessed in sequential and
+ * continuous manner */
+static inline bool
+sdhci_buff_access_is_sequential(SDHCIState *s, unsigned byte_num)
+{
+    if ((s->data_count & 0x3) != byte_num) {
+        ERRPRINT("Non-sequential access to Buffer Data Port register"
+                "is prohibited\n");
+        return false;
+    }
+    return true;
+}
+
+static uint32_t sdhci_read(SDHCIState *s, unsigned int offset, unsigned size)
+{
+    uint32_t ret = 0;
+
+    switch (offset & ~0x3) {
+    case SDHC_SYSAD:
+        ret = s->sdmasysad;
+        break;
+    case SDHC_BLKSIZE:
+        ret = s->blksize | (s->blkcnt << 16);
+        break;
+    case SDHC_ARGUMENT:
+        ret = s->argument;
+        break;
+    case SDHC_TRNMOD:
+        ret = s->trnmod | (s->cmdreg << 16);
+        break;
+    case SDHC_RSPREG0 ... SDHC_RSPREG3:
+        ret = s->rspreg[((offset & ~0x3) - SDHC_RSPREG0) >> 2];
+        break;
+    case  SDHC_BDATA:
+        if (sdhci_buff_access_is_sequential(s, offset - SDHC_BDATA)) {
+            ret = SDHCI_GET_CLASS(s)->bdata_read(s, size);
+            DPRINT_L2("read %ub: addr[0x%04x] -> %u\n", size, offset, ret);
+            return ret;
+        }
+        break;
+    case SDHC_PRNSTS:
+        ret = s->prnsts;
+        break;
+    case SDHC_HOSTCTL:
+        ret = s->hostctl | (s->pwrcon << 8) | (s->blkgap << 16) |
+              (s->wakcon << 24);
+        break;
+    case SDHC_CLKCON:
+        ret = s->clkcon | (s->timeoutcon << 16);
+        break;
+    case SDHC_NORINTSTS:
+        ret = s->norintsts | (s->errintsts << 16);
+        break;
+    case SDHC_NORINTSTSEN:
+        ret = s->norintstsen | (s->errintstsen << 16);
+        break;
+    case SDHC_NORINTSIGEN:
+        ret = s->norintsigen | (s->errintsigen << 16);
+        break;
+    case SDHC_ACMD12ERRSTS:
+        ret = s->acmd12errsts;
+        break;
+    case SDHC_CAPAREG:
+        ret = s->capareg;
+        break;
+    case SDHC_MAXCURR:
+        ret = s->maxcurr;
+        break;
+    case SDHC_ADMAERR:
+        ret =  s->admaerr;
+        break;
+    case SDHC_ADMASYSADDR:
+        ret = (uint32_t)s->admasysaddr;
+        break;
+    case SDHC_ADMASYSADDR + 4:
+        ret = (uint32_t)(s->admasysaddr >> 32);
+        break;
+    case SDHC_SLOT_INT_STATUS:
+        ret = (SD_HOST_SPECv2_VERS << 16) | sdhci_slotint(s);
+        break;
+    default:
+        ERRPRINT("bad %ub read: addr[0x%04x]\n", size, offset);
+        break;
+    }
+
+    ret >>= (offset & 0x3) * 8;
+    ret &= (1ULL << (size * 8)) - 1;
+    DPRINT_L2("read %ub: addr[0x%04x] -> %u(0x%x)\n", size, offset, ret, ret);
+    return ret;
+}
+
+static inline void sdhci_blkgap_write(SDHCIState *s, uint8_t value)
+{
+    if ((value & SDHC_STOP_AT_GAP_REQ) && (s->blkgap & SDHC_STOP_AT_GAP_REQ)) {
+        return;
+    }
+    s->blkgap = value & SDHC_STOP_AT_GAP_REQ;
+
+    if ((value & SDHC_CONTINUE_REQ) && s->stopped_state &&
+            (s->blkgap & SDHC_STOP_AT_GAP_REQ) == 0) {
+        if (s->stopped_state == sdhc_gap_read) {
+            s->prnsts |= SDHC_DAT_LINE_ACTIVE | SDHC_DOING_READ;
+            SDHCI_GET_CLASS(s)->read_block_from_card(s);
+        } else {
+            s->prnsts |= SDHC_DAT_LINE_ACTIVE | SDHC_DOING_WRITE;
+            SDHCI_GET_CLASS(s)->write_block_to_card(s);
+        }
+        s->stopped_state = sdhc_not_stopped;
+    } else if (!s->stopped_state && (value & SDHC_STOP_AT_GAP_REQ)) {
+        if (s->prnsts & SDHC_DOING_READ) {
+            s->stopped_state = sdhc_gap_read;
+        } else if (s->prnsts & SDHC_DOING_WRITE) {
+            s->stopped_state = sdhc_gap_write;
+        }
+    }
+}
+
+static inline void sdhci_reset_write(SDHCIState *s, uint8_t value)
+{
+    switch (value) {
+    case SDHC_RESET_ALL:
+        DEVICE_GET_CLASS(s)->reset(DEVICE(s));
+        break;
+    case SDHC_RESET_CMD:
+        s->prnsts &= ~SDHC_CMD_INHIBIT;
+        s->norintsts &= ~SDHC_NIS_CMDCMP;
+        break;
+    case SDHC_RESET_DATA:
+        s->data_count = 0;
+        s->prnsts &= ~(SDHC_SPACE_AVAILABLE | SDHC_DATA_AVAILABLE |
+                SDHC_DOING_READ | SDHC_DOING_WRITE |
+                SDHC_DATA_INHIBIT | SDHC_DAT_LINE_ACTIVE);
+        s->blkgap &= ~(SDHC_STOP_AT_GAP_REQ | SDHC_CONTINUE_REQ);
+        s->stopped_state = sdhc_not_stopped;
+        s->norintsts &= ~(SDHC_NIS_WBUFRDY | SDHC_NIS_RBUFRDY |
+                SDHC_NIS_DMA | SDHC_NIS_TRSCMP | SDHC_NIS_BLKGAP);
+        break;
+    }
+}
+
+static void
+sdhci_write(SDHCIState *s, unsigned int offset, uint32_t value, unsigned size)
+{
+    unsigned shift =  8 * (offset & 0x3);
+    uint32_t mask = ~(((1ULL << (size * 8)) - 1) << shift);
+    value <<= shift;
+
+    switch (offset & ~0x3) {
+    case SDHC_SYSAD:
+        s->sdmasysad = (s->sdmasysad & mask) | value;
+        MASKED_WRITE(s->sdmasysad, mask, value);
+        /* Writing to last byte of sdmasysad might trigger transfer */
+        if (!(mask & 0xFF000000) && TRANSFERRING_DATA(s->prnsts) && s->blkcnt &&
+                s->blksize && SDHC_DMA_TYPE(s->hostctl) == SDHC_CTRL_SDMA) {
+            SDHCI_GET_CLASS(s)->do_sdma_multi(s);
+        }
+        break;
+    case SDHC_BLKSIZE:
+        if (!TRANSFERRING_DATA(s->prnsts)) {
+            MASKED_WRITE(s->blksize, mask, value);
+            MASKED_WRITE(s->blkcnt, mask >> 16, value >> 16);
+        }
+        break;
+    case SDHC_ARGUMENT:
+        MASKED_WRITE(s->argument, mask, value);
+        break;
+    case SDHC_TRNMOD:
+        /* DMA can be enabled only if it is supported as indicated by
+         * capabilities register */
+        if (!(s->capareg & SDHC_CAN_DO_DMA)) {
+            value &= ~SDHC_TRNS_DMA;
+        }
+        MASKED_WRITE(s->trnmod, mask, value);
+        MASKED_WRITE(s->cmdreg, mask >> 16, value >> 16);
+
+        /* Writing to the upper byte of CMDREG triggers SD command generation */
+        if ((mask & 0xFF000000) || !SDHCI_GET_CLASS(s)->can_issue_command(s)) {
+            break;
+        }
+
+        SDHCI_GET_CLASS(s)->send_command(s);
+        break;
+    case  SDHC_BDATA:
+        if (sdhci_buff_access_is_sequential(s, offset - SDHC_BDATA)) {
+            SDHCI_GET_CLASS(s)->bdata_write(s, value >> shift, size);
+        }
+        break;
+    case SDHC_HOSTCTL:
+        if (!(mask & 0xFF0000)) {
+            sdhci_blkgap_write(s, value >> 16);
+        }
+        MASKED_WRITE(s->hostctl, mask, value);
+        MASKED_WRITE(s->pwrcon, mask >> 8, value >> 8);
+        MASKED_WRITE(s->wakcon, mask >> 24, value >> 24);
+        if (!(s->prnsts & SDHC_CARD_PRESENT) || ((s->pwrcon >> 1) & 0x7) < 5 ||
+                !(s->capareg & (1 << (31 - ((s->pwrcon >> 1) & 0x7))))) {
+            s->pwrcon &= ~SDHC_POWER_ON;
+        }
+        break;
+    case SDHC_CLKCON:
+        if (!(mask & 0xFF000000)) {
+            sdhci_reset_write(s, value >> 24);
+        }
+        MASKED_WRITE(s->clkcon, mask, value);
+        MASKED_WRITE(s->timeoutcon, mask >> 16, value >> 16);
+        if (s->clkcon & SDHC_CLOCK_INT_EN) {
+            s->clkcon |= SDHC_CLOCK_INT_STABLE;
+        } else {
+            s->clkcon &= ~SDHC_CLOCK_INT_STABLE;
+        }
+        break;
+    case SDHC_NORINTSTS:
+        if (s->norintstsen & SDHC_NISEN_CARDINT) {
+            value &= ~SDHC_NIS_CARDINT;
+        }
+        s->norintsts &= mask | ~value;
+        s->errintsts &= (mask >> 16) | ~(value >> 16);
+        if (s->errintsts) {
+            s->norintsts |= SDHC_NIS_ERR;
+        } else {
+            s->norintsts &= ~SDHC_NIS_ERR;
+        }
+        sdhci_update_irq(s);
+        break;
+    case SDHC_NORINTSTSEN:
+        MASKED_WRITE(s->norintstsen, mask, value);
+        MASKED_WRITE(s->errintstsen, mask >> 16, value >> 16);
+        s->norintsts &= s->norintstsen;
+        s->errintsts &= s->errintstsen;
+        if (s->errintsts) {
+            s->norintsts |= SDHC_NIS_ERR;
+        } else {
+            s->norintsts &= ~SDHC_NIS_ERR;
+        }
+        sdhci_update_irq(s);
+        break;
+    case SDHC_NORINTSIGEN:
+        MASKED_WRITE(s->norintsigen, mask, value);
+        MASKED_WRITE(s->errintsigen, mask >> 16, value >> 16);
+        sdhci_update_irq(s);
+        break;
+    case SDHC_ADMAERR:
+        MASKED_WRITE(s->admaerr, mask, value);
+        break;
+    case SDHC_ADMASYSADDR:
+        s->admasysaddr = (s->admasysaddr & (0xFFFFFFFF00000000ULL |
+                (uint64_t)mask)) | (uint64_t)value;
+        break;
+    case SDHC_ADMASYSADDR + 4:
+        s->admasysaddr = (s->admasysaddr & (0x00000000FFFFFFFFULL |
+                ((uint64_t)mask << 32))) | ((uint64_t)value << 32);
+        break;
+    case SDHC_FEAER:
+        s->acmd12errsts |= value;
+        s->errintsts |= (value >> 16) & s->errintstsen;
+        if (s->acmd12errsts) {
+            s->errintsts |= SDHC_EIS_CMD12ERR;
+        }
+        if (s->errintsts) {
+            s->norintsts |= SDHC_NIS_ERR;
+        }
+        sdhci_update_irq(s);
+        break;
+    default:
+        ERRPRINT("bad %ub write offset: addr[0x%04x] <- %u(0x%x)\n",
+                size, offset, value >> shift, value >> shift);
+        break;
+    }
+    DPRINT_L2("write %ub: addr[0x%04x] <- %u(0x%x)\n",
+            size, offset, value >> shift, value >> shift);
+}
+
+static uint64_t
+sdhci_readfn(void *opaque, hwaddr offset, unsigned size)
+{
+    SDHCIState *s = (SDHCIState *)opaque;
+
+    return SDHCI_GET_CLASS(s)->mem_read(s, offset, size);
+}
+
+static void
+sdhci_writefn(void *opaque, hwaddr off, uint64_t val, unsigned sz)
+{
+    SDHCIState *s = (SDHCIState *)opaque;
+
+    SDHCI_GET_CLASS(s)->mem_write(s, off, val, sz);
+}
+
+static const MemoryRegionOps sdhci_mmio_ops = {
+    .read = sdhci_readfn,
+    .write = sdhci_writefn,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+        .unaligned = false
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static inline unsigned int sdhci_get_fifolen(SDHCIState *s)
+{
+    switch (SDHC_CAPAB_BLOCKSIZE(s->capareg)) {
+    case 0:
+        return 512;
+    case 1:
+        return 1024;
+    case 2:
+        return 2048;
+    default:
+        hw_error("SDHC: unsupported value for maximum block size\n");
+        return 0;
+    }
+}
+
+static void sdhci_initfn(Object *obj)
+{
+    SDHCIState *s = SDHCI(obj);
+    DriveInfo *di;
+
+    di = drive_get_next(IF_SD);
+    s->card = sd_init(di ? di->bdrv : NULL, 0);
+    s->eject_cb = qemu_allocate_irqs(sdhci_insert_eject_cb, s, 1)[0];
+    s->ro_cb = qemu_allocate_irqs(sdhci_card_readonly_cb, s, 1)[0];
+    sd_set_cb(s->card, s->ro_cb, s->eject_cb);
+
+    s->insert_timer = qemu_new_timer_ns(vm_clock, sdhci_raise_insertion_irq, s);
+    s->transfer_timer = qemu_new_timer_ns(vm_clock, sdhci_do_data_transfer, s);
+}
+
+static void sdhci_uninitfn(Object *obj)
+{
+    SDHCIState *s = SDHCI(obj);
+
+    qemu_del_timer(s->insert_timer);
+    qemu_free_timer(s->insert_timer);
+    qemu_del_timer(s->transfer_timer);
+    qemu_free_timer(s->transfer_timer);
+    qemu_free_irqs(&s->eject_cb);
+    qemu_free_irqs(&s->ro_cb);
+
+    if (s->fifo_buffer) {
+        g_free(s->fifo_buffer);
+        s->fifo_buffer = NULL;
+    }
+}
+
+const VMStateDescription sdhci_vmstate = {
+    .name = "sdhci",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32(sdmasysad, SDHCIState),
+        VMSTATE_UINT16(blksize, SDHCIState),
+        VMSTATE_UINT16(blkcnt, SDHCIState),
+        VMSTATE_UINT32(argument, SDHCIState),
+        VMSTATE_UINT16(trnmod, SDHCIState),
+        VMSTATE_UINT16(cmdreg, SDHCIState),
+        VMSTATE_UINT32_ARRAY(rspreg, SDHCIState, 4),
+        VMSTATE_UINT32(prnsts, SDHCIState),
+        VMSTATE_UINT8(hostctl, SDHCIState),
+        VMSTATE_UINT8(pwrcon, SDHCIState),
+        VMSTATE_UINT8(blkgap, SDHCIState),
+        VMSTATE_UINT8(wakcon, SDHCIState),
+        VMSTATE_UINT16(clkcon, SDHCIState),
+        VMSTATE_UINT8(timeoutcon, SDHCIState),
+        VMSTATE_UINT8(admaerr, SDHCIState),
+        VMSTATE_UINT16(norintsts, SDHCIState),
+        VMSTATE_UINT16(errintsts, SDHCIState),
+        VMSTATE_UINT16(norintstsen, SDHCIState),
+        VMSTATE_UINT16(errintstsen, SDHCIState),
+        VMSTATE_UINT16(norintsigen, SDHCIState),
+        VMSTATE_UINT16(errintsigen, SDHCIState),
+        VMSTATE_UINT16(acmd12errsts, SDHCIState),
+        VMSTATE_UINT16(data_count, SDHCIState),
+        VMSTATE_UINT64(admasysaddr, SDHCIState),
+        VMSTATE_UINT8(stopped_state, SDHCIState),
+        VMSTATE_VBUFFER_UINT32(fifo_buffer, SDHCIState, 1, NULL, 0, buf_maxsz),
+        VMSTATE_TIMER(insert_timer, SDHCIState),
+        VMSTATE_TIMER(transfer_timer, SDHCIState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+/* Capabilities registers provide information on supported features of this
+ * specific host controller implementation */
+static Property sdhci_properties[] = {
+    DEFINE_PROP_HEX32("capareg", SDHCIState, capareg,
+            SDHC_CAPAB_REG_DEFAULT),
+    DEFINE_PROP_HEX32("maxcurr", SDHCIState, maxcurr, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void sdhci_realize(DeviceState *dev, Error ** errp)
+{
+    SDHCIState *s = SDHCI(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    s->buf_maxsz = sdhci_get_fifolen(s);
+    s->fifo_buffer = g_malloc0(s->buf_maxsz);
+    sysbus_init_irq(sbd, &s->irq);
+    memory_region_init_io(&s->iomem, &sdhci_mmio_ops, s, "sdhci",
+            SDHC_REGISTERS_MAP_SIZE);
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static void sdhci_generic_reset(DeviceState *ds)
+{
+    SDHCIState *s = SDHCI(ds);
+    SDHCI_GET_CLASS(s)->reset(s);
+}
+
+static void sdhci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SDHCIClass *k = SDHCI_CLASS(klass);
+
+    dc->vmsd = &sdhci_vmstate;
+    dc->props = sdhci_properties;
+    dc->reset = sdhci_generic_reset;
+    dc->realize = sdhci_realize;
+
+    k->reset = sdhci_reset;
+    k->mem_read = sdhci_read;
+    k->mem_write = sdhci_write;
+    k->send_command = sdhci_send_command;
+    k->can_issue_command = sdhci_can_issue_command;
+    k->data_transfer = sdhci_data_transfer;
+    k->end_data_transfer = sdhci_end_transfer;
+    k->do_sdma_single = sdhci_sdma_transfer_single_block;
+    k->do_sdma_multi = sdhci_sdma_transfer_multi_blocks;
+    k->do_adma = sdhci_do_adma;
+    k->read_block_from_card = sdhci_read_block_from_card;
+    k->write_block_to_card = sdhci_write_block_to_card;
+    k->bdata_read = sdhci_read_dataport;
+    k->bdata_write = sdhci_write_dataport;
+}
+
+static const TypeInfo sdhci_type_info = {
+    .name = TYPE_SDHCI,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SDHCIState),
+    .instance_init = sdhci_initfn,
+    .instance_finalize = sdhci_uninitfn,
+    .class_init = sdhci_class_init,
+    .class_size = sizeof(SDHCIClass)
+};
+
+static void sdhci_register_types(void)
+{
+    type_register_static(&sdhci_type_info);
+}
+
+type_init(sdhci_register_types)
diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c
new file mode 100644 (file)
index 0000000..4d3c4f6
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * SSI to SD card adapter.
+ *
+ * Copyright (c) 2007-2009 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GNU GPL v2.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "sysemu/blockdev.h"
+#include "hw/ssi.h"
+#include "hw/sd.h"
+
+//#define DEBUG_SSI_SD 1
+
+#ifdef DEBUG_SSI_SD
+#define DPRINTF(fmt, ...) \
+do { printf("ssi_sd: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "ssi_sd: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "ssi_sd: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+typedef enum {
+    SSI_SD_CMD,
+    SSI_SD_CMDARG,
+    SSI_SD_RESPONSE,
+    SSI_SD_DATA_START,
+    SSI_SD_DATA_READ,
+} ssi_sd_mode;
+
+typedef struct {
+    SSISlave ssidev;
+    ssi_sd_mode mode;
+    int cmd;
+    uint8_t cmdarg[4];
+    uint8_t response[5];
+    int arglen;
+    int response_pos;
+    int stopping;
+    SDState *sd;
+} ssi_sd_state;
+
+/* State word bits.  */
+#define SSI_SDR_LOCKED          0x0001
+#define SSI_SDR_WP_ERASE        0x0002
+#define SSI_SDR_ERROR           0x0004
+#define SSI_SDR_CC_ERROR        0x0008
+#define SSI_SDR_ECC_FAILED      0x0010
+#define SSI_SDR_WP_VIOLATION    0x0020
+#define SSI_SDR_ERASE_PARAM     0x0040
+#define SSI_SDR_OUT_OF_RANGE    0x0080
+#define SSI_SDR_IDLE            0x0100
+#define SSI_SDR_ERASE_RESET     0x0200
+#define SSI_SDR_ILLEGAL_COMMAND 0x0400
+#define SSI_SDR_COM_CRC_ERROR   0x0800
+#define SSI_SDR_ERASE_SEQ_ERROR 0x1000
+#define SSI_SDR_ADDRESS_ERROR   0x2000
+#define SSI_SDR_PARAMETER_ERROR 0x4000
+
+static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val)
+{
+    ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, dev);
+
+    /* Special case: allow CMD12 (STOP TRANSMISSION) while reading data.  */
+    if (s->mode == SSI_SD_DATA_READ && val == 0x4d) {
+        s->mode = SSI_SD_CMD;
+        /* There must be at least one byte delay before the card responds.  */
+        s->stopping = 1;
+    }
+
+    switch (s->mode) {
+    case SSI_SD_CMD:
+        if (val == 0xff) {
+            DPRINTF("NULL command\n");
+            return 0xff;
+        }
+        s->cmd = val & 0x3f;
+        s->mode = SSI_SD_CMDARG;
+        s->arglen = 0;
+        return 0xff;
+    case SSI_SD_CMDARG:
+        if (s->arglen == 4) {
+            SDRequest request;
+            uint8_t longresp[16];
+            /* FIXME: Check CRC.  */
+            request.cmd = s->cmd;
+            request.arg = (s->cmdarg[0] << 24) | (s->cmdarg[1] << 16)
+                           | (s->cmdarg[2] << 8) | s->cmdarg[3];
+            DPRINTF("CMD%d arg 0x%08x\n", s->cmd, request.arg);
+            s->arglen = sd_do_command(s->sd, &request, longresp);
+            if (s->arglen <= 0) {
+                s->arglen = 1;
+                s->response[0] = 4;
+                DPRINTF("SD command failed\n");
+            } else if (s->cmd == 58) {
+                /* CMD58 returns R3 response (OCR)  */
+                DPRINTF("Returned OCR\n");
+                s->arglen = 5;
+                s->response[0] = 1;
+                memcpy(&s->response[1], longresp, 4);
+            } else if (s->arglen != 4) {
+                BADF("Unexpected response to cmd %d\n", s->cmd);
+                /* Illegal command is about as near as we can get.  */
+                s->arglen = 1;
+                s->response[0] = 4;
+            } else {
+                /* All other commands return status.  */
+                uint32_t cardstatus;
+                uint16_t status;
+                /* CMD13 returns a 2-byte statuse work. Other commands
+                   only return the first byte.  */
+                s->arglen = (s->cmd == 13) ? 2 : 1;
+                cardstatus = (longresp[0] << 24) | (longresp[1] << 16)
+                             | (longresp[2] << 8) | longresp[3];
+                status = 0;
+                if (((cardstatus >> 9) & 0xf) < 4)
+                    status |= SSI_SDR_IDLE;
+                if (cardstatus & ERASE_RESET)
+                    status |= SSI_SDR_ERASE_RESET;
+                if (cardstatus & ILLEGAL_COMMAND)
+                    status |= SSI_SDR_ILLEGAL_COMMAND;
+                if (cardstatus & COM_CRC_ERROR)
+                    status |= SSI_SDR_COM_CRC_ERROR;
+                if (cardstatus & ERASE_SEQ_ERROR)
+                    status |= SSI_SDR_ERASE_SEQ_ERROR;
+                if (cardstatus & ADDRESS_ERROR)
+                    status |= SSI_SDR_ADDRESS_ERROR;
+                if (cardstatus & CARD_IS_LOCKED)
+                    status |= SSI_SDR_LOCKED;
+                if (cardstatus & (LOCK_UNLOCK_FAILED | WP_ERASE_SKIP))
+                    status |= SSI_SDR_WP_ERASE;
+                if (cardstatus & SD_ERROR)
+                    status |= SSI_SDR_ERROR;
+                if (cardstatus & CC_ERROR)
+                    status |= SSI_SDR_CC_ERROR;
+                if (cardstatus & CARD_ECC_FAILED)
+                    status |= SSI_SDR_ECC_FAILED;
+                if (cardstatus & WP_VIOLATION)
+                    status |= SSI_SDR_WP_VIOLATION;
+                if (cardstatus & ERASE_PARAM)
+                    status |= SSI_SDR_ERASE_PARAM;
+                if (cardstatus & (OUT_OF_RANGE | CID_CSD_OVERWRITE))
+                    status |= SSI_SDR_OUT_OF_RANGE;
+                /* ??? Don't know what Parameter Error really means, so
+                   assume it's set if the second byte is nonzero.  */
+                if (status & 0xff)
+                    status |= SSI_SDR_PARAMETER_ERROR;
+                s->response[0] = status >> 8;
+                s->response[1] = status;
+                DPRINTF("Card status 0x%02x\n", status);
+            }
+            s->mode = SSI_SD_RESPONSE;
+            s->response_pos = 0;
+        } else {
+            s->cmdarg[s->arglen++] = val;
+        }
+        return 0xff;
+    case SSI_SD_RESPONSE:
+        if (s->stopping) {
+            s->stopping = 0;
+            return 0xff;
+        }
+        if (s->response_pos < s->arglen) {
+            DPRINTF("Response 0x%02x\n", s->response[s->response_pos]);
+            return s->response[s->response_pos++];
+        }
+        if (sd_data_ready(s->sd)) {
+            DPRINTF("Data read\n");
+            s->mode = SSI_SD_DATA_START;
+        } else {
+            DPRINTF("End of command\n");
+            s->mode = SSI_SD_CMD;
+        }
+        return 0xff;
+    case SSI_SD_DATA_START:
+        DPRINTF("Start read block\n");
+        s->mode = SSI_SD_DATA_READ;
+        return 0xfe;
+    case SSI_SD_DATA_READ:
+        val = sd_read_data(s->sd);
+        if (!sd_data_ready(s->sd)) {
+            DPRINTF("Data read end\n");
+            s->mode = SSI_SD_CMD;
+        }
+        return val;
+    }
+    /* Should never happen.  */
+    return 0xff;
+}
+
+static void ssi_sd_save(QEMUFile *f, void *opaque)
+{
+    SSISlave *ss = SSI_SLAVE(opaque);
+    ssi_sd_state *s = (ssi_sd_state *)opaque;
+    int i;
+
+    qemu_put_be32(f, s->mode);
+    qemu_put_be32(f, s->cmd);
+    for (i = 0; i < 4; i++)
+        qemu_put_be32(f, s->cmdarg[i]);
+    for (i = 0; i < 5; i++)
+        qemu_put_be32(f, s->response[i]);
+    qemu_put_be32(f, s->arglen);
+    qemu_put_be32(f, s->response_pos);
+    qemu_put_be32(f, s->stopping);
+
+    qemu_put_be32(f, ss->cs);
+}
+
+static int ssi_sd_load(QEMUFile *f, void *opaque, int version_id)
+{
+    SSISlave *ss = SSI_SLAVE(opaque);
+    ssi_sd_state *s = (ssi_sd_state *)opaque;
+    int i;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    s->mode = qemu_get_be32(f);
+    s->cmd = qemu_get_be32(f);
+    for (i = 0; i < 4; i++)
+        s->cmdarg[i] = qemu_get_be32(f);
+    for (i = 0; i < 5; i++)
+        s->response[i] = qemu_get_be32(f);
+    s->arglen = qemu_get_be32(f);
+    s->response_pos = qemu_get_be32(f);
+    s->stopping = qemu_get_be32(f);
+
+    ss->cs = qemu_get_be32(f);
+
+    return 0;
+}
+
+static int ssi_sd_init(SSISlave *dev)
+{
+    ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, dev);
+    DriveInfo *dinfo;
+
+    s->mode = SSI_SD_CMD;
+    dinfo = drive_get_next(IF_SD);
+    s->sd = sd_init(dinfo ? dinfo->bdrv : NULL, 1);
+    register_savevm(&dev->qdev, "ssi_sd", -1, 1, ssi_sd_save, ssi_sd_load, s);
+    return 0;
+}
+
+static void ssi_sd_class_init(ObjectClass *klass, void *data)
+{
+    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
+
+    k->init = ssi_sd_init;
+    k->transfer = ssi_sd_transfer;
+    k->cs_polarity = SSI_CS_LOW;
+}
+
+static const TypeInfo ssi_sd_info = {
+    .name          = "ssi-sd",
+    .parent        = TYPE_SSI_SLAVE,
+    .instance_size = sizeof(ssi_sd_state),
+    .class_init    = ssi_sd_class_init,
+};
+
+static void ssi_sd_register_types(void)
+{
+    type_register_static(&ssi_sd_info);
+}
+
+type_init(ssi_sd_register_types)
diff --git a/hw/sdhci.c b/hw/sdhci.c
deleted file mode 100644 (file)
index 4a29e6c..0000000
+++ /dev/null
@@ -1,1300 +0,0 @@
-/*
- * SD Association Host Standard Specification v2.0 controller emulation
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * Mitsyanko Igor <i.mitsyanko@samsung.com>
- * Peter A.G. Crosthwaite <peter.crosthwaite@petalogix.com>
- *
- * Based on MMC controller for Samsung S5PC1xx-based board emulation
- * by Alexey Merkulov and Vladimir Monakhov.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/hw.h"
-#include "sysemu/blockdev.h"
-#include "sysemu/dma.h"
-#include "qemu/timer.h"
-#include "block/block_int.h"
-#include "qemu/bitops.h"
-
-#include "hw/sdhci.h"
-
-/* host controller debug messages */
-#ifndef SDHC_DEBUG
-#define SDHC_DEBUG                        0
-#endif
-
-#if SDHC_DEBUG == 0
-    #define DPRINT_L1(fmt, args...)       do { } while (0)
-    #define DPRINT_L2(fmt, args...)       do { } while (0)
-    #define ERRPRINT(fmt, args...)        do { } while (0)
-#elif SDHC_DEBUG == 1
-    #define DPRINT_L1(fmt, args...)       \
-        do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0)
-    #define DPRINT_L2(fmt, args...)       do { } while (0)
-    #define ERRPRINT(fmt, args...)        \
-        do {fprintf(stderr, "QEMU SDHC ERROR: "fmt, ## args); } while (0)
-#else
-    #define DPRINT_L1(fmt, args...)       \
-        do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0)
-    #define DPRINT_L2(fmt, args...)       \
-        do {fprintf(stderr, "QEMU SDHC: "fmt, ## args); } while (0)
-    #define ERRPRINT(fmt, args...)        \
-        do {fprintf(stderr, "QEMU SDHC ERROR: "fmt, ## args); } while (0)
-#endif
-
-/* Default SD/MMC host controller features information, which will be
- * presented in CAPABILITIES register of generic SD host controller at reset.
- * If not stated otherwise:
- * 0 - not supported, 1 - supported, other - prohibited.
- */
-#define SDHC_CAPAB_64BITBUS       0ul        /* 64-bit System Bus Support */
-#define SDHC_CAPAB_18V            1ul        /* Voltage support 1.8v */
-#define SDHC_CAPAB_30V            0ul        /* Voltage support 3.0v */
-#define SDHC_CAPAB_33V            1ul        /* Voltage support 3.3v */
-#define SDHC_CAPAB_SUSPRESUME     0ul        /* Suspend/resume support */
-#define SDHC_CAPAB_SDMA           1ul        /* SDMA support */
-#define SDHC_CAPAB_HIGHSPEED      1ul        /* High speed support */
-#define SDHC_CAPAB_ADMA1          1ul        /* ADMA1 support */
-#define SDHC_CAPAB_ADMA2          1ul        /* ADMA2 support */
-/* Maximum host controller R/W buffers size
- * Possible values: 512, 1024, 2048 bytes */
-#define SDHC_CAPAB_MAXBLOCKLENGTH 512ul
-/* Maximum clock frequency for SDclock in MHz
- * value in range 10-63 MHz, 0 - not defined */
-#define SDHC_CAPAB_BASECLKFREQ    0ul
-#define SDHC_CAPAB_TOUNIT         1ul  /* Timeout clock unit 0 - kHz, 1 - MHz */
-/* Timeout clock frequency 1-63, 0 - not defined */
-#define SDHC_CAPAB_TOCLKFREQ      0ul
-
-/* Now check all parameters and calculate CAPABILITIES REGISTER value */
-#if SDHC_CAPAB_64BITBUS > 1 || SDHC_CAPAB_18V > 1 || SDHC_CAPAB_30V > 1 ||     \
-    SDHC_CAPAB_33V > 1 || SDHC_CAPAB_SUSPRESUME > 1 || SDHC_CAPAB_SDMA > 1 ||  \
-    SDHC_CAPAB_HIGHSPEED > 1 || SDHC_CAPAB_ADMA2 > 1 || SDHC_CAPAB_ADMA1 > 1 ||\
-    SDHC_CAPAB_TOUNIT > 1
-#error Capabilities features can have value 0 or 1 only!
-#endif
-
-#if SDHC_CAPAB_MAXBLOCKLENGTH == 512
-#define MAX_BLOCK_LENGTH 0ul
-#elif SDHC_CAPAB_MAXBLOCKLENGTH == 1024
-#define MAX_BLOCK_LENGTH 1ul
-#elif SDHC_CAPAB_MAXBLOCKLENGTH == 2048
-#define MAX_BLOCK_LENGTH 2ul
-#else
-#error Max host controller block size can have value 512, 1024 or 2048 only!
-#endif
-
-#if (SDHC_CAPAB_BASECLKFREQ > 0 && SDHC_CAPAB_BASECLKFREQ < 10) || \
-    SDHC_CAPAB_BASECLKFREQ > 63
-#error SDclock frequency can have value in range 0, 10-63 only!
-#endif
-
-#if SDHC_CAPAB_TOCLKFREQ > 63
-#error Timeout clock frequency can have value in range 0-63 only!
-#endif
-
-#define SDHC_CAPAB_REG_DEFAULT                                 \
-   ((SDHC_CAPAB_64BITBUS << 28) | (SDHC_CAPAB_18V << 26) |     \
-    (SDHC_CAPAB_30V << 25) | (SDHC_CAPAB_33V << 24) |          \
-    (SDHC_CAPAB_SUSPRESUME << 23) | (SDHC_CAPAB_SDMA << 22) |  \
-    (SDHC_CAPAB_HIGHSPEED << 21) | (SDHC_CAPAB_ADMA1 << 20) |  \
-    (SDHC_CAPAB_ADMA2 << 19) | (MAX_BLOCK_LENGTH << 16) |      \
-    (SDHC_CAPAB_BASECLKFREQ << 8) | (SDHC_CAPAB_TOUNIT << 7) | \
-    (SDHC_CAPAB_TOCLKFREQ))
-
-#define MASKED_WRITE(reg, mask, val)  (reg = (reg & (mask)) | (val))
-
-static uint8_t sdhci_slotint(SDHCIState *s)
-{
-    return (s->norintsts & s->norintsigen) || (s->errintsts & s->errintsigen) ||
-         ((s->norintsts & SDHC_NIS_INSERT) && (s->wakcon & SDHC_WKUP_ON_INS)) ||
-         ((s->norintsts & SDHC_NIS_REMOVE) && (s->wakcon & SDHC_WKUP_ON_RMV));
-}
-
-static inline void sdhci_update_irq(SDHCIState *s)
-{
-    qemu_set_irq(s->irq, sdhci_slotint(s));
-}
-
-static void sdhci_raise_insertion_irq(void *opaque)
-{
-    SDHCIState *s = (SDHCIState *)opaque;
-
-    if (s->norintsts & SDHC_NIS_REMOVE) {
-        qemu_mod_timer(s->insert_timer,
-                       qemu_get_clock_ns(vm_clock) + SDHC_INSERTION_DELAY);
-    } else {
-        s->prnsts = 0x1ff0000;
-        if (s->norintstsen & SDHC_NISEN_INSERT) {
-            s->norintsts |= SDHC_NIS_INSERT;
-        }
-        sdhci_update_irq(s);
-    }
-}
-
-static void sdhci_insert_eject_cb(void *opaque, int irq, int level)
-{
-    SDHCIState *s = (SDHCIState *)opaque;
-    DPRINT_L1("Card state changed: %s!\n", level ? "insert" : "eject");
-
-    if ((s->norintsts & SDHC_NIS_REMOVE) && level) {
-        /* Give target some time to notice card ejection */
-        qemu_mod_timer(s->insert_timer,
-                       qemu_get_clock_ns(vm_clock) + SDHC_INSERTION_DELAY);
-    } else {
-        if (level) {
-            s->prnsts = 0x1ff0000;
-            if (s->norintstsen & SDHC_NISEN_INSERT) {
-                s->norintsts |= SDHC_NIS_INSERT;
-            }
-        } else {
-            s->prnsts = 0x1fa0000;
-            s->pwrcon &= ~SDHC_POWER_ON;
-            s->clkcon &= ~SDHC_CLOCK_SDCLK_EN;
-            if (s->norintstsen & SDHC_NISEN_REMOVE) {
-                s->norintsts |= SDHC_NIS_REMOVE;
-            }
-        }
-        sdhci_update_irq(s);
-    }
-}
-
-static void sdhci_card_readonly_cb(void *opaque, int irq, int level)
-{
-    SDHCIState *s = (SDHCIState *)opaque;
-
-    if (level) {
-        s->prnsts &= ~SDHC_WRITE_PROTECT;
-    } else {
-        /* Write enabled */
-        s->prnsts |= SDHC_WRITE_PROTECT;
-    }
-}
-
-static void sdhci_reset(SDHCIState *s)
-{
-    qemu_del_timer(s->insert_timer);
-    qemu_del_timer(s->transfer_timer);
-    /* Set all registers to 0. Capabilities registers are not cleared
-     * and assumed to always preserve their value, given to them during
-     * initialization */
-    memset(&s->sdmasysad, 0, (uintptr_t)&s->capareg - (uintptr_t)&s->sdmasysad);
-
-    sd_set_cb(s->card, s->ro_cb, s->eject_cb);
-    s->data_count = 0;
-    s->stopped_state = sdhc_not_stopped;
-}
-
-static void sdhci_do_data_transfer(void *opaque)
-{
-    SDHCIState *s = (SDHCIState *)opaque;
-
-    SDHCI_GET_CLASS(s)->data_transfer(s);
-}
-
-static void sdhci_send_command(SDHCIState *s)
-{
-    SDRequest request;
-    uint8_t response[16];
-    int rlen;
-
-    s->errintsts = 0;
-    s->acmd12errsts = 0;
-    request.cmd = s->cmdreg >> 8;
-    request.arg = s->argument;
-    DPRINT_L1("sending CMD%u ARG[0x%08x]\n", request.cmd, request.arg);
-    rlen = sd_do_command(s->card, &request, response);
-
-    if (s->cmdreg & SDHC_CMD_RESPONSE) {
-        if (rlen == 4) {
-            s->rspreg[0] = (response[0] << 24) | (response[1] << 16) |
-                           (response[2] << 8)  |  response[3];
-            s->rspreg[1] = s->rspreg[2] = s->rspreg[3] = 0;
-            DPRINT_L1("Response: RSPREG[31..0]=0x%08x\n", s->rspreg[0]);
-        } else if (rlen == 16) {
-            s->rspreg[0] = (response[11] << 24) | (response[12] << 16) |
-                           (response[13] << 8) |  response[14];
-            s->rspreg[1] = (response[7] << 24) | (response[8] << 16) |
-                           (response[9] << 8)  |  response[10];
-            s->rspreg[2] = (response[3] << 24) | (response[4] << 16) |
-                           (response[5] << 8)  |  response[6];
-            s->rspreg[3] = (response[0] << 16) | (response[1] << 8) |
-                            response[2];
-            DPRINT_L1("Response received:\n RSPREG[127..96]=0x%08x, RSPREG[95.."
-                  "64]=0x%08x,\n RSPREG[63..32]=0x%08x, RSPREG[31..0]=0x%08x\n",
-                  s->rspreg[3], s->rspreg[2], s->rspreg[1], s->rspreg[0]);
-        } else {
-            ERRPRINT("Timeout waiting for command response\n");
-            if (s->errintstsen & SDHC_EISEN_CMDTIMEOUT) {
-                s->errintsts |= SDHC_EIS_CMDTIMEOUT;
-                s->norintsts |= SDHC_NIS_ERR;
-            }
-        }
-
-        if ((s->norintstsen & SDHC_NISEN_TRSCMP) &&
-            (s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY) {
-            s->norintsts |= SDHC_NIS_TRSCMP;
-        }
-    } else if (rlen != 0 && (s->errintstsen & SDHC_EISEN_CMDIDX)) {
-        s->errintsts |= SDHC_EIS_CMDIDX;
-        s->norintsts |= SDHC_NIS_ERR;
-    }
-
-    if (s->norintstsen & SDHC_NISEN_CMDCMP) {
-        s->norintsts |= SDHC_NIS_CMDCMP;
-    }
-
-    sdhci_update_irq(s);
-
-    if (s->blksize && (s->cmdreg & SDHC_CMD_DATA_PRESENT)) {
-        sdhci_do_data_transfer(s);
-    }
-}
-
-static void sdhci_end_transfer(SDHCIState *s)
-{
-    /* Automatically send CMD12 to stop transfer if AutoCMD12 enabled */
-    if ((s->trnmod & SDHC_TRNS_ACMD12) != 0) {
-        SDRequest request;
-        uint8_t response[16];
-
-        request.cmd = 0x0C;
-        request.arg = 0;
-        DPRINT_L1("Automatically issue CMD%d %08x\n", request.cmd, request.arg);
-        sd_do_command(s->card, &request, response);
-        /* Auto CMD12 response goes to the upper Response register */
-        s->rspreg[3] = (response[0] << 24) | (response[1] << 16) |
-                (response[2] << 8) | response[3];
-    }
-
-    s->prnsts &= ~(SDHC_DOING_READ | SDHC_DOING_WRITE |
-            SDHC_DAT_LINE_ACTIVE | SDHC_DATA_INHIBIT |
-            SDHC_SPACE_AVAILABLE | SDHC_DATA_AVAILABLE);
-
-    if (s->norintstsen & SDHC_NISEN_TRSCMP) {
-        s->norintsts |= SDHC_NIS_TRSCMP;
-    }
-
-    sdhci_update_irq(s);
-}
-
-/*
- * Programmed i/o data transfer
- */
-
-/* Fill host controller's read buffer with BLKSIZE bytes of data from card */
-static void sdhci_read_block_from_card(SDHCIState *s)
-{
-    int index = 0;
-
-    if ((s->trnmod & SDHC_TRNS_MULTI) &&
-            (s->trnmod & SDHC_TRNS_BLK_CNT_EN) && (s->blkcnt == 0)) {
-        return;
-    }
-
-    for (index = 0; index < (s->blksize & 0x0fff); index++) {
-        s->fifo_buffer[index] = sd_read_data(s->card);
-    }
-
-    /* New data now available for READ through Buffer Port Register */
-    s->prnsts |= SDHC_DATA_AVAILABLE;
-    if (s->norintstsen & SDHC_NISEN_RBUFRDY) {
-        s->norintsts |= SDHC_NIS_RBUFRDY;
-    }
-
-    /* Clear DAT line active status if that was the last block */
-    if ((s->trnmod & SDHC_TRNS_MULTI) == 0 ||
-            ((s->trnmod & SDHC_TRNS_MULTI) && s->blkcnt == 1)) {
-        s->prnsts &= ~SDHC_DAT_LINE_ACTIVE;
-    }
-
-    /* If stop at block gap request was set and it's not the last block of
-     * data - generate Block Event interrupt */
-    if (s->stopped_state == sdhc_gap_read && (s->trnmod & SDHC_TRNS_MULTI) &&
-            s->blkcnt != 1)    {
-        s->prnsts &= ~SDHC_DAT_LINE_ACTIVE;
-        if (s->norintstsen & SDHC_EISEN_BLKGAP) {
-            s->norintsts |= SDHC_EIS_BLKGAP;
-        }
-    }
-
-    sdhci_update_irq(s);
-}
-
-/* Read @size byte of data from host controller @s BUFFER DATA PORT register */
-static uint32_t sdhci_read_dataport(SDHCIState *s, unsigned size)
-{
-    uint32_t value = 0;
-    int i;
-
-    /* first check that a valid data exists in host controller input buffer */
-    if ((s->prnsts & SDHC_DATA_AVAILABLE) == 0) {
-        ERRPRINT("Trying to read from empty buffer\n");
-        return 0;
-    }
-
-    for (i = 0; i < size; i++) {
-        value |= s->fifo_buffer[s->data_count] << i * 8;
-        s->data_count++;
-        /* check if we've read all valid data (blksize bytes) from buffer */
-        if ((s->data_count) >= (s->blksize & 0x0fff)) {
-            DPRINT_L2("All %u bytes of data have been read from input buffer\n",
-                    s->data_count);
-            s->prnsts &= ~SDHC_DATA_AVAILABLE; /* no more data in a buffer */
-            s->data_count = 0;  /* next buff read must start at position [0] */
-
-            if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
-                s->blkcnt--;
-            }
-
-            /* if that was the last block of data */
-            if ((s->trnmod & SDHC_TRNS_MULTI) == 0 ||
-                ((s->trnmod & SDHC_TRNS_BLK_CNT_EN) && (s->blkcnt == 0)) ||
-                 /* stop at gap request */
-                (s->stopped_state == sdhc_gap_read &&
-                 !(s->prnsts & SDHC_DAT_LINE_ACTIVE))) {
-                SDHCI_GET_CLASS(s)->end_data_transfer(s);
-            } else { /* if there are more data, read next block from card */
-                SDHCI_GET_CLASS(s)->read_block_from_card(s);
-            }
-            break;
-        }
-    }
-
-    return value;
-}
-
-/* Write data from host controller FIFO to card */
-static void sdhci_write_block_to_card(SDHCIState *s)
-{
-    int index = 0;
-
-    if (s->prnsts & SDHC_SPACE_AVAILABLE) {
-        if (s->norintstsen & SDHC_NISEN_WBUFRDY) {
-            s->norintsts |= SDHC_NIS_WBUFRDY;
-        }
-        sdhci_update_irq(s);
-        return;
-    }
-
-    if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
-        if (s->blkcnt == 0) {
-            return;
-        } else {
-            s->blkcnt--;
-        }
-    }
-
-    for (index = 0; index < (s->blksize & 0x0fff); index++) {
-        sd_write_data(s->card, s->fifo_buffer[index]);
-    }
-
-    /* Next data can be written through BUFFER DATORT register */
-    s->prnsts |= SDHC_SPACE_AVAILABLE;
-    if (s->norintstsen & SDHC_NISEN_WBUFRDY) {
-        s->norintsts |= SDHC_NIS_WBUFRDY;
-    }
-
-    /* Finish transfer if that was the last block of data */
-    if ((s->trnmod & SDHC_TRNS_MULTI) == 0 ||
-            ((s->trnmod & SDHC_TRNS_MULTI) &&
-            (s->trnmod & SDHC_TRNS_BLK_CNT_EN) && (s->blkcnt == 0))) {
-        SDHCI_GET_CLASS(s)->end_data_transfer(s);
-    }
-
-    /* Generate Block Gap Event if requested and if not the last block */
-    if (s->stopped_state == sdhc_gap_write && (s->trnmod & SDHC_TRNS_MULTI) &&
-            s->blkcnt > 0) {
-        s->prnsts &= ~SDHC_DOING_WRITE;
-        if (s->norintstsen & SDHC_EISEN_BLKGAP) {
-            s->norintsts |= SDHC_EIS_BLKGAP;
-        }
-        SDHCI_GET_CLASS(s)->end_data_transfer(s);
-    }
-
-    sdhci_update_irq(s);
-}
-
-/* Write @size bytes of @value data to host controller @s Buffer Data Port
- * register */
-static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size)
-{
-    unsigned i;
-
-    /* Check that there is free space left in a buffer */
-    if (!(s->prnsts & SDHC_SPACE_AVAILABLE)) {
-        ERRPRINT("Can't write to data buffer: buffer full\n");
-        return;
-    }
-
-    for (i = 0; i < size; i++) {
-        s->fifo_buffer[s->data_count] = value & 0xFF;
-        s->data_count++;
-        value >>= 8;
-        if (s->data_count >= (s->blksize & 0x0fff)) {
-            DPRINT_L2("write buffer filled with %u bytes of data\n",
-                    s->data_count);
-            s->data_count = 0;
-            s->prnsts &= ~SDHC_SPACE_AVAILABLE;
-            if (s->prnsts & SDHC_DOING_WRITE) {
-                SDHCI_GET_CLASS(s)->write_block_to_card(s);
-            }
-        }
-    }
-}
-
-/*
- * Single DMA data transfer
- */
-
-/* Multi block SDMA transfer */
-static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
-{
-    bool page_aligned = false;
-    unsigned int n, begin;
-    const uint16_t block_size = s->blksize & 0x0fff;
-    uint32_t boundary_chk = 1 << (((s->blksize & 0xf000) >> 12) + 12);
-    uint32_t boundary_count = boundary_chk - (s->sdmasysad % boundary_chk);
-
-    /* XXX: Some sd/mmc drivers (for example, u-boot-slp) do not account for
-     * possible stop at page boundary if initial address is not page aligned,
-     * allow them to work properly */
-    if ((s->sdmasysad % boundary_chk) == 0) {
-        page_aligned = true;
-    }
-
-    if (s->trnmod & SDHC_TRNS_READ) {
-        s->prnsts |= SDHC_DOING_READ | SDHC_DATA_INHIBIT |
-                SDHC_DAT_LINE_ACTIVE;
-        while (s->blkcnt) {
-            if (s->data_count == 0) {
-                for (n = 0; n < block_size; n++) {
-                    s->fifo_buffer[n] = sd_read_data(s->card);
-                }
-            }
-            begin = s->data_count;
-            if (((boundary_count + begin) < block_size) && page_aligned) {
-                s->data_count = boundary_count + begin;
-                boundary_count = 0;
-             } else {
-                s->data_count = block_size;
-                boundary_count -= block_size - begin;
-                if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
-                    s->blkcnt--;
-                }
-            }
-            dma_memory_write(&dma_context_memory, s->sdmasysad,
-                             &s->fifo_buffer[begin], s->data_count - begin);
-            s->sdmasysad += s->data_count - begin;
-            if (s->data_count == block_size) {
-                s->data_count = 0;
-            }
-            if (page_aligned && boundary_count == 0) {
-                break;
-            }
-        }
-    } else {
-        s->prnsts |= SDHC_DOING_WRITE | SDHC_DATA_INHIBIT |
-                SDHC_DAT_LINE_ACTIVE;
-        while (s->blkcnt) {
-            begin = s->data_count;
-            if (((boundary_count + begin) < block_size) && page_aligned) {
-                s->data_count = boundary_count + begin;
-                boundary_count = 0;
-             } else {
-                s->data_count = block_size;
-                boundary_count -= block_size - begin;
-            }
-            dma_memory_read(&dma_context_memory, s->sdmasysad,
-                            &s->fifo_buffer[begin], s->data_count);
-            s->sdmasysad += s->data_count - begin;
-            if (s->data_count == block_size) {
-                for (n = 0; n < block_size; n++) {
-                    sd_write_data(s->card, s->fifo_buffer[n]);
-                }
-                s->data_count = 0;
-                if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
-                    s->blkcnt--;
-                }
-            }
-            if (page_aligned && boundary_count == 0) {
-                break;
-            }
-        }
-    }
-
-    if (s->blkcnt == 0) {
-        SDHCI_GET_CLASS(s)->end_data_transfer(s);
-    } else {
-        if (s->norintstsen & SDHC_NISEN_DMA) {
-            s->norintsts |= SDHC_NIS_DMA;
-        }
-        sdhci_update_irq(s);
-    }
-}
-
-/* single block SDMA transfer */
-
-static void sdhci_sdma_transfer_single_block(SDHCIState *s)
-{
-    int n;
-    uint32_t datacnt = s->blksize & 0x0fff;
-
-    if (s->trnmod & SDHC_TRNS_READ) {
-        for (n = 0; n < datacnt; n++) {
-            s->fifo_buffer[n] = sd_read_data(s->card);
-        }
-        dma_memory_write(&dma_context_memory, s->sdmasysad, s->fifo_buffer,
-                         datacnt);
-    } else {
-        dma_memory_read(&dma_context_memory, s->sdmasysad, s->fifo_buffer,
-                        datacnt);
-        for (n = 0; n < datacnt; n++) {
-            sd_write_data(s->card, s->fifo_buffer[n]);
-        }
-    }
-
-    if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
-        s->blkcnt--;
-    }
-
-    SDHCI_GET_CLASS(s)->end_data_transfer(s);
-}
-
-typedef struct ADMADescr {
-    hwaddr addr;
-    uint16_t length;
-    uint8_t attr;
-    uint8_t incr;
-} ADMADescr;
-
-static void get_adma_description(SDHCIState *s, ADMADescr *dscr)
-{
-    uint32_t adma1 = 0;
-    uint64_t adma2 = 0;
-    hwaddr entry_addr = (hwaddr)s->admasysaddr;
-    switch (SDHC_DMA_TYPE(s->hostctl)) {
-    case SDHC_CTRL_ADMA2_32:
-        dma_memory_read(&dma_context_memory, entry_addr, (uint8_t *)&adma2,
-                        sizeof(adma2));
-        adma2 = le64_to_cpu(adma2);
-        /* The spec does not specify endianness of descriptor table.
-         * We currently assume that it is LE.
-         */
-        dscr->addr = (hwaddr)extract64(adma2, 32, 32) & ~0x3ull;
-        dscr->length = (uint16_t)extract64(adma2, 16, 16);
-        dscr->attr = (uint8_t)extract64(adma2, 0, 7);
-        dscr->incr = 8;
-        break;
-    case SDHC_CTRL_ADMA1_32:
-        dma_memory_read(&dma_context_memory, entry_addr, (uint8_t *)&adma1,
-                        sizeof(adma1));
-        adma1 = le32_to_cpu(adma1);
-        dscr->addr = (hwaddr)(adma1 & 0xFFFFF000);
-        dscr->attr = (uint8_t)extract32(adma1, 0, 7);
-        dscr->incr = 4;
-        if ((dscr->attr & SDHC_ADMA_ATTR_ACT_MASK) == SDHC_ADMA_ATTR_SET_LEN) {
-            dscr->length = (uint16_t)extract32(adma1, 12, 16);
-        } else {
-            dscr->length = 4096;
-        }
-        break;
-    case SDHC_CTRL_ADMA2_64:
-        dma_memory_read(&dma_context_memory, entry_addr,
-                        (uint8_t *)(&dscr->attr), 1);
-        dma_memory_read(&dma_context_memory, entry_addr + 2,
-                        (uint8_t *)(&dscr->length), 2);
-        dscr->length = le16_to_cpu(dscr->length);
-        dma_memory_read(&dma_context_memory, entry_addr + 4,
-                        (uint8_t *)(&dscr->addr), 8);
-        dscr->attr = le64_to_cpu(dscr->attr);
-        dscr->attr &= 0xfffffff8;
-        dscr->incr = 12;
-        break;
-    }
-}
-
-/* Advanced DMA data transfer */
-
-static void sdhci_do_adma(SDHCIState *s)
-{
-    unsigned int n, begin, length;
-    const uint16_t block_size = s->blksize & 0x0fff;
-    ADMADescr dscr;
-    int i;
-
-    for (i = 0; i < SDHC_ADMA_DESCS_PER_DELAY; ++i) {
-        s->admaerr &= ~SDHC_ADMAERR_LENGTH_MISMATCH;
-
-        get_adma_description(s, &dscr);
-        DPRINT_L2("ADMA loop: addr=" TARGET_FMT_plx ", len=%d, attr=%x\n",
-                dscr.addr, dscr.length, dscr.attr);
-
-        if ((dscr.attr & SDHC_ADMA_ATTR_VALID) == 0) {
-            /* Indicate that error occurred in ST_FDS state */
-            s->admaerr &= ~SDHC_ADMAERR_STATE_MASK;
-            s->admaerr |= SDHC_ADMAERR_STATE_ST_FDS;
-
-            /* Generate ADMA error interrupt */
-            if (s->errintstsen & SDHC_EISEN_ADMAERR) {
-                s->errintsts |= SDHC_EIS_ADMAERR;
-                s->norintsts |= SDHC_NIS_ERR;
-            }
-
-            sdhci_update_irq(s);
-            return;
-        }
-
-        length = dscr.length ? dscr.length : 65536;
-
-        switch (dscr.attr & SDHC_ADMA_ATTR_ACT_MASK) {
-        case SDHC_ADMA_ATTR_ACT_TRAN:  /* data transfer */
-
-            if (s->trnmod & SDHC_TRNS_READ) {
-                while (length) {
-                    if (s->data_count == 0) {
-                        for (n = 0; n < block_size; n++) {
-                            s->fifo_buffer[n] = sd_read_data(s->card);
-                        }
-                    }
-                    begin = s->data_count;
-                    if ((length + begin) < block_size) {
-                        s->data_count = length + begin;
-                        length = 0;
-                     } else {
-                        s->data_count = block_size;
-                        length -= block_size - begin;
-                    }
-                    dma_memory_write(&dma_context_memory, dscr.addr,
-                                     &s->fifo_buffer[begin],
-                                     s->data_count - begin);
-                    dscr.addr += s->data_count - begin;
-                    if (s->data_count == block_size) {
-                        s->data_count = 0;
-                        if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
-                            s->blkcnt--;
-                            if (s->blkcnt == 0) {
-                                break;
-                            }
-                        }
-                    }
-                }
-            } else {
-                while (length) {
-                    begin = s->data_count;
-                    if ((length + begin) < block_size) {
-                        s->data_count = length + begin;
-                        length = 0;
-                     } else {
-                        s->data_count = block_size;
-                        length -= block_size - begin;
-                    }
-                    dma_memory_read(&dma_context_memory, dscr.addr,
-                                    &s->fifo_buffer[begin], s->data_count);
-                    dscr.addr += s->data_count - begin;
-                    if (s->data_count == block_size) {
-                        for (n = 0; n < block_size; n++) {
-                            sd_write_data(s->card, s->fifo_buffer[n]);
-                        }
-                        s->data_count = 0;
-                        if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) {
-                            s->blkcnt--;
-                            if (s->blkcnt == 0) {
-                                break;
-                            }
-                        }
-                    }
-                }
-            }
-            s->admasysaddr += dscr.incr;
-            break;
-        case SDHC_ADMA_ATTR_ACT_LINK:   /* link to next descriptor table */
-            s->admasysaddr = dscr.addr;
-            DPRINT_L1("ADMA link: admasysaddr=0x%lx\n", s->admasysaddr);
-            break;
-        default:
-            s->admasysaddr += dscr.incr;
-            break;
-        }
-
-        /* ADMA transfer terminates if blkcnt == 0 or by END attribute */
-        if (((s->trnmod & SDHC_TRNS_BLK_CNT_EN) &&
-                    (s->blkcnt == 0)) || (dscr.attr & SDHC_ADMA_ATTR_END)) {
-            DPRINT_L2("ADMA transfer completed\n");
-            if (length || ((dscr.attr & SDHC_ADMA_ATTR_END) &&
-                (s->trnmod & SDHC_TRNS_BLK_CNT_EN) &&
-                s->blkcnt != 0)) {
-                ERRPRINT("SD/MMC host ADMA length mismatch\n");
-                s->admaerr |= SDHC_ADMAERR_LENGTH_MISMATCH |
-                        SDHC_ADMAERR_STATE_ST_TFR;
-                if (s->errintstsen & SDHC_EISEN_ADMAERR) {
-                    ERRPRINT("Set ADMA error flag\n");
-                    s->errintsts |= SDHC_EIS_ADMAERR;
-                    s->norintsts |= SDHC_NIS_ERR;
-                }
-
-                sdhci_update_irq(s);
-            }
-            SDHCI_GET_CLASS(s)->end_data_transfer(s);
-            return;
-        }
-
-        if (dscr.attr & SDHC_ADMA_ATTR_INT) {
-            DPRINT_L1("ADMA interrupt: admasysaddr=0x%lx\n", s->admasysaddr);
-            if (s->norintstsen & SDHC_NISEN_DMA) {
-                s->norintsts |= SDHC_NIS_DMA;
-            }
-
-            sdhci_update_irq(s);
-            return;
-        }
-    }
-
-    /* we have unfinished business - reschedule to continue ADMA */
-    qemu_mod_timer(s->transfer_timer,
-                   qemu_get_clock_ns(vm_clock) + SDHC_TRANSFER_DELAY);
-}
-
-/* Perform data transfer according to controller configuration */
-
-static void sdhci_data_transfer(SDHCIState *s)
-{
-    SDHCIClass *k = SDHCI_GET_CLASS(s);
-    s->data_count = 0;
-
-    if (s->trnmod & SDHC_TRNS_DMA) {
-        switch (SDHC_DMA_TYPE(s->hostctl)) {
-        case SDHC_CTRL_SDMA:
-            if ((s->trnmod & SDHC_TRNS_MULTI) &&
-                    (!(s->trnmod & SDHC_TRNS_BLK_CNT_EN) || s->blkcnt == 0)) {
-                break;
-            }
-
-            if ((s->blkcnt == 1) || !(s->trnmod & SDHC_TRNS_MULTI)) {
-                k->do_sdma_single(s);
-            } else {
-                k->do_sdma_multi(s);
-            }
-
-            break;
-        case SDHC_CTRL_ADMA1_32:
-            if (!(s->capareg & SDHC_CAN_DO_ADMA1)) {
-                ERRPRINT("ADMA1 not supported\n");
-                break;
-            }
-
-            k->do_adma(s);
-            break;
-        case SDHC_CTRL_ADMA2_32:
-            if (!(s->capareg & SDHC_CAN_DO_ADMA2)) {
-                ERRPRINT("ADMA2 not supported\n");
-                break;
-            }
-
-            k->do_adma(s);
-            break;
-        case SDHC_CTRL_ADMA2_64:
-            if (!(s->capareg & SDHC_CAN_DO_ADMA2) ||
-                    !(s->capareg & SDHC_64_BIT_BUS_SUPPORT)) {
-                ERRPRINT("64 bit ADMA not supported\n");
-                break;
-            }
-
-            k->do_adma(s);
-            break;
-        default:
-            ERRPRINT("Unsupported DMA type\n");
-            break;
-        }
-    } else {
-        if ((s->trnmod & SDHC_TRNS_READ) && sd_data_ready(s->card)) {
-            s->prnsts |= SDHC_DOING_READ | SDHC_DATA_INHIBIT |
-                    SDHC_DAT_LINE_ACTIVE;
-            SDHCI_GET_CLASS(s)->read_block_from_card(s);
-        } else {
-            s->prnsts |= SDHC_DOING_WRITE | SDHC_DAT_LINE_ACTIVE |
-                    SDHC_SPACE_AVAILABLE | SDHC_DATA_INHIBIT;
-            SDHCI_GET_CLASS(s)->write_block_to_card(s);
-        }
-    }
-}
-
-static bool sdhci_can_issue_command(SDHCIState *s)
-{
-    if (!SDHC_CLOCK_IS_ON(s->clkcon) || !(s->pwrcon & SDHC_POWER_ON) ||
-        (((s->prnsts & SDHC_DATA_INHIBIT) || s->stopped_state) &&
-        ((s->cmdreg & SDHC_CMD_DATA_PRESENT) ||
-        ((s->cmdreg & SDHC_CMD_RESPONSE) == SDHC_CMD_RSP_WITH_BUSY &&
-        !(SDHC_COMMAND_TYPE(s->cmdreg) == SDHC_CMD_ABORT))))) {
-        return false;
-    }
-
-    return true;
-}
-
-/* The Buffer Data Port register must be accessed in sequential and
- * continuous manner */
-static inline bool
-sdhci_buff_access_is_sequential(SDHCIState *s, unsigned byte_num)
-{
-    if ((s->data_count & 0x3) != byte_num) {
-        ERRPRINT("Non-sequential access to Buffer Data Port register"
-                "is prohibited\n");
-        return false;
-    }
-    return true;
-}
-
-static uint32_t sdhci_read(SDHCIState *s, unsigned int offset, unsigned size)
-{
-    uint32_t ret = 0;
-
-    switch (offset & ~0x3) {
-    case SDHC_SYSAD:
-        ret = s->sdmasysad;
-        break;
-    case SDHC_BLKSIZE:
-        ret = s->blksize | (s->blkcnt << 16);
-        break;
-    case SDHC_ARGUMENT:
-        ret = s->argument;
-        break;
-    case SDHC_TRNMOD:
-        ret = s->trnmod | (s->cmdreg << 16);
-        break;
-    case SDHC_RSPREG0 ... SDHC_RSPREG3:
-        ret = s->rspreg[((offset & ~0x3) - SDHC_RSPREG0) >> 2];
-        break;
-    case  SDHC_BDATA:
-        if (sdhci_buff_access_is_sequential(s, offset - SDHC_BDATA)) {
-            ret = SDHCI_GET_CLASS(s)->bdata_read(s, size);
-            DPRINT_L2("read %ub: addr[0x%04x] -> %u\n", size, offset, ret);
-            return ret;
-        }
-        break;
-    case SDHC_PRNSTS:
-        ret = s->prnsts;
-        break;
-    case SDHC_HOSTCTL:
-        ret = s->hostctl | (s->pwrcon << 8) | (s->blkgap << 16) |
-              (s->wakcon << 24);
-        break;
-    case SDHC_CLKCON:
-        ret = s->clkcon | (s->timeoutcon << 16);
-        break;
-    case SDHC_NORINTSTS:
-        ret = s->norintsts | (s->errintsts << 16);
-        break;
-    case SDHC_NORINTSTSEN:
-        ret = s->norintstsen | (s->errintstsen << 16);
-        break;
-    case SDHC_NORINTSIGEN:
-        ret = s->norintsigen | (s->errintsigen << 16);
-        break;
-    case SDHC_ACMD12ERRSTS:
-        ret = s->acmd12errsts;
-        break;
-    case SDHC_CAPAREG:
-        ret = s->capareg;
-        break;
-    case SDHC_MAXCURR:
-        ret = s->maxcurr;
-        break;
-    case SDHC_ADMAERR:
-        ret =  s->admaerr;
-        break;
-    case SDHC_ADMASYSADDR:
-        ret = (uint32_t)s->admasysaddr;
-        break;
-    case SDHC_ADMASYSADDR + 4:
-        ret = (uint32_t)(s->admasysaddr >> 32);
-        break;
-    case SDHC_SLOT_INT_STATUS:
-        ret = (SD_HOST_SPECv2_VERS << 16) | sdhci_slotint(s);
-        break;
-    default:
-        ERRPRINT("bad %ub read: addr[0x%04x]\n", size, offset);
-        break;
-    }
-
-    ret >>= (offset & 0x3) * 8;
-    ret &= (1ULL << (size * 8)) - 1;
-    DPRINT_L2("read %ub: addr[0x%04x] -> %u(0x%x)\n", size, offset, ret, ret);
-    return ret;
-}
-
-static inline void sdhci_blkgap_write(SDHCIState *s, uint8_t value)
-{
-    if ((value & SDHC_STOP_AT_GAP_REQ) && (s->blkgap & SDHC_STOP_AT_GAP_REQ)) {
-        return;
-    }
-    s->blkgap = value & SDHC_STOP_AT_GAP_REQ;
-
-    if ((value & SDHC_CONTINUE_REQ) && s->stopped_state &&
-            (s->blkgap & SDHC_STOP_AT_GAP_REQ) == 0) {
-        if (s->stopped_state == sdhc_gap_read) {
-            s->prnsts |= SDHC_DAT_LINE_ACTIVE | SDHC_DOING_READ;
-            SDHCI_GET_CLASS(s)->read_block_from_card(s);
-        } else {
-            s->prnsts |= SDHC_DAT_LINE_ACTIVE | SDHC_DOING_WRITE;
-            SDHCI_GET_CLASS(s)->write_block_to_card(s);
-        }
-        s->stopped_state = sdhc_not_stopped;
-    } else if (!s->stopped_state && (value & SDHC_STOP_AT_GAP_REQ)) {
-        if (s->prnsts & SDHC_DOING_READ) {
-            s->stopped_state = sdhc_gap_read;
-        } else if (s->prnsts & SDHC_DOING_WRITE) {
-            s->stopped_state = sdhc_gap_write;
-        }
-    }
-}
-
-static inline void sdhci_reset_write(SDHCIState *s, uint8_t value)
-{
-    switch (value) {
-    case SDHC_RESET_ALL:
-        DEVICE_GET_CLASS(s)->reset(DEVICE(s));
-        break;
-    case SDHC_RESET_CMD:
-        s->prnsts &= ~SDHC_CMD_INHIBIT;
-        s->norintsts &= ~SDHC_NIS_CMDCMP;
-        break;
-    case SDHC_RESET_DATA:
-        s->data_count = 0;
-        s->prnsts &= ~(SDHC_SPACE_AVAILABLE | SDHC_DATA_AVAILABLE |
-                SDHC_DOING_READ | SDHC_DOING_WRITE |
-                SDHC_DATA_INHIBIT | SDHC_DAT_LINE_ACTIVE);
-        s->blkgap &= ~(SDHC_STOP_AT_GAP_REQ | SDHC_CONTINUE_REQ);
-        s->stopped_state = sdhc_not_stopped;
-        s->norintsts &= ~(SDHC_NIS_WBUFRDY | SDHC_NIS_RBUFRDY |
-                SDHC_NIS_DMA | SDHC_NIS_TRSCMP | SDHC_NIS_BLKGAP);
-        break;
-    }
-}
-
-static void
-sdhci_write(SDHCIState *s, unsigned int offset, uint32_t value, unsigned size)
-{
-    unsigned shift =  8 * (offset & 0x3);
-    uint32_t mask = ~(((1ULL << (size * 8)) - 1) << shift);
-    value <<= shift;
-
-    switch (offset & ~0x3) {
-    case SDHC_SYSAD:
-        s->sdmasysad = (s->sdmasysad & mask) | value;
-        MASKED_WRITE(s->sdmasysad, mask, value);
-        /* Writing to last byte of sdmasysad might trigger transfer */
-        if (!(mask & 0xFF000000) && TRANSFERRING_DATA(s->prnsts) && s->blkcnt &&
-                s->blksize && SDHC_DMA_TYPE(s->hostctl) == SDHC_CTRL_SDMA) {
-            SDHCI_GET_CLASS(s)->do_sdma_multi(s);
-        }
-        break;
-    case SDHC_BLKSIZE:
-        if (!TRANSFERRING_DATA(s->prnsts)) {
-            MASKED_WRITE(s->blksize, mask, value);
-            MASKED_WRITE(s->blkcnt, mask >> 16, value >> 16);
-        }
-        break;
-    case SDHC_ARGUMENT:
-        MASKED_WRITE(s->argument, mask, value);
-        break;
-    case SDHC_TRNMOD:
-        /* DMA can be enabled only if it is supported as indicated by
-         * capabilities register */
-        if (!(s->capareg & SDHC_CAN_DO_DMA)) {
-            value &= ~SDHC_TRNS_DMA;
-        }
-        MASKED_WRITE(s->trnmod, mask, value);
-        MASKED_WRITE(s->cmdreg, mask >> 16, value >> 16);
-
-        /* Writing to the upper byte of CMDREG triggers SD command generation */
-        if ((mask & 0xFF000000) || !SDHCI_GET_CLASS(s)->can_issue_command(s)) {
-            break;
-        }
-
-        SDHCI_GET_CLASS(s)->send_command(s);
-        break;
-    case  SDHC_BDATA:
-        if (sdhci_buff_access_is_sequential(s, offset - SDHC_BDATA)) {
-            SDHCI_GET_CLASS(s)->bdata_write(s, value >> shift, size);
-        }
-        break;
-    case SDHC_HOSTCTL:
-        if (!(mask & 0xFF0000)) {
-            sdhci_blkgap_write(s, value >> 16);
-        }
-        MASKED_WRITE(s->hostctl, mask, value);
-        MASKED_WRITE(s->pwrcon, mask >> 8, value >> 8);
-        MASKED_WRITE(s->wakcon, mask >> 24, value >> 24);
-        if (!(s->prnsts & SDHC_CARD_PRESENT) || ((s->pwrcon >> 1) & 0x7) < 5 ||
-                !(s->capareg & (1 << (31 - ((s->pwrcon >> 1) & 0x7))))) {
-            s->pwrcon &= ~SDHC_POWER_ON;
-        }
-        break;
-    case SDHC_CLKCON:
-        if (!(mask & 0xFF000000)) {
-            sdhci_reset_write(s, value >> 24);
-        }
-        MASKED_WRITE(s->clkcon, mask, value);
-        MASKED_WRITE(s->timeoutcon, mask >> 16, value >> 16);
-        if (s->clkcon & SDHC_CLOCK_INT_EN) {
-            s->clkcon |= SDHC_CLOCK_INT_STABLE;
-        } else {
-            s->clkcon &= ~SDHC_CLOCK_INT_STABLE;
-        }
-        break;
-    case SDHC_NORINTSTS:
-        if (s->norintstsen & SDHC_NISEN_CARDINT) {
-            value &= ~SDHC_NIS_CARDINT;
-        }
-        s->norintsts &= mask | ~value;
-        s->errintsts &= (mask >> 16) | ~(value >> 16);
-        if (s->errintsts) {
-            s->norintsts |= SDHC_NIS_ERR;
-        } else {
-            s->norintsts &= ~SDHC_NIS_ERR;
-        }
-        sdhci_update_irq(s);
-        break;
-    case SDHC_NORINTSTSEN:
-        MASKED_WRITE(s->norintstsen, mask, value);
-        MASKED_WRITE(s->errintstsen, mask >> 16, value >> 16);
-        s->norintsts &= s->norintstsen;
-        s->errintsts &= s->errintstsen;
-        if (s->errintsts) {
-            s->norintsts |= SDHC_NIS_ERR;
-        } else {
-            s->norintsts &= ~SDHC_NIS_ERR;
-        }
-        sdhci_update_irq(s);
-        break;
-    case SDHC_NORINTSIGEN:
-        MASKED_WRITE(s->norintsigen, mask, value);
-        MASKED_WRITE(s->errintsigen, mask >> 16, value >> 16);
-        sdhci_update_irq(s);
-        break;
-    case SDHC_ADMAERR:
-        MASKED_WRITE(s->admaerr, mask, value);
-        break;
-    case SDHC_ADMASYSADDR:
-        s->admasysaddr = (s->admasysaddr & (0xFFFFFFFF00000000ULL |
-                (uint64_t)mask)) | (uint64_t)value;
-        break;
-    case SDHC_ADMASYSADDR + 4:
-        s->admasysaddr = (s->admasysaddr & (0x00000000FFFFFFFFULL |
-                ((uint64_t)mask << 32))) | ((uint64_t)value << 32);
-        break;
-    case SDHC_FEAER:
-        s->acmd12errsts |= value;
-        s->errintsts |= (value >> 16) & s->errintstsen;
-        if (s->acmd12errsts) {
-            s->errintsts |= SDHC_EIS_CMD12ERR;
-        }
-        if (s->errintsts) {
-            s->norintsts |= SDHC_NIS_ERR;
-        }
-        sdhci_update_irq(s);
-        break;
-    default:
-        ERRPRINT("bad %ub write offset: addr[0x%04x] <- %u(0x%x)\n",
-                size, offset, value >> shift, value >> shift);
-        break;
-    }
-    DPRINT_L2("write %ub: addr[0x%04x] <- %u(0x%x)\n",
-            size, offset, value >> shift, value >> shift);
-}
-
-static uint64_t
-sdhci_readfn(void *opaque, hwaddr offset, unsigned size)
-{
-    SDHCIState *s = (SDHCIState *)opaque;
-
-    return SDHCI_GET_CLASS(s)->mem_read(s, offset, size);
-}
-
-static void
-sdhci_writefn(void *opaque, hwaddr off, uint64_t val, unsigned sz)
-{
-    SDHCIState *s = (SDHCIState *)opaque;
-
-    SDHCI_GET_CLASS(s)->mem_write(s, off, val, sz);
-}
-
-static const MemoryRegionOps sdhci_mmio_ops = {
-    .read = sdhci_readfn,
-    .write = sdhci_writefn,
-    .valid = {
-        .min_access_size = 1,
-        .max_access_size = 4,
-        .unaligned = false
-    },
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static inline unsigned int sdhci_get_fifolen(SDHCIState *s)
-{
-    switch (SDHC_CAPAB_BLOCKSIZE(s->capareg)) {
-    case 0:
-        return 512;
-    case 1:
-        return 1024;
-    case 2:
-        return 2048;
-    default:
-        hw_error("SDHC: unsupported value for maximum block size\n");
-        return 0;
-    }
-}
-
-static void sdhci_initfn(Object *obj)
-{
-    SDHCIState *s = SDHCI(obj);
-    DriveInfo *di;
-
-    di = drive_get_next(IF_SD);
-    s->card = sd_init(di ? di->bdrv : NULL, 0);
-    s->eject_cb = qemu_allocate_irqs(sdhci_insert_eject_cb, s, 1)[0];
-    s->ro_cb = qemu_allocate_irqs(sdhci_card_readonly_cb, s, 1)[0];
-    sd_set_cb(s->card, s->ro_cb, s->eject_cb);
-
-    s->insert_timer = qemu_new_timer_ns(vm_clock, sdhci_raise_insertion_irq, s);
-    s->transfer_timer = qemu_new_timer_ns(vm_clock, sdhci_do_data_transfer, s);
-}
-
-static void sdhci_uninitfn(Object *obj)
-{
-    SDHCIState *s = SDHCI(obj);
-
-    qemu_del_timer(s->insert_timer);
-    qemu_free_timer(s->insert_timer);
-    qemu_del_timer(s->transfer_timer);
-    qemu_free_timer(s->transfer_timer);
-    qemu_free_irqs(&s->eject_cb);
-    qemu_free_irqs(&s->ro_cb);
-
-    if (s->fifo_buffer) {
-        g_free(s->fifo_buffer);
-        s->fifo_buffer = NULL;
-    }
-}
-
-const VMStateDescription sdhci_vmstate = {
-    .name = "sdhci",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT32(sdmasysad, SDHCIState),
-        VMSTATE_UINT16(blksize, SDHCIState),
-        VMSTATE_UINT16(blkcnt, SDHCIState),
-        VMSTATE_UINT32(argument, SDHCIState),
-        VMSTATE_UINT16(trnmod, SDHCIState),
-        VMSTATE_UINT16(cmdreg, SDHCIState),
-        VMSTATE_UINT32_ARRAY(rspreg, SDHCIState, 4),
-        VMSTATE_UINT32(prnsts, SDHCIState),
-        VMSTATE_UINT8(hostctl, SDHCIState),
-        VMSTATE_UINT8(pwrcon, SDHCIState),
-        VMSTATE_UINT8(blkgap, SDHCIState),
-        VMSTATE_UINT8(wakcon, SDHCIState),
-        VMSTATE_UINT16(clkcon, SDHCIState),
-        VMSTATE_UINT8(timeoutcon, SDHCIState),
-        VMSTATE_UINT8(admaerr, SDHCIState),
-        VMSTATE_UINT16(norintsts, SDHCIState),
-        VMSTATE_UINT16(errintsts, SDHCIState),
-        VMSTATE_UINT16(norintstsen, SDHCIState),
-        VMSTATE_UINT16(errintstsen, SDHCIState),
-        VMSTATE_UINT16(norintsigen, SDHCIState),
-        VMSTATE_UINT16(errintsigen, SDHCIState),
-        VMSTATE_UINT16(acmd12errsts, SDHCIState),
-        VMSTATE_UINT16(data_count, SDHCIState),
-        VMSTATE_UINT64(admasysaddr, SDHCIState),
-        VMSTATE_UINT8(stopped_state, SDHCIState),
-        VMSTATE_VBUFFER_UINT32(fifo_buffer, SDHCIState, 1, NULL, 0, buf_maxsz),
-        VMSTATE_TIMER(insert_timer, SDHCIState),
-        VMSTATE_TIMER(transfer_timer, SDHCIState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-/* Capabilities registers provide information on supported features of this
- * specific host controller implementation */
-static Property sdhci_properties[] = {
-    DEFINE_PROP_HEX32("capareg", SDHCIState, capareg,
-            SDHC_CAPAB_REG_DEFAULT),
-    DEFINE_PROP_HEX32("maxcurr", SDHCIState, maxcurr, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void sdhci_realize(DeviceState *dev, Error ** errp)
-{
-    SDHCIState *s = SDHCI(dev);
-    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
-
-    s->buf_maxsz = sdhci_get_fifolen(s);
-    s->fifo_buffer = g_malloc0(s->buf_maxsz);
-    sysbus_init_irq(sbd, &s->irq);
-    memory_region_init_io(&s->iomem, &sdhci_mmio_ops, s, "sdhci",
-            SDHC_REGISTERS_MAP_SIZE);
-    sysbus_init_mmio(sbd, &s->iomem);
-}
-
-static void sdhci_generic_reset(DeviceState *ds)
-{
-    SDHCIState *s = SDHCI(ds);
-    SDHCI_GET_CLASS(s)->reset(s);
-}
-
-static void sdhci_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SDHCIClass *k = SDHCI_CLASS(klass);
-
-    dc->vmsd = &sdhci_vmstate;
-    dc->props = sdhci_properties;
-    dc->reset = sdhci_generic_reset;
-    dc->realize = sdhci_realize;
-
-    k->reset = sdhci_reset;
-    k->mem_read = sdhci_read;
-    k->mem_write = sdhci_write;
-    k->send_command = sdhci_send_command;
-    k->can_issue_command = sdhci_can_issue_command;
-    k->data_transfer = sdhci_data_transfer;
-    k->end_data_transfer = sdhci_end_transfer;
-    k->do_sdma_single = sdhci_sdma_transfer_single_block;
-    k->do_sdma_multi = sdhci_sdma_transfer_multi_blocks;
-    k->do_adma = sdhci_do_adma;
-    k->read_block_from_card = sdhci_read_block_from_card;
-    k->write_block_to_card = sdhci_write_block_to_card;
-    k->bdata_read = sdhci_read_dataport;
-    k->bdata_write = sdhci_write_dataport;
-}
-
-static const TypeInfo sdhci_type_info = {
-    .name = TYPE_SDHCI,
-    .parent = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(SDHCIState),
-    .instance_init = sdhci_initfn,
-    .instance_finalize = sdhci_uninitfn,
-    .class_init = sdhci_class_init,
-    .class_size = sizeof(SDHCIClass)
-};
-
-static void sdhci_register_types(void)
-{
-    type_register_static(&sdhci_type_info);
-}
-
-type_init(sdhci_register_types)
diff --git a/hw/serial-isa.c b/hw/serial-isa.c
deleted file mode 100644 (file)
index ed140d0..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * QEMU 16550A UART emulation
- *
- * Copyright (c) 2003-2004 Fabrice Bellard
- * Copyright (c) 2008 Citrix Systems, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/char/serial.h"
-#include "hw/isa/isa.h"
-
-typedef struct ISASerialState {
-    ISADevice dev;
-    uint32_t index;
-    uint32_t iobase;
-    uint32_t isairq;
-    SerialState state;
-} ISASerialState;
-
-static const int isa_serial_io[MAX_SERIAL_PORTS] = {
-    0x3f8, 0x2f8, 0x3e8, 0x2e8
-};
-static const int isa_serial_irq[MAX_SERIAL_PORTS] = {
-    4, 3, 4, 3
-};
-
-static int serial_isa_initfn(ISADevice *dev)
-{
-    static int index;
-    ISASerialState *isa = DO_UPCAST(ISASerialState, dev, dev);
-    SerialState *s = &isa->state;
-
-    if (isa->index == -1) {
-        isa->index = index;
-    }
-    if (isa->index >= MAX_SERIAL_PORTS) {
-        return -1;
-    }
-    if (isa->iobase == -1) {
-        isa->iobase = isa_serial_io[isa->index];
-    }
-    if (isa->isairq == -1) {
-        isa->isairq = isa_serial_irq[isa->index];
-    }
-    index++;
-
-    s->baudbase = 115200;
-    isa_init_irq(dev, &s->irq, isa->isairq);
-    serial_init_core(s);
-    qdev_set_legacy_instance_id(&dev->qdev, isa->iobase, 3);
-
-    memory_region_init_io(&s->io, &serial_io_ops, s, "serial", 8);
-    isa_register_ioport(dev, &s->io, isa->iobase);
-    return 0;
-}
-
-static const VMStateDescription vmstate_isa_serial = {
-    .name = "serial",
-    .version_id = 3,
-    .minimum_version_id = 2,
-    .fields = (VMStateField[]) {
-        VMSTATE_STRUCT(state, ISASerialState, 0, vmstate_serial, SerialState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property serial_isa_properties[] = {
-    DEFINE_PROP_UINT32("index",  ISASerialState, index,   -1),
-    DEFINE_PROP_HEX32("iobase",  ISASerialState, iobase,  -1),
-    DEFINE_PROP_UINT32("irq",    ISASerialState, isairq,  -1),
-    DEFINE_PROP_CHR("chardev",   ISASerialState, state.chr),
-    DEFINE_PROP_UINT32("wakeup", ISASerialState, state.wakeup, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void serial_isa_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
-    ic->init = serial_isa_initfn;
-    dc->vmsd = &vmstate_isa_serial;
-    dc->props = serial_isa_properties;
-}
-
-static const TypeInfo serial_isa_info = {
-    .name          = "isa-serial",
-    .parent        = TYPE_ISA_DEVICE,
-    .instance_size = sizeof(ISASerialState),
-    .class_init    = serial_isa_class_initfn,
-};
-
-static void serial_register_types(void)
-{
-    type_register_static(&serial_isa_info);
-}
-
-type_init(serial_register_types)
-
-bool serial_isa_init(ISABus *bus, int index, CharDriverState *chr)
-{
-    ISADevice *dev;
-
-    dev = isa_try_create(bus, "isa-serial");
-    if (!dev) {
-        return false;
-    }
-    qdev_prop_set_uint32(&dev->qdev, "index", index);
-    qdev_prop_set_chr(&dev->qdev, "chardev", chr);
-    if (qdev_init(&dev->qdev) < 0) {
-        return false;
-    }
-    return true;
-}
diff --git a/hw/serial-pci.c b/hw/serial-pci.c
deleted file mode 100644 (file)
index 2138e35..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * QEMU 16550A UART emulation
- *
- * Copyright (c) 2003-2004 Fabrice Bellard
- * Copyright (c) 2008 Citrix Systems, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-/* see docs/specs/pci-serial.txt */
-
-#include "hw/char/serial.h"
-#include "hw/pci/pci.h"
-
-#define PCI_SERIAL_MAX_PORTS 4
-
-typedef struct PCISerialState {
-    PCIDevice dev;
-    SerialState state;
-} PCISerialState;
-
-typedef struct PCIMultiSerialState {
-    PCIDevice    dev;
-    MemoryRegion iobar;
-    uint32_t     ports;
-    char         *name[PCI_SERIAL_MAX_PORTS];
-    SerialState  state[PCI_SERIAL_MAX_PORTS];
-    uint32_t     level[PCI_SERIAL_MAX_PORTS];
-    qemu_irq     *irqs;
-} PCIMultiSerialState;
-
-static int serial_pci_init(PCIDevice *dev)
-{
-    PCISerialState *pci = DO_UPCAST(PCISerialState, dev, dev);
-    SerialState *s = &pci->state;
-
-    s->baudbase = 115200;
-    serial_init_core(s);
-
-    pci->dev.config[PCI_INTERRUPT_PIN] = 0x01;
-    s->irq = pci->dev.irq[0];
-
-    memory_region_init_io(&s->io, &serial_io_ops, s, "serial", 8);
-    pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io);
-    return 0;
-}
-
-static void multi_serial_irq_mux(void *opaque, int n, int level)
-{
-    PCIMultiSerialState *pci = opaque;
-    int i, pending = 0;
-
-    pci->level[n] = level;
-    for (i = 0; i < pci->ports; i++) {
-        if (pci->level[i]) {
-            pending = 1;
-        }
-    }
-    qemu_set_irq(pci->dev.irq[0], pending);
-}
-
-static int multi_serial_pci_init(PCIDevice *dev)
-{
-    PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
-    PCIMultiSerialState *pci = DO_UPCAST(PCIMultiSerialState, dev, dev);
-    SerialState *s;
-    int i;
-
-    switch (pc->device_id) {
-    case 0x0003:
-        pci->ports = 2;
-        break;
-    case 0x0004:
-        pci->ports = 4;
-        break;
-    }
-    assert(pci->ports > 0);
-    assert(pci->ports <= PCI_SERIAL_MAX_PORTS);
-
-    pci->dev.config[PCI_INTERRUPT_PIN] = 0x01;
-    memory_region_init(&pci->iobar, "multiserial", 8 * pci->ports);
-    pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->iobar);
-    pci->irqs = qemu_allocate_irqs(multi_serial_irq_mux, pci,
-                                   pci->ports);
-
-    for (i = 0; i < pci->ports; i++) {
-        s = pci->state + i;
-        s->baudbase = 115200;
-        serial_init_core(s);
-        s->irq = pci->irqs[i];
-        pci->name[i] = g_strdup_printf("uart #%d", i+1);
-        memory_region_init_io(&s->io, &serial_io_ops, s, pci->name[i], 8);
-        memory_region_add_subregion(&pci->iobar, 8 * i, &s->io);
-    }
-    return 0;
-}
-
-static void serial_pci_exit(PCIDevice *dev)
-{
-    PCISerialState *pci = DO_UPCAST(PCISerialState, dev, dev);
-    SerialState *s = &pci->state;
-
-    serial_exit_core(s);
-    memory_region_destroy(&s->io);
-}
-
-static void multi_serial_pci_exit(PCIDevice *dev)
-{
-    PCIMultiSerialState *pci = DO_UPCAST(PCIMultiSerialState, dev, dev);
-    SerialState *s;
-    int i;
-
-    for (i = 0; i < pci->ports; i++) {
-        s = pci->state + i;
-        serial_exit_core(s);
-        memory_region_destroy(&s->io);
-        g_free(pci->name[i]);
-    }
-    memory_region_destroy(&pci->iobar);
-    qemu_free_irqs(pci->irqs);
-}
-
-static const VMStateDescription vmstate_pci_serial = {
-    .name = "pci-serial",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_PCI_DEVICE(dev, PCISerialState),
-        VMSTATE_STRUCT(state, PCISerialState, 0, vmstate_serial, SerialState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_pci_multi_serial = {
-    .name = "pci-serial-multi",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_PCI_DEVICE(dev, PCIMultiSerialState),
-        VMSTATE_STRUCT_ARRAY(state, PCIMultiSerialState, PCI_SERIAL_MAX_PORTS,
-                             0, vmstate_serial, SerialState),
-        VMSTATE_UINT32_ARRAY(level, PCIMultiSerialState, PCI_SERIAL_MAX_PORTS),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property serial_pci_properties[] = {
-    DEFINE_PROP_CHR("chardev",  PCISerialState, state.chr),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static Property multi_2x_serial_pci_properties[] = {
-    DEFINE_PROP_CHR("chardev1",  PCIMultiSerialState, state[0].chr),
-    DEFINE_PROP_CHR("chardev2",  PCIMultiSerialState, state[1].chr),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static Property multi_4x_serial_pci_properties[] = {
-    DEFINE_PROP_CHR("chardev1",  PCIMultiSerialState, state[0].chr),
-    DEFINE_PROP_CHR("chardev2",  PCIMultiSerialState, state[1].chr),
-    DEFINE_PROP_CHR("chardev3",  PCIMultiSerialState, state[2].chr),
-    DEFINE_PROP_CHR("chardev4",  PCIMultiSerialState, state[3].chr),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void serial_pci_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
-    pc->init = serial_pci_init;
-    pc->exit = serial_pci_exit;
-    pc->vendor_id = PCI_VENDOR_ID_REDHAT;
-    pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL;
-    pc->revision = 1;
-    pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL;
-    dc->vmsd = &vmstate_pci_serial;
-    dc->props = serial_pci_properties;
-}
-
-static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
-    pc->init = multi_serial_pci_init;
-    pc->exit = multi_serial_pci_exit;
-    pc->vendor_id = PCI_VENDOR_ID_REDHAT;
-    pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL2;
-    pc->revision = 1;
-    pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL;
-    dc->vmsd = &vmstate_pci_multi_serial;
-    dc->props = multi_2x_serial_pci_properties;
-}
-
-static void multi_4x_serial_pci_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
-    pc->init = multi_serial_pci_init;
-    pc->exit = multi_serial_pci_exit;
-    pc->vendor_id = PCI_VENDOR_ID_REDHAT;
-    pc->device_id = PCI_DEVICE_ID_REDHAT_SERIAL4;
-    pc->revision = 1;
-    pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL;
-    dc->vmsd = &vmstate_pci_multi_serial;
-    dc->props = multi_4x_serial_pci_properties;
-}
-
-static const TypeInfo serial_pci_info = {
-    .name          = "pci-serial",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCISerialState),
-    .class_init    = serial_pci_class_initfn,
-};
-
-static const TypeInfo multi_2x_serial_pci_info = {
-    .name          = "pci-serial-2x",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIMultiSerialState),
-    .class_init    = multi_2x_serial_pci_class_initfn,
-};
-
-static const TypeInfo multi_4x_serial_pci_info = {
-    .name          = "pci-serial-4x",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIMultiSerialState),
-    .class_init    = multi_4x_serial_pci_class_initfn,
-};
-
-static void serial_pci_register_types(void)
-{
-    type_register_static(&serial_pci_info);
-    type_register_static(&multi_2x_serial_pci_info);
-    type_register_static(&multi_4x_serial_pci_info);
-}
-
-type_init(serial_pci_register_types)
diff --git a/hw/serial.c b/hw/serial.c
deleted file mode 100644 (file)
index 1151bf1..0000000
+++ /dev/null
@@ -1,789 +0,0 @@
-/*
- * QEMU 16550A UART emulation
- *
- * Copyright (c) 2003-2004 Fabrice Bellard
- * Copyright (c) 2008 Citrix Systems, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/char/serial.h"
-#include "char/char.h"
-#include "qemu/timer.h"
-#include "exec/address-spaces.h"
-
-//#define DEBUG_SERIAL
-
-#define UART_LCR_DLAB  0x80    /* Divisor latch access bit */
-
-#define UART_IER_MSI   0x08    /* Enable Modem status interrupt */
-#define UART_IER_RLSI  0x04    /* Enable receiver line status interrupt */
-#define UART_IER_THRI  0x02    /* Enable Transmitter holding register int. */
-#define UART_IER_RDI   0x01    /* Enable receiver data interrupt */
-
-#define UART_IIR_NO_INT        0x01    /* No interrupts pending */
-#define UART_IIR_ID    0x06    /* Mask for the interrupt ID */
-
-#define UART_IIR_MSI   0x00    /* Modem status interrupt */
-#define UART_IIR_THRI  0x02    /* Transmitter holding register empty */
-#define UART_IIR_RDI   0x04    /* Receiver data interrupt */
-#define UART_IIR_RLSI  0x06    /* Receiver line status interrupt */
-#define UART_IIR_CTI    0x0C    /* Character Timeout Indication */
-
-#define UART_IIR_FENF   0x80    /* Fifo enabled, but not functionning */
-#define UART_IIR_FE     0xC0    /* Fifo enabled */
-
-/*
- * These are the definitions for the Modem Control Register
- */
-#define UART_MCR_LOOP  0x10    /* Enable loopback test mode */
-#define UART_MCR_OUT2  0x08    /* Out2 complement */
-#define UART_MCR_OUT1  0x04    /* Out1 complement */
-#define UART_MCR_RTS   0x02    /* RTS complement */
-#define UART_MCR_DTR   0x01    /* DTR complement */
-
-/*
- * These are the definitions for the Modem Status Register
- */
-#define UART_MSR_DCD   0x80    /* Data Carrier Detect */
-#define UART_MSR_RI    0x40    /* Ring Indicator */
-#define UART_MSR_DSR   0x20    /* Data Set Ready */
-#define UART_MSR_CTS   0x10    /* Clear to Send */
-#define UART_MSR_DDCD  0x08    /* Delta DCD */
-#define UART_MSR_TERI  0x04    /* Trailing edge ring indicator */
-#define UART_MSR_DDSR  0x02    /* Delta DSR */
-#define UART_MSR_DCTS  0x01    /* Delta CTS */
-#define UART_MSR_ANY_DELTA 0x0F        /* Any of the delta bits! */
-
-#define UART_LSR_TEMT  0x40    /* Transmitter empty */
-#define UART_LSR_THRE  0x20    /* Transmit-hold-register empty */
-#define UART_LSR_BI    0x10    /* Break interrupt indicator */
-#define UART_LSR_FE    0x08    /* Frame error indicator */
-#define UART_LSR_PE    0x04    /* Parity error indicator */
-#define UART_LSR_OE    0x02    /* Overrun error indicator */
-#define UART_LSR_DR    0x01    /* Receiver data ready */
-#define UART_LSR_INT_ANY 0x1E  /* Any of the lsr-interrupt-triggering status bits */
-
-/* Interrupt trigger levels. The byte-counts are for 16550A - in newer UARTs the byte-count for each ITL is higher. */
-
-#define UART_FCR_ITL_1      0x00 /* 1 byte ITL */
-#define UART_FCR_ITL_2      0x40 /* 4 bytes ITL */
-#define UART_FCR_ITL_3      0x80 /* 8 bytes ITL */
-#define UART_FCR_ITL_4      0xC0 /* 14 bytes ITL */
-
-#define UART_FCR_DMS        0x08    /* DMA Mode Select */
-#define UART_FCR_XFR        0x04    /* XMIT Fifo Reset */
-#define UART_FCR_RFR        0x02    /* RCVR Fifo Reset */
-#define UART_FCR_FE         0x01    /* FIFO Enable */
-
-#define XMIT_FIFO           0
-#define RECV_FIFO           1
-#define MAX_XMIT_RETRY      4
-
-#ifdef DEBUG_SERIAL
-#define DPRINTF(fmt, ...) \
-do { fprintf(stderr, "serial: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) \
-do {} while (0)
-#endif
-
-static void serial_receive1(void *opaque, const uint8_t *buf, int size);
-
-static void fifo_clear(SerialState *s, int fifo)
-{
-    SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo;
-    memset(f->data, 0, UART_FIFO_LENGTH);
-    f->count = 0;
-    f->head = 0;
-    f->tail = 0;
-}
-
-static int fifo_put(SerialState *s, int fifo, uint8_t chr)
-{
-    SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo;
-
-    /* Receive overruns do not overwrite FIFO contents. */
-    if (fifo == XMIT_FIFO || f->count < UART_FIFO_LENGTH) {
-
-        f->data[f->head++] = chr;
-
-        if (f->head == UART_FIFO_LENGTH)
-            f->head = 0;
-    }
-
-    if (f->count < UART_FIFO_LENGTH)
-        f->count++;
-    else if (fifo == RECV_FIFO)
-        s->lsr |= UART_LSR_OE;
-
-    return 1;
-}
-
-static uint8_t fifo_get(SerialState *s, int fifo)
-{
-    SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo;
-    uint8_t c;
-
-    if(f->count == 0)
-        return 0;
-
-    c = f->data[f->tail++];
-    if (f->tail == UART_FIFO_LENGTH)
-        f->tail = 0;
-    f->count--;
-
-    return c;
-}
-
-static void serial_update_irq(SerialState *s)
-{
-    uint8_t tmp_iir = UART_IIR_NO_INT;
-
-    if ((s->ier & UART_IER_RLSI) && (s->lsr & UART_LSR_INT_ANY)) {
-        tmp_iir = UART_IIR_RLSI;
-    } else if ((s->ier & UART_IER_RDI) && s->timeout_ipending) {
-        /* Note that(s->ier & UART_IER_RDI) can mask this interrupt,
-         * this is not in the specification but is observed on existing
-         * hardware.  */
-        tmp_iir = UART_IIR_CTI;
-    } else if ((s->ier & UART_IER_RDI) && (s->lsr & UART_LSR_DR) &&
-               (!(s->fcr & UART_FCR_FE) ||
-                s->recv_fifo.count >= s->recv_fifo.itl)) {
-        tmp_iir = UART_IIR_RDI;
-    } else if ((s->ier & UART_IER_THRI) && s->thr_ipending) {
-        tmp_iir = UART_IIR_THRI;
-    } else if ((s->ier & UART_IER_MSI) && (s->msr & UART_MSR_ANY_DELTA)) {
-        tmp_iir = UART_IIR_MSI;
-    }
-
-    s->iir = tmp_iir | (s->iir & 0xF0);
-
-    if (tmp_iir != UART_IIR_NO_INT) {
-        qemu_irq_raise(s->irq);
-    } else {
-        qemu_irq_lower(s->irq);
-    }
-}
-
-static void serial_update_parameters(SerialState *s)
-{
-    int speed, parity, data_bits, stop_bits, frame_size;
-    QEMUSerialSetParams ssp;
-
-    if (s->divider == 0)
-        return;
-
-    /* Start bit. */
-    frame_size = 1;
-    if (s->lcr & 0x08) {
-        /* Parity bit. */
-        frame_size++;
-        if (s->lcr & 0x10)
-            parity = 'E';
-        else
-            parity = 'O';
-    } else {
-            parity = 'N';
-    }
-    if (s->lcr & 0x04)
-        stop_bits = 2;
-    else
-        stop_bits = 1;
-
-    data_bits = (s->lcr & 0x03) + 5;
-    frame_size += data_bits + stop_bits;
-    speed = s->baudbase / s->divider;
-    ssp.speed = speed;
-    ssp.parity = parity;
-    ssp.data_bits = data_bits;
-    ssp.stop_bits = stop_bits;
-    s->char_transmit_time =  (get_ticks_per_sec() / speed) * frame_size;
-    qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
-
-    DPRINTF("speed=%d parity=%c data=%d stop=%d\n",
-           speed, parity, data_bits, stop_bits);
-}
-
-static void serial_update_msl(SerialState *s)
-{
-    uint8_t omsr;
-    int flags;
-
-    qemu_del_timer(s->modem_status_poll);
-
-    if (qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags) == -ENOTSUP) {
-        s->poll_msl = -1;
-        return;
-    }
-
-    omsr = s->msr;
-
-    s->msr = (flags & CHR_TIOCM_CTS) ? s->msr | UART_MSR_CTS : s->msr & ~UART_MSR_CTS;
-    s->msr = (flags & CHR_TIOCM_DSR) ? s->msr | UART_MSR_DSR : s->msr & ~UART_MSR_DSR;
-    s->msr = (flags & CHR_TIOCM_CAR) ? s->msr | UART_MSR_DCD : s->msr & ~UART_MSR_DCD;
-    s->msr = (flags & CHR_TIOCM_RI) ? s->msr | UART_MSR_RI : s->msr & ~UART_MSR_RI;
-
-    if (s->msr != omsr) {
-         /* Set delta bits */
-         s->msr = s->msr | ((s->msr >> 4) ^ (omsr >> 4));
-         /* UART_MSR_TERI only if change was from 1 -> 0 */
-         if ((s->msr & UART_MSR_TERI) && !(omsr & UART_MSR_RI))
-             s->msr &= ~UART_MSR_TERI;
-         serial_update_irq(s);
-    }
-
-    /* The real 16550A apparently has a 250ns response latency to line status changes.
-       We'll be lazy and poll only every 10ms, and only poll it at all if MSI interrupts are turned on */
-
-    if (s->poll_msl)
-        qemu_mod_timer(s->modem_status_poll, qemu_get_clock_ns(vm_clock) + get_ticks_per_sec() / 100);
-}
-
-static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
-{
-    SerialState *s = opaque;
-
-    if (s->tsr_retry <= 0) {
-        if (s->fcr & UART_FCR_FE) {
-            s->tsr = fifo_get(s,XMIT_FIFO);
-            if (!s->xmit_fifo.count)
-                s->lsr |= UART_LSR_THRE;
-        } else if ((s->lsr & UART_LSR_THRE)) {
-            return FALSE;
-        } else {
-            s->tsr = s->thr;
-            s->lsr |= UART_LSR_THRE;
-            s->lsr &= ~UART_LSR_TEMT;
-        }
-    }
-
-    if (s->mcr & UART_MCR_LOOP) {
-        /* in loopback mode, say that we just received a char */
-        serial_receive1(s, &s->tsr, 1);
-    } else if (qemu_chr_fe_write(s->chr, &s->tsr, 1) != 1) {
-        if (s->tsr_retry >= 0 && s->tsr_retry < MAX_XMIT_RETRY &&
-            qemu_chr_fe_add_watch(s->chr, G_IO_OUT, serial_xmit, s) > 0) {
-            s->tsr_retry++;
-            return FALSE;
-        }
-        s->tsr_retry = 0;
-    } else {
-        s->tsr_retry = 0;
-    }
-
-    s->last_xmit_ts = qemu_get_clock_ns(vm_clock);
-
-    if (s->lsr & UART_LSR_THRE) {
-        s->lsr |= UART_LSR_TEMT;
-        s->thr_ipending = 1;
-        serial_update_irq(s);
-    }
-
-    return FALSE;
-}
-
-
-static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val,
-                                unsigned size)
-{
-    SerialState *s = opaque;
-
-    addr &= 7;
-    DPRINTF("write addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 "\n", addr, val);
-    switch(addr) {
-    default:
-    case 0:
-        if (s->lcr & UART_LCR_DLAB) {
-            s->divider = (s->divider & 0xff00) | val;
-            serial_update_parameters(s);
-        } else {
-            s->thr = (uint8_t) val;
-            if(s->fcr & UART_FCR_FE) {
-                fifo_put(s, XMIT_FIFO, s->thr);
-                s->thr_ipending = 0;
-                s->lsr &= ~UART_LSR_TEMT;
-                s->lsr &= ~UART_LSR_THRE;
-                serial_update_irq(s);
-            } else {
-                s->thr_ipending = 0;
-                s->lsr &= ~UART_LSR_THRE;
-                serial_update_irq(s);
-            }
-            serial_xmit(NULL, G_IO_OUT, s);
-        }
-        break;
-    case 1:
-        if (s->lcr & UART_LCR_DLAB) {
-            s->divider = (s->divider & 0x00ff) | (val << 8);
-            serial_update_parameters(s);
-        } else {
-            s->ier = val & 0x0f;
-            /* If the backend device is a real serial port, turn polling of the modem
-               status lines on physical port on or off depending on UART_IER_MSI state */
-            if (s->poll_msl >= 0) {
-                if (s->ier & UART_IER_MSI) {
-                     s->poll_msl = 1;
-                     serial_update_msl(s);
-                } else {
-                     qemu_del_timer(s->modem_status_poll);
-                     s->poll_msl = 0;
-                }
-            }
-            if (s->lsr & UART_LSR_THRE) {
-                s->thr_ipending = 1;
-                serial_update_irq(s);
-            }
-        }
-        break;
-    case 2:
-        val = val & 0xFF;
-
-        if (s->fcr == val)
-            break;
-
-        /* Did the enable/disable flag change? If so, make sure FIFOs get flushed */
-        if ((val ^ s->fcr) & UART_FCR_FE)
-            val |= UART_FCR_XFR | UART_FCR_RFR;
-
-        /* FIFO clear */
-
-        if (val & UART_FCR_RFR) {
-            qemu_del_timer(s->fifo_timeout_timer);
-            s->timeout_ipending=0;
-            fifo_clear(s,RECV_FIFO);
-        }
-
-        if (val & UART_FCR_XFR) {
-            fifo_clear(s,XMIT_FIFO);
-        }
-
-        if (val & UART_FCR_FE) {
-            s->iir |= UART_IIR_FE;
-            /* Set RECV_FIFO trigger Level */
-            switch (val & 0xC0) {
-            case UART_FCR_ITL_1:
-                s->recv_fifo.itl = 1;
-                break;
-            case UART_FCR_ITL_2:
-                s->recv_fifo.itl = 4;
-                break;
-            case UART_FCR_ITL_3:
-                s->recv_fifo.itl = 8;
-                break;
-            case UART_FCR_ITL_4:
-                s->recv_fifo.itl = 14;
-                break;
-            }
-        } else
-            s->iir &= ~UART_IIR_FE;
-
-        /* Set fcr - or at least the bits in it that are supposed to "stick" */
-        s->fcr = val & 0xC9;
-        serial_update_irq(s);
-        break;
-    case 3:
-        {
-            int break_enable;
-            s->lcr = val;
-            serial_update_parameters(s);
-            break_enable = (val >> 6) & 1;
-            if (break_enable != s->last_break_enable) {
-                s->last_break_enable = break_enable;
-                qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_BREAK,
-                               &break_enable);
-            }
-        }
-        break;
-    case 4:
-        {
-            int flags;
-            int old_mcr = s->mcr;
-            s->mcr = val & 0x1f;
-            if (val & UART_MCR_LOOP)
-                break;
-
-            if (s->poll_msl >= 0 && old_mcr != s->mcr) {
-
-                qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_GET_TIOCM, &flags);
-
-                flags &= ~(CHR_TIOCM_RTS | CHR_TIOCM_DTR);
-
-                if (val & UART_MCR_RTS)
-                    flags |= CHR_TIOCM_RTS;
-                if (val & UART_MCR_DTR)
-                    flags |= CHR_TIOCM_DTR;
-
-                qemu_chr_fe_ioctl(s->chr,CHR_IOCTL_SERIAL_SET_TIOCM, &flags);
-                /* Update the modem status after a one-character-send wait-time, since there may be a response
-                   from the device/computer at the other end of the serial line */
-                qemu_mod_timer(s->modem_status_poll, qemu_get_clock_ns(vm_clock) + s->char_transmit_time);
-            }
-        }
-        break;
-    case 5:
-        break;
-    case 6:
-        break;
-    case 7:
-        s->scr = val;
-        break;
-    }
-}
-
-static uint64_t serial_ioport_read(void *opaque, hwaddr addr, unsigned size)
-{
-    SerialState *s = opaque;
-    uint32_t ret;
-
-    addr &= 7;
-    switch(addr) {
-    default:
-    case 0:
-        if (s->lcr & UART_LCR_DLAB) {
-            ret = s->divider & 0xff;
-        } else {
-            if(s->fcr & UART_FCR_FE) {
-                ret = fifo_get(s,RECV_FIFO);
-                if (s->recv_fifo.count == 0)
-                    s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
-                else
-                    qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock_ns (vm_clock) + s->char_transmit_time * 4);
-                s->timeout_ipending = 0;
-            } else {
-                ret = s->rbr;
-                s->lsr &= ~(UART_LSR_DR | UART_LSR_BI);
-            }
-            serial_update_irq(s);
-            if (!(s->mcr & UART_MCR_LOOP)) {
-                /* in loopback mode, don't receive any data */
-                qemu_chr_accept_input(s->chr);
-            }
-        }
-        break;
-    case 1:
-        if (s->lcr & UART_LCR_DLAB) {
-            ret = (s->divider >> 8) & 0xff;
-        } else {
-            ret = s->ier;
-        }
-        break;
-    case 2:
-        ret = s->iir;
-        if ((ret & UART_IIR_ID) == UART_IIR_THRI) {
-            s->thr_ipending = 0;
-            serial_update_irq(s);
-        }
-        break;
-    case 3:
-        ret = s->lcr;
-        break;
-    case 4:
-        ret = s->mcr;
-        break;
-    case 5:
-        ret = s->lsr;
-        /* Clear break and overrun interrupts */
-        if (s->lsr & (UART_LSR_BI|UART_LSR_OE)) {
-            s->lsr &= ~(UART_LSR_BI|UART_LSR_OE);
-            serial_update_irq(s);
-        }
-        break;
-    case 6:
-        if (s->mcr & UART_MCR_LOOP) {
-            /* in loopback, the modem output pins are connected to the
-               inputs */
-            ret = (s->mcr & 0x0c) << 4;
-            ret |= (s->mcr & 0x02) << 3;
-            ret |= (s->mcr & 0x01) << 5;
-        } else {
-            if (s->poll_msl >= 0)
-                serial_update_msl(s);
-            ret = s->msr;
-            /* Clear delta bits & msr int after read, if they were set */
-            if (s->msr & UART_MSR_ANY_DELTA) {
-                s->msr &= 0xF0;
-                serial_update_irq(s);
-            }
-        }
-        break;
-    case 7:
-        ret = s->scr;
-        break;
-    }
-    DPRINTF("read addr=0x%" HWADDR_PRIx " val=0x%02x\n", addr, ret);
-    return ret;
-}
-
-static int serial_can_receive(SerialState *s)
-{
-    if(s->fcr & UART_FCR_FE) {
-        if(s->recv_fifo.count < UART_FIFO_LENGTH)
-        /* Advertise (fifo.itl - fifo.count) bytes when count < ITL, and 1 if above. If UART_FIFO_LENGTH - fifo.count is
-        advertised the effect will be to almost always fill the fifo completely before the guest has a chance to respond,
-        effectively overriding the ITL that the guest has set. */
-             return (s->recv_fifo.count <= s->recv_fifo.itl) ? s->recv_fifo.itl - s->recv_fifo.count : 1;
-        else
-             return 0;
-    } else {
-    return !(s->lsr & UART_LSR_DR);
-    }
-}
-
-static void serial_receive_break(SerialState *s)
-{
-    s->rbr = 0;
-    /* When the LSR_DR is set a null byte is pushed into the fifo */
-    fifo_put(s, RECV_FIFO, '\0');
-    s->lsr |= UART_LSR_BI | UART_LSR_DR;
-    serial_update_irq(s);
-}
-
-/* There's data in recv_fifo and s->rbr has not been read for 4 char transmit times */
-static void fifo_timeout_int (void *opaque) {
-    SerialState *s = opaque;
-    if (s->recv_fifo.count) {
-        s->timeout_ipending = 1;
-        serial_update_irq(s);
-    }
-}
-
-static int serial_can_receive1(void *opaque)
-{
-    SerialState *s = opaque;
-    return serial_can_receive(s);
-}
-
-static void serial_receive1(void *opaque, const uint8_t *buf, int size)
-{
-    SerialState *s = opaque;
-
-    if (s->wakeup) {
-        qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
-    }
-    if(s->fcr & UART_FCR_FE) {
-        int i;
-        for (i = 0; i < size; i++) {
-            fifo_put(s, RECV_FIFO, buf[i]);
-        }
-        s->lsr |= UART_LSR_DR;
-        /* call the timeout receive callback in 4 char transmit time */
-        qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock_ns (vm_clock) + s->char_transmit_time * 4);
-    } else {
-        if (s->lsr & UART_LSR_DR)
-            s->lsr |= UART_LSR_OE;
-        s->rbr = buf[0];
-        s->lsr |= UART_LSR_DR;
-    }
-    serial_update_irq(s);
-}
-
-static void serial_event(void *opaque, int event)
-{
-    SerialState *s = opaque;
-    DPRINTF("event %x\n", event);
-    if (event == CHR_EVENT_BREAK)
-        serial_receive_break(s);
-}
-
-static void serial_pre_save(void *opaque)
-{
-    SerialState *s = opaque;
-    s->fcr_vmstate = s->fcr;
-}
-
-static int serial_post_load(void *opaque, int version_id)
-{
-    SerialState *s = opaque;
-
-    if (version_id < 3) {
-        s->fcr_vmstate = 0;
-    }
-    /* Initialize fcr via setter to perform essential side-effects */
-    serial_ioport_write(s, 0x02, s->fcr_vmstate, 1);
-    serial_update_parameters(s);
-    return 0;
-}
-
-const VMStateDescription vmstate_serial = {
-    .name = "serial",
-    .version_id = 3,
-    .minimum_version_id = 2,
-    .pre_save = serial_pre_save,
-    .post_load = serial_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT16_V(divider, SerialState, 2),
-        VMSTATE_UINT8(rbr, SerialState),
-        VMSTATE_UINT8(ier, SerialState),
-        VMSTATE_UINT8(iir, SerialState),
-        VMSTATE_UINT8(lcr, SerialState),
-        VMSTATE_UINT8(mcr, SerialState),
-        VMSTATE_UINT8(lsr, SerialState),
-        VMSTATE_UINT8(msr, SerialState),
-        VMSTATE_UINT8(scr, SerialState),
-        VMSTATE_UINT8_V(fcr_vmstate, SerialState, 3),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void serial_reset(void *opaque)
-{
-    SerialState *s = opaque;
-
-    s->rbr = 0;
-    s->ier = 0;
-    s->iir = UART_IIR_NO_INT;
-    s->lcr = 0;
-    s->lsr = UART_LSR_TEMT | UART_LSR_THRE;
-    s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS;
-    /* Default to 9600 baud, 1 start bit, 8 data bits, 1 stop bit, no parity. */
-    s->divider = 0x0C;
-    s->mcr = UART_MCR_OUT2;
-    s->scr = 0;
-    s->tsr_retry = 0;
-    s->char_transmit_time = (get_ticks_per_sec() / 9600) * 10;
-    s->poll_msl = 0;
-
-    fifo_clear(s,RECV_FIFO);
-    fifo_clear(s,XMIT_FIFO);
-
-    s->last_xmit_ts = qemu_get_clock_ns(vm_clock);
-
-    s->thr_ipending = 0;
-    s->last_break_enable = 0;
-    qemu_irq_lower(s->irq);
-}
-
-void serial_init_core(SerialState *s)
-{
-    if (!s->chr) {
-        fprintf(stderr, "Can't create serial device, empty char device\n");
-       exit(1);
-    }
-
-    s->modem_status_poll = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) serial_update_msl, s);
-
-    s->fifo_timeout_timer = qemu_new_timer_ns(vm_clock, (QEMUTimerCB *) fifo_timeout_int, s);
-    qemu_register_reset(serial_reset, s);
-
-    qemu_chr_add_handlers(s->chr, serial_can_receive1, serial_receive1,
-                          serial_event, s);
-}
-
-void serial_exit_core(SerialState *s)
-{
-    qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL);
-    qemu_unregister_reset(serial_reset, s);
-}
-
-/* Change the main reference oscillator frequency. */
-void serial_set_frequency(SerialState *s, uint32_t frequency)
-{
-    s->baudbase = frequency;
-    serial_update_parameters(s);
-}
-
-const MemoryRegionOps serial_io_ops = {
-    .read = serial_ioport_read,
-    .write = serial_ioport_write,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1,
-    },
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-SerialState *serial_init(int base, qemu_irq irq, int baudbase,
-                         CharDriverState *chr, MemoryRegion *system_io)
-{
-    SerialState *s;
-
-    s = g_malloc0(sizeof(SerialState));
-
-    s->irq = irq;
-    s->baudbase = baudbase;
-    s->chr = chr;
-    serial_init_core(s);
-
-    vmstate_register(NULL, base, &vmstate_serial, s);
-
-    memory_region_init_io(&s->io, &serial_io_ops, s, "serial", 8);
-    memory_region_add_subregion(system_io, base, &s->io);
-
-    return s;
-}
-
-/* Memory mapped interface */
-static uint64_t serial_mm_read(void *opaque, hwaddr addr,
-                               unsigned size)
-{
-    SerialState *s = opaque;
-    return serial_ioport_read(s, addr >> s->it_shift, 1);
-}
-
-static void serial_mm_write(void *opaque, hwaddr addr,
-                            uint64_t value, unsigned size)
-{
-    SerialState *s = opaque;
-    value &= ~0u >> (32 - (size * 8));
-    serial_ioport_write(s, addr >> s->it_shift, value, 1);
-}
-
-static const MemoryRegionOps serial_mm_ops[3] = {
-    [DEVICE_NATIVE_ENDIAN] = {
-        .read = serial_mm_read,
-        .write = serial_mm_write,
-        .endianness = DEVICE_NATIVE_ENDIAN,
-    },
-    [DEVICE_LITTLE_ENDIAN] = {
-        .read = serial_mm_read,
-        .write = serial_mm_write,
-        .endianness = DEVICE_LITTLE_ENDIAN,
-    },
-    [DEVICE_BIG_ENDIAN] = {
-        .read = serial_mm_read,
-        .write = serial_mm_write,
-        .endianness = DEVICE_BIG_ENDIAN,
-    },
-};
-
-SerialState *serial_mm_init(MemoryRegion *address_space,
-                            hwaddr base, int it_shift,
-                            qemu_irq irq, int baudbase,
-                            CharDriverState *chr, enum device_endian end)
-{
-    SerialState *s;
-
-    s = g_malloc0(sizeof(SerialState));
-
-    s->it_shift = it_shift;
-    s->irq = irq;
-    s->baudbase = baudbase;
-    s->chr = chr;
-
-    serial_init_core(s);
-    vmstate_register(NULL, base, &vmstate_serial, s);
-
-    memory_region_init_io(&s->io, &serial_mm_ops[end], s,
-                          "serial", 8 << it_shift);
-    memory_region_add_subregion(address_space, base, &s->io);
-
-    serial_update_msl(s);
-    return s;
-}
diff --git a/hw/smbus.c b/hw/smbus.c
deleted file mode 100644 (file)
index 25d2d04..0000000
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * QEMU SMBus device emulation.
- *
- * Copyright (c) 2007 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the LGPL.
- */
-
-/* TODO: Implement PEC.  */
-
-#include "hw/hw.h"
-#include "hw/i2c/i2c.h"
-#include "hw/i2c/smbus.h"
-
-//#define DEBUG_SMBUS 1
-
-#ifdef DEBUG_SMBUS
-#define DPRINTF(fmt, ...) \
-do { printf("smbus(%02x): " fmt , dev->i2c.address, ## __VA_ARGS__); } while (0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0)
-#endif
-
-enum {
-    SMBUS_IDLE,
-    SMBUS_WRITE_DATA,
-    SMBUS_RECV_BYTE,
-    SMBUS_READ_DATA,
-    SMBUS_DONE,
-    SMBUS_CONFUSED = -1
-};
-
-static void smbus_do_quick_cmd(SMBusDevice *dev, int recv)
-{
-    SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
-
-    DPRINTF("Quick Command %d\n", recv);
-    if (sc->quick_cmd) {
-        sc->quick_cmd(dev, recv);
-    }
-}
-
-static void smbus_do_write(SMBusDevice *dev)
-{
-    SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
-
-    if (dev->data_len == 0) {
-        smbus_do_quick_cmd(dev, 0);
-    } else if (dev->data_len == 1) {
-        DPRINTF("Send Byte\n");
-        if (sc->send_byte) {
-            sc->send_byte(dev, dev->data_buf[0]);
-        }
-    } else {
-        dev->command = dev->data_buf[0];
-        DPRINTF("Command %d len %d\n", dev->command, dev->data_len - 1);
-        if (sc->write_data) {
-            sc->write_data(dev, dev->command, dev->data_buf + 1,
-                           dev->data_len - 1);
-        }
-    }
-}
-
-static void smbus_i2c_event(I2CSlave *s, enum i2c_event event)
-{
-    SMBusDevice *dev = SMBUS_DEVICE(s);
-
-    switch (event) {
-    case I2C_START_SEND:
-        switch (dev->mode) {
-        case SMBUS_IDLE:
-            DPRINTF("Incoming data\n");
-            dev->mode = SMBUS_WRITE_DATA;
-            break;
-        default:
-            BADF("Unexpected send start condition in state %d\n", dev->mode);
-            dev->mode = SMBUS_CONFUSED;
-            break;
-        }
-        break;
-
-    case I2C_START_RECV:
-        switch (dev->mode) {
-        case SMBUS_IDLE:
-            DPRINTF("Read mode\n");
-            dev->mode = SMBUS_RECV_BYTE;
-            break;
-        case SMBUS_WRITE_DATA:
-            if (dev->data_len == 0) {
-                BADF("Read after write with no data\n");
-                dev->mode = SMBUS_CONFUSED;
-            } else {
-                if (dev->data_len > 1) {
-                    smbus_do_write(dev);
-                } else {
-                    dev->command = dev->data_buf[0];
-                    DPRINTF("%02x: Command %d\n", dev->i2c.address,
-                            dev->command);
-                }
-                DPRINTF("Read mode\n");
-                dev->data_len = 0;
-                dev->mode = SMBUS_READ_DATA;
-            }
-            break;
-        default:
-            BADF("Unexpected recv start condition in state %d\n", dev->mode);
-            dev->mode = SMBUS_CONFUSED;
-            break;
-        }
-        break;
-
-    case I2C_FINISH:
-        switch (dev->mode) {
-        case SMBUS_WRITE_DATA:
-            smbus_do_write(dev);
-            break;
-        case SMBUS_RECV_BYTE:
-            smbus_do_quick_cmd(dev, 1);
-            break;
-        case SMBUS_READ_DATA:
-            BADF("Unexpected stop during receive\n");
-            break;
-        default:
-            /* Nothing to do.  */
-            break;
-        }
-        dev->mode = SMBUS_IDLE;
-        dev->data_len = 0;
-        break;
-
-    case I2C_NACK:
-        switch (dev->mode) {
-        case SMBUS_DONE:
-            /* Nothing to do.  */
-            break;
-        case SMBUS_READ_DATA:
-            dev->mode = SMBUS_DONE;
-            break;
-        default:
-            BADF("Unexpected NACK in state %d\n", dev->mode);
-            dev->mode = SMBUS_CONFUSED;
-            break;
-        }
-    }
-}
-
-static int smbus_i2c_recv(I2CSlave *s)
-{
-    SMBusDevice *dev = SMBUS_DEVICE(s);
-    SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
-    int ret;
-
-    switch (dev->mode) {
-    case SMBUS_RECV_BYTE:
-        if (sc->receive_byte) {
-            ret = sc->receive_byte(dev);
-        } else {
-            ret = 0;
-        }
-        DPRINTF("Receive Byte %02x\n", ret);
-        dev->mode = SMBUS_DONE;
-        break;
-    case SMBUS_READ_DATA:
-        if (sc->read_data) {
-            ret = sc->read_data(dev, dev->command, dev->data_len);
-            dev->data_len++;
-        } else {
-            ret = 0;
-        }
-        DPRINTF("Read data %02x\n", ret);
-        break;
-    default:
-        BADF("Unexpected read in state %d\n", dev->mode);
-        dev->mode = SMBUS_CONFUSED;
-        ret = 0;
-        break;
-    }
-    return ret;
-}
-
-static int smbus_i2c_send(I2CSlave *s, uint8_t data)
-{
-    SMBusDevice *dev = SMBUS_DEVICE(s);
-
-    switch (dev->mode) {
-    case SMBUS_WRITE_DATA:
-        DPRINTF("Write data %02x\n", data);
-        dev->data_buf[dev->data_len++] = data;
-        break;
-    default:
-        BADF("Unexpected write in state %d\n", dev->mode);
-        break;
-    }
-    return 0;
-}
-
-static int smbus_device_init(I2CSlave *i2c)
-{
-    SMBusDevice *dev = SMBUS_DEVICE(i2c);
-    SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
-
-    return sc->init(dev);
-}
-
-/* Master device commands.  */
-void smbus_quick_command(i2c_bus *bus, uint8_t addr, int read)
-{
-    i2c_start_transfer(bus, addr, read);
-    i2c_end_transfer(bus);
-}
-
-uint8_t smbus_receive_byte(i2c_bus *bus, uint8_t addr)
-{
-    uint8_t data;
-
-    i2c_start_transfer(bus, addr, 1);
-    data = i2c_recv(bus);
-    i2c_nack(bus);
-    i2c_end_transfer(bus);
-    return data;
-}
-
-void smbus_send_byte(i2c_bus *bus, uint8_t addr, uint8_t data)
-{
-    i2c_start_transfer(bus, addr, 0);
-    i2c_send(bus, data);
-    i2c_end_transfer(bus);
-}
-
-uint8_t smbus_read_byte(i2c_bus *bus, uint8_t addr, uint8_t command)
-{
-    uint8_t data;
-    i2c_start_transfer(bus, addr, 0);
-    i2c_send(bus, command);
-    i2c_start_transfer(bus, addr, 1);
-    data = i2c_recv(bus);
-    i2c_nack(bus);
-    i2c_end_transfer(bus);
-    return data;
-}
-
-void smbus_write_byte(i2c_bus *bus, uint8_t addr, uint8_t command, uint8_t data)
-{
-    i2c_start_transfer(bus, addr, 0);
-    i2c_send(bus, command);
-    i2c_send(bus, data);
-    i2c_end_transfer(bus);
-}
-
-uint16_t smbus_read_word(i2c_bus *bus, uint8_t addr, uint8_t command)
-{
-    uint16_t data;
-    i2c_start_transfer(bus, addr, 0);
-    i2c_send(bus, command);
-    i2c_start_transfer(bus, addr, 1);
-    data = i2c_recv(bus);
-    data |= i2c_recv(bus) << 8;
-    i2c_nack(bus);
-    i2c_end_transfer(bus);
-    return data;
-}
-
-void smbus_write_word(i2c_bus *bus, uint8_t addr, uint8_t command, uint16_t data)
-{
-    i2c_start_transfer(bus, addr, 0);
-    i2c_send(bus, command);
-    i2c_send(bus, data & 0xff);
-    i2c_send(bus, data >> 8);
-    i2c_end_transfer(bus);
-}
-
-int smbus_read_block(i2c_bus *bus, uint8_t addr, uint8_t command, uint8_t *data)
-{
-    int len;
-    int i;
-
-    i2c_start_transfer(bus, addr, 0);
-    i2c_send(bus, command);
-    i2c_start_transfer(bus, addr, 1);
-    len = i2c_recv(bus);
-    if (len > 32)
-        len = 0;
-    for (i = 0; i < len; i++)
-        data[i] = i2c_recv(bus);
-    i2c_nack(bus);
-    i2c_end_transfer(bus);
-    return len;
-}
-
-void smbus_write_block(i2c_bus *bus, uint8_t addr, uint8_t command, uint8_t *data,
-                       int len)
-{
-    int i;
-
-    if (len > 32)
-        len = 32;
-
-    i2c_start_transfer(bus, addr, 0);
-    i2c_send(bus, command);
-    i2c_send(bus, len);
-    for (i = 0; i < len; i++)
-        i2c_send(bus, data[i]);
-    i2c_end_transfer(bus);
-}
-
-static void smbus_device_class_init(ObjectClass *klass, void *data)
-{
-    I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
-
-    sc->init = smbus_device_init;
-    sc->event = smbus_i2c_event;
-    sc->recv = smbus_i2c_recv;
-    sc->send = smbus_i2c_send;
-}
-
-static const TypeInfo smbus_device_type_info = {
-    .name = TYPE_SMBUS_DEVICE,
-    .parent = TYPE_I2C_SLAVE,
-    .instance_size = sizeof(SMBusDevice),
-    .abstract = true,
-    .class_size = sizeof(SMBusDeviceClass),
-    .class_init = smbus_device_class_init,
-};
-
-static void smbus_device_register_types(void)
-{
-    type_register_static(&smbus_device_type_info);
-}
-
-type_init(smbus_device_register_types)
diff --git a/hw/smbus_eeprom.c b/hw/smbus_eeprom.c
deleted file mode 100644 (file)
index 0154283..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * QEMU SMBus EEPROM device
- *
- * Copyright (c) 2007 Arastra, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/hw.h"
-#include "hw/i2c/i2c.h"
-#include "hw/i2c/smbus.h"
-
-//#define DEBUG
-
-typedef struct SMBusEEPROMDevice {
-    SMBusDevice smbusdev;
-    void *data;
-    uint8_t offset;
-} SMBusEEPROMDevice;
-
-static void eeprom_quick_cmd(SMBusDevice *dev, uint8_t read)
-{
-#ifdef DEBUG
-    printf("eeprom_quick_cmd: addr=0x%02x read=%d\n", dev->i2c.address, read);
-#endif
-}
-
-static void eeprom_send_byte(SMBusDevice *dev, uint8_t val)
-{
-    SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
-#ifdef DEBUG
-    printf("eeprom_send_byte: addr=0x%02x val=0x%02x\n",
-           dev->i2c.address, val);
-#endif
-    eeprom->offset = val;
-}
-
-static uint8_t eeprom_receive_byte(SMBusDevice *dev)
-{
-    SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
-    uint8_t *data = eeprom->data;
-    uint8_t val = data[eeprom->offset++];
-#ifdef DEBUG
-    printf("eeprom_receive_byte: addr=0x%02x val=0x%02x\n",
-           dev->i2c.address, val);
-#endif
-    return val;
-}
-
-static void eeprom_write_data(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len)
-{
-    SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
-    int n;
-#ifdef DEBUG
-    printf("eeprom_write_byte: addr=0x%02x cmd=0x%02x val=0x%02x\n",
-           dev->i2c.address, cmd, buf[0]);
-#endif
-    /* An page write operation is not a valid SMBus command.
-       It is a block write without a length byte.  Fortunately we
-       get the full block anyway.  */
-    /* TODO: Should this set the current location?  */
-    if (cmd + len > 256)
-        n = 256 - cmd;
-    else
-        n = len;
-    memcpy(eeprom->data + cmd, buf, n);
-    len -= n;
-    if (len)
-        memcpy(eeprom->data, buf + n, len);
-}
-
-static uint8_t eeprom_read_data(SMBusDevice *dev, uint8_t cmd, int n)
-{
-    SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
-    /* If this is the first byte then set the current position.  */
-    if (n == 0)
-        eeprom->offset = cmd;
-    /* As with writes, we implement block reads without the
-       SMBus length byte.  */
-    return eeprom_receive_byte(dev);
-}
-
-static int smbus_eeprom_initfn(SMBusDevice *dev)
-{
-    SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *)dev;
-
-    eeprom->offset = 0;
-    return 0;
-}
-
-static Property smbus_eeprom_properties[] = {
-    DEFINE_PROP_PTR("data", SMBusEEPROMDevice, data),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(klass);
-
-    sc->init = smbus_eeprom_initfn;
-    sc->quick_cmd = eeprom_quick_cmd;
-    sc->send_byte = eeprom_send_byte;
-    sc->receive_byte = eeprom_receive_byte;
-    sc->write_data = eeprom_write_data;
-    sc->read_data = eeprom_read_data;
-    dc->props = smbus_eeprom_properties;
-}
-
-static const TypeInfo smbus_eeprom_info = {
-    .name          = "smbus-eeprom",
-    .parent        = TYPE_SMBUS_DEVICE,
-    .instance_size = sizeof(SMBusEEPROMDevice),
-    .class_init    = smbus_eeprom_class_initfn,
-};
-
-static void smbus_eeprom_register_types(void)
-{
-    type_register_static(&smbus_eeprom_info);
-}
-
-type_init(smbus_eeprom_register_types)
-
-void smbus_eeprom_init(i2c_bus *smbus, int nb_eeprom,
-                       const uint8_t *eeprom_spd, int eeprom_spd_size)
-{
-    int i;
-    uint8_t *eeprom_buf = g_malloc0(8 * 256); /* XXX: make this persistent */
-    if (eeprom_spd_size > 0) {
-        memcpy(eeprom_buf, eeprom_spd, eeprom_spd_size);
-    }
-
-    for (i = 0; i < nb_eeprom; i++) {
-        DeviceState *eeprom;
-        eeprom = qdev_create((BusState *)smbus, "smbus-eeprom");
-        qdev_prop_set_uint8(eeprom, "address", 0x50 + i);
-        qdev_prop_set_ptr(eeprom, "data", eeprom_buf + (i * 256));
-        qdev_init_nofail(eeprom);
-    }
-}
diff --git a/hw/smbus_ich9.c b/hw/smbus_ich9.c
deleted file mode 100644 (file)
index ca22978..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * ACPI implementation
- *
- * Copyright (c) 2006 Fabrice Bellard
- * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
- *               VA Linux Systems Japan K.K.
- * Copyright (C) 2012 Jason Baron <jbaron@redhat.com>
- *
- * This is based on acpi.c, but heavily rewritten.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- *
- */
-#include "hw/hw.h"
-#include "hw/i386/pc.h"
-#include "hw/i2c/pm_smbus.h"
-#include "hw/pci/pci.h"
-#include "sysemu/sysemu.h"
-#include "hw/i2c/i2c.h"
-#include "hw/i2c/smbus.h"
-
-#include "hw/i386/ich9.h"
-
-#define TYPE_ICH9_SMB_DEVICE "ICH9 SMB"
-#define ICH9_SMB_DEVICE(obj) \
-     OBJECT_CHECK(ICH9SMBState, (obj), TYPE_ICH9_SMB_DEVICE)
-
-typedef struct ICH9SMBState {
-    PCIDevice dev;
-
-    PMSMBus smb;
-} ICH9SMBState;
-
-static const VMStateDescription vmstate_ich9_smbus = {
-    .name = "ich9_smb",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_PCI_DEVICE(dev, struct ICH9SMBState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void ich9_smbus_write_config(PCIDevice *d, uint32_t address,
-                                    uint32_t val, int len)
-{
-    ICH9SMBState *s = ICH9_SMB_DEVICE(d);
-
-    pci_default_write_config(d, address, val, len);
-    if (range_covers_byte(address, len, ICH9_SMB_HOSTC)) {
-        uint8_t hostc = s->dev.config[ICH9_SMB_HOSTC];
-        if ((hostc & ICH9_SMB_HOSTC_HST_EN) &&
-            !(hostc & ICH9_SMB_HOSTC_I2C_EN)) {
-            memory_region_set_enabled(&s->smb.io, true);
-        } else {
-            memory_region_set_enabled(&s->smb.io, false);
-        }
-    }
-}
-
-static int ich9_smbus_initfn(PCIDevice *d)
-{
-    ICH9SMBState *s = ICH9_SMB_DEVICE(d);
-
-    /* TODO? D31IP.SMIP in chipset configuration space */
-    pci_config_set_interrupt_pin(d->config, 0x01); /* interrupt pin 1 */
-
-    pci_set_byte(d->config + ICH9_SMB_HOSTC, 0);
-    /* TODO bar0, bar1: 64bit BAR support*/
-
-    pm_smbus_init(&d->qdev, &s->smb);
-    pci_register_bar(d, ICH9_SMB_SMB_BASE_BAR, PCI_BASE_ADDRESS_SPACE_IO,
-                     &s->smb.io);
-    return 0;
-}
-
-static void ich9_smb_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->device_id = PCI_DEVICE_ID_INTEL_ICH9_6;
-    k->revision = ICH9_A2_SMB_REVISION;
-    k->class_id = PCI_CLASS_SERIAL_SMBUS;
-    dc->no_user = 1;
-    dc->vmsd = &vmstate_ich9_smbus;
-    dc->desc = "ICH9 SMBUS Bridge";
-    k->init = ich9_smbus_initfn;
-    k->config_write = ich9_smbus_write_config;
-}
-
-i2c_bus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base)
-{
-    PCIDevice *d =
-        pci_create_simple_multifunction(bus, devfn, true, TYPE_ICH9_SMB_DEVICE);
-    ICH9SMBState *s = ICH9_SMB_DEVICE(d);
-    return s->smb.smbus;
-}
-
-static const TypeInfo ich9_smb_info = {
-    .name   = TYPE_ICH9_SMB_DEVICE,
-    .parent = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(ICH9SMBState),
-    .class_init = ich9_smb_class_init,
-};
-
-static void ich9_smb_register(void)
-{
-    type_register_static(&ich9_smb_info);
-}
-
-type_init(ich9_smb_register);
diff --git a/hw/smc91c111.c b/hw/smc91c111.c
deleted file mode 100644 (file)
index f659256..0000000
+++ /dev/null
@@ -1,806 +0,0 @@
-/*
- * SMSC 91C111 Ethernet interface emulation
- *
- * Copyright (c) 2005 CodeSourcery, LLC.
- * Written by Paul Brook
- *
- * This code is licensed under the GPL
- */
-
-#include "hw/sysbus.h"
-#include "net/net.h"
-#include "hw/arm/devices.h"
-/* For crc32 */
-#include <zlib.h>
-
-/* Number of 2k memory pages available.  */
-#define NUM_PACKETS 4
-
-typedef struct {
-    SysBusDevice busdev;
-    NICState *nic;
-    NICConf conf;
-    uint16_t tcr;
-    uint16_t rcr;
-    uint16_t cr;
-    uint16_t ctr;
-    uint16_t gpr;
-    uint16_t ptr;
-    uint16_t ercv;
-    qemu_irq irq;
-    int bank;
-    int packet_num;
-    int tx_alloc;
-    /* Bitmask of allocated packets.  */
-    int allocated;
-    int tx_fifo_len;
-    int tx_fifo[NUM_PACKETS];
-    int rx_fifo_len;
-    int rx_fifo[NUM_PACKETS];
-    int tx_fifo_done_len;
-    int tx_fifo_done[NUM_PACKETS];
-    /* Packet buffer memory.  */
-    uint8_t data[NUM_PACKETS][2048];
-    uint8_t int_level;
-    uint8_t int_mask;
-    MemoryRegion mmio;
-} smc91c111_state;
-
-static const VMStateDescription vmstate_smc91c111 = {
-    .name = "smc91c111",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT16(tcr, smc91c111_state),
-        VMSTATE_UINT16(rcr, smc91c111_state),
-        VMSTATE_UINT16(cr, smc91c111_state),
-        VMSTATE_UINT16(ctr, smc91c111_state),
-        VMSTATE_UINT16(gpr, smc91c111_state),
-        VMSTATE_UINT16(ptr, smc91c111_state),
-        VMSTATE_UINT16(ercv, smc91c111_state),
-        VMSTATE_INT32(bank, smc91c111_state),
-        VMSTATE_INT32(packet_num, smc91c111_state),
-        VMSTATE_INT32(tx_alloc, smc91c111_state),
-        VMSTATE_INT32(allocated, smc91c111_state),
-        VMSTATE_INT32(tx_fifo_len, smc91c111_state),
-        VMSTATE_INT32_ARRAY(tx_fifo, smc91c111_state, NUM_PACKETS),
-        VMSTATE_INT32(rx_fifo_len, smc91c111_state),
-        VMSTATE_INT32_ARRAY(rx_fifo, smc91c111_state, NUM_PACKETS),
-        VMSTATE_INT32(tx_fifo_done_len, smc91c111_state),
-        VMSTATE_INT32_ARRAY(tx_fifo_done, smc91c111_state, NUM_PACKETS),
-        VMSTATE_BUFFER_UNSAFE(data, smc91c111_state, 0, NUM_PACKETS * 2048),
-        VMSTATE_UINT8(int_level, smc91c111_state),
-        VMSTATE_UINT8(int_mask, smc91c111_state),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-#define RCR_SOFT_RST  0x8000
-#define RCR_STRIP_CRC 0x0200
-#define RCR_RXEN      0x0100
-
-#define TCR_EPH_LOOP  0x2000
-#define TCR_NOCRC     0x0100
-#define TCR_PAD_EN    0x0080
-#define TCR_FORCOL    0x0004
-#define TCR_LOOP      0x0002
-#define TCR_TXEN      0x0001
-
-#define INT_MD        0x80
-#define INT_ERCV      0x40
-#define INT_EPH       0x20
-#define INT_RX_OVRN   0x10
-#define INT_ALLOC     0x08
-#define INT_TX_EMPTY  0x04
-#define INT_TX        0x02
-#define INT_RCV       0x01
-
-#define CTR_AUTO_RELEASE  0x0800
-#define CTR_RELOAD        0x0002
-#define CTR_STORE         0x0001
-
-#define RS_ALGNERR      0x8000
-#define RS_BRODCAST     0x4000
-#define RS_BADCRC       0x2000
-#define RS_ODDFRAME     0x1000
-#define RS_TOOLONG      0x0800
-#define RS_TOOSHORT     0x0400
-#define RS_MULTICAST    0x0001
-
-/* Update interrupt status.  */
-static void smc91c111_update(smc91c111_state *s)
-{
-    int level;
-
-    if (s->tx_fifo_len == 0)
-        s->int_level |= INT_TX_EMPTY;
-    if (s->tx_fifo_done_len != 0)
-        s->int_level |= INT_TX;
-    level = (s->int_level & s->int_mask) != 0;
-    qemu_set_irq(s->irq, level);
-}
-
-/* Try to allocate a packet.  Returns 0x80 on failure.  */
-static int smc91c111_allocate_packet(smc91c111_state *s)
-{
-    int i;
-    if (s->allocated == (1 << NUM_PACKETS) - 1) {
-        return 0x80;
-    }
-
-    for (i = 0; i < NUM_PACKETS; i++) {
-        if ((s->allocated & (1 << i)) == 0)
-            break;
-    }
-    s->allocated |= 1 << i;
-    return i;
-}
-
-
-/* Process a pending TX allocate.  */
-static void smc91c111_tx_alloc(smc91c111_state *s)
-{
-    s->tx_alloc = smc91c111_allocate_packet(s);
-    if (s->tx_alloc == 0x80)
-        return;
-    s->int_level |= INT_ALLOC;
-    smc91c111_update(s);
-}
-
-/* Remove and item from the RX FIFO.  */
-static void smc91c111_pop_rx_fifo(smc91c111_state *s)
-{
-    int i;
-
-    s->rx_fifo_len--;
-    if (s->rx_fifo_len) {
-        for (i = 0; i < s->rx_fifo_len; i++)
-            s->rx_fifo[i] = s->rx_fifo[i + 1];
-        s->int_level |= INT_RCV;
-    } else {
-        s->int_level &= ~INT_RCV;
-    }
-    smc91c111_update(s);
-}
-
-/* Remove an item from the TX completion FIFO.  */
-static void smc91c111_pop_tx_fifo_done(smc91c111_state *s)
-{
-    int i;
-
-    if (s->tx_fifo_done_len == 0)
-        return;
-    s->tx_fifo_done_len--;
-    for (i = 0; i < s->tx_fifo_done_len; i++)
-        s->tx_fifo_done[i] = s->tx_fifo_done[i + 1];
-}
-
-/* Release the memory allocated to a packet.  */
-static void smc91c111_release_packet(smc91c111_state *s, int packet)
-{
-    s->allocated &= ~(1 << packet);
-    if (s->tx_alloc == 0x80)
-        smc91c111_tx_alloc(s);
-}
-
-/* Flush the TX FIFO.  */
-static void smc91c111_do_tx(smc91c111_state *s)
-{
-    int i;
-    int len;
-    int control;
-    int packetnum;
-    uint8_t *p;
-
-    if ((s->tcr & TCR_TXEN) == 0)
-        return;
-    if (s->tx_fifo_len == 0)
-        return;
-    for (i = 0; i < s->tx_fifo_len; i++) {
-        packetnum = s->tx_fifo[i];
-        p = &s->data[packetnum][0];
-        /* Set status word.  */
-        *(p++) = 0x01;
-        *(p++) = 0x40;
-        len = *(p++);
-        len |= ((int)*(p++)) << 8;
-        len -= 6;
-        control = p[len + 1];
-        if (control & 0x20)
-            len++;
-        /* ??? This overwrites the data following the buffer.
-           Don't know what real hardware does.  */
-        if (len < 64 && (s->tcr & TCR_PAD_EN)) {
-            memset(p + len, 0, 64 - len);
-            len = 64;
-        }
-#if 0
-        {
-            int add_crc;
-
-            /* The card is supposed to append the CRC to the frame.
-               However none of the other network traffic has the CRC
-               appended.  Suspect this is low level ethernet detail we
-               don't need to worry about.  */
-            add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0;
-            if (add_crc) {
-                uint32_t crc;
-
-                crc = crc32(~0, p, len);
-                memcpy(p + len, &crc, 4);
-                len += 4;
-            }
-        }
-#endif
-        if (s->ctr & CTR_AUTO_RELEASE)
-            /* Race?  */
-            smc91c111_release_packet(s, packetnum);
-        else if (s->tx_fifo_done_len < NUM_PACKETS)
-            s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum;
-        qemu_send_packet(qemu_get_queue(s->nic), p, len);
-    }
-    s->tx_fifo_len = 0;
-    smc91c111_update(s);
-}
-
-/* Add a packet to the TX FIFO.  */
-static void smc91c111_queue_tx(smc91c111_state *s, int packet)
-{
-    if (s->tx_fifo_len == NUM_PACKETS)
-        return;
-    s->tx_fifo[s->tx_fifo_len++] = packet;
-    smc91c111_do_tx(s);
-}
-
-static void smc91c111_reset(DeviceState *dev)
-{
-    smc91c111_state *s = FROM_SYSBUS(smc91c111_state, SYS_BUS_DEVICE(dev));
-    s->bank = 0;
-    s->tx_fifo_len = 0;
-    s->tx_fifo_done_len = 0;
-    s->rx_fifo_len = 0;
-    s->allocated = 0;
-    s->packet_num = 0;
-    s->tx_alloc = 0;
-    s->tcr = 0;
-    s->rcr = 0;
-    s->cr = 0xa0b1;
-    s->ctr = 0x1210;
-    s->ptr = 0;
-    s->ercv = 0x1f;
-    s->int_level = INT_TX_EMPTY;
-    s->int_mask = 0;
-    smc91c111_update(s);
-}
-
-#define SET_LOW(name, val) s->name = (s->name & 0xff00) | val
-#define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8)
-
-static void smc91c111_writeb(void *opaque, hwaddr offset,
-                             uint32_t value)
-{
-    smc91c111_state *s = (smc91c111_state *)opaque;
-
-    offset = offset & 0xf;
-    if (offset == 14) {
-        s->bank = value;
-        return;
-    }
-    if (offset == 15)
-        return;
-    switch (s->bank) {
-    case 0:
-        switch (offset) {
-        case 0: /* TCR */
-            SET_LOW(tcr, value);
-            return;
-        case 1:
-            SET_HIGH(tcr, value);
-            return;
-        case 4: /* RCR */
-            SET_LOW(rcr, value);
-            return;
-        case 5:
-            SET_HIGH(rcr, value);
-            if (s->rcr & RCR_SOFT_RST)
-                smc91c111_reset(&s->busdev.qdev);
-            return;
-        case 10: case 11: /* RPCR */
-            /* Ignored */
-            return;
-        case 12: case 13: /* Reserved */
-            return;
-        }
-        break;
-
-    case 1:
-        switch (offset) {
-        case 0: /* CONFIG */
-            SET_LOW(cr, value);
-            return;
-        case 1:
-            SET_HIGH(cr,value);
-            return;
-        case 2: case 3: /* BASE */
-        case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
-            /* Not implemented.  */
-            return;
-        case 10: /* Genral Purpose */
-            SET_LOW(gpr, value);
-            return;
-        case 11:
-            SET_HIGH(gpr, value);
-            return;
-        case 12: /* Control */
-            if (value & 1)
-                fprintf(stderr, "smc91c111:EEPROM store not implemented\n");
-            if (value & 2)
-                fprintf(stderr, "smc91c111:EEPROM reload not implemented\n");
-            value &= ~3;
-            SET_LOW(ctr, value);
-            return;
-        case 13:
-            SET_HIGH(ctr, value);
-            return;
-        }
-        break;
-
-    case 2:
-        switch (offset) {
-        case 0: /* MMU Command */
-            switch (value >> 5) {
-            case 0: /* no-op */
-                break;
-            case 1: /* Allocate for TX.  */
-                s->tx_alloc = 0x80;
-                s->int_level &= ~INT_ALLOC;
-                smc91c111_update(s);
-                smc91c111_tx_alloc(s);
-                break;
-            case 2: /* Reset MMU.  */
-                s->allocated = 0;
-                s->tx_fifo_len = 0;
-                s->tx_fifo_done_len = 0;
-                s->rx_fifo_len = 0;
-                s->tx_alloc = 0;
-                break;
-            case 3: /* Remove from RX FIFO.  */
-                smc91c111_pop_rx_fifo(s);
-                break;
-            case 4: /* Remove from RX FIFO and release.  */
-                if (s->rx_fifo_len > 0) {
-                    smc91c111_release_packet(s, s->rx_fifo[0]);
-                }
-                smc91c111_pop_rx_fifo(s);
-                break;
-            case 5: /* Release.  */
-                smc91c111_release_packet(s, s->packet_num);
-                break;
-            case 6: /* Add to TX FIFO.  */
-                smc91c111_queue_tx(s, s->packet_num);
-                break;
-            case 7: /* Reset TX FIFO.  */
-                s->tx_fifo_len = 0;
-                s->tx_fifo_done_len = 0;
-                break;
-            }
-            return;
-        case 1:
-            /* Ignore.  */
-            return;
-        case 2: /* Packet Number Register */
-            s->packet_num = value;
-            return;
-        case 3: case 4: case 5:
-            /* Should be readonly, but linux writes to them anyway. Ignore.  */
-            return;
-        case 6: /* Pointer */
-            SET_LOW(ptr, value);
-            return;
-        case 7:
-            SET_HIGH(ptr, value);
-            return;
-        case 8: case 9: case 10: case 11: /* Data */
-            {
-                int p;
-                int n;
-
-                if (s->ptr & 0x8000)
-                    n = s->rx_fifo[0];
-                else
-                    n = s->packet_num;
-                p = s->ptr & 0x07ff;
-                if (s->ptr & 0x4000) {
-                    s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff);
-                } else {
-                    p += (offset & 3);
-                }
-                s->data[n][p] = value;
-            }
-            return;
-        case 12: /* Interrupt ACK.  */
-            s->int_level &= ~(value & 0xd6);
-            if (value & INT_TX)
-                smc91c111_pop_tx_fifo_done(s);
-            smc91c111_update(s);
-            return;
-        case 13: /* Interrupt mask.  */
-            s->int_mask = value;
-            smc91c111_update(s);
-            return;
-        }
-        break;
-
-    case 3:
-        switch (offset) {
-        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
-            /* Multicast table.  */
-            /* Not implemented.  */
-            return;
-        case 8: case 9: /* Management Interface.  */
-            /* Not implemented.  */
-            return;
-        case 12: /* Early receive.  */
-            s->ercv = value & 0x1f;
-            return;
-        case 13:
-            /* Ignore.  */
-            return;
-        }
-        break;
-    }
-    hw_error("smc91c111_write: Bad reg %d:%x\n", s->bank, (int)offset);
-}
-
-static uint32_t smc91c111_readb(void *opaque, hwaddr offset)
-{
-    smc91c111_state *s = (smc91c111_state *)opaque;
-
-    offset = offset & 0xf;
-    if (offset == 14) {
-        return s->bank;
-    }
-    if (offset == 15)
-        return 0x33;
-    switch (s->bank) {
-    case 0:
-        switch (offset) {
-        case 0: /* TCR */
-            return s->tcr & 0xff;
-        case 1:
-            return s->tcr >> 8;
-        case 2: /* EPH Status */
-            return 0;
-        case 3:
-            return 0x40;
-        case 4: /* RCR */
-            return s->rcr & 0xff;
-        case 5:
-            return s->rcr >> 8;
-        case 6: /* Counter */
-        case 7:
-            /* Not implemented.  */
-            return 0;
-        case 8: /* Memory size.  */
-            return NUM_PACKETS;
-        case 9: /* Free memory available.  */
-            {
-                int i;
-                int n;
-                n = 0;
-                for (i = 0; i < NUM_PACKETS; i++) {
-                    if (s->allocated & (1 << i))
-                        n++;
-                }
-                return n;
-            }
-        case 10: case 11: /* RPCR */
-            /* Not implemented.  */
-            return 0;
-        case 12: case 13: /* Reserved */
-            return 0;
-        }
-        break;
-
-    case 1:
-        switch (offset) {
-        case 0: /* CONFIG */
-            return s->cr & 0xff;
-        case 1:
-            return s->cr >> 8;
-        case 2: case 3: /* BASE */
-            /* Not implemented.  */
-            return 0;
-        case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
-            return s->conf.macaddr.a[offset - 4];
-        case 10: /* General Purpose */
-            return s->gpr & 0xff;
-        case 11:
-            return s->gpr >> 8;
-        case 12: /* Control */
-            return s->ctr & 0xff;
-        case 13:
-            return s->ctr >> 8;
-        }
-        break;
-
-    case 2:
-        switch (offset) {
-        case 0: case 1: /* MMUCR Busy bit.  */
-            return 0;
-        case 2: /* Packet Number.  */
-            return s->packet_num;
-        case 3: /* Allocation Result.  */
-            return s->tx_alloc;
-        case 4: /* TX FIFO */
-            if (s->tx_fifo_done_len == 0)
-                return 0x80;
-            else
-                return s->tx_fifo_done[0];
-        case 5: /* RX FIFO */
-            if (s->rx_fifo_len == 0)
-                return 0x80;
-            else
-                return s->rx_fifo[0];
-        case 6: /* Pointer */
-            return s->ptr & 0xff;
-        case 7:
-            return (s->ptr >> 8) & 0xf7;
-        case 8: case 9: case 10: case 11: /* Data */
-            {
-                int p;
-                int n;
-
-                if (s->ptr & 0x8000)
-                    n = s->rx_fifo[0];
-                else
-                    n = s->packet_num;
-                p = s->ptr & 0x07ff;
-                if (s->ptr & 0x4000) {
-                    s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff);
-                } else {
-                    p += (offset & 3);
-                }
-                return s->data[n][p];
-            }
-        case 12: /* Interrupt status.  */
-            return s->int_level;
-        case 13: /* Interrupt mask.  */
-            return s->int_mask;
-        }
-        break;
-
-    case 3:
-        switch (offset) {
-        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
-            /* Multicast table.  */
-            /* Not implemented.  */
-            return 0;
-        case 8: /* Management Interface.  */
-            /* Not implemented.  */
-            return 0x30;
-        case 9:
-            return 0x33;
-        case 10: /* Revision.  */
-            return 0x91;
-        case 11:
-            return 0x33;
-        case 12:
-            return s->ercv;
-        case 13:
-            return 0;
-        }
-        break;
-    }
-    hw_error("smc91c111_read: Bad reg %d:%x\n", s->bank, (int)offset);
-    return 0;
-}
-
-static void smc91c111_writew(void *opaque, hwaddr offset,
-                             uint32_t value)
-{
-    smc91c111_writeb(opaque, offset, value & 0xff);
-    smc91c111_writeb(opaque, offset + 1, value >> 8);
-}
-
-static void smc91c111_writel(void *opaque, hwaddr offset,
-                             uint32_t value)
-{
-    /* 32-bit writes to offset 0xc only actually write to the bank select
-       register (offset 0xe)  */
-    if (offset != 0xc)
-        smc91c111_writew(opaque, offset, value & 0xffff);
-    smc91c111_writew(opaque, offset + 2, value >> 16);
-}
-
-static uint32_t smc91c111_readw(void *opaque, hwaddr offset)
-{
-    uint32_t val;
-    val = smc91c111_readb(opaque, offset);
-    val |= smc91c111_readb(opaque, offset + 1) << 8;
-    return val;
-}
-
-static uint32_t smc91c111_readl(void *opaque, hwaddr offset)
-{
-    uint32_t val;
-    val = smc91c111_readw(opaque, offset);
-    val |= smc91c111_readw(opaque, offset + 2) << 16;
-    return val;
-}
-
-static int smc91c111_can_receive(NetClientState *nc)
-{
-    smc91c111_state *s = qemu_get_nic_opaque(nc);
-
-    if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
-        return 1;
-    if (s->allocated == (1 << NUM_PACKETS) - 1)
-        return 0;
-    return 1;
-}
-
-static ssize_t smc91c111_receive(NetClientState *nc, const uint8_t *buf, size_t size)
-{
-    smc91c111_state *s = qemu_get_nic_opaque(nc);
-    int status;
-    int packetsize;
-    uint32_t crc;
-    int packetnum;
-    uint8_t *p;
-
-    if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
-        return -1;
-    /* Short packets are padded with zeros.  Receiving a packet
-       < 64 bytes long is considered an error condition.  */
-    if (size < 64)
-        packetsize = 64;
-    else
-        packetsize = (size & ~1);
-    packetsize += 6;
-    crc = (s->rcr & RCR_STRIP_CRC) == 0;
-    if (crc)
-        packetsize += 4;
-    /* TODO: Flag overrun and receive errors.  */
-    if (packetsize > 2048)
-        return -1;
-    packetnum = smc91c111_allocate_packet(s);
-    if (packetnum == 0x80)
-        return -1;
-    s->rx_fifo[s->rx_fifo_len++] = packetnum;
-
-    p = &s->data[packetnum][0];
-    /* ??? Multicast packets?  */
-    status = 0;
-    if (size > 1518)
-        status |= RS_TOOLONG;
-    if (size & 1)
-        status |= RS_ODDFRAME;
-    *(p++) = status & 0xff;
-    *(p++) = status >> 8;
-    *(p++) = packetsize & 0xff;
-    *(p++) = packetsize >> 8;
-    memcpy(p, buf, size & ~1);
-    p += (size & ~1);
-    /* Pad short packets.  */
-    if (size < 64) {
-        int pad;
-
-        if (size & 1)
-            *(p++) = buf[size - 1];
-        pad = 64 - size;
-        memset(p, 0, pad);
-        p += pad;
-        size = 64;
-    }
-    /* It's not clear if the CRC should go before or after the last byte in
-       odd sized packets.  Linux disables the CRC, so that's no help.
-       The pictures in the documentation show the CRC aligned on a 16-bit
-       boundary before the last odd byte, so that's what we do.  */
-    if (crc) {
-        crc = crc32(~0, buf, size);
-        *(p++) = crc & 0xff; crc >>= 8;
-        *(p++) = crc & 0xff; crc >>= 8;
-        *(p++) = crc & 0xff; crc >>= 8;
-        *(p++) = crc & 0xff;
-    }
-    if (size & 1) {
-        *(p++) = buf[size - 1];
-        *p = 0x60;
-    } else {
-        *(p++) = 0;
-        *p = 0x40;
-    }
-    /* TODO: Raise early RX interrupt?  */
-    s->int_level |= INT_RCV;
-    smc91c111_update(s);
-
-    return size;
-}
-
-static const MemoryRegionOps smc91c111_mem_ops = {
-    /* The special case for 32 bit writes to 0xc means we can't just
-     * set .impl.min/max_access_size to 1, unfortunately
-     */
-    .old_mmio = {
-        .read = { smc91c111_readb, smc91c111_readw, smc91c111_readl, },
-        .write = { smc91c111_writeb, smc91c111_writew, smc91c111_writel, },
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void smc91c111_cleanup(NetClientState *nc)
-{
-    smc91c111_state *s = qemu_get_nic_opaque(nc);
-
-    s->nic = NULL;
-}
-
-static NetClientInfo net_smc91c111_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
-    .size = sizeof(NICState),
-    .can_receive = smc91c111_can_receive,
-    .receive = smc91c111_receive,
-    .cleanup = smc91c111_cleanup,
-};
-
-static int smc91c111_init1(SysBusDevice *dev)
-{
-    smc91c111_state *s = FROM_SYSBUS(smc91c111_state, dev);
-    memory_region_init_io(&s->mmio, &smc91c111_mem_ops, s,
-                          "smc91c111-mmio", 16);
-    sysbus_init_mmio(dev, &s->mmio);
-    sysbus_init_irq(dev, &s->irq);
-    qemu_macaddr_default_if_unset(&s->conf.macaddr);
-    s->nic = qemu_new_nic(&net_smc91c111_info, &s->conf,
-                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
-    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
-    /* ??? Save/restore.  */
-    return 0;
-}
-
-static Property smc91c111_properties[] = {
-    DEFINE_NIC_PROPERTIES(smc91c111_state, conf),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void smc91c111_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = smc91c111_init1;
-    dc->reset = smc91c111_reset;
-    dc->vmsd = &vmstate_smc91c111;
-    dc->props = smc91c111_properties;
-}
-
-static const TypeInfo smc91c111_info = {
-    .name          = "smc91c111",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(smc91c111_state),
-    .class_init    = smc91c111_class_init,
-};
-
-static void smc91c111_register_types(void)
-{
-    type_register_static(&smc91c111_info);
-}
-
-/* Legacy helper function.  Should go away when machine config files are
-   implemented.  */
-void smc91c111_init(NICInfo *nd, uint32_t base, qemu_irq irq)
-{
-    DeviceState *dev;
-    SysBusDevice *s;
-
-    qemu_check_nic_model(nd, "smc91c111");
-    dev = qdev_create(NULL, "smc91c111");
-    qdev_set_nic_properties(dev, nd);
-    qdev_init_nofail(dev);
-    s = SYS_BUS_DEVICE(dev);
-    sysbus_mmio_map(s, 0, base);
-    sysbus_connect_irq(s, 0, irq);
-}
-
-type_init(smc91c111_register_types)
diff --git a/hw/ssd0303.c b/hw/ssd0303.c
deleted file mode 100644 (file)
index 183a878..0000000
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * SSD0303 OLED controller with OSRAM Pictiva 96x16 display.
- *
- * Copyright (c) 2006-2007 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the GPL.
- */
-
-/* The controller can support a variety of different displays, but we only
-   implement one.  Most of the commends relating to brightness and geometry
-   setup are ignored. */
-#include "hw/i2c/i2c.h"
-#include "ui/console.h"
-
-//#define DEBUG_SSD0303 1
-
-#ifdef DEBUG_SSD0303
-#define DPRINTF(fmt, ...) \
-do { printf("ssd0303: " fmt , ## __VA_ARGS__); } while (0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "ssd0303: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "ssd0303: error: " fmt , ## __VA_ARGS__);} while (0)
-#endif
-
-/* Scaling factor for pixels.  */
-#define MAGNIFY 4
-
-enum ssd0303_mode
-{
-    SSD0303_IDLE,
-    SSD0303_DATA,
-    SSD0303_CMD
-};
-
-enum ssd0303_cmd {
-    SSD0303_CMD_NONE,
-    SSD0303_CMD_SKIP1
-};
-
-typedef struct {
-    I2CSlave i2c;
-    QemuConsole *con;
-    int row;
-    int col;
-    int start_line;
-    int mirror;
-    int flash;
-    int enabled;
-    int inverse;
-    int redraw;
-    enum ssd0303_mode mode;
-    enum ssd0303_cmd cmd_state;
-    uint8_t framebuffer[132*8];
-} ssd0303_state;
-
-static int ssd0303_recv(I2CSlave *i2c)
-{
-    BADF("Reads not implemented\n");
-    return -1;
-}
-
-static int ssd0303_send(I2CSlave *i2c, uint8_t data)
-{
-    ssd0303_state *s = (ssd0303_state *)i2c;
-    enum ssd0303_cmd old_cmd_state;
-    switch (s->mode) {
-    case SSD0303_IDLE:
-        DPRINTF("byte 0x%02x\n", data);
-        if (data == 0x80)
-            s->mode = SSD0303_CMD;
-        else if (data == 0x40)
-            s->mode = SSD0303_DATA;
-        else
-            BADF("Unexpected byte 0x%x\n", data);
-        break;
-    case SSD0303_DATA:
-        DPRINTF("data 0x%02x\n", data);
-        if (s->col < 132) {
-            s->framebuffer[s->col + s->row * 132] = data;
-            s->col++;
-            s->redraw = 1;
-        }
-        break;
-    case SSD0303_CMD:
-        old_cmd_state = s->cmd_state;
-        s->cmd_state = SSD0303_CMD_NONE;
-        switch (old_cmd_state) {
-        case SSD0303_CMD_NONE:
-            DPRINTF("cmd 0x%02x\n", data);
-            s->mode = SSD0303_IDLE;
-            switch (data) {
-            case 0x00 ... 0x0f: /* Set lower column address.  */
-                s->col = (s->col & 0xf0) | (data & 0xf);
-                break;
-            case 0x10 ... 0x20: /* Set higher column address.  */
-                s->col = (s->col & 0x0f) | ((data & 0xf) << 4);
-                break;
-            case 0x40 ... 0x7f: /* Set start line.  */
-                s->start_line = 0;
-                break;
-            case 0x81: /* Set contrast (Ignored).  */
-                s->cmd_state = SSD0303_CMD_SKIP1;
-                break;
-            case 0xa0: /* Mirror off.  */
-                s->mirror = 0;
-                break;
-            case 0xa1: /* Mirror off.  */
-                s->mirror = 1;
-                break;
-            case 0xa4: /* Entire display off.  */
-                s->flash = 0;
-                break;
-            case 0xa5: /* Entire display on.  */
-                s->flash = 1;
-                break;
-            case 0xa6: /* Inverse off.  */
-                s->inverse = 0;
-                break;
-            case 0xa7: /* Inverse on.  */
-                s->inverse = 1;
-                break;
-            case 0xa8: /* Set multiplied ratio (Ignored).  */
-                s->cmd_state = SSD0303_CMD_SKIP1;
-                break;
-            case 0xad: /* DC-DC power control.  */
-                s->cmd_state = SSD0303_CMD_SKIP1;
-                break;
-            case 0xae: /* Display off.  */
-                s->enabled = 0;
-                break;
-            case 0xaf: /* Display on.  */
-                s->enabled = 1;
-                break;
-            case 0xb0 ... 0xbf: /* Set Page address.  */
-                s->row = data & 7;
-                break;
-            case 0xc0 ... 0xc8: /* Set COM output direction (Ignored).  */
-                break;
-            case 0xd3: /* Set display offset (Ignored).  */
-                s->cmd_state = SSD0303_CMD_SKIP1;
-                break;
-            case 0xd5: /* Set display clock (Ignored).  */
-                s->cmd_state = SSD0303_CMD_SKIP1;
-                break;
-            case 0xd8: /* Set color and power mode (Ignored).  */
-                s->cmd_state = SSD0303_CMD_SKIP1;
-                break;
-            case 0xd9: /* Set pre-charge period (Ignored).  */
-                s->cmd_state = SSD0303_CMD_SKIP1;
-                break;
-            case 0xda: /* Set COM pin configuration (Ignored).  */
-                s->cmd_state = SSD0303_CMD_SKIP1;
-                break;
-            case 0xdb: /* Set VCOM dselect level (Ignored).  */
-                s->cmd_state = SSD0303_CMD_SKIP1;
-                break;
-            case 0xe3: /* no-op.  */
-                break;
-            default:
-                BADF("Unknown command: 0x%x\n", data);
-            }
-            break;
-        case SSD0303_CMD_SKIP1:
-            DPRINTF("skip 0x%02x\n", data);
-            break;
-        }
-        break;
-    }
-    return 0;
-}
-
-static void ssd0303_event(I2CSlave *i2c, enum i2c_event event)
-{
-    ssd0303_state *s = (ssd0303_state *)i2c;
-    switch (event) {
-    case I2C_FINISH:
-        s->mode = SSD0303_IDLE;
-        break;
-    case I2C_START_RECV:
-    case I2C_START_SEND:
-    case I2C_NACK:
-        /* Nothing to do.  */
-        break;
-    }
-}
-
-static void ssd0303_update_display(void *opaque)
-{
-    ssd0303_state *s = (ssd0303_state *)opaque;
-    DisplaySurface *surface = qemu_console_surface(s->con);
-    uint8_t *dest;
-    uint8_t *src;
-    int x;
-    int y;
-    int line;
-    char *colors[2];
-    char colortab[MAGNIFY * 8];
-    int dest_width;
-    uint8_t mask;
-
-    if (!s->redraw)
-        return;
-
-    switch (surface_bits_per_pixel(surface)) {
-    case 0:
-        return;
-    case 15:
-        dest_width = 2;
-        break;
-    case 16:
-        dest_width = 2;
-        break;
-    case 24:
-        dest_width = 3;
-        break;
-    case 32:
-        dest_width = 4;
-        break;
-    default:
-        BADF("Bad color depth\n");
-        return;
-    }
-    dest_width *= MAGNIFY;
-    memset(colortab, 0xff, dest_width);
-    memset(colortab + dest_width, 0, dest_width);
-    if (s->flash) {
-        colors[0] = colortab;
-        colors[1] = colortab;
-    } else if (s->inverse) {
-        colors[0] = colortab;
-        colors[1] = colortab + dest_width;
-    } else {
-        colors[0] = colortab + dest_width;
-        colors[1] = colortab;
-    }
-    dest = surface_data(surface);
-    for (y = 0; y < 16; y++) {
-        line = (y + s->start_line) & 63;
-        src = s->framebuffer + 132 * (line >> 3) + 36;
-        mask = 1 << (line & 7);
-        for (x = 0; x < 96; x++) {
-            memcpy(dest, colors[(*src & mask) != 0], dest_width);
-            dest += dest_width;
-            src++;
-        }
-        for (x = 1; x < MAGNIFY; x++) {
-            memcpy(dest, dest - dest_width * 96, dest_width * 96);
-            dest += dest_width * 96;
-        }
-    }
-    s->redraw = 0;
-    dpy_gfx_update(s->con, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY);
-}
-
-static void ssd0303_invalidate_display(void * opaque)
-{
-    ssd0303_state *s = (ssd0303_state *)opaque;
-    s->redraw = 1;
-}
-
-static const VMStateDescription vmstate_ssd0303 = {
-    .name = "ssd0303_oled",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField []) {
-        VMSTATE_INT32(row, ssd0303_state),
-        VMSTATE_INT32(col, ssd0303_state),
-        VMSTATE_INT32(start_line, ssd0303_state),
-        VMSTATE_INT32(mirror, ssd0303_state),
-        VMSTATE_INT32(flash, ssd0303_state),
-        VMSTATE_INT32(enabled, ssd0303_state),
-        VMSTATE_INT32(inverse, ssd0303_state),
-        VMSTATE_INT32(redraw, ssd0303_state),
-        VMSTATE_UINT32(mode, ssd0303_state),
-        VMSTATE_UINT32(cmd_state, ssd0303_state),
-        VMSTATE_BUFFER(framebuffer, ssd0303_state),
-        VMSTATE_I2C_SLAVE(i2c, ssd0303_state),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int ssd0303_init(I2CSlave *i2c)
-{
-    ssd0303_state *s = FROM_I2C_SLAVE(ssd0303_state, i2c);
-
-    s->con = graphic_console_init(ssd0303_update_display,
-                                  ssd0303_invalidate_display,
-                                  NULL, NULL, s);
-    qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY);
-    return 0;
-}
-
-static void ssd0303_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
-
-    k->init = ssd0303_init;
-    k->event = ssd0303_event;
-    k->recv = ssd0303_recv;
-    k->send = ssd0303_send;
-    dc->vmsd = &vmstate_ssd0303;
-}
-
-static const TypeInfo ssd0303_info = {
-    .name          = "ssd0303",
-    .parent        = TYPE_I2C_SLAVE,
-    .instance_size = sizeof(ssd0303_state),
-    .class_init    = ssd0303_class_init,
-};
-
-static void ssd0303_register_types(void)
-{
-    type_register_static(&ssd0303_info);
-}
-
-type_init(ssd0303_register_types)
diff --git a/hw/ssd0323.c b/hw/ssd0323.c
deleted file mode 100644 (file)
index 5cf2f70..0000000
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- * SSD0323 OLED controller with OSRAM Pictiva 128x64 display.
- *
- * Copyright (c) 2006-2007 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the GPL.
- */
-
-/* The controller can support a variety of different displays, but we only
-   implement one.  Most of the commends relating to brightness and geometry
-   setup are ignored. */
-#include "hw/ssi.h"
-#include "ui/console.h"
-
-//#define DEBUG_SSD0323 1
-
-#ifdef DEBUG_SSD0323
-#define DPRINTF(fmt, ...) \
-do { printf("ssd0323: " fmt , ## __VA_ARGS__); } while (0)
-#define BADF(fmt, ...) \
-do { \
-    fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__); abort(); \
-} while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__);} while (0)
-#endif
-
-/* Scaling factor for pixels.  */
-#define MAGNIFY 4
-
-#define REMAP_SWAP_COLUMN 0x01
-#define REMAP_SWAP_NYBBLE 0x02
-#define REMAP_VERTICAL    0x04
-#define REMAP_SWAP_COM    0x10
-#define REMAP_SPLIT_COM   0x40
-
-enum ssd0323_mode
-{
-    SSD0323_CMD,
-    SSD0323_DATA
-};
-
-typedef struct {
-    SSISlave ssidev;
-    QemuConsole *con;
-
-    int cmd_len;
-    int cmd;
-    int cmd_data[8];
-    int row;
-    int row_start;
-    int row_end;
-    int col;
-    int col_start;
-    int col_end;
-    int redraw;
-    int remap;
-    enum ssd0323_mode mode;
-    uint8_t framebuffer[128 * 80 / 2];
-} ssd0323_state;
-
-static uint32_t ssd0323_transfer(SSISlave *dev, uint32_t data)
-{
-    ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, dev);
-
-    switch (s->mode) {
-    case SSD0323_DATA:
-        DPRINTF("data 0x%02x\n", data);
-        s->framebuffer[s->col + s->row * 64] = data;
-        if (s->remap & REMAP_VERTICAL) {
-            s->row++;
-            if (s->row > s->row_end) {
-                s->row = s->row_start;
-                s->col++;
-            }
-            if (s->col > s->col_end) {
-                s->col = s->col_start;
-            }
-        } else {
-            s->col++;
-            if (s->col > s->col_end) {
-                s->row++;
-                s->col = s->col_start;
-            }
-            if (s->row > s->row_end) {
-                s->row = s->row_start;
-            }
-        }
-        s->redraw = 1;
-        break;
-    case SSD0323_CMD:
-        DPRINTF("cmd 0x%02x\n", data);
-        if (s->cmd_len == 0) {
-            s->cmd = data;
-        } else {
-            s->cmd_data[s->cmd_len - 1] = data;
-        }
-        s->cmd_len++;
-        switch (s->cmd) {
-#define DATA(x) if (s->cmd_len <= (x)) return 0
-        case 0x15: /* Set column.  */
-            DATA(2);
-            s->col = s->col_start = s->cmd_data[0] % 64;
-            s->col_end = s->cmd_data[1] % 64;
-            break;
-        case 0x75: /* Set row.  */
-            DATA(2);
-            s->row = s->row_start = s->cmd_data[0] % 80;
-            s->row_end = s->cmd_data[1] % 80;
-            break;
-        case 0x81: /* Set contrast */
-            DATA(1);
-            break;
-        case 0x84: case 0x85: case 0x86: /* Max current.  */
-            DATA(0);
-            break;
-        case 0xa0: /* Set remapping.  */
-            /* FIXME: Implement this.  */
-            DATA(1);
-            s->remap = s->cmd_data[0];
-            break;
-        case 0xa1: /* Set display start line.  */
-        case 0xa2: /* Set display offset.  */
-            /* FIXME: Implement these.  */
-            DATA(1);
-            break;
-        case 0xa4: /* Normal mode.  */
-        case 0xa5: /* All on.  */
-        case 0xa6: /* All off.  */
-        case 0xa7: /* Inverse.  */
-            /* FIXME: Implement these.  */
-            DATA(0);
-            break;
-        case 0xa8: /* Set multiplex ratio.  */
-        case 0xad: /* Set DC-DC converter.  */
-            DATA(1);
-            /* Ignored.  Don't care.  */
-            break;
-        case 0xae: /* Display off.  */
-        case 0xaf: /* Display on.  */
-            DATA(0);
-            /* TODO: Implement power control.  */
-            break;
-        case 0xb1: /* Set phase length.  */
-        case 0xb2: /* Set row period.  */
-        case 0xb3: /* Set clock rate.  */
-        case 0xbc: /* Set precharge.  */
-        case 0xbe: /* Set VCOMH.  */
-        case 0xbf: /* Set segment low.  */
-            DATA(1);
-            /* Ignored.  Don't care.  */
-            break;
-        case 0xb8: /* Set grey scale table.  */
-            /* FIXME: Implement this.  */
-            DATA(8);
-            break;
-        case 0xe3: /* NOP.  */
-            DATA(0);
-            break;
-        case 0xff: /* Nasty hack because we don't handle chip selects
-                      properly.  */
-            break;
-        default:
-            BADF("Unknown command: 0x%x\n", data);
-        }
-        s->cmd_len = 0;
-        return 0;
-    }
-    return 0;
-}
-
-static void ssd0323_update_display(void *opaque)
-{
-    ssd0323_state *s = (ssd0323_state *)opaque;
-    DisplaySurface *surface = qemu_console_surface(s->con);
-    uint8_t *dest;
-    uint8_t *src;
-    int x;
-    int y;
-    int i;
-    int line;
-    char *colors[16];
-    char colortab[MAGNIFY * 64];
-    char *p;
-    int dest_width;
-
-    if (!s->redraw)
-        return;
-
-    switch (surface_bits_per_pixel(surface)) {
-    case 0:
-        return;
-    case 15:
-        dest_width = 2;
-        break;
-    case 16:
-        dest_width = 2;
-        break;
-    case 24:
-        dest_width = 3;
-        break;
-    case 32:
-        dest_width = 4;
-        break;
-    default:
-        BADF("Bad color depth\n");
-        return;
-    }
-    p = colortab;
-    for (i = 0; i < 16; i++) {
-        int n;
-        colors[i] = p;
-        switch (surface_bits_per_pixel(surface)) {
-        case 15:
-            n = i * 2 + (i >> 3);
-            p[0] = n | (n << 5);
-            p[1] = (n << 2) | (n >> 3);
-            break;
-        case 16:
-            n = i * 2 + (i >> 3);
-            p[0] = n | (n << 6) | ((n << 1) & 0x20);
-            p[1] = (n << 3) | (n >> 2);
-            break;
-        case 24:
-        case 32:
-            n = (i << 4) | i;
-            p[0] = p[1] = p[2] = n;
-            break;
-        default:
-            BADF("Bad color depth\n");
-            return;
-        }
-        p += dest_width;
-    }
-    /* TODO: Implement row/column remapping.  */
-    dest = surface_data(surface);
-    for (y = 0; y < 64; y++) {
-        line = y;
-        src = s->framebuffer + 64 * line;
-        for (x = 0; x < 64; x++) {
-            int val;
-            val = *src >> 4;
-            for (i = 0; i < MAGNIFY; i++) {
-                memcpy(dest, colors[val], dest_width);
-                dest += dest_width;
-            }
-            val = *src & 0xf;
-            for (i = 0; i < MAGNIFY; i++) {
-                memcpy(dest, colors[val], dest_width);
-                dest += dest_width;
-            }
-            src++;
-        }
-        for (i = 1; i < MAGNIFY; i++) {
-            memcpy(dest, dest - dest_width * MAGNIFY * 128,
-                   dest_width * 128 * MAGNIFY);
-            dest += dest_width * 128 * MAGNIFY;
-        }
-    }
-    s->redraw = 0;
-    dpy_gfx_update(s->con, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY);
-}
-
-static void ssd0323_invalidate_display(void * opaque)
-{
-    ssd0323_state *s = (ssd0323_state *)opaque;
-    s->redraw = 1;
-}
-
-/* Command/data input.  */
-static void ssd0323_cd(void *opaque, int n, int level)
-{
-    ssd0323_state *s = (ssd0323_state *)opaque;
-    DPRINTF("%s mode\n", level ? "Data" : "Command");
-    s->mode = level ? SSD0323_DATA : SSD0323_CMD;
-}
-
-static void ssd0323_save(QEMUFile *f, void *opaque)
-{
-    SSISlave *ss = SSI_SLAVE(opaque);
-    ssd0323_state *s = (ssd0323_state *)opaque;
-    int i;
-
-    qemu_put_be32(f, s->cmd_len);
-    qemu_put_be32(f, s->cmd);
-    for (i = 0; i < 8; i++)
-        qemu_put_be32(f, s->cmd_data[i]);
-    qemu_put_be32(f, s->row);
-    qemu_put_be32(f, s->row_start);
-    qemu_put_be32(f, s->row_end);
-    qemu_put_be32(f, s->col);
-    qemu_put_be32(f, s->col_start);
-    qemu_put_be32(f, s->col_end);
-    qemu_put_be32(f, s->redraw);
-    qemu_put_be32(f, s->remap);
-    qemu_put_be32(f, s->mode);
-    qemu_put_buffer(f, s->framebuffer, sizeof(s->framebuffer));
-
-    qemu_put_be32(f, ss->cs);
-}
-
-static int ssd0323_load(QEMUFile *f, void *opaque, int version_id)
-{
-    SSISlave *ss = SSI_SLAVE(opaque);
-    ssd0323_state *s = (ssd0323_state *)opaque;
-    int i;
-
-    if (version_id != 1)
-        return -EINVAL;
-
-    s->cmd_len = qemu_get_be32(f);
-    s->cmd = qemu_get_be32(f);
-    for (i = 0; i < 8; i++)
-        s->cmd_data[i] = qemu_get_be32(f);
-    s->row = qemu_get_be32(f);
-    s->row_start = qemu_get_be32(f);
-    s->row_end = qemu_get_be32(f);
-    s->col = qemu_get_be32(f);
-    s->col_start = qemu_get_be32(f);
-    s->col_end = qemu_get_be32(f);
-    s->redraw = qemu_get_be32(f);
-    s->remap = qemu_get_be32(f);
-    s->mode = qemu_get_be32(f);
-    qemu_get_buffer(f, s->framebuffer, sizeof(s->framebuffer));
-
-    ss->cs = qemu_get_be32(f);
-
-    return 0;
-}
-
-static int ssd0323_init(SSISlave *dev)
-{
-    ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, dev);
-
-    s->col_end = 63;
-    s->row_end = 79;
-    s->con = graphic_console_init(ssd0323_update_display,
-                                  ssd0323_invalidate_display,
-                                  NULL, NULL, s);
-    qemu_console_resize(s->con, 128 * MAGNIFY, 64 * MAGNIFY);
-
-    qdev_init_gpio_in(&dev->qdev, ssd0323_cd, 1);
-
-    register_savevm(&dev->qdev, "ssd0323_oled", -1, 1,
-                    ssd0323_save, ssd0323_load, s);
-    return 0;
-}
-
-static void ssd0323_class_init(ObjectClass *klass, void *data)
-{
-    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
-
-    k->init = ssd0323_init;
-    k->transfer = ssd0323_transfer;
-    k->cs_polarity = SSI_CS_HIGH;
-}
-
-static const TypeInfo ssd0323_info = {
-    .name          = "ssd0323",
-    .parent        = TYPE_SSI_SLAVE,
-    .instance_size = sizeof(ssd0323_state),
-    .class_init    = ssd0323_class_init,
-};
-
-static void ssd03232_register_types(void)
-{
-    type_register_static(&ssd0323_info);
-}
-
-type_init(ssd03232_register_types)
diff --git a/hw/ssi-sd.c b/hw/ssi-sd.c
deleted file mode 100644 (file)
index 4d3c4f6..0000000
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * SSI to SD card adapter.
- *
- * Copyright (c) 2007-2009 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the GNU GPL v2.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "sysemu/blockdev.h"
-#include "hw/ssi.h"
-#include "hw/sd.h"
-
-//#define DEBUG_SSI_SD 1
-
-#ifdef DEBUG_SSI_SD
-#define DPRINTF(fmt, ...) \
-do { printf("ssi_sd: " fmt , ## __VA_ARGS__); } while (0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "ssi_sd: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
-#else
-#define DPRINTF(fmt, ...) do {} while(0)
-#define BADF(fmt, ...) \
-do { fprintf(stderr, "ssi_sd: error: " fmt , ## __VA_ARGS__);} while (0)
-#endif
-
-typedef enum {
-    SSI_SD_CMD,
-    SSI_SD_CMDARG,
-    SSI_SD_RESPONSE,
-    SSI_SD_DATA_START,
-    SSI_SD_DATA_READ,
-} ssi_sd_mode;
-
-typedef struct {
-    SSISlave ssidev;
-    ssi_sd_mode mode;
-    int cmd;
-    uint8_t cmdarg[4];
-    uint8_t response[5];
-    int arglen;
-    int response_pos;
-    int stopping;
-    SDState *sd;
-} ssi_sd_state;
-
-/* State word bits.  */
-#define SSI_SDR_LOCKED          0x0001
-#define SSI_SDR_WP_ERASE        0x0002
-#define SSI_SDR_ERROR           0x0004
-#define SSI_SDR_CC_ERROR        0x0008
-#define SSI_SDR_ECC_FAILED      0x0010
-#define SSI_SDR_WP_VIOLATION    0x0020
-#define SSI_SDR_ERASE_PARAM     0x0040
-#define SSI_SDR_OUT_OF_RANGE    0x0080
-#define SSI_SDR_IDLE            0x0100
-#define SSI_SDR_ERASE_RESET     0x0200
-#define SSI_SDR_ILLEGAL_COMMAND 0x0400
-#define SSI_SDR_COM_CRC_ERROR   0x0800
-#define SSI_SDR_ERASE_SEQ_ERROR 0x1000
-#define SSI_SDR_ADDRESS_ERROR   0x2000
-#define SSI_SDR_PARAMETER_ERROR 0x4000
-
-static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val)
-{
-    ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, dev);
-
-    /* Special case: allow CMD12 (STOP TRANSMISSION) while reading data.  */
-    if (s->mode == SSI_SD_DATA_READ && val == 0x4d) {
-        s->mode = SSI_SD_CMD;
-        /* There must be at least one byte delay before the card responds.  */
-        s->stopping = 1;
-    }
-
-    switch (s->mode) {
-    case SSI_SD_CMD:
-        if (val == 0xff) {
-            DPRINTF("NULL command\n");
-            return 0xff;
-        }
-        s->cmd = val & 0x3f;
-        s->mode = SSI_SD_CMDARG;
-        s->arglen = 0;
-        return 0xff;
-    case SSI_SD_CMDARG:
-        if (s->arglen == 4) {
-            SDRequest request;
-            uint8_t longresp[16];
-            /* FIXME: Check CRC.  */
-            request.cmd = s->cmd;
-            request.arg = (s->cmdarg[0] << 24) | (s->cmdarg[1] << 16)
-                           | (s->cmdarg[2] << 8) | s->cmdarg[3];
-            DPRINTF("CMD%d arg 0x%08x\n", s->cmd, request.arg);
-            s->arglen = sd_do_command(s->sd, &request, longresp);
-            if (s->arglen <= 0) {
-                s->arglen = 1;
-                s->response[0] = 4;
-                DPRINTF("SD command failed\n");
-            } else if (s->cmd == 58) {
-                /* CMD58 returns R3 response (OCR)  */
-                DPRINTF("Returned OCR\n");
-                s->arglen = 5;
-                s->response[0] = 1;
-                memcpy(&s->response[1], longresp, 4);
-            } else if (s->arglen != 4) {
-                BADF("Unexpected response to cmd %d\n", s->cmd);
-                /* Illegal command is about as near as we can get.  */
-                s->arglen = 1;
-                s->response[0] = 4;
-            } else {
-                /* All other commands return status.  */
-                uint32_t cardstatus;
-                uint16_t status;
-                /* CMD13 returns a 2-byte statuse work. Other commands
-                   only return the first byte.  */
-                s->arglen = (s->cmd == 13) ? 2 : 1;
-                cardstatus = (longresp[0] << 24) | (longresp[1] << 16)
-                             | (longresp[2] << 8) | longresp[3];
-                status = 0;
-                if (((cardstatus >> 9) & 0xf) < 4)
-                    status |= SSI_SDR_IDLE;
-                if (cardstatus & ERASE_RESET)
-                    status |= SSI_SDR_ERASE_RESET;
-                if (cardstatus & ILLEGAL_COMMAND)
-                    status |= SSI_SDR_ILLEGAL_COMMAND;
-                if (cardstatus & COM_CRC_ERROR)
-                    status |= SSI_SDR_COM_CRC_ERROR;
-                if (cardstatus & ERASE_SEQ_ERROR)
-                    status |= SSI_SDR_ERASE_SEQ_ERROR;
-                if (cardstatus & ADDRESS_ERROR)
-                    status |= SSI_SDR_ADDRESS_ERROR;
-                if (cardstatus & CARD_IS_LOCKED)
-                    status |= SSI_SDR_LOCKED;
-                if (cardstatus & (LOCK_UNLOCK_FAILED | WP_ERASE_SKIP))
-                    status |= SSI_SDR_WP_ERASE;
-                if (cardstatus & SD_ERROR)
-                    status |= SSI_SDR_ERROR;
-                if (cardstatus & CC_ERROR)
-                    status |= SSI_SDR_CC_ERROR;
-                if (cardstatus & CARD_ECC_FAILED)
-                    status |= SSI_SDR_ECC_FAILED;
-                if (cardstatus & WP_VIOLATION)
-                    status |= SSI_SDR_WP_VIOLATION;
-                if (cardstatus & ERASE_PARAM)
-                    status |= SSI_SDR_ERASE_PARAM;
-                if (cardstatus & (OUT_OF_RANGE | CID_CSD_OVERWRITE))
-                    status |= SSI_SDR_OUT_OF_RANGE;
-                /* ??? Don't know what Parameter Error really means, so
-                   assume it's set if the second byte is nonzero.  */
-                if (status & 0xff)
-                    status |= SSI_SDR_PARAMETER_ERROR;
-                s->response[0] = status >> 8;
-                s->response[1] = status;
-                DPRINTF("Card status 0x%02x\n", status);
-            }
-            s->mode = SSI_SD_RESPONSE;
-            s->response_pos = 0;
-        } else {
-            s->cmdarg[s->arglen++] = val;
-        }
-        return 0xff;
-    case SSI_SD_RESPONSE:
-        if (s->stopping) {
-            s->stopping = 0;
-            return 0xff;
-        }
-        if (s->response_pos < s->arglen) {
-            DPRINTF("Response 0x%02x\n", s->response[s->response_pos]);
-            return s->response[s->response_pos++];
-        }
-        if (sd_data_ready(s->sd)) {
-            DPRINTF("Data read\n");
-            s->mode = SSI_SD_DATA_START;
-        } else {
-            DPRINTF("End of command\n");
-            s->mode = SSI_SD_CMD;
-        }
-        return 0xff;
-    case SSI_SD_DATA_START:
-        DPRINTF("Start read block\n");
-        s->mode = SSI_SD_DATA_READ;
-        return 0xfe;
-    case SSI_SD_DATA_READ:
-        val = sd_read_data(s->sd);
-        if (!sd_data_ready(s->sd)) {
-            DPRINTF("Data read end\n");
-            s->mode = SSI_SD_CMD;
-        }
-        return val;
-    }
-    /* Should never happen.  */
-    return 0xff;
-}
-
-static void ssi_sd_save(QEMUFile *f, void *opaque)
-{
-    SSISlave *ss = SSI_SLAVE(opaque);
-    ssi_sd_state *s = (ssi_sd_state *)opaque;
-    int i;
-
-    qemu_put_be32(f, s->mode);
-    qemu_put_be32(f, s->cmd);
-    for (i = 0; i < 4; i++)
-        qemu_put_be32(f, s->cmdarg[i]);
-    for (i = 0; i < 5; i++)
-        qemu_put_be32(f, s->response[i]);
-    qemu_put_be32(f, s->arglen);
-    qemu_put_be32(f, s->response_pos);
-    qemu_put_be32(f, s->stopping);
-
-    qemu_put_be32(f, ss->cs);
-}
-
-static int ssi_sd_load(QEMUFile *f, void *opaque, int version_id)
-{
-    SSISlave *ss = SSI_SLAVE(opaque);
-    ssi_sd_state *s = (ssi_sd_state *)opaque;
-    int i;
-
-    if (version_id != 1)
-        return -EINVAL;
-
-    s->mode = qemu_get_be32(f);
-    s->cmd = qemu_get_be32(f);
-    for (i = 0; i < 4; i++)
-        s->cmdarg[i] = qemu_get_be32(f);
-    for (i = 0; i < 5; i++)
-        s->response[i] = qemu_get_be32(f);
-    s->arglen = qemu_get_be32(f);
-    s->response_pos = qemu_get_be32(f);
-    s->stopping = qemu_get_be32(f);
-
-    ss->cs = qemu_get_be32(f);
-
-    return 0;
-}
-
-static int ssi_sd_init(SSISlave *dev)
-{
-    ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, dev);
-    DriveInfo *dinfo;
-
-    s->mode = SSI_SD_CMD;
-    dinfo = drive_get_next(IF_SD);
-    s->sd = sd_init(dinfo ? dinfo->bdrv : NULL, 1);
-    register_savevm(&dev->qdev, "ssi_sd", -1, 1, ssi_sd_save, ssi_sd_load, s);
-    return 0;
-}
-
-static void ssi_sd_class_init(ObjectClass *klass, void *data)
-{
-    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
-
-    k->init = ssi_sd_init;
-    k->transfer = ssi_sd_transfer;
-    k->cs_polarity = SSI_CS_LOW;
-}
-
-static const TypeInfo ssi_sd_info = {
-    .name          = "ssi-sd",
-    .parent        = TYPE_SSI_SLAVE,
-    .instance_size = sizeof(ssi_sd_state),
-    .class_init    = ssi_sd_class_init,
-};
-
-static void ssi_sd_register_types(void)
-{
-    type_register_static(&ssi_sd_info);
-}
-
-type_init(ssi_sd_register_types)
diff --git a/hw/ssi.c b/hw/ssi.c
deleted file mode 100644 (file)
index 1264d9d..0000000
--- a/hw/ssi.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * QEMU Synchronous Serial Interface support
- *
- * Copyright (c) 2009 CodeSourcery.
- * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
- * Copyright (c) 2012 PetaLogix Pty Ltd.
- * Written by Paul Brook
- *
- * This code is licensed under the GNU GPL v2.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include "hw/ssi.h"
-
-struct SSIBus {
-    BusState qbus;
-};
-
-#define TYPE_SSI_BUS "SSI"
-#define SSI_BUS(obj) OBJECT_CHECK(SSIBus, (obj), TYPE_SSI_BUS)
-
-static const TypeInfo ssi_bus_info = {
-    .name = TYPE_SSI_BUS,
-    .parent = TYPE_BUS,
-    .instance_size = sizeof(SSIBus),
-};
-
-static void ssi_cs_default(void *opaque, int n, int level)
-{
-    SSISlave *s = SSI_SLAVE(opaque);
-    bool cs = !!level;
-    assert(n == 0);
-    if (s->cs != cs) {
-        SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s);
-        if (ssc->set_cs) {
-            ssc->set_cs(s, cs);
-        }
-    }
-    s->cs = cs;
-}
-
-static uint32_t ssi_transfer_raw_default(SSISlave *dev, uint32_t val)
-{
-    SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(dev);
-
-    if ((dev->cs && ssc->cs_polarity == SSI_CS_HIGH) ||
-            (!dev->cs && ssc->cs_polarity == SSI_CS_LOW) ||
-            ssc->cs_polarity == SSI_CS_NONE) {
-        return ssc->transfer(dev, val);
-    }
-    return 0;
-}
-
-static int ssi_slave_init(DeviceState *dev)
-{
-    SSISlave *s = SSI_SLAVE(dev);
-    SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s);
-
-    if (ssc->transfer_raw == ssi_transfer_raw_default &&
-            ssc->cs_polarity != SSI_CS_NONE) {
-        qdev_init_gpio_in(&s->qdev, ssi_cs_default, 1);
-    }
-
-    return ssc->init(s);
-}
-
-static void ssi_slave_class_init(ObjectClass *klass, void *data)
-{
-    SSISlaveClass *ssc = SSI_SLAVE_CLASS(klass);
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    dc->init = ssi_slave_init;
-    dc->bus_type = TYPE_SSI_BUS;
-    if (!ssc->transfer_raw) {
-        ssc->transfer_raw = ssi_transfer_raw_default;
-    }
-}
-
-static const TypeInfo ssi_slave_info = {
-    .name = TYPE_SSI_SLAVE,
-    .parent = TYPE_DEVICE,
-    .class_init = ssi_slave_class_init,
-    .class_size = sizeof(SSISlaveClass),
-    .abstract = true,
-};
-
-DeviceState *ssi_create_slave_no_init(SSIBus *bus, const char *name)
-{
-    return qdev_create(&bus->qbus, name);
-}
-
-DeviceState *ssi_create_slave(SSIBus *bus, const char *name)
-{
-    DeviceState *dev = ssi_create_slave_no_init(bus, name);
-
-    qdev_init_nofail(dev);
-    return dev;
-}
-
-SSIBus *ssi_create_bus(DeviceState *parent, const char *name)
-{
-    BusState *bus;
-    bus = qbus_create(TYPE_SSI_BUS, parent, name);
-    return FROM_QBUS(SSIBus, bus);
-}
-
-uint32_t ssi_transfer(SSIBus *bus, uint32_t val)
-{
-    BusChild *kid;
-    SSISlaveClass *ssc;
-    uint32_t r = 0;
-
-    QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
-        SSISlave *slave = SSI_SLAVE(kid->child);
-        ssc = SSI_SLAVE_GET_CLASS(slave);
-        r |= ssc->transfer_raw(slave, val);
-    }
-
-    return r;
-}
-
-const VMStateDescription vmstate_ssi_slave = {
-    .name = "SSISlave",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_BOOL(cs, SSISlave),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void ssi_slave_register_types(void)
-{
-    type_register_static(&ssi_bus_info);
-    type_register_static(&ssi_slave_info);
-}
-
-type_init(ssi_slave_register_types)
-
-typedef struct SSIAutoConnectArg {
-    qemu_irq **cs_linep;
-    SSIBus *bus;
-} SSIAutoConnectArg;
-
-static int ssi_auto_connect_slave(Object *child, void *opaque)
-{
-    SSIAutoConnectArg *arg = opaque;
-    SSISlave *dev = (SSISlave *)object_dynamic_cast(child, TYPE_SSI_SLAVE);
-    qemu_irq cs_line;
-
-    if (!dev) {
-        return 0;
-    }
-
-    cs_line = qdev_get_gpio_in(DEVICE(dev), 0);
-    qdev_set_parent_bus(DEVICE(dev), &arg->bus->qbus);
-    **arg->cs_linep = cs_line;
-    (*arg->cs_linep)++;
-    return 0;
-}
-
-void ssi_auto_connect_slaves(DeviceState *parent, qemu_irq *cs_line,
-                             SSIBus *bus)
-{
-    SSIAutoConnectArg arg = {
-        .cs_linep = &cs_line,
-        .bus = bus
-    };
-
-    object_child_foreach(OBJECT(parent), ssi_auto_connect_slave, &arg);
-}
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..daada5c66ae2f4b9336555fcc340c937978f0849 100644 (file)
@@ -0,0 +1,2 @@
+common-obj-$(CONFIG_PL022) += pl022.o
+common-obj-$(CONFIG_SSI) += ssi.o
diff --git a/hw/ssi/pl022.c b/hw/ssi/pl022.c
new file mode 100644 (file)
index 0000000..536c216
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * Arm PrimeCell PL022 Synchronous Serial Port
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/ssi.h"
+
+//#define DEBUG_PL022 1
+
+#ifdef DEBUG_PL022
+#define DPRINTF(fmt, ...) \
+do { printf("pl022: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+#define PL022_CR1_LBM 0x01
+#define PL022_CR1_SSE 0x02
+#define PL022_CR1_MS  0x04
+#define PL022_CR1_SDO 0x08
+
+#define PL022_SR_TFE  0x01
+#define PL022_SR_TNF  0x02
+#define PL022_SR_RNE  0x04
+#define PL022_SR_RFF  0x08
+#define PL022_SR_BSY  0x10
+
+#define PL022_INT_ROR 0x01
+#define PL022_INT_RT  0x04
+#define PL022_INT_RX  0x04
+#define PL022_INT_TX  0x08
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    uint32_t cr0;
+    uint32_t cr1;
+    uint32_t bitmask;
+    uint32_t sr;
+    uint32_t cpsr;
+    uint32_t is;
+    uint32_t im;
+    /* The FIFO head points to the next empty entry.  */
+    int tx_fifo_head;
+    int rx_fifo_head;
+    int tx_fifo_len;
+    int rx_fifo_len;
+    uint16_t tx_fifo[8];
+    uint16_t rx_fifo[8];
+    qemu_irq irq;
+    SSIBus *ssi;
+} pl022_state;
+
+static const unsigned char pl022_id[8] =
+  { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl022_update(pl022_state *s)
+{
+    s->sr = 0;
+    if (s->tx_fifo_len == 0)
+        s->sr |= PL022_SR_TFE;
+    if (s->tx_fifo_len != 8)
+        s->sr |= PL022_SR_TNF;
+    if (s->rx_fifo_len != 0)
+        s->sr |= PL022_SR_RNE;
+    if (s->rx_fifo_len == 8)
+        s->sr |= PL022_SR_RFF;
+    if (s->tx_fifo_len)
+        s->sr |= PL022_SR_BSY;
+    s->is = 0;
+    if (s->rx_fifo_len >= 4)
+        s->is |= PL022_INT_RX;
+    if (s->tx_fifo_len <= 4)
+        s->is |= PL022_INT_TX;
+
+    qemu_set_irq(s->irq, (s->is & s->im) != 0);
+}
+
+static void pl022_xfer(pl022_state *s)
+{
+    int i;
+    int o;
+    int val;
+
+    if ((s->cr1 & PL022_CR1_SSE) == 0) {
+        pl022_update(s);
+        DPRINTF("Disabled\n");
+        return;
+    }
+
+    DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len);
+    i = (s->tx_fifo_head - s->tx_fifo_len) & 7;
+    o = s->rx_fifo_head;
+    /* ??? We do not emulate the line speed.
+       This may break some applications.  The are two problematic cases:
+        (a) A driver feeds data into the TX FIFO until it is full,
+         and only then drains the RX FIFO.  On real hardware the CPU can
+         feed data fast enough that the RX fifo never gets chance to overflow.
+        (b) A driver transmits data, deliberately allowing the RX FIFO to
+         overflow because it ignores the RX data anyway.
+
+       We choose to support (a) by stalling the transmit engine if it would
+       cause the RX FIFO to overflow.  In practice much transmit-only code
+       falls into (a) because it flushes the RX FIFO to determine when
+       the transfer has completed.  */
+    while (s->tx_fifo_len && s->rx_fifo_len < 8) {
+        DPRINTF("xfer\n");
+        val = s->tx_fifo[i];
+        if (s->cr1 & PL022_CR1_LBM) {
+            /* Loopback mode.  */
+        } else {
+            val = ssi_transfer(s->ssi, val);
+        }
+        s->rx_fifo[o] = val & s->bitmask;
+        i = (i + 1) & 7;
+        o = (o + 1) & 7;
+        s->tx_fifo_len--;
+        s->rx_fifo_len++;
+    }
+    s->rx_fifo_head = o;
+    pl022_update(s);
+}
+
+static uint64_t pl022_read(void *opaque, hwaddr offset,
+                           unsigned size)
+{
+    pl022_state *s = (pl022_state *)opaque;
+    int val;
+
+    if (offset >= 0xfe0 && offset < 0x1000) {
+        return pl022_id[(offset - 0xfe0) >> 2];
+    }
+    switch (offset) {
+    case 0x00: /* CR0 */
+      return s->cr0;
+    case 0x04: /* CR1 */
+      return s->cr1;
+    case 0x08: /* DR */
+        if (s->rx_fifo_len) {
+            val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7];
+            DPRINTF("RX %02x\n", val);
+            s->rx_fifo_len--;
+            pl022_xfer(s);
+        } else {
+            val = 0;
+        }
+        return val;
+    case 0x0c: /* SR */
+        return s->sr;
+    case 0x10: /* CPSR */
+        return s->cpsr;
+    case 0x14: /* IMSC */
+        return s->im;
+    case 0x18: /* RIS */
+        return s->is;
+    case 0x1c: /* MIS */
+        return s->im & s->is;
+    case 0x20: /* DMACR */
+        /* Not implemented.  */
+        return 0;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl022_read: Bad offset %x\n", (int)offset);
+        return 0;
+    }
+}
+
+static void pl022_write(void *opaque, hwaddr offset,
+                        uint64_t value, unsigned size)
+{
+    pl022_state *s = (pl022_state *)opaque;
+
+    switch (offset) {
+    case 0x00: /* CR0 */
+        s->cr0 = value;
+        /* Clock rate and format are ignored.  */
+        s->bitmask = (1 << ((value & 15) + 1)) - 1;
+        break;
+    case 0x04: /* CR1 */
+        s->cr1 = value;
+        if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
+                   == (PL022_CR1_MS | PL022_CR1_SSE)) {
+            BADF("SPI slave mode not implemented\n");
+        }
+        pl022_xfer(s);
+        break;
+    case 0x08: /* DR */
+        if (s->tx_fifo_len < 8) {
+            DPRINTF("TX %02x\n", (unsigned)value);
+            s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
+            s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
+            s->tx_fifo_len++;
+            pl022_xfer(s);
+        }
+        break;
+    case 0x10: /* CPSR */
+        /* Prescaler.  Ignored.  */
+        s->cpsr = value & 0xff;
+        break;
+    case 0x14: /* IMSC */
+        s->im = value;
+        pl022_update(s);
+        break;
+    case 0x20: /* DMACR */
+        if (value) {
+            qemu_log_mask(LOG_UNIMP, "pl022: DMA not implemented\n");
+        }
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl022_write: Bad offset %x\n", (int)offset);
+    }
+}
+
+static void pl022_reset(pl022_state *s)
+{
+    s->rx_fifo_len = 0;
+    s->tx_fifo_len = 0;
+    s->im = 0;
+    s->is = PL022_INT_TX;
+    s->sr = PL022_SR_TFE | PL022_SR_TNF;
+}
+
+static const MemoryRegionOps pl022_ops = {
+    .read = pl022_read,
+    .write = pl022_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_pl022 = {
+    .name = "pl022_ssp",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32(cr0, pl022_state),
+        VMSTATE_UINT32(cr1, pl022_state),
+        VMSTATE_UINT32(bitmask, pl022_state),
+        VMSTATE_UINT32(sr, pl022_state),
+        VMSTATE_UINT32(cpsr, pl022_state),
+        VMSTATE_UINT32(is, pl022_state),
+        VMSTATE_UINT32(im, pl022_state),
+        VMSTATE_INT32(tx_fifo_head, pl022_state),
+        VMSTATE_INT32(rx_fifo_head, pl022_state),
+        VMSTATE_INT32(tx_fifo_len, pl022_state),
+        VMSTATE_INT32(rx_fifo_len, pl022_state),
+        VMSTATE_UINT16(tx_fifo[0], pl022_state),
+        VMSTATE_UINT16(rx_fifo[0], pl022_state),
+        VMSTATE_UINT16(tx_fifo[1], pl022_state),
+        VMSTATE_UINT16(rx_fifo[1], pl022_state),
+        VMSTATE_UINT16(tx_fifo[2], pl022_state),
+        VMSTATE_UINT16(rx_fifo[2], pl022_state),
+        VMSTATE_UINT16(tx_fifo[3], pl022_state),
+        VMSTATE_UINT16(rx_fifo[3], pl022_state),
+        VMSTATE_UINT16(tx_fifo[4], pl022_state),
+        VMSTATE_UINT16(rx_fifo[4], pl022_state),
+        VMSTATE_UINT16(tx_fifo[5], pl022_state),
+        VMSTATE_UINT16(rx_fifo[5], pl022_state),
+        VMSTATE_UINT16(tx_fifo[6], pl022_state),
+        VMSTATE_UINT16(rx_fifo[6], pl022_state),
+        VMSTATE_UINT16(tx_fifo[7], pl022_state),
+        VMSTATE_UINT16(rx_fifo[7], pl022_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int pl022_init(SysBusDevice *dev)
+{
+    pl022_state *s = FROM_SYSBUS(pl022_state, dev);
+
+    memory_region_init_io(&s->iomem, &pl022_ops, s, "pl022", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    sysbus_init_irq(dev, &s->irq);
+    s->ssi = ssi_create_bus(&dev->qdev, "ssi");
+    pl022_reset(s);
+    vmstate_register(&dev->qdev, -1, &vmstate_pl022, s);
+    return 0;
+}
+
+static void pl022_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = pl022_init;
+}
+
+static const TypeInfo pl022_info = {
+    .name          = "pl022",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl022_state),
+    .class_init    = pl022_class_init,
+};
+
+static void pl022_register_types(void)
+{
+    type_register_static(&pl022_info);
+}
+
+type_init(pl022_register_types)
diff --git a/hw/ssi/ssi.c b/hw/ssi/ssi.c
new file mode 100644 (file)
index 0000000..1264d9d
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * QEMU Synchronous Serial Interface support
+ *
+ * Copyright (c) 2009 CodeSourcery.
+ * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
+ * Copyright (c) 2012 PetaLogix Pty Ltd.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GNU GPL v2.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "hw/ssi.h"
+
+struct SSIBus {
+    BusState qbus;
+};
+
+#define TYPE_SSI_BUS "SSI"
+#define SSI_BUS(obj) OBJECT_CHECK(SSIBus, (obj), TYPE_SSI_BUS)
+
+static const TypeInfo ssi_bus_info = {
+    .name = TYPE_SSI_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(SSIBus),
+};
+
+static void ssi_cs_default(void *opaque, int n, int level)
+{
+    SSISlave *s = SSI_SLAVE(opaque);
+    bool cs = !!level;
+    assert(n == 0);
+    if (s->cs != cs) {
+        SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s);
+        if (ssc->set_cs) {
+            ssc->set_cs(s, cs);
+        }
+    }
+    s->cs = cs;
+}
+
+static uint32_t ssi_transfer_raw_default(SSISlave *dev, uint32_t val)
+{
+    SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(dev);
+
+    if ((dev->cs && ssc->cs_polarity == SSI_CS_HIGH) ||
+            (!dev->cs && ssc->cs_polarity == SSI_CS_LOW) ||
+            ssc->cs_polarity == SSI_CS_NONE) {
+        return ssc->transfer(dev, val);
+    }
+    return 0;
+}
+
+static int ssi_slave_init(DeviceState *dev)
+{
+    SSISlave *s = SSI_SLAVE(dev);
+    SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s);
+
+    if (ssc->transfer_raw == ssi_transfer_raw_default &&
+            ssc->cs_polarity != SSI_CS_NONE) {
+        qdev_init_gpio_in(&s->qdev, ssi_cs_default, 1);
+    }
+
+    return ssc->init(s);
+}
+
+static void ssi_slave_class_init(ObjectClass *klass, void *data)
+{
+    SSISlaveClass *ssc = SSI_SLAVE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->init = ssi_slave_init;
+    dc->bus_type = TYPE_SSI_BUS;
+    if (!ssc->transfer_raw) {
+        ssc->transfer_raw = ssi_transfer_raw_default;
+    }
+}
+
+static const TypeInfo ssi_slave_info = {
+    .name = TYPE_SSI_SLAVE,
+    .parent = TYPE_DEVICE,
+    .class_init = ssi_slave_class_init,
+    .class_size = sizeof(SSISlaveClass),
+    .abstract = true,
+};
+
+DeviceState *ssi_create_slave_no_init(SSIBus *bus, const char *name)
+{
+    return qdev_create(&bus->qbus, name);
+}
+
+DeviceState *ssi_create_slave(SSIBus *bus, const char *name)
+{
+    DeviceState *dev = ssi_create_slave_no_init(bus, name);
+
+    qdev_init_nofail(dev);
+    return dev;
+}
+
+SSIBus *ssi_create_bus(DeviceState *parent, const char *name)
+{
+    BusState *bus;
+    bus = qbus_create(TYPE_SSI_BUS, parent, name);
+    return FROM_QBUS(SSIBus, bus);
+}
+
+uint32_t ssi_transfer(SSIBus *bus, uint32_t val)
+{
+    BusChild *kid;
+    SSISlaveClass *ssc;
+    uint32_t r = 0;
+
+    QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
+        SSISlave *slave = SSI_SLAVE(kid->child);
+        ssc = SSI_SLAVE_GET_CLASS(slave);
+        r |= ssc->transfer_raw(slave, val);
+    }
+
+    return r;
+}
+
+const VMStateDescription vmstate_ssi_slave = {
+    .name = "SSISlave",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_BOOL(cs, SSISlave),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ssi_slave_register_types(void)
+{
+    type_register_static(&ssi_bus_info);
+    type_register_static(&ssi_slave_info);
+}
+
+type_init(ssi_slave_register_types)
+
+typedef struct SSIAutoConnectArg {
+    qemu_irq **cs_linep;
+    SSIBus *bus;
+} SSIAutoConnectArg;
+
+static int ssi_auto_connect_slave(Object *child, void *opaque)
+{
+    SSIAutoConnectArg *arg = opaque;
+    SSISlave *dev = (SSISlave *)object_dynamic_cast(child, TYPE_SSI_SLAVE);
+    qemu_irq cs_line;
+
+    if (!dev) {
+        return 0;
+    }
+
+    cs_line = qdev_get_gpio_in(DEVICE(dev), 0);
+    qdev_set_parent_bus(DEVICE(dev), &arg->bus->qbus);
+    **arg->cs_linep = cs_line;
+    (*arg->cs_linep)++;
+    return 0;
+}
+
+void ssi_auto_connect_slaves(DeviceState *parent, qemu_irq *cs_line,
+                             SSIBus *bus)
+{
+    SSIAutoConnectArg arg = {
+        .cs_linep = &cs_line,
+        .bus = bus
+    };
+
+    object_child_foreach(OBJECT(parent), ssi_auto_connect_slave, &arg);
+}
diff --git a/hw/stellaris_input.c b/hw/stellaris_input.c
deleted file mode 100644 (file)
index f83fc3f..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Gamepad style buttons connected to IRQ/GPIO lines
- *
- * Copyright (c) 2007 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the GPL.
- */
-#include "hw/hw.h"
-#include "hw/arm/devices.h"
-#include "ui/console.h"
-
-typedef struct {
-    qemu_irq irq;
-    int keycode;
-    uint8_t pressed;
-} gamepad_button;
-
-typedef struct {
-    gamepad_button *buttons;
-    int num_buttons;
-    int extension;
-} gamepad_state;
-
-static void stellaris_gamepad_put_key(void * opaque, int keycode)
-{
-    gamepad_state *s = (gamepad_state *)opaque;
-    int i;
-    int down;
-
-    if (keycode == 0xe0 && !s->extension) {
-        s->extension = 0x80;
-        return;
-    }
-
-    down = (keycode & 0x80) == 0;
-    keycode = (keycode & 0x7f) | s->extension;
-
-    for (i = 0; i < s->num_buttons; i++) {
-        if (s->buttons[i].keycode == keycode
-                && s->buttons[i].pressed != down) {
-            s->buttons[i].pressed = down;
-            qemu_set_irq(s->buttons[i].irq, down);
-        }
-    }
-
-    s->extension = 0;
-}
-
-static const VMStateDescription vmstate_stellaris_button = {
-    .name = "stellaris_button",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT8(pressed, gamepad_button),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_stellaris_gamepad = {
-    .name = "stellaris_gamepad",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_INT32(extension, gamepad_state),
-        VMSTATE_STRUCT_VARRAY_INT32(buttons, gamepad_state, num_buttons, 0,
-                              vmstate_stellaris_button, gamepad_button),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-/* Returns an array 5 ouput slots.  */
-void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode)
-{
-    gamepad_state *s;
-    int i;
-
-    s = (gamepad_state *)g_malloc0(sizeof (gamepad_state));
-    s->buttons = (gamepad_button *)g_malloc0(n * sizeof (gamepad_button));
-    for (i = 0; i < n; i++) {
-        s->buttons[i].irq = irq[i];
-        s->buttons[i].keycode = keycode[i];
-    }
-    s->num_buttons = n;
-    qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s);
-    vmstate_register(NULL, -1, &vmstate_stellaris_gamepad, s);
-}
diff --git a/hw/stream.c b/hw/stream.c
deleted file mode 100644 (file)
index a07d6a5..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "hw/stream.h"
-
-void
-stream_push(StreamSlave *sink, uint8_t *buf, size_t len, uint32_t *app)
-{
-    StreamSlaveClass *k =  STREAM_SLAVE_GET_CLASS(sink);
-
-    k->push(sink, buf, len, app);
-}
-
-static const TypeInfo stream_slave_info = {
-    .name          = TYPE_STREAM_SLAVE,
-    .parent        = TYPE_INTERFACE,
-    .class_size = sizeof(StreamSlaveClass),
-};
-
-
-static void stream_slave_register_types(void)
-{
-    type_register_static(&stream_slave_info);
-}
-
-type_init(stream_slave_register_types)
diff --git a/hw/sysbus.c b/hw/sysbus.c
deleted file mode 100644 (file)
index 9004d8c..0000000
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- *  System (CPU) Bus device support code
- *
- *  Copyright (c) 2009 CodeSourcery
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/sysbus.h"
-#include "monitor/monitor.h"
-#include "exec/address-spaces.h"
-
-static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent);
-static char *sysbus_get_fw_dev_path(DeviceState *dev);
-
-static void system_bus_class_init(ObjectClass *klass, void *data)
-{
-    BusClass *k = BUS_CLASS(klass);
-
-    k->print_dev = sysbus_dev_print;
-    k->get_fw_dev_path = sysbus_get_fw_dev_path;
-}
-
-static const TypeInfo system_bus_info = {
-    .name = TYPE_SYSTEM_BUS,
-    .parent = TYPE_BUS,
-    .instance_size = sizeof(BusState),
-    .class_init = system_bus_class_init,
-};
-
-void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq)
-{
-    assert(n >= 0 && n < dev->num_irq);
-    dev->irqs[n] = NULL;
-    if (dev->irqp[n]) {
-        *dev->irqp[n] = irq;
-    }
-}
-
-static void sysbus_mmio_map_common(SysBusDevice *dev, int n, hwaddr addr,
-                                   bool may_overlap, unsigned priority)
-{
-    assert(n >= 0 && n < dev->num_mmio);
-
-    if (dev->mmio[n].addr == addr) {
-        /* ??? region already mapped here.  */
-        return;
-    }
-    if (dev->mmio[n].addr != (hwaddr)-1) {
-        /* Unregister previous mapping.  */
-        memory_region_del_subregion(get_system_memory(), dev->mmio[n].memory);
-    }
-    dev->mmio[n].addr = addr;
-    if (may_overlap) {
-        memory_region_add_subregion_overlap(get_system_memory(),
-                                            addr,
-                                            dev->mmio[n].memory,
-                                            priority);
-    }
-    else {
-        memory_region_add_subregion(get_system_memory(),
-                                    addr,
-                                    dev->mmio[n].memory);
-    }
-}
-
-void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr)
-{
-    sysbus_mmio_map_common(dev, n, addr, false, 0);
-}
-
-void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr,
-                             unsigned priority)
-{
-    sysbus_mmio_map_common(dev, n, addr, true, priority);
-}
-
-/* Request an IRQ source.  The actual IRQ object may be populated later.  */
-void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p)
-{
-    int n;
-
-    assert(dev->num_irq < QDEV_MAX_IRQ);
-    n = dev->num_irq++;
-    dev->irqp[n] = p;
-}
-
-/* Pass IRQs from a target device.  */
-void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target)
-{
-    int i;
-    assert(dev->num_irq == 0);
-    dev->num_irq = target->num_irq;
-    for (i = 0; i < dev->num_irq; i++) {
-        dev->irqp[i] = target->irqp[i];
-    }
-}
-
-void sysbus_init_mmio(SysBusDevice *dev, MemoryRegion *memory)
-{
-    int n;
-
-    assert(dev->num_mmio < QDEV_MAX_MMIO);
-    n = dev->num_mmio++;
-    dev->mmio[n].addr = -1;
-    dev->mmio[n].memory = memory;
-}
-
-MemoryRegion *sysbus_mmio_get_region(SysBusDevice *dev, int n)
-{
-    return dev->mmio[n].memory;
-}
-
-void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size)
-{
-    pio_addr_t i;
-
-    for (i = 0; i < size; i++) {
-        assert(dev->num_pio < QDEV_MAX_PIO);
-        dev->pio[dev->num_pio++] = ioport++;
-    }
-}
-
-static int sysbus_device_init(DeviceState *dev)
-{
-    SysBusDevice *sd = SYS_BUS_DEVICE(dev);
-    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_GET_CLASS(sd);
-
-    if (!sbc->init) {
-        return 0;
-    }
-    return sbc->init(sd);
-}
-
-DeviceState *sysbus_create_varargs(const char *name,
-                                   hwaddr addr, ...)
-{
-    DeviceState *dev;
-    SysBusDevice *s;
-    va_list va;
-    qemu_irq irq;
-    int n;
-
-    dev = qdev_create(NULL, name);
-    s = SYS_BUS_DEVICE(dev);
-    qdev_init_nofail(dev);
-    if (addr != (hwaddr)-1) {
-        sysbus_mmio_map(s, 0, addr);
-    }
-    va_start(va, addr);
-    n = 0;
-    while (1) {
-        irq = va_arg(va, qemu_irq);
-        if (!irq) {
-            break;
-        }
-        sysbus_connect_irq(s, n, irq);
-        n++;
-    }
-    va_end(va);
-    return dev;
-}
-
-DeviceState *sysbus_try_create_varargs(const char *name,
-                                       hwaddr addr, ...)
-{
-    DeviceState *dev;
-    SysBusDevice *s;
-    va_list va;
-    qemu_irq irq;
-    int n;
-
-    dev = qdev_try_create(NULL, name);
-    if (!dev) {
-        return NULL;
-    }
-    s = SYS_BUS_DEVICE(dev);
-    qdev_init_nofail(dev);
-    if (addr != (hwaddr)-1) {
-        sysbus_mmio_map(s, 0, addr);
-    }
-    va_start(va, addr);
-    n = 0;
-    while (1) {
-        irq = va_arg(va, qemu_irq);
-        if (!irq) {
-            break;
-        }
-        sysbus_connect_irq(s, n, irq);
-        n++;
-    }
-    va_end(va);
-    return dev;
-}
-
-static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent)
-{
-    SysBusDevice *s = SYS_BUS_DEVICE(dev);
-    hwaddr size;
-    int i;
-
-    monitor_printf(mon, "%*sirq %d\n", indent, "", s->num_irq);
-    for (i = 0; i < s->num_mmio; i++) {
-        size = memory_region_size(s->mmio[i].memory);
-        monitor_printf(mon, "%*smmio " TARGET_FMT_plx "/" TARGET_FMT_plx "\n",
-                       indent, "", s->mmio[i].addr, size);
-    }
-}
-
-static char *sysbus_get_fw_dev_path(DeviceState *dev)
-{
-    SysBusDevice *s = SYS_BUS_DEVICE(dev);
-    char path[40];
-    int off;
-
-    off = snprintf(path, sizeof(path), "%s", qdev_fw_name(dev));
-
-    if (s->num_mmio) {
-        snprintf(path + off, sizeof(path) - off, "@"TARGET_FMT_plx,
-                 s->mmio[0].addr);
-    } else if (s->num_pio) {
-        snprintf(path + off, sizeof(path) - off, "@i%04x", s->pio[0]);
-    }
-
-    return g_strdup(path);
-}
-
-void sysbus_add_io(SysBusDevice *dev, hwaddr addr,
-                       MemoryRegion *mem)
-{
-    memory_region_add_subregion(get_system_io(), addr, mem);
-}
-
-void sysbus_del_io(SysBusDevice *dev, MemoryRegion *mem)
-{
-    memory_region_del_subregion(get_system_io(), mem);
-}
-
-MemoryRegion *sysbus_address_space(SysBusDevice *dev)
-{
-    return get_system_memory();
-}
-
-static void sysbus_device_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *k = DEVICE_CLASS(klass);
-    k->init = sysbus_device_init;
-    k->bus_type = TYPE_SYSTEM_BUS;
-}
-
-static const TypeInfo sysbus_device_type_info = {
-    .name = TYPE_SYS_BUS_DEVICE,
-    .parent = TYPE_DEVICE,
-    .instance_size = sizeof(SysBusDevice),
-    .abstract = true,
-    .class_size = sizeof(SysBusDeviceClass),
-    .class_init = sysbus_device_class_init,
-};
-
-/* This is a nasty hack to allow passing a NULL bus to qdev_create.  */
-static BusState *main_system_bus;
-
-static void main_system_bus_create(void)
-{
-    /* assign main_system_bus before qbus_create_inplace()
-     * in order to make "if (bus != sysbus_get_default())" work */
-    main_system_bus = g_malloc0(system_bus_info.instance_size);
-    qbus_create_inplace(main_system_bus, TYPE_SYSTEM_BUS, NULL,
-                        "main-system-bus");
-    OBJECT(main_system_bus)->free = g_free;
-    object_property_add_child(container_get(qdev_get_machine(),
-                                            "/unattached"),
-                              "sysbus", OBJECT(main_system_bus), NULL);
-}
-
-BusState *sysbus_get_default(void)
-{
-    if (!main_system_bus) {
-        main_system_bus_create();
-    }
-    return main_system_bus;
-}
-
-static void sysbus_register_types(void)
-{
-    type_register_static(&system_bus_info);
-    type_register_static(&sysbus_device_type_info);
-}
-
-type_init(sysbus_register_types)
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..12781dd2a3a9973864da54e13600b58e382c38e4 100644 (file)
@@ -0,0 +1,10 @@
+common-obj-$(CONFIG_ARM_TIMER) += arm_timer.o
+common-obj-$(CONFIG_CADENCE) += cadence_ttc.o
+common-obj-$(CONFIG_DS1338) += ds1338.o
+common-obj-$(CONFIG_HPET) += hpet.o
+common-obj-$(CONFIG_I8254) += i8254_common.o i8254.o
+common-obj-$(CONFIG_M48T59) += m48t59.o
+common-obj-$(CONFIG_PL031) += pl031.o
+common-obj-$(CONFIG_PUV3) += puv3_ost.o
+common-obj-$(CONFIG_TWL92230) += twl92230.o
+common-obj-$(CONFIG_XILINX) += xilinx_timer.o
diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c
new file mode 100644 (file)
index 0000000..6449870
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * ARM PrimeCell Timer modules.
+ *
+ * Copyright (c) 2005-2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "qemu-common.h"
+#include "hw/qdev.h"
+#include "hw/ptimer.h"
+
+/* Common timer implementation.  */
+
+#define TIMER_CTRL_ONESHOT      (1 << 0)
+#define TIMER_CTRL_32BIT        (1 << 1)
+#define TIMER_CTRL_DIV1         (0 << 2)
+#define TIMER_CTRL_DIV16        (1 << 2)
+#define TIMER_CTRL_DIV256       (2 << 2)
+#define TIMER_CTRL_IE           (1 << 5)
+#define TIMER_CTRL_PERIODIC     (1 << 6)
+#define TIMER_CTRL_ENABLE       (1 << 7)
+
+typedef struct {
+    ptimer_state *timer;
+    uint32_t control;
+    uint32_t limit;
+    int freq;
+    int int_level;
+    qemu_irq irq;
+} arm_timer_state;
+
+/* Check all active timers, and schedule the next timer interrupt.  */
+
+static void arm_timer_update(arm_timer_state *s)
+{
+    /* Update interrupts.  */
+    if (s->int_level && (s->control & TIMER_CTRL_IE)) {
+        qemu_irq_raise(s->irq);
+    } else {
+        qemu_irq_lower(s->irq);
+    }
+}
+
+static uint32_t arm_timer_read(void *opaque, hwaddr offset)
+{
+    arm_timer_state *s = (arm_timer_state *)opaque;
+
+    switch (offset >> 2) {
+    case 0: /* TimerLoad */
+    case 6: /* TimerBGLoad */
+        return s->limit;
+    case 1: /* TimerValue */
+        return ptimer_get_count(s->timer);
+    case 2: /* TimerControl */
+        return s->control;
+    case 4: /* TimerRIS */
+        return s->int_level;
+    case 5: /* TimerMIS */
+        if ((s->control & TIMER_CTRL_IE) == 0)
+            return 0;
+        return s->int_level;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Bad offset %x\n", __func__, (int)offset);
+        return 0;
+    }
+}
+
+/* Reset the timer limit after settings have changed.  */
+static void arm_timer_recalibrate(arm_timer_state *s, int reload)
+{
+    uint32_t limit;
+
+    if ((s->control & (TIMER_CTRL_PERIODIC | TIMER_CTRL_ONESHOT)) == 0) {
+        /* Free running.  */
+        if (s->control & TIMER_CTRL_32BIT)
+            limit = 0xffffffff;
+        else
+            limit = 0xffff;
+    } else {
+          /* Periodic.  */
+          limit = s->limit;
+    }
+    ptimer_set_limit(s->timer, limit, reload);
+}
+
+static void arm_timer_write(void *opaque, hwaddr offset,
+                            uint32_t value)
+{
+    arm_timer_state *s = (arm_timer_state *)opaque;
+    int freq;
+
+    switch (offset >> 2) {
+    case 0: /* TimerLoad */
+        s->limit = value;
+        arm_timer_recalibrate(s, 1);
+        break;
+    case 1: /* TimerValue */
+        /* ??? Linux seems to want to write to this readonly register.
+           Ignore it.  */
+        break;
+    case 2: /* TimerControl */
+        if (s->control & TIMER_CTRL_ENABLE) {
+            /* Pause the timer if it is running.  This may cause some
+               inaccuracy dure to rounding, but avoids a whole lot of other
+               messyness.  */
+            ptimer_stop(s->timer);
+        }
+        s->control = value;
+        freq = s->freq;
+        /* ??? Need to recalculate expiry time after changing divisor.  */
+        switch ((value >> 2) & 3) {
+        case 1: freq >>= 4; break;
+        case 2: freq >>= 8; break;
+        }
+        arm_timer_recalibrate(s, s->control & TIMER_CTRL_ENABLE);
+        ptimer_set_freq(s->timer, freq);
+        if (s->control & TIMER_CTRL_ENABLE) {
+            /* Restart the timer if still enabled.  */
+            ptimer_run(s->timer, (s->control & TIMER_CTRL_ONESHOT) != 0);
+        }
+        break;
+    case 3: /* TimerIntClr */
+        s->int_level = 0;
+        break;
+    case 6: /* TimerBGLoad */
+        s->limit = value;
+        arm_timer_recalibrate(s, 0);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Bad offset %x\n", __func__, (int)offset);
+    }
+    arm_timer_update(s);
+}
+
+static void arm_timer_tick(void *opaque)
+{
+    arm_timer_state *s = (arm_timer_state *)opaque;
+    s->int_level = 1;
+    arm_timer_update(s);
+}
+
+static const VMStateDescription vmstate_arm_timer = {
+    .name = "arm_timer",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32(control, arm_timer_state),
+        VMSTATE_UINT32(limit, arm_timer_state),
+        VMSTATE_INT32(int_level, arm_timer_state),
+        VMSTATE_PTIMER(timer, arm_timer_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static arm_timer_state *arm_timer_init(uint32_t freq)
+{
+    arm_timer_state *s;
+    QEMUBH *bh;
+
+    s = (arm_timer_state *)g_malloc0(sizeof(arm_timer_state));
+    s->freq = freq;
+    s->control = TIMER_CTRL_IE;
+
+    bh = qemu_bh_new(arm_timer_tick, s);
+    s->timer = ptimer_init(bh);
+    vmstate_register(NULL, -1, &vmstate_arm_timer, s);
+    return s;
+}
+
+/* ARM PrimeCell SP804 dual timer module.
+ * Docs at
+ * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0271d/index.html
+*/
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    arm_timer_state *timer[2];
+    uint32_t freq0, freq1;
+    int level[2];
+    qemu_irq irq;
+} sp804_state;
+
+static const uint8_t sp804_ids[] = {
+    /* Timer ID */
+    0x04, 0x18, 0x14, 0,
+    /* PrimeCell ID */
+    0xd, 0xf0, 0x05, 0xb1
+};
+
+/* Merge the IRQs from the two component devices.  */
+static void sp804_set_irq(void *opaque, int irq, int level)
+{
+    sp804_state *s = (sp804_state *)opaque;
+
+    s->level[irq] = level;
+    qemu_set_irq(s->irq, s->level[0] || s->level[1]);
+}
+
+static uint64_t sp804_read(void *opaque, hwaddr offset,
+                           unsigned size)
+{
+    sp804_state *s = (sp804_state *)opaque;
+
+    if (offset < 0x20) {
+        return arm_timer_read(s->timer[0], offset);
+    }
+    if (offset < 0x40) {
+        return arm_timer_read(s->timer[1], offset - 0x20);
+    }
+
+    /* TimerPeriphID */
+    if (offset >= 0xfe0 && offset <= 0xffc) {
+        return sp804_ids[(offset - 0xfe0) >> 2];
+    }
+
+    switch (offset) {
+    /* Integration Test control registers, which we won't support */
+    case 0xf00: /* TimerITCR */
+    case 0xf04: /* TimerITOP (strictly write only but..) */
+        qemu_log_mask(LOG_UNIMP,
+                      "%s: integration test registers unimplemented\n",
+                      __func__);
+        return 0;
+    }
+
+    qemu_log_mask(LOG_GUEST_ERROR,
+                  "%s: Bad offset %x\n", __func__, (int)offset);
+    return 0;
+}
+
+static void sp804_write(void *opaque, hwaddr offset,
+                        uint64_t value, unsigned size)
+{
+    sp804_state *s = (sp804_state *)opaque;
+
+    if (offset < 0x20) {
+        arm_timer_write(s->timer[0], offset, value);
+        return;
+    }
+
+    if (offset < 0x40) {
+        arm_timer_write(s->timer[1], offset - 0x20, value);
+        return;
+    }
+
+    /* Technically we could be writing to the Test Registers, but not likely */
+    qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %x\n",
+                  __func__, (int)offset);
+}
+
+static const MemoryRegionOps sp804_ops = {
+    .read = sp804_read,
+    .write = sp804_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_sp804 = {
+    .name = "sp804",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_INT32_ARRAY(level, sp804_state, 2),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int sp804_init(SysBusDevice *dev)
+{
+    sp804_state *s = FROM_SYSBUS(sp804_state, dev);
+    qemu_irq *qi;
+
+    qi = qemu_allocate_irqs(sp804_set_irq, s, 2);
+    sysbus_init_irq(dev, &s->irq);
+    s->timer[0] = arm_timer_init(s->freq0);
+    s->timer[1] = arm_timer_init(s->freq1);
+    s->timer[0]->irq = qi[0];
+    s->timer[1]->irq = qi[1];
+    memory_region_init_io(&s->iomem, &sp804_ops, s, "sp804", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    vmstate_register(&dev->qdev, -1, &vmstate_sp804, s);
+    return 0;
+}
+
+/* Integrator/CP timer module.  */
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    arm_timer_state *timer[3];
+} icp_pit_state;
+
+static uint64_t icp_pit_read(void *opaque, hwaddr offset,
+                             unsigned size)
+{
+    icp_pit_state *s = (icp_pit_state *)opaque;
+    int n;
+
+    /* ??? Don't know the PrimeCell ID for this device.  */
+    n = offset >> 8;
+    if (n > 2) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad timer %d\n", __func__, n);
+    }
+
+    return arm_timer_read(s->timer[n], offset & 0xff);
+}
+
+static void icp_pit_write(void *opaque, hwaddr offset,
+                          uint64_t value, unsigned size)
+{
+    icp_pit_state *s = (icp_pit_state *)opaque;
+    int n;
+
+    n = offset >> 8;
+    if (n > 2) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad timer %d\n", __func__, n);
+    }
+
+    arm_timer_write(s->timer[n], offset & 0xff, value);
+}
+
+static const MemoryRegionOps icp_pit_ops = {
+    .read = icp_pit_read,
+    .write = icp_pit_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int icp_pit_init(SysBusDevice *dev)
+{
+    icp_pit_state *s = FROM_SYSBUS(icp_pit_state, dev);
+
+    /* Timer 0 runs at the system clock speed (40MHz).  */
+    s->timer[0] = arm_timer_init(40000000);
+    /* The other two timers run at 1MHz.  */
+    s->timer[1] = arm_timer_init(1000000);
+    s->timer[2] = arm_timer_init(1000000);
+
+    sysbus_init_irq(dev, &s->timer[0]->irq);
+    sysbus_init_irq(dev, &s->timer[1]->irq);
+    sysbus_init_irq(dev, &s->timer[2]->irq);
+
+    memory_region_init_io(&s->iomem, &icp_pit_ops, s, "icp_pit", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    /* This device has no state to save/restore.  The component timers will
+       save themselves.  */
+    return 0;
+}
+
+static void icp_pit_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = icp_pit_init;
+}
+
+static const TypeInfo icp_pit_info = {
+    .name          = "integrator_pit",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(icp_pit_state),
+    .class_init    = icp_pit_class_init,
+};
+
+static Property sp804_properties[] = {
+    DEFINE_PROP_UINT32("freq0", sp804_state, freq0, 1000000),
+    DEFINE_PROP_UINT32("freq1", sp804_state, freq1, 1000000),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void sp804_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *k = DEVICE_CLASS(klass);
+
+    sdc->init = sp804_init;
+    k->props = sp804_properties;
+}
+
+static const TypeInfo sp804_info = {
+    .name          = "sp804",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(sp804_state),
+    .class_init    = sp804_class_init,
+};
+
+static void arm_timer_register_types(void)
+{
+    type_register_static(&icp_pit_info);
+    type_register_static(&sp804_info);
+}
+
+type_init(arm_timer_register_types)
diff --git a/hw/timer/cadence_ttc.c b/hw/timer/cadence_ttc.c
new file mode 100644 (file)
index 0000000..ba584f4
--- /dev/null
@@ -0,0 +1,489 @@
+/*
+ * Xilinx Zynq cadence TTC model
+ *
+ * Copyright (c) 2011 Xilinx Inc.
+ * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
+ * Copyright (c) 2012 PetaLogix Pty Ltd.
+ * Written By Haibing Ma
+ *            M. Habib
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+
+#ifdef CADENCE_TTC_ERR_DEBUG
+#define DB_PRINT(...) do { \
+    fprintf(stderr,  ": %s: ", __func__); \
+    fprintf(stderr, ## __VA_ARGS__); \
+    } while (0);
+#else
+    #define DB_PRINT(...)
+#endif
+
+#define COUNTER_INTR_IV     0x00000001
+#define COUNTER_INTR_M1     0x00000002
+#define COUNTER_INTR_M2     0x00000004
+#define COUNTER_INTR_M3     0x00000008
+#define COUNTER_INTR_OV     0x00000010
+#define COUNTER_INTR_EV     0x00000020
+
+#define COUNTER_CTRL_DIS    0x00000001
+#define COUNTER_CTRL_INT    0x00000002
+#define COUNTER_CTRL_DEC    0x00000004
+#define COUNTER_CTRL_MATCH  0x00000008
+#define COUNTER_CTRL_RST    0x00000010
+
+#define CLOCK_CTRL_PS_EN    0x00000001
+#define CLOCK_CTRL_PS_V     0x0000001e
+
+typedef struct {
+    QEMUTimer *timer;
+    int freq;
+
+    uint32_t reg_clock;
+    uint32_t reg_count;
+    uint32_t reg_value;
+    uint16_t reg_interval;
+    uint16_t reg_match[3];
+    uint32_t reg_intr;
+    uint32_t reg_intr_en;
+    uint32_t reg_event_ctrl;
+    uint32_t reg_event;
+
+    uint64_t cpu_time;
+    unsigned int cpu_time_valid;
+
+    qemu_irq irq;
+} CadenceTimerState;
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    CadenceTimerState timer[3];
+} CadenceTTCState;
+
+static void cadence_timer_update(CadenceTimerState *s)
+{
+    qemu_set_irq(s->irq, !!(s->reg_intr & s->reg_intr_en));
+}
+
+static CadenceTimerState *cadence_timer_from_addr(void *opaque,
+                                        hwaddr offset)
+{
+    unsigned int index;
+    CadenceTTCState *s = (CadenceTTCState *)opaque;
+
+    index = (offset >> 2) % 3;
+
+    return &s->timer[index];
+}
+
+static uint64_t cadence_timer_get_ns(CadenceTimerState *s, uint64_t timer_steps)
+{
+    /* timer_steps has max value of 0x100000000. double check it
+     * (or overflow can happen below) */
+    assert(timer_steps <= 1ULL << 32);
+
+    uint64_t r = timer_steps * 1000000000ULL;
+    if (s->reg_clock & CLOCK_CTRL_PS_EN) {
+        r >>= 16 - (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1);
+    } else {
+        r >>= 16;
+    }
+    r /= (uint64_t)s->freq;
+    return r;
+}
+
+static uint64_t cadence_timer_get_steps(CadenceTimerState *s, uint64_t ns)
+{
+    uint64_t to_divide = 1000000000ULL;
+
+    uint64_t r = ns;
+     /* for very large intervals (> 8s) do some division first to stop
+      * overflow (costs some prescision) */
+    while (r >= 8ULL << 30 && to_divide > 1) {
+        r /= 1000;
+        to_divide /= 1000;
+    }
+    r <<= 16;
+    /* keep early-dividing as needed */
+    while (r >= 8ULL << 30 && to_divide > 1) {
+        r /= 1000;
+        to_divide /= 1000;
+    }
+    r *= (uint64_t)s->freq;
+    if (s->reg_clock & CLOCK_CTRL_PS_EN) {
+        r /= 1 << (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1);
+    }
+
+    r /= to_divide;
+    return r;
+}
+
+/* determine if x is in between a and b, exclusive of a, inclusive of b */
+
+static inline int64_t is_between(int64_t x, int64_t a, int64_t b)
+{
+    if (a < b) {
+        return x > a && x <= b;
+    }
+    return x < a && x >= b;
+}
+
+static void cadence_timer_run(CadenceTimerState *s)
+{
+    int i;
+    int64_t event_interval, next_value;
+
+    assert(s->cpu_time_valid); /* cadence_timer_sync must be called first */
+
+    if (s->reg_count & COUNTER_CTRL_DIS) {
+        s->cpu_time_valid = 0;
+        return;
+    }
+
+    { /* figure out what's going to happen next (rollover or match) */
+        int64_t interval = (uint64_t)((s->reg_count & COUNTER_CTRL_INT) ?
+                (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16;
+        next_value = (s->reg_count & COUNTER_CTRL_DEC) ? -1ULL : interval;
+        for (i = 0; i < 3; ++i) {
+            int64_t cand = (uint64_t)s->reg_match[i] << 16;
+            if (is_between(cand, (uint64_t)s->reg_value, next_value)) {
+                next_value = cand;
+            }
+        }
+    }
+    DB_PRINT("next timer event value: %09llx\n",
+            (unsigned long long)next_value);
+
+    event_interval = next_value - (int64_t)s->reg_value;
+    event_interval = (event_interval < 0) ? -event_interval : event_interval;
+
+    qemu_mod_timer(s->timer, s->cpu_time +
+                cadence_timer_get_ns(s, event_interval));
+}
+
+static void cadence_timer_sync(CadenceTimerState *s)
+{
+    int i;
+    int64_t r, x;
+    int64_t interval = ((s->reg_count & COUNTER_CTRL_INT) ?
+            (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16;
+    uint64_t old_time = s->cpu_time;
+
+    s->cpu_time = qemu_get_clock_ns(vm_clock);
+    DB_PRINT("cpu time: %lld ns\n", (long long)old_time);
+
+    if (!s->cpu_time_valid || old_time == s->cpu_time) {
+        s->cpu_time_valid = 1;
+        return;
+    }
+
+    r = (int64_t)cadence_timer_get_steps(s, s->cpu_time - old_time);
+    x = (int64_t)s->reg_value + ((s->reg_count & COUNTER_CTRL_DEC) ? -r : r);
+
+    for (i = 0; i < 3; ++i) {
+        int64_t m = (int64_t)s->reg_match[i] << 16;
+        if (m > interval) {
+            continue;
+        }
+        /* check to see if match event has occurred. check m +/- interval
+         * to account for match events in wrap around cases */
+        if (is_between(m, s->reg_value, x) ||
+            is_between(m + interval, s->reg_value, x) ||
+            is_between(m - interval, s->reg_value, x)) {
+            s->reg_intr |= (2 << i);
+        }
+    }
+    while (x < 0) {
+        x += interval;
+    }
+    s->reg_value = (uint32_t)(x % interval);
+
+    if (s->reg_value != x) {
+        s->reg_intr |= (s->reg_count & COUNTER_CTRL_INT) ?
+            COUNTER_INTR_IV : COUNTER_INTR_OV;
+    }
+    cadence_timer_update(s);
+}
+
+static void cadence_timer_tick(void *opaque)
+{
+    CadenceTimerState *s = opaque;
+
+    DB_PRINT("\n");
+    cadence_timer_sync(s);
+    cadence_timer_run(s);
+}
+
+static uint32_t cadence_ttc_read_imp(void *opaque, hwaddr offset)
+{
+    CadenceTimerState *s = cadence_timer_from_addr(opaque, offset);
+    uint32_t value;
+
+    cadence_timer_sync(s);
+    cadence_timer_run(s);
+
+    switch (offset) {
+    case 0x00: /* clock control */
+    case 0x04:
+    case 0x08:
+        return s->reg_clock;
+
+    case 0x0c: /* counter control */
+    case 0x10:
+    case 0x14:
+        return s->reg_count;
+
+    case 0x18: /* counter value */
+    case 0x1c:
+    case 0x20:
+        return (uint16_t)(s->reg_value >> 16);
+
+    case 0x24: /* reg_interval counter */
+    case 0x28:
+    case 0x2c:
+        return s->reg_interval;
+
+    case 0x30: /* match 1 counter */
+    case 0x34:
+    case 0x38:
+        return s->reg_match[0];
+
+    case 0x3c: /* match 2 counter */
+    case 0x40:
+    case 0x44:
+        return s->reg_match[1];
+
+    case 0x48: /* match 3 counter */
+    case 0x4c:
+    case 0x50:
+        return s->reg_match[2];
+
+    case 0x54: /* interrupt register */
+    case 0x58:
+    case 0x5c:
+        /* cleared after read */
+        value = s->reg_intr;
+        s->reg_intr = 0;
+        cadence_timer_update(s);
+        return value;
+
+    case 0x60: /* interrupt enable */
+    case 0x64:
+    case 0x68:
+        return s->reg_intr_en;
+
+    case 0x6c:
+    case 0x70:
+    case 0x74:
+        return s->reg_event_ctrl;
+
+    case 0x78:
+    case 0x7c:
+    case 0x80:
+        return s->reg_event;
+
+    default:
+        return 0;
+    }
+}
+
+static uint64_t cadence_ttc_read(void *opaque, hwaddr offset,
+    unsigned size)
+{
+    uint32_t ret = cadence_ttc_read_imp(opaque, offset);
+
+    DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)ret);
+    return ret;
+}
+
+static void cadence_ttc_write(void *opaque, hwaddr offset,
+        uint64_t value, unsigned size)
+{
+    CadenceTimerState *s = cadence_timer_from_addr(opaque, offset);
+
+    DB_PRINT("addr: %08x data %08x\n", (unsigned)offset, (unsigned)value);
+
+    cadence_timer_sync(s);
+
+    switch (offset) {
+    case 0x00: /* clock control */
+    case 0x04:
+    case 0x08:
+        s->reg_clock = value & 0x3F;
+        break;
+
+    case 0x0c: /* counter control */
+    case 0x10:
+    case 0x14:
+        if (value & COUNTER_CTRL_RST) {
+            s->reg_value = 0;
+        }
+        s->reg_count = value & 0x3f & ~COUNTER_CTRL_RST;
+        break;
+
+    case 0x24: /* interval register */
+    case 0x28:
+    case 0x2c:
+        s->reg_interval = value & 0xffff;
+        break;
+
+    case 0x30: /* match register */
+    case 0x34:
+    case 0x38:
+        s->reg_match[0] = value & 0xffff;
+
+    case 0x3c: /* match register */
+    case 0x40:
+    case 0x44:
+        s->reg_match[1] = value & 0xffff;
+
+    case 0x48: /* match register */
+    case 0x4c:
+    case 0x50:
+        s->reg_match[2] = value & 0xffff;
+        break;
+
+    case 0x54: /* interrupt register */
+    case 0x58:
+    case 0x5c:
+        break;
+
+    case 0x60: /* interrupt enable */
+    case 0x64:
+    case 0x68:
+        s->reg_intr_en = value & 0x3f;
+        break;
+
+    case 0x6c: /* event control */
+    case 0x70:
+    case 0x74:
+        s->reg_event_ctrl = value & 0x07;
+        break;
+
+    default:
+        return;
+    }
+
+    cadence_timer_run(s);
+    cadence_timer_update(s);
+}
+
+static const MemoryRegionOps cadence_ttc_ops = {
+    .read = cadence_ttc_read,
+    .write = cadence_ttc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void cadence_timer_reset(CadenceTimerState *s)
+{
+   s->reg_count = 0x21;
+}
+
+static void cadence_timer_init(uint32_t freq, CadenceTimerState *s)
+{
+    memset(s, 0, sizeof(CadenceTimerState));
+    s->freq = freq;
+
+    cadence_timer_reset(s);
+
+    s->timer = qemu_new_timer_ns(vm_clock, cadence_timer_tick, s);
+}
+
+static int cadence_ttc_init(SysBusDevice *dev)
+{
+    CadenceTTCState *s = FROM_SYSBUS(CadenceTTCState, dev);
+    int i;
+
+    for (i = 0; i < 3; ++i) {
+        cadence_timer_init(133000000, &s->timer[i]);
+        sysbus_init_irq(dev, &s->timer[i].irq);
+    }
+
+    memory_region_init_io(&s->iomem, &cadence_ttc_ops, s, "timer", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static void cadence_timer_pre_save(void *opaque)
+{
+    cadence_timer_sync((CadenceTimerState *)opaque);
+}
+
+static int cadence_timer_post_load(void *opaque, int version_id)
+{
+    CadenceTimerState *s = opaque;
+
+    s->cpu_time_valid = 0;
+    cadence_timer_sync(s);
+    cadence_timer_run(s);
+    cadence_timer_update(s);
+    return 0;
+}
+
+static const VMStateDescription vmstate_cadence_timer = {
+    .name = "cadence_timer",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_save = cadence_timer_pre_save,
+    .post_load = cadence_timer_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(reg_clock, CadenceTimerState),
+        VMSTATE_UINT32(reg_count, CadenceTimerState),
+        VMSTATE_UINT32(reg_value, CadenceTimerState),
+        VMSTATE_UINT16(reg_interval, CadenceTimerState),
+        VMSTATE_UINT16_ARRAY(reg_match, CadenceTimerState, 3),
+        VMSTATE_UINT32(reg_intr, CadenceTimerState),
+        VMSTATE_UINT32(reg_intr_en, CadenceTimerState),
+        VMSTATE_UINT32(reg_event_ctrl, CadenceTimerState),
+        VMSTATE_UINT32(reg_event, CadenceTimerState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_cadence_ttc = {
+    .name = "cadence_TTC",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT_ARRAY(timer, CadenceTTCState, 3, 0,
+                            vmstate_cadence_timer,
+                            CadenceTimerState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void cadence_ttc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = cadence_ttc_init;
+    dc->vmsd = &vmstate_cadence_ttc;
+}
+
+static const TypeInfo cadence_ttc_info = {
+    .name  = "cadence_ttc",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(CadenceTTCState),
+    .class_init = cadence_ttc_class_init,
+};
+
+static void cadence_ttc_register_types(void)
+{
+    type_register_static(&cadence_ttc_info);
+}
+
+type_init(cadence_ttc_register_types)
diff --git a/hw/timer/ds1338.c b/hw/timer/ds1338.c
new file mode 100644 (file)
index 0000000..8987cdc
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * MAXIM DS1338 I2C RTC+NVRAM
+ *
+ * Copyright (c) 2009 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GNU GPL v2.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "hw/i2c/i2c.h"
+
+/* Size of NVRAM including both the user-accessible area and the
+ * secondary register area.
+ */
+#define NVRAM_SIZE 64
+
+/* Flags definitions */
+#define SECONDS_CH 0x80
+#define HOURS_12   0x40
+#define HOURS_PM   0x20
+#define CTRL_OSF   0x20
+
+typedef struct {
+    I2CSlave i2c;
+    int64_t offset;
+    uint8_t wday_offset;
+    uint8_t nvram[NVRAM_SIZE];
+    int32_t ptr;
+    bool addr_byte;
+} DS1338State;
+
+static const VMStateDescription vmstate_ds1338 = {
+    .name = "ds1338",
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_I2C_SLAVE(i2c, DS1338State),
+        VMSTATE_INT64(offset, DS1338State),
+        VMSTATE_UINT8_V(wday_offset, DS1338State, 2),
+        VMSTATE_UINT8_ARRAY(nvram, DS1338State, NVRAM_SIZE),
+        VMSTATE_INT32(ptr, DS1338State),
+        VMSTATE_BOOL(addr_byte, DS1338State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void capture_current_time(DS1338State *s)
+{
+    /* Capture the current time into the secondary registers
+     * which will be actually read by the data transfer operation.
+     */
+    struct tm now;
+    qemu_get_timedate(&now, s->offset);
+    s->nvram[0] = to_bcd(now.tm_sec);
+    s->nvram[1] = to_bcd(now.tm_min);
+    if (s->nvram[2] & HOURS_12) {
+        int tmp = now.tm_hour;
+        if (tmp % 12 == 0) {
+            tmp += 12;
+        }
+        if (tmp <= 12) {
+            s->nvram[2] = HOURS_12 | to_bcd(tmp);
+        } else {
+            s->nvram[2] = HOURS_12 | HOURS_PM | to_bcd(tmp - 12);
+        }
+    } else {
+        s->nvram[2] = to_bcd(now.tm_hour);
+    }
+    s->nvram[3] = (now.tm_wday + s->wday_offset) % 7 + 1;
+    s->nvram[4] = to_bcd(now.tm_mday);
+    s->nvram[5] = to_bcd(now.tm_mon + 1);
+    s->nvram[6] = to_bcd(now.tm_year - 100);
+}
+
+static void inc_regptr(DS1338State *s)
+{
+    /* The register pointer wraps around after 0x3F; wraparound
+     * causes the current time/date to be retransferred into
+     * the secondary registers.
+     */
+    s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1);
+    if (!s->ptr) {
+        capture_current_time(s);
+    }
+}
+
+static void ds1338_event(I2CSlave *i2c, enum i2c_event event)
+{
+    DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
+
+    switch (event) {
+    case I2C_START_RECV:
+        /* In h/w, capture happens on any START condition, not just a
+         * START_RECV, but there is no need to actually capture on
+         * START_SEND, because the guest can't get at that data
+         * without going through a START_RECV which would overwrite it.
+         */
+        capture_current_time(s);
+        break;
+    case I2C_START_SEND:
+        s->addr_byte = true;
+        break;
+    default:
+        break;
+    }
+}
+
+static int ds1338_recv(I2CSlave *i2c)
+{
+    DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
+    uint8_t res;
+
+    res  = s->nvram[s->ptr];
+    inc_regptr(s);
+    return res;
+}
+
+static int ds1338_send(I2CSlave *i2c, uint8_t data)
+{
+    DS1338State *s = FROM_I2C_SLAVE(DS1338State, i2c);
+    if (s->addr_byte) {
+        s->ptr = data & (NVRAM_SIZE - 1);
+        s->addr_byte = false;
+        return 0;
+    }
+    if (s->ptr < 7) {
+        /* Time register. */
+        struct tm now;
+        qemu_get_timedate(&now, s->offset);
+        switch(s->ptr) {
+        case 0:
+            /* TODO: Implement CH (stop) bit.  */
+            now.tm_sec = from_bcd(data & 0x7f);
+            break;
+        case 1:
+            now.tm_min = from_bcd(data & 0x7f);
+            break;
+        case 2:
+            if (data & HOURS_12) {
+                int tmp = from_bcd(data & (HOURS_PM - 1));
+                if (data & HOURS_PM) {
+                    tmp += 12;
+                }
+                if (tmp % 12 == 0) {
+                    tmp -= 12;
+                }
+                now.tm_hour = tmp;
+            } else {
+                now.tm_hour = from_bcd(data & (HOURS_12 - 1));
+            }
+            break;
+        case 3:
+            {
+                /* The day field is supposed to contain a value in
+                   the range 1-7. Otherwise behavior is undefined.
+                 */
+                int user_wday = (data & 7) - 1;
+                s->wday_offset = (user_wday - now.tm_wday + 7) % 7;
+            }
+            break;
+        case 4:
+            now.tm_mday = from_bcd(data & 0x3f);
+            break;
+        case 5:
+            now.tm_mon = from_bcd(data & 0x1f) - 1;
+            break;
+        case 6:
+            now.tm_year = from_bcd(data) + 100;
+            break;
+        }
+        s->offset = qemu_timedate_diff(&now);
+    } else if (s->ptr == 7) {
+        /* Control register. */
+
+        /* Ensure bits 2, 3 and 6 will read back as zero. */
+        data &= 0xB3;
+
+        /* Attempting to write the OSF flag to logic 1 leaves the
+           value unchanged. */
+        data = (data & ~CTRL_OSF) | (data & s->nvram[s->ptr] & CTRL_OSF);
+
+        s->nvram[s->ptr] = data;
+    } else {
+        s->nvram[s->ptr] = data;
+    }
+    inc_regptr(s);
+    return 0;
+}
+
+static int ds1338_init(I2CSlave *i2c)
+{
+    return 0;
+}
+
+static void ds1338_reset(DeviceState *dev)
+{
+    DS1338State *s = FROM_I2C_SLAVE(DS1338State, I2C_SLAVE(dev));
+
+    /* The clock is running and synchronized with the host */
+    s->offset = 0;
+    s->wday_offset = 0;
+    memset(s->nvram, 0, NVRAM_SIZE);
+    s->ptr = 0;
+    s->addr_byte = false;
+}
+
+static void ds1338_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+    k->init = ds1338_init;
+    k->event = ds1338_event;
+    k->recv = ds1338_recv;
+    k->send = ds1338_send;
+    dc->reset = ds1338_reset;
+    dc->vmsd = &vmstate_ds1338;
+}
+
+static const TypeInfo ds1338_info = {
+    .name          = "ds1338",
+    .parent        = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(DS1338State),
+    .class_init    = ds1338_class_init,
+};
+
+static void ds1338_register_types(void)
+{
+    type_register_static(&ds1338_info);
+}
+
+type_init(ds1338_register_types)
diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c
new file mode 100644 (file)
index 0000000..95dd01d
--- /dev/null
@@ -0,0 +1,760 @@
+/*
+ *  High Precisition Event Timer emulation
+ *
+ *  Copyright (c) 2007 Alexander Graf
+ *  Copyright (c) 2008 IBM Corporation
+ *
+ *  Authors: Beth Kon <bkon@us.ibm.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * *****************************************************************
+ *
+ * This driver attempts to emulate an HPET device in software.
+ */
+
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "ui/console.h"
+#include "qemu/timer.h"
+#include "hw/timer/hpet.h"
+#include "hw/sysbus.h"
+#include "hw/timer/mc146818rtc.h"
+#include "hw/timer/i8254.h"
+
+//#define HPET_DEBUG
+#ifdef HPET_DEBUG
+#define DPRINTF printf
+#else
+#define DPRINTF(...)
+#endif
+
+#define HPET_MSI_SUPPORT        0
+
+struct HPETState;
+typedef struct HPETTimer {  /* timers */
+    uint8_t tn;             /*timer number*/
+    QEMUTimer *qemu_timer;
+    struct HPETState *state;
+    /* Memory-mapped, software visible timer registers */
+    uint64_t config;        /* configuration/cap */
+    uint64_t cmp;           /* comparator */
+    uint64_t fsb;           /* FSB route */
+    /* Hidden register state */
+    uint64_t period;        /* Last value written to comparator */
+    uint8_t wrap_flag;      /* timer pop will indicate wrap for one-shot 32-bit
+                             * mode. Next pop will be actual timer expiration.
+                             */
+} HPETTimer;
+
+typedef struct HPETState {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    uint64_t hpet_offset;
+    qemu_irq irqs[HPET_NUM_IRQ_ROUTES];
+    uint32_t flags;
+    uint8_t rtc_irq_level;
+    qemu_irq pit_enabled;
+    uint8_t num_timers;
+    HPETTimer timer[HPET_MAX_TIMERS];
+
+    /* Memory-mapped, software visible registers */
+    uint64_t capability;        /* capabilities */
+    uint64_t config;            /* configuration */
+    uint64_t isr;               /* interrupt status reg */
+    uint64_t hpet_counter;      /* main counter */
+    uint8_t  hpet_id;           /* instance id */
+} HPETState;
+
+static uint32_t hpet_in_legacy_mode(HPETState *s)
+{
+    return s->config & HPET_CFG_LEGACY;
+}
+
+static uint32_t timer_int_route(struct HPETTimer *timer)
+{
+    return (timer->config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT;
+}
+
+static uint32_t timer_fsb_route(HPETTimer *t)
+{
+    return t->config & HPET_TN_FSB_ENABLE;
+}
+
+static uint32_t hpet_enabled(HPETState *s)
+{
+    return s->config & HPET_CFG_ENABLE;
+}
+
+static uint32_t timer_is_periodic(HPETTimer *t)
+{
+    return t->config & HPET_TN_PERIODIC;
+}
+
+static uint32_t timer_enabled(HPETTimer *t)
+{
+    return t->config & HPET_TN_ENABLE;
+}
+
+static uint32_t hpet_time_after(uint64_t a, uint64_t b)
+{
+    return ((int32_t)(b) - (int32_t)(a) < 0);
+}
+
+static uint32_t hpet_time_after64(uint64_t a, uint64_t b)
+{
+    return ((int64_t)(b) - (int64_t)(a) < 0);
+}
+
+static uint64_t ticks_to_ns(uint64_t value)
+{
+    return (muldiv64(value, HPET_CLK_PERIOD, FS_PER_NS));
+}
+
+static uint64_t ns_to_ticks(uint64_t value)
+{
+    return (muldiv64(value, FS_PER_NS, HPET_CLK_PERIOD));
+}
+
+static uint64_t hpet_fixup_reg(uint64_t new, uint64_t old, uint64_t mask)
+{
+    new &= mask;
+    new |= old & ~mask;
+    return new;
+}
+
+static int activating_bit(uint64_t old, uint64_t new, uint64_t mask)
+{
+    return (!(old & mask) && (new & mask));
+}
+
+static int deactivating_bit(uint64_t old, uint64_t new, uint64_t mask)
+{
+    return ((old & mask) && !(new & mask));
+}
+
+static uint64_t hpet_get_ticks(HPETState *s)
+{
+    return ns_to_ticks(qemu_get_clock_ns(vm_clock) + s->hpet_offset);
+}
+
+/*
+ * calculate diff between comparator value and current ticks
+ */
+static inline uint64_t hpet_calculate_diff(HPETTimer *t, uint64_t current)
+{
+
+    if (t->config & HPET_TN_32BIT) {
+        uint32_t diff, cmp;
+
+        cmp = (uint32_t)t->cmp;
+        diff = cmp - (uint32_t)current;
+        diff = (int32_t)diff > 0 ? diff : (uint32_t)1;
+        return (uint64_t)diff;
+    } else {
+        uint64_t diff, cmp;
+
+        cmp = t->cmp;
+        diff = cmp - current;
+        diff = (int64_t)diff > 0 ? diff : (uint64_t)1;
+        return diff;
+    }
+}
+
+static void update_irq(struct HPETTimer *timer, int set)
+{
+    uint64_t mask;
+    HPETState *s;
+    int route;
+
+    if (timer->tn <= 1 && hpet_in_legacy_mode(timer->state)) {
+        /* if LegacyReplacementRoute bit is set, HPET specification requires
+         * timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC,
+         * timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC.
+         */
+        route = (timer->tn == 0) ? 0 : RTC_ISA_IRQ;
+    } else {
+        route = timer_int_route(timer);
+    }
+    s = timer->state;
+    mask = 1 << timer->tn;
+    if (!set || !timer_enabled(timer) || !hpet_enabled(timer->state)) {
+        s->isr &= ~mask;
+        if (!timer_fsb_route(timer)) {
+            qemu_irq_lower(s->irqs[route]);
+        }
+    } else if (timer_fsb_route(timer)) {
+        stl_le_phys(timer->fsb >> 32, timer->fsb & 0xffffffff);
+    } else if (timer->config & HPET_TN_TYPE_LEVEL) {
+        s->isr |= mask;
+        qemu_irq_raise(s->irqs[route]);
+    } else {
+        s->isr &= ~mask;
+        qemu_irq_pulse(s->irqs[route]);
+    }
+}
+
+static void hpet_pre_save(void *opaque)
+{
+    HPETState *s = opaque;
+
+    /* save current counter value */
+    s->hpet_counter = hpet_get_ticks(s);
+}
+
+static int hpet_pre_load(void *opaque)
+{
+    HPETState *s = opaque;
+
+    /* version 1 only supports 3, later versions will load the actual value */
+    s->num_timers = HPET_MIN_TIMERS;
+    return 0;
+}
+
+static int hpet_post_load(void *opaque, int version_id)
+{
+    HPETState *s = opaque;
+
+    /* Recalculate the offset between the main counter and guest time */
+    s->hpet_offset = ticks_to_ns(s->hpet_counter) - qemu_get_clock_ns(vm_clock);
+
+    /* Push number of timers into capability returned via HPET_ID */
+    s->capability &= ~HPET_ID_NUM_TIM_MASK;
+    s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
+    hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
+
+    /* Derive HPET_MSI_SUPPORT from the capability of the first timer. */
+    s->flags &= ~(1 << HPET_MSI_SUPPORT);
+    if (s->timer[0].config & HPET_TN_FSB_CAP) {
+        s->flags |= 1 << HPET_MSI_SUPPORT;
+    }
+    return 0;
+}
+
+static bool hpet_rtc_irq_level_needed(void *opaque)
+{
+    HPETState *s = opaque;
+
+    return s->rtc_irq_level != 0;
+}
+
+static const VMStateDescription vmstate_hpet_rtc_irq_level = {
+    .name = "hpet/rtc_irq_level",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(rtc_irq_level, HPETState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_hpet_timer = {
+    .name = "hpet_timer",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT8(tn, HPETTimer),
+        VMSTATE_UINT64(config, HPETTimer),
+        VMSTATE_UINT64(cmp, HPETTimer),
+        VMSTATE_UINT64(fsb, HPETTimer),
+        VMSTATE_UINT64(period, HPETTimer),
+        VMSTATE_UINT8(wrap_flag, HPETTimer),
+        VMSTATE_TIMER(qemu_timer, HPETTimer),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static const VMStateDescription vmstate_hpet = {
+    .name = "hpet",
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .pre_save = hpet_pre_save,
+    .pre_load = hpet_pre_load,
+    .post_load = hpet_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT64(config, HPETState),
+        VMSTATE_UINT64(isr, HPETState),
+        VMSTATE_UINT64(hpet_counter, HPETState),
+        VMSTATE_UINT8_V(num_timers, HPETState, 2),
+        VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0,
+                                    vmstate_hpet_timer, HPETTimer),
+        VMSTATE_END_OF_LIST()
+    },
+    .subsections = (VMStateSubsection[]) {
+        {
+            .vmsd = &vmstate_hpet_rtc_irq_level,
+            .needed = hpet_rtc_irq_level_needed,
+        }, {
+            /* empty */
+        }
+    }
+};
+
+/*
+ * timer expiration callback
+ */
+static void hpet_timer(void *opaque)
+{
+    HPETTimer *t = opaque;
+    uint64_t diff;
+
+    uint64_t period = t->period;
+    uint64_t cur_tick = hpet_get_ticks(t->state);
+
+    if (timer_is_periodic(t) && period != 0) {
+        if (t->config & HPET_TN_32BIT) {
+            while (hpet_time_after(cur_tick, t->cmp)) {
+                t->cmp = (uint32_t)(t->cmp + t->period);
+            }
+        } else {
+            while (hpet_time_after64(cur_tick, t->cmp)) {
+                t->cmp += period;
+            }
+        }
+        diff = hpet_calculate_diff(t, cur_tick);
+        qemu_mod_timer(t->qemu_timer,
+                       qemu_get_clock_ns(vm_clock) + (int64_t)ticks_to_ns(diff));
+    } else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) {
+        if (t->wrap_flag) {
+            diff = hpet_calculate_diff(t, cur_tick);
+            qemu_mod_timer(t->qemu_timer, qemu_get_clock_ns(vm_clock) +
+                           (int64_t)ticks_to_ns(diff));
+            t->wrap_flag = 0;
+        }
+    }
+    update_irq(t, 1);
+}
+
+static void hpet_set_timer(HPETTimer *t)
+{
+    uint64_t diff;
+    uint32_t wrap_diff;  /* how many ticks until we wrap? */
+    uint64_t cur_tick = hpet_get_ticks(t->state);
+
+    /* whenever new timer is being set up, make sure wrap_flag is 0 */
+    t->wrap_flag = 0;
+    diff = hpet_calculate_diff(t, cur_tick);
+
+    /* hpet spec says in one-shot 32-bit mode, generate an interrupt when
+     * counter wraps in addition to an interrupt with comparator match.
+     */
+    if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) {
+        wrap_diff = 0xffffffff - (uint32_t)cur_tick;
+        if (wrap_diff < (uint32_t)diff) {
+            diff = wrap_diff;
+            t->wrap_flag = 1;
+        }
+    }
+    qemu_mod_timer(t->qemu_timer,
+                   qemu_get_clock_ns(vm_clock) + (int64_t)ticks_to_ns(diff));
+}
+
+static void hpet_del_timer(HPETTimer *t)
+{
+    qemu_del_timer(t->qemu_timer);
+    update_irq(t, 0);
+}
+
+#ifdef HPET_DEBUG
+static uint32_t hpet_ram_readb(void *opaque, hwaddr addr)
+{
+    printf("qemu: hpet_read b at %" PRIx64 "\n", addr);
+    return 0;
+}
+
+static uint32_t hpet_ram_readw(void *opaque, hwaddr addr)
+{
+    printf("qemu: hpet_read w at %" PRIx64 "\n", addr);
+    return 0;
+}
+#endif
+
+static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
+                              unsigned size)
+{
+    HPETState *s = opaque;
+    uint64_t cur_tick, index;
+
+    DPRINTF("qemu: Enter hpet_ram_readl at %" PRIx64 "\n", addr);
+    index = addr;
+    /*address range of all TN regs*/
+    if (index >= 0x100 && index <= 0x3ff) {
+        uint8_t timer_id = (addr - 0x100) / 0x20;
+        HPETTimer *timer = &s->timer[timer_id];
+
+        if (timer_id > s->num_timers) {
+            DPRINTF("qemu: timer id out of range\n");
+            return 0;
+        }
+
+        switch ((addr - 0x100) % 0x20) {
+        case HPET_TN_CFG:
+            return timer->config;
+        case HPET_TN_CFG + 4: // Interrupt capabilities
+            return timer->config >> 32;
+        case HPET_TN_CMP: // comparator register
+            return timer->cmp;
+        case HPET_TN_CMP + 4:
+            return timer->cmp >> 32;
+        case HPET_TN_ROUTE:
+            return timer->fsb;
+        case HPET_TN_ROUTE + 4:
+            return timer->fsb >> 32;
+        default:
+            DPRINTF("qemu: invalid hpet_ram_readl\n");
+            break;
+        }
+    } else {
+        switch (index) {
+        case HPET_ID:
+            return s->capability;
+        case HPET_PERIOD:
+            return s->capability >> 32;
+        case HPET_CFG:
+            return s->config;
+        case HPET_CFG + 4:
+            DPRINTF("qemu: invalid HPET_CFG + 4 hpet_ram_readl\n");
+            return 0;
+        case HPET_COUNTER:
+            if (hpet_enabled(s)) {
+                cur_tick = hpet_get_ticks(s);
+            } else {
+                cur_tick = s->hpet_counter;
+            }
+            DPRINTF("qemu: reading counter  = %" PRIx64 "\n", cur_tick);
+            return cur_tick;
+        case HPET_COUNTER + 4:
+            if (hpet_enabled(s)) {
+                cur_tick = hpet_get_ticks(s);
+            } else {
+                cur_tick = s->hpet_counter;
+            }
+            DPRINTF("qemu: reading counter + 4  = %" PRIx64 "\n", cur_tick);
+            return cur_tick >> 32;
+        case HPET_STATUS:
+            return s->isr;
+        default:
+            DPRINTF("qemu: invalid hpet_ram_readl\n");
+            break;
+        }
+    }
+    return 0;
+}
+
+static void hpet_ram_write(void *opaque, hwaddr addr,
+                           uint64_t value, unsigned size)
+{
+    int i;
+    HPETState *s = opaque;
+    uint64_t old_val, new_val, val, index;
+
+    DPRINTF("qemu: Enter hpet_ram_writel at %" PRIx64 " = %#x\n", addr, value);
+    index = addr;
+    old_val = hpet_ram_read(opaque, addr, 4);
+    new_val = value;
+
+    /*address range of all TN regs*/
+    if (index >= 0x100 && index <= 0x3ff) {
+        uint8_t timer_id = (addr - 0x100) / 0x20;
+        HPETTimer *timer = &s->timer[timer_id];
+
+        DPRINTF("qemu: hpet_ram_writel timer_id = %#x\n", timer_id);
+        if (timer_id > s->num_timers) {
+            DPRINTF("qemu: timer id out of range\n");
+            return;
+        }
+        switch ((addr - 0x100) % 0x20) {
+        case HPET_TN_CFG:
+            DPRINTF("qemu: hpet_ram_writel HPET_TN_CFG\n");
+            if (activating_bit(old_val, new_val, HPET_TN_FSB_ENABLE)) {
+                update_irq(timer, 0);
+            }
+            val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK);
+            timer->config = (timer->config & 0xffffffff00000000ULL) | val;
+            if (new_val & HPET_TN_32BIT) {
+                timer->cmp = (uint32_t)timer->cmp;
+                timer->period = (uint32_t)timer->period;
+            }
+            if (activating_bit(old_val, new_val, HPET_TN_ENABLE)) {
+                hpet_set_timer(timer);
+            } else if (deactivating_bit(old_val, new_val, HPET_TN_ENABLE)) {
+                hpet_del_timer(timer);
+            }
+            break;
+        case HPET_TN_CFG + 4: // Interrupt capabilities
+            DPRINTF("qemu: invalid HPET_TN_CFG+4 write\n");
+            break;
+        case HPET_TN_CMP: // comparator register
+            DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP\n");
+            if (timer->config & HPET_TN_32BIT) {
+                new_val = (uint32_t)new_val;
+            }
+            if (!timer_is_periodic(timer)
+                || (timer->config & HPET_TN_SETVAL)) {
+                timer->cmp = (timer->cmp & 0xffffffff00000000ULL) | new_val;
+            }
+            if (timer_is_periodic(timer)) {
+                /*
+                 * FIXME: Clamp period to reasonable min value?
+                 * Clamp period to reasonable max value
+                 */
+                new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1;
+                timer->period =
+                    (timer->period & 0xffffffff00000000ULL) | new_val;
+            }
+            timer->config &= ~HPET_TN_SETVAL;
+            if (hpet_enabled(s)) {
+                hpet_set_timer(timer);
+            }
+            break;
+        case HPET_TN_CMP + 4: // comparator register high order
+            DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP + 4\n");
+            if (!timer_is_periodic(timer)
+                || (timer->config & HPET_TN_SETVAL)) {
+                timer->cmp = (timer->cmp & 0xffffffffULL) | new_val << 32;
+            } else {
+                /*
+                 * FIXME: Clamp period to reasonable min value?
+                 * Clamp period to reasonable max value
+                 */
+                new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1;
+                timer->period =
+                    (timer->period & 0xffffffffULL) | new_val << 32;
+                }
+                timer->config &= ~HPET_TN_SETVAL;
+                if (hpet_enabled(s)) {
+                    hpet_set_timer(timer);
+                }
+                break;
+        case HPET_TN_ROUTE:
+            timer->fsb = (timer->fsb & 0xffffffff00000000ULL) | new_val;
+            break;
+        case HPET_TN_ROUTE + 4:
+            timer->fsb = (new_val << 32) | (timer->fsb & 0xffffffff);
+            break;
+        default:
+            DPRINTF("qemu: invalid hpet_ram_writel\n");
+            break;
+        }
+        return;
+    } else {
+        switch (index) {
+        case HPET_ID:
+            return;
+        case HPET_CFG:
+            val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK);
+            s->config = (s->config & 0xffffffff00000000ULL) | val;
+            if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
+                /* Enable main counter and interrupt generation. */
+                s->hpet_offset =
+                    ticks_to_ns(s->hpet_counter) - qemu_get_clock_ns(vm_clock);
+                for (i = 0; i < s->num_timers; i++) {
+                    if ((&s->timer[i])->cmp != ~0ULL) {
+                        hpet_set_timer(&s->timer[i]);
+                    }
+                }
+            } else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
+                /* Halt main counter and disable interrupt generation. */
+                s->hpet_counter = hpet_get_ticks(s);
+                for (i = 0; i < s->num_timers; i++) {
+                    hpet_del_timer(&s->timer[i]);
+                }
+            }
+            /* i8254 and RTC output pins are disabled
+             * when HPET is in legacy mode */
+            if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
+                qemu_set_irq(s->pit_enabled, 0);
+                qemu_irq_lower(s->irqs[0]);
+                qemu_irq_lower(s->irqs[RTC_ISA_IRQ]);
+            } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
+                qemu_irq_lower(s->irqs[0]);
+                qemu_set_irq(s->pit_enabled, 1);
+                qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level);
+            }
+            break;
+        case HPET_CFG + 4:
+            DPRINTF("qemu: invalid HPET_CFG+4 write\n");
+            break;
+        case HPET_STATUS:
+            val = new_val & s->isr;
+            for (i = 0; i < s->num_timers; i++) {
+                if (val & (1 << i)) {
+                    update_irq(&s->timer[i], 0);
+                }
+            }
+            break;
+        case HPET_COUNTER:
+            if (hpet_enabled(s)) {
+                DPRINTF("qemu: Writing counter while HPET enabled!\n");
+            }
+            s->hpet_counter =
+                (s->hpet_counter & 0xffffffff00000000ULL) | value;
+            DPRINTF("qemu: HPET counter written. ctr = %#x -> %" PRIx64 "\n",
+                    value, s->hpet_counter);
+            break;
+        case HPET_COUNTER + 4:
+            if (hpet_enabled(s)) {
+                DPRINTF("qemu: Writing counter while HPET enabled!\n");
+            }
+            s->hpet_counter =
+                (s->hpet_counter & 0xffffffffULL) | (((uint64_t)value) << 32);
+            DPRINTF("qemu: HPET counter + 4 written. ctr = %#x -> %" PRIx64 "\n",
+                    value, s->hpet_counter);
+            break;
+        default:
+            DPRINTF("qemu: invalid hpet_ram_writel\n");
+            break;
+        }
+    }
+}
+
+static const MemoryRegionOps hpet_ram_ops = {
+    .read = hpet_ram_read,
+    .write = hpet_ram_write,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void hpet_reset(DeviceState *d)
+{
+    HPETState *s = FROM_SYSBUS(HPETState, SYS_BUS_DEVICE(d));
+    int i;
+
+    for (i = 0; i < s->num_timers; i++) {
+        HPETTimer *timer = &s->timer[i];
+
+        hpet_del_timer(timer);
+        timer->cmp = ~0ULL;
+        timer->config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP;
+        if (s->flags & (1 << HPET_MSI_SUPPORT)) {
+            timer->config |= HPET_TN_FSB_CAP;
+        }
+        /* advertise availability of ioapic inti2 */
+        timer->config |=  0x00000004ULL << 32;
+        timer->period = 0ULL;
+        timer->wrap_flag = 0;
+    }
+
+    qemu_set_irq(s->pit_enabled, 1);
+    s->hpet_counter = 0ULL;
+    s->hpet_offset = 0ULL;
+    s->config = 0ULL;
+    hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability;
+    hpet_cfg.hpet[s->hpet_id].address = SYS_BUS_DEVICE(d)->mmio[0].addr;
+
+    /* to document that the RTC lowers its output on reset as well */
+    s->rtc_irq_level = 0;
+}
+
+static void hpet_handle_legacy_irq(void *opaque, int n, int level)
+{
+    HPETState *s = FROM_SYSBUS(HPETState, opaque);
+
+    if (n == HPET_LEGACY_PIT_INT) {
+        if (!hpet_in_legacy_mode(s)) {
+            qemu_set_irq(s->irqs[0], level);
+        }
+    } else {
+        s->rtc_irq_level = level;
+        if (!hpet_in_legacy_mode(s)) {
+            qemu_set_irq(s->irqs[RTC_ISA_IRQ], level);
+        }
+    }
+}
+
+static int hpet_init(SysBusDevice *dev)
+{
+    HPETState *s = FROM_SYSBUS(HPETState, dev);
+    int i;
+    HPETTimer *timer;
+
+    if (hpet_cfg.count == UINT8_MAX) {
+        /* first instance */
+        hpet_cfg.count = 0;
+    }
+
+    if (hpet_cfg.count == 8) {
+        fprintf(stderr, "Only 8 instances of HPET is allowed\n");
+        return -1;
+    }
+
+    s->hpet_id = hpet_cfg.count++;
+
+    for (i = 0; i < HPET_NUM_IRQ_ROUTES; i++) {
+        sysbus_init_irq(dev, &s->irqs[i]);
+    }
+
+    if (s->num_timers < HPET_MIN_TIMERS) {
+        s->num_timers = HPET_MIN_TIMERS;
+    } else if (s->num_timers > HPET_MAX_TIMERS) {
+        s->num_timers = HPET_MAX_TIMERS;
+    }
+    for (i = 0; i < HPET_MAX_TIMERS; i++) {
+        timer = &s->timer[i];
+        timer->qemu_timer = qemu_new_timer_ns(vm_clock, hpet_timer, timer);
+        timer->tn = i;
+        timer->state = s;
+    }
+
+    /* 64-bit main counter; LegacyReplacementRoute. */
+    s->capability = 0x8086a001ULL;
+    s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT;
+    s->capability |= ((HPET_CLK_PERIOD) << 32);
+
+    qdev_init_gpio_in(&dev->qdev, hpet_handle_legacy_irq, 2);
+    qdev_init_gpio_out(&dev->qdev, &s->pit_enabled, 1);
+
+    /* HPET Area */
+    memory_region_init_io(&s->iomem, &hpet_ram_ops, s, "hpet", 0x400);
+    sysbus_init_mmio(dev, &s->iomem);
+    return 0;
+}
+
+static Property hpet_device_properties[] = {
+    DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS),
+    DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void hpet_device_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = hpet_init;
+    dc->no_user = 1;
+    dc->reset = hpet_reset;
+    dc->vmsd = &vmstate_hpet;
+    dc->props = hpet_device_properties;
+}
+
+static const TypeInfo hpet_device_info = {
+    .name          = "hpet",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(HPETState),
+    .class_init    = hpet_device_class_init,
+};
+
+static void hpet_register_types(void)
+{
+    type_register_static(&hpet_device_info);
+}
+
+type_init(hpet_register_types)
diff --git a/hw/timer/i8254.c b/hw/timer/i8254.c
new file mode 100644 (file)
index 0000000..20c0c36
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * QEMU 8253/8254 interval timer emulation
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "hw/isa/isa.h"
+#include "qemu/timer.h"
+#include "hw/timer/i8254.h"
+#include "hw/timer/i8254_internal.h"
+
+//#define DEBUG_PIT
+
+#define RW_STATE_LSB 1
+#define RW_STATE_MSB 2
+#define RW_STATE_WORD0 3
+#define RW_STATE_WORD1 4
+
+static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
+
+static int pit_get_count(PITChannelState *s)
+{
+    uint64_t d;
+    int counter;
+
+    d = muldiv64(qemu_get_clock_ns(vm_clock) - s->count_load_time, PIT_FREQ,
+                 get_ticks_per_sec());
+    switch(s->mode) {
+    case 0:
+    case 1:
+    case 4:
+    case 5:
+        counter = (s->count - d) & 0xffff;
+        break;
+    case 3:
+        /* XXX: may be incorrect for odd counts */
+        counter = s->count - ((2 * d) % s->count);
+        break;
+    default:
+        counter = s->count - (d % s->count);
+        break;
+    }
+    return counter;
+}
+
+/* val must be 0 or 1 */
+static void pit_set_channel_gate(PITCommonState *s, PITChannelState *sc,
+                                 int val)
+{
+    switch (sc->mode) {
+    default:
+    case 0:
+    case 4:
+        /* XXX: just disable/enable counting */
+        break;
+    case 1:
+    case 5:
+        if (sc->gate < val) {
+            /* restart counting on rising edge */
+            sc->count_load_time = qemu_get_clock_ns(vm_clock);
+            pit_irq_timer_update(sc, sc->count_load_time);
+        }
+        break;
+    case 2:
+    case 3:
+        if (sc->gate < val) {
+            /* restart counting on rising edge */
+            sc->count_load_time = qemu_get_clock_ns(vm_clock);
+            pit_irq_timer_update(sc, sc->count_load_time);
+        }
+        /* XXX: disable/enable counting */
+        break;
+    }
+    sc->gate = val;
+}
+
+static inline void pit_load_count(PITChannelState *s, int val)
+{
+    if (val == 0)
+        val = 0x10000;
+    s->count_load_time = qemu_get_clock_ns(vm_clock);
+    s->count = val;
+    pit_irq_timer_update(s, s->count_load_time);
+}
+
+/* if already latched, do not latch again */
+static void pit_latch_count(PITChannelState *s)
+{
+    if (!s->count_latched) {
+        s->latched_count = pit_get_count(s);
+        s->count_latched = s->rw_mode;
+    }
+}
+
+static void pit_ioport_write(void *opaque, hwaddr addr,
+                             uint64_t val, unsigned size)
+{
+    PITCommonState *pit = opaque;
+    int channel, access;
+    PITChannelState *s;
+
+    addr &= 3;
+    if (addr == 3) {
+        channel = val >> 6;
+        if (channel == 3) {
+            /* read back command */
+            for(channel = 0; channel < 3; channel++) {
+                s = &pit->channels[channel];
+                if (val & (2 << channel)) {
+                    if (!(val & 0x20)) {
+                        pit_latch_count(s);
+                    }
+                    if (!(val & 0x10) && !s->status_latched) {
+                        /* status latch */
+                        /* XXX: add BCD and null count */
+                        s->status =
+                            (pit_get_out(s,
+                                         qemu_get_clock_ns(vm_clock)) << 7) |
+                            (s->rw_mode << 4) |
+                            (s->mode << 1) |
+                            s->bcd;
+                        s->status_latched = 1;
+                    }
+                }
+            }
+        } else {
+            s = &pit->channels[channel];
+            access = (val >> 4) & 3;
+            if (access == 0) {
+                pit_latch_count(s);
+            } else {
+                s->rw_mode = access;
+                s->read_state = access;
+                s->write_state = access;
+
+                s->mode = (val >> 1) & 7;
+                s->bcd = val & 1;
+                /* XXX: update irq timer ? */
+            }
+        }
+    } else {
+        s = &pit->channels[addr];
+        switch(s->write_state) {
+        default:
+        case RW_STATE_LSB:
+            pit_load_count(s, val);
+            break;
+        case RW_STATE_MSB:
+            pit_load_count(s, val << 8);
+            break;
+        case RW_STATE_WORD0:
+            s->write_latch = val;
+            s->write_state = RW_STATE_WORD1;
+            break;
+        case RW_STATE_WORD1:
+            pit_load_count(s, s->write_latch | (val << 8));
+            s->write_state = RW_STATE_WORD0;
+            break;
+        }
+    }
+}
+
+static uint64_t pit_ioport_read(void *opaque, hwaddr addr,
+                                unsigned size)
+{
+    PITCommonState *pit = opaque;
+    int ret, count;
+    PITChannelState *s;
+
+    addr &= 3;
+    s = &pit->channels[addr];
+    if (s->status_latched) {
+        s->status_latched = 0;
+        ret = s->status;
+    } else if (s->count_latched) {
+        switch(s->count_latched) {
+        default:
+        case RW_STATE_LSB:
+            ret = s->latched_count & 0xff;
+            s->count_latched = 0;
+            break;
+        case RW_STATE_MSB:
+            ret = s->latched_count >> 8;
+            s->count_latched = 0;
+            break;
+        case RW_STATE_WORD0:
+            ret = s->latched_count & 0xff;
+            s->count_latched = RW_STATE_MSB;
+            break;
+        }
+    } else {
+        switch(s->read_state) {
+        default:
+        case RW_STATE_LSB:
+            count = pit_get_count(s);
+            ret = count & 0xff;
+            break;
+        case RW_STATE_MSB:
+            count = pit_get_count(s);
+            ret = (count >> 8) & 0xff;
+            break;
+        case RW_STATE_WORD0:
+            count = pit_get_count(s);
+            ret = count & 0xff;
+            s->read_state = RW_STATE_WORD1;
+            break;
+        case RW_STATE_WORD1:
+            count = pit_get_count(s);
+            ret = (count >> 8) & 0xff;
+            s->read_state = RW_STATE_WORD0;
+            break;
+        }
+    }
+    return ret;
+}
+
+static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
+{
+    int64_t expire_time;
+    int irq_level;
+
+    if (!s->irq_timer || s->irq_disabled) {
+        return;
+    }
+    expire_time = pit_get_next_transition_time(s, current_time);
+    irq_level = pit_get_out(s, current_time);
+    qemu_set_irq(s->irq, irq_level);
+#ifdef DEBUG_PIT
+    printf("irq_level=%d next_delay=%f\n",
+           irq_level,
+           (double)(expire_time - current_time) / get_ticks_per_sec());
+#endif
+    s->next_transition_time = expire_time;
+    if (expire_time != -1)
+        qemu_mod_timer(s->irq_timer, expire_time);
+    else
+        qemu_del_timer(s->irq_timer);
+}
+
+static void pit_irq_timer(void *opaque)
+{
+    PITChannelState *s = opaque;
+
+    pit_irq_timer_update(s, s->next_transition_time);
+}
+
+static void pit_reset(DeviceState *dev)
+{
+    PITCommonState *pit = DO_UPCAST(PITCommonState, dev.qdev, dev);
+    PITChannelState *s;
+
+    pit_reset_common(pit);
+
+    s = &pit->channels[0];
+    if (!s->irq_disabled) {
+        qemu_mod_timer(s->irq_timer, s->next_transition_time);
+    }
+}
+
+/* When HPET is operating in legacy mode, suppress the ignored timer IRQ,
+ * reenable it when legacy mode is left again. */
+static void pit_irq_control(void *opaque, int n, int enable)
+{
+    PITCommonState *pit = opaque;
+    PITChannelState *s = &pit->channels[0];
+
+    if (enable) {
+        s->irq_disabled = 0;
+        pit_irq_timer_update(s, qemu_get_clock_ns(vm_clock));
+    } else {
+        s->irq_disabled = 1;
+        qemu_del_timer(s->irq_timer);
+    }
+}
+
+static const MemoryRegionOps pit_ioport_ops = {
+    .read = pit_ioport_read,
+    .write = pit_ioport_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void pit_post_load(PITCommonState *s)
+{
+    PITChannelState *sc = &s->channels[0];
+
+    if (sc->next_transition_time != -1) {
+        qemu_mod_timer(sc->irq_timer, sc->next_transition_time);
+    } else {
+        qemu_del_timer(sc->irq_timer);
+    }
+}
+
+static int pit_initfn(PITCommonState *pit)
+{
+    PITChannelState *s;
+
+    s = &pit->channels[0];
+    /* the timer 0 is connected to an IRQ */
+    s->irq_timer = qemu_new_timer_ns(vm_clock, pit_irq_timer, s);
+    qdev_init_gpio_out(&pit->dev.qdev, &s->irq, 1);
+
+    memory_region_init_io(&pit->ioports, &pit_ioport_ops, pit, "pit", 4);
+
+    qdev_init_gpio_in(&pit->dev.qdev, pit_irq_control, 1);
+
+    return 0;
+}
+
+static Property pit_properties[] = {
+    DEFINE_PROP_HEX32("iobase", PITCommonState, iobase,  -1),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pit_class_initfn(ObjectClass *klass, void *data)
+{
+    PITCommonClass *k = PIT_COMMON_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    k->init = pit_initfn;
+    k->set_channel_gate = pit_set_channel_gate;
+    k->get_channel_info = pit_get_channel_info_common;
+    k->post_load = pit_post_load;
+    dc->reset = pit_reset;
+    dc->props = pit_properties;
+}
+
+static const TypeInfo pit_info = {
+    .name          = "isa-pit",
+    .parent        = TYPE_PIT_COMMON,
+    .instance_size = sizeof(PITCommonState),
+    .class_init    = pit_class_initfn,
+};
+
+static void pit_register_types(void)
+{
+    type_register_static(&pit_info);
+}
+
+type_init(pit_register_types)
diff --git a/hw/timer/i8254_common.c b/hw/timer/i8254_common.c
new file mode 100644 (file)
index 0000000..5342df4
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * QEMU 8253/8254 - common bits of emulated and KVM kernel model
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ * Copyright (c) 2012      Jan Kiszka, Siemens AG
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/i386/pc.h"
+#include "hw/isa/isa.h"
+#include "qemu/timer.h"
+#include "hw/timer/i8254.h"
+#include "hw/timer/i8254_internal.h"
+
+/* val must be 0 or 1 */
+void pit_set_gate(ISADevice *dev, int channel, int val)
+{
+    PITCommonState *pit = PIT_COMMON(dev);
+    PITChannelState *s = &pit->channels[channel];
+    PITCommonClass *c = PIT_COMMON_GET_CLASS(pit);
+
+    c->set_channel_gate(pit, s, val);
+}
+
+/* get pit output bit */
+int pit_get_out(PITChannelState *s, int64_t current_time)
+{
+    uint64_t d;
+    int out;
+
+    d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
+                 get_ticks_per_sec());
+    switch (s->mode) {
+    default:
+    case 0:
+        out = (d >= s->count);
+        break;
+    case 1:
+        out = (d < s->count);
+        break;
+    case 2:
+        if ((d % s->count) == 0 && d != 0) {
+            out = 1;
+        } else {
+            out = 0;
+        }
+        break;
+    case 3:
+        out = (d % s->count) < ((s->count + 1) >> 1);
+        break;
+    case 4:
+    case 5:
+        out = (d == s->count);
+        break;
+    }
+    return out;
+}
+
+/* return -1 if no transition will occur.  */
+int64_t pit_get_next_transition_time(PITChannelState *s, int64_t current_time)
+{
+    uint64_t d, next_time, base;
+    int period2;
+
+    d = muldiv64(current_time - s->count_load_time, PIT_FREQ,
+                 get_ticks_per_sec());
+    switch (s->mode) {
+    default:
+    case 0:
+    case 1:
+        if (d < s->count) {
+            next_time = s->count;
+        } else {
+            return -1;
+        }
+        break;
+    case 2:
+        base = (d / s->count) * s->count;
+        if ((d - base) == 0 && d != 0) {
+            next_time = base + s->count;
+        } else {
+            next_time = base + s->count + 1;
+        }
+        break;
+    case 3:
+        base = (d / s->count) * s->count;
+        period2 = ((s->count + 1) >> 1);
+        if ((d - base) < period2) {
+            next_time = base + period2;
+        } else {
+            next_time = base + s->count;
+        }
+        break;
+    case 4:
+    case 5:
+        if (d < s->count) {
+            next_time = s->count;
+        } else if (d == s->count) {
+            next_time = s->count + 1;
+        } else {
+            return -1;
+        }
+        break;
+    }
+    /* convert to timer units */
+    next_time = s->count_load_time + muldiv64(next_time, get_ticks_per_sec(),
+                                              PIT_FREQ);
+    /* fix potential rounding problems */
+    /* XXX: better solution: use a clock at PIT_FREQ Hz */
+    if (next_time <= current_time) {
+        next_time = current_time + 1;
+    }
+    return next_time;
+}
+
+void pit_get_channel_info_common(PITCommonState *s, PITChannelState *sc,
+                                 PITChannelInfo *info)
+{
+    info->gate = sc->gate;
+    info->mode = sc->mode;
+    info->initial_count = sc->count;
+    info->out = pit_get_out(sc, qemu_get_clock_ns(vm_clock));
+}
+
+void pit_get_channel_info(ISADevice *dev, int channel, PITChannelInfo *info)
+{
+    PITCommonState *pit = PIT_COMMON(dev);
+    PITChannelState *s = &pit->channels[channel];
+    PITCommonClass *c = PIT_COMMON_GET_CLASS(pit);
+
+    c->get_channel_info(pit, s, info);
+}
+
+void pit_reset_common(PITCommonState *pit)
+{
+    PITChannelState *s;
+    int i;
+
+    for (i = 0; i < 3; i++) {
+        s = &pit->channels[i];
+        s->mode = 3;
+        s->gate = (i != 2);
+        s->count_load_time = qemu_get_clock_ns(vm_clock);
+        s->count = 0x10000;
+        if (i == 0 && !s->irq_disabled) {
+            s->next_transition_time =
+                pit_get_next_transition_time(s, s->count_load_time);
+        }
+    }
+}
+
+static int pit_init_common(ISADevice *dev)
+{
+    PITCommonState *pit = PIT_COMMON(dev);
+    PITCommonClass *c = PIT_COMMON_GET_CLASS(pit);
+    int ret;
+
+    ret = c->init(pit);
+    if (ret < 0) {
+        return ret;
+    }
+
+    isa_register_ioport(dev, &pit->ioports, pit->iobase);
+
+    qdev_set_legacy_instance_id(&dev->qdev, pit->iobase, 2);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_pit_channel = {
+    .name = "pit channel",
+    .version_id = 2,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields = (VMStateField[]) {
+        VMSTATE_INT32(count, PITChannelState),
+        VMSTATE_UINT16(latched_count, PITChannelState),
+        VMSTATE_UINT8(count_latched, PITChannelState),
+        VMSTATE_UINT8(status_latched, PITChannelState),
+        VMSTATE_UINT8(status, PITChannelState),
+        VMSTATE_UINT8(read_state, PITChannelState),
+        VMSTATE_UINT8(write_state, PITChannelState),
+        VMSTATE_UINT8(write_latch, PITChannelState),
+        VMSTATE_UINT8(rw_mode, PITChannelState),
+        VMSTATE_UINT8(mode, PITChannelState),
+        VMSTATE_UINT8(bcd, PITChannelState),
+        VMSTATE_UINT8(gate, PITChannelState),
+        VMSTATE_INT64(count_load_time, PITChannelState),
+        VMSTATE_INT64(next_transition_time, PITChannelState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int pit_load_old(QEMUFile *f, void *opaque, int version_id)
+{
+    PITCommonState *pit = opaque;
+    PITCommonClass *c = PIT_COMMON_GET_CLASS(pit);
+    PITChannelState *s;
+    int i;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    for (i = 0; i < 3; i++) {
+        s = &pit->channels[i];
+        s->count = qemu_get_be32(f);
+        qemu_get_be16s(f, &s->latched_count);
+        qemu_get_8s(f, &s->count_latched);
+        qemu_get_8s(f, &s->status_latched);
+        qemu_get_8s(f, &s->status);
+        qemu_get_8s(f, &s->read_state);
+        qemu_get_8s(f, &s->write_state);
+        qemu_get_8s(f, &s->write_latch);
+        qemu_get_8s(f, &s->rw_mode);
+        qemu_get_8s(f, &s->mode);
+        qemu_get_8s(f, &s->bcd);
+        qemu_get_8s(f, &s->gate);
+        s->count_load_time = qemu_get_be64(f);
+        s->irq_disabled = 0;
+        if (i == 0) {
+            s->next_transition_time = qemu_get_be64(f);
+        }
+    }
+    if (c->post_load) {
+        c->post_load(pit);
+    }
+    return 0;
+}
+
+static void pit_dispatch_pre_save(void *opaque)
+{
+    PITCommonState *s = opaque;
+    PITCommonClass *c = PIT_COMMON_GET_CLASS(s);
+
+    if (c->pre_save) {
+        c->pre_save(s);
+    }
+}
+
+static int pit_dispatch_post_load(void *opaque, int version_id)
+{
+    PITCommonState *s = opaque;
+    PITCommonClass *c = PIT_COMMON_GET_CLASS(s);
+
+    if (c->post_load) {
+        c->post_load(s);
+    }
+    return 0;
+}
+
+static const VMStateDescription vmstate_pit_common = {
+    .name = "i8254",
+    .version_id = 3,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 1,
+    .load_state_old = pit_load_old,
+    .pre_save = pit_dispatch_pre_save,
+    .post_load = pit_dispatch_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_V(channels[0].irq_disabled, PITCommonState, 3),
+        VMSTATE_STRUCT_ARRAY(channels, PITCommonState, 3, 2,
+                             vmstate_pit_channel, PITChannelState),
+        VMSTATE_INT64(channels[0].next_transition_time,
+                      PITCommonState), /* formerly irq_timer */
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void pit_common_class_init(ObjectClass *klass, void *data)
+{
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    ic->init = pit_init_common;
+    dc->vmsd = &vmstate_pit_common;
+    dc->no_user = 1;
+}
+
+static const TypeInfo pit_common_type = {
+    .name          = TYPE_PIT_COMMON,
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(PITCommonState),
+    .class_size    = sizeof(PITCommonClass),
+    .class_init    = pit_common_class_init,
+    .abstract      = true,
+};
+
+static void register_devices(void)
+{
+    type_register_static(&pit_common_type);
+}
+
+type_init(register_devices);
diff --git a/hw/timer/m48t59.c b/hw/timer/m48t59.c
new file mode 100644 (file)
index 0000000..5019e06
--- /dev/null
@@ -0,0 +1,778 @@
+/*
+ * QEMU M48T59 and M48T08 NVRAM emulation for PPC PREP and Sparc platforms
+ *
+ * Copyright (c) 2003-2005, 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/timer/m48t59.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+#include "hw/sysbus.h"
+#include "hw/isa/isa.h"
+#include "exec/address-spaces.h"
+
+//#define DEBUG_NVRAM
+
+#if defined(DEBUG_NVRAM)
+#define NVRAM_PRINTF(fmt, ...) do { printf(fmt , ## __VA_ARGS__); } while (0)
+#else
+#define NVRAM_PRINTF(fmt, ...) do { } while (0)
+#endif
+
+/*
+ * The M48T02, M48T08 and M48T59 chips are very similar. The newer '59 has
+ * alarm and a watchdog timer and related control registers. In the
+ * PPC platform there is also a nvram lock function.
+ */
+
+/*
+ * Chipset docs:
+ * http://www.st.com/stonline/products/literature/ds/2410/m48t02.pdf
+ * http://www.st.com/stonline/products/literature/ds/2411/m48t08.pdf
+ * http://www.st.com/stonline/products/literature/od/7001/m48t59y.pdf
+ */
+
+struct M48t59State {
+    /* Hardware parameters */
+    qemu_irq IRQ;
+    MemoryRegion iomem;
+    uint32_t io_base;
+    uint32_t size;
+    /* RTC management */
+    time_t   time_offset;
+    time_t   stop_time;
+    /* Alarm & watchdog */
+    struct tm alarm;
+    struct QEMUTimer *alrm_timer;
+    struct QEMUTimer *wd_timer;
+    /* NVRAM storage */
+    uint8_t *buffer;
+    /* Model parameters */
+    uint32_t model; /* 2 = m48t02, 8 = m48t08, 59 = m48t59 */
+    /* NVRAM storage */
+    uint16_t addr;
+    uint8_t  lock;
+};
+
+typedef struct M48t59ISAState {
+    ISADevice busdev;
+    M48t59State state;
+    MemoryRegion io;
+} M48t59ISAState;
+
+typedef struct M48t59SysBusState {
+    SysBusDevice busdev;
+    M48t59State state;
+    MemoryRegion io;
+} M48t59SysBusState;
+
+/* Fake timer functions */
+
+/* Alarm management */
+static void alarm_cb (void *opaque)
+{
+    struct tm tm;
+    uint64_t next_time;
+    M48t59State *NVRAM = opaque;
+
+    qemu_set_irq(NVRAM->IRQ, 1);
+    if ((NVRAM->buffer[0x1FF5] & 0x80) == 0 &&
+       (NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
+       (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
+       (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
+        /* Repeat once a month */
+        qemu_get_timedate(&tm, NVRAM->time_offset);
+        tm.tm_mon++;
+        if (tm.tm_mon == 13) {
+            tm.tm_mon = 1;
+            tm.tm_year++;
+        }
+        next_time = qemu_timedate_diff(&tm) - NVRAM->time_offset;
+    } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
+              (NVRAM->buffer[0x1FF4] & 0x80) == 0 &&
+              (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
+              (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
+        /* Repeat once a day */
+        next_time = 24 * 60 * 60;
+    } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
+              (NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
+              (NVRAM->buffer[0x1FF3] & 0x80) == 0 &&
+              (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
+        /* Repeat once an hour */
+        next_time = 60 * 60;
+    } else if ((NVRAM->buffer[0x1FF5] & 0x80) != 0 &&
+              (NVRAM->buffer[0x1FF4] & 0x80) != 0 &&
+              (NVRAM->buffer[0x1FF3] & 0x80) != 0 &&
+              (NVRAM->buffer[0x1FF2] & 0x80) == 0) {
+        /* Repeat once a minute */
+        next_time = 60;
+    } else {
+        /* Repeat once a second */
+        next_time = 1;
+    }
+    qemu_mod_timer(NVRAM->alrm_timer, qemu_get_clock_ns(rtc_clock) +
+                    next_time * 1000);
+    qemu_set_irq(NVRAM->IRQ, 0);
+}
+
+static void set_alarm(M48t59State *NVRAM)
+{
+    int diff;
+    if (NVRAM->alrm_timer != NULL) {
+        qemu_del_timer(NVRAM->alrm_timer);
+        diff = qemu_timedate_diff(&NVRAM->alarm) - NVRAM->time_offset;
+        if (diff > 0)
+            qemu_mod_timer(NVRAM->alrm_timer, diff * 1000);
+    }
+}
+
+/* RTC management helpers */
+static inline void get_time(M48t59State *NVRAM, struct tm *tm)
+{
+    qemu_get_timedate(tm, NVRAM->time_offset);
+}
+
+static void set_time(M48t59State *NVRAM, struct tm *tm)
+{
+    NVRAM->time_offset = qemu_timedate_diff(tm);
+    set_alarm(NVRAM);
+}
+
+/* Watchdog management */
+static void watchdog_cb (void *opaque)
+{
+    M48t59State *NVRAM = opaque;
+
+    NVRAM->buffer[0x1FF0] |= 0x80;
+    if (NVRAM->buffer[0x1FF7] & 0x80) {
+       NVRAM->buffer[0x1FF7] = 0x00;
+       NVRAM->buffer[0x1FFC] &= ~0x40;
+        /* May it be a hw CPU Reset instead ? */
+        qemu_system_reset_request();
+    } else {
+       qemu_set_irq(NVRAM->IRQ, 1);
+       qemu_set_irq(NVRAM->IRQ, 0);
+    }
+}
+
+static void set_up_watchdog(M48t59State *NVRAM, uint8_t value)
+{
+    uint64_t interval; /* in 1/16 seconds */
+
+    NVRAM->buffer[0x1FF0] &= ~0x80;
+    if (NVRAM->wd_timer != NULL) {
+        qemu_del_timer(NVRAM->wd_timer);
+        if (value != 0) {
+            interval = (1 << (2 * (value & 0x03))) * ((value >> 2) & 0x1F);
+            qemu_mod_timer(NVRAM->wd_timer, ((uint64_t)time(NULL) * 1000) +
+                           ((interval * 1000) >> 4));
+        }
+    }
+}
+
+/* Direct access to NVRAM */
+void m48t59_write (void *opaque, uint32_t addr, uint32_t val)
+{
+    M48t59State *NVRAM = opaque;
+    struct tm tm;
+    int tmp;
+
+    if (addr > 0x1FF8 && addr < 0x2000)
+       NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val);
+
+    /* check for NVRAM access */
+    if ((NVRAM->model == 2 && addr < 0x7f8) ||
+        (NVRAM->model == 8 && addr < 0x1ff8) ||
+        (NVRAM->model == 59 && addr < 0x1ff0)) {
+        goto do_write;
+    }
+
+    /* TOD access */
+    switch (addr) {
+    case 0x1FF0:
+        /* flags register : read-only */
+        break;
+    case 0x1FF1:
+        /* unused */
+        break;
+    case 0x1FF2:
+        /* alarm seconds */
+        tmp = from_bcd(val & 0x7F);
+        if (tmp >= 0 && tmp <= 59) {
+            NVRAM->alarm.tm_sec = tmp;
+            NVRAM->buffer[0x1FF2] = val;
+            set_alarm(NVRAM);
+        }
+        break;
+    case 0x1FF3:
+        /* alarm minutes */
+        tmp = from_bcd(val & 0x7F);
+        if (tmp >= 0 && tmp <= 59) {
+            NVRAM->alarm.tm_min = tmp;
+            NVRAM->buffer[0x1FF3] = val;
+            set_alarm(NVRAM);
+        }
+        break;
+    case 0x1FF4:
+        /* alarm hours */
+        tmp = from_bcd(val & 0x3F);
+        if (tmp >= 0 && tmp <= 23) {
+            NVRAM->alarm.tm_hour = tmp;
+            NVRAM->buffer[0x1FF4] = val;
+            set_alarm(NVRAM);
+        }
+        break;
+    case 0x1FF5:
+        /* alarm date */
+        tmp = from_bcd(val & 0x3F);
+        if (tmp != 0) {
+            NVRAM->alarm.tm_mday = tmp;
+            NVRAM->buffer[0x1FF5] = val;
+            set_alarm(NVRAM);
+        }
+        break;
+    case 0x1FF6:
+        /* interrupts */
+        NVRAM->buffer[0x1FF6] = val;
+        break;
+    case 0x1FF7:
+        /* watchdog */
+        NVRAM->buffer[0x1FF7] = val;
+        set_up_watchdog(NVRAM, val);
+        break;
+    case 0x1FF8:
+    case 0x07F8:
+        /* control */
+       NVRAM->buffer[addr] = (val & ~0xA0) | 0x90;
+        break;
+    case 0x1FF9:
+    case 0x07F9:
+        /* seconds (BCD) */
+       tmp = from_bcd(val & 0x7F);
+       if (tmp >= 0 && tmp <= 59) {
+           get_time(NVRAM, &tm);
+           tm.tm_sec = tmp;
+           set_time(NVRAM, &tm);
+       }
+        if ((val & 0x80) ^ (NVRAM->buffer[addr] & 0x80)) {
+           if (val & 0x80) {
+               NVRAM->stop_time = time(NULL);
+           } else {
+               NVRAM->time_offset += NVRAM->stop_time - time(NULL);
+               NVRAM->stop_time = 0;
+           }
+       }
+        NVRAM->buffer[addr] = val & 0x80;
+        break;
+    case 0x1FFA:
+    case 0x07FA:
+        /* minutes (BCD) */
+       tmp = from_bcd(val & 0x7F);
+       if (tmp >= 0 && tmp <= 59) {
+           get_time(NVRAM, &tm);
+           tm.tm_min = tmp;
+           set_time(NVRAM, &tm);
+       }
+        break;
+    case 0x1FFB:
+    case 0x07FB:
+        /* hours (BCD) */
+       tmp = from_bcd(val & 0x3F);
+       if (tmp >= 0 && tmp <= 23) {
+           get_time(NVRAM, &tm);
+           tm.tm_hour = tmp;
+           set_time(NVRAM, &tm);
+       }
+        break;
+    case 0x1FFC:
+    case 0x07FC:
+        /* day of the week / century */
+       tmp = from_bcd(val & 0x07);
+       get_time(NVRAM, &tm);
+       tm.tm_wday = tmp;
+       set_time(NVRAM, &tm);
+        NVRAM->buffer[addr] = val & 0x40;
+        break;
+    case 0x1FFD:
+    case 0x07FD:
+        /* date (BCD) */
+       tmp = from_bcd(val & 0x3F);
+       if (tmp != 0) {
+           get_time(NVRAM, &tm);
+           tm.tm_mday = tmp;
+           set_time(NVRAM, &tm);
+       }
+        break;
+    case 0x1FFE:
+    case 0x07FE:
+        /* month */
+       tmp = from_bcd(val & 0x1F);
+       if (tmp >= 1 && tmp <= 12) {
+           get_time(NVRAM, &tm);
+           tm.tm_mon = tmp - 1;
+           set_time(NVRAM, &tm);
+       }
+        break;
+    case 0x1FFF:
+    case 0x07FF:
+        /* year */
+       tmp = from_bcd(val);
+       if (tmp >= 0 && tmp <= 99) {
+           get_time(NVRAM, &tm);
+            if (NVRAM->model == 8) {
+                tm.tm_year = from_bcd(val) + 68; // Base year is 1968
+            } else {
+                tm.tm_year = from_bcd(val);
+            }
+           set_time(NVRAM, &tm);
+       }
+        break;
+    default:
+        /* Check lock registers state */
+        if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1))
+            break;
+        if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2))
+            break;
+    do_write:
+        if (addr < NVRAM->size) {
+            NVRAM->buffer[addr] = val & 0xFF;
+       }
+        break;
+    }
+}
+
+uint32_t m48t59_read (void *opaque, uint32_t addr)
+{
+    M48t59State *NVRAM = opaque;
+    struct tm tm;
+    uint32_t retval = 0xFF;
+
+    /* check for NVRAM access */
+    if ((NVRAM->model == 2 && addr < 0x078f) ||
+        (NVRAM->model == 8 && addr < 0x1ff8) ||
+        (NVRAM->model == 59 && addr < 0x1ff0)) {
+        goto do_read;
+    }
+
+    /* TOD access */
+    switch (addr) {
+    case 0x1FF0:
+        /* flags register */
+       goto do_read;
+    case 0x1FF1:
+        /* unused */
+       retval = 0;
+        break;
+    case 0x1FF2:
+        /* alarm seconds */
+       goto do_read;
+    case 0x1FF3:
+        /* alarm minutes */
+       goto do_read;
+    case 0x1FF4:
+        /* alarm hours */
+       goto do_read;
+    case 0x1FF5:
+        /* alarm date */
+       goto do_read;
+    case 0x1FF6:
+        /* interrupts */
+       goto do_read;
+    case 0x1FF7:
+       /* A read resets the watchdog */
+       set_up_watchdog(NVRAM, NVRAM->buffer[0x1FF7]);
+       goto do_read;
+    case 0x1FF8:
+    case 0x07F8:
+        /* control */
+       goto do_read;
+    case 0x1FF9:
+    case 0x07F9:
+        /* seconds (BCD) */
+        get_time(NVRAM, &tm);
+        retval = (NVRAM->buffer[addr] & 0x80) | to_bcd(tm.tm_sec);
+        break;
+    case 0x1FFA:
+    case 0x07FA:
+        /* minutes (BCD) */
+        get_time(NVRAM, &tm);
+        retval = to_bcd(tm.tm_min);
+        break;
+    case 0x1FFB:
+    case 0x07FB:
+        /* hours (BCD) */
+        get_time(NVRAM, &tm);
+        retval = to_bcd(tm.tm_hour);
+        break;
+    case 0x1FFC:
+    case 0x07FC:
+        /* day of the week / century */
+        get_time(NVRAM, &tm);
+        retval = NVRAM->buffer[addr] | tm.tm_wday;
+        break;
+    case 0x1FFD:
+    case 0x07FD:
+        /* date */
+        get_time(NVRAM, &tm);
+        retval = to_bcd(tm.tm_mday);
+        break;
+    case 0x1FFE:
+    case 0x07FE:
+        /* month */
+        get_time(NVRAM, &tm);
+        retval = to_bcd(tm.tm_mon + 1);
+        break;
+    case 0x1FFF:
+    case 0x07FF:
+        /* year */
+        get_time(NVRAM, &tm);
+        if (NVRAM->model == 8) {
+            retval = to_bcd(tm.tm_year - 68); // Base year is 1968
+        } else {
+            retval = to_bcd(tm.tm_year);
+        }
+        break;
+    default:
+        /* Check lock registers state */
+        if (addr >= 0x20 && addr <= 0x2F && (NVRAM->lock & 1))
+            break;
+        if (addr >= 0x30 && addr <= 0x3F && (NVRAM->lock & 2))
+            break;
+    do_read:
+        if (addr < NVRAM->size) {
+            retval = NVRAM->buffer[addr];
+       }
+        break;
+    }
+    if (addr > 0x1FF9 && addr < 0x2000)
+       NVRAM_PRINTF("%s: 0x%08x <= 0x%08x\n", __func__, addr, retval);
+
+    return retval;
+}
+
+void m48t59_toggle_lock (void *opaque, int lock)
+{
+    M48t59State *NVRAM = opaque;
+
+    NVRAM->lock ^= 1 << lock;
+}
+
+/* IO access to NVRAM */
+static void NVRAM_writeb(void *opaque, hwaddr addr, uint64_t val,
+                         unsigned size)
+{
+    M48t59State *NVRAM = opaque;
+
+    NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val);
+    switch (addr) {
+    case 0:
+        NVRAM->addr &= ~0x00FF;
+        NVRAM->addr |= val;
+        break;
+    case 1:
+        NVRAM->addr &= ~0xFF00;
+        NVRAM->addr |= val << 8;
+        break;
+    case 3:
+        m48t59_write(NVRAM, NVRAM->addr, val);
+        NVRAM->addr = 0x0000;
+        break;
+    default:
+        break;
+    }
+}
+
+static uint64_t NVRAM_readb(void *opaque, hwaddr addr, unsigned size)
+{
+    M48t59State *NVRAM = opaque;
+    uint32_t retval;
+
+    switch (addr) {
+    case 3:
+        retval = m48t59_read(NVRAM, NVRAM->addr);
+        break;
+    default:
+        retval = -1;
+        break;
+    }
+    NVRAM_PRINTF("%s: 0x%08x <= 0x%08x\n", __func__, addr, retval);
+
+    return retval;
+}
+
+static void nvram_writeb (void *opaque, hwaddr addr, uint32_t value)
+{
+    M48t59State *NVRAM = opaque;
+
+    m48t59_write(NVRAM, addr, value & 0xff);
+}
+
+static void nvram_writew (void *opaque, hwaddr addr, uint32_t value)
+{
+    M48t59State *NVRAM = opaque;
+
+    m48t59_write(NVRAM, addr, (value >> 8) & 0xff);
+    m48t59_write(NVRAM, addr + 1, value & 0xff);
+}
+
+static void nvram_writel (void *opaque, hwaddr addr, uint32_t value)
+{
+    M48t59State *NVRAM = opaque;
+
+    m48t59_write(NVRAM, addr, (value >> 24) & 0xff);
+    m48t59_write(NVRAM, addr + 1, (value >> 16) & 0xff);
+    m48t59_write(NVRAM, addr + 2, (value >> 8) & 0xff);
+    m48t59_write(NVRAM, addr + 3, value & 0xff);
+}
+
+static uint32_t nvram_readb (void *opaque, hwaddr addr)
+{
+    M48t59State *NVRAM = opaque;
+    uint32_t retval;
+
+    retval = m48t59_read(NVRAM, addr);
+    return retval;
+}
+
+static uint32_t nvram_readw (void *opaque, hwaddr addr)
+{
+    M48t59State *NVRAM = opaque;
+    uint32_t retval;
+
+    retval = m48t59_read(NVRAM, addr) << 8;
+    retval |= m48t59_read(NVRAM, addr + 1);
+    return retval;
+}
+
+static uint32_t nvram_readl (void *opaque, hwaddr addr)
+{
+    M48t59State *NVRAM = opaque;
+    uint32_t retval;
+
+    retval = m48t59_read(NVRAM, addr) << 24;
+    retval |= m48t59_read(NVRAM, addr + 1) << 16;
+    retval |= m48t59_read(NVRAM, addr + 2) << 8;
+    retval |= m48t59_read(NVRAM, addr + 3);
+    return retval;
+}
+
+static const MemoryRegionOps nvram_ops = {
+    .old_mmio = {
+        .read = { nvram_readb, nvram_readw, nvram_readl, },
+        .write = { nvram_writeb, nvram_writew, nvram_writel, },
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_m48t59 = {
+    .name = "m48t59",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT8(lock, M48t59State),
+        VMSTATE_UINT16(addr, M48t59State),
+        VMSTATE_VBUFFER_UINT32(buffer, M48t59State, 0, NULL, 0, size),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void m48t59_reset_common(M48t59State *NVRAM)
+{
+    NVRAM->addr = 0;
+    NVRAM->lock = 0;
+    if (NVRAM->alrm_timer != NULL)
+        qemu_del_timer(NVRAM->alrm_timer);
+
+    if (NVRAM->wd_timer != NULL)
+        qemu_del_timer(NVRAM->wd_timer);
+}
+
+static void m48t59_reset_isa(DeviceState *d)
+{
+    M48t59ISAState *isa = container_of(d, M48t59ISAState, busdev.qdev);
+    M48t59State *NVRAM = &isa->state;
+
+    m48t59_reset_common(NVRAM);
+}
+
+static void m48t59_reset_sysbus(DeviceState *d)
+{
+    M48t59SysBusState *sys = container_of(d, M48t59SysBusState, busdev.qdev);
+    M48t59State *NVRAM = &sys->state;
+
+    m48t59_reset_common(NVRAM);
+}
+
+static const MemoryRegionOps m48t59_io_ops = {
+    .read = NVRAM_readb,
+    .write = NVRAM_writeb,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+/* Initialisation routine */
+M48t59State *m48t59_init(qemu_irq IRQ, hwaddr mem_base,
+                         uint32_t io_base, uint16_t size, int model)
+{
+    DeviceState *dev;
+    SysBusDevice *s;
+    M48t59SysBusState *d;
+    M48t59State *state;
+
+    dev = qdev_create(NULL, "m48t59");
+    qdev_prop_set_uint32(dev, "model", model);
+    qdev_prop_set_uint32(dev, "size", size);
+    qdev_prop_set_uint32(dev, "io_base", io_base);
+    qdev_init_nofail(dev);
+    s = SYS_BUS_DEVICE(dev);
+    d = FROM_SYSBUS(M48t59SysBusState, s);
+    state = &d->state;
+    sysbus_connect_irq(s, 0, IRQ);
+    memory_region_init_io(&d->io, &m48t59_io_ops, state, "m48t59", 4);
+    if (io_base != 0) {
+        memory_region_add_subregion(get_system_io(), io_base, &d->io);
+    }
+    if (mem_base != 0) {
+        sysbus_mmio_map(s, 0, mem_base);
+    }
+
+    return state;
+}
+
+M48t59State *m48t59_init_isa(ISABus *bus, uint32_t io_base, uint16_t size,
+                             int model)
+{
+    M48t59ISAState *d;
+    ISADevice *dev;
+    M48t59State *s;
+
+    dev = isa_create(bus, "m48t59_isa");
+    qdev_prop_set_uint32(&dev->qdev, "model", model);
+    qdev_prop_set_uint32(&dev->qdev, "size", size);
+    qdev_prop_set_uint32(&dev->qdev, "io_base", io_base);
+    qdev_init_nofail(&dev->qdev);
+    d = DO_UPCAST(M48t59ISAState, busdev, dev);
+    s = &d->state;
+
+    memory_region_init_io(&d->io, &m48t59_io_ops, s, "m48t59", 4);
+    if (io_base != 0) {
+        isa_register_ioport(dev, &d->io, io_base);
+    }
+
+    return s;
+}
+
+static void m48t59_init_common(M48t59State *s)
+{
+    s->buffer = g_malloc0(s->size);
+    if (s->model == 59) {
+        s->alrm_timer = qemu_new_timer_ns(rtc_clock, &alarm_cb, s);
+        s->wd_timer = qemu_new_timer_ns(vm_clock, &watchdog_cb, s);
+    }
+    qemu_get_timedate(&s->alarm, 0);
+
+    vmstate_register(NULL, -1, &vmstate_m48t59, s);
+}
+
+static int m48t59_init_isa1(ISADevice *dev)
+{
+    M48t59ISAState *d = DO_UPCAST(M48t59ISAState, busdev, dev);
+    M48t59State *s = &d->state;
+
+    isa_init_irq(dev, &s->IRQ, 8);
+    m48t59_init_common(s);
+
+    return 0;
+}
+
+static int m48t59_init1(SysBusDevice *dev)
+{
+    M48t59SysBusState *d = FROM_SYSBUS(M48t59SysBusState, dev);
+    M48t59State *s = &d->state;
+
+    sysbus_init_irq(dev, &s->IRQ);
+
+    memory_region_init_io(&s->iomem, &nvram_ops, s, "m48t59.nvram", s->size);
+    sysbus_init_mmio(dev, &s->iomem);
+    m48t59_init_common(s);
+
+    return 0;
+}
+
+static Property m48t59_isa_properties[] = {
+    DEFINE_PROP_UINT32("size",    M48t59ISAState, state.size,    -1),
+    DEFINE_PROP_UINT32("model",   M48t59ISAState, state.model,   -1),
+    DEFINE_PROP_HEX32( "io_base", M48t59ISAState, state.io_base,  0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void m48t59_init_class_isa1(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+    ic->init = m48t59_init_isa1;
+    dc->no_user = 1;
+    dc->reset = m48t59_reset_isa;
+    dc->props = m48t59_isa_properties;
+}
+
+static const TypeInfo m48t59_isa_info = {
+    .name          = "m48t59_isa",
+    .parent        = TYPE_ISA_DEVICE,
+    .instance_size = sizeof(M48t59ISAState),
+    .class_init    = m48t59_init_class_isa1,
+};
+
+static Property m48t59_properties[] = {
+    DEFINE_PROP_UINT32("size",    M48t59SysBusState, state.size,    -1),
+    DEFINE_PROP_UINT32("model",   M48t59SysBusState, state.model,   -1),
+    DEFINE_PROP_HEX32( "io_base", M48t59SysBusState, state.io_base,  0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void m48t59_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = m48t59_init1;
+    dc->reset = m48t59_reset_sysbus;
+    dc->props = m48t59_properties;
+}
+
+static const TypeInfo m48t59_info = {
+    .name          = "m48t59",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(M48t59SysBusState),
+    .class_init    = m48t59_class_init,
+};
+
+static void m48t59_register_types(void)
+{
+    type_register_static(&m48t59_info);
+    type_register_static(&m48t59_isa_info);
+}
+
+type_init(m48t59_register_types)
diff --git a/hw/timer/pl031.c b/hw/timer/pl031.c
new file mode 100644 (file)
index 0000000..764940b
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * ARM AMBA PrimeCell PL031 RTC
+ *
+ * Copyright (c) 2007 CodeSourcery
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "sysemu/sysemu.h"
+
+//#define DEBUG_PL031
+
+#ifdef DEBUG_PL031
+#define DPRINTF(fmt, ...) \
+do { printf("pl031: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+#define RTC_DR      0x00    /* Data read register */
+#define RTC_MR      0x04    /* Match register */
+#define RTC_LR      0x08    /* Data load register */
+#define RTC_CR      0x0c    /* Control register */
+#define RTC_IMSC    0x10    /* Interrupt mask and set register */
+#define RTC_RIS     0x14    /* Raw interrupt status register */
+#define RTC_MIS     0x18    /* Masked interrupt status register */
+#define RTC_ICR     0x1c    /* Interrupt clear register */
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    QEMUTimer *timer;
+    qemu_irq irq;
+
+    /* Needed to preserve the tick_count across migration, even if the
+     * absolute value of the rtc_clock is different on the source and
+     * destination.
+     */
+    uint32_t tick_offset_vmstate;
+    uint32_t tick_offset;
+
+    uint32_t mr;
+    uint32_t lr;
+    uint32_t cr;
+    uint32_t im;
+    uint32_t is;
+} pl031_state;
+
+static const unsigned char pl031_id[] = {
+    0x31, 0x10, 0x14, 0x00,         /* Device ID        */
+    0x0d, 0xf0, 0x05, 0xb1          /* Cell ID      */
+};
+
+static void pl031_update(pl031_state *s)
+{
+    qemu_set_irq(s->irq, s->is & s->im);
+}
+
+static void pl031_interrupt(void * opaque)
+{
+    pl031_state *s = (pl031_state *)opaque;
+
+    s->is = 1;
+    DPRINTF("Alarm raised\n");
+    pl031_update(s);
+}
+
+static uint32_t pl031_get_count(pl031_state *s)
+{
+    int64_t now = qemu_get_clock_ns(rtc_clock);
+    return s->tick_offset + now / get_ticks_per_sec();
+}
+
+static void pl031_set_alarm(pl031_state *s)
+{
+    uint32_t ticks;
+
+    /* The timer wraps around.  This subtraction also wraps in the same way,
+       and gives correct results when alarm < now_ticks.  */
+    ticks = s->mr - pl031_get_count(s);
+    DPRINTF("Alarm set in %ud ticks\n", ticks);
+    if (ticks == 0) {
+        qemu_del_timer(s->timer);
+        pl031_interrupt(s);
+    } else {
+        int64_t now = qemu_get_clock_ns(rtc_clock);
+        qemu_mod_timer(s->timer, now + (int64_t)ticks * get_ticks_per_sec());
+    }
+}
+
+static uint64_t pl031_read(void *opaque, hwaddr offset,
+                           unsigned size)
+{
+    pl031_state *s = (pl031_state *)opaque;
+
+    if (offset >= 0xfe0  &&  offset < 0x1000)
+        return pl031_id[(offset - 0xfe0) >> 2];
+
+    switch (offset) {
+    case RTC_DR:
+        return pl031_get_count(s);
+    case RTC_MR:
+        return s->mr;
+    case RTC_IMSC:
+        return s->im;
+    case RTC_RIS:
+        return s->is;
+    case RTC_LR:
+        return s->lr;
+    case RTC_CR:
+        /* RTC is permanently enabled.  */
+        return 1;
+    case RTC_MIS:
+        return s->is & s->im;
+    case RTC_ICR:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl031: read of write-only register at offset 0x%x\n",
+                      (int)offset);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl031_read: Bad offset 0x%x\n", (int)offset);
+        break;
+    }
+
+    return 0;
+}
+
+static void pl031_write(void * opaque, hwaddr offset,
+                        uint64_t value, unsigned size)
+{
+    pl031_state *s = (pl031_state *)opaque;
+
+
+    switch (offset) {
+    case RTC_LR:
+        s->tick_offset += value - pl031_get_count(s);
+        pl031_set_alarm(s);
+        break;
+    case RTC_MR:
+        s->mr = value;
+        pl031_set_alarm(s);
+        break;
+    case RTC_IMSC:
+        s->im = value & 1;
+        DPRINTF("Interrupt mask %d\n", s->im);
+        pl031_update(s);
+        break;
+    case RTC_ICR:
+        /* The PL031 documentation (DDI0224B) states that the interrupt is
+           cleared when bit 0 of the written value is set.  However the
+           arm926e documentation (DDI0287B) states that the interrupt is
+           cleared when any value is written.  */
+        DPRINTF("Interrupt cleared");
+        s->is = 0;
+        pl031_update(s);
+        break;
+    case RTC_CR:
+        /* Written value is ignored.  */
+        break;
+
+    case RTC_DR:
+    case RTC_MIS:
+    case RTC_RIS:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl031: write to read-only register at offset 0x%x\n",
+                      (int)offset);
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "pl031_write: Bad offset 0x%x\n", (int)offset);
+        break;
+    }
+}
+
+static const MemoryRegionOps pl031_ops = {
+    .read = pl031_read,
+    .write = pl031_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int pl031_init(SysBusDevice *dev)
+{
+    pl031_state *s = FROM_SYSBUS(pl031_state, dev);
+    struct tm tm;
+
+    memory_region_init_io(&s->iomem, &pl031_ops, s, "pl031", 0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    sysbus_init_irq(dev, &s->irq);
+    qemu_get_timedate(&tm, 0);
+    s->tick_offset = mktimegm(&tm) - qemu_get_clock_ns(rtc_clock) / get_ticks_per_sec();
+
+    s->timer = qemu_new_timer_ns(rtc_clock, pl031_interrupt, s);
+    return 0;
+}
+
+static void pl031_pre_save(void *opaque)
+{
+    pl031_state *s = opaque;
+
+    /* tick_offset is base_time - rtc_clock base time.  Instead, we want to
+     * store the base time relative to the vm_clock for backwards-compatibility.  */
+    int64_t delta = qemu_get_clock_ns(rtc_clock) - qemu_get_clock_ns(vm_clock);
+    s->tick_offset_vmstate = s->tick_offset + delta / get_ticks_per_sec();
+}
+
+static int pl031_post_load(void *opaque, int version_id)
+{
+    pl031_state *s = opaque;
+
+    int64_t delta = qemu_get_clock_ns(rtc_clock) - qemu_get_clock_ns(vm_clock);
+    s->tick_offset = s->tick_offset_vmstate - delta / get_ticks_per_sec();
+    pl031_set_alarm(s);
+    return 0;
+}
+
+static const VMStateDescription vmstate_pl031 = {
+    .name = "pl031",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .pre_save = pl031_pre_save,
+    .post_load = pl031_post_load,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(tick_offset_vmstate, pl031_state),
+        VMSTATE_UINT32(mr, pl031_state),
+        VMSTATE_UINT32(lr, pl031_state),
+        VMSTATE_UINT32(cr, pl031_state),
+        VMSTATE_UINT32(im, pl031_state),
+        VMSTATE_UINT32(is, pl031_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void pl031_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = pl031_init;
+    dc->no_user = 1;
+    dc->vmsd = &vmstate_pl031;
+}
+
+static const TypeInfo pl031_info = {
+    .name          = "pl031",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(pl031_state),
+    .class_init    = pl031_class_init,
+};
+
+static void pl031_register_types(void)
+{
+    type_register_static(&pl031_info);
+}
+
+type_init(pl031_register_types)
diff --git a/hw/timer/puv3_ost.c b/hw/timer/puv3_ost.c
new file mode 100644 (file)
index 0000000..0c3d827
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * OSTimer device simulation in PKUnity SoC
+ *
+ * Copyright (C) 2010-2012 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation, or any later version.
+ * See the COPYING file in the top-level directory.
+ */
+#include "hw/sysbus.h"
+#include "hw/ptimer.h"
+
+#undef DEBUG_PUV3
+#include "hw/unicore32/puv3.h"
+
+/* puv3 ostimer implementation. */
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    QEMUBH *bh;
+    qemu_irq irq;
+    ptimer_state *ptimer;
+
+    uint32_t reg_OSMR0;
+    uint32_t reg_OSCR;
+    uint32_t reg_OSSR;
+    uint32_t reg_OIER;
+} PUV3OSTState;
+
+static uint64_t puv3_ost_read(void *opaque, hwaddr offset,
+        unsigned size)
+{
+    PUV3OSTState *s = opaque;
+    uint32_t ret = 0;
+
+    switch (offset) {
+    case 0x10: /* Counter Register */
+        ret = s->reg_OSMR0 - (uint32_t)ptimer_get_count(s->ptimer);
+        break;
+    case 0x14: /* Status Register */
+        ret = s->reg_OSSR;
+        break;
+    case 0x1c: /* Interrupt Enable Register */
+        ret = s->reg_OIER;
+        break;
+    default:
+        DPRINTF("Bad offset %x\n", (int)offset);
+    }
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, ret);
+    return ret;
+}
+
+static void puv3_ost_write(void *opaque, hwaddr offset,
+        uint64_t value, unsigned size)
+{
+    PUV3OSTState *s = opaque;
+
+    DPRINTF("offset 0x%x, value 0x%x\n", offset, value);
+    switch (offset) {
+    case 0x00: /* Match Register 0 */
+        s->reg_OSMR0 = value;
+        if (s->reg_OSMR0 > s->reg_OSCR) {
+            ptimer_set_count(s->ptimer, s->reg_OSMR0 - s->reg_OSCR);
+        } else {
+            ptimer_set_count(s->ptimer, s->reg_OSMR0 +
+                    (0xffffffff - s->reg_OSCR));
+        }
+        ptimer_run(s->ptimer, 2);
+        break;
+    case 0x14: /* Status Register */
+        assert(value == 0);
+        if (s->reg_OSSR) {
+            s->reg_OSSR = value;
+            qemu_irq_lower(s->irq);
+        }
+        break;
+    case 0x1c: /* Interrupt Enable Register */
+        s->reg_OIER = value;
+        break;
+    default:
+        DPRINTF("Bad offset %x\n", (int)offset);
+    }
+}
+
+static const MemoryRegionOps puv3_ost_ops = {
+    .read = puv3_ost_read,
+    .write = puv3_ost_write,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void puv3_ost_tick(void *opaque)
+{
+    PUV3OSTState *s = opaque;
+
+    DPRINTF("ost hit when ptimer counter from 0x%x to 0x%x!\n",
+            s->reg_OSCR, s->reg_OSMR0);
+
+    s->reg_OSCR = s->reg_OSMR0;
+    if (s->reg_OIER) {
+        s->reg_OSSR = 1;
+        qemu_irq_raise(s->irq);
+    }
+}
+
+static int puv3_ost_init(SysBusDevice *dev)
+{
+    PUV3OSTState *s = FROM_SYSBUS(PUV3OSTState, dev);
+
+    s->reg_OIER = 0;
+    s->reg_OSSR = 0;
+    s->reg_OSMR0 = 0;
+    s->reg_OSCR = 0;
+
+    sysbus_init_irq(dev, &s->irq);
+
+    s->bh = qemu_bh_new(puv3_ost_tick, s);
+    s->ptimer = ptimer_init(s->bh);
+    ptimer_set_freq(s->ptimer, 50 * 1000 * 1000);
+
+    memory_region_init_io(&s->iomem, &puv3_ost_ops, s, "puv3_ost",
+            PUV3_REGS_OFFSET);
+    sysbus_init_mmio(dev, &s->iomem);
+
+    return 0;
+}
+
+static void puv3_ost_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+    sdc->init = puv3_ost_init;
+}
+
+static const TypeInfo puv3_ost_info = {
+    .name = "puv3_ost",
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(PUV3OSTState),
+    .class_init = puv3_ost_class_init,
+};
+
+static void puv3_ost_register_type(void)
+{
+    type_register_static(&puv3_ost_info);
+}
+
+type_init(puv3_ost_register_type)
diff --git a/hw/timer/twl92230.c b/hw/timer/twl92230.c
new file mode 100644 (file)
index 0000000..b730d85
--- /dev/null
@@ -0,0 +1,882 @@
+/*
+ * TI TWL92230C energy-management companion device for the OMAP24xx.
+ * Aka. Menelaus (N4200 MENELAUS1_V2.2)
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "qemu/timer.h"
+#include "hw/i2c/i2c.h"
+#include "sysemu/sysemu.h"
+#include "ui/console.h"
+
+#define VERBOSE 1
+
+typedef struct {
+    I2CSlave i2c;
+
+    int firstbyte;
+    uint8_t reg;
+
+    uint8_t vcore[5];
+    uint8_t dcdc[3];
+    uint8_t ldo[8];
+    uint8_t sleep[2];
+    uint8_t osc;
+    uint8_t detect;
+    uint16_t mask;
+    uint16_t status;
+    uint8_t dir;
+    uint8_t inputs;
+    uint8_t outputs;
+    uint8_t bbsms;
+    uint8_t pull[4];
+    uint8_t mmc_ctrl[3];
+    uint8_t mmc_debounce;
+    struct {
+        uint8_t ctrl;
+        uint16_t comp;
+        QEMUTimer *hz_tm;
+        int64_t next;
+        struct tm tm;
+        struct tm new;
+        struct tm alm;
+        int sec_offset;
+        int alm_sec;
+        int next_comp;
+    } rtc;
+    uint16_t rtc_next_vmstate;
+    qemu_irq out[4];
+    uint8_t pwrbtn_state;
+} MenelausState;
+
+static inline void menelaus_update(MenelausState *s)
+{
+    qemu_set_irq(s->out[3], s->status & ~s->mask);
+}
+
+static inline void menelaus_rtc_start(MenelausState *s)
+{
+    s->rtc.next += qemu_get_clock_ms(rtc_clock);
+    qemu_mod_timer(s->rtc.hz_tm, s->rtc.next);
+}
+
+static inline void menelaus_rtc_stop(MenelausState *s)
+{
+    qemu_del_timer(s->rtc.hz_tm);
+    s->rtc.next -= qemu_get_clock_ms(rtc_clock);
+    if (s->rtc.next < 1)
+        s->rtc.next = 1;
+}
+
+static void menelaus_rtc_update(MenelausState *s)
+{
+    qemu_get_timedate(&s->rtc.tm, s->rtc.sec_offset);
+}
+
+static void menelaus_alm_update(MenelausState *s)
+{
+    if ((s->rtc.ctrl & 3) == 3)
+        s->rtc.alm_sec = qemu_timedate_diff(&s->rtc.alm) - s->rtc.sec_offset;
+}
+
+static void menelaus_rtc_hz(void *opaque)
+{
+    MenelausState *s = (MenelausState *) opaque;
+
+    s->rtc.next_comp --;
+    s->rtc.alm_sec --;
+    s->rtc.next += 1000;
+    qemu_mod_timer(s->rtc.hz_tm, s->rtc.next);
+    if ((s->rtc.ctrl >> 3) & 3) {                              /* EVERY */
+        menelaus_rtc_update(s);
+        if (((s->rtc.ctrl >> 3) & 3) == 1 && !s->rtc.tm.tm_sec)
+            s->status |= 1 << 8;                               /* RTCTMR */
+        else if (((s->rtc.ctrl >> 3) & 3) == 2 && !s->rtc.tm.tm_min)
+            s->status |= 1 << 8;                               /* RTCTMR */
+        else if (!s->rtc.tm.tm_hour)
+            s->status |= 1 << 8;                               /* RTCTMR */
+    } else
+        s->status |= 1 << 8;                                   /* RTCTMR */
+    if ((s->rtc.ctrl >> 1) & 1) {                              /* RTC_AL_EN */
+        if (s->rtc.alm_sec == 0)
+            s->status |= 1 << 9;                               /* RTCALM */
+        /* TODO: wake-up */
+    }
+    if (s->rtc.next_comp <= 0) {
+        s->rtc.next -= muldiv64((int16_t) s->rtc.comp, 1000, 0x8000);
+        s->rtc.next_comp = 3600;
+    }
+    menelaus_update(s);
+}
+
+static void menelaus_reset(I2CSlave *i2c)
+{
+    MenelausState *s = (MenelausState *) i2c;
+    s->reg = 0x00;
+
+    s->vcore[0] = 0x0c;        /* XXX: X-loader needs 0x8c? check!  */
+    s->vcore[1] = 0x05;
+    s->vcore[2] = 0x02;
+    s->vcore[3] = 0x0c;
+    s->vcore[4] = 0x03;
+    s->dcdc[0] = 0x33; /* Depends on wiring */
+    s->dcdc[1] = 0x03;
+    s->dcdc[2] = 0x00;
+    s->ldo[0] = 0x95;
+    s->ldo[1] = 0x7e;
+    s->ldo[2] = 0x00;
+    s->ldo[3] = 0x00;  /* Depends on wiring */
+    s->ldo[4] = 0x03;  /* Depends on wiring */
+    s->ldo[5] = 0x00;
+    s->ldo[6] = 0x00;
+    s->ldo[7] = 0x00;
+    s->sleep[0] = 0x00;
+    s->sleep[1] = 0x00;
+    s->osc = 0x01;
+    s->detect = 0x09;
+    s->mask = 0x0fff;
+    s->status = 0;
+    s->dir = 0x07;
+    s->outputs = 0x00;
+    s->bbsms = 0x00;
+    s->pull[0] = 0x00;
+    s->pull[1] = 0x00;
+    s->pull[2] = 0x00;
+    s->pull[3] = 0x00;
+    s->mmc_ctrl[0] = 0x03;
+    s->mmc_ctrl[1] = 0xc0;
+    s->mmc_ctrl[2] = 0x00;
+    s->mmc_debounce = 0x05;
+
+    if (s->rtc.ctrl & 1)
+        menelaus_rtc_stop(s);
+    s->rtc.ctrl = 0x00;
+    s->rtc.comp = 0x0000;
+    s->rtc.next = 1000;
+    s->rtc.sec_offset = 0;
+    s->rtc.next_comp = 1800;
+    s->rtc.alm_sec = 1800;
+    s->rtc.alm.tm_sec = 0x00;
+    s->rtc.alm.tm_min = 0x00;
+    s->rtc.alm.tm_hour = 0x00;
+    s->rtc.alm.tm_mday = 0x01;
+    s->rtc.alm.tm_mon = 0x00;
+    s->rtc.alm.tm_year = 2004;
+    menelaus_update(s);
+}
+
+static void menelaus_gpio_set(void *opaque, int line, int level)
+{
+    MenelausState *s = (MenelausState *) opaque;
+
+    if (line < 3) {
+        /* No interrupt generated */
+        s->inputs &= ~(1 << line);
+        s->inputs |= level << line;
+        return;
+    }
+
+    if (!s->pwrbtn_state && level) {
+        s->status |= 1 << 11;                                  /* PSHBTN */
+        menelaus_update(s);
+    }
+    s->pwrbtn_state = level;
+}
+
+#define MENELAUS_REV           0x01
+#define MENELAUS_VCORE_CTRL1   0x02
+#define MENELAUS_VCORE_CTRL2   0x03
+#define MENELAUS_VCORE_CTRL3   0x04
+#define MENELAUS_VCORE_CTRL4   0x05
+#define MENELAUS_VCORE_CTRL5   0x06
+#define MENELAUS_DCDC_CTRL1    0x07
+#define MENELAUS_DCDC_CTRL2    0x08
+#define MENELAUS_DCDC_CTRL3    0x09
+#define MENELAUS_LDO_CTRL1     0x0a
+#define MENELAUS_LDO_CTRL2     0x0b
+#define MENELAUS_LDO_CTRL3     0x0c
+#define MENELAUS_LDO_CTRL4     0x0d
+#define MENELAUS_LDO_CTRL5     0x0e
+#define MENELAUS_LDO_CTRL6     0x0f
+#define MENELAUS_LDO_CTRL7     0x10
+#define MENELAUS_LDO_CTRL8     0x11
+#define MENELAUS_SLEEP_CTRL1   0x12
+#define MENELAUS_SLEEP_CTRL2   0x13
+#define MENELAUS_DEVICE_OFF    0x14
+#define MENELAUS_OSC_CTRL      0x15
+#define MENELAUS_DETECT_CTRL   0x16
+#define MENELAUS_INT_MASK1     0x17
+#define MENELAUS_INT_MASK2     0x18
+#define MENELAUS_INT_STATUS1   0x19
+#define MENELAUS_INT_STATUS2   0x1a
+#define MENELAUS_INT_ACK1      0x1b
+#define MENELAUS_INT_ACK2      0x1c
+#define MENELAUS_GPIO_CTRL     0x1d
+#define MENELAUS_GPIO_IN       0x1e
+#define MENELAUS_GPIO_OUT      0x1f
+#define MENELAUS_BBSMS         0x20
+#define MENELAUS_RTC_CTRL      0x21
+#define MENELAUS_RTC_UPDATE    0x22
+#define MENELAUS_RTC_SEC       0x23
+#define MENELAUS_RTC_MIN       0x24
+#define MENELAUS_RTC_HR                0x25
+#define MENELAUS_RTC_DAY       0x26
+#define MENELAUS_RTC_MON       0x27
+#define MENELAUS_RTC_YR                0x28
+#define MENELAUS_RTC_WKDAY     0x29
+#define MENELAUS_RTC_AL_SEC    0x2a
+#define MENELAUS_RTC_AL_MIN    0x2b
+#define MENELAUS_RTC_AL_HR     0x2c
+#define MENELAUS_RTC_AL_DAY    0x2d
+#define MENELAUS_RTC_AL_MON    0x2e
+#define MENELAUS_RTC_AL_YR     0x2f
+#define MENELAUS_RTC_COMP_MSB  0x30
+#define MENELAUS_RTC_COMP_LSB  0x31
+#define MENELAUS_S1_PULL_EN    0x32
+#define MENELAUS_S1_PULL_DIR   0x33
+#define MENELAUS_S2_PULL_EN    0x34
+#define MENELAUS_S2_PULL_DIR   0x35
+#define MENELAUS_MCT_CTRL1     0x36
+#define MENELAUS_MCT_CTRL2     0x37
+#define MENELAUS_MCT_CTRL3     0x38
+#define MENELAUS_MCT_PIN_ST    0x39
+#define MENELAUS_DEBOUNCE1     0x3a
+
+static uint8_t menelaus_read(void *opaque, uint8_t addr)
+{
+    MenelausState *s = (MenelausState *) opaque;
+    int reg = 0;
+
+    switch (addr) {
+    case MENELAUS_REV:
+        return 0x22;
+
+    case MENELAUS_VCORE_CTRL5: reg ++;
+    case MENELAUS_VCORE_CTRL4: reg ++;
+    case MENELAUS_VCORE_CTRL3: reg ++;
+    case MENELAUS_VCORE_CTRL2: reg ++;
+    case MENELAUS_VCORE_CTRL1:
+        return s->vcore[reg];
+
+    case MENELAUS_DCDC_CTRL3: reg ++;
+    case MENELAUS_DCDC_CTRL2: reg ++;
+    case MENELAUS_DCDC_CTRL1:
+        return s->dcdc[reg];
+
+    case MENELAUS_LDO_CTRL8: reg ++;
+    case MENELAUS_LDO_CTRL7: reg ++;
+    case MENELAUS_LDO_CTRL6: reg ++;
+    case MENELAUS_LDO_CTRL5: reg ++;
+    case MENELAUS_LDO_CTRL4: reg ++;
+    case MENELAUS_LDO_CTRL3: reg ++;
+    case MENELAUS_LDO_CTRL2: reg ++;
+    case MENELAUS_LDO_CTRL1:
+        return s->ldo[reg];
+
+    case MENELAUS_SLEEP_CTRL2: reg ++;
+    case MENELAUS_SLEEP_CTRL1:
+        return s->sleep[reg];
+
+    case MENELAUS_DEVICE_OFF:
+        return 0;
+
+    case MENELAUS_OSC_CTRL:
+        return s->osc | (1 << 7);                      /* CLK32K_GOOD */
+
+    case MENELAUS_DETECT_CTRL:
+        return s->detect;
+
+    case MENELAUS_INT_MASK1:
+        return (s->mask >> 0) & 0xff;
+    case MENELAUS_INT_MASK2:
+        return (s->mask >> 8) & 0xff;
+
+    case MENELAUS_INT_STATUS1:
+        return (s->status >> 0) & 0xff;
+    case MENELAUS_INT_STATUS2:
+        return (s->status >> 8) & 0xff;
+
+    case MENELAUS_INT_ACK1:
+    case MENELAUS_INT_ACK2:
+        return 0;
+
+    case MENELAUS_GPIO_CTRL:
+        return s->dir;
+    case MENELAUS_GPIO_IN:
+        return s->inputs | (~s->dir & s->outputs);
+    case MENELAUS_GPIO_OUT:
+        return s->outputs;
+
+    case MENELAUS_BBSMS:
+        return s->bbsms;
+
+    case MENELAUS_RTC_CTRL:
+        return s->rtc.ctrl;
+    case MENELAUS_RTC_UPDATE:
+        return 0x00;
+    case MENELAUS_RTC_SEC:
+        menelaus_rtc_update(s);
+        return to_bcd(s->rtc.tm.tm_sec);
+    case MENELAUS_RTC_MIN:
+        menelaus_rtc_update(s);
+        return to_bcd(s->rtc.tm.tm_min);
+    case MENELAUS_RTC_HR:
+        menelaus_rtc_update(s);
+        if ((s->rtc.ctrl >> 2) & 1)                    /* MODE12_n24 */
+            return to_bcd((s->rtc.tm.tm_hour % 12) + 1) |
+                    (!!(s->rtc.tm.tm_hour >= 12) << 7);        /* PM_nAM */
+        else
+            return to_bcd(s->rtc.tm.tm_hour);
+    case MENELAUS_RTC_DAY:
+        menelaus_rtc_update(s);
+        return to_bcd(s->rtc.tm.tm_mday);
+    case MENELAUS_RTC_MON:
+        menelaus_rtc_update(s);
+        return to_bcd(s->rtc.tm.tm_mon + 1);
+    case MENELAUS_RTC_YR:
+        menelaus_rtc_update(s);
+        return to_bcd(s->rtc.tm.tm_year - 2000);
+    case MENELAUS_RTC_WKDAY:
+        menelaus_rtc_update(s);
+        return to_bcd(s->rtc.tm.tm_wday);
+    case MENELAUS_RTC_AL_SEC:
+        return to_bcd(s->rtc.alm.tm_sec);
+    case MENELAUS_RTC_AL_MIN:
+        return to_bcd(s->rtc.alm.tm_min);
+    case MENELAUS_RTC_AL_HR:
+        if ((s->rtc.ctrl >> 2) & 1)                    /* MODE12_n24 */
+            return to_bcd((s->rtc.alm.tm_hour % 12) + 1) |
+                    (!!(s->rtc.alm.tm_hour >= 12) << 7);/* AL_PM_nAM */
+        else
+            return to_bcd(s->rtc.alm.tm_hour);
+    case MENELAUS_RTC_AL_DAY:
+        return to_bcd(s->rtc.alm.tm_mday);
+    case MENELAUS_RTC_AL_MON:
+        return to_bcd(s->rtc.alm.tm_mon + 1);
+    case MENELAUS_RTC_AL_YR:
+        return to_bcd(s->rtc.alm.tm_year - 2000);
+    case MENELAUS_RTC_COMP_MSB:
+        return (s->rtc.comp >> 8) & 0xff;
+    case MENELAUS_RTC_COMP_LSB:
+        return (s->rtc.comp >> 0) & 0xff;
+
+    case MENELAUS_S1_PULL_EN:
+        return s->pull[0];
+    case MENELAUS_S1_PULL_DIR:
+        return s->pull[1];
+    case MENELAUS_S2_PULL_EN:
+        return s->pull[2];
+    case MENELAUS_S2_PULL_DIR:
+        return s->pull[3];
+
+    case MENELAUS_MCT_CTRL3: reg ++;
+    case MENELAUS_MCT_CTRL2: reg ++;
+    case MENELAUS_MCT_CTRL1:
+        return s->mmc_ctrl[reg];
+    case MENELAUS_MCT_PIN_ST:
+        /* TODO: return the real Card Detect */
+        return 0;
+    case MENELAUS_DEBOUNCE1:
+        return s->mmc_debounce;
+
+    default:
+#ifdef VERBOSE
+        printf("%s: unknown register %02x\n", __FUNCTION__, addr);
+#endif
+        break;
+    }
+    return 0;
+}
+
+static void menelaus_write(void *opaque, uint8_t addr, uint8_t value)
+{
+    MenelausState *s = (MenelausState *) opaque;
+    int line;
+    int reg = 0;
+    struct tm tm;
+
+    switch (addr) {
+    case MENELAUS_VCORE_CTRL1:
+        s->vcore[0] = (value & 0xe) | MIN(value & 0x1f, 0x12);
+        break;
+    case MENELAUS_VCORE_CTRL2:
+        s->vcore[1] = value;
+        break;
+    case MENELAUS_VCORE_CTRL3:
+        s->vcore[2] = MIN(value & 0x1f, 0x12);
+        break;
+    case MENELAUS_VCORE_CTRL4:
+        s->vcore[3] = MIN(value & 0x1f, 0x12);
+        break;
+    case MENELAUS_VCORE_CTRL5:
+        s->vcore[4] = value & 3;
+        /* XXX
+         * auto set to 3 on M_Active, nRESWARM
+         * auto set to 0 on M_WaitOn, M_Backup
+         */
+        break;
+
+    case MENELAUS_DCDC_CTRL1:
+        s->dcdc[0] = value & 0x3f;
+        break;
+    case MENELAUS_DCDC_CTRL2:
+        s->dcdc[1] = value & 0x07;
+        /* XXX
+         * auto set to 3 on M_Active, nRESWARM
+         * auto set to 0 on M_WaitOn, M_Backup
+         */
+        break;
+    case MENELAUS_DCDC_CTRL3:
+        s->dcdc[2] = value & 0x07;
+        break;
+
+    case MENELAUS_LDO_CTRL1:
+        s->ldo[0] = value;
+        break;
+    case MENELAUS_LDO_CTRL2:
+        s->ldo[1] = value & 0x7f;
+        /* XXX
+         * auto set to 0x7e on M_WaitOn, M_Backup
+         */
+        break;
+    case MENELAUS_LDO_CTRL3:
+        s->ldo[2] = value & 3;
+        /* XXX
+         * auto set to 3 on M_Active, nRESWARM
+         * auto set to 0 on M_WaitOn, M_Backup
+         */
+        break;
+    case MENELAUS_LDO_CTRL4:
+        s->ldo[3] = value & 3;
+        /* XXX
+         * auto set to 3 on M_Active, nRESWARM
+         * auto set to 0 on M_WaitOn, M_Backup
+         */
+        break;
+    case MENELAUS_LDO_CTRL5:
+        s->ldo[4] = value & 3;
+        /* XXX
+         * auto set to 3 on M_Active, nRESWARM
+         * auto set to 0 on M_WaitOn, M_Backup
+         */
+        break;
+    case MENELAUS_LDO_CTRL6:
+        s->ldo[5] = value & 3;
+        break;
+    case MENELAUS_LDO_CTRL7:
+        s->ldo[6] = value & 3;
+        break;
+    case MENELAUS_LDO_CTRL8:
+        s->ldo[7] = value & 3;
+        break;
+
+    case MENELAUS_SLEEP_CTRL2: reg ++;
+    case MENELAUS_SLEEP_CTRL1:
+        s->sleep[reg] = value;
+        break;
+
+    case MENELAUS_DEVICE_OFF:
+        if (value & 1)
+            menelaus_reset(&s->i2c);
+        break;
+
+    case MENELAUS_OSC_CTRL:
+        s->osc = value & 7;
+        break;
+
+    case MENELAUS_DETECT_CTRL:
+        s->detect = value & 0x7f;
+        break;
+
+    case MENELAUS_INT_MASK1:
+        s->mask &= 0xf00;
+        s->mask |= value << 0;
+        menelaus_update(s);
+        break;
+    case MENELAUS_INT_MASK2:
+        s->mask &= 0x0ff;
+        s->mask |= value << 8;
+        menelaus_update(s);
+        break;
+
+    case MENELAUS_INT_ACK1:
+        s->status &= ~(((uint16_t) value) << 0);
+        menelaus_update(s);
+        break;
+    case MENELAUS_INT_ACK2:
+        s->status &= ~(((uint16_t) value) << 8);
+        menelaus_update(s);
+        break;
+
+    case MENELAUS_GPIO_CTRL:
+        for (line = 0; line < 3; line ++) {
+            if (((s->dir ^ value) >> line) & 1) {
+                qemu_set_irq(s->out[line],
+                             ((s->outputs & ~s->dir) >> line) & 1);
+            }
+        }
+        s->dir = value & 0x67;
+        break;
+    case MENELAUS_GPIO_OUT:
+        for (line = 0; line < 3; line ++) {
+            if ((((s->outputs ^ value) & ~s->dir) >> line) & 1) {
+                qemu_set_irq(s->out[line], (s->outputs >> line) & 1);
+            }
+        }
+        s->outputs = value & 0x07;
+        break;
+
+    case MENELAUS_BBSMS:
+        s->bbsms = 0x0d;
+        break;
+
+    case MENELAUS_RTC_CTRL:
+        if ((s->rtc.ctrl ^ value) & 1) {                       /* RTC_EN */
+            if (value & 1)
+                menelaus_rtc_start(s);
+            else
+                menelaus_rtc_stop(s);
+        }
+        s->rtc.ctrl = value & 0x1f;
+        menelaus_alm_update(s);
+        break;
+    case MENELAUS_RTC_UPDATE:
+        menelaus_rtc_update(s);
+        memcpy(&tm, &s->rtc.tm, sizeof(tm));
+        switch (value & 0xf) {
+        case 0:
+            break;
+        case 1:
+            tm.tm_sec = s->rtc.new.tm_sec;
+            break;
+        case 2:
+            tm.tm_min = s->rtc.new.tm_min;
+            break;
+        case 3:
+            if (s->rtc.new.tm_hour > 23)
+                goto rtc_badness;
+            tm.tm_hour = s->rtc.new.tm_hour;
+            break;
+        case 4:
+            if (s->rtc.new.tm_mday < 1)
+                goto rtc_badness;
+            /* TODO check range */
+            tm.tm_mday = s->rtc.new.tm_mday;
+            break;
+        case 5:
+            if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11)
+                goto rtc_badness;
+            tm.tm_mon = s->rtc.new.tm_mon;
+            break;
+        case 6:
+            tm.tm_year = s->rtc.new.tm_year;
+            break;
+        case 7:
+            /* TODO set .tm_mday instead */
+            tm.tm_wday = s->rtc.new.tm_wday;
+            break;
+        case 8:
+            if (s->rtc.new.tm_hour > 23)
+                goto rtc_badness;
+            if (s->rtc.new.tm_mday < 1)
+                goto rtc_badness;
+            if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11)
+                goto rtc_badness;
+            tm.tm_sec = s->rtc.new.tm_sec;
+            tm.tm_min = s->rtc.new.tm_min;
+            tm.tm_hour = s->rtc.new.tm_hour;
+            tm.tm_mday = s->rtc.new.tm_mday;
+            tm.tm_mon = s->rtc.new.tm_mon;
+            tm.tm_year = s->rtc.new.tm_year;
+            break;
+        rtc_badness:
+        default:
+            fprintf(stderr, "%s: bad RTC_UPDATE value %02x\n",
+                            __FUNCTION__, value);
+            s->status |= 1 << 10;                              /* RTCERR */
+            menelaus_update(s);
+        }
+        s->rtc.sec_offset = qemu_timedate_diff(&tm);
+        break;
+    case MENELAUS_RTC_SEC:
+        s->rtc.tm.tm_sec = from_bcd(value & 0x7f);
+        break;
+    case MENELAUS_RTC_MIN:
+        s->rtc.tm.tm_min = from_bcd(value & 0x7f);
+        break;
+    case MENELAUS_RTC_HR:
+        s->rtc.tm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */
+                MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) :
+                from_bcd(value & 0x3f);
+        break;
+    case MENELAUS_RTC_DAY:
+        s->rtc.tm.tm_mday = from_bcd(value);
+        break;
+    case MENELAUS_RTC_MON:
+        s->rtc.tm.tm_mon = MAX(1, from_bcd(value)) - 1;
+        break;
+    case MENELAUS_RTC_YR:
+        s->rtc.tm.tm_year = 2000 + from_bcd(value);
+        break;
+    case MENELAUS_RTC_WKDAY:
+        s->rtc.tm.tm_mday = from_bcd(value);
+        break;
+    case MENELAUS_RTC_AL_SEC:
+        s->rtc.alm.tm_sec = from_bcd(value & 0x7f);
+        menelaus_alm_update(s);
+        break;
+    case MENELAUS_RTC_AL_MIN:
+        s->rtc.alm.tm_min = from_bcd(value & 0x7f);
+        menelaus_alm_update(s);
+        break;
+    case MENELAUS_RTC_AL_HR:
+        s->rtc.alm.tm_hour = (s->rtc.ctrl & (1 << 2)) ?        /* MODE12_n24 */
+                MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) :
+                from_bcd(value & 0x3f);
+        menelaus_alm_update(s);
+        break;
+    case MENELAUS_RTC_AL_DAY:
+        s->rtc.alm.tm_mday = from_bcd(value);
+        menelaus_alm_update(s);
+        break;
+    case MENELAUS_RTC_AL_MON:
+        s->rtc.alm.tm_mon = MAX(1, from_bcd(value)) - 1;
+        menelaus_alm_update(s);
+        break;
+    case MENELAUS_RTC_AL_YR:
+        s->rtc.alm.tm_year = 2000 + from_bcd(value);
+        menelaus_alm_update(s);
+        break;
+    case MENELAUS_RTC_COMP_MSB:
+        s->rtc.comp &= 0xff;
+        s->rtc.comp |= value << 8;
+        break;
+    case MENELAUS_RTC_COMP_LSB:
+        s->rtc.comp &= 0xff << 8;
+        s->rtc.comp |= value;
+        break;
+
+    case MENELAUS_S1_PULL_EN:
+        s->pull[0] = value;
+        break;
+    case MENELAUS_S1_PULL_DIR:
+        s->pull[1] = value & 0x1f;
+        break;
+    case MENELAUS_S2_PULL_EN:
+        s->pull[2] = value;
+        break;
+    case MENELAUS_S2_PULL_DIR:
+        s->pull[3] = value & 0x1f;
+        break;
+
+    case MENELAUS_MCT_CTRL1:
+        s->mmc_ctrl[0] = value & 0x7f;
+        break;
+    case MENELAUS_MCT_CTRL2:
+        s->mmc_ctrl[1] = value;
+        /* TODO update Card Detect interrupts */
+        break;
+    case MENELAUS_MCT_CTRL3:
+        s->mmc_ctrl[2] = value & 0xf;
+        break;
+    case MENELAUS_DEBOUNCE1:
+        s->mmc_debounce = value & 0x3f;
+        break;
+
+    default:
+#ifdef VERBOSE
+        printf("%s: unknown register %02x\n", __FUNCTION__, addr);
+#endif
+    }
+}
+
+static void menelaus_event(I2CSlave *i2c, enum i2c_event event)
+{
+    MenelausState *s = (MenelausState *) i2c;
+
+    if (event == I2C_START_SEND)
+        s->firstbyte = 1;
+}
+
+static int menelaus_tx(I2CSlave *i2c, uint8_t data)
+{
+    MenelausState *s = (MenelausState *) i2c;
+    /* Interpret register address byte */
+    if (s->firstbyte) {
+        s->reg = data;
+        s->firstbyte = 0;
+    } else
+        menelaus_write(s, s->reg ++, data);
+
+    return 0;
+}
+
+static int menelaus_rx(I2CSlave *i2c)
+{
+    MenelausState *s = (MenelausState *) i2c;
+
+    return menelaus_read(s, s->reg ++);
+}
+
+/* Save restore 32 bit int as uint16_t
+   This is a Big hack, but it is how the old state did it.
+   Or we broke compatibility in the state, or we can't use struct tm
+ */
+
+static int get_int32_as_uint16(QEMUFile *f, void *pv, size_t size)
+{
+    int *v = pv;
+    *v = qemu_get_be16(f);
+    return 0;
+}
+
+static void put_int32_as_uint16(QEMUFile *f, void *pv, size_t size)
+{
+    int *v = pv;
+    qemu_put_be16(f, *v);
+}
+
+static const VMStateInfo vmstate_hack_int32_as_uint16 = {
+    .name = "int32_as_uint16",
+    .get  = get_int32_as_uint16,
+    .put  = put_int32_as_uint16,
+};
+
+#define VMSTATE_UINT16_HACK(_f, _s)                                  \
+    VMSTATE_SINGLE(_f, _s, 0, vmstate_hack_int32_as_uint16, int32_t)
+
+
+static const VMStateDescription vmstate_menelaus_tm = {
+    .name = "menelaus_tm",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT16_HACK(tm_sec, struct tm),
+        VMSTATE_UINT16_HACK(tm_min, struct tm),
+        VMSTATE_UINT16_HACK(tm_hour, struct tm),
+        VMSTATE_UINT16_HACK(tm_mday, struct tm),
+        VMSTATE_UINT16_HACK(tm_min, struct tm),
+        VMSTATE_UINT16_HACK(tm_year, struct tm),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void menelaus_pre_save(void *opaque)
+{
+    MenelausState *s = opaque;
+    /* Should be <= 1000 */
+    s->rtc_next_vmstate =  s->rtc.next - qemu_get_clock_ms(rtc_clock);
+}
+
+static int menelaus_post_load(void *opaque, int version_id)
+{
+    MenelausState *s = opaque;
+
+    if (s->rtc.ctrl & 1)                                       /* RTC_EN */
+        menelaus_rtc_stop(s);
+
+    s->rtc.next = s->rtc_next_vmstate;
+
+    menelaus_alm_update(s);
+    menelaus_update(s);
+    if (s->rtc.ctrl & 1)                                       /* RTC_EN */
+        menelaus_rtc_start(s);
+    return 0;
+}
+
+static const VMStateDescription vmstate_menelaus = {
+    .name = "menelaus",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .minimum_version_id_old = 0,
+    .pre_save = menelaus_pre_save,
+    .post_load = menelaus_post_load,
+    .fields      = (VMStateField []) {
+        VMSTATE_INT32(firstbyte, MenelausState),
+        VMSTATE_UINT8(reg, MenelausState),
+        VMSTATE_UINT8_ARRAY(vcore, MenelausState, 5),
+        VMSTATE_UINT8_ARRAY(dcdc, MenelausState, 3),
+        VMSTATE_UINT8_ARRAY(ldo, MenelausState, 8),
+        VMSTATE_UINT8_ARRAY(sleep, MenelausState, 2),
+        VMSTATE_UINT8(osc, MenelausState),
+        VMSTATE_UINT8(detect, MenelausState),
+        VMSTATE_UINT16(mask, MenelausState),
+        VMSTATE_UINT16(status, MenelausState),
+        VMSTATE_UINT8(dir, MenelausState),
+        VMSTATE_UINT8(inputs, MenelausState),
+        VMSTATE_UINT8(outputs, MenelausState),
+        VMSTATE_UINT8(bbsms, MenelausState),
+        VMSTATE_UINT8_ARRAY(pull, MenelausState, 4),
+        VMSTATE_UINT8_ARRAY(mmc_ctrl, MenelausState, 3),
+        VMSTATE_UINT8(mmc_debounce, MenelausState),
+        VMSTATE_UINT8(rtc.ctrl, MenelausState),
+        VMSTATE_UINT16(rtc.comp, MenelausState),
+        VMSTATE_UINT16(rtc_next_vmstate, MenelausState),
+        VMSTATE_STRUCT(rtc.new, MenelausState, 0, vmstate_menelaus_tm,
+                       struct tm),
+        VMSTATE_STRUCT(rtc.alm, MenelausState, 0, vmstate_menelaus_tm,
+                       struct tm),
+        VMSTATE_UINT8(pwrbtn_state, MenelausState),
+        VMSTATE_I2C_SLAVE(i2c, MenelausState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int twl92230_init(I2CSlave *i2c)
+{
+    MenelausState *s = FROM_I2C_SLAVE(MenelausState, i2c);
+
+    s->rtc.hz_tm = qemu_new_timer_ms(rtc_clock, menelaus_rtc_hz, s);
+    /* Three output pins plus one interrupt pin.  */
+    qdev_init_gpio_out(&i2c->qdev, s->out, 4);
+
+    /* Three input pins plus one power-button pin.  */
+    qdev_init_gpio_in(&i2c->qdev, menelaus_gpio_set, 4);
+
+    menelaus_reset(&s->i2c);
+
+    return 0;
+}
+
+static void twl92230_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
+
+    sc->init = twl92230_init;
+    sc->event = menelaus_event;
+    sc->recv = menelaus_rx;
+    sc->send = menelaus_tx;
+    dc->vmsd = &vmstate_menelaus;
+}
+
+static const TypeInfo twl92230_info = {
+    .name          = "twl92230",
+    .parent        = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(MenelausState),
+    .class_init    = twl92230_class_init,
+};
+
+static void twl92230_register_types(void)
+{
+    type_register_static(&twl92230_info);
+}
+
+type_init(twl92230_register_types)
diff --git a/hw/timer/xilinx_timer.c b/hw/timer/xilinx_timer.c
new file mode 100644 (file)
index 0000000..0c39cff
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * QEMU model of the Xilinx timer block.
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/sysbus.h"
+#include "hw/ptimer.h"
+#include "qemu/log.h"
+
+#define D(x)
+
+#define R_TCSR     0
+#define R_TLR      1
+#define R_TCR      2
+#define R_MAX      4
+
+#define TCSR_MDT        (1<<0)
+#define TCSR_UDT        (1<<1)
+#define TCSR_GENT       (1<<2)
+#define TCSR_CAPT       (1<<3)
+#define TCSR_ARHT       (1<<4)
+#define TCSR_LOAD       (1<<5)
+#define TCSR_ENIT       (1<<6)
+#define TCSR_ENT        (1<<7)
+#define TCSR_TINT       (1<<8)
+#define TCSR_PWMA       (1<<9)
+#define TCSR_ENALL      (1<<10)
+
+struct xlx_timer
+{
+    QEMUBH *bh;
+    ptimer_state *ptimer;
+    void *parent;
+    int nr; /* for debug.  */
+
+    unsigned long timer_div;
+
+    uint32_t regs[R_MAX];
+};
+
+struct timerblock
+{
+    SysBusDevice busdev;
+    MemoryRegion mmio;
+    qemu_irq irq;
+    uint8_t one_timer_only;
+    uint32_t freq_hz;
+    struct xlx_timer *timers;
+};
+
+static inline unsigned int num_timers(struct timerblock *t)
+{
+    return 2 - t->one_timer_only;
+}
+
+static inline unsigned int timer_from_addr(hwaddr addr)
+{
+    /* Timers get a 4x32bit control reg area each.  */
+    return addr >> 2;
+}
+
+static void timer_update_irq(struct timerblock *t)
+{
+    unsigned int i, irq = 0;
+    uint32_t csr;
+
+    for (i = 0; i < num_timers(t); i++) {
+        csr = t->timers[i].regs[R_TCSR];
+        irq |= (csr & TCSR_TINT) && (csr & TCSR_ENIT);
+    }
+
+    /* All timers within the same slave share a single IRQ line.  */
+    qemu_set_irq(t->irq, !!irq);
+}
+
+static uint64_t
+timer_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    struct timerblock *t = opaque;
+    struct xlx_timer *xt;
+    uint32_t r = 0;
+    unsigned int timer;
+
+    addr >>= 2;
+    timer = timer_from_addr(addr);
+    xt = &t->timers[timer];
+    /* Further decoding to address a specific timers reg.  */
+    addr &= 0x3;
+    switch (addr)
+    {
+        case R_TCR:
+                r = ptimer_get_count(xt->ptimer);
+                if (!(xt->regs[R_TCSR] & TCSR_UDT))
+                    r = ~r;
+                D(qemu_log("xlx_timer t=%d read counter=%x udt=%d\n",
+                         timer, r, xt->regs[R_TCSR] & TCSR_UDT));
+            break;
+        default:
+            if (addr < ARRAY_SIZE(xt->regs))
+                r = xt->regs[addr];
+            break;
+
+    }
+    D(fprintf(stderr, "%s timer=%d %x=%x\n", __func__, timer, addr * 4, r));
+    return r;
+}
+
+static void timer_enable(struct xlx_timer *xt)
+{
+    uint64_t count;
+
+    D(fprintf(stderr, "%s timer=%d down=%d\n", __func__,
+              xt->nr, xt->regs[R_TCSR] & TCSR_UDT));
+
+    ptimer_stop(xt->ptimer);
+
+    if (xt->regs[R_TCSR] & TCSR_UDT)
+        count = xt->regs[R_TLR];
+    else
+        count = ~0 - xt->regs[R_TLR];
+    ptimer_set_limit(xt->ptimer, count, 1);
+    ptimer_run(xt->ptimer, 1);
+}
+
+static void
+timer_write(void *opaque, hwaddr addr,
+            uint64_t val64, unsigned int size)
+{
+    struct timerblock *t = opaque;
+    struct xlx_timer *xt;
+    unsigned int timer;
+    uint32_t value = val64;
+
+    addr >>= 2;
+    timer = timer_from_addr(addr);
+    xt = &t->timers[timer];
+    D(fprintf(stderr, "%s addr=%x val=%x (timer=%d off=%d)\n",
+             __func__, addr * 4, value, timer, addr & 3));
+    /* Further decoding to address a specific timers reg.  */
+    addr &= 3;
+    switch (addr) 
+    {
+        case R_TCSR:
+            if (value & TCSR_TINT)
+                value &= ~TCSR_TINT;
+
+            xt->regs[addr] = value;
+            if (value & TCSR_ENT)
+                timer_enable(xt);
+            break;
+        default:
+            if (addr < ARRAY_SIZE(xt->regs))
+                xt->regs[addr] = value;
+            break;
+    }
+    timer_update_irq(t);
+}
+
+static const MemoryRegionOps timer_ops = {
+    .read = timer_read,
+    .write = timer_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void timer_hit(void *opaque)
+{
+    struct xlx_timer *xt = opaque;
+    struct timerblock *t = xt->parent;
+    D(fprintf(stderr, "%s %d\n", __func__, xt->nr));
+    xt->regs[R_TCSR] |= TCSR_TINT;
+
+    if (xt->regs[R_TCSR] & TCSR_ARHT)
+        timer_enable(xt);
+    timer_update_irq(t);
+}
+
+static int xilinx_timer_init(SysBusDevice *dev)
+{
+    struct timerblock *t = FROM_SYSBUS(typeof (*t), dev);
+    unsigned int i;
+
+    /* All timers share a single irq line.  */
+    sysbus_init_irq(dev, &t->irq);
+
+    /* Init all the ptimers.  */
+    t->timers = g_malloc0(sizeof t->timers[0] * num_timers(t));
+    for (i = 0; i < num_timers(t); i++) {
+        struct xlx_timer *xt = &t->timers[i];
+
+        xt->parent = t;
+        xt->nr = i;
+        xt->bh = qemu_bh_new(timer_hit, xt);
+        xt->ptimer = ptimer_init(xt->bh);
+        ptimer_set_freq(xt->ptimer, t->freq_hz);
+    }
+
+    memory_region_init_io(&t->mmio, &timer_ops, t, "xlnx.xps-timer",
+                          R_MAX * 4 * num_timers(t));
+    sysbus_init_mmio(dev, &t->mmio);
+    return 0;
+}
+
+static Property xilinx_timer_properties[] = {
+    DEFINE_PROP_UINT32("clock-frequency", struct timerblock, freq_hz,
+                                                                62 * 1000000),
+    DEFINE_PROP_UINT8("one-timer-only", struct timerblock, one_timer_only, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xilinx_timer_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = xilinx_timer_init;
+    dc->props = xilinx_timer_properties;
+}
+
+static const TypeInfo xilinx_timer_info = {
+    .name          = "xlnx.xps-timer",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(struct timerblock),
+    .class_init    = xilinx_timer_class_init,
+};
+
+static void xilinx_timer_register_types(void)
+{
+    type_register_static(&xilinx_timer_info);
+}
+
+type_init(xilinx_timer_register_types)
diff --git a/hw/tmp105.c b/hw/tmp105.c
deleted file mode 100644 (file)
index 21a27a6..0000000
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Texas Instruments TMP105 temperature sensor.
- *
- * Copyright (C) 2008 Nokia Corporation
- * Written by Andrzej Zaborowski <andrew@openedhand.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 or
- * (at your option) version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/hw.h"
-#include "hw/i2c/i2c.h"
-#include "hw/tmp105.h"
-#include "qapi/visitor.h"
-
-static void tmp105_interrupt_update(TMP105State *s)
-{
-    qemu_set_irq(s->pin, s->alarm ^ ((~s->config >> 2) & 1));  /* POL */
-}
-
-static void tmp105_alarm_update(TMP105State *s)
-{
-    if ((s->config >> 0) & 1) {                                        /* SD */
-        if ((s->config >> 7) & 1)                              /* OS */
-            s->config &= ~(1 << 7);                            /* OS */
-        else
-            return;
-    }
-
-    if ((s->config >> 1) & 1) {                                        /* TM */
-        if (s->temperature >= s->limit[1])
-            s->alarm = 1;
-        else if (s->temperature < s->limit[0])
-            s->alarm = 1;
-    } else {
-        if (s->temperature >= s->limit[1])
-            s->alarm = 1;
-        else if (s->temperature < s->limit[0])
-            s->alarm = 0;
-    }
-
-    tmp105_interrupt_update(s);
-}
-
-static void tmp105_get_temperature(Object *obj, Visitor *v, void *opaque,
-                                   const char *name, Error **errp)
-{
-    TMP105State *s = TMP105(obj);
-    int64_t value = s->temperature;
-
-    visit_type_int(v, &value, name, errp);
-}
-
-/* Units are 0.001 centigrades relative to 0 C.  */
-static void tmp105_set_temperature(Object *obj, Visitor *v, void *opaque,
-                                   const char *name, Error **errp)
-{
-    TMP105State *s = TMP105(obj);
-    int64_t temp;
-
-    visit_type_int(v, &temp, name, errp);
-    if (error_is_set(errp)) {
-        return;
-    }
-    if (temp >= 128000 || temp < -128000) {
-        error_setg(errp, "value %" PRId64 ".%03" PRIu64 " Â°C is out of range",
-                   temp / 1000, temp % 1000);
-        return;
-    }
-
-    s->temperature = ((int16_t) (temp * 0x800 / 128000)) << 4;
-
-    tmp105_alarm_update(s);
-}
-
-static const int tmp105_faultq[4] = { 1, 2, 4, 6 };
-
-static void tmp105_read(TMP105State *s)
-{
-    s->len = 0;
-
-    if ((s->config >> 1) & 1) {                                        /* TM */
-        s->alarm = 0;
-        tmp105_interrupt_update(s);
-    }
-
-    switch (s->pointer & 3) {
-    case TMP105_REG_TEMPERATURE:
-        s->buf[s->len ++] = (((uint16_t) s->temperature) >> 8);
-        s->buf[s->len ++] = (((uint16_t) s->temperature) >> 0) &
-                (0xf0 << ((~s->config >> 5) & 3));             /* R */
-        break;
-
-    case TMP105_REG_CONFIG:
-        s->buf[s->len ++] = s->config;
-        break;
-
-    case TMP105_REG_T_LOW:
-        s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 8;
-        s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 0;
-        break;
-
-    case TMP105_REG_T_HIGH:
-        s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 8;
-        s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 0;
-        break;
-    }
-}
-
-static void tmp105_write(TMP105State *s)
-{
-    switch (s->pointer & 3) {
-    case TMP105_REG_TEMPERATURE:
-        break;
-
-    case TMP105_REG_CONFIG:
-        if (s->buf[0] & ~s->config & (1 << 0))                 /* SD */
-            printf("%s: TMP105 shutdown\n", __FUNCTION__);
-        s->config = s->buf[0];
-        s->faults = tmp105_faultq[(s->config >> 3) & 3];       /* F */
-        tmp105_alarm_update(s);
-        break;
-
-    case TMP105_REG_T_LOW:
-    case TMP105_REG_T_HIGH:
-        if (s->len >= 3)
-            s->limit[s->pointer & 1] = (int16_t)
-                    ((((uint16_t) s->buf[0]) << 8) | s->buf[1]);
-        tmp105_alarm_update(s);
-        break;
-    }
-}
-
-static int tmp105_rx(I2CSlave *i2c)
-{
-    TMP105State *s = TMP105(i2c);
-
-    if (s->len < 2) {
-        return s->buf[s->len ++];
-    } else {
-        return 0xff;
-    }
-}
-
-static int tmp105_tx(I2CSlave *i2c, uint8_t data)
-{
-    TMP105State *s = TMP105(i2c);
-
-    if (s->len == 0) {
-        s->pointer = data;
-        s->len++;
-    } else {
-        if (s->len <= 2) {
-            s->buf[s->len - 1] = data;
-        }
-        s->len++;
-        tmp105_write(s);
-    }
-
-    return 0;
-}
-
-static void tmp105_event(I2CSlave *i2c, enum i2c_event event)
-{
-    TMP105State *s = TMP105(i2c);
-
-    if (event == I2C_START_RECV) {
-        tmp105_read(s);
-    }
-
-    s->len = 0;
-}
-
-static int tmp105_post_load(void *opaque, int version_id)
-{
-    TMP105State *s = opaque;
-
-    s->faults = tmp105_faultq[(s->config >> 3) & 3];           /* F */
-
-    tmp105_interrupt_update(s);
-    return 0;
-}
-
-static const VMStateDescription vmstate_tmp105 = {
-    .name = "TMP105",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .post_load = tmp105_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT8(len, TMP105State),
-        VMSTATE_UINT8_ARRAY(buf, TMP105State, 2),
-        VMSTATE_UINT8(pointer, TMP105State),
-        VMSTATE_UINT8(config, TMP105State),
-        VMSTATE_INT16(temperature, TMP105State),
-        VMSTATE_INT16_ARRAY(limit, TMP105State, 2),
-        VMSTATE_UINT8(alarm, TMP105State),
-        VMSTATE_I2C_SLAVE(i2c, TMP105State),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void tmp105_reset(I2CSlave *i2c)
-{
-    TMP105State *s = TMP105(i2c);
-
-    s->temperature = 0;
-    s->pointer = 0;
-    s->config = 0;
-    s->faults = tmp105_faultq[(s->config >> 3) & 3];
-    s->alarm = 0;
-
-    tmp105_interrupt_update(s);
-}
-
-static int tmp105_init(I2CSlave *i2c)
-{
-    TMP105State *s = TMP105(i2c);
-
-    qdev_init_gpio_out(&i2c->qdev, &s->pin, 1);
-
-    tmp105_reset(&s->i2c);
-
-    return 0;
-}
-
-static void tmp105_initfn(Object *obj)
-{
-    object_property_add(obj, "temperature", "int",
-                        tmp105_get_temperature,
-                        tmp105_set_temperature, NULL, NULL, NULL);
-}
-
-static void tmp105_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
-
-    k->init = tmp105_init;
-    k->event = tmp105_event;
-    k->recv = tmp105_rx;
-    k->send = tmp105_tx;
-    dc->vmsd = &vmstate_tmp105;
-}
-
-static const TypeInfo tmp105_info = {
-    .name          = TYPE_TMP105,
-    .parent        = TYPE_I2C_SLAVE,
-    .instance_size = sizeof(TMP105State),
-    .instance_init = tmp105_initfn,
-    .class_init    = tmp105_class_init,
-};
-
-static void tmp105_register_types(void)
-{
-    type_register_static(&tmp105_info);
-}
-
-type_init(tmp105_register_types)
diff --git a/hw/tpci200.c b/hw/tpci200.c
deleted file mode 100644 (file)
index e3408ef..0000000
+++ /dev/null
@@ -1,671 +0,0 @@
-/*
- * QEMU TEWS TPCI200 IndustryPack carrier emulation
- *
- * Copyright (C) 2012 Igalia, S.L.
- * Author: Alberto Garcia <agarcia@igalia.com>
- *
- * This code is licensed under the GNU GPL v2 or (at your option) any
- * later version.
- */
-
-#include "hw/ipack.h"
-#include "hw/pci/pci.h"
-#include "qemu/bitops.h"
-#include <stdio.h>
-
-/* #define DEBUG_TPCI */
-
-#ifdef DEBUG_TPCI
-#define DPRINTF(fmt, ...) \
-    do { fprintf(stderr, "TPCI200: " fmt, ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) do { } while (0)
-#endif
-
-#define N_MODULES 4
-
-#define IP_ID_SPACE  2
-#define IP_INT_SPACE 3
-#define IP_IO_SPACE_ADDR_MASK  0x7F
-#define IP_ID_SPACE_ADDR_MASK  0x3F
-#define IP_INT_SPACE_ADDR_MASK 0x3F
-
-#define STATUS_INT(IP, INTNO) BIT((IP) * 2 + (INTNO))
-#define STATUS_TIME(IP)       BIT((IP) + 12)
-#define STATUS_ERR_ANY        0xF00
-
-#define CTRL_CLKRATE          BIT(0)
-#define CTRL_RECOVER          BIT(1)
-#define CTRL_TIME_INT         BIT(2)
-#define CTRL_ERR_INT          BIT(3)
-#define CTRL_INT_EDGE(INTNO)  BIT(4 + (INTNO))
-#define CTRL_INT(INTNO)       BIT(6 + (INTNO))
-
-#define REG_REV_ID    0x00
-#define REG_IP_A_CTRL 0x02
-#define REG_IP_B_CTRL 0x04
-#define REG_IP_C_CTRL 0x06
-#define REG_IP_D_CTRL 0x08
-#define REG_RESET     0x0A
-#define REG_STATUS    0x0C
-#define IP_N_FROM_REG(REG) ((REG) / 2 - 1)
-
-typedef struct {
-    PCIDevice dev;
-    IPackBus bus;
-    MemoryRegion mmio;
-    MemoryRegion io;
-    MemoryRegion las0;
-    MemoryRegion las1;
-    MemoryRegion las2;
-    MemoryRegion las3;
-    bool big_endian[3];
-    uint8_t ctrl[N_MODULES];
-    uint16_t status;
-    uint8_t int_set;
-} TPCI200State;
-
-#define TYPE_TPCI200 "tpci200"
-
-#define TPCI200(obj) \
-    OBJECT_CHECK(TPCI200State, (obj), TYPE_TPCI200)
-
-static const uint8_t local_config_regs[] = {
-    0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xFC, 0xFF, 0x0F, 0x00, 0x00, 0x00,
-    0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
-    0x00, 0x08, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x01,
-    0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x60, 0x41, 0xD4,
-    0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x01,
-    0x14, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x08, 0x01, 0x02,
-    0x00, 0x04, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x80, 0x02, 0x41,
-    0x00, 0x00, 0x00, 0x00, 0x40, 0x7A, 0x00, 0x52, 0x92, 0x24, 0x02
-};
-
-static void adjust_addr(bool big_endian, hwaddr *addr, unsigned size)
-{
-    /* During 8 bit access in big endian mode,
-       odd and even addresses are swapped */
-    if (big_endian && size == 1) {
-        *addr ^= 1;
-    }
-}
-
-static uint64_t adjust_value(bool big_endian, uint64_t *val, unsigned size)
-{
-    /* Local spaces only support 8/16 bit access,
-     * so there's no need to care for sizes > 2 */
-    if (big_endian && size == 2) {
-        *val = bswap16(*val);
-    }
-    return *val;
-}
-
-static void tpci200_set_irq(void *opaque, int intno, int level)
-{
-    IPackDevice *ip = opaque;
-    IPackBus *bus = IPACK_BUS(qdev_get_parent_bus(DEVICE(ip)));
-    PCIDevice *pcidev = PCI_DEVICE(BUS(bus)->parent);
-    TPCI200State *dev = TPCI200(pcidev);
-    unsigned ip_n = ip->slot;
-    uint16_t prev_status = dev->status;
-
-    assert(ip->slot >= 0 && ip->slot < N_MODULES);
-
-    /* The requested interrupt must be enabled in the IP CONTROL
-     * register */
-    if (!(dev->ctrl[ip_n] & CTRL_INT(intno))) {
-        return;
-    }
-
-    /* Update the interrupt status in the IP STATUS register */
-    if (level) {
-        dev->status |=  STATUS_INT(ip_n, intno);
-    } else {
-        dev->status &= ~STATUS_INT(ip_n, intno);
-    }
-
-    /* Return if there are no changes */
-    if (dev->status == prev_status) {
-        return;
-    }
-
-    DPRINTF("IP %u INT%u#: %u\n", ip_n, intno, level);
-
-    /* Check if the interrupt is edge sensitive */
-    if (dev->ctrl[ip_n] & CTRL_INT_EDGE(intno)) {
-        if (level) {
-            qemu_set_irq(dev->dev.irq[0], !dev->int_set);
-            qemu_set_irq(dev->dev.irq[0],  dev->int_set);
-        }
-    } else {
-        unsigned i, j;
-        uint16_t level_status = dev->status;
-
-        /* Check if there are any level sensitive interrupts set by
-           removing the ones that are edge sensitive from the status
-           register */
-        for (i = 0; i < N_MODULES; i++) {
-            for (j = 0; j < 2; j++) {
-                if (dev->ctrl[i] & CTRL_INT_EDGE(j)) {
-                    level_status &= ~STATUS_INT(i, j);
-                }
-            }
-        }
-
-        if (level_status && !dev->int_set) {
-            qemu_irq_raise(dev->dev.irq[0]);
-            dev->int_set = 1;
-        } else if (!level_status && dev->int_set) {
-            qemu_irq_lower(dev->dev.irq[0]);
-            dev->int_set = 0;
-        }
-    }
-}
-
-static uint64_t tpci200_read_cfg(void *opaque, hwaddr addr, unsigned size)
-{
-    TPCI200State *s = opaque;
-    uint8_t ret = 0;
-    if (addr < ARRAY_SIZE(local_config_regs)) {
-        ret = local_config_regs[addr];
-    }
-    /* Endianness is stored in the first bit of these registers */
-    if ((addr == 0x2b && s->big_endian[0]) ||
-        (addr == 0x2f && s->big_endian[1]) ||
-        (addr == 0x33 && s->big_endian[2])) {
-        ret |= 1;
-    }
-    DPRINTF("Read from LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) ret);
-    return ret;
-}
-
-static void tpci200_write_cfg(void *opaque, hwaddr addr, uint64_t val,
-                              unsigned size)
-{
-    TPCI200State *s = opaque;
-    /* Endianness is stored in the first bit of these registers */
-    if (addr == 0x2b || addr == 0x2f || addr == 0x33) {
-        unsigned las = (addr - 0x2b) / 4;
-        s->big_endian[las] = val & 1;
-        DPRINTF("LAS%u big endian mode: %u\n", las, (unsigned) val & 1);
-    } else {
-        DPRINTF("Write to LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) val);
-    }
-}
-
-static uint64_t tpci200_read_las0(void *opaque, hwaddr addr, unsigned size)
-{
-    TPCI200State *s = opaque;
-    uint64_t ret = 0;
-
-    switch (addr) {
-
-    case REG_REV_ID:
-        DPRINTF("Read REVISION ID\n"); /* Current value is 0x00 */
-        break;
-
-    case REG_IP_A_CTRL:
-    case REG_IP_B_CTRL:
-    case REG_IP_C_CTRL:
-    case REG_IP_D_CTRL:
-        {
-            unsigned ip_n = IP_N_FROM_REG(addr);
-            ret = s->ctrl[ip_n];
-            DPRINTF("Read IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) ret);
-        }
-        break;
-
-    case REG_RESET:
-        DPRINTF("Read RESET\n"); /* Not implemented */
-        break;
-
-    case REG_STATUS:
-        ret = s->status;
-        DPRINTF("Read STATUS: 0x%x\n", (unsigned) ret);
-        break;
-
-    /* Reserved */
-    default:
-        DPRINTF("Unsupported read from LAS0 0x%x\n", (unsigned) addr);
-        break;
-    }
-
-    return adjust_value(s->big_endian[0], &ret, size);
-}
-
-static void tpci200_write_las0(void *opaque, hwaddr addr, uint64_t val,
-                               unsigned size)
-{
-    TPCI200State *s = opaque;
-
-    adjust_value(s->big_endian[0], &val, size);
-
-    switch (addr) {
-
-    case REG_REV_ID:
-        DPRINTF("Write Revision ID: 0x%x\n", (unsigned) val); /* No effect */
-        break;
-
-    case REG_IP_A_CTRL:
-    case REG_IP_B_CTRL:
-    case REG_IP_C_CTRL:
-    case REG_IP_D_CTRL:
-        {
-            unsigned ip_n = IP_N_FROM_REG(addr);
-            s->ctrl[ip_n] = val;
-            DPRINTF("Write IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) val);
-        }
-        break;
-
-    case REG_RESET:
-        DPRINTF("Write RESET: 0x%x\n", (unsigned) val); /* Not implemented */
-        break;
-
-    case REG_STATUS:
-        {
-            unsigned i;
-
-            for (i = 0; i < N_MODULES; i++) {
-                IPackDevice *ip = ipack_device_find(&s->bus, i);
-
-                if (ip != NULL) {
-                    if (val & STATUS_INT(i, 0)) {
-                        DPRINTF("Clear IP %c INT0# status\n", 'A' + i);
-                        qemu_irq_lower(ip->irq[0]);
-                    }
-                    if (val & STATUS_INT(i, 1)) {
-                        DPRINTF("Clear IP %c INT1# status\n", 'A' + i);
-                        qemu_irq_lower(ip->irq[1]);
-                    }
-                }
-
-                if (val & STATUS_TIME(i)) {
-                    DPRINTF("Clear IP %c timeout\n", 'A' + i);
-                    s->status &= ~STATUS_TIME(i);
-                }
-            }
-
-            if (val & STATUS_ERR_ANY) {
-                DPRINTF("Unexpected write to STATUS register: 0x%x\n",
-                        (unsigned) val);
-            }
-        }
-        break;
-
-    /* Reserved */
-    default:
-        DPRINTF("Unsupported write to LAS0 0x%x: 0x%x\n",
-                (unsigned) addr, (unsigned) val);
-        break;
-    }
-}
-
-static uint64_t tpci200_read_las1(void *opaque, hwaddr addr, unsigned size)
-{
-    TPCI200State *s = opaque;
-    IPackDevice *ip;
-    uint64_t ret = 0;
-    unsigned ip_n, space;
-    uint8_t offset;
-
-    adjust_addr(s->big_endian[1], &addr, size);
-
-    /*
-     * The address is divided into the IP module number (0-4), the IP
-     * address space (I/O, ID, INT) and the offset within that space.
-     */
-    ip_n = addr >> 8;
-    space = (addr >> 6) & 3;
-    ip = ipack_device_find(&s->bus, ip_n);
-
-    if (ip == NULL) {
-        DPRINTF("Read LAS1: IP module %u not installed\n", ip_n);
-    } else {
-        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
-        switch (space) {
-
-        case IP_ID_SPACE:
-            offset = addr & IP_ID_SPACE_ADDR_MASK;
-            if (k->id_read) {
-                ret = k->id_read(ip, offset);
-            }
-            break;
-
-        case IP_INT_SPACE:
-            offset = addr & IP_INT_SPACE_ADDR_MASK;
-
-            /* Read address 0 to ACK IP INT0# and address 2 to ACK IP INT1# */
-            if (offset == 0 || offset == 2) {
-                unsigned intno = offset / 2;
-                bool int_set = s->status & STATUS_INT(ip_n, intno);
-                bool int_edge_sensitive = s->ctrl[ip_n] & CTRL_INT_EDGE(intno);
-                if (int_set && !int_edge_sensitive) {
-                    qemu_irq_lower(ip->irq[intno]);
-                }
-            }
-
-            if (k->int_read) {
-                ret = k->int_read(ip, offset);
-            }
-            break;
-
-        default:
-            offset = addr & IP_IO_SPACE_ADDR_MASK;
-            if (k->io_read) {
-                ret = k->io_read(ip, offset);
-            }
-            break;
-        }
-    }
-
-    return adjust_value(s->big_endian[1], &ret, size);
-}
-
-static void tpci200_write_las1(void *opaque, hwaddr addr, uint64_t val,
-                               unsigned size)
-{
-    TPCI200State *s = opaque;
-    IPackDevice *ip;
-    unsigned ip_n, space;
-    uint8_t offset;
-
-    adjust_addr(s->big_endian[1], &addr, size);
-    adjust_value(s->big_endian[1], &val, size);
-
-    /*
-     * The address is divided into the IP module number, the IP
-     * address space (I/O, ID, INT) and the offset within that space.
-     */
-    ip_n = addr >> 8;
-    space = (addr >> 6) & 3;
-    ip = ipack_device_find(&s->bus, ip_n);
-
-    if (ip == NULL) {
-        DPRINTF("Write LAS1: IP module %u not installed\n", ip_n);
-    } else {
-        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
-        switch (space) {
-
-        case IP_ID_SPACE:
-            offset = addr & IP_ID_SPACE_ADDR_MASK;
-            if (k->id_write) {
-                k->id_write(ip, offset, val);
-            }
-            break;
-
-        case IP_INT_SPACE:
-            offset = addr & IP_INT_SPACE_ADDR_MASK;
-            if (k->int_write) {
-                k->int_write(ip, offset, val);
-            }
-            break;
-
-        default:
-            offset = addr & IP_IO_SPACE_ADDR_MASK;
-            if (k->io_write) {
-                k->io_write(ip, offset, val);
-            }
-            break;
-        }
-    }
-}
-
-static uint64_t tpci200_read_las2(void *opaque, hwaddr addr, unsigned size)
-{
-    TPCI200State *s = opaque;
-    IPackDevice *ip;
-    uint64_t ret = 0;
-    unsigned ip_n;
-    uint32_t offset;
-
-    adjust_addr(s->big_endian[2], &addr, size);
-
-    /*
-     * The address is divided into the IP module number and the offset
-     * within the IP module MEM space.
-     */
-    ip_n = addr >> 23;
-    offset = addr & 0x7fffff;
-    ip = ipack_device_find(&s->bus, ip_n);
-
-    if (ip == NULL) {
-        DPRINTF("Read LAS2: IP module %u not installed\n", ip_n);
-    } else {
-        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
-        if (k->mem_read16) {
-            ret = k->mem_read16(ip, offset);
-        }
-    }
-
-    return adjust_value(s->big_endian[2], &ret, size);
-}
-
-static void tpci200_write_las2(void *opaque, hwaddr addr, uint64_t val,
-                               unsigned size)
-{
-    TPCI200State *s = opaque;
-    IPackDevice *ip;
-    unsigned ip_n;
-    uint32_t offset;
-
-    adjust_addr(s->big_endian[2], &addr, size);
-    adjust_value(s->big_endian[2], &val, size);
-
-    /*
-     * The address is divided into the IP module number and the offset
-     * within the IP module MEM space.
-     */
-    ip_n = addr >> 23;
-    offset = addr & 0x7fffff;
-    ip = ipack_device_find(&s->bus, ip_n);
-
-    if (ip == NULL) {
-        DPRINTF("Write LAS2: IP module %u not installed\n", ip_n);
-    } else {
-        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
-        if (k->mem_write16) {
-            k->mem_write16(ip, offset, val);
-        }
-    }
-}
-
-static uint64_t tpci200_read_las3(void *opaque, hwaddr addr, unsigned size)
-{
-    TPCI200State *s = opaque;
-    IPackDevice *ip;
-    uint64_t ret = 0;
-    /*
-     * The address is divided into the IP module number and the offset
-     * within the IP module MEM space.
-     */
-    unsigned ip_n = addr >> 22;
-    uint32_t offset = addr & 0x3fffff;
-
-    ip = ipack_device_find(&s->bus, ip_n);
-
-    if (ip == NULL) {
-        DPRINTF("Read LAS3: IP module %u not installed\n", ip_n);
-    } else {
-        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
-        if (k->mem_read8) {
-            ret = k->mem_read8(ip, offset);
-        }
-    }
-
-    return ret;
-}
-
-static void tpci200_write_las3(void *opaque, hwaddr addr, uint64_t val,
-                               unsigned size)
-{
-    TPCI200State *s = opaque;
-    IPackDevice *ip;
-    /*
-     * The address is divided into the IP module number and the offset
-     * within the IP module MEM space.
-     */
-    unsigned ip_n = addr >> 22;
-    uint32_t offset = addr & 0x3fffff;
-
-    ip = ipack_device_find(&s->bus, ip_n);
-
-    if (ip == NULL) {
-        DPRINTF("Write LAS3: IP module %u not installed\n", ip_n);
-    } else {
-        IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip);
-        if (k->mem_write8) {
-            k->mem_write8(ip, offset, val);
-        }
-    }
-}
-
-static const MemoryRegionOps tpci200_cfg_ops = {
-    .read = tpci200_read_cfg,
-    .write = tpci200_write_cfg,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .valid =  {
-        .min_access_size = 1,
-        .max_access_size = 4
-    },
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 1
-    }
-};
-
-static const MemoryRegionOps tpci200_las0_ops = {
-    .read = tpci200_read_las0,
-    .write = tpci200_write_las0,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .valid =  {
-        .min_access_size = 2,
-        .max_access_size = 2
-    }
-};
-
-static const MemoryRegionOps tpci200_las1_ops = {
-    .read = tpci200_read_las1,
-    .write = tpci200_write_las1,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .valid =  {
-        .min_access_size = 1,
-        .max_access_size = 2
-    }
-};
-
-static const MemoryRegionOps tpci200_las2_ops = {
-    .read = tpci200_read_las2,
-    .write = tpci200_write_las2,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .valid =  {
-        .min_access_size = 1,
-        .max_access_size = 2
-    }
-};
-
-static const MemoryRegionOps tpci200_las3_ops = {
-    .read = tpci200_read_las3,
-    .write = tpci200_write_las3,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .valid =  {
-        .min_access_size = 1,
-        .max_access_size = 1
-    }
-};
-
-static int tpci200_initfn(PCIDevice *pci_dev)
-{
-    TPCI200State *s = TPCI200(pci_dev);
-    uint8_t *c = s->dev.config;
-
-    pci_set_word(c + PCI_COMMAND, 0x0003);
-    pci_set_word(c + PCI_STATUS,  0x0280);
-
-    pci_set_byte(c + PCI_INTERRUPT_PIN, 0x01); /* Interrupt pin A */
-
-    pci_set_byte(c + PCI_CAPABILITY_LIST, 0x40);
-    pci_set_long(c + 0x40, 0x48014801);
-    pci_set_long(c + 0x48, 0x00024C06);
-    pci_set_long(c + 0x4C, 0x00000003);
-
-    memory_region_init_io(&s->mmio, &tpci200_cfg_ops,
-                          s, "tpci200_mmio", 128);
-    memory_region_init_io(&s->io,   &tpci200_cfg_ops,
-                          s, "tpci200_io",   128);
-    memory_region_init_io(&s->las0, &tpci200_las0_ops,
-                          s, "tpci200_las0", 256);
-    memory_region_init_io(&s->las1, &tpci200_las1_ops,
-                          s, "tpci200_las1", 1024);
-    memory_region_init_io(&s->las2, &tpci200_las2_ops,
-                          s, "tpci200_las2", 1024*1024*32);
-    memory_region_init_io(&s->las3, &tpci200_las3_ops,
-                          s, "tpci200_las3", 1024*1024*16);
-    pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
-    pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO,     &s->io);
-    pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las0);
-    pci_register_bar(&s->dev, 3, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las1);
-    pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las2);
-    pci_register_bar(&s->dev, 5, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las3);
-
-    ipack_bus_new_inplace(&s->bus, DEVICE(&s->dev), NULL,
-                          N_MODULES, tpci200_set_irq);
-
-    return 0;
-}
-
-static void tpci200_exitfn(PCIDevice *pci_dev)
-{
-    TPCI200State *s = TPCI200(pci_dev);
-
-    memory_region_destroy(&s->mmio);
-    memory_region_destroy(&s->io);
-    memory_region_destroy(&s->las0);
-    memory_region_destroy(&s->las1);
-    memory_region_destroy(&s->las2);
-    memory_region_destroy(&s->las3);
-}
-
-static const VMStateDescription vmstate_tpci200 = {
-    .name = "tpci200",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_PCI_DEVICE(dev, TPCI200State),
-        VMSTATE_BOOL_ARRAY(big_endian, TPCI200State, 3),
-        VMSTATE_UINT8_ARRAY(ctrl, TPCI200State, N_MODULES),
-        VMSTATE_UINT16(status, TPCI200State),
-        VMSTATE_UINT8(int_set, TPCI200State),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void tpci200_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = tpci200_initfn;
-    k->exit = tpci200_exitfn;
-    k->vendor_id = PCI_VENDOR_ID_TEWS;
-    k->device_id = PCI_DEVICE_ID_TEWS_TPCI200;
-    k->class_id = PCI_CLASS_BRIDGE_OTHER;
-    k->subsystem_vendor_id = PCI_VENDOR_ID_TEWS;
-    k->subsystem_id = 0x300A;
-    dc->desc = "TEWS TPCI200 IndustryPack carrier";
-    dc->vmsd = &vmstate_tpci200;
-}
-
-static const TypeInfo tpci200_info = {
-    .name          = TYPE_TPCI200,
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(TPCI200State),
-    .class_init    = tpci200_class_init,
-};
-
-static void tpci200_register_types(void)
-{
-    type_register_static(&tpci200_info);
-}
-
-type_init(tpci200_register_types)
diff --git a/hw/tsc2005.c b/hw/tsc2005.c
deleted file mode 100644 (file)
index 34ee1fb..0000000
+++ /dev/null
@@ -1,593 +0,0 @@
-/*
- * TI TSC2005 emulator.
- *
- * Copyright (c) 2006 Andrzej Zaborowski  <balrog@zabor.org>
- * Copyright (C) 2008 Nokia Corporation
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 or
- * (at your option) version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/hw.h"
-#include "qemu/timer.h"
-#include "ui/console.h"
-#include "hw/arm/devices.h"
-
-#define TSC_CUT_RESOLUTION(value, p)   ((value) >> (16 - (p ? 12 : 10)))
-
-typedef struct {
-    qemu_irq pint;     /* Combination of the nPENIRQ and DAV signals */
-    QEMUTimer *timer;
-    uint16_t model;
-
-    int x, y;
-    int pressure;
-
-    int state, reg, irq, command;
-    uint16_t data, dav;
-
-    int busy;
-    int enabled;
-    int host_mode;
-    int function;
-    int nextfunction;
-    int precision;
-    int nextprecision;
-    int filter;
-    int pin_func;
-    int timing[2];
-    int noise;
-    int reset;
-    int pdst;
-    int pnd0;
-    uint16_t temp_thr[2];
-    uint16_t aux_thr[2];
-
-    int tr[8];
-} TSC2005State;
-
-enum {
-    TSC_MODE_XYZ_SCAN  = 0x0,
-    TSC_MODE_XY_SCAN,
-    TSC_MODE_X,
-    TSC_MODE_Y,
-    TSC_MODE_Z,
-    TSC_MODE_AUX,
-    TSC_MODE_TEMP1,
-    TSC_MODE_TEMP2,
-    TSC_MODE_AUX_SCAN,
-    TSC_MODE_X_TEST,
-    TSC_MODE_Y_TEST,
-    TSC_MODE_TS_TEST,
-    TSC_MODE_RESERVED,
-    TSC_MODE_XX_DRV,
-    TSC_MODE_YY_DRV,
-    TSC_MODE_YX_DRV,
-};
-
-static const uint16_t mode_regs[16] = {
-    0xf000,    /* X, Y, Z scan */
-    0xc000,    /* X, Y scan */
-    0x8000,    /* X */
-    0x4000,    /* Y */
-    0x3000,    /* Z */
-    0x0800,    /* AUX */
-    0x0400,    /* TEMP1 */
-    0x0200,    /* TEMP2 */
-    0x0800,    /* AUX scan */
-    0x0040,    /* X test */
-    0x0020,    /* Y test */
-    0x0080,    /* Short-circuit test */
-    0x0000,    /* Reserved */
-    0x0000,    /* X+, X- drivers */
-    0x0000,    /* Y+, Y- drivers */
-    0x0000,    /* Y+, X- drivers */
-};
-
-#define X_TRANSFORM(s)                 \
-    ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3])
-#define Y_TRANSFORM(s)                 \
-    ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7])
-#define Z1_TRANSFORM(s)                        \
-    ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4)
-#define Z2_TRANSFORM(s)                        \
-    ((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4)
-
-#define AUX_VAL                                (700 << 4)      /* +/- 3 at 12-bit */
-#define TEMP1_VAL                      (1264 << 4)     /* +/- 5 at 12-bit */
-#define TEMP2_VAL                      (1531 << 4)     /* +/- 5 at 12-bit */
-
-static uint16_t tsc2005_read(TSC2005State *s, int reg)
-{
-    uint16_t ret;
-
-    switch (reg) {
-    case 0x0:  /* X */
-        s->dav &= ~mode_regs[TSC_MODE_X];
-        return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) +
-                (s->noise & 3);
-    case 0x1:  /* Y */
-        s->dav &= ~mode_regs[TSC_MODE_Y];
-        s->noise ++;
-        return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^
-                (s->noise & 3);
-    case 0x2:  /* Z1 */
-        s->dav &= 0xdfff;
-        return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) -
-                (s->noise & 3);
-    case 0x3:  /* Z2 */
-        s->dav &= 0xefff;
-        return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) |
-                (s->noise & 3);
-
-    case 0x4:  /* AUX */
-        s->dav &= ~mode_regs[TSC_MODE_AUX];
-        return TSC_CUT_RESOLUTION(AUX_VAL, s->precision);
-
-    case 0x5:  /* TEMP1 */
-        s->dav &= ~mode_regs[TSC_MODE_TEMP1];
-        return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) -
-                (s->noise & 5);
-    case 0x6:  /* TEMP2 */
-        s->dav &= 0xdfff;
-        s->dav &= ~mode_regs[TSC_MODE_TEMP2];
-        return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^
-                (s->noise & 3);
-
-    case 0x7:  /* Status */
-        ret = s->dav | (s->reset << 7) | (s->pdst << 2) | 0x0;
-        s->dav &= ~(mode_regs[TSC_MODE_X_TEST] | mode_regs[TSC_MODE_Y_TEST] |
-                        mode_regs[TSC_MODE_TS_TEST]);
-        s->reset = 1;
-        return ret;
-
-    case 0x8:  /* AUX high treshold */
-        return s->aux_thr[1];
-    case 0x9:  /* AUX low treshold */
-        return s->aux_thr[0];
-
-    case 0xa:  /* TEMP high treshold */
-        return s->temp_thr[1];
-    case 0xb:  /* TEMP low treshold */
-        return s->temp_thr[0];
-
-    case 0xc:  /* CFR0 */
-        return (s->pressure << 15) | ((!s->busy) << 14) |
-                (s->nextprecision << 13) | s->timing[0]; 
-    case 0xd:  /* CFR1 */
-        return s->timing[1];
-    case 0xe:  /* CFR2 */
-        return (s->pin_func << 14) | s->filter;
-
-    case 0xf:  /* Function select status */
-        return s->function >= 0 ? 1 << s->function : 0;
-    }
-
-    /* Never gets here */
-    return 0xffff;
-}
-
-static void tsc2005_write(TSC2005State *s, int reg, uint16_t data)
-{
-    switch (reg) {
-    case 0x8:  /* AUX high treshold */
-        s->aux_thr[1] = data;
-        break;
-    case 0x9:  /* AUX low treshold */
-        s->aux_thr[0] = data;
-        break;
-
-    case 0xa:  /* TEMP high treshold */
-        s->temp_thr[1] = data;
-        break;
-    case 0xb:  /* TEMP low treshold */
-        s->temp_thr[0] = data;
-        break;
-
-    case 0xc:  /* CFR0 */
-        s->host_mode = data >> 15;
-        if (s->enabled != !(data & 0x4000)) {
-            s->enabled = !(data & 0x4000);
-            fprintf(stderr, "%s: touchscreen sense %sabled\n",
-                            __FUNCTION__, s->enabled ? "en" : "dis");
-            if (s->busy && !s->enabled)
-                qemu_del_timer(s->timer);
-            s->busy &= s->enabled;
-        }
-        s->nextprecision = (data >> 13) & 1;
-        s->timing[0] = data & 0x1fff;
-        if ((s->timing[0] >> 11) == 3)
-            fprintf(stderr, "%s: illegal conversion clock setting\n",
-                            __FUNCTION__);
-        break;
-    case 0xd:  /* CFR1 */
-        s->timing[1] = data & 0xf07;
-        break;
-    case 0xe:  /* CFR2 */
-        s->pin_func = (data >> 14) & 3;
-        s->filter = data & 0x3fff;
-        break;
-
-    default:
-        fprintf(stderr, "%s: write into read-only register %x\n",
-                        __FUNCTION__, reg);
-    }
-}
-
-/* This handles most of the chip's logic.  */
-static void tsc2005_pin_update(TSC2005State *s)
-{
-    int64_t expires;
-    int pin_state;
-
-    switch (s->pin_func) {
-    case 0:
-        pin_state = !s->pressure && !!s->dav;
-        break;
-    case 1:
-    case 3:
-    default:
-        pin_state = !s->dav;
-        break;
-    case 2:
-        pin_state = !s->pressure;
-    }
-
-    if (pin_state != s->irq) {
-        s->irq = pin_state;
-        qemu_set_irq(s->pint, s->irq);
-    }
-
-    switch (s->nextfunction) {
-    case TSC_MODE_XYZ_SCAN:
-    case TSC_MODE_XY_SCAN:
-        if (!s->host_mode && s->dav)
-            s->enabled = 0;
-        if (!s->pressure)
-            return;
-        /* Fall through */
-    case TSC_MODE_AUX_SCAN:
-        break;
-
-    case TSC_MODE_X:
-    case TSC_MODE_Y:
-    case TSC_MODE_Z:
-        if (!s->pressure)
-            return;
-        /* Fall through */
-    case TSC_MODE_AUX:
-    case TSC_MODE_TEMP1:
-    case TSC_MODE_TEMP2:
-    case TSC_MODE_X_TEST:
-    case TSC_MODE_Y_TEST:
-    case TSC_MODE_TS_TEST:
-        if (s->dav)
-            s->enabled = 0;
-        break;
-
-    case TSC_MODE_RESERVED:
-    case TSC_MODE_XX_DRV:
-    case TSC_MODE_YY_DRV:
-    case TSC_MODE_YX_DRV:
-    default:
-        return;
-    }
-
-    if (!s->enabled || s->busy)
-        return;
-
-    s->busy = 1;
-    s->precision = s->nextprecision;
-    s->function = s->nextfunction;
-    s->pdst = !s->pnd0;        /* Synchronised on internal clock */
-    expires = qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() >> 7);
-    qemu_mod_timer(s->timer, expires);
-}
-
-static void tsc2005_reset(TSC2005State *s)
-{
-    s->state = 0;
-    s->pin_func = 0;
-    s->enabled = 0;
-    s->busy = 0;
-    s->nextprecision = 0;
-    s->nextfunction = 0;
-    s->timing[0] = 0;
-    s->timing[1] = 0;
-    s->irq = 0;
-    s->dav = 0;
-    s->reset = 0;
-    s->pdst = 1;
-    s->pnd0 = 0;
-    s->function = -1;
-    s->temp_thr[0] = 0x000;
-    s->temp_thr[1] = 0xfff;
-    s->aux_thr[0] = 0x000;
-    s->aux_thr[1] = 0xfff;
-
-    tsc2005_pin_update(s);
-}
-
-static uint8_t tsc2005_txrx_word(void *opaque, uint8_t value)
-{
-    TSC2005State *s = opaque;
-    uint32_t ret = 0;
-
-    switch (s->state ++) {
-    case 0:
-        if (value & 0x80) {
-            /* Command */
-            if (value & (1 << 1))
-                tsc2005_reset(s);
-            else {
-                s->nextfunction = (value >> 3) & 0xf;
-                s->nextprecision = (value >> 2) & 1;
-                if (s->enabled != !(value & 1)) {
-                    s->enabled = !(value & 1);
-                    fprintf(stderr, "%s: touchscreen sense %sabled\n",
-                                    __FUNCTION__, s->enabled ? "en" : "dis");
-                    if (s->busy && !s->enabled)
-                        qemu_del_timer(s->timer);
-                    s->busy &= s->enabled;
-                }
-                tsc2005_pin_update(s);
-            }
-
-            s->state = 0;
-        } else if (value) {
-            /* Data transfer */
-            s->reg = (value >> 3) & 0xf;
-            s->pnd0 = (value >> 1) & 1;
-            s->command = value & 1;
-
-            if (s->command) {
-                /* Read */
-                s->data = tsc2005_read(s, s->reg);
-                tsc2005_pin_update(s);
-            } else
-                s->data = 0;
-        } else
-            s->state = 0;
-        break;
-
-    case 1:
-        if (s->command)
-            ret = (s->data >> 8) & 0xff;
-        else
-            s->data |= value << 8;
-        break;
-
-    case 2:
-        if (s->command)
-            ret = s->data & 0xff;
-        else {
-            s->data |= value;
-            tsc2005_write(s, s->reg, s->data);
-            tsc2005_pin_update(s);
-        }
-
-        s->state = 0;
-        break;
-    }
-
-    return ret;
-}
-
-uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len)
-{
-    uint32_t ret = 0;
-
-    len &= ~7;
-    while (len > 0) {
-        len -= 8;
-        ret |= tsc2005_txrx_word(opaque, (value >> len) & 0xff) << len;
-    }
-
-    return ret;
-}
-
-static void tsc2005_timer_tick(void *opaque)
-{
-    TSC2005State *s = opaque;
-
-    /* Timer ticked -- a set of conversions has been finished.  */
-
-    if (!s->busy)
-        return;
-
-    s->busy = 0;
-    s->dav |= mode_regs[s->function];
-    s->function = -1;
-    tsc2005_pin_update(s);
-}
-
-static void tsc2005_touchscreen_event(void *opaque,
-                int x, int y, int z, int buttons_state)
-{
-    TSC2005State *s = opaque;
-    int p = s->pressure;
-
-    if (buttons_state) {
-        s->x = x;
-        s->y = y;
-    }
-    s->pressure = !!buttons_state;
-
-    /*
-     * Note: We would get better responsiveness in the guest by
-     * signaling TS events immediately, but for now we simulate
-     * the first conversion delay for sake of correctness.
-     */
-    if (p != s->pressure)
-        tsc2005_pin_update(s);
-}
-
-static void tsc2005_save(QEMUFile *f, void *opaque)
-{
-    TSC2005State *s = (TSC2005State *) opaque;
-    int i;
-
-    qemu_put_be16(f, s->x);
-    qemu_put_be16(f, s->y);
-    qemu_put_byte(f, s->pressure);
-
-    qemu_put_byte(f, s->state);
-    qemu_put_byte(f, s->reg);
-    qemu_put_byte(f, s->command);
-
-    qemu_put_byte(f, s->irq);
-    qemu_put_be16s(f, &s->dav);
-    qemu_put_be16s(f, &s->data);
-
-    qemu_put_timer(f, s->timer);
-    qemu_put_byte(f, s->enabled);
-    qemu_put_byte(f, s->host_mode);
-    qemu_put_byte(f, s->function);
-    qemu_put_byte(f, s->nextfunction);
-    qemu_put_byte(f, s->precision);
-    qemu_put_byte(f, s->nextprecision);
-    qemu_put_be16(f, s->filter);
-    qemu_put_byte(f, s->pin_func);
-    qemu_put_be16(f, s->timing[0]);
-    qemu_put_be16(f, s->timing[1]);
-    qemu_put_be16s(f, &s->temp_thr[0]);
-    qemu_put_be16s(f, &s->temp_thr[1]);
-    qemu_put_be16s(f, &s->aux_thr[0]);
-    qemu_put_be16s(f, &s->aux_thr[1]);
-    qemu_put_be32(f, s->noise);
-    qemu_put_byte(f, s->reset);
-    qemu_put_byte(f, s->pdst);
-    qemu_put_byte(f, s->pnd0);
-
-    for (i = 0; i < 8; i ++)
-        qemu_put_be32(f, s->tr[i]);
-}
-
-static int tsc2005_load(QEMUFile *f, void *opaque, int version_id)
-{
-    TSC2005State *s = (TSC2005State *) opaque;
-    int i;
-
-    s->x = qemu_get_be16(f);
-    s->y = qemu_get_be16(f);
-    s->pressure = qemu_get_byte(f);
-
-    s->state = qemu_get_byte(f);
-    s->reg = qemu_get_byte(f);
-    s->command = qemu_get_byte(f);
-
-    s->irq = qemu_get_byte(f);
-    qemu_get_be16s(f, &s->dav);
-    qemu_get_be16s(f, &s->data);
-
-    qemu_get_timer(f, s->timer);
-    s->enabled = qemu_get_byte(f);
-    s->host_mode = qemu_get_byte(f);
-    s->function = qemu_get_byte(f);
-    s->nextfunction = qemu_get_byte(f);
-    s->precision = qemu_get_byte(f);
-    s->nextprecision = qemu_get_byte(f);
-    s->filter = qemu_get_be16(f);
-    s->pin_func = qemu_get_byte(f);
-    s->timing[0] = qemu_get_be16(f);
-    s->timing[1] = qemu_get_be16(f);
-    qemu_get_be16s(f, &s->temp_thr[0]);
-    qemu_get_be16s(f, &s->temp_thr[1]);
-    qemu_get_be16s(f, &s->aux_thr[0]);
-    qemu_get_be16s(f, &s->aux_thr[1]);
-    s->noise = qemu_get_be32(f);
-    s->reset = qemu_get_byte(f);
-    s->pdst = qemu_get_byte(f);
-    s->pnd0 = qemu_get_byte(f);
-
-    for (i = 0; i < 8; i ++)
-        s->tr[i] = qemu_get_be32(f);
-
-    s->busy = qemu_timer_pending(s->timer);
-    tsc2005_pin_update(s);
-
-    return 0;
-}
-
-void *tsc2005_init(qemu_irq pintdav)
-{
-    TSC2005State *s;
-
-    s = (TSC2005State *)
-            g_malloc0(sizeof(TSC2005State));
-    s->x = 400;
-    s->y = 240;
-    s->pressure = 0;
-    s->precision = s->nextprecision = 0;
-    s->timer = qemu_new_timer_ns(vm_clock, tsc2005_timer_tick, s);
-    s->pint = pintdav;
-    s->model = 0x2005;
-
-    s->tr[0] = 0;
-    s->tr[1] = 1;
-    s->tr[2] = 1;
-    s->tr[3] = 0;
-    s->tr[4] = 1;
-    s->tr[5] = 0;
-    s->tr[6] = 1;
-    s->tr[7] = 0;
-
-    tsc2005_reset(s);
-
-    qemu_add_mouse_event_handler(tsc2005_touchscreen_event, s, 1,
-                    "QEMU TSC2005-driven Touchscreen");
-
-    qemu_register_reset((void *) tsc2005_reset, s);
-    register_savevm(NULL, "tsc2005", -1, 0, tsc2005_save, tsc2005_load, s);
-
-    return s;
-}
-
-/*
- * Use tslib generated calibration data to generate ADC input values
- * from the touchscreen.  Assuming 12-bit precision was used during
- * tslib calibration.
- */
-void tsc2005_set_transform(void *opaque, MouseTransformInfo *info)
-{
-    TSC2005State *s = (TSC2005State *) opaque;
-
-    /* This version assumes touchscreen X & Y axis are parallel or
-     * perpendicular to LCD's  X & Y axis in some way.  */
-    if (abs(info->a[0]) > abs(info->a[1])) {
-        s->tr[0] = 0;
-        s->tr[1] = -info->a[6] * info->x;
-        s->tr[2] = info->a[0];
-        s->tr[3] = -info->a[2] / info->a[0];
-        s->tr[4] = info->a[6] * info->y;
-        s->tr[5] = 0;
-        s->tr[6] = info->a[4];
-        s->tr[7] = -info->a[5] / info->a[4];
-    } else {
-        s->tr[0] = info->a[6] * info->y;
-        s->tr[1] = 0;
-        s->tr[2] = info->a[1];
-        s->tr[3] = -info->a[2] / info->a[1];
-        s->tr[4] = 0;
-        s->tr[5] = -info->a[6] * info->x;
-        s->tr[6] = info->a[3];
-        s->tr[7] = -info->a[5] / info->a[3];
-    }
-
-    s->tr[0] >>= 11;
-    s->tr[1] >>= 11;
-    s->tr[3] <<= 4;
-    s->tr[4] >>= 11;
-    s->tr[5] >>= 11;
-    s->tr[7] <<= 4;
-}
diff --git a/hw/twl92230.c b/hw/twl92230.c
deleted file mode 100644 (file)
index b730d85..0000000
+++ /dev/null
@@ -1,882 +0,0 @@
-/*
- * TI TWL92230C energy-management companion device for the OMAP24xx.
- * Aka. Menelaus (N4200 MENELAUS1_V2.2)
- *
- * Copyright (C) 2008 Nokia Corporation
- * Written by Andrzej Zaborowski <andrew@openedhand.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 or
- * (at your option) version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/hw.h"
-#include "qemu/timer.h"
-#include "hw/i2c/i2c.h"
-#include "sysemu/sysemu.h"
-#include "ui/console.h"
-
-#define VERBOSE 1
-
-typedef struct {
-    I2CSlave i2c;
-
-    int firstbyte;
-    uint8_t reg;
-
-    uint8_t vcore[5];
-    uint8_t dcdc[3];
-    uint8_t ldo[8];
-    uint8_t sleep[2];
-    uint8_t osc;
-    uint8_t detect;
-    uint16_t mask;
-    uint16_t status;
-    uint8_t dir;
-    uint8_t inputs;
-    uint8_t outputs;
-    uint8_t bbsms;
-    uint8_t pull[4];
-    uint8_t mmc_ctrl[3];
-    uint8_t mmc_debounce;
-    struct {
-        uint8_t ctrl;
-        uint16_t comp;
-        QEMUTimer *hz_tm;
-        int64_t next;
-        struct tm tm;
-        struct tm new;
-        struct tm alm;
-        int sec_offset;
-        int alm_sec;
-        int next_comp;
-    } rtc;
-    uint16_t rtc_next_vmstate;
-    qemu_irq out[4];
-    uint8_t pwrbtn_state;
-} MenelausState;
-
-static inline void menelaus_update(MenelausState *s)
-{
-    qemu_set_irq(s->out[3], s->status & ~s->mask);
-}
-
-static inline void menelaus_rtc_start(MenelausState *s)
-{
-    s->rtc.next += qemu_get_clock_ms(rtc_clock);
-    qemu_mod_timer(s->rtc.hz_tm, s->rtc.next);
-}
-
-static inline void menelaus_rtc_stop(MenelausState *s)
-{
-    qemu_del_timer(s->rtc.hz_tm);
-    s->rtc.next -= qemu_get_clock_ms(rtc_clock);
-    if (s->rtc.next < 1)
-        s->rtc.next = 1;
-}
-
-static void menelaus_rtc_update(MenelausState *s)
-{
-    qemu_get_timedate(&s->rtc.tm, s->rtc.sec_offset);
-}
-
-static void menelaus_alm_update(MenelausState *s)
-{
-    if ((s->rtc.ctrl & 3) == 3)
-        s->rtc.alm_sec = qemu_timedate_diff(&s->rtc.alm) - s->rtc.sec_offset;
-}
-
-static void menelaus_rtc_hz(void *opaque)
-{
-    MenelausState *s = (MenelausState *) opaque;
-
-    s->rtc.next_comp --;
-    s->rtc.alm_sec --;
-    s->rtc.next += 1000;
-    qemu_mod_timer(s->rtc.hz_tm, s->rtc.next);
-    if ((s->rtc.ctrl >> 3) & 3) {                              /* EVERY */
-        menelaus_rtc_update(s);
-        if (((s->rtc.ctrl >> 3) & 3) == 1 && !s->rtc.tm.tm_sec)
-            s->status |= 1 << 8;                               /* RTCTMR */
-        else if (((s->rtc.ctrl >> 3) & 3) == 2 && !s->rtc.tm.tm_min)
-            s->status |= 1 << 8;                               /* RTCTMR */
-        else if (!s->rtc.tm.tm_hour)
-            s->status |= 1 << 8;                               /* RTCTMR */
-    } else
-        s->status |= 1 << 8;                                   /* RTCTMR */
-    if ((s->rtc.ctrl >> 1) & 1) {                              /* RTC_AL_EN */
-        if (s->rtc.alm_sec == 0)
-            s->status |= 1 << 9;                               /* RTCALM */
-        /* TODO: wake-up */
-    }
-    if (s->rtc.next_comp <= 0) {
-        s->rtc.next -= muldiv64((int16_t) s->rtc.comp, 1000, 0x8000);
-        s->rtc.next_comp = 3600;
-    }
-    menelaus_update(s);
-}
-
-static void menelaus_reset(I2CSlave *i2c)
-{
-    MenelausState *s = (MenelausState *) i2c;
-    s->reg = 0x00;
-
-    s->vcore[0] = 0x0c;        /* XXX: X-loader needs 0x8c? check!  */
-    s->vcore[1] = 0x05;
-    s->vcore[2] = 0x02;
-    s->vcore[3] = 0x0c;
-    s->vcore[4] = 0x03;
-    s->dcdc[0] = 0x33; /* Depends on wiring */
-    s->dcdc[1] = 0x03;
-    s->dcdc[2] = 0x00;
-    s->ldo[0] = 0x95;
-    s->ldo[1] = 0x7e;
-    s->ldo[2] = 0x00;
-    s->ldo[3] = 0x00;  /* Depends on wiring */
-    s->ldo[4] = 0x03;  /* Depends on wiring */
-    s->ldo[5] = 0x00;
-    s->ldo[6] = 0x00;
-    s->ldo[7] = 0x00;
-    s->sleep[0] = 0x00;
-    s->sleep[1] = 0x00;
-    s->osc = 0x01;
-    s->detect = 0x09;
-    s->mask = 0x0fff;
-    s->status = 0;
-    s->dir = 0x07;
-    s->outputs = 0x00;
-    s->bbsms = 0x00;
-    s->pull[0] = 0x00;
-    s->pull[1] = 0x00;
-    s->pull[2] = 0x00;
-    s->pull[3] = 0x00;
-    s->mmc_ctrl[0] = 0x03;
-    s->mmc_ctrl[1] = 0xc0;
-    s->mmc_ctrl[2] = 0x00;
-    s->mmc_debounce = 0x05;
-
-    if (s->rtc.ctrl & 1)
-        menelaus_rtc_stop(s);
-    s->rtc.ctrl = 0x00;
-    s->rtc.comp = 0x0000;
-    s->rtc.next = 1000;
-    s->rtc.sec_offset = 0;
-    s->rtc.next_comp = 1800;
-    s->rtc.alm_sec = 1800;
-    s->rtc.alm.tm_sec = 0x00;
-    s->rtc.alm.tm_min = 0x00;
-    s->rtc.alm.tm_hour = 0x00;
-    s->rtc.alm.tm_mday = 0x01;
-    s->rtc.alm.tm_mon = 0x00;
-    s->rtc.alm.tm_year = 2004;
-    menelaus_update(s);
-}
-
-static void menelaus_gpio_set(void *opaque, int line, int level)
-{
-    MenelausState *s = (MenelausState *) opaque;
-
-    if (line < 3) {
-        /* No interrupt generated */
-        s->inputs &= ~(1 << line);
-        s->inputs |= level << line;
-        return;
-    }
-
-    if (!s->pwrbtn_state && level) {
-        s->status |= 1 << 11;                                  /* PSHBTN */
-        menelaus_update(s);
-    }
-    s->pwrbtn_state = level;
-}
-
-#define MENELAUS_REV           0x01
-#define MENELAUS_VCORE_CTRL1   0x02
-#define MENELAUS_VCORE_CTRL2   0x03
-#define MENELAUS_VCORE_CTRL3   0x04
-#define MENELAUS_VCORE_CTRL4   0x05
-#define MENELAUS_VCORE_CTRL5   0x06
-#define MENELAUS_DCDC_CTRL1    0x07
-#define MENELAUS_DCDC_CTRL2    0x08
-#define MENELAUS_DCDC_CTRL3    0x09
-#define MENELAUS_LDO_CTRL1     0x0a
-#define MENELAUS_LDO_CTRL2     0x0b
-#define MENELAUS_LDO_CTRL3     0x0c
-#define MENELAUS_LDO_CTRL4     0x0d
-#define MENELAUS_LDO_CTRL5     0x0e
-#define MENELAUS_LDO_CTRL6     0x0f
-#define MENELAUS_LDO_CTRL7     0x10
-#define MENELAUS_LDO_CTRL8     0x11
-#define MENELAUS_SLEEP_CTRL1   0x12
-#define MENELAUS_SLEEP_CTRL2   0x13
-#define MENELAUS_DEVICE_OFF    0x14
-#define MENELAUS_OSC_CTRL      0x15
-#define MENELAUS_DETECT_CTRL   0x16
-#define MENELAUS_INT_MASK1     0x17
-#define MENELAUS_INT_MASK2     0x18
-#define MENELAUS_INT_STATUS1   0x19
-#define MENELAUS_INT_STATUS2   0x1a
-#define MENELAUS_INT_ACK1      0x1b
-#define MENELAUS_INT_ACK2      0x1c
-#define MENELAUS_GPIO_CTRL     0x1d
-#define MENELAUS_GPIO_IN       0x1e
-#define MENELAUS_GPIO_OUT      0x1f
-#define MENELAUS_BBSMS         0x20
-#define MENELAUS_RTC_CTRL      0x21
-#define MENELAUS_RTC_UPDATE    0x22
-#define MENELAUS_RTC_SEC       0x23
-#define MENELAUS_RTC_MIN       0x24
-#define MENELAUS_RTC_HR                0x25
-#define MENELAUS_RTC_DAY       0x26
-#define MENELAUS_RTC_MON       0x27
-#define MENELAUS_RTC_YR                0x28
-#define MENELAUS_RTC_WKDAY     0x29
-#define MENELAUS_RTC_AL_SEC    0x2a
-#define MENELAUS_RTC_AL_MIN    0x2b
-#define MENELAUS_RTC_AL_HR     0x2c
-#define MENELAUS_RTC_AL_DAY    0x2d
-#define MENELAUS_RTC_AL_MON    0x2e
-#define MENELAUS_RTC_AL_YR     0x2f
-#define MENELAUS_RTC_COMP_MSB  0x30
-#define MENELAUS_RTC_COMP_LSB  0x31
-#define MENELAUS_S1_PULL_EN    0x32
-#define MENELAUS_S1_PULL_DIR   0x33
-#define MENELAUS_S2_PULL_EN    0x34
-#define MENELAUS_S2_PULL_DIR   0x35
-#define MENELAUS_MCT_CTRL1     0x36
-#define MENELAUS_MCT_CTRL2     0x37
-#define MENELAUS_MCT_CTRL3     0x38
-#define MENELAUS_MCT_PIN_ST    0x39
-#define MENELAUS_DEBOUNCE1     0x3a
-
-static uint8_t menelaus_read(void *opaque, uint8_t addr)
-{
-    MenelausState *s = (MenelausState *) opaque;
-    int reg = 0;
-
-    switch (addr) {
-    case MENELAUS_REV:
-        return 0x22;
-
-    case MENELAUS_VCORE_CTRL5: reg ++;
-    case MENELAUS_VCORE_CTRL4: reg ++;
-    case MENELAUS_VCORE_CTRL3: reg ++;
-    case MENELAUS_VCORE_CTRL2: reg ++;
-    case MENELAUS_VCORE_CTRL1:
-        return s->vcore[reg];
-
-    case MENELAUS_DCDC_CTRL3: reg ++;
-    case MENELAUS_DCDC_CTRL2: reg ++;
-    case MENELAUS_DCDC_CTRL1:
-        return s->dcdc[reg];
-
-    case MENELAUS_LDO_CTRL8: reg ++;
-    case MENELAUS_LDO_CTRL7: reg ++;
-    case MENELAUS_LDO_CTRL6: reg ++;
-    case MENELAUS_LDO_CTRL5: reg ++;
-    case MENELAUS_LDO_CTRL4: reg ++;
-    case MENELAUS_LDO_CTRL3: reg ++;
-    case MENELAUS_LDO_CTRL2: reg ++;
-    case MENELAUS_LDO_CTRL1:
-        return s->ldo[reg];
-
-    case MENELAUS_SLEEP_CTRL2: reg ++;
-    case MENELAUS_SLEEP_CTRL1:
-        return s->sleep[reg];
-
-    case MENELAUS_DEVICE_OFF:
-        return 0;
-
-    case MENELAUS_OSC_CTRL:
-        return s->osc | (1 << 7);                      /* CLK32K_GOOD */
-
-    case MENELAUS_DETECT_CTRL:
-        return s->detect;
-
-    case MENELAUS_INT_MASK1:
-        return (s->mask >> 0) & 0xff;
-    case MENELAUS_INT_MASK2:
-        return (s->mask >> 8) & 0xff;
-
-    case MENELAUS_INT_STATUS1:
-        return (s->status >> 0) & 0xff;
-    case MENELAUS_INT_STATUS2:
-        return (s->status >> 8) & 0xff;
-
-    case MENELAUS_INT_ACK1:
-    case MENELAUS_INT_ACK2:
-        return 0;
-
-    case MENELAUS_GPIO_CTRL:
-        return s->dir;
-    case MENELAUS_GPIO_IN:
-        return s->inputs | (~s->dir & s->outputs);
-    case MENELAUS_GPIO_OUT:
-        return s->outputs;
-
-    case MENELAUS_BBSMS:
-        return s->bbsms;
-
-    case MENELAUS_RTC_CTRL:
-        return s->rtc.ctrl;
-    case MENELAUS_RTC_UPDATE:
-        return 0x00;
-    case MENELAUS_RTC_SEC:
-        menelaus_rtc_update(s);
-        return to_bcd(s->rtc.tm.tm_sec);
-    case MENELAUS_RTC_MIN:
-        menelaus_rtc_update(s);
-        return to_bcd(s->rtc.tm.tm_min);
-    case MENELAUS_RTC_HR:
-        menelaus_rtc_update(s);
-        if ((s->rtc.ctrl >> 2) & 1)                    /* MODE12_n24 */
-            return to_bcd((s->rtc.tm.tm_hour % 12) + 1) |
-                    (!!(s->rtc.tm.tm_hour >= 12) << 7);        /* PM_nAM */
-        else
-            return to_bcd(s->rtc.tm.tm_hour);
-    case MENELAUS_RTC_DAY:
-        menelaus_rtc_update(s);
-        return to_bcd(s->rtc.tm.tm_mday);
-    case MENELAUS_RTC_MON:
-        menelaus_rtc_update(s);
-        return to_bcd(s->rtc.tm.tm_mon + 1);
-    case MENELAUS_RTC_YR:
-        menelaus_rtc_update(s);
-        return to_bcd(s->rtc.tm.tm_year - 2000);
-    case MENELAUS_RTC_WKDAY:
-        menelaus_rtc_update(s);
-        return to_bcd(s->rtc.tm.tm_wday);
-    case MENELAUS_RTC_AL_SEC:
-        return to_bcd(s->rtc.alm.tm_sec);
-    case MENELAUS_RTC_AL_MIN:
-        return to_bcd(s->rtc.alm.tm_min);
-    case MENELAUS_RTC_AL_HR:
-        if ((s->rtc.ctrl >> 2) & 1)                    /* MODE12_n24 */
-            return to_bcd((s->rtc.alm.tm_hour % 12) + 1) |
-                    (!!(s->rtc.alm.tm_hour >= 12) << 7);/* AL_PM_nAM */
-        else
-            return to_bcd(s->rtc.alm.tm_hour);
-    case MENELAUS_RTC_AL_DAY:
-        return to_bcd(s->rtc.alm.tm_mday);
-    case MENELAUS_RTC_AL_MON:
-        return to_bcd(s->rtc.alm.tm_mon + 1);
-    case MENELAUS_RTC_AL_YR:
-        return to_bcd(s->rtc.alm.tm_year - 2000);
-    case MENELAUS_RTC_COMP_MSB:
-        return (s->rtc.comp >> 8) & 0xff;
-    case MENELAUS_RTC_COMP_LSB:
-        return (s->rtc.comp >> 0) & 0xff;
-
-    case MENELAUS_S1_PULL_EN:
-        return s->pull[0];
-    case MENELAUS_S1_PULL_DIR:
-        return s->pull[1];
-    case MENELAUS_S2_PULL_EN:
-        return s->pull[2];
-    case MENELAUS_S2_PULL_DIR:
-        return s->pull[3];
-
-    case MENELAUS_MCT_CTRL3: reg ++;
-    case MENELAUS_MCT_CTRL2: reg ++;
-    case MENELAUS_MCT_CTRL1:
-        return s->mmc_ctrl[reg];
-    case MENELAUS_MCT_PIN_ST:
-        /* TODO: return the real Card Detect */
-        return 0;
-    case MENELAUS_DEBOUNCE1:
-        return s->mmc_debounce;
-
-    default:
-#ifdef VERBOSE
-        printf("%s: unknown register %02x\n", __FUNCTION__, addr);
-#endif
-        break;
-    }
-    return 0;
-}
-
-static void menelaus_write(void *opaque, uint8_t addr, uint8_t value)
-{
-    MenelausState *s = (MenelausState *) opaque;
-    int line;
-    int reg = 0;
-    struct tm tm;
-
-    switch (addr) {
-    case MENELAUS_VCORE_CTRL1:
-        s->vcore[0] = (value & 0xe) | MIN(value & 0x1f, 0x12);
-        break;
-    case MENELAUS_VCORE_CTRL2:
-        s->vcore[1] = value;
-        break;
-    case MENELAUS_VCORE_CTRL3:
-        s->vcore[2] = MIN(value & 0x1f, 0x12);
-        break;
-    case MENELAUS_VCORE_CTRL4:
-        s->vcore[3] = MIN(value & 0x1f, 0x12);
-        break;
-    case MENELAUS_VCORE_CTRL5:
-        s->vcore[4] = value & 3;
-        /* XXX
-         * auto set to 3 on M_Active, nRESWARM
-         * auto set to 0 on M_WaitOn, M_Backup
-         */
-        break;
-
-    case MENELAUS_DCDC_CTRL1:
-        s->dcdc[0] = value & 0x3f;
-        break;
-    case MENELAUS_DCDC_CTRL2:
-        s->dcdc[1] = value & 0x07;
-        /* XXX
-         * auto set to 3 on M_Active, nRESWARM
-         * auto set to 0 on M_WaitOn, M_Backup
-         */
-        break;
-    case MENELAUS_DCDC_CTRL3:
-        s->dcdc[2] = value & 0x07;
-        break;
-
-    case MENELAUS_LDO_CTRL1:
-        s->ldo[0] = value;
-        break;
-    case MENELAUS_LDO_CTRL2:
-        s->ldo[1] = value & 0x7f;
-        /* XXX
-         * auto set to 0x7e on M_WaitOn, M_Backup
-         */
-        break;
-    case MENELAUS_LDO_CTRL3:
-        s->ldo[2] = value & 3;
-        /* XXX
-         * auto set to 3 on M_Active, nRESWARM
-         * auto set to 0 on M_WaitOn, M_Backup
-         */
-        break;
-    case MENELAUS_LDO_CTRL4:
-        s->ldo[3] = value & 3;
-        /* XXX
-         * auto set to 3 on M_Active, nRESWARM
-         * auto set to 0 on M_WaitOn, M_Backup
-         */
-        break;
-    case MENELAUS_LDO_CTRL5:
-        s->ldo[4] = value & 3;
-        /* XXX
-         * auto set to 3 on M_Active, nRESWARM
-         * auto set to 0 on M_WaitOn, M_Backup
-         */
-        break;
-    case MENELAUS_LDO_CTRL6:
-        s->ldo[5] = value & 3;
-        break;
-    case MENELAUS_LDO_CTRL7:
-        s->ldo[6] = value & 3;
-        break;
-    case MENELAUS_LDO_CTRL8:
-        s->ldo[7] = value & 3;
-        break;
-
-    case MENELAUS_SLEEP_CTRL2: reg ++;
-    case MENELAUS_SLEEP_CTRL1:
-        s->sleep[reg] = value;
-        break;
-
-    case MENELAUS_DEVICE_OFF:
-        if (value & 1)
-            menelaus_reset(&s->i2c);
-        break;
-
-    case MENELAUS_OSC_CTRL:
-        s->osc = value & 7;
-        break;
-
-    case MENELAUS_DETECT_CTRL:
-        s->detect = value & 0x7f;
-        break;
-
-    case MENELAUS_INT_MASK1:
-        s->mask &= 0xf00;
-        s->mask |= value << 0;
-        menelaus_update(s);
-        break;
-    case MENELAUS_INT_MASK2:
-        s->mask &= 0x0ff;
-        s->mask |= value << 8;
-        menelaus_update(s);
-        break;
-
-    case MENELAUS_INT_ACK1:
-        s->status &= ~(((uint16_t) value) << 0);
-        menelaus_update(s);
-        break;
-    case MENELAUS_INT_ACK2:
-        s->status &= ~(((uint16_t) value) << 8);
-        menelaus_update(s);
-        break;
-
-    case MENELAUS_GPIO_CTRL:
-        for (line = 0; line < 3; line ++) {
-            if (((s->dir ^ value) >> line) & 1) {
-                qemu_set_irq(s->out[line],
-                             ((s->outputs & ~s->dir) >> line) & 1);
-            }
-        }
-        s->dir = value & 0x67;
-        break;
-    case MENELAUS_GPIO_OUT:
-        for (line = 0; line < 3; line ++) {
-            if ((((s->outputs ^ value) & ~s->dir) >> line) & 1) {
-                qemu_set_irq(s->out[line], (s->outputs >> line) & 1);
-            }
-        }
-        s->outputs = value & 0x07;
-        break;
-
-    case MENELAUS_BBSMS:
-        s->bbsms = 0x0d;
-        break;
-
-    case MENELAUS_RTC_CTRL:
-        if ((s->rtc.ctrl ^ value) & 1) {                       /* RTC_EN */
-            if (value & 1)
-                menelaus_rtc_start(s);
-            else
-                menelaus_rtc_stop(s);
-        }
-        s->rtc.ctrl = value & 0x1f;
-        menelaus_alm_update(s);
-        break;
-    case MENELAUS_RTC_UPDATE:
-        menelaus_rtc_update(s);
-        memcpy(&tm, &s->rtc.tm, sizeof(tm));
-        switch (value & 0xf) {
-        case 0:
-            break;
-        case 1:
-            tm.tm_sec = s->rtc.new.tm_sec;
-            break;
-        case 2:
-            tm.tm_min = s->rtc.new.tm_min;
-            break;
-        case 3:
-            if (s->rtc.new.tm_hour > 23)
-                goto rtc_badness;
-            tm.tm_hour = s->rtc.new.tm_hour;
-            break;
-        case 4:
-            if (s->rtc.new.tm_mday < 1)
-                goto rtc_badness;
-            /* TODO check range */
-            tm.tm_mday = s->rtc.new.tm_mday;
-            break;
-        case 5:
-            if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11)
-                goto rtc_badness;
-            tm.tm_mon = s->rtc.new.tm_mon;
-            break;
-        case 6:
-            tm.tm_year = s->rtc.new.tm_year;
-            break;
-        case 7:
-            /* TODO set .tm_mday instead */
-            tm.tm_wday = s->rtc.new.tm_wday;
-            break;
-        case 8:
-            if (s->rtc.new.tm_hour > 23)
-                goto rtc_badness;
-            if (s->rtc.new.tm_mday < 1)
-                goto rtc_badness;
-            if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11)
-                goto rtc_badness;
-            tm.tm_sec = s->rtc.new.tm_sec;
-            tm.tm_min = s->rtc.new.tm_min;
-            tm.tm_hour = s->rtc.new.tm_hour;
-            tm.tm_mday = s->rtc.new.tm_mday;
-            tm.tm_mon = s->rtc.new.tm_mon;
-            tm.tm_year = s->rtc.new.tm_year;
-            break;
-        rtc_badness:
-        default:
-            fprintf(stderr, "%s: bad RTC_UPDATE value %02x\n",
-                            __FUNCTION__, value);
-            s->status |= 1 << 10;                              /* RTCERR */
-            menelaus_update(s);
-        }
-        s->rtc.sec_offset = qemu_timedate_diff(&tm);
-        break;
-    case MENELAUS_RTC_SEC:
-        s->rtc.tm.tm_sec = from_bcd(value & 0x7f);
-        break;
-    case MENELAUS_RTC_MIN:
-        s->rtc.tm.tm_min = from_bcd(value & 0x7f);
-        break;
-    case MENELAUS_RTC_HR:
-        s->rtc.tm.tm_hour = (s->rtc.ctrl & (1 << 2)) ? /* MODE12_n24 */
-                MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) :
-                from_bcd(value & 0x3f);
-        break;
-    case MENELAUS_RTC_DAY:
-        s->rtc.tm.tm_mday = from_bcd(value);
-        break;
-    case MENELAUS_RTC_MON:
-        s->rtc.tm.tm_mon = MAX(1, from_bcd(value)) - 1;
-        break;
-    case MENELAUS_RTC_YR:
-        s->rtc.tm.tm_year = 2000 + from_bcd(value);
-        break;
-    case MENELAUS_RTC_WKDAY:
-        s->rtc.tm.tm_mday = from_bcd(value);
-        break;
-    case MENELAUS_RTC_AL_SEC:
-        s->rtc.alm.tm_sec = from_bcd(value & 0x7f);
-        menelaus_alm_update(s);
-        break;
-    case MENELAUS_RTC_AL_MIN:
-        s->rtc.alm.tm_min = from_bcd(value & 0x7f);
-        menelaus_alm_update(s);
-        break;
-    case MENELAUS_RTC_AL_HR:
-        s->rtc.alm.tm_hour = (s->rtc.ctrl & (1 << 2)) ?        /* MODE12_n24 */
-                MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) :
-                from_bcd(value & 0x3f);
-        menelaus_alm_update(s);
-        break;
-    case MENELAUS_RTC_AL_DAY:
-        s->rtc.alm.tm_mday = from_bcd(value);
-        menelaus_alm_update(s);
-        break;
-    case MENELAUS_RTC_AL_MON:
-        s->rtc.alm.tm_mon = MAX(1, from_bcd(value)) - 1;
-        menelaus_alm_update(s);
-        break;
-    case MENELAUS_RTC_AL_YR:
-        s->rtc.alm.tm_year = 2000 + from_bcd(value);
-        menelaus_alm_update(s);
-        break;
-    case MENELAUS_RTC_COMP_MSB:
-        s->rtc.comp &= 0xff;
-        s->rtc.comp |= value << 8;
-        break;
-    case MENELAUS_RTC_COMP_LSB:
-        s->rtc.comp &= 0xff << 8;
-        s->rtc.comp |= value;
-        break;
-
-    case MENELAUS_S1_PULL_EN:
-        s->pull[0] = value;
-        break;
-    case MENELAUS_S1_PULL_DIR:
-        s->pull[1] = value & 0x1f;
-        break;
-    case MENELAUS_S2_PULL_EN:
-        s->pull[2] = value;
-        break;
-    case MENELAUS_S2_PULL_DIR:
-        s->pull[3] = value & 0x1f;
-        break;
-
-    case MENELAUS_MCT_CTRL1:
-        s->mmc_ctrl[0] = value & 0x7f;
-        break;
-    case MENELAUS_MCT_CTRL2:
-        s->mmc_ctrl[1] = value;
-        /* TODO update Card Detect interrupts */
-        break;
-    case MENELAUS_MCT_CTRL3:
-        s->mmc_ctrl[2] = value & 0xf;
-        break;
-    case MENELAUS_DEBOUNCE1:
-        s->mmc_debounce = value & 0x3f;
-        break;
-
-    default:
-#ifdef VERBOSE
-        printf("%s: unknown register %02x\n", __FUNCTION__, addr);
-#endif
-    }
-}
-
-static void menelaus_event(I2CSlave *i2c, enum i2c_event event)
-{
-    MenelausState *s = (MenelausState *) i2c;
-
-    if (event == I2C_START_SEND)
-        s->firstbyte = 1;
-}
-
-static int menelaus_tx(I2CSlave *i2c, uint8_t data)
-{
-    MenelausState *s = (MenelausState *) i2c;
-    /* Interpret register address byte */
-    if (s->firstbyte) {
-        s->reg = data;
-        s->firstbyte = 0;
-    } else
-        menelaus_write(s, s->reg ++, data);
-
-    return 0;
-}
-
-static int menelaus_rx(I2CSlave *i2c)
-{
-    MenelausState *s = (MenelausState *) i2c;
-
-    return menelaus_read(s, s->reg ++);
-}
-
-/* Save restore 32 bit int as uint16_t
-   This is a Big hack, but it is how the old state did it.
-   Or we broke compatibility in the state, or we can't use struct tm
- */
-
-static int get_int32_as_uint16(QEMUFile *f, void *pv, size_t size)
-{
-    int *v = pv;
-    *v = qemu_get_be16(f);
-    return 0;
-}
-
-static void put_int32_as_uint16(QEMUFile *f, void *pv, size_t size)
-{
-    int *v = pv;
-    qemu_put_be16(f, *v);
-}
-
-static const VMStateInfo vmstate_hack_int32_as_uint16 = {
-    .name = "int32_as_uint16",
-    .get  = get_int32_as_uint16,
-    .put  = put_int32_as_uint16,
-};
-
-#define VMSTATE_UINT16_HACK(_f, _s)                                  \
-    VMSTATE_SINGLE(_f, _s, 0, vmstate_hack_int32_as_uint16, int32_t)
-
-
-static const VMStateDescription vmstate_menelaus_tm = {
-    .name = "menelaus_tm",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT16_HACK(tm_sec, struct tm),
-        VMSTATE_UINT16_HACK(tm_min, struct tm),
-        VMSTATE_UINT16_HACK(tm_hour, struct tm),
-        VMSTATE_UINT16_HACK(tm_mday, struct tm),
-        VMSTATE_UINT16_HACK(tm_min, struct tm),
-        VMSTATE_UINT16_HACK(tm_year, struct tm),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void menelaus_pre_save(void *opaque)
-{
-    MenelausState *s = opaque;
-    /* Should be <= 1000 */
-    s->rtc_next_vmstate =  s->rtc.next - qemu_get_clock_ms(rtc_clock);
-}
-
-static int menelaus_post_load(void *opaque, int version_id)
-{
-    MenelausState *s = opaque;
-
-    if (s->rtc.ctrl & 1)                                       /* RTC_EN */
-        menelaus_rtc_stop(s);
-
-    s->rtc.next = s->rtc_next_vmstate;
-
-    menelaus_alm_update(s);
-    menelaus_update(s);
-    if (s->rtc.ctrl & 1)                                       /* RTC_EN */
-        menelaus_rtc_start(s);
-    return 0;
-}
-
-static const VMStateDescription vmstate_menelaus = {
-    .name = "menelaus",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .pre_save = menelaus_pre_save,
-    .post_load = menelaus_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_INT32(firstbyte, MenelausState),
-        VMSTATE_UINT8(reg, MenelausState),
-        VMSTATE_UINT8_ARRAY(vcore, MenelausState, 5),
-        VMSTATE_UINT8_ARRAY(dcdc, MenelausState, 3),
-        VMSTATE_UINT8_ARRAY(ldo, MenelausState, 8),
-        VMSTATE_UINT8_ARRAY(sleep, MenelausState, 2),
-        VMSTATE_UINT8(osc, MenelausState),
-        VMSTATE_UINT8(detect, MenelausState),
-        VMSTATE_UINT16(mask, MenelausState),
-        VMSTATE_UINT16(status, MenelausState),
-        VMSTATE_UINT8(dir, MenelausState),
-        VMSTATE_UINT8(inputs, MenelausState),
-        VMSTATE_UINT8(outputs, MenelausState),
-        VMSTATE_UINT8(bbsms, MenelausState),
-        VMSTATE_UINT8_ARRAY(pull, MenelausState, 4),
-        VMSTATE_UINT8_ARRAY(mmc_ctrl, MenelausState, 3),
-        VMSTATE_UINT8(mmc_debounce, MenelausState),
-        VMSTATE_UINT8(rtc.ctrl, MenelausState),
-        VMSTATE_UINT16(rtc.comp, MenelausState),
-        VMSTATE_UINT16(rtc_next_vmstate, MenelausState),
-        VMSTATE_STRUCT(rtc.new, MenelausState, 0, vmstate_menelaus_tm,
-                       struct tm),
-        VMSTATE_STRUCT(rtc.alm, MenelausState, 0, vmstate_menelaus_tm,
-                       struct tm),
-        VMSTATE_UINT8(pwrbtn_state, MenelausState),
-        VMSTATE_I2C_SLAVE(i2c, MenelausState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int twl92230_init(I2CSlave *i2c)
-{
-    MenelausState *s = FROM_I2C_SLAVE(MenelausState, i2c);
-
-    s->rtc.hz_tm = qemu_new_timer_ms(rtc_clock, menelaus_rtc_hz, s);
-    /* Three output pins plus one interrupt pin.  */
-    qdev_init_gpio_out(&i2c->qdev, s->out, 4);
-
-    /* Three input pins plus one power-button pin.  */
-    qdev_init_gpio_in(&i2c->qdev, menelaus_gpio_set, 4);
-
-    menelaus_reset(&s->i2c);
-
-    return 0;
-}
-
-static void twl92230_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
-
-    sc->init = twl92230_init;
-    sc->event = menelaus_event;
-    sc->recv = menelaus_rx;
-    sc->send = menelaus_tx;
-    dc->vmsd = &vmstate_menelaus;
-}
-
-static const TypeInfo twl92230_info = {
-    .name          = "twl92230",
-    .parent        = TYPE_I2C_SLAVE,
-    .instance_size = sizeof(MenelausState),
-    .class_init    = twl92230_class_init,
-};
-
-static void twl92230_register_types(void)
-{
-    type_register_static(&twl92230_info);
-}
-
-type_init(twl92230_register_types)
diff --git a/hw/unin_pci.c b/hw/unin_pci.c
deleted file mode 100644 (file)
index fff235d..0000000
+++ /dev/null
@@ -1,492 +0,0 @@
-/*
- * QEMU Uninorth PCI host (for all Mac99 and newer machines)
- *
- * Copyright (c) 2006 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/ppc/mac.h"
-#include "hw/pci/pci.h"
-#include "hw/pci/pci_host.h"
-
-/* debug UniNorth */
-//#define DEBUG_UNIN
-
-#ifdef DEBUG_UNIN
-#define UNIN_DPRINTF(fmt, ...)                                  \
-    do { printf("UNIN: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define UNIN_DPRINTF(fmt, ...)
-#endif
-
-static const int unin_irq_line[] = { 0x1b, 0x1c, 0x1d, 0x1e };
-
-#define TYPE_UNI_NORTH_PCI_HOST_BRIDGE "uni-north-pci-pcihost"
-#define TYPE_UNI_NORTH_AGP_HOST_BRIDGE "uni-north-agp-pcihost"
-#define TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE "uni-north-internal-pci-pcihost"
-#define TYPE_U3_AGP_HOST_BRIDGE "u3-agp-pcihost"
-
-#define UNI_NORTH_PCI_HOST_BRIDGE(obj) \
-    OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_PCI_HOST_BRIDGE)
-#define UNI_NORTH_AGP_HOST_BRIDGE(obj) \
-    OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_AGP_HOST_BRIDGE)
-#define UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(obj) \
-    OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE)
-#define U3_AGP_HOST_BRIDGE(obj) \
-    OBJECT_CHECK(UNINState, (obj), TYPE_U3_AGP_HOST_BRIDGE)
-
-typedef struct UNINState {
-    PCIHostState parent_obj;
-
-    MemoryRegion pci_mmio;
-    MemoryRegion pci_hole;
-} UNINState;
-
-static int pci_unin_map_irq(PCIDevice *pci_dev, int irq_num)
-{
-    int retval;
-    int devfn = pci_dev->devfn & 0x00FFFFFF;
-
-    retval = (((devfn >> 11) & 0x1F) + irq_num) & 3;
-
-    return retval;
-}
-
-static void pci_unin_set_irq(void *opaque, int irq_num, int level)
-{
-    qemu_irq *pic = opaque;
-
-    UNIN_DPRINTF("%s: setting INT %d = %d\n", __func__,
-                 unin_irq_line[irq_num], level);
-    qemu_set_irq(pic[unin_irq_line[irq_num]], level);
-}
-
-static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr)
-{
-    uint32_t retval;
-
-    if (reg & (1u << 31)) {
-        /* XXX OpenBIOS compatibility hack */
-        retval = reg | (addr & 3);
-    } else if (reg & 1) {
-        /* CFA1 style */
-        retval = (reg & ~7u) | (addr & 7);
-    } else {
-        uint32_t slot, func;
-
-        /* Grab CFA0 style values */
-        slot = ffs(reg & 0xfffff800) - 1;
-        func = (reg >> 8) & 7;
-
-        /* ... and then convert them to x86 format */
-        /* config pointer */
-        retval = (reg & (0xff - 7)) | (addr & 7);
-        /* slot */
-        retval |= slot << 11;
-        /* fn */
-        retval |= func << 8;
-    }
-
-
-    UNIN_DPRINTF("Converted config space accessor %08x/%08x -> %08x\n",
-                 reg, addr, retval);
-
-    return retval;
-}
-
-static void unin_data_write(void *opaque, hwaddr addr,
-                            uint64_t val, unsigned len)
-{
-    UNINState *s = opaque;
-    PCIHostState *phb = PCI_HOST_BRIDGE(s);
-    UNIN_DPRINTF("write addr %" TARGET_FMT_plx " len %d val %"PRIx64"\n",
-                 addr, len, val);
-    pci_data_write(phb->bus,
-                   unin_get_config_reg(phb->config_reg, addr),
-                   val, len);
-}
-
-static uint64_t unin_data_read(void *opaque, hwaddr addr,
-                               unsigned len)
-{
-    UNINState *s = opaque;
-    PCIHostState *phb = PCI_HOST_BRIDGE(s);
-    uint32_t val;
-
-    val = pci_data_read(phb->bus,
-                        unin_get_config_reg(phb->config_reg, addr),
-                        len);
-    UNIN_DPRINTF("read addr %" TARGET_FMT_plx " len %d val %x\n",
-                 addr, len, val);
-    return val;
-}
-
-static const MemoryRegionOps unin_data_ops = {
-    .read = unin_data_read,
-    .write = unin_data_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static int pci_unin_main_init_device(SysBusDevice *dev)
-{
-    PCIHostState *h;
-
-    /* Use values found on a real PowerMac */
-    /* Uninorth main bus */
-    h = PCI_HOST_BRIDGE(dev);
-
-    memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops,
-                          dev, "pci-conf-idx", 0x1000);
-    memory_region_init_io(&h->data_mem, &unin_data_ops, dev,
-                          "pci-conf-data", 0x1000);
-    sysbus_init_mmio(dev, &h->conf_mem);
-    sysbus_init_mmio(dev, &h->data_mem);
-
-    return 0;
-}
-
-
-static int pci_u3_agp_init_device(SysBusDevice *dev)
-{
-    PCIHostState *h;
-
-    /* Uninorth U3 AGP bus */
-    h = PCI_HOST_BRIDGE(dev);
-
-    memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops,
-                          dev, "pci-conf-idx", 0x1000);
-    memory_region_init_io(&h->data_mem, &unin_data_ops, dev,
-                          "pci-conf-data", 0x1000);
-    sysbus_init_mmio(dev, &h->conf_mem);
-    sysbus_init_mmio(dev, &h->data_mem);
-
-    return 0;
-}
-
-static int pci_unin_agp_init_device(SysBusDevice *dev)
-{
-    PCIHostState *h;
-
-    /* Uninorth AGP bus */
-    h = PCI_HOST_BRIDGE(dev);
-
-    memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops,
-                          dev, "pci-conf-idx", 0x1000);
-    memory_region_init_io(&h->data_mem, &pci_host_data_le_ops,
-                          dev, "pci-conf-data", 0x1000);
-    sysbus_init_mmio(dev, &h->conf_mem);
-    sysbus_init_mmio(dev, &h->data_mem);
-    return 0;
-}
-
-static int pci_unin_internal_init_device(SysBusDevice *dev)
-{
-    PCIHostState *h;
-
-    /* Uninorth internal bus */
-    h = PCI_HOST_BRIDGE(dev);
-
-    memory_region_init_io(&h->conf_mem, &pci_host_conf_le_ops,
-                          dev, "pci-conf-idx", 0x1000);
-    memory_region_init_io(&h->data_mem, &pci_host_data_le_ops,
-                          dev, "pci-conf-data", 0x1000);
-    sysbus_init_mmio(dev, &h->conf_mem);
-    sysbus_init_mmio(dev, &h->data_mem);
-    return 0;
-}
-
-PCIBus *pci_pmac_init(qemu_irq *pic,
-                      MemoryRegion *address_space_mem,
-                      MemoryRegion *address_space_io)
-{
-    DeviceState *dev;
-    SysBusDevice *s;
-    PCIHostState *h;
-    UNINState *d;
-
-    /* Use values found on a real PowerMac */
-    /* Uninorth main bus */
-    dev = qdev_create(NULL, TYPE_UNI_NORTH_PCI_HOST_BRIDGE);
-    qdev_init_nofail(dev);
-    s = SYS_BUS_DEVICE(dev);
-    h = PCI_HOST_BRIDGE(s);
-    d = UNI_NORTH_PCI_HOST_BRIDGE(dev);
-    memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL);
-    memory_region_init_alias(&d->pci_hole, "pci-hole", &d->pci_mmio,
-                             0x80000000ULL, 0x70000000ULL);
-    memory_region_add_subregion(address_space_mem, 0x80000000ULL,
-                                &d->pci_hole);
-
-    h->bus = pci_register_bus(dev, "pci",
-                              pci_unin_set_irq, pci_unin_map_irq,
-                              pic,
-                              &d->pci_mmio,
-                              address_space_io,
-                              PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS);
-
-#if 0
-    pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north");
-#endif
-
-    sysbus_mmio_map(s, 0, 0xf2800000);
-    sysbus_mmio_map(s, 1, 0xf2c00000);
-
-    /* DEC 21154 bridge */
-#if 0
-    /* XXX: not activated as PPC BIOS doesn't handle multiple buses properly */
-    pci_create_simple(h->bus, PCI_DEVFN(12, 0), "dec-21154");
-#endif
-
-    /* Uninorth AGP bus */
-    pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-agp");
-    dev = qdev_create(NULL, TYPE_UNI_NORTH_AGP_HOST_BRIDGE);
-    qdev_init_nofail(dev);
-    s = SYS_BUS_DEVICE(dev);
-    sysbus_mmio_map(s, 0, 0xf0800000);
-    sysbus_mmio_map(s, 1, 0xf0c00000);
-
-    /* Uninorth internal bus */
-#if 0
-    /* XXX: not needed for now */
-    pci_create_simple(h->bus, PCI_DEVFN(14, 0),
-                      "uni-north-internal-pci");
-    dev = qdev_create(NULL, TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE);
-    qdev_init_nofail(dev);
-    s = SYS_BUS_DEVICE(dev);
-    sysbus_mmio_map(s, 0, 0xf4800000);
-    sysbus_mmio_map(s, 1, 0xf4c00000);
-#endif
-
-    return h->bus;
-}
-
-PCIBus *pci_pmac_u3_init(qemu_irq *pic,
-                         MemoryRegion *address_space_mem,
-                         MemoryRegion *address_space_io)
-{
-    DeviceState *dev;
-    SysBusDevice *s;
-    PCIHostState *h;
-    UNINState *d;
-
-    /* Uninorth AGP bus */
-
-    dev = qdev_create(NULL, TYPE_U3_AGP_HOST_BRIDGE);
-    qdev_init_nofail(dev);
-    s = SYS_BUS_DEVICE(dev);
-    h = PCI_HOST_BRIDGE(dev);
-    d = U3_AGP_HOST_BRIDGE(dev);
-
-    memory_region_init(&d->pci_mmio, "pci-mmio", 0x100000000ULL);
-    memory_region_init_alias(&d->pci_hole, "pci-hole", &d->pci_mmio,
-                             0x80000000ULL, 0x70000000ULL);
-    memory_region_add_subregion(address_space_mem, 0x80000000ULL,
-                                &d->pci_hole);
-
-    h->bus = pci_register_bus(dev, "pci",
-                              pci_unin_set_irq, pci_unin_map_irq,
-                              pic,
-                              &d->pci_mmio,
-                              address_space_io,
-                              PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS);
-
-    sysbus_mmio_map(s, 0, 0xf0800000);
-    sysbus_mmio_map(s, 1, 0xf0c00000);
-
-    pci_create_simple(h->bus, 11 << 3, "u3-agp");
-
-    return h->bus;
-}
-
-static int unin_main_pci_host_init(PCIDevice *d)
-{
-    d->config[0x0C] = 0x08; // cache_line_size
-    d->config[0x0D] = 0x10; // latency_timer
-    d->config[0x34] = 0x00; // capabilities_pointer
-    return 0;
-}
-
-static int unin_agp_pci_host_init(PCIDevice *d)
-{
-    d->config[0x0C] = 0x08; // cache_line_size
-    d->config[0x0D] = 0x10; // latency_timer
-    //    d->config[0x34] = 0x80; // capabilities_pointer
-    return 0;
-}
-
-static int u3_agp_pci_host_init(PCIDevice *d)
-{
-    /* cache line size */
-    d->config[0x0C] = 0x08;
-    /* latency timer */
-    d->config[0x0D] = 0x10;
-    return 0;
-}
-
-static int unin_internal_pci_host_init(PCIDevice *d)
-{
-    d->config[0x0C] = 0x08; // cache_line_size
-    d->config[0x0D] = 0x10; // latency_timer
-    d->config[0x34] = 0x00; // capabilities_pointer
-    return 0;
-}
-
-static void unin_main_pci_host_class_init(ObjectClass *klass, void *data)
-{
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init      = unin_main_pci_host_init;
-    k->vendor_id = PCI_VENDOR_ID_APPLE;
-    k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_PCI;
-    k->revision  = 0x00;
-    k->class_id  = PCI_CLASS_BRIDGE_HOST;
-}
-
-static const TypeInfo unin_main_pci_host_info = {
-    .name = "uni-north-pci",
-    .parent = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIDevice),
-    .class_init = unin_main_pci_host_class_init,
-};
-
-static void u3_agp_pci_host_class_init(ObjectClass *klass, void *data)
-{
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init      = u3_agp_pci_host_init;
-    k->vendor_id = PCI_VENDOR_ID_APPLE;
-    k->device_id = PCI_DEVICE_ID_APPLE_U3_AGP;
-    k->revision  = 0x00;
-    k->class_id  = PCI_CLASS_BRIDGE_HOST;
-}
-
-static const TypeInfo u3_agp_pci_host_info = {
-    .name = "u3-agp",
-    .parent = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIDevice),
-    .class_init = u3_agp_pci_host_class_init,
-};
-
-static void unin_agp_pci_host_class_init(ObjectClass *klass, void *data)
-{
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init      = unin_agp_pci_host_init;
-    k->vendor_id = PCI_VENDOR_ID_APPLE;
-    k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_AGP;
-    k->revision  = 0x00;
-    k->class_id  = PCI_CLASS_BRIDGE_HOST;
-}
-
-static const TypeInfo unin_agp_pci_host_info = {
-    .name = "uni-north-agp",
-    .parent = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIDevice),
-    .class_init = unin_agp_pci_host_class_init,
-};
-
-static void unin_internal_pci_host_class_init(ObjectClass *klass, void *data)
-{
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init      = unin_internal_pci_host_init;
-    k->vendor_id = PCI_VENDOR_ID_APPLE;
-    k->device_id = PCI_DEVICE_ID_APPLE_UNI_N_I_PCI;
-    k->revision  = 0x00;
-    k->class_id  = PCI_CLASS_BRIDGE_HOST;
-}
-
-static const TypeInfo unin_internal_pci_host_info = {
-    .name = "uni-north-internal-pci",
-    .parent = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIDevice),
-    .class_init = unin_internal_pci_host_class_init,
-};
-
-static void pci_unin_main_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sbc->init = pci_unin_main_init_device;
-}
-
-static const TypeInfo pci_unin_main_info = {
-    .name          = TYPE_UNI_NORTH_PCI_HOST_BRIDGE,
-    .parent        = TYPE_PCI_HOST_BRIDGE,
-    .instance_size = sizeof(UNINState),
-    .class_init    = pci_unin_main_class_init,
-};
-
-static void pci_u3_agp_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sbc->init = pci_u3_agp_init_device;
-}
-
-static const TypeInfo pci_u3_agp_info = {
-    .name          = TYPE_U3_AGP_HOST_BRIDGE,
-    .parent        = TYPE_PCI_HOST_BRIDGE,
-    .instance_size = sizeof(UNINState),
-    .class_init    = pci_u3_agp_class_init,
-};
-
-static void pci_unin_agp_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sbc->init = pci_unin_agp_init_device;
-}
-
-static const TypeInfo pci_unin_agp_info = {
-    .name          = TYPE_UNI_NORTH_AGP_HOST_BRIDGE,
-    .parent        = TYPE_PCI_HOST_BRIDGE,
-    .instance_size = sizeof(UNINState),
-    .class_init    = pci_unin_agp_class_init,
-};
-
-static void pci_unin_internal_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sbc->init = pci_unin_internal_init_device;
-}
-
-static const TypeInfo pci_unin_internal_info = {
-    .name          = TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE,
-    .parent        = TYPE_PCI_HOST_BRIDGE,
-    .instance_size = sizeof(UNINState),
-    .class_init    = pci_unin_internal_class_init,
-};
-
-static void unin_register_types(void)
-{
-    type_register_static(&unin_main_pci_host_info);
-    type_register_static(&u3_agp_pci_host_info);
-    type_register_static(&unin_agp_pci_host_info);
-    type_register_static(&unin_internal_pci_host_info);
-
-    type_register_static(&pci_unin_main_info);
-    type_register_static(&pci_u3_agp_info);
-    type_register_static(&pci_unin_agp_info);
-    type_register_static(&pci_unin_internal_info);
-}
-
-type_init(unin_register_types)
index e63e287ce0db32d46246c09a3f890af674af978b..5c2064422e093637864fc81e14d0934badbf0e62 100644 (file)
@@ -21,7 +21,12 @@ common-obj-$(CONFIG_USB_NETWORK)      += dev-network.o
 # FIXME: make configurable too
 CONFIG_USB_BLUETOOTH := y
 common-obj-$(CONFIG_USB_BLUETOOTH)    += dev-bluetooth.o
-common-obj-$(CONFIG_USB_SMARTCARD)    += dev-smartcard-reader.o
+
+ifeq ($(CONFIG_USB_SMARTCARD),y)
+common-obj-y                          += dev-smartcard-reader.o
+common-obj-y                          += ccid-card-passthru.o
+common-obj-$(CONFIG_SMARTCARD_NSS)    += ccid-card-emulated.o
+endif
 
 # usb redirection
 common-obj-$(CONFIG_USB_REDIR) += redirect.o quirks.o
diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c
new file mode 100644 (file)
index 0000000..c8f8ba3
--- /dev/null
@@ -0,0 +1,602 @@
+/*
+ * CCID Card Device. Emulated card.
+ *
+ * Copyright (c) 2011 Red Hat.
+ * Written by Alon Levy.
+ *
+ * This code is licensed under the GNU LGPL, version 2 or later.
+ */
+
+/*
+ * It can be used to provide access to the local hardware in a non exclusive
+ * way, or it can use certificates. It requires the usb-ccid bus.
+ *
+ * Usage 1: standard, mirror hardware reader+card:
+ * qemu .. -usb -device usb-ccid -device ccid-card-emulated
+ *
+ * Usage 2: use certificates, no hardware required
+ * one time: create the certificates:
+ *  for i in 1 2 3; do
+ *      certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=user$i" -n user$i
+ *  done
+ * qemu .. -usb -device usb-ccid \
+ *  -device ccid-card-emulated,cert1=user1,cert2=user2,cert3=user3
+ *
+ * If you use a non default db for the certificates you can specify it using
+ * the db parameter.
+ */
+
+#include <eventt.h>
+#include <vevent.h>
+#include <vreader.h>
+#include <vcard_emul.h>
+
+#include "qemu/thread.h"
+#include "char/char.h"
+#include "monitor/monitor.h"
+#include "hw/ccid.h"
+
+#define DPRINTF(card, lvl, fmt, ...) \
+do {\
+    if (lvl <= card->debug) {\
+        printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__);\
+    } \
+} while (0)
+
+#define EMULATED_DEV_NAME "ccid-card-emulated"
+
+#define BACKEND_NSS_EMULATED_NAME "nss-emulated"
+#define BACKEND_CERTIFICATES_NAME "certificates"
+
+enum {
+    BACKEND_NSS_EMULATED = 1,
+    BACKEND_CERTIFICATES
+};
+
+#define DEFAULT_BACKEND BACKEND_NSS_EMULATED
+
+typedef struct EmulatedState EmulatedState;
+
+enum {
+    EMUL_READER_INSERT = 0,
+    EMUL_READER_REMOVE,
+    EMUL_CARD_INSERT,
+    EMUL_CARD_REMOVE,
+    EMUL_GUEST_APDU,
+    EMUL_RESPONSE_APDU,
+    EMUL_ERROR,
+};
+
+static const char *emul_event_to_string(uint32_t emul_event)
+{
+    switch (emul_event) {
+    case EMUL_READER_INSERT:
+        return "EMUL_READER_INSERT";
+    case EMUL_READER_REMOVE:
+        return "EMUL_READER_REMOVE";
+    case EMUL_CARD_INSERT:
+        return "EMUL_CARD_INSERT";
+    case EMUL_CARD_REMOVE:
+        return "EMUL_CARD_REMOVE";
+    case EMUL_GUEST_APDU:
+        return "EMUL_GUEST_APDU";
+    case EMUL_RESPONSE_APDU:
+        return "EMUL_RESPONSE_APDU";
+    case EMUL_ERROR:
+        return "EMUL_ERROR";
+    }
+    return "UNKNOWN";
+}
+
+typedef struct EmulEvent {
+    QSIMPLEQ_ENTRY(EmulEvent) entry;
+    union {
+        struct {
+            uint32_t type;
+        } gen;
+        struct {
+            uint32_t type;
+            uint64_t code;
+        } error;
+        struct {
+            uint32_t type;
+            uint32_t len;
+            uint8_t data[];
+        } data;
+    } p;
+} EmulEvent;
+
+#define MAX_ATR_SIZE 40
+struct EmulatedState {
+    CCIDCardState base;
+    uint8_t  debug;
+    char    *backend_str;
+    uint32_t backend;
+    char    *cert1;
+    char    *cert2;
+    char    *cert3;
+    char    *db;
+    uint8_t  atr[MAX_ATR_SIZE];
+    uint8_t  atr_length;
+    QSIMPLEQ_HEAD(event_list, EmulEvent) event_list;
+    QemuMutex event_list_mutex;
+    QemuThread event_thread_id;
+    VReader *reader;
+    QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list;
+    QemuMutex vreader_mutex; /* and guest_apdu_list mutex */
+    QemuMutex handle_apdu_mutex;
+    QemuCond handle_apdu_cond;
+    int      pipe[2];
+    int      quit_apdu_thread;
+    QemuThread apdu_thread_id;
+};
+
+static void emulated_apdu_from_guest(CCIDCardState *base,
+    const uint8_t *apdu, uint32_t len)
+{
+    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
+    EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len);
+
+    assert(event);
+    event->p.data.type = EMUL_GUEST_APDU;
+    event->p.data.len = len;
+    memcpy(event->p.data.data, apdu, len);
+    qemu_mutex_lock(&card->vreader_mutex);
+    QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry);
+    qemu_mutex_unlock(&card->vreader_mutex);
+    qemu_mutex_lock(&card->handle_apdu_mutex);
+    qemu_cond_signal(&card->handle_apdu_cond);
+    qemu_mutex_unlock(&card->handle_apdu_mutex);
+}
+
+static const uint8_t *emulated_get_atr(CCIDCardState *base, uint32_t *len)
+{
+    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
+
+    *len = card->atr_length;
+    return card->atr;
+}
+
+static void emulated_push_event(EmulatedState *card, EmulEvent *event)
+{
+    qemu_mutex_lock(&card->event_list_mutex);
+    QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry);
+    qemu_mutex_unlock(&card->event_list_mutex);
+    if (write(card->pipe[1], card, 1) != 1) {
+        DPRINTF(card, 1, "write to pipe failed\n");
+    }
+}
+
+static void emulated_push_type(EmulatedState *card, uint32_t type)
+{
+    EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent));
+
+    assert(event);
+    event->p.gen.type = type;
+    emulated_push_event(card, event);
+}
+
+static void emulated_push_error(EmulatedState *card, uint64_t code)
+{
+    EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent));
+
+    assert(event);
+    event->p.error.type = EMUL_ERROR;
+    event->p.error.code = code;
+    emulated_push_event(card, event);
+}
+
+static void emulated_push_data_type(EmulatedState *card, uint32_t type,
+    const uint8_t *data, uint32_t len)
+{
+    EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len);
+
+    assert(event);
+    event->p.data.type = type;
+    event->p.data.len = len;
+    memcpy(event->p.data.data, data, len);
+    emulated_push_event(card, event);
+}
+
+static void emulated_push_reader_insert(EmulatedState *card)
+{
+    emulated_push_type(card, EMUL_READER_INSERT);
+}
+
+static void emulated_push_reader_remove(EmulatedState *card)
+{
+    emulated_push_type(card, EMUL_READER_REMOVE);
+}
+
+static void emulated_push_card_insert(EmulatedState *card,
+    const uint8_t *atr, uint32_t len)
+{
+    emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len);
+}
+
+static void emulated_push_card_remove(EmulatedState *card)
+{
+    emulated_push_type(card, EMUL_CARD_REMOVE);
+}
+
+static void emulated_push_response_apdu(EmulatedState *card,
+    const uint8_t *apdu, uint32_t len)
+{
+    emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len);
+}
+
+#define APDU_BUF_SIZE 270
+static void *handle_apdu_thread(void* arg)
+{
+    EmulatedState *card = arg;
+    uint8_t recv_data[APDU_BUF_SIZE];
+    int recv_len;
+    VReaderStatus reader_status;
+    EmulEvent *event;
+
+    while (1) {
+        qemu_mutex_lock(&card->handle_apdu_mutex);
+        qemu_cond_wait(&card->handle_apdu_cond, &card->handle_apdu_mutex);
+        qemu_mutex_unlock(&card->handle_apdu_mutex);
+        if (card->quit_apdu_thread) {
+            card->quit_apdu_thread = 0; /* debugging */
+            break;
+        }
+        qemu_mutex_lock(&card->vreader_mutex);
+        while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) {
+            event = QSIMPLEQ_FIRST(&card->guest_apdu_list);
+            assert((unsigned long)event > 1000);
+            QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry);
+            if (event->p.data.type != EMUL_GUEST_APDU) {
+                DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n");
+                g_free(event);
+                continue;
+            }
+            if (card->reader == NULL) {
+                DPRINTF(card, 1, "reader is NULL\n");
+                g_free(event);
+                continue;
+            }
+            recv_len = sizeof(recv_data);
+            reader_status = vreader_xfr_bytes(card->reader,
+                    event->p.data.data, event->p.data.len,
+                    recv_data, &recv_len);
+            DPRINTF(card, 2, "got back apdu of length %d\n", recv_len);
+            if (reader_status == VREADER_OK) {
+                emulated_push_response_apdu(card, recv_data, recv_len);
+            } else {
+                emulated_push_error(card, reader_status);
+            }
+            g_free(event);
+        }
+        qemu_mutex_unlock(&card->vreader_mutex);
+    }
+    return NULL;
+}
+
+static void *event_thread(void *arg)
+{
+    int atr_len = MAX_ATR_SIZE;
+    uint8_t atr[MAX_ATR_SIZE];
+    VEvent *event = NULL;
+    EmulatedState *card = arg;
+
+    while (1) {
+        const char *reader_name;
+
+        event = vevent_wait_next_vevent();
+        if (event == NULL || event->type == VEVENT_LAST) {
+            break;
+        }
+        if (event->type != VEVENT_READER_INSERT) {
+            if (card->reader == NULL && event->reader != NULL) {
+                /* Happens after device_add followed by card remove or insert.
+                 * XXX: create synthetic add_reader events if vcard_emul_init
+                 * already called, which happens if device_del and device_add
+                 * are called */
+                card->reader = vreader_reference(event->reader);
+            } else {
+                if (event->reader != card->reader) {
+                    fprintf(stderr,
+                        "ERROR: wrong reader: quiting event_thread\n");
+                    break;
+                }
+            }
+        }
+        switch (event->type) {
+        case VEVENT_READER_INSERT:
+            /* TODO: take a specific reader. i.e. track which reader
+             * we are seeing here, check it is the one we want (the first,
+             * or by a particular name), and ignore if we don't want it.
+             */
+            reader_name = vreader_get_name(event->reader);
+            if (card->reader != NULL) {
+                DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n",
+                    vreader_get_name(card->reader), reader_name);
+                qemu_mutex_lock(&card->vreader_mutex);
+                vreader_free(card->reader);
+                qemu_mutex_unlock(&card->vreader_mutex);
+                emulated_push_reader_remove(card);
+            }
+            qemu_mutex_lock(&card->vreader_mutex);
+            DPRINTF(card, 2, "READER INSERT %s\n", reader_name);
+            card->reader = vreader_reference(event->reader);
+            qemu_mutex_unlock(&card->vreader_mutex);
+            emulated_push_reader_insert(card);
+            break;
+        case VEVENT_READER_REMOVE:
+            DPRINTF(card, 2, " READER REMOVE: %s\n",
+                    vreader_get_name(event->reader));
+            qemu_mutex_lock(&card->vreader_mutex);
+            vreader_free(card->reader);
+            card->reader = NULL;
+            qemu_mutex_unlock(&card->vreader_mutex);
+            emulated_push_reader_remove(card);
+            break;
+        case VEVENT_CARD_INSERT:
+            /* get the ATR (intended as a response to a power on from the
+             * reader */
+            atr_len = MAX_ATR_SIZE;
+            vreader_power_on(event->reader, atr, &atr_len);
+            card->atr_length = (uint8_t)atr_len;
+            DPRINTF(card, 2, " CARD INSERT\n");
+            emulated_push_card_insert(card, atr, atr_len);
+            break;
+        case VEVENT_CARD_REMOVE:
+            DPRINTF(card, 2, " CARD REMOVE\n");
+            emulated_push_card_remove(card);
+            break;
+        case VEVENT_LAST: /* quit */
+            vevent_delete(event);
+            return NULL;
+            break;
+        default:
+            break;
+        }
+        vevent_delete(event);
+    }
+    return NULL;
+}
+
+static void pipe_read(void *opaque)
+{
+    EmulatedState *card = opaque;
+    EmulEvent *event, *next;
+    char dummy;
+    int len;
+
+    do {
+        len = read(card->pipe[0], &dummy, sizeof(dummy));
+    } while (len == sizeof(dummy));
+    qemu_mutex_lock(&card->event_list_mutex);
+    QSIMPLEQ_FOREACH_SAFE(event, &card->event_list, entry, next) {
+        DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type));
+        switch (event->p.gen.type) {
+        case EMUL_RESPONSE_APDU:
+            ccid_card_send_apdu_to_guest(&card->base, event->p.data.data,
+                event->p.data.len);
+            break;
+        case EMUL_READER_INSERT:
+            ccid_card_ccid_attach(&card->base);
+            break;
+        case EMUL_READER_REMOVE:
+            ccid_card_ccid_detach(&card->base);
+            break;
+        case EMUL_CARD_INSERT:
+            assert(event->p.data.len <= MAX_ATR_SIZE);
+            card->atr_length = event->p.data.len;
+            memcpy(card->atr, event->p.data.data, card->atr_length);
+            ccid_card_card_inserted(&card->base);
+            break;
+        case EMUL_CARD_REMOVE:
+            ccid_card_card_removed(&card->base);
+            break;
+        case EMUL_ERROR:
+            ccid_card_card_error(&card->base, event->p.error.code);
+            break;
+        default:
+            DPRINTF(card, 2, "unexpected event\n");
+            break;
+        }
+        g_free(event);
+    }
+    QSIMPLEQ_INIT(&card->event_list);
+    qemu_mutex_unlock(&card->event_list_mutex);
+}
+
+static int init_pipe_signaling(EmulatedState *card)
+{
+    if (pipe(card->pipe) < 0) {
+        DPRINTF(card, 2, "pipe creation failed\n");
+        return -1;
+    }
+    fcntl(card->pipe[0], F_SETFL, O_NONBLOCK);
+    fcntl(card->pipe[1], F_SETFL, O_NONBLOCK);
+    fcntl(card->pipe[0], F_SETOWN, getpid());
+    qemu_set_fd_handler(card->pipe[0], pipe_read, NULL, card);
+    return 0;
+}
+
+#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb"
+#define CERTIFICATES_ARGS_TEMPLATE\
+    "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)"
+
+static int wrap_vcard_emul_init(VCardEmulOptions *options)
+{
+    static int called;
+    static int options_was_null;
+
+    if (called) {
+        if ((options == NULL) != options_was_null) {
+            printf("%s: warning: running emulated with certificates"
+                   " and emulated side by side is not supported\n",
+                   __func__);
+            return VCARD_EMUL_FAIL;
+        }
+        vcard_emul_replay_insertion_events();
+        return VCARD_EMUL_OK;
+    }
+    options_was_null = (options == NULL);
+    called = 1;
+    return vcard_emul_init(options);
+}
+
+static int emulated_initialize_vcard_from_certificates(EmulatedState *card)
+{
+    char emul_args[200];
+    VCardEmulOptions *options = NULL;
+
+    snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE,
+        card->db ? card->db : CERTIFICATES_DEFAULT_DB,
+        card->cert1, card->cert2, card->cert3);
+    options = vcard_emul_options(emul_args);
+    if (options == NULL) {
+        printf("%s: warning: not using certificates due to"
+               " initialization error\n", __func__);
+    }
+    return wrap_vcard_emul_init(options);
+}
+
+typedef struct EnumTable {
+    const char *name;
+    uint32_t value;
+} EnumTable;
+
+EnumTable backend_enum_table[] = {
+    {BACKEND_NSS_EMULATED_NAME, BACKEND_NSS_EMULATED},
+    {BACKEND_CERTIFICATES_NAME, BACKEND_CERTIFICATES},
+    {NULL, 0},
+};
+
+static uint32_t parse_enumeration(char *str,
+    EnumTable *table, uint32_t not_found_value)
+{
+    uint32_t ret = not_found_value;
+
+    while (table->name != NULL) {
+        if (strcmp(table->name, str) == 0) {
+            ret = table->value;
+            break;
+        }
+        table++;
+    }
+    return ret;
+}
+
+static int emulated_initfn(CCIDCardState *base)
+{
+    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
+    VCardEmulError ret;
+    EnumTable *ptable;
+
+    QSIMPLEQ_INIT(&card->event_list);
+    QSIMPLEQ_INIT(&card->guest_apdu_list);
+    qemu_mutex_init(&card->event_list_mutex);
+    qemu_mutex_init(&card->vreader_mutex);
+    qemu_mutex_init(&card->handle_apdu_mutex);
+    qemu_cond_init(&card->handle_apdu_cond);
+    card->reader = NULL;
+    card->quit_apdu_thread = 0;
+    if (init_pipe_signaling(card) < 0) {
+        return -1;
+    }
+    card->backend = parse_enumeration(card->backend_str, backend_enum_table, 0);
+    if (card->backend == 0) {
+        printf("unknown backend, must be one of:\n");
+        for (ptable = backend_enum_table; ptable->name != NULL; ++ptable) {
+            printf("%s\n", ptable->name);
+        }
+        return -1;
+    }
+
+    /* TODO: a passthru backened that works on local machine. third card type?*/
+    if (card->backend == BACKEND_CERTIFICATES) {
+        if (card->cert1 != NULL && card->cert2 != NULL && card->cert3 != NULL) {
+            ret = emulated_initialize_vcard_from_certificates(card);
+        } else {
+            printf("%s: you must provide all three certs for"
+                   " certificates backend\n", EMULATED_DEV_NAME);
+            return -1;
+        }
+    } else {
+        if (card->backend != BACKEND_NSS_EMULATED) {
+            printf("%s: bad backend specified. The options are:\n%s (default),"
+                " %s.\n", EMULATED_DEV_NAME, BACKEND_NSS_EMULATED_NAME,
+                BACKEND_CERTIFICATES_NAME);
+            return -1;
+        }
+        if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) {
+            printf("%s: unexpected cert parameters to nss emulated backend\n",
+                   EMULATED_DEV_NAME);
+            return -1;
+        }
+        /* default to mirroring the local hardware readers */
+        ret = wrap_vcard_emul_init(NULL);
+    }
+    if (ret != VCARD_EMUL_OK) {
+        printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME);
+        return -1;
+    }
+    qemu_thread_create(&card->event_thread_id, event_thread, card,
+                       QEMU_THREAD_JOINABLE);
+    qemu_thread_create(&card->apdu_thread_id, handle_apdu_thread, card,
+                       QEMU_THREAD_JOINABLE);
+    return 0;
+}
+
+static int emulated_exitfn(CCIDCardState *base)
+{
+    EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
+    VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL);
+
+    vevent_queue_vevent(vevent); /* stop vevent thread */
+    qemu_thread_join(&card->event_thread_id);
+
+    card->quit_apdu_thread = 1; /* stop handle_apdu thread */
+    qemu_cond_signal(&card->handle_apdu_cond);
+    qemu_thread_join(&card->apdu_thread_id);
+
+    /* threads exited, can destroy all condvars/mutexes */
+    qemu_cond_destroy(&card->handle_apdu_cond);
+    qemu_mutex_destroy(&card->handle_apdu_mutex);
+    qemu_mutex_destroy(&card->vreader_mutex);
+    qemu_mutex_destroy(&card->event_list_mutex);
+    return 0;
+}
+
+static Property emulated_card_properties[] = {
+    DEFINE_PROP_STRING("backend", EmulatedState, backend_str),
+    DEFINE_PROP_STRING("cert1", EmulatedState, cert1),
+    DEFINE_PROP_STRING("cert2", EmulatedState, cert2),
+    DEFINE_PROP_STRING("cert3", EmulatedState, cert3),
+    DEFINE_PROP_STRING("db", EmulatedState, db),
+    DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void emulated_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    CCIDCardClass *cc = CCID_CARD_CLASS(klass);
+
+    cc->initfn = emulated_initfn;
+    cc->exitfn = emulated_exitfn;
+    cc->get_atr = emulated_get_atr;
+    cc->apdu_from_guest = emulated_apdu_from_guest;
+    dc->desc = "emulated smartcard";
+    dc->props = emulated_card_properties;
+}
+
+static const TypeInfo emulated_card_info = {
+    .name          = EMULATED_DEV_NAME,
+    .parent        = TYPE_CCID_CARD,
+    .instance_size = sizeof(EmulatedState),
+    .class_init    = emulated_class_initfn,
+};
+
+static void ccid_card_emulated_register_types(void)
+{
+    type_register_static(&emulated_card_info);
+}
+
+type_init(ccid_card_emulated_register_types)
diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
new file mode 100644 (file)
index 0000000..984bd0b
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * CCID Passthru Card Device emulation
+ *
+ * Copyright (c) 2011 Red Hat.
+ * Written by Alon Levy.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "char/char.h"
+#include "qemu/sockets.h"
+#include "monitor/monitor.h"
+#include "hw/ccid.h"
+#include "libcacard/vscard_common.h"
+
+#define DPRINTF(card, lvl, fmt, ...)                    \
+do {                                                    \
+    if (lvl <= card->debug) {                           \
+        printf("ccid-card-passthru: " fmt , ## __VA_ARGS__);     \
+    }                                                   \
+} while (0)
+
+#define D_WARN 1
+#define D_INFO 2
+#define D_MORE_INFO 3
+#define D_VERBOSE 4
+
+/* TODO: do we still need this? */
+uint8_t DEFAULT_ATR[] = {
+/*
+ * From some example somewhere
+ * 0x3B, 0xB0, 0x18, 0x00, 0xD1, 0x81, 0x05, 0xB1, 0x40, 0x38, 0x1F, 0x03, 0x28
+ */
+
+/* From an Athena smart card */
+ 0x3B, 0xD5, 0x18, 0xFF, 0x80, 0x91, 0xFE, 0x1F, 0xC3, 0x80, 0x73, 0xC8, 0x21,
+ 0x13, 0x08
+};
+
+
+#define PASSTHRU_DEV_NAME "ccid-card-passthru"
+#define VSCARD_IN_SIZE 65536
+
+/* maximum size of ATR - from 7816-3 */
+#define MAX_ATR_SIZE        40
+
+typedef struct PassthruState PassthruState;
+
+struct PassthruState {
+    CCIDCardState base;
+    CharDriverState *cs;
+    uint8_t  vscard_in_data[VSCARD_IN_SIZE];
+    uint32_t vscard_in_pos;
+    uint32_t vscard_in_hdr;
+    uint8_t  atr[MAX_ATR_SIZE];
+    uint8_t  atr_length;
+    uint8_t  debug;
+};
+
+/*
+ * VSCard protocol over chardev
+ * This code should not depend on the card type.
+ */
+
+static void ccid_card_vscard_send_msg(PassthruState *s,
+        VSCMsgType type, uint32_t reader_id,
+        const uint8_t *payload, uint32_t length)
+{
+    VSCMsgHeader scr_msg_header;
+
+    scr_msg_header.type = htonl(type);
+    scr_msg_header.reader_id = htonl(reader_id);
+    scr_msg_header.length = htonl(length);
+    qemu_chr_fe_write(s->cs, (uint8_t *)&scr_msg_header, sizeof(VSCMsgHeader));
+    qemu_chr_fe_write(s->cs, payload, length);
+}
+
+static void ccid_card_vscard_send_apdu(PassthruState *s,
+    const uint8_t *apdu, uint32_t length)
+{
+    ccid_card_vscard_send_msg(
+        s, VSC_APDU, VSCARD_MINIMAL_READER_ID, apdu, length);
+}
+
+static void ccid_card_vscard_send_error(PassthruState *s,
+                    uint32_t reader_id, VSCErrorCode code)
+{
+    VSCMsgError msg = {.code = htonl(code)};
+
+    ccid_card_vscard_send_msg(
+        s, VSC_Error, reader_id, (uint8_t *)&msg, sizeof(msg));
+}
+
+static void ccid_card_vscard_send_init(PassthruState *s)
+{
+    VSCMsgInit msg = {
+        .version = htonl(VSCARD_VERSION),
+        .magic = VSCARD_MAGIC,
+        .capabilities = {0}
+    };
+
+    ccid_card_vscard_send_msg(s, VSC_Init, VSCARD_UNDEFINED_READER_ID,
+                         (uint8_t *)&msg, sizeof(msg));
+}
+
+static int ccid_card_vscard_can_read(void *opaque)
+{
+    PassthruState *card = opaque;
+
+    return VSCARD_IN_SIZE >= card->vscard_in_pos ?
+           VSCARD_IN_SIZE - card->vscard_in_pos : 0;
+}
+
+static void ccid_card_vscard_handle_init(
+    PassthruState *card, VSCMsgHeader *hdr, VSCMsgInit *init)
+{
+    uint32_t *capabilities;
+    int num_capabilities;
+    int i;
+
+    capabilities = init->capabilities;
+    num_capabilities =
+        1 + ((hdr->length - sizeof(VSCMsgInit)) / sizeof(uint32_t));
+    init->version = ntohl(init->version);
+    for (i = 0 ; i < num_capabilities; ++i) {
+        capabilities[i] = ntohl(capabilities[i]);
+    }
+    if (init->magic != VSCARD_MAGIC) {
+        error_report("wrong magic");
+        /* we can't disconnect the chardev */
+    }
+    if (init->version != VSCARD_VERSION) {
+        DPRINTF(card, D_WARN,
+            "got version %d, have %d", init->version, VSCARD_VERSION);
+    }
+    /* future handling of capabilities, none exist atm */
+    ccid_card_vscard_send_init(card);
+}
+
+static void ccid_card_vscard_handle_message(PassthruState *card,
+    VSCMsgHeader *scr_msg_header)
+{
+    uint8_t *data = (uint8_t *)&scr_msg_header[1];
+
+    switch (scr_msg_header->type) {
+    case VSC_ATR:
+        DPRINTF(card, D_INFO, "VSC_ATR %d\n", scr_msg_header->length);
+        if (scr_msg_header->length > MAX_ATR_SIZE) {
+            error_report("ATR size exceeds spec, ignoring");
+            ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
+                                        VSC_GENERAL_ERROR);
+            break;
+        }
+        memcpy(card->atr, data, scr_msg_header->length);
+        card->atr_length = scr_msg_header->length;
+        ccid_card_card_inserted(&card->base);
+        ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
+                                    VSC_SUCCESS);
+        break;
+    case VSC_APDU:
+        ccid_card_send_apdu_to_guest(
+            &card->base, data, scr_msg_header->length);
+        break;
+    case VSC_CardRemove:
+        DPRINTF(card, D_INFO, "VSC_CardRemove\n");
+        ccid_card_card_removed(&card->base);
+        ccid_card_vscard_send_error(card,
+            scr_msg_header->reader_id, VSC_SUCCESS);
+        break;
+    case VSC_Init:
+        ccid_card_vscard_handle_init(
+            card, scr_msg_header, (VSCMsgInit *)data);
+        break;
+    case VSC_Error:
+        ccid_card_card_error(&card->base, *(uint32_t *)data);
+        break;
+    case VSC_ReaderAdd:
+        if (ccid_card_ccid_attach(&card->base) < 0) {
+            ccid_card_vscard_send_error(card, VSCARD_UNDEFINED_READER_ID,
+                                      VSC_CANNOT_ADD_MORE_READERS);
+        } else {
+            ccid_card_vscard_send_error(card, VSCARD_MINIMAL_READER_ID,
+                                        VSC_SUCCESS);
+        }
+        break;
+    case VSC_ReaderRemove:
+        ccid_card_ccid_detach(&card->base);
+        ccid_card_vscard_send_error(card,
+            scr_msg_header->reader_id, VSC_SUCCESS);
+        break;
+    default:
+        printf("usb-ccid: chardev: unexpected message of type %X\n",
+               scr_msg_header->type);
+        ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
+            VSC_GENERAL_ERROR);
+    }
+}
+
+static void ccid_card_vscard_drop_connection(PassthruState *card)
+{
+    qemu_chr_delete(card->cs);
+    card->vscard_in_pos = card->vscard_in_hdr = 0;
+}
+
+static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size)
+{
+    PassthruState *card = opaque;
+    VSCMsgHeader *hdr;
+
+    if (card->vscard_in_pos + size > VSCARD_IN_SIZE) {
+        error_report(
+            "no room for data: pos %d +  size %d > %d. dropping connection.",
+            card->vscard_in_pos, size, VSCARD_IN_SIZE);
+        ccid_card_vscard_drop_connection(card);
+        return;
+    }
+    assert(card->vscard_in_pos < VSCARD_IN_SIZE);
+    assert(card->vscard_in_hdr < VSCARD_IN_SIZE);
+    memcpy(card->vscard_in_data + card->vscard_in_pos, buf, size);
+    card->vscard_in_pos += size;
+    hdr = (VSCMsgHeader *)(card->vscard_in_data + card->vscard_in_hdr);
+
+    while ((card->vscard_in_pos - card->vscard_in_hdr >= sizeof(VSCMsgHeader))
+         &&(card->vscard_in_pos - card->vscard_in_hdr >=
+                                  sizeof(VSCMsgHeader) + ntohl(hdr->length))) {
+        hdr->reader_id = ntohl(hdr->reader_id);
+        hdr->length = ntohl(hdr->length);
+        hdr->type = ntohl(hdr->type);
+        ccid_card_vscard_handle_message(card, hdr);
+        card->vscard_in_hdr += hdr->length + sizeof(VSCMsgHeader);
+        hdr = (VSCMsgHeader *)(card->vscard_in_data + card->vscard_in_hdr);
+    }
+    if (card->vscard_in_hdr == card->vscard_in_pos) {
+        card->vscard_in_pos = card->vscard_in_hdr = 0;
+    }
+}
+
+static void ccid_card_vscard_event(void *opaque, int event)
+{
+    PassthruState *card = opaque;
+
+    switch (event) {
+    case CHR_EVENT_BREAK:
+        card->vscard_in_pos = card->vscard_in_hdr = 0;
+        break;
+    case CHR_EVENT_FOCUS:
+        break;
+    case CHR_EVENT_OPENED:
+        DPRINTF(card, D_INFO, "%s: CHR_EVENT_OPENED\n", __func__);
+        break;
+    }
+}
+
+/* End VSCard handling */
+
+static void passthru_apdu_from_guest(
+    CCIDCardState *base, const uint8_t *apdu, uint32_t len)
+{
+    PassthruState *card = DO_UPCAST(PassthruState, base, base);
+
+    if (!card->cs) {
+        printf("ccid-passthru: no chardev, discarding apdu length %d\n", len);
+        return;
+    }
+    ccid_card_vscard_send_apdu(card, apdu, len);
+}
+
+static const uint8_t *passthru_get_atr(CCIDCardState *base, uint32_t *len)
+{
+    PassthruState *card = DO_UPCAST(PassthruState, base, base);
+
+    *len = card->atr_length;
+    return card->atr;
+}
+
+static int passthru_initfn(CCIDCardState *base)
+{
+    PassthruState *card = DO_UPCAST(PassthruState, base, base);
+
+    card->vscard_in_pos = 0;
+    card->vscard_in_hdr = 0;
+    if (card->cs) {
+        DPRINTF(card, D_INFO, "initing chardev\n");
+        qemu_chr_add_handlers(card->cs,
+            ccid_card_vscard_can_read,
+            ccid_card_vscard_read,
+            ccid_card_vscard_event, card);
+        ccid_card_vscard_send_init(card);
+    } else {
+        error_report("missing chardev");
+        return -1;
+    }
+    assert(sizeof(DEFAULT_ATR) <= MAX_ATR_SIZE);
+    memcpy(card->atr, DEFAULT_ATR, sizeof(DEFAULT_ATR));
+    card->atr_length = sizeof(DEFAULT_ATR);
+    return 0;
+}
+
+static int passthru_exitfn(CCIDCardState *base)
+{
+    return 0;
+}
+
+static VMStateDescription passthru_vmstate = {
+    .name = PASSTHRU_DEV_NAME,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_BUFFER(vscard_in_data, PassthruState),
+        VMSTATE_UINT32(vscard_in_pos, PassthruState),
+        VMSTATE_UINT32(vscard_in_hdr, PassthruState),
+        VMSTATE_BUFFER(atr, PassthruState),
+        VMSTATE_UINT8(atr_length, PassthruState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property passthru_card_properties[] = {
+    DEFINE_PROP_CHR("chardev", PassthruState, cs),
+    DEFINE_PROP_UINT8("debug", PassthruState, debug, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void passthru_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    CCIDCardClass *cc = CCID_CARD_CLASS(klass);
+
+    cc->initfn = passthru_initfn;
+    cc->exitfn = passthru_exitfn;
+    cc->get_atr = passthru_get_atr;
+    cc->apdu_from_guest = passthru_apdu_from_guest;
+    dc->desc = "passthrough smartcard";
+    dc->vmsd = &passthru_vmstate;
+    dc->props = passthru_card_properties;
+}
+
+static const TypeInfo passthru_card_info = {
+    .name          = PASSTHRU_DEV_NAME,
+    .parent        = TYPE_CCID_CARD,
+    .instance_size = sizeof(PassthruState),
+    .class_init    = passthru_class_initfn,
+};
+
+static void ccid_card_passthru_register_types(void)
+{
+    type_register_static(&passthru_card_info);
+}
+
+type_init(ccid_card_passthru_register_types)
diff --git a/hw/versatile_i2c.c b/hw/versatile_i2c.c
deleted file mode 100644 (file)
index d0444ae..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * ARM Versatile I2C controller
- *
- * Copyright (c) 2006-2007 CodeSourcery.
- * Copyright (c) 2012 Oskar Andero <oskar.andero@gmail.com>
- *
- * This file is derived from hw/realview.c by Paul Brook
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "hw/sysbus.h"
-#include "hw/bitbang_i2c.h"
-
-typedef struct {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    bitbang_i2c_interface *bitbang;
-    int out;
-    int in;
-} VersatileI2CState;
-
-static uint64_t versatile_i2c_read(void *opaque, hwaddr offset,
-                                   unsigned size)
-{
-    VersatileI2CState *s = (VersatileI2CState *)opaque;
-
-    if (offset == 0) {
-        return (s->out & 1) | (s->in << 1);
-    } else {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: Bad offset 0x%x\n", __func__, (int)offset);
-        return -1;
-    }
-}
-
-static void versatile_i2c_write(void *opaque, hwaddr offset,
-                                uint64_t value, unsigned size)
-{
-    VersatileI2CState *s = (VersatileI2CState *)opaque;
-
-    switch (offset) {
-    case 0:
-        s->out |= value & 3;
-        break;
-    case 4:
-        s->out &= ~value;
-        break;
-    default:
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: Bad offset 0x%x\n", __func__, (int)offset);
-    }
-    bitbang_i2c_set(s->bitbang, BITBANG_I2C_SCL, (s->out & 1) != 0);
-    s->in = bitbang_i2c_set(s->bitbang, BITBANG_I2C_SDA, (s->out & 2) != 0);
-}
-
-static const MemoryRegionOps versatile_i2c_ops = {
-    .read = versatile_i2c_read,
-    .write = versatile_i2c_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int versatile_i2c_init(SysBusDevice *dev)
-{
-    VersatileI2CState *s = FROM_SYSBUS(VersatileI2CState, dev);
-    i2c_bus *bus;
-
-    bus = i2c_init_bus(&dev->qdev, "i2c");
-    s->bitbang = bitbang_i2c_init(bus);
-    memory_region_init_io(&s->iomem, &versatile_i2c_ops, s,
-                          "versatile_i2c", 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-    return 0;
-}
-
-static void versatile_i2c_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = versatile_i2c_init;
-}
-
-static const TypeInfo versatile_i2c_info = {
-    .name          = "versatile_i2c",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(VersatileI2CState),
-    .class_init    = versatile_i2c_class_init,
-};
-
-static void versatile_i2c_register_types(void)
-{
-    type_register_static(&versatile_i2c_info);
-}
-
-type_init(versatile_i2c_register_types)
diff --git a/hw/versatile_pci.c b/hw/versatile_pci.c
deleted file mode 100644 (file)
index d67ca79..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * ARM Versatile/PB PCI host controller
- *
- * Copyright (c) 2006-2009 CodeSourcery.
- * Written by Paul Brook
- *
- * This code is licensed under the LGPL.
- */
-
-#include "hw/sysbus.h"
-#include "hw/pci/pci.h"
-#include "hw/pci/pci_host.h"
-#include "exec/address-spaces.h"
-
-typedef struct {
-    SysBusDevice busdev;
-    qemu_irq irq[4];
-    int realview;
-    MemoryRegion mem_config;
-    MemoryRegion mem_config2;
-    MemoryRegion isa;
-} PCIVPBState;
-
-static inline uint32_t vpb_pci_config_addr(hwaddr addr)
-{
-    return addr & 0xffffff;
-}
-
-static void pci_vpb_config_write(void *opaque, hwaddr addr,
-                                 uint64_t val, unsigned size)
-{
-    pci_data_write(opaque, vpb_pci_config_addr(addr), val, size);
-}
-
-static uint64_t pci_vpb_config_read(void *opaque, hwaddr addr,
-                                    unsigned size)
-{
-    uint32_t val;
-    val = pci_data_read(opaque, vpb_pci_config_addr(addr), size);
-    return val;
-}
-
-static const MemoryRegionOps pci_vpb_config_ops = {
-    .read = pci_vpb_config_read,
-    .write = pci_vpb_config_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int pci_vpb_map_irq(PCIDevice *d, int irq_num)
-{
-    return irq_num;
-}
-
-static void pci_vpb_set_irq(void *opaque, int irq_num, int level)
-{
-    qemu_irq *pic = opaque;
-
-    qemu_set_irq(pic[irq_num], level);
-}
-
-static int pci_vpb_init(SysBusDevice *dev)
-{
-    PCIVPBState *s = FROM_SYSBUS(PCIVPBState, dev);
-    PCIBus *bus;
-    int i;
-
-    for (i = 0; i < 4; i++) {
-        sysbus_init_irq(dev, &s->irq[i]);
-    }
-    bus = pci_register_bus(&dev->qdev, "pci",
-                           pci_vpb_set_irq, pci_vpb_map_irq, s->irq,
-                           get_system_memory(), get_system_io(),
-                           PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS);
-
-    /* ??? Register memory space.  */
-
-    /* Our memory regions are:
-     * 0 : PCI self config window
-     * 1 : PCI config window
-     * 2 : PCI IO window (realview_pci only)
-     */
-    memory_region_init_io(&s->mem_config, &pci_vpb_config_ops, bus,
-                          "pci-vpb-selfconfig", 0x1000000);
-    sysbus_init_mmio(dev, &s->mem_config);
-    memory_region_init_io(&s->mem_config2, &pci_vpb_config_ops, bus,
-                          "pci-vpb-config", 0x1000000);
-    sysbus_init_mmio(dev, &s->mem_config2);
-    if (s->realview) {
-        isa_mmio_setup(&s->isa, 0x0100000);
-        sysbus_init_mmio(dev, &s->isa);
-    }
-
-    pci_create_simple(bus, -1, "versatile_pci_host");
-    return 0;
-}
-
-static int pci_realview_init(SysBusDevice *dev)
-{
-    PCIVPBState *s = FROM_SYSBUS(PCIVPBState, dev);
-    s->realview = 1;
-    return pci_vpb_init(dev);
-}
-
-static int versatile_pci_host_init(PCIDevice *d)
-{
-    pci_set_word(d->config + PCI_STATUS,
-                PCI_STATUS_66MHZ | PCI_STATUS_DEVSEL_MEDIUM);
-    pci_set_byte(d->config + PCI_LATENCY_TIMER, 0x10);
-    return 0;
-}
-
-static void versatile_pci_host_class_init(ObjectClass *klass, void *data)
-{
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = versatile_pci_host_init;
-    k->vendor_id = PCI_VENDOR_ID_XILINX;
-    k->device_id = PCI_DEVICE_ID_XILINX_XC2VP30;
-    k->class_id = PCI_CLASS_PROCESSOR_CO;
-}
-
-static const TypeInfo versatile_pci_host_info = {
-    .name          = "versatile_pci_host",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIDevice),
-    .class_init    = versatile_pci_host_class_init,
-};
-
-static void pci_vpb_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sdc->init = pci_vpb_init;
-}
-
-static const TypeInfo pci_vpb_info = {
-    .name          = "versatile_pci",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(PCIVPBState),
-    .class_init    = pci_vpb_class_init,
-};
-
-static void pci_realview_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sdc->init = pci_realview_init;
-}
-
-static const TypeInfo pci_realview_info = {
-    .name          = "realview_pci",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(PCIVPBState),
-    .class_init    = pci_realview_class_init,
-};
-
-static void versatile_pci_register_types(void)
-{
-    type_register_static(&pci_vpb_info);
-    type_register_static(&pci_realview_info);
-    type_register_static(&versatile_pci_host_info);
-}
-
-type_init(versatile_pci_register_types)
diff --git a/hw/vga-isa-mm.c b/hw/vga-isa-mm.c
deleted file mode 100644 (file)
index 3b08720..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * QEMU ISA MM VGA Emulator.
- *
- * Copyright (c) 2003 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "ui/console.h"
-#include "hw/i386/pc.h"
-#include "hw/vga_int.h"
-#include "ui/pixel_ops.h"
-#include "qemu/timer.h"
-
-#define VGA_RAM_SIZE (8192 * 1024)
-
-typedef struct ISAVGAMMState {
-    VGACommonState vga;
-    int it_shift;
-} ISAVGAMMState;
-
-/* Memory mapped interface */
-static uint32_t vga_mm_readb (void *opaque, hwaddr addr)
-{
-    ISAVGAMMState *s = opaque;
-
-    return vga_ioport_read(&s->vga, addr >> s->it_shift) & 0xff;
-}
-
-static void vga_mm_writeb (void *opaque,
-                           hwaddr addr, uint32_t value)
-{
-    ISAVGAMMState *s = opaque;
-
-    vga_ioport_write(&s->vga, addr >> s->it_shift, value & 0xff);
-}
-
-static uint32_t vga_mm_readw (void *opaque, hwaddr addr)
-{
-    ISAVGAMMState *s = opaque;
-
-    return vga_ioport_read(&s->vga, addr >> s->it_shift) & 0xffff;
-}
-
-static void vga_mm_writew (void *opaque,
-                           hwaddr addr, uint32_t value)
-{
-    ISAVGAMMState *s = opaque;
-
-    vga_ioport_write(&s->vga, addr >> s->it_shift, value & 0xffff);
-}
-
-static uint32_t vga_mm_readl (void *opaque, hwaddr addr)
-{
-    ISAVGAMMState *s = opaque;
-
-    return vga_ioport_read(&s->vga, addr >> s->it_shift);
-}
-
-static void vga_mm_writel (void *opaque,
-                           hwaddr addr, uint32_t value)
-{
-    ISAVGAMMState *s = opaque;
-
-    vga_ioport_write(&s->vga, addr >> s->it_shift, value);
-}
-
-static const MemoryRegionOps vga_mm_ctrl_ops = {
-    .old_mmio = {
-        .read = {
-            vga_mm_readb,
-            vga_mm_readw,
-            vga_mm_readl,
-        },
-        .write = {
-            vga_mm_writeb,
-            vga_mm_writew,
-            vga_mm_writel,
-        },
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void vga_mm_init(ISAVGAMMState *s, hwaddr vram_base,
-                        hwaddr ctrl_base, int it_shift,
-                        MemoryRegion *address_space)
-{
-    MemoryRegion *s_ioport_ctrl, *vga_io_memory;
-
-    s->it_shift = it_shift;
-    s_ioport_ctrl = g_malloc(sizeof(*s_ioport_ctrl));
-    memory_region_init_io(s_ioport_ctrl, &vga_mm_ctrl_ops, s,
-                          "vga-mm-ctrl", 0x100000);
-    memory_region_set_flush_coalesced(s_ioport_ctrl);
-
-    vga_io_memory = g_malloc(sizeof(*vga_io_memory));
-    /* XXX: endianness? */
-    memory_region_init_io(vga_io_memory, &vga_mem_ops, &s->vga,
-                          "vga-mem", 0x20000);
-
-    vmstate_register(NULL, 0, &vmstate_vga_common, s);
-
-    memory_region_add_subregion(address_space, ctrl_base, s_ioport_ctrl);
-    s->vga.bank_offset = 0;
-    memory_region_add_subregion(address_space,
-                                vram_base + 0x000a0000, vga_io_memory);
-    memory_region_set_coalescing(vga_io_memory);
-}
-
-int isa_vga_mm_init(hwaddr vram_base,
-                    hwaddr ctrl_base, int it_shift,
-                    MemoryRegion *address_space)
-{
-    ISAVGAMMState *s;
-
-    s = g_malloc0(sizeof(*s));
-
-    s->vga.vram_size_mb = VGA_RAM_SIZE >> 20;
-    vga_common_init(&s->vga);
-    vga_mm_init(s, vram_base, ctrl_base, it_shift, address_space);
-
-    s->vga.con = graphic_console_init(s->vga.update, s->vga.invalidate,
-                                      s->vga.screen_dump, s->vga.text_update,
-                                      s);
-
-    vga_init_vbe(&s->vga, address_space);
-    return 0;
-}
diff --git a/hw/vga-isa.c b/hw/vga-isa.c
deleted file mode 100644 (file)
index 89d7fa6..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * QEMU ISA VGA Emulator.
- *
- * see docs/specs/standard-vga.txt for virtual hardware specs.
- *
- * Copyright (c) 2003 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "ui/console.h"
-#include "hw/i386/pc.h"
-#include "hw/vga_int.h"
-#include "ui/pixel_ops.h"
-#include "qemu/timer.h"
-#include "hw/loader.h"
-
-typedef struct ISAVGAState {
-    ISADevice dev;
-    struct VGACommonState state;
-} ISAVGAState;
-
-static void vga_reset_isa(DeviceState *dev)
-{
-    ISAVGAState *d = container_of(dev, ISAVGAState, dev.qdev);
-    VGACommonState *s = &d->state;
-
-    vga_common_reset(s);
-}
-
-static int vga_initfn(ISADevice *dev)
-{
-    ISAVGAState *d = DO_UPCAST(ISAVGAState, dev, dev);
-    VGACommonState *s = &d->state;
-    MemoryRegion *vga_io_memory;
-    const MemoryRegionPortio *vga_ports, *vbe_ports;
-
-    vga_common_init(s);
-    s->legacy_address_space = isa_address_space(dev);
-    vga_io_memory = vga_init_io(s, &vga_ports, &vbe_ports);
-    isa_register_portio_list(dev, 0x3b0, vga_ports, s, "vga");
-    if (vbe_ports) {
-        isa_register_portio_list(dev, 0x1ce, vbe_ports, s, "vbe");
-    }
-    memory_region_add_subregion_overlap(isa_address_space(dev),
-                                        isa_mem_base + 0x000a0000,
-                                        vga_io_memory, 1);
-    memory_region_set_coalescing(vga_io_memory);
-    s->con = graphic_console_init(s->update, s->invalidate,
-                                  s->screen_dump, s->text_update, s);
-
-    vga_init_vbe(s, isa_address_space(dev));
-    /* ROM BIOS */
-    rom_add_vga(VGABIOS_FILENAME);
-    return 0;
-}
-
-static Property vga_isa_properties[] = {
-    DEFINE_PROP_UINT32("vgamem_mb", ISAVGAState, state.vram_size_mb, 8),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void vga_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
-    ic->init = vga_initfn;
-    dc->reset = vga_reset_isa;
-    dc->vmsd = &vmstate_vga_common;
-    dc->props = vga_isa_properties;
-}
-
-static const TypeInfo vga_info = {
-    .name          = "isa-vga",
-    .parent        = TYPE_ISA_DEVICE,
-    .instance_size = sizeof(ISAVGAState),
-    .class_init    = vga_class_initfn,
-};
-
-static void vga_register_types(void)
-{
-    type_register_static(&vga_info);
-}
-
-type_init(vga_register_types)
diff --git a/hw/vga-pci.c b/hw/vga-pci.c
deleted file mode 100644 (file)
index 05fa9bc..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * QEMU PCI VGA Emulator.
- *
- * see docs/specs/standard-vga.txt for virtual hardware specs.
- *
- * Copyright (c) 2003 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "ui/console.h"
-#include "hw/pci/pci.h"
-#include "hw/vga_int.h"
-#include "ui/pixel_ops.h"
-#include "qemu/timer.h"
-#include "hw/loader.h"
-
-#define PCI_VGA_IOPORT_OFFSET 0x400
-#define PCI_VGA_IOPORT_SIZE   (0x3e0 - 0x3c0)
-#define PCI_VGA_BOCHS_OFFSET  0x500
-#define PCI_VGA_BOCHS_SIZE    (0x0b * 2)
-#define PCI_VGA_MMIO_SIZE     0x1000
-
-enum vga_pci_flags {
-    PCI_VGA_FLAG_ENABLE_MMIO = 1,
-};
-
-typedef struct PCIVGAState {
-    PCIDevice dev;
-    VGACommonState vga;
-    uint32_t flags;
-    MemoryRegion mmio;
-    MemoryRegion ioport;
-    MemoryRegion bochs;
-} PCIVGAState;
-
-static const VMStateDescription vmstate_vga_pci = {
-    .name = "vga",
-    .version_id = 2,
-    .minimum_version_id = 2,
-    .minimum_version_id_old = 2,
-    .fields      = (VMStateField []) {
-        VMSTATE_PCI_DEVICE(dev, PCIVGAState),
-        VMSTATE_STRUCT(vga, PCIVGAState, 0, vmstate_vga_common, VGACommonState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static uint64_t pci_vga_ioport_read(void *ptr, hwaddr addr,
-                                    unsigned size)
-{
-    PCIVGAState *d = ptr;
-    uint64_t ret = 0;
-
-    switch (size) {
-    case 1:
-        ret = vga_ioport_read(&d->vga, addr);
-        break;
-    case 2:
-        ret  = vga_ioport_read(&d->vga, addr);
-        ret |= vga_ioport_read(&d->vga, addr+1) << 8;
-        break;
-    }
-    return ret;
-}
-
-static void pci_vga_ioport_write(void *ptr, hwaddr addr,
-                                 uint64_t val, unsigned size)
-{
-    PCIVGAState *d = ptr;
-
-    switch (size) {
-    case 1:
-        vga_ioport_write(&d->vga, addr + 0x3c0, val);
-        break;
-    case 2:
-        /*
-         * Update bytes in little endian order.  Allows to update
-         * indexed registers with a single word write because the
-         * index byte is updated first.
-         */
-        vga_ioport_write(&d->vga, addr + 0x3c0, val & 0xff);
-        vga_ioport_write(&d->vga, addr + 0x3c1, (val >> 8) & 0xff);
-        break;
-    }
-}
-
-static const MemoryRegionOps pci_vga_ioport_ops = {
-    .read = pci_vga_ioport_read,
-    .write = pci_vga_ioport_write,
-    .valid.min_access_size = 1,
-    .valid.max_access_size = 4,
-    .impl.min_access_size = 1,
-    .impl.max_access_size = 2,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static uint64_t pci_vga_bochs_read(void *ptr, hwaddr addr,
-                                   unsigned size)
-{
-    PCIVGAState *d = ptr;
-    int index = addr >> 1;
-
-    vbe_ioport_write_index(&d->vga, 0, index);
-    return vbe_ioport_read_data(&d->vga, 0);
-}
-
-static void pci_vga_bochs_write(void *ptr, hwaddr addr,
-                                uint64_t val, unsigned size)
-{
-    PCIVGAState *d = ptr;
-    int index = addr >> 1;
-
-    vbe_ioport_write_index(&d->vga, 0, index);
-    vbe_ioport_write_data(&d->vga, 0, val);
-}
-
-static const MemoryRegionOps pci_vga_bochs_ops = {
-    .read = pci_vga_bochs_read,
-    .write = pci_vga_bochs_write,
-    .valid.min_access_size = 1,
-    .valid.max_access_size = 4,
-    .impl.min_access_size = 2,
-    .impl.max_access_size = 2,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static int pci_std_vga_initfn(PCIDevice *dev)
-{
-    PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, dev);
-    VGACommonState *s = &d->vga;
-
-    /* vga + console init */
-    vga_common_init(s);
-    vga_init(s, pci_address_space(dev), pci_address_space_io(dev), true);
-
-    s->con = graphic_console_init(s->update, s->invalidate,
-                                  s->screen_dump, s->text_update, s);
-
-    /* XXX: VGA_RAM_SIZE must be a power of two */
-    pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
-
-    /* mmio bar for vga register access */
-    if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_MMIO)) {
-        memory_region_init(&d->mmio, "vga.mmio", 4096);
-        memory_region_init_io(&d->ioport, &pci_vga_ioport_ops, d,
-                              "vga ioports remapped", PCI_VGA_IOPORT_SIZE);
-        memory_region_init_io(&d->bochs, &pci_vga_bochs_ops, d,
-                              "bochs dispi interface", PCI_VGA_BOCHS_SIZE);
-
-        memory_region_add_subregion(&d->mmio, PCI_VGA_IOPORT_OFFSET,
-                                    &d->ioport);
-        memory_region_add_subregion(&d->mmio, PCI_VGA_BOCHS_OFFSET,
-                                    &d->bochs);
-        pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
-    }
-
-    if (!dev->rom_bar) {
-        /* compatibility with pc-0.13 and older */
-        vga_init_vbe(s, pci_address_space(dev));
-    }
-
-    return 0;
-}
-
-static Property vga_pci_properties[] = {
-    DEFINE_PROP_UINT32("vgamem_mb", PCIVGAState, vga.vram_size_mb, 16),
-    DEFINE_PROP_BIT("mmio", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_MMIO, true),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void vga_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->no_hotplug = 1;
-    k->init = pci_std_vga_initfn;
-    k->romfile = "vgabios-stdvga.bin";
-    k->vendor_id = PCI_VENDOR_ID_QEMU;
-    k->device_id = PCI_DEVICE_ID_QEMU_VGA;
-    k->class_id = PCI_CLASS_DISPLAY_VGA;
-    dc->vmsd = &vmstate_vga_pci;
-    dc->props = vga_pci_properties;
-}
-
-static const TypeInfo vga_info = {
-    .name          = "VGA",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIVGAState),
-    .class_init    = vga_class_init,
-};
-
-static void vga_register_types(void)
-{
-    type_register_static(&vga_info);
-}
-
-type_init(vga_register_types)
diff --git a/hw/virtio-bus.c b/hw/virtio-bus.c
deleted file mode 100644 (file)
index 1596a1c..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * VirtioBus
- *
- *  Copyright (C) 2012 : GreenSocs Ltd
- *      http://www.greensocs.com/ , email: info@greensocs.com
- *
- *  Developed by :
- *  Frederic Konrad   <fred.konrad@greensocs.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "hw/hw.h"
-#include "qemu/error-report.h"
-#include "hw/qdev.h"
-#include "hw/virtio/virtio-bus.h"
-#include "hw/virtio/virtio.h"
-
-/* #define DEBUG_VIRTIO_BUS */
-
-#ifdef DEBUG_VIRTIO_BUS
-#define DPRINTF(fmt, ...) \
-do { printf("virtio_bus: " fmt , ## __VA_ARGS__); } while (0)
-#else
-#define DPRINTF(fmt, ...) do { } while (0)
-#endif
-
-/* Plug the VirtIODevice */
-int virtio_bus_plug_device(VirtIODevice *vdev)
-{
-    DeviceState *qdev = DEVICE(vdev);
-    BusState *qbus = BUS(qdev_get_parent_bus(qdev));
-    VirtioBusState *bus = VIRTIO_BUS(qbus);
-    VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
-    DPRINTF("%s: plug device.\n", qbus->name);
-
-    bus->vdev = vdev;
-
-    /*
-     * The lines below will disappear when we drop VirtIOBindings, at the end
-     * of the series.
-     */
-    bus->bindings.notify = klass->notify;
-    bus->bindings.save_config = klass->save_config;
-    bus->bindings.save_queue = klass->save_queue;
-    bus->bindings.load_config = klass->load_config;
-    bus->bindings.load_queue = klass->load_queue;
-    bus->bindings.load_done = klass->load_done;
-    bus->bindings.get_features = klass->get_features;
-    bus->bindings.query_guest_notifiers = klass->query_guest_notifiers;
-    bus->bindings.set_guest_notifiers = klass->set_guest_notifiers;
-    bus->bindings.set_host_notifier = klass->set_host_notifier;
-    bus->bindings.vmstate_change = klass->vmstate_change;
-    virtio_bind_device(bus->vdev, &bus->bindings, qbus->parent);
-
-    if (klass->device_plugged != NULL) {
-        klass->device_plugged(qbus->parent);
-    }
-
-    return 0;
-}
-
-/* Reset the virtio_bus */
-void virtio_bus_reset(VirtioBusState *bus)
-{
-    DPRINTF("%s: reset device.\n", qbus->name);
-    if (bus->vdev != NULL) {
-        virtio_reset(bus->vdev);
-    }
-}
-
-/* Destroy the VirtIODevice */
-void virtio_bus_destroy_device(VirtioBusState *bus)
-{
-    DeviceState *qdev;
-    BusState *qbus = BUS(bus);
-    VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
-    DPRINTF("%s: remove device.\n", qbus->name);
-
-    if (bus->vdev != NULL) {
-        if (klass->device_unplug != NULL) {
-            klass->device_unplug(qbus->parent);
-        }
-        qdev = DEVICE(bus->vdev);
-        qdev_free(qdev);
-        bus->vdev = NULL;
-    }
-}
-
-/* Get the device id of the plugged device. */
-uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus)
-{
-    assert(bus->vdev != NULL);
-    return bus->vdev->device_id;
-}
-
-/* Get the config_len field of the plugged device. */
-size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus)
-{
-    assert(bus->vdev != NULL);
-    return bus->vdev->config_len;
-}
-
-/* Get the features of the plugged device. */
-uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus,
-                                    uint32_t requested_features)
-{
-    VirtioDeviceClass *k;
-    assert(bus->vdev != NULL);
-    k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
-    assert(k->get_features != NULL);
-    return k->get_features(bus->vdev, requested_features);
-}
-
-/* Get bad features of the plugged device. */
-uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus)
-{
-    VirtioDeviceClass *k;
-    assert(bus->vdev != NULL);
-    k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
-    if (k->bad_features != NULL) {
-        return k->bad_features(bus->vdev);
-    } else {
-        return 0;
-    }
-}
-
-/* Get config of the plugged device. */
-void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config)
-{
-    VirtioDeviceClass *k;
-    assert(bus->vdev != NULL);
-    k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
-    if (k->get_config != NULL) {
-        k->get_config(bus->vdev, config);
-    }
-}
-
-static const TypeInfo virtio_bus_info = {
-    .name = TYPE_VIRTIO_BUS,
-    .parent = TYPE_BUS,
-    .instance_size = sizeof(VirtioBusState),
-    .abstract = true,
-    .class_size = sizeof(VirtioBusClass),
-};
-
-static void virtio_register_types(void)
-{
-    type_register_static(&virtio_bus_info);
-}
-
-type_init(virtio_register_types)
diff --git a/hw/virtio-console.c b/hw/virtio-console.c
deleted file mode 100644 (file)
index 31f672c..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Virtio Console and Generic Serial Port Devices
- *
- * Copyright Red Hat, Inc. 2009, 2010
- *
- * Authors:
- *  Amit Shah <amit.shah@redhat.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
- */
-
-#include "char/char.h"
-#include "qemu/error-report.h"
-#include "trace.h"
-#include "hw/virtio/virtio-serial.h"
-
-typedef struct VirtConsole {
-    VirtIOSerialPort port;
-    CharDriverState *chr;
-} VirtConsole;
-
-/*
- * Callback function that's called from chardevs when backend becomes
- * writable.
- */
-static gboolean chr_write_unblocked(GIOChannel *chan, GIOCondition cond,
-                                    void *opaque)
-{
-    VirtConsole *vcon = opaque;
-
-    virtio_serial_throttle_port(&vcon->port, false);
-    return FALSE;
-}
-
-/* Callback function that's called when the guest sends us data */
-static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
-{
-    VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
-    ssize_t ret;
-
-    if (!vcon->chr) {
-        /* If there's no backend, we can just say we consumed all data. */
-        return len;
-    }
-
-    ret = qemu_chr_fe_write(vcon->chr, buf, len);
-    trace_virtio_console_flush_buf(port->id, len, ret);
-
-    if (ret <= 0) {
-        VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
-
-        /*
-         * Ideally we'd get a better error code than just -1, but
-         * that's what the chardev interface gives us right now.  If
-         * we had a finer-grained message, like -EPIPE, we could close
-         * this connection.
-         */
-        ret = 0;
-        if (!k->is_console) {
-            virtio_serial_throttle_port(port, true);
-            qemu_chr_fe_add_watch(vcon->chr, G_IO_OUT, chr_write_unblocked,
-                                  vcon);
-        }
-    }
-    return ret;
-}
-
-/* Callback function that's called when the guest opens/closes the port */
-static void set_guest_connected(VirtIOSerialPort *port, int guest_connected)
-{
-    VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
-
-    if (!vcon->chr) {
-        return;
-    }
-    qemu_chr_fe_set_open(vcon->chr, guest_connected);
-}
-
-/* Readiness of the guest to accept data on a port */
-static int chr_can_read(void *opaque)
-{
-    VirtConsole *vcon = opaque;
-
-    return virtio_serial_guest_ready(&vcon->port);
-}
-
-/* Send data from a char device over to the guest */
-static void chr_read(void *opaque, const uint8_t *buf, int size)
-{
-    VirtConsole *vcon = opaque;
-
-    trace_virtio_console_chr_read(vcon->port.id, size);
-    virtio_serial_write(&vcon->port, buf, size);
-}
-
-static void chr_event(void *opaque, int event)
-{
-    VirtConsole *vcon = opaque;
-
-    trace_virtio_console_chr_event(vcon->port.id, event);
-    switch (event) {
-    case CHR_EVENT_OPENED:
-        virtio_serial_open(&vcon->port);
-        break;
-    case CHR_EVENT_CLOSED:
-        virtio_serial_close(&vcon->port);
-        break;
-    }
-}
-
-static int virtconsole_initfn(VirtIOSerialPort *port)
-{
-    VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
-    VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
-
-    if (port->id == 0 && !k->is_console) {
-        error_report("Port number 0 on virtio-serial devices reserved for virtconsole devices for backward compatibility.");
-        return -1;
-    }
-
-    if (vcon->chr) {
-        vcon->chr->explicit_fe_open = 1;
-        qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event,
-                              vcon);
-    }
-
-    return 0;
-}
-
-static Property virtconsole_properties[] = {
-    DEFINE_PROP_CHR("chardev", VirtConsole, chr),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void virtconsole_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass);
-
-    k->is_console = true;
-    k->init = virtconsole_initfn;
-    k->have_data = flush_buf;
-    k->set_guest_connected = set_guest_connected;
-    dc->props = virtconsole_properties;
-}
-
-static const TypeInfo virtconsole_info = {
-    .name          = "virtconsole",
-    .parent        = TYPE_VIRTIO_SERIAL_PORT,
-    .instance_size = sizeof(VirtConsole),
-    .class_init    = virtconsole_class_init,
-};
-
-static Property virtserialport_properties[] = {
-    DEFINE_PROP_CHR("chardev", VirtConsole, chr),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void virtserialport_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass);
-
-    k->init = virtconsole_initfn;
-    k->have_data = flush_buf;
-    k->set_guest_connected = set_guest_connected;
-    dc->props = virtserialport_properties;
-}
-
-static const TypeInfo virtserialport_info = {
-    .name          = "virtserialport",
-    .parent        = TYPE_VIRTIO_SERIAL_PORT,
-    .instance_size = sizeof(VirtConsole),
-    .class_init    = virtserialport_class_init,
-};
-
-static void virtconsole_register_types(void)
-{
-    type_register_static(&virtconsole_info);
-    type_register_static(&virtserialport_info);
-}
-
-type_init(virtconsole_register_types)
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
deleted file mode 100644 (file)
index 943b429..0000000
+++ /dev/null
@@ -1,1514 +0,0 @@
-/*
- * Virtio PCI Bindings
- *
- * Copyright IBM, Corp. 2007
- * Copyright (c) 2009 CodeSourcery
- *
- * Authors:
- *  Anthony Liguori   <aliguori@us.ibm.com>
- *  Paul Brook        <paul@codesourcery.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include <inttypes.h>
-
-#include "hw/virtio/virtio.h"
-#include "hw/virtio/virtio-blk.h"
-#include "hw/virtio/virtio-net.h"
-#include "hw/virtio/virtio-serial.h"
-#include "hw/virtio/virtio-scsi.h"
-#include "hw/virtio/virtio-balloon.h"
-#include "hw/pci/pci.h"
-#include "qemu/error-report.h"
-#include "hw/pci/msi.h"
-#include "hw/pci/msix.h"
-#include "hw/loader.h"
-#include "sysemu/kvm.h"
-#include "sysemu/blockdev.h"
-#include "hw/virtio-pci.h"
-#include "qemu/range.h"
-#include "hw/virtio/virtio-bus.h"
-
-/* from Linux's linux/virtio_pci.h */
-
-/* A 32-bit r/o bitmask of the features supported by the host */
-#define VIRTIO_PCI_HOST_FEATURES        0
-
-/* A 32-bit r/w bitmask of features activated by the guest */
-#define VIRTIO_PCI_GUEST_FEATURES       4
-
-/* A 32-bit r/w PFN for the currently selected queue */
-#define VIRTIO_PCI_QUEUE_PFN            8
-
-/* A 16-bit r/o queue size for the currently selected queue */
-#define VIRTIO_PCI_QUEUE_NUM            12
-
-/* A 16-bit r/w queue selector */
-#define VIRTIO_PCI_QUEUE_SEL            14
-
-/* A 16-bit r/w queue notifier */
-#define VIRTIO_PCI_QUEUE_NOTIFY         16
-
-/* An 8-bit device status register.  */
-#define VIRTIO_PCI_STATUS               18
-
-/* An 8-bit r/o interrupt status register.  Reading the value will return the
- * current contents of the ISR and will also clear it.  This is effectively
- * a read-and-acknowledge. */
-#define VIRTIO_PCI_ISR                  19
-
-/* MSI-X registers: only enabled if MSI-X is enabled. */
-/* A 16-bit vector for configuration changes. */
-#define VIRTIO_MSI_CONFIG_VECTOR        20
-/* A 16-bit vector for selected queue notifications. */
-#define VIRTIO_MSI_QUEUE_VECTOR         22
-
-/* Config space size */
-#define VIRTIO_PCI_CONFIG_NOMSI         20
-#define VIRTIO_PCI_CONFIG_MSI           24
-#define VIRTIO_PCI_REGION_SIZE(dev)     (msix_present(dev) ? \
-                                         VIRTIO_PCI_CONFIG_MSI : \
-                                         VIRTIO_PCI_CONFIG_NOMSI)
-
-/* The remaining space is defined by each driver as the per-driver
- * configuration space */
-#define VIRTIO_PCI_CONFIG(dev)          (msix_enabled(dev) ? \
-                                         VIRTIO_PCI_CONFIG_MSI : \
-                                         VIRTIO_PCI_CONFIG_NOMSI)
-
-/* How many bits to shift physical queue address written to QUEUE_PFN.
- * 12 is historical, and due to x86 page size. */
-#define VIRTIO_PCI_QUEUE_ADDR_SHIFT    12
-
-/* Flags track per-device state like workarounds for quirks in older guests. */
-#define VIRTIO_PCI_FLAG_BUS_MASTER_BUG  (1 << 0)
-
-/* QEMU doesn't strictly need write barriers since everything runs in
- * lock-step.  We'll leave the calls to wmb() in though to make it obvious for
- * KVM or if kqemu gets SMP support.
- */
-#define wmb() do { } while (0)
-
-/* HACK for virtio to determine if it's running a big endian guest */
-bool virtio_is_big_endian(void);
-
-/* virtio device */
-/* DeviceState to VirtIOPCIProxy. For use off data-path. TODO: use QOM. */
-static inline VirtIOPCIProxy *to_virtio_pci_proxy(DeviceState *d)
-{
-    return container_of(d, VirtIOPCIProxy, pci_dev.qdev);
-}
-
-/* DeviceState to VirtIOPCIProxy. Note: used on datapath,
- * be careful and test performance if you change this.
- */
-static inline VirtIOPCIProxy *to_virtio_pci_proxy_fast(DeviceState *d)
-{
-    return container_of(d, VirtIOPCIProxy, pci_dev.qdev);
-}
-
-static void virtio_pci_notify(DeviceState *d, uint16_t vector)
-{
-    VirtIOPCIProxy *proxy = to_virtio_pci_proxy_fast(d);
-    if (msix_enabled(&proxy->pci_dev))
-        msix_notify(&proxy->pci_dev, vector);
-    else
-        qemu_set_irq(proxy->pci_dev.irq[0], proxy->vdev->isr & 1);
-}
-
-static void virtio_pci_save_config(DeviceState *d, QEMUFile *f)
-{
-    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
-    pci_device_save(&proxy->pci_dev, f);
-    msix_save(&proxy->pci_dev, f);
-    if (msix_present(&proxy->pci_dev))
-        qemu_put_be16(f, proxy->vdev->config_vector);
-}
-
-static void virtio_pci_save_queue(DeviceState *d, int n, QEMUFile *f)
-{
-    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
-    if (msix_present(&proxy->pci_dev))
-        qemu_put_be16(f, virtio_queue_vector(proxy->vdev, n));
-}
-
-static int virtio_pci_load_config(DeviceState *d, QEMUFile *f)
-{
-    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
-    int ret;
-    ret = pci_device_load(&proxy->pci_dev, f);
-    if (ret) {
-        return ret;
-    }
-    msix_unuse_all_vectors(&proxy->pci_dev);
-    msix_load(&proxy->pci_dev, f);
-    if (msix_present(&proxy->pci_dev)) {
-        qemu_get_be16s(f, &proxy->vdev->config_vector);
-    } else {
-        proxy->vdev->config_vector = VIRTIO_NO_VECTOR;
-    }
-    if (proxy->vdev->config_vector != VIRTIO_NO_VECTOR) {
-        return msix_vector_use(&proxy->pci_dev, proxy->vdev->config_vector);
-    }
-    return 0;
-}
-
-static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f)
-{
-    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
-    uint16_t vector;
-    if (msix_present(&proxy->pci_dev)) {
-        qemu_get_be16s(f, &vector);
-    } else {
-        vector = VIRTIO_NO_VECTOR;
-    }
-    virtio_queue_set_vector(proxy->vdev, n, vector);
-    if (vector != VIRTIO_NO_VECTOR) {
-        return msix_vector_use(&proxy->pci_dev, vector);
-    }
-    return 0;
-}
-
-static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
-                                                 int n, bool assign, bool set_handler)
-{
-    VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
-    EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
-    int r = 0;
-
-    if (assign) {
-        r = event_notifier_init(notifier, 1);
-        if (r < 0) {
-            error_report("%s: unable to init event notifier: %d",
-                         __func__, r);
-            return r;
-        }
-        virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler);
-        memory_region_add_eventfd(&proxy->bar, VIRTIO_PCI_QUEUE_NOTIFY, 2,
-                                  true, n, notifier);
-    } else {
-        memory_region_del_eventfd(&proxy->bar, VIRTIO_PCI_QUEUE_NOTIFY, 2,
-                                  true, n, notifier);
-        virtio_queue_set_host_notifier_fd_handler(vq, false, false);
-        event_notifier_cleanup(notifier);
-    }
-    return r;
-}
-
-static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy)
-{
-    int n, r;
-
-    if (!(proxy->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD) ||
-        proxy->ioeventfd_disabled ||
-        proxy->ioeventfd_started) {
-        return;
-    }
-
-    for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
-        if (!virtio_queue_get_num(proxy->vdev, n)) {
-            continue;
-        }
-
-        r = virtio_pci_set_host_notifier_internal(proxy, n, true, true);
-        if (r < 0) {
-            goto assign_error;
-        }
-    }
-    proxy->ioeventfd_started = true;
-    return;
-
-assign_error:
-    while (--n >= 0) {
-        if (!virtio_queue_get_num(proxy->vdev, n)) {
-            continue;
-        }
-
-        r = virtio_pci_set_host_notifier_internal(proxy, n, false, false);
-        assert(r >= 0);
-    }
-    proxy->ioeventfd_started = false;
-    error_report("%s: failed. Fallback to a userspace (slower).", __func__);
-}
-
-static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy)
-{
-    int r;
-    int n;
-
-    if (!proxy->ioeventfd_started) {
-        return;
-    }
-
-    for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
-        if (!virtio_queue_get_num(proxy->vdev, n)) {
-            continue;
-        }
-
-        r = virtio_pci_set_host_notifier_internal(proxy, n, false, false);
-        assert(r >= 0);
-    }
-    proxy->ioeventfd_started = false;
-}
-
-static void virtio_pci_reset(DeviceState *d)
-{
-    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
-    virtio_pci_stop_ioeventfd(proxy);
-    virtio_reset(proxy->vdev);
-    msix_unuse_all_vectors(&proxy->pci_dev);
-    proxy->flags &= ~VIRTIO_PCI_FLAG_BUS_MASTER_BUG;
-}
-
-static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
-{
-    VirtIOPCIProxy *proxy = opaque;
-    VirtIODevice *vdev = proxy->vdev;
-    hwaddr pa;
-
-    switch (addr) {
-    case VIRTIO_PCI_GUEST_FEATURES:
-       /* Guest does not negotiate properly?  We have to assume nothing. */
-       if (val & (1 << VIRTIO_F_BAD_FEATURE)) {
-            val = vdev->bad_features ? vdev->bad_features(vdev) : 0;
-       }
-        virtio_set_features(vdev, val);
-        break;
-    case VIRTIO_PCI_QUEUE_PFN:
-        pa = (hwaddr)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT;
-        if (pa == 0) {
-            virtio_pci_stop_ioeventfd(proxy);
-            virtio_reset(proxy->vdev);
-            msix_unuse_all_vectors(&proxy->pci_dev);
-        }
-        else
-            virtio_queue_set_addr(vdev, vdev->queue_sel, pa);
-        break;
-    case VIRTIO_PCI_QUEUE_SEL:
-        if (val < VIRTIO_PCI_QUEUE_MAX)
-            vdev->queue_sel = val;
-        break;
-    case VIRTIO_PCI_QUEUE_NOTIFY:
-        if (val < VIRTIO_PCI_QUEUE_MAX) {
-            virtio_queue_notify(vdev, val);
-        }
-        break;
-    case VIRTIO_PCI_STATUS:
-        if (!(val & VIRTIO_CONFIG_S_DRIVER_OK)) {
-            virtio_pci_stop_ioeventfd(proxy);
-        }
-
-        virtio_set_status(vdev, val & 0xFF);
-
-        if (val & VIRTIO_CONFIG_S_DRIVER_OK) {
-            virtio_pci_start_ioeventfd(proxy);
-        }
-
-        if (vdev->status == 0) {
-            virtio_reset(proxy->vdev);
-            msix_unuse_all_vectors(&proxy->pci_dev);
-        }
-
-        /* Linux before 2.6.34 sets the device as OK without enabling
-           the PCI device bus master bit. In this case we need to disable
-           some safety checks. */
-        if ((val & VIRTIO_CONFIG_S_DRIVER_OK) &&
-            !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) {
-            proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG;
-        }
-        break;
-    case VIRTIO_MSI_CONFIG_VECTOR:
-        msix_vector_unuse(&proxy->pci_dev, vdev->config_vector);
-        /* Make it possible for guest to discover an error took place. */
-        if (msix_vector_use(&proxy->pci_dev, val) < 0)
-            val = VIRTIO_NO_VECTOR;
-        vdev->config_vector = val;
-        break;
-    case VIRTIO_MSI_QUEUE_VECTOR:
-        msix_vector_unuse(&proxy->pci_dev,
-                          virtio_queue_vector(vdev, vdev->queue_sel));
-        /* Make it possible for guest to discover an error took place. */
-        if (msix_vector_use(&proxy->pci_dev, val) < 0)
-            val = VIRTIO_NO_VECTOR;
-        virtio_queue_set_vector(vdev, vdev->queue_sel, val);
-        break;
-    default:
-        error_report("%s: unexpected address 0x%x value 0x%x",
-                     __func__, addr, val);
-        break;
-    }
-}
-
-static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr)
-{
-    VirtIODevice *vdev = proxy->vdev;
-    uint32_t ret = 0xFFFFFFFF;
-
-    switch (addr) {
-    case VIRTIO_PCI_HOST_FEATURES:
-        ret = proxy->host_features;
-        break;
-    case VIRTIO_PCI_GUEST_FEATURES:
-        ret = vdev->guest_features;
-        break;
-    case VIRTIO_PCI_QUEUE_PFN:
-        ret = virtio_queue_get_addr(vdev, vdev->queue_sel)
-              >> VIRTIO_PCI_QUEUE_ADDR_SHIFT;
-        break;
-    case VIRTIO_PCI_QUEUE_NUM:
-        ret = virtio_queue_get_num(vdev, vdev->queue_sel);
-        break;
-    case VIRTIO_PCI_QUEUE_SEL:
-        ret = vdev->queue_sel;
-        break;
-    case VIRTIO_PCI_STATUS:
-        ret = vdev->status;
-        break;
-    case VIRTIO_PCI_ISR:
-        /* reading from the ISR also clears it. */
-        ret = vdev->isr;
-        vdev->isr = 0;
-        qemu_set_irq(proxy->pci_dev.irq[0], 0);
-        break;
-    case VIRTIO_MSI_CONFIG_VECTOR:
-        ret = vdev->config_vector;
-        break;
-    case VIRTIO_MSI_QUEUE_VECTOR:
-        ret = virtio_queue_vector(vdev, vdev->queue_sel);
-        break;
-    default:
-        break;
-    }
-
-    return ret;
-}
-
-static uint64_t virtio_pci_config_read(void *opaque, hwaddr addr,
-                                       unsigned size)
-{
-    VirtIOPCIProxy *proxy = opaque;
-    uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
-    uint64_t val = 0;
-    if (addr < config) {
-        return virtio_ioport_read(proxy, addr);
-    }
-    addr -= config;
-
-    switch (size) {
-    case 1:
-        val = virtio_config_readb(proxy->vdev, addr);
-        break;
-    case 2:
-        val = virtio_config_readw(proxy->vdev, addr);
-        if (virtio_is_big_endian()) {
-            val = bswap16(val);
-        }
-        break;
-    case 4:
-        val = virtio_config_readl(proxy->vdev, addr);
-        if (virtio_is_big_endian()) {
-            val = bswap32(val);
-        }
-        break;
-    }
-    return val;
-}
-
-static void virtio_pci_config_write(void *opaque, hwaddr addr,
-                                    uint64_t val, unsigned size)
-{
-    VirtIOPCIProxy *proxy = opaque;
-    uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
-    if (addr < config) {
-        virtio_ioport_write(proxy, addr, val);
-        return;
-    }
-    addr -= config;
-    /*
-     * Virtio-PCI is odd. Ioports are LE but config space is target native
-     * endian.
-     */
-    switch (size) {
-    case 1:
-        virtio_config_writeb(proxy->vdev, addr, val);
-        break;
-    case 2:
-        if (virtio_is_big_endian()) {
-            val = bswap16(val);
-        }
-        virtio_config_writew(proxy->vdev, addr, val);
-        break;
-    case 4:
-        if (virtio_is_big_endian()) {
-            val = bswap32(val);
-        }
-        virtio_config_writel(proxy->vdev, addr, val);
-        break;
-    }
-}
-
-static const MemoryRegionOps virtio_pci_config_ops = {
-    .read = virtio_pci_config_read,
-    .write = virtio_pci_config_write,
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 4,
-    },
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,
-                                uint32_t val, int len)
-{
-    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
-
-    pci_default_write_config(pci_dev, address, val, len);
-
-    if (range_covers_byte(address, len, PCI_COMMAND) &&
-        !(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER) &&
-        !(proxy->flags & VIRTIO_PCI_FLAG_BUS_MASTER_BUG)) {
-        virtio_pci_stop_ioeventfd(proxy);
-        virtio_set_status(proxy->vdev,
-                          proxy->vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK);
-    }
-}
-
-static unsigned virtio_pci_get_features(DeviceState *d)
-{
-    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
-    return proxy->host_features;
-}
-
-static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy,
-                                        unsigned int queue_no,
-                                        unsigned int vector,
-                                        MSIMessage msg)
-{
-    VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
-    int ret;
-
-    if (irqfd->users == 0) {
-        ret = kvm_irqchip_add_msi_route(kvm_state, msg);
-        if (ret < 0) {
-            return ret;
-        }
-        irqfd->virq = ret;
-    }
-    irqfd->users++;
-    return 0;
-}
-
-static void kvm_virtio_pci_vq_vector_release(VirtIOPCIProxy *proxy,
-                                             unsigned int vector)
-{
-    VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
-    if (--irqfd->users == 0) {
-        kvm_irqchip_release_virq(kvm_state, irqfd->virq);
-    }
-}
-
-static int kvm_virtio_pci_irqfd_use(VirtIOPCIProxy *proxy,
-                                 unsigned int queue_no,
-                                 unsigned int vector)
-{
-    VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
-    VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
-    EventNotifier *n = virtio_queue_get_guest_notifier(vq);
-    int ret;
-    ret = kvm_irqchip_add_irqfd_notifier(kvm_state, n, irqfd->virq);
-    return ret;
-}
-
-static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy,
-                                      unsigned int queue_no,
-                                      unsigned int vector)
-{
-    VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
-    EventNotifier *n = virtio_queue_get_guest_notifier(vq);
-    VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
-    int ret;
-
-    ret = kvm_irqchip_remove_irqfd_notifier(kvm_state, n, irqfd->virq);
-    assert(ret == 0);
-}
-
-static int kvm_virtio_pci_vector_use(VirtIOPCIProxy *proxy, int nvqs)
-{
-    PCIDevice *dev = &proxy->pci_dev;
-    VirtIODevice *vdev = proxy->vdev;
-    unsigned int vector;
-    int ret, queue_no;
-    MSIMessage msg;
-
-    for (queue_no = 0; queue_no < nvqs; queue_no++) {
-        if (!virtio_queue_get_num(vdev, queue_no)) {
-            break;
-        }
-        vector = virtio_queue_vector(vdev, queue_no);
-        if (vector >= msix_nr_vectors_allocated(dev)) {
-            continue;
-        }
-        msg = msix_get_message(dev, vector);
-        ret = kvm_virtio_pci_vq_vector_use(proxy, queue_no, vector, msg);
-        if (ret < 0) {
-            goto undo;
-        }
-        /* If guest supports masking, set up irqfd now.
-         * Otherwise, delay until unmasked in the frontend.
-         */
-        if (proxy->vdev->guest_notifier_mask) {
-            ret = kvm_virtio_pci_irqfd_use(proxy, queue_no, vector);
-            if (ret < 0) {
-                kvm_virtio_pci_vq_vector_release(proxy, vector);
-                goto undo;
-            }
-        }
-    }
-    return 0;
-
-undo:
-    while (--queue_no >= 0) {
-        vector = virtio_queue_vector(vdev, queue_no);
-        if (vector >= msix_nr_vectors_allocated(dev)) {
-            continue;
-        }
-        if (proxy->vdev->guest_notifier_mask) {
-            kvm_virtio_pci_irqfd_release(proxy, queue_no, vector);
-        }
-        kvm_virtio_pci_vq_vector_release(proxy, vector);
-    }
-    return ret;
-}
-
-static void kvm_virtio_pci_vector_release(VirtIOPCIProxy *proxy, int nvqs)
-{
-    PCIDevice *dev = &proxy->pci_dev;
-    VirtIODevice *vdev = proxy->vdev;
-    unsigned int vector;
-    int queue_no;
-
-    for (queue_no = 0; queue_no < nvqs; queue_no++) {
-        if (!virtio_queue_get_num(vdev, queue_no)) {
-            break;
-        }
-        vector = virtio_queue_vector(vdev, queue_no);
-        if (vector >= msix_nr_vectors_allocated(dev)) {
-            continue;
-        }
-        /* If guest supports masking, clean up irqfd now.
-         * Otherwise, it was cleaned when masked in the frontend.
-         */
-        if (proxy->vdev->guest_notifier_mask) {
-            kvm_virtio_pci_irqfd_release(proxy, queue_no, vector);
-        }
-        kvm_virtio_pci_vq_vector_release(proxy, vector);
-    }
-}
-
-static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy,
-                                       unsigned int queue_no,
-                                       unsigned int vector,
-                                       MSIMessage msg)
-{
-    VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
-    EventNotifier *n = virtio_queue_get_guest_notifier(vq);
-    VirtIOIRQFD *irqfd;
-    int ret = 0;
-
-    if (proxy->vector_irqfd) {
-        irqfd = &proxy->vector_irqfd[vector];
-        if (irqfd->msg.data != msg.data || irqfd->msg.address != msg.address) {
-            ret = kvm_irqchip_update_msi_route(kvm_state, irqfd->virq, msg);
-            if (ret < 0) {
-                return ret;
-            }
-        }
-    }
-
-    /* If guest supports masking, irqfd is already setup, unmask it.
-     * Otherwise, set it up now.
-     */
-    if (proxy->vdev->guest_notifier_mask) {
-        proxy->vdev->guest_notifier_mask(proxy->vdev, queue_no, false);
-        /* Test after unmasking to avoid losing events. */
-        if (proxy->vdev->guest_notifier_pending &&
-            proxy->vdev->guest_notifier_pending(proxy->vdev, queue_no)) {
-            event_notifier_set(n);
-        }
-    } else {
-        ret = kvm_virtio_pci_irqfd_use(proxy, queue_no, vector);
-    }
-    return ret;
-}
-
-static void virtio_pci_vq_vector_mask(VirtIOPCIProxy *proxy,
-                                             unsigned int queue_no,
-                                             unsigned int vector)
-{
-    /* If guest supports masking, keep irqfd but mask it.
-     * Otherwise, clean it up now.
-     */ 
-    if (proxy->vdev->guest_notifier_mask) {
-        proxy->vdev->guest_notifier_mask(proxy->vdev, queue_no, true);
-    } else {
-        kvm_virtio_pci_irqfd_release(proxy, queue_no, vector);
-    }
-}
-
-static int virtio_pci_vector_unmask(PCIDevice *dev, unsigned vector,
-                                    MSIMessage msg)
-{
-    VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
-    VirtIODevice *vdev = proxy->vdev;
-    int ret, queue_no;
-
-    for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) {
-        if (!virtio_queue_get_num(vdev, queue_no)) {
-            break;
-        }
-        if (virtio_queue_vector(vdev, queue_no) != vector) {
-            continue;
-        }
-        ret = virtio_pci_vq_vector_unmask(proxy, queue_no, vector, msg);
-        if (ret < 0) {
-            goto undo;
-        }
-    }
-    return 0;
-
-undo:
-    while (--queue_no >= 0) {
-        if (virtio_queue_vector(vdev, queue_no) != vector) {
-            continue;
-        }
-        virtio_pci_vq_vector_mask(proxy, queue_no, vector);
-    }
-    return ret;
-}
-
-static void virtio_pci_vector_mask(PCIDevice *dev, unsigned vector)
-{
-    VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
-    VirtIODevice *vdev = proxy->vdev;
-    int queue_no;
-
-    for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) {
-        if (!virtio_queue_get_num(vdev, queue_no)) {
-            break;
-        }
-        if (virtio_queue_vector(vdev, queue_no) != vector) {
-            continue;
-        }
-        virtio_pci_vq_vector_mask(proxy, queue_no, vector);
-    }
-}
-
-static void virtio_pci_vector_poll(PCIDevice *dev,
-                                   unsigned int vector_start,
-                                   unsigned int vector_end)
-{
-    VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
-    VirtIODevice *vdev = proxy->vdev;
-    int queue_no;
-    unsigned int vector;
-    EventNotifier *notifier;
-    VirtQueue *vq;
-
-    for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) {
-        if (!virtio_queue_get_num(vdev, queue_no)) {
-            break;
-        }
-        vector = virtio_queue_vector(vdev, queue_no);
-        if (vector < vector_start || vector >= vector_end ||
-            !msix_is_masked(dev, vector)) {
-            continue;
-        }
-        vq = virtio_get_queue(vdev, queue_no);
-        notifier = virtio_queue_get_guest_notifier(vq);
-        if (vdev->guest_notifier_pending) {
-            if (vdev->guest_notifier_pending(vdev, queue_no)) {
-                msix_set_pending(dev, vector);
-            }
-        } else if (event_notifier_test_and_clear(notifier)) {
-            msix_set_pending(dev, vector);
-        }
-    }
-}
-
-static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign,
-                                         bool with_irqfd)
-{
-    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
-    VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
-    EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
-
-    if (assign) {
-        int r = event_notifier_init(notifier, 0);
-        if (r < 0) {
-            return r;
-        }
-        virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd);
-    } else {
-        virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd);
-        event_notifier_cleanup(notifier);
-    }
-
-    return 0;
-}
-
-static bool virtio_pci_query_guest_notifiers(DeviceState *d)
-{
-    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
-    return msix_enabled(&proxy->pci_dev);
-}
-
-static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign)
-{
-    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
-    VirtIODevice *vdev = proxy->vdev;
-    int r, n;
-    bool with_irqfd = msix_enabled(&proxy->pci_dev) &&
-        kvm_msi_via_irqfd_enabled();
-
-    nvqs = MIN(nvqs, VIRTIO_PCI_QUEUE_MAX);
-
-    /* When deassigning, pass a consistent nvqs value
-     * to avoid leaking notifiers.
-     */
-    assert(assign || nvqs == proxy->nvqs_with_notifiers);
-
-    proxy->nvqs_with_notifiers = nvqs;
-
-    /* Must unset vector notifier while guest notifier is still assigned */
-    if ((proxy->vector_irqfd || vdev->guest_notifier_mask) && !assign) {
-        msix_unset_vector_notifiers(&proxy->pci_dev);
-        if (proxy->vector_irqfd) {
-            kvm_virtio_pci_vector_release(proxy, nvqs);
-            g_free(proxy->vector_irqfd);
-            proxy->vector_irqfd = NULL;
-        }
-    }
-
-    for (n = 0; n < nvqs; n++) {
-        if (!virtio_queue_get_num(vdev, n)) {
-            break;
-        }
-
-        r = virtio_pci_set_guest_notifier(d, n, assign,
-                                          kvm_msi_via_irqfd_enabled());
-        if (r < 0) {
-            goto assign_error;
-        }
-    }
-
-    /* Must set vector notifier after guest notifier has been assigned */
-    if ((with_irqfd || vdev->guest_notifier_mask) && assign) {
-        if (with_irqfd) {
-            proxy->vector_irqfd =
-                g_malloc0(sizeof(*proxy->vector_irqfd) *
-                          msix_nr_vectors_allocated(&proxy->pci_dev));
-            r = kvm_virtio_pci_vector_use(proxy, nvqs);
-            if (r < 0) {
-                goto assign_error;
-            }
-        }
-        r = msix_set_vector_notifiers(&proxy->pci_dev,
-                                      virtio_pci_vector_unmask,
-                                      virtio_pci_vector_mask,
-                                      virtio_pci_vector_poll);
-        if (r < 0) {
-            goto notifiers_error;
-        }
-    }
-
-    return 0;
-
-notifiers_error:
-    if (with_irqfd) {
-        assert(assign);
-        kvm_virtio_pci_vector_release(proxy, nvqs);
-    }
-
-assign_error:
-    /* We get here on assignment failure. Recover by undoing for VQs 0 .. n. */
-    assert(assign);
-    while (--n >= 0) {
-        virtio_pci_set_guest_notifier(d, n, !assign, with_irqfd);
-    }
-    return r;
-}
-
-static int virtio_pci_set_host_notifier(DeviceState *d, int n, bool assign)
-{
-    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
-
-    /* Stop using ioeventfd for virtqueue kick if the device starts using host
-     * notifiers.  This makes it easy to avoid stepping on each others' toes.
-     */
-    proxy->ioeventfd_disabled = assign;
-    if (assign) {
-        virtio_pci_stop_ioeventfd(proxy);
-    }
-    /* We don't need to start here: it's not needed because backend
-     * currently only stops on status change away from ok,
-     * reset, vmstop and such. If we do add code to start here,
-     * need to check vmstate, device state etc. */
-    return virtio_pci_set_host_notifier_internal(proxy, n, assign, false);
-}
-
-static void virtio_pci_vmstate_change(DeviceState *d, bool running)
-{
-    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
-
-    if (running) {
-        /* Try to find out if the guest has bus master disabled, but is
-           in ready state. Then we have a buggy guest OS. */
-        if ((proxy->vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) &&
-            !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) {
-            proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG;
-        }
-        virtio_pci_start_ioeventfd(proxy);
-    } else {
-        virtio_pci_stop_ioeventfd(proxy);
-    }
-}
-
-static const VirtIOBindings virtio_pci_bindings = {
-    .notify = virtio_pci_notify,
-    .save_config = virtio_pci_save_config,
-    .load_config = virtio_pci_load_config,
-    .save_queue = virtio_pci_save_queue,
-    .load_queue = virtio_pci_load_queue,
-    .get_features = virtio_pci_get_features,
-    .query_guest_notifiers = virtio_pci_query_guest_notifiers,
-    .set_host_notifier = virtio_pci_set_host_notifier,
-    .set_guest_notifiers = virtio_pci_set_guest_notifiers,
-    .vmstate_change = virtio_pci_vmstate_change,
-};
-
-void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev)
-{
-    uint8_t *config;
-    uint32_t size;
-
-    proxy->vdev = vdev;
-
-    config = proxy->pci_dev.config;
-
-    if (proxy->class_code) {
-        pci_config_set_class(config, proxy->class_code);
-    }
-    pci_set_word(config + PCI_SUBSYSTEM_VENDOR_ID,
-                 pci_get_word(config + PCI_VENDOR_ID));
-    pci_set_word(config + PCI_SUBSYSTEM_ID, vdev->device_id);
-    config[PCI_INTERRUPT_PIN] = 1;
-
-    if (vdev->nvectors &&
-        msix_init_exclusive_bar(&proxy->pci_dev, vdev->nvectors, 1)) {
-        vdev->nvectors = 0;
-    }
-
-    proxy->pci_dev.config_write = virtio_write_config;
-
-    size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev) + vdev->config_len;
-    if (size & (size-1))
-        size = 1 << qemu_fls(size);
-
-    memory_region_init_io(&proxy->bar, &virtio_pci_config_ops, proxy,
-                          "virtio-pci", size);
-    pci_register_bar(&proxy->pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO,
-                     &proxy->bar);
-
-    if (!kvm_has_many_ioeventfds()) {
-        proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD;
-    }
-
-    virtio_bind_device(vdev, &virtio_pci_bindings, DEVICE(proxy));
-    proxy->host_features |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY;
-    proxy->host_features |= 0x1 << VIRTIO_F_BAD_FEATURE;
-    proxy->host_features = vdev->get_features(vdev, proxy->host_features);
-}
-
-static void virtio_exit_pci(PCIDevice *pci_dev)
-{
-    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
-
-    memory_region_destroy(&proxy->bar);
-    msix_uninit_exclusive_bar(pci_dev);
-}
-
-static int virtio_serial_init_pci(PCIDevice *pci_dev)
-{
-    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
-    VirtIODevice *vdev;
-
-    if (proxy->class_code != PCI_CLASS_COMMUNICATION_OTHER &&
-        proxy->class_code != PCI_CLASS_DISPLAY_OTHER && /* qemu 0.10 */
-        proxy->class_code != PCI_CLASS_OTHERS)          /* qemu-kvm  */
-        proxy->class_code = PCI_CLASS_COMMUNICATION_OTHER;
-
-    vdev = virtio_serial_init(&pci_dev->qdev, &proxy->serial);
-    if (!vdev) {
-        return -1;
-    }
-
-    /* backwards-compatibility with machines that were created with
-       DEV_NVECTORS_UNSPECIFIED */
-    vdev->nvectors = proxy->nvectors == DEV_NVECTORS_UNSPECIFIED
-                                        ? proxy->serial.max_virtserial_ports + 1
-                                        : proxy->nvectors;
-    virtio_init_pci(proxy, vdev);
-    proxy->nvectors = vdev->nvectors;
-    return 0;
-}
-
-static void virtio_serial_exit_pci(PCIDevice *pci_dev)
-{
-    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
-
-    virtio_pci_stop_ioeventfd(proxy);
-    virtio_serial_exit(proxy->vdev);
-    virtio_exit_pci(pci_dev);
-}
-
-static int virtio_net_init_pci(PCIDevice *pci_dev)
-{
-    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
-    VirtIODevice *vdev;
-
-    vdev = virtio_net_init(&pci_dev->qdev, &proxy->nic, &proxy->net,
-                           proxy->host_features);
-
-    vdev->nvectors = proxy->nvectors;
-    virtio_init_pci(proxy, vdev);
-
-    /* make the actual value visible */
-    proxy->nvectors = vdev->nvectors;
-    return 0;
-}
-
-static void virtio_net_exit_pci(PCIDevice *pci_dev)
-{
-    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
-
-    virtio_pci_stop_ioeventfd(proxy);
-    virtio_net_exit(proxy->vdev);
-    virtio_exit_pci(pci_dev);
-}
-
-static int virtio_rng_init_pci(PCIDevice *pci_dev)
-{
-    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
-    VirtIODevice *vdev;
-
-    if (proxy->rng.rng == NULL) {
-        proxy->rng.default_backend = RNG_RANDOM(object_new(TYPE_RNG_RANDOM));
-
-        object_property_add_child(OBJECT(pci_dev),
-                                  "default-backend",
-                                  OBJECT(proxy->rng.default_backend),
-                                  NULL);
-
-        object_property_set_link(OBJECT(pci_dev),
-                                 OBJECT(proxy->rng.default_backend),
-                                 "rng", NULL);
-    }
-
-    vdev = virtio_rng_init(&pci_dev->qdev, &proxy->rng);
-    if (!vdev) {
-        return -1;
-    }
-    virtio_init_pci(proxy, vdev);
-    return 0;
-}
-
-static void virtio_rng_exit_pci(PCIDevice *pci_dev)
-{
-    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
-
-    virtio_pci_stop_ioeventfd(proxy);
-    virtio_rng_exit(proxy->vdev);
-    virtio_exit_pci(pci_dev);
-}
-
-static Property virtio_net_properties[] = {
-    DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false),
-    DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3),
-    DEFINE_VIRTIO_NET_FEATURES(VirtIOPCIProxy, host_features),
-    DEFINE_NIC_PROPERTIES(VirtIOPCIProxy, nic),
-    DEFINE_PROP_UINT32("x-txtimer", VirtIOPCIProxy, net.txtimer, TX_TIMER_INTERVAL),
-    DEFINE_PROP_INT32("x-txburst", VirtIOPCIProxy, net.txburst, TX_BURST),
-    DEFINE_PROP_STRING("tx", VirtIOPCIProxy, net.tx),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void virtio_net_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = virtio_net_init_pci;
-    k->exit = virtio_net_exit_pci;
-    k->romfile = "efi-virtio.rom";
-    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
-    k->device_id = PCI_DEVICE_ID_VIRTIO_NET;
-    k->revision = VIRTIO_PCI_ABI_VERSION;
-    k->class_id = PCI_CLASS_NETWORK_ETHERNET;
-    dc->reset = virtio_pci_reset;
-    dc->props = virtio_net_properties;
-}
-
-static const TypeInfo virtio_net_info = {
-    .name          = "virtio-net-pci",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(VirtIOPCIProxy),
-    .class_init    = virtio_net_class_init,
-};
-
-static Property virtio_serial_properties[] = {
-    DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
-    DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
-    DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
-    DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
-    DEFINE_PROP_UINT32("max_ports", VirtIOPCIProxy, serial.max_virtserial_ports, 31),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void virtio_serial_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = virtio_serial_init_pci;
-    k->exit = virtio_serial_exit_pci;
-    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
-    k->device_id = PCI_DEVICE_ID_VIRTIO_CONSOLE;
-    k->revision = VIRTIO_PCI_ABI_VERSION;
-    k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
-    dc->reset = virtio_pci_reset;
-    dc->props = virtio_serial_properties;
-}
-
-static const TypeInfo virtio_serial_info = {
-    .name          = "virtio-serial-pci",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(VirtIOPCIProxy),
-    .class_init    = virtio_serial_class_init,
-};
-
-static void virtio_rng_initfn(Object *obj)
-{
-    PCIDevice *pci_dev = PCI_DEVICE(obj);
-    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
-
-    object_property_add_link(obj, "rng", TYPE_RNG_BACKEND,
-                             (Object **)&proxy->rng.rng, NULL);
-}
-
-static Property virtio_rng_properties[] = {
-    DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
-    /* Set a default rate limit of 2^47 bytes per minute or roughly 2TB/s.  If
-       you have an entropy source capable of generating more entropy than this
-       and you can pass it through via virtio-rng, then hats off to you.  Until
-       then, this is unlimited for all practical purposes.
-    */
-    DEFINE_PROP_UINT64("max-bytes", VirtIOPCIProxy, rng.max_bytes, INT64_MAX),
-    DEFINE_PROP_UINT32("period", VirtIOPCIProxy, rng.period_ms, 1 << 16),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void virtio_rng_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = virtio_rng_init_pci;
-    k->exit = virtio_rng_exit_pci;
-    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
-    k->device_id = PCI_DEVICE_ID_VIRTIO_RNG;
-    k->revision = VIRTIO_PCI_ABI_VERSION;
-    k->class_id = PCI_CLASS_OTHERS;
-    dc->reset = virtio_pci_reset;
-    dc->props = virtio_rng_properties;
-}
-
-static const TypeInfo virtio_rng_info = {
-    .name          = "virtio-rng-pci",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(VirtIOPCIProxy),
-    .instance_init = virtio_rng_initfn,
-    .class_init    = virtio_rng_class_init,
-};
-
-#ifdef CONFIG_VIRTFS
-static int virtio_9p_init_pci(PCIDevice *pci_dev)
-{
-    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
-    VirtIODevice *vdev;
-
-    vdev = virtio_9p_init(&pci_dev->qdev, &proxy->fsconf);
-    vdev->nvectors = proxy->nvectors;
-    virtio_init_pci(proxy, vdev);
-    /* make the actual value visible */
-    proxy->nvectors = vdev->nvectors;
-    return 0;
-}
-
-static Property virtio_9p_properties[] = {
-    DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
-    DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
-    DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
-    DEFINE_PROP_STRING("mount_tag", VirtIOPCIProxy, fsconf.tag),
-    DEFINE_PROP_STRING("fsdev", VirtIOPCIProxy, fsconf.fsdev_id),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void virtio_9p_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = virtio_9p_init_pci;
-    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
-    k->device_id = PCI_DEVICE_ID_VIRTIO_9P;
-    k->revision = VIRTIO_PCI_ABI_VERSION;
-    k->class_id = 0x2;
-    dc->props = virtio_9p_properties;
-    dc->reset = virtio_pci_reset;
-}
-
-static const TypeInfo virtio_9p_info = {
-    .name          = "virtio-9p-pci",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(VirtIOPCIProxy),
-    .class_init    = virtio_9p_class_init,
-};
-#endif
-
-/*
- * virtio-pci: This is the PCIDevice which has a virtio-pci-bus.
- */
-
-/* This is called by virtio-bus just after the device is plugged. */
-static void virtio_pci_device_plugged(DeviceState *d)
-{
-    VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
-    VirtioBusState *bus = &proxy->bus;
-    uint8_t *config;
-    uint32_t size;
-
-    proxy->vdev = bus->vdev;
-
-    config = proxy->pci_dev.config;
-    if (proxy->class_code) {
-        pci_config_set_class(config, proxy->class_code);
-    }
-    pci_set_word(config + PCI_SUBSYSTEM_VENDOR_ID,
-                 pci_get_word(config + PCI_VENDOR_ID));
-    pci_set_word(config + PCI_SUBSYSTEM_ID, virtio_bus_get_vdev_id(bus));
-    config[PCI_INTERRUPT_PIN] = 1;
-
-    if (proxy->nvectors &&
-        msix_init_exclusive_bar(&proxy->pci_dev, proxy->nvectors, 1)) {
-        proxy->nvectors = 0;
-    }
-
-    proxy->pci_dev.config_write = virtio_write_config;
-
-    size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev)
-         + virtio_bus_get_vdev_config_len(bus);
-    if (size & (size - 1)) {
-        size = 1 << qemu_fls(size);
-    }
-
-    memory_region_init_io(&proxy->bar, &virtio_pci_config_ops, proxy,
-                          "virtio-pci", size);
-    pci_register_bar(&proxy->pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO,
-                     &proxy->bar);
-
-    if (!kvm_has_many_ioeventfds()) {
-        proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD;
-    }
-
-    proxy->host_features |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY;
-    proxy->host_features |= 0x1 << VIRTIO_F_BAD_FEATURE;
-    proxy->host_features = virtio_bus_get_vdev_features(bus,
-                                                      proxy->host_features);
-}
-
-static int virtio_pci_init(PCIDevice *pci_dev)
-{
-    VirtIOPCIProxy *dev = VIRTIO_PCI(pci_dev);
-    VirtioPCIClass *k = VIRTIO_PCI_GET_CLASS(pci_dev);
-    virtio_pci_bus_new(&dev->bus, dev);
-    if (k->init != NULL) {
-        return k->init(dev);
-    }
-    return 0;
-}
-
-static void virtio_pci_exit(PCIDevice *pci_dev)
-{
-    VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev);
-    virtio_pci_stop_ioeventfd(proxy);
-    virtio_exit_pci(pci_dev);
-}
-
-/*
- * This will be renamed virtio_pci_reset at the end of the series.
- * virtio_pci_reset is still in use at this moment.
- */
-static void virtio_pci_rst(DeviceState *qdev)
-{
-    VirtIOPCIProxy *proxy = VIRTIO_PCI(qdev);
-    VirtioBusState *bus = VIRTIO_BUS(&proxy->bus);
-    virtio_pci_stop_ioeventfd(proxy);
-    virtio_bus_reset(bus);
-    msix_unuse_all_vectors(&proxy->pci_dev);
-    proxy->flags &= ~VIRTIO_PCI_FLAG_BUS_MASTER_BUG;
-}
-
-static void virtio_pci_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->init = virtio_pci_init;
-    k->exit = virtio_pci_exit;
-    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
-    k->revision = VIRTIO_PCI_ABI_VERSION;
-    k->class_id = PCI_CLASS_OTHERS;
-    dc->reset = virtio_pci_rst;
-}
-
-static const TypeInfo virtio_pci_info = {
-    .name          = TYPE_VIRTIO_PCI,
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(VirtIOPCIProxy),
-    .class_init    = virtio_pci_class_init,
-    .class_size    = sizeof(VirtioPCIClass),
-    .abstract      = true,
-};
-
-/* virtio-blk-pci */
-
-static Property virtio_blk_pci_properties[] = {
-    DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
-    DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
-                    VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
-    DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
-#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
-    DEFINE_PROP_BIT("x-data-plane", VirtIOBlkPCI, blk.data_plane, 0, false),
-#endif
-    DEFINE_VIRTIO_BLK_FEATURES(VirtIOPCIProxy, host_features),
-    DEFINE_VIRTIO_BLK_PROPERTIES(VirtIOBlkPCI, blk),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static int virtio_blk_pci_init(VirtIOPCIProxy *vpci_dev)
-{
-    VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(vpci_dev);
-    DeviceState *vdev = DEVICE(&dev->vdev);
-    virtio_blk_set_conf(vdev, &(dev->blk));
-    qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
-    if (qdev_init(vdev) < 0) {
-        return -1;
-    }
-    return 0;
-}
-
-static void virtio_blk_pci_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
-    PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
-
-    dc->props = virtio_blk_pci_properties;
-    k->init = virtio_blk_pci_init;
-    pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
-    pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK;
-    pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
-    pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
-}
-
-static void virtio_blk_pci_instance_init(Object *obj)
-{
-    VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(obj);
-    object_initialize(OBJECT(&dev->vdev), TYPE_VIRTIO_BLK);
-    object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
-}
-
-static const TypeInfo virtio_blk_pci_info = {
-    .name          = TYPE_VIRTIO_BLK_PCI,
-    .parent        = TYPE_VIRTIO_PCI,
-    .instance_size = sizeof(VirtIOBlkPCI),
-    .instance_init = virtio_blk_pci_instance_init,
-    .class_init    = virtio_blk_pci_class_init,
-};
-
-/* virtio-scsi-pci */
-
-static Property virtio_scsi_pci_properties[] = {
-    DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
-                    VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
-    DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
-                       DEV_NVECTORS_UNSPECIFIED),
-    DEFINE_VIRTIO_SCSI_FEATURES(VirtIOPCIProxy, host_features),
-    DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSIPCI, vdev.conf),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static int virtio_scsi_pci_init_pci(VirtIOPCIProxy *vpci_dev)
-{
-    VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(vpci_dev);
-    DeviceState *vdev = DEVICE(&dev->vdev);
-
-    if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
-        vpci_dev->nvectors = dev->vdev.conf.num_queues + 3;
-    }
-
-    qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
-    if (qdev_init(vdev) < 0) {
-        return -1;
-    }
-    return 0;
-}
-
-static void virtio_scsi_pci_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
-    PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
-    k->init = virtio_scsi_pci_init_pci;
-    dc->props = virtio_scsi_pci_properties;
-    pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
-    pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI;
-    pcidev_k->revision = 0x00;
-    pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
-}
-
-static void virtio_scsi_pci_instance_init(Object *obj)
-{
-    VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(obj);
-    object_initialize(OBJECT(&dev->vdev), TYPE_VIRTIO_SCSI);
-    object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
-}
-
-static const TypeInfo virtio_scsi_pci_info = {
-    .name          = TYPE_VIRTIO_SCSI_PCI,
-    .parent        = TYPE_VIRTIO_PCI,
-    .instance_size = sizeof(VirtIOSCSIPCI),
-    .instance_init = virtio_scsi_pci_instance_init,
-    .class_init    = virtio_scsi_pci_class_init,
-};
-
-/* virtio-balloon-pci */
-
-static Property virtio_balloon_pci_properties[] = {
-    DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
-    DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static int virtio_balloon_pci_init(VirtIOPCIProxy *vpci_dev)
-{
-    VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(vpci_dev);
-    DeviceState *vdev = DEVICE(&dev->vdev);
-
-    if (vpci_dev->class_code != PCI_CLASS_OTHERS &&
-        vpci_dev->class_code != PCI_CLASS_MEMORY_RAM) { /* qemu < 1.1 */
-        vpci_dev->class_code = PCI_CLASS_OTHERS;
-    }
-
-    qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
-    if (qdev_init(vdev) < 0) {
-        return -1;
-    }
-    return 0;
-}
-
-static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
-    PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
-    k->init = virtio_balloon_pci_init;
-    dc->props = virtio_balloon_pci_properties;
-    pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
-    pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BALLOON;
-    pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
-    pcidev_k->class_id = PCI_CLASS_OTHERS;
-}
-
-static void virtio_balloon_pci_instance_init(Object *obj)
-{
-    VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(obj);
-    object_initialize(OBJECT(&dev->vdev), TYPE_VIRTIO_BALLOON);
-    object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
-}
-
-static const TypeInfo virtio_balloon_pci_info = {
-    .name          = TYPE_VIRTIO_BALLOON_PCI,
-    .parent        = TYPE_VIRTIO_PCI,
-    .instance_size = sizeof(VirtIOBalloonPCI),
-    .instance_init = virtio_balloon_pci_instance_init,
-    .class_init    = virtio_balloon_pci_class_init,
-};
-
-/* virtio-pci-bus */
-
-void virtio_pci_bus_new(VirtioBusState *bus, VirtIOPCIProxy *dev)
-{
-    DeviceState *qdev = DEVICE(dev);
-    BusState *qbus;
-    qbus_create_inplace((BusState *)bus, TYPE_VIRTIO_PCI_BUS, qdev, NULL);
-    qbus = BUS(bus);
-    qbus->allow_hotplug = 1;
-}
-
-static void virtio_pci_bus_class_init(ObjectClass *klass, void *data)
-{
-    BusClass *bus_class = BUS_CLASS(klass);
-    VirtioBusClass *k = VIRTIO_BUS_CLASS(klass);
-    bus_class->max_dev = 1;
-    k->notify = virtio_pci_notify;
-    k->save_config = virtio_pci_save_config;
-    k->load_config = virtio_pci_load_config;
-    k->save_queue = virtio_pci_save_queue;
-    k->load_queue = virtio_pci_load_queue;
-    k->get_features = virtio_pci_get_features;
-    k->query_guest_notifiers = virtio_pci_query_guest_notifiers;
-    k->set_host_notifier = virtio_pci_set_host_notifier;
-    k->set_guest_notifiers = virtio_pci_set_guest_notifiers;
-    k->vmstate_change = virtio_pci_vmstate_change;
-    k->device_plugged = virtio_pci_device_plugged;
-}
-
-static const TypeInfo virtio_pci_bus_info = {
-    .name          = TYPE_VIRTIO_PCI_BUS,
-    .parent        = TYPE_VIRTIO_BUS,
-    .instance_size = sizeof(VirtioPCIBusState),
-    .class_init    = virtio_pci_bus_class_init,
-};
-
-static void virtio_pci_register_types(void)
-{
-    type_register_static(&virtio_net_info);
-    type_register_static(&virtio_serial_info);
-    type_register_static(&virtio_rng_info);
-    type_register_static(&virtio_pci_bus_info);
-    type_register_static(&virtio_pci_info);
-#ifdef CONFIG_VIRTFS
-    type_register_static(&virtio_9p_info);
-#endif
-    type_register_static(&virtio_blk_pci_info);
-    type_register_static(&virtio_scsi_pci_info);
-    type_register_static(&virtio_balloon_pci_info);
-}
-
-type_init(virtio_pci_register_types)
diff --git a/hw/virtio-rng.c b/hw/virtio-rng.c
deleted file mode 100644 (file)
index 6079b2a..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * A virtio device implementing a hardware random number generator.
- *
- * Copyright 2012 Red Hat, Inc.
- * Copyright 2012 Amit Shah <amit.shah@redhat.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or
- * (at your option) any later version.  See the COPYING file in the
- * top-level directory.
- */
-
-#include "qemu/iov.h"
-#include "hw/qdev.h"
-#include "qapi/qmp/qerror.h"
-#include "hw/virtio/virtio.h"
-#include "hw/virtio/virtio-rng.h"
-#include "qemu/rng.h"
-
-static bool is_guest_ready(VirtIORNG *vrng)
-{
-    if (virtio_queue_ready(vrng->vq)
-        && (vrng->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
-        return true;
-    }
-    return false;
-}
-
-static size_t get_request_size(VirtQueue *vq, unsigned quota)
-{
-    unsigned int in, out;
-
-    virtqueue_get_avail_bytes(vq, &in, &out, quota, 0);
-    return in;
-}
-
-static void virtio_rng_process(VirtIORNG *vrng);
-
-/* Send data from a char device over to the guest */
-static void chr_read(void *opaque, const void *buf, size_t size)
-{
-    VirtIORNG *vrng = opaque;
-    VirtQueueElement elem;
-    size_t len;
-    int offset;
-
-    if (!is_guest_ready(vrng)) {
-        return;
-    }
-
-    vrng->quota_remaining -= size;
-
-    offset = 0;
-    while (offset < size) {
-        if (!virtqueue_pop(vrng->vq, &elem)) {
-            break;
-        }
-        len = iov_from_buf(elem.in_sg, elem.in_num,
-                           0, buf + offset, size - offset);
-        offset += len;
-
-        virtqueue_push(vrng->vq, &elem, len);
-    }
-    virtio_notify(&vrng->vdev, vrng->vq);
-}
-
-static void virtio_rng_process(VirtIORNG *vrng)
-{
-    size_t size;
-    unsigned quota;
-
-    if (!is_guest_ready(vrng)) {
-        return;
-    }
-
-    if (vrng->quota_remaining < 0) {
-        quota = 0;
-    } else {
-        quota = MIN((uint64_t)vrng->quota_remaining, (uint64_t)UINT32_MAX);
-    }
-    size = get_request_size(vrng->vq, quota);
-    size = MIN(vrng->quota_remaining, size);
-    if (size) {
-        rng_backend_request_entropy(vrng->rng, size, chr_read, vrng);
-    }
-}
-
-static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
-{
-    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
-    virtio_rng_process(vrng);
-}
-
-static uint32_t get_features(VirtIODevice *vdev, uint32_t f)
-{
-    return f;
-}
-
-static void virtio_rng_save(QEMUFile *f, void *opaque)
-{
-    VirtIORNG *vrng = opaque;
-
-    virtio_save(&vrng->vdev, f);
-}
-
-static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id)
-{
-    VirtIORNG *vrng = opaque;
-
-    if (version_id != 1) {
-        return -EINVAL;
-    }
-    virtio_load(&vrng->vdev, f);
-
-    /* We may have an element ready but couldn't process it due to a quota
-     * limit.  Make sure to try again after live migration when the quota may
-     * have been reset.
-     */
-    virtio_rng_process(vrng);
-
-    return 0;
-}
-
-static void check_rate_limit(void *opaque)
-{
-    VirtIORNG *s = opaque;
-
-    s->quota_remaining = s->conf->max_bytes;
-    virtio_rng_process(s);
-    qemu_mod_timer(s->rate_limit_timer,
-                   qemu_get_clock_ms(vm_clock) + s->conf->period_ms);
-}
-
-
-VirtIODevice *virtio_rng_init(DeviceState *dev, VirtIORNGConf *conf)
-{
-    VirtIORNG *vrng;
-    VirtIODevice *vdev;
-    Error *local_err = NULL;
-
-    vdev = virtio_common_init("virtio-rng", VIRTIO_ID_RNG, 0,
-                              sizeof(VirtIORNG));
-
-    vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
-
-    vrng->rng = conf->rng;
-    if (vrng->rng == NULL) {
-        qerror_report(QERR_INVALID_PARAMETER_VALUE, "rng", "a valid object");
-        return NULL;
-    }
-
-    rng_backend_open(vrng->rng, &local_err);
-    if (local_err) {
-        qerror_report_err(local_err);
-        error_free(local_err);
-        return NULL;
-    }
-
-    vrng->vq = virtio_add_queue(vdev, 8, handle_input);
-    vrng->vdev.get_features = get_features;
-
-    vrng->qdev = dev;
-    vrng->conf = conf;
-
-    assert(vrng->conf->max_bytes <= INT64_MAX);
-    vrng->quota_remaining = vrng->conf->max_bytes;
-
-    vrng->rate_limit_timer = qemu_new_timer_ms(vm_clock,
-                                               check_rate_limit, vrng);
-
-    qemu_mod_timer(vrng->rate_limit_timer,
-                   qemu_get_clock_ms(vm_clock) + vrng->conf->period_ms);
-
-    register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save,
-                    virtio_rng_load, vrng);
-
-    return vdev;
-}
-
-void virtio_rng_exit(VirtIODevice *vdev)
-{
-    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
-
-    qemu_del_timer(vrng->rate_limit_timer);
-    qemu_free_timer(vrng->rate_limit_timer);
-    unregister_savevm(vrng->qdev, "virtio-rng", vrng);
-    virtio_cleanup(vdev);
-}
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ed63495a7f619fececb53a25fadf8861ad539dca 100644 (file)
@@ -0,0 +1,4 @@
+common-obj-$(CONFIG_VIRTIO) += virtio-rng.o
+common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
+common-obj-$(CONFIG_VIRTIO) += virtio-bus.o
+
diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c
new file mode 100644 (file)
index 0000000..1596a1c
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * VirtioBus
+ *
+ *  Copyright (C) 2012 : GreenSocs Ltd
+ *      http://www.greensocs.com/ , email: info@greensocs.com
+ *
+ *  Developed by :
+ *  Frederic Konrad   <fred.konrad@greensocs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "hw/hw.h"
+#include "qemu/error-report.h"
+#include "hw/qdev.h"
+#include "hw/virtio/virtio-bus.h"
+#include "hw/virtio/virtio.h"
+
+/* #define DEBUG_VIRTIO_BUS */
+
+#ifdef DEBUG_VIRTIO_BUS
+#define DPRINTF(fmt, ...) \
+do { printf("virtio_bus: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+/* Plug the VirtIODevice */
+int virtio_bus_plug_device(VirtIODevice *vdev)
+{
+    DeviceState *qdev = DEVICE(vdev);
+    BusState *qbus = BUS(qdev_get_parent_bus(qdev));
+    VirtioBusState *bus = VIRTIO_BUS(qbus);
+    VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
+    DPRINTF("%s: plug device.\n", qbus->name);
+
+    bus->vdev = vdev;
+
+    /*
+     * The lines below will disappear when we drop VirtIOBindings, at the end
+     * of the series.
+     */
+    bus->bindings.notify = klass->notify;
+    bus->bindings.save_config = klass->save_config;
+    bus->bindings.save_queue = klass->save_queue;
+    bus->bindings.load_config = klass->load_config;
+    bus->bindings.load_queue = klass->load_queue;
+    bus->bindings.load_done = klass->load_done;
+    bus->bindings.get_features = klass->get_features;
+    bus->bindings.query_guest_notifiers = klass->query_guest_notifiers;
+    bus->bindings.set_guest_notifiers = klass->set_guest_notifiers;
+    bus->bindings.set_host_notifier = klass->set_host_notifier;
+    bus->bindings.vmstate_change = klass->vmstate_change;
+    virtio_bind_device(bus->vdev, &bus->bindings, qbus->parent);
+
+    if (klass->device_plugged != NULL) {
+        klass->device_plugged(qbus->parent);
+    }
+
+    return 0;
+}
+
+/* Reset the virtio_bus */
+void virtio_bus_reset(VirtioBusState *bus)
+{
+    DPRINTF("%s: reset device.\n", qbus->name);
+    if (bus->vdev != NULL) {
+        virtio_reset(bus->vdev);
+    }
+}
+
+/* Destroy the VirtIODevice */
+void virtio_bus_destroy_device(VirtioBusState *bus)
+{
+    DeviceState *qdev;
+    BusState *qbus = BUS(bus);
+    VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
+    DPRINTF("%s: remove device.\n", qbus->name);
+
+    if (bus->vdev != NULL) {
+        if (klass->device_unplug != NULL) {
+            klass->device_unplug(qbus->parent);
+        }
+        qdev = DEVICE(bus->vdev);
+        qdev_free(qdev);
+        bus->vdev = NULL;
+    }
+}
+
+/* Get the device id of the plugged device. */
+uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus)
+{
+    assert(bus->vdev != NULL);
+    return bus->vdev->device_id;
+}
+
+/* Get the config_len field of the plugged device. */
+size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus)
+{
+    assert(bus->vdev != NULL);
+    return bus->vdev->config_len;
+}
+
+/* Get the features of the plugged device. */
+uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus,
+                                    uint32_t requested_features)
+{
+    VirtioDeviceClass *k;
+    assert(bus->vdev != NULL);
+    k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
+    assert(k->get_features != NULL);
+    return k->get_features(bus->vdev, requested_features);
+}
+
+/* Get bad features of the plugged device. */
+uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus)
+{
+    VirtioDeviceClass *k;
+    assert(bus->vdev != NULL);
+    k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
+    if (k->bad_features != NULL) {
+        return k->bad_features(bus->vdev);
+    } else {
+        return 0;
+    }
+}
+
+/* Get config of the plugged device. */
+void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config)
+{
+    VirtioDeviceClass *k;
+    assert(bus->vdev != NULL);
+    k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
+    if (k->get_config != NULL) {
+        k->get_config(bus->vdev, config);
+    }
+}
+
+static const TypeInfo virtio_bus_info = {
+    .name = TYPE_VIRTIO_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(VirtioBusState),
+    .abstract = true,
+    .class_size = sizeof(VirtioBusClass),
+};
+
+static void virtio_register_types(void)
+{
+    type_register_static(&virtio_bus_info);
+}
+
+type_init(virtio_register_types)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
new file mode 100644 (file)
index 0000000..943b429
--- /dev/null
@@ -0,0 +1,1514 @@
+/*
+ * Virtio PCI Bindings
+ *
+ * Copyright IBM, Corp. 2007
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Paul Brook        <paul@codesourcery.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include <inttypes.h>
+
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-blk.h"
+#include "hw/virtio/virtio-net.h"
+#include "hw/virtio/virtio-serial.h"
+#include "hw/virtio/virtio-scsi.h"
+#include "hw/virtio/virtio-balloon.h"
+#include "hw/pci/pci.h"
+#include "qemu/error-report.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/msix.h"
+#include "hw/loader.h"
+#include "sysemu/kvm.h"
+#include "sysemu/blockdev.h"
+#include "hw/virtio-pci.h"
+#include "qemu/range.h"
+#include "hw/virtio/virtio-bus.h"
+
+/* from Linux's linux/virtio_pci.h */
+
+/* A 32-bit r/o bitmask of the features supported by the host */
+#define VIRTIO_PCI_HOST_FEATURES        0
+
+/* A 32-bit r/w bitmask of features activated by the guest */
+#define VIRTIO_PCI_GUEST_FEATURES       4
+
+/* A 32-bit r/w PFN for the currently selected queue */
+#define VIRTIO_PCI_QUEUE_PFN            8
+
+/* A 16-bit r/o queue size for the currently selected queue */
+#define VIRTIO_PCI_QUEUE_NUM            12
+
+/* A 16-bit r/w queue selector */
+#define VIRTIO_PCI_QUEUE_SEL            14
+
+/* A 16-bit r/w queue notifier */
+#define VIRTIO_PCI_QUEUE_NOTIFY         16
+
+/* An 8-bit device status register.  */
+#define VIRTIO_PCI_STATUS               18
+
+/* An 8-bit r/o interrupt status register.  Reading the value will return the
+ * current contents of the ISR and will also clear it.  This is effectively
+ * a read-and-acknowledge. */
+#define VIRTIO_PCI_ISR                  19
+
+/* MSI-X registers: only enabled if MSI-X is enabled. */
+/* A 16-bit vector for configuration changes. */
+#define VIRTIO_MSI_CONFIG_VECTOR        20
+/* A 16-bit vector for selected queue notifications. */
+#define VIRTIO_MSI_QUEUE_VECTOR         22
+
+/* Config space size */
+#define VIRTIO_PCI_CONFIG_NOMSI         20
+#define VIRTIO_PCI_CONFIG_MSI           24
+#define VIRTIO_PCI_REGION_SIZE(dev)     (msix_present(dev) ? \
+                                         VIRTIO_PCI_CONFIG_MSI : \
+                                         VIRTIO_PCI_CONFIG_NOMSI)
+
+/* The remaining space is defined by each driver as the per-driver
+ * configuration space */
+#define VIRTIO_PCI_CONFIG(dev)          (msix_enabled(dev) ? \
+                                         VIRTIO_PCI_CONFIG_MSI : \
+                                         VIRTIO_PCI_CONFIG_NOMSI)
+
+/* How many bits to shift physical queue address written to QUEUE_PFN.
+ * 12 is historical, and due to x86 page size. */
+#define VIRTIO_PCI_QUEUE_ADDR_SHIFT    12
+
+/* Flags track per-device state like workarounds for quirks in older guests. */
+#define VIRTIO_PCI_FLAG_BUS_MASTER_BUG  (1 << 0)
+
+/* QEMU doesn't strictly need write barriers since everything runs in
+ * lock-step.  We'll leave the calls to wmb() in though to make it obvious for
+ * KVM or if kqemu gets SMP support.
+ */
+#define wmb() do { } while (0)
+
+/* HACK for virtio to determine if it's running a big endian guest */
+bool virtio_is_big_endian(void);
+
+/* virtio device */
+/* DeviceState to VirtIOPCIProxy. For use off data-path. TODO: use QOM. */
+static inline VirtIOPCIProxy *to_virtio_pci_proxy(DeviceState *d)
+{
+    return container_of(d, VirtIOPCIProxy, pci_dev.qdev);
+}
+
+/* DeviceState to VirtIOPCIProxy. Note: used on datapath,
+ * be careful and test performance if you change this.
+ */
+static inline VirtIOPCIProxy *to_virtio_pci_proxy_fast(DeviceState *d)
+{
+    return container_of(d, VirtIOPCIProxy, pci_dev.qdev);
+}
+
+static void virtio_pci_notify(DeviceState *d, uint16_t vector)
+{
+    VirtIOPCIProxy *proxy = to_virtio_pci_proxy_fast(d);
+    if (msix_enabled(&proxy->pci_dev))
+        msix_notify(&proxy->pci_dev, vector);
+    else
+        qemu_set_irq(proxy->pci_dev.irq[0], proxy->vdev->isr & 1);
+}
+
+static void virtio_pci_save_config(DeviceState *d, QEMUFile *f)
+{
+    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+    pci_device_save(&proxy->pci_dev, f);
+    msix_save(&proxy->pci_dev, f);
+    if (msix_present(&proxy->pci_dev))
+        qemu_put_be16(f, proxy->vdev->config_vector);
+}
+
+static void virtio_pci_save_queue(DeviceState *d, int n, QEMUFile *f)
+{
+    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+    if (msix_present(&proxy->pci_dev))
+        qemu_put_be16(f, virtio_queue_vector(proxy->vdev, n));
+}
+
+static int virtio_pci_load_config(DeviceState *d, QEMUFile *f)
+{
+    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+    int ret;
+    ret = pci_device_load(&proxy->pci_dev, f);
+    if (ret) {
+        return ret;
+    }
+    msix_unuse_all_vectors(&proxy->pci_dev);
+    msix_load(&proxy->pci_dev, f);
+    if (msix_present(&proxy->pci_dev)) {
+        qemu_get_be16s(f, &proxy->vdev->config_vector);
+    } else {
+        proxy->vdev->config_vector = VIRTIO_NO_VECTOR;
+    }
+    if (proxy->vdev->config_vector != VIRTIO_NO_VECTOR) {
+        return msix_vector_use(&proxy->pci_dev, proxy->vdev->config_vector);
+    }
+    return 0;
+}
+
+static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f)
+{
+    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+    uint16_t vector;
+    if (msix_present(&proxy->pci_dev)) {
+        qemu_get_be16s(f, &vector);
+    } else {
+        vector = VIRTIO_NO_VECTOR;
+    }
+    virtio_queue_set_vector(proxy->vdev, n, vector);
+    if (vector != VIRTIO_NO_VECTOR) {
+        return msix_vector_use(&proxy->pci_dev, vector);
+    }
+    return 0;
+}
+
+static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
+                                                 int n, bool assign, bool set_handler)
+{
+    VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
+    EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
+    int r = 0;
+
+    if (assign) {
+        r = event_notifier_init(notifier, 1);
+        if (r < 0) {
+            error_report("%s: unable to init event notifier: %d",
+                         __func__, r);
+            return r;
+        }
+        virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler);
+        memory_region_add_eventfd(&proxy->bar, VIRTIO_PCI_QUEUE_NOTIFY, 2,
+                                  true, n, notifier);
+    } else {
+        memory_region_del_eventfd(&proxy->bar, VIRTIO_PCI_QUEUE_NOTIFY, 2,
+                                  true, n, notifier);
+        virtio_queue_set_host_notifier_fd_handler(vq, false, false);
+        event_notifier_cleanup(notifier);
+    }
+    return r;
+}
+
+static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy)
+{
+    int n, r;
+
+    if (!(proxy->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD) ||
+        proxy->ioeventfd_disabled ||
+        proxy->ioeventfd_started) {
+        return;
+    }
+
+    for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
+        if (!virtio_queue_get_num(proxy->vdev, n)) {
+            continue;
+        }
+
+        r = virtio_pci_set_host_notifier_internal(proxy, n, true, true);
+        if (r < 0) {
+            goto assign_error;
+        }
+    }
+    proxy->ioeventfd_started = true;
+    return;
+
+assign_error:
+    while (--n >= 0) {
+        if (!virtio_queue_get_num(proxy->vdev, n)) {
+            continue;
+        }
+
+        r = virtio_pci_set_host_notifier_internal(proxy, n, false, false);
+        assert(r >= 0);
+    }
+    proxy->ioeventfd_started = false;
+    error_report("%s: failed. Fallback to a userspace (slower).", __func__);
+}
+
+static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy)
+{
+    int r;
+    int n;
+
+    if (!proxy->ioeventfd_started) {
+        return;
+    }
+
+    for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
+        if (!virtio_queue_get_num(proxy->vdev, n)) {
+            continue;
+        }
+
+        r = virtio_pci_set_host_notifier_internal(proxy, n, false, false);
+        assert(r >= 0);
+    }
+    proxy->ioeventfd_started = false;
+}
+
+static void virtio_pci_reset(DeviceState *d)
+{
+    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+    virtio_pci_stop_ioeventfd(proxy);
+    virtio_reset(proxy->vdev);
+    msix_unuse_all_vectors(&proxy->pci_dev);
+    proxy->flags &= ~VIRTIO_PCI_FLAG_BUS_MASTER_BUG;
+}
+
+static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    VirtIOPCIProxy *proxy = opaque;
+    VirtIODevice *vdev = proxy->vdev;
+    hwaddr pa;
+
+    switch (addr) {
+    case VIRTIO_PCI_GUEST_FEATURES:
+       /* Guest does not negotiate properly?  We have to assume nothing. */
+       if (val & (1 << VIRTIO_F_BAD_FEATURE)) {
+            val = vdev->bad_features ? vdev->bad_features(vdev) : 0;
+       }
+        virtio_set_features(vdev, val);
+        break;
+    case VIRTIO_PCI_QUEUE_PFN:
+        pa = (hwaddr)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT;
+        if (pa == 0) {
+            virtio_pci_stop_ioeventfd(proxy);
+            virtio_reset(proxy->vdev);
+            msix_unuse_all_vectors(&proxy->pci_dev);
+        }
+        else
+            virtio_queue_set_addr(vdev, vdev->queue_sel, pa);
+        break;
+    case VIRTIO_PCI_QUEUE_SEL:
+        if (val < VIRTIO_PCI_QUEUE_MAX)
+            vdev->queue_sel = val;
+        break;
+    case VIRTIO_PCI_QUEUE_NOTIFY:
+        if (val < VIRTIO_PCI_QUEUE_MAX) {
+            virtio_queue_notify(vdev, val);
+        }
+        break;
+    case VIRTIO_PCI_STATUS:
+        if (!(val & VIRTIO_CONFIG_S_DRIVER_OK)) {
+            virtio_pci_stop_ioeventfd(proxy);
+        }
+
+        virtio_set_status(vdev, val & 0xFF);
+
+        if (val & VIRTIO_CONFIG_S_DRIVER_OK) {
+            virtio_pci_start_ioeventfd(proxy);
+        }
+
+        if (vdev->status == 0) {
+            virtio_reset(proxy->vdev);
+            msix_unuse_all_vectors(&proxy->pci_dev);
+        }
+
+        /* Linux before 2.6.34 sets the device as OK without enabling
+           the PCI device bus master bit. In this case we need to disable
+           some safety checks. */
+        if ((val & VIRTIO_CONFIG_S_DRIVER_OK) &&
+            !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) {
+            proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG;
+        }
+        break;
+    case VIRTIO_MSI_CONFIG_VECTOR:
+        msix_vector_unuse(&proxy->pci_dev, vdev->config_vector);
+        /* Make it possible for guest to discover an error took place. */
+        if (msix_vector_use(&proxy->pci_dev, val) < 0)
+            val = VIRTIO_NO_VECTOR;
+        vdev->config_vector = val;
+        break;
+    case VIRTIO_MSI_QUEUE_VECTOR:
+        msix_vector_unuse(&proxy->pci_dev,
+                          virtio_queue_vector(vdev, vdev->queue_sel));
+        /* Make it possible for guest to discover an error took place. */
+        if (msix_vector_use(&proxy->pci_dev, val) < 0)
+            val = VIRTIO_NO_VECTOR;
+        virtio_queue_set_vector(vdev, vdev->queue_sel, val);
+        break;
+    default:
+        error_report("%s: unexpected address 0x%x value 0x%x",
+                     __func__, addr, val);
+        break;
+    }
+}
+
+static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr)
+{
+    VirtIODevice *vdev = proxy->vdev;
+    uint32_t ret = 0xFFFFFFFF;
+
+    switch (addr) {
+    case VIRTIO_PCI_HOST_FEATURES:
+        ret = proxy->host_features;
+        break;
+    case VIRTIO_PCI_GUEST_FEATURES:
+        ret = vdev->guest_features;
+        break;
+    case VIRTIO_PCI_QUEUE_PFN:
+        ret = virtio_queue_get_addr(vdev, vdev->queue_sel)
+              >> VIRTIO_PCI_QUEUE_ADDR_SHIFT;
+        break;
+    case VIRTIO_PCI_QUEUE_NUM:
+        ret = virtio_queue_get_num(vdev, vdev->queue_sel);
+        break;
+    case VIRTIO_PCI_QUEUE_SEL:
+        ret = vdev->queue_sel;
+        break;
+    case VIRTIO_PCI_STATUS:
+        ret = vdev->status;
+        break;
+    case VIRTIO_PCI_ISR:
+        /* reading from the ISR also clears it. */
+        ret = vdev->isr;
+        vdev->isr = 0;
+        qemu_set_irq(proxy->pci_dev.irq[0], 0);
+        break;
+    case VIRTIO_MSI_CONFIG_VECTOR:
+        ret = vdev->config_vector;
+        break;
+    case VIRTIO_MSI_QUEUE_VECTOR:
+        ret = virtio_queue_vector(vdev, vdev->queue_sel);
+        break;
+    default:
+        break;
+    }
+
+    return ret;
+}
+
+static uint64_t virtio_pci_config_read(void *opaque, hwaddr addr,
+                                       unsigned size)
+{
+    VirtIOPCIProxy *proxy = opaque;
+    uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
+    uint64_t val = 0;
+    if (addr < config) {
+        return virtio_ioport_read(proxy, addr);
+    }
+    addr -= config;
+
+    switch (size) {
+    case 1:
+        val = virtio_config_readb(proxy->vdev, addr);
+        break;
+    case 2:
+        val = virtio_config_readw(proxy->vdev, addr);
+        if (virtio_is_big_endian()) {
+            val = bswap16(val);
+        }
+        break;
+    case 4:
+        val = virtio_config_readl(proxy->vdev, addr);
+        if (virtio_is_big_endian()) {
+            val = bswap32(val);
+        }
+        break;
+    }
+    return val;
+}
+
+static void virtio_pci_config_write(void *opaque, hwaddr addr,
+                                    uint64_t val, unsigned size)
+{
+    VirtIOPCIProxy *proxy = opaque;
+    uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
+    if (addr < config) {
+        virtio_ioport_write(proxy, addr, val);
+        return;
+    }
+    addr -= config;
+    /*
+     * Virtio-PCI is odd. Ioports are LE but config space is target native
+     * endian.
+     */
+    switch (size) {
+    case 1:
+        virtio_config_writeb(proxy->vdev, addr, val);
+        break;
+    case 2:
+        if (virtio_is_big_endian()) {
+            val = bswap16(val);
+        }
+        virtio_config_writew(proxy->vdev, addr, val);
+        break;
+    case 4:
+        if (virtio_is_big_endian()) {
+            val = bswap32(val);
+        }
+        virtio_config_writel(proxy->vdev, addr, val);
+        break;
+    }
+}
+
+static const MemoryRegionOps virtio_pci_config_ops = {
+    .read = virtio_pci_config_read,
+    .write = virtio_pci_config_write,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,
+                                uint32_t val, int len)
+{
+    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+
+    pci_default_write_config(pci_dev, address, val, len);
+
+    if (range_covers_byte(address, len, PCI_COMMAND) &&
+        !(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER) &&
+        !(proxy->flags & VIRTIO_PCI_FLAG_BUS_MASTER_BUG)) {
+        virtio_pci_stop_ioeventfd(proxy);
+        virtio_set_status(proxy->vdev,
+                          proxy->vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK);
+    }
+}
+
+static unsigned virtio_pci_get_features(DeviceState *d)
+{
+    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+    return proxy->host_features;
+}
+
+static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy,
+                                        unsigned int queue_no,
+                                        unsigned int vector,
+                                        MSIMessage msg)
+{
+    VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
+    int ret;
+
+    if (irqfd->users == 0) {
+        ret = kvm_irqchip_add_msi_route(kvm_state, msg);
+        if (ret < 0) {
+            return ret;
+        }
+        irqfd->virq = ret;
+    }
+    irqfd->users++;
+    return 0;
+}
+
+static void kvm_virtio_pci_vq_vector_release(VirtIOPCIProxy *proxy,
+                                             unsigned int vector)
+{
+    VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
+    if (--irqfd->users == 0) {
+        kvm_irqchip_release_virq(kvm_state, irqfd->virq);
+    }
+}
+
+static int kvm_virtio_pci_irqfd_use(VirtIOPCIProxy *proxy,
+                                 unsigned int queue_no,
+                                 unsigned int vector)
+{
+    VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
+    VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
+    EventNotifier *n = virtio_queue_get_guest_notifier(vq);
+    int ret;
+    ret = kvm_irqchip_add_irqfd_notifier(kvm_state, n, irqfd->virq);
+    return ret;
+}
+
+static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy,
+                                      unsigned int queue_no,
+                                      unsigned int vector)
+{
+    VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
+    EventNotifier *n = virtio_queue_get_guest_notifier(vq);
+    VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
+    int ret;
+
+    ret = kvm_irqchip_remove_irqfd_notifier(kvm_state, n, irqfd->virq);
+    assert(ret == 0);
+}
+
+static int kvm_virtio_pci_vector_use(VirtIOPCIProxy *proxy, int nvqs)
+{
+    PCIDevice *dev = &proxy->pci_dev;
+    VirtIODevice *vdev = proxy->vdev;
+    unsigned int vector;
+    int ret, queue_no;
+    MSIMessage msg;
+
+    for (queue_no = 0; queue_no < nvqs; queue_no++) {
+        if (!virtio_queue_get_num(vdev, queue_no)) {
+            break;
+        }
+        vector = virtio_queue_vector(vdev, queue_no);
+        if (vector >= msix_nr_vectors_allocated(dev)) {
+            continue;
+        }
+        msg = msix_get_message(dev, vector);
+        ret = kvm_virtio_pci_vq_vector_use(proxy, queue_no, vector, msg);
+        if (ret < 0) {
+            goto undo;
+        }
+        /* If guest supports masking, set up irqfd now.
+         * Otherwise, delay until unmasked in the frontend.
+         */
+        if (proxy->vdev->guest_notifier_mask) {
+            ret = kvm_virtio_pci_irqfd_use(proxy, queue_no, vector);
+            if (ret < 0) {
+                kvm_virtio_pci_vq_vector_release(proxy, vector);
+                goto undo;
+            }
+        }
+    }
+    return 0;
+
+undo:
+    while (--queue_no >= 0) {
+        vector = virtio_queue_vector(vdev, queue_no);
+        if (vector >= msix_nr_vectors_allocated(dev)) {
+            continue;
+        }
+        if (proxy->vdev->guest_notifier_mask) {
+            kvm_virtio_pci_irqfd_release(proxy, queue_no, vector);
+        }
+        kvm_virtio_pci_vq_vector_release(proxy, vector);
+    }
+    return ret;
+}
+
+static void kvm_virtio_pci_vector_release(VirtIOPCIProxy *proxy, int nvqs)
+{
+    PCIDevice *dev = &proxy->pci_dev;
+    VirtIODevice *vdev = proxy->vdev;
+    unsigned int vector;
+    int queue_no;
+
+    for (queue_no = 0; queue_no < nvqs; queue_no++) {
+        if (!virtio_queue_get_num(vdev, queue_no)) {
+            break;
+        }
+        vector = virtio_queue_vector(vdev, queue_no);
+        if (vector >= msix_nr_vectors_allocated(dev)) {
+            continue;
+        }
+        /* If guest supports masking, clean up irqfd now.
+         * Otherwise, it was cleaned when masked in the frontend.
+         */
+        if (proxy->vdev->guest_notifier_mask) {
+            kvm_virtio_pci_irqfd_release(proxy, queue_no, vector);
+        }
+        kvm_virtio_pci_vq_vector_release(proxy, vector);
+    }
+}
+
+static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy,
+                                       unsigned int queue_no,
+                                       unsigned int vector,
+                                       MSIMessage msg)
+{
+    VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
+    EventNotifier *n = virtio_queue_get_guest_notifier(vq);
+    VirtIOIRQFD *irqfd;
+    int ret = 0;
+
+    if (proxy->vector_irqfd) {
+        irqfd = &proxy->vector_irqfd[vector];
+        if (irqfd->msg.data != msg.data || irqfd->msg.address != msg.address) {
+            ret = kvm_irqchip_update_msi_route(kvm_state, irqfd->virq, msg);
+            if (ret < 0) {
+                return ret;
+            }
+        }
+    }
+
+    /* If guest supports masking, irqfd is already setup, unmask it.
+     * Otherwise, set it up now.
+     */
+    if (proxy->vdev->guest_notifier_mask) {
+        proxy->vdev->guest_notifier_mask(proxy->vdev, queue_no, false);
+        /* Test after unmasking to avoid losing events. */
+        if (proxy->vdev->guest_notifier_pending &&
+            proxy->vdev->guest_notifier_pending(proxy->vdev, queue_no)) {
+            event_notifier_set(n);
+        }
+    } else {
+        ret = kvm_virtio_pci_irqfd_use(proxy, queue_no, vector);
+    }
+    return ret;
+}
+
+static void virtio_pci_vq_vector_mask(VirtIOPCIProxy *proxy,
+                                             unsigned int queue_no,
+                                             unsigned int vector)
+{
+    /* If guest supports masking, keep irqfd but mask it.
+     * Otherwise, clean it up now.
+     */ 
+    if (proxy->vdev->guest_notifier_mask) {
+        proxy->vdev->guest_notifier_mask(proxy->vdev, queue_no, true);
+    } else {
+        kvm_virtio_pci_irqfd_release(proxy, queue_no, vector);
+    }
+}
+
+static int virtio_pci_vector_unmask(PCIDevice *dev, unsigned vector,
+                                    MSIMessage msg)
+{
+    VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
+    VirtIODevice *vdev = proxy->vdev;
+    int ret, queue_no;
+
+    for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) {
+        if (!virtio_queue_get_num(vdev, queue_no)) {
+            break;
+        }
+        if (virtio_queue_vector(vdev, queue_no) != vector) {
+            continue;
+        }
+        ret = virtio_pci_vq_vector_unmask(proxy, queue_no, vector, msg);
+        if (ret < 0) {
+            goto undo;
+        }
+    }
+    return 0;
+
+undo:
+    while (--queue_no >= 0) {
+        if (virtio_queue_vector(vdev, queue_no) != vector) {
+            continue;
+        }
+        virtio_pci_vq_vector_mask(proxy, queue_no, vector);
+    }
+    return ret;
+}
+
+static void virtio_pci_vector_mask(PCIDevice *dev, unsigned vector)
+{
+    VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
+    VirtIODevice *vdev = proxy->vdev;
+    int queue_no;
+
+    for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) {
+        if (!virtio_queue_get_num(vdev, queue_no)) {
+            break;
+        }
+        if (virtio_queue_vector(vdev, queue_no) != vector) {
+            continue;
+        }
+        virtio_pci_vq_vector_mask(proxy, queue_no, vector);
+    }
+}
+
+static void virtio_pci_vector_poll(PCIDevice *dev,
+                                   unsigned int vector_start,
+                                   unsigned int vector_end)
+{
+    VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
+    VirtIODevice *vdev = proxy->vdev;
+    int queue_no;
+    unsigned int vector;
+    EventNotifier *notifier;
+    VirtQueue *vq;
+
+    for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) {
+        if (!virtio_queue_get_num(vdev, queue_no)) {
+            break;
+        }
+        vector = virtio_queue_vector(vdev, queue_no);
+        if (vector < vector_start || vector >= vector_end ||
+            !msix_is_masked(dev, vector)) {
+            continue;
+        }
+        vq = virtio_get_queue(vdev, queue_no);
+        notifier = virtio_queue_get_guest_notifier(vq);
+        if (vdev->guest_notifier_pending) {
+            if (vdev->guest_notifier_pending(vdev, queue_no)) {
+                msix_set_pending(dev, vector);
+            }
+        } else if (event_notifier_test_and_clear(notifier)) {
+            msix_set_pending(dev, vector);
+        }
+    }
+}
+
+static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign,
+                                         bool with_irqfd)
+{
+    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+    VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
+    EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
+
+    if (assign) {
+        int r = event_notifier_init(notifier, 0);
+        if (r < 0) {
+            return r;
+        }
+        virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd);
+    } else {
+        virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd);
+        event_notifier_cleanup(notifier);
+    }
+
+    return 0;
+}
+
+static bool virtio_pci_query_guest_notifiers(DeviceState *d)
+{
+    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+    return msix_enabled(&proxy->pci_dev);
+}
+
+static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign)
+{
+    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+    VirtIODevice *vdev = proxy->vdev;
+    int r, n;
+    bool with_irqfd = msix_enabled(&proxy->pci_dev) &&
+        kvm_msi_via_irqfd_enabled();
+
+    nvqs = MIN(nvqs, VIRTIO_PCI_QUEUE_MAX);
+
+    /* When deassigning, pass a consistent nvqs value
+     * to avoid leaking notifiers.
+     */
+    assert(assign || nvqs == proxy->nvqs_with_notifiers);
+
+    proxy->nvqs_with_notifiers = nvqs;
+
+    /* Must unset vector notifier while guest notifier is still assigned */
+    if ((proxy->vector_irqfd || vdev->guest_notifier_mask) && !assign) {
+        msix_unset_vector_notifiers(&proxy->pci_dev);
+        if (proxy->vector_irqfd) {
+            kvm_virtio_pci_vector_release(proxy, nvqs);
+            g_free(proxy->vector_irqfd);
+            proxy->vector_irqfd = NULL;
+        }
+    }
+
+    for (n = 0; n < nvqs; n++) {
+        if (!virtio_queue_get_num(vdev, n)) {
+            break;
+        }
+
+        r = virtio_pci_set_guest_notifier(d, n, assign,
+                                          kvm_msi_via_irqfd_enabled());
+        if (r < 0) {
+            goto assign_error;
+        }
+    }
+
+    /* Must set vector notifier after guest notifier has been assigned */
+    if ((with_irqfd || vdev->guest_notifier_mask) && assign) {
+        if (with_irqfd) {
+            proxy->vector_irqfd =
+                g_malloc0(sizeof(*proxy->vector_irqfd) *
+                          msix_nr_vectors_allocated(&proxy->pci_dev));
+            r = kvm_virtio_pci_vector_use(proxy, nvqs);
+            if (r < 0) {
+                goto assign_error;
+            }
+        }
+        r = msix_set_vector_notifiers(&proxy->pci_dev,
+                                      virtio_pci_vector_unmask,
+                                      virtio_pci_vector_mask,
+                                      virtio_pci_vector_poll);
+        if (r < 0) {
+            goto notifiers_error;
+        }
+    }
+
+    return 0;
+
+notifiers_error:
+    if (with_irqfd) {
+        assert(assign);
+        kvm_virtio_pci_vector_release(proxy, nvqs);
+    }
+
+assign_error:
+    /* We get here on assignment failure. Recover by undoing for VQs 0 .. n. */
+    assert(assign);
+    while (--n >= 0) {
+        virtio_pci_set_guest_notifier(d, n, !assign, with_irqfd);
+    }
+    return r;
+}
+
+static int virtio_pci_set_host_notifier(DeviceState *d, int n, bool assign)
+{
+    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+
+    /* Stop using ioeventfd for virtqueue kick if the device starts using host
+     * notifiers.  This makes it easy to avoid stepping on each others' toes.
+     */
+    proxy->ioeventfd_disabled = assign;
+    if (assign) {
+        virtio_pci_stop_ioeventfd(proxy);
+    }
+    /* We don't need to start here: it's not needed because backend
+     * currently only stops on status change away from ok,
+     * reset, vmstop and such. If we do add code to start here,
+     * need to check vmstate, device state etc. */
+    return virtio_pci_set_host_notifier_internal(proxy, n, assign, false);
+}
+
+static void virtio_pci_vmstate_change(DeviceState *d, bool running)
+{
+    VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
+
+    if (running) {
+        /* Try to find out if the guest has bus master disabled, but is
+           in ready state. Then we have a buggy guest OS. */
+        if ((proxy->vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) &&
+            !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) {
+            proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG;
+        }
+        virtio_pci_start_ioeventfd(proxy);
+    } else {
+        virtio_pci_stop_ioeventfd(proxy);
+    }
+}
+
+static const VirtIOBindings virtio_pci_bindings = {
+    .notify = virtio_pci_notify,
+    .save_config = virtio_pci_save_config,
+    .load_config = virtio_pci_load_config,
+    .save_queue = virtio_pci_save_queue,
+    .load_queue = virtio_pci_load_queue,
+    .get_features = virtio_pci_get_features,
+    .query_guest_notifiers = virtio_pci_query_guest_notifiers,
+    .set_host_notifier = virtio_pci_set_host_notifier,
+    .set_guest_notifiers = virtio_pci_set_guest_notifiers,
+    .vmstate_change = virtio_pci_vmstate_change,
+};
+
+void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev)
+{
+    uint8_t *config;
+    uint32_t size;
+
+    proxy->vdev = vdev;
+
+    config = proxy->pci_dev.config;
+
+    if (proxy->class_code) {
+        pci_config_set_class(config, proxy->class_code);
+    }
+    pci_set_word(config + PCI_SUBSYSTEM_VENDOR_ID,
+                 pci_get_word(config + PCI_VENDOR_ID));
+    pci_set_word(config + PCI_SUBSYSTEM_ID, vdev->device_id);
+    config[PCI_INTERRUPT_PIN] = 1;
+
+    if (vdev->nvectors &&
+        msix_init_exclusive_bar(&proxy->pci_dev, vdev->nvectors, 1)) {
+        vdev->nvectors = 0;
+    }
+
+    proxy->pci_dev.config_write = virtio_write_config;
+
+    size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev) + vdev->config_len;
+    if (size & (size-1))
+        size = 1 << qemu_fls(size);
+
+    memory_region_init_io(&proxy->bar, &virtio_pci_config_ops, proxy,
+                          "virtio-pci", size);
+    pci_register_bar(&proxy->pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO,
+                     &proxy->bar);
+
+    if (!kvm_has_many_ioeventfds()) {
+        proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD;
+    }
+
+    virtio_bind_device(vdev, &virtio_pci_bindings, DEVICE(proxy));
+    proxy->host_features |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY;
+    proxy->host_features |= 0x1 << VIRTIO_F_BAD_FEATURE;
+    proxy->host_features = vdev->get_features(vdev, proxy->host_features);
+}
+
+static void virtio_exit_pci(PCIDevice *pci_dev)
+{
+    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+
+    memory_region_destroy(&proxy->bar);
+    msix_uninit_exclusive_bar(pci_dev);
+}
+
+static int virtio_serial_init_pci(PCIDevice *pci_dev)
+{
+    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+    VirtIODevice *vdev;
+
+    if (proxy->class_code != PCI_CLASS_COMMUNICATION_OTHER &&
+        proxy->class_code != PCI_CLASS_DISPLAY_OTHER && /* qemu 0.10 */
+        proxy->class_code != PCI_CLASS_OTHERS)          /* qemu-kvm  */
+        proxy->class_code = PCI_CLASS_COMMUNICATION_OTHER;
+
+    vdev = virtio_serial_init(&pci_dev->qdev, &proxy->serial);
+    if (!vdev) {
+        return -1;
+    }
+
+    /* backwards-compatibility with machines that were created with
+       DEV_NVECTORS_UNSPECIFIED */
+    vdev->nvectors = proxy->nvectors == DEV_NVECTORS_UNSPECIFIED
+                                        ? proxy->serial.max_virtserial_ports + 1
+                                        : proxy->nvectors;
+    virtio_init_pci(proxy, vdev);
+    proxy->nvectors = vdev->nvectors;
+    return 0;
+}
+
+static void virtio_serial_exit_pci(PCIDevice *pci_dev)
+{
+    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+
+    virtio_pci_stop_ioeventfd(proxy);
+    virtio_serial_exit(proxy->vdev);
+    virtio_exit_pci(pci_dev);
+}
+
+static int virtio_net_init_pci(PCIDevice *pci_dev)
+{
+    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+    VirtIODevice *vdev;
+
+    vdev = virtio_net_init(&pci_dev->qdev, &proxy->nic, &proxy->net,
+                           proxy->host_features);
+
+    vdev->nvectors = proxy->nvectors;
+    virtio_init_pci(proxy, vdev);
+
+    /* make the actual value visible */
+    proxy->nvectors = vdev->nvectors;
+    return 0;
+}
+
+static void virtio_net_exit_pci(PCIDevice *pci_dev)
+{
+    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+
+    virtio_pci_stop_ioeventfd(proxy);
+    virtio_net_exit(proxy->vdev);
+    virtio_exit_pci(pci_dev);
+}
+
+static int virtio_rng_init_pci(PCIDevice *pci_dev)
+{
+    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+    VirtIODevice *vdev;
+
+    if (proxy->rng.rng == NULL) {
+        proxy->rng.default_backend = RNG_RANDOM(object_new(TYPE_RNG_RANDOM));
+
+        object_property_add_child(OBJECT(pci_dev),
+                                  "default-backend",
+                                  OBJECT(proxy->rng.default_backend),
+                                  NULL);
+
+        object_property_set_link(OBJECT(pci_dev),
+                                 OBJECT(proxy->rng.default_backend),
+                                 "rng", NULL);
+    }
+
+    vdev = virtio_rng_init(&pci_dev->qdev, &proxy->rng);
+    if (!vdev) {
+        return -1;
+    }
+    virtio_init_pci(proxy, vdev);
+    return 0;
+}
+
+static void virtio_rng_exit_pci(PCIDevice *pci_dev)
+{
+    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+
+    virtio_pci_stop_ioeventfd(proxy);
+    virtio_rng_exit(proxy->vdev);
+    virtio_exit_pci(pci_dev);
+}
+
+static Property virtio_net_properties[] = {
+    DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false),
+    DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3),
+    DEFINE_VIRTIO_NET_FEATURES(VirtIOPCIProxy, host_features),
+    DEFINE_NIC_PROPERTIES(VirtIOPCIProxy, nic),
+    DEFINE_PROP_UINT32("x-txtimer", VirtIOPCIProxy, net.txtimer, TX_TIMER_INTERVAL),
+    DEFINE_PROP_INT32("x-txburst", VirtIOPCIProxy, net.txburst, TX_BURST),
+    DEFINE_PROP_STRING("tx", VirtIOPCIProxy, net.tx),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_net_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = virtio_net_init_pci;
+    k->exit = virtio_net_exit_pci;
+    k->romfile = "efi-virtio.rom";
+    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+    k->device_id = PCI_DEVICE_ID_VIRTIO_NET;
+    k->revision = VIRTIO_PCI_ABI_VERSION;
+    k->class_id = PCI_CLASS_NETWORK_ETHERNET;
+    dc->reset = virtio_pci_reset;
+    dc->props = virtio_net_properties;
+}
+
+static const TypeInfo virtio_net_info = {
+    .name          = "virtio-net-pci",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(VirtIOPCIProxy),
+    .class_init    = virtio_net_class_init,
+};
+
+static Property virtio_serial_properties[] = {
+    DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
+    DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+    DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
+    DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+    DEFINE_PROP_UINT32("max_ports", VirtIOPCIProxy, serial.max_virtserial_ports, 31),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_serial_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = virtio_serial_init_pci;
+    k->exit = virtio_serial_exit_pci;
+    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+    k->device_id = PCI_DEVICE_ID_VIRTIO_CONSOLE;
+    k->revision = VIRTIO_PCI_ABI_VERSION;
+    k->class_id = PCI_CLASS_COMMUNICATION_OTHER;
+    dc->reset = virtio_pci_reset;
+    dc->props = virtio_serial_properties;
+}
+
+static const TypeInfo virtio_serial_info = {
+    .name          = "virtio-serial-pci",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(VirtIOPCIProxy),
+    .class_init    = virtio_serial_class_init,
+};
+
+static void virtio_rng_initfn(Object *obj)
+{
+    PCIDevice *pci_dev = PCI_DEVICE(obj);
+    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+
+    object_property_add_link(obj, "rng", TYPE_RNG_BACKEND,
+                             (Object **)&proxy->rng.rng, NULL);
+}
+
+static Property virtio_rng_properties[] = {
+    DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+    /* Set a default rate limit of 2^47 bytes per minute or roughly 2TB/s.  If
+       you have an entropy source capable of generating more entropy than this
+       and you can pass it through via virtio-rng, then hats off to you.  Until
+       then, this is unlimited for all practical purposes.
+    */
+    DEFINE_PROP_UINT64("max-bytes", VirtIOPCIProxy, rng.max_bytes, INT64_MAX),
+    DEFINE_PROP_UINT32("period", VirtIOPCIProxy, rng.period_ms, 1 << 16),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_rng_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = virtio_rng_init_pci;
+    k->exit = virtio_rng_exit_pci;
+    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+    k->device_id = PCI_DEVICE_ID_VIRTIO_RNG;
+    k->revision = VIRTIO_PCI_ABI_VERSION;
+    k->class_id = PCI_CLASS_OTHERS;
+    dc->reset = virtio_pci_reset;
+    dc->props = virtio_rng_properties;
+}
+
+static const TypeInfo virtio_rng_info = {
+    .name          = "virtio-rng-pci",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(VirtIOPCIProxy),
+    .instance_init = virtio_rng_initfn,
+    .class_init    = virtio_rng_class_init,
+};
+
+#ifdef CONFIG_VIRTFS
+static int virtio_9p_init_pci(PCIDevice *pci_dev)
+{
+    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+    VirtIODevice *vdev;
+
+    vdev = virtio_9p_init(&pci_dev->qdev, &proxy->fsconf);
+    vdev->nvectors = proxy->nvectors;
+    virtio_init_pci(proxy, vdev);
+    /* make the actual value visible */
+    proxy->nvectors = vdev->nvectors;
+    return 0;
+}
+
+static Property virtio_9p_properties[] = {
+    DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
+    DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+    DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+    DEFINE_PROP_STRING("mount_tag", VirtIOPCIProxy, fsconf.tag),
+    DEFINE_PROP_STRING("fsdev", VirtIOPCIProxy, fsconf.fsdev_id),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_9p_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = virtio_9p_init_pci;
+    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+    k->device_id = PCI_DEVICE_ID_VIRTIO_9P;
+    k->revision = VIRTIO_PCI_ABI_VERSION;
+    k->class_id = 0x2;
+    dc->props = virtio_9p_properties;
+    dc->reset = virtio_pci_reset;
+}
+
+static const TypeInfo virtio_9p_info = {
+    .name          = "virtio-9p-pci",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(VirtIOPCIProxy),
+    .class_init    = virtio_9p_class_init,
+};
+#endif
+
+/*
+ * virtio-pci: This is the PCIDevice which has a virtio-pci-bus.
+ */
+
+/* This is called by virtio-bus just after the device is plugged. */
+static void virtio_pci_device_plugged(DeviceState *d)
+{
+    VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
+    VirtioBusState *bus = &proxy->bus;
+    uint8_t *config;
+    uint32_t size;
+
+    proxy->vdev = bus->vdev;
+
+    config = proxy->pci_dev.config;
+    if (proxy->class_code) {
+        pci_config_set_class(config, proxy->class_code);
+    }
+    pci_set_word(config + PCI_SUBSYSTEM_VENDOR_ID,
+                 pci_get_word(config + PCI_VENDOR_ID));
+    pci_set_word(config + PCI_SUBSYSTEM_ID, virtio_bus_get_vdev_id(bus));
+    config[PCI_INTERRUPT_PIN] = 1;
+
+    if (proxy->nvectors &&
+        msix_init_exclusive_bar(&proxy->pci_dev, proxy->nvectors, 1)) {
+        proxy->nvectors = 0;
+    }
+
+    proxy->pci_dev.config_write = virtio_write_config;
+
+    size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev)
+         + virtio_bus_get_vdev_config_len(bus);
+    if (size & (size - 1)) {
+        size = 1 << qemu_fls(size);
+    }
+
+    memory_region_init_io(&proxy->bar, &virtio_pci_config_ops, proxy,
+                          "virtio-pci", size);
+    pci_register_bar(&proxy->pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO,
+                     &proxy->bar);
+
+    if (!kvm_has_many_ioeventfds()) {
+        proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD;
+    }
+
+    proxy->host_features |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY;
+    proxy->host_features |= 0x1 << VIRTIO_F_BAD_FEATURE;
+    proxy->host_features = virtio_bus_get_vdev_features(bus,
+                                                      proxy->host_features);
+}
+
+static int virtio_pci_init(PCIDevice *pci_dev)
+{
+    VirtIOPCIProxy *dev = VIRTIO_PCI(pci_dev);
+    VirtioPCIClass *k = VIRTIO_PCI_GET_CLASS(pci_dev);
+    virtio_pci_bus_new(&dev->bus, dev);
+    if (k->init != NULL) {
+        return k->init(dev);
+    }
+    return 0;
+}
+
+static void virtio_pci_exit(PCIDevice *pci_dev)
+{
+    VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev);
+    virtio_pci_stop_ioeventfd(proxy);
+    virtio_exit_pci(pci_dev);
+}
+
+/*
+ * This will be renamed virtio_pci_reset at the end of the series.
+ * virtio_pci_reset is still in use at this moment.
+ */
+static void virtio_pci_rst(DeviceState *qdev)
+{
+    VirtIOPCIProxy *proxy = VIRTIO_PCI(qdev);
+    VirtioBusState *bus = VIRTIO_BUS(&proxy->bus);
+    virtio_pci_stop_ioeventfd(proxy);
+    virtio_bus_reset(bus);
+    msix_unuse_all_vectors(&proxy->pci_dev);
+    proxy->flags &= ~VIRTIO_PCI_FLAG_BUS_MASTER_BUG;
+}
+
+static void virtio_pci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = virtio_pci_init;
+    k->exit = virtio_pci_exit;
+    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+    k->revision = VIRTIO_PCI_ABI_VERSION;
+    k->class_id = PCI_CLASS_OTHERS;
+    dc->reset = virtio_pci_rst;
+}
+
+static const TypeInfo virtio_pci_info = {
+    .name          = TYPE_VIRTIO_PCI,
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(VirtIOPCIProxy),
+    .class_init    = virtio_pci_class_init,
+    .class_size    = sizeof(VirtioPCIClass),
+    .abstract      = true,
+};
+
+/* virtio-blk-pci */
+
+static Property virtio_blk_pci_properties[] = {
+    DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
+    DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
+                    VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
+    DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
+    DEFINE_PROP_BIT("x-data-plane", VirtIOBlkPCI, blk.data_plane, 0, false),
+#endif
+    DEFINE_VIRTIO_BLK_FEATURES(VirtIOPCIProxy, host_features),
+    DEFINE_VIRTIO_BLK_PROPERTIES(VirtIOBlkPCI, blk),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static int virtio_blk_pci_init(VirtIOPCIProxy *vpci_dev)
+{
+    VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(vpci_dev);
+    DeviceState *vdev = DEVICE(&dev->vdev);
+    virtio_blk_set_conf(vdev, &(dev->blk));
+    qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+    if (qdev_init(vdev) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+static void virtio_blk_pci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+    PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+    dc->props = virtio_blk_pci_properties;
+    k->init = virtio_blk_pci_init;
+    pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+    pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK;
+    pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
+    pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
+}
+
+static void virtio_blk_pci_instance_init(Object *obj)
+{
+    VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(obj);
+    object_initialize(OBJECT(&dev->vdev), TYPE_VIRTIO_BLK);
+    object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
+}
+
+static const TypeInfo virtio_blk_pci_info = {
+    .name          = TYPE_VIRTIO_BLK_PCI,
+    .parent        = TYPE_VIRTIO_PCI,
+    .instance_size = sizeof(VirtIOBlkPCI),
+    .instance_init = virtio_blk_pci_instance_init,
+    .class_init    = virtio_blk_pci_class_init,
+};
+
+/* virtio-scsi-pci */
+
+static Property virtio_scsi_pci_properties[] = {
+    DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
+                    VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
+    DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors,
+                       DEV_NVECTORS_UNSPECIFIED),
+    DEFINE_VIRTIO_SCSI_FEATURES(VirtIOPCIProxy, host_features),
+    DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOSCSIPCI, vdev.conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static int virtio_scsi_pci_init_pci(VirtIOPCIProxy *vpci_dev)
+{
+    VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(vpci_dev);
+    DeviceState *vdev = DEVICE(&dev->vdev);
+
+    if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) {
+        vpci_dev->nvectors = dev->vdev.conf.num_queues + 3;
+    }
+
+    qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+    if (qdev_init(vdev) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+static void virtio_scsi_pci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+    PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+    k->init = virtio_scsi_pci_init_pci;
+    dc->props = virtio_scsi_pci_properties;
+    pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+    pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI;
+    pcidev_k->revision = 0x00;
+    pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI;
+}
+
+static void virtio_scsi_pci_instance_init(Object *obj)
+{
+    VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(obj);
+    object_initialize(OBJECT(&dev->vdev), TYPE_VIRTIO_SCSI);
+    object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
+}
+
+static const TypeInfo virtio_scsi_pci_info = {
+    .name          = TYPE_VIRTIO_SCSI_PCI,
+    .parent        = TYPE_VIRTIO_PCI,
+    .instance_size = sizeof(VirtIOSCSIPCI),
+    .instance_init = virtio_scsi_pci_instance_init,
+    .class_init    = virtio_scsi_pci_class_init,
+};
+
+/* virtio-balloon-pci */
+
+static Property virtio_balloon_pci_properties[] = {
+    DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+    DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static int virtio_balloon_pci_init(VirtIOPCIProxy *vpci_dev)
+{
+    VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(vpci_dev);
+    DeviceState *vdev = DEVICE(&dev->vdev);
+
+    if (vpci_dev->class_code != PCI_CLASS_OTHERS &&
+        vpci_dev->class_code != PCI_CLASS_MEMORY_RAM) { /* qemu < 1.1 */
+        vpci_dev->class_code = PCI_CLASS_OTHERS;
+    }
+
+    qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+    if (qdev_init(vdev) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+    PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+    k->init = virtio_balloon_pci_init;
+    dc->props = virtio_balloon_pci_properties;
+    pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+    pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BALLOON;
+    pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
+    pcidev_k->class_id = PCI_CLASS_OTHERS;
+}
+
+static void virtio_balloon_pci_instance_init(Object *obj)
+{
+    VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(obj);
+    object_initialize(OBJECT(&dev->vdev), TYPE_VIRTIO_BALLOON);
+    object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
+}
+
+static const TypeInfo virtio_balloon_pci_info = {
+    .name          = TYPE_VIRTIO_BALLOON_PCI,
+    .parent        = TYPE_VIRTIO_PCI,
+    .instance_size = sizeof(VirtIOBalloonPCI),
+    .instance_init = virtio_balloon_pci_instance_init,
+    .class_init    = virtio_balloon_pci_class_init,
+};
+
+/* virtio-pci-bus */
+
+void virtio_pci_bus_new(VirtioBusState *bus, VirtIOPCIProxy *dev)
+{
+    DeviceState *qdev = DEVICE(dev);
+    BusState *qbus;
+    qbus_create_inplace((BusState *)bus, TYPE_VIRTIO_PCI_BUS, qdev, NULL);
+    qbus = BUS(bus);
+    qbus->allow_hotplug = 1;
+}
+
+static void virtio_pci_bus_class_init(ObjectClass *klass, void *data)
+{
+    BusClass *bus_class = BUS_CLASS(klass);
+    VirtioBusClass *k = VIRTIO_BUS_CLASS(klass);
+    bus_class->max_dev = 1;
+    k->notify = virtio_pci_notify;
+    k->save_config = virtio_pci_save_config;
+    k->load_config = virtio_pci_load_config;
+    k->save_queue = virtio_pci_save_queue;
+    k->load_queue = virtio_pci_load_queue;
+    k->get_features = virtio_pci_get_features;
+    k->query_guest_notifiers = virtio_pci_query_guest_notifiers;
+    k->set_host_notifier = virtio_pci_set_host_notifier;
+    k->set_guest_notifiers = virtio_pci_set_guest_notifiers;
+    k->vmstate_change = virtio_pci_vmstate_change;
+    k->device_plugged = virtio_pci_device_plugged;
+}
+
+static const TypeInfo virtio_pci_bus_info = {
+    .name          = TYPE_VIRTIO_PCI_BUS,
+    .parent        = TYPE_VIRTIO_BUS,
+    .instance_size = sizeof(VirtioPCIBusState),
+    .class_init    = virtio_pci_bus_class_init,
+};
+
+static void virtio_pci_register_types(void)
+{
+    type_register_static(&virtio_net_info);
+    type_register_static(&virtio_serial_info);
+    type_register_static(&virtio_rng_info);
+    type_register_static(&virtio_pci_bus_info);
+    type_register_static(&virtio_pci_info);
+#ifdef CONFIG_VIRTFS
+    type_register_static(&virtio_9p_info);
+#endif
+    type_register_static(&virtio_blk_pci_info);
+    type_register_static(&virtio_scsi_pci_info);
+    type_register_static(&virtio_balloon_pci_info);
+}
+
+type_init(virtio_pci_register_types)
diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c
new file mode 100644 (file)
index 0000000..6079b2a
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * A virtio device implementing a hardware random number generator.
+ *
+ * Copyright 2012 Red Hat, Inc.
+ * Copyright 2012 Amit Shah <amit.shah@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version.  See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/iov.h"
+#include "hw/qdev.h"
+#include "qapi/qmp/qerror.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-rng.h"
+#include "qemu/rng.h"
+
+static bool is_guest_ready(VirtIORNG *vrng)
+{
+    if (virtio_queue_ready(vrng->vq)
+        && (vrng->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
+        return true;
+    }
+    return false;
+}
+
+static size_t get_request_size(VirtQueue *vq, unsigned quota)
+{
+    unsigned int in, out;
+
+    virtqueue_get_avail_bytes(vq, &in, &out, quota, 0);
+    return in;
+}
+
+static void virtio_rng_process(VirtIORNG *vrng);
+
+/* Send data from a char device over to the guest */
+static void chr_read(void *opaque, const void *buf, size_t size)
+{
+    VirtIORNG *vrng = opaque;
+    VirtQueueElement elem;
+    size_t len;
+    int offset;
+
+    if (!is_guest_ready(vrng)) {
+        return;
+    }
+
+    vrng->quota_remaining -= size;
+
+    offset = 0;
+    while (offset < size) {
+        if (!virtqueue_pop(vrng->vq, &elem)) {
+            break;
+        }
+        len = iov_from_buf(elem.in_sg, elem.in_num,
+                           0, buf + offset, size - offset);
+        offset += len;
+
+        virtqueue_push(vrng->vq, &elem, len);
+    }
+    virtio_notify(&vrng->vdev, vrng->vq);
+}
+
+static void virtio_rng_process(VirtIORNG *vrng)
+{
+    size_t size;
+    unsigned quota;
+
+    if (!is_guest_ready(vrng)) {
+        return;
+    }
+
+    if (vrng->quota_remaining < 0) {
+        quota = 0;
+    } else {
+        quota = MIN((uint64_t)vrng->quota_remaining, (uint64_t)UINT32_MAX);
+    }
+    size = get_request_size(vrng->vq, quota);
+    size = MIN(vrng->quota_remaining, size);
+    if (size) {
+        rng_backend_request_entropy(vrng->rng, size, chr_read, vrng);
+    }
+}
+
+static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
+{
+    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
+    virtio_rng_process(vrng);
+}
+
+static uint32_t get_features(VirtIODevice *vdev, uint32_t f)
+{
+    return f;
+}
+
+static void virtio_rng_save(QEMUFile *f, void *opaque)
+{
+    VirtIORNG *vrng = opaque;
+
+    virtio_save(&vrng->vdev, f);
+}
+
+static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id)
+{
+    VirtIORNG *vrng = opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+    virtio_load(&vrng->vdev, f);
+
+    /* We may have an element ready but couldn't process it due to a quota
+     * limit.  Make sure to try again after live migration when the quota may
+     * have been reset.
+     */
+    virtio_rng_process(vrng);
+
+    return 0;
+}
+
+static void check_rate_limit(void *opaque)
+{
+    VirtIORNG *s = opaque;
+
+    s->quota_remaining = s->conf->max_bytes;
+    virtio_rng_process(s);
+    qemu_mod_timer(s->rate_limit_timer,
+                   qemu_get_clock_ms(vm_clock) + s->conf->period_ms);
+}
+
+
+VirtIODevice *virtio_rng_init(DeviceState *dev, VirtIORNGConf *conf)
+{
+    VirtIORNG *vrng;
+    VirtIODevice *vdev;
+    Error *local_err = NULL;
+
+    vdev = virtio_common_init("virtio-rng", VIRTIO_ID_RNG, 0,
+                              sizeof(VirtIORNG));
+
+    vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
+
+    vrng->rng = conf->rng;
+    if (vrng->rng == NULL) {
+        qerror_report(QERR_INVALID_PARAMETER_VALUE, "rng", "a valid object");
+        return NULL;
+    }
+
+    rng_backend_open(vrng->rng, &local_err);
+    if (local_err) {
+        qerror_report_err(local_err);
+        error_free(local_err);
+        return NULL;
+    }
+
+    vrng->vq = virtio_add_queue(vdev, 8, handle_input);
+    vrng->vdev.get_features = get_features;
+
+    vrng->qdev = dev;
+    vrng->conf = conf;
+
+    assert(vrng->conf->max_bytes <= INT64_MAX);
+    vrng->quota_remaining = vrng->conf->max_bytes;
+
+    vrng->rate_limit_timer = qemu_new_timer_ms(vm_clock,
+                                               check_rate_limit, vrng);
+
+    qemu_mod_timer(vrng->rate_limit_timer,
+                   qemu_get_clock_ms(vm_clock) + vrng->conf->period_ms);
+
+    register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save,
+                    virtio_rng_load, vrng);
+
+    return vdev;
+}
+
+void virtio_rng_exit(VirtIODevice *vdev)
+{
+    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
+
+    qemu_del_timer(vrng->rate_limit_timer);
+    qemu_free_timer(vrng->rate_limit_timer);
+    unregister_savevm(vrng->qdev, "virtio-rng", vrng);
+    virtio_cleanup(vdev);
+}
diff --git a/hw/vmmouse.c b/hw/vmmouse.c
deleted file mode 100644 (file)
index f4f9c93..0000000
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * QEMU VMMouse emulation
- *
- * Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "ui/console.h"
-#include "hw/input/ps2.h"
-#include "hw/i386/pc.h"
-#include "hw/qdev.h"
-
-/* debug only vmmouse */
-//#define DEBUG_VMMOUSE
-
-/* VMMouse Commands */
-#define VMMOUSE_GETVERSION     10
-#define VMMOUSE_DATA           39
-#define VMMOUSE_STATUS         40
-#define VMMOUSE_COMMAND                41
-
-#define VMMOUSE_READ_ID                        0x45414552
-#define VMMOUSE_DISABLE                        0x000000f5
-#define VMMOUSE_REQUEST_RELATIVE       0x4c455252
-#define VMMOUSE_REQUEST_ABSOLUTE       0x53424152
-
-#define VMMOUSE_QUEUE_SIZE     1024
-
-#define VMMOUSE_VERSION                0x3442554a
-
-#ifdef DEBUG_VMMOUSE
-#define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
-#else
-#define DPRINTF(fmt, ...) do { } while (0)
-#endif
-
-typedef struct _VMMouseState
-{
-    ISADevice dev;
-    uint32_t queue[VMMOUSE_QUEUE_SIZE];
-    int32_t queue_size;
-    uint16_t nb_queue;
-    uint16_t status;
-    uint8_t absolute;
-    QEMUPutMouseEntry *entry;
-    void *ps2_mouse;
-} VMMouseState;
-
-static uint32_t vmmouse_get_status(VMMouseState *s)
-{
-    DPRINTF("vmmouse_get_status()\n");
-    return (s->status << 16) | s->nb_queue;
-}
-
-static void vmmouse_mouse_event(void *opaque, int x, int y, int dz, int buttons_state)
-{
-    VMMouseState *s = opaque;
-    int buttons = 0;
-
-    if (s->nb_queue > (VMMOUSE_QUEUE_SIZE - 4))
-        return;
-
-    DPRINTF("vmmouse_mouse_event(%d, %d, %d, %d)\n",
-            x, y, dz, buttons_state);
-
-    if ((buttons_state & MOUSE_EVENT_LBUTTON))
-        buttons |= 0x20;
-    if ((buttons_state & MOUSE_EVENT_RBUTTON))
-        buttons |= 0x10;
-    if ((buttons_state & MOUSE_EVENT_MBUTTON))
-        buttons |= 0x08;
-
-    if (s->absolute) {
-        x <<= 1;
-        y <<= 1;
-    }
-
-    s->queue[s->nb_queue++] = buttons;
-    s->queue[s->nb_queue++] = x;
-    s->queue[s->nb_queue++] = y;
-    s->queue[s->nb_queue++] = dz;
-
-    /* need to still generate PS2 events to notify driver to
-       read from queue */
-    i8042_isa_mouse_fake_event(s->ps2_mouse);
-}
-
-static void vmmouse_remove_handler(VMMouseState *s)
-{
-    if (s->entry) {
-        qemu_remove_mouse_event_handler(s->entry);
-        s->entry = NULL;
-    }
-}
-
-static void vmmouse_update_handler(VMMouseState *s, int absolute)
-{
-    if (s->status != 0) {
-        return;
-    }
-    if (s->absolute != absolute) {
-        s->absolute = absolute;
-        vmmouse_remove_handler(s);
-    }
-    if (s->entry == NULL) {
-        s->entry = qemu_add_mouse_event_handler(vmmouse_mouse_event,
-                                                s, s->absolute,
-                                                "vmmouse");
-        qemu_activate_mouse_event_handler(s->entry);
-    }
-}
-
-static void vmmouse_read_id(VMMouseState *s)
-{
-    DPRINTF("vmmouse_read_id()\n");
-
-    if (s->nb_queue == VMMOUSE_QUEUE_SIZE)
-        return;
-
-    s->queue[s->nb_queue++] = VMMOUSE_VERSION;
-    s->status = 0;
-}
-
-static void vmmouse_request_relative(VMMouseState *s)
-{
-    DPRINTF("vmmouse_request_relative()\n");
-    vmmouse_update_handler(s, 0);
-}
-
-static void vmmouse_request_absolute(VMMouseState *s)
-{
-    DPRINTF("vmmouse_request_absolute()\n");
-    vmmouse_update_handler(s, 1);
-}
-
-static void vmmouse_disable(VMMouseState *s)
-{
-    DPRINTF("vmmouse_disable()\n");
-    s->status = 0xffff;
-    vmmouse_remove_handler(s);
-}
-
-static void vmmouse_data(VMMouseState *s, uint32_t *data, uint32_t size)
-{
-    int i;
-
-    DPRINTF("vmmouse_data(%d)\n", size);
-
-    if (size == 0 || size > 6 || size > s->nb_queue) {
-        printf("vmmouse: driver requested too much data %d\n", size);
-        s->status = 0xffff;
-        vmmouse_remove_handler(s);
-        return;
-    }
-
-    for (i = 0; i < size; i++)
-        data[i] = s->queue[i];
-
-    s->nb_queue -= size;
-    if (s->nb_queue)
-        memmove(s->queue, &s->queue[size], sizeof(s->queue[0]) * s->nb_queue);
-}
-
-static uint32_t vmmouse_ioport_read(void *opaque, uint32_t addr)
-{
-    VMMouseState *s = opaque;
-    uint32_t data[6];
-    uint16_t command;
-
-    vmmouse_get_data(data);
-
-    command = data[2] & 0xFFFF;
-
-    switch (command) {
-    case VMMOUSE_STATUS:
-        data[0] = vmmouse_get_status(s);
-        break;
-    case VMMOUSE_COMMAND:
-        switch (data[1]) {
-        case VMMOUSE_DISABLE:
-            vmmouse_disable(s);
-            break;
-        case VMMOUSE_READ_ID:
-            vmmouse_read_id(s);
-            break;
-        case VMMOUSE_REQUEST_RELATIVE:
-            vmmouse_request_relative(s);
-            break;
-        case VMMOUSE_REQUEST_ABSOLUTE:
-            vmmouse_request_absolute(s);
-            break;
-        default:
-            printf("vmmouse: unknown command %x\n", data[1]);
-            break;
-        }
-        break;
-    case VMMOUSE_DATA:
-        vmmouse_data(s, data, data[1]);
-        break;
-    default:
-        printf("vmmouse: unknown command %x\n", command);
-        break;
-    }
-
-    vmmouse_set_data(data);
-    return data[0];
-}
-
-static int vmmouse_post_load(void *opaque, int version_id)
-{
-    VMMouseState *s = opaque;
-
-    vmmouse_remove_handler(s);
-    vmmouse_update_handler(s, s->absolute);
-    return 0;
-}
-
-static const VMStateDescription vmstate_vmmouse = {
-    .name = "vmmouse",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .post_load = vmmouse_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_INT32_EQUAL(queue_size, VMMouseState),
-        VMSTATE_UINT32_ARRAY(queue, VMMouseState, VMMOUSE_QUEUE_SIZE),
-        VMSTATE_UINT16(nb_queue, VMMouseState),
-        VMSTATE_UINT16(status, VMMouseState),
-        VMSTATE_UINT8(absolute, VMMouseState),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void vmmouse_reset(DeviceState *d)
-{
-    VMMouseState *s = container_of(d, VMMouseState, dev.qdev);
-
-    s->queue_size = VMMOUSE_QUEUE_SIZE;
-
-    vmmouse_disable(s);
-}
-
-static int vmmouse_initfn(ISADevice *dev)
-{
-    VMMouseState *s = DO_UPCAST(VMMouseState, dev, dev);
-
-    DPRINTF("vmmouse_init\n");
-
-    vmport_register(VMMOUSE_STATUS, vmmouse_ioport_read, s);
-    vmport_register(VMMOUSE_COMMAND, vmmouse_ioport_read, s);
-    vmport_register(VMMOUSE_DATA, vmmouse_ioport_read, s);
-
-    return 0;
-}
-
-static Property vmmouse_properties[] = {
-    DEFINE_PROP_PTR("ps2_mouse", VMMouseState, ps2_mouse),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void vmmouse_class_initfn(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
-    ic->init = vmmouse_initfn;
-    dc->no_user = 1;
-    dc->reset = vmmouse_reset;
-    dc->vmsd = &vmstate_vmmouse;
-    dc->props = vmmouse_properties;
-}
-
-static const TypeInfo vmmouse_info = {
-    .name          = "vmmouse",
-    .parent        = TYPE_ISA_DEVICE,
-    .instance_size = sizeof(VMMouseState),
-    .class_init    = vmmouse_class_initfn,
-};
-
-static void vmmouse_register_types(void)
-{
-    type_register_static(&vmmouse_info);
-}
-
-type_init(vmmouse_register_types)
diff --git a/hw/vmware_utils.h b/hw/vmware_utils.h
deleted file mode 100644 (file)
index 5307e2c..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * QEMU VMWARE paravirtual devices - auxiliary code
- *
- * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
- *
- * Developed by Daynix Computing LTD (http://www.daynix.com)
- *
- * Authors:
- * Dmitry Fleytman <dmitry@daynix.com>
- * Yan Vugenfirer <yan@daynix.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- *
- */
-
-#ifndef VMWARE_UTILS_H
-#define VMWARE_UTILS_H
-
-#include "qemu/range.h"
-
-#ifndef VMW_SHPRN
-#define VMW_SHPRN(fmt, ...) do {} while (0)
-#endif
-
-/*
- * Shared memory access functions with byte swap support
- * Each function contains printout for reverse-engineering needs
- *
- */
-static inline void
-vmw_shmem_read(hwaddr addr, void *buf, int len)
-{
-    VMW_SHPRN("SHMEM r: %" PRIx64 ", len: %d to %p", addr, len, buf);
-    cpu_physical_memory_read(addr, buf, len);
-}
-
-static inline void
-vmw_shmem_write(hwaddr addr, void *buf, int len)
-{
-    VMW_SHPRN("SHMEM w: %" PRIx64 ", len: %d to %p", addr, len, buf);
-    cpu_physical_memory_write(addr, buf, len);
-}
-
-static inline void
-vmw_shmem_rw(hwaddr addr, void *buf, int len, int is_write)
-{
-    VMW_SHPRN("SHMEM r/w: %" PRIx64 ", len: %d (to %p), is write: %d",
-              addr, len, buf, is_write);
-
-    cpu_physical_memory_rw(addr, buf, len, is_write);
-}
-
-static inline void
-vmw_shmem_set(hwaddr addr, uint8 val, int len)
-{
-    int i;
-    VMW_SHPRN("SHMEM set: %" PRIx64 ", len: %d (value 0x%X)", addr, len, val);
-
-    for (i = 0; i < len; i++) {
-        cpu_physical_memory_write(addr + i, &val, 1);
-    }
-}
-
-static inline uint32_t
-vmw_shmem_ld8(hwaddr addr)
-{
-    uint8_t res = ldub_phys(addr);
-    VMW_SHPRN("SHMEM load8: %" PRIx64 " (value 0x%X)", addr, res);
-    return res;
-}
-
-static inline void
-vmw_shmem_st8(hwaddr addr, uint8_t value)
-{
-    VMW_SHPRN("SHMEM store8: %" PRIx64 " (value 0x%X)", addr, value);
-    stb_phys(addr, value);
-}
-
-static inline uint32_t
-vmw_shmem_ld16(hwaddr addr)
-{
-    uint16_t res = lduw_le_phys(addr);
-    VMW_SHPRN("SHMEM load16: %" PRIx64 " (value 0x%X)", addr, res);
-    return res;
-}
-
-static inline void
-vmw_shmem_st16(hwaddr addr, uint16_t value)
-{
-    VMW_SHPRN("SHMEM store16: %" PRIx64 " (value 0x%X)", addr, value);
-    stw_le_phys(addr, value);
-}
-
-static inline uint32_t
-vmw_shmem_ld32(hwaddr addr)
-{
-    uint32_t res = ldl_le_phys(addr);
-    VMW_SHPRN("SHMEM load32: %" PRIx64 " (value 0x%X)", addr, res);
-    return res;
-}
-
-static inline void
-vmw_shmem_st32(hwaddr addr, uint32_t value)
-{
-    VMW_SHPRN("SHMEM store32: %" PRIx64 " (value 0x%X)", addr, value);
-    stl_le_phys(addr, value);
-}
-
-static inline uint64_t
-vmw_shmem_ld64(hwaddr addr)
-{
-    uint64_t res = ldq_le_phys(addr);
-    VMW_SHPRN("SHMEM load64: %" PRIx64 " (value %" PRIx64 ")", addr, res);
-    return res;
-}
-
-static inline void
-vmw_shmem_st64(hwaddr addr, uint64_t value)
-{
-    VMW_SHPRN("SHMEM store64: %" PRIx64 " (value %" PRIx64 ")", addr, value);
-    stq_le_phys(addr, value);
-}
-
-/* Macros for simplification of operations on array-style registers */
-
-/*
- * Whether <addr> lies inside of array-style register defined by <base>,
- * number of elements (<cnt>) and element size (<regsize>)
- *
-*/
-#define VMW_IS_MULTIREG_ADDR(addr, base, cnt, regsize)                 \
-    range_covers_byte(base, cnt * regsize, addr)
-
-/*
- * Returns index of given register (<addr>) in array-style register defined by
- * <base> and element size (<regsize>)
- *
-*/
-#define VMW_MULTIREG_IDX_BY_ADDR(addr, base, regsize)                  \
-    (((addr) - (base)) / (regsize))
-
-#endif
diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c
deleted file mode 100644 (file)
index 5b9ce8f..0000000
+++ /dev/null
@@ -1,1282 +0,0 @@
-/*
- * QEMU VMware-SVGA "chipset".
- *
- * Copyright (c) 2007 Andrzej Zaborowski  <balrog@zabor.org>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#include "hw/hw.h"
-#include "hw/loader.h"
-#include "ui/console.h"
-#include "hw/pci/pci.h"
-
-#undef VERBOSE
-#define HW_RECT_ACCEL
-#define HW_FILL_ACCEL
-#define HW_MOUSE_ACCEL
-
-#include "hw/vga_int.h"
-
-/* See http://vmware-svga.sf.net/ for some documentation on VMWare SVGA */
-
-struct vmsvga_state_s {
-    VGACommonState vga;
-
-    int invalidated;
-    int depth;
-    int bypp;
-    int enable;
-    int config;
-    struct {
-        int id;
-        int x;
-        int y;
-        int on;
-    } cursor;
-
-    int index;
-    int scratch_size;
-    uint32_t *scratch;
-    int new_width;
-    int new_height;
-    uint32_t guest;
-    uint32_t svgaid;
-    int syncing;
-
-    MemoryRegion fifo_ram;
-    uint8_t *fifo_ptr;
-    unsigned int fifo_size;
-
-    union {
-        uint32_t *fifo;
-        struct QEMU_PACKED {
-            uint32_t min;
-            uint32_t max;
-            uint32_t next_cmd;
-            uint32_t stop;
-            /* Add registers here when adding capabilities.  */
-            uint32_t fifo[0];
-        } *cmd;
-    };
-
-#define REDRAW_FIFO_LEN  512
-    struct vmsvga_rect_s {
-        int x, y, w, h;
-    } redraw_fifo[REDRAW_FIFO_LEN];
-    int redraw_fifo_first, redraw_fifo_last;
-};
-
-struct pci_vmsvga_state_s {
-    PCIDevice card;
-    struct vmsvga_state_s chip;
-    MemoryRegion io_bar;
-};
-
-#define SVGA_MAGIC              0x900000UL
-#define SVGA_MAKE_ID(ver)       (SVGA_MAGIC << 8 | (ver))
-#define SVGA_ID_0               SVGA_MAKE_ID(0)
-#define SVGA_ID_1               SVGA_MAKE_ID(1)
-#define SVGA_ID_2               SVGA_MAKE_ID(2)
-
-#define SVGA_LEGACY_BASE_PORT   0x4560
-#define SVGA_INDEX_PORT         0x0
-#define SVGA_VALUE_PORT         0x1
-#define SVGA_BIOS_PORT          0x2
-
-#define SVGA_VERSION_2
-
-#ifdef SVGA_VERSION_2
-# define SVGA_ID                SVGA_ID_2
-# define SVGA_IO_BASE           SVGA_LEGACY_BASE_PORT
-# define SVGA_IO_MUL            1
-# define SVGA_FIFO_SIZE         0x10000
-# define SVGA_PCI_DEVICE_ID     PCI_DEVICE_ID_VMWARE_SVGA2
-#else
-# define SVGA_ID                SVGA_ID_1
-# define SVGA_IO_BASE           SVGA_LEGACY_BASE_PORT
-# define SVGA_IO_MUL            4
-# define SVGA_FIFO_SIZE         0x10000
-# define SVGA_PCI_DEVICE_ID     PCI_DEVICE_ID_VMWARE_SVGA
-#endif
-
-enum {
-    /* ID 0, 1 and 2 registers */
-    SVGA_REG_ID = 0,
-    SVGA_REG_ENABLE = 1,
-    SVGA_REG_WIDTH = 2,
-    SVGA_REG_HEIGHT = 3,
-    SVGA_REG_MAX_WIDTH = 4,
-    SVGA_REG_MAX_HEIGHT = 5,
-    SVGA_REG_DEPTH = 6,
-    SVGA_REG_BITS_PER_PIXEL = 7,        /* Current bpp in the guest */
-    SVGA_REG_PSEUDOCOLOR = 8,
-    SVGA_REG_RED_MASK = 9,
-    SVGA_REG_GREEN_MASK = 10,
-    SVGA_REG_BLUE_MASK = 11,
-    SVGA_REG_BYTES_PER_LINE = 12,
-    SVGA_REG_FB_START = 13,
-    SVGA_REG_FB_OFFSET = 14,
-    SVGA_REG_VRAM_SIZE = 15,
-    SVGA_REG_FB_SIZE = 16,
-
-    /* ID 1 and 2 registers */
-    SVGA_REG_CAPABILITIES = 17,
-    SVGA_REG_MEM_START = 18,            /* Memory for command FIFO */
-    SVGA_REG_MEM_SIZE = 19,
-    SVGA_REG_CONFIG_DONE = 20,          /* Set when memory area configured */
-    SVGA_REG_SYNC = 21,                 /* Write to force synchronization */
-    SVGA_REG_BUSY = 22,                 /* Read to check if sync is done */
-    SVGA_REG_GUEST_ID = 23,             /* Set guest OS identifier */
-    SVGA_REG_CURSOR_ID = 24,            /* ID of cursor */
-    SVGA_REG_CURSOR_X = 25,             /* Set cursor X position */
-    SVGA_REG_CURSOR_Y = 26,             /* Set cursor Y position */
-    SVGA_REG_CURSOR_ON = 27,            /* Turn cursor on/off */
-    SVGA_REG_HOST_BITS_PER_PIXEL = 28,  /* Current bpp in the host */
-    SVGA_REG_SCRATCH_SIZE = 29,         /* Number of scratch registers */
-    SVGA_REG_MEM_REGS = 30,             /* Number of FIFO registers */
-    SVGA_REG_NUM_DISPLAYS = 31,         /* Number of guest displays */
-    SVGA_REG_PITCHLOCK = 32,            /* Fixed pitch for all modes */
-
-    SVGA_PALETTE_BASE = 1024,           /* Base of SVGA color map */
-    SVGA_PALETTE_END  = SVGA_PALETTE_BASE + 767,
-    SVGA_SCRATCH_BASE = SVGA_PALETTE_BASE + 768,
-};
-
-#define SVGA_CAP_NONE                   0
-#define SVGA_CAP_RECT_FILL              (1 << 0)
-#define SVGA_CAP_RECT_COPY              (1 << 1)
-#define SVGA_CAP_RECT_PAT_FILL          (1 << 2)
-#define SVGA_CAP_LEGACY_OFFSCREEN       (1 << 3)
-#define SVGA_CAP_RASTER_OP              (1 << 4)
-#define SVGA_CAP_CURSOR                 (1 << 5)
-#define SVGA_CAP_CURSOR_BYPASS          (1 << 6)
-#define SVGA_CAP_CURSOR_BYPASS_2        (1 << 7)
-#define SVGA_CAP_8BIT_EMULATION         (1 << 8)
-#define SVGA_CAP_ALPHA_CURSOR           (1 << 9)
-#define SVGA_CAP_GLYPH                  (1 << 10)
-#define SVGA_CAP_GLYPH_CLIPPING         (1 << 11)
-#define SVGA_CAP_OFFSCREEN_1            (1 << 12)
-#define SVGA_CAP_ALPHA_BLEND            (1 << 13)
-#define SVGA_CAP_3D                     (1 << 14)
-#define SVGA_CAP_EXTENDED_FIFO          (1 << 15)
-#define SVGA_CAP_MULTIMON               (1 << 16)
-#define SVGA_CAP_PITCHLOCK              (1 << 17)
-
-/*
- * FIFO offsets (seen as an array of 32-bit words)
- */
-enum {
-    /*
-     * The original defined FIFO offsets
-     */
-    SVGA_FIFO_MIN = 0,
-    SVGA_FIFO_MAX,      /* The distance from MIN to MAX must be at least 10K */
-    SVGA_FIFO_NEXT_CMD,
-    SVGA_FIFO_STOP,
-
-    /*
-     * Additional offsets added as of SVGA_CAP_EXTENDED_FIFO
-     */
-    SVGA_FIFO_CAPABILITIES = 4,
-    SVGA_FIFO_FLAGS,
-    SVGA_FIFO_FENCE,
-    SVGA_FIFO_3D_HWVERSION,
-    SVGA_FIFO_PITCHLOCK,
-};
-
-#define SVGA_FIFO_CAP_NONE              0
-#define SVGA_FIFO_CAP_FENCE             (1 << 0)
-#define SVGA_FIFO_CAP_ACCELFRONT        (1 << 1)
-#define SVGA_FIFO_CAP_PITCHLOCK         (1 << 2)
-
-#define SVGA_FIFO_FLAG_NONE             0
-#define SVGA_FIFO_FLAG_ACCELFRONT       (1 << 0)
-
-/* These values can probably be changed arbitrarily.  */
-#define SVGA_SCRATCH_SIZE               0x8000
-#define SVGA_MAX_WIDTH                  2360
-#define SVGA_MAX_HEIGHT                 1770
-
-#ifdef VERBOSE
-# define GUEST_OS_BASE          0x5001
-static const char *vmsvga_guest_id[] = {
-    [0x00] = "Dos",
-    [0x01] = "Windows 3.1",
-    [0x02] = "Windows 95",
-    [0x03] = "Windows 98",
-    [0x04] = "Windows ME",
-    [0x05] = "Windows NT",
-    [0x06] = "Windows 2000",
-    [0x07] = "Linux",
-    [0x08] = "OS/2",
-    [0x09] = "an unknown OS",
-    [0x0a] = "BSD",
-    [0x0b] = "Whistler",
-    [0x0c] = "an unknown OS",
-    [0x0d] = "an unknown OS",
-    [0x0e] = "an unknown OS",
-    [0x0f] = "an unknown OS",
-    [0x10] = "an unknown OS",
-    [0x11] = "an unknown OS",
-    [0x12] = "an unknown OS",
-    [0x13] = "an unknown OS",
-    [0x14] = "an unknown OS",
-    [0x15] = "Windows 2003",
-};
-#endif
-
-enum {
-    SVGA_CMD_INVALID_CMD = 0,
-    SVGA_CMD_UPDATE = 1,
-    SVGA_CMD_RECT_FILL = 2,
-    SVGA_CMD_RECT_COPY = 3,
-    SVGA_CMD_DEFINE_BITMAP = 4,
-    SVGA_CMD_DEFINE_BITMAP_SCANLINE = 5,
-    SVGA_CMD_DEFINE_PIXMAP = 6,
-    SVGA_CMD_DEFINE_PIXMAP_SCANLINE = 7,
-    SVGA_CMD_RECT_BITMAP_FILL = 8,
-    SVGA_CMD_RECT_PIXMAP_FILL = 9,
-    SVGA_CMD_RECT_BITMAP_COPY = 10,
-    SVGA_CMD_RECT_PIXMAP_COPY = 11,
-    SVGA_CMD_FREE_OBJECT = 12,
-    SVGA_CMD_RECT_ROP_FILL = 13,
-    SVGA_CMD_RECT_ROP_COPY = 14,
-    SVGA_CMD_RECT_ROP_BITMAP_FILL = 15,
-    SVGA_CMD_RECT_ROP_PIXMAP_FILL = 16,
-    SVGA_CMD_RECT_ROP_BITMAP_COPY = 17,
-    SVGA_CMD_RECT_ROP_PIXMAP_COPY = 18,
-    SVGA_CMD_DEFINE_CURSOR = 19,
-    SVGA_CMD_DISPLAY_CURSOR = 20,
-    SVGA_CMD_MOVE_CURSOR = 21,
-    SVGA_CMD_DEFINE_ALPHA_CURSOR = 22,
-    SVGA_CMD_DRAW_GLYPH = 23,
-    SVGA_CMD_DRAW_GLYPH_CLIPPED = 24,
-    SVGA_CMD_UPDATE_VERBOSE = 25,
-    SVGA_CMD_SURFACE_FILL = 26,
-    SVGA_CMD_SURFACE_COPY = 27,
-    SVGA_CMD_SURFACE_ALPHA_BLEND = 28,
-    SVGA_CMD_FRONT_ROP_FILL = 29,
-    SVGA_CMD_FENCE = 30,
-};
-
-/* Legal values for the SVGA_REG_CURSOR_ON register in cursor bypass mode */
-enum {
-    SVGA_CURSOR_ON_HIDE = 0,
-    SVGA_CURSOR_ON_SHOW = 1,
-    SVGA_CURSOR_ON_REMOVE_FROM_FB = 2,
-    SVGA_CURSOR_ON_RESTORE_TO_FB = 3,
-};
-
-static inline void vmsvga_update_rect(struct vmsvga_state_s *s,
-                int x, int y, int w, int h)
-{
-    DisplaySurface *surface = qemu_console_surface(s->vga.con);
-    int line;
-    int bypl;
-    int width;
-    int start;
-    uint8_t *src;
-    uint8_t *dst;
-
-    if (x < 0) {
-        fprintf(stderr, "%s: update x was < 0 (%d)\n", __func__, x);
-        w += x;
-        x = 0;
-    }
-    if (w < 0) {
-        fprintf(stderr, "%s: update w was < 0 (%d)\n", __func__, w);
-        w = 0;
-    }
-    if (x + w > surface_width(surface)) {
-        fprintf(stderr, "%s: update width too large x: %d, w: %d\n",
-                __func__, x, w);
-        x = MIN(x, surface_width(surface));
-        w = surface_width(surface) - x;
-    }
-
-    if (y < 0) {
-        fprintf(stderr, "%s: update y was < 0 (%d)\n",  __func__, y);
-        h += y;
-        y = 0;
-    }
-    if (h < 0) {
-        fprintf(stderr, "%s: update h was < 0 (%d)\n",  __func__, h);
-        h = 0;
-    }
-    if (y + h > surface_height(surface)) {
-        fprintf(stderr, "%s: update height too large y: %d, h: %d\n",
-                __func__, y, h);
-        y = MIN(y, surface_height(surface));
-        h = surface_height(surface) - y;
-    }
-
-    bypl = surface_stride(surface);
-    width = surface_bytes_per_pixel(surface) * w;
-    start = surface_bytes_per_pixel(surface) * x + bypl * y;
-    src = s->vga.vram_ptr + start;
-    dst = surface_data(surface) + start;
-
-    for (line = h; line > 0; line--, src += bypl, dst += bypl) {
-        memcpy(dst, src, width);
-    }
-    dpy_gfx_update(s->vga.con, x, y, w, h);
-}
-
-static inline void vmsvga_update_rect_delayed(struct vmsvga_state_s *s,
-                int x, int y, int w, int h)
-{
-    struct vmsvga_rect_s *rect = &s->redraw_fifo[s->redraw_fifo_last++];
-
-    s->redraw_fifo_last &= REDRAW_FIFO_LEN - 1;
-    rect->x = x;
-    rect->y = y;
-    rect->w = w;
-    rect->h = h;
-}
-
-static inline void vmsvga_update_rect_flush(struct vmsvga_state_s *s)
-{
-    struct vmsvga_rect_s *rect;
-
-    if (s->invalidated) {
-        s->redraw_fifo_first = s->redraw_fifo_last;
-        return;
-    }
-    /* Overlapping region updates can be optimised out here - if someone
-     * knows a smart algorithm to do that, please share.  */
-    while (s->redraw_fifo_first != s->redraw_fifo_last) {
-        rect = &s->redraw_fifo[s->redraw_fifo_first++];
-        s->redraw_fifo_first &= REDRAW_FIFO_LEN - 1;
-        vmsvga_update_rect(s, rect->x, rect->y, rect->w, rect->h);
-    }
-}
-
-#ifdef HW_RECT_ACCEL
-static inline void vmsvga_copy_rect(struct vmsvga_state_s *s,
-                int x0, int y0, int x1, int y1, int w, int h)
-{
-    DisplaySurface *surface = qemu_console_surface(s->vga.con);
-    uint8_t *vram = s->vga.vram_ptr;
-    int bypl = surface_stride(surface);
-    int bypp = surface_bytes_per_pixel(surface);
-    int width = bypp * w;
-    int line = h;
-    uint8_t *ptr[2];
-
-    if (y1 > y0) {
-        ptr[0] = vram + bypp * x0 + bypl * (y0 + h - 1);
-        ptr[1] = vram + bypp * x1 + bypl * (y1 + h - 1);
-        for (; line > 0; line --, ptr[0] -= bypl, ptr[1] -= bypl) {
-            memmove(ptr[1], ptr[0], width);
-        }
-    } else {
-        ptr[0] = vram + bypp * x0 + bypl * y0;
-        ptr[1] = vram + bypp * x1 + bypl * y1;
-        for (; line > 0; line --, ptr[0] += bypl, ptr[1] += bypl) {
-            memmove(ptr[1], ptr[0], width);
-        }
-    }
-
-    vmsvga_update_rect_delayed(s, x1, y1, w, h);
-}
-#endif
-
-#ifdef HW_FILL_ACCEL
-static inline void vmsvga_fill_rect(struct vmsvga_state_s *s,
-                uint32_t c, int x, int y, int w, int h)
-{
-    DisplaySurface *surface = qemu_console_surface(s->vga.con);
-    int bypl = surface_stride(surface);
-    int width = surface_bytes_per_pixel(surface) * w;
-    int line = h;
-    int column;
-    uint8_t *fst;
-    uint8_t *dst;
-    uint8_t *src;
-    uint8_t col[4];
-
-    col[0] = c;
-    col[1] = c >> 8;
-    col[2] = c >> 16;
-    col[3] = c >> 24;
-
-    fst = s->vga.vram_ptr + surface_bytes_per_pixel(surface) * x + bypl * y;
-
-    if (line--) {
-        dst = fst;
-        src = col;
-        for (column = width; column > 0; column--) {
-            *(dst++) = *(src++);
-            if (src - col == surface_bytes_per_pixel(surface)) {
-                src = col;
-            }
-        }
-        dst = fst;
-        for (; line > 0; line--) {
-            dst += bypl;
-            memcpy(dst, fst, width);
-        }
-    }
-
-    vmsvga_update_rect_delayed(s, x, y, w, h);
-}
-#endif
-
-struct vmsvga_cursor_definition_s {
-    int width;
-    int height;
-    int id;
-    int bpp;
-    int hot_x;
-    int hot_y;
-    uint32_t mask[1024];
-    uint32_t image[4096];
-};
-
-#define SVGA_BITMAP_SIZE(w, h)          ((((w) + 31) >> 5) * (h))
-#define SVGA_PIXMAP_SIZE(w, h, bpp)     (((((w) * (bpp)) + 31) >> 5) * (h))
-
-#ifdef HW_MOUSE_ACCEL
-static inline void vmsvga_cursor_define(struct vmsvga_state_s *s,
-                struct vmsvga_cursor_definition_s *c)
-{
-    QEMUCursor *qc;
-    int i, pixels;
-
-    qc = cursor_alloc(c->width, c->height);
-    qc->hot_x = c->hot_x;
-    qc->hot_y = c->hot_y;
-    switch (c->bpp) {
-    case 1:
-        cursor_set_mono(qc, 0xffffff, 0x000000, (void *)c->image,
-                        1, (void *)c->mask);
-#ifdef DEBUG
-        cursor_print_ascii_art(qc, "vmware/mono");
-#endif
-        break;
-    case 32:
-        /* fill alpha channel from mask, set color to zero */
-        cursor_set_mono(qc, 0x000000, 0x000000, (void *)c->mask,
-                        1, (void *)c->mask);
-        /* add in rgb values */
-        pixels = c->width * c->height;
-        for (i = 0; i < pixels; i++) {
-            qc->data[i] |= c->image[i] & 0xffffff;
-        }
-#ifdef DEBUG
-        cursor_print_ascii_art(qc, "vmware/32bit");
-#endif
-        break;
-    default:
-        fprintf(stderr, "%s: unhandled bpp %d, using fallback cursor\n",
-                __func__, c->bpp);
-        cursor_put(qc);
-        qc = cursor_builtin_left_ptr();
-    }
-
-    dpy_cursor_define(s->vga.con, qc);
-    cursor_put(qc);
-}
-#endif
-
-#define CMD(f)  le32_to_cpu(s->cmd->f)
-
-static inline int vmsvga_fifo_length(struct vmsvga_state_s *s)
-{
-    int num;
-
-    if (!s->config || !s->enable) {
-        return 0;
-    }
-    num = CMD(next_cmd) - CMD(stop);
-    if (num < 0) {
-        num += CMD(max) - CMD(min);
-    }
-    return num >> 2;
-}
-
-static inline uint32_t vmsvga_fifo_read_raw(struct vmsvga_state_s *s)
-{
-    uint32_t cmd = s->fifo[CMD(stop) >> 2];
-
-    s->cmd->stop = cpu_to_le32(CMD(stop) + 4);
-    if (CMD(stop) >= CMD(max)) {
-        s->cmd->stop = s->cmd->min;
-    }
-    return cmd;
-}
-
-static inline uint32_t vmsvga_fifo_read(struct vmsvga_state_s *s)
-{
-    return le32_to_cpu(vmsvga_fifo_read_raw(s));
-}
-
-static void vmsvga_fifo_run(struct vmsvga_state_s *s)
-{
-    uint32_t cmd, colour;
-    int args, len;
-    int x, y, dx, dy, width, height;
-    struct vmsvga_cursor_definition_s cursor;
-    uint32_t cmd_start;
-
-    len = vmsvga_fifo_length(s);
-    while (len > 0) {
-        /* May need to go back to the start of the command if incomplete */
-        cmd_start = s->cmd->stop;
-
-        switch (cmd = vmsvga_fifo_read(s)) {
-        case SVGA_CMD_UPDATE:
-        case SVGA_CMD_UPDATE_VERBOSE:
-            len -= 5;
-            if (len < 0) {
-                goto rewind;
-            }
-
-            x = vmsvga_fifo_read(s);
-            y = vmsvga_fifo_read(s);
-            width = vmsvga_fifo_read(s);
-            height = vmsvga_fifo_read(s);
-            vmsvga_update_rect_delayed(s, x, y, width, height);
-            break;
-
-        case SVGA_CMD_RECT_FILL:
-            len -= 6;
-            if (len < 0) {
-                goto rewind;
-            }
-
-            colour = vmsvga_fifo_read(s);
-            x = vmsvga_fifo_read(s);
-            y = vmsvga_fifo_read(s);
-            width = vmsvga_fifo_read(s);
-            height = vmsvga_fifo_read(s);
-#ifdef HW_FILL_ACCEL
-            vmsvga_fill_rect(s, colour, x, y, width, height);
-            break;
-#else
-            args = 0;
-            goto badcmd;
-#endif
-
-        case SVGA_CMD_RECT_COPY:
-            len -= 7;
-            if (len < 0) {
-                goto rewind;
-            }
-
-            x = vmsvga_fifo_read(s);
-            y = vmsvga_fifo_read(s);
-            dx = vmsvga_fifo_read(s);
-            dy = vmsvga_fifo_read(s);
-            width = vmsvga_fifo_read(s);
-            height = vmsvga_fifo_read(s);
-#ifdef HW_RECT_ACCEL
-            vmsvga_copy_rect(s, x, y, dx, dy, width, height);
-            break;
-#else
-            args = 0;
-            goto badcmd;
-#endif
-
-        case SVGA_CMD_DEFINE_CURSOR:
-            len -= 8;
-            if (len < 0) {
-                goto rewind;
-            }
-
-            cursor.id = vmsvga_fifo_read(s);
-            cursor.hot_x = vmsvga_fifo_read(s);
-            cursor.hot_y = vmsvga_fifo_read(s);
-            cursor.width = x = vmsvga_fifo_read(s);
-            cursor.height = y = vmsvga_fifo_read(s);
-            vmsvga_fifo_read(s);
-            cursor.bpp = vmsvga_fifo_read(s);
-
-            args = SVGA_BITMAP_SIZE(x, y) + SVGA_PIXMAP_SIZE(x, y, cursor.bpp);
-            if (SVGA_BITMAP_SIZE(x, y) > sizeof cursor.mask ||
-                SVGA_PIXMAP_SIZE(x, y, cursor.bpp) > sizeof cursor.image) {
-                    goto badcmd;
-            }
-
-            len -= args;
-            if (len < 0) {
-                goto rewind;
-            }
-
-            for (args = 0; args < SVGA_BITMAP_SIZE(x, y); args++) {
-                cursor.mask[args] = vmsvga_fifo_read_raw(s);
-            }
-            for (args = 0; args < SVGA_PIXMAP_SIZE(x, y, cursor.bpp); args++) {
-                cursor.image[args] = vmsvga_fifo_read_raw(s);
-            }
-#ifdef HW_MOUSE_ACCEL
-            vmsvga_cursor_define(s, &cursor);
-            break;
-#else
-            args = 0;
-            goto badcmd;
-#endif
-
-        /*
-         * Other commands that we at least know the number of arguments
-         * for so we can avoid FIFO desync if driver uses them illegally.
-         */
-        case SVGA_CMD_DEFINE_ALPHA_CURSOR:
-            len -= 6;
-            if (len < 0) {
-                goto rewind;
-            }
-            vmsvga_fifo_read(s);
-            vmsvga_fifo_read(s);
-            vmsvga_fifo_read(s);
-            x = vmsvga_fifo_read(s);
-            y = vmsvga_fifo_read(s);
-            args = x * y;
-            goto badcmd;
-        case SVGA_CMD_RECT_ROP_FILL:
-            args = 6;
-            goto badcmd;
-        case SVGA_CMD_RECT_ROP_COPY:
-            args = 7;
-            goto badcmd;
-        case SVGA_CMD_DRAW_GLYPH_CLIPPED:
-            len -= 4;
-            if (len < 0) {
-                goto rewind;
-            }
-            vmsvga_fifo_read(s);
-            vmsvga_fifo_read(s);
-            args = 7 + (vmsvga_fifo_read(s) >> 2);
-            goto badcmd;
-        case SVGA_CMD_SURFACE_ALPHA_BLEND:
-            args = 12;
-            goto badcmd;
-
-        /*
-         * Other commands that are not listed as depending on any
-         * CAPABILITIES bits, but are not described in the README either.
-         */
-        case SVGA_CMD_SURFACE_FILL:
-        case SVGA_CMD_SURFACE_COPY:
-        case SVGA_CMD_FRONT_ROP_FILL:
-        case SVGA_CMD_FENCE:
-        case SVGA_CMD_INVALID_CMD:
-            break; /* Nop */
-
-        default:
-            args = 0;
-        badcmd:
-            len -= args;
-            if (len < 0) {
-                goto rewind;
-            }
-            while (args--) {
-                vmsvga_fifo_read(s);
-            }
-            printf("%s: Unknown command 0x%02x in SVGA command FIFO\n",
-                   __func__, cmd);
-            break;
-
-        rewind:
-            s->cmd->stop = cmd_start;
-            break;
-        }
-    }
-
-    s->syncing = 0;
-}
-
-static uint32_t vmsvga_index_read(void *opaque, uint32_t address)
-{
-    struct vmsvga_state_s *s = opaque;
-
-    return s->index;
-}
-
-static void vmsvga_index_write(void *opaque, uint32_t address, uint32_t index)
-{
-    struct vmsvga_state_s *s = opaque;
-
-    s->index = index;
-}
-
-static uint32_t vmsvga_value_read(void *opaque, uint32_t address)
-{
-    uint32_t caps;
-    struct vmsvga_state_s *s = opaque;
-    DisplaySurface *surface = qemu_console_surface(s->vga.con);
-
-    switch (s->index) {
-    case SVGA_REG_ID:
-        return s->svgaid;
-
-    case SVGA_REG_ENABLE:
-        return s->enable;
-
-    case SVGA_REG_WIDTH:
-        return surface_width(surface);
-
-    case SVGA_REG_HEIGHT:
-        return surface_height(surface);
-
-    case SVGA_REG_MAX_WIDTH:
-        return SVGA_MAX_WIDTH;
-
-    case SVGA_REG_MAX_HEIGHT:
-        return SVGA_MAX_HEIGHT;
-
-    case SVGA_REG_DEPTH:
-        return s->depth;
-
-    case SVGA_REG_BITS_PER_PIXEL:
-        return (s->depth + 7) & ~7;
-
-    case SVGA_REG_PSEUDOCOLOR:
-        return 0x0;
-
-    case SVGA_REG_RED_MASK:
-        return surface->pf.rmask;
-
-    case SVGA_REG_GREEN_MASK:
-        return surface->pf.gmask;
-
-    case SVGA_REG_BLUE_MASK:
-        return surface->pf.bmask;
-
-    case SVGA_REG_BYTES_PER_LINE:
-        return s->bypp * s->new_width;
-
-    case SVGA_REG_FB_START: {
-        struct pci_vmsvga_state_s *pci_vmsvga
-            = container_of(s, struct pci_vmsvga_state_s, chip);
-        return pci_get_bar_addr(&pci_vmsvga->card, 1);
-    }
-
-    case SVGA_REG_FB_OFFSET:
-        return 0x0;
-
-    case SVGA_REG_VRAM_SIZE:
-        return s->vga.vram_size; /* No physical VRAM besides the framebuffer */
-
-    case SVGA_REG_FB_SIZE:
-        return s->vga.vram_size;
-
-    case SVGA_REG_CAPABILITIES:
-        caps = SVGA_CAP_NONE;
-#ifdef HW_RECT_ACCEL
-        caps |= SVGA_CAP_RECT_COPY;
-#endif
-#ifdef HW_FILL_ACCEL
-        caps |= SVGA_CAP_RECT_FILL;
-#endif
-#ifdef HW_MOUSE_ACCEL
-        if (dpy_cursor_define_supported(s->vga.con)) {
-            caps |= SVGA_CAP_CURSOR | SVGA_CAP_CURSOR_BYPASS_2 |
-                    SVGA_CAP_CURSOR_BYPASS;
-        }
-#endif
-        return caps;
-
-    case SVGA_REG_MEM_START: {
-        struct pci_vmsvga_state_s *pci_vmsvga
-            = container_of(s, struct pci_vmsvga_state_s, chip);
-        return pci_get_bar_addr(&pci_vmsvga->card, 2);
-    }
-
-    case SVGA_REG_MEM_SIZE:
-        return s->fifo_size;
-
-    case SVGA_REG_CONFIG_DONE:
-        return s->config;
-
-    case SVGA_REG_SYNC:
-    case SVGA_REG_BUSY:
-        return s->syncing;
-
-    case SVGA_REG_GUEST_ID:
-        return s->guest;
-
-    case SVGA_REG_CURSOR_ID:
-        return s->cursor.id;
-
-    case SVGA_REG_CURSOR_X:
-        return s->cursor.x;
-
-    case SVGA_REG_CURSOR_Y:
-        return s->cursor.x;
-
-    case SVGA_REG_CURSOR_ON:
-        return s->cursor.on;
-
-    case SVGA_REG_HOST_BITS_PER_PIXEL:
-        return (s->depth + 7) & ~7;
-
-    case SVGA_REG_SCRATCH_SIZE:
-        return s->scratch_size;
-
-    case SVGA_REG_MEM_REGS:
-    case SVGA_REG_NUM_DISPLAYS:
-    case SVGA_REG_PITCHLOCK:
-    case SVGA_PALETTE_BASE ... SVGA_PALETTE_END:
-        return 0;
-
-    default:
-        if (s->index >= SVGA_SCRATCH_BASE &&
-            s->index < SVGA_SCRATCH_BASE + s->scratch_size) {
-            return s->scratch[s->index - SVGA_SCRATCH_BASE];
-        }
-        printf("%s: Bad register %02x\n", __func__, s->index);
-    }
-
-    return 0;
-}
-
-static void vmsvga_value_write(void *opaque, uint32_t address, uint32_t value)
-{
-    struct vmsvga_state_s *s = opaque;
-
-    switch (s->index) {
-    case SVGA_REG_ID:
-        if (value == SVGA_ID_2 || value == SVGA_ID_1 || value == SVGA_ID_0) {
-            s->svgaid = value;
-        }
-        break;
-
-    case SVGA_REG_ENABLE:
-        s->enable = !!value;
-        s->invalidated = 1;
-        s->vga.invalidate(&s->vga);
-        if (s->enable && s->config) {
-            vga_dirty_log_stop(&s->vga);
-        } else {
-            vga_dirty_log_start(&s->vga);
-        }
-        break;
-
-    case SVGA_REG_WIDTH:
-        if (value <= SVGA_MAX_WIDTH) {
-            s->new_width = value;
-            s->invalidated = 1;
-        } else {
-            printf("%s: Bad width: %i\n", __func__, value);
-        }
-        break;
-
-    case SVGA_REG_HEIGHT:
-        if (value <= SVGA_MAX_HEIGHT) {
-            s->new_height = value;
-            s->invalidated = 1;
-        } else {
-            printf("%s: Bad height: %i\n", __func__, value);
-        }
-        break;
-
-    case SVGA_REG_BITS_PER_PIXEL:
-        if (value != s->depth) {
-            printf("%s: Bad bits per pixel: %i bits\n", __func__, value);
-            s->config = 0;
-        }
-        break;
-
-    case SVGA_REG_CONFIG_DONE:
-        if (value) {
-            s->fifo = (uint32_t *) s->fifo_ptr;
-            /* Check range and alignment.  */
-            if ((CMD(min) | CMD(max) | CMD(next_cmd) | CMD(stop)) & 3) {
-                break;
-            }
-            if (CMD(min) < (uint8_t *) s->cmd->fifo - (uint8_t *) s->fifo) {
-                break;
-            }
-            if (CMD(max) > SVGA_FIFO_SIZE) {
-                break;
-            }
-            if (CMD(max) < CMD(min) + 10 * 1024) {
-                break;
-            }
-            vga_dirty_log_stop(&s->vga);
-        }
-        s->config = !!value;
-        break;
-
-    case SVGA_REG_SYNC:
-        s->syncing = 1;
-        vmsvga_fifo_run(s); /* Or should we just wait for update_display? */
-        break;
-
-    case SVGA_REG_GUEST_ID:
-        s->guest = value;
-#ifdef VERBOSE
-        if (value >= GUEST_OS_BASE && value < GUEST_OS_BASE +
-            ARRAY_SIZE(vmsvga_guest_id)) {
-            printf("%s: guest runs %s.\n", __func__,
-                   vmsvga_guest_id[value - GUEST_OS_BASE]);
-        }
-#endif
-        break;
-
-    case SVGA_REG_CURSOR_ID:
-        s->cursor.id = value;
-        break;
-
-    case SVGA_REG_CURSOR_X:
-        s->cursor.x = value;
-        break;
-
-    case SVGA_REG_CURSOR_Y:
-        s->cursor.y = value;
-        break;
-
-    case SVGA_REG_CURSOR_ON:
-        s->cursor.on |= (value == SVGA_CURSOR_ON_SHOW);
-        s->cursor.on &= (value != SVGA_CURSOR_ON_HIDE);
-#ifdef HW_MOUSE_ACCEL
-        if (value <= SVGA_CURSOR_ON_SHOW) {
-            dpy_mouse_set(s->vga.con, s->cursor.x, s->cursor.y, s->cursor.on);
-        }
-#endif
-        break;
-
-    case SVGA_REG_DEPTH:
-    case SVGA_REG_MEM_REGS:
-    case SVGA_REG_NUM_DISPLAYS:
-    case SVGA_REG_PITCHLOCK:
-    case SVGA_PALETTE_BASE ... SVGA_PALETTE_END:
-        break;
-
-    default:
-        if (s->index >= SVGA_SCRATCH_BASE &&
-                s->index < SVGA_SCRATCH_BASE + s->scratch_size) {
-            s->scratch[s->index - SVGA_SCRATCH_BASE] = value;
-            break;
-        }
-        printf("%s: Bad register %02x\n", __func__, s->index);
-    }
-}
-
-static uint32_t vmsvga_bios_read(void *opaque, uint32_t address)
-{
-    printf("%s: what are we supposed to return?\n", __func__);
-    return 0xcafe;
-}
-
-static void vmsvga_bios_write(void *opaque, uint32_t address, uint32_t data)
-{
-    printf("%s: what are we supposed to do with (%08x)?\n", __func__, data);
-}
-
-static inline void vmsvga_check_size(struct vmsvga_state_s *s)
-{
-    DisplaySurface *surface = qemu_console_surface(s->vga.con);
-
-    if (s->new_width != surface_width(surface) ||
-        s->new_height != surface_height(surface)) {
-        qemu_console_resize(s->vga.con, s->new_width, s->new_height);
-        s->invalidated = 1;
-    }
-}
-
-static void vmsvga_update_display(void *opaque)
-{
-    struct vmsvga_state_s *s = opaque;
-    DisplaySurface *surface = qemu_console_surface(s->vga.con);
-    bool dirty = false;
-
-    if (!s->enable) {
-        s->vga.update(&s->vga);
-        return;
-    }
-
-    vmsvga_check_size(s);
-
-    vmsvga_fifo_run(s);
-    vmsvga_update_rect_flush(s);
-
-    /*
-     * Is it more efficient to look at vram VGA-dirty bits or wait
-     * for the driver to issue SVGA_CMD_UPDATE?
-     */
-    if (memory_region_is_logging(&s->vga.vram)) {
-        vga_sync_dirty_bitmap(&s->vga);
-        dirty = memory_region_get_dirty(&s->vga.vram, 0,
-            surface_stride(surface) * surface_height(surface),
-            DIRTY_MEMORY_VGA);
-    }
-    if (s->invalidated || dirty) {
-        s->invalidated = 0;
-        memcpy(surface_data(surface), s->vga.vram_ptr,
-               surface_stride(surface) * surface_height(surface));
-        dpy_gfx_update(s->vga.con, 0, 0,
-                   surface_width(surface), surface_height(surface));
-    }
-    if (dirty) {
-        memory_region_reset_dirty(&s->vga.vram, 0,
-            surface_stride(surface) * surface_height(surface),
-            DIRTY_MEMORY_VGA);
-    }
-}
-
-static void vmsvga_reset(DeviceState *dev)
-{
-    struct pci_vmsvga_state_s *pci =
-        DO_UPCAST(struct pci_vmsvga_state_s, card.qdev, dev);
-    struct vmsvga_state_s *s = &pci->chip;
-
-    s->index = 0;
-    s->enable = 0;
-    s->config = 0;
-    s->svgaid = SVGA_ID;
-    s->cursor.on = 0;
-    s->redraw_fifo_first = 0;
-    s->redraw_fifo_last = 0;
-    s->syncing = 0;
-
-    vga_dirty_log_start(&s->vga);
-}
-
-static void vmsvga_invalidate_display(void *opaque)
-{
-    struct vmsvga_state_s *s = opaque;
-    if (!s->enable) {
-        s->vga.invalidate(&s->vga);
-        return;
-    }
-
-    s->invalidated = 1;
-}
-
-/* save the vga display in a PPM image even if no display is
-   available */
-static void vmsvga_screen_dump(void *opaque, const char *filename, bool cswitch,
-                               Error **errp)
-{
-    struct vmsvga_state_s *s = opaque;
-    DisplaySurface *surface = qemu_console_surface(s->vga.con);
-
-    if (!s->enable) {
-        s->vga.screen_dump(&s->vga, filename, cswitch, errp);
-        return;
-    }
-
-    if (surface_bits_per_pixel(surface) == 32) {
-        DisplaySurface *ds = qemu_create_displaysurface_from(
-                                 surface_width(surface),
-                                 surface_height(surface),
-                                 32,
-                                 surface_stride(surface),
-                                 s->vga.vram_ptr, false);
-        ppm_save(filename, ds, errp);
-        g_free(ds);
-    }
-}
-
-static void vmsvga_text_update(void *opaque, console_ch_t *chardata)
-{
-    struct vmsvga_state_s *s = opaque;
-
-    if (s->vga.text_update) {
-        s->vga.text_update(&s->vga, chardata);
-    }
-}
-
-static int vmsvga_post_load(void *opaque, int version_id)
-{
-    struct vmsvga_state_s *s = opaque;
-
-    s->invalidated = 1;
-    if (s->config) {
-        s->fifo = (uint32_t *) s->fifo_ptr;
-    }
-    return 0;
-}
-
-static const VMStateDescription vmstate_vmware_vga_internal = {
-    .name = "vmware_vga_internal",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .post_load = vmsvga_post_load,
-    .fields      = (VMStateField[]) {
-        VMSTATE_INT32_EQUAL(depth, struct vmsvga_state_s),
-        VMSTATE_INT32(enable, struct vmsvga_state_s),
-        VMSTATE_INT32(config, struct vmsvga_state_s),
-        VMSTATE_INT32(cursor.id, struct vmsvga_state_s),
-        VMSTATE_INT32(cursor.x, struct vmsvga_state_s),
-        VMSTATE_INT32(cursor.y, struct vmsvga_state_s),
-        VMSTATE_INT32(cursor.on, struct vmsvga_state_s),
-        VMSTATE_INT32(index, struct vmsvga_state_s),
-        VMSTATE_VARRAY_INT32(scratch, struct vmsvga_state_s,
-                             scratch_size, 0, vmstate_info_uint32, uint32_t),
-        VMSTATE_INT32(new_width, struct vmsvga_state_s),
-        VMSTATE_INT32(new_height, struct vmsvga_state_s),
-        VMSTATE_UINT32(guest, struct vmsvga_state_s),
-        VMSTATE_UINT32(svgaid, struct vmsvga_state_s),
-        VMSTATE_INT32(syncing, struct vmsvga_state_s),
-        VMSTATE_UNUSED(4), /* was fb_size */
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_vmware_vga = {
-    .name = "vmware_vga",
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .fields      = (VMStateField[]) {
-        VMSTATE_PCI_DEVICE(card, struct pci_vmsvga_state_s),
-        VMSTATE_STRUCT(chip, struct pci_vmsvga_state_s, 0,
-                       vmstate_vmware_vga_internal, struct vmsvga_state_s),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void vmsvga_init(struct vmsvga_state_s *s,
-                        MemoryRegion *address_space, MemoryRegion *io)
-{
-    DisplaySurface *surface;
-
-    s->scratch_size = SVGA_SCRATCH_SIZE;
-    s->scratch = g_malloc(s->scratch_size * 4);
-
-    s->vga.con = graphic_console_init(vmsvga_update_display,
-                                      vmsvga_invalidate_display,
-                                      vmsvga_screen_dump,
-                                      vmsvga_text_update, s);
-    surface = qemu_console_surface(s->vga.con);
-
-    s->fifo_size = SVGA_FIFO_SIZE;
-    memory_region_init_ram(&s->fifo_ram, "vmsvga.fifo", s->fifo_size);
-    vmstate_register_ram_global(&s->fifo_ram);
-    s->fifo_ptr = memory_region_get_ram_ptr(&s->fifo_ram);
-
-    vga_common_init(&s->vga);
-    vga_init(&s->vga, address_space, io, true);
-    vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga);
-    /* Save some values here in case they are changed later.
-     * This is suspicious and needs more though why it is needed. */
-    s->depth = surface_bits_per_pixel(surface);
-    s->bypp = surface_bytes_per_pixel(surface);
-}
-
-static uint64_t vmsvga_io_read(void *opaque, hwaddr addr, unsigned size)
-{
-    struct vmsvga_state_s *s = opaque;
-
-    switch (addr) {
-    case SVGA_IO_MUL * SVGA_INDEX_PORT: return vmsvga_index_read(s, addr);
-    case SVGA_IO_MUL * SVGA_VALUE_PORT: return vmsvga_value_read(s, addr);
-    case SVGA_IO_MUL * SVGA_BIOS_PORT: return vmsvga_bios_read(s, addr);
-    default: return -1u;
-    }
-}
-
-static void vmsvga_io_write(void *opaque, hwaddr addr,
-                            uint64_t data, unsigned size)
-{
-    struct vmsvga_state_s *s = opaque;
-
-    switch (addr) {
-    case SVGA_IO_MUL * SVGA_INDEX_PORT:
-        vmsvga_index_write(s, addr, data);
-        break;
-    case SVGA_IO_MUL * SVGA_VALUE_PORT:
-        vmsvga_value_write(s, addr, data);
-        break;
-    case SVGA_IO_MUL * SVGA_BIOS_PORT:
-        vmsvga_bios_write(s, addr, data);
-        break;
-    }
-}
-
-static const MemoryRegionOps vmsvga_io_ops = {
-    .read = vmsvga_io_read,
-    .write = vmsvga_io_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .valid = {
-        .min_access_size = 4,
-        .max_access_size = 4,
-    },
-};
-
-static int pci_vmsvga_initfn(PCIDevice *dev)
-{
-    struct pci_vmsvga_state_s *s =
-        DO_UPCAST(struct pci_vmsvga_state_s, card, dev);
-
-    s->card.config[PCI_CACHE_LINE_SIZE] = 0x08;         /* Cache line size */
-    s->card.config[PCI_LATENCY_TIMER] = 0x40;           /* Latency timer */
-    s->card.config[PCI_INTERRUPT_LINE] = 0xff;          /* End */
-
-    memory_region_init_io(&s->io_bar, &vmsvga_io_ops, &s->chip,
-                          "vmsvga-io", 0x10);
-    memory_region_set_flush_coalesced(&s->io_bar);
-    pci_register_bar(&s->card, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
-
-    vmsvga_init(&s->chip, pci_address_space(dev), pci_address_space_io(dev));
-
-    pci_register_bar(&s->card, 1, PCI_BASE_ADDRESS_MEM_PREFETCH,
-                     &s->chip.vga.vram);
-    pci_register_bar(&s->card, 2, PCI_BASE_ADDRESS_MEM_PREFETCH,
-                     &s->chip.fifo_ram);
-
-    if (!dev->rom_bar) {
-        /* compatibility with pc-0.13 and older */
-        vga_init_vbe(&s->chip.vga, pci_address_space(dev));
-    }
-
-    return 0;
-}
-
-static Property vga_vmware_properties[] = {
-    DEFINE_PROP_UINT32("vgamem_mb", struct pci_vmsvga_state_s,
-                       chip.vga.vram_size_mb, 16),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void vmsvga_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->no_hotplug = 1;
-    k->init = pci_vmsvga_initfn;
-    k->romfile = "vgabios-vmware.bin";
-    k->vendor_id = PCI_VENDOR_ID_VMWARE;
-    k->device_id = SVGA_PCI_DEVICE_ID;
-    k->class_id = PCI_CLASS_DISPLAY_VGA;
-    k->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE;
-    k->subsystem_id = SVGA_PCI_DEVICE_ID;
-    dc->reset = vmsvga_reset;
-    dc->vmsd = &vmstate_vmware_vga;
-    dc->props = vga_vmware_properties;
-}
-
-static const TypeInfo vmsvga_info = {
-    .name          = "vmware-svga",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(struct pci_vmsvga_state_s),
-    .class_init    = vmsvga_class_init,
-};
-
-static void vmsvga_register_types(void)
-{
-    type_register_static(&vmsvga_info);
-}
-
-type_init(vmsvga_register_types)
diff --git a/hw/vmxnet3.c b/hw/vmxnet3.c
deleted file mode 100644 (file)
index 5916624..0000000
+++ /dev/null
@@ -1,2460 +0,0 @@
-/*
- * QEMU VMWARE VMXNET3 paravirtual NIC
- *
- * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
- *
- * Developed by Daynix Computing LTD (http://www.daynix.com)
- *
- * Authors:
- * Dmitry Fleytman <dmitry@daynix.com>
- * Tamir Shomer <tamirs@daynix.com>
- * Yan Vugenfirer <yan@daynix.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.
- * See the COPYING file in the top-level directory.
- *
- */
-
-#include "hw/hw.h"
-#include "hw/pci/pci.h"
-#include "net/net.h"
-#include "net/tap.h"
-#include "net/checksum.h"
-#include "sysemu/sysemu.h"
-#include "qemu-common.h"
-#include "qemu/bswap.h"
-#include "hw/pci/msix.h"
-#include "hw/pci/msi.h"
-
-#include "vmxnet3.h"
-#include "vmxnet_debug.h"
-#include "vmware_utils.h"
-#include "vmxnet_tx_pkt.h"
-#include "vmxnet_rx_pkt.h"
-
-#define PCI_DEVICE_ID_VMWARE_VMXNET3_REVISION 0x1
-#define VMXNET3_MSIX_BAR_SIZE 0x2000
-
-#define VMXNET3_BAR0_IDX      (0)
-#define VMXNET3_BAR1_IDX      (1)
-#define VMXNET3_MSIX_BAR_IDX  (2)
-
-#define VMXNET3_OFF_MSIX_TABLE (0x000)
-#define VMXNET3_OFF_MSIX_PBA   (0x800)
-
-/* Link speed in Mbps should be shifted by 16 */
-#define VMXNET3_LINK_SPEED      (1000 << 16)
-
-/* Link status: 1 - up, 0 - down. */
-#define VMXNET3_LINK_STATUS_UP  0x1
-
-/* Least significant bit should be set for revision and version */
-#define VMXNET3_DEVICE_VERSION    0x1
-#define VMXNET3_DEVICE_REVISION   0x1
-
-/* Macros for rings descriptors access */
-#define VMXNET3_READ_TX_QUEUE_DESCR8(dpa, field) \
-    (vmw_shmem_ld8(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field)))
-
-#define VMXNET3_WRITE_TX_QUEUE_DESCR8(dpa, field, value) \
-    (vmw_shmem_st8(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field, value)))
-
-#define VMXNET3_READ_TX_QUEUE_DESCR32(dpa, field) \
-    (vmw_shmem_ld32(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field)))
-
-#define VMXNET3_WRITE_TX_QUEUE_DESCR32(dpa, field, value) \
-    (vmw_shmem_st32(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field), value))
-
-#define VMXNET3_READ_TX_QUEUE_DESCR64(dpa, field) \
-    (vmw_shmem_ld64(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field)))
-
-#define VMXNET3_WRITE_TX_QUEUE_DESCR64(dpa, field, value) \
-    (vmw_shmem_st64(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field), value))
-
-#define VMXNET3_READ_RX_QUEUE_DESCR64(dpa, field) \
-    (vmw_shmem_ld64(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field)))
-
-#define VMXNET3_READ_RX_QUEUE_DESCR32(dpa, field) \
-    (vmw_shmem_ld32(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field)))
-
-#define VMXNET3_WRITE_RX_QUEUE_DESCR64(dpa, field, value) \
-    (vmw_shmem_st64(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field), value))
-
-#define VMXNET3_WRITE_RX_QUEUE_DESCR8(dpa, field, value) \
-    (vmw_shmem_st8(dpa + offsetof(struct Vmxnet3_RxQueueDesc, field), value))
-
-/* Macros for guest driver shared area access */
-#define VMXNET3_READ_DRV_SHARED64(shpa, field) \
-    (vmw_shmem_ld64(shpa + offsetof(struct Vmxnet3_DriverShared, field)))
-
-#define VMXNET3_READ_DRV_SHARED32(shpa, field) \
-    (vmw_shmem_ld32(shpa + offsetof(struct Vmxnet3_DriverShared, field)))
-
-#define VMXNET3_WRITE_DRV_SHARED32(shpa, field, val) \
-    (vmw_shmem_st32(shpa + offsetof(struct Vmxnet3_DriverShared, field), val))
-
-#define VMXNET3_READ_DRV_SHARED16(shpa, field) \
-    (vmw_shmem_ld16(shpa + offsetof(struct Vmxnet3_DriverShared, field)))
-
-#define VMXNET3_READ_DRV_SHARED8(shpa, field) \
-    (vmw_shmem_ld8(shpa + offsetof(struct Vmxnet3_DriverShared, field)))
-
-#define VMXNET3_READ_DRV_SHARED(shpa, field, b, l) \
-    (vmw_shmem_read(shpa + offsetof(struct Vmxnet3_DriverShared, field), b, l))
-
-#define VMXNET_FLAG_IS_SET(field, flag) (((field) & (flag)) == (flag))
-
-#define TYPE_VMXNET3 "vmxnet3"
-#define VMXNET3(obj) OBJECT_CHECK(VMXNET3State, (obj), TYPE_VMXNET3)
-
-/* Cyclic ring abstraction */
-typedef struct {
-    hwaddr pa;
-    size_t size;
-    size_t cell_size;
-    size_t next;
-    uint8_t gen;
-} Vmxnet3Ring;
-
-static inline void vmxnet3_ring_init(Vmxnet3Ring *ring,
-                                     hwaddr pa,
-                                     size_t size,
-                                     size_t cell_size,
-                                     bool zero_region)
-{
-    ring->pa = pa;
-    ring->size = size;
-    ring->cell_size = cell_size;
-    ring->gen = VMXNET3_INIT_GEN;
-    ring->next = 0;
-
-    if (zero_region) {
-        vmw_shmem_set(pa, 0, size * cell_size);
-    }
-}
-
-#define VMXNET3_RING_DUMP(macro, ring_name, ridx, r)                         \
-    macro("%s#%d: base %" PRIx64 " size %lu cell_size %lu gen %d next %lu",  \
-          (ring_name), (ridx),                                               \
-          (r)->pa, (r)->size, (r)->cell_size, (r)->gen, (r)->next)
-
-static inline void vmxnet3_ring_inc(Vmxnet3Ring *ring)
-{
-    if (++ring->next >= ring->size) {
-        ring->next = 0;
-        ring->gen ^= 1;
-    }
-}
-
-static inline void vmxnet3_ring_dec(Vmxnet3Ring *ring)
-{
-    if (ring->next-- == 0) {
-        ring->next = ring->size - 1;
-        ring->gen ^= 1;
-    }
-}
-
-static inline hwaddr vmxnet3_ring_curr_cell_pa(Vmxnet3Ring *ring)
-{
-    return ring->pa + ring->next * ring->cell_size;
-}
-
-static inline void vmxnet3_ring_read_curr_cell(Vmxnet3Ring *ring, void *buff)
-{
-    vmw_shmem_read(vmxnet3_ring_curr_cell_pa(ring), buff, ring->cell_size);
-}
-
-static inline void vmxnet3_ring_write_curr_cell(Vmxnet3Ring *ring, void *buff)
-{
-    vmw_shmem_write(vmxnet3_ring_curr_cell_pa(ring), buff, ring->cell_size);
-}
-
-static inline size_t vmxnet3_ring_curr_cell_idx(Vmxnet3Ring *ring)
-{
-    return ring->next;
-}
-
-static inline uint8_t vmxnet3_ring_curr_gen(Vmxnet3Ring *ring)
-{
-    return ring->gen;
-}
-
-/* Debug trace-related functions */
-static inline void
-vmxnet3_dump_tx_descr(struct Vmxnet3_TxDesc *descr)
-{
-    VMW_PKPRN("TX DESCR: "
-              "addr %" PRIx64 ", len: %d, gen: %d, rsvd: %d, "
-              "dtype: %d, ext1: %d, msscof: %d, hlen: %d, om: %d, "
-              "eop: %d, cq: %d, ext2: %d, ti: %d, tci: %d",
-              le64_to_cpu(descr->addr), descr->len, descr->gen, descr->rsvd,
-              descr->dtype, descr->ext1, descr->msscof, descr->hlen, descr->om,
-              descr->eop, descr->cq, descr->ext2, descr->ti, descr->tci);
-}
-
-static inline void
-vmxnet3_dump_virt_hdr(struct virtio_net_hdr *vhdr)
-{
-    VMW_PKPRN("VHDR: flags 0x%x, gso_type: 0x%x, hdr_len: %d, gso_size: %d, "
-              "csum_start: %d, csum_offset: %d",
-              vhdr->flags, vhdr->gso_type, vhdr->hdr_len, vhdr->gso_size,
-              vhdr->csum_start, vhdr->csum_offset);
-}
-
-static inline void
-vmxnet3_dump_rx_descr(struct Vmxnet3_RxDesc *descr)
-{
-    VMW_PKPRN("RX DESCR: addr %" PRIx64 ", len: %d, gen: %d, rsvd: %d, "
-              "dtype: %d, ext1: %d, btype: %d",
-              le64_to_cpu(descr->addr), descr->len, descr->gen,
-              descr->rsvd, descr->dtype, descr->ext1, descr->btype);
-}
-
-/* Device state and helper functions */
-#define VMXNET3_RX_RINGS_PER_QUEUE (2)
-
-typedef struct {
-    Vmxnet3Ring tx_ring;
-    Vmxnet3Ring comp_ring;
-
-    uint8_t intr_idx;
-    hwaddr tx_stats_pa;
-    struct UPT1_TxStats txq_stats;
-} Vmxnet3TxqDescr;
-
-typedef struct {
-    Vmxnet3Ring rx_ring[VMXNET3_RX_RINGS_PER_QUEUE];
-    Vmxnet3Ring comp_ring;
-    uint8_t intr_idx;
-    hwaddr rx_stats_pa;
-    struct UPT1_RxStats rxq_stats;
-} Vmxnet3RxqDescr;
-
-typedef struct {
-    bool is_masked;
-    bool is_pending;
-    bool is_asserted;
-} Vmxnet3IntState;
-
-typedef struct {
-        PCIDevice parent_obj;
-        NICState *nic;
-        NICConf conf;
-        MemoryRegion bar0;
-        MemoryRegion bar1;
-        MemoryRegion msix_bar;
-
-        Vmxnet3RxqDescr rxq_descr[VMXNET3_DEVICE_MAX_RX_QUEUES];
-        Vmxnet3TxqDescr txq_descr[VMXNET3_DEVICE_MAX_TX_QUEUES];
-
-        /* Whether MSI-X support was installed successfully */
-        bool msix_used;
-        /* Whether MSI support was installed successfully */
-        bool msi_used;
-        hwaddr drv_shmem;
-        hwaddr temp_shared_guest_driver_memory;
-
-        uint8_t txq_num;
-
-        /* This boolean tells whether RX packet being indicated has to */
-        /* be split into head and body chunks from different RX rings  */
-        bool rx_packets_compound;
-
-        bool rx_vlan_stripping;
-        bool lro_supported;
-
-        uint8_t rxq_num;
-
-        /* Network MTU */
-        uint32_t mtu;
-
-        /* Maximum number of fragments for indicated TX packets */
-        uint32_t max_tx_frags;
-
-        /* Maximum number of fragments for indicated RX packets */
-        uint16_t max_rx_frags;
-
-        /* Index for events interrupt */
-        uint8_t event_int_idx;
-
-        /* Whether automatic interrupts masking enabled */
-        bool auto_int_masking;
-
-        bool peer_has_vhdr;
-
-        /* TX packets to QEMU interface */
-        struct VmxnetTxPkt *tx_pkt;
-        uint32_t offload_mode;
-        uint32_t cso_or_gso_size;
-        uint16_t tci;
-        bool needs_vlan;
-
-        struct VmxnetRxPkt *rx_pkt;
-
-        bool tx_sop;
-        bool skip_current_tx_pkt;
-
-        uint32_t device_active;
-        uint32_t last_command;
-
-        uint32_t link_status_and_speed;
-
-        Vmxnet3IntState interrupt_states[VMXNET3_MAX_INTRS];
-
-        uint32_t temp_mac;   /* To store the low part first */
-
-        MACAddr perm_mac;
-        uint32_t vlan_table[VMXNET3_VFT_SIZE];
-        uint32_t rx_mode;
-        MACAddr *mcast_list;
-        uint32_t mcast_list_len;
-        uint32_t mcast_list_buff_size; /* needed for live migration. */
-} VMXNET3State;
-
-/* Interrupt management */
-
-/*
- *This function returns sign whether interrupt line is in asserted state
- * This depends on the type of interrupt used. For INTX interrupt line will
- * be asserted until explicit deassertion, for MSI(X) interrupt line will
- * be deasserted automatically due to notification semantics of the MSI(X)
- * interrupts
- */
-static bool _vmxnet3_assert_interrupt_line(VMXNET3State *s, uint32_t int_idx)
-{
-    PCIDevice *d = PCI_DEVICE(s);
-
-    if (s->msix_used && msix_enabled(d)) {
-        VMW_IRPRN("Sending MSI-X notification for vector %u", int_idx);
-        msix_notify(d, int_idx);
-        return false;
-    }
-    if (s->msi_used && msi_enabled(d)) {
-        VMW_IRPRN("Sending MSI notification for vector %u", int_idx);
-        msi_notify(d, int_idx);
-        return false;
-    }
-
-    VMW_IRPRN("Asserting line for interrupt %u", int_idx);
-    qemu_set_irq(d->irq[int_idx], 1);
-    return true;
-}
-
-static void _vmxnet3_deassert_interrupt_line(VMXNET3State *s, int lidx)
-{
-    PCIDevice *d = PCI_DEVICE(s);
-
-    /*
-     * This function should never be called for MSI(X) interrupts
-     * because deassertion never required for message interrupts
-     */
-    assert(!s->msix_used || !msix_enabled(d));
-    /*
-     * This function should never be called for MSI(X) interrupts
-     * because deassertion never required for message interrupts
-     */
-    assert(!s->msi_used || !msi_enabled(d));
-
-    VMW_IRPRN("Deasserting line for interrupt %u", lidx);
-    qemu_set_irq(d->irq[lidx], 0);
-}
-
-static void vmxnet3_update_interrupt_line_state(VMXNET3State *s, int lidx)
-{
-    if (!s->interrupt_states[lidx].is_pending &&
-       s->interrupt_states[lidx].is_asserted) {
-        VMW_IRPRN("New interrupt line state for index %d is DOWN", lidx);
-        _vmxnet3_deassert_interrupt_line(s, lidx);
-        s->interrupt_states[lidx].is_asserted = false;
-        return;
-    }
-
-    if (s->interrupt_states[lidx].is_pending &&
-       !s->interrupt_states[lidx].is_masked &&
-       !s->interrupt_states[lidx].is_asserted) {
-        VMW_IRPRN("New interrupt line state for index %d is UP", lidx);
-        s->interrupt_states[lidx].is_asserted =
-            _vmxnet3_assert_interrupt_line(s, lidx);
-        s->interrupt_states[lidx].is_pending = false;
-        return;
-    }
-}
-
-static void vmxnet3_trigger_interrupt(VMXNET3State *s, int lidx)
-{
-    PCIDevice *d = PCI_DEVICE(s);
-    s->interrupt_states[lidx].is_pending = true;
-    vmxnet3_update_interrupt_line_state(s, lidx);
-
-    if (s->msix_used && msix_enabled(d) && s->auto_int_masking) {
-        goto do_automask;
-    }
-
-    if (s->msi_used && msi_enabled(d) && s->auto_int_masking) {
-        goto do_automask;
-    }
-
-    return;
-
-do_automask:
-    s->interrupt_states[lidx].is_masked = true;
-    vmxnet3_update_interrupt_line_state(s, lidx);
-}
-
-static bool vmxnet3_interrupt_asserted(VMXNET3State *s, int lidx)
-{
-    return s->interrupt_states[lidx].is_asserted;
-}
-
-static void vmxnet3_clear_interrupt(VMXNET3State *s, int int_idx)
-{
-    s->interrupt_states[int_idx].is_pending = false;
-    if (s->auto_int_masking) {
-        s->interrupt_states[int_idx].is_masked = true;
-    }
-    vmxnet3_update_interrupt_line_state(s, int_idx);
-}
-
-static void
-vmxnet3_on_interrupt_mask_changed(VMXNET3State *s, int lidx, bool is_masked)
-{
-    s->interrupt_states[lidx].is_masked = is_masked;
-    vmxnet3_update_interrupt_line_state(s, lidx);
-}
-
-static bool vmxnet3_verify_driver_magic(hwaddr dshmem)
-{
-    return (VMXNET3_READ_DRV_SHARED32(dshmem, magic) == VMXNET3_REV1_MAGIC);
-}
-
-#define VMXNET3_GET_BYTE(x, byte_num) (((x) >> (byte_num)*8) & 0xFF)
-#define VMXNET3_MAKE_BYTE(byte_num, val) \
-    (((uint32_t)((val) & 0xFF)) << (byte_num)*8)
-
-static void vmxnet3_set_variable_mac(VMXNET3State *s, uint32_t h, uint32_t l)
-{
-    s->conf.macaddr.a[0] = VMXNET3_GET_BYTE(l,  0);
-    s->conf.macaddr.a[1] = VMXNET3_GET_BYTE(l,  1);
-    s->conf.macaddr.a[2] = VMXNET3_GET_BYTE(l,  2);
-    s->conf.macaddr.a[3] = VMXNET3_GET_BYTE(l,  3);
-    s->conf.macaddr.a[4] = VMXNET3_GET_BYTE(h, 0);
-    s->conf.macaddr.a[5] = VMXNET3_GET_BYTE(h, 1);
-
-    VMW_CFPRN("Variable MAC: " VMXNET_MF, VMXNET_MA(s->conf.macaddr.a));
-
-    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
-}
-
-static uint64_t vmxnet3_get_mac_low(MACAddr *addr)
-{
-    return VMXNET3_MAKE_BYTE(0, addr->a[0]) |
-           VMXNET3_MAKE_BYTE(1, addr->a[1]) |
-           VMXNET3_MAKE_BYTE(2, addr->a[2]) |
-           VMXNET3_MAKE_BYTE(3, addr->a[3]);
-}
-
-static uint64_t vmxnet3_get_mac_high(MACAddr *addr)
-{
-    return VMXNET3_MAKE_BYTE(0, addr->a[4]) |
-           VMXNET3_MAKE_BYTE(1, addr->a[5]);
-}
-
-static void
-vmxnet3_inc_tx_consumption_counter(VMXNET3State *s, int qidx)
-{
-    vmxnet3_ring_inc(&s->txq_descr[qidx].tx_ring);
-}
-
-static inline void
-vmxnet3_inc_rx_consumption_counter(VMXNET3State *s, int qidx, int ridx)
-{
-    vmxnet3_ring_inc(&s->rxq_descr[qidx].rx_ring[ridx]);
-}
-
-static inline void
-vmxnet3_inc_tx_completion_counter(VMXNET3State *s, int qidx)
-{
-    vmxnet3_ring_inc(&s->txq_descr[qidx].comp_ring);
-}
-
-static void
-vmxnet3_inc_rx_completion_counter(VMXNET3State *s, int qidx)
-{
-    vmxnet3_ring_inc(&s->rxq_descr[qidx].comp_ring);
-}
-
-static void
-vmxnet3_dec_rx_completion_counter(VMXNET3State *s, int qidx)
-{
-    vmxnet3_ring_dec(&s->rxq_descr[qidx].comp_ring);
-}
-
-static void vmxnet3_complete_packet(VMXNET3State *s, int qidx, uint32 tx_ridx)
-{
-    struct Vmxnet3_TxCompDesc txcq_descr;
-
-    VMXNET3_RING_DUMP(VMW_RIPRN, "TXC", qidx, &s->txq_descr[qidx].comp_ring);
-
-    txcq_descr.txdIdx = tx_ridx;
-    txcq_descr.gen = vmxnet3_ring_curr_gen(&s->txq_descr[qidx].comp_ring);
-
-    vmxnet3_ring_write_curr_cell(&s->txq_descr[qidx].comp_ring, &txcq_descr);
-
-    /* Flush changes in TX descriptor before changing the counter value */
-    smp_wmb();
-
-    vmxnet3_inc_tx_completion_counter(s, qidx);
-    vmxnet3_trigger_interrupt(s, s->txq_descr[qidx].intr_idx);
-}
-
-static bool
-vmxnet3_setup_tx_offloads(VMXNET3State *s)
-{
-    switch (s->offload_mode) {
-    case VMXNET3_OM_NONE:
-        vmxnet_tx_pkt_build_vheader(s->tx_pkt, false, false, 0);
-        break;
-
-    case VMXNET3_OM_CSUM:
-        vmxnet_tx_pkt_build_vheader(s->tx_pkt, false, true, 0);
-        VMW_PKPRN("L4 CSO requested\n");
-        break;
-
-    case VMXNET3_OM_TSO:
-        vmxnet_tx_pkt_build_vheader(s->tx_pkt, true, true,
-            s->cso_or_gso_size);
-        vmxnet_tx_pkt_update_ip_checksums(s->tx_pkt);
-        VMW_PKPRN("GSO offload requested.");
-        break;
-
-    default:
-        assert(false);
-        return false;
-    }
-
-    return true;
-}
-
-static void
-vmxnet3_tx_retrieve_metadata(VMXNET3State *s,
-                             const struct Vmxnet3_TxDesc *txd)
-{
-    s->offload_mode = txd->om;
-    s->cso_or_gso_size = txd->msscof;
-    s->tci = txd->tci;
-    s->needs_vlan = txd->ti;
-}
-
-typedef enum {
-    VMXNET3_PKT_STATUS_OK,
-    VMXNET3_PKT_STATUS_ERROR,
-    VMXNET3_PKT_STATUS_DISCARD,/* only for tx */
-    VMXNET3_PKT_STATUS_OUT_OF_BUF /* only for rx */
-} Vmxnet3PktStatus;
-
-static void
-vmxnet3_on_tx_done_update_stats(VMXNET3State *s, int qidx,
-    Vmxnet3PktStatus status)
-{
-    size_t tot_len = vmxnet_tx_pkt_get_total_len(s->tx_pkt);
-    struct UPT1_TxStats *stats = &s->txq_descr[qidx].txq_stats;
-
-    switch (status) {
-    case VMXNET3_PKT_STATUS_OK:
-        switch (vmxnet_tx_pkt_get_packet_type(s->tx_pkt)) {
-        case ETH_PKT_BCAST:
-            stats->bcastPktsTxOK++;
-            stats->bcastBytesTxOK += tot_len;
-            break;
-        case ETH_PKT_MCAST:
-            stats->mcastPktsTxOK++;
-            stats->mcastBytesTxOK += tot_len;
-            break;
-        case ETH_PKT_UCAST:
-            stats->ucastPktsTxOK++;
-            stats->ucastBytesTxOK += tot_len;
-            break;
-        default:
-            assert(false);
-        }
-
-        if (s->offload_mode == VMXNET3_OM_TSO) {
-            /*
-             * According to VMWARE headers this statistic is a number
-             * of packets after segmentation but since we don't have
-             * this information in QEMU model, the best we can do is to
-             * provide number of non-segmented packets
-             */
-            stats->TSOPktsTxOK++;
-            stats->TSOBytesTxOK += tot_len;
-        }
-        break;
-
-    case VMXNET3_PKT_STATUS_DISCARD:
-        stats->pktsTxDiscard++;
-        break;
-
-    case VMXNET3_PKT_STATUS_ERROR:
-        stats->pktsTxError++;
-        break;
-
-    default:
-        assert(false);
-    }
-}
-
-static void
-vmxnet3_on_rx_done_update_stats(VMXNET3State *s,
-                                int qidx,
-                                Vmxnet3PktStatus status)
-{
-    struct UPT1_RxStats *stats = &s->rxq_descr[qidx].rxq_stats;
-    size_t tot_len = vmxnet_rx_pkt_get_total_len(s->rx_pkt);
-
-    switch (status) {
-    case VMXNET3_PKT_STATUS_OUT_OF_BUF:
-        stats->pktsRxOutOfBuf++;
-        break;
-
-    case VMXNET3_PKT_STATUS_ERROR:
-        stats->pktsRxError++;
-        break;
-    case VMXNET3_PKT_STATUS_OK:
-        switch (vmxnet_rx_pkt_get_packet_type(s->rx_pkt)) {
-        case ETH_PKT_BCAST:
-            stats->bcastPktsRxOK++;
-            stats->bcastBytesRxOK += tot_len;
-            break;
-        case ETH_PKT_MCAST:
-            stats->mcastPktsRxOK++;
-            stats->mcastBytesRxOK += tot_len;
-            break;
-        case ETH_PKT_UCAST:
-            stats->ucastPktsRxOK++;
-            stats->ucastBytesRxOK += tot_len;
-            break;
-        default:
-            assert(false);
-        }
-
-        if (tot_len > s->mtu) {
-            stats->LROPktsRxOK++;
-            stats->LROBytesRxOK += tot_len;
-        }
-        break;
-    default:
-        assert(false);
-    }
-}
-
-static inline bool
-vmxnet3_pop_next_tx_descr(VMXNET3State *s,
-                          int qidx,
-                          struct Vmxnet3_TxDesc *txd,
-                          uint32_t *descr_idx)
-{
-    Vmxnet3Ring *ring = &s->txq_descr[qidx].tx_ring;
-
-    vmxnet3_ring_read_curr_cell(ring, txd);
-    if (txd->gen == vmxnet3_ring_curr_gen(ring)) {
-        /* Only read after generation field verification */
-        smp_rmb();
-        /* Re-read to be sure we got the latest version */
-        vmxnet3_ring_read_curr_cell(ring, txd);
-        VMXNET3_RING_DUMP(VMW_RIPRN, "TX", qidx, ring);
-        *descr_idx = vmxnet3_ring_curr_cell_idx(ring);
-        vmxnet3_inc_tx_consumption_counter(s, qidx);
-        return true;
-    }
-
-    return false;
-}
-
-static bool
-vmxnet3_send_packet(VMXNET3State *s, uint32_t qidx)
-{
-    Vmxnet3PktStatus status = VMXNET3_PKT_STATUS_OK;
-
-    if (!vmxnet3_setup_tx_offloads(s)) {
-        status = VMXNET3_PKT_STATUS_ERROR;
-        goto func_exit;
-    }
-
-    /* debug prints */
-    vmxnet3_dump_virt_hdr(vmxnet_tx_pkt_get_vhdr(s->tx_pkt));
-    vmxnet_tx_pkt_dump(s->tx_pkt);
-
-    if (!vmxnet_tx_pkt_send(s->tx_pkt, qemu_get_queue(s->nic))) {
-        status = VMXNET3_PKT_STATUS_DISCARD;
-        goto func_exit;
-    }
-
-func_exit:
-    vmxnet3_on_tx_done_update_stats(s, qidx, status);
-    return (status == VMXNET3_PKT_STATUS_OK);
-}
-
-static void vmxnet3_process_tx_queue(VMXNET3State *s, int qidx)
-{
-    struct Vmxnet3_TxDesc txd;
-    uint32_t txd_idx;
-    uint32_t data_len;
-    hwaddr data_pa;
-
-    for (;;) {
-        if (!vmxnet3_pop_next_tx_descr(s, qidx, &txd, &txd_idx)) {
-            break;
-        }
-
-        vmxnet3_dump_tx_descr(&txd);
-
-        if (!s->skip_current_tx_pkt) {
-            data_len = (txd.len > 0) ? txd.len : VMXNET3_MAX_TX_BUF_SIZE;
-            data_pa = le64_to_cpu(txd.addr);
-
-            if (!vmxnet_tx_pkt_add_raw_fragment(s->tx_pkt,
-                                                data_pa,
-                                                data_len)) {
-                s->skip_current_tx_pkt = true;
-            }
-        }
-
-        if (s->tx_sop) {
-            vmxnet3_tx_retrieve_metadata(s, &txd);
-            s->tx_sop = false;
-        }
-
-        if (txd.eop) {
-            if (!s->skip_current_tx_pkt) {
-                vmxnet_tx_pkt_parse(s->tx_pkt);
-
-                if (s->needs_vlan) {
-                    vmxnet_tx_pkt_setup_vlan_header(s->tx_pkt, s->tci);
-                }
-
-                vmxnet3_send_packet(s, qidx);
-            } else {
-                vmxnet3_on_tx_done_update_stats(s, qidx,
-                                                VMXNET3_PKT_STATUS_ERROR);
-            }
-
-            vmxnet3_complete_packet(s, qidx, txd_idx);
-            s->tx_sop = true;
-            s->skip_current_tx_pkt = false;
-            vmxnet_tx_pkt_reset(s->tx_pkt);
-        }
-    }
-}
-
-static inline void
-vmxnet3_read_next_rx_descr(VMXNET3State *s, int qidx, int ridx,
-                           struct Vmxnet3_RxDesc *dbuf, uint32_t *didx)
-{
-    Vmxnet3Ring *ring = &s->rxq_descr[qidx].rx_ring[ridx];
-    *didx = vmxnet3_ring_curr_cell_idx(ring);
-    vmxnet3_ring_read_curr_cell(ring, dbuf);
-}
-
-static inline uint8_t
-vmxnet3_get_rx_ring_gen(VMXNET3State *s, int qidx, int ridx)
-{
-    return s->rxq_descr[qidx].rx_ring[ridx].gen;
-}
-
-static inline hwaddr
-vmxnet3_pop_rxc_descr(VMXNET3State *s, int qidx, uint32_t *descr_gen)
-{
-    uint8_t ring_gen;
-    struct Vmxnet3_RxCompDesc rxcd;
-
-    hwaddr daddr =
-        vmxnet3_ring_curr_cell_pa(&s->rxq_descr[qidx].comp_ring);
-
-    cpu_physical_memory_read(daddr, &rxcd, sizeof(struct Vmxnet3_RxCompDesc));
-    ring_gen = vmxnet3_ring_curr_gen(&s->rxq_descr[qidx].comp_ring);
-
-    if (rxcd.gen != ring_gen) {
-        *descr_gen = ring_gen;
-        vmxnet3_inc_rx_completion_counter(s, qidx);
-        return daddr;
-    }
-
-    return 0;
-}
-
-static inline void
-vmxnet3_revert_rxc_descr(VMXNET3State *s, int qidx)
-{
-    vmxnet3_dec_rx_completion_counter(s, qidx);
-}
-
-#define RXQ_IDX      (0)
-#define RX_HEAD_BODY_RING (0)
-#define RX_BODY_ONLY_RING (1)
-
-static bool
-vmxnet3_get_next_head_rx_descr(VMXNET3State *s,
-                               struct Vmxnet3_RxDesc *descr_buf,
-                               uint32_t *descr_idx,
-                               uint32_t *ridx)
-{
-    for (;;) {
-        uint32_t ring_gen;
-        vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING,
-                                   descr_buf, descr_idx);
-
-        /* If no more free descriptors - return */
-        ring_gen = vmxnet3_get_rx_ring_gen(s, RXQ_IDX, RX_HEAD_BODY_RING);
-        if (descr_buf->gen != ring_gen) {
-            return false;
-        }
-
-        /* Only read after generation field verification */
-        smp_rmb();
-        /* Re-read to be sure we got the latest version */
-        vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING,
-                                   descr_buf, descr_idx);
-
-        /* Mark current descriptor as used/skipped */
-        vmxnet3_inc_rx_consumption_counter(s, RXQ_IDX, RX_HEAD_BODY_RING);
-
-        /* If this is what we are looking for - return */
-        if (descr_buf->btype == VMXNET3_RXD_BTYPE_HEAD) {
-            *ridx = RX_HEAD_BODY_RING;
-            return true;
-        }
-    }
-}
-
-static bool
-vmxnet3_get_next_body_rx_descr(VMXNET3State *s,
-                               struct Vmxnet3_RxDesc *d,
-                               uint32_t *didx,
-                               uint32_t *ridx)
-{
-    vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING, d, didx);
-
-    /* Try to find corresponding descriptor in head/body ring */
-    if (d->gen == vmxnet3_get_rx_ring_gen(s, RXQ_IDX, RX_HEAD_BODY_RING)) {
-        /* Only read after generation field verification */
-        smp_rmb();
-        /* Re-read to be sure we got the latest version */
-        vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_HEAD_BODY_RING, d, didx);
-        if (d->btype == VMXNET3_RXD_BTYPE_BODY) {
-            vmxnet3_inc_rx_consumption_counter(s, RXQ_IDX, RX_HEAD_BODY_RING);
-            *ridx = RX_HEAD_BODY_RING;
-            return true;
-        }
-    }
-
-    /*
-     * If there is no free descriptors on head/body ring or next free
-     * descriptor is a head descriptor switch to body only ring
-     */
-    vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_BODY_ONLY_RING, d, didx);
-
-    /* If no more free descriptors - return */
-    if (d->gen == vmxnet3_get_rx_ring_gen(s, RXQ_IDX, RX_BODY_ONLY_RING)) {
-        /* Only read after generation field verification */
-        smp_rmb();
-        /* Re-read to be sure we got the latest version */
-        vmxnet3_read_next_rx_descr(s, RXQ_IDX, RX_BODY_ONLY_RING, d, didx);
-        assert(d->btype == VMXNET3_RXD_BTYPE_BODY);
-        *ridx = RX_BODY_ONLY_RING;
-        vmxnet3_inc_rx_consumption_counter(s, RXQ_IDX, RX_BODY_ONLY_RING);
-        return true;
-    }
-
-    return false;
-}
-
-static inline bool
-vmxnet3_get_next_rx_descr(VMXNET3State *s, bool is_head,
-                          struct Vmxnet3_RxDesc *descr_buf,
-                          uint32_t *descr_idx,
-                          uint32_t *ridx)
-{
-    if (is_head || !s->rx_packets_compound) {
-        return vmxnet3_get_next_head_rx_descr(s, descr_buf, descr_idx, ridx);
-    } else {
-        return vmxnet3_get_next_body_rx_descr(s, descr_buf, descr_idx, ridx);
-    }
-}
-
-static void vmxnet3_rx_update_descr(struct VmxnetRxPkt *pkt,
-    struct Vmxnet3_RxCompDesc *rxcd)
-{
-    int csum_ok, is_gso;
-    bool isip4, isip6, istcp, isudp;
-    struct virtio_net_hdr *vhdr;
-    uint8_t offload_type;
-
-    if (vmxnet_rx_pkt_is_vlan_stripped(pkt)) {
-        rxcd->ts = 1;
-        rxcd->tci = vmxnet_rx_pkt_get_vlan_tag(pkt);
-    }
-
-    if (!vmxnet_rx_pkt_has_virt_hdr(pkt)) {
-        goto nocsum;
-    }
-
-    vhdr = vmxnet_rx_pkt_get_vhdr(pkt);
-    /*
-     * Checksum is valid when lower level tell so or when lower level
-     * requires checksum offload telling that packet produced/bridged
-     * locally and did travel over network after last checksum calculation
-     * or production
-     */
-    csum_ok = VMXNET_FLAG_IS_SET(vhdr->flags, VIRTIO_NET_HDR_F_DATA_VALID) ||
-              VMXNET_FLAG_IS_SET(vhdr->flags, VIRTIO_NET_HDR_F_NEEDS_CSUM);
-
-    offload_type = vhdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN;
-    is_gso = (offload_type != VIRTIO_NET_HDR_GSO_NONE) ? 1 : 0;
-
-    if (!csum_ok && !is_gso) {
-        goto nocsum;
-    }
-
-    vmxnet_rx_pkt_get_protocols(pkt, &isip4, &isip6, &isudp, &istcp);
-    if ((!istcp && !isudp) || (!isip4 && !isip6)) {
-        goto nocsum;
-    }
-
-    rxcd->cnc = 0;
-    rxcd->v4 = isip4 ? 1 : 0;
-    rxcd->v6 = isip6 ? 1 : 0;
-    rxcd->tcp = istcp ? 1 : 0;
-    rxcd->udp = isudp ? 1 : 0;
-    rxcd->fcs = rxcd->tuc = rxcd->ipc = 1;
-    return;
-
-nocsum:
-    rxcd->cnc = 1;
-    return;
-}
-
-static void
-vmxnet3_physical_memory_writev(const struct iovec *iov,
-                               size_t start_iov_off,
-                               hwaddr target_addr,
-                               size_t bytes_to_copy)
-{
-    size_t curr_off = 0;
-    size_t copied = 0;
-
-    while (bytes_to_copy) {
-        if (start_iov_off < (curr_off + iov->iov_len)) {
-            size_t chunk_len =
-                MIN((curr_off + iov->iov_len) - start_iov_off, bytes_to_copy);
-
-            cpu_physical_memory_write(target_addr + copied,
-                                      iov->iov_base + start_iov_off - curr_off,
-                                      chunk_len);
-
-            copied += chunk_len;
-            start_iov_off += chunk_len;
-            curr_off = start_iov_off;
-            bytes_to_copy -= chunk_len;
-        } else {
-            curr_off += iov->iov_len;
-        }
-        iov++;
-    }
-}
-
-static bool
-vmxnet3_indicate_packet(VMXNET3State *s)
-{
-    struct Vmxnet3_RxDesc rxd;
-    bool is_head = true;
-    uint32_t rxd_idx;
-    uint32_t rx_ridx = 0;
-
-    struct Vmxnet3_RxCompDesc rxcd;
-    uint32_t new_rxcd_gen = VMXNET3_INIT_GEN;
-    hwaddr new_rxcd_pa = 0;
-    hwaddr ready_rxcd_pa = 0;
-    struct iovec *data = vmxnet_rx_pkt_get_iovec(s->rx_pkt);
-    size_t bytes_copied = 0;
-    size_t bytes_left = vmxnet_rx_pkt_get_total_len(s->rx_pkt);
-    uint16_t num_frags = 0;
-    size_t chunk_size;
-
-    vmxnet_rx_pkt_dump(s->rx_pkt);
-
-    while (bytes_left > 0) {
-
-        /* cannot add more frags to packet */
-        if (num_frags == s->max_rx_frags) {
-            break;
-        }
-
-        new_rxcd_pa = vmxnet3_pop_rxc_descr(s, RXQ_IDX, &new_rxcd_gen);
-        if (!new_rxcd_pa) {
-            break;
-        }
-
-        if (!vmxnet3_get_next_rx_descr(s, is_head, &rxd, &rxd_idx, &rx_ridx)) {
-            break;
-        }
-
-        chunk_size = MIN(bytes_left, rxd.len);
-        vmxnet3_physical_memory_writev(data, bytes_copied,
-                                       le64_to_cpu(rxd.addr), chunk_size);
-        bytes_copied += chunk_size;
-        bytes_left -= chunk_size;
-
-        vmxnet3_dump_rx_descr(&rxd);
-
-        if (0 != ready_rxcd_pa) {
-            cpu_physical_memory_write(ready_rxcd_pa, &rxcd, sizeof(rxcd));
-        }
-
-        memset(&rxcd, 0, sizeof(struct Vmxnet3_RxCompDesc));
-        rxcd.rxdIdx = rxd_idx;
-        rxcd.len = chunk_size;
-        rxcd.sop = is_head;
-        rxcd.gen = new_rxcd_gen;
-        rxcd.rqID = RXQ_IDX + rx_ridx * s->rxq_num;
-
-        if (0 == bytes_left) {
-            vmxnet3_rx_update_descr(s->rx_pkt, &rxcd);
-        }
-
-        VMW_RIPRN("RX Completion descriptor: rxRing: %lu rxIdx %lu len %lu "
-                  "sop %d csum_correct %lu",
-                  (unsigned long) rx_ridx,
-                  (unsigned long) rxcd.rxdIdx,
-                  (unsigned long) rxcd.len,
-                  (int) rxcd.sop,
-                  (unsigned long) rxcd.tuc);
-
-        is_head = false;
-        ready_rxcd_pa = new_rxcd_pa;
-        new_rxcd_pa = 0;
-    }
-
-    if (0 != ready_rxcd_pa) {
-        rxcd.eop = 1;
-        rxcd.err = (0 != bytes_left);
-        cpu_physical_memory_write(ready_rxcd_pa, &rxcd, sizeof(rxcd));
-
-        /* Flush RX descriptor changes */
-        smp_wmb();
-    }
-
-    if (0 != new_rxcd_pa) {
-        vmxnet3_revert_rxc_descr(s, RXQ_IDX);
-    }
-
-    vmxnet3_trigger_interrupt(s, s->rxq_descr[RXQ_IDX].intr_idx);
-
-    if (bytes_left == 0) {
-        vmxnet3_on_rx_done_update_stats(s, RXQ_IDX, VMXNET3_PKT_STATUS_OK);
-        return true;
-    } else if (num_frags == s->max_rx_frags) {
-        vmxnet3_on_rx_done_update_stats(s, RXQ_IDX, VMXNET3_PKT_STATUS_ERROR);
-        return false;
-    } else {
-        vmxnet3_on_rx_done_update_stats(s, RXQ_IDX,
-                                        VMXNET3_PKT_STATUS_OUT_OF_BUF);
-        return false;
-    }
-}
-
-static void
-vmxnet3_io_bar0_write(void *opaque, hwaddr addr,
-                      uint64_t val, unsigned size)
-{
-    VMXNET3State *s = opaque;
-
-    if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_TXPROD,
-                        VMXNET3_DEVICE_MAX_TX_QUEUES, VMXNET3_REG_ALIGN)) {
-        int tx_queue_idx =
-            VMW_MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_TXPROD,
-                                     VMXNET3_REG_ALIGN);
-        assert(tx_queue_idx <= s->txq_num);
-        vmxnet3_process_tx_queue(s, tx_queue_idx);
-        return;
-    }
-
-    if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_IMR,
-                        VMXNET3_MAX_INTRS, VMXNET3_REG_ALIGN)) {
-        int l = VMW_MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_IMR,
-                                         VMXNET3_REG_ALIGN);
-
-        VMW_CBPRN("Interrupt mask for line %d written: 0x%" PRIx64, l, val);
-
-        vmxnet3_on_interrupt_mask_changed(s, l, val);
-        return;
-    }
-
-    if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_RXPROD,
-                        VMXNET3_DEVICE_MAX_RX_QUEUES, VMXNET3_REG_ALIGN) ||
-       VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_RXPROD2,
-                        VMXNET3_DEVICE_MAX_RX_QUEUES, VMXNET3_REG_ALIGN)) {
-        return;
-    }
-
-    VMW_WRPRN("BAR0 unknown write [%" PRIx64 "] = %" PRIx64 ", size %d",
-              (uint64_t) addr, val, size);
-}
-
-static uint64_t
-vmxnet3_io_bar0_read(void *opaque, hwaddr addr, unsigned size)
-{
-    if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_IMR,
-                        VMXNET3_MAX_INTRS, VMXNET3_REG_ALIGN)) {
-        assert(false);
-    }
-
-    VMW_CBPRN("BAR0 unknown read [%" PRIx64 "], size %d", addr, size);
-    return 0;
-}
-
-static void vmxnet3_reset_interrupt_states(VMXNET3State *s)
-{
-    int i;
-    for (i = 0; i < ARRAY_SIZE(s->interrupt_states); i++) {
-        s->interrupt_states[i].is_asserted = false;
-        s->interrupt_states[i].is_pending = false;
-        s->interrupt_states[i].is_masked = true;
-    }
-}
-
-static void vmxnet3_reset_mac(VMXNET3State *s)
-{
-    memcpy(&s->conf.macaddr.a, &s->perm_mac.a, sizeof(s->perm_mac.a));
-    VMW_CFPRN("MAC address set to: " VMXNET_MF, VMXNET_MA(s->conf.macaddr.a));
-}
-
-static void vmxnet3_deactivate_device(VMXNET3State *s)
-{
-    VMW_CBPRN("Deactivating vmxnet3...");
-    s->device_active = false;
-}
-
-static void vmxnet3_reset(VMXNET3State *s)
-{
-    VMW_CBPRN("Resetting vmxnet3...");
-
-    vmxnet3_deactivate_device(s);
-    vmxnet3_reset_interrupt_states(s);
-    vmxnet_tx_pkt_reset(s->tx_pkt);
-    s->drv_shmem = 0;
-    s->tx_sop = true;
-    s->skip_current_tx_pkt = false;
-}
-
-static void vmxnet3_update_rx_mode(VMXNET3State *s)
-{
-    s->rx_mode = VMXNET3_READ_DRV_SHARED32(s->drv_shmem,
-                                           devRead.rxFilterConf.rxMode);
-    VMW_CFPRN("RX mode: 0x%08X", s->rx_mode);
-}
-
-static void vmxnet3_update_vlan_filters(VMXNET3State *s)
-{
-    int i;
-
-    /* Copy configuration from shared memory */
-    VMXNET3_READ_DRV_SHARED(s->drv_shmem,
-                            devRead.rxFilterConf.vfTable,
-                            s->vlan_table,
-                            sizeof(s->vlan_table));
-
-    /* Invert byte order when needed */
-    for (i = 0; i < ARRAY_SIZE(s->vlan_table); i++) {
-        s->vlan_table[i] = le32_to_cpu(s->vlan_table[i]);
-    }
-
-    /* Dump configuration for debugging purposes */
-    VMW_CFPRN("Configured VLANs:");
-    for (i = 0; i < sizeof(s->vlan_table) * 8; i++) {
-        if (VMXNET3_VFTABLE_ENTRY_IS_SET(s->vlan_table, i)) {
-            VMW_CFPRN("\tVLAN %d is present", i);
-        }
-    }
-}
-
-static void vmxnet3_update_mcast_filters(VMXNET3State *s)
-{
-    uint16_t list_bytes =
-        VMXNET3_READ_DRV_SHARED16(s->drv_shmem,
-                                  devRead.rxFilterConf.mfTableLen);
-
-    s->mcast_list_len = list_bytes / sizeof(s->mcast_list[0]);
-
-    s->mcast_list = g_realloc(s->mcast_list, list_bytes);
-    if (NULL == s->mcast_list) {
-        if (0 == s->mcast_list_len) {
-            VMW_CFPRN("Current multicast list is empty");
-        } else {
-            VMW_ERPRN("Failed to allocate multicast list of %d elements",
-                      s->mcast_list_len);
-        }
-        s->mcast_list_len = 0;
-    } else {
-        int i;
-        hwaddr mcast_list_pa =
-            VMXNET3_READ_DRV_SHARED64(s->drv_shmem,
-                                      devRead.rxFilterConf.mfTablePA);
-
-        cpu_physical_memory_read(mcast_list_pa, s->mcast_list, list_bytes);
-        VMW_CFPRN("Current multicast list len is %d:", s->mcast_list_len);
-        for (i = 0; i < s->mcast_list_len; i++) {
-            VMW_CFPRN("\t" VMXNET_MF, VMXNET_MA(s->mcast_list[i].a));
-        }
-    }
-}
-
-static void vmxnet3_setup_rx_filtering(VMXNET3State *s)
-{
-    vmxnet3_update_rx_mode(s);
-    vmxnet3_update_vlan_filters(s);
-    vmxnet3_update_mcast_filters(s);
-}
-
-static uint32_t vmxnet3_get_interrupt_config(VMXNET3State *s)
-{
-    uint32_t interrupt_mode = VMXNET3_IT_AUTO | (VMXNET3_IMM_AUTO << 2);
-    VMW_CFPRN("Interrupt config is 0x%X", interrupt_mode);
-    return interrupt_mode;
-}
-
-static void vmxnet3_fill_stats(VMXNET3State *s)
-{
-    int i;
-    for (i = 0; i < s->txq_num; i++) {
-        cpu_physical_memory_write(s->txq_descr[i].tx_stats_pa,
-                                  &s->txq_descr[i].txq_stats,
-                                  sizeof(s->txq_descr[i].txq_stats));
-    }
-
-    for (i = 0; i < s->rxq_num; i++) {
-        cpu_physical_memory_write(s->rxq_descr[i].rx_stats_pa,
-                                  &s->rxq_descr[i].rxq_stats,
-                                  sizeof(s->rxq_descr[i].rxq_stats));
-    }
-}
-
-static void vmxnet3_adjust_by_guest_type(VMXNET3State *s)
-{
-    struct Vmxnet3_GOSInfo gos;
-
-    VMXNET3_READ_DRV_SHARED(s->drv_shmem, devRead.misc.driverInfo.gos,
-                            &gos, sizeof(gos));
-    s->rx_packets_compound =
-        (gos.gosType == VMXNET3_GOS_TYPE_WIN) ? false : true;
-
-    VMW_CFPRN("Guest type specifics: RXCOMPOUND: %d", s->rx_packets_compound);
-}
-
-static void
-vmxnet3_dump_conf_descr(const char *name,
-                        struct Vmxnet3_VariableLenConfDesc *pm_descr)
-{
-    VMW_CFPRN("%s descriptor dump: Version %u, Length %u",
-              name, pm_descr->confVer, pm_descr->confLen);
-
-};
-
-static void vmxnet3_update_pm_state(VMXNET3State *s)
-{
-    struct Vmxnet3_VariableLenConfDesc pm_descr;
-
-    pm_descr.confLen =
-        VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.pmConfDesc.confLen);
-    pm_descr.confVer =
-        VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.pmConfDesc.confVer);
-    pm_descr.confPA =
-        VMXNET3_READ_DRV_SHARED64(s->drv_shmem, devRead.pmConfDesc.confPA);
-
-    vmxnet3_dump_conf_descr("PM State", &pm_descr);
-}
-
-static void vmxnet3_update_features(VMXNET3State *s)
-{
-    uint32_t guest_features;
-    int rxcso_supported;
-
-    guest_features = VMXNET3_READ_DRV_SHARED32(s->drv_shmem,
-                                               devRead.misc.uptFeatures);
-
-    rxcso_supported = VMXNET_FLAG_IS_SET(guest_features, UPT1_F_RXCSUM);
-    s->rx_vlan_stripping = VMXNET_FLAG_IS_SET(guest_features, UPT1_F_RXVLAN);
-    s->lro_supported = VMXNET_FLAG_IS_SET(guest_features, UPT1_F_LRO);
-
-    VMW_CFPRN("Features configuration: LRO: %d, RXCSUM: %d, VLANSTRIP: %d",
-              s->lro_supported, rxcso_supported,
-              s->rx_vlan_stripping);
-    if (s->peer_has_vhdr) {
-        tap_set_offload(qemu_get_queue(s->nic)->peer,
-                        rxcso_supported,
-                        s->lro_supported,
-                        s->lro_supported,
-                        0,
-                        0);
-    }
-}
-
-static void vmxnet3_activate_device(VMXNET3State *s)
-{
-    int i;
-    static const uint32_t VMXNET3_DEF_TX_THRESHOLD = 1;
-    hwaddr qdescr_table_pa;
-    uint64_t pa;
-    uint32_t size;
-
-    /* Verify configuration consistency */
-    if (!vmxnet3_verify_driver_magic(s->drv_shmem)) {
-        VMW_ERPRN("Device configuration received from driver is invalid");
-        return;
-    }
-
-    vmxnet3_adjust_by_guest_type(s);
-    vmxnet3_update_features(s);
-    vmxnet3_update_pm_state(s);
-    vmxnet3_setup_rx_filtering(s);
-    /* Cache fields from shared memory */
-    s->mtu = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, devRead.misc.mtu);
-    VMW_CFPRN("MTU is %u", s->mtu);
-
-    s->max_rx_frags =
-        VMXNET3_READ_DRV_SHARED16(s->drv_shmem, devRead.misc.maxNumRxSG);
-
-    VMW_CFPRN("Max RX fragments is %u", s->max_rx_frags);
-
-    s->event_int_idx =
-        VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.intrConf.eventIntrIdx);
-    VMW_CFPRN("Events interrupt line is %u", s->event_int_idx);
-
-    s->auto_int_masking =
-        VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.intrConf.autoMask);
-    VMW_CFPRN("Automatic interrupt masking is %d", (int)s->auto_int_masking);
-
-    s->txq_num =
-        VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.misc.numTxQueues);
-    s->rxq_num =
-        VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.misc.numRxQueues);
-
-    VMW_CFPRN("Number of TX/RX queues %u/%u", s->txq_num, s->rxq_num);
-    assert(s->txq_num <= VMXNET3_DEVICE_MAX_TX_QUEUES);
-
-    qdescr_table_pa =
-        VMXNET3_READ_DRV_SHARED64(s->drv_shmem, devRead.misc.queueDescPA);
-    VMW_CFPRN("TX queues descriptors table is at 0x%" PRIx64, qdescr_table_pa);
-
-    /*
-     * Worst-case scenario is a packet that holds all TX rings space so
-     * we calculate total size of all TX rings for max TX fragments number
-     */
-    s->max_tx_frags = 0;
-
-    /* TX queues */
-    for (i = 0; i < s->txq_num; i++) {
-        hwaddr qdescr_pa =
-            qdescr_table_pa + i * sizeof(struct Vmxnet3_TxQueueDesc);
-
-        /* Read interrupt number for this TX queue */
-        s->txq_descr[i].intr_idx =
-            VMXNET3_READ_TX_QUEUE_DESCR8(qdescr_pa, conf.intrIdx);
-
-        VMW_CFPRN("TX Queue %d interrupt: %d", i, s->txq_descr[i].intr_idx);
-
-        /* Read rings memory locations for TX queues */
-        pa = VMXNET3_READ_TX_QUEUE_DESCR64(qdescr_pa, conf.txRingBasePA);
-        size = VMXNET3_READ_TX_QUEUE_DESCR32(qdescr_pa, conf.txRingSize);
-
-        vmxnet3_ring_init(&s->txq_descr[i].tx_ring, pa, size,
-                          sizeof(struct Vmxnet3_TxDesc), false);
-        VMXNET3_RING_DUMP(VMW_CFPRN, "TX", i, &s->txq_descr[i].tx_ring);
-
-        s->max_tx_frags += size;
-
-        /* TXC ring */
-        pa = VMXNET3_READ_TX_QUEUE_DESCR64(qdescr_pa, conf.compRingBasePA);
-        size = VMXNET3_READ_TX_QUEUE_DESCR32(qdescr_pa, conf.compRingSize);
-        vmxnet3_ring_init(&s->txq_descr[i].comp_ring, pa, size,
-                          sizeof(struct Vmxnet3_TxCompDesc), true);
-        VMXNET3_RING_DUMP(VMW_CFPRN, "TXC", i, &s->txq_descr[i].comp_ring);
-
-        s->txq_descr[i].tx_stats_pa =
-            qdescr_pa + offsetof(struct Vmxnet3_TxQueueDesc, stats);
-
-        memset(&s->txq_descr[i].txq_stats, 0,
-               sizeof(s->txq_descr[i].txq_stats));
-
-        /* Fill device-managed parameters for queues */
-        VMXNET3_WRITE_TX_QUEUE_DESCR32(qdescr_pa,
-                                       ctrl.txThreshold,
-                                       VMXNET3_DEF_TX_THRESHOLD);
-    }
-
-    /* Preallocate TX packet wrapper */
-    VMW_CFPRN("Max TX fragments is %u", s->max_tx_frags);
-    vmxnet_tx_pkt_init(&s->tx_pkt, s->max_tx_frags, s->peer_has_vhdr);
-    vmxnet_rx_pkt_init(&s->rx_pkt, s->peer_has_vhdr);
-
-    /* Read rings memory locations for RX queues */
-    for (i = 0; i < s->rxq_num; i++) {
-        int j;
-        hwaddr qd_pa =
-            qdescr_table_pa + s->txq_num * sizeof(struct Vmxnet3_TxQueueDesc) +
-            i * sizeof(struct Vmxnet3_RxQueueDesc);
-
-        /* Read interrupt number for this RX queue */
-        s->rxq_descr[i].intr_idx =
-            VMXNET3_READ_TX_QUEUE_DESCR8(qd_pa, conf.intrIdx);
-
-        VMW_CFPRN("RX Queue %d interrupt: %d", i, s->rxq_descr[i].intr_idx);
-
-        /* Read rings memory locations */
-        for (j = 0; j < VMXNET3_RX_RINGS_PER_QUEUE; j++) {
-            /* RX rings */
-            pa = VMXNET3_READ_RX_QUEUE_DESCR64(qd_pa, conf.rxRingBasePA[j]);
-            size = VMXNET3_READ_RX_QUEUE_DESCR32(qd_pa, conf.rxRingSize[j]);
-            vmxnet3_ring_init(&s->rxq_descr[i].rx_ring[j], pa, size,
-                              sizeof(struct Vmxnet3_RxDesc), false);
-            VMW_CFPRN("RX queue %d:%d: Base: %" PRIx64 ", Size: %d",
-                      i, j, pa, size);
-        }
-
-        /* RXC ring */
-        pa = VMXNET3_READ_RX_QUEUE_DESCR64(qd_pa, conf.compRingBasePA);
-        size = VMXNET3_READ_RX_QUEUE_DESCR32(qd_pa, conf.compRingSize);
-        vmxnet3_ring_init(&s->rxq_descr[i].comp_ring, pa, size,
-                          sizeof(struct Vmxnet3_RxCompDesc), true);
-        VMW_CFPRN("RXC queue %d: Base: %" PRIx64 ", Size: %d", i, pa, size);
-
-        s->rxq_descr[i].rx_stats_pa =
-            qd_pa + offsetof(struct Vmxnet3_RxQueueDesc, stats);
-        memset(&s->rxq_descr[i].rxq_stats, 0,
-               sizeof(s->rxq_descr[i].rxq_stats));
-    }
-
-    /* Make sure everything is in place before device activation */
-    smp_wmb();
-
-    vmxnet3_reset_mac(s);
-
-    s->device_active = true;
-}
-
-static void vmxnet3_handle_command(VMXNET3State *s, uint64_t cmd)
-{
-    s->last_command = cmd;
-
-    switch (cmd) {
-    case VMXNET3_CMD_GET_PERM_MAC_HI:
-        VMW_CBPRN("Set: Get upper part of permanent MAC");
-        break;
-
-    case VMXNET3_CMD_GET_PERM_MAC_LO:
-        VMW_CBPRN("Set: Get lower part of permanent MAC");
-        break;
-
-    case VMXNET3_CMD_GET_STATS:
-        VMW_CBPRN("Set: Get device statistics");
-        vmxnet3_fill_stats(s);
-        break;
-
-    case VMXNET3_CMD_ACTIVATE_DEV:
-        VMW_CBPRN("Set: Activating vmxnet3 device");
-        vmxnet3_activate_device(s);
-        break;
-
-    case VMXNET3_CMD_UPDATE_RX_MODE:
-        VMW_CBPRN("Set: Update rx mode");
-        vmxnet3_update_rx_mode(s);
-        break;
-
-    case VMXNET3_CMD_UPDATE_VLAN_FILTERS:
-        VMW_CBPRN("Set: Update VLAN filters");
-        vmxnet3_update_vlan_filters(s);
-        break;
-
-    case VMXNET3_CMD_UPDATE_MAC_FILTERS:
-        VMW_CBPRN("Set: Update MAC filters");
-        vmxnet3_update_mcast_filters(s);
-        break;
-
-    case VMXNET3_CMD_UPDATE_FEATURE:
-        VMW_CBPRN("Set: Update features");
-        vmxnet3_update_features(s);
-        break;
-
-    case VMXNET3_CMD_UPDATE_PMCFG:
-        VMW_CBPRN("Set: Update power management config");
-        vmxnet3_update_pm_state(s);
-        break;
-
-    case VMXNET3_CMD_GET_LINK:
-        VMW_CBPRN("Set: Get link");
-        break;
-
-    case VMXNET3_CMD_RESET_DEV:
-        VMW_CBPRN("Set: Reset device");
-        vmxnet3_reset(s);
-        break;
-
-    case VMXNET3_CMD_QUIESCE_DEV:
-        VMW_CBPRN("Set: VMXNET3_CMD_QUIESCE_DEV - pause the device");
-        vmxnet3_deactivate_device(s);
-        break;
-
-    case VMXNET3_CMD_GET_CONF_INTR:
-        VMW_CBPRN("Set: VMXNET3_CMD_GET_CONF_INTR - interrupt configuration");
-        break;
-
-    default:
-        VMW_CBPRN("Received unknown command: %" PRIx64, cmd);
-        break;
-    }
-}
-
-static uint64_t vmxnet3_get_command_status(VMXNET3State *s)
-{
-    uint64_t ret;
-
-    switch (s->last_command) {
-    case VMXNET3_CMD_ACTIVATE_DEV:
-        ret = (s->device_active) ? 0 : -1;
-        VMW_CFPRN("Device active: %" PRIx64, ret);
-        break;
-
-    case VMXNET3_CMD_GET_LINK:
-        ret = s->link_status_and_speed;
-        VMW_CFPRN("Link and speed: %" PRIx64, ret);
-        break;
-
-    case VMXNET3_CMD_GET_PERM_MAC_LO:
-        ret = vmxnet3_get_mac_low(&s->perm_mac);
-        break;
-
-    case VMXNET3_CMD_GET_PERM_MAC_HI:
-        ret = vmxnet3_get_mac_high(&s->perm_mac);
-        break;
-
-    case VMXNET3_CMD_GET_CONF_INTR:
-        ret = vmxnet3_get_interrupt_config(s);
-        break;
-
-    default:
-        VMW_WRPRN("Received request for unknown command: %x", s->last_command);
-        ret = -1;
-        break;
-    }
-
-    return ret;
-}
-
-static void vmxnet3_set_events(VMXNET3State *s, uint32_t val)
-{
-    uint32_t events;
-
-    VMW_CBPRN("Setting events: 0x%x", val);
-    events = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, ecr) | val;
-    VMXNET3_WRITE_DRV_SHARED32(s->drv_shmem, ecr, events);
-}
-
-static void vmxnet3_ack_events(VMXNET3State *s, uint32_t val)
-{
-    uint32_t events;
-
-    VMW_CBPRN("Clearing events: 0x%x", val);
-    events = VMXNET3_READ_DRV_SHARED32(s->drv_shmem, ecr) & ~val;
-    VMXNET3_WRITE_DRV_SHARED32(s->drv_shmem, ecr, events);
-}
-
-static void
-vmxnet3_io_bar1_write(void *opaque,
-                      hwaddr addr,
-                      uint64_t val,
-                      unsigned size)
-{
-    VMXNET3State *s = opaque;
-
-    switch (addr) {
-    /* Vmxnet3 Revision Report Selection */
-    case VMXNET3_REG_VRRS:
-        VMW_CBPRN("Write BAR1 [VMXNET3_REG_VRRS] = %" PRIx64 ", size %d",
-                  val, size);
-        break;
-
-    /* UPT Version Report Selection */
-    case VMXNET3_REG_UVRS:
-        VMW_CBPRN("Write BAR1 [VMXNET3_REG_UVRS] = %" PRIx64 ", size %d",
-                  val, size);
-        break;
-
-    /* Driver Shared Address Low */
-    case VMXNET3_REG_DSAL:
-        VMW_CBPRN("Write BAR1 [VMXNET3_REG_DSAL] = %" PRIx64 ", size %d",
-                  val, size);
-        /*
-         * Guest driver will first write the low part of the shared
-         * memory address. We save it to temp variable and set the
-         * shared address only after we get the high part
-         */
-        if (0 == val) {
-            s->device_active = false;
-        }
-        s->temp_shared_guest_driver_memory = val;
-        s->drv_shmem = 0;
-        break;
-
-    /* Driver Shared Address High */
-    case VMXNET3_REG_DSAH:
-        VMW_CBPRN("Write BAR1 [VMXNET3_REG_DSAH] = %" PRIx64 ", size %d",
-                  val, size);
-        /*
-         * Set the shared memory between guest driver and device.
-         * We already should have low address part.
-         */
-        s->drv_shmem = s->temp_shared_guest_driver_memory | (val << 32);
-        break;
-
-    /* Command */
-    case VMXNET3_REG_CMD:
-        VMW_CBPRN("Write BAR1 [VMXNET3_REG_CMD] = %" PRIx64 ", size %d",
-                  val, size);
-        vmxnet3_handle_command(s, val);
-        break;
-
-    /* MAC Address Low */
-    case VMXNET3_REG_MACL:
-        VMW_CBPRN("Write BAR1 [VMXNET3_REG_MACL] = %" PRIx64 ", size %d",
-                  val, size);
-        s->temp_mac = val;
-        break;
-
-    /* MAC Address High */
-    case VMXNET3_REG_MACH:
-        VMW_CBPRN("Write BAR1 [VMXNET3_REG_MACH] = %" PRIx64 ", size %d",
-                  val, size);
-        vmxnet3_set_variable_mac(s, val, s->temp_mac);
-        break;
-
-    /* Interrupt Cause Register */
-    case VMXNET3_REG_ICR:
-        VMW_CBPRN("Write BAR1 [VMXNET3_REG_ICR] = %" PRIx64 ", size %d",
-                  val, size);
-        assert(false);
-        break;
-
-    /* Event Cause Register */
-    case VMXNET3_REG_ECR:
-        VMW_CBPRN("Write BAR1 [VMXNET3_REG_ECR] = %" PRIx64 ", size %d",
-                  val, size);
-        vmxnet3_ack_events(s, val);
-        break;
-
-    default:
-        VMW_CBPRN("Unknown Write to BAR1 [%" PRIx64 "] = %" PRIx64 ", size %d",
-                  addr, val, size);
-        break;
-    }
-}
-
-static uint64_t
-vmxnet3_io_bar1_read(void *opaque, hwaddr addr, unsigned size)
-{
-        VMXNET3State *s = opaque;
-        uint64_t ret = 0;
-
-        switch (addr) {
-        /* Vmxnet3 Revision Report Selection */
-        case VMXNET3_REG_VRRS:
-            VMW_CBPRN("Read BAR1 [VMXNET3_REG_VRRS], size %d", size);
-            ret = VMXNET3_DEVICE_REVISION;
-            break;
-
-        /* UPT Version Report Selection */
-        case VMXNET3_REG_UVRS:
-            VMW_CBPRN("Read BAR1 [VMXNET3_REG_UVRS], size %d", size);
-            ret = VMXNET3_DEVICE_VERSION;
-            break;
-
-        /* Command */
-        case VMXNET3_REG_CMD:
-            VMW_CBPRN("Read BAR1 [VMXNET3_REG_CMD], size %d", size);
-            ret = vmxnet3_get_command_status(s);
-            break;
-
-        /* MAC Address Low */
-        case VMXNET3_REG_MACL:
-            VMW_CBPRN("Read BAR1 [VMXNET3_REG_MACL], size %d", size);
-            ret = vmxnet3_get_mac_low(&s->conf.macaddr);
-            break;
-
-        /* MAC Address High */
-        case VMXNET3_REG_MACH:
-            VMW_CBPRN("Read BAR1 [VMXNET3_REG_MACH], size %d", size);
-            ret = vmxnet3_get_mac_high(&s->conf.macaddr);
-            break;
-
-        /*
-         * Interrupt Cause Register
-         * Used for legacy interrupts only so interrupt index always 0
-         */
-        case VMXNET3_REG_ICR:
-            VMW_CBPRN("Read BAR1 [VMXNET3_REG_ICR], size %d", size);
-            if (vmxnet3_interrupt_asserted(s, 0)) {
-                vmxnet3_clear_interrupt(s, 0);
-                ret = true;
-            } else {
-                ret = false;
-            }
-            break;
-
-        default:
-            VMW_CBPRN("Unknow read BAR1[%" PRIx64 "], %d bytes", addr, size);
-            break;
-        }
-
-        return ret;
-}
-
-static int
-vmxnet3_can_receive(NetClientState *nc)
-{
-    VMXNET3State *s = qemu_get_nic_opaque(nc);
-    return s->device_active &&
-           VMXNET_FLAG_IS_SET(s->link_status_and_speed, VMXNET3_LINK_STATUS_UP);
-}
-
-static inline bool
-vmxnet3_is_registered_vlan(VMXNET3State *s, const void *data)
-{
-    uint16_t vlan_tag = eth_get_pkt_tci(data) & VLAN_VID_MASK;
-    if (IS_SPECIAL_VLAN_ID(vlan_tag)) {
-        return true;
-    }
-
-    return VMXNET3_VFTABLE_ENTRY_IS_SET(s->vlan_table, vlan_tag);
-}
-
-static bool
-vmxnet3_is_allowed_mcast_group(VMXNET3State *s, const uint8_t *group_mac)
-{
-    int i;
-    for (i = 0; i < s->mcast_list_len; i++) {
-        if (!memcmp(group_mac, s->mcast_list[i].a, sizeof(s->mcast_list[i]))) {
-            return true;
-        }
-    }
-    return false;
-}
-
-static bool
-vmxnet3_rx_filter_may_indicate(VMXNET3State *s, const void *data,
-    size_t size)
-{
-    struct eth_header *ehdr = PKT_GET_ETH_HDR(data);
-
-    if (VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_PROMISC)) {
-        return true;
-    }
-
-    if (!vmxnet3_is_registered_vlan(s, data)) {
-        return false;
-    }
-
-    switch (vmxnet_rx_pkt_get_packet_type(s->rx_pkt)) {
-    case ETH_PKT_UCAST:
-        if (!VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_UCAST)) {
-            return false;
-        }
-        if (memcmp(s->conf.macaddr.a, ehdr->h_dest, ETH_ALEN)) {
-            return false;
-        }
-        break;
-
-    case ETH_PKT_BCAST:
-        if (!VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_BCAST)) {
-            return false;
-        }
-        break;
-
-    case ETH_PKT_MCAST:
-        if (VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_ALL_MULTI)) {
-            return true;
-        }
-        if (!VMXNET_FLAG_IS_SET(s->rx_mode, VMXNET3_RXM_MCAST)) {
-            return false;
-        }
-        if (!vmxnet3_is_allowed_mcast_group(s, ehdr->h_dest)) {
-            return false;
-        }
-        break;
-
-    default:
-        assert(false);
-    }
-
-    return true;
-}
-
-static ssize_t
-vmxnet3_receive(NetClientState *nc, const uint8_t *buf, size_t size)
-{
-    VMXNET3State *s = qemu_get_nic_opaque(nc);
-    size_t bytes_indicated;
-
-    if (!vmxnet3_can_receive(nc)) {
-        VMW_PKPRN("Cannot receive now");
-        return -1;
-    }
-
-    if (s->peer_has_vhdr) {
-        vmxnet_rx_pkt_set_vhdr(s->rx_pkt, (struct virtio_net_hdr *)buf);
-        buf += sizeof(struct virtio_net_hdr);
-        size -= sizeof(struct virtio_net_hdr);
-    }
-
-    vmxnet_rx_pkt_set_packet_type(s->rx_pkt,
-        get_eth_packet_type(PKT_GET_ETH_HDR(buf)));
-
-    if (vmxnet3_rx_filter_may_indicate(s, buf, size)) {
-        vmxnet_rx_pkt_attach_data(s->rx_pkt, buf, size, s->rx_vlan_stripping);
-        bytes_indicated = vmxnet3_indicate_packet(s) ? size : -1;
-        if (bytes_indicated < size) {
-            VMW_PKPRN("RX: %lu of %lu bytes indicated", bytes_indicated, size);
-        }
-    } else {
-        VMW_PKPRN("Packet dropped by RX filter");
-        bytes_indicated = size;
-    }
-
-    assert(size > 0);
-    assert(bytes_indicated != 0);
-    return bytes_indicated;
-}
-
-static void vmxnet3_cleanup(NetClientState *nc)
-{
-    VMXNET3State *s = qemu_get_nic_opaque(nc);
-    s->nic = NULL;
-}
-
-static void vmxnet3_set_link_status(NetClientState *nc)
-{
-    VMXNET3State *s = qemu_get_nic_opaque(nc);
-
-    if (nc->link_down) {
-        s->link_status_and_speed &= ~VMXNET3_LINK_STATUS_UP;
-    } else {
-        s->link_status_and_speed |= VMXNET3_LINK_STATUS_UP;
-    }
-
-    vmxnet3_set_events(s, VMXNET3_ECR_LINK);
-    vmxnet3_trigger_interrupt(s, s->event_int_idx);
-}
-
-static NetClientInfo net_vmxnet3_info = {
-        .type = NET_CLIENT_OPTIONS_KIND_NIC,
-        .size = sizeof(NICState),
-        .can_receive = vmxnet3_can_receive,
-        .receive = vmxnet3_receive,
-        .cleanup = vmxnet3_cleanup,
-        .link_status_changed = vmxnet3_set_link_status,
-};
-
-static bool vmxnet3_peer_has_vnet_hdr(VMXNET3State *s)
-{
-    NetClientState *peer = qemu_get_queue(s->nic)->peer;
-
-    if ((NULL != peer)                              &&
-        (peer->info->type == NET_CLIENT_OPTIONS_KIND_TAP)   &&
-        tap_has_vnet_hdr(peer)) {
-        return true;
-    }
-
-    VMW_WRPRN("Peer has no virtio extension. Task offloads will be emulated.");
-    return false;
-}
-
-static void vmxnet3_net_uninit(VMXNET3State *s)
-{
-    g_free(s->mcast_list);
-    vmxnet_tx_pkt_reset(s->tx_pkt);
-    vmxnet_tx_pkt_uninit(s->tx_pkt);
-    vmxnet_rx_pkt_uninit(s->rx_pkt);
-    qemu_del_net_client(qemu_get_queue(s->nic));
-}
-
-static void vmxnet3_net_init(VMXNET3State *s)
-{
-    DeviceState *d = DEVICE(s);
-
-    VMW_CBPRN("vmxnet3_net_init called...");
-
-    qemu_macaddr_default_if_unset(&s->conf.macaddr);
-
-    /* Windows guest will query the address that was set on init */
-    memcpy(&s->perm_mac.a, &s->conf.macaddr.a, sizeof(s->perm_mac.a));
-
-    s->mcast_list = NULL;
-    s->mcast_list_len = 0;
-
-    s->link_status_and_speed = VMXNET3_LINK_SPEED | VMXNET3_LINK_STATUS_UP;
-
-    VMW_CFPRN("Permanent MAC: " MAC_FMT, MAC_ARG(s->perm_mac.a));
-
-    s->nic = qemu_new_nic(&net_vmxnet3_info, &s->conf,
-                          object_get_typename(OBJECT(s)),
-                          d->id, s);
-
-    s->peer_has_vhdr = vmxnet3_peer_has_vnet_hdr(s);
-    s->tx_sop = true;
-    s->skip_current_tx_pkt = false;
-    s->tx_pkt = NULL;
-    s->rx_pkt = NULL;
-    s->rx_vlan_stripping = false;
-    s->lro_supported = false;
-
-    if (s->peer_has_vhdr) {
-        tap_set_vnet_hdr_len(qemu_get_queue(s->nic)->peer,
-            sizeof(struct virtio_net_hdr));
-
-        tap_using_vnet_hdr(qemu_get_queue(s->nic)->peer, 1);
-    }
-
-    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
-}
-
-static void
-vmxnet3_unuse_msix_vectors(VMXNET3State *s, int num_vectors)
-{
-    PCIDevice *d = PCI_DEVICE(s);
-    int i;
-    for (i = 0; i < num_vectors; i++) {
-        msix_vector_unuse(d, i);
-    }
-}
-
-static bool
-vmxnet3_use_msix_vectors(VMXNET3State *s, int num_vectors)
-{
-    PCIDevice *d = PCI_DEVICE(s);
-    int i;
-    for (i = 0; i < num_vectors; i++) {
-        int res = msix_vector_use(d, i);
-        if (0 > res) {
-            VMW_WRPRN("Failed to use MSI-X vector %d, error %d", i, res);
-            vmxnet3_unuse_msix_vectors(s, i);
-            return false;
-        }
-    }
-    return true;
-}
-
-static bool
-vmxnet3_init_msix(VMXNET3State *s)
-{
-    PCIDevice *d = PCI_DEVICE(s);
-    int res = msix_init(d, VMXNET3_MAX_INTRS,
-                        &s->msix_bar,
-                        VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_TABLE,
-                        &s->msix_bar,
-                        VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_PBA,
-                        0);
-
-    if (0 > res) {
-        VMW_WRPRN("Failed to initialize MSI-X, error %d", res);
-        s->msix_used = false;
-    } else {
-        if (!vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS)) {
-            VMW_WRPRN("Failed to use MSI-X vectors, error %d", res);
-            msix_uninit(d, &s->msix_bar, &s->msix_bar);
-            s->msix_used = false;
-        } else {
-            s->msix_used = true;
-        }
-    }
-    return s->msix_used;
-}
-
-static void
-vmxnet3_cleanup_msix(VMXNET3State *s)
-{
-    PCIDevice *d = PCI_DEVICE(s);
-
-    if (s->msix_used) {
-        msix_vector_unuse(d, VMXNET3_MAX_INTRS);
-        msix_uninit(d, &s->msix_bar, &s->msix_bar);
-    }
-}
-
-#define VMXNET3_MSI_NUM_VECTORS   (1)
-#define VMXNET3_MSI_OFFSET        (0x50)
-#define VMXNET3_USE_64BIT         (true)
-#define VMXNET3_PER_VECTOR_MASK   (false)
-
-static bool
-vmxnet3_init_msi(VMXNET3State *s)
-{
-    PCIDevice *d = PCI_DEVICE(s);
-    int res;
-
-    res = msi_init(d, VMXNET3_MSI_OFFSET, VMXNET3_MSI_NUM_VECTORS,
-                   VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK);
-    if (0 > res) {
-        VMW_WRPRN("Failed to initialize MSI, error %d", res);
-        s->msi_used = false;
-    } else {
-        s->msi_used = true;
-    }
-
-    return s->msi_used;
-}
-
-static void
-vmxnet3_cleanup_msi(VMXNET3State *s)
-{
-    PCIDevice *d = PCI_DEVICE(s);
-
-    if (s->msi_used) {
-        msi_uninit(d);
-    }
-}
-
-static void
-vmxnet3_msix_save(QEMUFile *f, void *opaque)
-{
-    PCIDevice *d = PCI_DEVICE(opaque);
-    msix_save(d, f);
-}
-
-static int
-vmxnet3_msix_load(QEMUFile *f, void *opaque, int version_id)
-{
-    PCIDevice *d = PCI_DEVICE(opaque);
-    msix_load(d, f);
-    return 0;
-}
-
-static const MemoryRegionOps b0_ops = {
-    .read = vmxnet3_io_bar0_read,
-    .write = vmxnet3_io_bar0_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .impl = {
-            .min_access_size = 4,
-            .max_access_size = 4,
-    },
-};
-
-static const MemoryRegionOps b1_ops = {
-    .read = vmxnet3_io_bar1_read,
-    .write = vmxnet3_io_bar1_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .impl = {
-            .min_access_size = 4,
-            .max_access_size = 4,
-    },
-};
-
-static int vmxnet3_pci_init(PCIDevice *pci_dev)
-{
-    DeviceState *dev = DEVICE(pci_dev);
-    VMXNET3State *s = VMXNET3(pci_dev);
-
-    VMW_CBPRN("Starting init...");
-
-    memory_region_init_io(&s->bar0, &b0_ops, s,
-                          "vmxnet3-b0", VMXNET3_PT_REG_SIZE);
-    pci_register_bar(pci_dev, VMXNET3_BAR0_IDX,
-                     PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0);
-
-    memory_region_init_io(&s->bar1, &b1_ops, s,
-                          "vmxnet3-b1", VMXNET3_VD_REG_SIZE);
-    pci_register_bar(pci_dev, VMXNET3_BAR1_IDX,
-                     PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar1);
-
-    memory_region_init(&s->msix_bar, "vmxnet3-msix-bar",
-                       VMXNET3_MSIX_BAR_SIZE);
-    pci_register_bar(pci_dev, VMXNET3_MSIX_BAR_IDX,
-                     PCI_BASE_ADDRESS_SPACE_MEMORY, &s->msix_bar);
-
-    vmxnet3_reset_interrupt_states(s);
-
-    /* Interrupt pin A */
-    pci_dev->config[PCI_INTERRUPT_PIN] = 0x01;
-
-    if (!vmxnet3_init_msix(s)) {
-        VMW_WRPRN("Failed to initialize MSI-X, configuration is inconsistent.");
-    }
-
-    if (!vmxnet3_init_msi(s)) {
-        VMW_WRPRN("Failed to initialize MSI, configuration is inconsistent.");
-    }
-
-    vmxnet3_net_init(s);
-
-    register_savevm(dev, "vmxnet3-msix", -1, 1,
-                    vmxnet3_msix_save, vmxnet3_msix_load, s);
-
-    add_boot_device_path(s->conf.bootindex, dev, "/ethernet-phy@0");
-
-    return 0;
-}
-
-
-static void vmxnet3_pci_uninit(PCIDevice *pci_dev)
-{
-    DeviceState *dev = DEVICE(pci_dev);
-    VMXNET3State *s = VMXNET3(pci_dev);
-
-    VMW_CBPRN("Starting uninit...");
-
-    unregister_savevm(dev, "vmxnet3-msix", s);
-
-    vmxnet3_net_uninit(s);
-
-    vmxnet3_cleanup_msix(s);
-
-    vmxnet3_cleanup_msi(s);
-
-    memory_region_destroy(&s->bar0);
-    memory_region_destroy(&s->bar1);
-    memory_region_destroy(&s->msix_bar);
-}
-
-static void vmxnet3_qdev_reset(DeviceState *dev)
-{
-    PCIDevice *d = PCI_DEVICE(dev);
-    VMXNET3State *s = VMXNET3(d);
-
-    VMW_CBPRN("Starting QDEV reset...");
-    vmxnet3_reset(s);
-}
-
-static bool vmxnet3_mc_list_needed(void *opaque)
-{
-    return true;
-}
-
-static int vmxnet3_mcast_list_pre_load(void *opaque)
-{
-    VMXNET3State *s = opaque;
-
-    s->mcast_list = g_malloc(s->mcast_list_buff_size);
-
-    return 0;
-}
-
-
-static void vmxnet3_pre_save(void *opaque)
-{
-    VMXNET3State *s = opaque;
-
-    s->mcast_list_buff_size = s->mcast_list_len * sizeof(MACAddr);
-}
-
-static const VMStateDescription vmxstate_vmxnet3_mcast_list = {
-    .name = "vmxnet3/mcast_list",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .pre_load = vmxnet3_mcast_list_pre_load,
-    .fields = (VMStateField[]) {
-        VMSTATE_VBUFFER_UINT32(mcast_list, VMXNET3State, 0, NULL, 0,
-            mcast_list_buff_size),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void vmxnet3_get_ring_from_file(QEMUFile *f, Vmxnet3Ring *r)
-{
-    r->pa = qemu_get_be64(f);
-    r->size = qemu_get_be32(f);
-    r->cell_size = qemu_get_be32(f);
-    r->next = qemu_get_be32(f);
-    r->gen = qemu_get_byte(f);
-}
-
-static void vmxnet3_put_ring_to_file(QEMUFile *f, Vmxnet3Ring *r)
-{
-    qemu_put_be64(f, r->pa);
-    qemu_put_be32(f, r->size);
-    qemu_put_be32(f, r->cell_size);
-    qemu_put_be32(f, r->next);
-    qemu_put_byte(f, r->gen);
-}
-
-static void vmxnet3_get_tx_stats_from_file(QEMUFile *f,
-    struct UPT1_TxStats *tx_stat)
-{
-    tx_stat->TSOPktsTxOK = qemu_get_be64(f);
-    tx_stat->TSOBytesTxOK = qemu_get_be64(f);
-    tx_stat->ucastPktsTxOK = qemu_get_be64(f);
-    tx_stat->ucastBytesTxOK = qemu_get_be64(f);
-    tx_stat->mcastPktsTxOK = qemu_get_be64(f);
-    tx_stat->mcastBytesTxOK = qemu_get_be64(f);
-    tx_stat->bcastPktsTxOK = qemu_get_be64(f);
-    tx_stat->bcastBytesTxOK = qemu_get_be64(f);
-    tx_stat->pktsTxError = qemu_get_be64(f);
-    tx_stat->pktsTxDiscard = qemu_get_be64(f);
-}
-
-static void vmxnet3_put_tx_stats_to_file(QEMUFile *f,
-    struct UPT1_TxStats *tx_stat)
-{
-    qemu_put_be64(f, tx_stat->TSOPktsTxOK);
-    qemu_put_be64(f, tx_stat->TSOBytesTxOK);
-    qemu_put_be64(f, tx_stat->ucastPktsTxOK);
-    qemu_put_be64(f, tx_stat->ucastBytesTxOK);
-    qemu_put_be64(f, tx_stat->mcastPktsTxOK);
-    qemu_put_be64(f, tx_stat->mcastBytesTxOK);
-    qemu_put_be64(f, tx_stat->bcastPktsTxOK);
-    qemu_put_be64(f, tx_stat->bcastBytesTxOK);
-    qemu_put_be64(f, tx_stat->pktsTxError);
-    qemu_put_be64(f, tx_stat->pktsTxDiscard);
-}
-
-static int vmxnet3_get_txq_descr(QEMUFile *f, void *pv, size_t size)
-{
-    Vmxnet3TxqDescr *r = pv;
-
-    vmxnet3_get_ring_from_file(f, &r->tx_ring);
-    vmxnet3_get_ring_from_file(f, &r->comp_ring);
-    r->intr_idx = qemu_get_byte(f);
-    r->tx_stats_pa = qemu_get_be64(f);
-
-    vmxnet3_get_tx_stats_from_file(f, &r->txq_stats);
-
-    return 0;
-}
-
-static void vmxnet3_put_txq_descr(QEMUFile *f, void *pv, size_t size)
-{
-    Vmxnet3TxqDescr *r = pv;
-
-    vmxnet3_put_ring_to_file(f, &r->tx_ring);
-    vmxnet3_put_ring_to_file(f, &r->comp_ring);
-    qemu_put_byte(f, r->intr_idx);
-    qemu_put_be64(f, r->tx_stats_pa);
-    vmxnet3_put_tx_stats_to_file(f, &r->txq_stats);
-}
-
-const VMStateInfo txq_descr_info = {
-    .name = "txq_descr",
-    .get = vmxnet3_get_txq_descr,
-    .put = vmxnet3_put_txq_descr
-};
-
-static void vmxnet3_get_rx_stats_from_file(QEMUFile *f,
-    struct UPT1_RxStats *rx_stat)
-{
-    rx_stat->LROPktsRxOK = qemu_get_be64(f);
-    rx_stat->LROBytesRxOK = qemu_get_be64(f);
-    rx_stat->ucastPktsRxOK = qemu_get_be64(f);
-    rx_stat->ucastBytesRxOK = qemu_get_be64(f);
-    rx_stat->mcastPktsRxOK = qemu_get_be64(f);
-    rx_stat->mcastBytesRxOK = qemu_get_be64(f);
-    rx_stat->bcastPktsRxOK = qemu_get_be64(f);
-    rx_stat->bcastBytesRxOK = qemu_get_be64(f);
-    rx_stat->pktsRxOutOfBuf = qemu_get_be64(f);
-    rx_stat->pktsRxError = qemu_get_be64(f);
-}
-
-static void vmxnet3_put_rx_stats_to_file(QEMUFile *f,
-    struct UPT1_RxStats *rx_stat)
-{
-    qemu_put_be64(f, rx_stat->LROPktsRxOK);
-    qemu_put_be64(f, rx_stat->LROBytesRxOK);
-    qemu_put_be64(f, rx_stat->ucastPktsRxOK);
-    qemu_put_be64(f, rx_stat->ucastBytesRxOK);
-    qemu_put_be64(f, rx_stat->mcastPktsRxOK);
-    qemu_put_be64(f, rx_stat->mcastBytesRxOK);
-    qemu_put_be64(f, rx_stat->bcastPktsRxOK);
-    qemu_put_be64(f, rx_stat->bcastBytesRxOK);
-    qemu_put_be64(f, rx_stat->pktsRxOutOfBuf);
-    qemu_put_be64(f, rx_stat->pktsRxError);
-}
-
-static int vmxnet3_get_rxq_descr(QEMUFile *f, void *pv, size_t size)
-{
-    Vmxnet3RxqDescr *r = pv;
-    int i;
-
-    for (i = 0; i < VMXNET3_RX_RINGS_PER_QUEUE; i++) {
-        vmxnet3_get_ring_from_file(f, &r->rx_ring[i]);
-    }
-
-    vmxnet3_get_ring_from_file(f, &r->comp_ring);
-    r->intr_idx = qemu_get_byte(f);
-    r->rx_stats_pa = qemu_get_be64(f);
-
-    vmxnet3_get_rx_stats_from_file(f, &r->rxq_stats);
-
-    return 0;
-}
-
-static void vmxnet3_put_rxq_descr(QEMUFile *f, void *pv, size_t size)
-{
-    Vmxnet3RxqDescr *r = pv;
-    int i;
-
-    for (i = 0; i < VMXNET3_RX_RINGS_PER_QUEUE; i++) {
-        vmxnet3_put_ring_to_file(f, &r->rx_ring[i]);
-    }
-
-    vmxnet3_put_ring_to_file(f, &r->comp_ring);
-    qemu_put_byte(f, r->intr_idx);
-    qemu_put_be64(f, r->rx_stats_pa);
-    vmxnet3_put_rx_stats_to_file(f, &r->rxq_stats);
-}
-
-static int vmxnet3_post_load(void *opaque, int version_id)
-{
-    VMXNET3State *s = opaque;
-    PCIDevice *d = PCI_DEVICE(s);
-
-    vmxnet_tx_pkt_init(&s->tx_pkt, s->max_tx_frags, s->peer_has_vhdr);
-    vmxnet_rx_pkt_init(&s->rx_pkt, s->peer_has_vhdr);
-
-    if (s->msix_used) {
-        if  (!vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS)) {
-            VMW_WRPRN("Failed to re-use MSI-X vectors");
-            msix_uninit(d, &s->msix_bar, &s->msix_bar);
-            s->msix_used = false;
-            return -1;
-        }
-    }
-
-    return 0;
-}
-
-const VMStateInfo rxq_descr_info = {
-    .name = "rxq_descr",
-    .get = vmxnet3_get_rxq_descr,
-    .put = vmxnet3_put_rxq_descr
-};
-
-static int vmxnet3_get_int_state(QEMUFile *f, void *pv, size_t size)
-{
-    Vmxnet3IntState *r = pv;
-
-    r->is_masked = qemu_get_byte(f);
-    r->is_pending = qemu_get_byte(f);
-    r->is_asserted = qemu_get_byte(f);
-
-    return 0;
-}
-
-static void vmxnet3_put_int_state(QEMUFile *f, void *pv, size_t size)
-{
-    Vmxnet3IntState *r = pv;
-
-    qemu_put_byte(f, r->is_masked);
-    qemu_put_byte(f, r->is_pending);
-    qemu_put_byte(f, r->is_asserted);
-}
-
-const VMStateInfo int_state_info = {
-    .name = "int_state",
-    .get = vmxnet3_get_int_state,
-    .put = vmxnet3_put_int_state
-};
-
-static const VMStateDescription vmstate_vmxnet3 = {
-    .name = "vmxnet3",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .pre_save = vmxnet3_pre_save,
-    .post_load = vmxnet3_post_load,
-    .fields      = (VMStateField[]) {
-            VMSTATE_PCI_DEVICE(parent_obj, VMXNET3State),
-            VMSTATE_BOOL(rx_packets_compound, VMXNET3State),
-            VMSTATE_BOOL(rx_vlan_stripping, VMXNET3State),
-            VMSTATE_BOOL(lro_supported, VMXNET3State),
-            VMSTATE_UINT32(rx_mode, VMXNET3State),
-            VMSTATE_UINT32(mcast_list_len, VMXNET3State),
-            VMSTATE_UINT32(mcast_list_buff_size, VMXNET3State),
-            VMSTATE_UINT32_ARRAY(vlan_table, VMXNET3State, VMXNET3_VFT_SIZE),
-            VMSTATE_UINT32(mtu, VMXNET3State),
-            VMSTATE_UINT16(max_rx_frags, VMXNET3State),
-            VMSTATE_UINT32(max_tx_frags, VMXNET3State),
-            VMSTATE_UINT8(event_int_idx, VMXNET3State),
-            VMSTATE_BOOL(auto_int_masking, VMXNET3State),
-            VMSTATE_UINT8(txq_num, VMXNET3State),
-            VMSTATE_UINT8(rxq_num, VMXNET3State),
-            VMSTATE_UINT32(device_active, VMXNET3State),
-            VMSTATE_UINT32(last_command, VMXNET3State),
-            VMSTATE_UINT32(link_status_and_speed, VMXNET3State),
-            VMSTATE_UINT32(temp_mac, VMXNET3State),
-            VMSTATE_UINT64(drv_shmem, VMXNET3State),
-            VMSTATE_UINT64(temp_shared_guest_driver_memory, VMXNET3State),
-
-            VMSTATE_ARRAY(txq_descr, VMXNET3State,
-                VMXNET3_DEVICE_MAX_TX_QUEUES, 0, txq_descr_info,
-                Vmxnet3TxqDescr),
-            VMSTATE_ARRAY(rxq_descr, VMXNET3State,
-                VMXNET3_DEVICE_MAX_RX_QUEUES, 0, rxq_descr_info,
-                Vmxnet3RxqDescr),
-            VMSTATE_ARRAY(interrupt_states, VMXNET3State, VMXNET3_MAX_INTRS,
-                0, int_state_info, Vmxnet3IntState),
-
-            VMSTATE_END_OF_LIST()
-    },
-    .subsections = (VMStateSubsection[]) {
-        {
-            .vmsd = &vmxstate_vmxnet3_mcast_list,
-            .needed = vmxnet3_mc_list_needed
-        },
-        {
-            /* empty element. */
-        }
-    }
-};
-
-static void
-vmxnet3_write_config(PCIDevice *pci_dev, uint32_t addr, uint32_t val, int len)
-{
-    pci_default_write_config(pci_dev, addr, val, len);
-    msix_write_config(pci_dev, addr, val, len);
-    msi_write_config(pci_dev, addr, val, len);
-}
-
-static Property vmxnet3_properties[] = {
-    DEFINE_NIC_PROPERTIES(VMXNET3State, conf),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void vmxnet3_class_init(ObjectClass *class, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(class);
-    PCIDeviceClass *c = PCI_DEVICE_CLASS(class);
-
-    c->init = vmxnet3_pci_init;
-    c->exit = vmxnet3_pci_uninit;
-    c->vendor_id = PCI_VENDOR_ID_VMWARE;
-    c->device_id = PCI_DEVICE_ID_VMWARE_VMXNET3;
-    c->revision = PCI_DEVICE_ID_VMWARE_VMXNET3_REVISION;
-    c->class_id = PCI_CLASS_NETWORK_ETHERNET;
-    c->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE;
-    c->subsystem_id = PCI_DEVICE_ID_VMWARE_VMXNET3;
-    c->config_write = vmxnet3_write_config,
-    dc->desc = "VMWare Paravirtualized Ethernet v3";
-    dc->reset = vmxnet3_qdev_reset;
-    dc->vmsd = &vmstate_vmxnet3;
-    dc->props = vmxnet3_properties;
-}
-
-static const TypeInfo vmxnet3_info = {
-    .name          = TYPE_VMXNET3,
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(VMXNET3State),
-    .class_init    = vmxnet3_class_init,
-};
-
-static void vmxnet3_register_types(void)
-{
-    VMW_CBPRN("vmxnet3_register_types called...");
-    type_register_static(&vmxnet3_info);
-}
-
-type_init(vmxnet3_register_types)
diff --git a/hw/vmxnet3.h b/hw/vmxnet3.h
deleted file mode 100644 (file)
index 7db0c8f..0000000
+++ /dev/null
@@ -1,760 +0,0 @@
-/*
- * QEMU VMWARE VMXNET3 paravirtual NIC interface definitions
- *
- * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
- *
- * Developed by Daynix Computing LTD (http://www.daynix.com)
- *
- * Authors:
- * Dmitry Fleytman <dmitry@daynix.com>
- * Tamir Shomer <tamirs@daynix.com>
- * Yan Vugenfirer <yan@daynix.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.
- * See the COPYING file in the top-level directory.
- *
- */
-
-#ifndef _QEMU_VMXNET3_H
-#define _QEMU_VMXNET3_H
-
-#define VMXNET3_DEVICE_MAX_TX_QUEUES 8
-#define VMXNET3_DEVICE_MAX_RX_QUEUES 8   /* Keep this value as a power of 2 */
-
-/*
- * VMWARE headers we got from Linux kernel do not fully comply QEMU coding
- * standards in sense of types and defines used.
- * Since we didn't want to change VMWARE code, following set of typedefs
- * and defines needed to compile these headers with QEMU introduced.
- */
-#define u64     uint64_t
-#define u32     uint32_t
-#define u16     uint16_t
-#define u8      uint8_t
-#define __le16  uint16_t
-#define __le32  uint32_t
-#define __le64  uint64_t
-#define __packed QEMU_PACKED
-
-#if defined(HOST_WORDS_BIGENDIAN)
-#define const_cpu_to_le64(x) bswap_64(x)
-#define __BIG_ENDIAN_BITFIELD
-#else
-#define const_cpu_to_le64(x) (x)
-#endif
-
-/*
- * Following is an interface definition for
- * VMXNET3 device as provided by VMWARE
- * See original copyright from Linux kernel v3.2.8
- * header file drivers/net/vmxnet3/vmxnet3_defs.h below.
- */
-
-/*
- * Linux driver for VMware's vmxnet3 ethernet NIC.
- *
- * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; version 2 of the License and no later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
- * NON INFRINGEMENT.  See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * The full GNU General Public License is included in this distribution in
- * the file called "COPYING".
- *
- * Maintained by: Shreyas Bhatewara <pv-drivers@vmware.com>
- *
- */
-
-struct UPT1_TxStats {
-    u64            TSOPktsTxOK;  /* TSO pkts post-segmentation */
-    u64            TSOBytesTxOK;
-    u64            ucastPktsTxOK;
-    u64            ucastBytesTxOK;
-    u64            mcastPktsTxOK;
-    u64            mcastBytesTxOK;
-    u64            bcastPktsTxOK;
-    u64            bcastBytesTxOK;
-    u64            pktsTxError;
-    u64            pktsTxDiscard;
-};
-
-struct UPT1_RxStats {
-    u64            LROPktsRxOK;    /* LRO pkts */
-    u64            LROBytesRxOK;   /* bytes from LRO pkts */
-    /* the following counters are for pkts from the wire, i.e., pre-LRO */
-    u64            ucastPktsRxOK;
-    u64            ucastBytesRxOK;
-    u64            mcastPktsRxOK;
-    u64            mcastBytesRxOK;
-    u64            bcastPktsRxOK;
-    u64            bcastBytesRxOK;
-    u64            pktsRxOutOfBuf;
-    u64            pktsRxError;
-};
-
-/* interrupt moderation level */
-enum {
-    UPT1_IML_NONE        = 0, /* no interrupt moderation */
-    UPT1_IML_HIGHEST    = 7, /* least intr generated */
-    UPT1_IML_ADAPTIVE    = 8, /* adpative intr moderation */
-};
-/* values for UPT1_RSSConf.hashFunc */
-enum {
-    UPT1_RSS_HASH_TYPE_NONE      = 0x0,
-    UPT1_RSS_HASH_TYPE_IPV4      = 0x01,
-    UPT1_RSS_HASH_TYPE_TCP_IPV4  = 0x02,
-    UPT1_RSS_HASH_TYPE_IPV6      = 0x04,
-    UPT1_RSS_HASH_TYPE_TCP_IPV6  = 0x08,
-};
-
-enum {
-    UPT1_RSS_HASH_FUNC_NONE      = 0x0,
-    UPT1_RSS_HASH_FUNC_TOEPLITZ  = 0x01,
-};
-
-#define UPT1_RSS_MAX_KEY_SIZE        40
-#define UPT1_RSS_MAX_IND_TABLE_SIZE  128
-
-struct UPT1_RSSConf {
-    u16            hashType;
-    u16            hashFunc;
-    u16            hashKeySize;
-    u16            indTableSize;
-    u8            hashKey[UPT1_RSS_MAX_KEY_SIZE];
-    u8            indTable[UPT1_RSS_MAX_IND_TABLE_SIZE];
-};
-
-/* features */
-enum {
-    UPT1_F_RXCSUM        = const_cpu_to_le64(0x0001), /* rx csum verification */
-    UPT1_F_RSS        = const_cpu_to_le64(0x0002),
-    UPT1_F_RXVLAN        = const_cpu_to_le64(0x0004), /* VLAN tag stripping */
-    UPT1_F_LRO        = const_cpu_to_le64(0x0008),
-};
-
-/* all registers are 32 bit wide */
-/* BAR 1 */
-enum {
-    VMXNET3_REG_VRRS    = 0x0,    /* Vmxnet3 Revision Report Selection */
-    VMXNET3_REG_UVRS    = 0x8,    /* UPT Version Report Selection */
-    VMXNET3_REG_DSAL    = 0x10,    /* Driver Shared Address Low */
-    VMXNET3_REG_DSAH    = 0x18,    /* Driver Shared Address High */
-    VMXNET3_REG_CMD        = 0x20,    /* Command */
-    VMXNET3_REG_MACL    = 0x28,    /* MAC Address Low */
-    VMXNET3_REG_MACH    = 0x30,    /* MAC Address High */
-    VMXNET3_REG_ICR        = 0x38,    /* Interrupt Cause Register */
-    VMXNET3_REG_ECR        = 0x40    /* Event Cause Register */
-};
-
-/* BAR 0 */
-enum {
-    VMXNET3_REG_IMR        = 0x0,     /* Interrupt Mask Register */
-    VMXNET3_REG_TXPROD    = 0x600, /* Tx Producer Index */
-    VMXNET3_REG_RXPROD    = 0x800, /* Rx Producer Index for ring 1 */
-    VMXNET3_REG_RXPROD2    = 0xA00     /* Rx Producer Index for ring 2 */
-};
-
-#define VMXNET3_PT_REG_SIZE     4096    /* BAR 0 */
-#define VMXNET3_VD_REG_SIZE     4096    /* BAR 1 */
-
-#define VMXNET3_REG_ALIGN       8    /* All registers are 8-byte aligned. */
-#define VMXNET3_REG_ALIGN_MASK  0x7
-
-/* I/O Mapped access to registers */
-#define VMXNET3_IO_TYPE_PT              0
-#define VMXNET3_IO_TYPE_VD              1
-#define VMXNET3_IO_ADDR(type, reg)      (((type) << 24) | ((reg) & 0xFFFFFF))
-#define VMXNET3_IO_TYPE(addr)           ((addr) >> 24)
-#define VMXNET3_IO_REG(addr)            ((addr) & 0xFFFFFF)
-
-enum {
-    VMXNET3_CMD_FIRST_SET = 0xCAFE0000,
-    VMXNET3_CMD_ACTIVATE_DEV = VMXNET3_CMD_FIRST_SET, /* 0xCAFE0000 */
-    VMXNET3_CMD_QUIESCE_DEV,                          /* 0xCAFE0001 */
-    VMXNET3_CMD_RESET_DEV,                            /* 0xCAFE0002 */
-    VMXNET3_CMD_UPDATE_RX_MODE,                       /* 0xCAFE0003 */
-    VMXNET3_CMD_UPDATE_MAC_FILTERS,                   /* 0xCAFE0004 */
-    VMXNET3_CMD_UPDATE_VLAN_FILTERS,                  /* 0xCAFE0005 */
-    VMXNET3_CMD_UPDATE_RSSIDT,                        /* 0xCAFE0006 */
-    VMXNET3_CMD_UPDATE_IML,                           /* 0xCAFE0007 */
-    VMXNET3_CMD_UPDATE_PMCFG,                         /* 0xCAFE0008 */
-    VMXNET3_CMD_UPDATE_FEATURE,                       /* 0xCAFE0009 */
-    VMXNET3_CMD_LOAD_PLUGIN,                          /* 0xCAFE000A */
-
-    VMXNET3_CMD_FIRST_GET = 0xF00D0000,
-    VMXNET3_CMD_GET_QUEUE_STATUS = VMXNET3_CMD_FIRST_GET, /* 0xF00D0000 */
-    VMXNET3_CMD_GET_STATS,                                /* 0xF00D0001 */
-    VMXNET3_CMD_GET_LINK,                                 /* 0xF00D0002 */
-    VMXNET3_CMD_GET_PERM_MAC_LO,                          /* 0xF00D0003 */
-    VMXNET3_CMD_GET_PERM_MAC_HI,                          /* 0xF00D0004 */
-    VMXNET3_CMD_GET_DID_LO,                               /* 0xF00D0005 */
-    VMXNET3_CMD_GET_DID_HI,                               /* 0xF00D0006 */
-    VMXNET3_CMD_GET_DEV_EXTRA_INFO,                       /* 0xF00D0007 */
-    VMXNET3_CMD_GET_CONF_INTR                             /* 0xF00D0008 */
-};
-
-/*
- *    Little Endian layout of bitfields -
- *    Byte 0 :    7.....len.....0
- *    Byte 1 :    rsvd gen 13.len.8
- *    Byte 2 :     5.msscof.0 ext1  dtype
- *    Byte 3 :     13...msscof...6
- *
- *    Big Endian layout of bitfields -
- *    Byte 0:        13...msscof...6
- *    Byte 1 :     5.msscof.0 ext1  dtype
- *    Byte 2 :    rsvd gen 13.len.8
- *    Byte 3 :    7.....len.....0
- *
- *    Thus, le32_to_cpu on the dword will allow the big endian driver to read
- *    the bit fields correctly. And cpu_to_le32 will convert bitfields
- *    bit fields written by big endian driver to format required by device.
- */
-
-struct Vmxnet3_TxDesc {
-    __le64 addr;
-
-#ifdef __BIG_ENDIAN_BITFIELD
-    u32 msscof:14;  /* MSS, checksum offset, flags */
-    u32 ext1:1;
-    u32 dtype:1;    /* descriptor type */
-    u32 rsvd:1;
-    u32 gen:1;      /* generation bit */
-    u32 len:14;
-#else
-    u32 len:14;
-    u32 gen:1;      /* generation bit */
-    u32 rsvd:1;
-    u32 dtype:1;    /* descriptor type */
-    u32 ext1:1;
-    u32 msscof:14;  /* MSS, checksum offset, flags */
-#endif  /* __BIG_ENDIAN_BITFIELD */
-
-#ifdef __BIG_ENDIAN_BITFIELD
-    u32 tci:16;     /* Tag to Insert */
-    u32 ti:1;       /* VLAN Tag Insertion */
-    u32 ext2:1;
-    u32 cq:1;       /* completion request */
-    u32 eop:1;      /* End Of Packet */
-    u32 om:2;       /* offload mode */
-    u32 hlen:10;    /* header len */
-#else
-    u32 hlen:10;    /* header len */
-    u32 om:2;       /* offload mode */
-    u32 eop:1;      /* End Of Packet */
-    u32 cq:1;       /* completion request */
-    u32 ext2:1;
-    u32 ti:1;       /* VLAN Tag Insertion */
-    u32 tci:16;     /* Tag to Insert */
-#endif  /* __BIG_ENDIAN_BITFIELD */
-};
-
-/* TxDesc.OM values */
-#define VMXNET3_OM_NONE        0
-#define VMXNET3_OM_CSUM        2
-#define VMXNET3_OM_TSO        3
-
-/* fields in TxDesc we access w/o using bit fields */
-#define VMXNET3_TXD_EOP_SHIFT    12
-#define VMXNET3_TXD_CQ_SHIFT    13
-#define VMXNET3_TXD_GEN_SHIFT    14
-#define VMXNET3_TXD_EOP_DWORD_SHIFT 3
-#define VMXNET3_TXD_GEN_DWORD_SHIFT 2
-
-#define VMXNET3_TXD_CQ        (1 << VMXNET3_TXD_CQ_SHIFT)
-#define VMXNET3_TXD_EOP        (1 << VMXNET3_TXD_EOP_SHIFT)
-#define VMXNET3_TXD_GEN        (1 << VMXNET3_TXD_GEN_SHIFT)
-
-#define VMXNET3_HDR_COPY_SIZE   128
-
-
-struct Vmxnet3_TxDataDesc {
-    u8        data[VMXNET3_HDR_COPY_SIZE];
-};
-
-#define VMXNET3_TCD_GEN_SHIFT    31
-#define VMXNET3_TCD_GEN_SIZE    1
-#define VMXNET3_TCD_TXIDX_SHIFT    0
-#define VMXNET3_TCD_TXIDX_SIZE    12
-#define VMXNET3_TCD_GEN_DWORD_SHIFT    3
-
-struct Vmxnet3_TxCompDesc {
-    u32        txdIdx:12;    /* Index of the EOP TxDesc */
-    u32        ext1:20;
-
-    __le32        ext2;
-    __le32        ext3;
-
-    u32        rsvd:24;
-    u32        type:7;       /* completion type */
-    u32        gen:1;        /* generation bit */
-};
-
-struct Vmxnet3_RxDesc {
-    __le64        addr;
-
-#ifdef __BIG_ENDIAN_BITFIELD
-    u32        gen:1;        /* Generation bit */
-    u32        rsvd:15;
-    u32        dtype:1;      /* Descriptor type */
-    u32        btype:1;      /* Buffer Type */
-    u32        len:14;
-#else
-    u32        len:14;
-    u32        btype:1;      /* Buffer Type */
-    u32        dtype:1;      /* Descriptor type */
-    u32        rsvd:15;
-    u32        gen:1;        /* Generation bit */
-#endif
-    u32        ext1;
-};
-
-/* values of RXD.BTYPE */
-#define VMXNET3_RXD_BTYPE_HEAD   0    /* head only */
-#define VMXNET3_RXD_BTYPE_BODY   1    /* body only */
-
-/* fields in RxDesc we access w/o using bit fields */
-#define VMXNET3_RXD_BTYPE_SHIFT  14
-#define VMXNET3_RXD_GEN_SHIFT    31
-
-struct Vmxnet3_RxCompDesc {
-#ifdef __BIG_ENDIAN_BITFIELD
-    u32        ext2:1;
-    u32        cnc:1;        /* Checksum Not Calculated */
-    u32        rssType:4;    /* RSS hash type used */
-    u32        rqID:10;      /* rx queue/ring ID */
-    u32        sop:1;        /* Start of Packet */
-    u32        eop:1;        /* End of Packet */
-    u32        ext1:2;
-    u32        rxdIdx:12;    /* Index of the RxDesc */
-#else
-    u32        rxdIdx:12;    /* Index of the RxDesc */
-    u32        ext1:2;
-    u32        eop:1;        /* End of Packet */
-    u32        sop:1;        /* Start of Packet */
-    u32        rqID:10;      /* rx queue/ring ID */
-    u32        rssType:4;    /* RSS hash type used */
-    u32        cnc:1;        /* Checksum Not Calculated */
-    u32        ext2:1;
-#endif  /* __BIG_ENDIAN_BITFIELD */
-
-    __le32        rssHash;      /* RSS hash value */
-
-#ifdef __BIG_ENDIAN_BITFIELD
-    u32        tci:16;       /* Tag stripped */
-    u32        ts:1;         /* Tag is stripped */
-    u32        err:1;        /* Error */
-    u32        len:14;       /* data length */
-#else
-    u32        len:14;       /* data length */
-    u32        err:1;        /* Error */
-    u32        ts:1;         /* Tag is stripped */
-    u32        tci:16;       /* Tag stripped */
-#endif  /* __BIG_ENDIAN_BITFIELD */
-
-
-#ifdef __BIG_ENDIAN_BITFIELD
-    u32        gen:1;        /* generation bit */
-    u32        type:7;       /* completion type */
-    u32        fcs:1;        /* Frame CRC correct */
-    u32        frg:1;        /* IP Fragment */
-    u32        v4:1;         /* IPv4 */
-    u32        v6:1;         /* IPv6 */
-    u32        ipc:1;        /* IP Checksum Correct */
-    u32        tcp:1;        /* TCP packet */
-    u32        udp:1;        /* UDP packet */
-    u32        tuc:1;        /* TCP/UDP Checksum Correct */
-    u32        csum:16;
-#else
-    u32        csum:16;
-    u32        tuc:1;        /* TCP/UDP Checksum Correct */
-    u32        udp:1;        /* UDP packet */
-    u32        tcp:1;        /* TCP packet */
-    u32        ipc:1;        /* IP Checksum Correct */
-    u32        v6:1;         /* IPv6 */
-    u32        v4:1;         /* IPv4 */
-    u32        frg:1;        /* IP Fragment */
-    u32        fcs:1;        /* Frame CRC correct */
-    u32        type:7;       /* completion type */
-    u32        gen:1;        /* generation bit */
-#endif  /* __BIG_ENDIAN_BITFIELD */
-};
-
-/* fields in RxCompDesc we access via Vmxnet3_GenericDesc.dword[3] */
-#define VMXNET3_RCD_TUC_SHIFT    16
-#define VMXNET3_RCD_IPC_SHIFT    19
-
-/* fields in RxCompDesc we access via Vmxnet3_GenericDesc.qword[1] */
-#define VMXNET3_RCD_TYPE_SHIFT    56
-#define VMXNET3_RCD_GEN_SHIFT    63
-
-/* csum OK for TCP/UDP pkts over IP */
-#define VMXNET3_RCD_CSUM_OK (1 << VMXNET3_RCD_TUC_SHIFT | \
-                     1 << VMXNET3_RCD_IPC_SHIFT)
-#define VMXNET3_TXD_GEN_SIZE 1
-#define VMXNET3_TXD_EOP_SIZE 1
-
-/* value of RxCompDesc.rssType */
-enum {
-    VMXNET3_RCD_RSS_TYPE_NONE     = 0,
-    VMXNET3_RCD_RSS_TYPE_IPV4     = 1,
-    VMXNET3_RCD_RSS_TYPE_TCPIPV4  = 2,
-    VMXNET3_RCD_RSS_TYPE_IPV6     = 3,
-    VMXNET3_RCD_RSS_TYPE_TCPIPV6  = 4,
-};
-
-
-/* a union for accessing all cmd/completion descriptors */
-union Vmxnet3_GenericDesc {
-    __le64                qword[2];
-    __le32                dword[4];
-    __le16                word[8];
-    struct Vmxnet3_TxDesc        txd;
-    struct Vmxnet3_RxDesc        rxd;
-    struct Vmxnet3_TxCompDesc    tcd;
-    struct Vmxnet3_RxCompDesc    rcd;
-};
-
-#define VMXNET3_INIT_GEN       1
-
-/* Max size of a single tx buffer */
-#define VMXNET3_MAX_TX_BUF_SIZE  (1 << 14)
-
-/* # of tx desc needed for a tx buffer size */
-#define VMXNET3_TXD_NEEDED(size) (((size) + VMXNET3_MAX_TX_BUF_SIZE - 1) / \
-                    VMXNET3_MAX_TX_BUF_SIZE)
-
-/* max # of tx descs for a non-tso pkt */
-#define VMXNET3_MAX_TXD_PER_PKT 16
-
-/* Max size of a single rx buffer */
-#define VMXNET3_MAX_RX_BUF_SIZE  ((1 << 14) - 1)
-/* Minimum size of a type 0 buffer */
-#define VMXNET3_MIN_T0_BUF_SIZE  128
-#define VMXNET3_MAX_CSUM_OFFSET  1024
-
-/* Ring base address alignment */
-#define VMXNET3_RING_BA_ALIGN   512
-#define VMXNET3_RING_BA_MASK    (VMXNET3_RING_BA_ALIGN - 1)
-
-/* Ring size must be a multiple of 32 */
-#define VMXNET3_RING_SIZE_ALIGN 32
-#define VMXNET3_RING_SIZE_MASK  (VMXNET3_RING_SIZE_ALIGN - 1)
-
-/* Max ring size */
-#define VMXNET3_TX_RING_MAX_SIZE   4096
-#define VMXNET3_TC_RING_MAX_SIZE   4096
-#define VMXNET3_RX_RING_MAX_SIZE   4096
-#define VMXNET3_RC_RING_MAX_SIZE   8192
-
-/* a list of reasons for queue stop */
-
-enum {
- VMXNET3_ERR_NOEOP        = 0x80000000, /* cannot find the EOP desc of a pkt */
- VMXNET3_ERR_TXD_REUSE    = 0x80000001, /* reuse TxDesc before tx completion */
- VMXNET3_ERR_BIG_PKT      = 0x80000002, /* too many TxDesc for a pkt */
- VMXNET3_ERR_DESC_NOT_SPT = 0x80000003, /* descriptor type not supported */
- VMXNET3_ERR_SMALL_BUF    = 0x80000004, /* type 0 buffer too small */
- VMXNET3_ERR_STRESS       = 0x80000005, /* stress option firing in vmkernel */
- VMXNET3_ERR_SWITCH       = 0x80000006, /* mode switch failure */
- VMXNET3_ERR_TXD_INVALID  = 0x80000007, /* invalid TxDesc */
-};
-
-/* completion descriptor types */
-#define VMXNET3_CDTYPE_TXCOMP      0    /* Tx Completion Descriptor */
-#define VMXNET3_CDTYPE_RXCOMP      3    /* Rx Completion Descriptor */
-
-enum {
-    VMXNET3_GOS_BITS_UNK    = 0,   /* unknown */
-    VMXNET3_GOS_BITS_32     = 1,
-    VMXNET3_GOS_BITS_64     = 2,
-};
-
-#define VMXNET3_GOS_TYPE_UNK        0 /* unknown */
-#define VMXNET3_GOS_TYPE_LINUX      1
-#define VMXNET3_GOS_TYPE_WIN        2
-#define VMXNET3_GOS_TYPE_SOLARIS    3
-#define VMXNET3_GOS_TYPE_FREEBSD    4
-#define VMXNET3_GOS_TYPE_PXE        5
-
-struct Vmxnet3_GOSInfo {
-#ifdef __BIG_ENDIAN_BITFIELD
-    u32        gosMisc:10;    /* other info about gos */
-    u32        gosVer:16;     /* gos version */
-    u32        gosType:4;     /* which guest */
-    u32        gosBits:2;    /* 32-bit or 64-bit? */
-#else
-    u32        gosBits:2;     /* 32-bit or 64-bit? */
-    u32        gosType:4;     /* which guest */
-    u32        gosVer:16;     /* gos version */
-    u32        gosMisc:10;    /* other info about gos */
-#endif  /* __BIG_ENDIAN_BITFIELD */
-};
-
-struct Vmxnet3_DriverInfo {
-    __le32                version;
-    struct Vmxnet3_GOSInfo        gos;
-    __le32                vmxnet3RevSpt;
-    __le32                uptVerSpt;
-};
-
-
-#define VMXNET3_REV1_MAGIC  0xbabefee1
-
-/*
- * QueueDescPA must be 128 bytes aligned. It points to an array of
- * Vmxnet3_TxQueueDesc followed by an array of Vmxnet3_RxQueueDesc.
- * The number of Vmxnet3_TxQueueDesc/Vmxnet3_RxQueueDesc are specified by
- * Vmxnet3_MiscConf.numTxQueues/numRxQueues, respectively.
- */
-#define VMXNET3_QUEUE_DESC_ALIGN  128
-
-
-struct Vmxnet3_MiscConf {
-    struct Vmxnet3_DriverInfo driverInfo;
-    __le64        uptFeatures;
-    __le64        ddPA;         /* driver data PA */
-    __le64        queueDescPA;  /* queue descriptor table PA */
-    __le32        ddLen;        /* driver data len */
-    __le32        queueDescLen; /* queue desc. table len in bytes */
-    __le32        mtu;
-    __le16        maxNumRxSG;
-    u8        numTxQueues;
-    u8        numRxQueues;
-    __le32        reserved[4];
-};
-
-
-struct Vmxnet3_TxQueueConf {
-    __le64        txRingBasePA;
-    __le64        dataRingBasePA;
-    __le64        compRingBasePA;
-    __le64        ddPA;         /* driver data */
-    __le64        reserved;
-    __le32        txRingSize;   /* # of tx desc */
-    __le32        dataRingSize; /* # of data desc */
-    __le32        compRingSize; /* # of comp desc */
-    __le32        ddLen;        /* size of driver data */
-    u8        intrIdx;
-    u8        _pad[7];
-};
-
-
-struct Vmxnet3_RxQueueConf {
-    __le64        rxRingBasePA[2];
-    __le64        compRingBasePA;
-    __le64        ddPA;            /* driver data */
-    __le64        reserved;
-    __le32        rxRingSize[2];   /* # of rx desc */
-    __le32        compRingSize;    /* # of rx comp desc */
-    __le32        ddLen;           /* size of driver data */
-    u8        intrIdx;
-    u8        _pad[7];
-};
-
-
-enum vmxnet3_intr_mask_mode {
-    VMXNET3_IMM_AUTO   = 0,
-    VMXNET3_IMM_ACTIVE = 1,
-    VMXNET3_IMM_LAZY   = 2
-};
-
-enum vmxnet3_intr_type {
-    VMXNET3_IT_AUTO = 0,
-    VMXNET3_IT_INTX = 1,
-    VMXNET3_IT_MSI  = 2,
-    VMXNET3_IT_MSIX = 3
-};
-
-#define VMXNET3_MAX_TX_QUEUES  8
-#define VMXNET3_MAX_RX_QUEUES  16
-/* addition 1 for events */
-#define VMXNET3_MAX_INTRS      25
-
-/* value of intrCtrl */
-#define VMXNET3_IC_DISABLE_ALL  0x1   /* bit 0 */
-
-
-struct Vmxnet3_IntrConf {
-    bool        autoMask;
-    u8        numIntrs;      /* # of interrupts */
-    u8        eventIntrIdx;
-    u8        modLevels[VMXNET3_MAX_INTRS];    /* moderation level for
-                             * each intr */
-    __le32        intrCtrl;
-    __le32        reserved[2];
-};
-
-/* one bit per VLAN ID, the size is in the units of u32 */
-#define VMXNET3_VFT_SIZE  (4096/(sizeof(uint32_t)*8))
-
-
-struct Vmxnet3_QueueStatus {
-    bool        stopped;
-    u8        _pad[3];
-    __le32        error;
-};
-
-
-struct Vmxnet3_TxQueueCtrl {
-    __le32        txNumDeferred;
-    __le32        txThreshold;
-    __le64        reserved;
-};
-
-
-struct Vmxnet3_RxQueueCtrl {
-    bool        updateRxProd;
-    u8        _pad[7];
-    __le64        reserved;
-};
-
-enum {
-    VMXNET3_RXM_UCAST     = 0x01,  /* unicast only */
-    VMXNET3_RXM_MCAST     = 0x02,  /* multicast passing the filters */
-    VMXNET3_RXM_BCAST     = 0x04,  /* broadcast only */
-    VMXNET3_RXM_ALL_MULTI = 0x08,  /* all multicast */
-    VMXNET3_RXM_PROMISC   = 0x10  /* promiscuous */
-};
-
-struct Vmxnet3_RxFilterConf {
-    __le32        rxMode;       /* VMXNET3_RXM_xxx */
-    __le16        mfTableLen;   /* size of the multicast filter table */
-    __le16        _pad1;
-    __le64        mfTablePA;    /* PA of the multicast filters table */
-    __le32        vfTable[VMXNET3_VFT_SIZE]; /* vlan filter */
-};
-
-
-#define VMXNET3_PM_MAX_FILTERS        6
-#define VMXNET3_PM_MAX_PATTERN_SIZE   128
-#define VMXNET3_PM_MAX_MASK_SIZE      (VMXNET3_PM_MAX_PATTERN_SIZE / 8)
-
-#define VMXNET3_PM_WAKEUP_MAGIC  cpu_to_le16(0x01)  /* wake up on magic pkts */
-#define VMXNET3_PM_WAKEUP_FILTER cpu_to_le16(0x02)  /* wake up on pkts matching
-                                                     * filters */
-
-
-struct Vmxnet3_PM_PktFilter {
-    u8        maskSize;
-    u8        patternSize;
-    u8        mask[VMXNET3_PM_MAX_MASK_SIZE];
-    u8        pattern[VMXNET3_PM_MAX_PATTERN_SIZE];
-    u8        pad[6];
-};
-
-
-struct Vmxnet3_PMConf {
-    __le16        wakeUpEvents;  /* VMXNET3_PM_WAKEUP_xxx */
-    u8        numFilters;
-    u8        pad[5];
-    struct Vmxnet3_PM_PktFilter filters[VMXNET3_PM_MAX_FILTERS];
-};
-
-
-struct Vmxnet3_VariableLenConfDesc {
-    __le32        confVer;
-    __le32        confLen;
-    __le64        confPA;
-};
-
-
-struct Vmxnet3_TxQueueDesc {
-    struct Vmxnet3_TxQueueCtrl        ctrl;
-    struct Vmxnet3_TxQueueConf        conf;
-
-    /* Driver read after a GET command */
-    struct Vmxnet3_QueueStatus        status;
-    struct UPT1_TxStats            stats;
-    u8                    _pad[88]; /* 128 aligned */
-};
-
-
-struct Vmxnet3_RxQueueDesc {
-    struct Vmxnet3_RxQueueCtrl        ctrl;
-    struct Vmxnet3_RxQueueConf        conf;
-    /* Driver read after a GET commad */
-    struct Vmxnet3_QueueStatus        status;
-    struct UPT1_RxStats            stats;
-    u8                      __pad[88]; /* 128 aligned */
-};
-
-
-struct Vmxnet3_DSDevRead {
-    /* read-only region for device, read by dev in response to a SET cmd */
-    struct Vmxnet3_MiscConf            misc;
-    struct Vmxnet3_IntrConf            intrConf;
-    struct Vmxnet3_RxFilterConf        rxFilterConf;
-    struct Vmxnet3_VariableLenConfDesc    rssConfDesc;
-    struct Vmxnet3_VariableLenConfDesc    pmConfDesc;
-    struct Vmxnet3_VariableLenConfDesc    pluginConfDesc;
-};
-
-/* All structures in DriverShared are padded to multiples of 8 bytes */
-struct Vmxnet3_DriverShared {
-    __le32              magic;
-    /* make devRead start at 64bit boundaries */
-    __le32              pad;
-    struct Vmxnet3_DSDevRead    devRead;
-    __le32              ecr;
-    __le32              reserved[5];
-};
-
-
-#define VMXNET3_ECR_RQERR       (1 << 0)
-#define VMXNET3_ECR_TQERR       (1 << 1)
-#define VMXNET3_ECR_LINK        (1 << 2)
-#define VMXNET3_ECR_DIC         (1 << 3)
-#define VMXNET3_ECR_DEBUG       (1 << 4)
-
-/* flip the gen bit of a ring */
-#define VMXNET3_FLIP_RING_GEN(gen) ((gen) = (gen) ^ 0x1)
-
-/* only use this if moving the idx won't affect the gen bit */
-#define VMXNET3_INC_RING_IDX_ONLY(idx, ring_size) \
-    do {\
-        (idx)++;\
-        if (unlikely((idx) == (ring_size))) {\
-            (idx) = 0;\
-        } \
-    } while (0)
-
-#define VMXNET3_SET_VFTABLE_ENTRY(vfTable, vid) \
-    (vfTable[vid >> 5] |= (1 << (vid & 31)))
-#define VMXNET3_CLEAR_VFTABLE_ENTRY(vfTable, vid) \
-    (vfTable[vid >> 5] &= ~(1 << (vid & 31)))
-
-#define VMXNET3_VFTABLE_ENTRY_IS_SET(vfTable, vid) \
-    ((vfTable[vid >> 5] & (1 << (vid & 31))) != 0)
-
-#define VMXNET3_MAX_MTU     9000
-#define VMXNET3_MIN_MTU     60
-
-#define VMXNET3_LINK_UP         (10000 << 16 | 1)    /* 10 Gbps, up */
-#define VMXNET3_LINK_DOWN       0
-
-#undef u64
-#undef u32
-#undef u16
-#undef u8
-#undef __le16
-#undef __le32
-#undef __le64
-#undef __packed
-#undef const_cpu_to_le64
-#if defined(HOST_WORDS_BIGENDIAN)
-#undef __BIG_ENDIAN_BITFIELD
-#endif
-
-#endif
diff --git a/hw/vmxnet_debug.h b/hw/vmxnet_debug.h
deleted file mode 100644 (file)
index 96dae0f..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * QEMU VMWARE VMXNET* paravirtual NICs - debugging facilities
- *
- * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
- *
- * Developed by Daynix Computing LTD (http://www.daynix.com)
- *
- * Authors:
- * Dmitry Fleytman <dmitry@daynix.com>
- * Tamir Shomer <tamirs@daynix.com>
- * Yan Vugenfirer <yan@daynix.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- *
- */
-
-#ifndef _QEMU_VMXNET_DEBUG_H
-#define _QEMU_VMXNET_DEBUG_H
-
-#define VMXNET_DEVICE_NAME "vmxnet3"
-
-/* #define VMXNET_DEBUG_CB */
-#define VMXNET_DEBUG_WARNINGS
-#define VMXNET_DEBUG_ERRORS
-/* #define VMXNET_DEBUG_INTERRUPTS */
-/* #define VMXNET_DEBUG_CONFIG */
-/* #define VMXNET_DEBUG_RINGS */
-/* #define VMXNET_DEBUG_PACKETS */
-/* #define VMXNET_DEBUG_SHMEM_ACCESS */
-
-#ifdef VMXNET_DEBUG_SHMEM_ACCESS
-#define VMW_SHPRN(fmt, ...)                                                   \
-    do {                                                                      \
-        printf("[%s][SH][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__,       \
-            ## __VA_ARGS__);                                                  \
-    } while (0)
-#else
-#define VMW_SHPRN(fmt, ...) do {} while (0)
-#endif
-
-#ifdef VMXNET_DEBUG_CB
-#define VMW_CBPRN(fmt, ...)                                                   \
-    do {                                                                      \
-        printf("[%s][CB][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__,       \
-            ## __VA_ARGS__);                                                  \
-    } while (0)
-#else
-#define VMW_CBPRN(fmt, ...) do {} while (0)
-#endif
-
-#ifdef VMXNET_DEBUG_PACKETS
-#define VMW_PKPRN(fmt, ...)                                                   \
-    do {                                                                      \
-        printf("[%s][PK][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__,       \
-            ## __VA_ARGS__);                                                  \
-    } while (0)
-#else
-#define VMW_PKPRN(fmt, ...) do {} while (0)
-#endif
-
-#ifdef VMXNET_DEBUG_WARNINGS
-#define VMW_WRPRN(fmt, ...)                                                   \
-    do {                                                                      \
-        printf("[%s][WR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__,       \
-            ## __VA_ARGS__);                                                  \
-    } while (0)
-#else
-#define VMW_WRPRN(fmt, ...) do {} while (0)
-#endif
-
-#ifdef VMXNET_DEBUG_ERRORS
-#define VMW_ERPRN(fmt, ...)                                                   \
-    do {                                                                      \
-        printf("[%s][ER][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__,       \
-            ## __VA_ARGS__);                                                  \
-    } while (0)
-#else
-#define VMW_ERPRN(fmt, ...) do {} while (0)
-#endif
-
-#ifdef VMXNET_DEBUG_INTERRUPTS
-#define VMW_IRPRN(fmt, ...)                                                   \
-    do {                                                                      \
-        printf("[%s][IR][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__,       \
-            ## __VA_ARGS__);                                                  \
-    } while (0)
-#else
-#define VMW_IRPRN(fmt, ...) do {} while (0)
-#endif
-
-#ifdef VMXNET_DEBUG_CONFIG
-#define VMW_CFPRN(fmt, ...)                                                   \
-    do {                                                                      \
-        printf("[%s][CF][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__,       \
-            ## __VA_ARGS__);                                                  \
-    } while (0)
-#else
-#define VMW_CFPRN(fmt, ...) do {} while (0)
-#endif
-
-#ifdef VMXNET_DEBUG_RINGS
-#define VMW_RIPRN(fmt, ...)                                                   \
-    do {                                                                      \
-        printf("[%s][RI][%s]: " fmt "\n", VMXNET_DEVICE_NAME, __func__,       \
-            ## __VA_ARGS__);                                                  \
-    } while (0)
-#else
-#define VMW_RIPRN(fmt, ...) do {} while (0)
-#endif
-
-#define VMXNET_MF       "%02X:%02X:%02X:%02X:%02X:%02X"
-#define VMXNET_MA(a)    (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
-
-#endif /* _QEMU_VMXNET3_DEBUG_H  */
diff --git a/hw/vmxnet_rx_pkt.c b/hw/vmxnet_rx_pkt.c
deleted file mode 100644 (file)
index a40e346..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * QEMU VMWARE VMXNET* paravirtual NICs - RX packets abstractions
- *
- * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
- *
- * Developed by Daynix Computing LTD (http://www.daynix.com)
- *
- * Authors:
- * Dmitry Fleytman <dmitry@daynix.com>
- * Tamir Shomer <tamirs@daynix.com>
- * Yan Vugenfirer <yan@daynix.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- *
- */
-
-#include "vmxnet_rx_pkt.h"
-#include "net/eth.h"
-#include "qemu-common.h"
-#include "qemu/iov.h"
-#include "net/checksum.h"
-#include "net/tap.h"
-
-/*
- * RX packet may contain up to 2 fragments - rebuilt eth header
- * in case of VLAN tag stripping
- * and payload received from QEMU - in any case
- */
-#define VMXNET_MAX_RX_PACKET_FRAGMENTS (2)
-
-struct VmxnetRxPkt {
-    struct virtio_net_hdr virt_hdr;
-    uint8_t ehdr_buf[ETH_MAX_L2_HDR_LEN];
-    struct iovec vec[VMXNET_MAX_RX_PACKET_FRAGMENTS];
-    uint16_t vec_len;
-    uint32_t tot_len;
-    uint16_t tci;
-    bool vlan_stripped;
-    bool has_virt_hdr;
-    eth_pkt_types_e packet_type;
-
-    /* Analysis results */
-    bool isip4;
-    bool isip6;
-    bool isudp;
-    bool istcp;
-};
-
-void vmxnet_rx_pkt_init(struct VmxnetRxPkt **pkt, bool has_virt_hdr)
-{
-    struct VmxnetRxPkt *p = g_malloc0(sizeof *p);
-    p->has_virt_hdr = has_virt_hdr;
-    *pkt = p;
-}
-
-void vmxnet_rx_pkt_uninit(struct VmxnetRxPkt *pkt)
-{
-    g_free(pkt);
-}
-
-struct virtio_net_hdr *vmxnet_rx_pkt_get_vhdr(struct VmxnetRxPkt *pkt)
-{
-    assert(pkt);
-    return &pkt->virt_hdr;
-}
-
-void vmxnet_rx_pkt_attach_data(struct VmxnetRxPkt *pkt, const void *data,
-                               size_t len, bool strip_vlan)
-{
-    uint16_t tci = 0;
-    uint16_t ploff;
-    assert(pkt);
-    pkt->vlan_stripped = false;
-
-    if (strip_vlan) {
-        pkt->vlan_stripped = eth_strip_vlan(data, pkt->ehdr_buf, &ploff, &tci);
-    }
-
-    if (pkt->vlan_stripped) {
-        pkt->vec[0].iov_base = pkt->ehdr_buf;
-        pkt->vec[0].iov_len = ploff - sizeof(struct vlan_header);
-        pkt->vec[1].iov_base = (uint8_t *) data + ploff;
-        pkt->vec[1].iov_len = len - ploff;
-        pkt->vec_len = 2;
-        pkt->tot_len = len - ploff + sizeof(struct eth_header);
-    } else {
-        pkt->vec[0].iov_base = (void *)data;
-        pkt->vec[0].iov_len = len;
-        pkt->vec_len = 1;
-        pkt->tot_len = len;
-    }
-
-    pkt->tci = tci;
-
-    eth_get_protocols(data, len, &pkt->isip4, &pkt->isip6,
-        &pkt->isudp, &pkt->istcp);
-}
-
-void vmxnet_rx_pkt_dump(struct VmxnetRxPkt *pkt)
-{
-#ifdef VMXNET_RX_PKT_DEBUG
-    VmxnetRxPkt *pkt = (VmxnetRxPkt *)pkt;
-    assert(pkt);
-
-    printf("RX PKT: tot_len: %d, vlan_stripped: %d, vlan_tag: %d\n",
-              pkt->tot_len, pkt->vlan_stripped, pkt->tci);
-#endif
-}
-
-void vmxnet_rx_pkt_set_packet_type(struct VmxnetRxPkt *pkt,
-    eth_pkt_types_e packet_type)
-{
-    assert(pkt);
-
-    pkt->packet_type = packet_type;
-
-}
-
-eth_pkt_types_e vmxnet_rx_pkt_get_packet_type(struct VmxnetRxPkt *pkt)
-{
-    assert(pkt);
-
-    return pkt->packet_type;
-}
-
-size_t vmxnet_rx_pkt_get_total_len(struct VmxnetRxPkt *pkt)
-{
-    assert(pkt);
-
-    return pkt->tot_len;
-}
-
-void vmxnet_rx_pkt_get_protocols(struct VmxnetRxPkt *pkt,
-                                 bool *isip4, bool *isip6,
-                                 bool *isudp, bool *istcp)
-{
-    assert(pkt);
-
-    *isip4 = pkt->isip4;
-    *isip6 = pkt->isip6;
-    *isudp = pkt->isudp;
-    *istcp = pkt->istcp;
-}
-
-struct iovec *vmxnet_rx_pkt_get_iovec(struct VmxnetRxPkt *pkt)
-{
-    assert(pkt);
-
-    return pkt->vec;
-}
-
-void vmxnet_rx_pkt_set_vhdr(struct VmxnetRxPkt *pkt,
-                            struct virtio_net_hdr *vhdr)
-{
-    assert(pkt);
-
-    memcpy(&pkt->virt_hdr, vhdr, sizeof pkt->virt_hdr);
-}
-
-bool vmxnet_rx_pkt_is_vlan_stripped(struct VmxnetRxPkt *pkt)
-{
-    assert(pkt);
-
-    return pkt->vlan_stripped;
-}
-
-bool vmxnet_rx_pkt_has_virt_hdr(struct VmxnetRxPkt *pkt)
-{
-    assert(pkt);
-
-    return pkt->has_virt_hdr;
-}
-
-uint16_t vmxnet_rx_pkt_get_num_frags(struct VmxnetRxPkt *pkt)
-{
-    assert(pkt);
-
-    return pkt->vec_len;
-}
-
-uint16_t vmxnet_rx_pkt_get_vlan_tag(struct VmxnetRxPkt *pkt)
-{
-    assert(pkt);
-
-    return pkt->tci;
-}
diff --git a/hw/vmxnet_rx_pkt.h b/hw/vmxnet_rx_pkt.h
deleted file mode 100644 (file)
index 6b2c60e..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * QEMU VMWARE VMXNET* paravirtual NICs - RX packets abstraction
- *
- * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
- *
- * Developed by Daynix Computing LTD (http://www.daynix.com)
- *
- * Authors:
- * Dmitry Fleytman <dmitry@daynix.com>
- * Tamir Shomer <tamirs@daynix.com>
- * Yan Vugenfirer <yan@daynix.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- *
- */
-
-#ifndef VMXNET_RX_PKT_H
-#define VMXNET_RX_PKT_H
-
-#include "stdint.h"
-#include "stdbool.h"
-#include "net/eth.h"
-
-/* defines to enable packet dump functions */
-/*#define VMXNET_RX_PKT_DEBUG*/
-
-struct VmxnetRxPkt;
-
-/**
- * Clean all rx packet resources
- *
- * @pkt:            packet
- *
- */
-void vmxnet_rx_pkt_uninit(struct VmxnetRxPkt *pkt);
-
-/**
- * Init function for rx packet functionality
- *
- * @pkt:            packet pointer
- * @has_virt_hdr:   device uses virtio header
- *
- */
-void vmxnet_rx_pkt_init(struct VmxnetRxPkt **pkt, bool has_virt_hdr);
-
-/**
- * returns total length of data attached to rx context
- *
- * @pkt:            packet
- *
- * Return:  nothing
- *
- */
-size_t vmxnet_rx_pkt_get_total_len(struct VmxnetRxPkt *pkt);
-
-/**
- * fetches packet analysis results
- *
- * @pkt:            packet
- * @isip4:          whether the packet given is IPv4
- * @isip6:          whether the packet given is IPv6
- * @isudp:          whether the packet given is UDP
- * @istcp:          whether the packet given is TCP
- *
- */
-void vmxnet_rx_pkt_get_protocols(struct VmxnetRxPkt *pkt,
-                                 bool *isip4, bool *isip6,
-                                 bool *isudp, bool *istcp);
-
-/**
- * returns virtio header stored in rx context
- *
- * @pkt:            packet
- * @ret:            virtio header
- *
- */
-struct virtio_net_hdr *vmxnet_rx_pkt_get_vhdr(struct VmxnetRxPkt *pkt);
-
-/**
- * returns packet type
- *
- * @pkt:            packet
- * @ret:            packet type
- *
- */
-eth_pkt_types_e vmxnet_rx_pkt_get_packet_type(struct VmxnetRxPkt *pkt);
-
-/**
- * returns vlan tag
- *
- * @pkt:            packet
- * @ret:            VLAN tag
- *
- */
-uint16_t vmxnet_rx_pkt_get_vlan_tag(struct VmxnetRxPkt *pkt);
-
-/**
- * tells whether vlan was stripped from the packet
- *
- * @pkt:            packet
- * @ret:            VLAN stripped sign
- *
- */
-bool vmxnet_rx_pkt_is_vlan_stripped(struct VmxnetRxPkt *pkt);
-
-/**
- * notifies caller if the packet has virtio header
- *
- * @pkt:            packet
- * @ret:            true if packet has virtio header, false otherwize
- *
- */
-bool vmxnet_rx_pkt_has_virt_hdr(struct VmxnetRxPkt *pkt);
-
-/**
- * returns number of frags attached to the packet
- *
- * @pkt:            packet
- * @ret:            number of frags
- *
- */
-uint16_t vmxnet_rx_pkt_get_num_frags(struct VmxnetRxPkt *pkt);
-
-/**
- * attach data to rx packet
- *
- * @pkt:            packet
- * @data:           pointer to the data buffer
- * @len:            data length
- * @strip_vlan:     should the module strip vlan from data
- *
- */
-void vmxnet_rx_pkt_attach_data(struct VmxnetRxPkt *pkt, const void *data,
-    size_t len, bool strip_vlan);
-
-/**
- * returns io vector that holds the attached data
- *
- * @pkt:            packet
- * @ret:            pointer to IOVec
- *
- */
-struct iovec *vmxnet_rx_pkt_get_iovec(struct VmxnetRxPkt *pkt);
-
-/**
- * prints rx packet data if debug is enabled
- *
- * @pkt:            packet
- *
- */
-void vmxnet_rx_pkt_dump(struct VmxnetRxPkt *pkt);
-
-/**
- * copy passed vhdr data to packet context
- *
- * @pkt:            packet
- * @vhdr:           VHDR buffer
- *
- */
-void vmxnet_rx_pkt_set_vhdr(struct VmxnetRxPkt *pkt,
-    struct virtio_net_hdr *vhdr);
-
-/**
- * save packet type in packet context
- *
- * @pkt:            packet
- * @packet_type:    the packet type
- *
- */
-void vmxnet_rx_pkt_set_packet_type(struct VmxnetRxPkt *pkt,
-    eth_pkt_types_e packet_type);
-
-#endif
diff --git a/hw/vmxnet_tx_pkt.c b/hw/vmxnet_tx_pkt.c
deleted file mode 100644 (file)
index b1e795b..0000000
+++ /dev/null
@@ -1,567 +0,0 @@
-/*
- * QEMU VMWARE VMXNET* paravirtual NICs - TX packets abstractions
- *
- * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
- *
- * Developed by Daynix Computing LTD (http://www.daynix.com)
- *
- * Authors:
- * Dmitry Fleytman <dmitry@daynix.com>
- * Tamir Shomer <tamirs@daynix.com>
- * Yan Vugenfirer <yan@daynix.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- *
- */
-
-#include "vmxnet_tx_pkt.h"
-#include "net/eth.h"
-#include "qemu-common.h"
-#include "qemu/iov.h"
-#include "net/checksum.h"
-#include "net/tap.h"
-#include "net/net.h"
-#include "exec/cpu-common.h"
-
-enum {
-    VMXNET_TX_PKT_VHDR_FRAG = 0,
-    VMXNET_TX_PKT_L2HDR_FRAG,
-    VMXNET_TX_PKT_L3HDR_FRAG,
-    VMXNET_TX_PKT_PL_START_FRAG
-};
-
-/* TX packet private context */
-struct VmxnetTxPkt {
-    struct virtio_net_hdr virt_hdr;
-    bool has_virt_hdr;
-
-    struct iovec *raw;
-    uint32_t raw_frags;
-    uint32_t max_raw_frags;
-
-    struct iovec *vec;
-
-    uint8_t l2_hdr[ETH_MAX_L2_HDR_LEN];
-
-    uint32_t payload_len;
-
-    uint32_t payload_frags;
-    uint32_t max_payload_frags;
-
-    uint16_t hdr_len;
-    eth_pkt_types_e packet_type;
-    uint8_t l4proto;
-};
-
-void vmxnet_tx_pkt_init(struct VmxnetTxPkt **pkt, uint32_t max_frags,
-    bool has_virt_hdr)
-{
-    struct VmxnetTxPkt *p = g_malloc0(sizeof *p);
-
-    p->vec = g_malloc((sizeof *p->vec) *
-        (max_frags + VMXNET_TX_PKT_PL_START_FRAG));
-
-    p->raw = g_malloc((sizeof *p->raw) * max_frags);
-
-    p->max_payload_frags = max_frags;
-    p->max_raw_frags = max_frags;
-    p->has_virt_hdr = has_virt_hdr;
-    p->vec[VMXNET_TX_PKT_VHDR_FRAG].iov_base = &p->virt_hdr;
-    p->vec[VMXNET_TX_PKT_VHDR_FRAG].iov_len =
-        p->has_virt_hdr ? sizeof p->virt_hdr : 0;
-    p->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base = &p->l2_hdr;
-    p->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base = NULL;
-    p->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len = 0;
-
-    *pkt = p;
-}
-
-void vmxnet_tx_pkt_uninit(struct VmxnetTxPkt *pkt)
-{
-    if (pkt) {
-        g_free(pkt->vec);
-        g_free(pkt->raw);
-        g_free(pkt);
-    }
-}
-
-void vmxnet_tx_pkt_update_ip_checksums(struct VmxnetTxPkt *pkt)
-{
-    uint16_t csum;
-    uint32_t ph_raw_csum;
-    assert(pkt);
-    uint8_t gso_type = pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN;
-    struct ip_header *ip_hdr;
-
-    if (VIRTIO_NET_HDR_GSO_TCPV4 != gso_type &&
-        VIRTIO_NET_HDR_GSO_UDP != gso_type) {
-        return;
-    }
-
-    ip_hdr = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base;
-
-    if (pkt->payload_len + pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len >
-        ETH_MAX_IP_DGRAM_LEN) {
-        return;
-    }
-
-    ip_hdr->ip_len = cpu_to_be16(pkt->payload_len +
-        pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len);
-
-    /* Calculate IP header checksum                    */
-    ip_hdr->ip_sum = 0;
-    csum = net_raw_checksum((uint8_t *)ip_hdr,
-        pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len);
-    ip_hdr->ip_sum = cpu_to_be16(csum);
-
-    /* Calculate IP pseudo header checksum             */
-    ph_raw_csum = eth_calc_pseudo_hdr_csum(ip_hdr, pkt->payload_len);
-    csum = cpu_to_be16(~net_checksum_finish(ph_raw_csum));
-    iov_from_buf(&pkt->vec[VMXNET_TX_PKT_PL_START_FRAG], pkt->payload_frags,
-                 pkt->virt_hdr.csum_offset, &csum, sizeof(csum));
-}
-
-static void vmxnet_tx_pkt_calculate_hdr_len(struct VmxnetTxPkt *pkt)
-{
-    pkt->hdr_len = pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len +
-        pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len;
-}
-
-static bool vmxnet_tx_pkt_parse_headers(struct VmxnetTxPkt *pkt)
-{
-    struct iovec *l2_hdr, *l3_hdr;
-    size_t bytes_read;
-    size_t full_ip6hdr_len;
-    uint16_t l3_proto;
-
-    assert(pkt);
-
-    l2_hdr = &pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG];
-    l3_hdr = &pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG];
-
-    bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 0, l2_hdr->iov_base,
-                            ETH_MAX_L2_HDR_LEN);
-    if (bytes_read < ETH_MAX_L2_HDR_LEN) {
-        l2_hdr->iov_len = 0;
-        return false;
-    } else {
-        l2_hdr->iov_len = eth_get_l2_hdr_length(l2_hdr->iov_base);
-    }
-
-    l3_proto = eth_get_l3_proto(l2_hdr->iov_base, l2_hdr->iov_len);
-
-    switch (l3_proto) {
-    case ETH_P_IP:
-        l3_hdr->iov_base = g_malloc(ETH_MAX_IP4_HDR_LEN);
-
-        bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len,
-                                l3_hdr->iov_base, sizeof(struct ip_header));
-
-        if (bytes_read < sizeof(struct ip_header)) {
-            l3_hdr->iov_len = 0;
-            return false;
-        }
-
-        l3_hdr->iov_len = IP_HDR_GET_LEN(l3_hdr->iov_base);
-        pkt->l4proto = ((struct ip_header *) l3_hdr->iov_base)->ip_p;
-
-        /* copy optional IPv4 header data */
-        bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags,
-                                l2_hdr->iov_len + sizeof(struct ip_header),
-                                l3_hdr->iov_base + sizeof(struct ip_header),
-                                l3_hdr->iov_len - sizeof(struct ip_header));
-        if (bytes_read < l3_hdr->iov_len - sizeof(struct ip_header)) {
-            l3_hdr->iov_len = 0;
-            return false;
-        }
-        break;
-
-    case ETH_P_IPV6:
-        if (!eth_parse_ipv6_hdr(pkt->raw, pkt->raw_frags, l2_hdr->iov_len,
-                               &pkt->l4proto, &full_ip6hdr_len)) {
-            l3_hdr->iov_len = 0;
-            return false;
-        }
-
-        l3_hdr->iov_base = g_malloc(full_ip6hdr_len);
-
-        bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, l2_hdr->iov_len,
-                                l3_hdr->iov_base, full_ip6hdr_len);
-
-        if (bytes_read < full_ip6hdr_len) {
-            l3_hdr->iov_len = 0;
-            return false;
-        } else {
-            l3_hdr->iov_len = full_ip6hdr_len;
-        }
-        break;
-
-    default:
-        l3_hdr->iov_len = 0;
-        break;
-    }
-
-    vmxnet_tx_pkt_calculate_hdr_len(pkt);
-    pkt->packet_type = get_eth_packet_type(l2_hdr->iov_base);
-    return true;
-}
-
-static bool vmxnet_tx_pkt_rebuild_payload(struct VmxnetTxPkt *pkt)
-{
-    size_t payload_len = iov_size(pkt->raw, pkt->raw_frags) - pkt->hdr_len;
-
-    pkt->payload_frags = iov_copy(&pkt->vec[VMXNET_TX_PKT_PL_START_FRAG],
-                                pkt->max_payload_frags,
-                                pkt->raw, pkt->raw_frags,
-                                pkt->hdr_len, payload_len);
-
-    if (pkt->payload_frags != (uint32_t) -1) {
-        pkt->payload_len = payload_len;
-        return true;
-    } else {
-        return false;
-    }
-}
-
-bool vmxnet_tx_pkt_parse(struct VmxnetTxPkt *pkt)
-{
-    return vmxnet_tx_pkt_parse_headers(pkt) &&
-           vmxnet_tx_pkt_rebuild_payload(pkt);
-}
-
-struct virtio_net_hdr *vmxnet_tx_pkt_get_vhdr(struct VmxnetTxPkt *pkt)
-{
-    assert(pkt);
-    return &pkt->virt_hdr;
-}
-
-static uint8_t vmxnet_tx_pkt_get_gso_type(struct VmxnetTxPkt *pkt,
-                                          bool tso_enable)
-{
-    uint8_t rc = VIRTIO_NET_HDR_GSO_NONE;
-    uint16_t l3_proto;
-
-    l3_proto = eth_get_l3_proto(pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base,
-        pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len);
-
-    if (!tso_enable) {
-        goto func_exit;
-    }
-
-    rc = eth_get_gso_type(l3_proto, pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base,
-                          pkt->l4proto);
-
-func_exit:
-    return rc;
-}
-
-void vmxnet_tx_pkt_build_vheader(struct VmxnetTxPkt *pkt, bool tso_enable,
-    bool csum_enable, uint32_t gso_size)
-{
-    struct tcp_hdr l4hdr;
-    assert(pkt);
-
-    /* csum has to be enabled if tso is. */
-    assert(csum_enable || !tso_enable);
-
-    pkt->virt_hdr.gso_type = vmxnet_tx_pkt_get_gso_type(pkt, tso_enable);
-
-    switch (pkt->virt_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
-    case VIRTIO_NET_HDR_GSO_NONE:
-        pkt->virt_hdr.hdr_len = 0;
-        pkt->virt_hdr.gso_size = 0;
-        break;
-
-    case VIRTIO_NET_HDR_GSO_UDP:
-        pkt->virt_hdr.gso_size = IP_FRAG_ALIGN_SIZE(gso_size);
-        pkt->virt_hdr.hdr_len = pkt->hdr_len + sizeof(struct udp_header);
-        break;
-
-    case VIRTIO_NET_HDR_GSO_TCPV4:
-    case VIRTIO_NET_HDR_GSO_TCPV6:
-        iov_to_buf(&pkt->vec[VMXNET_TX_PKT_PL_START_FRAG], pkt->payload_frags,
-                   0, &l4hdr, sizeof(l4hdr));
-        pkt->virt_hdr.hdr_len = pkt->hdr_len + l4hdr.th_off * sizeof(uint32_t);
-        pkt->virt_hdr.gso_size = IP_FRAG_ALIGN_SIZE(gso_size);
-        break;
-
-    default:
-        assert(false);
-    }
-
-    if (csum_enable) {
-        switch (pkt->l4proto) {
-        case IP_PROTO_TCP:
-            pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
-            pkt->virt_hdr.csum_start = pkt->hdr_len;
-            pkt->virt_hdr.csum_offset = offsetof(struct tcp_hdr, th_sum);
-            break;
-        case IP_PROTO_UDP:
-            pkt->virt_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
-            pkt->virt_hdr.csum_start = pkt->hdr_len;
-            pkt->virt_hdr.csum_offset = offsetof(struct udp_hdr, uh_sum);
-            break;
-        default:
-            break;
-        }
-    }
-}
-
-void vmxnet_tx_pkt_setup_vlan_header(struct VmxnetTxPkt *pkt, uint16_t vlan)
-{
-    bool is_new;
-    assert(pkt);
-
-    eth_setup_vlan_headers(pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base,
-        vlan, &is_new);
-
-    /* update l2hdrlen */
-    if (is_new) {
-        pkt->hdr_len += sizeof(struct vlan_header);
-        pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len +=
-            sizeof(struct vlan_header);
-    }
-}
-
-bool vmxnet_tx_pkt_add_raw_fragment(struct VmxnetTxPkt *pkt, hwaddr pa,
-    size_t len)
-{
-    hwaddr mapped_len = 0;
-    struct iovec *ventry;
-    assert(pkt);
-    assert(pkt->max_raw_frags > pkt->raw_frags);
-
-    if (!len) {
-        return true;
-     }
-
-    ventry = &pkt->raw[pkt->raw_frags];
-    mapped_len = len;
-
-    ventry->iov_base = cpu_physical_memory_map(pa, &mapped_len, false);
-    ventry->iov_len = mapped_len;
-    pkt->raw_frags += !!ventry->iov_base;
-
-    if ((ventry->iov_base == NULL) || (len != mapped_len)) {
-        return false;
-    }
-
-    return true;
-}
-
-eth_pkt_types_e vmxnet_tx_pkt_get_packet_type(struct VmxnetTxPkt *pkt)
-{
-    assert(pkt);
-
-    return pkt->packet_type;
-}
-
-size_t vmxnet_tx_pkt_get_total_len(struct VmxnetTxPkt *pkt)
-{
-    assert(pkt);
-
-    return pkt->hdr_len + pkt->payload_len;
-}
-
-void vmxnet_tx_pkt_dump(struct VmxnetTxPkt *pkt)
-{
-#ifdef VMXNET_TX_PKT_DEBUG
-    assert(pkt);
-
-    printf("TX PKT: hdr_len: %d, pkt_type: 0x%X, l2hdr_len: %lu, "
-        "l3hdr_len: %lu, payload_len: %u\n", pkt->hdr_len, pkt->packet_type,
-        pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len,
-        pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len, pkt->payload_len);
-#endif
-}
-
-void vmxnet_tx_pkt_reset(struct VmxnetTxPkt *pkt)
-{
-    int i;
-
-    /* no assert, as reset can be called before tx_pkt_init */
-    if (!pkt) {
-        return;
-    }
-
-    memset(&pkt->virt_hdr, 0, sizeof(pkt->virt_hdr));
-
-    g_free(pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base);
-    pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base = NULL;
-
-    assert(pkt->vec);
-    for (i = VMXNET_TX_PKT_L2HDR_FRAG;
-         i < pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG; i++) {
-        pkt->vec[i].iov_len = 0;
-    }
-    pkt->payload_len = 0;
-    pkt->payload_frags = 0;
-
-    assert(pkt->raw);
-    for (i = 0; i < pkt->raw_frags; i++) {
-        assert(pkt->raw[i].iov_base);
-        cpu_physical_memory_unmap(pkt->raw[i].iov_base, pkt->raw[i].iov_len,
-                                  false, pkt->raw[i].iov_len);
-        pkt->raw[i].iov_len = 0;
-    }
-    pkt->raw_frags = 0;
-
-    pkt->hdr_len = 0;
-    pkt->packet_type = 0;
-    pkt->l4proto = 0;
-}
-
-static void vmxnet_tx_pkt_do_sw_csum(struct VmxnetTxPkt *pkt)
-{
-    struct iovec *iov = &pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG];
-    uint32_t csum_cntr;
-    uint16_t csum = 0;
-    /* num of iovec without vhdr */
-    uint32_t iov_len = pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG - 1;
-    uint16_t csl;
-    struct ip_header *iphdr;
-    size_t csum_offset = pkt->virt_hdr.csum_start + pkt->virt_hdr.csum_offset;
-
-    /* Put zero to checksum field */
-    iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum);
-
-    /* Calculate L4 TCP/UDP checksum */
-    csl = pkt->payload_len;
-
-    /* data checksum */
-    csum_cntr =
-        net_checksum_add_iov(iov, iov_len, pkt->virt_hdr.csum_start, csl);
-    /* add pseudo header to csum */
-    iphdr = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base;
-    csum_cntr += eth_calc_pseudo_hdr_csum(iphdr, csl);
-
-    /* Put the checksum obtained into the packet */
-    csum = cpu_to_be16(net_checksum_finish(csum_cntr));
-    iov_from_buf(iov, iov_len, csum_offset, &csum, sizeof csum);
-}
-
-enum {
-    VMXNET_TX_PKT_FRAGMENT_L2_HDR_POS = 0,
-    VMXNET_TX_PKT_FRAGMENT_L3_HDR_POS,
-    VMXNET_TX_PKT_FRAGMENT_HEADER_NUM
-};
-
-#define VMXNET_MAX_FRAG_SG_LIST (64)
-
-static size_t vmxnet_tx_pkt_fetch_fragment(struct VmxnetTxPkt *pkt,
-    int *src_idx, size_t *src_offset, struct iovec *dst, int *dst_idx)
-{
-    size_t fetched = 0;
-    struct iovec *src = pkt->vec;
-
-    *dst_idx = VMXNET_TX_PKT_FRAGMENT_HEADER_NUM;
-
-    while (fetched < pkt->virt_hdr.gso_size) {
-
-        /* no more place in fragment iov */
-        if (*dst_idx == VMXNET_MAX_FRAG_SG_LIST) {
-            break;
-        }
-
-        /* no more data in iovec */
-        if (*src_idx == (pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG)) {
-            break;
-        }
-
-
-        dst[*dst_idx].iov_base = src[*src_idx].iov_base + *src_offset;
-        dst[*dst_idx].iov_len = MIN(src[*src_idx].iov_len - *src_offset,
-            pkt->virt_hdr.gso_size - fetched);
-
-        *src_offset += dst[*dst_idx].iov_len;
-        fetched += dst[*dst_idx].iov_len;
-
-        if (*src_offset == src[*src_idx].iov_len) {
-            *src_offset = 0;
-            (*src_idx)++;
-        }
-
-        (*dst_idx)++;
-    }
-
-    return fetched;
-}
-
-static bool vmxnet_tx_pkt_do_sw_fragmentation(struct VmxnetTxPkt *pkt,
-    NetClientState *nc)
-{
-    struct iovec fragment[VMXNET_MAX_FRAG_SG_LIST];
-    size_t fragment_len = 0;
-    bool more_frags = false;
-
-    /* some pointers for shorter code */
-    void *l2_iov_base, *l3_iov_base;
-    size_t l2_iov_len, l3_iov_len;
-    int src_idx =  VMXNET_TX_PKT_PL_START_FRAG, dst_idx;
-    size_t src_offset = 0;
-    size_t fragment_offset = 0;
-
-    l2_iov_base = pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_base;
-    l2_iov_len = pkt->vec[VMXNET_TX_PKT_L2HDR_FRAG].iov_len;
-    l3_iov_base = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_base;
-    l3_iov_len = pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len;
-
-    /* Copy headers */
-    fragment[VMXNET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_base = l2_iov_base;
-    fragment[VMXNET_TX_PKT_FRAGMENT_L2_HDR_POS].iov_len = l2_iov_len;
-    fragment[VMXNET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_base = l3_iov_base;
-    fragment[VMXNET_TX_PKT_FRAGMENT_L3_HDR_POS].iov_len = l3_iov_len;
-
-
-    /* Put as much data as possible and send */
-    do {
-        fragment_len = vmxnet_tx_pkt_fetch_fragment(pkt, &src_idx, &src_offset,
-            fragment, &dst_idx);
-
-        more_frags = (fragment_offset + fragment_len < pkt->payload_len);
-
-        eth_setup_ip4_fragmentation(l2_iov_base, l2_iov_len, l3_iov_base,
-            l3_iov_len, fragment_len, fragment_offset, more_frags);
-
-        eth_fix_ip4_checksum(l3_iov_base, l3_iov_len);
-
-        qemu_sendv_packet(nc, fragment, dst_idx);
-
-        fragment_offset += fragment_len;
-
-    } while (more_frags);
-
-    return true;
-}
-
-bool vmxnet_tx_pkt_send(struct VmxnetTxPkt *pkt, NetClientState *nc)
-{
-    assert(pkt);
-
-    if (!pkt->has_virt_hdr &&
-        pkt->virt_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
-        vmxnet_tx_pkt_do_sw_csum(pkt);
-    }
-
-    /*
-     * Since underlying infrastructure does not support IP datagrams longer
-     * than 64K we should drop such packets and don't even try to send
-     */
-    if (VIRTIO_NET_HDR_GSO_NONE != pkt->virt_hdr.gso_type) {
-        if (pkt->payload_len >
-            ETH_MAX_IP_DGRAM_LEN -
-            pkt->vec[VMXNET_TX_PKT_L3HDR_FRAG].iov_len) {
-            return false;
-        }
-    }
-
-    if (pkt->has_virt_hdr ||
-        pkt->virt_hdr.gso_type == VIRTIO_NET_HDR_GSO_NONE) {
-        qemu_sendv_packet(nc, pkt->vec,
-            pkt->payload_frags + VMXNET_TX_PKT_PL_START_FRAG);
-        return true;
-    }
-
-    return vmxnet_tx_pkt_do_sw_fragmentation(pkt, nc);
-}
diff --git a/hw/vmxnet_tx_pkt.h b/hw/vmxnet_tx_pkt.h
deleted file mode 100644 (file)
index 57121a6..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * QEMU VMWARE VMXNET* paravirtual NICs - TX packets abstraction
- *
- * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
- *
- * Developed by Daynix Computing LTD (http://www.daynix.com)
- *
- * Authors:
- * Dmitry Fleytman <dmitry@daynix.com>
- * Tamir Shomer <tamirs@daynix.com>
- * Yan Vugenfirer <yan@daynix.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- *
- */
-
-#ifndef VMXNET_TX_PKT_H
-#define VMXNET_TX_PKT_H
-
-#include "stdint.h"
-#include "stdbool.h"
-#include "net/eth.h"
-#include "exec/hwaddr.h"
-
-/* define to enable packet dump functions */
-/*#define VMXNET_TX_PKT_DEBUG*/
-
-struct VmxnetTxPkt;
-
-/**
- * Init function for tx packet functionality
- *
- * @pkt:            packet pointer
- * @max_frags:      max tx ip fragments
- * @has_virt_hdr:   device uses virtio header.
- */
-void vmxnet_tx_pkt_init(struct VmxnetTxPkt **pkt, uint32_t max_frags,
-    bool has_virt_hdr);
-
-/**
- * Clean all tx packet resources.
- *
- * @pkt:            packet.
- */
-void vmxnet_tx_pkt_uninit(struct VmxnetTxPkt *pkt);
-
-/**
- * get virtio header
- *
- * @pkt:            packet
- * @ret:            virtio header
- */
-struct virtio_net_hdr *vmxnet_tx_pkt_get_vhdr(struct VmxnetTxPkt *pkt);
-
-/**
- * build virtio header (will be stored in module context)
- *
- * @pkt:            packet
- * @tso_enable:     TSO enabled
- * @csum_enable:    CSO enabled
- * @gso_size:       MSS size for TSO
- *
- */
-void vmxnet_tx_pkt_build_vheader(struct VmxnetTxPkt *pkt, bool tso_enable,
-    bool csum_enable, uint32_t gso_size);
-
-/**
- * updates vlan tag, and adds vlan header in case it is missing
- *
- * @pkt:            packet
- * @vlan:           VLAN tag
- *
- */
-void vmxnet_tx_pkt_setup_vlan_header(struct VmxnetTxPkt *pkt, uint16_t vlan);
-
-/**
- * populate data fragment into pkt context.
- *
- * @pkt:            packet
- * @pa:             physical address of fragment
- * @len:            length of fragment
- *
- */
-bool vmxnet_tx_pkt_add_raw_fragment(struct VmxnetTxPkt *pkt, hwaddr pa,
-    size_t len);
-
-/**
- * fix ip header fields and calculate checksums needed.
- *
- * @pkt:            packet
- *
- */
-void vmxnet_tx_pkt_update_ip_checksums(struct VmxnetTxPkt *pkt);
-
-/**
- * get length of all populated data.
- *
- * @pkt:            packet
- * @ret:            total data length
- *
- */
-size_t vmxnet_tx_pkt_get_total_len(struct VmxnetTxPkt *pkt);
-
-/**
- * get packet type
- *
- * @pkt:            packet
- * @ret:            packet type
- *
- */
-eth_pkt_types_e vmxnet_tx_pkt_get_packet_type(struct VmxnetTxPkt *pkt);
-
-/**
- * prints packet data if debug is enabled
- *
- * @pkt:            packet
- *
- */
-void vmxnet_tx_pkt_dump(struct VmxnetTxPkt *pkt);
-
-/**
- * reset tx packet private context (needed to be called between packets)
- *
- * @pkt:            packet
- *
- */
-void vmxnet_tx_pkt_reset(struct VmxnetTxPkt *pkt);
-
-/**
- * Send packet to qemu. handles sw offloads if vhdr is not supported.
- *
- * @pkt:            packet
- * @nc:             NetClientState
- * @ret:            operation result
- *
- */
-bool vmxnet_tx_pkt_send(struct VmxnetTxPkt *pkt, NetClientState *nc);
-
-/**
- * parse raw packet data and analyze offload requirements.
- *
- * @pkt:            packet
- *
- */
-bool vmxnet_tx_pkt_parse(struct VmxnetTxPkt *pkt);
-
-#endif
diff --git a/hw/watchdog.c b/hw/watchdog.c
deleted file mode 100644 (file)
index cb4e1f9..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Virtual hardware watchdog.
- *
- * Copyright (C) 2009 Red Hat Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- * By Richard W.M. Jones (rjones@redhat.com).
- */
-
-#include "qemu-common.h"
-#include "qemu/option.h"
-#include "qemu/config-file.h"
-#include "qemu/queue.h"
-#include "qapi/qmp/types.h"
-#include "monitor/monitor.h"
-#include "sysemu/sysemu.h"
-#include "sysemu/watchdog.h"
-
-/* Possible values for action parameter. */
-#define WDT_RESET        1     /* Hard reset. */
-#define WDT_SHUTDOWN     2     /* Shutdown. */
-#define WDT_POWEROFF     3     /* Quit. */
-#define WDT_PAUSE        4     /* Pause. */
-#define WDT_DEBUG        5     /* Prints a message and continues running. */
-#define WDT_NONE         6     /* Do nothing. */
-
-static int watchdog_action = WDT_RESET;
-static QLIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
-
-void watchdog_add_model(WatchdogTimerModel *model)
-{
-    QLIST_INSERT_HEAD(&watchdog_list, model, entry);
-}
-
-/* Returns:
- *   0 = continue
- *   1 = exit program with error
- *   2 = exit program without error
- */
-int select_watchdog(const char *p)
-{
-    WatchdogTimerModel *model;
-    QemuOpts *opts;
-
-    /* -watchdog ? lists available devices and exits cleanly. */
-    if (is_help_option(p)) {
-        QLIST_FOREACH(model, &watchdog_list, entry) {
-            fprintf(stderr, "\t%s\t%s\n",
-                     model->wdt_name, model->wdt_description);
-        }
-        return 2;
-    }
-
-    QLIST_FOREACH(model, &watchdog_list, entry) {
-        if (strcasecmp(model->wdt_name, p) == 0) {
-            /* add the device */
-            opts = qemu_opts_create_nofail(qemu_find_opts("device"));
-            qemu_opt_set(opts, "driver", p);
-            return 0;
-        }
-    }
-
-    fprintf(stderr, "Unknown -watchdog device. Supported devices are:\n");
-    QLIST_FOREACH(model, &watchdog_list, entry) {
-        fprintf(stderr, "\t%s\t%s\n",
-                 model->wdt_name, model->wdt_description);
-    }
-    return 1;
-}
-
-int select_watchdog_action(const char *p)
-{
-    if (strcasecmp(p, "reset") == 0)
-        watchdog_action = WDT_RESET;
-    else if (strcasecmp(p, "shutdown") == 0)
-        watchdog_action = WDT_SHUTDOWN;
-    else if (strcasecmp(p, "poweroff") == 0)
-        watchdog_action = WDT_POWEROFF;
-    else if (strcasecmp(p, "pause") == 0)
-        watchdog_action = WDT_PAUSE;
-    else if (strcasecmp(p, "debug") == 0)
-        watchdog_action = WDT_DEBUG;
-    else if (strcasecmp(p, "none") == 0)
-        watchdog_action = WDT_NONE;
-    else
-        return -1;
-
-    return 0;
-}
-
-static void watchdog_mon_event(const char *action)
-{
-    QObject *data;
-
-    data = qobject_from_jsonf("{ 'action': %s }", action);
-    monitor_protocol_event(QEVENT_WATCHDOG, data);
-    qobject_decref(data);
-}
-
-/* This actually performs the "action" once a watchdog has expired,
- * ie. reboot, shutdown, exit, etc.
- */
-void watchdog_perform_action(void)
-{
-    switch(watchdog_action) {
-    case WDT_RESET:             /* same as 'system_reset' in monitor */
-        watchdog_mon_event("reset");
-        qemu_system_reset_request();
-        break;
-
-    case WDT_SHUTDOWN:          /* same as 'system_powerdown' in monitor */
-        watchdog_mon_event("shutdown");
-        qemu_system_powerdown_request();
-        break;
-
-    case WDT_POWEROFF:          /* same as 'quit' command in monitor */
-        watchdog_mon_event("poweroff");
-        exit(0);
-        break;
-
-    case WDT_PAUSE:             /* same as 'stop' command in monitor */
-        watchdog_mon_event("pause");
-        vm_stop(RUN_STATE_WATCHDOG);
-        break;
-
-    case WDT_DEBUG:
-        watchdog_mon_event("debug");
-        fprintf(stderr, "watchdog: timer fired\n");
-        break;
-
-    case WDT_NONE:
-        watchdog_mon_event("none");
-        break;
-    }
-}
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f57133b72905897f969521ef9fda23ed44d3b0f5 100644 (file)
@@ -0,0 +1,2 @@
+common-obj-y += watchdog.o
+common-obj-$(CONFIG_PCI) += wdt_i6300esb.o
diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c
new file mode 100644 (file)
index 0000000..cb4e1f9
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#include "qemu-common.h"
+#include "qemu/option.h"
+#include "qemu/config-file.h"
+#include "qemu/queue.h"
+#include "qapi/qmp/types.h"
+#include "monitor/monitor.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/watchdog.h"
+
+/* Possible values for action parameter. */
+#define WDT_RESET        1     /* Hard reset. */
+#define WDT_SHUTDOWN     2     /* Shutdown. */
+#define WDT_POWEROFF     3     /* Quit. */
+#define WDT_PAUSE        4     /* Pause. */
+#define WDT_DEBUG        5     /* Prints a message and continues running. */
+#define WDT_NONE         6     /* Do nothing. */
+
+static int watchdog_action = WDT_RESET;
+static QLIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list;
+
+void watchdog_add_model(WatchdogTimerModel *model)
+{
+    QLIST_INSERT_HEAD(&watchdog_list, model, entry);
+}
+
+/* Returns:
+ *   0 = continue
+ *   1 = exit program with error
+ *   2 = exit program without error
+ */
+int select_watchdog(const char *p)
+{
+    WatchdogTimerModel *model;
+    QemuOpts *opts;
+
+    /* -watchdog ? lists available devices and exits cleanly. */
+    if (is_help_option(p)) {
+        QLIST_FOREACH(model, &watchdog_list, entry) {
+            fprintf(stderr, "\t%s\t%s\n",
+                     model->wdt_name, model->wdt_description);
+        }
+        return 2;
+    }
+
+    QLIST_FOREACH(model, &watchdog_list, entry) {
+        if (strcasecmp(model->wdt_name, p) == 0) {
+            /* add the device */
+            opts = qemu_opts_create_nofail(qemu_find_opts("device"));
+            qemu_opt_set(opts, "driver", p);
+            return 0;
+        }
+    }
+
+    fprintf(stderr, "Unknown -watchdog device. Supported devices are:\n");
+    QLIST_FOREACH(model, &watchdog_list, entry) {
+        fprintf(stderr, "\t%s\t%s\n",
+                 model->wdt_name, model->wdt_description);
+    }
+    return 1;
+}
+
+int select_watchdog_action(const char *p)
+{
+    if (strcasecmp(p, "reset") == 0)
+        watchdog_action = WDT_RESET;
+    else if (strcasecmp(p, "shutdown") == 0)
+        watchdog_action = WDT_SHUTDOWN;
+    else if (strcasecmp(p, "poweroff") == 0)
+        watchdog_action = WDT_POWEROFF;
+    else if (strcasecmp(p, "pause") == 0)
+        watchdog_action = WDT_PAUSE;
+    else if (strcasecmp(p, "debug") == 0)
+        watchdog_action = WDT_DEBUG;
+    else if (strcasecmp(p, "none") == 0)
+        watchdog_action = WDT_NONE;
+    else
+        return -1;
+
+    return 0;
+}
+
+static void watchdog_mon_event(const char *action)
+{
+    QObject *data;
+
+    data = qobject_from_jsonf("{ 'action': %s }", action);
+    monitor_protocol_event(QEVENT_WATCHDOG, data);
+    qobject_decref(data);
+}
+
+/* This actually performs the "action" once a watchdog has expired,
+ * ie. reboot, shutdown, exit, etc.
+ */
+void watchdog_perform_action(void)
+{
+    switch(watchdog_action) {
+    case WDT_RESET:             /* same as 'system_reset' in monitor */
+        watchdog_mon_event("reset");
+        qemu_system_reset_request();
+        break;
+
+    case WDT_SHUTDOWN:          /* same as 'system_powerdown' in monitor */
+        watchdog_mon_event("shutdown");
+        qemu_system_powerdown_request();
+        break;
+
+    case WDT_POWEROFF:          /* same as 'quit' command in monitor */
+        watchdog_mon_event("poweroff");
+        exit(0);
+        break;
+
+    case WDT_PAUSE:             /* same as 'stop' command in monitor */
+        watchdog_mon_event("pause");
+        vm_stop(RUN_STATE_WATCHDOG);
+        break;
+
+    case WDT_DEBUG:
+        watchdog_mon_event("debug");
+        fprintf(stderr, "watchdog: timer fired\n");
+        break;
+
+    case WDT_NONE:
+        watchdog_mon_event("none");
+        break;
+    }
+}
diff --git a/hw/watchdog/wdt_i6300esb.c b/hw/watchdog/wdt_i6300esb.c
new file mode 100644 (file)
index 0000000..1407fba
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+ * Virtual hardware watchdog.
+ *
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * By Richard W.M. Jones (rjones@redhat.com).
+ */
+
+#include <inttypes.h>
+
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "sysemu/watchdog.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+
+/*#define I6300ESB_DEBUG 1*/
+
+#ifdef I6300ESB_DEBUG
+#define i6300esb_debug(fs,...) \
+    fprintf(stderr,"i6300esb: %s: "fs,__func__,##__VA_ARGS__)
+#else
+#define i6300esb_debug(fs,...)
+#endif
+
+/* PCI configuration registers */
+#define ESB_CONFIG_REG  0x60            /* Config register                   */
+#define ESB_LOCK_REG    0x68            /* WDT lock register                 */
+
+/* Memory mapped registers (offset from base address) */
+#define ESB_TIMER1_REG  0x00            /* Timer1 value after each reset     */
+#define ESB_TIMER2_REG  0x04            /* Timer2 value after each reset     */
+#define ESB_GINTSR_REG  0x08            /* General Interrupt Status Register */
+#define ESB_RELOAD_REG  0x0c            /* Reload register                   */
+
+/* Lock register bits */
+#define ESB_WDT_FUNC    (0x01 << 2)   /* Watchdog functionality            */
+#define ESB_WDT_ENABLE  (0x01 << 1)   /* Enable WDT                        */
+#define ESB_WDT_LOCK    (0x01 << 0)   /* Lock (nowayout)                   */
+
+/* Config register bits */
+#define ESB_WDT_REBOOT  (0x01 << 5)   /* Enable reboot on timeout          */
+#define ESB_WDT_FREQ    (0x01 << 2)   /* Decrement frequency               */
+#define ESB_WDT_INTTYPE (0x11 << 0)   /* Interrupt type on timer1 timeout  */
+
+/* Reload register bits */
+#define ESB_WDT_RELOAD  (0x01 << 8)    /* prevent timeout                   */
+
+/* Magic constants */
+#define ESB_UNLOCK1     0x80            /* Step 1 to unlock reset registers  */
+#define ESB_UNLOCK2     0x86            /* Step 2 to unlock reset registers  */
+
+/* Device state. */
+struct I6300State {
+    PCIDevice dev;
+    MemoryRegion io_mem;
+
+    int reboot_enabled;         /* "Reboot" on timer expiry.  The real action
+                                 * performed depends on the -watchdog-action
+                                 * param passed on QEMU command line.
+                                 */
+    int clock_scale;            /* Clock scale. */
+#define CLOCK_SCALE_1KHZ 0
+#define CLOCK_SCALE_1MHZ 1
+
+    int int_type;               /* Interrupt type generated. */
+#define INT_TYPE_IRQ 0          /* APIC 1, INT 10 */
+#define INT_TYPE_SMI 2
+#define INT_TYPE_DISABLED 3
+
+    int free_run;               /* If true, reload timer on expiry. */
+    int locked;                 /* If true, enabled field cannot be changed. */
+    int enabled;                /* If true, watchdog is enabled. */
+
+    QEMUTimer *timer;           /* The actual watchdog timer. */
+
+    uint32_t timer1_preload;    /* Values preloaded into timer1, timer2. */
+    uint32_t timer2_preload;
+    int stage;                  /* Stage (1 or 2). */
+
+    int unlock_state;           /* Guest writes 0x80, 0x86 to unlock the
+                                 * registers, and we transition through
+                                 * states 0 -> 1 -> 2 when this happens.
+                                 */
+
+    int previous_reboot_flag;   /* If the watchdog caused the previous
+                                 * reboot, this flag will be set.
+                                 */
+};
+
+typedef struct I6300State I6300State;
+
+/* This function is called when the watchdog has either been enabled
+ * (hence it starts counting down) or has been keep-alived.
+ */
+static void i6300esb_restart_timer(I6300State *d, int stage)
+{
+    int64_t timeout;
+
+    if (!d->enabled)
+        return;
+
+    d->stage = stage;
+
+    if (d->stage <= 1)
+        timeout = d->timer1_preload;
+    else
+        timeout = d->timer2_preload;
+
+    if (d->clock_scale == CLOCK_SCALE_1KHZ)
+        timeout <<= 15;
+    else
+        timeout <<= 5;
+
+    /* Get the timeout in units of ticks_per_sec. */
+    timeout = get_ticks_per_sec() * timeout / 33000000;
+
+    i6300esb_debug("stage %d, timeout %" PRIi64 "\n", d->stage, timeout);
+
+    qemu_mod_timer(d->timer, qemu_get_clock_ns(vm_clock) + timeout);
+}
+
+/* This is called when the guest disables the watchdog. */
+static void i6300esb_disable_timer(I6300State *d)
+{
+    i6300esb_debug("timer disabled\n");
+
+    qemu_del_timer(d->timer);
+}
+
+static void i6300esb_reset(DeviceState *dev)
+{
+    PCIDevice *pdev = PCI_DEVICE(dev);
+    I6300State *d = DO_UPCAST(I6300State, dev, pdev);
+
+    i6300esb_debug("I6300State = %p\n", d);
+
+    i6300esb_disable_timer(d);
+
+    /* NB: Don't change d->previous_reboot_flag in this function. */
+
+    d->reboot_enabled = 1;
+    d->clock_scale = CLOCK_SCALE_1KHZ;
+    d->int_type = INT_TYPE_IRQ;
+    d->free_run = 0;
+    d->locked = 0;
+    d->enabled = 0;
+    d->timer1_preload = 0xfffff;
+    d->timer2_preload = 0xfffff;
+    d->stage = 1;
+    d->unlock_state = 0;
+}
+
+/* This function is called when the watchdog expires.  Note that
+ * the hardware has two timers, and so expiry happens in two stages.
+ * If d->stage == 1 then we perform the first stage action (usually,
+ * sending an interrupt) and then restart the timer again for the
+ * second stage.  If the second stage expires then the watchdog
+ * really has run out.
+ */
+static void i6300esb_timer_expired(void *vp)
+{
+    I6300State *d = vp;
+
+    i6300esb_debug("stage %d\n", d->stage);
+
+    if (d->stage == 1) {
+        /* What to do at the end of stage 1? */
+        switch (d->int_type) {
+        case INT_TYPE_IRQ:
+            fprintf(stderr, "i6300esb_timer_expired: I would send APIC 1 INT 10 here if I knew how (XXX)\n");
+            break;
+        case INT_TYPE_SMI:
+            fprintf(stderr, "i6300esb_timer_expired: I would send SMI here if I knew how (XXX)\n");
+            break;
+        }
+
+        /* Start the second stage. */
+        i6300esb_restart_timer(d, 2);
+    } else {
+        /* Second stage expired, reboot for real. */
+        if (d->reboot_enabled) {
+            d->previous_reboot_flag = 1;
+            watchdog_perform_action(); /* This reboots, exits, etc */
+            i6300esb_reset(&d->dev.qdev);
+        }
+
+        /* In "free running mode" we start stage 1 again. */
+        if (d->free_run)
+            i6300esb_restart_timer(d, 1);
+    }
+}
+
+static void i6300esb_config_write(PCIDevice *dev, uint32_t addr,
+                                  uint32_t data, int len)
+{
+    I6300State *d = DO_UPCAST(I6300State, dev, dev);
+    int old;
+
+    i6300esb_debug("addr = %x, data = %x, len = %d\n", addr, data, len);
+
+    if (addr == ESB_CONFIG_REG && len == 2) {
+        d->reboot_enabled = (data & ESB_WDT_REBOOT) == 0;
+        d->clock_scale =
+            (data & ESB_WDT_FREQ) != 0 ? CLOCK_SCALE_1MHZ : CLOCK_SCALE_1KHZ;
+        d->int_type = (data & ESB_WDT_INTTYPE);
+    } else if (addr == ESB_LOCK_REG && len == 1) {
+        if (!d->locked) {
+            d->locked = (data & ESB_WDT_LOCK) != 0;
+            d->free_run = (data & ESB_WDT_FUNC) != 0;
+            old = d->enabled;
+            d->enabled = (data & ESB_WDT_ENABLE) != 0;
+            if (!old && d->enabled) /* Enabled transitioned from 0 -> 1 */
+                i6300esb_restart_timer(d, 1);
+            else if (!d->enabled)
+                i6300esb_disable_timer(d);
+        }
+    } else {
+        pci_default_write_config(dev, addr, data, len);
+    }
+}
+
+static uint32_t i6300esb_config_read(PCIDevice *dev, uint32_t addr, int len)
+{
+    I6300State *d = DO_UPCAST(I6300State, dev, dev);
+    uint32_t data;
+
+    i6300esb_debug ("addr = %x, len = %d\n", addr, len);
+
+    if (addr == ESB_CONFIG_REG && len == 2) {
+        data =
+            (d->reboot_enabled ? 0 : ESB_WDT_REBOOT) |
+            (d->clock_scale == CLOCK_SCALE_1MHZ ? ESB_WDT_FREQ : 0) |
+            d->int_type;
+        return data;
+    } else if (addr == ESB_LOCK_REG && len == 1) {
+        data =
+            (d->free_run ? ESB_WDT_FUNC : 0) |
+            (d->locked ? ESB_WDT_LOCK : 0) |
+            (d->enabled ? ESB_WDT_ENABLE : 0);
+        return data;
+    } else {
+        return pci_default_read_config(dev, addr, len);
+    }
+}
+
+static uint32_t i6300esb_mem_readb(void *vp, hwaddr addr)
+{
+    i6300esb_debug ("addr = %x\n", (int) addr);
+
+    return 0;
+}
+
+static uint32_t i6300esb_mem_readw(void *vp, hwaddr addr)
+{
+    uint32_t data = 0;
+    I6300State *d = vp;
+
+    i6300esb_debug("addr = %x\n", (int) addr);
+
+    if (addr == 0xc) {
+        /* The previous reboot flag is really bit 9, but there is
+         * a bug in the Linux driver where it thinks it's bit 12.
+         * Set both.
+         */
+        data = d->previous_reboot_flag ? 0x1200 : 0;
+    }
+
+    return data;
+}
+
+static uint32_t i6300esb_mem_readl(void *vp, hwaddr addr)
+{
+    i6300esb_debug("addr = %x\n", (int) addr);
+
+    return 0;
+}
+
+static void i6300esb_mem_writeb(void *vp, hwaddr addr, uint32_t val)
+{
+    I6300State *d = vp;
+
+    i6300esb_debug("addr = %x, val = %x\n", (int) addr, val);
+
+    if (addr == 0xc && val == 0x80)
+        d->unlock_state = 1;
+    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+        d->unlock_state = 2;
+}
+
+static void i6300esb_mem_writew(void *vp, hwaddr addr, uint32_t val)
+{
+    I6300State *d = vp;
+
+    i6300esb_debug("addr = %x, val = %x\n", (int) addr, val);
+
+    if (addr == 0xc && val == 0x80)
+        d->unlock_state = 1;
+    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+        d->unlock_state = 2;
+    else {
+        if (d->unlock_state == 2) {
+            if (addr == 0xc) {
+                if ((val & 0x100) != 0)
+                    /* This is the "ping" from the userspace watchdog in
+                     * the guest ...
+                     */
+                    i6300esb_restart_timer(d, 1);
+
+                /* Setting bit 9 resets the previous reboot flag.
+                 * There's a bug in the Linux driver where it sets
+                 * bit 12 instead.
+                 */
+                if ((val & 0x200) != 0 || (val & 0x1000) != 0) {
+                    d->previous_reboot_flag = 0;
+                }
+            }
+
+            d->unlock_state = 0;
+        }
+    }
+}
+
+static void i6300esb_mem_writel(void *vp, hwaddr addr, uint32_t val)
+{
+    I6300State *d = vp;
+
+    i6300esb_debug ("addr = %x, val = %x\n", (int) addr, val);
+
+    if (addr == 0xc && val == 0x80)
+        d->unlock_state = 1;
+    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
+        d->unlock_state = 2;
+    else {
+        if (d->unlock_state == 2) {
+            if (addr == 0)
+                d->timer1_preload = val & 0xfffff;
+            else if (addr == 4)
+                d->timer2_preload = val & 0xfffff;
+
+            d->unlock_state = 0;
+        }
+    }
+}
+
+static const MemoryRegionOps i6300esb_ops = {
+    .old_mmio = {
+        .read = {
+            i6300esb_mem_readb,
+            i6300esb_mem_readw,
+            i6300esb_mem_readl,
+        },
+        .write = {
+            i6300esb_mem_writeb,
+            i6300esb_mem_writew,
+            i6300esb_mem_writel,
+        },
+    },
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_i6300esb = {
+    .name = "i6300esb_wdt",
+    .version_id = sizeof(I6300State),
+    .minimum_version_id = sizeof(I6300State),
+    .minimum_version_id_old = sizeof(I6300State),
+    .fields      = (VMStateField []) {
+        VMSTATE_PCI_DEVICE(dev, I6300State),
+        VMSTATE_INT32(reboot_enabled, I6300State),
+        VMSTATE_INT32(clock_scale, I6300State),
+        VMSTATE_INT32(int_type, I6300State),
+        VMSTATE_INT32(free_run, I6300State),
+        VMSTATE_INT32(locked, I6300State),
+        VMSTATE_INT32(enabled, I6300State),
+        VMSTATE_TIMER(timer, I6300State),
+        VMSTATE_UINT32(timer1_preload, I6300State),
+        VMSTATE_UINT32(timer2_preload, I6300State),
+        VMSTATE_INT32(stage, I6300State),
+        VMSTATE_INT32(unlock_state, I6300State),
+        VMSTATE_INT32(previous_reboot_flag, I6300State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static int i6300esb_init(PCIDevice *dev)
+{
+    I6300State *d = DO_UPCAST(I6300State, dev, dev);
+
+    i6300esb_debug("I6300State = %p\n", d);
+
+    d->timer = qemu_new_timer_ns(vm_clock, i6300esb_timer_expired, d);
+    d->previous_reboot_flag = 0;
+
+    memory_region_init_io(&d->io_mem, &i6300esb_ops, d, "i6300esb", 0x10);
+    pci_register_bar(&d->dev, 0, 0, &d->io_mem);
+    /* qemu_register_coalesced_mmio (addr, 0x10); ? */
+
+    return 0;
+}
+
+static void i6300esb_exit(PCIDevice *dev)
+{
+    I6300State *d = DO_UPCAST(I6300State, dev, dev);
+
+    memory_region_destroy(&d->io_mem);
+}
+
+static WatchdogTimerModel model = {
+    .wdt_name = "i6300esb",
+    .wdt_description = "Intel 6300ESB",
+};
+
+static void i6300esb_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->config_read = i6300esb_config_read;
+    k->config_write = i6300esb_config_write;
+    k->init = i6300esb_init;
+    k->exit = i6300esb_exit;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_ESB_9;
+    k->class_id = PCI_CLASS_SYSTEM_OTHER;
+    dc->reset = i6300esb_reset;
+    dc->vmsd = &vmstate_i6300esb;
+}
+
+static const TypeInfo i6300esb_info = {
+    .name          = "i6300esb",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(I6300State),
+    .class_init    = i6300esb_class_init,
+};
+
+static void i6300esb_register_types(void)
+{
+    watchdog_add_model(&model);
+    type_register_static(&i6300esb_info);
+}
+
+type_init(i6300esb_register_types)
diff --git a/hw/wdt_i6300esb.c b/hw/wdt_i6300esb.c
deleted file mode 100644 (file)
index 1407fba..0000000
+++ /dev/null
@@ -1,455 +0,0 @@
-/*
- * Virtual hardware watchdog.
- *
- * Copyright (C) 2009 Red Hat Inc.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- * By Richard W.M. Jones (rjones@redhat.com).
- */
-
-#include <inttypes.h>
-
-#include "qemu-common.h"
-#include "qemu/timer.h"
-#include "sysemu/watchdog.h"
-#include "hw/hw.h"
-#include "hw/pci/pci.h"
-
-/*#define I6300ESB_DEBUG 1*/
-
-#ifdef I6300ESB_DEBUG
-#define i6300esb_debug(fs,...) \
-    fprintf(stderr,"i6300esb: %s: "fs,__func__,##__VA_ARGS__)
-#else
-#define i6300esb_debug(fs,...)
-#endif
-
-/* PCI configuration registers */
-#define ESB_CONFIG_REG  0x60            /* Config register                   */
-#define ESB_LOCK_REG    0x68            /* WDT lock register                 */
-
-/* Memory mapped registers (offset from base address) */
-#define ESB_TIMER1_REG  0x00            /* Timer1 value after each reset     */
-#define ESB_TIMER2_REG  0x04            /* Timer2 value after each reset     */
-#define ESB_GINTSR_REG  0x08            /* General Interrupt Status Register */
-#define ESB_RELOAD_REG  0x0c            /* Reload register                   */
-
-/* Lock register bits */
-#define ESB_WDT_FUNC    (0x01 << 2)   /* Watchdog functionality            */
-#define ESB_WDT_ENABLE  (0x01 << 1)   /* Enable WDT                        */
-#define ESB_WDT_LOCK    (0x01 << 0)   /* Lock (nowayout)                   */
-
-/* Config register bits */
-#define ESB_WDT_REBOOT  (0x01 << 5)   /* Enable reboot on timeout          */
-#define ESB_WDT_FREQ    (0x01 << 2)   /* Decrement frequency               */
-#define ESB_WDT_INTTYPE (0x11 << 0)   /* Interrupt type on timer1 timeout  */
-
-/* Reload register bits */
-#define ESB_WDT_RELOAD  (0x01 << 8)    /* prevent timeout                   */
-
-/* Magic constants */
-#define ESB_UNLOCK1     0x80            /* Step 1 to unlock reset registers  */
-#define ESB_UNLOCK2     0x86            /* Step 2 to unlock reset registers  */
-
-/* Device state. */
-struct I6300State {
-    PCIDevice dev;
-    MemoryRegion io_mem;
-
-    int reboot_enabled;         /* "Reboot" on timer expiry.  The real action
-                                 * performed depends on the -watchdog-action
-                                 * param passed on QEMU command line.
-                                 */
-    int clock_scale;            /* Clock scale. */
-#define CLOCK_SCALE_1KHZ 0
-#define CLOCK_SCALE_1MHZ 1
-
-    int int_type;               /* Interrupt type generated. */
-#define INT_TYPE_IRQ 0          /* APIC 1, INT 10 */
-#define INT_TYPE_SMI 2
-#define INT_TYPE_DISABLED 3
-
-    int free_run;               /* If true, reload timer on expiry. */
-    int locked;                 /* If true, enabled field cannot be changed. */
-    int enabled;                /* If true, watchdog is enabled. */
-
-    QEMUTimer *timer;           /* The actual watchdog timer. */
-
-    uint32_t timer1_preload;    /* Values preloaded into timer1, timer2. */
-    uint32_t timer2_preload;
-    int stage;                  /* Stage (1 or 2). */
-
-    int unlock_state;           /* Guest writes 0x80, 0x86 to unlock the
-                                 * registers, and we transition through
-                                 * states 0 -> 1 -> 2 when this happens.
-                                 */
-
-    int previous_reboot_flag;   /* If the watchdog caused the previous
-                                 * reboot, this flag will be set.
-                                 */
-};
-
-typedef struct I6300State I6300State;
-
-/* This function is called when the watchdog has either been enabled
- * (hence it starts counting down) or has been keep-alived.
- */
-static void i6300esb_restart_timer(I6300State *d, int stage)
-{
-    int64_t timeout;
-
-    if (!d->enabled)
-        return;
-
-    d->stage = stage;
-
-    if (d->stage <= 1)
-        timeout = d->timer1_preload;
-    else
-        timeout = d->timer2_preload;
-
-    if (d->clock_scale == CLOCK_SCALE_1KHZ)
-        timeout <<= 15;
-    else
-        timeout <<= 5;
-
-    /* Get the timeout in units of ticks_per_sec. */
-    timeout = get_ticks_per_sec() * timeout / 33000000;
-
-    i6300esb_debug("stage %d, timeout %" PRIi64 "\n", d->stage, timeout);
-
-    qemu_mod_timer(d->timer, qemu_get_clock_ns(vm_clock) + timeout);
-}
-
-/* This is called when the guest disables the watchdog. */
-static void i6300esb_disable_timer(I6300State *d)
-{
-    i6300esb_debug("timer disabled\n");
-
-    qemu_del_timer(d->timer);
-}
-
-static void i6300esb_reset(DeviceState *dev)
-{
-    PCIDevice *pdev = PCI_DEVICE(dev);
-    I6300State *d = DO_UPCAST(I6300State, dev, pdev);
-
-    i6300esb_debug("I6300State = %p\n", d);
-
-    i6300esb_disable_timer(d);
-
-    /* NB: Don't change d->previous_reboot_flag in this function. */
-
-    d->reboot_enabled = 1;
-    d->clock_scale = CLOCK_SCALE_1KHZ;
-    d->int_type = INT_TYPE_IRQ;
-    d->free_run = 0;
-    d->locked = 0;
-    d->enabled = 0;
-    d->timer1_preload = 0xfffff;
-    d->timer2_preload = 0xfffff;
-    d->stage = 1;
-    d->unlock_state = 0;
-}
-
-/* This function is called when the watchdog expires.  Note that
- * the hardware has two timers, and so expiry happens in two stages.
- * If d->stage == 1 then we perform the first stage action (usually,
- * sending an interrupt) and then restart the timer again for the
- * second stage.  If the second stage expires then the watchdog
- * really has run out.
- */
-static void i6300esb_timer_expired(void *vp)
-{
-    I6300State *d = vp;
-
-    i6300esb_debug("stage %d\n", d->stage);
-
-    if (d->stage == 1) {
-        /* What to do at the end of stage 1? */
-        switch (d->int_type) {
-        case INT_TYPE_IRQ:
-            fprintf(stderr, "i6300esb_timer_expired: I would send APIC 1 INT 10 here if I knew how (XXX)\n");
-            break;
-        case INT_TYPE_SMI:
-            fprintf(stderr, "i6300esb_timer_expired: I would send SMI here if I knew how (XXX)\n");
-            break;
-        }
-
-        /* Start the second stage. */
-        i6300esb_restart_timer(d, 2);
-    } else {
-        /* Second stage expired, reboot for real. */
-        if (d->reboot_enabled) {
-            d->previous_reboot_flag = 1;
-            watchdog_perform_action(); /* This reboots, exits, etc */
-            i6300esb_reset(&d->dev.qdev);
-        }
-
-        /* In "free running mode" we start stage 1 again. */
-        if (d->free_run)
-            i6300esb_restart_timer(d, 1);
-    }
-}
-
-static void i6300esb_config_write(PCIDevice *dev, uint32_t addr,
-                                  uint32_t data, int len)
-{
-    I6300State *d = DO_UPCAST(I6300State, dev, dev);
-    int old;
-
-    i6300esb_debug("addr = %x, data = %x, len = %d\n", addr, data, len);
-
-    if (addr == ESB_CONFIG_REG && len == 2) {
-        d->reboot_enabled = (data & ESB_WDT_REBOOT) == 0;
-        d->clock_scale =
-            (data & ESB_WDT_FREQ) != 0 ? CLOCK_SCALE_1MHZ : CLOCK_SCALE_1KHZ;
-        d->int_type = (data & ESB_WDT_INTTYPE);
-    } else if (addr == ESB_LOCK_REG && len == 1) {
-        if (!d->locked) {
-            d->locked = (data & ESB_WDT_LOCK) != 0;
-            d->free_run = (data & ESB_WDT_FUNC) != 0;
-            old = d->enabled;
-            d->enabled = (data & ESB_WDT_ENABLE) != 0;
-            if (!old && d->enabled) /* Enabled transitioned from 0 -> 1 */
-                i6300esb_restart_timer(d, 1);
-            else if (!d->enabled)
-                i6300esb_disable_timer(d);
-        }
-    } else {
-        pci_default_write_config(dev, addr, data, len);
-    }
-}
-
-static uint32_t i6300esb_config_read(PCIDevice *dev, uint32_t addr, int len)
-{
-    I6300State *d = DO_UPCAST(I6300State, dev, dev);
-    uint32_t data;
-
-    i6300esb_debug ("addr = %x, len = %d\n", addr, len);
-
-    if (addr == ESB_CONFIG_REG && len == 2) {
-        data =
-            (d->reboot_enabled ? 0 : ESB_WDT_REBOOT) |
-            (d->clock_scale == CLOCK_SCALE_1MHZ ? ESB_WDT_FREQ : 0) |
-            d->int_type;
-        return data;
-    } else if (addr == ESB_LOCK_REG && len == 1) {
-        data =
-            (d->free_run ? ESB_WDT_FUNC : 0) |
-            (d->locked ? ESB_WDT_LOCK : 0) |
-            (d->enabled ? ESB_WDT_ENABLE : 0);
-        return data;
-    } else {
-        return pci_default_read_config(dev, addr, len);
-    }
-}
-
-static uint32_t i6300esb_mem_readb(void *vp, hwaddr addr)
-{
-    i6300esb_debug ("addr = %x\n", (int) addr);
-
-    return 0;
-}
-
-static uint32_t i6300esb_mem_readw(void *vp, hwaddr addr)
-{
-    uint32_t data = 0;
-    I6300State *d = vp;
-
-    i6300esb_debug("addr = %x\n", (int) addr);
-
-    if (addr == 0xc) {
-        /* The previous reboot flag is really bit 9, but there is
-         * a bug in the Linux driver where it thinks it's bit 12.
-         * Set both.
-         */
-        data = d->previous_reboot_flag ? 0x1200 : 0;
-    }
-
-    return data;
-}
-
-static uint32_t i6300esb_mem_readl(void *vp, hwaddr addr)
-{
-    i6300esb_debug("addr = %x\n", (int) addr);
-
-    return 0;
-}
-
-static void i6300esb_mem_writeb(void *vp, hwaddr addr, uint32_t val)
-{
-    I6300State *d = vp;
-
-    i6300esb_debug("addr = %x, val = %x\n", (int) addr, val);
-
-    if (addr == 0xc && val == 0x80)
-        d->unlock_state = 1;
-    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
-        d->unlock_state = 2;
-}
-
-static void i6300esb_mem_writew(void *vp, hwaddr addr, uint32_t val)
-{
-    I6300State *d = vp;
-
-    i6300esb_debug("addr = %x, val = %x\n", (int) addr, val);
-
-    if (addr == 0xc && val == 0x80)
-        d->unlock_state = 1;
-    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
-        d->unlock_state = 2;
-    else {
-        if (d->unlock_state == 2) {
-            if (addr == 0xc) {
-                if ((val & 0x100) != 0)
-                    /* This is the "ping" from the userspace watchdog in
-                     * the guest ...
-                     */
-                    i6300esb_restart_timer(d, 1);
-
-                /* Setting bit 9 resets the previous reboot flag.
-                 * There's a bug in the Linux driver where it sets
-                 * bit 12 instead.
-                 */
-                if ((val & 0x200) != 0 || (val & 0x1000) != 0) {
-                    d->previous_reboot_flag = 0;
-                }
-            }
-
-            d->unlock_state = 0;
-        }
-    }
-}
-
-static void i6300esb_mem_writel(void *vp, hwaddr addr, uint32_t val)
-{
-    I6300State *d = vp;
-
-    i6300esb_debug ("addr = %x, val = %x\n", (int) addr, val);
-
-    if (addr == 0xc && val == 0x80)
-        d->unlock_state = 1;
-    else if (addr == 0xc && val == 0x86 && d->unlock_state == 1)
-        d->unlock_state = 2;
-    else {
-        if (d->unlock_state == 2) {
-            if (addr == 0)
-                d->timer1_preload = val & 0xfffff;
-            else if (addr == 4)
-                d->timer2_preload = val & 0xfffff;
-
-            d->unlock_state = 0;
-        }
-    }
-}
-
-static const MemoryRegionOps i6300esb_ops = {
-    .old_mmio = {
-        .read = {
-            i6300esb_mem_readb,
-            i6300esb_mem_readw,
-            i6300esb_mem_readl,
-        },
-        .write = {
-            i6300esb_mem_writeb,
-            i6300esb_mem_writew,
-            i6300esb_mem_writel,
-        },
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static const VMStateDescription vmstate_i6300esb = {
-    .name = "i6300esb_wdt",
-    .version_id = sizeof(I6300State),
-    .minimum_version_id = sizeof(I6300State),
-    .minimum_version_id_old = sizeof(I6300State),
-    .fields      = (VMStateField []) {
-        VMSTATE_PCI_DEVICE(dev, I6300State),
-        VMSTATE_INT32(reboot_enabled, I6300State),
-        VMSTATE_INT32(clock_scale, I6300State),
-        VMSTATE_INT32(int_type, I6300State),
-        VMSTATE_INT32(free_run, I6300State),
-        VMSTATE_INT32(locked, I6300State),
-        VMSTATE_INT32(enabled, I6300State),
-        VMSTATE_TIMER(timer, I6300State),
-        VMSTATE_UINT32(timer1_preload, I6300State),
-        VMSTATE_UINT32(timer2_preload, I6300State),
-        VMSTATE_INT32(stage, I6300State),
-        VMSTATE_INT32(unlock_state, I6300State),
-        VMSTATE_INT32(previous_reboot_flag, I6300State),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int i6300esb_init(PCIDevice *dev)
-{
-    I6300State *d = DO_UPCAST(I6300State, dev, dev);
-
-    i6300esb_debug("I6300State = %p\n", d);
-
-    d->timer = qemu_new_timer_ns(vm_clock, i6300esb_timer_expired, d);
-    d->previous_reboot_flag = 0;
-
-    memory_region_init_io(&d->io_mem, &i6300esb_ops, d, "i6300esb", 0x10);
-    pci_register_bar(&d->dev, 0, 0, &d->io_mem);
-    /* qemu_register_coalesced_mmio (addr, 0x10); ? */
-
-    return 0;
-}
-
-static void i6300esb_exit(PCIDevice *dev)
-{
-    I6300State *d = DO_UPCAST(I6300State, dev, dev);
-
-    memory_region_destroy(&d->io_mem);
-}
-
-static WatchdogTimerModel model = {
-    .wdt_name = "i6300esb",
-    .wdt_description = "Intel 6300ESB",
-};
-
-static void i6300esb_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->config_read = i6300esb_config_read;
-    k->config_write = i6300esb_config_write;
-    k->init = i6300esb_init;
-    k->exit = i6300esb_exit;
-    k->vendor_id = PCI_VENDOR_ID_INTEL;
-    k->device_id = PCI_DEVICE_ID_INTEL_ESB_9;
-    k->class_id = PCI_CLASS_SYSTEM_OTHER;
-    dc->reset = i6300esb_reset;
-    dc->vmsd = &vmstate_i6300esb;
-}
-
-static const TypeInfo i6300esb_info = {
-    .name          = "i6300esb",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(I6300State),
-    .class_init    = i6300esb_class_init,
-};
-
-static void i6300esb_register_types(void)
-{
-    watchdog_add_model(&model);
-    type_register_static(&i6300esb_info);
-}
-
-type_init(i6300esb_register_types)
diff --git a/hw/wm8750.c b/hw/wm8750.c
deleted file mode 100644 (file)
index 6b5a349..0000000
+++ /dev/null
@@ -1,716 +0,0 @@
-/*
- * WM8750 audio CODEC.
- *
- * Copyright (c) 2006 Openedhand Ltd.
- * Written by Andrzej Zaborowski <balrog@zabor.org>
- *
- * This file is licensed under GNU GPL.
- */
-
-#include "hw/hw.h"
-#include "hw/i2c/i2c.h"
-#include "audio/audio.h"
-
-#define IN_PORT_N      3
-#define OUT_PORT_N     3
-
-#define CODEC          "wm8750"
-
-typedef struct {
-    int adc;
-    int adc_hz;
-    int dac;
-    int dac_hz;
-} WMRate;
-
-typedef struct {
-    I2CSlave i2c;
-    uint8_t i2c_data[2];
-    int i2c_len;
-    QEMUSoundCard card;
-    SWVoiceIn *adc_voice[IN_PORT_N];
-    SWVoiceOut *dac_voice[OUT_PORT_N];
-    int enable;
-    void (*data_req)(void *, int, int);
-    void *opaque;
-    uint8_t data_in[4096];
-    uint8_t data_out[4096];
-    int idx_in, req_in;
-    int idx_out, req_out;
-
-    SWVoiceOut **out[2];
-    uint8_t outvol[7], outmute[2];
-    SWVoiceIn **in[2];
-    uint8_t invol[4], inmute[2];
-
-    uint8_t diff[2], pol, ds, monomix[2], alc, mute;
-    uint8_t path[4], mpath[2], power, format;
-    const WMRate *rate;
-    uint8_t rate_vmstate;
-    int adc_hz, dac_hz, ext_adc_hz, ext_dac_hz, master;
-} WM8750State;
-
-/* pow(10.0, -i / 20.0) * 255, i = 0..42 */
-static const uint8_t wm8750_vol_db_table[] = {
-    255, 227, 203, 181, 161, 143, 128, 114, 102, 90, 81, 72, 64, 57, 51, 45,
-    40, 36, 32, 29, 26, 23, 20, 18, 16, 14, 13, 11, 10, 9, 8, 7, 6, 6, 5, 5,
-    4, 4, 3, 3, 3, 2, 2
-};
-
-#define WM8750_OUTVOL_TRANSFORM(x)     wm8750_vol_db_table[(0x7f - x) / 3]
-#define WM8750_INVOL_TRANSFORM(x)      (x << 2)
-
-static inline void wm8750_in_load(WM8750State *s)
-{
-    if (s->idx_in + s->req_in <= sizeof(s->data_in))
-        return;
-    s->idx_in = audio_MAX(0, (int) sizeof(s->data_in) - s->req_in);
-    AUD_read(*s->in[0], s->data_in + s->idx_in,
-             sizeof(s->data_in) - s->idx_in);
-}
-
-static inline void wm8750_out_flush(WM8750State *s)
-{
-    int sent = 0;
-    while (sent < s->idx_out)
-        sent += AUD_write(*s->out[0], s->data_out + sent, s->idx_out - sent)
-                ?: s->idx_out;
-    s->idx_out = 0;
-}
-
-static void wm8750_audio_in_cb(void *opaque, int avail_b)
-{
-    WM8750State *s = (WM8750State *) opaque;
-    s->req_in = avail_b;
-    s->data_req(s->opaque, s->req_out >> 2, avail_b >> 2);
-}
-
-static void wm8750_audio_out_cb(void *opaque, int free_b)
-{
-    WM8750State *s = (WM8750State *) opaque;
-
-    if (s->idx_out >= free_b) {
-        s->idx_out = free_b;
-        s->req_out = 0;
-        wm8750_out_flush(s);
-    } else
-        s->req_out = free_b - s->idx_out;
-    s->data_req(s->opaque, s->req_out >> 2, s->req_in >> 2);
-}
-
-static const WMRate wm_rate_table[] = {
-    {  256, 48000,  256, 48000 },      /* SR: 00000 */
-    {  384, 48000,  384, 48000 },      /* SR: 00001 */
-    {  256, 48000, 1536,  8000 },      /* SR: 00010 */
-    {  384, 48000, 2304,  8000 },      /* SR: 00011 */
-    { 1536,  8000,  256, 48000 },      /* SR: 00100 */
-    { 2304,  8000,  384, 48000 },      /* SR: 00101 */
-    { 1536,  8000, 1536,  8000 },      /* SR: 00110 */
-    { 2304,  8000, 2304,  8000 },      /* SR: 00111 */
-    { 1024, 12000, 1024, 12000 },      /* SR: 01000 */
-    { 1526, 12000, 1536, 12000 },      /* SR: 01001 */
-    {  768, 16000,  768, 16000 },      /* SR: 01010 */
-    { 1152, 16000, 1152, 16000 },      /* SR: 01011 */
-    {  384, 32000,  384, 32000 },      /* SR: 01100 */
-    {  576, 32000,  576, 32000 },      /* SR: 01101 */
-    {  128, 96000,  128, 96000 },      /* SR: 01110 */
-    {  192, 96000,  192, 96000 },      /* SR: 01111 */
-    {  256, 44100,  256, 44100 },      /* SR: 10000 */
-    {  384, 44100,  384, 44100 },      /* SR: 10001 */
-    {  256, 44100, 1408,  8018 },      /* SR: 10010 */
-    {  384, 44100, 2112,  8018 },      /* SR: 10011 */
-    { 1408,  8018,  256, 44100 },      /* SR: 10100 */
-    { 2112,  8018,  384, 44100 },      /* SR: 10101 */
-    { 1408,  8018, 1408,  8018 },      /* SR: 10110 */
-    { 2112,  8018, 2112,  8018 },      /* SR: 10111 */
-    { 1024, 11025, 1024, 11025 },      /* SR: 11000 */
-    { 1536, 11025, 1536, 11025 },      /* SR: 11001 */
-    {  512, 22050,  512, 22050 },      /* SR: 11010 */
-    {  768, 22050,  768, 22050 },      /* SR: 11011 */
-    {  512, 24000,  512, 24000 },      /* SR: 11100 */
-    {  768, 24000,  768, 24000 },      /* SR: 11101 */
-    {  128, 88200,  128, 88200 },      /* SR: 11110 */
-    {  192, 88200,  192, 88200 },      /* SR: 11111 */
-};
-
-static void wm8750_vol_update(WM8750State *s)
-{
-    /* FIXME: multiply all volumes by s->invol[2], s->invol[3] */
-
-    AUD_set_volume_in(s->adc_voice[0], s->mute,
-                    s->inmute[0] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[0]),
-                    s->inmute[1] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[1]));
-    AUD_set_volume_in(s->adc_voice[1], s->mute,
-                    s->inmute[0] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[0]),
-                    s->inmute[1] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[1]));
-    AUD_set_volume_in(s->adc_voice[2], s->mute,
-                    s->inmute[0] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[0]),
-                    s->inmute[1] ? 0 : WM8750_INVOL_TRANSFORM(s->invol[1]));
-
-    /* FIXME: multiply all volumes by s->outvol[0], s->outvol[1] */
-
-    /* Speaker: LOUT2VOL ROUT2VOL */
-    AUD_set_volume_out(s->dac_voice[0], s->mute,
-                    s->outmute[0] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[4]),
-                    s->outmute[1] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[5]));
-
-    /* Headphone: LOUT1VOL ROUT1VOL */
-    AUD_set_volume_out(s->dac_voice[1], s->mute,
-                    s->outmute[0] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[2]),
-                    s->outmute[1] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[3]));
-
-    /* MONOOUT: MONOVOL MONOVOL */
-    AUD_set_volume_out(s->dac_voice[2], s->mute,
-                    s->outmute[0] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[6]),
-                    s->outmute[1] ? 0 : WM8750_OUTVOL_TRANSFORM(s->outvol[6]));
-}
-
-static void wm8750_set_format(WM8750State *s)
-{
-    int i;
-    struct audsettings in_fmt;
-    struct audsettings out_fmt;
-
-    wm8750_out_flush(s);
-
-    if (s->in[0] && *s->in[0])
-        AUD_set_active_in(*s->in[0], 0);
-    if (s->out[0] && *s->out[0])
-        AUD_set_active_out(*s->out[0], 0);
-
-    for (i = 0; i < IN_PORT_N; i ++)
-        if (s->adc_voice[i]) {
-            AUD_close_in(&s->card, s->adc_voice[i]);
-            s->adc_voice[i] = NULL;
-        }
-    for (i = 0; i < OUT_PORT_N; i ++)
-        if (s->dac_voice[i]) {
-            AUD_close_out(&s->card, s->dac_voice[i]);
-            s->dac_voice[i] = NULL;
-        }
-
-    if (!s->enable)
-        return;
-
-    /* Setup input */
-    in_fmt.endianness = 0;
-    in_fmt.nchannels = 2;
-    in_fmt.freq = s->adc_hz;
-    in_fmt.fmt = AUD_FMT_S16;
-
-    s->adc_voice[0] = AUD_open_in(&s->card, s->adc_voice[0],
-                    CODEC ".input1", s, wm8750_audio_in_cb, &in_fmt);
-    s->adc_voice[1] = AUD_open_in(&s->card, s->adc_voice[1],
-                    CODEC ".input2", s, wm8750_audio_in_cb, &in_fmt);
-    s->adc_voice[2] = AUD_open_in(&s->card, s->adc_voice[2],
-                    CODEC ".input3", s, wm8750_audio_in_cb, &in_fmt);
-
-    /* Setup output */
-    out_fmt.endianness = 0;
-    out_fmt.nchannels = 2;
-    out_fmt.freq = s->dac_hz;
-    out_fmt.fmt = AUD_FMT_S16;
-
-    s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
-                    CODEC ".speaker", s, wm8750_audio_out_cb, &out_fmt);
-    s->dac_voice[1] = AUD_open_out(&s->card, s->dac_voice[1],
-                    CODEC ".headphone", s, wm8750_audio_out_cb, &out_fmt);
-    /* MONOMIX is also in stereo for simplicity */
-    s->dac_voice[2] = AUD_open_out(&s->card, s->dac_voice[2],
-                    CODEC ".monomix", s, wm8750_audio_out_cb, &out_fmt);
-    /* no sense emulating OUT3 which is a mix of other outputs */
-
-    wm8750_vol_update(s);
-
-    /* We should connect the left and right channels to their
-     * respective inputs/outputs but we have completely no need
-     * for mixing or combining paths to different ports, so we
-     * connect both channels to where the left channel is routed.  */
-    if (s->in[0] && *s->in[0])
-        AUD_set_active_in(*s->in[0], 1);
-    if (s->out[0] && *s->out[0])
-        AUD_set_active_out(*s->out[0], 1);
-}
-
-static void wm8750_clk_update(WM8750State *s, int ext)
-{
-    if (s->master || !s->ext_dac_hz)
-        s->dac_hz = s->rate->dac_hz;
-    else
-        s->dac_hz = s->ext_dac_hz;
-
-    if (s->master || !s->ext_adc_hz)
-        s->adc_hz = s->rate->adc_hz;
-    else
-        s->adc_hz = s->ext_adc_hz;
-
-    if (s->master || (!s->ext_dac_hz && !s->ext_adc_hz)) {
-        if (!ext)
-            wm8750_set_format(s);
-    } else {
-        if (ext)
-            wm8750_set_format(s);
-    }
-}
-
-static void wm8750_reset(I2CSlave *i2c)
-{
-    WM8750State *s = (WM8750State *) i2c;
-    s->rate = &wm_rate_table[0];
-    s->enable = 0;
-    wm8750_clk_update(s, 1);
-    s->diff[0] = 0;
-    s->diff[1] = 0;
-    s->ds = 0;
-    s->alc = 0;
-    s->in[0] = &s->adc_voice[0];
-    s->invol[0] = 0x17;
-    s->invol[1] = 0x17;
-    s->invol[2] = 0xc3;
-    s->invol[3] = 0xc3;
-    s->out[0] = &s->dac_voice[0];
-    s->outvol[0] = 0xff;
-    s->outvol[1] = 0xff;
-    s->outvol[2] = 0x79;
-    s->outvol[3] = 0x79;
-    s->outvol[4] = 0x79;
-    s->outvol[5] = 0x79;
-    s->outvol[6] = 0x79;
-    s->inmute[0] = 0;
-    s->inmute[1] = 0;
-    s->outmute[0] = 0;
-    s->outmute[1] = 0;
-    s->mute = 1;
-    s->path[0] = 0;
-    s->path[1] = 0;
-    s->path[2] = 0;
-    s->path[3] = 0;
-    s->mpath[0] = 0;
-    s->mpath[1] = 0;
-    s->format = 0x0a;
-    s->idx_in = sizeof(s->data_in);
-    s->req_in = 0;
-    s->idx_out = 0;
-    s->req_out = 0;
-    wm8750_vol_update(s);
-    s->i2c_len = 0;
-}
-
-static void wm8750_event(I2CSlave *i2c, enum i2c_event event)
-{
-    WM8750State *s = (WM8750State *) i2c;
-
-    switch (event) {
-    case I2C_START_SEND:
-        s->i2c_len = 0;
-        break;
-    case I2C_FINISH:
-#ifdef VERBOSE
-        if (s->i2c_len < 2)
-            printf("%s: message too short (%i bytes)\n",
-                            __FUNCTION__, s->i2c_len);
-#endif
-        break;
-    default:
-        break;
-    }
-}
-
-#define WM8750_LINVOL  0x00
-#define WM8750_RINVOL  0x01
-#define WM8750_LOUT1V  0x02
-#define WM8750_ROUT1V  0x03
-#define WM8750_ADCDAC  0x05
-#define WM8750_IFACE   0x07
-#define WM8750_SRATE   0x08
-#define WM8750_LDAC    0x0a
-#define WM8750_RDAC    0x0b
-#define WM8750_BASS    0x0c
-#define WM8750_TREBLE  0x0d
-#define WM8750_RESET   0x0f
-#define WM8750_3D      0x10
-#define WM8750_ALC1    0x11
-#define WM8750_ALC2    0x12
-#define WM8750_ALC3    0x13
-#define WM8750_NGATE   0x14
-#define WM8750_LADC    0x15
-#define WM8750_RADC    0x16
-#define WM8750_ADCTL1  0x17
-#define WM8750_ADCTL2  0x18
-#define WM8750_PWR1    0x19
-#define WM8750_PWR2    0x1a
-#define WM8750_ADCTL3  0x1b
-#define WM8750_ADCIN   0x1f
-#define WM8750_LADCIN  0x20
-#define WM8750_RADCIN  0x21
-#define WM8750_LOUTM1  0x22
-#define WM8750_LOUTM2  0x23
-#define WM8750_ROUTM1  0x24
-#define WM8750_ROUTM2  0x25
-#define WM8750_MOUTM1  0x26
-#define WM8750_MOUTM2  0x27
-#define WM8750_LOUT2V  0x28
-#define WM8750_ROUT2V  0x29
-#define WM8750_MOUTV   0x2a
-
-static int wm8750_tx(I2CSlave *i2c, uint8_t data)
-{
-    WM8750State *s = (WM8750State *) i2c;
-    uint8_t cmd;
-    uint16_t value;
-
-    if (s->i2c_len >= 2) {
-#ifdef VERBOSE
-        printf("%s: long message (%i bytes)\n", __func__, s->i2c_len);
-#endif
-        return 1;
-    }
-    s->i2c_data[s->i2c_len ++] = data;
-    if (s->i2c_len != 2)
-        return 0;
-
-    cmd = s->i2c_data[0] >> 1;
-    value = ((s->i2c_data[0] << 8) | s->i2c_data[1]) & 0x1ff;
-
-    switch (cmd) {
-    case WM8750_LADCIN:        /* ADC Signal Path Control (Left) */
-        s->diff[0] = (((value >> 6) & 3) == 3);        /* LINSEL */
-        if (s->diff[0])
-            s->in[0] = &s->adc_voice[0 + s->ds * 1];
-        else
-            s->in[0] = &s->adc_voice[((value >> 6) & 3) * 1 + 0];
-        break;
-
-    case WM8750_RADCIN:        /* ADC Signal Path Control (Right) */
-        s->diff[1] = (((value >> 6) & 3) == 3);        /* RINSEL */
-        if (s->diff[1])
-            s->in[1] = &s->adc_voice[0 + s->ds * 1];
-        else
-            s->in[1] = &s->adc_voice[((value >> 6) & 3) * 1 + 0];
-        break;
-
-    case WM8750_ADCIN: /* ADC Input Mode */
-        s->ds = (value >> 8) & 1;      /* DS */
-        if (s->diff[0])
-            s->in[0] = &s->adc_voice[0 + s->ds * 1];
-        if (s->diff[1])
-            s->in[1] = &s->adc_voice[0 + s->ds * 1];
-        s->monomix[0] = (value >> 6) & 3;      /* MONOMIX */
-        break;
-
-    case WM8750_ADCTL1:        /* Additional Control (1) */
-        s->monomix[1] = (value >> 1) & 1;      /* DMONOMIX */
-        break;
-
-    case WM8750_PWR1:  /* Power Management (1) */
-        s->enable = ((value >> 6) & 7) == 3;   /* VMIDSEL, VREF */
-        wm8750_set_format(s);
-        break;
-
-    case WM8750_LINVOL:        /* Left Channel PGA */
-        s->invol[0] = value & 0x3f;            /* LINVOL */
-        s->inmute[0] = (value >> 7) & 1;       /* LINMUTE */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_RINVOL:        /* Right Channel PGA */
-        s->invol[1] = value & 0x3f;            /* RINVOL */
-        s->inmute[1] = (value >> 7) & 1;       /* RINMUTE */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_ADCDAC:        /* ADC and DAC Control */
-        s->pol = (value >> 5) & 3;             /* ADCPOL */
-        s->mute = (value >> 3) & 1;            /* DACMU */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_ADCTL3:        /* Additional Control (3) */
-        break;
-
-    case WM8750_LADC:  /* Left ADC Digital Volume */
-        s->invol[2] = value & 0xff;            /* LADCVOL */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_RADC:  /* Right ADC Digital Volume */
-        s->invol[3] = value & 0xff;            /* RADCVOL */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_ALC1:  /* ALC Control (1) */
-        s->alc = (value >> 7) & 3;             /* ALCSEL */
-        break;
-
-    case WM8750_NGATE: /* Noise Gate Control */
-    case WM8750_3D:    /* 3D enhance */
-        break;
-
-    case WM8750_LDAC:  /* Left Channel Digital Volume */
-        s->outvol[0] = value & 0xff;           /* LDACVOL */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_RDAC:  /* Right Channel Digital Volume */
-        s->outvol[1] = value & 0xff;           /* RDACVOL */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_BASS:  /* Bass Control */
-        break;
-
-    case WM8750_LOUTM1:        /* Left Mixer Control (1) */
-        s->path[0] = (value >> 8) & 1;         /* LD2LO */
-        /* TODO: mute/unmute respective paths */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_LOUTM2:        /* Left Mixer Control (2) */
-        s->path[1] = (value >> 8) & 1;         /* RD2LO */
-        /* TODO: mute/unmute respective paths */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_ROUTM1:        /* Right Mixer Control (1) */
-        s->path[2] = (value >> 8) & 1;         /* LD2RO */
-        /* TODO: mute/unmute respective paths */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_ROUTM2:        /* Right Mixer Control (2) */
-        s->path[3] = (value >> 8) & 1;         /* RD2RO */
-        /* TODO: mute/unmute respective paths */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_MOUTM1:        /* Mono Mixer Control (1) */
-        s->mpath[0] = (value >> 8) & 1;                /* LD2MO */
-        /* TODO: mute/unmute respective paths */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_MOUTM2:        /* Mono Mixer Control (2) */
-        s->mpath[1] = (value >> 8) & 1;                /* RD2MO */
-        /* TODO: mute/unmute respective paths */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_LOUT1V:        /* LOUT1 Volume */
-        s->outvol[2] = value & 0x7f;           /* LOUT1VOL */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_LOUT2V:        /* LOUT2 Volume */
-        s->outvol[4] = value & 0x7f;           /* LOUT2VOL */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_ROUT1V:        /* ROUT1 Volume */
-        s->outvol[3] = value & 0x7f;           /* ROUT1VOL */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_ROUT2V:        /* ROUT2 Volume */
-        s->outvol[5] = value & 0x7f;           /* ROUT2VOL */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_MOUTV: /* MONOOUT Volume */
-        s->outvol[6] = value & 0x7f;           /* MONOOUTVOL */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_ADCTL2:        /* Additional Control (2) */
-        break;
-
-    case WM8750_PWR2:  /* Power Management (2) */
-        s->power = value & 0x7e;
-        /* TODO: mute/unmute respective paths */
-        wm8750_vol_update(s);
-        break;
-
-    case WM8750_IFACE: /* Digital Audio Interface Format */
-        s->format = value;
-        s->master = (value >> 6) & 1;                  /* MS */
-        wm8750_clk_update(s, s->master);
-        break;
-
-    case WM8750_SRATE: /* Clocking and Sample Rate Control */
-        s->rate = &wm_rate_table[(value >> 1) & 0x1f];
-        wm8750_clk_update(s, 0);
-        break;
-
-    case WM8750_RESET: /* Reset */
-        wm8750_reset(&s->i2c);
-        break;
-
-#ifdef VERBOSE
-    default:
-        printf("%s: unknown register %02x\n", __FUNCTION__, cmd);
-#endif
-    }
-
-    return 0;
-}
-
-static int wm8750_rx(I2CSlave *i2c)
-{
-    return 0x00;
-}
-
-static void wm8750_pre_save(void *opaque)
-{
-    WM8750State *s = opaque;
-
-    s->rate_vmstate = s->rate - wm_rate_table;
-}
-
-static int wm8750_post_load(void *opaque, int version_id)
-{
-    WM8750State *s = opaque;
-
-    s->rate = &wm_rate_table[s->rate_vmstate & 0x1f];
-    return 0;
-}
-
-static const VMStateDescription vmstate_wm8750 = {
-    .name = CODEC,
-    .version_id = 0,
-    .minimum_version_id = 0,
-    .minimum_version_id_old = 0,
-    .pre_save = wm8750_pre_save,
-    .post_load = wm8750_post_load,
-    .fields      = (VMStateField []) {
-        VMSTATE_UINT8_ARRAY(i2c_data, WM8750State, 2),
-        VMSTATE_INT32(i2c_len, WM8750State),
-        VMSTATE_INT32(enable, WM8750State),
-        VMSTATE_INT32(idx_in, WM8750State),
-        VMSTATE_INT32(req_in, WM8750State),
-        VMSTATE_INT32(idx_out, WM8750State),
-        VMSTATE_INT32(req_out, WM8750State),
-        VMSTATE_UINT8_ARRAY(outvol, WM8750State, 7),
-        VMSTATE_UINT8_ARRAY(outmute, WM8750State, 2),
-        VMSTATE_UINT8_ARRAY(invol, WM8750State, 4),
-        VMSTATE_UINT8_ARRAY(inmute, WM8750State, 2),
-        VMSTATE_UINT8_ARRAY(diff, WM8750State, 2),
-        VMSTATE_UINT8(pol, WM8750State),
-        VMSTATE_UINT8(ds, WM8750State),
-        VMSTATE_UINT8_ARRAY(monomix, WM8750State, 2),
-        VMSTATE_UINT8(alc, WM8750State),
-        VMSTATE_UINT8(mute, WM8750State),
-        VMSTATE_UINT8_ARRAY(path, WM8750State, 4),
-        VMSTATE_UINT8_ARRAY(mpath, WM8750State, 2),
-        VMSTATE_UINT8(format, WM8750State),
-        VMSTATE_UINT8(power, WM8750State),
-        VMSTATE_UINT8(rate_vmstate, WM8750State),
-        VMSTATE_I2C_SLAVE(i2c, WM8750State),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static int wm8750_init(I2CSlave *i2c)
-{
-    WM8750State *s = FROM_I2C_SLAVE(WM8750State, i2c);
-
-    AUD_register_card(CODEC, &s->card);
-    wm8750_reset(&s->i2c);
-
-    return 0;
-}
-
-#if 0
-static void wm8750_fini(I2CSlave *i2c)
-{
-    WM8750State *s = (WM8750State *) i2c;
-    wm8750_reset(&s->i2c);
-    AUD_remove_card(&s->card);
-    g_free(s);
-}
-#endif
-
-void wm8750_data_req_set(DeviceState *dev,
-                void (*data_req)(void *, int, int), void *opaque)
-{
-    WM8750State *s = FROM_I2C_SLAVE(WM8750State, I2C_SLAVE(dev));
-    s->data_req = data_req;
-    s->opaque = opaque;
-}
-
-void wm8750_dac_dat(void *opaque, uint32_t sample)
-{
-    WM8750State *s = (WM8750State *) opaque;
-
-    *(uint32_t *) &s->data_out[s->idx_out] = sample;
-    s->req_out -= 4;
-    s->idx_out += 4;
-    if (s->idx_out >= sizeof(s->data_out) || s->req_out <= 0)
-        wm8750_out_flush(s);
-}
-
-void *wm8750_dac_buffer(void *opaque, int samples)
-{
-    WM8750State *s = (WM8750State *) opaque;
-    /* XXX: Should check if there are <i>samples</i> free samples available */
-    void *ret = s->data_out + s->idx_out;
-
-    s->idx_out += samples << 2;
-    s->req_out -= samples << 2;
-    return ret;
-}
-
-void wm8750_dac_commit(void *opaque)
-{
-    WM8750State *s = (WM8750State *) opaque;
-
-    wm8750_out_flush(s);
-}
-
-uint32_t wm8750_adc_dat(void *opaque)
-{
-    WM8750State *s = (WM8750State *) opaque;
-    uint32_t *data;
-
-    if (s->idx_in >= sizeof(s->data_in))
-        wm8750_in_load(s);
-
-    data = (uint32_t *) &s->data_in[s->idx_in];
-    s->req_in -= 4;
-    s->idx_in += 4;
-    return *data;
-}
-
-void wm8750_set_bclk_in(void *opaque, int new_hz)
-{
-    WM8750State *s = (WM8750State *) opaque;
-
-    s->ext_adc_hz = new_hz;
-    s->ext_dac_hz = new_hz;
-    wm8750_clk_update(s, 1);
-}
-
-static void wm8750_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
-
-    sc->init = wm8750_init;
-    sc->event = wm8750_event;
-    sc->recv = wm8750_rx;
-    sc->send = wm8750_tx;
-    dc->vmsd = &vmstate_wm8750;
-}
-
-static const TypeInfo wm8750_info = {
-    .name          = "wm8750",
-    .parent        = TYPE_I2C_SLAVE,
-    .instance_size = sizeof(WM8750State),
-    .class_init    = wm8750_class_init,
-};
-
-static void wm8750_register_types(void)
-{
-    type_register_static(&wm8750_info);
-}
-
-type_init(wm8750_register_types)
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4b209a7b9a56e7fd34f7e11dc3e5ac7319497649 100644 (file)
@@ -0,0 +1,2 @@
+# xen backend driver support
+common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o
diff --git a/hw/xen/xen_backend.c b/hw/xen/xen_backend.c
new file mode 100644 (file)
index 0000000..2a8c9f5
--- /dev/null
@@ -0,0 +1,800 @@
+/*
+ *  xen backend driver infrastructure
+ *  (c) 2008 Gerd Hoffmann <kraxel@redhat.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; under version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *  Contributions after 2012-01-13 are licensed under the terms of the
+ *  GNU GPL, version 2 or (at your option) any later version.
+ */
+
+/*
+ * TODO: add some xenbus / xenstore concepts overview here.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/signal.h>
+
+#include "hw/hw.h"
+#include "char/char.h"
+#include "qemu/log.h"
+#include "hw/xen/xen_backend.h"
+
+#include <xen/grant_table.h>
+
+/* ------------------------------------------------------------- */
+
+/* public */
+XenXC xen_xc = XC_HANDLER_INITIAL_VALUE;
+XenGnttab xen_xcg = XC_HANDLER_INITIAL_VALUE;
+struct xs_handle *xenstore = NULL;
+const char *xen_protocol;
+
+/* private */
+static QTAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = QTAILQ_HEAD_INITIALIZER(xendevs);
+static int debug = 0;
+
+/* ------------------------------------------------------------- */
+
+int xenstore_write_str(const char *base, const char *node, const char *val)
+{
+    char abspath[XEN_BUFSIZE];
+
+    snprintf(abspath, sizeof(abspath), "%s/%s", base, node);
+    if (!xs_write(xenstore, 0, abspath, val, strlen(val))) {
+        return -1;
+    }
+    return 0;
+}
+
+char *xenstore_read_str(const char *base, const char *node)
+{
+    char abspath[XEN_BUFSIZE];
+    unsigned int len;
+    char *str, *ret = NULL;
+
+    snprintf(abspath, sizeof(abspath), "%s/%s", base, node);
+    str = xs_read(xenstore, 0, abspath, &len);
+    if (str != NULL) {
+        /* move to qemu-allocated memory to make sure
+         * callers can savely g_free() stuff. */
+        ret = g_strdup(str);
+        free(str);
+    }
+    return ret;
+}
+
+int xenstore_write_int(const char *base, const char *node, int ival)
+{
+    char val[12];
+
+    snprintf(val, sizeof(val), "%d", ival);
+    return xenstore_write_str(base, node, val);
+}
+
+int xenstore_write_int64(const char *base, const char *node, int64_t ival)
+{
+    char val[21];
+
+    snprintf(val, sizeof(val), "%"PRId64, ival);
+    return xenstore_write_str(base, node, val);
+}
+
+int xenstore_read_int(const char *base, const char *node, int *ival)
+{
+    char *val;
+    int rc = -1;
+
+    val = xenstore_read_str(base, node);
+    if (val && 1 == sscanf(val, "%d", ival)) {
+        rc = 0;
+    }
+    g_free(val);
+    return rc;
+}
+
+int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val)
+{
+    return xenstore_write_str(xendev->be, node, val);
+}
+
+int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival)
+{
+    return xenstore_write_int(xendev->be, node, ival);
+}
+
+int xenstore_write_be_int64(struct XenDevice *xendev, const char *node, int64_t ival)
+{
+    return xenstore_write_int64(xendev->be, node, ival);
+}
+
+char *xenstore_read_be_str(struct XenDevice *xendev, const char *node)
+{
+    return xenstore_read_str(xendev->be, node);
+}
+
+int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival)
+{
+    return xenstore_read_int(xendev->be, node, ival);
+}
+
+char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node)
+{
+    return xenstore_read_str(xendev->fe, node);
+}
+
+int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival)
+{
+    return xenstore_read_int(xendev->fe, node, ival);
+}
+
+/* ------------------------------------------------------------- */
+
+const char *xenbus_strstate(enum xenbus_state state)
+{
+    static const char *const name[] = {
+        [ XenbusStateUnknown      ] = "Unknown",
+        [ XenbusStateInitialising ] = "Initialising",
+        [ XenbusStateInitWait     ] = "InitWait",
+        [ XenbusStateInitialised  ] = "Initialised",
+        [ XenbusStateConnected    ] = "Connected",
+        [ XenbusStateClosing      ] = "Closing",
+        [ XenbusStateClosed       ] = "Closed",
+    };
+    return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID";
+}
+
+int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state)
+{
+    int rc;
+
+    rc = xenstore_write_be_int(xendev, "state", state);
+    if (rc < 0) {
+        return rc;
+    }
+    xen_be_printf(xendev, 1, "backend state: %s -> %s\n",
+                  xenbus_strstate(xendev->be_state), xenbus_strstate(state));
+    xendev->be_state = state;
+    return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+struct XenDevice *xen_be_find_xendev(const char *type, int dom, int dev)
+{
+    struct XenDevice *xendev;
+
+    QTAILQ_FOREACH(xendev, &xendevs, next) {
+        if (xendev->dom != dom) {
+            continue;
+        }
+        if (xendev->dev != dev) {
+            continue;
+        }
+        if (strcmp(xendev->type, type) != 0) {
+            continue;
+        }
+        return xendev;
+    }
+    return NULL;
+}
+
+/*
+ * get xen backend device, allocate a new one if it doesn't exist.
+ */
+static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev,
+                                           struct XenDevOps *ops)
+{
+    struct XenDevice *xendev;
+    char *dom0;
+
+    xendev = xen_be_find_xendev(type, dom, dev);
+    if (xendev) {
+        return xendev;
+    }
+
+    /* init new xendev */
+    xendev = g_malloc0(ops->size);
+    xendev->type  = type;
+    xendev->dom   = dom;
+    xendev->dev   = dev;
+    xendev->ops   = ops;
+
+    dom0 = xs_get_domain_path(xenstore, 0);
+    snprintf(xendev->be, sizeof(xendev->be), "%s/backend/%s/%d/%d",
+             dom0, xendev->type, xendev->dom, xendev->dev);
+    snprintf(xendev->name, sizeof(xendev->name), "%s-%d",
+             xendev->type, xendev->dev);
+    free(dom0);
+
+    xendev->debug      = debug;
+    xendev->local_port = -1;
+
+    xendev->evtchndev = xen_xc_evtchn_open(NULL, 0);
+    if (xendev->evtchndev == XC_HANDLER_INITIAL_VALUE) {
+        xen_be_printf(NULL, 0, "can't open evtchn device\n");
+        g_free(xendev);
+        return NULL;
+    }
+    fcntl(xc_evtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC);
+
+    if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) {
+        xendev->gnttabdev = xen_xc_gnttab_open(NULL, 0);
+        if (xendev->gnttabdev == XC_HANDLER_INITIAL_VALUE) {
+            xen_be_printf(NULL, 0, "can't open gnttab device\n");
+            xc_evtchn_close(xendev->evtchndev);
+            g_free(xendev);
+            return NULL;
+        }
+    } else {
+        xendev->gnttabdev = XC_HANDLER_INITIAL_VALUE;
+    }
+
+    QTAILQ_INSERT_TAIL(&xendevs, xendev, next);
+
+    if (xendev->ops->alloc) {
+        xendev->ops->alloc(xendev);
+    }
+
+    return xendev;
+}
+
+/*
+ * release xen backend device.
+ */
+static struct XenDevice *xen_be_del_xendev(int dom, int dev)
+{
+    struct XenDevice *xendev, *xnext;
+
+    /*
+     * This is pretty much like QTAILQ_FOREACH(xendev, &xendevs, next) but
+     * we save the next pointer in xnext because we might free xendev.
+     */
+    xnext = xendevs.tqh_first;
+    while (xnext) {
+        xendev = xnext;
+        xnext = xendev->next.tqe_next;
+
+        if (xendev->dom != dom) {
+            continue;
+        }
+        if (xendev->dev != dev && dev != -1) {
+            continue;
+        }
+
+        if (xendev->ops->free) {
+            xendev->ops->free(xendev);
+        }
+
+        if (xendev->fe) {
+            char token[XEN_BUFSIZE];
+            snprintf(token, sizeof(token), "fe:%p", xendev);
+            xs_unwatch(xenstore, xendev->fe, token);
+            g_free(xendev->fe);
+        }
+
+        if (xendev->evtchndev != XC_HANDLER_INITIAL_VALUE) {
+            xc_evtchn_close(xendev->evtchndev);
+        }
+        if (xendev->gnttabdev != XC_HANDLER_INITIAL_VALUE) {
+            xc_gnttab_close(xendev->gnttabdev);
+        }
+
+        QTAILQ_REMOVE(&xendevs, xendev, next);
+        g_free(xendev);
+    }
+    return NULL;
+}
+
+/*
+ * Sync internal data structures on xenstore updates.
+ * Node specifies the changed field.  node = NULL means
+ * update all fields (used for initialization).
+ */
+static void xen_be_backend_changed(struct XenDevice *xendev, const char *node)
+{
+    if (node == NULL  ||  strcmp(node, "online") == 0) {
+        if (xenstore_read_be_int(xendev, "online", &xendev->online) == -1) {
+            xendev->online = 0;
+        }
+    }
+
+    if (node) {
+        xen_be_printf(xendev, 2, "backend update: %s\n", node);
+        if (xendev->ops->backend_changed) {
+            xendev->ops->backend_changed(xendev, node);
+        }
+    }
+}
+
+static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node)
+{
+    int fe_state;
+
+    if (node == NULL  ||  strcmp(node, "state") == 0) {
+        if (xenstore_read_fe_int(xendev, "state", &fe_state) == -1) {
+            fe_state = XenbusStateUnknown;
+        }
+        if (xendev->fe_state != fe_state) {
+            xen_be_printf(xendev, 1, "frontend state: %s -> %s\n",
+                          xenbus_strstate(xendev->fe_state),
+                          xenbus_strstate(fe_state));
+        }
+        xendev->fe_state = fe_state;
+    }
+    if (node == NULL  ||  strcmp(node, "protocol") == 0) {
+        g_free(xendev->protocol);
+        xendev->protocol = xenstore_read_fe_str(xendev, "protocol");
+        if (xendev->protocol) {
+            xen_be_printf(xendev, 1, "frontend protocol: %s\n", xendev->protocol);
+        }
+    }
+
+    if (node) {
+        xen_be_printf(xendev, 2, "frontend update: %s\n", node);
+        if (xendev->ops->frontend_changed) {
+            xendev->ops->frontend_changed(xendev, node);
+        }
+    }
+}
+
+/* ------------------------------------------------------------- */
+/* Check for possible state transitions and perform them.        */
+
+/*
+ * Initial xendev setup.  Read frontend path, register watch for it.
+ * Should succeed once xend finished setting up the backend device.
+ *
+ * Also sets initial state (-> Initializing) when done.  Which
+ * only affects the xendev->be_state variable as xenbus should
+ * already be put into that state by xend.
+ */
+static int xen_be_try_setup(struct XenDevice *xendev)
+{
+    char token[XEN_BUFSIZE];
+    int be_state;
+
+    if (xenstore_read_be_int(xendev, "state", &be_state) == -1) {
+        xen_be_printf(xendev, 0, "reading backend state failed\n");
+        return -1;
+    }
+
+    if (be_state != XenbusStateInitialising) {
+        xen_be_printf(xendev, 0, "initial backend state is wrong (%s)\n",
+                      xenbus_strstate(be_state));
+        return -1;
+    }
+
+    xendev->fe = xenstore_read_be_str(xendev, "frontend");
+    if (xendev->fe == NULL) {
+        xen_be_printf(xendev, 0, "reading frontend path failed\n");
+        return -1;
+    }
+
+    /* setup frontend watch */
+    snprintf(token, sizeof(token), "fe:%p", xendev);
+    if (!xs_watch(xenstore, xendev->fe, token)) {
+        xen_be_printf(xendev, 0, "watching frontend path (%s) failed\n",
+                      xendev->fe);
+        return -1;
+    }
+    xen_be_set_state(xendev, XenbusStateInitialising);
+
+    xen_be_backend_changed(xendev, NULL);
+    xen_be_frontend_changed(xendev, NULL);
+    return 0;
+}
+
+/*
+ * Try initialize xendev.  Prepare everything the backend can do
+ * without synchronizing with the frontend.  Fakes hotplug-status.  No
+ * hotplug involved here because this is about userspace drivers, thus
+ * there are kernel backend devices which could invoke hotplug.
+ *
+ * Goes to InitWait on success.
+ */
+static int xen_be_try_init(struct XenDevice *xendev)
+{
+    int rc = 0;
+
+    if (!xendev->online) {
+        xen_be_printf(xendev, 1, "not online\n");
+        return -1;
+    }
+
+    if (xendev->ops->init) {
+        rc = xendev->ops->init(xendev);
+    }
+    if (rc != 0) {
+        xen_be_printf(xendev, 1, "init() failed\n");
+        return rc;
+    }
+
+    xenstore_write_be_str(xendev, "hotplug-status", "connected");
+    xen_be_set_state(xendev, XenbusStateInitWait);
+    return 0;
+}
+
+/*
+ * Try to initialise xendev.  Depends on the frontend being ready
+ * for it (shared ring and evtchn info in xenstore, state being
+ * Initialised or Connected).
+ *
+ * Goes to Connected on success.
+ */
+static int xen_be_try_initialise(struct XenDevice *xendev)
+{
+    int rc = 0;
+
+    if (xendev->fe_state != XenbusStateInitialised  &&
+        xendev->fe_state != XenbusStateConnected) {
+        if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) {
+            xen_be_printf(xendev, 2, "frontend not ready, ignoring\n");
+        } else {
+            xen_be_printf(xendev, 2, "frontend not ready (yet)\n");
+            return -1;
+        }
+    }
+
+    if (xendev->ops->initialise) {
+        rc = xendev->ops->initialise(xendev);
+    }
+    if (rc != 0) {
+        xen_be_printf(xendev, 0, "initialise() failed\n");
+        return rc;
+    }
+
+    xen_be_set_state(xendev, XenbusStateConnected);
+    return 0;
+}
+
+/*
+ * Try to let xendev know that it is connected.  Depends on the
+ * frontend being Connected.  Note that this may be called more
+ * than once since the backend state is not modified.
+ */
+static void xen_be_try_connected(struct XenDevice *xendev)
+{
+    if (!xendev->ops->connected) {
+        return;
+    }
+
+    if (xendev->fe_state != XenbusStateConnected) {
+        if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) {
+            xen_be_printf(xendev, 2, "frontend not ready, ignoring\n");
+        } else {
+            xen_be_printf(xendev, 2, "frontend not ready (yet)\n");
+            return;
+        }
+    }
+
+    xendev->ops->connected(xendev);
+}
+
+/*
+ * Teardown connection.
+ *
+ * Goes to Closed when done.
+ */
+static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state)
+{
+    if (xendev->be_state != XenbusStateClosing &&
+        xendev->be_state != XenbusStateClosed  &&
+        xendev->ops->disconnect) {
+        xendev->ops->disconnect(xendev);
+    }
+    if (xendev->be_state != state) {
+        xen_be_set_state(xendev, state);
+    }
+}
+
+/*
+ * Try to reset xendev, for reconnection by another frontend instance.
+ */
+static int xen_be_try_reset(struct XenDevice *xendev)
+{
+    if (xendev->fe_state != XenbusStateInitialising) {
+        return -1;
+    }
+
+    xen_be_printf(xendev, 1, "device reset (for re-connect)\n");
+    xen_be_set_state(xendev, XenbusStateInitialising);
+    return 0;
+}
+
+/*
+ * state change dispatcher function
+ */
+void xen_be_check_state(struct XenDevice *xendev)
+{
+    int rc = 0;
+
+    /* frontend may request shutdown from almost anywhere */
+    if (xendev->fe_state == XenbusStateClosing ||
+        xendev->fe_state == XenbusStateClosed) {
+        xen_be_disconnect(xendev, xendev->fe_state);
+        return;
+    }
+
+    /* check for possible backend state transitions */
+    for (;;) {
+        switch (xendev->be_state) {
+        case XenbusStateUnknown:
+            rc = xen_be_try_setup(xendev);
+            break;
+        case XenbusStateInitialising:
+            rc = xen_be_try_init(xendev);
+            break;
+        case XenbusStateInitWait:
+            rc = xen_be_try_initialise(xendev);
+            break;
+        case XenbusStateConnected:
+            /* xendev->be_state doesn't change */
+            xen_be_try_connected(xendev);
+            rc = -1;
+            break;
+        case XenbusStateClosed:
+            rc = xen_be_try_reset(xendev);
+            break;
+        default:
+            rc = -1;
+        }
+        if (rc != 0) {
+            break;
+        }
+    }
+}
+
+/* ------------------------------------------------------------- */
+
+static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops)
+{
+    struct XenDevice *xendev;
+    char path[XEN_BUFSIZE], token[XEN_BUFSIZE];
+    char **dev = NULL, *dom0;
+    unsigned int cdev, j;
+
+    /* setup watch */
+    dom0 = xs_get_domain_path(xenstore, 0);
+    snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops);
+    snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom);
+    free(dom0);
+    if (!xs_watch(xenstore, path, token)) {
+        xen_be_printf(NULL, 0, "xen be: watching backend path (%s) failed\n", path);
+        return -1;
+    }
+
+    /* look for backends */
+    dev = xs_directory(xenstore, 0, path, &cdev);
+    if (!dev) {
+        return 0;
+    }
+    for (j = 0; j < cdev; j++) {
+        xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops);
+        if (xendev == NULL) {
+            continue;
+        }
+        xen_be_check_state(xendev);
+    }
+    free(dev);
+    return 0;
+}
+
+static void xenstore_update_be(char *watch, char *type, int dom,
+                               struct XenDevOps *ops)
+{
+    struct XenDevice *xendev;
+    char path[XEN_BUFSIZE], *dom0, *bepath;
+    unsigned int len, dev;
+
+    dom0 = xs_get_domain_path(xenstore, 0);
+    len = snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom);
+    free(dom0);
+    if (strncmp(path, watch, len) != 0) {
+        return;
+    }
+    if (sscanf(watch+len, "/%u/%255s", &dev, path) != 2) {
+        strcpy(path, "");
+        if (sscanf(watch+len, "/%u", &dev) != 1) {
+            dev = -1;
+        }
+    }
+    if (dev == -1) {
+        return;
+    }
+
+    xendev = xen_be_get_xendev(type, dom, dev, ops);
+    if (xendev != NULL) {
+        bepath = xs_read(xenstore, 0, xendev->be, &len);
+        if (bepath == NULL) {
+            xen_be_del_xendev(dom, dev);
+        } else {
+            free(bepath);
+            xen_be_backend_changed(xendev, path);
+            xen_be_check_state(xendev);
+        }
+    }
+}
+
+static void xenstore_update_fe(char *watch, struct XenDevice *xendev)
+{
+    char *node;
+    unsigned int len;
+
+    len = strlen(xendev->fe);
+    if (strncmp(xendev->fe, watch, len) != 0) {
+        return;
+    }
+    if (watch[len] != '/') {
+        return;
+    }
+    node = watch + len + 1;
+
+    xen_be_frontend_changed(xendev, node);
+    xen_be_check_state(xendev);
+}
+
+static void xenstore_update(void *unused)
+{
+    char **vec = NULL;
+    intptr_t type, ops, ptr;
+    unsigned int dom, count;
+
+    vec = xs_read_watch(xenstore, &count);
+    if (vec == NULL) {
+        goto cleanup;
+    }
+
+    if (sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR,
+               &type, &dom, &ops) == 3) {
+        xenstore_update_be(vec[XS_WATCH_PATH], (void*)type, dom, (void*)ops);
+    }
+    if (sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr) == 1) {
+        xenstore_update_fe(vec[XS_WATCH_PATH], (void*)ptr);
+    }
+
+cleanup:
+    free(vec);
+}
+
+static void xen_be_evtchn_event(void *opaque)
+{
+    struct XenDevice *xendev = opaque;
+    evtchn_port_t port;
+
+    port = xc_evtchn_pending(xendev->evtchndev);
+    if (port != xendev->local_port) {
+        xen_be_printf(xendev, 0, "xc_evtchn_pending returned %d (expected %d)\n",
+                      port, xendev->local_port);
+        return;
+    }
+    xc_evtchn_unmask(xendev->evtchndev, port);
+
+    if (xendev->ops->event) {
+        xendev->ops->event(xendev);
+    }
+}
+
+/* -------------------------------------------------------------------- */
+
+int xen_be_init(void)
+{
+    xenstore = xs_daemon_open();
+    if (!xenstore) {
+        xen_be_printf(NULL, 0, "can't connect to xenstored\n");
+        return -1;
+    }
+
+    if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) {
+        goto err;
+    }
+
+    if (xen_xc == XC_HANDLER_INITIAL_VALUE) {
+        /* Check if xen_init() have been called */
+        goto err;
+    }
+    return 0;
+
+err:
+    qemu_set_fd_handler(xs_fileno(xenstore), NULL, NULL, NULL);
+    xs_daemon_close(xenstore);
+    xenstore = NULL;
+
+    return -1;
+}
+
+int xen_be_register(const char *type, struct XenDevOps *ops)
+{
+    return xenstore_scan(type, xen_domid, ops);
+}
+
+int xen_be_bind_evtchn(struct XenDevice *xendev)
+{
+    if (xendev->local_port != -1) {
+        return 0;
+    }
+    xendev->local_port = xc_evtchn_bind_interdomain
+        (xendev->evtchndev, xendev->dom, xendev->remote_port);
+    if (xendev->local_port == -1) {
+        xen_be_printf(xendev, 0, "xc_evtchn_bind_interdomain failed\n");
+        return -1;
+    }
+    xen_be_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port);
+    qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev),
+                        xen_be_evtchn_event, NULL, xendev);
+    return 0;
+}
+
+void xen_be_unbind_evtchn(struct XenDevice *xendev)
+{
+    if (xendev->local_port == -1) {
+        return;
+    }
+    qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), NULL, NULL, NULL);
+    xc_evtchn_unbind(xendev->evtchndev, xendev->local_port);
+    xen_be_printf(xendev, 2, "unbind evtchn port %d\n", xendev->local_port);
+    xendev->local_port = -1;
+}
+
+int xen_be_send_notify(struct XenDevice *xendev)
+{
+    return xc_evtchn_notify(xendev->evtchndev, xendev->local_port);
+}
+
+/*
+ * msg_level:
+ *  0 == errors (stderr + logfile).
+ *  1 == informative debug messages (logfile only).
+ *  2 == noisy debug messages (logfile only).
+ *  3 == will flood your log (logfile only).
+ */
+void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...)
+{
+    va_list args;
+
+    if (xendev) {
+        if (msg_level > xendev->debug) {
+            return;
+        }
+        qemu_log("xen be: %s: ", xendev->name);
+        if (msg_level == 0) {
+            fprintf(stderr, "xen be: %s: ", xendev->name);
+        }
+    } else {
+        if (msg_level > debug) {
+            return;
+        }
+        qemu_log("xen be core: ");
+        if (msg_level == 0) {
+            fprintf(stderr, "xen be core: ");
+        }
+    }
+    va_start(args, fmt);
+    qemu_log_vprintf(fmt, args);
+    va_end(args);
+    if (msg_level == 0) {
+        va_start(args, fmt);
+        vfprintf(stderr, fmt, args);
+        va_end(args);
+    }
+    qemu_log_flush();
+}
diff --git a/hw/xen/xen_devconfig.c b/hw/xen/xen_devconfig.c
new file mode 100644 (file)
index 0000000..fa998ef
--- /dev/null
@@ -0,0 +1,174 @@
+#include "hw/xen/xen_backend.h"
+#include "sysemu/blockdev.h"
+
+/* ------------------------------------------------------------- */
+
+struct xs_dirs {
+    char *xs_dir;
+    QTAILQ_ENTRY(xs_dirs) list;
+};
+static QTAILQ_HEAD(xs_dirs_head, xs_dirs) xs_cleanup = QTAILQ_HEAD_INITIALIZER(xs_cleanup);
+
+static void xen_config_cleanup_dir(char *dir)
+{
+    struct xs_dirs *d;
+
+    d = g_malloc(sizeof(*d));
+    d->xs_dir = dir;
+    QTAILQ_INSERT_TAIL(&xs_cleanup, d, list);
+}
+
+void xen_config_cleanup(void)
+{
+    struct xs_dirs *d;
+
+    QTAILQ_FOREACH(d, &xs_cleanup, list) {
+       xs_rm(xenstore, 0, d->xs_dir);
+    }
+}
+
+/* ------------------------------------------------------------- */
+
+static int xen_config_dev_mkdir(char *dev, int p)
+{
+    struct xs_permissions perms[2] = {{
+            .id    = 0, /* set owner: dom0 */
+        },{
+            .id    = xen_domid,
+            .perms = p,
+        }};
+
+    if (!xs_mkdir(xenstore, 0, dev)) {
+       xen_be_printf(NULL, 0, "xs_mkdir %s: failed\n", dev);
+       return -1;
+    }
+    xen_config_cleanup_dir(g_strdup(dev));
+
+    if (!xs_set_permissions(xenstore, 0, dev, perms, 2)) {
+       xen_be_printf(NULL, 0, "xs_set_permissions %s: failed\n", dev);
+       return -1;
+    }
+    return 0;
+}
+
+static int xen_config_dev_dirs(const char *ftype, const char *btype, int vdev,
+                              char *fe, char *be, int len)
+{
+    char *dom;
+
+    dom = xs_get_domain_path(xenstore, xen_domid);
+    snprintf(fe, len, "%s/device/%s/%d", dom, ftype, vdev);
+    free(dom);
+
+    dom = xs_get_domain_path(xenstore, 0);
+    snprintf(be, len, "%s/backend/%s/%d/%d", dom, btype, xen_domid, vdev);
+    free(dom);
+
+    xen_config_dev_mkdir(fe, XS_PERM_READ | XS_PERM_WRITE);
+    xen_config_dev_mkdir(be, XS_PERM_READ);
+    return 0;
+}
+
+static int xen_config_dev_all(char *fe, char *be)
+{
+    /* frontend */
+    if (xen_protocol)
+        xenstore_write_str(fe, "protocol", xen_protocol);
+
+    xenstore_write_int(fe, "state",           XenbusStateInitialising);
+    xenstore_write_int(fe, "backend-id",      0);
+    xenstore_write_str(fe, "backend",         be);
+
+    /* backend */
+    xenstore_write_str(be, "domain",          qemu_name ? qemu_name : "no-name");
+    xenstore_write_int(be, "online",          1);
+    xenstore_write_int(be, "state",           XenbusStateInitialising);
+    xenstore_write_int(be, "frontend-id",     xen_domid);
+    xenstore_write_str(be, "frontend",        fe);
+
+    return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+int xen_config_dev_blk(DriveInfo *disk)
+{
+    char fe[256], be[256], device_name[32];
+    int vdev = 202 * 256 + 16 * disk->unit;
+    int cdrom = disk->media_cd;
+    const char *devtype = cdrom ? "cdrom" : "disk";
+    const char *mode    = cdrom ? "r"     : "w";
+    const char *filename = qemu_opt_get(disk->opts, "file");
+
+    snprintf(device_name, sizeof(device_name), "xvd%c", 'a' + disk->unit);
+    xen_be_printf(NULL, 1, "config disk %d [%s]: %s\n",
+                  disk->unit, device_name, filename);
+    xen_config_dev_dirs("vbd", "qdisk", vdev, fe, be, sizeof(fe));
+
+    /* frontend */
+    xenstore_write_int(fe, "virtual-device",  vdev);
+    xenstore_write_str(fe, "device-type",     devtype);
+
+    /* backend */
+    xenstore_write_str(be, "dev",             device_name);
+    xenstore_write_str(be, "type",            "file");
+    xenstore_write_str(be, "params",          filename);
+    xenstore_write_str(be, "mode",            mode);
+
+    /* common stuff */
+    return xen_config_dev_all(fe, be);
+}
+
+int xen_config_dev_nic(NICInfo *nic)
+{
+    char fe[256], be[256];
+    char mac[20];
+    int vlan_id = -1;
+
+    net_hub_id_for_client(nic->netdev, &vlan_id);
+    snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
+             nic->macaddr.a[0], nic->macaddr.a[1], nic->macaddr.a[2],
+             nic->macaddr.a[3], nic->macaddr.a[4], nic->macaddr.a[5]);
+    xen_be_printf(NULL, 1, "config nic %d: mac=\"%s\"\n", vlan_id, mac);
+    xen_config_dev_dirs("vif", "qnic", vlan_id, fe, be, sizeof(fe));
+
+    /* frontend */
+    xenstore_write_int(fe, "handle",     vlan_id);
+    xenstore_write_str(fe, "mac",        mac);
+
+    /* backend */
+    xenstore_write_int(be, "handle",     vlan_id);
+    xenstore_write_str(be, "mac",        mac);
+
+    /* common stuff */
+    return xen_config_dev_all(fe, be);
+}
+
+int xen_config_dev_vfb(int vdev, const char *type)
+{
+    char fe[256], be[256];
+
+    xen_config_dev_dirs("vfb", "vfb", vdev, fe, be, sizeof(fe));
+
+    /* backend */
+    xenstore_write_str(be, "type",  type);
+
+    /* common stuff */
+    return xen_config_dev_all(fe, be);
+}
+
+int xen_config_dev_vkbd(int vdev)
+{
+    char fe[256], be[256];
+
+    xen_config_dev_dirs("vkbd", "vkbd", vdev, fe, be, sizeof(fe));
+    return xen_config_dev_all(fe, be);
+}
+
+int xen_config_dev_console(int vdev)
+{
+    char fe[256], be[256];
+
+    xen_config_dev_dirs("console", "console", vdev, fe, be, sizeof(fe));
+    return xen_config_dev_all(fe, be);
+}
diff --git a/hw/xen_backend.c b/hw/xen_backend.c
deleted file mode 100644 (file)
index 2a8c9f5..0000000
+++ /dev/null
@@ -1,800 +0,0 @@
-/*
- *  xen backend driver infrastructure
- *  (c) 2008 Gerd Hoffmann <kraxel@redhat.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; under version 2 of the License.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- *  Contributions after 2012-01-13 are licensed under the terms of the
- *  GNU GPL, version 2 or (at your option) any later version.
- */
-
-/*
- * TODO: add some xenbus / xenstore concepts overview here.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <sys/signal.h>
-
-#include "hw/hw.h"
-#include "char/char.h"
-#include "qemu/log.h"
-#include "hw/xen/xen_backend.h"
-
-#include <xen/grant_table.h>
-
-/* ------------------------------------------------------------- */
-
-/* public */
-XenXC xen_xc = XC_HANDLER_INITIAL_VALUE;
-XenGnttab xen_xcg = XC_HANDLER_INITIAL_VALUE;
-struct xs_handle *xenstore = NULL;
-const char *xen_protocol;
-
-/* private */
-static QTAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = QTAILQ_HEAD_INITIALIZER(xendevs);
-static int debug = 0;
-
-/* ------------------------------------------------------------- */
-
-int xenstore_write_str(const char *base, const char *node, const char *val)
-{
-    char abspath[XEN_BUFSIZE];
-
-    snprintf(abspath, sizeof(abspath), "%s/%s", base, node);
-    if (!xs_write(xenstore, 0, abspath, val, strlen(val))) {
-        return -1;
-    }
-    return 0;
-}
-
-char *xenstore_read_str(const char *base, const char *node)
-{
-    char abspath[XEN_BUFSIZE];
-    unsigned int len;
-    char *str, *ret = NULL;
-
-    snprintf(abspath, sizeof(abspath), "%s/%s", base, node);
-    str = xs_read(xenstore, 0, abspath, &len);
-    if (str != NULL) {
-        /* move to qemu-allocated memory to make sure
-         * callers can savely g_free() stuff. */
-        ret = g_strdup(str);
-        free(str);
-    }
-    return ret;
-}
-
-int xenstore_write_int(const char *base, const char *node, int ival)
-{
-    char val[12];
-
-    snprintf(val, sizeof(val), "%d", ival);
-    return xenstore_write_str(base, node, val);
-}
-
-int xenstore_write_int64(const char *base, const char *node, int64_t ival)
-{
-    char val[21];
-
-    snprintf(val, sizeof(val), "%"PRId64, ival);
-    return xenstore_write_str(base, node, val);
-}
-
-int xenstore_read_int(const char *base, const char *node, int *ival)
-{
-    char *val;
-    int rc = -1;
-
-    val = xenstore_read_str(base, node);
-    if (val && 1 == sscanf(val, "%d", ival)) {
-        rc = 0;
-    }
-    g_free(val);
-    return rc;
-}
-
-int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val)
-{
-    return xenstore_write_str(xendev->be, node, val);
-}
-
-int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival)
-{
-    return xenstore_write_int(xendev->be, node, ival);
-}
-
-int xenstore_write_be_int64(struct XenDevice *xendev, const char *node, int64_t ival)
-{
-    return xenstore_write_int64(xendev->be, node, ival);
-}
-
-char *xenstore_read_be_str(struct XenDevice *xendev, const char *node)
-{
-    return xenstore_read_str(xendev->be, node);
-}
-
-int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival)
-{
-    return xenstore_read_int(xendev->be, node, ival);
-}
-
-char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node)
-{
-    return xenstore_read_str(xendev->fe, node);
-}
-
-int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival)
-{
-    return xenstore_read_int(xendev->fe, node, ival);
-}
-
-/* ------------------------------------------------------------- */
-
-const char *xenbus_strstate(enum xenbus_state state)
-{
-    static const char *const name[] = {
-        [ XenbusStateUnknown      ] = "Unknown",
-        [ XenbusStateInitialising ] = "Initialising",
-        [ XenbusStateInitWait     ] = "InitWait",
-        [ XenbusStateInitialised  ] = "Initialised",
-        [ XenbusStateConnected    ] = "Connected",
-        [ XenbusStateClosing      ] = "Closing",
-        [ XenbusStateClosed       ] = "Closed",
-    };
-    return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID";
-}
-
-int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state)
-{
-    int rc;
-
-    rc = xenstore_write_be_int(xendev, "state", state);
-    if (rc < 0) {
-        return rc;
-    }
-    xen_be_printf(xendev, 1, "backend state: %s -> %s\n",
-                  xenbus_strstate(xendev->be_state), xenbus_strstate(state));
-    xendev->be_state = state;
-    return 0;
-}
-
-/* ------------------------------------------------------------- */
-
-struct XenDevice *xen_be_find_xendev(const char *type, int dom, int dev)
-{
-    struct XenDevice *xendev;
-
-    QTAILQ_FOREACH(xendev, &xendevs, next) {
-        if (xendev->dom != dom) {
-            continue;
-        }
-        if (xendev->dev != dev) {
-            continue;
-        }
-        if (strcmp(xendev->type, type) != 0) {
-            continue;
-        }
-        return xendev;
-    }
-    return NULL;
-}
-
-/*
- * get xen backend device, allocate a new one if it doesn't exist.
- */
-static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev,
-                                           struct XenDevOps *ops)
-{
-    struct XenDevice *xendev;
-    char *dom0;
-
-    xendev = xen_be_find_xendev(type, dom, dev);
-    if (xendev) {
-        return xendev;
-    }
-
-    /* init new xendev */
-    xendev = g_malloc0(ops->size);
-    xendev->type  = type;
-    xendev->dom   = dom;
-    xendev->dev   = dev;
-    xendev->ops   = ops;
-
-    dom0 = xs_get_domain_path(xenstore, 0);
-    snprintf(xendev->be, sizeof(xendev->be), "%s/backend/%s/%d/%d",
-             dom0, xendev->type, xendev->dom, xendev->dev);
-    snprintf(xendev->name, sizeof(xendev->name), "%s-%d",
-             xendev->type, xendev->dev);
-    free(dom0);
-
-    xendev->debug      = debug;
-    xendev->local_port = -1;
-
-    xendev->evtchndev = xen_xc_evtchn_open(NULL, 0);
-    if (xendev->evtchndev == XC_HANDLER_INITIAL_VALUE) {
-        xen_be_printf(NULL, 0, "can't open evtchn device\n");
-        g_free(xendev);
-        return NULL;
-    }
-    fcntl(xc_evtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC);
-
-    if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) {
-        xendev->gnttabdev = xen_xc_gnttab_open(NULL, 0);
-        if (xendev->gnttabdev == XC_HANDLER_INITIAL_VALUE) {
-            xen_be_printf(NULL, 0, "can't open gnttab device\n");
-            xc_evtchn_close(xendev->evtchndev);
-            g_free(xendev);
-            return NULL;
-        }
-    } else {
-        xendev->gnttabdev = XC_HANDLER_INITIAL_VALUE;
-    }
-
-    QTAILQ_INSERT_TAIL(&xendevs, xendev, next);
-
-    if (xendev->ops->alloc) {
-        xendev->ops->alloc(xendev);
-    }
-
-    return xendev;
-}
-
-/*
- * release xen backend device.
- */
-static struct XenDevice *xen_be_del_xendev(int dom, int dev)
-{
-    struct XenDevice *xendev, *xnext;
-
-    /*
-     * This is pretty much like QTAILQ_FOREACH(xendev, &xendevs, next) but
-     * we save the next pointer in xnext because we might free xendev.
-     */
-    xnext = xendevs.tqh_first;
-    while (xnext) {
-        xendev = xnext;
-        xnext = xendev->next.tqe_next;
-
-        if (xendev->dom != dom) {
-            continue;
-        }
-        if (xendev->dev != dev && dev != -1) {
-            continue;
-        }
-
-        if (xendev->ops->free) {
-            xendev->ops->free(xendev);
-        }
-
-        if (xendev->fe) {
-            char token[XEN_BUFSIZE];
-            snprintf(token, sizeof(token), "fe:%p", xendev);
-            xs_unwatch(xenstore, xendev->fe, token);
-            g_free(xendev->fe);
-        }
-
-        if (xendev->evtchndev != XC_HANDLER_INITIAL_VALUE) {
-            xc_evtchn_close(xendev->evtchndev);
-        }
-        if (xendev->gnttabdev != XC_HANDLER_INITIAL_VALUE) {
-            xc_gnttab_close(xendev->gnttabdev);
-        }
-
-        QTAILQ_REMOVE(&xendevs, xendev, next);
-        g_free(xendev);
-    }
-    return NULL;
-}
-
-/*
- * Sync internal data structures on xenstore updates.
- * Node specifies the changed field.  node = NULL means
- * update all fields (used for initialization).
- */
-static void xen_be_backend_changed(struct XenDevice *xendev, const char *node)
-{
-    if (node == NULL  ||  strcmp(node, "online") == 0) {
-        if (xenstore_read_be_int(xendev, "online", &xendev->online) == -1) {
-            xendev->online = 0;
-        }
-    }
-
-    if (node) {
-        xen_be_printf(xendev, 2, "backend update: %s\n", node);
-        if (xendev->ops->backend_changed) {
-            xendev->ops->backend_changed(xendev, node);
-        }
-    }
-}
-
-static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node)
-{
-    int fe_state;
-
-    if (node == NULL  ||  strcmp(node, "state") == 0) {
-        if (xenstore_read_fe_int(xendev, "state", &fe_state) == -1) {
-            fe_state = XenbusStateUnknown;
-        }
-        if (xendev->fe_state != fe_state) {
-            xen_be_printf(xendev, 1, "frontend state: %s -> %s\n",
-                          xenbus_strstate(xendev->fe_state),
-                          xenbus_strstate(fe_state));
-        }
-        xendev->fe_state = fe_state;
-    }
-    if (node == NULL  ||  strcmp(node, "protocol") == 0) {
-        g_free(xendev->protocol);
-        xendev->protocol = xenstore_read_fe_str(xendev, "protocol");
-        if (xendev->protocol) {
-            xen_be_printf(xendev, 1, "frontend protocol: %s\n", xendev->protocol);
-        }
-    }
-
-    if (node) {
-        xen_be_printf(xendev, 2, "frontend update: %s\n", node);
-        if (xendev->ops->frontend_changed) {
-            xendev->ops->frontend_changed(xendev, node);
-        }
-    }
-}
-
-/* ------------------------------------------------------------- */
-/* Check for possible state transitions and perform them.        */
-
-/*
- * Initial xendev setup.  Read frontend path, register watch for it.
- * Should succeed once xend finished setting up the backend device.
- *
- * Also sets initial state (-> Initializing) when done.  Which
- * only affects the xendev->be_state variable as xenbus should
- * already be put into that state by xend.
- */
-static int xen_be_try_setup(struct XenDevice *xendev)
-{
-    char token[XEN_BUFSIZE];
-    int be_state;
-
-    if (xenstore_read_be_int(xendev, "state", &be_state) == -1) {
-        xen_be_printf(xendev, 0, "reading backend state failed\n");
-        return -1;
-    }
-
-    if (be_state != XenbusStateInitialising) {
-        xen_be_printf(xendev, 0, "initial backend state is wrong (%s)\n",
-                      xenbus_strstate(be_state));
-        return -1;
-    }
-
-    xendev->fe = xenstore_read_be_str(xendev, "frontend");
-    if (xendev->fe == NULL) {
-        xen_be_printf(xendev, 0, "reading frontend path failed\n");
-        return -1;
-    }
-
-    /* setup frontend watch */
-    snprintf(token, sizeof(token), "fe:%p", xendev);
-    if (!xs_watch(xenstore, xendev->fe, token)) {
-        xen_be_printf(xendev, 0, "watching frontend path (%s) failed\n",
-                      xendev->fe);
-        return -1;
-    }
-    xen_be_set_state(xendev, XenbusStateInitialising);
-
-    xen_be_backend_changed(xendev, NULL);
-    xen_be_frontend_changed(xendev, NULL);
-    return 0;
-}
-
-/*
- * Try initialize xendev.  Prepare everything the backend can do
- * without synchronizing with the frontend.  Fakes hotplug-status.  No
- * hotplug involved here because this is about userspace drivers, thus
- * there are kernel backend devices which could invoke hotplug.
- *
- * Goes to InitWait on success.
- */
-static int xen_be_try_init(struct XenDevice *xendev)
-{
-    int rc = 0;
-
-    if (!xendev->online) {
-        xen_be_printf(xendev, 1, "not online\n");
-        return -1;
-    }
-
-    if (xendev->ops->init) {
-        rc = xendev->ops->init(xendev);
-    }
-    if (rc != 0) {
-        xen_be_printf(xendev, 1, "init() failed\n");
-        return rc;
-    }
-
-    xenstore_write_be_str(xendev, "hotplug-status", "connected");
-    xen_be_set_state(xendev, XenbusStateInitWait);
-    return 0;
-}
-
-/*
- * Try to initialise xendev.  Depends on the frontend being ready
- * for it (shared ring and evtchn info in xenstore, state being
- * Initialised or Connected).
- *
- * Goes to Connected on success.
- */
-static int xen_be_try_initialise(struct XenDevice *xendev)
-{
-    int rc = 0;
-
-    if (xendev->fe_state != XenbusStateInitialised  &&
-        xendev->fe_state != XenbusStateConnected) {
-        if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) {
-            xen_be_printf(xendev, 2, "frontend not ready, ignoring\n");
-        } else {
-            xen_be_printf(xendev, 2, "frontend not ready (yet)\n");
-            return -1;
-        }
-    }
-
-    if (xendev->ops->initialise) {
-        rc = xendev->ops->initialise(xendev);
-    }
-    if (rc != 0) {
-        xen_be_printf(xendev, 0, "initialise() failed\n");
-        return rc;
-    }
-
-    xen_be_set_state(xendev, XenbusStateConnected);
-    return 0;
-}
-
-/*
- * Try to let xendev know that it is connected.  Depends on the
- * frontend being Connected.  Note that this may be called more
- * than once since the backend state is not modified.
- */
-static void xen_be_try_connected(struct XenDevice *xendev)
-{
-    if (!xendev->ops->connected) {
-        return;
-    }
-
-    if (xendev->fe_state != XenbusStateConnected) {
-        if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) {
-            xen_be_printf(xendev, 2, "frontend not ready, ignoring\n");
-        } else {
-            xen_be_printf(xendev, 2, "frontend not ready (yet)\n");
-            return;
-        }
-    }
-
-    xendev->ops->connected(xendev);
-}
-
-/*
- * Teardown connection.
- *
- * Goes to Closed when done.
- */
-static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state)
-{
-    if (xendev->be_state != XenbusStateClosing &&
-        xendev->be_state != XenbusStateClosed  &&
-        xendev->ops->disconnect) {
-        xendev->ops->disconnect(xendev);
-    }
-    if (xendev->be_state != state) {
-        xen_be_set_state(xendev, state);
-    }
-}
-
-/*
- * Try to reset xendev, for reconnection by another frontend instance.
- */
-static int xen_be_try_reset(struct XenDevice *xendev)
-{
-    if (xendev->fe_state != XenbusStateInitialising) {
-        return -1;
-    }
-
-    xen_be_printf(xendev, 1, "device reset (for re-connect)\n");
-    xen_be_set_state(xendev, XenbusStateInitialising);
-    return 0;
-}
-
-/*
- * state change dispatcher function
- */
-void xen_be_check_state(struct XenDevice *xendev)
-{
-    int rc = 0;
-
-    /* frontend may request shutdown from almost anywhere */
-    if (xendev->fe_state == XenbusStateClosing ||
-        xendev->fe_state == XenbusStateClosed) {
-        xen_be_disconnect(xendev, xendev->fe_state);
-        return;
-    }
-
-    /* check for possible backend state transitions */
-    for (;;) {
-        switch (xendev->be_state) {
-        case XenbusStateUnknown:
-            rc = xen_be_try_setup(xendev);
-            break;
-        case XenbusStateInitialising:
-            rc = xen_be_try_init(xendev);
-            break;
-        case XenbusStateInitWait:
-            rc = xen_be_try_initialise(xendev);
-            break;
-        case XenbusStateConnected:
-            /* xendev->be_state doesn't change */
-            xen_be_try_connected(xendev);
-            rc = -1;
-            break;
-        case XenbusStateClosed:
-            rc = xen_be_try_reset(xendev);
-            break;
-        default:
-            rc = -1;
-        }
-        if (rc != 0) {
-            break;
-        }
-    }
-}
-
-/* ------------------------------------------------------------- */
-
-static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops)
-{
-    struct XenDevice *xendev;
-    char path[XEN_BUFSIZE], token[XEN_BUFSIZE];
-    char **dev = NULL, *dom0;
-    unsigned int cdev, j;
-
-    /* setup watch */
-    dom0 = xs_get_domain_path(xenstore, 0);
-    snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops);
-    snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom);
-    free(dom0);
-    if (!xs_watch(xenstore, path, token)) {
-        xen_be_printf(NULL, 0, "xen be: watching backend path (%s) failed\n", path);
-        return -1;
-    }
-
-    /* look for backends */
-    dev = xs_directory(xenstore, 0, path, &cdev);
-    if (!dev) {
-        return 0;
-    }
-    for (j = 0; j < cdev; j++) {
-        xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops);
-        if (xendev == NULL) {
-            continue;
-        }
-        xen_be_check_state(xendev);
-    }
-    free(dev);
-    return 0;
-}
-
-static void xenstore_update_be(char *watch, char *type, int dom,
-                               struct XenDevOps *ops)
-{
-    struct XenDevice *xendev;
-    char path[XEN_BUFSIZE], *dom0, *bepath;
-    unsigned int len, dev;
-
-    dom0 = xs_get_domain_path(xenstore, 0);
-    len = snprintf(path, sizeof(path), "%s/backend/%s/%d", dom0, type, dom);
-    free(dom0);
-    if (strncmp(path, watch, len) != 0) {
-        return;
-    }
-    if (sscanf(watch+len, "/%u/%255s", &dev, path) != 2) {
-        strcpy(path, "");
-        if (sscanf(watch+len, "/%u", &dev) != 1) {
-            dev = -1;
-        }
-    }
-    if (dev == -1) {
-        return;
-    }
-
-    xendev = xen_be_get_xendev(type, dom, dev, ops);
-    if (xendev != NULL) {
-        bepath = xs_read(xenstore, 0, xendev->be, &len);
-        if (bepath == NULL) {
-            xen_be_del_xendev(dom, dev);
-        } else {
-            free(bepath);
-            xen_be_backend_changed(xendev, path);
-            xen_be_check_state(xendev);
-        }
-    }
-}
-
-static void xenstore_update_fe(char *watch, struct XenDevice *xendev)
-{
-    char *node;
-    unsigned int len;
-
-    len = strlen(xendev->fe);
-    if (strncmp(xendev->fe, watch, len) != 0) {
-        return;
-    }
-    if (watch[len] != '/') {
-        return;
-    }
-    node = watch + len + 1;
-
-    xen_be_frontend_changed(xendev, node);
-    xen_be_check_state(xendev);
-}
-
-static void xenstore_update(void *unused)
-{
-    char **vec = NULL;
-    intptr_t type, ops, ptr;
-    unsigned int dom, count;
-
-    vec = xs_read_watch(xenstore, &count);
-    if (vec == NULL) {
-        goto cleanup;
-    }
-
-    if (sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR,
-               &type, &dom, &ops) == 3) {
-        xenstore_update_be(vec[XS_WATCH_PATH], (void*)type, dom, (void*)ops);
-    }
-    if (sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr) == 1) {
-        xenstore_update_fe(vec[XS_WATCH_PATH], (void*)ptr);
-    }
-
-cleanup:
-    free(vec);
-}
-
-static void xen_be_evtchn_event(void *opaque)
-{
-    struct XenDevice *xendev = opaque;
-    evtchn_port_t port;
-
-    port = xc_evtchn_pending(xendev->evtchndev);
-    if (port != xendev->local_port) {
-        xen_be_printf(xendev, 0, "xc_evtchn_pending returned %d (expected %d)\n",
-                      port, xendev->local_port);
-        return;
-    }
-    xc_evtchn_unmask(xendev->evtchndev, port);
-
-    if (xendev->ops->event) {
-        xendev->ops->event(xendev);
-    }
-}
-
-/* -------------------------------------------------------------------- */
-
-int xen_be_init(void)
-{
-    xenstore = xs_daemon_open();
-    if (!xenstore) {
-        xen_be_printf(NULL, 0, "can't connect to xenstored\n");
-        return -1;
-    }
-
-    if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) {
-        goto err;
-    }
-
-    if (xen_xc == XC_HANDLER_INITIAL_VALUE) {
-        /* Check if xen_init() have been called */
-        goto err;
-    }
-    return 0;
-
-err:
-    qemu_set_fd_handler(xs_fileno(xenstore), NULL, NULL, NULL);
-    xs_daemon_close(xenstore);
-    xenstore = NULL;
-
-    return -1;
-}
-
-int xen_be_register(const char *type, struct XenDevOps *ops)
-{
-    return xenstore_scan(type, xen_domid, ops);
-}
-
-int xen_be_bind_evtchn(struct XenDevice *xendev)
-{
-    if (xendev->local_port != -1) {
-        return 0;
-    }
-    xendev->local_port = xc_evtchn_bind_interdomain
-        (xendev->evtchndev, xendev->dom, xendev->remote_port);
-    if (xendev->local_port == -1) {
-        xen_be_printf(xendev, 0, "xc_evtchn_bind_interdomain failed\n");
-        return -1;
-    }
-    xen_be_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port);
-    qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev),
-                        xen_be_evtchn_event, NULL, xendev);
-    return 0;
-}
-
-void xen_be_unbind_evtchn(struct XenDevice *xendev)
-{
-    if (xendev->local_port == -1) {
-        return;
-    }
-    qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), NULL, NULL, NULL);
-    xc_evtchn_unbind(xendev->evtchndev, xendev->local_port);
-    xen_be_printf(xendev, 2, "unbind evtchn port %d\n", xendev->local_port);
-    xendev->local_port = -1;
-}
-
-int xen_be_send_notify(struct XenDevice *xendev)
-{
-    return xc_evtchn_notify(xendev->evtchndev, xendev->local_port);
-}
-
-/*
- * msg_level:
- *  0 == errors (stderr + logfile).
- *  1 == informative debug messages (logfile only).
- *  2 == noisy debug messages (logfile only).
- *  3 == will flood your log (logfile only).
- */
-void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...)
-{
-    va_list args;
-
-    if (xendev) {
-        if (msg_level > xendev->debug) {
-            return;
-        }
-        qemu_log("xen be: %s: ", xendev->name);
-        if (msg_level == 0) {
-            fprintf(stderr, "xen be: %s: ", xendev->name);
-        }
-    } else {
-        if (msg_level > debug) {
-            return;
-        }
-        qemu_log("xen be core: ");
-        if (msg_level == 0) {
-            fprintf(stderr, "xen be core: ");
-        }
-    }
-    va_start(args, fmt);
-    qemu_log_vprintf(fmt, args);
-    va_end(args);
-    if (msg_level == 0) {
-        va_start(args, fmt);
-        vfprintf(stderr, fmt, args);
-        va_end(args);
-    }
-    qemu_log_flush();
-}
diff --git a/hw/xen_console.c b/hw/xen_console.c
deleted file mode 100644 (file)
index efc3232..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- *  Copyright (C) International Business Machines  Corp., 2005
- *  Author(s): Anthony Liguori <aliguori@us.ibm.com>
- *
- *  Copyright (C) Red Hat 2007
- *
- *  Xen Console
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; under version 2 of the License.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/select.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <termios.h>
-#include <stdarg.h>
-#include <sys/mman.h>
-
-#include "hw/hw.h"
-#include "char/char.h"
-#include "hw/xen/xen_backend.h"
-
-#include <xen/io/console.h>
-
-struct buffer {
-    uint8_t *data;
-    size_t consumed;
-    size_t size;
-    size_t capacity;
-    size_t max_capacity;
-};
-
-struct XenConsole {
-    struct XenDevice  xendev;  /* must be first */
-    struct buffer     buffer;
-    char              console[XEN_BUFSIZE];
-    int               ring_ref;
-    void              *sring;
-    CharDriverState   *chr;
-    int               backlog;
-};
-
-static void buffer_append(struct XenConsole *con)
-{
-    struct buffer *buffer = &con->buffer;
-    XENCONS_RING_IDX cons, prod, size;
-    struct xencons_interface *intf = con->sring;
-
-    cons = intf->out_cons;
-    prod = intf->out_prod;
-    xen_mb();
-
-    size = prod - cons;
-    if ((size == 0) || (size > sizeof(intf->out)))
-       return;
-
-    if ((buffer->capacity - buffer->size) < size) {
-       buffer->capacity += (size + 1024);
-       buffer->data = g_realloc(buffer->data, buffer->capacity);
-    }
-
-    while (cons != prod)
-       buffer->data[buffer->size++] = intf->out[
-           MASK_XENCONS_IDX(cons++, intf->out)];
-
-    xen_mb();
-    intf->out_cons = cons;
-    xen_be_send_notify(&con->xendev);
-
-    if (buffer->max_capacity &&
-       buffer->size > buffer->max_capacity) {
-       /* Discard the middle of the data. */
-
-       size_t over = buffer->size - buffer->max_capacity;
-       uint8_t *maxpos = buffer->data + buffer->max_capacity;
-
-       memmove(maxpos - over, maxpos, over);
-       buffer->data = g_realloc(buffer->data, buffer->max_capacity);
-       buffer->size = buffer->capacity = buffer->max_capacity;
-
-       if (buffer->consumed > buffer->max_capacity - over)
-           buffer->consumed = buffer->max_capacity - over;
-    }
-}
-
-static void buffer_advance(struct buffer *buffer, size_t len)
-{
-    buffer->consumed += len;
-    if (buffer->consumed == buffer->size) {
-       buffer->consumed = 0;
-       buffer->size = 0;
-    }
-}
-
-static int ring_free_bytes(struct XenConsole *con)
-{
-    struct xencons_interface *intf = con->sring;
-    XENCONS_RING_IDX cons, prod, space;
-
-    cons = intf->in_cons;
-    prod = intf->in_prod;
-    xen_mb();
-
-    space = prod - cons;
-    if (space > sizeof(intf->in))
-       return 0; /* ring is screwed: ignore it */
-
-    return (sizeof(intf->in) - space);
-}
-
-static int xencons_can_receive(void *opaque)
-{
-    struct XenConsole *con = opaque;
-    return ring_free_bytes(con);
-}
-
-static void xencons_receive(void *opaque, const uint8_t *buf, int len)
-{
-    struct XenConsole *con = opaque;
-    struct xencons_interface *intf = con->sring;
-    XENCONS_RING_IDX prod;
-    int i, max;
-
-    max = ring_free_bytes(con);
-    /* The can_receive() func limits this, but check again anyway */
-    if (max < len)
-       len = max;
-
-    prod = intf->in_prod;
-    for (i = 0; i < len; i++) {
-       intf->in[MASK_XENCONS_IDX(prod++, intf->in)] =
-           buf[i];
-    }
-    xen_wmb();
-    intf->in_prod = prod;
-    xen_be_send_notify(&con->xendev);
-}
-
-static void xencons_send(struct XenConsole *con)
-{
-    ssize_t len, size;
-
-    size = con->buffer.size - con->buffer.consumed;
-    if (con->chr)
-        len = qemu_chr_fe_write(con->chr, con->buffer.data + con->buffer.consumed,
-                             size);
-    else
-        len = size;
-    if (len < 1) {
-       if (!con->backlog) {
-           con->backlog = 1;
-           xen_be_printf(&con->xendev, 1, "backlog piling up, nobody listening?\n");
-       }
-    } else {
-       buffer_advance(&con->buffer, len);
-       if (con->backlog && len == size) {
-           con->backlog = 0;
-           xen_be_printf(&con->xendev, 1, "backlog is gone\n");
-       }
-    }
-}
-
-/* -------------------------------------------------------------------- */
-
-static int con_init(struct XenDevice *xendev)
-{
-    struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
-    char *type, *dom, label[32];
-    int ret = 0;
-    const char *output;
-
-    /* setup */
-    dom = xs_get_domain_path(xenstore, con->xendev.dom);
-    if (!xendev->dev) {
-        snprintf(con->console, sizeof(con->console), "%s/console", dom);
-    } else {
-        snprintf(con->console, sizeof(con->console), "%s/device/console/%d", dom, xendev->dev);
-    }
-    free(dom);
-
-    type = xenstore_read_str(con->console, "type");
-    if (!type || strcmp(type, "ioemu") != 0) {
-       xen_be_printf(xendev, 1, "not for me (type=%s)\n", type);
-        ret = -1;
-        goto out;
-    }
-
-    output = xenstore_read_str(con->console, "output");
-
-    /* no Xen override, use qemu output device */
-    if (output == NULL) {
-        con->chr = serial_hds[con->xendev.dev];
-    } else {
-        snprintf(label, sizeof(label), "xencons%d", con->xendev.dev);
-        con->chr = qemu_chr_new(label, output, NULL);
-    }
-
-    xenstore_store_pv_console_info(con->xendev.dev, con->chr);
-
-out:
-    g_free(type);
-    return ret;
-}
-
-static int con_initialise(struct XenDevice *xendev)
-{
-    struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
-    int limit;
-
-    if (xenstore_read_int(con->console, "ring-ref", &con->ring_ref) == -1)
-       return -1;
-    if (xenstore_read_int(con->console, "port", &con->xendev.remote_port) == -1)
-       return -1;
-    if (xenstore_read_int(con->console, "limit", &limit) == 0)
-       con->buffer.max_capacity = limit;
-
-    if (!xendev->dev) {
-        con->sring = xc_map_foreign_range(xen_xc, con->xendev.dom,
-                                          XC_PAGE_SIZE,
-                                          PROT_READ|PROT_WRITE,
-                                          con->ring_ref);
-    } else {
-        con->sring = xc_gnttab_map_grant_ref(xendev->gnttabdev, con->xendev.dom,
-                                             con->ring_ref,
-                                             PROT_READ|PROT_WRITE);
-    }
-    if (!con->sring)
-       return -1;
-
-    xen_be_bind_evtchn(&con->xendev);
-    if (con->chr) {
-        if (qemu_chr_fe_claim(con->chr) == 0) {
-            qemu_chr_add_handlers(con->chr, xencons_can_receive,
-                                  xencons_receive, NULL, con);
-        } else {
-            xen_be_printf(xendev, 0,
-                          "xen_console_init error chardev %s already used\n",
-                          con->chr->label);
-            con->chr = NULL;
-        }
-    }
-
-    xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n",
-                 con->ring_ref,
-                 con->xendev.remote_port,
-                 con->xendev.local_port,
-                 con->buffer.max_capacity);
-    return 0;
-}
-
-static void con_disconnect(struct XenDevice *xendev)
-{
-    struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
-
-    if (!xendev->dev) {
-        return;
-    }
-    if (con->chr) {
-        qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL);
-        qemu_chr_fe_release(con->chr);
-    }
-    xen_be_unbind_evtchn(&con->xendev);
-
-    if (con->sring) {
-        if (!xendev->gnttabdev) {
-            munmap(con->sring, XC_PAGE_SIZE);
-        } else {
-            xc_gnttab_munmap(xendev->gnttabdev, con->sring, 1);
-        }
-       con->sring = NULL;
-    }
-}
-
-static void con_event(struct XenDevice *xendev)
-{
-    struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
-
-    buffer_append(con);
-    if (con->buffer.size - con->buffer.consumed)
-       xencons_send(con);
-}
-
-/* -------------------------------------------------------------------- */
-
-struct XenDevOps xen_console_ops = {
-    .size       = sizeof(struct XenConsole),
-    .flags      = DEVOPS_FLAG_IGNORE_STATE|DEVOPS_FLAG_NEED_GNTDEV,
-    .init       = con_init,
-    .initialise = con_initialise,
-    .event      = con_event,
-    .disconnect = con_disconnect,
-};
diff --git a/hw/xen_devconfig.c b/hw/xen_devconfig.c
deleted file mode 100644 (file)
index fa998ef..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-#include "hw/xen/xen_backend.h"
-#include "sysemu/blockdev.h"
-
-/* ------------------------------------------------------------- */
-
-struct xs_dirs {
-    char *xs_dir;
-    QTAILQ_ENTRY(xs_dirs) list;
-};
-static QTAILQ_HEAD(xs_dirs_head, xs_dirs) xs_cleanup = QTAILQ_HEAD_INITIALIZER(xs_cleanup);
-
-static void xen_config_cleanup_dir(char *dir)
-{
-    struct xs_dirs *d;
-
-    d = g_malloc(sizeof(*d));
-    d->xs_dir = dir;
-    QTAILQ_INSERT_TAIL(&xs_cleanup, d, list);
-}
-
-void xen_config_cleanup(void)
-{
-    struct xs_dirs *d;
-
-    QTAILQ_FOREACH(d, &xs_cleanup, list) {
-       xs_rm(xenstore, 0, d->xs_dir);
-    }
-}
-
-/* ------------------------------------------------------------- */
-
-static int xen_config_dev_mkdir(char *dev, int p)
-{
-    struct xs_permissions perms[2] = {{
-            .id    = 0, /* set owner: dom0 */
-        },{
-            .id    = xen_domid,
-            .perms = p,
-        }};
-
-    if (!xs_mkdir(xenstore, 0, dev)) {
-       xen_be_printf(NULL, 0, "xs_mkdir %s: failed\n", dev);
-       return -1;
-    }
-    xen_config_cleanup_dir(g_strdup(dev));
-
-    if (!xs_set_permissions(xenstore, 0, dev, perms, 2)) {
-       xen_be_printf(NULL, 0, "xs_set_permissions %s: failed\n", dev);
-       return -1;
-    }
-    return 0;
-}
-
-static int xen_config_dev_dirs(const char *ftype, const char *btype, int vdev,
-                              char *fe, char *be, int len)
-{
-    char *dom;
-
-    dom = xs_get_domain_path(xenstore, xen_domid);
-    snprintf(fe, len, "%s/device/%s/%d", dom, ftype, vdev);
-    free(dom);
-
-    dom = xs_get_domain_path(xenstore, 0);
-    snprintf(be, len, "%s/backend/%s/%d/%d", dom, btype, xen_domid, vdev);
-    free(dom);
-
-    xen_config_dev_mkdir(fe, XS_PERM_READ | XS_PERM_WRITE);
-    xen_config_dev_mkdir(be, XS_PERM_READ);
-    return 0;
-}
-
-static int xen_config_dev_all(char *fe, char *be)
-{
-    /* frontend */
-    if (xen_protocol)
-        xenstore_write_str(fe, "protocol", xen_protocol);
-
-    xenstore_write_int(fe, "state",           XenbusStateInitialising);
-    xenstore_write_int(fe, "backend-id",      0);
-    xenstore_write_str(fe, "backend",         be);
-
-    /* backend */
-    xenstore_write_str(be, "domain",          qemu_name ? qemu_name : "no-name");
-    xenstore_write_int(be, "online",          1);
-    xenstore_write_int(be, "state",           XenbusStateInitialising);
-    xenstore_write_int(be, "frontend-id",     xen_domid);
-    xenstore_write_str(be, "frontend",        fe);
-
-    return 0;
-}
-
-/* ------------------------------------------------------------- */
-
-int xen_config_dev_blk(DriveInfo *disk)
-{
-    char fe[256], be[256], device_name[32];
-    int vdev = 202 * 256 + 16 * disk->unit;
-    int cdrom = disk->media_cd;
-    const char *devtype = cdrom ? "cdrom" : "disk";
-    const char *mode    = cdrom ? "r"     : "w";
-    const char *filename = qemu_opt_get(disk->opts, "file");
-
-    snprintf(device_name, sizeof(device_name), "xvd%c", 'a' + disk->unit);
-    xen_be_printf(NULL, 1, "config disk %d [%s]: %s\n",
-                  disk->unit, device_name, filename);
-    xen_config_dev_dirs("vbd", "qdisk", vdev, fe, be, sizeof(fe));
-
-    /* frontend */
-    xenstore_write_int(fe, "virtual-device",  vdev);
-    xenstore_write_str(fe, "device-type",     devtype);
-
-    /* backend */
-    xenstore_write_str(be, "dev",             device_name);
-    xenstore_write_str(be, "type",            "file");
-    xenstore_write_str(be, "params",          filename);
-    xenstore_write_str(be, "mode",            mode);
-
-    /* common stuff */
-    return xen_config_dev_all(fe, be);
-}
-
-int xen_config_dev_nic(NICInfo *nic)
-{
-    char fe[256], be[256];
-    char mac[20];
-    int vlan_id = -1;
-
-    net_hub_id_for_client(nic->netdev, &vlan_id);
-    snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
-             nic->macaddr.a[0], nic->macaddr.a[1], nic->macaddr.a[2],
-             nic->macaddr.a[3], nic->macaddr.a[4], nic->macaddr.a[5]);
-    xen_be_printf(NULL, 1, "config nic %d: mac=\"%s\"\n", vlan_id, mac);
-    xen_config_dev_dirs("vif", "qnic", vlan_id, fe, be, sizeof(fe));
-
-    /* frontend */
-    xenstore_write_int(fe, "handle",     vlan_id);
-    xenstore_write_str(fe, "mac",        mac);
-
-    /* backend */
-    xenstore_write_int(be, "handle",     vlan_id);
-    xenstore_write_str(be, "mac",        mac);
-
-    /* common stuff */
-    return xen_config_dev_all(fe, be);
-}
-
-int xen_config_dev_vfb(int vdev, const char *type)
-{
-    char fe[256], be[256];
-
-    xen_config_dev_dirs("vfb", "vfb", vdev, fe, be, sizeof(fe));
-
-    /* backend */
-    xenstore_write_str(be, "type",  type);
-
-    /* common stuff */
-    return xen_config_dev_all(fe, be);
-}
-
-int xen_config_dev_vkbd(int vdev)
-{
-    char fe[256], be[256];
-
-    xen_config_dev_dirs("vkbd", "vkbd", vdev, fe, be, sizeof(fe));
-    return xen_config_dev_all(fe, be);
-}
-
-int xen_config_dev_console(int vdev)
-{
-    char fe[256], be[256];
-
-    xen_config_dev_dirs("console", "console", vdev, fe, be, sizeof(fe));
-    return xen_config_dev_all(fe, be);
-}
diff --git a/hw/xen_disk.c b/hw/xen_disk.c
deleted file mode 100644 (file)
index 532347b..0000000
+++ /dev/null
@@ -1,972 +0,0 @@
-/*
- *  xen paravirt block device backend
- *
- *  (c) Gerd Hoffmann <kraxel@redhat.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; under version 2 of the License.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- *  Contributions after 2012-01-13 are licensed under the terms of the
- *  GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <unistd.h>
-#include <signal.h>
-#include <inttypes.h>
-#include <time.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <sys/uio.h>
-
-#include "hw/hw.h"
-#include "hw/xen/xen_backend.h"
-#include "hw/xen_blkif.h"
-#include "sysemu/blockdev.h"
-
-/* ------------------------------------------------------------- */
-
-static int batch_maps   = 0;
-
-static int max_requests = 32;
-
-/* ------------------------------------------------------------- */
-
-#define BLOCK_SIZE  512
-#define IOCB_COUNT  (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2)
-
-struct PersistentGrant {
-    void *page;
-    struct XenBlkDev *blkdev;
-};
-
-typedef struct PersistentGrant PersistentGrant;
-
-struct ioreq {
-    blkif_request_t     req;
-    int16_t             status;
-
-    /* parsed request */
-    off_t               start;
-    QEMUIOVector        v;
-    int                 presync;
-    int                 postsync;
-    uint8_t             mapped;
-
-    /* grant mapping */
-    uint32_t            domids[BLKIF_MAX_SEGMENTS_PER_REQUEST];
-    uint32_t            refs[BLKIF_MAX_SEGMENTS_PER_REQUEST];
-    int                 prot;
-    void                *page[BLKIF_MAX_SEGMENTS_PER_REQUEST];
-    void                *pages;
-    int                 num_unmap;
-
-    /* aio status */
-    int                 aio_inflight;
-    int                 aio_errors;
-
-    struct XenBlkDev    *blkdev;
-    QLIST_ENTRY(ioreq)   list;
-    BlockAcctCookie     acct;
-};
-
-struct XenBlkDev {
-    struct XenDevice    xendev;  /* must be first */
-    char                *params;
-    char                *mode;
-    char                *type;
-    char                *dev;
-    char                *devtype;
-    const char          *fileproto;
-    const char          *filename;
-    int                 ring_ref;
-    void                *sring;
-    int64_t             file_blk;
-    int64_t             file_size;
-    int                 protocol;
-    blkif_back_rings_t  rings;
-    int                 more_work;
-    int                 cnt_map;
-
-    /* request lists */
-    QLIST_HEAD(inflight_head, ioreq) inflight;
-    QLIST_HEAD(finished_head, ioreq) finished;
-    QLIST_HEAD(freelist_head, ioreq) freelist;
-    int                 requests_total;
-    int                 requests_inflight;
-    int                 requests_finished;
-
-    /* Persistent grants extension */
-    gboolean            feature_persistent;
-    GTree               *persistent_gnts;
-    unsigned int        persistent_gnt_count;
-    unsigned int        max_grants;
-
-    /* qemu block driver */
-    DriveInfo           *dinfo;
-    BlockDriverState    *bs;
-    QEMUBH              *bh;
-};
-
-/* ------------------------------------------------------------- */
-
-static void ioreq_reset(struct ioreq *ioreq)
-{
-    memset(&ioreq->req, 0, sizeof(ioreq->req));
-    ioreq->status = 0;
-    ioreq->start = 0;
-    ioreq->presync = 0;
-    ioreq->postsync = 0;
-    ioreq->mapped = 0;
-
-    memset(ioreq->domids, 0, sizeof(ioreq->domids));
-    memset(ioreq->refs, 0, sizeof(ioreq->refs));
-    ioreq->prot = 0;
-    memset(ioreq->page, 0, sizeof(ioreq->page));
-    ioreq->pages = NULL;
-
-    ioreq->aio_inflight = 0;
-    ioreq->aio_errors = 0;
-
-    ioreq->blkdev = NULL;
-    memset(&ioreq->list, 0, sizeof(ioreq->list));
-    memset(&ioreq->acct, 0, sizeof(ioreq->acct));
-
-    qemu_iovec_reset(&ioreq->v);
-}
-
-static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
-{
-    uint ua = GPOINTER_TO_UINT(a);
-    uint ub = GPOINTER_TO_UINT(b);
-    return (ua > ub) - (ua < ub);
-}
-
-static void destroy_grant(gpointer pgnt)
-{
-    PersistentGrant *grant = pgnt;
-    XenGnttab gnt = grant->blkdev->xendev.gnttabdev;
-
-    if (xc_gnttab_munmap(gnt, grant->page, 1) != 0) {
-        xen_be_printf(&grant->blkdev->xendev, 0,
-                      "xc_gnttab_munmap failed: %s\n",
-                      strerror(errno));
-    }
-    grant->blkdev->persistent_gnt_count--;
-    xen_be_printf(&grant->blkdev->xendev, 3,
-                  "unmapped grant %p\n", grant->page);
-    g_free(grant);
-}
-
-static struct ioreq *ioreq_start(struct XenBlkDev *blkdev)
-{
-    struct ioreq *ioreq = NULL;
-
-    if (QLIST_EMPTY(&blkdev->freelist)) {
-        if (blkdev->requests_total >= max_requests) {
-            goto out;
-        }
-        /* allocate new struct */
-        ioreq = g_malloc0(sizeof(*ioreq));
-        ioreq->blkdev = blkdev;
-        blkdev->requests_total++;
-        qemu_iovec_init(&ioreq->v, BLKIF_MAX_SEGMENTS_PER_REQUEST);
-    } else {
-        /* get one from freelist */
-        ioreq = QLIST_FIRST(&blkdev->freelist);
-        QLIST_REMOVE(ioreq, list);
-    }
-    QLIST_INSERT_HEAD(&blkdev->inflight, ioreq, list);
-    blkdev->requests_inflight++;
-
-out:
-    return ioreq;
-}
-
-static void ioreq_finish(struct ioreq *ioreq)
-{
-    struct XenBlkDev *blkdev = ioreq->blkdev;
-
-    QLIST_REMOVE(ioreq, list);
-    QLIST_INSERT_HEAD(&blkdev->finished, ioreq, list);
-    blkdev->requests_inflight--;
-    blkdev->requests_finished++;
-}
-
-static void ioreq_release(struct ioreq *ioreq, bool finish)
-{
-    struct XenBlkDev *blkdev = ioreq->blkdev;
-
-    QLIST_REMOVE(ioreq, list);
-    ioreq_reset(ioreq);
-    ioreq->blkdev = blkdev;
-    QLIST_INSERT_HEAD(&blkdev->freelist, ioreq, list);
-    if (finish) {
-        blkdev->requests_finished--;
-    } else {
-        blkdev->requests_inflight--;
-    }
-}
-
-/*
- * translate request into iovec + start offset
- * do sanity checks along the way
- */
-static int ioreq_parse(struct ioreq *ioreq)
-{
-    struct XenBlkDev *blkdev = ioreq->blkdev;
-    uintptr_t mem;
-    size_t len;
-    int i;
-
-    xen_be_printf(&blkdev->xendev, 3,
-                  "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n",
-                  ioreq->req.operation, ioreq->req.nr_segments,
-                  ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number);
-    switch (ioreq->req.operation) {
-    case BLKIF_OP_READ:
-        ioreq->prot = PROT_WRITE; /* to memory */
-        break;
-    case BLKIF_OP_FLUSH_DISKCACHE:
-        ioreq->presync = 1;
-        if (!ioreq->req.nr_segments) {
-            return 0;
-        }
-        /* fall through */
-    case BLKIF_OP_WRITE:
-        ioreq->prot = PROT_READ; /* from memory */
-        break;
-    default:
-        xen_be_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n",
-                      ioreq->req.operation);
-        goto err;
-    };
-
-    if (ioreq->req.operation != BLKIF_OP_READ && blkdev->mode[0] != 'w') {
-        xen_be_printf(&blkdev->xendev, 0, "error: write req for ro device\n");
-        goto err;
-    }
-
-    ioreq->start = ioreq->req.sector_number * blkdev->file_blk;
-    for (i = 0; i < ioreq->req.nr_segments; i++) {
-        if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) {
-            xen_be_printf(&blkdev->xendev, 0, "error: nr_segments too big\n");
-            goto err;
-        }
-        if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) {
-            xen_be_printf(&blkdev->xendev, 0, "error: first > last sector\n");
-            goto err;
-        }
-        if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) {
-            xen_be_printf(&blkdev->xendev, 0, "error: page crossing\n");
-            goto err;
-        }
-
-        ioreq->domids[i] = blkdev->xendev.dom;
-        ioreq->refs[i]   = ioreq->req.seg[i].gref;
-
-        mem = ioreq->req.seg[i].first_sect * blkdev->file_blk;
-        len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk;
-        qemu_iovec_add(&ioreq->v, (void*)mem, len);
-    }
-    if (ioreq->start + ioreq->v.size > blkdev->file_size) {
-        xen_be_printf(&blkdev->xendev, 0, "error: access beyond end of file\n");
-        goto err;
-    }
-    return 0;
-
-err:
-    ioreq->status = BLKIF_RSP_ERROR;
-    return -1;
-}
-
-static void ioreq_unmap(struct ioreq *ioreq)
-{
-    XenGnttab gnt = ioreq->blkdev->xendev.gnttabdev;
-    int i;
-
-    if (ioreq->num_unmap == 0 || ioreq->mapped == 0) {
-        return;
-    }
-    if (batch_maps) {
-        if (!ioreq->pages) {
-            return;
-        }
-        if (xc_gnttab_munmap(gnt, ioreq->pages, ioreq->num_unmap) != 0) {
-            xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n",
-                          strerror(errno));
-        }
-        ioreq->blkdev->cnt_map -= ioreq->num_unmap;
-        ioreq->pages = NULL;
-    } else {
-        for (i = 0; i < ioreq->num_unmap; i++) {
-            if (!ioreq->page[i]) {
-                continue;
-            }
-            if (xc_gnttab_munmap(gnt, ioreq->page[i], 1) != 0) {
-                xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n",
-                              strerror(errno));
-            }
-            ioreq->blkdev->cnt_map--;
-            ioreq->page[i] = NULL;
-        }
-    }
-    ioreq->mapped = 0;
-}
-
-static int ioreq_map(struct ioreq *ioreq)
-{
-    XenGnttab gnt = ioreq->blkdev->xendev.gnttabdev;
-    uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST];
-    uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST];
-    void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST];
-    int i, j, new_maps = 0;
-    PersistentGrant *grant;
-    /* domids and refs variables will contain the information necessary
-     * to map the grants that are needed to fulfill this request.
-     *
-     * After mapping the needed grants, the page array will contain the
-     * memory address of each granted page in the order specified in ioreq
-     * (disregarding if it's a persistent grant or not).
-     */
-
-    if (ioreq->v.niov == 0 || ioreq->mapped == 1) {
-        return 0;
-    }
-    if (ioreq->blkdev->feature_persistent) {
-        for (i = 0; i < ioreq->v.niov; i++) {
-            grant = g_tree_lookup(ioreq->blkdev->persistent_gnts,
-                                    GUINT_TO_POINTER(ioreq->refs[i]));
-
-            if (grant != NULL) {
-                page[i] = grant->page;
-                xen_be_printf(&ioreq->blkdev->xendev, 3,
-                              "using persistent-grant %" PRIu32 "\n",
-                              ioreq->refs[i]);
-            } else {
-                    /* Add the grant to the list of grants that
-                     * should be mapped
-                     */
-                    domids[new_maps] = ioreq->domids[i];
-                    refs[new_maps] = ioreq->refs[i];
-                    page[i] = NULL;
-                    new_maps++;
-            }
-        }
-        /* Set the protection to RW, since grants may be reused later
-         * with a different protection than the one needed for this request
-         */
-        ioreq->prot = PROT_WRITE | PROT_READ;
-    } else {
-        /* All grants in the request should be mapped */
-        memcpy(refs, ioreq->refs, sizeof(refs));
-        memcpy(domids, ioreq->domids, sizeof(domids));
-        memset(page, 0, sizeof(page));
-        new_maps = ioreq->v.niov;
-    }
-
-    if (batch_maps && new_maps) {
-        ioreq->pages = xc_gnttab_map_grant_refs
-            (gnt, new_maps, domids, refs, ioreq->prot);
-        if (ioreq->pages == NULL) {
-            xen_be_printf(&ioreq->blkdev->xendev, 0,
-                          "can't map %d grant refs (%s, %d maps)\n",
-                          new_maps, strerror(errno), ioreq->blkdev->cnt_map);
-            return -1;
-        }
-        for (i = 0, j = 0; i < ioreq->v.niov; i++) {
-            if (page[i] == NULL) {
-                page[i] = ioreq->pages + (j++) * XC_PAGE_SIZE;
-            }
-        }
-        ioreq->blkdev->cnt_map += new_maps;
-    } else if (new_maps)  {
-        for (i = 0; i < new_maps; i++) {
-            ioreq->page[i] = xc_gnttab_map_grant_ref
-                (gnt, domids[i], refs[i], ioreq->prot);
-            if (ioreq->page[i] == NULL) {
-                xen_be_printf(&ioreq->blkdev->xendev, 0,
-                              "can't map grant ref %d (%s, %d maps)\n",
-                              refs[i], strerror(errno), ioreq->blkdev->cnt_map);
-                ioreq_unmap(ioreq);
-                return -1;
-            }
-            ioreq->blkdev->cnt_map++;
-        }
-        for (i = 0, j = 0; i < ioreq->v.niov; i++) {
-            if (page[i] == NULL) {
-                page[i] = ioreq->page[j++];
-            }
-        }
-    }
-    if (ioreq->blkdev->feature_persistent) {
-        while ((ioreq->blkdev->persistent_gnt_count < ioreq->blkdev->max_grants)
-              && new_maps) {
-            /* Go through the list of newly mapped grants and add as many
-             * as possible to the list of persistently mapped grants.
-             *
-             * Since we start at the end of ioreq->page(s), we only need
-             * to decrease new_maps to prevent this granted pages from
-             * being unmapped in ioreq_unmap.
-             */
-            grant = g_malloc0(sizeof(*grant));
-            new_maps--;
-            if (batch_maps) {
-                grant->page = ioreq->pages + (new_maps) * XC_PAGE_SIZE;
-            } else {
-                grant->page = ioreq->page[new_maps];
-            }
-            grant->blkdev = ioreq->blkdev;
-            xen_be_printf(&ioreq->blkdev->xendev, 3,
-                          "adding grant %" PRIu32 " page: %p\n",
-                          refs[new_maps], grant->page);
-            g_tree_insert(ioreq->blkdev->persistent_gnts,
-                          GUINT_TO_POINTER(refs[new_maps]),
-                          grant);
-            ioreq->blkdev->persistent_gnt_count++;
-        }
-    }
-    for (i = 0; i < ioreq->v.niov; i++) {
-        ioreq->v.iov[i].iov_base += (uintptr_t)page[i];
-    }
-    ioreq->mapped = 1;
-    ioreq->num_unmap = new_maps;
-    return 0;
-}
-
-static int ioreq_runio_qemu_aio(struct ioreq *ioreq);
-
-static void qemu_aio_complete(void *opaque, int ret)
-{
-    struct ioreq *ioreq = opaque;
-
-    if (ret != 0) {
-        xen_be_printf(&ioreq->blkdev->xendev, 0, "%s I/O error\n",
-                      ioreq->req.operation == BLKIF_OP_READ ? "read" : "write");
-        ioreq->aio_errors++;
-    }
-
-    ioreq->aio_inflight--;
-    if (ioreq->presync) {
-        ioreq->presync = 0;
-        ioreq_runio_qemu_aio(ioreq);
-        return;
-    }
-    if (ioreq->aio_inflight > 0) {
-        return;
-    }
-    if (ioreq->postsync) {
-        ioreq->postsync = 0;
-        ioreq->aio_inflight++;
-        bdrv_aio_flush(ioreq->blkdev->bs, qemu_aio_complete, ioreq);
-        return;
-    }
-
-    ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY;
-    ioreq_unmap(ioreq);
-    ioreq_finish(ioreq);
-    bdrv_acct_done(ioreq->blkdev->bs, &ioreq->acct);
-    qemu_bh_schedule(ioreq->blkdev->bh);
-}
-
-static int ioreq_runio_qemu_aio(struct ioreq *ioreq)
-{
-    struct XenBlkDev *blkdev = ioreq->blkdev;
-
-    if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) {
-        goto err_no_map;
-    }
-
-    ioreq->aio_inflight++;
-    if (ioreq->presync) {
-        bdrv_aio_flush(ioreq->blkdev->bs, qemu_aio_complete, ioreq);
-        return 0;
-    }
-
-    switch (ioreq->req.operation) {
-    case BLKIF_OP_READ:
-        bdrv_acct_start(blkdev->bs, &ioreq->acct, ioreq->v.size, BDRV_ACCT_READ);
-        ioreq->aio_inflight++;
-        bdrv_aio_readv(blkdev->bs, ioreq->start / BLOCK_SIZE,
-                       &ioreq->v, ioreq->v.size / BLOCK_SIZE,
-                       qemu_aio_complete, ioreq);
-        break;
-    case BLKIF_OP_WRITE:
-    case BLKIF_OP_FLUSH_DISKCACHE:
-        if (!ioreq->req.nr_segments) {
-            break;
-        }
-
-        bdrv_acct_start(blkdev->bs, &ioreq->acct, ioreq->v.size, BDRV_ACCT_WRITE);
-        ioreq->aio_inflight++;
-        bdrv_aio_writev(blkdev->bs, ioreq->start / BLOCK_SIZE,
-                        &ioreq->v, ioreq->v.size / BLOCK_SIZE,
-                        qemu_aio_complete, ioreq);
-        break;
-    default:
-        /* unknown operation (shouldn't happen -- parse catches this) */
-        goto err;
-    }
-
-    qemu_aio_complete(ioreq, 0);
-
-    return 0;
-
-err:
-    ioreq_unmap(ioreq);
-err_no_map:
-    ioreq_finish(ioreq);
-    ioreq->status = BLKIF_RSP_ERROR;
-    return -1;
-}
-
-static int blk_send_response_one(struct ioreq *ioreq)
-{
-    struct XenBlkDev  *blkdev = ioreq->blkdev;
-    int               send_notify   = 0;
-    int               have_requests = 0;
-    blkif_response_t  resp;
-    void              *dst;
-
-    resp.id        = ioreq->req.id;
-    resp.operation = ioreq->req.operation;
-    resp.status    = ioreq->status;
-
-    /* Place on the response ring for the relevant domain. */
-    switch (blkdev->protocol) {
-    case BLKIF_PROTOCOL_NATIVE:
-        dst = RING_GET_RESPONSE(&blkdev->rings.native, blkdev->rings.native.rsp_prod_pvt);
-        break;
-    case BLKIF_PROTOCOL_X86_32:
-        dst = RING_GET_RESPONSE(&blkdev->rings.x86_32_part,
-                                blkdev->rings.x86_32_part.rsp_prod_pvt);
-        break;
-    case BLKIF_PROTOCOL_X86_64:
-        dst = RING_GET_RESPONSE(&blkdev->rings.x86_64_part,
-                                blkdev->rings.x86_64_part.rsp_prod_pvt);
-        break;
-    default:
-        dst = NULL;
-    }
-    memcpy(dst, &resp, sizeof(resp));
-    blkdev->rings.common.rsp_prod_pvt++;
-
-    RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify);
-    if (blkdev->rings.common.rsp_prod_pvt == blkdev->rings.common.req_cons) {
-        /*
-         * Tail check for pending requests. Allows frontend to avoid
-         * notifications if requests are already in flight (lower
-         * overheads and promotes batching).
-         */
-        RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests);
-    } else if (RING_HAS_UNCONSUMED_REQUESTS(&blkdev->rings.common)) {
-        have_requests = 1;
-    }
-
-    if (have_requests) {
-        blkdev->more_work++;
-    }
-    return send_notify;
-}
-
-/* walk finished list, send outstanding responses, free requests */
-static void blk_send_response_all(struct XenBlkDev *blkdev)
-{
-    struct ioreq *ioreq;
-    int send_notify = 0;
-
-    while (!QLIST_EMPTY(&blkdev->finished)) {
-        ioreq = QLIST_FIRST(&blkdev->finished);
-        send_notify += blk_send_response_one(ioreq);
-        ioreq_release(ioreq, true);
-    }
-    if (send_notify) {
-        xen_be_send_notify(&blkdev->xendev);
-    }
-}
-
-static int blk_get_request(struct XenBlkDev *blkdev, struct ioreq *ioreq, RING_IDX rc)
-{
-    switch (blkdev->protocol) {
-    case BLKIF_PROTOCOL_NATIVE:
-        memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc),
-               sizeof(ioreq->req));
-        break;
-    case BLKIF_PROTOCOL_X86_32:
-        blkif_get_x86_32_req(&ioreq->req,
-                             RING_GET_REQUEST(&blkdev->rings.x86_32_part, rc));
-        break;
-    case BLKIF_PROTOCOL_X86_64:
-        blkif_get_x86_64_req(&ioreq->req,
-                             RING_GET_REQUEST(&blkdev->rings.x86_64_part, rc));
-        break;
-    }
-    return 0;
-}
-
-static void blk_handle_requests(struct XenBlkDev *blkdev)
-{
-    RING_IDX rc, rp;
-    struct ioreq *ioreq;
-
-    blkdev->more_work = 0;
-
-    rc = blkdev->rings.common.req_cons;
-    rp = blkdev->rings.common.sring->req_prod;
-    xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
-
-    blk_send_response_all(blkdev);
-    while (rc != rp) {
-        /* pull request from ring */
-        if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) {
-            break;
-        }
-        ioreq = ioreq_start(blkdev);
-        if (ioreq == NULL) {
-            blkdev->more_work++;
-            break;
-        }
-        blk_get_request(blkdev, ioreq, rc);
-        blkdev->rings.common.req_cons = ++rc;
-
-        /* parse them */
-        if (ioreq_parse(ioreq) != 0) {
-            if (blk_send_response_one(ioreq)) {
-                xen_be_send_notify(&blkdev->xendev);
-            }
-            ioreq_release(ioreq, false);
-            continue;
-        }
-
-        ioreq_runio_qemu_aio(ioreq);
-    }
-
-    if (blkdev->more_work && blkdev->requests_inflight < max_requests) {
-        qemu_bh_schedule(blkdev->bh);
-    }
-}
-
-/* ------------------------------------------------------------- */
-
-static void blk_bh(void *opaque)
-{
-    struct XenBlkDev *blkdev = opaque;
-    blk_handle_requests(blkdev);
-}
-
-/*
- * We need to account for the grant allocations requiring contiguous
- * chunks; the worst case number would be
- *     max_req * max_seg + (max_req - 1) * (max_seg - 1) + 1,
- * but in order to keep things simple just use
- *     2 * max_req * max_seg.
- */
-#define MAX_GRANTS(max_req, max_seg) (2 * (max_req) * (max_seg))
-
-static void blk_alloc(struct XenDevice *xendev)
-{
-    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
-
-    QLIST_INIT(&blkdev->inflight);
-    QLIST_INIT(&blkdev->finished);
-    QLIST_INIT(&blkdev->freelist);
-    blkdev->bh = qemu_bh_new(blk_bh, blkdev);
-    if (xen_mode != XEN_EMULATE) {
-        batch_maps = 1;
-    }
-    if (xc_gnttab_set_max_grants(xendev->gnttabdev,
-            MAX_GRANTS(max_requests, BLKIF_MAX_SEGMENTS_PER_REQUEST)) < 0) {
-        xen_be_printf(xendev, 0, "xc_gnttab_set_max_grants failed: %s\n",
-                      strerror(errno));
-    }
-}
-
-static int blk_init(struct XenDevice *xendev)
-{
-    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
-    int info = 0;
-
-    /* read xenstore entries */
-    if (blkdev->params == NULL) {
-        char *h = NULL;
-        blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params");
-        if (blkdev->params != NULL) {
-            h = strchr(blkdev->params, ':');
-        }
-        if (h != NULL) {
-            blkdev->fileproto = blkdev->params;
-            blkdev->filename  = h+1;
-            *h = 0;
-        } else {
-            blkdev->fileproto = "<unset>";
-            blkdev->filename  = blkdev->params;
-        }
-    }
-    if (!strcmp("aio", blkdev->fileproto)) {
-        blkdev->fileproto = "raw";
-    }
-    if (blkdev->mode == NULL) {
-        blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode");
-    }
-    if (blkdev->type == NULL) {
-        blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type");
-    }
-    if (blkdev->dev == NULL) {
-        blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev");
-    }
-    if (blkdev->devtype == NULL) {
-        blkdev->devtype = xenstore_read_be_str(&blkdev->xendev, "device-type");
-    }
-
-    /* do we have all we need? */
-    if (blkdev->params == NULL ||
-        blkdev->mode == NULL   ||
-        blkdev->type == NULL   ||
-        blkdev->dev == NULL) {
-        goto out_error;
-    }
-
-    /* read-only ? */
-    if (strcmp(blkdev->mode, "w")) {
-        info  |= VDISK_READONLY;
-    }
-
-    /* cdrom ? */
-    if (blkdev->devtype && !strcmp(blkdev->devtype, "cdrom")) {
-        info  |= VDISK_CDROM;
-    }
-
-    blkdev->file_blk  = BLOCK_SIZE;
-
-    /* fill info
-     * blk_connect supplies sector-size and sectors
-     */
-    xenstore_write_be_int(&blkdev->xendev, "feature-flush-cache", 1);
-    xenstore_write_be_int(&blkdev->xendev, "feature-persistent", 1);
-    xenstore_write_be_int(&blkdev->xendev, "info", info);
-    return 0;
-
-out_error:
-    g_free(blkdev->params);
-    blkdev->params = NULL;
-    g_free(blkdev->mode);
-    blkdev->mode = NULL;
-    g_free(blkdev->type);
-    blkdev->type = NULL;
-    g_free(blkdev->dev);
-    blkdev->dev = NULL;
-    g_free(blkdev->devtype);
-    blkdev->devtype = NULL;
-    return -1;
-}
-
-static int blk_connect(struct XenDevice *xendev)
-{
-    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
-    int pers, index, qflags;
-
-    /* read-only ? */
-    qflags = BDRV_O_CACHE_WB | BDRV_O_NATIVE_AIO;
-    if (strcmp(blkdev->mode, "w") == 0) {
-        qflags |= BDRV_O_RDWR;
-    }
-
-    /* init qemu block driver */
-    index = (blkdev->xendev.dev - 202 * 256) / 16;
-    blkdev->dinfo = drive_get(IF_XEN, 0, index);
-    if (!blkdev->dinfo) {
-        /* setup via xenbus -> create new block driver instance */
-        xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n");
-        blkdev->bs = bdrv_new(blkdev->dev);
-        if (blkdev->bs) {
-            if (bdrv_open(blkdev->bs, blkdev->filename, NULL, qflags,
-                        bdrv_find_whitelisted_format(blkdev->fileproto)) != 0) {
-                bdrv_delete(blkdev->bs);
-                blkdev->bs = NULL;
-            }
-        }
-        if (!blkdev->bs) {
-            return -1;
-        }
-    } else {
-        /* setup via qemu cmdline -> already setup for us */
-        xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n");
-        blkdev->bs = blkdev->dinfo->bdrv;
-    }
-    bdrv_attach_dev_nofail(blkdev->bs, blkdev);
-    blkdev->file_size = bdrv_getlength(blkdev->bs);
-    if (blkdev->file_size < 0) {
-        xen_be_printf(&blkdev->xendev, 1, "bdrv_getlength: %d (%s) | drv %s\n",
-                      (int)blkdev->file_size, strerror(-blkdev->file_size),
-                      bdrv_get_format_name(blkdev->bs) ?: "-");
-        blkdev->file_size = 0;
-    }
-
-    xen_be_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\","
-                  " size %" PRId64 " (%" PRId64 " MB)\n",
-                  blkdev->type, blkdev->fileproto, blkdev->filename,
-                  blkdev->file_size, blkdev->file_size >> 20);
-
-    /* Fill in number of sector size and number of sectors */
-    xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk);
-    xenstore_write_be_int64(&blkdev->xendev, "sectors",
-                            blkdev->file_size / blkdev->file_blk);
-
-    if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref) == -1) {
-        return -1;
-    }
-    if (xenstore_read_fe_int(&blkdev->xendev, "event-channel",
-                             &blkdev->xendev.remote_port) == -1) {
-        return -1;
-    }
-    if (xenstore_read_fe_int(&blkdev->xendev, "feature-persistent", &pers)) {
-        blkdev->feature_persistent = FALSE;
-    } else {
-        blkdev->feature_persistent = !!pers;
-    }
-
-    blkdev->protocol = BLKIF_PROTOCOL_NATIVE;
-    if (blkdev->xendev.protocol) {
-        if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32) == 0) {
-            blkdev->protocol = BLKIF_PROTOCOL_X86_32;
-        }
-        if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64) == 0) {
-            blkdev->protocol = BLKIF_PROTOCOL_X86_64;
-        }
-    }
-
-    blkdev->sring = xc_gnttab_map_grant_ref(blkdev->xendev.gnttabdev,
-                                            blkdev->xendev.dom,
-                                            blkdev->ring_ref,
-                                            PROT_READ | PROT_WRITE);
-    if (!blkdev->sring) {
-        return -1;
-    }
-    blkdev->cnt_map++;
-
-    switch (blkdev->protocol) {
-    case BLKIF_PROTOCOL_NATIVE:
-    {
-        blkif_sring_t *sring_native = blkdev->sring;
-        BACK_RING_INIT(&blkdev->rings.native, sring_native, XC_PAGE_SIZE);
-        break;
-    }
-    case BLKIF_PROTOCOL_X86_32:
-    {
-        blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring;
-
-        BACK_RING_INIT(&blkdev->rings.x86_32_part, sring_x86_32, XC_PAGE_SIZE);
-        break;
-    }
-    case BLKIF_PROTOCOL_X86_64:
-    {
-        blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring;
-
-        BACK_RING_INIT(&blkdev->rings.x86_64_part, sring_x86_64, XC_PAGE_SIZE);
-        break;
-    }
-    }
-
-    if (blkdev->feature_persistent) {
-        /* Init persistent grants */
-        blkdev->max_grants = max_requests * BLKIF_MAX_SEGMENTS_PER_REQUEST;
-        blkdev->persistent_gnts = g_tree_new_full((GCompareDataFunc)int_cmp,
-                                             NULL, NULL,
-                                             (GDestroyNotify)destroy_grant);
-        blkdev->persistent_gnt_count = 0;
-    }
-
-    xen_be_bind_evtchn(&blkdev->xendev);
-
-    xen_be_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, "
-                  "remote port %d, local port %d\n",
-                  blkdev->xendev.protocol, blkdev->ring_ref,
-                  blkdev->xendev.remote_port, blkdev->xendev.local_port);
-    return 0;
-}
-
-static void blk_disconnect(struct XenDevice *xendev)
-{
-    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
-
-    if (blkdev->bs) {
-        if (!blkdev->dinfo) {
-            /* close/delete only if we created it ourself */
-            bdrv_close(blkdev->bs);
-            bdrv_detach_dev(blkdev->bs, blkdev);
-            bdrv_delete(blkdev->bs);
-        }
-        blkdev->bs = NULL;
-    }
-    xen_be_unbind_evtchn(&blkdev->xendev);
-
-    if (blkdev->sring) {
-        xc_gnttab_munmap(blkdev->xendev.gnttabdev, blkdev->sring, 1);
-        blkdev->cnt_map--;
-        blkdev->sring = NULL;
-    }
-}
-
-static int blk_free(struct XenDevice *xendev)
-{
-    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
-    struct ioreq *ioreq;
-
-    if (blkdev->bs || blkdev->sring) {
-        blk_disconnect(xendev);
-    }
-
-    /* Free persistent grants */
-    if (blkdev->feature_persistent) {
-        g_tree_destroy(blkdev->persistent_gnts);
-    }
-
-    while (!QLIST_EMPTY(&blkdev->freelist)) {
-        ioreq = QLIST_FIRST(&blkdev->freelist);
-        QLIST_REMOVE(ioreq, list);
-        qemu_iovec_destroy(&ioreq->v);
-        g_free(ioreq);
-    }
-
-    g_free(blkdev->params);
-    g_free(blkdev->mode);
-    g_free(blkdev->type);
-    g_free(blkdev->dev);
-    g_free(blkdev->devtype);
-    qemu_bh_delete(blkdev->bh);
-    return 0;
-}
-
-static void blk_event(struct XenDevice *xendev)
-{
-    struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
-
-    qemu_bh_schedule(blkdev->bh);
-}
-
-struct XenDevOps xen_blkdev_ops = {
-    .size       = sizeof(struct XenBlkDev),
-    .flags      = DEVOPS_FLAG_NEED_GNTDEV,
-    .alloc      = blk_alloc,
-    .init       = blk_init,
-    .initialise    = blk_connect,
-    .disconnect = blk_disconnect,
-    .event      = blk_event,
-    .free       = blk_free,
-};
diff --git a/hw/xen_nic.c b/hw/xen_nic.c
deleted file mode 100644 (file)
index 63918ae..0000000
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- *  xen paravirt network card backend
- *
- *  (c) Gerd Hoffmann <kraxel@redhat.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; under version 2 of the License.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- *  Contributions after 2012-01-13 are licensed under the terms of the
- *  GNU GPL, version 2 or (at your option) any later version.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <unistd.h>
-#include <signal.h>
-#include <inttypes.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <sys/wait.h>
-
-#include "hw/hw.h"
-#include "net/net.h"
-#include "net/checksum.h"
-#include "net/util.h"
-#include "hw/xen/xen_backend.h"
-
-#include <xen/io/netif.h>
-
-/* ------------------------------------------------------------- */
-
-struct XenNetDev {
-    struct XenDevice      xendev;  /* must be first */
-    char                  *mac;
-    int                   tx_work;
-    int                   tx_ring_ref;
-    int                   rx_ring_ref;
-    struct netif_tx_sring *txs;
-    struct netif_rx_sring *rxs;
-    netif_tx_back_ring_t  tx_ring;
-    netif_rx_back_ring_t  rx_ring;
-    NICConf               conf;
-    NICState              *nic;
-};
-
-/* ------------------------------------------------------------- */
-
-static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, int8_t st)
-{
-    RING_IDX i = netdev->tx_ring.rsp_prod_pvt;
-    netif_tx_response_t *resp;
-    int notify;
-
-    resp = RING_GET_RESPONSE(&netdev->tx_ring, i);
-    resp->id     = txp->id;
-    resp->status = st;
-
-#if 0
-    if (txp->flags & NETTXF_extra_info) {
-        RING_GET_RESPONSE(&netdev->tx_ring, ++i)->status = NETIF_RSP_NULL;
-    }
-#endif
-
-    netdev->tx_ring.rsp_prod_pvt = ++i;
-    RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify);
-    if (notify) {
-        xen_be_send_notify(&netdev->xendev);
-    }
-
-    if (i == netdev->tx_ring.req_cons) {
-        int more_to_do;
-        RING_FINAL_CHECK_FOR_REQUESTS(&netdev->tx_ring, more_to_do);
-        if (more_to_do) {
-            netdev->tx_work++;
-        }
-    }
-}
-
-static void net_tx_error(struct XenNetDev *netdev, netif_tx_request_t *txp, RING_IDX end)
-{
-#if 0
-    /*
-     * Hmm, why netback fails everything in the ring?
-     * Should we do that even when not supporting SG and TSO?
-     */
-    RING_IDX cons = netdev->tx_ring.req_cons;
-
-    do {
-        make_tx_response(netif, txp, NETIF_RSP_ERROR);
-        if (cons >= end) {
-            break;
-        }
-        txp = RING_GET_REQUEST(&netdev->tx_ring, cons++);
-    } while (1);
-    netdev->tx_ring.req_cons = cons;
-    netif_schedule_work(netif);
-    netif_put(netif);
-#else
-    net_tx_response(netdev, txp, NETIF_RSP_ERROR);
-#endif
-}
-
-static void net_tx_packets(struct XenNetDev *netdev)
-{
-    netif_tx_request_t txreq;
-    RING_IDX rc, rp;
-    void *page;
-    void *tmpbuf = NULL;
-
-    for (;;) {
-        rc = netdev->tx_ring.req_cons;
-        rp = netdev->tx_ring.sring->req_prod;
-        xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
-
-        while ((rc != rp)) {
-            if (RING_REQUEST_CONS_OVERFLOW(&netdev->tx_ring, rc)) {
-                break;
-            }
-            memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq));
-            netdev->tx_ring.req_cons = ++rc;
-
-#if 1
-            /* should not happen in theory, we don't announce the *
-             * feature-{sg,gso,whatelse} flags in xenstore (yet?) */
-            if (txreq.flags & NETTXF_extra_info) {
-                xen_be_printf(&netdev->xendev, 0, "FIXME: extra info flag\n");
-                net_tx_error(netdev, &txreq, rc);
-                continue;
-            }
-            if (txreq.flags & NETTXF_more_data) {
-                xen_be_printf(&netdev->xendev, 0, "FIXME: more data flag\n");
-                net_tx_error(netdev, &txreq, rc);
-                continue;
-            }
-#endif
-
-            if (txreq.size < 14) {
-                xen_be_printf(&netdev->xendev, 0, "bad packet size: %d\n", txreq.size);
-                net_tx_error(netdev, &txreq, rc);
-                continue;
-            }
-
-            if ((txreq.offset + txreq.size) > XC_PAGE_SIZE) {
-                xen_be_printf(&netdev->xendev, 0, "error: page crossing\n");
-                net_tx_error(netdev, &txreq, rc);
-                continue;
-            }
-
-            xen_be_printf(&netdev->xendev, 3, "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n",
-                          txreq.gref, txreq.offset, txreq.size, txreq.flags,
-                          (txreq.flags & NETTXF_csum_blank)     ? " csum_blank"     : "",
-                          (txreq.flags & NETTXF_data_validated) ? " data_validated" : "",
-                          (txreq.flags & NETTXF_more_data)      ? " more_data"      : "",
-                          (txreq.flags & NETTXF_extra_info)     ? " extra_info"     : "");
-
-            page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev,
-                                           netdev->xendev.dom,
-                                           txreq.gref, PROT_READ);
-            if (page == NULL) {
-                xen_be_printf(&netdev->xendev, 0, "error: tx gref dereference failed (%d)\n",
-                              txreq.gref);
-                net_tx_error(netdev, &txreq, rc);
-                continue;
-            }
-            if (txreq.flags & NETTXF_csum_blank) {
-                /* have read-only mapping -> can't fill checksum in-place */
-                if (!tmpbuf) {
-                    tmpbuf = g_malloc(XC_PAGE_SIZE);
-                }
-                memcpy(tmpbuf, page + txreq.offset, txreq.size);
-                net_checksum_calculate(tmpbuf, txreq.size);
-                qemu_send_packet(qemu_get_queue(netdev->nic), tmpbuf,
-                                 txreq.size);
-            } else {
-                qemu_send_packet(qemu_get_queue(netdev->nic),
-                                 page + txreq.offset, txreq.size);
-            }
-            xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1);
-            net_tx_response(netdev, &txreq, NETIF_RSP_OKAY);
-        }
-        if (!netdev->tx_work) {
-            break;
-        }
-        netdev->tx_work = 0;
-    }
-    g_free(tmpbuf);
-}
-
-/* ------------------------------------------------------------- */
-
-static void net_rx_response(struct XenNetDev *netdev,
-                            netif_rx_request_t *req, int8_t st,
-                            uint16_t offset, uint16_t size,
-                            uint16_t flags)
-{
-    RING_IDX i = netdev->rx_ring.rsp_prod_pvt;
-    netif_rx_response_t *resp;
-    int notify;
-
-    resp = RING_GET_RESPONSE(&netdev->rx_ring, i);
-    resp->offset     = offset;
-    resp->flags      = flags;
-    resp->id         = req->id;
-    resp->status     = (int16_t)size;
-    if (st < 0) {
-        resp->status = (int16_t)st;
-    }
-
-    xen_be_printf(&netdev->xendev, 3, "rx response: idx %d, status %d, flags 0x%x\n",
-                  i, resp->status, resp->flags);
-
-    netdev->rx_ring.rsp_prod_pvt = ++i;
-    RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify);
-    if (notify) {
-        xen_be_send_notify(&netdev->xendev);
-    }
-}
-
-#define NET_IP_ALIGN 2
-
-static int net_rx_ok(NetClientState *nc)
-{
-    struct XenNetDev *netdev = qemu_get_nic_opaque(nc);
-    RING_IDX rc, rp;
-
-    if (netdev->xendev.be_state != XenbusStateConnected) {
-        return 0;
-    }
-
-    rc = netdev->rx_ring.req_cons;
-    rp = netdev->rx_ring.sring->req_prod;
-    xen_rmb();
-
-    if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) {
-        xen_be_printf(&netdev->xendev, 2, "%s: no rx buffers (%d/%d)\n",
-                      __FUNCTION__, rc, rp);
-        return 0;
-    }
-    return 1;
-}
-
-static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size)
-{
-    struct XenNetDev *netdev = qemu_get_nic_opaque(nc);
-    netif_rx_request_t rxreq;
-    RING_IDX rc, rp;
-    void *page;
-
-    if (netdev->xendev.be_state != XenbusStateConnected) {
-        return -1;
-    }
-
-    rc = netdev->rx_ring.req_cons;
-    rp = netdev->rx_ring.sring->req_prod;
-    xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
-
-    if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) {
-        xen_be_printf(&netdev->xendev, 2, "no buffer, drop packet\n");
-        return -1;
-    }
-    if (size > XC_PAGE_SIZE - NET_IP_ALIGN) {
-        xen_be_printf(&netdev->xendev, 0, "packet too big (%lu > %ld)",
-                      (unsigned long)size, XC_PAGE_SIZE - NET_IP_ALIGN);
-        return -1;
-    }
-
-    memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq));
-    netdev->rx_ring.req_cons = ++rc;
-
-    page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev,
-                                   netdev->xendev.dom,
-                                   rxreq.gref, PROT_WRITE);
-    if (page == NULL) {
-        xen_be_printf(&netdev->xendev, 0, "error: rx gref dereference failed (%d)\n",
-                      rxreq.gref);
-        net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0);
-        return -1;
-    }
-    memcpy(page + NET_IP_ALIGN, buf, size);
-    xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1);
-    net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0);
-
-    return size;
-}
-
-/* ------------------------------------------------------------- */
-
-static NetClientInfo net_xen_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
-    .size = sizeof(NICState),
-    .can_receive = net_rx_ok,
-    .receive = net_rx_packet,
-};
-
-static int net_init(struct XenDevice *xendev)
-{
-    struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
-
-    /* read xenstore entries */
-    if (netdev->mac == NULL) {
-        netdev->mac = xenstore_read_be_str(&netdev->xendev, "mac");
-    }
-
-    /* do we have all we need? */
-    if (netdev->mac == NULL) {
-        return -1;
-    }
-
-    if (net_parse_macaddr(netdev->conf.macaddr.a, netdev->mac) < 0) {
-        return -1;
-    }
-
-    netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf,
-                               "xen", NULL, netdev);
-
-    snprintf(qemu_get_queue(netdev->nic)->info_str,
-             sizeof(qemu_get_queue(netdev->nic)->info_str),
-             "nic: xenbus vif macaddr=%s", netdev->mac);
-
-    /* fill info */
-    xenstore_write_be_int(&netdev->xendev, "feature-rx-copy", 1);
-    xenstore_write_be_int(&netdev->xendev, "feature-rx-flip", 0);
-
-    return 0;
-}
-
-static int net_connect(struct XenDevice *xendev)
-{
-    struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
-    int rx_copy;
-
-    if (xenstore_read_fe_int(&netdev->xendev, "tx-ring-ref",
-                             &netdev->tx_ring_ref) == -1) {
-        return -1;
-    }
-    if (xenstore_read_fe_int(&netdev->xendev, "rx-ring-ref",
-                             &netdev->rx_ring_ref) == -1) {
-        return 1;
-    }
-    if (xenstore_read_fe_int(&netdev->xendev, "event-channel",
-                             &netdev->xendev.remote_port) == -1) {
-        return -1;
-    }
-
-    if (xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy) == -1) {
-        rx_copy = 0;
-    }
-    if (rx_copy == 0) {
-        xen_be_printf(&netdev->xendev, 0, "frontend doesn't support rx-copy.\n");
-        return -1;
-    }
-
-    netdev->txs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev,
-                                          netdev->xendev.dom,
-                                          netdev->tx_ring_ref,
-                                          PROT_READ | PROT_WRITE);
-    netdev->rxs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev,
-                                          netdev->xendev.dom,
-                                          netdev->rx_ring_ref,
-                                          PROT_READ | PROT_WRITE);
-    if (!netdev->txs || !netdev->rxs) {
-        return -1;
-    }
-    BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XC_PAGE_SIZE);
-    BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XC_PAGE_SIZE);
-
-    xen_be_bind_evtchn(&netdev->xendev);
-
-    xen_be_printf(&netdev->xendev, 1, "ok: tx-ring-ref %d, rx-ring-ref %d, "
-                  "remote port %d, local port %d\n",
-                  netdev->tx_ring_ref, netdev->rx_ring_ref,
-                  netdev->xendev.remote_port, netdev->xendev.local_port);
-
-    net_tx_packets(netdev);
-    return 0;
-}
-
-static void net_disconnect(struct XenDevice *xendev)
-{
-    struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
-
-    xen_be_unbind_evtchn(&netdev->xendev);
-
-    if (netdev->txs) {
-        xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1);
-        netdev->txs = NULL;
-    }
-    if (netdev->rxs) {
-        xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1);
-        netdev->rxs = NULL;
-    }
-    if (netdev->nic) {
-        qemu_del_nic(netdev->nic);
-        netdev->nic = NULL;
-    }
-}
-
-static void net_event(struct XenDevice *xendev)
-{
-    struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
-    net_tx_packets(netdev);
-    qemu_flush_queued_packets(qemu_get_queue(netdev->nic));
-}
-
-static int net_free(struct XenDevice *xendev)
-{
-    struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev);
-
-    g_free(netdev->mac);
-    return 0;
-}
-
-/* ------------------------------------------------------------- */
-
-struct XenDevOps xen_netdev_ops = {
-    .size       = sizeof(struct XenNetDev),
-    .flags      = DEVOPS_FLAG_NEED_GNTDEV,
-    .init       = net_init,
-    .initialise    = net_connect,
-    .event      = net_event,
-    .disconnect = net_disconnect,
-    .free       = net_free,
-};
diff --git a/hw/xenfb.c b/hw/xenfb.c
deleted file mode 100644 (file)
index 8e42661..0000000
+++ /dev/null
@@ -1,1021 +0,0 @@
-/*
- *  xen paravirt framebuffer backend
- *
- *  Copyright IBM, Corp. 2005-2006
- *  Copyright Red Hat, Inc. 2006-2008
- *
- *  Authors:
- *       Anthony Liguori <aliguori@us.ibm.com>,
- *       Markus Armbruster <armbru@redhat.com>,
- *       Daniel P. Berrange <berrange@redhat.com>,
- *       Pat Campbell <plc@novell.com>,
- *       Gerd Hoffmann <kraxel@redhat.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; under version 2 of the License.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdarg.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-
-#include "hw/hw.h"
-#include "ui/console.h"
-#include "char/char.h"
-#include "hw/xen/xen_backend.h"
-
-#include <xen/event_channel.h>
-#include <xen/io/fbif.h>
-#include <xen/io/kbdif.h>
-#include <xen/io/protocols.h>
-
-#ifndef BTN_LEFT
-#define BTN_LEFT 0x110 /* from <linux/input.h> */
-#endif
-
-/* -------------------------------------------------------------------- */
-
-struct common {
-    struct XenDevice  xendev;  /* must be first */
-    void              *page;
-    QemuConsole       *con;
-};
-
-struct XenInput {
-    struct common c;
-    int abs_pointer_wanted; /* Whether guest supports absolute pointer */
-    int button_state;       /* Last seen pointer button state */
-    int extended;
-    QEMUPutMouseEntry *qmouse;
-};
-
-#define UP_QUEUE 8
-
-struct XenFB {
-    struct common     c;
-    size_t            fb_len;
-    int               row_stride;
-    int               depth;
-    int               width;
-    int               height;
-    int               offset;
-    void              *pixels;
-    int               fbpages;
-    int               feature_update;
-    int               refresh_period;
-    int               bug_trigger;
-    int               have_console;
-    int               do_resize;
-
-    struct {
-       int x,y,w,h;
-    } up_rects[UP_QUEUE];
-    int               up_count;
-    int               up_fullscreen;
-};
-
-/* -------------------------------------------------------------------- */
-
-static int common_bind(struct common *c)
-{
-    int mfn;
-
-    if (xenstore_read_fe_int(&c->xendev, "page-ref", &mfn) == -1)
-       return -1;
-    if (xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port) == -1)
-       return -1;
-
-    c->page = xc_map_foreign_range(xen_xc, c->xendev.dom,
-                                  XC_PAGE_SIZE,
-                                  PROT_READ | PROT_WRITE, mfn);
-    if (c->page == NULL)
-       return -1;
-
-    xen_be_bind_evtchn(&c->xendev);
-    xen_be_printf(&c->xendev, 1, "ring mfn %d, remote-port %d, local-port %d\n",
-                 mfn, c->xendev.remote_port, c->xendev.local_port);
-
-    return 0;
-}
-
-static void common_unbind(struct common *c)
-{
-    xen_be_unbind_evtchn(&c->xendev);
-    if (c->page) {
-       munmap(c->page, XC_PAGE_SIZE);
-       c->page = NULL;
-    }
-}
-
-/* -------------------------------------------------------------------- */
-
-#if 0
-/*
- * These two tables are not needed any more, but left in here
- * intentionally as documentation, to show how scancode2linux[]
- * was generated.
- *
- * Tables to map from scancode to Linux input layer keycode.
- * Scancodes are hardware-specific.  These maps assumes a
- * standard AT or PS/2 keyboard which is what QEMU feeds us.
- */
-const unsigned char atkbd_set2_keycode[512] = {
-
-     0, 67, 65, 63, 61, 59, 60, 88,  0, 68, 66, 64, 62, 15, 41,117,
-     0, 56, 42, 93, 29, 16,  2,  0,  0,  0, 44, 31, 30, 17,  3,  0,
-     0, 46, 45, 32, 18,  5,  4, 95,  0, 57, 47, 33, 20, 19,  6,183,
-     0, 49, 48, 35, 34, 21,  7,184,  0,  0, 50, 36, 22,  8,  9,185,
-     0, 51, 37, 23, 24, 11, 10,  0,  0, 52, 53, 38, 39, 25, 12,  0,
-     0, 89, 40,  0, 26, 13,  0,  0, 58, 54, 28, 27,  0, 43,  0, 85,
-     0, 86, 91, 90, 92,  0, 14, 94,  0, 79,124, 75, 71,121,  0,  0,
-    82, 83, 80, 76, 77, 72,  1, 69, 87, 78, 81, 74, 55, 73, 70, 99,
-
-      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-    217,100,255,  0, 97,165,  0,  0,156,  0,  0,  0,  0,  0,  0,125,
-    173,114,  0,113,  0,  0,  0,126,128,  0,  0,140,  0,  0,  0,127,
-    159,  0,115,  0,164,  0,  0,116,158,  0,150,166,  0,  0,  0,142,
-    157,  0,  0,  0,  0,  0,  0,  0,155,  0, 98,  0,  0,163,  0,  0,
-    226,  0,  0,  0,  0,  0,  0,  0,  0,255, 96,  0,  0,  0,143,  0,
-      0,  0,  0,  0,  0,  0,  0,  0,  0,107,  0,105,102,  0,  0,112,
-    110,111,108,112,106,103,  0,119,  0,118,109,  0, 99,104,119,  0,
-
-};
-
-const unsigned char atkbd_unxlate_table[128] = {
-
-      0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
-     21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
-     35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
-     50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88,  5,  6,  4, 12,  3,
-     11,  2, 10,  1,  9,119,126,108,117,125,123,107,115,116,121,105,
-    114,122,112,113,127, 96, 97,120,  7, 15, 23, 31, 39, 47, 55, 63,
-     71, 79, 86, 94,  8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
-     19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
-
-};
-#endif
-
-/*
- * for (i = 0; i < 128; i++) {
- *     scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]];
- *     scancode2linux[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80];
- * }
- */
-static const unsigned char scancode2linux[512] = {
-      0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
-     16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-     32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
-     48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
-     64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
-     80, 81, 82, 83, 99,  0, 86, 87, 88,117,  0,  0, 95,183,184,185,
-      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     93,  0,  0, 89,  0,  0, 85, 91, 90, 92,  0, 94,  0,124,121,  0,
-
-      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-    165,  0,  0,  0,  0,  0,  0,  0,  0,163,  0,  0, 96, 97,  0,  0,
-    113,140,164,  0,166,  0,  0,  0,  0,  0,255,  0,  0,  0,114,  0,
-    115,  0,150,  0,  0, 98,255, 99,100,  0,  0,  0,  0,  0,  0,  0,
-      0,  0,  0,  0,  0,119,119,102,103,104,  0,105,112,106,118,107,
-    108,109,110,111,  0,  0,  0,  0,  0,  0,  0,125,126,127,116,142,
-      0,  0,  0,143,  0,217,156,173,128,159,158,157,155,226,  0,112,
-      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-};
-
-/* Send an event to the keyboard frontend driver */
-static int xenfb_kbd_event(struct XenInput *xenfb,
-                          union xenkbd_in_event *event)
-{
-    struct xenkbd_page *page = xenfb->c.page;
-    uint32_t prod;
-
-    if (xenfb->c.xendev.be_state != XenbusStateConnected)
-       return 0;
-    if (!page)
-        return 0;
-
-    prod = page->in_prod;
-    if (prod - page->in_cons == XENKBD_IN_RING_LEN) {
-       errno = EAGAIN;
-       return -1;
-    }
-
-    xen_mb();          /* ensure ring space available */
-    XENKBD_IN_RING_REF(page, prod) = *event;
-    xen_wmb();         /* ensure ring contents visible */
-    page->in_prod = prod + 1;
-    return xen_be_send_notify(&xenfb->c.xendev);
-}
-
-/* Send a keyboard (or mouse button) event */
-static int xenfb_send_key(struct XenInput *xenfb, bool down, int keycode)
-{
-    union xenkbd_in_event event;
-
-    memset(&event, 0, XENKBD_IN_EVENT_SIZE);
-    event.type = XENKBD_TYPE_KEY;
-    event.key.pressed = down ? 1 : 0;
-    event.key.keycode = keycode;
-
-    return xenfb_kbd_event(xenfb, &event);
-}
-
-/* Send a relative mouse movement event */
-static int xenfb_send_motion(struct XenInput *xenfb,
-                            int rel_x, int rel_y, int rel_z)
-{
-    union xenkbd_in_event event;
-
-    memset(&event, 0, XENKBD_IN_EVENT_SIZE);
-    event.type = XENKBD_TYPE_MOTION;
-    event.motion.rel_x = rel_x;
-    event.motion.rel_y = rel_y;
-#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207
-    event.motion.rel_z = rel_z;
-#endif
-
-    return xenfb_kbd_event(xenfb, &event);
-}
-
-/* Send an absolute mouse movement event */
-static int xenfb_send_position(struct XenInput *xenfb,
-                              int abs_x, int abs_y, int z)
-{
-    union xenkbd_in_event event;
-
-    memset(&event, 0, XENKBD_IN_EVENT_SIZE);
-    event.type = XENKBD_TYPE_POS;
-    event.pos.abs_x = abs_x;
-    event.pos.abs_y = abs_y;
-#if __XEN_LATEST_INTERFACE_VERSION__ == 0x00030207
-    event.pos.abs_z = z;
-#endif
-#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030208
-    event.pos.rel_z = z;
-#endif
-
-    return xenfb_kbd_event(xenfb, &event);
-}
-
-/*
- * Send a key event from the client to the guest OS
- * QEMU gives us a raw scancode from an AT / PS/2 style keyboard.
- * We have to turn this into a Linux Input layer keycode.
- *
- * Extra complexity from the fact that with extended scancodes
- * (like those produced by arrow keys) this method gets called
- * twice, but we only want to send a single event. So we have to
- * track the '0xe0' scancode state & collapse the extended keys
- * as needed.
- *
- * Wish we could just send scancodes straight to the guest which
- * already has code for dealing with this...
- */
-static void xenfb_key_event(void *opaque, int scancode)
-{
-    struct XenInput *xenfb = opaque;
-    int down = 1;
-
-    if (scancode == 0xe0) {
-       xenfb->extended = 1;
-       return;
-    } else if (scancode & 0x80) {
-       scancode &= 0x7f;
-       down = 0;
-    }
-    if (xenfb->extended) {
-       scancode |= 0x80;
-       xenfb->extended = 0;
-    }
-    xenfb_send_key(xenfb, down, scancode2linux[scancode]);
-}
-
-/*
- * Send a mouse event from the client to the guest OS
- *
- * The QEMU mouse can be in either relative, or absolute mode.
- * Movement is sent separately from button state, which has to
- * be encoded as virtual key events. We also don't actually get
- * given any button up/down events, so have to track changes in
- * the button state.
- */
-static void xenfb_mouse_event(void *opaque,
-                             int dx, int dy, int dz, int button_state)
-{
-    struct XenInput *xenfb = opaque;
-    DisplaySurface *surface = qemu_console_surface(xenfb->c.con);
-    int dw = surface_width(surface);
-    int dh = surface_height(surface);
-    int i;
-
-    if (xenfb->abs_pointer_wanted)
-       xenfb_send_position(xenfb,
-                           dx * (dw - 1) / 0x7fff,
-                           dy * (dh - 1) / 0x7fff,
-                           dz);
-    else
-       xenfb_send_motion(xenfb, dx, dy, dz);
-
-    for (i = 0 ; i < 8 ; i++) {
-       int lastDown = xenfb->button_state & (1 << i);
-       int down = button_state & (1 << i);
-       if (down == lastDown)
-           continue;
-
-       if (xenfb_send_key(xenfb, down, BTN_LEFT+i) < 0)
-           return;
-    }
-    xenfb->button_state = button_state;
-}
-
-static int input_init(struct XenDevice *xendev)
-{
-    xenstore_write_be_int(xendev, "feature-abs-pointer", 1);
-    return 0;
-}
-
-static int input_initialise(struct XenDevice *xendev)
-{
-    struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
-    int rc;
-
-    if (!in->c.con) {
-        xen_be_printf(xendev, 1, "ds not set (yet)\n");
-        return -1;
-    }
-
-    rc = common_bind(&in->c);
-    if (rc != 0)
-       return rc;
-
-    qemu_add_kbd_event_handler(xenfb_key_event, in);
-    return 0;
-}
-
-static void input_connected(struct XenDevice *xendev)
-{
-    struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
-
-    if (xenstore_read_fe_int(xendev, "request-abs-pointer",
-                             &in->abs_pointer_wanted) == -1) {
-        in->abs_pointer_wanted = 0;
-    }
-
-    if (in->qmouse) {
-        qemu_remove_mouse_event_handler(in->qmouse);
-    }
-    in->qmouse = qemu_add_mouse_event_handler(xenfb_mouse_event, in,
-                                             in->abs_pointer_wanted,
-                                             "Xen PVFB Mouse");
-}
-
-static void input_disconnect(struct XenDevice *xendev)
-{
-    struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
-
-    if (in->qmouse) {
-       qemu_remove_mouse_event_handler(in->qmouse);
-       in->qmouse = NULL;
-    }
-    qemu_add_kbd_event_handler(NULL, NULL);
-    common_unbind(&in->c);
-}
-
-static void input_event(struct XenDevice *xendev)
-{
-    struct XenInput *xenfb = container_of(xendev, struct XenInput, c.xendev);
-    struct xenkbd_page *page = xenfb->c.page;
-
-    /* We don't understand any keyboard events, so just ignore them. */
-    if (page->out_prod == page->out_cons)
-       return;
-    page->out_cons = page->out_prod;
-    xen_be_send_notify(&xenfb->c.xendev);
-}
-
-/* -------------------------------------------------------------------- */
-
-static void xenfb_copy_mfns(int mode, int count, unsigned long *dst, void *src)
-{
-    uint32_t *src32 = src;
-    uint64_t *src64 = src;
-    int i;
-
-    for (i = 0; i < count; i++)
-       dst[i] = (mode == 32) ? src32[i] : src64[i];
-}
-
-static int xenfb_map_fb(struct XenFB *xenfb)
-{
-    struct xenfb_page *page = xenfb->c.page;
-    char *protocol = xenfb->c.xendev.protocol;
-    int n_fbdirs;
-    unsigned long *pgmfns = NULL;
-    unsigned long *fbmfns = NULL;
-    void *map, *pd;
-    int mode, ret = -1;
-
-    /* default to native */
-    pd = page->pd;
-    mode = sizeof(unsigned long) * 8;
-
-    if (!protocol) {
-       /*
-        * Undefined protocol, some guesswork needed.
-        *
-        * Old frontends which don't set the protocol use
-        * one page directory only, thus pd[1] must be zero.
-        * pd[1] of the 32bit struct layout and the lower
-        * 32 bits of pd[0] of the 64bit struct layout have
-        * the same location, so we can check that ...
-        */
-       uint32_t *ptr32 = NULL;
-       uint32_t *ptr64 = NULL;
-#if defined(__i386__)
-       ptr32 = (void*)page->pd;
-       ptr64 = ((void*)page->pd) + 4;
-#elif defined(__x86_64__)
-       ptr32 = ((void*)page->pd) - 4;
-       ptr64 = (void*)page->pd;
-#endif
-       if (ptr32) {
-           if (ptr32[1] == 0) {
-               mode = 32;
-               pd   = ptr32;
-           } else {
-               mode = 64;
-               pd   = ptr64;
-           }
-       }
-#if defined(__x86_64__)
-    } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_32) == 0) {
-       /* 64bit dom0, 32bit domU */
-       mode = 32;
-       pd   = ((void*)page->pd) - 4;
-#elif defined(__i386__)
-    } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_64) == 0) {
-       /* 32bit dom0, 64bit domU */
-       mode = 64;
-       pd   = ((void*)page->pd) + 4;
-#endif
-    }
-
-    if (xenfb->pixels) {
-        munmap(xenfb->pixels, xenfb->fbpages * XC_PAGE_SIZE);
-        xenfb->pixels = NULL;
-    }
-
-    xenfb->fbpages = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
-    n_fbdirs = xenfb->fbpages * mode / 8;
-    n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
-
-    pgmfns = g_malloc0(sizeof(unsigned long) * n_fbdirs);
-    fbmfns = g_malloc0(sizeof(unsigned long) * xenfb->fbpages);
-
-    xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd);
-    map = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom,
-                              PROT_READ, pgmfns, n_fbdirs);
-    if (map == NULL)
-       goto out;
-    xenfb_copy_mfns(mode, xenfb->fbpages, fbmfns, map);
-    munmap(map, n_fbdirs * XC_PAGE_SIZE);
-
-    xenfb->pixels = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom,
-                                        PROT_READ | PROT_WRITE, fbmfns, xenfb->fbpages);
-    if (xenfb->pixels == NULL)
-       goto out;
-
-    ret = 0; /* all is fine */
-
-out:
-    g_free(pgmfns);
-    g_free(fbmfns);
-    return ret;
-}
-
-static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim,
-                             int width, int height, int depth,
-                             size_t fb_len, int offset, int row_stride)
-{
-    size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd);
-    size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz;
-    size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz;
-    size_t fb_len_max = fb_pages * XC_PAGE_SIZE;
-    int max_width, max_height;
-
-    if (fb_len_lim > fb_len_max) {
-       xen_be_printf(&xenfb->c.xendev, 0, "fb size limit %zu exceeds %zu, corrected\n",
-                     fb_len_lim, fb_len_max);
-       fb_len_lim = fb_len_max;
-    }
-    if (fb_len_lim && fb_len > fb_len_lim) {
-       xen_be_printf(&xenfb->c.xendev, 0, "frontend fb size %zu limited to %zu\n",
-                     fb_len, fb_len_lim);
-       fb_len = fb_len_lim;
-    }
-    if (depth != 8 && depth != 16 && depth != 24 && depth != 32) {
-       xen_be_printf(&xenfb->c.xendev, 0, "can't handle frontend fb depth %d\n",
-                     depth);
-       return -1;
-    }
-    if (row_stride <= 0 || row_stride > fb_len) {
-       xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend stride %d\n", row_stride);
-       return -1;
-    }
-    max_width = row_stride / (depth / 8);
-    if (width < 0 || width > max_width) {
-       xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend width %d limited to %d\n",
-                     width, max_width);
-       width = max_width;
-    }
-    if (offset < 0 || offset >= fb_len) {
-       xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend offset %d (max %zu)\n",
-                     offset, fb_len - 1);
-       return -1;
-    }
-    max_height = (fb_len - offset) / row_stride;
-    if (height < 0 || height > max_height) {
-       xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend height %d limited to %d\n",
-                     height, max_height);
-       height = max_height;
-    }
-    xenfb->fb_len = fb_len;
-    xenfb->row_stride = row_stride;
-    xenfb->depth = depth;
-    xenfb->width = width;
-    xenfb->height = height;
-    xenfb->offset = offset;
-    xenfb->up_fullscreen = 1;
-    xenfb->do_resize = 1;
-    xen_be_printf(&xenfb->c.xendev, 1, "framebuffer %dx%dx%d offset %d stride %d\n",
-                 width, height, depth, offset, row_stride);
-    return 0;
-}
-
-/* A convenient function for munging pixels between different depths */
-#define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB)                        \
-    for (line = y ; line < (y+h) ; line++) {                           \
-       SRC_T *src = (SRC_T *)(xenfb->pixels                            \
-                              + xenfb->offset                          \
-                              + (line * xenfb->row_stride)             \
-                              + (x * xenfb->depth / 8));               \
-       DST_T *dst = (DST_T *)(data                                     \
-                              + (line * linesize)                      \
-                              + (x * bpp / 8));                        \
-       int col;                                                        \
-       const int RSS = 32 - (RSB + GSB + BSB);                         \
-       const int GSS = 32 - (GSB + BSB);                               \
-       const int BSS = 32 - (BSB);                                     \
-       const uint32_t RSM = (~0U) << (32 - RSB);                       \
-       const uint32_t GSM = (~0U) << (32 - GSB);                       \
-       const uint32_t BSM = (~0U) << (32 - BSB);                       \
-       const int RDS = 32 - (RDB + GDB + BDB);                         \
-       const int GDS = 32 - (GDB + BDB);                               \
-       const int BDS = 32 - (BDB);                                     \
-       const uint32_t RDM = (~0U) << (32 - RDB);                       \
-       const uint32_t GDM = (~0U) << (32 - GDB);                       \
-       const uint32_t BDM = (~0U) << (32 - BDB);                       \
-       for (col = x ; col < (x+w) ; col++) {                           \
-           uint32_t spix = *src;                                       \
-           *dst = (((spix << RSS) & RSM & RDM) >> RDS) |               \
-               (((spix << GSS) & GSM & GDM) >> GDS) |                  \
-               (((spix << BSS) & BSM & BDM) >> BDS);                   \
-           src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8);   \
-           dst = (DST_T *) ((unsigned long) dst + bpp / 8);            \
-       }                                                               \
-    }
-
-
-/*
- * This copies data from the guest framebuffer region, into QEMU's
- * displaysurface. qemu uses 16 or 32 bpp.  In case the pv framebuffer
- * uses something else we must convert and copy, otherwise we can
- * supply the buffer directly and no thing here.
- */
-static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h)
-{
-    DisplaySurface *surface = qemu_console_surface(xenfb->c.con);
-    int line, oops = 0;
-    int bpp = surface_bits_per_pixel(surface);
-    int linesize = surface_stride(surface);
-    uint8_t *data = surface_data(surface);
-
-    if (!is_buffer_shared(surface)) {
-        switch (xenfb->depth) {
-        case 8:
-            if (bpp == 16) {
-                BLT(uint8_t, uint16_t,   3, 3, 2,   5, 6, 5);
-            } else if (bpp == 32) {
-                BLT(uint8_t, uint32_t,   3, 3, 2,   8, 8, 8);
-            } else {
-                oops = 1;
-            }
-            break;
-        case 24:
-            if (bpp == 16) {
-                BLT(uint32_t, uint16_t,  8, 8, 8,   5, 6, 5);
-            } else if (bpp == 32) {
-                BLT(uint32_t, uint32_t,  8, 8, 8,   8, 8, 8);
-            } else {
-                oops = 1;
-            }
-            break;
-        default:
-            oops = 1;
-       }
-    }
-    if (oops) /* should not happen */
-        xen_be_printf(&xenfb->c.xendev, 0, "%s: oops: convert %d -> %d bpp?\n",
-                      __FUNCTION__, xenfb->depth, bpp);
-
-    dpy_gfx_update(xenfb->c.con, x, y, w, h);
-}
-
-#if 0 /* def XENFB_TYPE_REFRESH_PERIOD */
-static int xenfb_queue_full(struct XenFB *xenfb)
-{
-    struct xenfb_page *page = xenfb->c.page;
-    uint32_t cons, prod;
-
-    if (!page)
-        return 1;
-
-    prod = page->in_prod;
-    cons = page->in_cons;
-    return prod - cons == XENFB_IN_RING_LEN;
-}
-
-static void xenfb_send_event(struct XenFB *xenfb, union xenfb_in_event *event)
-{
-    uint32_t prod;
-    struct xenfb_page *page = xenfb->c.page;
-
-    prod = page->in_prod;
-    /* caller ensures !xenfb_queue_full() */
-    xen_mb();                   /* ensure ring space available */
-    XENFB_IN_RING_REF(page, prod) = *event;
-    xen_wmb();                  /* ensure ring contents visible */
-    page->in_prod = prod + 1;
-
-    xen_be_send_notify(&xenfb->c.xendev);
-}
-
-static void xenfb_send_refresh_period(struct XenFB *xenfb, int period)
-{
-    union xenfb_in_event event;
-
-    memset(&event, 0, sizeof(event));
-    event.type = XENFB_TYPE_REFRESH_PERIOD;
-    event.refresh_period.period = period;
-    xenfb_send_event(xenfb, &event);
-}
-#endif
-
-/*
- * Periodic update of display.
- * Also transmit the refresh interval to the frontend.
- *
- * Never ever do any qemu display operations
- * (resize, screen update) outside this function.
- * Our screen might be inactive.  When asked for
- * an update we know it is active.
- */
-static void xenfb_update(void *opaque)
-{
-    struct XenFB *xenfb = opaque;
-    DisplaySurface *surface;
-    int i;
-
-    if (xenfb->c.xendev.be_state != XenbusStateConnected)
-        return;
-
-    if (xenfb->feature_update) {
-#if 0 /* XENFB_TYPE_REFRESH_PERIOD */
-        struct DisplayChangeListener *l;
-        int period = 99999999;
-        int idle = 1;
-
-       if (xenfb_queue_full(xenfb))
-           return;
-
-        QLIST_FOREACH(l, &xenfb->c.ds->listeners, next) {
-            if (l->idle)
-                continue;
-            idle = 0;
-            if (!l->gui_timer_interval) {
-                if (period > GUI_REFRESH_INTERVAL)
-                    period = GUI_REFRESH_INTERVAL;
-            } else {
-                if (period > l->gui_timer_interval)
-                    period = l->gui_timer_interval;
-            }
-        }
-        if (idle)
-           period = XENFB_NO_REFRESH;
-
-       if (xenfb->refresh_period != period) {
-           xenfb_send_refresh_period(xenfb, period);
-           xenfb->refresh_period = period;
-            xen_be_printf(&xenfb->c.xendev, 1, "refresh period: %d\n", period);
-       }
-#else
-       ; /* nothing */
-#endif
-    } else {
-       /* we don't get update notifications, thus use the
-        * sledge hammer approach ... */
-       xenfb->up_fullscreen = 1;
-    }
-
-    /* resize if needed */
-    if (xenfb->do_resize) {
-        xenfb->do_resize = 0;
-        switch (xenfb->depth) {
-        case 16:
-        case 32:
-            /* console.c supported depth -> buffer can be used directly */
-            surface = qemu_create_displaysurface_from
-                (xenfb->width, xenfb->height, xenfb->depth,
-                 xenfb->row_stride, xenfb->pixels + xenfb->offset,
-                 false);
-            break;
-        default:
-            /* we must convert stuff */
-            surface = qemu_create_displaysurface(xenfb->width, xenfb->height);
-            break;
-        }
-        dpy_gfx_replace_surface(xenfb->c.con, surface);
-        xen_be_printf(&xenfb->c.xendev, 1, "update: resizing: %dx%d @ %d bpp%s\n",
-                      xenfb->width, xenfb->height, xenfb->depth,
-                      is_buffer_shared(surface) ? " (shared)" : "");
-        xenfb->up_fullscreen = 1;
-    }
-
-    /* run queued updates */
-    if (xenfb->up_fullscreen) {
-       xen_be_printf(&xenfb->c.xendev, 3, "update: fullscreen\n");
-       xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height);
-    } else if (xenfb->up_count) {
-       xen_be_printf(&xenfb->c.xendev, 3, "update: %d rects\n", xenfb->up_count);
-       for (i = 0; i < xenfb->up_count; i++)
-           xenfb_guest_copy(xenfb,
-                            xenfb->up_rects[i].x,
-                            xenfb->up_rects[i].y,
-                            xenfb->up_rects[i].w,
-                            xenfb->up_rects[i].h);
-    } else {
-       xen_be_printf(&xenfb->c.xendev, 3, "update: nothing\n");
-    }
-    xenfb->up_count = 0;
-    xenfb->up_fullscreen = 0;
-}
-
-/* QEMU display state changed, so refresh the framebuffer copy */
-static void xenfb_invalidate(void *opaque)
-{
-    struct XenFB *xenfb = opaque;
-    xenfb->up_fullscreen = 1;
-}
-
-static void xenfb_handle_events(struct XenFB *xenfb)
-{
-    uint32_t prod, cons;
-    struct xenfb_page *page = xenfb->c.page;
-
-    prod = page->out_prod;
-    if (prod == page->out_cons)
-       return;
-    xen_rmb();         /* ensure we see ring contents up to prod */
-    for (cons = page->out_cons; cons != prod; cons++) {
-       union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons);
-       int x, y, w, h;
-
-       switch (event->type) {
-       case XENFB_TYPE_UPDATE:
-           if (xenfb->up_count == UP_QUEUE)
-               xenfb->up_fullscreen = 1;
-           if (xenfb->up_fullscreen)
-               break;
-           x = MAX(event->update.x, 0);
-           y = MAX(event->update.y, 0);
-           w = MIN(event->update.width, xenfb->width - x);
-           h = MIN(event->update.height, xenfb->height - y);
-           if (w < 0 || h < 0) {
-                xen_be_printf(&xenfb->c.xendev, 1, "bogus update ignored\n");
-               break;
-           }
-           if (x != event->update.x ||
-                y != event->update.y ||
-               w != event->update.width ||
-               h != event->update.height) {
-                xen_be_printf(&xenfb->c.xendev, 1, "bogus update clipped\n");
-           }
-           if (w == xenfb->width && h > xenfb->height / 2) {
-               /* scroll detector: updated more than 50% of the lines,
-                * don't bother keeping track of the rectangles then */
-               xenfb->up_fullscreen = 1;
-           } else {
-               xenfb->up_rects[xenfb->up_count].x = x;
-               xenfb->up_rects[xenfb->up_count].y = y;
-               xenfb->up_rects[xenfb->up_count].w = w;
-               xenfb->up_rects[xenfb->up_count].h = h;
-               xenfb->up_count++;
-           }
-           break;
-#ifdef XENFB_TYPE_RESIZE
-       case XENFB_TYPE_RESIZE:
-           if (xenfb_configure_fb(xenfb, xenfb->fb_len,
-                                  event->resize.width,
-                                  event->resize.height,
-                                  event->resize.depth,
-                                  xenfb->fb_len,
-                                  event->resize.offset,
-                                  event->resize.stride) < 0)
-               break;
-           xenfb_invalidate(xenfb);
-           break;
-#endif
-       }
-    }
-    xen_mb();          /* ensure we're done with ring contents */
-    page->out_cons = cons;
-}
-
-static int fb_init(struct XenDevice *xendev)
-{
-    struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
-
-    fb->refresh_period = -1;
-
-#ifdef XENFB_TYPE_RESIZE
-    xenstore_write_be_int(xendev, "feature-resize", 1);
-#endif
-    return 0;
-}
-
-static int fb_initialise(struct XenDevice *xendev)
-{
-    struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
-    struct xenfb_page *fb_page;
-    int videoram;
-    int rc;
-
-    if (xenstore_read_fe_int(xendev, "videoram", &videoram) == -1)
-       videoram = 0;
-
-    rc = common_bind(&fb->c);
-    if (rc != 0)
-       return rc;
-
-    fb_page = fb->c.page;
-    rc = xenfb_configure_fb(fb, videoram * 1024 * 1024U,
-                           fb_page->width, fb_page->height, fb_page->depth,
-                           fb_page->mem_length, 0, fb_page->line_length);
-    if (rc != 0)
-       return rc;
-
-    rc = xenfb_map_fb(fb);
-    if (rc != 0)
-       return rc;
-
-#if 0  /* handled in xen_init_display() for now */
-    if (!fb->have_console) {
-        fb->c.ds = graphic_console_init(xenfb_update,
-                                        xenfb_invalidate,
-                                        NULL,
-                                        NULL,
-                                        fb);
-        fb->have_console = 1;
-    }
-#endif
-
-    if (xenstore_read_fe_int(xendev, "feature-update", &fb->feature_update) == -1)
-       fb->feature_update = 0;
-    if (fb->feature_update)
-       xenstore_write_be_int(xendev, "request-update", 1);
-
-    xen_be_printf(xendev, 1, "feature-update=%d, videoram=%d\n",
-                 fb->feature_update, videoram);
-    return 0;
-}
-
-static void fb_disconnect(struct XenDevice *xendev)
-{
-    struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
-
-    /*
-     * FIXME: qemu can't un-init gfx display (yet?).
-     *   Replacing the framebuffer with anonymous shared memory
-     *   instead.  This releases the guest pages and keeps qemu happy.
-     */
-    fb->pixels = mmap(fb->pixels, fb->fbpages * XC_PAGE_SIZE,
-                      PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON,
-                      -1, 0);
-    common_unbind(&fb->c);
-    fb->feature_update = 0;
-    fb->bug_trigger    = 0;
-}
-
-static void fb_frontend_changed(struct XenDevice *xendev, const char *node)
-{
-    struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
-
-    /*
-     * Set state to Connected *again* once the frontend switched
-     * to connected.  We must trigger the watch a second time to
-     * workaround a frontend bug.
-     */
-    if (fb->bug_trigger == 0 && strcmp(node, "state") == 0 &&
-        xendev->fe_state == XenbusStateConnected &&
-        xendev->be_state == XenbusStateConnected) {
-        xen_be_printf(xendev, 2, "re-trigger connected (frontend bug)\n");
-        xen_be_set_state(xendev, XenbusStateConnected);
-        fb->bug_trigger = 1; /* only once */
-    }
-}
-
-static void fb_event(struct XenDevice *xendev)
-{
-    struct XenFB *xenfb = container_of(xendev, struct XenFB, c.xendev);
-
-    xenfb_handle_events(xenfb);
-    xen_be_send_notify(&xenfb->c.xendev);
-}
-
-/* -------------------------------------------------------------------- */
-
-struct XenDevOps xen_kbdmouse_ops = {
-    .size       = sizeof(struct XenInput),
-    .init       = input_init,
-    .initialise = input_initialise,
-    .connected  = input_connected,
-    .disconnect = input_disconnect,
-    .event      = input_event,
-};
-
-struct XenDevOps xen_framebuffer_ops = {
-    .size       = sizeof(struct XenFB),
-    .init       = fb_init,
-    .initialise = fb_initialise,
-    .disconnect = fb_disconnect,
-    .event      = fb_event,
-    .frontend_changed = fb_frontend_changed,
-};
-
-/*
- * FIXME/TODO: Kill this.
- * Temporary needed while DisplayState reorganization is in flight.
- */
-void xen_init_display(int domid)
-{
-    struct XenDevice *xfb, *xin;
-    struct XenFB *fb;
-    struct XenInput *in;
-    int i = 0;
-
-wait_more:
-    i++;
-    main_loop_wait(true);
-    xfb = xen_be_find_xendev("vfb", domid, 0);
-    xin = xen_be_find_xendev("vkbd", domid, 0);
-    if (!xfb || !xin) {
-        if (i < 256) {
-            usleep(10000);
-            goto wait_more;
-        }
-        xen_be_printf(NULL, 1, "displaystate setup failed\n");
-        return;
-    }
-
-    /* vfb */
-    fb = container_of(xfb, struct XenFB, c.xendev);
-    fb->c.con = graphic_console_init(xenfb_update,
-                                     xenfb_invalidate,
-                                     NULL,
-                                     NULL,
-                                     fb);
-    fb->have_console = 1;
-
-    /* vkbd */
-    in = container_of(xin, struct XenInput, c.xendev);
-    in->c.con = fb->c.con;
-
-    /* retry ->init() */
-    xen_be_check_state(xin);
-    xen_be_check_state(xfb);
-}
diff --git a/hw/xgmac.c b/hw/xgmac.c
deleted file mode 100644 (file)
index 5275f48..0000000
+++ /dev/null
@@ -1,433 +0,0 @@
-/*
- * QEMU model of XGMAC Ethernet.
- *
- * derived from the Xilinx AXI-Ethernet by Edgar E. Iglesias.
- *
- * Copyright (c) 2011 Calxeda, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/sysbus.h"
-#include "char/char.h"
-#include "qemu/log.h"
-#include "net/net.h"
-#include "net/checksum.h"
-
-#ifdef DEBUG_XGMAC
-#define DEBUGF_BRK(message, args...) do { \
-                                         fprintf(stderr, (message), ## args); \
-                                     } while (0)
-#else
-#define DEBUGF_BRK(message, args...) do { } while (0)
-#endif
-
-#define XGMAC_CONTROL           0x00000000   /* MAC Configuration */
-#define XGMAC_FRAME_FILTER      0x00000001   /* MAC Frame Filter */
-#define XGMAC_FLOW_CTRL         0x00000006   /* MAC Flow Control */
-#define XGMAC_VLAN_TAG          0x00000007   /* VLAN Tags */
-#define XGMAC_VERSION           0x00000008   /* Version */
-/* VLAN tag for insertion or replacement into tx frames */
-#define XGMAC_VLAN_INCL         0x00000009
-#define XGMAC_LPI_CTRL          0x0000000a   /* LPI Control and Status */
-#define XGMAC_LPI_TIMER         0x0000000b   /* LPI Timers Control */
-#define XGMAC_TX_PACE           0x0000000c   /* Transmit Pace and Stretch */
-#define XGMAC_VLAN_HASH         0x0000000d   /* VLAN Hash Table */
-#define XGMAC_DEBUG             0x0000000e   /* Debug */
-#define XGMAC_INT_STATUS        0x0000000f   /* Interrupt and Control */
-/* HASH table registers */
-#define XGMAC_HASH(n)           ((0x00000300/4) + (n))
-#define XGMAC_NUM_HASH          16
-/* Operation Mode */
-#define XGMAC_OPMODE            (0x00000400/4)
-/* Remote Wake-Up Frame Filter */
-#define XGMAC_REMOTE_WAKE       (0x00000700/4)
-/* PMT Control and Status */
-#define XGMAC_PMT               (0x00000704/4)
-
-#define XGMAC_ADDR_HIGH(reg)    (0x00000010+((reg) * 2))
-#define XGMAC_ADDR_LOW(reg)     (0x00000011+((reg) * 2))
-
-#define DMA_BUS_MODE            0x000003c0   /* Bus Mode */
-#define DMA_XMT_POLL_DEMAND     0x000003c1   /* Transmit Poll Demand */
-#define DMA_RCV_POLL_DEMAND     0x000003c2   /* Received Poll Demand */
-#define DMA_RCV_BASE_ADDR       0x000003c3   /* Receive List Base */
-#define DMA_TX_BASE_ADDR        0x000003c4   /* Transmit List Base */
-#define DMA_STATUS              0x000003c5   /* Status Register */
-#define DMA_CONTROL             0x000003c6   /* Ctrl (Operational Mode) */
-#define DMA_INTR_ENA            0x000003c7   /* Interrupt Enable */
-#define DMA_MISSED_FRAME_CTR    0x000003c8   /* Missed Frame Counter */
-/* Receive Interrupt Watchdog Timer */
-#define DMA_RI_WATCHDOG_TIMER   0x000003c9
-#define DMA_AXI_BUS             0x000003ca   /* AXI Bus Mode */
-#define DMA_AXI_STATUS          0x000003cb   /* AXI Status */
-#define DMA_CUR_TX_DESC_ADDR    0x000003d2   /* Current Host Tx Descriptor */
-#define DMA_CUR_RX_DESC_ADDR    0x000003d3   /* Current Host Rx Descriptor */
-#define DMA_CUR_TX_BUF_ADDR     0x000003d4   /* Current Host Tx Buffer */
-#define DMA_CUR_RX_BUF_ADDR     0x000003d5   /* Current Host Rx Buffer */
-#define DMA_HW_FEATURE          0x000003d6   /* Enabled Hardware Features */
-
-/* DMA Status register defines */
-#define DMA_STATUS_GMI          0x08000000   /* MMC interrupt */
-#define DMA_STATUS_GLI          0x04000000   /* GMAC Line interface int */
-#define DMA_STATUS_EB_MASK      0x00380000   /* Error Bits Mask */
-#define DMA_STATUS_EB_TX_ABORT  0x00080000   /* Error Bits - TX Abort */
-#define DMA_STATUS_EB_RX_ABORT  0x00100000   /* Error Bits - RX Abort */
-#define DMA_STATUS_TS_MASK      0x00700000   /* Transmit Process State */
-#define DMA_STATUS_TS_SHIFT     20
-#define DMA_STATUS_RS_MASK      0x000e0000   /* Receive Process State */
-#define DMA_STATUS_RS_SHIFT     17
-#define DMA_STATUS_NIS          0x00010000   /* Normal Interrupt Summary */
-#define DMA_STATUS_AIS          0x00008000   /* Abnormal Interrupt Summary */
-#define DMA_STATUS_ERI          0x00004000   /* Early Receive Interrupt */
-#define DMA_STATUS_FBI          0x00002000   /* Fatal Bus Error Interrupt */
-#define DMA_STATUS_ETI          0x00000400   /* Early Transmit Interrupt */
-#define DMA_STATUS_RWT          0x00000200   /* Receive Watchdog Timeout */
-#define DMA_STATUS_RPS          0x00000100   /* Receive Process Stopped */
-#define DMA_STATUS_RU           0x00000080   /* Receive Buffer Unavailable */
-#define DMA_STATUS_RI           0x00000040   /* Receive Interrupt */
-#define DMA_STATUS_UNF          0x00000020   /* Transmit Underflow */
-#define DMA_STATUS_OVF          0x00000010   /* Receive Overflow */
-#define DMA_STATUS_TJT          0x00000008   /* Transmit Jabber Timeout */
-#define DMA_STATUS_TU           0x00000004   /* Transmit Buffer Unavailable */
-#define DMA_STATUS_TPS          0x00000002   /* Transmit Process Stopped */
-#define DMA_STATUS_TI           0x00000001   /* Transmit Interrupt */
-
-/* DMA Control register defines */
-#define DMA_CONTROL_ST          0x00002000   /* Start/Stop Transmission */
-#define DMA_CONTROL_SR          0x00000002   /* Start/Stop Receive */
-#define DMA_CONTROL_DFF         0x01000000   /* Disable flush of rx frames */
-
-struct desc {
-    uint32_t ctl_stat;
-    uint16_t buffer1_size;
-    uint16_t buffer2_size;
-    uint32_t buffer1_addr;
-    uint32_t buffer2_addr;
-    uint32_t ext_stat;
-    uint32_t res[3];
-};
-
-#define R_MAX 0x400
-
-typedef struct RxTxStats {
-    uint64_t rx_bytes;
-    uint64_t tx_bytes;
-
-    uint64_t rx;
-    uint64_t rx_bcast;
-    uint64_t rx_mcast;
-} RxTxStats;
-
-typedef struct XgmacState {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    qemu_irq sbd_irq;
-    qemu_irq pmt_irq;
-    qemu_irq mci_irq;
-    NICState *nic;
-    NICConf conf;
-
-    struct RxTxStats stats;
-    uint32_t regs[R_MAX];
-} XgmacState;
-
-const VMStateDescription vmstate_rxtx_stats = {
-    .name = "xgmac_stats",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields      = (VMStateField[]) {
-        VMSTATE_UINT64(rx_bytes, RxTxStats),
-        VMSTATE_UINT64(tx_bytes, RxTxStats),
-        VMSTATE_UINT64(rx, RxTxStats),
-        VMSTATE_UINT64(rx_bcast, RxTxStats),
-        VMSTATE_UINT64(rx_mcast, RxTxStats),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static const VMStateDescription vmstate_xgmac = {
-    .name = "xgmac",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_STRUCT(stats, XgmacState, 0, vmstate_rxtx_stats, RxTxStats),
-        VMSTATE_UINT32_ARRAY(regs, XgmacState, R_MAX),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static void xgmac_read_desc(struct XgmacState *s, struct desc *d, int rx)
-{
-    uint32_t addr = rx ? s->regs[DMA_CUR_RX_DESC_ADDR] :
-        s->regs[DMA_CUR_TX_DESC_ADDR];
-    cpu_physical_memory_read(addr, d, sizeof(*d));
-}
-
-static void xgmac_write_desc(struct XgmacState *s, struct desc *d, int rx)
-{
-    int reg = rx ? DMA_CUR_RX_DESC_ADDR : DMA_CUR_TX_DESC_ADDR;
-    uint32_t addr = s->regs[reg];
-
-    if (!rx && (d->ctl_stat & 0x00200000)) {
-        s->regs[reg] = s->regs[DMA_TX_BASE_ADDR];
-    } else if (rx && (d->buffer1_size & 0x8000)) {
-        s->regs[reg] = s->regs[DMA_RCV_BASE_ADDR];
-    } else {
-        s->regs[reg] += sizeof(*d);
-    }
-    cpu_physical_memory_write(addr, d, sizeof(*d));
-}
-
-static void xgmac_enet_send(struct XgmacState *s)
-{
-    struct desc bd;
-    int frame_size;
-    int len;
-    uint8_t frame[8192];
-    uint8_t *ptr;
-
-    ptr = frame;
-    frame_size = 0;
-    while (1) {
-        xgmac_read_desc(s, &bd, 0);
-        if ((bd.ctl_stat & 0x80000000) == 0) {
-            /* Run out of descriptors to transmit.  */
-            break;
-        }
-        len = (bd.buffer1_size & 0xfff) + (bd.buffer2_size & 0xfff);
-
-        if ((bd.buffer1_size & 0xfff) > 2048) {
-            DEBUGF_BRK("qemu:%s:ERROR...ERROR...ERROR... -- "
-                        "xgmac buffer 1 len on send > 2048 (0x%x)\n",
-                         __func__, bd.buffer1_size & 0xfff);
-        }
-        if ((bd.buffer2_size & 0xfff) != 0) {
-            DEBUGF_BRK("qemu:%s:ERROR...ERROR...ERROR... -- "
-                        "xgmac buffer 2 len on send != 0 (0x%x)\n",
-                        __func__, bd.buffer2_size & 0xfff);
-        }
-        if (len >= sizeof(frame)) {
-            DEBUGF_BRK("qemu:%s: buffer overflow %d read into %zu "
-                        "buffer\n" , __func__, len, sizeof(frame));
-            DEBUGF_BRK("qemu:%s: buffer1.size=%d; buffer2.size=%d\n",
-                        __func__, bd.buffer1_size, bd.buffer2_size);
-        }
-
-        cpu_physical_memory_read(bd.buffer1_addr, ptr, len);
-        ptr += len;
-        frame_size += len;
-        if (bd.ctl_stat & 0x20000000) {
-            /* Last buffer in frame.  */
-            qemu_send_packet(qemu_get_queue(s->nic), frame, len);
-            ptr = frame;
-            frame_size = 0;
-            s->regs[DMA_STATUS] |= DMA_STATUS_TI | DMA_STATUS_NIS;
-        }
-        bd.ctl_stat &= ~0x80000000;
-        /* Write back the modified descriptor.  */
-        xgmac_write_desc(s, &bd, 0);
-    }
-}
-
-static void enet_update_irq(struct XgmacState *s)
-{
-    int stat = s->regs[DMA_STATUS] & s->regs[DMA_INTR_ENA];
-    qemu_set_irq(s->sbd_irq, !!stat);
-}
-
-static uint64_t enet_read(void *opaque, hwaddr addr, unsigned size)
-{
-    struct XgmacState *s = opaque;
-    uint64_t r = 0;
-    addr >>= 2;
-
-    switch (addr) {
-    case XGMAC_VERSION:
-        r = 0x1012;
-        break;
-    default:
-        if (addr < ARRAY_SIZE(s->regs)) {
-            r = s->regs[addr];
-        }
-        break;
-    }
-    return r;
-}
-
-static void enet_write(void *opaque, hwaddr addr,
-                       uint64_t value, unsigned size)
-{
-    struct XgmacState *s = opaque;
-
-    addr >>= 2;
-    switch (addr) {
-    case DMA_BUS_MODE:
-        s->regs[DMA_BUS_MODE] = value & ~0x1;
-        break;
-    case DMA_XMT_POLL_DEMAND:
-        xgmac_enet_send(s);
-        break;
-    case DMA_STATUS:
-        s->regs[DMA_STATUS] = s->regs[DMA_STATUS] & ~value;
-        break;
-    case DMA_RCV_BASE_ADDR:
-        s->regs[DMA_RCV_BASE_ADDR] = s->regs[DMA_CUR_RX_DESC_ADDR] = value;
-        break;
-    case DMA_TX_BASE_ADDR:
-        s->regs[DMA_TX_BASE_ADDR] = s->regs[DMA_CUR_TX_DESC_ADDR] = value;
-        break;
-    default:
-        if (addr < ARRAY_SIZE(s->regs)) {
-            s->regs[addr] = value;
-        }
-        break;
-    }
-    enet_update_irq(s);
-}
-
-static const MemoryRegionOps enet_mem_ops = {
-    .read = enet_read,
-    .write = enet_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static int eth_can_rx(NetClientState *nc)
-{
-    struct XgmacState *s = qemu_get_nic_opaque(nc);
-
-    /* RX enabled?  */
-    return s->regs[DMA_CONTROL] & DMA_CONTROL_SR;
-}
-
-static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
-{
-    struct XgmacState *s = qemu_get_nic_opaque(nc);
-    static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff,
-                                              0xff, 0xff, 0xff};
-    int unicast, broadcast, multicast;
-    struct desc bd;
-    ssize_t ret;
-
-    unicast = ~buf[0] & 0x1;
-    broadcast = memcmp(buf, sa_bcast, 6) == 0;
-    multicast = !unicast && !broadcast;
-    if (size < 12) {
-        s->regs[DMA_STATUS] |= DMA_STATUS_RI | DMA_STATUS_NIS;
-        ret = -1;
-        goto out;
-    }
-
-    xgmac_read_desc(s, &bd, 1);
-    if ((bd.ctl_stat & 0x80000000) == 0) {
-        s->regs[DMA_STATUS] |= DMA_STATUS_RU | DMA_STATUS_AIS;
-        ret = size;
-        goto out;
-    }
-
-    cpu_physical_memory_write(bd.buffer1_addr, buf, size);
-
-    /* Add in the 4 bytes for crc (the real hw returns length incl crc) */
-    size += 4;
-    bd.ctl_stat = (size << 16) | 0x300;
-    xgmac_write_desc(s, &bd, 1);
-
-    s->stats.rx_bytes += size;
-    s->stats.rx++;
-    if (multicast) {
-        s->stats.rx_mcast++;
-    } else if (broadcast) {
-        s->stats.rx_bcast++;
-    }
-
-    s->regs[DMA_STATUS] |= DMA_STATUS_RI | DMA_STATUS_NIS;
-    ret = size;
-
-out:
-    enet_update_irq(s);
-    return ret;
-}
-
-static void eth_cleanup(NetClientState *nc)
-{
-    struct XgmacState *s = qemu_get_nic_opaque(nc);
-    s->nic = NULL;
-}
-
-static NetClientInfo net_xgmac_enet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
-    .size = sizeof(NICState),
-    .can_receive = eth_can_rx,
-    .receive = eth_rx,
-    .cleanup = eth_cleanup,
-};
-
-static int xgmac_enet_init(SysBusDevice *dev)
-{
-    struct XgmacState *s = FROM_SYSBUS(typeof(*s), dev);
-
-    memory_region_init_io(&s->iomem, &enet_mem_ops, s, "xgmac", 0x1000);
-    sysbus_init_mmio(dev, &s->iomem);
-    sysbus_init_irq(dev, &s->sbd_irq);
-    sysbus_init_irq(dev, &s->pmt_irq);
-    sysbus_init_irq(dev, &s->mci_irq);
-
-    qemu_macaddr_default_if_unset(&s->conf.macaddr);
-    s->nic = qemu_new_nic(&net_xgmac_enet_info, &s->conf,
-                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
-    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
-
-    s->regs[XGMAC_ADDR_HIGH(0)] = (s->conf.macaddr.a[5] << 8) |
-                                   s->conf.macaddr.a[4];
-    s->regs[XGMAC_ADDR_LOW(0)] = (s->conf.macaddr.a[3] << 24) |
-                                 (s->conf.macaddr.a[2] << 16) |
-                                 (s->conf.macaddr.a[1] << 8) |
-                                  s->conf.macaddr.a[0];
-
-    return 0;
-}
-
-static Property xgmac_properties[] = {
-    DEFINE_NIC_PROPERTIES(struct XgmacState, conf),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void xgmac_enet_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    sbc->init = xgmac_enet_init;
-    dc->vmsd = &vmstate_xgmac;
-    dc->props = xgmac_properties;
-}
-
-static const TypeInfo xgmac_enet_info = {
-    .name          = "xgmac",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(struct XgmacState),
-    .class_init    = xgmac_enet_class_init,
-};
-
-static void xgmac_enet_register_types(void)
-{
-    type_register_static(&xgmac_enet_info);
-}
-
-type_init(xgmac_enet_register_types)
diff --git a/hw/xilinx_axidma.c b/hw/xilinx_axidma.c
deleted file mode 100644 (file)
index 8db1a74..0000000
+++ /dev/null
@@ -1,523 +0,0 @@
-/*
- * QEMU model of Xilinx AXI-DMA block.
- *
- * Copyright (c) 2011 Edgar E. Iglesias.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/sysbus.h"
-#include "qemu/timer.h"
-#include "hw/ptimer.h"
-#include "qemu/log.h"
-#include "hw/qdev-addr.h"
-
-#include "hw/stream.h"
-
-#define D(x)
-
-#define R_DMACR             (0x00 / 4)
-#define R_DMASR             (0x04 / 4)
-#define R_CURDESC           (0x08 / 4)
-#define R_TAILDESC          (0x10 / 4)
-#define R_MAX               (0x30 / 4)
-
-enum {
-    DMACR_RUNSTOP = 1,
-    DMACR_TAILPTR_MODE = 2,
-    DMACR_RESET = 4
-};
-
-enum {
-    DMASR_HALTED = 1,
-    DMASR_IDLE  = 2,
-    DMASR_IOC_IRQ  = 1 << 12,
-    DMASR_DLY_IRQ  = 1 << 13,
-
-    DMASR_IRQ_MASK = 7 << 12
-};
-
-struct SDesc {
-    uint64_t nxtdesc;
-    uint64_t buffer_address;
-    uint64_t reserved;
-    uint32_t control;
-    uint32_t status;
-    uint32_t app[6];
-};
-
-enum {
-    SDESC_CTRL_EOF = (1 << 26),
-    SDESC_CTRL_SOF = (1 << 27),
-
-    SDESC_CTRL_LEN_MASK = (1 << 23) - 1
-};
-
-enum {
-    SDESC_STATUS_EOF = (1 << 26),
-    SDESC_STATUS_SOF_BIT = 27,
-    SDESC_STATUS_SOF = (1 << SDESC_STATUS_SOF_BIT),
-    SDESC_STATUS_COMPLETE = (1 << 31)
-};
-
-struct Stream {
-    QEMUBH *bh;
-    ptimer_state *ptimer;
-    qemu_irq irq;
-
-    int nr;
-
-    struct SDesc desc;
-    int pos;
-    unsigned int complete_cnt;
-    uint32_t regs[R_MAX];
-};
-
-struct XilinxAXIDMA {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    uint32_t freqhz;
-    StreamSlave *tx_dev;
-
-    struct Stream streams[2];
-};
-
-/*
- * Helper calls to extract info from desriptors and other trivial
- * state from regs.
- */
-static inline int stream_desc_sof(struct SDesc *d)
-{
-    return d->control & SDESC_CTRL_SOF;
-}
-
-static inline int stream_desc_eof(struct SDesc *d)
-{
-    return d->control & SDESC_CTRL_EOF;
-}
-
-static inline int stream_resetting(struct Stream *s)
-{
-    return !!(s->regs[R_DMACR] & DMACR_RESET);
-}
-
-static inline int stream_running(struct Stream *s)
-{
-    return s->regs[R_DMACR] & DMACR_RUNSTOP;
-}
-
-static inline int stream_halted(struct Stream *s)
-{
-    return s->regs[R_DMASR] & DMASR_HALTED;
-}
-
-static inline int stream_idle(struct Stream *s)
-{
-    return !!(s->regs[R_DMASR] & DMASR_IDLE);
-}
-
-static void stream_reset(struct Stream *s)
-{
-    s->regs[R_DMASR] = DMASR_HALTED;  /* starts up halted.  */
-    s->regs[R_DMACR] = 1 << 16; /* Starts with one in compl threshold.  */
-}
-
-/* Map an offset addr into a channel index.  */
-static inline int streamid_from_addr(hwaddr addr)
-{
-    int sid;
-
-    sid = addr / (0x30);
-    sid &= 1;
-    return sid;
-}
-
-#ifdef DEBUG_ENET
-static void stream_desc_show(struct SDesc *d)
-{
-    qemu_log("buffer_addr  = " PRIx64 "\n", d->buffer_address);
-    qemu_log("nxtdesc      = " PRIx64 "\n", d->nxtdesc);
-    qemu_log("control      = %x\n", d->control);
-    qemu_log("status       = %x\n", d->status);
-}
-#endif
-
-static void stream_desc_load(struct Stream *s, hwaddr addr)
-{
-    struct SDesc *d = &s->desc;
-    int i;
-
-    cpu_physical_memory_read(addr, (void *) d, sizeof *d);
-
-    /* Convert from LE into host endianness.  */
-    d->buffer_address = le64_to_cpu(d->buffer_address);
-    d->nxtdesc = le64_to_cpu(d->nxtdesc);
-    d->control = le32_to_cpu(d->control);
-    d->status = le32_to_cpu(d->status);
-    for (i = 0; i < ARRAY_SIZE(d->app); i++) {
-        d->app[i] = le32_to_cpu(d->app[i]);
-    }
-}
-
-static void stream_desc_store(struct Stream *s, hwaddr addr)
-{
-    struct SDesc *d = &s->desc;
-    int i;
-
-    /* Convert from host endianness into LE.  */
-    d->buffer_address = cpu_to_le64(d->buffer_address);
-    d->nxtdesc = cpu_to_le64(d->nxtdesc);
-    d->control = cpu_to_le32(d->control);
-    d->status = cpu_to_le32(d->status);
-    for (i = 0; i < ARRAY_SIZE(d->app); i++) {
-        d->app[i] = cpu_to_le32(d->app[i]);
-    }
-    cpu_physical_memory_write(addr, (void *) d, sizeof *d);
-}
-
-static void stream_update_irq(struct Stream *s)
-{
-    unsigned int pending, mask, irq;
-
-    pending = s->regs[R_DMASR] & DMASR_IRQ_MASK;
-    mask = s->regs[R_DMACR] & DMASR_IRQ_MASK;
-
-    irq = pending & mask;
-
-    qemu_set_irq(s->irq, !!irq);
-}
-
-static void stream_reload_complete_cnt(struct Stream *s)
-{
-    unsigned int comp_th;
-    comp_th = (s->regs[R_DMACR] >> 16) & 0xff;
-    s->complete_cnt = comp_th;
-}
-
-static void timer_hit(void *opaque)
-{
-    struct Stream *s = opaque;
-
-    stream_reload_complete_cnt(s);
-    s->regs[R_DMASR] |= DMASR_DLY_IRQ;
-    stream_update_irq(s);
-}
-
-static void stream_complete(struct Stream *s)
-{
-    unsigned int comp_delay;
-
-    /* Start the delayed timer.  */
-    comp_delay = s->regs[R_DMACR] >> 24;
-    if (comp_delay) {
-        ptimer_stop(s->ptimer);
-        ptimer_set_count(s->ptimer, comp_delay);
-        ptimer_run(s->ptimer, 1);
-    }
-
-    s->complete_cnt--;
-    if (s->complete_cnt == 0) {
-        /* Raise the IOC irq.  */
-        s->regs[R_DMASR] |= DMASR_IOC_IRQ;
-        stream_reload_complete_cnt(s);
-    }
-}
-
-static void stream_process_mem2s(struct Stream *s,
-                                 StreamSlave *tx_dev)
-{
-    uint32_t prev_d;
-    unsigned char txbuf[16 * 1024];
-    unsigned int txlen;
-    uint32_t app[6];
-
-    if (!stream_running(s) || stream_idle(s)) {
-        return;
-    }
-
-    while (1) {
-        stream_desc_load(s, s->regs[R_CURDESC]);
-
-        if (s->desc.status & SDESC_STATUS_COMPLETE) {
-            s->regs[R_DMASR] |= DMASR_IDLE;
-            break;
-        }
-
-        if (stream_desc_sof(&s->desc)) {
-            s->pos = 0;
-            memcpy(app, s->desc.app, sizeof app);
-        }
-
-        txlen = s->desc.control & SDESC_CTRL_LEN_MASK;
-        if ((txlen + s->pos) > sizeof txbuf) {
-            hw_error("%s: too small internal txbuf! %d\n", __func__,
-                     txlen + s->pos);
-        }
-
-        cpu_physical_memory_read(s->desc.buffer_address,
-                                 txbuf + s->pos, txlen);
-        s->pos += txlen;
-
-        if (stream_desc_eof(&s->desc)) {
-            stream_push(tx_dev, txbuf, s->pos, app);
-            s->pos = 0;
-            stream_complete(s);
-        }
-
-        /* Update the descriptor.  */
-        s->desc.status = txlen | SDESC_STATUS_COMPLETE;
-        stream_desc_store(s, s->regs[R_CURDESC]);
-
-        /* Advance.  */
-        prev_d = s->regs[R_CURDESC];
-        s->regs[R_CURDESC] = s->desc.nxtdesc;
-        if (prev_d == s->regs[R_TAILDESC]) {
-            s->regs[R_DMASR] |= DMASR_IDLE;
-            break;
-        }
-    }
-}
-
-static void stream_process_s2mem(struct Stream *s,
-                                 unsigned char *buf, size_t len, uint32_t *app)
-{
-    uint32_t prev_d;
-    unsigned int rxlen;
-    int pos = 0;
-    int sof = 1;
-
-    if (!stream_running(s) || stream_idle(s)) {
-        return;
-    }
-
-    while (len) {
-        stream_desc_load(s, s->regs[R_CURDESC]);
-
-        if (s->desc.status & SDESC_STATUS_COMPLETE) {
-            s->regs[R_DMASR] |= DMASR_IDLE;
-            break;
-        }
-
-        rxlen = s->desc.control & SDESC_CTRL_LEN_MASK;
-        if (rxlen > len) {
-            /* It fits.  */
-            rxlen = len;
-        }
-
-        cpu_physical_memory_write(s->desc.buffer_address, buf + pos, rxlen);
-        len -= rxlen;
-        pos += rxlen;
-
-        /* Update the descriptor.  */
-        if (!len) {
-            int i;
-
-            stream_complete(s);
-            for (i = 0; i < 5; i++) {
-                s->desc.app[i] = app[i];
-            }
-            s->desc.status |= SDESC_STATUS_EOF;
-        }
-
-        s->desc.status |= sof << SDESC_STATUS_SOF_BIT;
-        s->desc.status |= SDESC_STATUS_COMPLETE;
-        stream_desc_store(s, s->regs[R_CURDESC]);
-        sof = 0;
-
-        /* Advance.  */
-        prev_d = s->regs[R_CURDESC];
-        s->regs[R_CURDESC] = s->desc.nxtdesc;
-        if (prev_d == s->regs[R_TAILDESC]) {
-            s->regs[R_DMASR] |= DMASR_IDLE;
-            break;
-        }
-    }
-}
-
-static void
-axidma_push(StreamSlave *obj, unsigned char *buf, size_t len, uint32_t *app)
-{
-    struct XilinxAXIDMA *d = FROM_SYSBUS(typeof(*d), SYS_BUS_DEVICE(obj));
-    struct Stream *s = &d->streams[1];
-
-    if (!app) {
-        hw_error("No stream app data!\n");
-    }
-    stream_process_s2mem(s, buf, len, app);
-    stream_update_irq(s);
-}
-
-static uint64_t axidma_read(void *opaque, hwaddr addr,
-                            unsigned size)
-{
-    struct XilinxAXIDMA *d = opaque;
-    struct Stream *s;
-    uint32_t r = 0;
-    int sid;
-
-    sid = streamid_from_addr(addr);
-    s = &d->streams[sid];
-
-    addr = addr % 0x30;
-    addr >>= 2;
-    switch (addr) {
-        case R_DMACR:
-            /* Simulate one cycles reset delay.  */
-            s->regs[addr] &= ~DMACR_RESET;
-            r = s->regs[addr];
-            break;
-        case R_DMASR:
-            s->regs[addr] &= 0xffff;
-            s->regs[addr] |= (s->complete_cnt & 0xff) << 16;
-            s->regs[addr] |= (ptimer_get_count(s->ptimer) & 0xff) << 24;
-            r = s->regs[addr];
-            break;
-        default:
-            r = s->regs[addr];
-            D(qemu_log("%s ch=%d addr=" TARGET_FMT_plx " v=%x\n",
-                           __func__, sid, addr * 4, r));
-            break;
-    }
-    return r;
-
-}
-
-static void axidma_write(void *opaque, hwaddr addr,
-                         uint64_t value, unsigned size)
-{
-    struct XilinxAXIDMA *d = opaque;
-    struct Stream *s;
-    int sid;
-
-    sid = streamid_from_addr(addr);
-    s = &d->streams[sid];
-
-    addr = addr % 0x30;
-    addr >>= 2;
-    switch (addr) {
-        case R_DMACR:
-            /* Tailptr mode is always on.  */
-            value |= DMACR_TAILPTR_MODE;
-            /* Remember our previous reset state.  */
-            value |= (s->regs[addr] & DMACR_RESET);
-            s->regs[addr] = value;
-
-            if (value & DMACR_RESET) {
-                stream_reset(s);
-            }
-
-            if ((value & 1) && !stream_resetting(s)) {
-                /* Start processing.  */
-                s->regs[R_DMASR] &= ~(DMASR_HALTED | DMASR_IDLE);
-            }
-            stream_reload_complete_cnt(s);
-            break;
-
-        case R_DMASR:
-            /* Mask away write to clear irq lines.  */
-            value &= ~(value & DMASR_IRQ_MASK);
-            s->regs[addr] = value;
-            break;
-
-        case R_TAILDESC:
-            s->regs[addr] = value;
-            s->regs[R_DMASR] &= ~DMASR_IDLE; /* Not idle.  */
-            if (!sid) {
-                stream_process_mem2s(s, d->tx_dev);
-            }
-            break;
-        default:
-            D(qemu_log("%s: ch=%d addr=" TARGET_FMT_plx " v=%x\n",
-                  __func__, sid, addr * 4, (unsigned)value));
-            s->regs[addr] = value;
-            break;
-    }
-    stream_update_irq(s);
-}
-
-static const MemoryRegionOps axidma_ops = {
-    .read = axidma_read,
-    .write = axidma_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static int xilinx_axidma_init(SysBusDevice *dev)
-{
-    struct XilinxAXIDMA *s = FROM_SYSBUS(typeof(*s), dev);
-    int i;
-
-    sysbus_init_irq(dev, &s->streams[0].irq);
-    sysbus_init_irq(dev, &s->streams[1].irq);
-
-    memory_region_init_io(&s->iomem, &axidma_ops, s,
-                          "xlnx.axi-dma", R_MAX * 4 * 2);
-    sysbus_init_mmio(dev, &s->iomem);
-
-    for (i = 0; i < 2; i++) {
-        stream_reset(&s->streams[i]);
-        s->streams[i].nr = i;
-        s->streams[i].bh = qemu_bh_new(timer_hit, &s->streams[i]);
-        s->streams[i].ptimer = ptimer_init(s->streams[i].bh);
-        ptimer_set_freq(s->streams[i].ptimer, s->freqhz);
-    }
-    return 0;
-}
-
-static void xilinx_axidma_initfn(Object *obj)
-{
-    struct XilinxAXIDMA *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj));
-
-    object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
-                             (Object **) &s->tx_dev, NULL);
-}
-
-static Property axidma_properties[] = {
-    DEFINE_PROP_UINT32("freqhz", struct XilinxAXIDMA, freqhz, 50000000),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void axidma_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-    StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
-
-    k->init = xilinx_axidma_init;
-    dc->props = axidma_properties;
-    ssc->push = axidma_push;
-}
-
-static const TypeInfo axidma_info = {
-    .name          = "xlnx.axi-dma",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(struct XilinxAXIDMA),
-    .class_init    = axidma_class_init,
-    .instance_init = xilinx_axidma_initfn,
-    .interfaces = (InterfaceInfo[]) {
-        { TYPE_STREAM_SLAVE },
-        { }
-    }
-};
-
-static void xilinx_axidma_register_types(void)
-{
-    type_register_static(&axidma_info);
-}
-
-type_init(xilinx_axidma_register_types)
diff --git a/hw/xilinx_axienet.c b/hw/xilinx_axienet.c
deleted file mode 100644 (file)
index 07c4bad..0000000
+++ /dev/null
@@ -1,918 +0,0 @@
-/*
- * QEMU model of Xilinx AXI-Ethernet.
- *
- * Copyright (c) 2011 Edgar E. Iglesias.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/sysbus.h"
-#include "qemu/log.h"
-#include "net/net.h"
-#include "net/checksum.h"
-#include "qapi/qmp/qerror.h"
-
-#include "hw/stream.h"
-
-#define DPHY(x)
-
-/* Advertisement control register. */
-#define ADVERTISE_10HALF        0x0020  /* Try for 10mbps half-duplex  */
-#define ADVERTISE_10FULL        0x0040  /* Try for 10mbps full-duplex  */
-#define ADVERTISE_100HALF       0x0080  /* Try for 100mbps half-duplex */
-#define ADVERTISE_100FULL       0x0100  /* Try for 100mbps full-duplex */
-
-struct PHY {
-    uint32_t regs[32];
-
-    int link;
-
-    unsigned int (*read)(struct PHY *phy, unsigned int req);
-    void (*write)(struct PHY *phy, unsigned int req,
-                  unsigned int data);
-};
-
-static unsigned int tdk_read(struct PHY *phy, unsigned int req)
-{
-    int regnum;
-    unsigned r = 0;
-
-    regnum = req & 0x1f;
-
-    switch (regnum) {
-        case 1:
-            if (!phy->link) {
-                break;
-            }
-            /* MR1.  */
-            /* Speeds and modes.  */
-            r |= (1 << 13) | (1 << 14);
-            r |= (1 << 11) | (1 << 12);
-            r |= (1 << 5); /* Autoneg complete.  */
-            r |= (1 << 3); /* Autoneg able.  */
-            r |= (1 << 2); /* link.  */
-            r |= (1 << 1); /* link.  */
-            break;
-        case 5:
-            /* Link partner ability.
-               We are kind; always agree with whatever best mode
-               the guest advertises.  */
-            r = 1 << 14; /* Success.  */
-            /* Copy advertised modes.  */
-            r |= phy->regs[4] & (15 << 5);
-            /* Autoneg support.  */
-            r |= 1;
-            break;
-        case 17:
-            /* Marvel PHY on many xilinx boards.  */
-            r = 0x8000; /* 1000Mb  */
-            break;
-        case 18:
-            {
-                /* Diagnostics reg.  */
-                int duplex = 0;
-                int speed_100 = 0;
-
-                if (!phy->link) {
-                    break;
-                }
-
-                /* Are we advertising 100 half or 100 duplex ? */
-                speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF);
-                speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL);
-
-                /* Are we advertising 10 duplex or 100 duplex ? */
-                duplex = !!(phy->regs[4] & ADVERTISE_100FULL);
-                duplex |= !!(phy->regs[4] & ADVERTISE_10FULL);
-                r = (speed_100 << 10) | (duplex << 11);
-            }
-            break;
-
-        default:
-            r = phy->regs[regnum];
-            break;
-    }
-    DPHY(qemu_log("\n%s %x = reg[%d]\n", __func__, r, regnum));
-    return r;
-}
-
-static void
-tdk_write(struct PHY *phy, unsigned int req, unsigned int data)
-{
-    int regnum;
-
-    regnum = req & 0x1f;
-    DPHY(qemu_log("%s reg[%d] = %x\n", __func__, regnum, data));
-    switch (regnum) {
-        default:
-            phy->regs[regnum] = data;
-            break;
-    }
-}
-
-static void
-tdk_init(struct PHY *phy)
-{
-    phy->regs[0] = 0x3100;
-    /* PHY Id.  */
-    phy->regs[2] = 0x0300;
-    phy->regs[3] = 0xe400;
-    /* Autonegotiation advertisement reg.  */
-    phy->regs[4] = 0x01E1;
-    phy->link = 1;
-
-    phy->read = tdk_read;
-    phy->write = tdk_write;
-}
-
-struct MDIOBus {
-    /* bus.  */
-    int mdc;
-    int mdio;
-
-    /* decoder.  */
-    enum {
-        PREAMBLE,
-        SOF,
-        OPC,
-        ADDR,
-        REQ,
-        TURNAROUND,
-        DATA
-    } state;
-    unsigned int drive;
-
-    unsigned int cnt;
-    unsigned int addr;
-    unsigned int opc;
-    unsigned int req;
-    unsigned int data;
-
-    struct PHY *devs[32];
-};
-
-static void
-mdio_attach(struct MDIOBus *bus, struct PHY *phy, unsigned int addr)
-{
-    bus->devs[addr & 0x1f] = phy;
-}
-
-#ifdef USE_THIS_DEAD_CODE
-static void
-mdio_detach(struct MDIOBus *bus, struct PHY *phy, unsigned int addr)
-{
-    bus->devs[addr & 0x1f] = NULL;
-}
-#endif
-
-static uint16_t mdio_read_req(struct MDIOBus *bus, unsigned int addr,
-                  unsigned int reg)
-{
-    struct PHY *phy;
-    uint16_t data;
-
-    phy = bus->devs[addr];
-    if (phy && phy->read) {
-        data = phy->read(phy, reg);
-    } else {
-        data = 0xffff;
-    }
-    DPHY(qemu_log("%s addr=%d reg=%d data=%x\n", __func__, addr, reg, data));
-    return data;
-}
-
-static void mdio_write_req(struct MDIOBus *bus, unsigned int addr,
-               unsigned int reg, uint16_t data)
-{
-    struct PHY *phy;
-
-    DPHY(qemu_log("%s addr=%d reg=%d data=%x\n", __func__, addr, reg, data));
-    phy = bus->devs[addr];
-    if (phy && phy->write) {
-        phy->write(phy, reg, data);
-    }
-}
-
-#define DENET(x)
-
-#define R_RAF      (0x000 / 4)
-enum {
-    RAF_MCAST_REJ = (1 << 1),
-    RAF_BCAST_REJ = (1 << 2),
-    RAF_EMCF_EN = (1 << 12),
-    RAF_NEWFUNC_EN = (1 << 11)
-};
-
-#define R_IS       (0x00C / 4)
-enum {
-    IS_HARD_ACCESS_COMPLETE = 1,
-    IS_AUTONEG = (1 << 1),
-    IS_RX_COMPLETE = (1 << 2),
-    IS_RX_REJECT = (1 << 3),
-    IS_TX_COMPLETE = (1 << 5),
-    IS_RX_DCM_LOCK = (1 << 6),
-    IS_MGM_RDY = (1 << 7),
-    IS_PHY_RST_DONE = (1 << 8),
-};
-
-#define R_IP       (0x010 / 4)
-#define R_IE       (0x014 / 4)
-#define R_UAWL     (0x020 / 4)
-#define R_UAWU     (0x024 / 4)
-#define R_PPST     (0x030 / 4)
-enum {
-    PPST_LINKSTATUS = (1 << 0),
-    PPST_PHY_LINKSTATUS = (1 << 7),
-};
-
-#define R_STATS_RX_BYTESL (0x200 / 4)
-#define R_STATS_RX_BYTESH (0x204 / 4)
-#define R_STATS_TX_BYTESL (0x208 / 4)
-#define R_STATS_TX_BYTESH (0x20C / 4)
-#define R_STATS_RXL       (0x290 / 4)
-#define R_STATS_RXH       (0x294 / 4)
-#define R_STATS_RX_BCASTL (0x2a0 / 4)
-#define R_STATS_RX_BCASTH (0x2a4 / 4)
-#define R_STATS_RX_MCASTL (0x2a8 / 4)
-#define R_STATS_RX_MCASTH (0x2ac / 4)
-
-#define R_RCW0     (0x400 / 4)
-#define R_RCW1     (0x404 / 4)
-enum {
-    RCW1_VLAN = (1 << 27),
-    RCW1_RX   = (1 << 28),
-    RCW1_FCS  = (1 << 29),
-    RCW1_JUM  = (1 << 30),
-    RCW1_RST  = (1 << 31),
-};
-
-#define R_TC       (0x408 / 4)
-enum {
-    TC_VLAN = (1 << 27),
-    TC_TX   = (1 << 28),
-    TC_FCS  = (1 << 29),
-    TC_JUM  = (1 << 30),
-    TC_RST  = (1 << 31),
-};
-
-#define R_EMMC     (0x410 / 4)
-enum {
-    EMMC_LINKSPEED_10MB = (0 << 30),
-    EMMC_LINKSPEED_100MB = (1 << 30),
-    EMMC_LINKSPEED_1000MB = (2 << 30),
-};
-
-#define R_PHYC     (0x414 / 4)
-
-#define R_MC       (0x500 / 4)
-#define MC_EN      (1 << 6)
-
-#define R_MCR      (0x504 / 4)
-#define R_MWD      (0x508 / 4)
-#define R_MRD      (0x50c / 4)
-#define R_MIS      (0x600 / 4)
-#define R_MIP      (0x620 / 4)
-#define R_MIE      (0x640 / 4)
-#define R_MIC      (0x640 / 4)
-
-#define R_UAW0     (0x700 / 4)
-#define R_UAW1     (0x704 / 4)
-#define R_FMI      (0x708 / 4)
-#define R_AF0      (0x710 / 4)
-#define R_AF1      (0x714 / 4)
-#define R_MAX      (0x34 / 4)
-
-/* Indirect registers.  */
-struct TEMAC  {
-    struct MDIOBus mdio_bus;
-    struct PHY phy;
-
-    void *parent;
-};
-
-struct XilinxAXIEnet {
-    SysBusDevice busdev;
-    MemoryRegion iomem;
-    qemu_irq irq;
-    StreamSlave *tx_dev;
-    NICState *nic;
-    NICConf conf;
-
-
-    uint32_t c_rxmem;
-    uint32_t c_txmem;
-    uint32_t c_phyaddr;
-
-    struct TEMAC TEMAC;
-
-    /* MII regs.  */
-    union {
-        uint32_t regs[4];
-        struct {
-            uint32_t mc;
-            uint32_t mcr;
-            uint32_t mwd;
-            uint32_t mrd;
-        };
-    } mii;
-
-    struct {
-        uint64_t rx_bytes;
-        uint64_t tx_bytes;
-
-        uint64_t rx;
-        uint64_t rx_bcast;
-        uint64_t rx_mcast;
-    } stats;
-
-    /* Receive configuration words.  */
-    uint32_t rcw[2];
-    /* Transmit config.  */
-    uint32_t tc;
-    uint32_t emmc;
-    uint32_t phyc;
-
-    /* Unicast Address Word.  */
-    uint32_t uaw[2];
-    /* Unicast address filter used with extended mcast.  */
-    uint32_t ext_uaw[2];
-    uint32_t fmi;
-
-    uint32_t regs[R_MAX];
-
-    /* Multicast filter addrs.  */
-    uint32_t maddr[4][2];
-    /* 32K x 1 lookup filter.  */
-    uint32_t ext_mtable[1024];
-
-
-    uint8_t *rxmem;
-};
-
-static void axienet_rx_reset(struct XilinxAXIEnet *s)
-{
-    s->rcw[1] = RCW1_JUM | RCW1_FCS | RCW1_RX | RCW1_VLAN;
-}
-
-static void axienet_tx_reset(struct XilinxAXIEnet *s)
-{
-    s->tc = TC_JUM | TC_TX | TC_VLAN;
-}
-
-static inline int axienet_rx_resetting(struct XilinxAXIEnet *s)
-{
-    return s->rcw[1] & RCW1_RST;
-}
-
-static inline int axienet_rx_enabled(struct XilinxAXIEnet *s)
-{
-    return s->rcw[1] & RCW1_RX;
-}
-
-static inline int axienet_extmcf_enabled(struct XilinxAXIEnet *s)
-{
-    return !!(s->regs[R_RAF] & RAF_EMCF_EN);
-}
-
-static inline int axienet_newfunc_enabled(struct XilinxAXIEnet *s)
-{
-    return !!(s->regs[R_RAF] & RAF_NEWFUNC_EN);
-}
-
-static void axienet_reset(struct XilinxAXIEnet *s)
-{
-    axienet_rx_reset(s);
-    axienet_tx_reset(s);
-
-    s->regs[R_PPST] = PPST_LINKSTATUS | PPST_PHY_LINKSTATUS;
-    s->regs[R_IS] = IS_AUTONEG | IS_RX_DCM_LOCK | IS_MGM_RDY | IS_PHY_RST_DONE;
-
-    s->emmc = EMMC_LINKSPEED_100MB;
-}
-
-static void enet_update_irq(struct XilinxAXIEnet *s)
-{
-    s->regs[R_IP] = s->regs[R_IS] & s->regs[R_IE];
-    qemu_set_irq(s->irq, !!s->regs[R_IP]);
-}
-
-static uint64_t enet_read(void *opaque, hwaddr addr, unsigned size)
-{
-    struct XilinxAXIEnet *s = opaque;
-    uint32_t r = 0;
-    addr >>= 2;
-
-    switch (addr) {
-        case R_RCW0:
-        case R_RCW1:
-            r = s->rcw[addr & 1];
-            break;
-
-        case R_TC:
-            r = s->tc;
-            break;
-
-        case R_EMMC:
-            r = s->emmc;
-            break;
-
-        case R_PHYC:
-            r = s->phyc;
-            break;
-
-        case R_MCR:
-            r = s->mii.regs[addr & 3] | (1 << 7); /* Always ready.  */
-            break;
-
-        case R_STATS_RX_BYTESL:
-        case R_STATS_RX_BYTESH:
-            r = s->stats.rx_bytes >> (32 * (addr & 1));
-            break;
-
-        case R_STATS_TX_BYTESL:
-        case R_STATS_TX_BYTESH:
-            r = s->stats.tx_bytes >> (32 * (addr & 1));
-            break;
-
-        case R_STATS_RXL:
-        case R_STATS_RXH:
-            r = s->stats.rx >> (32 * (addr & 1));
-            break;
-        case R_STATS_RX_BCASTL:
-        case R_STATS_RX_BCASTH:
-            r = s->stats.rx_bcast >> (32 * (addr & 1));
-            break;
-        case R_STATS_RX_MCASTL:
-        case R_STATS_RX_MCASTH:
-            r = s->stats.rx_mcast >> (32 * (addr & 1));
-            break;
-
-        case R_MC:
-        case R_MWD:
-        case R_MRD:
-            r = s->mii.regs[addr & 3];
-            break;
-
-        case R_UAW0:
-        case R_UAW1:
-            r = s->uaw[addr & 1];
-            break;
-
-        case R_UAWU:
-        case R_UAWL:
-            r = s->ext_uaw[addr & 1];
-            break;
-
-        case R_FMI:
-            r = s->fmi;
-            break;
-
-        case R_AF0:
-        case R_AF1:
-            r = s->maddr[s->fmi & 3][addr & 1];
-            break;
-
-        case 0x8000 ... 0x83ff:
-            r = s->ext_mtable[addr - 0x8000];
-            break;
-
-        default:
-            if (addr < ARRAY_SIZE(s->regs)) {
-                r = s->regs[addr];
-            }
-            DENET(qemu_log("%s addr=" TARGET_FMT_plx " v=%x\n",
-                            __func__, addr * 4, r));
-            break;
-    }
-    return r;
-}
-
-static void enet_write(void *opaque, hwaddr addr,
-                       uint64_t value, unsigned size)
-{
-    struct XilinxAXIEnet *s = opaque;
-    struct TEMAC *t = &s->TEMAC;
-
-    addr >>= 2;
-    switch (addr) {
-        case R_RCW0:
-        case R_RCW1:
-            s->rcw[addr & 1] = value;
-            if ((addr & 1) && value & RCW1_RST) {
-                axienet_rx_reset(s);
-            } else {
-                qemu_flush_queued_packets(qemu_get_queue(s->nic));
-            }
-            break;
-
-        case R_TC:
-            s->tc = value;
-            if (value & TC_RST) {
-                axienet_tx_reset(s);
-            }
-            break;
-
-        case R_EMMC:
-            s->emmc = value;
-            break;
-
-        case R_PHYC:
-            s->phyc = value;
-            break;
-
-        case R_MC:
-             value &= ((1 < 7) - 1);
-
-             /* Enable the MII.  */
-             if (value & MC_EN) {
-                 unsigned int miiclkdiv = value & ((1 << 6) - 1);
-                 if (!miiclkdiv) {
-                     qemu_log("AXIENET: MDIO enabled but MDIOCLK is zero!\n");
-                 }
-             }
-             s->mii.mc = value;
-             break;
-
-        case R_MCR: {
-             unsigned int phyaddr = (value >> 24) & 0x1f;
-             unsigned int regaddr = (value >> 16) & 0x1f;
-             unsigned int op = (value >> 14) & 3;
-             unsigned int initiate = (value >> 11) & 1;
-
-             if (initiate) {
-                 if (op == 1) {
-                     mdio_write_req(&t->mdio_bus, phyaddr, regaddr, s->mii.mwd);
-                 } else if (op == 2) {
-                     s->mii.mrd = mdio_read_req(&t->mdio_bus, phyaddr, regaddr);
-                 } else {
-                     qemu_log("AXIENET: invalid MDIOBus OP=%d\n", op);
-                 }
-             }
-             s->mii.mcr = value;
-             break;
-        }
-
-        case R_MWD:
-        case R_MRD:
-             s->mii.regs[addr & 3] = value;
-             break;
-
-
-        case R_UAW0:
-        case R_UAW1:
-            s->uaw[addr & 1] = value;
-            break;
-
-        case R_UAWL:
-        case R_UAWU:
-            s->ext_uaw[addr & 1] = value;
-            break;
-
-        case R_FMI:
-            s->fmi = value;
-            break;
-
-        case R_AF0:
-        case R_AF1:
-            s->maddr[s->fmi & 3][addr & 1] = value;
-            break;
-
-        case R_IS:
-            s->regs[addr] &= ~value;
-            break;
-
-        case 0x8000 ... 0x83ff:
-            s->ext_mtable[addr - 0x8000] = value;
-            break;
-
-        default:
-            DENET(qemu_log("%s addr=" TARGET_FMT_plx " v=%x\n",
-                           __func__, addr * 4, (unsigned)value));
-            if (addr < ARRAY_SIZE(s->regs)) {
-                s->regs[addr] = value;
-            }
-            break;
-    }
-    enet_update_irq(s);
-}
-
-static const MemoryRegionOps enet_ops = {
-    .read = enet_read,
-    .write = enet_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
-static int eth_can_rx(NetClientState *nc)
-{
-    struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
-
-    /* RX enabled?  */
-    return !axienet_rx_resetting(s) && axienet_rx_enabled(s);
-}
-
-static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1)
-{
-    int match = 1;
-
-    if (memcmp(buf, &f0, 4)) {
-        match = 0;
-    }
-
-    if (buf[4] != (f1 & 0xff) || buf[5] != ((f1 >> 8) & 0xff)) {
-        match = 0;
-    }
-
-    return match;
-}
-
-static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
-{
-    struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
-    static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff,
-                                              0xff, 0xff, 0xff};
-    static const unsigned char sa_ipmcast[3] = {0x01, 0x00, 0x52};
-    uint32_t app[6] = {0};
-    int promisc = s->fmi & (1 << 31);
-    int unicast, broadcast, multicast, ip_multicast = 0;
-    uint32_t csum32;
-    uint16_t csum16;
-    int i;
-
-    DENET(qemu_log("%s: %zd bytes\n", __func__, size));
-
-    unicast = ~buf[0] & 0x1;
-    broadcast = memcmp(buf, sa_bcast, 6) == 0;
-    multicast = !unicast && !broadcast;
-    if (multicast && (memcmp(sa_ipmcast, buf, sizeof sa_ipmcast) == 0)) {
-        ip_multicast = 1;
-    }
-
-    /* Jumbo or vlan sizes ?  */
-    if (!(s->rcw[1] & RCW1_JUM)) {
-        if (size > 1518 && size <= 1522 && !(s->rcw[1] & RCW1_VLAN)) {
-            return size;
-        }
-    }
-
-    /* Basic Address filters.  If you want to use the extended filters
-       you'll generally have to place the ethernet mac into promiscuous mode
-       to avoid the basic filtering from dropping most frames.  */
-    if (!promisc) {
-        if (unicast) {
-            if (!enet_match_addr(buf, s->uaw[0], s->uaw[1])) {
-                return size;
-            }
-        } else {
-            if (broadcast) {
-                /* Broadcast.  */
-                if (s->regs[R_RAF] & RAF_BCAST_REJ) {
-                    return size;
-                }
-            } else {
-                int drop = 1;
-
-                /* Multicast.  */
-                if (s->regs[R_RAF] & RAF_MCAST_REJ) {
-                    return size;
-                }
-
-                for (i = 0; i < 4; i++) {
-                    if (enet_match_addr(buf, s->maddr[i][0], s->maddr[i][1])) {
-                        drop = 0;
-                        break;
-                    }
-                }
-
-                if (drop) {
-                    return size;
-                }
-            }
-        }
-    }
-
-    /* Extended mcast filtering enabled?  */
-    if (axienet_newfunc_enabled(s) && axienet_extmcf_enabled(s)) {
-        if (unicast) {
-            if (!enet_match_addr(buf, s->ext_uaw[0], s->ext_uaw[1])) {
-                return size;
-            }
-        } else {
-            if (broadcast) {
-                /* Broadcast. ???  */
-                if (s->regs[R_RAF] & RAF_BCAST_REJ) {
-                    return size;
-                }
-            } else {
-                int idx, bit;
-
-                /* Multicast.  */
-                if (!memcmp(buf, sa_ipmcast, 3)) {
-                    return size;
-                }
-
-                idx  = (buf[4] & 0x7f) << 8;
-                idx |= buf[5];
-
-                bit = 1 << (idx & 0x1f);
-                idx >>= 5;
-
-                if (!(s->ext_mtable[idx] & bit)) {
-                    return size;
-                }
-            }
-        }
-    }
-
-    if (size < 12) {
-        s->regs[R_IS] |= IS_RX_REJECT;
-        enet_update_irq(s);
-        return -1;
-    }
-
-    if (size > (s->c_rxmem - 4)) {
-        size = s->c_rxmem - 4;
-    }
-
-    memcpy(s->rxmem, buf, size);
-    memset(s->rxmem + size, 0, 4); /* Clear the FCS.  */
-
-    if (s->rcw[1] & RCW1_FCS) {
-        size += 4; /* fcs is inband.  */
-    }
-
-    app[0] = 5 << 28;
-    csum32 = net_checksum_add(size - 14, (uint8_t *)s->rxmem + 14);
-    /* Fold it once.  */
-    csum32 = (csum32 & 0xffff) + (csum32 >> 16);
-    /* And twice to get rid of possible carries.  */
-    csum16 = (csum32 & 0xffff) + (csum32 >> 16);
-    app[3] = csum16;
-    app[4] = size & 0xffff;
-
-    s->stats.rx_bytes += size;
-    s->stats.rx++;
-    if (multicast) {
-        s->stats.rx_mcast++;
-        app[2] |= 1 | (ip_multicast << 1);
-    } else if (broadcast) {
-        s->stats.rx_bcast++;
-        app[2] |= 1 << 3;
-    }
-
-    /* Good frame.  */
-    app[2] |= 1 << 6;
-
-    stream_push(s->tx_dev, (void *)s->rxmem, size, app);
-
-    s->regs[R_IS] |= IS_RX_COMPLETE;
-    enet_update_irq(s);
-    return size;
-}
-
-static void eth_cleanup(NetClientState *nc)
-{
-    /* FIXME.  */
-    struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
-    g_free(s->rxmem);
-    g_free(s);
-}
-
-static void
-axienet_stream_push(StreamSlave *obj, uint8_t *buf, size_t size, uint32_t *hdr)
-{
-    struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj));
-
-    /* TX enable ?  */
-    if (!(s->tc & TC_TX)) {
-        return;
-    }
-
-    /* Jumbo or vlan sizes ?  */
-    if (!(s->tc & TC_JUM)) {
-        if (size > 1518 && size <= 1522 && !(s->tc & TC_VLAN)) {
-            return;
-        }
-    }
-
-    if (hdr[0] & 1) {
-        unsigned int start_off = hdr[1] >> 16;
-        unsigned int write_off = hdr[1] & 0xffff;
-        uint32_t tmp_csum;
-        uint16_t csum;
-
-        tmp_csum = net_checksum_add(size - start_off,
-                                    (uint8_t *)buf + start_off);
-        /* Accumulate the seed.  */
-        tmp_csum += hdr[2] & 0xffff;
-
-        /* Fold the 32bit partial checksum.  */
-        csum = net_checksum_finish(tmp_csum);
-
-        /* Writeback.  */
-        buf[write_off] = csum >> 8;
-        buf[write_off + 1] = csum & 0xff;
-    }
-
-    qemu_send_packet(qemu_get_queue(s->nic), buf, size);
-
-    s->stats.tx_bytes += size;
-    s->regs[R_IS] |= IS_TX_COMPLETE;
-    enet_update_irq(s);
-}
-
-static NetClientInfo net_xilinx_enet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
-    .size = sizeof(NICState),
-    .can_receive = eth_can_rx,
-    .receive = eth_rx,
-    .cleanup = eth_cleanup,
-};
-
-static int xilinx_enet_init(SysBusDevice *dev)
-{
-    struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), dev);
-
-    sysbus_init_irq(dev, &s->irq);
-
-    memory_region_init_io(&s->iomem, &enet_ops, s, "enet", 0x40000);
-    sysbus_init_mmio(dev, &s->iomem);
-
-    qemu_macaddr_default_if_unset(&s->conf.macaddr);
-    s->nic = qemu_new_nic(&net_xilinx_enet_info, &s->conf,
-                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
-    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
-
-    tdk_init(&s->TEMAC.phy);
-    mdio_attach(&s->TEMAC.mdio_bus, &s->TEMAC.phy, s->c_phyaddr);
-
-    s->TEMAC.parent = s;
-
-    s->rxmem = g_malloc(s->c_rxmem);
-    axienet_reset(s);
-
-    return 0;
-}
-
-static void xilinx_enet_initfn(Object *obj)
-{
-    struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj));
-    Error *errp = NULL;
-
-    object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
-                             (Object **) &s->tx_dev, &errp);
-    assert_no_error(errp);
-}
-
-static Property xilinx_enet_properties[] = {
-    DEFINE_PROP_UINT32("phyaddr", struct XilinxAXIEnet, c_phyaddr, 7),
-    DEFINE_PROP_UINT32("rxmem", struct XilinxAXIEnet, c_rxmem, 0x1000),
-    DEFINE_PROP_UINT32("txmem", struct XilinxAXIEnet, c_txmem, 0x1000),
-    DEFINE_NIC_PROPERTIES(struct XilinxAXIEnet, conf),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void xilinx_enet_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-    StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
-
-    k->init = xilinx_enet_init;
-    dc->props = xilinx_enet_properties;
-    ssc->push = axienet_stream_push;
-}
-
-static const TypeInfo xilinx_enet_info = {
-    .name          = "xlnx.axi-ethernet",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(struct XilinxAXIEnet),
-    .class_init    = xilinx_enet_class_init,
-    .instance_init = xilinx_enet_initfn,
-    .interfaces = (InterfaceInfo[]) {
-            { TYPE_STREAM_SLAVE },
-            { }
-    }
-};
-
-static void xilinx_enet_register_types(void)
-{
-    type_register_static(&xilinx_enet_info);
-}
-
-type_init(xilinx_enet_register_types)
diff --git a/hw/xilinx_intc.c b/hw/xilinx_intc.c
deleted file mode 100644 (file)
index b106e72..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * QEMU Xilinx OPB Interrupt Controller.
- *
- * Copyright (c) 2009 Edgar E. Iglesias.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/sysbus.h"
-#include "hw/hw.h"
-
-#define D(x)
-
-#define R_ISR       0
-#define R_IPR       1
-#define R_IER       2
-#define R_IAR       3
-#define R_SIE       4
-#define R_CIE       5
-#define R_IVR       6
-#define R_MER       7
-#define R_MAX       8
-
-struct xlx_pic
-{
-    SysBusDevice busdev;
-    MemoryRegion mmio;
-    qemu_irq parent_irq;
-
-    /* Configuration reg chosen at synthesis-time. QEMU populates
-       the bits at board-setup.  */
-    uint32_t c_kind_of_intr;
-
-    /* Runtime control registers.  */
-    uint32_t regs[R_MAX];
-};
-
-static void update_irq(struct xlx_pic *p)
-{
-    uint32_t i;
-    /* Update the pending register.  */
-    p->regs[R_IPR] = p->regs[R_ISR] & p->regs[R_IER];
-
-    /* Update the vector register.  */
-    for (i = 0; i < 32; i++) {
-        if (p->regs[R_IPR] & (1 << i))
-            break;
-    }
-    if (i == 32)
-        i = ~0;
-
-    p->regs[R_IVR] = i;
-    if ((p->regs[R_MER] & 1) && p->regs[R_IPR]) {
-        qemu_irq_raise(p->parent_irq);
-    } else {
-        qemu_irq_lower(p->parent_irq);
-    }
-}
-
-static uint64_t
-pic_read(void *opaque, hwaddr addr, unsigned int size)
-{
-    struct xlx_pic *p = opaque;
-    uint32_t r = 0;
-
-    addr >>= 2;
-    switch (addr)
-    {
-        default:
-            if (addr < ARRAY_SIZE(p->regs))
-                r = p->regs[addr];
-            break;
-
-    }
-    D(printf("%s %x=%x\n", __func__, addr * 4, r));
-    return r;
-}
-
-static void
-pic_write(void *opaque, hwaddr addr,
-          uint64_t val64, unsigned int size)
-{
-    struct xlx_pic *p = opaque;
-    uint32_t value = val64;
-
-    addr >>= 2;
-    D(qemu_log("%s addr=%x val=%x\n", __func__, addr * 4, value));
-    switch (addr) 
-    {
-        case R_IAR:
-            p->regs[R_ISR] &= ~value; /* ACK.  */
-            break;
-        case R_SIE:
-            p->regs[R_IER] |= value;  /* Atomic set ie.  */
-            break;
-        case R_CIE:
-            p->regs[R_IER] &= ~value; /* Atomic clear ie.  */
-            break;
-        default:
-            if (addr < ARRAY_SIZE(p->regs))
-                p->regs[addr] = value;
-            break;
-    }
-    update_irq(p);
-}
-
-static const MemoryRegionOps pic_ops = {
-    .read = pic_read,
-    .write = pic_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .valid = {
-        .min_access_size = 4,
-        .max_access_size = 4
-    }
-};
-
-static void irq_handler(void *opaque, int irq, int level)
-{
-    struct xlx_pic *p = opaque;
-
-    if (!(p->regs[R_MER] & 2)) {
-        qemu_irq_lower(p->parent_irq);
-        return;
-    }
-
-    /* Update source flops. Don't clear unless level triggered.
-       Edge triggered interrupts only go away when explicitely acked to
-       the interrupt controller.  */
-    if (!(p->c_kind_of_intr & (1 << irq)) || level) {
-        p->regs[R_ISR] &= ~(1 << irq);
-        p->regs[R_ISR] |= (level << irq);
-    }
-    update_irq(p);
-}
-
-static int xilinx_intc_init(SysBusDevice *dev)
-{
-    struct xlx_pic *p = FROM_SYSBUS(typeof (*p), dev);
-
-    qdev_init_gpio_in(&dev->qdev, irq_handler, 32);
-    sysbus_init_irq(dev, &p->parent_irq);
-
-    memory_region_init_io(&p->mmio, &pic_ops, p, "xlnx.xps-intc", R_MAX * 4);
-    sysbus_init_mmio(dev, &p->mmio);
-    return 0;
-}
-
-static Property xilinx_intc_properties[] = {
-    DEFINE_PROP_UINT32("kind-of-intr", struct xlx_pic, c_kind_of_intr, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void xilinx_intc_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = xilinx_intc_init;
-    dc->props = xilinx_intc_properties;
-}
-
-static const TypeInfo xilinx_intc_info = {
-    .name          = "xlnx.xps-intc",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(struct xlx_pic),
-    .class_init    = xilinx_intc_class_init,
-};
-
-static void xilinx_intc_register_types(void)
-{
-    type_register_static(&xilinx_intc_info);
-}
-
-type_init(xilinx_intc_register_types)
diff --git a/hw/xilinx_timer.c b/hw/xilinx_timer.c
deleted file mode 100644 (file)
index 0c39cff..0000000
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * QEMU model of the Xilinx timer block.
- *
- * Copyright (c) 2009 Edgar E. Iglesias.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/sysbus.h"
-#include "hw/ptimer.h"
-#include "qemu/log.h"
-
-#define D(x)
-
-#define R_TCSR     0
-#define R_TLR      1
-#define R_TCR      2
-#define R_MAX      4
-
-#define TCSR_MDT        (1<<0)
-#define TCSR_UDT        (1<<1)
-#define TCSR_GENT       (1<<2)
-#define TCSR_CAPT       (1<<3)
-#define TCSR_ARHT       (1<<4)
-#define TCSR_LOAD       (1<<5)
-#define TCSR_ENIT       (1<<6)
-#define TCSR_ENT        (1<<7)
-#define TCSR_TINT       (1<<8)
-#define TCSR_PWMA       (1<<9)
-#define TCSR_ENALL      (1<<10)
-
-struct xlx_timer
-{
-    QEMUBH *bh;
-    ptimer_state *ptimer;
-    void *parent;
-    int nr; /* for debug.  */
-
-    unsigned long timer_div;
-
-    uint32_t regs[R_MAX];
-};
-
-struct timerblock
-{
-    SysBusDevice busdev;
-    MemoryRegion mmio;
-    qemu_irq irq;
-    uint8_t one_timer_only;
-    uint32_t freq_hz;
-    struct xlx_timer *timers;
-};
-
-static inline unsigned int num_timers(struct timerblock *t)
-{
-    return 2 - t->one_timer_only;
-}
-
-static inline unsigned int timer_from_addr(hwaddr addr)
-{
-    /* Timers get a 4x32bit control reg area each.  */
-    return addr >> 2;
-}
-
-static void timer_update_irq(struct timerblock *t)
-{
-    unsigned int i, irq = 0;
-    uint32_t csr;
-
-    for (i = 0; i < num_timers(t); i++) {
-        csr = t->timers[i].regs[R_TCSR];
-        irq |= (csr & TCSR_TINT) && (csr & TCSR_ENIT);
-    }
-
-    /* All timers within the same slave share a single IRQ line.  */
-    qemu_set_irq(t->irq, !!irq);
-}
-
-static uint64_t
-timer_read(void *opaque, hwaddr addr, unsigned int size)
-{
-    struct timerblock *t = opaque;
-    struct xlx_timer *xt;
-    uint32_t r = 0;
-    unsigned int timer;
-
-    addr >>= 2;
-    timer = timer_from_addr(addr);
-    xt = &t->timers[timer];
-    /* Further decoding to address a specific timers reg.  */
-    addr &= 0x3;
-    switch (addr)
-    {
-        case R_TCR:
-                r = ptimer_get_count(xt->ptimer);
-                if (!(xt->regs[R_TCSR] & TCSR_UDT))
-                    r = ~r;
-                D(qemu_log("xlx_timer t=%d read counter=%x udt=%d\n",
-                         timer, r, xt->regs[R_TCSR] & TCSR_UDT));
-            break;
-        default:
-            if (addr < ARRAY_SIZE(xt->regs))
-                r = xt->regs[addr];
-            break;
-
-    }
-    D(fprintf(stderr, "%s timer=%d %x=%x\n", __func__, timer, addr * 4, r));
-    return r;
-}
-
-static void timer_enable(struct xlx_timer *xt)
-{
-    uint64_t count;
-
-    D(fprintf(stderr, "%s timer=%d down=%d\n", __func__,
-              xt->nr, xt->regs[R_TCSR] & TCSR_UDT));
-
-    ptimer_stop(xt->ptimer);
-
-    if (xt->regs[R_TCSR] & TCSR_UDT)
-        count = xt->regs[R_TLR];
-    else
-        count = ~0 - xt->regs[R_TLR];
-    ptimer_set_limit(xt->ptimer, count, 1);
-    ptimer_run(xt->ptimer, 1);
-}
-
-static void
-timer_write(void *opaque, hwaddr addr,
-            uint64_t val64, unsigned int size)
-{
-    struct timerblock *t = opaque;
-    struct xlx_timer *xt;
-    unsigned int timer;
-    uint32_t value = val64;
-
-    addr >>= 2;
-    timer = timer_from_addr(addr);
-    xt = &t->timers[timer];
-    D(fprintf(stderr, "%s addr=%x val=%x (timer=%d off=%d)\n",
-             __func__, addr * 4, value, timer, addr & 3));
-    /* Further decoding to address a specific timers reg.  */
-    addr &= 3;
-    switch (addr) 
-    {
-        case R_TCSR:
-            if (value & TCSR_TINT)
-                value &= ~TCSR_TINT;
-
-            xt->regs[addr] = value;
-            if (value & TCSR_ENT)
-                timer_enable(xt);
-            break;
-        default:
-            if (addr < ARRAY_SIZE(xt->regs))
-                xt->regs[addr] = value;
-            break;
-    }
-    timer_update_irq(t);
-}
-
-static const MemoryRegionOps timer_ops = {
-    .read = timer_read,
-    .write = timer_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .valid = {
-        .min_access_size = 4,
-        .max_access_size = 4
-    }
-};
-
-static void timer_hit(void *opaque)
-{
-    struct xlx_timer *xt = opaque;
-    struct timerblock *t = xt->parent;
-    D(fprintf(stderr, "%s %d\n", __func__, xt->nr));
-    xt->regs[R_TCSR] |= TCSR_TINT;
-
-    if (xt->regs[R_TCSR] & TCSR_ARHT)
-        timer_enable(xt);
-    timer_update_irq(t);
-}
-
-static int xilinx_timer_init(SysBusDevice *dev)
-{
-    struct timerblock *t = FROM_SYSBUS(typeof (*t), dev);
-    unsigned int i;
-
-    /* All timers share a single irq line.  */
-    sysbus_init_irq(dev, &t->irq);
-
-    /* Init all the ptimers.  */
-    t->timers = g_malloc0(sizeof t->timers[0] * num_timers(t));
-    for (i = 0; i < num_timers(t); i++) {
-        struct xlx_timer *xt = &t->timers[i];
-
-        xt->parent = t;
-        xt->nr = i;
-        xt->bh = qemu_bh_new(timer_hit, xt);
-        xt->ptimer = ptimer_init(xt->bh);
-        ptimer_set_freq(xt->ptimer, t->freq_hz);
-    }
-
-    memory_region_init_io(&t->mmio, &timer_ops, t, "xlnx.xps-timer",
-                          R_MAX * 4 * num_timers(t));
-    sysbus_init_mmio(dev, &t->mmio);
-    return 0;
-}
-
-static Property xilinx_timer_properties[] = {
-    DEFINE_PROP_UINT32("clock-frequency", struct timerblock, freq_hz,
-                                                                62 * 1000000),
-    DEFINE_PROP_UINT8("one-timer-only", struct timerblock, one_timer_only, 0),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void xilinx_timer_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
-    k->init = xilinx_timer_init;
-    dc->props = xilinx_timer_properties;
-}
-
-static const TypeInfo xilinx_timer_info = {
-    .name          = "xlnx.xps-timer",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(struct timerblock),
-    .class_init    = xilinx_timer_class_init,
-};
-
-static void xilinx_timer_register_types(void)
-{
-    type_register_static(&xilinx_timer_info);
-}
-
-type_init(xilinx_timer_register_types)
diff --git a/hw/xilinx_uartlite.c b/hw/xilinx_uartlite.c
deleted file mode 100644 (file)
index 079f4d4..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * QEMU model of Xilinx uartlite.
- *
- * Copyright (c) 2009 Edgar E. Iglesias.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "hw/sysbus.h"
-#include "char/char.h"
-
-#define DUART(x)
-
-#define R_RX            0
-#define R_TX            1
-#define R_STATUS        2
-#define R_CTRL          3
-#define R_MAX           4
-
-#define STATUS_RXVALID    0x01
-#define STATUS_RXFULL     0x02
-#define STATUS_TXEMPTY    0x04
-#define STATUS_TXFULL     0x08
-#define STATUS_IE         0x10
-#define STATUS_OVERRUN    0x20
-#define STATUS_FRAME      0x40
-#define STATUS_PARITY     0x80
-
-#define CONTROL_RST_TX    0x01
-#define CONTROL_RST_RX    0x02
-#define CONTROL_IE        0x10
-
-struct xlx_uartlite
-{
-    SysBusDevice busdev;
-    MemoryRegion mmio;
-    CharDriverState *chr;
-    qemu_irq irq;
-
-    uint8_t rx_fifo[8];
-    unsigned int rx_fifo_pos;
-    unsigned int rx_fifo_len;
-
-    uint32_t regs[R_MAX];
-};
-
-static void uart_update_irq(struct xlx_uartlite *s)
-{
-    unsigned int irq;
-
-    if (s->rx_fifo_len)
-        s->regs[R_STATUS] |= STATUS_IE;
-
-    irq = (s->regs[R_STATUS] & STATUS_IE) && (s->regs[R_CTRL] & CONTROL_IE);
-    qemu_set_irq(s->irq, irq);
-}
-
-static void uart_update_status(struct xlx_uartlite *s)
-{
-    uint32_t r;
-
-    r = s->regs[R_STATUS];
-    r &= ~7;
-    r |= 1 << 2; /* Tx fifo is always empty. We are fast :) */
-    r |= (s->rx_fifo_len == sizeof (s->rx_fifo)) << 1;
-    r |= (!!s->rx_fifo_len);
-    s->regs[R_STATUS] = r;
-}
-
-static uint64_t
-uart_read(void *opaque, hwaddr addr, unsigned int size)
-{
-    struct xlx_uartlite *s = opaque;
-    uint32_t r = 0;
-    addr >>= 2;
-    switch (addr)
-    {
-        case R_RX:
-            r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 7];
-            if (s->rx_fifo_len)
-                s->rx_fifo_len--;
-            uart_update_status(s);
-            uart_update_irq(s);
-            qemu_chr_accept_input(s->chr);
-            break;
-
-        default:
-            if (addr < ARRAY_SIZE(s->regs))
-                r = s->regs[addr];
-            DUART(qemu_log("%s addr=%x v=%x\n", __func__, addr, r));
-            break;
-    }
-    return r;
-}
-
-static void
-uart_write(void *opaque, hwaddr addr,
-           uint64_t val64, unsigned int size)
-{
-    struct xlx_uartlite *s = opaque;
-    uint32_t value = val64;
-    unsigned char ch = value;
-
-    addr >>= 2;
-    switch (addr)
-    {
-        case R_STATUS:
-            hw_error("write to UART STATUS?\n");
-            break;
-
-        case R_CTRL:
-            if (value & CONTROL_RST_RX) {
-                s->rx_fifo_pos = 0;
-                s->rx_fifo_len = 0;
-            }
-            s->regs[addr] = value;
-            break;
-
-        case R_TX:
-            if (s->chr)
-                qemu_chr_fe_write(s->chr, &ch, 1);
-
-            s->regs[addr] = value;
-
-            /* hax.  */
-            s->regs[R_STATUS] |= STATUS_IE;
-            break;
-
-        default:
-            DUART(printf("%s addr=%x v=%x\n", __func__, addr, value));
-            if (addr < ARRAY_SIZE(s->regs))
-                s->regs[addr] = value;
-            break;
-    }
-    uart_update_status(s);
-    uart_update_irq(s);
-}
-
-static const MemoryRegionOps uart_ops = {
-    .read = uart_read,
-    .write = uart_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .valid = {
-        .min_access_size = 1,
-        .max_access_size = 4
-    }
-};
-
-static void uart_rx(void *opaque, const uint8_t *buf, int size)
-{
-    struct xlx_uartlite *s = opaque;
-
-    /* Got a byte.  */
-    if (s->rx_fifo_len >= 8) {
-        printf("WARNING: UART dropped char.\n");
-        return;
-    }
-    s->rx_fifo[s->rx_fifo_pos] = *buf;
-    s->rx_fifo_pos++;
-    s->rx_fifo_pos &= 0x7;
-    s->rx_fifo_len++;
-
-    uart_update_status(s);
-    uart_update_irq(s);
-}
-
-static int uart_can_rx(void *opaque)
-{
-    struct xlx_uartlite *s = opaque;
-
-    return s->rx_fifo_len < sizeof(s->rx_fifo);
-}
-
-static void uart_event(void *opaque, int event)
-{
-
-}
-
-static int xilinx_uartlite_init(SysBusDevice *dev)
-{
-    struct xlx_uartlite *s = FROM_SYSBUS(typeof (*s), dev);
-
-    sysbus_init_irq(dev, &s->irq);
-
-    uart_update_status(s);
-    memory_region_init_io(&s->mmio, &uart_ops, s, "xlnx.xps-uartlite",
-                                                                R_MAX * 4);
-    sysbus_init_mmio(dev, &s->mmio);
-
-    s->chr = qemu_char_get_next_serial();
-    if (s->chr)
-        qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
-    return 0;
-}
-
-static void xilinx_uartlite_class_init(ObjectClass *klass, void *data)
-{
-    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
-
-    sdc->init = xilinx_uartlite_init;
-}
-
-static const TypeInfo xilinx_uartlite_info = {
-    .name          = "xlnx.xps-uartlite",
-    .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof (struct xlx_uartlite),
-    .class_init    = xilinx_uartlite_class_init,
-};
-
-static void xilinx_uart_register_types(void)
-{
-    type_register_static(&xilinx_uartlite_info);
-}
-
-type_init(xilinx_uart_register_types)
diff --git a/hw/xio3130_downstream.c b/hw/xio3130_downstream.c
deleted file mode 100644 (file)
index b868f56..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * x3130_downstream.c
- * TI X3130 pci express downstream port switch
- *
- * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
- *                    VA Linux Systems Japan K.K.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/pci/pci_ids.h"
-#include "hw/pci/msi.h"
-#include "hw/pci/pcie.h"
-#include "hw/xio3130_downstream.h"
-
-#define PCI_DEVICE_ID_TI_XIO3130D       0x8233  /* downstream port */
-#define XIO3130_REVISION                0x1
-#define XIO3130_MSI_OFFSET              0x70
-#define XIO3130_MSI_SUPPORTED_FLAGS     PCI_MSI_FLAGS_64BIT
-#define XIO3130_MSI_NR_VECTOR           1
-#define XIO3130_SSVID_OFFSET            0x80
-#define XIO3130_SSVID_SVID              0
-#define XIO3130_SSVID_SSID              0
-#define XIO3130_EXP_OFFSET              0x90
-#define XIO3130_AER_OFFSET              0x100
-
-static void xio3130_downstream_write_config(PCIDevice *d, uint32_t address,
-                                         uint32_t val, int len)
-{
-    pci_bridge_write_config(d, address, val, len);
-    pcie_cap_flr_write_config(d, address, val, len);
-    pcie_cap_slot_write_config(d, address, val, len);
-    pcie_aer_write_config(d, address, val, len);
-}
-
-static void xio3130_downstream_reset(DeviceState *qdev)
-{
-    PCIDevice *d = PCI_DEVICE(qdev);
-
-    pcie_cap_deverr_reset(d);
-    pcie_cap_slot_reset(d);
-    pcie_cap_ari_reset(d);
-    pci_bridge_reset(qdev);
-}
-
-static int xio3130_downstream_initfn(PCIDevice *d)
-{
-    PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
-    PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
-    PCIESlot *s = DO_UPCAST(PCIESlot, port, p);
-    int rc;
-
-    rc = pci_bridge_initfn(d, TYPE_PCIE_BUS);
-    if (rc < 0) {
-        return rc;
-    }
-
-    pcie_port_init_reg(d);
-
-    rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
-                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
-                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
-    if (rc < 0) {
-        goto err_bridge;
-    }
-    rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET,
-                               XIO3130_SSVID_SVID, XIO3130_SSVID_SSID);
-    if (rc < 0) {
-        goto err_bridge;
-    }
-    rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_DOWNSTREAM,
-                       p->port);
-    if (rc < 0) {
-        goto err_msi;
-    }
-    pcie_cap_flr_init(d);
-    pcie_cap_deverr_init(d);
-    pcie_cap_slot_init(d, s->slot);
-    pcie_chassis_create(s->chassis);
-    rc = pcie_chassis_add_slot(s);
-    if (rc < 0) {
-        goto err_pcie_cap;
-    }
-    pcie_cap_ari_init(d);
-    rc = pcie_aer_init(d, XIO3130_AER_OFFSET);
-    if (rc < 0) {
-        goto err;
-    }
-
-    return 0;
-
-err:
-    pcie_chassis_del_slot(s);
-err_pcie_cap:
-    pcie_cap_exit(d);
-err_msi:
-    msi_uninit(d);
-err_bridge:
-    pci_bridge_exitfn(d);
-    return rc;
-}
-
-static void xio3130_downstream_exitfn(PCIDevice *d)
-{
-    PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
-    PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
-    PCIESlot *s = DO_UPCAST(PCIESlot, port, p);
-
-    pcie_aer_exit(d);
-    pcie_chassis_del_slot(s);
-    pcie_cap_exit(d);
-    msi_uninit(d);
-    pci_bridge_exitfn(d);
-}
-
-PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction,
-                                  const char *bus_name, pci_map_irq_fn map_irq,
-                                  uint8_t port, uint8_t chassis,
-                                  uint16_t slot)
-{
-    PCIDevice *d;
-    PCIBridge *br;
-    DeviceState *qdev;
-
-    d = pci_create_multifunction(bus, devfn, multifunction,
-                                 "xio3130-downstream");
-    if (!d) {
-        return NULL;
-    }
-    br = DO_UPCAST(PCIBridge, dev, d);
-
-    qdev = &br->dev.qdev;
-    pci_bridge_map_irq(br, bus_name, map_irq);
-    qdev_prop_set_uint8(qdev, "port", port);
-    qdev_prop_set_uint8(qdev, "chassis", chassis);
-    qdev_prop_set_uint16(qdev, "slot", slot);
-    qdev_init_nofail(qdev);
-
-    return DO_UPCAST(PCIESlot, port, DO_UPCAST(PCIEPort, br, br));
-}
-
-static const VMStateDescription vmstate_xio3130_downstream = {
-    .name = "xio3130-express-downstream-port",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .post_load = pcie_cap_slot_post_load,
-    .fields = (VMStateField[]) {
-        VMSTATE_PCIE_DEVICE(port.br.dev, PCIESlot),
-        VMSTATE_STRUCT(port.br.dev.exp.aer_log, PCIESlot, 0,
-                       vmstate_pcie_aer_log, PCIEAERLog),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property xio3130_downstream_properties[] = {
-    DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0),
-    DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0),
-    DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0),
-    DEFINE_PROP_UINT16("aer_log_max", PCIESlot,
-    port.br.dev.exp.aer_log.log_max,
-    PCIE_AER_LOG_MAX_DEFAULT),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void xio3130_downstream_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->is_express = 1;
-    k->is_bridge = 1;
-    k->config_write = xio3130_downstream_write_config;
-    k->init = xio3130_downstream_initfn;
-    k->exit = xio3130_downstream_exitfn;
-    k->vendor_id = PCI_VENDOR_ID_TI;
-    k->device_id = PCI_DEVICE_ID_TI_XIO3130D;
-    k->revision = XIO3130_REVISION;
-    dc->desc = "TI X3130 Downstream Port of PCI Express Switch";
-    dc->reset = xio3130_downstream_reset;
-    dc->vmsd = &vmstate_xio3130_downstream;
-    dc->props = xio3130_downstream_properties;
-}
-
-static const TypeInfo xio3130_downstream_info = {
-    .name          = "xio3130-downstream",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIESlot),
-    .class_init    = xio3130_downstream_class_init,
-};
-
-static void xio3130_downstream_register_types(void)
-{
-    type_register_static(&xio3130_downstream_info);
-}
-
-type_init(xio3130_downstream_register_types)
-
-/*
- * Local variables:
- *  c-indent-level: 4
- *  c-basic-offset: 4
- *  tab-width: 8
- *  indent-tab-mode: nil
- * End:
- */
diff --git a/hw/xio3130_upstream.c b/hw/xio3130_upstream.c
deleted file mode 100644 (file)
index cd5d97d..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * xio3130_upstream.c
- * TI X3130 pci express upstream port switch
- *
- * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
- *                    VA Linux Systems Japan K.K.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "hw/pci/pci_ids.h"
-#include "hw/pci/msi.h"
-#include "hw/pci/pcie.h"
-#include "hw/xio3130_upstream.h"
-
-#define PCI_DEVICE_ID_TI_XIO3130U       0x8232  /* upstream port */
-#define XIO3130_REVISION                0x2
-#define XIO3130_MSI_OFFSET              0x70
-#define XIO3130_MSI_SUPPORTED_FLAGS     PCI_MSI_FLAGS_64BIT
-#define XIO3130_MSI_NR_VECTOR           1
-#define XIO3130_SSVID_OFFSET            0x80
-#define XIO3130_SSVID_SVID              0
-#define XIO3130_SSVID_SSID              0
-#define XIO3130_EXP_OFFSET              0x90
-#define XIO3130_AER_OFFSET              0x100
-
-static void xio3130_upstream_write_config(PCIDevice *d, uint32_t address,
-                                          uint32_t val, int len)
-{
-    pci_bridge_write_config(d, address, val, len);
-    pcie_cap_flr_write_config(d, address, val, len);
-    pcie_aer_write_config(d, address, val, len);
-}
-
-static void xio3130_upstream_reset(DeviceState *qdev)
-{
-    PCIDevice *d = PCI_DEVICE(qdev);
-
-    pci_bridge_reset(qdev);
-    pcie_cap_deverr_reset(d);
-}
-
-static int xio3130_upstream_initfn(PCIDevice *d)
-{
-    PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
-    PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
-    int rc;
-
-    rc = pci_bridge_initfn(d, TYPE_PCIE_BUS);
-    if (rc < 0) {
-        return rc;
-    }
-
-    pcie_port_init_reg(d);
-
-    rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
-                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
-                  XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
-    if (rc < 0) {
-        goto err_bridge;
-    }
-    rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET,
-                               XIO3130_SSVID_SVID, XIO3130_SSVID_SSID);
-    if (rc < 0) {
-        goto err_bridge;
-    }
-    rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_UPSTREAM,
-                       p->port);
-    if (rc < 0) {
-        goto err_msi;
-    }
-    pcie_cap_flr_init(d);
-    pcie_cap_deverr_init(d);
-    rc = pcie_aer_init(d, XIO3130_AER_OFFSET);
-    if (rc < 0) {
-        goto err;
-    }
-
-    return 0;
-
-err:
-    pcie_cap_exit(d);
-err_msi:
-    msi_uninit(d);
-err_bridge:
-    pci_bridge_exitfn(d);
-    return rc;
-}
-
-static void xio3130_upstream_exitfn(PCIDevice *d)
-{
-    pcie_aer_exit(d);
-    pcie_cap_exit(d);
-    msi_uninit(d);
-    pci_bridge_exitfn(d);
-}
-
-PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction,
-                             const char *bus_name, pci_map_irq_fn map_irq,
-                             uint8_t port)
-{
-    PCIDevice *d;
-    PCIBridge *br;
-    DeviceState *qdev;
-
-    d = pci_create_multifunction(bus, devfn, multifunction, "x3130-upstream");
-    if (!d) {
-        return NULL;
-    }
-    br = DO_UPCAST(PCIBridge, dev, d);
-
-    qdev = &br->dev.qdev;
-    pci_bridge_map_irq(br, bus_name, map_irq);
-    qdev_prop_set_uint8(qdev, "port", port);
-    qdev_init_nofail(qdev);
-
-    return DO_UPCAST(PCIEPort, br, br);
-}
-
-static const VMStateDescription vmstate_xio3130_upstream = {
-    .name = "xio3130-express-upstream-port",
-    .version_id = 1,
-    .minimum_version_id = 1,
-    .minimum_version_id_old = 1,
-    .fields = (VMStateField[]) {
-        VMSTATE_PCIE_DEVICE(br.dev, PCIEPort),
-        VMSTATE_STRUCT(br.dev.exp.aer_log, PCIEPort, 0, vmstate_pcie_aer_log,
-                       PCIEAERLog),
-        VMSTATE_END_OF_LIST()
-    }
-};
-
-static Property xio3130_upstream_properties[] = {
-    DEFINE_PROP_UINT8("port", PCIEPort, port, 0),
-    DEFINE_PROP_UINT16("aer_log_max", PCIEPort, br.dev.exp.aer_log.log_max,
-    PCIE_AER_LOG_MAX_DEFAULT),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void xio3130_upstream_class_init(ObjectClass *klass, void *data)
-{
-    DeviceClass *dc = DEVICE_CLASS(klass);
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-
-    k->is_express = 1;
-    k->is_bridge = 1;
-    k->config_write = xio3130_upstream_write_config;
-    k->init = xio3130_upstream_initfn;
-    k->exit = xio3130_upstream_exitfn;
-    k->vendor_id = PCI_VENDOR_ID_TI;
-    k->device_id = PCI_DEVICE_ID_TI_XIO3130U;
-    k->revision = XIO3130_REVISION;
-    dc->desc = "TI X3130 Upstream Port of PCI Express Switch";
-    dc->reset = xio3130_upstream_reset;
-    dc->vmsd = &vmstate_xio3130_upstream;
-    dc->props = xio3130_upstream_properties;
-}
-
-static const TypeInfo xio3130_upstream_info = {
-    .name          = "x3130-upstream",
-    .parent        = TYPE_PCI_DEVICE,
-    .instance_size = sizeof(PCIEPort),
-    .class_init    = xio3130_upstream_class_init,
-};
-
-static void xio3130_upstream_register_types(void)
-{
-    type_register_static(&xio3130_upstream_info);
-}
-
-type_init(xio3130_upstream_register_types)
-
-
-/*
- * Local variables:
- *  c-indent-level: 4
- *  c-basic-offset: 4
- *  tab-width: 8
- *  indent-tab-mode: nil
- * End:
- */