diff options
Diffstat (limited to 'hw')
167 files changed, 4133 insertions, 2139 deletions
diff --git a/hw/a9mpcore.c b/hw/a9mpcore.c index b5e5328395..6f108f4ce2 100644 --- a/hw/a9mpcore.c +++ b/hw/a9mpcore.c @@ -4,7 +4,7 @@ * Copyright (c) 2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* 64 external IRQ lines. */ @@ -20,19 +20,30 @@ #include "pc.h" #include "acpi.h" -struct acpi_table_header -{ - char signature [4]; /* ACPI signature (4 ASCII characters) */ +struct acpi_table_header { + uint16_t _length; /* our length, not actual part of the hdr */ + /* XXX why we have 2 length fields here? */ + 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 */ + 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 */ + char asl_compiler_id[4]; /* ASL compiler vendor ID */ uint32_t asl_compiler_revision; /* ASL compiler revision number */ } __attribute__((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 dfl_hdr[ACPI_TABLE_HDR_SIZE] = + "\0\0" /* fake _length (2) */ + "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 *acpi_tables; size_t acpi_tables_len; @@ -40,163 +51,198 @@ static int acpi_checksum(const uint8_t *data, int len) { int sum, i; sum = 0; - for(i = 0; i < len; i++) + for (i = 0; i < len; i++) { sum += data[i]; + } return (-sum) & 0xff; } +/* like strncpy() but zero-fills the tail of destination */ +static void strzcpy(char *dst, const char *src, size_t size) +{ + size_t len = strlen(src); + if (len >= size) { + len = size; + } else { + memset(dst + len, 0, size - len); + } + memcpy(dst, src, len); +} + +/* XXX fixme: this function uses obsolete argument parsing interface */ int acpi_table_add(const char *t) { - static const char *dfl_id = "QEMUQEMU"; char buf[1024], *p, *f; - struct acpi_table_header acpi_hdr; unsigned long val; - uint32_t length; - struct acpi_table_header *acpi_hdr_p; - size_t off; + size_t len, start, allen; + bool has_header; + int changed; + int r; + struct acpi_table_header hdr; + + r = 0; + r |= get_param_value(buf, sizeof(buf), "data", t) ? 1 : 0; + r |= get_param_value(buf, sizeof(buf), "file", t) ? 2 : 0; + switch (r) { + case 0: + buf[0] = '\0'; + /* fallthrough for default behavior */ + case 1: + has_header = false; + break; + case 2: + has_header = true; + break; + default: + fprintf(stderr, "acpitable: both data and file are specified\n"); + return -1; + } - memset(&acpi_hdr, 0, sizeof(acpi_hdr)); - - if (get_param_value(buf, sizeof(buf), "sig", t)) { - strncpy(acpi_hdr.signature, buf, 4); + if (!acpi_tables) { + allen = sizeof(uint16_t); + acpi_tables = qemu_mallocz(allen); } else { - strncpy(acpi_hdr.signature, dfl_id, 4); + allen = acpi_tables_len; } + + start = allen; + acpi_tables = qemu_realloc(acpi_tables, start + ACPI_TABLE_HDR_SIZE); + allen += has_header ? ACPI_TABLE_PFX_SIZE : ACPI_TABLE_HDR_SIZE; + + /* now read in the data files, reallocating buffer as needed */ + + for (f = strtok(buf, ":"); f; f = strtok(NULL, ":")) { + int fd = open(f, O_RDONLY); + + if (fd < 0) { + fprintf(stderr, "can't open file %s: %s\n", f, strerror(errno)); + return -1; + } + + for (;;) { + char data[8192]; + r = read(fd, data, sizeof(data)); + if (r == 0) { + break; + } else if (r > 0) { + acpi_tables = qemu_realloc(acpi_tables, allen + r); + memcpy(acpi_tables + allen, data, r); + allen += r; + } else if (errno != EINTR) { + fprintf(stderr, "can't read file %s: %s\n", + f, strerror(errno)); + close(fd); + return -1; + } + } + + close(fd); + } + + /* now fill in the header fields */ + + f = acpi_tables + start; /* start of the table */ + changed = 0; + + /* copy the header to temp place to align the fields */ + memcpy(&hdr, has_header ? f : dfl_hdr, ACPI_TABLE_HDR_SIZE); + + /* length of the table minus our prefix */ + len = allen - start - ACPI_TABLE_PFX_SIZE; + + hdr._length = cpu_to_le16(len); + + if (get_param_value(buf, sizeof(buf), "sig", t)) { + strzcpy(hdr.sig, buf, sizeof(hdr.sig)); + ++changed; + } + + /* length of the table including header, in bytes */ + if (has_header) { + /* check if actual length is correct */ + val = le32_to_cpu(hdr.length); + if (val != len) { + fprintf(stderr, + "warning: acpitable has wrong length," + " header says %lu, actual size %zu bytes\n", + val, len); + ++changed; + } + } + /* we may avoid putting length here if has_header is true */ + hdr.length = cpu_to_le32(len); + if (get_param_value(buf, sizeof(buf), "rev", t)) { - val = strtoul(buf, &p, 10); - if (val > 255 || *p != '\0') - goto out; - } else { - val = 1; + val = strtoul(buf, &p, 0); + if (val > 255 || *p) { + fprintf(stderr, "acpitable: \"rev=%s\" is invalid\n", buf); + return -1; + } + hdr.revision = (uint8_t)val; + ++changed; } - acpi_hdr.revision = (int8_t)val; if (get_param_value(buf, sizeof(buf), "oem_id", t)) { - strncpy(acpi_hdr.oem_id, buf, 6); - } else { - strncpy(acpi_hdr.oem_id, dfl_id, 6); + strzcpy(hdr.oem_id, buf, sizeof(hdr.oem_id)); + ++changed; } if (get_param_value(buf, sizeof(buf), "oem_table_id", t)) { - strncpy(acpi_hdr.oem_table_id, buf, 8); - } else { - strncpy(acpi_hdr.oem_table_id, dfl_id, 8); + strzcpy(hdr.oem_table_id, buf, sizeof(hdr.oem_table_id)); + ++changed; } if (get_param_value(buf, sizeof(buf), "oem_rev", t)) { - val = strtol(buf, &p, 10); - if(*p != '\0') - goto out; - } else { - val = 1; + val = strtol(buf, &p, 0); + if (*p) { + fprintf(stderr, "acpitable: \"oem_rev=%s\" is invalid\n", buf); + return -1; + } + hdr.oem_revision = cpu_to_le32(val); + ++changed; } - acpi_hdr.oem_revision = cpu_to_le32(val); if (get_param_value(buf, sizeof(buf), "asl_compiler_id", t)) { - strncpy(acpi_hdr.asl_compiler_id, buf, 4); - } else { - strncpy(acpi_hdr.asl_compiler_id, dfl_id, 4); + strzcpy(hdr.asl_compiler_id, buf, sizeof(hdr.asl_compiler_id)); + ++changed; } if (get_param_value(buf, sizeof(buf), "asl_compiler_rev", t)) { - val = strtol(buf, &p, 10); - if(*p != '\0') - goto out; - } else { - val = 1; - } - acpi_hdr.asl_compiler_revision = cpu_to_le32(val); - - if (!get_param_value(buf, sizeof(buf), "data", t)) { - buf[0] = '\0'; - } - - length = sizeof(acpi_hdr); - - f = buf; - while (buf[0]) { - struct stat s; - char *n = strchr(f, ':'); - if (n) - *n = '\0'; - if(stat(f, &s) < 0) { - fprintf(stderr, "Can't stat file '%s': %s\n", f, strerror(errno)); - goto out; + val = strtol(buf, &p, 0); + if (*p) { + fprintf(stderr, "acpitable: \"%s=%s\" is invalid\n", + "asl_compiler_rev", buf); + return -1; } - length += s.st_size; - if (!n) - break; - *n = ':'; - f = n + 1; + hdr.asl_compiler_revision = cpu_to_le32(val); + ++changed; } - if (!acpi_tables) { - acpi_tables_len = sizeof(uint16_t); - acpi_tables = qemu_mallocz(acpi_tables_len); + if (!has_header && !changed) { + fprintf(stderr, "warning: acpitable: no table headers are specified\n"); } - acpi_tables = qemu_realloc(acpi_tables, - acpi_tables_len + sizeof(uint16_t) + length); - p = acpi_tables + acpi_tables_len; - acpi_tables_len += sizeof(uint16_t) + length; - - *(uint16_t*)p = cpu_to_le32(length); - p += sizeof(uint16_t); - memcpy(p, &acpi_hdr, sizeof(acpi_hdr)); - off = sizeof(acpi_hdr); - - f = buf; - while (buf[0]) { - struct stat s; - int fd; - char *n = strchr(f, ':'); - if (n) - *n = '\0'; - fd = open(f, O_RDONLY); - - if(fd < 0) - goto out; - if(fstat(fd, &s) < 0) { - close(fd); - goto out; - } - /* off < length is necessary because file size can be changed - under our foot */ - while(s.st_size && off < length) { - int r; - r = read(fd, p + off, s.st_size); - if (r > 0) { - off += r; - s.st_size -= r; - } else if ((r < 0 && errno != EINTR) || r == 0) { - close(fd); - goto out; - } - } - close(fd); - if (!n) - break; - f = n + 1; - } - if (off < length) { - /* don't pass random value in process to guest */ - memset(p + off, 0, length - off); + /* now calculate checksum of the table, complete with the header */ + /* we may as well leave checksum intact if has_header is true */ + /* alternatively there may be a way to set cksum to a given value */ + hdr.checksum = 0; /* for checksum calculation */ + + /* put header back */ + memcpy(f, &hdr, sizeof(hdr)); + + if (changed || !has_header || 1) { + ((struct acpi_table_header *)f)->checksum = + acpi_checksum((uint8_t *)f + ACPI_TABLE_PFX_SIZE, len); } - acpi_hdr_p = (struct acpi_table_header*)p; - acpi_hdr_p->length = cpu_to_le32(length); - acpi_hdr_p->checksum = acpi_checksum((uint8_t*)p, length); /* increase number of tables */ - (*(uint16_t*)acpi_tables) = - cpu_to_le32(le32_to_cpu(*(uint16_t*)acpi_tables) + 1); + (*(uint16_t *)acpi_tables) = + cpu_to_le32(le32_to_cpu(*(uint16_t *)acpi_tables) + 1); + + acpi_tables_len = allen; return 0; -out: - if (acpi_tables) { - qemu_free(acpi_tables); - acpi_tables = NULL; - } - return -1; + } /* ACPI PM1a EVT */ diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c index 350558b859..29f0f76c35 100644 --- a/hw/acpi_piix4.c +++ b/hw/acpi_piix4.c @@ -23,6 +23,7 @@ #include "acpi.h" #include "sysemu.h" #include "range.h" +#include "ioport.h" //#define DEBUG @@ -63,6 +64,7 @@ typedef struct PIIX4PMState { qemu_irq irq; qemu_irq smi_irq; int kvm_enabled; + Notifier machine_ready; /* for pci hotplug */ ACPIGPE gpe; @@ -311,6 +313,19 @@ static void piix4_powerdown(void *opaque, int irq, int power_failing) acpi_pm1_evt_power_down(pm1a, tmr); } +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); @@ -337,11 +352,6 @@ static int piix4_pm_initfn(PCIDevice *dev) /* XXX: which specification is used ? The i82731AB has different mappings */ - pci_conf[0x5f] = (parallel_hds[0] != NULL ? 0x80 : 0) | 0x10; - pci_conf[0x63] = 0x60; - pci_conf[0x67] = (serial_hds[0] != NULL ? 0x08 : 0) | - (serial_hds[1] != NULL ? 0x90 : 0); - pci_conf[0x90] = s->smb_io_base | 1; pci_conf[0x91] = s->smb_io_base >> 8; pci_conf[0xd2] = 0x09; @@ -354,6 +364,8 @@ static int piix4_pm_initfn(PCIDevice *dev) qemu_system_powerdown = *qemu_allocate_irqs(piix4_powerdown, s, 1); pm_smbus_init(&s->dev.qdev, &s->smb); + 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(dev->bus, s); diff --git a/hw/an5206.c b/hw/an5206.c index 42a0163fbd..04ca420a90 100644 --- a/hw/an5206.c +++ b/hw/an5206.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" diff --git a/hw/apb_pci.c b/hw/apb_pci.c index 974c87a8ce..8b9939c06a 100644 --- a/hw/apb_pci.c +++ b/hw/apb_pci.c @@ -34,6 +34,7 @@ #include "rwhandler.h" #include "apb_pci.h" #include "sysemu.h" +#include "exec-memory.h" /* debug APB */ //#define DEBUG_APB @@ -346,6 +347,7 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base, d->bus = pci_register_bus(&d->busdev.qdev, "pci", pci_apb_set_irq, pci_pbm_map_irq, d, + get_system_memory(), 0, 32); pci_bus_set_mem_base(d->bus, mem_base); diff --git a/hw/arm-misc.h b/hw/arm-misc.h index 9aeeaea759..f8a747289b 100644 --- a/hw/arm-misc.h +++ b/hw/arm-misc.h @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. * */ diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c index 3bbd8856cf..b47707f7bb 100644 --- a/hw/arm11mpcore.c +++ b/hw/arm11mpcore.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* ??? The MPCore TRM says the on-chip controller has 224 external IRQ lines diff --git a/hw/arm_boot.c b/hw/arm_boot.c index e0215768b1..215d5dec64 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 0e934ecd64..fb07314d52 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* This file contains implementation code for the RealView EB interrupt diff --git a/hw/arm_pic.c b/hw/arm_pic.c index f44568cebb..985148a380 100644 --- a/hw/arm_pic.c +++ b/hw/arm_pic.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL + * This code is licensed under the LGPL */ #include "hw.h" diff --git a/hw/arm_sysctl.c b/hw/arm_sysctl.c index 9225b588b8..fd0c8bc3d6 100644 --- a/hw/arm_sysctl.c +++ b/hw/arm_sysctl.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" diff --git a/hw/arm_timer.c b/hw/arm_timer.c index dac9e70750..fd9448f055 100644 --- a/hw/arm_timer.c +++ b/hw/arm_timer.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index d06eec9b39..1df8d4db45 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. * * The ARMv7M System controller is fairly tightly tied in with the * NVIC. Much of that is also implemented here. diff --git a/hw/axis_dev88.c b/hw/axis_dev88.c index 0e2135afd0..e0a8c14c12 100644 --- a/hw/axis_dev88.c +++ b/hw/axis_dev88.c @@ -30,13 +30,14 @@ #include "loader.h" #include "elf.h" #include "cris-boot.h" +#include "blockdev.h" #define D(x) #define DNAND(x) struct nand_state_t { - NANDFlashState *nand; + DeviceState *nand; unsigned int rdy:1; unsigned int ale:1; unsigned int cle:1; @@ -251,6 +252,7 @@ void axisdev88_init (ram_addr_t ram_size, CPUState *env; DeviceState *dev; SysBusDevice *s; + DriveInfo *nand; qemu_irq irq[30], nmi[2], *cpu_irq; void *etraxfs_dmac; struct etraxfs_dma_client *eth[2] = {NULL, NULL}; @@ -278,7 +280,9 @@ void axisdev88_init (ram_addr_t ram_size, /* Attach a NAND flash to CS1. */ - nand_state.nand = nand_init(NAND_MFR_STMICRO, 0x39); + nand = drive_get(IF_MTD, 0, 0); + nand_state.nand = nand_init(nand ? nand->bdrv : NULL, + NAND_MFR_STMICRO, 0x39); nand_regs = cpu_register_io_memory(nand_read, nand_write, &nand_state, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(0x10000000, 0x05000000, nand_regs); @@ -576,7 +576,7 @@ static void baum_close(struct CharDriverState *chr) qemu_free(baum); } -CharDriverState *chr_baum_init(QemuOpts *opts) +int chr_baum_init(QemuOpts *opts, CharDriverState **_chr) { BaumDriverState *baum; CharDriverState *chr; @@ -629,7 +629,8 @@ CharDriverState *chr_baum_init(QemuOpts *opts) qemu_chr_generic_open(chr); - return chr; + *_chr = chr; + return 0; fail: qemu_free_timer(baum->cellCount_timer); @@ -638,5 +639,5 @@ fail_handle: qemu_free(handle); qemu_free(chr); qemu_free(baum); - return NULL; + return -EIO; } @@ -23,4 +23,4 @@ */ /* char device */ -CharDriverState *chr_baum_init(QemuOpts *opts); +int chr_baum_init(QemuOpts *opts, CharDriverState **_chr); diff --git a/hw/bitbang_i2c.c b/hw/bitbang_i2c.c index 2937b5c4a1..53e9c5c4c4 100644 --- a/hw/bitbang_i2c.c +++ b/hw/bitbang_i2c.c @@ -4,7 +4,7 @@ * * Copyright (c) 2008 Jan Kiszka * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "hw.h" #include "bitbang_i2c.h" diff --git a/hw/bonito.c b/hw/bonito.c index e8c57a36ff..5f62dda6e2 100644 --- a/hw/bonito.c +++ b/hw/bonito.c @@ -42,6 +42,7 @@ #include "mips.h" #include "pci_host.h" #include "sysemu.h" +#include "exec-memory.h" //#define DEBUG_BONITO @@ -773,7 +774,8 @@ PCIBus *bonito_init(qemu_irq *pic) dev = qdev_create(NULL, "Bonito-pcihost"); pcihost = FROM_SYSBUS(BonitoState, sysbus_from_qdev(dev)); b = pci_register_bus(&pcihost->busdev.qdev, "pci", pci_bonito_set_irq, - pci_bonito_map_irq, pic, 0x28, 32); + pci_bonito_map_irq, pic, get_system_memory(), + 0x28, 32); pcihost->bus = b; qdev_init_nofail(dev); diff --git a/hw/bt-hid.c b/hw/bt-hid.c index 09120af074..5f1afe3e89 100644 --- a/hw/bt-hid.c +++ b/hw/bt-hid.c @@ -19,7 +19,9 @@ */ #include "qemu-common.h" -#include "usb.h" +#include "qemu-timer.h" +#include "console.h" +#include "hid.h" #include "bt.h" enum hid_transaction_req { @@ -86,7 +88,7 @@ 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; - USBDevice *usbdev; + HIDState hid; int proto; int connected; @@ -111,7 +113,7 @@ static void bt_hid_reset(struct bt_hid_device_s *s) bt_l2cap_device_done(&s->btdev); bt_l2cap_device_init(&s->btdev, net); - s->usbdev->info->handle_reset(s->usbdev); + hid_reset(&s->hid); s->proto = BT_HID_PROTO_REPORT; s->state = bt_state_ready; s->dataother.len = 0; @@ -124,23 +126,16 @@ static void bt_hid_reset(struct bt_hid_device_s *s) static int bt_hid_out(struct bt_hid_device_s *s) { - USBPacket p; - if (s->data_type == BT_DATA_OUTPUT) { - p.pid = USB_TOKEN_OUT; - p.devep = 1; - p.data = s->dataout.buffer; - p.len = s->dataout.len; - s->dataout.len = s->usbdev->info->handle_data(s->usbdev, &p); - - return s->dataout.len; + /* 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? */ - p.devep = 0; + ; } return -1; @@ -148,14 +143,8 @@ static int bt_hid_out(struct bt_hid_device_s *s) static int bt_hid_in(struct bt_hid_device_s *s) { - USBPacket p; - - p.pid = USB_TOKEN_IN; - p.devep = 1; - p.data = s->datain.buffer; - p.len = sizeof(s->datain.buffer); - s->datain.len = s->usbdev->info->handle_data(s->usbdev, &p); - + s->datain.len = hid_keyboard_poll(&s->hid, s->datain.buffer, + sizeof(s->datain.buffer)); return s->datain.len; } @@ -323,8 +312,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s, break; } s->proto = parameter; - s->usbdev->info->handle_control(s->usbdev, NULL, SET_PROTOCOL, s->proto, 0, 0, - NULL); + s->hid.protocol = parameter; ret = BT_HS_SUCCESSFUL; break; @@ -333,8 +321,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s, ret = BT_HS_ERR_INVALID_PARAMETER; break; } - s->usbdev->info->handle_control(s->usbdev, NULL, GET_IDLE, 0, 0, 1, - s->control->sdu_out(s->control, 1)); + *s->control->sdu_out(s->control, 1) = s->hid.idle; s->control->sdu_submit(s->control); break; @@ -344,11 +331,7 @@ static void bt_hid_control_transaction(struct bt_hid_device_s *s, break; } - /* We don't need to know about the Idle Rate here really, - * so just pass it on to the device. */ - ret = s->usbdev->info->handle_control(s->usbdev, NULL, - SET_IDLE, data[1], 0, 0, NULL) ? - BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER; + s->hid.idle = data[1]; /* XXX: Does this generate a handshake? */ break; @@ -385,9 +368,10 @@ static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len) bt_hid_control_transaction(hid, data, len); } -static void bt_hid_datain(void *opaque) +static void bt_hid_datain(HIDState *hs) { - struct bt_hid_device_s *hid = opaque; + 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 @@ -450,7 +434,7 @@ static void bt_hid_connected_update(struct bt_hid_device_s *hid) hid->btdev.device.inquiry_scan = !hid->connected; if (hid->connected && !prev) { - hid->usbdev->info->handle_reset(hid->usbdev); + hid_reset(&hid->hid); hid->proto = BT_HID_PROTO_REPORT; } @@ -518,7 +502,7 @@ static void bt_hid_destroy(struct bt_device_s *dev) bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG); bt_l2cap_device_done(&hid->btdev); - hid->usbdev->info->handle_destroy(hid->usbdev); + hid_free(&hid->hid); qemu_free(hid); } @@ -531,7 +515,7 @@ enum peripheral_minor_class { }; static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net, - USBDevice *dev, enum peripheral_minor_class minor) + enum peripheral_minor_class minor) { struct bt_hid_device_s *s = qemu_mallocz(sizeof(*s)); uint32_t class = @@ -551,9 +535,8 @@ static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net, bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR, BT_HID_MTU, bt_hid_new_interrupt_ch); - s->usbdev = dev; - s->btdev.device.lmp_name = s->usbdev->product_desc; - usb_hid_datain_cb(s->usbdev, s, bt_hid_datain); + hid_init(&s->hid, HID_KEYBOARD, bt_hid_datain); + s->btdev.device.lmp_name = "BT Keyboard"; s->btdev.device.handle_destroy = bt_hid_destroy; @@ -566,6 +549,5 @@ static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net, struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net) { - USBDevice *dev = usb_create_simple(NULL /* FIXME */, "usb-kbd"); - return bt_hid_init(net, dev, class_keyboard); + return bt_hid_init(net, class_keyboard); } diff --git a/hw/ccid-card-emulated.c b/hw/ccid-card-emulated.c index 0b0718426d..4762e85116 100644 --- a/hw/ccid-card-emulated.c +++ b/hw/ccid-card-emulated.c @@ -4,7 +4,7 @@ * Copyright (c) 2011 Red Hat. * Written by Alon Levy. * - * This code is licenced under the GNU LGPL, version 2 or later. + * This code is licensed under the GNU LGPL, version 2 or later. */ /* @@ -4,7 +4,7 @@ * Copyright (c) 2011 Red Hat. * Written by Alon Levy. * - * This code is licenced under the GNU LGPL, version 2 or later. + * This code is licensed under the GNU LGPL, version 2 or later. */ #ifndef CCID_H @@ -29,7 +29,6 @@ struct CCIDCardState { */ struct CCIDCardInfo { DeviceInfo qdev; - void (*print)(Monitor *mon, CCIDCardState *card, int indent); const uint8_t *(*get_atr)(CCIDCardState *card, uint32_t *len); void (*apdu_from_guest)(CCIDCardState *card, const uint8_t *apdu, diff --git a/hw/dp8393x.c b/hw/dp8393x.c index c332dd59d2..1bcd8eeba9 100644 --- a/hw/dp8393x.c +++ b/hw/dp8393x.c @@ -898,7 +898,7 @@ void dp83932_init(NICInfo *nd, target_phys_addr_t base, int it_shift, s->watchdog = qemu_new_timer_ns(vm_clock, dp8393x_watchdog, s); s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */ - memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(s->conf.macaddr)); + s->conf.macaddr = nd->macaddr; s->conf.vlan = nd->vlan; s->conf.peer = nd->netdev; diff --git a/hw/ds1225y.c b/hw/ds1225y.c index b1c52321fe..662d7b5166 100644 --- a/hw/ds1225y.c +++ b/hw/ds1225y.c @@ -22,31 +22,24 @@ * THE SOFTWARE. */ -#include "hw.h" -#include "mips.h" -#include "nvram.h" +#include "sysbus.h" +#include "trace.h" -//#define DEBUG_NVRAM - -typedef struct ds1225y_t -{ +typedef struct { + DeviceState qdev; uint32_t chip_size; + char *filename; QEMUFile *file; uint8_t *contents; - uint8_t protection; -} ds1225y_t; - +} NvRamState; static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr) { - ds1225y_t *s = opaque; + NvRamState *s = opaque; uint32_t val; val = s->contents[addr]; - -#ifdef DEBUG_NVRAM - printf("nvram: read 0x%x at " TARGET_FMT_lx "\n", val, addr); -#endif + trace_nvram_read(addr, val); return val; } @@ -70,13 +63,12 @@ static uint32_t nvram_readl (void *opaque, target_phys_addr_t addr) static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t val) { - ds1225y_t *s = opaque; + NvRamState *s = opaque; -#ifdef DEBUG_NVRAM - printf("nvram: write 0x%x at " TARGET_FMT_lx "\n", val, addr); -#endif + val &= 0xff; + trace_nvram_write(addr, s->contents[addr], val); - s->contents[addr] = val & 0xff; + s->contents[addr] = val; if (s->file) { qemu_fseek(s->file, addr, SEEK_SET); qemu_put_byte(s->file, (int)val); @@ -98,34 +90,6 @@ static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t val) nvram_writeb(opaque, addr + 3, (val >> 24) & 0xff); } -static void nvram_writeb_protected (void *opaque, target_phys_addr_t addr, uint32_t val) -{ - ds1225y_t *s = opaque; - - if (s->protection != 7) { -#ifdef DEBUG_NVRAM - printf("nvram: prevent write of 0x%x at " TARGET_FMT_lx "\n", val, addr); -#endif - return; - } - - nvram_writeb(opaque, addr, val); -} - -static void nvram_writew_protected (void *opaque, target_phys_addr_t addr, uint32_t val) -{ - nvram_writeb_protected(opaque, addr, val & 0xff); - nvram_writeb_protected(opaque, addr + 1, (val >> 8) & 0xff); -} - -static void nvram_writel_protected (void *opaque, target_phys_addr_t addr, uint32_t val) -{ - nvram_writeb_protected(opaque, addr, val & 0xff); - nvram_writeb_protected(opaque, addr + 1, (val >> 8) & 0xff); - nvram_writeb_protected(opaque, addr + 2, (val >> 16) & 0xff); - nvram_writeb_protected(opaque, addr + 3, (val >> 24) & 0xff); -} - static CPUReadMemoryFunc * const nvram_read[] = { &nvram_readb, &nvram_readw, @@ -138,45 +102,83 @@ static CPUWriteMemoryFunc * const nvram_write[] = { &nvram_writel, }; -static CPUWriteMemoryFunc * const nvram_write_protected[] = { - &nvram_writeb_protected, - &nvram_writew_protected, - &nvram_writel_protected, +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) { + qemu_fclose(s->file); + } + + /* Write back nvram contents */ + s->file = qemu_fopen(s->filename, "wb"); + if (s->file) { + /* Write back contents, as 'wb' mode cleaned the file */ + qemu_put_buffer(s->file, s->contents, s->chip_size); + qemu_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() + } }; -/* Initialisation routine */ -void *ds1225y_init(target_phys_addr_t mem_base, const char *filename) +typedef struct { + SysBusDevice busdev; + NvRamState nvram; +} SysBusNvRamState; + +static int nvram_sysbus_initfn(SysBusDevice *dev) { - ds1225y_t *s; - int mem_indexRW, mem_indexRP; + NvRamState *s = &FROM_SYSBUS(SysBusNvRamState, dev)->nvram; QEMUFile *file; + int s_io; - s = qemu_mallocz(sizeof(ds1225y_t)); - s->chip_size = 0x2000; /* Fixed for ds1225y chip: 8 KiB */ s->contents = qemu_mallocz(s->chip_size); - s->protection = 7; + + s_io = cpu_register_io_memory(nvram_read, nvram_write, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, s->chip_size, s_io); /* Read current file */ - file = qemu_fopen(filename, "rb"); + file = qemu_fopen(s->filename, "rb"); if (file) { /* Read nvram contents */ qemu_get_buffer(file, s->contents, s->chip_size); qemu_fclose(file); } - s->file = qemu_fopen(filename, "wb"); - if (s->file) { - /* Write back contents, as 'wb' mode cleaned the file */ - qemu_put_buffer(s->file, s->contents, s->chip_size); - qemu_fflush(s->file); - } + nvram_post_load(s, 0); + + return 0; +} + +static SysBusDeviceInfo nvram_sysbus_info = { + .qdev.name = "ds1225y", + .qdev.size = sizeof(SysBusNvRamState), + .qdev.vmsd = &vmstate_nvram, + .init = nvram_sysbus_initfn, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("size", SysBusNvRamState, nvram.chip_size, 0x2000), + DEFINE_PROP_STRING("filename", SysBusNvRamState, nvram.filename), + DEFINE_PROP_END_OF_LIST(), + }, +}; - /* Read/write memory */ - mem_indexRW = cpu_register_io_memory(nvram_read, nvram_write, s, - DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(mem_base, s->chip_size, mem_indexRW); - /* Read/write protected memory */ - mem_indexRP = cpu_register_io_memory(nvram_read, nvram_write_protected, s, - DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(mem_base + s->chip_size, s->chip_size, mem_indexRP); - return s; +static void nvram_register(void) +{ + sysbus_register_withprop(&nvram_sysbus_info); } + +device_init(nvram_register) diff --git a/hw/ds1338.c b/hw/ds1338.c index 6f5ae5e6c1..3522af5b5a 100644 --- a/hw/ds1338.c +++ b/hw/ds1338.c @@ -4,7 +4,7 @@ * Copyright (c) 2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "i2c.h" diff --git a/hw/dummy_m68k.c b/hw/dummy_m68k.c index cec1cc8e82..eed9e3843c 100644 --- a/hw/dummy_m68k.c +++ b/hw/dummy_m68k.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" @@ -244,7 +244,7 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid) DPRINTF("do_busid_cmd: busid 0x%x\n", busid); lun = busid & 7; - s->current_req = scsi_req_new(s->current_dev, 0, lun); + s->current_req = scsi_req_new(s->current_dev, 0, lun, NULL); datalen = scsi_req_enqueue(s->current_req, buf); s->ti_size = datalen; if (datalen != 0) { diff --git a/hw/etraxfs_eth.c b/hw/etraxfs_eth.c index 6aa4007203..dff5f55f33 100644 --- a/hw/etraxfs_eth.c +++ b/hw/etraxfs_eth.c @@ -602,7 +602,7 @@ void *etraxfs_eth_init(NICInfo *nd, target_phys_addr_t base, int phyaddr) DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory (base, 0x5c, eth->ethregs); - memcpy(eth->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); + eth->conf.macaddr = nd->macaddr; eth->conf.vlan = nd->vlan; eth->conf.peer = nd->netdev; diff --git a/hw/flash.h b/hw/flash.h index c22e1a922c..140ae39801 100644 --- a/hw/flash.h +++ b/hw/flash.h @@ -18,14 +18,13 @@ pflash_t *pflash_cfi02_register(target_phys_addr_t base, ram_addr_t off, int be); /* nand.c */ -typedef struct NANDFlashState NANDFlashState; -NANDFlashState *nand_init(int manf_id, int chip_id); -void nand_done(NANDFlashState *s); -void nand_setpins(NANDFlashState *s, uint8_t cle, uint8_t ale, +DeviceState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id); +void nand_setpins(DeviceState *dev, uint8_t cle, uint8_t ale, uint8_t ce, uint8_t wp, uint8_t gnd); -void nand_getpins(NANDFlashState *s, int *rb); -void nand_setio(NANDFlashState *s, uint8_t value); -uint8_t nand_getio(NANDFlashState *s); +void nand_getpins(DeviceState *dev, int *rb); +void nand_setio(DeviceState *dev, uint32_t value); +uint32_t nand_getio(DeviceState *dev); +uint32_t nand_getbuswidth(DeviceState *dev); #define NAND_MFR_TOSHIBA 0x98 #define NAND_MFR_SAMSUNG 0xec @@ -39,7 +38,9 @@ uint8_t nand_getio(NANDFlashState *s); /* onenand.c */ void onenand_base_update(void *opaque, target_phys_addr_t new); void onenand_base_unmap(void *opaque); -void *onenand_init(uint32_t id, int regshift, qemu_irq irq); +void *onenand_init(BlockDriverState *bdrv, + uint16_t man_id, uint16_t dev_id, uint16_t ver_id, + int regshift, qemu_irq irq); void *onenand_raw_otp(void *opaque); /* ecc.c */ diff --git a/hw/fw_cfg.c b/hw/fw_cfg.c index 85c8c3c7bf..a29db9055d 100644 --- a/hw/fw_cfg.c +++ b/hw/fw_cfg.c @@ -26,6 +26,7 @@ #include "isa.h" #include "fw_cfg.h" #include "sysbus.h" +#include "qemu-error.h" /* debug firmware config */ //#define DEBUG_FW_CFG @@ -56,6 +57,143 @@ struct FWCfgState { Notifier machine_ready; }; +#define JPG_FILE 0 +#define BMP_FILE 1 + +static FILE *probe_splashfile(char *filename, int *file_sizep, int *file_typep) +{ + FILE *fp = NULL; + int fop_ret; + int file_size; + int file_type = -1; + unsigned char buf[2] = {0, 0}; + unsigned int filehead_value = 0; + int bmp_bpp; + + fp = fopen(filename, "rb"); + if (fp == NULL) { + error_report("failed to open file '%s'.", filename); + return fp; + } + /* check file size */ + fseek(fp, 0L, SEEK_END); + file_size = ftell(fp); + if (file_size < 2) { + error_report("file size is less than 2 bytes '%s'.", filename); + fclose(fp); + fp = NULL; + return fp; + } + /* check magic ID */ + fseek(fp, 0L, SEEK_SET); + fop_ret = fread(buf, 1, 2, fp); + filehead_value = (buf[0] + (buf[1] << 8)) & 0xffff; + if (filehead_value == 0xd8ff) { + file_type = JPG_FILE; + } else { + if (filehead_value == 0x4d42) { + file_type = BMP_FILE; + } + } + if (file_type < 0) { + error_report("'%s' not jpg/bmp file,head:0x%x.", + filename, filehead_value); + fclose(fp); + fp = NULL; + return fp; + } + /* check BMP bpp */ + if (file_type == BMP_FILE) { + fseek(fp, 28, SEEK_SET); + fop_ret = fread(buf, 1, 2, fp); + bmp_bpp = (buf[0] + (buf[1] << 8)) & 0xffff; + if (bmp_bpp != 24) { + error_report("only 24bpp bmp file is supported."); + fclose(fp); + fp = NULL; + return fp; + } + } + /* return values */ + *file_sizep = file_size; + *file_typep = file_type; + return fp; +} + +static void fw_cfg_bootsplash(FWCfgState *s) +{ + int boot_splash_time = -1; + const char *boot_splash_filename = NULL; + char *p; + char *filename; + FILE *fp; + int fop_ret; + int file_size; + int file_type = -1; + 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; + } + /* probing the file */ + fp = probe_splashfile(filename, &file_size, &file_type); + if (fp == NULL) { + qemu_free(filename); + return; + } + /* loading file data */ + if (boot_splash_filedata != NULL) { + qemu_free(boot_splash_filedata); + } + boot_splash_filedata = qemu_malloc(file_size); + boot_splash_filedata_size = file_size; + fseek(fp, 0L, SEEK_SET); + fop_ret = fread(boot_splash_filedata, 1, file_size, fp); + fclose(fp); + /* 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); + } + qemu_free(filename); + } +} + static void fw_cfg_write(FWCfgState *s, uint8_t value) { int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); @@ -316,7 +454,7 @@ int fw_cfg_add_file(FWCfgState *s, const char *filename, uint8_t *data, return 1; } -static void fw_cfg_machine_ready(struct Notifier* n) +static void fw_cfg_machine_ready(struct Notifier *n, void *data) { uint32_t len; FWCfgState *s = container_of(n, FWCfgState, machine_ready); @@ -352,7 +490,7 @@ FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); fw_cfg_add_i16(s, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu); - + fw_cfg_bootsplash(s); s->machine_ready.notify = fw_cfg_machine_ready; qemu_add_machine_init_done_notifier(&s->machine_ready); diff --git a/hw/grackle_pci.c b/hw/grackle_pci.c index cee07e06c7..da67cf9b38 100644 --- a/hw/grackle_pci.c +++ b/hw/grackle_pci.c @@ -61,7 +61,8 @@ static void pci_grackle_reset(void *opaque) { } -PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic) +PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, + MemoryRegion *address_space) { DeviceState *dev; SysBusDevice *s; @@ -74,7 +75,7 @@ PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic) d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci", pci_grackle_set_irq, pci_grackle_map_irq, - pic, 0, 4); + pic, address_space, 0, 4); pci_create_simple(d->host_state.bus, 0, "grackle"); diff --git a/hw/gt64xxx.c b/hw/gt64xxx.c index 8e1f6a069d..65e63ddab8 100644 --- a/hw/gt64xxx.c +++ b/hw/gt64xxx.c @@ -27,6 +27,7 @@ #include "pci.h" #include "pci_host.h" #include "pc.h" +#include "exec-memory.h" //#define DEBUG @@ -1092,7 +1093,8 @@ PCIBus *gt64120_register(qemu_irq *pic) d = FROM_SYSBUS(GT64120State, s); d->pci.bus = pci_register_bus(&d->busdev.qdev, "pci", gt64120_pci_set_irq, gt64120_pci_map_irq, - pic, PCI_DEVFN(18, 0), 4); + pic, get_system_memory(), + PCI_DEVFN(18, 0), 4); d->ISD_handle = cpu_register_io_memory(gt64120_read, gt64120_write, d, DEVICE_NATIVE_ENDIAN); diff --git a/hw/hid.c b/hw/hid.c new file mode 100644 index 0000000000..7b5ef5fc92 --- /dev/null +++ b/hw/hid.c @@ -0,0 +1,403 @@ +/* + * 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.h" +#include "console.h" +#include "qemu-timer.h" +#include "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; +} + +void hid_set_next_idle(HIDState *hs, int64_t curtime) +{ + hs->next_idle_clock = curtime + (get_ticks_per_sec() * hs->idle * 4) / 1000; +} + +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)) { + x1 = 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; + } +} + +int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len) +{ + int dx, dy, dz, b, l; + int index; + HIDPointerEvent *e; + + if (!hs->ptr.mouse_grabbed) { + qemu_activate_mouse_event_handler(hs->ptr.eh_entry); + hs->ptr.mouse_grabbed = 1; + } + + /* 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) +{ + 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: + qemu_add_kbd_event_handler(hid_keyboard_event, hs); + 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; +} + +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; + } +} + +void hid_init(HIDState *hs, int kind, HIDEventFunc event) +{ + hs->kind = kind; + hs->event = event; + + 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"); + } +} diff --git a/hw/hid.h b/hw/hid.h new file mode 100644 index 0000000000..4a8fa5b63f --- /dev/null +++ b/hw/hid.h @@ -0,0 +1,58 @@ +#ifndef QEMU_HID_H +#define QEMU_HID_H + +#define HID_MOUSE 1 +#define HID_TABLET 2 +#define HID_KEYBOARD 3 + +typedef struct HIDPointerEvent { + int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */ + int32_t dz, buttons_state; +} HIDPointerEvent; + +#define QUEUE_LENGTH 16 /* should be enough for a triple-click */ +#define QUEUE_MASK (QUEUE_LENGTH-1u) +#define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK) + +typedef struct HIDState HIDState; +typedef void (*HIDEventFunc)(HIDState *s); + +typedef struct HIDMouseState { + HIDPointerEvent queue[QUEUE_LENGTH]; + int mouse_grabbed; + QEMUPutMouseEntry *eh_entry; +} HIDMouseState; + +typedef struct HIDKeyboardState { + uint32_t keycodes[QUEUE_LENGTH]; + uint16_t modifiers; + uint8_t leds; + uint8_t key[16]; + int32_t keys; +} HIDKeyboardState; + +struct HIDState { + union { + HIDMouseState ptr; + HIDKeyboardState kbd; + }; + uint32_t head; /* index into circular queue */ + uint32_t n; + int kind; + int32_t protocol; + uint8_t idle; + int64_t next_idle_clock; + HIDEventFunc event; +}; + +void hid_init(HIDState *hs, int kind, HIDEventFunc event); +void hid_reset(HIDState *hs); +void hid_free(HIDState *hs); + +bool hid_has_events(HIDState *hs); +void hid_set_next_idle(HIDState *hs, int64_t curtime); +int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len); +int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len); +int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len); + +#endif /* QEMU_HID_H */ @@ -324,6 +324,7 @@ typedef struct VMStateSubsection { struct VMStateDescription { const char *name; + int unmigratable; int version_id; int minimum_version_id; int minimum_version_id_old; @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ #include "i2c.h" @@ -72,6 +72,6 @@ void wm8750_set_bclk_in(void *opaque, int new_hz); void tmp105_set(i2c_slave *i2c, int temp); /* lm832x.c */ -void lm832x_key_event(i2c_slave *i2c, int key, int state); +void lm832x_key_event(DeviceState *dev, int key, int state); #endif @@ -13,6 +13,7 @@ ISADevice *isa_ide_init(int iobase, int iobase2, int isairq, /* ide-pci.c */ void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table, int secondary_ide_enabled); +PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); void vt82c686b_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); diff --git a/hw/ide/ich.c b/hw/ide/ich.c index 054e0734e4..d241ea8005 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -72,6 +72,11 @@ #include <hw/ide/pci.h> #include <hw/ide/ahci.h> +static const VMStateDescription vmstate_ahci = { + .name = "ahci", + .unmigratable = 1, +}; + static int pci_ich9_ahci_init(PCIDevice *dev) { struct AHCIPCIState *d; @@ -123,6 +128,7 @@ static PCIDeviceInfo ich_ahci_info[] = { .qdev.name = "ich9-ahci", .qdev.alias = "ahci", .qdev.size = sizeof(AHCIPCIState), + .qdev.vmsd = &vmstate_ahci, .init = pci_ich9_ahci_init, .exit = pci_ich9_uninit, .config_write = pci_ich9_write_config, diff --git a/hw/ide/piix.c b/hw/ide/piix.c index 84f72b0a66..f527dbd57e 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -149,6 +149,42 @@ static int pci_piix_ide_initfn(PCIDevice *dev) return 0; } +static int pci_piix3_xen_ide_unplug(DeviceState *dev) +{ + PCIDevice *pci_dev; + PCIIDEState *pci_ide; + DriveInfo *di; + int i = 0; + + pci_dev = DO_UPCAST(PCIDevice, qdev, dev); + pci_ide = DO_UPCAST(PCIIDEState, dev, pci_dev); + + for (; i < 3; i++) { + di = drive_get_by_index(IF_IDE, i); + if (di != NULL && di->bdrv != NULL && !di->bdrv->removable) { + DeviceState *ds = bdrv_get_attached(di->bdrv); + if (ds) { + bdrv_detach(di->bdrv, ds); + } + bdrv_close(di->bdrv); + pci_ide->bus[di->bus].ifs[di->unit].bs = NULL; + drive_put_ref(di); + } + } + qdev_reset_all(&(pci_ide->dev.qdev)); + return 0; +} + +PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) +{ + PCIDevice *dev; + + dev = pci_create_simple(bus, devfn, "piix3-ide-xen"); + dev->qdev.info->unplug = pci_piix3_xen_ide_unplug; + pci_ide_create_devs(dev, hd_table); + return dev; +} + /* hd_table must contain 4 block drivers */ /* NOTE: for the PIIX3, the IRQs and IOports are hardcoded */ PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) @@ -182,6 +218,14 @@ static PCIDeviceInfo piix_ide_info[] = { .device_id = PCI_DEVICE_ID_INTEL_82371SB_1, .class_id = PCI_CLASS_STORAGE_IDE, },{ + .qdev.name = "piix3-ide-xen", + .qdev.size = sizeof(PCIIDEState), + .qdev.no_user = 1, + .init = pci_piix_ide_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82371SB_1, + .class_id = PCI_CLASS_STORAGE_IDE, + },{ .qdev.name = "piix4-ide", .qdev.size = sizeof(PCIIDEState), .qdev.no_user = 1, diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index d9b8f24bb5..6bd8d20270 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -31,6 +31,10 @@ static struct BusInfo ide_bus_info = { .name = "IDE", .size = sizeof(IDEBus), .get_fw_dev_path = idebus_get_fw_dev_path, + .props = (Property[]) { + DEFINE_PROP_UINT32("unit", IDEDevice, unit, -1), + DEFINE_PROP_END_OF_LIST(), + }, }; void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id) @@ -174,7 +178,6 @@ static int ide_drive_initfn(IDEDevice *dev) } #define DEFINE_IDE_DEV_PROPERTIES() \ - DEFINE_PROP_UINT32("unit", IDEDrive, dev.unit, -1), \ DEFINE_BLOCK_PROPERTIES(IDEDrive, dev.conf), \ DEFINE_PROP_STRING("ver", IDEDrive, dev.version), \ DEFINE_PROP_STRING("serial", IDEDrive, dev.serial) diff --git a/hw/integratorcp.c b/hw/integratorcp.c index a6c27be82c..281410899f 100644 --- a/hw/integratorcp.c +++ b/hw/integratorcp.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "sysbus.h" diff --git a/hw/lan9118.c b/hw/lan9118.c index 3f3c05df4c..73a8661ca3 100644 --- a/hw/lan9118.c +++ b/hw/lan9118.c @@ -4,7 +4,7 @@ * Copyright (c) 2009 CodeSourcery, LLC. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2 + * This code is licensed under the GNU GPL v2 */ #include "sysbus.h" diff --git a/hw/lm832x.c b/hw/lm832x.c index 590a4ccff9..992ce49729 100644 --- a/hw/lm832x.c +++ b/hw/lm832x.c @@ -474,9 +474,9 @@ static int lm8323_init(i2c_slave *i2c) return 0; } -void lm832x_key_event(struct i2c_slave *i2c, int key, int state) +void lm832x_key_event(DeviceState *dev, int key, int state) { - LM823KbdState *s = (LM823KbdState *) i2c; + LM823KbdState *s = FROM_I2C_SLAVE(LM823KbdState, I2C_SLAVE_FROM_QDEV(dev)); if ((s->status & INT_ERROR) && (s->error & ERR_FIFOOVR)) return; diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index 940b43abfd..e9904c49d9 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ /* ??? Need to check if the {read,write}[wl] routines work properly on @@ -661,7 +661,7 @@ static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag) static void lsi_request_cancelled(SCSIRequest *req) { LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); - lsi_request *p; + lsi_request *p = req->hba_private; if (s->current && req == s->current->req) { scsi_req_unref(req); @@ -670,7 +670,6 @@ static void lsi_request_cancelled(SCSIRequest *req) return; } - p = lsi_find_by_tag(s, req->tag); if (p) { QTAILQ_REMOVE(&s->queue, p, next); scsi_req_unref(req); @@ -680,18 +679,12 @@ static void lsi_request_cancelled(SCSIRequest *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_tag(LSIState *s, uint32_t tag, uint32_t len) +static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len) { - lsi_request *p; - - p = lsi_find_by_tag(s, tag); - if (!p) { - BADF("IO with unknown tag %d\n", tag); - return 1; - } + lsi_request *p = req->hba_private; if (p->pending) { - BADF("Multiple IO pending for tag %d\n", tag); + BADF("Multiple IO pending for request %p\n", p); } p->pending = len; /* Reselect if waiting for it, or if reselection triggers an IRQ @@ -743,9 +736,9 @@ static void lsi_transfer_data(SCSIRequest *req, uint32_t len) LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent); int out; - if (s->waiting == 1 || !s->current || req->tag != s->current->tag || + if (s->waiting == 1 || !s->current || req->hba_private != s->current || (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) { - if (lsi_queue_tag(s, req->tag, len)) { + if (lsi_queue_req(s, req, len)) { return; } } @@ -789,7 +782,8 @@ static void lsi_do_command(LSIState *s) assert(s->current == NULL); s->current = qemu_mallocz(sizeof(lsi_request)); s->current->tag = s->select_tag; - s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun); + s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun, + s->current); n = scsi_req_enqueue(s->current->req, buf); if (n) { diff --git a/hw/marvell_88w8618_audio.c b/hw/marvell_88w8618_audio.c index 3eff925b0e..f8c5242867 100644 --- a/hw/marvell_88w8618_audio.c +++ b/hw/marvell_88w8618_audio.c @@ -4,7 +4,7 @@ * * Copyright (c) 2008 Jan Kiszka * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "sysbus.h" #include "hw.h" diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index 1c9a706b1b..feb3b25acd 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -99,6 +99,7 @@ typedef struct RTCState { QEMUTimer *coalesced_timer; QEMUTimer *second_timer; QEMUTimer *second_timer2; + Notifier clock_reset_notifier; } RTCState; static void rtc_set_time(RTCState *s); @@ -572,6 +573,22 @@ static const VMStateDescription vmstate_rtc = { } }; +static void rtc_notify_clock_reset(Notifier *notifier, void *data) +{ + RTCState *s = container_of(notifier, RTCState, clock_reset_notifier); + int64_t now = *(int64_t *)data; + + rtc_set_date_from_host(&s->dev); + s->next_second_time = now + (get_ticks_per_sec() * 99) / 100; + qemu_mod_timer(s->second_timer2, s->next_second_time); + rtc_timer_update(s, now); +#ifdef TARGET_I386 + if (rtc_td_hack) { + rtc_coalesced_timer_update(s); + } +#endif +} + static void rtc_reset(void *opaque) { RTCState *s = opaque; @@ -608,6 +625,9 @@ static int rtc_initfn(ISADevice *dev) s->second_timer = qemu_new_timer_ns(rtc_clock, rtc_update_second, s); s->second_timer2 = qemu_new_timer_ns(rtc_clock, rtc_update_second2, s); + s->clock_reset_notifier.notify = rtc_notify_clock_reset; + qemu_register_clock_reset_notifier(rtc_clock, &s->clock_reset_notifier); + s->next_second_time = qemu_get_clock_ns(rtc_clock) + (get_ticks_per_sec() * 99) / 100; qemu_mod_timer(s->second_timer2, s->next_second_time); diff --git a/hw/mcf5206.c b/hw/mcf5206.c index 2a618d4446..fce282d98b 100644 --- a/hw/mcf5206.c +++ b/hw/mcf5206.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "mcf.h" diff --git a/hw/mcf5208.c b/hw/mcf5208.c index 17a692d4a3..78fbc5f232 100644 --- a/hw/mcf5208.c +++ b/hw/mcf5208.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "mcf.h" diff --git a/hw/mcf_fec.c b/hw/mcf_fec.c index 21035da345..748eb5906b 100644 --- a/hw/mcf_fec.c +++ b/hw/mcf_fec.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "net.h" @@ -471,7 +471,7 @@ void mcf_fec_init(NICInfo *nd, target_phys_addr_t base, qemu_irq *irq) DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x400, s->mmio_index); - memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); + s->conf.macaddr = nd->macaddr; s->conf.vlan = nd->vlan; s->conf.peer = nd->netdev; diff --git a/hw/mcf_intc.c b/hw/mcf_intc.c index ac04295198..6cb0a09b7f 100644 --- a/hw/mcf_intc.c +++ b/hw/mcf_intc.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "mcf.h" diff --git a/hw/mcf_uart.c b/hw/mcf_uart.c index db57096af2..905e116de6 100644 --- a/hw/mcf_uart.c +++ b/hw/mcf_uart.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "hw.h" #include "mcf.h" diff --git a/hw/milkymist-softusb.c b/hw/milkymist-softusb.c index 5ab35c3827..75c85aeb6f 100644 --- a/hw/milkymist-softusb.c +++ b/hw/milkymist-softusb.c @@ -234,11 +234,11 @@ static void softusb_usbdev_datain(void *opaque) USBPacket p; - p.pid = USB_TOKEN_IN; - p.devep = 1; - p.data = s->kbd_usb_buffer; - p.len = sizeof(s->kbd_usb_buffer); + usb_packet_init(&p); + usb_packet_setup(&p, USB_TOKEN_IN, 0, 1); + usb_packet_addbuf(&p, s->kbd_usb_buffer, sizeof(s->kbd_usb_buffer)); s->usbdev->info->handle_data(s->usbdev, &p); + usb_packet_cleanup(&p); softusb_kbd_changed(s); } @@ -247,16 +247,21 @@ static void softusb_attach(USBPort *port) { } -static void softusb_device_destroy(USBBus *bus, USBDevice *dev) +static void softusb_detach(USBPort *port) +{ +} + +static void softusb_child_detach(USBPort *port, USBDevice *child) { } static USBPortOps softusb_ops = { .attach = softusb_attach, + .detach = softusb_detach, + .child_detach = softusb_child_detach, }; static USBBusOps softusb_bus_ops = { - .device_destroy = softusb_device_destroy, }; static void milkymist_softusb_reset(DeviceState *d) @@ -305,10 +310,12 @@ static int milkymist_softusb_init(SysBusDevice *dev) usb_bus_new(&s->usbbus, &softusb_bus_ops, NULL); /* our two ports */ + /* FIXME: claim to support full speed devices. qemu mouse and keyboard + * report themselves as full speed devices. */ usb_register_port(&s->usbbus, &s->usbport[0], NULL, 0, &softusb_ops, - USB_SPEED_MASK_LOW); + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); usb_register_port(&s->usbbus, &s->usbport[1], NULL, 1, &softusb_ops, - USB_SPEED_MASK_LOW); + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); /* and finally create an usb keyboard */ s->usbdev = usb_create_simple(&s->usbbus, "usb-kbd"); @@ -8,10 +8,6 @@ PCIBus *gt64120_register(qemu_irq *pic); /* bonito.c */ PCIBus *bonito_init(qemu_irq *pic); -/* ds1225y.c */ -void *ds1225y_init(target_phys_addr_t mem_base, const char *filename); -void ds1225y_set_protection(void *opaque, int protection); - /* g364fb.c */ int g364fb_mm_init(target_phys_addr_t vram_base, target_phys_addr_t ctrl_base, int it_shift, diff --git a/hw/mips_jazz.c b/hw/mips_jazz.c index a1003945fd..f6ab6dc455 100644 --- a/hw/mips_jazz.c +++ b/hw/mips_jazz.c @@ -37,6 +37,7 @@ #include "loader.h" #include "mc146818rtc.h" #include "blockdev.h" +#include "sysbus.h" enum jazz_model_e { @@ -115,6 +116,8 @@ void mips_jazz_init (ram_addr_t ram_size, void* rc4030_opaque; int s_rtc, s_dma_dummy; NICInfo *nd; + DeviceState *dev; + SysBusDevice *sysbus; ISADevice *pit; DriveInfo *fds[MAX_FD]; qemu_irq esp_reset, dma_enable; @@ -266,8 +269,11 @@ void mips_jazz_init (ram_addr_t ram_size, /* FIXME: missing Jazz sound at 0x8000c000, rc4030[2] */ audio_init(i8259, NULL); - /* NVRAM: Unprotected at 0x9000, Protected at 0xa000, Read only at 0xb000 */ - ds1225y_init(0x80009000, "nvram"); + /* NVRAM */ + dev = qdev_create(NULL, "ds1225y"); + qdev_init_nofail(dev); + sysbus = sysbus_from_qdev(dev); + sysbus_mmio_map(sysbus, 0, 0x80009000); /* LED indicator */ jazz_led_init(0x8000f000); diff --git a/hw/mipsnet.c b/hw/mipsnet.c index 26aad51eab..0db3ba7a89 100644 --- a/hw/mipsnet.c +++ b/hw/mipsnet.c @@ -258,7 +258,7 @@ void mipsnet_init (int base, qemu_irq irq, NICInfo *nd) s->irq = irq; if (nd) { - memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); + s->conf.macaddr = nd->macaddr; s->conf.vlan = nd->vlan; s->conf.peer = nd->netdev; diff --git a/hw/mpcore.c b/hw/mpcore.c index 379065a3eb..d778507516 100644 --- a/hw/mpcore.c +++ b/hw/mpcore.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/msmouse.c b/hw/msmouse.c index 05f893ca93..67c6cd43e0 100644 --- a/hw/msmouse.c +++ b/hw/msmouse.c @@ -64,7 +64,7 @@ static void msmouse_chr_close (struct CharDriverState *chr) qemu_free (chr); } -CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts) +int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr) { CharDriverState *chr; @@ -74,5 +74,6 @@ CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts) qemu_add_mouse_event_handler(msmouse_event, chr, 0, "QEMU Microsoft Mouse"); - return chr; + *_chr = chr; + return 0; } diff --git a/hw/msmouse.h b/hw/msmouse.h index 456cb21424..8b853b35bf 100644 --- a/hw/msmouse.h +++ b/hw/msmouse.h @@ -1,2 +1,2 @@ /* msmouse.c */ -CharDriverState *qemu_chr_open_msmouse(QemuOpts *opts); +int qemu_chr_open_msmouse(QemuOpts *opts, CharDriverState **_chr); diff --git a/hw/multiboot.c b/hw/multiboot.c index 6e6cfb9531..a1d3f41293 100644 --- a/hw/multiboot.c +++ b/hw/multiboot.c @@ -97,11 +97,11 @@ typedef struct { static uint32_t mb_add_cmdline(MultibootState *s, const char *cmdline) { - int len = strlen(cmdline) + 1; target_phys_addr_t p = s->offset_cmdlines; + char *b = (char *)s->mb_buf + p; - pstrcpy((char *)s->mb_buf + p, len, cmdline); - s->offset_cmdlines += len; + get_opt_value(b, strlen(cmdline) + 1, cmdline); + s->offset_cmdlines += strlen(b) + 1; return s->mb_buf_phys + p; } @@ -198,11 +198,14 @@ int load_multiboot(void *fw_cfg, } else { /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */ uint32_t mh_header_addr = ldl_p(header+i+12); + uint32_t mh_load_end_addr = ldl_p(header+i+20); + uint32_t mh_bss_end_addr = ldl_p(header+i+24); mh_load_addr = ldl_p(header+i+16); uint32_t mb_kernel_text_offset = i - (mh_header_addr - mh_load_addr); + uint32_t mb_load_size = mh_load_end_addr - mh_load_addr; mh_entry_addr = ldl_p(header+i+28); - mb_kernel_size = kernel_file_size - mb_kernel_text_offset; + mb_kernel_size = mh_bss_end_addr - mh_load_addr; /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_VBE. uint32_t mh_mode_type = ldl_p(header+i+32); @@ -212,17 +215,18 @@ int load_multiboot(void *fw_cfg, mb_debug("multiboot: mh_header_addr = %#x\n", mh_header_addr); mb_debug("multiboot: mh_load_addr = %#x\n", mh_load_addr); - mb_debug("multiboot: mh_load_end_addr = %#x\n", ldl_p(header+i+20)); - mb_debug("multiboot: mh_bss_end_addr = %#x\n", ldl_p(header+i+24)); + mb_debug("multiboot: mh_load_end_addr = %#x\n", mh_load_end_addr); + mb_debug("multiboot: mh_bss_end_addr = %#x\n", mh_bss_end_addr); mb_debug("qemu: loading multiboot kernel (%#x bytes) at %#x\n", - mb_kernel_size, mh_load_addr); + mb_load_size, mh_load_addr); mbs.mb_buf = qemu_malloc(mb_kernel_size); fseek(f, mb_kernel_text_offset, SEEK_SET); - if (fread(mbs.mb_buf, 1, mb_kernel_size, f) != mb_kernel_size) { + if (fread(mbs.mb_buf, 1, mb_load_size, f) != mb_load_size) { fprintf(stderr, "fread() failed\n"); exit(1); } + memset(mbs.mb_buf + mb_load_size, 0, mb_kernel_size - mb_load_size); fclose(f); } @@ -238,7 +242,7 @@ int load_multiboot(void *fw_cfg, const char *r = initrd_filename; mbs.mb_buf_size += strlen(r) + 1; mbs.mb_mods_avail = 1; - while ((r = strchr(r, ','))) { + while (*(r = get_opt_value(NULL, 0, r))) { mbs.mb_mods_avail++; r++; } @@ -252,7 +256,7 @@ int load_multiboot(void *fw_cfg, mbs.offset_cmdlines = mbs.offset_mbinfo + mbs.mb_mods_avail * MB_MOD_SIZE; if (initrd_filename) { - char *next_initrd; + char *next_initrd, not_last; mbs.offset_mods = mbs.mb_buf_size; @@ -261,9 +265,9 @@ int load_multiboot(void *fw_cfg, int mb_mod_length; uint32_t offs = mbs.mb_buf_size; - next_initrd = strchr(initrd_filename, ','); - if (next_initrd) - *next_initrd = '\0'; + next_initrd = (char *)get_opt_value(NULL, 0, initrd_filename); + not_last = *next_initrd; + *next_initrd = '\0'; /* if a space comes after the module filename, treat everything after that as parameters */ target_phys_addr_t c = mb_add_cmdline(&mbs, initrd_filename); @@ -287,7 +291,7 @@ int load_multiboot(void *fw_cfg, (char *)mbs.mb_buf + offs, (char *)mbs.mb_buf + offs + mb_mod_length, c); initrd_filename = next_initrd+1; - } while (next_initrd); + } while (not_last); } /* Commandline support */ diff --git a/hw/musicpal.c b/hw/musicpal.c index 52b2931d15..63dd391176 100644 --- a/hw/musicpal.c +++ b/hw/musicpal.c @@ -3,7 +3,7 @@ * * Copyright (c) 2008 Jan Kiszka * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "sysbus.h" @@ -6,6 +6,10 @@ * 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. */ @@ -14,7 +18,7 @@ # include "hw.h" # include "flash.h" # include "blockdev.h" -/* FIXME: Pass block device as an argument. */ +# include "sysbus.h" # define NAND_CMD_READ0 0x00 # define NAND_CMD_READ1 0x01 @@ -44,8 +48,11 @@ # 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; @@ -58,18 +65,28 @@ struct NANDFlashState { uint8_t *ioaddr; int iolen; - uint32_t cmd, addr; + 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, uint32_t addr, int offset); + 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 @@ -201,8 +218,9 @@ static const struct { [0xc5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, }; -static void nand_reset(NANDFlashState *s) +static void nand_reset(DeviceState *dev) { + NANDFlashState *s = FROM_SYSBUS(NANDFlashState, sysbus_from_qdev(dev)); s->cmd = NAND_CMD_READ0; s->addr = 0; s->addrlen = 0; @@ -211,6 +229,14 @@ static void nand_reset(NANDFlashState *s) s->status &= NAND_IOSTATUS_UNPROTCT; } +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; @@ -220,15 +246,19 @@ static void nand_command(NANDFlashState *s) break; case NAND_CMD_READID: - s->io[0] = s->manf_id; - s->io[1] = s->chip_id; - s->io[2] = 'Q'; /* Don't-care byte (often 0xa5) */ - if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) - s->io[3] = 0x15; /* Page Size, Block Size, Spare Size.. */ - else - s->io[3] = 0xc0; /* Multi-plane */ s->ioaddr = s->io; - s->iolen = 4; + 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: @@ -244,7 +274,7 @@ static void nand_command(NANDFlashState *s) break; case NAND_CMD_RESET: - nand_reset(s); + nand_reset(&s->busdev.qdev); break; case NAND_CMD_PAGEPROGRAM1: @@ -273,9 +303,9 @@ static void nand_command(NANDFlashState *s) break; case NAND_CMD_READSTATUS: - s->io[0] = s->status; s->ioaddr = s->io; - s->iolen = 1; + s->iolen = 0; + nand_pushio_byte(s, s->status); break; default: @@ -304,9 +334,9 @@ static int nand_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_nand = { .name = "nand", - .version_id = 0, - .minimum_version_id = 0, - .minimum_version_id_old = 0, + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, .pre_save = nand_pre_save, .post_load = nand_post_load, .fields = (VMStateField[]) { @@ -319,7 +349,7 @@ static const VMStateDescription vmstate_nand = { VMSTATE_UINT32(ioaddr_vmstate, NANDFlashState), VMSTATE_INT32(iolen, NANDFlashState), VMSTATE_UINT32(cmd, NANDFlashState), - VMSTATE_UINT32(addr, NANDFlashState), + VMSTATE_UINT64(addr, NANDFlashState), VMSTATE_INT32(addrlen, NANDFlashState), VMSTATE_INT32(status, NANDFlashState), VMSTATE_INT32(offset, NANDFlashState), @@ -328,15 +358,85 @@ static const VMStateDescription vmstate_nand = { } }; +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: + hw_error("%s: Unsupported NAND block size.\n", __func__); + } + + pagesize = 1 << s->oob_shift; + s->mem_oob = 1; + if (s->bdrv && bdrv_getlength(s->bdrv) >= + (s->pages << s->page_shift) + (s->pages << s->oob_shift)) { + pagesize = 0; + s->mem_oob = 0; + } + + if (!s->bdrv) { + pagesize += 1 << s->page_shift; + } + if (pagesize) { + s->storage = (uint8_t *) memset(qemu_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 SysBusDeviceInfo nand_info = { + .init = nand_device_init, + .qdev.name = "nand", + .qdev.size = sizeof(NANDFlashState), + .qdev.reset = nand_reset, + .qdev.vmsd = &vmstate_nand, + .qdev.props = (Property[]) { + 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_create_device(void) +{ + sysbus_register_withprop(&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(NANDFlashState *s, uint8_t cle, uint8_t ale, +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; @@ -348,13 +448,15 @@ void nand_setpins(NANDFlashState *s, uint8_t cle, uint8_t ale, s->status &= ~NAND_IOSTATUS_UNPROTCT; } -void nand_getpins(NANDFlashState *s, int *rb) +void nand_getpins(DeviceState *dev, int *rb) { *rb = 1; } -void nand_setio(NANDFlashState *s, uint8_t value) +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) @@ -400,40 +502,69 @@ void nand_setio(NANDFlashState *s, uint8_t value) s->addr = (s->addr & mask) | v; s->addrlen ++; - if (s->addrlen == 1 && s->cmd == NAND_CMD_READID) - nand_command(s); - - if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && - s->addrlen == 3 && ( - s->cmd == NAND_CMD_READ0 || - s->cmd == NAND_CMD_PAGEPROGRAM1)) - nand_command(s); - if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && - s->addrlen == 4 && ( - s->cmd == NAND_CMD_READ0 || - s->cmd == NAND_CMD_PAGEPROGRAM1)) - nand_command(s); + 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)) - s->io[s->iolen ++] = value; + 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)) { - s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] = value; - s->addr ++; + for (i = s->buswidth; i--; s->addr++, value >>= 8) { + s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] = + (uint8_t) (value & 0xff); + } } } } -uint8_t nand_getio(NANDFlashState *s) +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 = (s->addr & ((1 << s->addr_shift) - 1)) + s->offset; + offset = (int) (s->addr & ((1 << s->addr_shift) - 1)) + s->offset; s->offset = 0; s->blk_load(s, s->addr, offset); @@ -446,129 +577,90 @@ uint8_t nand_getio(NANDFlashState *s) if (s->ce || s->iolen <= 0) return 0; - s->iolen --; - s->addr++; - return *(s->ioaddr ++); + 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; } -NANDFlashState *nand_init(int manf_id, int chip_id) +DeviceState *nand_init(BlockDriverState *bdrv, int manf_id, int chip_id) { - int pagesize; - NANDFlashState *s; - DriveInfo *dinfo; + DeviceState *dev; if (nand_flash_ids[chip_id].size == 0) { hw_error("%s: Unsupported NAND chip ID.\n", __FUNCTION__); } - - s = (NANDFlashState *) qemu_mallocz(sizeof(NANDFlashState)); - dinfo = drive_get(IF_MTD, 0, 0); - if (dinfo) - s->bdrv = dinfo->bdrv; - s->manf_id = manf_id; - s->chip_id = chip_id; - 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; + 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); } - 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: - hw_error("%s: Unsupported NAND block size.\n", __FUNCTION__); - } - - pagesize = 1 << s->oob_shift; - s->mem_oob = 1; - if (s->bdrv && bdrv_getlength(s->bdrv) >= - (s->pages << s->page_shift) + (s->pages << s->oob_shift)) { - pagesize = 0; - s->mem_oob = 0; - } - - if (!s->bdrv) - pagesize += 1 << s->page_shift; - if (pagesize) - s->storage = (uint8_t *) memset(qemu_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; - - vmstate_register(NULL, -1, &vmstate_nand, s); - - return s; + qdev_init_nofail(dev); + return dev; } -void nand_done(NANDFlashState *s) -{ - if (s->bdrv) { - bdrv_close(s->bdrv); - bdrv_delete(s->bdrv); - } - - if (!s->bdrv || s->mem_oob) - qemu_free(s->storage); - - qemu_free(s); -} +device_init(nand_create_device) #else /* Program a single page */ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s) { - uint32_t off, page, sector, soff; + uint64_t off, page, sector, soff; uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200]; if (PAGE(s->addr) >= s->pages) return; if (!s->bdrv) { - memcpy(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) + + 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) == -1) { - printf("%s: read error in sector %i\n", __FUNCTION__, sector); + printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); return; } - memcpy(iobuf + (soff | off), s->io, MIN(s->iolen, PAGE_SIZE - off)); + mem_and(iobuf + (soff | off), s->io, MIN(s->iolen, PAGE_SIZE - off)); if (off + s->iolen > PAGE_SIZE) { page = PAGE(s->addr); - memcpy(s->storage + (page << OOB_SHIFT), s->io + PAGE_SIZE - off, + 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) == -1) - printf("%s: write error in sector %i\n", __FUNCTION__, sector); + 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) == -1) { - printf("%s: read error in sector %i\n", __FUNCTION__, sector); + printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); return; } - memcpy(iobuf + soff, s->io, s->iolen); + mem_and(iobuf + soff, s->io, s->iolen); if (bdrv_write(s->bdrv, sector, iobuf, PAGE_SECTORS + 2) == -1) - printf("%s: write error in sector %i\n", __FUNCTION__, sector); + printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); } s->offset = 0; } @@ -576,7 +668,7 @@ static void glue(nand_blk_write_, PAGE_SIZE)(NANDFlashState *s) /* Erase a single block */ static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s) { - uint32_t i, page, addr; + uint64_t i, page, addr; uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, }; addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1); @@ -593,34 +685,35 @@ static void glue(nand_blk_erase_, PAGE_SIZE)(NANDFlashState *s) page = SECTOR(addr + (ADDR_SHIFT + s->erase_shift)); for (; i < page; i ++) if (bdrv_write(s->bdrv, i, iobuf, 1) == -1) - printf("%s: write error in sector %i\n", __FUNCTION__, i); + 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) == -1) - printf("%s: read error in sector %i\n", __FUNCTION__, page); + 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) == -1) - printf("%s: write error in sector %i\n", __FUNCTION__, page); + 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) == -1) - printf("%s: write error in sector %i\n", __FUNCTION__, i >> 9); + printf("%s: write error in sector %" PRIu64 "\n", + __func__, i >> 9); page = i >> 9; if (bdrv_read(s->bdrv, page, iobuf, 1) == -1) - printf("%s: read error in sector %i\n", __FUNCTION__, page); + 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) == -1) - printf("%s: write error in sector %i\n", __FUNCTION__, page); + printf("%s: write error in sector %" PRIu64 "\n", __func__, page); } } static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s, - uint32_t addr, int offset) + uint64_t addr, int offset) { if (PAGE(addr) >= s->pages) return; @@ -628,8 +721,8 @@ static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s, if (s->bdrv) { if (s->mem_oob) { if (bdrv_read(s->bdrv, SECTOR(addr), s->io, PAGE_SECTORS) == -1) - printf("%s: read error in sector %i\n", - __FUNCTION__, SECTOR(addr)); + 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); @@ -637,8 +730,8 @@ static void glue(nand_blk_load_, PAGE_SIZE)(NANDFlashState *s, } else { if (bdrv_read(s->bdrv, PAGE_START(addr) >> 9, s->io, (PAGE_SECTORS + 2)) == -1) - printf("%s: read error in sector %i\n", - __FUNCTION__, PAGE_START(addr) >> 9); + printf("%s: read error in sector %" PRIu64 "\n", + __func__, PAGE_START(addr) >> 9); s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset; } } else { diff --git a/hw/nseries.c b/hw/nseries.c index 2f84f5305b..6a5575e78e 100644 --- a/hw/nseries.c +++ b/hw/nseries.c @@ -31,6 +31,7 @@ #include "hw.h" #include "bt.h" #include "loader.h" +#include "blockdev.h" /* Nokia N8x0 support */ struct n800_s { @@ -45,7 +46,7 @@ struct n800_s { i2c_bus *i2c; int keymap[0x80]; - i2c_slave *kbd; + DeviceState *kbd; TUSBState *usb; void *retu; @@ -134,9 +135,9 @@ static void n800_mmc_cs_cb(void *opaque, int line, int level) static void n8x0_gpio_setup(struct n800_s *s) { qemu_irq *mmc_cs = qemu_allocate_irqs(n800_mmc_cs_cb, s->cpu->mmc, 1); - omap2_gpio_out_set(s->cpu->gpif, N8X0_MMC_CS_GPIO, mmc_cs[0]); + qdev_connect_gpio_out(s->cpu->gpio, N8X0_MMC_CS_GPIO, mmc_cs[0]); - qemu_irq_lower(omap2_gpio_in_get(s->cpu->gpif, N800_BAT_COVER_GPIO)[0]); + qemu_irq_lower(qdev_get_gpio_in(s->cpu->gpio, N800_BAT_COVER_GPIO)); } #define MAEMO_CAL_HEADER(...) \ @@ -163,13 +164,15 @@ static const uint8_t n8x0_cal_bt_id[] = { static void n8x0_nand_setup(struct n800_s *s) { char *otp_region; + DriveInfo *dinfo; - /* Either ec40xx or ec48xx are OK for the ID */ + dinfo = drive_get(IF_MTD, 0, 0); + /* Either 0x40 or 0x48 are OK for the device ID */ + s->nand = onenand_init(dinfo ? dinfo->bdrv : 0, + NAND_MFR_SAMSUNG, 0x48, 0, 1, + qdev_get_gpio_in(s->cpu->gpio, N8X0_ONENAND_GPIO)); omap_gpmc_attach(s->cpu->gpmc, N8X0_ONENAND_CS, 0, onenand_base_update, - onenand_base_unmap, - (s->nand = onenand_init(0xec4800, 1, - omap2_gpio_in_get(s->cpu->gpif, - N8X0_ONENAND_GPIO)[0]))); + onenand_base_unmap, s->nand); otp_region = onenand_raw_otp(s->nand); memcpy(otp_region + 0x000, n8x0_cal_wlan_mac, sizeof(n8x0_cal_wlan_mac)); @@ -180,7 +183,7 @@ static void n8x0_nand_setup(struct n800_s *s) static void n8x0_i2c_setup(struct n800_s *s) { DeviceState *dev; - qemu_irq tmp_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_TMP105_GPIO)[0]; + qemu_irq tmp_irq = qdev_get_gpio_in(s->cpu->gpio, N8X0_TMP105_GPIO); /* Attach the CPU on one end of our I2C bus. */ s->i2c = omap_i2c_bus(s->cpu->i2c[0]); @@ -249,8 +252,8 @@ static void n800_tsc_kbd_setup(struct n800_s *s) /* XXX: are the three pins inverted inside the chip between the * tsc and the cpu (N4111)? */ qemu_irq penirq = NULL; /* NC */ - qemu_irq kbirq = omap2_gpio_in_get(s->cpu->gpif, N800_TSC_KP_IRQ_GPIO)[0]; - qemu_irq dav = omap2_gpio_in_get(s->cpu->gpif, N800_TSC_TS_GPIO)[0]; + qemu_irq kbirq = qdev_get_gpio_in(s->cpu->gpio, N800_TSC_KP_IRQ_GPIO); + qemu_irq dav = qdev_get_gpio_in(s->cpu->gpio, N800_TSC_TS_GPIO); s->ts.chip = tsc2301_init(penirq, kbirq, dav); s->ts.opaque = s->ts.chip->opaque; @@ -269,7 +272,7 @@ static void n800_tsc_kbd_setup(struct n800_s *s) static void n810_tsc_setup(struct n800_s *s) { - qemu_irq pintdav = omap2_gpio_in_get(s->cpu->gpif, N810_TSC_TS_GPIO)[0]; + qemu_irq pintdav = qdev_get_gpio_in(s->cpu->gpio, N810_TSC_TS_GPIO); s->ts.opaque = tsc2005_init(pintdav); s->ts.txrx = tsc2005_txrx; @@ -361,8 +364,7 @@ static int n810_keys[0x80] = { static void n810_kbd_setup(struct n800_s *s) { - qemu_irq kbd_irq = omap2_gpio_in_get(s->cpu->gpif, N810_KEYBOARD_GPIO)[0]; - DeviceState *dev; + qemu_irq kbd_irq = qdev_get_gpio_in(s->cpu->gpio, N810_KEYBOARD_GPIO); int i; for (i = 0; i < 0x80; i ++) @@ -375,8 +377,8 @@ static void n810_kbd_setup(struct n800_s *s) /* Attach the LM8322 keyboard to the I2C bus, * should happen in n8x0_i2c_setup and s->kbd be initialised here. */ - dev = i2c_create_slave(s->i2c, "lm8323", N810_LM8323_ADDR); - qdev_connect_gpio_out(dev, 0, kbd_irq); + s->kbd = i2c_create_slave(s->i2c, "lm8323", N810_LM8323_ADDR); + qdev_connect_gpio_out(s->kbd, 0, kbd_irq); } /* LCD MIPI DBI-C controller (URAL) */ @@ -726,15 +728,15 @@ static void n8x0_dss_setup(struct n800_s *s) static void n8x0_cbus_setup(struct n800_s *s) { - qemu_irq dat_out = omap2_gpio_in_get(s->cpu->gpif, N8X0_CBUS_DAT_GPIO)[0]; - qemu_irq retu_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_RETU_GPIO)[0]; - qemu_irq tahvo_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_TAHVO_GPIO)[0]; + qemu_irq dat_out = qdev_get_gpio_in(s->cpu->gpio, N8X0_CBUS_DAT_GPIO); + qemu_irq retu_irq = qdev_get_gpio_in(s->cpu->gpio, N8X0_RETU_GPIO); + qemu_irq tahvo_irq = qdev_get_gpio_in(s->cpu->gpio, N8X0_TAHVO_GPIO); CBus *cbus = cbus_init(dat_out); - omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_CLK_GPIO, cbus->clk); - omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_DAT_GPIO, cbus->dat); - omap2_gpio_out_set(s->cpu->gpif, N8X0_CBUS_SEL_GPIO, cbus->sel); + qdev_connect_gpio_out(s->cpu->gpio, N8X0_CBUS_CLK_GPIO, cbus->clk); + qdev_connect_gpio_out(s->cpu->gpio, N8X0_CBUS_DAT_GPIO, cbus->dat); + qdev_connect_gpio_out(s->cpu->gpio, N8X0_CBUS_SEL_GPIO, cbus->sel); cbus_attach(cbus, s->retu = retu_init(retu_irq, 1)); cbus_attach(cbus, s->tahvo = tahvo_init(tahvo_irq, 1)); @@ -743,12 +745,11 @@ static void n8x0_cbus_setup(struct n800_s *s) static void n8x0_uart_setup(struct n800_s *s) { CharDriverState *radio = uart_hci_init( - omap2_gpio_in_get(s->cpu->gpif, - N8X0_BT_HOST_WKUP_GPIO)[0]); + qdev_get_gpio_in(s->cpu->gpio, N8X0_BT_HOST_WKUP_GPIO)); - omap2_gpio_out_set(s->cpu->gpif, N8X0_BT_RESET_GPIO, + qdev_connect_gpio_out(s->cpu->gpio, N8X0_BT_RESET_GPIO, csrhci_pins_get(radio)[csrhci_pin_reset]); - omap2_gpio_out_set(s->cpu->gpif, N8X0_BT_WKUP_GPIO, + qdev_connect_gpio_out(s->cpu->gpio, N8X0_BT_WKUP_GPIO, csrhci_pins_get(radio)[csrhci_pin_wakeup]); omap_uart_attach(s->cpu->uart[BT_UART], radio); @@ -763,7 +764,7 @@ static void n8x0_usb_power_cb(void *opaque, int line, int level) static void n8x0_usb_setup(struct n800_s *s) { - qemu_irq tusb_irq = omap2_gpio_in_get(s->cpu->gpif, N8X0_TUSB_INT_GPIO)[0]; + qemu_irq tusb_irq = qdev_get_gpio_in(s->cpu->gpio, N8X0_TUSB_INT_GPIO); qemu_irq tusb_pwr = qemu_allocate_irqs(n8x0_usb_power_cb, s, 1)[0]; TUSBState *tusb = tusb6010_init(tusb_irq); @@ -774,7 +775,7 @@ static void n8x0_usb_setup(struct n800_s *s) tusb6010_sync_io(tusb), NULL, NULL, tusb); s->usb = tusb; - omap2_gpio_out_set(s->cpu->gpif, N8X0_TUSB_ENABLE_GPIO, tusb_pwr); + qdev_connect_gpio_out(s->cpu->gpio, N8X0_TUSB_ENABLE_GPIO, tusb_pwr); } /* Setup done before the main bootloader starts by some early setup code @@ -1020,7 +1021,7 @@ static void n8x0_boot_init(void *opaque) /* If the machine has a slided keyboard, open it */ if (s->kbd) - qemu_irq_raise(omap2_gpio_in_get(s->cpu->gpif, N810_SLIDE_GPIO)[0]); + qemu_irq_raise(qdev_get_gpio_in(s->cpu->gpio, N810_SLIDE_GPIO)); } #define OMAP_TAG_NOKIA_BT 0x4e01 @@ -93,6 +93,8 @@ struct omap_target_agent_s *omap_l4ta_get( int cs); target_phys_addr_t omap_l4_attach(struct omap_target_agent_s *ta, int region, int iotype); +target_phys_addr_t omap_l4_region_base(struct omap_target_agent_s *ta, + int region); int l4_register_io_memory(CPUReadMemoryFunc * const *mem_read, CPUWriteMemoryFunc * const *mem_write, void *opaque); @@ -681,22 +683,6 @@ qemu_irq *omap_mpuio_in_get(struct omap_mpuio_s *s); void omap_mpuio_out_set(struct omap_mpuio_s *s, int line, qemu_irq handler); void omap_mpuio_key(struct omap_mpuio_s *s, int row, int col, int down); -/* omap1 gpio module interface */ -struct omap_gpio_s; -struct omap_gpio_s *omap_gpio_init(target_phys_addr_t base, - qemu_irq irq, omap_clk clk); -void omap_gpio_reset(struct omap_gpio_s *s); -qemu_irq *omap_gpio_in_get(struct omap_gpio_s *s); -void omap_gpio_out_set(struct omap_gpio_s *s, int line, qemu_irq handler); - -/* omap2 gpio interface */ -struct omap_gpif_s; -struct omap_gpif_s *omap2_gpio_init(struct omap_target_agent_s *ta, - qemu_irq *irq, omap_clk *fclk, omap_clk iclk, int modules); -void omap_gpif_reset(struct omap_gpif_s *s); -qemu_irq *omap2_gpio_in_get(struct omap_gpif_s *s, int start); -void omap2_gpio_out_set(struct omap_gpif_s *s, int line, qemu_irq handler); - struct uWireSlave { uint16_t (*receive)(void *opaque); void (*send)(void *opaque, uint16_t data); @@ -850,7 +836,7 @@ struct omap_mpu_state_s { /* MPUI-TIPB peripherals */ struct omap_uart_s *uart[3]; - struct omap_gpio_s *gpio; + DeviceState *gpio; struct omap_mcbsp_s *mcbsp1; struct omap_mcbsp_s *mcbsp3; @@ -948,8 +934,6 @@ struct omap_mpu_state_s { struct omap_gpmc_s *gpmc; struct omap_sysctl_s *sysc; - struct omap_gpif_s *gpif; - struct omap_mcspi_s *mcspi[2]; struct omap_dss_s *dss; diff --git a/hw/omap1.c b/hw/omap1.c index 364c26f877..400de475d9 100644 --- a/hw/omap1.c +++ b/hw/omap1.c @@ -27,6 +27,7 @@ #include "pc.h" #include "blockdev.h" #include "range.h" +#include "sysbus.h" /* Should signal the TCMI/GPMC */ uint32_t omap_badwidth_read8(void *opaque, target_phys_addr_t addr) @@ -3585,7 +3586,6 @@ static void omap1_mpu_reset(void *opaque) omap_uart_reset(mpu->uart[2]); omap_mmc_reset(mpu->mmc); omap_mpuio_reset(mpu->mpuio); - omap_gpio_reset(mpu->gpio); omap_uwire_reset(mpu->microwire); omap_pwl_reset(mpu); omap_pwt_reset(mpu); @@ -3845,8 +3845,12 @@ struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size, s->irq[1][OMAP_INT_KEYBOARD], s->irq[1][OMAP_INT_MPUIO], s->wakeup, omap_findclk(s, "clk32-kHz")); - s->gpio = omap_gpio_init(0xfffce000, s->irq[0][OMAP_INT_GPIO_BANK1], - omap_findclk(s, "arm_gpio_ck")); + s->gpio = qdev_create(NULL, "omap-gpio"); + qdev_prop_set_int32(s->gpio, "mpu_model", s->mpu_model); + qdev_init_nofail(s->gpio); + sysbus_connect_irq(sysbus_from_qdev(s->gpio), 0, + s->irq[0][OMAP_INT_GPIO_BANK1]); + sysbus_mmio_map(sysbus_from_qdev(s->gpio), 0, 0xfffce000); s->microwire = omap_uwire_init(0xfffb3000, &s->irq[1][OMAP_INT_uWireTX], s->drq[OMAP_DMA_UWIRE_TX], omap_findclk(s, "mpuper_ck")); diff --git a/hw/omap2.c b/hw/omap2.c index 0f13272c7b..c9b35405ed 100644 --- a/hw/omap2.c +++ b/hw/omap2.c @@ -27,6 +27,7 @@ #include "qemu-char.h" #include "flash.h" #include "soc_dma.h" +#include "sysbus.h" #include "audio/audio.h" /* Enhanced Audio Controller (CODEC only) */ @@ -2203,7 +2204,6 @@ static void omap2_mpu_reset(void *opaque) omap_uart_reset(mpu->uart[1]); omap_uart_reset(mpu->uart[2]); omap_mmc_reset(mpu->mmc); - omap_gpif_reset(mpu->gpif); omap_mcspi_reset(mpu->mcspi[0]); omap_mcspi_reset(mpu->mcspi[1]); omap_i2c_reset(mpu->i2c[0]); @@ -2232,9 +2232,10 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size, ram_addr_t sram_base, q2_base; qemu_irq *cpu_irq; qemu_irq dma_irqs[4]; - omap_clk gpio_clks[4]; DriveInfo *dinfo; int i; + SysBusDevice *busdev; + struct omap_target_agent_s *ta; /* Core */ s->mpu_model = omap2420; @@ -2377,13 +2378,28 @@ struct omap_mpu_state_s *omap2420_mpu_init(unsigned long sdram_size, omap_findclk(s, "i2c2.fclk"), omap_findclk(s, "i2c2.iclk")); - gpio_clks[0] = omap_findclk(s, "gpio1_dbclk"); - gpio_clks[1] = omap_findclk(s, "gpio2_dbclk"); - gpio_clks[2] = omap_findclk(s, "gpio3_dbclk"); - gpio_clks[3] = omap_findclk(s, "gpio4_dbclk"); - s->gpif = omap2_gpio_init(omap_l4ta(s->l4, 3), - &s->irq[0][OMAP_INT_24XX_GPIO_BANK1], - gpio_clks, omap_findclk(s, "gpio_iclk"), 4); + s->gpio = qdev_create(NULL, "omap2-gpio"); + qdev_prop_set_int32(s->gpio, "mpu_model", s->mpu_model); + qdev_prop_set_ptr(s->gpio, "iclk", omap_findclk(s, "gpio_iclk")); + qdev_prop_set_ptr(s->gpio, "fclk0", omap_findclk(s, "gpio1_dbclk")); + qdev_prop_set_ptr(s->gpio, "fclk1", omap_findclk(s, "gpio2_dbclk")); + qdev_prop_set_ptr(s->gpio, "fclk2", omap_findclk(s, "gpio3_dbclk")); + qdev_prop_set_ptr(s->gpio, "fclk3", omap_findclk(s, "gpio4_dbclk")); + if (s->mpu_model == omap2430) { + qdev_prop_set_ptr(s->gpio, "fclk4", omap_findclk(s, "gpio5_dbclk")); + } + qdev_init_nofail(s->gpio); + busdev = sysbus_from_qdev(s->gpio); + sysbus_connect_irq(busdev, 0, s->irq[0][OMAP_INT_24XX_GPIO_BANK1]); + sysbus_connect_irq(busdev, 3, s->irq[0][OMAP_INT_24XX_GPIO_BANK2]); + sysbus_connect_irq(busdev, 6, s->irq[0][OMAP_INT_24XX_GPIO_BANK3]); + sysbus_connect_irq(busdev, 9, s->irq[0][OMAP_INT_24XX_GPIO_BANK4]); + ta = omap_l4ta(s->l4, 3); + sysbus_mmio_map(busdev, 0, omap_l4_region_base(ta, 1)); + sysbus_mmio_map(busdev, 1, omap_l4_region_base(ta, 0)); + sysbus_mmio_map(busdev, 2, omap_l4_region_base(ta, 2)); + sysbus_mmio_map(busdev, 3, omap_l4_region_base(ta, 4)); + sysbus_mmio_map(busdev, 4, omap_l4_region_base(ta, 5)); s->sdrc = omap_sdrc_init(0x68009000); s->gpmc = omap_gpmc_init(0x6800a000, s->irq[0][OMAP_INT_24XX_GPMC_IRQ]); diff --git a/hw/omap_clk.c b/hw/omap_clk.c index 6bcabef8ac..577b326ae9 100644 --- a/hw/omap_clk.c +++ b/hw/omap_clk.c @@ -836,7 +836,7 @@ static struct clk i2c2_iclk = { .parent = &core_l4_iclk, }; -static struct clk gpio_dbclk[4] = { +static struct clk gpio_dbclk[5] = { { .name = "gpio1_dbclk", .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, @@ -853,6 +853,10 @@ static struct clk gpio_dbclk[4] = { .name = "gpio4_dbclk", .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, .parent = &wu_32k_clk, + }, { + .name = "gpio5_dbclk", + .flags = CLOCK_IN_OMAP243X, + .parent = &wu_32k_clk, }, }; diff --git a/hw/omap_gpio.c b/hw/omap_gpio.c index 478f7d9825..c23964c66d 100644 --- a/hw/omap_gpio.c +++ b/hw/omap_gpio.c @@ -20,10 +20,10 @@ #include "hw.h" #include "omap.h" -/* General-Purpose I/O */ +#include "sysbus.h" + struct omap_gpio_s { qemu_irq irq; - qemu_irq *in; qemu_irq handler[16]; uint16_t inputs; @@ -35,9 +35,17 @@ struct omap_gpio_s { uint16_t pins; }; +struct omap_gpif_s { + SysBusDevice busdev; + int mpu_model; + void *clk; + struct omap_gpio_s omap1; +}; + +/* General-Purpose I/O of OMAP1 */ static void omap_gpio_set(void *opaque, int line, int level) { - struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; + struct omap_gpio_s *s = &((struct omap_gpif_s *) opaque)->omap1; uint16_t prev = s->inputs; if (level) @@ -160,7 +168,7 @@ static CPUWriteMemoryFunc * const omap_gpio_writefn[] = { omap_badwidth_write16, }; -void omap_gpio_reset(struct omap_gpio_s *s) +static void omap_gpio_reset(struct omap_gpio_s *s) { s->inputs = 0; s->outputs = ~0; @@ -171,43 +179,12 @@ void omap_gpio_reset(struct omap_gpio_s *s) s->pins = ~0; } -struct omap_gpio_s *omap_gpio_init(target_phys_addr_t base, - qemu_irq irq, omap_clk clk) -{ - int iomemtype; - struct omap_gpio_s *s = (struct omap_gpio_s *) - qemu_mallocz(sizeof(struct omap_gpio_s)); - - s->irq = irq; - s->in = qemu_allocate_irqs(omap_gpio_set, s, 16); - omap_gpio_reset(s); - - iomemtype = cpu_register_io_memory(omap_gpio_readfn, - omap_gpio_writefn, s, DEVICE_NATIVE_ENDIAN); - cpu_register_physical_memory(base, 0x1000, iomemtype); - - return s; -} - -qemu_irq *omap_gpio_in_get(struct omap_gpio_s *s) -{ - return s->in; -} - -void omap_gpio_out_set(struct omap_gpio_s *s, int line, qemu_irq handler) -{ - if (line >= 16 || line < 0) - hw_error("%s: No GPIO line %i\n", __FUNCTION__, line); - s->handler[line] = handler; -} - -/* General-Purpose Interface of OMAP2 */ struct omap2_gpio_s { qemu_irq irq[2]; qemu_irq wkup; - qemu_irq *in; - qemu_irq handler[32]; + qemu_irq *handler; + uint8_t revision; uint8_t config[2]; uint32_t inputs; uint32_t outputs; @@ -221,8 +198,21 @@ struct omap2_gpio_s { uint8_t delay; }; +struct omap2_gpif_s { + SysBusDevice busdev; + int mpu_model; + void *iclk; + void *fclk[6]; + int modulecount; + struct omap2_gpio_s *modules; + qemu_irq *handler; + int autoidle; + int gpo; +}; + +/* General-Purpose Interface of OMAP2/3 */ static inline void omap2_gpio_module_int_update(struct omap2_gpio_s *s, - int line) + int line) { qemu_set_irq(s->irq[line], s->ints[line] & s->mask[line]); } @@ -269,10 +259,12 @@ static inline void omap2_gpio_module_int(struct omap2_gpio_s *s, int line) omap2_gpio_module_wake(s, line); } -static void omap2_gpio_module_set(void *opaque, int line, int level) +static void omap2_gpio_set(void *opaque, int line, int level) { - struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; + struct omap2_gpif_s *p = opaque; + struct omap2_gpio_s *s = &p->modules[line >> 5]; + line &= 31; if (level) { if (s->dir & (1 << line) & ((~s->inputs & s->edge[0]) | s->level[1])) omap2_gpio_module_int(s, line); @@ -308,7 +300,7 @@ static uint32_t omap2_gpio_module_read(void *opaque, target_phys_addr_t addr) switch (addr) { case 0x00: /* GPIO_REVISION */ - return 0x18; + return s->revision; case 0x10: /* GPIO_SYSCONFIG */ return s->config[0]; @@ -583,45 +575,28 @@ static CPUWriteMemoryFunc * const omap2_gpio_module_writefn[] = { omap2_gpio_module_write, }; -static void omap2_gpio_module_init(struct omap2_gpio_s *s, - struct omap_target_agent_s *ta, int region, - qemu_irq mpu, qemu_irq dsp, qemu_irq wkup, - omap_clk fclk, omap_clk iclk) +static void omap_gpif_reset(DeviceState *dev) { - int iomemtype; - - s->irq[0] = mpu; - s->irq[1] = dsp; - s->wkup = wkup; - s->in = qemu_allocate_irqs(omap2_gpio_module_set, s, 32); - - iomemtype = l4_register_io_memory(omap2_gpio_module_readfn, - omap2_gpio_module_writefn, s); - omap_l4_attach(ta, region, iomemtype); + struct omap_gpif_s *s = FROM_SYSBUS(struct omap_gpif_s, + sysbus_from_qdev(dev)); + omap_gpio_reset(&s->omap1); } -struct omap_gpif_s { - struct omap2_gpio_s module[5]; - int modules; - - int autoidle; - int gpo; -}; - -void omap_gpif_reset(struct omap_gpif_s *s) +static void omap2_gpif_reset(DeviceState *dev) { int i; - - for (i = 0; i < s->modules; i ++) - omap2_gpio_module_reset(s->module + i); - + struct omap2_gpif_s *s = FROM_SYSBUS(struct omap2_gpif_s, + sysbus_from_qdev(dev)); + for (i = 0; i < s->modulecount; i++) { + omap2_gpio_module_reset(&s->modules[i]); + } s->autoidle = 0; s->gpo = 0; } -static uint32_t omap_gpif_top_read(void *opaque, target_phys_addr_t addr) +static uint32_t omap2_gpif_top_read(void *opaque, target_phys_addr_t addr) { - struct omap_gpif_s *s = (struct omap_gpif_s *) opaque; + struct omap2_gpif_s *s = (struct omap2_gpif_s *) opaque; switch (addr) { case 0x00: /* IPGENERICOCPSPL_REVISION */ @@ -647,10 +622,10 @@ static uint32_t omap_gpif_top_read(void *opaque, target_phys_addr_t addr) return 0; } -static void omap_gpif_top_write(void *opaque, target_phys_addr_t addr, +static void omap2_gpif_top_write(void *opaque, target_phys_addr_t addr, uint32_t value) { - struct omap_gpif_s *s = (struct omap_gpif_s *) opaque; + struct omap2_gpif_s *s = (struct omap2_gpif_s *) opaque; switch (addr) { case 0x00: /* IPGENERICOCPSPL_REVISION */ @@ -662,7 +637,7 @@ static void omap_gpif_top_write(void *opaque, target_phys_addr_t addr, case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */ if (value & (1 << 1)) /* SOFTRESET */ - omap_gpif_reset(s); + omap2_gpif_reset(&s->busdev.qdev); s->autoidle = value & 1; break; @@ -676,50 +651,119 @@ static void omap_gpif_top_write(void *opaque, target_phys_addr_t addr, } } -static CPUReadMemoryFunc * const omap_gpif_top_readfn[] = { - omap_gpif_top_read, - omap_gpif_top_read, - omap_gpif_top_read, +static CPUReadMemoryFunc * const omap2_gpif_top_readfn[] = { + omap2_gpif_top_read, + omap2_gpif_top_read, + omap2_gpif_top_read, }; -static CPUWriteMemoryFunc * const omap_gpif_top_writefn[] = { - omap_gpif_top_write, - omap_gpif_top_write, - omap_gpif_top_write, +static CPUWriteMemoryFunc * const omap2_gpif_top_writefn[] = { + omap2_gpif_top_write, + omap2_gpif_top_write, + omap2_gpif_top_write, }; -struct omap_gpif_s *omap2_gpio_init(struct omap_target_agent_s *ta, - qemu_irq *irq, omap_clk *fclk, omap_clk iclk, int modules) +static int omap_gpio_init(SysBusDevice *dev) { - int iomemtype, i; - struct omap_gpif_s *s = (struct omap_gpif_s *) - qemu_mallocz(sizeof(struct omap_gpif_s)); - int region[4] = { 0, 2, 4, 5 }; + struct omap_gpif_s *s = FROM_SYSBUS(struct omap_gpif_s, dev); + if (!s->clk) { + hw_error("omap-gpio: clk not connected\n"); + } + qdev_init_gpio_in(&dev->qdev, omap_gpio_set, 16); + qdev_init_gpio_out(&dev->qdev, s->omap1.handler, 16); + sysbus_init_irq(dev, &s->omap1.irq); + sysbus_init_mmio(dev, 0x1000, + cpu_register_io_memory(omap_gpio_readfn, + omap_gpio_writefn, + &s->omap1, + DEVICE_NATIVE_ENDIAN)); + return 0; +} - s->modules = modules; - for (i = 0; i < modules; i ++) - omap2_gpio_module_init(s->module + i, ta, region[i], - irq[i], NULL, NULL, fclk[i], iclk); +static int omap2_gpio_init(SysBusDevice *dev) +{ + int i; + struct omap2_gpif_s *s = FROM_SYSBUS(struct omap2_gpif_s, dev); + if (!s->iclk) { + hw_error("omap2-gpio: iclk not connected\n"); + } + if (s->mpu_model < omap3430) { + s->modulecount = (s->mpu_model < omap2430) ? 4 : 5; + sysbus_init_mmio(dev, 0x1000, + cpu_register_io_memory(omap2_gpif_top_readfn, + omap2_gpif_top_writefn, s, + DEVICE_NATIVE_ENDIAN)); + } else { + s->modulecount = 6; + } + s->modules = qemu_mallocz(s->modulecount * sizeof(struct omap2_gpio_s)); + s->handler = qemu_mallocz(s->modulecount * 32 * sizeof(qemu_irq)); + qdev_init_gpio_in(&dev->qdev, omap2_gpio_set, s->modulecount * 32); + qdev_init_gpio_out(&dev->qdev, s->handler, s->modulecount * 32); + for (i = 0; i < s->modulecount; i++) { + struct omap2_gpio_s *m = &s->modules[i]; + if (!s->fclk[i]) { + hw_error("omap2-gpio: fclk%d not connected\n", i); + } + m->revision = (s->mpu_model < omap3430) ? 0x18 : 0x25; + m->handler = &s->handler[i * 32]; + sysbus_init_irq(dev, &m->irq[0]); /* mpu irq */ + sysbus_init_irq(dev, &m->irq[1]); /* dsp irq */ + sysbus_init_irq(dev, &m->wkup); + sysbus_init_mmio(dev, 0x1000, + cpu_register_io_memory(omap2_gpio_module_readfn, + omap2_gpio_module_writefn, + m, DEVICE_NATIVE_ENDIAN)); + } + return 0; +} - omap_gpif_reset(s); +/* Using qdev pointer properties for the clocks is not ideal. + * qdev should support a generic means of defining a 'port' with + * an arbitrary interface for connecting two devices. Then we + * could reframe the omap clock API in terms of clock ports, + * and get some type safety. For now the best qdev provides is + * passing an arbitrary pointer. + * (It's not possible to pass in the string which is the clock + * name, because this device does not have the necessary information + * (ie the struct omap_mpu_state_s*) to do the clockname to pointer + * translation.) + */ - iomemtype = l4_register_io_memory(omap_gpif_top_readfn, - omap_gpif_top_writefn, s); - omap_l4_attach(ta, 1, iomemtype); +static SysBusDeviceInfo omap_gpio_info = { + .init = omap_gpio_init, + .qdev.name = "omap-gpio", + .qdev.size = sizeof(struct omap_gpif_s), + .qdev.reset = omap_gpif_reset, + .qdev.props = (Property[]) { + DEFINE_PROP_INT32("mpu_model", struct omap_gpif_s, mpu_model, 0), + DEFINE_PROP_PTR("clk", struct omap_gpif_s, clk), + DEFINE_PROP_END_OF_LIST() + } +}; - return s; -} +static SysBusDeviceInfo omap2_gpio_info = { + .init = omap2_gpio_init, + .qdev.name = "omap2-gpio", + .qdev.size = sizeof(struct omap2_gpif_s), + .qdev.reset = omap2_gpif_reset, + .qdev.props = (Property[]) { + DEFINE_PROP_INT32("mpu_model", struct omap2_gpif_s, mpu_model, 0), + DEFINE_PROP_PTR("iclk", struct omap2_gpif_s, iclk), + DEFINE_PROP_PTR("fclk0", struct omap2_gpif_s, fclk[0]), + DEFINE_PROP_PTR("fclk1", struct omap2_gpif_s, fclk[1]), + DEFINE_PROP_PTR("fclk2", struct omap2_gpif_s, fclk[2]), + DEFINE_PROP_PTR("fclk3", struct omap2_gpif_s, fclk[3]), + DEFINE_PROP_PTR("fclk4", struct omap2_gpif_s, fclk[4]), + DEFINE_PROP_PTR("fclk5", struct omap2_gpif_s, fclk[5]), + DEFINE_PROP_END_OF_LIST() + } +}; -qemu_irq *omap2_gpio_in_get(struct omap_gpif_s *s, int start) +static void omap_gpio_register_device(void) { - if (start >= s->modules * 32 || start < 0) - hw_error("%s: No GPIO line %i\n", __FUNCTION__, start); - return s->module[start >> 5].in + (start & 31); + sysbus_register_withprop(&omap_gpio_info); + sysbus_register_withprop(&omap2_gpio_info); } -void omap2_gpio_out_set(struct omap_gpif_s *s, int line, qemu_irq handler) -{ - if (line >= s->modules * 32 || line < 0) - hw_error("%s: No GPIO line %i\n", __FUNCTION__, line); - s->module[line >> 5].handler[line & 31] = handler; -} +device_init(omap_gpio_register_device) diff --git a/hw/omap_l4.c b/hw/omap_l4.c index 4af0ca8ea6..59c84b19a2 100644 --- a/hw/omap_l4.c +++ b/hw/omap_l4.c @@ -146,6 +146,12 @@ struct omap_l4_s *omap_l4_init(target_phys_addr_t base, int ta_num) return bus; } +target_phys_addr_t omap_l4_region_base(struct omap_target_agent_s *ta, + int region) +{ + return ta->bus->base + ta->start[region].offset; +} + static uint32_t omap_l4ta_read(void *opaque, target_phys_addr_t addr) { struct omap_target_agent_s *s = (struct omap_target_agent_s *) opaque; diff --git a/hw/onenand.c b/hw/onenand.c index 71c1ab40b4..b0cbebc178 100644 --- a/hw/onenand.c +++ b/hw/onenand.c @@ -31,7 +31,11 @@ #define BLOCK_SHIFT (PAGE_SHIFT + 6) typedef struct { - uint32_t id; + struct { + uint16_t man; + uint16_t dev; + uint16_t ver; + } id; int shift; target_phys_addr_t base; qemu_irq intr; @@ -175,14 +179,39 @@ static inline int onenand_load_main(OneNANDState *s, int sec, int secn, static inline int onenand_prog_main(OneNANDState *s, int sec, int secn, void *src) { - if (s->bdrv_cur) - return bdrv_write(s->bdrv_cur, sec, src, secn) < 0; - else if (sec + secn > s->secs_cur) - return 1; - - memcpy(s->current + (sec << 9), src, secn << 9); + int result = 0; + + if (secn > 0) { + uint32_t size = (uint32_t) secn * 512; + const uint8_t *sp = (const uint8_t *) src; + uint8_t *dp = 0; + if (s->bdrv_cur) { + dp = qemu_malloc(size); + if (!dp || bdrv_read(s->bdrv_cur, sec, dp, secn) < 0) { + result = 1; + } + } else { + if (sec + secn > s->secs_cur) { + result = 1; + } else { + dp = (uint8_t *) s->current + (sec << 9); + } + } + if (!result) { + uint32_t i; + for (i = 0; i < size; i++) { + dp[i] &= sp[i]; + } + if (s->bdrv_cur) { + result = bdrv_write(s->bdrv_cur, sec, dp, secn) < 0; + } + } + if (dp && s->bdrv_cur) { + qemu_free(dp); + } + } - return 0; + return result; } static inline int onenand_load_spare(OneNANDState *s, int sec, int secn, @@ -205,35 +234,87 @@ static inline int onenand_load_spare(OneNANDState *s, int sec, int secn, static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn, void *src) { - uint8_t buf[512]; - - if (s->bdrv_cur) { - if (bdrv_read(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0) - return 1; - memcpy(buf + ((sec & 31) << 4), src, secn << 4); - return bdrv_write(s->bdrv_cur, s->secs_cur + (sec >> 5), buf, 1) < 0; - } else if (sec + secn > s->secs_cur) - return 1; - - memcpy(s->current + (s->secs_cur << 9) + (sec << 4), src, secn << 4); - - return 0; + int result = 0; + if (secn > 0) { + const uint8_t *sp = (const uint8_t *) src; + uint8_t *dp = 0, *dpp = 0; + if (s->bdrv_cur) { + dp = qemu_malloc(512); + if (!dp || bdrv_read(s->bdrv_cur, + s->secs_cur + (sec >> 5), + dp, 1) < 0) { + result = 1; + } else { + dpp = dp + ((sec & 31) << 4); + } + } else { + if (sec + secn > s->secs_cur) { + result = 1; + } else { + dpp = s->current + (s->secs_cur << 9) + (sec << 4); + } + } + if (!result) { + uint32_t i; + for (i = 0; i < (secn << 4); i++) { + dpp[i] &= sp[i]; + } + if (s->bdrv_cur) { + result = bdrv_write(s->bdrv_cur, s->secs_cur + (sec >> 5), + dp, 1) < 0; + } + } + if (dp) { + qemu_free(dp); + } + } + return result; } static inline int onenand_erase(OneNANDState *s, int sec, int num) { - /* TODO: optimise */ - uint8_t buf[512]; - - memset(buf, 0xff, sizeof(buf)); - for (; num > 0; num --, sec ++) { - if (onenand_prog_main(s, sec, 1, buf)) - return 1; - if (onenand_prog_spare(s, sec, 1, buf)) - return 1; + uint8_t *blankbuf, *tmpbuf; + blankbuf = qemu_malloc(512); + if (!blankbuf) { + return 1; + } + tmpbuf = qemu_malloc(512); + if (!tmpbuf) { + qemu_free(blankbuf); + return 1; + } + memset(blankbuf, 0xff, 512); + for (; num > 0; num--, sec++) { + if (s->bdrv_cur) { + int erasesec = s->secs_cur + (sec >> 5); + if (bdrv_write(s->bdrv_cur, sec, blankbuf, 1)) { + goto fail; + } + if (bdrv_read(s->bdrv_cur, erasesec, tmpbuf, 1) < 0) { + goto fail; + } + memcpy(tmpbuf + ((sec & 31) << 4), blankbuf, 1 << 4); + if (bdrv_write(s->bdrv_cur, erasesec, tmpbuf, 1) < 0) { + goto fail; + } + } else { + if (sec + 1 > s->secs_cur) { + goto fail; + } + memcpy(s->current + (sec << 9), blankbuf, 512); + memcpy(s->current + (s->secs_cur << 9) + (sec << 4), + blankbuf, 1 << 4); + } } + qemu_free(tmpbuf); + qemu_free(blankbuf); return 0; + +fail: + qemu_free(tmpbuf); + qemu_free(blankbuf); + return 1; } static void onenand_command(OneNANDState *s, int cmd) @@ -453,12 +534,12 @@ static uint32_t onenand_read(void *opaque, target_phys_addr_t addr) return lduw_le_p(s->boot[0] + addr); case 0xf000: /* Manufacturer ID */ - return (s->id >> 16) & 0xff; + return s->id.man; case 0xf001: /* Device ID */ - return (s->id >> 8) & 0xff; - /* TODO: get the following values from a real chip! */ + return s->id.dev; case 0xf002: /* Version ID */ - return (s->id >> 0) & 0xff; + return s->id.ver; + /* TODO: get the following values from a real chip! */ case 0xf003: /* Data Buffer size */ return 1 << PAGE_SHIFT; case 0xf004: /* Boot Buffer size */ @@ -541,8 +622,8 @@ static void onenand_write(void *opaque, target_phys_addr_t addr, case 0x0090: /* Read Identification Data */ memset(s->boot[0], 0, 3 << s->shift); - s->boot[0][0 << s->shift] = (s->id >> 16) & 0xff; - s->boot[0][1 << s->shift] = (s->id >> 8) & 0xff; + s->boot[0][0 << s->shift] = s->id.man & 0xff; + s->boot[0][1 << s->shift] = s->id.dev & 0xff; s->boot[0][2 << s->shift] = s->wpstatus & 0xff; break; @@ -615,28 +696,31 @@ static CPUWriteMemoryFunc * const onenand_writefn[] = { onenand_write, }; -void *onenand_init(uint32_t id, int regshift, qemu_irq irq) +void *onenand_init(BlockDriverState *bdrv, + uint16_t man_id, uint16_t dev_id, uint16_t ver_id, + int regshift, qemu_irq irq) { OneNANDState *s = (OneNANDState *) qemu_mallocz(sizeof(*s)); - DriveInfo *dinfo = drive_get(IF_MTD, 0, 0); - uint32_t size = 1 << (24 + ((id >> 12) & 7)); + uint32_t size = 1 << (24 + ((dev_id >> 4) & 7)); void *ram; s->shift = regshift; s->intr = irq; s->rdy = NULL; - s->id = id; + s->id.man = man_id; + s->id.dev = dev_id; + s->id.ver = ver_id; s->blocks = size >> BLOCK_SHIFT; s->secs = size >> 9; s->blockwp = qemu_malloc(s->blocks); - s->density_mask = (id & (1 << 11)) ? (1 << (6 + ((id >> 12) & 7))) : 0; + s->density_mask = (dev_id & 0x08) ? (1 << (6 + ((dev_id >> 4) & 7))) : 0; s->iomemtype = cpu_register_io_memory(onenand_readfn, onenand_writefn, s, DEVICE_NATIVE_ENDIAN); - if (!dinfo) + s->bdrv = bdrv; + if (!s->bdrv) { s->image = memset(qemu_malloc(size + (size >> 5)), 0xff, size + (size >> 5)); - else - s->bdrv = dinfo->bdrv; + } s->otp = memset(qemu_malloc((64 + 2) << PAGE_SHIFT), 0xff, (64 + 2) << PAGE_SHIFT); s->ram = qemu_ram_alloc(NULL, "onenand.ram", 0xc000 << s->shift); @@ -94,7 +94,7 @@ static void palmte_microwire_setup(struct omap_mpu_state_s *cpu) { uWireSlave *tsc; - tsc = tsc2102_init(omap_gpio_in_get(cpu->gpio)[PALMTE_PINTDAV_GPIO]); + tsc = tsc2102_init(qdev_get_gpio_in(cpu->gpio, PALMTE_PINTDAV_GPIO)); omap_uwire_attach(cpu->microwire, tsc, 0); omap_mcbsp_i2s_attach(cpu->mcbsp1, tsc210x_codec(tsc)); @@ -163,24 +163,24 @@ static void palmte_gpio_setup(struct omap_mpu_state_s *cpu) qemu_irq *misc_gpio; omap_mmc_handlers(cpu->mmc, - omap_gpio_in_get(cpu->gpio)[PALMTE_MMC_WP_GPIO], + qdev_get_gpio_in(cpu->gpio, PALMTE_MMC_WP_GPIO), qemu_irq_invert(omap_mpuio_in_get(cpu->mpuio) [PALMTE_MMC_SWITCH_GPIO])); misc_gpio = qemu_allocate_irqs(palmte_onoff_gpios, cpu, 7); - omap_gpio_out_set(cpu->gpio, PALMTE_MMC_POWER_GPIO, misc_gpio[0]); - omap_gpio_out_set(cpu->gpio, PALMTE_SPEAKER_GPIO, misc_gpio[1]); - omap_gpio_out_set(cpu->gpio, 11, misc_gpio[2]); - omap_gpio_out_set(cpu->gpio, 12, misc_gpio[3]); - omap_gpio_out_set(cpu->gpio, 13, misc_gpio[4]); - omap_mpuio_out_set(cpu->mpuio, 1, misc_gpio[5]); - omap_mpuio_out_set(cpu->mpuio, 3, misc_gpio[6]); + qdev_connect_gpio_out(cpu->gpio, PALMTE_MMC_POWER_GPIO, misc_gpio[0]); + qdev_connect_gpio_out(cpu->gpio, PALMTE_SPEAKER_GPIO, misc_gpio[1]); + qdev_connect_gpio_out(cpu->gpio, 11, misc_gpio[2]); + qdev_connect_gpio_out(cpu->gpio, 12, misc_gpio[3]); + qdev_connect_gpio_out(cpu->gpio, 13, misc_gpio[4]); + omap_mpuio_out_set(cpu->mpuio, 1, misc_gpio[5]); + omap_mpuio_out_set(cpu->mpuio, 3, misc_gpio[6]); /* Reset some inputs to initial state. */ - qemu_irq_lower(omap_gpio_in_get(cpu->gpio)[PALMTE_USBDETECT_GPIO]); - qemu_irq_lower(omap_gpio_in_get(cpu->gpio)[PALMTE_USB_OR_DC_GPIO]); - qemu_irq_lower(omap_gpio_in_get(cpu->gpio)[4]); - qemu_irq_lower(omap_gpio_in_get(cpu->gpio)[PALMTE_HEADPHONES_GPIO]); + qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_USBDETECT_GPIO)); + qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_USB_OR_DC_GPIO)); + qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, 4)); + qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_HEADPHONES_GPIO)); qemu_irq_lower(omap_mpuio_in_get(cpu->mpuio)[PALMTE_DC_GPIO]); qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[6]); qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[7]); @@ -41,6 +41,7 @@ #include "sysemu.h" #include "blockdev.h" #include "ui/qemu-spice.h" +#include "memory.h" /* output Bochs bios info messages */ //#define DEBUG_BIOS @@ -957,7 +958,8 @@ void pc_cpus_init(const char *cpu_model) } } -void pc_memory_init(const char *kernel_filename, +void pc_memory_init(MemoryRegion *system_memory, + const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, ram_addr_t below_4g_mem_size, @@ -965,22 +967,30 @@ void pc_memory_init(const char *kernel_filename, { char *filename; int ret, linux_boot, i; - ram_addr_t ram_addr, bios_offset, option_rom_offset; + MemoryRegion *ram, *bios, *isa_bios, *option_rom_mr; + MemoryRegion *ram_below_4g, *ram_above_4g; int bios_size, isa_bios_size; void *fw_cfg; linux_boot = (kernel_filename != NULL); - /* allocate RAM */ - ram_addr = qemu_ram_alloc(NULL, "pc.ram", - below_4g_mem_size + above_4g_mem_size); - cpu_register_physical_memory(0, 0xa0000, ram_addr); - cpu_register_physical_memory(0x100000, - below_4g_mem_size - 0x100000, - ram_addr + 0x100000); + /* Allocate RAM. We allocate it as a single memory region and use + * aliases to address portions of it, mostly for backwards compatiblity + * with older qemus that used qemu_ram_alloc(). + */ + ram = qemu_malloc(sizeof(*ram)); + memory_region_init_ram(ram, NULL, "pc.ram", + below_4g_mem_size + above_4g_mem_size); + ram_below_4g = qemu_malloc(sizeof(*ram_below_4g)); + memory_region_init_alias(ram_below_4g, "ram-below-4g", ram, + 0, below_4g_mem_size); + memory_region_add_subregion(system_memory, 0, ram_below_4g); if (above_4g_mem_size > 0) { - cpu_register_physical_memory(0x100000000ULL, above_4g_mem_size, - ram_addr + below_4g_mem_size); + ram_above_4g = qemu_malloc(sizeof(*ram_above_4g)); + memory_region_init_alias(ram_above_4g, "ram-above-4g", ram, + below_4g_mem_size, above_4g_mem_size); + memory_region_add_subregion(system_memory, 0x100000000ULL, + ram_above_4g); } /* BIOS load */ @@ -996,7 +1006,9 @@ void pc_memory_init(const char *kernel_filename, (bios_size % 65536) != 0) { goto bios_error; } - bios_offset = qemu_ram_alloc(NULL, "pc.bios", bios_size); + bios = qemu_malloc(sizeof(*bios)); + memory_region_init_ram(bios, NULL, "pc.bios", bios_size); + memory_region_set_readonly(bios, true); ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); if (ret != 0) { bios_error: @@ -1010,16 +1022,26 @@ void pc_memory_init(const char *kernel_filename, isa_bios_size = bios_size; if (isa_bios_size > (128 * 1024)) isa_bios_size = 128 * 1024; - cpu_register_physical_memory(0x100000 - isa_bios_size, - isa_bios_size, - (bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM); - - option_rom_offset = qemu_ram_alloc(NULL, "pc.rom", PC_ROM_SIZE); - cpu_register_physical_memory(PC_ROM_MIN_VGA, PC_ROM_SIZE, option_rom_offset); + isa_bios = qemu_malloc(sizeof(*isa_bios)); + memory_region_init_alias(isa_bios, "isa-bios", bios, + bios_size - isa_bios_size, isa_bios_size); + memory_region_add_subregion_overlap(system_memory, + 0x100000 - isa_bios_size, + isa_bios, + 1); + memory_region_set_readonly(isa_bios, true); + + option_rom_mr = qemu_malloc(sizeof(*option_rom_mr)); + memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE); + memory_region_add_subregion_overlap(system_memory, + PC_ROM_MIN_VGA, + option_rom_mr, + 1); /* map all the bios at the top of memory */ - cpu_register_physical_memory((uint32_t)(-bios_size), - bios_size, bios_offset | IO_MEM_ROM); + memory_region_add_subregion(system_memory, + (uint32_t)(-bios_size), + bios); fw_cfg = bochs_bios_init(); rom_set_fw(fw_cfg); @@ -2,10 +2,12 @@ #define HW_PC_H #include "qemu-common.h" +#include "memory.h" #include "ioport.h" #include "isa.h" #include "fdc.h" #include "net.h" +#include "memory.h" /* PC-style peripherals (also used by other machines). */ @@ -129,7 +131,8 @@ void pc_cmos_set_s3_resume(void *opaque, int irq, int level); void pc_acpi_smi_interrupt(void *opaque, int irq, int level); void pc_cpus_init(const char *cpu_model); -void pc_memory_init(const char *kernel_filename, +void pc_memory_init(MemoryRegion *system_memory, + const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, ram_addr_t below_4g_mem_size, @@ -175,7 +178,9 @@ int pcspk_audio_init(qemu_irq *pic); struct PCII440FXState; typedef struct PCII440FXState PCII440FXState; -PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn, qemu_irq *pic, ram_addr_t ram_size); +PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn, + qemu_irq *pic, MemoryRegion *address_space, + ram_addr_t ram_size); void i440fx_init_memory_mappings(PCII440FXState *d); /* piix4.c */ diff --git a/hw/pc_piix.c b/hw/pc_piix.c index c5c16b4571..c0a2abe4bc 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -39,6 +39,8 @@ #include "blockdev.h" #include "smbus.h" #include "xen.h" +#include "memory.h" +#include "exec-memory.h" #ifdef CONFIG_XEN # include <xen/hvm/hvm_info_table.h> #endif @@ -66,7 +68,8 @@ static void ioapic_init(IsaIrqState *isa_irq_state) } /* PC hardware initialisation */ -static void pc_init1(ram_addr_t ram_size, +static void pc_init1(MemoryRegion *system_memory, + ram_addr_t ram_size, const char *boot_device, const char *kernel_filename, const char *kernel_cmdline, @@ -106,7 +109,8 @@ static void pc_init1(ram_addr_t ram_size, /* allocate ram and load rom/bios */ if (!xen_enabled()) { - pc_memory_init(kernel_filename, kernel_cmdline, initrd_filename, + pc_memory_init(system_memory, + kernel_filename, kernel_cmdline, initrd_filename, below_4g_mem_size, above_4g_mem_size); } @@ -124,7 +128,8 @@ static void pc_init1(ram_addr_t ram_size, isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24); if (pci_enabled) { - pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq, ram_size); + pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq, + system_memory, ram_size); } else { pci_bus = NULL; i440fx_state = NULL; @@ -155,7 +160,11 @@ static void pc_init1(ram_addr_t ram_size, ide_drive_get(hd, MAX_IDE_BUS); if (pci_enabled) { PCIDevice *dev; - dev = pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1); + if (xen_enabled()) { + dev = pci_piix3_xen_ide_init(pci_bus, hd, piix3_devfn + 1); + } else { + dev = pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1); + } idebus[0] = qdev_get_child_bus(&dev->qdev, "ide.0"); idebus[1] = qdev_get_child_bus(&dev->qdev, "ide.1"); } else { @@ -208,7 +217,8 @@ static void pc_init_pci(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { - pc_init1(ram_size, boot_device, + pc_init1(get_system_memory(), + ram_size, boot_device, kernel_filename, kernel_cmdline, initrd_filename, cpu_model, 1, 1); } @@ -220,7 +230,8 @@ static void pc_init_pci_no_kvmclock(ram_addr_t ram_size, const char *initrd_filename, const char *cpu_model) { - pc_init1(ram_size, boot_device, + pc_init1(get_system_memory(), + ram_size, boot_device, kernel_filename, kernel_cmdline, initrd_filename, cpu_model, 1, 0); } @@ -234,7 +245,8 @@ static void pc_init_isa(ram_addr_t ram_size, { if (cpu_model == NULL) cpu_model = "486"; - pc_init1(ram_size, boot_device, + pc_init1(get_system_memory(), + ram_size, boot_device, kernel_filename, kernel_cmdline, initrd_filename, cpu_model, 0, 1); } @@ -263,11 +263,14 @@ int pci_find_domain(const PCIBus *bus) } void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, - const char *name, uint8_t devfn_min) + const char *name, + MemoryRegion *address_space, + uint8_t devfn_min) { qbus_create_inplace(&bus->qbus, &pci_bus_info, parent, name); assert(PCI_FUNC(devfn_min) == 0); bus->devfn_min = devfn_min; + bus->address_space = address_space; /* host bridge */ QLIST_INIT(&bus->child); @@ -276,13 +279,14 @@ void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, vmstate_register(NULL, -1, &vmstate_pcibus, bus); } -PCIBus *pci_bus_new(DeviceState *parent, const char *name, uint8_t devfn_min) +PCIBus *pci_bus_new(DeviceState *parent, const char *name, + MemoryRegion *address_space, uint8_t devfn_min) { PCIBus *bus; bus = qemu_mallocz(sizeof(*bus)); bus->qbus.qdev_allocated = 1; - pci_bus_new_inplace(bus, parent, name, devfn_min); + pci_bus_new_inplace(bus, parent, name, address_space, devfn_min); return bus; } @@ -310,11 +314,13 @@ void pci_bus_set_mem_base(PCIBus *bus, target_phys_addr_t base) PCIBus *pci_register_bus(DeviceState *parent, const char *name, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, - void *irq_opaque, uint8_t devfn_min, int nirq) + void *irq_opaque, + MemoryRegion *address_space, + uint8_t devfn_min, int nirq) { PCIBus *bus; - bus = pci_bus_new(parent, name, devfn_min); + bus = pci_bus_new(parent, name, address_space, devfn_min); pci_bus_irqs(bus, set_irq, map_irq, irq_opaque, nirq); return bus; } @@ -838,10 +844,15 @@ static void pci_unregister_io_regions(PCIDevice *pci_dev) if (r->type == PCI_BASE_ADDRESS_SPACE_IO) { isa_unassign_ioport(r->addr, r->filtered_size); } else { - cpu_register_physical_memory(pci_to_cpu_addr(pci_dev->bus, - r->addr), - r->filtered_size, - IO_MEM_UNASSIGNED); + if (r->memory) { + memory_region_del_subregion(pci_dev->bus->address_space, + r->memory); + } else { + cpu_register_physical_memory(pci_to_cpu_addr(pci_dev->bus, + r->addr), + r->filtered_size, + IO_MEM_UNASSIGNED); + } } } } @@ -887,6 +898,7 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, r->type = type; r->map_func = map_func; r->ram_addr = IO_MEM_UNASSIGNED; + r->memory = NULL; wmask = ~(size - 1); addr = pci_bar(pci_dev, region_num); @@ -912,6 +924,16 @@ static void pci_simple_bar_mapfunc(PCIDevice *pci_dev, int region_num, pci_dev->io_regions[region_num].ram_addr); } +static void pci_simple_bar_mapfunc_region(PCIDevice *pci_dev, int region_num, + pcibus_t addr, pcibus_t size, + int type) +{ + memory_region_add_subregion_overlap(pci_dev->bus->address_space, + addr, + pci_dev->io_regions[region_num].memory, + 1); +} + void pci_register_bar_simple(PCIDevice *pci_dev, int region_num, pcibus_t size, uint8_t attr, ram_addr_t ram_addr) { @@ -921,6 +943,15 @@ void pci_register_bar_simple(PCIDevice *pci_dev, int region_num, pci_dev->io_regions[region_num].ram_addr = ram_addr; } +void pci_register_bar_region(PCIDevice *pci_dev, int region_num, + uint8_t attr, MemoryRegion *memory) +{ + pci_register_bar(pci_dev, region_num, memory_region_size(memory), + PCI_BASE_ADDRESS_SPACE_MEMORY | attr, + pci_simple_bar_mapfunc_region); + pci_dev->io_regions[region_num].memory = memory; +} + static void pci_bridge_filter(PCIDevice *d, pcibus_t *addr, pcibus_t *size, uint8_t type) { @@ -1059,10 +1090,16 @@ static void pci_update_mappings(PCIDevice *d) isa_unassign_ioport(r->addr, r->filtered_size); } } else { - cpu_register_physical_memory(pci_to_cpu_addr(d->bus, r->addr), - r->filtered_size, - IO_MEM_UNASSIGNED); - qemu_unregister_coalesced_mmio(r->addr, r->filtered_size); + if (r->memory) { + memory_region_del_subregion(d->bus->address_space, + r->memory); + } else { + cpu_register_physical_memory(pci_to_cpu_addr(d->bus, + r->addr), + r->filtered_size, + IO_MEM_UNASSIGNED); + qemu_unregister_coalesced_mmio(r->addr, r->filtered_size); + } } } r->addr = new_addr; @@ -5,6 +5,7 @@ #include "qobject.h" #include "qdev.h" +#include "memory.h" /* PCI includes legacy ISA access. */ #include "isa.h" @@ -93,6 +94,7 @@ typedef struct PCIIORegion { uint8_t type; PCIMapIORegionFunc *map_func; ram_addr_t ram_addr; + MemoryRegion *memory; } PCIIORegion; #define PCI_ROM_SLOT 6 @@ -203,6 +205,8 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, PCIMapIORegionFunc *map_func); void pci_register_bar_simple(PCIDevice *pci_dev, int region_num, pcibus_t size, uint8_t attr, ram_addr_t ram_addr); +void pci_register_bar_region(PCIDevice *pci_dev, int region_num, + uint8_t attr, MemoryRegion *memory); int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t offset, uint8_t size); @@ -233,15 +237,20 @@ typedef enum { typedef int (*pci_hotplug_fn)(DeviceState *qdev, PCIDevice *pci_dev, PCIHotplugState state); void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, - const char *name, uint8_t devfn_min); -PCIBus *pci_bus_new(DeviceState *parent, const char *name, uint8_t devfn_min); + const char *name, + MemoryRegion *address_space, + uint8_t devfn_min); +PCIBus *pci_bus_new(DeviceState *parent, const char *name, + MemoryRegion *address_space, uint8_t devfn_min); void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, void *irq_opaque, int nirq); int pci_bus_get_irq_level(PCIBus *bus, int irq_num); void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *dev); PCIBus *pci_register_bus(DeviceState *parent, const char *name, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, - void *irq_opaque, uint8_t devfn_min, int nirq); + void *irq_opaque, + MemoryRegion *address_space, + uint8_t devfn_min, int nirq); void pci_device_reset(PCIDevice *dev); void pci_bus_reset(PCIBus *bus); diff --git a/hw/pci_host.h b/hw/pci_host.h index c8390eec56..7f551143bb 100644 --- a/hw/pci_host.h +++ b/hw/pci_host.h @@ -35,6 +35,7 @@ struct PCIHostState { SysBusDevice busdev; ReadWriteHandler conf_handler; ReadWriteHandler data_handler; + MemoryRegion *address_space; uint32_t config_reg; PCIBus *bus; }; diff --git a/hw/pci_ids.h b/hw/pci_ids.h index b49c602844..83f38934ec 100644 --- a/hw/pci_ids.h +++ b/hw/pci_ids.h @@ -109,6 +109,14 @@ #define PCI_DEVICE_ID_INTEL_82371AB 0x7111 #define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 #define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI1 0x2934 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI2 0x2935 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI3 0x2936 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI4 0x2937 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI5 0x2938 +#define PCI_DEVICE_ID_INTEL_82801I_UHCI6 0x2939 +#define PCI_DEVICE_ID_INTEL_82801I_EHCI1 0x293a +#define PCI_DEVICE_ID_INTEL_82801I_EHCI2 0x293c #define PCI_VENDOR_ID_XEN 0x5853 #define PCI_DEVICE_ID_XEN_PLATFORM 0x0001 diff --git a/hw/pci_internals.h b/hw/pci_internals.h index fbe1866808..c3a463a703 100644 --- a/hw/pci_internals.h +++ b/hw/pci_internals.h @@ -25,6 +25,7 @@ struct PCIBus { PCIDevice *devices[PCI_SLOT_MAX * PCI_FUNC_MAX]; PCIDevice *parent_dev; target_phys_addr_t mem_base; + MemoryRegion *address_space; QLIST_HEAD(, PCIBus) child; /* this will be replaced by qdev later */ QLIST_ENTRY(PCIBus) sibling;/* this will be replaced by qdev later */ diff --git a/hw/piix_pci.c b/hw/piix_pci.c index d08b31a266..80d6665350 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -241,7 +241,9 @@ static int i440fx_initfn(PCIDevice *dev) static PCIBus *i440fx_common_init(const char *device_name, PCII440FXState **pi440fx_state, int *piix3_devfn, - qemu_irq *pic, ram_addr_t ram_size) + qemu_irq *pic, + MemoryRegion *address_space, + ram_addr_t ram_size) { DeviceState *dev; PCIBus *b; @@ -251,7 +253,8 @@ static PCIBus *i440fx_common_init(const char *device_name, dev = qdev_create(NULL, "i440FX-pcihost"); s = FROM_SYSBUS(I440FXState, sysbus_from_qdev(dev)); - b = pci_bus_new(&s->busdev.qdev, NULL, 0); + s->address_space = address_space; + b = pci_bus_new(&s->busdev.qdev, NULL, s->address_space, 0); s->bus = b; qdev_init_nofail(dev); @@ -288,11 +291,13 @@ static PCIBus *i440fx_common_init(const char *device_name, } PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, - qemu_irq *pic, ram_addr_t ram_size) + qemu_irq *pic, MemoryRegion *address_space, + ram_addr_t ram_size) { PCIBus *b; - b = i440fx_common_init("i440FX", pi440fx_state, piix3_devfn, pic, ram_size); + b = i440fx_common_init("i440FX", pi440fx_state, piix3_devfn, pic, + address_space, ram_size); return b; } diff --git a/hw/pl011.c b/hw/pl011.c index 3b94b14cb9..997ce848f8 100644 --- a/hw/pl011.c +++ b/hw/pl011.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pl022.c b/hw/pl022.c index 00e494a0de..9a1cb710f3 100644 --- a/hw/pl022.c +++ b/hw/pl022.c @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pl050.c b/hw/pl050.c index b155cc07b6..f7fa2e253c 100644 --- a/hw/pl050.c +++ b/hw/pl050.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pl061.c b/hw/pl061.c index 372dfc2da2..79e5c53e89 100644 --- a/hw/pl061.c +++ b/hw/pl061.c @@ -5,7 +5,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pl080.c b/hw/pl080.c index dd8139ba96..5ba3b0859b 100644 --- a/hw/pl080.c +++ b/hw/pl080.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pl110.c b/hw/pl110.c index 06d2dfada6..62aba17ad4 100644 --- a/hw/pl110.c +++ b/hw/pl110.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU LGPL + * This code is licensed under the GNU LGPL */ #include "sysbus.h" diff --git a/hw/pl110_template.h b/hw/pl110_template.h index b3c9077dcc..d303336786 100644 --- a/hw/pl110_template.h +++ b/hw/pl110_template.h @@ -4,7 +4,7 @@ * Copyright (c) 2005 CodeSourcery, LLC. * Written by Paul Brook * - * This code is licenced under the GNU LGPL + * This code is licensed under the GNU LGPL * * Framebuffer format conversion routines. */ diff --git a/hw/pl181.c b/hw/pl181.c index 6bc79f5f7a..0943c09eca 100644 --- a/hw/pl181.c +++ b/hw/pl181.c @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "blockdev.h" diff --git a/hw/pl190.c b/hw/pl190.c index 75f2ba1966..8dc7e42861 100644 --- a/hw/pl190.c +++ b/hw/pl190.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/ppc4xx_pci.c b/hw/ppc4xx_pci.c index 299473c4b5..15c24f6e7a 100644 --- a/hw/ppc4xx_pci.c +++ b/hw/ppc4xx_pci.c @@ -24,6 +24,7 @@ #include "ppc4xx.h" #include "pci.h" #include "pci_host.h" +#include "exec-memory.h" #undef DEBUG #ifdef DEBUG @@ -345,7 +346,9 @@ PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4], controller->pci_state.bus = pci_register_bus(NULL, "pci", ppc4xx_pci_set_irq, ppc4xx_pci_map_irq, - pci_irqs, 0, 4); + pci_irqs, + get_system_memory(), + 0, 4); controller->pci_dev = pci_register_device(controller->pci_state.bus, "host bridge", sizeof(PCIDevice), diff --git a/hw/ppc_mac.h b/hw/ppc_mac.h index 68dade7e40..6fad20a745 100644 --- a/hw/ppc_mac.h +++ b/hw/ppc_mac.h @@ -25,6 +25,8 @@ #if !defined(__PPC_MAC_H__) #define __PPC_MAC_H__ +#include "memory.h" + /* SMP is not enabled, for now */ #define MAX_CPUS 1 @@ -52,11 +54,12 @@ qemu_irq *heathrow_pic_init(int *pmem_index, int nb_cpus, qemu_irq **irqs); /* Grackle PCI */ -PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic); +PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, + MemoryRegion *address_space); /* UniNorth PCI */ -PCIBus *pci_pmac_init(qemu_irq *pic); -PCIBus *pci_pmac_u3_init(qemu_irq *pic); +PCIBus *pci_pmac_init(qemu_irq *pic, MemoryRegion *address_space); +PCIBus *pci_pmac_u3_init(qemu_irq *pic, MemoryRegion *address_space); /* Mac NVRAM */ typedef struct MacIONVRAMState MacIONVRAMState; diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c index 5bce709bab..2c0fae8ef3 100644 --- a/hw/ppc_newworld.c +++ b/hw/ppc_newworld.c @@ -67,6 +67,7 @@ #include "kvm_ppc.h" #include "hw/usb.h" #include "blockdev.h" +#include "exec-memory.h" #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 @@ -317,10 +318,10 @@ static void ppc_core99_init (ram_addr_t ram_size, pic = openpic_init(NULL, &pic_mem_index, smp_cpus, openpic_irqs, NULL); if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) { /* 970 gets a U3 bus */ - pci_bus = pci_pmac_u3_init(pic); + pci_bus = pci_pmac_u3_init(pic, get_system_memory()); machine_arch = ARCH_MAC99_U3; } else { - pci_bus = pci_pmac_init(pic); + pci_bus = pci_pmac_init(pic, get_system_memory()); machine_arch = ARCH_MAC99; } /* init basic PC hardware */ diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c index 20cd8e1a8d..585afd6c4b 100644 --- a/hw/ppc_oldworld.c +++ b/hw/ppc_oldworld.c @@ -43,6 +43,7 @@ #include "kvm.h" #include "kvm_ppc.h" #include "blockdev.h" +#include "exec-memory.h" #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 @@ -233,7 +234,7 @@ static void ppc_heathrow_init (ram_addr_t ram_size, hw_error("Only 6xx bus is supported on heathrow machine\n"); } pic = heathrow_pic_init(&pic_mem_index, 1, heathrow_irqs); - pci_bus = pci_grackle_init(0xfec00000, pic); + pci_bus = pci_grackle_init(0xfec00000, pic, get_system_memory()); pci_vga_init(pci_bus); escc_mem_index = escc_init(0x80013000, pic[0x0f], pic[0x10], serial_hds[0], diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c index 0e9cfc24cd..91ebe07dcd 100644 --- a/hw/ppc_prep.c +++ b/hw/ppc_prep.c @@ -38,6 +38,7 @@ #include "loader.h" #include "mc146818rtc.h" #include "blockdev.h" +#include "exec-memory.h" //#define HARD_DEBUG_PPC_IO //#define DEBUG_PPC_IO @@ -648,7 +649,7 @@ static void ppc_prep_init (ram_addr_t ram_size, hw_error("Only 6xx bus is supported on PREP machine\n"); } i8259 = i8259_init(first_cpu->irq_inputs[PPC6xx_INPUT_INT]); - pci_bus = pci_prep_init(i8259); + pci_bus = pci_prep_init(i8259, get_system_memory()); /* Hmm, prep has no pci-isa bridge ??? */ isa_bus_new(NULL); isa_bus_irqs(i8259); diff --git a/hw/ppce500_pci.c b/hw/ppce500_pci.c index fc11af4374..134453965b 100644 --- a/hw/ppce500_pci.c +++ b/hw/ppce500_pci.c @@ -274,12 +274,15 @@ static void e500_pci_map(SysBusDevice *dev, target_phys_addr_t base) s->reg); } +#include "exec-memory.h" + static int e500_pcihost_initfn(SysBusDevice *dev) { PCIHostState *h; PPCE500PCIState *s; PCIBus *b; int i; + MemoryRegion *address_space = get_system_memory(); h = FROM_SYSBUS(PCIHostState, sysbus_from_qdev(dev)); s = DO_UPCAST(PPCE500PCIState, pci_state, h); @@ -289,7 +292,8 @@ static int e500_pcihost_initfn(SysBusDevice *dev) } b = pci_register_bus(&s->pci_state.busdev.qdev, NULL, mpc85xx_pci_set_irq, - mpc85xx_pci_map_irq, s->irq, PCI_DEVFN(0x11, 0), 4); + mpc85xx_pci_map_irq, s->irq, address_space, + PCI_DEVFN(0x11, 0), 4); s->pci_state.bus = b; pci_create_simple(b, 0, "e500-host-bridge"); diff --git a/hw/prep_pci.c b/hw/prep_pci.c index f88b8254c2..da02f0ea8e 100644 --- a/hw/prep_pci.c +++ b/hw/prep_pci.c @@ -110,7 +110,7 @@ static void prep_set_irq(void *opaque, int irq_num, int level) qemu_set_irq(pic[(irq_num & 1) ? 11 : 9] , level); } -PCIBus *pci_prep_init(qemu_irq *pic) +PCIBus *pci_prep_init(qemu_irq *pic, MemoryRegion *address_space) { PREPPCIState *s; PCIDevice *d; @@ -118,7 +118,8 @@ PCIBus *pci_prep_init(qemu_irq *pic) s = qemu_mallocz(sizeof(PREPPCIState)); s->bus = pci_register_bus(NULL, "pci", - prep_set_irq, prep_map_irq, pic, 0, 4); + prep_set_irq, prep_map_irq, pic, + address_space, 0, 4); pci_host_conf_register_ioport(0xcf8, s); diff --git a/hw/prep_pci.h b/hw/prep_pci.h index cd6851288c..a27368b99b 100644 --- a/hw/prep_pci.h +++ b/hw/prep_pci.h @@ -2,7 +2,8 @@ #define QEMU_PREP_PCI_H #include "qemu-common.h" +#include "memory.h" -PCIBus *pci_prep_init(qemu_irq *pic); +PCIBus *pci_prep_init(qemu_irq *pic, MemoryRegion *address_space); #endif diff --git a/hw/ptimer.c b/hw/ptimer.c index 47964a67e1..6f13ce92fc 100644 --- a/hw/ptimer.c +++ b/hw/ptimer.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 CodeSourcery. * - * This code is licenced under the GNU LGPL. + * This code is licensed under the GNU LGPL. */ #include "hw.h" #include "qemu-timer.h" @@ -4,7 +4,7 @@ * Copyright (c) 2006 Openedhand Ltd. * Written by Andrzej Zaborowski <balrog@zabor.org> * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #ifndef PXA_H # define PXA_H "pxa.h" diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c index ac5d95d718..cf9311014d 100644 --- a/hw/pxa2xx.c +++ b/hw/pxa2xx.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 Openedhand Ltd. * Written by Andrzej Zaborowski <balrog@zabor.org> * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/pxa2xx_dma.c b/hw/pxa2xx_dma.c index a67498b2bc..599581e266 100644 --- a/hw/pxa2xx_dma.c +++ b/hw/pxa2xx_dma.c @@ -5,7 +5,7 @@ * Copyright (c) 2006 Thorsten Zitterell * Written by Andrzej Zaborowski <balrog@zabor.org> * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" diff --git a/hw/pxa2xx_pic.c b/hw/pxa2xx_pic.c index e9a536102b..bdd82e6bf2 100644 --- a/hw/pxa2xx_pic.c +++ b/hw/pxa2xx_pic.c @@ -5,7 +5,7 @@ * Copyright (c) 2006 Thorsten Zitterell * Written by Andrzej Zaborowski <balrog@zabor.org> * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" diff --git a/hw/pxa2xx_timer.c b/hw/pxa2xx_timer.c index f777a21226..4235e42639 100644 --- a/hw/pxa2xx_timer.c +++ b/hw/pxa2xx_timer.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 Openedhand Ltd. * Copyright (c) 2006 Thorsten Zitterell * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" @@ -289,6 +289,9 @@ int qdev_init(DeviceState *dev) dev->alias_required_for_version); } dev->state = DEV_STATE_INITIALIZED; + if (dev->hotplugged && dev->info->reset) { + dev->info->reset(dev); + } return 0; } @@ -459,7 +462,7 @@ void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin) void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) { - qdev_prop_set_macaddr(dev, "mac", nd->macaddr); + qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a); if (nd->vlan) qdev_prop_set_vlan(dev, "vlan", nd->vlan); if (nd->netdev) diff --git a/hw/qxl-logger.c b/hw/qxl-logger.c index 76f43e646c..74cadba302 100644 --- a/hw/qxl-logger.c +++ b/hw/qxl-logger.c @@ -19,6 +19,7 @@ * along with this program; if not, see <http://www.gnu.org/licenses/>. */ +#include "qemu-timer.h" #include "qxl.h" static const char *qxl_type[] = { @@ -223,7 +224,8 @@ void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext) if (!qxl->cmdlog) { return; } - fprintf(stderr, "qxl-%d/%s:", qxl->id, ring); + fprintf(stderr, "%ld qxl-%d/%s:", qemu_get_clock_ns(vm_clock), + qxl->id, ring); fprintf(stderr, " cmd @ 0x%" PRIx64 " %s%s", ext->cmd.data, qxl_name(qxl_type, ext->cmd.type), compat ? "(compat)" : ""); diff --git a/hw/qxl-render.c b/hw/qxl-render.c index 1316066599..643ff2d841 100644 --- a/hw/qxl-render.c +++ b/hw/qxl-render.c @@ -124,8 +124,8 @@ void qxl_render_update(PCIQXLDevice *qxl) update.bottom = qxl->guest_primary.surface.height; memset(dirty, 0, sizeof(dirty)); - qxl->ssd.worker->update_area(qxl->ssd.worker, 0, &update, - dirty, ARRAY_SIZE(dirty), 1); + qxl_spice_update_area(qxl, 0, &update, + dirty, ARRAY_SIZE(dirty), 1, QXL_SYNC); for (i = 0; i < ARRAY_SIZE(dirty); i++) { if (qemu_spice_rect_is_empty(dirty+i)) { @@ -120,11 +120,127 @@ static QXLMode qxl_modes[] = { static PCIQXLDevice *qxl0; static void qxl_send_events(PCIQXLDevice *d, uint32_t events); -static void qxl_destroy_primary(PCIQXLDevice *d); +static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async); static void qxl_reset_memslots(PCIQXLDevice *d); static void qxl_reset_surfaces(PCIQXLDevice *d); static void qxl_ring_set_dirty(PCIQXLDevice *qxl); +void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...) +{ +#if SPICE_INTERFACE_QXL_MINOR >= 1 + qxl_send_events(qxl, QXL_INTERRUPT_ERROR); +#endif + if (qxl->guestdebug) { + va_list ap; + va_start(ap, msg); + fprintf(stderr, "qxl-%d: guest bug: ", qxl->id); + vfprintf(stderr, msg, ap); + fprintf(stderr, "\n"); + va_end(ap); + } +} + + +void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, + struct QXLRect *area, struct QXLRect *dirty_rects, + uint32_t num_dirty_rects, + uint32_t clear_dirty_region, + qxl_async_io async) +{ + if (async == QXL_SYNC) { + qxl->ssd.worker->update_area(qxl->ssd.worker, surface_id, area, + dirty_rects, num_dirty_rects, clear_dirty_region); + } else { +#if SPICE_INTERFACE_QXL_MINOR >= 1 + spice_qxl_update_area_async(&qxl->ssd.qxl, surface_id, area, + clear_dirty_region, 0); +#else + abort(); +#endif + } +} + +static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl, + uint32_t id) +{ + qemu_mutex_lock(&qxl->track_lock); + qxl->guest_surfaces.cmds[id] = 0; + qxl->guest_surfaces.count--; + qemu_mutex_unlock(&qxl->track_lock); +} + +static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id, + qxl_async_io async) +{ + if (async) { +#if SPICE_INTERFACE_QXL_MINOR < 1 + abort(); +#else + spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, + (uint64_t)id); +#endif + } else { + qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id); + qxl_spice_destroy_surface_wait_complete(qxl, id); + } +} + +#if SPICE_INTERFACE_QXL_MINOR >= 1 +static void qxl_spice_flush_surfaces_async(PCIQXLDevice *qxl) +{ + spice_qxl_flush_surfaces_async(&qxl->ssd.qxl, 0); +} +#endif + +void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, + uint32_t count) +{ + qxl->ssd.worker->loadvm_commands(qxl->ssd.worker, ext, count); +} + +void qxl_spice_oom(PCIQXLDevice *qxl) +{ + qxl->ssd.worker->oom(qxl->ssd.worker); +} + +void qxl_spice_reset_memslots(PCIQXLDevice *qxl) +{ + qxl->ssd.worker->reset_memslots(qxl->ssd.worker); +} + +static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl) +{ + qemu_mutex_lock(&qxl->track_lock); + memset(&qxl->guest_surfaces.cmds, 0, sizeof(qxl->guest_surfaces.cmds)); + qxl->guest_surfaces.count = 0; + qemu_mutex_unlock(&qxl->track_lock); +} + +static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async) +{ + if (async) { +#if SPICE_INTERFACE_QXL_MINOR < 1 + abort(); +#else + spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl, 0); +#endif + } else { + qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker); + qxl_spice_destroy_surfaces_complete(qxl); + } +} + +void qxl_spice_reset_image_cache(PCIQXLDevice *qxl) +{ + qxl->ssd.worker->reset_image_cache(qxl->ssd.worker); +} + +void qxl_spice_reset_cursor(PCIQXLDevice *qxl) +{ + qxl->ssd.worker->reset_cursor(qxl->ssd.worker); +} + + static inline uint32_t msb_mask(uint32_t val) { uint32_t mask; @@ -270,6 +386,7 @@ static void qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext) QXLSurfaceCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); uint32_t id = le32_to_cpu(cmd->surface_id); PANIC_ON(id >= NUM_SURFACES); + qemu_mutex_lock(&qxl->track_lock); if (cmd->type == QXL_SURFACE_CMD_CREATE) { qxl->guest_surfaces.cmds[id] = ext->cmd.data; qxl->guest_surfaces.count++; @@ -280,6 +397,7 @@ static void qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext) qxl->guest_surfaces.cmds[id] = 0; qxl->guest_surfaces.count--; } + qemu_mutex_unlock(&qxl->track_lock); break; } case QXL_CMD_CURSOR: @@ -336,6 +454,58 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) info->n_surfaces = NUM_SURFACES; } +static const char *qxl_mode_to_string(int mode) +{ + switch (mode) { + case QXL_MODE_COMPAT: + return "compat"; + case QXL_MODE_NATIVE: + return "native"; + case QXL_MODE_UNDEFINED: + return "undefined"; + case QXL_MODE_VGA: + return "vga"; + } + return "INVALID"; +} + +static const char *io_port_to_string(uint32_t io_port) +{ + if (io_port >= QXL_IO_RANGE_SIZE) { + return "out of range"; + } + static const char *io_port_to_string[QXL_IO_RANGE_SIZE + 1] = { + [QXL_IO_NOTIFY_CMD] = "QXL_IO_NOTIFY_CMD", + [QXL_IO_NOTIFY_CURSOR] = "QXL_IO_NOTIFY_CURSOR", + [QXL_IO_UPDATE_AREA] = "QXL_IO_UPDATE_AREA", + [QXL_IO_UPDATE_IRQ] = "QXL_IO_UPDATE_IRQ", + [QXL_IO_NOTIFY_OOM] = "QXL_IO_NOTIFY_OOM", + [QXL_IO_RESET] = "QXL_IO_RESET", + [QXL_IO_SET_MODE] = "QXL_IO_SET_MODE", + [QXL_IO_LOG] = "QXL_IO_LOG", + [QXL_IO_MEMSLOT_ADD] = "QXL_IO_MEMSLOT_ADD", + [QXL_IO_MEMSLOT_DEL] = "QXL_IO_MEMSLOT_DEL", + [QXL_IO_DETACH_PRIMARY] = "QXL_IO_DETACH_PRIMARY", + [QXL_IO_ATTACH_PRIMARY] = "QXL_IO_ATTACH_PRIMARY", + [QXL_IO_CREATE_PRIMARY] = "QXL_IO_CREATE_PRIMARY", + [QXL_IO_DESTROY_PRIMARY] = "QXL_IO_DESTROY_PRIMARY", + [QXL_IO_DESTROY_SURFACE_WAIT] = "QXL_IO_DESTROY_SURFACE_WAIT", + [QXL_IO_DESTROY_ALL_SURFACES] = "QXL_IO_DESTROY_ALL_SURFACES", +#if SPICE_INTERFACE_QXL_MINOR >= 1 + [QXL_IO_UPDATE_AREA_ASYNC] = "QXL_IO_UPDATE_AREA_ASYNC", + [QXL_IO_MEMSLOT_ADD_ASYNC] = "QXL_IO_MEMSLOT_ADD_ASYNC", + [QXL_IO_CREATE_PRIMARY_ASYNC] = "QXL_IO_CREATE_PRIMARY_ASYNC", + [QXL_IO_DESTROY_PRIMARY_ASYNC] = "QXL_IO_DESTROY_PRIMARY_ASYNC", + [QXL_IO_DESTROY_SURFACE_ASYNC] = "QXL_IO_DESTROY_SURFACE_ASYNC", + [QXL_IO_DESTROY_ALL_SURFACES_ASYNC] + = "QXL_IO_DESTROY_ALL_SURFACES_ASYNC", + [QXL_IO_FLUSH_SURFACES_ASYNC] = "QXL_IO_FLUSH_SURFACES_ASYNC", + [QXL_IO_FLUSH_RELEASE] = "QXL_IO_FLUSH_RELEASE", +#endif + }; + return io_port_to_string[io_port]; +} + /* called from spice server thread context only */ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) { @@ -358,18 +528,19 @@ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) } qemu_mutex_unlock(&qxl->ssd.lock); if (ret) { + dprint(qxl, 2, "%s %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode)); qxl_log_command(qxl, "vga", ext); } return ret; case QXL_MODE_COMPAT: case QXL_MODE_NATIVE: case QXL_MODE_UNDEFINED: - dprint(qxl, 2, "%s: %s\n", __FUNCTION__, - qxl->cmdflags ? "compat" : "native"); + dprint(qxl, 4, "%s: %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode)); ring = &qxl->ram->cmd_ring; if (SPICE_RING_IS_EMPTY(ring)) { return false; } + dprint(qxl, 2, "%s: %s\n", __FUNCTION__, qxl_mode_to_string(qxl->mode)); SPICE_RING_CONS_ITEM(ring, cmd); ext->cmd = *cmd; ext->group_id = MEMSLOT_GROUP_GUEST; @@ -563,6 +734,38 @@ static int interface_flush_resources(QXLInstance *sin) return ret; } +static void qxl_create_guest_primary_complete(PCIQXLDevice *d); + +#if SPICE_INTERFACE_QXL_MINOR >= 1 + +/* called from spice server thread context only */ +static void interface_async_complete(QXLInstance *sin, uint64_t cookie) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + uint32_t current_async; + + qemu_mutex_lock(&qxl->async_lock); + current_async = qxl->current_async; + qxl->current_async = QXL_UNDEFINED_IO; + qemu_mutex_unlock(&qxl->async_lock); + + dprint(qxl, 2, "async_complete: %d (%ld) done\n", current_async, cookie); + switch (current_async) { + case QXL_IO_CREATE_PRIMARY_ASYNC: + qxl_create_guest_primary_complete(qxl); + break; + case QXL_IO_DESTROY_ALL_SURFACES_ASYNC: + qxl_spice_destroy_surfaces_complete(qxl); + break; + case QXL_IO_DESTROY_SURFACE_ASYNC: + qxl_spice_destroy_surface_wait_complete(qxl, (uint32_t)cookie); + break; + } + qxl_send_events(qxl, QXL_INTERRUPT_IO_CMD); +} + +#endif + static const QXLInterface qxl_interface = { .base.type = SPICE_INTERFACE_QXL, .base.description = "qxl gpu", @@ -582,6 +785,9 @@ static const QXLInterface qxl_interface = { .req_cursor_notification = interface_req_cursor_notification, .notify_update = interface_notify_update, .flush_resources = interface_flush_resources, +#if SPICE_INTERFACE_QXL_MINOR >= 1 + .async_complete = interface_async_complete, +#endif }; static void qxl_enter_vga_mode(PCIQXLDevice *d) @@ -601,7 +807,7 @@ static void qxl_exit_vga_mode(PCIQXLDevice *d) return; } dprint(d, 1, "%s\n", __FUNCTION__); - qxl_destroy_primary(d); + qxl_destroy_primary(d, QXL_SYNC); } static void qxl_set_irq(PCIQXLDevice *d) @@ -640,8 +846,8 @@ static void qxl_reset_state(PCIQXLDevice *d) QXLRam *ram = d->ram; QXLRom *rom = d->rom; - assert(SPICE_RING_IS_EMPTY(&ram->cmd_ring)); - assert(SPICE_RING_IS_EMPTY(&ram->cursor_ring)); + assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cmd_ring)); + assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cursor_ring)); d->shadow_rom.update_id = cpu_to_le32(0); *rom = d->shadow_rom; qxl_rom_set_dirty(d); @@ -668,8 +874,8 @@ static void qxl_hard_reset(PCIQXLDevice *d, int loadvm) dprint(d, 1, "%s: start%s\n", __FUNCTION__, loadvm ? " (loadvm)" : ""); - d->ssd.worker->reset_cursor(d->ssd.worker); - d->ssd.worker->reset_image_cache(d->ssd.worker); + qxl_spice_reset_cursor(d); + qxl_spice_reset_image_cache(d); qxl_reset_surfaces(d); qxl_reset_memslots(d); @@ -698,13 +904,14 @@ static void qxl_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) if (qxl->mode != QXL_MODE_VGA) { dprint(qxl, 1, "%s\n", __FUNCTION__); - qxl_destroy_primary(qxl); + qxl_destroy_primary(qxl, QXL_SYNC); qxl_soft_reset(qxl); } vga_ioport_write(opaque, addr, val); } -static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta) +static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, + qxl_async_io async) { static const int regions[] = { QXL_RAM_RANGE_INDEX, @@ -774,7 +981,7 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta) __FUNCTION__, memslot.slot_id, memslot.virt_start, memslot.virt_end); - d->ssd.worker->add_memslot(d->ssd.worker, &memslot); + qemu_spice_add_memslot(&d->ssd, &memslot, async); d->guest_slots[slot_id].ptr = (void*)memslot.virt_start; d->guest_slots[slot_id].size = memslot.virt_end - memslot.virt_start; d->guest_slots[slot_id].delta = delta; @@ -784,14 +991,14 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta) static void qxl_del_memslot(PCIQXLDevice *d, uint32_t slot_id) { dprint(d, 1, "%s: slot %d\n", __FUNCTION__, slot_id); - d->ssd.worker->del_memslot(d->ssd.worker, MEMSLOT_GROUP_HOST, slot_id); + qemu_spice_del_memslot(&d->ssd, MEMSLOT_GROUP_HOST, slot_id); d->guest_slots[slot_id].active = 0; } static void qxl_reset_memslots(PCIQXLDevice *d) { dprint(d, 1, "%s:\n", __FUNCTION__); - d->ssd.worker->reset_memslots(d->ssd.worker); + qxl_spice_reset_memslots(d); memset(&d->guest_slots, 0, sizeof(d->guest_slots)); } @@ -799,8 +1006,7 @@ static void qxl_reset_surfaces(PCIQXLDevice *d) { dprint(d, 1, "%s:\n", __FUNCTION__); d->mode = QXL_MODE_UNDEFINED; - d->ssd.worker->destroy_surfaces(d->ssd.worker); - memset(&d->guest_surfaces.cmds, 0, sizeof(d->guest_surfaces.cmds)); + qxl_spice_destroy_surfaces(d, QXL_SYNC); } /* called from spice server thread context only */ @@ -825,7 +1031,14 @@ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id) } } -static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm) +static void qxl_create_guest_primary_complete(PCIQXLDevice *qxl) +{ + /* for local rendering */ + qxl_render_resize(qxl); +} + +static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm, + qxl_async_io async) { QXLDevSurfaceCreate surface; QXLSurfaceCreate *sc = &qxl->guest_primary.surface; @@ -853,22 +1066,26 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm) qxl->mode = QXL_MODE_NATIVE; qxl->cmdflags = 0; - qxl->ssd.worker->create_primary_surface(qxl->ssd.worker, 0, &surface); + qemu_spice_create_primary_surface(&qxl->ssd, 0, &surface, async); - /* for local rendering */ - qxl_render_resize(qxl); + if (async == QXL_SYNC) { + qxl_create_guest_primary_complete(qxl); + } } -static void qxl_destroy_primary(PCIQXLDevice *d) +/* return 1 if surface destoy was initiated (in QXL_ASYNC case) or + * done (in QXL_SYNC case), 0 otherwise. */ +static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async) { if (d->mode == QXL_MODE_UNDEFINED) { - return; + return 0; } dprint(d, 1, "%s\n", __FUNCTION__); d->mode = QXL_MODE_UNDEFINED; - d->ssd.worker->destroy_primary_surface(d->ssd.worker, 0); + qemu_spice_destroy_primary_surface(&d->ssd, 0, async); + return 1; } static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm) @@ -898,10 +1115,10 @@ static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm) } d->guest_slots[0].slot = slot; - qxl_add_memslot(d, 0, devmem); + qxl_add_memslot(d, 0, devmem, QXL_SYNC); d->guest_primary.surface = surface; - qxl_create_guest_primary(d, 0); + qxl_create_guest_primary(d, 0, QXL_SYNC); d->mode = QXL_MODE_COMPAT; d->cmdflags = QXL_COMMAND_FLAG_COMPAT; @@ -919,6 +1136,10 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) { PCIQXLDevice *d = opaque; uint32_t io_port = addr - d->io_base; + qxl_async_io async = QXL_SYNC; +#if SPICE_INTERFACE_QXL_MINOR >= 1 + uint32_t orig_io_port = io_port; +#endif switch (io_port) { case QXL_IO_RESET: @@ -926,27 +1147,83 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) case QXL_IO_MEMSLOT_ADD: case QXL_IO_MEMSLOT_DEL: case QXL_IO_CREATE_PRIMARY: + case QXL_IO_UPDATE_IRQ: + case QXL_IO_LOG: +#if SPICE_INTERFACE_QXL_MINOR >= 1 + case QXL_IO_MEMSLOT_ADD_ASYNC: + case QXL_IO_CREATE_PRIMARY_ASYNC: +#endif break; default: - if (d->mode == QXL_MODE_NATIVE || d->mode == QXL_MODE_COMPAT) + if (d->mode != QXL_MODE_VGA) { break; - dprint(d, 1, "%s: unexpected port 0x%x in vga mode\n", __FUNCTION__, io_port); + } + dprint(d, 1, "%s: unexpected port 0x%x (%s) in vga mode\n", + __func__, io_port, io_port_to_string(io_port)); +#if SPICE_INTERFACE_QXL_MINOR >= 1 + /* be nice to buggy guest drivers */ + if (io_port >= QXL_IO_UPDATE_AREA_ASYNC && + io_port <= QXL_IO_DESTROY_ALL_SURFACES_ASYNC) { + qxl_send_events(d, QXL_INTERRUPT_IO_CMD); + } +#endif return; } +#if SPICE_INTERFACE_QXL_MINOR >= 1 + /* we change the io_port to avoid ifdeffery in the main switch */ + orig_io_port = io_port; + switch (io_port) { + case QXL_IO_UPDATE_AREA_ASYNC: + io_port = QXL_IO_UPDATE_AREA; + goto async_common; + case QXL_IO_MEMSLOT_ADD_ASYNC: + io_port = QXL_IO_MEMSLOT_ADD; + goto async_common; + case QXL_IO_CREATE_PRIMARY_ASYNC: + io_port = QXL_IO_CREATE_PRIMARY; + goto async_common; + case QXL_IO_DESTROY_PRIMARY_ASYNC: + io_port = QXL_IO_DESTROY_PRIMARY; + goto async_common; + case QXL_IO_DESTROY_SURFACE_ASYNC: + io_port = QXL_IO_DESTROY_SURFACE_WAIT; + goto async_common; + case QXL_IO_DESTROY_ALL_SURFACES_ASYNC: + io_port = QXL_IO_DESTROY_ALL_SURFACES; + goto async_common; + case QXL_IO_FLUSH_SURFACES_ASYNC: +async_common: + async = QXL_ASYNC; + qemu_mutex_lock(&d->async_lock); + if (d->current_async != QXL_UNDEFINED_IO) { + qxl_guest_bug(d, "%d async started before last (%d) complete", + io_port, d->current_async); + qemu_mutex_unlock(&d->async_lock); + return; + } + d->current_async = orig_io_port; + qemu_mutex_unlock(&d->async_lock); + dprint(d, 2, "start async %d (%d)\n", io_port, val); + break; + default: + break; + } +#endif + switch (io_port) { case QXL_IO_UPDATE_AREA: { QXLRect update = d->ram->update_area; - d->ssd.worker->update_area(d->ssd.worker, d->ram->update_surface, - &update, NULL, 0, 0); + qxl_spice_update_area(d, d->ram->update_surface, + &update, NULL, 0, 0, async); break; } case QXL_IO_NOTIFY_CMD: - d->ssd.worker->wakeup(d->ssd.worker); + qemu_spice_wakeup(&d->ssd); break; case QXL_IO_NOTIFY_CURSOR: - d->ssd.worker->wakeup(d->ssd.worker); + qemu_spice_wakeup(&d->ssd); break; case QXL_IO_UPDATE_IRQ: qxl_set_irq(d); @@ -960,7 +1237,7 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) break; } d->oom_running = 1; - d->ssd.worker->oom(d->ssd.worker); + qxl_spice_oom(d); d->oom_running = 0; break; case QXL_IO_SET_MODE: @@ -969,7 +1246,8 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) break; case QXL_IO_LOG: if (d->guestdebug) { - fprintf(stderr, "qxl/guest: %s", d->ram->log_buf); + fprintf(stderr, "qxl/guest-%d: %ld: %s", d->id, + qemu_get_clock_ns(vm_clock), d->ram->log_buf); } break; case QXL_IO_RESET: @@ -977,35 +1255,97 @@ static void ioport_write(void *opaque, uint32_t addr, uint32_t val) qxl_hard_reset(d, 0); break; case QXL_IO_MEMSLOT_ADD: - PANIC_ON(val >= NUM_MEMSLOTS); - PANIC_ON(d->guest_slots[val].active); + if (val >= NUM_MEMSLOTS) { + qxl_guest_bug(d, "QXL_IO_MEMSLOT_ADD: val out of range"); + break; + } + if (d->guest_slots[val].active) { + qxl_guest_bug(d, "QXL_IO_MEMSLOT_ADD: memory slot already active"); + break; + } d->guest_slots[val].slot = d->ram->mem_slot; - qxl_add_memslot(d, val, 0); + qxl_add_memslot(d, val, 0, async); break; case QXL_IO_MEMSLOT_DEL: + if (val >= NUM_MEMSLOTS) { + qxl_guest_bug(d, "QXL_IO_MEMSLOT_DEL: val out of range"); + break; + } qxl_del_memslot(d, val); break; case QXL_IO_CREATE_PRIMARY: - PANIC_ON(val != 0); - dprint(d, 1, "QXL_IO_CREATE_PRIMARY\n"); + if (val != 0) { + qxl_guest_bug(d, "QXL_IO_CREATE_PRIMARY (async=%d): val != 0", + async); + goto cancel_async; + } + dprint(d, 1, "QXL_IO_CREATE_PRIMARY async=%d\n", async); d->guest_primary.surface = d->ram->create_surface; - qxl_create_guest_primary(d, 0); + qxl_create_guest_primary(d, 0, async); break; case QXL_IO_DESTROY_PRIMARY: - PANIC_ON(val != 0); - dprint(d, 1, "QXL_IO_DESTROY_PRIMARY\n"); - qxl_destroy_primary(d); + if (val != 0) { + qxl_guest_bug(d, "QXL_IO_DESTROY_PRIMARY (async=%d): val != 0", + async); + goto cancel_async; + } + dprint(d, 1, "QXL_IO_DESTROY_PRIMARY (async=%d) (%s)\n", async, + qxl_mode_to_string(d->mode)); + if (!qxl_destroy_primary(d, async)) { + dprint(d, 1, "QXL_IO_DESTROY_PRIMARY_ASYNC in %s, ignored\n", + qxl_mode_to_string(d->mode)); + goto cancel_async; + } break; case QXL_IO_DESTROY_SURFACE_WAIT: - d->ssd.worker->destroy_surface_wait(d->ssd.worker, val); + if (val >= NUM_SURFACES) { + qxl_guest_bug(d, "QXL_IO_DESTROY_SURFACE (async=%d):" + "%d >= NUM_SURFACES", async, val); + goto cancel_async; + } + qxl_spice_destroy_surface_wait(d, val, async); + break; +#if SPICE_INTERFACE_QXL_MINOR >= 1 + case QXL_IO_FLUSH_RELEASE: { + QXLReleaseRing *ring = &d->ram->release_ring; + if (ring->prod - ring->cons + 1 == ring->num_items) { + fprintf(stderr, + "ERROR: no flush, full release ring [p%d,%dc]\n", + ring->prod, ring->cons); + } + qxl_push_free_res(d, 1 /* flush */); + dprint(d, 1, "QXL_IO_FLUSH_RELEASE exit (%s, s#=%d, res#=%d,%p)\n", + qxl_mode_to_string(d->mode), d->guest_surfaces.count, + d->num_free_res, d->last_release); + break; + } + case QXL_IO_FLUSH_SURFACES_ASYNC: + dprint(d, 1, "QXL_IO_FLUSH_SURFACES_ASYNC (%d) (%s, s#=%d, res#=%d)\n", + val, qxl_mode_to_string(d->mode), d->guest_surfaces.count, + d->num_free_res); + qxl_spice_flush_surfaces_async(d); break; +#endif case QXL_IO_DESTROY_ALL_SURFACES: - d->ssd.worker->destroy_surfaces(d->ssd.worker); + d->mode = QXL_MODE_UNDEFINED; + qxl_spice_destroy_surfaces(d, async); break; default: fprintf(stderr, "%s: ioport=0x%x, abort()\n", __FUNCTION__, io_port); abort(); } + return; +cancel_async: +#if SPICE_INTERFACE_QXL_MINOR >= 1 + if (async) { + qxl_send_events(d, QXL_INTERRUPT_IO_CMD); + qemu_mutex_lock(&d->async_lock); + d->current_async = QXL_UNDEFINED_IO; + qemu_mutex_unlock(&d->async_lock); + } +#else + return; +#endif } static uint32_t ioport_read(void *opaque, uint32_t addr) @@ -1167,11 +1507,14 @@ static void qxl_vm_change_state_handler(void *opaque, int running, int reason) qemu_spice_vm_change_state_handler(&qxl->ssd, running, reason); if (!running && qxl->mode == QXL_MODE_NATIVE) { - /* dirty all vram (which holds surfaces) to make sure it is saved */ + /* dirty all vram (which holds surfaces) and devram (primary surface) + * to make sure they are saved */ /* FIXME #1: should go out during "live" stage */ /* FIXME #2: we only need to save the areas which are actually used */ - ram_addr_t addr = qxl->vram_offset; - qxl_set_dirty(addr, addr + qxl->vram_size); + ram_addr_t vram_addr = qxl->vram_offset; + ram_addr_t surface0_addr = qxl->vga.vram_offset + qxl->shadow_rom.draw_area_offset; + qxl_set_dirty(vram_addr, vram_addr + qxl->vram_size); + qxl_set_dirty(surface0_addr, surface0_addr + qxl->shadow_rom.surface0_area_size); } } @@ -1207,7 +1550,6 @@ static DisplayChangeListener display_listener = { static int qxl_init_common(PCIQXLDevice *qxl) { uint8_t* config = qxl->pci.config; - uint32_t pci_device_id; uint32_t pci_device_rev; uint32_t io_size; @@ -1215,23 +1557,25 @@ static int qxl_init_common(PCIQXLDevice *qxl) qxl->generation = 1; qxl->num_memslots = NUM_MEMSLOTS; qxl->num_surfaces = NUM_SURFACES; + qemu_mutex_init(&qxl->track_lock); + qemu_mutex_init(&qxl->async_lock); + qxl->current_async = QXL_UNDEFINED_IO; switch (qxl->revision) { case 1: /* spice 0.4 -- qxl-1 */ - pci_device_id = QXL_DEVICE_ID_STABLE; pci_device_rev = QXL_REVISION_STABLE_V04; break; case 2: /* spice 0.6 -- qxl-2 */ - pci_device_id = QXL_DEVICE_ID_STABLE; pci_device_rev = QXL_REVISION_STABLE_V06; break; - default: /* experimental */ - pci_device_id = QXL_DEVICE_ID_DEVEL; - pci_device_rev = 1; +#if SPICE_INTERFACE_QXL_MINOR >= 1 + case 3: /* qxl-3 */ +#endif + default: + pci_device_rev = QXL_DEFAULT_REVISION; break; } - pci_config_set_device_id(config, pci_device_id); pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev); pci_set_byte(&config[PCI_INTERRUPT_PIN], 1); @@ -1300,12 +1644,7 @@ static int qxl_init_primary(PCIDevice *dev) vga->ds = graphic_console_init(qxl_hw_update, qxl_hw_invalidate, qxl_hw_screen_dump, qxl_hw_text_update, qxl); - qxl->ssd.ds = vga->ds; - qemu_mutex_init(&qxl->ssd.lock); - qxl->ssd.mouse_x = -1; - qxl->ssd.mouse_y = -1; - qxl->ssd.bufsize = (16 * 1024 * 1024); - qxl->ssd.buf = qemu_malloc(qxl->ssd.bufsize); + qemu_spice_display_init_common(&qxl->ssd, vga->ds); qxl0 = qxl; register_displaychangelistener(vga->ds, &display_listener); @@ -1375,7 +1714,8 @@ static int qxl_post_load(void *opaque, int version) d->modes = (QXLModes*)((uint8_t*)d->rom + d->rom->modes_offset); - dprint(d, 1, "%s: restore mode\n", __FUNCTION__); + dprint(d, 1, "%s: restore mode (%s)\n", __FUNCTION__, + qxl_mode_to_string(d->mode)); newmode = d->mode; d->mode = QXL_MODE_UNDEFINED; switch (newmode) { @@ -1389,9 +1729,9 @@ static int qxl_post_load(void *opaque, int version) if (!d->guest_slots[i].active) { continue; } - qxl_add_memslot(d, i, 0); + qxl_add_memslot(d, i, 0, QXL_SYNC); } - qxl_create_guest_primary(d, 1); + qxl_create_guest_primary(d, 1, QXL_SYNC); /* replay surface-create and cursor-set commands */ cmds = qemu_mallocz(sizeof(QXLCommandExt) * (NUM_SURFACES + 1)); @@ -1408,7 +1748,7 @@ static int qxl_post_load(void *opaque, int version) cmds[out].cmd.type = QXL_CMD_CURSOR; cmds[out].group_id = MEMSLOT_GROUP_GUEST; out++; - d->ssd.worker->loadvm_commands(d->ssd.worker, cmds, out); + qxl_spice_loadvm_commands(d, cmds, out); qemu_free(cmds); break; @@ -1492,11 +1832,15 @@ static PCIDeviceInfo qxl_info_primary = { .config_write = qxl_write_config, .romfile = "vgabios-qxl.bin", .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, 2), + 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), @@ -1512,11 +1856,15 @@ static PCIDeviceInfo qxl_info_secondary = { .qdev.vmsd = &qxl_vmstate, .init = qxl_init_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, 2), + 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), @@ -15,6 +15,8 @@ enum qxl_mode { QXL_MODE_NATIVE, }; +#define QXL_UNDEFINED_IO UINT32_MAX + typedef struct PCIQXLDevice { PCIDevice pci; SimpleSpiceDisplay ssd; @@ -30,6 +32,9 @@ typedef struct PCIQXLDevice { int32_t num_memslots; int32_t num_surfaces; + uint32_t current_async; + QemuMutex async_lock; + struct guest_slots { QXLMemSlot slot; void *ptr; @@ -55,6 +60,8 @@ typedef struct PCIQXLDevice { } guest_surfaces; QXLPHYSICAL guest_cursor; + QemuMutex track_lock; + /* thread signaling */ pthread_t main; int pipe[2]; @@ -84,7 +91,7 @@ typedef struct PCIQXLDevice { #define PANIC_ON(x) if ((x)) { \ printf("%s: PANIC %s failed\n", __FUNCTION__, #x); \ - exit(-1); \ + abort(); \ } #define dprint(_qxl, _level, _fmt, ...) \ @@ -95,8 +102,27 @@ typedef struct PCIQXLDevice { } \ } while (0) +#if SPICE_INTERFACE_QXL_MINOR >= 1 +#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V10 +#else +#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V06 +#endif + /* qxl.c */ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id); +void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...); + +void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, + struct QXLRect *area, struct QXLRect *dirty_rects, + uint32_t num_dirty_rects, + uint32_t clear_dirty_region, + qxl_async_io async); +void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, + uint32_t count); +void qxl_spice_oom(PCIQXLDevice *qxl); +void qxl_spice_reset_memslots(PCIQXLDevice *qxl); +void qxl_spice_reset_image_cache(PCIQXLDevice *qxl); +void qxl_spice_reset_cursor(PCIQXLDevice *qxl); /* qxl-logger.c */ void qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id); @@ -106,3 +132,9 @@ void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext); void qxl_render_resize(PCIQXLDevice *qxl); void qxl_render_update(PCIQXLDevice *qxl); void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext); +#if SPICE_INTERFACE_QXL_MINOR >= 1 +void qxl_spice_update_area_async(PCIQXLDevice *qxl, uint32_t surface_id, + struct QXLRect *area, + uint32_t clear_dirty_region, + int is_vga); +#endif diff --git a/hw/realview.c b/hw/realview.c index 82f3d82d44..94ab900512 100644 --- a/hw/realview.c +++ b/hw/realview.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/realview_gic.c b/hw/realview_gic.c index db908b6439..43a2a0d5ed 100644 --- a/hw/realview_gic.c +++ b/hw/realview_gic.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index ad6a730be0..0b0344c1fd 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -131,7 +131,8 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus) return res; } -SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun) +SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, + uint32_t lun, void *hba_private) { SCSIRequest *req; @@ -141,14 +142,16 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l req->dev = d; req->tag = tag; req->lun = lun; + req->hba_private = hba_private; req->status = -1; 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) +SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, + void *hba_private) { - return d->info->alloc_req(d, tag, lun); + return d->info->alloc_req(d, tag, lun, hba_private); } uint8_t *scsi_req_get_buf(SCSIRequest *req) @@ -220,7 +223,7 @@ static int scsi_req_length(SCSIRequest *req, uint8_t *cmd) switch(cmd[0]) { case TEST_UNIT_READY: - case REZERO_UNIT: + case REWIND: case START_STOP: case SEEK_6: case WRITE_FILEMARKS: @@ -229,24 +232,24 @@ static int scsi_req_length(SCSIRequest *req, uint8_t *cmd) case RELEASE: case ERASE: case ALLOW_MEDIUM_REMOVAL: - case VERIFY: + case VERIFY_10: case SEEK_10: case SYNCHRONIZE_CACHE: case LOCK_UNLOCK_CACHE: case LOAD_UNLOAD: case SET_CD_SPEED: case SET_LIMITS: - case WRITE_LONG: + case WRITE_LONG_10: case MOVE_MEDIUM: case UPDATE_BLOCK: req->cmd.xfer = 0; break; case MODE_SENSE: break; - case WRITE_SAME: + case WRITE_SAME_10: req->cmd.xfer = 1; break; - case READ_CAPACITY: + case READ_CAPACITY_10: req->cmd.xfer = 8; break; case READ_BLOCK_LIMITS: @@ -262,7 +265,7 @@ static int scsi_req_length(SCSIRequest *req, uint8_t *cmd) req->cmd.xfer *= 8; break; case WRITE_10: - case WRITE_VERIFY: + case WRITE_VERIFY_10: case WRITE_6: case WRITE_12: case WRITE_VERIFY_12: @@ -322,7 +325,7 @@ static void scsi_req_xfer_mode(SCSIRequest *req) switch (req->cmd.buf[0]) { case WRITE_6: case WRITE_10: - case WRITE_VERIFY: + case WRITE_VERIFY_10: case WRITE_12: case WRITE_VERIFY_12: case WRITE_16: @@ -342,15 +345,13 @@ static void scsi_req_xfer_mode(SCSIRequest *req) case SEARCH_HIGH: case SEARCH_LOW: case UPDATE_BLOCK: - case WRITE_LONG: - case WRITE_SAME: + case WRITE_LONG_10: + case WRITE_SAME_10: case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12: - case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG: - case WRITE_LONG_2: case PERSISTENT_RESERVE_OUT: case MAINTENANCE_OUT: req->cmd.mode = SCSI_XFER_TO_DEV; @@ -514,8 +515,7 @@ static const char *scsi_command_name(uint8_t cmd) { static const char *names[] = { [ TEST_UNIT_READY ] = "TEST_UNIT_READY", - [ REZERO_UNIT ] = "REZERO_UNIT", - /* REWIND and REZERO_UNIT use the same operation code */ + [ REWIND ] = "REWIND", [ REQUEST_SENSE ] = "REQUEST_SENSE", [ FORMAT_UNIT ] = "FORMAT_UNIT", [ READ_BLOCK_LIMITS ] = "READ_BLOCK_LIMITS", @@ -540,14 +540,12 @@ static const char *scsi_command_name(uint8_t cmd) [ RECEIVE_DIAGNOSTIC ] = "RECEIVE_DIAGNOSTIC", [ SEND_DIAGNOSTIC ] = "SEND_DIAGNOSTIC", [ ALLOW_MEDIUM_REMOVAL ] = "ALLOW_MEDIUM_REMOVAL", - - [ SET_WINDOW ] = "SET_WINDOW", - [ READ_CAPACITY ] = "READ_CAPACITY", + [ READ_CAPACITY_10 ] = "READ_CAPACITY_10", [ READ_10 ] = "READ_10", [ WRITE_10 ] = "WRITE_10", [ SEEK_10 ] = "SEEK_10", - [ WRITE_VERIFY ] = "WRITE_VERIFY", - [ VERIFY ] = "VERIFY", + [ WRITE_VERIFY_10 ] = "WRITE_VERIFY_10", + [ VERIFY_10 ] = "VERIFY_10", [ SEARCH_HIGH ] = "SEARCH_HIGH", [ SEARCH_EQUAL ] = "SEARCH_EQUAL", [ SEARCH_LOW ] = "SEARCH_LOW", @@ -563,11 +561,14 @@ static const char *scsi_command_name(uint8_t cmd) [ WRITE_BUFFER ] = "WRITE_BUFFER", [ READ_BUFFER ] = "READ_BUFFER", [ UPDATE_BLOCK ] = "UPDATE_BLOCK", - [ READ_LONG ] = "READ_LONG", - [ WRITE_LONG ] = "WRITE_LONG", + [ READ_LONG_10 ] = "READ_LONG_10", + [ WRITE_LONG_10 ] = "WRITE_LONG_10", [ CHANGE_DEFINITION ] = "CHANGE_DEFINITION", - [ WRITE_SAME ] = "WRITE_SAME", + [ WRITE_SAME_10 ] = "WRITE_SAME_10", + [ UNMAP ] = "UNMAP", [ READ_TOC ] = "READ_TOC", + [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT", + [ GET_CONFIGURATION ] = "GET_CONFIGURATION", [ LOG_SELECT ] = "LOG_SELECT", [ LOG_SENSE ] = "LOG_SENSE", [ MODE_SELECT_10 ] = "MODE_SELECT_10", @@ -576,27 +577,39 @@ static const char *scsi_command_name(uint8_t cmd) [ 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 ] = "ATA_PASSTHROUGH", + [ 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", + [ SYNCHRONIZE_CACHE_16 ] = "SYNCHRONIZE_CACHE_16", + [ LOCATE_16 ] = "LOCATE_16", + [ WRITE_SAME_16 ] = "WRITE_SAME_16", + [ ERASE_16 ] = "ERASE_16", + [ SERVICE_ACTION_IN ] = "SERVICE_ACTION_IN", + [ WRITE_LONG_16 ] = "WRITE_LONG_16", + [ REPORT_LUNS ] = "REPORT_LUNS", + [ BLANK ] = "BLANK", + [ MAINTENANCE_IN ] = "MAINTENANCE_IN", + [ MAINTENANCE_OUT ] = "MAINTENANCE_OUT", [ MOVE_MEDIUM ] = "MOVE_MEDIUM", + [ LOAD_UNLOAD ] = "LOAD_UNLOAD", [ READ_12 ] = "READ_12", [ WRITE_12 ] = "WRITE_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", - [ WRITE_LONG_2 ] = "WRITE_LONG_2", - - [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT", - [ GET_CONFIGURATION ] = "GET_CONFIGURATION", - [ READ_16 ] = "READ_16", - [ WRITE_16 ] = "WRITE_16", - [ WRITE_VERIFY_16 ] = "WRITE_VERIFY_16", - [ SERVICE_ACTION_IN ] = "SERVICE_ACTION_IN", - [ REPORT_LUNS ] = "REPORT_LUNS", - [ LOAD_UNLOAD ] = "LOAD_UNLOAD", + [ READ_DEFECT_DATA_12 ] = "READ_DEFECT_DATA_12", [ SET_CD_SPEED ] = "SET_CD_SPEED", - [ BLANK ] = "BLANK", }; if (cmd >= ARRAY_SIZE(names) || names[cmd] == NULL) diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h index 413cce07b5..27010b74c0 100644 --- a/hw/scsi-defs.h +++ b/hw/scsi-defs.h @@ -25,7 +25,7 @@ */ #define TEST_UNIT_READY 0x00 -#define REZERO_UNIT 0x01 +#define REWIND 0x01 #define REQUEST_SENSE 0x03 #define FORMAT_UNIT 0x04 #define READ_BLOCK_LIMITS 0x05 @@ -48,14 +48,13 @@ #define RECEIVE_DIAGNOSTIC 0x1c #define SEND_DIAGNOSTIC 0x1d #define ALLOW_MEDIUM_REMOVAL 0x1e - -#define SET_WINDOW 0x24 -#define READ_CAPACITY 0x25 +#define READ_CAPACITY_10 0x25 #define READ_10 0x28 #define WRITE_10 0x2a #define SEEK_10 0x2b -#define WRITE_VERIFY 0x2e -#define VERIFY 0x2f +#define LOCATE_10 0x2b +#define WRITE_VERIFY_10 0x2e +#define VERIFY_10 0x2f #define SEARCH_HIGH 0x30 #define SEARCH_EQUAL 0x31 #define SEARCH_LOW 0x32 @@ -71,11 +70,14 @@ #define WRITE_BUFFER 0x3b #define READ_BUFFER 0x3c #define UPDATE_BLOCK 0x3d -#define READ_LONG 0x3e -#define WRITE_LONG 0x3f +#define READ_LONG_10 0x3e +#define WRITE_LONG_10 0x3f #define CHANGE_DEFINITION 0x40 -#define WRITE_SAME 0x41 +#define WRITE_SAME_10 0x41 +#define UNMAP 0x42 #define READ_TOC 0x43 +#define REPORT_DENSITY_SUPPORT 0x44 +#define GET_CONFIGURATION 0x46 #define LOG_SELECT 0x4c #define LOG_SENSE 0x4d #define MODE_SELECT_10 0x55 @@ -84,32 +86,40 @@ #define MODE_SENSE_10 0x5a #define PERSISTENT_RESERVE_IN 0x5e #define PERSISTENT_RESERVE_OUT 0x5f +#define VARLENGTH_CDB 0x7f +#define WRITE_FILEMARKS_16 0x80 +#define EXTENDED_COPY 0x83 +#define ATA_PASSTHROUGH 0x85 +#define ACCESS_CONTROL_IN 0x86 +#define ACCESS_CONTROL_OUT 0x87 +#define READ_16 0x88 +#define COMPARE_AND_WRITE 0x89 +#define WRITE_16 0x8a +#define WRITE_VERIFY_16 0x8e +#define VERIFY_16 0x8f +#define SYNCHRONIZE_CACHE_16 0x91 +#define LOCATE_16 0x92 #define WRITE_SAME_16 0x93 +#define ERASE_16 0x93 +#define SERVICE_ACTION_IN 0x9e +#define WRITE_LONG_16 0x9f +#define REPORT_LUNS 0xa0 +#define BLANK 0xa1 #define MAINTENANCE_IN 0xa3 #define MAINTENANCE_OUT 0xa4 #define MOVE_MEDIUM 0xa5 +#define LOAD_UNLOAD 0xa6 #define READ_12 0xa8 #define WRITE_12 0xaa #define WRITE_VERIFY_12 0xae +#define VERIFY_12 0xaf #define SEARCH_HIGH_12 0xb0 #define SEARCH_EQUAL_12 0xb1 #define SEARCH_LOW_12 0xb2 #define READ_ELEMENT_STATUS 0xb8 #define SEND_VOLUME_TAG 0xb6 -#define WRITE_LONG_2 0xea - -/* from hw/scsi-generic.c */ -#define REWIND 0x01 -#define REPORT_DENSITY_SUPPORT 0x44 -#define GET_CONFIGURATION 0x46 -#define READ_16 0x88 -#define WRITE_16 0x8a -#define WRITE_VERIFY_16 0x8e -#define SERVICE_ACTION_IN 0x9e -#define REPORT_LUNS 0xa0 -#define LOAD_UNLOAD 0xa6 -#define SET_CD_SPEED 0xbb -#define BLANK 0xa1 +#define READ_DEFECT_DATA_12 0xb7 +#define SET_CD_SPEED 0xbb /* * SAM Status codes @@ -154,6 +164,7 @@ #define TYPE_DISK 0x00 #define TYPE_TAPE 0x01 +#define TYPE_PRINTER 0x02 #define TYPE_PROCESSOR 0x03 /* HP scanners use this */ #define TYPE_WORM 0x04 /* Treated as ROM by our system */ #define TYPE_ROM 0x05 @@ -161,6 +172,9 @@ #define TYPE_MOD 0x07 /* Magneto-optical disk - * - treated as TYPE_DISK */ #define TYPE_MEDIUM_CHANGER 0x08 -#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */ +#define TYPE_STORAGE_ARRAY 0x0c /* Storage array device */ +#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */ +#define TYPE_RBC 0x0e /* Simplified Direct-Access Device */ +#define TYPE_OSD 0x11 /* Object-storage Device */ #define TYPE_NO_LUN 0x7f diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index a8c7372d3e..fa198f928c 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -12,7 +12,7 @@ * 2009-Oct-13 Artyom Tarasenko : implemented the block descriptor in the * MODE SENSE response. * - * This code is licenced under the LGPL. + * 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 @@ -59,8 +59,6 @@ typedef struct SCSIDiskReq { uint32_t status; } SCSIDiskReq; -typedef enum { SCSI_HD, SCSI_CD } SCSIDriveKind; - struct SCSIDiskState { SCSIDevice qdev; @@ -74,20 +72,19 @@ struct SCSIDiskState char *version; char *serial; SCSISense sense; - SCSIDriveKind drive_kind; }; static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type); static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf); static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, - uint32_t lun) + uint32_t lun, void *hba_private) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); SCSIRequest *req; SCSIDiskReq *r; - req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun); + req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun, hba_private); r = DO_UPCAST(SCSIDiskReq, req, req); r->iov.iov_base = qemu_blockalign(s->bs, SCSI_DMA_BUF_SIZE); return req; @@ -382,7 +379,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) return -1; } - if (s->drive_kind == SCSI_CD) { + if (s->qdev.type == TYPE_ROM) { outbuf[buflen++] = 5; } else { outbuf[buflen++] = 0; @@ -398,9 +395,10 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) "buffer size %zd\n", req->cmd.xfer); pages = buflen++; outbuf[buflen++] = 0x00; // list of supported pages (this page) - outbuf[buflen++] = 0x80; // unit serial number + if (s->serial) + outbuf[buflen++] = 0x80; // unit serial number outbuf[buflen++] = 0x83; // device identification - if (s->drive_kind == SCSI_HD) { + if (s->qdev.type == TYPE_DISK) { outbuf[buflen++] = 0xb0; // block limits outbuf[buflen++] = 0xb2; // thin provisioning } @@ -409,8 +407,14 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) } case 0x80: /* Device serial number, optional */ { - int l = strlen(s->serial); + int l; + if (!s->serial) { + DPRINTF("Inquiry (EVPD[Serial number] not supported\n"); + return -1; + } + + l = strlen(s->serial); if (l > req->cmd.xfer) l = req->cmd.xfer; if (l > 20) @@ -453,7 +457,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) unsigned int opt_io_size = s->qdev.conf.opt_io_size / s->qdev.blocksize; - if (s->drive_kind == SCSI_CD) { + if (s->qdev.type == TYPE_ROM) { DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", page_code); return -1; @@ -519,16 +523,15 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) memset(outbuf, 0, buflen); if (req->lun) { - outbuf[0] = 0x7f; /* LUN not supported */ + outbuf[0] = 0x7f; /* LUN not supported */ return buflen; } - if (s->drive_kind == SCSI_CD) { - outbuf[0] = 5; + outbuf[0] = s->qdev.type & 0x1f; + if (s->qdev.type == TYPE_ROM) { outbuf[1] = 0x80; memcpy(&outbuf[16], "QEMU CD-ROM ", 16); } else { - outbuf[0] = 0; outbuf[1] = s->removable ? 0x80 : 0; memcpy(&outbuf[16], "QEMU HARDDISK ", 16); } @@ -654,7 +657,7 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p, return p[1] + 2; case 0x2a: /* CD Capabilities and Mechanical Status page. */ - if (s->drive_kind != SCSI_CD) + if (s->qdev.type != TYPE_ROM) return 0; p[0] = 0x2a; p[1] = 0x14; @@ -829,7 +832,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) case TEST_UNIT_READY: if (!bdrv_is_inserted(s->bs)) goto not_ready; - break; + break; case REQUEST_SENSE: if (req->cmd.xfer < 4) goto illegal_request; @@ -841,7 +844,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) buflen = scsi_disk_emulate_inquiry(req, outbuf); if (buflen < 0) goto illegal_request; - break; + break; case MODE_SENSE: case MODE_SENSE_10: buflen = scsi_disk_emulate_mode_sense(req, outbuf); @@ -870,18 +873,18 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) goto illegal_request; break; case START_STOP: - if (s->drive_kind == SCSI_CD && (req->cmd.buf[4] & 2)) { + if (s->qdev.type == TYPE_ROM && (req->cmd.buf[4] & 2)) { /* load/eject medium */ bdrv_eject(s->bs, !(req->cmd.buf[4] & 1)); } - break; + break; case ALLOW_MEDIUM_REMOVAL: bdrv_set_locked(s->bs, req->cmd.buf[4] & 1); - break; - case READ_CAPACITY: + break; + case READ_CAPACITY_10: /* The normal LEN field for this command is zero. */ - memset(outbuf, 0, 8); - bdrv_get_geometry(s->bs, &nb_sectors); + memset(outbuf, 0, 8); + bdrv_get_geometry(s->bs, &nb_sectors); if (!nb_sectors) goto not_ready; nb_sectors /= s->cluster_size; @@ -901,7 +904,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) outbuf[6] = s->cluster_size * 2; outbuf[7] = 0; buflen = 8; - break; + break; case SYNCHRONIZE_CACHE: ret = bdrv_flush(s->bs); if (ret < 0) { @@ -963,13 +966,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) outbuf[3] = 8; buflen = 16; break; - case VERIFY: - break; - case REZERO_UNIT: - DPRINTF("Rezero Unit\n"); - if (!bdrv_is_inserted(s->bs)) { - goto not_ready; - } + case VERIFY_10: break; default: scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); @@ -1007,7 +1004,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) command = buf[0]; outbuf = (uint8_t *)r->iov.iov_base; - DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]); + DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", req->lun, req->tag, buf[0]); if (scsi_req_parse(&r->req, buf) != 0) { BADF("Unsupported command length, command %x\n", command); @@ -1045,14 +1042,13 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) case RELEASE_10: case START_STOP: case ALLOW_MEDIUM_REMOVAL: - case READ_CAPACITY: + case READ_CAPACITY_10: case SYNCHRONIZE_CACHE: case READ_TOC: case GET_CONFIGURATION: case SERVICE_ACTION_IN: case REPORT_LUNS: - case VERIFY: - case REZERO_UNIT: + case VERIFY_10: rc = scsi_disk_emulate_command(r, outbuf); if (rc < 0) { return 0; @@ -1075,7 +1071,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) case WRITE_10: case WRITE_12: case WRITE_16: - case WRITE_VERIFY: + case WRITE_VERIFY_10: case WRITE_VERIFY_12: case WRITE_VERIFY_16: len = r->req.cmd.xfer / s->qdev.blocksize; @@ -1183,7 +1179,7 @@ static void scsi_destroy(SCSIDevice *dev) blockdev_mark_auto_del(s->qdev.conf.bs); } -static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind) +static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); DriveInfo *dinfo; @@ -1193,9 +1189,8 @@ static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind) return -1; } s->bs = s->qdev.conf.bs; - s->drive_kind = kind; - if (kind == SCSI_HD && !bdrv_is_inserted(s->bs)) { + if (scsi_type == TYPE_DISK && !bdrv_is_inserted(s->bs)) { error_report("Device needs media, but drive is empty"); return -1; } @@ -1203,7 +1198,9 @@ static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind) if (!s->serial) { /* try to fall back to value set with legacy -drive serial=... */ dinfo = drive_get_by_blockdev(s->bs); - s->serial = qemu_strdup(*dinfo->serial ? dinfo->serial : "0"); + if (*dinfo->serial) { + s->serial = qemu_strdup(dinfo->serial); + } } if (!s->version) { @@ -1215,44 +1212,47 @@ static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind) return -1; } - if (kind == SCSI_CD) { + if (scsi_type == TYPE_ROM) { s->qdev.blocksize = 2048; - } else { + } else if (scsi_type == TYPE_DISK) { s->qdev.blocksize = s->qdev.conf.logical_block_size; + } else { + error_report("scsi-disk: Unhandled SCSI type %02x", scsi_type); + return -1; } s->cluster_size = s->qdev.blocksize / 512; s->bs->buffer_alignment = s->qdev.blocksize; - s->qdev.type = TYPE_DISK; + s->qdev.type = scsi_type; qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s); - bdrv_set_removable(s->bs, kind == SCSI_CD); + bdrv_set_removable(s->bs, scsi_type == TYPE_ROM); add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, ",0"); return 0; } static int scsi_hd_initfn(SCSIDevice *dev) { - return scsi_initfn(dev, SCSI_HD); + return scsi_initfn(dev, TYPE_DISK); } static int scsi_cd_initfn(SCSIDevice *dev) { - return scsi_initfn(dev, SCSI_CD); + return scsi_initfn(dev, TYPE_ROM); } static int scsi_disk_initfn(SCSIDevice *dev) { - SCSIDriveKind kind; DriveInfo *dinfo; + uint8_t scsi_type; if (!dev->conf.bs) { - kind = SCSI_HD; /* will die in scsi_initfn() */ + scsi_type = TYPE_DISK; /* will die in scsi_initfn() */ } else { dinfo = drive_get_by_blockdev(dev->conf.bs); - kind = dinfo->media_cd ? SCSI_CD : SCSI_HD; + scsi_type = dinfo->media_cd ? TYPE_ROM : TYPE_DISK; } - return scsi_initfn(dev, kind); + return scsi_initfn(dev, scsi_type); } #define DEFINE_SCSI_DISK_PROPERTIES() \ diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c index 8e59c7ee89..7b0026eb98 100644 --- a/hw/scsi-generic.c +++ b/hw/scsi-generic.c @@ -7,7 +7,7 @@ * * Written by Laurent Vivier <Laurent.Vivier@bull.net> * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. * */ @@ -96,11 +96,12 @@ static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len) return size; } -static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) +static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, + void *hba_private) { SCSIRequest *req; - req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun); + req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun, hba_private); return req; } @@ -405,7 +406,7 @@ static int get_blocksize(BlockDriverState *bdrv) memset(cmd, 0, sizeof(cmd)); memset(buf, 0, sizeof(buf)); - cmd[0] = READ_CAPACITY; + cmd[0] = READ_CAPACITY_10; memset(&io_header, 0, sizeof(io_header)); io_header.interface_id = 'S'; @@ -43,6 +43,7 @@ struct SCSIRequest { } cmd; BlockDriverAIOCB *aiocb; bool enqueued; + void *hba_private; QTAILQ_ENTRY(SCSIRequest) next; }; @@ -67,7 +68,8 @@ struct SCSIDeviceInfo { DeviceInfo qdev; scsi_qdev_initfn init; void (*destroy)(SCSIDevice *s); - SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun); + SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun, + void *hba_private); void (*free_req)(SCSIRequest *req); int32_t (*send_command)(SCSIRequest *req, uint8_t *buf); void (*read_data)(SCSIRequest *req); @@ -138,8 +140,10 @@ extern const struct SCSISense sense_code_LUN_FAILURE; int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed); int scsi_sense_valid(SCSISense sense); -SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun); -SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun); +SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, + uint32_t lun, void *hba_private); +SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, + void *hba_private); int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf); void scsi_req_free(SCSIRequest *req); SCSIRequest *scsi_req_ref(SCSIRequest *req); @@ -393,9 +393,7 @@ static void sd_reset(SDState *sd, BlockDriverState *bdrv) } else { sect = 0; } - sect <<= 9; - - size = sect + 1; + size = sect << 9; sect = (size >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) + 1; @@ -1450,14 +1448,8 @@ void sd_write_data(SDState *sd, uint8_t value) break; case 25: /* CMD25: WRITE_MULTIPLE_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->data_start += sd->blk_len; - sd->data_offset = 0; + 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; @@ -1466,6 +1458,15 @@ void sd_write_data(SDState *sd, uint8_t value) 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. */ diff --git a/hw/sh_intc.c b/hw/sh_intc.c index 0734da90f0..c43b99f811 100644 --- a/hw/sh_intc.c +++ b/hw/sh_intc.c @@ -5,7 +5,7 @@ * Based on sh_timer.c and arm_timer.c by Paul Brook * Copyright (c) 2005-2006 CodeSourcery. * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sh_intc.h" diff --git a/hw/sh_pci.c b/hw/sh_pci.c index a076cf2ff0..0ef93a062e 100644 --- a/hw/sh_pci.c +++ b/hw/sh_pci.c @@ -26,6 +26,7 @@ #include "pci.h" #include "pci_host.h" #include "bswap.h" +#include "exec-memory.h" typedef struct SHPCIState { SysBusDevice busdev; @@ -127,7 +128,8 @@ static int sh_pci_init_device(SysBusDevice *dev) } s->bus = pci_register_bus(&s->busdev.qdev, "pci", sh_pci_set_irq, sh_pci_map_irq, - s->irq, PCI_DEVFN(0, 0), 4); + s->irq, get_system_memory(), + PCI_DEVFN(0, 0), 4); s->memconfig = cpu_register_io_memory(sh_pci_reg.r, sh_pci_reg.w, s, DEVICE_NATIVE_ENDIAN); sysbus_init_mmio_cb(dev, 0x224, sh_pci_map); diff --git a/hw/sh_timer.c b/hw/sh_timer.c index 5eec6b7c14..5df7fb64bc 100644 --- a/hw/sh_timer.c +++ b/hw/sh_timer.c @@ -5,7 +5,7 @@ * Based on arm_timer.c by Paul Brook * Copyright (c) 2005-2006 CodeSourcery. * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" diff --git a/hw/smbus.c b/hw/smbus.c index e464539150..ff027c814f 100644 --- a/hw/smbus.c +++ b/hw/smbus.c @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ /* TODO: Implement PEC. */ diff --git a/hw/smc91c111.c b/hw/smc91c111.c index 701baafe6c..3a8a85c1f1 100644 --- a/hw/smc91c111.c +++ b/hw/smc91c111.c @@ -4,7 +4,7 @@ * Copyright (c) 2005 CodeSourcery, LLC. * Written by Paul Brook * - * This code is licenced under the GPL + * This code is licensed under the GPL */ #include "sysbus.h" diff --git a/hw/spapr_hcall.c b/hw/spapr_hcall.c index 5cd8d8f5ae..f7ead04a96 100644 --- a/hw/spapr_hcall.c +++ b/hw/spapr_hcall.c @@ -1,9 +1,9 @@ #include "sysemu.h" #include "cpu.h" +#include "dyngen-exec.h" #include "qemu-char.h" #include "sysemu.h" #include "qemu-char.h" -#include "exec.h" #include "helper_regs.h" #include "hw/spapr.h" diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index 1c901ef6eb..646b1e3caf 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -121,7 +121,7 @@ static struct vscsi_req *vscsi_get_req(VSCSIState *s) return NULL; } -static void vscsi_put_req(VSCSIState *s, vscsi_req *req) +static void vscsi_put_req(vscsi_req *req) { if (req->sreq != NULL) { scsi_req_unref(req->sreq); @@ -130,15 +130,6 @@ static void vscsi_put_req(VSCSIState *s, vscsi_req *req) req->active = 0; } -static vscsi_req *vscsi_find_req(VSCSIState *s, SCSIRequest *req) -{ - uint32_t tag = req->tag; - if (tag >= VSCSI_REQ_LIMIT || !s->reqs[tag].active) { - return NULL; - } - return &s->reqs[tag]; -} - static void vscsi_decode_id_lun(uint64_t srp_lun, int *id, int *lun) { /* XXX Figure that one out properly ! This is crackpot */ @@ -454,7 +445,7 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req) if (n) { req->senselen = n; vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0); - vscsi_put_req(s, req); + vscsi_put_req(req); return; } @@ -483,7 +474,7 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req) static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len) { VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent); - vscsi_req *req = vscsi_find_req(s, sreq); + vscsi_req *req = sreq->hba_private; uint8_t *buf; int rc = 0; @@ -531,7 +522,7 @@ static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len) static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status) { VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent); - vscsi_req *req = vscsi_find_req(s, sreq); + vscsi_req *req = sreq->hba_private; int32_t res_in = 0, res_out = 0; dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x status=0x%x, req=%p\n", @@ -563,15 +554,14 @@ static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status) } } vscsi_send_rsp(s, req, 0, res_in, res_out); - vscsi_put_req(s, req); + vscsi_put_req(req); } static void vscsi_request_cancelled(SCSIRequest *sreq) { - VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent); - vscsi_req *req = vscsi_find_req(s, sreq); + vscsi_req *req = sreq->hba_private; - vscsi_put_req(s, req); + vscsi_put_req(req); } static void vscsi_process_login(VSCSIState *s, vscsi_req *req) @@ -659,7 +649,7 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req) } req->lun = lun; - req->sreq = scsi_req_new(sdev, req->qtag, lun); + req->sreq = scsi_req_new(sdev, req->qtag, lun, req); n = scsi_req_enqueue(req->sreq, srp->cmd.cdb); dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x ID %d LUN %d ret: %d\n", @@ -858,7 +848,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq) } if (done) { - vscsi_put_req(s, req); + vscsi_put_req(req); } } diff --git a/hw/spitz.c b/hw/spitz.c index 006f7a97e3..c05b5f7d56 100644 --- a/hw/spitz.c +++ b/hw/spitz.c @@ -48,7 +48,7 @@ typedef struct { SysBusDevice busdev; - NANDFlashState *nand; + DeviceState *nand; uint8_t ctl; uint8_t manf_id; uint8_t chip_id; @@ -169,11 +169,13 @@ static void sl_flash_register(PXA2xxState *cpu, int size) static int sl_nand_init(SysBusDevice *dev) { int iomemtype; SLNANDState *s; + DriveInfo *nand; s = FROM_SYSBUS(SLNANDState, dev); s->ctl = 0; - s->nand = nand_init(s->manf_id, s->chip_id); + nand = drive_get(IF_MTD, 0, 0); + s->nand = nand_init(nand ? nand->bdrv : NULL, s->manf_id, s->chip_id); iomemtype = cpu_register_io_memory(sl_readfn, sl_writefn, s, DEVICE_NATIVE_ENDIAN); diff --git a/hw/ssd0303.c b/hw/ssd0303.c index b39e2596fb..401fdf592a 100644 --- a/hw/ssd0303.c +++ b/hw/ssd0303.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* The controller can support a variety of different displays, but we only diff --git a/hw/ssd0323.c b/hw/ssd0323.c index 8643961144..1eb3823fed 100644 --- a/hw/ssd0323.c +++ b/hw/ssd0323.c @@ -4,7 +4,7 @@ * Copyright (c) 2006-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ /* The controller can support a variety of different displays, but we only diff --git a/hw/ssi-sd.c b/hw/ssi-sd.c index fb4b649279..18dabd64a6 100644 --- a/hw/ssi-sd.c +++ b/hw/ssi-sd.c @@ -4,7 +4,7 @@ * Copyright (c) 2007-2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "blockdev.h" @@ -4,7 +4,7 @@ * Copyright (c) 2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GNU GPL v2. + * This code is licensed under the GNU GPL v2. */ #include "ssi.h" diff --git a/hw/stellaris.c b/hw/stellaris.c index ac9fcc1f38..a28093043a 100644 --- a/hw/stellaris.c +++ b/hw/stellaris.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" @@ -1230,7 +1230,7 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, } } - stellaris_sys_init(0x400fe000, pic[28], board, nd_table[0].macaddr); + stellaris_sys_init(0x400fe000, pic[28], board, nd_table[0].macaddr.a); for (i = 0; i < 7; i++) { if (board->dc4 & (1 << i)) { diff --git a/hw/stellaris_enet.c b/hw/stellaris_enet.c index 6a0583a256..12919317ec 100644 --- a/hw/stellaris_enet.c +++ b/hw/stellaris_enet.c @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" #include "net.h" diff --git a/hw/stellaris_input.c b/hw/stellaris_input.c index 06c5f9d955..95604ecded 100644 --- a/hw/stellaris_input.c +++ b/hw/stellaris_input.c @@ -4,7 +4,7 @@ * Copyright (c) 2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "hw.h" #include "devices.h" diff --git a/hw/sysbus.c b/hw/sysbus.c index 2e22be7b25..ea442acb50 100644 --- a/hw/sysbus.c +++ b/hw/sysbus.c @@ -19,6 +19,7 @@ #include "sysbus.h" #include "monitor.h" +#include "exec-memory.h" static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent); static char *sysbus_get_fw_dev_path(DeviceState *dev); @@ -49,11 +50,20 @@ void sysbus_mmio_map(SysBusDevice *dev, int n, target_phys_addr_t addr) } if (dev->mmio[n].addr != (target_phys_addr_t)-1) { /* Unregister previous mapping. */ - cpu_register_physical_memory(dev->mmio[n].addr, dev->mmio[n].size, - IO_MEM_UNASSIGNED); + if (dev->mmio[n].memory) { + memory_region_del_subregion(get_system_memory(), + dev->mmio[n].memory); + } else { + cpu_register_physical_memory(dev->mmio[n].addr, dev->mmio[n].size, + IO_MEM_UNASSIGNED); + } } dev->mmio[n].addr = addr; - if (dev->mmio[n].cb) { + if (dev->mmio[n].memory) { + memory_region_add_subregion(get_system_memory(), + addr, + dev->mmio[n].memory); + } else if (dev->mmio[n].cb) { dev->mmio[n].cb(dev, addr); } else { cpu_register_physical_memory(addr, dev->mmio[n].size, @@ -107,6 +117,17 @@ void sysbus_init_mmio_cb(SysBusDevice *dev, target_phys_addr_t size, dev->mmio[n].cb = cb; } +void sysbus_init_mmio_region(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].size = memory_region_size(memory); + dev->mmio[n].memory = memory; +} + void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size) { pio_addr_t i; diff --git a/hw/sysbus.h b/hw/sysbus.h index 4e8cb16d42..5f62e2da31 100644 --- a/hw/sysbus.h +++ b/hw/sysbus.h @@ -4,6 +4,7 @@ /* Devices attached directly to the main system bus. */ #include "qdev.h" +#include "memory.h" #define QDEV_MAX_MMIO 32 #define QDEV_MAX_PIO 32 @@ -23,6 +24,7 @@ struct SysBusDevice { target_phys_addr_t size; mmio_mapfunc cb; ram_addr_t iofunc; + MemoryRegion *memory; } mmio[QDEV_MAX_MMIO]; int num_pio; pio_addr_t pio[QDEV_MAX_PIO]; @@ -46,6 +48,7 @@ void sysbus_init_mmio(SysBusDevice *dev, target_phys_addr_t size, ram_addr_t iofunc); void sysbus_init_mmio_cb(SysBusDevice *dev, target_phys_addr_t size, mmio_mapfunc cb); +void sysbus_init_mmio_region(SysBusDevice *dev, MemoryRegion *memory); void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p); void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target); void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size); diff --git a/hw/tc6393xb.c b/hw/tc6393xb.c index ed49e944df..a1c48bf1d9 100644 --- a/hw/tc6393xb.c +++ b/hw/tc6393xb.c @@ -12,6 +12,7 @@ #include "flash.h" #include "console.h" #include "pixel_ops.h" +#include "blockdev.h" #define IRQ_TC6393_NAND 0 #define IRQ_TC6393_MMC 1 @@ -117,7 +118,7 @@ struct TC6393xbState { } nand; int nand_enable; uint32_t nand_phys; - NANDFlashState *flash; + DeviceState *flash; ECCState ecc; DisplayState *ds; @@ -566,6 +567,7 @@ TC6393xbState *tc6393xb_init(uint32_t base, qemu_irq irq) { int iomemtype; TC6393xbState *s; + DriveInfo *nand; CPUReadMemoryFunc * const tc6393xb_readfn[] = { tc6393xb_readb, tc6393xb_readw, @@ -586,7 +588,8 @@ TC6393xbState *tc6393xb_init(uint32_t base, qemu_irq irq) s->sub_irqs = qemu_allocate_irqs(tc6393xb_sub_irq, s, TC6393XB_NR_IRQS); - s->flash = nand_init(NAND_MFR_TOSHIBA, 0x76); + nand = drive_get(IF_MTD, 0, 0); + s->flash = nand_init(nand ? nand->bdrv : NULL, NAND_MFR_TOSHIBA, 0x76); iomemtype = cpu_register_io_memory(tc6393xb_readfn, tc6393xb_writefn, s, DEVICE_NATIVE_ENDIAN); diff --git a/hw/unin_pci.c b/hw/unin_pci.c index d364daa53a..b499523c93 100644 --- a/hw/unin_pci.c +++ b/hw/unin_pci.c @@ -201,7 +201,7 @@ static int pci_unin_internal_init_device(SysBusDevice *dev) return 0; } -PCIBus *pci_pmac_init(qemu_irq *pic) +PCIBus *pci_pmac_init(qemu_irq *pic, MemoryRegion *address_space) { DeviceState *dev; SysBusDevice *s; @@ -215,7 +215,8 @@ PCIBus *pci_pmac_init(qemu_irq *pic) d = FROM_SYSBUS(UNINState, s); d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci", pci_unin_set_irq, pci_unin_map_irq, - pic, PCI_DEVFN(11, 0), 4); + pic, address_space, + PCI_DEVFN(11, 0), 4); #if 0 pci_create_simple(d->host_state.bus, PCI_DEVFN(11, 0), "uni-north"); @@ -252,7 +253,7 @@ PCIBus *pci_pmac_init(qemu_irq *pic) return d->host_state.bus; } -PCIBus *pci_pmac_u3_init(qemu_irq *pic) +PCIBus *pci_pmac_u3_init(qemu_irq *pic, MemoryRegion *address_space) { DeviceState *dev; SysBusDevice *s; @@ -267,7 +268,8 @@ PCIBus *pci_pmac_u3_init(qemu_irq *pic) d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci", pci_unin_set_irq, pci_unin_map_irq, - pic, PCI_DEVFN(11, 0), 4); + pic, address_space, + PCI_DEVFN(11, 0), 4); sysbus_mmio_map(s, 0, 0xf0800000); sysbus_mmio_map(s, 1, 0xf0c00000); diff --git a/hw/usb-bt.c b/hw/usb-bt.c index baae4876ea..529fa3355d 100644 --- a/hw/usb-bt.c +++ b/hw/usb-bt.c @@ -99,13 +99,13 @@ static const USBDescIface desc_iface_bluetooth[] = { .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0, .bInterval = 0x01, }, @@ -120,13 +120,13 @@ static const USBDescIface desc_iface_bluetooth[] = { .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x09, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x09, .bInterval = 0x01, }, @@ -141,13 +141,13 @@ static const USBDescIface desc_iface_bluetooth[] = { .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x11, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x11, .bInterval = 0x01, }, @@ -162,13 +162,13 @@ static const USBDescIface desc_iface_bluetooth[] = { .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x19, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x19, .bInterval = 0x01, }, @@ -183,13 +183,13 @@ static const USBDescIface desc_iface_bluetooth[] = { .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x21, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x21, .bInterval = 0x01, }, @@ -204,13 +204,13 @@ static const USBDescIface desc_iface_bluetooth[] = { .eps = (USBDescEndpoint[]) { { .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x31, .bInterval = 0x01, }, { .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, - .bmAttributes = USB_ENDPOINT_XFER_INT, + .bmAttributes = USB_ENDPOINT_XFER_ISOC, .wMaxPacketSize = 0x31, .bInterval = 0x01, }, @@ -294,9 +294,9 @@ static inline int usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo, if (likely(!fifo->len)) return USB_RET_STALL; - len = MIN(p->len, fifo->fifo[fifo->start].len); - memcpy(p->data, fifo->fifo[fifo->start].data, len); - if (len == p->len) { + len = MIN(p->iov.size, fifo->fifo[fifo->start].len); + usb_packet_copy(p, fifo->fifo[fifo->start].data, len); + if (len == p->iov.size) { fifo->fifo[fifo->start].len -= len; fifo->fifo[fifo->start].data += len; } else { @@ -319,20 +319,13 @@ static inline void usb_bt_fifo_out_enqueue(struct USBBtState *s, struct usb_hci_out_fifo_s *fifo, void (*send)(struct HCIInfo *, const uint8_t *, int), int (*complete)(const uint8_t *, int), - const uint8_t *data, int len) + USBPacket *p) { - if (fifo->len) { - memcpy(fifo->data + fifo->len, data, len); - fifo->len += len; - if (complete(fifo->data, fifo->len)) { - send(s->hci, fifo->data, fifo->len); - fifo->len = 0; - } - } else if (complete(data, len)) - send(s->hci, data, len); - else { - memcpy(fifo->data, data, len); - fifo->len = len; + usb_packet_copy(p, fifo->data + fifo->len, p->iov.size); + fifo->len += p->iov.size; + if (complete(fifo->data, fifo->len)) { + send(s->hci, fifo->data, fifo->len); + fifo->len = 0; } /* TODO: do we need to loop? */ @@ -432,7 +425,7 @@ static int usb_bt_handle_control(USBDevice *dev, USBPacket *p, case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE) << 8): if (s->config) usb_bt_fifo_out_enqueue(s, &s->outcmd, s->hci->cmd_send, - usb_bt_hci_cmd_complete, data, length); + usb_bt_hci_cmd_complete, p); break; default: fail: @@ -474,12 +467,12 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p) switch (p->devep & 0xf) { case USB_ACL_EP: usb_bt_fifo_out_enqueue(s, &s->outacl, s->hci->acl_send, - usb_bt_hci_acl_complete, p->data, p->len); + usb_bt_hci_acl_complete, p); break; case USB_SCO_EP: usb_bt_fifo_out_enqueue(s, &s->outsco, s->hci->sco_send, - usb_bt_hci_sco_complete, p->data, p->len); + usb_bt_hci_sco_complete, p); break; default: @@ -548,10 +541,16 @@ USBDevice *usb_bt_init(HCIInfo *hci) return dev; } +static const VMStateDescription vmstate_usb_bt = { + .name = "usb-bt", + .unmigratable = 1, +}; + static struct USBDeviceInfo bt_info = { .product_desc = "QEMU BT dongle", .qdev.name = "usb-bt-dongle", .qdev.size = sizeof(struct USBBtState), + .qdev.vmsd = &vmstate_usb_bt, .usb_desc = &desc_bluetooth, .init = usb_bt_initfn, .handle_packet = usb_generic_handle_packet, diff --git a/hw/usb-bus.c b/hw/usb-bus.c index 2abce12de5..f1dd55eccd 100644 --- a/hw/usb-bus.c +++ b/hw/usb-bus.c @@ -82,12 +82,10 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base) static int usb_qdev_exit(DeviceState *qdev) { USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev); - USBBus *bus = usb_bus_from_device(dev); if (dev->attached) { usb_device_detach(dev); } - bus->ops->device_destroy(bus, dev); if (dev->info->handle_destroy) { dev->info->handle_destroy(dev); } @@ -140,19 +138,55 @@ USBDevice *usb_create_simple(USBBus *bus, const char *name) return dev; } -void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, - USBPortOps *ops, int speedmask) +static void usb_fill_port(USBPort *port, void *opaque, int index, + USBPortOps *ops, int speedmask) { port->opaque = opaque; port->index = index; - port->opaque = opaque; - port->index = index; port->ops = ops; port->speedmask = speedmask; + usb_port_location(port, NULL, index + 1); +} + +void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, + USBPortOps *ops, int speedmask) +{ + usb_fill_port(port, opaque, index, ops, speedmask); QTAILQ_INSERT_TAIL(&bus->free, port, next); bus->nfree++; } +int usb_register_companion(const char *masterbus, USBPort *ports[], + uint32_t portcount, uint32_t firstport, + void *opaque, USBPortOps *ops, int speedmask) +{ + USBBus *bus; + int i; + + QTAILQ_FOREACH(bus, &busses, next) { + if (strcmp(bus->qbus.name, masterbus) == 0) { + break; + } + } + + if (!bus || !bus->ops->register_companion) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus", + "an USB masterbus"); + if (bus) { + error_printf_unless_qmp( + "USB bus '%s' does not allow companion controllers\n", + masterbus); + } + return -1; + } + + for (i = 0; i < portcount; i++) { + usb_fill_port(ports[i], opaque, i, ops, speedmask); + } + + return bus->ops->register_companion(bus, ports, portcount, firstport); +} + void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr) { if (upstream) { diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c index d3922998c5..66aeb211af 100644 --- a/hw/usb-ccid.c +++ b/hw/usb-ccid.c @@ -934,16 +934,16 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p) { CCID_Header *ccid_header; - if (p->len + s->bulk_out_pos > BULK_OUT_DATA_SIZE) { + if (p->iov.size + s->bulk_out_pos > BULK_OUT_DATA_SIZE) { return USB_RET_STALL; } ccid_header = (CCID_Header *)s->bulk_out_data; - memcpy(s->bulk_out_data + s->bulk_out_pos, p->data, p->len); - s->bulk_out_pos += p->len; - if (p->len == CCID_MAX_PACKET_SIZE) { + usb_packet_copy(p, s->bulk_out_data + s->bulk_out_pos, p->iov.size); + s->bulk_out_pos += p->iov.size; + if (p->iov.size == CCID_MAX_PACKET_SIZE) { DPRINTF(s, D_VERBOSE, - "usb-ccid: bulk_in: expecting more packets (%d/%d)\n", - p->len, ccid_header->dwLength); + "usb-ccid: bulk_in: expecting more packets (%zd/%d)\n", + p->iov.size, ccid_header->dwLength); return 0; } if (s->bulk_out_pos < 10) { @@ -1006,15 +1006,17 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p) return 0; } -static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, uint8_t *data, int len) +static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p) { int ret = 0; - assert(len > 0); + assert(p->iov.size > 0); ccid_bulk_in_get(s); if (s->current_bulk_in != NULL) { - ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, len); - memcpy(data, s->current_bulk_in->data + s->current_bulk_in->pos, ret); + ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, + p->iov.size); + usb_packet_copy(p, s->current_bulk_in->data + + s->current_bulk_in->pos, ret); s->current_bulk_in->pos += ret; if (s->current_bulk_in->pos == s->current_bulk_in->len) { ccid_bulk_in_release(s); @@ -1025,11 +1027,13 @@ static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, uint8_t *data, int len) } if (ret > 0) { DPRINTF(s, D_MORE_INFO, - "%s: %d/%d req/act to guest (BULK_IN)\n", __func__, len, ret); + "%s: %zd/%d req/act to guest (BULK_IN)\n", + __func__, p->iov.size, ret); } - if (ret != USB_RET_NAK && ret < len) { + if (ret != USB_RET_NAK && ret < p->iov.size) { DPRINTF(s, 1, - "%s: returning short (EREMOTEIO) %d < %d\n", __func__, ret, len); + "%s: returning short (EREMOTEIO) %d < %zd\n", + __func__, ret, p->iov.size); } return ret; } @@ -1038,8 +1042,7 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p) { USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev); int ret = 0; - uint8_t *data = p->data; - int len = p->len; + uint8_t buf[2]; switch (p->pid) { case USB_TOKEN_OUT: @@ -1049,24 +1052,25 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p) case USB_TOKEN_IN: switch (p->devep & 0xf) { case CCID_BULK_IN_EP: - if (!len) { + if (!p->iov.size) { ret = USB_RET_NAK; } else { - ret = ccid_bulk_in_copy_to_guest(s, data, len); + ret = ccid_bulk_in_copy_to_guest(s, p); } break; case CCID_INT_IN_EP: if (s->notify_slot_change) { /* page 56, RDR_to_PC_NotifySlotChange */ - data[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange; - data[1] = s->bmSlotICCState; + buf[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange; + buf[1] = s->bmSlotICCState; + usb_packet_copy(p, buf, 2); ret = 2; s->notify_slot_change = false; s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK; DPRINTF(s, D_INFO, "handle_data: int_in: notify_slot_change %X, " - "requested len %d\n", - s->bmSlotICCState, len); + "requested len %zd\n", + s->bmSlotICCState, p->iov.size); } break; default: @@ -1104,20 +1108,9 @@ static Answer *ccid_peek_next_answer(USBCCIDState *s) : &s->pending_answers[s->pending_answers_start % PENDING_ANSWERS_NUM]; } -static void ccid_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) -{ - CCIDCardState *card = DO_UPCAST(CCIDCardState, qdev, qdev); - CCIDCardInfo *info = DO_UPCAST(CCIDCardInfo, qdev, qdev->info); - - if (info->print) { - info->print(mon, card, indent); - } -} - static struct BusInfo ccid_bus_info = { .name = "ccid-bus", .size = sizeof(CCIDBus), - .print_dev = ccid_bus_dev_print, .props = (Property[]) { DEFINE_PROP_UINT32("slot", struct CCIDCardState, slot, 0), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index 91fb7dea93..2b43895315 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -20,9 +20,6 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - * TODO: - * o Downstream port handoff */ #include "hw.h" @@ -31,6 +28,7 @@ #include "pci.h" #include "monitor.h" #include "trace.h" +#include "dma.h" #define EHCI_DEBUG 0 @@ -103,10 +101,10 @@ #define PORTSC_BEGIN PORTSC #define PORTSC_END (PORTSC + 4 * NB_PORTS) /* - * Bits that are reserverd or are read-only are masked out of values + * Bits that are reserved or are read-only are masked out of values * written to us by software */ -#define PORTSC_RO_MASK 0x007021c5 +#define PORTSC_RO_MASK 0x007001c0 #define PORTSC_RWC_MASK 0x0000002a #define PORTSC_WKOC_E (1 << 22) // Wake on Over Current Enable #define PORTSC_WKDS_E (1 << 21) // Wake on Disconnect Enable @@ -133,7 +131,7 @@ #define FRAME_TIMER_NS (1000000000 / FRAME_TIMER_FREQ) #define NB_MAXINTRATE 8 // Max rate at which controller issues ints -#define NB_PORTS 4 // Number of downstream ports +#define NB_PORTS 6 // Number of downstream ports #define BUFF_SIZE 5*4096 // Max bytes to transfer per transaction #define MAX_ITERATIONS 20 // Max number of QH before we break the loop #define MAX_QH 100 // Max allowable queue heads in a chain @@ -272,6 +270,7 @@ typedef struct EHCIqtd { uint32_t bufptr[5]; // Standard buffer pointer #define QTD_BUFPTR_MASK 0xfffff000 +#define QTD_BUFPTR_SH 12 } EHCIqtd; /* EHCI spec version 1.0 Section 3.6 @@ -360,7 +359,7 @@ struct EHCIQueue { uint32_t qtdaddr; // address QTD read from USBPacket packet; - uint8_t buffer[BUFF_SIZE]; + QEMUSGList sgl; int pid; uint32_t tbytes; enum async_state async; @@ -373,7 +372,7 @@ struct EHCIState { qemu_irq irq; target_phys_addr_t mem_base; int mem; - int num_ports; + int companion_count; /* properties */ uint32_t freq; @@ -409,6 +408,7 @@ struct EHCIState { int astate; // Current state in asynchronous schedule int pstate; // Current state in periodic schedule USBPort ports[NB_PORTS]; + USBPort *companion_ports[NB_PORTS]; uint32_t usbsts_pending; QTAILQ_HEAD(, EHCIQueue) queues; @@ -416,7 +416,7 @@ struct EHCIState { uint32_t p_fetch_addr; // which address to look at next USBPacket ipacket; - uint8_t ibuffer[BUFF_SIZE]; + QEMUSGList isgl; int isoch_pause; uint64_t last_run_ns; @@ -731,17 +731,17 @@ static void ehci_attach(USBPort *port) trace_usb_ehci_port_attach(port->index, port->dev->product_desc); + if (*portsc & PORTSC_POWNER) { + USBPort *companion = s->companion_ports[port->index]; + companion->dev = port->dev; + companion->ops->attach(companion); + return; + } + *portsc |= PORTSC_CONNECT; *portsc |= PORTSC_CSC; - /* - * If a high speed device is attached then we own this port(indicated - * by zero in the PORTSC_POWNER bit field) so set the status bit - * and set an interrupt if enabled. - */ - if ( !(*portsc & PORTSC_POWNER)) { - ehci_set_interrupt(s, USBSTS_PCD); - } + ehci_set_interrupt(s, USBSTS_PCD); } static void ehci_detach(USBPort *port) @@ -751,17 +751,88 @@ static void ehci_detach(USBPort *port) trace_usb_ehci_port_detach(port->index); - *portsc &= ~PORTSC_CONNECT; + if (*portsc & PORTSC_POWNER) { + USBPort *companion = s->companion_ports[port->index]; + companion->ops->detach(companion); + companion->dev = NULL; + return; + } + + ehci_queues_rip_device(s, port->dev); + + *portsc &= ~(PORTSC_CONNECT|PORTSC_PED); *portsc |= PORTSC_CSC; - /* - * If a high speed device is attached then we own this port(indicated - * by zero in the PORTSC_POWNER bit field) so set the status bit - * and set an interrupt if enabled. - */ - if ( !(*portsc & PORTSC_POWNER)) { - ehci_set_interrupt(s, USBSTS_PCD); + ehci_set_interrupt(s, USBSTS_PCD); +} + +static void ehci_child_detach(USBPort *port, USBDevice *child) +{ + EHCIState *s = port->opaque; + uint32_t portsc = s->portsc[port->index]; + + if (portsc & PORTSC_POWNER) { + USBPort *companion = s->companion_ports[port->index]; + companion->ops->child_detach(companion, child); + companion->dev = NULL; + return; } + + ehci_queues_rip_device(s, child); +} + +static void ehci_wakeup(USBPort *port) +{ + EHCIState *s = port->opaque; + uint32_t portsc = s->portsc[port->index]; + + if (portsc & PORTSC_POWNER) { + USBPort *companion = s->companion_ports[port->index]; + if (companion->ops->wakeup) { + companion->ops->wakeup(companion); + } + } +} + +static int ehci_register_companion(USBBus *bus, USBPort *ports[], + uint32_t portcount, uint32_t firstport) +{ + EHCIState *s = container_of(bus, EHCIState, bus); + uint32_t i; + + if (firstport + portcount > NB_PORTS) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "firstport", + "firstport on masterbus"); + error_printf_unless_qmp( + "firstport value of %u makes companion take ports %u - %u, which " + "is outside of the valid range of 0 - %u\n", firstport, firstport, + firstport + portcount - 1, NB_PORTS - 1); + return -1; + } + + for (i = 0; i < portcount; i++) { + if (s->companion_ports[firstport + i]) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus", + "an USB masterbus"); + error_printf_unless_qmp( + "port %u on masterbus %s already has a companion assigned\n", + firstport + i, bus->qbus.name); + return -1; + } + } + + for (i = 0; i < portcount; i++) { + s->companion_ports[firstport + i] = ports[i]; + s->ports[firstport + i].speedmask |= + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL; + /* Ensure devs attached before the initial reset go to the companion */ + s->portsc[firstport + i] = PORTSC_POWNER; + } + + s->companion_count++; + s->mmio[0x05] = (s->companion_count << 4) | portcount; + + return 0; } /* 4.1 host controller initialization */ @@ -769,9 +840,21 @@ static void ehci_reset(void *opaque) { EHCIState *s = opaque; int i; + USBDevice *devs[NB_PORTS]; trace_usb_ehci_reset(); + /* + * Do the detach before touching portsc, so that it correctly gets send to + * us or to our companion based on PORTSC_POWNER before the reset. + */ + for(i = 0; i < NB_PORTS; i++) { + devs[i] = s->ports[i].dev; + if (devs[i]) { + usb_attach(&s->ports[i], NULL); + } + } + memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE); s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH; @@ -783,10 +866,13 @@ static void ehci_reset(void *opaque) s->attach_poll_counter = 0; for(i = 0; i < NB_PORTS; i++) { - s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER; - - if (s->ports[i].dev) { - usb_attach(&s->ports[i], s->ports[i].dev); + if (s->companion_ports[i]) { + s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER; + } else { + s->portsc[i] = PORTSC_PPOWER; + } + if (devs[i]) { + usb_attach(&s->ports[i], devs[i]); } } ehci_queues_rip_all(s); @@ -836,43 +922,67 @@ static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val) exit(1); } +static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner) +{ + USBDevice *dev = s->ports[port].dev; + uint32_t *portsc = &s->portsc[port]; + uint32_t orig; + + if (s->companion_ports[port] == NULL) + return; + + owner = owner & PORTSC_POWNER; + orig = *portsc & PORTSC_POWNER; + + if (!(owner ^ orig)) { + return; + } + + if (dev) { + usb_attach(&s->ports[port], NULL); + } + + *portsc &= ~PORTSC_POWNER; + *portsc |= owner; + + if (dev) { + usb_attach(&s->ports[port], dev); + } +} + static void handle_port_status_write(EHCIState *s, int port, uint32_t val) { uint32_t *portsc = &s->portsc[port]; - int rwc; USBDevice *dev = s->ports[port].dev; - rwc = val & PORTSC_RWC_MASK; + /* Clear rwc bits */ + *portsc &= ~(val & PORTSC_RWC_MASK); + /* The guest may clear, but not set the PED bit */ + *portsc &= val | ~PORTSC_PED; + /* POWNER is masked out by RO_MASK as it is RO when we've no companion */ + handle_port_owner_write(s, port, val); + /* And finally apply RO_MASK */ val &= PORTSC_RO_MASK; - // handle_read_write_clear(&val, portsc, PORTSC_PEDC | PORTSC_CSC); - - *portsc &= ~rwc; - if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) { trace_usb_ehci_port_reset(port, 1); } if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) { trace_usb_ehci_port_reset(port, 0); - usb_attach(&s->ports[port], dev); - - // TODO how to handle reset of ports with no device if (dev) { + usb_attach(&s->ports[port], dev); usb_send_msg(dev, USB_MSG_RESET); - } - - if (s->ports[port].dev) { *portsc &= ~PORTSC_CSC; } - /* Table 2.16 Set the enable bit(and enable bit change) to indicate + /* + * Table 2.16 Set the enable bit(and enable bit change) to indicate * to SW that this port has a high speed device attached - * - * TODO - when to disable? */ - val |= PORTSC_PED; - val |= PORTSC_PEDC; + if (dev && (dev->speedmask & USB_SPEED_MASK_HIGH)) { + val |= PORTSC_PED; + } } *portsc &= ~PORTSC_RO_MASK; @@ -955,7 +1065,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) val &= 0x1; if (val) { for(i = 0; i < NB_PORTS; i++) - s->portsc[i] &= ~PORTSC_POWNER; + handle_port_owner_write(s, i, 0); } break; @@ -1057,68 +1167,75 @@ static int ehci_qh_do_overlay(EHCIQueue *q) return 0; } -static int ehci_buffer_rw(EHCIQueue *q, int bytes, int rw) +static int ehci_init_transfer(EHCIQueue *q) { - int bufpos = 0; - int cpage, offset; - uint32_t head; - uint32_t tail; - - - if (!bytes) { - return 0; - } - - cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); - if (cpage > 4) { - fprintf(stderr, "cpage out of range (%d)\n", cpage); - return USB_RET_PROCERR; - } + uint32_t cpage, offset, bytes, plen; + target_phys_addr_t page; + cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); + bytes = get_field(q->qh.token, QTD_TOKEN_TBYTES); offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK; + qemu_sglist_init(&q->sgl, 5); - do { - /* start and end of this page */ - head = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK; - tail = head + ~QTD_BUFPTR_MASK + 1; - /* add offset into page */ - head |= offset; - - if (bytes <= (tail - head)) { - tail = head + bytes; + while (bytes > 0) { + if (cpage > 4) { + fprintf(stderr, "cpage out of range (%d)\n", cpage); + return USB_RET_PROCERR; } - trace_usb_ehci_data(rw, cpage, offset, head, tail-head, bufpos); - cpu_physical_memory_rw(head, q->buffer + bufpos, tail - head, rw); - - bufpos += (tail - head); - offset += (tail - head); - bytes -= (tail - head); - - if (bytes > 0) { - cpage++; + page = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK; + page += offset; + plen = bytes; + if (plen > 4096 - offset) { + plen = 4096 - offset; offset = 0; + cpage++; } - } while (bytes > 0); - /* save cpage */ - set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE); + qemu_sglist_add(&q->sgl, page, plen); + bytes -= plen; + } + return 0; +} - /* save offset into cpage */ - q->qh.bufptr[0] &= QTD_BUFPTR_MASK; - q->qh.bufptr[0] |= offset; +static void ehci_finish_transfer(EHCIQueue *q, int status) +{ + uint32_t cpage, offset; - return 0; + qemu_sglist_destroy(&q->sgl); + + if (status > 0) { + /* update cpage & offset */ + cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); + offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK; + + offset += status; + cpage += offset >> QTD_BUFPTR_SH; + offset &= ~QTD_BUFPTR_MASK; + + set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE); + q->qh.bufptr[0] &= QTD_BUFPTR_MASK; + q->qh.bufptr[0] |= offset; + } } -static void ehci_async_complete_packet(USBDevice *dev, USBPacket *packet) +static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) { - EHCIQueue *q = container_of(packet, EHCIQueue, packet); + EHCIQueue *q; + EHCIState *s = port->opaque; + uint32_t portsc = s->portsc[port->index]; + if (portsc & PORTSC_POWNER) { + USBPort *companion = s->companion_ports[port->index]; + companion->ops->complete(companion, packet); + return; + } + + q = container_of(packet, EHCIQueue, packet); trace_usb_ehci_queue_action(q, "wakeup"); assert(q->async == EHCI_ASYNC_INFLIGHT); q->async = EHCI_ASYNC_FINISHED; - q->usb_status = packet->len; + q->usb_status = packet->result; } static void ehci_execute_complete(EHCIQueue *q) @@ -1178,10 +1295,6 @@ err: } if (q->tbytes && q->pid == USB_TOKEN_IN) { - if (ehci_buffer_rw(q, q->usb_status, 1) != 0) { - q->usb_status = USB_RET_PROCERR; - return; - } q->tbytes -= q->usb_status; } else { q->tbytes = 0; @@ -1190,6 +1303,8 @@ err: DPRINTF("updating tbytes to %d\n", q->tbytes); set_field(&q->qh.token, q->tbytes, QTD_TOKEN_TBYTES); } + ehci_finish_transfer(q, q->usb_status); + usb_packet_unmap(&q->packet); q->qh.token ^= QTD_TOKEN_DTOGGLE; q->qh.token &= ~QTD_TOKEN_ACTIVE; @@ -1229,8 +1344,7 @@ static int ehci_execute(EHCIQueue *q) default: fprintf(stderr, "bad token\n"); break; } - if ((q->tbytes && q->pid != USB_TOKEN_IN) && - (ehci_buffer_rw(q, q->tbytes, 0) != 0)) { + if (ehci_init_transfer(q) != 0) { return USB_RET_PROCERR; } @@ -1239,30 +1353,26 @@ static int ehci_execute(EHCIQueue *q) ret = USB_RET_NODEV; + usb_packet_setup(&q->packet, q->pid, devadr, endp); + usb_packet_map(&q->packet, &q->sgl); + // TO-DO: associating device with ehci port for(i = 0; i < NB_PORTS; i++) { port = &q->ehci->ports[i]; dev = port->dev; - // TODO sometime we will also need to check if we are the port owner - if (!(q->ehci->portsc[i] &(PORTSC_CONNECT))) { DPRINTF("Port %d, no exec, not connected(%08X)\n", i, q->ehci->portsc[i]); continue; } - q->packet.pid = q->pid; - q->packet.devaddr = devadr; - q->packet.devep = endp; - q->packet.data = q->buffer; - q->packet.len = q->tbytes; - ret = usb_handle_packet(dev, &q->packet); - DPRINTF("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d\n", + DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd " + "(total %d) endp %x ret %d\n", q->qhaddr, q->qh.next, q->qtdaddr, q->pid, - q->packet.len, q->tbytes, endp, ret); + q->packet.iov.size, q->tbytes, endp, ret); if (ret != USB_RET_NODEV) { break; @@ -1286,7 +1396,7 @@ static int ehci_process_itd(EHCIState *ehci, USBPort *port; USBDevice *dev; int ret; - uint32_t i, j, len, len1, len2, pid, dir, devaddr, endp; + uint32_t i, j, len, pid, dir, devaddr, endp; uint32_t pg, off, ptr1, ptr2, max, mult; dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION); @@ -1311,45 +1421,31 @@ static int ehci_process_itd(EHCIState *ehci, return USB_RET_PROCERR; } + qemu_sglist_init(&ehci->isgl, 2); if (off + len > 4096) { /* transfer crosses page border */ - len2 = off + len - 4096; - len1 = len - len2; + uint32_t len2 = off + len - 4096; + uint32_t len1 = len - len2; + qemu_sglist_add(&ehci->isgl, ptr1 + off, len1); + qemu_sglist_add(&ehci->isgl, ptr2, len2); } else { - len1 = len; - len2 = 0; + qemu_sglist_add(&ehci->isgl, ptr1 + off, len); } - if (!dir) { - pid = USB_TOKEN_OUT; - trace_usb_ehci_data(0, pg, off, ptr1 + off, len1, 0); - cpu_physical_memory_rw(ptr1 + off, &ehci->ibuffer[0], len1, 0); - if (len2) { - trace_usb_ehci_data(0, pg+1, 0, ptr2, len2, len1); - cpu_physical_memory_rw(ptr2, &ehci->ibuffer[len1], len2, 0); - } - } else { - pid = USB_TOKEN_IN; - } + pid = dir ? USB_TOKEN_IN : USB_TOKEN_OUT; - ret = USB_RET_NODEV; + usb_packet_setup(&ehci->ipacket, pid, devaddr, endp); + usb_packet_map(&ehci->ipacket, &ehci->isgl); + ret = USB_RET_NODEV; for (j = 0; j < NB_PORTS; j++) { port = &ehci->ports[j]; dev = port->dev; - // TODO sometime we will also need to check if we are the port owner - if (!(ehci->portsc[j] &(PORTSC_CONNECT))) { continue; } - ehci->ipacket.pid = pid; - ehci->ipacket.devaddr = devaddr; - ehci->ipacket.devep = endp; - ehci->ipacket.data = ehci->ibuffer; - ehci->ipacket.len = len; - ret = usb_handle_packet(dev, &ehci->ipacket); if (ret != USB_RET_NODEV) { @@ -1357,6 +1453,9 @@ static int ehci_process_itd(EHCIState *ehci, } } + usb_packet_unmap(&ehci->ipacket); + qemu_sglist_destroy(&ehci->isgl); + #if 0 /* In isoch, there is no facility to indicate a NAK so let's * instead just complete a zero-byte transaction. Setting @@ -1394,20 +1493,6 @@ static int ehci_process_itd(EHCIState *ehci, set_field(&itd->transact[i], len - ret, ITD_XACT_LENGTH); } else { /* IN */ - if (len1 > ret) { - len1 = ret; - } - if (len2 > ret - len1) { - len2 = ret - len1; - } - if (len1) { - trace_usb_ehci_data(1, pg, off, ptr1 + off, len1, 0); - cpu_physical_memory_rw(ptr1 + off, &ehci->ibuffer[0], len1, 1); - } - if (len2) { - trace_usb_ehci_data(1, pg+1, 0, ptr2, len2, len1); - cpu_physical_memory_rw(ptr2, &ehci->ibuffer[len1], len2, 1); - } set_field(&itd->transact[i], ret, ITD_XACT_LENGTH); } @@ -2117,38 +2202,55 @@ static void ehci_map(PCIDevice *pci_dev, int region_num, cpu_register_physical_memory(addr, size, s->mem); } -static void ehci_device_destroy(USBBus *bus, USBDevice *dev) -{ - EHCIState *s = container_of(bus, EHCIState, bus); - - ehci_queues_rip_device(s, dev); -} - static int usb_ehci_initfn(PCIDevice *dev); static USBPortOps ehci_port_ops = { .attach = ehci_attach, .detach = ehci_detach, + .child_detach = ehci_child_detach, + .wakeup = ehci_wakeup, .complete = ehci_async_complete_packet, }; static USBBusOps ehci_bus_ops = { - .device_destroy = ehci_device_destroy, + .register_companion = ehci_register_companion, +}; + +static const VMStateDescription vmstate_ehci = { + .name = "ehci", + .unmigratable = 1, }; -static PCIDeviceInfo ehci_info = { - .qdev.name = "usb-ehci", - .qdev.size = sizeof(EHCIState), - .init = usb_ehci_initfn, - .vendor_id = PCI_VENDOR_ID_INTEL, - .device_id = PCI_DEVICE_ID_INTEL_82801D, - .revision = 0x10, - .class_id = PCI_CLASS_SERIAL_USB, - .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("freq", EHCIState, freq, FRAME_TIMER_FREQ), - DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128), - DEFINE_PROP_END_OF_LIST(), - }, +static Property ehci_properties[] = { + DEFINE_PROP_UINT32("freq", EHCIState, freq, FRAME_TIMER_FREQ), + DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128), + DEFINE_PROP_END_OF_LIST(), +}; + +static PCIDeviceInfo ehci_info[] = { + { + .qdev.name = "usb-ehci", + .qdev.size = sizeof(EHCIState), + .qdev.vmsd = &vmstate_ehci, + .init = usb_ehci_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801D, /* ich4 */ + .revision = 0x10, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = ehci_properties, + },{ + .qdev.name = "ich9-usb-ehci1", + .qdev.size = sizeof(EHCIState), + .qdev.vmsd = &vmstate_ehci, + .init = usb_ehci_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_EHCI1, + .revision = 0x03, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = ehci_properties, + },{ + /* end of list */ + } }; static int usb_ehci_initfn(PCIDevice *dev) @@ -2206,7 +2308,6 @@ static int usb_ehci_initfn(PCIDevice *dev) for(i = 0; i < NB_PORTS; i++) { usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops, USB_SPEED_MASK_HIGH); - usb_port_location(&s->ports[i], NULL, i+1); s->ports[i].dev = 0; } @@ -2228,7 +2329,7 @@ static int usb_ehci_initfn(PCIDevice *dev) static void ehci_register(void) { - pci_qdev_register(&ehci_info); + pci_qdev_register_many(ehci_info); } device_init(ehci_register); diff --git a/hw/usb-hid.c b/hw/usb-hid.c index d711b5c0be..e5d57de888 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -27,6 +27,7 @@ #include "usb.h" #include "usb-desc.h" #include "qemu-timer.h" +#include "hid.h" /* HID interface requests */ #define GET_REPORT 0xa101 @@ -41,46 +42,9 @@ #define USB_DT_REPORT 0x22 #define USB_DT_PHY 0x23 -#define USB_MOUSE 1 -#define USB_TABLET 2 -#define USB_KEYBOARD 3 - -typedef struct USBPointerEvent { - int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */ - int32_t dz, buttons_state; -} USBPointerEvent; - -#define QUEUE_LENGTH 16 /* should be enough for a triple-click */ -#define QUEUE_MASK (QUEUE_LENGTH-1u) -#define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK) - -typedef struct USBMouseState { - USBPointerEvent queue[QUEUE_LENGTH]; - int mouse_grabbed; - QEMUPutMouseEntry *eh_entry; -} USBMouseState; - -typedef struct USBKeyboardState { - uint32_t keycodes[QUEUE_LENGTH]; - uint16_t modifiers; - uint8_t leds; - uint8_t key[16]; - int32_t keys; -} USBKeyboardState; - typedef struct USBHIDState { USBDevice dev; - union { - USBMouseState ptr; - USBKeyboardState kbd; - }; - uint32_t head; /* index into circular queue */ - uint32_t n; - int kind; - int32_t protocol; - uint8_t idle; - int64_t next_idle_clock; - int changed; + HIDState hid; void *datain_opaque; void (*datain)(void *); } USBHIDState; @@ -394,342 +358,29 @@ static const uint8_t qemu_keyboard_hid_report_descriptor[] = { 0xc0, /* End Collection */ }; -#define USB_HID_USAGE_ERROR_ROLLOVER 0x01 -#define USB_HID_USAGE_POSTFAIL 0x02 -#define USB_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 usb_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, -}; - -static void usb_hid_changed(USBHIDState *hs) -{ - hs->changed = 1; - - if (hs->datain) - hs->datain(hs->datain_opaque); - - usb_wakeup(&hs->dev); -} - -static void usb_pointer_event_clear(USBPointerEvent *e, int buttons) { - e->xdx = e->ydy = e->dz = 0; - e->buttons_state = buttons; -} - -static void usb_pointer_event_combine(USBPointerEvent *e, int xyrel, - int x1, int y1, int z1) { - if (xyrel) { - e->xdx += x1; - e->ydy += y1; - } else { - e->xdx = x1; - e->ydy = y1; - } - e->dz += z1; -} - -static void usb_pointer_event(void *opaque, - int x1, int y1, int z1, int buttons_state) -{ - USBHIDState *hs = opaque; - USBMouseState *s = &hs->ptr; - 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. */ - s->queue[use_slot].buttons_state = buttons_state; - } else if (hs->n < 2 || - s->queue[use_slot].buttons_state != buttons_state || - s->queue[previous_slot].buttons_state != s->queue[use_slot].buttons_state) { - /* Cannot or should not combine, so add an empty item to the queue. */ - QUEUE_INCR(use_slot); - hs->n++; - usb_pointer_event_clear(&s->queue[use_slot], buttons_state); - } - usb_pointer_event_combine(&s->queue[use_slot], - hs->kind == USB_MOUSE, - x1, y1, z1); - usb_hid_changed(hs); -} - -static void usb_keyboard_event(void *opaque, int keycode) -{ - USBHIDState *hs = opaque; - USBKeyboardState *s = &hs->kbd; - 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++; - s->keycodes[slot] = keycode; - usb_hid_changed(hs); -} - -static void usb_keyboard_process_keycode(USBHIDState *hs) -{ - USBKeyboardState *s = &hs->kbd; - 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 = s->keycodes[slot]; - - key = keycode & 0x7f; - hid_code = usb_hid_usage_keys[key | ((s->modifiers >> 1) & (1 << 7))]; - s->modifiers &= ~(1 << 8); - - switch (hid_code) { - case 0x00: - return; - - case 0xe0: - if (s->modifiers & (1 << 9)) { - s->modifiers ^= 3 << 8; - usb_hid_changed(hs); - return; - } - case 0xe1 ... 0xe7: - if (keycode & (1 << 7)) { - s->modifiers &= ~(1 << (hid_code & 0x0f)); - usb_hid_changed(hs); - return; - } - case 0xe8 ... 0xef: - s->modifiers |= 1 << (hid_code & 0x0f); - usb_hid_changed(hs); - return; - } - - if (keycode & (1 << 7)) { - for (i = s->keys - 1; i >= 0; i --) - if (s->key[i] == hid_code) { - s->key[i] = s->key[-- s->keys]; - s->key[s->keys] = 0x00; - break; - } - if (i < 0) - return; - } else { - for (i = s->keys - 1; i >= 0; i --) - if (s->key[i] == hid_code) - break; - if (i < 0) { - if (s->keys < sizeof(s->key)) - s->key[s->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; -} - -static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len) -{ - int dx, dy, dz, b, l; - int index; - USBMouseState *s = &hs->ptr; - USBPointerEvent *e; - - if (!s->mouse_grabbed) { - qemu_activate_mouse_event_handler(s->eh_entry); - s->mouse_grabbed = 1; - } - - /* When the buffer is empty, return the last event. Relative - movements will all be zero. */ - index = (hs->n ? hs->head : hs->head - 1); - e = &s->queue[index & QUEUE_MASK]; - - if (hs->kind == USB_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 == USB_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 USB_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 USB_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; -} - -static int usb_keyboard_poll(USBHIDState *hs, uint8_t *buf, int len) +static void usb_hid_changed(HIDState *hs) { - USBKeyboardState *s = &hs->kbd; - if (len < 2) - return 0; - - usb_keyboard_process_keycode(hs); - - buf[0] = s->modifiers & 0xff; - buf[1] = 0; - if (s->keys > 6) - memset(buf + 2, USB_HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2); - else - memcpy(buf + 2, s->key, MIN(8, len) - 2); - - return MIN(8, len); -} + USBHIDState *us = container_of(hs, USBHIDState, hid); -static int usb_keyboard_write(USBKeyboardState *s, 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 */ - s->leds = buf[0]; - if (s->leds & 0x04) - ledstate |= QEMU_SCROLL_LOCK_LED; - if (s->leds & 0x01) - ledstate |= QEMU_NUM_LOCK_LED; - if (s->leds & 0x02) - ledstate |= QEMU_CAPS_LOCK_LED; - kbd_put_ledstate(ledstate); + if (us->datain) { + us->datain(us->datain_opaque); } - return 0; -} -static void usb_mouse_handle_reset(USBDevice *dev) -{ - USBHIDState *s = (USBHIDState *)dev; - - memset(s->ptr.queue, 0, sizeof (s->ptr.queue)); - s->head = 0; - s->n = 0; - s->protocol = 1; + usb_wakeup(&us->dev); } -static void usb_keyboard_handle_reset(USBDevice *dev) +static void usb_hid_handle_reset(USBDevice *dev) { - USBHIDState *s = (USBHIDState *)dev; - - qemu_add_kbd_event_handler(usb_keyboard_event, s); - memset(s->kbd.keycodes, 0, sizeof (s->kbd.keycodes)); - s->head = 0; - s->n = 0; - memset(s->kbd.key, 0, sizeof (s->kbd.key)); - s->kbd.keys = 0; - s->protocol = 1; -} + USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); -static void usb_hid_set_next_idle(USBHIDState *s, int64_t curtime) -{ - s->next_idle_clock = curtime + (get_ticks_per_sec() * s->idle * 4) / 1000; + hid_reset(&us->hid); } static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, int request, int value, int index, int length, uint8_t *data) { - USBHIDState *s = (USBHIDState *)dev; + USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); + HIDState *hs = &us->hid; int ret; ret = usb_desc_handle_control(dev, p, request, value, index, length, data); @@ -738,7 +389,7 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, } ret = 0; - switch(request) { + switch (request) { case DeviceRequest | USB_REQ_GET_INTERFACE: data[0] = 0; ret = 1; @@ -748,17 +399,17 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, break; /* hid specific requests */ case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: - switch(value >> 8) { + switch (value >> 8) { case 0x22: - if (s->kind == USB_MOUSE) { + if (hs->kind == HID_MOUSE) { memcpy(data, qemu_mouse_hid_report_descriptor, sizeof(qemu_mouse_hid_report_descriptor)); ret = sizeof(qemu_mouse_hid_report_descriptor); - } else if (s->kind == USB_TABLET) { - memcpy(data, qemu_tablet_hid_report_descriptor, + } else if (hs->kind == HID_TABLET) { + memcpy(data, qemu_tablet_hid_report_descriptor, sizeof(qemu_tablet_hid_report_descriptor)); ret = sizeof(qemu_tablet_hid_report_descriptor); - } else if (s->kind == USB_KEYBOARD) { + } else if (hs->kind == HID_KEYBOARD) { memcpy(data, qemu_keyboard_hid_report_descriptor, sizeof(qemu_keyboard_hid_report_descriptor)); ret = sizeof(qemu_keyboard_hid_report_descriptor); @@ -769,36 +420,40 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, } break; case GET_REPORT: - if (s->kind == USB_MOUSE || s->kind == USB_TABLET) - ret = usb_pointer_poll(s, data, length); - else if (s->kind == USB_KEYBOARD) - ret = usb_keyboard_poll(s, data, length); + if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { + ret = hid_pointer_poll(hs, data, length); + } else if (hs->kind == HID_KEYBOARD) { + ret = hid_keyboard_poll(hs, data, length); + } break; case SET_REPORT: - if (s->kind == USB_KEYBOARD) - ret = usb_keyboard_write(&s->kbd, data, length); - else + if (hs->kind == HID_KEYBOARD) { + ret = hid_keyboard_write(hs, data, length); + } else { goto fail; + } break; case GET_PROTOCOL: - if (s->kind != USB_KEYBOARD && s->kind != USB_MOUSE) + if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) { goto fail; + } ret = 1; - data[0] = s->protocol; + data[0] = hs->protocol; break; case SET_PROTOCOL: - if (s->kind != USB_KEYBOARD && s->kind != USB_MOUSE) + if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) { goto fail; + } ret = 0; - s->protocol = value; + hs->protocol = value; break; case GET_IDLE: ret = 1; - data[0] = s->idle; + data[0] = hs->idle; break; case SET_IDLE: - s->idle = (uint8_t) (value >> 8); - usb_hid_set_next_idle(s, qemu_get_clock_ns(vm_clock)); + hs->idle = (uint8_t) (value >> 8); + hid_set_next_idle(hs, qemu_get_clock_ns(vm_clock)); ret = 0; break; default: @@ -811,23 +466,26 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) { - USBHIDState *s = (USBHIDState *)dev; + USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); + HIDState *hs = &us->hid; + uint8_t buf[p->iov.size]; int ret = 0; - switch(p->pid) { + switch (p->pid) { case USB_TOKEN_IN: if (p->devep == 1) { int64_t curtime = qemu_get_clock_ns(vm_clock); - if (!s->changed && (!s->idle || s->next_idle_clock - curtime > 0)) + if (!hid_has_events(hs) && + (!hs->idle || hs->next_idle_clock - curtime > 0)) { return USB_RET_NAK; - usb_hid_set_next_idle(s, curtime); - if (s->kind == USB_MOUSE || s->kind == USB_TABLET) { - ret = usb_pointer_poll(s, p->data, p->len); } - else if (s->kind == USB_KEYBOARD) { - ret = usb_keyboard_poll(s, p->data, p->len); + hid_set_next_idle(hs, curtime); + if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { + ret = hid_pointer_poll(hs, buf, p->iov.size); + } else if (hs->kind == HID_KEYBOARD) { + ret = hid_keyboard_poll(hs, buf, p->iov.size); } - s->changed = s->n > 0; + usb_packet_copy(p, buf, ret); } else { goto fail; } @@ -843,50 +501,33 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) static void usb_hid_handle_destroy(USBDevice *dev) { - USBHIDState *s = (USBHIDState *)dev; + USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); - switch(s->kind) { - case USB_KEYBOARD: - qemu_remove_kbd_event_handler(); - break; - default: - qemu_remove_mouse_event_handler(s->ptr.eh_entry); - } + hid_free(&us->hid); } static int usb_hid_initfn(USBDevice *dev, int kind) { - USBHIDState *s = DO_UPCAST(USBHIDState, dev, dev); + USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); usb_desc_init(dev); - s->kind = kind; - - if (s->kind == USB_MOUSE) { - s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, s, - 0, "QEMU USB Mouse"); - } else if (s->kind == USB_TABLET) { - s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, s, - 1, "QEMU USB Tablet"); - } - - /* Force poll routine to be run and grab input the first time. */ - s->changed = 1; + hid_init(&us->hid, kind, usb_hid_changed); return 0; } static int usb_tablet_initfn(USBDevice *dev) { - return usb_hid_initfn(dev, USB_TABLET); + return usb_hid_initfn(dev, HID_TABLET); } static int usb_mouse_initfn(USBDevice *dev) { - return usb_hid_initfn(dev, USB_MOUSE); + return usb_hid_initfn(dev, HID_MOUSE); } static int usb_keyboard_initfn(USBDevice *dev) { - return usb_hid_initfn(dev, USB_KEYBOARD); + return usb_hid_initfn(dev, HID_KEYBOARD); } void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *)) @@ -901,8 +542,8 @@ static int usb_hid_post_load(void *opaque, int version_id) { USBHIDState *s = opaque; - if (s->idle) { - usb_hid_set_next_idle(s, qemu_get_clock_ns(vm_clock)); + if (s->hid.idle) { + hid_set_next_idle(&s->hid, qemu_get_clock_ns(vm_clock)); } return 0; } @@ -912,10 +553,10 @@ static const VMStateDescription vmstate_usb_ptr_queue = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField []) { - VMSTATE_INT32(xdx, USBPointerEvent), - VMSTATE_INT32(ydy, USBPointerEvent), - VMSTATE_INT32(dz, USBPointerEvent), - VMSTATE_INT32(buttons_state, USBPointerEvent), + VMSTATE_INT32(xdx, HIDPointerEvent), + VMSTATE_INT32(ydy, HIDPointerEvent), + VMSTATE_INT32(dz, HIDPointerEvent), + VMSTATE_INT32(buttons_state, HIDPointerEvent), VMSTATE_END_OF_LIST() } }; @@ -926,12 +567,12 @@ static const VMStateDescription vmstate_usb_ptr = { .post_load = usb_hid_post_load, .fields = (VMStateField []) { VMSTATE_USB_DEVICE(dev, USBHIDState), - VMSTATE_STRUCT_ARRAY(ptr.queue, USBHIDState, QUEUE_LENGTH, 0, - vmstate_usb_ptr_queue, USBPointerEvent), - VMSTATE_UINT32(head, USBHIDState), - VMSTATE_UINT32(n, USBHIDState), - VMSTATE_INT32(protocol, USBHIDState), - VMSTATE_UINT8(idle, USBHIDState), + VMSTATE_STRUCT_ARRAY(hid.ptr.queue, USBHIDState, QUEUE_LENGTH, 0, + vmstate_usb_ptr_queue, HIDPointerEvent), + VMSTATE_UINT32(hid.head, USBHIDState), + VMSTATE_UINT32(hid.n, USBHIDState), + VMSTATE_INT32(hid.protocol, USBHIDState), + VMSTATE_UINT8(hid.idle, USBHIDState), VMSTATE_END_OF_LIST() } }; @@ -943,15 +584,15 @@ static const VMStateDescription vmstate_usb_kbd = { .post_load = usb_hid_post_load, .fields = (VMStateField []) { VMSTATE_USB_DEVICE(dev, USBHIDState), - VMSTATE_UINT32_ARRAY(kbd.keycodes, USBHIDState, QUEUE_LENGTH), - VMSTATE_UINT32(head, USBHIDState), - VMSTATE_UINT32(n, USBHIDState), - VMSTATE_UINT16(kbd.modifiers, USBHIDState), - VMSTATE_UINT8(kbd.leds, USBHIDState), - VMSTATE_UINT8_ARRAY(kbd.key, USBHIDState, 16), - VMSTATE_INT32(kbd.keys, USBHIDState), - VMSTATE_INT32(protocol, USBHIDState), - VMSTATE_UINT8(idle, USBHIDState), + VMSTATE_UINT32_ARRAY(hid.kbd.keycodes, USBHIDState, QUEUE_LENGTH), + VMSTATE_UINT32(hid.head, USBHIDState), + VMSTATE_UINT32(hid.n, USBHIDState), + VMSTATE_UINT16(hid.kbd.modifiers, USBHIDState), + VMSTATE_UINT8(hid.kbd.leds, USBHIDState), + VMSTATE_UINT8_ARRAY(hid.kbd.key, USBHIDState, 16), + VMSTATE_INT32(hid.kbd.keys, USBHIDState), + VMSTATE_INT32(hid.protocol, USBHIDState), + VMSTATE_UINT8(hid.idle, USBHIDState), VMSTATE_END_OF_LIST() } }; @@ -966,7 +607,7 @@ static struct USBDeviceInfo hid_info[] = { .usb_desc = &desc_tablet, .init = usb_tablet_initfn, .handle_packet = usb_generic_handle_packet, - .handle_reset = usb_mouse_handle_reset, + .handle_reset = usb_hid_handle_reset, .handle_control = usb_hid_handle_control, .handle_data = usb_hid_handle_data, .handle_destroy = usb_hid_handle_destroy, @@ -979,7 +620,7 @@ static struct USBDeviceInfo hid_info[] = { .usb_desc = &desc_mouse, .init = usb_mouse_initfn, .handle_packet = usb_generic_handle_packet, - .handle_reset = usb_mouse_handle_reset, + .handle_reset = usb_hid_handle_reset, .handle_control = usb_hid_handle_control, .handle_data = usb_hid_handle_data, .handle_destroy = usb_hid_handle_destroy, @@ -992,7 +633,7 @@ static struct USBDeviceInfo hid_info[] = { .usb_desc = &desc_keyboard, .init = usb_keyboard_initfn, .handle_packet = usb_generic_handle_packet, - .handle_reset = usb_keyboard_handle_reset, + .handle_reset = usb_hid_handle_reset, .handle_control = usb_hid_handle_control, .handle_data = usb_hid_handle_data, .handle_destroy = usb_hid_handle_destroy, diff --git a/hw/usb-hub.c b/hw/usb-hub.c index 6e2a35839d..c49c547d0c 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -138,74 +138,6 @@ static const USBDesc desc_hub = { .str = desc_strings, }; -static const uint8_t qemu_hub_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - 0x01, /* u8 bDescriptorType; Device */ - 0x10, 0x01, /* u16 bcdUSB; v1.1 */ - - 0x09, /* u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* u8 bDeviceSubClass; */ - 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ - 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ - - 0x00, 0x00, /* u16 idVendor; */ - 0x00, 0x00, /* u16 idProduct; */ - 0x01, 0x01, /* u16 bcdDevice */ - - 0x03, /* u8 iManufacturer; */ - 0x02, /* u8 iProduct; */ - 0x01, /* u8 iSerialNumber; */ - 0x01 /* u8 bNumConfigurations; */ -}; - -/* XXX: patch interrupt size */ -static const uint8_t qemu_hub_config_descriptor[] = { - - /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ - 0x19, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x00, /* u8 iConfiguration; */ - 0xe0, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 0x00, /* u8 MaxPower; */ - - /* USB 1.1: - * USB 2.0, single TT organization (mandatory): - * one interface, protocol 0 - * - * USB 2.0, multiple TT organization (optional): - * two interfaces, protocols 1 (like single TT) - * and 2 (multiple TT mode) ... config is - * sometimes settable - * NOT IMPLEMENTED - */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - 0x04, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x01, /* u8 if_bNumEndpoints; */ - 0x09, /* u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* u8 if_bInterfaceSubClass; */ - 0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ - 0x00, /* u8 if_iInterface; */ - - /* one endpoint (status change endpoint) */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ - 0x02, 0x00, /* u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ - 0xff /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ -}; - static const uint8_t qemu_hub_hub_descriptor[] = { 0x00, /* u8 bLength; patched in later */ @@ -238,6 +170,9 @@ static void usb_hub_detach(USBPort *port1) USBHubState *s = port1->opaque; USBHubPort *port = &s->ports[port1->index]; + /* Let upstream know the device on this port is gone */ + s->dev.port->ops->child_detach(s->dev.port, port1->dev); + port->wPortStatus &= ~PORT_STAT_CONNECTION; port->wPortChange |= PORT_STAT_C_CONNECTION; if (port->wPortStatus & PORT_STAT_ENABLE) { @@ -246,10 +181,18 @@ static void usb_hub_detach(USBPort *port1) } } -static void usb_hub_wakeup(USBDevice *dev) +static void usb_hub_child_detach(USBPort *port1, USBDevice *child) +{ + USBHubState *s = port1->opaque; + + /* Pass along upstream */ + s->dev.port->ops->child_detach(s->dev.port, child); +} + +static void usb_hub_wakeup(USBPort *port1) { - USBHubState *s = dev->port->opaque; - USBHubPort *port = &s->ports[dev->port->index]; + USBHubState *s = port1->opaque; + USBHubPort *port = &s->ports[port1->index]; if (port->wPortStatus & PORT_STAT_SUSPEND) { port->wPortChange |= PORT_STAT_C_SUSPEND; @@ -257,9 +200,9 @@ static void usb_hub_wakeup(USBDevice *dev) } } -static void usb_hub_complete(USBDevice *dev, USBPacket *packet) +static void usb_hub_complete(USBPort *port, USBPacket *packet) { - USBHubState *s = dev->port->opaque; + USBHubState *s = port->opaque; /* * Just pass it along upstream for now. @@ -451,11 +394,12 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p) if (p->devep == 1) { USBHubPort *port; unsigned int status; + uint8_t buf[4]; int i, n; n = (NUM_PORTS + 1 + 7) / 8; - if (p->len == 1) { /* FreeBSD workaround */ + if (p->iov.size == 1) { /* FreeBSD workaround */ n = 1; - } else if (n > p->len) { + } else if (n > p->iov.size) { return USB_RET_BABBLE; } status = 0; @@ -466,8 +410,9 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p) } if (status != 0) { for(i = 0; i < n; i++) { - p->data[i] = status >> (8 * i); + buf[i] = status >> (8 * i); } + usb_packet_copy(p, buf, n); ret = n; } else { ret = USB_RET_NAK; /* usb11 11.13.1 */ @@ -537,6 +482,7 @@ static void usb_hub_handle_destroy(USBDevice *dev) static USBPortOps usb_hub_port_ops = { .attach = usb_hub_attach, .detach = usb_hub_detach, + .child_detach = usb_hub_child_detach, .wakeup = usb_hub_wakeup, .complete = usb_hub_complete, }; diff --git a/hw/usb-libhw.c b/hw/usb-libhw.c new file mode 100644 index 0000000000..162b42bd5b --- /dev/null +++ b/hw/usb-libhw.c @@ -0,0 +1,63 @@ +/* + * QEMU USB emulation, libhw bits. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 "cpu-common.h" +#include "usb.h" +#include "dma.h" + +int usb_packet_map(USBPacket *p, QEMUSGList *sgl) +{ + int is_write = (p->pid == USB_TOKEN_IN); + target_phys_addr_t len; + void *mem; + int i; + + for (i = 0; i < sgl->nsg; i++) { + len = sgl->sg[i].len; + mem = cpu_physical_memory_map(sgl->sg[i].base, &len, + is_write); + if (!mem) { + goto err; + } + qemu_iovec_add(&p->iov, mem, len); + if (len != sgl->sg[i].len) { + goto err; + } + } + return 0; + +err: + usb_packet_unmap(p); + return -1; +} + +void usb_packet_unmap(USBPacket *p) +{ + int is_write = (p->pid == USB_TOKEN_IN); + int i; + + for (i = 0; i < p->iov.niov; i++) { + cpu_physical_memory_unmap(p->iov.iov[i].iov_base, + p->iov.iov[i].iov_len, is_write, + p->iov.iov[i].iov_len); + } +} diff --git a/hw/usb-msd.c b/hw/usb-msd.c index 86582cc723..90e57fbf6b 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -4,7 +4,7 @@ * Copyright (c) 2006 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ #include "qemu-common.h" @@ -43,8 +43,6 @@ typedef struct { enum USBMSDMode mode; uint32_t scsi_len; uint8_t *scsi_buf; - uint32_t usb_len; - uint8_t *usb_buf; uint32_t data_len; uint32_t residue; uint32_t tag; @@ -176,20 +174,14 @@ static const USBDesc desc = { .str = desc_strings, }; -static void usb_msd_copy_data(MSDState *s) +static void usb_msd_copy_data(MSDState *s, USBPacket *p) { uint32_t len; - len = s->usb_len; + len = p->iov.size - p->result; if (len > s->scsi_len) len = s->scsi_len; - if (s->mode == USB_MSDM_DATAIN) { - memcpy(s->usb_buf, s->scsi_buf, len); - } else { - memcpy(s->scsi_buf, s->usb_buf, len); - } - s->usb_len -= len; + usb_packet_copy(p, s->scsi_buf, len); s->scsi_len -= len; - s->usb_buf += len; s->scsi_buf += len; s->data_len -= len; if (s->scsi_len == 0 || s->data_len == 0) { @@ -207,8 +199,9 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p) csw.residue = s->residue; csw.status = s->result; - len = MIN(sizeof(csw), p->len); - memcpy(p->data, &csw, len); + len = MIN(sizeof(csw), p->iov.size); + usb_packet_copy(p, &csw, len); + p->result = len; } static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) @@ -216,16 +209,13 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); USBPacket *p = s->packet; - if (req->tag != s->tag) { - fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag); - } - assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV)); s->scsi_len = len; s->scsi_buf = scsi_req_get_buf(req); if (p) { - usb_msd_copy_data(s); - if (s->packet && s->usb_len == 0) { + usb_msd_copy_data(s, p); + p = s->packet; + if (p && p->result == p->iov.size) { /* Set s->packet to NULL before calling usb_packet_complete because another request may be issued before usb_packet_complete returns. */ @@ -241,9 +231,6 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status) MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); USBPacket *p = s->packet; - if (req->tag != s->tag) { - fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag); - } DPRINTF("Command complete %d\n", status); s->residue = s->data_len; s->result = status != 0; @@ -255,11 +242,9 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status) s->mode = USB_MSDM_CBW; } else { if (s->data_len) { - s->data_len -= s->usb_len; - if (s->mode == USB_MSDM_DATAIN) { - memset(s->usb_buf, 0, s->usb_len); - } - s->usb_len = 0; + int len = (p->iov.size - p->result); + usb_packet_skip(p, len); + s->data_len -= len; } if (s->data_len == 0) { s->mode = USB_MSDM_CSW; @@ -349,8 +334,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) int ret = 0; struct usb_msd_cbw cbw; uint8_t devep = p->devep; - uint8_t *data = p->data; - int len = p->len; switch (p->pid) { case USB_TOKEN_OUT: @@ -359,11 +342,11 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) switch (s->mode) { case USB_MSDM_CBW: - if (len != 31) { + if (p->iov.size != 31) { fprintf(stderr, "usb-msd: Bad CBW size"); goto fail; } - memcpy(&cbw, data, 31); + usb_packet_copy(p, &cbw, 31); if (le32_to_cpu(cbw.sig) != 0x43425355) { fprintf(stderr, "usb-msd: Bad signature %08x\n", le32_to_cpu(cbw.sig)); @@ -387,43 +370,46 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) s->tag, cbw.flags, cbw.cmd_len, s->data_len); s->residue = 0; s->scsi_len = 0; - s->req = scsi_req_new(s->scsi_dev, s->tag, 0); + s->req = scsi_req_new(s->scsi_dev, s->tag, 0, NULL); scsi_req_enqueue(s->req, cbw.cmd); /* ??? Should check that USB and SCSI data transfer directions match. */ if (s->mode != USB_MSDM_CSW && s->residue == 0) { scsi_req_continue(s->req); } - ret = len; + ret = p->result; break; case USB_MSDM_DATAOUT: - DPRINTF("Data out %d/%d\n", len, s->data_len); - if (len > s->data_len) + DPRINTF("Data out %zd/%d\n", p->iov.size, s->data_len); + if (p->iov.size > s->data_len) { goto fail; + } - s->usb_buf = data; - s->usb_len = len; if (s->scsi_len) { - usb_msd_copy_data(s); + usb_msd_copy_data(s, p); } - if (s->residue && s->usb_len) { - s->data_len -= s->usb_len; - if (s->data_len == 0) - s->mode = USB_MSDM_CSW; - s->usb_len = 0; + if (s->residue) { + int len = p->iov.size - p->result; + if (len) { + usb_packet_skip(p, len); + s->data_len -= len; + if (s->data_len == 0) { + s->mode = USB_MSDM_CSW; + } + } } - if (s->usb_len) { + if (p->result < p->iov.size) { DPRINTF("Deferring packet %p\n", p); s->packet = p; ret = USB_RET_ASYNC; } else { - ret = len; + ret = p->result; } break; default: - DPRINTF("Unexpected write (len %d)\n", len); + DPRINTF("Unexpected write (len %zd)\n", p->iov.size); goto fail; } break; @@ -434,18 +420,20 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) switch (s->mode) { case USB_MSDM_DATAOUT: - if (s->data_len != 0 || len < 13) + if (s->data_len != 0 || p->iov.size < 13) { goto fail; + } /* Waiting for SCSI write to complete. */ s->packet = p; ret = USB_RET_ASYNC; break; case USB_MSDM_CSW: - DPRINTF("Command status %d tag 0x%x, len %d\n", - s->result, s->tag, len); - if (len < 13) + DPRINTF("Command status %d tag 0x%x, len %zd\n", + s->result, s->tag, p->iov.size); + if (p->iov.size < 13) { goto fail; + } usb_msd_send_status(s, p); s->mode = USB_MSDM_CBW; @@ -453,32 +441,32 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) break; case USB_MSDM_DATAIN: - DPRINTF("Data in %d/%d, scsi_len %d\n", len, s->data_len, s->scsi_len); - if (len > s->data_len) - len = s->data_len; - s->usb_buf = data; - s->usb_len = len; + DPRINTF("Data in %zd/%d, scsi_len %d\n", + p->iov.size, s->data_len, s->scsi_len); if (s->scsi_len) { - usb_msd_copy_data(s); + usb_msd_copy_data(s, p); } - if (s->residue && s->usb_len) { - s->data_len -= s->usb_len; - memset(s->usb_buf, 0, s->usb_len); - if (s->data_len == 0) - s->mode = USB_MSDM_CSW; - s->usb_len = 0; + if (s->residue) { + int len = p->iov.size - p->result; + if (len) { + usb_packet_skip(p, len); + s->data_len -= len; + if (s->data_len == 0) { + s->mode = USB_MSDM_CSW; + } + } } - if (s->usb_len) { + if (p->result < p->iov.size) { DPRINTF("Deferring packet %p\n", p); s->packet = p; ret = USB_RET_ASYNC; } else { - ret = len; + ret = p->result; } break; default: - DPRINTF("Unexpected read (len %d)\n", len); + DPRINTF("Unexpected read (len %zd)\n", p->iov.size); goto fail; } break; @@ -623,11 +611,23 @@ static USBDevice *usb_msd_init(const char *filename) return dev; } +static const VMStateDescription vmstate_usb_msd = { + .name = "usb-storage", + .unmigratable = 1, /* FIXME: handle transactions which are in flight */ + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_USB_DEVICE(dev, MSDState), + VMSTATE_END_OF_LIST() + } +}; + static struct USBDeviceInfo msd_info = { .product_desc = "QEMU USB MSD", .qdev.name = "usb-storage", .qdev.fw_name = "storage", .qdev.size = sizeof(MSDState), + .qdev.vmsd = &vmstate_usb_msd, .usb_desc = &desc, .init = usb_msd_initfn, .handle_packet = usb_generic_handle_packet, diff --git a/hw/usb-musb.c b/hw/usb-musb.c index 21f35afa92..d3ccde9199 100644 --- a/hw/usb-musb.c +++ b/hw/usb-musb.c @@ -261,17 +261,18 @@ static void musb_attach(USBPort *port); static void musb_detach(USBPort *port); -static void musb_schedule_cb(USBDevice *dev, USBPacket *p); -static void musb_device_destroy(USBBus *bus, USBDevice *dev); +static void musb_child_detach(USBPort *port, USBDevice *child); +static void musb_schedule_cb(USBPort *port, USBPacket *p); +static void musb_async_cancel_device(MUSBState *s, USBDevice *dev); static USBPortOps musb_port_ops = { .attach = musb_attach, .detach = musb_detach, + .child_detach = musb_child_detach, .complete = musb_schedule_cb, }; static USBBusOps musb_bus_ops = { - .device_destroy = musb_device_destroy, }; typedef struct MUSBPacket MUSBPacket; @@ -364,12 +365,13 @@ struct MUSBState *musb_init(qemu_irq *irqs) s->ep[i].maxp[1] = 0x40; s->ep[i].musb = s; s->ep[i].epnum = i; + usb_packet_init(&s->ep[i].packey[0].p); + usb_packet_init(&s->ep[i].packey[1].p); } usb_bus_new(&s->bus, &musb_bus_ops, NULL /* FIXME */); usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops, USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - usb_port_location(&s->port, NULL, 1); return s; } @@ -498,10 +500,19 @@ static void musb_detach(USBPort *port) { MUSBState *s = (MUSBState *) port->opaque; + musb_async_cancel_device(s, port->dev); + musb_intr_set(s, musb_irq_disconnect, 1); musb_session_update(s, 1, s->session); } +static void musb_child_detach(USBPort *port, USBDevice *child) +{ + MUSBState *s = (MUSBState *) port->opaque; + + musb_async_cancel_device(s, child); +} + static void musb_cb_tick0(void *opaque) { MUSBEndPoint *ep = (MUSBEndPoint *) opaque; @@ -518,7 +529,7 @@ static void musb_cb_tick1(void *opaque) #define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0) -static void musb_schedule_cb(USBDevice *dev, USBPacket *packey) +static void musb_schedule_cb(USBPort *port, USBPacket *packey) { MUSBPacket *p = container_of(packey, MUSBPacket, p); MUSBEndPoint *ep = p->ep; @@ -596,12 +607,10 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep, ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT; ep->delayed_cb[dir] = cb; - ep->packey[dir].p.pid = pid; /* A wild guess on the FADDR semantics... */ - ep->packey[dir].p.devaddr = ep->faddr[idx]; - ep->packey[dir].p.devep = ep->type[idx] & 0xf; - ep->packey[dir].p.data = (void *) ep->buf[idx]; - ep->packey[dir].p.len = len; + usb_packet_setup(&ep->packey[dir].p, pid, ep->faddr[idx], + ep->type[idx] & 0xf); + usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len); ep->packey[dir].ep = ep; ep->packey[dir].dir = dir; @@ -616,7 +625,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep, } ep->status[dir] = ret; - usb_packet_complete(s->port.dev, &ep->packey[dir].p); + musb_schedule_cb(&s->port, &ep->packey[dir].p); } static void musb_tx_packet_complete(USBPacket *packey, void *opaque) @@ -729,7 +738,7 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque) if (ep->status[1] == USB_RET_STALL) { ep->status[1] = 0; - packey->len = 0; + packey->result = 0; ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL; if (!epnum) @@ -743,7 +752,7 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque) * Data-errors in Isochronous. */ if (ep->interrupt[1]) return musb_packet(s, ep, epnum, USB_TOKEN_IN, - packey->len, musb_rx_packet_complete, 1); + packey->iov.size, musb_rx_packet_complete, 1); ep->csr[1] |= MGC_M_RXCSR_DATAERROR; if (!epnum) @@ -768,14 +777,14 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque) /* TODO: check len for over/underruns of an OUT packet? */ /* TODO: perhaps make use of e->ext_size[1] here. */ - packey->len = ep->status[1]; + packey->result = ep->status[1]; if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) { ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY; if (!epnum) ep->csr[0] |= MGC_M_CSR0_RXPKTRDY; - ep->rxcount = packey->len; /* XXX: MIN(packey->len, ep->maxp[1]); */ + ep->rxcount = packey->result; /* XXX: MIN(packey->len, ep->maxp[1]); */ /* In DMA mode: assert DMA request for this EP */ } @@ -783,9 +792,8 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque) musb_rx_intr_set(s, epnum, 1); } -static void musb_device_destroy(USBBus *bus, USBDevice *dev) +static void musb_async_cancel_device(MUSBState *s, USBDevice *dev) { - MUSBState *s = container_of(bus, MUSBState, bus); int ep, dir; for (ep = 0; ep < 16; ep++) { @@ -848,12 +856,12 @@ static void musb_rx_req(MUSBState *s, int epnum) * 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */ if (ep->packey[1].p.pid == USB_TOKEN_IN && ep->status[1] >= 0 && (ep->fifostart[1]) + ep->rxcount < - ep->packey[1].p.len) { + ep->packey[1].p.iov.size) { TRACE("0x%08x, %d", ep->fifostart[1], ep->rxcount ); ep->fifostart[1] += ep->rxcount; ep->fifolen[1] = 0; - ep->rxcount = MIN(ep->packey[0].p.len - (ep->fifostart[1]), + ep->rxcount = MIN(ep->packey[0].p.iov.size - (ep->fifostart[1]), ep->maxp[1]); ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; diff --git a/hw/usb-net.c b/hw/usb-net.c index 9be709f7cf..0cb47d63b3 100644 --- a/hw/usb-net.c +++ b/hw/usb-net.c @@ -29,6 +29,7 @@ #include "net.h" #include "qemu-queue.h" #include "sysemu.h" +#include "iov.h" /*#define TRAFFIC_DEBUG*/ /* Thanks to NetChip Technologies for donating this product ID. @@ -1121,28 +1122,23 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p, static int usb_net_handle_statusin(USBNetState *s, USBPacket *p) { + le32 buf[2]; int ret = 8; - if (p->len < 8) + if (p->iov.size < 8) { return USB_RET_STALL; + } - ((le32 *) p->data)[0] = cpu_to_le32(1); - ((le32 *) p->data)[1] = cpu_to_le32(0); + buf[0] = cpu_to_le32(1); + buf[1] = cpu_to_le32(0); + usb_packet_copy(p, buf, 8); if (!s->rndis_resp.tqh_first) ret = USB_RET_NAK; #ifdef TRAFFIC_DEBUG - fprintf(stderr, "usbnet: interrupt poll len %u return %d", p->len, ret); - { - int i; - fprintf(stderr, ":"); - for (i = 0; i < ret; i++) { - if (!(i & 15)) - fprintf(stderr, "\n%04x:", i); - fprintf(stderr, " %02x", p->data[i]); - } - fprintf(stderr, "\n\n"); - } + fprintf(stderr, "usbnet: interrupt poll len %zu return %d", + p->iov.size, ret); + iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret); #endif return ret; @@ -1162,9 +1158,10 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p) return ret; } ret = s->in_len - s->in_ptr; - if (ret > p->len) - ret = p->len; - memcpy(p->data, &s->in_buf[s->in_ptr], ret); + if (ret > p->iov.size) { + ret = p->iov.size; + } + usb_packet_copy(p, &s->in_buf[s->in_ptr], ret); s->in_ptr += ret; if (s->in_ptr >= s->in_len && (is_rndis(s) || (s->in_len & (64 - 1)) || !ret)) { @@ -1173,17 +1170,8 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p) } #ifdef TRAFFIC_DEBUG - fprintf(stderr, "usbnet: data in len %u return %d", p->len, ret); - { - int i; - fprintf(stderr, ":"); - for (i = 0; i < ret; i++) { - if (!(i & 15)) - fprintf(stderr, "\n%04x:", i); - fprintf(stderr, " %02x", p->data[i]); - } - fprintf(stderr, "\n\n"); - } + fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, ret); + iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret); #endif return ret; @@ -1191,29 +1179,20 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p) static int usb_net_handle_dataout(USBNetState *s, USBPacket *p) { - int ret = p->len; + int ret = p->iov.size; int sz = sizeof(s->out_buf) - s->out_ptr; struct rndis_packet_msg_type *msg = (struct rndis_packet_msg_type *) s->out_buf; uint32_t len; #ifdef TRAFFIC_DEBUG - fprintf(stderr, "usbnet: data out len %u\n", p->len); - { - int i; - fprintf(stderr, ":"); - for (i = 0; i < p->len; i++) { - if (!(i & 15)) - fprintf(stderr, "\n%04x:", i); - fprintf(stderr, " %02x", p->data[i]); - } - fprintf(stderr, "\n\n"); - } + fprintf(stderr, "usbnet: data out len %zu\n", p->iov.size); + iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->iov.size); #endif if (sz > ret) sz = ret; - memcpy(&s->out_buf[s->out_ptr], p->data, sz); + usb_packet_copy(p, &s->out_buf[s->out_ptr], sz); s->out_ptr += sz; if (!is_rndis(s)) { @@ -1277,8 +1256,8 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p) } if (ret == USB_RET_STALL) fprintf(stderr, "usbnet: failed data transaction: " - "pid 0x%x ep 0x%x len 0x%x\n", - p->pid, p->devep, p->len); + "pid 0x%x ep 0x%x len 0x%zx\n", + p->pid, p->devep, p->iov.size); return ret; } @@ -1414,11 +1393,17 @@ static USBDevice *usb_net_init(const char *cmdline) return dev; } +static const VMStateDescription vmstate_usb_net = { + .name = "usb-net", + .unmigratable = 1, +}; + static struct USBDeviceInfo net_info = { .product_desc = "QEMU USB Network Interface", .qdev.name = "usb-net", .qdev.fw_name = "network", .qdev.size = sizeof(USBNetState), + .qdev.vmsd = &vmstate_usb_net, .usb_desc = &desc_net, .init = usb_net_initfn, .handle_packet = usb_generic_handle_packet, diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c index 1c29b9fa6c..d39bcb0c0d 100644 --- a/hw/usb-ohci.c +++ b/hw/usb-ohci.c @@ -62,7 +62,7 @@ typedef struct OHCIPort { typedef struct { USBBus bus; qemu_irq irq; - int mem; + MemoryRegion mem; int num_ports; const char *name; @@ -124,6 +124,7 @@ struct ohci_hcca { }; static void ohci_bus_stop(OHCIState *ohci); +static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev); /* Bitfields for the first word of an Endpoint Desciptor. */ #define OHCI_ED_FA_SHIFT 0 @@ -326,6 +327,7 @@ static void ohci_attach(USBPort *port1) { OHCIState *s = port1->opaque; OHCIPort *port = &s->rhport[port1->index]; + uint32_t old_state = port->ctrl; /* set connect status */ port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC; @@ -343,6 +345,10 @@ static void ohci_attach(USBPort *port1) } DPRINTF("usb-ohci: Attached port %d\n", port1->index); + + if (old_state != port->ctrl) { + ohci_set_interrupt(s, OHCI_INTR_RHSC); + } } static void ohci_detach(USBPort *port1) @@ -351,6 +357,8 @@ static void ohci_detach(USBPort *port1) OHCIPort *port = &s->rhport[port1->index]; uint32_t old_state = port->ctrl; + ohci_async_cancel_device(s, port1->dev); + /* set connect status */ if (port->ctrl & OHCI_PORT_CCS) { port->ctrl &= ~OHCI_PORT_CCS; @@ -363,19 +371,18 @@ static void ohci_detach(USBPort *port1) } DPRINTF("usb-ohci: Detached port %d\n", port1->index); - if (old_state != port->ctrl) + if (old_state != port->ctrl) { ohci_set_interrupt(s, OHCI_INTR_RHSC); + } } -static void ohci_wakeup(USBDevice *dev) +static void ohci_wakeup(USBPort *port1) { - USBBus *bus = usb_bus_from_device(dev); - OHCIState *s = container_of(bus, OHCIState, bus); - int portnum = dev->port->index; - OHCIPort *port = &s->rhport[portnum]; + OHCIState *s = port1->opaque; + OHCIPort *port = &s->rhport[port1->index]; uint32_t intr = 0; if (port->ctrl & OHCI_PORT_PSS) { - DPRINTF("usb-ohci: port %d: wakeup\n", portnum); + DPRINTF("usb-ohci: port %d: wakeup\n", port1->index); port->ctrl |= OHCI_PORT_PSSC; port->ctrl &= ~OHCI_PORT_PSS; intr = OHCI_INTR_RHSC; @@ -394,6 +401,13 @@ static void ohci_wakeup(USBDevice *dev) ohci_set_interrupt(s, intr); } +static void ohci_child_detach(USBPort *port1, USBDevice *child) +{ + OHCIState *s = port1->opaque; + + ohci_async_cancel_device(s, child); +} + /* Reset the controller */ static void ohci_reset(void *opaque) { @@ -602,7 +616,7 @@ static void ohci_copy_iso_td(OHCIState *ohci, static void ohci_process_lists(OHCIState *ohci, int completion); -static void ohci_async_complete_packet(USBDevice *dev, USBPacket *packet) +static void ohci_async_complete_packet(USBPort *port, USBPacket *packet) { OHCIState *ohci = container_of(packet, OHCIState, usb_packet); #ifdef DEBUG_PACKET @@ -763,18 +777,17 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, } if (completion) { - ret = ohci->usb_packet.len; + ret = ohci->usb_packet.result; } else { ret = USB_RET_NODEV; for (i = 0; i < ohci->num_ports; i++) { dev = ohci->rhport[i].port.dev; if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0) continue; - ohci->usb_packet.pid = pid; - ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA); - ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN); - ohci->usb_packet.data = ohci->usb_buf; - ohci->usb_packet.len = len; + usb_packet_setup(&ohci->usb_packet, pid, + OHCI_BM(ed->flags, ED_FA), + OHCI_BM(ed->flags, ED_EN)); + usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); ret = usb_handle_packet(dev, &ohci->usb_packet); if (ret != USB_RET_NODEV) break; @@ -945,7 +958,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) } #endif if (completion) { - ret = ohci->usb_packet.len; + ret = ohci->usb_packet.result; ohci->async_td = 0; ohci->async_complete = 0; } else { @@ -966,11 +979,10 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) #endif return 1; } - ohci->usb_packet.pid = pid; - ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA); - ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN); - ohci->usb_packet.data = ohci->usb_buf; - ohci->usb_packet.len = len; + usb_packet_setup(&ohci->usb_packet, pid, + OHCI_BM(ed->flags, ED_FA), + OHCI_BM(ed->flags, ED_EN)); + usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); ret = usb_handle_packet(dev, &ohci->usb_packet); if (ret != USB_RET_NODEV) break; @@ -1426,13 +1438,13 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val) return; } -static uint32_t ohci_mem_read(void *ptr, target_phys_addr_t addr) +static uint64_t ohci_mem_read(void *opaque, + target_phys_addr_t addr, + unsigned size) { - OHCIState *ohci = ptr; + OHCIState *ohci = opaque; uint32_t retval; - addr &= 0xff; - /* Only aligned reads are allowed on OHCI */ if (addr & 3) { fprintf(stderr, "usb-ohci: Mis-aligned read\n"); @@ -1549,11 +1561,12 @@ static uint32_t ohci_mem_read(void *ptr, target_phys_addr_t addr) return retval; } -static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val) +static void ohci_mem_write(void *opaque, + target_phys_addr_t addr, + uint64_t val, + unsigned size) { - OHCIState *ohci = ptr; - - addr &= 0xff; + OHCIState *ohci = opaque; /* Only aligned reads are allowed on OHCI */ if (addr & 3) { @@ -1675,43 +1688,34 @@ static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val) } } -static void ohci_device_destroy(USBBus *bus, USBDevice *dev) +static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev) { - OHCIState *ohci = container_of(bus, OHCIState, bus); - if (ohci->async_td && ohci->usb_packet.owner == dev) { usb_cancel_packet(&ohci->usb_packet); ohci->async_td = 0; } } -/* Only dword reads are defined on OHCI register space */ -static CPUReadMemoryFunc * const ohci_readfn[3]={ - ohci_mem_read, - ohci_mem_read, - ohci_mem_read -}; - -/* Only dword writes are defined on OHCI register space */ -static CPUWriteMemoryFunc * const ohci_writefn[3]={ - ohci_mem_write, - ohci_mem_write, - ohci_mem_write +static const MemoryRegionOps ohci_mem_ops = { + .read = ohci_mem_read, + .write = ohci_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, }; static USBPortOps ohci_port_ops = { .attach = ohci_attach, .detach = ohci_detach, + .child_detach = ohci_child_detach, .wakeup = ohci_wakeup, .complete = ohci_async_complete_packet, }; static USBBusOps ohci_bus_ops = { - .device_destroy = ohci_device_destroy, }; -static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, - int num_ports, uint32_t localmem_base) +static int usb_ohci_init(OHCIState *ohci, DeviceState *dev, + int num_ports, uint32_t localmem_base, + char *masterbus, uint32_t firstport) { int i; @@ -1731,43 +1735,62 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, usb_frame_time, usb_bit_time); } - ohci->mem = cpu_register_io_memory(ohci_readfn, ohci_writefn, ohci, - DEVICE_LITTLE_ENDIAN); + ohci->num_ports = num_ports; + if (masterbus) { + USBPort *ports[OHCI_MAX_PORTS]; + for(i = 0; i < num_ports; i++) { + ports[i] = &ohci->rhport[i].port; + } + if (usb_register_companion(masterbus, ports, num_ports, + firstport, ohci, &ohci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) { + return -1; + } + } else { + usb_bus_new(&ohci->bus, &ohci_bus_ops, dev); + for (i = 0; i < num_ports; i++) { + usb_register_port(&ohci->bus, &ohci->rhport[i].port, + ohci, i, &ohci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); + } + } + + memory_region_init_io(&ohci->mem, &ohci_mem_ops, ohci, "ohci", 256); ohci->localmem_base = localmem_base; ohci->name = dev->info->name; - - usb_bus_new(&ohci->bus, &ohci_bus_ops, dev); - ohci->num_ports = num_ports; - for (i = 0; i < num_ports; i++) { - usb_register_port(&ohci->bus, &ohci->rhport[i].port, ohci, i, &ohci_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - usb_port_location(&ohci->rhport[i].port, NULL, i+1); - } + usb_packet_init(&ohci->usb_packet); ohci->async_td = 0; qemu_register_reset(ohci_reset, ohci); + + return 0; } typedef struct { PCIDevice pci_dev; OHCIState state; + char *masterbus; + uint32_t num_ports; + uint32_t firstport; } OHCIPCIState; static int usb_ohci_initfn_pci(struct PCIDevice *dev) { OHCIPCIState *ohci = DO_UPCAST(OHCIPCIState, pci_dev, dev); - int num_ports = 3; ohci->pci_dev.config[PCI_CLASS_PROG] = 0x10; /* OHCI */ /* TODO: RST# value should be 0. */ ohci->pci_dev.config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */ - usb_ohci_init(&ohci->state, &dev->qdev, num_ports, 0); + if (usb_ohci_init(&ohci->state, &dev->qdev, ohci->num_ports, 0, + ohci->masterbus, ohci->firstport) != 0) { + return -1; + } ohci->state.irq = ohci->pci_dev.irq[0]; /* TODO: avoid cast below by using dev */ - pci_register_bar_simple(&ohci->pci_dev, 0, 256, 0, ohci->state.mem); + pci_register_bar_region(&ohci->pci_dev, 0, 0, &ohci->state.mem); return 0; } @@ -1787,9 +1810,10 @@ static int ohci_init_pxa(SysBusDevice *dev) { OHCISysBusState *s = FROM_SYSBUS(OHCISysBusState, dev); - usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset); + /* Cannot fail as we pass NULL for masterbus */ + usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset, NULL, 0); sysbus_init_irq(dev, &s->ohci.irq); - sysbus_init_mmio(dev, 0x1000, s->ohci.mem); + sysbus_init_mmio_region(dev, &s->ohci.mem); return 0; } @@ -1802,6 +1826,12 @@ static PCIDeviceInfo ohci_pci_info = { .vendor_id = PCI_VENDOR_ID_APPLE, .device_id = PCI_DEVICE_ID_APPLE_IPID_USB, .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = (Property[]) { + DEFINE_PROP_STRING("masterbus", OHCIPCIState, masterbus), + DEFINE_PROP_UINT32("num-ports", OHCIPCIState, num_ports, 3), + DEFINE_PROP_UINT32("firstport", OHCIPCIState, firstport, 0), + DEFINE_PROP_END_OF_LIST(), + }, }; static SysBusDeviceInfo ohci_sysbus_info = { diff --git a/hw/usb-serial.c b/hw/usb-serial.c index 59cb0fb2f7..bf2b775e83 100644 --- a/hw/usb-serial.c +++ b/hw/usb-serial.c @@ -5,7 +5,7 @@ * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org> * Written by Paul Brook, reused for FTDI by Samuel Thibault * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ #include "qemu-common.h" @@ -359,37 +359,42 @@ static int usb_serial_handle_control(USBDevice *dev, USBPacket *p, static int usb_serial_handle_data(USBDevice *dev, USBPacket *p) { USBSerialState *s = (USBSerialState *)dev; - int ret = 0; + int i, ret = 0; uint8_t devep = p->devep; - uint8_t *data = p->data; - int len = p->len; - int first_len; + struct iovec *iov; + uint8_t header[2]; + int first_len, len; switch (p->pid) { case USB_TOKEN_OUT: if (devep != 2) goto fail; - qemu_chr_write(s->cs, data, len); + for (i = 0; i < p->iov.niov; i++) { + iov = p->iov.iov + i; + qemu_chr_write(s->cs, iov->iov_base, iov->iov_len); + } break; case USB_TOKEN_IN: if (devep != 1) goto fail; first_len = RECV_BUF - s->recv_ptr; + len = p->iov.size; if (len <= 2) { ret = USB_RET_NAK; break; } - *data++ = usb_get_modem_lines(s) | 1; + header[0] = usb_get_modem_lines(s) | 1; /* We do not have the uart details */ /* handle serial break */ if (s->event_trigger && s->event_trigger & FTDI_BI) { s->event_trigger &= ~FTDI_BI; - *data = FTDI_BI; + header[1] = FTDI_BI; + usb_packet_copy(p, header, 2); ret = 2; break; } else { - *data++ = 0; + header[1] = 0; } len -= 2; if (len > s->recv_used) @@ -400,9 +405,10 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p) } if (first_len > len) first_len = len; - memcpy(data, s->recv_buf + s->recv_ptr, first_len); + usb_packet_copy(p, header, 2); + usb_packet_copy(p, s->recv_buf + s->recv_ptr, first_len); if (len > first_len) - memcpy(data + first_len, s->recv_buf, len - first_len); + usb_packet_copy(p, s->recv_buf, len - first_len); s->recv_used -= len; s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; ret = len + 2; @@ -566,10 +572,16 @@ static USBDevice *usb_braille_init(const char *unused) return dev; } +static const VMStateDescription vmstate_usb_serial = { + .name = "usb-serial", + .unmigratable = 1, +}; + static struct USBDeviceInfo serial_info = { .product_desc = "QEMU USB Serial", .qdev.name = "usb-serial", .qdev.size = sizeof(USBSerialState), + .qdev.vmsd = &vmstate_usb_serial, .usb_desc = &desc_serial, .init = usb_serial_initfn, .handle_packet = usb_generic_handle_packet, @@ -589,6 +601,7 @@ static struct USBDeviceInfo braille_info = { .product_desc = "QEMU USB Braille", .qdev.name = "usb-braille", .qdev.size = sizeof(USBSerialState), + .qdev.vmsd = &vmstate_usb_serial, .usb_desc = &desc_braille, .init = usb_serial_initfn, .handle_packet = usb_generic_handle_packet, diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 405fa7b65e..824e3a5e8b 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -30,6 +30,8 @@ #include "pci.h" #include "qemu-timer.h" #include "usb-uhci.h" +#include "iov.h" +#include "dma.h" //#define DEBUG //#define DEBUG_DUMP_DATA @@ -93,17 +95,12 @@ static const char *pid2str(int pid) #endif #ifdef DEBUG_DUMP_DATA -static void dump_data(const uint8_t *data, int len) +static void dump_data(USBPacket *p, int ret) { - int i; - - printf("uhci: data: "); - for(i = 0; i < len; i++) - printf(" %02x", data[i]); - printf("\n"); + iov_hexdump(p->iov.iov, p->iov.niov, stderr, "uhci", ret); } #else -static void dump_data(const uint8_t *data, int len) {} +static void dump_data(USBPacket *p, int ret) {} #endif typedef struct UHCIState UHCIState; @@ -115,6 +112,7 @@ typedef struct UHCIState UHCIState; */ typedef struct UHCIAsync { USBPacket packet; + QEMUSGList sgl; UHCIState *uhci; QTAILQ_ENTRY(UHCIAsync) next; uint32_t td; @@ -122,7 +120,6 @@ typedef struct UHCIAsync { int8_t valid; uint8_t isoc; uint8_t done; - uint8_t buffer[2048]; } UHCIAsync; typedef struct UHCIPort { @@ -132,7 +129,7 @@ typedef struct UHCIPort { struct UHCIState { PCIDevice dev; - USBBus bus; + USBBus bus; /* Note unused when we're a companion controller */ uint16_t cmd; /* cmd register */ uint16_t status; uint16_t intr; /* interrupt enable register */ @@ -150,6 +147,10 @@ struct UHCIState { /* Active packets */ QTAILQ_HEAD(,UHCIAsync) async_pending; uint8_t num_ports_vmstate; + + /* Properties */ + char *masterbus; + uint32_t firstport; }; typedef struct UHCI_TD { @@ -175,12 +176,16 @@ static UHCIAsync *uhci_async_alloc(UHCIState *s) async->token = 0; async->done = 0; async->isoc = 0; + usb_packet_init(&async->packet); + qemu_sglist_init(&async->sgl, 1); return async; } static void uhci_async_free(UHCIState *s, UHCIAsync *async) { + usb_packet_cleanup(&async->packet); + qemu_sglist_destroy(&async->sgl); qemu_free(async); } @@ -606,6 +611,8 @@ static void uhci_detach(USBPort *port1) UHCIState *s = port1->opaque; UHCIPort *port = &s->ports[port1->index]; + uhci_async_cancel_device(s, port1->dev); + /* set connect status */ if (port->ctrl & UHCI_PORT_CCS) { port->ctrl &= ~UHCI_PORT_CCS; @@ -620,11 +627,17 @@ static void uhci_detach(USBPort *port1) uhci_resume(s); } -static void uhci_wakeup(USBDevice *dev) +static void uhci_child_detach(USBPort *port1, USBDevice *child) { - USBBus *bus = usb_bus_from_device(dev); - UHCIState *s = container_of(bus, UHCIState, bus); - UHCIPort *port = s->ports + dev->port->index; + UHCIState *s = port1->opaque; + + uhci_async_cancel_device(s, child); +} + +static void uhci_wakeup(USBPort *port1) +{ + UHCIState *s = port1->opaque; + UHCIPort *port = &s->ports[port1->index]; if (port->ctrl & UHCI_PORT_SUSPEND && !(port->ctrl & UHCI_PORT_RD)) { port->ctrl |= UHCI_PORT_RD; @@ -636,10 +649,10 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p) { int i, ret; - DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %d\n", - pid2str(p->pid), p->devaddr, p->devep, p->len); + DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %zd\n", + pid2str(p->pid), p->devaddr, p->devep, p->iov.size); if (p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_SETUP) - dump_data(p->data, p->len); + dump_data(p, 0); ret = USB_RET_NODEV; for (i = 0; i < NB_PORTS && ret == USB_RET_NODEV; i++) { @@ -650,14 +663,14 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p) ret = usb_handle_packet(dev, p); } - DPRINTF("uhci: packet exit. ret %d len %d\n", ret, p->len); + DPRINTF("uhci: packet exit. ret %d len %zd\n", ret, p->iov.size); if (p->pid == USB_TOKEN_IN && ret > 0) - dump_data(p->data, ret); + dump_data(p, ret); return ret; } -static void uhci_async_complete(USBDevice *dev, USBPacket *packet); +static void uhci_async_complete(USBPort *port, USBPacket *packet); static void uhci_process_frame(UHCIState *s); /* return -1 if fatal error (frame must be stopped) @@ -672,7 +685,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; - ret = async->packet.len; + ret = async->packet.result; if (td->ctrl & TD_CTRL_IOS) td->ctrl &= ~TD_CTRL_ACTIVE; @@ -680,7 +693,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ if (ret < 0) goto out; - len = async->packet.len; + len = async->packet.result; td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); /* The NAK bit may have been set by a previous frame, so clear it @@ -696,11 +709,6 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ goto out; } - if (len > 0) { - /* write the data back */ - cpu_physical_memory_write(td->buffer, async->buffer, len); - } - if ((td->ctrl & TD_CTRL_SPD) && len < max_len) { *int_mask |= 0x02; /* short packet: do not update QH */ @@ -718,6 +726,9 @@ out: td->ctrl |= TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; s->status |= UHCI_STS_USBERR; + if (td->ctrl & TD_CTRL_IOC) { + *int_mask |= 0x01; + } uhci_update_irq(s); return 1; @@ -725,6 +736,9 @@ out: td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; s->status |= UHCI_STS_USBERR; + if (td->ctrl & TD_CTRL_IOC) { + *int_mask |= 0x01; + } uhci_update_irq(s); /* frame interrupted */ return -1; @@ -809,16 +823,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; - async->packet.pid = pid; - async->packet.devaddr = (td->token >> 8) & 0x7f; - async->packet.devep = (td->token >> 15) & 0xf; - async->packet.data = async->buffer; - async->packet.len = max_len; + usb_packet_setup(&async->packet, pid, (td->token >> 8) & 0x7f, + (td->token >> 15) & 0xf); + qemu_sglist_add(&async->sgl, td->buffer, max_len); + usb_packet_map(&async->packet, &async->sgl); switch(pid) { case USB_TOKEN_OUT: case USB_TOKEN_SETUP: - cpu_physical_memory_read(td->buffer, async->buffer, max_len); len = uhci_broadcast_packet(s, &async->packet); if (len >= 0) len = max_len; @@ -841,15 +853,16 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in return 2; } - async->packet.len = len; + async->packet.result = len; done: len = uhci_complete_td(s, td, async, int_mask); + usb_packet_unmap(&async->packet); uhci_async_free(s, async); return len; } -static void uhci_async_complete(USBDevice *dev, USBPacket *packet) +static void uhci_async_complete(USBPort *port, USBPacket *packet) { UHCIAsync *async = container_of(packet, UHCIAsync, packet); UHCIState *s = async->uhci; @@ -1096,22 +1109,15 @@ static void uhci_map(PCIDevice *pci_dev, int region_num, register_ioport_read(addr, 32, 1, uhci_ioport_readb, s); } -static void uhci_device_destroy(USBBus *bus, USBDevice *dev) -{ - UHCIState *s = container_of(bus, UHCIState, bus); - - uhci_async_cancel_device(s, dev); -} - static USBPortOps uhci_port_ops = { .attach = uhci_attach, .detach = uhci_detach, + .child_detach = uhci_child_detach, .wakeup = uhci_wakeup, .complete = uhci_async_complete, }; static USBBusOps uhci_bus_ops = { - .device_destroy = uhci_device_destroy, }; static int usb_uhci_common_initfn(PCIDevice *dev) @@ -1125,11 +1131,22 @@ static int usb_uhci_common_initfn(PCIDevice *dev) pci_conf[PCI_INTERRUPT_PIN] = 4; // interrupt pin 3 pci_conf[USB_SBRN] = USB_RELEASE_1; // release number - usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev); - for(i = 0; i < NB_PORTS; i++) { - usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - usb_port_location(&s->ports[i].port, NULL, i+1); + if (s->masterbus) { + USBPort *ports[NB_PORTS]; + for(i = 0; i < NB_PORTS; i++) { + ports[i] = &s->ports[i].port; + } + if (usb_register_companion(s->masterbus, ports, NB_PORTS, + s->firstport, s, &uhci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) { + return -1; + } + } else { + usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev); + for (i = 0; i < NB_PORTS; i++) { + usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); + } } s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s); s->num_ports_vmstate = NB_PORTS; @@ -1160,6 +1177,12 @@ static int usb_uhci_vt82c686b_initfn(PCIDevice *dev) return usb_uhci_common_initfn(dev); } +static Property uhci_properties[] = { + DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), + DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static PCIDeviceInfo uhci_info[] = { { .qdev.name = "piix3-usb-uhci", @@ -1170,6 +1193,7 @@ static PCIDeviceInfo uhci_info[] = { .device_id = PCI_DEVICE_ID_INTEL_82371SB_2, .revision = 0x01, .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, },{ .qdev.name = "piix4-usb-uhci", .qdev.size = sizeof(UHCIState), @@ -1179,6 +1203,7 @@ static PCIDeviceInfo uhci_info[] = { .device_id = PCI_DEVICE_ID_INTEL_82371AB_2, .revision = 0x01, .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, },{ .qdev.name = "vt82c686b-usb-uhci", .qdev.size = sizeof(UHCIState), @@ -1188,6 +1213,37 @@ static PCIDeviceInfo uhci_info[] = { .device_id = PCI_DEVICE_ID_VIA_UHCI, .revision = 0x01, .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, + },{ + .qdev.name = "ich9-usb-uhci1", + .qdev.size = sizeof(UHCIState), + .qdev.vmsd = &vmstate_uhci, + .init = usb_uhci_common_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1, + .revision = 0x03, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, + },{ + .qdev.name = "ich9-usb-uhci2", + .qdev.size = sizeof(UHCIState), + .qdev.vmsd = &vmstate_uhci, + .init = usb_uhci_common_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2, + .revision = 0x03, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, + },{ + .qdev.name = "ich9-usb-uhci3", + .qdev.size = sizeof(UHCIState), + .qdev.vmsd = &vmstate_uhci, + .init = usb_uhci_common_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3, + .revision = 0x03, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, },{ /* end of list */ } diff --git a/hw/usb-wacom.c b/hw/usb-wacom.c index 9d348e170e..25580067f2 100644 --- a/hw/usb-wacom.c +++ b/hw/usb-wacom.c @@ -308,6 +308,7 @@ static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p, static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p) { USBWacomState *s = (USBWacomState *) dev; + uint8_t buf[p->iov.size]; int ret = 0; switch (p->pid) { @@ -317,9 +318,10 @@ static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p) return USB_RET_NAK; s->changed = 0; if (s->mode == WACOM_MODE_HID) - ret = usb_mouse_poll(s, p->data, p->len); + ret = usb_mouse_poll(s, buf, p->iov.size); else if (s->mode == WACOM_MODE_WACOM) - ret = usb_wacom_poll(s, p->data, p->len); + ret = usb_wacom_poll(s, buf, p->iov.size); + usb_packet_copy(p, buf, ret); break; } /* Fall through. */ @@ -349,6 +351,11 @@ static int usb_wacom_initfn(USBDevice *dev) return 0; } +static const VMStateDescription vmstate_usb_wacom = { + .name = "usb-wacom", + .unmigratable = 1, +}; + static struct USBDeviceInfo wacom_info = { .product_desc = "QEMU PenPartner Tablet", .qdev.name = "usb-wacom-tablet", @@ -356,6 +363,7 @@ static struct USBDeviceInfo wacom_info = { .usbdevice_name = "wacom-tablet", .usb_desc = &desc_wacom, .qdev.size = sizeof(USBWacomState), + .qdev.vmsd = &vmstate_usb_wacom, .init = usb_wacom_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_wacom_handle_reset, @@ -25,6 +25,7 @@ */ #include "qemu-common.h" #include "usb.h" +#include "iov.h" void usb_attach(USBPort *port, USBDevice *dev) { @@ -40,19 +41,18 @@ void usb_attach(USBPort *port, USBDevice *dev) } else { /* detach */ dev = port->dev; + assert(dev); port->ops->detach(port); - if (dev) { - usb_send_msg(dev, USB_MSG_DETACH); - dev->port = NULL; - port->dev = NULL; - } + usb_send_msg(dev, USB_MSG_DETACH); + dev->port = NULL; + port->dev = NULL; } } void usb_wakeup(USBDevice *dev) { if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) { - dev->port->ops->wakeup(dev); + dev->port->ops->wakeup(dev->port); } } @@ -73,10 +73,11 @@ static int do_token_setup(USBDevice *s, USBPacket *p) int request, value, index; int ret = 0; - if (p->len != 8) + if (p->iov.size != 8) { return USB_RET_STALL; - - memcpy(s->setup_buf, p->data, 8); + } + + usb_packet_copy(p, s->setup_buf, p->iov.size); s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; s->setup_index = 0; @@ -145,9 +146,10 @@ static int do_token_in(USBDevice *s, USBPacket *p) case SETUP_STATE_DATA: if (s->setup_buf[0] & USB_DIR_IN) { int len = s->setup_len - s->setup_index; - if (len > p->len) - len = p->len; - memcpy(p->data, s->data_buf + s->setup_index, len); + if (len > p->iov.size) { + len = p->iov.size; + } + usb_packet_copy(p, s->data_buf + s->setup_index, len); s->setup_index += len; if (s->setup_index >= s->setup_len) s->setup_state = SETUP_STATE_ACK; @@ -180,9 +182,10 @@ static int do_token_out(USBDevice *s, USBPacket *p) case SETUP_STATE_DATA: if (!(s->setup_buf[0] & USB_DIR_IN)) { int len = s->setup_len - s->setup_index; - if (len > p->len) - len = p->len; - memcpy(s->data_buf + s->setup_index, p->data, len); + if (len > p->iov.size) { + len = p->iov.size; + } + usb_packet_copy(p, s->data_buf + s->setup_index, len); s->setup_index += len; if (s->setup_index >= s->setup_len) s->setup_state = SETUP_STATE_ACK; @@ -252,22 +255,22 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p) usb_packet_complete to complete their async control packets. */ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p) { - if (p->len < 0) { + if (p->result < 0) { s->setup_state = SETUP_STATE_IDLE; } switch (s->setup_state) { case SETUP_STATE_SETUP: - if (p->len < s->setup_len) { - s->setup_len = p->len; + if (p->result < s->setup_len) { + s->setup_len = p->result; } s->setup_state = SETUP_STATE_DATA; - p->len = 8; + p->result = 8; break; case SETUP_STATE_ACK: s->setup_state = SETUP_STATE_IDLE; - p->len = 0; + p->result = 0; break; default: @@ -335,7 +338,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p) { /* Note: p->owner != dev is possible in case dev is a hub */ assert(p->owner != NULL); - dev->port->ops->complete(dev, p); + dev->port->ops->complete(dev->port, p); p->owner = NULL; } @@ -348,3 +351,57 @@ void usb_cancel_packet(USBPacket * p) p->owner->info->cancel_packet(p->owner, p); p->owner = NULL; } + + +void usb_packet_init(USBPacket *p) +{ + qemu_iovec_init(&p->iov, 1); +} + +void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep) +{ + p->pid = pid; + p->devaddr = addr; + p->devep = ep; + p->result = 0; + qemu_iovec_reset(&p->iov); +} + +void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len) +{ + qemu_iovec_add(&p->iov, ptr, len); +} + +void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes) +{ + assert(p->result >= 0); + assert(p->result + bytes <= p->iov.size); + switch (p->pid) { + case USB_TOKEN_SETUP: + case USB_TOKEN_OUT: + iov_to_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes); + break; + case USB_TOKEN_IN: + iov_from_buf(p->iov.iov, p->iov.niov, ptr, p->result, bytes); + break; + default: + fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid); + abort(); + } + p->result += bytes; +} + +void usb_packet_skip(USBPacket *p, size_t bytes) +{ + assert(p->result >= 0); + assert(p->result + bytes <= p->iov.size); + if (p->pid == USB_TOKEN_IN) { + iov_clear(p->iov.iov, p->iov.niov, p->result, bytes); + } + p->result += bytes; +} + +void usb_packet_cleanup(USBPacket *p) +{ + qemu_iovec_destroy(&p->iov); +} @@ -252,8 +252,18 @@ struct USBDeviceInfo { typedef struct USBPortOps { void (*attach)(USBPort *port); void (*detach)(USBPort *port); - void (*wakeup)(USBDevice *dev); - void (*complete)(USBDevice *dev, USBPacket *p); + /* + * This gets called when a device downstream from the device attached to + * the port (iow attached through a hub) gets detached. + */ + void (*child_detach)(USBPort *port, USBDevice *child); + void (*wakeup)(USBPort *port); + /* + * Note that port->dev will be different then the device from which + * the packet originated when a hub is involved, if you want the orginating + * device use p->owner + */ + void (*complete)(USBPort *port, USBPacket *p); } USBPortOps; /* USB port on which a device can be connected */ @@ -275,12 +285,21 @@ struct USBPacket { int pid; uint8_t devaddr; uint8_t devep; - uint8_t *data; - int len; + QEMUIOVector iov; + int result; /* transfer length or USB_RET_* status code */ /* Internal use by the USB layer. */ USBDevice *owner; }; +void usb_packet_init(USBPacket *p); +void usb_packet_setup(USBPacket *p, int pid, uint8_t addr, uint8_t ep); +void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len); +int usb_packet_map(USBPacket *p, QEMUSGList *sgl); +void usb_packet_unmap(USBPacket *p); +void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes); +void usb_packet_skip(USBPacket *p, size_t bytes); +void usb_packet_cleanup(USBPacket *p); + int usb_handle_packet(USBDevice *dev, USBPacket *p); void usb_packet_complete(USBDevice *dev, USBPacket *p); void usb_cancel_packet(USBPacket * p); @@ -344,7 +363,8 @@ struct USBBus { }; struct USBBusOps { - void (*device_destroy)(USBBus *bus, USBDevice *dev); + int (*register_companion)(USBBus *bus, USBPort *ports[], + uint32_t portcount, uint32_t firstport); }; void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host); @@ -356,6 +376,9 @@ USBDevice *usb_create_simple(USBBus *bus, const char *name); USBDevice *usbdevice_create(const char *cmdline); void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, USBPortOps *ops, int speedmask); +int usb_register_companion(const char *masterbus, USBPort *ports[], + uint32_t portcount, uint32_t firstport, + void *opaque, USBPortOps *ops, int speedmask); void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr); void usb_unregister_port(USBBus *bus, USBPort *port); int usb_device_attach(USBDevice *dev); diff --git a/hw/versatile_pci.c b/hw/versatile_pci.c index 8e75ffccfb..cffe387187 100644 --- a/hw/versatile_pci.c +++ b/hw/versatile_pci.c @@ -4,12 +4,13 @@ * Copyright (c) 2006-2009 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the LGPL. + * This code is licensed under the LGPL. */ #include "sysbus.h" #include "pci.h" #include "pci_host.h" +#include "exec-memory.h" typedef struct { SysBusDevice busdev; @@ -111,6 +112,7 @@ static int pci_vpb_init(SysBusDevice *dev) } bus = pci_register_bus(&dev->qdev, "pci", pci_vpb_set_irq, pci_vpb_map_irq, s->irq, + get_system_memory(), PCI_DEVFN(11, 0), 4); /* ??? Register memory space. */ diff --git a/hw/versatilepb.c b/hw/versatilepb.c index 46b6a3f383..147fe29b61 100644 --- a/hw/versatilepb.c +++ b/hw/versatilepb.c @@ -4,7 +4,7 @@ * Copyright (c) 2005-2007 CodeSourcery. * Written by Paul Brook * - * This code is licenced under the GPL. + * This code is licensed under the GPL. */ #include "sysbus.h" diff --git a/hw/vga_int.h b/hw/vga_int.h index d2811bdf1c..eee91a84f3 100644 --- a/hw/vga_int.h +++ b/hw/vga_int.h @@ -106,13 +106,13 @@ typedef void (* vga_update_retrace_info_fn)(struct VGACommonState *s); typedef struct VGACommonState { uint8_t *vram_ptr; ram_addr_t vram_offset; + target_phys_addr_t lfb_addr; + target_phys_addr_t lfb_end; + target_phys_addr_t map_addr; + target_phys_addr_t map_end; uint32_t vram_size; - uint32_t lfb_addr; - uint32_t lfb_end; - uint32_t map_addr; - uint32_t map_end; - uint32_t lfb_vram_mapped; /* whether 0xa0000 is mapped as ram */ uint32_t latch; + uint32_t lfb_vram_mapped; /* whether 0xa0000 is mapped as ram */ uint8_t sr_index; uint8_t sr[256]; uint8_t gr_index; diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c index 70a8710343..072a88a382 100644 --- a/hw/virtio-balloon.c +++ b/hw/virtio-balloon.c @@ -1,7 +1,9 @@ /* - * Virtio Block Device + * Virtio Balloon Device * * Copyright IBM, Corp. 2008 + * Copyright (C) 2011 Red Hat, Inc. + * Copyright (C) 2011 Amit Shah <amit.shah@redhat.com> * * Authors: * Anthony Liguori <aliguori@us.ibm.com> @@ -43,6 +45,7 @@ typedef struct VirtIOBalloon size_t stats_vq_offset; MonitorCompletion *stats_callback; void *stats_opaque_callback_data; + DeviceState *qdev; } VirtIOBalloon; static VirtIOBalloon *to_virtio_balloon(VirtIODevice *vdev) @@ -199,36 +202,44 @@ static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f) return f; } -static void virtio_balloon_to_target(void *opaque, ram_addr_t target, - MonitorCompletion cb, void *cb_data) +static void virtio_balloon_stat(void *opaque, MonitorCompletion cb, + void *cb_data) { VirtIOBalloon *dev = opaque; - if (target > ram_size) - target = ram_size; + /* For now, only allow one request at a time. This restriction can be + * removed later by queueing callback and data pairs. + */ + 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))) { + virtqueue_push(dev->svq, &dev->stats_vq_elem, dev->stats_vq_offset); + virtio_notify(&dev->vdev, dev->svq); + return; + } + + /* 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); +} +static void virtio_balloon_to_target(void *opaque, ram_addr_t target) +{ + VirtIOBalloon *dev = opaque; + + if (target > ram_size) { + target = ram_size; + } if (target) { dev->num_pages = (ram_size - target) >> VIRTIO_BALLOON_PFN_SHIFT; virtio_notify_config(&dev->vdev); - } else { - /* For now, only allow one request at a time. This restriction can be - * removed later by queueing callback and data pairs. - */ - 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))) { - virtqueue_push(dev->svq, &dev->stats_vq_elem, dev->stats_vq_offset); - virtio_notify(&dev->vdev, dev->svq); - } else { - /* 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); - } } } @@ -259,6 +270,7 @@ static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id) VirtIODevice *virtio_balloon_init(DeviceState *dev) { VirtIOBalloon *s; + int ret; s = (VirtIOBalloon *)virtio_common_init("virtio-balloon", VIRTIO_ID_BALLOON, @@ -268,15 +280,29 @@ VirtIODevice *virtio_balloon_init(DeviceState *dev) s->vdev.set_config = virtio_balloon_set_config; s->vdev.get_features = virtio_balloon_get_features; + ret = qemu_add_balloon_handler(virtio_balloon_to_target, + virtio_balloon_stat, s); + if (ret < 0) { + virtio_cleanup(&s->vdev); + return NULL; + } + s->ivq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output); s->dvq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output); s->svq = virtio_add_queue(&s->vdev, 128, virtio_balloon_receive_stats); reset_stats(s); - qemu_add_balloon_handler(virtio_balloon_to_target, s); + s->qdev = dev; register_savevm(dev, "virtio-balloon", -1, 1, virtio_balloon_save, virtio_balloon_load, s); return &s->vdev; } + +void virtio_balloon_exit(VirtIODevice *vdev) +{ + VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev); + unregister_savevm(s->qdev, "virtio-balloon", s); + virtio_cleanup(vdev); +} diff --git a/hw/virtio-console.c b/hw/virtio-console.c index b076331d37..fe5e188bf4 100644 --- a/hw/virtio-console.c +++ b/hw/virtio-console.c @@ -12,6 +12,7 @@ #include "qemu-char.h" #include "qemu-error.h" +#include "trace.h" #include "virtio-serial.h" typedef struct VirtConsole { @@ -24,8 +25,26 @@ typedef struct VirtConsole { static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len) { VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); - - return qemu_chr_write(vcon->chr, buf, len); + ssize_t ret; + + ret = qemu_chr_write(vcon->chr, buf, len); + trace_virtio_console_flush_buf(port->id, len, ret); + + if (ret < 0) { + /* + * 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. Absent such error messages, the most we + * can do is to return 0 here. + * + * This will prevent stray -1 values to go to + * virtio-serial-bus.c and cause abort()s in + * do_flush_queued_data(). + */ + ret = 0; + } + return ret; } /* Callback function that's called when the guest opens the port */ @@ -57,6 +76,7 @@ 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); } @@ -64,6 +84,7 @@ 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); @@ -118,9 +139,7 @@ static VirtIOSerialPortInfo virtconsole_info = { .init = virtconsole_initfn, .exit = virtconsole_exitfn, .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID), DEFINE_PROP_CHR("chardev", VirtConsole, chr), - DEFINE_PROP_STRING("name", VirtConsole, port.name), DEFINE_PROP_END_OF_LIST(), }, }; @@ -137,9 +156,7 @@ static VirtIOSerialPortInfo virtserialport_info = { .init = virtconsole_initfn, .exit = virtconsole_exitfn, .qdev.props = (Property[]) { - DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID), DEFINE_PROP_CHR("chardev", VirtConsole, chr), - DEFINE_PROP_STRING("name", VirtConsole, port.name), DEFINE_PROP_END_OF_LIST(), }, }; diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 09c665babe..3f10391f3e 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -657,7 +657,7 @@ static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_ /* copy in packet. ugh */ len = iov_from_buf(sg, elem.in_num, - buf + offset, size - offset); + buf + offset, 0, size - offset); total += len; offset += len; /* If buffers can't be merged, at this point we diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index 4f770fe185..f3b3293db0 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -788,10 +788,22 @@ static int virtio_balloon_init_pci(PCIDevice *pci_dev) VirtIODevice *vdev; vdev = virtio_balloon_init(&pci_dev->qdev); + if (!vdev) { + return -1; + } virtio_init_pci(proxy, vdev); return 0; } +static int virtio_balloon_exit_pci(PCIDevice *pci_dev) +{ + VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + + virtio_pci_stop_ioeventfd(proxy); + virtio_balloon_exit(proxy->vdev); + return virtio_exit_pci(pci_dev); +} + static PCIDeviceInfo virtio_info[] = { { .qdev.name = "virtio-blk-pci", @@ -866,7 +878,7 @@ static PCIDeviceInfo virtio_info[] = { .qdev.alias = "virtio-balloon", .qdev.size = sizeof(VirtIOPCIProxy), .init = virtio_balloon_init_pci, - .exit = virtio_exit_pci, + .exit = virtio_balloon_exit_pci, .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, .device_id = PCI_DEVICE_ID_VIRTIO_BALLOON, .revision = VIRTIO_PCI_ABI_VERSION, diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c index 7f6db7bffe..c5eb931095 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -19,6 +19,7 @@ #include "monitor.h" #include "qemu-queue.h" #include "sysbus.h" +#include "trace.h" #include "virtio-serial.h" /* The virtio-serial bus on top of which the ports will ride as devices */ @@ -103,7 +104,7 @@ static size_t write_to_port(VirtIOSerialPort *port, } len = iov_from_buf(elem.in_sg, elem.in_num, - buf + offset, size - offset); + buf + offset, 0, size - offset); offset += len; virtqueue_push(vq, &elem, len); @@ -221,6 +222,7 @@ static size_t send_control_event(VirtIOSerialPort *port, uint16_t event, stw_p(&cpkt.event, event); stw_p(&cpkt.value, value); + trace_virtio_serial_send_control_event(port->id, event, value); return send_control_msg(port, &cpkt, sizeof(cpkt)); } @@ -302,6 +304,7 @@ void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) return; } + trace_virtio_serial_throttle_port(port->id, throttle); port->throttled = throttle; if (throttle) { return; @@ -328,6 +331,8 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) cpkt.event = lduw_p(&gcpkt->event); cpkt.value = lduw_p(&gcpkt->value); + trace_virtio_serial_handle_control_message(cpkt.event, cpkt.value); + if (cpkt.event == VIRTIO_CONSOLE_DEVICE_READY) { if (!cpkt.value) { error_report("virtio-serial-bus: Guest failure in adding device %s", @@ -346,11 +351,13 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) port = find_port_by_id(vser, ldl_p(&gcpkt->id)); if (!port) { - error_report("virtio-serial-bus: Unexpected port id %u for device %s\n", + error_report("virtio-serial-bus: Unexpected port id %u for device %s", ldl_p(&gcpkt->id), vser->bus.qbus.name); return; } + trace_virtio_serial_handle_control_message_port(port->id); + info = DO_UPCAST(VirtIOSerialPortInfo, qdev, port->dev.info); switch(cpkt.event) { @@ -661,20 +668,22 @@ static struct BusInfo virtser_bus_info = { .name = "virtio-serial-bus", .size = sizeof(VirtIOSerialBus), .print_dev = virtser_bus_dev_print, + .props = (Property[]) { + DEFINE_PROP_UINT32("nr", VirtIOSerialPort, id, VIRTIO_CONSOLE_BAD_ID), + DEFINE_PROP_STRING("name", VirtIOSerialPort, name), + DEFINE_PROP_END_OF_LIST() + } }; static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) { VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev); - monitor_printf(mon, "%*s dev-prop-int: id: %u\n", - indent, "", port->id); - monitor_printf(mon, "%*s dev-prop-int: guest_connected: %d\n", - indent, "", port->guest_connected); - monitor_printf(mon, "%*s dev-prop-int: host_connected: %d\n", - indent, "", port->host_connected); - monitor_printf(mon, "%*s dev-prop-int: throttled: %d\n", - indent, "", port->throttled); + monitor_printf(mon, "%*sport %d, guest %s, host %s, throttle %s\n", + indent, "", port->id, + port->guest_connected ? "on" : "off", + port->host_connected ? "on" : "off", + port->throttled ? "on" : "off"); } /* This function is only used if a port id is not provided by the user */ diff --git a/hw/virtio.h b/hw/virtio.h index 0fd0bb0ac5..c1292647fe 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -213,6 +213,7 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf); void virtio_net_exit(VirtIODevice *vdev); void virtio_blk_exit(VirtIODevice *vdev); void virtio_serial_exit(VirtIODevice *vdev); +void virtio_balloon_exit(VirtIODevice *vdev); #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \ DEFINE_PROP_BIT("indirect_desc", _state, _field, \ @@ -24,22 +24,13 @@ extern int xen_allowed; static inline int xen_enabled(void) { -#ifdef CONFIG_XEN +#if defined(CONFIG_XEN_BACKEND) && !defined(CONFIG_NO_XEN) return xen_allowed; #else return 0; #endif } -static inline int xen_mapcache_enabled(void) -{ -#ifdef CONFIG_XEN_MAPCACHE - return xen_enabled(); -#else - return 0; -#endif -} - int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num); void xen_piix3_set_irq(void *opaque, int irq_num, int level); void xen_piix_pci_write_config_client(uint32_t address, uint32_t val, int len); @@ -50,6 +41,7 @@ qemu_irq *xen_interrupt_controller_init(void); int xen_init(void); int xen_hvm_init(void); void xen_vcpu_init(void); +void xenstore_store_pv_console_info(int i, struct CharDriverState *chr); #if defined(NEED_CPU_H) && !defined(CONFIG_USER_ONLY) void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size); diff --git a/hw/xen_common.h b/hw/xen_common.h index 2c79af64d0..0409ac7971 100644 --- a/hw/xen_common.h +++ b/hw/xen_common.h @@ -85,6 +85,18 @@ static inline int xc_domain_add_to_physmap(int xc_handle, uint32_t domid, return xc_memory_op(xc_handle, XENMEM_add_to_physmap, &xatp); } +static inline struct xs_handle *xs_open(unsigned long flags) +{ + return xs_daemon_open(); +} + +static inline void xs_close(struct xs_handle *xsh) +{ + if (xsh != NULL) { + xs_daemon_close(xsh); + } +} + /* Xen 4.1 */ #else diff --git a/hw/xen_console.c b/hw/xen_console.c index c6c8163813..8ef104c9ac 100644 --- a/hw/xen_console.c +++ b/hw/xen_console.c @@ -179,7 +179,9 @@ static void xencons_send(struct XenConsole *con) static int con_init(struct XenDevice *xendev) { struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); - char *type, *dom; + char *type, *dom, label[32]; + int ret = 0; + const char *output; /* setup */ dom = xs_get_domain_path(xenstore, con->xendev.dom); @@ -189,16 +191,25 @@ static int con_init(struct XenDevice *xendev) 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); - return -1; + ret = -1; + goto out; } - if (!serial_hds[con->xendev.dev]) - xen_be_printf(xendev, 1, "WARNING: serial line %d not configured\n", - con->xendev.dev); - else + 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_open(label, output, NULL); + } - return 0; + xenstore_store_pv_console_info(con->xendev.dev, con->chr); + +out: + qemu_free(type); + return ret; } static int con_connect(struct XenDevice *xendev) diff --git a/hw/xen_devconfig.c b/hw/xen_devconfig.c index 3a9215566d..6926c54f4f 100644 --- a/hw/xen_devconfig.c +++ b/hw/xen_devconfig.c @@ -126,8 +126,8 @@ int xen_config_dev_nic(NICInfo *nic) char mac[20]; snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", - nic->macaddr[0], nic->macaddr[1], nic->macaddr[2], - nic->macaddr[3], nic->macaddr[4], nic->macaddr[5]); + 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", nic->vlan->id, mac); xen_config_dev_dirs("vif", "qnic", nic->vlan->id, fe, be, sizeof(fe)); diff --git a/hw/xen_disk.c b/hw/xen_disk.c index 0c298afa8d..add815f273 100644 --- a/hw/xen_disk.c +++ b/hw/xen_disk.c @@ -616,12 +616,14 @@ static int blk_init(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); int index, qflags, have_barriers, info = 0; - char *h; /* read xenstore entries */ if (blkdev->params == NULL) { + char *h = NULL; blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); - h = strchr(blkdev->params, ':'); + if (blkdev->params != NULL) { + h = strchr(blkdev->params, ':'); + } if (h != NULL) { blkdev->fileproto = blkdev->params; blkdev->filename = h+1; @@ -631,6 +633,9 @@ static int blk_init(struct XenDevice *xendev) 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"); } @@ -649,7 +654,7 @@ static int blk_init(struct XenDevice *xendev) blkdev->mode == NULL || blkdev->type == NULL || blkdev->dev == NULL) { - return -1; + goto out_error; } /* read-only ? */ @@ -672,10 +677,15 @@ static int blk_init(struct XenDevice *xendev) /* 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 (bdrv_open(blkdev->bs, blkdev->filename, qflags, - bdrv_find_whitelisted_format(blkdev->fileproto)) != 0) { - bdrv_delete(blkdev->bs); - return -1; + if (blkdev->bs) { + if (bdrv_open(blkdev->bs, blkdev->filename, qflags, + bdrv_find_whitelisted_format(blkdev->fileproto)) != 0) { + bdrv_delete(blkdev->bs); + blkdev->bs = NULL; + } + } + if (!blkdev->bs) { + goto out_error; } } else { /* setup via qemu cmdline -> already setup for us */ @@ -704,6 +714,19 @@ static int blk_init(struct XenDevice *xendev) xenstore_write_be_int(&blkdev->xendev, "sectors", blkdev->file_size / blkdev->file_blk); return 0; + +out_error: + qemu_free(blkdev->params); + blkdev->params = NULL; + qemu_free(blkdev->mode); + blkdev->mode = NULL; + qemu_free(blkdev->type); + blkdev->type = NULL; + qemu_free(blkdev->dev); + blkdev->dev = NULL; + qemu_free(blkdev->devtype); + blkdev->devtype = NULL; + return -1; } static int blk_connect(struct XenDevice *xendev) diff --git a/hw/xen_platform.c b/hw/xen_platform.c index f43e175b4e..fb6be6a464 100644 --- a/hw/xen_platform.c +++ b/hw/xen_platform.c @@ -76,6 +76,35 @@ static void log_writeb(PCIXenPlatformState *s, char val) } /* Xen Platform, Fixed IOPort */ +#define UNPLUG_ALL_IDE_DISKS 1 +#define UNPLUG_ALL_NICS 2 +#define UNPLUG_AUX_IDE_DISKS 4 + +static void unplug_nic(PCIBus *b, PCIDevice *d) +{ + if (pci_get_word(d->config + PCI_CLASS_DEVICE) == + PCI_CLASS_NETWORK_ETHERNET) { + qdev_unplug(&(d->qdev)); + } +} + +static void pci_unplug_nics(PCIBus *bus) +{ + pci_for_each_device(bus, 0, unplug_nic); +} + +static void unplug_disks(PCIBus *b, PCIDevice *d) +{ + if (pci_get_word(d->config + PCI_CLASS_DEVICE) == + PCI_CLASS_STORAGE_IDE) { + qdev_unplug(&(d->qdev)); + } +} + +static void pci_unplug_disks(PCIBus *bus) +{ + pci_for_each_device(bus, 0, unplug_disks); +} static void platform_fixed_ioport_writew(void *opaque, uint32_t addr, uint32_t val) { @@ -83,10 +112,22 @@ static void platform_fixed_ioport_writew(void *opaque, uint32_t addr, uint32_t v switch (addr - XEN_PLATFORM_IOPORT) { case 0: - /* TODO: */ /* Unplug devices. Value is a bitmask of which devices to unplug, with bit 0 the IDE devices, bit 1 the network devices, and bit 2 the non-primary-master IDE devices. */ + if (val & UNPLUG_ALL_IDE_DISKS) { + DPRINTF("unplug disks\n"); + qemu_aio_flush(); + bdrv_flush_all(); + pci_unplug_disks(s->pci_dev.bus); + } + if (val & UNPLUG_ALL_NICS) { + DPRINTF("unplug nics\n"); + pci_unplug_nics(s->pci_dev.bus); + } + if (val & UNPLUG_AUX_IDE_DISKS) { + DPRINTF("unplug auxiliary disks not supported\n"); + } break; case 2: switch (val) { diff --git a/hw/xenfb.c b/hw/xenfb.c index 1db75fbe49..0a01ae30cc 100644 --- a/hw/xenfb.c +++ b/hw/xenfb.c @@ -347,13 +347,6 @@ static void xenfb_mouse_event(void *opaque, static int input_init(struct XenDevice *xendev) { - struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); - - if (!in->c.ds) { - xen_be_printf(xendev, 1, "ds not set (yet)\n"); - return -1; - } - xenstore_write_be_int(xendev, "feature-abs-pointer", 1); return 0; } @@ -367,6 +360,18 @@ static int input_connect(struct XenDevice *xendev) &in->abs_pointer_wanted) == -1) in->abs_pointer_wanted = 0; + if (!in->c.ds) { + char *vfb = xenstore_read_str(NULL, "device/vfb"); + if (vfb == NULL) { + /* there is no vfb, run vkbd on its own */ + in->c.ds = get_displaystate(); + } else { + qemu_free(vfb); + xen_be_printf(xendev, 1, "ds not set (yet)\n"); + return -1; + } + } + rc = common_bind(&in->c); if (rc != 0) return rc; diff --git a/hw/z2.c b/hw/z2.c new file mode 100644 index 0000000000..f93a1bf0fe --- /dev/null +++ b/hw/z2.c @@ -0,0 +1,358 @@ +/* + * PXA270-based Zipit Z2 device + * + * Copyright (c) 2011 by Vasily Khoruzhick <anarsoul@gmail.com> + * + * Code is based on mainstone platform. + * + * This code is licensed under the GNU GPL v2. + */ + +#include "hw.h" +#include "pxa.h" +#include "arm-misc.h" +#include "devices.h" +#include "i2c.h" +#include "ssi.h" +#include "boards.h" +#include "sysemu.h" +#include "flash.h" +#include "blockdev.h" +#include "console.h" +#include "audio/audio.h" + +#ifdef DEBUG_Z2 +#define DPRINTF(fmt, ...) \ + printf(fmt, ## __VA_ARGS__) +#else +#define DPRINTF(fmt, ...) +#endif + +static struct keymap map[0x100] = { + [0 ... 0xff] = { -1, -1 }, + [0x3b] = {0, 0}, /* Option = F1 */ + [0xc8] = {0, 1}, /* Up */ + [0xd0] = {0, 2}, /* Down */ + [0xcb] = {0, 3}, /* Left */ + [0xcd] = {0, 4}, /* Right */ + [0xcf] = {0, 5}, /* End */ + [0x0d] = {0, 6}, /* KPPLUS */ + [0xc7] = {1, 0}, /* Home */ + [0x10] = {1, 1}, /* Q */ + [0x17] = {1, 2}, /* I */ + [0x22] = {1, 3}, /* G */ + [0x2d] = {1, 4}, /* X */ + [0x1c] = {1, 5}, /* Enter */ + [0x0c] = {1, 6}, /* KPMINUS */ + [0xc9] = {2, 0}, /* PageUp */ + [0x11] = {2, 1}, /* W */ + [0x18] = {2, 2}, /* O */ + [0x23] = {2, 3}, /* H */ + [0x2e] = {2, 4}, /* C */ + [0x38] = {2, 5}, /* LeftAlt */ + [0xd1] = {3, 0}, /* PageDown */ + [0x12] = {3, 1}, /* E */ + [0x19] = {3, 2}, /* P */ + [0x24] = {3, 3}, /* J */ + [0x2f] = {3, 4}, /* V */ + [0x2a] = {3, 5}, /* LeftShift */ + [0x01] = {4, 0}, /* Esc */ + [0x13] = {4, 1}, /* R */ + [0x1e] = {4, 2}, /* A */ + [0x25] = {4, 3}, /* K */ + [0x30] = {4, 4}, /* B */ + [0x1d] = {4, 5}, /* LeftCtrl */ + [0x0f] = {5, 0}, /* Tab */ + [0x14] = {5, 1}, /* T */ + [0x1f] = {5, 2}, /* S */ + [0x26] = {5, 3}, /* L */ + [0x31] = {5, 4}, /* N */ + [0x39] = {5, 5}, /* Space */ + [0x3c] = {6, 0}, /* Stop = F2 */ + [0x15] = {6, 1}, /* Y */ + [0x20] = {6, 2}, /* D */ + [0x0e] = {6, 3}, /* Backspace */ + [0x32] = {6, 4}, /* M */ + [0x33] = {6, 5}, /* Comma */ + [0x3d] = {7, 0}, /* Play = F3 */ + [0x16] = {7, 1}, /* U */ + [0x21] = {7, 2}, /* F */ + [0x2c] = {7, 3}, /* Z */ + [0x27] = {7, 4}, /* Semicolon */ + [0x34] = {7, 5}, /* Dot */ +}; + +#define Z2_RAM_SIZE 0x02000000 +#define Z2_FLASH_BASE 0x00000000 +#define Z2_FLASH_SIZE 0x00800000 + +static struct arm_boot_info z2_binfo = { + .loader_start = PXA2XX_SDRAM_BASE, + .ram_size = Z2_RAM_SIZE, +}; + +#define Z2_GPIO_SD_DETECT 96 +#define Z2_GPIO_AC_IN 0 +#define Z2_GPIO_KEY_ON 1 +#define Z2_GPIO_LCD_CS 88 + +typedef struct { + SSISlave ssidev; + int32_t selected; + int32_t enabled; + uint8_t buf[3]; + uint32_t cur_reg; + int pos; +} ZipitLCD; + +static uint32_t zipit_lcd_transfer(SSISlave *dev, uint32_t value) +{ + ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev); + uint16_t val; + if (z->selected) { + z->buf[z->pos] = value & 0xff; + z->pos++; + } + if (z->pos == 3) { + switch (z->buf[0]) { + case 0x74: + DPRINTF("%s: reg: 0x%.2x\n", __func__, z->buf[2]); + z->cur_reg = z->buf[2]; + break; + case 0x76: + val = z->buf[1] << 8 | z->buf[2]; + DPRINTF("%s: value: 0x%.4x\n", __func__, val); + if (z->cur_reg == 0x22 && val == 0x0000) { + z->enabled = 1; + printf("%s: LCD enabled\n", __func__); + } else if (z->cur_reg == 0x10 && val == 0x0000) { + z->enabled = 0; + printf("%s: LCD disabled\n", __func__); + } + break; + default: + DPRINTF("%s: unknown command!\n", __func__); + break; + } + z->pos = 0; + } + return 0; +} + +static void z2_lcd_cs(void *opaque, int line, int level) +{ + ZipitLCD *z2_lcd = opaque; + z2_lcd->selected = !level; +} + +static int zipit_lcd_init(SSISlave *dev) +{ + ZipitLCD *z = FROM_SSI_SLAVE(ZipitLCD, dev); + z->selected = 0; + z->enabled = 0; + z->pos = 0; + + return 0; +} + +static VMStateDescription vmstate_zipit_lcd_state = { + .name = "zipit-lcd", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(selected, ZipitLCD), + VMSTATE_INT32(enabled, ZipitLCD), + VMSTATE_BUFFER(buf, ZipitLCD), + VMSTATE_UINT32(cur_reg, ZipitLCD), + VMSTATE_INT32(pos, ZipitLCD), + VMSTATE_END_OF_LIST(), + } +}; + +static SSISlaveInfo zipit_lcd_info = { + .qdev.name = "zipit-lcd", + .qdev.size = sizeof(ZipitLCD), + .qdev.vmsd = &vmstate_zipit_lcd_state, + .init = zipit_lcd_init, + .transfer = zipit_lcd_transfer +}; + +typedef struct { + i2c_slave i2c; + int len; + uint8_t buf[3]; +} AER915State; + +static int aer915_send(i2c_slave *i2c, uint8_t data) +{ + AER915State *s = FROM_I2C_SLAVE(AER915State, i2c); + s->buf[s->len] = data; + if (s->len++ > 2) { + DPRINTF("%s: message too long (%i bytes)\n", + __func__, s->len); + return 1; + } + + if (s->len == 2) { + DPRINTF("%s: reg %d value 0x%02x\n", __func__, + s->buf[0], s->buf[1]); + } + + return 0; +} + +static void aer915_event(i2c_slave *i2c, enum i2c_event event) +{ + AER915State *s = FROM_I2C_SLAVE(AER915State, i2c); + switch (event) { + case I2C_START_SEND: + s->len = 0; + break; + case I2C_START_RECV: + if (s->len != 1) { + DPRINTF("%s: short message!?\n", __func__); + } + break; + case I2C_FINISH: + break; + default: + break; + } +} + +static int aer915_recv(i2c_slave *slave) +{ + int retval = 0x00; + AER915State *s = FROM_I2C_SLAVE(AER915State, slave); + + switch (s->buf[0]) { + /* Return hardcoded battery voltage, + * 0xf0 means ~4.1V + */ + case 0x02: + retval = 0xf0; + break; + /* Return 0x00 for other regs, + * we don't know what they are for, + * anyway they return 0x00 on real hardware. + */ + default: + break; + } + + return retval; +} + +static int aer915_init(i2c_slave *i2c) +{ + /* Nothing to do. */ + return 0; +} + +static VMStateDescription vmstate_aer915_state = { + .name = "aer915", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(len, AER915State), + VMSTATE_BUFFER(buf, AER915State), + VMSTATE_END_OF_LIST(), + } +}; + +static I2CSlaveInfo aer915_info = { + .qdev.name = "aer915", + .qdev.size = sizeof(AER915State), + .qdev.vmsd = &vmstate_aer915_state, + .init = aer915_init, + .event = aer915_event, + .recv = aer915_recv, + .send = aer915_send +}; + +static void z2_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + uint32_t sector_len = 0x10000; + PXA2xxState *cpu; + DriveInfo *dinfo; + int be; + void *z2_lcd; + i2c_bus *bus; + DeviceState *wm; + + if (!cpu_model) { + cpu_model = "pxa270-c5"; + } + + /* Setup CPU & memory */ + cpu = pxa270_init(z2_binfo.ram_size, cpu_model); + +#ifdef TARGET_WORDS_BIGENDIAN + be = 1; +#else + be = 0; +#endif + dinfo = drive_get(IF_PFLASH, 0, 0); + if (!dinfo) { + fprintf(stderr, "Flash image must be given with the " + "'pflash' parameter\n"); + exit(1); + } + + if (!pflash_cfi01_register(Z2_FLASH_BASE, + qemu_ram_alloc(NULL, "z2.flash0", Z2_FLASH_SIZE), + dinfo->bdrv, sector_len, + Z2_FLASH_SIZE / sector_len, 4, 0, 0, 0, 0, + be)) { + fprintf(stderr, "qemu: Error registering flash memory.\n"); + exit(1); + } + + /* setup keypad */ + pxa27x_register_keypad(cpu->kp, map, 0x100); + + /* MMC/SD host */ + pxa2xx_mmci_handlers(cpu->mmc, + NULL, + qdev_get_gpio_in(cpu->gpio, Z2_GPIO_SD_DETECT)); + + ssi_register_slave(&zipit_lcd_info); + i2c_register_slave(&aer915_info); + z2_lcd = ssi_create_slave(cpu->ssp[1], "zipit-lcd"); + bus = pxa2xx_i2c_bus(cpu->i2c[0]); + i2c_create_slave(bus, "aer915", 0x55); + wm = i2c_create_slave(bus, "wm8750", 0x1b); + cpu->i2s->opaque = wm; + cpu->i2s->codec_out = wm8750_dac_dat; + cpu->i2s->codec_in = wm8750_adc_dat; + wm8750_data_req_set(wm, cpu->i2s->data_req, cpu->i2s); + + qdev_connect_gpio_out(cpu->gpio, Z2_GPIO_LCD_CS, + qemu_allocate_irqs(z2_lcd_cs, z2_lcd, 1)[0]); + + if (kernel_filename) { + z2_binfo.kernel_filename = kernel_filename; + z2_binfo.kernel_cmdline = kernel_cmdline; + z2_binfo.initrd_filename = initrd_filename; + z2_binfo.board_id = 0x6dd; + arm_load_kernel(cpu->env, &z2_binfo); + } +} + +static QEMUMachine z2_machine = { + .name = "z2", + .desc = "Zipit Z2 (PXA27x)", + .init = z2_init, +}; + +static void z2_machine_init(void) +{ + qemu_register_machine(&z2_machine); +} + +machine_init(z2_machine_init); |