mirror of https://gitee.com/openkylin/qemu.git
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:
parent
5b19d9a247
commit
d52affa7f6
2
Makefile
2
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
|
||||
|
|
54
hw/esp.c
54
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
1
hw/pci.h
1
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);
|
||||
|
|
19
hw/qdev.c
19
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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
158
hw/scsi-disk.c
158
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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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__ */
|
||||
|
|
26
hw/usb-msd.c
26
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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue