diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/audiodev.h | 2 | ||||
-rw-r--r-- | hw/ide/ahci.c | 16 | ||||
-rw-r--r-- | hw/lm4549.c | 336 | ||||
-rw-r--r-- | hw/lm4549.h | 43 | ||||
-rw-r--r-- | hw/pci-stub.c | 15 | ||||
-rw-r--r-- | hw/pci.c | 322 | ||||
-rw-r--r-- | hw/pci.h | 4 | ||||
-rw-r--r-- | hw/pl041.c | 636 | ||||
-rw-r--r-- | hw/pl041.h | 135 | ||||
-rw-r--r-- | hw/pl041.hx | 81 | ||||
-rw-r--r-- | hw/qxl.c | 66 | ||||
-rw-r--r-- | hw/qxl.h | 3 | ||||
-rw-r--r-- | hw/realview.c | 8 | ||||
-rw-r--r-- | hw/versatilepb.c | 8 | ||||
-rw-r--r-- | hw/vexpress.c | 7 | ||||
-rw-r--r-- | hw/virtio-balloon.c | 78 |
16 files changed, 1435 insertions, 325 deletions
diff --git a/hw/audiodev.h b/hw/audiodev.h index 8e930b2..d60c349 100644 --- a/hw/audiodev.h +++ b/hw/audiodev.h @@ -11,7 +11,7 @@ int Adlib_init(qemu_irq *pic); int GUS_init(qemu_irq *pic); /* ac97.c */ -int ac97_init(PCIBus *buf); +int ac97_init(PCIBus *bus); /* cs4231a.c */ int cs4231a_init(qemu_irq *pic); diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 1c7e3a0..0af201d 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -327,7 +327,7 @@ static void ahci_mem_write(void *opaque, target_phys_addr_t addr, } if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) { - DPRINTF(-1, "(addr 0x%08X), val 0x%08X\n", (unsigned) addr, val); + DPRINTF(-1, "(addr 0x%08X), val 0x%08"PRIX64"\n", (unsigned) addr, val); switch (addr) { case HOST_CAP: /* R/WO, RO */ @@ -777,7 +777,8 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, ncq_tfs->sector_count = ((uint16_t)ncq_fis->sector_count_high << 8) | ncq_fis->sector_count_low; - DPRINTF(port, "NCQ transfer LBA from %ld to %ld, drive max %ld\n", + DPRINTF(port, "NCQ transfer LBA from %"PRId64" to %"PRId64", " + "drive max %"PRId64"\n", ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 2, s->dev[port].port.ifs[0].nb_sectors - 1); @@ -786,10 +787,12 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, switch(ncq_fis->command) { case READ_FPDMA_QUEUED: - DPRINTF(port, "NCQ reading %d sectors from LBA %ld, tag %d\n", + DPRINTF(port, "NCQ reading %d sectors from LBA %"PRId64", " + "tag %d\n", ncq_tfs->sector_count-1, ncq_tfs->lba, ncq_tfs->tag); - DPRINTF(port, "tag %d aio read %ld\n", ncq_tfs->tag, ncq_tfs->lba); + DPRINTF(port, "tag %d aio read %"PRId64"\n", + ncq_tfs->tag, ncq_tfs->lba); bdrv_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct, (ncq_tfs->sector_count-1) * BDRV_SECTOR_SIZE, @@ -799,10 +802,11 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, ncq_cb, ncq_tfs); break; case WRITE_FPDMA_QUEUED: - DPRINTF(port, "NCQ writing %d sectors to LBA %ld, tag %d\n", + DPRINTF(port, "NCQ writing %d sectors to LBA %"PRId64", tag %d\n", ncq_tfs->sector_count-1, ncq_tfs->lba, ncq_tfs->tag); - DPRINTF(port, "tag %d aio write %ld\n", ncq_tfs->tag, ncq_tfs->lba); + DPRINTF(port, "tag %d aio write %"PRId64"\n", + ncq_tfs->tag, ncq_tfs->lba); bdrv_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct, (ncq_tfs->sector_count-1) * BDRV_SECTOR_SIZE, diff --git a/hw/lm4549.c b/hw/lm4549.c new file mode 100644 index 0000000..4d5b831 --- /dev/null +++ b/hw/lm4549.c @@ -0,0 +1,336 @@ +/* + * LM4549 Audio Codec Interface + * + * Copyright (c) 2011 + * Written by Mathieu Sonet - www.elasticsheep.com + * + * This code is licenced under the GPL. + * + * ***************************************************************** + * + * This driver emulates the LM4549 codec. + * + * It supports only one playback voice and no record voice. + */ + +#include "hw.h" +#include "audio/audio.h" +#include "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, target_phys_addr_t 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, + target_phys_addr_t 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) { + DPRINTF("write_sample Buffer full\n"); + return 0; + } + + /* Store 16-bit samples in the buffer */ + s->buffer[s->buffer_level++] = (left >> 4); + s->buffer[s->buffer_level++] = (right >> 4); + + if (s->buffer_level == LM4549_BUFFER_SIZE) { + /* Trigger the transfer of the buffer to the audio host */ + lm4549_audio_transfer(s); + } + + return 1; +} + +static int lm4549_post_load(void *opaque, int version_id) +{ + lm4549_state *s = (lm4549_state *)opaque; + uint16_t *regfile = s->regfile; + + /* Re-open a voice with the current sample rate */ + uint32_t freq = regfile[LM4549_PCM_Front_DAC_Rate]; + + DPRINTF("post_load freq = %i\n", freq); + DPRINTF("post_load voice_is_active = %i\n", s->voice_is_active); + + struct audsettings as; + as.freq = freq; + as.nchannels = 2; + as.fmt = AUD_FMT_S16; + as.endianness = 0; + + s->voice = AUD_open_out( + &s->card, + s->voice, + "lm4549.out", + s, + lm4549_audio_out_callback, + &as + ); + + /* Request data */ + if (s->voice_is_active == 1) { + lm4549_audio_out_callback(s, AUD_get_buffer_size_out(s->voice)); + } + + return 0; +} + +void lm4549_init(lm4549_state *s, lm4549_callback data_req_cb, void* opaque) +{ + struct audsettings as; + + /* Store the callback and opaque pointer */ + s->data_req_cb = data_req_cb; + s->opaque = opaque; + + /* Init the registers */ + lm4549_reset(s); + + /* Register an audio card */ + AUD_register_card("lm4549", &s->card); + + /* Open a default voice */ + as.freq = 48000; + as.nchannels = 2; + as.fmt = AUD_FMT_S16; + as.endianness = 0; + + s->voice = AUD_open_out( + &s->card, + s->voice, + "lm4549.out", + s, + lm4549_audio_out_callback, + &as + ); + + AUD_set_volume_out(s->voice, 0, 255, 255); + + s->voice_is_active = 0; + + /* Reset the input buffer */ + memset(s->buffer, 0x00, sizeof(s->buffer)); + s->buffer_level = 0; + +#if defined(LM4549_DUMP_DAC_INPUT) + fp_dac_input = fopen("lm4549_dac_input.pcm", "wb"); + if (!fp_dac_input) { + hw_error("Unable to open lm4549_dac_input.pcm for writing\n"); + } +#endif +} + +const VMStateDescription vmstate_lm4549_state = { + .name = "lm4549_state", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = &lm4549_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(voice_is_active, lm4549_state), + VMSTATE_UINT16_ARRAY(regfile, lm4549_state, 128), + VMSTATE_UINT16_ARRAY(buffer, lm4549_state, LM4549_BUFFER_SIZE), + VMSTATE_UINT32(buffer_level, lm4549_state), + VMSTATE_END_OF_LIST() + } +}; diff --git a/hw/lm4549.h b/hw/lm4549.h new file mode 100644 index 0000000..70d0ac1 --- /dev/null +++ b/hw/lm4549.h @@ -0,0 +1,43 @@ +/* + * LM4549 Audio Codec Interface + * + * Copyright (c) 2011 + * Written by Mathieu Sonet - www.elasticsheep.com + * + * This code is licenced under the GPL. + * + * ***************************************************************** + */ + +#ifndef HW_LM4549_H +#define HW_LM4549_H + +#include "audio/audio.h" + +typedef void (*lm4549_callback)(void *opaque); + +#define LM4549_BUFFER_SIZE (512 * 2) /* 512 16-bit stereo samples */ + + +typedef struct { + QEMUSoundCard card; + SWVoiceOut *voice; + uint32_t voice_is_active; + + uint16_t regfile[128]; + lm4549_callback data_req_cb; + void *opaque; + + uint16_t buffer[LM4549_BUFFER_SIZE]; + uint32_t buffer_level; +} lm4549_state; + +extern const VMStateDescription vmstate_lm4549_state; + + +void lm4549_init(lm4549_state *s, lm4549_callback data_req, void *opaque); +uint32_t lm4549_read(lm4549_state *s, target_phys_addr_t offset); +void lm4549_write(lm4549_state *s, target_phys_addr_t offset, uint32_t value); +uint32_t lm4549_write_samples(lm4549_state *s, uint32_t left, uint32_t right); + +#endif /* #ifndef HW_LM4549_H */ diff --git a/hw/pci-stub.c b/hw/pci-stub.c index 1fb105d..636171c 100644 --- a/hw/pci-stub.c +++ b/hw/pci-stub.c @@ -21,20 +21,17 @@ #include "sysemu.h" #include "monitor.h" #include "pci.h" +#include "qmp-commands.h" -static void pci_error_message(Monitor *mon) +PciInfoList *qmp_query_pci(Error **errp) { - monitor_printf(mon, "PCI devices not supported\n"); + error_set(errp, QERR_UNSUPPORTED); + return NULL; } -void do_pci_info(Monitor *mon, QObject **ret_data) -{ - pci_error_message(mon); -} - -void do_pci_info_print(Monitor *mon, const QObject *data) +static void pci_error_message(Monitor *mon) { - pci_error_message(mon); + monitor_printf(mon, "PCI devices not supported\n"); } int do_pcie_aer_inejct_error(Monitor *mon, @@ -29,8 +29,8 @@ #include "net.h" #include "sysemu.h" #include "loader.h" -#include "qemu-objects.h" #include "range.h" +#include "qmp-commands.h" //#define DEBUG_PCI #ifdef DEBUG_PCI @@ -1164,276 +1164,194 @@ void pci_for_each_device(PCIBus *bus, int bus_num, } } -static void pci_device_print(Monitor *mon, QDict *device) +static const pci_class_desc *get_class_desc(int class) { - QDict *qdict; - QListEntry *entry; - uint64_t addr, size; - - monitor_printf(mon, " Bus %2" PRId64 ", ", qdict_get_int(device, "bus")); - monitor_printf(mon, "device %3" PRId64 ", function %" PRId64 ":\n", - qdict_get_int(device, "slot"), - qdict_get_int(device, "function")); - monitor_printf(mon, " "); - - qdict = qdict_get_qdict(device, "class_info"); - if (qdict_haskey(qdict, "desc")) { - monitor_printf(mon, "%s", qdict_get_str(qdict, "desc")); - } else { - monitor_printf(mon, "Class %04" PRId64, qdict_get_int(qdict, "class")); - } - - qdict = qdict_get_qdict(device, "id"); - monitor_printf(mon, ": PCI device %04" PRIx64 ":%04" PRIx64 "\n", - qdict_get_int(qdict, "device"), - qdict_get_int(qdict, "vendor")); + const pci_class_desc *desc; - if (qdict_haskey(device, "irq")) { - monitor_printf(mon, " IRQ %" PRId64 ".\n", - qdict_get_int(device, "irq")); + desc = pci_class_descriptions; + while (desc->desc && class != desc->class) { + desc++; } - if (qdict_haskey(device, "pci_bridge")) { - QDict *info; - - qdict = qdict_get_qdict(device, "pci_bridge"); - - info = qdict_get_qdict(qdict, "bus"); - monitor_printf(mon, " BUS %" PRId64 ".\n", - qdict_get_int(info, "number")); - monitor_printf(mon, " secondary bus %" PRId64 ".\n", - qdict_get_int(info, "secondary")); - monitor_printf(mon, " subordinate bus %" PRId64 ".\n", - qdict_get_int(info, "subordinate")); + return desc; +} - info = qdict_get_qdict(qdict, "io_range"); - monitor_printf(mon, " IO range [0x%04"PRIx64", 0x%04"PRIx64"]\n", - qdict_get_int(info, "base"), - qdict_get_int(info, "limit")); +static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num); - info = qdict_get_qdict(qdict, "memory_range"); - monitor_printf(mon, - " memory range [0x%08"PRIx64", 0x%08"PRIx64"]\n", - qdict_get_int(info, "base"), - qdict_get_int(info, "limit")); +static PciMemoryRegionList *qmp_query_pci_regions(const PCIDevice *dev) +{ + PciMemoryRegionList *head = NULL, *cur_item = NULL; + int i; - info = qdict_get_qdict(qdict, "prefetchable_range"); - monitor_printf(mon, " prefetchable memory range " - "[0x%08"PRIx64", 0x%08"PRIx64"]\n", - qdict_get_int(info, "base"), - qdict_get_int(info, "limit")); - } + for (i = 0; i < PCI_NUM_REGIONS; i++) { + const PCIIORegion *r = &dev->io_regions[i]; + PciMemoryRegionList *region; - QLIST_FOREACH_ENTRY(qdict_get_qlist(device, "regions"), entry) { - qdict = qobject_to_qdict(qlist_entry_obj(entry)); - monitor_printf(mon, " BAR%d: ", (int) qdict_get_int(qdict, "bar")); + if (!r->size) { + continue; + } - addr = qdict_get_int(qdict, "address"); - size = qdict_get_int(qdict, "size"); + region = g_malloc0(sizeof(*region)); + region->value = g_malloc0(sizeof(*region->value)); - if (!strcmp(qdict_get_str(qdict, "type"), "io")) { - monitor_printf(mon, "I/O at 0x%04"FMT_PCIBUS - " [0x%04"FMT_PCIBUS"].\n", - addr, addr + size - 1); + if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { + region->value->type = g_strdup("io"); } else { - monitor_printf(mon, "%d bit%s memory at 0x%08"FMT_PCIBUS - " [0x%08"FMT_PCIBUS"].\n", - qdict_get_bool(qdict, "mem_type_64") ? 64 : 32, - qdict_get_bool(qdict, "prefetch") ? - " prefetchable" : "", addr, addr + size - 1); + region->value->type = g_strdup("memory"); + region->value->has_prefetch = true; + region->value->prefetch = !!(r->type & PCI_BASE_ADDRESS_MEM_PREFETCH); + region->value->has_mem_type_64 = true; + region->value->mem_type_64 = !!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64); } - } - monitor_printf(mon, " id \"%s\"\n", qdict_get_str(device, "qdev_id")); + region->value->bar = i; + region->value->address = r->addr; + region->value->size = r->size; - if (qdict_haskey(device, "pci_bridge")) { - qdict = qdict_get_qdict(device, "pci_bridge"); - if (qdict_haskey(qdict, "devices")) { - QListEntry *dev; - QLIST_FOREACH_ENTRY(qdict_get_qlist(qdict, "devices"), dev) { - pci_device_print(mon, qobject_to_qdict(qlist_entry_obj(dev))); - } + /* XXX: waiting for the qapi to support GSList */ + if (!cur_item) { + head = cur_item = region; + } else { + cur_item->next = region; + cur_item = region; } } -} - -void do_pci_info_print(Monitor *mon, const QObject *data) -{ - QListEntry *bus, *dev; - QLIST_FOREACH_ENTRY(qobject_to_qlist(data), bus) { - QDict *qdict = qobject_to_qdict(qlist_entry_obj(bus)); - QLIST_FOREACH_ENTRY(qdict_get_qlist(qdict, "devices"), dev) { - pci_device_print(mon, qobject_to_qdict(qlist_entry_obj(dev))); - } - } + return head; } -static QObject *pci_get_dev_class(const PCIDevice *dev) +static PciBridgeInfo *qmp_query_pci_bridge(PCIDevice *dev, PCIBus *bus, + int bus_num) { - int class; - const pci_class_desc *desc; + PciBridgeInfo *info; - class = pci_get_word(dev->config + PCI_CLASS_DEVICE); - desc = pci_class_descriptions; - while (desc->desc && class != desc->class) - desc++; + info = g_malloc0(sizeof(*info)); - if (desc->desc) { - return qobject_from_jsonf("{ 'desc': %s, 'class': %d }", - desc->desc, class); - } else { - return qobject_from_jsonf("{ 'class': %d }", class); - } -} + info->bus.number = dev->config[PCI_PRIMARY_BUS]; + info->bus.secondary = dev->config[PCI_SECONDARY_BUS]; + info->bus.subordinate = dev->config[PCI_SUBORDINATE_BUS]; -static QObject *pci_get_dev_id(const PCIDevice *dev) -{ - return qobject_from_jsonf("{ 'device': %d, 'vendor': %d }", - pci_get_word(dev->config + PCI_VENDOR_ID), - pci_get_word(dev->config + PCI_DEVICE_ID)); -} + info->bus.io_range = g_malloc0(sizeof(*info->bus.io_range)); + info->bus.io_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO); + info->bus.io_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO); -static QObject *pci_get_regions_list(const PCIDevice *dev) -{ - int i; - QList *regions_list; - - regions_list = qlist_new(); - - for (i = 0; i < PCI_NUM_REGIONS; i++) { - QObject *obj; - const PCIIORegion *r = &dev->io_regions[i]; + info->bus.memory_range = g_malloc0(sizeof(*info->bus.memory_range)); + info->bus.memory_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); + info->bus.memory_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); - if (!r->size) { - continue; - } + info->bus.prefetchable_range = g_malloc0(sizeof(*info->bus.prefetchable_range)); + info->bus.prefetchable_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + info->bus.prefetchable_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { - obj = qobject_from_jsonf("{ 'bar': %d, 'type': 'io', " - "'address': %" PRId64 ", " - "'size': %" PRId64 " }", - i, r->addr, r->size); - } else { - int mem_type_64 = r->type & PCI_BASE_ADDRESS_MEM_TYPE_64; - - obj = qobject_from_jsonf("{ 'bar': %d, 'type': 'memory', " - "'mem_type_64': %i, 'prefetch': %i, " - "'address': %" PRId64 ", " - "'size': %" PRId64 " }", - i, mem_type_64, - r->type & PCI_BASE_ADDRESS_MEM_PREFETCH, - r->addr, r->size); + if (dev->config[PCI_SECONDARY_BUS] != 0) { + PCIBus *child_bus = pci_find_bus(bus, dev->config[PCI_SECONDARY_BUS]); + if (child_bus) { + info->has_devices = true; + info->devices = qmp_query_pci_devices(child_bus, dev->config[PCI_SECONDARY_BUS]); } - - qlist_append_obj(regions_list, obj); } - return QOBJECT(regions_list); + return info; } -static QObject *pci_get_devices_list(PCIBus *bus, int bus_num); - -static QObject *pci_get_dev_dict(PCIDevice *dev, PCIBus *bus, int bus_num) +static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus, + int bus_num) { + const pci_class_desc *desc; + PciDeviceInfo *info; uint8_t type; - QObject *obj; + int class; + + info = g_malloc0(sizeof(*info)); + info->bus = bus_num; + info->slot = PCI_SLOT(dev->devfn); + info->function = PCI_FUNC(dev->devfn); - obj = qobject_from_jsonf("{ 'bus': %d, 'slot': %d, 'function': %d," "'class_info': %p, 'id': %p, 'regions': %p," - " 'qdev_id': %s }", - bus_num, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), - pci_get_dev_class(dev), pci_get_dev_id(dev), - pci_get_regions_list(dev), - dev->qdev.id ? dev->qdev.id : ""); + class = pci_get_word(dev->config + PCI_CLASS_DEVICE); + info->class_info.class = class; + desc = get_class_desc(class); + if (desc->desc) { + info->class_info.has_desc = true; + info->class_info.desc = g_strdup(desc->desc); + } + + info->id.vendor = pci_get_word(dev->config + PCI_VENDOR_ID); + info->id.device = pci_get_word(dev->config + PCI_DEVICE_ID); + info->regions = qmp_query_pci_regions(dev); + info->qdev_id = g_strdup(dev->qdev.id ? dev->qdev.id : ""); if (dev->config[PCI_INTERRUPT_PIN] != 0) { - QDict *qdict = qobject_to_qdict(obj); - qdict_put(qdict, "irq", qint_from_int(dev->config[PCI_INTERRUPT_LINE])); + info->has_irq = true; + info->irq = dev->config[PCI_INTERRUPT_LINE]; } type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; if (type == PCI_HEADER_TYPE_BRIDGE) { - QDict *qdict; - QObject *pci_bridge; - - pci_bridge = qobject_from_jsonf("{ 'bus': " - "{ 'number': %d, 'secondary': %d, 'subordinate': %d }, " - "'io_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "}, " - "'memory_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "}, " - "'prefetchable_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "} }", - dev->config[PCI_PRIMARY_BUS], dev->config[PCI_SECONDARY_BUS], - dev->config[PCI_SUBORDINATE_BUS], - pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO), - pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO), - pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY), - pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY), - pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_PREFETCH), - pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_PREFETCH)); - - if (dev->config[PCI_SECONDARY_BUS] != 0) { - PCIBus *child_bus = pci_find_bus(bus, dev->config[PCI_SECONDARY_BUS]); - - if (child_bus) { - qdict = qobject_to_qdict(pci_bridge); - qdict_put_obj(qdict, "devices", - pci_get_devices_list(child_bus, - dev->config[PCI_SECONDARY_BUS])); - } - } - qdict = qobject_to_qdict(obj); - qdict_put_obj(qdict, "pci_bridge", pci_bridge); + info->has_pci_bridge = true; + info->pci_bridge = qmp_query_pci_bridge(dev, bus, bus_num); } - return obj; + return info; } -static QObject *pci_get_devices_list(PCIBus *bus, int bus_num) +static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num) { - int devfn; + PciDeviceInfoList *info, *head = NULL, *cur_item = NULL; PCIDevice *dev; - QList *dev_list; - - dev_list = qlist_new(); + int devfn; for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { dev = bus->devices[devfn]; if (dev) { - qlist_append_obj(dev_list, pci_get_dev_dict(dev, bus, bus_num)); + info = g_malloc0(sizeof(*info)); + info->value = qmp_query_pci_device(dev, bus, bus_num); + + /* XXX: waiting for the qapi to support GSList */ + if (!cur_item) { + head = cur_item = info; + } else { + cur_item->next = info; + cur_item = info; + } } } - return QOBJECT(dev_list); + return head; } -static QObject *pci_get_bus_dict(PCIBus *bus, int bus_num) +static PciInfo *qmp_query_pci_bus(PCIBus *bus, int bus_num) { + PciInfo *info = NULL; + bus = pci_find_bus(bus, bus_num); if (bus) { - return qobject_from_jsonf("{ 'bus': %d, 'devices': %p }", - bus_num, pci_get_devices_list(bus, bus_num)); + info = g_malloc0(sizeof(*info)); + info->bus = bus_num; + info->devices = qmp_query_pci_devices(bus, bus_num); } - return NULL; + return info; } -void do_pci_info(Monitor *mon, QObject **ret_data) +PciInfoList *qmp_query_pci(Error **errp) { - QList *bus_list; + PciInfoList *info, *head = NULL, *cur_item = NULL; struct PCIHostBus *host; - bus_list = qlist_new(); - QLIST_FOREACH(host, &host_buses, next) { - QObject *obj = pci_get_bus_dict(host->bus, 0); - if (obj) { - qlist_append_obj(bus_list, obj); + info = g_malloc0(sizeof(*info)); + info->value = qmp_query_pci_bus(host->bus, 0); + + /* XXX: waiting for the qapi to support GSList */ + if (!cur_item) { + head = cur_item = info; + } else { + cur_item->next = info; + cur_item = info; } } - *ret_data = QOBJECT(bus_list); + return head; } static const char * const pci_nic_models[] = { @@ -2,7 +2,6 @@ #define QEMU_PCI_H #include "qemu-common.h" -#include "qobject.h" #include "qdev.h" #include "memory.h" @@ -271,9 +270,6 @@ int pci_parse_devaddr(const char *addr, int *domp, int *busp, int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp, unsigned *slotp); -void do_pci_info_print(Monitor *mon, const QObject *data); -void do_pci_info(Monitor *mon, QObject **ret_data); - void pci_device_deassert_intx(PCIDevice *dev); static inline void diff --git a/hw/pl041.c b/hw/pl041.c new file mode 100644 index 0000000..efd52ac --- /dev/null +++ b/hw/pl041.c @@ -0,0 +1,636 @@ +/* + * Arm PrimeCell PL041 Advanced Audio Codec Interface + * + * Copyright (c) 2011 + * Written by Mathieu Sonet - www.elasticsheep.com + * + * This code is licenced 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 "sysbus.h" + +#include "pl041.h" +#include "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(target_phys_addr_t 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, target_phys_addr_t 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, target_phys_addr_t 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 */ + fprintf(stderr, "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_region(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 SysBusDeviceInfo pl041_device_info = { + .init = pl041_init, + .qdev.name = "pl041", + .qdev.size = sizeof(pl041_state), + .qdev.vmsd = &vmstate_pl041, + .qdev.reset = pl041_device_reset, + .qdev.no_user = 1, + .qdev.props = (Property[]) { + /* 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_register_device(void) +{ + sysbus_register_withprop(&pl041_device_info); +} + +device_init(pl041_register_device) diff --git a/hw/pl041.h b/hw/pl041.h new file mode 100644 index 0000000..1f22432 --- /dev/null +++ b/hw/pl041.h @@ -0,0 +1,135 @@ +/* + * Arm PrimeCell PL041 Advanced Audio Codec Interface + * + * Copyright (c) 2011 + * Written by Mathieu Sonet - www.elasticsheep.com + * + * This code is licenced under the GPL. + * + * ***************************************************************** + */ + +#ifndef HW_PL041_H +#define HW_PL041_H + +/* Register file */ +#define REGISTER(name, offset) uint32_t name; +typedef struct { + #include "pl041.hx" +} pl041_regfile; +#undef REGISTER + +/* Register addresses */ +#define REGISTER(name, offset) PL041_##name = offset, +enum { + #include "pl041.hx" + + PL041_periphid0 = 0xFE0, + PL041_periphid1 = 0xFE4, + PL041_periphid2 = 0xFE8, + PL041_periphid3 = 0xFEC, + PL041_pcellid0 = 0xFF0, + PL041_pcellid1 = 0xFF4, + PL041_pcellid2 = 0xFF8, + PL041_pcellid3 = 0xFFC, +}; +#undef REGISTER + +/* Register bits */ + +/* IEx */ +#define TXCIE (1 << 0) +#define RXTIE (1 << 1) +#define TXIE (1 << 2) +#define RXIE (1 << 3) +#define RXOIE (1 << 4) +#define TXUIE (1 << 5) +#define RXTOIE (1 << 6) + +/* TXCRx */ +#define TXEN (1 << 0) +#define TXSLOT1 (1 << 1) +#define TXSLOT2 (1 << 2) +#define TXSLOT3 (1 << 3) +#define TXSLOT4 (1 << 4) +#define TXCOMPACT (1 << 15) +#define TXFEN (1 << 16) + +#define TXSLOT_MASK_BIT (1) +#define TXSLOT_MASK (0xFFF << TXSLOT_MASK_BIT) + +#define TSIZE_MASK_BIT (13) +#define TSIZE_MASK (0x3 << TSIZE_MASK_BIT) + +#define TSIZE_16BITS (0x0 << TSIZE_MASK_BIT) +#define TSIZE_18BITS (0x1 << TSIZE_MASK_BIT) +#define TSIZE_20BITS (0x2 << TSIZE_MASK_BIT) +#define TSIZE_12BITS (0x3 << TSIZE_MASK_BIT) + +/* SRx */ +#define RXFE (1 << 0) +#define TXFE (1 << 1) +#define RXHF (1 << 2) +#define TXHE (1 << 3) +#define RXFF (1 << 4) +#define TXFF (1 << 5) +#define RXBUSY (1 << 6) +#define TXBUSY (1 << 7) +#define RXOVERRUN (1 << 8) +#define TXUNDERRUN (1 << 9) +#define RXTIMEOUT (1 << 10) +#define RXTOFE (1 << 11) + +/* ISRx */ +#define TXCINTR (1 << 0) +#define RXTOINTR (1 << 1) +#define TXINTR (1 << 2) +#define RXINTR (1 << 3) +#define ORINTR (1 << 4) +#define URINTR (1 << 5) +#define RXTOFEINTR (1 << 6) + +/* SLFR */ +#define SL1RXBUSY (1 << 0) +#define SL1TXBUSY (1 << 1) +#define SL2RXBUSY (1 << 2) +#define SL2TXBUSY (1 << 3) +#define SL12RXBUSY (1 << 4) +#define SL12TXBUSY (1 << 5) +#define SL1RXVALID (1 << 6) +#define SL1TXEMPTY (1 << 7) +#define SL2RXVALID (1 << 8) +#define SL2TXEMPTY (1 << 9) +#define SL12RXVALID (1 << 10) +#define SL12TXEMPTY (1 << 11) +#define RAWGPIOINT (1 << 12) +#define RWIS (1 << 13) + +/* MAINCR */ +#define AACIFE (1 << 0) +#define LOOPBACK (1 << 1) +#define LOWPOWER (1 << 2) +#define SL1RXEN (1 << 3) +#define SL1TXEN (1 << 4) +#define SL2RXEN (1 << 5) +#define SL2TXEN (1 << 6) +#define SL12RXEN (1 << 7) +#define SL12TXEN (1 << 8) +#define DMAENABLE (1 << 9) + +/* INTCLR */ +#define WISC (1 << 0) +#define RXOEC1 (1 << 1) +#define RXOEC2 (1 << 2) +#define RXOEC3 (1 << 3) +#define RXOEC4 (1 << 4) +#define TXUEC1 (1 << 5) +#define TXUEC2 (1 << 6) +#define TXUEC3 (1 << 7) +#define TXUEC4 (1 << 8) +#define RXTOFEC1 (1 << 9) +#define RXTOFEC2 (1 << 10) +#define RXTOFEC3 (1 << 11) +#define RXTOFEC4 (1 << 12) + +#endif /* #ifndef HW_PL041_H */ diff --git a/hw/pl041.hx b/hw/pl041.hx new file mode 100644 index 0000000..e972996 --- /dev/null +++ b/hw/pl041.hx @@ -0,0 +1,81 @@ +/* + * Arm PrimeCell PL041 Advanced Audio Codec Interface + * + * Copyright (c) 2011 + * Written by Mathieu Sonet - www.elasticsheep.com + * + * This code is licenced 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 ) @@ -18,8 +18,6 @@ * along with this program; if not, see <http://www.gnu.org/licenses/>. */ -#include <pthread.h> - #include "qemu-common.h" #include "qemu-timer.h" #include "qemu-queue.h" @@ -238,6 +236,9 @@ void qxl_spice_reset_image_cache(PCIQXLDevice *qxl) void qxl_spice_reset_cursor(PCIQXLDevice *qxl) { qxl->ssd.worker->reset_cursor(qxl->ssd.worker); + qemu_mutex_lock(&qxl->track_lock); + qxl->guest_cursor = 0; + qemu_mutex_unlock(&qxl->track_lock); } @@ -330,6 +331,7 @@ static void init_qxl_ram(PCIQXLDevice *d) d->ram->magic = cpu_to_le32(QXL_RAM_MAGIC); d->ram->int_pending = cpu_to_le32(0); d->ram->int_mask = cpu_to_le32(0); + d->ram->update_surface = 0; SPICE_RING_INIT(&d->ram->cmd_ring); SPICE_RING_INIT(&d->ram->cursor_ring); SPICE_RING_INIT(&d->ram->release_ring); @@ -402,7 +404,9 @@ static void qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext) { QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); if (cmd->type == QXL_CURSOR_SET) { + qemu_mutex_lock(&qxl->track_lock); qxl->guest_cursor = ext->cmd.data; + qemu_mutex_unlock(&qxl->track_lock); } break; } @@ -1067,6 +1071,7 @@ static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async) d->mode = QXL_MODE_UNDEFINED; qemu_spice_destroy_primary_surface(&d->ssd, 0, async); + qxl_spice_reset_cursor(d); return 1; } @@ -1215,10 +1220,6 @@ async_common: if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) { break; } - pthread_yield(); - if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) { - break; - } d->oom_running = 1; qxl_spice_oom(d); d->oom_running = 0; @@ -1372,7 +1373,7 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events) if ((old_pending & le_events) == le_events) { return; } - if (pthread_self() == d->main) { + if (qemu_thread_is_self(&d->main)) { qxl_update_irq(d); } else { if (write(d->pipe[1], d, 1) != 1) { @@ -1391,7 +1392,7 @@ static void init_pipe_signaling(PCIQXLDevice *d) fcntl(d->pipe[1], F_SETFL, O_NONBLOCK); fcntl(d->pipe[0], F_SETOWN, getpid()); - d->main = pthread_self(); + qemu_thread_get_self(&d->main); qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d); } @@ -1710,10 +1711,12 @@ static int qxl_post_load(void *opaque, int version) cmds[out].group_id = MEMSLOT_GROUP_GUEST; out++; } - cmds[out].cmd.data = d->guest_cursor; - cmds[out].cmd.type = QXL_CMD_CURSOR; - cmds[out].group_id = MEMSLOT_GROUP_GUEST; - out++; + if (d->guest_cursor) { + cmds[out].cmd.data = d->guest_cursor; + cmds[out].cmd.type = QXL_CMD_CURSOR; + cmds[out].group_id = MEMSLOT_GROUP_GUEST; + out++; + } qxl_spice_loadvm_commands(d, cmds, out); g_free(cmds); @@ -1787,6 +1790,19 @@ static VMStateDescription qxl_vmstate = { }, }; +static Property qxl_properties[] = { + DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, + 64 * 1024 * 1024), + DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, + 64 * 1024 * 1024), + DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, + QXL_DEFAULT_REVISION), + DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0), + DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0), + DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static PCIDeviceInfo qxl_info_primary = { .qdev.name = "qxl-vga", .qdev.desc = "Spice QXL GPU (primary, vga compatible)", @@ -1799,18 +1815,7 @@ static PCIDeviceInfo qxl_info_primary = { .vendor_id = REDHAT_PCI_VENDOR_ID, .device_id = QXL_DEVICE_ID_STABLE, .class_id = PCI_CLASS_DISPLAY_VGA, - .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, - 64 * 1024 * 1024), - DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, - 64 * 1024 * 1024), - DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, - QXL_DEFAULT_REVISION), - DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0), - DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0), - DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0), - DEFINE_PROP_END_OF_LIST(), - } + .qdev.props = qxl_properties, }; static PCIDeviceInfo qxl_info_secondary = { @@ -1823,18 +1828,7 @@ static PCIDeviceInfo qxl_info_secondary = { .vendor_id = REDHAT_PCI_VENDOR_ID, .device_id = QXL_DEVICE_ID_STABLE, .class_id = PCI_CLASS_DISPLAY_OTHER, - .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, - 64 * 1024 * 1024), - DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, - 64 * 1024 * 1024), - DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, - QXL_DEFAULT_REVISION), - DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0), - DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0), - DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0), - DEFINE_PROP_END_OF_LIST(), - } + .qdev.props = qxl_properties, }; static void qxl_register(void) @@ -4,6 +4,7 @@ #include "hw.h" #include "pci.h" #include "vga_int.h" +#include "qemu-thread.h" #include "ui/qemu-spice.h" #include "ui/spice-display.h" @@ -63,7 +64,7 @@ typedef struct PCIQXLDevice { QemuMutex track_lock; /* thread signaling */ - pthread_t main; + QemuThread main; int pipe[2]; /* ram pci bar */ diff --git a/hw/realview.c b/hw/realview.c index 14281b0..9a8e63c 100644 --- a/hw/realview.c +++ b/hw/realview.c @@ -125,7 +125,7 @@ static void realview_init(ram_addr_t ram_size, MemoryRegion *ram_hi = g_new(MemoryRegion, 1); MemoryRegion *ram_alias = g_new(MemoryRegion, 1); MemoryRegion *ram_hack = g_new(MemoryRegion, 1); - DeviceState *dev, *sysctl, *gpio2; + DeviceState *dev, *sysctl, *gpio2, *pl041; SysBusDevice *busdev; qemu_irq *irqp; qemu_irq pic[64]; @@ -232,6 +232,12 @@ static void realview_init(ram_addr_t ram_size, pic[n] = qdev_get_gpio_in(dev, n); } + pl041 = qdev_create(NULL, "pl041"); + qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512); + qdev_init_nofail(pl041); + sysbus_mmio_map(sysbus_from_qdev(pl041), 0, 0x10004000); + sysbus_connect_irq(sysbus_from_qdev(pl041), 0, pic[19]); + sysbus_create_simple("pl050_keyboard", 0x10006000, pic[20]); sysbus_create_simple("pl050_mouse", 0x10007000, pic[21]); diff --git a/hw/versatilepb.c b/hw/versatilepb.c index 68402cc..6370600 100644 --- a/hw/versatilepb.c +++ b/hw/versatilepb.c @@ -182,6 +182,7 @@ static void versatile_init(ram_addr_t ram_size, qemu_irq sic[32]; DeviceState *dev, *sysctl; SysBusDevice *busdev; + DeviceState *pl041; PCIBus *pci_bus; NICInfo *nd; int n; @@ -273,6 +274,13 @@ static void versatile_init(ram_addr_t ram_size, /* Add PL031 Real Time Clock. */ sysbus_create_simple("pl031", 0x101e8000, pic[10]); + /* Add PL041 AACI Interface to the LM4549 codec */ + pl041 = qdev_create(NULL, "pl041"); + qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512); + qdev_init_nofail(pl041); + sysbus_mmio_map(sysbus_from_qdev(pl041), 0, 0x10004000); + sysbus_connect_irq(sysbus_from_qdev(pl041), 0, sic[24]); + /* Memory map for Versatile/PB: */ /* 0x10000000 System registers. */ /* 0x10001000 PCI controller config registers. */ diff --git a/hw/vexpress.c b/hw/vexpress.c index c9766dd..0940a26 100644 --- a/hw/vexpress.c +++ b/hw/vexpress.c @@ -41,7 +41,7 @@ static void vexpress_a9_init(ram_addr_t ram_size, { CPUState *env = NULL; ram_addr_t ram_offset, vram_offset, sram_offset; - DeviceState *dev, *sysctl; + DeviceState *dev, *sysctl, *pl041; SysBusDevice *busdev; qemu_irq *irqp; qemu_irq pic[64]; @@ -118,6 +118,11 @@ static void vexpress_a9_init(ram_addr_t ram_size, /* 0x10001000 SP810 system control */ /* 0x10002000 serial bus PCI */ /* 0x10004000 PL041 audio */ + pl041 = qdev_create(NULL, "pl041"); + qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512); + qdev_init_nofail(pl041); + sysbus_mmio_map(sysbus_from_qdev(pl041), 0, 0x10004000); + sysbus_connect_irq(sysbus_from_qdev(pl041), 0, pic[11]); dev = sysbus_create_varargs("pl181", 0x10005000, pic[9], pic[10], NULL); /* Wire up MMC card detect and read-only signals */ diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c index 5f8f4bd..e24a2bf 100644 --- a/hw/virtio-balloon.c +++ b/hw/virtio-balloon.c @@ -18,22 +18,14 @@ #include "virtio.h" #include "pc.h" #include "cpu.h" -#include "monitor.h" #include "balloon.h" #include "virtio-balloon.h" #include "kvm.h" -#include "qlist.h" -#include "qint.h" -#include "qstring.h" #if defined(__linux__) #include <sys/mman.h> #endif -/* Disable guest-provided stats by now (https://bugzilla.redhat.com/show_bug.cgi?id=623903) */ -#define ENABLE_GUEST_STATS 0 - - typedef struct VirtIOBalloon { VirtIODevice vdev; @@ -43,8 +35,6 @@ typedef struct VirtIOBalloon uint64_t stats[VIRTIO_BALLOON_S_NR]; VirtQueueElement stats_vq_elem; size_t stats_vq_offset; - MonitorCompletion *stats_callback; - void *stats_opaque_callback_data; DeviceState *qdev; } VirtIOBalloon; @@ -76,31 +66,6 @@ static inline void reset_stats(VirtIOBalloon *dev) for (i = 0; i < VIRTIO_BALLOON_S_NR; dev->stats[i++] = -1); } -static void stat_put(QDict *dict, const char *label, uint64_t val) -{ - if (val != -1) - qdict_put(dict, label, qint_from_int(val)); -} - -static QObject *get_stats_qobject(VirtIOBalloon *dev) -{ - QDict *dict = qdict_new(); - uint64_t actual = ram_size - ((uint64_t) dev->actual << - VIRTIO_BALLOON_PFN_SHIFT); - - stat_put(dict, "actual", actual); -#if ENABLE_GUEST_STATS - stat_put(dict, "mem_swapped_in", dev->stats[VIRTIO_BALLOON_S_SWAP_IN]); - stat_put(dict, "mem_swapped_out", dev->stats[VIRTIO_BALLOON_S_SWAP_OUT]); - stat_put(dict, "major_page_faults", dev->stats[VIRTIO_BALLOON_S_MAJFLT]); - stat_put(dict, "minor_page_faults", dev->stats[VIRTIO_BALLOON_S_MINFLT]); - stat_put(dict, "free_mem", dev->stats[VIRTIO_BALLOON_S_MEMFREE]); - stat_put(dict, "total_mem", dev->stats[VIRTIO_BALLOON_S_MEMTOT]); -#endif - - return QOBJECT(dict); -} - static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBalloon *s = to_virtio_balloon(vdev); @@ -131,20 +96,6 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) } } -static void complete_stats_request(VirtIOBalloon *vb) -{ - QObject *stats; - - if (!vb->stats_opaque_callback_data) - return; - - stats = get_stats_qobject(vb); - vb->stats_callback(vb->stats_opaque_callback_data, stats); - qobject_decref(stats); - vb->stats_opaque_callback_data = NULL; - vb->stats_callback = NULL; -} - static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) { VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev); @@ -172,8 +123,6 @@ static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) s->stats[tag] = val; } s->stats_vq_offset = offset; - - complete_stats_request(s); } static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data) @@ -202,32 +151,33 @@ static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f) return f; } -static void virtio_balloon_stat(void *opaque, MonitorCompletion cb, - void *cb_data) +static void virtio_balloon_stat(void *opaque, BalloonInfo *info) { VirtIOBalloon *dev = opaque; - /* For now, only allow one request at a time. This restriction can be - * removed later by queueing callback and data pairs. +#if 0 + /* Disable guest-provided stats for now. For more details please check: + * https://bugzilla.redhat.com/show_bug.cgi?id=623903 + * + * If you do enable it (which is probably not going to happen as we + * need a new command for it), remember that you also need to fill the + * appropriate members of the BalloonInfo structure so that the stats + * are returned to the client. */ - if (dev->stats_callback != NULL) { - return; - } - dev->stats_callback = cb; - dev->stats_opaque_callback_data = cb_data; - - if (ENABLE_GUEST_STATS - && (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ))) { + if (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ)) { virtqueue_push(dev->svq, &dev->stats_vq_elem, dev->stats_vq_offset); virtio_notify(&dev->vdev, dev->svq); return; } +#endif /* Stats are not supported. Clear out any stale values that might * have been set by a more featureful guest kernel. */ reset_stats(dev); - complete_stats_request(dev); + + info->actual = ram_size - ((uint64_t) dev->actual << + VIRTIO_BALLOON_PFN_SHIFT); } static void virtio_balloon_to_target(void *opaque, ram_addr_t target) |