diff options
-rwxr-xr-x | configure | 8 | ||||
-rw-r--r-- | hw/qxl-render.c | 170 | ||||
-rw-r--r-- | hw/qxl.c | 215 | ||||
-rw-r--r-- | hw/qxl.h | 31 | ||||
-rw-r--r-- | hw/usb-bt.c | 4 | ||||
-rw-r--r-- | hw/usb-bus.c | 18 | ||||
-rw-r--r-- | hw/usb-ccid.c | 2 | ||||
-rw-r--r-- | hw/usb-desc.c | 20 | ||||
-rw-r--r-- | hw/usb-ehci.c | 71 | ||||
-rw-r--r-- | hw/usb-hid.c | 3 | ||||
-rw-r--r-- | hw/usb-msd.c | 4 | ||||
-rw-r--r-- | hw/usb-net.c | 4 | ||||
-rw-r--r-- | hw/usb-serial.c | 8 | ||||
-rw-r--r-- | hw/usb-uhci.c | 314 | ||||
-rw-r--r-- | hw/usb-xhci.c | 6 | ||||
-rw-r--r-- | hw/usb.c | 27 | ||||
-rw-r--r-- | hw/usb.h | 7 | ||||
-rw-r--r-- | libcacard/vcardt.h | 4 | ||||
-rw-r--r-- | trace-events | 3 | ||||
-rw-r--r-- | ui/sdl.c | 4 | ||||
-rw-r--r-- | ui/spice-core.c | 47 | ||||
-rw-r--r-- | ui/spice-display.c | 57 | ||||
-rw-r--r-- | ui/spice-display.h | 21 | ||||
-rw-r--r-- | usb-bsd.c | 4 | ||||
-rw-r--r-- | usb-linux.c | 4 | ||||
-rw-r--r-- | usb-redir.c | 46 | ||||
-rw-r--r-- | vl.c | 7 |
27 files changed, 670 insertions, 439 deletions
@@ -2554,7 +2554,7 @@ int main(void) { spice_server_new(); return 0; } EOF spice_cflags=$($pkg_config --cflags spice-protocol spice-server 2>/dev/null) spice_libs=$($pkg_config --libs spice-protocol spice-server 2>/dev/null) - if $pkg_config --atleast-version=0.6.0 spice-server >/dev/null 2>&1 && \ + if $pkg_config --atleast-version=0.8.2 spice-server >/dev/null 2>&1 && \ compile_prog "$spice_cflags" "$spice_libs" ; then spice="yes" libs_softmmu="$libs_softmmu $spice_libs" @@ -2578,8 +2578,8 @@ if test "$smartcard" != "no" ; then int main(void) { PK11_FreeSlot(0); return 0; } EOF smartcard_cflags="-I\$(SRC_PATH)/libcacard" - libcacard_libs=$($pkg_config --libs nss 2>/dev/null) - libcacard_cflags=$($pkg_config --cflags nss 2>/dev/null) + libcacard_libs="$($pkg_config --libs nss 2>/dev/null) $glib_libs" + libcacard_cflags="$($pkg_config --cflags nss 2>/dev/null) $glib_cflags" if $pkg_config --atleast-version=3.12.8 nss >/dev/null 2>&1 && \ compile_prog "$smartcard_cflags $libcacard_cflags" "$libcacard_libs"; then smartcard_nss="yes" @@ -2599,7 +2599,7 @@ fi # check for usbredirparser for usb network redirection support if test "$usb_redir" != "no" ; then - if $pkg_config --atleast-version=0.3.3 libusbredirparser >/dev/null 2>&1 ; then + if $pkg_config --atleast-version=0.3.4 libusbredirparser >/dev/null 2>&1 ; then usb_redir="yes" usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null) usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null) diff --git a/hw/qxl-render.c b/hw/qxl-render.c index 708414376e..25857f6a20 100644 --- a/hw/qxl-render.c +++ b/hw/qxl-render.c @@ -21,14 +21,31 @@ #include "qxl.h" -static void qxl_flip(PCIQXLDevice *qxl, QXLRect *rect) +static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) { - uint8_t *src = qxl->guest_primary.data; - uint8_t *dst = qxl->guest_primary.flipped; + uint8_t *src; + uint8_t *dst = qxl->vga.ds->surface->data; int len, i; - src += (qxl->guest_primary.surface.height - rect->top - 1) * - qxl->guest_primary.abs_stride; + if (is_buffer_shared(qxl->vga.ds->surface)) { + return; + } + if (!qxl->guest_primary.data) { + dprint(qxl, 1, "%s: initializing guest_primary.data\n", __func__); + qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram); + } + dprint(qxl, 2, "%s: stride %d, [%d, %d, %d, %d]\n", __func__, + qxl->guest_primary.qxl_stride, + rect->left, rect->right, rect->top, rect->bottom); + src = qxl->guest_primary.data; + if (qxl->guest_primary.qxl_stride < 0) { + /* qxl surface is upside down, walk src scanlines + * in reverse order to flip it */ + src += (qxl->guest_primary.surface.height - rect->top - 1) * + qxl->guest_primary.abs_stride; + } else { + src += rect->top * qxl->guest_primary.abs_stride; + } dst += rect->top * qxl->guest_primary.abs_stride; src += rect->left * qxl->guest_primary.bytes_pp; dst += rect->left * qxl->guest_primary.bytes_pp; @@ -37,7 +54,7 @@ static void qxl_flip(PCIQXLDevice *qxl, QXLRect *rect) for (i = rect->top; i < rect->bottom; i++) { memcpy(dst, src, len); dst += qxl->guest_primary.abs_stride; - src -= qxl->guest_primary.abs_stride; + src += qxl->guest_primary.qxl_stride; } } @@ -71,84 +88,109 @@ void qxl_render_resize(PCIQXLDevice *qxl) } } -void qxl_render_update(PCIQXLDevice *qxl) +static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area) +{ + area->left = 0; + area->right = qxl->guest_primary.surface.width; + area->top = 0; + area->bottom = qxl->guest_primary.surface.height; +} + +static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) { VGACommonState *vga = &qxl->vga; - QXLRect dirty[32], update; - void *ptr; - int i, redraw = 0; - - if (!is_buffer_shared(vga->ds->surface)) { - dprint(qxl, 1, "%s: restoring shared displaysurface\n", __func__); - qxl->guest_primary.resized++; - qxl->guest_primary.commands++; - redraw = 1; - } + int i; + DisplaySurface *surface = vga->ds->surface; if (qxl->guest_primary.resized) { qxl->guest_primary.resized = 0; - - if (qxl->guest_primary.flipped) { - g_free(qxl->guest_primary.flipped); - qxl->guest_primary.flipped = NULL; - } - qemu_free_displaysurface(vga->ds); - qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram); - if (qxl->guest_primary.qxl_stride < 0) { - /* spice surface is upside down -> need extra buffer to flip */ - qxl->guest_primary.flipped = - g_malloc(qxl->guest_primary.surface.width * - qxl->guest_primary.abs_stride); - ptr = qxl->guest_primary.flipped; - } else { - ptr = qxl->guest_primary.data; - } - dprint(qxl, 1, "%s: %dx%d, stride %d, bpp %d, depth %d, flip %s\n", + qxl_set_rect_to_surface(qxl, &qxl->dirty[0]); + qxl->num_dirty_rects = 1; + dprint(qxl, 1, "%s: %dx%d, stride %d, bpp %d, depth %d\n", __FUNCTION__, qxl->guest_primary.surface.width, qxl->guest_primary.surface.height, qxl->guest_primary.qxl_stride, qxl->guest_primary.bytes_pp, - qxl->guest_primary.bits_pp, - qxl->guest_primary.flipped ? "yes" : "no"); - vga->ds->surface = + qxl->guest_primary.bits_pp); + } + if (surface->width != qxl->guest_primary.surface.width || + surface->height != qxl->guest_primary.surface.height) { + if (qxl->guest_primary.qxl_stride > 0) { + dprint(qxl, 1, "%s: using guest_primary for displaysurface\n", + __func__); + qemu_free_displaysurface(vga->ds); qemu_create_displaysurface_from(qxl->guest_primary.surface.width, qxl->guest_primary.surface.height, qxl->guest_primary.bits_pp, qxl->guest_primary.abs_stride, - ptr); - dpy_resize(vga->ds); - } - - update.left = 0; - update.right = qxl->guest_primary.surface.width; - update.top = 0; - update.bottom = qxl->guest_primary.surface.height; - - memset(dirty, 0, sizeof(dirty)); - if (runstate_is_running() && qxl->guest_primary.commands) { - qxl->guest_primary.commands = 0; - qxl_spice_update_area(qxl, 0, &update, - dirty, ARRAY_SIZE(dirty), 1, QXL_SYNC); - } - if (redraw) { - memset(dirty, 0, sizeof(dirty)); - dirty[0] = update; + qxl->guest_primary.data); + } else { + dprint(qxl, 1, "%s: resizing displaysurface to guest_primary\n", + __func__); + qemu_resize_displaysurface(vga->ds, + qxl->guest_primary.surface.width, + qxl->guest_primary.surface.height); + } } - - for (i = 0; i < ARRAY_SIZE(dirty); i++) { - if (qemu_spice_rect_is_empty(dirty+i)) { + for (i = 0; i < qxl->num_dirty_rects; i++) { + if (qemu_spice_rect_is_empty(qxl->dirty+i)) { break; } - if (qxl->guest_primary.flipped) { - qxl_flip(qxl, dirty+i); - } + qxl_blit(qxl, qxl->dirty+i); dpy_update(vga->ds, - dirty[i].left, dirty[i].top, - dirty[i].right - dirty[i].left, - dirty[i].bottom - dirty[i].top); + qxl->dirty[i].left, qxl->dirty[i].top, + qxl->dirty[i].right - qxl->dirty[i].left, + qxl->dirty[i].bottom - qxl->dirty[i].top); } + qxl->num_dirty_rects = 0; +} + +/* + * use ssd.lock to protect render_update_cookie_num. + * qxl_render_update is called by io thread or vcpu thread, and the completion + * callbacks are called by spice_server thread, defering to bh called from the + * io thread. + */ +void qxl_render_update(PCIQXLDevice *qxl) +{ + QXLCookie *cookie; + + qemu_mutex_lock(&qxl->ssd.lock); + + if (!runstate_is_running() || !qxl->guest_primary.commands) { + qxl_render_update_area_unlocked(qxl); + qemu_mutex_unlock(&qxl->ssd.lock); + return; + } + + qxl->guest_primary.commands = 0; + qxl->render_update_cookie_num++; + qemu_mutex_unlock(&qxl->ssd.lock); + cookie = qxl_cookie_new(QXL_COOKIE_TYPE_RENDER_UPDATE_AREA, + 0); + qxl_set_rect_to_surface(qxl, &cookie->u.render.area); + qxl_spice_update_area(qxl, 0, &cookie->u.render.area, NULL, + 0, 1 /* clear_dirty_region */, QXL_ASYNC, cookie); +} + +void qxl_render_update_area_bh(void *opaque) +{ + PCIQXLDevice *qxl = opaque; + + qemu_mutex_lock(&qxl->ssd.lock); + qxl_render_update_area_unlocked(qxl); + qemu_mutex_unlock(&qxl->ssd.lock); +} + +void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie) +{ + qemu_mutex_lock(&qxl->ssd.lock); + qemu_bh_schedule(qxl->update_area_bh); + qxl->render_update_cookie_num--; + qemu_mutex_unlock(&qxl->ssd.lock); + g_free(cookie); } static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor) @@ -125,9 +125,7 @@ static void qxl_ring_set_dirty(PCIQXLDevice *qxl); void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...) { -#if SPICE_INTERFACE_QXL_MINOR >= 1 qxl_send_events(qxl, QXL_INTERRUPT_ERROR); -#endif if (qxl->guestdebug) { va_list ap; va_start(ap, msg); @@ -143,18 +141,15 @@ void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, struct QXLRect *area, struct QXLRect *dirty_rects, uint32_t num_dirty_rects, uint32_t clear_dirty_region, - qxl_async_io async) + qxl_async_io async, struct QXLCookie *cookie) { if (async == QXL_SYNC) { qxl->ssd.worker->update_area(qxl->ssd.worker, surface_id, area, dirty_rects, num_dirty_rects, clear_dirty_region); } else { -#if SPICE_INTERFACE_QXL_MINOR >= 1 + assert(cookie != NULL); spice_qxl_update_area_async(&qxl->ssd.qxl, surface_id, area, - clear_dirty_region, 0); -#else - abort(); -#endif + clear_dirty_region, (uint64_t)cookie); } } @@ -170,25 +165,25 @@ static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl, static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id, qxl_async_io async) { + QXLCookie *cookie; + if (async) { -#if SPICE_INTERFACE_QXL_MINOR < 1 - abort(); -#else - spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, - (uint64_t)id); -#endif + cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_DESTROY_SURFACE_ASYNC); + cookie->u.surface_id = id; + spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, (uint64_t)cookie); } else { qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id); qxl_spice_destroy_surface_wait_complete(qxl, id); } } -#if SPICE_INTERFACE_QXL_MINOR >= 1 static void qxl_spice_flush_surfaces_async(PCIQXLDevice *qxl) { - spice_qxl_flush_surfaces_async(&qxl->ssd.qxl, 0); + spice_qxl_flush_surfaces_async(&qxl->ssd.qxl, + (uint64_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_FLUSH_SURFACES_ASYNC)); } -#endif void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, uint32_t count) @@ -217,11 +212,9 @@ static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl) static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async) { if (async) { -#if SPICE_INTERFACE_QXL_MINOR < 1 - abort(); -#else - spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl, 0); -#endif + spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl, + (uint64_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_DESTROY_ALL_SURFACES_ASYNC)); } else { qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker); qxl_spice_destroy_surfaces_complete(qxl); @@ -490,7 +483,6 @@ static const char *io_port_to_string(uint32_t io_port) [QXL_IO_DESTROY_PRIMARY] = "QXL_IO_DESTROY_PRIMARY", [QXL_IO_DESTROY_SURFACE_WAIT] = "QXL_IO_DESTROY_SURFACE_WAIT", [QXL_IO_DESTROY_ALL_SURFACES] = "QXL_IO_DESTROY_ALL_SURFACES", -#if SPICE_INTERFACE_QXL_MINOR >= 1 [QXL_IO_UPDATE_AREA_ASYNC] = "QXL_IO_UPDATE_AREA_ASYNC", [QXL_IO_MEMSLOT_ADD_ASYNC] = "QXL_IO_MEMSLOT_ADD_ASYNC", [QXL_IO_CREATE_PRIMARY_ASYNC] = "QXL_IO_CREATE_PRIMARY_ASYNC", @@ -500,7 +492,6 @@ static const char *io_port_to_string(uint32_t io_port) = "QXL_IO_DESTROY_ALL_SURFACES_ASYNC", [QXL_IO_FLUSH_SURFACES_ASYNC] = "QXL_IO_FLUSH_SURFACES_ASYNC", [QXL_IO_FLUSH_RELEASE] = "QXL_IO_FLUSH_RELEASE", -#endif }; return io_port_to_string[io_port]; } @@ -735,12 +726,9 @@ static int interface_flush_resources(QXLInstance *sin) static void qxl_create_guest_primary_complete(PCIQXLDevice *d); -#if SPICE_INTERFACE_QXL_MINOR >= 1 - /* called from spice server thread context only */ -static void interface_async_complete(QXLInstance *sin, uint64_t cookie) +static void interface_async_complete_io(PCIQXLDevice *qxl, QXLCookie *cookie) { - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); uint32_t current_async; qemu_mutex_lock(&qxl->async_lock); @@ -748,9 +736,22 @@ static void interface_async_complete(QXLInstance *sin, uint64_t cookie) qxl->current_async = QXL_UNDEFINED_IO; qemu_mutex_unlock(&qxl->async_lock); - dprint(qxl, 2, "async_complete: %d (%" PRId64 ") done\n", - current_async, cookie); + dprint(qxl, 2, "async_complete: %d (%p) done\n", current_async, cookie); + if (!cookie) { + fprintf(stderr, "qxl: %s: error, cookie is NULL\n", __func__); + return; + } + if (cookie && current_async != cookie->io) { + fprintf(stderr, + "qxl: %s: error: current_async = %d != %ld = cookie->io\n", + __func__, current_async, cookie->io); + } switch (current_async) { + case QXL_IO_MEMSLOT_ADD_ASYNC: + case QXL_IO_DESTROY_PRIMARY_ASYNC: + case QXL_IO_UPDATE_AREA_ASYNC: + case QXL_IO_FLUSH_SURFACES_ASYNC: + break; case QXL_IO_CREATE_PRIMARY_ASYNC: qxl_create_guest_primary_complete(qxl); break; @@ -758,13 +759,75 @@ static void interface_async_complete(QXLInstance *sin, uint64_t cookie) qxl_spice_destroy_surfaces_complete(qxl); break; case QXL_IO_DESTROY_SURFACE_ASYNC: - qxl_spice_destroy_surface_wait_complete(qxl, (uint32_t)cookie); + qxl_spice_destroy_surface_wait_complete(qxl, cookie->u.surface_id); break; + default: + fprintf(stderr, "qxl: %s: unexpected current_async %d\n", __func__, + current_async); } qxl_send_events(qxl, QXL_INTERRUPT_IO_CMD); } -#endif +/* called from spice server thread context only */ +static void interface_update_area_complete(QXLInstance *sin, + uint32_t surface_id, + QXLRect *dirty, uint32_t num_updated_rects) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + int i; + int qxl_i; + + qemu_mutex_lock(&qxl->ssd.lock); + if (surface_id != 0 || !qxl->render_update_cookie_num) { + qemu_mutex_unlock(&qxl->ssd.lock); + return; + } + if (qxl->num_dirty_rects + num_updated_rects > QXL_NUM_DIRTY_RECTS) { + /* + * overflow - treat this as a full update. Not expected to be common. + */ + dprint(qxl, 1, "%s: overflow of dirty rects\n", __func__); + qxl->guest_primary.resized = 1; + } + if (qxl->guest_primary.resized) { + /* + * Don't bother copying or scheduling the bh since we will flip + * the whole area anyway on completion of the update_area async call + */ + qemu_mutex_unlock(&qxl->ssd.lock); + return; + } + qxl_i = qxl->num_dirty_rects; + for (i = 0; i < num_updated_rects; i++) { + qxl->dirty[qxl_i++] = dirty[i]; + } + qxl->num_dirty_rects += num_updated_rects; + dprint(qxl, 1, "%s: scheduling update_area_bh, #dirty %d\n", + __func__, qxl->num_dirty_rects); + qemu_bh_schedule(qxl->update_area_bh); + qemu_mutex_unlock(&qxl->ssd.lock); +} + +/* called from spice server thread context only */ +static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + QXLCookie *cookie = (QXLCookie *)cookie_token; + + switch (cookie->type) { + case QXL_COOKIE_TYPE_IO: + interface_async_complete_io(qxl, cookie); + g_free(cookie); + break; + case QXL_COOKIE_TYPE_RENDER_UPDATE_AREA: + qxl_render_update_area_done(qxl, cookie); + break; + default: + fprintf(stderr, "qxl: %s: unexpected cookie type %d\n", + __func__, cookie->type); + g_free(cookie); + } +} static const QXLInterface qxl_interface = { .base.type = SPICE_INTERFACE_QXL, @@ -785,9 +848,8 @@ static const QXLInterface qxl_interface = { .req_cursor_notification = interface_req_cursor_notification, .notify_update = interface_notify_update, .flush_resources = interface_flush_resources, -#if SPICE_INTERFACE_QXL_MINOR >= 1 .async_complete = interface_async_complete, -#endif + .update_area_complete = interface_update_area_complete, }; static void qxl_enter_vga_mode(PCIQXLDevice *d) @@ -914,6 +976,7 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, static const int regions[] = { QXL_RAM_RANGE_INDEX, QXL_VRAM_RANGE_INDEX, + QXL_VRAM64_RANGE_INDEX, }; uint64_t guest_start; uint64_t guest_end; @@ -960,6 +1023,7 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vga.vram); break; case QXL_VRAM_RANGE_INDEX: + case 4 /* vram 64bit */: virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vram_bar); break; default: @@ -1078,9 +1142,7 @@ static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async) if (d->mode == QXL_MODE_UNDEFINED) { return 0; } - dprint(d, 1, "%s\n", __FUNCTION__); - d->mode = QXL_MODE_UNDEFINED; qemu_spice_destroy_primary_surface(&d->ssd, 0, async); qxl_spice_reset_cursor(d); @@ -1137,9 +1199,7 @@ static void ioport_write(void *opaque, target_phys_addr_t addr, PCIQXLDevice *d = opaque; uint32_t io_port = addr; qxl_async_io async = QXL_SYNC; -#if SPICE_INTERFACE_QXL_MINOR >= 1 uint32_t orig_io_port = io_port; -#endif switch (io_port) { case QXL_IO_RESET: @@ -1149,10 +1209,8 @@ static void ioport_write(void *opaque, target_phys_addr_t addr, case QXL_IO_CREATE_PRIMARY: case QXL_IO_UPDATE_IRQ: case QXL_IO_LOG: -#if SPICE_INTERFACE_QXL_MINOR >= 1 case QXL_IO_MEMSLOT_ADD_ASYNC: case QXL_IO_CREATE_PRIMARY_ASYNC: -#endif break; default: if (d->mode != QXL_MODE_VGA) { @@ -1160,17 +1218,14 @@ static void ioport_write(void *opaque, target_phys_addr_t addr, } dprint(d, 1, "%s: unexpected port 0x%x (%s) in vga mode\n", __func__, io_port, io_port_to_string(io_port)); -#if SPICE_INTERFACE_QXL_MINOR >= 1 /* be nice to buggy guest drivers */ if (io_port >= QXL_IO_UPDATE_AREA_ASYNC && io_port <= QXL_IO_DESTROY_ALL_SURFACES_ASYNC) { qxl_send_events(d, QXL_INTERRUPT_IO_CMD); } -#endif return; } -#if SPICE_INTERFACE_QXL_MINOR >= 1 /* we change the io_port to avoid ifdeffery in the main switch */ orig_io_port = io_port; switch (io_port) { @@ -1209,14 +1264,21 @@ async_common: default: break; } -#endif switch (io_port) { case QXL_IO_UPDATE_AREA: { + QXLCookie *cookie = NULL; QXLRect update = d->ram->update_area; + + if (async == QXL_ASYNC) { + cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_UPDATE_AREA_ASYNC); + cookie->u.area = update; + } qxl_spice_update_area(d, d->ram->update_surface, - &update, NULL, 0, 0, async); + cookie ? &cookie->u.area : &update, + NULL, 0, 0, async, cookie); break; } case QXL_IO_NOTIFY_CMD: @@ -1301,7 +1363,6 @@ async_common: } qxl_spice_destroy_surface_wait(d, val, async); break; -#if SPICE_INTERFACE_QXL_MINOR >= 1 case QXL_IO_FLUSH_RELEASE: { QXLReleaseRing *ring = &d->ram->release_ring; if (ring->prod - ring->cons + 1 == ring->num_items) { @@ -1322,7 +1383,6 @@ async_common: d->num_free_res); qxl_spice_flush_surfaces_async(d); break; -#endif case QXL_IO_DESTROY_ALL_SURFACES: d->mode = QXL_MODE_UNDEFINED; qxl_spice_destroy_surfaces(d, async); @@ -1333,16 +1393,12 @@ async_common: } return; cancel_async: -#if SPICE_INTERFACE_QXL_MINOR >= 1 if (async) { qxl_send_events(d, QXL_INTERRUPT_IO_CMD); qemu_mutex_lock(&d->async_lock); d->current_async = QXL_UNDEFINED_IO; qemu_mutex_unlock(&d->async_lock); } -#else - return; -#endif } static uint64_t ioport_read(void *opaque, target_phys_addr_t addr, @@ -1545,6 +1601,10 @@ static void display_refresh(struct DisplayState *ds) { if (qxl0->mode == QXL_MODE_VGA) { qemu_spice_display_refresh(&qxl0->ssd); + } else { + qemu_mutex_lock(&qxl0->ssd.lock); + qemu_spice_cursor_refresh_unlocked(&qxl0->ssd); + qemu_mutex_unlock(&qxl0->ssd.lock); } } @@ -1564,18 +1624,28 @@ static void qxl_init_ramsize(PCIQXLDevice *qxl, uint32_t ram_min_mb) qxl->vga.vram_size = ram_min_mb * 1024 * 1024; } - /* vram (surfaces, bar 1) */ + /* vram32 (surfaces, 32bit, bar 1) */ + if (qxl->vram32_size_mb != -1) { + qxl->vram32_size = qxl->vram32_size_mb * 1024 * 1024; + } + if (qxl->vram32_size < 4096) { + qxl->vram32_size = 4096; + } + + /* vram (surfaces, 64bit, bar 4+5) */ if (qxl->vram_size_mb != -1) { qxl->vram_size = qxl->vram_size_mb * 1024 * 1024; } - if (qxl->vram_size < 4096) { - qxl->vram_size = 4096; + if (qxl->vram_size < qxl->vram32_size) { + qxl->vram_size = qxl->vram32_size; } + if (qxl->revision == 1) { + qxl->vram32_size = 4096; qxl->vram_size = 4096; } - qxl->vga.vram_size = msb_mask(qxl->vga.vram_size * 2 - 1); + qxl->vram32_size = msb_mask(qxl->vram32_size * 2 - 1); qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1); } @@ -1600,9 +1670,7 @@ static int qxl_init_common(PCIQXLDevice *qxl) case 2: /* spice 0.6 -- qxl-2 */ pci_device_rev = QXL_REVISION_STABLE_V06; break; -#if SPICE_INTERFACE_QXL_MINOR >= 1 case 3: /* qxl-3 */ -#endif default: pci_device_rev = QXL_DEFAULT_REVISION; break; @@ -1619,6 +1687,8 @@ static int qxl_init_common(PCIQXLDevice *qxl) memory_region_init_ram(&qxl->vram_bar, "qxl.vram", qxl->vram_size); vmstate_register_ram(&qxl->vram_bar, &qxl->pci.qdev); + memory_region_init_alias(&qxl->vram32_bar, "qxl.vram32", &qxl->vram_bar, + 0, qxl->vram32_size); io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1); if (qxl->revision == 1) { @@ -1642,7 +1712,29 @@ static int qxl_init_common(PCIQXLDevice *qxl) PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vga.vram); pci_register_bar(&qxl->pci, QXL_VRAM_RANGE_INDEX, - PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vram_bar); + PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vram32_bar); + + if (qxl->vram32_size < qxl->vram_size) { + /* + * Make the 64bit vram bar show up only in case it is + * configured to be larger than the 32bit vram bar. + */ + pci_register_bar(&qxl->pci, QXL_VRAM64_RANGE_INDEX, + PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64 | + PCI_BASE_ADDRESS_MEM_PREFETCH, + &qxl->vram_bar); + } + + /* print pci bar details */ + dprint(qxl, 1, "ram/%s: %d MB [region 0]\n", + qxl->id == 0 ? "pri" : "sec", + qxl->vga.vram_size / (1024*1024)); + dprint(qxl, 1, "vram/32: %d MB [region 1]\n", + qxl->vram32_size / (1024*1024)); + dprint(qxl, 1, "vram/64: %d MB %s\n", + qxl->vram_size / (1024*1024), + qxl->vram32_size < qxl->vram_size ? "[region 4]" : "[unmapped]"); qxl->ssd.qxl.base.sif = &qxl_interface.base; qxl->ssd.qxl.id = qxl->id; @@ -1652,6 +1744,8 @@ static int qxl_init_common(PCIQXLDevice *qxl) init_pipe_signaling(qxl); qxl_reset_state(qxl); + qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl); + return 0; } @@ -1859,7 +1953,7 @@ static VMStateDescription qxl_vmstate = { static Property qxl_properties[] = { DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024), - DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, + DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram32_size, 64 * 1024 * 1024), DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, QXL_DEFAULT_REVISION), @@ -1867,7 +1961,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, vram_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_END_OF_LIST(), }; @@ -16,8 +16,14 @@ enum qxl_mode { QXL_MODE_NATIVE, }; +#ifndef QXL_VRAM64_RANGE_INDEX +#define QXL_VRAM64_RANGE_INDEX 4 +#endif + #define QXL_UNDEFINED_IO UINT32_MAX +#define QXL_NUM_DIRTY_RECTS 64 + typedef struct PCIQXLDevice { PCIDevice pci; SimpleSpiceDisplay ssd; @@ -52,7 +58,7 @@ typedef struct PCIQXLDevice { uint32_t abs_stride; uint32_t bits_pp; uint32_t bytes_pp; - uint8_t *data, *flipped; + uint8_t *data; } guest_primary; struct surfaces { @@ -86,6 +92,8 @@ typedef struct PCIQXLDevice { /* vram pci bar */ uint32_t vram_size; MemoryRegion vram_bar; + uint32_t vram32_size; + MemoryRegion vram32_bar; /* io bar */ MemoryRegion io_bar; @@ -93,6 +101,13 @@ typedef struct PCIQXLDevice { /* user-friendly properties (in megabytes) */ uint32_t ram_size_mb; uint32_t vram_size_mb; + uint32_t vram32_size_mb; + + /* qxl_render_update state */ + int render_update_cookie_num; + int num_dirty_rects; + QXLRect dirty[QXL_NUM_DIRTY_RECTS]; + QEMUBH *update_area_bh; } PCIQXLDevice; #define PANIC_ON(x) if ((x)) { \ @@ -108,11 +123,7 @@ typedef struct PCIQXLDevice { } \ } while (0) -#if SPICE_INTERFACE_QXL_MINOR >= 1 #define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V10 -#else -#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V06 -#endif /* qxl.c */ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id); @@ -122,7 +133,7 @@ void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, struct QXLRect *area, struct QXLRect *dirty_rects, uint32_t num_dirty_rects, uint32_t clear_dirty_region, - qxl_async_io async); + qxl_async_io async, QXLCookie *cookie); void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, uint32_t count); void qxl_spice_oom(PCIQXLDevice *qxl); @@ -138,9 +149,5 @@ void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext); void qxl_render_resize(PCIQXLDevice *qxl); void qxl_render_update(PCIQXLDevice *qxl); void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext); -#if SPICE_INTERFACE_QXL_MINOR >= 1 -void qxl_spice_update_area_async(PCIQXLDevice *qxl, uint32_t surface_id, - struct QXLRect *area, - uint32_t clear_dirty_region, - int is_vga); -#endif +void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie); +void qxl_render_update_area_bh(void *opaque); diff --git a/hw/usb-bt.c b/hw/usb-bt.c index 649bdcf2d7..23c39ecc23 100644 --- a/hw/usb-bt.c +++ b/hw/usb-bt.c @@ -498,14 +498,14 @@ static int usb_bt_initfn(USBDevice *dev) return 0; } -USBDevice *usb_bt_init(HCIInfo *hci) +USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci) { USBDevice *dev; struct USBBtState *s; if (!hci) return NULL; - dev = usb_create_simple(NULL /* FIXME */, "usb-bt-dongle"); + dev = usb_create_simple(bus, "usb-bt-dongle"); if (!dev) { return NULL; } diff --git a/hw/usb-bus.c b/hw/usb-bus.c index ae79a4527b..70b7ebc086 100644 --- a/hw/usb-bus.c +++ b/hw/usb-bus.c @@ -203,13 +203,14 @@ typedef struct LegacyUSBFactory { const char *name; const char *usbdevice_name; - USBDevice *(*usbdevice_init)(const char *params); + USBDevice *(*usbdevice_init)(USBBus *bus, const char *params); } LegacyUSBFactory; static GSList *legacy_usb_factory; void usb_legacy_register(const char *typename, const char *usbdevice_name, - USBDevice *(*usbdevice_init)(const char *params)) + USBDevice *(*usbdevice_init)(USBBus *bus, + const char *params)) { if (usbdevice_name) { LegacyUSBFactory *f = g_malloc0(sizeof(*f)); @@ -224,17 +225,6 @@ USBDevice *usb_create(USBBus *bus, const char *name) { DeviceState *dev; -#if 1 - /* temporary stopgap until all usb is properly qdev-ified */ - if (!bus) { - bus = usb_bus_find(-1); - if (!bus) - return NULL; - error_report("%s: no bus specified, using \"%s\" for \"%s\"", - __FUNCTION__, bus->qbus.name, name); - } -#endif - dev = qdev_create(&bus->qbus, name); return USB_DEVICE(dev); } @@ -565,7 +555,7 @@ USBDevice *usbdevice_create(const char *cmdline) } return usb_create_simple(bus, f->name); } - return f->usbdevice_init(params); + return f->usbdevice_init(bus, params); } static void usb_device_class_init(ObjectClass *klass, void *data) diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c index 0b2ac8037a..ce01e343c6 100644 --- a/hw/usb-ccid.c +++ b/hw/usb-ccid.c @@ -447,7 +447,7 @@ static const USBDescDevice desc_device = { { .bNumInterfaces = 1, .bConfigurationValue = 1, - .bmAttributes = 0xa0, + .bmAttributes = 0xe0, .bMaxPower = 50, .nif = 1, .ifs = &desc_iface0, diff --git a/hw/usb-desc.c b/hw/usb-desc.c index 3c3ed6a802..ccf85ade9e 100644 --- a/hw/usb-desc.c +++ b/hw/usb-desc.c @@ -536,7 +536,11 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p, break; case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = dev->config->bConfigurationValue; + /* + * 9.4.2: 0 should be returned if the device is unconfigured, otherwise + * the non zero value of bConfigurationValue. + */ + data[0] = dev->config ? dev->config->bConfigurationValue : 0; ret = 1; break; case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: @@ -544,9 +548,18 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p, trace_usb_set_config(dev->addr, value, ret); break; - case DeviceRequest | USB_REQ_GET_STATUS: + case DeviceRequest | USB_REQ_GET_STATUS: { + const USBDescConfig *config = dev->config ? + dev->config : &dev->device->confs[0]; + data[0] = 0; - if (dev->config->bmAttributes & 0x40) { + /* + * Default state: Device behavior when this request is received while + * the device is in the Default state is not specified. + * We return the same value that a configured device would return if + * it used the first configuration. + */ + if (config->bmAttributes & 0x40) { data[0] |= 1 << USB_DEVICE_SELF_POWERED; } if (dev->remote_wakeup) { @@ -555,6 +568,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p, data[1] = 0x00; ret = 2; break; + } case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: if (value == USB_DEVICE_REMOTE_WAKEUP) { dev->remote_wakeup = 0; diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c index e699814305..afc8ccf458 100644 --- a/hw/usb-ehci.c +++ b/hw/usb-ehci.c @@ -912,6 +912,7 @@ static void ehci_reset(void *opaque) } } ehci_queues_rip_all(s); + qemu_del_timer(s->frame_timer); } static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr) @@ -1070,7 +1071,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) if (val & USBCMD_HCRESET) { ehci_reset(s); - val &= ~USBCMD_HCRESET; + val = s->usbcmd; } /* not supporting dynamic frame list size at the moment */ @@ -1458,44 +1459,22 @@ static int ehci_process_itd(EHCIState *ehci, dev = ehci_find_device(ehci, devaddr); ep = usb_ep_get(dev, pid, endp); - usb_packet_setup(&ehci->ipacket, pid, ep); - usb_packet_map(&ehci->ipacket, &ehci->isgl); - - ret = usb_handle_packet(dev, &ehci->ipacket); - - usb_packet_unmap(&ehci->ipacket); - qemu_sglist_destroy(&ehci->isgl); - -#if 0 - /* In isoch, there is no facility to indicate a NAK so let's - * instead just complete a zero-byte transaction. Setting - * DBERR seems too draconian. - */ - - if (ret == USB_RET_NAK) { - if (ehci->isoch_pause > 0) { - DPRINTF("ISOCH: received a NAK but paused so returning\n"); - ehci->isoch_pause--; - return 0; - } else if (ehci->isoch_pause == -1) { - DPRINTF("ISOCH: recv NAK & isoch pause inactive, setting\n"); - // Pause frindex for up to 50 msec waiting for data from - // remote - ehci->isoch_pause = 50; - return 0; - } else { - DPRINTF("ISOCH: isoch pause timeout! return 0\n"); - ret = 0; - } + if (ep->type == USB_ENDPOINT_XFER_ISOC) { + usb_packet_setup(&ehci->ipacket, pid, ep); + usb_packet_map(&ehci->ipacket, &ehci->isgl); + ret = usb_handle_packet(dev, &ehci->ipacket); + assert(ret != USB_RET_ASYNC); + usb_packet_unmap(&ehci->ipacket); } else { - DPRINTF("ISOCH: received ACK, clearing pause\n"); - ehci->isoch_pause = -1; + DPRINTF("ISOCH: attempt to addess non-iso endpoint\n"); + ret = USB_RET_NAK; } -#else + qemu_sglist_destroy(&ehci->isgl); + if (ret == USB_RET_NAK) { + /* no data for us, so do a zero-length transfer */ ret = 0; } -#endif if (ret >= 0) { if (!dir) { @@ -1505,11 +1484,27 @@ static int ehci_process_itd(EHCIState *ehci, /* IN */ set_field(&itd->transact[i], ret, ITD_XACT_LENGTH); } - - if (itd->transact[i] & ITD_XACT_IOC) { - ehci_record_interrupt(ehci, USBSTS_INT); + } else { + switch (ret) { + default: + fprintf(stderr, "Unexpected iso usb result: %d\n", ret); + /* Fall through */ + case USB_RET_NODEV: + /* 3.3.2: XACTERR is only allowed on IN transactions */ + if (dir) { + itd->transact[i] |= ITD_XACT_XACTERR; + ehci_record_interrupt(ehci, USBSTS_ERRINT); + } + break; + case USB_RET_BABBLE: + itd->transact[i] |= ITD_XACT_BABBLE; + ehci_record_interrupt(ehci, USBSTS_ERRINT); + break; } } + if (itd->transact[i] & ITD_XACT_IOC) { + ehci_record_interrupt(ehci, USBSTS_INT); + } itd->transact[i] &= ~ITD_XACT_ACTIVE; } } @@ -2368,8 +2363,6 @@ static int usb_ehci_initfn(PCIDevice *dev) memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE); pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem); - fprintf(stderr, "*** EHCI support is under development ***\n"); - return 0; } diff --git a/hw/usb-hid.c b/hw/usb-hid.c index 7fc0bd81aa..37bca78eca 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -466,6 +466,9 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) case USB_TOKEN_IN: if (p->ep->nr == 1) { int64_t curtime = qemu_get_clock_ns(vm_clock); + if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { + hid_pointer_activate(hs); + } if (!hid_has_events(hs) && (!hs->idle || hs->next_idle_clock - curtime > 0)) { return USB_RET_NAK; diff --git a/hw/usb-msd.c b/hw/usb-msd.c index 5fbd2d021b..c6f08a0313 100644 --- a/hw/usb-msd.c +++ b/hw/usb-msd.c @@ -568,7 +568,7 @@ static int usb_msd_initfn(USBDevice *dev) return 0; } -static USBDevice *usb_msd_init(const char *filename) +static USBDevice *usb_msd_init(USBBus *bus, const char *filename) { static int nr=0; char id[8]; @@ -611,7 +611,7 @@ static USBDevice *usb_msd_init(const char *filename) } /* create guest device */ - dev = usb_create(NULL /* FIXME */, "usb-storage"); + dev = usb_create(bus, "usb-storage"); if (!dev) { return NULL; } diff --git a/hw/usb-net.c b/hw/usb-net.c index 49d5d4db65..22b82017e3 100644 --- a/hw/usb-net.c +++ b/hw/usb-net.c @@ -1353,7 +1353,7 @@ static int usb_net_initfn(USBDevice *dev) return 0; } -static USBDevice *usb_net_init(const char *cmdline) +static USBDevice *usb_net_init(USBBus *bus, const char *cmdline) { USBDevice *dev; QemuOpts *opts; @@ -1371,7 +1371,7 @@ static USBDevice *usb_net_init(const char *cmdline) return NULL; } - dev = usb_create(NULL /* FIXME */, "usb-net"); + dev = usb_create(bus, "usb-net"); if (!dev) { return NULL; } diff --git a/hw/usb-serial.c b/hw/usb-serial.c index 52676e8f7b..0aae379b20 100644 --- a/hw/usb-serial.c +++ b/hw/usb-serial.c @@ -492,7 +492,7 @@ static int usb_serial_initfn(USBDevice *dev) return 0; } -static USBDevice *usb_serial_init(const char *filename) +static USBDevice *usb_serial_init(USBBus *bus, const char *filename) { USBDevice *dev; CharDriverState *cdrv; @@ -535,7 +535,7 @@ static USBDevice *usb_serial_init(const char *filename) if (!cdrv) return NULL; - dev = usb_create(NULL /* FIXME */, "usb-serial"); + dev = usb_create(bus, "usb-serial"); if (!dev) { return NULL; } @@ -549,7 +549,7 @@ static USBDevice *usb_serial_init(const char *filename) return dev; } -static USBDevice *usb_braille_init(const char *unused) +static USBDevice *usb_braille_init(USBBus *bus, const char *unused) { USBDevice *dev; CharDriverState *cdrv; @@ -558,7 +558,7 @@ static USBDevice *usb_braille_init(const char *unused) if (!cdrv) return NULL; - dev = usb_create(NULL /* FIXME */, "usb-braille"); + dev = usb_create(bus, "usb-braille"); qdev_prop_set_chr(&dev->qdev, "chardev", cdrv); qdev_init_nofail(&dev->qdev); diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 2280dc792d..70e3881321 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -95,23 +95,32 @@ static const char *pid2str(int pid) #endif typedef struct UHCIState UHCIState; +typedef struct UHCIAsync UHCIAsync; +typedef struct UHCIQueue UHCIQueue; /* * Pending async transaction. * 'packet' must be the first field because completion * handler does "(UHCIAsync *) pkt" cast. */ -typedef struct UHCIAsync { + +struct UHCIAsync { USBPacket packet; QEMUSGList sgl; - UHCIState *uhci; + UHCIQueue *queue; QTAILQ_ENTRY(UHCIAsync) next; uint32_t td; - uint32_t token; - int8_t valid; uint8_t isoc; uint8_t done; -} UHCIAsync; +}; + +struct UHCIQueue { + uint32_t token; + UHCIState *uhci; + QTAILQ_ENTRY(UHCIQueue) next; + QTAILQ_HEAD(, UHCIAsync) asyncs; + int8_t valid; +}; typedef struct UHCIPort { USBPort port; @@ -137,7 +146,7 @@ struct UHCIState { uint32_t pending_int_mask; /* Active packets */ - QTAILQ_HEAD(,UHCIAsync) async_pending; + QTAILQ_HEAD(, UHCIQueue) queues; uint8_t num_ports_vmstate; /* Properties */ @@ -157,62 +166,90 @@ typedef struct UHCI_QH { uint32_t el_link; } UHCI_QH; -static UHCIAsync *uhci_async_alloc(UHCIState *s) +static inline int32_t uhci_queue_token(UHCI_TD *td) +{ + /* covers ep, dev, pid -> identifies the endpoint */ + return td->token & 0x7ffff; +} + +static UHCIQueue *uhci_queue_get(UHCIState *s, UHCI_TD *td) +{ + uint32_t token = uhci_queue_token(td); + UHCIQueue *queue; + + QTAILQ_FOREACH(queue, &s->queues, next) { + if (queue->token == token) { + return queue; + } + } + + queue = g_new0(UHCIQueue, 1); + queue->uhci = s; + queue->token = token; + QTAILQ_INIT(&queue->asyncs); + QTAILQ_INSERT_HEAD(&s->queues, queue, next); + return queue; +} + +static void uhci_queue_free(UHCIQueue *queue) { - UHCIAsync *async = g_malloc(sizeof(UHCIAsync)); - - memset(&async->packet, 0, sizeof(async->packet)); - async->uhci = s; - async->valid = 0; - async->td = 0; - async->token = 0; - async->done = 0; - async->isoc = 0; + UHCIState *s = queue->uhci; + + QTAILQ_REMOVE(&s->queues, queue, next); + g_free(queue); +} + +static UHCIAsync *uhci_async_alloc(UHCIQueue *queue) +{ + UHCIAsync *async = g_new0(UHCIAsync, 1); + + async->queue = queue; usb_packet_init(&async->packet); - pci_dma_sglist_init(&async->sgl, &s->dev, 1); + pci_dma_sglist_init(&async->sgl, &queue->uhci->dev, 1); return async; } -static void uhci_async_free(UHCIState *s, UHCIAsync *async) +static void uhci_async_free(UHCIAsync *async) { usb_packet_cleanup(&async->packet); qemu_sglist_destroy(&async->sgl); g_free(async); } -static void uhci_async_link(UHCIState *s, UHCIAsync *async) +static void uhci_async_link(UHCIAsync *async) { - QTAILQ_INSERT_HEAD(&s->async_pending, async, next); + UHCIQueue *queue = async->queue; + QTAILQ_INSERT_TAIL(&queue->asyncs, async, next); } -static void uhci_async_unlink(UHCIState *s, UHCIAsync *async) +static void uhci_async_unlink(UHCIAsync *async) { - QTAILQ_REMOVE(&s->async_pending, async, next); + UHCIQueue *queue = async->queue; + QTAILQ_REMOVE(&queue->asyncs, async, next); } -static void uhci_async_cancel(UHCIState *s, UHCIAsync *async) +static void uhci_async_cancel(UHCIAsync *async) { DPRINTF("uhci: cancel td 0x%x token 0x%x done %u\n", async->td, async->token, async->done); if (!async->done) usb_cancel_packet(&async->packet); - uhci_async_free(s, async); + uhci_async_free(async); } /* * Mark all outstanding async packets as invalid. * This is used for canceling them when TDs are removed by the HCD. */ -static UHCIAsync *uhci_async_validate_begin(UHCIState *s) +static void uhci_async_validate_begin(UHCIState *s) { - UHCIAsync *async; + UHCIQueue *queue; - QTAILQ_FOREACH(async, &s->async_pending, next) { - async->valid--; + QTAILQ_FOREACH(queue, &s->queues, next) { + queue->valid--; } - return NULL; } /* @@ -220,77 +257,74 @@ static UHCIAsync *uhci_async_validate_begin(UHCIState *s) */ static void uhci_async_validate_end(UHCIState *s) { - UHCIAsync *curr, *n; + UHCIQueue *queue, *n; + UHCIAsync *async; - QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) { - if (curr->valid > 0) { + QTAILQ_FOREACH_SAFE(queue, &s->queues, next, n) { + if (queue->valid > 0) { continue; } - uhci_async_unlink(s, curr); - uhci_async_cancel(s, curr); + while (!QTAILQ_EMPTY(&queue->asyncs)) { + async = QTAILQ_FIRST(&queue->asyncs); + uhci_async_unlink(async); + uhci_async_cancel(async); + } + uhci_queue_free(queue); } } static void uhci_async_cancel_device(UHCIState *s, USBDevice *dev) { + UHCIQueue *queue; UHCIAsync *curr, *n; - QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) { - if (!usb_packet_is_inflight(&curr->packet) || - curr->packet.ep->dev != dev) { - continue; + QTAILQ_FOREACH(queue, &s->queues, next) { + QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) { + if (!usb_packet_is_inflight(&curr->packet) || + curr->packet.ep->dev != dev) { + continue; + } + uhci_async_unlink(curr); + uhci_async_cancel(curr); } - uhci_async_unlink(s, curr); - uhci_async_cancel(s, curr); } } static void uhci_async_cancel_all(UHCIState *s) { + UHCIQueue *queue; UHCIAsync *curr, *n; - QTAILQ_FOREACH_SAFE(curr, &s->async_pending, next, n) { - uhci_async_unlink(s, curr); - uhci_async_cancel(s, curr); + QTAILQ_FOREACH(queue, &s->queues, next) { + QTAILQ_FOREACH_SAFE(curr, &queue->asyncs, next, n) { + uhci_async_unlink(curr); + uhci_async_cancel(curr); + } } } -static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token) +static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, UHCI_TD *td) { + uint32_t token = uhci_queue_token(td); + UHCIQueue *queue; UHCIAsync *async; - UHCIAsync *match = NULL; - int count = 0; - - /* - * We're looking for the best match here. ie both td addr and token. - * Otherwise we return last good match. ie just token. - * It's ok to match just token because it identifies the transaction - * rather well, token includes: device addr, endpoint, size, etc. - * - * Also since we queue async transactions in reverse order by returning - * last good match we restores the order. - * - * It's expected that we wont have a ton of outstanding transactions. - * If we ever do we'd want to optimize this algorithm. - */ - QTAILQ_FOREACH(async, &s->async_pending, next) { - if (async->token == token) { - /* Good match */ - match = async; - - if (async->td == addr) { - /* Best match */ - break; - } + QTAILQ_FOREACH(queue, &s->queues, next) { + if (queue->token == token) { + break; } - count++; + } + if (queue == NULL) { + return NULL; } - if (count > 64) - fprintf(stderr, "uhci: warning lots of async transactions\n"); + QTAILQ_FOREACH(async, &queue->asyncs, next) { + if (async->td == addr) { + return async; + } + } - return match; + return NULL; } static void uhci_update_irq(UHCIState *s) @@ -759,8 +793,7 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in { UHCIAsync *async; int len = 0, max_len; - uint8_t pid, isoc; - uint32_t token; + uint8_t pid; USBDevice *dev; USBEndpoint *ep; @@ -768,41 +801,29 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in if (!(td->ctrl & TD_CTRL_ACTIVE)) return 1; - /* token field is not unique for isochronous requests, - * so use the destination buffer - */ - if (td->ctrl & TD_CTRL_IOS) { - token = td->buffer; - isoc = 1; - } else { - token = td->token; - isoc = 0; - } - - async = uhci_async_find_td(s, addr, token); + async = uhci_async_find_td(s, addr, td); if (async) { /* Already submitted */ - async->valid = 32; + async->queue->valid = 32; if (!async->done) return 1; - uhci_async_unlink(s, async); + uhci_async_unlink(async); goto done; } /* Allocate new packet */ - async = uhci_async_alloc(s); + async = uhci_async_alloc(uhci_queue_get(s, td)); if (!async) return 1; /* valid needs to be large enough to handle 10 frame delay * for initial isochronous requests */ - async->valid = 32; + async->queue->valid = 32; async->td = addr; - async->token = token; - async->isoc = isoc; + async->isoc = td->ctrl & TD_CTRL_IOS; max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; @@ -827,14 +848,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in default: /* invalid pid : frame interrupted */ - uhci_async_free(s, async); + uhci_async_free(async); s->status |= UHCI_STS_HCPERR; uhci_update_irq(s); return -1; } if (len == USB_RET_ASYNC) { - uhci_async_link(s, async); + uhci_async_link(async); return 2; } @@ -843,14 +864,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in done: len = uhci_complete_td(s, td, async, int_mask); usb_packet_unmap(&async->packet); - uhci_async_free(s, async); + uhci_async_free(async); return len; } static void uhci_async_complete(USBPort *port, USBPacket *packet) { UHCIAsync *async = container_of(packet, UHCIAsync, packet); - UHCIState *s = async->uhci; + UHCIState *s = async->queue->uhci; DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token); @@ -865,14 +886,14 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet) le32_to_cpus(&td.token); le32_to_cpus(&td.buffer); - uhci_async_unlink(s, async); + uhci_async_unlink(async); uhci_complete_td(s, &td, async, &int_mask); s->pending_int_mask |= int_mask; /* update the status bits of the TD */ val = cpu_to_le32(td.ctrl); pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val)); - uhci_async_free(s, async); + uhci_async_free(async); } else { async->done = 1; uhci_process_frame(s); @@ -921,6 +942,34 @@ static int qhdb_insert(QhDb *db, uint32_t addr) return 0; } +static void uhci_fill_queue(UHCIState *s, UHCI_TD *td) +{ + uint32_t int_mask = 0; + uint32_t plink = td->link; + uint32_t token = uhci_queue_token(td); + UHCI_TD ptd; + int ret; + + fprintf(stderr, "%s: -- %x\n", __func__, token); + while (is_valid(plink)) { + pci_dma_read(&s->dev, plink & ~0xf, &ptd, sizeof(ptd)); + le32_to_cpus(&ptd.link); + le32_to_cpus(&ptd.ctrl); + le32_to_cpus(&ptd.token); + le32_to_cpus(&ptd.buffer); + if (!(ptd.ctrl & TD_CTRL_ACTIVE)) { + break; + } + if (uhci_queue_token(&ptd) != token) { + break; + } + ret = uhci_handle_td(s, plink, &ptd, &int_mask); + assert(ret == 2); /* got USB_RET_ASYNC */ + assert(int_mask == 0); + plink = ptd.link; + } +} + static void uhci_process_frame(UHCIState *s) { uint32_t frame_addr, link, old_td_ctrl, val, int_mask; @@ -1008,49 +1057,62 @@ static void uhci_process_frame(UHCIState *s) pci_dma_write(&s->dev, (link & ~0xf) + 4, &val, sizeof(val)); } - if (ret < 0) { - /* interrupted frame */ - break; - } - - if (ret == 2 || ret == 1) { - DPRINTF("uhci: TD 0x%x %s. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", - link, ret == 2 ? "pend" : "skip", - td.link, td.ctrl, td.token, curr_qh); + switch (ret) { + case -1: /* interrupted frame */ + goto out; + case 1: /* goto next queue */ + DPRINTF("uhci: TD 0x%x skip. " + "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", + link, td.link, td.ctrl, td.token, curr_qh); link = curr_qh ? qh.link : td.link; continue; - } - /* completed TD */ + case 2: /* got USB_RET_ASYNC */ + DPRINTF("uhci: TD 0x%x async. " + "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", + link, td.link, td.ctrl, td.token, curr_qh); + if (is_valid(td.link)) { + uhci_fill_queue(s, &td); + } + link = curr_qh ? qh.link : td.link; + continue; - DPRINTF("uhci: TD 0x%x done. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", - link, td.link, td.ctrl, td.token, curr_qh); + case 0: /* completed TD */ + DPRINTF("uhci: TD 0x%x done. " + "link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", + link, td.link, td.ctrl, td.token, curr_qh); - link = td.link; - td_count++; - bytes_count += (td.ctrl & 0x7ff) + 1; + link = td.link; + td_count++; + bytes_count += (td.ctrl & 0x7ff) + 1; - if (curr_qh) { - /* update QH element link */ - qh.el_link = link; - val = cpu_to_le32(qh.el_link); - pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val)); + if (curr_qh) { + /* update QH element link */ + qh.el_link = link; + val = cpu_to_le32(qh.el_link); + pci_dma_write(&s->dev, (curr_qh & ~0xf) + 4, &val, sizeof(val)); - if (!depth_first(link)) { - /* done with this QH */ + if (!depth_first(link)) { + /* done with this QH */ - DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n", - curr_qh, qh.link, qh.el_link); + DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n", + curr_qh, qh.link, qh.el_link); - curr_qh = 0; - link = qh.link; + curr_qh = 0; + link = qh.link; + } } + break; + + default: + assert(!"unknown return code"); } /* go to the next entry */ } +out: s->pending_int_mask |= int_mask; } @@ -1148,7 +1210,7 @@ static int usb_uhci_common_initfn(PCIDevice *dev) } s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s); s->num_ports_vmstate = NB_PORTS; - QTAILQ_INIT(&s->async_pending); + QTAILQ_INIT(&s->queues); qemu_register_reset(uhci_reset, s); diff --git a/hw/usb-xhci.c b/hw/usb-xhci.c index 008b0b5718..fc5b542d99 100644 --- a/hw/usb-xhci.c +++ b/hw/usb-xhci.c @@ -1769,12 +1769,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid epctx->retry = xfer; break; } - - /* - * Qemu usb can't handle multiple in-flight xfers. - * Stop here for now. - */ - break; } } @@ -26,6 +26,7 @@ #include "qemu-common.h" #include "usb.h" #include "iov.h" +#include "trace.h" void usb_attach(USBPort *port) { @@ -390,7 +391,6 @@ void usb_packet_init(USBPacket *p) void usb_packet_set_state(USBPacket *p, USBPacketState state) { -#ifdef DEBUG static const char *name[] = { [USB_PACKET_UNDEFINED] = "undef", [USB_PACKET_SETUP] = "setup", @@ -399,28 +399,11 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state) [USB_PACKET_COMPLETE] = "complete", [USB_PACKET_CANCELED] = "canceled", }; - static const char *rets[] = { - [-USB_RET_NODEV] = "NODEV", - [-USB_RET_NAK] = "NAK", - [-USB_RET_STALL] = "STALL", - [-USB_RET_BABBLE] = "BABBLE", - [-USB_RET_ASYNC] = "ASYNC", - }; - char add[16] = ""; + USBDevice *dev = p->ep->dev; + USBBus *bus = usb_bus_from_device(dev); - if (state == USB_PACKET_COMPLETE) { - if (p->result < 0) { - snprintf(add, sizeof(add), " - %s", rets[-p->result]); - } else { - snprintf(add, sizeof(add), " - %d", p->result); - } - } - fprintf(stderr, "bus %s, port %s, dev %d, ep %d: packet %p: %s -> %s%s\n", - p->ep->dev->qdev.parent_bus->name, - p->ep->dev->port->path, - p->ep->dev->addr, p->ep->nr, - p, name[p->state], name[state], add); -#endif + trace_usb_packet_state_change(bus->busnr, dev->port->path, p->ep->nr, + p, name[p->state], name[state]); p->state = state; } @@ -373,12 +373,12 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p); int set_usb_string(uint8_t *buf, const char *str); /* usb-linux.c */ -USBDevice *usb_host_device_open(const char *devname); +USBDevice *usb_host_device_open(USBBus *bus, const char *devname); int usb_host_device_close(const char *devname); void usb_host_info(Monitor *mon); /* usb-bt.c */ -USBDevice *usb_bt_init(HCIInfo *hci); +USBDevice *usb_bt_init(USBBus *bus, HCIInfo *hci); /* usb ports of the VM */ @@ -431,7 +431,8 @@ struct USBBusOps { void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host); USBBus *usb_bus_find(int busnr); void usb_legacy_register(const char *typename, const char *usbdevice_name, - USBDevice *(*usbdevice_init)(const char *params)); + USBDevice *(*usbdevice_init)(USBBus *bus, + const char *params)); USBDevice *usb_create(USBBus *bus, const char *name); USBDevice *usb_create_simple(USBBus *bus, const char *name); USBDevice *usbdevice_create(const char *cmdline); diff --git a/libcacard/vcardt.h b/libcacard/vcardt.h index 538bdde3df..d4d8e2ed18 100644 --- a/libcacard/vcardt.h +++ b/libcacard/vcardt.h @@ -26,8 +26,8 @@ typedef struct VCardEmulStruct VCardEmul; #define MAX_CHANNEL 4 /* create an ATR with appropriate historical bytes */ -#define VCARD_ATR_PREFIX(size) 0x3b, 0x66+(size), 0x00, 0xff, \ - 'V', 'C', 'A', 'R', 'D', '_' +#define VCARD_ATR_PREFIX(size) (0x3b, 0x68+(size), 0x00, 0xff, \ + 'V', 'C', 'A', 'R', 'D', '_') typedef enum { diff --git a/trace-events b/trace-events index e918ff61fd..c5d0f0f547 100644 --- a/trace-events +++ b/trace-events @@ -227,6 +227,9 @@ sun4m_iommu_page_get_flags(uint64_t pa, uint64_t iopte, uint32_t ret) "get flags sun4m_iommu_translate_pa(uint64_t addr, uint64_t pa, uint32_t iopte) "xlate dva %"PRIx64" => pa %"PRIx64" iopte = %x" sun4m_iommu_bad_addr(uint64_t addr) "bad addr %"PRIx64 +# hw/usb.c +usb_packet_state_change(int bus, const char *port, int ep, void *p, const char *o, const char *n) "bus %d, port %s, ep %d, packet %p, state %s -> %s" + # hw/usb-bus.c usb_port_claim(int bus, const char *port) "bus %d, port %s" usb_port_attach(int bus, const char *port) "bus %d, port %s" @@ -167,10 +167,6 @@ static PixelFormat sdl_to_qemu_pixelformat(SDL_PixelFormat *sdl_pf) static DisplaySurface* sdl_create_displaysurface(int width, int height) { DisplaySurface *surface = (DisplaySurface*) g_malloc0(sizeof(DisplaySurface)); - if (surface == NULL) { - fprintf(stderr, "sdl_create_displaysurface: malloc failed\n"); - exit(1); - } surface->width = width; surface->height = height; diff --git a/ui/spice-core.c b/ui/spice-core.c index 1308a3d886..c1091e1602 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -139,8 +139,6 @@ static void watch_remove(SpiceWatch *watch) g_free(watch); } -#if SPICE_INTERFACE_CORE_MINOR >= 3 - typedef struct ChannelList ChannelList; struct ChannelList { SpiceChannelEventInfo *info; @@ -229,8 +227,8 @@ static void channel_event(int event, SpiceChannelEventInfo *info) add_addr_info(server, (struct sockaddr *)&info->laddr_ext, info->llen_ext); } else { - fprintf(stderr, "spice: %s, extended address is expected\n", - __func__); + error_report("spice: %s, extended address is expected", + __func__); #endif add_addr_info(client, &info->paddr, info->plen); add_addr_info(server, &info->laddr, info->llen); @@ -257,15 +255,6 @@ static void channel_event(int event, SpiceChannelEventInfo *info) } } -#else /* SPICE_INTERFACE_CORE_MINOR >= 3 */ - -static QList *channel_list_get(void) -{ - return NULL; -} - -#endif /* SPICE_INTERFACE_CORE_MINOR >= 3 */ - static SpiceCoreInterface core_interface = { .base.type = SPICE_INTERFACE_CORE, .base.description = "qemu core services", @@ -281,9 +270,7 @@ static SpiceCoreInterface core_interface = { .watch_update_mask = watch_update_mask, .watch_remove = watch_remove, -#if SPICE_INTERFACE_CORE_MINOR >= 3 .channel_event = channel_event, -#endif }; #ifdef SPICE_INTERFACE_MIGRATION @@ -346,7 +333,7 @@ static int parse_name(const char *string, const char *optname, if (value != -1) { return value; } - fprintf(stderr, "spice: invalid %s: %s\n", optname, string); + error_report("spice: invalid %s: %s", optname, string); exit(1); } @@ -490,7 +477,6 @@ static void migration_state_notifier(Notifier *notifier, void *data) spice_server_migrate_start(spice_server); #endif } else if (migration_has_finished(s)) { -#if SPICE_SERVER_VERSION >= 0x000701 /* 0.7.1 */ #ifndef SPICE_INTERFACE_MIGRATION spice_server_migrate_switch(spice_server); #else @@ -498,7 +484,6 @@ static void migration_state_notifier(Notifier *notifier, void *data) } else if (migration_has_failed(s)) { spice_server_migrate_end(spice_server, false); #endif -#endif } } @@ -526,6 +511,12 @@ static int add_channel(const char *name, const char *value, void *opaque) int rc; if (strcmp(name, "tls-channel") == 0) { + int *tls_port = opaque; + if (!*tls_port) { + error_report("spice: tried to setup tls-channel" + " without specifying a TLS port"); + exit(1); + } security = SPICE_CHANNEL_SECURITY_SSL; } if (strcmp(name, "plaintext-channel") == 0) { @@ -540,7 +531,7 @@ static int add_channel(const char *name, const char *value, void *opaque) rc = spice_server_set_channel_security(spice_server, value, security); } if (rc != 0) { - fprintf(stderr, "spice: failed to set channel security for %s\n", value); + error_report("spice: failed to set channel security for %s", value); exit(1); } return 0; @@ -568,15 +559,15 @@ void qemu_spice_init(void) port = qemu_opt_get_number(opts, "port", 0); tls_port = qemu_opt_get_number(opts, "tls-port", 0); if (!port && !tls_port) { - fprintf(stderr, "neither port nor tls-port specified for spice."); + error_report("neither port nor tls-port specified for spice"); exit(1); } if (port < 0 || port > 65535) { - fprintf(stderr, "spice port is out of range"); + error_report("spice port is out of range"); exit(1); } if (tls_port < 0 || tls_port > 65535) { - fprintf(stderr, "spice tls-port is out of range"); + error_report("spice tls-port is out of range"); exit(1); } password = qemu_opt_get(opts, "password"); @@ -646,11 +637,11 @@ void qemu_spice_init(void) #if SPICE_SERVER_VERSION >= 0x000900 /* 0.9.0 */ if (spice_server_set_sasl_appname(spice_server, "qemu") == -1 || spice_server_set_sasl(spice_server, 1) == -1) { - fprintf(stderr, "spice: failed to enable sasl\n"); + error_report("spice: failed to enable sasl"); exit(1); } #else - fprintf(stderr, "spice: sasl is not available (spice >= 0.9 required)\n"); + error_report("spice: sasl is not available (spice >= 0.9 required)"); exit(1); #endif } @@ -659,11 +650,9 @@ void qemu_spice_init(void) spice_server_set_noauth(spice_server); } -#if SPICE_SERVER_VERSION >= 0x000801 if (qemu_opt_get_bool(opts, "disable-copy-paste", 0)) { spice_server_set_agent_copypaste(spice_server, false); } -#endif compression = SPICE_IMAGE_COMPRESS_AUTO_GLZ; str = qemu_opt_get(opts, "image-compression"); @@ -697,10 +686,10 @@ void qemu_spice_init(void) spice_server_set_playback_compression (spice_server, qemu_opt_get_bool(opts, "playback-compression", 1)); - qemu_opt_foreach(opts, add_channel, NULL, 0); + qemu_opt_foreach(opts, add_channel, &tls_port, 0); if (0 != spice_server_init(spice_server, &core_interface)) { - fprintf(stderr, "failed to initialize spice server"); + error_report("failed to initialize spice server"); exit(1); }; using_spice = 1; @@ -725,7 +714,7 @@ int qemu_spice_add_interface(SpiceBaseInstance *sin) { if (!spice_server) { if (QTAILQ_FIRST(&qemu_spice_opts.head) != NULL) { - fprintf(stderr, "Oops: spice configured but not active\n"); + error_report("Oops: spice configured but not active"); exit(1); } /* diff --git a/ui/spice-display.c b/ui/spice-display.c index 6c302a3909..ab266aedc1 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -60,15 +60,23 @@ void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r) dest->right = MAX(dest->right, r->right); } +QXLCookie *qxl_cookie_new(int type, uint64_t io) +{ + QXLCookie *cookie; + + cookie = g_malloc0(sizeof(*cookie)); + cookie->type = type; + cookie->io = io; + return cookie; +} + void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot, qxl_async_io async) { if (async != QXL_SYNC) { -#if SPICE_INTERFACE_QXL_MINOR >= 1 - spice_qxl_add_memslot_async(&ssd->qxl, memslot, 0); -#else - abort(); -#endif + spice_qxl_add_memslot_async(&ssd->qxl, memslot, + (uint64_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_MEMSLOT_ADD_ASYNC)); } else { ssd->worker->add_memslot(ssd->worker, memslot); } @@ -84,11 +92,9 @@ void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id, qxl_async_io async) { if (async != QXL_SYNC) { -#if SPICE_INTERFACE_QXL_MINOR >= 1 - spice_qxl_create_primary_surface_async(&ssd->qxl, id, surface, 0); -#else - abort(); -#endif + spice_qxl_create_primary_surface_async(&ssd->qxl, id, surface, + (uint64_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_CREATE_PRIMARY_ASYNC)); } else { ssd->worker->create_primary_surface(ssd->worker, id, surface); } @@ -99,11 +105,9 @@ void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id, qxl_async_io async) { if (async != QXL_SYNC) { -#if SPICE_INTERFACE_QXL_MINOR >= 1 - spice_qxl_destroy_primary_surface_async(&ssd->qxl, id, 0); -#else - abort(); -#endif + spice_qxl_destroy_primary_surface_async(&ssd->qxl, id, + (uint64_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_DESTROY_PRIMARY_ASYNC)); } else { ssd->worker->destroy_primary_surface(ssd->worker, id); } @@ -317,16 +321,8 @@ void qemu_spice_display_resize(SimpleSpiceDisplay *ssd) ssd->notify++; } -void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd) +void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd) { - dprint(3, "%s:\n", __FUNCTION__); - vga_hw_update(); - - qemu_mutex_lock(&ssd->lock); - if (ssd->update == NULL) { - ssd->update = qemu_spice_create_update(ssd); - ssd->notify++; - } if (ssd->cursor) { ssd->ds->cursor_define(ssd->cursor); cursor_put(ssd->cursor); @@ -337,6 +333,19 @@ void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd) ssd->mouse_x = -1; ssd->mouse_y = -1; } +} + +void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd) +{ + dprint(3, "%s:\n", __func__); + vga_hw_update(); + + qemu_mutex_lock(&ssd->lock); + if (ssd->update == NULL) { + ssd->update = qemu_spice_create_update(ssd); + ssd->notify++; + } + qemu_spice_cursor_refresh_unlocked(ssd); qemu_mutex_unlock(&ssd->lock); if (ssd->notify) { diff --git a/ui/spice-display.h b/ui/spice-display.h index 5e52df99be..12e50b6efc 100644 --- a/ui/spice-display.h +++ b/ui/spice-display.h @@ -48,6 +48,26 @@ typedef enum qxl_async_io { QXL_ASYNC, } qxl_async_io; +enum { + QXL_COOKIE_TYPE_IO, + QXL_COOKIE_TYPE_RENDER_UPDATE_AREA, +}; + +typedef struct QXLCookie { + int type; + uint64_t io; + union { + uint32_t surface_id; + QXLRect area; + struct { + QXLRect area; + int redraw; + } render; + } u; +} QXLCookie; + +QXLCookie *qxl_cookie_new(int type, uint64_t io); + typedef struct SimpleSpiceDisplay SimpleSpiceDisplay; typedef struct SimpleSpiceUpdate SimpleSpiceUpdate; @@ -97,6 +117,7 @@ void qemu_spice_display_update(SimpleSpiceDisplay *ssd, int x, int y, int w, int h); void qemu_spice_display_resize(SimpleSpiceDisplay *ssd); void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd); +void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd); void qemu_spice_add_memslot(SimpleSpiceDisplay *ssd, QXLDevMemSlot *memslot, qxl_async_io async); @@ -298,7 +298,7 @@ static int usb_host_initfn(USBDevice *dev) return 0; } -USBDevice *usb_host_device_open(const char *devname) +USBDevice *usb_host_device_open(USBBus *guest_bus, const char *devname) { struct usb_device_info bus_info, dev_info; USBDevice *d = NULL, *ret = NULL; @@ -358,7 +358,7 @@ USBDevice *usb_host_device_open(const char *devname) goto fail_dfd; } - d = usb_create(NULL /* FIXME */, "usb-host"); + d = usb_create(guest_bus, "usb-host"); dev = DO_UPCAST(USBHostDevice, dev, d); if (dev_info.udi_speed == 1) { diff --git a/usb-linux.c b/usb-linux.c index 24700fc1f7..47994f3ceb 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -1443,13 +1443,13 @@ static void usb_host_register_types(void) type_init(usb_host_register_types) -USBDevice *usb_host_device_open(const char *devname) +USBDevice *usb_host_device_open(USBBus *bus, const char *devname) { struct USBAutoFilter filter; USBDevice *dev; char *p; - dev = usb_create(NULL /* FIXME */, "usb-host"); + dev = usb_create(bus, "usb-host"); if (strstr(devname, "auto:")) { if (parse_filter(devname, &filter) < 0) { diff --git a/usb-redir.c b/usb-redir.c index a59b347f01..755492f379 100644 --- a/usb-redir.c +++ b/usb-redir.c @@ -106,6 +106,7 @@ struct AsyncURB { QTAILQ_ENTRY(AsyncURB)next; }; +static void usbredir_hello(void *priv, struct usb_redir_hello_header *h); static void usbredir_device_connect(void *priv, struct usb_redir_device_connect_header *device_connect); static void usbredir_device_disconnect(void *priv); @@ -430,7 +431,7 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, /* Check iso_error for stream errors, otherwise its an underrun */ status = dev->endpoint[EP2I(ep)].iso_error; dev->endpoint[EP2I(ep)].iso_error = 0; - return usbredir_handle_status(dev, status, 0); + return status ? USB_RET_NAK : 0; } DPRINTF2("iso-token-in ep %02X status %d len %d queue-size: %d\n", ep, isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size); @@ -438,7 +439,7 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, status = isop->status; if (status != usb_redir_success) { bufp_free(dev, isop, ep); - return usbredir_handle_status(dev, status, 0); + return USB_RET_NAK; } len = isop->len; @@ -547,7 +548,10 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev, /* Check interrupt_error for stream errors */ status = dev->endpoint[EP2I(ep)].interrupt_error; dev->endpoint[EP2I(ep)].interrupt_error = 0; - return usbredir_handle_status(dev, status, 0); + if (status) { + return usbredir_handle_status(dev, status, 0); + } + return USB_RET_NAK; } DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep, intp->status, intp->len); @@ -802,6 +806,7 @@ static void usbredir_open_close_bh(void *opaque) dev->parser->log_func = usbredir_log; dev->parser->read_func = usbredir_read; dev->parser->write_func = usbredir_write; + dev->parser->hello_func = usbredir_hello; dev->parser->device_connect_func = usbredir_device_connect; dev->parser->device_disconnect_func = usbredir_device_disconnect; dev->parser->interface_info_func = usbredir_interface_info; @@ -820,6 +825,7 @@ static void usbredir_open_close_bh(void *opaque) dev->read_buf_size = 0; usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version); + usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); usbredirparser_init(dev->parser, VERSION, caps, USB_REDIR_CAPS_SIZE, 0); usbredirparser_do_write(dev->parser); } @@ -958,7 +964,7 @@ static int usbredir_check_filter(USBRedirDevice *dev) { if (dev->interface_info.interface_count == 0) { ERROR("No interface info for device\n"); - return -1; + goto error; } if (dev->filter_rules) { @@ -966,7 +972,7 @@ static int usbredir_check_filter(USBRedirDevice *dev) usb_redir_cap_connect_device_version)) { ERROR("Device filter specified and peer does not have the " "connect_device_version capability\n"); - return -1; + goto error; } if (usbredirfilter_check( @@ -983,11 +989,19 @@ static int usbredir_check_filter(USBRedirDevice *dev) dev->device_info.product_id, dev->device_info.device_version_bcd, 0) != 0) { - return -1; + goto error; } } return 0; + +error: + 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); + } + return -1; } /* @@ -1012,6 +1026,19 @@ static int usbredir_handle_status(USBRedirDevice *dev, } } +static void usbredir_hello(void *priv, struct usb_redir_hello_header *h) +{ + USBRedirDevice *dev = priv; + + /* Try to send the filter info now that we've the usb-host's caps */ + if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter) && + dev->filter_rules) { + usbredirparser_send_filter_filter(dev->parser, dev->filter_rules, + dev->filter_rules_count); + usbredirparser_do_write(dev->parser); + } +} + static void usbredir_device_connect(void *priv, struct usb_redir_device_connect_header *device_connect) { @@ -1049,8 +1076,10 @@ static void usbredir_device_connect(void *priv, usb_redir_cap_connect_device_version)) { INFO("attaching %s device %04x:%04x version %d.%d class %02x\n", speed, device_connect->vendor_id, device_connect->product_id, - device_connect->device_version_bcd >> 8, - device_connect->device_version_bcd & 0xff, + ((device_connect->device_version_bcd & 0xf000) >> 12) * 10 + + ((device_connect->device_version_bcd & 0x0f00) >> 8), + ((device_connect->device_version_bcd & 0x00f0) >> 4) * 10 + + ((device_connect->device_version_bcd & 0x000f) >> 0), device_connect->device_class); } else { INFO("attaching %s device %04x:%04x class %02x\n", speed, @@ -1111,7 +1140,6 @@ static void usbredir_interface_info(void *priv, if (usbredir_check_filter(dev)) { ERROR("Device no longer matches filter after interface info " "change, disconnecting!\n"); - usbredir_device_disconnect(dev); } } } @@ -1052,12 +1052,13 @@ static int usb_device_add(const char *devname) #ifndef CONFIG_LINUX /* only the linux version is qdev-ified, usb-bsd still needs this */ if (strstart(devname, "host:", &p)) { - dev = usb_host_device_open(p); + dev = usb_host_device_open(usb_bus_find(-1), p); } else #endif if (!strcmp(devname, "bt") || strstart(devname, "bt:", &p)) { - dev = usb_bt_init(devname[2] ? hci_init(p) : - bt_new_hci(qemu_find_bt_vlan(0))); + dev = usb_bt_init(usb_bus_find(-1), + devname[2] ? hci_init(p) + : bt_new_hci(qemu_find_bt_vlan(0))); } else { return -1; } |