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:
pbrook 2006-08-12 01:04:27 +00:00
parent 4ca9c76f36
commit 4d611c9a2f
14 changed files with 745 additions and 292 deletions

142
hw/esp.c
View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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:

View File

@ -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);
}

View File

@ -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) +

View File

@ -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);
}

View File

@ -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
View 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.

View File

@ -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
View File

@ -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);