Merge remote-tracking branch 'origin/master' into xbox

This commit is contained in:
espes 2012-11-19 12:29:57 +11:00
commit 17d4dd207a
127 changed files with 5493 additions and 3992 deletions

View File

@ -122,7 +122,7 @@ subdir-pixman: pixman/Makefile
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C pixman V="$(V)" all,) $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C pixman V="$(V)" all,)
pixman/Makefile: $(SRC_PATH)/pixman/configure pixman/Makefile: $(SRC_PATH)/pixman/configure
(cd pixman; $(SRC_PATH)/pixman/configure --disable-shared --enable-static) (cd pixman; CFLAGS="$(CFLAGS) -fPIC" $(SRC_PATH)/pixman/configure $(AUTOCONF_HOST) --disable-gtk --disable-shared --enable-static)
$(SRC_PATH)/pixman/configure: $(SRC_PATH)/pixman/configure:
(cd $(SRC_PATH)/pixman; autoreconf -v --install) (cd $(SRC_PATH)/pixman; autoreconf -v --install)
@ -157,6 +157,12 @@ version.o: $(SRC_PATH)/version.rc config-host.h
$(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@") $(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@")
version-obj-$(CONFIG_WIN32) += version.o version-obj-$(CONFIG_WIN32) += version.o
######################################################################
# Build library with stubs
libqemustub.a: $(stub-obj-y)
###################################################################### ######################################################################
# Support building shared library libcacard # Support building shared library libcacard
@ -183,13 +189,13 @@ tools-obj-y = $(oslib-obj-y) $(trace-obj-y) qemu-tool.o qemu-timer.o \
main-loop.o iohandler.o error.o main-loop.o iohandler.o error.o
tools-obj-$(CONFIG_POSIX) += compatfd.o tools-obj-$(CONFIG_POSIX) += compatfd.o
qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y) qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y) libqemustub.a
qemu-nbd$(EXESUF): qemu-nbd.o $(tools-obj-y) $(block-obj-y) qemu-nbd$(EXESUF): qemu-nbd.o $(tools-obj-y) $(block-obj-y) libqemustub.a
qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) $(block-obj-y) qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) $(block-obj-y) libqemustub.a
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o
vscclient$(EXESUF): $(libcacard-y) $(oslib-obj-y) $(trace-obj-y) libcacard/vscclient.o vscclient$(EXESUF): $(libcacard-y) $(oslib-obj-y) $(trace-obj-y) libcacard/vscclient.o libqemustub.a
$(call quiet-command,$(CC) $(LDFLAGS) -o $@ $^ $(libcacard_libs) $(LIBS)," LINK $@") $(call quiet-command,$(CC) $(LDFLAGS) -o $@ $^ $(libcacard_libs) $(LIBS)," LINK $@")
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/virtio-9p-marshal.o oslib-posix.o $(trace-obj-y) fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/virtio-9p-marshal.o oslib-posix.o $(trace-obj-y)
@ -232,7 +238,7 @@ $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h) QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h)
$(qga-obj-y) qemu-ga.o: $(QGALIB_GEN) $(qga-obj-y) qemu-ga.o: $(QGALIB_GEN)
qemu-ga$(EXESUF): qemu-ga.o $(qga-obj-y) $(oslib-obj-y) $(trace-obj-y) $(qapi-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-ga$(EXESUF): qemu-ga.o $(qga-obj-y) $(oslib-obj-y) $(trace-obj-y) $(qapi-obj-y) $(qobject-obj-y) $(version-obj-y) libqemustub.a
QEMULIBS=libuser libdis libdis-user QEMULIBS=libuser libdis libdis-user
@ -278,6 +284,7 @@ distclean: clean
for d in $(TARGET_DIRS) $(QEMULIBS); do \ for d in $(TARGET_DIRS) $(QEMULIBS); do \
rm -rf $$d || exit 1 ; \ rm -rf $$d || exit 1 ; \
done done
test -f pixman/config.log && make -C pixman distclean
KEYMAPS=da en-gb et fr fr-ch is lt modifiers no pt-br sv \ KEYMAPS=da en-gb et fr fr-ch is lt modifiers no pt-br sv \
ar de en-us fi fr-be hr it lv nl pl ru th \ ar de en-us fi fr-be hr it lv nl pl ru th \

View File

@ -1,3 +1,7 @@
#######################################################################
# Stub library, linked in tools
stub-obj-y = stubs/
####################################################################### #######################################################################
# Target-independent parts used in system and user emulation # Target-independent parts used in system and user emulation
universal-obj-y = universal-obj-y =
@ -78,7 +82,6 @@ common-obj-y += input.o
common-obj-y += buffered_file.o migration.o migration-tcp.o common-obj-y += buffered_file.o migration.o migration-tcp.o
common-obj-y += qemu-char.o #aio.o common-obj-y += qemu-char.o #aio.o
common-obj-y += block-migration.o iohandler.o common-obj-y += block-migration.o iohandler.o
common-obj-y += pflib.o
common-obj-y += bitmap.o bitops.o common-obj-y += bitmap.o bitops.o
common-obj-y += page_cache.o common-obj-y += page_cache.o
@ -101,6 +104,8 @@ common-obj-y += vl.o
common-obj-$(CONFIG_SLIRP) += slirp/ common-obj-$(CONFIG_SLIRP) += slirp/
common-obj-y += backends/
###################################################################### ######################################################################
# libseccomp # libseccomp
ifeq ($(CONFIG_SECCOMP),y) ifeq ($(CONFIG_SECCOMP),y)
@ -238,6 +243,7 @@ vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
QEMU_CFLAGS+=$(GLIB_CFLAGS) QEMU_CFLAGS+=$(GLIB_CFLAGS)
nested-vars += \ nested-vars += \
stub-obj-y \
qga-obj-y \ qga-obj-y \
qom-obj-y \ qom-obj-y \
qapi-obj-y \ qapi-obj-y \

View File

@ -162,12 +162,12 @@ endif #CONFIG_LINUX_USER
ifdef QEMU_PROGW ifdef QEMU_PROGW
# The linker builds a windows executable. Make also a console executable. # The linker builds a windows executable. Make also a console executable.
$(QEMU_PROGW): $(all-obj-y) $(QEMU_PROGW): $(all-obj-y) ../libqemustub.a
$(call LINK,$^) $(call LINK,$^)
$(QEMU_PROG): $(QEMU_PROGW) $(QEMU_PROG): $(QEMU_PROGW)
$(call quiet-command,$(OBJCOPY) --subsystem console $(QEMU_PROGW) $(QEMU_PROG)," GEN $(TARGET_DIR)$(QEMU_PROG)") $(call quiet-command,$(OBJCOPY) --subsystem console $(QEMU_PROGW) $(QEMU_PROG)," GEN $(TARGET_DIR)$(QEMU_PROG)")
else else
$(QEMU_PROG): $(all-obj-y) $(QEMU_PROG): $(all-obj-y) ../libqemustub.a
$(call LINK,$^) $(call LINK,$^)
endif endif

View File

@ -122,11 +122,9 @@ aio_ctx_prepare(GSource *source, gint *timeout)
{ {
AioContext *ctx = (AioContext *) source; AioContext *ctx = (AioContext *) source;
QEMUBH *bh; QEMUBH *bh;
bool scheduled = false;
for (bh = ctx->first_bh; bh; bh = bh->next) { for (bh = ctx->first_bh; bh; bh = bh->next) {
if (!bh->deleted && bh->scheduled) { if (!bh->deleted && bh->scheduled) {
scheduled = true;
if (bh->idle) { if (bh->idle) {
/* idle bottom halves will be polled at least /* idle bottom halves will be polled at least
* every 10ms */ * every 10ms */
@ -135,12 +133,12 @@ aio_ctx_prepare(GSource *source, gint *timeout)
/* non-idle bottom halves will be executed /* non-idle bottom halves will be executed
* immediately */ * immediately */
*timeout = 0; *timeout = 0;
break; return true;
} }
} }
} }
return scheduled; return false;
} }
static gboolean static gboolean

1
backends/Makefile.objs Normal file
View File

@ -0,0 +1 @@
common-obj-y += rng.o rng-random.o rng-egd.o

224
backends/rng-egd.c Normal file
View File

@ -0,0 +1,224 @@
/*
* QEMU Random Number Generator Backend
*
* 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.
*/
#include "qemu/rng.h"
#include "qemu-char.h"
#include "qerror.h"
#include "hw/qdev.h" /* just for DEFINE_PROP_CHR */
#define TYPE_RNG_EGD "rng-egd"
#define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD)
typedef struct RngEgd
{
RngBackend parent;
CharDriverState *chr;
char *chr_name;
GSList *requests;
} RngEgd;
typedef struct RngRequest
{
EntropyReceiveFunc *receive_entropy;
uint8_t *data;
void *opaque;
size_t offset;
size_t size;
} RngRequest;
static void rng_egd_request_entropy(RngBackend *b, size_t size,
EntropyReceiveFunc *receive_entropy,
void *opaque)
{
RngEgd *s = RNG_EGD(b);
RngRequest *req;
req = g_malloc(sizeof(*req));
req->offset = 0;
req->size = size;
req->receive_entropy = receive_entropy;
req->opaque = opaque;
req->data = g_malloc(req->size);
while (size > 0) {
uint8_t header[2];
uint8_t len = MIN(size, 255);
/* synchronous entropy request */
header[0] = 0x02;
header[1] = len;
qemu_chr_fe_write(s->chr, header, sizeof(header));
size -= len;
}
s->requests = g_slist_append(s->requests, req);
}
static void rng_egd_free_request(RngRequest *req)
{
g_free(req->data);
g_free(req);
}
static int rng_egd_chr_can_read(void *opaque)
{
RngEgd *s = RNG_EGD(opaque);
GSList *i;
int size = 0;
for (i = s->requests; i; i = i->next) {
RngRequest *req = i->data;
size += req->size - req->offset;
}
return size;
}
static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size)
{
RngEgd *s = RNG_EGD(opaque);
while (size > 0 && s->requests) {
RngRequest *req = s->requests->data;
int len = MIN(size, req->size - req->offset);
memcpy(req->data + req->offset, buf, len);
req->offset += len;
size -= len;
if (req->offset == req->size) {
s->requests = g_slist_remove_link(s->requests, s->requests);
req->receive_entropy(req->opaque, req->data, req->size);
rng_egd_free_request(req);
}
}
}
static void rng_egd_free_requests(RngEgd *s)
{
GSList *i;
for (i = s->requests; i; i = i->next) {
rng_egd_free_request(i->data);
}
g_slist_free(s->requests);
s->requests = NULL;
}
static void rng_egd_cancel_requests(RngBackend *b)
{
RngEgd *s = RNG_EGD(b);
/* We simply delete the list of pending requests. If there is data in the
* queue waiting to be read, this is okay, because there will always be
* more data than we requested originally
*/
rng_egd_free_requests(s);
}
static void rng_egd_opened(RngBackend *b, Error **errp)
{
RngEgd *s = RNG_EGD(b);
if (s->chr_name == NULL) {
error_set(errp, QERR_INVALID_PARAMETER_VALUE,
"chardev", "a valid character device");
return;
}
s->chr = qemu_chr_find(s->chr_name);
if (s->chr == NULL) {
error_set(errp, QERR_DEVICE_NOT_FOUND, s->chr_name);
return;
}
/* FIXME we should resubmit pending requests when the CDS reconnects. */
qemu_chr_add_handlers(s->chr, rng_egd_chr_can_read, rng_egd_chr_read,
NULL, s);
}
static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp)
{
RngBackend *b = RNG_BACKEND(obj);
RngEgd *s = RNG_EGD(b);
if (b->opened) {
error_set(errp, QERR_PERMISSION_DENIED);
} else {
g_free(s->chr_name);
s->chr_name = g_strdup(value);
}
}
static char *rng_egd_get_chardev(Object *obj, Error **errp)
{
RngEgd *s = RNG_EGD(obj);
if (s->chr && s->chr->label) {
return g_strdup(s->chr->label);
}
return NULL;
}
static void rng_egd_init(Object *obj)
{
object_property_add_str(obj, "chardev",
rng_egd_get_chardev, rng_egd_set_chardev,
NULL);
}
static void rng_egd_finalize(Object *obj)
{
RngEgd *s = RNG_EGD(obj);
if (s->chr) {
qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL);
}
g_free(s->chr_name);
rng_egd_free_requests(s);
}
static void rng_egd_class_init(ObjectClass *klass, void *data)
{
RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
rbc->request_entropy = rng_egd_request_entropy;
rbc->cancel_requests = rng_egd_cancel_requests;
rbc->opened = rng_egd_opened;
}
static TypeInfo rng_egd_info = {
.name = TYPE_RNG_EGD,
.parent = TYPE_RNG_BACKEND,
.instance_size = sizeof(RngEgd),
.class_init = rng_egd_class_init,
.instance_init = rng_egd_init,
.instance_finalize = rng_egd_finalize,
};
static void register_types(void)
{
type_register_static(&rng_egd_info);
}
type_init(register_types);

161
backends/rng-random.c Normal file
View File

@ -0,0 +1,161 @@
/*
* QEMU Random Number Generator Backend
*
* 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.
*/
#include "qemu/rng-random.h"
#include "qemu/rng.h"
#include "qerror.h"
#include "main-loop.h"
struct RndRandom
{
RngBackend parent;
int fd;
char *filename;
EntropyReceiveFunc *receive_func;
void *opaque;
size_t size;
};
/**
* A simple and incomplete backend to request entropy from /dev/random.
*
* This backend exposes an additional "filename" property that can be used to
* set the filename to use to open the backend.
*/
static void entropy_available(void *opaque)
{
RndRandom *s = RNG_RANDOM(opaque);
uint8_t buffer[s->size];
ssize_t len;
len = read(s->fd, buffer, s->size);
g_assert(len != -1);
s->receive_func(s->opaque, buffer, len);
s->receive_func = NULL;
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
}
static void rng_random_request_entropy(RngBackend *b, size_t size,
EntropyReceiveFunc *receive_entropy,
void *opaque)
{
RndRandom *s = RNG_RANDOM(b);
if (s->receive_func) {
s->receive_func(s->opaque, NULL, 0);
}
s->receive_func = receive_entropy;
s->opaque = opaque;
s->size = size;
qemu_set_fd_handler(s->fd, entropy_available, NULL, s);
}
static void rng_random_opened(RngBackend *b, Error **errp)
{
RndRandom *s = RNG_RANDOM(b);
if (s->filename == NULL) {
error_set(errp, QERR_INVALID_PARAMETER_VALUE,
"filename", "a valid filename");
} else {
s->fd = open(s->filename, O_RDONLY | O_NONBLOCK);
if (s->fd == -1) {
error_set(errp, QERR_OPEN_FILE_FAILED, s->filename);
}
}
}
static char *rng_random_get_filename(Object *obj, Error **errp)
{
RndRandom *s = RNG_RANDOM(obj);
if (s->filename) {
return g_strdup(s->filename);
}
return NULL;
}
static void rng_random_set_filename(Object *obj, const char *filename,
Error **errp)
{
RngBackend *b = RNG_BACKEND(obj);
RndRandom *s = RNG_RANDOM(obj);
if (b->opened) {
error_set(errp, QERR_PERMISSION_DENIED);
return;
}
if (s->filename) {
g_free(s->filename);
}
s->filename = g_strdup(filename);
}
static void rng_random_init(Object *obj)
{
RndRandom *s = RNG_RANDOM(obj);
object_property_add_str(obj, "filename",
rng_random_get_filename,
rng_random_set_filename,
NULL);
s->filename = g_strdup("/dev/random");
}
static void rng_random_finalize(Object *obj)
{
RndRandom *s = RNG_RANDOM(obj);
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
if (s->fd != -1) {
close(s->fd);
}
g_free(s->filename);
}
static void rng_random_class_init(ObjectClass *klass, void *data)
{
RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
rbc->request_entropy = rng_random_request_entropy;
rbc->opened = rng_random_opened;
}
static TypeInfo rng_random_info = {
.name = TYPE_RNG_RANDOM,
.parent = TYPE_RNG_BACKEND,
.instance_size = sizeof(RndRandom),
.class_init = rng_random_class_init,
.instance_init = rng_random_init,
.instance_finalize = rng_random_finalize,
};
static void register_types(void)
{
type_register_static(&rng_random_info);
}
type_init(register_types);

93
backends/rng.c Normal file
View File

@ -0,0 +1,93 @@
/*
* QEMU Random Number Generator Backend
*
* 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.
*/
#include "qemu/rng.h"
#include "qerror.h"
void rng_backend_request_entropy(RngBackend *s, size_t size,
EntropyReceiveFunc *receive_entropy,
void *opaque)
{
RngBackendClass *k = RNG_BACKEND_GET_CLASS(s);
if (k->request_entropy) {
k->request_entropy(s, size, receive_entropy, opaque);
}
}
void rng_backend_cancel_requests(RngBackend *s)
{
RngBackendClass *k = RNG_BACKEND_GET_CLASS(s);
if (k->cancel_requests) {
k->cancel_requests(s);
}
}
static bool rng_backend_prop_get_opened(Object *obj, Error **errp)
{
RngBackend *s = RNG_BACKEND(obj);
return s->opened;
}
void rng_backend_open(RngBackend *s, Error **errp)
{
object_property_set_bool(OBJECT(s), true, "opened", errp);
}
static void rng_backend_prop_set_opened(Object *obj, bool value, Error **errp)
{
RngBackend *s = RNG_BACKEND(obj);
RngBackendClass *k = RNG_BACKEND_GET_CLASS(s);
if (value == s->opened) {
return;
}
if (!value && s->opened) {
error_set(errp, QERR_PERMISSION_DENIED);
return;
}
if (k->opened) {
k->opened(s, errp);
}
if (!error_is_set(errp)) {
s->opened = value;
}
}
static void rng_backend_init(Object *obj)
{
object_property_add_bool(obj, "opened",
rng_backend_prop_get_opened,
rng_backend_prop_set_opened,
NULL);
}
static TypeInfo rng_backend_info = {
.name = TYPE_RNG_BACKEND,
.parent = TYPE_OBJECT,
.instance_size = sizeof(RngBackend),
.instance_init = rng_backend_init,
.class_size = sizeof(RngBackendClass),
.abstract = true,
};
static void register_types(void)
{
type_register_static(&rng_backend_info);
}
type_init(register_types);

View File

@ -28,6 +28,7 @@
#include "qemu-common.h" #include "qemu-common.h"
#include "nbd.h" #include "nbd.h"
#include "uri.h"
#include "block_int.h" #include "block_int.h"
#include "module.h" #include "module.h"
#include "qemu_socket.h" #include "qemu_socket.h"
@ -55,7 +56,6 @@ typedef struct BDRVNBDState {
uint32_t nbdflags; uint32_t nbdflags;
off_t size; off_t size;
size_t blocksize; size_t blocksize;
char *export_name; /* An NBD server may export several devices */
CoMutex send_mutex; CoMutex send_mutex;
CoMutex free_sema; CoMutex free_sema;
@ -65,13 +65,75 @@ typedef struct BDRVNBDState {
Coroutine *recv_coroutine[MAX_NBD_REQUESTS]; Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
struct nbd_reply reply; struct nbd_reply reply;
/* If it begins with '/', this is a UNIX domain socket. Otherwise, int is_unix;
* it's a string of the form <hostname|ip4|\[ip6\]>:port
*/
char *host_spec; char *host_spec;
char *export_name; /* An NBD server may export several devices */
} BDRVNBDState; } BDRVNBDState;
static int nbd_config(BDRVNBDState *s, const char *filename, int flags) static int nbd_parse_uri(BDRVNBDState *s, const char *filename)
{
URI *uri;
const char *p;
QueryParams *qp = NULL;
int ret = 0;
uri = uri_parse(filename);
if (!uri) {
return -EINVAL;
}
/* transport */
if (!strcmp(uri->scheme, "nbd")) {
s->is_unix = false;
} else if (!strcmp(uri->scheme, "nbd+tcp")) {
s->is_unix = false;
} else if (!strcmp(uri->scheme, "nbd+unix")) {
s->is_unix = true;
} else {
ret = -EINVAL;
goto out;
}
p = uri->path ? uri->path : "/";
p += strspn(p, "/");
if (p[0]) {
s->export_name = g_strdup(p);
}
qp = query_params_parse(uri->query);
if (qp->n > 1 || (s->is_unix && !qp->n) || (!s->is_unix && qp->n)) {
ret = -EINVAL;
goto out;
}
if (s->is_unix) {
/* nbd+unix:///export?socket=path */
if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) {
ret = -EINVAL;
goto out;
}
s->host_spec = g_strdup(qp->p[0].value);
} else {
/* nbd[+tcp]://host:port/export */
if (!uri->server) {
ret = -EINVAL;
goto out;
}
if (!uri->port) {
uri->port = NBD_DEFAULT_PORT;
}
s->host_spec = g_strdup_printf("%s:%d", uri->server, uri->port);
}
out:
if (qp) {
query_params_free(qp);
}
uri_free(uri);
return ret;
}
static int nbd_config(BDRVNBDState *s, const char *filename)
{ {
char *file; char *file;
char *export_name; char *export_name;
@ -79,6 +141,10 @@ static int nbd_config(BDRVNBDState *s, const char *filename, int flags)
const char *unixpath; const char *unixpath;
int err = -EINVAL; int err = -EINVAL;
if (strstr(filename, "://")) {
return nbd_parse_uri(s, filename);
}
file = g_strdup(filename); file = g_strdup(filename);
export_name = strstr(file, EN_OPTSTR); export_name = strstr(file, EN_OPTSTR);
@ -98,11 +164,10 @@ static int nbd_config(BDRVNBDState *s, const char *filename, int flags)
/* are we a UNIX or TCP socket? */ /* are we a UNIX or TCP socket? */
if (strstart(host_spec, "unix:", &unixpath)) { if (strstart(host_spec, "unix:", &unixpath)) {
if (unixpath[0] != '/') { /* We demand an absolute path*/ s->is_unix = true;
goto out;
}
s->host_spec = g_strdup(unixpath); s->host_spec = g_strdup(unixpath);
} else { } else {
s->is_unix = false;
s->host_spec = g_strdup(host_spec); s->host_spec = g_strdup(host_spec);
} }
@ -262,7 +327,7 @@ static int nbd_establish_connection(BlockDriverState *bs)
off_t size; off_t size;
size_t blocksize; size_t blocksize;
if (s->host_spec[0] == '/') { if (s->is_unix) {
sock = unix_socket_outgoing(s->host_spec); sock = unix_socket_outgoing(s->host_spec);
} else { } else {
sock = tcp_socket_outgoing_spec(s->host_spec); sock = tcp_socket_outgoing_spec(s->host_spec);
@ -320,7 +385,7 @@ static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
qemu_co_mutex_init(&s->free_sema); qemu_co_mutex_init(&s->free_sema);
/* Pop the config into our state object. Exit if invalid. */ /* Pop the config into our state object. Exit if invalid. */
result = nbd_config(s, filename, flags); result = nbd_config(s, filename);
if (result != 0) { if (result != 0) {
return result; return result;
} }
@ -498,6 +563,33 @@ static int64_t nbd_getlength(BlockDriverState *bs)
static BlockDriver bdrv_nbd = { static BlockDriver bdrv_nbd = {
.format_name = "nbd", .format_name = "nbd",
.protocol_name = "nbd",
.instance_size = sizeof(BDRVNBDState),
.bdrv_file_open = nbd_open,
.bdrv_co_readv = nbd_co_readv,
.bdrv_co_writev = nbd_co_writev,
.bdrv_close = nbd_close,
.bdrv_co_flush_to_os = nbd_co_flush,
.bdrv_co_discard = nbd_co_discard,
.bdrv_getlength = nbd_getlength,
};
static BlockDriver bdrv_nbd_tcp = {
.format_name = "nbd",
.protocol_name = "nbd+tcp",
.instance_size = sizeof(BDRVNBDState),
.bdrv_file_open = nbd_open,
.bdrv_co_readv = nbd_co_readv,
.bdrv_co_writev = nbd_co_writev,
.bdrv_close = nbd_close,
.bdrv_co_flush_to_os = nbd_co_flush,
.bdrv_co_discard = nbd_co_discard,
.bdrv_getlength = nbd_getlength,
};
static BlockDriver bdrv_nbd_unix = {
.format_name = "nbd",
.protocol_name = "nbd+unix",
.instance_size = sizeof(BDRVNBDState), .instance_size = sizeof(BDRVNBDState),
.bdrv_file_open = nbd_open, .bdrv_file_open = nbd_open,
.bdrv_co_readv = nbd_co_readv, .bdrv_co_readv = nbd_co_readv,
@ -506,12 +598,13 @@ static BlockDriver bdrv_nbd = {
.bdrv_co_flush_to_os = nbd_co_flush, .bdrv_co_flush_to_os = nbd_co_flush,
.bdrv_co_discard = nbd_co_discard, .bdrv_co_discard = nbd_co_discard,
.bdrv_getlength = nbd_getlength, .bdrv_getlength = nbd_getlength,
.protocol_name = "nbd",
}; };
static void bdrv_nbd_init(void) static void bdrv_nbd_init(void)
{ {
bdrv_register(&bdrv_nbd); bdrv_register(&bdrv_nbd);
bdrv_register(&bdrv_nbd_tcp);
bdrv_register(&bdrv_nbd_unix);
} }
block_init(bdrv_nbd_init); block_init(bdrv_nbd_init);

View File

@ -82,6 +82,11 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
NBDExport *exp; NBDExport *exp;
NBDCloseNotifier *n; NBDCloseNotifier *n;
if (server_fd == -1) {
error_setg(errp, "NBD server not running");
return;
}
if (nbd_export_find(device)) { if (nbd_export_find(device)) {
error_setg(errp, "NBD server already exporting device '%s'", device); error_setg(errp, "NBD server already exporting device '%s'", device);
return; return;
@ -93,6 +98,13 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
return; return;
} }
if (!has_writable) {
writable = true;
}
if (bdrv_is_read_only(bs)) {
writable = false;
}
exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY, exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY,
nbd_server_put_ref); nbd_server_put_ref);
@ -113,7 +125,9 @@ void qmp_nbd_server_stop(Error **errp)
nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp)); nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp));
} }
qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL); if (server_fd != -1) {
close(server_fd); qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL);
server_fd = -1; close(server_fd);
server_fd = -1;
}
} }

View File

@ -50,20 +50,9 @@
# define __printf__ __gnu_printf__ # define __printf__ __gnu_printf__
# endif # endif
# endif # endif
# if defined(__APPLE__)
# define QEMU_WEAK_ALIAS(newname, oldname) \
static typeof(oldname) weak_##newname __attribute__((unused, weakref(#oldname)))
# define QEMU_WEAK_REF(newname, oldname) (weak_##newname ? weak_##newname : oldname)
# else
# define QEMU_WEAK_ALIAS(newname, oldname) \
typeof(oldname) newname __attribute__((weak, alias (#oldname)))
# define QEMU_WEAK_REF(newname, oldname) newname
# endif
#else #else
#define GCC_ATTR /**/ #define GCC_ATTR /**/
#define GCC_FMT_ATTR(n, m) #define GCC_FMT_ATTR(n, m)
#define QEMU_WEAK_ALIAS(newname, oldname) \
_Pragma("weak " #newname "=" #oldname)
#endif #endif
#endif /* COMPILER_H */ #endif /* COMPILER_H */

31
configure vendored
View File

@ -1383,7 +1383,7 @@ fi
# libseccomp check # libseccomp check
if test "$seccomp" != "no" ; then if test "$seccomp" != "no" ; then
if $pkg_config libseccomp --modversion >/dev/null 2>&1; then if $pkg_config --atleast-version=1.0.0 libseccomp --modversion >/dev/null 2>&1; then
LIBS=`$pkg_config --libs libseccomp` LIBS=`$pkg_config --libs libseccomp`
seccomp="yes" seccomp="yes"
else else
@ -2121,11 +2121,10 @@ else
echo " git submodule update --init pixman" echo " git submodule update --init pixman"
exit 1 exit 1
fi fi
pixman_cflags="-I${source_path}/pixman/pixman" mkdir -p pixman/pixman
pixman_libs="-Lpixman/pixman/.libs -lpixman-1" pixman_cflags="-I\$(SRC_PATH)/pixman/pixman -I\$(BUILD_DIR)/pixman/pixman"
pixman_libs="-L\$(BUILD_DIR)/pixman/pixman/.libs -lpixman-1"
fi fi
QEMU_CFLAGS="$QEMU_CFLAGS $pixman_cflags"
libs_softmmu="$libs_softmmu $pixman_libs"
########################################## ##########################################
# libcap probe # libcap probe
@ -3150,6 +3149,10 @@ if test "$cpu" = "ppc64" -a "$targetos" != "Darwin" ; then
roms="$roms spapr-rtas" roms="$roms spapr-rtas"
fi fi
# add pixman flags after all config tests are done
QEMU_CFLAGS="$QEMU_CFLAGS $pixman_cflags"
libs_softmmu="$libs_softmmu $pixman_libs"
echo "Install prefix $prefix" echo "Install prefix $prefix"
echo "BIOS directory `eval echo $qemu_datadir`" echo "BIOS directory `eval echo $qemu_datadir`"
echo "binary directory `eval echo $bindir`" echo "binary directory `eval echo $bindir`"
@ -3659,6 +3662,11 @@ if test "$sparse" = "yes" ; then
echo "HOST_CC := REAL_CC=\"\$(HOST_CC)\" cgcc" >> $config_host_mak echo "HOST_CC := REAL_CC=\"\$(HOST_CC)\" cgcc" >> $config_host_mak
echo "QEMU_CFLAGS += -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-non-pointer-null" >> $config_host_mak echo "QEMU_CFLAGS += -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-non-pointer-null" >> $config_host_mak
fi fi
if test "$cross_prefix" != ""; then
echo "AUTOCONF_HOST := --host=${cross_prefix%-}" >> $config_host_mak
else
echo "AUTOCONF_HOST := " >> $config_host_mak
fi
echo "LDFLAGS=$LDFLAGS" >> $config_host_mak echo "LDFLAGS=$LDFLAGS" >> $config_host_mak
echo "ARLIBS_BEGIN=$arlibs_begin" >> $config_host_mak echo "ARLIBS_BEGIN=$arlibs_begin" >> $config_host_mak
echo "ARLIBS_END=$arlibs_end" >> $config_host_mak echo "ARLIBS_END=$arlibs_end" >> $config_host_mak
@ -3895,7 +3903,10 @@ upper() {
case "$cpu" in case "$cpu" in
i386|x86_64|ppc) i386|x86_64|ppc)
echo "CONFIG_QEMU_LDST_OPTIMIZATION=y" >> $config_target_mak # The TCG interpreter currently does not support ld/st optimization.
if test "$tcg_interpreter" = "no" ; then
echo "CONFIG_QEMU_LDST_OPTIMIZATION=y" >> $config_target_mak
fi
;; ;;
esac esac
@ -3960,9 +3971,6 @@ if test "$target_softmmu" = "yes" ; then
if test "$smartcard_nss" = "yes" ; then if test "$smartcard_nss" = "yes" ; then
echo "subdir-$target: subdir-libcacard" >> $config_host_mak echo "subdir-$target: subdir-libcacard" >> $config_host_mak
fi fi
if test "$pixman" = "internal" ; then
echo "subdir-$target: subdir-pixman" >> $config_host_mak
fi
case "$target_arch2" in case "$target_arch2" in
i386|x86_64) i386|x86_64)
echo "CONFIG_HAVE_CORE_DUMP=y" >> $config_target_mak echo "CONFIG_HAVE_CORE_DUMP=y" >> $config_target_mak
@ -4160,13 +4168,16 @@ echo "QEMU_INCLUDES+=$includes" >> $config_target_mak
done # for target in $targets done # for target in $targets
if [ "$pixman" = "internal" ]; then
echo "config-host.h: subdir-pixman" >> $config_host_mak
fi
# build tree in object directory in case the source is not in the current directory # build tree in object directory in case the source is not in the current directory
DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32" DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32"
DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas" DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas"
DIRS="$DIRS roms/seabios roms/vgabios" DIRS="$DIRS roms/seabios roms/vgabios"
DIRS="$DIRS qapi-generated" DIRS="$DIRS qapi-generated"
DIRS="$DIRS libcacard libcacard/libcacard libcacard/trace" DIRS="$DIRS libcacard libcacard/libcacard libcacard/trace"
DIRS="$DIRS pixman"
FILES="Makefile tests/tcg/Makefile qdict-test-data.txt" FILES="Makefile tests/tcg/Makefile qdict-test-data.txt"
FILES="$FILES tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit" FILES="$FILES tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit"
FILES="$FILES tests/tcg/lm32/Makefile libcacard/Makefile" FILES="$FILES tests/tcg/lm32/Makefile libcacard/Makefile"

View File

@ -377,6 +377,11 @@ static inline pixman_format_code_t ds_get_format(DisplayState *ds)
return ds->surface->format; return ds->surface->format;
} }
static inline pixman_image_t *ds_get_image(DisplayState *ds)
{
return ds->surface->image;
}
static inline int ds_get_depth(DisplayState *ds) static inline int ds_get_depth(DisplayState *ds)
{ {
return ds->surface->pf.depth; return ds->surface->pf.depth;

5
dma.h
View File

@ -68,6 +68,11 @@ struct DMAContext {
DMAUnmapFunc *unmap; DMAUnmapFunc *unmap;
}; };
/* A global DMA context corresponding to the address_space_memory
* AddressSpace, for sysbus devices which do DMA.
*/
extern DMAContext dma_context_memory;
static inline void dma_barrier(DMAContext *dma, DMADirection dir) static inline void dma_barrier(DMAContext *dma, DMADirection dir)
{ {
/* /*

View File

@ -36,7 +36,8 @@ IO ports used
03c0 - 03df : standard vga ports 03c0 - 03df : standard vga ports
01ce : bochs vbe interface index port 01ce : bochs vbe interface index port
01cf : bochs vbe interface data port 01cf : bochs vbe interface data port (x86 only)
01d0 : bochs vbe interface data port
Memory regions used Memory regions used

View File

@ -290,10 +290,11 @@ extern int tb_invalidated_flag;
/* The return address may point to the start of the next instruction. /* The return address may point to the start of the next instruction.
Subtracting one gets us the call instruction itself. */ Subtracting one gets us the call instruction itself. */
#if defined(CONFIG_TCG_INTERPRETER) #if defined(CONFIG_TCG_INTERPRETER)
/* Alpha and SH4 user mode emulations and Softmmu call GETPC(). /* Softmmu, Alpha, MIPS, SH4 and SPARC user mode emulations call GETPC().
For all others, GETPC remains undefined (which makes TCI a little faster. */ For all others, GETPC remains undefined (which makes TCI a little faster. */
# if defined(CONFIG_SOFTMMU) || defined(TARGET_ALPHA) || defined(TARGET_SH4) \ # if defined(CONFIG_SOFTMMU) || \
|| defined(TARGET_SPARC) defined(TARGET_ALPHA) || defined(TARGET_MIPS) || \
defined(TARGET_SH4) || defined(TARGET_SPARC)
extern uintptr_t tci_tb_ptr; extern uintptr_t tci_tb_ptr;
# define GETPC() tci_tb_ptr # define GETPC() tci_tb_ptr
# endif # endif

5
exec.c
View File

@ -34,6 +34,7 @@
#include "hw/xen.h" #include "hw/xen.h"
#include "qemu-timer.h" #include "qemu-timer.h"
#include "memory.h" #include "memory.h"
#include "dma.h"
#include "exec-memory.h" #include "exec-memory.h"
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
#include <qemu.h> #include <qemu.h>
@ -103,6 +104,7 @@ static MemoryRegion *system_io;
AddressSpace address_space_io; AddressSpace address_space_io;
AddressSpace address_space_memory; AddressSpace address_space_memory;
DMAContext dma_context_memory;
MemoryRegion io_mem_ram, io_mem_rom, io_mem_unassigned, io_mem_notdirty; MemoryRegion io_mem_ram, io_mem_rom, io_mem_unassigned, io_mem_notdirty;
static MemoryRegion io_mem_subpage_ram; static MemoryRegion io_mem_subpage_ram;
@ -3294,6 +3296,9 @@ static void memory_map_init(void)
memory_listener_register(&core_memory_listener, &address_space_memory); memory_listener_register(&core_memory_listener, &address_space_memory);
memory_listener_register(&io_memory_listener, &address_space_io); memory_listener_register(&io_memory_listener, &address_space_io);
memory_listener_register(&tcg_memory_listener, &address_space_memory); memory_listener_register(&tcg_memory_listener, &address_space_memory);
dma_context_init(&dma_context_memory, &address_space_memory,
NULL, NULL, NULL);
} }
MemoryRegion *get_system_memory(void) MemoryRegion *get_system_memory(void)

View File

@ -16,7 +16,7 @@ static inline void gen_icount_start(void)
count = tcg_temp_local_new_i32(); count = tcg_temp_local_new_i32();
tcg_gen_ld_i32(count, cpu_env, offsetof(CPUArchState, icount_decr.u32)); tcg_gen_ld_i32(count, cpu_env, offsetof(CPUArchState, icount_decr.u32));
/* This is a horrid hack to allow fixing up the value later. */ /* This is a horrid hack to allow fixing up the value later. */
icount_arg = gen_opparam_ptr + 1; icount_arg = tcg_ctx.gen_opparam_ptr + 1;
tcg_gen_subi_i32(count, count, 0xdeadbeef); tcg_gen_subi_i32(count, count, 0xdeadbeef);
tcg_gen_brcondi_i32(TCG_COND_LT, count, 0, icount_label); tcg_gen_brcondi_i32(TCG_COND_LT, count, 0, icount_label);

View File

@ -1310,6 +1310,51 @@ Remove all matches from the access control list, and set the default
policy back to @code{deny}. policy back to @code{deny}.
ETEXI ETEXI
{
.name = "nbd_server_start",
.args_type = "all:-a,writable:-w,uri:s",
.params = "nbd_server_start [-a] [-w] host:port",
.help = "serve block devices on the given host and port",
.mhandler.cmd = hmp_nbd_server_start,
},
STEXI
@item nbd_server_start @var{host}:@var{port}
@findex nbd_server_start
Start an NBD server on the given host and/or port. If the @option{-a}
option is included, all of the virtual machine's block devices that
have an inserted media on them are automatically exported; in this case,
the @option{-w} option makes the devices writable too.
ETEXI
{
.name = "nbd_server_add",
.args_type = "writable:-w,device:B",
.params = "nbd_server_add [-w] device",
.help = "export a block device via NBD",
.mhandler.cmd = hmp_nbd_server_add,
},
STEXI
@item nbd_server_add @var{device}
@findex nbd_server_add
Export a block device through QEMU's NBD server, which must be started
beforehand with @command{nbd_server_start}. The @option{-w} option makes the
exported device writable too.
ETEXI
{
.name = "nbd_server_stop",
.args_type = "",
.params = "nbd_server_stop",
.help = "stop serving block devices using the NBD protocol",
.mhandler.cmd = hmp_nbd_server_stop,
},
STEXI
@item nbd_server_stop
@findex nbd_server_stop
Stop the QEMU embedded NBD server.
ETEXI
#if defined(TARGET_I386) #if defined(TARGET_I386)
{ {

76
hmp.c
View File

@ -18,6 +18,7 @@
#include "qemu-option.h" #include "qemu-option.h"
#include "qemu-timer.h" #include "qemu-timer.h"
#include "qmp-commands.h" #include "qmp-commands.h"
#include "qemu_socket.h"
#include "monitor.h" #include "monitor.h"
#include "console.h" #include "console.h"
@ -1259,3 +1260,78 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict)
qmp_screendump(filename, &err); qmp_screendump(filename, &err);
hmp_handle_error(mon, &err); hmp_handle_error(mon, &err);
} }
void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
{
const char *uri = qdict_get_str(qdict, "uri");
int writable = qdict_get_try_bool(qdict, "writable", 0);
int all = qdict_get_try_bool(qdict, "all", 0);
Error *local_err = NULL;
BlockInfoList *block_list, *info;
SocketAddress *addr;
if (writable && !all) {
error_setg(&local_err, "-w only valid together with -a");
goto exit;
}
/* First check if the address is valid and start the server. */
addr = socket_parse(uri, &local_err);
if (local_err != NULL) {
goto exit;
}
qmp_nbd_server_start(addr, &local_err);
qapi_free_SocketAddress(addr);
if (local_err != NULL) {
goto exit;
}
if (!all) {
return;
}
/* Then try adding all block devices. If one fails, close all and
* exit.
*/
block_list = qmp_query_block(NULL);
for (info = block_list; info; info = info->next) {
if (!info->value->has_inserted) {
continue;
}
qmp_nbd_server_add(info->value->device, true, writable, &local_err);
if (local_err != NULL) {
qmp_nbd_server_stop(NULL);
break;
}
}
qapi_free_BlockInfoList(block_list);
exit:
hmp_handle_error(mon, &local_err);
}
void hmp_nbd_server_add(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
int writable = qdict_get_try_bool(qdict, "writable", 0);
Error *local_err = NULL;
qmp_nbd_server_add(device, true, writable, &local_err);
if (local_err != NULL) {
hmp_handle_error(mon, &local_err);
}
}
void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict)
{
Error *errp = NULL;
qmp_nbd_server_stop(&errp);
hmp_handle_error(mon, &errp);
}

3
hmp.h
View File

@ -77,5 +77,8 @@ void hmp_getfd(Monitor *mon, const QDict *qdict);
void hmp_closefd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict);
void hmp_send_key(Monitor *mon, const QDict *qdict); void hmp_send_key(Monitor *mon, const QDict *qdict);
void hmp_screen_dump(Monitor *mon, const QDict *qdict); void hmp_screen_dump(Monitor *mon, const QDict *qdict);
void hmp_nbd_server_start(Monitor *mon, const QDict *qdict);
void hmp_nbd_server_add(Monitor *mon, const QDict *qdict);
void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict);
#endif #endif

View File

@ -1,6 +1,7 @@
common-obj-y = usb/ ide/ common-obj-y = usb/ ide/
common-obj-y += loader.o common-obj-y += loader.o
common-obj-$(CONFIG_VIRTIO) += virtio-console.o common-obj-$(CONFIG_VIRTIO) += virtio-console.o
common-obj-$(CONFIG_VIRTIO) += virtio-rng.o
common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
common-obj-y += fw_cfg.o common-obj-y += fw_cfg.o
common-obj-$(CONFIG_PCI) += pci.o pci_bridge.o pci_bridge_dev.o common-obj-$(CONFIG_PCI) += pci.o pci_bridge.o pci_bridge_dev.o

View File

@ -25,7 +25,6 @@
#include "iov.h" #include "iov.h"
#include "scsi.h" #include "scsi.h"
#include "scsi-defs.h" #include "scsi-defs.h"
#include "block_int.h"
#include "trace.h" #include "trace.h"
#include "mfi.h" #include "mfi.h"
@ -1080,6 +1079,7 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd)
/* Logical device size is in blocks */ /* Logical device size is in blocks */
bdrv_get_geometry(conf->bs, &ld_size); bdrv_get_geometry(conf->bs, &ld_size);
info.ld_list[num_ld_disks].ld.v.target_id = sdev->id; info.ld_list[num_ld_disks].ld.v.target_id = sdev->id;
info.ld_list[num_ld_disks].ld.v.lun_id = sdev->lun;
info.ld_list[num_ld_disks].state = MFI_LD_STATE_OPTIMAL; info.ld_list[num_ld_disks].state = MFI_LD_STATE_OPTIMAL;
info.ld_list[num_ld_disks].size = cpu_to_le64(ld_size); info.ld_list[num_ld_disks].size = cpu_to_le64(ld_size);
num_ld_disks++; num_ld_disks++;

View File

@ -1085,7 +1085,7 @@ struct mfi_pd_list {
union mfi_ld_ref { union mfi_ld_ref {
struct { struct {
uint8_t target_id; uint8_t target_id;
uint8_t reserved; uint8_t lun_id;
uint16_t seq; uint16_t seq;
} v; } v;
uint32_t ref; uint32_t ref;

View File

@ -861,7 +861,8 @@ void mips_malta_init(QEMUMachineInitArgs *args)
be = 0; be = 0;
#endif #endif
/* FPGA */ /* FPGA */
malta_fpga_init(system_memory, FPGA_ADDRESS, env->irq[2], serial_hds[2]); /* The CBUS UART is attached to the MIPS CPU INT2 pin, ie interrupt 4 */
malta_fpga_init(system_memory, FPGA_ADDRESS, env->irq[4], serial_hds[2]);
/* Load firmware in flash / BIOS. */ /* Load firmware in flash / BIOS. */
dinfo = drive_get(IF_PFLASH, 0, fl_idx); dinfo = drive_get(IF_PFLASH, 0, fl_idx);

View File

@ -76,6 +76,7 @@
#define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002 #define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002
#define PCI_DEVICE_ID_VIRTIO_CONSOLE 0x1003 #define PCI_DEVICE_ID_VIRTIO_CONSOLE 0x1003
#define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004 #define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004
#define PCI_DEVICE_ID_VIRTIO_RNG 0x1005
#define FMT_PCIBUS PRIx64 #define FMT_PCIBUS PRIx64

View File

@ -293,6 +293,10 @@ void qxl_spice_reset_cursor(PCIQXLDevice *qxl)
qemu_mutex_lock(&qxl->track_lock); qemu_mutex_lock(&qxl->track_lock);
qxl->guest_cursor = 0; qxl->guest_cursor = 0;
qemu_mutex_unlock(&qxl->track_lock); qemu_mutex_unlock(&qxl->track_lock);
if (qxl->ssd.cursor) {
cursor_put(qxl->ssd.cursor);
}
qxl->ssd.cursor = cursor_builtin_hidden();
} }
@ -447,6 +451,12 @@ static int qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext)
qxl->ssd.num_surfaces); qxl->ssd.num_surfaces);
return 1; return 1;
} }
if (cmd->type == QXL_SURFACE_CMD_CREATE &&
(cmd->u.surface_create.stride & 0x03) != 0) {
qxl_set_guest_bug(qxl, "QXL_CMD_SURFACE stride = %d %% 4 != 0\n",
cmd->u.surface_create.stride);
return 1;
}
qemu_mutex_lock(&qxl->track_lock); qemu_mutex_lock(&qxl->track_lock);
if (cmd->type == QXL_SURFACE_CMD_CREATE) { if (cmd->type == QXL_SURFACE_CMD_CREATE) {
qxl->guest_surfaces.cmds[id] = ext->cmd.data; qxl->guest_surfaces.cmds[id] = ext->cmd.data;
@ -1059,7 +1069,7 @@ static void qxl_enter_vga_mode(PCIQXLDevice *d)
trace_qxl_enter_vga_mode(d->id); trace_qxl_enter_vga_mode(d->id);
qemu_spice_create_host_primary(&d->ssd); qemu_spice_create_host_primary(&d->ssd);
d->mode = QXL_MODE_VGA; d->mode = QXL_MODE_VGA;
memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty)); dpy_gfx_resize(d->ssd.ds);
vga_dirty_log_start(&d->vga); vga_dirty_log_start(&d->vga);
} }
@ -1357,6 +1367,12 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
trace_qxl_create_guest_primary_rest(qxl->id, sc->stride, sc->type, trace_qxl_create_guest_primary_rest(qxl->id, sc->stride, sc->type,
sc->flags); sc->flags);
if ((surface.stride & 0x3) != 0) {
qxl_set_guest_bug(qxl, "primary surface stride = %d %% 4 != 0",
surface.stride);
return;
}
surface.mouse_mode = true; surface.mouse_mode = true;
surface.group_id = MEMSLOT_GROUP_GUEST; surface.group_id = MEMSLOT_GROUP_GUEST;
if (loadvm) { if (loadvm) {
@ -1689,7 +1705,13 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
uint32_t le_events = cpu_to_le32(events); uint32_t le_events = cpu_to_le32(events);
trace_qxl_send_events(d->id, events); trace_qxl_send_events(d->id, events);
assert(qemu_spice_display_is_running(&d->ssd)); if (!qemu_spice_display_is_running(&d->ssd)) {
/* spice-server tracks guest running state and should not do this */
fprintf(stderr, "%s: spice-server bug: guest stopped, ignoring\n",
__func__);
trace_qxl_send_events_vm_stopped(d->id, events);
return;
}
old_pending = __sync_fetch_and_or(&d->ram->int_pending, le_events); old_pending = __sync_fetch_and_or(&d->ram->int_pending, le_events);
if ((old_pending & le_events) == le_events) { if ((old_pending & le_events) == le_events) {
return; return;
@ -2027,6 +2049,7 @@ static int qxl_init_primary(PCIDevice *dev)
PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev); PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
VGACommonState *vga = &qxl->vga; VGACommonState *vga = &qxl->vga;
PortioList *qxl_vga_port_list = g_new(PortioList, 1); PortioList *qxl_vga_port_list = g_new(PortioList, 1);
int rc;
qxl->id = 0; qxl->id = 0;
qxl_init_ramsize(qxl); qxl_init_ramsize(qxl);
@ -2041,9 +2064,14 @@ static int qxl_init_primary(PCIDevice *dev)
qemu_spice_display_init_common(&qxl->ssd, vga->ds); qemu_spice_display_init_common(&qxl->ssd, vga->ds);
qxl0 = qxl; qxl0 = qxl;
register_displaychangelistener(vga->ds, &display_listener);
return qxl_init_common(qxl); rc = qxl_init_common(qxl);
if (rc != 0) {
return rc;
}
register_displaychangelistener(vga->ds, &display_listener);
return rc;
} }
static int qxl_init_secondary(PCIDevice *dev) static int qxl_init_secondary(PCIDevice *dev)

View File

@ -26,6 +26,7 @@
#include "loader.h" #include "loader.h"
#include "elf.h" #include "elf.h"
#include "hw/virtio.h" #include "hw/virtio.h"
#include "hw/virtio-rng.h"
#include "hw/virtio-serial.h" #include "hw/virtio-serial.h"
#include "hw/virtio-net.h" #include "hw/virtio-net.h"
#include "hw/sysbus.h" #include "hw/sysbus.h"
@ -206,6 +207,18 @@ static int s390_virtio_scsi_init(VirtIOS390Device *dev)
return s390_virtio_device_init(dev, vdev); return s390_virtio_device_init(dev, vdev);
} }
static int s390_virtio_rng_init(VirtIOS390Device *dev)
{
VirtIODevice *vdev;
vdev = virtio_rng_init((DeviceState *)dev, &dev->rng);
if (!vdev) {
return -1;
}
return s390_virtio_device_init(dev, vdev);
}
static uint64_t s390_virtio_device_vq_token(VirtIOS390Device *dev, int vq) static uint64_t s390_virtio_device_vq_token(VirtIOS390Device *dev, int vq)
{ {
ram_addr_t token_off; ram_addr_t token_off;
@ -448,6 +461,29 @@ static TypeInfo s390_virtio_serial = {
.class_init = s390_virtio_serial_class_init, .class_init = s390_virtio_serial_class_init,
}; };
static void s390_virtio_rng_initfn(Object *obj)
{
VirtIOS390Device *dev = VIRTIO_S390_DEVICE(obj);
object_property_add_link(obj, "rng", TYPE_RNG_BACKEND,
(Object **)&dev->rng.rng, NULL);
}
static void s390_virtio_rng_class_init(ObjectClass *klass, void *data)
{
VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
k->init = s390_virtio_rng_init;
}
static TypeInfo s390_virtio_rng = {
.name = "virtio-rng-s390",
.parent = TYPE_VIRTIO_S390_DEVICE,
.instance_size = sizeof(VirtIOS390Device),
.instance_init = s390_virtio_rng_initfn,
.class_init = s390_virtio_rng_class_init,
};
static int s390_virtio_busdev_init(DeviceState *dev) static int s390_virtio_busdev_init(DeviceState *dev)
{ {
VirtIOS390Device *_dev = (VirtIOS390Device *)dev; VirtIOS390Device *_dev = (VirtIOS390Device *)dev;
@ -528,6 +564,7 @@ static void s390_virtio_register_types(void)
type_register_static(&s390_virtio_blk); type_register_static(&s390_virtio_blk);
type_register_static(&s390_virtio_net); type_register_static(&s390_virtio_net);
type_register_static(&s390_virtio_scsi); type_register_static(&s390_virtio_scsi);
type_register_static(&s390_virtio_rng);
type_register_static(&s390_virtio_bridge_info); type_register_static(&s390_virtio_bridge_info);
} }

View File

@ -19,6 +19,7 @@
#include "virtio-blk.h" #include "virtio-blk.h"
#include "virtio-net.h" #include "virtio-net.h"
#include "virtio-rng.h"
#include "virtio-serial.h" #include "virtio-serial.h"
#include "virtio-scsi.h" #include "virtio-scsi.h"
@ -75,6 +76,7 @@ struct VirtIOS390Device {
virtio_serial_conf serial; virtio_serial_conf serial;
virtio_net_conf net; virtio_net_conf net;
VirtIOSCSIConf scsi; VirtIOSCSIConf scsi;
VirtIORNGConf rng;
}; };
typedef struct VirtIOS390Bus { typedef struct VirtIOS390Bus {

View File

@ -652,7 +652,6 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
if (buflen > SCSI_MAX_INQUIRY_LEN) { if (buflen > SCSI_MAX_INQUIRY_LEN) {
buflen = SCSI_MAX_INQUIRY_LEN; buflen = SCSI_MAX_INQUIRY_LEN;
} }
memset(outbuf, 0, buflen);
outbuf[0] = s->qdev.type & 0x1f; outbuf[0] = s->qdev.type & 0x1f;
outbuf[1] = (s->features & (1 << SCSI_DISK_F_REMOVABLE)) ? 0x80 : 0; outbuf[1] = (s->features & (1 << SCSI_DISK_F_REMOVABLE)) ? 0x80 : 0;
@ -1388,6 +1387,7 @@ invalid_param_len:
static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf) static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf)
{ {
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint8_t *p = inbuf; uint8_t *p = inbuf;
int cmd = r->req.cmd.buf[0]; int cmd = r->req.cmd.buf[0];
int len = r->req.cmd.xfer; int len = r->req.cmd.xfer;
@ -1424,6 +1424,14 @@ static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf)
return; return;
} }
} }
if (!bdrv_enable_write_cache(s->qdev.conf.bs)) {
/* The request is used as the AIO opaque value, so add a ref. */
scsi_req_ref(&r->req);
bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_aio_complete, r);
return;
}
scsi_req_complete(&r->req, GOOD); scsi_req_complete(&r->req, GOOD);
return; return;
@ -1596,24 +1604,26 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
break; break;
} }
/*
* FIXME: we shouldn't return anything bigger than 4k, but the code
* requires the buffer to be as big as req->cmd.xfer in several
* places. So, do not allow CDBs with a very large ALLOCATION
* LENGTH. The real fix would be to modify scsi_read_data and
* dma_buf_read, so that they return data beyond the buflen
* as all zeros.
*/
if (req->cmd.xfer > 65536) {
goto illegal_request;
}
r->buflen = MAX(4096, req->cmd.xfer);
if (!r->iov.iov_base) { if (!r->iov.iov_base) {
/*
* FIXME: we shouldn't return anything bigger than 4k, but the code
* requires the buffer to be as big as req->cmd.xfer in several
* places. So, do not allow CDBs with a very large ALLOCATION
* LENGTH. The real fix would be to modify scsi_read_data and
* dma_buf_read, so that they return data beyond the buflen
* as all zeros.
*/
if (req->cmd.xfer > 65536) {
goto illegal_request;
}
r->buflen = MAX(4096, req->cmd.xfer);
r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen); r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen);
} }
buflen = req->cmd.xfer; buflen = req->cmd.xfer;
outbuf = r->iov.iov_base; outbuf = r->iov.iov_base;
memset(outbuf, 0, r->buflen);
switch (req->cmd.buf[0]) { switch (req->cmd.buf[0]) {
case TEST_UNIT_READY: case TEST_UNIT_READY:
assert(!s->tray_open && bdrv_is_inserted(s->qdev.conf.bs)); assert(!s->tray_open && bdrv_is_inserted(s->qdev.conf.bs));
@ -1694,12 +1704,14 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
outbuf[5] = 0; outbuf[5] = 0;
outbuf[6] = s->qdev.blocksize >> 8; outbuf[6] = s->qdev.blocksize >> 8;
outbuf[7] = 0; outbuf[7] = 0;
buflen = 8;
break; break;
case REQUEST_SENSE: case REQUEST_SENSE:
/* Just return "NO SENSE". */ /* Just return "NO SENSE". */
buflen = scsi_build_sense(NULL, 0, outbuf, r->buflen, buflen = scsi_build_sense(NULL, 0, outbuf, r->buflen,
(req->cmd.buf[1] & 1) == 0); (req->cmd.buf[1] & 1) == 0);
if (buflen < 0) {
goto illegal_request;
}
break; break;
case MECHANISM_STATUS: case MECHANISM_STATUS:
buflen = scsi_emulate_mechanism_status(s, outbuf); buflen = scsi_emulate_mechanism_status(s, outbuf);
@ -1770,7 +1782,6 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
} }
/* Protection, exponent and lowest lba field left blank. */ /* Protection, exponent and lowest lba field left blank. */
buflen = req->cmd.xfer;
break; break;
} }
DPRINTF("Unsupported Service Action In\n"); DPRINTF("Unsupported Service Action In\n");
@ -1827,7 +1838,7 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
return 0; return 0;
} }
assert(!r->req.aiocb); assert(!r->req.aiocb);
r->iov.iov_len = MIN(buflen, req->cmd.xfer); r->iov.iov_len = MIN(r->buflen, req->cmd.xfer);
if (r->iov.iov_len == 0) { if (r->iov.iov_len == 0) {
scsi_req_complete(&r->req, GOOD); scsi_req_complete(&r->req, GOOD);
} }
@ -1962,7 +1973,6 @@ static void scsi_disk_resize_cb(void *opaque)
* direct-access devices. * direct-access devices.
*/ */
if (s->qdev.type == TYPE_DISK) { if (s->qdev.type == TYPE_DISK) {
scsi_device_set_ua(&s->qdev, SENSE_CODE(CAPACITY_CHANGED));
scsi_device_report_change(&s->qdev, SENSE_CODE(CAPACITY_CHANGED)); scsi_device_report_change(&s->qdev, SENSE_CODE(CAPACITY_CHANGED));
} }
} }

View File

@ -38,6 +38,7 @@
#define USB_TOKEN_IN 0x69 /* device -> host */ #define USB_TOKEN_IN 0x69 /* device -> host */
#define USB_TOKEN_OUT 0xe1 /* host -> device */ #define USB_TOKEN_OUT 0xe1 /* host -> device */
#define USB_RET_SUCCESS (0)
#define USB_RET_NODEV (-1) #define USB_RET_NODEV (-1)
#define USB_RET_NAK (-2) #define USB_RET_NAK (-2)
#define USB_RET_STALL (-3) #define USB_RET_STALL (-3)
@ -280,18 +281,20 @@ typedef struct USBDeviceClass {
* Process control request. * Process control request.
* Called from handle_packet(). * Called from handle_packet().
* *
* Returns length or one of the USB_RET_ codes. * Status gets stored in p->status, and if p->status == USB_RET_SUCCESS
* then the number of bytes transfered is stored in p->actual_length
*/ */
int (*handle_control)(USBDevice *dev, USBPacket *p, int request, int value, void (*handle_control)(USBDevice *dev, USBPacket *p, int request, int value,
int index, int length, uint8_t *data); int index, int length, uint8_t *data);
/* /*
* Process data transfers (both BULK and ISOC). * Process data transfers (both BULK and ISOC).
* Called from handle_packet(). * Called from handle_packet().
* *
* Returns length or one of the USB_RET_ codes. * Status gets stored in p->status, and if p->status == USB_RET_SUCCESS
* then the number of bytes transfered is stored in p->actual_length
*/ */
int (*handle_data)(USBDevice *dev, USBPacket *p); void (*handle_data)(USBDevice *dev, USBPacket *p);
void (*set_interface)(USBDevice *dev, int interface, void (*set_interface)(USBDevice *dev, int interface,
int alt_old, int alt_new); int alt_old, int alt_new);
@ -354,7 +357,8 @@ struct USBPacket {
uint64_t parameter; /* control transfers */ uint64_t parameter; /* control transfers */
bool short_not_ok; bool short_not_ok;
bool int_req; bool int_req;
int result; /* transfer length or USB_RET_* status code */ int status; /* USB_RET_* status code */
int actual_length; /* Number of bytes actually transfered */
/* Internal use by the USB layer. */ /* Internal use by the USB layer. */
USBPacketState state; USBPacketState state;
USBCombinedPacket *combined; USBCombinedPacket *combined;
@ -388,7 +392,7 @@ static inline bool usb_packet_is_inflight(USBPacket *p)
USBDevice *usb_find_device(USBPort *port, uint8_t addr); USBDevice *usb_find_device(USBPort *port, uint8_t addr);
int usb_handle_packet(USBDevice *dev, USBPacket *p); void usb_handle_packet(USBDevice *dev, USBPacket *p);
void usb_packet_complete(USBDevice *dev, USBPacket *p); void usb_packet_complete(USBDevice *dev, USBPacket *p);
void usb_packet_complete_one(USBDevice *dev, USBPacket *p); void usb_packet_complete_one(USBDevice *dev, USBPacket *p);
void usb_cancel_packet(USBPacket * p); void usb_cancel_packet(USBPacket * p);
@ -523,10 +527,10 @@ void usb_device_handle_attach(USBDevice *dev);
void usb_device_handle_reset(USBDevice *dev); void usb_device_handle_reset(USBDevice *dev);
int usb_device_handle_control(USBDevice *dev, USBPacket *p, int request, int value, void usb_device_handle_control(USBDevice *dev, USBPacket *p, int request,
int index, int length, uint8_t *data); int val, int index, int length, uint8_t *data);
int usb_device_handle_data(USBDevice *dev, USBPacket *p); void usb_device_handle_data(USBDevice *dev, USBPacket *p);
void usb_device_set_interface(USBDevice *dev, int interface, void usb_device_set_interface(USBDevice *dev, int interface,
int alt_old, int alt_new); int alt_old, int alt_new);

View File

@ -140,24 +140,21 @@ void usb_device_handle_reset(USBDevice *dev)
} }
} }
int usb_device_handle_control(USBDevice *dev, USBPacket *p, int request, void usb_device_handle_control(USBDevice *dev, USBPacket *p, int request,
int value, int index, int length, uint8_t *data) int value, int index, int length, uint8_t *data)
{ {
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->handle_control) { if (klass->handle_control) {
return klass->handle_control(dev, p, request, value, index, length, klass->handle_control(dev, p, request, value, index, length, data);
data);
} }
return -ENOSYS;
} }
int usb_device_handle_data(USBDevice *dev, USBPacket *p) void usb_device_handle_data(USBDevice *dev, USBPacket *p)
{ {
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
if (klass->handle_data) { if (klass->handle_data) {
return klass->handle_data(dev, p); klass->handle_data(dev, p);
} }
return -ENOSYS;
} }
const char *usb_device_get_product_desc(USBDevice *dev) const char *usb_device_get_product_desc(USBDevice *dev)

View File

@ -31,12 +31,16 @@ static void usb_combined_packet_add(USBCombinedPacket *combined, USBPacket *p)
p->combined = combined; p->combined = combined;
} }
/* Note will free combined when the last packet gets removed */
static void usb_combined_packet_remove(USBCombinedPacket *combined, static void usb_combined_packet_remove(USBCombinedPacket *combined,
USBPacket *p) USBPacket *p)
{ {
assert(p->combined == combined); assert(p->combined == combined);
p->combined = NULL; p->combined = NULL;
QTAILQ_REMOVE(&combined->packets, p, combined_entry); QTAILQ_REMOVE(&combined->packets, p, combined_entry);
if (QTAILQ_EMPTY(&combined->packets)) {
g_free(combined);
}
} }
/* Also handles completion of non combined packets for pipelined input eps */ /* Also handles completion of non combined packets for pipelined input eps */
@ -45,9 +49,8 @@ void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p)
USBCombinedPacket *combined = p->combined; USBCombinedPacket *combined = p->combined;
USBEndpoint *ep = p->ep; USBEndpoint *ep = p->ep;
USBPacket *next; USBPacket *next;
enum { completing, complete, leftover }; int status, actual_length;
int result, state = completing; bool short_not_ok, done = false;
bool short_not_ok;
if (combined == NULL) { if (combined == NULL) {
usb_packet_complete_one(dev, p); usb_packet_complete_one(dev, p);
@ -56,37 +59,39 @@ void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p)
assert(combined->first == p && p == QTAILQ_FIRST(&combined->packets)); assert(combined->first == p && p == QTAILQ_FIRST(&combined->packets));
result = combined->first->result; status = combined->first->status;
actual_length = combined->first->actual_length;
short_not_ok = QTAILQ_LAST(&combined->packets, packets_head)->short_not_ok; short_not_ok = QTAILQ_LAST(&combined->packets, packets_head)->short_not_ok;
QTAILQ_FOREACH_SAFE(p, &combined->packets, combined_entry, next) { QTAILQ_FOREACH_SAFE(p, &combined->packets, combined_entry, next) {
if (state == completing) { if (!done) {
/* Distribute data over uncombined packets */ /* Distribute data over uncombined packets */
if (result >= p->iov.size) { if (actual_length >= p->iov.size) {
p->result = p->iov.size; p->actual_length = p->iov.size;
} else { } else {
/* Send short or error packet to complete the transfer */ /* Send short or error packet to complete the transfer */
p->result = result; p->actual_length = actual_length;
state = complete; done = true;
}
/* Report status on the last packet */
if (done || next == NULL) {
p->status = status;
} else {
p->status = USB_RET_SUCCESS;
} }
p->short_not_ok = short_not_ok; p->short_not_ok = short_not_ok;
/* Note will free combined when the last packet gets removed! */
usb_combined_packet_remove(combined, p); usb_combined_packet_remove(combined, p);
usb_packet_complete_one(dev, p); usb_packet_complete_one(dev, p);
result -= p->result; actual_length -= p->actual_length;
} else { } else {
/* Remove any leftover packets from the queue */ /* Remove any leftover packets from the queue */
state = leftover; p->status = USB_RET_REMOVE_FROM_QUEUE;
p->result = USB_RET_REMOVE_FROM_QUEUE; /* Note will free combined on the last packet! */
dev->port->ops->complete(dev->port, p); dev->port->ops->complete(dev->port, p);
} }
} }
/* /* Do not use combined here, it has been freed! */
* If we had leftover packets the hcd driver will have cancelled them
* and usb_combined_packet_cancel has already freed combined!
*/
if (state != leftover) {
g_free(combined);
}
leave: leave:
/* Check if there are packets in the queue waiting for our completion */ /* Check if there are packets in the queue waiting for our completion */
usb_ep_combine_input_packets(ep); usb_ep_combine_input_packets(ep);
@ -97,14 +102,13 @@ void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p)
{ {
USBCombinedPacket *combined = p->combined; USBCombinedPacket *combined = p->combined;
assert(combined != NULL); assert(combined != NULL);
USBPacket *first = p->combined->first;
/* Note will free combined on the last packet! */
usb_combined_packet_remove(combined, p); usb_combined_packet_remove(combined, p);
if (p == combined->first) { if (p == first) {
usb_device_cancel_packet(dev, p); usb_device_cancel_packet(dev, p);
} }
if (QTAILQ_EMPTY(&combined->packets)) {
g_free(combined);
}
} }
/* /*
@ -117,7 +121,7 @@ void usb_ep_combine_input_packets(USBEndpoint *ep)
{ {
USBPacket *p, *u, *next, *prev = NULL, *first = NULL; USBPacket *p, *u, *next, *prev = NULL, *first = NULL;
USBPort *port = ep->dev->port; USBPort *port = ep->dev->port;
int ret, totalsize; int totalsize;
assert(ep->pipeline); assert(ep->pipeline);
assert(ep->pid == USB_TOKEN_IN); assert(ep->pid == USB_TOKEN_IN);
@ -125,7 +129,7 @@ void usb_ep_combine_input_packets(USBEndpoint *ep)
QTAILQ_FOREACH_SAFE(p, &ep->queue, queue, next) { QTAILQ_FOREACH_SAFE(p, &ep->queue, queue, next) {
/* Empty the queue on a halt */ /* Empty the queue on a halt */
if (ep->halted) { if (ep->halted) {
p->result = USB_RET_REMOVE_FROM_QUEUE; p->status = USB_RET_REMOVE_FROM_QUEUE;
port->ops->complete(port, p); port->ops->complete(port, p);
continue; continue;
} }
@ -166,8 +170,8 @@ void usb_ep_combine_input_packets(USBEndpoint *ep)
next == NULL || next == NULL ||
/* Work around for Linux usbfs bulk splitting + migration */ /* Work around for Linux usbfs bulk splitting + migration */
(totalsize == 16348 && p->int_req)) { (totalsize == 16348 && p->int_req)) {
ret = usb_device_handle_data(ep->dev, first); usb_device_handle_data(ep->dev, first);
assert(ret == USB_RET_ASYNC); assert(first->status == USB_RET_ASYNC);
if (first->combined) { if (first->combined) {
QTAILQ_FOREACH(u, &first->combined->packets, combined_entry) { QTAILQ_FOREACH(u, &first->combined->packets, combined_entry) {
usb_packet_set_state(u, USB_PACKET_ASYNC); usb_packet_set_state(u, USB_PACKET_ASYNC);

View File

@ -97,17 +97,17 @@ void usb_wakeup(USBEndpoint *ep)
#define SETUP_STATE_ACK 3 #define SETUP_STATE_ACK 3
#define SETUP_STATE_PARAM 4 #define SETUP_STATE_PARAM 4
static int do_token_setup(USBDevice *s, USBPacket *p) static void do_token_setup(USBDevice *s, USBPacket *p)
{ {
int request, value, index; int request, value, index;
int ret = 0;
if (p->iov.size != 8) { if (p->iov.size != 8) {
return USB_RET_STALL; p->status = USB_RET_STALL;
return;
} }
usb_packet_copy(p, s->setup_buf, p->iov.size); usb_packet_copy(p, s->setup_buf, p->iov.size);
p->result = 0; p->actual_length = 0;
s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6];
s->setup_index = 0; s->setup_index = 0;
@ -116,24 +116,26 @@ static int do_token_setup(USBDevice *s, USBPacket *p)
index = (s->setup_buf[5] << 8) | s->setup_buf[4]; index = (s->setup_buf[5] << 8) | s->setup_buf[4];
if (s->setup_buf[0] & USB_DIR_IN) { if (s->setup_buf[0] & USB_DIR_IN) {
ret = usb_device_handle_control(s, p, request, value, index, usb_device_handle_control(s, p, request, value, index,
s->setup_len, s->data_buf); s->setup_len, s->data_buf);
if (ret == USB_RET_ASYNC) { if (p->status == USB_RET_ASYNC) {
s->setup_state = SETUP_STATE_SETUP; s->setup_state = SETUP_STATE_SETUP;
return USB_RET_ASYNC; }
if (p->status != USB_RET_SUCCESS) {
return;
} }
if (ret < 0)
return ret;
if (ret < s->setup_len) if (p->actual_length < s->setup_len) {
s->setup_len = ret; s->setup_len = p->actual_length;
}
s->setup_state = SETUP_STATE_DATA; s->setup_state = SETUP_STATE_DATA;
} else { } else {
if (s->setup_len > sizeof(s->data_buf)) { if (s->setup_len > sizeof(s->data_buf)) {
fprintf(stderr, fprintf(stderr,
"usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
s->setup_len, sizeof(s->data_buf)); s->setup_len, sizeof(s->data_buf));
return USB_RET_STALL; p->status = USB_RET_STALL;
return;
} }
if (s->setup_len == 0) if (s->setup_len == 0)
s->setup_state = SETUP_STATE_ACK; s->setup_state = SETUP_STATE_ACK;
@ -141,13 +143,12 @@ static int do_token_setup(USBDevice *s, USBPacket *p)
s->setup_state = SETUP_STATE_DATA; s->setup_state = SETUP_STATE_DATA;
} }
return ret; p->actual_length = 8;
} }
static int do_token_in(USBDevice *s, USBPacket *p) static void do_token_in(USBDevice *s, USBPacket *p)
{ {
int request, value, index; int request, value, index;
int ret = 0;
assert(p->ep->nr == 0); assert(p->ep->nr == 0);
@ -158,19 +159,15 @@ static int do_token_in(USBDevice *s, USBPacket *p)
switch(s->setup_state) { switch(s->setup_state) {
case SETUP_STATE_ACK: case SETUP_STATE_ACK:
if (!(s->setup_buf[0] & USB_DIR_IN)) { if (!(s->setup_buf[0] & USB_DIR_IN)) {
ret = usb_device_handle_control(s, p, request, value, index, usb_device_handle_control(s, p, request, value, index,
s->setup_len, s->data_buf); s->setup_len, s->data_buf);
if (ret == USB_RET_ASYNC) { if (p->status == USB_RET_ASYNC) {
return USB_RET_ASYNC; return;
} }
s->setup_state = SETUP_STATE_IDLE; s->setup_state = SETUP_STATE_IDLE;
if (ret > 0) p->actual_length = 0;
return 0;
return ret;
} }
break;
/* return 0 byte */
return 0;
case SETUP_STATE_DATA: case SETUP_STATE_DATA:
if (s->setup_buf[0] & USB_DIR_IN) { if (s->setup_buf[0] & USB_DIR_IN) {
@ -180,20 +177,21 @@ static int do_token_in(USBDevice *s, USBPacket *p)
} }
usb_packet_copy(p, s->data_buf + s->setup_index, len); usb_packet_copy(p, s->data_buf + s->setup_index, len);
s->setup_index += len; s->setup_index += len;
if (s->setup_index >= s->setup_len) if (s->setup_index >= s->setup_len) {
s->setup_state = SETUP_STATE_ACK; s->setup_state = SETUP_STATE_ACK;
return len; }
return;
} }
s->setup_state = SETUP_STATE_IDLE; s->setup_state = SETUP_STATE_IDLE;
return USB_RET_STALL; p->status = USB_RET_STALL;
break;
default: default:
return USB_RET_STALL; p->status = USB_RET_STALL;
} }
} }
static int do_token_out(USBDevice *s, USBPacket *p) static void do_token_out(USBDevice *s, USBPacket *p)
{ {
assert(p->ep->nr == 0); assert(p->ep->nr == 0);
@ -205,7 +203,7 @@ static int do_token_out(USBDevice *s, USBPacket *p)
} else { } else {
/* ignore additional output */ /* ignore additional output */
} }
return 0; break;
case SETUP_STATE_DATA: case SETUP_STATE_DATA:
if (!(s->setup_buf[0] & USB_DIR_IN)) { if (!(s->setup_buf[0] & USB_DIR_IN)) {
@ -215,23 +213,23 @@ static int do_token_out(USBDevice *s, USBPacket *p)
} }
usb_packet_copy(p, s->data_buf + s->setup_index, len); usb_packet_copy(p, s->data_buf + s->setup_index, len);
s->setup_index += len; s->setup_index += len;
if (s->setup_index >= s->setup_len) if (s->setup_index >= s->setup_len) {
s->setup_state = SETUP_STATE_ACK; s->setup_state = SETUP_STATE_ACK;
return len; }
return;
} }
s->setup_state = SETUP_STATE_IDLE; s->setup_state = SETUP_STATE_IDLE;
return USB_RET_STALL; p->status = USB_RET_STALL;
break;
default: default:
return USB_RET_STALL; p->status = USB_RET_STALL;
} }
} }
static int do_parameter(USBDevice *s, USBPacket *p) static void do_parameter(USBDevice *s, USBPacket *p)
{ {
int request, value, index; int i, request, value, index;
int i, ret = 0;
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
s->setup_buf[i] = p->parameter >> (i*8); s->setup_buf[i] = p->parameter >> (i*8);
@ -249,27 +247,27 @@ static int do_parameter(USBDevice *s, USBPacket *p)
fprintf(stderr, fprintf(stderr,
"usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
s->setup_len, sizeof(s->data_buf)); s->setup_len, sizeof(s->data_buf));
return USB_RET_STALL; p->status = USB_RET_STALL;
return;
} }
if (p->pid == USB_TOKEN_OUT) { if (p->pid == USB_TOKEN_OUT) {
usb_packet_copy(p, s->data_buf, s->setup_len); usb_packet_copy(p, s->data_buf, s->setup_len);
} }
ret = usb_device_handle_control(s, p, request, value, index, usb_device_handle_control(s, p, request, value, index,
s->setup_len, s->data_buf); s->setup_len, s->data_buf);
if (ret < 0) { if (p->status == USB_RET_ASYNC) {
return ret; return;
} }
if (ret < s->setup_len) { if (p->actual_length < s->setup_len) {
s->setup_len = ret; s->setup_len = p->actual_length;
} }
if (p->pid == USB_TOKEN_IN) { if (p->pid == USB_TOKEN_IN) {
p->actual_length = 0;
usb_packet_copy(p, s->data_buf, s->setup_len); usb_packet_copy(p, s->data_buf, s->setup_len);
} }
return ret;
} }
/* ctrl complete function for devices which use usb_generic_handle_packet and /* ctrl complete function for devices which use usb_generic_handle_packet and
@ -278,30 +276,30 @@ static int do_parameter(USBDevice *s, USBPacket *p)
usb_packet_complete to complete their async control packets. */ usb_packet_complete to complete their async control packets. */
void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p) void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p)
{ {
if (p->result < 0) { if (p->status < 0) {
s->setup_state = SETUP_STATE_IDLE; s->setup_state = SETUP_STATE_IDLE;
} }
switch (s->setup_state) { switch (s->setup_state) {
case SETUP_STATE_SETUP: case SETUP_STATE_SETUP:
if (p->result < s->setup_len) { if (p->actual_length < s->setup_len) {
s->setup_len = p->result; s->setup_len = p->actual_length;
} }
s->setup_state = SETUP_STATE_DATA; s->setup_state = SETUP_STATE_DATA;
p->result = 8; p->actual_length = 8;
break; break;
case SETUP_STATE_ACK: case SETUP_STATE_ACK:
s->setup_state = SETUP_STATE_IDLE; s->setup_state = SETUP_STATE_IDLE;
p->result = 0; p->actual_length = 0;
break; break;
case SETUP_STATE_PARAM: case SETUP_STATE_PARAM:
if (p->result < s->setup_len) { if (p->actual_length < s->setup_len) {
s->setup_len = p->result; s->setup_len = p->actual_length;
} }
if (p->pid == USB_TOKEN_IN) { if (p->pid == USB_TOKEN_IN) {
p->result = 0; p->actual_length = 0;
usb_packet_copy(p, s->data_buf, s->setup_len); usb_packet_copy(p, s->data_buf, s->setup_len);
} }
break; break;
@ -342,40 +340,57 @@ USBDevice *usb_find_device(USBPort *port, uint8_t addr)
return usb_device_find_device(dev, addr); return usb_device_find_device(dev, addr);
} }
static int usb_process_one(USBPacket *p) static void usb_process_one(USBPacket *p)
{ {
USBDevice *dev = p->ep->dev; USBDevice *dev = p->ep->dev;
/*
* Handlers expect status to be initialized to USB_RET_SUCCESS, but it
* can be USB_RET_NAK here from a previous usb_process_one() call,
* or USB_RET_ASYNC from going through usb_queue_one().
*/
p->status = USB_RET_SUCCESS;
if (p->ep->nr == 0) { if (p->ep->nr == 0) {
/* control pipe */ /* control pipe */
if (p->parameter) { if (p->parameter) {
return do_parameter(dev, p); do_parameter(dev, p);
return;
} }
switch (p->pid) { switch (p->pid) {
case USB_TOKEN_SETUP: case USB_TOKEN_SETUP:
return do_token_setup(dev, p); do_token_setup(dev, p);
break;
case USB_TOKEN_IN: case USB_TOKEN_IN:
return do_token_in(dev, p); do_token_in(dev, p);
break;
case USB_TOKEN_OUT: case USB_TOKEN_OUT:
return do_token_out(dev, p); do_token_out(dev, p);
break;
default: default:
return USB_RET_STALL; p->status = USB_RET_STALL;
} }
} else { } else {
/* data pipe */ /* data pipe */
return usb_device_handle_data(dev, p); usb_device_handle_data(dev, p);
} }
} }
/* Hand over a packet to a device for processing. Return value static void usb_queue_one(USBPacket *p)
{
usb_packet_set_state(p, USB_PACKET_QUEUED);
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
p->status = USB_RET_ASYNC;
}
/* Hand over a packet to a device for processing. p->status ==
USB_RET_ASYNC indicates the processing isn't finished yet, the USB_RET_ASYNC indicates the processing isn't finished yet, the
driver will call usb_packet_complete() when done processing it. */ driver will call usb_packet_complete() when done processing it. */
int usb_handle_packet(USBDevice *dev, USBPacket *p) void usb_handle_packet(USBDevice *dev, USBPacket *p)
{ {
int ret;
if (dev == NULL) { if (dev == NULL) {
return USB_RET_NODEV; p->status = USB_RET_NODEV;
return;
} }
assert(dev == p->ep->dev); assert(dev == p->ep->dev);
assert(dev->state == USB_STATE_DEFAULT); assert(dev->state == USB_STATE_DEFAULT);
@ -389,32 +404,26 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
} }
if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) { if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) {
ret = usb_process_one(p); usb_process_one(p);
if (ret == USB_RET_ASYNC) { if (p->status == USB_RET_ASYNC) {
assert(p->ep->type != USB_ENDPOINT_XFER_ISOC); assert(p->ep->type != USB_ENDPOINT_XFER_ISOC);
usb_packet_set_state(p, USB_PACKET_ASYNC); usb_packet_set_state(p, USB_PACKET_ASYNC);
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue); QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
} else if (ret == USB_RET_ADD_TO_QUEUE) { } else if (p->status == USB_RET_ADD_TO_QUEUE) {
usb_packet_set_state(p, USB_PACKET_QUEUED); usb_queue_one(p);
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
ret = USB_RET_ASYNC;
} else { } else {
/* /*
* When pipelining is enabled usb-devices must always return async, * When pipelining is enabled usb-devices must always return async,
* otherwise packets can complete out of order! * otherwise packets can complete out of order!
*/ */
assert(!p->ep->pipeline || QTAILQ_EMPTY(&p->ep->queue)); assert(!p->ep->pipeline || QTAILQ_EMPTY(&p->ep->queue));
if (ret != USB_RET_NAK) { if (p->status != USB_RET_NAK) {
p->result = ret;
usb_packet_set_state(p, USB_PACKET_COMPLETE); usb_packet_set_state(p, USB_PACKET_COMPLETE);
} }
} }
} else { } else {
ret = USB_RET_ASYNC; usb_queue_one(p);
usb_packet_set_state(p, USB_PACKET_QUEUED);
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
} }
return ret;
} }
void usb_packet_complete_one(USBDevice *dev, USBPacket *p) void usb_packet_complete_one(USBDevice *dev, USBPacket *p)
@ -422,9 +431,10 @@ void usb_packet_complete_one(USBDevice *dev, USBPacket *p)
USBEndpoint *ep = p->ep; USBEndpoint *ep = p->ep;
assert(QTAILQ_FIRST(&ep->queue) == p); assert(QTAILQ_FIRST(&ep->queue) == p);
assert(p->result != USB_RET_ASYNC && p->result != USB_RET_NAK); assert(p->status != USB_RET_ASYNC && p->status != USB_RET_NAK);
if (p->result < 0 || (p->short_not_ok && (p->result < p->iov.size))) { if (p->status != USB_RET_SUCCESS ||
(p->short_not_ok && (p->actual_length < p->iov.size))) {
ep->halted = true; ep->halted = true;
} }
usb_packet_set_state(p, USB_PACKET_COMPLETE); usb_packet_set_state(p, USB_PACKET_COMPLETE);
@ -438,7 +448,6 @@ void usb_packet_complete_one(USBDevice *dev, USBPacket *p)
void usb_packet_complete(USBDevice *dev, USBPacket *p) void usb_packet_complete(USBDevice *dev, USBPacket *p)
{ {
USBEndpoint *ep = p->ep; USBEndpoint *ep = p->ep;
int ret;
usb_packet_check_state(p, USB_PACKET_ASYNC); usb_packet_check_state(p, USB_PACKET_ASYNC);
usb_packet_complete_one(dev, p); usb_packet_complete_one(dev, p);
@ -447,7 +456,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
p = QTAILQ_FIRST(&ep->queue); p = QTAILQ_FIRST(&ep->queue);
if (ep->halted) { if (ep->halted) {
/* Empty the queue on a halt */ /* Empty the queue on a halt */
p->result = USB_RET_REMOVE_FROM_QUEUE; p->status = USB_RET_REMOVE_FROM_QUEUE;
dev->port->ops->complete(dev->port, p); dev->port->ops->complete(dev->port, p);
continue; continue;
} }
@ -455,12 +464,11 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
break; break;
} }
usb_packet_check_state(p, USB_PACKET_QUEUED); usb_packet_check_state(p, USB_PACKET_QUEUED);
ret = usb_process_one(p); usb_process_one(p);
if (ret == USB_RET_ASYNC) { if (p->status == USB_RET_ASYNC) {
usb_packet_set_state(p, USB_PACKET_ASYNC); usb_packet_set_state(p, USB_PACKET_ASYNC);
break; break;
} }
p->result = ret;
usb_packet_complete_one(ep->dev, p); usb_packet_complete_one(ep->dev, p);
} }
} }
@ -541,7 +549,8 @@ void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id,
p->id = id; p->id = id;
p->pid = pid; p->pid = pid;
p->ep = ep; p->ep = ep;
p->result = 0; p->status = USB_RET_SUCCESS;
p->actual_length = 0;
p->parameter = 0; p->parameter = 0;
p->short_not_ok = short_not_ok; p->short_not_ok = short_not_ok;
p->int_req = int_req; p->int_req = int_req;
@ -557,31 +566,31 @@ void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len)
void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes) void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes)
{ {
assert(p->result >= 0); assert(p->actual_length >= 0);
assert(p->result + bytes <= p->iov.size); assert(p->actual_length + bytes <= p->iov.size);
switch (p->pid) { switch (p->pid) {
case USB_TOKEN_SETUP: case USB_TOKEN_SETUP:
case USB_TOKEN_OUT: case USB_TOKEN_OUT:
iov_to_buf(p->iov.iov, p->iov.niov, p->result, ptr, bytes); iov_to_buf(p->iov.iov, p->iov.niov, p->actual_length, ptr, bytes);
break; break;
case USB_TOKEN_IN: case USB_TOKEN_IN:
iov_from_buf(p->iov.iov, p->iov.niov, p->result, ptr, bytes); iov_from_buf(p->iov.iov, p->iov.niov, p->actual_length, ptr, bytes);
break; break;
default: default:
fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid); fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid);
abort(); abort();
} }
p->result += bytes; p->actual_length += bytes;
} }
void usb_packet_skip(USBPacket *p, size_t bytes) void usb_packet_skip(USBPacket *p, size_t bytes)
{ {
assert(p->result >= 0); assert(p->actual_length >= 0);
assert(p->result + bytes <= p->iov.size); assert(p->actual_length + bytes <= p->iov.size);
if (p->pid == USB_TOKEN_IN) { if (p->pid == USB_TOKEN_IN) {
iov_memset(p->iov.iov, p->iov.niov, p->result, 0, bytes); iov_memset(p->iov.iov, p->iov.niov, p->actual_length, 0, bytes);
} }
p->result += bytes; p->actual_length += bytes;
} }
void usb_packet_cleanup(USBPacket *p) void usb_packet_cleanup(USBPacket *p)

View File

@ -626,7 +626,8 @@ int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len)
return pos; return pos;
} }
int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len) int usb_desc_get_descriptor(USBDevice *dev, USBPacket *p,
int value, uint8_t *dest, size_t len)
{ {
const USBDesc *desc = usb_device_get_usb_desc(dev); const USBDesc *desc = usb_device_get_usb_desc(dev);
const USBDescDevice *other_dev; const USBDescDevice *other_dev;
@ -696,6 +697,8 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
ret = len; ret = len;
} }
memcpy(dest, buf, ret); memcpy(dest, buf, ret);
p->actual_length = ret;
ret = 0;
} }
return ret; return ret;
} }
@ -715,7 +718,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
break; break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR: case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
ret = usb_desc_get_descriptor(dev, value, data, length); ret = usb_desc_get_descriptor(dev, p, value, data, length);
break; break;
case DeviceRequest | USB_REQ_GET_CONFIGURATION: case DeviceRequest | USB_REQ_GET_CONFIGURATION:
@ -724,7 +727,8 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
* the non zero value of bConfigurationValue. * the non zero value of bConfigurationValue.
*/ */
data[0] = dev->config ? dev->config->bConfigurationValue : 0; data[0] = dev->config ? dev->config->bConfigurationValue : 0;
ret = 1; p->actual_length = 1;
ret = 0;
break; break;
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
ret = usb_desc_set_config(dev, value); ret = usb_desc_set_config(dev, value);
@ -749,7 +753,8 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP; data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP;
} }
data[1] = 0x00; data[1] = 0x00;
ret = 2; p->actual_length = 2;
ret = 0;
break; break;
} }
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
@ -772,7 +777,8 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
break; break;
} }
data[0] = dev->altsetting[index]; data[0] = dev->altsetting[index];
ret = 1; p->actual_length = 1;
ret = 0;
break; break;
case InterfaceOutRequest | USB_REQ_SET_INTERFACE: case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
ret = usb_desc_set_interface(dev, index, value); ret = usb_desc_set_interface(dev, index, value);

View File

@ -216,7 +216,8 @@ void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str);
void usb_desc_create_serial(USBDevice *dev); void usb_desc_create_serial(USBDevice *dev);
const char *usb_desc_get_string(USBDevice *dev, uint8_t index); const char *usb_desc_get_string(USBDevice *dev, uint8_t index);
int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len); int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len);
int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len); int usb_desc_get_descriptor(USBDevice *dev, USBPacket *p,
int value, uint8_t *dest, size_t len);
int usb_desc_handle_control(USBDevice *dev, USBPacket *p, int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data); int request, int value, int index, int length, uint8_t *data);

View File

@ -503,7 +503,7 @@ static int usb_audio_set_control(USBAudioState *s, uint8_t attrib,
return ret; return ret;
} }
static int usb_audio_handle_control(USBDevice *dev, USBPacket *p, static void usb_audio_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int request, int value, int index,
int length, uint8_t *data) int length, uint8_t *data)
{ {
@ -518,7 +518,7 @@ static int usb_audio_handle_control(USBDevice *dev, USBPacket *p,
ret = usb_desc_handle_control(dev, p, request, value, index, length, data); ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) { if (ret >= 0) {
return ret; return;
} }
switch (request) { switch (request) {
@ -534,6 +534,7 @@ static int usb_audio_handle_control(USBDevice *dev, USBPacket *p,
} }
goto fail; goto fail;
} }
p->actual_length = ret;
break; break;
case ClassInterfaceOutRequest | CR_SET_CUR: case ClassInterfaceOutRequest | CR_SET_CUR:
@ -557,10 +558,9 @@ fail:
"request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n", "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n",
request, value, index, length); request, value, index, length);
} }
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
return ret;
} }
static void usb_audio_set_interface(USBDevice *dev, int iface, static void usb_audio_set_interface(USBDevice *dev, int iface,
@ -583,50 +583,35 @@ static void usb_audio_handle_reset(USBDevice *dev)
usb_audio_set_output_altset(s, ALTSET_OFF); usb_audio_set_output_altset(s, ALTSET_OFF);
} }
static int usb_audio_handle_dataout(USBAudioState *s, USBPacket *p) static void usb_audio_handle_dataout(USBAudioState *s, USBPacket *p)
{ {
int rc;
if (s->out.altset == ALTSET_OFF) { if (s->out.altset == ALTSET_OFF) {
return USB_RET_STALL; p->status = USB_RET_STALL;
return;
} }
rc = streambuf_put(&s->out.buf, p); streambuf_put(&s->out.buf, p);
if (rc < p->iov.size && s->debug > 1) { if (p->actual_length < p->iov.size && s->debug > 1) {
fprintf(stderr, "usb-audio: output overrun (%zd bytes)\n", fprintf(stderr, "usb-audio: output overrun (%zd bytes)\n",
p->iov.size - rc); p->iov.size - p->actual_length);
} }
return 0;
} }
static int usb_audio_handle_data(USBDevice *dev, USBPacket *p) static void usb_audio_handle_data(USBDevice *dev, USBPacket *p)
{ {
USBAudioState *s = (USBAudioState *) dev; USBAudioState *s = (USBAudioState *) dev;
int ret = 0;
switch (p->pid) { if (p->pid == USB_TOKEN_OUT && p->ep->nr == 1) {
case USB_TOKEN_OUT: usb_audio_handle_dataout(s, p);
switch (p->ep->nr) { return;
case 1:
ret = usb_audio_handle_dataout(s, p);
break;
default:
goto fail;
}
break;
default:
fail:
ret = USB_RET_STALL;
break;
} }
if (ret == USB_RET_STALL && s->debug) {
p->status = USB_RET_STALL;
if (s->debug) {
fprintf(stderr, "usb-audio: failed data transaction: " fprintf(stderr, "usb-audio: failed data transaction: "
"pid 0x%x ep 0x%x len 0x%zx\n", "pid 0x%x ep 0x%x len 0x%zx\n",
p->pid, p->ep->nr, p->iov.size); p->pid, p->ep->nr, p->iov.size);
} }
return ret;
} }
static void usb_audio_handle_destroy(USBDevice *dev) static void usb_audio_handle_destroy(USBDevice *dev)

View File

@ -285,13 +285,15 @@ static void usb_bt_fifo_enqueue(struct usb_hci_in_fifo_s *fifo,
fifo->fifo[off].len = len; fifo->fifo[off].len = len;
} }
static inline int usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo, static inline void usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo,
USBPacket *p) USBPacket *p)
{ {
int len; int len;
if (likely(!fifo->len)) if (likely(!fifo->len)) {
return USB_RET_STALL; p->status = USB_RET_STALL;
return;
}
len = MIN(p->iov.size, fifo->fifo[fifo->start].len); len = MIN(p->iov.size, fifo->fifo[fifo->start].len);
usb_packet_copy(p, fifo->fifo[fifo->start].data, len); usb_packet_copy(p, fifo->fifo[fifo->start].data, len);
@ -310,8 +312,6 @@ static inline int usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo,
fifo->dstart = 0; fifo->dstart = 0;
fifo->dsize = DFIFO_LEN_MASK + 1; fifo->dsize = DFIFO_LEN_MASK + 1;
} }
return len;
} }
static inline void usb_bt_fifo_out_enqueue(struct USBBtState *s, static inline void usb_bt_fifo_out_enqueue(struct USBBtState *s,
@ -363,7 +363,7 @@ static void usb_bt_handle_reset(USBDevice *dev)
s->outsco.len = 0; s->outsco.len = 0;
} }
static int usb_bt_handle_control(USBDevice *dev, USBPacket *p, static void usb_bt_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data) int request, int value, int index, int length, uint8_t *data)
{ {
struct USBBtState *s = (struct USBBtState *) dev->opaque; struct USBBtState *s = (struct USBBtState *) dev->opaque;
@ -382,16 +382,15 @@ static int usb_bt_handle_control(USBDevice *dev, USBPacket *p,
usb_bt_fifo_reset(&s->sco); usb_bt_fifo_reset(&s->sco);
break; break;
} }
return ret; return;
} }
ret = 0;
switch (request) { switch (request) {
case InterfaceRequest | USB_REQ_GET_STATUS: case InterfaceRequest | USB_REQ_GET_STATUS:
case EndpointRequest | USB_REQ_GET_STATUS: case EndpointRequest | USB_REQ_GET_STATUS:
data[0] = 0x00; data[0] = 0x00;
data[1] = 0x00; data[1] = 0x00;
ret = 2; p->actual_length = 2;
break; break;
case InterfaceOutRequest | USB_REQ_CLEAR_FEATURE: case InterfaceOutRequest | USB_REQ_CLEAR_FEATURE:
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
@ -407,16 +406,14 @@ static int usb_bt_handle_control(USBDevice *dev, USBPacket *p,
break; break;
default: default:
fail: fail:
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
return ret;
} }
static int usb_bt_handle_data(USBDevice *dev, USBPacket *p) static void usb_bt_handle_data(USBDevice *dev, USBPacket *p)
{ {
struct USBBtState *s = (struct USBBtState *) dev->opaque; struct USBBtState *s = (struct USBBtState *) dev->opaque;
int ret = 0;
if (!s->config) if (!s->config)
goto fail; goto fail;
@ -425,15 +422,15 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p)
case USB_TOKEN_IN: case USB_TOKEN_IN:
switch (p->ep->nr) { switch (p->ep->nr) {
case USB_EVT_EP: case USB_EVT_EP:
ret = usb_bt_fifo_dequeue(&s->evt, p); usb_bt_fifo_dequeue(&s->evt, p);
break; break;
case USB_ACL_EP: case USB_ACL_EP:
ret = usb_bt_fifo_dequeue(&s->acl, p); usb_bt_fifo_dequeue(&s->acl, p);
break; break;
case USB_SCO_EP: case USB_SCO_EP:
ret = usb_bt_fifo_dequeue(&s->sco, p); usb_bt_fifo_dequeue(&s->sco, p);
break; break;
default: default:
@ -460,11 +457,9 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p)
default: default:
fail: fail:
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
return ret;
} }
static void usb_bt_out_hci_packet_event(void *opaque, static void usb_bt_out_hci_packet_event(void *opaque,

View File

@ -371,7 +371,7 @@ static void usb_hid_handle_reset(USBDevice *dev)
hid_reset(&us->hid); hid_reset(&us->hid);
} }
static int usb_hid_handle_control(USBDevice *dev, USBPacket *p, static void usb_hid_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data) int request, int value, int index, int length, uint8_t *data)
{ {
USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
@ -380,10 +380,9 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
ret = usb_desc_handle_control(dev, p, request, value, index, length, data); ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) { if (ret >= 0) {
return ret; return;
} }
ret = 0;
switch (request) { switch (request) {
/* hid specific requests */ /* hid specific requests */
case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: case InterfaceRequest | USB_REQ_GET_DESCRIPTOR:
@ -392,15 +391,15 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
if (hs->kind == HID_MOUSE) { if (hs->kind == HID_MOUSE) {
memcpy(data, qemu_mouse_hid_report_descriptor, memcpy(data, qemu_mouse_hid_report_descriptor,
sizeof(qemu_mouse_hid_report_descriptor)); sizeof(qemu_mouse_hid_report_descriptor));
ret = sizeof(qemu_mouse_hid_report_descriptor); p->actual_length = sizeof(qemu_mouse_hid_report_descriptor);
} else if (hs->kind == HID_TABLET) { } else if (hs->kind == HID_TABLET) {
memcpy(data, qemu_tablet_hid_report_descriptor, memcpy(data, qemu_tablet_hid_report_descriptor,
sizeof(qemu_tablet_hid_report_descriptor)); sizeof(qemu_tablet_hid_report_descriptor));
ret = sizeof(qemu_tablet_hid_report_descriptor); p->actual_length = sizeof(qemu_tablet_hid_report_descriptor);
} else if (hs->kind == HID_KEYBOARD) { } else if (hs->kind == HID_KEYBOARD) {
memcpy(data, qemu_keyboard_hid_report_descriptor, memcpy(data, qemu_keyboard_hid_report_descriptor,
sizeof(qemu_keyboard_hid_report_descriptor)); sizeof(qemu_keyboard_hid_report_descriptor));
ret = sizeof(qemu_keyboard_hid_report_descriptor); p->actual_length = sizeof(qemu_keyboard_hid_report_descriptor);
} }
break; break;
default: default:
@ -409,14 +408,14 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
break; break;
case GET_REPORT: case GET_REPORT:
if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
ret = hid_pointer_poll(hs, data, length); p->actual_length = hid_pointer_poll(hs, data, length);
} else if (hs->kind == HID_KEYBOARD) { } else if (hs->kind == HID_KEYBOARD) {
ret = hid_keyboard_poll(hs, data, length); p->actual_length = hid_keyboard_poll(hs, data, length);
} }
break; break;
case SET_REPORT: case SET_REPORT:
if (hs->kind == HID_KEYBOARD) { if (hs->kind == HID_KEYBOARD) {
ret = hid_keyboard_write(hs, data, length); p->actual_length = hid_keyboard_write(hs, data, length);
} else { } else {
goto fail; goto fail;
} }
@ -425,19 +424,18 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) { if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) {
goto fail; goto fail;
} }
ret = 1;
data[0] = hs->protocol; data[0] = hs->protocol;
p->actual_length = 1;
break; break;
case SET_PROTOCOL: case SET_PROTOCOL:
if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) { if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) {
goto fail; goto fail;
} }
ret = 0;
hs->protocol = value; hs->protocol = value;
break; break;
case GET_IDLE: case GET_IDLE:
ret = 1;
data[0] = hs->idle; data[0] = hs->idle;
p->actual_length = 1;
break; break;
case SET_IDLE: case SET_IDLE:
hs->idle = (uint8_t) (value >> 8); hs->idle = (uint8_t) (value >> 8);
@ -445,22 +443,20 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
hid_pointer_activate(hs); hid_pointer_activate(hs);
} }
ret = 0;
break; break;
default: default:
fail: fail:
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
return ret;
} }
static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) static void usb_hid_handle_data(USBDevice *dev, USBPacket *p)
{ {
USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev); USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
HIDState *hs = &us->hid; HIDState *hs = &us->hid;
uint8_t buf[p->iov.size]; uint8_t buf[p->iov.size];
int ret = 0; int len = 0;
switch (p->pid) { switch (p->pid) {
case USB_TOKEN_IN: case USB_TOKEN_IN:
@ -471,15 +467,16 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
} }
if (!hid_has_events(hs) && if (!hid_has_events(hs) &&
(!hs->idle || hs->next_idle_clock - curtime > 0)) { (!hs->idle || hs->next_idle_clock - curtime > 0)) {
return USB_RET_NAK; p->status = USB_RET_NAK;
return;
} }
hid_set_next_idle(hs, curtime); hid_set_next_idle(hs, curtime);
if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) { if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
ret = hid_pointer_poll(hs, buf, p->iov.size); len = hid_pointer_poll(hs, buf, p->iov.size);
} else if (hs->kind == HID_KEYBOARD) { } else if (hs->kind == HID_KEYBOARD) {
ret = hid_keyboard_poll(hs, buf, p->iov.size); len = hid_keyboard_poll(hs, buf, p->iov.size);
} }
usb_packet_copy(p, buf, ret); usb_packet_copy(p, buf, len);
} else { } else {
goto fail; goto fail;
} }
@ -487,10 +484,9 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
case USB_TOKEN_OUT: case USB_TOKEN_OUT:
default: default:
fail: fail:
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
return ret;
} }
static void usb_hid_handle_destroy(USBDevice *dev) static void usb_hid_handle_destroy(USBDevice *dev)

View File

@ -288,7 +288,7 @@ static const char *feature_name(int feature)
return name[feature] ?: "?"; return name[feature] ?: "?";
} }
static int usb_hub_handle_control(USBDevice *dev, USBPacket *p, static void usb_hub_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data) int request, int value, int index, int length, uint8_t *data)
{ {
USBHubState *s = (USBHubState *)dev; USBHubState *s = (USBHubState *)dev;
@ -298,7 +298,7 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
ret = usb_desc_handle_control(dev, p, request, value, index, length, data); ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) { if (ret >= 0) {
return ret; return;
} }
switch(request) { switch(request) {
@ -306,7 +306,6 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
if (value == 0 && index != 0x81) { /* clear ep halt */ if (value == 0 && index != 0x81) { /* clear ep halt */
goto fail; goto fail;
} }
ret = 0;
break; break;
/* usb specific requests */ /* usb specific requests */
case GetHubStatus: case GetHubStatus:
@ -314,7 +313,7 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
data[1] = 0; data[1] = 0;
data[2] = 0; data[2] = 0;
data[3] = 0; data[3] = 0;
ret = 4; p->actual_length = 4;
break; break;
case GetPortStatus: case GetPortStatus:
{ {
@ -331,16 +330,14 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
data[1] = port->wPortStatus >> 8; data[1] = port->wPortStatus >> 8;
data[2] = port->wPortChange; data[2] = port->wPortChange;
data[3] = port->wPortChange >> 8; data[3] = port->wPortChange >> 8;
ret = 4; p->actual_length = 4;
} }
break; break;
case SetHubFeature: case SetHubFeature:
case ClearHubFeature: case ClearHubFeature:
if (value == 0 || value == 1) { if (value != 0 && value != 1) {
} else {
goto fail; goto fail;
} }
ret = 0;
break; break;
case SetPortFeature: case SetPortFeature:
{ {
@ -373,7 +370,6 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
default: default:
goto fail; goto fail;
} }
ret = 0;
} }
break; break;
case ClearPortFeature: case ClearPortFeature:
@ -413,7 +409,6 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
default: default:
goto fail; goto fail;
} }
ret = 0;
} }
break; break;
case GetHubDescriptor: case GetHubDescriptor:
@ -437,22 +432,20 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
var_hub_size++; var_hub_size++;
} }
ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size; p->actual_length = sizeof(qemu_hub_hub_descriptor) + var_hub_size;
data[0] = ret; data[0] = p->actual_length;
break; break;
} }
default: default:
fail: fail:
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
return ret;
} }
static int usb_hub_handle_data(USBDevice *dev, USBPacket *p) static void usb_hub_handle_data(USBDevice *dev, USBPacket *p)
{ {
USBHubState *s = (USBHubState *)dev; USBHubState *s = (USBHubState *)dev;
int ret;
switch(p->pid) { switch(p->pid) {
case USB_TOKEN_IN: case USB_TOKEN_IN:
@ -465,7 +458,8 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
if (p->iov.size == 1) { /* FreeBSD workaround */ if (p->iov.size == 1) { /* FreeBSD workaround */
n = 1; n = 1;
} else if (n > p->iov.size) { } else if (n > p->iov.size) {
return USB_RET_BABBLE; p->status = USB_RET_BABBLE;
return;
} }
status = 0; status = 0;
for(i = 0; i < NUM_PORTS; i++) { for(i = 0; i < NUM_PORTS; i++) {
@ -478,9 +472,8 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
buf[i] = status >> (8 * i); buf[i] = status >> (8 * i);
} }
usb_packet_copy(p, buf, n); usb_packet_copy(p, buf, n);
ret = n;
} else { } else {
ret = USB_RET_NAK; /* usb11 11.13.1 */ p->status = USB_RET_NAK; /* usb11 11.13.1 */
} }
} else { } else {
goto fail; goto fail;
@ -489,10 +482,9 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
case USB_TOKEN_OUT: case USB_TOKEN_OUT:
default: default:
fail: fail:
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
return ret;
} }
static void usb_hub_handle_destroy(USBDevice *dev) static void usb_hub_handle_destroy(USBDevice *dev)

View File

@ -1048,7 +1048,7 @@ static void usb_net_handle_reset(USBDevice *dev)
{ {
} }
static int usb_net_handle_control(USBDevice *dev, USBPacket *p, static void usb_net_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data) int request, int value, int index, int length, uint8_t *data)
{ {
USBNetState *s = (USBNetState *) dev; USBNetState *s = (USBNetState *) dev;
@ -1056,10 +1056,9 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
ret = usb_desc_handle_control(dev, p, request, value, index, length, data); ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) { if (ret >= 0) {
return ret; return;
} }
ret = 0;
switch(request) { switch(request) {
case ClassInterfaceOutRequest | USB_CDC_SEND_ENCAPSULATED_COMMAND: case ClassInterfaceOutRequest | USB_CDC_SEND_ENCAPSULATED_COMMAND:
if (!is_rndis(s) || value || index != 0) { if (!is_rndis(s) || value || index != 0) {
@ -1078,22 +1077,25 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
} }
#endif #endif
ret = rndis_parse(s, data, length); ret = rndis_parse(s, data, length);
if (ret < 0) {
p->status = ret;
}
break; break;
case ClassInterfaceRequest | USB_CDC_GET_ENCAPSULATED_RESPONSE: case ClassInterfaceRequest | USB_CDC_GET_ENCAPSULATED_RESPONSE:
if (!is_rndis(s) || value || index != 0) { if (!is_rndis(s) || value || index != 0) {
goto fail; goto fail;
} }
ret = rndis_get_response(s, data); p->actual_length = rndis_get_response(s, data);
if (!ret) { if (p->actual_length == 0) {
data[0] = 0; data[0] = 0;
ret = 1; p->actual_length = 1;
} }
#ifdef TRAFFIC_DEBUG #ifdef TRAFFIC_DEBUG
{ {
unsigned int i; unsigned int i;
fprintf(stderr, "GET_ENCAPSULATED_RESPONSE:"); fprintf(stderr, "GET_ENCAPSULATED_RESPONSE:");
for (i = 0; i < ret; i++) { for (i = 0; i < p->actual_length; i++) {
if (!(i & 15)) if (!(i & 15))
fprintf(stderr, "\n%04x:", i); fprintf(stderr, "\n%04x:", i);
fprintf(stderr, " %02x", data[i]); fprintf(stderr, " %02x", data[i]);
@ -1108,72 +1110,67 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
fprintf(stderr, "usbnet: failed control transaction: " fprintf(stderr, "usbnet: failed control transaction: "
"request 0x%x value 0x%x index 0x%x length 0x%x\n", "request 0x%x value 0x%x index 0x%x length 0x%x\n",
request, value, index, length); request, value, index, length);
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
return ret;
} }
static int usb_net_handle_statusin(USBNetState *s, USBPacket *p) static void usb_net_handle_statusin(USBNetState *s, USBPacket *p)
{ {
le32 buf[2]; le32 buf[2];
int ret = 8;
if (p->iov.size < 8) { if (p->iov.size < 8) {
return USB_RET_STALL; p->status = USB_RET_STALL;
return;
} }
buf[0] = cpu_to_le32(1); buf[0] = cpu_to_le32(1);
buf[1] = cpu_to_le32(0); buf[1] = cpu_to_le32(0);
usb_packet_copy(p, buf, 8); usb_packet_copy(p, buf, 8);
if (!s->rndis_resp.tqh_first) if (!s->rndis_resp.tqh_first) {
ret = USB_RET_NAK; p->status = USB_RET_NAK;
}
#ifdef TRAFFIC_DEBUG #ifdef TRAFFIC_DEBUG
fprintf(stderr, "usbnet: interrupt poll len %zu return %d", fprintf(stderr, "usbnet: interrupt poll len %zu return %d",
p->iov.size, ret); p->iov.size, p->status);
iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret); iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->status);
#endif #endif
return ret;
} }
static int usb_net_handle_datain(USBNetState *s, USBPacket *p) static void usb_net_handle_datain(USBNetState *s, USBPacket *p)
{ {
int ret = USB_RET_NAK; int len;
if (s->in_ptr > s->in_len) { if (s->in_ptr > s->in_len) {
usb_net_reset_in_buf(s); usb_net_reset_in_buf(s);
ret = USB_RET_NAK; p->status = USB_RET_NAK;
return ret; return;
} }
if (!s->in_len) { if (!s->in_len) {
ret = USB_RET_NAK; p->status = USB_RET_NAK;
return ret; return;
} }
ret = s->in_len - s->in_ptr; len = s->in_len - s->in_ptr;
if (ret > p->iov.size) { if (len > p->iov.size) {
ret = p->iov.size; len = p->iov.size;
} }
usb_packet_copy(p, &s->in_buf[s->in_ptr], ret); usb_packet_copy(p, &s->in_buf[s->in_ptr], len);
s->in_ptr += ret; s->in_ptr += len;
if (s->in_ptr >= s->in_len && if (s->in_ptr >= s->in_len &&
(is_rndis(s) || (s->in_len & (64 - 1)) || !ret)) { (is_rndis(s) || (s->in_len & (64 - 1)) || !len)) {
/* no short packet necessary */ /* no short packet necessary */
usb_net_reset_in_buf(s); usb_net_reset_in_buf(s);
} }
#ifdef TRAFFIC_DEBUG #ifdef TRAFFIC_DEBUG
fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, ret); fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, len);
iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret); iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", len);
#endif #endif
return ret;
} }
static int usb_net_handle_dataout(USBNetState *s, USBPacket *p) static void usb_net_handle_dataout(USBNetState *s, USBPacket *p)
{ {
int ret = p->iov.size;
int sz = sizeof(s->out_buf) - s->out_ptr; int sz = sizeof(s->out_buf) - s->out_ptr;
struct rndis_packet_msg_type *msg = struct rndis_packet_msg_type *msg =
(struct rndis_packet_msg_type *) s->out_buf; (struct rndis_packet_msg_type *) s->out_buf;
@ -1184,21 +1181,23 @@ static int usb_net_handle_dataout(USBNetState *s, USBPacket *p)
iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->iov.size); iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->iov.size);
#endif #endif
if (sz > ret) if (sz > p->iov.size) {
sz = ret; sz = p->iov.size;
}
usb_packet_copy(p, &s->out_buf[s->out_ptr], sz); usb_packet_copy(p, &s->out_buf[s->out_ptr], sz);
s->out_ptr += sz; s->out_ptr += sz;
if (!is_rndis(s)) { if (!is_rndis(s)) {
if (ret < 64) { if (p->iov.size < 64) {
qemu_send_packet(&s->nic->nc, s->out_buf, s->out_ptr); qemu_send_packet(&s->nic->nc, s->out_buf, s->out_ptr);
s->out_ptr = 0; s->out_ptr = 0;
} }
return ret; return;
} }
len = le32_to_cpu(msg->MessageLength); len = le32_to_cpu(msg->MessageLength);
if (s->out_ptr < 8 || s->out_ptr < len) if (s->out_ptr < 8 || s->out_ptr < len) {
return ret; return;
}
if (le32_to_cpu(msg->MessageType) == RNDIS_PACKET_MSG) { if (le32_to_cpu(msg->MessageType) == RNDIS_PACKET_MSG) {
uint32_t offs = 8 + le32_to_cpu(msg->DataOffset); uint32_t offs = 8 + le32_to_cpu(msg->DataOffset);
uint32_t size = le32_to_cpu(msg->DataLength); uint32_t size = le32_to_cpu(msg->DataLength);
@ -1207,24 +1206,21 @@ static int usb_net_handle_dataout(USBNetState *s, USBPacket *p)
} }
s->out_ptr -= len; s->out_ptr -= len;
memmove(s->out_buf, &s->out_buf[len], s->out_ptr); memmove(s->out_buf, &s->out_buf[len], s->out_ptr);
return ret;
} }
static int usb_net_handle_data(USBDevice *dev, USBPacket *p) static void usb_net_handle_data(USBDevice *dev, USBPacket *p)
{ {
USBNetState *s = (USBNetState *) dev; USBNetState *s = (USBNetState *) dev;
int ret = 0;
switch(p->pid) { switch(p->pid) {
case USB_TOKEN_IN: case USB_TOKEN_IN:
switch (p->ep->nr) { switch (p->ep->nr) {
case 1: case 1:
ret = usb_net_handle_statusin(s, p); usb_net_handle_statusin(s, p);
break; break;
case 2: case 2:
ret = usb_net_handle_datain(s, p); usb_net_handle_datain(s, p);
break; break;
default: default:
@ -1235,7 +1231,7 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
case USB_TOKEN_OUT: case USB_TOKEN_OUT:
switch (p->ep->nr) { switch (p->ep->nr) {
case 2: case 2:
ret = usb_net_handle_dataout(s, p); usb_net_handle_dataout(s, p);
break; break;
default: default:
@ -1245,14 +1241,15 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
default: default:
fail: fail:
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
if (ret == USB_RET_STALL)
if (p->status == USB_RET_STALL) {
fprintf(stderr, "usbnet: failed data transaction: " fprintf(stderr, "usbnet: failed data transaction: "
"pid 0x%x ep 0x%x len 0x%zx\n", "pid 0x%x ep 0x%x len 0x%zx\n",
p->pid, p->ep->nr, p->iov.size); p->pid, p->ep->nr, p->iov.size);
return ret; }
} }
static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t size) static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t size)

View File

@ -219,7 +219,7 @@ static uint8_t usb_get_modem_lines(USBSerialState *s)
return ret; return ret;
} }
static int usb_serial_handle_control(USBDevice *dev, USBPacket *p, static void usb_serial_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data) int request, int value, int index, int length, uint8_t *data)
{ {
USBSerialState *s = (USBSerialState *)dev; USBSerialState *s = (USBSerialState *)dev;
@ -228,13 +228,11 @@ static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
DPRINTF("got control %x, value %x\n",request, value); DPRINTF("got control %x, value %x\n",request, value);
ret = usb_desc_handle_control(dev, p, request, value, index, length, data); ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) { if (ret >= 0) {
return ret; return;
} }
ret = 0;
switch (request) { switch (request) {
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
ret = 0;
break; break;
/* Class specific requests. */ /* Class specific requests. */
@ -323,7 +321,7 @@ static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
case DeviceInVendor | FTDI_GET_MDM_ST: case DeviceInVendor | FTDI_GET_MDM_ST:
data[0] = usb_get_modem_lines(s) | 1; data[0] = usb_get_modem_lines(s) | 1;
data[1] = 0; data[1] = 0;
ret = 2; p->actual_length = 2;
break; break;
case DeviceOutVendor | FTDI_SET_EVENT_CHR: case DeviceOutVendor | FTDI_SET_EVENT_CHR:
/* TODO: handle it */ /* TODO: handle it */
@ -338,25 +336,23 @@ static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
break; break;
case DeviceInVendor | FTDI_GET_LATENCY: case DeviceInVendor | FTDI_GET_LATENCY:
data[0] = s->latency; data[0] = s->latency;
ret = 1; p->actual_length = 1;
break; break;
default: default:
fail: fail:
DPRINTF("got unsupported/bogus control %x, value %x\n", request, value); DPRINTF("got unsupported/bogus control %x, value %x\n", request, value);
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
return ret;
} }
static int usb_serial_handle_data(USBDevice *dev, USBPacket *p) static void usb_serial_handle_data(USBDevice *dev, USBPacket *p)
{ {
USBSerialState *s = (USBSerialState *)dev; USBSerialState *s = (USBSerialState *)dev;
int i, ret = 0;
uint8_t devep = p->ep->nr; uint8_t devep = p->ep->nr;
struct iovec *iov; struct iovec *iov;
uint8_t header[2]; uint8_t header[2];
int first_len, len; int i, first_len, len;
switch (p->pid) { switch (p->pid) {
case USB_TOKEN_OUT: case USB_TOKEN_OUT:
@ -366,6 +362,7 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
iov = p->iov.iov + i; iov = p->iov.iov + i;
qemu_chr_fe_write(s->cs, iov->iov_base, iov->iov_len); qemu_chr_fe_write(s->cs, iov->iov_base, iov->iov_len);
} }
p->actual_length = p->iov.size;
break; break;
case USB_TOKEN_IN: case USB_TOKEN_IN:
@ -374,7 +371,7 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
first_len = RECV_BUF - s->recv_ptr; first_len = RECV_BUF - s->recv_ptr;
len = p->iov.size; len = p->iov.size;
if (len <= 2) { if (len <= 2) {
ret = USB_RET_NAK; p->status = USB_RET_NAK;
break; break;
} }
header[0] = usb_get_modem_lines(s) | 1; header[0] = usb_get_modem_lines(s) | 1;
@ -384,7 +381,6 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
s->event_trigger &= ~FTDI_BI; s->event_trigger &= ~FTDI_BI;
header[1] = FTDI_BI; header[1] = FTDI_BI;
usb_packet_copy(p, header, 2); usb_packet_copy(p, header, 2);
ret = 2;
break; break;
} else { } else {
header[1] = 0; header[1] = 0;
@ -393,7 +389,7 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
if (len > s->recv_used) if (len > s->recv_used)
len = s->recv_used; len = s->recv_used;
if (!len) { if (!len) {
ret = USB_RET_NAK; p->status = USB_RET_NAK;
break; break;
} }
if (first_len > len) if (first_len > len)
@ -404,17 +400,14 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
usb_packet_copy(p, s->recv_buf, len - first_len); usb_packet_copy(p, s->recv_buf, len - first_len);
s->recv_used -= len; s->recv_used -= len;
s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; s->recv_ptr = (s->recv_ptr + len) % RECV_BUF;
ret = len + 2;
break; break;
default: default:
DPRINTF("Bad token\n"); DPRINTF("Bad token\n");
fail: fail:
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
return ret;
} }
static void usb_serial_handle_destroy(USBDevice *dev) static void usb_serial_handle_destroy(USBDevice *dev)

View File

@ -635,39 +635,38 @@ static void ccid_handle_reset(USBDevice *dev)
ccid_reset(s); ccid_reset(s);
} }
static int ccid_handle_control(USBDevice *dev, USBPacket *p, int request, static void ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
int value, int index, int length, uint8_t *data) int value, int index, int length, uint8_t *data)
{ {
USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev); USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
int ret = 0; int ret;
DPRINTF(s, 1, "got control %x, value %x\n", request, value); DPRINTF(s, 1, "got control %x, value %x\n", request, value);
ret = usb_desc_handle_control(dev, p, request, value, index, length, data); ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) { if (ret >= 0) {
return ret; return;
} }
switch (request) { switch (request) {
/* Class specific requests. */ /* Class specific requests. */
case InterfaceOutClass | CCID_CONTROL_ABORT: case InterfaceOutClass | CCID_CONTROL_ABORT:
DPRINTF(s, 1, "ccid_control abort UNIMPLEMENTED\n"); DPRINTF(s, 1, "ccid_control abort UNIMPLEMENTED\n");
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
case InterfaceInClass | CCID_CONTROL_GET_CLOCK_FREQUENCIES: case InterfaceInClass | CCID_CONTROL_GET_CLOCK_FREQUENCIES:
DPRINTF(s, 1, "ccid_control get clock frequencies UNIMPLEMENTED\n"); DPRINTF(s, 1, "ccid_control get clock frequencies UNIMPLEMENTED\n");
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
case InterfaceInClass | CCID_CONTROL_GET_DATA_RATES: case InterfaceInClass | CCID_CONTROL_GET_DATA_RATES:
DPRINTF(s, 1, "ccid_control get data rates UNIMPLEMENTED\n"); DPRINTF(s, 1, "ccid_control get data rates UNIMPLEMENTED\n");
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
default: default:
DPRINTF(s, 1, "got unsupported/bogus control %x, value %x\n", DPRINTF(s, 1, "got unsupported/bogus control %x, value %x\n",
request, value); request, value);
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
return ret;
} }
static bool ccid_card_inserted(USBCCIDState *s) static bool ccid_card_inserted(USBCCIDState *s)
@ -870,18 +869,13 @@ static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock *recv)
} }
} }
/* static void ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
* Handle a single USB_TOKEN_OUT, return value returned to guest.
* Return value:
* 0 - all ok
* USB_RET_STALL - failed to handle packet
*/
static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
{ {
CCID_Header *ccid_header; CCID_Header *ccid_header;
if (p->iov.size + s->bulk_out_pos > BULK_OUT_DATA_SIZE) { if (p->iov.size + s->bulk_out_pos > BULK_OUT_DATA_SIZE) {
return USB_RET_STALL; p->status = USB_RET_STALL;
return;
} }
ccid_header = (CCID_Header *)s->bulk_out_data; ccid_header = (CCID_Header *)s->bulk_out_data;
usb_packet_copy(p, s->bulk_out_data + s->bulk_out_pos, p->iov.size); usb_packet_copy(p, s->bulk_out_data + s->bulk_out_pos, p->iov.size);
@ -890,7 +884,7 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
DPRINTF(s, D_VERBOSE, DPRINTF(s, D_VERBOSE,
"usb-ccid: bulk_in: expecting more packets (%zd/%d)\n", "usb-ccid: bulk_in: expecting more packets (%zd/%d)\n",
p->iov.size, ccid_header->dwLength); p->iov.size, ccid_header->dwLength);
return 0; return;
} }
if (s->bulk_out_pos < 10) { if (s->bulk_out_pos < 10) {
DPRINTF(s, 1, DPRINTF(s, 1,
@ -949,60 +943,52 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
} }
} }
s->bulk_out_pos = 0; s->bulk_out_pos = 0;
return 0;
} }
static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p) static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p)
{ {
int ret = 0; int len = 0;
assert(p->iov.size > 0);
ccid_bulk_in_get(s); ccid_bulk_in_get(s);
if (s->current_bulk_in != NULL) { if (s->current_bulk_in != NULL) {
ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, len = MIN(s->current_bulk_in->len - s->current_bulk_in->pos,
p->iov.size); p->iov.size);
usb_packet_copy(p, s->current_bulk_in->data + usb_packet_copy(p, s->current_bulk_in->data +
s->current_bulk_in->pos, ret); s->current_bulk_in->pos, len);
s->current_bulk_in->pos += ret; s->current_bulk_in->pos += len;
if (s->current_bulk_in->pos == s->current_bulk_in->len) { if (s->current_bulk_in->pos == s->current_bulk_in->len) {
ccid_bulk_in_release(s); ccid_bulk_in_release(s);
} }
} else { } else {
/* return when device has no data - usb 2.0 spec Table 8-4 */ /* return when device has no data - usb 2.0 spec Table 8-4 */
ret = USB_RET_NAK; p->status = USB_RET_NAK;
} }
if (ret > 0) { if (len) {
DPRINTF(s, D_MORE_INFO, DPRINTF(s, D_MORE_INFO,
"%s: %zd/%d req/act to guest (BULK_IN)\n", "%s: %zd/%d req/act to guest (BULK_IN)\n",
__func__, p->iov.size, ret); __func__, p->iov.size, len);
} }
if (ret != USB_RET_NAK && ret < p->iov.size) { if (len < p->iov.size) {
DPRINTF(s, 1, DPRINTF(s, 1,
"%s: returning short (EREMOTEIO) %d < %zd\n", "%s: returning short (EREMOTEIO) %d < %zd\n",
__func__, ret, p->iov.size); __func__, len, p->iov.size);
} }
return ret;
} }
static int ccid_handle_data(USBDevice *dev, USBPacket *p) static void ccid_handle_data(USBDevice *dev, USBPacket *p)
{ {
USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev); USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
int ret = 0;
uint8_t buf[2]; uint8_t buf[2];
switch (p->pid) { switch (p->pid) {
case USB_TOKEN_OUT: case USB_TOKEN_OUT:
ret = ccid_handle_bulk_out(s, p); ccid_handle_bulk_out(s, p);
break; break;
case USB_TOKEN_IN: case USB_TOKEN_IN:
switch (p->ep->nr) { switch (p->ep->nr) {
case CCID_BULK_IN_EP: case CCID_BULK_IN_EP:
if (!p->iov.size) { ccid_bulk_in_copy_to_guest(s, p);
ret = USB_RET_NAK;
} else {
ret = ccid_bulk_in_copy_to_guest(s, p);
}
break; break;
case CCID_INT_IN_EP: case CCID_INT_IN_EP:
if (s->notify_slot_change) { if (s->notify_slot_change) {
@ -1010,7 +996,6 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p)
buf[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange; buf[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange;
buf[1] = s->bmSlotICCState; buf[1] = s->bmSlotICCState;
usb_packet_copy(p, buf, 2); usb_packet_copy(p, buf, 2);
ret = 2;
s->notify_slot_change = false; s->notify_slot_change = false;
s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK; s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK;
DPRINTF(s, D_INFO, DPRINTF(s, D_INFO,
@ -1021,17 +1006,15 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p)
break; break;
default: default:
DPRINTF(s, 1, "Bad endpoint\n"); DPRINTF(s, 1, "Bad endpoint\n");
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
break; break;
default: default:
DPRINTF(s, 1, "Bad token\n"); DPRINTF(s, 1, "Bad token\n");
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
return ret;
} }
static void ccid_handle_destroy(USBDevice *dev) static void ccid_handle_destroy(USBDevice *dev)

View File

@ -215,7 +215,7 @@ static const USBDesc desc = {
static void usb_msd_copy_data(MSDState *s, USBPacket *p) static void usb_msd_copy_data(MSDState *s, USBPacket *p)
{ {
uint32_t len; uint32_t len;
len = p->iov.size - p->result; len = p->iov.size - p->actual_length;
if (len > s->scsi_len) if (len > s->scsi_len)
len = s->scsi_len; len = s->scsi_len;
usb_packet_copy(p, scsi_req_get_buf(s->req) + s->scsi_off, len); usb_packet_copy(p, scsi_req_get_buf(s->req) + s->scsi_off, len);
@ -263,7 +263,8 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
if (p) { if (p) {
usb_msd_copy_data(s, p); usb_msd_copy_data(s, p);
p = s->packet; p = s->packet;
if (p && p->result == p->iov.size) { if (p && p->actual_length == p->iov.size) {
p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
usb_msd_packet_complete(s); usb_msd_packet_complete(s);
} }
} }
@ -292,7 +293,7 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t r
s->mode = USB_MSDM_CBW; s->mode = USB_MSDM_CBW;
} else { } else {
if (s->data_len) { if (s->data_len) {
int len = (p->iov.size - p->result); int len = (p->iov.size - p->actual_length);
usb_packet_skip(p, len); usb_packet_skip(p, len);
s->data_len -= len; s->data_len -= len;
} }
@ -300,6 +301,7 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t r
s->mode = USB_MSDM_CSW; s->mode = USB_MSDM_CSW;
} }
} }
p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
usb_msd_packet_complete(s); usb_msd_packet_complete(s);
} else if (s->data_len == 0) { } else if (s->data_len == 0) {
s->mode = USB_MSDM_CSW; s->mode = USB_MSDM_CSW;
@ -330,14 +332,14 @@ static void usb_msd_handle_reset(USBDevice *dev)
assert(s->req == NULL); assert(s->req == NULL);
if (s->packet) { if (s->packet) {
s->packet->result = USB_RET_STALL; s->packet->status = USB_RET_STALL;
usb_msd_packet_complete(s); usb_msd_packet_complete(s);
} }
s->mode = USB_MSDM_CBW; s->mode = USB_MSDM_CBW;
} }
static int usb_msd_handle_control(USBDevice *dev, USBPacket *p, static void usb_msd_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data) int request, int value, int index, int length, uint8_t *data)
{ {
MSDState *s = (MSDState *)dev; MSDState *s = (MSDState *)dev;
@ -345,29 +347,25 @@ static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
ret = usb_desc_handle_control(dev, p, request, value, index, length, data); ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) { if (ret >= 0) {
return ret; return;
} }
ret = 0;
switch (request) { switch (request) {
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
ret = 0;
break; break;
/* Class specific requests. */ /* Class specific requests. */
case ClassInterfaceOutRequest | MassStorageReset: case ClassInterfaceOutRequest | MassStorageReset:
/* Reset state ready for the next CBW. */ /* Reset state ready for the next CBW. */
s->mode = USB_MSDM_CBW; s->mode = USB_MSDM_CBW;
ret = 0;
break; break;
case ClassInterfaceRequest | GetMaxLun: case ClassInterfaceRequest | GetMaxLun:
data[0] = 0; data[0] = 0;
ret = 1; p->actual_length = 1;
break; break;
default: default:
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
return ret;
} }
static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p) static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
@ -382,11 +380,10 @@ static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
} }
} }
static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) static void usb_msd_handle_data(USBDevice *dev, USBPacket *p)
{ {
MSDState *s = (MSDState *)dev; MSDState *s = (MSDState *)dev;
uint32_t tag; uint32_t tag;
int ret = 0;
struct usb_msd_cbw cbw; struct usb_msd_cbw cbw;
uint8_t devep = p->ep->nr; uint8_t devep = p->ep->nr;
@ -433,7 +430,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
if (s->req && s->req->cmd.xfer != SCSI_XFER_NONE) { if (s->req && s->req->cmd.xfer != SCSI_XFER_NONE) {
scsi_req_continue(s->req); scsi_req_continue(s->req);
} }
ret = p->result;
break; break;
case USB_MSDM_DATAOUT: case USB_MSDM_DATAOUT:
@ -446,7 +442,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
usb_msd_copy_data(s, p); usb_msd_copy_data(s, p);
} }
if (le32_to_cpu(s->csw.residue)) { if (le32_to_cpu(s->csw.residue)) {
int len = p->iov.size - p->result; int len = p->iov.size - p->actual_length;
if (len) { if (len) {
usb_packet_skip(p, len); usb_packet_skip(p, len);
s->data_len -= len; s->data_len -= len;
@ -455,12 +451,10 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
} }
} }
} }
if (p->result < p->iov.size) { if (p->actual_length < p->iov.size) {
DPRINTF("Deferring packet %p [wait data-out]\n", p); DPRINTF("Deferring packet %p [wait data-out]\n", p);
s->packet = p; s->packet = p;
ret = USB_RET_ASYNC; p->status = USB_RET_ASYNC;
} else {
ret = p->result;
} }
break; break;
@ -481,7 +475,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
} }
/* Waiting for SCSI write to complete. */ /* Waiting for SCSI write to complete. */
s->packet = p; s->packet = p;
ret = USB_RET_ASYNC; p->status = USB_RET_ASYNC;
break; break;
case USB_MSDM_CSW: case USB_MSDM_CSW:
@ -493,11 +487,10 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
/* still in flight */ /* still in flight */
DPRINTF("Deferring packet %p [wait status]\n", p); DPRINTF("Deferring packet %p [wait status]\n", p);
s->packet = p; s->packet = p;
ret = USB_RET_ASYNC; p->status = USB_RET_ASYNC;
} else { } else {
usb_msd_send_status(s, p); usb_msd_send_status(s, p);
s->mode = USB_MSDM_CBW; s->mode = USB_MSDM_CBW;
ret = 13;
} }
break; break;
@ -508,7 +501,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
usb_msd_copy_data(s, p); usb_msd_copy_data(s, p);
} }
if (le32_to_cpu(s->csw.residue)) { if (le32_to_cpu(s->csw.residue)) {
int len = p->iov.size - p->result; int len = p->iov.size - p->actual_length;
if (len) { if (len) {
usb_packet_skip(p, len); usb_packet_skip(p, len);
s->data_len -= len; s->data_len -= len;
@ -517,12 +510,10 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
} }
} }
} }
if (p->result < p->iov.size) { if (p->actual_length < p->iov.size) {
DPRINTF("Deferring packet %p [wait data-in]\n", p); DPRINTF("Deferring packet %p [wait data-in]\n", p);
s->packet = p; s->packet = p;
ret = USB_RET_ASYNC; p->status = USB_RET_ASYNC;
} else {
ret = p->result;
} }
break; break;
@ -535,11 +526,9 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
default: default:
DPRINTF("Bad token\n"); DPRINTF("Bad token\n");
fail: fail:
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
return ret;
} }
static void usb_msd_password_cb(void *opaque, int err) static void usb_msd_password_cb(void *opaque, int err)

View File

@ -256,10 +256,10 @@ static void usb_uas_send_status_bh(void *opaque)
uas->status = NULL; uas->status = NULL;
usb_packet_copy(p, &st->status, st->length); usb_packet_copy(p, &st->status, st->length);
p->result = st->length;
QTAILQ_REMOVE(&uas->results, st, next); QTAILQ_REMOVE(&uas->results, st, next);
g_free(st); g_free(st);
p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
usb_packet_complete(&uas->dev, p); usb_packet_complete(&uas->dev, p);
} }
@ -349,6 +349,7 @@ static void usb_uas_complete_data_packet(UASRequest *req)
p = req->data; p = req->data;
req->data = NULL; req->data = NULL;
req->data_async = false; req->data_async = false;
p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
usb_packet_complete(&req->uas->dev, p); usb_packet_complete(&req->uas->dev, p);
} }
@ -357,16 +358,16 @@ static void usb_uas_copy_data(UASRequest *req)
uint32_t length; uint32_t length;
length = MIN(req->buf_size - req->buf_off, length = MIN(req->buf_size - req->buf_off,
req->data->iov.size - req->data->result); req->data->iov.size - req->data->actual_length);
trace_usb_uas_xfer_data(req->uas->dev.addr, req->tag, length, trace_usb_uas_xfer_data(req->uas->dev.addr, req->tag, length,
req->data->result, req->data->iov.size, req->data->actual_length, req->data->iov.size,
req->buf_off, req->buf_size); req->buf_off, req->buf_size);
usb_packet_copy(req->data, scsi_req_get_buf(req->req) + req->buf_off, usb_packet_copy(req->data, scsi_req_get_buf(req->req) + req->buf_off,
length); length);
req->buf_off += length; req->buf_off += length;
req->data_off += length; req->data_off += length;
if (req->data->result == req->data->iov.size) { if (req->data->actual_length == req->data->iov.size) {
usb_uas_complete_data_packet(req); usb_uas_complete_data_packet(req);
} }
if (req->buf_size && req->buf_off == req->buf_size) { if (req->buf_size && req->buf_off == req->buf_size) {
@ -504,17 +505,17 @@ static void usb_uas_handle_reset(USBDevice *dev)
} }
} }
static int usb_uas_handle_control(USBDevice *dev, USBPacket *p, static void usb_uas_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data) int request, int value, int index, int length, uint8_t *data)
{ {
int ret; int ret;
ret = usb_desc_handle_control(dev, p, request, value, index, length, data); ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) { if (ret >= 0) {
return ret; return;
} }
fprintf(stderr, "%s: unhandled control request\n", __func__); fprintf(stderr, "%s: unhandled control request\n", __func__);
return USB_RET_STALL; p->status = USB_RET_STALL;
} }
static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p) static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p)
@ -641,13 +642,13 @@ incorrect_lun:
usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN, 0); usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN, 0);
} }
static int usb_uas_handle_data(USBDevice *dev, USBPacket *p) static void usb_uas_handle_data(USBDevice *dev, USBPacket *p)
{ {
UASDevice *uas = DO_UPCAST(UASDevice, dev, dev); UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
uas_ui ui; uas_ui ui;
UASStatus *st; UASStatus *st;
UASRequest *req; UASRequest *req;
int length, ret = 0; int length;
switch (p->ep->nr) { switch (p->ep->nr) {
case UAS_PIPE_ID_COMMAND: case UAS_PIPE_ID_COMMAND:
@ -656,16 +657,14 @@ static int usb_uas_handle_data(USBDevice *dev, USBPacket *p)
switch (ui.hdr.id) { switch (ui.hdr.id) {
case UAS_UI_COMMAND: case UAS_UI_COMMAND:
usb_uas_command(uas, &ui); usb_uas_command(uas, &ui);
ret = length;
break; break;
case UAS_UI_TASK_MGMT: case UAS_UI_TASK_MGMT:
usb_uas_task(uas, &ui); usb_uas_task(uas, &ui);
ret = length;
break; break;
default: default:
fprintf(stderr, "%s: unknown command ui: id 0x%x\n", fprintf(stderr, "%s: unknown command ui: id 0x%x\n",
__func__, ui.hdr.id); __func__, ui.hdr.id);
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
break; break;
@ -674,11 +673,10 @@ static int usb_uas_handle_data(USBDevice *dev, USBPacket *p)
if (st == NULL) { if (st == NULL) {
assert(uas->status == NULL); assert(uas->status == NULL);
uas->status = p; uas->status = p;
ret = USB_RET_ASYNC; p->status = USB_RET_ASYNC;
break; break;
} }
usb_packet_copy(p, &st->status, st->length); usb_packet_copy(p, &st->status, st->length);
ret = st->length;
QTAILQ_REMOVE(&uas->results, st, next); QTAILQ_REMOVE(&uas->results, st, next);
g_free(st); g_free(st);
break; break;
@ -687,28 +685,26 @@ static int usb_uas_handle_data(USBDevice *dev, USBPacket *p)
req = (p->ep->nr == UAS_PIPE_ID_DATA_IN) ? uas->datain : uas->dataout; req = (p->ep->nr == UAS_PIPE_ID_DATA_IN) ? uas->datain : uas->dataout;
if (req == NULL) { if (req == NULL) {
fprintf(stderr, "%s: no inflight request\n", __func__); fprintf(stderr, "%s: no inflight request\n", __func__);
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
scsi_req_ref(req->req); scsi_req_ref(req->req);
req->data = p; req->data = p;
usb_uas_copy_data(req); usb_uas_copy_data(req);
if (p->result == p->iov.size || req->complete) { if (p->actual_length == p->iov.size || req->complete) {
req->data = NULL; req->data = NULL;
ret = p->result;
} else { } else {
req->data_async = true; req->data_async = true;
ret = USB_RET_ASYNC; p->status = USB_RET_ASYNC;
} }
scsi_req_unref(req->req); scsi_req_unref(req->req);
usb_uas_start_next_transfer(uas); usb_uas_start_next_transfer(uas);
break; break;
default: default:
fprintf(stderr, "%s: invalid endpoint %d\n", __func__, p->ep->nr); fprintf(stderr, "%s: invalid endpoint %d\n", __func__, p->ep->nr);
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
return ret;
} }
static void usb_uas_handle_destroy(USBDevice *dev) static void usb_uas_handle_destroy(USBDevice *dev)

View File

@ -250,7 +250,7 @@ static void usb_wacom_handle_reset(USBDevice *dev)
s->mode = WACOM_MODE_HID; s->mode = WACOM_MODE_HID;
} }
static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p, static void usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data) int request, int value, int index, int length, uint8_t *data)
{ {
USBWacomState *s = (USBWacomState *) dev; USBWacomState *s = (USBWacomState *) dev;
@ -258,10 +258,9 @@ static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
ret = usb_desc_handle_control(dev, p, request, value, index, length, data); ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) { if (ret >= 0) {
return ret; return;
} }
ret = 0;
switch (request) { switch (request) {
case WACOM_SET_REPORT: case WACOM_SET_REPORT:
if (s->mouse_grabbed) { if (s->mouse_grabbed) {
@ -269,61 +268,58 @@ static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
s->mouse_grabbed = 0; s->mouse_grabbed = 0;
} }
s->mode = data[0]; s->mode = data[0];
ret = 0;
break; break;
case WACOM_GET_REPORT: case WACOM_GET_REPORT:
data[0] = 0; data[0] = 0;
data[1] = s->mode; data[1] = s->mode;
ret = 2; p->actual_length = 2;
break; break;
/* USB HID requests */ /* USB HID requests */
case HID_GET_REPORT: case HID_GET_REPORT:
if (s->mode == WACOM_MODE_HID) if (s->mode == WACOM_MODE_HID)
ret = usb_mouse_poll(s, data, length); p->actual_length = usb_mouse_poll(s, data, length);
else if (s->mode == WACOM_MODE_WACOM) else if (s->mode == WACOM_MODE_WACOM)
ret = usb_wacom_poll(s, data, length); p->actual_length = usb_wacom_poll(s, data, length);
break; break;
case HID_GET_IDLE: case HID_GET_IDLE:
ret = 1;
data[0] = s->idle; data[0] = s->idle;
p->actual_length = 1;
break; break;
case HID_SET_IDLE: case HID_SET_IDLE:
s->idle = (uint8_t) (value >> 8); s->idle = (uint8_t) (value >> 8);
ret = 0;
break; break;
default: default:
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
} }
return ret;
} }
static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p) static void usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
{ {
USBWacomState *s = (USBWacomState *) dev; USBWacomState *s = (USBWacomState *) dev;
uint8_t buf[p->iov.size]; uint8_t buf[p->iov.size];
int ret = 0; int len = 0;
switch (p->pid) { switch (p->pid) {
case USB_TOKEN_IN: case USB_TOKEN_IN:
if (p->ep->nr == 1) { if (p->ep->nr == 1) {
if (!(s->changed || s->idle)) if (!(s->changed || s->idle)) {
return USB_RET_NAK; p->status = USB_RET_NAK;
return;
}
s->changed = 0; s->changed = 0;
if (s->mode == WACOM_MODE_HID) if (s->mode == WACOM_MODE_HID)
ret = usb_mouse_poll(s, buf, p->iov.size); len = usb_mouse_poll(s, buf, p->iov.size);
else if (s->mode == WACOM_MODE_WACOM) else if (s->mode == WACOM_MODE_WACOM)
ret = usb_wacom_poll(s, buf, p->iov.size); len = usb_wacom_poll(s, buf, p->iov.size);
usb_packet_copy(p, buf, ret); usb_packet_copy(p, buf, len);
break; break;
} }
/* Fall through. */ /* Fall through. */
case USB_TOKEN_OUT: case USB_TOKEN_OUT:
default: default:
ret = USB_RET_STALL; p->status = USB_RET_STALL;
break;
} }
return ret;
} }
static void usb_wacom_handle_destroy(USBDevice *dev) static void usb_wacom_handle_destroy(USBDevice *dev)

View File

@ -91,6 +91,7 @@ static const VMStateDescription vmstate_ehci_pci = {
.fields = (VMStateField[]) { .fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(pcidev, EHCIPCIState), VMSTATE_PCI_DEVICE(pcidev, EHCIPCIState),
VMSTATE_STRUCT(ehci, EHCIPCIState, 2, vmstate_ehci, EHCIState), VMSTATE_STRUCT(ehci, EHCIPCIState, 2, vmstate_ehci, EHCIState),
VMSTATE_END_OF_LIST()
} }
}; };
@ -105,7 +106,7 @@ static void ehci_class_init(ObjectClass *klass, void *data)
k->device_id = i->device_id; k->device_id = i->device_id;
k->revision = i->revision; k->revision = i->revision;
k->class_id = PCI_CLASS_SERIAL_USB; k->class_id = PCI_CLASS_SERIAL_USB;
dc->vmsd = &vmstate_ehci; dc->vmsd = &vmstate_ehci_pci;
dc->props = ehci_pci_properties; dc->props = ehci_pci_properties;
} }

View File

@ -29,9 +29,6 @@
#include "hw/usb/hcd-ehci.h" #include "hw/usb/hcd-ehci.h"
/* internal processing - reset HC to try and recover */
#define USB_RET_PROCERR (-99)
/* Capability Registers Base Address - section 2.2 */ /* Capability Registers Base Address - section 2.2 */
#define CAPLENGTH 0x0000 /* 1-byte, 0x0001 reserved */ #define CAPLENGTH 0x0000 /* 1-byte, 0x0001 reserved */
#define HCIVERSION 0x0002 /* 2-bytes, i/f version # */ #define HCIVERSION 0x0002 /* 2-bytes, i/f version # */
@ -1111,7 +1108,7 @@ static int ehci_init_transfer(EHCIPacket *p)
while (bytes > 0) { while (bytes > 0) {
if (cpage > 4) { if (cpage > 4) {
fprintf(stderr, "cpage out of range (%d)\n", cpage); fprintf(stderr, "cpage out of range (%d)\n", cpage);
return USB_RET_PROCERR; return -1;
} }
page = p->qtd.bufptr[cpage] & QTD_BUFPTR_MASK; page = p->qtd.bufptr[cpage] & QTD_BUFPTR_MASK;
@ -1129,16 +1126,16 @@ static int ehci_init_transfer(EHCIPacket *p)
return 0; return 0;
} }
static void ehci_finish_transfer(EHCIQueue *q, int status) static void ehci_finish_transfer(EHCIQueue *q, int len)
{ {
uint32_t cpage, offset; uint32_t cpage, offset;
if (status > 0) { if (len > 0) {
/* update cpage & offset */ /* update cpage & offset */
cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE);
offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK; offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK;
offset += status; offset += len;
cpage += offset >> QTD_BUFPTR_SH; cpage += offset >> QTD_BUFPTR_SH;
offset &= ~QTD_BUFPTR_MASK; offset &= ~QTD_BUFPTR_MASK;
@ -1163,7 +1160,7 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
p = container_of(packet, EHCIPacket, packet); p = container_of(packet, EHCIPacket, packet);
assert(p->async == EHCI_ASYNC_INFLIGHT); assert(p->async == EHCI_ASYNC_INFLIGHT);
if (packet->result == USB_RET_REMOVE_FROM_QUEUE) { if (packet->status == USB_RET_REMOVE_FROM_QUEUE) {
trace_usb_ehci_packet_action(p->queue, p, "remove"); trace_usb_ehci_packet_action(p->queue, p, "remove");
ehci_free_packet(p); ehci_free_packet(p);
return; return;
@ -1171,7 +1168,6 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
trace_usb_ehci_packet_action(p->queue, p, "wakeup"); trace_usb_ehci_packet_action(p->queue, p, "wakeup");
p->async = EHCI_ASYNC_FINISHED; p->async = EHCI_ASYNC_FINISHED;
p->usb_status = packet->result;
if (p->queue->async) { if (p->queue->async) {
qemu_bh_schedule(p->queue->ehci->async_bh); qemu_bh_schedule(p->queue->ehci->async_bh);
@ -1181,58 +1177,60 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
static void ehci_execute_complete(EHCIQueue *q) static void ehci_execute_complete(EHCIQueue *q)
{ {
EHCIPacket *p = QTAILQ_FIRST(&q->packets); EHCIPacket *p = QTAILQ_FIRST(&q->packets);
uint32_t tbytes;
assert(p != NULL); assert(p != NULL);
assert(p->qtdaddr == q->qtdaddr); assert(p->qtdaddr == q->qtdaddr);
assert(p->async == EHCI_ASYNC_INITIALIZED || assert(p->async == EHCI_ASYNC_INITIALIZED ||
p->async == EHCI_ASYNC_FINISHED); p->async == EHCI_ASYNC_FINISHED);
DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n", DPRINTF("execute_complete: qhaddr 0x%x, next 0x%x, qtdaddr 0x%x, "
q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status); "status %d, actual_length %d\n",
q->qhaddr, q->qh.next, q->qtdaddr,
p->packet.status, p->packet.actual_length);
if (p->usb_status < 0) { switch (p->packet.status) {
switch (p->usb_status) { case USB_RET_SUCCESS:
case USB_RET_IOERROR: break;
case USB_RET_NODEV: case USB_RET_IOERROR:
q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR); case USB_RET_NODEV:
set_field(&q->qh.token, 0, QTD_TOKEN_CERR); q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR);
ehci_raise_irq(q->ehci, USBSTS_ERRINT); set_field(&q->qh.token, 0, QTD_TOKEN_CERR);
break; ehci_raise_irq(q->ehci, USBSTS_ERRINT);
case USB_RET_STALL: break;
q->qh.token |= QTD_TOKEN_HALT; case USB_RET_STALL:
ehci_raise_irq(q->ehci, USBSTS_ERRINT); q->qh.token |= QTD_TOKEN_HALT;
break; ehci_raise_irq(q->ehci, USBSTS_ERRINT);
case USB_RET_NAK: break;
set_field(&q->qh.altnext_qtd, 0, QH_ALTNEXT_NAKCNT); case USB_RET_NAK:
return; /* We're not done yet with this transaction */ set_field(&q->qh.altnext_qtd, 0, QH_ALTNEXT_NAKCNT);
case USB_RET_BABBLE: return; /* We're not done yet with this transaction */
q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE); case USB_RET_BABBLE:
ehci_raise_irq(q->ehci, USBSTS_ERRINT); q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
break; ehci_raise_irq(q->ehci, USBSTS_ERRINT);
default: break;
/* should not be triggerable */ default:
fprintf(stderr, "USB invalid response %d\n", p->usb_status); /* should not be triggerable */
assert(0); fprintf(stderr, "USB invalid response %d\n", p->packet.status);
break; assert(0);
break;
}
/* TODO check 4.12 for splits */
tbytes = get_field(q->qh.token, QTD_TOKEN_TBYTES);
if (tbytes && p->pid == USB_TOKEN_IN) {
tbytes -= p->packet.actual_length;
if (tbytes) {
/* 4.15.1.2 must raise int on a short input packet */
ehci_raise_irq(q->ehci, USBSTS_INT);
} }
} else { } else {
// TODO check 4.12 for splits tbytes = 0;
uint32_t tbytes = get_field(q->qh.token, QTD_TOKEN_TBYTES);
if (tbytes && p->pid == USB_TOKEN_IN) {
tbytes -= p->usb_status;
if (tbytes) {
/* 4.15.1.2 must raise int on a short input packet */
ehci_raise_irq(q->ehci, USBSTS_INT);
}
} else {
tbytes = 0;
}
DPRINTF("updating tbytes to %d\n", tbytes);
set_field(&q->qh.token, tbytes, QTD_TOKEN_TBYTES);
} }
ehci_finish_transfer(q, p->usb_status); DPRINTF("updating tbytes to %d\n", tbytes);
set_field(&q->qh.token, tbytes, QTD_TOKEN_TBYTES);
ehci_finish_transfer(q, p->packet.actual_length);
usb_packet_unmap(&p->packet, &p->sgl); usb_packet_unmap(&p->packet, &p->sgl);
qemu_sglist_destroy(&p->sgl); qemu_sglist_destroy(&p->sgl);
p->async = EHCI_ASYNC_NONE; p->async = EHCI_ASYNC_NONE;
@ -1248,12 +1246,10 @@ static void ehci_execute_complete(EHCIQueue *q)
} }
} }
// 4.10.3 /* 4.10.3 returns "again" */
static int ehci_execute(EHCIPacket *p, const char *action) static int ehci_execute(EHCIPacket *p, const char *action)
{ {
USBEndpoint *ep; USBEndpoint *ep;
int ret;
int endp; int endp;
bool spd; bool spd;
@ -1262,13 +1258,13 @@ static int ehci_execute(EHCIPacket *p, const char *action)
if (!(p->qtd.token & QTD_TOKEN_ACTIVE)) { if (!(p->qtd.token & QTD_TOKEN_ACTIVE)) {
fprintf(stderr, "Attempting to execute inactive qtd\n"); fprintf(stderr, "Attempting to execute inactive qtd\n");
return USB_RET_PROCERR; return -1;
} }
if (get_field(p->qtd.token, QTD_TOKEN_TBYTES) > BUFF_SIZE) { if (get_field(p->qtd.token, QTD_TOKEN_TBYTES) > BUFF_SIZE) {
ehci_trace_guest_bug(p->queue->ehci, ehci_trace_guest_bug(p->queue->ehci,
"guest requested more bytes than allowed"); "guest requested more bytes than allowed");
return USB_RET_PROCERR; return -1;
} }
p->pid = (p->qtd.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH; p->pid = (p->qtd.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH;
@ -1292,7 +1288,7 @@ static int ehci_execute(EHCIPacket *p, const char *action)
if (p->async == EHCI_ASYNC_NONE) { if (p->async == EHCI_ASYNC_NONE) {
if (ehci_init_transfer(p) != 0) { if (ehci_init_transfer(p) != 0) {
return USB_RET_PROCERR; return -1;
} }
spd = (p->pid == USB_TOKEN_IN && NLPTR_TBIT(p->qtd.altnext) == 0); spd = (p->pid == USB_TOKEN_IN && NLPTR_TBIT(p->qtd.altnext) == 0);
@ -1303,17 +1299,18 @@ static int ehci_execute(EHCIPacket *p, const char *action)
} }
trace_usb_ehci_packet_action(p->queue, p, action); trace_usb_ehci_packet_action(p->queue, p, action);
ret = usb_handle_packet(p->queue->dev, &p->packet); usb_handle_packet(p->queue->dev, &p->packet);
DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd endp %x ret %d\n", DPRINTF("submit: qh 0x%x next 0x%x qtd 0x%x pid 0x%x len %zd endp 0x%x "
q->qhaddr, q->qh.next, q->qtdaddr, q->pid, "status %d actual_length %d\n", p->queue->qhaddr, p->qtd.next,
q->packet.iov.size, endp, ret); p->qtdaddr, p->pid, p->packet.iov.size, endp, p->packet.status,
p->packet.actual_length);
if (ret > BUFF_SIZE) { if (p->packet.actual_length > BUFF_SIZE) {
fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n"); fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n");
return USB_RET_PROCERR; return -1;
} }
return ret; return 1;
} }
/* 4.7.2 /* 4.7.2
@ -1325,7 +1322,6 @@ static int ehci_process_itd(EHCIState *ehci,
{ {
USBDevice *dev; USBDevice *dev;
USBEndpoint *ep; USBEndpoint *ep;
int ret;
uint32_t i, len, pid, dir, devaddr, endp; uint32_t i, len, pid, dir, devaddr, endp;
uint32_t pg, off, ptr1, ptr2, max, mult; uint32_t pg, off, ptr1, ptr2, max, mult;
@ -1348,7 +1344,7 @@ static int ehci_process_itd(EHCIState *ehci,
} }
if (len > BUFF_SIZE) { if (len > BUFF_SIZE) {
return USB_RET_PROCERR; return -1;
} }
qemu_sglist_init(&ehci->isgl, 2, ehci->dma); qemu_sglist_init(&ehci->isgl, 2, ehci->dma);
@ -1370,45 +1366,45 @@ static int ehci_process_itd(EHCIState *ehci,
usb_packet_setup(&ehci->ipacket, pid, ep, addr, false, usb_packet_setup(&ehci->ipacket, pid, ep, addr, false,
(itd->transact[i] & ITD_XACT_IOC) != 0); (itd->transact[i] & ITD_XACT_IOC) != 0);
usb_packet_map(&ehci->ipacket, &ehci->isgl); usb_packet_map(&ehci->ipacket, &ehci->isgl);
ret = usb_handle_packet(dev, &ehci->ipacket); usb_handle_packet(dev, &ehci->ipacket);
usb_packet_unmap(&ehci->ipacket, &ehci->isgl); usb_packet_unmap(&ehci->ipacket, &ehci->isgl);
} else { } else {
DPRINTF("ISOCH: attempt to addess non-iso endpoint\n"); DPRINTF("ISOCH: attempt to addess non-iso endpoint\n");
ret = USB_RET_NAK; ehci->ipacket.status = USB_RET_NAK;
ehci->ipacket.actual_length = 0;
} }
qemu_sglist_destroy(&ehci->isgl); qemu_sglist_destroy(&ehci->isgl);
if (ret < 0) { switch (ehci->ipacket.status) {
switch (ret) { case USB_RET_SUCCESS:
default: break;
fprintf(stderr, "Unexpected iso usb result: %d\n", ret); default:
/* Fall through */ fprintf(stderr, "Unexpected iso usb result: %d\n",
case USB_RET_IOERROR: ehci->ipacket.status);
case USB_RET_NODEV: /* Fall through */
/* 3.3.2: XACTERR is only allowed on IN transactions */ case USB_RET_IOERROR:
if (dir) { case USB_RET_NODEV:
itd->transact[i] |= ITD_XACT_XACTERR; /* 3.3.2: XACTERR is only allowed on IN transactions */
ehci_raise_irq(ehci, USBSTS_ERRINT); if (dir) {
} itd->transact[i] |= ITD_XACT_XACTERR;
break;
case USB_RET_BABBLE:
itd->transact[i] |= ITD_XACT_BABBLE;
ehci_raise_irq(ehci, USBSTS_ERRINT); ehci_raise_irq(ehci, USBSTS_ERRINT);
break;
case USB_RET_NAK:
/* no data for us, so do a zero-length transfer */
ret = 0;
break;
} }
break;
case USB_RET_BABBLE:
itd->transact[i] |= ITD_XACT_BABBLE;
ehci_raise_irq(ehci, USBSTS_ERRINT);
break;
case USB_RET_NAK:
/* no data for us, so do a zero-length transfer */
ehci->ipacket.actual_length = 0;
break;
} }
if (ret >= 0) { if (!dir) {
if (!dir) { set_field(&itd->transact[i], len - ehci->ipacket.actual_length,
/* OUT */ ITD_XACT_LENGTH); /* OUT */
set_field(&itd->transact[i], len - ret, ITD_XACT_LENGTH); } else {
} else { set_field(&itd->transact[i], ehci->ipacket.actual_length,
/* IN */ ITD_XACT_LENGTH); /* IN */
set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
}
} }
if (itd->transact[i] & ITD_XACT_IOC) { if (itd->transact[i] & ITD_XACT_IOC) {
ehci_raise_irq(ehci, USBSTS_INT); ehci_raise_irq(ehci, USBSTS_INT);
@ -1746,8 +1742,7 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
break; break;
case EHCI_ASYNC_INFLIGHT: case EHCI_ASYNC_INFLIGHT:
/* Check if the guest has added new tds to the queue */ /* Check if the guest has added new tds to the queue */
again = (ehci_fill_queue(QTAILQ_LAST(&q->packets, pkts_head)) == again = ehci_fill_queue(QTAILQ_LAST(&q->packets, pkts_head));
USB_RET_PROCERR) ? -1 : 1;
/* Unfinished async handled packet, go horizontal */ /* Unfinished async handled packet, go horizontal */
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
break; break;
@ -1784,6 +1779,7 @@ static int ehci_state_horizqh(EHCIQueue *q)
return again; return again;
} }
/* Returns "again" */
static int ehci_fill_queue(EHCIPacket *p) static int ehci_fill_queue(EHCIPacket *p)
{ {
USBEndpoint *ep = p->packet.ep; USBEndpoint *ep = p->packet.ep;
@ -1812,17 +1808,14 @@ static int ehci_fill_queue(EHCIPacket *p)
p = ehci_alloc_packet(q); p = ehci_alloc_packet(q);
p->qtdaddr = qtdaddr; p->qtdaddr = qtdaddr;
p->qtd = qtd; p->qtd = qtd;
p->usb_status = ehci_execute(p, "queue"); if (ehci_execute(p, "queue") == -1) {
if (p->usb_status == USB_RET_PROCERR) { return -1;
break;
} }
assert(p->usb_status == USB_RET_ASYNC); assert(p->packet.status == USB_RET_ASYNC);
p->async = EHCI_ASYNC_INFLIGHT; p->async = EHCI_ASYNC_INFLIGHT;
} }
if (p->usb_status != USB_RET_PROCERR) { usb_device_flush_ep_queue(ep->dev, ep);
usb_device_flush_ep_queue(ep->dev, ep); return 1;
}
return p->usb_status;
} }
static int ehci_state_execute(EHCIQueue *q) static int ehci_state_execute(EHCIQueue *q)
@ -1851,18 +1844,17 @@ static int ehci_state_execute(EHCIQueue *q)
ehci_set_usbsts(q->ehci, USBSTS_REC); ehci_set_usbsts(q->ehci, USBSTS_REC);
} }
p->usb_status = ehci_execute(p, "process"); again = ehci_execute(p, "process");
if (p->usb_status == USB_RET_PROCERR) { if (again == -1) {
again = -1;
goto out; goto out;
} }
if (p->usb_status == USB_RET_ASYNC) { if (p->packet.status == USB_RET_ASYNC) {
ehci_flush_qh(q); ehci_flush_qh(q);
trace_usb_ehci_packet_action(p->queue, p, "async"); trace_usb_ehci_packet_action(p->queue, p, "async");
p->async = EHCI_ASYNC_INFLIGHT; p->async = EHCI_ASYNC_INFLIGHT;
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
if (q->async) { if (q->async) {
again = (ehci_fill_queue(p) == USB_RET_PROCERR) ? -1 : 1; again = ehci_fill_queue(p);
} else { } else {
again = 1; again = 1;
} }
@ -1891,7 +1883,7 @@ static int ehci_state_executing(EHCIQueue *q)
} }
/* 4.10.5 */ /* 4.10.5 */
if (p->usb_status == USB_RET_NAK) { if (p->packet.status == USB_RET_NAK) {
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
} else { } else {
ehci_set_state(q->ehci, q->async, EST_WRITEBACK); ehci_set_state(q->ehci, q->async, EST_WRITEBACK);

View File

@ -230,7 +230,6 @@ struct EHCIPacket {
QEMUSGList sgl; QEMUSGList sgl;
int pid; int pid;
enum async_state async; enum async_state async;
int usb_status;
}; };
struct EHCIQueue { struct EHCIQueue {

View File

@ -607,7 +607,6 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
{ {
USBDevice *dev; USBDevice *dev;
USBEndpoint *uep; USBEndpoint *uep;
int ret;
int idx = epnum && dir; int idx = epnum && dir;
int ttype; int ttype;
@ -632,15 +631,19 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
ep->packey[dir].ep = ep; ep->packey[dir].ep = ep;
ep->packey[dir].dir = dir; ep->packey[dir].dir = dir;
ret = usb_handle_packet(dev, &ep->packey[dir].p); usb_handle_packet(dev, &ep->packey[dir].p);
if (ret == USB_RET_ASYNC) { if (ep->packey[dir].p.status == USB_RET_ASYNC) {
usb_device_flush_ep_queue(dev, uep); usb_device_flush_ep_queue(dev, uep);
ep->status[dir] = len; ep->status[dir] = len;
return; return;
} }
ep->status[dir] = ret; if (ep->packey[dir].p.status == USB_RET_SUCCESS) {
ep->status[dir] = ep->packey[dir].p.actual_length;
} else {
ep->status[dir] = ep->packey[dir].p.status;
}
musb_schedule_cb(&s->port, &ep->packey[dir].p); musb_schedule_cb(&s->port, &ep->packey[dir].p);
} }
@ -754,7 +757,6 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
if (ep->status[1] == USB_RET_STALL) { if (ep->status[1] == USB_RET_STALL) {
ep->status[1] = 0; ep->status[1] = 0;
packey->result = 0;
ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL; ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL;
if (!epnum) if (!epnum)
@ -793,14 +795,12 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
/* TODO: check len for over/underruns of an OUT packet? */ /* TODO: check len for over/underruns of an OUT packet? */
/* TODO: perhaps make use of e->ext_size[1] here. */ /* TODO: perhaps make use of e->ext_size[1] here. */
packey->result = ep->status[1];
if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) { if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) {
ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY; ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY;
if (!epnum) if (!epnum)
ep->csr[0] |= MGC_M_CSR0_RXPKTRDY; ep->csr[0] |= MGC_M_CSR0_RXPKTRDY;
ep->rxcount = packey->result; /* XXX: MIN(packey->len, ep->maxp[1]); */ ep->rxcount = ep->status[1]; /* XXX: MIN(packey->len, ep->maxp[1]); */
/* In DMA mode: assert DMA request for this EP */ /* In DMA mode: assert DMA request for this EP */
} }

View File

@ -807,21 +807,24 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
DMA_DIRECTION_TO_DEVICE); DMA_DIRECTION_TO_DEVICE);
} }
if (completion) { if (!completion) {
ret = ohci->usb_packet.result;
} else {
bool int_req = relative_frame_number == frame_count && bool int_req = relative_frame_number == frame_count &&
OHCI_BM(iso_td.flags, TD_DI) == 0; OHCI_BM(iso_td.flags, TD_DI) == 0;
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); 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, addr, false, int_req);
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
ret = usb_handle_packet(dev, &ohci->usb_packet); usb_handle_packet(dev, &ohci->usb_packet);
if (ret == USB_RET_ASYNC) { if (ohci->usb_packet.status == USB_RET_ASYNC) {
usb_device_flush_ep_queue(dev, ep); usb_device_flush_ep_queue(dev, ep);
return 1; return 1;
} }
} }
if (ohci->usb_packet.status == USB_RET_SUCCESS) {
ret = ohci->usb_packet.actual_length;
} else {
ret = ohci->usb_packet.status;
}
#ifdef DEBUG_ISOCH #ifdef DEBUG_ISOCH
printf("so 0x%.8x eo 0x%.8x\nsa 0x%.8x ea 0x%.8x\ndir %s len %zu ret %d\n", printf("so 0x%.8x eo 0x%.8x\nsa 0x%.8x ea 0x%.8x\ndir %s len %zu ret %d\n",
@ -997,7 +1000,6 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
} }
#endif #endif
if (completion) { if (completion) {
ret = ohci->usb_packet.result;
ohci->async_td = 0; ohci->async_td = 0;
ohci->async_complete = 0; ohci->async_complete = 0;
} else { } else {
@ -1017,16 +1019,22 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
usb_packet_setup(&ohci->usb_packet, pid, ep, addr, !flag_r, usb_packet_setup(&ohci->usb_packet, pid, ep, addr, !flag_r,
OHCI_BM(td.flags, TD_DI) == 0); OHCI_BM(td.flags, TD_DI) == 0);
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
ret = usb_handle_packet(dev, &ohci->usb_packet); usb_handle_packet(dev, &ohci->usb_packet);
#ifdef DEBUG_PACKET #ifdef DEBUG_PACKET
DPRINTF("ret=%d\n", ret); DPRINTF("status=%d\n", ohci->usb_packet.status);
#endif #endif
if (ret == USB_RET_ASYNC) { if (ohci->usb_packet.status == USB_RET_ASYNC) {
usb_device_flush_ep_queue(dev, ep); usb_device_flush_ep_queue(dev, ep);
ohci->async_td = addr; ohci->async_td = addr;
return 1; return 1;
} }
} }
if (ohci->usb_packet.status == USB_RET_SUCCESS) {
ret = ohci->usb_packet.actual_length;
} else {
ret = ohci->usb_packet.status;
}
if (ret >= 0) { if (ret >= 0) {
if (dir == OHCI_TD_DIR_IN) { if (dir == OHCI_TD_DIR_IN) {
ohci_copy_td(ohci, &td, ohci->usb_buf, ret, ohci_copy_td(ohci, &td, ohci->usb_buf, ret,
@ -1851,7 +1859,7 @@ static int ohci_init_pxa(SysBusDevice *dev)
/* Cannot fail as we pass NULL for masterbus */ /* Cannot fail as we pass NULL for masterbus */
usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset, NULL, 0, usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset, NULL, 0,
NULL); &dma_context_memory);
sysbus_init_irq(dev, &s->ohci.irq); sysbus_init_irq(dev, &s->ohci.irq);
sysbus_init_mmio(dev, &s->ohci.mem); sysbus_init_mmio(dev, &s->ohci.mem);

View File

@ -780,22 +780,21 @@ static int uhci_handle_td_error(UHCIState *s, UHCI_TD *td, uint32_t td_addr,
static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask) static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask)
{ {
int len = 0, max_len, ret; int len = 0, max_len;
uint8_t pid; uint8_t pid;
max_len = ((td->token >> 21) + 1) & 0x7ff; max_len = ((td->token >> 21) + 1) & 0x7ff;
pid = td->token & 0xff; pid = td->token & 0xff;
ret = async->packet.result;
if (td->ctrl & TD_CTRL_IOS) if (td->ctrl & TD_CTRL_IOS)
td->ctrl &= ~TD_CTRL_ACTIVE; td->ctrl &= ~TD_CTRL_ACTIVE;
if (ret < 0) { if (async->packet.status != USB_RET_SUCCESS) {
return uhci_handle_td_error(s, td, async->td_addr, ret, int_mask); return uhci_handle_td_error(s, td, async->td_addr,
async->packet.status, int_mask);
} }
len = async->packet.result; len = async->packet.actual_length;
td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff);
/* The NAK bit may have been set by a previous frame, so clear it /* The NAK bit may have been set by a previous frame, so clear it
@ -824,7 +823,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
UHCI_TD *td, uint32_t td_addr, uint32_t *int_mask) UHCI_TD *td, uint32_t td_addr, uint32_t *int_mask)
{ {
int len = 0, max_len; int ret, max_len;
bool spd; bool spd;
bool queuing = (q != NULL); bool queuing = (q != NULL);
uint8_t pid = td->token & 0xff; uint8_t pid = td->token & 0xff;
@ -915,13 +914,14 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
switch(pid) { switch(pid) {
case USB_TOKEN_OUT: case USB_TOKEN_OUT:
case USB_TOKEN_SETUP: case USB_TOKEN_SETUP:
len = usb_handle_packet(q->ep->dev, &async->packet); usb_handle_packet(q->ep->dev, &async->packet);
if (len >= 0) if (async->packet.status == USB_RET_SUCCESS) {
len = max_len; async->packet.actual_length = max_len;
}
break; break;
case USB_TOKEN_IN: case USB_TOKEN_IN:
len = usb_handle_packet(q->ep->dev, &async->packet); usb_handle_packet(q->ep->dev, &async->packet);
break; break;
default: default:
@ -933,7 +933,7 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
return TD_RESULT_STOP_FRAME; return TD_RESULT_STOP_FRAME;
} }
if (len == USB_RET_ASYNC) { if (async->packet.status == USB_RET_ASYNC) {
uhci_async_link(async); uhci_async_link(async);
if (!queuing) { if (!queuing) {
uhci_queue_fill(q, td); uhci_queue_fill(q, td);
@ -941,13 +941,11 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
return TD_RESULT_ASYNC_START; return TD_RESULT_ASYNC_START;
} }
async->packet.result = len;
done: done:
len = uhci_complete_td(s, td, async, int_mask); ret = uhci_complete_td(s, td, async, int_mask);
usb_packet_unmap(&async->packet, &async->sgl); usb_packet_unmap(&async->packet, &async->sgl);
uhci_async_free(async); uhci_async_free(async);
return len; return ret;
} }
static void uhci_async_complete(USBPort *port, USBPacket *packet) static void uhci_async_complete(USBPort *port, USBPacket *packet)
@ -955,7 +953,7 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet)
UHCIAsync *async = container_of(packet, UHCIAsync, packet); UHCIAsync *async = container_of(packet, UHCIAsync, packet);
UHCIState *s = async->queue->uhci; UHCIState *s = async->queue->uhci;
if (packet->result == USB_RET_REMOVE_FROM_QUEUE) { if (packet->status == USB_RET_REMOVE_FROM_QUEUE) {
uhci_async_unlink(async); uhci_async_unlink(async);
uhci_async_cancel(async); uhci_async_cancel(async);
return; return;

View File

@ -634,6 +634,34 @@ static inline dma_addr_t xhci_mask64(uint64_t addr)
} }
} }
static inline void xhci_dma_read_u32s(XHCIState *xhci, dma_addr_t addr,
uint32_t *buf, size_t len)
{
int i;
assert((len % sizeof(uint32_t)) == 0);
pci_dma_read(&xhci->pci_dev, addr, buf, len);
for (i = 0; i < (len / sizeof(uint32_t)); i++) {
buf[i] = le32_to_cpu(buf[i]);
}
}
static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr,
uint32_t *buf, size_t len)
{
int i;
uint32_t tmp[len / sizeof(uint32_t)];
assert((len % sizeof(uint32_t)) == 0);
for (i = 0; i < (len / sizeof(uint32_t)); i++) {
tmp[i] = cpu_to_le32(buf[i]);
}
pci_dma_write(&xhci->pci_dev, addr, tmp, len);
}
static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport) static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport)
{ {
int index; int index;
@ -1045,14 +1073,14 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
{ {
uint32_t ctx[5]; uint32_t ctx[5];
pci_dma_read(&xhci->pci_dev, epctx->pctx, ctx, sizeof(ctx)); xhci_dma_read_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
ctx[0] &= ~EP_STATE_MASK; ctx[0] &= ~EP_STATE_MASK;
ctx[0] |= state; ctx[0] |= state;
ctx[2] = epctx->ring.dequeue | epctx->ring.ccs; ctx[2] = epctx->ring.dequeue | epctx->ring.ccs;
ctx[3] = (epctx->ring.dequeue >> 16) >> 16; ctx[3] = (epctx->ring.dequeue >> 16) >> 16;
DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n", DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n",
epctx->pctx, state, ctx[3], ctx[2]); epctx->pctx, state, ctx[3], ctx[2]);
pci_dma_write(&xhci->pci_dev, epctx->pctx, ctx, sizeof(ctx)); xhci_dma_write_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
epctx->state = state; epctx->state = state;
} }
@ -1388,7 +1416,7 @@ static void xhci_xfer_report(XHCITransfer *xfer)
XHCIState *xhci = xfer->xhci; XHCIState *xhci = xfer->xhci;
int i; int i;
left = xfer->packet.result < 0 ? 0 : xfer->packet.result; left = xfer->packet.actual_length;
for (i = 0; i < xfer->trb_count; i++) { for (i = 0; i < xfer->trb_count; i++) {
XHCITRB *trb = &xfer->trbs[i]; XHCITRB *trb = &xfer->trbs[i];
@ -1416,7 +1444,7 @@ static void xhci_xfer_report(XHCITransfer *xfer)
if (!reported && ((trb->control & TRB_TR_IOC) || if (!reported && ((trb->control & TRB_TR_IOC) ||
(shortpkt && (trb->control & TRB_TR_ISP)) || (shortpkt && (trb->control & TRB_TR_ISP)) ||
(xfer->status != CC_SUCCESS))) { (xfer->status != CC_SUCCESS && left == 0))) {
event.slotid = xfer->slotid; event.slotid = xfer->slotid;
event.epid = xfer->epid; event.epid = xfer->epid;
event.length = (trb->status & 0x1ffff) - chunk; event.length = (trb->status & 0x1ffff) - chunk;
@ -1490,16 +1518,16 @@ static int xhci_setup_packet(XHCITransfer *xfer)
return 0; return 0;
} }
static int xhci_complete_packet(XHCITransfer *xfer, int ret) static int xhci_complete_packet(XHCITransfer *xfer)
{ {
if (ret == USB_RET_ASYNC) { if (xfer->packet.status == USB_RET_ASYNC) {
trace_usb_xhci_xfer_async(xfer); trace_usb_xhci_xfer_async(xfer);
xfer->running_async = 1; xfer->running_async = 1;
xfer->running_retry = 0; xfer->running_retry = 0;
xfer->complete = 0; xfer->complete = 0;
xfer->cancelled = 0; xfer->cancelled = 0;
return 0; return 0;
} else if (ret == USB_RET_NAK) { } else if (xfer->packet.status == USB_RET_NAK) {
trace_usb_xhci_xfer_nak(xfer); trace_usb_xhci_xfer_nak(xfer);
xfer->running_async = 0; xfer->running_async = 0;
xfer->running_retry = 1; xfer->running_retry = 1;
@ -1513,16 +1541,16 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret)
xhci_xfer_unmap(xfer); xhci_xfer_unmap(xfer);
} }
if (ret >= 0) { if (xfer->packet.status == USB_RET_SUCCESS) {
trace_usb_xhci_xfer_success(xfer, ret); trace_usb_xhci_xfer_success(xfer, xfer->packet.actual_length);
xfer->status = CC_SUCCESS; xfer->status = CC_SUCCESS;
xhci_xfer_report(xfer); xhci_xfer_report(xfer);
return 0; return 0;
} }
/* error */ /* error */
trace_usb_xhci_xfer_error(xfer, ret); trace_usb_xhci_xfer_error(xfer, xfer->packet.status);
switch (ret) { switch (xfer->packet.status) {
case USB_RET_NODEV: case USB_RET_NODEV:
xfer->status = CC_USB_TRANSACTION_ERROR; xfer->status = CC_USB_TRANSACTION_ERROR;
xhci_xfer_report(xfer); xhci_xfer_report(xfer);
@ -1534,7 +1562,8 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret)
xhci_stall_ep(xfer); xhci_stall_ep(xfer);
break; break;
default: default:
fprintf(stderr, "%s: FIXME: ret = %d\n", __FUNCTION__, ret); fprintf(stderr, "%s: FIXME: status = %d\n", __func__,
xfer->packet.status);
FIXME(); FIXME();
} }
return 0; return 0;
@ -1544,7 +1573,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
{ {
XHCITRB *trb_setup, *trb_status; XHCITRB *trb_setup, *trb_status;
uint8_t bmRequestType; uint8_t bmRequestType;
int ret;
trb_setup = &xfer->trbs[0]; trb_setup = &xfer->trbs[0];
trb_status = &xfer->trbs[xfer->trb_count-1]; trb_status = &xfer->trbs[xfer->trb_count-1];
@ -1587,9 +1615,9 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
} }
xfer->packet.parameter = trb_setup->parameter; xfer->packet.parameter = trb_setup->parameter;
ret = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
xhci_complete_packet(xfer, ret); xhci_complete_packet(xfer);
if (!xfer->running_async && !xfer->running_retry) { if (!xfer->running_async && !xfer->running_retry) {
xhci_kick_ep(xhci, xfer->slotid, xfer->epid); xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
} }
@ -1636,7 +1664,6 @@ static void xhci_check_iso_kick(XHCIState *xhci, XHCITransfer *xfer,
static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx) static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
{ {
uint64_t mfindex; uint64_t mfindex;
int ret;
DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid); DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid);
@ -1671,9 +1698,9 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
if (xhci_setup_packet(xfer) < 0) { if (xhci_setup_packet(xfer) < 0) {
return -1; return -1;
} }
ret = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
xhci_complete_packet(xfer, ret); xhci_complete_packet(xfer);
if (!xfer->running_async && !xfer->running_retry) { if (!xfer->running_async && !xfer->running_retry) {
xhci_kick_ep(xhci, xfer->slotid, xfer->epid); xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
} }
@ -1711,7 +1738,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
if (epctx->retry) { if (epctx->retry) {
XHCITransfer *xfer = epctx->retry; XHCITransfer *xfer = epctx->retry;
int result;
trace_usb_xhci_xfer_retry(xfer); trace_usb_xhci_xfer_retry(xfer);
assert(xfer->running_retry); assert(xfer->running_retry);
@ -1725,19 +1751,19 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
if (xhci_setup_packet(xfer) < 0) { if (xhci_setup_packet(xfer) < 0) {
return; return;
} }
result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
assert(result != USB_RET_NAK); assert(xfer->packet.status != USB_RET_NAK);
xhci_complete_packet(xfer, result); xhci_complete_packet(xfer);
} else { } else {
/* retry nak'ed transfer */ /* retry nak'ed transfer */
if (xhci_setup_packet(xfer) < 0) { if (xhci_setup_packet(xfer) < 0) {
return; return;
} }
result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
if (result == USB_RET_NAK) { if (xfer->packet.status == USB_RET_NAK) {
return; return;
} }
xhci_complete_packet(xfer, result); xhci_complete_packet(xfer);
} }
assert(!xfer->running_retry); assert(!xfer->running_retry);
epctx->retry = NULL; epctx->retry = NULL;
@ -1883,14 +1909,14 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
assert(slotid >= 1 && slotid <= xhci->numslots); assert(slotid >= 1 && slotid <= xhci->numslots);
dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high); dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high);
pci_dma_read(&xhci->pci_dev, dcbaap + 8*slotid, &poctx, sizeof(poctx)); poctx = ldq_le_pci_dma(&xhci->pci_dev, dcbaap + 8*slotid);
ictx = xhci_mask64(pictx); ictx = xhci_mask64(pictx);
octx = xhci_mask64(le64_to_cpu(poctx)); octx = xhci_mask64(poctx);
DPRINTF("xhci: input context at "DMA_ADDR_FMT"\n", ictx); DPRINTF("xhci: input context at "DMA_ADDR_FMT"\n", ictx);
DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx); DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx);
pci_dma_read(&xhci->pci_dev, ictx, ictl_ctx, sizeof(ictl_ctx)); xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx));
if (ictl_ctx[0] != 0x0 || ictl_ctx[1] != 0x3) { if (ictl_ctx[0] != 0x0 || ictl_ctx[1] != 0x3) {
fprintf(stderr, "xhci: invalid input context control %08x %08x\n", fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
@ -1898,8 +1924,8 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
return CC_TRB_ERROR; return CC_TRB_ERROR;
} }
pci_dma_read(&xhci->pci_dev, ictx+32, slot_ctx, sizeof(slot_ctx)); xhci_dma_read_u32s(xhci, ictx+32, slot_ctx, sizeof(slot_ctx));
pci_dma_read(&xhci->pci_dev, ictx+64, ep0_ctx, sizeof(ep0_ctx)); xhci_dma_read_u32s(xhci, ictx+64, ep0_ctx, sizeof(ep0_ctx));
DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n", DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n",
slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
@ -1953,8 +1979,8 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n", DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n",
ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]); ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
pci_dma_write(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
pci_dma_write(&xhci->pci_dev, octx+32, ep0_ctx, sizeof(ep0_ctx)); xhci_dma_write_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx));
return res; return res;
} }
@ -1987,17 +2013,17 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
} }
} }
pci_dma_read(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT); slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT);
slot_ctx[3] |= SLOT_ADDRESSED << SLOT_STATE_SHIFT; slot_ctx[3] |= SLOT_ADDRESSED << SLOT_STATE_SHIFT;
DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
pci_dma_write(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
return CC_SUCCESS; return CC_SUCCESS;
} }
pci_dma_read(&xhci->pci_dev, ictx, ictl_ctx, sizeof(ictl_ctx)); xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx));
if ((ictl_ctx[0] & 0x3) != 0x0 || (ictl_ctx[1] & 0x3) != 0x1) { if ((ictl_ctx[0] & 0x3) != 0x0 || (ictl_ctx[1] & 0x3) != 0x1) {
fprintf(stderr, "xhci: invalid input context control %08x %08x\n", fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
@ -2005,8 +2031,8 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
return CC_TRB_ERROR; return CC_TRB_ERROR;
} }
pci_dma_read(&xhci->pci_dev, ictx+32, islot_ctx, sizeof(islot_ctx)); xhci_dma_read_u32s(xhci, ictx+32, islot_ctx, sizeof(islot_ctx));
pci_dma_read(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
if (SLOT_STATE(slot_ctx[3]) < SLOT_ADDRESSED) { if (SLOT_STATE(slot_ctx[3]) < SLOT_ADDRESSED) {
fprintf(stderr, "xhci: invalid slot state %08x\n", slot_ctx[3]); fprintf(stderr, "xhci: invalid slot state %08x\n", slot_ctx[3]);
@ -2018,8 +2044,7 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
xhci_disable_ep(xhci, slotid, i); xhci_disable_ep(xhci, slotid, i);
} }
if (ictl_ctx[1] & (1<<i)) { if (ictl_ctx[1] & (1<<i)) {
pci_dma_read(&xhci->pci_dev, ictx+32+(32*i), ep_ctx, xhci_dma_read_u32s(xhci, ictx+32+(32*i), ep_ctx, sizeof(ep_ctx));
sizeof(ep_ctx));
DPRINTF("xhci: input ep%d.%d context: %08x %08x %08x %08x %08x\n", DPRINTF("xhci: input ep%d.%d context: %08x %08x %08x %08x %08x\n",
i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2], i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2],
ep_ctx[3], ep_ctx[4]); ep_ctx[3], ep_ctx[4]);
@ -2031,7 +2056,7 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
DPRINTF("xhci: output ep%d.%d context: %08x %08x %08x %08x %08x\n", DPRINTF("xhci: output ep%d.%d context: %08x %08x %08x %08x %08x\n",
i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2], i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2],
ep_ctx[3], ep_ctx[4]); ep_ctx[3], ep_ctx[4]);
pci_dma_write(&xhci->pci_dev, octx+(32*i), ep_ctx, sizeof(ep_ctx)); xhci_dma_write_u32s(xhci, octx+(32*i), ep_ctx, sizeof(ep_ctx));
} }
} }
@ -2043,7 +2068,7 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
pci_dma_write(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
return CC_SUCCESS; return CC_SUCCESS;
} }
@ -2068,7 +2093,7 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
DPRINTF("xhci: input context at "DMA_ADDR_FMT"\n", ictx); DPRINTF("xhci: input context at "DMA_ADDR_FMT"\n", ictx);
DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx); DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx);
pci_dma_read(&xhci->pci_dev, ictx, ictl_ctx, sizeof(ictl_ctx)); xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx));
if (ictl_ctx[0] != 0x0 || ictl_ctx[1] & ~0x3) { if (ictl_ctx[0] != 0x0 || ictl_ctx[1] & ~0x3) {
fprintf(stderr, "xhci: invalid input context control %08x %08x\n", fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
@ -2077,12 +2102,12 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
} }
if (ictl_ctx[1] & 0x1) { if (ictl_ctx[1] & 0x1) {
pci_dma_read(&xhci->pci_dev, ictx+32, islot_ctx, sizeof(islot_ctx)); xhci_dma_read_u32s(xhci, ictx+32, islot_ctx, sizeof(islot_ctx));
DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n", DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n",
islot_ctx[0], islot_ctx[1], islot_ctx[2], islot_ctx[3]); islot_ctx[0], islot_ctx[1], islot_ctx[2], islot_ctx[3]);
pci_dma_read(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
slot_ctx[1] &= ~0xFFFF; /* max exit latency */ slot_ctx[1] &= ~0xFFFF; /* max exit latency */
slot_ctx[1] |= islot_ctx[1] & 0xFFFF; slot_ctx[1] |= islot_ctx[1] & 0xFFFF;
@ -2092,17 +2117,17 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
pci_dma_write(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
} }
if (ictl_ctx[1] & 0x2) { if (ictl_ctx[1] & 0x2) {
pci_dma_read(&xhci->pci_dev, ictx+64, iep0_ctx, sizeof(iep0_ctx)); xhci_dma_read_u32s(xhci, ictx+64, iep0_ctx, sizeof(iep0_ctx));
DPRINTF("xhci: input ep0 context: %08x %08x %08x %08x %08x\n", DPRINTF("xhci: input ep0 context: %08x %08x %08x %08x %08x\n",
iep0_ctx[0], iep0_ctx[1], iep0_ctx[2], iep0_ctx[0], iep0_ctx[1], iep0_ctx[2],
iep0_ctx[3], iep0_ctx[4]); iep0_ctx[3], iep0_ctx[4]);
pci_dma_read(&xhci->pci_dev, octx+32, ep0_ctx, sizeof(ep0_ctx)); xhci_dma_read_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx));
ep0_ctx[1] &= ~0xFFFF0000; /* max packet size*/ ep0_ctx[1] &= ~0xFFFF0000; /* max packet size*/
ep0_ctx[1] |= iep0_ctx[1] & 0xFFFF0000; ep0_ctx[1] |= iep0_ctx[1] & 0xFFFF0000;
@ -2110,7 +2135,7 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n", DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n",
ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]); ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
pci_dma_write(&xhci->pci_dev, octx+32, ep0_ctx, sizeof(ep0_ctx)); xhci_dma_write_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx));
} }
return CC_SUCCESS; return CC_SUCCESS;
@ -2135,12 +2160,12 @@ static TRBCCode xhci_reset_slot(XHCIState *xhci, unsigned int slotid)
} }
} }
pci_dma_read(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT); slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT);
slot_ctx[3] |= SLOT_DEFAULT << SLOT_STATE_SHIFT; slot_ctx[3] |= SLOT_DEFAULT << SLOT_STATE_SHIFT;
DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
pci_dma_write(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx)); xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
return CC_SUCCESS; return CC_SUCCESS;
} }
@ -2922,11 +2947,11 @@ static void xhci_complete(USBPort *port, USBPacket *packet)
{ {
XHCITransfer *xfer = container_of(packet, XHCITransfer, packet); XHCITransfer *xfer = container_of(packet, XHCITransfer, packet);
if (packet->result == USB_RET_REMOVE_FROM_QUEUE) { if (packet->status == USB_RET_REMOVE_FROM_QUEUE) {
xhci_ep_nuke_one_xfer(xfer); xhci_ep_nuke_one_xfer(xfer);
return; return;
} }
xhci_complete_packet(xfer, packet->result); xhci_complete_packet(xfer);
xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid); xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid);
} }

View File

@ -121,7 +121,7 @@ static void usb_host_handle_reset(USBDevice *dev)
* -check device states against transfer requests * -check device states against transfer requests
* and return appropriate response * and return appropriate response
*/ */
static int usb_host_handle_control(USBDevice *dev, static void usb_host_handle_control(USBDevice *dev,
USBPacket *p, USBPacket *p,
int request, int request,
int value, int value,
@ -139,7 +139,6 @@ static int usb_host_handle_control(USBDevice *dev,
/* specific SET_ADDRESS support */ /* specific SET_ADDRESS support */
dev->addr = value; dev->addr = value;
return 0;
} else if ((request >> 8) == UT_WRITE_DEVICE && } else if ((request >> 8) == UT_WRITE_DEVICE &&
(request & 0xff) == UR_SET_CONFIG) { (request & 0xff) == UR_SET_CONFIG) {
@ -151,10 +150,8 @@ static int usb_host_handle_control(USBDevice *dev,
printf("handle_control: failed to set configuration - %s\n", printf("handle_control: failed to set configuration - %s\n",
strerror(errno)); strerror(errno));
#endif #endif
return USB_RET_STALL; p->status = USB_RET_STALL;
} }
return 0;
} else if ((request >> 8) == UT_WRITE_INTERFACE && } else if ((request >> 8) == UT_WRITE_INTERFACE &&
(request & 0xff) == UR_SET_INTERFACE) { (request & 0xff) == UR_SET_INTERFACE) {
@ -168,10 +165,8 @@ static int usb_host_handle_control(USBDevice *dev,
printf("handle_control: failed to set alternate interface - %s\n", printf("handle_control: failed to set alternate interface - %s\n",
strerror(errno)); strerror(errno));
#endif #endif
return USB_RET_STALL; p->status = USB_RET_STALL;
} }
return 0;
} else { } else {
req.ucr_request.bmRequestType = request >> 8; req.ucr_request.bmRequestType = request >> 8;
req.ucr_request.bRequest = request & 0xff; req.ucr_request.bRequest = request & 0xff;
@ -201,14 +196,14 @@ static int usb_host_handle_control(USBDevice *dev,
printf("handle_control: error after request - %s\n", printf("handle_control: error after request - %s\n",
strerror(errno)); strerror(errno));
#endif #endif
return USB_RET_NAK; // STALL p->status = USB_RET_NAK; /* STALL */
} else { } else {
return req.ucr_actlen; p->actual_length = req.ucr_actlen;
} }
} }
} }
static int usb_host_handle_data(USBDevice *dev, USBPacket *p) static void usb_host_handle_data(USBDevice *dev, USBPacket *p)
{ {
USBHostDevice *s = (USBHostDevice *)dev; USBHostDevice *s = (USBHostDevice *)dev;
int ret, fd, mode; int ret, fd, mode;
@ -232,7 +227,8 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
fd = ensure_ep_open(s, devep, mode); fd = ensure_ep_open(s, devep, mode);
if (fd < 0) { if (fd < 0) {
sigprocmask(SIG_SETMASK, &old_mask, NULL); sigprocmask(SIG_SETMASK, &old_mask, NULL);
return USB_RET_NODEV; p->status = USB_RET_NODEV;
return;
} }
if (ioctl(fd, USB_SET_TIMEOUT, &timeout) < 0) { if (ioctl(fd, USB_SET_TIMEOUT, &timeout) < 0) {
@ -267,12 +263,13 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
switch(errno) { switch(errno) {
case ETIMEDOUT: case ETIMEDOUT:
case EINTR: case EINTR:
return USB_RET_NAK; p->status = USB_RET_NAK;
break;
default: default:
return USB_RET_STALL; p->status = USB_RET_STALL;
} }
} else { } else {
return ret; p->actual_length = ret;
} }
} }

View File

@ -366,28 +366,29 @@ static void async_complete(void *opaque)
if (p) { if (p) {
switch (aurb->urb.status) { switch (aurb->urb.status) {
case 0: case 0:
p->result += aurb->urb.actual_length; p->actual_length = aurb->urb.actual_length;
p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
break; break;
case -EPIPE: case -EPIPE:
set_halt(s, p->pid, p->ep->nr); set_halt(s, p->pid, p->ep->nr);
p->result = USB_RET_STALL; p->status = USB_RET_STALL;
break; break;
case -EOVERFLOW: case -EOVERFLOW:
p->result = USB_RET_BABBLE; p->status = USB_RET_BABBLE;
break; break;
default: default:
p->result = USB_RET_IOERROR; p->status = USB_RET_IOERROR;
break; break;
} }
if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) { if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
trace_usb_host_req_complete(s->bus_num, s->addr, p, p->result); trace_usb_host_req_complete(s->bus_num, s->addr, p, p->status);
usb_generic_async_ctrl_complete(&s->dev, p); usb_generic_async_ctrl_complete(&s->dev, p);
} else if (!aurb->more) { } else if (!aurb->more) {
trace_usb_host_req_complete(s->bus_num, s->addr, p, p->result); trace_usb_host_req_complete(s->bus_num, s->addr, p, p->status);
usb_packet_complete(&s->dev, p); usb_packet_complete(&s->dev, p);
} }
} }
@ -733,27 +734,31 @@ static void usb_host_stop_n_free_iso(USBHostDevice *s, int pid, uint8_t ep)
clear_iso_started(s, pid, ep); clear_iso_started(s, pid, ep);
} }
static int urb_status_to_usb_ret(int status) static void urb_status_to_usb_ret(int status, USBPacket *p)
{ {
switch (status) { switch (status) {
case -EPIPE: case -EPIPE:
return USB_RET_STALL; p->status = USB_RET_STALL;
break;
case -EOVERFLOW: case -EOVERFLOW:
return USB_RET_BABBLE; p->status = USB_RET_BABBLE;
break;
default: default:
return USB_RET_IOERROR; p->status = USB_RET_IOERROR;
} }
} }
static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in) static void usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
{ {
AsyncURB *aurb; AsyncURB *aurb;
int i, j, ret, max_packet_size, offset, len = 0; int i, j, max_packet_size, offset, len;
uint8_t *buf; uint8_t *buf;
max_packet_size = p->ep->max_packet_size; max_packet_size = p->ep->max_packet_size;
if (max_packet_size == 0) if (max_packet_size == 0) {
return USB_RET_NAK; p->status = USB_RET_NAK;
return;
}
aurb = get_iso_urb(s, p->pid, p->ep->nr); aurb = get_iso_urb(s, p->pid, p->ep->nr);
if (!aurb) { if (!aurb) {
@ -766,18 +771,17 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
if (in) { if (in) {
/* Check urb status */ /* Check urb status */
if (aurb[i].urb.status) { if (aurb[i].urb.status) {
len = urb_status_to_usb_ret(aurb[i].urb.status); urb_status_to_usb_ret(aurb[i].urb.status, p);
/* Move to the next urb */ /* Move to the next urb */
aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1; aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1;
/* Check frame status */ /* Check frame status */
} else if (aurb[i].urb.iso_frame_desc[j].status) { } else if (aurb[i].urb.iso_frame_desc[j].status) {
len = urb_status_to_usb_ret( urb_status_to_usb_ret(aurb[i].urb.iso_frame_desc[j].status, p);
aurb[i].urb.iso_frame_desc[j].status);
/* Check the frame fits */ /* Check the frame fits */
} else if (aurb[i].urb.iso_frame_desc[j].actual_length } else if (aurb[i].urb.iso_frame_desc[j].actual_length
> p->iov.size) { > p->iov.size) {
printf("husb: received iso data is larger then packet\n"); printf("husb: received iso data is larger then packet\n");
len = USB_RET_BABBLE; p->status = USB_RET_BABBLE;
/* All good copy data over */ /* All good copy data over */
} else { } else {
len = aurb[i].urb.iso_frame_desc[j].actual_length; len = aurb[i].urb.iso_frame_desc[j].actual_length;
@ -792,7 +796,8 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
/* Check the frame fits */ /* Check the frame fits */
if (len > max_packet_size) { if (len > max_packet_size) {
printf("husb: send iso data is larger then max packet size\n"); printf("husb: send iso data is larger then max packet size\n");
return USB_RET_NAK; p->status = USB_RET_NAK;
return;
} }
/* All good copy data over */ /* All good copy data over */
@ -823,17 +828,16 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
/* (Re)-submit all fully consumed / filled urbs */ /* (Re)-submit all fully consumed / filled urbs */
for (i = 0; i < s->iso_urb_count; i++) { for (i = 0; i < s->iso_urb_count; i++) {
if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) { if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]); if (ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]) < 0) {
if (ret < 0) {
perror("USBDEVFS_SUBMITURB"); perror("USBDEVFS_SUBMITURB");
if (!in || len == 0) { if (!in || p->status == USB_RET_SUCCESS) {
switch(errno) { switch(errno) {
case ETIMEDOUT: case ETIMEDOUT:
len = USB_RET_NAK; p->status = USB_RET_NAK;
break; break;
case EPIPE: case EPIPE:
default: default:
len = USB_RET_STALL; p->status = USB_RET_STALL;
} }
} }
break; break;
@ -843,11 +847,9 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
} }
} }
} }
return len;
} }
static int usb_host_handle_data(USBDevice *dev, USBPacket *p) static void usb_host_handle_data(USBDevice *dev, USBPacket *p)
{ {
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
struct usbdevfs_urb *urb; struct usbdevfs_urb *urb;
@ -862,7 +864,8 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
if (!is_valid(s, p->pid, p->ep->nr)) { if (!is_valid(s, p->pid, p->ep->nr)) {
trace_usb_host_req_complete(s->bus_num, s->addr, p, USB_RET_NAK); trace_usb_host_req_complete(s->bus_num, s->addr, p, USB_RET_NAK);
return USB_RET_NAK; p->status = USB_RET_NAK;
return;
} }
if (p->pid == USB_TOKEN_IN) { if (p->pid == USB_TOKEN_IN) {
@ -877,13 +880,15 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
if (ret < 0) { if (ret < 0) {
perror("USBDEVFS_CLEAR_HALT"); perror("USBDEVFS_CLEAR_HALT");
trace_usb_host_req_complete(s->bus_num, s->addr, p, USB_RET_NAK); trace_usb_host_req_complete(s->bus_num, s->addr, p, USB_RET_NAK);
return USB_RET_NAK; p->status = USB_RET_NAK;
return;
} }
clear_halt(s, p->pid, p->ep->nr); clear_halt(s, p->pid, p->ep->nr);
} }
if (is_isoc(s, p->pid, p->ep->nr)) { if (is_isoc(s, p->pid, p->ep->nr)) {
return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN); usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
return;
} }
v = 0; v = 0;
@ -933,17 +938,19 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
case ETIMEDOUT: case ETIMEDOUT:
trace_usb_host_req_complete(s->bus_num, s->addr, p, trace_usb_host_req_complete(s->bus_num, s->addr, p,
USB_RET_NAK); USB_RET_NAK);
return USB_RET_NAK; p->status = USB_RET_NAK;
break;
case EPIPE: case EPIPE:
default: default:
trace_usb_host_req_complete(s->bus_num, s->addr, p, trace_usb_host_req_complete(s->bus_num, s->addr, p,
USB_RET_STALL); USB_RET_STALL);
return USB_RET_STALL; p->status = USB_RET_STALL;
} }
return;
} }
} while (rem > 0); } while (rem > 0);
return USB_RET_ASYNC; p->status = USB_RET_ASYNC;
} }
static int ctrl_error(void) static int ctrl_error(void)
@ -955,14 +962,13 @@ static int ctrl_error(void)
} }
} }
static int usb_host_set_address(USBHostDevice *s, int addr) static void usb_host_set_address(USBHostDevice *s, int addr)
{ {
trace_usb_host_set_address(s->bus_num, s->addr, addr); trace_usb_host_set_address(s->bus_num, s->addr, addr);
s->dev.addr = addr; s->dev.addr = addr;
return 0;
} }
static int usb_host_set_config(USBHostDevice *s, int config) static void usb_host_set_config(USBHostDevice *s, int config, USBPacket *p)
{ {
int ret, first = 1; int ret, first = 1;
@ -987,14 +993,15 @@ again:
} }
if (ret < 0) { if (ret < 0) {
return ctrl_error(); p->status = ctrl_error();
return;
} }
usb_host_claim_interfaces(s, config); usb_host_claim_interfaces(s, config);
usb_linux_update_endp_table(s); usb_linux_update_endp_table(s);
return 0;
} }
static int usb_host_set_interface(USBHostDevice *s, int iface, int alt) static void usb_host_set_interface(USBHostDevice *s, int iface, int alt,
USBPacket *p)
{ {
struct usbdevfs_setinterface si; struct usbdevfs_setinterface si;
int i, ret; int i, ret;
@ -1011,7 +1018,8 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
} }
if (iface >= USB_MAX_INTERFACES) { if (iface >= USB_MAX_INTERFACES) {
return USB_RET_STALL; p->status = USB_RET_STALL;
return;
} }
si.interface = iface; si.interface = iface;
@ -1022,15 +1030,15 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
iface, alt, ret, errno); iface, alt, ret, errno);
if (ret < 0) { if (ret < 0) {
return ctrl_error(); p->status = ctrl_error();
return;
} }
s->dev.altsetting[iface] = alt; s->dev.altsetting[iface] = alt;
usb_linux_update_endp_table(s); usb_linux_update_endp_table(s);
return 0;
} }
static int usb_host_handle_control(USBDevice *dev, USBPacket *p, static void usb_host_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data) int request, int value, int index, int length, uint8_t *data)
{ {
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
@ -1048,19 +1056,19 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
switch (request) { switch (request) {
case DeviceOutRequest | USB_REQ_SET_ADDRESS: case DeviceOutRequest | USB_REQ_SET_ADDRESS:
ret = usb_host_set_address(s, value); usb_host_set_address(s, value);
trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret); trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
return ret; return;
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
ret = usb_host_set_config(s, value & 0xff); usb_host_set_config(s, value & 0xff, p);
trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret); trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
return ret; return;
case InterfaceOutRequest | USB_REQ_SET_INTERFACE: case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
ret = usb_host_set_interface(s, index, value); usb_host_set_interface(s, index, value, p);
trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret); trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
return ret; return;
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
if (value == 0) { /* clear halt */ if (value == 0) { /* clear halt */
@ -1068,17 +1076,16 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
ioctl(s->fd, USBDEVFS_CLEAR_HALT, &index); ioctl(s->fd, USBDEVFS_CLEAR_HALT, &index);
clear_halt(s, pid, index & 0x0f); clear_halt(s, pid, index & 0x0f);
trace_usb_host_req_emulated(s->bus_num, s->addr, p, 0); trace_usb_host_req_emulated(s->bus_num, s->addr, p, 0);
return 0; return;
} }
} }
/* The rest are asynchronous */ /* The rest are asynchronous */
assert(p && p->result == 0);
if (length > sizeof(dev->data_buf)) { if (length > sizeof(dev->data_buf)) {
fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n", fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n",
length, sizeof(dev->data_buf)); length, sizeof(dev->data_buf));
return USB_RET_STALL; p->status = USB_RET_STALL;
return;
} }
aurb = async_alloc(s); aurb = async_alloc(s);
@ -1112,14 +1119,17 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
switch(errno) { switch(errno) {
case ETIMEDOUT: case ETIMEDOUT:
return USB_RET_NAK; p->status = USB_RET_NAK;
break;
case EPIPE: case EPIPE:
default: default:
return USB_RET_STALL; p->status = USB_RET_STALL;
break;
} }
return;
} }
return USB_RET_ASYNC; p->status = USB_RET_ASYNC;
} }
/* returns 1 on problem encountered or 0 for success */ /* returns 1 on problem encountered or 0 for success */

View File

@ -141,8 +141,8 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id,
struct usb_redir_interrupt_packet_header *interrupt_header, struct usb_redir_interrupt_packet_header *interrupt_header,
uint8_t *data, int data_len); uint8_t *data, int data_len);
static int usbredir_handle_status(USBRedirDevice *dev, static void usbredir_handle_status(USBRedirDevice *dev, USBPacket *p,
int status, int actual_len); int status);
#define VERSION "qemu usb-redir guest " QEMU_VERSION #define VERSION "qemu usb-redir guest " QEMU_VERSION
@ -443,7 +443,7 @@ static void usbredir_handle_reset(USBDevice *udev)
usbredirparser_do_write(dev->parser); usbredirparser_do_write(dev->parser);
} }
static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p, static void usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
uint8_t ep) uint8_t ep)
{ {
int status, len; int status, len;
@ -500,7 +500,7 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
!dev->endpoint[EP2I(ep)].bufpq_prefilled) { !dev->endpoint[EP2I(ep)].bufpq_prefilled) {
if (dev->endpoint[EP2I(ep)].bufpq_size < if (dev->endpoint[EP2I(ep)].bufpq_size <
dev->endpoint[EP2I(ep)].bufpq_target_size) { dev->endpoint[EP2I(ep)].bufpq_target_size) {
return usbredir_handle_status(dev, 0, 0); return;
} }
dev->endpoint[EP2I(ep)].bufpq_prefilled = 1; dev->endpoint[EP2I(ep)].bufpq_prefilled = 1;
} }
@ -514,27 +514,23 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
/* Check iso_error for stream errors, otherwise its an underrun */ /* Check iso_error for stream errors, otherwise its an underrun */
status = dev->endpoint[EP2I(ep)].iso_error; status = dev->endpoint[EP2I(ep)].iso_error;
dev->endpoint[EP2I(ep)].iso_error = 0; dev->endpoint[EP2I(ep)].iso_error = 0;
return status ? USB_RET_IOERROR : 0; p->status = status ? USB_RET_IOERROR : USB_RET_SUCCESS;
return;
} }
DPRINTF2("iso-token-in ep %02X status %d len %d queue-size: %d\n", ep, DPRINTF2("iso-token-in ep %02X status %d len %d queue-size: %d\n", ep,
isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size); isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size);
status = isop->status; status = isop->status;
if (status != usb_redir_success) {
bufp_free(dev, isop, ep);
return USB_RET_IOERROR;
}
len = isop->len; len = isop->len;
if (len > p->iov.size) { if (len > p->iov.size) {
ERROR("received iso data is larger then packet ep %02X (%d > %d)\n", ERROR("received iso data is larger then packet ep %02X (%d > %d)\n",
ep, len, (int)p->iov.size); ep, len, (int)p->iov.size);
bufp_free(dev, isop, ep); len = p->iov.size;
return USB_RET_BABBLE; status = usb_redir_babble;
} }
usb_packet_copy(p, isop->data, len); usb_packet_copy(p, isop->data, len);
bufp_free(dev, isop, ep); bufp_free(dev, isop, ep);
return len; usbredir_handle_status(dev, p, status);
} else { } else {
/* If the stream was not started because of a pending error don't /* If the stream was not started because of a pending error don't
send the packet to the usb-host */ send the packet to the usb-host */
@ -554,7 +550,7 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
dev->endpoint[EP2I(ep)].iso_error = 0; dev->endpoint[EP2I(ep)].iso_error = 0;
DPRINTF2("iso-token-out ep %02X status %d len %zd\n", ep, status, DPRINTF2("iso-token-out ep %02X status %d len %zd\n", ep, status,
p->iov.size); p->iov.size);
return usbredir_handle_status(dev, status, p->iov.size); usbredir_handle_status(dev, p, status);
} }
} }
@ -572,7 +568,7 @@ static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep)
usbredir_free_bufpq(dev, ep); usbredir_free_bufpq(dev, ep);
} }
static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
uint8_t ep) uint8_t ep)
{ {
struct usb_redir_bulk_packet_header bulk_packet; struct usb_redir_bulk_packet_header bulk_packet;
@ -581,7 +577,8 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, size, p->id); DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, size, p->id);
if (usbredir_already_in_flight(dev, p->id)) { if (usbredir_already_in_flight(dev, p->id)) {
return USB_RET_ASYNC; p->status = USB_RET_ASYNC;
return;
} }
bulk_packet.endpoint = ep; bulk_packet.endpoint = ep;
@ -608,10 +605,10 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
&bulk_packet, buf, size); &bulk_packet, buf, size);
} }
usbredirparser_do_write(dev->parser); usbredirparser_do_write(dev->parser);
return USB_RET_ASYNC; p->status = USB_RET_ASYNC;
} }
static int usbredir_handle_interrupt_data(USBRedirDevice *dev, static void usbredir_handle_interrupt_data(USBRedirDevice *dev,
USBPacket *p, uint8_t ep) USBPacket *p, uint8_t ep)
{ {
if (ep & USB_DIR_IN) { if (ep & USB_DIR_IN) {
@ -643,28 +640,25 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
status = dev->endpoint[EP2I(ep)].interrupt_error; status = dev->endpoint[EP2I(ep)].interrupt_error;
dev->endpoint[EP2I(ep)].interrupt_error = 0; dev->endpoint[EP2I(ep)].interrupt_error = 0;
if (status) { if (status) {
return usbredir_handle_status(dev, status, 0); usbredir_handle_status(dev, p, status);
} else {
p->status = USB_RET_NAK;
} }
return USB_RET_NAK; return;
} }
DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep, DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep,
intp->status, intp->len); intp->status, intp->len);
status = intp->status; status = intp->status;
if (status != usb_redir_success) {
bufp_free(dev, intp, ep);
return usbredir_handle_status(dev, status, 0);
}
len = intp->len; len = intp->len;
if (len > p->iov.size) { if (len > p->iov.size) {
ERROR("received int data is larger then packet ep %02X\n", ep); ERROR("received int data is larger then packet ep %02X\n", ep);
bufp_free(dev, intp, ep); len = p->iov.size;
return USB_RET_BABBLE; status = usb_redir_babble;
} }
usb_packet_copy(p, intp->data, len); usb_packet_copy(p, intp->data, len);
bufp_free(dev, intp, ep); bufp_free(dev, intp, ep);
return len; usbredir_handle_status(dev, p, status);
} else { } else {
/* Output interrupt endpoint, normal async operation */ /* Output interrupt endpoint, normal async operation */
struct usb_redir_interrupt_packet_header interrupt_packet; struct usb_redir_interrupt_packet_header interrupt_packet;
@ -674,7 +668,8 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
p->iov.size, p->id); p->iov.size, p->id);
if (usbredir_already_in_flight(dev, p->id)) { if (usbredir_already_in_flight(dev, p->id)) {
return USB_RET_ASYNC; p->status = USB_RET_ASYNC;
return;
} }
interrupt_packet.endpoint = ep; interrupt_packet.endpoint = ep;
@ -685,7 +680,7 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
usbredirparser_send_interrupt_packet(dev->parser, p->id, usbredirparser_send_interrupt_packet(dev->parser, p->id,
&interrupt_packet, buf, p->iov.size); &interrupt_packet, buf, p->iov.size);
usbredirparser_do_write(dev->parser); usbredirparser_do_write(dev->parser);
return USB_RET_ASYNC; p->status = USB_RET_ASYNC;
} }
} }
@ -705,7 +700,7 @@ static void usbredir_stop_interrupt_receiving(USBRedirDevice *dev,
usbredir_free_bufpq(dev, ep); usbredir_free_bufpq(dev, ep);
} }
static int usbredir_handle_data(USBDevice *udev, USBPacket *p) static void usbredir_handle_data(USBDevice *udev, USBPacket *p)
{ {
USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
uint8_t ep; uint8_t ep;
@ -718,21 +713,26 @@ static int usbredir_handle_data(USBDevice *udev, USBPacket *p)
switch (dev->endpoint[EP2I(ep)].type) { switch (dev->endpoint[EP2I(ep)].type) {
case USB_ENDPOINT_XFER_CONTROL: case USB_ENDPOINT_XFER_CONTROL:
ERROR("handle_data called for control transfer on ep %02X\n", ep); ERROR("handle_data called for control transfer on ep %02X\n", ep);
return USB_RET_NAK; p->status = USB_RET_NAK;
break;
case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_ISOC:
return usbredir_handle_iso_data(dev, p, ep); usbredir_handle_iso_data(dev, p, ep);
break;
case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_BULK:
if (p->state == USB_PACKET_SETUP && p->pid == USB_TOKEN_IN && if (p->state == USB_PACKET_SETUP && p->pid == USB_TOKEN_IN &&
p->ep->pipeline) { p->ep->pipeline) {
return USB_RET_ADD_TO_QUEUE; p->status = USB_RET_ADD_TO_QUEUE;
break;
} }
return usbredir_handle_bulk_data(dev, p, ep); usbredir_handle_bulk_data(dev, p, ep);
break;
case USB_ENDPOINT_XFER_INT: case USB_ENDPOINT_XFER_INT:
return usbredir_handle_interrupt_data(dev, p, ep); usbredir_handle_interrupt_data(dev, p, ep);
break;
default: default:
ERROR("handle_data ep %02X has unknown type %d\n", ep, ERROR("handle_data ep %02X has unknown type %d\n", ep,
dev->endpoint[EP2I(ep)].type); dev->endpoint[EP2I(ep)].type);
return USB_RET_NAK; p->status = USB_RET_NAK;
} }
} }
@ -743,7 +743,7 @@ static void usbredir_flush_ep_queue(USBDevice *dev, USBEndpoint *ep)
} }
} }
static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p, static void usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
int config) int config)
{ {
struct usb_redir_set_configuration_header set_config; struct usb_redir_set_configuration_header set_config;
@ -768,19 +768,19 @@ static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
set_config.configuration = config; set_config.configuration = config;
usbredirparser_send_set_configuration(dev->parser, p->id, &set_config); usbredirparser_send_set_configuration(dev->parser, p->id, &set_config);
usbredirparser_do_write(dev->parser); usbredirparser_do_write(dev->parser);
return USB_RET_ASYNC; p->status = USB_RET_ASYNC;
} }
static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p) static void usbredir_get_config(USBRedirDevice *dev, USBPacket *p)
{ {
DPRINTF("get config id %"PRIu64"\n", p->id); DPRINTF("get config id %"PRIu64"\n", p->id);
usbredirparser_send_get_configuration(dev->parser, p->id); usbredirparser_send_get_configuration(dev->parser, p->id);
usbredirparser_do_write(dev->parser); usbredirparser_do_write(dev->parser);
return USB_RET_ASYNC; p->status = USB_RET_ASYNC;
} }
static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p, static void usbredir_set_interface(USBRedirDevice *dev, USBPacket *p,
int interface, int alt) int interface, int alt)
{ {
struct usb_redir_set_alt_setting_header set_alt; struct usb_redir_set_alt_setting_header set_alt;
@ -808,10 +808,10 @@ static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p,
set_alt.alt = alt; set_alt.alt = alt;
usbredirparser_send_set_alt_setting(dev->parser, p->id, &set_alt); usbredirparser_send_set_alt_setting(dev->parser, p->id, &set_alt);
usbredirparser_do_write(dev->parser); usbredirparser_do_write(dev->parser);
return USB_RET_ASYNC; p->status = USB_RET_ASYNC;
} }
static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p, static void usbredir_get_interface(USBRedirDevice *dev, USBPacket *p,
int interface) int interface)
{ {
struct usb_redir_get_alt_setting_header get_alt; struct usb_redir_get_alt_setting_header get_alt;
@ -821,17 +821,18 @@ static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p,
get_alt.interface = interface; get_alt.interface = interface;
usbredirparser_send_get_alt_setting(dev->parser, p->id, &get_alt); usbredirparser_send_get_alt_setting(dev->parser, p->id, &get_alt);
usbredirparser_do_write(dev->parser); usbredirparser_do_write(dev->parser);
return USB_RET_ASYNC; p->status = USB_RET_ASYNC;
} }
static int usbredir_handle_control(USBDevice *udev, USBPacket *p, static void usbredir_handle_control(USBDevice *udev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data) int request, int value, int index, int length, uint8_t *data)
{ {
USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
struct usb_redir_control_packet_header control_packet; struct usb_redir_control_packet_header control_packet;
if (usbredir_already_in_flight(dev, p->id)) { if (usbredir_already_in_flight(dev, p->id)) {
return USB_RET_ASYNC; p->status = USB_RET_ASYNC;
return;
} }
/* Special cases for certain standard device requests */ /* Special cases for certain standard device requests */
@ -839,15 +840,19 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
case DeviceOutRequest | USB_REQ_SET_ADDRESS: case DeviceOutRequest | USB_REQ_SET_ADDRESS:
DPRINTF("set address %d\n", value); DPRINTF("set address %d\n", value);
dev->dev.addr = value; dev->dev.addr = value;
return 0; return;
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
return usbredir_set_config(dev, p, value & 0xff); usbredir_set_config(dev, p, value & 0xff);
return;
case DeviceRequest | USB_REQ_GET_CONFIGURATION: case DeviceRequest | USB_REQ_GET_CONFIGURATION:
return usbredir_get_config(dev, p); usbredir_get_config(dev, p);
return;
case InterfaceOutRequest | USB_REQ_SET_INTERFACE: case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
return usbredir_set_interface(dev, p, index, value); usbredir_set_interface(dev, p, index, value);
return;
case InterfaceRequest | USB_REQ_GET_INTERFACE: case InterfaceRequest | USB_REQ_GET_INTERFACE:
return usbredir_get_interface(dev, p, index); usbredir_get_interface(dev, p, index);
return;
} }
/* Normal ctrl requests, note request is (bRequestType << 8) | bRequest */ /* Normal ctrl requests, note request is (bRequestType << 8) | bRequest */
@ -871,7 +876,7 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
&control_packet, data, length); &control_packet, data, length);
} }
usbredirparser_do_write(dev->parser); usbredirparser_do_write(dev->parser);
return USB_RET_ASYNC; p->status = USB_RET_ASYNC;
} }
/* /*
@ -1159,29 +1164,34 @@ error:
* usbredirparser packet complete callbacks * usbredirparser packet complete callbacks
*/ */
static int usbredir_handle_status(USBRedirDevice *dev, static void usbredir_handle_status(USBRedirDevice *dev, USBPacket *p,
int status, int actual_len) int status)
{ {
switch (status) { switch (status) {
case usb_redir_success: case usb_redir_success:
return actual_len; p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
break;
case usb_redir_stall: case usb_redir_stall:
return USB_RET_STALL; p->status = USB_RET_STALL;
break;
case usb_redir_cancelled: case usb_redir_cancelled:
/* /*
* When the usbredir-host unredirects a device, it will report a status * When the usbredir-host unredirects a device, it will report a status
* of cancelled for all pending packets, followed by a disconnect msg. * of cancelled for all pending packets, followed by a disconnect msg.
*/ */
return USB_RET_IOERROR; p->status = USB_RET_IOERROR;
break;
case usb_redir_inval: case usb_redir_inval:
WARNING("got invalid param error from usb-host?\n"); WARNING("got invalid param error from usb-host?\n");
return USB_RET_IOERROR; p->status = USB_RET_IOERROR;
break;
case usb_redir_babble: case usb_redir_babble:
return USB_RET_BABBLE; p->status = USB_RET_BABBLE;
break;
case usb_redir_ioerror: case usb_redir_ioerror:
case usb_redir_timeout: case usb_redir_timeout:
default: default:
return USB_RET_IOERROR; p->status = USB_RET_IOERROR;
} }
} }
@ -1412,7 +1422,6 @@ static void usbredir_configuration_status(void *priv, uint64_t id,
{ {
USBRedirDevice *dev = priv; USBRedirDevice *dev = priv;
USBPacket *p; USBPacket *p;
int len = 0;
DPRINTF("set config status %d config %d id %"PRIu64"\n", DPRINTF("set config status %d config %d id %"PRIu64"\n",
config_status->status, config_status->configuration, id); config_status->status, config_status->configuration, id);
@ -1421,9 +1430,9 @@ static void usbredir_configuration_status(void *priv, uint64_t id,
if (p) { if (p) {
if (dev->dev.setup_buf[0] & USB_DIR_IN) { if (dev->dev.setup_buf[0] & USB_DIR_IN) {
dev->dev.data_buf[0] = config_status->configuration; dev->dev.data_buf[0] = config_status->configuration;
len = 1; p->actual_length = 1;
} }
p->result = usbredir_handle_status(dev, config_status->status, len); usbredir_handle_status(dev, p, config_status->status);
usb_generic_async_ctrl_complete(&dev->dev, p); usb_generic_async_ctrl_complete(&dev->dev, p);
} }
} }
@ -1433,7 +1442,6 @@ static void usbredir_alt_setting_status(void *priv, uint64_t id,
{ {
USBRedirDevice *dev = priv; USBRedirDevice *dev = priv;
USBPacket *p; USBPacket *p;
int len = 0;
DPRINTF("alt status %d intf %d alt %d id: %"PRIu64"\n", DPRINTF("alt status %d intf %d alt %d id: %"PRIu64"\n",
alt_setting_status->status, alt_setting_status->interface, alt_setting_status->status, alt_setting_status->interface,
@ -1443,10 +1451,9 @@ static void usbredir_alt_setting_status(void *priv, uint64_t id,
if (p) { if (p) {
if (dev->dev.setup_buf[0] & USB_DIR_IN) { if (dev->dev.setup_buf[0] & USB_DIR_IN) {
dev->dev.data_buf[0] = alt_setting_status->alt; dev->dev.data_buf[0] = alt_setting_status->alt;
len = 1; p->actual_length = 1;
} }
p->result = usbredir_handle_status(dev, p, alt_setting_status->status);
usbredir_handle_status(dev, alt_setting_status->status, len);
usb_generic_async_ctrl_complete(&dev->dev, p); usb_generic_async_ctrl_complete(&dev->dev, p);
} }
} }
@ -1522,18 +1529,18 @@ static void usbredir_control_packet(void *priv, uint64_t id,
p = usbredir_find_packet_by_id(dev, 0, id); p = usbredir_find_packet_by_id(dev, 0, id);
if (p) { if (p) {
len = usbredir_handle_status(dev, control_packet->status, len); usbredir_handle_status(dev, p, control_packet->status);
if (len > 0) { if (data_len > 0) {
usbredir_log_data(dev, "ctrl data in:", data, data_len); usbredir_log_data(dev, "ctrl data in:", data, data_len);
if (data_len <= sizeof(dev->dev.data_buf)) { if (data_len > sizeof(dev->dev.data_buf)) {
memcpy(dev->dev.data_buf, data, data_len);
} else {
ERROR("ctrl buffer too small (%d > %zu)\n", ERROR("ctrl buffer too small (%d > %zu)\n",
data_len, sizeof(dev->dev.data_buf)); data_len, sizeof(dev->dev.data_buf));
len = USB_RET_STALL; p->status = USB_RET_STALL;
data_len = len = sizeof(dev->dev.data_buf);
} }
memcpy(dev->dev.data_buf, data, data_len);
} }
p->result = len; p->actual_length = len;
usb_generic_async_ctrl_complete(&dev->dev, p); usb_generic_async_ctrl_complete(&dev->dev, p);
} }
free(data); free(data);
@ -1554,23 +1561,23 @@ static void usbredir_bulk_packet(void *priv, uint64_t id,
p = usbredir_find_packet_by_id(dev, ep, id); p = usbredir_find_packet_by_id(dev, ep, id);
if (p) { if (p) {
size_t size = (p->combined) ? p->combined->iov.size : p->iov.size; size_t size = (p->combined) ? p->combined->iov.size : p->iov.size;
len = usbredir_handle_status(dev, bulk_packet->status, len); usbredir_handle_status(dev, p, bulk_packet->status);
if (len > 0) { if (data_len > 0) {
usbredir_log_data(dev, "bulk data in:", data, data_len); usbredir_log_data(dev, "bulk data in:", data, data_len);
if (data_len <= size) { if (data_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);
}
} else {
ERROR("bulk got more data then requested (%d > %zd)\n", ERROR("bulk got more data then requested (%d > %zd)\n",
data_len, p->iov.size); data_len, p->iov.size);
len = USB_RET_BABBLE; 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);
} }
} }
p->result = len; p->actual_length = len;
if (p->pid == USB_TOKEN_IN && p->ep->pipeline) { if (p->pid == USB_TOKEN_IN && p->ep->pipeline) {
usb_combined_input_packet_complete(&dev->dev, p); usb_combined_input_packet_complete(&dev->dev, p);
} else { } else {
@ -1632,12 +1639,10 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id,
/* bufp_alloc also adds the packet to the ep queue */ /* bufp_alloc also adds the packet to the ep queue */
bufp_alloc(dev, data, data_len, interrupt_packet->status, ep); bufp_alloc(dev, data, data_len, interrupt_packet->status, ep);
} else { } else {
int len = interrupt_packet->length;
USBPacket *p = usbredir_find_packet_by_id(dev, ep, id); USBPacket *p = usbredir_find_packet_by_id(dev, ep, id);
if (p) { if (p) {
p->result = usbredir_handle_status(dev, usbredir_handle_status(dev, p, interrupt_packet->status);
interrupt_packet->status, len); p->actual_length = interrupt_packet->length;
usb_packet_complete(&dev->dev, p); usb_packet_complete(&dev->dev, p);
} }
} }

View File

@ -185,6 +185,21 @@ static void vfio_unmask_intx(VFIODevice *vdev)
ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set);
} }
#ifdef CONFIG_KVM /* Unused outside of CONFIG_KVM code */
static void vfio_mask_intx(VFIODevice *vdev)
{
struct vfio_irq_set irq_set = {
.argsz = sizeof(irq_set),
.flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_MASK,
.index = VFIO_PCI_INTX_IRQ_INDEX,
.start = 0,
.count = 1,
};
ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set);
}
#endif
/* /*
* Disabling BAR mmaping can be slow, but toggling it around INTx can * Disabling BAR mmaping can be slow, but toggling it around INTx can
* also be a huge overhead. We try to get the best of both worlds by * also be a huge overhead. We try to get the best of both worlds by
@ -248,6 +263,161 @@ static void vfio_eoi(VFIODevice *vdev)
vfio_unmask_intx(vdev); vfio_unmask_intx(vdev);
} }
static void vfio_enable_intx_kvm(VFIODevice *vdev)
{
#ifdef CONFIG_KVM
struct kvm_irqfd irqfd = {
.fd = event_notifier_get_fd(&vdev->intx.interrupt),
.gsi = vdev->intx.route.irq,
.flags = KVM_IRQFD_FLAG_RESAMPLE,
};
struct vfio_irq_set *irq_set;
int ret, argsz;
int32_t *pfd;
if (!kvm_irqchip_in_kernel() ||
vdev->intx.route.mode != PCI_INTX_ENABLED ||
!kvm_check_extension(kvm_state, KVM_CAP_IRQFD_RESAMPLE)) {
return;
}
/* Get to a known interrupt state */
qemu_set_fd_handler(irqfd.fd, NULL, NULL, vdev);
vfio_mask_intx(vdev);
vdev->intx.pending = false;
qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0);
/* Get an eventfd for resample/unmask */
if (event_notifier_init(&vdev->intx.unmask, 0)) {
error_report("vfio: Error: event_notifier_init failed eoi\n");
goto fail;
}
/* KVM triggers it, VFIO listens for it */
irqfd.resamplefd = event_notifier_get_fd(&vdev->intx.unmask);
if (kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd)) {
error_report("vfio: Error: Failed to setup resample irqfd: %m\n");
goto fail_irqfd;
}
argsz = sizeof(*irq_set) + sizeof(*pfd);
irq_set = g_malloc0(argsz);
irq_set->argsz = argsz;
irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_UNMASK;
irq_set->index = VFIO_PCI_INTX_IRQ_INDEX;
irq_set->start = 0;
irq_set->count = 1;
pfd = (int32_t *)&irq_set->data;
*pfd = irqfd.resamplefd;
ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
g_free(irq_set);
if (ret) {
error_report("vfio: Error: Failed to setup INTx unmask fd: %m\n");
goto fail_vfio;
}
/* Let'em rip */
vfio_unmask_intx(vdev);
vdev->intx.kvm_accel = true;
DPRINTF("%s(%04x:%02x:%02x.%x) KVM INTx accel enabled\n",
__func__, vdev->host.domain, vdev->host.bus,
vdev->host.slot, vdev->host.function);
return;
fail_vfio:
irqfd.flags = KVM_IRQFD_FLAG_DEASSIGN;
kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd);
fail_irqfd:
event_notifier_cleanup(&vdev->intx.unmask);
fail:
qemu_set_fd_handler(irqfd.fd, vfio_intx_interrupt, NULL, vdev);
vfio_unmask_intx(vdev);
#endif
}
static void vfio_disable_intx_kvm(VFIODevice *vdev)
{
#ifdef CONFIG_KVM
struct kvm_irqfd irqfd = {
.fd = event_notifier_get_fd(&vdev->intx.interrupt),
.gsi = vdev->intx.route.irq,
.flags = KVM_IRQFD_FLAG_DEASSIGN,
};
if (!vdev->intx.kvm_accel) {
return;
}
/*
* Get to a known state, hardware masked, QEMU ready to accept new
* interrupts, QEMU IRQ de-asserted.
*/
vfio_mask_intx(vdev);
vdev->intx.pending = false;
qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0);
/* Tell KVM to stop listening for an INTx irqfd */
if (kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd)) {
error_report("vfio: Error: Failed to disable INTx irqfd: %m\n");
}
/* We only need to close the eventfd for VFIO to cleanup the kernel side */
event_notifier_cleanup(&vdev->intx.unmask);
/* QEMU starts listening for interrupt events. */
qemu_set_fd_handler(irqfd.fd, vfio_intx_interrupt, NULL, vdev);
vdev->intx.kvm_accel = false;
/* If we've missed an event, let it re-fire through QEMU */
vfio_unmask_intx(vdev);
DPRINTF("%s(%04x:%02x:%02x.%x) KVM INTx accel disabled\n",
__func__, vdev->host.domain, vdev->host.bus,
vdev->host.slot, vdev->host.function);
#endif
}
static void vfio_update_irq(PCIDevice *pdev)
{
VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
PCIINTxRoute route;
if (vdev->interrupt != VFIO_INT_INTx) {
return;
}
route = pci_device_route_intx_to_irq(&vdev->pdev, vdev->intx.pin);
if (!pci_intx_route_changed(&vdev->intx.route, &route)) {
return; /* Nothing changed */
}
DPRINTF("%s(%04x:%02x:%02x.%x) IRQ moved %d -> %d\n", __func__,
vdev->host.domain, vdev->host.bus, vdev->host.slot,
vdev->host.function, vdev->intx.route.irq, route.irq);
vfio_disable_intx_kvm(vdev);
vdev->intx.route = route;
if (route.mode != PCI_INTX_ENABLED) {
return;
}
vfio_enable_intx_kvm(vdev);
/* Re-enable the interrupt in cased we missed an EOI */
vfio_eoi(vdev);
}
static int vfio_enable_intx(VFIODevice *vdev) static int vfio_enable_intx(VFIODevice *vdev)
{ {
uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1); uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1);
@ -262,6 +432,18 @@ static int vfio_enable_intx(VFIODevice *vdev)
vfio_disable_interrupts(vdev); vfio_disable_interrupts(vdev);
vdev->intx.pin = pin - 1; /* Pin A (1) -> irq[0] */ vdev->intx.pin = pin - 1; /* Pin A (1) -> irq[0] */
#ifdef CONFIG_KVM
/*
* Only conditional to avoid generating error messages on platforms
* where we won't actually use the result anyway.
*/
if (kvm_check_extension(kvm_state, KVM_CAP_IRQFD_RESAMPLE)) {
vdev->intx.route = pci_device_route_intx_to_irq(&vdev->pdev,
vdev->intx.pin);
}
#endif
ret = event_notifier_init(&vdev->intx.interrupt, 0); ret = event_notifier_init(&vdev->intx.interrupt, 0);
if (ret) { if (ret) {
error_report("vfio: Error: event_notifier_init failed\n"); error_report("vfio: Error: event_notifier_init failed\n");
@ -290,6 +472,8 @@ static int vfio_enable_intx(VFIODevice *vdev)
return -errno; return -errno;
} }
vfio_enable_intx_kvm(vdev);
vdev->interrupt = VFIO_INT_INTx; vdev->interrupt = VFIO_INT_INTx;
DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain, DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain,
@ -303,6 +487,7 @@ static void vfio_disable_intx(VFIODevice *vdev)
int fd; int fd;
qemu_del_timer(vdev->intx.mmap_timer); qemu_del_timer(vdev->intx.mmap_timer);
vfio_disable_intx_kvm(vdev);
vfio_disable_irqindex(vdev, VFIO_PCI_INTX_IRQ_INDEX); vfio_disable_irqindex(vdev, VFIO_PCI_INTX_IRQ_INDEX);
vdev->intx.pending = false; vdev->intx.pending = false;
qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0); qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0);
@ -503,28 +688,6 @@ static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr)
vector->use = false; vector->use = false;
} }
/* TODO This should move to msi.c */
static MSIMessage msi_get_msg(PCIDevice *pdev, unsigned int vector)
{
uint16_t flags = pci_get_word(pdev->config + pdev->msi_cap + PCI_MSI_FLAGS);
bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
MSIMessage msg;
if (msi64bit) {
msg.address = pci_get_quad(pdev->config +
pdev->msi_cap + PCI_MSI_ADDRESS_LO);
} else {
msg.address = pci_get_long(pdev->config +
pdev->msi_cap + PCI_MSI_ADDRESS_LO);
}
msg.data = pci_get_word(pdev->config + pdev->msi_cap +
(msi64bit ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32));
msg.data += vector;
return msg;
}
static void vfio_enable_msix(VFIODevice *vdev) static void vfio_enable_msix(VFIODevice *vdev)
{ {
vfio_disable_interrupts(vdev); vfio_disable_interrupts(vdev);
@ -563,7 +726,7 @@ retry:
error_report("vfio: Error: event_notifier_init failed\n"); error_report("vfio: Error: event_notifier_init failed\n");
} }
msg = msi_get_msg(&vdev->pdev, i); msg = msi_get_message(&vdev->pdev, i);
/* /*
* Attempt to enable route through KVM irqchip, * Attempt to enable route through KVM irqchip,
@ -1839,6 +2002,7 @@ static int vfio_initfn(PCIDevice *pdev)
if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) { if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) {
vdev->intx.mmap_timer = qemu_new_timer_ms(vm_clock, vdev->intx.mmap_timer = qemu_new_timer_ms(vm_clock,
vfio_intx_mmap_enable, vdev); vfio_intx_mmap_enable, vdev);
pci_device_set_intx_routing_notifier(&vdev->pdev, vfio_update_irq);
ret = vfio_enable_intx(vdev); ret = vfio_enable_intx(vdev);
if (ret) { if (ret) {
goto out_teardown; goto out_teardown;

View File

@ -84,9 +84,10 @@ static void pci_vga_ioport_write(void *ptr, hwaddr addr,
uint64_t val, unsigned size) uint64_t val, unsigned size)
{ {
PCIVGAState *d = ptr; PCIVGAState *d = ptr;
switch (size) { switch (size) {
case 1: case 1:
vga_ioport_write(&d->vga, addr, val); vga_ioport_write(&d->vga, addr + 0x3c0, val);
break; break;
case 2: case 2:
/* /*
@ -94,8 +95,8 @@ static void pci_vga_ioport_write(void *ptr, hwaddr addr,
* indexed registers with a single word write because the * indexed registers with a single word write because the
* index byte is updated first. * index byte is updated first.
*/ */
vga_ioport_write(&d->vga, addr, val & 0xff); vga_ioport_write(&d->vga, addr + 0x3c0, val & 0xff);
vga_ioport_write(&d->vga, addr+1, (val >> 8) & 0xff); vga_ioport_write(&d->vga, addr + 0x3c1, (val >> 8) & 0xff);
break; break;
} }
} }

View File

@ -2321,9 +2321,8 @@ static const MemoryRegionPortio vbe_portio_list[] = {
{ 0, 1, 2, .read = vbe_ioport_read_index, .write = vbe_ioport_write_index }, { 0, 1, 2, .read = vbe_ioport_read_index, .write = vbe_ioport_write_index },
# ifdef TARGET_I386 # ifdef TARGET_I386
{ 1, 1, 2, .read = vbe_ioport_read_data, .write = vbe_ioport_write_data }, { 1, 1, 2, .read = vbe_ioport_read_data, .write = vbe_ioport_write_data },
# else
{ 2, 1, 2, .read = vbe_ioport_read_data, .write = vbe_ioport_write_data },
# endif # endif
{ 2, 1, 2, .read = vbe_ioport_read_data, .write = vbe_ioport_write_data },
PORTIO_END_OF_LIST(), PORTIO_END_OF_LIST(),
}; };

View File

@ -852,6 +852,41 @@ static void virtio_balloon_exit_pci(PCIDevice *pci_dev)
virtio_exit_pci(pci_dev); virtio_exit_pci(pci_dev);
} }
static int virtio_rng_init_pci(PCIDevice *pci_dev)
{
VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
VirtIODevice *vdev;
if (proxy->rng.rng == NULL) {
proxy->rng.default_backend = RNG_RANDOM(object_new(TYPE_RNG_RANDOM));
object_property_add_child(OBJECT(pci_dev),
"default-backend",
OBJECT(proxy->rng.default_backend),
NULL);
object_property_set_link(OBJECT(pci_dev),
OBJECT(proxy->rng.default_backend),
"rng", NULL);
}
vdev = virtio_rng_init(&pci_dev->qdev, &proxy->rng);
if (!vdev) {
return -1;
}
virtio_init_pci(proxy, vdev);
return 0;
}
static void virtio_rng_exit_pci(PCIDevice *pci_dev)
{
VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
virtio_pci_stop_ioeventfd(proxy);
virtio_rng_exit(proxy->vdev);
virtio_exit_pci(pci_dev);
}
static Property virtio_blk_properties[] = { static Property virtio_blk_properties[] = {
DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0), DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, blk.conf), DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, blk.conf),
@ -982,6 +1017,50 @@ static TypeInfo virtio_balloon_info = {
.class_init = virtio_balloon_class_init, .class_init = virtio_balloon_class_init,
}; };
static void virtio_rng_initfn(Object *obj)
{
PCIDevice *pci_dev = PCI_DEVICE(obj);
VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
object_property_add_link(obj, "rng", TYPE_RNG_BACKEND,
(Object **)&proxy->rng.rng, NULL);
}
static Property virtio_rng_properties[] = {
DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
/* Set a default rate limit of 2^47 bytes per minute or roughly 2TB/s. If
you have an entropy source capable of generating more entropy than this
and you can pass it through via virtio-rng, then hats off to you. Until
then, this is unlimited for all practical purposes.
*/
DEFINE_PROP_UINT64("max-bytes", VirtIOPCIProxy, rng.max_bytes, INT64_MAX),
DEFINE_PROP_UINT32("period", VirtIOPCIProxy, rng.period_ms, 1 << 16),
DEFINE_PROP_END_OF_LIST(),
};
static void virtio_rng_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->init = virtio_rng_init_pci;
k->exit = virtio_rng_exit_pci;
k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
k->device_id = PCI_DEVICE_ID_VIRTIO_RNG;
k->revision = VIRTIO_PCI_ABI_VERSION;
k->class_id = PCI_CLASS_OTHERS;
dc->reset = virtio_pci_reset;
dc->props = virtio_rng_properties;
}
static TypeInfo virtio_rng_info = {
.name = "virtio-rng-pci",
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(VirtIOPCIProxy),
.instance_init = virtio_rng_initfn,
.class_init = virtio_rng_class_init,
};
static int virtio_scsi_init_pci(PCIDevice *pci_dev) static int virtio_scsi_init_pci(PCIDevice *pci_dev)
{ {
VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
@ -1046,6 +1125,7 @@ static void virtio_pci_register_types(void)
type_register_static(&virtio_serial_info); type_register_static(&virtio_serial_info);
type_register_static(&virtio_balloon_info); type_register_static(&virtio_balloon_info);
type_register_static(&virtio_scsi_info); type_register_static(&virtio_scsi_info);
type_register_static(&virtio_rng_info);
} }
type_init(virtio_pci_register_types) type_init(virtio_pci_register_types)

View File

@ -17,6 +17,7 @@
#include "virtio-blk.h" #include "virtio-blk.h"
#include "virtio-net.h" #include "virtio-net.h"
#include "virtio-rng.h"
#include "virtio-serial.h" #include "virtio-serial.h"
#include "virtio-scsi.h" #include "virtio-scsi.h"
@ -46,6 +47,7 @@ typedef struct {
virtio_serial_conf serial; virtio_serial_conf serial;
virtio_net_conf net; virtio_net_conf net;
VirtIOSCSIConf scsi; VirtIOSCSIConf scsi;
VirtIORNGConf rng;
bool ioeventfd_disabled; bool ioeventfd_disabled;
bool ioeventfd_started; bool ioeventfd_started;
VirtIOIRQFD *vector_irqfd; VirtIOIRQFD *vector_irqfd;

258
hw/virtio-rng.c Normal file
View File

@ -0,0 +1,258 @@
/*
* A virtio device implementing a hardware random number generator.
*
* Copyright 2012 Red Hat, Inc.
* Copyright 2012 Amit Shah <amit.shah@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or
* (at your option) any later version. See the COPYING file in the
* top-level directory.
*/
#include "iov.h"
#include "qdev.h"
#include "virtio.h"
#include "virtio-rng.h"
#include "qemu/rng.h"
typedef struct VirtIORNG {
VirtIODevice vdev;
DeviceState *qdev;
/* Only one vq - guest puts buffer(s) on it when it needs entropy */
VirtQueue *vq;
VirtQueueElement elem;
/* Config data for the device -- currently only chardev */
VirtIORNGConf *conf;
/* Whether we've popped a vq element into 'elem' above */
bool popped;
RngBackend *rng;
/* We purposefully don't migrate this state. The quota will reset on the
* destination as a result. Rate limiting is host state, not guest state.
*/
QEMUTimer *rate_limit_timer;
int64_t quota_remaining;
} VirtIORNG;
static bool is_guest_ready(VirtIORNG *vrng)
{
if (virtio_queue_ready(vrng->vq)
&& (vrng->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
return true;
}
return false;
}
static size_t pop_an_elem(VirtIORNG *vrng)
{
size_t size;
if (!vrng->popped && !virtqueue_pop(vrng->vq, &vrng->elem)) {
return 0;
}
vrng->popped = true;
size = iov_size(vrng->elem.in_sg, vrng->elem.in_num);
return size;
}
static void virtio_rng_process(VirtIORNG *vrng);
/* Send data from a char device over to the guest */
static void chr_read(void *opaque, const void *buf, size_t size)
{
VirtIORNG *vrng = opaque;
size_t len;
int offset;
if (!is_guest_ready(vrng)) {
return;
}
vrng->quota_remaining -= size;
offset = 0;
while (offset < size) {
if (!pop_an_elem(vrng)) {
break;
}
len = iov_from_buf(vrng->elem.in_sg, vrng->elem.in_num,
0, buf + offset, size - offset);
offset += len;
virtqueue_push(vrng->vq, &vrng->elem, len);
vrng->popped = false;
}
virtio_notify(&vrng->vdev, vrng->vq);
/*
* Lastly, if we had multiple elems queued by the guest, and we
* didn't have enough data to fill them all, indicate we want more
* data.
*/
virtio_rng_process(vrng);
}
static void virtio_rng_process(VirtIORNG *vrng)
{
ssize_t size;
if (!is_guest_ready(vrng)) {
return;
}
size = pop_an_elem(vrng);
size = MIN(vrng->quota_remaining, size);
if (size > 0) {
rng_backend_request_entropy(vrng->rng, size, chr_read, vrng);
}
}
static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
virtio_rng_process(vrng);
}
static uint32_t get_features(VirtIODevice *vdev, uint32_t f)
{
return f;
}
static void virtio_rng_save(QEMUFile *f, void *opaque)
{
VirtIORNG *vrng = opaque;
virtio_save(&vrng->vdev, f);
qemu_put_byte(f, vrng->popped);
if (vrng->popped) {
int i;
qemu_put_be32(f, vrng->elem.index);
qemu_put_be32(f, vrng->elem.in_num);
for (i = 0; i < vrng->elem.in_num; i++) {
qemu_put_be64(f, vrng->elem.in_addr[i]);
}
qemu_put_be32(f, vrng->elem.out_num);
for (i = 0; i < vrng->elem.out_num; i++) {
qemu_put_be64(f, vrng->elem.out_addr[i]);
}
}
}
static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id)
{
VirtIORNG *vrng = opaque;
if (version_id != 1) {
return -EINVAL;
}
virtio_load(&vrng->vdev, f);
vrng->popped = qemu_get_byte(f);
if (vrng->popped) {
int i;
vrng->elem.index = qemu_get_be32(f);
vrng->elem.in_num = qemu_get_be32(f);
g_assert(vrng->elem.in_num < VIRTQUEUE_MAX_SIZE);
for (i = 0; i < vrng->elem.in_num; i++) {
vrng->elem.in_addr[i] = qemu_get_be64(f);
}
vrng->elem.out_num = qemu_get_be32(f);
g_assert(vrng->elem.out_num < VIRTQUEUE_MAX_SIZE);
for (i = 0; i < vrng->elem.out_num; i++) {
vrng->elem.out_addr[i] = qemu_get_be64(f);
}
virtqueue_map_sg(vrng->elem.in_sg, vrng->elem.in_addr,
vrng->elem.in_num, 1);
virtqueue_map_sg(vrng->elem.out_sg, vrng->elem.out_addr,
vrng->elem.out_num, 0);
}
/* We may have an element ready but couldn't process it due to a quota
limit. Make sure to try again after live migration when the quota may
have been reset.
*/
virtio_rng_process(vrng);
return 0;
}
static void check_rate_limit(void *opaque)
{
VirtIORNG *s = opaque;
s->quota_remaining = s->conf->max_bytes;
virtio_rng_process(s);
qemu_mod_timer(s->rate_limit_timer,
qemu_get_clock_ms(vm_clock) + s->conf->period_ms);
}
VirtIODevice *virtio_rng_init(DeviceState *dev, VirtIORNGConf *conf)
{
VirtIORNG *vrng;
VirtIODevice *vdev;
Error *local_err = NULL;
vdev = virtio_common_init("virtio-rng", VIRTIO_ID_RNG, 0,
sizeof(VirtIORNG));
vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
vrng->rng = conf->rng;
if (vrng->rng == NULL) {
qerror_report(QERR_INVALID_PARAMETER_VALUE, "rng", "a valid object");
return NULL;
}
rng_backend_open(vrng->rng, &local_err);
if (local_err) {
qerror_report_err(local_err);
error_free(local_err);
return NULL;
}
vrng->vq = virtio_add_queue(vdev, 8, handle_input);
vrng->vdev.get_features = get_features;
vrng->qdev = dev;
vrng->conf = conf;
vrng->popped = false;
vrng->quota_remaining = vrng->conf->max_bytes;
g_assert_cmpint(vrng->conf->max_bytes, <=, INT64_MAX);
vrng->rate_limit_timer = qemu_new_timer_ms(vm_clock,
check_rate_limit, vrng);
qemu_mod_timer(vrng->rate_limit_timer,
qemu_get_clock_ms(vm_clock) + vrng->conf->period_ms);
register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save,
virtio_rng_load, vrng);
return vdev;
}
void virtio_rng_exit(VirtIODevice *vdev)
{
VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
unregister_savevm(vrng->qdev, "virtio-rng", vrng);
virtio_cleanup(vdev);
}

28
hw/virtio-rng.h Normal file
View File

@ -0,0 +1,28 @@
/*
* Virtio RNG Support
*
* Copyright Red Hat, Inc. 2012
* Copyright Amit Shah <amit.shah@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or
* (at your option) any later version. See the COPYING file in the
* top-level directory.
*/
#ifndef _QEMU_VIRTIO_RNG_H
#define _QEMU_VIRTIO_RNG_H
#include "qemu/rng.h"
#include "qemu/rng-random.h"
/* The Virtio ID for the virtio rng device */
#define VIRTIO_ID_RNG 4
struct VirtIORNGConf {
RngBackend *rng;
uint64_t max_bytes;
uint32_t period_ms;
RndRandom *default_backend;
};
#endif

View File

@ -204,7 +204,7 @@ static void virtio_scsi_bad_req(void)
static void qemu_sgl_init_external(QEMUSGList *qsgl, struct iovec *sg, static void qemu_sgl_init_external(QEMUSGList *qsgl, struct iovec *sg,
hwaddr *addr, int num) hwaddr *addr, int num)
{ {
memset(qsgl, 0, sizeof(*qsgl)); qemu_sglist_init(qsgl, num, &dma_context_memory);
while (num--) { while (num--) {
qemu_sglist_add(qsgl, *(addr++), (sg++)->iov_len); qemu_sglist_add(qsgl, *(addr++), (sg++)->iov_len);
} }
@ -596,6 +596,10 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
VirtIOSCSIEvent *evt; VirtIOSCSIEvent *evt;
int in_size; int in_size;
if (!(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
return;
}
if (!req) { if (!req) {
s->events_dropped = true; s->events_dropped = true;
return; return;
@ -648,7 +652,6 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense)
VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
if (((s->vdev.guest_features >> VIRTIO_SCSI_F_CHANGE) & 1) && if (((s->vdev.guest_features >> VIRTIO_SCSI_F_CHANGE) & 1) &&
(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) &&
dev->type != TYPE_ROM) { dev->type != TYPE_ROM) {
virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_PARAM_CHANGE, virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_PARAM_CHANGE,
sense.asc | (sense.ascq << 8)); sense.asc | (sense.ascq << 8));
@ -659,8 +662,7 @@ static void virtio_scsi_hotplug(SCSIBus *bus, SCSIDevice *dev)
{ {
VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
if (((s->vdev.guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) && if ((s->vdev.guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) {
(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET, virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET,
VIRTIO_SCSI_EVT_RESET_RESCAN); VIRTIO_SCSI_EVT_RESET_RESCAN);
} }

View File

@ -203,6 +203,8 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *serial);
VirtIODevice *virtio_balloon_init(DeviceState *dev); VirtIODevice *virtio_balloon_init(DeviceState *dev);
typedef struct VirtIOSCSIConf VirtIOSCSIConf; typedef struct VirtIOSCSIConf VirtIOSCSIConf;
VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *conf); VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *conf);
typedef struct VirtIORNGConf VirtIORNGConf;
VirtIODevice *virtio_rng_init(DeviceState *dev, VirtIORNGConf *conf);
#ifdef CONFIG_LINUX #ifdef CONFIG_LINUX
VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf); VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf);
#endif #endif
@ -213,6 +215,7 @@ void virtio_blk_exit(VirtIODevice *vdev);
void virtio_serial_exit(VirtIODevice *vdev); void virtio_serial_exit(VirtIODevice *vdev);
void virtio_balloon_exit(VirtIODevice *vdev); void virtio_balloon_exit(VirtIODevice *vdev);
void virtio_scsi_exit(VirtIODevice *vdev); void virtio_scsi_exit(VirtIODevice *vdev);
void virtio_rng_exit(VirtIODevice *vdev);
#define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \ #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \
DEFINE_PROP_BIT("indirect_desc", _state, _field, \ DEFINE_PROP_BIT("indirect_desc", _state, _field, \

View File

@ -946,6 +946,22 @@ void object_property_add_str(Object *obj, const char *name,
void (*set)(Object *, const char *, struct Error **), void (*set)(Object *, const char *, struct Error **),
struct Error **errp); struct Error **errp);
/**
* object_property_add_bool:
* @obj: the object to add a property to
* @name: the name of the property
* @get: the getter or NULL if the property is write-only.
* @set: the setter or NULL if the property is read-only
* @errp: if an error occurs, a pointer to an area to store the error
*
* Add a bool property using getters/setters. This function will add a
* property of type 'bool'.
*/
void object_property_add_bool(Object *obj, const char *name,
bool (*get)(Object *, struct Error **),
void (*set)(Object *, bool, struct Error **),
struct Error **errp);
/** /**
* object_child_foreach: * object_child_foreach:
* @obj: the object whose children will be navigated * @obj: the object whose children will be navigated

22
include/qemu/rng-random.h Normal file
View File

@ -0,0 +1,22 @@
/*
* QEMU Random Number Generator Backend
*
* 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.
*/
#ifndef QEMU_RNG_RANDOM_H
#define QEMU_RNG_RANDOM_H
#include "qemu/object.h"
#define TYPE_RNG_RANDOM "rng-random"
#define RNG_RANDOM(obj) OBJECT_CHECK(RndRandom, (obj), TYPE_RNG_RANDOM)
typedef struct RndRandom RndRandom;
#endif

93
include/qemu/rng.h Normal file
View File

@ -0,0 +1,93 @@
/*
* QEMU Random Number Generator Backend
*
* 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.
*/
#ifndef QEMU_RNG_H
#define QEMU_RNG_H
#include "qemu/object.h"
#include "qemu-common.h"
#include "error.h"
#define TYPE_RNG_BACKEND "rng-backend"
#define RNG_BACKEND(obj) \
OBJECT_CHECK(RngBackend, (obj), TYPE_RNG_BACKEND)
#define RNG_BACKEND_GET_CLASS(obj) \
OBJECT_GET_CLASS(RngBackendClass, (obj), TYPE_RNG_BACKEND)
#define RNG_BACKEND_CLASS(klass) \
OBJECT_CLASS_CHECK(RngBackendClass, (klass), TYPE_RNG_BACKEND)
typedef struct RngBackendClass RngBackendClass;
typedef struct RngBackend RngBackend;
typedef void (EntropyReceiveFunc)(void *opaque,
const void *data,
size_t size);
struct RngBackendClass
{
ObjectClass parent_class;
void (*request_entropy)(RngBackend *s, size_t size,
EntropyReceiveFunc *recieve_entropy, void *opaque);
void (*cancel_requests)(RngBackend *s);
void (*opened)(RngBackend *s, Error **errp);
};
struct RngBackend
{
Object parent;
/*< protected >*/
bool opened;
};
/**
* rng_backend_request_entropy:
* @s: the backend to request entropy from
* @size: the number of bytes of data to request
* @receive_entropy: a function to be invoked when entropy is available
* @opaque: data that should be passed to @receive_entropy
*
* This function is used by the front-end to request entropy from an entropy
* source. This function can be called multiple times before @receive_entropy
* is invoked with different values of @receive_entropy and @opaque. The
* backend will queue each request and handle appropriate.
*
* The backend does not need to pass the full amount of data to @receive_entropy
* but will pass at a value greater than 0.
*/
void rng_backend_request_entropy(RngBackend *s, size_t size,
EntropyReceiveFunc *receive_entropy,
void *opaque);
/**
* rng_backend_cancel_requests:
* @s: the backend to cancel all pending requests in
*
* Cancels all pending requests submitted by @rng_backend_request_entropy. This
* should be used by a device during reset or in preparation for live migration
* to stop tracking any request.
*/
void rng_backend_cancel_requests(RngBackend *s);
/**
* rng_backend_open:
* @s: the backend to open
* @errp: a pointer to return the #Error object if an error occurs.
*
* This function will open the backend if it is not already open. Calling this
* function on an already opened backend will not result in an error.
*/
void rng_backend_open(RngBackend *s, Error **errp);
#endif

View File

@ -17,8 +17,8 @@
* Authors: Hollis Blanchard <hollisb@us.ibm.com> * Authors: Hollis Blanchard <hollisb@us.ibm.com>
*/ */
#ifndef __POWERPC_KVM_PARA_H__ #ifndef _UAPI__POWERPC_KVM_PARA_H__
#define __POWERPC_KVM_PARA_H__ #define _UAPI__POWERPC_KVM_PARA_H__
#include <linux/types.h> #include <linux/types.h>
@ -87,4 +87,4 @@ struct kvm_vcpu_arch_shared {
#define KVM_MAGIC_FEAT_MAS0_TO_SPRG7 (1 << 1) #define KVM_MAGIC_FEAT_MAS0_TO_SPRG7 (1 << 1)
#endif /* __POWERPC_KVM_PARA_H__ */ #endif /* _UAPI__POWERPC_KVM_PARA_H__ */

View File

@ -1,5 +1,5 @@
/* /*
* definition for paravirtual devices on s390 * User API definitions for paravirtual devices on s390
* *
* Copyright IBM Corp. 2008 * Copyright IBM Corp. 2008
* *
@ -9,9 +9,3 @@
* *
* Author(s): Christian Borntraeger <borntraeger@de.ibm.com> * Author(s): Christian Borntraeger <borntraeger@de.ibm.com>
*/ */
#ifndef __S390_KVM_PARA_H
#define __S390_KVM_PARA_H
#endif /* __S390_KVM_PARA_H */

View File

@ -9,6 +9,22 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/ioctl.h> #include <linux/ioctl.h>
#define DE_VECTOR 0
#define DB_VECTOR 1
#define BP_VECTOR 3
#define OF_VECTOR 4
#define BR_VECTOR 5
#define UD_VECTOR 6
#define NM_VECTOR 7
#define DF_VECTOR 8
#define TS_VECTOR 10
#define NP_VECTOR 11
#define SS_VECTOR 12
#define GP_VECTOR 13
#define PF_VECTOR 14
#define MF_VECTOR 16
#define MC_VECTOR 18
/* Select x86 specific features in <linux/kvm.h> */ /* Select x86 specific features in <linux/kvm.h> */
#define __KVM_HAVE_PIT #define __KVM_HAVE_PIT
#define __KVM_HAVE_IOAPIC #define __KVM_HAVE_IOAPIC
@ -25,6 +41,7 @@
#define __KVM_HAVE_DEBUGREGS #define __KVM_HAVE_DEBUGREGS
#define __KVM_HAVE_XSAVE #define __KVM_HAVE_XSAVE
#define __KVM_HAVE_XCRS #define __KVM_HAVE_XCRS
#define __KVM_HAVE_READONLY_MEM
/* Architectural interrupt line count. */ /* Architectural interrupt line count. */
#define KVM_NR_INTERRUPTS 256 #define KVM_NR_INTERRUPTS 256

View File

@ -101,9 +101,13 @@ struct kvm_userspace_memory_region {
__u64 userspace_addr; /* start of the userspace allocated memory */ __u64 userspace_addr; /* start of the userspace allocated memory */
}; };
/* for kvm_memory_region::flags */ /*
#define KVM_MEM_LOG_DIRTY_PAGES 1UL * The bit 0 ~ bit 15 of kvm_memory_region::flags are visible for userspace,
#define KVM_MEMSLOT_INVALID (1UL << 1) * other bits are reserved for kvm internal use which are defined in
* include/linux/kvm_host.h.
*/
#define KVM_MEM_LOG_DIRTY_PAGES (1UL << 0)
#define KVM_MEM_READONLY (1UL << 1)
/* for KVM_IRQ_LINE */ /* for KVM_IRQ_LINE */
struct kvm_irq_level { struct kvm_irq_level {
@ -618,6 +622,10 @@ struct kvm_ppc_smmu_info {
#define KVM_CAP_PPC_GET_SMMU_INFO 78 #define KVM_CAP_PPC_GET_SMMU_INFO 78
#define KVM_CAP_S390_COW 79 #define KVM_CAP_S390_COW 79
#define KVM_CAP_PPC_ALLOC_HTAB 80 #define KVM_CAP_PPC_ALLOC_HTAB 80
#ifdef __KVM_HAVE_READONLY_MEM
#define KVM_CAP_READONLY_MEM 81
#endif
#define KVM_CAP_IRQFD_RESAMPLE 82
#ifdef KVM_CAP_IRQ_ROUTING #ifdef KVM_CAP_IRQ_ROUTING
@ -683,12 +691,21 @@ struct kvm_xen_hvm_config {
#endif #endif
#define KVM_IRQFD_FLAG_DEASSIGN (1 << 0) #define KVM_IRQFD_FLAG_DEASSIGN (1 << 0)
/*
* Available with KVM_CAP_IRQFD_RESAMPLE
*
* KVM_IRQFD_FLAG_RESAMPLE indicates resamplefd is valid and specifies
* the irqfd to operate in resampling mode for level triggered interrupt
* emlation. See Documentation/virtual/kvm/api.txt.
*/
#define KVM_IRQFD_FLAG_RESAMPLE (1 << 1)
struct kvm_irqfd { struct kvm_irqfd {
__u32 fd; __u32 fd;
__u32 gsi; __u32 gsi;
__u32 flags; __u32 flags;
__u8 pad[20]; __u32 resamplefd;
__u8 pad[16];
}; };
struct kvm_clock_data { struct kvm_clock_data {

View File

@ -1,5 +1,5 @@
#ifndef __LINUX_KVM_PARA_H #ifndef _UAPI__LINUX_KVM_PARA_H
#define __LINUX_KVM_PARA_H #define _UAPI__LINUX_KVM_PARA_H
/* /*
* This header file provides a method for making a hypercall to the host * This header file provides a method for making a hypercall to the host
@ -25,4 +25,4 @@
*/ */
#include <asm/kvm_para.h> #include <asm/kvm_para.h>
#endif /* __LINUX_KVM_PARA_H */ #endif /* _UAPI__LINUX_KVM_PARA_H */

View File

@ -8,8 +8,8 @@
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef VFIO_H #ifndef _UAPIVFIO_H
#define VFIO_H #define _UAPIVFIO_H
#include <linux/types.h> #include <linux/types.h>
#include <linux/ioctl.h> #include <linux/ioctl.h>
@ -365,4 +365,4 @@ struct vfio_iommu_type1_dma_unmap {
#define VFIO_IOMMU_UNMAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 14) #define VFIO_IOMMU_UNMAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 14)
#endif /* VFIO_H */ #endif /* _UAPIVFIO_H */

View File

@ -1,5 +1,5 @@
#ifndef _LINUX_VIRTIO_CONFIG_H #ifndef _UAPI_LINUX_VIRTIO_CONFIG_H
#define _LINUX_VIRTIO_CONFIG_H #define _UAPI_LINUX_VIRTIO_CONFIG_H
/* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so /* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so
* anyone can use the definitions to implement compatible drivers/servers. * anyone can use the definitions to implement compatible drivers/servers.
* *
@ -51,4 +51,4 @@
* suppressed them? */ * suppressed them? */
#define VIRTIO_F_NOTIFY_ON_EMPTY 24 #define VIRTIO_F_NOTIFY_ON_EMPTY 24
#endif /* _LINUX_VIRTIO_CONFIG_H */ #endif /* _UAPI_LINUX_VIRTIO_CONFIG_H */

View File

@ -1,5 +1,5 @@
#ifndef _LINUX_VIRTIO_RING_H #ifndef _UAPI_LINUX_VIRTIO_RING_H
#define _LINUX_VIRTIO_RING_H #define _UAPI_LINUX_VIRTIO_RING_H
/* An interface for efficient virtio implementation, currently for use by KVM /* An interface for efficient virtio implementation, currently for use by KVM
* and lguest, but hopefully others soon. Do NOT change this since it will * and lguest, but hopefully others soon. Do NOT change this since it will
* break existing servers and clients. * break existing servers and clients.
@ -160,4 +160,4 @@ static __inline__ int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old
return (__u16)(new_idx - event_idx - 1) < (__u16)(new_idx - old); return (__u16)(new_idx - event_idx - 1) < (__u16)(new_idx - old);
} }
#endif /* _LINUX_VIRTIO_RING_H */ #endif /* _UAPI_LINUX_VIRTIO_RING_H */

23
nbd.c
View File

@ -596,24 +596,23 @@ int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize)
return -serrno; return -serrno;
} }
if (flags & NBD_FLAG_READ_ONLY) { if (ioctl(fd, NBD_SET_FLAGS, flags) < 0) {
int read_only = 1; if (errno == ENOTTY) {
TRACE("Setting readonly attribute"); int read_only = (flags & NBD_FLAG_READ_ONLY) != 0;
TRACE("Setting readonly attribute");
if (ioctl(fd, BLKROSET, (unsigned long) &read_only) < 0) { if (ioctl(fd, BLKROSET, (unsigned long) &read_only) < 0) {
int serrno = errno;
LOG("Failed setting read-only attribute");
return -serrno;
}
} else {
int serrno = errno; int serrno = errno;
LOG("Failed setting read-only attribute"); LOG("Failed setting flags");
return -serrno; return -serrno;
} }
} }
if (ioctl(fd, NBD_SET_FLAGS, flags) < 0
&& errno != ENOTTY) {
int serrno = errno;
LOG("Failed setting flags");
return -serrno;
}
TRACE("Negotiation ended"); TRACE("Negotiation ended");
return 0; return 0;

32
osdep.c
View File

@ -54,38 +54,6 @@ static bool fips_enabled = false;
static const char *qemu_version = QEMU_VERSION; static const char *qemu_version = QEMU_VERSION;
static int default_fdset_get_fd(int64_t fdset_id, int flags)
{
return -1;
}
QEMU_WEAK_ALIAS(monitor_fdset_get_fd, default_fdset_get_fd);
#define monitor_fdset_get_fd \
QEMU_WEAK_REF(monitor_fdset_get_fd, default_fdset_get_fd)
static int default_fdset_dup_fd_add(int64_t fdset_id, int dup_fd)
{
return -1;
}
QEMU_WEAK_ALIAS(monitor_fdset_dup_fd_add, default_fdset_dup_fd_add);
#define monitor_fdset_dup_fd_add \
QEMU_WEAK_REF(monitor_fdset_dup_fd_add, default_fdset_dup_fd_add)
static int default_fdset_dup_fd_remove(int dup_fd)
{
return -1;
}
QEMU_WEAK_ALIAS(monitor_fdset_dup_fd_remove, default_fdset_dup_fd_remove);
#define monitor_fdset_dup_fd_remove \
QEMU_WEAK_REF(monitor_fdset_dup_fd_remove, default_fdset_dup_fd_remove)
static int default_fdset_dup_fd_find(int dup_fd)
{
return -1;
}
QEMU_WEAK_ALIAS(monitor_fdset_dup_fd_find, default_fdset_dup_fd_find);
#define monitor_fdset_dup_fd_find \
QEMU_WEAK_REF(monitor_fdset_dup_fd_remove, default_fdset_dup_fd_find)
int socket_set_cork(int fd, int v) int socket_set_cork(int fd, int v)
{ {
#if defined(SOL_TCP) && defined(TCP_CORK) #if defined(SOL_TCP) && defined(TCP_CORK)

View File

@ -32,13 +32,6 @@
#include "trace.h" #include "trace.h"
#include "qemu_socket.h" #include "qemu_socket.h"
static void default_qemu_fd_register(int fd)
{
}
QEMU_WEAK_ALIAS(qemu_fd_register, default_qemu_fd_register);
#define qemu_fd_register \
QEMU_WEAK_REF(qemu_fd_register, default_qemu_fd_register)
void *qemu_oom_check(void *ptr) void *qemu_oom_check(void *ptr)
{ {
if (ptr == NULL) { if (ptr == NULL) {

215
pflib.c
View File

@ -1,215 +0,0 @@
/*
* PixelFormat conversion library.
*
* Author: Gerd Hoffmann <kraxel@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "qemu-common.h"
#include "console.h"
#include "pflib.h"
typedef struct QemuPixel QemuPixel;
typedef void (*pf_convert)(QemuPfConv *conv,
void *dst, void *src, uint32_t cnt);
typedef void (*pf_convert_from)(PixelFormat *pf,
QemuPixel *dst, void *src, uint32_t cnt);
typedef void (*pf_convert_to)(PixelFormat *pf,
void *dst, QemuPixel *src, uint32_t cnt);
struct QemuPfConv {
pf_convert convert;
PixelFormat src;
PixelFormat dst;
/* for copy_generic() */
pf_convert_from conv_from;
pf_convert_to conv_to;
QemuPixel *conv_buf;
uint32_t conv_cnt;
};
struct QemuPixel {
uint8_t red;
uint8_t green;
uint8_t blue;
uint8_t alpha;
};
/* ----------------------------------------------------------------------- */
/* PixelFormat -> QemuPixel conversions */
static void conv_16_to_pixel(PixelFormat *pf,
QemuPixel *dst, void *src, uint32_t cnt)
{
uint16_t *src16 = src;
while (cnt > 0) {
dst->red = ((*src16 & pf->rmask) >> pf->rshift) << (8 - pf->rbits);
dst->green = ((*src16 & pf->gmask) >> pf->gshift) << (8 - pf->gbits);
dst->blue = ((*src16 & pf->bmask) >> pf->bshift) << (8 - pf->bbits);
dst->alpha = ((*src16 & pf->amask) >> pf->ashift) << (8 - pf->abits);
dst++, src16++, cnt--;
}
}
/* assumes pf->{r,g,b,a}bits == 8 */
static void conv_32_to_pixel_fast(PixelFormat *pf,
QemuPixel *dst, void *src, uint32_t cnt)
{
uint32_t *src32 = src;
while (cnt > 0) {
dst->red = (*src32 & pf->rmask) >> pf->rshift;
dst->green = (*src32 & pf->gmask) >> pf->gshift;
dst->blue = (*src32 & pf->bmask) >> pf->bshift;
dst->alpha = (*src32 & pf->amask) >> pf->ashift;
dst++, src32++, cnt--;
}
}
static void conv_32_to_pixel_generic(PixelFormat *pf,
QemuPixel *dst, void *src, uint32_t cnt)
{
uint32_t *src32 = src;
while (cnt > 0) {
if (pf->rbits < 8) {
dst->red = ((*src32 & pf->rmask) >> pf->rshift) << (8 - pf->rbits);
} else {
dst->red = ((*src32 & pf->rmask) >> pf->rshift) >> (pf->rbits - 8);
}
if (pf->gbits < 8) {
dst->green = ((*src32 & pf->gmask) >> pf->gshift) << (8 - pf->gbits);
} else {
dst->green = ((*src32 & pf->gmask) >> pf->gshift) >> (pf->gbits - 8);
}
if (pf->bbits < 8) {
dst->blue = ((*src32 & pf->bmask) >> pf->bshift) << (8 - pf->bbits);
} else {
dst->blue = ((*src32 & pf->bmask) >> pf->bshift) >> (pf->bbits - 8);
}
if (pf->abits < 8) {
dst->alpha = ((*src32 & pf->amask) >> pf->ashift) << (8 - pf->abits);
} else {
dst->alpha = ((*src32 & pf->amask) >> pf->ashift) >> (pf->abits - 8);
}
dst++, src32++, cnt--;
}
}
/* ----------------------------------------------------------------------- */
/* QemuPixel -> PixelFormat conversions */
static void conv_pixel_to_16(PixelFormat *pf,
void *dst, QemuPixel *src, uint32_t cnt)
{
uint16_t *dst16 = dst;
while (cnt > 0) {
*dst16 = ((uint16_t)src->red >> (8 - pf->rbits)) << pf->rshift;
*dst16 |= ((uint16_t)src->green >> (8 - pf->gbits)) << pf->gshift;
*dst16 |= ((uint16_t)src->blue >> (8 - pf->bbits)) << pf->bshift;
*dst16 |= ((uint16_t)src->alpha >> (8 - pf->abits)) << pf->ashift;
dst16++, src++, cnt--;
}
}
static void conv_pixel_to_32(PixelFormat *pf,
void *dst, QemuPixel *src, uint32_t cnt)
{
uint32_t *dst32 = dst;
while (cnt > 0) {
*dst32 = ((uint32_t)src->red >> (8 - pf->rbits)) << pf->rshift;
*dst32 |= ((uint32_t)src->green >> (8 - pf->gbits)) << pf->gshift;
*dst32 |= ((uint32_t)src->blue >> (8 - pf->bbits)) << pf->bshift;
*dst32 |= ((uint32_t)src->alpha >> (8 - pf->abits)) << pf->ashift;
dst32++, src++, cnt--;
}
}
/* ----------------------------------------------------------------------- */
/* PixelFormat -> PixelFormat conversions */
static void convert_copy(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
{
uint32_t bytes = cnt * conv->src.bytes_per_pixel;
memcpy(dst, src, bytes);
}
static void convert_generic(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
{
if (conv->conv_cnt < cnt) {
conv->conv_cnt = cnt;
conv->conv_buf = g_realloc(conv->conv_buf, sizeof(QemuPixel) * conv->conv_cnt);
}
conv->conv_from(&conv->src, conv->conv_buf, src, cnt);
conv->conv_to(&conv->dst, dst, conv->conv_buf, cnt);
}
/* ----------------------------------------------------------------------- */
/* public interface */
QemuPfConv *qemu_pf_conv_get(PixelFormat *dst, PixelFormat *src)
{
QemuPfConv *conv = g_malloc0(sizeof(QemuPfConv));
conv->src = *src;
conv->dst = *dst;
if (memcmp(&conv->src, &conv->dst, sizeof(PixelFormat)) == 0) {
/* formats identical, can simply copy */
conv->convert = convert_copy;
} else {
/* generic two-step conversion: src -> QemuPixel -> dst */
switch (conv->src.bytes_per_pixel) {
case 2:
conv->conv_from = conv_16_to_pixel;
break;
case 4:
if (conv->src.rbits == 8 && conv->src.gbits == 8 && conv->src.bbits == 8) {
conv->conv_from = conv_32_to_pixel_fast;
} else {
conv->conv_from = conv_32_to_pixel_generic;
}
break;
default:
goto err;
}
switch (conv->dst.bytes_per_pixel) {
case 2:
conv->conv_to = conv_pixel_to_16;
break;
case 4:
conv->conv_to = conv_pixel_to_32;
break;
default:
goto err;
}
conv->convert = convert_generic;
}
return conv;
err:
g_free(conv);
return NULL;
}
void qemu_pf_conv_run(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
{
conv->convert(conv, dst, src, cnt);
}
void qemu_pf_conv_put(QemuPfConv *conv)
{
if (conv) {
g_free(conv->conv_buf);
g_free(conv);
}
}

20
pflib.h
View File

@ -1,20 +0,0 @@
#ifndef __QEMU_PFLIB_H
#define __QEMU_PFLIB_H
/*
* PixelFormat conversion library.
*
* Author: Gerd Hoffmann <kraxel@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
*/
typedef struct QemuPfConv QemuPfConv;
QemuPfConv *qemu_pf_conv_get(PixelFormat *dst, PixelFormat *src);
void qemu_pf_conv_run(QemuPfConv *conv, void *dst, void *src, uint32_t cnt);
void qemu_pf_conv_put(QemuPfConv *conv);
#endif

View File

@ -686,6 +686,15 @@ static QemuOptsList qemu_add_fd_opts = {
}, },
}; };
static QemuOptsList qemu_object_opts = {
.name = "object",
.implied_opt_name = "qom-type",
.head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head),
.desc = {
{ }
},
};
static QemuOptsList *vm_config_groups[32] = { static QemuOptsList *vm_config_groups[32] = {
&qemu_drive_opts, &qemu_drive_opts,
&qemu_chardev_opts, &qemu_chardev_opts,
@ -703,6 +712,7 @@ static QemuOptsList *vm_config_groups[32] = {
&qemu_iscsi_opts, &qemu_iscsi_opts,
&qemu_sandbox_opts, &qemu_sandbox_opts,
&qemu_add_fd_opts, &qemu_add_fd_opts,
&qemu_object_opts,
NULL, NULL,
}; };

View File

@ -610,14 +610,14 @@ QEMU can access directly to block device exported using the Network Block Device
protocol. protocol.
@example @example
qemu-system-i386 linux.img -hdb nbd:my_nbd_server.mydomain.org:1024 qemu-system-i386 linux.img -hdb nbd://my_nbd_server.mydomain.org:1024/
@end example @end example
If the NBD server is located on the same host, you can use an unix socket instead If the NBD server is located on the same host, you can use an unix socket instead
of an inet socket: of an inet socket:
@example @example
qemu-system-i386 linux.img -hdb nbd:unix:/tmp/my_socket qemu-system-i386 linux.img -hdb nbd+unix://?socket=/tmp/my_socket
@end example @end example
In this case, the block device must be exported using qemu-nbd: In this case, the block device must be exported using qemu-nbd:
@ -631,17 +631,26 @@ The use of qemu-nbd allows to share a disk between several guests:
qemu-nbd --socket=/tmp/my_socket --share=2 my_disk.qcow2 qemu-nbd --socket=/tmp/my_socket --share=2 my_disk.qcow2
@end example @end example
@noindent
and then you can use it with two guests: and then you can use it with two guests:
@example @example
qemu-system-i386 linux1.img -hdb nbd:unix:/tmp/my_socket qemu-system-i386 linux1.img -hdb nbd+unix://?socket=/tmp/my_socket
qemu-system-i386 linux2.img -hdb nbd:unix:/tmp/my_socket qemu-system-i386 linux2.img -hdb nbd+unix://?socket=/tmp/my_socket
@end example @end example
If the nbd-server uses named exports (since NBD 2.9.18), you must use the If the nbd-server uses named exports (supported since NBD 2.9.18, or with QEMU's
"exportname" option: own embedded NBD server), you must specify an export name in the URI:
@example @example
qemu-system-i386 -cdrom nbd:localhost:exportname=debian-500-ppc-netinst qemu-system-i386 -cdrom nbd://localhost/debian-500-ppc-netinst
qemu-system-i386 -cdrom nbd:localhost:exportname=openSUSE-11.1-ppc-netinst qemu-system-i386 -cdrom nbd://localhost/openSUSE-11.1-ppc-netinst
@end example
The URI syntax for NBD is supported since QEMU 1.3. An alternative syntax is
also available. Here are some example of the older syntax:
@example
qemu-system-i386 linux.img -hdb nbd:my_nbd_server.mydomain.org:1024
qemu-system-i386 linux2.img -hdb nbd:unix:/tmp/my_socket
qemu-system-i386 -cdrom nbd:localhost:10809:exportname=debian-500-ppc-netinst
@end example @end example
@node disk_images_sheepdog @node disk_images_sheepdog

View File

@ -539,6 +539,7 @@ int main(int argc, char **argv)
snprintf(sockpath, 128, SOCKET_PATH, basename(device)); snprintf(sockpath, 128, SOCKET_PATH, basename(device));
} }
qemu_init_main_loop();
bdrv_init(); bdrv_init();
atexit(bdrv_close_all); atexit(bdrv_close_all);
@ -584,7 +585,6 @@ int main(int argc, char **argv)
memset(&client_thread, 0, sizeof(client_thread)); memset(&client_thread, 0, sizeof(client_thread));
} }
qemu_init_main_loop();
qemu_set_fd_handler2(fd, nbd_can_accept, nbd_accept, NULL, qemu_set_fd_handler2(fd, nbd_can_accept, nbd_accept, NULL,
(void *)(uintptr_t)fd); (void *)(uintptr_t)fd);

View File

@ -2904,6 +2904,14 @@ DEF("no-kvm-irqchip", HAS_ARG, QEMU_OPTION_no_kvm_irqchip, "", QEMU_ARCH_I386)
HXCOMM Deprecated (ignored) HXCOMM Deprecated (ignored)
DEF("tdf", 0, QEMU_OPTION_tdf,"", QEMU_ARCH_ALL) DEF("tdf", 0, QEMU_OPTION_tdf,"", QEMU_ARCH_ALL)
DEF("object", HAS_ARG, QEMU_OPTION_object,
"-object TYPENAME[,PROP1=VALUE1,...]\n"
" create an new object of type TYPENAME setting properties\n"
" in the order they are specified. Note that the 'id'\n"
" property must be set. These objects are placed in the\n"
" '/objects' path.\n",
QEMU_ARCH_ALL)
HXCOMM This is the last statement. Insert new options before this line! HXCOMM This is the last statement. Insert new options before this line!
STEXI STEXI
@end table @end table

View File

@ -1,3 +1,8 @@
/*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu-pixman.h" #include "qemu-pixman.h"
int qemu_pixman_get_type(int rshift, int gshift, int bshift) int qemu_pixman_get_type(int rshift, int gshift, int bshift)
@ -51,6 +56,19 @@ void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb,
0, y, 0, 0, 0, 0, width, 1); 0, y, 0, 0, 0, 0, width, 1);
} }
pixman_image_t *qemu_pixman_mirror_create(pixman_format_code_t format,
pixman_image_t *image)
{
pixman_image_t *mirror;
mirror = pixman_image_create_bits(format,
pixman_image_get_width(image),
pixman_image_get_height(image),
NULL,
pixman_image_get_stride(image));
return mirror;
}
void qemu_pixman_image_unref(pixman_image_t *image) void qemu_pixman_image_unref(pixman_image_t *image)
{ {
if (image == NULL) { if (image == NULL) {

View File

@ -1,3 +1,8 @@
/*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef QEMU_PIXMAN_H #ifndef QEMU_PIXMAN_H
#define QEMU_PIXMAN_H #define QEMU_PIXMAN_H
@ -27,6 +32,8 @@ pixman_image_t *qemu_pixman_linebuf_create(pixman_format_code_t format,
int width); int width);
void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb, void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb,
int width, int y); int width, int y);
pixman_image_t *qemu_pixman_mirror_create(pixman_format_code_t format,
pixman_image_t *image);
void qemu_pixman_image_unref(pixman_image_t *image); void qemu_pixman_image_unref(pixman_image_t *image);
#endif /* QEMU_PIXMAN_H */ #endif /* QEMU_PIXMAN_H */

View File

@ -61,28 +61,6 @@ static QemuOptsList dummy_opts = {
}, },
}; };
static int default_monitor_get_fd(Monitor *mon, const char *name, Error **errp)
{
error_setg(errp, "only QEMU supports file descriptor passing");
return -1;
}
QEMU_WEAK_ALIAS(monitor_get_fd, default_monitor_get_fd);
#define monitor_get_fd \
QEMU_WEAK_REF(monitor_get_fd, default_monitor_get_fd)
static int default_qemu_set_fd_handler2(int fd,
IOCanReadHandler *fd_read_poll,
IOHandler *fd_read,
IOHandler *fd_write,
void *opaque)
{
abort();
}
QEMU_WEAK_ALIAS(qemu_set_fd_handler2, default_qemu_set_fd_handler2);
#define qemu_set_fd_handler2 \
QEMU_WEAK_REF(qemu_set_fd_handler2, default_qemu_set_fd_handler2)
static int inet_getport(struct addrinfo *e) static int inet_getport(struct addrinfo *e)
{ {
struct sockaddr_in *i4; struct sockaddr_in *i4;

9
qmp.c
View File

@ -471,15 +471,6 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
return prop_list; return prop_list;
} }
static CpuDefinitionInfoList *default_arch_query_cpu_definitions(Error **errp)
{
error_set(errp, QERR_NOT_SUPPORTED);
return NULL;
}
QEMU_WEAK_ALIAS(arch_query_cpu_definitions, default_arch_query_cpu_definitions);
#define arch_query_cpu_definitions \
QEMU_WEAK_REF(arch_query_cpu_definitions, default_arch_query_cpu_definitions)
CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
{ {
return arch_query_cpu_definitions(errp); return arch_query_cpu_definitions(errp);

View File

@ -1183,6 +1183,62 @@ void object_property_add_str(Object *obj, const char *name,
prop, errp); prop, errp);
} }
typedef struct BoolProperty
{
bool (*get)(Object *, Error **);
void (*set)(Object *, bool, Error **);
} BoolProperty;
static void property_get_bool(Object *obj, Visitor *v, void *opaque,
const char *name, Error **errp)
{
BoolProperty *prop = opaque;
bool value;
value = prop->get(obj, errp);
visit_type_bool(v, &value, name, errp);
}
static void property_set_bool(Object *obj, Visitor *v, void *opaque,
const char *name, Error **errp)
{
BoolProperty *prop = opaque;
bool value;
Error *local_err = NULL;
visit_type_bool(v, &value, name, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
prop->set(obj, value, errp);
}
static void property_release_bool(Object *obj, const char *name,
void *opaque)
{
BoolProperty *prop = opaque;
g_free(prop);
}
void object_property_add_bool(Object *obj, const char *name,
bool (*get)(Object *, Error **),
void (*set)(Object *, bool, Error **),
Error **errp)
{
BoolProperty *prop = g_malloc0(sizeof(*prop));
prop->get = get;
prop->set = set;
object_property_add(obj, name, "bool",
get ? property_get_bool : NULL,
set ? property_set_bool : NULL,
property_release_bool,
prop, errp);
}
static char *qdev_get_type(Object *obj, Error **errp) static char *qdev_get_type(Object *obj, Error **errp)
{ {
return g_strdup(object_get_typename(obj)); return g_strdup(object_get_typename(obj));

View File

@ -31,7 +31,7 @@ endif
%.o: %.m %.o: %.m
$(call quiet-command,$(OBJCC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," OBJC $(TARGET_DIR)$@") $(call quiet-command,$(OBJCC) $(QEMU_INCLUDES) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," OBJC $(TARGET_DIR)$@")
LINK = $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(sort $(1)) $(LIBS)," LINK $(TARGET_DIR)$@") LINK = $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(sort $(filter %.o, $1)) $(filter-out %.o, $1) $(LIBS)," LINK $(TARGET_DIR)$@")
%$(EXESUF): %.o %$(EXESUF): %.o
$(call LINK,$^) $(call LINK,$^)

8
stubs/Makefile.objs Normal file
View File

@ -0,0 +1,8 @@
stub-obj-y += arch-query-cpu-def.o
stub-obj-y += fdset-add-fd.o
stub-obj-y += fdset-find-fd.o
stub-obj-y += fdset-get-fd.o
stub-obj-y += fdset-remove-fd.o
stub-obj-y += get-fd.o
stub-obj-y += set-fd-handler.o
stub-obj-$(CONFIG_WIN32) += fd-register.o

View File

@ -0,0 +1,9 @@
#include "qemu-common.h"
#include "arch_init.h"
#include "qerror.h"
CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
{
error_set(errp, QERR_NOT_SUPPORTED);
return NULL;
}

6
stubs/fd-register.c Normal file
View File

@ -0,0 +1,6 @@
#include "qemu-common.h"
#include "main-loop.h"
void qemu_fd_register(int fd)
{
}

7
stubs/fdset-add-fd.c Normal file
View File

@ -0,0 +1,7 @@
#include "qemu-common.h"
#include "monitor.h"
int monitor_fdset_dup_fd_add(int64_t fdset_id, int dup_fd)
{
return -1;
}

7
stubs/fdset-find-fd.c Normal file
View File

@ -0,0 +1,7 @@
#include "qemu-common.h"
#include "monitor.h"
int monitor_fdset_dup_fd_find(int dup_fd)
{
return -1;
}

7
stubs/fdset-get-fd.c Normal file
View File

@ -0,0 +1,7 @@
#include "qemu-common.h"
#include "monitor.h"
int monitor_fdset_get_fd(int64_t fdset_id, int flags)
{
return -1;
}

7
stubs/fdset-remove-fd.c Normal file
View File

@ -0,0 +1,7 @@
#include "qemu-common.h"
#include "monitor.h"
int monitor_fdset_dup_fd_remove(int dupfd)
{
return -1;
}

8
stubs/get-fd.c Normal file
View File

@ -0,0 +1,8 @@
#include "qemu-common.h"
#include "monitor.h"
int monitor_get_fd(Monitor *mon, const char *name, Error **errp)
{
error_setg(errp, "only QEMU supports file descriptor passing");
return -1;
}

11
stubs/set-fd-handler.c Normal file
View File

@ -0,0 +1,11 @@
#include "qemu-common.h"
#include "main-loop.h"
int qemu_set_fd_handler2(int fd,
IOCanReadHandler *fd_read_poll,
IOHandler *fd_read,
IOHandler *fd_write,
void *opaque)
{
abort();
}

Some files were not shown because too many files have changed in this diff Show More