#include "sysemu.h"
#include "blockdev.h"
#include "block_int.h"
+#include "dma.h"
#ifdef __linux
#include <scsi/sg.h>
r->req.aiocb = NULL;
}
-static uint32_t scsi_init_iovec(SCSIDiskReq *r)
+static uint32_t scsi_init_iovec(SCSIDiskReq *r, size_t size)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
if (!r->iov.iov_base) {
- r->buflen = SCSI_DMA_BUF_SIZE;
+ r->buflen = size;
r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen);
}
r->iov.iov_len = MIN(r->sector_count * 512, r->buflen);
return r->qiov.size / 512;
}
-static void scsi_read_complete(void * opaque, int ret)
+static void scsi_disk_save_request(QEMUFile *f, SCSIRequest *req)
+{
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
+ qemu_put_be64s(f, &r->sector);
+ qemu_put_be32s(f, &r->sector_count);
+ qemu_put_be32s(f, &r->buflen);
+ if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+ qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len);
+ }
+}
+
+static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req)
+{
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
+ qemu_get_be64s(f, &r->sector);
+ qemu_get_be32s(f, &r->sector_count);
+ qemu_get_be32s(f, &r->buflen);
+ if (r->buflen) {
+ scsi_init_iovec(r, r->buflen);
+ if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+ qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len);
+ }
+ }
+
+ qemu_iovec_init_external(&r->qiov, &r->iov, 1);
+}
+
+static void scsi_flush_complete(void * opaque, int ret)
{
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
- int n;
- if (r->req.aiocb != NULL) {
- r->req.aiocb = NULL;
- bdrv_acct_done(s->qdev.conf.bs, &r->acct);
- }
+ bdrv_acct_done(s->qdev.conf.bs, &r->acct);
- if (ret) {
+ if (ret < 0) {
if (scsi_handle_rw_error(r, -ret)) {
goto done;
}
}
- DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->qiov.size);
+ scsi_req_complete(&r->req, GOOD);
- n = r->qiov.size / 512;
- r->sector += n;
- r->sector_count -= n;
- scsi_req_data(&r->req, r->qiov.size);
+done:
+ if (!r->req.io_canceled) {
+ scsi_req_unref(&r->req);
+ }
+}
+
+static bool scsi_is_cmd_fua(SCSICommand *cmd)
+{
+ switch (cmd->buf[0]) {
+ case READ_10:
+ case READ_12:
+ case READ_16:
+ case WRITE_10:
+ case WRITE_12:
+ case WRITE_16:
+ return (cmd->buf[1] & 8) != 0;
+
+ case WRITE_VERIFY_10:
+ case WRITE_VERIFY_12:
+ case WRITE_VERIFY_16:
+ return true;
+
+ case READ_6:
+ case WRITE_6:
+ default:
+ return false;
+ }
+}
+
+static void scsi_write_do_fua(SCSIDiskReq *r)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+ if (scsi_is_cmd_fua(&r->req.cmd)) {
+ bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
+ r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_flush_complete, r);
+ return;
+ }
+
+ scsi_req_complete(&r->req, GOOD);
+ if (!r->req.io_canceled) {
+ scsi_req_unref(&r->req);
+ }
+}
+
+static void scsi_dma_complete(void *opaque, int ret)
+{
+ SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+ bdrv_acct_done(s->qdev.conf.bs, &r->acct);
+
+ if (ret < 0) {
+ if (scsi_handle_rw_error(r, -ret)) {
+ goto done;
+ }
+ }
+
+ r->sector += r->sector_count;
+ r->sector_count = 0;
+ if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+ scsi_write_do_fua(r);
+ return;
+ } else {
+ scsi_req_complete(&r->req, GOOD);
+ }
done:
if (!r->req.io_canceled) {
}
}
-static void scsi_flush_complete(void * opaque, int ret)
+static void scsi_read_complete(void * opaque, int ret)
{
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ int n;
if (r->req.aiocb != NULL) {
r->req.aiocb = NULL;
}
}
- scsi_req_complete(&r->req, GOOD);
+ DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->qiov.size);
+
+ n = r->qiov.size / 512;
+ r->sector += n;
+ r->sector_count -= n;
+ scsi_req_data(&r->req, r->qiov.size);
done:
if (!r->req.io_canceled) {
return;
}
- n = scsi_init_iovec(r);
- bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
- r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
- scsi_read_complete, r);
- if (r->req.aiocb == NULL) {
- scsi_read_complete(r, -EIO);
+ if (r->req.sg) {
+ dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ);
+ r->req.resid -= r->req.sg->size;
+ r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector,
+ scsi_dma_complete, r);
+ } else {
+ n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
+ bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
+ r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
+ scsi_read_complete, r);
}
}
BlockErrorAction action = bdrv_get_on_error(s->qdev.conf.bs, is_read);
if (action == BLOCK_ERR_IGNORE) {
- bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_IGNORE, is_read);
+ bdrv_emit_qmp_error_event(s->qdev.conf.bs, BDRV_ACTION_IGNORE, is_read);
return 0;
}
if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC)
|| action == BLOCK_ERR_STOP_ANY) {
- bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_STOP, is_read);
+ bdrv_emit_qmp_error_event(s->qdev.conf.bs, BDRV_ACTION_STOP, is_read);
vm_stop(RUN_STATE_IO_ERROR);
bdrv_iostatus_set_err(s->qdev.conf.bs, error);
scsi_req_retry(&r->req);
scsi_check_condition(r, SENSE_CODE(IO_ERROR));
break;
}
- bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_REPORT, is_read);
+ bdrv_emit_qmp_error_event(s->qdev.conf.bs, BDRV_ACTION_REPORT, is_read);
}
return 1;
}
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
}
- if (ret) {
+ if (ret < 0) {
if (scsi_handle_rw_error(r, -ret)) {
goto done;
}
r->sector += n;
r->sector_count -= n;
if (r->sector_count == 0) {
- scsi_req_complete(&r->req, GOOD);
+ scsi_write_do_fua(r);
+ return;
} else {
- scsi_init_iovec(r);
+ scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, r->qiov.size);
scsi_req_data(&r->req, r->qiov.size);
}
return;
}
- n = r->qiov.size / 512;
- if (n) {
- if (s->tray_open) {
- scsi_write_complete(r, -ENOMEDIUM);
- return;
- }
+ if (!r->req.sg && !r->qiov.size) {
+ /* Called for the first time. Ask the driver to send us more data. */
+ scsi_write_complete(r, 0);
+ return;
+ }
+ if (s->tray_open) {
+ scsi_write_complete(r, -ENOMEDIUM);
+ return;
+ }
+
+ if (r->req.sg) {
+ dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_WRITE);
+ r->req.resid -= r->req.sg->size;
+ r->req.aiocb = dma_bdrv_write(s->qdev.conf.bs, r->req.sg, r->sector,
+ scsi_dma_complete, r);
+ } else {
+ n = r->qiov.size / 512;
bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_WRITE);
r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, r->sector, &r->qiov, n,
scsi_write_complete, r);
- if (r->req.aiocb == NULL) {
- scsi_write_complete(r, -ENOMEM);
- }
- } else {
- /* Called for the first time. Ask the driver to send us more data. */
- scsi_write_complete(r, 0);
}
}
}
l = strlen(s->serial);
- if (l > req->cmd.xfer) {
- l = req->cmd.xfer;
- }
if (l > 20) {
l = 20;
}
case 0x83: /* Device identification page, mandatory */
{
- int max_len = 255 - 8;
- int id_len = strlen(bdrv_get_device_name(s->qdev.conf.bs));
+ const char *str = s->serial ?: bdrv_get_device_name(s->qdev.conf.bs);
+ int max_len = s->serial ? 20 : 255 - 8;
+ int id_len = strlen(str);
if (id_len > max_len) {
id_len = max_len;
outbuf[buflen++] = 0; // reserved
outbuf[buflen++] = id_len; // length of data following
- memcpy(outbuf+buflen, bdrv_get_device_name(s->qdev.conf.bs), id_len);
+ memcpy(outbuf+buflen, str, id_len);
buflen += id_len;
break;
}
break;
}
/* if a geometry hint is available, use it */
- bdrv_get_geometry_hint(bdrv, &cylinders, &heads, &secs);
+ bdrv_guess_geometry(bdrv, &cylinders, &heads, &secs);
p[2] = (cylinders >> 16) & 0xff;
p[3] = (cylinders >> 8) & 0xff;
p[4] = cylinders & 0xff;
p[2] = 5000 >> 8;
p[3] = 5000 & 0xff;
/* if a geometry hint is available, use it */
- bdrv_get_geometry_hint(bdrv, &cylinders, &heads, &secs);
+ bdrv_guess_geometry(bdrv, &cylinders, &heads, &secs);
p[4] = heads & 0xff;
p[5] = secs & 0xff;
p[6] = s->qdev.blocksize >> 8;
p += 8;
}
+ /* MMC prescribes that CD/DVD drives have no block descriptors. */
bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
- if (!dbd && nb_sectors) {
+ if (!dbd && s->qdev.type == TYPE_DISK && nb_sectors) {
if (r->req.cmd.buf[0] == MODE_SENSE) {
outbuf[3] = 8; /* Block descriptor length */
} else { /* MODE_SENSE_10 */
outbuf[0] = ((buflen - 2) >> 8) & 0xff;
outbuf[1] = (buflen - 2) & 0xff;
}
- if (buflen > r->req.cmd.xfer) {
- buflen = r->req.cmd.xfer;
- }
return buflen;
}
default:
return -1;
}
- if (toclen > req->cmd.xfer) {
- toclen = req->cmd.xfer;
- }
return toclen;
}
: SENSE_CODE(NOT_READY_REMOVAL_PREVENTED));
return -1;
}
- bdrv_eject(s->qdev.conf.bs, !start);
- s->tray_open = !start;
+
+ if (s->tray_open != !start) {
+ bdrv_eject(s->qdev.conf.bs, !start);
+ s->tray_open = !start;
+ }
}
return 0;
}
outbuf = r->iov.iov_base;
switch (req->cmd.buf[0]) {
case TEST_UNIT_READY:
- if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
- goto not_ready;
- }
+ assert(!s->tray_open && bdrv_is_inserted(s->qdev.conf.bs));
break;
case INQUIRY:
buflen = scsi_disk_emulate_inquiry(req, outbuf);
memset(outbuf, 0, 8);
bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
if (!nb_sectors) {
- goto not_ready;
+ scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY));
+ return -1;
}
if ((req->cmd.buf[8] & 1) == 0 && req->cmd.lba) {
goto illegal_request;
outbuf[7] = 0;
buflen = 8;
break;
+ case REQUEST_SENSE:
+ /* Just return "NO SENSE". */
+ buflen = scsi_build_sense(NULL, 0, outbuf, r->buflen,
+ (req->cmd.buf[1] & 1) == 0);
+ break;
case MECHANISM_STATUS:
buflen = scsi_emulate_mechanism_status(s, outbuf);
if (buflen < 0) {
memset(outbuf, 0, req->cmd.xfer);
bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
if (!nb_sectors) {
- goto not_ready;
+ scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY));
+ return -1;
}
if ((req->cmd.buf[14] & 1) == 0 && req->cmd.lba) {
goto illegal_request;
scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
return -1;
}
+ buflen = MIN(buflen, req->cmd.xfer);
return buflen;
-not_ready:
- if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
- scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
- } else {
- scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY));
- }
- return -1;
-
illegal_request:
if (r->req.status == -1) {
scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
}
#endif
+ switch (command) {
+ case INQUIRY:
+ case MODE_SENSE:
+ case MODE_SENSE_10:
+ case RESERVE:
+ case RESERVE_10:
+ case RELEASE:
+ case RELEASE_10:
+ case START_STOP:
+ case ALLOW_MEDIUM_REMOVAL:
+ case GET_CONFIGURATION:
+ case GET_EVENT_STATUS_NOTIFICATION:
+ case MECHANISM_STATUS:
+ case REQUEST_SENSE:
+ break;
+
+ default:
+ if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
+ scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
+ return 0;
+ }
+ break;
+ }
+
switch (command) {
case TEST_UNIT_READY:
case INQUIRY:
case GET_EVENT_STATUS_NOTIFICATION:
case MECHANISM_STATUS:
case SERVICE_ACTION_IN_16:
+ case REQUEST_SENSE:
case VERIFY_10:
rc = scsi_disk_emulate_command(r);
if (rc < 0) {
scsi_req_ref(&r->req);
bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_flush_complete, r);
- if (r->req.aiocb == NULL) {
- scsi_flush_complete(r, -EIO);
- }
return 0;
case READ_6:
case READ_10:
goto fail;
}
break;
- case SEEK_6:
case SEEK_10:
- DPRINTF("Seek(%d) (sector %" PRId64 ")\n", command == SEEK_6 ? 6 : 10,
- r->req.cmd.lba);
+ DPRINTF("Seek(10) (sector %" PRId64 ")\n", r->req.cmd.lba);
if (r->req.cmd.lba > s->qdev.max_lba) {
goto illegal_lba;
}
}
break;
- case REQUEST_SENSE:
- abort();
default:
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
DriveInfo *dinfo;
if (!s->qdev.conf.bs) {
- error_report("scsi-disk: drive property not set");
+ error_report("drive property not set");
return -1;
}
}
if (bdrv_is_sg(s->qdev.conf.bs)) {
- error_report("scsi-disk: unwanted /dev/sg*");
+ error_report("unwanted /dev/sg*");
return -1;
}
bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize);
bdrv_iostatus_enable(s->qdev.conf.bs);
- add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, ",0");
+ add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, NULL);
return 0;
}
.write_data = scsi_write_data,
.cancel_io = scsi_cancel_io,
.get_buf = scsi_get_buf,
+ .load_request = scsi_disk_load_request,
+ .save_request = scsi_disk_save_request,
};
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
case WRITE_VERIFY_10:
case WRITE_VERIFY_12:
case WRITE_VERIFY_16:
+ /* If we are not using O_DIRECT, we might read stale data from the
+ * host cache if writes were made using other commands than these
+ * ones (such as WRITE SAME or EXTENDED COPY, etc.). So, without
+ * O_DIRECT everything must go through SG_IO.
+ */
+ if (!(s->qdev.conf.bs->open_flags & BDRV_O_NOCACHE)) {
+ break;
+ }
+
+ /* MMC writing cannot be done via pread/pwrite, because it sometimes
+ * involves writing beyond the maximum LBA or to negative LBA (lead-in).
+ * And once you do these writes, reading from the block device is
+ * unreliable, too. It is even possible that reads deliver random data
+ * from the host page cache (this is probably a Linux bug).
+ *
+ * We might use scsi_disk_reqops as long as no writing commands are
+ * seen, but performance usually isn't paramount on optical media. So,
+ * just make scsi-block operate the same as scsi-generic for them.
+ */
+ if (s->qdev.type == TYPE_ROM) {
+ break;
+ }
return scsi_req_alloc(&scsi_disk_reqops, &s->qdev, tag, lun,
hba_private);
}
DEFINE_PROP_STRING("ver", SCSIDiskState, version), \
DEFINE_PROP_STRING("serial", SCSIDiskState, serial)
-static SCSIDeviceInfo scsi_disk_info[] = {
- {
- .qdev.name = "scsi-hd",
- .qdev.fw_name = "disk",
- .qdev.desc = "virtual SCSI disk",
- .qdev.size = sizeof(SCSIDiskState),
- .qdev.reset = scsi_disk_reset,
- .init = scsi_hd_initfn,
- .destroy = scsi_destroy,
- .alloc_req = scsi_new_request,
- .unit_attention_reported = scsi_disk_unit_attention_reported,
- .qdev.props = (Property[]) {
- DEFINE_SCSI_DISK_PROPERTIES(),
- DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
- DEFINE_PROP_END_OF_LIST(),
- }
- },{
- .qdev.name = "scsi-cd",
- .qdev.fw_name = "disk",
- .qdev.desc = "virtual SCSI CD-ROM",
- .qdev.size = sizeof(SCSIDiskState),
- .qdev.reset = scsi_disk_reset,
- .init = scsi_cd_initfn,
- .destroy = scsi_destroy,
- .alloc_req = scsi_new_request,
- .unit_attention_reported = scsi_disk_unit_attention_reported,
- .qdev.props = (Property[]) {
- DEFINE_SCSI_DISK_PROPERTIES(),
- DEFINE_PROP_END_OF_LIST(),
- },
+static Property scsi_hd_properties[] = {
+ DEFINE_SCSI_DISK_PROPERTIES(),
+ DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription vmstate_scsi_disk_state = {
+ .name = "scsi-disk",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_SCSI_DEVICE(qdev, SCSIDiskState),
+ VMSTATE_BOOL(media_changed, SCSIDiskState),
+ VMSTATE_BOOL(media_event, SCSIDiskState),
+ VMSTATE_BOOL(eject_request, SCSIDiskState),
+ VMSTATE_BOOL(tray_open, SCSIDiskState),
+ VMSTATE_BOOL(tray_locked, SCSIDiskState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void scsi_hd_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
+
+ sc->init = scsi_hd_initfn;
+ sc->destroy = scsi_destroy;
+ sc->alloc_req = scsi_new_request;
+ sc->unit_attention_reported = scsi_disk_unit_attention_reported;
+ dc->fw_name = "disk";
+ dc->desc = "virtual SCSI disk";
+ dc->reset = scsi_disk_reset;
+ dc->props = scsi_hd_properties;
+ dc->vmsd = &vmstate_scsi_disk_state;
+}
+
+static TypeInfo scsi_hd_info = {
+ .name = "scsi-hd",
+ .parent = TYPE_SCSI_DEVICE,
+ .instance_size = sizeof(SCSIDiskState),
+ .class_init = scsi_hd_class_initfn,
+};
+
+static Property scsi_cd_properties[] = {
+ DEFINE_SCSI_DISK_PROPERTIES(),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void scsi_cd_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
+
+ sc->init = scsi_cd_initfn;
+ sc->destroy = scsi_destroy;
+ sc->alloc_req = scsi_new_request;
+ sc->unit_attention_reported = scsi_disk_unit_attention_reported;
+ dc->fw_name = "disk";
+ dc->desc = "virtual SCSI CD-ROM";
+ dc->reset = scsi_disk_reset;
+ dc->props = scsi_cd_properties;
+ dc->vmsd = &vmstate_scsi_disk_state;
+}
+
+static TypeInfo scsi_cd_info = {
+ .name = "scsi-cd",
+ .parent = TYPE_SCSI_DEVICE,
+ .instance_size = sizeof(SCSIDiskState),
+ .class_init = scsi_cd_class_initfn,
+};
+
#ifdef __linux__
- },{
- .qdev.name = "scsi-block",
- .qdev.fw_name = "disk",
- .qdev.desc = "SCSI block device passthrough",
- .qdev.size = sizeof(SCSIDiskState),
- .qdev.reset = scsi_disk_reset,
- .init = scsi_block_initfn,
- .destroy = scsi_destroy,
- .alloc_req = scsi_block_new_request,
- .qdev.props = (Property[]) {
- DEFINE_SCSI_DISK_PROPERTIES(),
- DEFINE_PROP_END_OF_LIST(),
- },
+static Property scsi_block_properties[] = {
+ DEFINE_SCSI_DISK_PROPERTIES(),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void scsi_block_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
+
+ sc->init = scsi_block_initfn;
+ sc->destroy = scsi_destroy;
+ sc->alloc_req = scsi_block_new_request;
+ dc->fw_name = "disk";
+ dc->desc = "SCSI block device passthrough";
+ dc->reset = scsi_disk_reset;
+ dc->props = scsi_block_properties;
+ dc->vmsd = &vmstate_scsi_disk_state;
+}
+
+static TypeInfo scsi_block_info = {
+ .name = "scsi-block",
+ .parent = TYPE_SCSI_DEVICE,
+ .instance_size = sizeof(SCSIDiskState),
+ .class_init = scsi_block_class_initfn,
+};
#endif
- },{
- .qdev.name = "scsi-disk", /* legacy -device scsi-disk */
- .qdev.fw_name = "disk",
- .qdev.desc = "virtual SCSI disk or CD-ROM (legacy)",
- .qdev.size = sizeof(SCSIDiskState),
- .qdev.reset = scsi_disk_reset,
- .init = scsi_disk_initfn,
- .destroy = scsi_destroy,
- .alloc_req = scsi_new_request,
- .unit_attention_reported = scsi_disk_unit_attention_reported,
- .qdev.props = (Property[]) {
- DEFINE_SCSI_DISK_PROPERTIES(),
- DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
- DEFINE_PROP_END_OF_LIST(),
- }
- }
+
+static Property scsi_disk_properties[] = {
+ DEFINE_SCSI_DISK_PROPERTIES(),
+ DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
+ DEFINE_PROP_END_OF_LIST(),
};
-static void scsi_disk_register_devices(void)
+static void scsi_disk_class_initfn(ObjectClass *klass, void *data)
{
- int i;
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass);
+
+ sc->init = scsi_disk_initfn;
+ sc->destroy = scsi_destroy;
+ sc->alloc_req = scsi_new_request;
+ sc->unit_attention_reported = scsi_disk_unit_attention_reported;
+ dc->fw_name = "disk";
+ dc->desc = "virtual SCSI disk or CD-ROM (legacy)";
+ dc->reset = scsi_disk_reset;
+ dc->props = scsi_disk_properties;
+ dc->vmsd = &vmstate_scsi_disk_state;
+}
- for (i = 0; i < ARRAY_SIZE(scsi_disk_info); i++) {
- scsi_qdev_register(&scsi_disk_info[i]);
- }
+static TypeInfo scsi_disk_info = {
+ .name = "scsi-disk",
+ .parent = TYPE_SCSI_DEVICE,
+ .instance_size = sizeof(SCSIDiskState),
+ .class_init = scsi_disk_class_initfn,
+};
+
+static void scsi_disk_register_types(void)
+{
+ type_register_static(&scsi_hd_info);
+ type_register_static(&scsi_cd_info);
+#ifdef __linux__
+ type_register_static(&scsi_block_info);
+#endif
+ type_register_static(&scsi_disk_info);
}
-device_init(scsi_disk_register_devices)
+
+type_init(scsi_disk_register_types)