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
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
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
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
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
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
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
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
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
-# 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/
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.
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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;
-}
+common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+# 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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
+
--- /dev/null
+/*
+**
+** 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;
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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, µ);
+}
+
+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)
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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()
+ }
+};
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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 )
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
+++ /dev/null
-/*
- * 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;
-}
+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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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(),
+ },
+};
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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,
+};
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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, ¶ms, 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, ¶ms, 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,
- ¶ms, 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(¶ms.bdaddr, bdaddr);
- params.link_type = ACL_LINK;
- params.encr_mode = 0x00; /* Encryption not required */
- bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, 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(¶ms.bdaddr, &host->bd_addr);
- params.link_type = ACL_LINK;
- params.encr_mode = 0x00; /* Encryption not required */
- bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, 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(¶ms.bdaddr, &link->host->bd_addr);
- memcpy(¶ms.dev_class, &link->host->class, sizeof(params.dev_class));
- params.link_type = ACL_LINK;
- bt_hci_event(hci, EVT_CONN_REQUEST, ¶ms, 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(¶ms.bdaddr, &link->slave->bd_addr);
- params.encr_mode = 0x00; /* Encryption not required */
- bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, 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,
- ¶ms, 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,
- ¶ms, 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,
- ¶ms, 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(¶ms.bdaddr, &slave->bd_addr);
- pstrcpy(params.name, sizeof(params.name), slave->lmp_name ?: "");
- bt_hci_event(hci, EVT_REMOTE_NAME_REQ_COMPLETE,
- ¶ms, 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,
- ¶ms, 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,
- ¶ms, 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,
- ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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,
- ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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);
-}
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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(¶ms->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, ¶ms, 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;
-}
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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;
-}
+common-obj-y += core.o l2cap.o sdp.o hci.o hid.o
+common-obj-y += hci-csr.o
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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, ¶ms, 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, ¶ms, 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,
+ ¶ms, 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(¶ms.bdaddr, bdaddr);
+ params.link_type = ACL_LINK;
+ params.encr_mode = 0x00; /* Encryption not required */
+ bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, 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(¶ms.bdaddr, &host->bd_addr);
+ params.link_type = ACL_LINK;
+ params.encr_mode = 0x00; /* Encryption not required */
+ bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, 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(¶ms.bdaddr, &link->host->bd_addr);
+ memcpy(¶ms.dev_class, &link->host->class, sizeof(params.dev_class));
+ params.link_type = ACL_LINK;
+ bt_hci_event(hci, EVT_CONN_REQUEST, ¶ms, 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(¶ms.bdaddr, &link->slave->bd_addr);
+ params.encr_mode = 0x00; /* Encryption not required */
+ bt_hci_event(hci, EVT_CONN_COMPLETE, ¶ms, 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,
+ ¶ms, 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,
+ ¶ms, 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,
+ ¶ms, 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(¶ms.bdaddr, &slave->bd_addr);
+ pstrcpy(params.name, sizeof(params.name), slave->lmp_name ?: "");
+ bt_hci_event(hci, EVT_REMOTE_NAME_REQ_COMPLETE,
+ ¶ms, 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,
+ ¶ms, 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,
+ ¶ms, 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,
+ ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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,
+ ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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(¶ms->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, ¶ms, 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;
+}
--- /dev/null
+/*
+ * 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);
+}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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;
-}
+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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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, ¶llel_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(¶llel_isa_info);
+}
+
+type_init(parallel_register_types)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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,
+};
--- /dev/null
+/*
+ * 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)
+++ /dev/null
-/*
- * 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)
+# 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
+
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#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);
+
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+#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);
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+#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)
--- /dev/null
+/*
+ * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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);
+}
+++ /dev/null
-/*
- * 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);
-}
+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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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)
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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(),
- },
-};
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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 */
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
-
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
-**
-** 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;
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+common-obj-$(CONFIG_MAX7310) += max7310.o
+common-obj-$(CONFIG_PL061) += pl061.o
+common-obj-$(CONFIG_PUV3) += puv3_gpio.o
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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);
- }
- }
-}
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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, µ);
-}
-
-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)
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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(),
- }
-};
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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);
+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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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(),
+ }
+};
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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)
+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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
+++ /dev/null
-/*
- * 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;
-}
-
+++ /dev/null
-/*
- * 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:
- */
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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)
+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
+
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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()
- }
-};
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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);
- }
- }
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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));
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-#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)
+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/
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+common-obj-y += macio.o
+common-obj-$(CONFIG_CUDA) += cuda.o
+common-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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));
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
+++ /dev/null
-/*
- * 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 */
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+#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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/**
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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,
+};
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
+++ /dev/null
-/*
- * 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);
-
+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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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, ¶llel_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(¶llel_isa_info);
-}
-
-type_init(parallel_register_types)
+++ /dev/null
-/*
- * 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)
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/
--- /dev/null
+common-obj-y += pci_bridge_dev.o
+common-obj-y += ioh3420.o xio3130_upstream.o xio3130_downstream.o
+common-obj-y += i82801b11.o
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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 )
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-#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);
-
-}
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-#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);
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/**
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-#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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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__ */
+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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+#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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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__ */
+++ /dev/null
-/*
- * 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, §);
- } 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;
-}
+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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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, §);
+ } 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;
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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);
-}
+common-obj-$(CONFIG_PL022) += pl022.o
+common-obj-$(CONFIG_SSI) += ssi.o
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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);
+}
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-#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)
+++ /dev/null
-/*
- * 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)
+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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
# 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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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);
-}
+common-obj-$(CONFIG_VIRTIO) += virtio-rng.o
+common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
+common-obj-$(CONFIG_VIRTIO) += virtio-bus.o
+
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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);
+}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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 */
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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;
- }
-}
+common-obj-y += watchdog.o
+common-obj-$(CONFIG_PCI) += wdt_i6300esb.o
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+# xen backend driver support
+common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+#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);
+}
+++ /dev/null
-/*
- * 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();
-}
+++ /dev/null
-/*
- * 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,
-};
+++ /dev/null
-#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);
-}
+++ /dev/null
-/*
- * 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,
-};
+++ /dev/null
-/*
- * 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,
-};
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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)
+++ /dev/null
-/*
- * 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:
- */
+++ /dev/null
-/*
- * 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:
- */