aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile3
-rw-r--r--TODO37
-rw-r--r--aio-posix.c130
-rw-r--r--async.c2
-rwxr-xr-xconfigure54
-rw-r--r--coroutine-sigaltstack.c26
-rw-r--r--coroutine-ucontext.c27
-rw-r--r--cpu-exec.c6
-rw-r--r--default-configs/alpha-softmmu.mak1
-rw-r--r--default-configs/arm-softmmu.mak1
-rw-r--r--default-configs/i386-softmmu.mak1
-rw-r--r--default-configs/m68k-softmmu.mak1
-rw-r--r--default-configs/mips-softmmu.mak1
-rw-r--r--default-configs/mips64-softmmu.mak1
-rw-r--r--default-configs/mips64el-softmmu.mak1
-rw-r--r--default-configs/mipsel-softmmu.mak1
-rw-r--r--default-configs/ppc-softmmu.mak1
-rw-r--r--default-configs/ppc64-softmmu.mak1
-rw-r--r--default-configs/ppcemb-softmmu.mak1
-rw-r--r--default-configs/sh4-softmmu.mak1
-rw-r--r--default-configs/sh4eb-softmmu.mak1
-rw-r--r--default-configs/sparc64-softmmu.mak1
-rw-r--r--default-configs/usb.mak8
-rw-r--r--default-configs/x86_64-softmmu.mak1
-rw-r--r--disas/i386.c9
-rw-r--r--disas/m68k.c11
-rw-r--r--hw/Makefile.objs2
-rw-r--r--hw/grlib_apbuart.c52
-rw-r--r--hw/ppc4xx_devs.c2
-rw-r--r--hw/qxl-render.c3
-rw-r--r--hw/usb.h13
-rw-r--r--hw/usb/Makefile.objs30
-rw-r--r--hw/usb/core.c35
-rw-r--r--hw/usb/desc.c9
-rw-r--r--hw/usb/dev-bluetooth.c2
-rw-r--r--hw/usb/dev-hid.c2
-rw-r--r--hw/usb/dev-hub.c10
-rw-r--r--hw/usb/dev-network.c2
-rw-r--r--hw/usb/dev-smartcard-reader.c2
-rw-r--r--hw/usb/dev-storage.c5
-rw-r--r--hw/usb/dev-uas.c247
-rw-r--r--hw/usb/dev-wacom.c4
-rw-r--r--hw/usb/hcd-ehci.c7
-rw-r--r--hw/usb/hcd-musb.c2
-rw-r--r--hw/usb/hcd-ohci.c4
-rw-r--r--hw/usb/hcd-uhci.c2
-rw-r--r--hw/usb/hcd-xhci.c275
-rw-r--r--hw/usb/host-bsd.c6
-rw-r--r--hw/usb/host-legacy.c144
-rw-r--r--hw/usb/host-linux.c125
-rw-r--r--hw/usb/host-stub.c5
-rw-r--r--hw/usb/host.h44
-rw-r--r--hw/usb/redirect.c20
-rw-r--r--hw/vga.c18
-rw-r--r--hw/vmware_vga.c2
-rw-r--r--hw/xenfb.c3
-rw-r--r--hw/xilinx_axienet.c4
-rw-r--r--include/block/aio.h3
-rw-r--r--include/exec/cpu-defs.h2
-rw-r--r--include/qemu/log.h8
-rw-r--r--include/qemu/main-loop.h4
-rw-r--r--include/sysemu/os-win32.h8
-rw-r--r--include/sysemu/sysemu.h2
-rw-r--r--include/ui/console.h13
-rw-r--r--iohandler.c40
-rw-r--r--main-loop.c158
-rw-r--r--monitor.c6
-rw-r--r--po/Makefile46
-rw-r--r--po/de_DE.po41
-rw-r--r--po/it.po41
-rw-r--r--po/messages.po41
-rw-r--r--qapi-schema.json6
-rw-r--r--qemu-char.c2
-rw-r--r--qemu-options.hx4
-rw-r--r--qom/object.c4
-rw-r--r--scripts/make_device_config.sh2
-rw-r--r--slirp/libslirp.h6
-rw-r--r--slirp/main.h1
-rw-r--r--slirp/slirp.c673
-rw-r--r--slirp/socket.c9
-rw-r--r--slirp/socket.h2
-rw-r--r--stubs/slirp.c6
-rw-r--r--target-cris/translate.c2
-rw-r--r--target-lm32/translate.c2
-rw-r--r--target-microblaze/translate.c2
-rw-r--r--target-openrisc/translate.c2
-rw-r--r--target-sparc/cpu.c6
-rw-r--r--target-sparc/cpu.h1
-rw-r--r--target-sparc/helper.c11
-rw-r--r--target-sparc/helper.h1
-rw-r--r--target-sparc/ldst_helper.c6
-rw-r--r--target-sparc/translate.c5
-rw-r--r--tests/.gitignore7
-rw-r--r--tests/rtc-test.c238
-rw-r--r--trace-events6
-rw-r--r--ui/Makefile.objs3
-rw-r--r--ui/console.c23
-rw-r--r--ui/gtk.c1217
-rw-r--r--user-exec.c2
-rw-r--r--vl.c53
101 files changed, 3160 insertions, 967 deletions
diff --git a/.gitignore b/.gitignore
index 53fe9c3078..27ad002970 100644
--- a/.gitignore
+++ b/.gitignore
@@ -83,12 +83,15 @@ fsdev/virtfs-proxy-helper.pod
patches
pc-bios/bios-pq/status
pc-bios/vgabios-pq/status
+pc-bios/optionrom/linuxboot.asm
pc-bios/optionrom/linuxboot.bin
pc-bios/optionrom/linuxboot.raw
pc-bios/optionrom/linuxboot.img
+pc-bios/optionrom/multiboot.asm
pc-bios/optionrom/multiboot.bin
pc-bios/optionrom/multiboot.raw
pc-bios/optionrom/multiboot.img
+pc-bios/optionrom/kvmvapic.asm
pc-bios/optionrom/kvmvapic.bin
pc-bios/optionrom/kvmvapic.raw
pc-bios/optionrom/kvmvapic.img
diff --git a/Makefile b/Makefile
index 0d9099a473..2262410f0f 100644
--- a/Makefile
+++ b/Makefile
@@ -314,6 +314,9 @@ ifneq ($(BLOBS),)
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \
done
endif
+ifeq ($(CONFIG_GTK),y)
+ $(MAKE) -C po $@
+endif
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps"
set -e; for x in $(KEYMAPS); do \
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(qemu_datadir)/keymaps"; \
diff --git a/TODO b/TODO
deleted file mode 100644
index 1d4c638f27..0000000000
--- a/TODO
+++ /dev/null
@@ -1,37 +0,0 @@
-General:
--------
-- cycle counter for all archs
-- cpu_interrupt() win32/SMP fix
-- merge PIC spurious interrupt patch
-- warning for OS/2: must not use 128 MB memory (merge bochs cmos patch ?)
-- config file (at least for windows/Mac OS X)
-- update doc: PCI infos.
-- basic VGA optimizations
-- better code fetch
-- do not resize vga if invalid size.
-- TLB code protection support for PPC
-- disable SMC handling for ARM/SPARC/PPC (not finished)
-- see undefined flags for BTx insn
-- keyboard output buffer filling timing emulation
-- tests for each target CPU
-- fix all remaining thread lock issues (must put TBs in a specific invalid
- state, find a solution for tb_flush()).
-
-ppc specific:
-------------
-- TLB invalidate not needed if msr_pr changes
-- enable shift optimizations ?
-
-linux-user specific:
--------------------
-- remove threading support as it cannot work at this point
-- improve IPC syscalls
-- more syscalls (in particular all 64 bit ones, IPCs, fix 64 bit
- issues, fix 16 bit uid issues)
-- use kernel traps for unaligned accesses on ARM ?
-
-
-lower priority:
---------------
-- int15 ah=86: use better timing
-- use -msoft-float on ARM
diff --git a/aio-posix.c b/aio-posix.c
index fe4dbb4523..b68eccd40c 100644
--- a/aio-posix.c
+++ b/aio-posix.c
@@ -25,6 +25,7 @@ struct AioHandler
IOHandler *io_write;
AioFlushHandler *io_flush;
int deleted;
+ int pollfds_idx;
void *opaque;
QLIST_ENTRY(AioHandler) node;
};
@@ -85,9 +86,10 @@ void aio_set_fd_handler(AioContext *ctx,
node->io_write = io_write;
node->io_flush = io_flush;
node->opaque = opaque;
+ node->pollfds_idx = -1;
- node->pfd.events = (io_read ? G_IO_IN | G_IO_HUP : 0);
- node->pfd.events |= (io_write ? G_IO_OUT : 0);
+ node->pfd.events = (io_read ? G_IO_IN | G_IO_HUP | G_IO_ERR : 0);
+ node->pfd.events |= (io_write ? G_IO_OUT | G_IO_ERR : 0);
}
aio_notify(ctx);
@@ -110,13 +112,6 @@ bool aio_pending(AioContext *ctx)
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
int revents;
- /*
- * FIXME: right now we cannot get G_IO_HUP and G_IO_ERR because
- * main-loop.c is still select based (due to the slirp legacy).
- * If main-loop.c ever switches to poll, G_IO_ERR should be
- * tested too. Dispatching G_IO_ERR to both handlers should be
- * okay, since handlers need to be ready for spurious wakeups.
- */
revents = node->pfd.revents & node->pfd.events;
if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read) {
return true;
@@ -129,30 +124,12 @@ bool aio_pending(AioContext *ctx)
return false;
}
-bool aio_poll(AioContext *ctx, bool blocking)
+static bool aio_dispatch(AioContext *ctx)
{
- static struct timeval tv0;
AioHandler *node;
- fd_set rdfds, wrfds;
- int max_fd = -1;
- int ret;
- bool busy, progress;
-
- progress = false;
-
- /*
- * If there are callbacks left that have been queued, we need to call then.
- * Do not call select in this case, because it is possible that the caller
- * does not need a complete flush (as is the case for qemu_aio_wait loops).
- */
- if (aio_bh_poll(ctx)) {
- blocking = false;
- progress = true;
- }
+ bool progress = false;
/*
- * Then dispatch any pending callbacks from the GSource.
- *
* We have to walk very carefully in case qemu_aio_set_fd_handler is
* called while we're walking.
*/
@@ -166,12 +143,15 @@ bool aio_poll(AioContext *ctx, bool blocking)
revents = node->pfd.revents & node->pfd.events;
node->pfd.revents = 0;
- /* See comment in aio_pending. */
- if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read) {
+ if (!node->deleted &&
+ (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) &&
+ node->io_read) {
node->io_read(node->opaque);
progress = true;
}
- if (revents & (G_IO_OUT | G_IO_ERR) && node->io_write) {
+ if (!node->deleted &&
+ (revents & (G_IO_OUT | G_IO_ERR)) &&
+ node->io_write) {
node->io_write(node->opaque);
progress = true;
}
@@ -186,6 +166,30 @@ bool aio_poll(AioContext *ctx, bool blocking)
g_free(tmp);
}
}
+ return progress;
+}
+
+bool aio_poll(AioContext *ctx, bool blocking)
+{
+ AioHandler *node;
+ int ret;
+ bool busy, progress;
+
+ progress = false;
+
+ /*
+ * If there are callbacks left that have been queued, we need to call them.
+ * Do not call select in this case, because it is possible that the caller
+ * does not need a complete flush (as is the case for qemu_aio_wait loops).
+ */
+ if (aio_bh_poll(ctx)) {
+ blocking = false;
+ progress = true;
+ }
+
+ if (aio_dispatch(ctx)) {
+ progress = true;
+ }
if (progress && !blocking) {
return true;
@@ -193,12 +197,13 @@ bool aio_poll(AioContext *ctx, bool blocking)
ctx->walking_handlers++;
- FD_ZERO(&rdfds);
- FD_ZERO(&wrfds);
+ g_array_set_size(ctx->pollfds, 0);
- /* fill fd sets */
+ /* fill pollfds */
busy = false;
QLIST_FOREACH(node, &ctx->aio_handlers, node) {
+ node->pollfds_idx = -1;
+
/* If there aren't pending AIO operations, don't invoke callbacks.
* Otherwise, if there are no AIO requests, qemu_aio_wait() would
* wait indefinitely.
@@ -209,13 +214,13 @@ bool aio_poll(AioContext *ctx, bool blocking)
}
busy = true;
}
- if (!node->deleted && node->io_read) {
- FD_SET(node->pfd.fd, &rdfds);
- max_fd = MAX(max_fd, node->pfd.fd + 1);
- }
- if (!node->deleted && node->io_write) {
- FD_SET(node->pfd.fd, &wrfds);
- max_fd = MAX(max_fd, node->pfd.fd + 1);
+ if (!node->deleted && node->pfd.events) {
+ GPollFD pfd = {
+ .fd = node->pfd.fd,
+ .events = node->pfd.events,
+ };
+ node->pollfds_idx = ctx->pollfds->len;
+ g_array_append_val(ctx->pollfds, pfd);
}
}
@@ -227,41 +232,22 @@ bool aio_poll(AioContext *ctx, bool blocking)
}
/* wait until next event */
- ret = select(max_fd, &rdfds, &wrfds, NULL, blocking ? NULL : &tv0);
+ ret = g_poll((GPollFD *)ctx->pollfds->data,
+ ctx->pollfds->len,
+ blocking ? -1 : 0);
/* if we have any readable fds, dispatch event */
if (ret > 0) {
- /* we have to walk very carefully in case
- * qemu_aio_set_fd_handler is called while we're walking */
- node = QLIST_FIRST(&ctx->aio_handlers);
- while (node) {
- AioHandler *tmp;
-
- ctx->walking_handlers++;
-
- if (!node->deleted &&
- FD_ISSET(node->pfd.fd, &rdfds) &&
- node->io_read) {
- node->io_read(node->opaque);
- progress = true;
- }
- if (!node->deleted &&
- FD_ISSET(node->pfd.fd, &wrfds) &&
- node->io_write) {
- node->io_write(node->opaque);
- progress = true;
- }
-
- tmp = node;
- node = QLIST_NEXT(node, node);
-
- ctx->walking_handlers--;
-
- if (!ctx->walking_handlers && tmp->deleted) {
- QLIST_REMOVE(tmp, node);
- g_free(tmp);
+ QLIST_FOREACH(node, &ctx->aio_handlers, node) {
+ if (node->pollfds_idx != -1) {
+ GPollFD *pfd = &g_array_index(ctx->pollfds, GPollFD,
+ node->pollfds_idx);
+ node->pfd.revents = pfd->revents;
}
}
+ if (aio_dispatch(ctx)) {
+ progress = true;
+ }
}
assert(progress || busy);
diff --git a/async.c b/async.c
index 72d268ae35..f2d47ba96d 100644
--- a/async.c
+++ b/async.c
@@ -174,6 +174,7 @@ aio_ctx_finalize(GSource *source)
aio_set_event_notifier(ctx, &ctx->notifier, NULL, NULL);
event_notifier_cleanup(&ctx->notifier);
+ g_array_free(ctx->pollfds, TRUE);
}
static GSourceFuncs aio_source_funcs = {
@@ -198,6 +199,7 @@ AioContext *aio_context_new(void)
{
AioContext *ctx;
ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext));
+ ctx->pollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD));
event_notifier_init(&ctx->notifier, false);
aio_set_event_notifier(ctx, &ctx->notifier,
(EventNotifierHandler *)
diff --git a/configure b/configure
index bf5970f74b..dcaa67c6d3 100755
--- a/configure
+++ b/configure
@@ -226,6 +226,7 @@ coroutine=""
seccomp=""
glusterfs=""
virtio_blk_data_plane=""
+gtk=""
# parse CC options first
for opt do
@@ -897,6 +898,10 @@ for opt do
;;
--enable-virtio-blk-data-plane) virtio_blk_data_plane="yes"
;;
+ --disable-gtk) gtk="no"
+ ;;
+ --enable-gtk) gtk="yes"
+ ;;
*) echo "ERROR: unknown option $opt"; show_help="yes"
;;
esac
@@ -1636,6 +1641,26 @@ if test "$sparse" != "no" ; then
fi
##########################################
+# GTK probe
+
+if test "$gtk" != "no"; then
+ if $pkg_config --exists 'gtk+-2.0 >= 2.18.0' && \
+ $pkg_config --exists 'vte >= 0.24.0'; then
+ gtk_cflags=`$pkg_config --cflags gtk+-2.0 2>/dev/null`
+ gtk_libs=`$pkg_config --libs gtk+-2.0 2>/dev/null`
+ vte_cflags=`$pkg_config --cflags vte 2>/dev/null`
+ vte_libs=`$pkg_config --libs vte 2>/dev/null`
+ libs_softmmu="$gtk_libs $vte_libs $libs_softmmu"
+ gtk="yes"
+ else
+ if test "$gtk" = "yes" ; then
+ feature_not_found "gtk"
+ fi
+ gtk="no"
+ fi
+fi
+
+##########################################
# SDL probe
# Look for sdl configuration program (pkg-config or sdl-config). Try
@@ -3090,20 +3115,27 @@ if compile_prog "" "" ; then
fi
########################################
-# check whether we can disable the -Wunused-but-set-variable
-# option with a pragma (this is needed to silence a warning in
-# some versions of the valgrind VALGRIND_STACK_DEREGISTER macro.)
-# This test has to be compiled with -Werror as otherwise an
-# unknown pragma is only a warning.
+# check whether we can disable warning option with a pragma (this is needed
+# to silence warnings in the headers of some versions of external libraries).
+# This test has to be compiled with -Werror as otherwise an unknown pragma is
+# only a warning.
+#
+# If we can't selectively disable warning in the code, disable -Werror so that
+# the build doesn't fail anyway.
+
pragma_disable_unused_but_set=no
cat > $TMPC << EOF
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+
int main(void) {
return 0;
}
EOF
if compile_prog "-Werror" "" ; then
pragma_diagnostic_available=yes
+else
+ werror=no
fi
########################################
@@ -3218,6 +3250,7 @@ fi
qemu_confdir=$sysconfdir$confsuffix
qemu_datadir=$datadir$confsuffix
+qemu_localedir="$datadir/locale"
tools=""
if test "$want_tools" = "yes" ; then
@@ -3301,6 +3334,7 @@ if test "$darwin" = "yes" ; then
fi
echo "pixman $pixman"
echo "SDL support $sdl"
+echo "GTK support $gtk"
echo "curses support $curses"
echo "curl support $curl"
echo "mingw32 support $mingw32"
@@ -3390,6 +3424,7 @@ echo "qemu_localstatedir=$local_statedir" >> $config_host_mak
echo "qemu_helperdir=$libexecdir" >> $config_host_mak
echo "extra_cflags=$EXTRA_CFLAGS" >> $config_host_mak
echo "extra_ldflags=$EXTRA_LDFLAGS" >> $config_host_mak
+echo "qemu_localedir=$qemu_localedir" >> $config_host_mak
echo "ARCH=$ARCH" >> $config_host_mak
if test "$debug_tcg" = "yes" ; then
@@ -3591,6 +3626,11 @@ if test "$bluez" = "yes" ; then
echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak
fi
echo "GLIB_CFLAGS=$glib_cflags" >> $config_host_mak
+if test "$gtk" = "yes" ; then
+ echo "CONFIG_GTK=y" >> $config_host_mak
+ echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak
+ echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
+fi
if test "$xen" = "yes" ; then
echo "CONFIG_XEN_BACKEND=y" >> $config_host_mak
echo "CONFIG_XEN_CTRL_INTERFACE_VERSION=$xen_ctrl_version" >> $config_host_mak
@@ -3723,7 +3763,7 @@ fi
# USB host support
case "$usb" in
linux)
- echo "HOST_USB=linux" >> $config_host_mak
+ echo "HOST_USB=linux legacy" >> $config_host_mak
;;
bsd)
echo "HOST_USB=bsd" >> $config_host_mak
@@ -4305,7 +4345,7 @@ DIRS="$DIRS roms/seabios roms/vgabios"
DIRS="$DIRS qapi-generated"
FILES="Makefile tests/tcg/Makefile qdict-test-data.txt"
FILES="$FILES tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit"
-FILES="$FILES tests/tcg/lm32/Makefile"
+FILES="$FILES tests/tcg/lm32/Makefile po/Makefile"
FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps"
FILES="$FILES pc-bios/spapr-rtas/Makefile"
FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile"
diff --git a/coroutine-sigaltstack.c b/coroutine-sigaltstack.c
index e37ebac9c4..1fb41c9f14 100644
--- a/coroutine-sigaltstack.c
+++ b/coroutine-sigaltstack.c
@@ -45,7 +45,7 @@ static unsigned int pool_size;
typedef struct {
Coroutine base;
void *stack;
- jmp_buf env;
+ sigjmp_buf env;
} CoroutineUContext;
/**
@@ -59,7 +59,7 @@ typedef struct {
CoroutineUContext leader;
/** Information for the signal handler (trampoline) */
- jmp_buf tr_reenter;
+ sigjmp_buf tr_reenter;
volatile sig_atomic_t tr_called;
void *tr_handler;
} CoroutineThreadState;
@@ -115,8 +115,8 @@ static void __attribute__((constructor)) coroutine_init(void)
static void coroutine_bootstrap(CoroutineUContext *self, Coroutine *co)
{
/* Initialize longjmp environment and switch back the caller */
- if (!setjmp(self->env)) {
- longjmp(*(jmp_buf *)co->entry_arg, 1);
+ if (!sigsetjmp(self->env, 0)) {
+ siglongjmp(*(sigjmp_buf *)co->entry_arg, 1);
}
while (true) {
@@ -145,14 +145,14 @@ static void coroutine_trampoline(int signal)
/*
* Here we have to do a bit of a ping pong between the caller, given that
* this is a signal handler and we have to do a return "soon". Then the
- * caller can reestablish everything and do a longjmp here again.
+ * caller can reestablish everything and do a siglongjmp here again.
*/
- if (!setjmp(coTS->tr_reenter)) {
+ if (!sigsetjmp(coTS->tr_reenter, 0)) {
return;
}
/*
- * Ok, the caller has longjmp'ed back to us, so now prepare
+ * Ok, the caller has siglongjmp'ed back to us, so now prepare
* us for the real machine state switching. We have to jump
* into another function here to get a new stack context for
* the auto variables (which have to be auto-variables
@@ -179,7 +179,7 @@ static Coroutine *coroutine_new(void)
/* The way to manipulate stack is with the sigaltstack function. We
* prepare a stack, with it delivering a signal to ourselves and then
- * put setjmp/longjmp where needed.
+ * put sigsetjmp/siglongjmp where needed.
* This has been done keeping coroutine-ucontext as a model and with the
* pth ideas (GNU Portable Threads). See coroutine-ucontext for the basics
* of the coroutines and see pth_mctx.c (from the pth project) for the
@@ -220,7 +220,7 @@ static Coroutine *coroutine_new(void)
/*
* Now transfer control onto the signal stack and set it up.
- * It will return immediately via "return" after the setjmp()
+ * It will return immediately via "return" after the sigsetjmp()
* was performed. Be careful here with race conditions. The
* signal can be delivered the first time sigsuspend() is
* called.
@@ -261,8 +261,8 @@ static Coroutine *coroutine_new(void)
* type-conversion warnings related to the `volatile' qualifier and
* the fact that `jmp_buf' usually is an array type.
*/
- if (!setjmp(old_env)) {
- longjmp(coTS->tr_reenter, 1);
+ if (!sigsetjmp(old_env, 0)) {
+ siglongjmp(coTS->tr_reenter, 1);
}
/*
@@ -311,9 +311,9 @@ CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
s->current = to_;
- ret = setjmp(from->env);
+ ret = sigsetjmp(from->env, 0);
if (ret == 0) {
- longjmp(to->env, action);
+ siglongjmp(to->env, action);
}
return ret;
}
diff --git a/coroutine-ucontext.c b/coroutine-ucontext.c
index a9c30e9df4..bd20e384b7 100644
--- a/coroutine-ucontext.c
+++ b/coroutine-ucontext.c
@@ -46,7 +46,7 @@ static unsigned int pool_size;
typedef struct {
Coroutine base;
void *stack;
- jmp_buf env;
+ sigjmp_buf env;
#ifdef CONFIG_VALGRIND_H
unsigned int valgrind_stack_id;
@@ -130,8 +130,8 @@ static void coroutine_trampoline(int i0, int i1)
co = &self->base;
/* Initialize longjmp environment and switch back the caller */
- if (!setjmp(self->env)) {
- longjmp(*(jmp_buf *)co->entry_arg, 1);
+ if (!sigsetjmp(self->env, 0)) {
+ siglongjmp(*(sigjmp_buf *)co->entry_arg, 1);
}
while (true) {
@@ -145,14 +145,15 @@ static Coroutine *coroutine_new(void)
const size_t stack_size = 1 << 20;
CoroutineUContext *co;
ucontext_t old_uc, uc;
- jmp_buf old_env;
+ sigjmp_buf old_env;
union cc_arg arg = {0};
- /* The ucontext functions preserve signal masks which incurs a system call
- * overhead. setjmp()/longjmp() does not preserve signal masks but only
- * works on the current stack. Since we need a way to create and switch to
- * a new stack, use the ucontext functions for that but setjmp()/longjmp()
- * for everything else.
+ /* The ucontext functions preserve signal masks which incurs a
+ * system call overhead. sigsetjmp(buf, 0)/siglongjmp() does not
+ * preserve signal masks but only works on the current stack.
+ * Since we need a way to create and switch to a new stack, use
+ * the ucontext functions for that but sigsetjmp()/siglongjmp() for
+ * everything else.
*/
if (getcontext(&uc) == -1) {
@@ -178,8 +179,8 @@ static Coroutine *coroutine_new(void)
makecontext(&uc, (void (*)(void))coroutine_trampoline,
2, arg.i[0], arg.i[1]);
- /* swapcontext() in, longjmp() back out */
- if (!setjmp(old_env)) {
+ /* swapcontext() in, siglongjmp() back out */
+ if (!sigsetjmp(old_env, 0)) {
swapcontext(&old_uc, &uc);
}
return &co->base;
@@ -242,9 +243,9 @@ CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
s->current = to_;
- ret = setjmp(from->env);
+ ret = sigsetjmp(from->env, 0);
if (ret == 0) {
- longjmp(to->env, action);
+ siglongjmp(to->env, action);
}
return ret;
}
diff --git a/cpu-exec.c b/cpu-exec.c
index 9fcfe9e0db..afbe4977ab 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -35,7 +35,7 @@ void cpu_loop_exit(CPUArchState *env)
CPUState *cpu = ENV_GET_CPU(env);
cpu->current_tb = NULL;
- longjmp(env->jmp_env, 1);
+ siglongjmp(env->jmp_env, 1);
}
/* exit the current TB from a signal handler. The host registers are
@@ -47,7 +47,7 @@ void cpu_resume_from_signal(CPUArchState *env, void *puc)
/* XXX: restore cpu registers saved in host registers */
env->exception_index = -1;
- longjmp(env->jmp_env, 1);
+ siglongjmp(env->jmp_env, 1);
}
#endif
@@ -234,7 +234,7 @@ int cpu_exec(CPUArchState *env)
/* prepare setjmp context for exception handling */
for(;;) {
- if (setjmp(env->jmp_env) == 0) {
+ if (sigsetjmp(env->jmp_env, 0) == 0) {
/* if an exception is pending, we execute it here */
if (env->exception_index >= 0) {
if (env->exception_index >= EXCP_INTERRUPT) {
diff --git a/default-configs/alpha-softmmu.mak b/default-configs/alpha-softmmu.mak
index 501dd413b7..2dbee94c89 100644
--- a/default-configs/alpha-softmmu.mak
+++ b/default-configs/alpha-softmmu.mak
@@ -1,6 +1,7 @@
# Default configuration for alpha-softmmu
include pci.mak
+include usb.mak
CONFIG_SERIAL=y
CONFIG_I8254=y
CONFIG_PCKBD=y
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 2f1a5c9943..b40f7b08e2 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -1,6 +1,7 @@
# Default configuration for arm-softmmu
include pci.mak
+include usb.mak
CONFIG_GDBSTUB_XML=y
CONFIG_VGA=y
CONFIG_ISA_MMIO=y
diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index 2c78175ae7..1b23025a98 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -1,6 +1,7 @@
# Default configuration for i386-softmmu
include pci.mak
+include usb.mak
CONFIG_VGA=y
CONFIG_VGA_PCI=y
CONFIG_VGA_ISA=y
diff --git a/default-configs/m68k-softmmu.mak b/default-configs/m68k-softmmu.mak
index 3e2ec3716c..778ea82a10 100644
--- a/default-configs/m68k-softmmu.mak
+++ b/default-configs/m68k-softmmu.mak
@@ -1,5 +1,6 @@
# Default configuration for m68k-softmmu
include pci.mak
+include usb.mak
CONFIG_GDBSTUB_XML=y
CONFIG_PTIMER=y
diff --git a/default-configs/mips-softmmu.mak b/default-configs/mips-softmmu.mak
index a271b1c6da..4f04a33732 100644
--- a/default-configs/mips-softmmu.mak
+++ b/default-configs/mips-softmmu.mak
@@ -1,6 +1,7 @@
# Default configuration for mips-softmmu
include pci.mak
+include usb.mak
CONFIG_ISA_MMIO=y
CONFIG_ESP=y
CONFIG_VGA=y
diff --git a/default-configs/mips64-softmmu.mak b/default-configs/mips64-softmmu.mak
index 0510bb6a53..a5b6c3c36a 100644
--- a/default-configs/mips64-softmmu.mak
+++ b/default-configs/mips64-softmmu.mak
@@ -1,6 +1,7 @@
# Default configuration for mips64-softmmu
include pci.mak
+include usb.mak
CONFIG_ISA_MMIO=y
CONFIG_ESP=y
CONFIG_VGA=y
diff --git a/default-configs/mips64el-softmmu.mak b/default-configs/mips64el-softmmu.mak
index ed3bed3b8d..a0e6de8e68 100644
--- a/default-configs/mips64el-softmmu.mak
+++ b/default-configs/mips64el-softmmu.mak
@@ -1,6 +1,7 @@
# Default configuration for mips64el-softmmu
include pci.mak
+include usb.mak
CONFIG_ISA_MMIO=y
CONFIG_ESP=y
CONFIG_VGA=y
diff --git a/default-configs/mipsel-softmmu.mak b/default-configs/mipsel-softmmu.mak
index fa3a2cacdd..753dd76a21 100644
--- a/default-configs/mipsel-softmmu.mak
+++ b/default-configs/mipsel-softmmu.mak
@@ -1,6 +1,7 @@
# Default configuration for mipsel-softmmu
include pci.mak
+include usb.mak
CONFIG_ISA_MMIO=y
CONFIG_ESP=y
CONFIG_VGA=y
diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak
index 1f4a1cff61..f9f8a8159b 100644
--- a/default-configs/ppc-softmmu.mak
+++ b/default-configs/ppc-softmmu.mak
@@ -1,6 +1,7 @@
# Default configuration for ppc-softmmu
include pci.mak
+include usb.mak
CONFIG_GDBSTUB_XML=y
CONFIG_ISA_MMIO=y
CONFIG_ESCC=y
diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak
index 5ff406caa5..dc44294378 100644
--- a/default-configs/ppc64-softmmu.mak
+++ b/default-configs/ppc64-softmmu.mak
@@ -1,6 +1,7 @@
# Default configuration for ppc64-softmmu
include pci.mak
+include usb.mak
CONFIG_GDBSTUB_XML=y
CONFIG_ISA_MMIO=y
CONFIG_ESCC=y
diff --git a/default-configs/ppcemb-softmmu.mak b/default-configs/ppcemb-softmmu.mak
index aaa9cdc1f7..1c6bcf93fc 100644
--- a/default-configs/ppcemb-softmmu.mak
+++ b/default-configs/ppcemb-softmmu.mak
@@ -1,6 +1,7 @@
# Default configuration for ppcemb-softmmu
include pci.mak
+include usb.mak
CONFIG_GDBSTUB_XML=y
CONFIG_ISA_MMIO=y
CONFIG_ESCC=y
diff --git a/default-configs/sh4-softmmu.mak b/default-configs/sh4-softmmu.mak
index 5c69acc5f5..e08b2ee106 100644
--- a/default-configs/sh4-softmmu.mak
+++ b/default-configs/sh4-softmmu.mak
@@ -1,6 +1,7 @@
# Default configuration for sh4-softmmu
include pci.mak
+include usb.mak
CONFIG_SERIAL=y
CONFIG_PTIMER=y
CONFIG_PFLASH_CFI02=y
diff --git a/default-configs/sh4eb-softmmu.mak b/default-configs/sh4eb-softmmu.mak
index 7cdc122201..3a8453552b 100644
--- a/default-configs/sh4eb-softmmu.mak
+++ b/default-configs/sh4eb-softmmu.mak
@@ -1,6 +1,7 @@
# Default configuration for sh4eb-softmmu
include pci.mak
+include usb.mak
CONFIG_SERIAL=y
CONFIG_PTIMER=y
CONFIG_PFLASH_CFI02=y
diff --git a/default-configs/sparc64-softmmu.mak b/default-configs/sparc64-softmmu.mak
index 03e8b42712..2145b6b29f 100644
--- a/default-configs/sparc64-softmmu.mak
+++ b/default-configs/sparc64-softmmu.mak
@@ -1,6 +1,7 @@
# Default configuration for sparc64-softmmu
include pci.mak
+include usb.mak
CONFIG_ISA_MMIO=y
CONFIG_M48T59=y
CONFIG_PTIMER=y
diff --git a/default-configs/usb.mak b/default-configs/usb.mak
new file mode 100644
index 0000000000..1bf9075e85
--- /dev/null
+++ b/default-configs/usb.mak
@@ -0,0 +1,8 @@
+CONFIG_USB_TABLET_WACOM=y
+CONFIG_USB_STORAGE_BOT=y
+CONFIG_USB_STORAGE_UAS=y
+CONFIG_USB_SMARTCARD=y
+CONFIG_USB_AUDIO=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_NETWORK=y
+CONFIG_USB_BLUETOOTH=y
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 233a8564eb..3392f5abd6 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -1,6 +1,7 @@
# Default configuration for x86_64-softmmu
include pci.mak
+include usb.mak
CONFIG_VGA=y
CONFIG_VGA_PCI=y
CONFIG_VGA_ISA=y
diff --git a/disas/i386.c b/disas/i386.c
index 3b006b13e0..73cc06f1c3 100644
--- a/disas/i386.c
+++ b/disas/i386.c
@@ -226,7 +226,7 @@ struct dis_private {
bfd_byte the_buffer[MAX_MNEM_SIZE];
bfd_vma insn_start;
int orig_sizeflag;
- jmp_buf bailout;
+ sigjmp_buf bailout;
};
enum address_mode
@@ -303,7 +303,7 @@ fetch_data2(struct disassemble_info *info, bfd_byte *addr)
STATUS. */
if (priv->max_fetched == priv->the_buffer)
(*info->memory_error_func) (status, start, info);
- longjmp (priv->bailout, 1);
+ siglongjmp(priv->bailout, 1);
}
else
priv->max_fetched = addr;
@@ -3661,7 +3661,7 @@ print_insn (bfd_vma pc, disassemble_info *info)
start_codep = priv.the_buffer;
codep = priv.the_buffer;
- if (setjmp (priv.bailout) != 0)
+ if (sigsetjmp(priv.bailout, 0) != 0)
{
const char *name;
@@ -4720,7 +4720,8 @@ print_operand_value (char *buf, size_t bufsize, int hex, bfd_vma disp)
buf[0] = '0';
buf[1] = 'x';
snprintf_vma (tmp, sizeof(tmp), disp);
- for (i = 0; tmp[i] == '0' && tmp[i + 1]; i++);
+ for (i = 0; tmp[i] == '0' && tmp[i + 1]; i++) {
+ }
pstrcpy (buf + 2, bufsize - 2, tmp + i);
}
else
diff --git a/disas/m68k.c b/disas/m68k.c
index c950241f79..cc0db96cae 100644
--- a/disas/m68k.c
+++ b/disas/m68k.c
@@ -624,7 +624,7 @@ struct private
bfd_byte *max_fetched;
bfd_byte the_buffer[MAXLEN];
bfd_vma insn_start;
- jmp_buf bailout;
+ sigjmp_buf bailout;
};
/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive)
@@ -644,7 +644,7 @@ fetch_data2(struct disassemble_info *info, bfd_byte *addr)
if (status != 0)
{
(*info->memory_error_func) (status, start, info);
- longjmp (priv->bailout, 1);
+ siglongjmp(priv->bailout, 1);
}
else
priv->max_fetched = addr;
@@ -1912,9 +1912,10 @@ print_insn_m68k (bfd_vma memaddr, disassemble_info *info)
priv.max_fetched = priv.the_buffer;
priv.insn_start = memaddr;
- if (setjmp (priv.bailout) != 0)
- /* Error return. */
- return -1;
+ if (sigsetjmp(priv.bailout, 0) != 0) {
+ /* Error return. */
+ return -1;
+ }
switch (info->mach)
{
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 447e32a42e..a1f3a808ac 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -38,8 +38,10 @@ common-obj-$(CONFIG_DMA) += dma.o
common-obj-$(CONFIG_I82374) += i82374.o
common-obj-$(CONFIG_HPET) += hpet.o
common-obj-$(CONFIG_APPLESMC) += applesmc.o
+ifeq ($(CONFIG_USB_SMARTCARD),y)
common-obj-y += ccid-card-passthru.o
common-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
+endif
common-obj-$(CONFIG_I8259) += i8259_common.o i8259.o
common-obj-y += fifo.o
common-obj-y += pam.o
diff --git a/hw/grlib_apbuart.c b/hw/grlib_apbuart.c
index 760bed0b72..ba1685afd1 100644
--- a/hw/grlib_apbuart.c
+++ b/hw/grlib_apbuart.c
@@ -75,7 +75,6 @@ typedef struct UART {
CharDriverState *chr;
/* registers */
- uint32_t receive;
uint32_t status;
uint32_t control;
@@ -136,12 +135,14 @@ static void grlib_apbuart_receive(void *opaque, const uint8_t *buf, int size)
{
UART *uart = opaque;
- uart_add_to_fifo(uart, buf, size);
+ if (uart->control & UART_RECEIVE_ENABLE) {
+ uart_add_to_fifo(uart, buf, size);
- uart->status |= UART_DATA_READY;
+ uart->status |= UART_DATA_READY;
- if (uart->control & UART_RECEIVE_INTERRUPT) {
- qemu_irq_pulse(uart->irq);
+ if (uart->control & UART_RECEIVE_INTERRUPT) {
+ qemu_irq_pulse(uart->irq);
+ }
}
}
@@ -193,8 +194,15 @@ static void grlib_apbuart_write(void *opaque, hwaddr addr,
switch (addr) {
case DATA_OFFSET:
case DATA_OFFSET + 3: /* When only one byte write */
- c = value & 0xFF;
- qemu_chr_fe_write(uart->chr, &c, 1);
+ /* Transmit when character device available and transmitter enabled */
+ if ((uart->chr) && (uart->control & UART_TRANSMIT_ENABLE)) {
+ c = value & 0xFF;
+ qemu_chr_fe_write(uart->chr, &c, 1);
+ /* Generate interrupt */
+ if (uart->control & UART_TRANSMIT_INTERRUPT) {
+ qemu_irq_pulse(uart->irq);
+ }
+ }
return;
case STATUS_OFFSET:
@@ -242,30 +250,44 @@ static int grlib_apbuart_init(SysBusDevice *dev)
return 0;
}
-static Property grlib_gptimer_properties[] = {
+static void grlib_apbuart_reset(DeviceState *d)
+{
+ UART *uart = container_of(d, UART, busdev.qdev);
+
+ /* Transmitter FIFO and shift registers are always empty in QEMU */
+ uart->status = UART_TRANSMIT_FIFO_EMPTY | UART_TRANSMIT_SHIFT_EMPTY;
+ /* Everything is off */
+ uart->control = 0;
+ /* Flush receive FIFO */
+ uart->len = 0;
+ uart->current = 0;
+}
+
+static Property grlib_apbuart_properties[] = {
DEFINE_PROP_CHR("chrdev", UART, chr),
DEFINE_PROP_END_OF_LIST(),
};
-static void grlib_gptimer_class_init(ObjectClass *klass, void *data)
+static void grlib_apbuart_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = grlib_apbuart_init;
- dc->props = grlib_gptimer_properties;
+ dc->reset = grlib_apbuart_reset;
+ dc->props = grlib_apbuart_properties;
}
-static const TypeInfo grlib_gptimer_info = {
+static const TypeInfo grlib_apbuart_info = {
.name = "grlib,apbuart",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(UART),
- .class_init = grlib_gptimer_class_init,
+ .class_init = grlib_apbuart_class_init,
};
-static void grlib_gptimer_register_types(void)
+static void grlib_apbuart_register_types(void)
{
- type_register_static(&grlib_gptimer_info);
+ type_register_static(&grlib_apbuart_info);
}
-type_init(grlib_gptimer_register_types)
+type_init(grlib_apbuart_register_types)
diff --git a/hw/ppc4xx_devs.c b/hw/ppc4xx_devs.c
index 5e491bc0b4..b6bb0e166a 100644
--- a/hw/ppc4xx_devs.c
+++ b/hw/ppc4xx_devs.c
@@ -700,7 +700,7 @@ ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks,
vmstate_register_ram_global(&ram_memories[i]);
ram_bases[i] = base;
ram_sizes[i] = bank_size;
- base += ram_size;
+ base += bank_size;
size_left -= bank_size;
break;
}
diff --git a/hw/qxl-render.c b/hw/qxl-render.c
index 88e63f8085..455fb91269 100644
--- a/hw/qxl-render.c
+++ b/hw/qxl-render.c
@@ -118,7 +118,8 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
qxl->guest_primary.surface.height,
qxl->guest_primary.bits_pp,
qxl->guest_primary.abs_stride,
- qxl->guest_primary.data);
+ qxl->guest_primary.data,
+ false);
} else {
qemu_resize_displaysurface(vga->ds,
qxl->guest_primary.surface.width,
diff --git a/hw/usb.h b/hw/usb.h
index bc42639b16..382496cf82 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -361,6 +361,7 @@ struct USBPacket {
int pid;
uint64_t id;
USBEndpoint *ep;
+ unsigned int stream;
QEMUIOVector iov;
uint64_t parameter; /* control transfers */
bool short_not_ok;
@@ -383,13 +384,15 @@ struct USBCombinedPacket {
void usb_packet_init(USBPacket *p);
void usb_packet_set_state(USBPacket *p, USBPacketState state);
void usb_packet_check_state(USBPacket *p, USBPacketState expected);
-void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id,
- bool short_not_ok, bool int_req);
+void usb_packet_setup(USBPacket *p, int pid,
+ USBEndpoint *ep, unsigned int stream,
+ uint64_t id, bool short_not_ok, bool int_req);
void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len);
int usb_packet_map(USBPacket *p, QEMUSGList *sgl);
void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl);
void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes);
void usb_packet_skip(USBPacket *p, size_t bytes);
+size_t usb_packet_size(USBPacket *p);
void usb_packet_cleanup(USBPacket *p);
static inline bool usb_packet_is_inflight(USBPacket *p)
@@ -417,6 +420,7 @@ void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep,
uint16_t raw);
int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep);
void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled);
+void usb_ep_set_halted(USBDevice *dev, int pid, int ep, bool halted);
USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep,
uint64_t id);
@@ -428,13 +432,12 @@ void usb_attach(USBPort *port);
void usb_detach(USBPort *port);
void usb_port_reset(USBPort *port);
void usb_device_reset(USBDevice *dev);
-void usb_wakeup(USBEndpoint *ep);
+void usb_wakeup(USBEndpoint *ep, unsigned int stream);
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(USBBus *bus, const char *devname);
-int usb_host_device_close(const char *devname);
void usb_host_info(Monitor *mon, const QDict *qdict);
/* usb-bt.c */
@@ -488,7 +491,7 @@ struct USBBus {
struct USBBusOps {
int (*register_companion)(USBBus *bus, USBPort *ports[],
uint32_t portcount, uint32_t firstport);
- void (*wakeup_endpoint)(USBBus *bus, USBEndpoint *ep);
+ void (*wakeup_endpoint)(USBBus *bus, USBEndpoint *ep, unsigned int stream);
};
void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host);
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index d1bbbc06e7..e63e287ce0 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -1,14 +1,30 @@
+# usb subsystem core
+common-obj-y += core.o combined-packet.o bus.o desc.o
+common-obj-y += libhw.o
+
+# usb host adapters
common-obj-$(CONFIG_USB_UHCI) += hcd-uhci.o
common-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o
common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o hcd-ehci-pci.o hcd-ehci-sysbus.o
common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o
-common-obj-y += libhw.o
+# emulated usb devices
+common-obj-y += dev-hub.o
+common-obj-y += dev-hid.o
+common-obj-$(CONFIG_USB_TABLET_WACOM) += dev-wacom.o
+common-obj-$(CONFIG_USB_STORAGE_BOT) += dev-storage.o
+common-obj-$(CONFIG_USB_STORAGE_UAS) += dev-uas.o
+common-obj-$(CONFIG_USB_AUDIO) += dev-audio.o
+common-obj-$(CONFIG_USB_SERIAL) += dev-serial.o
+common-obj-$(CONFIG_USB_NETWORK) += dev-network.o
+
+# FIXME: make configurable too
+CONFIG_USB_BLUETOOTH := y
+common-obj-$(CONFIG_USB_BLUETOOTH) += dev-bluetooth.o
+common-obj-$(CONFIG_USB_SMARTCARD) += dev-smartcard-reader.o
+
+# usb redirection
common-obj-$(CONFIG_USB_REDIR) += redirect.o quirks.o
-common-obj-y += core.o combined-packet.o bus.o desc.o dev-hub.o
-common-obj-y += host-$(HOST_USB).o dev-bluetooth.o
-common-obj-y += dev-hid.o dev-storage.o dev-wacom.o
-common-obj-y += dev-serial.o dev-network.o dev-audio.o
-common-obj-y += dev-smartcard-reader.o
-common-obj-y += dev-uas.o
+# usb pass-through
+common-obj-y += $(patsubst %,host-%.o,$(HOST_USB))
diff --git a/hw/usb/core.c b/hw/usb/core.c
index d057aab900..15a150aea0 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -71,7 +71,7 @@ void usb_device_reset(USBDevice *dev)
usb_device_handle_reset(dev);
}
-void usb_wakeup(USBEndpoint *ep)
+void usb_wakeup(USBEndpoint *ep, unsigned int stream)
{
USBDevice *dev = ep->dev;
USBBus *bus = usb_bus_from_device(dev);
@@ -80,7 +80,7 @@ void usb_wakeup(USBEndpoint *ep)
dev->port->ops->wakeup(dev->port);
}
if (bus->ops->wakeup_endpoint) {
- bus->ops->wakeup_endpoint(bus, ep);
+ bus->ops->wakeup_endpoint(bus, ep, stream);
}
}
@@ -545,14 +545,16 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state)
p->state = state;
}
-void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id,
- bool short_not_ok, bool int_req)
+void usb_packet_setup(USBPacket *p, int pid,
+ USBEndpoint *ep, unsigned int stream,
+ uint64_t id, bool short_not_ok, bool int_req)
{
assert(!usb_packet_is_inflight(p));
assert(p->iov.iov != NULL);
p->id = id;
p->pid = pid;
p->ep = ep;
+ p->stream = stream;
p->status = USB_RET_SUCCESS;
p->actual_length = 0;
p->parameter = 0;
@@ -570,15 +572,17 @@ void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len)
void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes)
{
+ QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov;
+
assert(p->actual_length >= 0);
- assert(p->actual_length + bytes <= p->iov.size);
+ assert(p->actual_length + bytes <= iov->size);
switch (p->pid) {
case USB_TOKEN_SETUP:
case USB_TOKEN_OUT:
- iov_to_buf(p->iov.iov, p->iov.niov, p->actual_length, ptr, bytes);
+ iov_to_buf(iov->iov, iov->niov, p->actual_length, ptr, bytes);
break;
case USB_TOKEN_IN:
- iov_from_buf(p->iov.iov, p->iov.niov, p->actual_length, ptr, bytes);
+ iov_from_buf(iov->iov, iov->niov, p->actual_length, ptr, bytes);
break;
default:
fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid);
@@ -589,14 +593,21 @@ void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes)
void usb_packet_skip(USBPacket *p, size_t bytes)
{
+ QEMUIOVector *iov = p->combined ? &p->combined->iov : &p->iov;
+
assert(p->actual_length >= 0);
- assert(p->actual_length + bytes <= p->iov.size);
+ assert(p->actual_length + bytes <= iov->size);
if (p->pid == USB_TOKEN_IN) {
- iov_memset(p->iov.iov, p->iov.niov, p->actual_length, 0, bytes);
+ iov_memset(iov->iov, iov->niov, p->actual_length, 0, bytes);
}
p->actual_length += bytes;
}
+size_t usb_packet_size(USBPacket *p)
+{
+ return p->combined ? p->combined->iov.size : p->iov.size;
+}
+
void usb_packet_cleanup(USBPacket *p)
{
assert(!usb_packet_is_inflight(p));
@@ -755,6 +766,12 @@ void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled)
uep->pipeline = enabled;
}
+void usb_ep_set_halted(USBDevice *dev, int pid, int ep, bool halted)
+{
+ struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
+ uep->halted = halted;
+}
+
USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep,
uint64_t id)
{
diff --git a/hw/usb/desc.c b/hw/usb/desc.c
index b7c32333d7..b389381326 100644
--- a/hw/usb/desc.c
+++ b/hw/usb/desc.c
@@ -225,12 +225,9 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, int flags,
d->u.endpoint.bRefresh = ep->bRefresh;
d->u.endpoint.bSynchAddress = ep->bSynchAddress;
}
- if (ep->extra) {
- memcpy(dest + bLength, ep->extra, extralen);
- }
if (superlen) {
- USBDescriptor *d = (void *)(dest + bLength + extralen);
+ USBDescriptor *d = (void *)(dest + bLength);
d->bLength = 0x06;
d->bDescriptorType = USB_DT_ENDPOINT_COMPANION;
@@ -243,6 +240,10 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, int flags,
usb_hi(ep->wBytesPerInterval);
}
+ if (ep->extra) {
+ memcpy(dest + bLength + superlen, ep->extra, extralen);
+ }
+
return bLength + extralen + superlen;
}
diff --git a/hw/usb/dev-bluetooth.c b/hw/usb/dev-bluetooth.c
index adbf9d4697..0f8aa48c85 100644
--- a/hw/usb/dev-bluetooth.c
+++ b/hw/usb/dev-bluetooth.c
@@ -478,7 +478,7 @@ static void usb_bt_out_hci_packet_event(void *opaque,
struct USBBtState *s = (struct USBBtState *) opaque;
if (s->evt.len == 0) {
- usb_wakeup(s->intr);
+ usb_wakeup(s->intr, 0);
}
usb_bt_fifo_enqueue(&s->evt, data, len);
}
diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c
index 29b64819ab..9701048887 100644
--- a/hw/usb/dev-hid.c
+++ b/hw/usb/dev-hid.c
@@ -423,7 +423,7 @@ static void usb_hid_changed(HIDState *hs)
{
USBHIDState *us = container_of(hs, USBHIDState, hid);
- usb_wakeup(us->intr);
+ usb_wakeup(us->intr, 0);
}
static void usb_hid_handle_reset(USBDevice *dev)
diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c
index 79f2f46d55..504c98c350 100644
--- a/hw/usb/dev-hub.c
+++ b/hw/usb/dev-hub.c
@@ -164,7 +164,7 @@ static void usb_hub_attach(USBPort *port1)
} else {
port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
}
- usb_wakeup(s->intr);
+ usb_wakeup(s->intr, 0);
}
static void usb_hub_detach(USBPort *port1)
@@ -173,7 +173,7 @@ static void usb_hub_detach(USBPort *port1)
USBHubPort *port = &s->ports[port1->index];
trace_usb_hub_detach(s->dev.addr, port1->index + 1);
- usb_wakeup(s->intr);
+ usb_wakeup(s->intr, 0);
/* Let upstream know the device on this port is gone */
s->dev.port->ops->child_detach(s->dev.port, port1->dev);
@@ -184,7 +184,7 @@ static void usb_hub_detach(USBPort *port1)
port->wPortStatus &= ~PORT_STAT_ENABLE;
port->wPortChange |= PORT_STAT_C_ENABLE;
}
- usb_wakeup(s->intr);
+ usb_wakeup(s->intr, 0);
}
static void usb_hub_child_detach(USBPort *port1, USBDevice *child)
@@ -202,7 +202,7 @@ static void usb_hub_wakeup(USBPort *port1)
if (port->wPortStatus & PORT_STAT_SUSPEND) {
port->wPortChange |= PORT_STAT_C_SUSPEND;
- usb_wakeup(s->intr);
+ usb_wakeup(s->intr, 0);
}
}
@@ -364,7 +364,7 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p,
port->wPortChange |= PORT_STAT_C_RESET;
/* set enable bit */
port->wPortStatus |= PORT_STAT_ENABLE;
- usb_wakeup(s->intr);
+ usb_wakeup(s->intr, 0);
}
break;
case PORT_POWER:
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index a01a5e793a..c08718b679 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -855,7 +855,7 @@ static void *rndis_queue_response(USBNetState *s, unsigned int length)
g_malloc0(sizeof(struct rndis_response) + length);
if (QTAILQ_EMPTY(&s->rndis_resp)) {
- usb_wakeup(s->intr);
+ usb_wakeup(s->intr, 0);
}
QTAILQ_INSERT_TAIL(&s->rndis_resp, r, entries);
diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c
index 979a473b37..caebc1c3ff 100644
--- a/hw/usb/dev-smartcard-reader.c
+++ b/hw/usb/dev-smartcard-reader.c
@@ -839,7 +839,7 @@ static void ccid_on_slot_change(USBCCIDState *s, bool full)
s->bmSlotICCState |= SLOT_0_CHANGED_MASK;
}
s->notify_slot_change = true;
- usb_wakeup(s->intr);
+ usb_wakeup(s->intr, 0);
}
static void ccid_write_data_block_error(
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index b89d00f7cf..d3f01aa2a7 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -400,6 +400,7 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p)
struct usb_msd_cbw cbw;
uint8_t devep = p->ep->nr;
SCSIDevice *scsi_dev;
+ uint32_t len;
switch (p->pid) {
case USB_TOKEN_OUT:
@@ -441,8 +442,8 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p)
#ifdef DEBUG_MSD
scsi_req_print(s->req);
#endif
- scsi_req_enqueue(s->req);
- if (s->req && s->req->cmd.xfer != SCSI_XFER_NONE) {
+ len = scsi_req_enqueue(s->req);
+ if (len) {
scsi_req_continue(s->req);
}
break;
diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c
index d904d1a40b..1ac5117ba7 100644
--- a/hw/usb/dev-uas.c
+++ b/hw/usb/dev-uas.c
@@ -99,6 +99,9 @@ typedef struct {
/* --------------------------------------------------------------------- */
+#define UAS_STREAM_BM_ATTR 4
+#define UAS_MAX_STREAMS (1 << UAS_STREAM_BM_ATTR)
+
typedef struct UASDevice UASDevice;
typedef struct UASRequest UASRequest;
typedef struct UASStatus UASStatus;
@@ -106,12 +109,18 @@ typedef struct UASStatus UASStatus;
struct UASDevice {
USBDevice dev;
SCSIBus bus;
- UASRequest *datain;
- UASRequest *dataout;
- USBPacket *status;
QEMUBH *status_bh;
QTAILQ_HEAD(, UASStatus) results;
QTAILQ_HEAD(, UASRequest) requests;
+
+ /* usb 2.0 only */
+ USBPacket *status2;
+ UASRequest *datain2;
+ UASRequest *dataout2;
+
+ /* usb 3.0 only */
+ USBPacket *data3[UAS_MAX_STREAMS];
+ USBPacket *status3[UAS_MAX_STREAMS];
};
struct UASRequest {
@@ -132,6 +141,7 @@ struct UASRequest {
};
struct UASStatus {
+ uint32_t stream;
uas_ui status;
uint32_t length;
QTAILQ_ENTRY(UASStatus) next;
@@ -144,6 +154,7 @@ enum {
STR_PRODUCT,
STR_SERIALNUMBER,
STR_CONFIG_HIGH,
+ STR_CONFIG_SUPER,
};
static const USBDescStrings desc_strings = {
@@ -151,6 +162,7 @@ static const USBDescStrings desc_strings = {
[STR_PRODUCT] = "USB Attached SCSI HBA",
[STR_SERIALNUMBER] = "27842",
[STR_CONFIG_HIGH] = "High speed config (usb 2.0)",
+ [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)",
};
static const USBDescIface desc_iface_high = {
@@ -204,6 +216,64 @@ static const USBDescIface desc_iface_high = {
}
};
+static const USBDescIface desc_iface_super = {
+ .bInterfaceNumber = 0,
+ .bNumEndpoints = 4,
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = 0x06, /* SCSI */
+ .bInterfaceProtocol = 0x62, /* UAS */
+ .eps = (USBDescEndpoint[]) {
+ {
+ .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_COMMAND,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 1024,
+ .bMaxBurst = 15,
+ .extra = (uint8_t[]) {
+ 0x04, /* u8 bLength */
+ 0x24, /* u8 bDescriptorType */
+ UAS_PIPE_ID_COMMAND,
+ 0x00, /* u8 bReserved */
+ },
+ },{
+ .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_STATUS,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 1024,
+ .bMaxBurst = 15,
+ .bmAttributes_super = UAS_STREAM_BM_ATTR,
+ .extra = (uint8_t[]) {
+ 0x04, /* u8 bLength */
+ 0x24, /* u8 bDescriptorType */
+ UAS_PIPE_ID_STATUS,
+ 0x00, /* u8 bReserved */
+ },
+ },{
+ .bEndpointAddress = USB_DIR_IN | UAS_PIPE_ID_DATA_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 1024,
+ .bMaxBurst = 15,
+ .bmAttributes_super = UAS_STREAM_BM_ATTR,
+ .extra = (uint8_t[]) {
+ 0x04, /* u8 bLength */
+ 0x24, /* u8 bDescriptorType */
+ UAS_PIPE_ID_DATA_IN,
+ 0x00, /* u8 bReserved */
+ },
+ },{
+ .bEndpointAddress = USB_DIR_OUT | UAS_PIPE_ID_DATA_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = 1024,
+ .bMaxBurst = 15,
+ .bmAttributes_super = UAS_STREAM_BM_ATTR,
+ .extra = (uint8_t[]) {
+ 0x04, /* u8 bLength */
+ 0x24, /* u8 bDescriptorType */
+ UAS_PIPE_ID_DATA_OUT,
+ 0x00, /* u8 bReserved */
+ },
+ },
+ }
+};
+
static const USBDescDevice desc_device_high = {
.bcdUSB = 0x0200,
.bMaxPacketSize0 = 64,
@@ -220,6 +290,22 @@ static const USBDescDevice desc_device_high = {
},
};
+static const USBDescDevice desc_device_super = {
+ .bcdUSB = 0x0300,
+ .bMaxPacketSize0 = 64,
+ .bNumConfigurations = 1,
+ .confs = (USBDescConfig[]) {
+ {
+ .bNumInterfaces = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = STR_CONFIG_SUPER,
+ .bmAttributes = 0xc0,
+ .nif = 1,
+ .ifs = &desc_iface_super,
+ },
+ },
+};
+
static const USBDesc desc = {
.id = {
.idVendor = 0x46f4, /* CRC16() of "QEMU" */
@@ -229,45 +315,68 @@ static const USBDesc desc = {
.iProduct = STR_PRODUCT,
.iSerialNumber = STR_SERIALNUMBER,
},
- .high = &desc_device_high,
- .str = desc_strings,
+ .high = &desc_device_high,
+ .super = &desc_device_super,
+ .str = desc_strings,
};
/* --------------------------------------------------------------------- */
-static UASStatus *usb_uas_alloc_status(uint8_t id, uint16_t tag)
+static bool uas_using_streams(UASDevice *uas)
+{
+ return uas->dev.speed == USB_SPEED_SUPER;
+}
+
+/* --------------------------------------------------------------------- */
+
+static UASStatus *usb_uas_alloc_status(UASDevice *uas, uint8_t id, uint16_t tag)
{
UASStatus *st = g_new0(UASStatus, 1);
st->status.hdr.id = id;
st->status.hdr.tag = cpu_to_be16(tag);
st->length = sizeof(uas_ui_header);
+ if (uas_using_streams(uas)) {
+ st->stream = tag;
+ }
return st;
}
static void usb_uas_send_status_bh(void *opaque)
{
UASDevice *uas = opaque;
- UASStatus *st = QTAILQ_FIRST(&uas->results);
- USBPacket *p = uas->status;
+ UASStatus *st;
+ USBPacket *p;
- assert(p != NULL);
- assert(st != NULL);
+ while ((st = QTAILQ_FIRST(&uas->results)) != NULL) {
+ if (uas_using_streams(uas)) {
+ p = uas->status3[st->stream];
+ uas->status3[st->stream] = NULL;
+ } else {
+ p = uas->status2;
+ uas->status2 = NULL;
+ }
+ if (p == NULL) {
+ break;
+ }
- uas->status = NULL;
- usb_packet_copy(p, &st->status, st->length);
- QTAILQ_REMOVE(&uas->results, st, next);
- g_free(st);
+ usb_packet_copy(p, &st->status, st->length);
+ QTAILQ_REMOVE(&uas->results, st, next);
+ g_free(st);
- p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
- usb_packet_complete(&uas->dev, p);
+ p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
+ usb_packet_complete(&uas->dev, p);
+ }
}
static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length)
{
+ USBPacket *p = uas_using_streams(uas) ?
+ uas->status3[st->stream] : uas->status2;
+
st->length += length;
QTAILQ_INSERT_TAIL(&uas->results, st, next);
- if (uas->status) {
+ if (p) {
/*
* Just schedule bh make sure any in-flight data transaction
* is finished before completing (sending) the status packet.
@@ -276,14 +385,14 @@ static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length)
} else {
USBEndpoint *ep = usb_ep_get(&uas->dev, USB_TOKEN_IN,
UAS_PIPE_ID_STATUS);
- usb_wakeup(ep);
+ usb_wakeup(ep, st->stream);
}
}
static void usb_uas_queue_response(UASDevice *uas, uint16_t tag,
uint8_t code, uint16_t add_info)
{
- UASStatus *st = usb_uas_alloc_status(UAS_UI_RESPONSE, tag);
+ UASStatus *st = usb_uas_alloc_status(uas, UAS_UI_RESPONSE, tag);
trace_usb_uas_response(uas->dev.addr, tag, code);
st->status.response.response_code = code;
@@ -293,7 +402,7 @@ static void usb_uas_queue_response(UASDevice *uas, uint16_t tag,
static void usb_uas_queue_sense(UASRequest *req, uint8_t status)
{
- UASStatus *st = usb_uas_alloc_status(UAS_UI_SENSE, req->tag);
+ UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_SENSE, req->tag);
int len, slen = 0;
trace_usb_uas_sense(req->uas->dev.addr, req->tag, status);
@@ -310,7 +419,8 @@ static void usb_uas_queue_sense(UASRequest *req, uint8_t status)
static void usb_uas_queue_read_ready(UASRequest *req)
{
- UASStatus *st = usb_uas_alloc_status(UAS_UI_READ_READY, req->tag);
+ UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_READ_READY,
+ req->tag);
trace_usb_uas_read_ready(req->uas->dev.addr, req->tag);
usb_uas_queue_status(req->uas, st, 0);
@@ -318,7 +428,8 @@ static void usb_uas_queue_read_ready(UASRequest *req)
static void usb_uas_queue_write_ready(UASRequest *req)
{
- UASStatus *st = usb_uas_alloc_status(UAS_UI_WRITE_READY, req->tag);
+ UASStatus *st = usb_uas_alloc_status(req->uas, UAS_UI_WRITE_READY,
+ req->tag);
trace_usb_uas_write_ready(req->uas->dev.addr, req->tag);
usb_uas_queue_status(req->uas, st, 0);
@@ -381,18 +492,22 @@ static void usb_uas_start_next_transfer(UASDevice *uas)
{
UASRequest *req;
+ if (uas_using_streams(uas)) {
+ return;
+ }
+
QTAILQ_FOREACH(req, &uas->requests, next) {
if (req->active || req->complete) {
continue;
}
- if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain == NULL) {
- uas->datain = req;
+ if (req->req->cmd.mode == SCSI_XFER_FROM_DEV && uas->datain2 == NULL) {
+ uas->datain2 = req;
usb_uas_queue_read_ready(req);
req->active = true;
return;
}
- if (req->req->cmd.mode == SCSI_XFER_TO_DEV && uas->dataout == NULL) {
- uas->dataout = req;
+ if (req->req->cmd.mode == SCSI_XFER_TO_DEV && uas->dataout2 == NULL) {
+ uas->dataout2 = req;
usb_uas_queue_write_ready(req);
req->active = true;
return;
@@ -417,11 +532,11 @@ static void usb_uas_scsi_free_request(SCSIBus *bus, void *priv)
UASRequest *req = priv;
UASDevice *uas = req->uas;
- if (req == uas->datain) {
- uas->datain = NULL;
+ if (req == uas->datain2) {
+ uas->datain2 = NULL;
}
- if (req == uas->dataout) {
- uas->dataout = NULL;
+ if (req == uas->dataout2) {
+ uas->dataout2 = NULL;
}
QTAILQ_REMOVE(&uas->requests, req, next);
g_free(req);
@@ -522,12 +637,25 @@ static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p)
{
UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
UASRequest *req, *nreq;
+ int i;
- if (uas->status == p) {
- uas->status = NULL;
+ if (uas->status2 == p) {
+ uas->status2 = NULL;
qemu_bh_cancel(uas->status_bh);
return;
}
+ if (uas_using_streams(uas)) {
+ for (i = 0; i < UAS_MAX_STREAMS; i++) {
+ if (uas->status3[i] == p) {
+ uas->status3[i] = NULL;
+ return;
+ }
+ if (uas->data3[i] == p) {
+ uas->data3[i] = NULL;
+ return;
+ }
+ }
+ }
QTAILQ_FOREACH_SAFE(req, &uas->requests, next, nreq) {
if (req->data == p) {
req->data = NULL;
@@ -555,9 +683,18 @@ static void usb_uas_command(UASDevice *uas, uas_ui *ui)
usb_uas_get_lun(req->lun),
req->lun >> 32, req->lun & 0xffffffff);
QTAILQ_INSERT_TAIL(&uas->requests, req, next);
+ if (uas_using_streams(uas) && uas->data3[req->tag] != NULL) {
+ req->data = uas->data3[req->tag];
+ req->data_async = true;
+ uas->data3[req->tag] = NULL;
+ }
+
req->req = scsi_req_new(req->dev, req->tag,
usb_uas_get_lun(req->lun),
ui->command.cdb, req);
+#if 1
+ scsi_req_print(req->req);
+#endif
len = scsi_req_enqueue(req->req);
if (len) {
req->data_size = len;
@@ -669,12 +806,26 @@ static void usb_uas_handle_data(USBDevice *dev, USBPacket *p)
}
break;
case UAS_PIPE_ID_STATUS:
- st = QTAILQ_FIRST(&uas->results);
- if (st == NULL) {
- assert(uas->status == NULL);
- uas->status = p;
- p->status = USB_RET_ASYNC;
- break;
+ if (p->stream) {
+ QTAILQ_FOREACH(st, &uas->results, next) {
+ if (st->stream == p->stream) {
+ break;
+ }
+ }
+ if (st == NULL) {
+ assert(uas->status3[p->stream] == NULL);
+ uas->status3[p->stream] = p;
+ p->status = USB_RET_ASYNC;
+ break;
+ }
+ } else {
+ st = QTAILQ_FIRST(&uas->results);
+ if (st == NULL) {
+ assert(uas->status2 == NULL);
+ uas->status2 = p;
+ p->status = USB_RET_ASYNC;
+ break;
+ }
}
usb_packet_copy(p, &st->status, st->length);
QTAILQ_REMOVE(&uas->results, st, next);
@@ -682,11 +833,23 @@ static void usb_uas_handle_data(USBDevice *dev, USBPacket *p)
break;
case UAS_PIPE_ID_DATA_IN:
case UAS_PIPE_ID_DATA_OUT:
- req = (p->ep->nr == UAS_PIPE_ID_DATA_IN) ? uas->datain : uas->dataout;
+ if (p->stream) {
+ req = usb_uas_find_request(uas, p->stream);
+ } else {
+ req = (p->ep->nr == UAS_PIPE_ID_DATA_IN)
+ ? uas->datain2 : uas->dataout2;
+ }
if (req == NULL) {
- fprintf(stderr, "%s: no inflight request\n", __func__);
- p->status = USB_RET_STALL;
- break;
+ if (p->stream) {
+ assert(uas->data3[p->stream] == NULL);
+ uas->data3[p->stream] = p;
+ p->status = USB_RET_ASYNC;
+ break;
+ } else {
+ fprintf(stderr, "%s: no inflight request\n", __func__);
+ p->status = USB_RET_STALL;
+ break;
+ }
}
scsi_req_ref(req->req);
req->data = p;
diff --git a/hw/usb/dev-wacom.c b/hw/usb/dev-wacom.c
index ab9fa2ef48..3be5cdefda 100644
--- a/hw/usb/dev-wacom.c
+++ b/hw/usb/dev-wacom.c
@@ -138,7 +138,7 @@ static void usb_mouse_event(void *opaque,
s->dz += dz1;
s->buttons_state = buttons_state;
s->changed = 1;
- usb_wakeup(s->intr);
+ usb_wakeup(s->intr, 0);
}
static void usb_wacom_event(void *opaque,
@@ -152,7 +152,7 @@ static void usb_wacom_event(void *opaque,
s->dz += dz;
s->buttons_state = buttons_state;
s->changed = 1;
- usb_wakeup(s->intr);
+ usb_wakeup(s->intr, 0);
}
static inline int int_clamp(int val, int vmin, int vmax)
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 70406592ef..5176251cb4 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -874,7 +874,8 @@ static int ehci_register_companion(USBBus *bus, USBPort *ports[],
return 0;
}
-static void ehci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep)
+static void ehci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep,
+ unsigned int stream)
{
EHCIState *s = container_of(bus, EHCIState, bus);
uint32_t portsc = s->portsc[ep->dev->port->index];
@@ -1420,7 +1421,7 @@ static int ehci_execute(EHCIPacket *p, const char *action)
}
spd = (p->pid == USB_TOKEN_IN && NLPTR_TBIT(p->qtd.altnext) == 0);
- usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr, spd,
+ usb_packet_setup(&p->packet, p->pid, ep, 0, p->qtdaddr, spd,
(p->qtd.token & QTD_TOKEN_IOC) != 0);
usb_packet_map(&p->packet, &p->sgl);
p->async = EHCI_ASYNC_INITIALIZED;
@@ -1493,7 +1494,7 @@ static int ehci_process_itd(EHCIState *ehci,
dev = ehci_find_device(ehci, devaddr);
ep = usb_ep_get(dev, pid, endp);
if (ep && ep->type == USB_ENDPOINT_XFER_ISOC) {
- usb_packet_setup(&ehci->ipacket, pid, ep, addr, false,
+ usb_packet_setup(&ehci->ipacket, pid, ep, 0, addr, false,
(itd->transact[i] & ITD_XACT_IOC) != 0);
usb_packet_map(&ehci->ipacket, &ehci->isgl);
usb_handle_packet(dev, &ehci->ipacket);
diff --git a/hw/usb/hcd-musb.c b/hw/usb/hcd-musb.c
index 64e9e834bf..7968e17c34 100644
--- a/hw/usb/hcd-musb.c
+++ b/hw/usb/hcd-musb.c
@@ -625,7 +625,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
/* A wild guess on the FADDR semantics... */
dev = usb_find_device(&s->port, ep->faddr[idx]);
uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf);
- usb_packet_setup(&ep->packey[dir].p, pid, uep,
+ usb_packet_setup(&ep->packey[dir].p, pid, uep, 0,
(dev->addr << 16) | (uep->nr << 8) | pid, false, true);
usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len);
ep->packey[dir].ep = ep;
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index dd9967b13d..51241cda78 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -830,7 +830,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
OHCI_BM(iso_td.flags, TD_DI) == 0;
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
- usb_packet_setup(&ohci->usb_packet, pid, ep, addr, false, int_req);
+ usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, false, int_req);
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
usb_handle_packet(dev, &ohci->usb_packet);
if (ohci->usb_packet.status == USB_RET_ASYNC) {
@@ -1034,7 +1034,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
}
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
- usb_packet_setup(&ohci->usb_packet, pid, ep, addr, !flag_r,
+ usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, !flag_r,
OHCI_BM(td.flags, TD_DI) == 0);
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
usb_handle_packet(dev, &ohci->usb_packet);
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index 60645aa21f..f8c42864d1 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -879,7 +879,7 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
max_len = ((td->token >> 21) + 1) & 0x7ff;
spd = (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) != 0);
- usb_packet_setup(&async->packet, pid, q->ep, td_addr, spd,
+ usb_packet_setup(&async->packet, pid, q->ep, 0, td_addr, spd,
(td->ctrl & TD_CTRL_IOC) != 0);
qemu_sglist_add(&async->sgl, td->buffer, max_len);
usb_packet_map(&async->packet, &async->sgl);
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 5fb0c488e8..07afdeef5b 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -34,8 +34,8 @@
#else
#define DPRINTF(...) do {} while (0)
#endif
-#define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \
- __func__, __LINE__); abort(); } while (0)
+#define FIXME(_msg) do { fprintf(stderr, "FIXME %s:%d %s\n", \
+ __func__, __LINE__, _msg); abort(); } while (0)
#define MAXPORTS_2 15
#define MAXPORTS_3 15
@@ -301,6 +301,8 @@ typedef enum TRBCCode {
#define SLOT_CONTEXT_ENTRIES_SHIFT 27
typedef struct XHCIState XHCIState;
+typedef struct XHCIStreamContext XHCIStreamContext;
+typedef struct XHCIEPContext XHCIEPContext;
#define get_field(data, field) \
(((data) >> field##_SHIFT) & field##_MASK)
@@ -351,6 +353,7 @@ typedef struct XHCITransfer {
unsigned int iso_pkts;
unsigned int slotid;
unsigned int epid;
+ unsigned int streamid;
bool in_xfer;
bool iso_xfer;
@@ -367,7 +370,14 @@ typedef struct XHCITransfer {
uint64_t mfindex_kick;
} XHCITransfer;
-typedef struct XHCIEPContext {
+struct XHCIStreamContext {
+ dma_addr_t pctx;
+ unsigned int sct;
+ XHCIRing ring;
+ XHCIStreamContext *sstreams;
+};
+
+struct XHCIEPContext {
XHCIState *xhci;
unsigned int slotid;
unsigned int epid;
@@ -382,11 +392,17 @@ typedef struct XHCIEPContext {
unsigned int max_psize;
uint32_t state;
+ /* streams */
+ unsigned int max_pstreams;
+ bool lsa;
+ unsigned int nr_pstreams;
+ XHCIStreamContext *pstreams;
+
/* iso xfer scheduling */
unsigned int interval;
int64_t mfindex_last;
QEMUTimer *kick_timer;
-} XHCIEPContext;
+};
typedef struct XHCISlot {
bool enabled;
@@ -482,7 +498,7 @@ enum xhci_flags {
};
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
- unsigned int epid);
+ unsigned int epid, unsigned int streamid);
static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
unsigned int epid);
static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v);
@@ -1068,18 +1084,116 @@ static void xhci_stop(XHCIState *xhci)
xhci->crcr_low &= ~CRCR_CRR;
}
+static XHCIStreamContext *xhci_alloc_stream_contexts(unsigned count,
+ dma_addr_t base)
+{
+ XHCIStreamContext *stctx;
+ unsigned int i;
+
+ stctx = g_new0(XHCIStreamContext, count);
+ for (i = 0; i < count; i++) {
+ stctx[i].pctx = base + i * 16;
+ stctx[i].sct = -1;
+ }
+ return stctx;
+}
+
+static void xhci_reset_streams(XHCIEPContext *epctx)
+{
+ unsigned int i;
+
+ for (i = 0; i < epctx->nr_pstreams; i++) {
+ epctx->pstreams[i].sct = -1;
+ g_free(epctx->pstreams[i].sstreams);
+ }
+}
+
+static void xhci_alloc_streams(XHCIEPContext *epctx, dma_addr_t base)
+{
+ assert(epctx->pstreams == NULL);
+ epctx->nr_pstreams = 2 << epctx->max_pstreams;
+ epctx->pstreams = xhci_alloc_stream_contexts(epctx->nr_pstreams, base);
+}
+
+static void xhci_free_streams(XHCIEPContext *epctx)
+{
+ int i;
+
+ assert(epctx->pstreams != NULL);
+
+ if (!epctx->lsa) {
+ for (i = 0; i < epctx->nr_pstreams; i++) {
+ g_free(epctx->pstreams[i].sstreams);
+ }
+ }
+ g_free(epctx->pstreams);
+ epctx->pstreams = NULL;
+ epctx->nr_pstreams = 0;
+}
+
+static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx,
+ unsigned int streamid,
+ uint32_t *cc_error)
+{
+ XHCIStreamContext *sctx;
+ dma_addr_t base;
+ uint32_t ctx[2], sct;
+
+ assert(streamid != 0);
+ if (epctx->lsa) {
+ if (streamid >= epctx->nr_pstreams) {
+ *cc_error = CC_INVALID_STREAM_ID_ERROR;
+ return NULL;
+ }
+ sctx = epctx->pstreams + streamid;
+ } else {
+ FIXME("secondary streams not implemented yet");
+ }
+
+ if (sctx->sct == -1) {
+ xhci_dma_read_u32s(epctx->xhci, sctx->pctx, ctx, sizeof(ctx));
+ fprintf(stderr, "%s: init sctx #%d @ " DMA_ADDR_FMT ": %08x %08x\n",
+ __func__, streamid, sctx->pctx, ctx[0], ctx[1]);
+ sct = (ctx[0] >> 1) & 0x07;
+ if (epctx->lsa && sct != 1) {
+ *cc_error = CC_INVALID_STREAM_TYPE_ERROR;
+ return NULL;
+ }
+ sctx->sct = sct;
+ base = xhci_addr64(ctx[0] & ~0xf, ctx[1]);
+ xhci_ring_init(epctx->xhci, &sctx->ring, base);
+ }
+ return sctx;
+}
+
static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
- uint32_t state)
+ XHCIStreamContext *sctx, uint32_t state)
{
uint32_t ctx[5];
+ uint32_t ctx2[2];
+ fprintf(stderr, "%s: epid %d, state %d\n",
+ __func__, epctx->epid, state);
xhci_dma_read_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
ctx[0] &= ~EP_STATE_MASK;
ctx[0] |= state;
- ctx[2] = epctx->ring.dequeue | epctx->ring.ccs;
- ctx[3] = (epctx->ring.dequeue >> 16) >> 16;
- DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n",
- epctx->pctx, state, ctx[3], ctx[2]);
+
+ /* update ring dequeue ptr */
+ if (epctx->nr_pstreams) {
+ if (sctx != NULL) {
+ xhci_dma_read_u32s(xhci, sctx->pctx, ctx2, sizeof(ctx2));
+ ctx2[0] &= 0xe;
+ ctx2[0] |= sctx->ring.dequeue | sctx->ring.ccs;
+ ctx2[1] = (sctx->ring.dequeue >> 16) >> 16;
+ xhci_dma_write_u32s(xhci, sctx->pctx, ctx2, sizeof(ctx2));
+ }
+ } else {
+ ctx[2] = epctx->ring.dequeue | epctx->ring.ccs;
+ ctx[3] = (epctx->ring.dequeue >> 16) >> 16;
+ DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n",
+ epctx->pctx, state, ctx[3], ctx[2]);
+ }
+
xhci_dma_write_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
epctx->state = state;
}
@@ -1087,7 +1201,7 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
static void xhci_ep_kick_timer(void *opaque)
{
XHCIEPContext *epctx = opaque;
- xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid);
+ xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid, 0);
}
static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
@@ -1117,16 +1231,22 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
slot->eps[epid-1] = epctx;
dequeue = xhci_addr64(ctx[2] & ~0xf, ctx[3]);
- xhci_ring_init(xhci, &epctx->ring, dequeue);
- epctx->ring.ccs = ctx[2] & 1;
epctx->type = (ctx[1] >> EP_TYPE_SHIFT) & EP_TYPE_MASK;
DPRINTF("xhci: endpoint %d.%d type is %d\n", epid/2, epid%2, epctx->type);
epctx->pctx = pctx;
epctx->max_psize = ctx[1]>>16;
epctx->max_psize *= 1+((ctx[1]>>8)&0xff);
+ epctx->max_pstreams = (ctx[0] >> 10) & 0xf;
+ epctx->lsa = (ctx[0] >> 15) & 1;
DPRINTF("xhci: endpoint %d.%d max transaction (burst) size is %d\n",
epid/2, epid%2, epctx->max_psize);
+ if (epctx->max_pstreams) {
+ xhci_alloc_streams(epctx, dequeue);
+ } else {
+ xhci_ring_init(xhci, &epctx->ring, dequeue);
+ epctx->ring.ccs = ctx[2] & 1;
+ }
for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) {
usb_packet_init(&epctx->transfers[i].packet);
}
@@ -1227,7 +1347,11 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
epctx = slot->eps[epid-1];
- xhci_set_ep_state(xhci, epctx, EP_DISABLED);
+ if (epctx->nr_pstreams) {
+ xhci_free_streams(epctx);
+ }
+
+ xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED);
qemu_free_timer(epctx->kick_timer);
g_free(epctx);
@@ -1264,7 +1388,11 @@ static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid,
epctx = slot->eps[epid-1];
- xhci_set_ep_state(xhci, epctx, EP_STOPPED);
+ xhci_set_ep_state(xhci, epctx, NULL, EP_STOPPED);
+
+ if (epctx->nr_pstreams) {
+ xhci_reset_streams(epctx);
+ }
return CC_SUCCESS;
}
@@ -1315,16 +1443,22 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid,
return CC_USB_TRANSACTION_ERROR;
}
- xhci_set_ep_state(xhci, epctx, EP_STOPPED);
+ xhci_set_ep_state(xhci, epctx, NULL, EP_STOPPED);
+
+ if (epctx->nr_pstreams) {
+ xhci_reset_streams(epctx);
+ }
return CC_SUCCESS;
}
static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
- unsigned int epid, uint64_t pdequeue)
+ unsigned int epid, unsigned int streamid,
+ uint64_t pdequeue)
{
XHCISlot *slot;
XHCIEPContext *epctx;
+ XHCIStreamContext *sctx;
dma_addr_t dequeue;
assert(slotid >= 1 && slotid <= xhci->numslots);
@@ -1334,7 +1468,7 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
return CC_TRB_ERROR;
}
- trace_usb_xhci_ep_set_dequeue(slotid, epid, pdequeue);
+ trace_usb_xhci_ep_set_dequeue(slotid, epid, streamid, pdequeue);
dequeue = xhci_mask64(pdequeue);
slot = &xhci->slots[slotid-1];
@@ -1346,16 +1480,26 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
epctx = slot->eps[epid-1];
-
if (epctx->state != EP_STOPPED) {
fprintf(stderr, "xhci: set EP dequeue pointer while EP %d not stopped\n", epid);
return CC_CONTEXT_STATE_ERROR;
}
- xhci_ring_init(xhci, &epctx->ring, dequeue & ~0xF);
- epctx->ring.ccs = dequeue & 1;
+ if (epctx->nr_pstreams) {
+ uint32_t err;
+ sctx = xhci_find_stream(epctx, streamid, &err);
+ if (sctx == NULL) {
+ return err;
+ }
+ xhci_ring_init(xhci, &sctx->ring, dequeue & ~0xf);
+ sctx->ring.ccs = dequeue & 1;
+ } else {
+ sctx = NULL;
+ xhci_ring_init(xhci, &epctx->ring, dequeue & ~0xF);
+ epctx->ring.ccs = dequeue & 1;
+ }
- xhci_set_ep_state(xhci, epctx, EP_STOPPED);
+ xhci_set_ep_state(xhci, epctx, sctx, EP_STOPPED);
return CC_SUCCESS;
}
@@ -1484,12 +1628,22 @@ static void xhci_stall_ep(XHCITransfer *xfer)
XHCIState *xhci = xfer->xhci;
XHCISlot *slot = &xhci->slots[xfer->slotid-1];
XHCIEPContext *epctx = slot->eps[xfer->epid-1];
+ uint32_t err;
+ XHCIStreamContext *sctx;
- epctx->ring.dequeue = xfer->trbs[0].addr;
- epctx->ring.ccs = xfer->trbs[0].ccs;
- xhci_set_ep_state(xhci, epctx, EP_HALTED);
- DPRINTF("xhci: stalled slot %d ep %d\n", xfer->slotid, xfer->epid);
- DPRINTF("xhci: will continue at "DMA_ADDR_FMT"\n", epctx->ring.dequeue);
+ if (epctx->nr_pstreams) {
+ sctx = xhci_find_stream(epctx, xfer->streamid, &err);
+ if (sctx == NULL) {
+ return;
+ }
+ sctx->ring.dequeue = xfer->trbs[0].addr;
+ sctx->ring.ccs = xfer->trbs[0].ccs;
+ xhci_set_ep_state(xhci, epctx, sctx, EP_HALTED);
+ } else {
+ epctx->ring.dequeue = xfer->trbs[0].addr;
+ epctx->ring.ccs = xfer->trbs[0].ccs;
+ xhci_set_ep_state(xhci, epctx, NULL, EP_HALTED);
+ }
}
static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer,
@@ -1518,8 +1672,8 @@ static int xhci_setup_packet(XHCITransfer *xfer)
}
xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */
- usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr, false,
- xfer->int_req);
+ usb_packet_setup(&xfer->packet, dir, ep, xfer->streamid,
+ xfer->trbs[0].addr, false, xfer->int_req);
usb_packet_map(&xfer->packet, &xfer->sgl);
DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
xfer->packet.pid, dev->addr, ep->nr);
@@ -1572,7 +1726,7 @@ static int xhci_complete_packet(XHCITransfer *xfer)
default:
fprintf(stderr, "%s: FIXME: status = %d\n", __func__,
xfer->packet.status);
- FIXME();
+ FIXME("unhandled USB_RET_*");
}
return 0;
}
@@ -1585,7 +1739,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
trb_setup = &xfer->trbs[0];
trb_status = &xfer->trbs[xfer->trb_count-1];
- trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid);
+ trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, xfer->streamid);
/* at most one Event Data TRB allowed after STATUS */
if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) {
@@ -1627,7 +1781,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
xhci_complete_packet(xfer);
if (!xfer->running_async && !xfer->running_retry) {
- xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
+ xhci_kick_ep(xhci, xfer->slotid, xfer->epid, 0);
}
return 0;
}
@@ -1710,26 +1864,29 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
xhci_complete_packet(xfer);
if (!xfer->running_async && !xfer->running_retry) {
- xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
+ xhci_kick_ep(xhci, xfer->slotid, xfer->epid, xfer->streamid);
}
return 0;
}
static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
{
- trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid);
+ trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, xfer->streamid);
return xhci_submit(xhci, xfer, epctx);
}
-static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid)
+static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
+ unsigned int epid, unsigned int streamid)
{
+ XHCIStreamContext *stctx;
XHCIEPContext *epctx;
+ XHCIRing *ring;
USBEndpoint *ep = NULL;
uint64_t mfindex;
int length;
int i;
- trace_usb_xhci_ep_kick(slotid, epid);
+ trace_usb_xhci_ep_kick(slotid, epid, streamid);
assert(slotid >= 1 && slotid <= xhci->numslots);
assert(epid >= 1 && epid <= 31);
@@ -1782,14 +1939,28 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
return;
}
- xhci_set_ep_state(xhci, epctx, EP_RUNNING);
+
+ if (epctx->nr_pstreams) {
+ uint32_t err;
+ stctx = xhci_find_stream(epctx, streamid, &err);
+ if (stctx == NULL) {
+ return;
+ }
+ ring = &stctx->ring;
+ xhci_set_ep_state(xhci, epctx, stctx, EP_RUNNING);
+ } else {
+ ring = &epctx->ring;
+ streamid = 0;
+ xhci_set_ep_state(xhci, epctx, NULL, EP_RUNNING);
+ }
+ assert(ring->base != 0);
while (1) {
XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer];
if (xfer->running_async || xfer->running_retry) {
break;
}
- length = xhci_ring_chain_length(xhci, &epctx->ring);
+ length = xhci_ring_chain_length(xhci, ring);
if (length < 0) {
break;
} else if (length == 0) {
@@ -1808,11 +1979,12 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
xfer->trb_count = length;
for (i = 0; i < length; i++) {
- assert(xhci_ring_fetch(xhci, &epctx->ring, &xfer->trbs[i], NULL));
+ assert(xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL));
}
xfer->xhci = xhci;
xfer->epid = epid;
xfer->slotid = slotid;
+ xfer->streamid = streamid;
if (epid == 1) {
if (xhci_fire_ctl_transfer(xhci, xfer) >= 0) {
@@ -1977,7 +2149,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
DPRINTF("xhci: device address is %d\n", slot->devaddr);
usb_device_reset(dev);
usb_packet_setup(&p, USB_TOKEN_OUT,
- usb_ep_get(dev, USB_TOKEN_OUT, 0),
+ usb_ep_get(dev, USB_TOKEN_OUT, 0), 0,
0, false, false);
usb_device_handle_control(dev, &p,
DeviceOutRequest | USB_REQ_SET_ADDRESS,
@@ -2357,11 +2529,14 @@ static void xhci_process_commands(XHCIState *xhci)
}
break;
case CR_SET_TR_DEQUEUE:
+ fprintf(stderr, "%s: CR_SET_TR_DEQUEUE\n", __func__);
slotid = xhci_get_slot(xhci, &event, &trb);
if (slotid) {
unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT)
& TRB_CR_EPID_MASK;
- event.ccode = xhci_set_ep_dequeue(xhci, slotid, epid,
+ unsigned int streamid = (trb.status >> 16) & 0xffff;
+ event.ccode = xhci_set_ep_dequeue(xhci, slotid,
+ epid, streamid,
trb.parameter);
}
break;
@@ -2554,9 +2729,9 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size)
break;
case 0x10: /* HCCPARAMS */
if (sizeof(dma_addr_t) == 4) {
- ret = 0x00081000;
+ ret = 0x00087000;
} else {
- ret = 0x00081001;
+ ret = 0x00087001;
}
break;
case 0x14: /* DBOFF */
@@ -2880,6 +3055,7 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg,
uint64_t val, unsigned size)
{
XHCIState *xhci = ptr;
+ unsigned int epid, streamid;
trace_usb_xhci_doorbell_write(reg, val);
@@ -2898,13 +3074,15 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg,
(uint32_t)val);
}
} else {
+ epid = val & 0xff;
+ streamid = (val >> 16) & 0xffff;
if (reg > xhci->numslots) {
fprintf(stderr, "xhci: bad doorbell %d\n", (int)reg);
- } else if (val > 31) {
+ } else if (epid > 31) {
fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n",
(int)reg, (uint32_t)val);
} else {
- xhci_kick_ep(xhci, reg, val);
+ xhci_kick_ep(xhci, reg, epid, streamid);
}
}
}
@@ -2988,7 +3166,7 @@ static void xhci_complete(USBPort *port, USBPacket *packet)
return;
}
xhci_complete_packet(xfer);
- xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid);
+ xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid, xfer->streamid);
}
static void xhci_child_detach(USBPort *uport, USBDevice *child)
@@ -3033,7 +3211,8 @@ static int xhci_find_epid(USBEndpoint *ep)
}
}
-static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep)
+static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep,
+ unsigned int stream)
{
XHCIState *xhci = container_of(bus, XHCIState, bus);
int slotid;
@@ -3044,7 +3223,7 @@ static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep)
DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr);
return;
}
- xhci_kick_ep(xhci, slotid, xhci_find_epid(ep));
+ xhci_kick_ep(xhci, slotid, xhci_find_epid(ep), stream);
}
static USBBusOps xhci_bus_ops = {
diff --git a/hw/usb/host-bsd.c b/hw/usb/host-bsd.c
index 07f0e01cc0..39f22810b3 100644
--- a/hw/usb/host-bsd.c
+++ b/hw/usb/host-bsd.c
@@ -637,9 +637,3 @@ void usb_host_info(Monitor *mon, const QDict *qdict)
{
usb_host_scan(mon, usb_host_info_device);
}
-
-/* XXX add this */
-int usb_host_device_close(const char *devname)
-{
- return 0;
-}
diff --git a/hw/usb/host-legacy.c b/hw/usb/host-legacy.c
new file mode 100644
index 0000000000..3a5f705721
--- /dev/null
+++ b/hw/usb/host-legacy.c
@@ -0,0 +1,144 @@
+/*
+ * Linux host USB redirector
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Copyright (c) 2008 Max Krasnyansky
+ * Support for host device auto connect & disconnect
+ * Major rewrite to support fully async operation
+ *
+ * Copyright 2008 TJ <linux@tjworld.net>
+ * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition
+ * to the legacy /proc/bus/usb USB device discovery and handling
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "hw/usb.h"
+#include "hw/usb/host.h"
+
+/*
+ * Autoconnect filter
+ * Format:
+ * auto:bus:dev[:vid:pid]
+ * auto:bus.dev[:vid:pid]
+ *
+ * bus - bus number (dec, * means any)
+ * dev - device number (dec, * means any)
+ * vid - vendor id (hex, * means any)
+ * pid - product id (hex, * means any)
+ *
+ * See 'lsusb' output.
+ */
+static int parse_filter(const char *spec, struct USBAutoFilter *f)
+{
+ enum { BUS, DEV, VID, PID, DONE };
+ const char *p = spec;
+ int i;
+
+ f->bus_num = 0;
+ f->addr = 0;
+ f->vendor_id = 0;
+ f->product_id = 0;
+
+ for (i = BUS; i < DONE; i++) {
+ p = strpbrk(p, ":.");
+ if (!p) {
+ break;
+ }
+ p++;
+
+ if (*p == '*') {
+ continue;
+ }
+ switch (i) {
+ case BUS:
+ f->bus_num = strtol(p, NULL, 10);
+ break;
+ case DEV:
+ f->addr = strtol(p, NULL, 10);
+ break;
+ case VID:
+ f->vendor_id = strtol(p, NULL, 16);
+ break;
+ case PID:
+ f->product_id = strtol(p, NULL, 16);
+ break;
+ }
+ }
+
+ if (i < DEV) {
+ fprintf(stderr, "husb: invalid auto filter spec %s\n", spec);
+ return -1;
+ }
+
+ return 0;
+}
+
+USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
+{
+ struct USBAutoFilter filter;
+ USBDevice *dev;
+ char *p;
+
+ dev = usb_create(bus, "usb-host");
+
+ if (strstr(devname, "auto:")) {
+ if (parse_filter(devname, &filter) < 0) {
+ goto fail;
+ }
+ } else {
+ p = strchr(devname, '.');
+ if (p) {
+ filter.bus_num = strtoul(devname, NULL, 0);
+ filter.addr = strtoul(p + 1, NULL, 0);
+ filter.vendor_id = 0;
+ filter.product_id = 0;
+ } else {
+ p = strchr(devname, ':');
+ if (p) {
+ filter.bus_num = 0;
+ filter.addr = 0;
+ filter.vendor_id = strtoul(devname, NULL, 16);
+ filter.product_id = strtoul(p + 1, NULL, 16);
+ } else {
+ goto fail;
+ }
+ }
+ }
+
+ qdev_prop_set_uint32(&dev->qdev, "hostbus", filter.bus_num);
+ qdev_prop_set_uint32(&dev->qdev, "hostaddr", filter.addr);
+ qdev_prop_set_uint32(&dev->qdev, "vendorid", filter.vendor_id);
+ qdev_prop_set_uint32(&dev->qdev, "productid", filter.product_id);
+ qdev_init_nofail(&dev->qdev);
+ return dev;
+
+fail:
+ qdev_free(&dev->qdev);
+ return NULL;
+}
+
+static void usb_host_register_types(void)
+{
+ usb_legacy_register("usb-host", "host", usb_host_device_open);
+}
+
+type_init(usb_host_register_types)
diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c
index a2cff8a74d..b67aeba096 100644
--- a/hw/usb/host-linux.c
+++ b/hw/usb/host-linux.c
@@ -43,6 +43,7 @@
#include <linux/version.h>
#include "hw/usb.h"
#include "hw/usb/desc.h"
+#include "hw/usb/host.h"
/* We redefine it to avoid version problems */
struct usb_ctrltransfer {
@@ -87,14 +88,6 @@ struct endp_data {
int inflight;
};
-struct USBAutoFilter {
- uint32_t bus_num;
- uint32_t addr;
- char *port;
- uint32_t vendor_id;
- uint32_t product_id;
-};
-
enum USBHostDeviceOptions {
USB_HOST_OPT_PIPELINE,
};
@@ -131,7 +124,6 @@ typedef struct USBHostDevice {
static QTAILQ_HEAD(, USBHostDevice) hostdevs = QTAILQ_HEAD_INITIALIZER(hostdevs);
static int usb_host_close(USBHostDevice *dev);
-static int parse_filter(const char *spec, struct USBAutoFilter *f);
static void usb_host_auto_check(void *unused);
static int usb_host_read_file(char *line, size_t line_size,
const char *device_file, const char *device_name);
@@ -1544,75 +1536,10 @@ static const TypeInfo usb_host_dev_info = {
static void usb_host_register_types(void)
{
type_register_static(&usb_host_dev_info);
- usb_legacy_register("usb-host", "host", usb_host_device_open);
}
type_init(usb_host_register_types)
-USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
-{
- struct USBAutoFilter filter;
- USBDevice *dev;
- char *p;
-
- dev = usb_create(bus, "usb-host");
-
- if (strstr(devname, "auto:")) {
- if (parse_filter(devname, &filter) < 0) {
- goto fail;
- }
- } else {
- if ((p = strchr(devname, '.'))) {
- filter.bus_num = strtoul(devname, NULL, 0);
- filter.addr = strtoul(p + 1, NULL, 0);
- filter.vendor_id = 0;
- filter.product_id = 0;
- } else if ((p = strchr(devname, ':'))) {
- filter.bus_num = 0;
- filter.addr = 0;
- filter.vendor_id = strtoul(devname, NULL, 16);
- filter.product_id = strtoul(p + 1, NULL, 16);
- } else {
- goto fail;
- }
- }
-
- qdev_prop_set_uint32(&dev->qdev, "hostbus", filter.bus_num);
- qdev_prop_set_uint32(&dev->qdev, "hostaddr", filter.addr);
- qdev_prop_set_uint32(&dev->qdev, "vendorid", filter.vendor_id);
- qdev_prop_set_uint32(&dev->qdev, "productid", filter.product_id);
- qdev_init_nofail(&dev->qdev);
- return dev;
-
-fail:
- qdev_free(&dev->qdev);
- return NULL;
-}
-
-int usb_host_device_close(const char *devname)
-{
-#if 0
- char product_name[PRODUCT_NAME_SZ];
- int bus_num, addr;
- USBHostDevice *s;
-
- if (strstr(devname, "auto:")) {
- return usb_host_auto_del(devname);
- }
- if (usb_host_find_device(&bus_num, &addr, product_name,
- sizeof(product_name), devname) < 0) {
- return -1;
- }
- s = hostdev_find(bus_num, addr);
- if (s) {
- usb_device_delete_addr(s->bus_num, s->dev.addr);
- return 0;
- }
-#endif
-
- return -1;
-}
-
/*
* Read sys file-system device file
*
@@ -1840,56 +1767,6 @@ static void usb_host_auto_check(void *unused)
qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000);
}
-/*
- * Autoconnect filter
- * Format:
- * auto:bus:dev[:vid:pid]
- * auto:bus.dev[:vid:pid]
- *
- * bus - bus number (dec, * means any)
- * dev - device number (dec, * means any)
- * vid - vendor id (hex, * means any)
- * pid - product id (hex, * means any)
- *
- * See 'lsusb' output.
- */
-static int parse_filter(const char *spec, struct USBAutoFilter *f)
-{
- enum { BUS, DEV, VID, PID, DONE };
- const char *p = spec;
- int i;
-
- f->bus_num = 0;
- f->addr = 0;
- f->vendor_id = 0;
- f->product_id = 0;
-
- for (i = BUS; i < DONE; i++) {
- p = strpbrk(p, ":.");
- if (!p) {
- break;
- }
- p++;
-
- if (*p == '*') {
- continue;
- }
- switch(i) {
- case BUS: f->bus_num = strtol(p, NULL, 10); break;
- case DEV: f->addr = strtol(p, NULL, 10); break;
- case VID: f->vendor_id = strtol(p, NULL, 16); break;
- case PID: f->product_id = strtol(p, NULL, 16); break;
- }
- }
-
- if (i < DEV) {
- fprintf(stderr, "husb: invalid auto filter spec %s\n", spec);
- return -1;
- }
-
- return 0;
-}
-
/**********************/
/* USB host device info */
diff --git a/hw/usb/host-stub.c b/hw/usb/host-stub.c
index 8affba76c1..28d8032caf 100644
--- a/hw/usb/host-stub.c
+++ b/hw/usb/host-stub.c
@@ -45,8 +45,3 @@ USBDevice *usb_host_device_open(USBBus *bus, const char *devname)
{
return NULL;
}
-
-int usb_host_device_close(const char *devname)
-{
- return 0;
-}
diff --git a/hw/usb/host.h b/hw/usb/host.h
new file mode 100644
index 0000000000..048ff3b482
--- /dev/null
+++ b/hw/usb/host.h
@@ -0,0 +1,44 @@
+/*
+ * Linux host USB redirector
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Copyright (c) 2008 Max Krasnyansky
+ * Support for host device auto connect & disconnect
+ * Major rewrite to support fully async operation
+ *
+ * Copyright 2008 TJ <linux@tjworld.net>
+ * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition
+ * to the legacy /proc/bus/usb USB device discovery and handling
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef QEMU_USB_HOST_H
+#define QEMU_USB_HOST_H
+
+struct USBAutoFilter {
+ uint32_t bus_num;
+ uint32_t addr;
+ char *port;
+ uint32_t vendor_id;
+ uint32_t product_id;
+};
+
+#endif /* QEMU_USB_HOST_H */
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 8c0ead07c5..c519b9b92a 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -737,7 +737,7 @@ static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
uint8_t ep)
{
struct usb_redir_bulk_packet_header bulk_packet;
- size_t size = (p->combined) ? p->combined->iov.size : p->iov.size;
+ size_t size = usb_packet_size(p);
const int maxp = dev->endpoint[EP2I(ep)].max_packet_size;
if (usbredir_already_in_flight(dev, p->id)) {
@@ -771,12 +771,7 @@ static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
&bulk_packet, NULL, 0);
} else {
uint8_t buf[size];
- if (p->combined) {
- iov_to_buf(p->combined->iov.iov, p->combined->iov.niov,
- 0, buf, size);
- } else {
- usb_packet_copy(p, buf, size);
- }
+ usb_packet_copy(p, buf, size);
usbredir_log_data(dev, "bulk data out:", buf, size);
usbredirparser_send_bulk_packet(dev->parser, p->id,
&bulk_packet, buf, size);
@@ -1830,7 +1825,7 @@ static void usbredir_bulk_packet(void *priv, uint64_t id,
p = usbredir_find_packet_by_id(dev, ep, id);
if (p) {
- size_t size = (p->combined) ? p->combined->iov.size : p->iov.size;
+ size_t size = usb_packet_size(p);
usbredir_handle_status(dev, p, bulk_packet->status);
if (data_len > 0) {
usbredir_log_data(dev, "bulk data in:", data, data_len);
@@ -1840,12 +1835,7 @@ static void usbredir_bulk_packet(void *priv, uint64_t id,
p->status = USB_RET_BABBLE;
data_len = len = size;
}
- if (p->combined) {
- iov_from_buf(p->combined->iov.iov, p->combined->iov.niov,
- 0, data, data_len);
- } else {
- usb_packet_copy(p, data, data_len);
- }
+ usb_packet_copy(p, data, data_len);
}
p->actual_length = len;
if (p->pid == USB_TOKEN_IN && p->ep->pipeline) {
@@ -1907,7 +1897,7 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id,
}
if (QTAILQ_EMPTY(&dev->endpoint[EP2I(ep)].bufpq)) {
- usb_wakeup(usb_ep_get(&dev->dev, USB_TOKEN_IN, ep & 0x0f));
+ usb_wakeup(usb_ep_get(&dev->dev, USB_TOKEN_IN, ep & 0x0f), 0);
}
/* bufp_alloc also adds the packet to the ep queue */
diff --git a/hw/vga.c b/hw/vga.c
index e2ba7f208c..1caf23d7b6 100644
--- a/hw/vga.c
+++ b/hw/vga.c
@@ -1643,6 +1643,11 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
uint8_t *d;
uint32_t v, addr1, addr;
vga_draw_line_func *vga_draw_line;
+#if defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
+ static const bool byteswap = false;
+#else
+ static const bool byteswap = true;
+#endif
full_update |= update_basic_params(s);
@@ -1685,18 +1690,11 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
disp_width != s->last_width ||
height != s->last_height ||
s->last_depth != depth) {
-#if defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
- if (depth == 16 || depth == 32) {
-#else
- if (depth == 32) {
-#endif
+ if (depth == 32 || (depth == 16 && !byteswap)) {
qemu_free_displaysurface(s->ds);
s->ds->surface = qemu_create_displaysurface_from(disp_width, height, depth,
s->line_offset,
- s->vram_ptr + (s->start_addr * 4));
-#if defined(HOST_WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
- s->ds->surface->pf = qemu_different_endianness_pixelformat(depth);
-#endif
+ s->vram_ptr + (s->start_addr * 4), byteswap);
dpy_gfx_resize(s->ds);
} else {
qemu_console_resize(s->ds, disp_width, height);
@@ -1715,7 +1713,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
s->ds->surface = qemu_create_displaysurface_from(disp_width,
height, depth,
s->line_offset,
- s->vram_ptr + (s->start_addr * 4));
+ s->vram_ptr + (s->start_addr * 4), byteswap);
dpy_gfx_setdata(s->ds);
}
diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c
index cd15ee40a8..8fc201bfb9 100644
--- a/hw/vmware_vga.c
+++ b/hw/vmware_vga.c
@@ -1074,7 +1074,7 @@ static void vmsvga_screen_dump(void *opaque, const char *filename, bool cswitch,
ds_get_height(s->vga.ds),
32,
ds_get_linesize(s->vga.ds),
- s->vga.vram_ptr);
+ s->vga.vram_ptr, false);
ppm_save(filename, ds, errp);
g_free(ds);
}
diff --git a/hw/xenfb.c b/hw/xenfb.c
index 903efd3073..7f1f6b4643 100644
--- a/hw/xenfb.c
+++ b/hw/xenfb.c
@@ -756,7 +756,8 @@ static void xenfb_update(void *opaque)
qemu_free_displaysurface(xenfb->c.ds);
xenfb->c.ds->surface = qemu_create_displaysurface_from
(xenfb->width, xenfb->height, xenfb->depth,
- xenfb->row_stride, xenfb->pixels + xenfb->offset);
+ xenfb->row_stride, xenfb->pixels + xenfb->offset,
+ false);
break;
default:
/* we must convert stuff */
diff --git a/hw/xilinx_axienet.c b/hw/xilinx_axienet.c
index 34e344ce2c..e5d9251b8b 100644
--- a/hw/xilinx_axienet.c
+++ b/hw/xilinx_axienet.c
@@ -869,9 +869,11 @@ static int xilinx_enet_init(SysBusDevice *dev)
static void xilinx_enet_initfn(Object *obj)
{
struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj));
+ Error *errp = NULL;
object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
- (Object **) &s->tx_dev, NULL);
+ (Object **) &s->tx_dev, &errp);
+ assert_no_error(errp);
}
static Property xilinx_enet_properties[] = {
diff --git a/include/block/aio.h b/include/block/aio.h
index 8eda924599..5b54d383fc 100644
--- a/include/block/aio.h
+++ b/include/block/aio.h
@@ -63,6 +63,9 @@ typedef struct AioContext {
/* Used for aio_notify. */
EventNotifier notifier;
+
+ /* GPollFDs for aio_poll() */
+ GArray *pollfds;
} AioContext;
/* Returns 1 if there are still outstanding AIO requests; 0 otherwise */
diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h
index ae64590cdf..3dc96568ac 100644
--- a/include/exec/cpu-defs.h
+++ b/include/exec/cpu-defs.h
@@ -184,7 +184,7 @@ typedef struct CPUWatchpoint {
struct GDBRegisterState *gdb_regs; \
\
/* Core interrupt code */ \
- jmp_buf jmp_env; \
+ sigjmp_buf jmp_env; \
int exception_index; \
\
CPUArchState *next_cpu; /* next CPU sharing TB cache */ \
diff --git a/include/qemu/log.h b/include/qemu/log.h
index 5a46555112..452700329e 100644
--- a/include/qemu/log.h
+++ b/include/qemu/log.h
@@ -126,14 +126,6 @@ static inline void qemu_log_set_file(FILE *f)
qemu_logfile = f;
}
-/* Set up a new log file, only if none is set */
-static inline void qemu_log_try_set_file(FILE *f)
-{
- if (!qemu_logfile) {
- qemu_logfile = f;
- }
-}
-
/* define log items */
typedef struct QEMULogItem {
int mask;
diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h
index e8059c3d0a..09952885a9 100644
--- a/include/qemu/main-loop.h
+++ b/include/qemu/main-loop.h
@@ -297,8 +297,8 @@ void qemu_mutex_unlock_iothread(void);
/* internal interfaces */
void qemu_fd_register(int fd);
-void qemu_iohandler_fill(int *pnfds, fd_set *readfds, fd_set *writefds, fd_set *xfds);
-void qemu_iohandler_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds, int rc);
+void qemu_iohandler_fill(GArray *pollfds);
+void qemu_iohandler_poll(GArray *pollfds, int rc);
QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque);
void qemu_bh_schedule_idle(QEMUBH *bh);
diff --git a/include/sysemu/os-win32.h b/include/sysemu/os-win32.h
index bf9edeb9ab..71f5fa0a91 100644
--- a/include/sysemu/os-win32.h
+++ b/include/sysemu/os-win32.h
@@ -63,6 +63,14 @@
# undef setjmp
# define setjmp(env) _setjmp(env, NULL)
#endif
+/* QEMU uses sigsetjmp()/siglongjmp() as the portable way to specify
+ * "longjmp and don't touch the signal masks". Since we know that the
+ * savemask parameter will always be zero we can safely define these
+ * in terms of setjmp/longjmp on Win32.
+ */
+#define sigjmp_buf jmp_buf
+#define sigsetjmp(env, savemask) setjmp(env)
+#define siglongjmp(env, val) longjmp(env, val)
/* Declaration of ffs() is missing in MinGW's strings.h. */
int ffs(int i);
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index 1d9599e5f4..b19ec952b4 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -89,12 +89,12 @@ typedef enum DisplayType
DT_DEFAULT,
DT_CURSES,
DT_SDL,
+ DT_GTK,
DT_NOGRAPHIC,
DT_NONE,
} DisplayType;
extern int autostart;
-extern int bios_size;
typedef enum {
VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL,
diff --git a/include/ui/console.h b/include/ui/console.h
index fc23baa06b..c42bca6efe 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -184,7 +184,8 @@ struct DisplayState {
void register_displaystate(DisplayState *ds);
DisplayState *get_displaystate(void);
DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp,
- int linesize, uint8_t *data);
+ int linesize, uint8_t *data,
+ bool byteswap);
PixelFormat qemu_different_endianness_pixelformat(int bpp);
PixelFormat qemu_default_pixelformat(int bpp);
@@ -442,7 +443,6 @@ void vga_hw_text_update(console_ch_t *chardata);
int is_graphic_console(void);
int is_fixedsize_console(void);
-CharDriverState *text_console_init(QemuOpts *opts);
void text_consoles_set_display(DisplayState *ds);
void console_select(unsigned int index);
void console_color_init(DisplayState *ds);
@@ -450,6 +450,11 @@ void qemu_console_resize(DisplayState *ds, int width, int height);
void qemu_console_copy(DisplayState *ds, int src_x, int src_y,
int dst_x, int dst_y, int w, int h);
+typedef CharDriverState *(VcHandler)(QemuOpts *);
+
+CharDriverState *vc_init(QemuOpts *opts);
+void register_vc_handler(VcHandler *handler);
+
/* sdl.c */
void sdl_display_init(DisplayState *ds, int full_screen, int no_frame);
@@ -482,4 +487,8 @@ void curses_display_init(DisplayState *ds, int full_screen);
int index_from_key(const char *key);
int index_from_keycode(int code);
+/* gtk.c */
+void early_gtk_display_init(void);
+void gtk_display_init(DisplayState *ds);
+
#endif
diff --git a/iohandler.c b/iohandler.c
index 2523adc11d..ae2ef8f966 100644
--- a/iohandler.c
+++ b/iohandler.c
@@ -39,6 +39,7 @@ typedef struct IOHandlerRecord {
void *opaque;
QLIST_ENTRY(IOHandlerRecord) next;
int fd;
+ int pollfds_idx;
bool deleted;
} IOHandlerRecord;
@@ -78,6 +79,7 @@ int qemu_set_fd_handler2(int fd,
ioh->fd_read = fd_read;
ioh->fd_write = fd_write;
ioh->opaque = opaque;
+ ioh->pollfds_idx = -1;
ioh->deleted = 0;
qemu_notify_event();
}
@@ -92,38 +94,56 @@ int qemu_set_fd_handler(int fd,
return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque);
}
-void qemu_iohandler_fill(int *pnfds, fd_set *readfds, fd_set *writefds, fd_set *xfds)
+void qemu_iohandler_fill(GArray *pollfds)
{
IOHandlerRecord *ioh;
QLIST_FOREACH(ioh, &io_handlers, next) {
+ int events = 0;
+
if (ioh->deleted)
continue;
if (ioh->fd_read &&
(!ioh->fd_read_poll ||
ioh->fd_read_poll(ioh->opaque) != 0)) {
- FD_SET(ioh->fd, readfds);
- if (ioh->fd > *pnfds)
- *pnfds = ioh->fd;
+ events |= G_IO_IN | G_IO_HUP | G_IO_ERR;
}
if (ioh->fd_write) {
- FD_SET(ioh->fd, writefds);
- if (ioh->fd > *pnfds)
- *pnfds = ioh->fd;
+ events |= G_IO_OUT | G_IO_ERR;
+ }
+ if (events) {
+ GPollFD pfd = {
+ .fd = ioh->fd,
+ .events = events,
+ };
+ ioh->pollfds_idx = pollfds->len;
+ g_array_append_val(pollfds, pfd);
+ } else {
+ ioh->pollfds_idx = -1;
}
}
}
-void qemu_iohandler_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds, int ret)
+void qemu_iohandler_poll(GArray *pollfds, int ret)
{
if (ret > 0) {
IOHandlerRecord *pioh, *ioh;
QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) {
- if (!ioh->deleted && ioh->fd_read && FD_ISSET(ioh->fd, readfds)) {
+ int revents = 0;
+
+ if (!ioh->deleted && ioh->pollfds_idx != -1) {
+ GPollFD *pfd = &g_array_index(pollfds, GPollFD,
+ ioh->pollfds_idx);
+ revents = pfd->revents;
+ }
+
+ if (!ioh->deleted && ioh->fd_read &&
+ (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR))) {
ioh->fd_read(ioh->opaque);
}
- if (!ioh->deleted && ioh->fd_write && FD_ISSET(ioh->fd, writefds)) {
+ if (!ioh->deleted && ioh->fd_write &&
+ (revents & (G_IO_OUT | G_IO_ERR))) {
ioh->fd_write(ioh->opaque);
}
diff --git a/main-loop.c b/main-loop.c
index 6f52ac39bc..8c9b58c14c 100644
--- a/main-loop.c
+++ b/main-loop.c
@@ -117,6 +117,8 @@ void qemu_notify_event(void)
aio_notify(qemu_aio_context);
}
+static GArray *gpollfds;
+
int qemu_init_main_loop(void)
{
int ret;
@@ -133,6 +135,7 @@ int qemu_init_main_loop(void)
return ret;
}
+ gpollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD));
qemu_aio_context = aio_context_new();
src = aio_get_g_source(qemu_aio_context);
g_source_attach(src, NULL);
@@ -140,100 +143,63 @@ int qemu_init_main_loop(void)
return 0;
}
-static fd_set rfds, wfds, xfds;
-static int nfds;
-static GPollFD poll_fds[1024 * 2]; /* this is probably overkill */
-static int n_poll_fds;
static int max_priority;
#ifndef _WIN32
-static void glib_select_fill(int *max_fd, fd_set *rfds, fd_set *wfds,
- fd_set *xfds, uint32_t *cur_timeout)
+static int glib_pollfds_idx;
+static int glib_n_poll_fds;
+
+static void glib_pollfds_fill(uint32_t *cur_timeout)
{
GMainContext *context = g_main_context_default();
- int i;
int timeout = 0;
+ int n;
g_main_context_prepare(context, &max_priority);
- n_poll_fds = g_main_context_query(context, max_priority, &timeout,
- poll_fds, ARRAY_SIZE(poll_fds));
- g_assert(n_poll_fds <= ARRAY_SIZE(poll_fds));
-
- for (i = 0; i < n_poll_fds; i++) {
- GPollFD *p = &poll_fds[i];
-
- if ((p->events & G_IO_IN)) {
- FD_SET(p->fd, rfds);
- *max_fd = MAX(*max_fd, p->fd);
- }
- if ((p->events & G_IO_OUT)) {
- FD_SET(p->fd, wfds);
- *max_fd = MAX(*max_fd, p->fd);
- }
- if ((p->events & G_IO_ERR)) {
- FD_SET(p->fd, xfds);
- *max_fd = MAX(*max_fd, p->fd);
- }
- }
+ glib_pollfds_idx = gpollfds->len;
+ n = glib_n_poll_fds;
+ do {
+ GPollFD *pfds;
+ glib_n_poll_fds = n;
+ g_array_set_size(gpollfds, glib_pollfds_idx + glib_n_poll_fds);
+ pfds = &g_array_index(gpollfds, GPollFD, glib_pollfds_idx);
+ n = g_main_context_query(context, max_priority, &timeout, pfds,
+ glib_n_poll_fds);
+ } while (n != glib_n_poll_fds);
if (timeout >= 0 && timeout < *cur_timeout) {
*cur_timeout = timeout;
}
}
-static void glib_select_poll(fd_set *rfds, fd_set *wfds, fd_set *xfds,
- bool err)
+static void glib_pollfds_poll(void)
{
GMainContext *context = g_main_context_default();
+ GPollFD *pfds = &g_array_index(gpollfds, GPollFD, glib_pollfds_idx);
- if (!err) {
- int i;
-
- for (i = 0; i < n_poll_fds; i++) {
- GPollFD *p = &poll_fds[i];
-
- if ((p->events & G_IO_IN) && FD_ISSET(p->fd, rfds)) {
- p->revents |= G_IO_IN;
- }
- if ((p->events & G_IO_OUT) && FD_ISSET(p->fd, wfds)) {
- p->revents |= G_IO_OUT;
- }
- if ((p->events & G_IO_ERR) && FD_ISSET(p->fd, xfds)) {
- p->revents |= G_IO_ERR;
- }
- }
- }
-
- if (g_main_context_check(context, max_priority, poll_fds, n_poll_fds)) {
+ if (g_main_context_check(context, max_priority, pfds, glib_n_poll_fds)) {
g_main_context_dispatch(context);
}
}
static int os_host_main_loop_wait(uint32_t timeout)
{
- struct timeval tv, *tvarg = NULL;
int ret;
- glib_select_fill(&nfds, &rfds, &wfds, &xfds, &timeout);
-
- if (timeout < UINT32_MAX) {
- tvarg = &tv;
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout % 1000) * 1000;
- }
+ glib_pollfds_fill(&timeout);
if (timeout > 0) {
qemu_mutex_unlock_iothread();
}
- ret = select(nfds + 1, &rfds, &wfds, &xfds, tvarg);
+ ret = g_poll((GPollFD *)gpollfds->data, gpollfds->len, timeout);
if (timeout > 0) {
qemu_mutex_lock_iothread();
}
- glib_select_poll(&rfds, &wfds, &xfds, (ret < 0));
+ glib_pollfds_poll();
return ret;
}
#else
@@ -327,14 +293,67 @@ void qemu_fd_register(int fd)
FD_CONNECT | FD_WRITE | FD_OOB);
}
+static int pollfds_fill(GArray *pollfds, fd_set *rfds, fd_set *wfds,
+ fd_set *xfds)
+{
+ int nfds = -1;
+ int i;
+
+ for (i = 0; i < pollfds->len; i++) {
+ GPollFD *pfd = &g_array_index(pollfds, GPollFD, i);
+ int fd = pfd->fd;
+ int events = pfd->events;
+ if (events & (G_IO_IN | G_IO_HUP | G_IO_ERR)) {
+ FD_SET(fd, rfds);
+ nfds = MAX(nfds, fd);
+ }
+ if (events & (G_IO_OUT | G_IO_ERR)) {
+ FD_SET(fd, wfds);
+ nfds = MAX(nfds, fd);
+ }
+ if (events & G_IO_PRI) {
+ FD_SET(fd, xfds);
+ nfds = MAX(nfds, fd);
+ }
+ }
+ return nfds;
+}
+
+static void pollfds_poll(GArray *pollfds, int nfds, fd_set *rfds,
+ fd_set *wfds, fd_set *xfds)
+{
+ int i;
+
+ for (i = 0; i < pollfds->len; i++) {
+ GPollFD *pfd = &g_array_index(pollfds, GPollFD, i);
+ int fd = pfd->fd;
+ int revents = 0;
+
+ if (FD_ISSET(fd, rfds)) {
+ revents |= G_IO_IN | G_IO_HUP | G_IO_ERR;
+ }
+ if (FD_ISSET(fd, wfds)) {
+ revents |= G_IO_OUT | G_IO_ERR;
+ }
+ if (FD_ISSET(fd, xfds)) {
+ revents |= G_IO_PRI;
+ }
+ pfd->revents = revents & pfd->events;
+ }
+}
+
static int os_host_main_loop_wait(uint32_t timeout)
{
GMainContext *context = g_main_context_default();
- int select_ret, g_poll_ret, ret, i;
+ GPollFD poll_fds[1024 * 2]; /* this is probably overkill */
+ int select_ret = 0;
+ int g_poll_ret, ret, i, n_poll_fds;
PollingEntry *pe;
WaitObjects *w = &wait_objects;
gint poll_timeout;
static struct timeval tv0;
+ fd_set rfds, wfds, xfds;
+ int nfds;
/* XXX: need to suppress polling by better using win32 events */
ret = 0;
@@ -381,11 +400,18 @@ static int os_host_main_loop_wait(uint32_t timeout)
* improve socket latency.
*/
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&xfds);
+ nfds = pollfds_fill(gpollfds, &rfds, &wfds, &xfds);
if (nfds >= 0) {
select_ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv0);
if (select_ret != 0) {
timeout = 0;
}
+ if (select_ret > 0) {
+ pollfds_poll(gpollfds, nfds, &rfds, &wfds, &xfds);
+ }
}
return select_ret || g_poll_ret;
@@ -402,21 +428,17 @@ int main_loop_wait(int nonblocking)
}
/* poll any events */
+ g_array_set_size(gpollfds, 0); /* reset for new iteration */
/* XXX: separate device handlers from system ones */
- nfds = -1;
- FD_ZERO(&rfds);
- FD_ZERO(&wfds);
- FD_ZERO(&xfds);
-
#ifdef CONFIG_SLIRP
slirp_update_timeout(&timeout);
- slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
+ slirp_pollfds_fill(gpollfds);
#endif
- qemu_iohandler_fill(&nfds, &rfds, &wfds, &xfds);
+ qemu_iohandler_fill(gpollfds);
ret = os_host_main_loop_wait(timeout);
- qemu_iohandler_poll(&rfds, &wfds, &xfds, ret);
+ qemu_iohandler_poll(gpollfds, ret);
#ifdef CONFIG_SLIRP
- slirp_select_poll(&rfds, &wfds, &xfds, (ret < 0));
+ slirp_pollfds_poll(gpollfds, (ret < 0));
#endif
qemu_run_all_timers();
diff --git a/monitor.c b/monitor.c
index 6a0f2573f5..32a6e74fd9 100644
--- a/monitor.c
+++ b/monitor.c
@@ -2740,7 +2740,7 @@ static const mon_cmd_t qmp_cmds[] = {
/*******************************************************************/
static const char *pch;
-static jmp_buf expr_env;
+static sigjmp_buf expr_env;
#define MD_TLONG 0
#define MD_I32 1
@@ -3135,7 +3135,7 @@ static const MonitorDef monitor_defs[] = {
static void expr_error(Monitor *mon, const char *msg)
{
monitor_printf(mon, "%s\n", msg);
- longjmp(expr_env, 1);
+ siglongjmp(expr_env, 1);
}
/* return 0 if OK, -1 if not found */
@@ -3345,7 +3345,7 @@ static int64_t expr_sum(Monitor *mon)
static int get_expr(Monitor *mon, int64_t *pval, const char **pp)
{
pch = *pp;
- if (setjmp(expr_env)) {
+ if (sigsetjmp(expr_env, 0)) {
*pp = pch;
return -1;
}
diff --git a/po/Makefile b/po/Makefile
new file mode 100644
index 0000000000..2b4420f178
--- /dev/null
+++ b/po/Makefile
@@ -0,0 +1,46 @@
+# This makefile is very special as it's meant to build as part of the build
+# process and also within the source tree to update the translation files.
+
+VERSION=$(shell cat ../VERSION)
+TRANSLATIONS=de_DE it
+SRCS=$(addsuffix .po, $(TRANSLATIONS))
+OBJS=$(addsuffix .mo, $(TRANSLATIONS))
+
+SRC_PATH=..
+
+-include ../config-host.mak
+
+vpath %.po $(SRC_PATH)/po
+
+all:
+ @echo Use 'make update' to update translation files
+ @echo or us 'make build' or 'make install' to build and install
+ @echo the translation files
+
+update: $(SRCS)
+
+build: $(OBJS)
+
+clean:
+ $(RM) $(OBJS)
+
+install: $(OBJS)
+ for obj in $(OBJS); do \
+ base=`basename $$obj .mo`; \
+ $(INSTALL) -d $(DESTDIR)$(prefix)/share/locale/$$base/LC_MESSAGES; \
+ $(INSTALL) -m644 $$obj $(DESTDIR)$(prefix)/share/locale/$$base/LC_MESSAGES/qemu.mo; \
+ done
+
+%.mo:
+ @msgfmt -o $@ $(SRC_PATH)/po/`basename $@ .mo`.po
+
+messages.po: $(SRC_PATH)/ui/gtk.c
+ @xgettext -o $@ --foreign-user --package-name=QEMU --package-version=1.0.50 --msgid-bugs-address=qemu-devel@nongnu.org -k_ -C $<
+
+de_DE.po: messages.po $(SRC_PATH)/ui/gtk.c
+ @msgmerge $@ $< > $@.bak && mv $@.bak $@
+
+it.po: messages.po $(SRC_PATH)/ui/gtk.c
+ @msgmerge $@ $< > $@.bak && mv $@.bak $@
+
+.PHONY: $(SRCS) clean all
diff --git a/po/de_DE.po b/po/de_DE.po
new file mode 100644
index 0000000000..875578349d
--- /dev/null
+++ b/po/de_DE.po
@@ -0,0 +1,41 @@
+# German translation for QEMU.
+# This file is put in the public domain.
+# Kevin Wolf <kwolf@redhat.com>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: QEMU 1.4.50\n"
+"Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n"
+"POT-Creation-Date: 2013-02-08 09:21-0600\n"
+"PO-Revision-Date: 2012-02-28 16:00+0100\n"
+"Last-Translator: Kevin Wolf <kwolf@redhat.com>\n"
+"Language-Team: Deutsch <de@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+
+#: ../ui/gtk.c:990
+msgid "_File"
+msgstr "_Datei"
+
+#: ../ui/gtk.c:1000
+msgid "_View"
+msgstr "_Ansicht"
+
+#: ../ui/gtk.c:1029
+msgid "Zoom To _Fit"
+msgstr "Auf _Fenstergröße skalieren"
+
+#: ../ui/gtk.c:1035
+msgid "Grab On _Hover"
+msgstr "Tastatur _automatisch einfangen"
+
+#: ../ui/gtk.c:1038
+msgid "_Grab Input"
+msgstr "_Eingabegeräte einfangen"
+
+#: ../ui/gtk.c:1064
+msgid "Show _Tabs"
+msgstr "_Tableiste anzeigen"
diff --git a/po/it.po b/po/it.po
new file mode 100644
index 0000000000..7d77fff2d3
--- /dev/null
+++ b/po/it.po
@@ -0,0 +1,41 @@
+# Italian translation for QEMU.
+# This file is put in the public domain.
+# Paolo Bonzini <pbonzini@redhat.com>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: QEMU 1.4.50\n"
+"Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n"
+"POT-Creation-Date: 2013-02-08 09:21-0600\n"
+"PO-Revision-Date: 2012-02-27 08:23+0100\n"
+"Last-Translator: Paolo Bonzini <pbonzini@redhat.com>\n"
+"Language-Team: Italian <it@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+
+#: ../ui/gtk.c:990
+msgid "_File"
+msgstr "_File"
+
+#: ../ui/gtk.c:1000
+msgid "_View"
+msgstr "_Visualizza"
+
+#: ../ui/gtk.c:1029
+msgid "Zoom To _Fit"
+msgstr "Adatta alla _finestra"
+
+#: ../ui/gtk.c:1035
+msgid "Grab On _Hover"
+msgstr "Cattura _automatica input"
+
+#: ../ui/gtk.c:1038
+msgid "_Grab Input"
+msgstr "_Cattura input"
+
+#: ../ui/gtk.c:1064
+msgid "Show _Tabs"
+msgstr "Mostra _tab"
diff --git a/po/messages.po b/po/messages.po
new file mode 100644
index 0000000000..191e81cc7c
--- /dev/null
+++ b/po/messages.po
@@ -0,0 +1,41 @@
+# SOME DESCRIPTIVE TITLE.
+# This file is put in the public domain.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: QEMU 1.4.50\n"
+"Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n"
+"POT-Creation-Date: 2013-02-08 09:21-0600\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../ui/gtk.c:990
+msgid "_File"
+msgstr ""
+
+#: ../ui/gtk.c:1000
+msgid "_View"
+msgstr ""
+
+#: ../ui/gtk.c:1029
+msgid "Zoom To _Fit"
+msgstr ""
+
+#: ../ui/gtk.c:1035
+msgid "Grab On _Hover"
+msgstr ""
+
+#: ../ui/gtk.c:1038
+msgid "_Grab Input"
+msgstr ""
+
+#: ../ui/gtk.c:1064
+msgid "Show _Tabs"
+msgstr ""
diff --git a/qapi-schema.json b/qapi-schema.json
index 7275b5dd6a..cd7ea25e4c 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2504,6 +2504,9 @@
#
# @fd: #optional file descriptor of an already opened tap
#
+# @fds: #optional multiple file descriptors of already opened multiqueue capable
+# tap
+#
# @script: #optional script to initialize the interface
#
# @downscript: #optional script to shut down the interface
@@ -2518,6 +2521,9 @@
#
# @vhostfd: #optional file descriptor of an already opened vhost net device
#
+# @vhostfds: #optional file descriptors of multiple already opened vhost net
+# devices
+#
# @vhostforce: #optional vhost on for non-MSIX virtio guests
#
# Since 1.2
diff --git a/qemu-char.c b/qemu-char.c
index e4b0f5304f..160decc2f0 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -2980,7 +2980,7 @@ static const struct {
{ .name = "socket", .open = qemu_chr_open_socket },
{ .name = "udp", .open = qemu_chr_open_udp },
{ .name = "msmouse", .open = qemu_chr_open_msmouse },
- { .name = "vc", .open = text_console_init },
+ { .name = "vc", .open = vc_init },
{ .name = "memory", .open = qemu_chr_open_ringbuf },
#ifdef _WIN32
{ .name = "file", .open = qemu_chr_open_win_file_out },
diff --git a/qemu-options.hx b/qemu-options.hx
index 4bc9c85d9e..2832d82148 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1354,7 +1354,7 @@ DEF("net", HAS_ARG, QEMU_OPTION_net,
"-net tap[,vlan=n][,name=str],ifname=name\n"
" connect the host TAP network interface to VLAN 'n'\n"
#else
- "-net tap[,vlan=n][,name=str][,fd=h][,ifname=name][,script=file][,downscript=dfile][,helper=helper][,sndbuf=nbytes][,vnet_hdr=on|off][,vhost=on|off][,vhostfd=h][,vhostforce=on|off]\n"
+ "-net tap[,vlan=n][,name=str][,fd=h][,fds=x:y:...:z][,ifname=name][,script=file][,downscript=dfile][,helper=helper][,sndbuf=nbytes][,vnet_hdr=on|off][,vhost=on|off][,vhostfd=h][,vhostfds=x:y:...:z][,vhostforce=on|off]\n"
" connect the host TAP network interface to VLAN 'n'\n"
" use network scripts 'file' (default=" DEFAULT_NETWORK_SCRIPT ")\n"
" to configure it and 'dfile' (default=" DEFAULT_NETWORK_DOWN_SCRIPT ")\n"
@@ -1363,6 +1363,7 @@ DEF("net", HAS_ARG, QEMU_OPTION_net,
" use network helper 'helper' (default=" DEFAULT_BRIDGE_HELPER ") to\n"
" configure it\n"
" use 'fd=h' to connect to an already opened TAP interface\n"
+ " use 'fds=x:y:...:z' to connect to already opened multiqueue capable TAP interfaces\n"
" use 'sndbuf=nbytes' to limit the size of the send buffer (the\n"
" default is disabled 'sndbuf=0' to enable flow control set 'sndbuf=1048576')\n"
" use vnet_hdr=off to avoid enabling the IFF_VNET_HDR tap flag\n"
@@ -1371,6 +1372,7 @@ DEF("net", HAS_ARG, QEMU_OPTION_net,
" (only has effect for virtio guests which use MSIX)\n"
" use vhostforce=on to force vhost on for non-MSIX virtio guests\n"
" use 'vhostfd=h' to connect to an already opened vhost net device\n"
+ " use 'vhostfds=x:y:...:z to connect to multiple already opened vhost net devices\n"
"-net bridge[,vlan=n][,name=str][,br=bridge][,helper=helper]\n"
" connects a host TAP network interface to a host bridge device 'br'\n"
" (default=" DEFAULT_BRIDGE_INTERFACE ") using the program 'helper'\n"
diff --git a/qom/object.c b/qom/object.c
index 563e45b0cc..3d638ff273 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -245,6 +245,7 @@ static void type_initialize(TypeImpl *ti)
g_assert(parent->class_size <= ti->class_size);
memcpy(ti->class, parent->class, parent->class_size);
+ ti->class->interfaces = NULL;
for (e = parent->class->interfaces; e; e = e->next) {
ObjectClass *iface = e->data;
@@ -448,7 +449,8 @@ ObjectClass *object_class_dynamic_cast(ObjectClass *class,
TypeImpl *type = class->type;
ObjectClass *ret = NULL;
- if (type->num_interfaces && type_is_ancestor(target_type, type_interface)) {
+ if (type->class->interfaces &&
+ type_is_ancestor(target_type, type_interface)) {
int found = 0;
GSList *i;
diff --git a/scripts/make_device_config.sh b/scripts/make_device_config.sh
index 0778fe2a42..81fe94259d 100644
--- a/scripts/make_device_config.sh
+++ b/scripts/make_device_config.sh
@@ -18,7 +18,7 @@ process_includes () {
f=$src
while [ -n "$f" ] ; do
- f=`tr -d '\r' < $f | awk '/^include / {printf "'$src_dir'/%s", $2}'`
+ f=`cat $f | tr -d '\r' | awk '/^include / {printf "'$src_dir'/%s ", $2}'`
[ $? = 0 ] || exit 1
all_includes="$all_includes $f"
done
diff --git a/slirp/libslirp.h b/slirp/libslirp.h
index 49609c2ad7..ceabff81b2 100644
--- a/slirp/libslirp.h
+++ b/slirp/libslirp.h
@@ -17,11 +17,9 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
void slirp_cleanup(Slirp *slirp);
void slirp_update_timeout(uint32_t *timeout);
-void slirp_select_fill(int *pnfds,
- fd_set *readfds, fd_set *writefds, fd_set *xfds);
+void slirp_pollfds_fill(GArray *pollfds);
-void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds,
- int select_error);
+void slirp_pollfds_poll(GArray *pollfds, int select_error);
void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len);
diff --git a/slirp/main.h b/slirp/main.h
index 66e4f9252f..f2e58cfe2d 100644
--- a/slirp/main.h
+++ b/slirp/main.h
@@ -31,7 +31,6 @@ extern int ctty_closed;
extern char *slirp_tty;
extern char *exec_shell;
extern u_int curtime;
-extern fd_set *global_readfds, *global_writefds, *global_xfds;
extern struct in_addr loopback_addr;
extern unsigned long loopback_mask;
extern char *username;
diff --git a/slirp/slirp.c b/slirp/slirp.c
index 0e6e232789..bd9b7cb644 100644
--- a/slirp/slirp.c
+++ b/slirp/slirp.c
@@ -39,9 +39,6 @@ static const uint8_t special_ethaddr[ETH_ALEN] = {
static const uint8_t zero_ethaddr[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
-/* XXX: suppress those select globals */
-fd_set *global_readfds, *global_writefds, *global_xfds;
-
u_int curtime;
static u_int time_fasttimo, last_slowtimo;
static int do_slowtimo;
@@ -261,7 +258,6 @@ void slirp_cleanup(Slirp *slirp)
#define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
#define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
-#define UPD_NFDS(x) if (nfds < (x)) nfds = (x)
void slirp_update_timeout(uint32_t *timeout)
{
@@ -270,156 +266,179 @@ void slirp_update_timeout(uint32_t *timeout)
}
}
-void slirp_select_fill(int *pnfds,
- fd_set *readfds, fd_set *writefds, fd_set *xfds)
+void slirp_pollfds_fill(GArray *pollfds)
{
Slirp *slirp;
struct socket *so, *so_next;
- int nfds;
if (QTAILQ_EMPTY(&slirp_instances)) {
return;
}
- /* fail safe */
- global_readfds = NULL;
- global_writefds = NULL;
- global_xfds = NULL;
-
- nfds = *pnfds;
- /*
- * First, TCP sockets
- */
- do_slowtimo = 0;
-
- QTAILQ_FOREACH(slirp, &slirp_instances, entry) {
- /*
- * *_slowtimo needs calling if there are IP fragments
- * in the fragment queue, or there are TCP connections active
- */
- do_slowtimo |= ((slirp->tcb.so_next != &slirp->tcb) ||
- (&slirp->ipq.ip_link != slirp->ipq.ip_link.next));
-
- for (so = slirp->tcb.so_next; so != &slirp->tcb;
- so = so_next) {
- so_next = so->so_next;
-
- /*
- * See if we need a tcp_fasttimo
- */
- if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK)
- time_fasttimo = curtime; /* Flag when we want a fasttimo */
-
- /*
- * NOFDREF can include still connecting to local-host,
- * newly socreated() sockets etc. Don't want to select these.
- */
- if (so->so_state & SS_NOFDREF || so->s == -1)
- continue;
-
- /*
- * Set for reading sockets which are accepting
- */
- if (so->so_state & SS_FACCEPTCONN) {
- FD_SET(so->s, readfds);
- UPD_NFDS(so->s);
- continue;
- }
-
- /*
- * Set for writing sockets which are connecting
- */
- if (so->so_state & SS_ISFCONNECTING) {
- FD_SET(so->s, writefds);
- UPD_NFDS(so->s);
- continue;
- }
-
- /*
- * Set for writing if we are connected, can send more, and
- * we have something to send
- */
- if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) {
- FD_SET(so->s, writefds);
- UPD_NFDS(so->s);
- }
-
- /*
- * Set for reading (and urgent data) if we are connected, can
- * receive more, and we have room for it XXX /2 ?
- */
- if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) {
- FD_SET(so->s, readfds);
- FD_SET(so->s, xfds);
- UPD_NFDS(so->s);
- }
- }
-
- /*
- * UDP sockets
- */
- for (so = slirp->udb.so_next; so != &slirp->udb;
- so = so_next) {
- so_next = so->so_next;
-
- /*
- * See if it's timed out
- */
- if (so->so_expire) {
- if (so->so_expire <= curtime) {
- udp_detach(so);
- continue;
- } else
- do_slowtimo = 1; /* Let socket expire */
- }
-
- /*
- * When UDP packets are received from over the
- * link, they're sendto()'d straight away, so
- * no need for setting for writing
- * Limit the number of packets queued by this session
- * to 4. Note that even though we try and limit this
- * to 4 packets, the session could have more queued
- * if the packets needed to be fragmented
- * (XXX <= 4 ?)
- */
- if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) {
- FD_SET(so->s, readfds);
- UPD_NFDS(so->s);
- }
- }
+ /*
+ * First, TCP sockets
+ */
+ do_slowtimo = 0;
- /*
- * ICMP sockets
- */
- for (so = slirp->icmp.so_next; so != &slirp->icmp;
- so = so_next) {
- so_next = so->so_next;
+ QTAILQ_FOREACH(slirp, &slirp_instances, entry) {
+ /*
+ * *_slowtimo needs calling if there are IP fragments
+ * in the fragment queue, or there are TCP connections active
+ */
+ do_slowtimo |= ((slirp->tcb.so_next != &slirp->tcb) ||
+ (&slirp->ipq.ip_link != slirp->ipq.ip_link.next));
+
+ for (so = slirp->tcb.so_next; so != &slirp->tcb;
+ so = so_next) {
+ int events = 0;
+
+ so_next = so->so_next;
+
+ so->pollfds_idx = -1;
+
+ /*
+ * See if we need a tcp_fasttimo
+ */
+ if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK) {
+ time_fasttimo = curtime; /* Flag when we want a fasttimo */
+ }
- /*
- * See if it's timed out
- */
- if (so->so_expire) {
- if (so->so_expire <= curtime) {
- icmp_detach(so);
- continue;
- } else {
- do_slowtimo = 1; /* Let socket expire */
- }
- }
+ /*
+ * NOFDREF can include still connecting to local-host,
+ * newly socreated() sockets etc. Don't want to select these.
+ */
+ if (so->so_state & SS_NOFDREF || so->s == -1) {
+ continue;
+ }
- if (so->so_state & SS_ISFCONNECTED) {
- FD_SET(so->s, readfds);
- UPD_NFDS(so->s);
- }
+ /*
+ * Set for reading sockets which are accepting
+ */
+ if (so->so_state & SS_FACCEPTCONN) {
+ GPollFD pfd = {
+ .fd = so->s,
+ .events = G_IO_IN | G_IO_HUP | G_IO_ERR,
+ };
+ so->pollfds_idx = pollfds->len;
+ g_array_append_val(pollfds, pfd);
+ continue;
+ }
+
+ /*
+ * Set for writing sockets which are connecting
+ */
+ if (so->so_state & SS_ISFCONNECTING) {
+ GPollFD pfd = {
+ .fd = so->s,
+ .events = G_IO_OUT | G_IO_ERR,
+ };
+ so->pollfds_idx = pollfds->len;
+ g_array_append_val(pollfds, pfd);
+ continue;
+ }
+
+ /*
+ * Set for writing if we are connected, can send more, and
+ * we have something to send
+ */
+ if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) {
+ events |= G_IO_OUT | G_IO_ERR;
+ }
+
+ /*
+ * Set for reading (and urgent data) if we are connected, can
+ * receive more, and we have room for it XXX /2 ?
+ */
+ if (CONN_CANFRCV(so) &&
+ (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) {
+ events |= G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI;
+ }
+
+ if (events) {
+ GPollFD pfd = {
+ .fd = so->s,
+ .events = events,
+ };
+ so->pollfds_idx = pollfds->len;
+ g_array_append_val(pollfds, pfd);
+ }
+ }
+
+ /*
+ * UDP sockets
+ */
+ for (so = slirp->udb.so_next; so != &slirp->udb;
+ so = so_next) {
+ so_next = so->so_next;
+
+ so->pollfds_idx = -1;
+
+ /*
+ * See if it's timed out
+ */
+ if (so->so_expire) {
+ if (so->so_expire <= curtime) {
+ udp_detach(so);
+ continue;
+ } else {
+ do_slowtimo = 1; /* Let socket expire */
+ }
+ }
+
+ /*
+ * When UDP packets are received from over the
+ * link, they're sendto()'d straight away, so
+ * no need for setting for writing
+ * Limit the number of packets queued by this session
+ * to 4. Note that even though we try and limit this
+ * to 4 packets, the session could have more queued
+ * if the packets needed to be fragmented
+ * (XXX <= 4 ?)
+ */
+ if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) {
+ GPollFD pfd = {
+ .fd = so->s,
+ .events = G_IO_IN | G_IO_HUP | G_IO_ERR,
+ };
+ so->pollfds_idx = pollfds->len;
+ g_array_append_val(pollfds, pfd);
+ }
+ }
+
+ /*
+ * ICMP sockets
+ */
+ for (so = slirp->icmp.so_next; so != &slirp->icmp;
+ so = so_next) {
+ so_next = so->so_next;
+
+ so->pollfds_idx = -1;
+
+ /*
+ * See if it's timed out
+ */
+ if (so->so_expire) {
+ if (so->so_expire <= curtime) {
+ icmp_detach(so);
+ continue;
+ } else {
+ do_slowtimo = 1; /* Let socket expire */
}
- }
+ }
- *pnfds = nfds;
+ if (so->so_state & SS_ISFCONNECTED) {
+ GPollFD pfd = {
+ .fd = so->s,
+ .events = G_IO_IN | G_IO_HUP | G_IO_ERR,
+ };
+ so->pollfds_idx = pollfds->len;
+ g_array_append_val(pollfds, pfd);
+ }
+ }
+ }
}
-void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds,
- int select_error)
+void slirp_pollfds_poll(GArray *pollfds, int select_error)
{
Slirp *slirp;
struct socket *so, *so_next;
@@ -429,184 +448,202 @@ void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds,
return;
}
- global_readfds = readfds;
- global_writefds = writefds;
- global_xfds = xfds;
-
curtime = qemu_get_clock_ms(rt_clock);
QTAILQ_FOREACH(slirp, &slirp_instances, entry) {
- /*
- * See if anything has timed out
- */
- if (time_fasttimo && ((curtime - time_fasttimo) >= 2)) {
- tcp_fasttimo(slirp);
- time_fasttimo = 0;
- }
- if (do_slowtimo && ((curtime - last_slowtimo) >= 499)) {
- ip_slowtimo(slirp);
- tcp_slowtimo(slirp);
- last_slowtimo = curtime;
- }
-
- /*
- * Check sockets
- */
- if (!select_error) {
- /*
- * Check TCP sockets
- */
- for (so = slirp->tcb.so_next; so != &slirp->tcb;
- so = so_next) {
- so_next = so->so_next;
-
- /*
- * FD_ISSET is meaningless on these sockets
- * (and they can crash the program)
- */
- if (so->so_state & SS_NOFDREF || so->s == -1)
- continue;
-
- /*
- * Check for URG data
- * This will soread as well, so no need to
- * test for readfds below if this succeeds
- */
- if (FD_ISSET(so->s, xfds))
- sorecvoob(so);
- /*
- * Check sockets for reading
- */
- else if (FD_ISSET(so->s, readfds)) {
- /*
- * Check for incoming connections
- */
- if (so->so_state & SS_FACCEPTCONN) {
- tcp_connect(so);
- continue;
- } /* else */
- ret = soread(so);
-
- /* Output it if we read something */
- if (ret > 0)
- tcp_output(sototcpcb(so));
- }
-
- /*
- * Check sockets for writing
- */
- if (FD_ISSET(so->s, writefds)) {
- /*
- * Check for non-blocking, still-connecting sockets
- */
- if (so->so_state & SS_ISFCONNECTING) {
- /* Connected */
- so->so_state &= ~SS_ISFCONNECTING;
-
- ret = send(so->s, (const void *) &ret, 0, 0);
- if (ret < 0) {
- /* XXXXX Must fix, zero bytes is a NOP */
- if (errno == EAGAIN || errno == EWOULDBLOCK ||
- errno == EINPROGRESS || errno == ENOTCONN)
- continue;
-
- /* else failed */
- so->so_state &= SS_PERSISTENT_MASK;
- so->so_state |= SS_NOFDREF;
- }
- /* else so->so_state &= ~SS_ISFCONNECTING; */
-
- /*
- * Continue tcp_input
- */
- tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
- /* continue; */
- } else
- ret = sowrite(so);
- /*
- * XXXXX If we wrote something (a lot), there
- * could be a need for a window update.
- * In the worst case, the remote will send
- * a window probe to get things going again
- */
- }
-
- /*
- * Probe a still-connecting, non-blocking socket
- * to check if it's still alive
- */
-#ifdef PROBE_CONN
- if (so->so_state & SS_ISFCONNECTING) {
- ret = qemu_recv(so->s, &ret, 0,0);
-
- if (ret < 0) {
- /* XXX */
- if (errno == EAGAIN || errno == EWOULDBLOCK ||
- errno == EINPROGRESS || errno == ENOTCONN)
- continue; /* Still connecting, continue */
-
- /* else failed */
- so->so_state &= SS_PERSISTENT_MASK;
- so->so_state |= SS_NOFDREF;
-
- /* tcp_input will take care of it */
- } else {
- ret = send(so->s, &ret, 0,0);
- if (ret < 0) {
- /* XXX */
- if (errno == EAGAIN || errno == EWOULDBLOCK ||
- errno == EINPROGRESS || errno == ENOTCONN)
- continue;
- /* else failed */
- so->so_state &= SS_PERSISTENT_MASK;
- so->so_state |= SS_NOFDREF;
- } else
- so->so_state &= ~SS_ISFCONNECTING;
-
- }
- tcp_input((struct mbuf *)NULL, sizeof(struct ip),so);
- } /* SS_ISFCONNECTING */
-#endif
- }
-
- /*
- * Now UDP sockets.
- * Incoming packets are sent straight away, they're not buffered.
- * Incoming UDP data isn't buffered either.
- */
- for (so = slirp->udb.so_next; so != &slirp->udb;
- so = so_next) {
- so_next = so->so_next;
-
- if (so->s != -1 && FD_ISSET(so->s, readfds)) {
- sorecvfrom(so);
+ /*
+ * See if anything has timed out
+ */
+ if (time_fasttimo && ((curtime - time_fasttimo) >= 2)) {
+ tcp_fasttimo(slirp);
+ time_fasttimo = 0;
+ }
+ if (do_slowtimo && ((curtime - last_slowtimo) >= 499)) {
+ ip_slowtimo(slirp);
+ tcp_slowtimo(slirp);
+ last_slowtimo = curtime;
+ }
+
+ /*
+ * Check sockets
+ */
+ if (!select_error) {
+ /*
+ * Check TCP sockets
+ */
+ for (so = slirp->tcb.so_next; so != &slirp->tcb;
+ so = so_next) {
+ int revents;
+
+ so_next = so->so_next;
+
+ revents = 0;
+ if (so->pollfds_idx != -1) {
+ revents = g_array_index(pollfds, GPollFD,
+ so->pollfds_idx).revents;
+ }
+
+ if (so->so_state & SS_NOFDREF || so->s == -1) {
+ continue;
+ }
+
+ /*
+ * Check for URG data
+ * This will soread as well, so no need to
+ * test for G_IO_IN below if this succeeds
+ */
+ if (revents & G_IO_PRI) {
+ sorecvoob(so);
+ }
+ /*
+ * Check sockets for reading
+ */
+ else if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) {
+ /*
+ * Check for incoming connections
+ */
+ if (so->so_state & SS_FACCEPTCONN) {
+ tcp_connect(so);
+ continue;
+ } /* else */
+ ret = soread(so);
+
+ /* Output it if we read something */
+ if (ret > 0) {
+ tcp_output(sototcpcb(so));
+ }
+ }
+
+ /*
+ * Check sockets for writing
+ */
+ if (!(so->so_state & SS_NOFDREF) &&
+ (revents & (G_IO_OUT | G_IO_ERR))) {
+ /*
+ * Check for non-blocking, still-connecting sockets
+ */
+ if (so->so_state & SS_ISFCONNECTING) {
+ /* Connected */
+ so->so_state &= ~SS_ISFCONNECTING;
+
+ ret = send(so->s, (const void *) &ret, 0, 0);
+ if (ret < 0) {
+ /* XXXXX Must fix, zero bytes is a NOP */
+ if (errno == EAGAIN || errno == EWOULDBLOCK ||
+ errno == EINPROGRESS || errno == ENOTCONN) {
+ continue;
+ }
+
+ /* else failed */
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_NOFDREF;
}
- }
+ /* else so->so_state &= ~SS_ISFCONNECTING; */
+
+ /*
+ * Continue tcp_input
+ */
+ tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
+ /* continue; */
+ } else {
+ ret = sowrite(so);
+ }
+ /*
+ * XXXXX If we wrote something (a lot), there
+ * could be a need for a window update.
+ * In the worst case, the remote will send
+ * a window probe to get things going again
+ */
+ }
/*
- * Check incoming ICMP relies.
+ * Probe a still-connecting, non-blocking socket
+ * to check if it's still alive
*/
- for (so = slirp->icmp.so_next; so != &slirp->icmp;
- so = so_next) {
- so_next = so->so_next;
+#ifdef PROBE_CONN
+ if (so->so_state & SS_ISFCONNECTING) {
+ ret = qemu_recv(so->s, &ret, 0, 0);
+
+ if (ret < 0) {
+ /* XXX */
+ if (errno == EAGAIN || errno == EWOULDBLOCK ||
+ errno == EINPROGRESS || errno == ENOTCONN) {
+ continue; /* Still connecting, continue */
+ }
+
+ /* else failed */
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_NOFDREF;
+
+ /* tcp_input will take care of it */
+ } else {
+ ret = send(so->s, &ret, 0, 0);
+ if (ret < 0) {
+ /* XXX */
+ if (errno == EAGAIN || errno == EWOULDBLOCK ||
+ errno == EINPROGRESS || errno == ENOTCONN) {
+ continue;
+ }
+ /* else failed */
+ so->so_state &= SS_PERSISTENT_MASK;
+ so->so_state |= SS_NOFDREF;
+ } else {
+ so->so_state &= ~SS_ISFCONNECTING;
+ }
- if (so->s != -1 && FD_ISSET(so->s, readfds)) {
- icmp_receive(so);
}
+ tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
+ } /* SS_ISFCONNECTING */
+#endif
+ }
+
+ /*
+ * Now UDP sockets.
+ * Incoming packets are sent straight away, they're not buffered.
+ * Incoming UDP data isn't buffered either.
+ */
+ for (so = slirp->udb.so_next; so != &slirp->udb;
+ so = so_next) {
+ int revents;
+
+ so_next = so->so_next;
+
+ revents = 0;
+ if (so->pollfds_idx != -1) {
+ revents = g_array_index(pollfds, GPollFD,
+ so->pollfds_idx).revents;
+ }
+
+ if (so->s != -1 &&
+ (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR))) {
+ sorecvfrom(so);
}
- }
+ }
+
+ /*
+ * Check incoming ICMP relies.
+ */
+ for (so = slirp->icmp.so_next; so != &slirp->icmp;
+ so = so_next) {
+ int revents;
+
+ so_next = so->so_next;
+
+ revents = 0;
+ if (so->pollfds_idx != -1) {
+ revents = g_array_index(pollfds, GPollFD,
+ so->pollfds_idx).revents;
+ }
+
+ if (so->s != -1 &&
+ (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR))) {
+ icmp_receive(so);
+ }
+ }
+ }
if_start(slirp);
}
-
- /* clear global file descriptor sets.
- * these reside on the stack in vl.c
- * so they're unusable if we're not in
- * slirp_select_fill or slirp_select_poll.
- */
- global_readfds = NULL;
- global_writefds = NULL;
- global_xfds = NULL;
}
static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
@@ -827,12 +864,12 @@ int slirp_add_exec(Slirp *slirp, int do_pty, const void *args,
ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags)
{
- if (so->s == -1 && so->extra) {
- qemu_chr_fe_write(so->extra, buf, len);
- return len;
- }
+ if (so->s == -1 && so->extra) {
+ qemu_chr_fe_write(so->extra, buf, len);
+ return len;
+ }
- return send(so->s, buf, len, flags);
+ return send(so->s, buf, len, flags);
}
static struct socket *
@@ -852,18 +889,20 @@ slirp_find_ctl_socket(Slirp *slirp, struct in_addr guest_addr, int guest_port)
size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
int guest_port)
{
- struct iovec iov[2];
- struct socket *so;
+ struct iovec iov[2];
+ struct socket *so;
- so = slirp_find_ctl_socket(slirp, guest_addr, guest_port);
+ so = slirp_find_ctl_socket(slirp, guest_addr, guest_port);
- if (!so || so->so_state & SS_NOFDREF)
- return 0;
+ if (!so || so->so_state & SS_NOFDREF) {
+ return 0;
+ }
- if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen/2))
- return 0;
+ if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen/2)) {
+ return 0;
+ }
- return sopreprbuf(so, iov, NULL);
+ return sopreprbuf(so, iov, NULL);
}
void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port,
diff --git a/slirp/socket.c b/slirp/socket.c
index 77b0c98197..a7ab933c43 100644
--- a/slirp/socket.c
+++ b/slirp/socket.c
@@ -680,9 +680,6 @@ sofcantrcvmore(struct socket *so)
{
if ((so->so_state & SS_NOFDREF) == 0) {
shutdown(so->s,0);
- if(global_writefds) {
- FD_CLR(so->s,global_writefds);
- }
}
so->so_state &= ~(SS_ISFCONNECTING);
if (so->so_state & SS_FCANTSENDMORE) {
@@ -698,12 +695,6 @@ sofcantsendmore(struct socket *so)
{
if ((so->so_state & SS_NOFDREF) == 0) {
shutdown(so->s,1); /* send FIN to fhost */
- if (global_readfds) {
- FD_CLR(so->s,global_readfds);
- }
- if (global_xfds) {
- FD_CLR(so->s,global_xfds);
- }
}
so->so_state &= ~(SS_ISFCONNECTING);
if (so->so_state & SS_FCANTRCVMORE) {
diff --git a/slirp/socket.h b/slirp/socket.h
index 857b0da311..57e0407ebc 100644
--- a/slirp/socket.h
+++ b/slirp/socket.h
@@ -20,6 +20,8 @@ struct socket {
int s; /* The actual socket */
+ int pollfds_idx; /* GPollFD GArray index */
+
Slirp *slirp; /* managing slirp instance */
/* XXX union these with not-yet-used sbuf params */
diff --git a/stubs/slirp.c b/stubs/slirp.c
index 9a3309a2b9..f1fc833f7a 100644
--- a/stubs/slirp.c
+++ b/stubs/slirp.c
@@ -5,13 +5,11 @@ void slirp_update_timeout(uint32_t *timeout)
{
}
-void slirp_select_fill(int *pnfds, fd_set *readfds,
- fd_set *writefds, fd_set *xfds)
+void slirp_pollfds_fill(GArray *pollfds)
{
}
-void slirp_select_poll(fd_set *readfds, fd_set *writefds,
- fd_set *xfds, int select_error)
+void slirp_pollfds_poll(GArray *pollfds, int select_error)
{
}
diff --git a/target-cris/translate.c b/target-cris/translate.c
index 04a5379775..2cf01a52e7 100644
--- a/target-cris/translate.c
+++ b/target-cris/translate.c
@@ -3215,8 +3215,6 @@ gen_intermediate_code_internal(CPUCRISState *env, TranslationBlock *tb,
int num_insns;
int max_insns;
- qemu_log_try_set_file(stderr);
-
if (env->pregs[PR_VR] == 32) {
dc->decoder = crisv32_decoder;
dc->clear_locked_irq = 0;
diff --git a/target-lm32/translate.c b/target-lm32/translate.c
index 6b87340174..ccaf838afa 100644
--- a/target-lm32/translate.c
+++ b/target-lm32/translate.c
@@ -1012,8 +1012,6 @@ static void gen_intermediate_code_internal(CPULM32State *env,
int num_insns;
int max_insns;
- qemu_log_try_set_file(stderr);
-
pc_start = tb->pc;
dc->env = env;
dc->tb = tb;
diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c
index 12ea820522..687b7d1433 100644
--- a/target-microblaze/translate.c
+++ b/target-microblaze/translate.c
@@ -1734,8 +1734,6 @@ gen_intermediate_code_internal(CPUMBState *env, TranslationBlock *tb,
int num_insns;
int max_insns;
- qemu_log_try_set_file(stderr);
-
pc_start = tb->pc;
dc->env = env;
dc->tb = tb;
diff --git a/target-openrisc/translate.c b/target-openrisc/translate.c
index 1e1b30cdcb..23e853e488 100644
--- a/target-openrisc/translate.c
+++ b/target-openrisc/translate.c
@@ -1670,8 +1670,6 @@ static inline void gen_intermediate_code_internal(OpenRISCCPU *cpu,
int num_insns;
int max_insns;
- qemu_log_try_set_file(stderr);
-
pc_start = tb->pc;
dc->tb = tb;
diff --git a/target-sparc/cpu.c b/target-sparc/cpu.c
index ef52df6d74..50def61848 100644
--- a/target-sparc/cpu.c
+++ b/target-sparc/cpu.c
@@ -580,13 +580,13 @@ static const sparc_def_t sparc_defs[] = {
.fpu_version = 4 << 17, /* FPU version 4 (Meiko) */
.mmu_version = 0xf3000000,
.mmu_bm = 0x00000000,
- .mmu_ctpr_mask = 0x007ffff0,
- .mmu_cxr_mask = 0x0000003f,
+ .mmu_ctpr_mask = 0xfffffffc,
+ .mmu_cxr_mask = 0x000000ff,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN |
- CPU_FEATURE_ASR17 | CPU_FEATURE_CACHE_CTRL,
+ CPU_FEATURE_ASR17 | CPU_FEATURE_CACHE_CTRL | CPU_FEATURE_POWERDOWN,
},
#endif
};
diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
index 7389b03514..a2f2cc8989 100644
--- a/target-sparc/cpu.h
+++ b/target-sparc/cpu.h
@@ -270,6 +270,7 @@ typedef struct sparc_def_t {
#define CPU_FEATURE_TA0_SHUTDOWN (1 << 14) /* Shutdown on "ta 0x0" */
#define CPU_FEATURE_ASR17 (1 << 15)
#define CPU_FEATURE_CACHE_CTRL (1 << 16)
+#define CPU_FEATURE_POWERDOWN (1 << 17)
#ifndef TARGET_SPARC64
#define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | \
diff --git a/target-sparc/helper.c b/target-sparc/helper.c
index 91ecfc7aa8..58e7efe567 100644
--- a/target-sparc/helper.c
+++ b/target-sparc/helper.c
@@ -225,3 +225,14 @@ target_ulong helper_tsubcctv(CPUSPARCState *env, target_ulong src1,
cpu_restore_state(env, GETPC());
helper_raise_exception(env, TT_TOVF);
}
+
+#ifndef TARGET_SPARC64
+void helper_power_down(CPUSPARCState *env)
+{
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
+ env->pc = env->npc;
+ env->npc = env->pc + 4;
+ cpu_loop_exit(env);
+}
+#endif
diff --git a/target-sparc/helper.h b/target-sparc/helper.h
index cfcdab1ea4..15f73283fa 100644
--- a/target-sparc/helper.h
+++ b/target-sparc/helper.h
@@ -4,6 +4,7 @@
DEF_HELPER_1(rett, void, env)
DEF_HELPER_2(wrpsr, void, env, tl)
DEF_HELPER_1(rdpsr, tl, env)
+DEF_HELPER_1(power_down, void, env)
#else
DEF_HELPER_2(wrpil, void, env, tl)
DEF_HELPER_2(wrpstate, void, env, tl)
diff --git a/target-sparc/ldst_helper.c b/target-sparc/ldst_helper.c
index 7decd66d0b..6d767fb45a 100644
--- a/target-sparc/ldst_helper.c
+++ b/target-sparc/ldst_helper.c
@@ -514,6 +514,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, int asi, int size,
#endif
break;
case 3: /* MMU probe */
+ case 0x18: /* LEON3 MMU probe */
{
int mmulev;
@@ -528,6 +529,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, int asi, int size,
}
break;
case 4: /* read MMU regs */
+ case 0x19: /* LEON3 read MMU regs */
{
int reg = (addr >> 8) & 0x1f;
@@ -603,6 +605,7 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, int asi, int size,
case 0xf: /* D-cache data */
break;
case 0x20: /* MMU passthrough */
+ case 0x1c: /* LEON MMU passthrough */
switch (size) {
case 1:
ret = ldub_phys(addr);
@@ -844,6 +847,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, int asi,
#endif
break;
case 3: /* MMU flush */
+ case 0x18: /* LEON3 MMU flush */
{
int mmulev;
@@ -868,6 +872,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, int asi,
}
break;
case 4: /* write MMU regs */
+ case 0x19: /* LEON3 write MMU regs */
{
int reg = (addr >> 8) & 0x1f;
uint32_t oldreg;
@@ -996,6 +1001,7 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, int asi,
}
break;
case 0x20: /* MMU passthrough */
+ case 0x1c: /* LEON MMU passthrough */
{
switch (size) {
case 1:
diff --git a/target-sparc/translate.c b/target-sparc/translate.c
index ca75e1aa48..26c2056b93 100644
--- a/target-sparc/translate.c
+++ b/target-sparc/translate.c
@@ -3642,6 +3642,11 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn)
in the SPARCv8
manual, nop on the
microSPARC II */
+ if ((rd == 0x13) && (dc->def->features &
+ CPU_FEATURE_POWERDOWN)) {
+ /* LEON3 power-down */
+ gen_helper_power_down(cpu_env);
+ }
break;
#else
case 0x2: /* V9 wrccr */
diff --git a/tests/.gitignore b/tests/.gitignore
index 38c94ef1da..fb05c2ae87 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -4,11 +4,18 @@ check-qint
check-qjson
check-qlist
check-qstring
+test-aio
+test-cutils
+test-hbitmap
+test-iov
+test-mul64
test-qapi-types.[ch]
test-qapi-visit.[ch]
test-qmp-commands.h
test-qmp-commands
test-qmp-input-strict
test-qmp-marshal.c
+test-thread-pool
test-x86-cpuid
+test-xbzrle
*-test
diff --git a/tests/rtc-test.c b/tests/rtc-test.c
index 203c0fc363..c5fd042610 100644
--- a/tests/rtc-test.c
+++ b/tests/rtc-test.c
@@ -26,11 +26,6 @@ static int bcd2dec(int value)
return (((value >> 4) & 0x0F) * 10) + (value & 0x0F);
}
-static int dec2bcd(int value)
-{
- return ((value / 10) << 4) | (value % 10);
-}
-
static uint8_t cmos_read(uint8_t reg)
{
outb(base + 0, reg);
@@ -184,7 +179,7 @@ static int wiggle = 2;
static void set_year_20xx(void)
{
/* Set BCD mode */
- cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & ~REG_B_DM);
+ cmos_write(RTC_REG_B, REG_B_24H);
cmos_write(RTC_REG_A, 0x76);
cmos_write(RTC_YEAR, 0x11);
cmos_write(RTC_CENTURY, 0x20);
@@ -236,7 +231,7 @@ static void set_year_20xx(void)
static void set_year_1980(void)
{
/* Set BCD mode */
- cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & ~REG_B_DM);
+ cmos_write(RTC_REG_B, REG_B_24H);
cmos_write(RTC_REG_A, 0x76);
cmos_write(RTC_YEAR, 0x80);
cmos_write(RTC_CENTURY, 0x19);
@@ -259,32 +254,17 @@ static void set_year_1980(void)
static void bcd_check_time(void)
{
/* Set BCD mode */
- cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & ~REG_B_DM);
+ cmos_write(RTC_REG_B, REG_B_24H);
check_time(wiggle);
}
static void dec_check_time(void)
{
/* Set DEC mode */
- cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_DM);
+ cmos_write(RTC_REG_B, REG_B_24H | REG_B_DM);
check_time(wiggle);
}
-static void set_alarm_time(struct tm *tm)
-{
- int sec;
-
- sec = tm->tm_sec;
-
- if ((cmos_read(RTC_REG_B) & REG_B_DM) == 0) {
- sec = dec2bcd(sec);
- }
-
- cmos_write(RTC_SECONDS_ALARM, sec);
- cmos_write(RTC_MINUTES_ALARM, RTC_ALARM_DONT_CARE);
- cmos_write(RTC_HOURS_ALARM, RTC_ALARM_DONT_CARE);
-}
-
static void alarm_time(void)
{
struct tm now;
@@ -295,13 +275,15 @@ static void alarm_time(void)
gmtime_r(&ts, &now);
/* set DEC mode */
- cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_DM);
+ cmos_write(RTC_REG_B, REG_B_24H | REG_B_DM);
g_assert(!get_irq(RTC_ISA_IRQ));
cmos_read(RTC_REG_C);
now.tm_sec = (now.tm_sec + 2) % 60;
- set_alarm_time(&now);
+ cmos_write(RTC_SECONDS_ALARM, now.tm_sec);
+ cmos_write(RTC_MINUTES_ALARM, RTC_ALARM_DONT_CARE);
+ cmos_write(RTC_HOURS_ALARM, RTC_ALARM_DONT_CARE);
cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) | REG_B_AIE);
for (i = 0; i < 2 + wiggle; i++) {
@@ -317,6 +299,197 @@ static void alarm_time(void)
g_assert(cmos_read(RTC_REG_C) == 0);
}
+static void set_time(int mode, int h, int m, int s)
+{
+ /* set BCD 12 hour mode */
+ cmos_write(RTC_REG_B, mode);
+
+ cmos_write(RTC_REG_A, 0x76);
+ cmos_write(RTC_HOURS, h);
+ cmos_write(RTC_MINUTES, m);
+ cmos_write(RTC_SECONDS, s);
+ cmos_write(RTC_REG_A, 0x26);
+}
+
+#define assert_time(h, m, s) \
+ do { \
+ g_assert_cmpint(cmos_read(RTC_HOURS), ==, h); \
+ g_assert_cmpint(cmos_read(RTC_MINUTES), ==, m); \
+ g_assert_cmpint(cmos_read(RTC_SECONDS), ==, s); \
+ } while(0)
+
+static void basic_12h_bcd(void)
+{
+ /* set BCD 12 hour mode */
+ set_time(0, 0x81, 0x59, 0x00);
+ clock_step(1000000000LL);
+ assert_time(0x81, 0x59, 0x01);
+ clock_step(59000000000LL);
+ assert_time(0x82, 0x00, 0x00);
+
+ /* test BCD wraparound */
+ set_time(0, 0x09, 0x59, 0x59);
+ clock_step(60000000000LL);
+ assert_time(0x10, 0x00, 0x59);
+
+ /* 12 AM -> 1 AM */
+ set_time(0, 0x12, 0x59, 0x59);
+ clock_step(1000000000LL);
+ assert_time(0x01, 0x00, 0x00);
+
+ /* 12 PM -> 1 PM */
+ set_time(0, 0x92, 0x59, 0x59);
+ clock_step(1000000000LL);
+ assert_time(0x81, 0x00, 0x00);
+
+ /* 11 AM -> 12 PM */
+ set_time(0, 0x11, 0x59, 0x59);
+ clock_step(1000000000LL);
+ assert_time(0x92, 0x00, 0x00);
+ /* TODO: test day wraparound */
+
+ /* 11 PM -> 12 AM */
+ set_time(0, 0x91, 0x59, 0x59);
+ clock_step(1000000000LL);
+ assert_time(0x12, 0x00, 0x00);
+ /* TODO: test day wraparound */
+}
+
+static void basic_12h_dec(void)
+{
+ /* set decimal 12 hour mode */
+ set_time(REG_B_DM, 0x81, 59, 0);
+ clock_step(1000000000LL);
+ assert_time(0x81, 59, 1);
+ clock_step(59000000000LL);
+ assert_time(0x82, 0, 0);
+
+ /* 12 PM -> 1 PM */
+ set_time(REG_B_DM, 0x8c, 59, 59);
+ clock_step(1000000000LL);
+ assert_time(0x81, 0, 0);
+
+ /* 12 AM -> 1 AM */
+ set_time(REG_B_DM, 0x0c, 59, 59);
+ clock_step(1000000000LL);
+ assert_time(0x01, 0, 0);
+
+ /* 11 AM -> 12 PM */
+ set_time(REG_B_DM, 0x0b, 59, 59);
+ clock_step(1000000000LL);
+ assert_time(0x8c, 0, 0);
+
+ /* 11 PM -> 12 AM */
+ set_time(REG_B_DM, 0x8b, 59, 59);
+ clock_step(1000000000LL);
+ assert_time(0x0c, 0, 0);
+ /* TODO: test day wraparound */
+}
+
+static void basic_24h_bcd(void)
+{
+ /* set BCD 24 hour mode */
+ set_time(REG_B_24H, 0x09, 0x59, 0x00);
+ clock_step(1000000000LL);
+ assert_time(0x09, 0x59, 0x01);
+ clock_step(59000000000LL);
+ assert_time(0x10, 0x00, 0x00);
+
+ /* test BCD wraparound */
+ set_time(REG_B_24H, 0x09, 0x59, 0x00);
+ clock_step(60000000000LL);
+ assert_time(0x10, 0x00, 0x00);
+
+ /* TODO: test day wraparound */
+ set_time(REG_B_24H, 0x23, 0x59, 0x00);
+ clock_step(60000000000LL);
+ assert_time(0x00, 0x00, 0x00);
+}
+
+static void basic_24h_dec(void)
+{
+ /* set decimal 24 hour mode */
+ set_time(REG_B_24H | REG_B_DM, 9, 59, 0);
+ clock_step(1000000000LL);
+ assert_time(9, 59, 1);
+ clock_step(59000000000LL);
+ assert_time(10, 0, 0);
+
+ /* test BCD wraparound */
+ set_time(REG_B_24H | REG_B_DM, 9, 59, 0);
+ clock_step(60000000000LL);
+ assert_time(10, 0, 0);
+
+ /* TODO: test day wraparound */
+ set_time(REG_B_24H | REG_B_DM, 23, 59, 0);
+ clock_step(60000000000LL);
+ assert_time(0, 0, 0);
+}
+
+static void am_pm_alarm(void)
+{
+ cmos_write(RTC_MINUTES_ALARM, 0xC0);
+ cmos_write(RTC_SECONDS_ALARM, 0xC0);
+
+ /* set BCD 12 hour mode */
+ cmos_write(RTC_REG_B, 0);
+
+ /* Set time and alarm hour. */
+ cmos_write(RTC_REG_A, 0x76);
+ cmos_write(RTC_HOURS_ALARM, 0x82);
+ cmos_write(RTC_HOURS, 0x81);
+ cmos_write(RTC_MINUTES, 0x59);
+ cmos_write(RTC_SECONDS, 0x00);
+ cmos_read(RTC_REG_C);
+ cmos_write(RTC_REG_A, 0x26);
+
+ /* Check that alarm triggers when AM/PM is set. */
+ clock_step(60000000000LL);
+ g_assert(cmos_read(RTC_HOURS) == 0x82);
+ g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0);
+
+ /*
+ * Each of the following two tests takes over 60 seconds due to the time
+ * needed to report the PIT interrupts. Unfortunately, our PIT device
+ * model keeps counting even when GATE=0, so we cannot simply disable
+ * it in main().
+ */
+ if (g_test_quick()) {
+ return;
+ }
+
+ /* set DEC 12 hour mode */
+ cmos_write(RTC_REG_B, REG_B_DM);
+
+ /* Set time and alarm hour. */
+ cmos_write(RTC_REG_A, 0x76);
+ cmos_write(RTC_HOURS_ALARM, 0x82);
+ cmos_write(RTC_HOURS, 3);
+ cmos_write(RTC_MINUTES, 0);
+ cmos_write(RTC_SECONDS, 0);
+ cmos_read(RTC_REG_C);
+ cmos_write(RTC_REG_A, 0x26);
+
+ /* Check that alarm triggers. */
+ clock_step(3600 * 11 * 1000000000LL);
+ g_assert(cmos_read(RTC_HOURS) == 0x82);
+ g_assert((cmos_read(RTC_REG_C) & REG_C_AF) != 0);
+
+ /* Same as above, with inverted HOURS and HOURS_ALARM. */
+ cmos_write(RTC_REG_A, 0x76);
+ cmos_write(RTC_HOURS_ALARM, 2);
+ cmos_write(RTC_HOURS, 3);
+ cmos_write(RTC_MINUTES, 0);
+ cmos_write(RTC_SECONDS, 0);
+ cmos_read(RTC_REG_C);
+ cmos_write(RTC_REG_A, 0x26);
+
+ /* Check that alarm does not trigger if hours differ only by AM/PM. */
+ clock_step(3600 * 11 * 1000000000LL);
+ g_assert(cmos_read(RTC_HOURS) == 0x82);
+ g_assert((cmos_read(RTC_REG_C) & REG_C_AF) == 0);
+}
+
/* success if no crash or abort */
static void fuzz_registers(void)
{
@@ -336,7 +509,7 @@ static void fuzz_registers(void)
static void register_b_set_flag(void)
{
/* Enable binary-coded decimal (BCD) mode and SET flag in Register B*/
- cmos_write(RTC_REG_B, (cmos_read(RTC_REG_B) & ~REG_B_DM) | REG_B_SET);
+ cmos_write(RTC_REG_B, REG_B_24H | REG_B_SET);
cmos_write(RTC_REG_A, 0x76);
cmos_write(RTC_YEAR, 0x11);
@@ -382,9 +555,14 @@ int main(int argc, char **argv)
s = qtest_start("-display none -rtc clock=vm");
qtest_irq_intercept_in(s, "ioapic");
- qtest_add_func("/rtc/bcd/check-time", bcd_check_time);
- qtest_add_func("/rtc/dec/check-time", dec_check_time);
- qtest_add_func("/rtc/alarm-time", alarm_time);
+ qtest_add_func("/rtc/check-time/bcd", bcd_check_time);
+ qtest_add_func("/rtc/check-time/dec", dec_check_time);
+ qtest_add_func("/rtc/alarm/interrupt", alarm_time);
+ qtest_add_func("/rtc/alarm/am-pm", am_pm_alarm);
+ qtest_add_func("/rtc/basic/dec-24h", basic_24h_dec);
+ qtest_add_func("/rtc/basic/bcd-24h", basic_24h_bcd);
+ qtest_add_func("/rtc/basic/dec-12h", basic_12h_dec);
+ qtest_add_func("/rtc/basic/bcd-12h", basic_12h_bcd);
qtest_add_func("/rtc/set-year/20xx", set_year_20xx);
qtest_add_func("/rtc/set-year/1980", set_year_1980);
qtest_add_func("/rtc/register_b_set_flag", register_b_set_flag);
diff --git a/trace-events b/trace-events
index 1011f27676..a27ae43032 100644
--- a/trace-events
+++ b/trace-events
@@ -370,11 +370,11 @@ usb_xhci_slot_evaluate(uint32_t slotid) "slotid %d"
usb_xhci_slot_reset(uint32_t slotid) "slotid %d"
usb_xhci_ep_enable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
usb_xhci_ep_disable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
-usb_xhci_ep_set_dequeue(uint32_t slotid, uint32_t epid, uint64_t param) "slotid %d, epid %d, ptr %016" PRIx64
-usb_xhci_ep_kick(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
+usb_xhci_ep_set_dequeue(uint32_t slotid, uint32_t epid, uint32_t streamid, uint64_t param) "slotid %d, epid %d, streamid %d, ptr %016" PRIx64
+usb_xhci_ep_kick(uint32_t slotid, uint32_t epid, uint32_t streamid) "slotid %d, epid %d, streamid %d"
usb_xhci_ep_stop(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
usb_xhci_ep_reset(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
-usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid) "%p: slotid %d, epid %d"
+usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid, uint32_t streamid) "%p: slotid %d, epid %d, streamid %d"
usb_xhci_xfer_async(void *xfer) "%p"
usb_xhci_xfer_nak(void *xfer) "%p"
usb_xhci_xfer_retry(void *xfer) "%p"
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index d9db073584..85c50cd89b 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -13,7 +13,10 @@ common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
common-obj-$(CONFIG_COCOA) += cocoa.o
common-obj-$(CONFIG_CURSES) += curses.o
common-obj-$(CONFIG_VNC) += $(vnc-obj-y)
+common-obj-$(CONFIG_GTK) += gtk.o
$(obj)/sdl.o $(obj)/sdl_zoom.o: QEMU_CFLAGS += $(SDL_CFLAGS)
$(obj)/cocoa.o: $(SRC_PATH)/$(obj)/cocoa.m
+
+$(obj)/gtk.o: QEMU_CFLAGS += $(GTK_CFLAGS) $(VTE_CFLAGS)
diff --git a/ui/console.c b/ui/console.c
index 0a68836d50..0d95f32123 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -1339,11 +1339,16 @@ DisplaySurface *qemu_resize_displaysurface(DisplayState *ds,
}
DisplaySurface *qemu_create_displaysurface_from(int width, int height, int bpp,
- int linesize, uint8_t *data)
+ int linesize, uint8_t *data,
+ bool byteswap)
{
DisplaySurface *surface = g_new0(DisplaySurface, 1);
- surface->pf = qemu_default_pixelformat(bpp);
+ if (byteswap) {
+ surface->pf = qemu_different_endianness_pixelformat(bpp);
+ } else {
+ surface->pf = qemu_default_pixelformat(bpp);
+ }
surface->format = qemu_pixman_get_format(&surface->pf);
assert(surface->format != 0);
@@ -1532,7 +1537,7 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
chr->init(chr);
}
-CharDriverState *text_console_init(QemuOpts *opts)
+static CharDriverState *text_console_init(QemuOpts *opts)
{
CharDriverState *chr;
QemuConsole *s;
@@ -1568,6 +1573,18 @@ CharDriverState *text_console_init(QemuOpts *opts)
return chr;
}
+static VcHandler *vc_handler = text_console_init;
+
+CharDriverState *vc_init(QemuOpts *opts)
+{
+ return vc_handler(opts);
+}
+
+void register_vc_handler(VcHandler *handler)
+{
+ vc_handler = handler;
+}
+
void text_consoles_set_display(DisplayState *ds)
{
int i;
diff --git a/ui/gtk.c b/ui/gtk.c
new file mode 100644
index 0000000000..dcce36d243
--- /dev/null
+++ b/ui/gtk.c
@@ -0,0 +1,1217 @@
+/*
+ * GTK UI
+ *
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ * Portions from gtk-vnc:
+ *
+ * GTK VNC Widget
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.0 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define GETTEXT_PACKAGE "qemu"
+#define LOCALEDIR "po"
+
+#include "qemu-common.h"
+
+#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
+/* Work around an -Wstrict-prototypes warning in GTK headers */
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#endif
+#include <gtk/gtk.h>
+#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
+#pragma GCC diagnostic error "-Wstrict-prototypes"
+#endif
+
+
+#include <gdk/gdkkeysyms.h>
+#include <glib/gi18n.h>
+#include <locale.h>
+#include <vte/vte.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <pty.h>
+#include <math.h>
+
+#include "ui/console.h"
+#include "sysemu/sysemu.h"
+#include "qmp-commands.h"
+#include "x_keymap.h"
+#include "keymaps.h"
+#include "char/char.h"
+
+//#define DEBUG_GTK
+
+#ifdef DEBUG_GTK
+#define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...) do { } while (0)
+#endif
+
+#define MAX_VCS 10
+
+typedef struct VirtualConsole
+{
+ GtkWidget *menu_item;
+ GtkWidget *terminal;
+ GtkWidget *scrolled_window;
+ CharDriverState *chr;
+ int fd;
+} VirtualConsole;
+
+typedef struct GtkDisplayState
+{
+ GtkWidget *window;
+
+ GtkWidget *menu_bar;
+
+ GtkAccelGroup *accel_group;
+
+ GtkWidget *machine_menu_item;
+ GtkWidget *machine_menu;
+ GtkWidget *pause_item;
+ GtkWidget *reset_item;
+ GtkWidget *powerdown_item;
+ GtkWidget *quit_item;
+
+ GtkWidget *view_menu_item;
+ GtkWidget *view_menu;
+ GtkWidget *full_screen_item;
+ GtkWidget *zoom_in_item;
+ GtkWidget *zoom_out_item;
+ GtkWidget *zoom_fixed_item;
+ GtkWidget *zoom_fit_item;
+ GtkWidget *grab_item;
+ GtkWidget *grab_on_hover_item;
+ GtkWidget *vga_item;
+
+ int nb_vcs;
+ VirtualConsole vc[MAX_VCS];
+
+ GtkWidget *show_tabs_item;
+
+ GtkWidget *vbox;
+ GtkWidget *notebook;
+ GtkWidget *drawing_area;
+ cairo_surface_t *surface;
+ DisplayChangeListener dcl;
+ DisplayState *ds;
+ int button_mask;
+ int last_x;
+ int last_y;
+
+ double scale_x;
+ double scale_y;
+ gboolean full_screen;
+
+ GdkCursor *null_cursor;
+ Notifier mouse_mode_notifier;
+ gboolean free_scale;
+
+ bool external_pause_update;
+} GtkDisplayState;
+
+static GtkDisplayState *global_state;
+
+/** Utility Functions **/
+
+static bool gd_is_grab_active(GtkDisplayState *s)
+{
+ return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
+}
+
+static bool gd_grab_on_hover(GtkDisplayState *s)
+{
+ return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
+}
+
+static bool gd_on_vga(GtkDisplayState *s)
+{
+ return gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook)) == 0;
+}
+
+static void gd_update_cursor(GtkDisplayState *s, gboolean override)
+{
+ GdkWindow *window;
+ bool on_vga;
+
+ window = gtk_widget_get_window(GTK_WIDGET(s->drawing_area));
+
+ on_vga = gd_on_vga(s);
+
+ if ((override || on_vga) &&
+ (s->full_screen || kbd_mouse_is_absolute() || gd_is_grab_active(s))) {
+ gdk_window_set_cursor(window, s->null_cursor);
+ } else {
+ gdk_window_set_cursor(window, NULL);
+ }
+}
+
+static void gd_update_caption(GtkDisplayState *s)
+{
+ const char *status = "";
+ gchar *title;
+ const char *grab = "";
+ bool is_paused = !runstate_is_running();
+
+ if (gd_is_grab_active(s)) {
+ grab = " - Press Ctrl+Alt+G to release grab";
+ }
+
+ if (is_paused) {
+ status = " [Paused]";
+ }
+ s->external_pause_update = true;
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item),
+ is_paused);
+ s->external_pause_update = false;
+
+ if (qemu_name) {
+ title = g_strdup_printf("QEMU (%s)%s%s", qemu_name, status, grab);
+ } else {
+ title = g_strdup_printf("QEMU%s%s", status, grab);
+ }
+
+ gtk_window_set_title(GTK_WINDOW(s->window), title);
+
+ g_free(title);
+}
+
+/** DisplayState Callbacks **/
+
+static void gd_update(DisplayState *ds, int x, int y, int w, int h)
+{
+ GtkDisplayState *s = ds->opaque;
+ int x1, x2, y1, y2;
+ int mx, my;
+ int fbw, fbh;
+ int ww, wh;
+
+ DPRINTF("update(x=%d, y=%d, w=%d, h=%d)\n", x, y, w, h);
+
+ x1 = floor(x * s->scale_x);
+ y1 = floor(y * s->scale_y);
+
+ x2 = ceil(x * s->scale_x + w * s->scale_x);
+ y2 = ceil(y * s->scale_y + h * s->scale_y);
+
+ fbw = ds_get_width(s->ds) * s->scale_x;
+ fbh = ds_get_height(s->ds) * s->scale_y;
+
+ gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
+
+ mx = my = 0;
+ if (ww > fbw) {
+ mx = (ww - fbw) / 2;
+ }
+ if (wh > fbh) {
+ my = (wh - fbh) / 2;
+ }
+
+ gtk_widget_queue_draw_area(s->drawing_area, mx + x1, my + y1, (x2 - x1), (y2 - y1));
+}
+
+static void gd_refresh(DisplayState *ds)
+{
+ vga_hw_update();
+}
+
+static void gd_resize(DisplayState *ds)
+{
+ GtkDisplayState *s = ds->opaque;
+ cairo_format_t kind;
+ int stride;
+
+ DPRINTF("resize(width=%d, height=%d)\n",
+ ds_get_width(ds), ds_get_height(ds));
+
+ if (s->surface) {
+ cairo_surface_destroy(s->surface);
+ }
+
+ switch (ds->surface->pf.bits_per_pixel) {
+ case 8:
+ kind = CAIRO_FORMAT_A8;
+ break;
+ case 16:
+ kind = CAIRO_FORMAT_RGB16_565;
+ break;
+ case 32:
+ kind = CAIRO_FORMAT_RGB24;
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+
+ stride = cairo_format_stride_for_width(kind, ds_get_width(ds));
+ g_assert(ds_get_linesize(ds) == stride);
+
+ s->surface = cairo_image_surface_create_for_data(ds_get_data(ds),
+ kind,
+ ds_get_width(ds),
+ ds_get_height(ds),
+ ds_get_linesize(ds));
+
+ if (!s->full_screen) {
+ GtkRequisition req;
+ double sx, sy;
+
+ if (s->free_scale) {
+ sx = s->scale_x;
+ sy = s->scale_y;
+
+ s->scale_y = 1.0;
+ s->scale_x = 1.0;
+ } else {
+ sx = 1.0;
+ sy = 1.0;
+ }
+
+ gtk_widget_set_size_request(s->drawing_area,
+ ds_get_width(ds) * s->scale_x,
+ ds_get_height(ds) * s->scale_y);
+ gtk_widget_size_request(s->vbox, &req);
+
+ gtk_window_resize(GTK_WINDOW(s->window),
+ req.width * sx, req.height * sy);
+ }
+}
+
+/** QEMU Events **/
+
+static void gd_change_runstate(void *opaque, int running, RunState state)
+{
+ GtkDisplayState *s = opaque;
+
+ gd_update_caption(s);
+}
+
+static void gd_mouse_mode_change(Notifier *notify, void *data)
+{
+ gd_update_cursor(container_of(notify, GtkDisplayState, mouse_mode_notifier),
+ FALSE);
+}
+
+/** GTK Events **/
+
+static gboolean gd_window_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
+{
+ GtkDisplayState *s = opaque;
+ GtkAccelGroupEntry *entries;
+ guint n_entries = 0;
+ gboolean propagate_accel = TRUE;
+ gboolean handled = FALSE;
+
+ entries = gtk_accel_group_query(s->accel_group, key->keyval,
+ key->state, &n_entries);
+ if (n_entries) {
+ const char *quark = g_quark_to_string(entries[0].accel_path_quark);
+
+ if (gd_is_grab_active(s) && strstart(quark, "<QEMU>/File/", NULL)) {
+ propagate_accel = FALSE;
+ }
+ }
+
+ if (!handled && propagate_accel) {
+ handled = gtk_window_activate_key(GTK_WINDOW(widget), key);
+ }
+
+ if (!handled) {
+ handled = gtk_window_propagate_key_event(GTK_WINDOW(widget), key);
+ }
+
+ return handled;
+}
+
+static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
+ void *opaque)
+{
+ GtkDisplayState *s = opaque;
+
+ if (!no_quit) {
+ unregister_displaychangelistener(s->ds, &s->dcl);
+ qmp_quit(NULL);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
+{
+ GtkDisplayState *s = opaque;
+ int mx, my;
+ int ww, wh;
+ int fbw, fbh;
+
+ if (!gtk_widget_get_realized(widget)) {
+ return FALSE;
+ }
+
+ fbw = ds_get_width(s->ds);
+ fbh = ds_get_height(s->ds);
+
+ gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
+
+ if (s->full_screen) {
+ s->scale_x = (double)ww / fbw;
+ s->scale_y = (double)wh / fbh;
+ } else if (s->free_scale) {
+ double sx, sy;
+
+ sx = (double)ww / fbw;
+ sy = (double)wh / fbh;
+
+ s->scale_x = s->scale_y = MIN(sx, sy);
+ }
+
+ fbw *= s->scale_x;
+ fbh *= s->scale_y;
+
+ mx = my = 0;
+ if (ww > fbw) {
+ mx = (ww - fbw) / 2;
+ }
+ if (wh > fbh) {
+ my = (wh - fbh) / 2;
+ }
+
+ cairo_rectangle(cr, 0, 0, ww, wh);
+
+ /* Optionally cut out the inner area where the pixmap
+ will be drawn. This avoids 'flashing' since we're
+ not double-buffering. Note we're using the undocumented
+ behaviour of drawing the rectangle from right to left
+ to cut out the whole */
+ cairo_rectangle(cr, mx + fbw, my,
+ -1 * fbw, fbh);
+ cairo_fill(cr);
+
+ cairo_scale(cr, s->scale_x, s->scale_y);
+ cairo_set_source_surface(cr, s->surface, mx / s->scale_x, my / s->scale_y);
+ cairo_paint(cr);
+
+ return TRUE;
+}
+
+static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose,
+ void *opaque)
+{
+ cairo_t *cr;
+ gboolean ret;
+
+ cr = gdk_cairo_create(gtk_widget_get_window(widget));
+ cairo_rectangle(cr,
+ expose->area.x,
+ expose->area.y,
+ expose->area.width,
+ expose->area.height);
+ cairo_clip(cr);
+
+ ret = gd_draw_event(widget, cr, opaque);
+
+ cairo_destroy(cr);
+
+ return ret;
+}
+
+static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
+ void *opaque)
+{
+ GtkDisplayState *s = opaque;
+ int dx, dy;
+ int x, y;
+ int mx, my;
+ int fbh, fbw;
+ int ww, wh;
+
+ fbw = ds_get_width(s->ds) * s->scale_x;
+ fbh = ds_get_height(s->ds) * s->scale_y;
+
+ gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
+
+ mx = my = 0;
+ if (ww > fbw) {
+ mx = (ww - fbw) / 2;
+ }
+ if (wh > fbh) {
+ my = (wh - fbh) / 2;
+ }
+
+ x = (motion->x - mx) / s->scale_x;
+ y = (motion->y - my) / s->scale_y;
+
+ if (x < 0 || y < 0 ||
+ x >= ds_get_width(s->ds) ||
+ y >= ds_get_height(s->ds)) {
+ return TRUE;
+ }
+
+ if (kbd_mouse_is_absolute()) {
+ dx = x * 0x7FFF / (ds_get_width(s->ds) - 1);
+ dy = y * 0x7FFF / (ds_get_height(s->ds) - 1);
+ } else if (s->last_x == -1 || s->last_y == -1) {
+ dx = 0;
+ dy = 0;
+ } else {
+ dx = x - s->last_x;
+ dy = y - s->last_y;
+ }
+
+ s->last_x = x;
+ s->last_y = y;
+
+ if (kbd_mouse_is_absolute() || gd_is_grab_active(s)) {
+ kbd_mouse_event(dx, dy, 0, s->button_mask);
+ }
+
+ if (!kbd_mouse_is_absolute() && gd_is_grab_active(s)) {
+ GdkDrawable *drawable = GDK_DRAWABLE(gtk_widget_get_window(s->drawing_area));
+ GdkDisplay *display = gdk_drawable_get_display(drawable);
+ GdkScreen *screen = gdk_drawable_get_screen(drawable);
+ int x = (int)motion->x_root;
+ int y = (int)motion->y_root;
+
+ /* In relative mode check to see if client pointer hit
+ * one of the screen edges, and if so move it back by
+ * 200 pixels. This is important because the pointer
+ * in the server doesn't correspond 1-for-1, and so
+ * may still be only half way across the screen. Without
+ * this warp, the server pointer would thus appear to hit
+ * an invisible wall */
+ if (x == 0) {
+ x += 200;
+ }
+ if (y == 0) {
+ y += 200;
+ }
+ if (x == (gdk_screen_get_width(screen) - 1)) {
+ x -= 200;
+ }
+ if (y == (gdk_screen_get_height(screen) - 1)) {
+ y -= 200;
+ }
+
+ if (x != (int)motion->x_root || y != (int)motion->y_root) {
+ gdk_display_warp_pointer(display, screen, x, y);
+ s->last_x = -1;
+ s->last_y = -1;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
+ void *opaque)
+{
+ GtkDisplayState *s = opaque;
+ int dx, dy;
+ int n;
+
+ if (button->button == 1) {
+ n = 0x01;
+ } else if (button->button == 2) {
+ n = 0x04;
+ } else if (button->button == 3) {
+ n = 0x02;
+ } else {
+ n = 0x00;
+ }
+
+ if (button->type == GDK_BUTTON_PRESS) {
+ s->button_mask |= n;
+ } else if (button->type == GDK_BUTTON_RELEASE) {
+ s->button_mask &= ~n;
+ }
+
+ if (kbd_mouse_is_absolute()) {
+ dx = s->last_x * 0x7FFF / (ds_get_width(s->ds) - 1);
+ dy = s->last_y * 0x7FFF / (ds_get_height(s->ds) - 1);
+ } else {
+ dx = 0;
+ dy = 0;
+ }
+
+ kbd_mouse_event(dx, dy, 0, s->button_mask);
+
+ return TRUE;
+}
+
+static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
+{
+ int gdk_keycode;
+ int qemu_keycode;
+
+ gdk_keycode = key->hardware_keycode;
+
+ if (gdk_keycode < 9) {
+ qemu_keycode = 0;
+ } else if (gdk_keycode < 97) {
+ qemu_keycode = gdk_keycode - 8;
+ } else if (gdk_keycode < 158) {
+ qemu_keycode = translate_evdev_keycode(gdk_keycode - 97);
+ } else if (gdk_keycode == 208) { /* Hiragana_Katakana */
+ qemu_keycode = 0x70;
+ } else if (gdk_keycode == 211) { /* backslash */
+ qemu_keycode = 0x73;
+ } else {
+ qemu_keycode = 0;
+ }
+
+ DPRINTF("translated GDK keycode %d to QEMU keycode %d (%s)\n",
+ gdk_keycode, qemu_keycode,
+ (key->type == GDK_KEY_PRESS) ? "down" : "up");
+
+ if (qemu_keycode & SCANCODE_GREY) {
+ kbd_put_keycode(SCANCODE_EMUL0);
+ }
+
+ if (key->type == GDK_KEY_PRESS) {
+ kbd_put_keycode(qemu_keycode & SCANCODE_KEYCODEMASK);
+ } else if (key->type == GDK_KEY_RELEASE) {
+ kbd_put_keycode(qemu_keycode | SCANCODE_UP);
+ } else {
+ g_assert_not_reached();
+ }
+
+ return TRUE;
+}
+
+/** Window Menu Actions **/
+
+static void gd_menu_pause(GtkMenuItem *item, void *opaque)
+{
+ GtkDisplayState *s = opaque;
+
+ if (s->external_pause_update) {
+ return;
+ }
+ if (runstate_is_running()) {
+ qmp_stop(NULL);
+ } else {
+ qmp_cont(NULL);
+ }
+}
+
+static void gd_menu_reset(GtkMenuItem *item, void *opaque)
+{
+ qmp_system_reset(NULL);
+}
+
+static void gd_menu_powerdown(GtkMenuItem *item, void *opaque)
+{
+ qmp_system_powerdown(NULL);
+}
+
+static void gd_menu_quit(GtkMenuItem *item, void *opaque)
+{
+ qmp_quit(NULL);
+}
+
+static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
+{
+ GtkDisplayState *s = opaque;
+
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vga_item))) {
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), 0);
+ } else {
+ int i;
+
+ for (i = 0; i < s->nb_vcs; i++) {
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->vc[i].menu_item))) {
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(s->notebook), i + 1);
+ break;
+ }
+ }
+ }
+}
+
+static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
+{
+ GtkDisplayState *s = opaque;
+
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
+ } else {
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
+ }
+}
+
+static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
+{
+ GtkDisplayState *s = opaque;
+
+ if (!s->full_screen) {
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
+ gtk_widget_set_size_request(s->menu_bar, 0, 0);
+ gtk_widget_set_size_request(s->drawing_area, -1, -1);
+ gtk_window_fullscreen(GTK_WINDOW(s->window));
+ if (gd_on_vga(s)) {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), TRUE);
+ }
+ s->full_screen = TRUE;
+ } else {
+ gtk_window_unfullscreen(GTK_WINDOW(s->window));
+ gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
+ gtk_widget_set_size_request(s->menu_bar, -1, -1);
+ gtk_widget_set_size_request(s->drawing_area,
+ ds_get_width(s->ds), ds_get_height(s->ds));
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item), FALSE);
+ s->full_screen = FALSE;
+ s->scale_x = 1.0;
+ s->scale_y = 1.0;
+ }
+
+ gd_update_cursor(s, FALSE);
+}
+
+static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
+{
+ GtkDisplayState *s = opaque;
+
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
+ FALSE);
+
+ s->scale_x += .25;
+ s->scale_y += .25;
+
+ gd_resize(s->ds);
+}
+
+static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
+{
+ GtkDisplayState *s = opaque;
+
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
+ FALSE);
+
+ s->scale_x -= .25;
+ s->scale_y -= .25;
+
+ s->scale_x = MAX(s->scale_x, .25);
+ s->scale_y = MAX(s->scale_y, .25);
+
+ gd_resize(s->ds);
+}
+
+static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
+{
+ GtkDisplayState *s = opaque;
+
+ s->scale_x = 1.0;
+ s->scale_y = 1.0;
+
+ gd_resize(s->ds);
+}
+
+static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
+{
+ GtkDisplayState *s = opaque;
+ int ww, wh;
+
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
+ s->free_scale = TRUE;
+ } else {
+ s->free_scale = FALSE;
+ }
+
+ gd_resize(s->ds);
+
+ gdk_drawable_get_size(gtk_widget_get_window(s->drawing_area), &ww, &wh);
+ gtk_widget_queue_draw_area(s->drawing_area, 0, 0, ww, wh);
+}
+
+static void gd_grab_keyboard(GtkDisplayState *s)
+{
+ gdk_keyboard_grab(gtk_widget_get_window(GTK_WIDGET(s->drawing_area)),
+ FALSE,
+ GDK_CURRENT_TIME);
+}
+
+static void gd_ungrab_keyboard(GtkDisplayState *s)
+{
+ gdk_keyboard_ungrab(GDK_CURRENT_TIME);
+}
+
+static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
+{
+ GtkDisplayState *s = opaque;
+
+ if (gd_is_grab_active(s)) {
+ gd_grab_keyboard(s);
+ gdk_pointer_grab(gtk_widget_get_window(GTK_WIDGET(s->drawing_area)),
+ FALSE, /* All events to come to our window directly */
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_MOTION_MASK |
+ GDK_SCROLL_MASK,
+ NULL, /* Allow cursor to move over entire desktop */
+ s->null_cursor,
+ GDK_CURRENT_TIME);
+ } else {
+ gd_ungrab_keyboard(s);
+ gdk_pointer_ungrab(GDK_CURRENT_TIME);
+ }
+
+ gd_update_caption(s);
+ gd_update_cursor(s, FALSE);
+}
+
+static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
+ gpointer data)
+{
+ GtkDisplayState *s = data;
+ guint last_page;
+ gboolean on_vga;
+
+ if (!gtk_widget_get_realized(s->notebook)) {
+ return;
+ }
+
+ last_page = gtk_notebook_get_current_page(nb);
+
+ if (last_page) {
+ gtk_widget_set_size_request(s->vc[last_page - 1].terminal, -1, -1);
+ }
+
+ on_vga = arg2 == 0;
+
+ if (!on_vga) {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
+ FALSE);
+ } else if (s->full_screen) {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
+ TRUE);
+ }
+
+ if (arg2 == 0) {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->vga_item), TRUE);
+ } else {
+ VirtualConsole *vc = &s->vc[arg2 - 1];
+ VteTerminal *term = VTE_TERMINAL(vc->terminal);
+ int width, height;
+
+ width = 80 * vte_terminal_get_char_width(term);
+ height = 25 * vte_terminal_get_char_height(term);
+
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
+ gtk_widget_set_size_request(vc->terminal, width, height);
+ }
+
+ gtk_widget_set_sensitive(s->grab_item, on_vga);
+
+ gd_update_cursor(s, TRUE);
+}
+
+static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data)
+{
+ GtkDisplayState *s = data;
+
+ if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) {
+ gd_grab_keyboard(s);
+ }
+
+ return TRUE;
+}
+
+static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, gpointer data)
+{
+ GtkDisplayState *s = data;
+
+ if (!gd_is_grab_active(s) && gd_grab_on_hover(s)) {
+ gd_ungrab_keyboard(s);
+ }
+
+ return TRUE;
+}
+
+/** Virtual Console Callbacks **/
+
+static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+ VirtualConsole *vc = chr->opaque;
+
+ return write(vc->fd, buf, len);
+}
+
+static int nb_vcs;
+static CharDriverState *vcs[MAX_VCS];
+
+static CharDriverState *gd_vc_handler(QemuOpts *opts)
+{
+ CharDriverState *chr;
+
+ chr = g_malloc0(sizeof(*chr));
+ chr->chr_write = gd_vc_chr_write;
+
+ vcs[nb_vcs++] = chr;
+
+ return chr;
+}
+
+void early_gtk_display_init(void)
+{
+ register_vc_handler(gd_vc_handler);
+}
+
+static gboolean gd_vc_in(GIOChannel *chan, GIOCondition cond, void *opaque)
+{
+ VirtualConsole *vc = opaque;
+ uint8_t buffer[1024];
+ ssize_t len;
+
+ len = read(vc->fd, buffer, sizeof(buffer));
+ if (len <= 0) {
+ return FALSE;
+ }
+
+ qemu_chr_be_write(vc->chr, buffer, len);
+
+ return TRUE;
+}
+
+static GSList *gd_vc_init(GtkDisplayState *s, VirtualConsole *vc, int index, GSList *group)
+{
+ const char *label;
+ char buffer[32];
+ char path[32];
+#if VTE_CHECK_VERSION(0, 26, 0)
+ VtePty *pty;
+#endif
+ GIOChannel *chan;
+ GtkWidget *scrolled_window;
+ GtkAdjustment *vadjustment;
+ int master_fd, slave_fd, ret;
+ struct termios tty;
+
+ snprintf(buffer, sizeof(buffer), "vc%d", index);
+ snprintf(path, sizeof(path), "<QEMU>/View/VC%d", index);
+
+ vc->chr = vcs[index];
+
+ if (vc->chr->label) {
+ label = vc->chr->label;
+ } else {
+ label = buffer;
+ }
+
+ vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, label);
+ group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
+ gtk_menu_item_set_accel_path(GTK_MENU_ITEM(vc->menu_item), path);
+ gtk_accel_map_add_entry(path, GDK_KEY_2 + index, GDK_CONTROL_MASK | GDK_MOD1_MASK);
+
+ vc->terminal = vte_terminal_new();
+
+ ret = openpty(&master_fd, &slave_fd, NULL, NULL, NULL);
+ g_assert(ret != -1);
+
+ /* Set raw attributes on the pty. */
+ tcgetattr(slave_fd, &tty);
+ cfmakeraw(&tty);
+ tcsetattr(slave_fd, TCSAFLUSH, &tty);
+
+#if VTE_CHECK_VERSION(0, 26, 0)
+ pty = vte_pty_new_foreign(master_fd, NULL);
+ vte_terminal_set_pty_object(VTE_TERMINAL(vc->terminal), pty);
+#else
+ vte_terminal_set_pty(VTE_TERMINAL(vc->terminal), master_fd);
+#endif
+
+ vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->terminal), -1);
+
+ vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->terminal));
+
+ scrolled_window = gtk_scrolled_window_new(NULL, vadjustment);
+ gtk_container_add(GTK_CONTAINER(scrolled_window), vc->terminal);
+
+ vte_terminal_set_size(VTE_TERMINAL(vc->terminal), 80, 25);
+
+ vc->fd = slave_fd;
+ vc->chr->opaque = vc;
+ vc->scrolled_window = scrolled_window;
+
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vc->scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), scrolled_window, gtk_label_new(label));
+ g_signal_connect(vc->menu_item, "activate",
+ G_CALLBACK(gd_menu_switch_vc), s);
+
+ gtk_menu_append(GTK_MENU(s->view_menu), vc->menu_item);
+
+ qemu_chr_generic_open(vc->chr);
+ if (vc->chr->init) {
+ vc->chr->init(vc->chr);
+ }
+
+ chan = g_io_channel_unix_new(vc->fd);
+ g_io_add_watch(chan, G_IO_IN, gd_vc_in, vc);
+
+ return group;
+}
+
+/** Window Creation **/
+
+static void gd_connect_signals(GtkDisplayState *s)
+{
+ g_signal_connect(s->show_tabs_item, "activate",
+ G_CALLBACK(gd_menu_show_tabs), s);
+
+ g_signal_connect(s->window, "key-press-event",
+ G_CALLBACK(gd_window_key_event), s);
+ g_signal_connect(s->window, "delete-event",
+ G_CALLBACK(gd_window_close), s);
+
+ g_signal_connect(s->drawing_area, "expose-event",
+ G_CALLBACK(gd_expose_event), s);
+ g_signal_connect(s->drawing_area, "motion-notify-event",
+ G_CALLBACK(gd_motion_event), s);
+ g_signal_connect(s->drawing_area, "button-press-event",
+ G_CALLBACK(gd_button_event), s);
+ g_signal_connect(s->drawing_area, "button-release-event",
+ G_CALLBACK(gd_button_event), s);
+ g_signal_connect(s->drawing_area, "key-press-event",
+ G_CALLBACK(gd_key_event), s);
+ g_signal_connect(s->drawing_area, "key-release-event",
+ G_CALLBACK(gd_key_event), s);
+
+ g_signal_connect(s->pause_item, "activate",
+ G_CALLBACK(gd_menu_pause), s);
+ g_signal_connect(s->reset_item, "activate",
+ G_CALLBACK(gd_menu_reset), s);
+ g_signal_connect(s->powerdown_item, "activate",
+ G_CALLBACK(gd_menu_powerdown), s);
+ g_signal_connect(s->quit_item, "activate",
+ G_CALLBACK(gd_menu_quit), s);
+ g_signal_connect(s->full_screen_item, "activate",
+ G_CALLBACK(gd_menu_full_screen), s);
+ g_signal_connect(s->zoom_in_item, "activate",
+ G_CALLBACK(gd_menu_zoom_in), s);
+ g_signal_connect(s->zoom_out_item, "activate",
+ G_CALLBACK(gd_menu_zoom_out), s);
+ g_signal_connect(s->zoom_fixed_item, "activate",
+ G_CALLBACK(gd_menu_zoom_fixed), s);
+ g_signal_connect(s->zoom_fit_item, "activate",
+ G_CALLBACK(gd_menu_zoom_fit), s);
+ g_signal_connect(s->vga_item, "activate",
+ G_CALLBACK(gd_menu_switch_vc), s);
+ g_signal_connect(s->grab_item, "activate",
+ G_CALLBACK(gd_menu_grab_input), s);
+ g_signal_connect(s->notebook, "switch-page",
+ G_CALLBACK(gd_change_page), s);
+ g_signal_connect(s->drawing_area, "enter-notify-event",
+ G_CALLBACK(gd_enter_event), s);
+ g_signal_connect(s->drawing_area, "leave-notify-event",
+ G_CALLBACK(gd_leave_event), s);
+}
+
+static void gd_create_menus(GtkDisplayState *s)
+{
+ GtkStockItem item;
+ GtkAccelGroup *accel_group;
+ GSList *group = NULL;
+ GtkWidget *separator;
+ int i;
+
+ accel_group = gtk_accel_group_new();
+ s->machine_menu = gtk_menu_new();
+ gtk_menu_set_accel_group(GTK_MENU(s->machine_menu), accel_group);
+ s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
+
+ s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
+ gtk_menu_append(GTK_MENU(s->machine_menu), s->pause_item);
+
+ separator = gtk_separator_menu_item_new();
+ gtk_menu_append(GTK_MENU(s->machine_menu), separator);
+
+ s->reset_item = gtk_image_menu_item_new_with_mnemonic(_("_Reset"));
+ gtk_menu_append(GTK_MENU(s->machine_menu), s->reset_item);
+
+ s->powerdown_item = gtk_image_menu_item_new_with_mnemonic(_("Power _Down"));
+ gtk_menu_append(GTK_MENU(s->machine_menu), s->powerdown_item);
+
+ separator = gtk_separator_menu_item_new();
+ gtk_menu_append(GTK_MENU(s->machine_menu), separator);
+
+ s->quit_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
+ gtk_stock_lookup(GTK_STOCK_QUIT, &item);
+ gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
+ "<QEMU>/Machine/Quit");
+ gtk_accel_map_add_entry("<QEMU>/Machine/Quit", item.keyval, item.modifier);
+ gtk_menu_append(GTK_MENU(s->machine_menu), s->quit_item);
+
+ s->view_menu = gtk_menu_new();
+ gtk_menu_set_accel_group(GTK_MENU(s->view_menu), accel_group);
+ s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
+
+ s->full_screen_item =
+ gtk_image_menu_item_new_from_stock(GTK_STOCK_FULLSCREEN, NULL);
+ gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->full_screen_item),
+ "<QEMU>/View/Full Screen");
+ gtk_accel_map_add_entry("<QEMU>/View/Full Screen", GDK_KEY_f, GDK_CONTROL_MASK | GDK_MOD1_MASK);
+ gtk_menu_append(GTK_MENU(s->view_menu), s->full_screen_item);
+
+ separator = gtk_separator_menu_item_new();
+ gtk_menu_append(GTK_MENU(s->view_menu), separator);
+
+ s->zoom_in_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_IN, NULL);
+ gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
+ "<QEMU>/View/Zoom In");
+ gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus, GDK_CONTROL_MASK | GDK_MOD1_MASK);
+ gtk_menu_append(GTK_MENU(s->view_menu), s->zoom_in_item);
+
+ s->zoom_out_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_OUT, NULL);
+ gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
+ "<QEMU>/View/Zoom Out");
+ gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus, GDK_CONTROL_MASK | GDK_MOD1_MASK);
+ gtk_menu_append(GTK_MENU(s->view_menu), s->zoom_out_item);
+
+ s->zoom_fixed_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ZOOM_100, NULL);
+ gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
+ "<QEMU>/View/Zoom Fixed");
+ gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0, GDK_CONTROL_MASK | GDK_MOD1_MASK);
+ gtk_menu_append(GTK_MENU(s->view_menu), s->zoom_fixed_item);
+
+ s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
+ gtk_menu_append(GTK_MENU(s->view_menu), s->zoom_fit_item);
+
+ separator = gtk_separator_menu_item_new();
+ gtk_menu_append(GTK_MENU(s->view_menu), separator);
+
+ s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
+ gtk_menu_append(GTK_MENU(s->view_menu), s->grab_on_hover_item);
+
+ s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
+ gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
+ "<QEMU>/View/Grab Input");
+ gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g, GDK_CONTROL_MASK | GDK_MOD1_MASK);
+ gtk_menu_append(GTK_MENU(s->view_menu), s->grab_item);
+
+ separator = gtk_separator_menu_item_new();
+ gtk_menu_append(GTK_MENU(s->view_menu), separator);
+
+ s->vga_item = gtk_radio_menu_item_new_with_mnemonic(group, "_VGA");
+ group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(s->vga_item));
+ gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->vga_item),
+ "<QEMU>/View/VGA");
+ gtk_accel_map_add_entry("<QEMU>/View/VGA", GDK_KEY_1, GDK_CONTROL_MASK | GDK_MOD1_MASK);
+ gtk_menu_append(GTK_MENU(s->view_menu), s->vga_item);
+
+ for (i = 0; i < nb_vcs; i++) {
+ VirtualConsole *vc = &s->vc[i];
+
+ group = gd_vc_init(s, vc, i, group);
+ s->nb_vcs++;
+ }
+
+ separator = gtk_separator_menu_item_new();
+ gtk_menu_append(GTK_MENU(s->view_menu), separator);
+
+ s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
+ gtk_menu_append(GTK_MENU(s->view_menu), s->show_tabs_item);
+
+ g_object_set_data(G_OBJECT(s->window), "accel_group", accel_group);
+ gtk_window_add_accel_group(GTK_WINDOW(s->window), accel_group);
+ s->accel_group = accel_group;
+
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
+ s->machine_menu);
+ gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item);
+
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
+ gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
+}
+
+void gtk_display_init(DisplayState *ds)
+{
+ GtkDisplayState *s = g_malloc0(sizeof(*s));
+
+ gtk_init(NULL, NULL);
+
+ ds->opaque = s;
+ s->ds = ds;
+ s->dcl.dpy_gfx_update = gd_update;
+ s->dcl.dpy_gfx_resize = gd_resize;
+ s->dcl.dpy_refresh = gd_refresh;
+
+ s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ s->vbox = gtk_vbox_new(FALSE, 0);
+ s->notebook = gtk_notebook_new();
+ s->drawing_area = gtk_drawing_area_new();
+ s->menu_bar = gtk_menu_bar_new();
+
+ s->scale_x = 1.0;
+ s->scale_y = 1.0;
+ s->free_scale = FALSE;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
+ textdomain("qemu");
+
+ s->null_cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
+
+ s->mouse_mode_notifier.notify = gd_mouse_mode_change;
+ qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
+ qemu_add_vm_change_state_handler(gd_change_runstate, s);
+
+ gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), s->drawing_area, gtk_label_new("VGA"));
+
+ gd_create_menus(s);
+
+ gd_connect_signals(s);
+
+ gtk_widget_add_events(s->drawing_area,
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_MOTION_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK |
+ GDK_SCROLL_MASK |
+ GDK_KEY_PRESS_MASK);
+ gtk_widget_set_double_buffered(s->drawing_area, FALSE);
+ gtk_widget_set_can_focus(s->drawing_area, TRUE);
+
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
+ gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
+
+ gd_update_caption(s);
+
+ gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
+
+ gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
+
+ gtk_widget_show_all(s->window);
+
+ register_displaychangelistener(ds, &s->dcl);
+
+ global_state = s;
+}
diff --git a/user-exec.c b/user-exec.c
index c71acbc503..71bd6c531c 100644
--- a/user-exec.c
+++ b/user-exec.c
@@ -70,7 +70,7 @@ void cpu_resume_from_signal(CPUArchState *env1, void *puc)
#endif
}
env1->exception_index = -1;
- longjmp(env1->jmp_env, 1);
+ siglongjmp(env1->jmp_env, 1);
}
/* 'pc' is the host PC at which the exception was raised. 'address' is
diff --git a/vl.c b/vl.c
index c5b0eea29b..0da156e8b4 100644
--- a/vl.c
+++ b/vl.c
@@ -1427,8 +1427,9 @@ static int usb_device_del(const char *devname)
int bus_num, addr;
const char *p;
- if (strstart(devname, "host:", &p))
- return usb_host_device_close(p);
+ if (strstart(devname, "host:", &p)) {
+ return -1;
+ }
if (!usb_enabled(false)) {
return -1;
@@ -2206,6 +2207,13 @@ static DisplayType select_display(const char *p)
fprintf(stderr, "Curses support is disabled\n");
exit(1);
#endif
+ } else if (strstart(p, "gtk", &opts)) {
+#ifdef CONFIG_GTK
+ display = DT_GTK;
+#else
+ fprintf(stderr, "GTK support is disabled\n");
+ exit(1);
+#endif
} else if (strstart(p, "none", &opts)) {
display = DT_NONE;
} else {
@@ -3998,6 +4006,28 @@ int main(int argc, char **argv, char **envp)
}
}
+ if (using_spice) {
+ display_remote++;
+ }
+ if (display_type == DT_DEFAULT && !display_remote) {
+#if defined(CONFIG_GTK)
+ display_type = DT_GTK;
+#elif defined(CONFIG_SDL) || defined(CONFIG_COCOA)
+ display_type = DT_SDL;
+#elif defined(CONFIG_VNC)
+ vnc_display = "localhost:0,to=99";
+ show_vnc_port = 1;
+#else
+ display_type = DT_NONE;
+#endif
+ }
+
+#if defined(CONFIG_GTK)
+ if (display_type == DT_GTK) {
+ early_gtk_display_init();
+ }
+#endif
+
socket_init();
if (qemu_opts_foreach(qemu_find_opts("chardev"), chardev_init_func, NULL, 1) != 0)
@@ -4226,20 +4256,6 @@ int main(int argc, char **argv, char **envp)
/* just use the first displaystate for the moment */
ds = get_displaystate();
- if (using_spice)
- display_remote++;
- if (display_type == DT_DEFAULT && !display_remote) {
-#if defined(CONFIG_SDL) || defined(CONFIG_COCOA)
- display_type = DT_SDL;
-#elif defined(CONFIG_VNC)
- vnc_display = "localhost:0,to=99";
- show_vnc_port = 1;
-#else
- display_type = DT_NONE;
-#endif
- }
-
-
/* init local displays */
switch (display_type) {
case DT_NOGRAPHIC:
@@ -4258,6 +4274,11 @@ int main(int argc, char **argv, char **envp)
cocoa_display_init(ds, full_screen);
break;
#endif
+#if defined(CONFIG_GTK)
+ case DT_GTK:
+ gtk_display_init(ds);
+ break;
+#endif
default:
break;
}