diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/ac97.c | 139 | ||||
-rw-r--r-- | hw/e1000.c | 2 | ||||
-rw-r--r-- | hw/pc_piix.c | 4 | ||||
-rw-r--r-- | hw/pflash_cfi01.c | 1 | ||||
-rw-r--r-- | hw/qxl-render.c | 1 | ||||
-rw-r--r-- | hw/qxl.c | 6 | ||||
-rw-r--r-- | hw/qxl.h | 2 | ||||
-rw-r--r-- | hw/scsi-bus.c | 8 | ||||
-rw-r--r-- | hw/scsi-defs.h | 1 | ||||
-rw-r--r-- | hw/scsi-disk.c | 170 | ||||
-rw-r--r-- | hw/usb.h | 5 | ||||
-rw-r--r-- | hw/usb/bus.c | 17 | ||||
-rw-r--r-- | hw/usb/core.c | 17 | ||||
-rw-r--r-- | hw/usb/desc.c | 126 | ||||
-rw-r--r-- | hw/usb/desc.h | 63 | ||||
-rw-r--r-- | hw/usb/dev-hub.c | 43 | ||||
-rw-r--r-- | hw/usb/hcd-ehci.c | 16 | ||||
-rw-r--r-- | hw/usb/hcd-uhci.c | 16 | ||||
-rw-r--r-- | hw/usb/host-linux.c | 237 | ||||
-rw-r--r-- | hw/usb/redirect.c | 11 | ||||
-rw-r--r-- | hw/virtio-scsi.c | 26 | ||||
-rw-r--r-- | hw/virtio.c | 7 | ||||
-rw-r--r-- | hw/virtio.h | 1 |
23 files changed, 594 insertions, 325 deletions
@@ -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)) { @@ -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(), }; @@ -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: @@ -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); |