Merge remote-tracking branch 'bonzini/scsi.2' into staging
Conflicts: hw/usb-msd.c
This commit is contained in:
commit
ed7ec84007
119
hw/esp.c
119
hw/esp.c
@ -61,10 +61,11 @@ struct ESPState {
|
||||
int32_t ti_size;
|
||||
uint32_t ti_rptr, ti_wptr;
|
||||
uint8_t ti_buf[TI_BUFSZ];
|
||||
uint32_t sense;
|
||||
uint32_t status;
|
||||
uint32_t dma;
|
||||
SCSIBus bus;
|
||||
SCSIDevice *current_dev;
|
||||
SCSIRequest *current_req;
|
||||
uint8_t cmdbuf[TI_BUFSZ];
|
||||
uint32_t cmdlen;
|
||||
uint32_t do_cmd;
|
||||
@ -187,6 +188,17 @@ static void esp_dma_enable(void *opaque, int irq, int level)
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_request_cancelled(SCSIRequest *req)
|
||||
{
|
||||
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent);
|
||||
|
||||
if (req == s->current_req) {
|
||||
scsi_req_unref(s->current_req);
|
||||
s->current_req = NULL;
|
||||
s->current_dev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t get_cmd(ESPState *s, uint8_t *buf)
|
||||
{
|
||||
uint32_t dmalen;
|
||||
@ -209,7 +221,7 @@ 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->info->cancel_io(s->current_dev, 0);
|
||||
scsi_req_cancel(s->current_req);
|
||||
s->async_len = 0;
|
||||
}
|
||||
|
||||
@ -232,7 +244,8 @@ 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->info->send_command(s->current_dev, 0, buf, lun);
|
||||
s->current_req = scsi_req_new(s->current_dev, 0, lun);
|
||||
datalen = scsi_req_enqueue(s->current_req, buf);
|
||||
s->ti_size = datalen;
|
||||
if (datalen != 0) {
|
||||
s->rregs[ESP_RSTAT] = STAT_TC;
|
||||
@ -240,11 +253,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->info->read_data(s->current_dev, 0);
|
||||
} else {
|
||||
s->rregs[ESP_RSTAT] |= STAT_DO;
|
||||
s->current_dev->info->write_data(s->current_dev, 0);
|
||||
}
|
||||
scsi_req_continue(s->current_req);
|
||||
}
|
||||
s->rregs[ESP_RINTR] = INTR_BS | INTR_FC;
|
||||
s->rregs[ESP_RSEQ] = SEQ_CD;
|
||||
@ -306,8 +318,8 @@ static void handle_satn_stop(ESPState *s)
|
||||
|
||||
static void write_response(ESPState *s)
|
||||
{
|
||||
DPRINTF("Transfer status (sense=%d)\n", s->sense);
|
||||
s->ti_buf[0] = s->sense;
|
||||
DPRINTF("Transfer status (status=%d)\n", s->status);
|
||||
s->ti_buf[0] = s->status;
|
||||
s->ti_buf[1] = 0;
|
||||
if (s->dma) {
|
||||
s->dma_memory_write(s->dma_opaque, s->ti_buf, 2);
|
||||
@ -370,53 +382,56 @@ static void esp_do_dma(ESPState *s)
|
||||
else
|
||||
s->ti_size -= len;
|
||||
if (s->async_len == 0) {
|
||||
if (to_device) {
|
||||
// ti_size is negative
|
||||
s->current_dev->info->write_data(s->current_dev, 0);
|
||||
} else {
|
||||
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. */
|
||||
if (s->dma_left == 0 && s->ti_size > 0) {
|
||||
esp_dma_done(s);
|
||||
}
|
||||
scsi_req_continue(s->current_req);
|
||||
/* 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. */
|
||||
if (to_device || s->dma_left != 0 || s->ti_size == 0) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* Partially filled a scsi buffer. Complete immediately. */
|
||||
esp_dma_done(s);
|
||||
}
|
||||
|
||||
/* Partially filled a scsi buffer. Complete immediately. */
|
||||
esp_dma_done(s);
|
||||
}
|
||||
|
||||
static void esp_command_complete(SCSIRequest *req, uint32_t status)
|
||||
{
|
||||
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent);
|
||||
|
||||
DPRINTF("SCSI Command complete\n");
|
||||
if (s->ti_size != 0) {
|
||||
DPRINTF("SCSI command completed unexpectedly\n");
|
||||
}
|
||||
s->ti_size = 0;
|
||||
s->dma_left = 0;
|
||||
s->async_len = 0;
|
||||
if (status) {
|
||||
DPRINTF("Command failed\n");
|
||||
}
|
||||
s->status = status;
|
||||
s->rregs[ESP_RSTAT] = STAT_ST;
|
||||
esp_dma_done(s);
|
||||
if (s->current_req) {
|
||||
scsi_req_unref(s->current_req);
|
||||
s->current_req = NULL;
|
||||
s->current_dev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_command_complete(SCSIBus *bus, int reason, uint32_t tag,
|
||||
uint32_t arg)
|
||||
static void esp_transfer_data(SCSIRequest *req, uint32_t len)
|
||||
{
|
||||
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, bus->qbus.parent);
|
||||
ESPState *s = DO_UPCAST(ESPState, busdev.qdev, req->bus->qbus.parent);
|
||||
|
||||
if (reason == SCSI_REASON_DONE) {
|
||||
DPRINTF("SCSI Command complete\n");
|
||||
if (s->ti_size != 0)
|
||||
DPRINTF("SCSI command completed unexpectedly\n");
|
||||
s->ti_size = 0;
|
||||
s->dma_left = 0;
|
||||
s->async_len = 0;
|
||||
if (arg)
|
||||
DPRINTF("Command failed\n");
|
||||
s->sense = arg;
|
||||
s->rregs[ESP_RSTAT] = STAT_ST;
|
||||
DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size);
|
||||
s->async_len = len;
|
||||
s->async_buf = scsi_req_get_buf(req);
|
||||
if (s->dma_left) {
|
||||
esp_do_dma(s);
|
||||
} else if (s->dma_counter != 0 && s->ti_size <= 0) {
|
||||
/* If this was the last part of a DMA transfer then the
|
||||
completion interrupt is deferred to here. */
|
||||
esp_dma_done(s);
|
||||
s->current_dev = NULL;
|
||||
} else {
|
||||
DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size);
|
||||
s->async_len = arg;
|
||||
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) {
|
||||
/* If this was the last part of a DMA transfer then the
|
||||
completion interrupt is deferred to here. */
|
||||
esp_dma_done(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -678,7 +693,7 @@ static const VMStateDescription vmstate_esp = {
|
||||
VMSTATE_UINT32(ti_rptr, ESPState),
|
||||
VMSTATE_UINT32(ti_wptr, ESPState),
|
||||
VMSTATE_BUFFER(ti_buf, ESPState),
|
||||
VMSTATE_UINT32(sense, ESPState),
|
||||
VMSTATE_UINT32(status, ESPState),
|
||||
VMSTATE_UINT32(dma, ESPState),
|
||||
VMSTATE_BUFFER(cmdbuf, ESPState),
|
||||
VMSTATE_UINT32(cmdlen, ESPState),
|
||||
@ -714,6 +729,12 @@ void esp_init(target_phys_addr_t espaddr, int it_shift,
|
||||
*dma_enable = qdev_get_gpio_in(dev, 1);
|
||||
}
|
||||
|
||||
static const struct SCSIBusOps esp_scsi_ops = {
|
||||
.transfer_data = esp_transfer_data,
|
||||
.complete = esp_command_complete,
|
||||
.cancel = esp_request_cancelled
|
||||
};
|
||||
|
||||
static int esp_init1(SysBusDevice *dev)
|
||||
{
|
||||
ESPState *s = FROM_SYSBUS(ESPState, dev);
|
||||
@ -728,7 +749,7 @@ static int esp_init1(SysBusDevice *dev)
|
||||
|
||||
qdev_init_gpio_in(&dev->qdev, esp_gpio_demux, 2);
|
||||
|
||||
scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, esp_command_complete);
|
||||
scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, &esp_scsi_ops);
|
||||
return scsi_bus_legacy_handle_cmdline(&s->bus);
|
||||
}
|
||||
|
||||
|
200
hw/lsi53c895a.c
200
hw/lsi53c895a.c
@ -174,6 +174,7 @@ do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0)
|
||||
#define LSI_TAG_VALID (1 << 16)
|
||||
|
||||
typedef struct lsi_request {
|
||||
SCSIRequest *req;
|
||||
uint32_t tag;
|
||||
uint32_t dma_len;
|
||||
uint8_t *dma_buf;
|
||||
@ -567,11 +568,9 @@ static void lsi_do_dma(LSIState *s, int out)
|
||||
s->csbc += count;
|
||||
s->dnad += count;
|
||||
s->dbc -= count;
|
||||
|
||||
if (s->current->dma_buf == NULL) {
|
||||
s->current->dma_buf = dev->info->get_buf(dev, s->current->tag);
|
||||
if (s->current->dma_buf == NULL) {
|
||||
s->current->dma_buf = scsi_req_get_buf(s->current->req);
|
||||
}
|
||||
|
||||
/* ??? Set SFBR to first data byte. */
|
||||
if (out) {
|
||||
cpu_physical_memory_read(addr, s->current->dma_buf, count);
|
||||
@ -581,13 +580,7 @@ static void lsi_do_dma(LSIState *s, int 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(dev, s->current->tag);
|
||||
} else {
|
||||
/* Request any remaining data. */
|
||||
dev->info->read_data(dev, s->current->tag);
|
||||
}
|
||||
scsi_req_continue(s->current->req);
|
||||
} else {
|
||||
s->current->dma_buf += count;
|
||||
lsi_resume_script(s);
|
||||
@ -652,82 +645,123 @@ static void lsi_reselect(LSIState *s, lsi_request *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_tag(LSIState *s, uint32_t tag, uint32_t arg)
|
||||
static lsi_request *lsi_find_by_tag(LSIState *s, uint32_t tag)
|
||||
{
|
||||
lsi_request *p;
|
||||
|
||||
QTAILQ_FOREACH(p, &s->queue, next) {
|
||||
if (p->tag == tag) {
|
||||
if (p->pending) {
|
||||
BADF("Multiple IO pending for tag %d\n", tag);
|
||||
}
|
||||
p->pending = arg;
|
||||
/* 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
|
||||
is also required that there are no pending interrupts waiting
|
||||
for service from the device driver. */
|
||||
if (s->waiting == 1 ||
|
||||
(lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON) &&
|
||||
!(s->istat0 & (LSI_ISTAT0_SIP | LSI_ISTAT0_DIP)))) {
|
||||
/* Reselect device. */
|
||||
lsi_reselect(s, p);
|
||||
return 0;
|
||||
} else {
|
||||
DPRINTF("Queueing IO tag=0x%x\n", tag);
|
||||
p->pending = arg;
|
||||
return 1;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
BADF("IO with unknown tag %d\n", tag);
|
||||
return 1;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Callback to indicate that the SCSI layer has completed a transfer. */
|
||||
static void lsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
|
||||
uint32_t arg)
|
||||
static void lsi_request_cancelled(SCSIRequest *req)
|
||||
{
|
||||
LSIState *s = DO_UPCAST(LSIState, dev.qdev, bus->qbus.parent);
|
||||
LSIState *s = DO_UPCAST(LSIState, dev.qdev, req->bus->qbus.parent);
|
||||
lsi_request *p;
|
||||
|
||||
if (s->current && req == s->current->req) {
|
||||
scsi_req_unref(req);
|
||||
qemu_free(s->current);
|
||||
s->current = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
p = lsi_find_by_tag(s, req->tag);
|
||||
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_tag(LSIState *s, uint32_t tag, uint32_t len)
|
||||
{
|
||||
lsi_request *p;
|
||||
|
||||
p = lsi_find_by_tag(s, tag);
|
||||
if (!p) {
|
||||
BADF("IO with unknown tag %d\n", tag);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (p->pending) {
|
||||
BADF("Multiple IO pending for tag %d\n", tag);
|
||||
}
|
||||
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
|
||||
is also required that there are no pending interrupts waiting
|
||||
for service from the device driver. */
|
||||
if (s->waiting == 1 ||
|
||||
(lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON) &&
|
||||
!(s->istat0 & (LSI_ISTAT0_SIP | LSI_ISTAT0_DIP)))) {
|
||||
/* Reselect device. */
|
||||
lsi_reselect(s, p);
|
||||
return 0;
|
||||
} else {
|
||||
DPRINTF("Queueing IO tag=0x%x\n", tag);
|
||||
p->pending = len;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
lsi_resume_script(s);
|
||||
}
|
||||
|
||||
if (s->waiting == 1 || !s->current || tag != s->current->tag ||
|
||||
/* 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 ||
|
||||
(lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) {
|
||||
if (lsi_queue_tag(s, tag, arg))
|
||||
if (lsi_queue_tag(s, req->tag, len)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
|
||||
|
||||
/* host adapter (re)connected */
|
||||
DPRINTF("Data ready tag=0x%x len=%d\n", 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -755,16 +789,17 @@ static void lsi_do_command(LSIState *s)
|
||||
assert(s->current == NULL);
|
||||
s->current = qemu_mallocz(sizeof(lsi_request));
|
||||
s->current->tag = s->select_tag;
|
||||
s->current->req = scsi_req_new(dev, s->current->tag, s->current_lun);
|
||||
|
||||
n = dev->info->send_command(dev, s->current->tag, buf, s->current_lun);
|
||||
if (n > 0) {
|
||||
lsi_set_phase(s, PHASE_DI);
|
||||
dev->info->read_data(dev, s->current->tag);
|
||||
} else if (n < 0) {
|
||||
lsi_set_phase(s, PHASE_DO);
|
||||
dev->info->write_data(dev, s->current->tag);
|
||||
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. */
|
||||
@ -855,13 +890,15 @@ static void lsi_do_msgout(LSIState *s)
|
||||
int len;
|
||||
uint32_t current_tag;
|
||||
SCSIDevice *current_dev;
|
||||
lsi_request *p, *p_next;
|
||||
lsi_request *current_req, *p, *p_next;
|
||||
int id;
|
||||
|
||||
if (s->current) {
|
||||
current_tag = s->current->tag;
|
||||
current_req = s->current;
|
||||
} else {
|
||||
current_tag = s->select_tag;
|
||||
current_req = lsi_find_by_tag(s, current_tag);
|
||||
}
|
||||
id = (current_tag >> 8) & 0xf;
|
||||
current_dev = s->bus.devs[id];
|
||||
@ -913,7 +950,9 @@ static void lsi_do_msgout(LSIState *s)
|
||||
case 0x0d:
|
||||
/* The ABORT TAG message clears the current I/O process only. */
|
||||
DPRINTF("MSG: ABORT TAG tag=0x%x\n", current_tag);
|
||||
current_dev->info->cancel_io(current_dev, current_tag);
|
||||
if (current_req) {
|
||||
scsi_req_cancel(current_req->req);
|
||||
}
|
||||
lsi_disconnect(s);
|
||||
break;
|
||||
case 0x06:
|
||||
@ -936,7 +975,9 @@ static void lsi_do_msgout(LSIState *s)
|
||||
}
|
||||
|
||||
/* clear the current I/O process */
|
||||
current_dev->info->cancel_io(current_dev, current_tag);
|
||||
if (s->current) {
|
||||
scsi_req_cancel(s->current->req);
|
||||
}
|
||||
|
||||
/* As the current implemented devices scsi_disk and scsi_generic
|
||||
only support one LUN, we don't need to keep track of LUNs.
|
||||
@ -948,8 +989,7 @@ static void lsi_do_msgout(LSIState *s)
|
||||
id = current_tag & 0x0000ff00;
|
||||
QTAILQ_FOREACH_SAFE(p, &s->queue, next, p_next) {
|
||||
if ((p->tag & 0x0000ff00) == id) {
|
||||
current_dev->info->cancel_io(current_dev, p->tag);
|
||||
QTAILQ_REMOVE(&s->queue, p, next);
|
||||
scsi_req_cancel(p->req);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2205,6 +2245,12 @@ static int lsi_scsi_uninit(PCIDevice *d)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct SCSIBusOps lsi_scsi_ops = {
|
||||
.transfer_data = lsi_transfer_data,
|
||||
.complete = lsi_command_complete,
|
||||
.cancel = lsi_request_cancelled
|
||||
};
|
||||
|
||||
static int lsi_scsi_init(PCIDevice *dev)
|
||||
{
|
||||
LSIState *s = DO_UPCAST(LSIState, dev, dev);
|
||||
@ -2241,7 +2287,7 @@ static int lsi_scsi_init(PCIDevice *dev)
|
||||
PCI_BASE_ADDRESS_SPACE_MEMORY, lsi_ram_mapfunc);
|
||||
QTAILQ_INIT(&s->queue);
|
||||
|
||||
scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, lsi_command_complete);
|
||||
scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, &lsi_scsi_ops);
|
||||
if (!dev->qdev.hotplugged) {
|
||||
return scsi_bus_legacy_handle_cmdline(&s->bus);
|
||||
}
|
||||
|
220
hw/scsi-bus.c
220
hw/scsi-bus.c
@ -4,6 +4,7 @@
|
||||
#include "scsi-defs.h"
|
||||
#include "qdev.h"
|
||||
#include "blockdev.h"
|
||||
#include "trace.h"
|
||||
|
||||
static char *scsibus_get_fw_dev_path(DeviceState *dev);
|
||||
|
||||
@ -20,13 +21,13 @@ static int next_scsi_bus;
|
||||
|
||||
/* Create a scsi bus, and attach devices to it. */
|
||||
void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev,
|
||||
scsi_completionfn complete)
|
||||
const SCSIBusOps *ops)
|
||||
{
|
||||
qbus_create_inplace(&bus->qbus, &scsi_bus_info, host, NULL);
|
||||
bus->busnr = next_scsi_bus++;
|
||||
bus->tcq = tcq;
|
||||
bus->ndev = ndev;
|
||||
bus->complete = complete;
|
||||
bus->ops = ops;
|
||||
bus->qbus.allow_hotplug = 1;
|
||||
}
|
||||
|
||||
@ -135,42 +136,60 @@ SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t l
|
||||
SCSIRequest *req;
|
||||
|
||||
req = qemu_mallocz(size);
|
||||
req->refcount = 1;
|
||||
req->bus = scsi_bus_from_device(d);
|
||||
req->dev = d;
|
||||
req->tag = tag;
|
||||
req->lun = lun;
|
||||
req->status = -1;
|
||||
req->enqueued = true;
|
||||
QTAILQ_INSERT_TAIL(&d->requests, req, next);
|
||||
trace_scsi_req_alloc(req->dev->id, req->lun, req->tag);
|
||||
return req;
|
||||
}
|
||||
|
||||
SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag)
|
||||
SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun)
|
||||
{
|
||||
SCSIRequest *req;
|
||||
return d->info->alloc_req(d, tag, lun);
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(req, &d->requests, next) {
|
||||
if (req->tag == tag) {
|
||||
return req;
|
||||
}
|
||||
uint8_t *scsi_req_get_buf(SCSIRequest *req)
|
||||
{
|
||||
return req->dev->info->get_buf(req);
|
||||
}
|
||||
|
||||
int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len)
|
||||
{
|
||||
if (req->dev->info->get_sense) {
|
||||
return req->dev->info->get_sense(req, buf, len);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf)
|
||||
{
|
||||
int32_t rc;
|
||||
|
||||
assert(!req->enqueued);
|
||||
scsi_req_ref(req);
|
||||
req->enqueued = true;
|
||||
QTAILQ_INSERT_TAIL(&req->dev->requests, req, next);
|
||||
|
||||
scsi_req_ref(req);
|
||||
rc = req->dev->info->send_command(req, buf);
|
||||
scsi_req_unref(req);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void scsi_req_dequeue(SCSIRequest *req)
|
||||
{
|
||||
trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag);
|
||||
if (req->enqueued) {
|
||||
QTAILQ_REMOVE(&req->dev->requests, req, next);
|
||||
req->enqueued = false;
|
||||
scsi_req_unref(req);
|
||||
}
|
||||
}
|
||||
|
||||
void scsi_req_free(SCSIRequest *req)
|
||||
{
|
||||
scsi_req_dequeue(req);
|
||||
qemu_free(req);
|
||||
}
|
||||
|
||||
static int scsi_req_length(SCSIRequest *req, uint8_t *cmd)
|
||||
{
|
||||
switch (cmd[0] >> 5) {
|
||||
@ -195,6 +214,7 @@ static int scsi_req_length(SCSIRequest *req, uint8_t *cmd)
|
||||
req->cmd.len = 12;
|
||||
break;
|
||||
default:
|
||||
trace_scsi_req_parse_bad(req->dev->id, req->lun, req->tag, cmd[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -392,9 +412,100 @@ int scsi_req_parse(SCSIRequest *req, uint8_t *buf)
|
||||
memcpy(req->cmd.buf, buf, req->cmd.len);
|
||||
scsi_req_xfer_mode(req);
|
||||
req->cmd.lba = scsi_req_lba(req);
|
||||
trace_scsi_req_parsed(req->dev->id, req->lun, req->tag, buf[0],
|
||||
req->cmd.mode, req->cmd.xfer, req->cmd.lba);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Predefined sense codes
|
||||
*/
|
||||
|
||||
/* No sense data available */
|
||||
const struct SCSISense sense_code_NO_SENSE = {
|
||||
.key = NO_SENSE , .asc = 0x00 , .ascq = 0x00
|
||||
};
|
||||
|
||||
/* LUN not ready, Manual intervention required */
|
||||
const struct SCSISense sense_code_LUN_NOT_READY = {
|
||||
.key = NOT_READY, .asc = 0x04, .ascq = 0x03
|
||||
};
|
||||
|
||||
/* LUN not ready, Medium not present */
|
||||
const struct SCSISense sense_code_NO_MEDIUM = {
|
||||
.key = NOT_READY, .asc = 0x3a, .ascq = 0x00
|
||||
};
|
||||
|
||||
/* Hardware error, internal target failure */
|
||||
const struct SCSISense sense_code_TARGET_FAILURE = {
|
||||
.key = HARDWARE_ERROR, .asc = 0x44, .ascq = 0x00
|
||||
};
|
||||
|
||||
/* Illegal request, invalid command operation code */
|
||||
const struct SCSISense sense_code_INVALID_OPCODE = {
|
||||
.key = ILLEGAL_REQUEST, .asc = 0x20, .ascq = 0x00
|
||||
};
|
||||
|
||||
/* Illegal request, LBA out of range */
|
||||
const struct SCSISense sense_code_LBA_OUT_OF_RANGE = {
|
||||
.key = ILLEGAL_REQUEST, .asc = 0x21, .ascq = 0x00
|
||||
};
|
||||
|
||||
/* Illegal request, Invalid field in CDB */
|
||||
const struct SCSISense sense_code_INVALID_FIELD = {
|
||||
.key = ILLEGAL_REQUEST, .asc = 0x24, .ascq = 0x00
|
||||
};
|
||||
|
||||
/* Illegal request, LUN not supported */
|
||||
const struct SCSISense sense_code_LUN_NOT_SUPPORTED = {
|
||||
.key = ILLEGAL_REQUEST, .asc = 0x25, .ascq = 0x00
|
||||
};
|
||||
|
||||
/* Command aborted, I/O process terminated */
|
||||
const struct SCSISense sense_code_IO_ERROR = {
|
||||
.key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06
|
||||
};
|
||||
|
||||
/* Command aborted, I_T Nexus loss occurred */
|
||||
const struct SCSISense sense_code_I_T_NEXUS_LOSS = {
|
||||
.key = ABORTED_COMMAND, .asc = 0x29, .ascq = 0x07
|
||||
};
|
||||
|
||||
/* Command aborted, Logical Unit failure */
|
||||
const struct SCSISense sense_code_LUN_FAILURE = {
|
||||
.key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01
|
||||
};
|
||||
|
||||
/*
|
||||
* scsi_build_sense
|
||||
*
|
||||
* Build a sense buffer
|
||||
*/
|
||||
int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed)
|
||||
{
|
||||
if (!fixed && len < 8) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(buf, 0, len);
|
||||
if (fixed) {
|
||||
/* Return fixed format sense buffer */
|
||||
buf[0] = 0xf0;
|
||||
buf[2] = sense.key;
|
||||
buf[7] = 7;
|
||||
buf[12] = sense.asc;
|
||||
buf[13] = sense.ascq;
|
||||
return MIN(len, 18);
|
||||
} else {
|
||||
/* Return descriptor format sense buffer */
|
||||
buf[0] = 0x72;
|
||||
buf[1] = sense.key;
|
||||
buf[2] = sense.asc;
|
||||
buf[3] = sense.ascq;
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *scsi_command_name(uint8_t cmd)
|
||||
{
|
||||
static const char *names[] = {
|
||||
@ -489,6 +600,43 @@ static const char *scsi_command_name(uint8_t cmd)
|
||||
return names[cmd];
|
||||
}
|
||||
|
||||
SCSIRequest *scsi_req_ref(SCSIRequest *req)
|
||||
{
|
||||
req->refcount++;
|
||||
return req;
|
||||
}
|
||||
|
||||
void scsi_req_unref(SCSIRequest *req)
|
||||
{
|
||||
if (--req->refcount == 0) {
|
||||
if (req->dev->info->free_req) {
|
||||
req->dev->info->free_req(req);
|
||||
}
|
||||
qemu_free(req);
|
||||
}
|
||||
}
|
||||
|
||||
/* Tell the device that we finished processing this chunk of I/O. It
|
||||
will start the next chunk or complete the command. */
|
||||
void scsi_req_continue(SCSIRequest *req)
|
||||
{
|
||||
trace_scsi_req_continue(req->dev->id, req->lun, req->tag);
|
||||
if (req->cmd.mode == SCSI_XFER_TO_DEV) {
|
||||
req->dev->info->write_data(req);
|
||||
} else {
|
||||
req->dev->info->read_data(req);
|
||||
}
|
||||
}
|
||||
|
||||
/* Called by the devices when data is ready for the HBA. The HBA should
|
||||
start a DMA operation to read or fill the device's data buffer.
|
||||
Once it completes, calling scsi_req_continue will restart I/O. */
|
||||
void scsi_req_data(SCSIRequest *req, int len)
|
||||
{
|
||||
trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
|
||||
req->bus->ops->transfer_data(req, len);
|
||||
}
|
||||
|
||||
void scsi_req_print(SCSIRequest *req)
|
||||
{
|
||||
FILE *fp = stderr;
|
||||
@ -520,10 +668,42 @@ void scsi_req_print(SCSIRequest *req)
|
||||
void scsi_req_complete(SCSIRequest *req)
|
||||
{
|
||||
assert(req->status != -1);
|
||||
scsi_req_ref(req);
|
||||
scsi_req_dequeue(req);
|
||||
req->bus->complete(req->bus, SCSI_REASON_DONE,
|
||||
req->tag,
|
||||
req->status);
|
||||
req->bus->ops->complete(req, req->status);
|
||||
scsi_req_unref(req);
|
||||
}
|
||||
|
||||
void scsi_req_cancel(SCSIRequest *req)
|
||||
{
|
||||
if (req->dev && req->dev->info->cancel_io) {
|
||||
req->dev->info->cancel_io(req);
|
||||
}
|
||||
scsi_req_ref(req);
|
||||
scsi_req_dequeue(req);
|
||||
if (req->bus->ops->cancel) {
|
||||
req->bus->ops->cancel(req);
|
||||
}
|
||||
scsi_req_unref(req);
|
||||
}
|
||||
|
||||
void scsi_req_abort(SCSIRequest *req, int status)
|
||||
{
|
||||
req->status = status;
|
||||
if (req->dev && req->dev->info->cancel_io) {
|
||||
req->dev->info->cancel_io(req);
|
||||
}
|
||||
scsi_req_complete(req);
|
||||
}
|
||||
|
||||
void scsi_device_purge_requests(SCSIDevice *sdev)
|
||||
{
|
||||
SCSIRequest *req;
|
||||
|
||||
while (!QTAILQ_EMPTY(&sdev->requests)) {
|
||||
req = QTAILQ_FIRST(&sdev->requests);
|
||||
scsi_req_cancel(req);
|
||||
}
|
||||
}
|
||||
|
||||
static char *scsibus_get_fw_dev_path(DeviceState *dev)
|
||||
|
271
hw/scsi-disk.c
271
hw/scsi-disk.c
@ -49,14 +49,8 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
|
||||
|
||||
typedef struct SCSIDiskState SCSIDiskState;
|
||||
|
||||
typedef struct SCSISense {
|
||||
uint8_t key;
|
||||
} SCSISense;
|
||||
|
||||
typedef struct SCSIDiskReq {
|
||||
SCSIRequest req;
|
||||
/* ??? 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. */
|
||||
/* Both sector and sector_count are in terms of qemu 512 byte blocks. */
|
||||
uint64_t sector;
|
||||
uint32_t sector_count;
|
||||
@ -86,27 +80,24 @@ struct SCSIDiskState
|
||||
static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type);
|
||||
static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf);
|
||||
|
||||
static SCSIDiskReq *scsi_new_request(SCSIDiskState *s, uint32_t tag,
|
||||
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag,
|
||||
uint32_t lun)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
|
||||
SCSIRequest *req;
|
||||
SCSIDiskReq *r;
|
||||
|
||||
req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun);
|
||||
r = DO_UPCAST(SCSIDiskReq, req, req);
|
||||
r->iov.iov_base = qemu_blockalign(s->bs, SCSI_DMA_BUF_SIZE);
|
||||
return r;
|
||||
return req;
|
||||
}
|
||||
|
||||
static void scsi_remove_request(SCSIDiskReq *r)
|
||||
static void scsi_free_request(SCSIRequest *req)
|
||||
{
|
||||
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
|
||||
|
||||
qemu_vfree(r->iov.iov_base);
|
||||
scsi_req_free(&r->req);
|
||||
}
|
||||
|
||||
static SCSIDiskReq *scsi_find_request(SCSIDiskState *s, uint32_t tag)
|
||||
{
|
||||
return DO_UPCAST(SCSIDiskReq, req, scsi_req_find(&s->qdev, tag));
|
||||
}
|
||||
|
||||
static void scsi_disk_clear_sense(SCSIDiskState *s)
|
||||
@ -114,42 +105,33 @@ static void scsi_disk_clear_sense(SCSIDiskState *s)
|
||||
memset(&s->sense, 0, sizeof(s->sense));
|
||||
}
|
||||
|
||||
static void scsi_disk_set_sense(SCSIDiskState *s, uint8_t key)
|
||||
{
|
||||
s->sense.key = key;
|
||||
}
|
||||
|
||||
static void scsi_req_set_status(SCSIDiskReq *r, int status, int sense_code)
|
||||
static void scsi_req_set_status(SCSIDiskReq *r, int status, SCSISense sense)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
|
||||
r->req.status = status;
|
||||
scsi_disk_set_sense(s, sense_code);
|
||||
s->sense = sense;
|
||||
}
|
||||
|
||||
/* Helper function for command completion. */
|
||||
static void scsi_command_complete(SCSIDiskReq *r, int status, int sense)
|
||||
static void scsi_command_complete(SCSIDiskReq *r, int status, SCSISense sense)
|
||||
{
|
||||
DPRINTF("Command complete tag=0x%x status=%d sense=%d\n",
|
||||
r->req.tag, status, sense);
|
||||
DPRINTF("Command complete tag=0x%x status=%d sense=%d/%d/%d\n",
|
||||
r->req.tag, status, sense.key, sense.asc, sense.ascq);
|
||||
scsi_req_set_status(r, status, sense);
|
||||
scsi_req_complete(&r->req);
|
||||
scsi_remove_request(r);
|
||||
}
|
||||
|
||||
/* Cancel a pending data transfer. */
|
||||
static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
|
||||
static void scsi_cancel_io(SCSIRequest *req)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
|
||||
SCSIDiskReq *r;
|
||||
DPRINTF("Cancel tag=0x%x\n", tag);
|
||||
r = scsi_find_request(s, tag);
|
||||
if (r) {
|
||||
if (r->req.aiocb)
|
||||
bdrv_aio_cancel(r->req.aiocb);
|
||||
r->req.aiocb = NULL;
|
||||
scsi_remove_request(r);
|
||||
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
|
||||
|
||||
DPRINTF("Cancel tag=0x%x\n", req->tag);
|
||||
if (r->req.aiocb) {
|
||||
bdrv_aio_cancel(r->req.aiocb);
|
||||
}
|
||||
r->req.aiocb = NULL;
|
||||
}
|
||||
|
||||
static void scsi_read_complete(void * opaque, int ret)
|
||||
@ -170,30 +152,38 @@ static void scsi_read_complete(void * opaque, int ret)
|
||||
n = r->iov.iov_len / 512;
|
||||
r->sector += n;
|
||||
r->sector_count -= n;
|
||||
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len);
|
||||
scsi_req_data(&r->req, r->iov.iov_len);
|
||||
}
|
||||
|
||||
|
||||
static void scsi_read_request(SCSIDiskReq *r)
|
||||
/* Read more data from scsi device into buffer. */
|
||||
static void scsi_read_data(SCSIRequest *req)
|
||||
{
|
||||
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
uint32_t n;
|
||||
|
||||
if (r->sector_count == (uint32_t)-1) {
|
||||
DPRINTF("Read buf_len=%zd\n", r->iov.iov_len);
|
||||
r->sector_count = 0;
|
||||
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len);
|
||||
scsi_req_data(&r->req, r->iov.iov_len);
|
||||
return;
|
||||
}
|
||||
DPRINTF("Read sector_count=%d\n", r->sector_count);
|
||||
if (r->sector_count == 0) {
|
||||
scsi_command_complete(r, GOOD, NO_SENSE);
|
||||
scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
|
||||
return;
|
||||
}
|
||||
|
||||
/* No data transfer may already be in progress */
|
||||
assert(r->req.aiocb == NULL);
|
||||
|
||||
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
|
||||
DPRINTF("Data transfer direction invalid\n");
|
||||
scsi_read_complete(r, -EINVAL);
|
||||
return;
|
||||
}
|
||||
|
||||
n = r->sector_count;
|
||||
if (n > SCSI_DMA_BUF_SIZE / 512)
|
||||
n = SCSI_DMA_BUF_SIZE / 512;
|
||||
@ -207,23 +197,6 @@ static void scsi_read_request(SCSIDiskReq *r)
|
||||
}
|
||||
}
|
||||
|
||||
/* Read more data from scsi device into buffer. */
|
||||
static void scsi_read_data(SCSIDevice *d, uint32_t tag)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
|
||||
SCSIDiskReq *r;
|
||||
|
||||
r = scsi_find_request(s, tag);
|
||||
if (!r) {
|
||||
BADF("Bad read tag 0x%x\n", tag);
|
||||
/* ??? This is the wrong error. */
|
||||
scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
scsi_read_request(r);
|
||||
}
|
||||
|
||||
static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type)
|
||||
{
|
||||
int is_read = (type == SCSI_REQ_STATUS_RETRY_READ);
|
||||
@ -245,13 +218,24 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type)
|
||||
vm_stop(VMSTOP_DISKFULL);
|
||||
} else {
|
||||
if (type == SCSI_REQ_STATUS_RETRY_READ) {
|
||||
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, 0);
|
||||
scsi_req_data(&r->req, 0);
|
||||
}
|
||||
switch (error) {
|
||||
case ENOMEM:
|
||||
scsi_command_complete(r, CHECK_CONDITION,
|
||||
SENSE_CODE(TARGET_FAILURE));
|
||||
break;
|
||||
case EINVAL:
|
||||
scsi_command_complete(r, CHECK_CONDITION,
|
||||
SENSE_CODE(INVALID_FIELD));
|
||||
break;
|
||||
default:
|
||||
scsi_command_complete(r, CHECK_CONDITION,
|
||||
SENSE_CODE(IO_ERROR));
|
||||
break;
|
||||
}
|
||||
scsi_command_complete(r, CHECK_CONDITION,
|
||||
HARDWARE_ERROR);
|
||||
bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -273,7 +257,7 @@ static void scsi_write_complete(void * opaque, int ret)
|
||||
r->sector += n;
|
||||
r->sector_count -= n;
|
||||
if (r->sector_count == 0) {
|
||||
scsi_command_complete(r, GOOD, NO_SENSE);
|
||||
scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
|
||||
} else {
|
||||
len = r->sector_count * 512;
|
||||
if (len > SCSI_DMA_BUF_SIZE) {
|
||||
@ -281,25 +265,32 @@ static void scsi_write_complete(void * opaque, int ret)
|
||||
}
|
||||
r->iov.iov_len = len;
|
||||
DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, len);
|
||||
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len);
|
||||
scsi_req_data(&r->req, len);
|
||||
}
|
||||
}
|
||||
|
||||
static void scsi_write_request(SCSIDiskReq *r)
|
||||
static void scsi_write_data(SCSIRequest *req)
|
||||
{
|
||||
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
uint32_t n;
|
||||
|
||||
/* No data transfer may already be in progress */
|
||||
assert(r->req.aiocb == NULL);
|
||||
|
||||
if (r->req.cmd.mode != SCSI_XFER_TO_DEV) {
|
||||
DPRINTF("Data transfer direction invalid\n");
|
||||
scsi_write_complete(r, -EINVAL);
|
||||
return;
|
||||
}
|
||||
|
||||
n = r->iov.iov_len / 512;
|
||||
if (n) {
|
||||
qemu_iovec_init_external(&r->qiov, &r->iov, 1);
|
||||
r->req.aiocb = bdrv_aio_writev(s->bs, r->sector, &r->qiov, n,
|
||||
scsi_write_complete, r);
|
||||
if (r->req.aiocb == NULL) {
|
||||
scsi_write_complete(r, -EIO);
|
||||
scsi_write_complete(r, -ENOMEM);
|
||||
}
|
||||
} else {
|
||||
/* Invoke completion routine to fetch data from host. */
|
||||
@ -307,26 +298,6 @@ static void scsi_write_request(SCSIDiskReq *r)
|
||||
}
|
||||
}
|
||||
|
||||
/* Write data to a scsi device. Returns nonzero on failure.
|
||||
The transfer may complete asynchronously. */
|
||||
static int scsi_write_data(SCSIDevice *d, uint32_t tag)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
|
||||
SCSIDiskReq *r;
|
||||
|
||||
DPRINTF("Write data tag=0x%x\n", tag);
|
||||
r = scsi_find_request(s, tag);
|
||||
if (!r) {
|
||||
BADF("Bad write tag 0x%x\n", tag);
|
||||
scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR);
|
||||
return 1;
|
||||
}
|
||||
|
||||
scsi_write_request(r);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scsi_dma_restart_bh(void *opaque)
|
||||
{
|
||||
SCSIDiskState *s = opaque;
|
||||
@ -347,15 +318,15 @@ static void scsi_dma_restart_bh(void *opaque)
|
||||
|
||||
switch (status & SCSI_REQ_STATUS_RETRY_TYPE_MASK) {
|
||||
case SCSI_REQ_STATUS_RETRY_READ:
|
||||
scsi_read_request(r);
|
||||
scsi_read_data(&r->req);
|
||||
break;
|
||||
case SCSI_REQ_STATUS_RETRY_WRITE:
|
||||
scsi_write_request(r);
|
||||
scsi_write_data(&r->req);
|
||||
break;
|
||||
case SCSI_REQ_STATUS_RETRY_FLUSH:
|
||||
ret = scsi_disk_emulate_command(r, r->iov.iov_base);
|
||||
if (ret == 0) {
|
||||
scsi_command_complete(r, GOOD, NO_SENSE);
|
||||
scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -376,19 +347,21 @@ 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)
|
||||
static uint8_t *scsi_get_buf(SCSIRequest *req)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
|
||||
SCSIDiskReq *r;
|
||||
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
|
||||
|
||||
r = scsi_find_request(s, tag);
|
||||
if (!r) {
|
||||
BADF("Bad buffer tag 0x%x\n", tag);
|
||||
return NULL;
|
||||
}
|
||||
return (uint8_t *)r->iov.iov_base;
|
||||
}
|
||||
|
||||
/* Copy sense information into the provided buffer */
|
||||
static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
|
||||
|
||||
return scsi_build_sense(s->sense, outbuf, len, len > 14);
|
||||
}
|
||||
|
||||
static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
|
||||
@ -545,7 +518,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
|
||||
|
||||
memset(outbuf, 0, buflen);
|
||||
|
||||
if (req->lun || req->cmd.buf[1] >> 5) {
|
||||
if (req->lun) {
|
||||
outbuf[0] = 0x7f; /* LUN not supported */
|
||||
return buflen;
|
||||
}
|
||||
@ -860,19 +833,8 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
|
||||
case REQUEST_SENSE:
|
||||
if (req->cmd.xfer < 4)
|
||||
goto illegal_request;
|
||||
memset(outbuf, 0, 4);
|
||||
buflen = 4;
|
||||
if (s->sense.key == NOT_READY && req->cmd.xfer >= 18) {
|
||||
memset(outbuf, 0, 18);
|
||||
buflen = 18;
|
||||
outbuf[7] = 10;
|
||||
/* asc 0x3a, ascq 0: Medium not present */
|
||||
outbuf[12] = 0x3a;
|
||||
outbuf[13] = 0;
|
||||
}
|
||||
outbuf[0] = 0xf0;
|
||||
outbuf[1] = 0;
|
||||
outbuf[2] = s->sense.key;
|
||||
buflen = scsi_build_sense(s->sense, outbuf, req->cmd.xfer,
|
||||
req->cmd.xfer > 13);
|
||||
scsi_disk_clear_sense(s);
|
||||
break;
|
||||
case INQUIRY:
|
||||
@ -1010,17 +972,22 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto illegal_request;
|
||||
scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
|
||||
return -1;
|
||||
}
|
||||
scsi_req_set_status(r, GOOD, NO_SENSE);
|
||||
scsi_req_set_status(r, GOOD, SENSE_CODE(NO_SENSE));
|
||||
return buflen;
|
||||
|
||||
not_ready:
|
||||
scsi_command_complete(r, CHECK_CONDITION, NOT_READY);
|
||||
if (!bdrv_is_inserted(s->bs)) {
|
||||
scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(NO_MEDIUM));
|
||||
} else {
|
||||
scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LUN_NOT_READY));
|
||||
}
|
||||
return -1;
|
||||
|
||||
illegal_request:
|
||||
scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST);
|
||||
scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1029,33 +996,23 @@ illegal_request:
|
||||
(eg. disk reads), negative for transfers to the device (eg. disk writes),
|
||||
and zero if the command does not transfer any data. */
|
||||
|
||||
static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
|
||||
uint8_t *buf, int lun)
|
||||
static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
|
||||
uint32_t len;
|
||||
int is_write;
|
||||
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
|
||||
int32_t len;
|
||||
uint8_t command;
|
||||
uint8_t *outbuf;
|
||||
SCSIDiskReq *r;
|
||||
int rc;
|
||||
|
||||
command = buf[0];
|
||||
r = scsi_find_request(s, tag);
|
||||
if (r) {
|
||||
BADF("Tag 0x%x already in use\n", tag);
|
||||
scsi_cancel_io(d, 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, lun);
|
||||
outbuf = (uint8_t *)r->iov.iov_base;
|
||||
is_write = 0;
|
||||
DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]);
|
||||
|
||||
if (scsi_req_parse(&r->req, buf) != 0) {
|
||||
BADF("Unsupported command length, command %x\n", command);
|
||||
goto fail;
|
||||
scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
|
||||
return 0;
|
||||
}
|
||||
#ifdef DEBUG_SCSI
|
||||
{
|
||||
@ -1067,11 +1024,14 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
|
||||
}
|
||||
#endif
|
||||
|
||||
if (lun || buf[1] >> 5) {
|
||||
if (req->lun) {
|
||||
/* Only LUN 0 supported. */
|
||||
DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5);
|
||||
if (command != REQUEST_SENSE && command != INQUIRY)
|
||||
goto fail;
|
||||
DPRINTF("Unimplemented LUN %d\n", req->lun);
|
||||
if (command != REQUEST_SENSE && command != INQUIRY) {
|
||||
scsi_command_complete(r, CHECK_CONDITION,
|
||||
SENSE_CODE(LUN_NOT_SUPPORTED));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
switch (command) {
|
||||
case TEST_UNIT_READY:
|
||||
@ -1104,7 +1064,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
|
||||
case READ_10:
|
||||
case READ_12:
|
||||
case READ_16:
|
||||
len = r->req.cmd.xfer / d->blocksize;
|
||||
len = r->req.cmd.xfer / s->qdev.blocksize;
|
||||
DPRINTF("Read (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len);
|
||||
if (r->req.cmd.lba > s->max_lba)
|
||||
goto illegal_lba;
|
||||
@ -1118,7 +1078,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
|
||||
case WRITE_VERIFY:
|
||||
case WRITE_VERIFY_12:
|
||||
case WRITE_VERIFY_16:
|
||||
len = r->req.cmd.xfer / d->blocksize;
|
||||
len = r->req.cmd.xfer / s->qdev.blocksize;
|
||||
DPRINTF("Write %s(sector %" PRId64 ", count %d)\n",
|
||||
(command & 0xe) == 0xe ? "And Verify " : "",
|
||||
r->req.cmd.lba, len);
|
||||
@ -1126,7 +1086,6 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
|
||||
goto illegal_lba;
|
||||
r->sector = r->req.cmd.lba * s->cluster_size;
|
||||
r->sector_count = len * s->cluster_size;
|
||||
is_write = 1;
|
||||
break;
|
||||
case MODE_SELECT:
|
||||
DPRINTF("Mode Select(6) (len %lu)\n", (long)r->req.cmd.xfer);
|
||||
@ -1153,7 +1112,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
|
||||
}
|
||||
break;
|
||||
case WRITE_SAME_16:
|
||||
len = r->req.cmd.xfer / d->blocksize;
|
||||
len = r->req.cmd.xfer / s->qdev.blocksize;
|
||||
|
||||
DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n",
|
||||
r->req.cmd.lba, len);
|
||||
@ -1179,18 +1138,20 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
|
||||
break;
|
||||
default:
|
||||
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
|
||||
scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE));
|
||||
return 0;
|
||||
fail:
|
||||
scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST);
|
||||
scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD));
|
||||
return 0;
|
||||
illegal_lba:
|
||||
scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR);
|
||||
scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LBA_OUT_OF_RANGE));
|
||||
return 0;
|
||||
}
|
||||
if (r->sector_count == 0 && r->iov.iov_len == 0) {
|
||||
scsi_command_complete(r, GOOD, NO_SENSE);
|
||||
scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE));
|
||||
}
|
||||
len = r->sector_count * 512 + r->iov.iov_len;
|
||||
if (is_write) {
|
||||
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
|
||||
return -len;
|
||||
} else {
|
||||
if (!r->sector_count)
|
||||
@ -1199,25 +1160,12 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
|
||||
}
|
||||
}
|
||||
|
||||
static void scsi_disk_purge_requests(SCSIDiskState *s)
|
||||
{
|
||||
SCSIDiskReq *r;
|
||||
|
||||
while (!QTAILQ_EMPTY(&s->qdev.requests)) {
|
||||
r = DO_UPCAST(SCSIDiskReq, req, QTAILQ_FIRST(&s->qdev.requests));
|
||||
if (r->req.aiocb) {
|
||||
bdrv_aio_cancel(r->req.aiocb);
|
||||
}
|
||||
scsi_remove_request(r);
|
||||
}
|
||||
}
|
||||
|
||||
static void scsi_disk_reset(DeviceState *dev)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev);
|
||||
uint64_t nb_sectors;
|
||||
|
||||
scsi_disk_purge_requests(s);
|
||||
scsi_device_purge_requests(&s->qdev);
|
||||
|
||||
bdrv_get_geometry(s->bs, &nb_sectors);
|
||||
nb_sectors /= s->cluster_size;
|
||||
@ -1231,7 +1179,7 @@ static void scsi_destroy(SCSIDevice *dev)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
|
||||
|
||||
scsi_disk_purge_requests(s);
|
||||
scsi_device_purge_requests(&s->qdev);
|
||||
blockdev_mark_auto_del(s->qdev.conf.bs);
|
||||
}
|
||||
|
||||
@ -1321,11 +1269,14 @@ static SCSIDeviceInfo scsi_disk_info[] = {
|
||||
.qdev.reset = scsi_disk_reset,
|
||||
.init = scsi_hd_initfn,
|
||||
.destroy = scsi_destroy,
|
||||
.alloc_req = scsi_new_request,
|
||||
.free_req = scsi_free_request,
|
||||
.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,
|
||||
.get_sense = scsi_get_sense,
|
||||
.qdev.props = (Property[]) {
|
||||
DEFINE_SCSI_DISK_PROPERTIES(),
|
||||
DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
|
||||
@ -1339,11 +1290,14 @@ static SCSIDeviceInfo scsi_disk_info[] = {
|
||||
.qdev.reset = scsi_disk_reset,
|
||||
.init = scsi_cd_initfn,
|
||||
.destroy = scsi_destroy,
|
||||
.alloc_req = scsi_new_request,
|
||||
.free_req = scsi_free_request,
|
||||
.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,
|
||||
.get_sense = scsi_get_sense,
|
||||
.qdev.props = (Property[]) {
|
||||
DEFINE_SCSI_DISK_PROPERTIES(),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
@ -1356,11 +1310,14 @@ static SCSIDeviceInfo scsi_disk_info[] = {
|
||||
.qdev.reset = scsi_disk_reset,
|
||||
.init = scsi_disk_initfn,
|
||||
.destroy = scsi_destroy,
|
||||
.alloc_req = scsi_new_request,
|
||||
.free_req = scsi_free_request,
|
||||
.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,
|
||||
.get_sense = scsi_get_sense,
|
||||
.qdev.props = (Property[]) {
|
||||
DEFINE_SCSI_DISK_PROPERTIES(),
|
||||
DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
|
||||
|
@ -66,23 +66,49 @@ struct SCSIGenericState
|
||||
uint8_t senselen;
|
||||
};
|
||||
|
||||
static SCSIGenericReq *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun)
|
||||
static void scsi_set_sense(SCSIGenericState *s, SCSISense sense)
|
||||
{
|
||||
s->senselen = scsi_build_sense(sense, s->sensebuf, SCSI_SENSE_BUF_SIZE, 0);
|
||||
s->driver_status = SG_ERR_DRIVER_SENSE;
|
||||
}
|
||||
|
||||
static void scsi_clear_sense(SCSIGenericState *s)
|
||||
{
|
||||
memset(s->sensebuf, 0, SCSI_SENSE_BUF_SIZE);
|
||||
s->senselen = 0;
|
||||
s->driver_status = 0;
|
||||
}
|
||||
|
||||
static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len)
|
||||
{
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
|
||||
int size = SCSI_SENSE_BUF_SIZE;
|
||||
|
||||
if (!(s->driver_status & SG_ERR_DRIVER_SENSE)) {
|
||||
size = scsi_build_sense(SENSE_CODE(NO_SENSE), s->sensebuf,
|
||||
SCSI_SENSE_BUF_SIZE, 0);
|
||||
}
|
||||
if (size > len) {
|
||||
size = len;
|
||||
}
|
||||
memcpy(outbuf, s->sensebuf, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun)
|
||||
{
|
||||
SCSIRequest *req;
|
||||
|
||||
req = scsi_req_alloc(sizeof(SCSIGenericReq), d, tag, lun);
|
||||
return DO_UPCAST(SCSIGenericReq, req, req);
|
||||
return req;
|
||||
}
|
||||
|
||||
static void scsi_remove_request(SCSIGenericReq *r)
|
||||
static void scsi_free_request(SCSIRequest *req)
|
||||
{
|
||||
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
|
||||
|
||||
qemu_free(r->buf);
|
||||
scsi_req_free(&r->req);
|
||||
}
|
||||
|
||||
static SCSIGenericReq *scsi_find_request(SCSIGenericState *s, uint32_t tag)
|
||||
{
|
||||
return DO_UPCAST(SCSIGenericReq, req, scsi_req_find(&s->qdev, tag));
|
||||
}
|
||||
|
||||
/* Helper function for command completion. */
|
||||
@ -91,13 +117,30 @@ static void scsi_command_complete(void *opaque, int ret)
|
||||
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
|
||||
|
||||
r->req.aiocb = NULL;
|
||||
s->driver_status = r->io_header.driver_status;
|
||||
if (s->driver_status & SG_ERR_DRIVER_SENSE)
|
||||
s->senselen = r->io_header.sb_len_wr;
|
||||
|
||||
if (ret != 0)
|
||||
r->req.status = BUSY;
|
||||
else {
|
||||
if (ret != 0) {
|
||||
switch (ret) {
|
||||
case -EDOM:
|
||||
r->req.status = TASK_SET_FULL;
|
||||
break;
|
||||
case -EINVAL:
|
||||
r->req.status = CHECK_CONDITION;
|
||||
scsi_set_sense(s, SENSE_CODE(INVALID_FIELD));
|
||||
break;
|
||||
case -ENOMEM:
|
||||
r->req.status = CHECK_CONDITION;
|
||||
scsi_set_sense(s, SENSE_CODE(TARGET_FAILURE));
|
||||
break;
|
||||
default:
|
||||
r->req.status = CHECK_CONDITION;
|
||||
scsi_set_sense(s, SENSE_CODE(IO_ERROR));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) {
|
||||
r->req.status = BUSY;
|
||||
BADF("Driver Timeout\n");
|
||||
@ -112,23 +155,18 @@ static void scsi_command_complete(void *opaque, int ret)
|
||||
r, r->req.tag, r->req.status);
|
||||
|
||||
scsi_req_complete(&r->req);
|
||||
scsi_remove_request(r);
|
||||
}
|
||||
|
||||
/* Cancel a pending data transfer. */
|
||||
static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
|
||||
static void scsi_cancel_io(SCSIRequest *req)
|
||||
{
|
||||
DPRINTF("scsi_cancel_io 0x%x\n", tag);
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
|
||||
SCSIGenericReq *r;
|
||||
DPRINTF("Cancel tag=0x%x\n", tag);
|
||||
r = scsi_find_request(s, tag);
|
||||
if (r) {
|
||||
if (r->req.aiocb)
|
||||
bdrv_aio_cancel(r->req.aiocb);
|
||||
r->req.aiocb = NULL;
|
||||
scsi_remove_request(r);
|
||||
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
|
||||
|
||||
DPRINTF("Cancel tag=0x%x\n", req->tag);
|
||||
if (r->req.aiocb) {
|
||||
bdrv_aio_cancel(r->req.aiocb);
|
||||
}
|
||||
r->req.aiocb = NULL;
|
||||
}
|
||||
|
||||
static int execute_command(BlockDriverState *bdrv,
|
||||
@ -152,7 +190,7 @@ static int execute_command(BlockDriverState *bdrv,
|
||||
r->req.aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r);
|
||||
if (r->req.aiocb == NULL) {
|
||||
BADF("execute_command: read failed !\n");
|
||||
return -1;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -163,6 +201,7 @@ static void scsi_read_complete(void * opaque, int ret)
|
||||
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
|
||||
int len;
|
||||
|
||||
r->req.aiocb = NULL;
|
||||
if (ret) {
|
||||
DPRINTF("IO error ret %d\n", ret);
|
||||
scsi_command_complete(r, ret);
|
||||
@ -172,27 +211,21 @@ static void scsi_read_complete(void * opaque, int ret)
|
||||
DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len);
|
||||
|
||||
r->len = -1;
|
||||
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len);
|
||||
if (len == 0)
|
||||
if (len == 0) {
|
||||
scsi_command_complete(r, 0);
|
||||
} else {
|
||||
scsi_req_data(&r->req, len);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read more data from scsi device into buffer. */
|
||||
static void scsi_read_data(SCSIDevice *d, uint32_t tag)
|
||||
static void scsi_read_data(SCSIRequest *req)
|
||||
{
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
|
||||
SCSIGenericReq *r;
|
||||
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
|
||||
int ret;
|
||||
|
||||
DPRINTF("scsi_read_data 0x%x\n", tag);
|
||||
r = scsi_find_request(s, tag);
|
||||
if (!r) {
|
||||
BADF("Bad read tag 0x%x\n", tag);
|
||||
/* ??? This is the wrong error. */
|
||||
scsi_command_complete(r, -EINVAL);
|
||||
return;
|
||||
}
|
||||
|
||||
DPRINTF("scsi_read_data 0x%x\n", req->tag);
|
||||
if (r->len == -1) {
|
||||
scsi_command_complete(r, 0);
|
||||
return;
|
||||
@ -210,13 +243,15 @@ 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]);
|
||||
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, s->senselen);
|
||||
scsi_req_data(&r->req, s->senselen);
|
||||
/* Clear sensebuf after REQUEST_SENSE */
|
||||
scsi_clear_sense(s);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = execute_command(s->bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
|
||||
if (ret == -1) {
|
||||
scsi_command_complete(r, -EINVAL);
|
||||
if (ret < 0) {
|
||||
scsi_command_complete(r, ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -227,6 +262,7 @@ static void scsi_write_complete(void * opaque, int ret)
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
|
||||
|
||||
DPRINTF("scsi_write_complete() ret = %d\n", ret);
|
||||
r->req.aiocb = NULL;
|
||||
if (ret) {
|
||||
DPRINTF("IO error\n");
|
||||
scsi_command_complete(r, ret);
|
||||
@ -244,46 +280,30 @@ static void scsi_write_complete(void * opaque, int ret)
|
||||
|
||||
/* Write data to a scsi device. Returns nonzero on failure.
|
||||
The transfer may complete asynchronously. */
|
||||
static int scsi_write_data(SCSIDevice *d, uint32_t tag)
|
||||
static void scsi_write_data(SCSIRequest *req)
|
||||
{
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
|
||||
SCSIGenericReq *r;
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
|
||||
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
|
||||
int ret;
|
||||
|
||||
DPRINTF("scsi_write_data 0x%x\n", tag);
|
||||
r = scsi_find_request(s, tag);
|
||||
if (!r) {
|
||||
BADF("Bad write tag 0x%x\n", tag);
|
||||
/* ??? This is the wrong error. */
|
||||
scsi_command_complete(r, -EINVAL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DPRINTF("scsi_write_data 0x%x\n", req->tag);
|
||||
if (r->len == 0) {
|
||||
r->len = r->buflen;
|
||||
r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->len);
|
||||
return 0;
|
||||
scsi_req_data(&r->req, r->len);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = execute_command(s->bs, r, SG_DXFER_TO_DEV, scsi_write_complete);
|
||||
if (ret == -1) {
|
||||
scsi_command_complete(r, -EINVAL);
|
||||
return 1;
|
||||
if (ret < 0) {
|
||||
scsi_command_complete(r, ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return a pointer to the data buffer. */
|
||||
static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
|
||||
static uint8_t *scsi_get_buf(SCSIRequest *req)
|
||||
{
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
|
||||
SCSIGenericReq *r;
|
||||
r = scsi_find_request(s, tag);
|
||||
if (!r) {
|
||||
BADF("Bad buffer tag 0x%x\n", tag);
|
||||
return NULL;
|
||||
}
|
||||
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
|
||||
|
||||
return r->buf;
|
||||
}
|
||||
|
||||
@ -311,42 +331,23 @@ static void scsi_req_fixup(SCSIRequest *req)
|
||||
(eg. disk reads), negative for transfers to the device (eg. disk writes),
|
||||
and zero if the command does not transfer any data. */
|
||||
|
||||
static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
|
||||
uint8_t *cmd, int lun)
|
||||
static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
|
||||
{
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
|
||||
SCSIGenericReq *r;
|
||||
SCSIBus *bus;
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
|
||||
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
|
||||
int ret;
|
||||
|
||||
if (cmd[0] != REQUEST_SENSE &&
|
||||
(lun != s->lun || (cmd[1] >> 5) != s->lun)) {
|
||||
DPRINTF("Unimplemented LUN %d\n", lun ? lun : cmd[1] >> 5);
|
||||
|
||||
s->sensebuf[0] = 0x70;
|
||||
s->sensebuf[1] = 0x00;
|
||||
s->sensebuf[2] = ILLEGAL_REQUEST;
|
||||
s->sensebuf[3] = 0x00;
|
||||
s->sensebuf[4] = 0x00;
|
||||
s->sensebuf[5] = 0x00;
|
||||
s->sensebuf[6] = 0x00;
|
||||
s->senselen = 7;
|
||||
s->driver_status = SG_ERR_DRIVER_SENSE;
|
||||
bus = scsi_bus_from_device(d);
|
||||
bus->complete(bus, SCSI_REASON_DONE, tag, CHECK_CONDITION);
|
||||
if (cmd[0] != REQUEST_SENSE && req->lun != s->lun) {
|
||||
DPRINTF("Unimplemented LUN %d\n", req->lun);
|
||||
scsi_set_sense(s, SENSE_CODE(LUN_NOT_SUPPORTED));
|
||||
r->req.status = CHECK_CONDITION;
|
||||
scsi_req_complete(&r->req);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = scsi_find_request(s, tag);
|
||||
if (r) {
|
||||
BADF("Tag 0x%x already in use %p\n", tag, r);
|
||||
scsi_cancel_io(d, tag);
|
||||
}
|
||||
r = scsi_new_request(d, tag, lun);
|
||||
|
||||
if (-1 == scsi_req_parse(&r->req, cmd)) {
|
||||
BADF("Unsupported command length, command %x\n", cmd[0]);
|
||||
scsi_remove_request(r);
|
||||
scsi_command_complete(r, -EINVAL);
|
||||
return 0;
|
||||
}
|
||||
scsi_req_fixup(&r->req);
|
||||
@ -370,8 +371,8 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
|
||||
r->buflen = 0;
|
||||
r->buf = NULL;
|
||||
ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete);
|
||||
if (ret == -1) {
|
||||
scsi_command_complete(r, -EINVAL);
|
||||
if (ret < 0) {
|
||||
scsi_command_complete(r, ret);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
@ -389,9 +390,9 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
|
||||
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
|
||||
r->len = 0;
|
||||
return -r->req.cmd.xfer;
|
||||
} else {
|
||||
return r->req.cmd.xfer;
|
||||
}
|
||||
|
||||
return r->req.cmd.xfer;
|
||||
}
|
||||
|
||||
static int get_blocksize(BlockDriverState *bdrv)
|
||||
@ -455,31 +456,18 @@ static int get_stream_blocksize(BlockDriverState *bdrv)
|
||||
return (buf[9] << 16) | (buf[10] << 8) | buf[11];
|
||||
}
|
||||
|
||||
static void scsi_generic_purge_requests(SCSIGenericState *s)
|
||||
{
|
||||
SCSIGenericReq *r;
|
||||
|
||||
while (!QTAILQ_EMPTY(&s->qdev.requests)) {
|
||||
r = DO_UPCAST(SCSIGenericReq, req, QTAILQ_FIRST(&s->qdev.requests));
|
||||
if (r->req.aiocb) {
|
||||
bdrv_aio_cancel(r->req.aiocb);
|
||||
}
|
||||
scsi_remove_request(r);
|
||||
}
|
||||
}
|
||||
|
||||
static void scsi_generic_reset(DeviceState *dev)
|
||||
{
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev.qdev, dev);
|
||||
|
||||
scsi_generic_purge_requests(s);
|
||||
scsi_device_purge_requests(&s->qdev);
|
||||
}
|
||||
|
||||
static void scsi_destroy(SCSIDevice *d)
|
||||
{
|
||||
SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
|
||||
|
||||
scsi_generic_purge_requests(s);
|
||||
scsi_device_purge_requests(&s->qdev);
|
||||
blockdev_mark_auto_del(s->qdev.conf.bs);
|
||||
}
|
||||
|
||||
@ -556,11 +544,14 @@ static SCSIDeviceInfo scsi_generic_info = {
|
||||
.qdev.reset = scsi_generic_reset,
|
||||
.init = scsi_generic_initfn,
|
||||
.destroy = scsi_destroy,
|
||||
.alloc_req = scsi_new_request,
|
||||
.free_req = scsi_free_request,
|
||||
.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,
|
||||
.get_sense = scsi_get_sense,
|
||||
.qdev.props = (Property[]) {
|
||||
DEFINE_BLOCK_PROPERTIES(SCSIGenericState, qdev.conf),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
|
91
hw/scsi.h
91
hw/scsi.h
@ -9,17 +9,11 @@
|
||||
|
||||
#define SCSI_CMD_BUF_SIZE 16
|
||||
|
||||
/* scsi-disk.c */
|
||||
enum scsi_reason {
|
||||
SCSI_REASON_DONE, /* Command complete. */
|
||||
SCSI_REASON_DATA /* Transfer complete, more data required. */
|
||||
};
|
||||
|
||||
typedef struct SCSIBus SCSIBus;
|
||||
typedef struct SCSIBusOps SCSIBusOps;
|
||||
typedef struct SCSIDevice SCSIDevice;
|
||||
typedef struct SCSIDeviceInfo SCSIDeviceInfo;
|
||||
typedef void (*scsi_completionfn)(SCSIBus *bus, int reason, uint32_t tag,
|
||||
uint32_t arg);
|
||||
typedef struct SCSIRequest SCSIRequest;
|
||||
|
||||
enum SCSIXferMode {
|
||||
SCSI_XFER_NONE, /* TEST_UNIT_READY, ... */
|
||||
@ -27,9 +21,16 @@ enum SCSIXferMode {
|
||||
SCSI_XFER_TO_DEV, /* WRITE, MODE_SELECT, ... */
|
||||
};
|
||||
|
||||
typedef struct SCSIRequest {
|
||||
typedef struct SCSISense {
|
||||
uint8_t key;
|
||||
uint8_t asc;
|
||||
uint8_t ascq;
|
||||
} SCSISense;
|
||||
|
||||
struct SCSIRequest {
|
||||
SCSIBus *bus;
|
||||
SCSIDevice *dev;
|
||||
uint32_t refcount;
|
||||
uint32_t tag;
|
||||
uint32_t lun;
|
||||
uint32_t status;
|
||||
@ -43,7 +44,7 @@ typedef struct SCSIRequest {
|
||||
BlockDriverAIOCB *aiocb;
|
||||
bool enqueued;
|
||||
QTAILQ_ENTRY(SCSIRequest) next;
|
||||
} SCSIRequest;
|
||||
};
|
||||
|
||||
struct SCSIDevice
|
||||
{
|
||||
@ -66,28 +67,34 @@ 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);
|
||||
void (*read_data)(SCSIDevice *s, uint32_t tag);
|
||||
int (*write_data)(SCSIDevice *s, uint32_t tag);
|
||||
void (*cancel_io)(SCSIDevice *s, uint32_t tag);
|
||||
uint8_t *(*get_buf)(SCSIDevice *s, uint32_t tag);
|
||||
SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun);
|
||||
void (*free_req)(SCSIRequest *req);
|
||||
int32_t (*send_command)(SCSIRequest *req, uint8_t *buf);
|
||||
void (*read_data)(SCSIRequest *req);
|
||||
void (*write_data)(SCSIRequest *req);
|
||||
void (*cancel_io)(SCSIRequest *req);
|
||||
uint8_t *(*get_buf)(SCSIRequest *req);
|
||||
int (*get_sense)(SCSIRequest *req, uint8_t *buf, int len);
|
||||
};
|
||||
|
||||
struct SCSIBusOps {
|
||||
void (*transfer_data)(SCSIRequest *req, uint32_t arg);
|
||||
void (*complete)(SCSIRequest *req, uint32_t arg);
|
||||
void (*cancel)(SCSIRequest *req);
|
||||
};
|
||||
|
||||
typedef void (*SCSIAttachFn)(DeviceState *host, BlockDriverState *bdrv,
|
||||
int unit);
|
||||
struct SCSIBus {
|
||||
BusState qbus;
|
||||
int busnr;
|
||||
|
||||
int tcq, ndev;
|
||||
scsi_completionfn complete;
|
||||
const SCSIBusOps *ops;
|
||||
|
||||
SCSIDevice *devs[MAX_SCSI_DEVS];
|
||||
};
|
||||
|
||||
void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev,
|
||||
scsi_completionfn complete);
|
||||
const SCSIBusOps *ops);
|
||||
void scsi_qdev_register(SCSIDeviceInfo *info);
|
||||
|
||||
static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d)
|
||||
@ -99,12 +106,54 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv,
|
||||
int unit, bool removable);
|
||||
int scsi_bus_legacy_handle_cmdline(SCSIBus *bus);
|
||||
|
||||
/*
|
||||
* Predefined sense codes
|
||||
*/
|
||||
|
||||
/* No sense data available */
|
||||
extern const struct SCSISense sense_code_NO_SENSE;
|
||||
/* LUN not ready, Manual intervention required */
|
||||
extern const struct SCSISense sense_code_LUN_NOT_READY;
|
||||
/* LUN not ready, Medium not present */
|
||||
extern const struct SCSISense sense_code_NO_MEDIUM;
|
||||
/* Hardware error, internal target failure */
|
||||
extern const struct SCSISense sense_code_TARGET_FAILURE;
|
||||
/* Illegal request, invalid command operation code */
|
||||
extern const struct SCSISense sense_code_INVALID_OPCODE;
|
||||
/* Illegal request, LBA out of range */
|
||||
extern const struct SCSISense sense_code_LBA_OUT_OF_RANGE;
|
||||
/* Illegal request, Invalid field in CDB */
|
||||
extern const struct SCSISense sense_code_INVALID_FIELD;
|
||||
/* Illegal request, LUN not supported */
|
||||
extern const struct SCSISense sense_code_LUN_NOT_SUPPORTED;
|
||||
/* Command aborted, I/O process terminated */
|
||||
extern const struct SCSISense sense_code_IO_ERROR;
|
||||
/* Command aborted, I_T Nexus loss occurred */
|
||||
extern const struct SCSISense sense_code_I_T_NEXUS_LOSS;
|
||||
/* Command aborted, Logical Unit failure */
|
||||
extern const struct SCSISense sense_code_LUN_FAILURE;
|
||||
|
||||
#define SENSE_CODE(x) sense_code_ ## x
|
||||
|
||||
int scsi_build_sense(SCSISense sense, uint8_t *buf, int len, int fixed);
|
||||
int scsi_sense_valid(SCSISense sense);
|
||||
|
||||
SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun);
|
||||
SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag);
|
||||
SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun);
|
||||
int32_t scsi_req_enqueue(SCSIRequest *req, uint8_t *buf);
|
||||
void scsi_req_free(SCSIRequest *req);
|
||||
SCSIRequest *scsi_req_ref(SCSIRequest *req);
|
||||
void scsi_req_unref(SCSIRequest *req);
|
||||
|
||||
int scsi_req_parse(SCSIRequest *req, uint8_t *buf);
|
||||
void scsi_req_print(SCSIRequest *req);
|
||||
void scsi_req_continue(SCSIRequest *req);
|
||||
void scsi_req_data(SCSIRequest *req, int len);
|
||||
void scsi_req_complete(SCSIRequest *req);
|
||||
uint8_t *scsi_req_get_buf(SCSIRequest *req);
|
||||
int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len);
|
||||
void scsi_req_abort(SCSIRequest *req, int status);
|
||||
void scsi_req_cancel(SCSIRequest *req);
|
||||
void scsi_device_purge_requests(SCSIDevice *sdev);
|
||||
|
||||
#endif
|
||||
|
185
hw/spapr_vscsi.c
185
hw/spapr_vscsi.c
@ -74,7 +74,7 @@ typedef struct vscsi_req {
|
||||
union viosrp_iu iu;
|
||||
|
||||
/* SCSI request tracking */
|
||||
SCSIDevice *sdev;
|
||||
SCSIRequest *sreq;
|
||||
uint32_t qtag; /* qemu tag != srp tag */
|
||||
int lun;
|
||||
int active;
|
||||
@ -123,11 +123,16 @@ static struct vscsi_req *vscsi_get_req(VSCSIState *s)
|
||||
|
||||
static void vscsi_put_req(VSCSIState *s, vscsi_req *req)
|
||||
{
|
||||
if (req->sreq != NULL) {
|
||||
scsi_req_unref(req->sreq);
|
||||
}
|
||||
req->sreq = NULL;
|
||||
req->active = 0;
|
||||
}
|
||||
|
||||
static vscsi_req *vscsi_find_req(VSCSIState *s, uint32_t tag)
|
||||
static vscsi_req *vscsi_find_req(VSCSIState *s, SCSIRequest *req)
|
||||
{
|
||||
uint32_t tag = req->tag;
|
||||
if (tag >= VSCSI_REQ_LIMIT || !s->reqs[tag].active) {
|
||||
return NULL;
|
||||
}
|
||||
@ -442,10 +447,18 @@ static int vscsi_preprocess_desc(vscsi_req *req)
|
||||
|
||||
static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
|
||||
{
|
||||
SCSIDevice *sdev = req->sdev;
|
||||
uint8_t *cdb = req->iu.srp.cmd.cdb;
|
||||
int n;
|
||||
|
||||
n = scsi_req_get_sense(req->sreq, req->sense, sizeof(req->sense));
|
||||
if (n) {
|
||||
req->senselen = n;
|
||||
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
|
||||
vscsi_put_req(s, req);
|
||||
return;
|
||||
}
|
||||
|
||||
dprintf("VSCSI: Got CHECK_CONDITION, requesting sense...\n");
|
||||
cdb[0] = 3;
|
||||
cdb[1] = 0;
|
||||
cdb[2] = 0;
|
||||
@ -453,66 +466,92 @@ static void vscsi_send_request_sense(VSCSIState *s, vscsi_req *req)
|
||||
cdb[4] = 96;
|
||||
cdb[5] = 0;
|
||||
req->sensing = 1;
|
||||
n = sdev->info->send_command(sdev, req->qtag, cdb, req->lun);
|
||||
n = scsi_req_enqueue(req->sreq, cdb);
|
||||
dprintf("VSCSI: Queued request sense tag 0x%x\n", req->qtag);
|
||||
if (n < 0) {
|
||||
fprintf(stderr, "VSCSI: REQUEST_SENSE wants write data !?!?!?\n");
|
||||
sdev->info->cancel_io(sdev, req->qtag);
|
||||
vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
|
||||
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
|
||||
vscsi_put_req(s, req);
|
||||
scsi_req_abort(req->sreq, CHECK_CONDITION);
|
||||
return;
|
||||
} else if (n == 0) {
|
||||
return;
|
||||
}
|
||||
sdev->info->read_data(sdev, req->qtag);
|
||||
scsi_req_continue(req->sreq);
|
||||
}
|
||||
|
||||
/* Callback to indicate that the SCSI layer has completed a transfer. */
|
||||
static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
|
||||
uint32_t arg)
|
||||
static void vscsi_transfer_data(SCSIRequest *sreq, uint32_t len)
|
||||
{
|
||||
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, bus->qbus.parent);
|
||||
vscsi_req *req = vscsi_find_req(s, tag);
|
||||
SCSIDevice *sdev;
|
||||
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
|
||||
vscsi_req *req = vscsi_find_req(s, sreq);
|
||||
uint8_t *buf;
|
||||
int32_t res_in = 0, res_out = 0;
|
||||
int len, rc = 0;
|
||||
int rc = 0;
|
||||
|
||||
dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x arg=0x%x, req=%p\n",
|
||||
reason, tag, arg, req);
|
||||
dprintf("VSCSI: SCSI xfer complete tag=0x%x len=0x%x, req=%p\n",
|
||||
sreq->tag, len, req);
|
||||
if (req == NULL) {
|
||||
fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", tag);
|
||||
fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag);
|
||||
return;
|
||||
}
|
||||
sdev = req->sdev;
|
||||
|
||||
if (req->sensing) {
|
||||
if (reason == SCSI_REASON_DONE) {
|
||||
dprintf("VSCSI: Sense done !\n");
|
||||
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
|
||||
vscsi_put_req(s, req);
|
||||
} else {
|
||||
uint8_t *buf = sdev->info->get_buf(sdev, tag);
|
||||
uint8_t *buf = scsi_req_get_buf(sreq);
|
||||
|
||||
len = MIN(arg, SCSI_SENSE_BUF_SIZE);
|
||||
dprintf("VSCSI: Sense data, %d bytes:\n", len);
|
||||
dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
buf[0], buf[1], buf[2], buf[3],
|
||||
buf[4], buf[5], buf[6], buf[7]);
|
||||
dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
buf[8], buf[9], buf[10], buf[11],
|
||||
buf[12], buf[13], buf[14], buf[15]);
|
||||
memcpy(req->sense, buf, len);
|
||||
req->senselen = len;
|
||||
sdev->info->read_data(sdev, req->qtag);
|
||||
}
|
||||
len = MIN(len, SCSI_SENSE_BUF_SIZE);
|
||||
dprintf("VSCSI: Sense data, %d bytes:\n", len);
|
||||
dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
buf[0], buf[1], buf[2], buf[3],
|
||||
buf[4], buf[5], buf[6], buf[7]);
|
||||
dprintf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
buf[8], buf[9], buf[10], buf[11],
|
||||
buf[12], buf[13], buf[14], buf[15]);
|
||||
memcpy(req->sense, buf, len);
|
||||
req->senselen = len;
|
||||
scsi_req_continue(req->sreq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (reason == SCSI_REASON_DONE) {
|
||||
dprintf("VSCSI: Command complete err=%d\n", arg);
|
||||
if (arg == 0) {
|
||||
if (len) {
|
||||
buf = scsi_req_get_buf(sreq);
|
||||
rc = vscsi_srp_transfer_data(s, req, req->writing, buf, len);
|
||||
}
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "VSCSI: RDMA error rc=%d!\n", rc);
|
||||
vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
|
||||
scsi_req_abort(req->sreq, CHECK_CONDITION);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Start next chunk */
|
||||
req->data_len -= rc;
|
||||
scsi_req_continue(sreq);
|
||||
}
|
||||
|
||||
/* Callback to indicate that the SCSI layer has completed a transfer. */
|
||||
static void vscsi_command_complete(SCSIRequest *sreq, uint32_t status)
|
||||
{
|
||||
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
|
||||
vscsi_req *req = vscsi_find_req(s, sreq);
|
||||
int32_t res_in = 0, res_out = 0;
|
||||
|
||||
dprintf("VSCSI: SCSI cmd complete, r=0x%x tag=0x%x status=0x%x, req=%p\n",
|
||||
reason, sreq->tag, status, req);
|
||||
if (req == NULL) {
|
||||
fprintf(stderr, "VSCSI: Can't find request for tag 0x%x\n", sreq->tag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!req->sensing && status == CHECK_CONDITION) {
|
||||
vscsi_send_request_sense(s, req);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->sensing) {
|
||||
dprintf("VSCSI: Sense done !\n");
|
||||
status = CHECK_CONDITION;
|
||||
} else {
|
||||
dprintf("VSCSI: Command complete err=%d\n", status);
|
||||
if (status == 0) {
|
||||
/* We handle overflows, not underflows for normal commands,
|
||||
* but hopefully nobody cares
|
||||
*/
|
||||
@ -521,41 +560,18 @@ static void vscsi_command_complete(SCSIBus *bus, int reason, uint32_t tag,
|
||||
} else {
|
||||
res_in = req->data_len;
|
||||
}
|
||||
vscsi_send_rsp(s, req, 0, res_in, res_out);
|
||||
} else if (arg == CHECK_CONDITION) {
|
||||
dprintf("VSCSI: Got CHECK_CONDITION, requesting sense...\n");
|
||||
vscsi_send_request_sense(s, req);
|
||||
return;
|
||||
} else {
|
||||
vscsi_send_rsp(s, req, arg, 0, 0);
|
||||
}
|
||||
vscsi_put_req(s, req);
|
||||
return;
|
||||
}
|
||||
vscsi_send_rsp(s, req, 0, res_in, res_out);
|
||||
vscsi_put_req(s, req);
|
||||
}
|
||||
|
||||
/* "arg" is how much we have read for reads and how much we want
|
||||
* to write for writes (ie, how much is to be DMA'd)
|
||||
*/
|
||||
if (arg) {
|
||||
buf = sdev->info->get_buf(sdev, tag);
|
||||
rc = vscsi_srp_transfer_data(s, req, req->writing, buf, arg);
|
||||
}
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "VSCSI: RDMA error rc=%d!\n", rc);
|
||||
sdev->info->cancel_io(sdev, req->qtag);
|
||||
vscsi_makeup_sense(s, req, HARDWARE_ERROR, 0, 0);
|
||||
vscsi_send_rsp(s, req, CHECK_CONDITION, 0, 0);
|
||||
vscsi_put_req(s, req);
|
||||
return;
|
||||
}
|
||||
static void vscsi_request_cancelled(SCSIRequest *sreq)
|
||||
{
|
||||
VSCSIState *s = DO_UPCAST(VSCSIState, vdev.qdev, sreq->bus->qbus.parent);
|
||||
vscsi_req *req = vscsi_find_req(s, sreq);
|
||||
|
||||
/* Start next chunk */
|
||||
req->data_len -= rc;
|
||||
if (req->writing) {
|
||||
sdev->info->write_data(sdev, req->qtag);
|
||||
} else {
|
||||
sdev->info->read_data(sdev, req->qtag);
|
||||
}
|
||||
vscsi_put_req(s, req);
|
||||
}
|
||||
|
||||
static void vscsi_process_login(VSCSIState *s, vscsi_req *req)
|
||||
@ -642,9 +658,9 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
|
||||
} return 1;
|
||||
}
|
||||
|
||||
req->sdev = sdev;
|
||||
req->lun = lun;
|
||||
n = sdev->info->send_command(sdev, req->qtag, srp->cmd.cdb, lun);
|
||||
req->sreq = scsi_req_new(sdev, req->qtag, lun);
|
||||
n = scsi_req_enqueue(req->sreq, srp->cmd.cdb);
|
||||
|
||||
dprintf("VSCSI: Queued command tag 0x%x CMD 0x%x ID %d LUN %d ret: %d\n",
|
||||
req->qtag, srp->cmd.cdb[0], id, lun, n);
|
||||
@ -657,15 +673,14 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
|
||||
|
||||
/* Preprocess RDMA descriptors */
|
||||
vscsi_preprocess_desc(req);
|
||||
}
|
||||
|
||||
/* Get transfer direction and initiate transfer */
|
||||
if (n > 0) {
|
||||
req->data_len = n;
|
||||
sdev->info->read_data(sdev, req->qtag);
|
||||
} else if (n < 0) {
|
||||
req->data_len = -n;
|
||||
sdev->info->write_data(sdev, req->qtag);
|
||||
/* Get transfer direction and initiate transfer */
|
||||
if (n > 0) {
|
||||
req->data_len = n;
|
||||
} else if (n < 0) {
|
||||
req->data_len = -n;
|
||||
}
|
||||
scsi_req_continue(req->sreq);
|
||||
}
|
||||
/* Don't touch req here, it may have been recycled already */
|
||||
|
||||
@ -907,6 +922,12 @@ static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct SCSIBusOps vscsi_scsi_ops = {
|
||||
.transfer_data = vscsi_transfer_data,
|
||||
.complete = vscsi_command_complete,
|
||||
.cancel = vscsi_request_cancelled
|
||||
};
|
||||
|
||||
static int spapr_vscsi_init(VIOsPAPRDevice *dev)
|
||||
{
|
||||
VSCSIState *s = DO_UPCAST(VSCSIState, vdev, dev);
|
||||
@ -923,7 +944,7 @@ static int spapr_vscsi_init(VIOsPAPRDevice *dev)
|
||||
dev->crq.SendFunc = vscsi_do_crq;
|
||||
|
||||
scsi_bus_new(&s->bus, &dev->qdev, 1, VSCSI_REQ_LIMIT,
|
||||
vscsi_command_complete);
|
||||
&vscsi_scsi_ops);
|
||||
if (!dev->qdev.hotplugged) {
|
||||
scsi_bus_legacy_handle_cmdline(&s->bus);
|
||||
}
|
||||
|
122
hw/usb-msd.c
122
hw/usb-msd.c
@ -48,6 +48,7 @@ typedef struct {
|
||||
uint32_t data_len;
|
||||
uint32_t residue;
|
||||
uint32_t tag;
|
||||
SCSIRequest *req;
|
||||
SCSIBus bus;
|
||||
BlockConf conf;
|
||||
SCSIDevice *scsi_dev;
|
||||
@ -191,11 +192,7 @@ static void usb_msd_copy_data(MSDState *s)
|
||||
s->scsi_buf += len;
|
||||
s->data_len -= len;
|
||||
if (s->scsi_len == 0 || s->data_len == 0) {
|
||||
if (s->mode == USB_MSDM_DATAIN) {
|
||||
s->scsi_dev->info->read_data(s->scsi_dev, s->tag);
|
||||
} else if (s->mode == USB_MSDM_DATAOUT) {
|
||||
s->scsi_dev->info->write_data(s->scsi_dev, s->tag);
|
||||
}
|
||||
scsi_req_continue(s->req);
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,44 +210,18 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p)
|
||||
memcpy(p->data, &csw, len);
|
||||
}
|
||||
|
||||
static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
|
||||
uint32_t arg)
|
||||
static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
|
||||
{
|
||||
MSDState *s = DO_UPCAST(MSDState, dev.qdev, bus->qbus.parent);
|
||||
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
|
||||
USBPacket *p = s->packet;
|
||||
|
||||
if (tag != s->tag) {
|
||||
fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", tag);
|
||||
if (req->tag != s->tag) {
|
||||
fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag);
|
||||
}
|
||||
if (reason == SCSI_REASON_DONE) {
|
||||
DPRINTF("Command complete %d\n", arg);
|
||||
s->residue = s->data_len;
|
||||
s->result = arg != 0;
|
||||
if (s->packet) {
|
||||
if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) {
|
||||
/* A deferred packet with no write data remaining must be
|
||||
the status read packet. */
|
||||
usb_msd_send_status(s, p);
|
||||
s->mode = USB_MSDM_CBW;
|
||||
} else {
|
||||
if (s->data_len) {
|
||||
s->data_len -= s->usb_len;
|
||||
if (s->mode == USB_MSDM_DATAIN)
|
||||
memset(s->usb_buf, 0, s->usb_len);
|
||||
s->usb_len = 0;
|
||||
}
|
||||
if (s->data_len == 0)
|
||||
s->mode = USB_MSDM_CSW;
|
||||
}
|
||||
s->packet = NULL;
|
||||
usb_packet_complete(&s->dev, p);
|
||||
} else if (s->data_len == 0) {
|
||||
s->mode = USB_MSDM_CSW;
|
||||
}
|
||||
return;
|
||||
}
|
||||
s->scsi_len = arg;
|
||||
s->scsi_buf = s->scsi_dev->info->get_buf(s->scsi_dev, tag);
|
||||
|
||||
assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
|
||||
s->scsi_len = len;
|
||||
s->scsi_buf = scsi_req_get_buf(req);
|
||||
if (p) {
|
||||
usb_msd_copy_data(s);
|
||||
if (s->packet && s->usb_len == 0) {
|
||||
@ -264,6 +235,56 @@ static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_msd_command_complete(SCSIRequest *req, uint32_t status)
|
||||
{
|
||||
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
|
||||
USBPacket *p = s->packet;
|
||||
|
||||
if (req->tag != s->tag) {
|
||||
fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", req->tag);
|
||||
}
|
||||
DPRINTF("Command complete %d\n", status);
|
||||
s->residue = s->data_len;
|
||||
s->result = status != 0;
|
||||
if (s->packet) {
|
||||
if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) {
|
||||
/* A deferred packet with no write data remaining must be
|
||||
the status read packet. */
|
||||
usb_msd_send_status(s, p);
|
||||
s->mode = USB_MSDM_CBW;
|
||||
} else {
|
||||
if (s->data_len) {
|
||||
s->data_len -= s->usb_len;
|
||||
if (s->mode == USB_MSDM_DATAIN) {
|
||||
memset(s->usb_buf, 0, s->usb_len);
|
||||
}
|
||||
s->usb_len = 0;
|
||||
}
|
||||
if (s->data_len == 0) {
|
||||
s->mode = USB_MSDM_CSW;
|
||||
}
|
||||
}
|
||||
s->packet = NULL;
|
||||
usb_packet_complete(&s->dev, p);
|
||||
} else if (s->data_len == 0) {
|
||||
s->mode = USB_MSDM_CSW;
|
||||
}
|
||||
scsi_req_unref(req);
|
||||
s->req = NULL;
|
||||
}
|
||||
|
||||
static void usb_msd_request_cancelled(SCSIRequest *req)
|
||||
{
|
||||
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
|
||||
|
||||
if (req == s->req) {
|
||||
scsi_req_unref(s->req);
|
||||
s->req = NULL;
|
||||
s->packet = NULL;
|
||||
s->scsi_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_msd_handle_reset(USBDevice *dev)
|
||||
{
|
||||
MSDState *s = (MSDState *)dev;
|
||||
@ -318,9 +339,7 @@ static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
|
||||
static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
MSDState *s = DO_UPCAST(MSDState, dev, dev);
|
||||
s->scsi_dev->info->cancel_io(s->scsi_dev, s->tag);
|
||||
s->packet = NULL;
|
||||
s->scsi_len = 0;
|
||||
scsi_req_cancel(s->req);
|
||||
}
|
||||
|
||||
static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||
@ -367,15 +386,12 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||
s->tag, cbw.flags, cbw.cmd_len, s->data_len);
|
||||
s->residue = 0;
|
||||
s->scsi_len = 0;
|
||||
s->scsi_dev->info->send_command(s->scsi_dev, s->tag, cbw.cmd, 0);
|
||||
s->req = scsi_req_new(s->scsi_dev, s->tag, 0);
|
||||
scsi_req_enqueue(s->req, cbw.cmd);
|
||||
/* ??? Should check that USB and SCSI data transfer
|
||||
directions match. */
|
||||
if (s->residue == 0) {
|
||||
if (s->mode == USB_MSDM_DATAIN) {
|
||||
s->scsi_dev->info->read_data(s->scsi_dev, s->tag);
|
||||
} else if (s->mode == USB_MSDM_DATAOUT) {
|
||||
s->scsi_dev->info->write_data(s->scsi_dev, s->tag);
|
||||
}
|
||||
if (s->mode != USB_MSDM_CSW && s->residue == 0) {
|
||||
scsi_req_continue(s->req);
|
||||
}
|
||||
ret = len;
|
||||
break;
|
||||
@ -486,6 +502,12 @@ static void usb_msd_password_cb(void *opaque, int err)
|
||||
qdev_unplug(&s->dev.qdev);
|
||||
}
|
||||
|
||||
static const struct SCSIBusOps usb_msd_scsi_ops = {
|
||||
.transfer_data = usb_msd_transfer_data,
|
||||
.complete = usb_msd_command_complete,
|
||||
.cancel = usb_msd_request_cancelled
|
||||
};
|
||||
|
||||
static int usb_msd_initfn(USBDevice *dev)
|
||||
{
|
||||
MSDState *s = DO_UPCAST(MSDState, dev, dev);
|
||||
@ -515,7 +537,7 @@ static int usb_msd_initfn(USBDevice *dev)
|
||||
}
|
||||
|
||||
usb_desc_init(dev);
|
||||
scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, usb_msd_command_complete);
|
||||
scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, &usb_msd_scsi_ops);
|
||||
s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable);
|
||||
if (!s->scsi_dev) {
|
||||
return -1;
|
||||
|
@ -205,6 +205,14 @@ disable usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d
|
||||
disable usb_clear_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
|
||||
disable usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
|
||||
|
||||
# hw/scsi-bus.c
|
||||
disable scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d"
|
||||
disable scsi_req_data(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d"
|
||||
disable scsi_req_dequeue(int target, int lun, int tag) "target %d lun %d tag %d"
|
||||
disable scsi_req_continue(int target, int lun, int tag) "target %d lun %d tag %d"
|
||||
disable scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer, uint64_t lba) "target %d lun %d tag %d command %d dir %d length %d lba %"PRIu64""
|
||||
disable scsi_req_parse_bad(int target, int lun, int tag, int cmd) "target %d lun %d tag %d command %d"
|
||||
|
||||
# vl.c
|
||||
disable vm_state_notify(int running, int reason) "running %d reason %d"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user