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
142
hw/esp.c
142
hw/esp.c
@ -62,6 +62,11 @@ struct ESPState {
|
||||
uint8_t cmdbuf[TI_BUFSZ];
|
||||
int cmdlen;
|
||||
int do_cmd;
|
||||
|
||||
uint32_t dma_left;
|
||||
uint8_t async_buf[TARGET_PAGE_SIZE];
|
||||
uint32_t async_ptr;
|
||||
uint32_t async_len;
|
||||
};
|
||||
|
||||
#define STAT_DO 0x00
|
||||
@ -72,6 +77,8 @@ struct ESPState {
|
||||
#define STAT_MO 0x07
|
||||
|
||||
#define STAT_TC 0x10
|
||||
#define STAT_PE 0x20
|
||||
#define STAT_GE 0x40
|
||||
#define STAT_IN 0x80
|
||||
|
||||
#define INTR_FC 0x08
|
||||
@ -195,26 +202,85 @@ static void write_response(ESPState *s)
|
||||
|
||||
}
|
||||
|
||||
static void esp_command_complete(void *opaque, uint32_t tag, int sense)
|
||||
static void esp_do_dma(ESPState *s)
|
||||
{
|
||||
uint32_t dmaptr, minlen, len, from, to;
|
||||
int to_device;
|
||||
dmaptr = iommu_translate(s->espdmaregs[1]);
|
||||
to_device = (s->espdmaregs[0] & DMA_WRITE_MEM) == 0;
|
||||
from = s->espdmaregs[1];
|
||||
minlen = s->dma_left;
|
||||
to = from + minlen;
|
||||
dmaptr = iommu_translate(s->espdmaregs[1]);
|
||||
if ((from & TARGET_PAGE_MASK) != (to & TARGET_PAGE_MASK)) {
|
||||
len = TARGET_PAGE_SIZE - (from & ~TARGET_PAGE_MASK);
|
||||
} else {
|
||||
len = to - from;
|
||||
}
|
||||
DPRINTF("DMA address p %08x v %08x len %08x, from %08x, to %08x\n", dmaptr, s->espdmaregs[1], len, from, to);
|
||||
s->espdmaregs[1] += len;
|
||||
if (s->do_cmd) {
|
||||
s->ti_size -= len;
|
||||
DPRINTF("command len %d + %d\n", s->cmdlen, len);
|
||||
cpu_physical_memory_read(dmaptr, &s->cmdbuf[s->cmdlen], len);
|
||||
s->ti_size = 0;
|
||||
s->cmdlen = 0;
|
||||
s->do_cmd = 0;
|
||||
do_cmd(s, s->cmdbuf);
|
||||
return;
|
||||
} else {
|
||||
s->async_len = len;
|
||||
s->dma_left -= len;
|
||||
if (to_device) {
|
||||
s->async_ptr = -1;
|
||||
cpu_physical_memory_read(dmaptr, s->async_buf, len);
|
||||
scsi_write_data(s->current_dev, s->async_buf, len);
|
||||
} else {
|
||||
s->async_ptr = dmaptr;
|
||||
scsi_read_data(s->current_dev, s->async_buf, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_command_complete(void *opaque, uint32_t reason, int sense)
|
||||
{
|
||||
ESPState *s = (ESPState *)opaque;
|
||||
|
||||
DPRINTF("SCSI Command complete\n");
|
||||
if (s->ti_size != 0)
|
||||
DPRINTF("SCSI command completed unexpectedly\n");
|
||||
s->ti_size = 0;
|
||||
if (sense)
|
||||
DPRINTF("Command failed\n");
|
||||
s->sense = sense;
|
||||
s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
|
||||
s->ti_size -= s->async_len;
|
||||
s->espdmaregs[1] += s->async_len;
|
||||
if (s->async_ptr != (uint32_t)-1) {
|
||||
cpu_physical_memory_write(s->async_ptr, s->async_buf, s->async_len);
|
||||
}
|
||||
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;
|
||||
if (sense)
|
||||
DPRINTF("Command failed\n");
|
||||
s->sense = sense;
|
||||
} else {
|
||||
DPRINTF("transfer %d/%d\n", s->dma_left, s->ti_size);
|
||||
}
|
||||
if (s->dma_left) {
|
||||
esp_do_dma(s);
|
||||
} else {
|
||||
if (s->ti_size) {
|
||||
s->rregs[4] |= STAT_IN | STAT_TC;
|
||||
} else {
|
||||
s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
|
||||
}
|
||||
s->rregs[5] = INTR_BS;
|
||||
s->rregs[6] = 0;
|
||||
s->rregs[7] = 0;
|
||||
s->espdmaregs[0] |= DMA_INTR;
|
||||
pic_set_irq(s->irq, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_ti(ESPState *s)
|
||||
{
|
||||
uint32_t dmaptr, dmalen, minlen, len, from, to;
|
||||
unsigned int i;
|
||||
int to_device;
|
||||
uint8_t buf[TARGET_PAGE_SIZE];
|
||||
uint32_t dmalen, minlen;
|
||||
|
||||
dmalen = s->wregs[0] | (s->wregs[1] << 8);
|
||||
if (dmalen==0) {
|
||||
@ -227,47 +293,9 @@ static void handle_ti(ESPState *s)
|
||||
minlen = (dmalen < s->ti_size) ? dmalen : s->ti_size;
|
||||
DPRINTF("Transfer Information len %d\n", minlen);
|
||||
if (s->dma) {
|
||||
dmaptr = iommu_translate(s->espdmaregs[1]);
|
||||
/* Check if the transfer writes to to reads from the device. */
|
||||
to_device = (s->espdmaregs[0] & DMA_WRITE_MEM) == 0;
|
||||
DPRINTF("DMA Direction: %c, addr 0x%8.8x %08x\n",
|
||||
to_device ? 'r': 'w', dmaptr, s->ti_size);
|
||||
from = s->espdmaregs[1];
|
||||
to = from + minlen;
|
||||
for (i = 0; i < minlen; i += len, from += len) {
|
||||
dmaptr = iommu_translate(s->espdmaregs[1] + i);
|
||||
if ((from & TARGET_PAGE_MASK) != (to & TARGET_PAGE_MASK)) {
|
||||
len = TARGET_PAGE_SIZE - (from & ~TARGET_PAGE_MASK);
|
||||
} else {
|
||||
len = to - from;
|
||||
}
|
||||
DPRINTF("DMA address p %08x v %08x len %08x, from %08x, to %08x\n", dmaptr, s->espdmaregs[1] + i, len, from, to);
|
||||
s->ti_size -= len;
|
||||
if (s->do_cmd) {
|
||||
DPRINTF("command len %d + %d\n", s->cmdlen, len);
|
||||
cpu_physical_memory_read(dmaptr, &s->cmdbuf[s->cmdlen], len);
|
||||
s->ti_size = 0;
|
||||
s->cmdlen = 0;
|
||||
s->do_cmd = 0;
|
||||
do_cmd(s, s->cmdbuf);
|
||||
return;
|
||||
} else {
|
||||
if (to_device) {
|
||||
cpu_physical_memory_read(dmaptr, buf, len);
|
||||
scsi_write_data(s->current_dev, buf, len);
|
||||
} else {
|
||||
scsi_read_data(s->current_dev, buf, len);
|
||||
cpu_physical_memory_write(dmaptr, buf, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s->ti_size) {
|
||||
s->rregs[4] = STAT_IN | STAT_TC | (to_device ? STAT_DO : STAT_DI);
|
||||
}
|
||||
s->rregs[5] = INTR_BS;
|
||||
s->rregs[6] = 0;
|
||||
s->rregs[7] = 0;
|
||||
s->espdmaregs[0] |= DMA_INTR;
|
||||
s->dma_left = minlen;
|
||||
s->rregs[4] &= ~STAT_TC;
|
||||
esp_do_dma(s);
|
||||
} else if (s->do_cmd) {
|
||||
DPRINTF("command len %d\n", s->cmdlen);
|
||||
s->ti_size = 0;
|
||||
@ -276,7 +304,6 @@ static void handle_ti(ESPState *s)
|
||||
do_cmd(s, s->cmdbuf);
|
||||
return;
|
||||
}
|
||||
pic_set_irq(s->irq, 1);
|
||||
}
|
||||
|
||||
static void esp_reset(void *opaque)
|
||||
@ -320,8 +347,8 @@ static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr)
|
||||
break;
|
||||
case 5:
|
||||
// interrupt
|
||||
// Clear status bits except TC
|
||||
s->rregs[4] &= STAT_TC;
|
||||
// Clear interrupt/error status bits
|
||||
s->rregs[4] &= ~(STAT_IN | STAT_GE | STAT_PE);
|
||||
pic_set_irq(s->irq, 0);
|
||||
s->espdmaregs[0] &= ~DMA_INTR;
|
||||
break;
|
||||
@ -342,6 +369,7 @@ static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
|
||||
case 0:
|
||||
case 1:
|
||||
s->rregs[saddr] = val;
|
||||
s->rregs[4] &= ~STAT_TC;
|
||||
break;
|
||||
case 2:
|
||||
// FIFO
|
||||
|
@ -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;
|
||||
|
152
hw/scsi-disk.c
152
hw/scsi-disk.c
@ -25,6 +25,7 @@ do { fprintf(stderr, "scsi-disk: " fmt , ##args); } while (0)
|
||||
|
||||
#define SENSE_NO_SENSE 0
|
||||
#define SENSE_NOT_READY 2
|
||||
#define SENSE_HARDWARE_ERROR 4
|
||||
#define SENSE_ILLEGAL_REQUEST 5
|
||||
|
||||
struct SCSIDevice
|
||||
@ -46,7 +47,13 @@ struct SCSIDevice
|
||||
int buf_pos;
|
||||
int buf_len;
|
||||
int sense;
|
||||
BlockDriverAIOCB *aiocb;
|
||||
/* Data still to be transfered after this request completes. */
|
||||
uint8_t *aiodata;
|
||||
uint32_t aiolen;
|
||||
char buf[512];
|
||||
/* Completion functions may be called from either scsi_{read,write}_data
|
||||
or from the AIO completion routines. */
|
||||
scsi_completionfn completion;
|
||||
void *opaque;
|
||||
};
|
||||
@ -54,10 +61,49 @@ struct SCSIDevice
|
||||
static void scsi_command_complete(SCSIDevice *s, int sense)
|
||||
{
|
||||
s->sense = sense;
|
||||
s->completion(s->opaque, s->tag, sense);
|
||||
s->completion(s->opaque, SCSI_REASON_DONE, sense);
|
||||
}
|
||||
|
||||
/* Read data from a scsi device. Returns nonzero on failure. */
|
||||
static void scsi_transfer_complete(SCSIDevice *s)
|
||||
{
|
||||
s->completion(s->opaque, SCSI_REASON_DATA, 0);
|
||||
s->aiocb = NULL;
|
||||
}
|
||||
|
||||
static void scsi_read_complete(void * opaque, int ret)
|
||||
{
|
||||
SCSIDevice *s = (SCSIDevice *)opaque;
|
||||
|
||||
if (ret) {
|
||||
DPRINTF("IO error\n");
|
||||
scsi_command_complete(s, SENSE_HARDWARE_ERROR);
|
||||
}
|
||||
|
||||
if (s->aiolen) {
|
||||
/* Read the remaining data. Full and partial sectors are transferred
|
||||
separately. */
|
||||
scsi_read_data(s, s->aiodata, s->aiolen);
|
||||
} else {
|
||||
if (s->buf_len == 0 && s->sector_count == 0)
|
||||
scsi_command_complete(s, SENSE_NO_SENSE);
|
||||
else
|
||||
scsi_transfer_complete(s);
|
||||
}
|
||||
}
|
||||
|
||||
/* Cancel a pending data transfer. */
|
||||
void scsi_cancel_io(SCSIDevice *s)
|
||||
{
|
||||
if (!s->aiocb) {
|
||||
BADF("Cancel with no pending IO\n");
|
||||
return;
|
||||
}
|
||||
bdrv_aio_cancel(s->aiocb);
|
||||
s->aiocb = NULL;
|
||||
}
|
||||
|
||||
/* Read data from a scsi device. Returns nonzero on failure.
|
||||
The transfer may complete asynchronously. */
|
||||
int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len)
|
||||
{
|
||||
uint32_t n;
|
||||
@ -84,14 +130,19 @@ int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len)
|
||||
n = s->sector_count;
|
||||
|
||||
if (n != 0) {
|
||||
bdrv_read(s->bdrv, s->sector, data, n);
|
||||
data += n * 512;
|
||||
len -= n * 512;
|
||||
s->aiolen = len - n * 512;
|
||||
s->aiodata = data + n * 512;
|
||||
s->aiocb = bdrv_aio_read(s->bdrv, s->sector, data, n,
|
||||
scsi_read_complete, s);
|
||||
if (s->aiocb == NULL)
|
||||
scsi_command_complete(s, SENSE_HARDWARE_ERROR);
|
||||
s->sector += n;
|
||||
s->sector_count -= n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len && s->sector_count) {
|
||||
/* TODO: Make this use AIO. */
|
||||
bdrv_read(s->bdrv, s->sector, s->buf, 1);
|
||||
s->sector++;
|
||||
s->sector_count--;
|
||||
@ -106,11 +157,53 @@ int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len)
|
||||
|
||||
if (s->buf_len == 0 && s->sector_count == 0)
|
||||
scsi_command_complete(s, SENSE_NO_SENSE);
|
||||
else
|
||||
scsi_transfer_complete(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read data to a scsi device. Returns nonzero on failure. */
|
||||
static void scsi_write_complete(void * opaque, int ret)
|
||||
{
|
||||
SCSIDevice *s = (SCSIDevice *)opaque;
|
||||
|
||||
if (ret) {
|
||||
fprintf(stderr, "scsi-disc: IO write error\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (s->sector_count == 0)
|
||||
scsi_command_complete(s, SENSE_NO_SENSE);
|
||||
else
|
||||
scsi_transfer_complete(s);
|
||||
}
|
||||
|
||||
static uint32_t scsi_write_partial_sector(SCSIDevice *s, uint8_t *data,
|
||||
uint32_t len)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = 512 - s->buf_len;
|
||||
if (n > len)
|
||||
n = len;
|
||||
|
||||
memcpy(s->buf + s->buf_len, data, n);
|
||||
data += n;
|
||||
s->buf_len += n;
|
||||
len -= n;
|
||||
if (s->buf_len == 512) {
|
||||
/* A full sector has been accumulated. Write it to disk. */
|
||||
/* TODO: Make this use async IO. */
|
||||
bdrv_write(s->bdrv, s->sector, s->buf, 1);
|
||||
s->buf_len = 0;
|
||||
s->sector++;
|
||||
s->sector_count--;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Write data to a scsi device. Returns nonzero on failure.
|
||||
The transfer may complete asynchronously. */
|
||||
int scsi_write_data(SCSIDevice *s, uint8_t *data, uint32_t len)
|
||||
{
|
||||
uint32_t n;
|
||||
@ -125,48 +218,39 @@ int scsi_write_data(SCSIDevice *s, uint8_t *data, uint32_t len)
|
||||
return 1;
|
||||
|
||||
if (s->buf_len != 0 || len < 512) {
|
||||
n = 512 - s->buf_len;
|
||||
if (n > len)
|
||||
n = len;
|
||||
|
||||
memcpy(s->buf + s->buf_len, data, n);
|
||||
data += n;
|
||||
s->buf_len += n;
|
||||
n = scsi_write_partial_sector(s, data, len);
|
||||
len -= n;
|
||||
if (s->buf_len == 512) {
|
||||
/* A full sector has been accumulated. Write it to disk. */
|
||||
bdrv_write(s->bdrv, s->sector, s->buf, 1);
|
||||
s->buf_len = 0;
|
||||
s->sector++;
|
||||
s->sector_count--;
|
||||
}
|
||||
data += n;
|
||||
}
|
||||
|
||||
n = len / 512;
|
||||
if (n > s->sector_count)
|
||||
n = s->sector_count;
|
||||
return 1;
|
||||
|
||||
if (n != 0) {
|
||||
bdrv_write(s->bdrv, s->sector, data, n);
|
||||
s->aiocb = bdrv_aio_write(s->bdrv, s->sector, data, n,
|
||||
scsi_write_complete, s);
|
||||
if (s->aiocb == NULL)
|
||||
scsi_command_complete(s, SENSE_HARDWARE_ERROR);
|
||||
data += n * 512;
|
||||
len -= n * 512;
|
||||
s->sector += n;
|
||||
s->sector_count -= n;
|
||||
}
|
||||
|
||||
if (len >= 512)
|
||||
return 1;
|
||||
|
||||
if (len && s->sector_count) {
|
||||
/* Recurse to complete the partial write. */
|
||||
return scsi_write_data(s, data, len);
|
||||
if (len) {
|
||||
if (s->sector_count == 0)
|
||||
return 1;
|
||||
/* Complete a partial write. */
|
||||
scsi_write_partial_sector(s, data, len);
|
||||
}
|
||||
if (n == 0) {
|
||||
/* Transfer completes immediately. */
|
||||
if (s->sector_count == 0)
|
||||
scsi_command_complete(s, SENSE_NO_SENSE);
|
||||
else
|
||||
scsi_transfer_complete(s);
|
||||
}
|
||||
|
||||
if (len != 0)
|
||||
return 1;
|
||||
|
||||
if (s->sector_count == 0)
|
||||
scsi_command_complete(s, SENSE_NO_SENSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
11
hw/usb-hid.c
11
hw/usb-hid.c
@ -474,19 +474,18 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int usb_mouse_handle_data(USBDevice *dev, int pid,
|
||||
uint8_t devep, uint8_t *data, int len)
|
||||
static int usb_mouse_handle_data(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
USBMouseState *s = (USBMouseState *)dev;
|
||||
int ret = 0;
|
||||
|
||||
switch(pid) {
|
||||
switch(p->pid) {
|
||||
case USB_TOKEN_IN:
|
||||
if (devep == 1) {
|
||||
if (p->devep == 1) {
|
||||
if (s->kind == USB_MOUSE)
|
||||
ret = usb_mouse_poll(s, data, len);
|
||||
ret = usb_mouse_poll(s, p->data, p->len);
|
||||
else if (s->kind == USB_TABLET)
|
||||
ret = usb_tablet_poll(s, data, len);
|
||||
ret = usb_tablet_poll(s, p->data, p->len);
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
|
46
hw/usb-hub.c
46
hw/usb-hub.c
@ -180,8 +180,7 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev)
|
||||
port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
|
||||
port->port.dev = dev;
|
||||
/* send the attach message */
|
||||
dev->handle_packet(dev,
|
||||
USB_MSG_ATTACH, 0, 0, NULL, 0);
|
||||
usb_send_msg(dev, USB_MSG_ATTACH);
|
||||
} else {
|
||||
dev = port->port.dev;
|
||||
if (dev) {
|
||||
@ -192,8 +191,7 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev)
|
||||
port->wPortChange |= PORT_STAT_C_ENABLE;
|
||||
}
|
||||
/* send the detach message */
|
||||
dev->handle_packet(dev,
|
||||
USB_MSG_DETACH, 0, 0, NULL, 0);
|
||||
usb_send_msg(dev, USB_MSG_DETACH);
|
||||
port->port.dev = NULL;
|
||||
}
|
||||
}
|
||||
@ -349,8 +347,7 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
|
||||
break;
|
||||
case PORT_RESET:
|
||||
if (dev) {
|
||||
dev->handle_packet(dev,
|
||||
USB_MSG_RESET, 0, 0, NULL, 0);
|
||||
usb_send_msg(dev, USB_MSG_RESET);
|
||||
port->wPortChange |= PORT_STAT_C_RESET;
|
||||
/* set enable bit */
|
||||
port->wPortStatus |= PORT_STAT_ENABLE;
|
||||
@ -434,22 +431,21 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int usb_hub_handle_data(USBDevice *dev, int pid,
|
||||
uint8_t devep, uint8_t *data, int len)
|
||||
static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
USBHubState *s = (USBHubState *)dev;
|
||||
int ret;
|
||||
|
||||
switch(pid) {
|
||||
switch(p->pid) {
|
||||
case USB_TOKEN_IN:
|
||||
if (devep == 1) {
|
||||
if (p->devep == 1) {
|
||||
USBHubPort *port;
|
||||
unsigned int status;
|
||||
int i, n;
|
||||
n = (s->nb_ports + 1 + 7) / 8;
|
||||
if (len == 1) { /* FreeBSD workaround */
|
||||
if (p->len == 1) { /* FreeBSD workaround */
|
||||
n = 1;
|
||||
} else if (n > len) {
|
||||
} else if (n > p->len) {
|
||||
return USB_RET_BABBLE;
|
||||
}
|
||||
status = 0;
|
||||
@ -460,7 +456,7 @@ static int usb_hub_handle_data(USBDevice *dev, int pid,
|
||||
}
|
||||
if (status != 0) {
|
||||
for(i = 0; i < n; i++) {
|
||||
data[i] = status >> (8 * i);
|
||||
p->data[i] = status >> (8 * i);
|
||||
}
|
||||
ret = n;
|
||||
} else {
|
||||
@ -479,9 +475,7 @@ static int usb_hub_handle_data(USBDevice *dev, int pid,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int usb_hub_broadcast_packet(USBHubState *s, int pid,
|
||||
uint8_t devaddr, uint8_t devep,
|
||||
uint8_t *data, int len)
|
||||
static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p)
|
||||
{
|
||||
USBHubPort *port;
|
||||
USBDevice *dev;
|
||||
@ -491,9 +485,7 @@ static int usb_hub_broadcast_packet(USBHubState *s, int pid,
|
||||
port = &s->ports[i];
|
||||
dev = port->port.dev;
|
||||
if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
|
||||
ret = dev->handle_packet(dev, pid,
|
||||
devaddr, devep,
|
||||
data, len);
|
||||
ret = dev->handle_packet(dev, p);
|
||||
if (ret != USB_RET_NODEV) {
|
||||
return ret;
|
||||
}
|
||||
@ -502,9 +494,7 @@ static int usb_hub_broadcast_packet(USBHubState *s, int pid,
|
||||
return USB_RET_NODEV;
|
||||
}
|
||||
|
||||
static int usb_hub_handle_packet(USBDevice *dev, int pid,
|
||||
uint8_t devaddr, uint8_t devep,
|
||||
uint8_t *data, int len)
|
||||
static int usb_hub_handle_packet(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
USBHubState *s = (USBHubState *)dev;
|
||||
|
||||
@ -513,14 +503,14 @@ static int usb_hub_handle_packet(USBDevice *dev, int pid,
|
||||
#endif
|
||||
if (dev->state == USB_STATE_DEFAULT &&
|
||||
dev->addr != 0 &&
|
||||
devaddr != dev->addr &&
|
||||
(pid == USB_TOKEN_SETUP ||
|
||||
pid == USB_TOKEN_OUT ||
|
||||
pid == USB_TOKEN_IN)) {
|
||||
p->devaddr != dev->addr &&
|
||||
(p->pid == USB_TOKEN_SETUP ||
|
||||
p->pid == USB_TOKEN_OUT ||
|
||||
p->pid == USB_TOKEN_IN)) {
|
||||
/* broadcast the packet to the devices */
|
||||
return usb_hub_broadcast_packet(s, pid, devaddr, devep, data, len);
|
||||
return usb_hub_broadcast_packet(s, p);
|
||||
}
|
||||
return usb_generic_handle_packet(dev, pid, devaddr, devep, data, len);
|
||||
return usb_generic_handle_packet(dev, p);
|
||||
}
|
||||
|
||||
static void usb_hub_handle_destroy(USBDevice *dev)
|
||||
|
66
hw/usb-msd.c
66
hw/usb-msd.c
@ -33,9 +33,12 @@ typedef struct {
|
||||
USBDevice dev;
|
||||
enum USBMSDMode mode;
|
||||
uint32_t data_len;
|
||||
uint32_t transfer_len;
|
||||
uint32_t tag;
|
||||
SCSIDevice *scsi_dev;
|
||||
int result;
|
||||
/* For async completion. */
|
||||
USBPacket *packet;
|
||||
} MSDState;
|
||||
|
||||
static const uint8_t qemu_msd_dev_descriptor[] = {
|
||||
@ -103,13 +106,27 @@ static const uint8_t qemu_msd_config_descriptor[] = {
|
||||
0x00 /* u8 ep_bInterval; */
|
||||
};
|
||||
|
||||
static void usb_msd_command_complete(void *opaque, uint32_t tag, int fail)
|
||||
static void usb_msd_command_complete(void *opaque, uint32_t reason, int fail)
|
||||
{
|
||||
MSDState *s = (MSDState *)opaque;
|
||||
USBPacket *p;
|
||||
|
||||
DPRINTF("Command complete\n");
|
||||
s->result = fail;
|
||||
s->mode = USB_MSDM_CSW;
|
||||
s->data_len -= s->transfer_len;
|
||||
s->transfer_len = 0;
|
||||
if (reason == SCSI_REASON_DONE) {
|
||||
DPRINTF("Command complete %d\n", fail);
|
||||
s->result = fail;
|
||||
s->mode = USB_MSDM_CSW;
|
||||
}
|
||||
if (s->packet) {
|
||||
/* Set s->packet to NULL before calling usb_packet_complete because
|
||||
annother request may be issues before usb_packet_complete returns.
|
||||
*/
|
||||
DPRINTF("Packet complete %p\n", p);
|
||||
p = s->packet;
|
||||
s->packet = NULL;
|
||||
usb_packet_complete(p);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_msd_handle_reset(USBDevice *dev)
|
||||
@ -250,15 +267,24 @@ struct usb_msd_csw {
|
||||
uint8_t status;
|
||||
};
|
||||
|
||||
static int usb_msd_handle_data(USBDevice *dev, int pid, uint8_t devep,
|
||||
uint8_t *data, int len)
|
||||
static void usb_msd_cancel_io(USBPacket *p, void *opaque)
|
||||
{
|
||||
MSDState *s = opaque;
|
||||
scsi_cancel_io(s->scsi_dev);
|
||||
s->packet = NULL;
|
||||
}
|
||||
|
||||
static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
MSDState *s = (MSDState *)dev;
|
||||
int ret = 0;
|
||||
struct usb_msd_cbw cbw;
|
||||
struct usb_msd_csw csw;
|
||||
uint8_t devep = p->devep;
|
||||
uint8_t *data = p->data;
|
||||
int len = p->len;
|
||||
|
||||
switch (pid) {
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
if (devep != 2)
|
||||
goto fail;
|
||||
@ -300,13 +326,18 @@ static int usb_msd_handle_data(USBDevice *dev, int pid, uint8_t devep,
|
||||
if (len > s->data_len)
|
||||
goto fail;
|
||||
|
||||
s->transfer_len = len;
|
||||
if (scsi_write_data(s->scsi_dev, data, len))
|
||||
goto fail;
|
||||
|
||||
s->data_len -= len;
|
||||
if (s->data_len == 0)
|
||||
s->mode = USB_MSDM_CSW;
|
||||
ret = len;
|
||||
if (s->transfer_len == 0) {
|
||||
ret = len;
|
||||
} else {
|
||||
DPRINTF("Deferring packet %p\n", p);
|
||||
usb_defer_packet(p, usb_msd_cancel_io, s);
|
||||
s->packet = p;
|
||||
ret = USB_RET_ASYNC;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -340,13 +371,18 @@ static int usb_msd_handle_data(USBDevice *dev, int pid, uint8_t devep,
|
||||
if (len > s->data_len)
|
||||
len = s->data_len;
|
||||
|
||||
s->transfer_len = len;
|
||||
if (scsi_read_data(s->scsi_dev, data, len))
|
||||
goto fail;
|
||||
|
||||
s->data_len -= len;
|
||||
if (s->data_len == 0)
|
||||
s->mode = USB_MSDM_CSW;
|
||||
ret = len;
|
||||
if (s->transfer_len == 0) {
|
||||
ret = len;
|
||||
} else {
|
||||
DPRINTF("Deferring packet %p\n", p);
|
||||
usb_defer_packet(p, usb_msd_cancel_io, s);
|
||||
s->packet = p;
|
||||
ret = USB_RET_ASYNC;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
156
hw/usb-ohci.c
156
hw/usb-ohci.c
@ -89,6 +89,14 @@ typedef struct {
|
||||
uint32_t rhdesc_a, rhdesc_b;
|
||||
uint32_t rhstatus;
|
||||
OHCIPort rhport[OHCI_MAX_PORTS];
|
||||
|
||||
/* Active packets. */
|
||||
uint32_t old_ctl;
|
||||
USBPacket usb_packet;
|
||||
uint8_t usb_buf[8192];
|
||||
uint32_t async_td;
|
||||
int async_complete;
|
||||
|
||||
} OHCIState;
|
||||
|
||||
/* Host Controller Communications Area */
|
||||
@ -288,8 +296,7 @@ static void ohci_attach(USBPort *port1, USBDevice *dev)
|
||||
port->ctrl &= ~OHCI_PORT_LSDA;
|
||||
port->port.dev = dev;
|
||||
/* send the attach message */
|
||||
dev->handle_packet(dev,
|
||||
USB_MSG_ATTACH, 0, 0, NULL, 0);
|
||||
usb_send_msg(dev, USB_MSG_ATTACH);
|
||||
dprintf("usb-ohci: Attached port %d\n", port1->index);
|
||||
} else {
|
||||
/* set connect status */
|
||||
@ -305,8 +312,7 @@ static void ohci_attach(USBPort *port1, USBDevice *dev)
|
||||
dev = port->port.dev;
|
||||
if (dev) {
|
||||
/* send the detach message */
|
||||
dev->handle_packet(dev,
|
||||
USB_MSG_DETACH, 0, 0, NULL, 0);
|
||||
usb_send_msg(dev, USB_MSG_DETACH);
|
||||
}
|
||||
port->port.dev = NULL;
|
||||
dprintf("usb-ohci: Detached port %d\n", port1->index);
|
||||
@ -323,6 +329,7 @@ static void ohci_reset(OHCIState *ohci)
|
||||
int i;
|
||||
|
||||
ohci->ctl = 0;
|
||||
ohci->old_ctl = 0;
|
||||
ohci->status = 0;
|
||||
ohci->intr_status = 0;
|
||||
ohci->intr = OHCI_INTR_MIE;
|
||||
@ -356,6 +363,10 @@ static void ohci_reset(OHCIState *ohci)
|
||||
if (port->port.dev)
|
||||
ohci_attach(&port->port, port->port.dev);
|
||||
}
|
||||
if (ohci->async_td) {
|
||||
usb_cancel_packet(&ohci->usb_packet);
|
||||
ohci->async_td = 0;
|
||||
}
|
||||
dprintf("usb-ohci: Reset %s\n", ohci->pci_dev.name);
|
||||
}
|
||||
|
||||
@ -423,6 +434,18 @@ static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write)
|
||||
cpu_physical_memory_rw(ptr, buf, len - n, write);
|
||||
}
|
||||
|
||||
static void ohci_process_lists(OHCIState *ohci);
|
||||
|
||||
static void ohci_async_complete_packet(USBPacket * packet, void *opaque)
|
||||
{
|
||||
OHCIState *ohci = opaque;
|
||||
#ifdef DEBUG_PACKET
|
||||
dprintf("Async packet complete\n");
|
||||
#endif
|
||||
ohci->async_complete = 1;
|
||||
ohci_process_lists(ohci);
|
||||
}
|
||||
|
||||
/* Service a transport descriptor.
|
||||
Returns nonzero to terminate processing of this endpoint. */
|
||||
|
||||
@ -430,7 +453,6 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
||||
{
|
||||
int dir;
|
||||
size_t len = 0;
|
||||
uint8_t buf[8192];
|
||||
char *str = NULL;
|
||||
int pid;
|
||||
int ret;
|
||||
@ -439,8 +461,17 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
||||
struct ohci_td td;
|
||||
uint32_t addr;
|
||||
int flag_r;
|
||||
int completion;
|
||||
|
||||
addr = ed->head & OHCI_DPTR_MASK;
|
||||
/* See if this TD has already been submitted to the device. */
|
||||
completion = (addr == ohci->async_td);
|
||||
if (completion && !ohci->async_complete) {
|
||||
#ifdef DEBUG_PACKET
|
||||
dprintf("Skipping async TD\n");
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
if (!ohci_read_td(addr, &td)) {
|
||||
fprintf(stderr, "usb-ohci: TD read error at %x\n", addr);
|
||||
return 0;
|
||||
@ -481,8 +512,8 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
||||
len = (td.be - td.cbp) + 1;
|
||||
}
|
||||
|
||||
if (len && dir != OHCI_TD_DIR_IN) {
|
||||
ohci_copy_td(&td, buf, len, 0);
|
||||
if (len && dir != OHCI_TD_DIR_IN && !completion) {
|
||||
ohci_copy_td(&td, ohci->usb_buf, len, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -494,31 +525,58 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
||||
if (len >= 0 && dir != OHCI_TD_DIR_IN) {
|
||||
dprintf(" data:");
|
||||
for (i = 0; i < len; i++)
|
||||
printf(" %.2x", buf[i]);
|
||||
printf(" %.2x", ohci->usb_buf[i]);
|
||||
dprintf("\n");
|
||||
}
|
||||
#endif
|
||||
ret = USB_RET_NODEV;
|
||||
for (i = 0; i < ohci->num_ports; i++) {
|
||||
dev = ohci->rhport[i].port.dev;
|
||||
if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0)
|
||||
continue;
|
||||
if (completion) {
|
||||
ret = ohci->usb_packet.len;
|
||||
ohci->async_td = 0;
|
||||
ohci->async_complete = 0;
|
||||
} else {
|
||||
ret = USB_RET_NODEV;
|
||||
for (i = 0; i < ohci->num_ports; i++) {
|
||||
dev = ohci->rhport[i].port.dev;
|
||||
if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0)
|
||||
continue;
|
||||
|
||||
ret = dev->handle_packet(dev, pid, OHCI_BM(ed->flags, ED_FA),
|
||||
OHCI_BM(ed->flags, ED_EN), buf, len);
|
||||
if (ret != USB_RET_NODEV)
|
||||
break;
|
||||
}
|
||||
if (ohci->async_td) {
|
||||
/* ??? The hardware should allow one active packet per
|
||||
endpoint. We only allow one active packet per controller.
|
||||
This should be sufficient as long as devices respond in a
|
||||
timely manner.
|
||||
*/
|
||||
#ifdef DEBUG_PACKET
|
||||
dprintf("ret=%d\n", ret);
|
||||
dprintf("Too many pending packets\n");
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
ohci->usb_packet.pid = pid;
|
||||
ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA);
|
||||
ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
|
||||
ohci->usb_packet.data = ohci->usb_buf;
|
||||
ohci->usb_packet.len = len;
|
||||
ohci->usb_packet.complete_cb = ohci_async_complete_packet;
|
||||
ohci->usb_packet.complete_opaque = ohci;
|
||||
ret = dev->handle_packet(dev, &ohci->usb_packet);
|
||||
if (ret != USB_RET_NODEV)
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG_PACKET
|
||||
dprintf("ret=%d\n", ret);
|
||||
#endif
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
ohci->async_td = addr;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (ret >= 0) {
|
||||
if (dir == OHCI_TD_DIR_IN) {
|
||||
ohci_copy_td(&td, buf, ret, 1);
|
||||
ohci_copy_td(&td, ohci->usb_buf, ret, 1);
|
||||
#ifdef DEBUG_PACKET
|
||||
dprintf(" data:");
|
||||
for (i = 0; i < ret; i++)
|
||||
printf(" %.2x", buf[i]);
|
||||
printf(" %.2x", ohci->usb_buf[i]);
|
||||
dprintf("\n");
|
||||
#endif
|
||||
} else {
|
||||
@ -608,8 +666,16 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head)
|
||||
|
||||
next_ed = ed.next & OHCI_DPTR_MASK;
|
||||
|
||||
if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K))
|
||||
if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) {
|
||||
uint32_t addr;
|
||||
/* Cancel pending packets for ED that have been paused. */
|
||||
addr = ed.head & OHCI_DPTR_MASK;
|
||||
if (ohci->async_td && addr == ohci->async_td) {
|
||||
usb_cancel_packet(&ohci->usb_packet);
|
||||
ohci->async_td = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Skip isochronous endpoints. */
|
||||
if (ed.flags & OHCI_ED_F)
|
||||
@ -646,21 +712,9 @@ static void ohci_sof(OHCIState *ohci)
|
||||
ohci_set_interrupt(ohci, OHCI_INTR_SF);
|
||||
}
|
||||
|
||||
/* Do frame processing on frame boundary */
|
||||
static void ohci_frame_boundary(void *opaque)
|
||||
/* Process Control and Bulk lists. */
|
||||
static void ohci_process_lists(OHCIState *ohci)
|
||||
{
|
||||
OHCIState *ohci = opaque;
|
||||
struct ohci_hcca hcca;
|
||||
|
||||
cpu_physical_memory_rw(ohci->hcca, (uint8_t *)&hcca, sizeof(hcca), 0);
|
||||
|
||||
/* Process all the lists at the end of the frame */
|
||||
if (ohci->ctl & OHCI_CTL_PLE) {
|
||||
int n;
|
||||
|
||||
n = ohci->frame_number & 0x1f;
|
||||
ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]));
|
||||
}
|
||||
if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) {
|
||||
if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head)
|
||||
dprintf("usb-ohci: head %x, cur %x\n", ohci->ctrl_head, ohci->ctrl_cur);
|
||||
@ -676,6 +730,32 @@ static void ohci_frame_boundary(void *opaque)
|
||||
ohci->status &= ~OHCI_STATUS_BLF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Do frame processing on frame boundary */
|
||||
static void ohci_frame_boundary(void *opaque)
|
||||
{
|
||||
OHCIState *ohci = opaque;
|
||||
struct ohci_hcca hcca;
|
||||
|
||||
cpu_physical_memory_rw(ohci->hcca, (uint8_t *)&hcca, sizeof(hcca), 0);
|
||||
|
||||
/* Process all the lists at the end of the frame */
|
||||
if (ohci->ctl & OHCI_CTL_PLE) {
|
||||
int n;
|
||||
|
||||
n = ohci->frame_number & 0x1f;
|
||||
ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]));
|
||||
}
|
||||
|
||||
/* Cancel all pending packets if either of the lists has been disabled. */
|
||||
if (ohci->async_td &&
|
||||
ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) {
|
||||
usb_cancel_packet(&ohci->usb_packet);
|
||||
ohci->async_td = 0;
|
||||
}
|
||||
ohci->old_ctl = ohci->ctl;
|
||||
ohci_process_lists(ohci);
|
||||
|
||||
/* Frame boundary, so do EOF stuf here */
|
||||
ohci->frt = ohci->fit;
|
||||
@ -907,8 +987,7 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val)
|
||||
|
||||
if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) {
|
||||
dprintf("usb-ohci: port %d: RESET\n", portnum);
|
||||
port->port.dev->handle_packet(port->port.dev, USB_MSG_RESET,
|
||||
0, 0, NULL, 0);
|
||||
usb_send_msg(port->port.dev, USB_MSG_RESET);
|
||||
port->ctrl &= ~OHCI_PORT_PRS;
|
||||
/* ??? Should this also set OHCI_PORT_PESC. */
|
||||
port->ctrl |= OHCI_PORT_PES | OHCI_PORT_PRSC;
|
||||
@ -1186,5 +1265,6 @@ void usb_ohci_init(struct PCIBus *bus, int num_ports, int devfn)
|
||||
qemu_register_usb_port(&ohci->rhport[i].port, ohci, i, ohci_attach);
|
||||
}
|
||||
|
||||
ohci->async_td = 0;
|
||||
ohci_reset(ohci);
|
||||
}
|
||||
|
244
hw/usb-uhci.c
244
hw/usb-uhci.c
@ -76,6 +76,18 @@ typedef struct UHCIState {
|
||||
uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */
|
||||
QEMUTimer *frame_timer;
|
||||
UHCIPort ports[NB_PORTS];
|
||||
|
||||
/* Interrupts that should be raised at the end of the current frame. */
|
||||
uint32_t pending_int_mask;
|
||||
/* For simplicity of implementation we only allow a single pending USB
|
||||
request. This means all usb traffic on this controller is effectively
|
||||
suspended until that transfer completes. When the transfer completes
|
||||
the next transfer from that queue will be processed. However
|
||||
other queues will not be processed until the next frame. The solution
|
||||
is to allow multiple pending requests. */
|
||||
uint32_t async_qh;
|
||||
USBPacket usb_packet;
|
||||
uint8_t usb_buf[1280];
|
||||
} UHCIState;
|
||||
|
||||
typedef struct UHCI_TD {
|
||||
@ -188,8 +200,7 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
|
||||
port = &s->ports[i];
|
||||
dev = port->port.dev;
|
||||
if (dev) {
|
||||
dev->handle_packet(dev,
|
||||
USB_MSG_RESET, 0, 0, NULL, 0);
|
||||
usb_send_msg(dev, USB_MSG_RESET);
|
||||
}
|
||||
}
|
||||
uhci_reset(s);
|
||||
@ -232,8 +243,7 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
|
||||
/* port reset */
|
||||
if ( (val & UHCI_PORT_RESET) &&
|
||||
!(port->ctrl & UHCI_PORT_RESET) ) {
|
||||
dev->handle_packet(dev,
|
||||
USB_MSG_RESET, 0, 0, NULL, 0);
|
||||
usb_send_msg(dev, USB_MSG_RESET);
|
||||
}
|
||||
}
|
||||
port->ctrl = (port->ctrl & 0x01fb) | (val & ~0x01fb);
|
||||
@ -336,8 +346,7 @@ static void uhci_attach(USBPort *port1, USBDevice *dev)
|
||||
port->ctrl &= ~UHCI_PORT_LSDA;
|
||||
port->port.dev = dev;
|
||||
/* send the attach message */
|
||||
dev->handle_packet(dev,
|
||||
USB_MSG_ATTACH, 0, 0, NULL, 0);
|
||||
usb_send_msg(dev, USB_MSG_ATTACH);
|
||||
} else {
|
||||
/* set connect status */
|
||||
if (port->ctrl & UHCI_PORT_CCS) {
|
||||
@ -352,16 +361,13 @@ static void uhci_attach(USBPort *port1, USBDevice *dev)
|
||||
dev = port->port.dev;
|
||||
if (dev) {
|
||||
/* send the detach message */
|
||||
dev->handle_packet(dev,
|
||||
USB_MSG_DETACH, 0, 0, NULL, 0);
|
||||
usb_send_msg(dev, USB_MSG_DETACH);
|
||||
}
|
||||
port->port.dev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int uhci_broadcast_packet(UHCIState *s, uint8_t pid,
|
||||
uint8_t devaddr, uint8_t devep,
|
||||
uint8_t *data, int len)
|
||||
static int uhci_broadcast_packet(UHCIState *s, USBPacket *p)
|
||||
{
|
||||
UHCIPort *port;
|
||||
USBDevice *dev;
|
||||
@ -370,18 +376,18 @@ static int uhci_broadcast_packet(UHCIState *s, uint8_t pid,
|
||||
#ifdef DEBUG_PACKET
|
||||
{
|
||||
const char *pidstr;
|
||||
switch(pid) {
|
||||
switch(p->pid) {
|
||||
case USB_TOKEN_SETUP: pidstr = "SETUP"; break;
|
||||
case USB_TOKEN_IN: pidstr = "IN"; break;
|
||||
case USB_TOKEN_OUT: pidstr = "OUT"; break;
|
||||
default: pidstr = "?"; break;
|
||||
}
|
||||
printf("frame %d: pid=%s addr=0x%02x ep=%d len=%d\n",
|
||||
s->frnum, pidstr, devaddr, devep, len);
|
||||
if (pid != USB_TOKEN_IN) {
|
||||
s->frnum, pidstr, p->devaddr, p->devep, p->len);
|
||||
if (p->pid != USB_TOKEN_IN) {
|
||||
printf(" data_out=");
|
||||
for(i = 0; i < len; i++) {
|
||||
printf(" %02x", data[i]);
|
||||
for(i = 0; i < p->len; i++) {
|
||||
printf(" %02x", p->data[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
@ -391,17 +397,17 @@ static int uhci_broadcast_packet(UHCIState *s, uint8_t pid,
|
||||
port = &s->ports[i];
|
||||
dev = port->port.dev;
|
||||
if (dev && (port->ctrl & UHCI_PORT_EN)) {
|
||||
ret = dev->handle_packet(dev, pid,
|
||||
devaddr, devep,
|
||||
data, len);
|
||||
ret = dev->handle_packet(dev, p);
|
||||
if (ret != USB_RET_NODEV) {
|
||||
#ifdef DEBUG_PACKET
|
||||
{
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
printf("usb-uhci: Async packet\n");
|
||||
} else {
|
||||
printf(" ret=%d ", ret);
|
||||
if (pid == USB_TOKEN_IN && ret > 0) {
|
||||
if (p->pid == USB_TOKEN_IN && ret > 0) {
|
||||
printf("data_in=");
|
||||
for(i = 0; i < ret; i++) {
|
||||
printf(" %02x", data[i]);
|
||||
printf(" %02x", p->data[i]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
@ -414,6 +420,8 @@ static int uhci_broadcast_packet(UHCIState *s, uint8_t pid,
|
||||
return USB_RET_NODEV;
|
||||
}
|
||||
|
||||
static void uhci_async_complete_packet(USBPacket * packet, void *opaque);
|
||||
|
||||
/* return -1 if fatal error (frame must be stopped)
|
||||
0 if TD successful
|
||||
1 if TD unsuccessful or inactive
|
||||
@ -421,9 +429,9 @@ static int uhci_broadcast_packet(UHCIState *s, uint8_t pid,
|
||||
static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask)
|
||||
{
|
||||
uint8_t pid;
|
||||
uint8_t buf[1280];
|
||||
int len, max_len, err, ret;
|
||||
|
||||
/* ??? This is wrong for async completion. */
|
||||
if (td->ctrl & TD_CTRL_IOC) {
|
||||
*int_mask |= 0x01;
|
||||
}
|
||||
@ -434,21 +442,8 @@ static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask)
|
||||
/* TD is active */
|
||||
max_len = ((td->token >> 21) + 1) & 0x7ff;
|
||||
pid = td->token & 0xff;
|
||||
switch(pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
case USB_TOKEN_SETUP:
|
||||
cpu_physical_memory_read(td->buffer, buf, max_len);
|
||||
ret = uhci_broadcast_packet(s, pid,
|
||||
(td->token >> 8) & 0x7f,
|
||||
(td->token >> 15) & 0xf,
|
||||
buf, max_len);
|
||||
len = max_len;
|
||||
break;
|
||||
case USB_TOKEN_IN:
|
||||
ret = uhci_broadcast_packet(s, pid,
|
||||
(td->token >> 8) & 0x7f,
|
||||
(td->token >> 15) & 0xf,
|
||||
buf, max_len);
|
||||
if (s->async_qh) {
|
||||
ret = s->usb_packet.len;
|
||||
if (ret >= 0) {
|
||||
len = ret;
|
||||
if (len > max_len) {
|
||||
@ -457,17 +452,52 @@ static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask)
|
||||
}
|
||||
if (len > 0) {
|
||||
/* write the data back */
|
||||
cpu_physical_memory_write(td->buffer, buf, len);
|
||||
cpu_physical_memory_write(td->buffer, s->usb_buf, len);
|
||||
}
|
||||
} else {
|
||||
len = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* invalid pid : frame interrupted */
|
||||
s->status |= UHCI_STS_HCPERR;
|
||||
uhci_update_irq(s);
|
||||
return -1;
|
||||
s->async_qh = 0;
|
||||
} else {
|
||||
s->usb_packet.pid = pid;
|
||||
s->usb_packet.devaddr = (td->token >> 8) & 0x7f;
|
||||
s->usb_packet.devep = (td->token >> 15) & 0xf;
|
||||
s->usb_packet.data = s->usb_buf;
|
||||
s->usb_packet.len = max_len;
|
||||
s->usb_packet.complete_cb = uhci_async_complete_packet;
|
||||
s->usb_packet.complete_opaque = s;
|
||||
switch(pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
case USB_TOKEN_SETUP:
|
||||
cpu_physical_memory_read(td->buffer, s->usb_buf, max_len);
|
||||
ret = uhci_broadcast_packet(s, &s->usb_packet);
|
||||
len = max_len;
|
||||
break;
|
||||
case USB_TOKEN_IN:
|
||||
ret = uhci_broadcast_packet(s, &s->usb_packet);
|
||||
if (ret >= 0) {
|
||||
len = ret;
|
||||
if (len > max_len) {
|
||||
len = max_len;
|
||||
ret = USB_RET_BABBLE;
|
||||
}
|
||||
if (len > 0) {
|
||||
/* write the data back */
|
||||
cpu_physical_memory_write(td->buffer, s->usb_buf, len);
|
||||
}
|
||||
} else {
|
||||
len = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* invalid pid : frame interrupted */
|
||||
s->status |= UHCI_STS_HCPERR;
|
||||
uhci_update_irq(s);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
return 2;
|
||||
}
|
||||
if (td->ctrl & TD_CTRL_IOS)
|
||||
td->ctrl &= ~TD_CTRL_ACTIVE;
|
||||
@ -520,6 +550,61 @@ static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask)
|
||||
}
|
||||
}
|
||||
|
||||
static void uhci_async_complete_packet(USBPacket * packet, void *opaque)
|
||||
{
|
||||
UHCIState *s = opaque;
|
||||
UHCI_QH qh;
|
||||
UHCI_TD td;
|
||||
uint32_t link;
|
||||
uint32_t old_td_ctrl;
|
||||
uint32_t val;
|
||||
int ret;
|
||||
|
||||
link = s->async_qh;
|
||||
if (!link) {
|
||||
/* This should never happen. It means a TD somehow got removed
|
||||
without cancelling the associated async IO request. */
|
||||
return;
|
||||
}
|
||||
cpu_physical_memory_read(link & ~0xf, (uint8_t *)&qh, sizeof(qh));
|
||||
le32_to_cpus(&qh.link);
|
||||
le32_to_cpus(&qh.el_link);
|
||||
/* Re-process the queue containing the async packet. */
|
||||
while (1) {
|
||||
cpu_physical_memory_read(qh.el_link & ~0xf,
|
||||
(uint8_t *)&td, sizeof(td));
|
||||
le32_to_cpus(&td.link);
|
||||
le32_to_cpus(&td.ctrl);
|
||||
le32_to_cpus(&td.token);
|
||||
le32_to_cpus(&td.buffer);
|
||||
old_td_ctrl = td.ctrl;
|
||||
ret = uhci_handle_td(s, &td, &s->pending_int_mask);
|
||||
/* update the status bits of the TD */
|
||||
if (old_td_ctrl != td.ctrl) {
|
||||
val = cpu_to_le32(td.ctrl);
|
||||
cpu_physical_memory_write((qh.el_link & ~0xf) + 4,
|
||||
(const uint8_t *)&val,
|
||||
sizeof(val));
|
||||
}
|
||||
if (ret < 0)
|
||||
break; /* interrupted frame */
|
||||
if (ret == 2) {
|
||||
s->async_qh = link;
|
||||
break;
|
||||
} else if (ret == 0) {
|
||||
/* update qh element link */
|
||||
qh.el_link = td.link;
|
||||
val = cpu_to_le32(qh.el_link);
|
||||
cpu_physical_memory_write((link & ~0xf) + 4,
|
||||
(const uint8_t *)&val,
|
||||
sizeof(val));
|
||||
if (!(qh.el_link & 4))
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void uhci_frame_timer(void *opaque)
|
||||
{
|
||||
UHCIState *s = opaque;
|
||||
@ -528,6 +613,7 @@ static void uhci_frame_timer(void *opaque)
|
||||
int int_mask, cnt, ret;
|
||||
UHCI_TD td;
|
||||
UHCI_QH qh;
|
||||
uint32_t old_async_qh;
|
||||
|
||||
if (!(s->cmd & UHCI_CMD_RS)) {
|
||||
qemu_del_timer(s->frame_timer);
|
||||
@ -535,6 +621,14 @@ static void uhci_frame_timer(void *opaque)
|
||||
s->status |= UHCI_STS_HCHALTED;
|
||||
return;
|
||||
}
|
||||
/* Complete the previous frame. */
|
||||
s->frnum = (s->frnum + 1) & 0x7ff;
|
||||
if (s->pending_int_mask) {
|
||||
s->status2 |= s->pending_int_mask;
|
||||
s->status |= UHCI_STS_USBINT;
|
||||
uhci_update_irq(s);
|
||||
}
|
||||
old_async_qh = s->async_qh;
|
||||
frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2);
|
||||
cpu_physical_memory_read(frame_addr, (uint8_t *)&link, 4);
|
||||
le32_to_cpus(&link);
|
||||
@ -546,6 +640,12 @@ static void uhci_frame_timer(void *opaque)
|
||||
/* valid frame */
|
||||
if (link & 2) {
|
||||
/* QH */
|
||||
if (link == s->async_qh) {
|
||||
/* We've found a previously issues packet.
|
||||
Nothing else to do. */
|
||||
old_async_qh = 0;
|
||||
break;
|
||||
}
|
||||
cpu_physical_memory_read(link & ~0xf, (uint8_t *)&qh, sizeof(qh));
|
||||
le32_to_cpus(&qh.link);
|
||||
le32_to_cpus(&qh.el_link);
|
||||
@ -556,6 +656,10 @@ static void uhci_frame_timer(void *opaque)
|
||||
} else if (qh.el_link & 2) {
|
||||
/* QH */
|
||||
link = qh.el_link;
|
||||
} else if (s->async_qh) {
|
||||
/* We can only cope with one pending packet. Keep looking
|
||||
for the previously issued packet. */
|
||||
link = qh.link;
|
||||
} else {
|
||||
/* TD */
|
||||
if (--cnt == 0)
|
||||
@ -577,7 +681,9 @@ static void uhci_frame_timer(void *opaque)
|
||||
}
|
||||
if (ret < 0)
|
||||
break; /* interrupted frame */
|
||||
if (ret == 0) {
|
||||
if (ret == 2) {
|
||||
s->async_qh = link;
|
||||
} else if (ret == 0) {
|
||||
/* update qh element link */
|
||||
qh.el_link = td.link;
|
||||
val = cpu_to_le32(qh.el_link);
|
||||
@ -599,25 +705,41 @@ static void uhci_frame_timer(void *opaque)
|
||||
le32_to_cpus(&td.ctrl);
|
||||
le32_to_cpus(&td.token);
|
||||
le32_to_cpus(&td.buffer);
|
||||
old_td_ctrl = td.ctrl;
|
||||
ret = uhci_handle_td(s, &td, &int_mask);
|
||||
/* update the status bits of the TD */
|
||||
if (old_td_ctrl != td.ctrl) {
|
||||
val = cpu_to_le32(td.ctrl);
|
||||
cpu_physical_memory_write((link & ~0xf) + 4,
|
||||
(const uint8_t *)&val,
|
||||
sizeof(val));
|
||||
/* Ignore isochonous transfers while there is an async packet
|
||||
pending. This is wrong, but we don't implement isochronous
|
||||
transfers anyway. */
|
||||
if (s->async_qh == 0) {
|
||||
old_td_ctrl = td.ctrl;
|
||||
ret = uhci_handle_td(s, &td, &int_mask);
|
||||
/* update the status bits of the TD */
|
||||
if (old_td_ctrl != td.ctrl) {
|
||||
val = cpu_to_le32(td.ctrl);
|
||||
cpu_physical_memory_write((link & ~0xf) + 4,
|
||||
(const uint8_t *)&val,
|
||||
sizeof(val));
|
||||
}
|
||||
if (ret < 0)
|
||||
break; /* interrupted frame */
|
||||
if (ret == 2) {
|
||||
/* We can't handle async isochronous transfers.
|
||||
Cancel The packet. */
|
||||
fprintf(stderr, "usb-uhci: Unimplemented async packet\n");
|
||||
usb_cancel_packet(&s->usb_packet);
|
||||
}
|
||||
}
|
||||
if (ret < 0)
|
||||
break; /* interrupted frame */
|
||||
link = td.link;
|
||||
}
|
||||
}
|
||||
s->frnum = (s->frnum + 1) & 0x7ff;
|
||||
if (int_mask) {
|
||||
s->status2 |= int_mask;
|
||||
s->status |= UHCI_STS_USBINT;
|
||||
uhci_update_irq(s);
|
||||
s->pending_int_mask = int_mask;
|
||||
if (old_async_qh) {
|
||||
/* A previously started transfer has disappeared from the transfer
|
||||
list. There's nothing useful we can do with it now, so just
|
||||
discard the packet and hope it wasn't too important. */
|
||||
#ifdef DEBUG
|
||||
printf("Discarding USB packet\n");
|
||||
#endif
|
||||
usb_cancel_packet(&s->usb_packet);
|
||||
s->async_qh = 0;
|
||||
}
|
||||
/* prepare the timer for the next frame */
|
||||
expire_time = qemu_get_clock(vm_clock) +
|
||||
|
32
hw/usb.c
32
hw/usb.c
@ -38,13 +38,13 @@ void usb_attach(USBPort *port, USBDevice *dev)
|
||||
#define SETUP_STATE_DATA 1
|
||||
#define SETUP_STATE_ACK 2
|
||||
|
||||
int usb_generic_handle_packet(USBDevice *s, int pid,
|
||||
uint8_t devaddr, uint8_t devep,
|
||||
uint8_t *data, int len)
|
||||
int usb_generic_handle_packet(USBDevice *s, USBPacket *p)
|
||||
{
|
||||
int l, ret = 0;
|
||||
int len = p->len;
|
||||
uint8_t *data = p->data;
|
||||
|
||||
switch(pid) {
|
||||
switch(p->pid) {
|
||||
case USB_MSG_ATTACH:
|
||||
s->state = USB_STATE_ATTACHED;
|
||||
break;
|
||||
@ -58,7 +58,7 @@ int usb_generic_handle_packet(USBDevice *s, int pid,
|
||||
s->handle_reset(s);
|
||||
break;
|
||||
case USB_TOKEN_SETUP:
|
||||
if (s->state < USB_STATE_DEFAULT || devaddr != s->addr)
|
||||
if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr)
|
||||
return USB_RET_NODEV;
|
||||
if (len != 8)
|
||||
goto fail;
|
||||
@ -85,9 +85,9 @@ int usb_generic_handle_packet(USBDevice *s, int pid,
|
||||
}
|
||||
break;
|
||||
case USB_TOKEN_IN:
|
||||
if (s->state < USB_STATE_DEFAULT || devaddr != s->addr)
|
||||
if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr)
|
||||
return USB_RET_NODEV;
|
||||
switch(devep) {
|
||||
switch(p->devep) {
|
||||
case 0:
|
||||
switch(s->setup_state) {
|
||||
case SETUP_STATE_ACK:
|
||||
@ -125,14 +125,14 @@ int usb_generic_handle_packet(USBDevice *s, int pid,
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = s->handle_data(s, pid, devep, data, len);
|
||||
ret = s->handle_data(s, p);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case USB_TOKEN_OUT:
|
||||
if (s->state < USB_STATE_DEFAULT || devaddr != s->addr)
|
||||
if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr)
|
||||
return USB_RET_NODEV;
|
||||
switch(devep) {
|
||||
switch(p->devep) {
|
||||
case 0:
|
||||
switch(s->setup_state) {
|
||||
case SETUP_STATE_ACK:
|
||||
@ -163,7 +163,7 @@ int usb_generic_handle_packet(USBDevice *s, int pid,
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = s->handle_data(s, pid, devep, data, len);
|
||||
ret = s->handle_data(s, p);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -191,3 +191,13 @@ int set_usb_string(uint8_t *buf, const char *str)
|
||||
}
|
||||
return q - buf;
|
||||
}
|
||||
|
||||
/* Send an internal message to a USB device. */
|
||||
void usb_send_msg(USBDevice *dev, int msg)
|
||||
{
|
||||
USBPacket p;
|
||||
memset(&p, 0, sizeof(p));
|
||||
p.pid = msg;
|
||||
dev->handle_packet(dev, &p);
|
||||
}
|
||||
|
||||
|
58
hw/usb.h
58
hw/usb.h
@ -34,6 +34,7 @@
|
||||
#define USB_RET_NAK (-2)
|
||||
#define USB_RET_STALL (-3)
|
||||
#define USB_RET_BABBLE (-4)
|
||||
#define USB_RET_ASYNC (-5)
|
||||
|
||||
#define USB_SPEED_LOW 0
|
||||
#define USB_SPEED_FULL 1
|
||||
@ -109,13 +110,12 @@
|
||||
|
||||
typedef struct USBPort USBPort;
|
||||
typedef struct USBDevice USBDevice;
|
||||
typedef struct USBPacket USBPacket;
|
||||
|
||||
/* definition of a USB device */
|
||||
struct USBDevice {
|
||||
void *opaque;
|
||||
int (*handle_packet)(USBDevice *dev, int pid,
|
||||
uint8_t devaddr, uint8_t devep,
|
||||
uint8_t *data, int len);
|
||||
int (*handle_packet)(USBDevice *dev, USBPacket *p);
|
||||
void (*handle_destroy)(USBDevice *dev);
|
||||
|
||||
int speed;
|
||||
@ -126,8 +126,7 @@ struct USBDevice {
|
||||
void (*handle_reset)(USBDevice *dev);
|
||||
int (*handle_control)(USBDevice *dev, int request, int value,
|
||||
int index, int length, uint8_t *data);
|
||||
int (*handle_data)(USBDevice *dev, int pid, uint8_t devep,
|
||||
uint8_t *data, int len);
|
||||
int (*handle_data)(USBDevice *dev, USBPacket *p);
|
||||
uint8_t addr;
|
||||
char devname[32];
|
||||
|
||||
@ -151,11 +150,54 @@ struct USBPort {
|
||||
struct USBPort *next; /* Used internally by qemu. */
|
||||
};
|
||||
|
||||
typedef void USBCallback(USBPacket * packet, void *opaque);
|
||||
|
||||
/* Structure used to hold information about an active USB packet. */
|
||||
struct USBPacket {
|
||||
/* Data fields for use by the driver. */
|
||||
int pid;
|
||||
uint8_t devaddr;
|
||||
uint8_t devep;
|
||||
uint8_t *data;
|
||||
int len;
|
||||
/* Internal use by the USB layer. */
|
||||
USBCallback *complete_cb;
|
||||
void *complete_opaque;
|
||||
USBCallback *cancel_cb;
|
||||
void * *cancel_opaque;
|
||||
};
|
||||
|
||||
/* Defer completion of a USB packet. The hadle_packet routine should then
|
||||
return USB_RET_ASYNC. Packets that complete immediately (before
|
||||
handle_packet returns) should not call this method. */
|
||||
static inline void usb_defer_packet(USBPacket *p, USBCallback *cancel,
|
||||
void * opaque)
|
||||
{
|
||||
p->cancel_cb = cancel;
|
||||
p->cancel_opaque = opaque;
|
||||
}
|
||||
|
||||
/* Notify the controller that an async packet is complete. This should only
|
||||
be called for packets previously deferred with usb_defer_packet, and
|
||||
should never be called from within handle_packet. */
|
||||
static inline void usb_packet_complete(USBPacket *p)
|
||||
{
|
||||
p->complete_cb(p, p->complete_opaque);
|
||||
}
|
||||
|
||||
/* Cancel an active packet. The packed must have been deferred with
|
||||
usb_defer_packet, and not yet completed. */
|
||||
static inline void usb_cancel_packet(USBPacket * p)
|
||||
{
|
||||
p->cancel_cb(p, p->cancel_opaque);
|
||||
}
|
||||
|
||||
void usb_attach(USBPort *port, USBDevice *dev);
|
||||
int usb_generic_handle_packet(USBDevice *s, int pid,
|
||||
uint8_t devaddr, uint8_t devep,
|
||||
uint8_t *data, int len);
|
||||
int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
|
||||
int set_usb_string(uint8_t *buf, const char *str);
|
||||
void usb_send_msg(USBDevice *dev, int msg);
|
||||
|
||||
void usb_packet_complete(USBPacket *p);
|
||||
|
||||
/* usb hub */
|
||||
USBDevice *usb_hub_init(int nb_ports);
|
||||
|
30
pc-bios/openbios-esp.diff
Normal file
30
pc-bios/openbios-esp.diff
Normal file
@ -0,0 +1,30 @@
|
||||
The ESP SCSI driver currently doesn't check whether a DMA requests has
|
||||
completed before checking its status. On older qemu versions this works ok
|
||||
because DMA happens instantly. On never qemu DMA can take an indeterminate
|
||||
amount of time ooto complete, just like on real hardware.
|
||||
|
||||
The patch below waits for the controller to raise the DMA interrupt after
|
||||
initiating a DMA request.
|
||||
|
||||
Index: drivers/esp.c
|
||||
===================================================================
|
||||
--- drivers/esp.c (revision 61)
|
||||
+++ drivers/esp.c (working copy)
|
||||
@@ -113,6 +113,8 @@ do_command(esp_private_t *esp, sd_privat
|
||||
esp->espdma.regs->cond_reg = 0;
|
||||
// Set ATN, issue command
|
||||
esp->ll->regs[ESP_CMD] = ESP_CMD_SELA | ESP_CMD_DMA;
|
||||
+ // Wait for DMA to complete
|
||||
+ while ((esp->espdma.regs->cond_reg & DMA_HNDL_INTR) == 0) /* no-op */;
|
||||
// Check status
|
||||
status = esp->ll->regs[ESP_STATUS];
|
||||
|
||||
@@ -129,6 +131,8 @@ do_command(esp_private_t *esp, sd_privat
|
||||
esp->espdma.regs->cond_reg = DMA_ST_WRITE;
|
||||
// Transfer
|
||||
esp->ll->regs[ESP_CMD] = ESP_CMD_TI | ESP_CMD_DMA;
|
||||
+ // Wait for DMA to complete
|
||||
+ while ((esp->espdma.regs->cond_reg & DMA_HNDL_INTR) == 0) /* no-op */;
|
||||
// Check status
|
||||
status = esp->ll->regs[ESP_STATUS];
|
||||
|
Binary file not shown.
11
usb-linux.c
11
usb-linux.c
@ -114,22 +114,21 @@ static int usb_host_handle_control(USBDevice *dev,
|
||||
}
|
||||
}
|
||||
|
||||
static int usb_host_handle_data(USBDevice *dev, int pid,
|
||||
uint8_t devep,
|
||||
uint8_t *data, int len)
|
||||
static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
USBHostDevice *s = (USBHostDevice *)dev;
|
||||
struct usbdevfs_bulktransfer bt;
|
||||
int ret;
|
||||
uint8_t devep = p->devep;
|
||||
|
||||
/* XXX: optimize and handle all data types by looking at the
|
||||
config descriptor */
|
||||
if (pid == USB_TOKEN_IN)
|
||||
if (p->pid == USB_TOKEN_IN)
|
||||
devep |= 0x80;
|
||||
bt.ep = devep;
|
||||
bt.len = len;
|
||||
bt.len = p->len;
|
||||
bt.timeout = 50;
|
||||
bt.data = data;
|
||||
bt.data = p->data;
|
||||
ret = ioctl(s->fd, USBDEVFS_BULK, &bt);
|
||||
if (ret < 0) {
|
||||
switch(errno) {
|
||||
|
9
vl.h
9
vl.h
@ -1146,6 +1146,11 @@ void do_usb_del(const char *devname);
|
||||
void usb_info(void);
|
||||
|
||||
/* scsi-disk.c */
|
||||
enum scsi_reason {
|
||||
SCSI_REASON_DONE, /* Command complete. */
|
||||
SCSI_REASON_DATA /* Transfer complete, more data required. */
|
||||
};
|
||||
|
||||
typedef struct SCSIDevice SCSIDevice;
|
||||
typedef void (*scsi_completionfn)(void *, uint32_t, int);
|
||||
|
||||
@ -1155,8 +1160,12 @@ SCSIDevice *scsi_disk_init(BlockDriverState *bdrv,
|
||||
void scsi_disk_destroy(SCSIDevice *s);
|
||||
|
||||
int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun);
|
||||
/* SCSI data transfers are asynchrnonous. However, unlike the block IO
|
||||
layer the completion routine may be called directly by
|
||||
scsi_{read,write}_data. */
|
||||
int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len);
|
||||
int scsi_write_data(SCSIDevice *s, uint8_t *data, uint32_t len);
|
||||
void scsi_cancel_io(SCSIDevice *s);
|
||||
|
||||
/* lsi53c895a.c */
|
||||
void lsi_scsi_attach(void *opaque, BlockDriverState *bd, int id);
|
||||
|
Loading…
Reference in New Issue
Block a user