mirror of https://github.com/xemu-project/xemu.git
Merge remote-tracking branch 'origin/master' into xbox
This commit is contained in:
commit
17d4dd207a
19
Makefile
19
Makefile
|
@ -122,7 +122,7 @@ subdir-pixman: pixman/Makefile
|
|||
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C pixman V="$(V)" all,)
|
||||
|
||||
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:
|
||||
(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)$@")
|
||||
|
||||
version-obj-$(CONFIG_WIN32) += version.o
|
||||
|
||||
######################################################################
|
||||
# Build library with stubs
|
||||
|
||||
libqemustub.a: $(stub-obj-y)
|
||||
|
||||
######################################################################
|
||||
# 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
|
||||
tools-obj-$(CONFIG_POSIX) += compatfd.o
|
||||
|
||||
qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y)
|
||||
qemu-nbd$(EXESUF): qemu-nbd.o $(tools-obj-y) $(block-obj-y)
|
||||
qemu-io$(EXESUF): qemu-io.o cmd.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) libqemustub.a
|
||||
qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) $(block-obj-y) libqemustub.a
|
||||
|
||||
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 $@")
|
||||
|
||||
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)
|
||||
$(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
|
||||
|
||||
|
@ -278,6 +284,7 @@ distclean: clean
|
|||
for d in $(TARGET_DIRS) $(QEMULIBS); do \
|
||||
rm -rf $$d || exit 1 ; \
|
||||
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 \
|
||||
ar de en-us fi fr-be hr it lv nl pl ru th \
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
#######################################################################
|
||||
# Stub library, linked in tools
|
||||
stub-obj-y = stubs/
|
||||
|
||||
#######################################################################
|
||||
# Target-independent parts used in system and user emulation
|
||||
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 += qemu-char.o #aio.o
|
||||
common-obj-y += block-migration.o iohandler.o
|
||||
common-obj-y += pflib.o
|
||||
common-obj-y += bitmap.o bitops.o
|
||||
common-obj-y += page_cache.o
|
||||
|
||||
|
@ -101,6 +104,8 @@ common-obj-y += vl.o
|
|||
|
||||
common-obj-$(CONFIG_SLIRP) += slirp/
|
||||
|
||||
common-obj-y += backends/
|
||||
|
||||
######################################################################
|
||||
# libseccomp
|
||||
ifeq ($(CONFIG_SECCOMP),y)
|
||||
|
@ -238,6 +243,7 @@ vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
|
|||
QEMU_CFLAGS+=$(GLIB_CFLAGS)
|
||||
|
||||
nested-vars += \
|
||||
stub-obj-y \
|
||||
qga-obj-y \
|
||||
qom-obj-y \
|
||||
qapi-obj-y \
|
||||
|
|
|
@ -162,12 +162,12 @@ endif #CONFIG_LINUX_USER
|
|||
|
||||
ifdef QEMU_PROGW
|
||||
# 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,$^)
|
||||
$(QEMU_PROG): $(QEMU_PROGW)
|
||||
$(call quiet-command,$(OBJCOPY) --subsystem console $(QEMU_PROGW) $(QEMU_PROG)," GEN $(TARGET_DIR)$(QEMU_PROG)")
|
||||
else
|
||||
$(QEMU_PROG): $(all-obj-y)
|
||||
$(QEMU_PROG): $(all-obj-y) ../libqemustub.a
|
||||
$(call LINK,$^)
|
||||
endif
|
||||
|
||||
|
|
6
async.c
6
async.c
|
@ -122,11 +122,9 @@ aio_ctx_prepare(GSource *source, gint *timeout)
|
|||
{
|
||||
AioContext *ctx = (AioContext *) source;
|
||||
QEMUBH *bh;
|
||||
bool scheduled = false;
|
||||
|
||||
for (bh = ctx->first_bh; bh; bh = bh->next) {
|
||||
if (!bh->deleted && bh->scheduled) {
|
||||
scheduled = true;
|
||||
if (bh->idle) {
|
||||
/* idle bottom halves will be polled at least
|
||||
* every 10ms */
|
||||
|
@ -135,12 +133,12 @@ aio_ctx_prepare(GSource *source, gint *timeout)
|
|||
/* non-idle bottom halves will be executed
|
||||
* immediately */
|
||||
*timeout = 0;
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return scheduled;
|
||||
return false;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
common-obj-y += rng.o rng-random.o rng-egd.o
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
115
block/nbd.c
115
block/nbd.c
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "qemu-common.h"
|
||||
#include "nbd.h"
|
||||
#include "uri.h"
|
||||
#include "block_int.h"
|
||||
#include "module.h"
|
||||
#include "qemu_socket.h"
|
||||
|
@ -55,7 +56,6 @@ typedef struct BDRVNBDState {
|
|||
uint32_t nbdflags;
|
||||
off_t size;
|
||||
size_t blocksize;
|
||||
char *export_name; /* An NBD server may export several devices */
|
||||
|
||||
CoMutex send_mutex;
|
||||
CoMutex free_sema;
|
||||
|
@ -65,13 +65,75 @@ typedef struct BDRVNBDState {
|
|||
Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
|
||||
struct nbd_reply reply;
|
||||
|
||||
/* If it begins with '/', this is a UNIX domain socket. Otherwise,
|
||||
* it's a string of the form <hostname|ip4|\[ip6\]>:port
|
||||
*/
|
||||
int is_unix;
|
||||
char *host_spec;
|
||||
char *export_name; /* An NBD server may export several devices */
|
||||
} 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 *export_name;
|
||||
|
@ -79,6 +141,10 @@ static int nbd_config(BDRVNBDState *s, const char *filename, int flags)
|
|||
const char *unixpath;
|
||||
int err = -EINVAL;
|
||||
|
||||
if (strstr(filename, "://")) {
|
||||
return nbd_parse_uri(s, filename);
|
||||
}
|
||||
|
||||
file = g_strdup(filename);
|
||||
|
||||
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? */
|
||||
if (strstart(host_spec, "unix:", &unixpath)) {
|
||||
if (unixpath[0] != '/') { /* We demand an absolute path*/
|
||||
goto out;
|
||||
}
|
||||
s->is_unix = true;
|
||||
s->host_spec = g_strdup(unixpath);
|
||||
} else {
|
||||
s->is_unix = false;
|
||||
s->host_spec = g_strdup(host_spec);
|
||||
}
|
||||
|
||||
|
@ -262,7 +327,7 @@ static int nbd_establish_connection(BlockDriverState *bs)
|
|||
off_t size;
|
||||
size_t blocksize;
|
||||
|
||||
if (s->host_spec[0] == '/') {
|
||||
if (s->is_unix) {
|
||||
sock = unix_socket_outgoing(s->host_spec);
|
||||
} else {
|
||||
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);
|
||||
|
||||
/* Pop the config into our state object. Exit if invalid. */
|
||||
result = nbd_config(s, filename, flags);
|
||||
result = nbd_config(s, filename);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
|
@ -498,6 +563,33 @@ static int64_t nbd_getlength(BlockDriverState *bs)
|
|||
|
||||
static BlockDriver bdrv_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),
|
||||
.bdrv_file_open = nbd_open,
|
||||
.bdrv_co_readv = nbd_co_readv,
|
||||
|
@ -506,12 +598,13 @@ static BlockDriver bdrv_nbd = {
|
|||
.bdrv_co_flush_to_os = nbd_co_flush,
|
||||
.bdrv_co_discard = nbd_co_discard,
|
||||
.bdrv_getlength = nbd_getlength,
|
||||
.protocol_name = "nbd",
|
||||
};
|
||||
|
||||
static void bdrv_nbd_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_nbd);
|
||||
bdrv_register(&bdrv_nbd_tcp);
|
||||
bdrv_register(&bdrv_nbd_unix);
|
||||
}
|
||||
|
||||
block_init(bdrv_nbd_init);
|
||||
|
|
|
@ -82,6 +82,11 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
|||
NBDExport *exp;
|
||||
NBDCloseNotifier *n;
|
||||
|
||||
if (server_fd == -1) {
|
||||
error_setg(errp, "NBD server not running");
|
||||
return;
|
||||
}
|
||||
|
||||
if (nbd_export_find(device)) {
|
||||
error_setg(errp, "NBD server already exporting device '%s'", device);
|
||||
return;
|
||||
|
@ -93,6 +98,13 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
|||
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,
|
||||
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));
|
||||
}
|
||||
|
||||
qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL);
|
||||
close(server_fd);
|
||||
server_fd = -1;
|
||||
if (server_fd != -1) {
|
||||
qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL);
|
||||
close(server_fd);
|
||||
server_fd = -1;
|
||||
}
|
||||
}
|
||||
|
|
11
compiler.h
11
compiler.h
|
@ -50,20 +50,9 @@
|
|||
# define __printf__ __gnu_printf__
|
||||
# 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
|
||||
#define GCC_ATTR /**/
|
||||
#define GCC_FMT_ATTR(n, m)
|
||||
#define QEMU_WEAK_ALIAS(newname, oldname) \
|
||||
_Pragma("weak " #newname "=" #oldname)
|
||||
#endif
|
||||
|
||||
#endif /* COMPILER_H */
|
||||
|
|
|
@ -1383,7 +1383,7 @@ fi
|
|||
# libseccomp check
|
||||
|
||||
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`
|
||||
seccomp="yes"
|
||||
else
|
||||
|
@ -2121,11 +2121,10 @@ else
|
|||
echo " git submodule update --init pixman"
|
||||
exit 1
|
||||
fi
|
||||
pixman_cflags="-I${source_path}/pixman/pixman"
|
||||
pixman_libs="-Lpixman/pixman/.libs -lpixman-1"
|
||||
mkdir -p pixman/pixman
|
||||
pixman_cflags="-I\$(SRC_PATH)/pixman/pixman -I\$(BUILD_DIR)/pixman/pixman"
|
||||
pixman_libs="-L\$(BUILD_DIR)/pixman/pixman/.libs -lpixman-1"
|
||||
fi
|
||||
QEMU_CFLAGS="$QEMU_CFLAGS $pixman_cflags"
|
||||
libs_softmmu="$libs_softmmu $pixman_libs"
|
||||
|
||||
##########################################
|
||||
# libcap probe
|
||||
|
@ -3150,6 +3149,10 @@ if test "$cpu" = "ppc64" -a "$targetos" != "Darwin" ; then
|
|||
roms="$roms spapr-rtas"
|
||||
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 "BIOS directory `eval echo $qemu_datadir`"
|
||||
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 "QEMU_CFLAGS += -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-non-pointer-null" >> $config_host_mak
|
||||
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 "ARLIBS_BEGIN=$arlibs_begin" >> $config_host_mak
|
||||
echo "ARLIBS_END=$arlibs_end" >> $config_host_mak
|
||||
|
@ -3895,7 +3903,10 @@ upper() {
|
|||
|
||||
case "$cpu" in
|
||||
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
|
||||
|
||||
|
@ -3960,9 +3971,6 @@ if test "$target_softmmu" = "yes" ; then
|
|||
if test "$smartcard_nss" = "yes" ; then
|
||||
echo "subdir-$target: subdir-libcacard" >> $config_host_mak
|
||||
fi
|
||||
if test "$pixman" = "internal" ; then
|
||||
echo "subdir-$target: subdir-pixman" >> $config_host_mak
|
||||
fi
|
||||
case "$target_arch2" in
|
||||
i386|x86_64)
|
||||
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
|
||||
|
||||
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
|
||||
DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32"
|
||||
DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas"
|
||||
DIRS="$DIRS roms/seabios roms/vgabios"
|
||||
DIRS="$DIRS qapi-generated"
|
||||
DIRS="$DIRS libcacard libcacard/libcacard libcacard/trace"
|
||||
DIRS="$DIRS pixman"
|
||||
FILES="Makefile tests/tcg/Makefile qdict-test-data.txt"
|
||||
FILES="$FILES tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit"
|
||||
FILES="$FILES tests/tcg/lm32/Makefile libcacard/Makefile"
|
||||
|
|
|
@ -377,6 +377,11 @@ static inline pixman_format_code_t ds_get_format(DisplayState *ds)
|
|||
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)
|
||||
{
|
||||
return ds->surface->pf.depth;
|
||||
|
|
5
dma.h
5
dma.h
|
@ -68,6 +68,11 @@ struct DMAContext {
|
|||
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)
|
||||
{
|
||||
/*
|
||||
|
|
|
@ -36,7 +36,8 @@ IO ports used
|
|||
|
||||
03c0 - 03df : standard vga ports
|
||||
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
|
||||
|
|
|
@ -290,10 +290,11 @@ extern int tb_invalidated_flag;
|
|||
/* The return address may point to the start of the next instruction.
|
||||
Subtracting one gets us the call instruction itself. */
|
||||
#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. */
|
||||
# if defined(CONFIG_SOFTMMU) || defined(TARGET_ALPHA) || defined(TARGET_SH4) \
|
||||
|| defined(TARGET_SPARC)
|
||||
# if defined(CONFIG_SOFTMMU) || \
|
||||
defined(TARGET_ALPHA) || defined(TARGET_MIPS) || \
|
||||
defined(TARGET_SH4) || defined(TARGET_SPARC)
|
||||
extern uintptr_t tci_tb_ptr;
|
||||
# define GETPC() tci_tb_ptr
|
||||
# endif
|
||||
|
|
5
exec.c
5
exec.c
|
@ -34,6 +34,7 @@
|
|||
#include "hw/xen.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "memory.h"
|
||||
#include "dma.h"
|
||||
#include "exec-memory.h"
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
#include <qemu.h>
|
||||
|
@ -103,6 +104,7 @@ static MemoryRegion *system_io;
|
|||
|
||||
AddressSpace address_space_io;
|
||||
AddressSpace address_space_memory;
|
||||
DMAContext dma_context_memory;
|
||||
|
||||
MemoryRegion io_mem_ram, io_mem_rom, io_mem_unassigned, io_mem_notdirty;
|
||||
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(&io_memory_listener, &address_space_io);
|
||||
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)
|
||||
|
|
|
@ -16,7 +16,7 @@ static inline void gen_icount_start(void)
|
|||
count = tcg_temp_local_new_i32();
|
||||
tcg_gen_ld_i32(count, cpu_env, offsetof(CPUArchState, icount_decr.u32));
|
||||
/* 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_brcondi_i32(TCG_COND_LT, count, 0, icount_label);
|
||||
|
|
|
@ -1310,6 +1310,51 @@ Remove all matches from the access control list, and set the default
|
|||
policy back to @code{deny}.
|
||||
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)
|
||||
|
||||
{
|
||||
|
|
76
hmp.c
76
hmp.c
|
@ -18,6 +18,7 @@
|
|||
#include "qemu-option.h"
|
||||
#include "qemu-timer.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "qemu_socket.h"
|
||||
#include "monitor.h"
|
||||
#include "console.h"
|
||||
|
||||
|
@ -1259,3 +1260,78 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict)
|
|||
qmp_screendump(filename, &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
3
hmp.h
|
@ -77,5 +77,8 @@ void hmp_getfd(Monitor *mon, const QDict *qdict);
|
|||
void hmp_closefd(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_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
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
common-obj-y = usb/ ide/
|
||||
common-obj-y += loader.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-y += fw_cfg.o
|
||||
common-obj-$(CONFIG_PCI) += pci.o pci_bridge.o pci_bridge_dev.o
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include "iov.h"
|
||||
#include "scsi.h"
|
||||
#include "scsi-defs.h"
|
||||
#include "block_int.h"
|
||||
#include "trace.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 */
|
||||
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.lun_id = sdev->lun;
|
||||
info.ld_list[num_ld_disks].state = MFI_LD_STATE_OPTIMAL;
|
||||
info.ld_list[num_ld_disks].size = cpu_to_le64(ld_size);
|
||||
num_ld_disks++;
|
||||
|
|
2
hw/mfi.h
2
hw/mfi.h
|
@ -1085,7 +1085,7 @@ struct mfi_pd_list {
|
|||
union mfi_ld_ref {
|
||||
struct {
|
||||
uint8_t target_id;
|
||||
uint8_t reserved;
|
||||
uint8_t lun_id;
|
||||
uint16_t seq;
|
||||
} v;
|
||||
uint32_t ref;
|
||||
|
|
|
@ -861,7 +861,8 @@ void mips_malta_init(QEMUMachineInitArgs *args)
|
|||
be = 0;
|
||||
#endif
|
||||
/* 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. */
|
||||
dinfo = drive_get(IF_PFLASH, 0, fl_idx);
|
||||
|
|
1
hw/pci.h
1
hw/pci.h
|
@ -76,6 +76,7 @@
|
|||
#define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002
|
||||
#define PCI_DEVICE_ID_VIRTIO_CONSOLE 0x1003
|
||||
#define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004
|
||||
#define PCI_DEVICE_ID_VIRTIO_RNG 0x1005
|
||||
|
||||
#define FMT_PCIBUS PRIx64
|
||||
|
||||
|
|
36
hw/qxl.c
36
hw/qxl.c
|
@ -293,6 +293,10 @@ void qxl_spice_reset_cursor(PCIQXLDevice *qxl)
|
|||
qemu_mutex_lock(&qxl->track_lock);
|
||||
qxl->guest_cursor = 0;
|
||||
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);
|
||||
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);
|
||||
if (cmd->type == QXL_SURFACE_CMD_CREATE) {
|
||||
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);
|
||||
qemu_spice_create_host_primary(&d->ssd);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
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.group_id = MEMSLOT_GROUP_GUEST;
|
||||
if (loadvm) {
|
||||
|
@ -1689,7 +1705,13 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
|
|||
uint32_t le_events = cpu_to_le32(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);
|
||||
if ((old_pending & le_events) == le_events) {
|
||||
return;
|
||||
|
@ -2027,6 +2049,7 @@ static int qxl_init_primary(PCIDevice *dev)
|
|||
PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
|
||||
VGACommonState *vga = &qxl->vga;
|
||||
PortioList *qxl_vga_port_list = g_new(PortioList, 1);
|
||||
int rc;
|
||||
|
||||
qxl->id = 0;
|
||||
qxl_init_ramsize(qxl);
|
||||
|
@ -2041,9 +2064,14 @@ static int qxl_init_primary(PCIDevice *dev)
|
|||
qemu_spice_display_init_common(&qxl->ssd, vga->ds);
|
||||
|
||||
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)
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "loader.h"
|
||||
#include "elf.h"
|
||||
#include "hw/virtio.h"
|
||||
#include "hw/virtio-rng.h"
|
||||
#include "hw/virtio-serial.h"
|
||||
#include "hw/virtio-net.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
@ -206,6 +207,18 @@ static int s390_virtio_scsi_init(VirtIOS390Device *dev)
|
|||
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)
|
||||
{
|
||||
ram_addr_t token_off;
|
||||
|
@ -448,6 +461,29 @@ static TypeInfo s390_virtio_serial = {
|
|||
.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)
|
||||
{
|
||||
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_net);
|
||||
type_register_static(&s390_virtio_scsi);
|
||||
type_register_static(&s390_virtio_rng);
|
||||
type_register_static(&s390_virtio_bridge_info);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "virtio-blk.h"
|
||||
#include "virtio-net.h"
|
||||
#include "virtio-rng.h"
|
||||
#include "virtio-serial.h"
|
||||
#include "virtio-scsi.h"
|
||||
|
||||
|
@ -75,6 +76,7 @@ struct VirtIOS390Device {
|
|||
virtio_serial_conf serial;
|
||||
virtio_net_conf net;
|
||||
VirtIOSCSIConf scsi;
|
||||
VirtIORNGConf rng;
|
||||
};
|
||||
|
||||
typedef struct VirtIOS390Bus {
|
||||
|
|
|
@ -652,7 +652,6 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
|
|||
if (buflen > SCSI_MAX_INQUIRY_LEN) {
|
||||
buflen = SCSI_MAX_INQUIRY_LEN;
|
||||
}
|
||||
memset(outbuf, 0, buflen);
|
||||
|
||||
outbuf[0] = s->qdev.type & 0x1f;
|
||||
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)
|
||||
{
|
||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
|
||||
uint8_t *p = inbuf;
|
||||
int cmd = r->req.cmd.buf[0];
|
||||
int len = r->req.cmd.xfer;
|
||||
|
@ -1424,6 +1424,14 @@ static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf)
|
|||
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);
|
||||
return;
|
||||
|
||||
|
@ -1596,24 +1604,26 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
|
|||
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) {
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
buflen = req->cmd.xfer;
|
||||
outbuf = r->iov.iov_base;
|
||||
memset(outbuf, 0, r->buflen);
|
||||
switch (req->cmd.buf[0]) {
|
||||
case TEST_UNIT_READY:
|
||||
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[6] = s->qdev.blocksize >> 8;
|
||||
outbuf[7] = 0;
|
||||
buflen = 8;
|
||||
break;
|
||||
case REQUEST_SENSE:
|
||||
/* Just return "NO SENSE". */
|
||||
buflen = scsi_build_sense(NULL, 0, outbuf, r->buflen,
|
||||
(req->cmd.buf[1] & 1) == 0);
|
||||
if (buflen < 0) {
|
||||
goto illegal_request;
|
||||
}
|
||||
break;
|
||||
case MECHANISM_STATUS:
|
||||
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. */
|
||||
buflen = req->cmd.xfer;
|
||||
break;
|
||||
}
|
||||
DPRINTF("Unsupported Service Action In\n");
|
||||
|
@ -1827,7 +1838,7 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
|
|||
return 0;
|
||||
}
|
||||
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) {
|
||||
scsi_req_complete(&r->req, GOOD);
|
||||
}
|
||||
|
@ -1962,7 +1973,6 @@ static void scsi_disk_resize_cb(void *opaque)
|
|||
* direct-access devices.
|
||||
*/
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
24
hw/usb.h
24
hw/usb.h
|
@ -38,6 +38,7 @@
|
|||
#define USB_TOKEN_IN 0x69 /* device -> host */
|
||||
#define USB_TOKEN_OUT 0xe1 /* host -> device */
|
||||
|
||||
#define USB_RET_SUCCESS (0)
|
||||
#define USB_RET_NODEV (-1)
|
||||
#define USB_RET_NAK (-2)
|
||||
#define USB_RET_STALL (-3)
|
||||
|
@ -280,18 +281,20 @@ typedef struct USBDeviceClass {
|
|||
* Process control request.
|
||||
* 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,
|
||||
int index, int length, uint8_t *data);
|
||||
void (*handle_control)(USBDevice *dev, USBPacket *p, int request, int value,
|
||||
int index, int length, uint8_t *data);
|
||||
|
||||
/*
|
||||
* Process data transfers (both BULK and ISOC).
|
||||
* 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,
|
||||
int alt_old, int alt_new);
|
||||
|
@ -354,7 +357,8 @@ struct USBPacket {
|
|||
uint64_t parameter; /* control transfers */
|
||||
bool short_not_ok;
|
||||
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. */
|
||||
USBPacketState state;
|
||||
USBCombinedPacket *combined;
|
||||
|
@ -388,7 +392,7 @@ static inline bool usb_packet_is_inflight(USBPacket *p)
|
|||
|
||||
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_one(USBDevice *dev, 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);
|
||||
|
||||
int usb_device_handle_control(USBDevice *dev, USBPacket *p, int request, int value,
|
||||
int index, int length, uint8_t *data);
|
||||
void usb_device_handle_control(USBDevice *dev, USBPacket *p, int request,
|
||||
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,
|
||||
int alt_old, int alt_new);
|
||||
|
|
13
hw/usb/bus.c
13
hw/usb/bus.c
|
@ -140,24 +140,21 @@ void usb_device_handle_reset(USBDevice *dev)
|
|||
}
|
||||
}
|
||||
|
||||
int usb_device_handle_control(USBDevice *dev, USBPacket *p, int request,
|
||||
int value, int index, int length, uint8_t *data)
|
||||
void usb_device_handle_control(USBDevice *dev, USBPacket *p, int request,
|
||||
int value, int index, int length, uint8_t *data)
|
||||
{
|
||||
USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
|
||||
if (klass->handle_control) {
|
||||
return klass->handle_control(dev, p, request, value, index, length,
|
||||
data);
|
||||
klass->handle_control(dev, p, request, value, index, length, 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);
|
||||
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)
|
||||
|
|
|
@ -31,12 +31,16 @@ static void usb_combined_packet_add(USBCombinedPacket *combined, USBPacket *p)
|
|||
p->combined = combined;
|
||||
}
|
||||
|
||||
/* Note will free combined when the last packet gets removed */
|
||||
static void usb_combined_packet_remove(USBCombinedPacket *combined,
|
||||
USBPacket *p)
|
||||
{
|
||||
assert(p->combined == combined);
|
||||
p->combined = NULL;
|
||||
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 */
|
||||
|
@ -45,9 +49,8 @@ void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p)
|
|||
USBCombinedPacket *combined = p->combined;
|
||||
USBEndpoint *ep = p->ep;
|
||||
USBPacket *next;
|
||||
enum { completing, complete, leftover };
|
||||
int result, state = completing;
|
||||
bool short_not_ok;
|
||||
int status, actual_length;
|
||||
bool short_not_ok, done = false;
|
||||
|
||||
if (combined == NULL) {
|
||||
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));
|
||||
|
||||
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;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(p, &combined->packets, combined_entry, next) {
|
||||
if (state == completing) {
|
||||
if (!done) {
|
||||
/* Distribute data over uncombined packets */
|
||||
if (result >= p->iov.size) {
|
||||
p->result = p->iov.size;
|
||||
if (actual_length >= p->iov.size) {
|
||||
p->actual_length = p->iov.size;
|
||||
} else {
|
||||
/* Send short or error packet to complete the transfer */
|
||||
p->result = result;
|
||||
state = complete;
|
||||
p->actual_length = actual_length;
|
||||
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;
|
||||
/* Note will free combined when the last packet gets removed! */
|
||||
usb_combined_packet_remove(combined, p);
|
||||
usb_packet_complete_one(dev, p);
|
||||
result -= p->result;
|
||||
actual_length -= p->actual_length;
|
||||
} else {
|
||||
/* Remove any leftover packets from the queue */
|
||||
state = leftover;
|
||||
p->result = USB_RET_REMOVE_FROM_QUEUE;
|
||||
p->status = USB_RET_REMOVE_FROM_QUEUE;
|
||||
/* Note will free combined on the last packet! */
|
||||
dev->port->ops->complete(dev->port, p);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
/* Do not use combined here, it has been freed! */
|
||||
leave:
|
||||
/* Check if there are packets in the queue waiting for our completion */
|
||||
usb_ep_combine_input_packets(ep);
|
||||
|
@ -97,14 +102,13 @@ void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p)
|
|||
{
|
||||
USBCombinedPacket *combined = p->combined;
|
||||
assert(combined != NULL);
|
||||
USBPacket *first = p->combined->first;
|
||||
|
||||
/* Note will free combined on the last packet! */
|
||||
usb_combined_packet_remove(combined, p);
|
||||
if (p == combined->first) {
|
||||
if (p == first) {
|
||||
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;
|
||||
USBPort *port = ep->dev->port;
|
||||
int ret, totalsize;
|
||||
int totalsize;
|
||||
|
||||
assert(ep->pipeline);
|
||||
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) {
|
||||
/* Empty the queue on a halt */
|
||||
if (ep->halted) {
|
||||
p->result = USB_RET_REMOVE_FROM_QUEUE;
|
||||
p->status = USB_RET_REMOVE_FROM_QUEUE;
|
||||
port->ops->complete(port, p);
|
||||
continue;
|
||||
}
|
||||
|
@ -166,8 +170,8 @@ void usb_ep_combine_input_packets(USBEndpoint *ep)
|
|||
next == NULL ||
|
||||
/* Work around for Linux usbfs bulk splitting + migration */
|
||||
(totalsize == 16348 && p->int_req)) {
|
||||
ret = usb_device_handle_data(ep->dev, first);
|
||||
assert(ret == USB_RET_ASYNC);
|
||||
usb_device_handle_data(ep->dev, first);
|
||||
assert(first->status == USB_RET_ASYNC);
|
||||
if (first->combined) {
|
||||
QTAILQ_FOREACH(u, &first->combined->packets, combined_entry) {
|
||||
usb_packet_set_state(u, USB_PACKET_ASYNC);
|
||||
|
|
209
hw/usb/core.c
209
hw/usb/core.c
|
@ -97,17 +97,17 @@ void usb_wakeup(USBEndpoint *ep)
|
|||
#define SETUP_STATE_ACK 3
|
||||
#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 ret = 0;
|
||||
|
||||
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);
|
||||
p->result = 0;
|
||||
p->actual_length = 0;
|
||||
s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6];
|
||||
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];
|
||||
|
||||
if (s->setup_buf[0] & USB_DIR_IN) {
|
||||
ret = usb_device_handle_control(s, p, request, value, index,
|
||||
s->setup_len, s->data_buf);
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
s->setup_state = SETUP_STATE_SETUP;
|
||||
return USB_RET_ASYNC;
|
||||
usb_device_handle_control(s, p, request, value, index,
|
||||
s->setup_len, s->data_buf);
|
||||
if (p->status == USB_RET_ASYNC) {
|
||||
s->setup_state = SETUP_STATE_SETUP;
|
||||
}
|
||||
if (p->status != USB_RET_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret < s->setup_len)
|
||||
s->setup_len = ret;
|
||||
if (p->actual_length < s->setup_len) {
|
||||
s->setup_len = p->actual_length;
|
||||
}
|
||||
s->setup_state = SETUP_STATE_DATA;
|
||||
} else {
|
||||
if (s->setup_len > sizeof(s->data_buf)) {
|
||||
fprintf(stderr,
|
||||
"usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
|
||||
s->setup_len, sizeof(s->data_buf));
|
||||
return USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
return;
|
||||
}
|
||||
if (s->setup_len == 0)
|
||||
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;
|
||||
}
|
||||
|
||||
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 ret = 0;
|
||||
|
||||
assert(p->ep->nr == 0);
|
||||
|
||||
|
@ -158,19 +159,15 @@ static int do_token_in(USBDevice *s, USBPacket *p)
|
|||
switch(s->setup_state) {
|
||||
case SETUP_STATE_ACK:
|
||||
if (!(s->setup_buf[0] & USB_DIR_IN)) {
|
||||
ret = usb_device_handle_control(s, p, request, value, index,
|
||||
s->setup_len, s->data_buf);
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
return USB_RET_ASYNC;
|
||||
usb_device_handle_control(s, p, request, value, index,
|
||||
s->setup_len, s->data_buf);
|
||||
if (p->status == USB_RET_ASYNC) {
|
||||
return;
|
||||
}
|
||||
s->setup_state = SETUP_STATE_IDLE;
|
||||
if (ret > 0)
|
||||
return 0;
|
||||
return ret;
|
||||
p->actual_length = 0;
|
||||
}
|
||||
|
||||
/* return 0 byte */
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case SETUP_STATE_DATA:
|
||||
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);
|
||||
s->setup_index += len;
|
||||
if (s->setup_index >= s->setup_len)
|
||||
if (s->setup_index >= s->setup_len) {
|
||||
s->setup_state = SETUP_STATE_ACK;
|
||||
return len;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
s->setup_state = SETUP_STATE_IDLE;
|
||||
return USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
|
||||
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);
|
||||
|
||||
|
@ -205,7 +203,7 @@ static int do_token_out(USBDevice *s, USBPacket *p)
|
|||
} else {
|
||||
/* ignore additional output */
|
||||
}
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case SETUP_STATE_DATA:
|
||||
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);
|
||||
s->setup_index += len;
|
||||
if (s->setup_index >= s->setup_len)
|
||||
if (s->setup_index >= s->setup_len) {
|
||||
s->setup_state = SETUP_STATE_ACK;
|
||||
return len;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
s->setup_state = SETUP_STATE_IDLE;
|
||||
return USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
|
||||
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, ret = 0;
|
||||
int i, request, value, index;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
s->setup_buf[i] = p->parameter >> (i*8);
|
||||
|
@ -249,27 +247,27 @@ static int do_parameter(USBDevice *s, USBPacket *p)
|
|||
fprintf(stderr,
|
||||
"usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
|
||||
s->setup_len, sizeof(s->data_buf));
|
||||
return USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->pid == USB_TOKEN_OUT) {
|
||||
usb_packet_copy(p, s->data_buf, s->setup_len);
|
||||
}
|
||||
|
||||
ret = usb_device_handle_control(s, p, request, value, index,
|
||||
s->setup_len, s->data_buf);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
usb_device_handle_control(s, p, request, value, index,
|
||||
s->setup_len, s->data_buf);
|
||||
if (p->status == USB_RET_ASYNC) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret < s->setup_len) {
|
||||
s->setup_len = ret;
|
||||
if (p->actual_length < s->setup_len) {
|
||||
s->setup_len = p->actual_length;
|
||||
}
|
||||
if (p->pid == USB_TOKEN_IN) {
|
||||
p->actual_length = 0;
|
||||
usb_packet_copy(p, s->data_buf, s->setup_len);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p)
|
||||
{
|
||||
if (p->result < 0) {
|
||||
if (p->status < 0) {
|
||||
s->setup_state = SETUP_STATE_IDLE;
|
||||
}
|
||||
|
||||
switch (s->setup_state) {
|
||||
case SETUP_STATE_SETUP:
|
||||
if (p->result < s->setup_len) {
|
||||
s->setup_len = p->result;
|
||||
if (p->actual_length < s->setup_len) {
|
||||
s->setup_len = p->actual_length;
|
||||
}
|
||||
s->setup_state = SETUP_STATE_DATA;
|
||||
p->result = 8;
|
||||
p->actual_length = 8;
|
||||
break;
|
||||
|
||||
case SETUP_STATE_ACK:
|
||||
s->setup_state = SETUP_STATE_IDLE;
|
||||
p->result = 0;
|
||||
p->actual_length = 0;
|
||||
break;
|
||||
|
||||
case SETUP_STATE_PARAM:
|
||||
if (p->result < s->setup_len) {
|
||||
s->setup_len = p->result;
|
||||
if (p->actual_length < s->setup_len) {
|
||||
s->setup_len = p->actual_length;
|
||||
}
|
||||
if (p->pid == USB_TOKEN_IN) {
|
||||
p->result = 0;
|
||||
p->actual_length = 0;
|
||||
usb_packet_copy(p, s->data_buf, s->setup_len);
|
||||
}
|
||||
break;
|
||||
|
@ -342,40 +340,57 @@ USBDevice *usb_find_device(USBPort *port, uint8_t 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;
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
/* control pipe */
|
||||
if (p->parameter) {
|
||||
return do_parameter(dev, p);
|
||||
do_parameter(dev, p);
|
||||
return;
|
||||
}
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_SETUP:
|
||||
return do_token_setup(dev, p);
|
||||
do_token_setup(dev, p);
|
||||
break;
|
||||
case USB_TOKEN_IN:
|
||||
return do_token_in(dev, p);
|
||||
do_token_in(dev, p);
|
||||
break;
|
||||
case USB_TOKEN_OUT:
|
||||
return do_token_out(dev, p);
|
||||
do_token_out(dev, p);
|
||||
break;
|
||||
default:
|
||||
return USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
}
|
||||
} else {
|
||||
/* 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
|
||||
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) {
|
||||
return USB_RET_NODEV;
|
||||
p->status = USB_RET_NODEV;
|
||||
return;
|
||||
}
|
||||
assert(dev == p->ep->dev);
|
||||
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) {
|
||||
ret = usb_process_one(p);
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
usb_process_one(p);
|
||||
if (p->status == USB_RET_ASYNC) {
|
||||
assert(p->ep->type != USB_ENDPOINT_XFER_ISOC);
|
||||
usb_packet_set_state(p, USB_PACKET_ASYNC);
|
||||
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
|
||||
} else if (ret == USB_RET_ADD_TO_QUEUE) {
|
||||
usb_packet_set_state(p, USB_PACKET_QUEUED);
|
||||
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
|
||||
ret = USB_RET_ASYNC;
|
||||
} else if (p->status == USB_RET_ADD_TO_QUEUE) {
|
||||
usb_queue_one(p);
|
||||
} else {
|
||||
/*
|
||||
* When pipelining is enabled usb-devices must always return async,
|
||||
* otherwise packets can complete out of order!
|
||||
*/
|
||||
assert(!p->ep->pipeline || QTAILQ_EMPTY(&p->ep->queue));
|
||||
if (ret != USB_RET_NAK) {
|
||||
p->result = ret;
|
||||
if (p->status != USB_RET_NAK) {
|
||||
usb_packet_set_state(p, USB_PACKET_COMPLETE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret = USB_RET_ASYNC;
|
||||
usb_packet_set_state(p, USB_PACKET_QUEUED);
|
||||
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
|
||||
usb_queue_one(p);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
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)
|
||||
{
|
||||
USBEndpoint *ep = p->ep;
|
||||
int ret;
|
||||
|
||||
usb_packet_check_state(p, USB_PACKET_ASYNC);
|
||||
usb_packet_complete_one(dev, p);
|
||||
|
@ -447,7 +456,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
|
|||
p = QTAILQ_FIRST(&ep->queue);
|
||||
if (ep->halted) {
|
||||
/* 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);
|
||||
continue;
|
||||
}
|
||||
|
@ -455,12 +464,11 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
|
|||
break;
|
||||
}
|
||||
usb_packet_check_state(p, USB_PACKET_QUEUED);
|
||||
ret = usb_process_one(p);
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
usb_process_one(p);
|
||||
if (p->status == USB_RET_ASYNC) {
|
||||
usb_packet_set_state(p, USB_PACKET_ASYNC);
|
||||
break;
|
||||
}
|
||||
p->result = ret;
|
||||
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->pid = pid;
|
||||
p->ep = ep;
|
||||
p->result = 0;
|
||||
p->status = USB_RET_SUCCESS;
|
||||
p->actual_length = 0;
|
||||
p->parameter = 0;
|
||||
p->short_not_ok = short_not_ok;
|
||||
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)
|
||||
{
|
||||
assert(p->result >= 0);
|
||||
assert(p->result + bytes <= p->iov.size);
|
||||
assert(p->actual_length >= 0);
|
||||
assert(p->actual_length + bytes <= p->iov.size);
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_SETUP:
|
||||
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;
|
||||
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;
|
||||
default:
|
||||
fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid);
|
||||
abort();
|
||||
}
|
||||
p->result += bytes;
|
||||
p->actual_length += bytes;
|
||||
}
|
||||
|
||||
void usb_packet_skip(USBPacket *p, size_t bytes)
|
||||
{
|
||||
assert(p->result >= 0);
|
||||
assert(p->result + bytes <= p->iov.size);
|
||||
assert(p->actual_length >= 0);
|
||||
assert(p->actual_length + bytes <= p->iov.size);
|
||||
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)
|
||||
|
|
|
@ -626,7 +626,8 @@ int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len)
|
|||
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 USBDescDevice *other_dev;
|
||||
|
@ -696,6 +697,8 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
|
|||
ret = len;
|
||||
}
|
||||
memcpy(dest, buf, ret);
|
||||
p->actual_length = ret;
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -715,7 +718,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
|
|||
break;
|
||||
|
||||
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;
|
||||
|
||||
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.
|
||||
*/
|
||||
data[0] = dev->config ? dev->config->bConfigurationValue : 0;
|
||||
ret = 1;
|
||||
p->actual_length = 1;
|
||||
ret = 0;
|
||||
break;
|
||||
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
||||
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[1] = 0x00;
|
||||
ret = 2;
|
||||
p->actual_length = 2;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
||||
|
@ -772,7 +777,8 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
|
|||
break;
|
||||
}
|
||||
data[0] = dev->altsetting[index];
|
||||
ret = 1;
|
||||
p->actual_length = 1;
|
||||
ret = 0;
|
||||
break;
|
||||
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
|
||||
ret = usb_desc_set_interface(dev, index, value);
|
||||
|
|
|
@ -216,7 +216,8 @@ void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str);
|
|||
void usb_desc_create_serial(USBDevice *dev);
|
||||
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_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 request, int value, int index, int length, uint8_t *data);
|
||||
|
||||
|
|
|
@ -503,7 +503,7 @@ static int usb_audio_set_control(USBAudioState *s, uint8_t attrib,
|
|||
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 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);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (request) {
|
||||
|
@ -534,6 +534,7 @@ static int usb_audio_handle_control(USBDevice *dev, USBPacket *p,
|
|||
}
|
||||
goto fail;
|
||||
}
|
||||
p->actual_length = ret;
|
||||
break;
|
||||
|
||||
case ClassInterfaceOutRequest | CR_SET_CUR:
|
||||
|
@ -557,10 +558,9 @@ fail:
|
|||
"request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n",
|
||||
request, value, index, length);
|
||||
}
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
return USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
return;
|
||||
}
|
||||
|
||||
rc = streambuf_put(&s->out.buf, p);
|
||||
if (rc < p->iov.size && s->debug > 1) {
|
||||
streambuf_put(&s->out.buf, p);
|
||||
if (p->actual_length < p->iov.size && s->debug > 1) {
|
||||
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;
|
||||
int ret = 0;
|
||||
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
switch (p->ep->nr) {
|
||||
case 1:
|
||||
ret = usb_audio_handle_dataout(s, p);
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
fail:
|
||||
ret = USB_RET_STALL;
|
||||
break;
|
||||
if (p->pid == USB_TOKEN_OUT && p->ep->nr == 1) {
|
||||
usb_audio_handle_dataout(s, p);
|
||||
return;
|
||||
}
|
||||
if (ret == USB_RET_STALL && s->debug) {
|
||||
|
||||
p->status = USB_RET_STALL;
|
||||
if (s->debug) {
|
||||
fprintf(stderr, "usb-audio: failed data transaction: "
|
||||
"pid 0x%x ep 0x%x len 0x%zx\n",
|
||||
p->pid, p->ep->nr, p->iov.size);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usb_audio_handle_destroy(USBDevice *dev)
|
||||
|
|
|
@ -285,13 +285,15 @@ static void usb_bt_fifo_enqueue(struct usb_hci_in_fifo_s *fifo,
|
|||
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)
|
||||
{
|
||||
int len;
|
||||
|
||||
if (likely(!fifo->len))
|
||||
return USB_RET_STALL;
|
||||
if (likely(!fifo->len)) {
|
||||
p->status = USB_RET_STALL;
|
||||
return;
|
||||
}
|
||||
|
||||
len = MIN(p->iov.size, fifo->fifo[fifo->start].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->dsize = DFIFO_LEN_MASK + 1;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
switch (request) {
|
||||
case InterfaceRequest | USB_REQ_GET_STATUS:
|
||||
case EndpointRequest | USB_REQ_GET_STATUS:
|
||||
data[0] = 0x00;
|
||||
data[1] = 0x00;
|
||||
ret = 2;
|
||||
p->actual_length = 2;
|
||||
break;
|
||||
case InterfaceOutRequest | 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;
|
||||
default:
|
||||
fail:
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
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;
|
||||
int ret = 0;
|
||||
|
||||
if (!s->config)
|
||||
goto fail;
|
||||
|
@ -425,15 +422,15 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p)
|
|||
case USB_TOKEN_IN:
|
||||
switch (p->ep->nr) {
|
||||
case USB_EVT_EP:
|
||||
ret = usb_bt_fifo_dequeue(&s->evt, p);
|
||||
usb_bt_fifo_dequeue(&s->evt, p);
|
||||
break;
|
||||
|
||||
case USB_ACL_EP:
|
||||
ret = usb_bt_fifo_dequeue(&s->acl, p);
|
||||
usb_bt_fifo_dequeue(&s->acl, p);
|
||||
break;
|
||||
|
||||
case USB_SCO_EP:
|
||||
ret = usb_bt_fifo_dequeue(&s->sco, p);
|
||||
usb_bt_fifo_dequeue(&s->sco, p);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -460,11 +457,9 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p)
|
|||
|
||||
default:
|
||||
fail:
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usb_bt_out_hci_packet_event(void *opaque,
|
||||
|
|
|
@ -371,7 +371,7 @@ static void usb_hid_handle_reset(USBDevice *dev)
|
|||
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)
|
||||
{
|
||||
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);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
switch (request) {
|
||||
/* hid specific requests */
|
||||
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) {
|
||||
memcpy(data, 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) {
|
||||
memcpy(data, 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) {
|
||||
memcpy(data, 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;
|
||||
default:
|
||||
|
@ -409,14 +408,14 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
|
|||
break;
|
||||
case GET_REPORT:
|
||||
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) {
|
||||
ret = hid_keyboard_poll(hs, data, length);
|
||||
p->actual_length = hid_keyboard_poll(hs, data, length);
|
||||
}
|
||||
break;
|
||||
case SET_REPORT:
|
||||
if (hs->kind == HID_KEYBOARD) {
|
||||
ret = hid_keyboard_write(hs, data, length);
|
||||
p->actual_length = hid_keyboard_write(hs, data, length);
|
||||
} else {
|
||||
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) {
|
||||
goto fail;
|
||||
}
|
||||
ret = 1;
|
||||
data[0] = hs->protocol;
|
||||
p->actual_length = 1;
|
||||
break;
|
||||
case SET_PROTOCOL:
|
||||
if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) {
|
||||
goto fail;
|
||||
}
|
||||
ret = 0;
|
||||
hs->protocol = value;
|
||||
break;
|
||||
case GET_IDLE:
|
||||
ret = 1;
|
||||
data[0] = hs->idle;
|
||||
p->actual_length = 1;
|
||||
break;
|
||||
case SET_IDLE:
|
||||
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) {
|
||||
hid_pointer_activate(hs);
|
||||
}
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
fail:
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
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);
|
||||
HIDState *hs = &us->hid;
|
||||
uint8_t buf[p->iov.size];
|
||||
int ret = 0;
|
||||
int len = 0;
|
||||
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_IN:
|
||||
|
@ -471,15 +467,16 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
|
|||
}
|
||||
if (!hid_has_events(hs) &&
|
||||
(!hs->idle || hs->next_idle_clock - curtime > 0)) {
|
||||
return USB_RET_NAK;
|
||||
p->status = USB_RET_NAK;
|
||||
return;
|
||||
}
|
||||
hid_set_next_idle(hs, curtime);
|
||||
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) {
|
||||
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 {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -487,10 +484,9 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
|
|||
case USB_TOKEN_OUT:
|
||||
default:
|
||||
fail:
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usb_hid_handle_destroy(USBDevice *dev)
|
||||
|
|
|
@ -288,7 +288,7 @@ static const char *feature_name(int 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)
|
||||
{
|
||||
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);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
|
||||
switch(request) {
|
||||
|
@ -306,7 +306,6 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
|
|||
if (value == 0 && index != 0x81) { /* clear ep halt */
|
||||
goto fail;
|
||||
}
|
||||
ret = 0;
|
||||
break;
|
||||
/* usb specific requests */
|
||||
case GetHubStatus:
|
||||
|
@ -314,7 +313,7 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
|
|||
data[1] = 0;
|
||||
data[2] = 0;
|
||||
data[3] = 0;
|
||||
ret = 4;
|
||||
p->actual_length = 4;
|
||||
break;
|
||||
case GetPortStatus:
|
||||
{
|
||||
|
@ -331,16 +330,14 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
|
|||
data[1] = port->wPortStatus >> 8;
|
||||
data[2] = port->wPortChange;
|
||||
data[3] = port->wPortChange >> 8;
|
||||
ret = 4;
|
||||
p->actual_length = 4;
|
||||
}
|
||||
break;
|
||||
case SetHubFeature:
|
||||
case ClearHubFeature:
|
||||
if (value == 0 || value == 1) {
|
||||
} else {
|
||||
if (value != 0 && value != 1) {
|
||||
goto fail;
|
||||
}
|
||||
ret = 0;
|
||||
break;
|
||||
case SetPortFeature:
|
||||
{
|
||||
|
@ -373,7 +370,6 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
|
|||
default:
|
||||
goto fail;
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
case ClearPortFeature:
|
||||
|
@ -413,7 +409,6 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
|
|||
default:
|
||||
goto fail;
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
case GetHubDescriptor:
|
||||
|
@ -437,22 +432,20 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
|
|||
var_hub_size++;
|
||||
}
|
||||
|
||||
ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size;
|
||||
data[0] = ret;
|
||||
p->actual_length = sizeof(qemu_hub_hub_descriptor) + var_hub_size;
|
||||
data[0] = p->actual_length;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
fail:
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
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;
|
||||
int ret;
|
||||
|
||||
switch(p->pid) {
|
||||
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 */
|
||||
n = 1;
|
||||
} else if (n > p->iov.size) {
|
||||
return USB_RET_BABBLE;
|
||||
p->status = USB_RET_BABBLE;
|
||||
return;
|
||||
}
|
||||
status = 0;
|
||||
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);
|
||||
}
|
||||
usb_packet_copy(p, buf, n);
|
||||
ret = n;
|
||||
} else {
|
||||
ret = USB_RET_NAK; /* usb11 11.13.1 */
|
||||
p->status = USB_RET_NAK; /* usb11 11.13.1 */
|
||||
}
|
||||
} else {
|
||||
goto fail;
|
||||
|
@ -489,10 +482,9 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
|
|||
case USB_TOKEN_OUT:
|
||||
default:
|
||||
fail:
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usb_hub_handle_destroy(USBDevice *dev)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
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);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
switch(request) {
|
||||
case ClassInterfaceOutRequest | USB_CDC_SEND_ENCAPSULATED_COMMAND:
|
||||
if (!is_rndis(s) || value || index != 0) {
|
||||
|
@ -1078,22 +1077,25 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
|
|||
}
|
||||
#endif
|
||||
ret = rndis_parse(s, data, length);
|
||||
if (ret < 0) {
|
||||
p->status = ret;
|
||||
}
|
||||
break;
|
||||
|
||||
case ClassInterfaceRequest | USB_CDC_GET_ENCAPSULATED_RESPONSE:
|
||||
if (!is_rndis(s) || value || index != 0) {
|
||||
goto fail;
|
||||
}
|
||||
ret = rndis_get_response(s, data);
|
||||
if (!ret) {
|
||||
p->actual_length = rndis_get_response(s, data);
|
||||
if (p->actual_length == 0) {
|
||||
data[0] = 0;
|
||||
ret = 1;
|
||||
p->actual_length = 1;
|
||||
}
|
||||
#ifdef TRAFFIC_DEBUG
|
||||
{
|
||||
unsigned int i;
|
||||
fprintf(stderr, "GET_ENCAPSULATED_RESPONSE:");
|
||||
for (i = 0; i < ret; i++) {
|
||||
for (i = 0; i < p->actual_length; i++) {
|
||||
if (!(i & 15))
|
||||
fprintf(stderr, "\n%04x:", 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: "
|
||||
"request 0x%x value 0x%x index 0x%x length 0x%x\n",
|
||||
request, value, index, length);
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
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];
|
||||
int ret = 8;
|
||||
|
||||
if (p->iov.size < 8) {
|
||||
return USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
return;
|
||||
}
|
||||
|
||||
buf[0] = cpu_to_le32(1);
|
||||
buf[1] = cpu_to_le32(0);
|
||||
usb_packet_copy(p, buf, 8);
|
||||
if (!s->rndis_resp.tqh_first)
|
||||
ret = USB_RET_NAK;
|
||||
if (!s->rndis_resp.tqh_first) {
|
||||
p->status = USB_RET_NAK;
|
||||
}
|
||||
|
||||
#ifdef TRAFFIC_DEBUG
|
||||
fprintf(stderr, "usbnet: interrupt poll len %zu return %d",
|
||||
p->iov.size, ret);
|
||||
iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret);
|
||||
p->iov.size, p->status);
|
||||
iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->status);
|
||||
#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) {
|
||||
usb_net_reset_in_buf(s);
|
||||
ret = USB_RET_NAK;
|
||||
return ret;
|
||||
p->status = USB_RET_NAK;
|
||||
return;
|
||||
}
|
||||
if (!s->in_len) {
|
||||
ret = USB_RET_NAK;
|
||||
return ret;
|
||||
p->status = USB_RET_NAK;
|
||||
return;
|
||||
}
|
||||
ret = s->in_len - s->in_ptr;
|
||||
if (ret > p->iov.size) {
|
||||
ret = p->iov.size;
|
||||
len = s->in_len - s->in_ptr;
|
||||
if (len > p->iov.size) {
|
||||
len = p->iov.size;
|
||||
}
|
||||
usb_packet_copy(p, &s->in_buf[s->in_ptr], ret);
|
||||
s->in_ptr += ret;
|
||||
usb_packet_copy(p, &s->in_buf[s->in_ptr], len);
|
||||
s->in_ptr += 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 */
|
||||
usb_net_reset_in_buf(s);
|
||||
}
|
||||
|
||||
#ifdef TRAFFIC_DEBUG
|
||||
fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, ret);
|
||||
iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret);
|
||||
fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, len);
|
||||
iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", len);
|
||||
#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;
|
||||
struct rndis_packet_msg_type *msg =
|
||||
(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);
|
||||
#endif
|
||||
|
||||
if (sz > ret)
|
||||
sz = ret;
|
||||
if (sz > p->iov.size) {
|
||||
sz = p->iov.size;
|
||||
}
|
||||
usb_packet_copy(p, &s->out_buf[s->out_ptr], sz);
|
||||
s->out_ptr += sz;
|
||||
|
||||
if (!is_rndis(s)) {
|
||||
if (ret < 64) {
|
||||
if (p->iov.size < 64) {
|
||||
qemu_send_packet(&s->nic->nc, s->out_buf, s->out_ptr);
|
||||
s->out_ptr = 0;
|
||||
}
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
len = le32_to_cpu(msg->MessageLength);
|
||||
if (s->out_ptr < 8 || s->out_ptr < len)
|
||||
return ret;
|
||||
if (s->out_ptr < 8 || s->out_ptr < len) {
|
||||
return;
|
||||
}
|
||||
if (le32_to_cpu(msg->MessageType) == RNDIS_PACKET_MSG) {
|
||||
uint32_t offs = 8 + le32_to_cpu(msg->DataOffset);
|
||||
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;
|
||||
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;
|
||||
int ret = 0;
|
||||
|
||||
switch(p->pid) {
|
||||
case USB_TOKEN_IN:
|
||||
switch (p->ep->nr) {
|
||||
case 1:
|
||||
ret = usb_net_handle_statusin(s, p);
|
||||
usb_net_handle_statusin(s, p);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
ret = usb_net_handle_datain(s, p);
|
||||
usb_net_handle_datain(s, p);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1235,7 +1231,7 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
|
|||
case USB_TOKEN_OUT:
|
||||
switch (p->ep->nr) {
|
||||
case 2:
|
||||
ret = usb_net_handle_dataout(s, p);
|
||||
usb_net_handle_dataout(s, p);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1245,14 +1241,15 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
|
|||
|
||||
default:
|
||||
fail:
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
if (ret == USB_RET_STALL)
|
||||
|
||||
if (p->status == USB_RET_STALL) {
|
||||
fprintf(stderr, "usbnet: failed data transaction: "
|
||||
"pid 0x%x ep 0x%x len 0x%zx\n",
|
||||
p->pid, p->ep->nr, p->iov.size);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t size)
|
||||
|
|
|
@ -219,7 +219,7 @@ static uint8_t usb_get_modem_lines(USBSerialState *s)
|
|||
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)
|
||||
{
|
||||
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);
|
||||
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
switch (request) {
|
||||
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
/* Class specific requests. */
|
||||
|
@ -323,7 +321,7 @@ static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
|
|||
case DeviceInVendor | FTDI_GET_MDM_ST:
|
||||
data[0] = usb_get_modem_lines(s) | 1;
|
||||
data[1] = 0;
|
||||
ret = 2;
|
||||
p->actual_length = 2;
|
||||
break;
|
||||
case DeviceOutVendor | FTDI_SET_EVENT_CHR:
|
||||
/* TODO: handle it */
|
||||
|
@ -338,25 +336,23 @@ static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
|
|||
break;
|
||||
case DeviceInVendor | FTDI_GET_LATENCY:
|
||||
data[0] = s->latency;
|
||||
ret = 1;
|
||||
p->actual_length = 1;
|
||||
break;
|
||||
default:
|
||||
fail:
|
||||
DPRINTF("got unsupported/bogus control %x, value %x\n", request, value);
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
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;
|
||||
int i, ret = 0;
|
||||
uint8_t devep = p->ep->nr;
|
||||
struct iovec *iov;
|
||||
uint8_t header[2];
|
||||
int first_len, len;
|
||||
int i, first_len, len;
|
||||
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
|
@ -366,6 +362,7 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
|
|||
iov = p->iov.iov + i;
|
||||
qemu_chr_fe_write(s->cs, iov->iov_base, iov->iov_len);
|
||||
}
|
||||
p->actual_length = p->iov.size;
|
||||
break;
|
||||
|
||||
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;
|
||||
len = p->iov.size;
|
||||
if (len <= 2) {
|
||||
ret = USB_RET_NAK;
|
||||
p->status = USB_RET_NAK;
|
||||
break;
|
||||
}
|
||||
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;
|
||||
header[1] = FTDI_BI;
|
||||
usb_packet_copy(p, header, 2);
|
||||
ret = 2;
|
||||
break;
|
||||
} else {
|
||||
header[1] = 0;
|
||||
|
@ -393,7 +389,7 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
|
|||
if (len > s->recv_used)
|
||||
len = s->recv_used;
|
||||
if (!len) {
|
||||
ret = USB_RET_NAK;
|
||||
p->status = USB_RET_NAK;
|
||||
break;
|
||||
}
|
||||
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);
|
||||
s->recv_used -= len;
|
||||
s->recv_ptr = (s->recv_ptr + len) % RECV_BUF;
|
||||
ret = len + 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
DPRINTF("Bad token\n");
|
||||
fail:
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usb_serial_handle_destroy(USBDevice *dev)
|
||||
|
|
|
@ -635,39 +635,38 @@ static void ccid_handle_reset(USBDevice *dev)
|
|||
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)
|
||||
{
|
||||
USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
DPRINTF(s, 1, "got control %x, value %x\n", request, value);
|
||||
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (request) {
|
||||
/* Class specific requests. */
|
||||
case InterfaceOutClass | CCID_CONTROL_ABORT:
|
||||
DPRINTF(s, 1, "ccid_control abort UNIMPLEMENTED\n");
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
case InterfaceInClass | CCID_CONTROL_GET_CLOCK_FREQUENCIES:
|
||||
DPRINTF(s, 1, "ccid_control get clock frequencies UNIMPLEMENTED\n");
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
case InterfaceInClass | CCID_CONTROL_GET_DATA_RATES:
|
||||
DPRINTF(s, 1, "ccid_control get data rates UNIMPLEMENTED\n");
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
default:
|
||||
DPRINTF(s, 1, "got unsupported/bogus control %x, value %x\n",
|
||||
request, value);
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool ccid_card_inserted(USBCCIDState *s)
|
||||
|
@ -870,18 +869,13 @@ static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock *recv)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
static void ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
|
||||
{
|
||||
CCID_Header *ccid_header;
|
||||
|
||||
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;
|
||||
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,
|
||||
"usb-ccid: bulk_in: expecting more packets (%zd/%d)\n",
|
||||
p->iov.size, ccid_header->dwLength);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
if (s->bulk_out_pos < 10) {
|
||||
DPRINTF(s, 1,
|
||||
|
@ -949,60 +943,52 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
|
|||
}
|
||||
}
|
||||
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);
|
||||
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);
|
||||
usb_packet_copy(p, s->current_bulk_in->data +
|
||||
s->current_bulk_in->pos, ret);
|
||||
s->current_bulk_in->pos += ret;
|
||||
s->current_bulk_in->pos, len);
|
||||
s->current_bulk_in->pos += len;
|
||||
if (s->current_bulk_in->pos == s->current_bulk_in->len) {
|
||||
ccid_bulk_in_release(s);
|
||||
}
|
||||
} else {
|
||||
/* 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,
|
||||
"%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,
|
||||
"%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);
|
||||
int ret = 0;
|
||||
uint8_t buf[2];
|
||||
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_OUT:
|
||||
ret = ccid_handle_bulk_out(s, p);
|
||||
ccid_handle_bulk_out(s, p);
|
||||
break;
|
||||
|
||||
case USB_TOKEN_IN:
|
||||
switch (p->ep->nr) {
|
||||
case CCID_BULK_IN_EP:
|
||||
if (!p->iov.size) {
|
||||
ret = USB_RET_NAK;
|
||||
} else {
|
||||
ret = ccid_bulk_in_copy_to_guest(s, p);
|
||||
}
|
||||
ccid_bulk_in_copy_to_guest(s, p);
|
||||
break;
|
||||
case CCID_INT_IN_EP:
|
||||
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[1] = s->bmSlotICCState;
|
||||
usb_packet_copy(p, buf, 2);
|
||||
ret = 2;
|
||||
s->notify_slot_change = false;
|
||||
s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK;
|
||||
DPRINTF(s, D_INFO,
|
||||
|
@ -1021,17 +1006,15 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p)
|
|||
break;
|
||||
default:
|
||||
DPRINTF(s, 1, "Bad endpoint\n");
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DPRINTF(s, 1, "Bad token\n");
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ccid_handle_destroy(USBDevice *dev)
|
||||
|
|
|
@ -215,7 +215,7 @@ static const USBDesc desc = {
|
|||
static void usb_msd_copy_data(MSDState *s, USBPacket *p)
|
||||
{
|
||||
uint32_t len;
|
||||
len = p->iov.size - p->result;
|
||||
len = p->iov.size - p->actual_length;
|
||||
if (len > s->scsi_len)
|
||||
len = s->scsi_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) {
|
||||
usb_msd_copy_data(s, p);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -292,7 +293,7 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t r
|
|||
s->mode = USB_MSDM_CBW;
|
||||
} else {
|
||||
if (s->data_len) {
|
||||
int len = (p->iov.size - p->result);
|
||||
int len = (p->iov.size - p->actual_length);
|
||||
usb_packet_skip(p, 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;
|
||||
}
|
||||
}
|
||||
p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
|
||||
usb_msd_packet_complete(s);
|
||||
} else if (s->data_len == 0) {
|
||||
s->mode = USB_MSDM_CSW;
|
||||
|
@ -330,14 +332,14 @@ static void usb_msd_handle_reset(USBDevice *dev)
|
|||
assert(s->req == NULL);
|
||||
|
||||
if (s->packet) {
|
||||
s->packet->result = USB_RET_STALL;
|
||||
s->packet->status = USB_RET_STALL;
|
||||
usb_msd_packet_complete(s);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
switch (request) {
|
||||
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
|
||||
ret = 0;
|
||||
break;
|
||||
/* Class specific requests. */
|
||||
case ClassInterfaceOutRequest | MassStorageReset:
|
||||
/* Reset state ready for the next CBW. */
|
||||
s->mode = USB_MSDM_CBW;
|
||||
ret = 0;
|
||||
break;
|
||||
case ClassInterfaceRequest | GetMaxLun:
|
||||
data[0] = 0;
|
||||
ret = 1;
|
||||
p->actual_length = 1;
|
||||
break;
|
||||
default:
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
uint32_t tag;
|
||||
int ret = 0;
|
||||
struct usb_msd_cbw cbw;
|
||||
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) {
|
||||
scsi_req_continue(s->req);
|
||||
}
|
||||
ret = p->result;
|
||||
break;
|
||||
|
||||
case USB_MSDM_DATAOUT:
|
||||
|
@ -446,7 +442,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
|||
usb_msd_copy_data(s, p);
|
||||
}
|
||||
if (le32_to_cpu(s->csw.residue)) {
|
||||
int len = p->iov.size - p->result;
|
||||
int len = p->iov.size - p->actual_length;
|
||||
if (len) {
|
||||
usb_packet_skip(p, 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);
|
||||
s->packet = p;
|
||||
ret = USB_RET_ASYNC;
|
||||
} else {
|
||||
ret = p->result;
|
||||
p->status = USB_RET_ASYNC;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -481,7 +475,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
|||
}
|
||||
/* Waiting for SCSI write to complete. */
|
||||
s->packet = p;
|
||||
ret = USB_RET_ASYNC;
|
||||
p->status = USB_RET_ASYNC;
|
||||
break;
|
||||
|
||||
case USB_MSDM_CSW:
|
||||
|
@ -493,11 +487,10 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
|||
/* still in flight */
|
||||
DPRINTF("Deferring packet %p [wait status]\n", p);
|
||||
s->packet = p;
|
||||
ret = USB_RET_ASYNC;
|
||||
p->status = USB_RET_ASYNC;
|
||||
} else {
|
||||
usb_msd_send_status(s, p);
|
||||
s->mode = USB_MSDM_CBW;
|
||||
ret = 13;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -508,7 +501,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
|||
usb_msd_copy_data(s, p);
|
||||
}
|
||||
if (le32_to_cpu(s->csw.residue)) {
|
||||
int len = p->iov.size - p->result;
|
||||
int len = p->iov.size - p->actual_length;
|
||||
if (len) {
|
||||
usb_packet_skip(p, 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);
|
||||
s->packet = p;
|
||||
ret = USB_RET_ASYNC;
|
||||
} else {
|
||||
ret = p->result;
|
||||
p->status = USB_RET_ASYNC;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -535,11 +526,9 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
|||
default:
|
||||
DPRINTF("Bad token\n");
|
||||
fail:
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usb_msd_password_cb(void *opaque, int err)
|
||||
|
|
|
@ -256,10 +256,10 @@ static void usb_uas_send_status_bh(void *opaque)
|
|||
|
||||
uas->status = NULL;
|
||||
usb_packet_copy(p, &st->status, st->length);
|
||||
p->result = st->length;
|
||||
QTAILQ_REMOVE(&uas->results, st, next);
|
||||
g_free(st);
|
||||
|
||||
p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
|
||||
usb_packet_complete(&uas->dev, p);
|
||||
}
|
||||
|
||||
|
@ -349,6 +349,7 @@ static void usb_uas_complete_data_packet(UASRequest *req)
|
|||
p = req->data;
|
||||
req->data = NULL;
|
||||
req->data_async = false;
|
||||
p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
|
||||
usb_packet_complete(&req->uas->dev, p);
|
||||
}
|
||||
|
||||
|
@ -357,16 +358,16 @@ static void usb_uas_copy_data(UASRequest *req)
|
|||
uint32_t length;
|
||||
|
||||
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,
|
||||
req->data->result, req->data->iov.size,
|
||||
req->data->actual_length, req->data->iov.size,
|
||||
req->buf_off, req->buf_size);
|
||||
usb_packet_copy(req->data, scsi_req_get_buf(req->req) + req->buf_off,
|
||||
length);
|
||||
req->buf_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);
|
||||
}
|
||||
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 ret;
|
||||
|
||||
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
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)
|
||||
|
@ -641,13 +642,13 @@ incorrect_lun:
|
|||
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);
|
||||
uas_ui ui;
|
||||
UASStatus *st;
|
||||
UASRequest *req;
|
||||
int length, ret = 0;
|
||||
int length;
|
||||
|
||||
switch (p->ep->nr) {
|
||||
case UAS_PIPE_ID_COMMAND:
|
||||
|
@ -656,16 +657,14 @@ static int usb_uas_handle_data(USBDevice *dev, USBPacket *p)
|
|||
switch (ui.hdr.id) {
|
||||
case UAS_UI_COMMAND:
|
||||
usb_uas_command(uas, &ui);
|
||||
ret = length;
|
||||
break;
|
||||
case UAS_UI_TASK_MGMT:
|
||||
usb_uas_task(uas, &ui);
|
||||
ret = length;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown command ui: id 0x%x\n",
|
||||
__func__, ui.hdr.id);
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -674,11 +673,10 @@ static int usb_uas_handle_data(USBDevice *dev, USBPacket *p)
|
|||
if (st == NULL) {
|
||||
assert(uas->status == NULL);
|
||||
uas->status = p;
|
||||
ret = USB_RET_ASYNC;
|
||||
p->status = USB_RET_ASYNC;
|
||||
break;
|
||||
}
|
||||
usb_packet_copy(p, &st->status, st->length);
|
||||
ret = st->length;
|
||||
QTAILQ_REMOVE(&uas->results, st, next);
|
||||
g_free(st);
|
||||
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;
|
||||
if (req == NULL) {
|
||||
fprintf(stderr, "%s: no inflight request\n", __func__);
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
scsi_req_ref(req->req);
|
||||
req->data = p;
|
||||
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;
|
||||
ret = p->result;
|
||||
} else {
|
||||
req->data_async = true;
|
||||
ret = USB_RET_ASYNC;
|
||||
p->status = USB_RET_ASYNC;
|
||||
}
|
||||
scsi_req_unref(req->req);
|
||||
usb_uas_start_next_transfer(uas);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: invalid endpoint %d\n", __func__, p->ep->nr);
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usb_uas_handle_destroy(USBDevice *dev)
|
||||
|
|
|
@ -250,7 +250,7 @@ static void usb_wacom_handle_reset(USBDevice *dev)
|
|||
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)
|
||||
{
|
||||
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);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
switch (request) {
|
||||
case WACOM_SET_REPORT:
|
||||
if (s->mouse_grabbed) {
|
||||
|
@ -269,61 +268,58 @@ static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
|
|||
s->mouse_grabbed = 0;
|
||||
}
|
||||
s->mode = data[0];
|
||||
ret = 0;
|
||||
break;
|
||||
case WACOM_GET_REPORT:
|
||||
data[0] = 0;
|
||||
data[1] = s->mode;
|
||||
ret = 2;
|
||||
p->actual_length = 2;
|
||||
break;
|
||||
/* USB HID requests */
|
||||
case HID_GET_REPORT:
|
||||
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)
|
||||
ret = usb_wacom_poll(s, data, length);
|
||||
p->actual_length = usb_wacom_poll(s, data, length);
|
||||
break;
|
||||
case HID_GET_IDLE:
|
||||
ret = 1;
|
||||
data[0] = s->idle;
|
||||
p->actual_length = 1;
|
||||
break;
|
||||
case HID_SET_IDLE:
|
||||
s->idle = (uint8_t) (value >> 8);
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
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;
|
||||
uint8_t buf[p->iov.size];
|
||||
int ret = 0;
|
||||
int len = 0;
|
||||
|
||||
switch (p->pid) {
|
||||
case USB_TOKEN_IN:
|
||||
if (p->ep->nr == 1) {
|
||||
if (!(s->changed || s->idle))
|
||||
return USB_RET_NAK;
|
||||
if (!(s->changed || s->idle)) {
|
||||
p->status = USB_RET_NAK;
|
||||
return;
|
||||
}
|
||||
s->changed = 0;
|
||||
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)
|
||||
ret = usb_wacom_poll(s, buf, p->iov.size);
|
||||
usb_packet_copy(p, buf, ret);
|
||||
len = usb_wacom_poll(s, buf, p->iov.size);
|
||||
usb_packet_copy(p, buf, len);
|
||||
break;
|
||||
}
|
||||
/* Fall through. */
|
||||
case USB_TOKEN_OUT:
|
||||
default:
|
||||
ret = USB_RET_STALL;
|
||||
break;
|
||||
p->status = USB_RET_STALL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usb_wacom_handle_destroy(USBDevice *dev)
|
||||
|
|
|
@ -91,6 +91,7 @@ static const VMStateDescription vmstate_ehci_pci = {
|
|||
.fields = (VMStateField[]) {
|
||||
VMSTATE_PCI_DEVICE(pcidev, EHCIPCIState),
|
||||
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->revision = i->revision;
|
||||
k->class_id = PCI_CLASS_SERIAL_USB;
|
||||
dc->vmsd = &vmstate_ehci;
|
||||
dc->vmsd = &vmstate_ehci_pci;
|
||||
dc->props = ehci_pci_properties;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,9 +29,6 @@
|
|||
|
||||
#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 */
|
||||
#define CAPLENGTH 0x0000 /* 1-byte, 0x0001 reserved */
|
||||
#define HCIVERSION 0x0002 /* 2-bytes, i/f version # */
|
||||
|
@ -1111,7 +1108,7 @@ static int ehci_init_transfer(EHCIPacket *p)
|
|||
while (bytes > 0) {
|
||||
if (cpage > 4) {
|
||||
fprintf(stderr, "cpage out of range (%d)\n", cpage);
|
||||
return USB_RET_PROCERR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
page = p->qtd.bufptr[cpage] & QTD_BUFPTR_MASK;
|
||||
|
@ -1129,16 +1126,16 @@ static int ehci_init_transfer(EHCIPacket *p)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ehci_finish_transfer(EHCIQueue *q, int status)
|
||||
static void ehci_finish_transfer(EHCIQueue *q, int len)
|
||||
{
|
||||
uint32_t cpage, offset;
|
||||
|
||||
if (status > 0) {
|
||||
if (len > 0) {
|
||||
/* update cpage & offset */
|
||||
cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE);
|
||||
offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK;
|
||||
|
||||
offset += status;
|
||||
offset += len;
|
||||
cpage += offset >> QTD_BUFPTR_SH;
|
||||
offset &= ~QTD_BUFPTR_MASK;
|
||||
|
||||
|
@ -1163,7 +1160,7 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
|
|||
p = container_of(packet, EHCIPacket, packet);
|
||||
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");
|
||||
ehci_free_packet(p);
|
||||
return;
|
||||
|
@ -1171,7 +1168,6 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
|
|||
|
||||
trace_usb_ehci_packet_action(p->queue, p, "wakeup");
|
||||
p->async = EHCI_ASYNC_FINISHED;
|
||||
p->usb_status = packet->result;
|
||||
|
||||
if (p->queue->async) {
|
||||
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)
|
||||
{
|
||||
EHCIPacket *p = QTAILQ_FIRST(&q->packets);
|
||||
uint32_t tbytes;
|
||||
|
||||
assert(p != NULL);
|
||||
assert(p->qtdaddr == q->qtdaddr);
|
||||
assert(p->async == EHCI_ASYNC_INITIALIZED ||
|
||||
p->async == EHCI_ASYNC_FINISHED);
|
||||
|
||||
DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n",
|
||||
q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status);
|
||||
DPRINTF("execute_complete: qhaddr 0x%x, next 0x%x, qtdaddr 0x%x, "
|
||||
"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->usb_status) {
|
||||
case USB_RET_IOERROR:
|
||||
case USB_RET_NODEV:
|
||||
q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR);
|
||||
set_field(&q->qh.token, 0, QTD_TOKEN_CERR);
|
||||
ehci_raise_irq(q->ehci, USBSTS_ERRINT);
|
||||
break;
|
||||
case USB_RET_STALL:
|
||||
q->qh.token |= QTD_TOKEN_HALT;
|
||||
ehci_raise_irq(q->ehci, USBSTS_ERRINT);
|
||||
break;
|
||||
case USB_RET_NAK:
|
||||
set_field(&q->qh.altnext_qtd, 0, QH_ALTNEXT_NAKCNT);
|
||||
return; /* We're not done yet with this transaction */
|
||||
case USB_RET_BABBLE:
|
||||
q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
|
||||
ehci_raise_irq(q->ehci, USBSTS_ERRINT);
|
||||
break;
|
||||
default:
|
||||
/* should not be triggerable */
|
||||
fprintf(stderr, "USB invalid response %d\n", p->usb_status);
|
||||
assert(0);
|
||||
break;
|
||||
switch (p->packet.status) {
|
||||
case USB_RET_SUCCESS:
|
||||
break;
|
||||
case USB_RET_IOERROR:
|
||||
case USB_RET_NODEV:
|
||||
q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR);
|
||||
set_field(&q->qh.token, 0, QTD_TOKEN_CERR);
|
||||
ehci_raise_irq(q->ehci, USBSTS_ERRINT);
|
||||
break;
|
||||
case USB_RET_STALL:
|
||||
q->qh.token |= QTD_TOKEN_HALT;
|
||||
ehci_raise_irq(q->ehci, USBSTS_ERRINT);
|
||||
break;
|
||||
case USB_RET_NAK:
|
||||
set_field(&q->qh.altnext_qtd, 0, QH_ALTNEXT_NAKCNT);
|
||||
return; /* We're not done yet with this transaction */
|
||||
case USB_RET_BABBLE:
|
||||
q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
|
||||
ehci_raise_irq(q->ehci, USBSTS_ERRINT);
|
||||
break;
|
||||
default:
|
||||
/* should not be triggerable */
|
||||
fprintf(stderr, "USB invalid response %d\n", p->packet.status);
|
||||
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 {
|
||||
// TODO check 4.12 for splits
|
||||
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);
|
||||
tbytes = 0;
|
||||
}
|
||||
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);
|
||||
qemu_sglist_destroy(&p->sgl);
|
||||
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)
|
||||
{
|
||||
USBEndpoint *ep;
|
||||
int ret;
|
||||
int endp;
|
||||
bool spd;
|
||||
|
||||
|
@ -1262,13 +1258,13 @@ static int ehci_execute(EHCIPacket *p, const char *action)
|
|||
|
||||
if (!(p->qtd.token & QTD_TOKEN_ACTIVE)) {
|
||||
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) {
|
||||
ehci_trace_guest_bug(p->queue->ehci,
|
||||
"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;
|
||||
|
@ -1292,7 +1288,7 @@ static int ehci_execute(EHCIPacket *p, const char *action)
|
|||
|
||||
if (p->async == EHCI_ASYNC_NONE) {
|
||||
if (ehci_init_transfer(p) != 0) {
|
||||
return USB_RET_PROCERR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
ret = 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",
|
||||
q->qhaddr, q->qh.next, q->qtdaddr, q->pid,
|
||||
q->packet.iov.size, endp, ret);
|
||||
usb_handle_packet(p->queue->dev, &p->packet);
|
||||
DPRINTF("submit: qh 0x%x next 0x%x qtd 0x%x pid 0x%x len %zd endp 0x%x "
|
||||
"status %d actual_length %d\n", p->queue->qhaddr, p->qtd.next,
|
||||
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");
|
||||
return USB_RET_PROCERR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 4.7.2
|
||||
|
@ -1325,7 +1322,6 @@ static int ehci_process_itd(EHCIState *ehci,
|
|||
{
|
||||
USBDevice *dev;
|
||||
USBEndpoint *ep;
|
||||
int ret;
|
||||
uint32_t i, len, pid, dir, devaddr, endp;
|
||||
uint32_t pg, off, ptr1, ptr2, max, mult;
|
||||
|
||||
|
@ -1348,7 +1344,7 @@ static int ehci_process_itd(EHCIState *ehci,
|
|||
}
|
||||
|
||||
if (len > BUFF_SIZE) {
|
||||
return USB_RET_PROCERR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
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,
|
||||
(itd->transact[i] & ITD_XACT_IOC) != 0);
|
||||
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);
|
||||
} else {
|
||||
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);
|
||||
|
||||
if (ret < 0) {
|
||||
switch (ret) {
|
||||
default:
|
||||
fprintf(stderr, "Unexpected iso usb result: %d\n", ret);
|
||||
/* Fall through */
|
||||
case USB_RET_IOERROR:
|
||||
case USB_RET_NODEV:
|
||||
/* 3.3.2: XACTERR is only allowed on IN transactions */
|
||||
if (dir) {
|
||||
itd->transact[i] |= ITD_XACT_XACTERR;
|
||||
ehci_raise_irq(ehci, USBSTS_ERRINT);
|
||||
}
|
||||
break;
|
||||
case USB_RET_BABBLE:
|
||||
itd->transact[i] |= ITD_XACT_BABBLE;
|
||||
switch (ehci->ipacket.status) {
|
||||
case USB_RET_SUCCESS:
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unexpected iso usb result: %d\n",
|
||||
ehci->ipacket.status);
|
||||
/* Fall through */
|
||||
case USB_RET_IOERROR:
|
||||
case USB_RET_NODEV:
|
||||
/* 3.3.2: XACTERR is only allowed on IN transactions */
|
||||
if (dir) {
|
||||
itd->transact[i] |= ITD_XACT_XACTERR;
|
||||
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) {
|
||||
/* OUT */
|
||||
set_field(&itd->transact[i], len - ret, ITD_XACT_LENGTH);
|
||||
} else {
|
||||
/* IN */
|
||||
set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
|
||||
}
|
||||
if (!dir) {
|
||||
set_field(&itd->transact[i], len - ehci->ipacket.actual_length,
|
||||
ITD_XACT_LENGTH); /* OUT */
|
||||
} else {
|
||||
set_field(&itd->transact[i], ehci->ipacket.actual_length,
|
||||
ITD_XACT_LENGTH); /* IN */
|
||||
}
|
||||
if (itd->transact[i] & ITD_XACT_IOC) {
|
||||
ehci_raise_irq(ehci, USBSTS_INT);
|
||||
|
@ -1746,8 +1742,7 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
|
|||
break;
|
||||
case EHCI_ASYNC_INFLIGHT:
|
||||
/* Check if the guest has added new tds to the queue */
|
||||
again = (ehci_fill_queue(QTAILQ_LAST(&q->packets, pkts_head)) ==
|
||||
USB_RET_PROCERR) ? -1 : 1;
|
||||
again = ehci_fill_queue(QTAILQ_LAST(&q->packets, pkts_head));
|
||||
/* Unfinished async handled packet, go horizontal */
|
||||
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
|
||||
break;
|
||||
|
@ -1784,6 +1779,7 @@ static int ehci_state_horizqh(EHCIQueue *q)
|
|||
return again;
|
||||
}
|
||||
|
||||
/* Returns "again" */
|
||||
static int ehci_fill_queue(EHCIPacket *p)
|
||||
{
|
||||
USBEndpoint *ep = p->packet.ep;
|
||||
|
@ -1812,17 +1808,14 @@ static int ehci_fill_queue(EHCIPacket *p)
|
|||
p = ehci_alloc_packet(q);
|
||||
p->qtdaddr = qtdaddr;
|
||||
p->qtd = qtd;
|
||||
p->usb_status = ehci_execute(p, "queue");
|
||||
if (p->usb_status == USB_RET_PROCERR) {
|
||||
break;
|
||||
if (ehci_execute(p, "queue") == -1) {
|
||||
return -1;
|
||||
}
|
||||
assert(p->usb_status == USB_RET_ASYNC);
|
||||
assert(p->packet.status == USB_RET_ASYNC);
|
||||
p->async = EHCI_ASYNC_INFLIGHT;
|
||||
}
|
||||
if (p->usb_status != USB_RET_PROCERR) {
|
||||
usb_device_flush_ep_queue(ep->dev, ep);
|
||||
}
|
||||
return p->usb_status;
|
||||
usb_device_flush_ep_queue(ep->dev, ep);
|
||||
return 1;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
p->usb_status = ehci_execute(p, "process");
|
||||
if (p->usb_status == USB_RET_PROCERR) {
|
||||
again = -1;
|
||||
again = ehci_execute(p, "process");
|
||||
if (again == -1) {
|
||||
goto out;
|
||||
}
|
||||
if (p->usb_status == USB_RET_ASYNC) {
|
||||
if (p->packet.status == USB_RET_ASYNC) {
|
||||
ehci_flush_qh(q);
|
||||
trace_usb_ehci_packet_action(p->queue, p, "async");
|
||||
p->async = EHCI_ASYNC_INFLIGHT;
|
||||
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
|
||||
if (q->async) {
|
||||
again = (ehci_fill_queue(p) == USB_RET_PROCERR) ? -1 : 1;
|
||||
again = ehci_fill_queue(p);
|
||||
} else {
|
||||
again = 1;
|
||||
}
|
||||
|
@ -1891,7 +1883,7 @@ static int ehci_state_executing(EHCIQueue *q)
|
|||
}
|
||||
|
||||
/* 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);
|
||||
} else {
|
||||
ehci_set_state(q->ehci, q->async, EST_WRITEBACK);
|
||||
|
|
|
@ -230,7 +230,6 @@ struct EHCIPacket {
|
|||
QEMUSGList sgl;
|
||||
int pid;
|
||||
enum async_state async;
|
||||
int usb_status;
|
||||
};
|
||||
|
||||
struct EHCIQueue {
|
||||
|
|
|
@ -607,7 +607,6 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
|
|||
{
|
||||
USBDevice *dev;
|
||||
USBEndpoint *uep;
|
||||
int ret;
|
||||
int idx = epnum && dir;
|
||||
int ttype;
|
||||
|
||||
|
@ -632,15 +631,19 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
|
|||
ep->packey[dir].ep = ep;
|
||||
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);
|
||||
ep->status[dir] = len;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -754,7 +757,6 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
|
|||
|
||||
if (ep->status[1] == USB_RET_STALL) {
|
||||
ep->status[1] = 0;
|
||||
packey->result = 0;
|
||||
|
||||
ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL;
|
||||
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: 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))) {
|
||||
ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY;
|
||||
if (!epnum)
|
||||
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 */
|
||||
}
|
||||
|
||||
|
|
|
@ -807,21 +807,24 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
|||
DMA_DIRECTION_TO_DEVICE);
|
||||
}
|
||||
|
||||
if (completion) {
|
||||
ret = ohci->usb_packet.result;
|
||||
} else {
|
||||
if (!completion) {
|
||||
bool int_req = relative_frame_number == frame_count &&
|
||||
OHCI_BM(iso_td.flags, TD_DI) == 0;
|
||||
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
|
||||
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
|
||||
usb_packet_setup(&ohci->usb_packet, pid, ep, addr, false, int_req);
|
||||
usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
|
||||
ret = usb_handle_packet(dev, &ohci->usb_packet);
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
usb_handle_packet(dev, &ohci->usb_packet);
|
||||
if (ohci->usb_packet.status == USB_RET_ASYNC) {
|
||||
usb_device_flush_ep_queue(dev, ep);
|
||||
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
|
||||
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
|
||||
if (completion) {
|
||||
ret = ohci->usb_packet.result;
|
||||
ohci->async_td = 0;
|
||||
ohci->async_complete = 0;
|
||||
} 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,
|
||||
OHCI_BM(td.flags, TD_DI) == 0);
|
||||
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
|
||||
DPRINTF("ret=%d\n", ret);
|
||||
DPRINTF("status=%d\n", ohci->usb_packet.status);
|
||||
#endif
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
if (ohci->usb_packet.status == USB_RET_ASYNC) {
|
||||
usb_device_flush_ep_queue(dev, ep);
|
||||
ohci->async_td = addr;
|
||||
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 (dir == OHCI_TD_DIR_IN) {
|
||||
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 */
|
||||
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_mmio(dev, &s->ohci.mem);
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
int len = 0, max_len, ret;
|
||||
int len = 0, max_len;
|
||||
uint8_t pid;
|
||||
|
||||
max_len = ((td->token >> 21) + 1) & 0x7ff;
|
||||
pid = td->token & 0xff;
|
||||
|
||||
ret = async->packet.result;
|
||||
|
||||
if (td->ctrl & TD_CTRL_IOS)
|
||||
td->ctrl &= ~TD_CTRL_ACTIVE;
|
||||
|
||||
if (ret < 0) {
|
||||
return uhci_handle_td_error(s, td, async->td_addr, ret, int_mask);
|
||||
if (async->packet.status != USB_RET_SUCCESS) {
|
||||
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);
|
||||
|
||||
/* 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,
|
||||
UHCI_TD *td, uint32_t td_addr, uint32_t *int_mask)
|
||||
{
|
||||
int len = 0, max_len;
|
||||
int ret, max_len;
|
||||
bool spd;
|
||||
bool queuing = (q != NULL);
|
||||
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) {
|
||||
case USB_TOKEN_OUT:
|
||||
case USB_TOKEN_SETUP:
|
||||
len = usb_handle_packet(q->ep->dev, &async->packet);
|
||||
if (len >= 0)
|
||||
len = max_len;
|
||||
usb_handle_packet(q->ep->dev, &async->packet);
|
||||
if (async->packet.status == USB_RET_SUCCESS) {
|
||||
async->packet.actual_length = max_len;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_TOKEN_IN:
|
||||
len = usb_handle_packet(q->ep->dev, &async->packet);
|
||||
usb_handle_packet(q->ep->dev, &async->packet);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -932,8 +932,8 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
|
|||
uhci_update_irq(s);
|
||||
return TD_RESULT_STOP_FRAME;
|
||||
}
|
||||
|
||||
if (len == USB_RET_ASYNC) {
|
||||
|
||||
if (async->packet.status == USB_RET_ASYNC) {
|
||||
uhci_async_link(async);
|
||||
if (!queuing) {
|
||||
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;
|
||||
}
|
||||
|
||||
async->packet.result = len;
|
||||
|
||||
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);
|
||||
uhci_async_free(async);
|
||||
return len;
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
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_cancel(async);
|
||||
return;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
int index;
|
||||
|
@ -1045,14 +1073,14 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
|
|||
{
|
||||
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] |= state;
|
||||
ctx[2] = epctx->ring.dequeue | epctx->ring.ccs;
|
||||
ctx[3] = (epctx->ring.dequeue >> 16) >> 16;
|
||||
DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n",
|
||||
epctx->pctx, state, ctx[3], ctx[2]);
|
||||
pci_dma_write(&xhci->pci_dev, epctx->pctx, ctx, sizeof(ctx));
|
||||
xhci_dma_write_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
|
||||
epctx->state = state;
|
||||
}
|
||||
|
||||
|
@ -1388,7 +1416,7 @@ static void xhci_xfer_report(XHCITransfer *xfer)
|
|||
XHCIState *xhci = xfer->xhci;
|
||||
int i;
|
||||
|
||||
left = xfer->packet.result < 0 ? 0 : xfer->packet.result;
|
||||
left = xfer->packet.actual_length;
|
||||
|
||||
for (i = 0; i < xfer->trb_count; i++) {
|
||||
XHCITRB *trb = &xfer->trbs[i];
|
||||
|
@ -1416,7 +1444,7 @@ static void xhci_xfer_report(XHCITransfer *xfer)
|
|||
|
||||
if (!reported && ((trb->control & TRB_TR_IOC) ||
|
||||
(shortpkt && (trb->control & TRB_TR_ISP)) ||
|
||||
(xfer->status != CC_SUCCESS))) {
|
||||
(xfer->status != CC_SUCCESS && left == 0))) {
|
||||
event.slotid = xfer->slotid;
|
||||
event.epid = xfer->epid;
|
||||
event.length = (trb->status & 0x1ffff) - chunk;
|
||||
|
@ -1490,16 +1518,16 @@ static int xhci_setup_packet(XHCITransfer *xfer)
|
|||
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);
|
||||
xfer->running_async = 1;
|
||||
xfer->running_retry = 0;
|
||||
xfer->complete = 0;
|
||||
xfer->cancelled = 0;
|
||||
return 0;
|
||||
} else if (ret == USB_RET_NAK) {
|
||||
} else if (xfer->packet.status == USB_RET_NAK) {
|
||||
trace_usb_xhci_xfer_nak(xfer);
|
||||
xfer->running_async = 0;
|
||||
xfer->running_retry = 1;
|
||||
|
@ -1513,16 +1541,16 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret)
|
|||
xhci_xfer_unmap(xfer);
|
||||
}
|
||||
|
||||
if (ret >= 0) {
|
||||
trace_usb_xhci_xfer_success(xfer, ret);
|
||||
if (xfer->packet.status == USB_RET_SUCCESS) {
|
||||
trace_usb_xhci_xfer_success(xfer, xfer->packet.actual_length);
|
||||
xfer->status = CC_SUCCESS;
|
||||
xhci_xfer_report(xfer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* error */
|
||||
trace_usb_xhci_xfer_error(xfer, ret);
|
||||
switch (ret) {
|
||||
trace_usb_xhci_xfer_error(xfer, xfer->packet.status);
|
||||
switch (xfer->packet.status) {
|
||||
case USB_RET_NODEV:
|
||||
xfer->status = CC_USB_TRANSACTION_ERROR;
|
||||
xhci_xfer_report(xfer);
|
||||
|
@ -1534,7 +1562,8 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret)
|
|||
xhci_stall_ep(xfer);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: FIXME: ret = %d\n", __FUNCTION__, ret);
|
||||
fprintf(stderr, "%s: FIXME: status = %d\n", __func__,
|
||||
xfer->packet.status);
|
||||
FIXME();
|
||||
}
|
||||
return 0;
|
||||
|
@ -1544,7 +1573,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
|
|||
{
|
||||
XHCITRB *trb_setup, *trb_status;
|
||||
uint8_t bmRequestType;
|
||||
int ret;
|
||||
|
||||
trb_setup = &xfer->trbs[0];
|
||||
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;
|
||||
|
||||
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) {
|
||||
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)
|
||||
{
|
||||
uint64_t mfindex;
|
||||
int ret;
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
XHCITransfer *xfer = epctx->retry;
|
||||
int result;
|
||||
|
||||
trace_usb_xhci_xfer_retry(xfer);
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
|
||||
assert(result != USB_RET_NAK);
|
||||
xhci_complete_packet(xfer, result);
|
||||
usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
|
||||
assert(xfer->packet.status != USB_RET_NAK);
|
||||
xhci_complete_packet(xfer);
|
||||
} else {
|
||||
/* retry nak'ed transfer */
|
||||
if (xhci_setup_packet(xfer) < 0) {
|
||||
return;
|
||||
}
|
||||
result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
|
||||
if (result == USB_RET_NAK) {
|
||||
usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
|
||||
if (xfer->packet.status == USB_RET_NAK) {
|
||||
return;
|
||||
}
|
||||
xhci_complete_packet(xfer, result);
|
||||
xhci_complete_packet(xfer);
|
||||
}
|
||||
assert(!xfer->running_retry);
|
||||
epctx->retry = NULL;
|
||||
|
@ -1883,14 +1909,14 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
|
|||
assert(slotid >= 1 && slotid <= xhci->numslots);
|
||||
|
||||
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);
|
||||
octx = xhci_mask64(le64_to_cpu(poctx));
|
||||
octx = xhci_mask64(poctx);
|
||||
|
||||
DPRINTF("xhci: input context at "DMA_ADDR_FMT"\n", ictx);
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
pci_dma_read(&xhci->pci_dev, 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+32, slot_ctx, sizeof(slot_ctx));
|
||||
xhci_dma_read_u32s(xhci, ictx+64, ep0_ctx, sizeof(ep0_ctx));
|
||||
|
||||
DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n",
|
||||
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",
|
||||
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));
|
||||
pci_dma_write(&xhci->pci_dev, octx+32, ep0_ctx, sizeof(ep0_ctx));
|
||||
xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
|
||||
xhci_dma_write_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx));
|
||||
|
||||
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_ADDRESSED << SLOT_STATE_SHIFT;
|
||||
DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
pci_dma_read(&xhci->pci_dev, ictx+32, islot_ctx, sizeof(islot_ctx));
|
||||
pci_dma_read(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx));
|
||||
xhci_dma_read_u32s(xhci, ictx+32, islot_ctx, sizeof(islot_ctx));
|
||||
xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
|
||||
|
||||
if (SLOT_STATE(slot_ctx[3]) < SLOT_ADDRESSED) {
|
||||
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);
|
||||
}
|
||||
if (ictl_ctx[1] & (1<<i)) {
|
||||
pci_dma_read(&xhci->pci_dev, ictx+32+(32*i), ep_ctx,
|
||||
sizeof(ep_ctx));
|
||||
xhci_dma_read_u32s(xhci, ictx+32+(32*i), ep_ctx, sizeof(ep_ctx));
|
||||
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],
|
||||
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",
|
||||
i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2],
|
||||
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",
|
||||
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;
|
||||
}
|
||||
|
@ -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: 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) {
|
||||
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) {
|
||||
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",
|
||||
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] |= 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",
|
||||
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) {
|
||||
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",
|
||||
iep0_ctx[0], iep0_ctx[1], iep0_ctx[2],
|
||||
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] |= 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",
|
||||
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;
|
||||
|
@ -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_DEFAULT << SLOT_STATE_SHIFT;
|
||||
DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
|
||||
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;
|
||||
}
|
||||
|
@ -2922,11 +2947,11 @@ static void xhci_complete(USBPort *port, USBPacket *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);
|
||||
return;
|
||||
}
|
||||
xhci_complete_packet(xfer, packet->result);
|
||||
xhci_complete_packet(xfer);
|
||||
xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid);
|
||||
}
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ static void usb_host_handle_reset(USBDevice *dev)
|
|||
* -check device states against transfer requests
|
||||
* and return appropriate response
|
||||
*/
|
||||
static int usb_host_handle_control(USBDevice *dev,
|
||||
static void usb_host_handle_control(USBDevice *dev,
|
||||
USBPacket *p,
|
||||
int request,
|
||||
int value,
|
||||
|
@ -139,7 +139,6 @@ static int usb_host_handle_control(USBDevice *dev,
|
|||
|
||||
/* specific SET_ADDRESS support */
|
||||
dev->addr = value;
|
||||
return 0;
|
||||
} else if ((request >> 8) == UT_WRITE_DEVICE &&
|
||||
(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",
|
||||
strerror(errno));
|
||||
#endif
|
||||
return USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else if ((request >> 8) == UT_WRITE_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",
|
||||
strerror(errno));
|
||||
#endif
|
||||
return USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
req.ucr_request.bmRequestType = request >> 8;
|
||||
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",
|
||||
strerror(errno));
|
||||
#endif
|
||||
return USB_RET_NAK; // STALL
|
||||
p->status = USB_RET_NAK; /* STALL */
|
||||
} 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;
|
||||
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);
|
||||
if (fd < 0) {
|
||||
sigprocmask(SIG_SETMASK, &old_mask, NULL);
|
||||
return USB_RET_NODEV;
|
||||
p->status = USB_RET_NODEV;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ioctl(fd, USB_SET_TIMEOUT, &timeout) < 0) {
|
||||
|
@ -267,12 +263,13 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
|
|||
switch(errno) {
|
||||
case ETIMEDOUT:
|
||||
case EINTR:
|
||||
return USB_RET_NAK;
|
||||
p->status = USB_RET_NAK;
|
||||
break;
|
||||
default:
|
||||
return USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
}
|
||||
} else {
|
||||
return ret;
|
||||
p->actual_length = ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -366,28 +366,29 @@ static void async_complete(void *opaque)
|
|||
if (p) {
|
||||
switch (aurb->urb.status) {
|
||||
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;
|
||||
|
||||
case -EPIPE:
|
||||
set_halt(s, p->pid, p->ep->nr);
|
||||
p->result = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
|
||||
case -EOVERFLOW:
|
||||
p->result = USB_RET_BABBLE;
|
||||
p->status = USB_RET_BABBLE;
|
||||
break;
|
||||
|
||||
default:
|
||||
p->result = USB_RET_IOERROR;
|
||||
p->status = USB_RET_IOERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
static int urb_status_to_usb_ret(int status)
|
||||
static void urb_status_to_usb_ret(int status, USBPacket *p)
|
||||
{
|
||||
switch (status) {
|
||||
case -EPIPE:
|
||||
return USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
case -EOVERFLOW:
|
||||
return USB_RET_BABBLE;
|
||||
p->status = USB_RET_BABBLE;
|
||||
break;
|
||||
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;
|
||||
int i, j, ret, max_packet_size, offset, len = 0;
|
||||
int i, j, max_packet_size, offset, len;
|
||||
uint8_t *buf;
|
||||
|
||||
max_packet_size = p->ep->max_packet_size;
|
||||
if (max_packet_size == 0)
|
||||
return USB_RET_NAK;
|
||||
if (max_packet_size == 0) {
|
||||
p->status = USB_RET_NAK;
|
||||
return;
|
||||
}
|
||||
|
||||
aurb = get_iso_urb(s, p->pid, p->ep->nr);
|
||||
if (!aurb) {
|
||||
|
@ -766,18 +771,17 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
|
|||
if (in) {
|
||||
/* Check 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 */
|
||||
aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1;
|
||||
/* Check frame status */
|
||||
} else if (aurb[i].urb.iso_frame_desc[j].status) {
|
||||
len = urb_status_to_usb_ret(
|
||||
aurb[i].urb.iso_frame_desc[j].status);
|
||||
urb_status_to_usb_ret(aurb[i].urb.iso_frame_desc[j].status, p);
|
||||
/* Check the frame fits */
|
||||
} else if (aurb[i].urb.iso_frame_desc[j].actual_length
|
||||
> p->iov.size) {
|
||||
printf("husb: received iso data is larger then packet\n");
|
||||
len = USB_RET_BABBLE;
|
||||
p->status = USB_RET_BABBLE;
|
||||
/* All good copy data over */
|
||||
} else {
|
||||
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 */
|
||||
if (len > max_packet_size) {
|
||||
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 */
|
||||
|
@ -823,17 +828,16 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
|
|||
/* (Re)-submit all fully consumed / filled urbs */
|
||||
for (i = 0; i < s->iso_urb_count; i++) {
|
||||
if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
|
||||
ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]);
|
||||
if (ret < 0) {
|
||||
if (ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]) < 0) {
|
||||
perror("USBDEVFS_SUBMITURB");
|
||||
if (!in || len == 0) {
|
||||
if (!in || p->status == USB_RET_SUCCESS) {
|
||||
switch(errno) {
|
||||
case ETIMEDOUT:
|
||||
len = USB_RET_NAK;
|
||||
p->status = USB_RET_NAK;
|
||||
break;
|
||||
case EPIPE:
|
||||
default:
|
||||
len = USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
}
|
||||
}
|
||||
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);
|
||||
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)) {
|
||||
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) {
|
||||
|
@ -877,13 +880,15 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
|
|||
if (ret < 0) {
|
||||
perror("USBDEVFS_CLEAR_HALT");
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -933,17 +938,19 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
|
|||
case ETIMEDOUT:
|
||||
trace_usb_host_req_complete(s->bus_num, s->addr, p,
|
||||
USB_RET_NAK);
|
||||
return USB_RET_NAK;
|
||||
p->status = USB_RET_NAK;
|
||||
break;
|
||||
case EPIPE:
|
||||
default:
|
||||
trace_usb_host_req_complete(s->bus_num, s->addr, p,
|
||||
USB_RET_STALL);
|
||||
return USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
} while (rem > 0);
|
||||
|
||||
return USB_RET_ASYNC;
|
||||
p->status = USB_RET_ASYNC;
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
|
@ -987,14 +993,15 @@ again:
|
|||
}
|
||||
|
||||
if (ret < 0) {
|
||||
return ctrl_error();
|
||||
p->status = ctrl_error();
|
||||
return;
|
||||
}
|
||||
usb_host_claim_interfaces(s, config);
|
||||
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;
|
||||
int i, ret;
|
||||
|
@ -1011,7 +1018,8 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
|
|||
}
|
||||
|
||||
if (iface >= USB_MAX_INTERFACES) {
|
||||
return USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
return;
|
||||
}
|
||||
|
||||
si.interface = iface;
|
||||
|
@ -1022,15 +1030,15 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
|
|||
iface, alt, ret, errno);
|
||||
|
||||
if (ret < 0) {
|
||||
return ctrl_error();
|
||||
p->status = ctrl_error();
|
||||
return;
|
||||
}
|
||||
|
||||
s->dev.altsetting[iface] = alt;
|
||||
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)
|
||||
{
|
||||
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
|
||||
|
@ -1048,19 +1056,19 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
|
|||
|
||||
switch (request) {
|
||||
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
||||
ret = usb_host_set_address(s, value);
|
||||
trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret);
|
||||
return ret;
|
||||
usb_host_set_address(s, value);
|
||||
trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
|
||||
return;
|
||||
|
||||
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
||||
ret = usb_host_set_config(s, value & 0xff);
|
||||
trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret);
|
||||
return ret;
|
||||
usb_host_set_config(s, value & 0xff, p);
|
||||
trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
|
||||
return;
|
||||
|
||||
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
|
||||
ret = usb_host_set_interface(s, index, value);
|
||||
trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret);
|
||||
return ret;
|
||||
usb_host_set_interface(s, index, value, p);
|
||||
trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
|
||||
return;
|
||||
|
||||
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
|
||||
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);
|
||||
clear_halt(s, pid, index & 0x0f);
|
||||
trace_usb_host_req_emulated(s->bus_num, s->addr, p, 0);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* The rest are asynchronous */
|
||||
assert(p && p->result == 0);
|
||||
|
||||
if (length > sizeof(dev->data_buf)) {
|
||||
fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n",
|
||||
length, sizeof(dev->data_buf));
|
||||
return USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
return;
|
||||
}
|
||||
|
||||
aurb = async_alloc(s);
|
||||
|
@ -1112,14 +1119,17 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
|
|||
|
||||
switch(errno) {
|
||||
case ETIMEDOUT:
|
||||
return USB_RET_NAK;
|
||||
p->status = USB_RET_NAK;
|
||||
break;
|
||||
case EPIPE:
|
||||
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 */
|
||||
|
|
|
@ -141,8 +141,8 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id,
|
|||
struct usb_redir_interrupt_packet_header *interrupt_header,
|
||||
uint8_t *data, int data_len);
|
||||
|
||||
static int usbredir_handle_status(USBRedirDevice *dev,
|
||||
int status, int actual_len);
|
||||
static void usbredir_handle_status(USBRedirDevice *dev, USBPacket *p,
|
||||
int status);
|
||||
|
||||
#define VERSION "qemu usb-redir guest " QEMU_VERSION
|
||||
|
||||
|
@ -443,7 +443,7 @@ static void usbredir_handle_reset(USBDevice *udev)
|
|||
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)
|
||||
{
|
||||
int status, len;
|
||||
|
@ -500,7 +500,7 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
|
|||
!dev->endpoint[EP2I(ep)].bufpq_prefilled) {
|
||||
if (dev->endpoint[EP2I(ep)].bufpq_size <
|
||||
dev->endpoint[EP2I(ep)].bufpq_target_size) {
|
||||
return usbredir_handle_status(dev, 0, 0);
|
||||
return;
|
||||
}
|
||||
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 */
|
||||
status = dev->endpoint[EP2I(ep)].iso_error;
|
||||
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,
|
||||
isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size);
|
||||
|
||||
status = isop->status;
|
||||
if (status != usb_redir_success) {
|
||||
bufp_free(dev, isop, ep);
|
||||
return USB_RET_IOERROR;
|
||||
}
|
||||
|
||||
len = isop->len;
|
||||
if (len > p->iov.size) {
|
||||
ERROR("received iso data is larger then packet ep %02X (%d > %d)\n",
|
||||
ep, len, (int)p->iov.size);
|
||||
bufp_free(dev, isop, ep);
|
||||
return USB_RET_BABBLE;
|
||||
len = p->iov.size;
|
||||
status = usb_redir_babble;
|
||||
}
|
||||
usb_packet_copy(p, isop->data, len);
|
||||
bufp_free(dev, isop, ep);
|
||||
return len;
|
||||
usbredir_handle_status(dev, p, status);
|
||||
} else {
|
||||
/* If the stream was not started because of a pending error don't
|
||||
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;
|
||||
DPRINTF2("iso-token-out ep %02X status %d len %zd\n", ep, status,
|
||||
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);
|
||||
}
|
||||
|
||||
static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
|
||||
static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
|
||||
uint8_t ep)
|
||||
{
|
||||
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);
|
||||
|
||||
if (usbredir_already_in_flight(dev, p->id)) {
|
||||
return USB_RET_ASYNC;
|
||||
p->status = USB_RET_ASYNC;
|
||||
return;
|
||||
}
|
||||
|
||||
bulk_packet.endpoint = ep;
|
||||
|
@ -608,10 +605,10 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
|
|||
&bulk_packet, buf, size);
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (ep & USB_DIR_IN) {
|
||||
|
@ -643,28 +640,25 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
|
|||
status = dev->endpoint[EP2I(ep)].interrupt_error;
|
||||
dev->endpoint[EP2I(ep)].interrupt_error = 0;
|
||||
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,
|
||||
intp->status, intp->len);
|
||||
|
||||
status = intp->status;
|
||||
if (status != usb_redir_success) {
|
||||
bufp_free(dev, intp, ep);
|
||||
return usbredir_handle_status(dev, status, 0);
|
||||
}
|
||||
|
||||
len = intp->len;
|
||||
if (len > p->iov.size) {
|
||||
ERROR("received int data is larger then packet ep %02X\n", ep);
|
||||
bufp_free(dev, intp, ep);
|
||||
return USB_RET_BABBLE;
|
||||
len = p->iov.size;
|
||||
status = usb_redir_babble;
|
||||
}
|
||||
usb_packet_copy(p, intp->data, len);
|
||||
bufp_free(dev, intp, ep);
|
||||
return len;
|
||||
usbredir_handle_status(dev, p, status);
|
||||
} else {
|
||||
/* Output interrupt endpoint, normal async operation */
|
||||
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);
|
||||
|
||||
if (usbredir_already_in_flight(dev, p->id)) {
|
||||
return USB_RET_ASYNC;
|
||||
p->status = USB_RET_ASYNC;
|
||||
return;
|
||||
}
|
||||
|
||||
interrupt_packet.endpoint = ep;
|
||||
|
@ -685,7 +680,7 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
|
|||
usbredirparser_send_interrupt_packet(dev->parser, p->id,
|
||||
&interrupt_packet, buf, p->iov.size);
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
uint8_t ep;
|
||||
|
@ -718,21 +713,26 @@ static int usbredir_handle_data(USBDevice *udev, USBPacket *p)
|
|||
switch (dev->endpoint[EP2I(ep)].type) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
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:
|
||||
return usbredir_handle_iso_data(dev, p, ep);
|
||||
usbredir_handle_iso_data(dev, p, ep);
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_BULK:
|
||||
if (p->state == USB_PACKET_SETUP && p->pid == USB_TOKEN_IN &&
|
||||
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:
|
||||
return usbredir_handle_interrupt_data(dev, p, ep);
|
||||
usbredir_handle_interrupt_data(dev, p, ep);
|
||||
break;
|
||||
default:
|
||||
ERROR("handle_data ep %02X has unknown type %d\n", ep,
|
||||
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)
|
||||
{
|
||||
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;
|
||||
usbredirparser_send_set_configuration(dev->parser, p->id, &set_config);
|
||||
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);
|
||||
|
||||
usbredirparser_send_get_configuration(dev->parser, p->id);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
usbredirparser_send_set_alt_setting(dev->parser, p->id, &set_alt);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
usbredirparser_send_get_alt_setting(dev->parser, p->id, &get_alt);
|
||||
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)
|
||||
{
|
||||
USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
|
||||
struct usb_redir_control_packet_header control_packet;
|
||||
|
||||
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 */
|
||||
|
@ -839,15 +840,19 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
|
|||
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
||||
DPRINTF("set address %d\n", value);
|
||||
dev->dev.addr = value;
|
||||
return 0;
|
||||
return;
|
||||
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:
|
||||
return usbredir_get_config(dev, p);
|
||||
usbredir_get_config(dev, p);
|
||||
return;
|
||||
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:
|
||||
return usbredir_get_interface(dev, p, index);
|
||||
usbredir_get_interface(dev, p, index);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
usbredirparser_do_write(dev->parser);
|
||||
return USB_RET_ASYNC;
|
||||
p->status = USB_RET_ASYNC;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1159,29 +1164,34 @@ error:
|
|||
* usbredirparser packet complete callbacks
|
||||
*/
|
||||
|
||||
static int usbredir_handle_status(USBRedirDevice *dev,
|
||||
int status, int actual_len)
|
||||
static void usbredir_handle_status(USBRedirDevice *dev, USBPacket *p,
|
||||
int status)
|
||||
{
|
||||
switch (status) {
|
||||
case usb_redir_success:
|
||||
return actual_len;
|
||||
p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
|
||||
break;
|
||||
case usb_redir_stall:
|
||||
return USB_RET_STALL;
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
case usb_redir_cancelled:
|
||||
/*
|
||||
* When the usbredir-host unredirects a device, it will report a status
|
||||
* 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:
|
||||
WARNING("got invalid param error from usb-host?\n");
|
||||
return USB_RET_IOERROR;
|
||||
p->status = USB_RET_IOERROR;
|
||||
break;
|
||||
case usb_redir_babble:
|
||||
return USB_RET_BABBLE;
|
||||
p->status = USB_RET_BABBLE;
|
||||
break;
|
||||
case usb_redir_ioerror:
|
||||
case usb_redir_timeout:
|
||||
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;
|
||||
USBPacket *p;
|
||||
int len = 0;
|
||||
|
||||
DPRINTF("set config status %d config %d id %"PRIu64"\n",
|
||||
config_status->status, config_status->configuration, id);
|
||||
|
@ -1421,9 +1430,9 @@ static void usbredir_configuration_status(void *priv, uint64_t id,
|
|||
if (p) {
|
||||
if (dev->dev.setup_buf[0] & USB_DIR_IN) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1433,7 +1442,6 @@ static void usbredir_alt_setting_status(void *priv, uint64_t id,
|
|||
{
|
||||
USBRedirDevice *dev = priv;
|
||||
USBPacket *p;
|
||||
int len = 0;
|
||||
|
||||
DPRINTF("alt status %d intf %d alt %d id: %"PRIu64"\n",
|
||||
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 (dev->dev.setup_buf[0] & USB_DIR_IN) {
|
||||
dev->dev.data_buf[0] = alt_setting_status->alt;
|
||||
len = 1;
|
||||
p->actual_length = 1;
|
||||
}
|
||||
p->result =
|
||||
usbredir_handle_status(dev, alt_setting_status->status, len);
|
||||
usbredir_handle_status(dev, p, alt_setting_status->status);
|
||||
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);
|
||||
if (p) {
|
||||
len = usbredir_handle_status(dev, control_packet->status, len);
|
||||
if (len > 0) {
|
||||
usbredir_handle_status(dev, p, control_packet->status);
|
||||
if (data_len > 0) {
|
||||
usbredir_log_data(dev, "ctrl data in:", data, data_len);
|
||||
if (data_len <= sizeof(dev->dev.data_buf)) {
|
||||
memcpy(dev->dev.data_buf, data, data_len);
|
||||
} else {
|
||||
if (data_len > sizeof(dev->dev.data_buf)) {
|
||||
ERROR("ctrl buffer too small (%d > %zu)\n",
|
||||
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);
|
||||
}
|
||||
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);
|
||||
if (p) {
|
||||
size_t size = (p->combined) ? p->combined->iov.size : p->iov.size;
|
||||
len = usbredir_handle_status(dev, bulk_packet->status, len);
|
||||
if (len > 0) {
|
||||
usbredir_handle_status(dev, p, bulk_packet->status);
|
||||
if (data_len > 0) {
|
||||
usbredir_log_data(dev, "bulk data in:", data, data_len);
|
||||
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 {
|
||||
if (data_len > size) {
|
||||
ERROR("bulk got more data then requested (%d > %zd)\n",
|
||||
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) {
|
||||
usb_combined_input_packet_complete(&dev->dev, p);
|
||||
} 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(dev, data, data_len, interrupt_packet->status, ep);
|
||||
} else {
|
||||
int len = interrupt_packet->length;
|
||||
|
||||
USBPacket *p = usbredir_find_packet_by_id(dev, ep, id);
|
||||
if (p) {
|
||||
p->result = usbredir_handle_status(dev,
|
||||
interrupt_packet->status, len);
|
||||
usbredir_handle_status(dev, p, interrupt_packet->status);
|
||||
p->actual_length = interrupt_packet->length;
|
||||
usb_packet_complete(&dev->dev, p);
|
||||
}
|
||||
}
|
||||
|
|
210
hw/vfio_pci.c
210
hw/vfio_pci.c
|
@ -185,6 +185,21 @@ static void vfio_unmask_intx(VFIODevice *vdev)
|
|||
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
|
||||
* 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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
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);
|
||||
if (ret) {
|
||||
error_report("vfio: Error: event_notifier_init failed\n");
|
||||
|
@ -290,6 +472,8 @@ static int vfio_enable_intx(VFIODevice *vdev)
|
|||
return -errno;
|
||||
}
|
||||
|
||||
vfio_enable_intx_kvm(vdev);
|
||||
|
||||
vdev->interrupt = VFIO_INT_INTx;
|
||||
|
||||
DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain,
|
||||
|
@ -303,6 +487,7 @@ static void vfio_disable_intx(VFIODevice *vdev)
|
|||
int fd;
|
||||
|
||||
qemu_del_timer(vdev->intx.mmap_timer);
|
||||
vfio_disable_intx_kvm(vdev);
|
||||
vfio_disable_irqindex(vdev, VFIO_PCI_INTX_IRQ_INDEX);
|
||||
vdev->intx.pending = false;
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
vfio_disable_interrupts(vdev);
|
||||
|
@ -563,7 +726,7 @@ retry:
|
|||
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,
|
||||
|
@ -1839,6 +2002,7 @@ static int vfio_initfn(PCIDevice *pdev)
|
|||
if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) {
|
||||
vdev->intx.mmap_timer = qemu_new_timer_ms(vm_clock,
|
||||
vfio_intx_mmap_enable, vdev);
|
||||
pci_device_set_intx_routing_notifier(&vdev->pdev, vfio_update_irq);
|
||||
ret = vfio_enable_intx(vdev);
|
||||
if (ret) {
|
||||
goto out_teardown;
|
||||
|
|
|
@ -84,9 +84,10 @@ static void pci_vga_ioport_write(void *ptr, hwaddr addr,
|
|||
uint64_t val, unsigned size)
|
||||
{
|
||||
PCIVGAState *d = ptr;
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
vga_ioport_write(&d->vga, addr, val);
|
||||
vga_ioport_write(&d->vga, addr + 0x3c0, val);
|
||||
break;
|
||||
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
|
||||
* index byte is updated first.
|
||||
*/
|
||||
vga_ioport_write(&d->vga, addr, val & 0xff);
|
||||
vga_ioport_write(&d->vga, addr+1, (val >> 8) & 0xff);
|
||||
vga_ioport_write(&d->vga, addr + 0x3c0, val & 0xff);
|
||||
vga_ioport_write(&d->vga, addr + 0x3c1, (val >> 8) & 0xff);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
3
hw/vga.c
3
hw/vga.c
|
@ -2321,9 +2321,8 @@ static const MemoryRegionPortio vbe_portio_list[] = {
|
|||
{ 0, 1, 2, .read = vbe_ioport_read_index, .write = vbe_ioport_write_index },
|
||||
# ifdef TARGET_I386
|
||||
{ 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
|
||||
{ 2, 1, 2, .read = vbe_ioport_read_data, .write = vbe_ioport_write_data },
|
||||
PORTIO_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
|
|
@ -852,6 +852,41 @@ static void virtio_balloon_exit_pci(PCIDevice *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[] = {
|
||||
DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
|
||||
DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, blk.conf),
|
||||
|
@ -982,6 +1017,50 @@ static TypeInfo virtio_balloon_info = {
|
|||
.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)
|
||||
{
|
||||
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_balloon_info);
|
||||
type_register_static(&virtio_scsi_info);
|
||||
type_register_static(&virtio_rng_info);
|
||||
}
|
||||
|
||||
type_init(virtio_pci_register_types)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "virtio-blk.h"
|
||||
#include "virtio-net.h"
|
||||
#include "virtio-rng.h"
|
||||
#include "virtio-serial.h"
|
||||
#include "virtio-scsi.h"
|
||||
|
||||
|
@ -46,6 +47,7 @@ typedef struct {
|
|||
virtio_serial_conf serial;
|
||||
virtio_net_conf net;
|
||||
VirtIOSCSIConf scsi;
|
||||
VirtIORNGConf rng;
|
||||
bool ioeventfd_disabled;
|
||||
bool ioeventfd_started;
|
||||
VirtIOIRQFD *vector_irqfd;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -204,7 +204,7 @@ static void virtio_scsi_bad_req(void)
|
|||
static void qemu_sgl_init_external(QEMUSGList *qsgl, struct iovec *sg,
|
||||
hwaddr *addr, int num)
|
||||
{
|
||||
memset(qsgl, 0, sizeof(*qsgl));
|
||||
qemu_sglist_init(qsgl, num, &dma_context_memory);
|
||||
while (num--) {
|
||||
qemu_sglist_add(qsgl, *(addr++), (sg++)->iov_len);
|
||||
}
|
||||
|
@ -596,6 +596,10 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
|
|||
VirtIOSCSIEvent *evt;
|
||||
int in_size;
|
||||
|
||||
if (!(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!req) {
|
||||
s->events_dropped = true;
|
||||
return;
|
||||
|
@ -648,7 +652,6 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense)
|
|||
VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
|
||||
|
||||
if (((s->vdev.guest_features >> VIRTIO_SCSI_F_CHANGE) & 1) &&
|
||||
(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) &&
|
||||
dev->type != TYPE_ROM) {
|
||||
virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_PARAM_CHANGE,
|
||||
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);
|
||||
|
||||
if (((s->vdev.guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) &&
|
||||
(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
|
||||
if ((s->vdev.guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) {
|
||||
virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET,
|
||||
VIRTIO_SCSI_EVT_RESET_RESCAN);
|
||||
}
|
||||
|
|
|
@ -203,6 +203,8 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *serial);
|
|||
VirtIODevice *virtio_balloon_init(DeviceState *dev);
|
||||
typedef struct VirtIOSCSIConf VirtIOSCSIConf;
|
||||
VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *conf);
|
||||
typedef struct VirtIORNGConf VirtIORNGConf;
|
||||
VirtIODevice *virtio_rng_init(DeviceState *dev, VirtIORNGConf *conf);
|
||||
#ifdef CONFIG_LINUX
|
||||
VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf);
|
||||
#endif
|
||||
|
@ -213,6 +215,7 @@ void virtio_blk_exit(VirtIODevice *vdev);
|
|||
void virtio_serial_exit(VirtIODevice *vdev);
|
||||
void virtio_balloon_exit(VirtIODevice *vdev);
|
||||
void virtio_scsi_exit(VirtIODevice *vdev);
|
||||
void virtio_rng_exit(VirtIODevice *vdev);
|
||||
|
||||
#define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \
|
||||
DEFINE_PROP_BIT("indirect_desc", _state, _field, \
|
||||
|
|
|
@ -946,6 +946,22 @@ void object_property_add_str(Object *obj, const char *name,
|
|||
void (*set)(Object *, const char *, struct Error **),
|
||||
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:
|
||||
* @obj: the object whose children will be navigated
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -17,8 +17,8 @@
|
|||
* Authors: Hollis Blanchard <hollisb@us.ibm.com>
|
||||
*/
|
||||
|
||||
#ifndef __POWERPC_KVM_PARA_H__
|
||||
#define __POWERPC_KVM_PARA_H__
|
||||
#ifndef _UAPI__POWERPC_KVM_PARA_H__
|
||||
#define _UAPI__POWERPC_KVM_PARA_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
|
@ -87,4 +87,4 @@ struct kvm_vcpu_arch_shared {
|
|||
#define KVM_MAGIC_FEAT_MAS0_TO_SPRG7 (1 << 1)
|
||||
|
||||
|
||||
#endif /* __POWERPC_KVM_PARA_H__ */
|
||||
#endif /* _UAPI__POWERPC_KVM_PARA_H__ */
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* definition for paravirtual devices on s390
|
||||
* User API definitions for paravirtual devices on s390
|
||||
*
|
||||
* Copyright IBM Corp. 2008
|
||||
*
|
||||
|
@ -9,9 +9,3 @@
|
|||
*
|
||||
* Author(s): Christian Borntraeger <borntraeger@de.ibm.com>
|
||||
*/
|
||||
|
||||
#ifndef __S390_KVM_PARA_H
|
||||
#define __S390_KVM_PARA_H
|
||||
|
||||
|
||||
#endif /* __S390_KVM_PARA_H */
|
||||
|
|
|
@ -9,6 +9,22 @@
|
|||
#include <linux/types.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> */
|
||||
#define __KVM_HAVE_PIT
|
||||
#define __KVM_HAVE_IOAPIC
|
||||
|
@ -25,6 +41,7 @@
|
|||
#define __KVM_HAVE_DEBUGREGS
|
||||
#define __KVM_HAVE_XSAVE
|
||||
#define __KVM_HAVE_XCRS
|
||||
#define __KVM_HAVE_READONLY_MEM
|
||||
|
||||
/* Architectural interrupt line count. */
|
||||
#define KVM_NR_INTERRUPTS 256
|
||||
|
|
|
@ -101,9 +101,13 @@ struct kvm_userspace_memory_region {
|
|||
__u64 userspace_addr; /* start of the userspace allocated memory */
|
||||
};
|
||||
|
||||
/* for kvm_memory_region::flags */
|
||||
#define KVM_MEM_LOG_DIRTY_PAGES 1UL
|
||||
#define KVM_MEMSLOT_INVALID (1UL << 1)
|
||||
/*
|
||||
* The bit 0 ~ bit 15 of kvm_memory_region::flags are visible for userspace,
|
||||
* 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 */
|
||||
struct kvm_irq_level {
|
||||
|
@ -618,6 +622,10 @@ struct kvm_ppc_smmu_info {
|
|||
#define KVM_CAP_PPC_GET_SMMU_INFO 78
|
||||
#define KVM_CAP_S390_COW 79
|
||||
#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
|
||||
|
||||
|
@ -683,12 +691,21 @@ struct kvm_xen_hvm_config {
|
|||
#endif
|
||||
|
||||
#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 {
|
||||
__u32 fd;
|
||||
__u32 gsi;
|
||||
__u32 flags;
|
||||
__u8 pad[20];
|
||||
__u32 resamplefd;
|
||||
__u8 pad[16];
|
||||
};
|
||||
|
||||
struct kvm_clock_data {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef __LINUX_KVM_PARA_H
|
||||
#define __LINUX_KVM_PARA_H
|
||||
#ifndef _UAPI__LINUX_KVM_PARA_H
|
||||
#define _UAPI__LINUX_KVM_PARA_H
|
||||
|
||||
/*
|
||||
* This header file provides a method for making a hypercall to the host
|
||||
|
@ -25,4 +25,4 @@
|
|||
*/
|
||||
#include <asm/kvm_para.h>
|
||||
|
||||
#endif /* __LINUX_KVM_PARA_H */
|
||||
#endif /* _UAPI__LINUX_KVM_PARA_H */
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef VFIO_H
|
||||
#define VFIO_H
|
||||
#ifndef _UAPIVFIO_H
|
||||
#define _UAPIVFIO_H
|
||||
|
||||
#include <linux/types.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)
|
||||
|
||||
#endif /* VFIO_H */
|
||||
#endif /* _UAPIVFIO_H */
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef _LINUX_VIRTIO_CONFIG_H
|
||||
#define _LINUX_VIRTIO_CONFIG_H
|
||||
#ifndef _UAPI_LINUX_VIRTIO_CONFIG_H
|
||||
#define _UAPI_LINUX_VIRTIO_CONFIG_H
|
||||
/* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so
|
||||
* anyone can use the definitions to implement compatible drivers/servers.
|
||||
*
|
||||
|
@ -51,4 +51,4 @@
|
|||
* suppressed them? */
|
||||
#define VIRTIO_F_NOTIFY_ON_EMPTY 24
|
||||
|
||||
#endif /* _LINUX_VIRTIO_CONFIG_H */
|
||||
#endif /* _UAPI_LINUX_VIRTIO_CONFIG_H */
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef _LINUX_VIRTIO_RING_H
|
||||
#define _LINUX_VIRTIO_RING_H
|
||||
#ifndef _UAPI_LINUX_VIRTIO_RING_H
|
||||
#define _UAPI_LINUX_VIRTIO_RING_H
|
||||
/* An interface for efficient virtio implementation, currently for use by KVM
|
||||
* and lguest, but hopefully others soon. Do NOT change this since it will
|
||||
* 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);
|
||||
}
|
||||
|
||||
#endif /* _LINUX_VIRTIO_RING_H */
|
||||
#endif /* _UAPI_LINUX_VIRTIO_RING_H */
|
||||
|
|
23
nbd.c
23
nbd.c
|
@ -596,24 +596,23 @@ int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize)
|
|||
return -serrno;
|
||||
}
|
||||
|
||||
if (flags & NBD_FLAG_READ_ONLY) {
|
||||
int read_only = 1;
|
||||
TRACE("Setting readonly attribute");
|
||||
if (ioctl(fd, NBD_SET_FLAGS, flags) < 0) {
|
||||
if (errno == ENOTTY) {
|
||||
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;
|
||||
LOG("Failed setting read-only attribute");
|
||||
LOG("Failed setting flags");
|
||||
return -serrno;
|
||||
}
|
||||
}
|
||||
|
||||
if (ioctl(fd, NBD_SET_FLAGS, flags) < 0
|
||||
&& errno != ENOTTY) {
|
||||
int serrno = errno;
|
||||
LOG("Failed setting flags");
|
||||
return -serrno;
|
||||
}
|
||||
|
||||
TRACE("Negotiation ended");
|
||||
|
||||
return 0;
|
||||
|
|
32
osdep.c
32
osdep.c
|
@ -54,38 +54,6 @@ static bool fips_enabled = false;
|
|||
|
||||
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)
|
||||
{
|
||||
#if defined(SOL_TCP) && defined(TCP_CORK)
|
||||
|
|
|
@ -32,13 +32,6 @@
|
|||
#include "trace.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)
|
||||
{
|
||||
if (ptr == NULL) {
|
||||
|
|
215
pflib.c
215
pflib.c
|
@ -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
20
pflib.h
|
@ -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
|
|
@ -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] = {
|
||||
&qemu_drive_opts,
|
||||
&qemu_chardev_opts,
|
||||
|
@ -703,6 +712,7 @@ static QemuOptsList *vm_config_groups[32] = {
|
|||
&qemu_iscsi_opts,
|
||||
&qemu_sandbox_opts,
|
||||
&qemu_add_fd_opts,
|
||||
&qemu_object_opts,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
|
@ -610,14 +610,14 @@ QEMU can access directly to block device exported using the Network Block Device
|
|||
protocol.
|
||||
|
||||
@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
|
||||
|
||||
If the NBD server is located on the same host, you can use an unix socket instead
|
||||
of an inet socket:
|
||||
|
||||
@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
|
||||
|
||||
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
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
and then you can use it with two guests:
|
||||
@example
|
||||
qemu-system-i386 linux1.img -hdb nbd:unix:/tmp/my_socket
|
||||
qemu-system-i386 linux2.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://?socket=/tmp/my_socket
|
||||
@end example
|
||||
|
||||
If the nbd-server uses named exports (since NBD 2.9.18), you must use the
|
||||
"exportname" option:
|
||||
If the nbd-server uses named exports (supported since NBD 2.9.18, or with QEMU's
|
||||
own embedded NBD server), you must specify an export name in the URI:
|
||||
@example
|
||||
qemu-system-i386 -cdrom nbd:localhost:exportname=debian-500-ppc-netinst
|
||||
qemu-system-i386 -cdrom nbd:localhost:exportname=openSUSE-11.1-ppc-netinst
|
||||
qemu-system-i386 -cdrom nbd://localhost/debian-500-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
|
||||
|
||||
@node disk_images_sheepdog
|
||||
|
|
|
@ -539,6 +539,7 @@ int main(int argc, char **argv)
|
|||
snprintf(sockpath, 128, SOCKET_PATH, basename(device));
|
||||
}
|
||||
|
||||
qemu_init_main_loop();
|
||||
bdrv_init();
|
||||
atexit(bdrv_close_all);
|
||||
|
||||
|
@ -584,7 +585,6 @@ int main(int argc, char **argv)
|
|||
memset(&client_thread, 0, sizeof(client_thread));
|
||||
}
|
||||
|
||||
qemu_init_main_loop();
|
||||
qemu_set_fd_handler2(fd, nbd_can_accept, nbd_accept, NULL,
|
||||
(void *)(uintptr_t)fd);
|
||||
|
||||
|
|
|
@ -2904,6 +2904,14 @@ DEF("no-kvm-irqchip", HAS_ARG, QEMU_OPTION_no_kvm_irqchip, "", QEMU_ARCH_I386)
|
|||
HXCOMM Deprecated (ignored)
|
||||
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!
|
||||
STEXI
|
||||
@end table
|
||||
|
|
|
@ -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"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (image == NULL) {
|
||||
|
|
|
@ -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
|
||||
#define QEMU_PIXMAN_H
|
||||
|
||||
|
@ -27,6 +32,8 @@ pixman_image_t *qemu_pixman_linebuf_create(pixman_format_code_t format,
|
|||
int width);
|
||||
void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb,
|
||||
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);
|
||||
|
||||
#endif /* QEMU_PIXMAN_H */
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
struct sockaddr_in *i4;
|
||||
|
|
9
qmp.c
9
qmp.c
|
@ -471,15 +471,6 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
|
|||
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)
|
||||
{
|
||||
return arch_query_cpu_definitions(errp);
|
||||
|
|
56
qom/object.c
56
qom/object.c
|
@ -1183,6 +1183,62 @@ void object_property_add_str(Object *obj, const char *name,
|
|||
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)
|
||||
{
|
||||
return g_strdup(object_get_typename(obj));
|
||||
|
|
|
@ -31,7 +31,7 @@ endif
|
|||
%.o: %.m
|
||||
$(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
|
||||
$(call LINK,$^)
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#include "qemu-common.h"
|
||||
#include "main-loop.h"
|
||||
|
||||
void qemu_fd_register(int fd)
|
||||
{
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#include "qemu-common.h"
|
||||
#include "monitor.h"
|
||||
|
||||
int monitor_fdset_dup_fd_find(int dup_fd)
|
||||
{
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#include "qemu-common.h"
|
||||
#include "monitor.h"
|
||||
|
||||
int monitor_fdset_get_fd(int64_t fdset_id, int flags)
|
||||
{
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#include "qemu-common.h"
|
||||
#include "monitor.h"
|
||||
|
||||
int monitor_fdset_dup_fd_remove(int dupfd)
|
||||
{
|
||||
return -1;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue