s->dnad += count;
s->dbc -= count;
if (s->current->dma_buf == NULL) {
- s->current->dma_buf = dev->info->get_buf(s->current->req);
+ s->current->dma_buf = scsi_req_get_buf(s->current->req);
}
/* ??? Set SFBR to first data byte. */
if (out) {
s->current->dma_len -= count;
if (s->current->dma_len == 0) {
s->current->dma_buf = NULL;
- if (out) {
- /* Write the data. */
- dev->info->write_data(s->current->req);
- } else {
- /* Request any remaining data. */
- dev->info->read_data(s->current->req);
- }
+ scsi_req_continue(s->current->req);
} else {
s->current->dma_buf += count;
lsi_resume_script(s);
return NULL;
}
-/* Record that data is available for a queued command. Returns zero if
- the device was reselected, nonzero if the IO is deferred. */
-static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg)
+static void lsi_request_cancelled(SCSIRequest *req)
{
- lsi_request *p;
+ LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
+ lsi_request *p = req->hba_private;
- p = lsi_find_by_tag(s, tag);
- if (!p) {
- BADF("IO with unknown tag %d\n", tag);
- return 1;
+ if (s->current && req == s->current->req) {
+ scsi_req_unref(req);
+ qemu_free(s->current);
+ s->current = NULL;
+ return;
+ }
+
+ if (p) {
+ QTAILQ_REMOVE(&s->queue, p, next);
+ scsi_req_unref(req);
+ qemu_free(p);
}
+}
+
+/* Record that data is available for a queued command. Returns zero if
+ the device was reselected, nonzero if the IO is deferred. */
+static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len)
+{
+ lsi_request *p = req->hba_private;
if (p->pending) {
- BADF("Multiple IO pending for tag %d\n", tag);
+ BADF("Multiple IO pending for request %p\n", p);
}
- p->pending = arg;
+ p->pending = len;
/* Reselect if waiting for it, or if reselection triggers an IRQ
and the bus is free.
Since no interrupt stacking is implemented in the emulation, it
return 0;
} else {
DPRINTF("Queueing IO tag=0x%x\n", tag);
- p->pending = arg;
+ p->pending = len;
return 1;
}
}
- /* Callback to indicate that the SCSI layer has completed a transfer. */
-static void lsi_command_complete(SCSIRequest *req, int reason, uint32_t arg)
+
+ /* Callback to indicate that the SCSI layer has completed a command. */
+static void lsi_command_complete(SCSIRequest *req, uint32_t status)
{
LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
int out;
out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
- if (reason == SCSI_REASON_DONE) {
- DPRINTF("Command complete status=%d\n", (int)arg);
- s->status = arg;
- s->command_complete = 2;
- if (s->waiting && s->dbc != 0) {
- /* Raise phase mismatch for short transfers. */
- lsi_bad_phase(s, out, PHASE_ST);
- } else {
- lsi_set_phase(s, PHASE_ST);
- }
+ DPRINTF("Command complete status=%d\n", (int)status);
+ s->status = status;
+ s->command_complete = 2;
+ if (s->waiting && s->dbc != 0) {
+ /* Raise phase mismatch for short transfers. */
+ lsi_bad_phase(s, out, PHASE_ST);
+ } else {
+ lsi_set_phase(s, PHASE_ST);
+ }
- if (s->current && req == s->current->req) {
- scsi_req_unref(s->current->req);
- qemu_free(s->current);
- s->current = NULL;
- }
- lsi_resume_script(s);
- return;
+ if (s->current && req == s->current->req) {
+ scsi_req_unref(s->current->req);
+ qemu_free(s->current);
+ s->current = NULL;
}
+ lsi_resume_script(s);
+}
+
+ /* Callback to indicate that the SCSI layer has completed a transfer. */
+static void lsi_transfer_data(SCSIRequest *req, uint32_t len)
+{
+ LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
+ int out;
- if (s->waiting == 1 || !s->current || req->tag != s->current->tag ||
+ if (s->waiting == 1 || !s->current || req->hba_private != s->current ||
(lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) {
- if (lsi_queue_tag(s, req->tag, arg)) {
+ if (lsi_queue_req(s, req, len)) {
return;
}
}
+ out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
+
/* host adapter (re)connected */
- DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, arg);
- s->current->dma_len = arg;
+ DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, len);
+ s->current->dma_len = len;
s->command_complete = 1;
- if (!s->waiting)
- return;
- if (s->waiting == 1 || s->dbc == 0) {
- lsi_resume_script(s);
- } else {
- lsi_do_dma(s, out);
+ if (s->waiting) {
+ if (s->waiting == 1 || s->dbc == 0) {
+ lsi_resume_script(s);
+ } else {
+ lsi_do_dma(s, out);
+ }
}
}
assert(s->current == NULL);
s->current = qemu_mallocz(sizeof(lsi_request));
s->current->tag = s->select_tag;
- s->current->req = dev->info->alloc_req(dev, s->current->tag,
- s->current_lun);
-
- n = dev->info->send_command(s->current->req, buf);
- if (n > 0) {
- lsi_set_phase(s, PHASE_DI);
- dev->info->read_data(s->current->req);
- } else if (n < 0) {
- lsi_set_phase(s, PHASE_DO);
- dev->info->write_data(s->current->req);
- }
+ s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun,
+ s->current);
+ n = scsi_req_enqueue(s->current->req, buf);
+ if (n) {
+ if (n > 0) {
+ lsi_set_phase(s, PHASE_DI);
+ } else if (n < 0) {
+ lsi_set_phase(s, PHASE_DO);
+ }
+ scsi_req_continue(s->current->req);
+ }
if (!s->command_complete) {
if (n) {
/* Command did not complete immediately so disconnect. */
uint8_t msg;
int len;
uint32_t current_tag;
- SCSIDevice *current_dev;
lsi_request *current_req, *p, *p_next;
int id;
current_req = lsi_find_by_tag(s, current_tag);
}
id = (current_tag >> 8) & 0xf;
- current_dev = s->bus.devs[id];
DPRINTF("MSG out len=%d\n", s->dbc);
while (s->dbc) {
/* The ABORT TAG message clears the current I/O process only. */
DPRINTF("MSG: ABORT TAG tag=0x%x\n", current_tag);
if (current_req) {
- current_dev->info->cancel_io(current_req->req);
+ scsi_req_cancel(current_req->req);
}
lsi_disconnect(s);
break;
/* clear the current I/O process */
if (s->current) {
- current_dev->info->cancel_io(s->current->req);
+ scsi_req_cancel(s->current->req);
}
/* As the current implemented devices scsi_disk and scsi_generic
id = current_tag & 0x0000ff00;
QTAILQ_FOREACH_SAFE(p, &s->queue, next, p_next) {
if ((p->tag & 0x0000ff00) == id) {
- current_dev->info->cancel_io(p->req);
- QTAILQ_REMOVE(&s->queue, p, next);
+ scsi_req_cancel(p->req);
}
}
}
static const struct SCSIBusOps lsi_scsi_ops = {
- .complete = lsi_command_complete
+ .transfer_data = lsi_transfer_data,
+ .complete = lsi_command_complete,
+ .cancel = lsi_request_cancelled
};
static int lsi_scsi_init(PCIDevice *dev)
pci_conf = s->dev.config;
- /* PCI Vendor ID (word) */
- pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_LSI_LOGIC);
- /* PCI device ID (word) */
- pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_LSI_53C895A);
- /* PCI base class code */
- pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_SCSI);
- /* PCI subsystem ID */
- pci_conf[PCI_SUBSYSTEM_ID] = 0x00;
- pci_conf[PCI_SUBSYSTEM_ID + 1] = 0x10;
/* PCI latency timer = 255 */
pci_conf[PCI_LATENCY_TIMER] = 0xff;
/* TODO: RST# value should be 0 */
.qdev.vmsd = &vmstate_lsi_scsi,
.init = lsi_scsi_init,
.exit = lsi_scsi_uninit,
+ .vendor_id = PCI_VENDOR_ID_LSI_LOGIC,
+ .device_id = PCI_DEVICE_ID_LSI_53C895A,
+ .class_id = PCI_CLASS_STORAGE_SCSI,
+ .subsystem_id = 0x1000,
};
static void lsi53c895a_register_devices(void)