scsi-bus.c 2.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#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.  */
18 19
void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev,
                  scsi_completionfn complete)
20
{
21
    qbus_create_inplace(&bus->qbus, &scsi_bus_info, host, NULL);
22 23 24 25
    bus->busnr = next_scsi_bus++;
    bus->tcq = tcq;
    bus->ndev = ndev;
    bus->complete = complete;
G
Gerd Hoffmann 已提交
26
    bus->qbus.allow_hotplug = 1;
27 28 29 30 31 32 33
}

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);
G
Gerd Hoffmann 已提交
34
    int rc = -1;
35 36 37 38 39 40 41 42 43 44 45 46 47

    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]) {
G
Gerd Hoffmann 已提交
48
        qdev_free(&bus->devs[dev->id]->qdev);
49 50 51 52
    }
    bus->devs[dev->id] = dev;

    dev->info = info;
G
Gerd Hoffmann 已提交
53 54 55 56
    rc = dev->info->init(dev);
    if (rc != 0) {
        bus->devs[dev->id] = NULL;
    }
57 58

err:
G
Gerd Hoffmann 已提交
59 60 61 62 63 64 65 66 67 68 69 70 71 72
    return rc;
}

static int scsi_qdev_exit(DeviceState *qdev)
{
    SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
    SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);

    assert(bus->devs[dev->id] != NULL);
    if (bus->devs[dev->id]->info->destroy) {
        bus->devs[dev->id]->info->destroy(bus->devs[dev->id]);
    }
    bus->devs[dev->id] = NULL;
    return 0;
73 74 75 76 77 78
}

void scsi_qdev_register(SCSIDeviceInfo *info)
{
    info->qdev.bus_info = &scsi_bus_info;
    info->qdev.init     = scsi_qdev_init;
G
Gerd Hoffmann 已提交
79
    info->qdev.unplug   = qdev_simple_unplug_cb;
G
Gerd Hoffmann 已提交
80
    info->qdev.exit     = scsi_qdev_exit;
81 82 83 84
    qdev_register(&info->qdev);
}

/* handle legacy '-drive if=scsi,...' cmd line args */
85
/* FIXME callers should check for failure, but don't */
86 87 88 89 90 91 92 93 94
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);
95 96
    if (qdev_init(dev) < 0)
        return NULL;
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
    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);
    }
}