sim-card
/
qemu
Archived
10
0
Fork 0

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 <kraxel@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
Gerd Hoffmann 2009-08-31 14:24:04 +02:00 committed by Anthony Liguori
parent 5b19d9a247
commit d52affa7f6
12 changed files with 353 additions and 276 deletions

View File

@ -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 += ssd0303.o ssd0323.o ads7846.o stellaris_input.o twl92230.o
obj-y += tmp105.o lm832x.o eeprom93xx.o tsc2005.o obj-y += tmp105.o lm832x.o eeprom93xx.o tsc2005.o
obj-y += scsi-disk.o cdrom.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.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 += usb-serial.o usb-net.o usb-bus.o
obj-y += sd.o ssi-sd.o obj-y += sd.o ssi-sd.o

View File

@ -63,7 +63,7 @@ struct ESPState {
uint8_t ti_buf[TI_BUFSZ]; uint8_t ti_buf[TI_BUFSZ];
uint32_t sense; uint32_t sense;
uint32_t dma; uint32_t dma;
SCSIDevice *scsi_dev[ESP_MAX_DEVS]; SCSIBus *bus;
SCSIDevice *current_dev; SCSIDevice *current_dev;
uint8_t cmdbuf[TI_BUFSZ]; uint8_t cmdbuf[TI_BUFSZ];
uint32_t cmdlen; uint32_t cmdlen;
@ -187,11 +187,11 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf)
if (s->current_dev) { if (s->current_dev) {
/* Started a new command before the old one finished. Cancel it. */ /* 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; 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 // No such drive
s->rregs[ESP_RSTAT] = 0; s->rregs[ESP_RSTAT] = 0;
s->rregs[ESP_RINTR] = INTR_DC; s->rregs[ESP_RINTR] = INTR_DC;
@ -199,7 +199,7 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf)
esp_raise_irq(s); esp_raise_irq(s);
return 0; return 0;
} }
s->current_dev = s->scsi_dev[target]; s->current_dev = s->bus->devs[target];
return dmalen; 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); DPRINTF("do_busid_cmd: busid 0x%x\n", busid);
lun = busid & 7; 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; s->ti_size = datalen;
if (datalen != 0) { if (datalen != 0) {
s->rregs[ESP_RSTAT] = STAT_TC; 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; s->dma_counter = 0;
if (datalen > 0) { if (datalen > 0) {
s->rregs[ESP_RSTAT] |= STAT_DI; 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 { } else {
s->rregs[ESP_RSTAT] |= STAT_DO; 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; 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 (s->async_len == 0) {
if (to_device) { if (to_device) {
// ti_size is negative // ti_size is negative
s->current_dev->write_data(s->current_dev, 0); s->current_dev->info->write_data(s->current_dev, 0);
} else { } 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 /* If there is still data to be read from the device then
complete the DMA operation immediately. Otherwise defer complete the DMA operation immediately. Otherwise defer
until the scsi layer has completed. */ 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) uint32_t arg)
{ {
ESPState *s = (ESPState *)opaque; ESPState *s = DO_UPCAST(ESPState, busdev.qdev, bus->qbus.parent);
if (reason == SCSI_REASON_DONE) { if (reason == SCSI_REASON_DONE) {
DPRINTF("SCSI Command complete\n"); DPRINTF("SCSI Command complete\n");
@ -375,7 +375,7 @@ static void esp_command_complete(void *opaque, int reason, uint32_t tag,
} else { } else {
DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size); DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size);
s->async_len = arg; 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) { if (s->dma_left) {
esp_do_dma(s); esp_do_dma(s);
} else if (s->dma_counter != 0 && s->ti_size <= 0) { } 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; 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, void esp_init(target_phys_addr_t espaddr, int it_shift,
espdma_memory_read_write dma_memory_read, espdma_memory_read_write dma_memory_read,
espdma_memory_read_write dma_memory_write, 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); 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; return 0;
} }

View File

@ -14,6 +14,7 @@
#include "hw.h" #include "hw.h"
#include "pci.h" #include "pci.h"
#include "scsi.h"
#include "scsi-disk.h" #include "scsi-disk.h"
#include "block_int.h" #include "block_int.h"
@ -192,7 +193,7 @@ typedef struct {
* 2 if processing DMA from lsi_execute_script. * 2 if processing DMA from lsi_execute_script.
* 3 if a DMA operation is in progress. */ * 3 if a DMA operation is in progress. */
int waiting; int waiting;
SCSIDevice *scsi_dev[LSI_MAX_DEVS]; SCSIBus *bus;
SCSIDevice *current_dev; SCSIDevice *current_dev;
int current_lun; int current_lun;
/* The tag is a combination of the device ID and the SCSI tag. */ /* 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; s->dbc -= count;
if (s->dma_buf == NULL) { if (s->dma_buf == NULL) {
s->dma_buf = s->current_dev->get_buf(s->current_dev, s->dma_buf = s->current_dev->info->get_buf(s->current_dev,
s->current_tag); s->current_tag);
} }
/* ??? Set SFBR to first data byte. */ /* ??? Set SFBR to first data byte. */
@ -525,10 +526,10 @@ static void lsi_do_dma(LSIState *s, int out)
s->dma_buf = NULL; s->dma_buf = NULL;
if (out) { if (out) {
/* Write the data. */ /* 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 { } else {
/* Request any remaining data. */ /* 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 { } else {
s->dma_buf += count; s->dma_buf += count;
@ -584,7 +585,7 @@ static void lsi_reselect(LSIState *s, uint32_t tag)
id = (tag >> 8) & 0xf; id = (tag >> 8) & 0xf;
s->ssid = id | 0x80; s->ssid = id | 0x80;
DPRINTF("Reselected target %d\n", id); 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->current_tag = tag;
s->scntl1 |= LSI_SCNTL1_CON; s->scntl1 |= LSI_SCNTL1_CON;
lsi_set_phase(s, PHASE_MI); 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. */ /* 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) uint32_t arg)
{ {
LSIState *s = opaque; LSIState *s = DO_UPCAST(LSIState, dev.qdev, bus->qbus.parent);
int out; int out;
out = (s->sstat1 & PHASE_MASK) == PHASE_DO; 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); cpu_physical_memory_read(s->dnad, buf, s->dbc);
s->sfbr = buf[0]; s->sfbr = buf[0];
s->command_complete = 0; s->command_complete = 0;
n = s->current_dev->send_command(s->current_dev, s->current_tag, buf, n = s->current_dev->info->send_command(s->current_dev, s->current_tag, buf,
s->current_lun); s->current_lun);
if (n > 0) { if (n > 0) {
lsi_set_phase(s, PHASE_DI); 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) { } else if (n < 0) {
lsi_set_phase(s, PHASE_DO); 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) { if (!s->command_complete) {
@ -1040,7 +1041,7 @@ again:
} }
s->sstat0 |= LSI_SSTAT0_WOA; s->sstat0 |= LSI_SSTAT0_WOA;
s->scntl1 &= ~LSI_SCNTL1_IARB; 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); DPRINTF("Selected absent target %d\n", id);
lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO); lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO);
lsi_disconnect(s); lsi_disconnect(s);
@ -1051,7 +1052,7 @@ again:
/* ??? Linux drivers compain when this is set. Maybe /* ??? Linux drivers compain when this is set. Maybe
it only applies in low-level mode (unimplemented). it only applies in low-level mode (unimplemented).
lsi_script_scsi_interrupt(s, LSI_SIST0_CMP, 0); */ 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->current_tag = id << 8;
s->scntl1 |= LSI_SCNTL1_CON; s->scntl1 |= LSI_SCNTL1_CON;
if (insn & (1 << 3)) { 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); 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) static void lsi_scsi_save(QEMUFile *f, void *opaque)
{ {
LSIState *s = opaque; LSIState *s = opaque;
@ -2202,16 +2178,17 @@ static int lsi_scsi_init(PCIDevice *dev)
lsi_soft_reset(s); 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); register_savevm("lsiscsi", -1, 0, lsi_scsi_save, lsi_scsi_load, s);
return 0; return 0;
} }
static PCIDeviceInfo lsi_info = { static PCIDeviceInfo lsi_info = {
.qdev.name = "lsi53c895a", .qdev.name = "lsi53c895a",
.qdev.size = sizeof(LSIState), .qdev.alias = "lsi",
.init = lsi_scsi_init, .qdev.size = sizeof(LSIState),
.init = lsi_scsi_init,
}; };
static void lsi53c895a_register_devices(void) static void lsi53c895a_register_devices(void)

View File

@ -30,6 +30,7 @@
#include "pc.h" #include "pc.h"
#include "monitor.h" #include "monitor.h"
#include "block_int.h" #include "block_int.h"
#include "scsi-disk.h"
#include "virtio-blk.h" #include "virtio-blk.h"
#if defined(TARGET_I386) || defined(TARGET_X86_64) #if defined(TARGET_I386) || defined(TARGET_X86_64)
@ -58,6 +59,7 @@ void drive_hot_add(Monitor *mon, const QDict *qdict)
DriveInfo *dinfo; DriveInfo *dinfo;
const char *pci_addr = qdict_get_str(qdict, "pci_addr"); const char *pci_addr = qdict_get_str(qdict, "pci_addr");
const char *opts = qdict_get_str(qdict, "opts"); const char *opts = qdict_get_str(qdict, "opts");
BusState *scsibus;
if (pci_read_devaddr(mon, pci_addr, &dom, &pci_bus, &slot)) { if (pci_read_devaddr(mon, pci_addr, &dom, &pci_bus, &slot)) {
return; return;
@ -82,8 +84,9 @@ void drive_hot_add(Monitor *mon, const QDict *qdict)
switch (type) { switch (type) {
case IF_SCSI: case IF_SCSI:
success = 1; success = 1;
lsi_scsi_attach(&dev->qdev, dinfo->bdrv, scsibus = LIST_FIRST(&dev->qdev.child_bus);
dinfo->unit); scsi_bus_legacy_add_drive(DO_UPCAST(SCSIBus, qbus, scsibus),
dinfo, dinfo->unit);
break; break;
default: default:
monitor_printf(mon, "Can't hot-add drive to type %d\n", type); monitor_printf(mon, "Can't hot-add drive to type %d\n", type);

View File

@ -331,7 +331,6 @@ PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name);
/* lsi53c895a.c */ /* lsi53c895a.c */
#define LSI_MAX_DEVS 7 #define LSI_MAX_DEVS 7
void lsi_scsi_attach(DeviceState *host, BlockDriverState *bd, int id);
/* vmware_vga.c */ /* vmware_vga.c */
void pci_vmsvga_init(PCIBus *bus); void pci_vmsvga_init(PCIBus *bus);

View File

@ -314,25 +314,6 @@ BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
return NULL; 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, static BusState *qbus_find_recursive(BusState *bus, const char *name,
const BusInfo *info) const BusInfo *info)
{ {

View File

@ -99,8 +99,6 @@ BusState *qdev_get_child_bus(DeviceState *dev, const char *name);
/*** Device API. ***/ /*** Device API. ***/
typedef int (*qdev_initfn)(DeviceState *dev, DeviceInfo *info); typedef int (*qdev_initfn)(DeviceState *dev, DeviceInfo *info);
typedef void (*SCSIAttachFn)(DeviceState *host, BlockDriverState *bdrv,
int unit);
struct DeviceInfo { struct DeviceInfo {
const char *name; 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_in(DeviceState *dev, qemu_irq_handler handler, int n);
void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, 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); CharDriverState *qdev_init_chardev(DeviceState *dev);
BusState *qdev_get_parent_bus(DeviceState *dev); BusState *qdev_get_parent_bus(DeviceState *dev);

93
hw/scsi-bus.c Normal file
View File

@ -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);
}
}

View File

@ -44,8 +44,11 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#define SCSI_REQ_STATUS_RETRY 0x01 #define SCSI_REQ_STATUS_RETRY 0x01
typedef struct SCSIDiskState SCSIDiskState;
typedef struct SCSIRequest { typedef struct SCSIRequest {
SCSIDeviceState *dev; SCSIBus *bus;
SCSIDiskState *dev;
uint32_t tag; uint32_t tag;
/* ??? We should probably keep track of whether the data transfer is /* ??? 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. */ a read or a write. Currently we rely on the host getting it right. */
@ -59,20 +62,16 @@ typedef struct SCSIRequest {
uint32_t status; uint32_t status;
} SCSIRequest; } SCSIRequest;
struct SCSIDeviceState struct SCSIDiskState
{ {
BlockDriverState *bdrv; SCSIDevice qdev;
DriveInfo *dinfo;
SCSIRequest *requests; SCSIRequest *requests;
/* The qemu block layer uses a fixed 512 byte sector size. /* The qemu block layer uses a fixed 512 byte sector size.
This is the number of 512 byte blocks in a single scsi sector. */ This is the number of 512 byte blocks in a single scsi sector. */
int cluster_size; int cluster_size;
uint64_t max_lba; uint64_t max_lba;
int sense; 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]; char drive_serial_str[21];
QEMUBH *bh; QEMUBH *bh;
}; };
@ -80,8 +79,9 @@ struct SCSIDeviceState
/* Global pool of SCSIRequest structures. */ /* Global pool of SCSIRequest structures. */
static SCSIRequest *free_requests = NULL; 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; SCSIRequest *r;
if (free_requests) { if (free_requests) {
@ -91,6 +91,7 @@ static SCSIRequest *scsi_new_request(SCSIDeviceState *s, uint32_t tag)
r = qemu_malloc(sizeof(SCSIRequest)); r = qemu_malloc(sizeof(SCSIRequest));
r->iov.iov_base = qemu_memalign(512, SCSI_DMA_BUF_SIZE); r->iov.iov_base = qemu_memalign(512, SCSI_DMA_BUF_SIZE);
} }
r->bus = scsi_bus_from_device(d);
r->dev = s; r->dev = s;
r->tag = tag; r->tag = tag;
r->sector_count = 0; 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) static void scsi_remove_request(SCSIRequest *r)
{ {
SCSIRequest *last; SCSIRequest *last;
SCSIDeviceState *s = r->dev; SCSIDiskState *s = r->dev;
if (s->requests == r) { if (s->requests == r) {
s->requests = r->next; s->requests = r->next;
@ -124,7 +125,7 @@ static void scsi_remove_request(SCSIRequest *r)
free_requests = 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; SCSIRequest *r;
@ -138,19 +139,19 @@ static SCSIRequest *scsi_find_request(SCSIDeviceState *s, uint32_t tag)
/* Helper function for command completion. */ /* Helper function for command completion. */
static void scsi_command_complete(SCSIRequest *r, int status, int sense) static void scsi_command_complete(SCSIRequest *r, int status, int sense)
{ {
SCSIDeviceState *s = r->dev; SCSIDiskState *s = r->dev;
uint32_t tag; uint32_t tag;
DPRINTF("Command complete tag=0x%x status=%d sense=%d\n", r->tag, status, sense); DPRINTF("Command complete tag=0x%x status=%d sense=%d\n", r->tag, status, sense);
s->sense = sense; s->sense = sense;
tag = r->tag; tag = r->tag;
scsi_remove_request(r); 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. */ /* Cancel a pending data transfer. */
static void scsi_cancel_io(SCSIDevice *d, uint32_t tag) static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
{ {
SCSIDeviceState *s = d->state; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *r; SCSIRequest *r;
DPRINTF("Cancel tag=0x%x\n", tag); DPRINTF("Cancel tag=0x%x\n", tag);
r = scsi_find_request(s, 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) static void scsi_read_complete(void * opaque, int ret)
{ {
SCSIRequest *r = (SCSIRequest *)opaque; SCSIRequest *r = (SCSIRequest *)opaque;
SCSIDeviceState *s = r->dev;
if (ret) { if (ret) {
DPRINTF("IO error\n"); 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); scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_NO_SENSE);
return; return;
} }
DPRINTF("Data ready tag=0x%x len=%" PRId64 "\n", r->tag, r->iov.iov_len); 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. */ /* Read more data from scsi device into buffer. */
static void scsi_read_data(SCSIDevice *d, uint32_t tag) static void scsi_read_data(SCSIDevice *d, uint32_t tag)
{ {
SCSIDeviceState *s = d->state; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *r; SCSIRequest *r;
uint32_t n; uint32_t n;
@ -195,7 +195,7 @@ static void scsi_read_data(SCSIDevice *d, uint32_t tag)
if (r->sector_count == (uint32_t)-1) { if (r->sector_count == (uint32_t)-1) {
DPRINTF("Read buf_len=%" PRId64 "\n", r->iov.iov_len); DPRINTF("Read buf_len=%" PRId64 "\n", r->iov.iov_len);
r->sector_count = 0; 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; return;
} }
DPRINTF("Read sector_count=%d\n", r->sector_count); 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; r->iov.iov_len = n * 512;
qemu_iovec_init_external(&r->qiov, &r->iov, 1); 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); scsi_read_complete, r);
if (r->aiocb == NULL) if (r->aiocb == NULL)
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR); 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) 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) if (action == BLOCK_ERR_IGNORE)
return 0; 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) static void scsi_write_complete(void * opaque, int ret)
{ {
SCSIRequest *r = (SCSIRequest *)opaque; SCSIRequest *r = (SCSIRequest *)opaque;
SCSIDeviceState *s = r->dev;
uint32_t len; uint32_t len;
uint32_t n; uint32_t n;
@ -263,19 +262,19 @@ static void scsi_write_complete(void * opaque, int ret)
} }
r->iov.iov_len = len; r->iov.iov_len = len;
DPRINTF("Write complete tag=0x%x more=%d\n", r->tag, 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) static void scsi_write_request(SCSIRequest *r)
{ {
SCSIDeviceState *s = r->dev; SCSIDiskState *s = r->dev;
uint32_t n; uint32_t n;
n = r->iov.iov_len / 512; n = r->iov.iov_len / 512;
if (n) { if (n) {
qemu_iovec_init_external(&r->qiov, &r->iov, 1); 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); scsi_write_complete, r);
if (r->aiocb == NULL) if (r->aiocb == NULL)
scsi_command_complete(r, STATUS_CHECK_CONDITION, scsi_command_complete(r, STATUS_CHECK_CONDITION,
@ -290,7 +289,7 @@ static void scsi_write_request(SCSIRequest *r)
The transfer may complete asynchronously. */ The transfer may complete asynchronously. */
static int scsi_write_data(SCSIDevice *d, uint32_t tag) static int scsi_write_data(SCSIDevice *d, uint32_t tag)
{ {
SCSIDeviceState *s = d->state; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *r; SCSIRequest *r;
DPRINTF("Write data tag=0x%x\n", tag); 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) static void scsi_dma_restart_bh(void *opaque)
{ {
SCSIDeviceState *s = opaque; SCSIDiskState *s = opaque;
SCSIRequest *r = s->requests; SCSIRequest *r = s->requests;
qemu_bh_delete(s->bh); 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) static void scsi_dma_restart_cb(void *opaque, int running, int reason)
{ {
SCSIDeviceState *s = opaque; SCSIDiskState *s = opaque;
if (!running) if (!running)
return; return;
@ -342,7 +341,7 @@ static void scsi_dma_restart_cb(void *opaque, int running, int reason)
/* Return a pointer to the data buffer. */ /* Return a pointer to the data buffer. */
static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag) static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
{ {
SCSIDeviceState *s = d->state; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *r; SCSIRequest *r;
r = scsi_find_request(s, tag); 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, static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
uint8_t *buf, int lun) uint8_t *buf, int lun)
{ {
SCSIDeviceState *s = d->state; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
uint64_t nb_sectors; uint64_t nb_sectors;
uint64_t lba; uint64_t lba;
uint32_t len; 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 /* ??? Tags are not unique for different luns. We only implement a
single lun, so this should not matter. */ 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; outbuf = (uint8_t *)r->iov.iov_base;
is_write = 0; is_write = 0;
DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[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) { switch (command) {
case 0x0: case 0x0:
DPRINTF("Test Unit Ready\n"); DPRINTF("Test Unit Ready\n");
if (!bdrv_is_inserted(s->bdrv)) if (!bdrv_is_inserted(s->dinfo->bdrv))
goto notready; goto notready;
break; break;
case 0x03: case 0x03:
@ -479,7 +478,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
r->iov.iov_len = 0; 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; outbuf[r->iov.iov_len++] = 5;
} else { } else {
outbuf[r->iov.iov_len++] = 0; 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; r->iov.iov_len = 0;
/* Supported page codes */ /* 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; outbuf[r->iov.iov_len++] = 5;
} else { } else {
outbuf[r->iov.iov_len++] = 0; 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 */ /* Device identification page, mandatory */
int max_len = 255 - 8; 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) if (id_len > max_len)
id_len = max_len; id_len = max_len;
DPRINTF("Inquiry EVPD[Device identification] " DPRINTF("Inquiry EVPD[Device identification] "
"buffer size %d\n", len); "buffer size %d\n", len);
r->iov.iov_len = 0; 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; outbuf[r->iov.iov_len++] = 5;
} else { } else {
outbuf[r->iov.iov_len++] = 0; 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 outbuf[r->iov.iov_len++] = id_len; // length of data following
memcpy(&outbuf[r->iov.iov_len], 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; r->iov.iov_len += id_len;
} }
break; break;
@ -591,7 +590,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
if (lun || buf[1] >> 5) { if (lun || buf[1] >> 5) {
outbuf[0] = 0x7f; /* LUN not supported */ 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[0] = 5;
outbuf[1] = 0x80; outbuf[1] = 0x80;
memcpy(&outbuf[16], "QEMU CD-ROM ", 16); 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[3] = 2; /* Format 2 */
outbuf[4] = len - 5; /* Additional Length = (Len - 1) - 4 */ outbuf[4] = len - 5; /* Additional Length = (Len - 1) - 4 */
/* Sync data transfer and TCQ. */ /* 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; r->iov.iov_len = len;
break; break;
case 0x16: case 0x16:
@ -632,7 +631,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
memset(p, 0, 4); memset(p, 0, 4);
outbuf[1] = 0; /* Default media type. */ outbuf[1] = 0; /* Default media type. */
outbuf[3] = 0; /* Block descriptor length. */ 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. */ outbuf[2] = 0x80; /* Readonly. */
} }
p += 4; p += 4;
@ -643,7 +642,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
p[0] = 4; p[0] = 4;
p[1] = 0x16; p[1] = 0x16;
/* if a geometry hint is available, use it */ /* 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[2] = (cylinders >> 16) & 0xff;
p[3] = (cylinders >> 8) & 0xff; p[3] = (cylinders >> 8) & 0xff;
p[4] = cylinders & 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[2] = 5000 >> 8;
p[3] = 5000 & 0xff; p[3] = 5000 & 0xff;
/* if a geometry hint is available, use it */ /* 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[4] = heads & 0xff;
p[5] = secs & 0xff; p[5] = secs & 0xff;
p[6] = s->cluster_size * 2; p[6] = s->cluster_size * 2;
@ -714,7 +713,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
p += 20; p += 20;
} }
if ((page == 0x3f || page == 0x2a) 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. */ /* CD Capabilities and Mechanical Status page. */
p[0] = 0x2a; p[0] = 0x2a;
p[1] = 0x14; 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, p[5] = 0xff; /* CD DA, DA accurate, RW supported,
RW corrected, C2 errors, ISRC, RW corrected, C2 errors, ISRC,
UPC, Bar code */ 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 */ /* Locking supported, jumper present, eject, tray */
p[7] = 0; /* no volume & mute control, no p[7] = 0; /* no volume & mute control, no
changer */ changer */
@ -751,20 +750,20 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
break; break;
case 0x1b: case 0x1b:
DPRINTF("Start Stop Unit\n"); 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)) (buf[4] & 2))
/* load/eject medium */ /* load/eject medium */
bdrv_eject(s->bdrv, !(buf[4] & 1)); bdrv_eject(s->dinfo->bdrv, !(buf[4] & 1));
break; break;
case 0x1e: case 0x1e:
DPRINTF("Prevent Allow Medium Removal (prevent = %d)\n", buf[4] & 3); 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; break;
case 0x25: case 0x25:
DPRINTF("Read Capacity\n"); DPRINTF("Read Capacity\n");
/* The normal LEN field for this command is zero. */ /* The normal LEN field for this command is zero. */
memset(outbuf, 0, 8); memset(outbuf, 0, 8);
bdrv_get_geometry(s->bdrv, &nb_sectors); bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors);
nb_sectors /= s->cluster_size; nb_sectors /= s->cluster_size;
/* Returned value is the address of the last sector. */ /* Returned value is the address of the last sector. */
if (nb_sectors) { if (nb_sectors) {
@ -810,7 +809,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
break; break;
case 0x35: case 0x35:
DPRINTF("Synchronise cache (sector %" PRId64 ", count %d)\n", lba, len); DPRINTF("Synchronise cache (sector %" PRId64 ", count %d)\n", lba, len);
bdrv_flush(s->bdrv); bdrv_flush(s->dinfo->bdrv);
break; break;
case 0x43: case 0x43:
{ {
@ -819,7 +818,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
msf = buf[1] & 2; msf = buf[1] & 2;
format = buf[2] & 0xf; format = buf[2] & 0xf;
start_track = buf[6]; 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); DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
nb_sectors /= s->cluster_size; nb_sectors /= s->cluster_size;
switch(format) { switch(format) {
@ -873,7 +872,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
if ((buf[1] & 31) == 0x10) { if ((buf[1] & 31) == 0x10) {
DPRINTF("SAI READ CAPACITY(16)\n"); DPRINTF("SAI READ CAPACITY(16)\n");
memset(outbuf, 0, len); memset(outbuf, 0, len);
bdrv_get_geometry(s->bdrv, &nb_sectors); bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors);
nb_sectors /= s->cluster_size; nb_sectors /= s->cluster_size;
/* Returned value is the address of the last sector. */ /* Returned value is the address of the last sector. */
if (nb_sectors) { if (nb_sectors) {
@ -937,45 +936,56 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
static void scsi_destroy(SCSIDevice *d) static void scsi_destroy(SCSIDevice *d)
{ {
qemu_free(d->state);
qemu_free(d); qemu_free(d);
} }
SCSIDevice *scsi_disk_init(BlockDriverState *bdrv, int tcq, static int scsi_disk_initfn(SCSIDevice *dev)
scsi_completionfn completion, void *opaque)
{ {
SCSIDevice *d; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
SCSIDeviceState *s;
uint64_t nb_sectors; uint64_t nb_sectors;
s = (SCSIDeviceState *)qemu_mallocz(sizeof(SCSIDeviceState)); if (!s->dinfo || !s->dinfo->bdrv) {
s->bdrv = bdrv; qemu_error("scsi-disk: drive property not set\n");
s->tcq = tcq; return -1;
s->completion = completion; }
s->opaque = opaque;
if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) { if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) {
s->cluster_size = 4; s->cluster_size = 4;
} else { } else {
s->cluster_size = 1; s->cluster_size = 1;
} }
bdrv_get_geometry(s->bdrv, &nb_sectors); bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors);
nb_sectors /= s->cluster_size; nb_sectors /= s->cluster_size;
if (nb_sectors) if (nb_sectors)
nb_sectors--; nb_sectors--;
s->max_lba = 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)); sizeof(s->drive_serial_str));
if (strlen(s->drive_serial_str) == 0) if (strlen(s->drive_serial_str) == 0)
pstrcpy(s->drive_serial_str, sizeof(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); qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s);
d = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice)); return 0;
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;
} }
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)

View File

@ -1,20 +1,36 @@
#ifndef SCSI_DISK_H #ifndef SCSI_DISK_H
#define SCSI_DISK_H #define SCSI_DISK_H
#include "qdev.h"
/* scsi-disk.c */ /* scsi-disk.c */
enum scsi_reason { enum scsi_reason {
SCSI_REASON_DONE, /* Command complete. */ SCSI_REASON_DONE, /* Command complete. */
SCSI_REASON_DATA /* Transfer complete, more data required. */ SCSI_REASON_DATA /* Transfer complete, more data required. */
}; };
typedef struct SCSIDeviceState SCSIDeviceState; typedef struct SCSIBus SCSIBus;
typedef struct SCSIDevice SCSIDevice; 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); uint32_t arg);
struct SCSIDevice 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); void (*destroy)(SCSIDevice *s);
int32_t (*send_command)(SCSIDevice *s, uint32_t tag, uint8_t *buf, int32_t (*send_command)(SCSIDevice *s, uint32_t tag, uint8_t *buf,
int lun); int lun);
@ -24,13 +40,28 @@ struct SCSIDevice
uint8_t *(*get_buf)(SCSIDevice *s, uint32_t tag); uint8_t *(*get_buf)(SCSIDevice *s, uint32_t tag);
}; };
SCSIDevice *scsi_disk_init(BlockDriverState *bdrv, int tcq, typedef void (*SCSIAttachFn)(DeviceState *host, BlockDriverState *bdrv,
scsi_completionfn completion, void *opaque); int unit);
SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq, struct SCSIBus {
scsi_completionfn completion, void *opaque); BusState qbus;
int busnr;
/* cdrom.c */ int tcq, ndev;
int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track); scsi_completionfn complete;
int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
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 #endif

View File

@ -15,15 +15,7 @@
#include "block.h" #include "block.h"
#include "scsi-disk.h" #include "scsi-disk.h"
#ifndef __linux__ #ifdef __linux__
SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq,
scsi_completionfn completion, void *opaque)
{
return NULL;
}
#else /* __linux__ */
//#define DEBUG_SCSI //#define DEBUG_SCSI
@ -60,10 +52,13 @@ do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
#define MAX_UINT ((unsigned int)-1) #define MAX_UINT ((unsigned int)-1)
#endif #endif
typedef struct SCSIGenericState SCSIGenericState;
typedef struct SCSIRequest { typedef struct SCSIRequest {
BlockDriverAIOCB *aiocb; BlockDriverAIOCB *aiocb;
struct SCSIRequest *next; struct SCSIRequest *next;
SCSIDeviceState *dev; SCSIBus *bus;
SCSIGenericState *dev;
uint32_t tag; uint32_t tag;
uint8_t cmd[SCSI_CMD_BUF_SIZE]; uint8_t cmd[SCSI_CMD_BUF_SIZE];
int cmdlen; int cmdlen;
@ -73,15 +68,14 @@ typedef struct SCSIRequest {
sg_io_hdr_t io_header; sg_io_hdr_t io_header;
} SCSIRequest; } SCSIRequest;
struct SCSIDeviceState struct SCSIGenericState
{ {
SCSIDevice qdev;
SCSIRequest *requests; SCSIRequest *requests;
BlockDriverState *bdrv; DriveInfo *dinfo;
int type; int type;
int blocksize; int blocksize;
int lun; int lun;
scsi_completionfn completion;
void *opaque;
int driver_status; int driver_status;
uint8_t sensebuf[SCSI_SENSE_BUF_SIZE]; uint8_t sensebuf[SCSI_SENSE_BUF_SIZE];
uint8_t senselen; uint8_t senselen;
@ -90,8 +84,9 @@ struct SCSIDeviceState
/* Global pool of SCSIRequest structures. */ /* Global pool of SCSIRequest structures. */
static SCSIRequest *free_requests = NULL; 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; SCSIRequest *r;
if (free_requests) { if (free_requests) {
@ -102,6 +97,7 @@ static SCSIRequest *scsi_new_request(SCSIDeviceState *s, uint32_t tag)
r->buf = NULL; r->buf = NULL;
r->buflen = 0; r->buflen = 0;
} }
r->bus = scsi_bus_from_device(d);
r->dev = s; r->dev = s;
r->tag = tag; r->tag = tag;
memset(r->cmd, 0, sizeof(r->cmd)); 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) static void scsi_remove_request(SCSIRequest *r)
{ {
SCSIRequest *last; SCSIRequest *last;
SCSIDeviceState *s = r->dev; SCSIGenericState *s = r->dev;
if (s->requests == r) { if (s->requests == r) {
s->requests = r->next; s->requests = r->next;
@ -138,7 +134,7 @@ static void scsi_remove_request(SCSIRequest *r)
free_requests = 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; 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) static void scsi_command_complete(void *opaque, int ret)
{ {
SCSIRequest *r = (SCSIRequest *)opaque; SCSIRequest *r = (SCSIRequest *)opaque;
SCSIDeviceState *s = r->dev; SCSIGenericState *s = r->dev;
uint32_t tag; uint32_t tag;
int status; int status;
@ -178,14 +174,14 @@ static void scsi_command_complete(void *opaque, int ret)
r, r->tag, status); r, r->tag, status);
tag = r->tag; tag = r->tag;
scsi_remove_request(r); 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. */ /* Cancel a pending data transfer. */
static void scsi_cancel_io(SCSIDevice *d, uint32_t tag) static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
{ {
DPRINTF("scsi_cancel_io 0x%x\n", tag); DPRINTF("scsi_cancel_io 0x%x\n", tag);
SCSIDeviceState *s = d->state; SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
SCSIRequest *r; SCSIRequest *r;
DPRINTF("Cancel tag=0x%x\n", tag); DPRINTF("Cancel tag=0x%x\n", tag);
r = scsi_find_request(s, 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) static void scsi_read_complete(void * opaque, int ret)
{ {
SCSIRequest *r = (SCSIRequest *)opaque; SCSIRequest *r = (SCSIRequest *)opaque;
SCSIDeviceState *s = r->dev;
int len; int len;
if (ret) { 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); DPRINTF("Data ready tag=0x%x len=%d\n", r->tag, len);
r->len = -1; 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) if (len == 0)
scsi_command_complete(r, 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. */ /* Read more data from scsi device into buffer. */
static void scsi_read_data(SCSIDevice *d, uint32_t tag) static void scsi_read_data(SCSIDevice *d, uint32_t tag)
{ {
SCSIDeviceState *s = d->state; SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
SCSIRequest *r; SCSIRequest *r;
int ret; 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", 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[0], r->buf[1], r->buf[2], r->buf[3],
r->buf[4], r->buf[5], r->buf[6], r->buf[7]); 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; 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) { if (ret == -1) {
scsi_command_complete(r, -EINVAL); scsi_command_complete(r, -EINVAL);
return; return;
@ -310,7 +305,7 @@ static void scsi_write_complete(void * opaque, int ret)
The transfer may complete asynchronously. */ The transfer may complete asynchronously. */
static int scsi_write_data(SCSIDevice *d, uint32_t tag) static int scsi_write_data(SCSIDevice *d, uint32_t tag)
{ {
SCSIDeviceState *s = d->state; SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
SCSIRequest *r; SCSIRequest *r;
int ret; int ret;
@ -325,11 +320,11 @@ static int scsi_write_data(SCSIDevice *d, uint32_t tag)
if (r->len == 0) { if (r->len == 0) {
r->len = r->buflen; 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; 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) { if (ret == -1) {
scsi_command_complete(r, -EINVAL); scsi_command_complete(r, -EINVAL);
return 1; return 1;
@ -341,7 +336,7 @@ static int scsi_write_data(SCSIDevice *d, uint32_t tag)
/* Return a pointer to the data buffer. */ /* Return a pointer to the data buffer. */
static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag) static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
{ {
SCSIDeviceState *s = d->state; SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
SCSIRequest *r; SCSIRequest *r;
r = scsi_find_request(s, tag); r = scsi_find_request(s, tag);
if (!r) { if (!r) {
@ -514,10 +509,11 @@ static int is_write(int command)
static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
uint8_t *cmd, int lun) uint8_t *cmd, int lun)
{ {
SCSIDeviceState *s = d->state; SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
uint32_t len=0; uint32_t len=0;
int cmdlen=0; int cmdlen=0;
SCSIRequest *r; SCSIRequest *r;
SCSIBus *bus;
int ret; int ret;
if (s->type == TYPE_TAPE) { 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->sensebuf[6] = 0x00;
s->senselen = 7; s->senselen = 7;
s->driver_status = SG_ERR_DRIVER_SENSE; 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; 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); BADF("Tag 0x%x already in use %p\n", tag, r);
scsi_cancel_io(d, tag); scsi_cancel_io(d, tag);
} }
r = scsi_new_request(s, tag); r = scsi_new_request(d, tag);
memcpy(r->cmd, cmd, cmdlen); memcpy(r->cmd, cmd, cmdlen);
r->cmdlen = cmdlen; r->cmdlen = cmdlen;
@ -567,7 +564,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
free(r->buf); free(r->buf);
r->buflen = 0; r->buflen = 0;
r->buf = NULL; 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) { if (ret == -1) {
scsi_command_complete(r, -EINVAL); scsi_command_complete(r, -EINVAL);
return 0; return 0;
@ -655,9 +652,10 @@ static int get_stream_blocksize(BlockDriverState *bdrv)
static void scsi_destroy(SCSIDevice *d) static void scsi_destroy(SCSIDevice *d)
{ {
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
SCSIRequest *r, *n; SCSIRequest *r, *n;
r = d->state->requests; r = s->requests;
while (r) { while (r) {
n = r->next; n = r->next;
qemu_free(r); qemu_free(r);
@ -671,51 +669,50 @@ static void scsi_destroy(SCSIDevice *d)
r = n; r = n;
} }
qemu_free(d->state);
qemu_free(d); qemu_free(d);
} }
SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq, static int scsi_generic_initfn(SCSIDevice *dev)
scsi_completionfn completion, void *opaque)
{ {
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, dev);
int sg_version; int sg_version;
SCSIDevice *d;
SCSIDeviceState *s;
struct sg_scsi_id scsiid; 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)) /* check we are really using a /dev/sg* file */
return NULL; 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 */ /* check we are using a driver managing SG_IO (version 3 and after */
if (bdrv_ioctl(s->dinfo->bdrv, SG_GET_VERSION_NUM, &sg_version) < 0 ||
if (bdrv_ioctl(bdrv, SG_GET_VERSION_NUM, &sg_version) < 0 || sg_version < 30000) {
sg_version < 30000) qemu_error("scsi-generic: scsi generic interface too old\n");
return NULL; return -1;
}
/* get LUN of the /dev/sg? */ /* get LUN of the /dev/sg? */
if (bdrv_ioctl(s->dinfo->bdrv, SG_GET_SCSI_ID, &scsiid)) {
if (bdrv_ioctl(bdrv, SG_GET_SCSI_ID, &scsiid)) qemu_error("scsi-generic: SG_GET_SCSI_ID ioctl failed\n");
return NULL; return -1;
}
/* define device state */ /* 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; s->lun = scsiid.lun;
DPRINTF("LUN %d\n", s->lun); DPRINTF("LUN %d\n", s->lun);
s->type = scsiid.scsi_type; s->type = scsiid.scsi_type;
DPRINTF("device type %d\n", s->type); DPRINTF("device type %d\n", s->type);
if (s->type == TYPE_TAPE) { if (s->type == TYPE_TAPE) {
s->blocksize = get_stream_blocksize(s->bdrv); s->blocksize = get_stream_blocksize(s->dinfo->bdrv);
if (s->blocksize == -1) if (s->blocksize == -1)
s->blocksize = 0; s->blocksize = 0;
} else { } else {
s->blocksize = get_blocksize(s->bdrv); s->blocksize = get_blocksize(s->dinfo->bdrv);
/* removable media returns 0 if not present */ /* removable media returns 0 if not present */
if (s->blocksize <= 0) { if (s->blocksize <= 0) {
if (s->type == TYPE_ROM || s->type == TYPE_WORM) 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); DPRINTF("block size %d\n", s->blocksize);
s->driver_status = 0; s->driver_status = 0;
memset(s->sensebuf, 0, sizeof(s->sensebuf)); 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;
return d;
} }
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__ */ #endif /* __linux__ */

View File

@ -44,6 +44,7 @@ typedef struct {
uint32_t residue; uint32_t residue;
uint32_t tag; uint32_t tag;
BlockDriverState *bs; BlockDriverState *bs;
SCSIBus *bus;
SCSIDevice *scsi_dev; SCSIDevice *scsi_dev;
int result; int result;
/* For async completion. */ /* For async completion. */
@ -150,9 +151,9 @@ static void usb_msd_copy_data(MSDState *s)
s->data_len -= len; s->data_len -= len;
if (s->scsi_len == 0) { if (s->scsi_len == 0) {
if (s->mode == USB_MSDM_DATAIN) { 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) { } 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); 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) uint32_t arg)
{ {
MSDState *s = (MSDState *)opaque; MSDState *s = DO_UPCAST(MSDState, dev.qdev, bus->qbus.parent);
USBPacket *p = s->packet; USBPacket *p = s->packet;
if (tag != s->tag) { if (tag != s->tag) {
@ -205,7 +206,7 @@ static void usb_msd_command_complete(void *opaque, int reason, uint32_t tag,
return; return;
} }
s->scsi_len = arg; 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) { if (p) {
usb_msd_copy_data(s); usb_msd_copy_data(s);
if (s->usb_len == 0) { 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) static void usb_msd_cancel_io(USBPacket *p, void *opaque)
{ {
MSDState *s = 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->packet = NULL;
s->scsi_len = 0; 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", DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
s->tag, cbw.flags, cbw.cmd_len, s->data_len); s->tag, cbw.flags, cbw.cmd_len, s->data_len);
s->residue = 0; 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 /* ??? Should check that USB and SCSI data transfer
directions match. */ directions match. */
if (s->residue == 0) { if (s->residue == 0) {
if (s->mode == USB_MSDM_DATAIN) { 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) { } 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; ret = len;
@ -509,7 +510,7 @@ static void usb_msd_handle_destroy(USBDevice *dev)
{ {
MSDState *s = (MSDState *)dev; MSDState *s = (MSDState *)dev;
s->scsi_dev->destroy(s->scsi_dev); s->scsi_dev->info->destroy(s->scsi_dev);
bdrv_delete(s->bs); bdrv_delete(s->bs);
qemu_free(s); 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)", snprintf(s->dev.devname, sizeof(s->dev.devname), "QEMU USB MSD(%.16s)",
filename); 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); usb_msd_handle_reset((USBDevice *)s);
return (USBDevice *)s; return (USBDevice *)s;
} }