qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 8/9] qdev/scsi: add scsi bus support to qdev, conver


From: Gerd Hoffmann
Subject: [Qemu-devel] [PATCH 8/9] qdev/scsi: add scsi bus support to qdev, convert drivers.
Date: Mon, 31 Aug 2009 14:24:04 +0200

 * Add SCSIBus.
 * Add SCSIDeviceInfo, move device callbacks here.
 * add qdev/scsi helper functions.
 * convert drivers.

Adding scsi disks via -device works now, i.e. you can do:

 -drive id=sda,if=none,...
 -device lsi
 -device scsi-disk,drive=sda

legacy command lines (-drive if=scsi,...) continue to work.

Signed-off-by: Gerd Hoffmann <address@hidden>
---
 Makefile          |    2 +-
 hw/esp.c          |   54 +++++-------------
 hw/lsi53c895a.c   |   65 +++++++---------------
 hw/pci-hotplug.c  |    7 ++-
 hw/pci.h          |    1 -
 hw/qdev.c         |   19 ------
 hw/qdev.h         |    4 -
 hw/scsi-bus.c     |   93 +++++++++++++++++++++++++++++++
 hw/scsi-disk.c    |  158 ++++++++++++++++++++++++++++-------------------------
 hw/scsi-disk.h    |   51 ++++++++++++++---
 hw/scsi-generic.c |  145 ++++++++++++++++++++++++++-----------------------
 hw/usb-msd.c      |   26 +++++----
 12 files changed, 351 insertions(+), 274 deletions(-)
 create mode 100644 hw/scsi-bus.c

diff --git a/Makefile b/Makefile
index 735282c..013e2c1 100644
--- a/Makefile
+++ b/Makefile
@@ -81,7 +81,7 @@ obj-y += i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o 
wm8750.o
 obj-y += ssd0303.o ssd0323.o ads7846.o stellaris_input.o twl92230.o
 obj-y += tmp105.o lm832x.o eeprom93xx.o tsc2005.o
 obj-y += scsi-disk.o cdrom.o
-obj-y += scsi-generic.o
+obj-y += scsi-generic.o scsi-bus.o
 obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
 obj-y += usb-serial.o usb-net.o usb-bus.o
 obj-y += sd.o ssi-sd.o
diff --git a/hw/esp.c b/hw/esp.c
index aad547e..527faa8 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -63,7 +63,7 @@ struct ESPState {
     uint8_t ti_buf[TI_BUFSZ];
     uint32_t sense;
     uint32_t dma;
-    SCSIDevice *scsi_dev[ESP_MAX_DEVS];
+    SCSIBus *bus;
     SCSIDevice *current_dev;
     uint8_t cmdbuf[TI_BUFSZ];
     uint32_t cmdlen;
@@ -187,11 +187,11 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf)
 
     if (s->current_dev) {
         /* Started a new command before the old one finished.  Cancel it.  */
-        s->current_dev->cancel_io(s->current_dev, 0);
+        s->current_dev->info->cancel_io(s->current_dev, 0);
         s->async_len = 0;
     }
 
-    if (target >= ESP_MAX_DEVS || !s->scsi_dev[target]) {
+    if (target >= ESP_MAX_DEVS || !s->bus->devs[target]) {
         // No such drive
         s->rregs[ESP_RSTAT] = 0;
         s->rregs[ESP_RINTR] = INTR_DC;
@@ -199,7 +199,7 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf)
         esp_raise_irq(s);
         return 0;
     }
-    s->current_dev = s->scsi_dev[target];
+    s->current_dev = s->bus->devs[target];
     return dmalen;
 }
 
@@ -210,7 +210,7 @@ static void do_cmd(ESPState *s, uint8_t *buf)
 
     DPRINTF("do_cmd: busid 0x%x\n", buf[0]);
     lun = buf[0] & 7;
-    datalen = s->current_dev->send_command(s->current_dev, 0, &buf[1], lun);
+    datalen = s->current_dev->info->send_command(s->current_dev, 0, &buf[1], 
lun);
     s->ti_size = datalen;
     if (datalen != 0) {
         s->rregs[ESP_RSTAT] = STAT_TC;
@@ -218,10 +218,10 @@ static void do_cmd(ESPState *s, uint8_t *buf)
         s->dma_counter = 0;
         if (datalen > 0) {
             s->rregs[ESP_RSTAT] |= STAT_DI;
-            s->current_dev->read_data(s->current_dev, 0);
+            s->current_dev->info->read_data(s->current_dev, 0);
         } else {
             s->rregs[ESP_RSTAT] |= STAT_DO;
-            s->current_dev->write_data(s->current_dev, 0);
+            s->current_dev->info->write_data(s->current_dev, 0);
         }
     }
     s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
@@ -320,9 +320,9 @@ static void esp_do_dma(ESPState *s)
     if (s->async_len == 0) {
         if (to_device) {
             // ti_size is negative
-            s->current_dev->write_data(s->current_dev, 0);
+            s->current_dev->info->write_data(s->current_dev, 0);
         } else {
-            s->current_dev->read_data(s->current_dev, 0);
+            s->current_dev->info->read_data(s->current_dev, 0);
             /* If there is still data to be read from the device then
                complete the DMA operation immediately.  Otherwise defer
                until the scsi layer has completed.  */
@@ -336,10 +336,10 @@ static void esp_do_dma(ESPState *s)
     }
 }
 
-static void esp_command_complete(void *opaque, int reason, uint32_t tag,
+static void esp_command_complete(SCSIBus *bus, int reason, uint32_t tag,
                                  uint32_t arg)
 {
-    ESPState *s = (ESPState *)opaque;
+    ESPState *s = DO_UPCAST(ESPState, busdev.qdev, bus->qbus.parent);
 
     if (reason == SCSI_REASON_DONE) {
         DPRINTF("SCSI Command complete\n");
@@ -357,7 +357,7 @@ static void esp_command_complete(void *opaque, int reason, 
uint32_t tag,
     } else {
         DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size);
         s->async_len = arg;
-        s->async_buf = s->current_dev->get_buf(s->current_dev, 0);
+        s->async_buf = s->current_dev->info->get_buf(s->current_dev, 0);
         if (s->dma_left) {
             esp_do_dma(s);
         } else if (s->dma_counter != 0 && s->ti_size <= 0) {
@@ -633,33 +633,6 @@ static int esp_load(QEMUFile *f, void *opaque, int 
version_id)
     return 0;
 }
 
-static void esp_scsi_attach(DeviceState *host, BlockDriverState *bd, int id)
-{
-    ESPState *s = FROM_SYSBUS(ESPState, sysbus_from_qdev(host));
-
-    if (id < 0) {
-        for (id = 0; id < ESP_MAX_DEVS; id++) {
-            if (id == (s->rregs[ESP_CFG1] & 0x7))
-                continue;
-            if (s->scsi_dev[id] == NULL)
-                break;
-        }
-    }
-    if (id >= ESP_MAX_DEVS) {
-        DPRINTF("Bad Device ID %d\n", id);
-        return;
-    }
-    if (s->scsi_dev[id]) {
-        DPRINTF("Destroying device %d\n", id);
-        s->scsi_dev[id]->destroy(s->scsi_dev[id]);
-    }
-    DPRINTF("Attaching block device %d\n", id);
-    /* Command queueing is not implemented.  */
-    s->scsi_dev[id] = scsi_generic_init(bd, 0, esp_command_complete, s);
-    if (s->scsi_dev[id] == NULL)
-        s->scsi_dev[id] = scsi_disk_init(bd, 0, esp_command_complete, s);
-}
-
 void esp_init(target_phys_addr_t espaddr, int it_shift,
               espdma_memory_read_write dma_memory_read,
               espdma_memory_read_write dma_memory_write,
@@ -700,7 +673,8 @@ static int esp_init1(SysBusDevice *dev)
 
     qdev_init_gpio_in(&dev->qdev, parent_esp_reset, 1);
 
-    scsi_bus_new(&dev->qdev, esp_scsi_attach);
+    s->bus = scsi_bus_new(&dev->qdev, 0, ESP_MAX_DEVS, esp_command_complete);
+    scsi_bus_legacy_handle_cmdline(s->bus);
     return 0;
 }
 
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index bf9f072..62bdca8 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -14,6 +14,7 @@
 
 #include "hw.h"
 #include "pci.h"
+#include "scsi.h"
 #include "scsi-disk.h"
 #include "block_int.h"
 
@@ -192,7 +193,7 @@ typedef struct {
      * 2 if processing DMA from lsi_execute_script.
      * 3 if a DMA operation is in progress.  */
     int waiting;
-    SCSIDevice *scsi_dev[LSI_MAX_DEVS];
+    SCSIBus *bus;
     SCSIDevice *current_dev;
     int current_lun;
     /* The tag is a combination of the device ID and the SCSI tag.  */
@@ -510,8 +511,8 @@ static void lsi_do_dma(LSIState *s, int out)
     s->dbc -= count;
 
     if (s->dma_buf == NULL) {
-        s->dma_buf = s->current_dev->get_buf(s->current_dev,
-                                             s->current_tag);
+        s->dma_buf = s->current_dev->info->get_buf(s->current_dev,
+                                                   s->current_tag);
     }
 
     /* ??? Set SFBR to first data byte.  */
@@ -525,10 +526,10 @@ static void lsi_do_dma(LSIState *s, int out)
         s->dma_buf = NULL;
         if (out) {
             /* Write the data.  */
-            s->current_dev->write_data(s->current_dev, s->current_tag);
+            s->current_dev->info->write_data(s->current_dev, s->current_tag);
         } else {
             /* Request any remaining data.  */
-            s->current_dev->read_data(s->current_dev, s->current_tag);
+            s->current_dev->info->read_data(s->current_dev, s->current_tag);
         }
     } else {
         s->dma_buf += count;
@@ -584,7 +585,7 @@ static void lsi_reselect(LSIState *s, uint32_t tag)
     id = (tag >> 8) & 0xf;
     s->ssid = id | 0x80;
     DPRINTF("Reselected target %d\n", id);
-    s->current_dev = s->scsi_dev[id];
+    s->current_dev = s->bus->devs[id];
     s->current_tag = tag;
     s->scntl1 |= LSI_SCNTL1_CON;
     lsi_set_phase(s, PHASE_MI);
@@ -632,10 +633,10 @@ static int lsi_queue_tag(LSIState *s, uint32_t tag, 
uint32_t arg)
 }
 
 /* Callback to indicate that the SCSI layer has completed a transfer.  */
-static void lsi_command_complete(void *opaque, int reason, uint32_t tag,
+static void lsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
                                  uint32_t arg)
 {
-    LSIState *s = opaque;
+    LSIState *s = DO_UPCAST(LSIState, dev.qdev, bus->qbus.parent);
     int out;
 
     out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
@@ -680,14 +681,14 @@ static void lsi_do_command(LSIState *s)
     cpu_physical_memory_read(s->dnad, buf, s->dbc);
     s->sfbr = buf[0];
     s->command_complete = 0;
-    n = s->current_dev->send_command(s->current_dev, s->current_tag, buf,
-                                     s->current_lun);
+    n = s->current_dev->info->send_command(s->current_dev, s->current_tag, buf,
+                                           s->current_lun);
     if (n > 0) {
         lsi_set_phase(s, PHASE_DI);
-        s->current_dev->read_data(s->current_dev, s->current_tag);
+        s->current_dev->info->read_data(s->current_dev, s->current_tag);
     } else if (n < 0) {
         lsi_set_phase(s, PHASE_DO);
-        s->current_dev->write_data(s->current_dev, s->current_tag);
+        s->current_dev->info->write_data(s->current_dev, s->current_tag);
     }
 
     if (!s->command_complete) {
@@ -1040,7 +1041,7 @@ again:
                 }
                 s->sstat0 |= LSI_SSTAT0_WOA;
                 s->scntl1 &= ~LSI_SCNTL1_IARB;
-                if (id >= LSI_MAX_DEVS || !s->scsi_dev[id]) {
+                if (id >= LSI_MAX_DEVS || !s->bus->devs[id]) {
                     DPRINTF("Selected absent target %d\n", id);
                     lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO);
                     lsi_disconnect(s);
@@ -1051,7 +1052,7 @@ again:
                 /* ??? Linux drivers compain when this is set.  Maybe
                    it only applies in low-level mode (unimplemented).
                 lsi_script_scsi_interrupt(s, LSI_SIST0_CMP, 0); */
-                s->current_dev = s->scsi_dev[id];
+                s->current_dev = s->bus->devs[id];
                 s->current_tag = id << 8;
                 s->scntl1 |= LSI_SCNTL1_CON;
                 if (insn & (1 << 3)) {
@@ -1958,31 +1959,6 @@ static void lsi_mmio_mapfunc(PCIDevice *pci_dev, int 
region_num,
     cpu_register_physical_memory(addr + 0, 0x400, s->mmio_io_addr);
 }
 
-void lsi_scsi_attach(DeviceState *host, BlockDriverState *bd, int id)
-{
-    LSIState *s = DO_UPCAST(LSIState, dev.qdev, host);
-
-    if (id < 0) {
-        for (id = 0; id < LSI_MAX_DEVS; id++) {
-            if (s->scsi_dev[id] == NULL)
-                break;
-        }
-    }
-    if (id >= LSI_MAX_DEVS) {
-        BADF("Bad Device ID %d\n", id);
-        return;
-    }
-    if (s->scsi_dev[id]) {
-        DPRINTF("Destroying device %d\n", id);
-        s->scsi_dev[id]->destroy(s->scsi_dev[id]);
-    }
-    DPRINTF("Attaching block device %d\n", id);
-    s->scsi_dev[id] = scsi_generic_init(bd, 1, lsi_command_complete, s);
-    if (s->scsi_dev[id] == NULL)
-        s->scsi_dev[id] = scsi_disk_init(bd, 1, lsi_command_complete, s);
-    bd->private = &s->dev;
-}
-
 static void lsi_scsi_save(QEMUFile *f, void *opaque)
 {
     LSIState *s = opaque;
@@ -2202,16 +2178,17 @@ static int lsi_scsi_init(PCIDevice *dev)
 
     lsi_soft_reset(s);
 
-    scsi_bus_new(&dev->qdev, lsi_scsi_attach);
-
+    s->bus = scsi_bus_new(&dev->qdev, 1, LSI_MAX_DEVS, lsi_command_complete);
+    scsi_bus_legacy_handle_cmdline(s->bus);
     register_savevm("lsiscsi", -1, 0, lsi_scsi_save, lsi_scsi_load, s);
     return 0;
 }
 
 static PCIDeviceInfo lsi_info = {
-    .qdev.name = "lsi53c895a",
-    .qdev.size = sizeof(LSIState),
-    .init      = lsi_scsi_init,
+    .qdev.name  = "lsi53c895a",
+    .qdev.alias = "lsi",
+    .qdev.size  = sizeof(LSIState),
+    .init       = lsi_scsi_init,
 };
 
 static void lsi53c895a_register_devices(void)
diff --git a/hw/pci-hotplug.c b/hw/pci-hotplug.c
index 26ff808..18c2562 100644
--- a/hw/pci-hotplug.c
+++ b/hw/pci-hotplug.c
@@ -30,6 +30,7 @@
 #include "pc.h"
 #include "monitor.h"
 #include "block_int.h"
+#include "scsi-disk.h"
 #include "virtio-blk.h"
 
 #if defined(TARGET_I386) || defined(TARGET_X86_64)
@@ -56,6 +57,7 @@ void drive_hot_add(Monitor *mon, const char *pci_addr, const 
char *opts)
     int success = 0;
     PCIDevice *dev;
     DriveInfo *dinfo;
+    BusState *scsibus;
 
     if (pci_read_devaddr(mon, pci_addr, &dom, &pci_bus, &slot)) {
         return;
@@ -80,8 +82,9 @@ void drive_hot_add(Monitor *mon, const char *pci_addr, const 
char *opts)
     switch (type) {
     case IF_SCSI:
         success = 1;
-        lsi_scsi_attach(&dev->qdev, dinfo->bdrv,
-                        dinfo->unit);
+        scsibus = LIST_FIRST(&dev->qdev.child_bus);
+        scsi_bus_legacy_add_drive(DO_UPCAST(SCSIBus, qbus, scsibus),
+                                  dinfo, dinfo->unit);
         break;
     default:
         monitor_printf(mon, "Can't hot-add drive to type %d\n", type);
diff --git a/hw/pci.h b/hw/pci.h
index f94341a..d0edfdb 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -331,7 +331,6 @@ PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const 
char *name);
 
 /* lsi53c895a.c */
 #define LSI_MAX_DEVS 7
-void lsi_scsi_attach(DeviceState *host, BlockDriverState *bd, int id);
 
 /* vmware_vga.c */
 void pci_vmsvga_init(PCIBus *bus);
diff --git a/hw/qdev.c b/hw/qdev.c
index f9754b1..0e9732b 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -314,25 +314,6 @@ BusState *qdev_get_child_bus(DeviceState *dev, const char 
*name)
     return NULL;
 }
 
-static int next_scsi_bus;
-
-/* Create a scsi bus, and attach devices to it.  */
-/* TODO: Actually create a scsi bus for hotplug to use.  */
-void scsi_bus_new(DeviceState *host, SCSIAttachFn attach)
-{
-   int bus = next_scsi_bus++;
-   int unit;
-   DriveInfo *dinfo;
-
-   for (unit = 0; unit < MAX_SCSI_DEVS; unit++) {
-       dinfo = drive_get(IF_SCSI, bus, unit);
-       if (!dinfo) {
-           continue;
-       }
-       attach(host, dinfo->bdrv, unit);
-   }
-}
-
 static BusState *qbus_find_recursive(BusState *bus, const char *name,
                                      const BusInfo *info)
 {
diff --git a/hw/qdev.h b/hw/qdev.h
index af735d7..56a0c1f 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -99,8 +99,6 @@ BusState *qdev_get_child_bus(DeviceState *dev, const char 
*name);
 /*** Device API.  ***/
 
 typedef int (*qdev_initfn)(DeviceState *dev, DeviceInfo *info);
-typedef void (*SCSIAttachFn)(DeviceState *host, BlockDriverState *bdrv,
-              int unit);
 
 struct DeviceInfo {
     const char *name;
@@ -123,8 +121,6 @@ void qdev_register(DeviceInfo *info);
 void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n);
 void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n);
 
-void scsi_bus_new(DeviceState *host, SCSIAttachFn attach);
-
 CharDriverState *qdev_init_chardev(DeviceState *dev);
 
 BusState *qdev_get_parent_bus(DeviceState *dev);
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
new file mode 100644
index 0000000..16afa05
--- /dev/null
+++ b/hw/scsi-bus.c
@@ -0,0 +1,93 @@
+#include "hw.h"
+#include "sysemu.h"
+#include "scsi-disk.h"
+#include "block.h"
+#include "qdev.h"
+
+static struct BusInfo scsi_bus_info = {
+    .name  = "SCSI",
+    .size  = sizeof(SCSIBus),
+    .props = (Property[]) {
+        DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+static int next_scsi_bus;
+
+/* Create a scsi bus, and attach devices to it.  */
+SCSIBus *scsi_bus_new(DeviceState *host, int tcq, int ndev,
+                      scsi_completionfn complete)
+{
+    SCSIBus *bus;
+
+    bus = FROM_QBUS(SCSIBus, qbus_create(&scsi_bus_info, host, NULL));
+    bus->busnr = next_scsi_bus++;
+    bus->tcq = tcq;
+    bus->ndev = ndev;
+    bus->complete = complete;
+    return bus;
+}
+
+static int scsi_qdev_init(DeviceState *qdev, DeviceInfo *base)
+{
+    SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
+    SCSIDeviceInfo *info = DO_UPCAST(SCSIDeviceInfo, qdev, base);
+    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
+
+    if (dev->id == -1) {
+        for (dev->id = 0; dev->id < bus->ndev; dev->id++) {
+            if (bus->devs[dev->id] == NULL)
+                break;
+        }
+    }
+    if (dev->id >= bus->ndev) {
+        qemu_error("bad scsi device id: %d\n", dev->id);
+        goto err;
+    }
+
+    if (bus->devs[dev->id]) {
+        bus->devs[dev->id]->info->destroy(bus->devs[dev->id]);
+    }
+    bus->devs[dev->id] = dev;
+
+    dev->info = info;
+    return dev->info->init(dev);
+
+err:
+    return -1;
+}
+
+void scsi_qdev_register(SCSIDeviceInfo *info)
+{
+    info->qdev.bus_info = &scsi_bus_info;
+    info->qdev.init     = scsi_qdev_init;
+    qdev_register(&info->qdev);
+}
+
+/* handle legacy '-drive if=scsi,...' cmd line args */
+SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, DriveInfo *dinfo, int unit)
+{
+    const char *driver;
+    DeviceState *dev;
+
+    driver = bdrv_is_sg(dinfo->bdrv) ? "scsi-generic" : "scsi-disk";
+    dev = qdev_create(&bus->qbus, driver);
+    qdev_prop_set_uint32(dev, "scsi-id", unit);
+    qdev_prop_set_drive(dev, "drive", dinfo);
+    qdev_init(dev);
+    return DO_UPCAST(SCSIDevice, qdev, dev);
+}
+
+void scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
+{
+    DriveInfo *dinfo;
+    int unit;
+
+    for (unit = 0; unit < MAX_SCSI_DEVS; unit++) {
+        dinfo = drive_get(IF_SCSI, bus->busnr, unit);
+        if (dinfo == NULL) {
+            continue;
+        }
+        scsi_bus_legacy_add_drive(bus, dinfo, unit);
+    }
+}
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 5b825c9..ffc2654 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -44,8 +44,11 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } 
while (0)
 
 #define SCSI_REQ_STATUS_RETRY 0x01
 
+typedef struct SCSIDiskState SCSIDiskState;
+
 typedef struct SCSIRequest {
-    SCSIDeviceState *dev;
+    SCSIBus *bus;
+    SCSIDiskState *dev;
     uint32_t tag;
     /* ??? We should probably keep track of whether the data transfer is
        a read or a write.  Currently we rely on the host getting it right.  */
@@ -59,20 +62,16 @@ typedef struct SCSIRequest {
     uint32_t status;
 } SCSIRequest;
 
-struct SCSIDeviceState
+struct SCSIDiskState
 {
-    BlockDriverState *bdrv;
+    SCSIDevice qdev;
+    DriveInfo *dinfo;
     SCSIRequest *requests;
     /* The qemu block layer uses a fixed 512 byte sector size.
        This is the number of 512 byte blocks in a single scsi sector.  */
     int cluster_size;
     uint64_t max_lba;
     int sense;
-    int tcq;
-    /* Completion functions may be called from either scsi_{read,write}_data
-       or from the AIO completion routines.  */
-    scsi_completionfn completion;
-    void *opaque;
     char drive_serial_str[21];
     QEMUBH *bh;
 };
@@ -80,8 +79,9 @@ struct SCSIDeviceState
 /* Global pool of SCSIRequest structures.  */
 static SCSIRequest *free_requests = NULL;
 
-static SCSIRequest *scsi_new_request(SCSIDeviceState *s, uint32_t tag)
+static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag)
 {
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
     SCSIRequest *r;
 
     if (free_requests) {
@@ -91,6 +91,7 @@ static SCSIRequest *scsi_new_request(SCSIDeviceState *s, 
uint32_t tag)
         r = qemu_malloc(sizeof(SCSIRequest));
         r->iov.iov_base = qemu_memalign(512, SCSI_DMA_BUF_SIZE);
     }
+    r->bus = scsi_bus_from_device(d);
     r->dev = s;
     r->tag = tag;
     r->sector_count = 0;
@@ -106,7 +107,7 @@ static SCSIRequest *scsi_new_request(SCSIDeviceState *s, 
uint32_t tag)
 static void scsi_remove_request(SCSIRequest *r)
 {
     SCSIRequest *last;
-    SCSIDeviceState *s = r->dev;
+    SCSIDiskState *s = r->dev;
 
     if (s->requests == r) {
         s->requests = r->next;
@@ -124,7 +125,7 @@ static void scsi_remove_request(SCSIRequest *r)
     free_requests = r;
 }
 
-static SCSIRequest *scsi_find_request(SCSIDeviceState *s, uint32_t tag)
+static SCSIRequest *scsi_find_request(SCSIDiskState *s, uint32_t tag)
 {
     SCSIRequest *r;
 
@@ -138,19 +139,19 @@ static SCSIRequest *scsi_find_request(SCSIDeviceState *s, 
uint32_t tag)
 /* Helper function for command completion.  */
 static void scsi_command_complete(SCSIRequest *r, int status, int sense)
 {
-    SCSIDeviceState *s = r->dev;
+    SCSIDiskState *s = r->dev;
     uint32_t tag;
     DPRINTF("Command complete tag=0x%x status=%d sense=%d\n", r->tag, status, 
sense);
     s->sense = sense;
     tag = r->tag;
     scsi_remove_request(r);
-    s->completion(s->opaque, SCSI_REASON_DONE, tag, status);
+    r->bus->complete(r->bus, SCSI_REASON_DONE, tag, status);
 }
 
 /* Cancel a pending data transfer.  */
 static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
 {
-    SCSIDeviceState *s = d->state;
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
     SCSIRequest *r;
     DPRINTF("Cancel tag=0x%x\n", tag);
     r = scsi_find_request(s, tag);
@@ -165,23 +166,22 @@ static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
 static void scsi_read_complete(void * opaque, int ret)
 {
     SCSIRequest *r = (SCSIRequest *)opaque;
-    SCSIDeviceState *s = r->dev;
 
     if (ret) {
         DPRINTF("IO error\n");
-        s->completion(s->opaque, SCSI_REASON_DATA, r->tag, 0);
+        r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, 0);
         scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_NO_SENSE);
         return;
     }
     DPRINTF("Data ready tag=0x%x len=%" PRId64 "\n", r->tag, r->iov.iov_len);
 
-    s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->iov.iov_len);
+    r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, r->iov.iov_len);
 }
 
 /* Read more data from scsi device into buffer.  */
 static void scsi_read_data(SCSIDevice *d, uint32_t tag)
 {
-    SCSIDeviceState *s = d->state;
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
     SCSIRequest *r;
     uint32_t n;
 
@@ -195,7 +195,7 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag)
     if (r->sector_count == (uint32_t)-1) {
         DPRINTF("Read buf_len=%" PRId64 "\n", r->iov.iov_len);
         r->sector_count = 0;
-        s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->iov.iov_len);
+        r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, r->iov.iov_len);
         return;
     }
     DPRINTF("Read sector_count=%d\n", r->sector_count);
@@ -210,7 +210,7 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag)
 
     r->iov.iov_len = n * 512;
     qemu_iovec_init_external(&r->qiov, &r->iov, 1);
-    r->aiocb = bdrv_aio_readv(s->bdrv, r->sector, &r->qiov, n,
+    r->aiocb = bdrv_aio_readv(s->dinfo->bdrv, r->sector, &r->qiov, n,
                               scsi_read_complete, r);
     if (r->aiocb == NULL)
         scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
@@ -220,7 +220,7 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag)
 
 static int scsi_handle_write_error(SCSIRequest *r, int error)
 {
-    BlockInterfaceErrorAction action = drive_get_onerror(r->dev->bdrv);
+    BlockInterfaceErrorAction action = drive_get_onerror(r->dev->dinfo->bdrv);
 
     if (action == BLOCK_ERR_IGNORE)
         return 0;
@@ -240,7 +240,6 @@ static int scsi_handle_write_error(SCSIRequest *r, int 
error)
 static void scsi_write_complete(void * opaque, int ret)
 {
     SCSIRequest *r = (SCSIRequest *)opaque;
-    SCSIDeviceState *s = r->dev;
     uint32_t len;
     uint32_t n;
 
@@ -263,19 +262,19 @@ static void scsi_write_complete(void * opaque, int ret)
         }
         r->iov.iov_len = len;
         DPRINTF("Write complete tag=0x%x more=%d\n", r->tag, len);
-        s->completion(s->opaque, SCSI_REASON_DATA, r->tag, len);
+        r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, len);
     }
 }
 
 static void scsi_write_request(SCSIRequest *r)
 {
-    SCSIDeviceState *s = r->dev;
+    SCSIDiskState *s = r->dev;
     uint32_t n;
 
     n = r->iov.iov_len / 512;
     if (n) {
         qemu_iovec_init_external(&r->qiov, &r->iov, 1);
-        r->aiocb = bdrv_aio_writev(s->bdrv, r->sector, &r->qiov, n,
+        r->aiocb = bdrv_aio_writev(s->dinfo->bdrv, r->sector, &r->qiov, n,
                                    scsi_write_complete, r);
         if (r->aiocb == NULL)
             scsi_command_complete(r, STATUS_CHECK_CONDITION,
@@ -290,7 +289,7 @@ static void scsi_write_request(SCSIRequest *r)
    The transfer may complete asynchronously.  */
 static int scsi_write_data(SCSIDevice *d, uint32_t tag)
 {
-    SCSIDeviceState *s = d->state;
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
     SCSIRequest *r;
 
     DPRINTF("Write data tag=0x%x\n", tag);
@@ -311,7 +310,7 @@ static int scsi_write_data(SCSIDevice *d, uint32_t tag)
 
 static void scsi_dma_restart_bh(void *opaque)
 {
-    SCSIDeviceState *s = opaque;
+    SCSIDiskState *s = opaque;
     SCSIRequest *r = s->requests;
 
     qemu_bh_delete(s->bh);
@@ -328,7 +327,7 @@ static void scsi_dma_restart_bh(void *opaque)
 
 static void scsi_dma_restart_cb(void *opaque, int running, int reason)
 {
-    SCSIDeviceState *s = opaque;
+    SCSIDiskState *s = opaque;
 
     if (!running)
         return;
@@ -342,7 +341,7 @@ static void scsi_dma_restart_cb(void *opaque, int running, 
int reason)
 /* Return a pointer to the data buffer.  */
 static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
 {
-    SCSIDeviceState *s = d->state;
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
     SCSIRequest *r;
 
     r = scsi_find_request(s, tag);
@@ -361,7 +360,7 @@ static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
 static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
                                  uint8_t *buf, int lun)
 {
-    SCSIDeviceState *s = d->state;
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
     uint64_t nb_sectors;
     uint64_t lba;
     uint32_t len;
@@ -379,7 +378,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
     }
     /* ??? Tags are not unique for different luns.  We only implement a
        single lun, so this should not matter.  */
-    r = scsi_new_request(s, tag);
+    r = scsi_new_request(d, tag);
     outbuf = (uint8_t *)r->iov.iov_base;
     is_write = 0;
     DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]);
@@ -433,7 +432,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
     switch (command) {
     case 0x0:
        DPRINTF("Test Unit Ready\n");
-        if (!bdrv_is_inserted(s->bdrv))
+        if (!bdrv_is_inserted(s->dinfo->bdrv))
             goto notready;
        break;
     case 0x03:
@@ -479,7 +478,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
 
                         r->iov.iov_len = 0;
 
-                        if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+                        if (bdrv_get_type_hint(s->dinfo->bdrv) == 
BDRV_TYPE_CDROM) {
                             outbuf[r->iov.iov_len++] = 5;
                         } else {
                             outbuf[r->iov.iov_len++] = 0;
@@ -510,7 +509,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
                         r->iov.iov_len = 0;
 
                         /* Supported page codes */
-                        if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+                        if (bdrv_get_type_hint(s->dinfo->bdrv) == 
BDRV_TYPE_CDROM) {
                             outbuf[r->iov.iov_len++] = 5;
                         } else {
                             outbuf[r->iov.iov_len++] = 0;
@@ -528,14 +527,14 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
                     {
                         /* Device identification page, mandatory */
                         int max_len = 255 - 8;
-                        int id_len = strlen(bdrv_get_device_name(s->bdrv));
+                        int id_len = 
strlen(bdrv_get_device_name(s->dinfo->bdrv));
                         if (id_len > max_len)
                             id_len = max_len;
 
                         DPRINTF("Inquiry EVPD[Device identification] "
                                 "buffer size %d\n", len);
                         r->iov.iov_len = 0;
-                        if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+                        if (bdrv_get_type_hint(s->dinfo->bdrv) == 
BDRV_TYPE_CDROM) {
                             outbuf[r->iov.iov_len++] = 5;
                         } else {
                             outbuf[r->iov.iov_len++] = 0;
@@ -551,7 +550,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
                         outbuf[r->iov.iov_len++] = id_len; // length of data 
following
 
                         memcpy(&outbuf[r->iov.iov_len],
-                               bdrv_get_device_name(s->bdrv), id_len);
+                               bdrv_get_device_name(s->dinfo->bdrv), id_len);
                         r->iov.iov_len += id_len;
                     }
                     break;
@@ -591,7 +590,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
 
         if (lun || buf[1] >> 5) {
             outbuf[0] = 0x7f;  /* LUN not supported */
-       } else if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+       } else if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) {
            outbuf[0] = 5;
             outbuf[1] = 0x80;
            memcpy(&outbuf[16], "QEMU CD-ROM    ", 16);
@@ -607,7 +606,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
        outbuf[3] = 2; /* Format 2 */
        outbuf[4] = len - 5; /* Additional Length = (Len - 1) - 4 */
         /* Sync data transfer and TCQ.  */
-        outbuf[7] = 0x10 | (s->tcq ? 0x02 : 0);
+        outbuf[7] = 0x10 | (r->bus->tcq ? 0x02 : 0);
        r->iov.iov_len = len;
        break;
     case 0x16:
@@ -632,7 +631,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
             memset(p, 0, 4);
             outbuf[1] = 0; /* Default media type.  */
             outbuf[3] = 0; /* Block descriptor length.  */
-            if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+            if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) {
                 outbuf[2] = 0x80; /* Readonly.  */
             }
             p += 4;
@@ -643,7 +642,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
                 p[0] = 4;
                 p[1] = 0x16;
                 /* if a geometry hint is available, use it */
-                bdrv_get_geometry_hint(s->bdrv, &cylinders, &heads, &secs);
+                bdrv_get_geometry_hint(s->dinfo->bdrv, &cylinders, &heads, 
&secs);
                 p[2] = (cylinders >> 16) & 0xff;
                 p[3] = (cylinders >> 8) & 0xff;
                 p[4] = cylinders & 0xff;
@@ -677,7 +676,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
                 p[2] = 5000 >> 8;
                 p[3] = 5000 & 0xff;
                 /* if a geometry hint is available, use it */
-                bdrv_get_geometry_hint(s->bdrv, &cylinders, &heads, &secs);
+                bdrv_get_geometry_hint(s->dinfo->bdrv, &cylinders, &heads, 
&secs);
                 p[4] = heads & 0xff;
                 p[5] = secs & 0xff;
                 p[6] = s->cluster_size * 2;
@@ -714,7 +713,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
                 p += 20;
             }
             if ((page == 0x3f || page == 0x2a)
-                    && (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM)) {
+                    && (bdrv_get_type_hint(s->dinfo->bdrv) == 
BDRV_TYPE_CDROM)) {
                 /* CD Capabilities and Mechanical Status page. */
                 p[0] = 0x2a;
                 p[1] = 0x14;
@@ -725,7 +724,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
                 p[5] = 0xff; /* CD DA, DA accurate, RW supported,
                                          RW corrected, C2 errors, ISRC,
                                          UPC, Bar code */
-                p[6] = 0x2d | (bdrv_is_locked(s->bdrv)? 2 : 0);
+                p[6] = 0x2d | (bdrv_is_locked(s->dinfo->bdrv)? 2 : 0);
                 /* Locking supported, jumper present, eject, tray */
                 p[7] = 0; /* no volume & mute control, no
                                       changer */
@@ -751,20 +750,20 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
         break;
     case 0x1b:
         DPRINTF("Start Stop Unit\n");
-        if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM &&
+        if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM &&
             (buf[4] & 2))
             /* load/eject medium */
-            bdrv_eject(s->bdrv, !(buf[4] & 1));
+            bdrv_eject(s->dinfo->bdrv, !(buf[4] & 1));
        break;
     case 0x1e:
         DPRINTF("Prevent Allow Medium Removal (prevent = %d)\n", buf[4] & 3);
-        bdrv_set_locked(s->bdrv, buf[4] & 1);
+        bdrv_set_locked(s->dinfo->bdrv, buf[4] & 1);
        break;
     case 0x25:
        DPRINTF("Read Capacity\n");
         /* The normal LEN field for this command is zero.  */
        memset(outbuf, 0, 8);
-       bdrv_get_geometry(s->bdrv, &nb_sectors);
+       bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors);
         nb_sectors /= s->cluster_size;
         /* Returned value is the address of the last sector.  */
         if (nb_sectors) {
@@ -810,7 +809,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
         break;
     case 0x35:
         DPRINTF("Synchronise cache (sector %" PRId64 ", count %d)\n", lba, 
len);
-        bdrv_flush(s->bdrv);
+        bdrv_flush(s->dinfo->bdrv);
         break;
     case 0x43:
         {
@@ -819,7 +818,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
             msf = buf[1] & 2;
             format = buf[2] & 0xf;
             start_track = buf[6];
-            bdrv_get_geometry(s->bdrv, &nb_sectors);
+            bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors);
             DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, 
format, msf >> 1);
             nb_sectors /= s->cluster_size;
             switch(format) {
@@ -873,7 +872,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
         if ((buf[1] & 31) == 0x10) {
             DPRINTF("SAI READ CAPACITY(16)\n");
             memset(outbuf, 0, len);
-            bdrv_get_geometry(s->bdrv, &nb_sectors);
+            bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors);
             nb_sectors /= s->cluster_size;
             /* Returned value is the address of the last sector.  */
             if (nb_sectors) {
@@ -937,45 +936,56 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
 
 static void scsi_destroy(SCSIDevice *d)
 {
-    qemu_free(d->state);
     qemu_free(d);
 }
 
-SCSIDevice *scsi_disk_init(BlockDriverState *bdrv, int tcq,
-                           scsi_completionfn completion, void *opaque)
+static int scsi_disk_initfn(SCSIDevice *dev)
 {
-    SCSIDevice *d;
-    SCSIDeviceState *s;
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
     uint64_t nb_sectors;
 
-    s = (SCSIDeviceState *)qemu_mallocz(sizeof(SCSIDeviceState));
-    s->bdrv = bdrv;
-    s->tcq = tcq;
-    s->completion = completion;
-    s->opaque = opaque;
-    if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+    if (!s->dinfo || !s->dinfo->bdrv) {
+        qemu_error("scsi-disk: drive property not set\n");
+        return -1;
+    }
+
+    if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) {
         s->cluster_size = 4;
     } else {
         s->cluster_size = 1;
     }
-    bdrv_get_geometry(s->bdrv, &nb_sectors);
+    bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors);
     nb_sectors /= s->cluster_size;
     if (nb_sectors)
         nb_sectors--;
     s->max_lba = nb_sectors;
-    strncpy(s->drive_serial_str, drive_get_serial(s->bdrv),
+    strncpy(s->drive_serial_str, drive_get_serial(s->dinfo->bdrv),
             sizeof(s->drive_serial_str));
     if (strlen(s->drive_serial_str) == 0)
         pstrcpy(s->drive_serial_str, sizeof(s->drive_serial_str), "0");
     qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s);
-    d = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice));
-    d->state = s;
-    d->destroy = scsi_destroy;
-    d->send_command = scsi_send_command;
-    d->read_data = scsi_read_data;
-    d->write_data = scsi_write_data;
-    d->cancel_io = scsi_cancel_io;
-    d->get_buf = scsi_get_buf;
-
-    return d;
+    return 0;
+}
+
+static SCSIDeviceInfo scsi_disk_info = {
+    .qdev.name    = "scsi-disk",
+    .qdev.desc    = "virtual scsi disk or cdrom",
+    .qdev.size    = sizeof(SCSIDiskState),
+    .init         = scsi_disk_initfn,
+    .destroy      = scsi_destroy,
+    .send_command = scsi_send_command,
+    .read_data    = scsi_read_data,
+    .write_data   = scsi_write_data,
+    .cancel_io    = scsi_cancel_io,
+    .get_buf      = scsi_get_buf,
+    .qdev.props   = (Property[]) {
+        DEFINE_PROP_DRIVE("drive", SCSIDiskState, dinfo),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+static void scsi_disk_register_devices(void)
+{
+    scsi_qdev_register(&scsi_disk_info);
 }
+device_init(scsi_disk_register_devices)
diff --git a/hw/scsi-disk.h b/hw/scsi-disk.h
index f42212b..febde44 100644
--- a/hw/scsi-disk.h
+++ b/hw/scsi-disk.h
@@ -1,20 +1,36 @@
 #ifndef SCSI_DISK_H
 #define SCSI_DISK_H
 
+#include "qdev.h"
+
 /* scsi-disk.c */
 enum scsi_reason {
     SCSI_REASON_DONE, /* Command complete.  */
     SCSI_REASON_DATA  /* Transfer complete, more data required.  */
 };
 
-typedef struct SCSIDeviceState SCSIDeviceState;
+typedef struct SCSIBus SCSIBus;
 typedef struct SCSIDevice SCSIDevice;
-typedef void (*scsi_completionfn)(void *opaque, int reason, uint32_t tag,
+typedef struct SCSIDeviceInfo SCSIDeviceInfo;
+typedef void (*scsi_completionfn)(SCSIBus *bus, int reason, uint32_t tag,
                                   uint32_t arg);
 
 struct SCSIDevice
 {
-    SCSIDeviceState *state;
+    DeviceState qdev;
+    uint32_t id;
+    SCSIDeviceInfo *info;
+};
+
+/* cdrom.c */
+int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track);
+int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
+
+/* scsi-bus.c */
+typedef int (*scsi_qdev_initfn)(SCSIDevice *dev);
+struct SCSIDeviceInfo {
+    DeviceInfo qdev;
+    scsi_qdev_initfn init;
     void (*destroy)(SCSIDevice *s);
     int32_t (*send_command)(SCSIDevice *s, uint32_t tag, uint8_t *buf,
                             int lun);
@@ -24,13 +40,28 @@ struct SCSIDevice
     uint8_t *(*get_buf)(SCSIDevice *s, uint32_t tag);
 };
 
-SCSIDevice *scsi_disk_init(BlockDriverState *bdrv, int tcq,
-                           scsi_completionfn completion, void *opaque);
-SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq,
-                           scsi_completionfn completion, void *opaque);
+typedef void (*SCSIAttachFn)(DeviceState *host, BlockDriverState *bdrv,
+              int unit);
+struct SCSIBus {
+    BusState qbus;
+    int busnr;
 
-/* cdrom.c */
-int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track);
-int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
+    int tcq, ndev;
+    scsi_completionfn complete;
+
+    SCSIDevice *devs[8];
+};
+
+SCSIBus *scsi_bus_new(DeviceState *host, int tcq, int ndev,
+                      scsi_completionfn complete);
+void scsi_qdev_register(SCSIDeviceInfo *info);
+
+static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d)
+{
+    return DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus);
+}
+
+SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, DriveInfo *dinfo, int 
unit);
+void scsi_bus_legacy_handle_cmdline(SCSIBus *bus);
 
 #endif
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index c827c04..f8c010b 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -15,15 +15,7 @@
 #include "block.h"
 #include "scsi-disk.h"
 
-#ifndef __linux__
-
-SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq,
-                              scsi_completionfn completion, void *opaque)
-{
-    return NULL;
-}
-
-#else /* __linux__ */
+#ifdef __linux__
 
 //#define DEBUG_SCSI
 
@@ -60,10 +52,13 @@ do { fprintf(stderr, "scsi-generic: " fmt , ## 
__VA_ARGS__); } while (0)
 #define MAX_UINT ((unsigned int)-1)
 #endif
 
+typedef struct SCSIGenericState SCSIGenericState;
+
 typedef struct SCSIRequest {
     BlockDriverAIOCB *aiocb;
     struct SCSIRequest *next;
-    SCSIDeviceState *dev;
+    SCSIBus *bus;
+    SCSIGenericState *dev;
     uint32_t tag;
     uint8_t cmd[SCSI_CMD_BUF_SIZE];
     int cmdlen;
@@ -73,15 +68,14 @@ typedef struct SCSIRequest {
     sg_io_hdr_t io_header;
 } SCSIRequest;
 
-struct SCSIDeviceState
+struct SCSIGenericState
 {
+    SCSIDevice qdev;
     SCSIRequest *requests;
-    BlockDriverState *bdrv;
+    DriveInfo *dinfo;
     int type;
     int blocksize;
     int lun;
-    scsi_completionfn completion;
-    void *opaque;
     int driver_status;
     uint8_t sensebuf[SCSI_SENSE_BUF_SIZE];
     uint8_t senselen;
@@ -90,8 +84,9 @@ struct SCSIDeviceState
 /* Global pool of SCSIRequest structures.  */
 static SCSIRequest *free_requests = NULL;
 
-static SCSIRequest *scsi_new_request(SCSIDeviceState *s, uint32_t tag)
+static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag)
 {
+    SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
     SCSIRequest *r;
 
     if (free_requests) {
@@ -102,6 +97,7 @@ static SCSIRequest *scsi_new_request(SCSIDeviceState *s, 
uint32_t tag)
         r->buf = NULL;
         r->buflen = 0;
     }
+    r->bus = scsi_bus_from_device(d);
     r->dev = s;
     r->tag = tag;
     memset(r->cmd, 0, sizeof(r->cmd));
@@ -120,7 +116,7 @@ static SCSIRequest *scsi_new_request(SCSIDeviceState *s, 
uint32_t tag)
 static void scsi_remove_request(SCSIRequest *r)
 {
     SCSIRequest *last;
-    SCSIDeviceState *s = r->dev;
+    SCSIGenericState *s = r->dev;
 
     if (s->requests == r) {
         s->requests = r->next;
@@ -138,7 +134,7 @@ static void scsi_remove_request(SCSIRequest *r)
     free_requests = r;
 }
 
-static SCSIRequest *scsi_find_request(SCSIDeviceState *s, uint32_t tag)
+static SCSIRequest *scsi_find_request(SCSIGenericState *s, uint32_t tag)
 {
     SCSIRequest *r;
 
@@ -153,7 +149,7 @@ static SCSIRequest *scsi_find_request(SCSIDeviceState *s, 
uint32_t tag)
 static void scsi_command_complete(void *opaque, int ret)
 {
     SCSIRequest *r = (SCSIRequest *)opaque;
-    SCSIDeviceState *s = r->dev;
+    SCSIGenericState *s = r->dev;
     uint32_t tag;
     int status;
 
@@ -178,14 +174,14 @@ static void scsi_command_complete(void *opaque, int ret)
             r, r->tag, status);
     tag = r->tag;
     scsi_remove_request(r);
-    s->completion(s->opaque, SCSI_REASON_DONE, tag, status);
+    r->bus->complete(r->bus, SCSI_REASON_DONE, tag, status);
 }
 
 /* Cancel a pending data transfer.  */
 static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
 {
     DPRINTF("scsi_cancel_io 0x%x\n", tag);
-    SCSIDeviceState *s = d->state;
+    SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
     SCSIRequest *r;
     DPRINTF("Cancel tag=0x%x\n", tag);
     r = scsi_find_request(s, tag);
@@ -225,7 +221,6 @@ static int execute_command(BlockDriverState *bdrv,
 static void scsi_read_complete(void * opaque, int ret)
 {
     SCSIRequest *r = (SCSIRequest *)opaque;
-    SCSIDeviceState *s = r->dev;
     int len;
 
     if (ret) {
@@ -237,7 +232,7 @@ static void scsi_read_complete(void * opaque, int ret)
     DPRINTF("Data ready tag=0x%x len=%d\n", r->tag, len);
 
     r->len = -1;
-    s->completion(s->opaque, SCSI_REASON_DATA, r->tag, len);
+    r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, len);
     if (len == 0)
         scsi_command_complete(r, 0);
 }
@@ -245,7 +240,7 @@ static void scsi_read_complete(void * opaque, int ret)
 /* Read more data from scsi device into buffer.  */
 static void scsi_read_data(SCSIDevice *d, uint32_t tag)
 {
-    SCSIDeviceState *s = d->state;
+    SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
     SCSIRequest *r;
     int ret;
 
@@ -275,11 +270,11 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag)
         DPRINTF("Sense: %d %d %d %d %d %d %d %d\n",
                 r->buf[0], r->buf[1], r->buf[2], r->buf[3],
                 r->buf[4], r->buf[5], r->buf[6], r->buf[7]);
-        s->completion(s->opaque, SCSI_REASON_DATA, r->tag, s->senselen);
+        r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, s->senselen);
         return;
     }
 
-    ret = execute_command(s->bdrv, r, SG_DXFER_FROM_DEV, scsi_read_complete);
+    ret = execute_command(s->dinfo->bdrv, r, SG_DXFER_FROM_DEV, 
scsi_read_complete);
     if (ret == -1) {
         scsi_command_complete(r, -EINVAL);
         return;
@@ -310,7 +305,7 @@ static void scsi_write_complete(void * opaque, int ret)
    The transfer may complete asynchronously.  */
 static int scsi_write_data(SCSIDevice *d, uint32_t tag)
 {
-    SCSIDeviceState *s = d->state;
+    SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
     SCSIRequest *r;
     int ret;
 
@@ -325,11 +320,11 @@ static int scsi_write_data(SCSIDevice *d, uint32_t tag)
 
     if (r->len == 0) {
         r->len = r->buflen;
-        s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->len);
+        r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, r->len);
         return 0;
     }
 
-    ret = execute_command(s->bdrv, r, SG_DXFER_TO_DEV, scsi_write_complete);
+    ret = execute_command(s->dinfo->bdrv, r, SG_DXFER_TO_DEV, 
scsi_write_complete);
     if (ret == -1) {
         scsi_command_complete(r, -EINVAL);
         return 1;
@@ -341,7 +336,7 @@ static int scsi_write_data(SCSIDevice *d, uint32_t tag)
 /* Return a pointer to the data buffer.  */
 static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
 {
-    SCSIDeviceState *s = d->state;
+    SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
     SCSIRequest *r;
     r = scsi_find_request(s, tag);
     if (!r) {
@@ -514,10 +509,11 @@ static int is_write(int command)
 static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
                                  uint8_t *cmd, int lun)
 {
-    SCSIDeviceState *s = d->state;
+    SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
     uint32_t len=0;
     int cmdlen=0;
     SCSIRequest *r;
+    SCSIBus *bus;
     int ret;
 
     if (s->type == TYPE_TAPE) {
@@ -548,7 +544,8 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
         s->sensebuf[6] = 0x00;
         s->senselen = 7;
         s->driver_status = SG_ERR_DRIVER_SENSE;
-        s->completion(s->opaque, SCSI_REASON_DONE, tag, CHECK_CONDITION << 1);
+        bus = scsi_bus_from_device(d);
+        bus->complete(bus, SCSI_REASON_DONE, tag, CHECK_CONDITION << 1);
         return 0;
     }
 
@@ -557,7 +554,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
         BADF("Tag 0x%x already in use %p\n", tag, r);
         scsi_cancel_io(d, tag);
     }
-    r = scsi_new_request(s, tag);
+    r = scsi_new_request(d, tag);
 
     memcpy(r->cmd, cmd, cmdlen);
     r->cmdlen = cmdlen;
@@ -567,7 +564,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t 
tag,
             free(r->buf);
         r->buflen = 0;
         r->buf = NULL;
-        ret = execute_command(s->bdrv, r, SG_DXFER_NONE, 
scsi_command_complete);
+        ret = execute_command(s->dinfo->bdrv, r, SG_DXFER_NONE, 
scsi_command_complete);
         if (ret == -1) {
             scsi_command_complete(r, -EINVAL);
             return 0;
@@ -655,9 +652,10 @@ static int get_stream_blocksize(BlockDriverState *bdrv)
 
 static void scsi_destroy(SCSIDevice *d)
 {
+    SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
     SCSIRequest *r, *n;
 
-    r = d->state->requests;
+    r = s->requests;
     while (r) {
         n = r->next;
         qemu_free(r);
@@ -671,51 +669,50 @@ static void scsi_destroy(SCSIDevice *d)
         r = n;
     }
 
-    qemu_free(d->state);
     qemu_free(d);
 }
 
-SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq,
-                              scsi_completionfn completion, void *opaque)
+static int scsi_generic_initfn(SCSIDevice *dev)
 {
+    SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, dev);
     int sg_version;
-    SCSIDevice *d;
-    SCSIDeviceState *s;
     struct sg_scsi_id scsiid;
 
-    /* check we are really using a /dev/sg* file */
+    if (!s->dinfo || !s->dinfo->bdrv) {
+        qemu_error("scsi-generic: drive property not set\n");
+        return -1;
+    }
 
-    if (!bdrv_is_sg(bdrv))
-        return NULL;
+    /* check we are really using a /dev/sg* file */
+    if (!bdrv_is_sg(s->dinfo->bdrv)) {
+        qemu_error("scsi-generic: not /dev/sg*\n");
+        return -1;
+    }
 
     /* check we are using a driver managing SG_IO (version 3 and after */
-
-    if (bdrv_ioctl(bdrv, SG_GET_VERSION_NUM, &sg_version) < 0 ||
-        sg_version < 30000)
-        return NULL;
+    if (bdrv_ioctl(s->dinfo->bdrv, SG_GET_VERSION_NUM, &sg_version) < 0 ||
+        sg_version < 30000) {
+        qemu_error("scsi-generic: scsi generic interface too old\n");
+        return -1;
+    }
 
     /* get LUN of the /dev/sg? */
-
-    if (bdrv_ioctl(bdrv, SG_GET_SCSI_ID, &scsiid))
-        return NULL;
+    if (bdrv_ioctl(s->dinfo->bdrv, SG_GET_SCSI_ID, &scsiid)) {
+        qemu_error("scsi-generic: SG_GET_SCSI_ID ioctl failed\n");
+        return -1;
+    }
 
     /* define device state */
-
-    s = (SCSIDeviceState *)qemu_mallocz(sizeof(SCSIDeviceState));
-    s->bdrv = bdrv;
-    s->requests = NULL;
-    s->completion = completion;
-    s->opaque = opaque;
     s->lun = scsiid.lun;
     DPRINTF("LUN %d\n", s->lun);
     s->type = scsiid.scsi_type;
     DPRINTF("device type %d\n", s->type);
     if (s->type == TYPE_TAPE) {
-        s->blocksize = get_stream_blocksize(s->bdrv);
+        s->blocksize = get_stream_blocksize(s->dinfo->bdrv);
         if (s->blocksize == -1)
             s->blocksize = 0;
     } else {
-        s->blocksize = get_blocksize(s->bdrv);
+        s->blocksize = get_blocksize(s->dinfo->bdrv);
         /* removable media returns 0 if not present */
         if (s->blocksize <= 0) {
             if (s->type == TYPE_ROM || s->type  == TYPE_WORM)
@@ -727,18 +724,30 @@ SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int 
tcq,
     DPRINTF("block size %d\n", s->blocksize);
     s->driver_status = 0;
     memset(s->sensebuf, 0, sizeof(s->sensebuf));
+    return 0;
+}
 
-    /* define function to manage device */
-
-    d = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice));
-    d->state = s;
-    d->destroy = scsi_destroy;
-    d->send_command = scsi_send_command;
-    d->read_data = scsi_read_data;
-    d->write_data = scsi_write_data;
-    d->cancel_io = scsi_cancel_io;
-    d->get_buf = scsi_get_buf;
+static SCSIDeviceInfo scsi_generic_info = {
+    .qdev.name    = "scsi-generic",
+    .qdev.desc    = "pass through generic scsi device (/dev/sg*)",
+    .qdev.size    = sizeof(SCSIGenericState),
+    .init         = scsi_generic_initfn,
+    .destroy      = scsi_destroy,
+    .send_command = scsi_send_command,
+    .read_data    = scsi_read_data,
+    .write_data   = scsi_write_data,
+    .cancel_io    = scsi_cancel_io,
+    .get_buf      = scsi_get_buf,
+    .qdev.props   = (Property[]) {
+        DEFINE_PROP_DRIVE("drive", SCSIGenericState, dinfo),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
 
-    return d;
+static void scsi_generic_register_devices(void)
+{
+    scsi_qdev_register(&scsi_generic_info);
 }
+device_init(scsi_generic_register_devices)
+
 #endif /* __linux__ */
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index bc5150f..222689a 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -44,6 +44,7 @@ typedef struct {
     uint32_t residue;
     uint32_t tag;
     BlockDriverState *bs;
+    SCSIBus *bus;
     SCSIDevice *scsi_dev;
     int result;
     /* For async completion.  */
@@ -150,9 +151,9 @@ static void usb_msd_copy_data(MSDState *s)
     s->data_len -= len;
     if (s->scsi_len == 0) {
         if (s->mode == USB_MSDM_DATAIN) {
-            s->scsi_dev->read_data(s->scsi_dev, s->tag);
+            s->scsi_dev->info->read_data(s->scsi_dev, s->tag);
         } else if (s->mode == USB_MSDM_DATAOUT) {
-            s->scsi_dev->write_data(s->scsi_dev, s->tag);
+            s->scsi_dev->info->write_data(s->scsi_dev, s->tag);
         }
     }
 }
@@ -168,10 +169,10 @@ static void usb_msd_send_status(MSDState *s)
     memcpy(s->usb_buf, &csw, 13);
 }
 
-static void usb_msd_command_complete(void *opaque, int reason, uint32_t tag,
+static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
                                      uint32_t arg)
 {
-    MSDState *s = (MSDState *)opaque;
+    MSDState *s = DO_UPCAST(MSDState, dev.qdev, bus->qbus.parent);
     USBPacket *p = s->packet;
 
     if (tag != s->tag) {
@@ -205,7 +206,7 @@ static void usb_msd_command_complete(void *opaque, int 
reason, uint32_t tag,
         return;
     }
     s->scsi_len = arg;
-    s->scsi_buf = s->scsi_dev->get_buf(s->scsi_dev, tag);
+    s->scsi_buf = s->scsi_dev->info->get_buf(s->scsi_dev, tag);
     if (p) {
         usb_msd_copy_data(s);
         if (s->usb_len == 0) {
@@ -343,7 +344,7 @@ static int usb_msd_handle_control(USBDevice *dev, int 
request, int value,
 static void usb_msd_cancel_io(USBPacket *p, void *opaque)
 {
     MSDState *s = opaque;
-    s->scsi_dev->cancel_io(s->scsi_dev, s->tag);
+    s->scsi_dev->info->cancel_io(s->scsi_dev, s->tag);
     s->packet = NULL;
     s->scsi_len = 0;
 }
@@ -391,14 +392,14 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket 
*p)
             DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
                     s->tag, cbw.flags, cbw.cmd_len, s->data_len);
             s->residue = 0;
-            s->scsi_dev->send_command(s->scsi_dev, s->tag, cbw.cmd, 0);
+            s->scsi_dev->info->send_command(s->scsi_dev, s->tag, cbw.cmd, 0);
             /* ??? Should check that USB and SCSI data transfer
                directions match.  */
             if (s->residue == 0) {
                 if (s->mode == USB_MSDM_DATAIN) {
-                    s->scsi_dev->read_data(s->scsi_dev, s->tag);
+                    s->scsi_dev->info->read_data(s->scsi_dev, s->tag);
                 } else if (s->mode == USB_MSDM_DATAOUT) {
-                    s->scsi_dev->write_data(s->scsi_dev, s->tag);
+                    s->scsi_dev->info->write_data(s->scsi_dev, s->tag);
                 }
             }
             ret = len;
@@ -509,7 +510,7 @@ static void usb_msd_handle_destroy(USBDevice *dev)
 {
     MSDState *s = (MSDState *)dev;
 
-    s->scsi_dev->destroy(s->scsi_dev);
+    s->scsi_dev->info->destroy(s->scsi_dev);
     bdrv_delete(s->bs);
     qemu_free(s);
 }
@@ -567,7 +568,10 @@ USBDevice *usb_msd_init(const char *filename)
     snprintf(s->dev.devname, sizeof(s->dev.devname), "QEMU USB MSD(%.16s)",
              filename);
 
-    s->scsi_dev = scsi_disk_init(bdrv, 0, usb_msd_command_complete, s);
+    s->bus = scsi_bus_new(&s->dev.qdev, 0, 1, usb_msd_command_complete);
+#if 0
+    s->scsi_dev = scsi_disk_init(s->bus, bdrv);
+#endif
     usb_msd_handle_reset((USBDevice *)s);
     return (USBDevice *)s;
 }
-- 
1.6.2.5





reply via email to

[Prev in Thread] Current Thread [Next in Thread]