aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/ac97.c139
-rw-r--r--hw/e1000.c2
-rw-r--r--hw/pc_piix.c4
-rw-r--r--hw/pflash_cfi01.c1
-rw-r--r--hw/qxl-render.c1
-rw-r--r--hw/qxl.c6
-rw-r--r--hw/qxl.h2
-rw-r--r--hw/scsi-bus.c8
-rw-r--r--hw/scsi-defs.h1
-rw-r--r--hw/scsi-disk.c170
-rw-r--r--hw/usb.h5
-rw-r--r--hw/usb/bus.c17
-rw-r--r--hw/usb/core.c17
-rw-r--r--hw/usb/desc.c126
-rw-r--r--hw/usb/desc.h63
-rw-r--r--hw/usb/dev-hub.c43
-rw-r--r--hw/usb/hcd-ehci.c16
-rw-r--r--hw/usb/hcd-uhci.c16
-rw-r--r--hw/usb/host-linux.c237
-rw-r--r--hw/usb/redirect.c11
-rw-r--r--hw/virtio-scsi.c26
-rw-r--r--hw/virtio.c7
-rw-r--r--hw/virtio.h1
23 files changed, 594 insertions, 325 deletions
diff --git a/hw/ac97.c b/hw/ac97.c
index c0fd0197a0..177f729d48 100644
--- a/hw/ac97.c
+++ b/hw/ac97.c
@@ -118,7 +118,6 @@ enum {
#define EACS_VRA 1
#define EACS_VRM 8
-#define VOL_MASK 0x1f
#define MUTE_SHIFT 15
#define REC_MASK 7
@@ -437,83 +436,55 @@ static void reset_voices (AC97LinkState *s, uint8_t active[LAST_INDEX])
AUD_set_active_in (s->voice_mc, active[MC_INDEX]);
}
-#ifdef USE_MIXER
-static void set_volume (AC97LinkState *s, int index,
- audmixerctl_t mt, uint32_t val)
+static void get_volume (uint16_t vol, uint16_t mask, int inverse,
+ int *mute, uint8_t *lvol, uint8_t *rvol)
{
- int mute = (val >> MUTE_SHIFT) & 1;
- uint8_t rvol = VOL_MASK - (val & VOL_MASK);
- uint8_t lvol = VOL_MASK - ((val >> 8) & VOL_MASK);
- rvol = 255 * rvol / VOL_MASK;
- lvol = 255 * lvol / VOL_MASK;
-
-#ifdef SOFT_VOLUME
- if (index == AC97_Master_Volume_Mute) {
- AUD_set_volume_out (s->voice_po, mute, lvol, rvol);
- }
- else {
- AUD_set_volume (mt, &mute, &lvol, &rvol);
- }
-#else
- AUD_set_volume (mt, &mute, &lvol, &rvol);
-#endif
+ *mute = (vol >> MUTE_SHIFT) & 1;
+ *rvol = (255 * (vol & mask)) / mask;
+ *lvol = (255 * ((vol >> 8) & mask)) / mask;
- rvol = VOL_MASK - ((VOL_MASK * rvol) / 255);
- lvol = VOL_MASK - ((VOL_MASK * lvol) / 255);
- mixer_store (s, index, val);
+ if (inverse) {
+ *rvol = 255 - *rvol;
+ *lvol = 255 - *lvol;
+ }
}
-static audrecsource_t ac97_to_aud_record_source (uint8_t i)
+static void update_combined_volume_out (AC97LinkState *s)
{
- switch (i) {
- case REC_MIC:
- return AUD_REC_MIC;
-
- case REC_CD:
- return AUD_REC_CD;
+ uint8_t lvol, rvol, plvol, prvol;
+ int mute, pmute;
- case REC_VIDEO:
- return AUD_REC_VIDEO;
+ get_volume (mixer_load (s, AC97_Master_Volume_Mute), 0x3f, 1,
+ &mute, &lvol, &rvol);
+ /* FIXME: should be 1f according to spec */
+ get_volume (mixer_load (s, AC97_PCM_Out_Volume_Mute), 0x3f, 1,
+ &pmute, &plvol, &prvol);
- case REC_AUX:
- return AUD_REC_AUX;
+ mute = mute | pmute;
+ lvol = (lvol * plvol) / 255;
+ rvol = (rvol * prvol) / 255;
- case REC_LINE_IN:
- return AUD_REC_LINE_IN;
-
- case REC_PHONE:
- return AUD_REC_PHONE;
-
- default:
- dolog ("Unknown record source %d, using MIC\n", i);
- return AUD_REC_MIC;
- }
+ AUD_set_volume_out (s->voice_po, mute, lvol, rvol);
}
-static uint8_t aud_to_ac97_record_source (audrecsource_t rs)
+static void update_volume_in (AC97LinkState *s)
{
- switch (rs) {
- case AUD_REC_MIC:
- return REC_MIC;
-
- case AUD_REC_CD:
- return REC_CD;
-
- case AUD_REC_VIDEO:
- return REC_VIDEO;
-
- case AUD_REC_AUX:
- return REC_AUX;
+ uint8_t lvol, rvol;
+ int mute;
- case AUD_REC_LINE_IN:
- return REC_LINE_IN;
+ get_volume (mixer_load (s, AC97_Record_Gain_Mute), 0x0f, 0,
+ &mute, &lvol, &rvol);
- case AUD_REC_PHONE:
- return REC_PHONE;
+ AUD_set_volume_in (s->voice_pi, mute, lvol, rvol);
+}
- default:
- dolog ("Unknown audio recording source %d using MIC\n", rs);
- return REC_MIC;
+static void set_volume (AC97LinkState *s, int index, uint32_t val)
+{
+ mixer_store (s, index, val);
+ if (index == AC97_Master_Volume_Mute || index == AC97_PCM_Out_Volume_Mute) {
+ update_combined_volume_out (s);
+ } else if (index == AC97_Record_Gain_Mute) {
+ update_volume_in (s);
}
}
@@ -521,14 +492,8 @@ static void record_select (AC97LinkState *s, uint32_t val)
{
uint8_t rs = val & REC_MASK;
uint8_t ls = (val >> 8) & REC_MASK;
- audrecsource_t ars = ac97_to_aud_record_source (rs);
- audrecsource_t als = ac97_to_aud_record_source (ls);
- AUD_set_record_source (&als, &ars);
- rs = aud_to_ac97_record_source (ars);
- ls = aud_to_ac97_record_source (als);
mixer_store (s, AC97_Record_Select, rs | (ls << 8));
}
-#endif
static void mixer_reset (AC97LinkState *s)
{
@@ -564,12 +529,11 @@ static void mixer_reset (AC97LinkState *s)
mixer_store (s, AC97_PCM_LR_ADC_Rate , 0xbb80);
mixer_store (s, AC97_MIC_ADC_Rate , 0xbb80);
-#ifdef USE_MIXER
record_select (s, 0);
- set_volume (s, AC97_Master_Volume_Mute, AUD_MIXER_VOLUME , 0x8000);
- set_volume (s, AC97_PCM_Out_Volume_Mute, AUD_MIXER_PCM , 0x8808);
- set_volume (s, AC97_Line_In_Volume_Mute, AUD_MIXER_LINE_IN, 0x8808);
-#endif
+ set_volume (s, AC97_Master_Volume_Mute, 0x8000);
+ set_volume (s, AC97_PCM_Out_Volume_Mute, 0x8808);
+ set_volume (s, AC97_Line_In_Volume_Mute, 0x8808);
+
reset_voices (s, active);
}
@@ -628,20 +592,15 @@ static void nam_writew (void *opaque, uint32_t addr, uint32_t val)
val |= mixer_load (s, index) & 0xf;
mixer_store (s, index, val);
break;
-#ifdef USE_MIXER
- case AC97_Master_Volume_Mute:
- set_volume (s, index, AUD_MIXER_VOLUME, val);
- break;
case AC97_PCM_Out_Volume_Mute:
- set_volume (s, index, AUD_MIXER_PCM, val);
- break;
+ case AC97_Master_Volume_Mute:
+ case AC97_Record_Gain_Mute:
case AC97_Line_In_Volume_Mute:
- set_volume (s, index, AUD_MIXER_LINE_IN, val);
+ set_volume (s, index, val);
break;
case AC97_Record_Select:
record_select (s, val);
break;
-#endif
case AC97_Vendor_ID1:
case AC97_Vendor_ID2:
dolog ("Attempt to write vendor ID to %#x\n", val);
@@ -1194,14 +1153,14 @@ static int ac97_post_load (void *opaque, int version_id)
uint8_t active[LAST_INDEX];
AC97LinkState *s = opaque;
-#ifdef USE_MIXER
record_select (s, mixer_load (s, AC97_Record_Select));
-#define V_(a, b) set_volume (s, a, b, mixer_load (s, a))
- V_ (AC97_Master_Volume_Mute, AUD_MIXER_VOLUME);
- V_ (AC97_PCM_Out_Volume_Mute, AUD_MIXER_PCM);
- V_ (AC97_Line_In_Volume_Mute, AUD_MIXER_LINE_IN);
-#undef V_
-#endif
+ set_volume (s, AC97_Master_Volume_Mute,
+ mixer_load (s, AC97_Master_Volume_Mute));
+ set_volume (s, AC97_PCM_Out_Volume_Mute,
+ mixer_load (s, AC97_PCM_Out_Volume_Mute));
+ set_volume (s, AC97_Line_In_Volume_Mute,
+ mixer_load (s, AC97_Line_In_Volume_Mute));
+
active[PI_INDEX] = !!(s->bm_regs[PI_INDEX].cr & CR_RPBM);
active[PO_INDEX] = !!(s->bm_regs[PO_INDEX].cr & CR_RPBM);
active[MC_INDEX] = !!(s->bm_regs[MC_INDEX].cr & CR_RPBM);
diff --git a/hw/e1000.c b/hw/e1000.c
index 7babc0b06e..9c764624f9 100644
--- a/hw/e1000.c
+++ b/hw/e1000.c
@@ -481,7 +481,7 @@ process_tx_desc(E1000State *s, struct e1000_tx_desc *dp)
} while (split_size -= bytes);
} else if (!tp->tse && tp->cptse) {
// context descriptor TSE is not set, while data descriptor TSE is set
- DBGOUT(TXERR, "TCP segmentaion Error\n");
+ DBGOUT(TXERR, "TCP segmentation error\n");
} else {
split_size = MIN(sizeof(tp->data) - tp->size, split_size);
pci_dma_read(&s->dev, addr, tp->data + tp->size, split_size);
diff --git a/hw/pc_piix.c b/hw/pc_piix.c
index 907d723728..6a75718fbb 100644
--- a/hw/pc_piix.c
+++ b/hw/pc_piix.c
@@ -377,6 +377,10 @@ static QEMUMachine pc_machine_v1_1 = {
.driver = "apic",\
.property = "vapic",\
.value = "off",\
+ },{\
+ .driver = "USB",\
+ .property = "full-path",\
+ .value = "no",\
}
static QEMUMachine pc_machine_v1_0 = {
diff --git a/hw/pflash_cfi01.c b/hw/pflash_cfi01.c
index b03f623cb1..d1c742379b 100644
--- a/hw/pflash_cfi01.c
+++ b/hw/pflash_cfi01.c
@@ -144,7 +144,6 @@ static uint32_t pflash_read (pflash_t *pfl, target_phys_addr_t offset,
} else {
ret = p[offset];
ret |= p[offset + 1] << 8;
- ret |= p[offset + 1] << 8;
ret |= p[offset + 2] << 16;
ret |= p[offset + 3] << 24;
}
diff --git a/hw/qxl-render.c b/hw/qxl-render.c
index 28ab182226..f7f1bfda04 100644
--- a/hw/qxl-render.c
+++ b/hw/qxl-render.c
@@ -127,6 +127,7 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
qxl->guest_primary.surface.width,
qxl->guest_primary.surface.height);
}
+ dpy_resize(vga->ds);
}
for (i = 0; i < qxl->num_dirty_rects; i++) {
if (qemu_spice_rect_is_empty(qxl->dirty+i)) {
diff --git a/hw/qxl.c b/hw/qxl.c
index 47a162e479..c3540c3d50 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -1370,7 +1370,7 @@ async_common:
case QXL_IO_DESTROY_SURFACE_WAIT:
if (val >= NUM_SURFACES) {
qxl_guest_bug(d, "QXL_IO_DESTROY_SURFACE (async=%d):"
- "%d >= NUM_SURFACES", async, val);
+ "%" PRIu64 " >= NUM_SURFACES", async, val);
goto cancel_async;
}
qxl_spice_destroy_surface_wait(d, val, async);
@@ -1959,8 +1959,8 @@ static Property qxl_properties[] = {
DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0),
DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0),
DEFINE_PROP_UINT32("ram_size_mb", PCIQXLDevice, ram_size_mb, -1),
- DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, 0),
- DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, 0),
+ DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, -1),
+ DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/qxl.h b/hw/qxl.h
index 11a0db3f7d..cbb1e2d6d4 100644
--- a/hw/qxl.h
+++ b/hw/qxl.h
@@ -127,7 +127,7 @@ typedef struct PCIQXLDevice {
/* qxl.c */
void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id);
-void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...);
+void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...) GCC_FMT_ATTR(2, 3);
void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id,
struct QXLRect *area, struct QXLRect *dirty_rects,
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 8e76c5d32c..dbdb99ce35 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -1200,6 +1200,7 @@ static const char *scsi_command_name(uint8_t cmd)
[ UNMAP ] = "UNMAP",
[ READ_TOC ] = "READ_TOC",
[ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT",
+ [ SANITIZE ] = "SANITIZE",
[ GET_CONFIGURATION ] = "GET_CONFIGURATION",
[ LOG_SELECT ] = "LOG_SELECT",
[ LOG_SENSE ] = "LOG_SENSE",
@@ -1430,15 +1431,18 @@ static char *scsibus_get_dev_path(DeviceState *dev)
SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev);
DeviceState *hba = dev->parent_bus->parent;
char *id = NULL;
+ char *path;
if (hba && hba->parent_bus && hba->parent_bus->info->get_dev_path) {
id = hba->parent_bus->info->get_dev_path(hba);
}
if (id) {
- return g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun);
+ path = g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun);
} else {
- return g_strdup_printf("%d:%d:%d", d->channel, d->id, d->lun);
+ path = g_strdup_printf("%d:%d:%d", d->channel, d->id, d->lun);
}
+ g_free(id);
+ return path;
}
static char *scsibus_get_fw_dev_path(DeviceState *dev)
diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h
index 354ed7b55b..ca24192d53 100644
--- a/hw/scsi-defs.h
+++ b/hw/scsi-defs.h
@@ -78,6 +78,7 @@
#define READ_TOC 0x43
#define REPORT_DENSITY_SUPPORT 0x44
#define GET_CONFIGURATION 0x46
+#define SANITIZE 0x48
#define GET_EVENT_STATUS_NOTIFICATION 0x4a
#define LOG_SELECT 0x4c
#define LOG_SENSE 0x4d
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 9949786e52..a029ab6e84 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -55,6 +55,7 @@ typedef struct SCSIDiskReq {
uint64_t sector;
uint32_t sector_count;
uint32_t buflen;
+ bool started;
struct iovec iov;
QEMUIOVector qiov;
BlockAcctCookie acct;
@@ -153,14 +154,80 @@ static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req)
qemu_iovec_init_external(&r->qiov, &r->iov, 1);
}
-static void scsi_dma_complete(void *opaque, int ret)
+static void scsi_flush_complete(void * opaque, int ret)
{
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
- if (ret) {
+ if (ret < 0) {
+ if (scsi_handle_rw_error(r, -ret)) {
+ goto done;
+ }
+ }
+
+ scsi_req_complete(&r->req, GOOD);
+
+done:
+ if (!r->req.io_canceled) {
+ scsi_req_unref(&r->req);
+ }
+}
+
+static bool scsi_is_cmd_fua(SCSICommand *cmd)
+{
+ switch (cmd->buf[0]) {
+ case READ_10:
+ case READ_12:
+ case READ_16:
+ case WRITE_10:
+ case WRITE_12:
+ case WRITE_16:
+ return (cmd->buf[1] & 8) != 0;
+
+ case VERIFY_10:
+ case VERIFY_12:
+ case VERIFY_16:
+ case WRITE_VERIFY_10:
+ case WRITE_VERIFY_12:
+ case WRITE_VERIFY_16:
+ return true;
+
+ case READ_6:
+ case WRITE_6:
+ default:
+ return false;
+ }
+}
+
+static void scsi_write_do_fua(SCSIDiskReq *r)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+ if (scsi_is_cmd_fua(&r->req.cmd)) {
+ bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
+ r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_flush_complete, r);
+ return;
+ }
+
+ scsi_req_complete(&r->req, GOOD);
+ if (!r->req.io_canceled) {
+ scsi_req_unref(&r->req);
+ }
+}
+
+static void scsi_dma_complete(void *opaque, int ret)
+{
+ SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+
+ if (r->req.aiocb != NULL) {
+ r->req.aiocb = NULL;
+ bdrv_acct_done(s->qdev.conf.bs, &r->acct);
+ }
+
+ if (ret < 0) {
if (scsi_handle_rw_error(r, -ret)) {
goto done;
}
@@ -168,10 +235,17 @@ static void scsi_dma_complete(void *opaque, int ret)
r->sector += r->sector_count;
r->sector_count = 0;
- scsi_req_complete(&r->req, GOOD);
+ if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
+ scsi_write_do_fua(r);
+ return;
+ } else {
+ scsi_req_complete(&r->req, GOOD);
+ }
done:
- scsi_req_unref(&r->req);
+ if (!r->req.io_canceled) {
+ scsi_req_unref(&r->req);
+ }
}
static void scsi_read_complete(void * opaque, int ret)
@@ -185,7 +259,7 @@ static void scsi_read_complete(void * opaque, int ret)
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
}
- if (ret) {
+ if (ret < 0) {
if (scsi_handle_rw_error(r, -ret)) {
goto done;
}
@@ -204,10 +278,12 @@ done:
}
}
-static void scsi_flush_complete(void * opaque, int ret)
+/* Actually issue a read to the block device. */
+static void scsi_do_read(void *opaque, int ret)
{
- SCSIDiskReq *r = (SCSIDiskReq *)opaque;
+ SCSIDiskReq *r = opaque;
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
+ uint32_t n;
if (r->req.aiocb != NULL) {
r->req.aiocb = NULL;
@@ -220,7 +296,17 @@ static void scsi_flush_complete(void * opaque, int ret)
}
}
- scsi_req_complete(&r->req, GOOD);
+ if (r->req.sg) {
+ dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ);
+ r->req.resid -= r->req.sg->size;
+ r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector,
+ scsi_dma_complete, r);
+ } else {
+ n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
+ bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
+ r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
+ scsi_read_complete, r);
+ }
done:
if (!r->req.io_canceled) {
@@ -233,11 +319,12 @@ static void scsi_read_data(SCSIRequest *req)
{
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
- uint32_t n;
+ bool first;
if (r->sector_count == (uint32_t)-1) {
DPRINTF("Read buf_len=%zd\n", r->iov.iov_len);
r->sector_count = 0;
+ r->started = true;
scsi_req_data(&r->req, r->iov.iov_len);
return;
}
@@ -264,16 +351,13 @@ static void scsi_read_data(SCSIRequest *req)
return;
}
- if (r->req.sg) {
- dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ);
- r->req.resid -= r->req.sg->size;
- r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector,
- scsi_dma_complete, r);
+ first = !r->started;
+ r->started = true;
+ if (first && scsi_is_cmd_fua(&r->req.cmd)) {
+ bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
+ r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_do_read, r);
} else {
- n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
- bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
- r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
- scsi_read_complete, r);
+ scsi_do_read(r, 0);
}
}
@@ -333,7 +417,7 @@ static void scsi_write_complete(void * opaque, int ret)
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
}
- if (ret) {
+ if (ret < 0) {
if (scsi_handle_rw_error(r, -ret)) {
goto done;
}
@@ -343,7 +427,8 @@ static void scsi_write_complete(void * opaque, int ret)
r->sector += n;
r->sector_count -= n;
if (r->sector_count == 0) {
- scsi_req_complete(&r->req, GOOD);
+ scsi_write_do_fua(r);
+ return;
} else {
scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, r->qiov.size);
@@ -375,6 +460,7 @@ static void scsi_write_data(SCSIRequest *req)
if (!r->req.sg && !r->qiov.size) {
/* Called for the first time. Ask the driver to send us more data. */
+ r->started = true;
scsi_write_complete(r, 0);
return;
}
@@ -383,6 +469,16 @@ static void scsi_write_data(SCSIRequest *req)
return;
}
+ if (r->req.cmd.buf[0] == VERIFY_10 || r->req.cmd.buf[0] == VERIFY_12 ||
+ r->req.cmd.buf[0] == VERIFY_16) {
+ if (r->req.sg) {
+ scsi_dma_complete(r, 0);
+ } else {
+ scsi_write_complete(r, 0);
+ }
+ return;
+ }
+
if (r->req.sg) {
dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_WRITE);
r->req.resid -= r->req.sg->size;
@@ -531,8 +627,8 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
{
outbuf[3] = buflen = 8;
outbuf[4] = 0;
- outbuf[5] = 0x40; /* write same with unmap supported */
- outbuf[6] = 0;
+ outbuf[5] = 0x60; /* write_same 10/16 supported */
+ outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1;
outbuf[7] = 0;
break;
}
@@ -984,11 +1080,12 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint64_t nb_sectors;
- int page, dbd, buflen, ret, page_control;
+ bool dbd;
+ int page, buflen, ret, page_control;
uint8_t *p;
uint8_t dev_specific_param;
- dbd = r->req.cmd.buf[1] & 0x8;
+ dbd = (r->req.cmd.buf[1] & 0x8) != 0;
page = r->req.cmd.buf[2] & 0x3f;
page_control = (r->req.cmd.buf[2] & 0xc0) >> 6;
DPRINTF("Mode Sense(%d) (page %d, xfer %zd, page_control %d)\n",
@@ -996,10 +1093,16 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
memset(outbuf, 0, r->req.cmd.xfer);
p = outbuf;
- if (bdrv_is_read_only(s->qdev.conf.bs)) {
- dev_specific_param = 0x80; /* Readonly. */
+ if (s->qdev.type == TYPE_DISK) {
+ dev_specific_param = 0x10; /* DPOFUA */
+ if (bdrv_is_read_only(s->qdev.conf.bs)) {
+ dev_specific_param |= 0x80; /* Readonly. */
+ }
} else {
+ /* MMC prescribes that CD/DVD drives have no block descriptors,
+ * and defines no device-specific parameter. */
dev_specific_param = 0x00;
+ dbd = true;
}
if (r->req.cmd.buf[0] == MODE_SENSE) {
@@ -1014,9 +1117,8 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
p += 8;
}
- /* MMC prescribes that CD/DVD drives have no block descriptors. */
bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
- if (!dbd && s->qdev.type == TYPE_DISK && nb_sectors) {
+ if (!dbd && nb_sectors) {
if (r->req.cmd.buf[0] == MODE_SENSE) {
outbuf[3] = 8; /* Block descriptor length */
} else { /* MODE_SENSE_10 */
@@ -1306,8 +1408,6 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r)
}
DPRINTF("Unsupported Service Action In\n");
goto illegal_request;
- case VERIFY_10:
- break;
default:
scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
return -1;
@@ -1391,7 +1491,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
case MECHANISM_STATUS:
case SERVICE_ACTION_IN_16:
case REQUEST_SENSE:
- case VERIFY_10:
rc = scsi_disk_emulate_command(r);
if (rc < 0) {
return 0;
@@ -1417,6 +1516,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512);
r->sector_count = len * (s->qdev.blocksize / 512);
break;
+ case VERIFY_10:
+ case VERIFY_12:
+ case VERIFY_16:
case WRITE_6:
case WRITE_10:
case WRITE_12:
@@ -1456,10 +1558,11 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
goto illegal_lba;
}
break;
+ case WRITE_SAME_10:
case WRITE_SAME_16:
len = r->req.cmd.xfer / s->qdev.blocksize;
- DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n",
+ DPRINTF("WRITE SAME() (sector %" PRId64 ", count %d)\n",
r->req.cmd.lba, len);
if (r->req.cmd.lba > s->qdev.max_lba) {
@@ -1767,6 +1870,9 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
case READ_10:
case READ_12:
case READ_16:
+ case VERIFY_10:
+ case VERIFY_12:
+ case VERIFY_16:
case WRITE_6:
case WRITE_10:
case WRITE_12:
diff --git a/hw/usb.h b/hw/usb.h
index e95085f0b3..ae7ccda18c 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -182,12 +182,17 @@ struct USBEndpoint {
QTAILQ_HEAD(, USBPacket) queue;
};
+enum USBDeviceFlags {
+ USB_DEV_FLAG_FULL_PATH,
+};
+
/* definition of a USB device */
struct USBDevice {
DeviceState qdev;
USBPort *port;
char *port_path;
void *opaque;
+ uint32_t flags;
/* Actual connected speed */
int speed;
diff --git a/hw/usb/bus.c b/hw/usb/bus.c
index d3f835895d..2068640a58 100644
--- a/hw/usb/bus.c
+++ b/hw/usb/bus.c
@@ -19,6 +19,8 @@ static struct BusInfo usb_bus_info = {
.get_fw_dev_path = usb_get_fw_dev_path,
.props = (Property[]) {
DEFINE_PROP_STRING("port", USBDevice, port_path),
+ DEFINE_PROP_BIT("full-path", USBDevice, flags,
+ USB_DEV_FLAG_FULL_PATH, true),
DEFINE_PROP_END_OF_LIST()
},
};
@@ -460,7 +462,20 @@ static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
static char *usb_get_dev_path(DeviceState *qdev)
{
USBDevice *dev = USB_DEVICE(qdev);
- return g_strdup(dev->port->path);
+ DeviceState *hcd = qdev->parent_bus->parent;
+ char *id = NULL;
+
+ if ((dev->flags & (1 << USB_DEV_FLAG_FULL_PATH)) &&
+ hcd && hcd->parent_bus && hcd->parent_bus->info->get_dev_path) {
+ id = hcd->parent_bus->info->get_dev_path(hcd);
+ }
+ if (id) {
+ char *ret = g_strdup_printf("%s/%s", id, dev->port->path);
+ g_free(id);
+ return ret;
+ } else {
+ return g_strdup(dev->port->path);
+ }
}
static char *usb_get_fw_dev_path(DeviceState *qdev)
diff --git a/hw/usb/core.c b/hw/usb/core.c
index a4048fe3e0..9a14a53852 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -484,12 +484,17 @@ void usb_packet_check_state(USBPacket *p, USBPacketState expected)
void usb_packet_set_state(USBPacket *p, USBPacketState state)
{
- USBDevice *dev = p->ep->dev;
- USBBus *bus = usb_bus_from_device(dev);
-
- trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr, p,
- usb_packet_state_name(p->state),
- usb_packet_state_name(state));
+ if (p->ep) {
+ USBDevice *dev = p->ep->dev;
+ USBBus *bus = usb_bus_from_device(dev);
+ trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr, p,
+ usb_packet_state_name(p->state),
+ usb_packet_state_name(state));
+ } else {
+ trace_usb_packet_state_change(-1, "", -1, p,
+ usb_packet_state_name(p->state),
+ usb_packet_state_name(state));
+ }
p->state = state;
}
diff --git a/hw/usb/desc.c b/hw/usb/desc.c
index 9847a75b83..3c77368cb0 100644
--- a/hw/usb/desc.c
+++ b/hw/usb/desc.c
@@ -18,32 +18,33 @@ int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
uint8_t *dest, size_t len)
{
uint8_t bLength = 0x12;
+ USBDescriptor *d = (void *)dest;
if (len < bLength) {
return -1;
}
- dest[0x00] = bLength;
- dest[0x01] = USB_DT_DEVICE;
-
- dest[0x02] = usb_lo(dev->bcdUSB);
- dest[0x03] = usb_hi(dev->bcdUSB);
- dest[0x04] = dev->bDeviceClass;
- dest[0x05] = dev->bDeviceSubClass;
- dest[0x06] = dev->bDeviceProtocol;
- dest[0x07] = dev->bMaxPacketSize0;
-
- dest[0x08] = usb_lo(id->idVendor);
- dest[0x09] = usb_hi(id->idVendor);
- dest[0x0a] = usb_lo(id->idProduct);
- dest[0x0b] = usb_hi(id->idProduct);
- dest[0x0c] = usb_lo(id->bcdDevice);
- dest[0x0d] = usb_hi(id->bcdDevice);
- dest[0x0e] = id->iManufacturer;
- dest[0x0f] = id->iProduct;
- dest[0x10] = id->iSerialNumber;
-
- dest[0x11] = dev->bNumConfigurations;
+ d->bLength = bLength;
+ d->bDescriptorType = USB_DT_DEVICE;
+
+ d->u.device.bcdUSB_lo = usb_lo(dev->bcdUSB);
+ d->u.device.bcdUSB_hi = usb_hi(dev->bcdUSB);
+ d->u.device.bDeviceClass = dev->bDeviceClass;
+ d->u.device.bDeviceSubClass = dev->bDeviceSubClass;
+ d->u.device.bDeviceProtocol = dev->bDeviceProtocol;
+ d->u.device.bMaxPacketSize0 = dev->bMaxPacketSize0;
+
+ d->u.device.idVendor_lo = usb_lo(id->idVendor);
+ d->u.device.idVendor_hi = usb_hi(id->idVendor);
+ d->u.device.idProduct_lo = usb_lo(id->idProduct);
+ d->u.device.idProduct_hi = usb_hi(id->idProduct);
+ d->u.device.bcdDevice_lo = usb_lo(id->bcdDevice);
+ d->u.device.bcdDevice_hi = usb_hi(id->bcdDevice);
+ d->u.device.iManufacturer = id->iManufacturer;
+ d->u.device.iProduct = id->iProduct;
+ d->u.device.iSerialNumber = id->iSerialNumber;
+
+ d->u.device.bNumConfigurations = dev->bNumConfigurations;
return bLength;
}
@@ -52,22 +53,23 @@ int usb_desc_device_qualifier(const USBDescDevice *dev,
uint8_t *dest, size_t len)
{
uint8_t bLength = 0x0a;
+ USBDescriptor *d = (void *)dest;
if (len < bLength) {
return -1;
}
- dest[0x00] = bLength;
- dest[0x01] = USB_DT_DEVICE_QUALIFIER;
+ d->bLength = bLength;
+ d->bDescriptorType = USB_DT_DEVICE_QUALIFIER;
- dest[0x02] = usb_lo(dev->bcdUSB);
- dest[0x03] = usb_hi(dev->bcdUSB);
- dest[0x04] = dev->bDeviceClass;
- dest[0x05] = dev->bDeviceSubClass;
- dest[0x06] = dev->bDeviceProtocol;
- dest[0x07] = dev->bMaxPacketSize0;
- dest[0x08] = dev->bNumConfigurations;
- dest[0x09] = 0; /* reserved */
+ d->u.device_qualifier.bcdUSB_lo = usb_lo(dev->bcdUSB);
+ d->u.device_qualifier.bcdUSB_hi = usb_hi(dev->bcdUSB);
+ d->u.device_qualifier.bDeviceClass = dev->bDeviceClass;
+ d->u.device_qualifier.bDeviceSubClass = dev->bDeviceSubClass;
+ d->u.device_qualifier.bDeviceProtocol = dev->bDeviceProtocol;
+ d->u.device_qualifier.bMaxPacketSize0 = dev->bMaxPacketSize0;
+ d->u.device_qualifier.bNumConfigurations = dev->bNumConfigurations;
+ d->u.device_qualifier.bReserved = 0;
return bLength;
}
@@ -76,22 +78,24 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
{
uint8_t bLength = 0x09;
uint16_t wTotalLength = 0;
+ USBDescriptor *d = (void *)dest;
int i, rc;
if (len < bLength) {
return -1;
}
- dest[0x00] = bLength;
- dest[0x01] = USB_DT_CONFIG;
- dest[0x04] = conf->bNumInterfaces;
- dest[0x05] = conf->bConfigurationValue;
- dest[0x06] = conf->iConfiguration;
- dest[0x07] = conf->bmAttributes;
- dest[0x08] = conf->bMaxPower;
+ d->bLength = bLength;
+ d->bDescriptorType = USB_DT_CONFIG;
+
+ d->u.config.bNumInterfaces = conf->bNumInterfaces;
+ d->u.config.bConfigurationValue = conf->bConfigurationValue;
+ d->u.config.iConfiguration = conf->iConfiguration;
+ d->u.config.bmAttributes = conf->bmAttributes;
+ d->u.config.bMaxPower = conf->bMaxPower;
wTotalLength += bLength;
- /* handle grouped interfaces if any*/
+ /* handle grouped interfaces if any */
for (i = 0; i < conf->nif_groups; i++) {
rc = usb_desc_iface_group(&(conf->if_groups[i]),
dest + wTotalLength,
@@ -111,8 +115,8 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
wTotalLength += rc;
}
- dest[0x02] = usb_lo(wTotalLength);
- dest[0x03] = usb_hi(wTotalLength);
+ d->u.config.wTotalLength_lo = usb_lo(wTotalLength);
+ d->u.config.wTotalLength_hi = usb_hi(wTotalLength);
return wTotalLength;
}
@@ -155,20 +159,22 @@ int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
{
uint8_t bLength = 0x09;
int i, rc, pos = 0;
+ USBDescriptor *d = (void *)dest;
if (len < bLength) {
return -1;
}
- dest[0x00] = bLength;
- dest[0x01] = USB_DT_INTERFACE;
- dest[0x02] = iface->bInterfaceNumber;
- dest[0x03] = iface->bAlternateSetting;
- dest[0x04] = iface->bNumEndpoints;
- dest[0x05] = iface->bInterfaceClass;
- dest[0x06] = iface->bInterfaceSubClass;
- dest[0x07] = iface->bInterfaceProtocol;
- dest[0x08] = iface->iInterface;
+ d->bLength = bLength;
+ d->bDescriptorType = USB_DT_INTERFACE;
+
+ d->u.interface.bInterfaceNumber = iface->bInterfaceNumber;
+ d->u.interface.bAlternateSetting = iface->bAlternateSetting;
+ d->u.interface.bNumEndpoints = iface->bNumEndpoints;
+ d->u.interface.bInterfaceClass = iface->bInterfaceClass;
+ d->u.interface.bInterfaceSubClass = iface->bInterfaceSubClass;
+ d->u.interface.bInterfaceProtocol = iface->bInterfaceProtocol;
+ d->u.interface.iInterface = iface->iInterface;
pos += bLength;
for (i = 0; i < iface->ndesc; i++) {
@@ -194,21 +200,23 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
{
uint8_t bLength = ep->is_audio ? 0x09 : 0x07;
uint8_t extralen = ep->extra ? ep->extra[0] : 0;
+ USBDescriptor *d = (void *)dest;
if (len < bLength + extralen) {
return -1;
}
- dest[0x00] = bLength;
- dest[0x01] = USB_DT_ENDPOINT;
- dest[0x02] = ep->bEndpointAddress;
- dest[0x03] = ep->bmAttributes;
- dest[0x04] = usb_lo(ep->wMaxPacketSize);
- dest[0x05] = usb_hi(ep->wMaxPacketSize);
- dest[0x06] = ep->bInterval;
+ d->bLength = bLength;
+ d->bDescriptorType = USB_DT_ENDPOINT;
+
+ d->u.endpoint.bEndpointAddress = ep->bEndpointAddress;
+ d->u.endpoint.bmAttributes = ep->bmAttributes;
+ d->u.endpoint.wMaxPacketSize_lo = usb_lo(ep->wMaxPacketSize);
+ d->u.endpoint.wMaxPacketSize_hi = usb_hi(ep->wMaxPacketSize);
+ d->u.endpoint.bInterval = ep->bInterval;
if (ep->is_audio) {
- dest[0x07] = ep->bRefresh;
- dest[0x08] = ep->bSynchAddress;
+ d->u.endpoint.bRefresh = ep->bRefresh;
+ d->u.endpoint.bSynchAddress = ep->bSynchAddress;
}
if (ep->extra) {
memcpy(dest + bLength, ep->extra, extralen);
diff --git a/hw/usb/desc.h b/hw/usb/desc.h
index d6e07ea5d2..d164e8f891 100644
--- a/hw/usb/desc.h
+++ b/hw/usb/desc.h
@@ -3,6 +3,69 @@
#include <inttypes.h>
+/* binary representation */
+typedef struct USBDescriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ union {
+ struct {
+ uint8_t bcdUSB_lo;
+ uint8_t bcdUSB_hi;
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t bMaxPacketSize0;
+ uint8_t idVendor_lo;
+ uint8_t idVendor_hi;
+ uint8_t idProduct_lo;
+ uint8_t idProduct_hi;
+ uint8_t bcdDevice_lo;
+ uint8_t bcdDevice_hi;
+ uint8_t iManufacturer;
+ uint8_t iProduct;
+ uint8_t iSerialNumber;
+ uint8_t bNumConfigurations;
+ } device;
+ struct {
+ uint8_t bcdUSB_lo;
+ uint8_t bcdUSB_hi;
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t bMaxPacketSize0;
+ uint8_t bNumConfigurations;
+ uint8_t bReserved;
+ } device_qualifier;
+ struct {
+ uint8_t wTotalLength_lo;
+ uint8_t wTotalLength_hi;
+ uint8_t bNumInterfaces;
+ uint8_t bConfigurationValue;
+ uint8_t iConfiguration;
+ uint8_t bmAttributes;
+ uint8_t bMaxPower;
+ } config;
+ struct {
+ uint8_t bInterfaceNumber;
+ uint8_t bAlternateSetting;
+ uint8_t bNumEndpoints;
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+ uint8_t iInterface;
+ } interface;
+ struct {
+ uint8_t bEndpointAddress;
+ uint8_t bmAttributes;
+ uint8_t wMaxPacketSize_lo;
+ uint8_t wMaxPacketSize_hi;
+ uint8_t bInterval;
+ uint8_t bRefresh; /* only audio ep */
+ uint8_t bSynchAddress; /* only audio ep */
+ } endpoint;
+ } u;
+} QEMU_PACKED USBDescriptor;
+
struct USBDescID {
uint16_t idVendor;
uint16_t idProduct;
diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c
index eb4e711207..9c9166551e 100644
--- a/hw/usb/dev-hub.c
+++ b/hw/usb/dev-hub.c
@@ -22,11 +22,10 @@
* THE SOFTWARE.
*/
#include "qemu-common.h"
+#include "trace.h"
#include "hw/usb.h"
#include "hw/usb/desc.h"
-//#define DEBUG
-
#define NUM_PORTS 8
typedef struct USBHubPort {
@@ -157,6 +156,7 @@ static void usb_hub_attach(USBPort *port1)
USBHubState *s = port1->opaque;
USBHubPort *port = &s->ports[port1->index];
+ trace_usb_hub_attach(s->dev.addr, port1->index + 1);
port->wPortStatus |= PORT_STAT_CONNECTION;
port->wPortChange |= PORT_STAT_C_CONNECTION;
if (port->port.dev->speed == USB_SPEED_LOW) {
@@ -172,6 +172,7 @@ static void usb_hub_detach(USBPort *port1)
USBHubState *s = port1->opaque;
USBHubPort *port = &s->ports[port1->index];
+ trace_usb_hub_detach(s->dev.addr, port1->index + 1);
usb_wakeup(s->intr);
/* Let upstream know the device on this port is gone */
@@ -247,6 +248,7 @@ static void usb_hub_handle_reset(USBDevice *dev)
USBHubPort *port;
int i;
+ trace_usb_hub_reset(s->dev.addr);
for (i = 0; i < NUM_PORTS; i++) {
port = s->ports + i;
port->wPortStatus = PORT_STAT_POWER;
@@ -261,12 +263,39 @@ static void usb_hub_handle_reset(USBDevice *dev)
}
}
+static const char *feature_name(int feature)
+{
+ static const char *name[] = {
+ [PORT_CONNECTION] = "connection",
+ [PORT_ENABLE] = "enable",
+ [PORT_SUSPEND] = "suspend",
+ [PORT_OVERCURRENT] = "overcurrent",
+ [PORT_RESET] = "reset",
+ [PORT_POWER] = "power",
+ [PORT_LOWSPEED] = "lowspeed",
+ [PORT_HIGHSPEED] = "highspeed",
+ [PORT_C_CONNECTION] = "change connection",
+ [PORT_C_ENABLE] = "change enable",
+ [PORT_C_SUSPEND] = "change suspend",
+ [PORT_C_OVERCURRENT] = "change overcurrent",
+ [PORT_C_RESET] = "change reset",
+ [PORT_TEST] = "test",
+ [PORT_INDICATOR] = "indicator",
+ };
+ if (feature < 0 || feature >= ARRAY_SIZE(name)) {
+ return "?";
+ }
+ return name[feature] ?: "?";
+}
+
static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data)
{
USBHubState *s = (USBHubState *)dev;
int ret;
+ trace_usb_hub_control(s->dev.addr, request, value, index, length);
+
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) {
return ret;
@@ -295,6 +324,9 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
goto fail;
}
port = &s->ports[n];
+ trace_usb_hub_get_port_status(s->dev.addr, index,
+ port->wPortStatus,
+ port->wPortChange);
data[0] = port->wPortStatus;
data[1] = port->wPortStatus >> 8;
data[2] = port->wPortChange;
@@ -315,6 +347,10 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
unsigned int n = index - 1;
USBHubPort *port;
USBDevice *dev;
+
+ trace_usb_hub_set_port_feature(s->dev.addr, index,
+ feature_name(value));
+
if (n >= NUM_PORTS) {
goto fail;
}
@@ -345,6 +381,9 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
unsigned int n = index - 1;
USBHubPort *port;
+ trace_usb_hub_clear_port_feature(s->dev.addr, index,
+ feature_name(value));
+
if (n >= NUM_PORTS) {
goto fail;
}
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 60f9f5bdb5..23631a47c9 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -403,7 +403,6 @@ struct EHCIState {
/*
* Internal states, shadow registers, etc
*/
- uint32_t sofv;
QEMUTimer *frame_timer;
int attach_poll_counter;
int astate; // Current state in asynchronous schedule
@@ -797,7 +796,6 @@ static void ehci_child_detach(USBPort *port, USBDevice *child)
if (portsc & PORTSC_POWNER) {
USBPort *companion = s->companion_ports[port->index];
companion->ops->child_detach(companion, child);
- companion->dev = NULL;
return;
}
@@ -1103,10 +1101,6 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
val &= USBINTR_MASK;
break;
- case FRINDEX:
- s->sofv = val >> 3;
- break;
-
case CONFIGFLAG:
val &= 0x1;
if (val) {
@@ -2015,7 +2009,6 @@ static void ehci_advance_state(EHCIState *ehci,
fprintf(stderr, "processing error - resetting ehci HC\n");
ehci_reset(ehci);
again = 0;
- assert(0);
}
}
while (again);
@@ -2150,13 +2143,14 @@ static void ehci_frame_timer(void *opaque)
if ( !(ehci->usbsts & USBSTS_HALT)) {
ehci->frindex += 8;
- if (ehci->frindex > 0x00001fff) {
- ehci->frindex = 0;
+ if (ehci->frindex == 0x00002000) {
ehci_set_interrupt(ehci, USBSTS_FLR);
}
- ehci->sofv = (ehci->frindex - 1) >> 3;
- ehci->sofv &= 0x000003ff;
+ if (ehci->frindex == 0x00004000) {
+ ehci_set_interrupt(ehci, USBSTS_FLR);
+ ehci->frindex = 0;
+ }
}
if (frames - i > ehci->maxframes) {
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index e55dad914c..266d550b9c 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -795,7 +795,8 @@ out:
return TD_RESULT_NEXT_QH;
}
-static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *int_mask)
+static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
+ uint32_t *int_mask, bool queuing)
{
UHCIAsync *async;
int len = 0, max_len;
@@ -814,6 +815,12 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
if (!async->done)
return TD_RESULT_ASYNC_CONT;
+ if (queuing) {
+ /* we are busy filling the queue, we are not prepared
+ to consume completed packages then, just leave them
+ in async state */
+ return TD_RESULT_ASYNC_CONT;
+ }
uhci_async_unlink(async);
goto done;
@@ -964,7 +971,10 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td)
break;
}
trace_usb_uhci_td_queue(plink & ~0xf, ptd.ctrl, ptd.token);
- ret = uhci_handle_td(s, plink, &ptd, &int_mask);
+ ret = uhci_handle_td(s, plink, &ptd, &int_mask, true);
+ if (ret == TD_RESULT_ASYNC_CONT) {
+ break;
+ }
assert(ret == TD_RESULT_ASYNC_START);
assert(int_mask == 0);
plink = ptd.link;
@@ -1045,7 +1055,7 @@ static void uhci_process_frame(UHCIState *s)
trace_usb_uhci_td_load(curr_qh & ~0xf, link & ~0xf, td.ctrl, td.token);
old_td_ctrl = td.ctrl;
- ret = uhci_handle_td(s, link, &td, &int_mask);
+ ret = uhci_handle_td(s, link, &td, &int_mask, false);
if (old_td_ctrl != td.ctrl) {
/* update the status bits of the TD */
val = cpu_to_le32(td.ctrl);
diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c
index 90919c242a..061a1b7825 100644
--- a/hw/usb/host-linux.c
+++ b/hw/usb/host-linux.c
@@ -42,6 +42,7 @@
#include <linux/usbdevice_fs.h>
#include <linux/version.h>
#include "hw/usb.h"
+#include "hw/usb/desc.h"
/* We redefine it to avoid version problems */
struct usb_ctrltransfer {
@@ -94,6 +95,10 @@ struct USBAutoFilter {
uint32_t product_id;
};
+enum USBHostDeviceOptions {
+ USB_HOST_OPT_PIPELINE,
+};
+
typedef struct USBHostDevice {
USBDevice dev;
int fd;
@@ -104,6 +109,7 @@ typedef struct USBHostDevice {
int descr_len;
int closing;
uint32_t iso_urb_count;
+ uint32_t options;
Notifier exit;
struct endp_data ep_in[USB_MAX_ENDPOINTS];
@@ -115,6 +121,7 @@ typedef struct USBHostDevice {
int addr;
char port[MAX_PORTLEN];
struct USBAutoFilter match;
+ int32_t bootindex;
int seen, errcount;
QTAILQ_ENTRY(USBHostDevice) next;
@@ -374,10 +381,10 @@ static void async_complete(void *opaque)
}
if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
- trace_usb_host_req_complete(s->bus_num, s->addr, p->result);
+ trace_usb_host_req_complete(s->bus_num, s->addr, p, p->result);
usb_generic_async_ctrl_complete(&s->dev, p);
} else if (!aurb->more) {
- trace_usb_host_req_complete(s->bus_num, s->addr, p->result);
+ trace_usb_host_req_complete(s->bus_num, s->addr, p, p->result);
usb_packet_complete(&s->dev, p);
}
}
@@ -391,12 +398,14 @@ static void usb_host_async_cancel(USBDevice *dev, USBPacket *p)
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
AsyncURB *aurb;
+ trace_usb_host_req_canceled(s->bus_num, s->addr, p);
+
QLIST_FOREACH(aurb, &s->aurbs, next) {
if (p != aurb->packet) {
continue;
}
- DPRINTF("husb: async cancel: packet %p, aurb %p\n", p, aurb);
+ trace_usb_host_urb_canceled(s->bus_num, s->addr, aurb);
/* Mark it as dead (see async_complete above) */
aurb->packet = NULL;
@@ -844,12 +853,12 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
uint8_t *pbuf;
uint8_t ep;
- trace_usb_host_req_data(s->bus_num, s->addr,
+ trace_usb_host_req_data(s->bus_num, s->addr, p,
p->pid == USB_TOKEN_IN,
p->ep->nr, p->iov.size);
if (!is_valid(s, p->pid, p->ep->nr)) {
- trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
+ trace_usb_host_req_complete(s->bus_num, s->addr, p, USB_RET_NAK);
return USB_RET_NAK;
}
@@ -864,7 +873,7 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &arg);
if (ret < 0) {
perror("USBDEVFS_CLEAR_HALT");
- trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
+ trace_usb_host_req_complete(s->bus_num, s->addr, p, USB_RET_NAK);
return USB_RET_NAK;
}
clear_halt(s, p->pid, p->ep->nr);
@@ -919,11 +928,13 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
switch(errno) {
case ETIMEDOUT:
- trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_NAK);
+ trace_usb_host_req_complete(s->bus_num, s->addr, p,
+ USB_RET_NAK);
return USB_RET_NAK;
case EPIPE:
default:
- trace_usb_host_req_complete(s->bus_num, s->addr, USB_RET_STALL);
+ trace_usb_host_req_complete(s->bus_num, s->addr, p,
+ USB_RET_STALL);
return USB_RET_STALL;
}
}
@@ -1030,17 +1041,23 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
*/
/* Note request is (bRequestType << 8) | bRequest */
- trace_usb_host_req_control(s->bus_num, s->addr, request, value, index);
+ trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index);
switch (request) {
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
- return usb_host_set_address(s, value);
+ ret = usb_host_set_address(s, value);
+ trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret);
+ return ret;
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
- return usb_host_set_config(s, value & 0xff);
+ ret = usb_host_set_config(s, value & 0xff);
+ trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret);
+ return ret;
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
- return usb_host_set_interface(s, index, value);
+ ret = usb_host_set_interface(s, index, value);
+ trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret);
+ return ret;
}
/* The rest are asynchronous */
@@ -1092,120 +1109,128 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
return USB_RET_ASYNC;
}
-static uint8_t usb_linux_get_alt_setting(USBHostDevice *s,
- uint8_t configuration, uint8_t interface)
-{
- char device_name[64], line[1024];
- int alt_setting;
-
- sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port,
- (int)configuration, (int)interface);
-
- if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting",
- device_name)) {
- /* Assume alt 0 on error */
- return 0;
- }
- if (sscanf(line, "%d", &alt_setting) != 1) {
- /* Assume alt 0 on error */
- return 0;
- }
- return alt_setting;
-}
-
/* returns 1 on problem encountered or 0 for success */
static int usb_linux_update_endp_table(USBHostDevice *s)
{
- uint8_t *descriptors;
- uint8_t devep, type, alt_interface;
- uint16_t raw;
- int interface, length, i, ep, pid;
+ static const char *tname[] = {
+ [USB_ENDPOINT_XFER_CONTROL] = "control",
+ [USB_ENDPOINT_XFER_ISOC] = "isoc",
+ [USB_ENDPOINT_XFER_BULK] = "bulk",
+ [USB_ENDPOINT_XFER_INT] = "int",
+ };
+ uint8_t devep, type;
+ uint16_t mps, v, p;
+ int ep, pid;
+ unsigned int i, configuration = -1, interface = -1, altsetting = -1;
struct endp_data *epd;
+ USBDescriptor *d;
+ bool active = false;
usb_ep_init(&s->dev);
- if (s->dev.configuration == 0) {
- /* not configured yet -- leave all endpoints disabled */
- return 0;
- }
-
- /* get the desired configuration, interface, and endpoint descriptors
- * from device description */
- descriptors = &s->descr[18];
- length = s->descr_len - 18;
- i = 0;
-
- while (i < length) {
- if (descriptors[i + 1] != USB_DT_CONFIG) {
- fprintf(stderr, "invalid descriptor data\n");
- return 1;
- } else if (descriptors[i + 5] != s->dev.configuration) {
- DPRINTF("not requested configuration %d\n", s->dev.configuration);
- i += (descriptors[i + 3] << 8) + descriptors[i + 2];
- continue;
+ for (i = 0;; i += d->bLength) {
+ if (i+2 >= s->descr_len) {
+ break;
}
- i += descriptors[i];
-
- if (descriptors[i + 1] != USB_DT_INTERFACE ||
- (descriptors[i + 1] == USB_DT_INTERFACE &&
- descriptors[i + 4] == 0)) {
- i += descriptors[i];
- continue;
+ d = (void *)(s->descr + i);
+ if (d->bLength < 2) {
+ trace_usb_host_parse_error(s->bus_num, s->addr,
+ "descriptor too short");
+ goto error;
}
-
- interface = descriptors[i + 2];
- alt_interface = usb_linux_get_alt_setting(s, s->dev.configuration,
- interface);
-
- /* the current interface descriptor is the active interface
- * and has endpoints */
- if (descriptors[i + 3] != alt_interface) {
- i += descriptors[i];
- continue;
- }
-
- /* advance to the endpoints */
- while (i < length && descriptors[i +1] != USB_DT_ENDPOINT) {
- i += descriptors[i];
+ if (i + d->bLength > s->descr_len) {
+ trace_usb_host_parse_error(s->bus_num, s->addr,
+ "descriptor too long");
+ goto error;
}
-
- if (i >= length)
+ switch (d->bDescriptorType) {
+ case 0:
+ trace_usb_host_parse_error(s->bus_num, s->addr,
+ "invalid descriptor type");
+ goto error;
+ case USB_DT_DEVICE:
+ if (d->bLength < 0x12) {
+ trace_usb_host_parse_error(s->bus_num, s->addr,
+ "device descriptor too short");
+ goto error;
+ }
+ v = (d->u.device.idVendor_hi << 8) | d->u.device.idVendor_lo;
+ p = (d->u.device.idProduct_hi << 8) | d->u.device.idProduct_lo;
+ trace_usb_host_parse_device(s->bus_num, s->addr, v, p);
break;
-
- while (i < length) {
- if (descriptors[i + 1] != USB_DT_ENDPOINT) {
- break;
+ case USB_DT_CONFIG:
+ if (d->bLength < 0x09) {
+ trace_usb_host_parse_error(s->bus_num, s->addr,
+ "config descriptor too short");
+ goto error;
}
-
- devep = descriptors[i + 2];
+ configuration = d->u.config.bConfigurationValue;
+ active = (configuration == s->dev.configuration);
+ trace_usb_host_parse_config(s->bus_num, s->addr,
+ configuration, active);
+ break;
+ case USB_DT_INTERFACE:
+ if (d->bLength < 0x09) {
+ trace_usb_host_parse_error(s->bus_num, s->addr,
+ "interface descriptor too short");
+ goto error;
+ }
+ interface = d->u.interface.bInterfaceNumber;
+ altsetting = d->u.interface.bAlternateSetting;
+ active = (configuration == s->dev.configuration) &&
+ (altsetting == s->dev.altsetting[interface]);
+ trace_usb_host_parse_interface(s->bus_num, s->addr,
+ interface, altsetting, active);
+ break;
+ case USB_DT_ENDPOINT:
+ if (d->bLength < 0x07) {
+ trace_usb_host_parse_error(s->bus_num, s->addr,
+ "endpoint descriptor too short");
+ goto error;
+ }
+ devep = d->u.endpoint.bEndpointAddress;
pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
ep = devep & 0xf;
if (ep == 0) {
- fprintf(stderr, "usb-linux: invalid ep descriptor, ep == 0\n");
- return 1;
+ trace_usb_host_parse_error(s->bus_num, s->addr,
+ "invalid endpoint address");
+ goto error;
}
- type = descriptors[i + 3] & 0x3;
- raw = descriptors[i + 4] + (descriptors[i + 5] << 8);
- usb_ep_set_max_packet_size(&s->dev, pid, ep, raw);
- assert(usb_ep_get_type(&s->dev, pid, ep) ==
- USB_ENDPOINT_XFER_INVALID);
- usb_ep_set_type(&s->dev, pid, ep, type);
- usb_ep_set_ifnum(&s->dev, pid, ep, interface);
- if (type == USB_ENDPOINT_XFER_BULK) {
- usb_ep_set_pipeline(&s->dev, pid, ep, true);
- }
+ type = d->u.endpoint.bmAttributes & 0x3;
+ mps = d->u.endpoint.wMaxPacketSize_lo |
+ (d->u.endpoint.wMaxPacketSize_hi << 8);
+ trace_usb_host_parse_endpoint(s->bus_num, s->addr, ep,
+ (devep & USB_DIR_IN) ? "in" : "out",
+ tname[type], active);
+
+ if (active) {
+ usb_ep_set_max_packet_size(&s->dev, pid, ep, mps);
+ assert(usb_ep_get_type(&s->dev, pid, ep) ==
+ USB_ENDPOINT_XFER_INVALID);
+ usb_ep_set_type(&s->dev, pid, ep, type);
+ usb_ep_set_ifnum(&s->dev, pid, ep, interface);
+ if ((s->options & (1 << USB_HOST_OPT_PIPELINE)) &&
+ (type == USB_ENDPOINT_XFER_BULK)) {
+ usb_ep_set_pipeline(&s->dev, pid, ep, true);
+ }
- epd = get_endp(s, pid, ep);
- epd->halted = 0;
+ epd = get_endp(s, pid, ep);
+ epd->halted = 0;
+ }
- i += descriptors[i];
+ break;
+ default:
+ trace_usb_host_parse_unknown(s->bus_num, s->addr,
+ d->bLength, d->bDescriptorType);
+ break;
}
}
-#ifdef DEBUG
- usb_ep_dump(&s->dev);
-#endif
return 0;
+
+error:
+ usb_ep_init(&s->dev);
+ return 1;
}
/*
@@ -1403,6 +1428,7 @@ static int usb_host_initfn(USBDevice *dev)
if (s->match.bus_num != 0 && s->match.port != NULL) {
usb_host_claim_port(s);
}
+ add_boot_device_path(s->bootindex, &dev->qdev, NULL);
return 0;
}
@@ -1418,6 +1444,9 @@ static Property usb_host_dev_properties[] = {
DEFINE_PROP_HEX32("vendorid", USBHostDevice, match.vendor_id, 0),
DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0),
DEFINE_PROP_UINT32("isobufs", USBHostDevice, iso_urb_count, 4),
+ DEFINE_PROP_INT32("bootindex", USBHostDevice, bootindex, -1),
+ DEFINE_PROP_BIT("pipeline", USBHostDevice, options,
+ USB_HOST_OPT_PIPELINE, true),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 8e9f175dbb..94ab4632ca 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -74,6 +74,7 @@ struct USBRedirDevice {
CharDriverState *cs;
uint8_t debug;
char *filter_str;
+ int32_t bootindex;
/* Data passed from chardev the fd_read cb to the usbredirparser read cb */
const uint8_t *read_buf;
int read_buf_size;
@@ -835,7 +836,13 @@ static void usbredir_do_attach(void *opaque)
{
USBRedirDevice *dev = opaque;
- usb_device_attach(&dev->dev);
+ if (usb_device_attach(&dev->dev) != 0) {
+ usbredir_device_disconnect(dev);
+ if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
+ usbredirparser_send_filter_reject(dev->parser);
+ usbredirparser_do_write(dev->parser);
+ }
+ }
}
/*
@@ -923,6 +930,7 @@ static int usbredir_initfn(USBDevice *udev)
qemu_chr_add_handlers(dev->cs, usbredir_chardev_can_read,
usbredir_chardev_read, usbredir_chardev_event, dev);
+ add_boot_device_path(dev->bootindex, &udev->qdev, NULL);
return 0;
}
@@ -1452,6 +1460,7 @@ static Property usbredir_properties[] = {
DEFINE_PROP_CHR("chardev", USBRedirDevice, cs),
DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0),
DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str),
+ DEFINE_PROP_INT32("bootindex", USBRedirDevice, bootindex, -1),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c
index 45d54faeb5..e8328f4652 100644
--- a/hw/virtio-scsi.c
+++ b/hw/virtio-scsi.c
@@ -129,12 +129,12 @@ typedef struct {
VirtIOSCSIConf *conf;
SCSIBus bus;
- VirtQueue *ctrl_vq;
- VirtQueue *event_vq;
- VirtQueue *cmd_vq;
uint32_t sense_size;
uint32_t cdb_size;
int resetting;
+ VirtQueue *ctrl_vq;
+ VirtQueue *event_vq;
+ VirtQueue *cmd_vqs[0];
} VirtIOSCSI;
typedef struct VirtIOSCSIReq {
@@ -240,7 +240,10 @@ static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq)
static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq)
{
VirtIOSCSIReq *req = sreq->hba_private;
+ uint32_t n = virtio_queue_get_id(req->vq) - 2;
+ assert(n < req->dev->conf->num_queues);
+ qemu_put_be32s(f, &n);
qemu_put_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
}
@@ -249,10 +252,13 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
SCSIBus *bus = sreq->bus;
VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
VirtIOSCSIReq *req;
+ uint32_t n;
req = g_malloc(sizeof(*req));
+ qemu_get_be32s(f, &n);
+ assert(n < s->conf->num_queues);
qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
- virtio_scsi_parse_req(s, s->cmd_vq, req);
+ virtio_scsi_parse_req(s, s->cmd_vqs[n], req);
scsi_req_ref(sreq);
req->sreq = sreq;
@@ -579,10 +585,12 @@ VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf)
{
VirtIOSCSI *s;
static int virtio_scsi_id;
+ size_t sz;
+ int i;
+ sz = sizeof(VirtIOSCSI) + proxyconf->num_queues * sizeof(VirtQueue *);
s = (VirtIOSCSI *)virtio_common_init("virtio-scsi", VIRTIO_ID_SCSI,
- sizeof(VirtIOSCSIConfig),
- sizeof(VirtIOSCSI));
+ sizeof(VirtIOSCSIConfig), sz);
s->qdev = dev;
s->conf = proxyconf;
@@ -597,8 +605,10 @@ VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf)
virtio_scsi_handle_ctrl);
s->event_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
NULL);
- s->cmd_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
- virtio_scsi_handle_cmd);
+ for (i = 0; i < s->conf->num_queues; i++) {
+ s->cmd_vqs[i] = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
+ virtio_scsi_handle_cmd);
+ }
scsi_bus_new(&s->bus, dev, &virtio_scsi_scsi_info);
if (!dev->hotplugged) {
diff --git a/hw/virtio.c b/hw/virtio.c
index 064aecf553..314abf8a18 100644
--- a/hw/virtio.c
+++ b/hw/virtio.c
@@ -624,6 +624,13 @@ int virtio_queue_get_num(VirtIODevice *vdev, int n)
return vdev->vq[n].vring.num;
}
+int virtio_queue_get_id(VirtQueue *vq)
+{
+ VirtIODevice *vdev = vq->vdev;
+ assert(vq >= &vdev->vq[0] && vq < &vdev->vq[VIRTIO_PCI_QUEUE_MAX]);
+ return vq - &vdev->vq[0];
+}
+
void virtio_queue_notify_vq(VirtQueue *vq)
{
if (vq->vring.desc) {
diff --git a/hw/virtio.h b/hw/virtio.h
index 400c092c95..0aef7d1bc0 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -229,6 +229,7 @@ target_phys_addr_t virtio_queue_get_ring_size(VirtIODevice *vdev, int n);
uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n);
void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx);
VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n);
+int virtio_queue_get_id(VirtQueue *vq);
EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq);
EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq);
void virtio_queue_notify_vq(VirtQueue *vq);