From d52affa7f6b9df3c7d44da0effbdfc8339c43914 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 31 Aug 2009 14:24:04 +0200 Subject: [PATCH] qdev/scsi: add scsi bus support to qdev, convert drivers. * 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 Signed-off-by: Anthony Liguori --- 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 | 149 +++++++++++++++++++++++-------------------- hw/usb-msd.c | 26 ++++---- 12 files changed, 353 insertions(+), 276 deletions(-) create mode 100644 hw/scsi-bus.c diff --git a/Makefile b/Makefile index e05fa161e4..cb2614b0c6 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 54878b610d..b5ddfb2f92 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_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid) DPRINTF("do_busid_cmd: busid 0x%x\n", busid); lun = busid & 7; - datalen = s->current_dev->send_command(s->current_dev, 0, buf, lun); + datalen = s->current_dev->info->send_command(s->current_dev, 0, buf, lun); s->ti_size = datalen; if (datalen != 0) { s->rregs[ESP_RSTAT] = STAT_TC; @@ -218,10 +218,10 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid) 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; @@ -338,9 +338,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. */ @@ -354,10 +354,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"); @@ -375,7 +375,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) { @@ -652,33 +652,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, @@ -719,7 +692,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 bf9f072fe9..62bdca8032 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 6bd837a401..5348dd1b2e 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) @@ -58,6 +59,7 @@ void drive_hot_add(Monitor *mon, const QDict *qdict) DriveInfo *dinfo; const char *pci_addr = qdict_get_str(qdict, "pci_addr"); const char *opts = qdict_get_str(qdict, "opts"); + BusState *scsibus; if (pci_read_devaddr(mon, pci_addr, &dom, &pci_bus, &slot)) { return; @@ -82,8 +84,9 @@ void drive_hot_add(Monitor *mon, const QDict *qdict) 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 06865918cd..dee2841fbd 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 f9754b1140..0e9732b1ca 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 af735d7209..56a0c1f1bb 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 0000000000..16afa0532a --- /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 5b825c9f79..ffc2654bf9 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 f42212bc22..febde44a92 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 c827c04a19..f8c010b639 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)); - - /* 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; - - return d; + return 0; } + +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(), + }, +}; + +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 bc5150f292..222689aa34 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; }