mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-06 17:23:56 -06:00
SCSI and USB async IO support.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2107 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
4ca9c76f36
commit
4d611c9a2f
14 changed files with 745 additions and 292 deletions
|
@ -152,6 +152,9 @@ do { fprintf(stderr, "lsi_scsi: " fmt , ##args); } while (0)
|
|||
/* The HBA is ID 7, so for simplicitly limit to 7 devices. */
|
||||
#define LSI_MAX_DEVS 7
|
||||
|
||||
/* Size of internal DMA buffer for async IO requests. */
|
||||
#define LSI_DMA_BLOCK_SIZE 0x10000
|
||||
|
||||
typedef struct {
|
||||
PCIDevice pci_dev;
|
||||
int mmio_io_addr;
|
||||
|
@ -162,7 +165,9 @@ typedef struct {
|
|||
int carry; /* ??? Should this be an a visible register somewhere? */
|
||||
int sense;
|
||||
uint8_t msg;
|
||||
/* Nonzero if a Wait Reselect instruction has been issued. */
|
||||
/* 0 if SCRIPTS are running or stopped.
|
||||
* 1 if a Wait Reselect instruction has been issued.
|
||||
* 2 if a DMA operation is in progress. */
|
||||
int waiting;
|
||||
SCSIDevice *scsi_dev[LSI_MAX_DEVS];
|
||||
SCSIDevice *current_dev;
|
||||
|
@ -226,6 +231,7 @@ typedef struct {
|
|||
uint32_t csbc;
|
||||
uint32_t scratch[13]; /* SCRATCHA-SCRATCHR */
|
||||
|
||||
uint8_t dma_buf[LSI_DMA_BLOCK_SIZE];
|
||||
/* Script ram is stored as 32-bit words in host byteorder. */
|
||||
uint32_t script_ram[2048];
|
||||
} LSIState;
|
||||
|
@ -295,6 +301,7 @@ static void lsi_soft_reset(LSIState *s)
|
|||
|
||||
static uint8_t lsi_reg_readb(LSIState *s, int offset);
|
||||
static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val);
|
||||
static void lsi_execute_script(LSIState *s);
|
||||
|
||||
static inline uint32_t read_dword(LSIState *s, uint32_t addr)
|
||||
{
|
||||
|
@ -402,21 +409,20 @@ static void lsi_bad_phase(LSIState *s, int out, int new_phase)
|
|||
lsi_set_phase(s, new_phase);
|
||||
}
|
||||
|
||||
/* Initiate a SCSI layer data transfer. */
|
||||
static void lsi_do_dma(LSIState *s, int out)
|
||||
{
|
||||
uint8_t buf[TARGET_PAGE_SIZE];
|
||||
uint32_t addr;
|
||||
uint32_t count;
|
||||
int n;
|
||||
|
||||
count = s->dbc;
|
||||
addr = s->dnad;
|
||||
DPRINTF("DMA %s addr=0x%08x len=%d avail=%d\n", out ? "out" : "in",
|
||||
if (count > LSI_DMA_BLOCK_SIZE)
|
||||
count = LSI_DMA_BLOCK_SIZE;
|
||||
DPRINTF("DMA addr=0x%08x len=%d avail=%d\n",
|
||||
addr, count, s->data_len);
|
||||
/* ??? Too long transfers are truncated. Don't know if this is the
|
||||
correct behavior. */
|
||||
if (count > s->data_len) {
|
||||
/* If the DMA length is greater then the device data length then
|
||||
/* If the DMA length is greater than the device data length then
|
||||
a phase mismatch will occur. */
|
||||
count = s->data_len;
|
||||
s->dbc = count;
|
||||
|
@ -426,20 +432,47 @@ static void lsi_do_dma(LSIState *s, int out)
|
|||
s->csbc += count;
|
||||
|
||||
/* ??? Set SFBR to first data byte. */
|
||||
while (count) {
|
||||
n = (count > TARGET_PAGE_SIZE) ? TARGET_PAGE_SIZE : count;
|
||||
if (out) {
|
||||
cpu_physical_memory_read(addr, buf, n);
|
||||
scsi_write_data(s->current_dev, buf, n);
|
||||
} else {
|
||||
scsi_read_data(s->current_dev, buf, n);
|
||||
cpu_physical_memory_write(addr, buf, n);
|
||||
}
|
||||
addr += n;
|
||||
count -= n;
|
||||
if ((s->sstat1 & PHASE_MASK) == PHASE_DO) {
|
||||
cpu_physical_memory_read(s->dnad, s->dma_buf, count);
|
||||
scsi_write_data(s->current_dev, s->dma_buf, count);
|
||||
} else {
|
||||
scsi_read_data(s->current_dev, s->dma_buf, count);
|
||||
}
|
||||
/* If the DMA did not complete then suspend execution. */
|
||||
if (s->dbc)
|
||||
s->waiting = 2;
|
||||
}
|
||||
|
||||
/* Callback to indicate that the SCSI layer has completed a transfer. */
|
||||
static void lsi_command_complete(void *opaque, uint32_t reason, int sense)
|
||||
{
|
||||
LSIState *s = (LSIState *)opaque;
|
||||
uint32_t count;
|
||||
int out;
|
||||
|
||||
out = ((s->sstat1 & PHASE_MASK) == PHASE_DO);
|
||||
count = s->dbc;
|
||||
if (count > LSI_DMA_BLOCK_SIZE)
|
||||
count = LSI_DMA_BLOCK_SIZE;
|
||||
if (!out)
|
||||
cpu_physical_memory_write(s->dnad, s->dma_buf, count);
|
||||
s->dnad += count;
|
||||
s->dbc -= count;
|
||||
|
||||
if (reason == SCSI_REASON_DONE) {
|
||||
DPRINTF("Command complete sense=%d\n", sense);
|
||||
s->sense = sense;
|
||||
lsi_set_phase(s, PHASE_ST);
|
||||
}
|
||||
|
||||
if (s->dbc) {
|
||||
lsi_do_dma(s, out);
|
||||
} else if (s->waiting == 2) {
|
||||
/* Restart SCRIPTS execution. */
|
||||
s->waiting = 0;
|
||||
lsi_execute_script(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void lsi_do_command(LSIState *s)
|
||||
{
|
||||
|
@ -461,15 +494,6 @@ static void lsi_do_command(LSIState *s)
|
|||
}
|
||||
}
|
||||
|
||||
static void lsi_command_complete(void *opaque, uint32_t tag, int sense)
|
||||
{
|
||||
LSIState *s = (LSIState *)opaque;
|
||||
|
||||
DPRINTF("Command complete sense=%d\n", sense);
|
||||
s->sense = sense;
|
||||
lsi_set_phase(s, PHASE_ST);
|
||||
}
|
||||
|
||||
static void lsi_do_status(LSIState *s)
|
||||
{
|
||||
DPRINTF("Get status len=%d sense=%d\n", s->dbc, s->sense);
|
||||
|
@ -1134,7 +1158,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
|
|||
s->istat0 &= ~LSI_ISTAT0_INTF;
|
||||
lsi_update_irq(s);
|
||||
}
|
||||
if (s->waiting && val & LSI_ISTAT0_SIGP) {
|
||||
if (s->waiting == 1 && val & LSI_ISTAT0_SIGP) {
|
||||
DPRINTF("Woken by SIGP\n");
|
||||
s->waiting = 0;
|
||||
s->dsp = s->dnad;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue