[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [RFC PATCH v2 3/5] ATAPI-SCSI bridge functions created an c
From: |
Alexander Bezzubikov |
Subject: |
[Qemu-devel] [RFC PATCH v2 3/5] ATAPI-SCSI bridge functions created an can be used by bridge |
Date: |
Tue, 18 Aug 2015 18:54:05 +0300 |
ide: bridge functions created
ide: Makefile corrected due to bridge creation
scsi: added function to enable bridge send SCSI requests
ide: bridge can now forward requests to SCSI
ide: bridge functions assigned to SCSIBusInfo
Signed-off-by: Alexander Bezzubikov <address@hidden>
---
hw/ide/Makefile.objs | 2 +-
hw/ide/atapi.c | 16 +++++++
hw/ide/bridge.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++
hw/ide/bridge.h | 1 +
hw/ide/internal.h | 2 +
hw/ide/qdev.c | 4 +-
hw/scsi/scsi-disk.c | 43 +++++++++++++++++++
include/hw/scsi/scsi.h | 3 ++
8 files changed, 182 insertions(+), 3 deletions(-)
create mode 100644 hw/ide/bridge.c
diff --git a/hw/ide/Makefile.objs b/hw/ide/Makefile.objs
index 729e9bd..f54f275 100644
--- a/hw/ide/Makefile.objs
+++ b/hw/ide/Makefile.objs
@@ -1,4 +1,4 @@
-common-obj-$(CONFIG_IDE_CORE) += core.o atapi.o
+common-obj-$(CONFIG_IDE_CORE) += core.o atapi.o bridge.o
common-obj-$(CONFIG_IDE_QDEV) += qdev.o
common-obj-$(CONFIG_IDE_PCI) += pci.o
common-obj-$(CONFIG_IDE_ISA) += isa.o
diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index f6135e1..3eb56e2 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -1253,6 +1253,22 @@ void ide_atapi_cmd(IDEState *s)
return;
}
+ if (s->drive_kind == IDE_BRIDGE) {
+ IDEDevice *dev = s->bus->master;
+ SCSIDevice *scsi_dev = scsi_device_find(&dev->scsi_bus, 0, 0, 0);
+ s->cur_req = scsi_new_request_from_bridge(scsi_dev, 0, 0, buf, NULL);
+
+ /* Necessary to prevent ide from reading while data isn't ready */
+ if (buf[0] == READ_10) {
+ s->status |= BUSY_STAT;
+ }
+
+ if (scsi_req_enqueue(s->cur_req)) {
+ scsi_req_continue(s->cur_req);
+ }
+ return;
+ }
+
/* Execute the command */
if (atapi_cmd_table[s->io_buffer[0]].handler) {
atapi_cmd_table[s->io_buffer[0]].handler(s, buf);
diff --git a/hw/ide/bridge.c b/hw/ide/bridge.c
new file mode 100644
index 0000000..2e93311
--- /dev/null
+++ b/hw/ide/bridge.c
@@ -0,0 +1,114 @@
+#include "hw/ide/bridge.h"
+
+void ide_bridge_do_transfer(IDEState *s)
+{
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, s->cur_req);
+
+ if (r->buflen > 0) {
+ int size = r->buflen;
+
+ int byte_count_limit = s->lcyl | (s->hcyl << 8);
+ if (byte_count_limit == 0xffff) {
+ byte_count_limit--;
+ }
+ if (size > byte_count_limit) {
+ /* byte count limit must be even if this case */
+ if (byte_count_limit & 1) {
+ byte_count_limit--;
+ }
+ size = byte_count_limit;
+ }
+ s->lcyl = size;
+ s->hcyl = size >> 8;
+ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO;
+
+ int offset = (r->buflen == r->qiov.size) ? 0 : r->qiov.size -
r->buflen;
+ r->buflen -= size;
+
+ ide_transfer_start(s, s->io_buffer + offset, size,
+ ide_bridge_do_transfer);
+ ide_set_irq(s->bus);
+ } else {
+ scsi_req_complete(s->cur_req, GOOD);
+ }
+}
+
+static void ide_bridge_dma_complete(void *opaque, int ret)
+{
+ IDEState *s = opaque;
+
+ s->io_buffer_size = s->bus->dma->iov.iov_len;
+ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
+ s->bus->dma->ops->rw_buf(s->bus->dma, 1);
+ scsi_req_complete(s->cur_req, GOOD);
+
+ s->status = READY_STAT | SEEK_STAT;
+
+ ide_set_irq(s->bus);
+ ide_set_inactive(s, false);
+}
+
+void ide_bridge_start_transfer(SCSIRequest *req, uint32_t len)
+{
+ IDEDevice *dev = IDE_DEVICE(req->bus->qbus.parent);
+ IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus);
+ IDEState *s = bus->ifs;
+ SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
+
+ int cmd = req->cmd.buf[0];
+ if (cmd == READ_10) {
+ if (s->feature & 1) {
+ s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT;
+ qemu_iovec_clone(&s->bus->dma->qiov, &r->qiov, NULL);
+ qemu_iovec_to_buf(&r->qiov, 0, s->io_buffer, r->qiov.size);
+ } else {
+ qemu_iovec_to_buf(&r->qiov, 0, s->io_buffer, r->qiov.size);
+ }
+ } else {
+ if (cmd == INQUIRY) {
+ len = 36;
+ }
+ r->iov.iov_len = len;
+ qemu_iovec_concat_iov(&r->qiov, &r->iov, len, 0, len);
+ qemu_iovec_to_buf(&r->qiov, 0, s->io_buffer, r->qiov.size);
+ }
+
+ s->io_buffer_index = 0;
+ s->status = READY_STAT | SEEK_STAT;
+
+ if (cmd != TEST_UNIT_READY && cmd != ALLOW_MEDIUM_REMOVAL) {
+ if (s->feature & 1) {
+ s->io_buffer_index = 0;
+ s->bus->retry_unit = s->unit;
+ s->bus->retry_sector_num = ide_get_sector(s);
+ s->bus->retry_nsector = s->nsector;
+
+ s->bus->dma->iov.iov_base = (void *)(s->io_buffer);
+ s->bus->dma->iov.iov_len = r->qiov.size;
+
+ if (cmd != READ_10) {
+ s->status = READY_STAT | SEEK_STAT | DRQ_STAT;
+ }
+
+ if (s->bus->dma->ops->start_dma) {
+ s->bus->dma->ops->start_dma(s->bus->dma, s,
+ ide_bridge_dma_complete);
+ }
+ } else {
+ r->buflen = r->qiov.size;
+ ide_bridge_do_transfer(s);
+ }
+ } else {
+ scsi_req_complete(req, GOOD);
+ }
+}
+
+void ide_bridge_complete(SCSIRequest *req, uint32_t status, size_t resid)
+{
+ IDEDevice *dev = IDE_DEVICE(req->bus->qbus.parent);
+ IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus);
+ IDEState *s = bus->ifs;
+
+ ide_atapi_cmd_ok(s);
+ ide_set_irq(s->bus);
+}
diff --git a/hw/ide/bridge.h b/hw/ide/bridge.h
index dca5d73..59f2f25 100644
--- a/hw/ide/bridge.h
+++ b/hw/ide/bridge.h
@@ -5,5 +5,6 @@
void ide_bridge_start_transfer(SCSIRequest *req, uint32_t len);
void ide_bridge_complete(SCSIRequest *req, uint32_t status, size_t resid);
+void ide_bridge_do_transfer(IDEState *s);
#endif
diff --git a/hw/ide/internal.h b/hw/ide/internal.h
index 79c85be..fd385bc 100644
--- a/hw/ide/internal.h
+++ b/hw/ide/internal.h
@@ -429,6 +429,8 @@ struct IDEState {
uint8_t *smart_selftest_data;
/* AHCI */
int ncq_queues;
+ /* ATAPI-SCSI bridge */
+ SCSIRequest *cur_req;
};
struct IDEDMAOps {
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
index 2ed0c39..e18d222 100644
--- a/hw/ide/qdev.c
+++ b/hw/ide/qdev.c
@@ -150,8 +150,8 @@ static const struct SCSIBusInfo atapi_scsi_info = {
.max_target = 0,
.max_lun = 0,
- .transfer_data = NULL,
- .complete = NULL,
+ .transfer_data = ide_bridge_start_transfer,
+ .complete = ide_bridge_complete,
.cancel = NULL
};
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 64f0694..f67d816 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -2440,6 +2440,49 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d,
uint32_t tag, uint32_t lun,
return req;
}
+SCSIRequest *scsi_new_request_from_bridge(SCSIDevice *d, uint32_t tag,
+ uint32_t lun, uint8_t *buf,
+ void *hba_private)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
+ SCSIDeviceClass *sc = SCSI_DEVICE_GET_CLASS(d);
+ SCSIRequest *req;
+ const SCSIReqOps *ops;
+ uint8_t command;
+
+ command = buf[0];
+ ops = scsi_disk_reqops_dispatch[command];
+ if (!ops) {
+ ops = &scsi_disk_emulate_reqops;
+ }
+ req = scsi_req_alloc(ops, &s->qdev, tag, lun, hba_private);
+ memcpy(req->cmd.buf, buf, 16);
+
+ SCSICommand cmd = { .len = 0 };
+
+ if (ops != NULL || !sc->parse_cdb) {
+ scsi_req_parse_cdb(d, &cmd, buf);
+ } else {
+ sc->parse_cdb(d, &cmd, buf, hba_private);
+ }
+
+ req->cmd = cmd;
+ req->resid = req->cmd.xfer;
+
+ #ifdef DEBUG_SCSI
+ DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]);
+ {
+ int i;
+ for (i = 1; i < scsi_cdb_length(buf); i++) {
+ printf(" 0x%02x", buf[i]);
+ }
+ printf("\n");
+ }
+ #endif
+
+ return req;
+}
+
#ifdef __linux__
static int get_device_type(SCSIDiskState *s)
{
diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h
index cdaf0f8..881ed23 100644
--- a/include/hw/scsi/scsi.h
+++ b/include/hw/scsi/scsi.h
@@ -249,6 +249,9 @@ SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops,
SCSIDevice *d,
uint32_t tag, uint32_t lun, void *hba_private);
SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
uint8_t *buf, void *hba_private);
+SCSIRequest *scsi_new_request_from_bridge(SCSIDevice *d, uint32_t tag,
+ uint32_t lun, uint8_t *buf,
+ void *hba_private);
int32_t scsi_req_enqueue(SCSIRequest *req);
void scsi_req_free(SCSIRequest *req);
SCSIRequest *scsi_req_ref(SCSIRequest *req);
--
2.1.4